diff --git a/README.md b/README.md index b611ec3ba..f903eb7c5 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,14 @@ > 你好,我是小傅哥,[《重学Java设计模式》](https://item.jd.com/13218336.html) 图书作者,一线互联网 Java 工程师、架构师。[:pencil2: 虫洞栈,博主](https://bugstack.cn),[:memo: 关于我](https://bugstack.cn/md/other/guide-to-reading.html) -🚜 **虫洞栈,与你:上最快的车、唠最狠的嗑,爬最高的坡、拿最贵的Offer!** - -- 👉 如果你不知道自己要从哪开始、要学什么、有什么结果,请看 [阅读指南](https://bugstack.cn/md/other/guide-to-reading.html) 这是一个码农研发人员提升自身技术栈广度和深度的经验之路,也是小傅哥的亲身学习经历汇总! -- 👉 如果你是刚入行、在外包、跨语言学习、想跳槽大厂、缺少学习动力等,可以阅读小傅哥的成长故事,这个系列包括了我的个人在外包到大厂的成长、跳槽的过程、互联网的学习经历 Go -> [关于小傅哥](https://bugstack.cn/md/about/me/2020-08-25-13%E5%B9%B4%E6%AF%95%E4%B8%9A%EF%BC%8C%E7%94%A8%E4%B8%A4%E5%B9%B4%E6%97%B6%E9%97%B4%E4%BB%8E%E5%A4%96%E5%8C%85%E8%B5%B0%E8%BF%9B%E4%BA%92%E8%81%94%E7%BD%91%E5%A4%A7%E5%8E%82.html) -- 👉 推荐 [在线阅读 bugstack.cn](https://bugstack.cn) (Github 访问速度比较慢可能会导致部分图片无法刷新出来) -- 👉 **【源码下载】** ❤下载指引:小傅哥博客【设计模式、Spring、字节码、插件、中间件等】涉及到的全部源码地址:[Go -> 源码索引](https://github.com/fuzhengwei/CodeGuide#1-%E6%BA%90%E7%A0%81) +- :dog: 13年毕业,大厂互联网T8架构师,全网40万粉编程知识博主。 +- :man_technologist: 成长:[关于我,从小白到架构师的成长经历](https://www.bilibili.com/video/BV1FF41137q5) +- :bus: 源码:[`RoadMap 编程路书`](https://github.com/fuzhengwei/RoadMap) | [`Java 数据结构和算法`](https://github.com/fuzhengwei/java-algorithms) | [`IM 仿微信`](https://github.com/fuzhengwei/NaiveChat) | [`Java 面经手册`](https://github.com/fuzhengwei/interview) | [`IntelliJ IDEA 插件开发`](https://github.com/fuzhengwei/guide-idea-plugin) | [`Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践`](https://github.com/fuzhengwei/Lottery) | [`API网关`](https://github.com/fuzhengwei/api-gateway) | [`手写MyBatis`](https://github.com/fuzhengwei/small-mybatis) | [`重学Java设计模式`](https://github.com/fuzhengwei/itstack-demo-design) | [`Netty 实战案例`](https://github.com/fuzhengwei/itstack-demo-netty) | [`字节码编程`](https://github.com/fuzhengwei/itstack-demo-bytecode) | [`ChatGPT AI 问答助手`](https://github.com/fuzhengwei/chatbot-api) | [更多搜索...](https://github.com/fuzhengwei?tab=repositories) +- :seedling: 干货:[公众号『 bugstack虫洞栈 』](https://bugstack.cn/images/personal/qrcode.png) +- :pencil: 博客:[bugstack.cn](https://bugstack.cn/) - 足够硬核,内容老狠了! +- :tv: 视频:[B站 小傅哥の码场](https://space.bilibili.com/15637440) +- :love_letter: 微信:[fustack](https://bugstack.cn/images/personal/fustack.png) - 备注来意 +- :feet: 我的编程知识星球:[实战生产级项目、手写框架级源码,可以向我 1对1 提问,解答技术/职场/规划问题](https://bugstack.cn/md/zsxq/introduce.html)
@@ -40,7 +42,7 @@ 1. **介绍**:关于 虫洞栈 · CodeGuide 的相关介绍请看:[关于`虫洞栈`的一些说明](https://bugstack.cn/md/other/guide-to-reading.html) 2. **贡献**:欢迎参与到 CodeGuide 的维护工作,你可以 `提交Issue`、`处理Issue`、`提交PR(Fork代码库,提交Pull requests) - 另外当你 Fork 代码库后,可以在阅读` [`bugstack.cn`](https://bugstack.cn/) 时,文末点击 “在 GitHub 上编辑此页” 进行提交PR Go -> [操作指导](https://github.com/fuzhengwei/CodeGuide/issues/260) -3. **PDF**:[《Java 面经手册》](https://download.csdn.net/download/Yao__Shun__Yu/14932325)、[《手撸 Spring》](https://download.csdn.net/download/Yao__Shun__Yu/21009038)、[《字节码编程》](https://download.csdn.net/download/Yao__Shun__Yu/12505051) +3. **PDF**:[《Java 面经手册》](https://download.csdn.net/download/Yao__Shun__Yu/14932325)、[《字节码编程》](https://download.csdn.net/download/Yao__Shun__Yu/12505051) 4. **小册**:[IM](https://blog.csdn.net/generalfu/category_10400631.html) - Netty4.x + SpringBoot 仿PC微信、[SpringBoot 中间件设计和开发](https://juejin.cn/book/6940996508632219689) - 16个中间件实现,包括测试工程等共计30个代码库,每一章节都会对应有一个中间件的设计和实现 5. **我的书籍**:[《重学Java设计模式》](https://item.jd.com/13218336.html) - 这是一本基于互联网真实案例编写的Java设计模式实践图书。全书以解决方案为核心,从实际开发业务中抽离出交易、营销、规则引擎、中间件、框架源码等22个真实场景,对设计模式进行全面、彻底的分析。 6. **知识星球**:[码农会锁](https://t.zsxq.com/jAi2nUf) - 技术解答/简历批阅/成长指导/内容分享/实战项目 - [《Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践》](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html) @@ -702,7 +704,7 @@ -### 8.3 Lottery 分布式抽奖系统 - 👉[**在线阅读**](https://bugstack.cn/md/project/lottery/Part-2/%E7%AC%AC01%E8%8A%82%EF%BC%9A%E5%BC%80%E7%AF%87%E4%BB%8B%E7%BB%8D.html) +### 8.3 Lottery 分布式抽奖系统 - 👉[**在线阅读**](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html) - [Lottery 分布式抽奖系统介绍](https://github.com/fuzhengwei/CodeGuide/blob/master/docs/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.md) diff --git a/build.sh b/build.sh new file mode 100644 index 000000000..2af146afc --- /dev/null +++ b/build.sh @@ -0,0 +1,11 @@ +# 先切换到指定的 Node.js 版本 sudo n +NODE_OPTIONS=--max-old-space-size=24096 npm run build + +# 优惠券地址;https://bugstack.cn/images/article/zsxq/zsxq-youhuiquan.png +# node 版本切换;https://www.cnblogs.com/fengchong0925/p/18489658 +# 全局安装;npm install -g n +# 安装指定node版本;sudo -E n 18.16.0 +# 查看已安装的版本列表;n list +# 删除指定版本;n rm 16.13.2 +# 切换node版本;sudo n +# 查看切换版本;node -v npm -v \ No newline at end of file diff --git a/docs/.vuepress/components/LockArticle.vue b/docs/.vuepress/components/LockArticle.vue index 602623f59..cbebdc22f 100644 --- a/docs/.vuepress/components/LockArticle.vue +++ b/docs/.vuepress/components/LockArticle.vue @@ -84,7 +84,7 @@ t.getToken().then(function (token) { $.ajax({ - url: 'https://api.bugstack.cn/interfaces/BlogApi.php', + url: 'https://x-api.itedus.cn/api/v1/blog/check', type: "GET", dataType: "text", data: { @@ -95,7 +95,7 @@ t._lock(articleObj); } else { t._unlock(articleObj); - t.setCookie("_unlock", "success", 7); + t.setCookie("_unlock", "success", 45); } }, error: function (data) { @@ -164,16 +164,16 @@ }, getToken: async function () { - // 浏览器 Cookie true 不限制 - if(navigator.cookieEnabled){ - let value = this.getCookie('BAEID'); - if (!value) { - return await this.getFingerprintId(); - } - return value.substring(value.length - 6).toUpperCase(); - } else{ - return await this.getFingerprintId(); - } + // 浏览器 Cookie true 不限制 + if(navigator.cookieEnabled){ + let value = this.getCookie('BAEID'); + if (!value) { + return await this.getFingerprintId(); + } + return value.substring(value.length - 6).toUpperCase(); + } else{ + return await this.getFingerprintId(); + } // return await this.getFingerprintId(); }, getFingerprintId: function () { @@ -205,9 +205,9 @@ if (parts.length === 2) return parts.pop().split(";").shift(); }, - setCookie: function (name, value, hours){ + setCookie: function (name, value, day){ let exp = new Date(); - exp.setTime(exp.getTime() + hours*60*60*1000); + exp.setTime(exp.getTime() + day*24*60*60*1000); // ;path=/ cookie全站有效 document.cookie = name + "="+ escape (value) + ";path=/;expires=" + exp.toGMTString(); }, diff --git a/docs/.vuepress/components/PayArticle.vue b/docs/.vuepress/components/PayArticle.vue index 0c7775520..ca78d464a 100644 --- a/docs/.vuepress/components/PayArticle.vue +++ b/docs/.vuepress/components/PayArticle.vue @@ -4,7 +4,7 @@
付费阅读 + style="position: absolute; left: 50%; top: 70%; bottom: 30px; transform: translate(-50%, -50%); width: 160px; height: 36px; line-height: 36px; font-size: 15px; text-align: center; border: 1px solid rgb(222, 104, 109); color: rgb(222, 104, 109); background: rgb(255, 255, 255); cursor: pointer; border-radius: 6px;">星球会员
diff --git a/docs/.vuepress/components/RoadMap.vue b/docs/.vuepress/components/RoadMap.vue new file mode 100644 index 000000000..71cd5ab96 --- /dev/null +++ b/docs/.vuepress/components/RoadMap.vue @@ -0,0 +1,2259 @@ + + + + + \ No newline at end of file diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 7eb99168c..c19b5b394 100755 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -32,8 +32,8 @@ module.exports = { locales: { "/": { lang: "zh-CN", - title: "bugstack 虫洞栈", - description: "包含: Java 基础,面经手册,Netty4.x,手写Spring,用Java实现JVM,重学Java设计模式,SpringBoot中间件开发,IDEA插件开发,DDD系统架构项目开发,字节码编程..." + title: "小傅哥 bugstack 虫洞栈", + description: "包含: Java 基础,面经手册,Netty4.x,手写MyBatis,用Java实现JVM,重学Java设计模式,SpringBoot中间件开发,IDEA插件开发,大营销抽奖系统,Java 实战项目训练,字节码编程..." } }, head: [ @@ -47,7 +47,7 @@ module.exports = { ["meta", {"http-equiv": "Expires", content: "0"}], ["meta", { name: "keywords", - content: "bugstack 虫洞栈, 重学Java设计模式, 字节码编程, 中间件, Spring, Java基础, 面经手册" + content: "欢迎来到小傅哥的编程世界,这里是bugstack虫洞栈!无论你是Java初学者还是资深开发者,我们都为你准备了丰富的学习资源和实战项目,包括Java基础教程、实战项目指南、应用项目开发、Java面试题解析、重学Java设计模式、字节码编程、手写Spring与MyBatis、API网关与中间件、DDD抽奖系统、SpringBoot Starter开发,以及ChatGPT与AI技术的应用。我们致力于为你提供最优质的学习体验,帮助你在Java开发的道路上不断进步。欢迎访问我们的官方网站获取更多资源,并加入我们的社交媒体平台,与全球开发者一起交流学习。" }], ["meta", {name: "apple-mobile-web-app-capable", content: "yes"}], ['script', @@ -70,12 +70,12 @@ module.exports = { async: 'async', src: '/js/fingerprint2.min.js', }], - ['script', - { - charset: 'utf-8', - async: 'async', - src: 'https://s9.cnzz.com/z_stat.php?id=1278232949&web_id=1278232949', - }], + // ['script', + // { + // charset: 'utf-8', + // async: 'async', + // src: 'https://s9.cnzz.com/z_stat.php?id=1278232949&web_id=1278232949', + // }], // 添加百度统计 ["script", {}, ` @@ -185,185 +185,208 @@ module.exports = { text: '导读', link: '/md/other/guide-to-reading.md' }, { - text: '算法', - items: [ - { - text: '数据结构', - link: '/md/algorithm/data-structures/2022-07-22-linked-list.md' - }, - { - text: '算法主题', - link: '/md/algorithm/logic/2020-03-14-野路子搞算法《两数之和》,带着小白刷面试.md' - } - ] + text: '编程路书', link: '/md/road-map/road-map.md' }, { - text: 'Java', - items: [ - { - text: '面经手册', - link: '/md/java/interview/2020-07-28-面经手册 · 开篇《面试官都问我啥》.md' - }, - { - text: '用Java实现JVM', - link: '/md/java/develop-jvm/2019-05-01-用Java实现JVM第一章《命令行工具》.md' - }, - { - text: '基础技术', - link: '/md/java/core/2020-01-06-[源码分析]咋嘞?你的IDEA过期了吧!加个Jar包就破解了,为什么?.md' - } - ] + text: 'AI Agent', link: '/md/ai/spring-ai.md' }, { - text: 'Spring', + text: '算法', items: [ { - text: 'Spring 手撸专栏', - link: '/md/spring/develop-spring/2021-05-16-第1章:开篇介绍,手写Spring能给你带来什么?.md' - }, - { - text: 'Mybatis 手撸专栏', - link: '/md/spring/develop-mybatis/2022-03-20-第1章:开篇介绍,手写Mybatis能给你带来什么?.md' + text: '数据结构', + link: '/md/algorithm/data-structures/data-structures.md' }, { - text: 'Spring Cloud', - link: '/md/spring/spring-cloud/2019-10-31-Spring Cloud零《总有一偏概述告诉你SpringCloud是什么》.md' + text: '算法主题', + link: '/md/algorithm/logic/math/math.md' }, { - text: '源码分析(Mybatis、Quartz)', - link: '/md/spring/source-code/2019-12-25-[源码分析]Mybatis接口没有实现类为什么可以执行增删改查.md' + text: '机器学习', + link: '/md/algorithm/model/2023-02-12-chat-gpt.md' } ] }, { - text: '面向对象', + text: '开发技术', items: [ { - text: '重学Java设计模式', + text: 'Java', items: [ { - text: '创建型模式', - link: '/md/develop/design-pattern/2020-05-20-重学Java设计模式《实战工厂方法模式》.md' + text: '面经手册', + link: '/md/java/interview/2020-07-28-面经手册 · 开篇《面试官都问我啥》.md' }, { - text: '结构型模式', - link: '/md/develop/design-pattern/2020-06-02-重学 Java 设计模式《适配器模式》.md' + text: '用Java实现JVM', + link: '/md/java/develop-jvm/2019-05-01-用Java实现JVM第一章《命令行工具》.md' }, { - text: '行为型模式', - link: '/md/develop/design-pattern/2020-06-18-重学 Java 设计模式《实战责任链模式》.md' + text: '基础技术', + link: '/md/java/core/2020-01-06-[源码分析]咋嘞?你的IDEA过期了吧!加个Jar包就破解了,为什么?.md' } ] }, { - text: '系统架构', + text: 'Spring', items: [ { - text: 'DDD 专题', - link: '/md/develop/framework/ddd/2019-10-15-DDD专题案例一《初识领域驱动设计DDD落地》.md' + text: 'Spring 手撸专栏', + link: '/md/spring/develop-spring/2021-05-16-第1章:开篇介绍,手写Spring能给你带来什么?.md' }, { - text: '工程框架', - link: '/md/develop/framework/frame/2019-12-22-架构框架搭建一《单体应用服务之SSM整合:Spring4 + SpringMvc + Mybatis》.md' + text: 'MyBatis 手撸专栏', + link: '/md/spring/develop-mybatis/2022-03-20-第1章:开篇介绍,手写Mybatis能给你带来什么?.md' }, { - text: '架构方案', - link: '/md/develop/framework/scheme/2021-02-04-基于IDEA插件开发和字节码插桩技术,实现研发交付质量自动分析.md' + text: 'Spring Cloud', + link: '/md/spring/spring-cloud/2019-10-31-Spring Cloud零《总有一偏概述告诉你SpringCloud是什么》.md' + }, + { + text: '源码分析(Mybatis、Quartz)', + link: '/md/spring/source-code/2019-12-25-[源码分析]Mybatis接口没有实现类为什么可以执行增删改查.md' } ] }, { - text: '标准', + text: '面向对象', items: [ { - text: '开发规范&事故', + text: '重学Java设计模式-创建型模式', + link: '/md/develop/design-pattern/2020-05-20-重学Java设计模式《实战工厂方法模式》.md' + }, + { + text: '重学Java设计模式-结构型模式', + link: '/md/develop/design-pattern/2020-06-02-重学 Java 设计模式《适配器模式》.md' + }, + { + text: '重学Java设计模式-行为型模式', + link: '/md/develop/design-pattern/2020-06-18-重学 Java 设计模式《实战责任链模式》.md' + }, + { + text: '系统架构-DDD 专题', + link: '/md/develop/framework/ddd/2019-10-15-DDD专题案例一《初识领域驱动设计DDD落地》.md' + }, + { + text: '系统架构-工程框架', + link: '/md/develop/framework/frame/2019-12-22-架构框架搭建一《单体应用服务之SSM整合:Spring4 + SpringMvc + Mybatis》.md' + }, + { + text: '系统架构-架构方案', + link: '/md/develop/framework/scheme/2021-02-04-基于IDEA插件开发和字节码插桩技术,实现研发交付质量自动分析.md' + }, + { + text: '标准-开发规范&事故', link: '/md/develop/standard/2020-09-14-一次代码评审,差点过不了试用期!.md' } ] - } - ] - }, - { - text: '中间件', - items: [ - { - text: 'SpringBoot 中间件开发', - link: '/md/assembly/middleware/2019-12-02-SpringBoot服务治理中间件之统一白名单验证.md' - }, - { - text: 'IDEA-Plugin', - link: '/md/assembly/idea-plugin/2021-08-27-技术调研,IDEA 插件怎么开发?.md' - }, - { - text: 'API网关:中间件设计和实践', - link: '/md/assembly/api-gateway/2022-08-12-开篇:如果让我设计一套,TPS百万级API网关.md' - } - ] - }, - { - text: 'Netty 4.x', - items: [ - { - text: '基础入门篇', - link: '/md/netty/base/2019-07-30-netty案例,netty4.1基础入门篇零《初入JavaIO之门BIO、NIO、AIO实战练习》.md' - }, - { - text: '中级拓展篇', - link: '/md/netty/expand/2019-08-16-netty案例,netty4.1中级拓展篇一《Netty与SpringBoot整合》.md' - }, - { - text: '高级应用篇', - link: '/md/netty/application/2019-09-01-手写RPC框架第一章《自定义配置xml》.md' }, { - text: '源码分析篇', - link: '/md/netty/source-code/2019-09-10-netty案例,netty4.1源码分析篇一《NioEventLoopGroup源码分析》.md' + text: 'Netty 4.x', + items: [ + { + text: '基础入门篇', + link: '/md/netty/base/2019-07-30-netty案例,netty4.1基础入门篇零《初入JavaIO之门BIO、NIO、AIO实战练习》.md' + }, + { + text: '中级拓展篇', + link: '/md/netty/expand/2019-08-16-netty案例,netty4.1中级拓展篇一《Netty与SpringBoot整合》.md' + }, + { + text: '高级应用篇', + link: '/md/netty/application/2019-09-01-手写RPC框架第一章《自定义配置xml》.md' + }, + { + text: '源码分析篇', + link: '/md/netty/source-code/2019-09-10-netty案例,netty4.1源码分析篇一《NioEventLoopGroup源码分析》.md' + }, + ] }, - ] - }, - { - text: '字节码编程', - items: [ { - text: '框架', items: [ + text: '字节码编程', + items: [ { - text: 'ASM', + text: '框架-ASM', link: '/md/bytecode/asm/2020-03-25-[ASM字节码编程]如果你只写CRUD,那这种技术你永远碰不到.md' }, { - text: 'Javassist', + text: '框架-Javassist', link: '/md/bytecode/javassist/2020-04-19-字节码编程,Javassist篇一《基于javassist的第一个案例helloworld》.md' }, { - text: 'Byte-Buddy', + text: '框架-Byte-Buddy', link: '/md/bytecode/byte-buddy/2020-05-08-字节码编程,Byte-buddy篇一《基于Byte Buddy语法创建的第一个HelloWorld》.md' + }, + { + text: '全链路监控-JavaAgent', + link: '/md/bytecode/agent/2019-07-10-基于JavaAgent的全链路监控一《嗨!JavaAgent》.md' + }, + { + text: '文档-ASM-DOC', + link: '/md/bytecode/asm-document/1引言.md' } ] }, { - text: '全链路监控', items: [ + text: '部署', + items: [ { - text: 'JavaAgent', - link: '/md/bytecode/agent/2019-07-10-基于JavaAgent的全链路监控一《嗨!JavaAgent》.md' + text: '部署工具', + link: '/md/devops/2023-04-18-tool.md' } ] }, - { - text: '文档', items: [ - {text: 'ASM-DOC', link: '/md/bytecode/asm-document/1引言.md'} - ] - } ] }, - { - text: '部署', - link: '/md/devops/2019-08-12-windows环境下安装elasticsearch6.2.2.md' - }, { text: '💯实战项目', items: [ + { + text: '创新类型(AI)', items: [ + { + text: 'AI Agent 脚手架 + 场景应用', + link: '/md/project/ai-agent-scaffold/ai-agent-scaffold.md' + }, + { + text: 'AI MCP Gateway 网关服务系统(更新中)', + link: '/md/project/ai-mcp-gateway/ai-mcp-gateway.md' + }, + { + text: 'AI Agent 拖拉拽 + 动态配置', + link: '/md/project/ai-knowledge/ai-knowledge.md' + }, + { + text: 'OpenAI 代码自动评审', + link: 'https://bugstack.cn/md/zsxq/project/openai-code-review.html' + }, + { + text: 'OpenAI 大模型应用服务体系构建', + link: '/md/project/chatgpt/chatgpt.md' + }, + { + text: 'ChatGPT AI 问答助手', + link: '/md/project/chatbot-api/chatbot-api.md' + }, + { + text: 'OpenAI SDK 组件项目', + link: 'https://bugstack.cn/md/zsxq/project/openai-sdk-java.html' + } + ] + }, { text: '业务类型', items: [ + { + text: '拼团交易平台系统', + link: '/md/project/group-buy-market/group-buy-market.md' + }, + { + text: '小型支付电商系统', + link: '/md/project/s-pay-mall/s-pay-mall.md' + }, + { + text: '大营销平台系统', + link: '/md/project/big-market/big-market.md' + }, { text: 'Lottery 分布式抽奖系统', link: '/md/project/lottery/introduce/Lottery抽奖系统.md' @@ -376,13 +399,53 @@ module.exports = { }, { text: '组件类型', items: [ + { + text: '本地任务消息组件', + link: '/md/project/local-task-message/local-task-message.md' + }, + { + text: '通用技术组件 - 🔧扳手工程', + link: 'https://bugstack.cn/md/zsxq/project/xfg-wrench.html' + }, + { + text: '透视业务流程-监控系统', + link: 'https://bugstack.cn/md/zsxq/project/business-behavior-monitor.html' + }, + { + text: '动态线程池组件', + link: 'https://bugstack.cn/md/zsxq/project/dynamic-thread-pool.html' + }, + { + text: '支付SDK设计和开发', + link: 'https://bugstack.cn/md/zsxq/project/ltzf-sdk-java.html' + }, { text: 'SpringBoot 中间件设计和开发', link: 'https://bugstack.cn/md/assembly/middleware/2021-03-31-%E3%80%8ASpringBoot%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%BC%80%E5%8F%91%E3%80%8B%E4%B8%93%E6%A0%8F%E5%B0%8F%E5%86%8C%E4%B8%8A%E7%BA%BF%E5%95%A6%EF%BC%81.html' }, { text: 'API网关:中间件设计和实践', - link: 'https://bugstack.cn/md/assembly/api-gateway/2022-08-12-%E5%BC%80%E7%AF%87%EF%BC%9A%E5%A6%82%E6%9E%9C%E8%AE%A9%E6%88%91%E8%AE%BE%E8%AE%A1%E4%B8%80%E5%A5%97%EF%BC%8CTPS%E7%99%BE%E4%B8%87%E7%BA%A7API%E7%BD%91%E5%85%B3.html' + link: 'https://bugstack.cn/md/assembly/api-gateway/api-gateway.html' + }, + { + text: 'IDEA Plugin 插件开发', + link: 'https://bugstack.cn/md/assembly/idea-plugin/2021-08-27-%E6%8A%80%E6%9C%AF%E8%B0%83%E7%A0%94%EF%BC%8CIDEA%20%E6%8F%92%E4%BB%B6%E6%80%8E%E4%B9%88%E5%BC%80%E5%8F%91%EF%BC%9F.html' + }, + ] + }, + { + text: '自学类型', items: [ + { + text: '场景案例', + link: '/md/project/ddd-scene-solution/alipay-sandbox.md' + }, + { + text: '吉祥外卖', + link: '/md/project/lucky-tackout/lucky-tackout.md' + }, + { + text: '钓鱼佬', + link: '/md/project/xfg-fish-pond/xfg-fish-pond.md' }, ] }, @@ -393,33 +456,37 @@ module.exports = { link: '/md/zsxq/introduce.md' }, { - text: '📝产品', + text: '📝产品(ai ide)', items: [ { text: '出版物', items: [ { text: '2021年出版《重学Java设计模式》', - link: 'https://item.jd.com/13218336.html' + link: '/md/product/book/design-pattern.md' + }, + { + text: '2023年出版《手写MyBatis:渐进式源码实践》', + link: '/md/product/book/mybatis.md' }, ] }, { text: 'PDF —— 加入星球免费获取', items: [ { - text: '免费《字节码编程手册》(密码:Rlxbh1ia)', - link: 'http://pan.bugstack.cn/?dl=05b281eff1476e2c22eb5114ced0dc4d' + text: '免费《字节码编程手册》', + link: 'https://drive.weixin.qq.com/s?k=ACMA4AfQABUJWQ0P92' }, { text: '免费《重学Java设计模式》——旧版PDF(密码:FWchEAF6)', - link: 'http://pan.bugstack.cn/?dl=431e114a26f810655d29b6dea54a680f' + link: 'https://drive.weixin.qq.com/s?k=ACMA4AfQABUg04LF5X' }, { - text: '付费《Java 面经手册》', - link: 'https://download.csdn.net/download/Yao__Shun__Yu/14932325' + text: '免费《倚天村 • 图解数据结构》', + link: 'https://drive.weixin.qq.com/s?k=ACMA4AfQABUm2EZtFm' }, { - text: '付费《手写 Spring》', - link: 'https://download.csdn.net/download/Yao__Shun__Yu/21009038' + text: '付费《Java 面经手册》', + link: 'https://download.csdn.net/download/Yao__Shun__Yu/14932325' }, { text: '付费《IDEA Plugin 开发手册》', @@ -431,7 +498,15 @@ module.exports = { text: '插件', items: [ { text: '💱 IDEA Plugin vo2dto —— 对象转换插件', - link: 'https://plugins.jetbrains.com/plugin/18262-vo2dto' + link: '/md/product/idea-plugin/vo2dto-v2.5.5.md' + }, + ] + }, + { + text: 'AI IDE', items: [ + { + text: '👨🏻‍💻WaLiCode,AI IDE Coding', + link: '/md/product/software/walicode.md' }, ] }, @@ -441,7 +516,10 @@ module.exports = { text: '关于', items: [ {text: '关于自己', link: '/md/about/me/about-me.md'}, - {text: '关于学习', link: '/md/about/study/2020-04-30-讲道理,只要你是一个爱折腾的程序员,毕业找工作真的不需要再花钱培训.md'}, + { + text: '关于学习', + link: '/md/about/study/2020-04-30-讲道理,只要你是一个爱折腾的程序员,毕业找工作真的不需要再花钱培训.md' + }, {text: '关于职场', link: '/md/about/job/2020-04-11-工作两年简历写成这样,谁要你呀!.md'} ] }, @@ -450,9 +528,10 @@ module.exports = { link: 'https://space.bilibili.com/15637440' }, { - text: '源码仓库', + text: '源码', items: [ - {text: '开源项目 - Github', link: 'https://github.com/fuzhengwei/CodeGuide'}, + {text: '开源项目 - Github', link: 'https://github.com/fuzhengwei'}, + {text: '开源项目 - Gitcode', link: 'https://gitcode.net/fuzhengwei'}, {text: '付费项目 - Gitcode', link: 'https://gitcode.net/KnowledgePlanet'}, ] } @@ -461,6 +540,7 @@ module.exports = { "/md/other/": genBarOther(), "/md/algorithm/data-structures/": genAlgorithmDataStructures(), "/md/algorithm/logic/": genAlgorithmLogic(), + "/md/algorithm/model/": genAlgorithmModel(), "/md/java/interview/": genBarJavaInterview(), "/md/java/develop-jvm/": genBarJavaDevelopJvm(), "/md/java/core/": genBarJavaCore(), @@ -480,9 +560,22 @@ module.exports = { "/md/bytecode/agent/": genBarBytecodeAgent(), "/md/bytecode/": genBarBytecodeAsmJavassistByteBuddy(), "/md/project/springboot-middleware/": getBarProjectSpringBootMiddleware(), + "/md/project/chatgpt/": getBarProjectChatGPT(), "/md/project/lottery/": getBarProjectLottery(), "/md/project/im/": getBarProjectIM(), + "/md/project/chatbot-api/": getBarProjectChatBotApi(), + "/md/project/big-market/": getBarBigMarket(), + "/md/project/s-pay-mall/": getBarSPayMall(), + "/md/project/group-buy-market/": getBarGroupBuyMarket(), + "/md/project/ai-knowledge/": getBarAiRagKnowledge(), + "/md/project/ai-agent-scaffold/": getBarAIAgentScaffold(), + "/md/project/ai-mcp-gateway/": getBarAIMCPGateway(), + "/md/project/local-task-message/": getBarLocalTaskMessage(), + "/md/project/": getBarDDDSceneSolution(), "/md/zsxq/": getBarZSXQ(), + "/md/product/": getBarProduct(), + "/md/road-map/": genBarGuide(), + "/md/ai/": genBarAI(), "/md/about/": genBarAbout() } } @@ -490,136 +583,466 @@ module.exports = { } }; -// algorithm/data-structures -function genAlgorithmDataStructures() { +// other +function genBarOther() { return [ { - title: "数据结构", - collapsable: false, - sidebarDepth: 0, + title: "学习指引", + collapsable: true, + sidebarDepth: 2, children: [ - "2022-07-22-linked-list.md", - "2022-07-30-array-list.md", - "2022-08-06-queue.md", - "2022-08-17-stack.md", - "2022-08-27-hash-table.md", - "2022-09-03-heap.md", - "2022-09-14-trie.md", - "2022-09-18-tree.md", + "road-map.md", + "guide-to-reading.md" ] } ] } -// algorithm/logic -function genAlgorithmLogic() { +function genBarAI() { return [ { - title: "算法主题", + title: "框架", collapsable: false, sidebarDepth: 0, children: [ - "2020-03-14-野路子搞算法《两数之和》,带着小白刷面试.md", - "2020-03-18-无重复字符的最长子串.md", + "spring-ai.md", + "google-adk.md", ] - } + }, + { + title: "组件", + collapsable: false, + sidebarDepth: 0, + children: [ + "agent-skill.md", + "a2a.md", + ] + }, + { + title: "工具", + collapsable: false, + sidebarDepth: 0, + children: [ + "draw.io.md", + "qclaw.md", + "openclaw.md", + "ai-ssh-opencode.md", + "github-models.md", + "trae.md", + ] + }, ] } -// java-interview -function genBarJavaInterview() { +function genBarGuide() { return [ { - title: "第 1 章 谈谈面试", + title: "简明教程(3)", collapsable: false, - sidebarDepth: 0, + sidebarDepth: 3, children: [ - "2020-07-28-面经手册 · 开篇《面试官都问我啥》.md", - "2020-07-30-面经手册 · 第1篇《认知自己的技术栈盲区》.md", - "2021-03-07-面试现场:小伙伴美团一面的分享和分析[含解答].md" + "road-map.md", + "introduce.md", + "cainiao.md", ] }, { - title: "第 2 章 数据结构和算法", + title: "工程脚手架(2)", collapsable: false, sidebarDepth: 0, children: [ - "2020-08-04-面经手册 · 第2篇《数据结构,HashCode为什么使用31作为乘数?》.md", - "2020-08-07-面经手册 · 第3篇《HashMap核心知识,扰动函数、负载因子、扩容链表拆分,深度学习》.md", - "2020-08-13-面经手册 · 第4篇《HashMap数据插入、查找、删除、遍历,源码分析》.md", - "2020-08-16-面经手册 · 第5篇《看图说话,讲解2-3平衡树「红黑树的前身」》.md", - "2020-08-20-面经手册 · 第6篇《带着面试题学习红黑树操作原理,解析什么时候染色、怎么进行旋转、与2-3树有什么关联》.md", - "2020-08-27-面经手册 · 第7篇《ArrayList也这么多知识?一个指定位置插入就把谢飞机面晕了!》.md", - "2020-08-30-面经手册 · 第8篇《LinkedList插入速度比ArrayList快?你确定吗?》.md", - "2020-09-03-面经手册 · 第9篇《队列是什么?什么是双端队列、延迟对列、阻塞队列,全是知识盲区!》.md", - "2020-09-10-面经手册 · 第10篇《扫盲java.util.Collections工具包,学习排序、二分、洗牌、旋转算法》.md", - "2020-09-17-面经手册 · 第11篇《StringBuilder 比 String 快?空嘴白牙的,证据呢!》.md", - "2020-09-23-面经手册 · 第12篇《面试官,ThreadLocal 你要这么问,我就挂了!》.md" + "ddd-archetype.md", + "ddd-archetype-maven.md", ] }, { - title: "第 3 章 并发和锁", + title: "系统架构(10)", collapsable: false, sidebarDepth: 0, children: [ - "2020-10-14-面经手册 · 第13篇《除了JDK、CGLIB,还有3种类代理方式?面试又卡住!》.md", - "2020-10-21-面经手册 · 第14篇《volatile 怎么实现的内存可见?没有 volatile 一定不可见吗?》.md", - "2020-10-28-面经手册 · 第15篇《码农会锁,synchronized 解毒,剖析源码深度分析!》.md", - "2020-11-04-面经手册 · 第16篇《码农会锁,ReentrantLock之公平锁讲解和实现》.md", - "2020-11-11-面经手册 · 第17篇《码农会锁,ReentrantLock之AQS原理分析和实践使用》.md", - "2020-11-18-面经手册 · 第18篇《AQS 共享锁,Semaphore、CountDownLatch,听说数据库连接池可以用到!》.md" + "mvc.md", + "ddd-guide-00.md", + "ddd-guide-01.md", + "ddd-guide-02.md", + "ddd-guide-03.md", + "ddd.md", + "ddd-model.md", + "mvc2ddd.md", + "ddd-dev-account.md", + "ddd-dev-pay.md", ] }, { - title: "第 4 章 多线程", + title: "开发环境(12)", collapsable: false, sidebarDepth: 0, children: [ - "2020-11-25-面经手册 · 第19篇《Thread.start() ,它是怎么让线程启动的呢?》.md", - "2020-12-02-面经手册 · 第20篇《Thread 线程,状态转换、方法使用、原理分析》.md", - "2020-12-09-面经手册 · 第21篇《手写线程池,对照学习ThreadPoolExecutor线程池实现原理!》.md", - "2020-12-16-面经手册 · 第22篇《线程池的介绍和使用,以及基于jvmti设计非入侵监控》.md" + "tool.md", + "intellij-idea.md", + "maven.md", + "maven-central.md", + "git.md", + "github.md", + "gitcode.md", + "gitee.md", + "mac.md", + "nas.md", + "trae.md", + "joycode.md", ] }, { - title: "第 5 章 JVM 虚拟机", - collapsable: false, + title: "开发技术(19)", + collapsable: true, sidebarDepth: 0, children: [ - "2020-12-23-面经手册 · 第23篇《JDK、JRE、JVM,是什么关系?》.md", - "2020-12-30-面经手册 · 第24篇《为了搞清楚类加载,竟然手撸JVM!》.md", - "2021-01-06-面经手册 · 第25篇《JVM内存模型总结,有各版本JDK对比、有元空间OOM监控案例、有Java版虚拟机,综合学习更容易!》.md", - "2021-01-13-面经手册 · 第26篇《JVM故障处理工具,使用总结》.md", - "2021-01-20-面经手册 · 第27篇《JVM 判断对象已死,实践验证GC回收》.md", + "spring-dependency-injection.md", + "mybatis.md", + "dubbo.md", + "rocketmq.md", + "rabbitmq.md", + "kafka.md", + "quartz.md", + "mysql.md", + "mysql-time-zone.md", + "db-router.md", + "sharding-jdbc.md", + "connection-pool.md", + "zookeeper.md", + "redis.md", + "ignite.md", + "canal.md", + "springcloud-feign.md", + "springcloud-stream.md", + "springcloud-bus.md", + ] + }, + { + title: "授权框架(3)", + collapsable: true, + sidebarDepth: 0, + children: [ + "spring-security.md", + "spring-oauth2.md", + "spring-oauth2-sso-01.md", ] }, { - title: "第 6 章 Spring", - collapsable: false, + title: "常用类库(5)", + collapsable: true, sidebarDepth: 0, children: [ - "2021-03-30-面经手册 · 第28篇《你说,怎么把Bean塞到Spring容器》.md", - "2021-04-07-面经手册 · 第29篇《Spring IOC 特性有哪些,不会读不懂源码!》.md", - "2021-04-18-面经手册 · 第30篇《关于 Spring 中 getBean 的全流程源码解析》.md", - "2021-05-05-面经手册 · 第31篇《Spring Bean IOC、AOP 循环依赖解读》.md", + "fastjson.md", + "guava.md", + "http.md", + "ratelimiter.md", + "disruptor.md", ] - } - ] -} - -// java-develop-jvm -function genBarJavaDevelopJvm() { - return [ + }, { - title: "用Java实现JVM", - collapsable: false, + title: "智能组件(8)", + collapsable: true, sidebarDepth: 0, children: [ - "2019-05-01-用Java实现JVM第一章《命令行工具》.md", - "2019-05-02-用Java实现JVM第二章《搜索class文件》.md", - "2019-05-03-用Java实现JVM第三章《解析class文件》.md", - "2019-05-04-用Java实现JVM第三章《解析class文件》附[classReader拆解].md", - "2019-05-05-用Java实现JVM第四章《运行时数据区》.md", + "spring-ai.md", + "google-adk.md", + "agent-skill.md", + "a2a.md", + "draw.io.md", + "ai-ssh-opencode.md", + "github-models.md", + "openclaw.md", + ] + }, + { + title: "工程测试(4)", + collapsable: true, + sidebarDepth: 0, + children: [ + "mock.md", + "jmeter.md", + "intellij-idea-remote-jvm-debug.md", + "arex-test.md", + ] + }, + { + title: "质量监控(7)", + collapsable: true, + sidebarDepth: 0, + children: [ + "skywalking.md", + "grafana.md", + "elk.md", + "dump-mat.md", + "dump-visualvm.md", + "arthas.md", + "13scan-jdumpspider.md", + ] + }, + { + title: "发布部署(20)", + collapsable: true, + sidebarDepth: 0, + children: [ + "cloud-server.md", + "linux.md", + "1panel.md", + "docker-what.md", + "docker.md", + "docker-install.md", + "docker-deploy-project.md", + "portainer.md", + "aliyun-workbench.md", + "github-actions-workflows.md", + "buddy.md", + "private-docker-hub.md", + "docker-idea.md", + "jenkins.md", + "frp.md", + "nginx.md", + "ssl.md", + "ssl-httpsok.md", + "ollama.md", + "aigc.md", + ] + }, + { + title: "应用网关(4)", + collapsable: true, + sidebarDepth: 0, + children: [ + "higress.md", + "higress-ai.md", + "springcloud-gateway.md", + "apisix.md", + ] + } + ] +} + +// algorithm/data-structures +function genAlgorithmDataStructures() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "data-structures.md", + ] + }, + { + title: "线性数据结构", + collapsable: false, + sidebarDepth: 0, + children: [ + "2022-07-22-linked-list.md", + "2022-07-30-array-list.md", + "2022-08-06-queue.md", + "2022-08-17-stack.md", + "2022-08-27-hash-table.md", + ] + }, + { + title: "树形数据结构", + collapsable: false, + sidebarDepth: 0, + children: [ + "2022-09-03-heap.md", + "2022-09-14-trie.md", + "2022-09-18-tree.md", + "2022-09-26-tree-avl.md", + "2022-10-01-tree-2-3.md", + "2022-10-02-tree-red-black.md", + "2022-10-04-disjoint-set.md", + ] + }, + { + title: "图论", + collapsable: false, + sidebarDepth: 0, + children: [ + "2022-10-03-graph.md", + ] + }, + { + title: "其他", + collapsable: false, + sidebarDepth: 0, + children: [ + "2022-10-05-bloom-filter.md", + ] + } + ] +} + +// algorithm/logic +function genAlgorithmLogic() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "math/math.md", + ] + }, + { + title: "数学", + collapsable: false, + sidebarDepth: 0, + children: [ + "math/2022-10-30-bits.md", + "math/2022-10-30-factorial.md", + "math/2022-11-05-fibonacci.md", + "math/2022-11-20-primality.md", + "math/2022-11-28-euclidean.md", + "math/2022-12-04-least-common-multiple.md", + "math/2022-12-11-sieve-of-eratosthenes.md", + "math/2022-12-12-is-power-of-two.md", + "math/2022-12-18-pascal-triangle.md", + "math/2022-12-23-radian.md", + "math/2023-01-08-fast-powering.md", + "math/2023-01-08-integer-partition.md", + "math/2023-01-09-liu-hui.md", + "math/2023-01-09-fourier-transform.md", + ] + }, + { + title: "集合", + collapsable: false, + sidebarDepth: 0, + children: [ + "sets/2023-02-09-cartesian-product.md", + "sets/2023-02-10-fisher-yates.md", + "sets/2023-02-11-power-set.md", + "sets/2023-02-12-permutations.md", + "sets/2023-02-13-combinations.md", + ] + }, + { + title: "刷题", + collapsable: false, + sidebarDepth: 0, + children: [ + "leetcode/2020-03-14-野路子搞算法《两数之和》,带着小白刷面试.md", + "leetcode/2020-03-18-无重复字符的最长子串.md", + ] + } + ] +} + +// algorithm/model +function genAlgorithmModel() { + return [ + { + title: "机器学习", + collapsable: false, + sidebarDepth: 0, + children: [ + "2023-02-12-chat-gpt.md", + "2023-02-18-gpt2-chitchat.md", + "2023-05-21-chatglm-6b.md", + "autoglm-phone-agent.md", + ] + } + ] +} + +// java-interview +function genBarJavaInterview() { + return [ + { + title: "第 1 章 谈谈面试", + collapsable: false, + sidebarDepth: 0, + children: [ + "2020-07-28-面经手册 · 开篇《面试官都问我啥》.md", + "2020-07-30-面经手册 · 第1篇《认知自己的技术栈盲区》.md", + "2021-03-07-面试现场:小伙伴美团一面的分享和分析[含解答].md" + ] + }, + { + title: "第 2 章 数据结构和算法", + collapsable: false, + sidebarDepth: 0, + children: [ + "2020-08-04-面经手册 · 第2篇《数据结构,HashCode为什么使用31作为乘数?》.md", + "2020-08-07-面经手册 · 第3篇《HashMap核心知识,扰动函数、负载因子、扩容链表拆分,深度学习》.md", + "2020-08-13-面经手册 · 第4篇《HashMap数据插入、查找、删除、遍历,源码分析》.md", + "2020-08-16-面经手册 · 第5篇《看图说话,讲解2-3平衡树「红黑树的前身」》.md", + "2020-08-20-面经手册 · 第6篇《带着面试题学习红黑树操作原理,解析什么时候染色、怎么进行旋转、与2-3树有什么关联》.md", + "2020-08-27-面经手册 · 第7篇《ArrayList也这么多知识?一个指定位置插入就把谢飞机面晕了!》.md", + "2020-08-30-面经手册 · 第8篇《LinkedList插入速度比ArrayList快?你确定吗?》.md", + "2020-09-03-面经手册 · 第9篇《队列是什么?什么是双端队列、延迟对列、阻塞队列,全是知识盲区!》.md", + "2020-09-10-面经手册 · 第10篇《扫盲java.util.Collections工具包,学习排序、二分、洗牌、旋转算法》.md", + "2020-09-17-面经手册 · 第11篇《StringBuilder 比 String 快?空嘴白牙的,证据呢!》.md", + "2020-09-23-面经手册 · 第12篇《面试官,ThreadLocal 你要这么问,我就挂了!》.md" + ] + }, + { + title: "第 3 章 并发和锁", + collapsable: false, + sidebarDepth: 0, + children: [ + "2020-10-14-面经手册 · 第13篇《除了JDK、CGLIB,还有3种类代理方式?面试又卡住!》.md", + "2020-10-21-面经手册 · 第14篇《volatile 怎么实现的内存可见?没有 volatile 一定不可见吗?》.md", + "2020-10-28-面经手册 · 第15篇《码农会锁,synchronized 解毒,剖析源码深度分析!》.md", + "2020-11-04-面经手册 · 第16篇《码农会锁,ReentrantLock之公平锁讲解和实现》.md", + "2020-11-11-面经手册 · 第17篇《码农会锁,ReentrantLock之AQS原理分析和实践使用》.md", + "2020-11-18-面经手册 · 第18篇《AQS 共享锁,Semaphore、CountDownLatch,听说数据库连接池可以用到!》.md" + ] + }, + { + title: "第 4 章 多线程", + collapsable: false, + sidebarDepth: 0, + children: [ + "2020-11-25-面经手册 · 第19篇《Thread.start() ,它是怎么让线程启动的呢?》.md", + "2020-12-02-面经手册 · 第20篇《Thread 线程,状态转换、方法使用、原理分析》.md", + "2020-12-09-面经手册 · 第21篇《手写线程池,对照学习ThreadPoolExecutor线程池实现原理!》.md", + "2020-12-16-面经手册 · 第22篇《线程池的介绍和使用,以及基于jvmti设计非入侵监控》.md" + ] + }, + { + title: "第 5 章 JVM 虚拟机", + collapsable: false, + sidebarDepth: 0, + children: [ + "2020-12-23-面经手册 · 第23篇《JDK、JRE、JVM,是什么关系?》.md", + "2020-12-30-面经手册 · 第24篇《为了搞清楚类加载,竟然手撸JVM!》.md", + "2021-01-06-面经手册 · 第25篇《JVM内存模型总结,有各版本JDK对比、有元空间OOM监控案例、有Java版虚拟机,综合学习更容易!》.md", + "2021-01-13-面经手册 · 第26篇《JVM故障处理工具,使用总结》.md", + "2021-01-20-面经手册 · 第27篇《JVM 判断对象已死,实践验证GC回收》.md", + ] + }, + { + title: "第 6 章 Spring", + collapsable: false, + sidebarDepth: 0, + children: [ + "2021-03-30-面经手册 · 第28篇《你说,怎么把Bean塞到Spring容器》.md", + "2021-04-07-面经手册 · 第29篇《Spring IOC 特性有哪些,不会读不懂源码!》.md", + "2021-04-18-面经手册 · 第30篇《关于 Spring 中 getBean 的全流程源码解析》.md", + "2021-05-05-面经手册 · 第31篇《Spring Bean IOC、AOP 循环依赖解读》.md", + ] + } + ] +} + +// java-develop-jvm +function genBarJavaDevelopJvm() { + return [ + { + title: "用Java实现JVM", + collapsable: false, + sidebarDepth: 0, + children: [ + "2019-05-01-用Java实现JVM第一章《命令行工具》.md", + "2019-05-02-用Java实现JVM第二章《搜索class文件》.md", + "2019-05-03-用Java实现JVM第三章《解析class文件》.md", + "2019-05-04-用Java实现JVM第三章《解析class文件》附[classReader拆解].md", + "2019-05-05-用Java实现JVM第四章《运行时数据区》.md", "2019-05-06-用Java实现JVM第五章《指令集和解释器》.md", "2019-05-07-用Java实现JVM第六章《类和对象》.md", "2019-05-08-用Java实现JVM第七章《方法调用和返回》.md", @@ -652,20 +1075,6 @@ function genBarJavaCore() { ] } -// other -function genBarOther() { - return [ - { - title: "阅读指南", - collapsable: false, - sidebarDepth: 2, - children: [ - "guide-to-reading.md" - ] - } - ] -} - // spring-develop-mybatis function genBarSpringDevelopMybatis() { return [ @@ -731,6 +1140,7 @@ function genBarSpringDevelopMybatis() { children: [ "2022-07-07-第21章:完结.md", "2022-07-15-第22章:Mybatis设计模式.md", + "2024-04-28-mybatis-source-code-analysis-diagram.md", ] } ] @@ -839,6 +1249,7 @@ function genBarDevelopDesignPattern() { sidebarDepth: 0, children: [ "2022-03-12-重学Java设计模式B站视频.md", + "2024-08-25-chain-tree.md", ] }, { @@ -891,12 +1302,24 @@ function genBarDevelopDesignPattern() { function genBarDevOPS() { return [ { - title: "Dev-OPS", + title: "环境配置", collapsable: false, sidebarDepth: 0, children: [ + "2023-04-18-tool.md", + "2023-04-18-docker.md", + "2023-04-18-portainer.md", + "2023-04-18-nginx.md", + "2024-03-23-yun.md", "2019-08-12-windows环境下安装elasticsearch6.2.2.md", "2019-08-13-elasticsearch-head插件安装.md", + ] + }, + { + title: "服务部署", + collapsable: false, + sidebarDepth: 0, + children: [ "2019-11-23-并不想吹牛皮,但!为了把Github博客粉丝转移到公众号,我干了!.md", "2020-03-28-GithubAndMyBlogAttacked.md", "2020-04-25-《Netty+JavaFx实战:仿桌面版微信聊天》代码开源+上云部署+视频讲解.md", @@ -907,6 +1330,8 @@ function genBarDevOPS() { "2021-11-01-迁移vuepress博客踩坑经历.md", "2021-11-07-关于怎么使用 webhooks 自动部署博客,详细教程文档!.md", "2022-03-04-教小白使用 docsify,搭建一个贼简单的所见即所得博客!.md", + "2023-03-25-免费部署部署ChatGPT.md", + "2024-01-30-vuepress-resume-blog.md", ] } ] @@ -948,6 +1373,7 @@ function genBarDevelopFramework() { "scheme/2021-07-19-调研字节码插桩技术,用于系统监控设计和实现.md", "scheme/2022-02-14-基于库表分段扫描和数据Redis预热,优化分布式延迟任务触达时效性.md", "scheme/2022-02-21-怎么说服领导,能让我用DDD架构.md", + "scheme/2024-06-19-通过可重入锁思想,设计MQ迁移方案.md", ] } ] @@ -1116,11 +1542,14 @@ function genApiGateway() { collapsable: false, sidebarDepth: 0, children: [ + "api-gateway.md", "2022-08-12-开篇:如果让我设计一套,TPS百万级API网关.md", + "2023-06-10-API 网关 - 媲美美团这套Shepherd网关架构!.md", + "notes.md", ] }, { - title: "第 1 部分 - 通信", + title: "第 1 部分 - 通信组件", collapsable: false, sidebarDepth: 0, children: [ @@ -1130,47 +1559,112 @@ function genApiGateway() { "2022-09-04-第4章:将连接抽象为数据源.md", "2022-09-10-第5章:HTTP请求参数解析.md", "2022-09-17-第6章:引入执行器封装服务调用.md", + "2022-09-25-第7章:权限认证组件.md", + "2022-10-15-第8章:网关会话鉴权处理.md", + "2022-12-04-第16章:网络通信配置提取.md", ] - } - ]; -} - -// netty 4.x -function genBarNetty() { - return [ + }, { - title: "基础入门篇", + title: "第 2 部分 - 注册中心", collapsable: false, sidebarDepth: 0, children: [ - "base/2019-07-30-netty案例,netty4.1基础入门篇零《初入JavaIO之门BIO、NIO、AIO实战练习》.md", - "base/2019-08-01-netty案例,netty4.1基础入门篇一《嗨!NettyServer》.md", - "base/2019-08-05-netty案例,netty4.1基础入门篇二《NettyServer接收数据》.md", - "base/2019-08-06-netty案例,netty4.1基础入门篇三《NettyServer字符串解码器》.md", - "base/2019-08-07-netty案例,netty4.1基础入门篇四《NettyServer收发数据》.md", - "base/2019-08-08-netty案例,netty4.1基础入门篇五《NettyServer字符串编码器》.md", - "base/2019-08-09-netty案例,netty4.1基础入门篇六《NettyServer群发消息》.md", - "base/2019-08-10-netty案例,netty4.1基础入门篇七《嗨!NettyClient》.md", - "base/2019-08-11-netty案例,netty4.1基础入门篇八《NettyClient半包粘包处理、编码解码处理、收发数据方式》.md", - "base/2019-08-12-netty案例,netty4.1基础入门篇九《自定义编码解码器,处理半包、粘包数据》.md", - "base/2019-08-13-netty案例,netty4.1基础入门篇十《关于ChannelOutboundHandlerAdapter简单使用》.md", - "base/2019-08-14-netty案例,netty4.1基础入门篇十一《netty udp通信方式案例Demo》.md", - "base/2019-08-15-netty案例,netty4.1基础入门篇十二《简单实现一个基于Netty搭建的Http服务》.md" + "2022-10-22-第9章:网关注册中心服务初始创建.md", + "2022-10-29-第10章:网关注册中心库表结构设计.md", + "2022-11-06-第11章:网关注册算力节点领域服务实现.md", + "2022-11-13-第12章:网关注册服务接口领域服务实现.md", + "2022-11-26-第14章:网关映射聚合信息查询实现.md", ] }, { - title: "中级拓展篇", + title: "第 3 部分 - 服务发现", collapsable: false, sidebarDepth: 0, children: [ - "expand/2019-08-16-netty案例,netty4.1中级拓展篇一《Netty与SpringBoot整合》.md", - "expand/2019-08-17-netty案例,netty4.1中级拓展篇二《Netty使用Protobuf传输数据》.md", - "expand/2019-08-18-netty案例,netty4.1中级拓展篇三《Netty传输Java对象》.md", - "expand/2019-08-19-netty案例,netty4.1中级拓展篇四《Netty传输文件、分片发送、断点续传》.md", - "expand/2019-08-20-netty案例,netty4.1中级拓展篇五《基于Netty搭建WebSocket,模仿微信聊天页面》.md", - "expand/2019-08-21-netty案例,netty4.1中级拓展篇六《SpringBoot+Netty+Elasticsearch收集日志信息数据存储》.md", - "expand/2019-08-22-netty案例,netty4.1中级拓展篇七《Netty请求响应同步通信》.md", - "expand/2019-08-23-netty案例,netty4.1中级拓展篇八《Netty心跳服务与断线重连》.md", + "2022-11-20-第13章:服务发现组件搭建和注册网关连接.md", + "2022-11-26-第15章:服务配置拉取和组件使用验证.md", + "2022-12-04-第17章:核心通信组件管理和处理服务映射.md", + "2022-12-10-第18章:容器关闭监听和异常管理.md", + "2023-01-01-第22章:订阅服务注册消息驱动网关映射.md", + "2023-02-11-第25章:网关Nginx负载模型配置.md", + "2023-02-25-第26章:动态刷新网关Nginx负载均衡配置.md", + "2023-03-04-第27章:实现网关算力节点动态负载功能.md", + ] + }, + { + title: "第 4 部分 - 镜像文件", + collapsable: false, + sidebarDepth: 0, + children: [ + "2022-12-10-第19章:网关引擎打包镜像部署.md", + ] + }, + { + title: "第 5 部分 - 服务注册", + collapsable: false, + sidebarDepth: 0, + children: [ + "2022-12-17-第20章:服务注册组件搭建采集接口信息.md", + "2022-12-24-第21章:应用服务接口注册到注册中心.md", + ] + }, + { + title: "第 6 部分 - 运营后台", + collapsable: false, + sidebarDepth: 0, + children: [ + "2023-01-15-第23章:网关运营管理后台框架搭建.md", + "2023-01-26-第24章:前后端分离应用的跨域接口调用.md", + ] + }, + { + title: "第 7 部分 - 扩展功能", + collapsable: false, + sidebarDepth: 0, + children: [ + "2023-03-11-第28章:网关组件工程模块合并.md", + "2023-03-18-第29章:功能完善,算力关联、接口上报、调用反馈.md", + ] + } + ]; +} + +// netty 4.x +function genBarNetty() { + return [ + { + title: "基础入门篇", + collapsable: false, + sidebarDepth: 0, + children: [ + "base/2019-07-30-netty案例,netty4.1基础入门篇零《初入JavaIO之门BIO、NIO、AIO实战练习》.md", + "base/2019-08-01-netty案例,netty4.1基础入门篇一《嗨!NettyServer》.md", + "base/2019-08-05-netty案例,netty4.1基础入门篇二《NettyServer接收数据》.md", + "base/2019-08-06-netty案例,netty4.1基础入门篇三《NettyServer字符串解码器》.md", + "base/2019-08-07-netty案例,netty4.1基础入门篇四《NettyServer收发数据》.md", + "base/2019-08-08-netty案例,netty4.1基础入门篇五《NettyServer字符串编码器》.md", + "base/2019-08-09-netty案例,netty4.1基础入门篇六《NettyServer群发消息》.md", + "base/2019-08-10-netty案例,netty4.1基础入门篇七《嗨!NettyClient》.md", + "base/2019-08-11-netty案例,netty4.1基础入门篇八《NettyClient半包粘包处理、编码解码处理、收发数据方式》.md", + "base/2019-08-12-netty案例,netty4.1基础入门篇九《自定义编码解码器,处理半包、粘包数据》.md", + "base/2019-08-13-netty案例,netty4.1基础入门篇十《关于ChannelOutboundHandlerAdapter简单使用》.md", + "base/2019-08-14-netty案例,netty4.1基础入门篇十一《netty udp通信方式案例Demo》.md", + "base/2019-08-15-netty案例,netty4.1基础入门篇十二《简单实现一个基于Netty搭建的Http服务》.md" + ] + }, + { + title: "中级拓展篇", + collapsable: false, + sidebarDepth: 0, + children: [ + "expand/2019-08-16-netty案例,netty4.1中级拓展篇一《Netty与SpringBoot整合》.md", + "expand/2019-08-17-netty案例,netty4.1中级拓展篇二《Netty使用Protobuf传输数据》.md", + "expand/2019-08-18-netty案例,netty4.1中级拓展篇三《Netty传输Java对象》.md", + "expand/2019-08-19-netty案例,netty4.1中级拓展篇四《Netty传输文件、分片发送、断点续传》.md", + "expand/2019-08-20-netty案例,netty4.1中级拓展篇五《基于Netty搭建WebSocket,模仿微信聊天页面》.md", + "expand/2019-08-21-netty案例,netty4.1中级拓展篇六《SpringBoot+Netty+Elasticsearch收集日志信息数据存储》.md", + "expand/2019-08-22-netty案例,netty4.1中级拓展篇七《Netty请求响应同步通信》.md", + "expand/2019-08-23-netty案例,netty4.1中级拓展篇八《Netty心跳服务与断线重连》.md", "expand/2019-08-24-netty案例,netty4.1中级拓展篇九《Netty集群部署实现跨服务端通信的落地方案》.md", "expand/2019-08-25-netty案例,netty4.1中级拓展篇十《Netty接收发送多种协议消息类型的通信处理方案》.md", "expand/2019-08-26-netty案例,netty4.1中级拓展篇十一《Netty基于ChunkedStream数据流切块传输》.md", @@ -1381,6 +1875,11 @@ function getBarZSXQ() { sidebarDepth: 1, children: [ "introduce.md", + "material/guide.md", + // "material/student-learn-all.md", + "material/student-learn-line.md", + // "material/student-learn-recruit.md", + "material/student-learn-advanced.md", ] }, { @@ -1388,21 +1887,54 @@ function getBarZSXQ() { collapsable: false, sidebarDepth: 0, children: [ - "material/guide.md", "material/architecture_design.md", "material/interview.md", + "material/dialogue-skills.md", + "material/speaking-skills.md", + "material/notes.md", "material/study-experience.md", "material/exam.md", ] }, { - title: "实战项目", + title: "创新项目(AI)", collapsable: false, sidebarDepth: 0, children: [ + "project/ai-agent-scaffold.md", + "project/ai-mcp-gateway.md", + "project/ai-knowledge.md", + "project/openai-code-review.md", + "project/chatgpt.md", + "project/chatbot-api.md", + "project/openai-sdk-java.md", + ] + }, + { + title: "业务项目", + collapsable: false, + sidebarDepth: 0, + children: [ + "project/group-buy-market.md", + "project/s-pay-mall.md", + "project/big-market.md", "project/lottery.md", "project/im.md", + ] + }, + { + title: "组件项目", + collapsable: false, + sidebarDepth: 0, + children: [ + "project/local-task-message.md", + "project/xfg-wrench.md", + "project/business-behavior-monitor.md", + "project/dynamic-thread-pool.md", + "project/ltzf-sdk-java.md", "project/api-gateway.md", + "project/springboot-starter.md", + "booklet/idea-plugin.md", ] }, { @@ -1411,8 +1943,9 @@ function getBarZSXQ() { sidebarDepth: 0, children: [ "booklet/java-interview.md", - "booklet/idea-plugin.md", "booklet/bytecode.md", + "booklet/java-design.md", + "booklet/data-structures.md", ] }, { @@ -1420,8 +1953,17 @@ function getBarZSXQ() { collapsable: true, sidebarDepth: 0, children: [ - "source-code/develop-spring.md", "source-code/develop-mybatis.md", + // "source-code/develop-spring.md", + ] + }, + { + title: "其他内容", + collapsable: true, + sidebarDepth: 1, + children: [ + "project/bug-code.md", + "material/openai.md", ] }, { @@ -1439,10 +1981,76 @@ function getBarZSXQ() { children: [ "other/join.md", ] + }, + { + title: "星球日记", + collapsable: true, + sidebarDepth: 0, + children: [ + "memorabilia/sideline.md", + "memorabilia/seven-thousand.md", + "memorabilia/ten-thousand.md", + "memorabilia/overall.md", + "memorabilia/biographical-notes.md", + "memorabilia/interview-zijie.md", + "memorabilia/110000-lines-of-code.md", + "memorabilia/java-resume-Project.md", + "memorabilia/student-offer.md", + "memorabilia/project-plan-v2406.md", + "memorabilia/xiaofuge-team.md", + "memorabilia/java-interview-experience.md", + "memorabilia/campus-recruitment-offer.md", + "memorabilia/job-hire-jd.md", + ] } ] } +function getBarProduct() { + return [ + { + title: "AI IDE", + collapsable: false, + sidebarDepth: 0, + children: [ + "software/walicode.md", + ] + }, + { + title: "出版物", + collapsable: false, + sidebarDepth: 0, + children: [ + "book/design-pattern.md", + "book/mybatis.md", + ] + }, + { + title: "电子书", + collapsable: false, + sidebarDepth: 0, + children: [ + "pdf/2020-05-17-小傅哥出书了《字节码编程》免费拿!.md", + "pdf/2020-07-12-重学 Java 设计模式.md", + "pdf/2020-10-04-《Java面经手册》PDF数据结构篇, 肝完出炉了!来吧,这本书帮你拿最贵的offer!.md", + "pdf/2021-01-26-Java面经手册PDF下载.md", + "pdf/2022-01-23-IDEA Plugin 开发手册.md", + ] + }, + { + title: "插件", + collapsable: false, + sidebarDepth: 0, + children: [ + "idea-plugin/vo2dto.md", + "idea-plugin/vo2dto-v2.5.1.md", + "idea-plugin/vo2dto-v2.5.5.md", + ] + }, + + ] +} + // project im function getBarProjectIM() { return [ @@ -1503,6 +2111,33 @@ function getBarProjectIM() { ]; } +function getBarProjectChatBotApi() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "chatbot-api.md", + ] + }, + { + title: "课程", + collapsable: false, + sidebarDepth: 0, + children: [ + "第1节:工程创建和仓库使用.md", + "第2节:创建知识星球,爬取接口信息.md", + "第3节:知识星球接口领域服务开发.md", + "第4节:对接ChatGPT,调用接口.md", + "第5节:整合知识星球与ChatGPT,完成自动化回答.md", + "第6节:部署服务到 Docker 容器.md", + "第7节:多组任务服务配置.md", + ] + }, + ]; +} + // project springboot-middleware function getBarProjectSpringBootMiddleware() { return [ @@ -1578,6 +2213,695 @@ function getBarProjectSpringBootMiddleware() { ]; } +function getBarProjectChatGPT() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "chatgpt.md", + "引言.md", + "notes.md", + "review.md", + ] + }, + { + title: "Dev-Ops", + collapsable: false, + sidebarDepth: 0, + children: [ + "dev-ops/第1节:push工程到仓库.md", + "dev-ops/第2节:Docker环境安装.md", + "dev-ops/第3节:Portainer环境安装.md", + "dev-ops/第4节:Nginx环境配置.md", + "dev-ops/第5节:服务镜像构建和容器部署.md", + "dev-ops/第6节:前后端构建镜像部署.md", + "dev-ops/第7节:网站添加百度统计.md", + "dev-ops/第8节:应用监控.md", + "dev-ops/第9节:部署上线.md", + ] + }, + { + title: "ChatGPT-API", + collapsable: false, + sidebarDepth: 0, + children: [ + "api/第1节:API工程搭建和简单访问认证.md", + "api/第2节:Shiro登录授权发放访问token.md", + "api/第3节:微信公众号验签和初步对接OpenAI.md", + "api/第4节:工程重构和流式异步响应接口实现.md", + "api/第5节:公众号发送验证码鉴权登录.md", + "api/第6节:白名单和敏感词规则过滤.md", + "api/第7节:用户额度账户领域实现.md", + "api/第8节:商品下单对接微信支付.md", + "api/第9节:OpenAi多渠道策略模式.md", + "api/第10节:应用分布式设计.md", + "api/第11节:dall-e文生图.md", + ] + }, + { + title: "ChatGPT-SDK", + collapsable: false, + sidebarDepth: 0, + children: [ + "sdk/第1节:ChatGPT-SDK组件工程简单功能实现.md", + "sdk/第2节:流式应答会话设计实现.md", + "sdk/第3节:完善实现各类常用接口.md", + "sdk/第4节:支持多渠道对话.md", + ] + }, + { + title: "ChatGLM-SDK", + collapsable: false, + sidebarDepth: 0, + children: [ + "sdk/chatglm-sdk-java.md", + "sdk/chatglm-sdk-java-v2.md", + ] + }, + { + title: "ChatGPT-WEB", + collapsable: false, + sidebarDepth: 0, + children: [ + "web/第1节:Web页面工程初始化.md", + "web/第2节:工具栏面板.md", + "web/第3节:按钮定义与事件实现.md", + "web/第4节:对话框列表.md", + "web/第5节:对话框消息.md", + "web/第6节:完善对话处理.md", + "web/第7节:对话角色设定.md", + "web/第8节:流式接口对接.md", + "web/第9节:公众号扫码登录.md", + "web/第10节:商品支付页.md", + ] + }, + { + title: "番外 - 课程阶段产物", + collapsable: false, + sidebarDepth: 0, + children: [ + "extra/ChatGPT-v1.0.md", + "extra/ChatGPT-v1.1.md", + "extra/ChatGPT-v1.2.md", + "extra/ChatGPT-v1.3.md", + ] + } + ] +} + +function getBarAIMCPGateway() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "ai-mcp-gateway.md", + "promotion/ai-mcp-gateway-stage-completion.md", + ] + }, + { + title: "第1部分:系统设计", + collapsable: false, + sidebarDepth: 0, + children: [ + "第1-1节:网关需求分析.md", + "第1-2节:系统建模设计.md", + "第1-3节:网关协议表.md", + "第1-4节:升级网关库表.md", + ] + }, + { + title: "第2部分:协议分析", + collapsable: false, + sidebarDepth: 0, + children: [ + "第2-1节:MCP服务实现.md", + "第2-2节:MCP代理调用.md", + "第2-3节:MCP通信协议.md", + ] + }, + { + title: "第3部分:网关实现", + collapsable: false, + sidebarDepth: 0, + children: [ + "第3-1节:工程初始化创建.md", + "第3-2节:会话管理服务实现.md", + "第3-3节:会话接口编排.md", + "第3-4节:会话消息结构设计.md", + "第3-5节:消息协议处理案例.md", + "第3-6节:基础层数据处理.md", + "第3-7节:协议消息处理-Initialize.md", + "第3-8节:协议消息处理-ToolsList.md", + "第3-9节:协议消息处理-ToolsCall.md", + "第3-10节:评审库表升级代码.md", + "第3-11节:会话内容编排处理.md", + "第3-12节:鉴权功能领域服务.md", + "第3-13节:鉴权功能编排处理.md", + "第3-14节:解析Swagger标准OpenAPI协议.md", + "第3-15节:协议域-协议解析处理.md", + "第3-16节:协议域-协议存储处理.md", + "第3-17节:网关域-配置数据存储(CRUD).md", + "第3-18节:管理端-API功能编排串联.md", + "第3-19节:管理端-API与UI对接.md", + "第3-20节:验证服务,LLM对接测试MCP接口.md", + "none.md", + ] + }, + ] +} + +function getBarLocalTaskMessage() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "local-task-message.md", + ] + }, + { + title: "课程", + collapsable: false, + sidebarDepth: 0, + children: [ + "第1节:组件需求分析.md", + "第2节:SpringEvent事件消息.md", + "第3节:任务表设计和数据写入.md", + "第4节:通知策略处理.md", + "第5节:动态任务补偿处理.md", + "第6节:切面拦截任务操作.md", + ] + } + ] +} + +function getBarAIAgentScaffold() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "ai-agent-scaffold.md", + "notes.md", + ] + }, + { + title: "1阶段 - 做设计", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-1/第1-1节:脚手架需求分析.md", + "part-1/第1-2节:系统架构设计.md", + ] + }, + { + title: "2阶段 - 智能体", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-2/第2-1节:工程初始化创建.md", + "part-2/第2-2节:Api功能测试.md", + "part-2/第2-3节:智能体配置表设计.md", + "part-2/第2-4节:装配域结构化定义.md", + "part-2/第2-5节:装配域节点-AiApiNode.md", + "part-2/第2-6节:装配域节点-ChatModelNode.md", + "part-2/第2-7节:装配域节点-AgentNode.md", + "part-2/第2-8节:装配域节点-AgentWorkflowNode.md", + "part-2/第2-9节:装配域节点-Loop、Parallel、Sequential.md", + "part-2/第2-10节:装配域节点-RunnerNode.md", + "part-2/第2-11节:智能体加载使用验证.md", + "part-2/第2-12节:增强装配-RunnerNode.md", + "part-2/第2-13节:增强装配-AgentWorkflowNode.md", + "part-2/第2-14节:增强装配-本地mcp.md", + "part-2/第2-15节:增强装配-回调plugin.md", + "part-2/第2-16节:fix-多模态能力使用.md", + "part-2/第2-17节:会话服务接口实现-service.md", + "part-2/第2-18节:会话服务接口实现-trigger.md", + "part-2/第2-19节:会话服务接口对接-ui.md", + "part-2/第2-20节:增强装配-skills.md", + ] + }, + { + title: "3阶段 - 脚手架", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-3/第3-1节:Maven脚手架配置.md", + "part-3/第3-2节:上传jar到maven仓库.md", + "part-3/第3-3节:部署脚手架网页.md", + ] + }, + { + title: "4阶段 - 场景 - draw.io", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-4/第4-0节:ai + draw.io 产品设计.md", + "part-4/第4-1节:初始化工程搭建.md", + "part-4/第4-2节:在页面嵌入draw.io组件和对话框.md", + "part-4/第4-3节:智能体API接口对接.md", + "part-4/第4-4节:AI+用户+DrawIO,交互式画图.md", + "part-4/第4-5节:ai-draw-io,云服务器部署.md", + ] + }, + { + title: "5阶段 - 场景 - MobileOpenClaw", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-5/第5-1节:初始化工程搭建.md", + "part-5/第5-2节:手机网关动作调度设计.md", + "part-5/第5-3节:服务端网络通信设计(Netty).md", + "part-5/第5-4节:初步通过智能体,操作手机设备.md", + "part-5/第5-5节:智能体工作流设计.md", + "part-5/第5-6节:智能体异步响应展示执行过程.md", + "part-5/第5-7节:使用AutoGLM-Phone-9B构建手机智能体.md", + "part-5/第5-8节:多版本安卓版本策略支持.md", + "part-5/第5-9节:会话上下文细化处理.md", + ] + } + ] +} + +function getBarAiRagKnowledge() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "ai-knowledge.md", + "notes.md", + ] + }, + { + title: "1阶段 RAG spring-ai v0.8.1", + collapsable: false, + sidebarDepth: 0, + children: [ + "第1节:AI RAG 知识库,项目介绍.md", + "第2节:初始化知识库工程&提交代码.md", + "第3接:Ollama DeepSeek 流式应答接口实现.md", + "第4节:Ollama DeepSeek 流式应答页面对接.md", + "第5节:Ollama RAG 知识库上传、解析和验证.md", + "第6节:Ollama RAG 知识库接口服务实现.md", + "第7节:基于AI工具,设计知识库UI和接口对接.md", + "第8节:Git仓库代码库解析到知识库.md", + "第9节:扩展OpenAI模型对接,以及完整AI对接.md", + "第10节:云服务器部署知识库(Docker、Nginx).md", + ] + }, + { + title: "2阶段 MCP spring-ai v1.0.0", + collapsable: false, + sidebarDepth: 0, + children: [ + "第11节:吃上细糠,升级SpringAI框架.md", + "第12节:康庄大道,上手 AI MCP 工作流.md", + "第13节,道山学海,实现MCP自动发帖服务.md", + "第14节:海纳百川,上线MCP自动发帖服务.md", + "第15节:川流不息,实现MCP微信公众号消息通知服务.md", + "第16节:息息相通,MCP 服务部署上线(sse 模式).md", + ] + }, + { + title: "3阶段 Agent spring-ai v1.0.0", + collapsable: false, + sidebarDepth: 0, + children: [ + "agent/第3-0节:AiAgent 项目介绍和系统演示.md", + "agent/第3-1节:Ai Agent 业务流程、系统架构、库表设计说明.md", + "agent/第3-2节:初始化项目工程.md", + "agent/第3-3节:AiAgent测试案例.md", + "agent/第3-4节:根据AiAgent案例,设计库表.md", + "agent/第3-5节:多数据源和Mapper配置.md", + "agent/第3-6节:数据加载模型设计.md", + "agent/第3-7节:动态实例化客户端API.md", + "agent/第3-8节:动态实例化对话模型.md", + "agent/第3-9节:动态实例化对话客户端.md", + "agent/第3-10节:Agent执行链路分析.md", + "agent/第3-11节:Agent执行链路设计.md", + "agent/第3-12节:Agent服务接口和UI对接.md", + "agent/第3-13节,Agent-ELK日志分析场景.md", + "agent/第3-14节,Agent-Prometheus监控分析场景.md", + "agent/第3-15节:FlowAgent执行链路分析.md", + "agent/第3-16节:FlowAgent执行链路设计.md", + "agent/第3-17节:增加调度器策略执行Agent链路.md", + "agent/第3-18节:动态执行智能体任务.md", + "agent/第3-19节:拖拉拽编排数据存储.md", + "agent/第3-20节:Agent管理后台实现.md", + "agent/第3-21节:在云服务器部署上线.md", + ] + }, + { + title: "扩展", + collapsable: false, + sidebarDepth: 0, + children: [ + "ext/ai-agent-flowgram.md", + "ext/ai-agent-mcp-auth.md", + "ext/ai-agent-auto.md", + "ext/ai-agent-notes.md", + "ext/ai-agent-job.md", + "promotion/AI MCP 已经帮我“干活”了!.md", + "promotion/AI MCP 再这么用下去,可就要”创业“变现了呀!.md", + ] + } + ] +} + +function getBarGroupBuyMarket() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "group-buy-market.md", + "notes.md", + "qa.md", + ] + }, + { + title: "第1部分:系统设计", + collapsable: false, + sidebarDepth: 0, + children: [ + "第1-1节:拼团需求分析.md", + "第1-2节:拼团库表设计.md", + "第1-3节:研发系统设计.md", + ] + }, + { + title: "第2部分:服务实现", + collapsable: false, + sidebarDepth: 0, + children: [ + "第2-1节:初始工程搭建.md", + "第2-2节:试算模型抽象模板设计.md", + "第2-3节:多线程异步数据加载.md", + "第2-4节:策略模式优惠折扣计算.md", + "第2-5节:人群标签数据采集.md", + "第2-6节:拆分库表关联关系.md", + "第2-7节:人群标签节点过滤.md", + "第2-8节:动态配置开关操作.md", + "第2-9节:拼团交易营销锁单.md", + "第2-10节:责任链抽象模板设计.md", + "第2-11节:交易规则责任链过滤.md", + "第2-12节:拼团组队结算统计.md", + "第2-13节:交易结算责任链过滤.md", + "第2-14节:拼团回调通知任务.md", + "第2-15节:根据UI展示封装接口.md", + "第2-16节:引入RabbitMQ分布式多端消费.md", + "第2-17节:发送MQ结算消息.md", + "第2-18节:消费MQ结算消息.md", + "第2-19节:独占锁和无锁化场景运用.md", + "第2-20节:函数式数据缓存和降级到DB处理.md", + "第2-21节:引入扳手工程.md", + "第2-22节:动态限流配置.md", + "第2-23节:ELK+AI MCP检索.md", + "第2-24节:系统监控+AIMCP分析.md", + "第2-25节:逆向流程场景分析.md", + "第2-26节:未支付退单流程.md", + "第2-27节:已支付未成团退单.md", + "第2-28节:已支付已成团退单.md", + "第2-29节:退单锁单量恢复.md", + "第2-30节:设计模式重构退单.md", + "第2-31节:退订接口和定时任务.md", + ] + }, + { + title: "第3部分:外部对接", + collapsable: false, + sidebarDepth: 0, + children: [ + "第3-1节:DeepSeek设计拼团UI.md", + "第3-2节:DeepSeek处理UI与接口对接.md", + "第3-3节:小商城对接营销锁单.md", + "第3-4节:小商城对接营销结算.md", + "第3-5节:小商城UI与接口对接.md", + "第3-6节:通过浏览器指纹获取登录ticket无痕登录.md", + "第3-7节:用户订单列表和退单UI.md", + "第3-8节:退单退款服务对接.md", + ] + }, + { + title: "第4部分:开发运维", + collapsable: false, + sidebarDepth: 0, + children: [ + "第4-1节:第1阶段部署云环境.md", + "第4-2节:第2阶段部署云环境.md", + "第4-3节:第3阶段部署云环境.md", + ] + }, + { + title: "番外 - 课程阶段产物", + collapsable: false, + sidebarDepth: 0, + children: [ + "promotion/group-buy-market-v1.md", + "promotion/group-buy-market-v2.md", + "promotion/group-buy-market-v3.md", + "promotion/group-buy-market-v4.md", + "promotion/group-buy-market-v5.md", + "promotion/group-buy-market-v6.md", + "promotion/group-buy-market-v7.md", + ] + } + ] +} + +function getBarSPayMall() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "s-pay-mall.md", + ] + }, + { + title: "第1部分:架构理论", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-1/第1节:DDD 架构概念.md", + "part-1/第2节:DDD 建模方法.md", + "part-1/第3节:DDD 工程模型.md", + ] + }, + { + title: "第2部分:需求设计", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-2/第1节:小型支付商城需求设计.md", + "part-2/第2节:支付商城四色建模设计.md", + "part-2/第3节:支付订单场景表设计.md", + ] + }, + { + title: "第3部分:功能实现 MVC", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-3-mvc/第1节:MVC 工程框架搭建.md", + "part-3-mvc/第2节:微信公众号鉴权.md", + "part-3-mvc/第3节:登录功能实现.md", + "part-3-mvc/第4节:商品下单.md", + "part-3-mvc/第5节:对接支付.md", + "part-3-mvc/第6节:支付回调.md", + "part-3-mvc/第7节:前端页面.md", + "part-3-mvc/第8节:Docker构建和部署.md", + ] + }, + { + title: "第3部分:功能实现 DDD", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-3-ddd/第1节:DDD 工程框架搭建.md", + "part-3-ddd/第2节:DDD 重构,微信公众号鉴权.md", + "part-3-ddd/第3节:DDD 重构,登录功能实现.md", + "part-3-ddd/第4节:DDD 重构,商品下单.md", + "part-3-ddd/第5节:DDD 重构,对接支付.md", + "part-3-ddd/第6节:DDD 重构,支付回调.md", + "part-3-ddd/第7节:前端页面.md", + "part-3-ddd/第8节:Docker构建和部署.md", + ] + }, + { + title: "第4部分:开发运维", + collapsable: false, + sidebarDepth: 0, + children: [ + "part-4/第1节:natapp 内网穿透.md", + "part-4/第2节:微信公众号测试评审申请.md", + "part-4/第3节:支付宝沙箱申请.md", + ] + } + ] +} + +function getBarBigMarket() { + return [ + { + title: "介绍", + collapsable: false, + sidebarDepth: 0, + children: [ + "big-market.md", + "ddd.md", + "system-design-diagram.md", + "notes.md", + "qa.md", + ] + }, + { + title: "第1部分:需求文档", + collapsable: false, + sidebarDepth: 0, + children: [ + "prd/第1节:营销场景的需求设计.md", + "prd/第2节:抽奖活动场景的需求设计.md", + "prd/第3节:用户行为奖励需求设计.md", + "prd/第4节:用户积分需求设计.md", + ] + }, + { + title: "第2部分:开发运维", + collapsable: false, + sidebarDepth: 0, + children: [ + "dev-ops/第1节:使用脚手架创建工程.md", + "dev-ops/第2节:第一阶段完成抽奖部署.md", + "dev-ops/第3节:引入Nacos+Dubbo框架.md", + "dev-ops/openai_big_market.md", + ] + }, + { + title: "第3部分:营销服务", + collapsable: false, + sidebarDepth: 0, + children: [ + "api/第1节:抽奖策略领域和库表设计.md", + "api/第2节:基础层持久化数据.md", + "api/第3节:策略概率装配处理.md", + "api/第4节:策略权重概率装配.md", + "api/第5节:抽奖前置规则过滤.md", + "api/第6节:抽奖后置规则过滤.md", + "api/第7节:责任链模式处理抽奖规则.md", + "api/第8节:抽奖规则树模型结构设计.md", + "api/第9节:模板模式串联抽奖规则.md", + "api/第10节:不超卖库存规则实现.md", + "api/第11节:抽奖API接口实现.md", + "api/第12节:用户参与抽奖活动库表设计.md", + "api/第13节:引入分库分表路由组件.md", + "api/第14节:抽奖活动订单流程设计.md", + "api/第15节:抽奖活动流水入库.md", + "api/第16节:引入MQ处理活动SKU库存一致性.md", + "api/第17节:用户领取活动库表设计.md", + "api/第18节:领取活动扣减账户额度.md", + "api/第19节:写入中奖记录和任务补偿发送.md", + "api/第20节:抽奖活动流程串联.md", + "api/第21节:活动信息API迭代和功能完善.md", + "api/第22节:用户行为返利入账.md", + "api/第23节:用户行为返利结算.md", + "api/第24节:规则完善和应用接口实现.md", + "api/第25节:积分发奖服务实现.md", + "api/第26节:积分领域调额服务.md", + "api/第27节:积分支付兑换商品.md", + "api/第28节:积分应用场景接口实现.md", + "api/第29节:分布式动态配置活动降级.md", + "api/第30节:分布式动态限流和熔断.md", + "api/第31节:分库分表数据同步ES.md", + "api/第32节:ES-ORM多数据源配置使用.md", + "api/第33节:xxl-job分布式任务调度.md", + ] + }, + { + title: "第4部分:前端页面", + collapsable: false, + sidebarDepth: 0, + children: [ + "web/第1节:React工程创建和抽奖组件使用.md", + "web/第2节:Mock接口对接抽奖页面.md", + "web/第3节:应用接口对接抽奖页面.md", + "web/第4节:抽奖活动页面设计和对接.md", + "web/第5节:对接联调额度签到权重接口.md", + "web/第6节:对接联调积分流程接口.md", + ] + }, + { + title: "第5部分:后台管理", + collapsable: false, + sidebarDepth: 0, + children: [ + "erp/第1节:初始后台运营页面.md", + "erp/第2节:querys模块提供查询接口.md", + ] + }, + { + title: "第6部分:外部对接", + collapsable: false, + sidebarDepth: 0, + children: [ + "distributed/第1节:对接OpenAI项目额度奖品接口.md", + "distributed/第2节:营销页面接口封装.md", + "distributed/第3节:RPC接口对接支付返利.md", + "distributed/第4节:活动上架发布预热对接.md", + ] + }, + { + title: "番外 - 课程阶段产物", + collapsable: false, + sidebarDepth: 0, + children: [ + "extra/big-market-v1.md", + "extra/big-market-v2.md", + "extra/big-market-v3.md", + "extra/big-market-v4.md", + "extra/big-market-v5.md", + "extra/big-market-v6.md", + "extra/big-market-v7.md", + ] + } + ] +} + +function getBarDDDSceneSolution() { + return [ + { + title: "案例", + collapsable: false, + sidebarDepth: 0, + children: [ + "ddd-scene-solution/alipay-sandbox.md", + "ddd-scene-solution/openai-tldraw.md", + "ddd-scene-solution/sensitive-word-content-moderation.md", + "ddd-scene-solution/weixin-login.md", + ] + }, + { + title: "项目", + collapsable: false, + sidebarDepth: 0, + children: [ + "lucky-tackout/lucky-tackout.md", + "xfg-fish-pond/xfg-fish-pond.md", + ] + }, + ] +} + // project lottery function getBarProjectLottery() { return [ @@ -1587,6 +2911,7 @@ function getBarProjectLottery() { sidebarDepth: 0, children: [ "introduce/Lottery抽奖系统.md", + "notes.md", ] }, { @@ -1666,6 +2991,19 @@ function getBarProjectLottery() { // About page function genBarAbout() { return [ + { + title: "年终总结", + collapsable: false, + sidebarDepth: 0, + children: [ + "me/2020-12-27-2020总结,作为技术号主的一年!.md", + "me/2022-01-27-2021年,小傅哥の年终总结!.md", + "me/2023-01-02-2022年,小傅哥の年终总结.md", + "me/2024-02-07-2023年,小傅哥の年终总结.md", + "me/2025-01-05-2024年,小傅哥の年终总结!.md", + "me/2025-01-04-2025年,小傅哥の年终总结.md", + ] + }, { title: "关于自己", collapsable: false, @@ -1673,13 +3011,13 @@ function genBarAbout() { children: [ "me/about-me.md", "me/2020-03-31-大学四年到毕业工作5年的学习路线资源汇总.md", + "me/2020-07-25-12天,这本《重学Java设计模式》PDF书籍下载量9k,新增粉丝1400人,Github上全球推荐榜.md", "me/2020-08-25-13年毕业,用两年时间从外包走进互联网大厂.md", "me/2020-10-09-让人怪不好意思的,粉丝破万,用了1年!.md", "me/2020-10-25-今天你写博客了吗.md", "me/2020-11-01-刚毕业不久,接私活赚了2万块!.md", "me/2020-11-29-北漂码农的我,把在大城市过成了屯子一样舒服,哈哈哈哈哈!.md", - "me/2020-12-27-2020总结,作为技术号主的一年!.md", "me/2021-01-31-这一年,想踏码进货一样!.md", "me/2021-05-26-小傅哥,一个有副业的码农.md", "me/2021-06-20-我,有10万+粉丝啦!.md", @@ -1687,8 +3025,13 @@ function genBarAbout() { "me/2021-09-05-我在CSDN赚了1.2万.md", "me/2021-10-24-炸!1024,小傅哥的博客升级啦,文章开源、支持PR,冲哇!.md", "me/2021-11-14-CodeGuide开源仓库.md", - "me/2022-01-27-2021年,小傅哥の年终总结!.md", "me/2022-05-22-copyright-violation.md", + "me/2023-04-16-这是我异动的第一周,为啥离开原部门?.md", + "me/2023-05-07-51假期代码旅游.md", + "me/2024-01-09-从T4到T8,4年时间,4次晋升。技术提升最快的那几年,我做了什么?.md", + "me/2024-01-28-大厂架构师小傅哥,上学时都做过哪些项目?.md", + "me/2024-11-17-gold-content.md", + "me/2025-07-03-嘎嘎强,嘎嘎哒学v2.0.md", ] }, { @@ -1709,6 +3052,12 @@ function genBarAbout() { "study/2022-02-07-你上车,我就把你带成卷王!.md", "study/2022-06-16-敲了几万行源码后,我给Mybatis画了张“全地图”.md", "study/2022-06-19-OnJava.md", + "study/2022-10-15-面试官:深度不够,建议回去深挖.md", + "study/2022-12-25-我把ChatGPT拉到微信群里了.md", + "study/2023-04-02-国外码农,会卷八股文吗?.md", + "study/2023-05-14-卧龙、凤雏!两源码学得一,代码质量都不会差!.md", + "study/2023-06-04-后端码农,怎么写好前端代码?.md", + "study/2024-03-03-到5万就好了.md", ] }, { @@ -1724,6 +3073,13 @@ function genBarAbout() { "job/2021-02-24-半年筛选了400+份简历,告诉你怎么写会被撩.md", "job/2021-12-02-刚提测就改需求,我是渣男吗.md", "job/2022-04-30-面试字节,小傅哥写了一份硬核简历!.md", + "job/2023-02-04-项目这么问,把你水分挤干.md", + "job/2023-03-19-你简历没项目,你得遭老罪喽!.md", + "job/2023-07-11-面试官都问你啥了.md", + "job/2023-09-13-工作内推.md", + "job/2024-09-08-阿里P7,就是很多人的天花板吗?.md", + "job/2025-11-16-现在转AI应用开发,是不是个机会?.md", + "job/2026-03-21-古法编程,是否还重要?.md", ] } ]; diff --git a/docs/.vuepress/public/images/article/about/ChatGPT-01.png b/docs/.vuepress/public/images/article/about/ChatGPT-01.png new file mode 100644 index 000000000..97feb6050 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ChatGPT-01.png differ diff --git a/docs/.vuepress/public/images/article/about/ChatGPT-02.png b/docs/.vuepress/public/images/article/about/ChatGPT-02.png new file mode 100644 index 000000000..e6229f40e Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ChatGPT-02.png differ diff --git a/docs/.vuepress/public/images/article/about/ChatGPT-03.png b/docs/.vuepress/public/images/article/about/ChatGPT-03.png new file mode 100644 index 000000000..11b1fd6e0 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ChatGPT-03.png differ diff --git a/docs/.vuepress/public/images/article/about/ChatGPT-04.png b/docs/.vuepress/public/images/article/about/ChatGPT-04.png new file mode 100644 index 000000000..d54e37036 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ChatGPT-04.png differ diff --git a/docs/.vuepress/public/images/article/about/ChatGPT-05.png b/docs/.vuepress/public/images/article/about/ChatGPT-05.png new file mode 100644 index 000000000..e023b14fc Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ChatGPT-05.png differ diff --git a/docs/.vuepress/public/images/article/about/ChatGPT-06.png b/docs/.vuepress/public/images/article/about/ChatGPT-06.png new file mode 100644 index 000000000..4c9a66bbd Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ChatGPT-06.png differ diff --git a/docs/.vuepress/public/images/article/about/ChatGPT-07.png b/docs/.vuepress/public/images/article/about/ChatGPT-07.png new file mode 100644 index 000000000..54dae4cd6 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ChatGPT-07.png differ diff --git a/docs/.vuepress/public/images/article/about/ChatGPT-08.png b/docs/.vuepress/public/images/article/about/ChatGPT-08.png new file mode 100644 index 000000000..4796d55e1 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ChatGPT-08.png differ diff --git a/docs/.vuepress/public/images/article/about/ChatGPT-09.png b/docs/.vuepress/public/images/article/about/ChatGPT-09.png new file mode 100644 index 000000000..335dcde39 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ChatGPT-09.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-00.jpg b/docs/.vuepress/public/images/article/about/about-2023-00.jpg new file mode 100644 index 000000000..996667847 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-00.jpg differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-01.png b/docs/.vuepress/public/images/article/about/about-2023-01.png new file mode 100644 index 000000000..866880e5a Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-02.png b/docs/.vuepress/public/images/article/about/about-2023-02.png new file mode 100644 index 000000000..e78239a6c Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-03.png b/docs/.vuepress/public/images/article/about/about-2023-03.png new file mode 100644 index 000000000..01b0d80eb Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-04.png b/docs/.vuepress/public/images/article/about/about-2023-04.png new file mode 100644 index 000000000..88b5a422b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-05.png b/docs/.vuepress/public/images/article/about/about-2023-05.png new file mode 100644 index 000000000..a2406a302 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-06.png b/docs/.vuepress/public/images/article/about/about-2023-06.png new file mode 100644 index 000000000..c1ac9d603 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-07.png b/docs/.vuepress/public/images/article/about/about-2023-07.png new file mode 100644 index 000000000..576a5461b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-07.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-08.png b/docs/.vuepress/public/images/article/about/about-2023-08.png new file mode 100644 index 000000000..de2a9dae4 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-08.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-09.png b/docs/.vuepress/public/images/article/about/about-2023-09.png new file mode 100644 index 000000000..1fbf11e3c Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-09.png differ diff --git a/docs/.vuepress/public/images/article/about/about-2023-10.png b/docs/.vuepress/public/images/article/about/about-2023-10.png new file mode 100644 index 000000000..bb5cae13d Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-2023-10.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230319-01.png b/docs/.vuepress/public/images/article/about/about-230319-01.png new file mode 100644 index 000000000..5a5aed01b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230319-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230319-02.png b/docs/.vuepress/public/images/article/about/about-230319-02.png new file mode 100644 index 000000000..872c39ba9 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230319-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230319-03.png b/docs/.vuepress/public/images/article/about/about-230319-03.png new file mode 100644 index 000000000..a94f77629 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230319-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-01.jpeg b/docs/.vuepress/public/images/article/about/about-230330-01.jpeg new file mode 100644 index 000000000..ec40e54be Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-01.jpeg differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-01.png b/docs/.vuepress/public/images/article/about/about-230330-01.png new file mode 100644 index 000000000..21fbc3922 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-02.png b/docs/.vuepress/public/images/article/about/about-230330-02.png new file mode 100644 index 000000000..8fc7aab16 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-03.png b/docs/.vuepress/public/images/article/about/about-230330-03.png new file mode 100644 index 000000000..a26808a4d Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-04.png b/docs/.vuepress/public/images/article/about/about-230330-04.png new file mode 100644 index 000000000..a03bac3a5 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-05.png b/docs/.vuepress/public/images/article/about/about-230330-05.png new file mode 100644 index 000000000..87bf4ed41 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-06.png b/docs/.vuepress/public/images/article/about/about-230330-06.png new file mode 100644 index 000000000..066a29dd5 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-07.png b/docs/.vuepress/public/images/article/about/about-230330-07.png new file mode 100644 index 000000000..e2eccf4f8 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-07.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-08.png b/docs/.vuepress/public/images/article/about/about-230330-08.png new file mode 100644 index 000000000..cf0d31756 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-08.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-09.png b/docs/.vuepress/public/images/article/about/about-230330-09.png new file mode 100644 index 000000000..703fe621d Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-09.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-10.png b/docs/.vuepress/public/images/article/about/about-230330-10.png new file mode 100644 index 000000000..1a953ab88 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-10.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230330-11.png b/docs/.vuepress/public/images/article/about/about-230330-11.png new file mode 100644 index 000000000..b19e2b15e Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230330-11.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230415-01.png b/docs/.vuepress/public/images/article/about/about-230415-01.png new file mode 100644 index 000000000..33dfa6229 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230415-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230415-02.png b/docs/.vuepress/public/images/article/about/about-230415-02.png new file mode 100644 index 000000000..fb19c63f9 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230415-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230415-03.png b/docs/.vuepress/public/images/article/about/about-230415-03.png new file mode 100644 index 000000000..51d254d44 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230415-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230415-04.png b/docs/.vuepress/public/images/article/about/about-230415-04.png new file mode 100644 index 000000000..5ca6fb5fb Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230415-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230415-05.png b/docs/.vuepress/public/images/article/about/about-230415-05.png new file mode 100644 index 000000000..520c119f6 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230415-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230415-06.png b/docs/.vuepress/public/images/article/about/about-230415-06.png new file mode 100644 index 000000000..88f9e730c Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230415-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230415-07.png b/docs/.vuepress/public/images/article/about/about-230415-07.png new file mode 100644 index 000000000..c67abee0c Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230415-07.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230514-01.png b/docs/.vuepress/public/images/article/about/about-230514-01.png new file mode 100644 index 000000000..beabcc30b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230514-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230514-02.png b/docs/.vuepress/public/images/article/about/about-230514-02.png new file mode 100644 index 000000000..76377ed0f Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230514-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230514-03.png b/docs/.vuepress/public/images/article/about/about-230514-03.png new file mode 100644 index 000000000..ae89406f5 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230514-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230604-01.png b/docs/.vuepress/public/images/article/about/about-230604-01.png new file mode 100644 index 000000000..56dfe175b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230604-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230604-02.png b/docs/.vuepress/public/images/article/about/about-230604-02.png new file mode 100644 index 000000000..4d97f6d94 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230604-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230604-03.png b/docs/.vuepress/public/images/article/about/about-230604-03.png new file mode 100644 index 000000000..5c561f80f Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230604-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230913-01.gif b/docs/.vuepress/public/images/article/about/about-230913-01.gif new file mode 100644 index 000000000..753bbc1d4 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230913-01.gif differ diff --git a/docs/.vuepress/public/images/article/about/about-230913-02.png b/docs/.vuepress/public/images/article/about/about-230913-02.png new file mode 100644 index 000000000..46c9a8ee3 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230913-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230913-03.png b/docs/.vuepress/public/images/article/about/about-230913-03.png new file mode 100644 index 000000000..3733e56c8 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230913-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230913-04.png b/docs/.vuepress/public/images/article/about/about-230913-04.png new file mode 100644 index 000000000..7a81648e5 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230913-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230913-05.png b/docs/.vuepress/public/images/article/about/about-230913-05.png new file mode 100644 index 000000000..6f32b218b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230913-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230913-06.png b/docs/.vuepress/public/images/article/about/about-230913-06.png new file mode 100644 index 000000000..d63bba35b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230913-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230913-07.png b/docs/.vuepress/public/images/article/about/about-230913-07.png new file mode 100644 index 000000000..48875cfa7 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230913-07.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230913-08.png b/docs/.vuepress/public/images/article/about/about-230913-08.png new file mode 100644 index 000000000..d53f598ad Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230913-08.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230919-01.png b/docs/.vuepress/public/images/article/about/about-230919-01.png new file mode 100644 index 000000000..c306ffd96 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230919-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230919-02.png b/docs/.vuepress/public/images/article/about/about-230919-02.png new file mode 100644 index 000000000..7c943a8bc Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230919-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230919-03.png b/docs/.vuepress/public/images/article/about/about-230919-03.png new file mode 100644 index 000000000..914e782b8 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230919-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230919-04.png b/docs/.vuepress/public/images/article/about/about-230919-04.png new file mode 100644 index 000000000..ddda4f497 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230919-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230919-05.png b/docs/.vuepress/public/images/article/about/about-230919-05.png new file mode 100644 index 000000000..fe9871c4b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230919-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-230919-06.png b/docs/.vuepress/public/images/article/about/about-230919-06.png new file mode 100644 index 000000000..2ffd38e78 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-230919-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240109-02.png b/docs/.vuepress/public/images/article/about/about-240109-02.png new file mode 100644 index 000000000..87fc478dc Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240109-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240109-03.png b/docs/.vuepress/public/images/article/about/about-240109-03.png new file mode 100644 index 000000000..b91da255d Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240109-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240128-01.png b/docs/.vuepress/public/images/article/about/about-240128-01.png new file mode 100644 index 000000000..6a6e95f49 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240128-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240128-02.png b/docs/.vuepress/public/images/article/about/about-240128-02.png new file mode 100644 index 000000000..d6496bcf4 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240128-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240128-03.png b/docs/.vuepress/public/images/article/about/about-240128-03.png new file mode 100644 index 000000000..6c4bfbb13 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240128-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240128-04.png b/docs/.vuepress/public/images/article/about/about-240128-04.png new file mode 100644 index 000000000..2125b94ca Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240128-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240128-05.png b/docs/.vuepress/public/images/article/about/about-240128-05.png new file mode 100644 index 000000000..8fa4c3f78 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240128-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240207-01.png b/docs/.vuepress/public/images/article/about/about-240207-01.png new file mode 100644 index 000000000..d12a4fd10 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240207-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-01.png b/docs/.vuepress/public/images/article/about/about-240303-01.png new file mode 100644 index 000000000..c06c81783 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-02.png b/docs/.vuepress/public/images/article/about/about-240303-02.png new file mode 100644 index 000000000..31e3aecf3 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-03.png b/docs/.vuepress/public/images/article/about/about-240303-03.png new file mode 100644 index 000000000..4fe4056f8 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-04.png b/docs/.vuepress/public/images/article/about/about-240303-04.png new file mode 100644 index 000000000..73e5ac614 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-05.png b/docs/.vuepress/public/images/article/about/about-240303-05.png new file mode 100644 index 000000000..066779604 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-06.png b/docs/.vuepress/public/images/article/about/about-240303-06.png new file mode 100644 index 000000000..d82609910 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-07.png b/docs/.vuepress/public/images/article/about/about-240303-07.png new file mode 100644 index 000000000..9bf602843 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-07.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-08.png b/docs/.vuepress/public/images/article/about/about-240303-08.png new file mode 100644 index 000000000..4bd942067 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-08.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-09.png b/docs/.vuepress/public/images/article/about/about-240303-09.png new file mode 100644 index 000000000..c66066df3 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-09.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-10.png b/docs/.vuepress/public/images/article/about/about-240303-10.png new file mode 100644 index 000000000..41c0b576c Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-10.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-11.png b/docs/.vuepress/public/images/article/about/about-240303-11.png new file mode 100644 index 000000000..aa37f1d2a Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-11.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-12.png b/docs/.vuepress/public/images/article/about/about-240303-12.png new file mode 100644 index 000000000..85b15a474 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-12.png differ diff --git a/docs/.vuepress/public/images/article/about/about-240303-13.png b/docs/.vuepress/public/images/article/about/about-240303-13.png new file mode 100644 index 000000000..1d8036199 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-240303-13.png differ diff --git a/docs/.vuepress/public/images/article/about/about-241117-01.gif b/docs/.vuepress/public/images/article/about/about-241117-01.gif new file mode 100644 index 000000000..07bee7721 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-241117-01.gif differ diff --git a/docs/.vuepress/public/images/article/about/about-241117-02.png b/docs/.vuepress/public/images/article/about/about-241117-02.png new file mode 100644 index 000000000..c81295834 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-241117-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-241117-03.png b/docs/.vuepress/public/images/article/about/about-241117-03.png new file mode 100644 index 000000000..248346345 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-241117-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-241117-04.png b/docs/.vuepress/public/images/article/about/about-241117-04.png new file mode 100644 index 000000000..c4a64f1e2 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-241117-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-241117-05.png b/docs/.vuepress/public/images/article/about/about-241117-05.png new file mode 100644 index 000000000..ef474068f Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-241117-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-241117-06.png b/docs/.vuepress/public/images/article/about/about-241117-06.png new file mode 100644 index 000000000..1f1d5d2dd Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-241117-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-241117-07.png b/docs/.vuepress/public/images/article/about/about-241117-07.png new file mode 100644 index 000000000..275bd7b5e Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-241117-07.png differ diff --git a/docs/.vuepress/public/images/article/about/about-250713-01.png b/docs/.vuepress/public/images/article/about/about-250713-01.png new file mode 100644 index 000000000..86b676bd2 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-250713-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-250713-02.png b/docs/.vuepress/public/images/article/about/about-250713-02.png new file mode 100644 index 000000000..0b2121fe6 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-250713-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-250713-03.png b/docs/.vuepress/public/images/article/about/about-250713-03.png new file mode 100644 index 000000000..3d4172f37 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-250713-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-250713-04.png b/docs/.vuepress/public/images/article/about/about-250713-04.png new file mode 100644 index 000000000..4c81ec588 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-250713-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-250713-05.png b/docs/.vuepress/public/images/article/about/about-250713-05.png new file mode 100644 index 000000000..dbe75d24b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-250713-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-250713-06.png b/docs/.vuepress/public/images/article/about/about-250713-06.png new file mode 100644 index 000000000..137c4bfa8 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-250713-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-250713-07.png b/docs/.vuepress/public/images/article/about/about-250713-07.png new file mode 100644 index 000000000..7a3ad83a2 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-250713-07.png differ diff --git a/docs/.vuepress/public/images/article/about/about-250713-08.png b/docs/.vuepress/public/images/article/about/about-250713-08.png new file mode 100644 index 000000000..12a8db470 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-250713-08.png differ diff --git a/docs/.vuepress/public/images/article/about/about-interview-250823-01.png b/docs/.vuepress/public/images/article/about/about-interview-250823-01.png new file mode 100644 index 000000000..aadee0905 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-interview-250823-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-job-240908-01.gif b/docs/.vuepress/public/images/article/about/about-job-240908-01.gif new file mode 100644 index 000000000..c97e3cb2b Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-240908-01.gif differ diff --git a/docs/.vuepress/public/images/article/about/about-job-240908-02.png b/docs/.vuepress/public/images/article/about/about-job-240908-02.png new file mode 100644 index 000000000..b012d4915 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-240908-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-job-240908-03.png b/docs/.vuepress/public/images/article/about/about-job-240908-03.png new file mode 100644 index 000000000..b4fa9e891 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-240908-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-job-240908-04.png b/docs/.vuepress/public/images/article/about/about-job-240908-04.png new file mode 100644 index 000000000..c59887462 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-240908-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-job-ai-code-01.png b/docs/.vuepress/public/images/article/about/about-job-ai-code-01.png new file mode 100644 index 000000000..45612a6ee Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-ai-code-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-job-ai-code-02.png b/docs/.vuepress/public/images/article/about/about-job-ai-code-02.png new file mode 100644 index 000000000..438d1c88d Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-ai-code-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-job-ai-code-03.png b/docs/.vuepress/public/images/article/about/about-job-ai-code-03.png new file mode 100644 index 000000000..ee6c677ce Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-ai-code-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-job-ai-code-04.png b/docs/.vuepress/public/images/article/about/about-job-ai-code-04.png new file mode 100644 index 000000000..a722f6866 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-ai-code-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-job-ai-code-05.png b/docs/.vuepress/public/images/article/about/about-job-ai-code-05.png new file mode 100644 index 000000000..256e3d9bb Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-ai-code-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-job-ai-code-06.png b/docs/.vuepress/public/images/article/about/about-job-ai-code-06.png new file mode 100644 index 000000000..9c1e4119e Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-job-ai-code-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-me-25-01.png b/docs/.vuepress/public/images/article/about/about-me-25-01.png new file mode 100644 index 000000000..22a5f6e63 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-me-25-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-me-25-02.png b/docs/.vuepress/public/images/article/about/about-me-25-02.png new file mode 100644 index 000000000..b2adfc1f6 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-me-25-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-me-25-03.png b/docs/.vuepress/public/images/article/about/about-me-25-03.png new file mode 100644 index 000000000..7e270fe5a Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-me-25-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-study-221016-00.png b/docs/.vuepress/public/images/article/about/about-study-221016-00.png new file mode 100644 index 000000000..f879a997d Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-study-221016-00.png differ diff --git a/docs/.vuepress/public/images/article/about/about-study-221016-01.png b/docs/.vuepress/public/images/article/about/about-study-221016-01.png new file mode 100644 index 000000000..e97ec0541 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-study-221016-01.png differ diff --git a/docs/.vuepress/public/images/article/about/about-study-221016-02.png b/docs/.vuepress/public/images/article/about/about-study-221016-02.png new file mode 100644 index 000000000..f7d1369da Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-study-221016-02.png differ diff --git a/docs/.vuepress/public/images/article/about/about-study-221016-03.png b/docs/.vuepress/public/images/article/about/about-study-221016-03.png new file mode 100644 index 000000000..85b51a75a Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-study-221016-03.png differ diff --git a/docs/.vuepress/public/images/article/about/about-study-221016-04.png b/docs/.vuepress/public/images/article/about/about-study-221016-04.png new file mode 100644 index 000000000..5500df9e5 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-study-221016-04.png differ diff --git a/docs/.vuepress/public/images/article/about/about-study-221016-05.png b/docs/.vuepress/public/images/article/about/about-study-221016-05.png new file mode 100644 index 000000000..ce9482b71 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-study-221016-05.png differ diff --git a/docs/.vuepress/public/images/article/about/about-study-221016-06.png b/docs/.vuepress/public/images/article/about/about-study-221016-06.png new file mode 100644 index 000000000..4a7534519 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-study-221016-06.png differ diff --git a/docs/.vuepress/public/images/article/about/about-study-221016-07.png b/docs/.vuepress/public/images/article/about/about-study-221016-07.png new file mode 100644 index 000000000..5f36b5f3a Binary files /dev/null and b/docs/.vuepress/public/images/article/about/about-study-221016-07.png differ diff --git a/docs/.vuepress/public/images/article/about/ai-agent-job-01.png b/docs/.vuepress/public/images/article/about/ai-agent-job-01.png new file mode 100644 index 000000000..a853d8cbd Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ai-agent-job-01.png differ diff --git a/docs/.vuepress/public/images/article/about/ai-agent-job-02.png b/docs/.vuepress/public/images/article/about/ai-agent-job-02.png new file mode 100644 index 000000000..bafd0d5fb Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ai-agent-job-02.png differ diff --git a/docs/.vuepress/public/images/article/about/ai-agent-job-03.png b/docs/.vuepress/public/images/article/about/ai-agent-job-03.png new file mode 100644 index 000000000..e6b4db398 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/ai-agent-job-03.png differ diff --git a/docs/.vuepress/public/images/article/about/jdyun-01.png b/docs/.vuepress/public/images/article/about/jdyun-01.png new file mode 100644 index 000000000..b2a3027e6 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/jdyun-01.png differ diff --git a/docs/.vuepress/public/images/article/about/jdyun-02.png b/docs/.vuepress/public/images/article/about/jdyun-02.png new file mode 100644 index 000000000..d10293512 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/jdyun-02.png differ diff --git a/docs/.vuepress/public/images/article/about/jdyun-03.png b/docs/.vuepress/public/images/article/about/jdyun-03.png new file mode 100644 index 000000000..a65c410a7 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/jdyun-03.png differ diff --git a/docs/.vuepress/public/images/article/about/jdyun-04.png b/docs/.vuepress/public/images/article/about/jdyun-04.png new file mode 100644 index 000000000..fa24c0bd5 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/jdyun-04.png differ diff --git a/docs/.vuepress/public/images/article/about/jdyun-05.png b/docs/.vuepress/public/images/article/about/jdyun-05.png new file mode 100644 index 000000000..2acefdfc9 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/jdyun-05.png differ diff --git a/docs/.vuepress/public/images/article/about/jdyun-06.png b/docs/.vuepress/public/images/article/about/jdyun-06.png new file mode 100644 index 000000000..4500ca627 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/jdyun-06.png differ diff --git a/docs/.vuepress/public/images/article/about/jdyun-07.png b/docs/.vuepress/public/images/article/about/jdyun-07.png new file mode 100644 index 000000000..8a88b601f Binary files /dev/null and b/docs/.vuepress/public/images/article/about/jdyun-07.png differ diff --git a/docs/.vuepress/public/images/article/about/lottery-interview-01.png b/docs/.vuepress/public/images/article/about/lottery-interview-01.png new file mode 100644 index 000000000..35260576f Binary files /dev/null and b/docs/.vuepress/public/images/article/about/lottery-interview-01.png differ diff --git a/docs/.vuepress/public/images/article/about/lottery-interview-02.png b/docs/.vuepress/public/images/article/about/lottery-interview-02.png new file mode 100644 index 000000000..14319cb45 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/lottery-interview-02.png differ diff --git a/docs/.vuepress/public/images/article/about/lottery-interview-03.png b/docs/.vuepress/public/images/article/about/lottery-interview-03.png new file mode 100644 index 000000000..1b5ecb98e Binary files /dev/null and b/docs/.vuepress/public/images/article/about/lottery-interview-03.png differ diff --git a/docs/.vuepress/public/images/article/about/lottery-interview-04.png b/docs/.vuepress/public/images/article/about/lottery-interview-04.png new file mode 100644 index 000000000..4b7c868c1 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/lottery-interview-04.png differ diff --git a/docs/.vuepress/public/images/article/about/lottery-interview-05.png b/docs/.vuepress/public/images/article/about/lottery-interview-05.png new file mode 100644 index 000000000..ed9a4d3d5 Binary files /dev/null and b/docs/.vuepress/public/images/article/about/lottery-interview-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/algorithms-220723-03.png b/docs/.vuepress/public/images/article/algorithm/algorithms-220723-03.png index 4cf027166..ade23a26d 100644 Binary files a/docs/.vuepress/public/images/article/algorithm/algorithms-220723-03.png and b/docs/.vuepress/public/images/article/algorithm/algorithms-220723-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/algorithms-220730-01.png b/docs/.vuepress/public/images/article/algorithm/algorithms-220730-01.png index 22487e7c9..16088e5df 100644 Binary files a/docs/.vuepress/public/images/article/algorithm/algorithms-220730-01.png and b/docs/.vuepress/public/images/article/algorithm/algorithms-220730-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/algorithms-220806-01.png b/docs/.vuepress/public/images/article/algorithm/algorithms-220806-01.png index 8bb555060..815411a37 100644 Binary files a/docs/.vuepress/public/images/article/algorithm/algorithms-220806-01.png and b/docs/.vuepress/public/images/article/algorithm/algorithms-220806-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/algorithms-220824-01.png b/docs/.vuepress/public/images/article/algorithm/algorithms-220824-01.png index 7061e04ef..b610bb2aa 100644 Binary files a/docs/.vuepress/public/images/article/algorithm/algorithms-220824-01.png and b/docs/.vuepress/public/images/article/algorithm/algorithms-220824-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/bloom-filter-01.png b/docs/.vuepress/public/images/article/algorithm/bloom-filter-01.png new file mode 100644 index 000000000..4a952a5a6 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/bloom-filter-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/data-structures-01.png b/docs/.vuepress/public/images/article/algorithm/data-structures-01.png new file mode 100644 index 000000000..76385f8aa Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/data-structures-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/data-structures-02.png b/docs/.vuepress/public/images/article/algorithm/data-structures-02.png new file mode 100644 index 000000000..2b8882e80 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/data-structures-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/data-structures-03.png b/docs/.vuepress/public/images/article/algorithm/data-structures-03.png new file mode 100644 index 000000000..ba579d492 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/data-structures-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/data-structures.png b/docs/.vuepress/public/images/article/algorithm/data-structures.png new file mode 100644 index 000000000..0cb113ce8 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/data-structures.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-01.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-01.png new file mode 100644 index 000000000..28646a09c Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-02.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-02.png new file mode 100644 index 000000000..d97d7a13e Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-03.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-03.png new file mode 100644 index 000000000..1bb70e823 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-04.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-04.png new file mode 100644 index 000000000..f31539cea Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-05.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-05.png new file mode 100644 index 000000000..312fefc06 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-06.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-06.png new file mode 100644 index 000000000..4e8903409 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-07.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-07.png new file mode 100644 index 000000000..e5c84be69 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-08.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-08.png new file mode 100644 index 000000000..89d99efec Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-09.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-09.png new file mode 100644 index 000000000..e96356756 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-09.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-10.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-10.png new file mode 100644 index 000000000..fc9cfb212 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-10.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/disjoint-set-11.png b/docs/.vuepress/public/images/article/algorithm/disjoint-set-11.png new file mode 100644 index 000000000..5ead3c05b Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/disjoint-set-11.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-01.png b/docs/.vuepress/public/images/article/algorithm/graph-01.png new file mode 100644 index 000000000..c1ab0c14d Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-02.png b/docs/.vuepress/public/images/article/algorithm/graph-02.png new file mode 100644 index 000000000..1ca422e7d Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-03.png b/docs/.vuepress/public/images/article/algorithm/graph-03.png new file mode 100644 index 000000000..fa844c2d0 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-04.png b/docs/.vuepress/public/images/article/algorithm/graph-04.png new file mode 100644 index 000000000..c5b8f516e Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-05.png b/docs/.vuepress/public/images/article/algorithm/graph-05.png new file mode 100644 index 000000000..93d9dd610 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-06.png b/docs/.vuepress/public/images/article/algorithm/graph-06.png new file mode 100644 index 000000000..f8a4b662d Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-07.png b/docs/.vuepress/public/images/article/algorithm/graph-07.png new file mode 100644 index 000000000..ce2641bb0 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-08.png b/docs/.vuepress/public/images/article/algorithm/graph-08.png new file mode 100644 index 000000000..433320850 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-09.png b/docs/.vuepress/public/images/article/algorithm/graph-09.png new file mode 100644 index 000000000..4f8695f6f Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-09.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-10.png b/docs/.vuepress/public/images/article/algorithm/graph-10.png new file mode 100644 index 000000000..21e472b63 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-10.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-11.png b/docs/.vuepress/public/images/article/algorithm/graph-11.png new file mode 100644 index 000000000..e4982fa88 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-11.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-12.png b/docs/.vuepress/public/images/article/algorithm/graph-12.png new file mode 100644 index 000000000..ac91bab04 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-12.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-13.png b/docs/.vuepress/public/images/article/algorithm/graph-13.png new file mode 100644 index 000000000..613b93bdd Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-13.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/graph-14.png b/docs/.vuepress/public/images/article/algorithm/graph-14.png new file mode 100644 index 000000000..d058d84b8 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/graph-14.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-00.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-00.png new file mode 100644 index 000000000..3de4830b7 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-00.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-01.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-01.png new file mode 100644 index 000000000..02c0db6f2 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-02.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-02.png new file mode 100644 index 000000000..0677fae7e Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-03.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-03.png new file mode 100644 index 000000000..230e299b6 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-04.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-04.png new file mode 100644 index 000000000..e30c6eaeb Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-05.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-05.png new file mode 100644 index 000000000..3d76da231 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-06.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-06.png new file mode 100644 index 000000000..e80882897 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-07.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-07.png new file mode 100644 index 000000000..84c11501d Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-08.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-08.png new file mode 100644 index 000000000..b59199717 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-09.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-09.png new file mode 100644 index 000000000..94af53d33 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-09.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-10.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-10.png new file mode 100644 index 000000000..9614221f4 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-10.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-11.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-11.png new file mode 100644 index 000000000..3ae4c1671 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-11.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-12.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-12.png new file mode 100644 index 000000000..a427aff84 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-12.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-13.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-13.png new file mode 100644 index 000000000..b28967d9c Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-13.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-14.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-14.png new file mode 100644 index 000000000..4aa68694c Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-14.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-15.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-15.png new file mode 100644 index 000000000..a8037d061 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-15.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/bits-16.png b/docs/.vuepress/public/images/article/algorithm/logic/bits-16.png new file mode 100644 index 000000000..5a5c0c847 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/bits-16.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/cartesian-product-01.png b/docs/.vuepress/public/images/article/algorithm/logic/cartesian-product-01.png new file mode 100644 index 000000000..4f4973b10 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/cartesian-product-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/combinations-01.jpeg b/docs/.vuepress/public/images/article/algorithm/logic/combinations-01.jpeg new file mode 100644 index 000000000..6a80a91cd Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/combinations-01.jpeg differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/combinations-02.jpeg b/docs/.vuepress/public/images/article/algorithm/logic/combinations-02.jpeg new file mode 100644 index 000000000..9262f99c8 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/combinations-02.jpeg differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/combinations-03.png b/docs/.vuepress/public/images/article/algorithm/logic/combinations-03.png new file mode 100644 index 000000000..4b4b0e34b Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/combinations-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/combinations-04.png b/docs/.vuepress/public/images/article/algorithm/logic/combinations-04.png new file mode 100644 index 000000000..bf8e6738e Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/combinations-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/euclidean-01.png b/docs/.vuepress/public/images/article/algorithm/logic/euclidean-01.png new file mode 100644 index 000000000..aa3f5820f Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/euclidean-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/factorial-01.png b/docs/.vuepress/public/images/article/algorithm/logic/factorial-01.png new file mode 100644 index 000000000..ae61d1b9d Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/factorial-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/factorial-02.png b/docs/.vuepress/public/images/article/algorithm/logic/factorial-02.png new file mode 100644 index 000000000..cb4cb9a14 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/factorial-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fast-powering-01.png b/docs/.vuepress/public/images/article/algorithm/logic/fast-powering-01.png new file mode 100644 index 000000000..4cf5f51e9 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fast-powering-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fast-powering-02.png b/docs/.vuepress/public/images/article/algorithm/logic/fast-powering-02.png new file mode 100644 index 000000000..2fe171b1b Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fast-powering-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-01.png b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-01.png new file mode 100644 index 000000000..d43de374e Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-02.png b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-02.png new file mode 100644 index 000000000..42333d97b Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-03.png b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-03.png new file mode 100644 index 000000000..0087f8117 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-04.png b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-04.png new file mode 100644 index 000000000..7a6fe09cb Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-05.png b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-05.png new file mode 100644 index 000000000..2d6b053cc Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-06.png b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-06.png new file mode 100644 index 000000000..faf768c5c Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-07.png b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-07.png new file mode 100644 index 000000000..06b366275 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-08.png b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-08.png new file mode 100644 index 000000000..d92bcfdf7 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fibonacci-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fisher-yates-01.png b/docs/.vuepress/public/images/article/algorithm/logic/fisher-yates-01.png new file mode 100644 index 000000000..53941a639 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fisher-yates-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-01.png b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-01.png new file mode 100644 index 000000000..5ed06037a Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-02.png b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-02.png new file mode 100644 index 000000000..b2aa302ae Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-03.png b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-03.png new file mode 100644 index 000000000..78cfbb6e6 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-04.png b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-04.png new file mode 100644 index 000000000..8e7311952 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-05.png b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-05.png new file mode 100644 index 000000000..1044ed4ad Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-06.png b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-06.png new file mode 100644 index 000000000..4ab152360 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-07.png b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-07.png new file mode 100644 index 000000000..d445dca28 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-08.png b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-08.png new file mode 100644 index 000000000..c67018e7a Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/fourier-transform-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/integer-partition-01.png b/docs/.vuepress/public/images/article/algorithm/logic/integer-partition-01.png new file mode 100644 index 000000000..554ff27bf Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/integer-partition-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/integer-partition-02.png b/docs/.vuepress/public/images/article/algorithm/logic/integer-partition-02.png new file mode 100644 index 000000000..b48625ff3 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/integer-partition-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/integer-partition-03.png b/docs/.vuepress/public/images/article/algorithm/logic/integer-partition-03.png new file mode 100644 index 000000000..527435665 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/integer-partition-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-01.png b/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-01.png new file mode 100644 index 000000000..057990c89 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-02.png b/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-02.png new file mode 100644 index 000000000..f76f1868f Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-03.png b/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-03.png new file mode 100644 index 000000000..6232f2ae3 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-04.png b/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-04.png new file mode 100644 index 000000000..ec5c8fcf5 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/is-power-of-two-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/least-common-multiple-01.png b/docs/.vuepress/public/images/article/algorithm/logic/least-common-multiple-01.png new file mode 100644 index 000000000..4fb457f0d Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/least-common-multiple-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/least-common-multiple-02.png b/docs/.vuepress/public/images/article/algorithm/logic/least-common-multiple-02.png new file mode 100644 index 000000000..95028a21f Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/least-common-multiple-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/least-common-multiple-03.png b/docs/.vuepress/public/images/article/algorithm/logic/least-common-multiple-03.png new file mode 100644 index 000000000..aac04e2ca Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/least-common-multiple-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/liu-hui-01.png b/docs/.vuepress/public/images/article/algorithm/logic/liu-hui-01.png new file mode 100644 index 000000000..ac8a0bb94 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/liu-hui-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/liu-hui-02.png b/docs/.vuepress/public/images/article/algorithm/logic/liu-hui-02.png new file mode 100644 index 000000000..c5ad90243 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/liu-hui-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-01.png b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-01.png new file mode 100644 index 000000000..1af590929 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-02.png b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-02.png new file mode 100644 index 000000000..80dc232f5 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-03.png b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-03.png new file mode 100644 index 000000000..c6ab2db98 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-04.png b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-04.png new file mode 100644 index 000000000..446c9dedf Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-05.png b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-05.png new file mode 100644 index 000000000..2c3e3df2e Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-06.png b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-06.png new file mode 100644 index 000000000..9d14bae82 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-07.png b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-07.png new file mode 100644 index 000000000..361e27c6b Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/pascal-triangle-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/permutations-01.jpeg b/docs/.vuepress/public/images/article/algorithm/logic/permutations-01.jpeg new file mode 100644 index 000000000..3e5c0a7c9 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/permutations-01.jpeg differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/permutations-02.jpeg b/docs/.vuepress/public/images/article/algorithm/logic/permutations-02.jpeg new file mode 100644 index 000000000..ca70b4d8c Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/permutations-02.jpeg differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/power-set-01.png b/docs/.vuepress/public/images/article/algorithm/logic/power-set-01.png new file mode 100644 index 000000000..c5539fd84 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/power-set-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-01.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-01.png new file mode 100644 index 000000000..eba19cecb Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-02.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-02.png new file mode 100644 index 000000000..01b04c1c8 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-03.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-03.png new file mode 100644 index 000000000..f1353f8ca Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-04.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-04.png new file mode 100644 index 000000000..af071c826 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-05.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-05.png new file mode 100644 index 000000000..0e797b1e7 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-06.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-06.png new file mode 100644 index 000000000..93028b770 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-07.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-07.png new file mode 100644 index 000000000..2ba7076ed Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-08.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-08.png new file mode 100644 index 000000000..c7303efa1 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-09.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-09.png new file mode 100644 index 000000000..c2a8e3b4b Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-09.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-10.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-10.png new file mode 100644 index 000000000..110867ac8 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-10.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-11.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-11.png new file mode 100644 index 000000000..9a8c55d13 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-11.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-12.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-12.png new file mode 100644 index 000000000..63ac01fcb Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-12.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-13.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-13.png new file mode 100644 index 000000000..96b9f09a7 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-13.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-14.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-14.png new file mode 100644 index 000000000..e71fe0910 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-14.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-15.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-15.png new file mode 100644 index 000000000..8373410dd Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-15.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-16.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-16.png new file mode 100644 index 000000000..e957961a5 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-16.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-17.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-17.png new file mode 100644 index 000000000..4f2eec038 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-17.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-18.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-18.png new file mode 100644 index 000000000..fb679d55e Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-18.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-19.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-19.png new file mode 100644 index 000000000..baa4ebe87 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-19.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/primality-20.png b/docs/.vuepress/public/images/article/algorithm/logic/primality-20.png new file mode 100644 index 000000000..989043165 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/primality-20.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/radian-01.gif b/docs/.vuepress/public/images/article/algorithm/logic/radian-01.gif new file mode 100644 index 000000000..75598b261 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/radian-01.gif differ diff --git a/docs/.vuepress/public/images/article/algorithm/logic/sieve-of-eratosthenes-01.png b/docs/.vuepress/public/images/article/algorithm/logic/sieve-of-eratosthenes-01.png new file mode 100644 index 000000000..15a62f624 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/logic/sieve-of-eratosthenes-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-00.jpg b/docs/.vuepress/public/images/article/algorithm/model/model-00.jpg new file mode 100644 index 000000000..9ab617665 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-00.jpg differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-01.png b/docs/.vuepress/public/images/article/algorithm/model/model-01.png new file mode 100644 index 000000000..cbccb6c4c Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-02.png b/docs/.vuepress/public/images/article/algorithm/model/model-02.png new file mode 100644 index 000000000..b131cd9b5 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-03.png b/docs/.vuepress/public/images/article/algorithm/model/model-03.png new file mode 100644 index 000000000..70c7195ea Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-04.png b/docs/.vuepress/public/images/article/algorithm/model/model-04.png new file mode 100644 index 000000000..71d5aadc2 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-05.png b/docs/.vuepress/public/images/article/algorithm/model/model-05.png new file mode 100644 index 000000000..d6a2d73e9 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-06.png b/docs/.vuepress/public/images/article/algorithm/model/model-06.png new file mode 100644 index 000000000..b367bbce1 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-07.png b/docs/.vuepress/public/images/article/algorithm/model/model-07.png new file mode 100644 index 000000000..80a5419e4 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-08.png b/docs/.vuepress/public/images/article/algorithm/model/model-08.png new file mode 100644 index 000000000..4d669a026 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-09.png b/docs/.vuepress/public/images/article/algorithm/model/model-09.png new file mode 100644 index 000000000..56427425b Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-09.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-10.png b/docs/.vuepress/public/images/article/algorithm/model/model-10.png new file mode 100644 index 000000000..7a4d63427 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-10.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-11.png b/docs/.vuepress/public/images/article/algorithm/model/model-11.png new file mode 100644 index 000000000..36b999af7 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-11.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-12.png b/docs/.vuepress/public/images/article/algorithm/model/model-12.png new file mode 100644 index 000000000..8797f5213 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-12.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-13.png b/docs/.vuepress/public/images/article/algorithm/model/model-13.png new file mode 100644 index 000000000..3bf82fe2e Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-13.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-2-01.png b/docs/.vuepress/public/images/article/algorithm/model/model-2-01.png new file mode 100644 index 000000000..a70824634 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-2-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-2-02.png b/docs/.vuepress/public/images/article/algorithm/model/model-2-02.png new file mode 100644 index 000000000..5c94fbf6d Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-2-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-2-03.png b/docs/.vuepress/public/images/article/algorithm/model/model-2-03.png new file mode 100644 index 000000000..ee69efa4f Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-2-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-2-04.png b/docs/.vuepress/public/images/article/algorithm/model/model-2-04.png new file mode 100644 index 000000000..b47284cee Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-2-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-01.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-01.png new file mode 100644 index 000000000..26c860e33 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-02.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-02.png new file mode 100644 index 000000000..a01ed0c54 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-03.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-03.png new file mode 100644 index 000000000..a20bed0d4 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-04.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-04.png new file mode 100644 index 000000000..c8e2f8dec Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-05.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-05.png new file mode 100644 index 000000000..96c6f4fe3 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-06.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-06.png new file mode 100644 index 000000000..718d785d5 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-07.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-07.png new file mode 100644 index 000000000..913f13cc9 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-08.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-08.png new file mode 100644 index 000000000..85acc26d3 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-09.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-09.png new file mode 100644 index 000000000..b62533e2b Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-09.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-10.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-10.png new file mode 100644 index 000000000..372844ed2 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-10.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-11.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-11.png new file mode 100644 index 000000000..6f5f2fdce Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-11.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-12.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-12.png new file mode 100644 index 000000000..2e8695a29 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-12.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-13.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-13.png new file mode 100644 index 000000000..55e83ec73 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-13.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-14.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-14.png new file mode 100644 index 000000000..b7bd2e7b5 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-14.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-15.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-15.png new file mode 100644 index 000000000..b68a353b3 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-15.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-16.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-16.png new file mode 100644 index 000000000..b85374af8 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-16.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-17.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-17.png new file mode 100644 index 000000000..57530a4a3 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-17.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model-3-18.png b/docs/.vuepress/public/images/article/algorithm/model/model-3-18.png new file mode 100644 index 000000000..c119d22f1 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model-3-18.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/model/model.png b/docs/.vuepress/public/images/article/algorithm/model/model.png new file mode 100644 index 000000000..fef483a8a Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/model/model.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-23-01.png b/docs/.vuepress/public/images/article/algorithm/tree-23-01.png new file mode 100644 index 000000000..00a3bdc59 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-23-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-23-02.png b/docs/.vuepress/public/images/article/algorithm/tree-23-02.png new file mode 100644 index 000000000..c511ed250 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-23-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-23-03.png b/docs/.vuepress/public/images/article/algorithm/tree-23-03.png new file mode 100644 index 000000000..4840032d9 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-23-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-avl-01.png b/docs/.vuepress/public/images/article/algorithm/tree-avl-01.png new file mode 100644 index 000000000..9cc76a3be Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-avl-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-avl-02.png b/docs/.vuepress/public/images/article/algorithm/tree-avl-02.png new file mode 100644 index 000000000..29a9189f2 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-avl-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-avl-03.png b/docs/.vuepress/public/images/article/algorithm/tree-avl-03.png new file mode 100644 index 000000000..9c668da47 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-avl-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-avl-04.png b/docs/.vuepress/public/images/article/algorithm/tree-avl-04.png new file mode 100644 index 000000000..1bb36907d Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-avl-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-avl-05.png b/docs/.vuepress/public/images/article/algorithm/tree-avl-05.png new file mode 100644 index 000000000..756e849ac Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-avl-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-avl-06.png b/docs/.vuepress/public/images/article/algorithm/tree-avl-06.png new file mode 100644 index 000000000..1f26c34f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-avl-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-avl-07.png b/docs/.vuepress/public/images/article/algorithm/tree-avl-07.png new file mode 100644 index 000000000..1daefabeb Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-avl-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-avl-08.png b/docs/.vuepress/public/images/article/algorithm/tree-avl-08.png new file mode 100644 index 000000000..78a427742 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-avl-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-avl-09.png b/docs/.vuepress/public/images/article/algorithm/tree-avl-09.png new file mode 100644 index 000000000..7d098ff99 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-avl-09.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-rbt-01.png b/docs/.vuepress/public/images/article/algorithm/tree-rbt-01.png new file mode 100644 index 000000000..1783bb3eb Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-rbt-01.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-rbt-02.png b/docs/.vuepress/public/images/article/algorithm/tree-rbt-02.png new file mode 100644 index 000000000..d4613fe5c Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-rbt-02.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-rbt-03.png b/docs/.vuepress/public/images/article/algorithm/tree-rbt-03.png new file mode 100644 index 000000000..17109c514 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-rbt-03.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-rbt-04.png b/docs/.vuepress/public/images/article/algorithm/tree-rbt-04.png new file mode 100644 index 000000000..6e7429ab1 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-rbt-04.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-rbt-05.png b/docs/.vuepress/public/images/article/algorithm/tree-rbt-05.png new file mode 100644 index 000000000..140af5b30 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-rbt-05.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-rbt-06.png b/docs/.vuepress/public/images/article/algorithm/tree-rbt-06.png new file mode 100644 index 000000000..d00392448 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-rbt-06.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-rbt-07.png b/docs/.vuepress/public/images/article/algorithm/tree-rbt-07.png new file mode 100644 index 000000000..50f3560b4 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-rbt-07.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-rbt-08.png b/docs/.vuepress/public/images/article/algorithm/tree-rbt-08.png new file mode 100644 index 000000000..3e294274b Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-rbt-08.png differ diff --git a/docs/.vuepress/public/images/article/algorithm/tree-rbt-09.png b/docs/.vuepress/public/images/article/algorithm/tree-rbt-09.png new file mode 100644 index 000000000..e90015865 Binary files /dev/null and b/docs/.vuepress/public/images/article/algorithm/tree-rbt-09.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-00.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-00.png new file mode 100644 index 000000000..c0a453ef6 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-00.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-01.png new file mode 100644 index 000000000..53866596b Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-02.png new file mode 100644 index 000000000..73ae03a4b Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-03.png new file mode 100644 index 000000000..305b8669a Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-04.png new file mode 100644 index 000000000..a85f507b6 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-05.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-05.png new file mode 100644 index 000000000..b118f7e67 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-05.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-06.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-06.png new file mode 100644 index 000000000..663e77fae Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-06.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-07.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-07.png new file mode 100644 index 000000000..1f862a692 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-07.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-08.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-08.png new file mode 100644 index 000000000..ceca0d968 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-0-08.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-10-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-10-01.png new file mode 100644 index 000000000..12798a28c Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-10-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-10-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-10-02.png new file mode 100644 index 000000000..19a7ee471 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-10-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-10-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-10-03.png new file mode 100644 index 000000000..9e1886daa Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-10-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-11-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-11-01.png new file mode 100644 index 000000000..957117005 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-11-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-11-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-11-02.png new file mode 100644 index 000000000..1f4921cb7 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-11-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-11-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-11-03.png new file mode 100644 index 000000000..de1ac5253 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-11-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-12-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-12-01.png new file mode 100644 index 000000000..da7a5787b Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-12-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-12-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-12-02.png new file mode 100644 index 000000000..a21e013e8 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-12-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-12-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-12-03.png new file mode 100644 index 000000000..f235136df Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-12-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-13-00.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-13-00.png new file mode 100644 index 000000000..74e4318db Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-13-00.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-13-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-13-01.png new file mode 100644 index 000000000..4e43322f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-13-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-13-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-13-02.png new file mode 100644 index 000000000..4b9a294bb Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-13-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-14-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-14-01.png new file mode 100644 index 000000000..6cbf878ba Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-14-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-14-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-14-02.png new file mode 100644 index 000000000..111c11178 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-14-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-14-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-14-03.png new file mode 100644 index 000000000..8f0db5a58 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-14-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-01.png new file mode 100644 index 000000000..e395198d9 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-02.png new file mode 100644 index 000000000..a34671df0 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-03.png new file mode 100644 index 000000000..af623c7d7 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-04.png new file mode 100644 index 000000000..22046a771 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-15-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-16-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-16-01.png new file mode 100644 index 000000000..304af5ec1 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-16-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-16-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-16-02.png new file mode 100644 index 000000000..d754fc74c Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-16-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-16-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-16-03.png new file mode 100644 index 000000000..e3e212664 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-16-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-17-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-17-01.png new file mode 100644 index 000000000..28babbf1a Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-17-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-17-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-17-02.png new file mode 100644 index 000000000..076598980 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-17-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-17-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-17-03.png new file mode 100644 index 000000000..0db24a65d Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-17-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-18-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-18-01.png new file mode 100644 index 000000000..263f263ec Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-18-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-18-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-18-02.png new file mode 100644 index 000000000..06449d028 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-18-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-18-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-18-03.png new file mode 100644 index 000000000..72089e499 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-18-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-01.png new file mode 100644 index 000000000..83c7ec2da Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-02.png new file mode 100644 index 000000000..1dae30eb4 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-03.png new file mode 100644 index 000000000..0b27f443c Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-04.png new file mode 100644 index 000000000..ee83fb611 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-05.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-05.png new file mode 100644 index 000000000..336f9daf6 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-19-05.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-20-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-20-01.png new file mode 100644 index 000000000..a905aa1ef Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-20-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-20-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-20-02.png new file mode 100644 index 000000000..c0f438deb Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-20-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-20-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-20-03.png new file mode 100644 index 000000000..5a3e70ad0 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-20-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-21-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-21-01.png new file mode 100644 index 000000000..edd85ba01 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-21-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-21-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-21-02.png new file mode 100644 index 000000000..04d83b3b0 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-21-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-21-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-21-03.png new file mode 100644 index 000000000..f23e5c6d2 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-21-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-01.png new file mode 100644 index 000000000..a76798c6c Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-02.png new file mode 100644 index 000000000..27dceaaa1 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-03.png new file mode 100644 index 000000000..390cfa17d Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-04.png new file mode 100644 index 000000000..bc9973f07 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-05.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-05.png new file mode 100644 index 000000000..2bed055ba Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-22-05.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-01.png new file mode 100644 index 000000000..7f3da5737 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-02.png new file mode 100644 index 000000000..df3c169e3 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-03.png new file mode 100644 index 000000000..492c36e5c Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-04.png new file mode 100644 index 000000000..c57d80734 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-23-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-01.png new file mode 100644 index 000000000..6522d3373 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-02.png new file mode 100644 index 000000000..777fbddc6 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-03.png new file mode 100644 index 000000000..f235136df Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-04.png new file mode 100644 index 000000000..bfa2f97af Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-05.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-05.png new file mode 100644 index 000000000..7ca86ea62 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-05.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-06.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-06.png new file mode 100644 index 000000000..969df3664 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-06.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-07.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-07.png new file mode 100644 index 000000000..4f07017a6 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-07.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-08.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-08.png new file mode 100644 index 000000000..c68d0b016 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-08.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-09.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-09.png new file mode 100644 index 000000000..1ba24cd4a Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-09.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-10.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-10.png new file mode 100644 index 000000000..4ffad2051 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-230610-10.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-01.png new file mode 100644 index 000000000..551a9fbf0 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-02.png new file mode 100644 index 000000000..e9bc9cfef Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-03.png new file mode 100644 index 000000000..1bcc98c29 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-04.png new file mode 100644 index 000000000..cb1216264 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-05.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-05.png new file mode 100644 index 000000000..ceca0d968 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-24-05.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-01.png new file mode 100644 index 000000000..0f195b3c3 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-02.png new file mode 100644 index 000000000..6094678a4 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-03.png new file mode 100644 index 000000000..c77ae637d Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-04.png new file mode 100644 index 000000000..582e2e620 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-05.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-05.png new file mode 100644 index 000000000..da84f28e8 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-05.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-06.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-06.png new file mode 100644 index 000000000..04124fdeb Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-06.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-07.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-07.png new file mode 100644 index 000000000..6a8e5a651 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-07.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-08.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-08.png new file mode 100644 index 000000000..773df0ccb Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-08.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-09.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-09.png new file mode 100644 index 000000000..96a5873d7 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-25-09.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-26-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-26-01.png new file mode 100644 index 000000000..8bc93b089 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-26-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-26-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-26-02.png new file mode 100644 index 000000000..020bc98f1 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-26-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-26-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-26-03.png new file mode 100644 index 000000000..6ee68b8c8 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-26-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-01.png new file mode 100644 index 000000000..1f862a692 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-02.png new file mode 100644 index 000000000..4b87a21c3 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-03.png new file mode 100644 index 000000000..753be8732 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-04.png new file mode 100644 index 000000000..82fca6aa8 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-27-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-01.png new file mode 100644 index 000000000..edbda3a09 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-02.png new file mode 100644 index 000000000..6fd50ec48 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-03.png new file mode 100644 index 000000000..26e0ee109 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-04.png new file mode 100644 index 000000000..a014fccd9 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-28-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-29-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-29-01.png new file mode 100644 index 000000000..a98a22eec Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-29-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-7-00.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-7-00.png new file mode 100644 index 000000000..9a104bab1 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-7-00.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-7-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-7-01.png new file mode 100644 index 000000000..0194f9036 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-7-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-7-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-7-02.png new file mode 100644 index 000000000..443311990 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-7-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-00.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-00.png new file mode 100644 index 000000000..ed81db01c Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-00.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-01.png new file mode 100644 index 000000000..4b5f691cf Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-02.png new file mode 100644 index 000000000..a42290d74 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-02.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-03.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-03.png new file mode 100644 index 000000000..d39aaebe0 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-03.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-04.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-04.png new file mode 100644 index 000000000..616e22565 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-8-04.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-9-00.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-9-00.png new file mode 100644 index 000000000..23c704418 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-9-00.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-9-01.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-9-01.png new file mode 100644 index 000000000..8deebbdbb Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-9-01.png differ diff --git a/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-9-02.png b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-9-02.png new file mode 100644 index 000000000..a086f0db9 Binary files /dev/null and b/docs/.vuepress/public/images/article/assembly/api-gateway/api-gateway-9-02.png differ diff --git a/docs/.vuepress/public/images/article/develop/develop-scheme-mq-01.png b/docs/.vuepress/public/images/article/develop/develop-scheme-mq-01.png new file mode 100755 index 000000000..f133b477d Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/develop-scheme-mq-01.png differ diff --git a/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-01.png b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-01.png new file mode 100644 index 000000000..4ec0fba81 Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-01.png differ diff --git a/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-02.png b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-02.png new file mode 100644 index 000000000..2f2cf2969 Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-02.png differ diff --git a/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-03.png b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-03.png new file mode 100644 index 000000000..851058767 Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-03.png differ diff --git a/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-04.png b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-04.png new file mode 100644 index 000000000..df8826f0a Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-04.png differ diff --git a/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-05.png b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-05.png new file mode 100644 index 000000000..63f901f38 Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-05.png differ diff --git a/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-06.png b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-06.png new file mode 100644 index 000000000..a2e057da5 Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-06.png differ diff --git a/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-07.png b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-07.png new file mode 100644 index 000000000..9a4d858f5 Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-07.png differ diff --git a/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-08.png b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-08.png new file mode 100644 index 000000000..95ceb2075 Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-08.png differ diff --git a/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-09.png b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-09.png new file mode 100644 index 000000000..e85cebf7d Binary files /dev/null and b/docs/.vuepress/public/images/article/develop/xfg-dev-tech-design-240528-09.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-01.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-01.png new file mode 100644 index 000000000..f284e1fba Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-01.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-02.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-02.png new file mode 100644 index 000000000..713ed65d3 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-02.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-03.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-03.png new file mode 100644 index 000000000..3ce0c723e Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-03.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-04.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-04.png new file mode 100644 index 000000000..d246c46b9 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-04.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-05.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-05.png new file mode 100644 index 000000000..7d91301e8 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-05.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-06.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-06.png new file mode 100644 index 000000000..ea1085591 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-06.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-07.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-07.png new file mode 100644 index 000000000..d503a128e Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-07.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-08.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-08.png new file mode 100644 index 000000000..c17a11c58 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-08.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-09.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-09.png new file mode 100644 index 000000000..155ab2043 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-09.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-10.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-10.png new file mode 100644 index 000000000..ee1c382c8 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-10.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-11.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-11.png new file mode 100644 index 000000000..017a90571 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-11.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-12.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-12.png new file mode 100644 index 000000000..d55285de8 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-12.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-13.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-13.png new file mode 100644 index 000000000..695445f54 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-13.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-14.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-14.png new file mode 100644 index 000000000..00a51ae35 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-14.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-15.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-15.png new file mode 100644 index 000000000..cefe33327 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-15.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-16.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-16.png new file mode 100644 index 000000000..07f55a53c Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-16.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-17.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-17.png new file mode 100644 index 000000000..6ec658d8e Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-17.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-18.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-18.png new file mode 100644 index 000000000..63099899a Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-18.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-19.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-19.png new file mode 100644 index 000000000..53e8c194a Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-19.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-230321-20.png b/docs/.vuepress/public/images/article/devops/dev-ops-230321-20.png new file mode 100644 index 000000000..958f54c97 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-230321-20.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-01.png b/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-01.png new file mode 100644 index 000000000..d099cf39b Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-01.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-02.png b/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-02.png new file mode 100644 index 000000000..aedd55765 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-02.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-03.png b/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-03.png new file mode 100644 index 000000000..6f470e379 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-03.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-04.png b/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-04.png new file mode 100644 index 000000000..054225fea Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-nginx-230418-04.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-portainer-230418-01.png b/docs/.vuepress/public/images/article/devops/dev-ops-portainer-230418-01.png new file mode 100644 index 000000000..2b9accbe1 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-portainer-230418-01.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-portainer-230418-02.png b/docs/.vuepress/public/images/article/devops/dev-ops-portainer-230418-02.png new file mode 100644 index 000000000..4fe82345e Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-portainer-230418-02.png differ diff --git a/docs/.vuepress/public/images/article/devops/dev-ops-portainer-230418-03.png b/docs/.vuepress/public/images/article/devops/dev-ops-portainer-230418-03.png new file mode 100644 index 000000000..e0f40d9da Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/dev-ops-portainer-230418-03.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-01.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-01.png new file mode 100644 index 000000000..13a26722d Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-01.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-02.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-02.png new file mode 100644 index 000000000..7b1facf4f Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-02.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-03.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-03.png new file mode 100644 index 000000000..ffeab6891 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-03.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-04.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-04.png new file mode 100644 index 000000000..03c5725ba Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-04.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-05.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-05.png new file mode 100644 index 000000000..088455d29 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-05.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-06.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-06.png new file mode 100644 index 000000000..7f61a3c29 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-06.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-07.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-07.png new file mode 100644 index 000000000..a56688f99 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-07.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-08.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-08.png new file mode 100644 index 000000000..1994f3a47 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-08.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-09.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-09.png new file mode 100644 index 000000000..3d71735e9 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-09.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-10.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-10.png new file mode 100644 index 000000000..382f39e09 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-10.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-11.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-11.png new file mode 100644 index 000000000..51ff3d691 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-11.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-12.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-12.png new file mode 100644 index 000000000..0513eddd0 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-12.png differ diff --git a/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-13.png b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-13.png new file mode 100644 index 000000000..515437932 Binary files /dev/null and b/docs/.vuepress/public/images/article/devops/xfg-dev-tech-blog-13.png differ diff --git a/docs/.vuepress/public/images/article/product/book/mybatis-01.png b/docs/.vuepress/public/images/article/product/book/mybatis-01.png new file mode 100644 index 000000000..2eb642445 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/mybatis-01.png differ diff --git a/docs/.vuepress/public/images/article/product/book/mybatis-02.png b/docs/.vuepress/public/images/article/product/book/mybatis-02.png new file mode 100644 index 000000000..07c276a3c Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/mybatis-02.png differ diff --git a/docs/.vuepress/public/images/article/product/book/mybatis-03.png b/docs/.vuepress/public/images/article/product/book/mybatis-03.png new file mode 100644 index 000000000..d5b7270dc Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/mybatis-03.png differ diff --git a/docs/.vuepress/public/images/article/product/book/mybatis-04.png b/docs/.vuepress/public/images/article/product/book/mybatis-04.png new file mode 100644 index 000000000..4dafa3de8 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/mybatis-04.png differ diff --git a/docs/.vuepress/public/images/article/product/book/mybatis-05.png b/docs/.vuepress/public/images/article/product/book/mybatis-05.png new file mode 100644 index 000000000..e145d7c7c Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/mybatis-05.png differ diff --git a/docs/.vuepress/public/images/article/product/book/mybatis-06.png b/docs/.vuepress/public/images/article/product/book/mybatis-06.png new file mode 100644 index 000000000..71f5c38af Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/mybatis-06.png differ diff --git a/docs/.vuepress/public/images/article/product/book/mybatis-t-01.png b/docs/.vuepress/public/images/article/product/book/mybatis-t-01.png new file mode 100644 index 000000000..c9b324413 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/mybatis-t-01.png differ diff --git a/docs/.vuepress/public/images/article/product/book/spring-t-01.png b/docs/.vuepress/public/images/article/product/book/spring-t-01.png new file mode 100644 index 000000000..6d73810f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/spring-t-01.png differ diff --git a/docs/.vuepress/public/images/article/product/book/spring-t-02.png b/docs/.vuepress/public/images/article/product/book/spring-t-02.png new file mode 100644 index 000000000..8908e9b92 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/spring-t-02.png differ diff --git a/docs/.vuepress/public/images/article/product/book/spring-t-03.png b/docs/.vuepress/public/images/article/product/book/spring-t-03.png new file mode 100644 index 000000000..74de9abd1 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/spring-t-03.png differ diff --git a/docs/.vuepress/public/images/article/product/book/spring-t-04.png b/docs/.vuepress/public/images/article/product/book/spring-t-04.png new file mode 100644 index 000000000..5d4b94db7 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/spring-t-04.png differ diff --git a/docs/.vuepress/public/images/article/product/book/spring-t-05.png b/docs/.vuepress/public/images/article/product/book/spring-t-05.png new file mode 100644 index 000000000..520833a74 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/spring-t-05.png differ diff --git a/docs/.vuepress/public/images/article/product/book/spring-t-06.png b/docs/.vuepress/public/images/article/product/book/spring-t-06.png new file mode 100644 index 000000000..860044c7a Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/spring-t-06.png differ diff --git a/docs/.vuepress/public/images/article/product/book/spring-t-07.png b/docs/.vuepress/public/images/article/product/book/spring-t-07.png new file mode 100644 index 000000000..dd516edc1 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/book/spring-t-07.png differ diff --git a/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-00.png b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-00.png new file mode 100644 index 000000000..e05eb3b18 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-00.png differ diff --git a/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-01.png b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-01.png new file mode 100644 index 000000000..d6df6698f Binary files /dev/null and b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-01.png differ diff --git a/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-02.png b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-02.png new file mode 100644 index 000000000..e26897778 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-02.png differ diff --git a/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-03.png b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-03.png new file mode 100644 index 000000000..7af35eb54 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-03.png differ diff --git a/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-04.png b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-04.png new file mode 100644 index 000000000..3b866f80d Binary files /dev/null and b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-04.png differ diff --git a/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-05.png b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-05.png new file mode 100644 index 000000000..0e599e9c4 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.1-05.png differ diff --git a/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.5-01.png b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.5-01.png new file mode 100644 index 000000000..73d0010a8 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.5-01.png differ diff --git a/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.5-02.png b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.5-02.png new file mode 100644 index 000000000..aa70d7799 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.5-02.png differ diff --git a/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.5-03.png b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.5-03.png new file mode 100644 index 000000000..2de7fc584 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/idea-plugin/vo2dto-2.5.5-03.png differ diff --git a/docs/.vuepress/public/images/article/product/software/product-walicode-00.png b/docs/.vuepress/public/images/article/product/software/product-walicode-00.png new file mode 100644 index 000000000..c6cb4d13c Binary files /dev/null and b/docs/.vuepress/public/images/article/product/software/product-walicode-00.png differ diff --git a/docs/.vuepress/public/images/article/product/software/product-walicode-01.png b/docs/.vuepress/public/images/article/product/software/product-walicode-01.png new file mode 100644 index 000000000..a5ca4bbca Binary files /dev/null and b/docs/.vuepress/public/images/article/product/software/product-walicode-01.png differ diff --git a/docs/.vuepress/public/images/article/product/software/product-walicode-02.png b/docs/.vuepress/public/images/article/product/software/product-walicode-02.png new file mode 100644 index 000000000..85df57845 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/software/product-walicode-02.png differ diff --git a/docs/.vuepress/public/images/article/product/software/product-walicode-03.png b/docs/.vuepress/public/images/article/product/software/product-walicode-03.png new file mode 100644 index 000000000..3f89becd6 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/software/product-walicode-03.png differ diff --git a/docs/.vuepress/public/images/article/product/software/product-walicode-04.png b/docs/.vuepress/public/images/article/product/software/product-walicode-04.png new file mode 100644 index 000000000..6c145f6db Binary files /dev/null and b/docs/.vuepress/public/images/article/product/software/product-walicode-04.png differ diff --git a/docs/.vuepress/public/images/article/product/software/product-walicode-05.png b/docs/.vuepress/public/images/article/product/software/product-walicode-05.png new file mode 100644 index 000000000..3f89becd6 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/software/product-walicode-05.png differ diff --git a/docs/.vuepress/public/images/article/product/software/product-walicode-06.png b/docs/.vuepress/public/images/article/product/software/product-walicode-06.png new file mode 100644 index 000000000..dbcc4dd94 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/software/product-walicode-06.png differ diff --git a/docs/.vuepress/public/images/article/product/software/product-walicode-07.png b/docs/.vuepress/public/images/article/product/software/product-walicode-07.png new file mode 100644 index 000000000..90c3473f8 Binary files /dev/null and b/docs/.vuepress/public/images/article/product/software/product-walicode-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/ai-agent-scaffold.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/ai-agent-scaffold.png new file mode 100644 index 000000000..ad5ff84e6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/ai-agent-scaffold.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-0-0-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-0-0-01.png new file mode 100644 index 000000000..4b0cad0f1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-0-0-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-mobileopenclaw-260307-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-mobileopenclaw-260307-01.png new file mode 100644 index 000000000..c4f70f510 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-mobileopenclaw-260307-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-mobileopenclaw-260307-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-mobileopenclaw-260307-02.png new file mode 100644 index 000000000..c92739856 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-mobileopenclaw-260307-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-mobileopenclaw-260307-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-mobileopenclaw-260307-03.png new file mode 100644 index 000000000..0d72f852c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-0/images/ai-agent-scaffold-mobileopenclaw-260307-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-01.png new file mode 100644 index 000000000..ca2f8ce62 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-02.png new file mode 100644 index 000000000..6d47c797f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-03.png new file mode 100644 index 000000000..2e066f914 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-04.png new file mode 100644 index 000000000..851f16dd2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-05.png new file mode 100644 index 000000000..040b9e8e0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-06.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-06.png new file mode 100644 index 000000000..b2f6d1b8c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-07.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-07.png new file mode 100644 index 000000000..b47852838 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-08.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-08.png new file mode 100644 index 000000000..a63c2a826 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-09.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-09.png new file mode 100644 index 000000000..e3a12db64 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-10.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-10.png new file mode 100644 index 000000000..1543caaba Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ai-agent-scaffold-1-1-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ppt.pptx b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ppt.pptx new file mode 100644 index 000000000..a85d6f698 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-1/images/ppt.pptx differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-01.png new file mode 100644 index 000000000..758751459 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-02.png new file mode 100644 index 000000000..469fc7c0b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-03.png new file mode 100644 index 000000000..2a0ef5509 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-04.png new file mode 100644 index 000000000..0cfb1c63f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-05.png new file mode 100644 index 000000000..e370f0354 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-1/1-2/images/ai-agent-scaffold-1-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-1/images/ai-agent-scaffold-2-1-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-1/images/ai-agent-scaffold-2-1-01.png new file mode 100644 index 000000000..485753b6c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-1/images/ai-agent-scaffold-2-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-1/images/ai-agent-scaffold-2-1-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-1/images/ai-agent-scaffold-2-1-02.png new file mode 100644 index 000000000..b660adc6b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-1/images/ai-agent-scaffold-2-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-1/images/ai-agent-scaffold-2-1-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-1/images/ai-agent-scaffold-2-1-03.png new file mode 100644 index 000000000..cf4be75d2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-1/images/ai-agent-scaffold-2-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-10/images/ai-agent-scaffold-2-10-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-10/images/ai-agent-scaffold-2-10-01.png new file mode 100644 index 000000000..229a3b43a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-10/images/ai-agent-scaffold-2-10-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-10/images/ai-agent-scaffold-2-10-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-10/images/ai-agent-scaffold-2-10-02.png new file mode 100644 index 000000000..8fbeaccfa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-10/images/ai-agent-scaffold-2-10-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-11/images/ai-agent-scaffold-2-11-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-11/images/ai-agent-scaffold-2-11-01.png new file mode 100644 index 000000000..2b67697d9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-11/images/ai-agent-scaffold-2-11-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-11/images/ai-agent-scaffold-2-11-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-11/images/ai-agent-scaffold-2-11-02.png new file mode 100644 index 000000000..004765e3f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-11/images/ai-agent-scaffold-2-11-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-01.png new file mode 100644 index 000000000..1cfc8c21d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-02.png new file mode 100644 index 000000000..30f9c172c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-03.png new file mode 100644 index 000000000..c9457310f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-04.png new file mode 100644 index 000000000..a8492b570 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-12/images/ai-agent-scaffold-2-12-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-13/images/ai-agent-scaffold-2-13-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-13/images/ai-agent-scaffold-2-13-01.png new file mode 100644 index 000000000..9bf59bbc9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-13/images/ai-agent-scaffold-2-13-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-13/images/ai-agent-scaffold-2-13-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-13/images/ai-agent-scaffold-2-13-02.png new file mode 100644 index 000000000..63bf34203 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-13/images/ai-agent-scaffold-2-13-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-14/images/ai-agent-scaffold-2-14-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-14/images/ai-agent-scaffold-2-14-01.png new file mode 100644 index 000000000..1f3c17c7c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-14/images/ai-agent-scaffold-2-14-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-14/images/ai-agent-scaffold-2-14-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-14/images/ai-agent-scaffold-2-14-02.png new file mode 100644 index 000000000..8660965b1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-14/images/ai-agent-scaffold-2-14-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-15/images/ai-agent-scaffold-2-15-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-15/images/ai-agent-scaffold-2-15-01.png new file mode 100644 index 000000000..8ace6c2a7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-15/images/ai-agent-scaffold-2-15-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-15/images/ai-agent-scaffold-2-15-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-15/images/ai-agent-scaffold-2-15-02.png new file mode 100644 index 000000000..1a57704c4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-15/images/ai-agent-scaffold-2-15-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-01.png new file mode 100644 index 000000000..666af7610 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-02.png new file mode 100644 index 000000000..4c43047c7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-03.png new file mode 100644 index 000000000..d88ff2f09 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-04.png new file mode 100644 index 000000000..d71bc7927 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-05.png new file mode 100644 index 000000000..b23d69f16 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-06.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-06.png new file mode 100644 index 000000000..372fee3dd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-07.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-07.png new file mode 100644 index 000000000..b1338d4cb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-08.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-08.png new file mode 100644 index 000000000..20d92c51f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-09.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-09.png new file mode 100644 index 000000000..0ca10afa8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-16/images/ai-agent-scaffold-2-16-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-17/images/ai-agent-scaffold-2-17-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-17/images/ai-agent-scaffold-2-17-01.png new file mode 100644 index 000000000..bb322427c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-17/images/ai-agent-scaffold-2-17-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-17/images/ai-agent-scaffold-2-17-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-17/images/ai-agent-scaffold-2-17-02.png new file mode 100644 index 000000000..d9bcd7323 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-17/images/ai-agent-scaffold-2-17-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-18/images/ai-agent-scaffold-2-18-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-18/images/ai-agent-scaffold-2-18-01.png new file mode 100644 index 000000000..bc9f321e4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-18/images/ai-agent-scaffold-2-18-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-18/images/ai-agent-scaffold-2-18-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-18/images/ai-agent-scaffold-2-18-02.png new file mode 100644 index 000000000..7790de817 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-18/images/ai-agent-scaffold-2-18-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-18/images/ai-agent-scaffold-2-18-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-18/images/ai-agent-scaffold-2-18-03.png new file mode 100644 index 000000000..3abb8d05f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-18/images/ai-agent-scaffold-2-18-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-01.png new file mode 100644 index 000000000..408e21f03 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-02.png new file mode 100644 index 000000000..57495dd40 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-03.png new file mode 100644 index 000000000..ee86f3013 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-04.png new file mode 100644 index 000000000..b0c012e9d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-05.png new file mode 100644 index 000000000..bf735ee7a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-06.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-06.png new file mode 100644 index 000000000..6e820fbae Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-19/images/ai-agent-scaffold-2-19-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-2/images/ai-agent-scaffold-2-2-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-2/images/ai-agent-scaffold-2-2-01.png new file mode 100644 index 000000000..63368a463 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-2/images/ai-agent-scaffold-2-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-20/images/ai-agent-scaffold-2-20-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-20/images/ai-agent-scaffold-2-20-01.png new file mode 100644 index 000000000..ebdcc9414 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-20/images/ai-agent-scaffold-2-20-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-20/images/ai-agent-scaffold-2-20-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-20/images/ai-agent-scaffold-2-20-02.png new file mode 100644 index 000000000..453650b99 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-20/images/ai-agent-scaffold-2-20-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-20/images/ai-agent-scaffold-2-20-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-20/images/ai-agent-scaffold-2-20-03.png new file mode 100644 index 000000000..5f638861d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-20/images/ai-agent-scaffold-2-20-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-3/images/ai-agent-scaffold-2-3-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-3/images/ai-agent-scaffold-2-3-01.png new file mode 100644 index 000000000..1e196eca1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-3/images/ai-agent-scaffold-2-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-3/images/ai-agent-scaffold-2-3-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-3/images/ai-agent-scaffold-2-3-02.png new file mode 100644 index 000000000..f285f5298 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-3/images/ai-agent-scaffold-2-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-3/images/ai-agent-scaffold-2-3-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-3/images/ai-agent-scaffold-2-3-03.png new file mode 100644 index 000000000..0b059f011 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-3/images/ai-agent-scaffold-2-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-4/images/ai-agent-scaffold-2-4-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-4/images/ai-agent-scaffold-2-4-01.png new file mode 100644 index 000000000..ecd999b17 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-4/images/ai-agent-scaffold-2-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-4/images/ai-agent-scaffold-2-4-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-4/images/ai-agent-scaffold-2-4-02.png new file mode 100644 index 000000000..72fc4fcd4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-4/images/ai-agent-scaffold-2-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-4/images/ai-agent-scaffold-2-4-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-4/images/ai-agent-scaffold-2-4-03.png new file mode 100644 index 000000000..7cffb5490 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-4/images/ai-agent-scaffold-2-4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-5/images/ai-agent-scaffold-2-5-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-5/images/ai-agent-scaffold-2-5-01.png new file mode 100644 index 000000000..d6f02ba35 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-5/images/ai-agent-scaffold-2-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-5/images/ai-agent-scaffold-2-5-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-5/images/ai-agent-scaffold-2-5-02.png new file mode 100644 index 000000000..351b0fcb4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-5/images/ai-agent-scaffold-2-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-6/images/ai-agent-scaffold-2-6-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-6/images/ai-agent-scaffold-2-6-01.png new file mode 100644 index 000000000..89f882a13 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-6/images/ai-agent-scaffold-2-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-6/images/ai-agent-scaffold-2-6-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-6/images/ai-agent-scaffold-2-6-02.png new file mode 100644 index 000000000..434a31e8c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-6/images/ai-agent-scaffold-2-6-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-7/images/ai-agent-scaffold-2-7-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-7/images/ai-agent-scaffold-2-7-01.png new file mode 100644 index 000000000..e6947c2c5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-7/images/ai-agent-scaffold-2-7-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-7/images/ai-agent-scaffold-2-7-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-7/images/ai-agent-scaffold-2-7-02.png new file mode 100644 index 000000000..60a04dae0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-7/images/ai-agent-scaffold-2-7-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-8/images/ai-agent-scaffold-2-8-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-8/images/ai-agent-scaffold-2-8-01.png new file mode 100644 index 000000000..b4727be1a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-8/images/ai-agent-scaffold-2-8-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-8/images/ai-agent-scaffold-2-8-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-8/images/ai-agent-scaffold-2-8-02.png new file mode 100644 index 000000000..37fdd953e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-8/images/ai-agent-scaffold-2-8-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-9/images/ai-agent-scaffold-2-9-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-9/images/ai-agent-scaffold-2-9-01.png new file mode 100644 index 000000000..b83e49831 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-9/images/ai-agent-scaffold-2-9-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-9/images/ai-agent-scaffold-2-9-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-9/images/ai-agent-scaffold-2-9-02.png new file mode 100644 index 000000000..a13e30d02 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-2/2-9/images/ai-agent-scaffold-2-9-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-01.png new file mode 100644 index 000000000..469fc7c0b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-02.png new file mode 100644 index 000000000..f7c6a87de Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-03.png new file mode 100644 index 000000000..4a58ccc72 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-04.png new file mode 100644 index 000000000..5ce511bbb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-05.png new file mode 100644 index 000000000..999367a95 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-06.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-06.png new file mode 100644 index 000000000..fd8b3dbb8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-07.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-07.png new file mode 100644 index 000000000..5a310decb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-08.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-08.png new file mode 100644 index 000000000..3ec34129e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-09.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-09.png new file mode 100644 index 000000000..75e3ab76d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-10.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-10.png new file mode 100644 index 000000000..bd033bfd5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-1/images/ai-agent-scaffold-3-1-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-01.png new file mode 100644 index 000000000..8c66787e1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-02.png new file mode 100644 index 000000000..0c37fa725 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-03.png new file mode 100644 index 000000000..9384e2d8d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-04.png new file mode 100644 index 000000000..683ffa91c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-05.png new file mode 100644 index 000000000..5eaca56de Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-06.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-06.png new file mode 100644 index 000000000..dbcba126c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-07.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-07.png new file mode 100644 index 000000000..73b4ae752 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-2/images/ai-agent-scaffold-3-2-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-01.png new file mode 100644 index 000000000..37d7ef507 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-02.png new file mode 100644 index 000000000..accc9a261 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-03.png new file mode 100644 index 000000000..895ae2e0d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-04.png new file mode 100644 index 000000000..579971d26 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-05.png new file mode 100644 index 000000000..b9b6fafee Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-06.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-06.png new file mode 100644 index 000000000..a021d9dc6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-07.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-07.png new file mode 100644 index 000000000..7a4d372ed Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-08.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-08.png new file mode 100644 index 000000000..872cfa4aa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-09.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-09.png new file mode 100644 index 000000000..b59b57b0d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-10.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-10.png new file mode 100644 index 000000000..3fc71458a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-3/3-3/images/ai-agent-scaffold-3-3-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-0/images/ai-agent-scaffold-4-0-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-0/images/ai-agent-scaffold-4-0-01.png new file mode 100644 index 000000000..06a288107 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-0/images/ai-agent-scaffold-4-0-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-0/images/ai-agent-scaffold-4-0-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-0/images/ai-agent-scaffold-4-0-02.png new file mode 100644 index 000000000..d6b2533d1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-0/images/ai-agent-scaffold-4-0-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-01.png new file mode 100644 index 000000000..cbf0a266a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-02.png new file mode 100644 index 000000000..6285e89ed Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-03.png new file mode 100644 index 000000000..9e7553045 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-04.png new file mode 100644 index 000000000..dc32e860d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-1/images/ai-agent-scaffold-4-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-2/images/ai-agent-scaffold-4-2-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-2/images/ai-agent-scaffold-4-2-01.png new file mode 100644 index 000000000..6bc4c60ae Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-2/images/ai-agent-scaffold-4-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-2/images/ai-agent-scaffold-4-2-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-2/images/ai-agent-scaffold-4-2-02.png new file mode 100644 index 000000000..ea7376fbe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-2/images/ai-agent-scaffold-4-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-2/images/ai-agent-scaffold-4-2-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-2/images/ai-agent-scaffold-4-2-03.png new file mode 100644 index 000000000..fa346e2fa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-2/images/ai-agent-scaffold-4-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-01.png new file mode 100644 index 000000000..8cb79830f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-02.png new file mode 100644 index 000000000..9afe9f01e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-03.png new file mode 100644 index 000000000..cb64abcc8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-04.png new file mode 100644 index 000000000..f454f8e93 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-05.png new file mode 100644 index 000000000..506041398 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-06.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-06.png new file mode 100644 index 000000000..6f2cf7b97 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-07.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-07.png new file mode 100644 index 000000000..e22d9dba4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-3/images/ai-agent-scaffold-4-3-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-01.png new file mode 100644 index 000000000..239d5a421 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-02.png new file mode 100644 index 000000000..a0861d1ea Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-03.png new file mode 100644 index 000000000..8aee4a55d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-04.png new file mode 100644 index 000000000..66747ddb2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-05.png new file mode 100644 index 000000000..b47852838 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-06.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-06.png new file mode 100644 index 000000000..c867ba605 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-4/images/ai-agent-scaffold-4-4-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-01.png new file mode 100644 index 000000000..de5a2914c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-02.png new file mode 100644 index 000000000..b4d52c3b6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-03.png new file mode 100644 index 000000000..16d58a44d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-04.png new file mode 100644 index 000000000..ba435b7c7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-05.png new file mode 100644 index 000000000..876dc2032 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-06.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-06.png new file mode 100644 index 000000000..62be0eb07 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-07.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-07.png new file mode 100644 index 000000000..d8213f196 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-4/4-5/images/ai-agent-scaffold-4-5-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-0/images/ai-agent-scaffold-5-0-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-0/images/ai-agent-scaffold-5-0-01.png new file mode 100644 index 000000000..bb0cdae00 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-0/images/ai-agent-scaffold-5-0-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-0/images/logo.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-0/images/logo.png new file mode 100644 index 000000000..429b2d42d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-0/images/logo.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-00.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-00.png new file mode 100644 index 000000000..7c466f457 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-00.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-01.png new file mode 100644 index 000000000..2f3fe1357 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-02.png new file mode 100644 index 000000000..99b172578 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-03.png new file mode 100644 index 000000000..4816954bb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-04.png new file mode 100644 index 000000000..e47009b00 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-05.png new file mode 100644 index 000000000..b93a74ddb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-1/images/ai-agent-scaffold-5-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-2/images/ai-agent-scaffold-5-2-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-2/images/ai-agent-scaffold-5-2-01.png new file mode 100644 index 000000000..258781d2a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-2/images/ai-agent-scaffold-5-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-2/images/ai-agent-scaffold-5-2-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-2/images/ai-agent-scaffold-5-2-02.png new file mode 100644 index 000000000..81b7ecaf9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-2/images/ai-agent-scaffold-5-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-2/images/ai-agent-scaffold-5-2-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-2/images/ai-agent-scaffold-5-2-03.png new file mode 100644 index 000000000..6b7d58eed Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-2/images/ai-agent-scaffold-5-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-3/images/ai-agent-scaffold-5-3-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-3/images/ai-agent-scaffold-5-3-01.png new file mode 100644 index 000000000..a774fd287 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-3/images/ai-agent-scaffold-5-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-3/images/ai-agent-scaffold-5-3-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-3/images/ai-agent-scaffold-5-3-02.png new file mode 100644 index 000000000..f993106fe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-3/images/ai-agent-scaffold-5-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-3/images/ai-agent-scaffold-5-3-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-3/images/ai-agent-scaffold-5-3-03.png new file mode 100644 index 000000000..847b630de Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-3/images/ai-agent-scaffold-5-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-01.png new file mode 100644 index 000000000..115f81115 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-02.png new file mode 100644 index 000000000..d7a210be9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-03.png new file mode 100644 index 000000000..3128727f8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-04.png new file mode 100644 index 000000000..3c43ab5db Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-4/images/ai-agent-scaffold-5-4-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-01.png new file mode 100644 index 000000000..970e7486f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-02.png new file mode 100644 index 000000000..373fe0d50 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-03.png new file mode 100644 index 000000000..236ddcb84 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-04.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-04.png new file mode 100644 index 000000000..8ef5d932b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-05.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-05.png new file mode 100644 index 000000000..a4f700950 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-5/images/ai-agent-scaffold-5-5-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-6/images/ai-agent-scaffold-5-6-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-6/images/ai-agent-scaffold-5-6-01.png new file mode 100644 index 000000000..ee3be6537 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-6/images/ai-agent-scaffold-5-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-6/images/ai-agent-scaffold-5-6-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-6/images/ai-agent-scaffold-5-6-02.png new file mode 100644 index 000000000..8b9d7043a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-6/images/ai-agent-scaffold-5-6-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-6/images/ai-agent-scaffold-5-6-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-6/images/ai-agent-scaffold-5-6-03.png new file mode 100644 index 000000000..86d74d9ae Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-6/images/ai-agent-scaffold-5-6-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-7/images/ai-agent-scaffold-5-7-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-7/images/ai-agent-scaffold-5-7-01.png new file mode 100644 index 000000000..4b58b4421 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-7/images/ai-agent-scaffold-5-7-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-7/images/ai-agent-scaffold-5-7-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-7/images/ai-agent-scaffold-5-7-02.png new file mode 100644 index 000000000..f41fdc283 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-7/images/ai-agent-scaffold-5-7-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-7/images/ai-agent-scaffold-5-7-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-7/images/ai-agent-scaffold-5-7-03.png new file mode 100644 index 000000000..21cb57c64 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-7/images/ai-agent-scaffold-5-7-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-8/images/ai-agent-scaffold-5-8-01.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-8/images/ai-agent-scaffold-5-8-01.png new file mode 100644 index 000000000..4e21a6106 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-8/images/ai-agent-scaffold-5-8-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-8/images/ai-agent-scaffold-5-8-02.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-8/images/ai-agent-scaffold-5-8-02.png new file mode 100644 index 000000000..d1bf41d39 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-8/images/ai-agent-scaffold-5-8-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-8/images/ai-agent-scaffold-5-8-03.png b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-8/images/ai-agent-scaffold-5-8-03.png new file mode 100644 index 000000000..9fb2ec11c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-agent-scaffold/part-5/5-8/images/ai-agent-scaffold-5-8-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-01.png new file mode 100644 index 000000000..796e21231 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-02.png new file mode 100644 index 000000000..01fd90f9b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-03.png new file mode 100644 index 000000000..31fe9b342 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-04.png new file mode 100644 index 000000000..14772da72 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-2-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-2-01.png new file mode 100644 index 000000000..6b7692b97 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-2-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-2-02.png new file mode 100644 index 000000000..ea9307b98 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-2-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-2-03.png new file mode 100644 index 000000000..649de5c7e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-01.png new file mode 100644 index 000000000..b624b92d4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-02.png new file mode 100644 index 000000000..c8866688d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-03.png new file mode 100644 index 000000000..febb05e7f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-04-1.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-04-1.png new file mode 100644 index 000000000..93ada7105 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-04-1.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-04.png new file mode 100644 index 000000000..8d4a3bec7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-05-1.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-05-1.png new file mode 100644 index 000000000..28fbf0708 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-05-1.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-05.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-05.png new file mode 100644 index 000000000..a1f36adae Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-06-1.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-06-1.png new file mode 100644 index 000000000..a775c1457 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-06-1.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-06.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-06.png new file mode 100644 index 000000000..05cb27795 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-07-1.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-07-1.png new file mode 100644 index 000000000..efe2a904b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-07-1.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-07.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-07.png new file mode 100644 index 000000000..8f9b8939a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-3-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-4-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-4-01.png new file mode 100644 index 000000000..c91db171a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-4-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-4-02.png new file mode 100644 index 000000000..892cf7fce Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-1-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-1-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-1-01.png new file mode 100644 index 000000000..a0ccf09cc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-1-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-1-02.png new file mode 100644 index 000000000..5d33ca481 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-1-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-1-03.png new file mode 100644 index 000000000..15232fee9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-01.png new file mode 100644 index 000000000..adb2077b0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-02.png new file mode 100644 index 000000000..e7e452366 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-03.png new file mode 100644 index 000000000..ff2caebd3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-04.png new file mode 100644 index 000000000..7d25c2ab5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-01.png new file mode 100644 index 000000000..0bfbd597e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-02.png new file mode 100644 index 000000000..017ee0286 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-03.png new file mode 100644 index 000000000..4227b708b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-04.png new file mode 100644 index 000000000..4befba4ec Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-05.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-05.png new file mode 100644 index 000000000..244f0b964 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-06.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-06.png new file mode 100644 index 000000000..87ae5b38c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-07.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-07.png new file mode 100644 index 000000000..ba4583147 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-08.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-08.png new file mode 100644 index 000000000..d67bf8b03 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-2-3-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-01.png new file mode 100644 index 000000000..b660adc6b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-02.png new file mode 100644 index 000000000..a5b52a8c8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-03.png new file mode 100644 index 000000000..485753b6c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-04.png new file mode 100644 index 000000000..5420bc958 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-01.png new file mode 100644 index 000000000..5838b6a61 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-02.png new file mode 100644 index 000000000..f363681bb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-03.png new file mode 100644 index 000000000..a74d20e9d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-04.png new file mode 100644 index 000000000..5c80dea8c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-05.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-05.png new file mode 100644 index 000000000..b41b2ac2b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-06.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-06.png new file mode 100644 index 000000000..6be709da3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-10-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-11-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-11-01.png new file mode 100644 index 000000000..2c5ccdfce Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-11-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-11-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-11-02.png new file mode 100644 index 000000000..0b6bb8e5d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-11-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-11-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-11-03.png new file mode 100644 index 000000000..83ea835b9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-11-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-01.png new file mode 100644 index 000000000..a9fd3181f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-02.png new file mode 100644 index 000000000..6073b850a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-03.png new file mode 100644 index 000000000..3e7ea1643 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-04.png new file mode 100644 index 000000000..1afe11c9e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-12-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-13-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-13-01.png new file mode 100644 index 000000000..a9a36f2b5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-13-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-13-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-13-02.png new file mode 100644 index 000000000..62b72d811 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-13-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-13-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-13-03.png new file mode 100644 index 000000000..11dfad080 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-13-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-01.png new file mode 100644 index 000000000..ed78d81f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-02.png new file mode 100644 index 000000000..f51004205 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-03.png new file mode 100644 index 000000000..57382fb0a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-04.png new file mode 100644 index 000000000..a432f0003 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-14-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-16-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-16-01.png new file mode 100644 index 000000000..f4b3186e7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-16-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-16-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-16-02.png new file mode 100644 index 000000000..38e7c5386 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-16-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-01.png new file mode 100644 index 000000000..654e8a3b0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-02.png new file mode 100644 index 000000000..1eb8ef49f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-03.png new file mode 100644 index 000000000..98d8c0027 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-04.png new file mode 100644 index 000000000..40fc70fd5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-18-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-01.png new file mode 100644 index 000000000..00ea6f071 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-02.png new file mode 100644 index 000000000..543e1054e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-03.png new file mode 100644 index 000000000..ec58c0554 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-04.png new file mode 100644 index 000000000..5a45a2ef7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-05.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-05.png new file mode 100644 index 000000000..7d1bd2e76 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-06.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-06.png new file mode 100644 index 000000000..2d8211a14 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-07.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-07.png new file mode 100644 index 000000000..00e3df281 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-08.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-08.png new file mode 100644 index 000000000..1a73601c8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-09.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-09.png new file mode 100644 index 000000000..f525f10c5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-19-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-2-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-2-01.png new file mode 100644 index 000000000..02e0b8953 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-2-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-2-02.png new file mode 100644 index 000000000..6894b6b05 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-20-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-20-01.png new file mode 100644 index 000000000..f34fefa66 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-20-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-20-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-20-02.png new file mode 100644 index 000000000..0e8ef5e9b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-20-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-01.png new file mode 100644 index 000000000..d9bc284c9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-02.png new file mode 100644 index 000000000..e4cc3b16e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-03.png new file mode 100644 index 000000000..f1fd3a7c1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-04.png new file mode 100644 index 000000000..4bccc6c05 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-01.png new file mode 100644 index 000000000..6f9bacd50 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-02.png new file mode 100644 index 000000000..ff076a8ea Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-03.png new file mode 100644 index 000000000..d93bb4b6a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-04.png new file mode 100644 index 000000000..95fa15002 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-4-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-5-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-5-01.png new file mode 100644 index 000000000..343f93f09 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-5-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-5-02.png new file mode 100644 index 000000000..536070517 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-5-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-5-03.png new file mode 100644 index 000000000..2bae7e9bb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-5-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-01.png new file mode 100644 index 000000000..9a2de0dac Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-02.png new file mode 100644 index 000000000..eb0ac05b1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-03.png new file mode 100644 index 000000000..281bf9711 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-04.png new file mode 100644 index 000000000..3dc6274a1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-6-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-01.png new file mode 100644 index 000000000..a44c74922 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-02.png new file mode 100644 index 000000000..6329a601d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-03.png new file mode 100644 index 000000000..08229294e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-04.png new file mode 100644 index 000000000..ffdf1fb74 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-05.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-05.png new file mode 100644 index 000000000..745e76504 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-06.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-06.png new file mode 100644 index 000000000..88b969686 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-07.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-07.png new file mode 100644 index 000000000..325b4931e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-08.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-08.png new file mode 100644 index 000000000..27cbff57e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-7-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-01.png new file mode 100644 index 000000000..e32f2e689 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-02.png new file mode 100644 index 000000000..0e3a018fa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-03.png new file mode 100644 index 000000000..4ce744dfe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-04.png new file mode 100644 index 000000000..ff31c024c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-05.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-05.png new file mode 100644 index 000000000..3f68602d0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-06.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-06.png new file mode 100644 index 000000000..23164998e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-07.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-07.png new file mode 100644 index 000000000..79a38781b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-8-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-01.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-01.png new file mode 100644 index 000000000..584c4aaa1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-02.png new file mode 100644 index 000000000..ed9bc1555 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-03.png new file mode 100644 index 000000000..eb3c1b1b5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-04.png new file mode 100644 index 000000000..7ec7a89dd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-05.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-05.png new file mode 100644 index 000000000..462205f7e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-06.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-06.png new file mode 100644 index 000000000..79964e866 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-3-9-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-00.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-00.png new file mode 100644 index 000000000..2408c3844 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-00.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-01.gif b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-01.gif new file mode 100644 index 000000000..2a6a043e2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-02.png new file mode 100644 index 000000000..8553684d3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-03.png new file mode 100644 index 000000000..7d1bd2e76 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-04.png new file mode 100644 index 000000000..2d8211a14 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-05.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-05.png new file mode 100644 index 000000000..00e3df281 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-06.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-06.png new file mode 100644 index 000000000..1a73601c8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-07.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-07.png new file mode 100644 index 000000000..9be3f741f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/ai-mcp-gateway-promotion-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-01.gif b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-01.gif new file mode 100644 index 000000000..aae4626af Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-02.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-02.png new file mode 100644 index 000000000..e7ef0a3d6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-03.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-03.png new file mode 100644 index 000000000..c8b1c0407 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-04.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-04.png new file mode 100644 index 000000000..8f19975d4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-05.png b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-05.png new file mode 100644 index 000000000..11faa2518 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-mcp-gateway/mcp-gateway-promotion-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-01.png new file mode 100644 index 000000000..098ba0443 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-02.png new file mode 100644 index 000000000..0166dd73d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-03.png new file mode 100644 index 000000000..462074582 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-04.png new file mode 100644 index 000000000..6e14c37c7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-05.png new file mode 100644 index 000000000..eaf34b393 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-06.png new file mode 100644 index 000000000..c0b78cead Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-07.png new file mode 100644 index 000000000..9ca37f333 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-1-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-01.png new file mode 100644 index 000000000..f1787ebad Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-02.png new file mode 100644 index 000000000..66ed25ba4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-03.png new file mode 100644 index 000000000..c5af04bf3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-04.png new file mode 100644 index 000000000..0cea30183 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-05.png new file mode 100644 index 000000000..8b8f05e7e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250517-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-01.png new file mode 100644 index 000000000..041ea5e40 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-02.png new file mode 100644 index 000000000..04471e771 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-03.png new file mode 100644 index 000000000..7e64df5de Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-04.png new file mode 100644 index 000000000..c03aef592 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-250524-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-01.png new file mode 100644 index 000000000..7ba5726b9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-02.png new file mode 100644 index 000000000..f6ad01606 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-03.png new file mode 100644 index 000000000..5b1bff3ac Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-04.png new file mode 100644 index 000000000..ca8ef3c05 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-05.png new file mode 100644 index 000000000..86be9a07d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-06.png new file mode 100644 index 000000000..99e7b5eda Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-07.png new file mode 100644 index 000000000..040b9e8e0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-08.png new file mode 100644 index 000000000..7cf49e15d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250810-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250817-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250817-01.png new file mode 100644 index 000000000..59eb10ce8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250817-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250817-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250817-02.png new file mode 100644 index 000000000..c80b4e7c7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250817-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250921-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250921-01.png new file mode 100644 index 000000000..2d73e2a90 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250921-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250921-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250921-02.png new file mode 100644 index 000000000..5d6f5a906 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250921-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250921-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250921-03.png new file mode 100644 index 000000000..99628d4f2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-agent-station-ext-250921-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-01.png new file mode 100644 index 000000000..37d03c66d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-02.png new file mode 100644 index 000000000..a9bcb4f52 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-03.png new file mode 100644 index 000000000..15ad51b46 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-04.png new file mode 100644 index 000000000..b2d4bf784 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-05.png new file mode 100644 index 000000000..500d98e1d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-06.png new file mode 100644 index 000000000..566e1656e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-07.png new file mode 100644 index 000000000..cdd40e46b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-knowledge-250413-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-01.png new file mode 100644 index 000000000..30ef61eed Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-02.gif b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-02.gif new file mode 100644 index 000000000..b5882a5f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-02.gif differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-03.png new file mode 100644 index 000000000..920fb55f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-04.png new file mode 100644 index 000000000..30a236c97 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-05.png new file mode 100644 index 000000000..bf6220631 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-06.png new file mode 100644 index 000000000..d614b2408 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-07.png new file mode 100644 index 000000000..11f1b6bf3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-08.png new file mode 100644 index 000000000..6d05d229e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-mcp-knowledge-250330-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-0-00.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-0-00.png new file mode 100644 index 000000000..40b8d60b4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-0-00.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-0-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-0-01.png new file mode 100644 index 000000000..e0898537b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-0-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-01.png new file mode 100644 index 000000000..c6a930721 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-02.png new file mode 100644 index 000000000..00b7723d7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-03.png new file mode 100644 index 000000000..cccc99bf6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-04.png new file mode 100644 index 000000000..50ffcda56 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-05.png new file mode 100644 index 000000000..b911fc0bf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-01.png new file mode 100644 index 000000000..63a359a3d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-02.png new file mode 100644 index 000000000..0b1f728dd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-03.png new file mode 100644 index 000000000..0c46d4584 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-04.png new file mode 100644 index 000000000..b4b0a48f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-05.png new file mode 100644 index 000000000..d8e1082ca Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-06.png new file mode 100644 index 000000000..e858f4225 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-07.png new file mode 100644 index 000000000..b02198e82 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-08.png new file mode 100644 index 000000000..9bae5f1eb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-09.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-09.png new file mode 100644 index 000000000..28f71f95b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-10-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-00-1.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-00-1.png new file mode 100644 index 000000000..8cc6b25f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-00-1.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-01.png new file mode 100644 index 000000000..beb65f8d2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-02.png new file mode 100644 index 000000000..3c369b246 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-03.png new file mode 100644 index 000000000..314c53cd6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-04.png new file mode 100644 index 000000000..a7461e5ff Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-11-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-01.png new file mode 100644 index 000000000..7fb5d4c0c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-02.png new file mode 100644 index 000000000..3747a5e95 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-03.png new file mode 100644 index 000000000..04eae84ba Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-04.png new file mode 100644 index 000000000..d86c218f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-05.png new file mode 100644 index 000000000..ecc5924a0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-06.png new file mode 100644 index 000000000..a8830fd11 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-07.png new file mode 100644 index 000000000..e8206d259 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-08.png new file mode 100644 index 000000000..43703a9d8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-09.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-09.png new file mode 100644 index 000000000..63ed1d877 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-10.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-10.png new file mode 100644 index 000000000..ceeb40ce1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-01.png new file mode 100644 index 000000000..8ef61d0b8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-02.png new file mode 100644 index 000000000..1e74edda0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-03.png new file mode 100644 index 000000000..b8287168e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-04.png new file mode 100644 index 000000000..ff858d112 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-05.png new file mode 100644 index 000000000..00bab426f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-06.png new file mode 100644 index 000000000..e30e7f73f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-12-12-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-01.png new file mode 100644 index 000000000..289161271 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-02.png new file mode 100644 index 000000000..93b61ea1e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-03.png new file mode 100644 index 000000000..1fb1f39e6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-04.png new file mode 100644 index 000000000..4a6ad8e71 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-05.png new file mode 100644 index 000000000..9a1c6101b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-06.png new file mode 100644 index 000000000..9e768ea57 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-07.png new file mode 100644 index 000000000..302c26ea9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-08.png new file mode 100644 index 000000000..da53d5a5e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-09.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-09.png new file mode 100644 index 000000000..1c47aeb22 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-10.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-10.png new file mode 100644 index 000000000..cd8efba15 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-11.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-11.png new file mode 100644 index 000000000..17ffdbd97 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-11.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-12.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-12.png new file mode 100644 index 000000000..639b78f64 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-12.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-13.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-13.png new file mode 100644 index 000000000..15ba76351 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-13-13.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-01.png new file mode 100644 index 000000000..94890772d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-02.png new file mode 100644 index 000000000..8d3511f57 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-03.png new file mode 100644 index 000000000..1a2af109d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-04.png new file mode 100644 index 000000000..399206904 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-05.png new file mode 100644 index 000000000..5048b623c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-06.png new file mode 100644 index 000000000..007cb28c4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-07.png new file mode 100644 index 000000000..a7d6bee54 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-08.png new file mode 100644 index 000000000..5cf001876 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-09.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-09.png new file mode 100644 index 000000000..6c0f13816 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-10.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-10.png new file mode 100644 index 000000000..b42b6d0c4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-11.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-11.png new file mode 100644 index 000000000..8adb2fd99 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-11.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-12.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-12.png new file mode 100644 index 000000000..d7a9bd1aa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-12.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-13.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-13.png new file mode 100644 index 000000000..4afc08e97 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-13.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-14.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-14.png new file mode 100644 index 000000000..b7b59dc44 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-14-14.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-15-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-15-01.png new file mode 100644 index 000000000..56de64a8a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-15-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-15-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-15-02.png new file mode 100644 index 000000000..de380b2f2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-15-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-15-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-15-03.png new file mode 100644 index 000000000..e7538a845 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-15-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-01.png new file mode 100644 index 000000000..e8509babf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-02.png new file mode 100644 index 000000000..4cd82e807 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-03.png new file mode 100644 index 000000000..24c8d6ab0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-04.png new file mode 100644 index 000000000..423a0139e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-05.png new file mode 100644 index 000000000..c8c3f5436 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-06.png new file mode 100644 index 000000000..3d6941b8b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-07.png new file mode 100644 index 000000000..00650cf88 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-08.png new file mode 100644 index 000000000..bd0da8e3b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-09.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-09.png new file mode 100644 index 000000000..f07968c57 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-10.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-10.png new file mode 100644 index 000000000..01e6f077b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-11.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-11.png new file mode 100644 index 000000000..271f30609 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-11.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-12.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-12.png new file mode 100644 index 000000000..8069b3e3c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-16-12.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-01.png new file mode 100644 index 000000000..866ccbb41 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-02.png new file mode 100644 index 000000000..45c2c2c85 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-03.png new file mode 100644 index 000000000..9429bd188 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-04.png new file mode 100644 index 000000000..941040b10 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-05.png new file mode 100644 index 000000000..c8a83fa53 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-01.png new file mode 100644 index 000000000..3b79db58a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-02.png new file mode 100644 index 000000000..05653bd79 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-03.png new file mode 100644 index 000000000..978f6a09c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-04.png new file mode 100644 index 000000000..dce9b6747 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-05.png new file mode 100644 index 000000000..afa6a6911 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-06.png new file mode 100644 index 000000000..f810a3118 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-07.png new file mode 100644 index 000000000..3d4feddab Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-08.png new file mode 100644 index 000000000..9bcef3f45 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-0-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-01.png new file mode 100644 index 000000000..7283e3aa6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-02.png new file mode 100644 index 000000000..7727ba881 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-1-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-1-01.png new file mode 100644 index 000000000..b5bbdce1d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-1-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-1-02.png new file mode 100644 index 000000000..0fde02b9b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-10-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-10-01.png new file mode 100644 index 000000000..9baad7c5a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-10-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-10-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-10-02.png new file mode 100644 index 000000000..9cb07a1d7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-10-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-01.png new file mode 100644 index 000000000..786e82759 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-02.png new file mode 100644 index 000000000..f080a05a6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-03.png new file mode 100644 index 000000000..fb234dc7f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-04.png new file mode 100644 index 000000000..c5f7f06f4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-11-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-01.png new file mode 100644 index 000000000..9b1aeded8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-02.png new file mode 100644 index 000000000..040b9e8e0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-03.png new file mode 100644 index 000000000..5571d3def Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-04.png new file mode 100644 index 000000000..b71d39e62 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-05.png new file mode 100644 index 000000000..a67260959 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-06.png new file mode 100644 index 000000000..7ab41408c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-12-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-01.png new file mode 100644 index 000000000..97f5f090c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-02.png new file mode 100644 index 000000000..f668aa163 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-03.png new file mode 100644 index 000000000..76aa86c13 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-04.png new file mode 100644 index 000000000..794256633 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-05.png new file mode 100644 index 000000000..1cbff6409 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-06.png new file mode 100644 index 000000000..15a2fce6b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-07.png new file mode 100644 index 000000000..94c1b271f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-08.png new file mode 100644 index 000000000..1c11a1e7c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-13-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-15-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-15-01.png new file mode 100644 index 000000000..2e066f914 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-15-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-15-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-15-02.png new file mode 100644 index 000000000..5e165a92f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-15-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-01.png new file mode 100644 index 000000000..d66ac8f17 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-02.png new file mode 100644 index 000000000..221eae276 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-03.png new file mode 100644 index 000000000..750b011ad Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-04.png new file mode 100644 index 000000000..e00bc1510 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-05.png new file mode 100644 index 000000000..e1b582a41 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-16-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-01.png new file mode 100644 index 000000000..851f16dd2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-02.png new file mode 100644 index 000000000..126da06a5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-03.png new file mode 100644 index 000000000..87964973b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-04.png new file mode 100644 index 000000000..71bf9272c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-17-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-01.png new file mode 100644 index 000000000..89074e025 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-02.png new file mode 100644 index 000000000..94cc7af6e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-03.png new file mode 100644 index 000000000..8e4b21647 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-04.png new file mode 100644 index 000000000..f54895357 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-18-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-01.png new file mode 100644 index 000000000..d253947b4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-02.png new file mode 100644 index 000000000..79bcfe630 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-03.png new file mode 100644 index 000000000..3f3f51a31 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-04.png new file mode 100644 index 000000000..3f45dfb5d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-05.png new file mode 100644 index 000000000..f35d7e35b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-06.png new file mode 100644 index 000000000..784d0b8b3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-07.png new file mode 100644 index 000000000..1a919d925 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-19-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-01.png new file mode 100644 index 000000000..fbfb8c474 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-02.png new file mode 100644 index 000000000..6c678a496 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-03.png new file mode 100644 index 000000000..7dc85bd09 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-04.png new file mode 100644 index 000000000..07efe9729 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-05.png new file mode 100644 index 000000000..be68de99f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-06.png new file mode 100644 index 000000000..5d28e632e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-07.png new file mode 100644 index 000000000..b840dc101 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-08.png new file mode 100644 index 000000000..eebb64fca Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-09.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-09.png new file mode 100644 index 000000000..a5fa4c564 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-10.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-10.png new file mode 100644 index 000000000..f61261e07 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-11.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-11.png new file mode 100644 index 000000000..dbe8c1660 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-11.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-12.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-12.png new file mode 100644 index 000000000..46a3431ae Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-2-12.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-01.png new file mode 100644 index 000000000..5c9f7de54 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-02.png new file mode 100644 index 000000000..de69255d8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-03.png new file mode 100644 index 000000000..f19061b14 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-04.png new file mode 100644 index 000000000..d081d93e9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-05.png new file mode 100644 index 000000000..4c35ba6c9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-06.png new file mode 100644 index 000000000..d021448ef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-07.png new file mode 100644 index 000000000..46b1e748f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-20-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-01.png new file mode 100644 index 000000000..c8f2df63d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-02.png new file mode 100644 index 000000000..1731ae34a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-03.png new file mode 100644 index 000000000..1ec050eb6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-04.png new file mode 100644 index 000000000..64a0e2e6c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-05.png new file mode 100644 index 000000000..9792485b6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-06.png new file mode 100644 index 000000000..1f08610df Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-07.png new file mode 100644 index 000000000..4c7c79bbb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-08.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-08.png new file mode 100644 index 000000000..3445d70d2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-21-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-3-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-3-01.png new file mode 100644 index 000000000..6c72e2f86 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-3-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-3-02.png new file mode 100644 index 000000000..129843d29 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-3-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-3-03.png new file mode 100644 index 000000000..c67c42a37 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-4-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-4-01.png new file mode 100644 index 000000000..b8191bb9e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-4-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-4-02.png new file mode 100644 index 000000000..9d18d20fb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-01.png new file mode 100644 index 000000000..3d807a4eb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-02.png new file mode 100644 index 000000000..757e76d28 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-03.png new file mode 100644 index 000000000..cbdb71778 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-04.png new file mode 100644 index 000000000..d15e43a49 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-5-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-6-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-6-01.png new file mode 100644 index 000000000..68d017da8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-6-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-6-02.png new file mode 100644 index 000000000..9b23b3a02 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-6-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-6-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-6-03.png new file mode 100644 index 000000000..b98217de4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-6-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-7-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-7-01.png new file mode 100644 index 000000000..3f00ca8f6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-7-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-7-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-7-02.png new file mode 100644 index 000000000..95db900f8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-7-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-8-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-8-01.png new file mode 100644 index 000000000..fcc03b4ac Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-8-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-8-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-8-02.png new file mode 100644 index 000000000..67ab78fdd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-8-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-8-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-8-03.png new file mode 100644 index 000000000..ef6163179 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-8-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-9-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-9-01.png new file mode 100644 index 000000000..a50d21c86 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-9-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-9-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-9-02.png new file mode 100644 index 000000000..6e1e117b2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-9-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-9-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-9-03.png new file mode 100644 index 000000000..32cd9c458 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-3-9-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-01.png new file mode 100644 index 000000000..bd0146c60 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-02.png new file mode 100644 index 000000000..1530b2440 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-03.png new file mode 100644 index 000000000..d8fb6c1d8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-04.png new file mode 100644 index 000000000..604c077b5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-05.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-05.png new file mode 100644 index 000000000..36a42813d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-06.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-06.png new file mode 100644 index 000000000..7b146035b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-07.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-07.png new file mode 100644 index 000000000..3ae3ffa14 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-4-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-5-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-5-01.png new file mode 100644 index 000000000..127cdd237 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-5-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-5-02.png new file mode 100644 index 000000000..488843d7c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-01.png new file mode 100644 index 000000000..467ce20f0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-02.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-02.png new file mode 100644 index 000000000..86d1a2178 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-03.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-03.png new file mode 100644 index 000000000..23bec8fcc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-04.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-04.png new file mode 100644 index 000000000..88c466876 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-6-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-9-01.png b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-9-01.png new file mode 100644 index 000000000..496eef7f4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ai-rag-knowledge/ai-rag-knowledge-9-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-01-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-01-01.png new file mode 100644 index 000000000..c91ff93ee Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-01-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-01-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-01-02.png new file mode 100644 index 000000000..9a7d5d734 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-01-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-01-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-01-03.png new file mode 100644 index 000000000..80e098349 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-01-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-01-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-01-04.png new file mode 100644 index 000000000..2b2d0dea4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-01-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-01-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-01-05.png new file mode 100644 index 000000000..288c2a01b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-01-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-01.png new file mode 100644 index 000000000..9ade35529 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-02.png new file mode 100644 index 000000000..751c13550 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-03.png new file mode 100644 index 000000000..e93dc1618 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-04.png new file mode 100644 index 000000000..aee68db6e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-05.png new file mode 100644 index 000000000..685b763a5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-06.png new file mode 100644 index 000000000..2331b3944 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-07.png new file mode 100644 index 000000000..98d84d2ab Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-08.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-08.png new file mode 100644 index 000000000..7bf7b6e15 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-09.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-09.png new file mode 100644 index 000000000..48c78111a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-09.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-10.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-10.png new file mode 100644 index 000000000..4d8df165a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-10.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-11.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-11.png new file mode 100644 index 000000000..d315a61fe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-11.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-12.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-12.png new file mode 100644 index 000000000..7740aff22 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-12.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-13.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-13.png new file mode 100644 index 000000000..ea51c9610 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-13.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-02-14.png b/docs/.vuepress/public/images/article/project/big-market/big-market-02-14.png new file mode 100644 index 000000000..26704b155 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-02-14.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-01.png new file mode 100644 index 000000000..55bcee15a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-02.png new file mode 100644 index 000000000..93612831b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-03.png new file mode 100644 index 000000000..9feba1635 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-04.png new file mode 100644 index 000000000..a41fcb234 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-05.png new file mode 100644 index 000000000..c946143a2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-06.png new file mode 100644 index 000000000..d5909d0b9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-07.png new file mode 100644 index 000000000..93b4f228b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-08.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-08.png new file mode 100644 index 000000000..a36174b38 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-09.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-09.png new file mode 100644 index 000000000..36487b9cd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-09.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-10.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-10.png new file mode 100644 index 000000000..f45d25493 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-10.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-03-11.png b/docs/.vuepress/public/images/article/project/big-market/big-market-03-11.png new file mode 100644 index 000000000..92b2dbf5f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-03-11.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-04-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-04-01.png new file mode 100644 index 000000000..b9aee82f0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-04-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-04-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-04-02.png new file mode 100644 index 000000000..fd435900e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-04-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-04-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-04-03.png new file mode 100644 index 000000000..60ff78c4a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-04-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-04-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-04-04.png new file mode 100644 index 000000000..dedd2b6e0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-04-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-04-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-04-05.png new file mode 100644 index 000000000..f7e7f460e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-04-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-04-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-04-06.png new file mode 100644 index 000000000..b30e73f84 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-04-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-04-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-04-07.png new file mode 100644 index 000000000..628459152 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-04-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-05-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-05-01.png new file mode 100644 index 000000000..36487b9cd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-05-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-05-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-05-02.png new file mode 100644 index 000000000..19182f2ab Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-05-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-05-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-05-03.png new file mode 100644 index 000000000..94dd86640 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-05-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-05-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-05-04.png new file mode 100644 index 000000000..52a29afae Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-05-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-05-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-05-05.png new file mode 100644 index 000000000..0258335c4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-05-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-06-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-06-01.png new file mode 100644 index 000000000..f7aa5e8f6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-06-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-06-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-06-02.png new file mode 100644 index 000000000..cc496b7f2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-06-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-06-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-06-03.png new file mode 100644 index 000000000..4612c9fad Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-06-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-07-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-07-01.png new file mode 100644 index 000000000..25d53c7dc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-07-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-07-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-07-02.png new file mode 100644 index 000000000..a2789318b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-07-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-08-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-08-01.png new file mode 100644 index 000000000..8ca0c2ca1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-08-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-08-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-08-02.png new file mode 100644 index 000000000..bc205b1f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-08-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-09-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-09-01.png new file mode 100644 index 000000000..8c428611b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-09-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-09-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-09-02.png new file mode 100644 index 000000000..06e2c2483 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-09-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-09-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-09-03.png new file mode 100644 index 000000000..769eeae86 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-09-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-09-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-09-04.png new file mode 100644 index 000000000..30f8207a0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-09-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-10-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-10-01.png new file mode 100644 index 000000000..7348f7cd8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-10-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-10-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-10-02.png new file mode 100644 index 000000000..c8d16f4c2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-10-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-10-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-10-03.png new file mode 100644 index 000000000..0c02cade9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-10-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-10-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-10-04.png new file mode 100644 index 000000000..ff30cfdbd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-10-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-10-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-10-05.png new file mode 100644 index 000000000..1e04faca2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-10-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-11-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-11-01.png new file mode 100644 index 000000000..c38097b51 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-11-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-11-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-11-02.png new file mode 100644 index 000000000..6d2773bda Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-11-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-11-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-11-03.png new file mode 100644 index 000000000..1bbe715d1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-11-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-12-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-12-01.png new file mode 100644 index 000000000..1c4dec17b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-12-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-12-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-12-02.png new file mode 100644 index 000000000..017bbc968 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-12-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-12-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-12-03.png new file mode 100644 index 000000000..276620893 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-12-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-12-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-12-04.png new file mode 100644 index 000000000..c5b2860c6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-12-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-12-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-12-05.png new file mode 100644 index 000000000..a4029d00d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-12-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-13-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-13-01.png new file mode 100644 index 000000000..d925c913a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-13-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-13-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-13-02.png new file mode 100644 index 000000000..506af94d1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-13-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-13-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-13-03.png new file mode 100644 index 000000000..ddc1dee3a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-13-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-13-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-13-04.png new file mode 100644 index 000000000..43af46095 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-13-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-13-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-13-05.png new file mode 100644 index 000000000..1fc8ff70c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-13-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-13-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-13-06.png new file mode 100644 index 000000000..8581a18fb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-13-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-13-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-13-07.png new file mode 100644 index 000000000..f658a91ad Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-13-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-13-08.png b/docs/.vuepress/public/images/article/project/big-market/big-market-13-08.png new file mode 100644 index 000000000..b3792202f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-13-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-13-09.png b/docs/.vuepress/public/images/article/project/big-market/big-market-13-09.png new file mode 100644 index 000000000..1080d61a2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-13-09.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-01.png new file mode 100644 index 000000000..d8fbd6dfe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-02.png new file mode 100644 index 000000000..0737f0ca9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-03.png new file mode 100644 index 000000000..dab340cfb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-04.png new file mode 100644 index 000000000..90119b81c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-05.png new file mode 100644 index 000000000..31045b717 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-06.png new file mode 100644 index 000000000..8b2870991 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-07.png new file mode 100644 index 000000000..2cb774b0f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-08.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-08.png new file mode 100644 index 000000000..2c05101b3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-09.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-09.png new file mode 100644 index 000000000..27962acbe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-09.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-10.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-10.png new file mode 100644 index 000000000..eb25377fa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-10.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-11.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-11.png new file mode 100644 index 000000000..a223f8cbf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-11.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-12.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-12.png new file mode 100644 index 000000000..0976905b9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-12.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-13.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-13.png new file mode 100644 index 000000000..28dff2a26 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-13.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-14-14.png b/docs/.vuepress/public/images/article/project/big-market/big-market-14-14.png new file mode 100644 index 000000000..d2fae4c37 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-14-14.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-15-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-15-01.png new file mode 100644 index 000000000..a8dd32337 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-15-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-15-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-15-02.png new file mode 100644 index 000000000..319fbec59 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-15-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-15-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-15-03.png new file mode 100644 index 000000000..4d84763e1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-15-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-15-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-15-04.png new file mode 100644 index 000000000..ba45f95e2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-15-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-15-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-15-05.png new file mode 100644 index 000000000..f19b0fc19 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-15-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-15-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-15-06.png new file mode 100644 index 000000000..3c1803ab3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-15-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-15-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-15-07.png new file mode 100644 index 000000000..b34b7501c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-15-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-15-08.png b/docs/.vuepress/public/images/article/project/big-market/big-market-15-08.png new file mode 100644 index 000000000..1230ff469 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-15-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-16-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-16-01.png new file mode 100644 index 000000000..ceca5e638 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-16-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-16-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-16-02.png new file mode 100644 index 000000000..f38363f84 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-16-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-16-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-16-03.png new file mode 100644 index 000000000..c7c2be919 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-16-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-01.png new file mode 100644 index 000000000..a4ce29c05 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-02.png new file mode 100644 index 000000000..44034ac70 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-03.png new file mode 100644 index 000000000..66162a076 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-04.png new file mode 100644 index 000000000..6e22cedca Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-05.png new file mode 100644 index 000000000..26d77b697 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-06.png new file mode 100644 index 000000000..b0994a6e9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-07.png new file mode 100644 index 000000000..9b7d082ef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-08.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-08.png new file mode 100644 index 000000000..a681bd72d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-09.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-09.png new file mode 100644 index 000000000..9175beca2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-09.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-10.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-10.png new file mode 100644 index 000000000..f70658f41 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-10.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-11.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-11.png new file mode 100644 index 000000000..d0a843a7f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-11.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-17-12.png b/docs/.vuepress/public/images/article/project/big-market/big-market-17-12.png new file mode 100644 index 000000000..369782399 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-17-12.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-18-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-18-01.png new file mode 100644 index 000000000..1738be571 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-18-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-18-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-18-02.png new file mode 100644 index 000000000..303494bc3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-18-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-18-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-18-03.png new file mode 100644 index 000000000..d5bf4fe27 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-18-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-18-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-18-04.png new file mode 100644 index 000000000..27cb79d5b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-18-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-19-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-19-01.png new file mode 100644 index 000000000..a5fee874e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-19-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-19-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-19-02.png new file mode 100644 index 000000000..38e87272e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-19-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-19-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-19-03.png new file mode 100644 index 000000000..ea4315b87 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-19-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-20-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-20-01.png new file mode 100644 index 000000000..7afbb0ebe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-20-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-20-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-20-02.png new file mode 100644 index 000000000..4bbffb57b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-20-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-21-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-21-01.png new file mode 100644 index 000000000..5186e2f08 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-21-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-21-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-21-02.png new file mode 100644 index 000000000..49ddbece8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-21-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-21-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-21-03.png new file mode 100644 index 000000000..7087d887a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-21-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-22-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-22-01.png new file mode 100644 index 000000000..d0e9ecbf2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-22-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-22-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-22-02.png new file mode 100644 index 000000000..20199d213 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-22-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-22-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-22-03.png new file mode 100644 index 000000000..d6a712137 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-22-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-22-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-22-04.png new file mode 100644 index 000000000..2e1c7e722 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-22-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-23-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-23-01.png new file mode 100644 index 000000000..83516b05a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-23-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-23-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-23-02.png new file mode 100644 index 000000000..c6f18a72b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-23-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-23-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-23-03.png new file mode 100644 index 000000000..970662ec7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-23-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-24-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-24-01.png new file mode 100644 index 000000000..dd8f9b942 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-24-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-24-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-24-02.png new file mode 100644 index 000000000..f49c8c95a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-24-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-24-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-24-03.png new file mode 100644 index 000000000..311cbbd5d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-24-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-24-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-24-04.png new file mode 100644 index 000000000..49a8bb28f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-24-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-25-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-25-01.png new file mode 100644 index 000000000..7086ac15a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-25-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-25-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-25-02.png new file mode 100644 index 000000000..9f97601a0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-25-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-25-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-25-03.png new file mode 100644 index 000000000..e15d2ff8e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-25-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-25-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-25-04.png new file mode 100644 index 000000000..9b4850b1b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-25-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-25-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-25-05.png new file mode 100644 index 000000000..141aa14ef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-25-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-26-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-26-01.png new file mode 100644 index 000000000..39bdc42a0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-26-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-26-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-26-02.png new file mode 100644 index 000000000..e04da1f32 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-26-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-26-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-26-03.png new file mode 100644 index 000000000..867e55f29 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-26-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-26-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-26-04.png new file mode 100644 index 000000000..8c1a07a91 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-26-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-26-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-26-05.png new file mode 100644 index 000000000..fa61cf26e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-26-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-27-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-27-01.png new file mode 100644 index 000000000..947d6038e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-27-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-27-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-27-02.png new file mode 100644 index 000000000..230676c3c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-27-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-27-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-27-03.png new file mode 100644 index 000000000..2e395db98 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-27-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-27-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-27-04.png new file mode 100644 index 000000000..80b95ab49 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-27-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-27-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-27-05.png new file mode 100644 index 000000000..d729cd33d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-27-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-28-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-28-01.png new file mode 100644 index 000000000..0426d985d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-28-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-28-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-28-02.png new file mode 100644 index 000000000..6a65052e1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-28-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-28-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-28-03.png new file mode 100644 index 000000000..309557e60 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-28-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-29-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-29-01.png new file mode 100644 index 000000000..5c8a09296 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-29-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-29-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-29-02.png new file mode 100644 index 000000000..6ec5d9a21 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-29-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-29-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-29-03.png new file mode 100644 index 000000000..219f0da49 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-29-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-29-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-29-04.png new file mode 100644 index 000000000..3a3e2b133 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-29-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-29-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-29-05.png new file mode 100644 index 000000000..1e414c72c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-29-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-30-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-30-01.png new file mode 100644 index 000000000..7cfeef47e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-30-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-30-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-30-02.png new file mode 100644 index 000000000..aa0dbe043 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-30-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-30-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-30-03.png new file mode 100644 index 000000000..b939f49b9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-30-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-31-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-31-01.png new file mode 100644 index 000000000..ef019146f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-31-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-31-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-31-02.png new file mode 100644 index 000000000..42ad00cb8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-31-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-31-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-31-03.png new file mode 100644 index 000000000..4c5241f98 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-31-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-31-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-31-04.png new file mode 100644 index 000000000..c4618a581 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-31-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-31-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-31-05.png new file mode 100644 index 000000000..6e1120233 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-31-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-32-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-32-01.png new file mode 100644 index 000000000..b97987a17 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-32-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-32-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-32-02.png new file mode 100644 index 000000000..da6f86624 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-32-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-32-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-32-03.png new file mode 100644 index 000000000..2d89d485d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-32-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-32-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-32-04.png new file mode 100644 index 000000000..7f79d4a09 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-32-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-32-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-32-05.png new file mode 100644 index 000000000..94c33d50d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-32-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-32-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-32-06.png new file mode 100644 index 000000000..53454eb7c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-32-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-33-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-33-01.png new file mode 100644 index 000000000..0c28865f4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-33-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-33-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-33-02.png new file mode 100644 index 000000000..ff85da070 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-33-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-33-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-33-03.png new file mode 100644 index 000000000..5ce977ab8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-33-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-33-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-33-04.png new file mode 100644 index 000000000..2a3f68a0f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-33-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-34-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-34-01.png new file mode 100644 index 000000000..4488e8b4d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-34-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-34-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-34-02.png new file mode 100644 index 000000000..626126692 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-34-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-34-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-34-03.png new file mode 100644 index 000000000..f9918824f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-34-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-34-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-34-04.png new file mode 100644 index 000000000..178ef2aeb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-34-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-35-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-35-01.png new file mode 100644 index 000000000..d6e7eb7a0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-35-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-35-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-35-02.png new file mode 100644 index 000000000..e3c65f3e7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-35-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-35-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-35-03.png new file mode 100644 index 000000000..14fdc549e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-35-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-36-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-36-01.png new file mode 100644 index 000000000..b59dc1023 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-36-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-36-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-36-02.png new file mode 100644 index 000000000..3a27b3448 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-36-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-36-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-36-03.png new file mode 100644 index 000000000..8ce2570c1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-36-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-36-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-36-04.png new file mode 100644 index 000000000..5ebd3315e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-36-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-36-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-36-05.png new file mode 100644 index 000000000..d4ddbafe1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-36-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-37-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-37-01.png new file mode 100644 index 000000000..c3919208f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-37-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-37-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-37-02.png new file mode 100644 index 000000000..8cb999293 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-37-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-38-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-38-01.png new file mode 100644 index 000000000..539b2bc42 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-38-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-38-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-38-02.png new file mode 100644 index 000000000..28b237fe3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-38-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-38-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-38-03.png new file mode 100644 index 000000000..1787c468e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-38-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-38-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-38-04.png new file mode 100644 index 000000000..eca91a52b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-38-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-38-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-38-05.png new file mode 100644 index 000000000..5d9723923 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-38-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-38-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-38-06.png new file mode 100644 index 000000000..3369e9e3e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-38-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-39-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-39-01.png new file mode 100644 index 000000000..fb62bf58e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-39-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-39-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-39-02.png new file mode 100644 index 000000000..3e13e84ed Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-39-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-39-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-39-03.png new file mode 100644 index 000000000..b4664f761 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-39-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-40-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-40-01.png new file mode 100644 index 000000000..2e78acf74 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-40-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-40-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-40-02.png new file mode 100644 index 000000000..5784c9fef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-40-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-40-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-40-03.png new file mode 100644 index 000000000..a79232b44 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-40-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-40-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-40-04.png new file mode 100644 index 000000000..cd084fcef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-40-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-41-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-41-01.png new file mode 100644 index 000000000..0fc88538c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-41-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-41-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-41-02.png new file mode 100644 index 000000000..85785ab3d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-41-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-41-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-41-03.png new file mode 100644 index 000000000..8e3235d96 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-41-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-41-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-41-04.png new file mode 100644 index 000000000..98450e9aa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-41-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-41-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-41-05.png new file mode 100644 index 000000000..2164df813 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-41-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-42-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-42-01.png new file mode 100644 index 000000000..31452af83 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-42-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-42-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-42-02.png new file mode 100644 index 000000000..46df0480b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-42-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-42-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-42-03.png new file mode 100644 index 000000000..282558f82 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-42-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-42-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-42-04.png new file mode 100644 index 000000000..80728adb8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-42-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-43-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-43-01.png new file mode 100644 index 000000000..ddb94ab55 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-43-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-43-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-43-02.png new file mode 100644 index 000000000..874153781 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-43-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-43-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-43-03.png new file mode 100644 index 000000000..f22263b11 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-43-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-44-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-44-01.png new file mode 100644 index 000000000..8a78392db Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-44-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-44-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-44-02.png new file mode 100644 index 000000000..8a230da47 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-44-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-44-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-44-03.png new file mode 100644 index 000000000..91f8faf3f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-44-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-44-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-44-04.png new file mode 100644 index 000000000..63b4b8454 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-44-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-44-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-44-05.png new file mode 100644 index 000000000..54cb31355 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-44-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-44-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-44-06.png new file mode 100644 index 000000000..04e476178 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-44-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-44-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-44-07.png new file mode 100644 index 000000000..8e4a68dc3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-44-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-44-08.png b/docs/.vuepress/public/images/article/project/big-market/big-market-44-08.png new file mode 100644 index 000000000..a7dc7d98f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-44-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-45-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-45-01.png new file mode 100644 index 000000000..590a2ecc9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-45-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-45-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-45-02.png new file mode 100644 index 000000000..1c60341ef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-45-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-46-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-46-01.png new file mode 100644 index 000000000..f50f3ee7e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-46-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-46-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-46-02.png new file mode 100644 index 000000000..5884f213e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-46-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-46-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-46-03.png new file mode 100644 index 000000000..e4049a5d7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-46-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-46-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-46-04.png new file mode 100644 index 000000000..2ddfea5a3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-46-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-46-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-46-05.png new file mode 100644 index 000000000..684f87747 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-46-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-46-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-46-06.png new file mode 100644 index 000000000..5d93f05c2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-46-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-47-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-47-01.png new file mode 100644 index 000000000..12bf636a2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-47-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-47-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-47-02.png new file mode 100644 index 000000000..7cc1be0a3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-47-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-47-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-47-03.png new file mode 100644 index 000000000..bf58aaad2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-47-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-47-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-47-04.png new file mode 100644 index 000000000..e33c4c547 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-47-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-47-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-47-05.png new file mode 100644 index 000000000..43fd4fd5d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-47-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-48-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-48-01.png new file mode 100644 index 000000000..4ad662e99 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-48-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-48-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-48-02.png new file mode 100644 index 000000000..64e273205 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-48-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-48-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-48-03.png new file mode 100644 index 000000000..a0c975d33 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-48-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-48-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-48-04.png new file mode 100644 index 000000000..572c914b3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-48-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-48-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-48-05.png new file mode 100644 index 000000000..ecea645c0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-48-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-48-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-48-06.png new file mode 100644 index 000000000..b1f5de43d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-48-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-49-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-49-01.png new file mode 100644 index 000000000..601ad166b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-49-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-49-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-49-02.png new file mode 100644 index 000000000..4bc067fad Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-49-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-49-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-49-03.png new file mode 100644 index 000000000..85fe68cfe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-49-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-49-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-49-04.png new file mode 100644 index 000000000..edc4586f4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-49-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-49-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-49-05.png new file mode 100644 index 000000000..86ebb96f9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-49-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-49-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-49-06.png new file mode 100644 index 000000000..7d9454720 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-49-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-50-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-50-01.png new file mode 100644 index 000000000..ca97a7b4c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-50-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-50-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-50-02.png new file mode 100644 index 000000000..634f4f4bf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-50-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-50-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-50-03.png new file mode 100644 index 000000000..72a546b95 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-50-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-50-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-50-04.png new file mode 100644 index 000000000..230c90831 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-50-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-51-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-51-01.png new file mode 100644 index 000000000..93108fbe2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-51-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-51-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-51-02.png new file mode 100644 index 000000000..2e4712e6a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-51-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-51-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-51-03.png new file mode 100644 index 000000000..49a4908c6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-51-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-51-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-51-04.png new file mode 100644 index 000000000..763b14796 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-51-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-52-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-52-01.png new file mode 100644 index 000000000..d8b683421 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-52-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-52-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-52-02.png new file mode 100644 index 000000000..25ff7d7df Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-52-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-52-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-52-03.png new file mode 100644 index 000000000..3c6967d0a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-52-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-53-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-53-01.png new file mode 100644 index 000000000..2813a9db1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-53-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-53-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-53-02.png new file mode 100644 index 000000000..50edfa7e0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-53-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-53-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-53-03.png new file mode 100644 index 000000000..990a7db27 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-53-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-53-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-53-04.png new file mode 100644 index 000000000..f196f8c2b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-53-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-53-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-53-05.png new file mode 100644 index 000000000..176b10803 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-53-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-53-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-53-06.png new file mode 100644 index 000000000..97a6512cb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-53-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-01.png new file mode 100644 index 000000000..adbac603d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-02.png new file mode 100644 index 000000000..85328fac7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-03.png new file mode 100644 index 000000000..3bb894277 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-04.png new file mode 100644 index 000000000..aa2b7afa4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-05.png new file mode 100644 index 000000000..610114578 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-06.png new file mode 100644 index 000000000..fcd432bd1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-07.png new file mode 100644 index 000000000..50910d8d1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-08.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-08.png new file mode 100644 index 000000000..ca4801f29 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-09.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-09.png new file mode 100644 index 000000000..6476abc21 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-09.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-10.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-10.png new file mode 100644 index 000000000..31e91bc35 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-10.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-11.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-11.png new file mode 100644 index 000000000..24647031b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-11.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-12.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-12.png new file mode 100644 index 000000000..0546783af Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-12.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-13.png b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-13.png new file mode 100644 index 000000000..96c2f57b0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-dev-ops-5-13.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-introduce-phase-2-01.gif b/docs/.vuepress/public/images/article/project/big-market/big-market-introduce-phase-2-01.gif new file mode 100644 index 000000000..5878e9636 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-introduce-phase-2-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-introduce-phase-2-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-introduce-phase-2-02.png new file mode 100644 index 000000000..b89993e91 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-introduce-phase-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-introduce-phase-2-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-introduce-phase-2-03.png new file mode 100644 index 000000000..525a904bc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-introduce-phase-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-01.png new file mode 100644 index 000000000..b183d421a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-02.png new file mode 100644 index 000000000..b2fe305aa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-03.png new file mode 100644 index 000000000..e0d91b044 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-04.png new file mode 100644 index 000000000..d5dabd66a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-05.png new file mode 100644 index 000000000..d61909685 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-06.png new file mode 100644 index 000000000..ecc720319 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-07.png new file mode 100644 index 000000000..57b3e4231 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-08.png b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-08.png new file mode 100644 index 000000000..9b6d0ba02 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-system-design-diagram-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-01.png new file mode 100644 index 000000000..2f20de44e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-02.png new file mode 100644 index 000000000..c977a55cf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-03.png new file mode 100644 index 000000000..e6f389e58 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-04.png new file mode 100644 index 000000000..f1d44d12d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-05.png new file mode 100644 index 000000000..3c00709d6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-try-it-out-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v1-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-01.png new file mode 100644 index 000000000..8f21eecef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v1-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-02.png new file mode 100644 index 000000000..46254295b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v1-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-03.png new file mode 100644 index 000000000..2a909ec2a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v1-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-04.png new file mode 100644 index 000000000..b5d3598f2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v1-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-05.png new file mode 100644 index 000000000..aa3c34346 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-01.gif b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-01.gif new file mode 100644 index 000000000..ef1cf8052 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-02.png new file mode 100644 index 000000000..f785dd895 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-03.png new file mode 100644 index 000000000..e5d05ca90 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-04.png new file mode 100644 index 000000000..81bcef197 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v3-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-05.png new file mode 100644 index 000000000..75e4426e3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v4-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v4-01.png new file mode 100644 index 000000000..b79333a98 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v4-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v4-02.png new file mode 100644 index 000000000..d07d64b3e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v4-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v4-03.png new file mode 100644 index 000000000..f36cdc16c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v4-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v4-04.png new file mode 100644 index 000000000..985f30a66 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v4-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v5-01.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-01.png new file mode 100644 index 000000000..de31f9da8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v5-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-02.png new file mode 100644 index 000000000..39358bd81 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v5-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-03.png new file mode 100644 index 000000000..89c456926 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v5-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-04.png new file mode 100644 index 000000000..5918811b7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v5-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-05.png new file mode 100644 index 000000000..1b52c33b3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v5-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-06.png new file mode 100644 index 000000000..e1f941e12 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v5-07.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-07.png new file mode 100644 index 000000000..9dff56f30 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v5-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v7-01.gif b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-01.gif new file mode 100644 index 000000000..d68abe650 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v7-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-02.png new file mode 100644 index 000000000..9a6473f7e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v7-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-03.png new file mode 100644 index 000000000..f41234fd1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v7-04.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-04.png new file mode 100644 index 000000000..31ef84f80 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v7-05.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-05.png new file mode 100644 index 000000000..67c349eeb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v7-06.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-06.png new file mode 100644 index 000000000..ed39a7a9c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v7-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v8-01.gif b/docs/.vuepress/public/images/article/project/big-market/big-market-v8-01.gif new file mode 100644 index 000000000..e60416b99 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v8-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v8-02.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v8-02.png new file mode 100644 index 000000000..7dbcad55b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v8-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/big-market-v8-03.png b/docs/.vuepress/public/images/article/project/big-market/big-market-v8-03.png new file mode 100644 index 000000000..763b14796 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/big-market-v8-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-01.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-01.png new file mode 100644 index 000000000..c58b678d9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-01.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-02.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-02.png new file mode 100644 index 000000000..6d8a2cd2c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-02.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-03.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-03.png new file mode 100644 index 000000000..996f26063 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-03.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-04.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-04.png new file mode 100644 index 000000000..be56dea85 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-04.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-05.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-05.png new file mode 100644 index 000000000..44e6d5e81 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-05.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-06.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-06.png new file mode 100644 index 000000000..c977a55cf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-06.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-07.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-07.png new file mode 100644 index 000000000..1a307335b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-07.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-08.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-08.png new file mode 100644 index 000000000..42bef6b3d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-08.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-09.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-09.png new file mode 100644 index 000000000..0de46e602 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-09.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-10.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-10.png new file mode 100644 index 000000000..d7d7eef10 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-10.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-11.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-11.png new file mode 100644 index 000000000..a0fc70df0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-11.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-12.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-12.png new file mode 100644 index 000000000..faa57098d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-12.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-13.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-13.png new file mode 100644 index 000000000..92e9d9120 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-13.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-14.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-14.png new file mode 100644 index 000000000..538db68aa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-14.png differ diff --git a/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-15.png b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-15.png new file mode 100644 index 000000000..531513dfb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/big-market/roadmap-ddd-stc-15.png differ diff --git a/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-01.png b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-01.png new file mode 100644 index 000000000..ee6795835 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-01.png differ diff --git a/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-02.png b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-02.png new file mode 100644 index 000000000..c34dd5fd5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-02.png differ diff --git a/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-03.png b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-03.png new file mode 100644 index 000000000..f0387cbd7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-03.png differ diff --git a/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-04.png b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-04.png new file mode 100644 index 000000000..083b84b99 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-04.png differ diff --git a/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-05.png b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-05.png new file mode 100644 index 000000000..cb400a39b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-05.png differ diff --git a/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-06.png b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-06.png new file mode 100644 index 000000000..f7bbfbe6b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-06.png differ diff --git a/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-07.png b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-07.png new file mode 100644 index 000000000..2ab997079 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/business-behavior-monitor/business-behavior-monitor-07.png differ diff --git a/docs/.vuepress/public/images/article/project/chatbot-api/chatbot-api.png b/docs/.vuepress/public/images/article/project/chatbot-api/chatbot-api.png new file mode 100644 index 000000000..883da7dcd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatbot-api/chatbot-api.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-01.png new file mode 100644 index 000000000..2714a40ce Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-02.png new file mode 100644 index 000000000..3ac83bac3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-03.png new file mode 100644 index 000000000..96d693eea Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-04.png new file mode 100644 index 000000000..267fe4c76 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-05.png new file mode 100644 index 000000000..090030160 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-06.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-06.png new file mode 100644 index 000000000..0700d8790 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-00-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-230422-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-230422-01.png new file mode 100644 index 000000000..c6fd0eda8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-230422-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-01.png new file mode 100644 index 000000000..90e83dadc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-02.png new file mode 100644 index 000000000..6a12212c8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-03.png new file mode 100644 index 000000000..36c962b17 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-04.png new file mode 100644 index 000000000..53a4a3cc1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-05.png new file mode 100644 index 000000000..c8687e88c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-01-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-01.png new file mode 100644 index 000000000..c7ebd4047 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-02.png new file mode 100644 index 000000000..c851a4918 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-03.png new file mode 100644 index 000000000..dd026a1d5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-04.png new file mode 100644 index 000000000..c98cec537 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-02-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-01.png new file mode 100644 index 000000000..1f5f363be Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-02.png new file mode 100644 index 000000000..724c9f35e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-03.png new file mode 100644 index 000000000..47831b9eb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-04.png new file mode 100644 index 000000000..3afbcdfd9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-05.png new file mode 100644 index 000000000..9442b3429 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-06.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-06.png new file mode 100644 index 000000000..5e3abcda1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-07.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-07.png new file mode 100644 index 000000000..58e488420 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-07.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-08.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-08.png new file mode 100644 index 000000000..4e4e38260 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-08.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-09.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-09.png new file mode 100644 index 000000000..11c3cd8d7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-09.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-10.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-10.png new file mode 100644 index 000000000..62da8c8e5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-03-10.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-01.png new file mode 100644 index 000000000..dea061d15 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-02.png new file mode 100644 index 000000000..03f239eb7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-03.png new file mode 100644 index 000000000..7f6262cd8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-04.png new file mode 100644 index 000000000..85994ac60 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-05.png new file mode 100644 index 000000000..968725295 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-06.gif b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-06.gif new file mode 100644 index 000000000..3904ae362 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-04-06.gif differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-01.png new file mode 100644 index 000000000..81dea689f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-02.png new file mode 100644 index 000000000..99f96f11f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-03.png new file mode 100644 index 000000000..cc69f2608 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-04.png new file mode 100644 index 000000000..eb1151151 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-05.png new file mode 100644 index 000000000..00cb324d0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-06.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-06.png new file mode 100644 index 000000000..f68e6dcb5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-07.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-07.png new file mode 100644 index 000000000..051f25851 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-05-07.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-01.png new file mode 100644 index 000000000..dda09debf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-02.png new file mode 100644 index 000000000..6cd74dc7c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-03.png new file mode 100644 index 000000000..d58e25e27 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-04.png new file mode 100644 index 000000000..ba0595af8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-06-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-01.png new file mode 100644 index 000000000..2c28ae79a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-02.png new file mode 100644 index 000000000..05fb2c221 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-03.png new file mode 100644 index 000000000..428393a02 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-04.png new file mode 100644 index 000000000..46071ad1c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-05.png new file mode 100644 index 000000000..50c9aeb5b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-07-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-01.png new file mode 100644 index 000000000..b65561be4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-02.png new file mode 100644 index 000000000..fcf4c561a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-03.png new file mode 100644 index 000000000..f97217dc9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-04.png new file mode 100644 index 000000000..25acb3b1e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-08-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-01.png new file mode 100644 index 000000000..2e599f4fb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-02.png new file mode 100644 index 000000000..f6e8d87dc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-03.png new file mode 100644 index 000000000..199667ee0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-04.png new file mode 100644 index 000000000..603b08d60 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-05.png new file mode 100644 index 000000000..7b196a78b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-09-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-10-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-10-01.png new file mode 100644 index 000000000..cb3f957ab Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-api-10-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-01-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-01-01.png new file mode 100644 index 000000000..7bfd4a9a3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-01-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-01-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-01-02.png new file mode 100644 index 000000000..97e1c6397 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-01-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-01-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-01-03.png new file mode 100644 index 000000000..9a0970e6a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-01-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-02-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-02-01.png new file mode 100644 index 000000000..b81b1a7f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-02-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-02-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-02-02.png new file mode 100644 index 000000000..b0cf55045 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-02-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-01.png new file mode 100644 index 000000000..782feaca6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-02.png new file mode 100644 index 000000000..ac45817c4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-03.png new file mode 100644 index 000000000..7eea2ab63 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-04.png new file mode 100644 index 000000000..bc8150762 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-04-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-01.png new file mode 100644 index 000000000..a3a0e10ed Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-02.png new file mode 100644 index 000000000..4e7132c51 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-03.png new file mode 100644 index 000000000..f2e943ee1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-04.png new file mode 100644 index 000000000..7e3b87fda Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-05.png new file mode 100644 index 000000000..25e939e61 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-06.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-06.png new file mode 100644 index 000000000..e6b803a54 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-07.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-07.png new file mode 100644 index 000000000..862fdea7a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-07.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-08.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-08.png new file mode 100644 index 000000000..a560da52c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-08.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-09.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-09.png new file mode 100644 index 000000000..aea4b7db2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-09.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-10.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-10.png new file mode 100644 index 000000000..121471ea3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-10.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-11.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-11.png new file mode 100644 index 000000000..613305807 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-05-11.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-01.png new file mode 100644 index 000000000..a49f7fd22 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-02.png new file mode 100644 index 000000000..99275b1df Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-03.png new file mode 100644 index 000000000..d45fda6a3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-04.png new file mode 100644 index 000000000..c01d65104 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-05.png new file mode 100644 index 000000000..2af348212 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-06.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-06.png new file mode 100644 index 000000000..6840d0648 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-07.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-07.png new file mode 100644 index 000000000..07b2fbf49 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-07.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-08.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-08.png new file mode 100644 index 000000000..6ee7fa108 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-08.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-09.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-09.png new file mode 100644 index 000000000..5485581de Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-09.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-10.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-10.png new file mode 100644 index 000000000..11395437f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-06-10.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-07-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-07-01.png new file mode 100644 index 000000000..c7d51ed18 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-07-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-07-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-07-02.png new file mode 100644 index 000000000..75551ea22 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-07-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-07-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-07-03.png new file mode 100644 index 000000000..daf0d2bbe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-07-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-08-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-08-01.png new file mode 100644 index 000000000..85c146e50 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-08-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-08-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-08-02.png new file mode 100644 index 000000000..54e140b87 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-dev-ops-08-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-01.gif b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-01.gif new file mode 100644 index 000000000..f15651fc7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-02.png new file mode 100644 index 000000000..527047b7a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-03.png new file mode 100644 index 000000000..cc4c9bef4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-04.png new file mode 100644 index 000000000..00bc86a70 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230723-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-01.png new file mode 100644 index 000000000..10b2b01c5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-02.png new file mode 100644 index 000000000..1c504b66a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-03.png new file mode 100644 index 000000000..f2c310db0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-04.png new file mode 100644 index 000000000..95b335af9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-05.png new file mode 100644 index 000000000..3cf06f595 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230827-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-01.png new file mode 100644 index 000000000..2066f1395 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-02.png new file mode 100644 index 000000000..22ee03025 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-03.png new file mode 100644 index 000000000..10b2b01c5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-04.png new file mode 100644 index 000000000..06aaba4e5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-05.png new file mode 100644 index 000000000..c60343d73 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-06.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-06.png new file mode 100644 index 000000000..c8cb33dd5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-07.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-07.png new file mode 100644 index 000000000..1f5f363be Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-07.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-08.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-08.png new file mode 100644 index 000000000..3d965df22 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-08.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-09.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-09.png new file mode 100644 index 000000000..3d7b5b3ba Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-230905-09.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-01.png new file mode 100644 index 000000000..3edf59a78 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-02.png new file mode 100644 index 000000000..d5294b0cc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-03.png new file mode 100644 index 000000000..1de948752 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-04.png new file mode 100644 index 000000000..5343f26df Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-05.png new file mode 100644 index 000000000..9e57b9c99 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-06.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-06.png new file mode 100644 index 000000000..83f7c35ef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-07.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-07.png new file mode 100644 index 000000000..cd7c2f5e6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-07.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-08.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-08.png new file mode 100644 index 000000000..1f8729b54 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-08.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-09.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-09.png new file mode 100644 index 000000000..727c2a5a0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231007-09.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-01.png new file mode 100644 index 000000000..df6ad41e4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-02.png new file mode 100644 index 000000000..5902ea48a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-03.png new file mode 100644 index 000000000..47e1ad664 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-04.png new file mode 100644 index 000000000..95156965e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-05.png new file mode 100644 index 000000000..f2dab92e9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-231011-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-01.png new file mode 100644 index 000000000..e7223ac6e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-02.png new file mode 100644 index 000000000..d88113c4b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-03.png new file mode 100644 index 000000000..23e0f2f39 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-04.png new file mode 100644 index 000000000..3a37b0ec4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-extra-240121-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-01.png new file mode 100644 index 000000000..9d48baa25 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-02.png new file mode 100644 index 000000000..58293df06 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-03.png new file mode 100644 index 000000000..0446ff4ba Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-04.png new file mode 100644 index 000000000..4084747d2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-05.png new file mode 100644 index 000000000..a92f10258 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-01-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-02-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-02-01.png new file mode 100644 index 000000000..2ef1c0b96 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-02-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-02-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-02-02.png new file mode 100644 index 000000000..d8a1e08c3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-02-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-03-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-03-01.png new file mode 100644 index 000000000..043792d66 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-03-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-03-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-03-02.png new file mode 100644 index 000000000..b25cfda12 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-03-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-03-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-03-03.png new file mode 100644 index 000000000..2b751beb6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-03-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-04-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-04-01.png new file mode 100644 index 000000000..e3bf8fc79 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-04-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-04-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-04-02.png new file mode 100644 index 000000000..b76867edc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-sdk-04-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-01.png new file mode 100644 index 000000000..a70824634 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-02.png new file mode 100644 index 000000000..cafbe148c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-03.png new file mode 100644 index 000000000..eba38a531 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-04.png new file mode 100644 index 000000000..311c9e690 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-05.png new file mode 100644 index 000000000..60a2bc145 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-06.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-06.png new file mode 100644 index 000000000..78a068cbb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-01-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-02-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-02-01.png new file mode 100644 index 000000000..b2ae9f143 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-02-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-02-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-02-02.png new file mode 100644 index 000000000..89480bb5c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-02-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-02-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-02-03.png new file mode 100644 index 000000000..98ea0b30c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-02-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-01.png new file mode 100644 index 000000000..970a8d5b6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-02.png new file mode 100644 index 000000000..14ec4c457 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-03.png new file mode 100644 index 000000000..98ea0b30c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-04.png new file mode 100644 index 000000000..b3d186354 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-03-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-04-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-04-01.png new file mode 100644 index 000000000..855b4411e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-04-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-04-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-04-02.png new file mode 100644 index 000000000..c4959bf4d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-04-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-04-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-04-03.png new file mode 100644 index 000000000..bb469fd58 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-04-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-05-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-05-01.png new file mode 100644 index 000000000..591039f5a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-05-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-05-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-05-02.png new file mode 100644 index 000000000..932cc80ed Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-05-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-05-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-05-03.png new file mode 100644 index 000000000..fbaffb8f6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-05-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-01.png new file mode 100644 index 000000000..5ef52c3ab Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-02.png new file mode 100644 index 000000000..bedfa9c23 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-03.png new file mode 100644 index 000000000..9edfb9603 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-04.png new file mode 100644 index 000000000..65d18f92b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-06-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-01.png new file mode 100644 index 000000000..45291b9f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-02.png new file mode 100644 index 000000000..f6762b049 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-03.png new file mode 100644 index 000000000..580fe3716 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-04.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-04.png new file mode 100644 index 000000000..d97540958 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-05.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-05.png new file mode 100644 index 000000000..79fd96541 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-06.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-06.png new file mode 100644 index 000000000..f241d271c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-07.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-07.png new file mode 100644 index 000000000..75cd956b1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-07-07.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-08-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-08-01.png new file mode 100644 index 000000000..6d170a6ad Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-08-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-08-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-08-02.png new file mode 100644 index 000000000..1ce755f7a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-08-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-08-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-08-03.png new file mode 100644 index 000000000..efa48c6be Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-08-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-09-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-09-01.png new file mode 100644 index 000000000..5b4650025 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-09-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-09-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-09-02.png new file mode 100644 index 000000000..419f03724 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-09-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-09-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-09-03.png new file mode 100644 index 000000000..44dedfc94 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-09-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-10-01.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-10-01.png new file mode 100644 index 000000000..cd7c2f5e6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-10-01.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-10-02.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-10-02.png new file mode 100644 index 000000000..8b42b2569 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-10-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-10-03.png b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-10-03.png new file mode 100644 index 000000000..1f8729b54 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/chatgpt-web-10-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/openai-01.jpg b/docs/.vuepress/public/images/article/project/chatgpt/openai-01.jpg new file mode 100644 index 000000000..426a19bba Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/openai-01.jpg differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/openai-02.png b/docs/.vuepress/public/images/article/project/chatgpt/openai-02.png new file mode 100644 index 000000000..fb0a54843 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/openai-02.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/openai-03.png b/docs/.vuepress/public/images/article/project/chatgpt/openai-03.png new file mode 100644 index 000000000..a4922ed75 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/openai-03.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/openai-04.png b/docs/.vuepress/public/images/article/project/chatgpt/openai-04.png new file mode 100644 index 000000000..e3242d2a2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/openai-04.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/openai-05.png b/docs/.vuepress/public/images/article/project/chatgpt/openai-05.png new file mode 100644 index 000000000..3f5d26b06 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/openai-05.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/openai-06.png b/docs/.vuepress/public/images/article/project/chatgpt/openai-06.png new file mode 100644 index 000000000..72cbb93e8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/openai-06.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/openai-07.png b/docs/.vuepress/public/images/article/project/chatgpt/openai-07.png new file mode 100644 index 000000000..1f8729b54 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/openai-07.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/openai-08.png b/docs/.vuepress/public/images/article/project/chatgpt/openai-08.png new file mode 100644 index 000000000..e73763651 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/openai-08.png differ diff --git a/docs/.vuepress/public/images/article/project/chatgpt/openai-09.png b/docs/.vuepress/public/images/article/project/chatgpt/openai-09.png new file mode 100644 index 000000000..7205f67da Binary files /dev/null and b/docs/.vuepress/public/images/article/project/chatgpt/openai-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-01.jpg b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-01.jpg new file mode 100644 index 000000000..4494fedae Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-01.jpg differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-01.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-01.png new file mode 100644 index 000000000..89cf304b5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-02.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-02.png new file mode 100644 index 000000000..701a153ba Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-03.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-03.png new file mode 100644 index 000000000..d6a242fe9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-04.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-04.png new file mode 100644 index 000000000..56e681b25 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-05.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-05.png new file mode 100644 index 000000000..b245fc314 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-06.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-06.png new file mode 100644 index 000000000..297532dc9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-07.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-07.png new file mode 100644 index 000000000..b88b82623 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-08.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-08.png new file mode 100644 index 000000000..7a6208e62 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-09.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-09.png new file mode 100644 index 000000000..b475171ea Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-10.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-10.png new file mode 100644 index 000000000..12c70e137 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-11.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-11.png new file mode 100644 index 000000000..553df362b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-11.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-12.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-12.png new file mode 100644 index 000000000..c6b8bfec8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-12.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-13.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-13.png new file mode 100644 index 000000000..6c6303eb6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-13.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-14.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-14.png new file mode 100644 index 000000000..83f47c90c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-14.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-15.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-15.png new file mode 100644 index 000000000..8b68325a2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-15.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-16.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-16.png new file mode 100644 index 000000000..cc8954064 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-16.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-17.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-17.png new file mode 100644 index 000000000..59b0da630 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-17.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-18.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-18.png new file mode 100644 index 000000000..a589b756b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-18.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-19.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-19.png new file mode 100644 index 000000000..b61224bda Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-19.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-20.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-20.png new file mode 100644 index 000000000..01fcc0824 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-20.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-21.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-21.png new file mode 100644 index 000000000..902772389 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-21.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-22.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-22.png new file mode 100644 index 000000000..ab8935e54 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-22.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-23.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-23.png new file mode 100644 index 000000000..8d506ff95 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-23.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-24.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-24.png new file mode 100644 index 000000000..379adff04 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/alipay-sandbox-24.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-01.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-01.png new file mode 100644 index 000000000..a42a191a5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-02.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-02.png new file mode 100644 index 000000000..91aaa706d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-03.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-03.png new file mode 100644 index 000000000..2ee76c15d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-04.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-04.png new file mode 100644 index 000000000..13813ce3d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-05.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-05.png new file mode 100644 index 000000000..9a78c6c10 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-06.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-06.png new file mode 100644 index 000000000..3e7547079 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-07.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-07.png new file mode 100644 index 000000000..3028546ba Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-08.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-08.png new file mode 100644 index 000000000..b5d4e7f7b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-09.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-09.png new file mode 100644 index 000000000..0aa9a369c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/openai-tldraw-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-01.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-01.png new file mode 100644 index 000000000..b659e7fb8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-02.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-02.png new file mode 100644 index 000000000..57c84a55c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-03.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-03.png new file mode 100644 index 000000000..07a9ac6f6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-04.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-04.png new file mode 100644 index 000000000..abcf30953 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-05.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-05.png new file mode 100644 index 000000000..a40d27c2c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-content-moderation-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-01.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-01.png new file mode 100644 index 000000000..abcf4b470 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-01.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-02.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-02.png new file mode 100644 index 000000000..aa298fe0b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-03.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-03.png new file mode 100644 index 000000000..e490b7494 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-04.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-04.png new file mode 100644 index 000000000..43da3ed49 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-05.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-05.png new file mode 100644 index 000000000..9f4942a49 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-05.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-06.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-06.png new file mode 100644 index 000000000..06f497ef7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-06.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-07.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-07.png new file mode 100644 index 000000000..bce7cce88 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-07.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-08.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-08.png new file mode 100644 index 000000000..d3cd5076f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-08.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-09.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-09.png new file mode 100644 index 000000000..d7d191915 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-09.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-10.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-10.png new file mode 100644 index 000000000..9ca84c449 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-10.png differ diff --git a/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-11.png b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-11.png new file mode 100644 index 000000000..f4b3fd52d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ddd-scene-solution/xfg-dev-tech-weixin-login-11.png differ diff --git a/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-01.png b/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-01.png new file mode 100644 index 000000000..3aeb4996a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-01.png differ diff --git a/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-02.png b/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-02.png new file mode 100644 index 000000000..bffbdbd63 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-02.png differ diff --git a/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-03.png b/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-03.png new file mode 100644 index 000000000..510fe423f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-03.png differ diff --git a/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-04.png b/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-04.png new file mode 100644 index 000000000..8041553e1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/dynamic-thread-pool/dynamic-thread-pool-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-01.png new file mode 100755 index 000000000..a77dce7e0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-02.png new file mode 100644 index 000000000..d0c2faa5a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-03.png new file mode 100644 index 000000000..f11d9c172 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-04.png new file mode 100644 index 000000000..3e4c31bd9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-2-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-2-01.png new file mode 100755 index 000000000..18373c4d0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-2-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-2-02.png new file mode 100644 index 000000000..85398e1f5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-2-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-2-03.png new file mode 100644 index 000000000..aecfbc484 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-01.png new file mode 100755 index 000000000..deeb57278 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-02.png new file mode 100644 index 000000000..ad9913a80 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-03.png new file mode 100644 index 000000000..5420a9486 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-04.png new file mode 100644 index 000000000..d252b1cf1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-1-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-01.png new file mode 100755 index 000000000..b82291008 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-02.png new file mode 100644 index 000000000..45c2c2c85 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-03.png new file mode 100644 index 000000000..51caebdf0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-04.png new file mode 100644 index 000000000..4d8ab0ba3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-05.png new file mode 100644 index 000000000..13ac69ed8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-10-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-10-01.png new file mode 100755 index 000000000..05bc77a6e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-10-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-10-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-10-02.png new file mode 100644 index 000000000..f39060f69 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-10-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-01.png new file mode 100755 index 000000000..85c5cb2e5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-02.png new file mode 100644 index 000000000..4f7e05ced Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-03.png new file mode 100644 index 000000000..09ccb2bc6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-04.png new file mode 100644 index 000000000..fe28a4f38 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-05.png new file mode 100644 index 000000000..7a2b89441 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-11-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-01.png new file mode 100755 index 000000000..3606d04e1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-02.png new file mode 100644 index 000000000..2f0e8d14c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-03.png new file mode 100644 index 000000000..c856b9e87 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-04.png new file mode 100644 index 000000000..b5665978c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-05.png new file mode 100644 index 000000000..d1a40ff30 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-06.png new file mode 100644 index 000000000..af1c60504 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-12-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-01.png new file mode 100755 index 000000000..c884d5408 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-02.png new file mode 100644 index 000000000..ad6ca2cbb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-03.png new file mode 100644 index 000000000..b1e7be26e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-04.png new file mode 100644 index 000000000..889bcf4b8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-05.png new file mode 100644 index 000000000..e03d2b6cc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-06.png new file mode 100644 index 000000000..cd852ac1d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-13-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-01.png new file mode 100755 index 000000000..ee4a7ee3a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-02.png new file mode 100644 index 000000000..9c736b620 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-03.png new file mode 100644 index 000000000..8c8f7fd0f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-04.png new file mode 100644 index 000000000..14937dd1a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-14-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-01.png new file mode 100755 index 000000000..c8b49a1d7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-02.png new file mode 100644 index 000000000..986a78f7f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-03.png new file mode 100644 index 000000000..3506000a0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-04.png new file mode 100644 index 000000000..a3fd8a14a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-05.png new file mode 100644 index 000000000..695840223 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-06.png new file mode 100644 index 000000000..ba055f25d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-07.png new file mode 100644 index 000000000..2b24014dd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-15-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-01.png new file mode 100755 index 000000000..9ba052966 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-02.png new file mode 100644 index 000000000..931f1a666 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-03.png new file mode 100644 index 000000000..52aab91db Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-04.png new file mode 100644 index 000000000..57924829d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-05.png new file mode 100644 index 000000000..d1de4275c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-06.png new file mode 100644 index 000000000..2a0946dcb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-07.png new file mode 100644 index 000000000..edb349e2a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-08.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-08.png new file mode 100644 index 000000000..0d3019273 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-16-08.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-01.png new file mode 100755 index 000000000..cdc863458 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-02.png new file mode 100644 index 000000000..bdae13477 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-03.png new file mode 100644 index 000000000..93a0704b9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-04.png new file mode 100644 index 000000000..b4b6ee124 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-17-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-01.png new file mode 100755 index 000000000..db2f28b6b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-02.png new file mode 100644 index 000000000..998d3feb8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-03.png new file mode 100644 index 000000000..e453e688b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-04.png new file mode 100644 index 000000000..b3b2ecaab Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-05.png new file mode 100644 index 000000000..3fd37d331 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-06.png new file mode 100644 index 000000000..6a8253e1b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-07.png new file mode 100644 index 000000000..057bd03ab Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-18-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-01.png new file mode 100755 index 000000000..01254b9e2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-02.png new file mode 100644 index 000000000..ecccf2307 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-03.png new file mode 100644 index 000000000..f113a8b5c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-04.png new file mode 100644 index 000000000..d5807e5e6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-19-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-2-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-2-01.png new file mode 100755 index 000000000..f53555121 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-2-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-2-02.png new file mode 100644 index 000000000..e0bc62e87 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-2-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-2-03.png new file mode 100644 index 000000000..a87652b3d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-20-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-20-01.png new file mode 100755 index 000000000..2af83e7b6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-20-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-20-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-20-02.png new file mode 100644 index 000000000..dda6ceaf4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-20-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-01.png new file mode 100755 index 000000000..b65b52fea Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-02.png new file mode 100644 index 000000000..8eee79253 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-03.png new file mode 100644 index 000000000..4f631d096 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-04.png new file mode 100644 index 000000000..2bb9b3279 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-05.png new file mode 100644 index 000000000..643965d2b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-06.png new file mode 100644 index 000000000..4c6274252 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-07.png new file mode 100644 index 000000000..2553d8b4d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-21-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-01.png new file mode 100755 index 000000000..47e379abe Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-02.png new file mode 100644 index 000000000..125f06912 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-03.png new file mode 100644 index 000000000..76ee0b584 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-04.png new file mode 100644 index 000000000..5cf021726 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-22-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-01.png new file mode 100755 index 000000000..a6406ec9c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-02.png new file mode 100644 index 000000000..a6fe9553c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-03.png new file mode 100644 index 000000000..4f40aa8af Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-04.png new file mode 100644 index 000000000..494c51c25 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-05.png new file mode 100644 index 000000000..ecb903ab1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-06.png new file mode 100644 index 000000000..b88fd23f0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-07.png new file mode 100644 index 000000000..e17c8f39c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-08.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-08.png new file mode 100644 index 000000000..244e0d7f9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-08.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-09.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-09.png new file mode 100644 index 000000000..0d1fbd769 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-09.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-10.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-10.png new file mode 100644 index 000000000..e1db230f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-10.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-11.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-11.png new file mode 100644 index 000000000..082e3d433 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-23-11.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-01.png new file mode 100755 index 000000000..e2d98a8c4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-02.png new file mode 100644 index 000000000..c0d103502 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-03.png new file mode 100644 index 000000000..b8d994512 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-04.png new file mode 100644 index 000000000..3f3904fc0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-05.png new file mode 100644 index 000000000..07416dcac Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-06.png new file mode 100644 index 000000000..f9c59efed Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-07.png new file mode 100644 index 000000000..c85bb6ba6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-08.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-08.png new file mode 100644 index 000000000..389890a57 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-08.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-09.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-09.png new file mode 100644 index 000000000..8c1801aea Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-09.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-10.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-10.png new file mode 100644 index 000000000..6f2a5e6eb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-10.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-11.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-11.png new file mode 100644 index 000000000..7ff3fe45b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-11.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-12.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-12.png new file mode 100644 index 000000000..4ad20bb1b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-12.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-13.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-13.png new file mode 100644 index 000000000..a5724ce4c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-13.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-14.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-14.png new file mode 100644 index 000000000..6a7ac4f11 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-14.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-15.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-15.png new file mode 100644 index 000000000..ef2202b67 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-15.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-16.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-16.png new file mode 100644 index 000000000..4ddba8640 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-24-16.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-01.png new file mode 100755 index 000000000..c9e3f4490 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-02.png new file mode 100644 index 000000000..1b5f4fa29 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-03.png new file mode 100644 index 000000000..5f7770fb3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-04.png new file mode 100644 index 000000000..cc252ebe8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-25-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-01.png new file mode 100755 index 000000000..de5b5170f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-02.png new file mode 100644 index 000000000..43ecb2eda Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-03.png new file mode 100644 index 000000000..64ad4bf38 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-04.png new file mode 100644 index 000000000..b32c58f20 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-26-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-01.png new file mode 100755 index 000000000..b959cd95a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-02.png new file mode 100644 index 000000000..978c94137 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-03.png new file mode 100644 index 000000000..14bfb07f7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-04.png new file mode 100644 index 000000000..ae6dfeb12 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-27-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-01.png new file mode 100755 index 000000000..31838b989 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-02.png new file mode 100644 index 000000000..121fd06f4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-03.png new file mode 100644 index 000000000..16ddf07dd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-04.png new file mode 100644 index 000000000..38bb7d7f4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-05.png new file mode 100644 index 000000000..86dea6e3b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-28-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-01.png new file mode 100755 index 000000000..1fefa7a0a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-02.png new file mode 100644 index 000000000..1aaf14d0e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-03.png new file mode 100644 index 000000000..eab506d5d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-04.png new file mode 100644 index 000000000..9f8ca3de4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-29-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-3-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-3-01.png new file mode 100755 index 000000000..7987a0889 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-3-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-3-02.png new file mode 100644 index 000000000..277fb1f3a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-01.png new file mode 100755 index 000000000..68a964a34 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-02.png new file mode 100644 index 000000000..87b072b57 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-03.png new file mode 100644 index 000000000..0ee4721db Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-04.png new file mode 100644 index 000000000..2071429d0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-30-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-01.png new file mode 100755 index 000000000..239b0cee4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-02.png new file mode 100644 index 000000000..bbf413d4f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-03.png new file mode 100644 index 000000000..9d4dac0c0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-04.png new file mode 100644 index 000000000..befbedacb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-31-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-4-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-4-01.png new file mode 100755 index 000000000..7230d1fac Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-4-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-4-02.png new file mode 100644 index 000000000..84d8a0d80 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-4-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-4-03.png new file mode 100644 index 000000000..10c7f4701 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-5-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-5-01.png new file mode 100755 index 000000000..b21426317 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-5-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-5-02.png new file mode 100644 index 000000000..a1380f443 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-5-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-5-03.png new file mode 100644 index 000000000..a48faa57f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-5-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-6-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-6-01.png new file mode 100755 index 000000000..1459d06c5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-6-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-6-02.png new file mode 100644 index 000000000..abb2c7147 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-6-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-6-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-6-03.png new file mode 100644 index 000000000..048edbad2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-6-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-01.png new file mode 100755 index 000000000..33fc4d76b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-02.png new file mode 100644 index 000000000..e51bc8f7c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-03.png new file mode 100644 index 000000000..8aab66507 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-04.png new file mode 100644 index 000000000..76efa198b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-7-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-8-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-8-01.png new file mode 100755 index 000000000..a89bd4457 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-8-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-8-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-8-02.png new file mode 100644 index 000000000..eadb631d2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-8-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-8-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-8-03.png new file mode 100644 index 000000000..e798d6572 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-8-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-9-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-9-01.png new file mode 100755 index 000000000..e23a63a58 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-9-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-9-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-9-02.png new file mode 100644 index 000000000..2918546b1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-9-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-9-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-9-03.png new file mode 100644 index 000000000..31d3d020f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-2-9-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-01.png new file mode 100755 index 000000000..64c12689b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-02.png new file mode 100755 index 000000000..5c19c2f4d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-03.png new file mode 100755 index 000000000..3c79aef06 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-04.png new file mode 100755 index 000000000..326a55536 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-05.png new file mode 100755 index 000000000..6c1313a76 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-241109-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-01.png new file mode 100755 index 000000000..8fdc3ee03 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-02.png new file mode 100644 index 000000000..cc7bd813e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-03.png new file mode 100644 index 000000000..ac6ca2d13 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-04.png new file mode 100644 index 000000000..b7357d384 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-05.png new file mode 100644 index 000000000..e9fba3ec1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-01.png new file mode 100755 index 000000000..075532cbf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-02.png new file mode 100644 index 000000000..c7309121d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-03.png new file mode 100644 index 000000000..feff1c8a4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-04.png new file mode 100644 index 000000000..9fcf2f686 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-05.png new file mode 100644 index 000000000..c81ddf439 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-06.png new file mode 100644 index 000000000..016d7584c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-2-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-01.png new file mode 100755 index 000000000..00694546f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-02.png new file mode 100644 index 000000000..716a4ca2b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-03.png new file mode 100644 index 000000000..ef5e15dcb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-04.png new file mode 100644 index 000000000..3847b5ea6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-05.png new file mode 100644 index 000000000..d039a38f6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-06.png new file mode 100644 index 000000000..79dc0dc86 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-07.png new file mode 100644 index 000000000..3d7d2be0a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-08.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-08.png new file mode 100644 index 000000000..21b1f7bc4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-3-08.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-4-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-4-01.png new file mode 100755 index 000000000..94571111d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-4-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-4-02.png new file mode 100644 index 000000000..92f41f708 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-4-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-4-03.png new file mode 100644 index 000000000..f265c188f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-5-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-5-01.png new file mode 100755 index 000000000..635390ffc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-5-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-5-02.png new file mode 100644 index 000000000..2652e8ba6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-5-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-5-03.png new file mode 100644 index 000000000..cca199176 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-5-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-01.png new file mode 100755 index 000000000..05ac88b76 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-02.png new file mode 100644 index 000000000..e0b78f87e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-03.png new file mode 100644 index 000000000..6ab929e8e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-04.png new file mode 100644 index 000000000..9c4efd4a7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-05.png new file mode 100644 index 000000000..6ad83f5a5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-6-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-01.png new file mode 100755 index 000000000..bc81b7f6b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-02.png new file mode 100644 index 000000000..a4c448ecc Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-03.png new file mode 100644 index 000000000..d3b451f0a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-04.png new file mode 100644 index 000000000..4d501269b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-05.png new file mode 100644 index 000000000..224ad3cb7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-06.png new file mode 100644 index 000000000..f54ae6f88 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-7-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-01.png new file mode 100755 index 000000000..e47c88d82 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-02.png new file mode 100644 index 000000000..4654ea88f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-03.png new file mode 100644 index 000000000..32fa8dfc6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-04.png new file mode 100644 index 000000000..cc3668986 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-05.png new file mode 100644 index 000000000..c819870bf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-06.png new file mode 100644 index 000000000..348a8bc05 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-07.png new file mode 100644 index 000000000..d4781a1f0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-08.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-08.png new file mode 100644 index 000000000..8ebed8cd0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-3-8-08.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-01.png new file mode 100755 index 000000000..dfc1f1781 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-02.png new file mode 100644 index 000000000..f849c9567 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-03.png new file mode 100644 index 000000000..8493ffdf6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-04.png new file mode 100644 index 000000000..91bda7fe3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-05.png new file mode 100644 index 000000000..5d0e3e81f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-06.png new file mode 100644 index 000000000..c12e464c0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-07.png new file mode 100644 index 000000000..4f467b52a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-08.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-08.png new file mode 100644 index 000000000..c383df54c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-1-08.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-01.png new file mode 100755 index 000000000..ed63ba4bf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-02.png new file mode 100644 index 000000000..25479bf79 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-03.png new file mode 100644 index 000000000..83480e7ef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-04.png new file mode 100644 index 000000000..bb31a2536 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-05.png new file mode 100644 index 000000000..550da8ec7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-06.png new file mode 100644 index 000000000..aafc71726 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-07.png new file mode 100644 index 000000000..c88a175c5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-08.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-08.png new file mode 100644 index 000000000..fde7a2378 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-2-08.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-01.png new file mode 100755 index 000000000..6d900205e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-02.png new file mode 100644 index 000000000..0548a3e95 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-03.png new file mode 100644 index 000000000..ad8eb566e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-04.png new file mode 100644 index 000000000..353e41dd4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-05.png new file mode 100644 index 000000000..67cff6d74 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-06.png new file mode 100644 index 000000000..8a16078ee Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-07.png new file mode 100644 index 000000000..ef967f867 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-4-3-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-notes-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-notes-01.png new file mode 100644 index 000000000..060939d7c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-notes-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-241229-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-241229-01.png new file mode 100644 index 000000000..1853f66de Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-241229-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-241229-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-241229-02.png new file mode 100644 index 000000000..68b5af395 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-241229-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-01.gif b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-01.gif new file mode 100644 index 000000000..5d637f90d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-02.png new file mode 100644 index 000000000..4bf2b198c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-03.png new file mode 100644 index 000000000..13a47fc30 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-04.png new file mode 100644 index 000000000..b2852152e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-05.png new file mode 100644 index 000000000..6381be155 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-06.png new file mode 100644 index 000000000..527f27d9b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250118-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-01.png new file mode 100644 index 000000000..8db8aa512 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-02.png new file mode 100644 index 000000000..c3a25fc4e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-03.png new file mode 100644 index 000000000..f0b40ef53 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-04.png new file mode 100644 index 000000000..e5310a097 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-05.png new file mode 100644 index 000000000..b9ec88b6f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-06.png new file mode 100644 index 000000000..16f387955 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-07.png new file mode 100644 index 000000000..569d5dba5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-08.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-08.png new file mode 100644 index 000000000..4db135cad Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250205-08.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250629-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250629-01.png new file mode 100644 index 000000000..258d2db63 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250629-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250629-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250629-02.png new file mode 100644 index 000000000..24b910750 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250629-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-01.png new file mode 100644 index 000000000..ba229e549 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-02.png new file mode 100644 index 000000000..a90eadc98 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-03.png new file mode 100644 index 000000000..fa410b53d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-04.png new file mode 100644 index 000000000..5acd6faaa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-250803-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-01.png new file mode 100644 index 000000000..e42a15181 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-02.png new file mode 100644 index 000000000..ebf2eb61b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-03.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-03.png new file mode 100644 index 000000000..13a32fbd9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-04.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-04.png new file mode 100644 index 000000000..c913caa22 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-04.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-05.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-05.png new file mode 100644 index 000000000..85f363fe0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-05.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-06.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-06.png new file mode 100644 index 000000000..60707c183 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-06.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-07.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-07.png new file mode 100644 index 000000000..219df9a91 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v4-07.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v5-01.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v5-01.png new file mode 100644 index 000000000..c575e158e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v5-02.png b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v5-02.png new file mode 100644 index 000000000..044718d9d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/group-buy-market/group-buy-market-promotion-v5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-01.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-01.png new file mode 100644 index 000000000..4b09b9734 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-01.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-1-01.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-1-01.png new file mode 100644 index 000000000..ad4902995 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-1-02.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-1-02.png new file mode 100644 index 000000000..bb7d25c93 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-1-03.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-1-03.png new file mode 100644 index 000000000..8b4c891c3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-01.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-01.png new file mode 100644 index 000000000..671241ef0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-02.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-02.png new file mode 100644 index 000000000..587f6b960 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-03.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-03.png new file mode 100644 index 000000000..fb2da7256 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-04.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-04.png new file mode 100644 index 000000000..45b14206c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-05.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-05.png new file mode 100644 index 000000000..27c555a7d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-01.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-01.png new file mode 100644 index 000000000..dc7abef89 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-02.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-02.png new file mode 100644 index 000000000..fbce624f0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-03.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-03.png new file mode 100644 index 000000000..246740714 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-04.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-04.png new file mode 100644 index 000000000..dd438d30c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-05.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-05.png new file mode 100644 index 000000000..acb15f2c0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-06.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-06.png new file mode 100644 index 000000000..a0bbf13f4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-06.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-07.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-07.png new file mode 100644 index 000000000..a5c391307 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-3-07.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-01.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-01.png new file mode 100644 index 000000000..656b058c9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-02.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-02.png new file mode 100644 index 000000000..880758f95 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-03.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-03.png new file mode 100644 index 000000000..0697811fa Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-04.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-04.png new file mode 100644 index 000000000..fcda29a67 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-04.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-05.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-05.png new file mode 100644 index 000000000..99e69594b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-4-05.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-01.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-01.png new file mode 100644 index 000000000..7458f5d45 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-02.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-02.png new file mode 100644 index 000000000..b36135fae Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-03.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-03.png new file mode 100644 index 000000000..3c468c66c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-03.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-04.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-04.png new file mode 100644 index 000000000..a5f929302 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-5-04.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-6-01.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-6-01.png new file mode 100644 index 000000000..818ae30d2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-6-02.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-6-02.png new file mode 100644 index 000000000..f79e504c3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-6-02.png differ diff --git a/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-6-03.png b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-6-03.png new file mode 100644 index 000000000..0a6599552 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/local-task-message/local-task-message-6-03.png differ diff --git a/docs/.vuepress/public/images/article/project/lottery/Part-1/1-00.png b/docs/.vuepress/public/images/article/project/lottery/Part-1/1-00.png new file mode 100644 index 000000000..8ca6cd190 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lottery/Part-1/1-00.png differ diff --git a/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-01.gif b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-01.gif new file mode 100644 index 000000000..a50b768b3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-02.png b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-02.png new file mode 100644 index 000000000..ece2c01b9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-02.png differ diff --git a/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-03.png b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-03.png new file mode 100644 index 000000000..7c7ac73f7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-03.png differ diff --git a/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-04.png b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-04.png new file mode 100644 index 000000000..6701498b4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-04.png differ diff --git a/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-05.png b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-05.png new file mode 100644 index 000000000..d6e7f015b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/ltzf-sdk-java/ltzf-sdk-java-05.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-01.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-01.png new file mode 100644 index 000000000..072c6fe3b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-01.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-02.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-02.png new file mode 100644 index 000000000..1d32cceac Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-02.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-03.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-03.png new file mode 100644 index 000000000..33786c268 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-03.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-04.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-04.png new file mode 100644 index 000000000..8b47275a5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-04.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-05.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-05.png new file mode 100644 index 000000000..4704c9486 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-05.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-06.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-06.png new file mode 100644 index 000000000..fc9460b32 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-06.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-07.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-07.png new file mode 100644 index 000000000..b738804ff Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-07.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-08.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-08.png new file mode 100644 index 000000000..4c969101b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-08.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-09.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-09.png new file mode 100644 index 000000000..502504c0b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-09.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-10.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-10.png new file mode 100644 index 000000000..fa65ea6d2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-10.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-11.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-11.png new file mode 100644 index 000000000..1f6235d5f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-11.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-12.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-12.png new file mode 100644 index 000000000..64e72cc74 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-12.png differ diff --git a/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-13.png b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-13.png new file mode 100644 index 000000000..55dedc253 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/lucky-tackout/lucky-tackout-introduction-13.png differ diff --git a/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-01.png b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-01.png new file mode 100644 index 000000000..7fb251c80 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-01.png differ diff --git a/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-02.png b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-02.png new file mode 100644 index 000000000..3227f65dd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-02.png differ diff --git a/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-03.png b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-03.png new file mode 100644 index 000000000..1e4eff18b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-03.png differ diff --git a/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-04.png b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-04.png new file mode 100644 index 000000000..7b74abea5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-04.png differ diff --git a/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-05.png b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-05.png new file mode 100644 index 000000000..a62dcdd6a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-05.png differ diff --git a/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-06.png b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-06.png new file mode 100644 index 000000000..589ca85ee Binary files /dev/null and b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-06.png differ diff --git a/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-07.png b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-07.png new file mode 100644 index 000000000..1fb525bff Binary files /dev/null and b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-07.png differ diff --git a/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-08.png b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-08.png new file mode 100644 index 000000000..fa0e5f247 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/openai-code-review/openai-code-review-08.png differ diff --git a/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-01.png b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-01.png new file mode 100644 index 000000000..d1f832197 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-02.png b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-02.png new file mode 100644 index 000000000..453c3da13 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-03.png b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-03.png new file mode 100644 index 000000000..afc1ef440 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-04.png b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-04.png new file mode 100644 index 000000000..4c4046f0e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-05.png b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-05.png new file mode 100644 index 000000000..ada1369cd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-06.png b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-06.png new file mode 100644 index 000000000..fd5c9ea1b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/prd/xfg-prd-1-06.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-0-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-0-01.png new file mode 100644 index 000000000..011385180 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-0-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-0-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-0-02.png new file mode 100644 index 000000000..1877708e1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-0-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-0-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-0-03.png new file mode 100644 index 000000000..d218c2d23 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-0-03.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-1-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-1-01.png new file mode 100644 index 000000000..dd555ce85 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-1-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-1-02.png new file mode 100644 index 000000000..9e3804676 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-1-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-1-03.png new file mode 100644 index 000000000..bb39cb099 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-01.png new file mode 100644 index 000000000..d73726c56 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-02.png new file mode 100644 index 000000000..3a3080d99 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-03.png new file mode 100644 index 000000000..e582f1150 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-04.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-04.png new file mode 100644 index 000000000..ded65f9c2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-05.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-05.png new file mode 100644 index 000000000..cb89076c7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-3-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-3-01.png new file mode 100644 index 000000000..2152ee64c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-3-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-3-02.png new file mode 100644 index 000000000..8cc8a9ed8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-2-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-01.png new file mode 100644 index 000000000..4ced3d23c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-02.png new file mode 100644 index 000000000..7044efbf4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-03.png new file mode 100644 index 000000000..b82d12b4c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-04.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-04.png new file mode 100644 index 000000000..2492516a2 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-05.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-05.png new file mode 100644 index 000000000..51eefdd64 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-05.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-06.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-06.png new file mode 100644 index 000000000..26f80cf88 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-06.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-07.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-07.png new file mode 100644 index 000000000..d56ac7d3d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-07.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-08.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-08.png new file mode 100644 index 000000000..89a9602c4 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-08.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-09.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-09.png new file mode 100644 index 000000000..aab97d7a6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-09.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-10.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-10.png new file mode 100644 index 000000000..c141e153e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-1-10.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-01.png new file mode 100644 index 000000000..f9fc6a122 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-02.png new file mode 100644 index 000000000..e52b790cb Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-03.png new file mode 100644 index 000000000..4a789947a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-04.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-04.png new file mode 100644 index 000000000..c8d8c5300 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-05.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-05.png new file mode 100644 index 000000000..407f25594 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-06.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-06.png new file mode 100644 index 000000000..9f96f9500 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-06.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-07.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-07.png new file mode 100644 index 000000000..853112319 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-07.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-08.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-08.png new file mode 100644 index 000000000..d9c7436c6 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-08.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-09.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-09.png new file mode 100644 index 000000000..39a6f9b4a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-09.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-10.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-10.png new file mode 100644 index 000000000..6791c4291 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-2-10.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-01.png new file mode 100644 index 000000000..92844e6f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-02.png new file mode 100644 index 000000000..ee4beac9e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-03.png new file mode 100644 index 000000000..af41df69a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-04.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-04.png new file mode 100644 index 000000000..f4357f47b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-05.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-05.png new file mode 100644 index 000000000..e657cb62a Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-06.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-06.png new file mode 100644 index 000000000..5d2c96932 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-4-3-06.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-1-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-1-01.png new file mode 100644 index 000000000..dac7c7cf9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-1-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-1-02.png new file mode 100644 index 000000000..ab8a37566 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-2-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-2-02.png new file mode 100644 index 000000000..7cd44a207 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-3-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-3-01.png new file mode 100644 index 000000000..ab7c08ed5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-4-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-4-01.png new file mode 100644 index 000000000..237532bf7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-6-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-6-01.png new file mode 100644 index 000000000..38efa3421 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-ddd-3-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-1-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-1-01.png new file mode 100644 index 000000000..95dea7831 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-1-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-1-02.png new file mode 100644 index 000000000..185a760dd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-1-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-1-03.png new file mode 100644 index 000000000..5855428bd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-01.png new file mode 100644 index 000000000..e35b79f0c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-02.png new file mode 100644 index 000000000..ae309ae21 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-03.png new file mode 100644 index 000000000..ca786a16b Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-04.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-04.png new file mode 100644 index 000000000..b9dc4d14f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-01.png new file mode 100644 index 000000000..1f92af8b7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-02.png new file mode 100644 index 000000000..69405813e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-03.png new file mode 100644 index 000000000..d1711112c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-04.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-04.png new file mode 100644 index 000000000..6d07d979d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-05.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-05.png new file mode 100644 index 000000000..efcd335e0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-4-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-4-01.png new file mode 100644 index 000000000..37ad10331 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-4-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-4-02.png new file mode 100644 index 000000000..29077bbb7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-5-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-5-01.png new file mode 100644 index 000000000..ebfd8a9f0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-5-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-5-02.png new file mode 100644 index 000000000..dde0ba2e9 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-5-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-6-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-6-01.png new file mode 100644 index 000000000..0cd9547e1 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-6-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-7-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-7-01.png new file mode 100644 index 000000000..b43222f48 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-7-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-8-01.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-8-01.png new file mode 100644 index 000000000..9a5bb8487 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-8-01.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-8-02.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-8-02.png new file mode 100644 index 000000000..2b7836b9f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-8-02.png differ diff --git a/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-8-03.png b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-8-03.png new file mode 100644 index 000000000..0a8af6ee7 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/s-pay-mall/s-pay-mall-mvc-3-8-03.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-01.png b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-01.png new file mode 100644 index 000000000..cd54100da Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-01.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-02.png b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-02.png new file mode 100644 index 000000000..65ed7f697 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-02.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-03.png b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-03.png new file mode 100644 index 000000000..caa337eb0 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-03.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-04.png b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-04.png new file mode 100644 index 000000000..e0e545804 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-04.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-05.png b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-05.png new file mode 100644 index 000000000..a0a8c20da Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-05.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-06.png b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-06.png new file mode 100644 index 000000000..f7bc15d80 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-06.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-07.png b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-07.png new file mode 100644 index 000000000..812777e8f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-fish-pond/xfg-fish-pond-introduction-07.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-01.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-01.png new file mode 100644 index 000000000..ff82e2c7d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-01.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-02.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-02.png new file mode 100644 index 000000000..861c6932d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-02.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-03.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-03.png new file mode 100644 index 000000000..c6a96cabf Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-03.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-04.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-04.png new file mode 100644 index 000000000..9e099d13e Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-04.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-05.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-05.png new file mode 100644 index 000000000..a87fe8ffd Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-0-05.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-01.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-01.png new file mode 100644 index 000000000..a79d39385 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-01.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-02.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-02.png new file mode 100644 index 000000000..475e72003 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-02.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-03.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-03.png new file mode 100644 index 000000000..e23b64cef Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-03.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-04.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-04.png new file mode 100644 index 000000000..01586774c Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-1-04.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-01.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-01.png new file mode 100644 index 000000000..8c47ef8a8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-01.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-02.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-02.png new file mode 100644 index 000000000..12b118ca8 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-02.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-03.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-03.png new file mode 100644 index 000000000..659693111 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-03.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-04.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-04.png new file mode 100644 index 000000000..d77d9dc90 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-04.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-05.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-05.png new file mode 100644 index 000000000..2573c9257 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-05.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-06.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-06.png new file mode 100644 index 000000000..481e66400 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-06.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-01.gif b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-01.gif new file mode 100644 index 000000000..2c1084909 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-01.gif differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-02.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-02.png new file mode 100644 index 000000000..3e9646293 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-03.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-03.png new file mode 100644 index 000000000..065b3dd7d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-04.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-04.png new file mode 100644 index 000000000..a14abee1d Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-05.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-05.png new file mode 100644 index 000000000..e6393e403 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-05.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-06.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-06.png new file mode 100644 index 000000000..08a6e4a0f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-06.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-07.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-07.png new file mode 100644 index 000000000..68f68ff98 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-07.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-08.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-08.png new file mode 100644 index 000000000..9bf95ee07 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-08.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-09.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-09.png new file mode 100644 index 000000000..e90c26c61 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-2-3-09.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-01.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-01.png new file mode 100644 index 000000000..53ab2800f Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-01.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-02.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-02.png new file mode 100644 index 000000000..e4ab3e542 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-02.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-03.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-03.png new file mode 100644 index 000000000..1a0329830 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-03.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-04.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-04.png new file mode 100644 index 000000000..92009f6ad Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-3-04.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-01.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-01.png new file mode 100644 index 000000000..51f91db50 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-01.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-02.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-02.png new file mode 100644 index 000000000..c90fb16a5 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-02.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-03.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-03.png new file mode 100644 index 000000000..772543295 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-03.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-04.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-04.png new file mode 100644 index 000000000..394ee4c53 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-4-04.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-5-01.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-5-01.png new file mode 100644 index 000000000..d4e9c0d78 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-5-01.png differ diff --git a/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-5-02.png b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-5-02.png new file mode 100644 index 000000000..37a812fd3 Binary files /dev/null and b/docs/.vuepress/public/images/article/project/xfg-wrench/xfg-wrench-5-02.png differ diff --git a/docs/.vuepress/public/images/article/spring/mybatis-240428-01.png b/docs/.vuepress/public/images/article/spring/mybatis-240428-01.png new file mode 100644 index 000000000..630f9767d Binary files /dev/null and b/docs/.vuepress/public/images/article/spring/mybatis-240428-01.png differ diff --git a/docs/.vuepress/public/images/article/spring/mybatis-240428-02.png b/docs/.vuepress/public/images/article/spring/mybatis-240428-02.png new file mode 100644 index 000000000..092573b99 Binary files /dev/null and b/docs/.vuepress/public/images/article/spring/mybatis-240428-02.png differ diff --git a/docs/.vuepress/public/images/article/spring/mybatis-240428-03.png b/docs/.vuepress/public/images/article/spring/mybatis-240428-03.png new file mode 100644 index 000000000..915397ab9 Binary files /dev/null and b/docs/.vuepress/public/images/article/spring/mybatis-240428-03.png differ diff --git a/docs/.vuepress/public/images/article/spring/mybatis-240428-04.png b/docs/.vuepress/public/images/article/spring/mybatis-240428-04.png new file mode 100644 index 000000000..5c237a433 Binary files /dev/null and b/docs/.vuepress/public/images/article/spring/mybatis-240428-04.png differ diff --git a/docs/.vuepress/public/images/article/spring/mybatis-240428-05.png b/docs/.vuepress/public/images/article/spring/mybatis-240428-05.png new file mode 100644 index 000000000..4915881eb Binary files /dev/null and b/docs/.vuepress/public/images/article/spring/mybatis-240428-05.png differ diff --git a/docs/.vuepress/public/images/article/spring/mybatis-240428-06.png b/docs/.vuepress/public/images/article/spring/mybatis-240428-06.png new file mode 100644 index 000000000..08238870c Binary files /dev/null and b/docs/.vuepress/public/images/article/spring/mybatis-240428-06.png differ diff --git a/docs/.vuepress/public/images/article/spring/mybatis-240428-07.png b/docs/.vuepress/public/images/article/spring/mybatis-240428-07.png new file mode 100644 index 000000000..e2ce1b386 Binary files /dev/null and b/docs/.vuepress/public/images/article/spring/mybatis-240428-07.png differ diff --git a/docs/.vuepress/public/images/article/spring/mybatis-240428-08.png b/docs/.vuepress/public/images/article/spring/mybatis-240428-08.png new file mode 100644 index 000000000..691195536 Binary files /dev/null and b/docs/.vuepress/public/images/article/spring/mybatis-240428-08.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-01.png b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-01.png new file mode 100644 index 000000000..e023cfdb3 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-02.png b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-02.png new file mode 100644 index 000000000..352d828df Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-03.png b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-03.png new file mode 100644 index 000000000..fde0bc0d8 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-04.png b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-04.png new file mode 100644 index 000000000..9341409d2 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-05.png b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-05.png new file mode 100644 index 000000000..3f6428b16 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-06.png b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-06.png new file mode 100644 index 000000000..79ac8e430 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-07.png b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-07.png new file mode 100644 index 000000000..8ada0dcd1 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/jianli-xiaozhao-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/job-hire-jd-01.png b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-01.png new file mode 100644 index 000000000..0125211b1 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/job-hire-jd-02.png b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-02.png new file mode 100644 index 000000000..0132c37d8 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/job-hire-jd-03.png b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-03.png new file mode 100644 index 000000000..0243629e9 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/job-hire-jd-04.png b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-04.png new file mode 100644 index 000000000..a6d54a915 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/job-hire-jd-05.png b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-05.png new file mode 100644 index 000000000..abba9c0f0 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/job-hire-jd-06.png b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-06.png new file mode 100644 index 000000000..3fad0bac4 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/job-hire-jd-07.png b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-07.png new file mode 100644 index 000000000..e2196d615 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/job-hire-jd-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-01.png b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-01.png new file mode 100644 index 000000000..879d7b9ac Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-02.png b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-02.png new file mode 100644 index 000000000..1334568a4 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-03.png b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-03.png new file mode 100644 index 000000000..7a8c25d1f Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-04.png b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-04.png new file mode 100644 index 000000000..bd196d5bf Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-05.png b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-05.png new file mode 100644 index 000000000..143bff78a Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-06.png b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-06.png new file mode 100644 index 000000000..9fe55e2de Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-07.png b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-07.png new file mode 100644 index 000000000..c908b6679 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/openai-sdk-java-00-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/pdf-maths-02.png b/docs/.vuepress/public/images/article/zsxq/pdf-maths-02.png new file mode 100644 index 000000000..d3642a597 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/pdf-maths-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/project-plan-v2406-01.gif b/docs/.vuepress/public/images/article/zsxq/project-plan-v2406-01.gif new file mode 100644 index 000000000..3c5ece404 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/project-plan-v2406-01.gif differ diff --git a/docs/.vuepress/public/images/article/zsxq/student-learn-01.gif b/docs/.vuepress/public/images/article/zsxq/student-learn-01.gif new file mode 100644 index 000000000..66c7aa5cf Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/student-learn-01.gif differ diff --git a/docs/.vuepress/public/images/article/zsxq/student-learn-02.png b/docs/.vuepress/public/images/article/zsxq/student-learn-02.png new file mode 100644 index 000000000..1cb999c80 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/student-learn-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/student-learn-03.png b/docs/.vuepress/public/images/article/zsxq/student-learn-03.png new file mode 100644 index 000000000..f37d35878 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/student-learn-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/student-learn-04.png b/docs/.vuepress/public/images/article/zsxq/student-learn-04.png new file mode 100644 index 000000000..5ddef8138 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/student-learn-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/student-learn-05.png b/docs/.vuepress/public/images/article/zsxq/student-learn-05.png new file mode 100644 index 000000000..c02fc8c54 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/student-learn-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/xingqiu-231018-02.png b/docs/.vuepress/public/images/article/zsxq/xingqiu-231018-02.png new file mode 100644 index 000000000..b0a02e5b6 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/xingqiu-231018-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230528-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-01.png new file mode 100644 index 000000000..5599dae47 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230528-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-02.png new file mode 100644 index 000000000..502d819b8 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230528-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-03.png new file mode 100644 index 000000000..6aeef3885 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230528-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-04.png new file mode 100644 index 000000000..abb320d17 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230528-05.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-05.png new file mode 100644 index 000000000..8cea7c1e8 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230528-06.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-06.png new file mode 100644 index 000000000..742e89a7d Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230528-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-01.png new file mode 100644 index 000000000..98a324022 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-02.png new file mode 100644 index 000000000..67cd935c3 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-03.png new file mode 100644 index 000000000..943aa48ca Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-04.png new file mode 100644 index 000000000..75dc54f1e Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-05.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-05.png new file mode 100644 index 000000000..1a5a798a1 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-06.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-06.png new file mode 100644 index 000000000..917a2ac5c Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-07.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-07.png new file mode 100644 index 000000000..7a4fe8f2e Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-08.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-08.png new file mode 100644 index 000000000..311bf1ab1 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-08.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-09.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-09.png new file mode 100644 index 000000000..04a923cd3 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-09.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-10.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-10.png new file mode 100644 index 000000000..7e57558df Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-10.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-11.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-11.png new file mode 100644 index 000000000..9120cee4e Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-11.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-12.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-12.png new file mode 100644 index 000000000..31dca1f89 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-12.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-13.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-13.png new file mode 100644 index 000000000..c2076d48a Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-13.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230625-14.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-14.png new file mode 100644 index 000000000..42af32b65 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230625-14.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230824-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-01.png new file mode 100644 index 000000000..2a9c27222 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230824-02.jpg b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-02.jpg new file mode 100644 index 000000000..7a681cfef Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-02.jpg differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230824-03.jpg b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-03.jpg new file mode 100644 index 000000000..63357fd69 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-03.jpg differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230824-04.jpg b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-04.jpg new file mode 100644 index 000000000..8a3425f25 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-04.jpg differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230824-05.jpg b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-05.jpg new file mode 100644 index 000000000..70591b021 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-05.jpg differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230824-06.jpg b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-06.jpg new file mode 100644 index 000000000..6f9b84bc1 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-06.jpg differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230824-07.jpg b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-07.jpg new file mode 100644 index 000000000..236fcb714 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-07.jpg differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-230824-08.jpg b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-08.jpg new file mode 100644 index 000000000..e050227a2 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-230824-08.jpg differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-01.png new file mode 100644 index 000000000..e6acd9432 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-02.png new file mode 100644 index 000000000..2ffe3745a Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-03.png new file mode 100644 index 000000000..5e5d5dc09 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-04.png new file mode 100644 index 000000000..4cd98bd61 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-05.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-05.png new file mode 100644 index 000000000..084aa793f Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-06.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-06.png new file mode 100644 index 000000000..b95370545 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-07.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-07.png new file mode 100644 index 000000000..087da0390 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-08.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-08.png new file mode 100644 index 000000000..2567bb5f9 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-08.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-09.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-09.png new file mode 100644 index 000000000..3f4c94aa0 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-09.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-10.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-10.png new file mode 100644 index 000000000..8fe6ea3f3 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-10.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-11.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-11.png new file mode 100644 index 000000000..83d051b7f Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-11.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-12.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-12.png new file mode 100644 index 000000000..c20c51a3c Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-12.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-231224-13.png b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-13.png new file mode 100644 index 000000000..74bd44387 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-231224-13.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-01.png new file mode 100644 index 000000000..3a8c10939 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-02.png new file mode 100644 index 000000000..e3cbcd494 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-03.png new file mode 100644 index 000000000..a864e47aa Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-04.png new file mode 100644 index 000000000..66cdef0f6 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-05.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-05.png new file mode 100644 index 000000000..b0a364fcb Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-06.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-06.png new file mode 100644 index 000000000..091fbaf71 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-07.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-07.png new file mode 100644 index 000000000..d67eddee7 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-08.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-08.png new file mode 100644 index 000000000..0da06441d Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-08.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-09.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-09.png new file mode 100644 index 000000000..7d811c41f Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-09.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-10.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-10.png new file mode 100644 index 000000000..f50b80a76 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-10.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-11.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-11.png new file mode 100644 index 000000000..1c9924e04 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-11.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240317-12.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-12.png new file mode 100644 index 000000000..3ee57d519 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240317-12.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240519-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-01.png new file mode 100644 index 000000000..20a5b6f9e Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240519-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-02.png new file mode 100644 index 000000000..717f9ac0b Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240519-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-03.png new file mode 100644 index 000000000..1cc7eeec4 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240519-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-04.png new file mode 100644 index 000000000..cae9636a9 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240519-05.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-05.png new file mode 100644 index 000000000..46b252b5a Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240519-06.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-06.png new file mode 100644 index 000000000..75227c92d Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240519-07.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-07.png new file mode 100644 index 000000000..aede4d725 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240519-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240811-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240811-01.png new file mode 100644 index 000000000..4e74e8ed3 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240811-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240811-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240811-02.png new file mode 100644 index 000000000..1aabff523 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240811-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240811-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240811-03.png new file mode 100644 index 000000000..840392084 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240811-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-240811-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-240811-04.png new file mode 100644 index 000000000..5c3eed47c Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-240811-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241007-01.gif b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-01.gif new file mode 100644 index 000000000..a8e4892e1 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-01.gif differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241007-02-2.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-02-2.png new file mode 100644 index 000000000..241a48fd0 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-02-2.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241007-02-3.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-02-3.png new file mode 100644 index 000000000..788b6aad2 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-02-3.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241007-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-02.png new file mode 100644 index 000000000..c8b986380 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241007-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-03.png new file mode 100644 index 000000000..d908f86a2 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241007-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-04.png new file mode 100644 index 000000000..98e3b0cf3 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241007-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241024-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-01.png new file mode 100644 index 000000000..37bfbaae2 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241024-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-02.png new file mode 100644 index 000000000..d54ed7826 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241024-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-03.png new file mode 100644 index 000000000..c0da6857c Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241024-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-04.png new file mode 100644 index 000000000..f69503a34 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241024-05.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-05.png new file mode 100644 index 000000000..d3531e166 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241024-06.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-06.png new file mode 100644 index 000000000..c04db94a3 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241024-07.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-07.png new file mode 100644 index 000000000..ff8ce887f Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241024-08.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-08.png new file mode 100644 index 000000000..7e91f252b Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241024-08.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-00.gif b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-00.gif new file mode 100644 index 000000000..25097b89a Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-00.gif differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-01.png new file mode 100755 index 000000000..9a61ca7b7 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-02.png new file mode 100755 index 000000000..46495d2b7 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-03.png new file mode 100755 index 000000000..3c82f25b4 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-04.png new file mode 100755 index 000000000..e2f9c4274 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-05.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-05.png new file mode 100755 index 000000000..4cc86127c Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-06.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-06.png new file mode 100755 index 000000000..db9447140 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-07.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-07.png new file mode 100755 index 000000000..b79911d09 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-08.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-08.png new file mode 100755 index 000000000..4af98f8d0 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-08.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-09.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-09.png new file mode 100755 index 000000000..a8826a4fe Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-09.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-241219-10.png b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-10.png new file mode 100755 index 000000000..4d998a367 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-241219-10.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-250115-01.jpg b/docs/.vuepress/public/images/article/zsxq/zsxq-250115-01.jpg new file mode 100644 index 000000000..6d55952a8 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-250115-01.jpg differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-door.png b/docs/.vuepress/public/images/article/zsxq/zsxq-door.png new file mode 100644 index 000000000..976ee0aa2 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-door.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-material-230802-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-material-230802-01.png new file mode 100644 index 000000000..3ad473e11 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-material-230802-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-material-230802-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-material-230802-02.png new file mode 100644 index 000000000..70fc80596 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-material-230802-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-material-230802-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-material-230802-03.png new file mode 100644 index 000000000..140023550 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-material-230802-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-01.png new file mode 100644 index 000000000..3d79c2d6e Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-02.png new file mode 100644 index 000000000..98757b6af Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-03.png new file mode 100644 index 000000000..430a1eec3 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-04.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-04.png new file mode 100644 index 000000000..27e475571 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-05.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-05.png new file mode 100644 index 000000000..74f57177d Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-06.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-06.png new file mode 100644 index 000000000..53199ce9a Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-07.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-07.png new file mode 100644 index 000000000..323595e55 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-08.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-08.png new file mode 100644 index 000000000..faebaafb9 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-08.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-09.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-09.png new file mode 100644 index 000000000..3af6c4105 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-09.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-10.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-10.png new file mode 100644 index 000000000..ba99ae707 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-10.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-openai-11.png b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-11.png new file mode 100644 index 000000000..4ede37657 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-openai-11.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-promotion-240505-01.png b/docs/.vuepress/public/images/article/zsxq/zsxq-promotion-240505-01.png new file mode 100644 index 000000000..058b36d0c Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-promotion-240505-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-promotion-240505-02.png b/docs/.vuepress/public/images/article/zsxq/zsxq-promotion-240505-02.png new file mode 100644 index 000000000..4da687153 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-promotion-240505-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-promotion-240505-03.png b/docs/.vuepress/public/images/article/zsxq/zsxq-promotion-240505-03.png new file mode 100644 index 000000000..130ede305 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-promotion-240505-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zsxq-youhuiquan.png b/docs/.vuepress/public/images/article/zsxq/zsxq-youhuiquan.png new file mode 100644 index 000000000..a77a0446c Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zsxq-youhuiquan.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zxsq-240917-01.png b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-01.png new file mode 100644 index 000000000..cb0ffc73f Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-01.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zxsq-240917-02.png b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-02.png new file mode 100644 index 000000000..8629112b5 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-02.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zxsq-240917-03.png b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-03.png new file mode 100644 index 000000000..75cca6a85 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-03.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zxsq-240917-04.png b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-04.png new file mode 100644 index 000000000..97d211b41 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-04.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zxsq-240917-05.png b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-05.png new file mode 100644 index 000000000..d46137941 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-05.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zxsq-240917-06.png b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-06.png new file mode 100644 index 000000000..28330ff55 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-06.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zxsq-240917-07.png b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-07.png new file mode 100644 index 000000000..dc36cbbd6 Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-07.png differ diff --git a/docs/.vuepress/public/images/article/zsxq/zxsq-240917-08.png b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-08.png new file mode 100644 index 000000000..2d1898f8b Binary files /dev/null and b/docs/.vuepress/public/images/article/zsxq/zxsq-240917-08.png differ diff --git a/docs/.vuepress/public/images/personal/wx.png b/docs/.vuepress/public/images/personal/wx.png new file mode 100644 index 000000000..3cbc440c7 Binary files /dev/null and b/docs/.vuepress/public/images/personal/wx.png differ diff --git a/docs/.vuepress/public/images/personal/wx_kefu.png b/docs/.vuepress/public/images/personal/wx_kefu.png new file mode 100644 index 000000000..81b3e7838 Binary files /dev/null and b/docs/.vuepress/public/images/personal/wx_kefu.png differ diff --git a/docs/.vuepress/public/images/personal/wx_little.png b/docs/.vuepress/public/images/personal/wx_little.png new file mode 100644 index 000000000..b98b265c6 Binary files /dev/null and b/docs/.vuepress/public/images/personal/wx_little.png differ diff --git a/docs/.vuepress/public/images/personal/wx_qiye.png b/docs/.vuepress/public/images/personal/wx_qiye.png new file mode 100644 index 000000000..4463c47fd Binary files /dev/null and b/docs/.vuepress/public/images/personal/wx_qiye.png differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/011-\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/1-\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/011-\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/1-\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.png" new file mode 100644 index 000000000..0cd2501f2 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/011-\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/1-\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/images/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/011-\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/2-\346\223\215\344\275\234\347\263\273\347\273\237/images/\346\223\215\344\275\234\347\263\273\347\273\237.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/011-\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/2-\346\223\215\344\275\234\347\263\273\347\273\237/images/\346\223\215\344\275\234\347\263\273\347\273\237.png" new file mode 100644 index 000000000..f75658c67 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/011-\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/2-\346\223\215\344\275\234\347\263\273\347\273\237/images/\346\223\215\344\275\234\347\263\273\347\273\237.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/011-\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/3-\347\274\226\350\257\221\345\216\237\347\220\206/images/\347\274\226\350\257\221\345\216\237\347\220\206.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/011-\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/3-\347\274\226\350\257\221\345\216\237\347\220\206/images/\347\274\226\350\257\221\345\216\237\347\220\206.png" new file mode 100644 index 000000000..961936259 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/011-\350\256\241\347\256\227\346\234\272\345\237\272\347\241\200/3-\347\274\226\350\257\221\345\216\237\347\220\206/images/\347\274\226\350\257\221\345\216\237\347\220\206.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/012-\347\274\226\347\250\213\345\237\272\347\241\200/1-\346\225\260\346\215\256\347\273\223\346\236\204/images/\346\225\260\346\215\256\347\273\223\346\236\204.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/012-\347\274\226\347\250\213\345\237\272\347\241\200/1-\346\225\260\346\215\256\347\273\223\346\236\204/images/\346\225\260\346\215\256\347\273\223\346\236\204.png" new file mode 100644 index 000000000..2862df831 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/012-\347\274\226\347\250\213\345\237\272\347\241\200/1-\346\225\260\346\215\256\347\273\223\346\236\204/images/\346\225\260\346\215\256\347\273\223\346\236\204.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/012-\347\274\226\347\250\213\345\237\272\347\241\200/2-\346\225\260\345\255\246\351\200\273\350\276\221/images/\346\225\260\345\255\246\351\200\273\350\276\221.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/012-\347\274\226\347\250\213\345\237\272\347\241\200/2-\346\225\260\345\255\246\351\200\273\350\276\221/images/\346\225\260\345\255\246\351\200\273\350\276\221.png" new file mode 100644 index 000000000..44e63e7cf Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/012-\347\274\226\347\250\213\345\237\272\347\241\200/2-\346\225\260\345\255\246\351\200\273\350\276\221/images/\346\225\260\345\255\246\351\200\273\350\276\221.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/012-\347\274\226\347\250\213\345\237\272\347\241\200/3-\350\256\276\350\256\241\346\250\241\345\274\217/images/\350\256\276\350\256\241\346\250\241\345\274\217.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/012-\347\274\226\347\250\213\345\237\272\347\241\200/3-\350\256\276\350\256\241\346\250\241\345\274\217/images/\350\256\276\350\256\241\346\250\241\345\274\217.png" new file mode 100644 index 000000000..46b311b91 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/012-\347\274\226\347\250\213\345\237\272\347\241\200/3-\350\256\276\350\256\241\346\250\241\345\274\217/images/\350\256\276\350\256\241\346\250\241\345\274\217.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/013-\345\220\216\347\253\257\350\257\255\350\250\200/1-C\350\257\255\350\250\200/images/c\350\257\255\350\250\200.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/013-\345\220\216\347\253\257\350\257\255\350\250\200/1-C\350\257\255\350\250\200/images/c\350\257\255\350\250\200.png" new file mode 100644 index 000000000..e8a87e708 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/013-\345\220\216\347\253\257\350\257\255\350\250\200/1-C\350\257\255\350\250\200/images/c\350\257\255\350\250\200.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/013-\345\220\216\347\253\257\350\257\255\350\250\200/2-Java/images/java.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/013-\345\220\216\347\253\257\350\257\255\350\250\200/2-Java/images/java.png" new file mode 100644 index 000000000..1749125fe Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/013-\345\220\216\347\253\257\350\257\255\350\250\200/2-Java/images/java.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/1-HTML/images/HTML.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/1-HTML/images/HTML.png" new file mode 100644 index 000000000..255daf5fd Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/1-HTML/images/HTML.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/2-JavaScript/images/JavaScript.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/2-JavaScript/images/JavaScript.png" new file mode 100644 index 000000000..775c3130f Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/2-JavaScript/images/JavaScript.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/3-CSS/images/CSS.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/3-CSS/images/CSS.png" new file mode 100644 index 000000000..a97ad67eb Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/3-CSS/images/CSS.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/4-VUE/images/VUE.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/4-VUE/images/VUE.png" new file mode 100644 index 000000000..baa678b7b Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/4-VUE/images/VUE.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/5-REACT/images/REACT.png" "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/5-REACT/images/REACT.png" new file mode 100644 index 000000000..4afb205ae Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/01-\345\237\272\347\241\200/014-\345\211\215\347\253\257\350\257\255\350\250\200/5-REACT/images/REACT.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/021-PPT/1-\346\236\266\346\236\204\345\233\276/images/\346\236\266\346\236\204\345\233\276.png" "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/021-PPT/1-\346\236\266\346\236\204\345\233\276/images/\346\236\266\346\236\204\345\233\276.png" new file mode 100644 index 000000000..01b8690b4 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/021-PPT/1-\346\236\266\346\236\204\345\233\276/images/\346\236\266\346\236\204\345\233\276.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/1-\347\261\273\345\233\276/images/\347\261\273\345\233\276.png" "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/1-\347\261\273\345\233\276/images/\347\261\273\345\233\276.png" new file mode 100644 index 000000000..4231e09c2 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/1-\347\261\273\345\233\276/images/\347\261\273\345\233\276.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/2-\346\265\201\347\250\213\345\233\276/images/\346\265\201\347\250\213\345\233\276.png" "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/2-\346\265\201\347\250\213\345\233\276/images/\346\265\201\347\250\213\345\233\276.png" new file mode 100644 index 000000000..a3c8b8af7 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/2-\346\265\201\347\250\213\345\233\276/images/\346\265\201\347\250\213\345\233\276.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/3-\346\227\266\345\272\217\345\233\276/images/\346\227\266\345\272\217\345\233\276.png" "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/3-\346\227\266\345\272\217\345\233\276/images/\346\227\266\345\272\217\345\233\276.png" new file mode 100644 index 000000000..bd4845da8 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/3-\346\227\266\345\272\217\345\233\276/images/\346\227\266\345\272\217\345\233\276.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/4-\346\213\223\346\211\221\345\233\276/images/\346\213\223\346\211\221\345\233\276.png" "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/4-\346\213\223\346\211\221\345\233\276/images/\346\213\223\346\211\221\345\233\276.png" new file mode 100644 index 000000000..34c7be7c5 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/022-draw/4-\346\213\223\346\211\221\345\233\276/images/\346\213\223\346\211\221\345\233\276.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/023-Xmind/1-\346\200\235\347\273\264\345\257\274\345\233\276/images/\346\200\235\347\273\264\345\257\274\345\233\276.png" "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/023-Xmind/1-\346\200\235\347\273\264\345\257\274\345\233\276/images/\346\200\235\347\273\264\345\257\274\345\233\276.png" new file mode 100644 index 000000000..4409ea801 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/023-Xmind/1-\346\200\235\347\273\264\345\257\274\345\233\276/images/\346\200\235\347\273\264\345\257\274\345\233\276.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/024-PowerDesigner/1-\346\225\260\346\215\256\345\272\223\345\273\272\346\250\241/images/\346\225\260\346\215\256\345\272\223\345\273\272\346\250\241.png" "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/024-PowerDesigner/1-\346\225\260\346\215\256\345\272\223\345\273\272\346\250\241/images/\346\225\260\346\215\256\345\272\223\345\273\272\346\250\241.png" new file mode 100644 index 000000000..54ac96f81 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/02-\350\256\276\350\256\241/024-PowerDesigner/1-\346\225\260\346\215\256\345\272\223\345\273\272\346\250\241/images/\346\225\260\346\215\256\345\272\223\345\273\272\346\250\241.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/031-\346\236\266\346\236\204\346\250\241\345\274\217/1-\345\215\225\344\275\223\345\272\224\347\224\250/images/\345\215\225\344\275\223\345\272\224\347\224\250.png" "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/031-\346\236\266\346\236\204\346\250\241\345\274\217/1-\345\215\225\344\275\223\345\272\224\347\224\250/images/\345\215\225\344\275\223\345\272\224\347\224\250.png" new file mode 100644 index 000000000..a0eb1a370 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/031-\346\236\266\346\236\204\346\250\241\345\274\217/1-\345\215\225\344\275\223\345\272\224\347\224\250/images/\345\215\225\344\275\223\345\272\224\347\224\250.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/031-\346\236\266\346\236\204\346\250\241\345\274\217/2-\345\276\256\346\234\215\345\212\241/images/\345\276\256\346\234\215\345\212\241.png" "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/031-\346\236\266\346\236\204\346\250\241\345\274\217/2-\345\276\256\346\234\215\345\212\241/images/\345\276\256\346\234\215\345\212\241.png" new file mode 100644 index 000000000..d93537a53 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/031-\346\236\266\346\236\204\346\250\241\345\274\217/2-\345\276\256\346\234\215\345\212\241/images/\345\276\256\346\234\215\345\212\241.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/031-\346\236\266\346\236\204\346\250\241\345\274\217/3-SOA/images/SOA.png" "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/031-\346\236\266\346\236\204\346\250\241\345\274\217/3-SOA/images/SOA.png" new file mode 100644 index 000000000..e4944ce93 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/031-\346\236\266\346\236\204\346\250\241\345\274\217/3-SOA/images/SOA.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/032-\346\236\266\346\236\204\345\210\206\345\261\202/1-MVC/images/MVC.png" "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/032-\346\236\266\346\236\204\345\210\206\345\261\202/1-MVC/images/MVC.png" new file mode 100644 index 000000000..9d4ea324c Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/032-\346\236\266\346\236\204\345\210\206\345\261\202/1-MVC/images/MVC.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/032-\346\236\266\346\236\204\345\210\206\345\261\202/2-DDD/images/DDD.png" "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/032-\346\236\266\346\236\204\345\210\206\345\261\202/2-DDD/images/DDD.png" new file mode 100644 index 000000000..39558a17a Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/032-\346\236\266\346\236\204\345\210\206\345\261\202/2-DDD/images/DDD.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/033-\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\345\216\237\345\210\231/1-\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241/images/\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241.png" "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/033-\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\345\216\237\345\210\231/1-\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241/images/\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241.png" new file mode 100644 index 000000000..a3d51ee5f Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/033-\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\345\216\237\345\210\231/1-\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241/images/\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/033-\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\345\216\237\345\210\231/2-\346\265\213\350\257\225\351\251\261\345\212\250\345\274\200\345\217\221/images/\346\265\213\350\257\225\351\251\261\345\212\250\345\274\200\345\217\221.png" "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/033-\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\345\216\237\345\210\231/2-\346\265\213\350\257\225\351\251\261\345\212\250\345\274\200\345\217\221/images/\346\265\213\350\257\225\351\251\261\345\212\250\345\274\200\345\217\221.png" new file mode 100644 index 000000000..0574a8fd5 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/03-\346\236\266\346\236\204/033-\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\345\216\237\345\210\231/2-\346\265\213\350\257\225\351\251\261\345\212\250\345\274\200\345\217\221/images/\346\265\213\350\257\225\351\251\261\345\212\250\345\274\200\345\217\221.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\344\273\243\347\240\201\346\211\230\347\256\241/1-Github/images/Github.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\344\273\243\347\240\201\346\211\230\347\256\241/1-Github/images/Github.png" new file mode 100644 index 000000000..a9828ea30 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\344\273\243\347\240\201\346\211\230\347\256\241/1-Github/images/Github.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\344\273\243\347\240\201\346\211\230\347\256\241/2-Gitcode/images/Gitcode.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\344\273\243\347\240\201\346\211\230\347\256\241/2-Gitcode/images/Gitcode.png" new file mode 100644 index 000000000..6b16eb53d Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\344\273\243\347\240\201\346\211\230\347\256\241/2-Gitcode/images/Gitcode.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\344\273\243\347\240\201\346\211\230\347\256\241/3-Gitee/images/Gitee.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\344\273\243\347\240\201\346\211\230\347\256\241/3-Gitee/images/Gitee.png" new file mode 100644 index 000000000..3dd233454 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\344\273\243\347\240\201\346\211\230\347\256\241/3-Gitee/images/Gitee.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\347\211\210\346\234\254\346\216\247\345\210\266/1-Git/images/Git.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\347\211\210\346\234\254\346\216\247\345\210\266/1-Git/images/Git.png" new file mode 100644 index 000000000..64e635d47 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\347\211\210\346\234\254\346\216\247\345\210\266/1-Git/images/Git.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\347\211\210\346\234\254\346\216\247\345\210\266/2-SVN/images/SVN.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\347\211\210\346\234\254\346\216\247\345\210\266/2-SVN/images/SVN.png" new file mode 100644 index 000000000..8592354e7 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/041-\347\211\210\346\234\254\346\216\247\345\210\266/2-SVN/images/SVN.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/042-\345\274\200\345\217\221\345\267\245\345\205\267/1-IntelliJ IDEA/images/IDEA.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/042-\345\274\200\345\217\221\345\267\245\345\205\267/1-IntelliJ IDEA/images/IDEA.png" new file mode 100644 index 000000000..c75144069 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/042-\345\274\200\345\217\221\345\267\245\345\205\267/1-IntelliJ IDEA/images/IDEA.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/042-\345\274\200\345\217\221\345\267\245\345\205\267/2-JetBrains Fleet/images/Fleet.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/042-\345\274\200\345\217\221\345\267\245\345\205\267/2-JetBrains Fleet/images/Fleet.png" new file mode 100644 index 000000000..9b91f0fdb Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/042-\345\274\200\345\217\221\345\267\245\345\205\267/2-JetBrains Fleet/images/Fleet.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/042-\345\274\200\345\217\221\345\267\245\345\205\267/3-Navicat/images/Navicat.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/042-\345\274\200\345\217\221\345\267\245\345\205\267/3-Navicat/images/Navicat.png" new file mode 100644 index 000000000..3eb33d96a Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/042-\345\274\200\345\217\221\345\267\245\345\205\267/3-Navicat/images/Navicat.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/043-\344\276\235\350\265\226\347\256\241\347\220\206/1-Maven/images/Maven.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/043-\344\276\235\350\265\226\347\256\241\347\220\206/1-Maven/images/Maven.png" new file mode 100644 index 000000000..6e31920e3 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/043-\344\276\235\350\265\226\347\256\241\347\220\206/1-Maven/images/Maven.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/043-\344\276\235\350\265\226\347\256\241\347\220\206/2-Gradle/images/Gradle.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/043-\344\276\235\350\265\226\347\256\241\347\220\206/2-Gradle/images/Gradle.png" new file mode 100644 index 000000000..43cbaa18d Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/043-\344\276\235\350\265\226\347\256\241\347\220\206/2-Gradle/images/Gradle.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/044-\350\204\232\346\211\213\346\236\266/1-Spring initializr/images/Initializr.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/044-\350\204\232\346\211\213\346\236\266/1-Spring initializr/images/Initializr.png" new file mode 100644 index 000000000..6399da869 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/044-\350\204\232\346\211\213\346\236\266/1-Spring initializr/images/Initializr.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/044-\350\204\232\346\211\213\346\236\266/2-Aliyun Java initializr/images/Initializr.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/044-\350\204\232\346\211\213\346\236\266/2-Aliyun Java initializr/images/Initializr.png" new file mode 100644 index 000000000..6399da869 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/044-\350\204\232\346\211\213\346\236\266/2-Aliyun Java initializr/images/Initializr.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/044-\350\204\232\346\211\213\346\236\266/3-IDEA Plugin Project/images/Initializr.png" "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/044-\350\204\232\346\211\213\346\236\266/3-IDEA Plugin Project/images/Initializr.png" new file mode 100644 index 000000000..6399da869 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/04-\347\216\257\345\242\203/044-\350\204\232\346\211\213\346\236\266/3-IDEA Plugin Project/images/Initializr.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/051-\346\241\206\346\236\266/1-Spring/images/Spring.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/051-\346\241\206\346\236\266/1-Spring/images/Spring.png" new file mode 100644 index 000000000..2c4fed3aa Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/051-\346\241\206\346\236\266/1-Spring/images/Spring.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/051-\346\241\206\346\236\266/2-SpringBoot/images/SpringBoot.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/051-\346\241\206\346\236\266/2-SpringBoot/images/SpringBoot.png" new file mode 100644 index 000000000..2c4fed3aa Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/051-\346\241\206\346\236\266/2-SpringBoot/images/SpringBoot.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/051-\346\241\206\346\236\266/3-MyBatis/images/MyBatis.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/051-\346\241\206\346\236\266/3-MyBatis/images/MyBatis.png" new file mode 100644 index 000000000..9f0afdc89 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/051-\346\241\206\346\236\266/3-MyBatis/images/MyBatis.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0510-NoSQL\346\225\260\346\215\256\345\272\223/1-Redis/images/Redis.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0510-NoSQL\346\225\260\346\215\256\345\272\223/1-Redis/images/Redis.png" new file mode 100644 index 000000000..31907c690 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0510-NoSQL\346\225\260\346\215\256\345\272\223/1-Redis/images/Redis.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0510-NoSQL\346\225\260\346\215\256\345\272\223/2-Memcached/images/Memcached.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0510-NoSQL\346\225\260\346\215\256\345\272\223/2-Memcached/images/Memcached.png" new file mode 100644 index 000000000..345bfb9f1 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0510-NoSQL\346\225\260\346\215\256\345\272\223/2-Memcached/images/Memcached.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0511-\345\233\276\345\275\242\346\225\260\346\215\256\345\272\223/1-Neo4j/images/Neo4j.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0511-\345\233\276\345\275\242\346\225\260\346\215\256\345\272\223/1-Neo4j/images/Neo4j.png" new file mode 100644 index 000000000..89f78e446 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0511-\345\233\276\345\275\242\346\225\260\346\215\256\345\272\223/1-Neo4j/images/Neo4j.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0512-\346\226\207\344\273\266\345\255\230\345\202\250/1-Hbase/images/Hbase.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0512-\346\226\207\344\273\266\345\255\230\345\202\250/1-Hbase/images/Hbase.png" new file mode 100644 index 000000000..612c3241a Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0512-\346\226\207\344\273\266\345\255\230\345\202\250/1-Hbase/images/Hbase.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0512-\346\226\207\344\273\266\345\255\230\345\202\250/2-Hadoop/images/Hadoop.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0512-\346\226\207\344\273\266\345\255\230\345\202\250/2-Hadoop/images/Hadoop.png" new file mode 100644 index 000000000..06cdfa539 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0512-\346\226\207\344\273\266\345\255\230\345\202\250/2-Hadoop/images/Hadoop.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0512-\346\226\207\344\273\266\345\255\230\345\202\250/3-MongoDB/images/MongoDB.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0512-\346\226\207\344\273\266\345\255\230\345\202\250/3-MongoDB/images/MongoDB.png" new file mode 100644 index 000000000..6d4a97e4b Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0512-\346\226\207\344\273\266\345\255\230\345\202\250/3-MongoDB/images/MongoDB.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0513-\346\220\234\347\264\242\345\274\225\346\223\216/1-ElasticSearch/images/Elasticsearch.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0513-\346\220\234\347\264\242\345\274\225\346\223\216/1-ElasticSearch/images/Elasticsearch.png" new file mode 100644 index 000000000..09aedf416 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0513-\346\220\234\347\264\242\345\274\225\346\223\216/1-ElasticSearch/images/Elasticsearch.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0513-\346\220\234\347\264\242\345\274\225\346\223\216/2-Solr/images/Solr.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0513-\346\220\234\347\264\242\345\274\225\346\223\216/2-Solr/images/Solr.png" new file mode 100644 index 000000000..2b17e6ba7 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0513-\346\220\234\347\264\242\345\274\225\346\223\216/2-Solr/images/Solr.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/1-Flink/images/Flink.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/1-Flink/images/Flink.png" new file mode 100644 index 000000000..9af7b8a33 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/1-Flink/images/Flink.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/2-Storm/images/Storm.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/2-Storm/images/Storm.png" new file mode 100644 index 000000000..ecf00f561 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/2-Storm/images/Storm.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/3-Spark/images/Spark.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/3-Spark/images/Spark.png" new file mode 100644 index 000000000..6b5f340f6 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/3-Spark/images/Spark.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/4-Hive/images/Hive.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/4-Hive/images/Hive.png" new file mode 100644 index 000000000..242c7c256 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0514-\345\244\247\346\225\260\346\215\256/4-Hive/images/Hive.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0515-\346\225\260\346\215\256\345\220\214\346\255\245/1-Otter/Otter.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0515-\346\225\260\346\215\256\345\220\214\346\255\245/1-Otter/Otter.png" new file mode 100644 index 000000000..7311881a7 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0515-\346\225\260\346\215\256\345\220\214\346\255\245/1-Otter/Otter.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0515-\346\225\260\346\215\256\345\220\214\346\255\245/2-Canal/Canal.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0515-\346\225\260\346\215\256\345\220\214\346\255\245/2-Canal/Canal.png" new file mode 100644 index 000000000..2121eb5da Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/0515-\346\225\260\346\215\256\345\220\214\346\255\245/2-Canal/Canal.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/052-RPC/1-Dubbo/images/Dubbo.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/052-RPC/1-Dubbo/images/Dubbo.png" new file mode 100644 index 000000000..f564a1ff8 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/052-RPC/1-Dubbo/images/Dubbo.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/052-RPC/2-GRPC/images/GRPC.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/052-RPC/2-GRPC/images/GRPC.png" new file mode 100644 index 000000000..f3950e0d9 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/052-RPC/2-GRPC/images/GRPC.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/1-Kafka/images/Kafka.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/1-Kafka/images/Kafka.png" new file mode 100644 index 000000000..2a04faf66 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/1-Kafka/images/Kafka.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/2-RabbitMQ/images/RabbitMQ.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/2-RabbitMQ/images/RabbitMQ.png" new file mode 100644 index 000000000..005de52e4 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/2-RabbitMQ/images/RabbitMQ.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/3-RocketMQ/images/RocketMQ.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/3-RocketMQ/images/RocketMQ.png" new file mode 100644 index 000000000..4c957a043 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/3-RocketMQ/images/RocketMQ.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/4-ActiveMQ/images/ActiveMQ.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/4-ActiveMQ/images/ActiveMQ.png" new file mode 100644 index 000000000..b39d1734c Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/053-MQ/4-ActiveMQ/images/ActiveMQ.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/054-\344\273\273\345\212\241\350\260\203\345\272\246/1-xxl-job/images/xxl-job.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/054-\344\273\273\345\212\241\350\260\203\345\272\246/1-xxl-job/images/xxl-job.png" new file mode 100644 index 000000000..9710f74c7 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/054-\344\273\273\345\212\241\350\260\203\345\272\246/1-xxl-job/images/xxl-job.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/054-\344\273\273\345\212\241\350\260\203\345\272\246/2-elastic-job/images/elastic-job.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/054-\344\273\273\345\212\241\350\260\203\345\272\246/2-elastic-job/images/elastic-job.png" new file mode 100644 index 000000000..2fded09b6 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/054-\344\273\273\345\212\241\350\260\203\345\272\246/2-elastic-job/images/elastic-job.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/054-\344\273\273\345\212\241\350\260\203\345\272\246/3-quartz/images/quartz.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/054-\344\273\273\345\212\241\350\260\203\345\272\246/3-quartz/images/quartz.png" new file mode 100644 index 000000000..2fded09b6 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/054-\344\273\273\345\212\241\350\260\203\345\272\246/3-quartz/images/quartz.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/055-\346\263\250\345\206\214\344\270\255\345\277\203/1-ZooKeeper/images/Zookeeper.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/055-\346\263\250\345\206\214\344\270\255\345\277\203/1-ZooKeeper/images/Zookeeper.png" new file mode 100644 index 000000000..8841e5314 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/055-\346\263\250\345\206\214\344\270\255\345\277\203/1-ZooKeeper/images/Zookeeper.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/055-\346\263\250\345\206\214\344\270\255\345\277\203/2-Eureka/images/Eureka.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/055-\346\263\250\345\206\214\344\270\255\345\277\203/2-Eureka/images/Eureka.png" new file mode 100644 index 000000000..ac4928450 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/055-\346\263\250\345\206\214\344\270\255\345\277\203/2-Eureka/images/Eureka.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/055-\346\263\250\345\206\214\344\270\255\345\277\203/3-Nacos/images/Nacos.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/055-\346\263\250\345\206\214\344\270\255\345\277\203/3-Nacos/images/Nacos.png" new file mode 100644 index 000000000..5f1a90801 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/055-\346\263\250\345\206\214\344\270\255\345\277\203/3-Nacos/images/Nacos.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/056-\346\225\260\346\215\256\346\272\220/1-DBCP/images/DBCP.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/056-\346\225\260\346\215\256\346\272\220/1-DBCP/images/DBCP.png" new file mode 100644 index 000000000..b8dc1b21b Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/056-\346\225\260\346\215\256\346\272\220/1-DBCP/images/DBCP.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/056-\346\225\260\346\215\256\346\272\220/2-C3P0/images/C3P0.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/056-\346\225\260\346\215\256\346\272\220/2-C3P0/images/C3P0.png" new file mode 100644 index 000000000..b8dc1b21b Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/056-\346\225\260\346\215\256\346\272\220/2-C3P0/images/C3P0.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/056-\346\225\260\346\215\256\346\272\220/3-Druid/images/Druid.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/056-\346\225\260\346\215\256\346\272\220/3-Druid/images/Druid.png" new file mode 100644 index 000000000..b8dc1b21b Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/056-\346\225\260\346\215\256\346\272\220/3-Druid/images/Druid.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/057-\345\210\206\345\272\223\345\210\206\350\241\250/1-MyCat/images/MyCat.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/057-\345\210\206\345\272\223\345\210\206\350\241\250/1-MyCat/images/MyCat.png" new file mode 100644 index 000000000..f2efcf1c1 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/057-\345\210\206\345\272\223\345\210\206\350\241\250/1-MyCat/images/MyCat.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/057-\345\210\206\345\272\223\345\210\206\350\241\250/2-Sharding-Jdbc/images/Sharding-Jdbc.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/057-\345\210\206\345\272\223\345\210\206\350\241\250/2-Sharding-Jdbc/images/Sharding-Jdbc.png" new file mode 100644 index 000000000..0b7d99286 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/057-\345\210\206\345\272\223\345\210\206\350\241\250/2-Sharding-Jdbc/images/Sharding-Jdbc.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/1-Spring Cloud Gateway/images/Spring.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/1-Spring Cloud Gateway/images/Spring.png" new file mode 100644 index 000000000..7d99f543d Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/1-Spring Cloud Gateway/images/Spring.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/2-Soul/images/Soul.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/2-Soul/images/Soul.png" new file mode 100644 index 000000000..173437197 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/2-Soul/images/Soul.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/3-Zuul/images/Zuul.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/3-Zuul/images/Zuul.png" new file mode 100644 index 000000000..547dc4afa Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/3-Zuul/images/Zuul.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/4-OpenResty/images/OpenResty.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/4-OpenResty/images/OpenResty.png" new file mode 100644 index 000000000..a68f8ec09 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/058-\347\275\221\345\205\263/4-OpenResty/images/OpenResty.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/059-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223/1-MySql/images/MySql.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/059-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223/1-MySql/images/MySql.png" new file mode 100644 index 000000000..e13a2bbd2 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/059-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223/1-MySql/images/MySql.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/059-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223/2-Oracle/images/Oracle.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/059-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223/2-Oracle/images/Oracle.png" new file mode 100644 index 000000000..7038ae576 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/059-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223/2-Oracle/images/Oracle.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/059-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223/3-TiDB/images/TIDB.png" "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/059-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223/3-TiDB/images/TIDB.png" new file mode 100644 index 000000000..291c1daef Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/05-\345\274\200\345\217\221/059-\345\205\263\347\263\273\345\236\213\346\225\260\346\215\256\345\272\223/3-TiDB/images/TIDB.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/061-\345\272\217\345\210\227\345\214\226/1-fastjson/images/fastjson.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/061-\345\272\217\345\210\227\345\214\226/1-fastjson/images/fastjson.png" new file mode 100644 index 000000000..4322425da Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/061-\345\272\217\345\210\227\345\214\226/1-fastjson/images/fastjson.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/061-\345\272\217\345\210\227\345\214\226/2-jackson/images/jackson.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/061-\345\272\217\345\210\227\345\214\226/2-jackson/images/jackson.png" new file mode 100644 index 000000000..4322425da Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/061-\345\272\217\345\210\227\345\214\226/2-jackson/images/jackson.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/061-\345\272\217\345\210\227\345\214\226/3-Gson/images/Gson.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/061-\345\272\217\345\210\227\345\214\226/3-Gson/images/Gson.png" new file mode 100644 index 000000000..4322425da Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/061-\345\272\217\345\210\227\345\214\226/3-Gson/images/Gson.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0610-Redis/1-Redisson/images/Redisson.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0610-Redis/1-Redisson/images/Redisson.png" new file mode 100644 index 000000000..0cb3a4d86 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0610-Redis/1-Redisson/images/Redisson.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0610-Redis/2-RedisTemplate/images/RedisTemplate.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0610-Redis/2-RedisTemplate/images/RedisTemplate.png" new file mode 100644 index 000000000..0cb3a4d86 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0610-Redis/2-RedisTemplate/images/RedisTemplate.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0610-Redis/3-Jedis/images/Jedis.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0610-Redis/3-Jedis/images/Jedis.png" new file mode 100644 index 000000000..0cb3a4d86 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0610-Redis/3-Jedis/images/Jedis.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0611-\345\205\266\344\273\226/1-Guava/images/Guava.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0611-\345\205\266\344\273\226/1-Guava/images/Guava.png" new file mode 100644 index 000000000..661a17cc5 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0611-\345\205\266\344\273\226/1-Guava/images/Guava.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0611-\345\205\266\344\273\226/2-Hutool/images/Hutool.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0611-\345\205\266\344\273\226/2-Hutool/images/Hutool.png" new file mode 100644 index 000000000..661a17cc5 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0611-\345\205\266\344\273\226/2-Hutool/images/Hutool.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0611-\345\205\266\344\273\226/3-TTL/images/TTL.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0611-\345\205\266\344\273\226/3-TTL/images/TTL.png" new file mode 100644 index 000000000..da429a683 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/0611-\345\205\266\344\273\226/3-TTL/images/TTL.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/062-\350\256\244\350\257\201\346\216\210\346\235\203/1-shiro/images/shiro.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/062-\350\256\244\350\257\201\346\216\210\346\235\203/1-shiro/images/shiro.png" new file mode 100644 index 000000000..2dc11d705 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/062-\350\256\244\350\257\201\346\216\210\346\235\203/1-shiro/images/shiro.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/062-\350\256\244\350\257\201\346\216\210\346\235\203/2-jwt/images/jwt.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/062-\350\256\244\350\257\201\346\216\210\346\235\203/2-jwt/images/jwt.png" new file mode 100644 index 000000000..2dc11d705 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/062-\350\256\244\350\257\201\346\216\210\346\235\203/2-jwt/images/jwt.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/062-\350\256\244\350\257\201\346\216\210\346\235\203/3-sso/images/sso.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/062-\350\256\244\350\257\201\346\216\210\346\235\203/3-sso/images/sso.png" new file mode 100644 index 000000000..dd1867361 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/062-\350\256\244\350\257\201\346\216\210\346\235\203/3-sso/images/sso.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/063-\345\255\227\350\212\202\347\240\201\346\241\206\346\236\266/1-ASM/images/ASM.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/063-\345\255\227\350\212\202\347\240\201\346\241\206\346\236\266/1-ASM/images/ASM.png" new file mode 100644 index 000000000..961936259 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/063-\345\255\227\350\212\202\347\240\201\346\241\206\346\236\266/1-ASM/images/ASM.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/063-\345\255\227\350\212\202\347\240\201\346\241\206\346\236\266/2-Javassist/images/Javassist.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/063-\345\255\227\350\212\202\347\240\201\346\241\206\346\236\266/2-Javassist/images/Javassist.png" new file mode 100644 index 000000000..961936259 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/063-\345\255\227\350\212\202\347\240\201\346\241\206\346\236\266/2-Javassist/images/Javassist.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/063-\345\255\227\350\212\202\347\240\201\346\241\206\346\236\266/3-Byte-Buddy/images/Byte-Buddy.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/063-\345\255\227\350\212\202\347\240\201\346\241\206\346\236\266/3-Byte-Buddy/images/Byte-Buddy.png" new file mode 100644 index 000000000..961936259 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/063-\345\255\227\350\212\202\347\240\201\346\241\206\346\236\266/3-Byte-Buddy/images/Byte-Buddy.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/064-\347\275\221\347\273\234\347\274\226\347\250\213/1-Netty/images/Netty.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/064-\347\275\221\347\273\234\347\274\226\347\250\213/1-Netty/images/Netty.png" new file mode 100644 index 000000000..9a87ae477 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/064-\347\275\221\347\273\234\347\274\226\347\250\213/1-Netty/images/Netty.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/064-\347\275\221\347\273\234\347\274\226\347\250\213/2-Mina/images/Mina.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/064-\347\275\221\347\273\234\347\274\226\347\250\213/2-Mina/images/Mina.png" new file mode 100644 index 000000000..9a87ae477 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/064-\347\275\221\347\273\234\347\274\226\347\250\213/2-Mina/images/Mina.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/064-\347\275\221\347\273\234\347\274\226\347\250\213/3-WebFlux/images/WebFlux.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/064-\347\275\221\347\273\234\347\274\226\347\250\213/3-WebFlux/images/WebFlux.png" new file mode 100644 index 000000000..b3d08c213 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/064-\347\275\221\347\273\234\347\274\226\347\250\213/3-WebFlux/images/WebFlux.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/065-\345\267\245\344\275\234\346\265\201/1-BPMN/images/BPMN.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/065-\345\267\245\344\275\234\346\265\201/1-BPMN/images/BPMN.png" new file mode 100644 index 000000000..4654e4e28 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/065-\345\267\245\344\275\234\346\265\201/1-BPMN/images/BPMN.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/065-\345\267\245\344\275\234\346\265\201/2-Activiti7/images/Activiti.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/065-\345\267\245\344\275\234\346\265\201/2-Activiti7/images/Activiti.png" new file mode 100644 index 000000000..f2f676605 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/065-\345\267\245\344\275\234\346\265\201/2-Activiti7/images/Activiti.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/066-\346\227\245\345\277\227/1-log4j/images/log4j.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/066-\346\227\245\345\277\227/1-log4j/images/log4j.png" new file mode 100644 index 000000000..0dbb8ddb0 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/066-\346\227\245\345\277\227/1-log4j/images/log4j.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/066-\346\227\245\345\277\227/2-logback/images/logback.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/066-\346\227\245\345\277\227/2-logback/images/logback.png" new file mode 100644 index 000000000..0dbb8ddb0 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/066-\346\227\245\345\277\227/2-logback/images/logback.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/066-\346\227\245\345\277\227/3-slf4j/images/slf4j.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/066-\346\227\245\345\277\227/3-slf4j/images/slf4j.png" new file mode 100644 index 000000000..0dbb8ddb0 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/066-\346\227\245\345\277\227/3-slf4j/images/slf4j.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/067-\346\234\215\345\212\241\346\262\273\347\220\206/1-Hystrix/images/Hystrix.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/067-\346\234\215\345\212\241\346\262\273\347\220\206/1-Hystrix/images/Hystrix.png" new file mode 100644 index 000000000..08638223f Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/067-\346\234\215\345\212\241\346\262\273\347\220\206/1-Hystrix/images/Hystrix.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/067-\346\234\215\345\212\241\346\262\273\347\220\206/2-Sentinel/images/Sentinel.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/067-\346\234\215\345\212\241\346\262\273\347\220\206/2-Sentinel/images/Sentinel.png" new file mode 100644 index 000000000..dd213c043 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/067-\346\234\215\345\212\241\346\262\273\347\220\206/2-Sentinel/images/Sentinel.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/067-\346\234\215\345\212\241\346\262\273\347\220\206/3-RateLimiter/images/RateLimiter.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/067-\346\234\215\345\212\241\346\262\273\347\220\206/3-RateLimiter/images/RateLimiter.png" new file mode 100644 index 000000000..a051193ef Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/067-\346\234\215\345\212\241\346\262\273\347\220\206/3-RateLimiter/images/RateLimiter.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/068-HTTP\346\241\206\346\236\266/1-HttpClient/images/HttpClient.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/068-HTTP\346\241\206\346\236\266/1-HttpClient/images/HttpClient.png" new file mode 100644 index 000000000..725009639 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/068-HTTP\346\241\206\346\236\266/1-HttpClient/images/HttpClient.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/068-HTTP\346\241\206\346\236\266/2-RestTemplate/images/RestTemplate.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/068-HTTP\346\241\206\346\236\266/2-RestTemplate/images/RestTemplate.png" new file mode 100644 index 000000000..725009639 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/068-HTTP\346\241\206\346\236\266/2-RestTemplate/images/RestTemplate.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/069-\350\247\204\345\210\231\345\274\225\346\223\216/1-Drools/images/Drools.png" "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/069-\350\247\204\345\210\231\345\274\225\346\223\216/1-Drools/images/Drools.png" new file mode 100644 index 000000000..f2f676605 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/06-\347\261\273\345\272\223/069-\350\247\204\345\210\231\345\274\225\346\223\216/1-Drools/images/Drools.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/071-Mock/1-Mockito/images/Mock.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/071-Mock/1-Mockito/images/Mock.png" new file mode 100644 index 000000000..d21c62df2 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/071-Mock/1-Mockito/images/Mock.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/1-Postman/images/Postman.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/1-Postman/images/Postman.png" new file mode 100644 index 000000000..11a40b18f Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/1-Postman/images/Postman.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/2-ApiFox/images/ApiFox.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/2-ApiFox/images/ApiFox.png" new file mode 100644 index 000000000..11a40b18f Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/2-ApiFox/images/ApiFox.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/3-ApiPost/images/ApiPost.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/3-ApiPost/images/ApiPost.png" new file mode 100644 index 000000000..11a40b18f Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/3-ApiPost/images/ApiPost.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/4-FastRequest/images/FastRequest.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/4-FastRequest/images/FastRequest.png" new file mode 100644 index 000000000..00aefd6b4 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/072-\350\260\203\350\257\225\345\267\245\345\205\267/4-FastRequest/images/FastRequest.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/1-OpenApi/OpenApi.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/1-OpenApi/OpenApi.png" new file mode 100644 index 000000000..12fbbf18d Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/1-OpenApi/OpenApi.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/2-Swagger/Swagger.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/2-Swagger/Swagger.png" new file mode 100644 index 000000000..3489012a7 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/2-Swagger/Swagger.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/3-Javadoc/Javadoc.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/3-Javadoc/Javadoc.png" new file mode 100644 index 000000000..c60664d9a Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/3-Javadoc/Javadoc.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/4-Yapi/Yapi.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/4-Yapi/Yapi.png" new file mode 100644 index 000000000..87a3d45a8 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/4-Yapi/Yapi.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/5-Torna/Torna.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/5-Torna/Torna.png" new file mode 100644 index 000000000..6e11db758 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/5-Torna/Torna.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/6-smart-doc/smart-doc.png" "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/6-smart-doc/smart-doc.png" new file mode 100644 index 000000000..856caf552 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/07-\350\260\203\350\257\225/073-API\347\256\241\347\220\206/6-smart-doc/smart-doc.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/08-\346\265\213\350\257\225/081-\345\215\225\345\205\203\346\265\213\350\257\225/1-JUnit/images/JUnit.png" "b/docs/.vuepress/public/images/roadmap/08-\346\265\213\350\257\225/081-\345\215\225\345\205\203\346\265\213\350\257\225/1-JUnit/images/JUnit.png" new file mode 100644 index 000000000..4c4c98079 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/08-\346\265\213\350\257\225/081-\345\215\225\345\205\203\346\265\213\350\257\225/1-JUnit/images/JUnit.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/08-\346\265\213\350\257\225/081-\345\215\225\345\205\203\346\265\213\350\257\225/2-TestNG/images/TestNG.png" "b/docs/.vuepress/public/images/roadmap/08-\346\265\213\350\257\225/081-\345\215\225\345\205\203\346\265\213\350\257\225/2-TestNG/images/TestNG.png" new file mode 100644 index 000000000..4c4c98079 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/08-\346\265\213\350\257\225/081-\345\215\225\345\205\203\346\265\213\350\257\225/2-TestNG/images/TestNG.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/08-\346\265\213\350\257\225/082-\346\200\247\350\203\275\346\265\213\350\257\225/1-Jemeter/images/Jemeter.png" "b/docs/.vuepress/public/images/roadmap/08-\346\265\213\350\257\225/082-\346\200\247\350\203\275\346\265\213\350\257\225/1-Jemeter/images/Jemeter.png" new file mode 100644 index 000000000..bbcf941ad Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/08-\346\265\213\350\257\225/082-\346\200\247\350\203\275\346\265\213\350\257\225/1-Jemeter/images/Jemeter.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/091-\344\273\243\347\240\201\345\256\241\346\237\245/1-github/images/github.png" "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/091-\344\273\243\347\240\201\345\256\241\346\237\245/1-github/images/github.png" new file mode 100644 index 000000000..a9828ea30 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/091-\344\273\243\347\240\201\345\256\241\346\237\245/1-github/images/github.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/092-\344\273\243\347\240\201\350\247\204\350\214\203/1-p3c/images/p3c.png" "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/092-\344\273\243\347\240\201\350\247\204\350\214\203/1-p3c/images/p3c.png" new file mode 100644 index 000000000..bc19952d9 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/092-\344\273\243\347\240\201\350\247\204\350\214\203/1-p3c/images/p3c.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/092-\344\273\243\347\240\201\350\247\204\350\214\203/1-pmd-idea/images/pmd-idea.png" "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/092-\344\273\243\347\240\201\350\247\204\350\214\203/1-pmd-idea/images/pmd-idea.png" new file mode 100644 index 000000000..bc19952d9 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/092-\344\273\243\347\240\201\350\247\204\350\214\203/1-pmd-idea/images/pmd-idea.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/093-\350\264\250\351\207\217\346\243\200\346\265\213/1-SonarQube/images/SonarQube.png" "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/093-\350\264\250\351\207\217\346\243\200\346\265\213/1-SonarQube/images/SonarQube.png" new file mode 100644 index 000000000..c6d9f3881 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/093-\350\264\250\351\207\217\346\243\200\346\265\213/1-SonarQube/images/SonarQube.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/1-Dapper/images/Dapper.png" "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/1-Dapper/images/Dapper.png" new file mode 100644 index 000000000..6bb356cd6 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/1-Dapper/images/Dapper.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/2-Pinpoint/images/Pinpoint.png" "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/2-Pinpoint/images/Pinpoint.png" new file mode 100644 index 000000000..6bb356cd6 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/2-Pinpoint/images/Pinpoint.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/3-Prometheus/images/Prometheus.png" "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/3-Prometheus/images/Prometheus.png" new file mode 100644 index 000000000..6bb356cd6 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/3-Prometheus/images/Prometheus.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/4-SkyWalking/images/SkyWalking.png" "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/4-SkyWalking/images/SkyWalking.png" new file mode 100644 index 000000000..6bb356cd6 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/4-SkyWalking/images/SkyWalking.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/5-BeeAPM/images/BeeAPM.png" "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/5-BeeAPM/images/BeeAPM.png" new file mode 100644 index 000000000..6bb356cd6 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/09-\350\264\250\351\207\217\345\210\206\346\236\220/094-\347\233\221\346\216\247\347\263\273\347\273\237/5-BeeAPM/images/BeeAPM.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/101-CICD/1-jenkins/images/jenkins.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/101-CICD/1-jenkins/images/jenkins.png" new file mode 100644 index 000000000..adefd070f Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/101-CICD/1-jenkins/images/jenkins.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/102-\345\217\221\345\270\203/1-War/images/War.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/102-\345\217\221\345\270\203/1-War/images/War.png" new file mode 100644 index 000000000..fe28057ff Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/102-\345\217\221\345\270\203/1-War/images/War.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/102-\345\217\221\345\270\203/2-Jar/images/Jar.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/102-\345\217\221\345\270\203/2-Jar/images/Jar.png" new file mode 100644 index 000000000..35de8d4e1 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/102-\345\217\221\345\270\203/2-Jar/images/Jar.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/103-\346\234\215\345\212\241\345\231\250/1-Nginx/images/Nginx.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/103-\346\234\215\345\212\241\345\231\250/1-Nginx/images/Nginx.png" new file mode 100644 index 000000000..e1aed3160 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/103-\346\234\215\345\212\241\345\231\250/1-Nginx/images/Nginx.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/103-\346\234\215\345\212\241\345\231\250/2-Apache/images/Apache.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/103-\346\234\215\345\212\241\345\231\250/2-Apache/images/Apache.png" new file mode 100644 index 000000000..86d41d915 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/103-\346\234\215\345\212\241\345\231\250/2-Apache/images/Apache.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/103-\346\234\215\345\212\241\345\231\250/3-Tomcat/images/Tomcat.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/103-\346\234\215\345\212\241\345\231\250/3-Tomcat/images/Tomcat.png" new file mode 100644 index 000000000..9a352eeb4 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/103-\346\234\215\345\212\241\345\231\250/3-Tomcat/images/Tomcat.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/104-\345\256\271\345\231\250\345\214\226/1-Docker/images/Docker.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/104-\345\256\271\345\231\250\345\214\226/1-Docker/images/Docker.png" new file mode 100644 index 000000000..b3c033a3b Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/104-\345\256\271\345\231\250\345\214\226/1-Docker/images/Docker.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/104-\345\256\271\345\231\250\345\214\226/2-K8S/images/K8S.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/104-\345\256\271\345\231\250\345\214\226/2-K8S/images/K8S.png" new file mode 100644 index 000000000..e8551a256 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/104-\345\256\271\345\231\250\345\214\226/2-K8S/images/K8S.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/105-\346\234\215\345\212\241\345\214\226/1-PaaS/images/PaaS.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/105-\346\234\215\345\212\241\345\214\226/1-PaaS/images/PaaS.png" new file mode 100644 index 000000000..4b6514769 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/105-\346\234\215\345\212\241\345\214\226/1-PaaS/images/PaaS.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/105-\346\234\215\345\212\241\345\214\226/2-SaaS/images/SaaS.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/105-\346\234\215\345\212\241\345\214\226/2-SaaS/images/SaaS.png" new file mode 100644 index 000000000..b919d4796 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/105-\346\234\215\345\212\241\345\214\226/2-SaaS/images/SaaS.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/105-\346\234\215\345\212\241\345\214\226/3-IaaS/images/IaaS.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/105-\346\234\215\345\212\241\345\214\226/3-IaaS/images/IaaS.png" new file mode 100644 index 000000000..65a2b8ff4 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/105-\346\234\215\345\212\241\345\214\226/3-IaaS/images/IaaS.png" differ diff --git "a/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/106-\344\272\221\345\216\237\347\224\237/1-Istio/images/Istio.png" "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/106-\344\272\221\345\216\237\347\224\237/1-Istio/images/Istio.png" new file mode 100644 index 000000000..3db4d3969 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/10-\345\217\221\345\270\203\351\203\250\347\275\262/106-\344\272\221\345\216\237\347\224\237/1-Istio/images/Istio.png" differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/Xnip2025-12-28_10-47-09.png b/docs/.vuepress/public/images/roadmap/tutorial/Xnip2025-12-28_10-47-09.png new file mode 100644 index 000000000..0d7f9601a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/Xnip2025-12-28_10-47-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/a-concise-learning-route.png b/docs/.vuepress/public/images/roadmap/tutorial/a-concise-learning-route.png new file mode 100644 index 000000000..dd8b7cf56 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/a-concise-learning-route.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-01.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-01.png new file mode 100755 index 000000000..92f71a7f5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-02.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-02.png new file mode 100755 index 000000000..e41ca3b19 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-03.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-03.png new file mode 100755 index 000000000..d1310ed8e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-04.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-04.png new file mode 100755 index 000000000..e795475fe Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-01-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-02-01.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-02-01.png new file mode 100755 index 000000000..ed7730884 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-02-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-00.gif b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-00.gif new file mode 100755 index 000000000..dd30fc5c6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-00.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-01.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-01.png new file mode 100755 index 000000000..37bc5613a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-02.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-02.png new file mode 100755 index 000000000..792d1b84c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-03.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-03.png new file mode 100755 index 000000000..471420a7a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-04.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-04.png new file mode 100755 index 000000000..02d611e8d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-05.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-05.png new file mode 100755 index 000000000..211ba00a7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-06.png b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-06.png new file mode 100755 index 000000000..2f0a36dc9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/ddd-easy-guide-03-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/draw.io-01.png b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-01.png new file mode 100644 index 000000000..c1fe9f54d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/draw.io-02.png b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-02.png new file mode 100644 index 000000000..bc28c2f0c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/draw.io-03.png b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-03.png new file mode 100644 index 000000000..35929acdb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/draw.io-04.png b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-04.png new file mode 100644 index 000000000..7549c9265 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/draw.io-05.png b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-05.png new file mode 100644 index 000000000..7f591702a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/draw.io-06.png b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-06.png new file mode 100644 index 000000000..60d69da8b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/draw.io-07.png b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-07.png new file mode 100644 index 000000000..808ca7cf5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/draw.io-08.png b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-08.png new file mode 100644 index 000000000..fd5659f43 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/draw.io-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/img.png b/docs/.vuepress/public/images/roadmap/tutorial/img.png new file mode 100644 index 000000000..7a61a114e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/img.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/img_1.png b/docs/.vuepress/public/images/roadmap/tutorial/img_1.png new file mode 100644 index 000000000..7a61a114e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/img_1.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-01.gif new file mode 100644 index 000000000..6fb467757 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-02.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-02.png new file mode 100644 index 000000000..db8e2c9c9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-03.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-03.png new file mode 100644 index 000000000..c0d12775c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-04.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-04.png new file mode 100644 index 000000000..b61c212c2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-05.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-05.png new file mode 100644 index 000000000..0a7cd50d1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-06.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-06.png new file mode 100644 index 000000000..d2e3959fe Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-07.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-07.png new file mode 100644 index 000000000..7e6e5e30b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-08.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-08.png new file mode 100644 index 000000000..cb7086192 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-09.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-09.png new file mode 100644 index 000000000..182d06168 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-10.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-10.png new file mode 100644 index 000000000..7f4dc9773 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-11.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-11.png new file mode 100644 index 000000000..351c388ee Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-12.png b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-12.png new file mode 100644 index 000000000..86d1b31eb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/intellij-idea-remote-jvm-debug-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-01.png new file mode 100644 index 000000000..9599da683 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-02.png new file mode 100644 index 000000000..5f4766da7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-03.png new file mode 100644 index 000000000..33c2ed33a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-04.png new file mode 100644 index 000000000..8cb3011a7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-1panel-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-01.png new file mode 100644 index 000000000..da5838af0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-02.png new file mode 100644 index 000000000..1bbf6a20e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-03.png new file mode 100644 index 000000000..a9f6f7c32 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-04.png new file mode 100644 index 000000000..3352e1b4d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-05.png new file mode 100644 index 000000000..f679fa3a3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-06.png new file mode 100644 index 000000000..fe891eafd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230617-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-01.png new file mode 100644 index 000000000..124068ac6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-02.png new file mode 100644 index 000000000..948fc06f9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-03.png new file mode 100644 index 000000000..752ade6df Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-04.png new file mode 100644 index 000000000..92f394879 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230623-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-01.png new file mode 100644 index 000000000..22af8eba6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-02.png new file mode 100644 index 000000000..c394bed8f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-03.png new file mode 100644 index 000000000..24e82f08f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-04.png new file mode 100644 index 000000000..fe423cda1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-05.png new file mode 100644 index 000000000..cb0251c06 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230624-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-01.png new file mode 100644 index 000000000..e0d8ca117 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-02.png new file mode 100644 index 000000000..e501edb30 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-03.png new file mode 100644 index 000000000..ab664cc06 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-04.png new file mode 100644 index 000000000..3ac51d4ea Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-05.png new file mode 100644 index 000000000..c179f4e64 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-06.png new file mode 100644 index 000000000..eb349f927 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230701-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-01.png new file mode 100644 index 000000000..5f6158d2a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-02.png new file mode 100644 index 000000000..09389120a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-03.png new file mode 100644 index 000000000..d6bc91569 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-04.png new file mode 100644 index 000000000..81f588aa5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-05.png new file mode 100644 index 000000000..4cff64ee6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-06.png new file mode 100644 index 000000000..8c19cf8a6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-07.png new file mode 100644 index 000000000..d0d5a8f8c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-08.png new file mode 100644 index 000000000..59703a028 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230704-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-230705-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230705-01.png new file mode 100644 index 000000000..ff6d100ff Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-230705-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-00.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-00.png new file mode 100644 index 000000000..eeba5f5c0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-00.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-01.png new file mode 100644 index 000000000..358d5d57b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-02.png new file mode 100644 index 000000000..ebdcc9414 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-03.png new file mode 100644 index 000000000..7308b1874 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-04.png new file mode 100644 index 000000000..5d1a4d512 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-05.png new file mode 100644 index 000000000..595c95a2c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-agent-skill-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-01.png new file mode 100644 index 000000000..c5cdf08d0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-02.png new file mode 100644 index 000000000..dcb66342f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-03.png new file mode 100644 index 000000000..4a8bb4ecc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-04.png new file mode 100644 index 000000000..af04c6ba6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-05.png new file mode 100644 index 000000000..cd18dbbef Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-06.png new file mode 100644 index 000000000..74c34090d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-07.png new file mode 100644 index 000000000..4380115e5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-08.png new file mode 100644 index 000000000..441ed5c9f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-09.png new file mode 100644 index 000000000..639c92891 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-10.png new file mode 100644 index 000000000..1365aaecb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-11.png new file mode 100644 index 000000000..f06c83ab9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-12.png new file mode 100644 index 000000000..8b2f45e59 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-13.png new file mode 100644 index 000000000..e8f3c928e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-14.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-14.png new file mode 100644 index 000000000..c56806efd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-15.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-15.png new file mode 100644 index 000000000..716c7b46d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-16.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-16.png new file mode 100644 index 000000000..e7f2458d4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ai-agent-openclaw-attempt-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-01.png new file mode 100644 index 000000000..27f96155c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-02.png new file mode 100644 index 000000000..07df0e716 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-03.png new file mode 100644 index 000000000..7a9c9003e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-04.png new file mode 100644 index 000000000..bdc8786e3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-05.png new file mode 100644 index 000000000..d8fa0ca30 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-06.png new file mode 100644 index 000000000..a84d5d132 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-07.png new file mode 100644 index 000000000..4a2a8f86a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-08.png new file mode 100644 index 000000000..b1e4efee0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-09.png new file mode 100644 index 000000000..32bbfcbf0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-10.png new file mode 100644 index 000000000..5d15173fa Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aigc-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-01.png new file mode 100644 index 000000000..6e7326585 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-02.png new file mode 100644 index 000000000..237e830cc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-03.png new file mode 100644 index 000000000..70c5bed00 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-04.png new file mode 100644 index 000000000..eb4182e3c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-05.png new file mode 100644 index 000000000..923da7f1b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-06.png new file mode 100644 index 000000000..7cf270c6a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-07.png new file mode 100644 index 000000000..8e2641563 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-08.png new file mode 100644 index 000000000..82b5bbbf6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-09.png new file mode 100644 index 000000000..7817fb940 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-10.png new file mode 100644 index 000000000..ac4ce9927 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-11.png new file mode 100644 index 000000000..9cbb196ba Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-12.png new file mode 100644 index 000000000..5b1233cf4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-13.png new file mode 100644 index 000000000..5e19871a6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-14.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-14.png new file mode 100644 index 000000000..95fa20d12 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-15.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-15.png new file mode 100644 index 000000000..60c232cc5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-16.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-16.png new file mode 100644 index 000000000..22103ace4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-17.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-17.png new file mode 100644 index 000000000..3d83021a7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-aliyun-workbench-17.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-01.png new file mode 100644 index 000000000..1f565b03e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-02.png new file mode 100644 index 000000000..47b3c0c1f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-03.png new file mode 100644 index 000000000..df31ca801 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-04.png new file mode 100644 index 000000000..7e72d67cd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-05.png new file mode 100644 index 000000000..6ade60971 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-06.png new file mode 100644 index 000000000..38b1cdab8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-07.png new file mode 100644 index 000000000..25e6716c0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-08.png new file mode 100644 index 000000000..465ad0c96 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-09.png new file mode 100644 index 000000000..7bf330d2a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-10.png new file mode 100644 index 000000000..127fc87a3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-11.png new file mode 100644 index 000000000..3b2b33628 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-12.png new file mode 100644 index 000000000..dac869260 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-apisix-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-01.png new file mode 100644 index 000000000..dda9ed57d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-02.png new file mode 100644 index 000000000..c2a752994 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-03.png new file mode 100644 index 000000000..1a84a9fe1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-04.png new file mode 100644 index 000000000..ef5362bf8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-architectural-design-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-01.png new file mode 100644 index 000000000..09d250359 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-02.png new file mode 100644 index 000000000..d1f9bd5e5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-03.png new file mode 100644 index 000000000..4399dcc1e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-04.png new file mode 100644 index 000000000..0e623d940 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-05.png new file mode 100644 index 000000000..aac28d6fe Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-06.png new file mode 100644 index 000000000..034162af7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-07.png new file mode 100644 index 000000000..292ac72a0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-08.png new file mode 100644 index 000000000..bb7bc2736 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-09.png new file mode 100644 index 000000000..c2f44aadc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-10.png new file mode 100644 index 000000000..14e236795 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-11.png new file mode 100644 index 000000000..019e09a39 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-12.png new file mode 100644 index 000000000..bf9b6a9ab Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-13.png new file mode 100644 index 000000000..a9fcc22f9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-arthas-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-01.gif new file mode 100644 index 000000000..53505eabb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-02.png new file mode 100644 index 000000000..01a896afc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-03.png new file mode 100644 index 000000000..0134cd1e4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-04.png new file mode 100644 index 000000000..7258b512b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-05.png new file mode 100644 index 000000000..be0105a06 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-06.png new file mode 100644 index 000000000..ef54e8019 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-07.png new file mode 100644 index 000000000..015f7a9f3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-08.png new file mode 100644 index 000000000..bee3a00b0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-09.png new file mode 100644 index 000000000..ed74a49a3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-10.png new file mode 100644 index 000000000..405f51bf2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-11.png new file mode 100644 index 000000000..3fa632177 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-12.png new file mode 100644 index 000000000..965ab4c97 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-13.png new file mode 100644 index 000000000..1aae4d71e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-14.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-14.png new file mode 100644 index 000000000..eeed13644 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-15.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-15.png new file mode 100644 index 000000000..bbe61a8b5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-16.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-16.png new file mode 100644 index 000000000..f04c5914c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-buddy-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-01.png new file mode 100644 index 000000000..0f0b9caea Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-02.png new file mode 100644 index 000000000..d2c71adf7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-03.png new file mode 100644 index 000000000..cb0a47398 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-04.png new file mode 100644 index 000000000..f9cb57b55 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-05.png new file mode 100644 index 000000000..edb51b167 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cainiao-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-01.png new file mode 100644 index 000000000..74bf528e4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-02.png new file mode 100644 index 000000000..73fcc58da Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-03.png new file mode 100644 index 000000000..59c755e50 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-04.png new file mode 100644 index 000000000..84669cbfd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-05.png new file mode 100644 index 000000000..26244a451 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-cloud-server-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-01.png new file mode 100644 index 000000000..5ea7ba65e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-02.png new file mode 100644 index 000000000..ab7d8c52f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-03.png new file mode 100644 index 000000000..9755a760b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-04.png new file mode 100644 index 000000000..8f9d77df3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-connection-pool-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-01.gif new file mode 100644 index 000000000..04dc37836 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-02.png new file mode 100644 index 000000000..a146fa4b4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-03.png new file mode 100644 index 000000000..a0c974959 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-04.png new file mode 100644 index 000000000..9e2398f84 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-05.png new file mode 100644 index 000000000..0b5603cc3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-06.png new file mode 100644 index 000000000..0d6305cda Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-07.png new file mode 100644 index 000000000..4a76bcdac Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-08.png new file mode 100644 index 000000000..b8f8a781c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-09.png new file mode 100644 index 000000000..c46b0e3c6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-10.png new file mode 100644 index 000000000..c221fb99b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-01.gif new file mode 100644 index 000000000..f32099b9d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-02.png new file mode 100644 index 000000000..a9bb17e00 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-03.png new file mode 100644 index 000000000..37014481d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-04.png new file mode 100644 index 000000000..10094eb59 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-disruptor-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-00.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-00.gif new file mode 100644 index 000000000..479d9ed45 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-00.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-01.png new file mode 100644 index 000000000..2bf30fa79 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-02.png new file mode 100644 index 000000000..d71057a83 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-03.png new file mode 100644 index 000000000..2798f5d87 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-04.png new file mode 100644 index 000000000..a4eda6f05 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-01.png new file mode 100644 index 000000000..038c8988f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-02.png new file mode 100644 index 000000000..e510ecdbf Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-03.png new file mode 100644 index 000000000..ff765afa8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-04.png new file mode 100644 index 000000000..0acb0ef50 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-05.png new file mode 100644 index 000000000..d44657c78 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-06.png new file mode 100644 index 000000000..209f3a719 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-07.png new file mode 100644 index 000000000..a88952496 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-08.png new file mode 100644 index 000000000..7f98bae43 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-09.png new file mode 100644 index 000000000..37e222db1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-10.png new file mode 100644 index 000000000..60c0065b0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-11.png new file mode 100644 index 000000000..e25857da2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-deploy-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-00.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-00.png new file mode 100644 index 000000000..0726e9403 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-00.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-01.png new file mode 100644 index 000000000..ad2e30144 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-02.png new file mode 100644 index 000000000..5dbf590bd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-03.png new file mode 100644 index 000000000..301cea5a7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-04.png new file mode 100644 index 000000000..562978114 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-05.png new file mode 100644 index 000000000..87520c886 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-06.png new file mode 100644 index 000000000..bee2dbc1d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-07.png new file mode 100644 index 000000000..393173534 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-08.png new file mode 100644 index 000000000..c083236ce Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-09.png new file mode 100644 index 000000000..475d66748 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-idea-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-01.png new file mode 100644 index 000000000..a8e83b083 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-02.png new file mode 100644 index 000000000..b5b613549 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-03.png new file mode 100644 index 000000000..c57449790 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-04.png new file mode 100644 index 000000000..d8490710e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-05.png new file mode 100644 index 000000000..a100f3058 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-06.png new file mode 100644 index 000000000..30ce95c19 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-07.png new file mode 100644 index 000000000..bc6b5cdf2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-docker-install-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-01.png new file mode 100644 index 000000000..ef2a7cf1a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-02.png new file mode 100644 index 000000000..c890448dd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-03.png new file mode 100644 index 000000000..51fc1581c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-04.png new file mode 100644 index 000000000..09a33cbed Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-05.png new file mode 100644 index 000000000..81abcc08e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-06.png new file mode 100644 index 000000000..972dc462e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-07.png new file mode 100644 index 000000000..1939631c2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-08.png new file mode 100644 index 000000000..0b81cf4e0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-elk-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-01.png new file mode 100644 index 000000000..0d65d5a12 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-02.png new file mode 100644 index 000000000..9ae618d63 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-03.png new file mode 100644 index 000000000..c8b24fd62 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-04.png new file mode 100644 index 000000000..000f727a6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-05.png new file mode 100644 index 000000000..7e31fcb8b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-06.png new file mode 100644 index 000000000..2240875cf Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-07.png new file mode 100644 index 000000000..7013cf6f3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-08.png new file mode 100644 index 000000000..54b941d0d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-09.png new file mode 100644 index 000000000..0b1f14fc0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-10.png new file mode 100644 index 000000000..f6d4ac592 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-11.png new file mode 100644 index 000000000..3a53c27aa Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-12.png new file mode 100644 index 000000000..8d0e046b0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-13.png new file mode 100644 index 000000000..e0965ab32 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-frp-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-01.png new file mode 100644 index 000000000..632185cfb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-02.png new file mode 100644 index 000000000..0202874c0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-03.png new file mode 100644 index 000000000..568234c7c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-04.png new file mode 100644 index 000000000..c1ce75ba5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-05.png new file mode 100644 index 000000000..b8c974045 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-06.png new file mode 100644 index 000000000..d5dc2d141 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-07.png new file mode 100644 index 000000000..4d969016f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-08.png new file mode 100644 index 000000000..a839db975 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-09.png new file mode 100644 index 000000000..b0d2912d7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-10.png new file mode 100644 index 000000000..ca4ecc512 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-11.png new file mode 100644 index 000000000..bec07081f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-12.png new file mode 100644 index 000000000..8edf825b3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-13.png new file mode 100644 index 000000000..20badc68b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-14.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-14.png new file mode 100644 index 000000000..311e76622 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-15.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-15.png new file mode 100644 index 000000000..ee224ae98 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-16.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-16.png new file mode 100644 index 000000000..743c486bb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-17.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-17.png new file mode 100644 index 000000000..940ebece6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-17.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-18.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-18.png new file mode 100644 index 000000000..ac6f2e79b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-18.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-19.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-19.png new file mode 100644 index 000000000..677cd6adc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-19.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-20.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-20.png new file mode 100644 index 000000000..844f9500a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-20.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-21.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-21.png new file mode 100644 index 000000000..66a75a92c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-21.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-22.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-22.png new file mode 100644 index 000000000..b3d46066d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-22.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-23.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-23.png new file mode 100644 index 000000000..7aa6effb6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-23.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-24.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-24.png new file mode 100644 index 000000000..d750688c7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-git-24.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-01.png new file mode 100644 index 000000000..69819a00c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-02.png new file mode 100644 index 000000000..a1bf8ba4d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-03.png new file mode 100644 index 000000000..8c4664a3a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-04.png new file mode 100644 index 000000000..8df777f6e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-05.png new file mode 100644 index 000000000..b976fc95e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-06.png new file mode 100644 index 000000000..2d0f22926 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-07.png new file mode 100644 index 000000000..acde2b4cd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-08.png new file mode 100644 index 000000000..8d901df7d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-09.png new file mode 100644 index 000000000..799eea1ce Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-actions-workflows-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-01.png new file mode 100644 index 000000000..64f4f58dc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-02.png new file mode 100644 index 000000000..9fbc61029 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-03.png new file mode 100644 index 000000000..cc77e4c58 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-04.png new file mode 100644 index 000000000..4728d1328 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-05.png new file mode 100644 index 000000000..47677b348 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-06.png new file mode 100644 index 000000000..f8fdf32da Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-07.png new file mode 100644 index 000000000..de436b72d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-08.png new file mode 100644 index 000000000..244bc6e6a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-github-modes-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-01.png new file mode 100644 index 000000000..42a560e19 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-02.png new file mode 100644 index 000000000..a60cec435 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-03.png new file mode 100644 index 000000000..875664022 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-04.png new file mode 100644 index 000000000..883001553 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-05.png new file mode 100644 index 000000000..10f7f87a4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-06.png new file mode 100644 index 000000000..e9173e8f1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-google-adk-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-01.png new file mode 100644 index 000000000..e25b79e6d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-02.png new file mode 100644 index 000000000..2ff6bdbaf Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-03.png new file mode 100644 index 000000000..b87a446cb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-04.png new file mode 100644 index 000000000..ded66964a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-05.png new file mode 100644 index 000000000..d60307bd1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-06.png new file mode 100644 index 000000000..3f05c50a9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-07.png new file mode 100644 index 000000000..88523b457 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-08.png new file mode 100644 index 000000000..6a6c2d4be Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-09.png new file mode 100644 index 000000000..d26883b9e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-grafana-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-01.gif new file mode 100644 index 000000000..2a6a043e2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-02.png new file mode 100644 index 000000000..6deb76af8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-03.png new file mode 100644 index 000000000..ba7d15785 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-04.png new file mode 100644 index 000000000..69c2239e0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-05.png new file mode 100644 index 000000000..3ea8daf99 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-06.png new file mode 100644 index 000000000..5b96ce19d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-07.png new file mode 100644 index 000000000..ac7a36964 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-08.png new file mode 100644 index 000000000..59c9a68b7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-09.png new file mode 100644 index 000000000..ea7dd3f5a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-10.png new file mode 100644 index 000000000..08c11c68d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-11.png new file mode 100644 index 000000000..e3ecff4aa Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-12.png new file mode 100644 index 000000000..cd6acf932 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-13.png new file mode 100644 index 000000000..ff5a3929e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-14.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-14.png new file mode 100644 index 000000000..7842833b1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-15.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-15.png new file mode 100644 index 000000000..a6884c4f9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-16.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-16.png new file mode 100644 index 000000000..503faff79 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-higress-ai-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-introduce-01.jpg b/docs/.vuepress/public/images/roadmap/tutorial/road-map-introduce-01.jpg new file mode 100644 index 000000000..5d35ed04a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-introduce-01.jpg differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-introduce-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-introduce-02.png new file mode 100644 index 000000000..d4dbffd85 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-introduce-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-01.png new file mode 100644 index 000000000..e015c2463 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-02.png new file mode 100644 index 000000000..9316bca60 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-03.png new file mode 100644 index 000000000..5a16d2869 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-04.png new file mode 100644 index 000000000..d8f9da698 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-05.png new file mode 100644 index 000000000..5a6349c38 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-06.png new file mode 100644 index 000000000..04839277b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-07.png new file mode 100644 index 000000000..e0bf78df3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-08.png new file mode 100644 index 000000000..665762591 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-09.png new file mode 100644 index 000000000..d4ac8f7a7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-10.png new file mode 100644 index 000000000..abbbfb815 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-11.png new file mode 100644 index 000000000..22afb2875 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-12.png new file mode 100644 index 000000000..01c896624 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-13.png new file mode 100644 index 000000000..94ec310b3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-joycode-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-01.gif new file mode 100644 index 000000000..2d96d62a9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-02.png new file mode 100644 index 000000000..5a92335df Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-03.png new file mode 100644 index 000000000..eeaf3b7c3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-04.png new file mode 100644 index 000000000..10149ab41 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-05.png new file mode 100644 index 000000000..4f7ff24b8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-06.png new file mode 100644 index 000000000..272c75ed4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-linux-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-01.gif new file mode 100644 index 000000000..3e9103a8c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-02.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-02.gif new file mode 100644 index 000000000..66af144d7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-02.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-03.png new file mode 100644 index 000000000..03d375099 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-04.png new file mode 100644 index 000000000..23bfc1ce9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-05.png new file mode 100644 index 000000000..26c3022ae Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-06.png new file mode 100644 index 000000000..438997207 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-07.png new file mode 100644 index 000000000..8caf650f2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mac-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-01.gif new file mode 100644 index 000000000..f298fac61 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-02.png new file mode 100644 index 000000000..0d8a668c9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-03.png new file mode 100644 index 000000000..c1696c1f3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-04.png new file mode 100644 index 000000000..5f11e856b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-05.png new file mode 100644 index 000000000..ff6a911b8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-06.png new file mode 100644 index 000000000..0f13431db Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-07.png new file mode 100644 index 000000000..64f767bcd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-08.png new file mode 100644 index 000000000..dfeaa17ab Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-09.png new file mode 100644 index 000000000..4f3f4ff87 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-10.png new file mode 100644 index 000000000..df2b6bf9c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-11.png new file mode 100644 index 000000000..2e00430a0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-12.png new file mode 100644 index 000000000..34c17315a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-13.png new file mode 100644 index 000000000..d79927edc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-14.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-14.png new file mode 100644 index 000000000..a20069560 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-15.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-15.png new file mode 100644 index 000000000..35961140e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-16.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-16.png new file mode 100644 index 000000000..ec96d087a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-17.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-17.png new file mode 100644 index 000000000..a9cacc934 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-17.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-18.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-18.png new file mode 100644 index 000000000..4c1a6981a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-18.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-19.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-19.png new file mode 100644 index 000000000..f9fda0cd9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-19.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-20.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-20.png new file mode 100644 index 000000000..d70d66466 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-20.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-21.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-21.png new file mode 100644 index 000000000..a3080cd17 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-21.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-22.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-22.png new file mode 100644 index 000000000..0fd15fbff Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-22.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-23.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-23.png new file mode 100644 index 000000000..9b420aa60 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-23.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-24.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-24.png new file mode 100644 index 000000000..905b2a162 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-24.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-01.png new file mode 100644 index 000000000..b660adc6b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-02.png new file mode 100644 index 000000000..caedc8c89 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-03.png new file mode 100644 index 000000000..f7e0146b8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-04.png new file mode 100644 index 000000000..9a97744b2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-05.png new file mode 100644 index 000000000..c22d9d6de Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-06.png new file mode 100644 index 000000000..072938ef4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-07.png new file mode 100644 index 000000000..36be3fbe5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-08.png new file mode 100644 index 000000000..eb9395264 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-09.png new file mode 100644 index 000000000..cf9e36930 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-10.png new file mode 100644 index 000000000..e3e9ed940 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-11.png new file mode 100644 index 000000000..15b8ce37d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-12.png new file mode 100644 index 000000000..8c57984be Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-13.png new file mode 100644 index 000000000..07f332b58 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-14.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-14.png new file mode 100644 index 000000000..6bc682540 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-15.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-15.png new file mode 100644 index 000000000..80ac4f672 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-16.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-16.png new file mode 100644 index 000000000..f6b0b4ff7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-17.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-17.png new file mode 100644 index 000000000..9bf948ce7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-17.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-18.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-18.png new file mode 100644 index 000000000..5ad68e168 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-maven-archetype-18.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-01.gif new file mode 100644 index 000000000..5b3dc1c0b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-02.png new file mode 100644 index 000000000..0dd6e48e1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-03.png new file mode 100644 index 000000000..0a48f2c13 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-04.png new file mode 100644 index 000000000..2078533fc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-mysql-time-zone-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-01.gif new file mode 100644 index 000000000..090e37ae9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-02.png new file mode 100644 index 000000000..e61d6bf93 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-03.png new file mode 100644 index 000000000..9f55039a5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-04.png new file mode 100644 index 000000000..252b49648 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-05.png new file mode 100644 index 000000000..30562382d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-06.png new file mode 100644 index 000000000..59b158b44 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-07.png new file mode 100644 index 000000000..a7860e169 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-08.png new file mode 100644 index 000000000..c72758bde Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-nas-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-01.png new file mode 100644 index 000000000..ee2a166d8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-02.png new file mode 100644 index 000000000..4f3f5793b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-03.png new file mode 100644 index 000000000..6f0b027df Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-04.png new file mode 100644 index 000000000..98eca0ffc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-05.png new file mode 100644 index 000000000..74b1d0a3b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-06.png new file mode 100644 index 000000000..ec63f5a06 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-07.png new file mode 100644 index 000000000..286a954d5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-08.png new file mode 100644 index 000000000..ade777de9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-09.png new file mode 100644 index 000000000..cc73f8f58 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-10.png new file mode 100644 index 000000000..f7f273bc2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-11.png new file mode 100644 index 000000000..9c455920b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-12.png new file mode 100644 index 000000000..fe2361aa1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-13.png new file mode 100644 index 000000000..2c84428e3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-14.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-14.png new file mode 100644 index 000000000..561f04eb4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-oauth2-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-00.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-00.gif new file mode 100644 index 000000000..977e4808e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-00.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-01.png new file mode 100644 index 000000000..160e50843 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-02.png new file mode 100644 index 000000000..5cfbd0586 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-03.png new file mode 100644 index 000000000..9429bd188 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-04.png new file mode 100644 index 000000000..0f4c7a2ba Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-05.png new file mode 100644 index 000000000..05dc48486 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-06.png new file mode 100644 index 000000000..25cd2d233 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-07.png new file mode 100644 index 000000000..a22a08baf Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-08.png new file mode 100644 index 000000000..dc8a3b40a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-09.png new file mode 100644 index 000000000..bf7dcdc8a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-10.png new file mode 100644 index 000000000..3bae3e205 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-11.png new file mode 100644 index 000000000..e221c1451 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ollama-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-01.gif new file mode 100644 index 000000000..cb6e9d748 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-02.png new file mode 100644 index 000000000..c230746e2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-03.png new file mode 100644 index 000000000..753f57d77 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-04.png new file mode 100644 index 000000000..f9132d68f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-05.png new file mode 100644 index 000000000..ce0b3ff9a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-06.png new file mode 100644 index 000000000..cdac29f60 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-07.png new file mode 100644 index 000000000..d28c83d25 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-08.png new file mode 100644 index 000000000..176565d16 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-09.png new file mode 100644 index 000000000..a7bc9ed75 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-10.png new file mode 100644 index 000000000..5511b6f66 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-11.png new file mode 100644 index 000000000..21d19b4ed Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-12.png new file mode 100644 index 000000000..460d47939 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-13.png new file mode 100644 index 000000000..7ea01653d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-14.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-14.png new file mode 100644 index 000000000..15d0ebd92 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-15.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-15.png new file mode 100644 index 000000000..093f7a423 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-16.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-16.png new file mode 100644 index 000000000..e920d454c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-17.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-17.png new file mode 100644 index 000000000..a5575f7c2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-17.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-18.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-18.png new file mode 100644 index 000000000..d3fa7b60f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-18.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-19.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-19.png new file mode 100644 index 000000000..3d31eed31 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-19.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-20.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-20.png new file mode 100644 index 000000000..d9a689de9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-20.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-21.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-21.png new file mode 100644 index 000000000..26ae2c19b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-21.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-22.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-22.png new file mode 100644 index 000000000..6b5767942 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-22.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-23.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-23.png new file mode 100644 index 000000000..f59dd95c3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-openclaw-23.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-01.png new file mode 100644 index 000000000..86b0aafe2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-02.png new file mode 100644 index 000000000..4f482e9cb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-03.png new file mode 100644 index 000000000..59a180a90 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-04.png new file mode 100644 index 000000000..d663606e5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-05.png new file mode 100644 index 000000000..7320d4523 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-06.png new file mode 100644 index 000000000..baf2383b1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-07.png new file mode 100644 index 000000000..f1dae07a1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-08.png new file mode 100644 index 000000000..820a83918 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-09.png new file mode 100644 index 000000000..a987a1f74 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-10.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-10.png new file mode 100644 index 000000000..3ba133a7a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-11.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-11.png new file mode 100644 index 000000000..b9479e9ee Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-12.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-12.png new file mode 100644 index 000000000..92c8c5545 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-13.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-13.png new file mode 100644 index 000000000..9768c5577 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-opencode-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-01.gif new file mode 100644 index 000000000..ca13671a7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-02.png new file mode 100644 index 000000000..83db76f77 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-03.png new file mode 100644 index 000000000..57faa52b5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-04.png new file mode 100644 index 000000000..112fe3764 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-05.png new file mode 100644 index 000000000..712fe01d5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-06.png new file mode 100644 index 000000000..3d6809743 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-private-dockerhub-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-dependency-injection-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-dependency-injection-01.png new file mode 100644 index 000000000..7f523b533 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-dependency-injection-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-dependency-injection-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-dependency-injection-02.png new file mode 100644 index 000000000..186b3e3fa Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-dependency-injection-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-01.png new file mode 100644 index 000000000..2de1fd999 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-02.png new file mode 100644 index 000000000..520eb9e61 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-03.png new file mode 100644 index 000000000..7ff3a8916 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-04.png new file mode 100644 index 000000000..5528a95ed Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-05.png new file mode 100644 index 000000000..956129d86 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-06.png new file mode 100644 index 000000000..61ffbc3a5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-07.png new file mode 100644 index 000000000..e950192bc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-08.png new file mode 100644 index 000000000..acdf41db1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-spring-security-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-01.png new file mode 100644 index 000000000..a4b30212d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-02.png new file mode 100644 index 000000000..e3262a557 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-03.png new file mode 100644 index 000000000..f8f49e431 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-04.png new file mode 100644 index 000000000..4169e2f6e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-05.png new file mode 100644 index 000000000..19896632a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-06.png new file mode 100644 index 000000000..cd3896bcb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-07.png new file mode 100644 index 000000000..92ce813df Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-08.png new file mode 100644 index 000000000..7b8f223e6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-09.png new file mode 100644 index 000000000..e7538a845 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-trae-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-01.png new file mode 100755 index 000000000..72c1cfbd4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-02.png new file mode 100755 index 000000000..bb91a8daa Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-03.png new file mode 100755 index 000000000..1ba0a88cf Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-04.png new file mode 100755 index 000000000..51cf26346 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-05.png new file mode 100755 index 000000000..045d409aa Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-06.png new file mode 100755 index 000000000..fb3420ba7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-07.png new file mode 100755 index 000000000..0cf748479 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-08.png new file mode 100755 index 000000000..215c6dcc0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-09.png new file mode 100755 index 000000000..d35dc34c8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-traffic-agent-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-01.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-01.png new file mode 100644 index 000000000..634305c99 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-02.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-02.png new file mode 100644 index 000000000..157d188fd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-03.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-03.png new file mode 100644 index 000000000..147e45d55 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-04.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-04.png new file mode 100644 index 000000000..dc0451d61 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-05.png new file mode 100644 index 000000000..b08a3fffc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-06.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-06.png new file mode 100644 index 000000000..5edf80a6a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-07.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-07.png new file mode 100644 index 000000000..a7c05b2e5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-08.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-08.png new file mode 100644 index 000000000..f6071c31c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-09.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-09.png new file mode 100644 index 000000000..851b3d50b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/road-map-w13scan-jdumpspider-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-01.png new file mode 100644 index 000000000..531ec326f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-02.png new file mode 100644 index 000000000..51006e214 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-03.png new file mode 100644 index 000000000..8595ac280 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-04.png new file mode 100644 index 000000000..e7905b141 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-05.png new file mode 100644 index 000000000..a5bf81ed7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-06.png new file mode 100644 index 000000000..decdebcd3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-07.png new file mode 100644 index 000000000..63dc6d03a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-08.png new file mode 100644 index 000000000..d24bc1d1c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-09.png new file mode 100644 index 000000000..ae7efeab6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-10.png new file mode 100644 index 000000000..eb5f33c31 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230706-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-01.png new file mode 100644 index 000000000..f2e2687dc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-02.png new file mode 100644 index 000000000..6121f8338 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-03.png new file mode 100644 index 000000000..017d1017c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-04.png new file mode 100644 index 000000000..fcee1c7fd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-05.png new file mode 100644 index 000000000..78853cfb1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-06.png new file mode 100644 index 000000000..decdebcd3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-07.png new file mode 100644 index 000000000..212028026 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-08.png new file mode 100644 index 000000000..d24bc1d1c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-09.png new file mode 100644 index 000000000..ae7efeab6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-10.png new file mode 100644 index 000000000..567b429a6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230707-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-01.png new file mode 100644 index 000000000..0bd06f22b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-02.png new file mode 100644 index 000000000..96e02119d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-03.png new file mode 100644 index 000000000..45d698bfc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-04.png new file mode 100644 index 000000000..e872f57d5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-05.png new file mode 100644 index 000000000..ce1ce11f6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-06.png new file mode 100644 index 000000000..decdebcd3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-07.png new file mode 100644 index 000000000..2049fb6c3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-08.png new file mode 100644 index 000000000..d24bc1d1c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-09.png new file mode 100644 index 000000000..ae7efeab6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-10.png new file mode 100644 index 000000000..6064c8d9d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-230708-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-01.png new file mode 100644 index 000000000..9db1bc2d7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-02.png new file mode 100644 index 000000000..a95e089fc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-03.png new file mode 100644 index 000000000..821c92209 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-04.png new file mode 100644 index 000000000..b0cc41cc9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-05.png new file mode 100644 index 000000000..053e35372 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-06.png new file mode 100644 index 000000000..75345aaac Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-07.png new file mode 100644 index 000000000..f49c600be Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-08.png new file mode 100644 index 000000000..2b513e283 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-09.png new file mode 100644 index 000000000..6ac3c460c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-10.png new file mode 100644 index 000000000..2dfe1cd30 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-11.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-11.png new file mode 100644 index 000000000..6eb43e5cc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-12.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-12.png new file mode 100644 index 000000000..9484aff07 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-canal-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ddd-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ddd-01.png new file mode 100644 index 000000000..d870b5cba Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ddd-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-01.png new file mode 100644 index 000000000..12583928b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-02.png new file mode 100644 index 000000000..e13f3419f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-03.png new file mode 100644 index 000000000..682447e2c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-04.png new file mode 100644 index 000000000..3b4ae0b3b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-05.png new file mode 100644 index 000000000..fc5a19ea2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-06.png new file mode 100644 index 000000000..155d14a7e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-dubbo-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-git-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-git-01.png new file mode 100644 index 000000000..f18fabc9d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-git-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-git-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-git-02.png new file mode 100644 index 000000000..3d2a6c0d1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-git-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-01.gif new file mode 100644 index 000000000..4a85967bd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-02.png new file mode 100644 index 000000000..a215f3e13 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-03.png new file mode 100644 index 000000000..c7173add1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-04.png new file mode 100644 index 000000000..390bad08b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-05.png new file mode 100644 index 000000000..d29564f22 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-06.png new file mode 100644 index 000000000..25089a45f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-07.png new file mode 100644 index 000000000..04aca14fd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-08.png new file mode 100644 index 000000000..e564a8ff1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-higress-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-01.png new file mode 100644 index 000000000..327a476aa Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-02.png new file mode 100644 index 000000000..9ce0f3394 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-03.png new file mode 100644 index 000000000..ef3b2c01a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-04.png new file mode 100644 index 000000000..865565fa7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-05.png new file mode 100644 index 000000000..c01b76a79 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-06.png new file mode 100644 index 000000000..8ee0291d1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-07.png new file mode 100644 index 000000000..ce38a66e1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-08.png new file mode 100644 index 000000000..7764ae2fe Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-09.png new file mode 100644 index 000000000..6b19e045f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-10.png new file mode 100644 index 000000000..05574c92c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-11.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-11.png new file mode 100644 index 000000000..aea616fda Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-12.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-12.png new file mode 100644 index 000000000..45aa138f0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-http-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-01.png new file mode 100644 index 000000000..b4c596269 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-02.png new file mode 100644 index 000000000..214dd73d1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-03.png new file mode 100644 index 000000000..b59961ed2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-04.png new file mode 100644 index 000000000..6545f0a76 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-05.png new file mode 100644 index 000000000..ed0e4439c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-06.png new file mode 100644 index 000000000..9188a1ba6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-07.png new file mode 100644 index 000000000..6829cfdc3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-08.png new file mode 100644 index 000000000..b7f9835db Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-09.png new file mode 100644 index 000000000..4adccada6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-10.png new file mode 100644 index 000000000..f515506df Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-11.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-11.png new file mode 100644 index 000000000..7d1df0dd4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ignite-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-01.png new file mode 100644 index 000000000..4da19649c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-02.png new file mode 100644 index 000000000..89fc50ab7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-03.png new file mode 100644 index 000000000..43602030e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-04.png new file mode 100644 index 000000000..da2479ad6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-05.png new file mode 100644 index 000000000..da295cf2d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-06.png new file mode 100644 index 000000000..0000e204c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-07.png new file mode 100644 index 000000000..b89e716fc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-08.png new file mode 100644 index 000000000..e4acfc9cc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-09.png new file mode 100644 index 000000000..4bee47357 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-10.png new file mode 100644 index 000000000..66a0cb8c6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-11.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-11.png new file mode 100644 index 000000000..1daed0fe5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-12.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-12.png new file mode 100644 index 000000000..a7d683c9f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-13.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-13.png new file mode 100644 index 000000000..73d420142 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-14.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-14.png new file mode 100644 index 000000000..6a69f2cfa Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jenkins-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-01.png new file mode 100644 index 000000000..ba50aa41b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-02.png new file mode 100644 index 000000000..6945f91ee Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-03.png new file mode 100644 index 000000000..403cbd21c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-04.png new file mode 100644 index 000000000..e3cbed78e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-05.png new file mode 100644 index 000000000..f5ba17952 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-06.png new file mode 100644 index 000000000..77c0c670b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-07.png new file mode 100644 index 000000000..19c26a0bc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-08.png new file mode 100644 index 000000000..89febb610 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-09.png new file mode 100644 index 000000000..7f0990acb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-10.png new file mode 100644 index 000000000..6842b8621 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-11.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-11.png new file mode 100644 index 000000000..4ce83b513 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-12.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-12.png new file mode 100644 index 000000000..692da4042 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-13.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-13.png new file mode 100644 index 000000000..c19e68a4d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-14.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-14.png new file mode 100644 index 000000000..7ab9f026f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-jmeter-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-01.png new file mode 100644 index 000000000..6a610476c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-02.png new file mode 100644 index 000000000..4232ec55a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-03.png new file mode 100644 index 000000000..a4dd7aa2b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-04.png new file mode 100644 index 000000000..a2056765c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-05.png new file mode 100644 index 000000000..1006acd40 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-06.png new file mode 100644 index 000000000..f3c3188d5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-07.png new file mode 100644 index 000000000..568a582b9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-08.png new file mode 100644 index 000000000..905590c2c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-kafka-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-01.png new file mode 100644 index 000000000..c5099e89e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-02.png new file mode 100644 index 000000000..b7314c408 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-03.png new file mode 100644 index 000000000..c7a719c3b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-04.png new file mode 100644 index 000000000..3c5bfad4a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mock-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-01.png new file mode 100644 index 000000000..e65008cbb Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-02.png new file mode 100644 index 000000000..442196171 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-03.png new file mode 100644 index 000000000..66f276358 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-04.png new file mode 100644 index 000000000..974af39ac Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-05.png new file mode 100644 index 000000000..a9a9c488f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-06.png new file mode 100644 index 000000000..32df9a1c0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mybatis-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mysql-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mysql-01.png new file mode 100644 index 000000000..2152ee64c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mysql-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mysql-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mysql-02.png new file mode 100644 index 000000000..a65104cee Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mysql-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mysql-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mysql-03.png new file mode 100644 index 000000000..31b5a88a9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-mysql-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-01.png new file mode 100644 index 000000000..20216091e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-02.png new file mode 100644 index 000000000..9f6363a3a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-03.png new file mode 100644 index 000000000..d6be0e7d6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-04.png new file mode 100644 index 000000000..4e57f8326 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-05.png new file mode 100644 index 000000000..c86afd28a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-06.png new file mode 100644 index 000000000..d0e283460 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-07.png new file mode 100644 index 000000000..5b4680044 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-08.png new file mode 100644 index 000000000..9989d96d5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-09.png new file mode 100644 index 000000000..3da7112dd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-10.png new file mode 100644 index 000000000..1945470ac Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-11.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-11.png new file mode 100644 index 000000000..2c1533893 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-12.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-12.png new file mode 100644 index 000000000..7743f2ad8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-13.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-13.png new file mode 100644 index 000000000..a08135be8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-14.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-14.png new file mode 100644 index 000000000..45ce04f39 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-15.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-15.png new file mode 100644 index 000000000..00f3eb8a1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-16.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-16.png new file mode 100644 index 000000000..6cc0dde80 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-17.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-17.png new file mode 100644 index 000000000..2d04fad25 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-17.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-18.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-18.png new file mode 100644 index 000000000..27dfbb314 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-phone-agent-18.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-01.png new file mode 100644 index 000000000..0dcfeebe0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-02.png new file mode 100644 index 000000000..7326a6545 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-03.png new file mode 100644 index 000000000..969b79676 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-04.png new file mode 100644 index 000000000..65f557531 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-05.png new file mode 100644 index 000000000..e4049a5d7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-06.png new file mode 100644 index 000000000..2ddfea5a3 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-07.png new file mode 100644 index 000000000..4767cf253 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-08.png new file mode 100644 index 000000000..98945b834 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-09.png new file mode 100644 index 000000000..847de9731 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-10.png new file mode 100644 index 000000000..660ea635f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-quartz-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rabbitmq-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rabbitmq-01.png new file mode 100644 index 000000000..be733a80f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rabbitmq-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rabbitmq-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rabbitmq-02.png new file mode 100644 index 000000000..71712b1fd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rabbitmq-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rabbitmq-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rabbitmq-03.png new file mode 100644 index 000000000..c77551465 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rabbitmq-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-01.png new file mode 100644 index 000000000..aaaedea82 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-02.png new file mode 100644 index 000000000..068459bab Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-03.png new file mode 100644 index 000000000..105396a9d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-04.png new file mode 100644 index 000000000..34c2e4494 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-05.png new file mode 100644 index 000000000..f1d8539fc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ratelimiter-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-01.png new file mode 100644 index 000000000..5ac9c6c3f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-02.png new file mode 100644 index 000000000..8627e0798 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-03.png new file mode 100644 index 000000000..26822f113 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-04.png new file mode 100644 index 000000000..0e1c4aad7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-05.png new file mode 100644 index 000000000..96c77c667 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-06.png new file mode 100644 index 000000000..67b2b92a1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-07.png new file mode 100644 index 000000000..52659e0ac Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-redis-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-01.png new file mode 100644 index 000000000..3da429d51 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-02.png new file mode 100644 index 000000000..fff95574d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-03.png new file mode 100644 index 000000000..d16dab900 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-04.png new file mode 100644 index 000000000..d1856fc01 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-05.png new file mode 100644 index 000000000..18ba0c5dc Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-06.png new file mode 100644 index 000000000..e0be5aa82 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-07.png new file mode 100644 index 000000000..d15a3312f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-08.png new file mode 100644 index 000000000..c28f5955c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-09.png new file mode 100644 index 000000000..a487564ea Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-10.png new file mode 100644 index 000000000..c3307099a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-11.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-11.png new file mode 100644 index 000000000..ac11dac5e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-rocketmq-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-shardingjdbc-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-shardingjdbc-01.png new file mode 100644 index 000000000..b6fea2fbe Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-shardingjdbc-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-shardingjdbc-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-shardingjdbc-02.png new file mode 100644 index 000000000..fce2b9b03 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-shardingjdbc-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-shardingjdbc-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-shardingjdbc-03.png new file mode 100644 index 000000000..06981de92 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-shardingjdbc-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-spring-ai-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-spring-ai-01.png new file mode 100644 index 000000000..6c3f3d812 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-spring-ai-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-01.png new file mode 100644 index 000000000..3efeecfd9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-02.png new file mode 100644 index 000000000..759e5c16c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-03.png new file mode 100644 index 000000000..6a5485442 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-04.png new file mode 100644 index 000000000..efc2a3236 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-05.png new file mode 100644 index 000000000..9f317ef3f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-06.png new file mode 100644 index 000000000..6225d3f71 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-07.png new file mode 100644 index 000000000..c66fab4fd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-08.png new file mode 100644 index 000000000..0bd6b19ce Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-09.png new file mode 100644 index 000000000..bd3ff1b9f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-bus-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-01.gif new file mode 100644 index 000000000..27c31ab25 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-02.png new file mode 100644 index 000000000..37cd5097f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-03.png new file mode 100644 index 000000000..f2d432b98 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-04.png new file mode 100644 index 000000000..ee242a673 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-05.png new file mode 100644 index 000000000..ce031bdfd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-06.png new file mode 100644 index 000000000..8610f2352 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-07.png new file mode 100644 index 000000000..3f6a559d6 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-feign-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-stream-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-stream-01.png new file mode 100644 index 000000000..481913e1b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-stream-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-stream-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-stream-02.png new file mode 100644 index 000000000..5b804eea9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-stream-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-stream-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-stream-03.png new file mode 100644 index 000000000..be5655d9c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-springcloud-stream-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-00.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-00.png new file mode 100644 index 000000000..05634d994 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-00.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-01.png new file mode 100644 index 000000000..a03eb1b8b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-02.png new file mode 100644 index 000000000..5c8f8ef7e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-03.png new file mode 100644 index 000000000..56b764aab Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-04.png new file mode 100644 index 000000000..2fa9a9ed0 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-05.png new file mode 100644 index 000000000..fe4fe503a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-06.png new file mode 100644 index 000000000..4bd235308 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-07.png new file mode 100644 index 000000000..56fe219e1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-08.png new file mode 100644 index 000000000..0b90aaa6d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-09.png new file mode 100644 index 000000000..f7245e0a1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-10.png new file mode 100644 index 000000000..2427794df Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-11.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-11.png new file mode 100644 index 000000000..8af3ee553 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-12.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-12.png new file mode 100644 index 000000000..527603e4f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-01.png new file mode 100644 index 000000000..079bfd518 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-02.png new file mode 100644 index 000000000..1e9496811 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-03.png new file mode 100644 index 000000000..9c077330f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-04.png new file mode 100644 index 000000000..143d3e1df Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-05.png new file mode 100644 index 000000000..1c5a64742 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-06.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-06.png new file mode 100644 index 000000000..0de4025b4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-07.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-07.png new file mode 100644 index 000000000..2a19dc659 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-08.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-08.png new file mode 100644 index 000000000..e1bc32085 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-09.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-09.png new file mode 100644 index 000000000..4bd1dab04 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-10.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-10.png new file mode 100644 index 000000000..28e2938ea Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-11.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-11.png new file mode 100644 index 000000000..04ef472c5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-12.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-12.png new file mode 100644 index 000000000..cf660de65 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-13.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-13.png new file mode 100644 index 000000000..304c15805 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-14.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-14.png new file mode 100644 index 000000000..dc0e5b860 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-15.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-15.png new file mode 100644 index 000000000..1c66b6e3f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-16.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-16.png new file mode 100644 index 000000000..d8556ce98 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-17.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-17.png new file mode 100644 index 000000000..57d703018 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-17.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-18.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-18.png new file mode 100644 index 000000000..0c14336d4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-ssl-httpsok-18.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-01.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-01.png new file mode 100644 index 000000000..d2ba38fc9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-02.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-02.png new file mode 100644 index 000000000..0074e1d40 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-03.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-03.png new file mode 100644 index 000000000..3c5cf3e9e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-04.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-04.png new file mode 100644 index 000000000..d8a850661 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-05.png b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-05.png new file mode 100644 index 000000000..ae0fcd364 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/roadmap-zookeeper-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-01.gif new file mode 100644 index 000000000..69b2da8d2 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-02.png b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-02.png new file mode 100644 index 000000000..fb10eef7d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-03.png b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-03.png new file mode 100644 index 000000000..b963cdaa7 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-04.png b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-04.png new file mode 100644 index 000000000..e8aebb8c4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-05.png b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-05.png new file mode 100644 index 000000000..f5be72464 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-06.png b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-06.png new file mode 100644 index 000000000..cca1aae7f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-07.png b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-07.png new file mode 100644 index 000000000..becd3d3e5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/spring-oauth2-sso-01-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-01.gif new file mode 100644 index 000000000..b2a5111d9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-02.png b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-02.png new file mode 100644 index 000000000..287300c18 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-03.png b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-03.png new file mode 100644 index 000000000..39f54fa22 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-04.png b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-04.png new file mode 100644 index 000000000..41f2751b8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-05.png b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-05.png new file mode 100644 index 000000000..af0377bc1 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-06.png b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-06.png new file mode 100644 index 000000000..26296d9bf Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-07.png b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-07.png new file mode 100644 index 000000000..c6595d2e8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-08.png b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-08.png new file mode 100644 index 000000000..e42001ebd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/springcloud-gateway-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-01.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-01.png new file mode 100644 index 000000000..eaca958f5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-02.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-02.png new file mode 100644 index 000000000..6e94fd966 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-03.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-03.png new file mode 100644 index 000000000..4f0560806 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-04.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-04.png new file mode 100644 index 000000000..d2a515562 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-05.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-05.png new file mode 100644 index 000000000..6d33e3e4a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-06.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-06.png new file mode 100644 index 000000000..5ec1f32e4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-07.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-07.png new file mode 100644 index 000000000..567c15aa8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-08.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-08.png new file mode 100644 index 000000000..9acac8d2a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-09.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-09.png new file mode 100644 index 000000000..4ff79f477 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-10.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-10.png new file mode 100644 index 000000000..e911f2c96 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-11.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-11.png new file mode 100644 index 000000000..507dc0a3b Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-12.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-12.png new file mode 100644 index 000000000..75cc7296a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-13.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-13.png new file mode 100644 index 000000000..132eb1299 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-dump-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-01.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-01.png new file mode 100644 index 000000000..88d1619bd Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-01.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-02.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-02.png new file mode 100644 index 000000000..a68e9e76f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-03.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-03.png new file mode 100644 index 000000000..c625f1a3a Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-04.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-04.png new file mode 100644 index 000000000..1ccd842ba Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-05.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-05.png new file mode 100644 index 000000000..c633cd888 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-06.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-06.png new file mode 100644 index 000000000..018312ee8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-07.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-07.png new file mode 100644 index 000000000..8e8d0b204 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-08.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-08.png new file mode 100644 index 000000000..0fd1bdb8d Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-09.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-09.png new file mode 100644 index 000000000..72737e3d5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-dev-tech-visualvm-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-01.gif b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-01.gif new file mode 100644 index 000000000..28172dc73 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-01.gif differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-02.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-02.png new file mode 100644 index 000000000..292123479 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-02.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-03.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-03.png new file mode 100644 index 000000000..b4dd78ac5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-03.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-04.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-04.png new file mode 100644 index 000000000..8d152b9f4 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-04.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-05.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-05.png new file mode 100644 index 000000000..ce2366ce5 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-05.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-06.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-06.png new file mode 100644 index 000000000..8c7a17b44 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-06.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-07.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-07.png new file mode 100644 index 000000000..e93dc1618 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-07.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-08.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-08.png new file mode 100644 index 000000000..a658a0ec9 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-08.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-09.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-09.png new file mode 100644 index 000000000..fcb3bc090 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-09.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-10.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-10.png new file mode 100644 index 000000000..440e56981 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-10.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-11.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-11.png new file mode 100644 index 000000000..dd9ec0b47 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-11.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-12.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-12.png new file mode 100644 index 000000000..81ff2047f Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-12.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-13.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-13.png new file mode 100644 index 000000000..882974265 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-13.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-14.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-14.png new file mode 100644 index 000000000..6c160034e Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-14.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-15.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-15.png new file mode 100644 index 000000000..5d465d4b8 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-15.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-16.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-16.png new file mode 100644 index 000000000..167db046c Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-16.png differ diff --git a/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-17.png b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-17.png new file mode 100644 index 000000000..3d0d50696 Binary files /dev/null and b/docs/.vuepress/public/images/roadmap/tutorial/xfg-frame-archetype-17.png differ diff --git "a/docs/.vuepress/public/images/roadmap/tutorial/\345\260\201\351\235\242.png" "b/docs/.vuepress/public/images/roadmap/tutorial/\345\260\201\351\235\242.png" new file mode 100644 index 000000000..237cdbe57 Binary files /dev/null and "b/docs/.vuepress/public/images/roadmap/tutorial/\345\260\201\351\235\242.png" differ diff --git a/docs/.vuepress/public/images/system/avatar.jpg b/docs/.vuepress/public/images/system/avatar.jpg new file mode 100644 index 000000000..b75822f13 Binary files /dev/null and b/docs/.vuepress/public/images/system/avatar.jpg differ diff --git a/docs/.vuepress/public/images/system/chatgpt-group.png b/docs/.vuepress/public/images/system/chatgpt-group.png new file mode 100644 index 000000000..fd8fecdab Binary files /dev/null and b/docs/.vuepress/public/images/system/chatgpt-group.png differ diff --git a/docs/.vuepress/public/images/system/code.png b/docs/.vuepress/public/images/system/code.png new file mode 100644 index 000000000..784062316 Binary files /dev/null and b/docs/.vuepress/public/images/system/code.png differ diff --git a/docs/.vuepress/public/images/system/emote/emote-01.png b/docs/.vuepress/public/images/system/emote/emote-01.png new file mode 100644 index 000000000..d5183f262 Binary files /dev/null and b/docs/.vuepress/public/images/system/emote/emote-01.png differ diff --git a/docs/.vuepress/public/images/system/interview.png b/docs/.vuepress/public/images/system/interview.png new file mode 100644 index 000000000..6f845cee4 Binary files /dev/null and b/docs/.vuepress/public/images/system/interview.png differ diff --git a/docs/.vuepress/public/images/system/openai-sdk-group.png b/docs/.vuepress/public/images/system/openai-sdk-group.png new file mode 100644 index 000000000..e5a15a27e Binary files /dev/null and b/docs/.vuepress/public/images/system/openai-sdk-group.png differ diff --git a/docs/.vuepress/public/images/system/system-introduce-01.png b/docs/.vuepress/public/images/system/system-introduce-01.png index 3521635bd..2eccd7bfe 100644 Binary files a/docs/.vuepress/public/images/system/system-introduce-01.png and b/docs/.vuepress/public/images/system/system-introduce-01.png differ diff --git a/docs/.vuepress/public/images/system/zsxq-project.png b/docs/.vuepress/public/images/system/zsxq-project.png index 4bbeea827..4dc6acd1d 100644 Binary files a/docs/.vuepress/public/images/system/zsxq-project.png and b/docs/.vuepress/public/images/system/zsxq-project.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/coupon.png b/docs/.vuepress/public/images/system/zsxq/coupon.png new file mode 100644 index 000000000..0571208d4 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/coupon.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-00.png b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-00.png new file mode 100644 index 000000000..80b141765 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-00.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-01.png b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-01.png new file mode 100644 index 000000000..89cf304b5 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-01.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-02.png b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-02.png new file mode 100644 index 000000000..b0a02e5b6 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-02.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-03.png b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-03.png new file mode 100644 index 000000000..d38e22573 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-03.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-04.png b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-04.png new file mode 100644 index 000000000..c5c5edf7a Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/xingqiu-231018-04.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-ai.jpeg b/docs/.vuepress/public/images/system/zsxq/zsxq-ai.jpeg new file mode 100644 index 000000000..d04c86c96 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-ai.jpeg differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-booklet.png b/docs/.vuepress/public/images/system/zsxq/zsxq-booklet.png new file mode 100644 index 000000000..fb53fe268 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-booklet.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-introduce.png b/docs/.vuepress/public/images/system/zsxq/zsxq-introduce.png new file mode 100644 index 000000000..7e272db92 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-introduce.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-project-01.png b/docs/.vuepress/public/images/system/zsxq/zsxq-project-01.png new file mode 100644 index 000000000..f8c8c72ea Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-project-01.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-project-02.png b/docs/.vuepress/public/images/system/zsxq/zsxq-project-02.png new file mode 100644 index 000000000..4362d3326 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-project-02.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-project-03.png b/docs/.vuepress/public/images/system/zsxq/zsxq-project-03.png new file mode 100644 index 000000000..e645302d2 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-project-03.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-project-04.png b/docs/.vuepress/public/images/system/zsxq/zsxq-project-04.png new file mode 100644 index 000000000..d04fb2e50 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-project-04.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-project-05.png b/docs/.vuepress/public/images/system/zsxq/zsxq-project-05.png new file mode 100644 index 000000000..d61d5a377 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-project-05.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-project-06.png b/docs/.vuepress/public/images/system/zsxq/zsxq-project-06.png new file mode 100644 index 000000000..a48683b4f Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-project-06.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-project-07.png b/docs/.vuepress/public/images/system/zsxq/zsxq-project-07.png new file mode 100644 index 000000000..0a5d62c25 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-project-07.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-project-08.png b/docs/.vuepress/public/images/system/zsxq/zsxq-project-08.png new file mode 100644 index 000000000..6700f3a9c Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-project-08.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-project-09.png b/docs/.vuepress/public/images/system/zsxq/zsxq-project-09.png new file mode 100644 index 000000000..8536f63fe Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-project-09.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-user-guide.png b/docs/.vuepress/public/images/system/zsxq/zsxq-user-guide.png new file mode 100644 index 000000000..5a529e191 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-user-guide.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-xuanchuan.png b/docs/.vuepress/public/images/system/zsxq/zsxq-xuanchuan.png new file mode 100644 index 000000000..28a00533b Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-xuanchuan.png differ diff --git a/docs/.vuepress/public/images/system/zsxq/zsxq-xufei.jpeg b/docs/.vuepress/public/images/system/zsxq/zsxq-xufei.jpeg new file mode 100644 index 000000000..d92fa7791 Binary files /dev/null and b/docs/.vuepress/public/images/system/zsxq/zsxq-xufei.jpeg differ diff --git a/docs/.vuepress/styles/index.styl b/docs/.vuepress/styles/index.styl index 3e91100ce..1ef828975 100755 --- a/docs/.vuepress/styles/index.styl +++ b/docs/.vuepress/styles/index.styl @@ -30,4 +30,12 @@ h2 // sidebar .sidebar width: 18rem - font-size: 15px \ No newline at end of file + font-size: 15px + +.page-bottom-footer { + text-align: center; + padding: 15px; + font-size: .8rem; + font-weight: 400; + color: var(--c-text-lightest) +} \ No newline at end of file diff --git a/docs/.vuepress/theme/components/NavLinks.vue b/docs/.vuepress/theme/components/NavLinks.vue index 98bd7280f..8c3c2a9fa 100755 --- a/docs/.vuepress/theme/components/NavLinks.vue +++ b/docs/.vuepress/theme/components/NavLinks.vue @@ -118,6 +118,10 @@ export default { + + +

Welcome to nginx!

+

If you see this page, the nginx web server is successfully installed and +working. Further configuration is required.

+ +

For online documentation and support please refer to +nginx.org.
+Commercial support is available at +nginx.com.

+ +

Thank you for using nginx.

+ + +root@ed8dc07f2ae6:/usr/share/nginx/html# +root@ed8dc07f2ae6:/usr/share/nginx/html# exit +exit +``` + +- 配置:`/etc/nginx` +- 网页:`/usr/share/nginx/html` + +### 2. 拷贝 Nginx + +创建目录 + +```shell +[root@vultr ~]# mkdir -p /data/nginx/conf +[root@vultr ~]# mkdir -p /data/nginx/html +``` + +拷贝文件 + +```shell +[root@vultr ~]# docker container cp Nginx:/etc/nginx/nginx.conf /data/nginx/conf +[root@vultr ~]# docker container cp Nginx:/usr/share/nginx/html/index.html /data/nginx/html +``` + +查看信息 + +```shell +[root@vultr ~]# ls /data/nginx/conf/ +nginx.conf +[root@vultr ~]# ls /data/nginx/html/ +index.html +``` + +### 3. 部署 Nginx + +```shell +docker run \ +--restart always \ +--name Nginx \ +-d \ +-v /data/nginx/html:/usr/share/nginx/html \ +-v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ +-p 80:80 \ +nginx +``` + +- 重启:`sudo service nginx restart` + + +## 三、证书安装 + +### 4.1 创建证书 + +SSL 免费的证书,在各个云服务厂商都有提供,可以自己申请。这里以阿里云举例; + +阿里云免费域名证书:[https://yundun.console.aliyun.com/?p=cas#/certExtend/free/cn-hangzhou](https://yundun.console.aliyun.com/?p=cas#/certExtend/free/cn-hangzhou) + +![](https://bugstack.cn/images/article/devops/dev-ops-nginx-230418-02.png) + +- 步骤1;通过免费的方式创建 SSL,之后通过引导的 DNS 方式进行验证。其实就是在你的域名里配置下验证信息。 +- 步骤2;申请后,3-5分钟左右 DNS 会验证通过,这个时候你直接下载 Nginx 的 SSL 包即可。里面有2个文件【x.key、x.pem】 + +### 4.2 准备内容 + +#### 4.2.1 单个证书 + +- 把下载好的 SSL 文件解压到桌面,你会得到一个文件夹,里面含有 x.key、x.pem 两个文件。 +- 创建一个 default.conf 这个文件配置的 SSL 信息 + +```conf +server { + listen 80; + listen [::]:80; + server_name openai.xfg.im; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name openai.xfg.im; + + ssl_certificate /etc/nginx/ssl/9740289_openai.xfg.im.pem; + ssl_certificate_key /etc/nginx/ssl/9740289_openai.xfg.im.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +- 你可以复制这份文件,在自己本地创建。注意修改域名和SSL文件路径。 + +#### 4.2.2 多个证书 + +如果你需要给1个以上的域名配置SSL,那么可以配置多组 server 如下; + +```shell script +server { + listen 80; + listen [::]:80; + server_name itedus.cn; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name itedus.cn; + + ssl_certificate /etc/nginx/ssl/9750021_itedus.cn.pem; + ssl_certificate_key /etc/nginx/ssl/9750021_itedus.cn.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} + +server { + listen 80; + listen [::]:80; + server_name chatgpt.itedus.cn; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name chatgpt.itedus.cn; + + ssl_certificate /etc/nginx/ssl/9749920_chatgpt.itedus.cn.pem; + ssl_certificate_key /etc/nginx/ssl/9749920_chatgpt.itedus.cn.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_pass http://180.76.119.100:3002; + proxy_http_version 1.1; + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +### 4.3 上传文件 + +你可以通过 `SFTP` 工具或者 `mkdir -p`、`touch` 命令创建一些服务器本地用于映射的文件夹和文件,这里小傅哥使用了 [Termius](https://www.termius.com/) 工具进行创建操作。 + +![](https://bugstack.cn/images/article/devops/dev-ops-nginx-230418-03.png) + +- 文件1;html +- 文件2;ssl - 把本地的 ssh 文件上传进来 +- 文件3;conf - 在 conf 下有个 `conf.d` 的文件夹,把 `default.conf` 上传进去。而 nginx.conf 传到 conf 中。 +- 文件4;logs - 创建日志 + +### 4.4 启动服务 + +在 nginx.conf 的配置文件有这么一句;`include /etc/nginx/conf.d/*.conf;` 那么只要是 conf.d 文件夹下的文件都会被加载。所以直接在 conf.d/default.conf 配置 SSL 就会被加载。接下来重新安装 Nginx 即可。`安装前记得删除 Nginx 你可以用命令【docker stop Nginx、docker rm Nginx】或者在 Portainer 中操作即可` + +```shell +docker run \ +--name Nginx \ +-p 443:443 -p 80:80 \ +-v /data/nginx/logs:/var/log/nginx \ +-v /data/nginx/html:/usr/share/nginx/html \ +-v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ +-v /data/nginx/conf/conf.d:/etc/nginx/conf.d \ +-v /data/nginx/ssl:/etc/nginx/ssl/ \ +--privileged=true -d --restart=always nginx +``` + +![](https://bugstack.cn/images/article/devops/dev-ops-nginx-230418-04.png) + +## 五、重定向 + +### 1. default.conf + +在 default.conf 中添加如下配置后重启 Nginx 即可; + +```shell +location /d5fe/ { + rewrite ^/d5fe/(.*)$ /$1 break; + proxy_pass https://api.x.com; + proxy_ssl_server_name on; + proxy_set_header Host api.x.com; + proxy_set_header Connection ''; + proxy_http_version 1.1; + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; +} +``` + +### 2. auth_request + +```shell +server { + listen 80; + listen [::]:80; + server_name api.xfg.im; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name api.xfg.im; + + ssl_certificate /etc/nginx/ssl/9877497_api.xfg.im.pem; + ssl_certificate_key /etc/nginx/ssl/9877497_api.xfg.im.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + root /usr/share/nginx/html; + index index.html index.htm; + } + + location /abc/ { + auth_request /auth; + rewrite ^/abc/(.*)$ /$1 break; + proxy_pass https://api.x.com; + proxy_ssl_server_name on; + proxy_set_header Host api.x.com; + proxy_set_header Connection ''; + proxy_http_version 1.1; + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location = /auth { + # 发送子请求到HTTP服务,验证客户端的凭据,返回响应码 + internal; + # 设置参数 + set $query ''; + if ($request_uri ~* "[^\?]+\?(.*)$") { + set $query $1; + } + # 验证成功,返回200 OK + proxy_pass http://207.246.123.*:8090/auth/token?$query; + # 发送原始请求 + proxy_pass_request_body off; + # 清空 Content-Type + proxy_set_header Content-Type ""; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +--- + +**其他资料**:[Nginx 简明教程 @dunwu](https://dunwu.github.io/nginx-tutorial/#/nginx-quickstart) - 非常适合学习Nginx配置。 + diff --git a/docs/md/devops/2023-04-18-portainer.md b/docs/md/devops/2023-04-18-portainer.md new file mode 100644 index 000000000..198531a1c --- /dev/null +++ b/docs/md/devops/2023-04-18-portainer.md @@ -0,0 +1,71 @@ +--- +title: Portainer +lock: need +--- + +# Portainer 环境配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- 官网:[https://www.portainer.io/](https://www.portainer.io/) +- 介绍:在任何数据中心、云、网络边缘或 IIOT 设备的 Kubernetes、Docker、Swarm 和 Nomad 上,在几分钟内部署、配置、故障排除和保护容器。 + +## 一、基础安装 + +### 1. 拉取最新的 Portainer + +```java +[root@CodeGuide portainer]# docker pull portainer/portainer +Using default tag: latest +latest: Pulling from portainer/portainer +94cfa856b2b1: Pull complete +49d59ee0881a: Pull complete +a2300fd28637: Pull complete +Digest: sha256:fb45b43738646048a0a0cc74fcee2865b69efde857e710126084ee5de9be0f3f +Status: Downloaded newer image for portainer/portainer:latest +docker.io/portainer/portainer:latest +``` + +- docker pull portainer/portainer +- 拉取 portainer + +### 2. 安装和启动 + +```java +[root@CodeGuide]# docker run -d --restart=always --name portainer -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer +``` + +### 3. 访问 Portainer + +- 地址:[http://39.96.*.*:9000/](#) +- 操作:登录后设置你的用户名和密码,并设置本地Docker即可,设置完成后,如下 + +
+ +
+ +
+ +
+ +## 二、链接服务 + +地址:[http://180.76.119.142:9000/#!/wizard/endpoints/create?envType=dockerStandalone](http://180.76.119.142:9000/#!/wizard/endpoints/create?envType=dockerStandalone) + +
+ +
+ +```shell script +docker run -d \ + -p 9001:9001 \ + --name portainer_agent \ + --restart=always \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v /var/lib/docker/volumes:/var/lib/docker/volumes \ + portainer/agent:2.16.2 +``` + diff --git a/docs/md/devops/2023-04-18-tool.md b/docs/md/devops/2023-04-18-tool.md new file mode 100644 index 000000000..b48153b94 --- /dev/null +++ b/docs/md/devops/2023-04-18-tool.md @@ -0,0 +1,36 @@ +--- +title: Tool +lock: need +--- + +# Tool 工具整理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## SSH 客户端工具 + +1. [nuoshell](https://www.nuoshell.com/) - 一款免费的专门为 Mac OSX 系统打造的,集多功能于一身的 SSH 客户端工具。 +2. [termius](https://termius.com/) - 含付费Plus功能,但基础功能已够用。可以支持电脑和移动设备使用,包括 SFTP 功能。 +3. [WindTerm](https://github.com/kingToolbox/WindTerm) - 专业的跨平台SSH/Sftp/Shell/Telnet/串口终端。完全免费,可用于商业和非商业用途,无任何限制。 +4. [SSH Config Editor](http://hejki.tilda.ws/ssheditor) - 一款用于管理OpenSSH ssh客户端配置文件的工具。 +5. [ZOC](https://www.emtec.com/zoc/) - ZOC • 适用于 macOS 和 Windows 的 SSH 客户端和终端仿真器。 +6. [SecureCRT](https://mobaxterm.mobatek.net/) - 带有 X11 服务器、选项卡式 SSH 客户端、网络工具等的增强型 Windows 终端。 +7. [Electerm](https://github.com/electerm/electerm) - 开源终端/ssh/telnet/serialport/sftp客户端(linux, mac, win)。 +8. [PuTTY](https://www.putty.org/) - PuTTY 是一个 SSH 和 telnet 客户端,最初由 Simon Tatham 为 Windows 平台开发。PuTTY 是开源软件,提供源代码,由一群志愿者开发和支持。 +9. [FinalShell SSH工具](http://www.hostbuf.com/t/988.html) - FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还是功能强大的开发,运维工具,充分满足开发,运维需求. +10. [Xterminal](https://www.terminal.icu/) - 不仅是强大的SSH工具,更提供本地控制台,以及更多即将推出的开发相关功能,让您专注于创造卓越的代码。 + +## MySql 客户端工具 + +1. [DBeaver Community - Free Universal Database Tool](https://dbeaver.io/download/) - `MySQL、Azure SQL Server、Apache Ignite...` +2. [Sequel Pro](https://www.sequelpro.com/) - `不太推荐了,有bug` +3. [Sequel Ace](https://apps.apple.com/us/app/sequel-ace/id1518036000?ls=1) - `推荐,是 Sequel Pro 的扩展维护版。免费好用!` +4. [Navicat](https://www.navicat.com.cn/products) - `同好用的就是收费,破解的在公司用可不行` +5. [IntelliJ Datagrip](https://www.jetbrains.com/zh-cn/datagrip/) - `免费使用30天` + +--- + +个人在使用 nuoshell、termius 挺稳又好用。 diff --git a/docs/md/devops/2024-01-30-vuepress-resume-blog.md b/docs/md/devops/2024-01-30-vuepress-resume-blog.md new file mode 100644 index 000000000..8e581b924 --- /dev/null +++ b/docs/md/devops/2024-01-30-vuepress-resume-blog.md @@ -0,0 +1,243 @@ +--- +title: 搭建博客,展示过往经历。让面试官更了解我,面试更稳了! +lock: need +--- + +# 搭建博客,展示过往经历。让面试官更了解我,面试更稳了! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +不少小伙伴都想放在简历上放一个动态博客链接,展示自己的过往技术经历和可运行的项目展示。通过这样的方式提高自己在面试中的竞争力。但因为没做过,又不知道从哪开始,找的一些案例教程又都很复杂。所以,小傅哥来帮你一下,让你SoEasy的搞定这东西。 + +
+ +
+ +当这个男人👨🏻准备下手的时候,一切技术将变得简单、容易,好理解! + +我把本次的分内容定义为简历博客,并已经做好了博客的框架([vuepress](https://theme-hope.vuejs.press/zh/))和内容案例,以及部署需要的Nginx配置。如果你需要部署到线上,那么需要准备一台云服务器([2c2g 50元/1年](https://gaga.plus/yun.html))。另外你还可以注册一个域名(ICP备案2周),这样就可以通过域名访问你的专属博客了。【要我说,每个程序员👨🏻‍💻都应该有一个自己的域名,有了它你才有了做一些事情的可能】。**接下来我会带着你搭建一个如图所示的个人简历博客** + +
+ +
+ +那么,接下来小傅哥就带着你,完成下简历博客的搭建。 + +>文末提供了8个Java实战项目,嘎嘎提升简历竞争力。💐 + +## 一、搭建准备 + +- 简历博客项目:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-blog](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-blog) +- 一台云服务器,2c2g 推荐 50元/1年 [https://gaga.plus/yun.html](https://gaga.plus/yun.html) +- 云服务器搭建 Docker、安装 Portainer 教程地址(包括学习视频):[https://bugstack.cn/md/road-map/docker.html](https://bugstack.cn/md/road-map/docker.html) +- 域名和域名备案(ICP备案2周),未购买也可以完成本节课程的操作。 +- SSL 申请,如果有域名了,可以申请免费的 SSL 让域名地址支持 HTTPS。申请地址:[https://yundun.console.aliyun.com/?p=cas#/certExtend/free/cn-hangzhou](https://yundun.console.aliyun.com/?p=cas#/certExtend/free/cn-hangzhou) +- Node.js 安装,地址:[https://nodejs.org/en/download](https://nodejs.org/en/download) - `选择自己机器适配的版本安装即可` + +## 二、项目说明 + +这里小傅哥已经把博客框架、内容案例、部署博客的环境,都准备好了。你只需要下载到本地就可以使用。 + +
+ +
+ +本博客采用 [VuePress Theme Hope](https://theme-hope.vuejs.press/zh/) 模板进行搭建。当你能完成部署本博客后,还可以参考模板做一些其他的扩展以及各类喜欢插件的安装。 + +- src 目录下,为博客的框架模板和已经写好的案例。在 `package.json`中有一个 `docs:build` 用于构建,还有一个 `docs:dev`用于本地启动测试。 +- dev-ops 提供的是博客的部署,这里的 docker-compose.yml 是 Docker 执行脚本安装 Nginx 环境。 +- 小傅哥在 `src/.vuepress-> config.ts`文件中下配置了把项目 build 到 nginx/html 文件夹。这样可以更加方便我们部署。 + +## 三、部署项目 + +当你把 xfg-dev-tech-blog 项目检出到本地,使用 IntelliJ IDEA 或者 WebStrom 工具打开后。会提示你执行 npm install 安装环境。如果没有提示你也可以自己在项目 Terminal 下执行。执行完安装就可以做博客的部署了。 + +
+ +
+ +### 1. 构建项目 + +
+ +
+ +- 打开项目的 `package.json`里面有`构建`和`运行`命名。 +- 构建的操作会把工程打包为 HTML 文件写入到 dev-ops/nginx/html 文件夹下。 +- 运行的操作会直接本地启动服务,启动后你可以在本地预览。这样你在修改一些内容的时候也可以随时看到效果。 + +### 2. 构建结果 + +
+ +
+ +- 如步骤操作,完成构建。当你看到 `dev-ops/nginx/html` 下有对应的一堆文件了,说明构建成功了。 + +### 3. Nginx 配置 + +对于 Nginx 的配置提供了3种方式,分别包括;IP访问、HTTP域名访问、HTTPS域名访问。 + +
+ +
+ +#### 1. localhost.conf + +这个配置方式为了方便,没有域名的伙伴们测试使用。也就是你部署后可以直接通过IP进行访问。 + +```shell +server { + listen 80; + listen [::]:80; + + location / { + root /usr/share/nginx/html; + index index.html; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + +} +``` + +- 直接把 80 请求转发到本地的根目录地址的 html 文件上即可 + +#### 2. http.blog.conf + +##### 2.1 配置域名解析 + +
+ +
+ +- 如果你有云服务器和域名,那么可以先如图,配置下域名解析地址。 + +##### 2.2 nginx 脚本 + +```shell +server { + listen 80; + listen [::]:80; + server_name blog.gaga.plus; + + location / { + root /usr/share/nginx/html; + index index.html; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + +} +``` + +#### 3. https.blog.conf + +如果你有域名那么还可以配置免费的ssl验证,配置完成后把 ssl key、pem 文件放到 dev-ops/nginx/ssl 文件夹下并上传到云服务器上。详细介绍:[https://bugstack.cn/md/devops/2023-04-18-nginx.html#_4-1-%E5%88%9B%E5%BB%BA%E8%AF%81%E4%B9%A6](https://bugstack.cn/md/devops/2023-04-18-nginx.html#_4-1-%E5%88%9B%E5%BB%BA%E8%AF%81%E4%B9%A6) + +```shell +server { + listen 80; + listen [::]:80; + server_name blogs.gaga.plus; + + rewrite ^(.*) https://$server_name$1 permanent; +} + +server { + listen 443 ssl; + server_name api.gaga.plus; + + ssl_certificate /etc/nginx/ssl/blogs.gaga.plus.pem; + ssl_certificate_key /etc/nginx/ssl/blogs.gaga.plus.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + root /usr/share/nginx/html; + index index.html; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +- 这个方式就是配置 ssl 的方式。 + +### 4. 上传文件 + +
+ +
+ +- 小傅哥这里使用的是 [https://termius.com/](https://termius.com/) 连接云服务器的 SSH 工具,自带 SFTP 。`你也可以使用其他 SSH/FTP 工具` +- 通过这个工具,连接云服务器,并把我们在工程下创建的 `dev-ops` 上传到云服务器端。 + +### 5. 服务启动 + +接下来,你只需要在云服务器进入到 dev-ops 文件夹下,执行脚本 `docker-compose -f docker-compose.yml up -d` 即可完成部署。 + +
+ +
+ +
+ +
+ +- 在你初次部署的时候,会拉取 Nginx 镜像。拉取完成后则会自动部署。 +- 部署完成后可以进入到 portainer 中查看 nginx 运行,以及进入到日志中查看运行情况。 + +### 6. 访问博客 + +- IP地址:[http://117.72.37.243/](http://117.72.37.243/) +- HTTP地址:[http://blog.gaga.plus/](http://blog.gaga.plus/) +- HTTPS地址:[https://blogs.gaga.plus/](https://blogs.gaga.plus/) + +
+ +
+ +
+ +
+ +**全部完成撒花 💐** 现在你就可以在简历上挂一个自己的博客了,还可以在博客中秀出自己的技术肌肉💪🏻,赢得面试官的青睐! + +>当然,有了简历博客,最好配有一套对应的高质量实战项目,撑起简历的技术度。那些CRUD/XXX管理系统,现在是越来越没竞争力了。早点学一些,每天积累一点,就能让自己有更多的选择。 + +## 四、实战项目 + +小傅哥工作10年+,深刻感受到在不同阶段(毕业~3年,3年~5年、5年+)都要有对应的能力,才能踏上换乘的列车。为了让自己的职业生涯走的更加稳妥,就不要只把自己当成满足工作需要的工具。早些积累、早些积累自己的技术栈体系闭环。这是非常重要的!!! + +>小傅哥把工作中的技术经验,陆续的编写出对应的博客和实战项目,整整写了3年多。这些内容可以让一个研发小白成长为技术大牛! + +
+ +
+ +**加入学习 https://gaga.plus** + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +**注意📢**,加入星球后你可以申请加入星球VIP群,获得高质量技术交流。此外星球的所有内容,都从星球的**课程入口**进入,里面做了非常细致的归档。 \ No newline at end of file diff --git a/docs/md/devops/2024-03-23-yun.md b/docs/md/devops/2024-03-23-yun.md new file mode 100644 index 000000000..0c71dd2b6 --- /dev/null +++ b/docs/md/devops/2024-03-23-yun.md @@ -0,0 +1,66 @@ +--- +title: 云服务器 +lock: need +--- + +# 云服务器,2c2g 1年50、2c4g 1年126、4c16g 1月26.5 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +不少学习编程的伙伴,都有把项目部署到云服务器,实践真实场景锻炼编程能力的诉求。包括;`云服务器配置`、`上线环境安装`、`安全组开通`,`以及部署后的性能压测`。这些真实的操作都是非常能锻炼人的,只有做过才懂。**😂 这些年,小傅哥的云服务器费用,花费了上万块了都!但很值得,学习到手了很多东西。** + +
+ +
+ +那云服务器有很便宜,很实惠的吗?🤔 + +与过去云服务厂商不多的时候相比,现在的云服务器市场很卷,所以最实惠的也就是咱们这样的小用户。但凡自己或家里有几个没注册过的新用户,都能拿到这么便宜、实惠、好用的云服务器。 + +**💐 2c2g 1年 50元、2c4g 1年126元【推荐】、4c16g 3个月80元,2c4g 3年 558【推荐】、2c8g 3年998** —— 优惠链接:[https://618.gaga.plus/](https://618.gaga.plus/) + +
+ +
+ +
+ +
+ +优惠链接:[https://yun.xfg.plus/](https://yun.xfg.plus/) - 我把最优惠的SKU做了汇总,你可以进入选择。点击后,还可以继续选配置。如果项目部署的较大,不要选2c2g。 + +>小傅哥,云服务器我有手✋🏻,我会买。那云服务器的使用教程有吗,能锻炼技能上云的项目有吗?🤔 —— 当然!有!你傅哥不就是卷这些的吗,管项目都卷了11万行代码给大家。 + +## 云服务器教程 + +
+ +
+ +- 关于云服务器的配置选择、环境安装、空间占用等各类实战的技巧,我都做了相关的视频,你只要跟着搞就可以。一次学习,永久收获! +- 注意:日常简单测试安装个轻量的环境,2c2g 会占用到 60%,长期使用推荐 2c4g。分布式所有环境(mysql、redis、nacos、elk、普罗米修斯、canal、前后端应用)需要 2c8g。 + +## 实战项目系列 + +小傅哥就是个实战派,1年、3年、2c2g、2c4g、2c8g 的服务器,我都有买,分别部署了不同的项目,自己可以锻炼,也让大家可以学习。地址:[https://gaga.plus](https://gaga.plus) - 进入后可以看到实战项目和部署演示。 + +
+ +
+ +
+ +
+ +在 `嘎嘎强 gaga.plus` 中有大量的实战项目,你都可以学习部署到云服务器上。而且会专门写好的教程和录制的手把手视频,教你从服务器选择、环境安装、应用构建、镜像部署、监控配置、应用压测,一步步带着你做。 + +## 嘎嘎实惠加入 + +小傅哥是一个大厂的架构师,经常会带着伙伴们,卷这些实际场景中非常有必要的技术。也会带着伙伴实战项目,这些项目也都是来自于互联网大厂中真实的业务场景,所有学习这样的项目无论是实习、校招、社招,都是有非常强的竞争力。别人还在玩玩具,而你已经涨能力! + +>🧧在小傅哥星球「码农会锁」有8个实战项目,每个都是从0到1开发并提供简历模板和面试题,并且还在继续开发,后续还将有更多!价格嘎嘎实惠,早点加入,早点提升自己。项目地址:https://gaga.plus \ No newline at end of file diff --git "a/docs/md/java/core/2019-12-21-[\346\234\211\347\202\271\345\271\262\350\264\247]JDK\343\200\201CGLIB\345\212\250\346\200\201\344\273\243\347\220\206\344\275\277\347\224\250\344\273\245\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/docs/md/java/core/2019-12-21-[\346\234\211\347\202\271\345\271\262\350\264\247]JDK\343\200\201CGLIB\345\212\250\346\200\201\344\273\243\347\220\206\344\275\277\347\224\250\344\273\245\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" index ebc37ce93..243e2e1dd 100644 --- "a/docs/md/java/core/2019-12-21-[\346\234\211\347\202\271\345\271\262\350\264\247]JDK\343\200\201CGLIB\345\212\250\346\200\201\344\273\243\347\220\206\344\275\277\347\224\250\344\273\245\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" +++ "b/docs/md/java/core/2019-12-21-[\346\234\211\347\202\271\345\271\262\350\264\247]JDK\343\200\201CGLIB\345\212\250\346\200\201\344\273\243\347\220\206\344\275\277\347\224\250\344\273\245\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -251,6 +251,6 @@ hi user 10001 ## 综上总结 -- 在我们实际使用中两种方式都用所有使用,也可以依照不同的诉求进行选择 +- 在我们实际使用中两种方式都有所使用,也可以依照不同的诉求进行选择 - 往往动态代理会和注解共同使用,代理类拿到以后获取方法的注解,并做相应的业务操作 - 有时候你是否会遇到增加AOP不生效,因为有时候有些类是被代理操作的,并没有执行你的自定义注解也就是切面 diff --git "a/docs/md/java/core/2020-11-22-\351\271\277\351\274\216\350\256\260 \302\267 \351\237\246\345\260\217\345\256\235\357\274\214\344\270\275\346\230\245\351\231\242\343\200\201\345\244\251\345\234\260\344\274\232\343\200\201\345\205\245\347\232\207\345\256\253\347\255\211\344\272\224\344\270\252\345\234\272\346\231\257\357\274\214\346\220\255\351\205\215\344\270\215\345\220\214\345\211\247\346\203\205\350\256\262\350\247\243\345\244\232\347\272\277\347\250\213\345\222\214\351\224\201\357\274\214\347\234\237\351\246\231\357\274\201.md" "b/docs/md/java/core/2020-11-22-\351\271\277\351\274\216\350\256\260 \302\267 \351\237\246\345\260\217\345\256\235\357\274\214\344\270\275\346\230\245\351\231\242\343\200\201\345\244\251\345\234\260\344\274\232\343\200\201\345\205\245\347\232\207\345\256\253\347\255\211\344\272\224\344\270\252\345\234\272\346\231\257\357\274\214\346\220\255\351\205\215\344\270\215\345\220\214\345\211\247\346\203\205\350\256\262\350\247\243\345\244\232\347\272\277\347\250\213\345\222\214\351\224\201\357\274\214\347\234\237\351\246\231\357\274\201.md" index 2d7b37cb0..4a66f6632 100644 --- "a/docs/md/java/core/2020-11-22-\351\271\277\351\274\216\350\256\260 \302\267 \351\237\246\345\260\217\345\256\235\357\274\214\344\270\275\346\230\245\351\231\242\343\200\201\345\244\251\345\234\260\344\274\232\343\200\201\345\205\245\347\232\207\345\256\253\347\255\211\344\272\224\344\270\252\345\234\272\346\231\257\357\274\214\346\220\255\351\205\215\344\270\215\345\220\214\345\211\247\346\203\205\350\256\262\350\247\243\345\244\232\347\272\277\347\250\213\345\222\214\351\224\201\357\274\214\347\234\237\351\246\231\357\274\201.md" +++ "b/docs/md/java/core/2020-11-22-\351\271\277\351\274\216\350\256\260 \302\267 \351\237\246\345\260\217\345\256\235\357\274\214\344\270\275\346\230\245\351\231\242\343\200\201\345\244\251\345\234\260\344\274\232\343\200\201\345\205\245\347\232\207\345\256\253\347\255\211\344\272\224\344\270\252\345\234\272\346\231\257\357\274\214\346\220\255\351\205\215\344\270\215\345\220\214\345\211\247\346\203\205\350\256\262\350\247\243\345\244\232\347\272\277\347\250\213\345\222\214\351\224\201\357\274\214\347\234\237\351\246\231\357\274\201.md" @@ -204,9 +204,9 @@ public class ReentrantLockTest { } public static void 招收杂役(String name) throws InterruptedException { - lock.lock(); try { - while (true) { + while (!lock.isLocked()) { + lock.lock(); System.out.println(name + ",排队等待进宫当杂役..."); Thread.sleep(1000); } @@ -227,12 +227,16 @@ public class ReentrantLockTest { - **测试结果**: ```java -路人甲,排队等待进宫当杂役... +路人丙,排队等待进宫当杂役... 韦小宝,进宫当太监,不用排队! -路人甲,排队等待进宫当杂役... -路人甲,排队等待进宫当杂役... -路人甲,排队等待进宫当杂役... -路人甲,排队等待进宫当杂役... +韦小宝,排队等待进宫当杂役... +路人庚,排队等待进宫当杂役... +路人丁,排队等待进宫当杂役... +路人乙,排队等待进宫当杂役... +路人戊,排队等待进宫当杂役... +路人己,排队等待进宫当杂役... +路人壬,排队等待进宫当杂役... +路人癸,排队等待进宫当杂役... 路人甲,排队等待进宫当杂役... ``` diff --git "a/docs/md/java/interview/2020-07-28-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \345\274\200\347\257\207\343\200\212\351\235\242\350\257\225\345\256\230\351\203\275\351\227\256\346\210\221\345\225\245\343\200\213.md" "b/docs/md/java/interview/2020-07-28-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \345\274\200\347\257\207\343\200\212\351\235\242\350\257\225\345\256\230\351\203\275\351\227\256\346\210\221\345\225\245\343\200\213.md" index 772769747..b289eb197 100755 --- "a/docs/md/java/interview/2020-07-28-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \345\274\200\347\257\207\343\200\212\351\235\242\350\257\225\345\256\230\351\203\275\351\227\256\346\210\221\345\225\245\343\200\213.md" +++ "b/docs/md/java/interview/2020-07-28-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \345\274\200\347\257\207\343\200\212\351\235\242\350\257\225\345\256\230\351\203\275\351\227\256\346\210\221\345\225\245\343\200\213.md" @@ -15,6 +15,7 @@ lock: need 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn)
原文:[https://mp.weixin.qq.com/s/yXVc6pdu28YAixBjs-bmOg](https://mp.weixin.qq.com/s/yXVc6pdu28YAixBjs-bmOg) +
八股:[https://t.zsxq.com/17UIvdgb3](https://t.zsxq.com/17UIvdgb3) - JavaGuide 星球含有更全、更细、更多的八股系列内容,推荐加入学习。 >沉淀、分享、成长,让自己和他人都能有所收获!😄 @@ -117,6 +118,8 @@ lock: need #### 4.1 技能 +[https://t.zsxq.com/17UIvdgb3](https://t.zsxq.com/17UIvdgb3) - JavaGuide 星球含有更全、更细、更多的八股系列内容,推荐加入学习。 + ##### 4.1.1 Java ###### JDK源码 diff --git "a/docs/md/java/interview/2020-08-04-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2542\347\257\207\343\200\212\346\225\260\346\215\256\347\273\223\346\236\204\357\274\214HashCode\344\270\272\344\273\200\344\271\210\344\275\277\347\224\25031\344\275\234\344\270\272\344\271\230\346\225\260\357\274\237\343\200\213.md" "b/docs/md/java/interview/2020-08-04-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2542\347\257\207\343\200\212\346\225\260\346\215\256\347\273\223\346\236\204\357\274\214HashCode\344\270\272\344\273\200\344\271\210\344\275\277\347\224\25031\344\275\234\344\270\272\344\271\230\346\225\260\357\274\237\343\200\213.md" index cacef48c6..ea3a23e6b 100755 --- "a/docs/md/java/interview/2020-08-04-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2542\347\257\207\343\200\212\346\225\260\346\215\256\347\273\223\346\236\204\357\274\214HashCode\344\270\272\344\273\200\344\271\210\344\275\277\347\224\25031\344\275\234\344\270\272\344\271\230\346\225\260\357\274\237\343\200\213.md" +++ "b/docs/md/java/interview/2020-08-04-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2542\347\257\207\343\200\212\346\225\260\346\215\256\347\273\223\346\236\204\357\274\214HashCode\344\270\272\344\273\200\344\271\210\344\275\277\347\224\25031\344\275\234\344\270\272\344\271\230\346\225\260\357\274\237\343\200\213.md" @@ -89,7 +89,7 @@ The value 31 was chosen because it is an odd prime. If it were even and the mult As Goodrich and Tamassia point out, If you take over 50,000 English words (formed as the union of the word lists provided in two variants of Unix), using the constants 31, 33, 37, 39, and 41 will produce less than 7 collisions in each case. Knowing this, it should come as no surprise that many Java implementations choose one of these constants. ``` -- 这个回答就很有实战意义了,告诉你用超过5千个单词计算hashCode,这个hashCode的运算使用31、33、37、39和41作为乘积,得到的碰撞结果,31被使用就很正常了。 +- 这个回答就很有实战意义了,告诉你用超过5万个单词计算hashCode,这个hashCode的运算使用31、33、37、39和41作为乘积,得到的碰撞结果,31被使用就很正常了。 - **他这句话就就可以作为我们实践的指向了。** ### 3. Hash值碰撞概率统计 diff --git "a/docs/md/java/interview/2020-08-07-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2543\347\257\207\343\200\212HashMap\346\240\270\345\277\203\347\237\245\350\257\206\357\274\214\346\211\260\345\212\250\345\207\275\346\225\260\343\200\201\350\264\237\350\275\275\345\233\240\345\255\220\343\200\201\346\211\251\345\256\271\351\223\276\350\241\250\346\213\206\345\210\206\357\274\214\346\267\261\345\272\246\345\255\246\344\271\240\343\200\213.md" "b/docs/md/java/interview/2020-08-07-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2543\347\257\207\343\200\212HashMap\346\240\270\345\277\203\347\237\245\350\257\206\357\274\214\346\211\260\345\212\250\345\207\275\346\225\260\343\200\201\350\264\237\350\275\275\345\233\240\345\255\220\343\200\201\346\211\251\345\256\271\351\223\276\350\241\250\346\213\206\345\210\206\357\274\214\346\267\261\345\272\246\345\255\246\344\271\240\343\200\213.md" index e28f90c2f..0c78bf7ee 100755 --- "a/docs/md/java/interview/2020-08-07-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2543\347\257\207\343\200\212HashMap\346\240\270\345\277\203\347\237\245\350\257\206\357\274\214\346\211\260\345\212\250\345\207\275\346\225\260\343\200\201\350\264\237\350\275\275\345\233\240\345\255\220\343\200\201\346\211\251\345\256\271\351\223\276\350\241\250\346\213\206\345\210\206\357\274\214\346\267\261\345\272\246\345\255\246\344\271\240\343\200\213.md" +++ "b/docs/md/java/interview/2020-08-07-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2543\347\257\207\343\200\212HashMap\346\240\270\345\277\203\347\237\245\350\257\206\357\274\214\346\211\260\345\212\250\345\207\275\346\225\260\343\200\201\350\264\237\350\275\275\345\233\240\345\255\220\343\200\201\346\211\251\345\256\271\351\223\276\350\241\250\346\213\206\345\210\206\357\274\214\346\267\261\345\272\246\345\255\246\344\271\240\343\200\213.md" @@ -78,7 +78,7 @@ System.out.println(JSON.toJSONString(tab)); 这段代码整体看起来也是非常简单,并没有什么复杂度,主要包括以下内容; 1. 初始化一组字符串集合,这里初始化了7个。 -2. 定义一个数组用于存放字符串,注意这里的长度是8,也就是2的倍数。这样的数组长度才会出现一个 `0111` 除高位以外都是1的特征,也是为了散列。 +2. 定义一个数组用于存放字符串,注意这里的长度是8,也就是2的3次幂。这样的数组长度才会出现一个 `0111` 除高位以外都是1的特征,也是为了散列。 3. 接下来就是循环存放数据,计算出每个字符串在数组中的位置。`key.hashCode() & (tab.length - 1)`。 4. 在字符串存放到数组的过程,如果遇到相同的元素,进行连接操作`模拟链表的过程`。 5. 最后输出存放结果。 @@ -114,7 +114,7 @@ key值=plop Idx=5 以上我们实现了一个简单的HashMap,或者说还算不上HashMap,只能算做一个散列数据存放的雏形。但这样的一个数据结构放在实际使用中,会有哪些问题呢? 1. 这里所有的元素存放都需要获取一个索引位置,而如果元素的位置不够散列碰撞严重,那么就失去了散列表存放的意义,没有达到预期的性能。 -2. 在获取索引ID的计算公式中,需要数组长度是2的倍数,那么怎么进行初始化这个数组大小。 +2. 在获取索引ID的计算公式中,需要数组长度是2的幂次方,那么怎么进行初始化这个数组大小。 3. 数组越小碰撞的越大,数组越大碰撞的越小,时间与空间如何取舍。 4. 目前存放7个元素,已经有两个位置都存放了2个字符串,那么链表越来越长怎么优化。 5. 随着元素的不断添加,数组长度不足扩容时,怎么把原有的元素,拆分到新的位置上去。 @@ -220,11 +220,11 @@ public void test_disturb() { ### 3. 初始化容量和负载因子 -接下来我们讨论下一个问题,从我们模仿HashMap的例子中以及HashMap默认的初始化大小里,都可以知道,散列数组需要一个2的倍数的长度,因为只有2的倍数在减1的时候,才会出现`01111`这样的值。 +接下来我们讨论下一个问题,从我们模仿HashMap的例子中以及HashMap默认的初始化大小里,都可以知道,散列数组需要一个2的幂次方的长度,因为只有2的幂次方在减1的时候,才会出现`01111`这样的值。 那么这里就有一个问题,我们在初始化HashMap的时候,如果传一个17个的值`new HashMap<>(17);`,它会怎么处理呢? -#### 3.1 寻找2的倍数最小值 +#### 3.1 寻找2的幂次方最小值 在HashMap的初始化中,有这样一段方法; @@ -237,7 +237,7 @@ public HashMap(int initialCapacity, float loadFactor) { ``` - 阈值`threshold`,通过方法`tableSizeFor`进行计算,是根据初始化来计算的。 -- 这个方法也就是要寻找比初始值大的,最小的那个2进制数值。比如传了17,我应该找到的是32。 +- 这个方法也就是要寻找比初始值大的,最小的那个2进制数值。比如传了17,我应该找到的是32(2的4次幂是16<17,所以找到2的5次幂32)。 计算阈值大小的方法; @@ -254,7 +254,7 @@ static final int tableSizeFor(int cap) { ``` - MAXIMUM_CAPACITY = 1 << 30,这个是临界范围,也就是最大的Map集合。 -- 乍一看可能有点晕😵怎么都在向右移位1、2、4、8、16,这主要是为了把二进制的各个位置都填上1,当二进制的各个位置都是1以后,就是一个标准的2的倍数减1了,最后把结果加1再返回即可。 +- 乍一看可能有点晕😵怎么都在向右移位1、2、4、8、16,这主要是为了把二进制的各个位置都填上1,当二进制的各个位置都是1以后,就是一个标准的2的幂次方减1了,最后把结果加1再返回即可。 那这里我们把17这样一个初始化计算阈值的过程,用图展示出来,方便理解; @@ -378,7 +378,7 @@ public void test_hashMap() { 从上面的示例可以很轻易的看出, 两次indexFor()的差别只是第二次参与位于比第一次左边有一位从0变为1, 而这个变化的1刚好是oldCap, 那么只需要判断原key的hash这个位上是否为1: 若是1, 则需要移动至oldCap + i的槽位, 若为0, 则不需要移动; -这也是HashMap的长度必须保证是2的倍数的原因, 正因为这种环环相扣的设计, HashMap.loadFactor的选值是3/4就能理解了, table.length * 3/4可以被优化为(table.length >> 2) << 2) - (table.length >> 2) == table.length - (table.lenght >> 2), JAVA的位运算比乘除的效率更高, 所以取3/4在保证hash冲突小的情况下兼顾了效率; +这也是HashMap的长度必须保证是2的幂次方的原因, 正因为这种环环相扣的设计, HashMap.loadFactor的选值是3/4就能理解了, table.length * 3/4可以被优化为((table.length >> 2) << 2) - (table.length >> 2) == table.length - (table.length >> 2), JAVA的位运算比乘除的效率更高, 所以取3/4在保证hash冲突小的情况下兼顾了效率; ## 四、总结 diff --git "a/docs/md/java/interview/2020-08-13-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2544\347\257\207\343\200\212HashMap\346\225\260\346\215\256\346\217\222\345\205\245\343\200\201\346\237\245\346\211\276\343\200\201\345\210\240\351\231\244\343\200\201\351\201\215\345\216\206\357\274\214\346\272\220\347\240\201\345\210\206\346\236\220\343\200\213.md" "b/docs/md/java/interview/2020-08-13-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2544\347\257\207\343\200\212HashMap\346\225\260\346\215\256\346\217\222\345\205\245\343\200\201\346\237\245\346\211\276\343\200\201\345\210\240\351\231\244\343\200\201\351\201\215\345\216\206\357\274\214\346\272\220\347\240\201\345\210\206\346\236\220\343\200\213.md" index 753ee44ed..eb35b7f3b 100755 --- "a/docs/md/java/interview/2020-08-13-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2544\347\257\207\343\200\212HashMap\346\225\260\346\215\256\346\217\222\345\205\245\343\200\201\346\237\245\346\211\276\343\200\201\345\210\240\351\231\244\343\200\201\351\201\215\345\216\206\357\274\214\346\272\220\347\240\201\345\210\206\346\236\220\343\200\213.md" +++ "b/docs/md/java/interview/2020-08-13-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2544\347\257\207\343\200\212HashMap\346\225\260\346\215\256\346\217\222\345\205\245\343\200\201\346\237\245\346\211\276\343\200\201\345\210\240\351\231\244\343\200\201\351\201\215\345\216\206\357\274\214\346\272\220\347\240\201\345\210\206\346\236\220\343\200\213.md" @@ -65,7 +65,7 @@ lock: need n = (tab = resize()).length; ``` -3. 根据哈希值计算下标,如果对应小标正好没有存放数据,则直接插入即可否则需要覆盖。`tab[i = (n - 1) & hash])` +3. 根据哈希值计算下标,如果对应下标正好没有存放数据,则直接插入即可否则需要覆盖。`tab[i = (n - 1) & hash])` 4. 判断tab[i]是否为树节点,否则向链表中插入数据,是则向树中插入节点。 @@ -164,7 +164,7 @@ final Node[] resize() { return oldTab; } - // 按旧容量和阀值的2倍计算新容量和阀值 + // 按旧容量和阈值的2倍计算新容量和阈值 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold @@ -178,12 +178,12 @@ final Node[] resize() { else { // zero initial threshold signifies using defaults // 这一部分也是,源代码中也有相应的英文注释 // 调用无参构造方法时,数组桶数组容量为默认容量 1 << 4; aka 16 - // 阀值;是默认容量与负载因子的乘积,0.75 + // 阈值;是默认容量与负载因子的乘积,0.75 newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } - // newThr为0,则使用阀值公式计算容量 + // newThr为0,则使用阈值公式计算容量 if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY @@ -247,8 +247,8 @@ final Node[] resize() { ``` 以上的代码稍微有些长,但是整体的逻辑还是蛮清晰的,主要包括; -1. 扩容时计算出新的newCap、newThr,这是两个单词的缩写,一个是Capacity ,另一个是阀Threshold -2. newCap用于创新的数组桶 `new Node[newCap];` +1. 扩容时计算出新的newCap、newThr,这是两个单词的缩写,一个是Capacity ,另一个是阈Threshold +2. newCap用于创建新的数组桶 `new Node[newCap];` 3. 随着扩容后,原来那些因为哈希碰撞,存放成链表和红黑树的元素,都需要进行拆分存放到新的位置中。 #### 1.4 链表树化 diff --git "a/docs/md/java/interview/2020-08-20-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2546\347\257\207\343\200\212\345\270\246\347\235\200\351\235\242\350\257\225\351\242\230\345\255\246\344\271\240\347\272\242\351\273\221\346\240\221\346\223\215\344\275\234\345\216\237\347\220\206\357\274\214\350\247\243\346\236\220\344\273\200\344\271\210\346\227\266\345\200\231\346\237\223\350\211\262\343\200\201\346\200\216\344\271\210\350\277\233\350\241\214\346\227\213\350\275\254\343\200\201\344\270\2162-3\346\240\221\346\234\211\344\273\200\344\271\210\345\205\263\350\201\224\343\200\213.md" "b/docs/md/java/interview/2020-08-20-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2546\347\257\207\343\200\212\345\270\246\347\235\200\351\235\242\350\257\225\351\242\230\345\255\246\344\271\240\347\272\242\351\273\221\346\240\221\346\223\215\344\275\234\345\216\237\347\220\206\357\274\214\350\247\243\346\236\220\344\273\200\344\271\210\346\227\266\345\200\231\346\237\223\350\211\262\343\200\201\346\200\216\344\271\210\350\277\233\350\241\214\346\227\213\350\275\254\343\200\201\344\270\2162-3\346\240\221\346\234\211\344\273\200\344\271\210\345\205\263\350\201\224\343\200\213.md" index 00ba3bc3b..b95d6c2e4 100755 --- "a/docs/md/java/interview/2020-08-20-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2546\347\257\207\343\200\212\345\270\246\347\235\200\351\235\242\350\257\225\351\242\230\345\255\246\344\271\240\347\272\242\351\273\221\346\240\221\346\223\215\344\275\234\345\216\237\347\220\206\357\274\214\350\247\243\346\236\220\344\273\200\344\271\210\346\227\266\345\200\231\346\237\223\350\211\262\343\200\201\346\200\216\344\271\210\350\277\233\350\241\214\346\227\213\350\275\254\343\200\201\344\270\2162-3\346\240\221\346\234\211\344\273\200\344\271\210\345\205\263\350\201\224\343\200\213.md" +++ "b/docs/md/java/interview/2020-08-20-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2546\347\257\207\343\200\212\345\270\246\347\235\200\351\235\242\350\257\225\351\242\230\345\255\246\344\271\240\347\272\242\351\273\221\346\240\221\346\223\215\344\275\234\345\216\237\347\220\206\357\274\214\350\247\243\346\236\220\344\273\200\344\271\210\346\227\266\345\200\231\346\237\223\350\211\262\343\200\201\346\200\216\344\271\210\350\277\233\350\241\214\346\227\213\350\275\254\343\200\201\344\270\2162-3\346\240\221\346\234\211\344\273\200\344\271\210\345\205\263\350\201\224\343\200\213.md" @@ -48,7 +48,7 @@ Rudolf Bayer 于1978年发明红黑树,在当时被称为`对称二叉 B 树(s 2. 节点是红黑或者黑色 3. 所有子叶节点都是黑色(叶子是NIL节点,默认没有画出来) 4. 每个红色节点必须有两个黑色子节点(也同样说明一条链路上不能有链路的红色节点) -5. 黑高,从任一节点到齐每个叶子节点,经过的路径都包含相同数目的黑色节点 +5. 从任一节点到齐每个叶子节点,经过的路径都包含相同数目的黑色节点 ``` 那么,这些规则是怎么总结定义出来的呢?接下里我们一步步分析讲解。 @@ -195,7 +195,7 @@ Rudolf Bayer 于1978年发明红黑树,在当时被称为`对称二叉 B 树(s ![](https://bugstack.cn/assets/images/2020/interview/interview-7-13.png) -##### 3.2.5 被删节点兄弟为黑色&含双黑节点(黑) +##### 3.2.5 被删节点兄弟为红色&含双黑节点(黑) ![](https://bugstack.cn/assets/images/2020/interview/interview-7-14.png) @@ -213,11 +213,11 @@ Rudolf Bayer 于1978年发明红黑树,在当时被称为`对称二叉 B 树(s ![](https://bugstack.cn/assets/images/2020/interview/interview-7-17.png) -##### 3.2.4 被删节点兄弟为黑色&不含子节点 +##### 3.3.4 被删节点兄弟为黑色&不含子节点 ![](https://bugstack.cn/assets/images/2020/interview/interview-7-18.png) -##### 3.2.5 被删节点兄弟为黑色&含双黑节点(黑) +##### 3.3.5 被删节点兄弟为红色&含双黑节点(黑) ![](https://bugstack.cn/assets/images/2020/interview/interview-7-19.png) diff --git "a/docs/md/java/interview/2020-08-27-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2547\347\257\207\343\200\212ArrayList\344\271\237\350\277\231\344\271\210\345\244\232\347\237\245\350\257\206\357\274\237\344\270\200\344\270\252\346\214\207\345\256\232\344\275\215\347\275\256\346\217\222\345\205\245\345\260\261\346\212\212\350\260\242\351\243\236\346\234\272\351\235\242\346\231\225\344\272\206\357\274\201\343\200\213.md" "b/docs/md/java/interview/2020-08-27-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2547\347\257\207\343\200\212ArrayList\344\271\237\350\277\231\344\271\210\345\244\232\347\237\245\350\257\206\357\274\237\344\270\200\344\270\252\346\214\207\345\256\232\344\275\215\347\275\256\346\217\222\345\205\245\345\260\261\346\212\212\350\260\242\351\243\236\346\234\272\351\235\242\346\231\225\344\272\206\357\274\201\343\200\213.md" index a2d5da729..2964200aa 100755 --- "a/docs/md/java/interview/2020-08-27-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2547\347\257\207\343\200\212ArrayList\344\271\237\350\277\231\344\271\210\345\244\232\347\237\245\350\257\206\357\274\237\344\270\200\344\270\252\346\214\207\345\256\232\344\275\215\347\275\256\346\217\222\345\205\245\345\260\261\346\212\212\350\260\242\351\243\236\346\234\272\351\235\242\346\231\225\344\272\206\357\274\201\343\200\213.md" +++ "b/docs/md/java/interview/2020-08-27-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2547\347\257\207\343\200\212ArrayList\344\271\237\350\277\231\344\271\210\345\244\232\347\237\245\350\257\206\357\274\237\344\270\200\344\270\252\346\214\207\345\256\232\344\275\215\347\275\256\346\217\222\345\205\245\345\260\261\346\212\212\350\260\242\351\243\236\346\234\272\351\235\242\346\231\225\344\272\206\357\274\201\343\200\213.md" @@ -112,11 +112,11 @@ list.add("ccc"); #### 1.2 方式02;内部类方式 ```java -ArrayList list = new ArrayList() \\{ +ArrayList list = new ArrayList() {{ add("aaa"); add("bbb"); add("ccc"); -\\}; +}}; ``` - 这种方式也是比较常用的,而且省去了多余的代码量。 diff --git "a/docs/md/java/interview/2020-09-03-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2549\347\257\207\343\200\212\351\230\237\345\210\227\346\230\257\344\273\200\344\271\210\357\274\237\344\273\200\344\271\210\346\230\257\345\217\214\347\253\257\351\230\237\345\210\227\343\200\201\345\273\266\350\277\237\345\257\271\345\210\227\343\200\201\351\230\273\345\241\236\351\230\237\345\210\227\357\274\214\345\205\250\346\230\257\347\237\245\350\257\206\347\233\262\345\214\272\357\274\201\343\200\213.md" "b/docs/md/java/interview/2020-09-03-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2549\347\257\207\343\200\212\351\230\237\345\210\227\346\230\257\344\273\200\344\271\210\357\274\237\344\273\200\344\271\210\346\230\257\345\217\214\347\253\257\351\230\237\345\210\227\343\200\201\345\273\266\350\277\237\345\257\271\345\210\227\343\200\201\351\230\273\345\241\236\351\230\237\345\210\227\357\274\214\345\205\250\346\230\257\347\237\245\350\257\206\347\233\262\345\214\272\357\274\201\343\200\213.md" index 9f32818df..5d1606bba 100755 --- "a/docs/md/java/interview/2020-09-03-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2549\347\257\207\343\200\212\351\230\237\345\210\227\346\230\257\344\273\200\344\271\210\357\274\237\344\273\200\344\271\210\346\230\257\345\217\214\347\253\257\351\230\237\345\210\227\343\200\201\345\273\266\350\277\237\345\257\271\345\210\227\343\200\201\351\230\273\345\241\236\351\230\237\345\210\227\357\274\214\345\205\250\346\230\257\347\237\245\350\257\206\347\233\262\345\214\272\357\274\201\343\200\213.md" +++ "b/docs/md/java/interview/2020-09-03-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\2549\347\257\207\343\200\212\351\230\237\345\210\227\346\230\257\344\273\200\344\271\210\357\274\237\344\273\200\344\271\210\346\230\257\345\217\214\347\253\257\351\230\237\345\210\227\343\200\201\345\273\266\350\277\237\345\257\271\345\210\227\343\200\201\351\230\273\345\241\236\351\230\237\345\210\227\357\274\214\345\205\250\346\230\257\347\237\245\350\257\206\347\233\262\345\214\272\357\274\201\343\200\213.md" @@ -441,7 +441,7 @@ void linkLast(E e) { `你是否有时候需要把一些数据存起来,倒计时到某个时刻在使用?` -在Java的队列数据结构中,还有一种队列是延时队列,可以通过设定存放时间,依次轮训获取。 +在Java的队列数据结构中,还有一种队列是延时队列,可以通过设定存放时间,依次轮询获取。 #### 4.1 功能使用 @@ -672,4 +672,4 @@ public class DataQueueStack { - 关于栈和队列的数据结构方面到这里就介绍完了,另外这里还有一些关于阻塞队列锁🔒的应用过程,到我们后面讲锁相关知识点,再重点介绍。 - 队列结构的设计非常适合某些需要`LIFO`或者`FIFO`的应用场景,同时在队列的数据结构中也有双端、延时和组合的功能类,使用起来也非常方便。 -- 数据结构方面的知识到本章节算是告一段落,如果有优秀的内容,后面还会继续补充。再下一章节小傅哥([bugstack.cn](http://bugstack.cn))准备给大家介绍,关于数据结构中涉及的算法部分,这些主要来自于`Collections`类的实现部分。 \ No newline at end of file +- 数据结构方面的知识到本章节算是告一段落,如果有优秀的内容,后面还会继续补充。再下一章节小傅哥([bugstack.cn](http://bugstack.cn))准备给大家介绍,关于数据结构中涉及的算法部分,这些主要来自于`Collections`类的实现部分。 diff --git "a/docs/md/java/interview/2020-09-23-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25412\347\257\207\343\200\212\351\235\242\350\257\225\345\256\230\357\274\214ThreadLocal \344\275\240\350\246\201\350\277\231\344\271\210\351\227\256\357\274\214\346\210\221\345\260\261\346\214\202\344\272\206\357\274\201\343\200\213.md" "b/docs/md/java/interview/2020-09-23-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25412\347\257\207\343\200\212\351\235\242\350\257\225\345\256\230\357\274\214ThreadLocal \344\275\240\350\246\201\350\277\231\344\271\210\351\227\256\357\274\214\346\210\221\345\260\261\346\214\202\344\272\206\357\274\201\343\200\213.md" index 844c7181c..2b06cf805 100755 --- "a/docs/md/java/interview/2020-09-23-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25412\347\257\207\343\200\212\351\235\242\350\257\225\345\256\230\357\274\214ThreadLocal \344\275\240\350\246\201\350\277\231\344\271\210\351\227\256\357\274\214\346\210\221\345\260\261\346\214\202\344\272\206\357\274\201\343\200\213.md" +++ "b/docs/md/java/interview/2020-09-23-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25412\347\257\207\343\200\212\351\235\242\350\257\225\345\256\230\357\274\214ThreadLocal \344\275\240\350\246\201\350\277\231\344\271\210\351\227\256\357\274\214\346\210\221\345\260\261\346\214\202\344\272\206\357\274\201\343\200\213.md" @@ -296,7 +296,7 @@ private void set(ThreadLocal key, Object value) { ### 3. 散列算法 -既然 `ThreadLocal` 是基于数组结构的拉链法存储,那就一定会有哈希的计算。但我们翻阅源码后,发现这个哈希计算与 `HashMap` 中的散列求数组下标计算的哈希方式不一样。如果你忘记了HashMap,可以翻阅文章[《HashMap 源码分析,插入、查找》](https://bugstack.cn/interview/2020/08/13/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC4%E7%AF%87-HashMap%E6%95%B0%E6%8D%AE%E6%8F%92%E5%85%A5-%E6%9F%A5%E6%89%BE-%E5%88%A0%E9%99%A4-%E9%81%8D%E5%8E%86-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.html)、[《HashMap 扰动函数、负载因子》](https://bugstack.cn/interview/2020/08/07/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC3%E7%AF%87-HashMap%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86-%E6%89%B0%E5%8A%A8%E5%87%BD%E6%95%B0-%E8%B4%9F%E8%BD%BD%E5%9B%A0%E5%AD%90-%E6%89%A9%E5%AE%B9%E9%93%BE%E8%A1%A8%E6%8B%86%E5%88%86-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0.html) +既然 `ThreadLocal` 是基于数组结构的开放寻址方式存储,那就一定会有哈希的计算。但我们翻阅源码后,发现这个哈希计算与 `HashMap` 中的散列求数组下标计算的哈希方式不一样。如果你忘记了HashMap,可以翻阅文章[《HashMap 源码分析,插入、查找》](https://bugstack.cn/interview/2020/08/13/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC4%E7%AF%87-HashMap%E6%95%B0%E6%8D%AE%E6%8F%92%E5%85%A5-%E6%9F%A5%E6%89%BE-%E5%88%A0%E9%99%A4-%E9%81%8D%E5%8E%86-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90.html)、[《HashMap 扰动函数、负载因子》](https://bugstack.cn/interview/2020/08/07/%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C-%E7%AC%AC3%E7%AF%87-HashMap%E6%A0%B8%E5%BF%83%E7%9F%A5%E8%AF%86-%E6%89%B0%E5%8A%A8%E5%87%BD%E6%95%B0-%E8%B4%9F%E8%BD%BD%E5%9B%A0%E5%AD%90-%E6%89%A9%E5%AE%B9%E9%93%BE%E8%A1%A8%E6%8B%86%E5%88%86-%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0.html) #### 3.1 神秘的数字 0x61c88647 @@ -454,7 +454,7 @@ Process finished with exit code 0 0. 中间是 `ThreadLocal` 的数组结构,之后在设置元素时分为四种不同的情况,另外元素的插入是通过斐波那契散列计算下标值,进行存放的。 1. 情况1,待插入的下标,是空位置直接插入。 2. 情况2,待插入的下标,不为空,key 相同,直接更新 -3. 情况3,待插入的下标,不为空,key 不相同,拉链法寻址 +3. 情况3,待插入的下标,不为空,key 不相同,开放寻址 4. 情况4,不为空,key 不相同,碰到过期key。其实情况4,遇到的是弱引用发生GC时,产生的情况。碰到这种情况,`ThreadLocal` 会进行探测清理过期key,这部分清理内容后续讲解。 ##### 4.2.2 源码分析 diff --git "a/docs/md/java/interview/2020-10-21-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25414\347\257\207\343\200\212volatile \346\200\216\344\271\210\345\256\236\347\216\260\347\232\204\345\206\205\345\255\230\345\217\257\350\247\201\357\274\237\346\262\241\346\234\211 volatile \344\270\200\345\256\232\344\270\215\345\217\257\350\247\201\345\220\227\357\274\237\343\200\213.md" "b/docs/md/java/interview/2020-10-21-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25414\347\257\207\343\200\212volatile \346\200\216\344\271\210\345\256\236\347\216\260\347\232\204\345\206\205\345\255\230\345\217\257\350\247\201\357\274\237\346\262\241\346\234\211 volatile \344\270\200\345\256\232\344\270\215\345\217\257\350\247\201\345\220\227\357\274\237\343\200\213.md" index e721eca5b..605894f2d 100755 --- "a/docs/md/java/interview/2020-10-21-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25414\347\257\207\343\200\212volatile \346\200\216\344\271\210\345\256\236\347\216\260\347\232\204\345\206\205\345\255\230\345\217\257\350\247\201\357\274\237\346\262\241\346\234\211 volatile \344\270\200\345\256\232\344\270\215\345\217\257\350\247\201\345\220\227\357\274\237\343\200\213.md" +++ "b/docs/md/java/interview/2020-10-21-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25414\347\257\207\343\200\212volatile \346\200\216\344\271\210\345\256\236\347\216\260\347\232\204\345\206\205\345\255\230\345\217\257\350\247\201\357\274\237\346\262\241\346\234\211 volatile \344\270\200\345\256\232\344\270\215\345\217\257\350\247\201\345\220\227\357\274\237\343\200\213.md" @@ -129,7 +129,7 @@ volatile关键字是Java虚拟机提供的的最轻量级的同步机制,它 在添加 volatile 关键字后,程序就符合预期的输出了 *你坏*。从我们对 volatile 的学习认知可以知道。volatile关键字是 JVM 提供的最轻量级的同步机制,用来修饰变量,用来保证变量对所有线程可见性。 -正在修饰后可以让字段在线程见可见,那么这个属性被修改值后,可以及时的在另外的线程中做出相应的反应。 +正在修饰后可以让字段在线程间可见,那么这个属性被修改值后,可以及时的在另外的线程中做出相应的反应。 ### 3. volatile怎么保证的可见性 @@ -145,7 +145,7 @@ volatile关键字是Java虚拟机提供的的最轻量级的同步机制,它 当我们把变量使用 volatile 修饰时 `public volatile boolean sign = false;`,线程01对变量进行操作时,会把变量变化的值强制刷新的到主内存。当线程02获取值时,会把自己的内存里的 sign 值过期掉,之后从主内存中读取。所以添加关键字后程序如预期输出结果。 -### 4. 反编译解毒可见性 +### 4. 反编译解读可见性 类似这样有深度的技术知识,最佳的方式就是深入理解原理,看看它到底做了什么才保证的内存可见性操作。 @@ -319,6 +319,6 @@ Process finished with exit code 0 ## 四、总结 -- 最后我们再总结下 volatile,它呢,会控制被修饰的变量在内存操作上主动把值刷新到主内存,JMM 会把该线程对应的CPU内存设置过期,从主内存中读取最新值。 +- 最后我们再总结下 volatile,它呢,会控制被修饰的变量在内存操作上主动把值刷新到主内存,JVM 会把该线程对应的CPU内存设置过期,从主内存中读取最新值。 - 那么,volatile 如何防止指令重排也是内存屏障,volatile 的内存屏故障是在读写操作的前后各添加一个 StoreStore屏障,也就是四个位置,来保证重排序时不能把内存屏障后面的指令重排序到内存屏障之前的位置。 - 另外 volatile 并不能解决原子性,如果需要解决原子性问题,需要使用 synchronzied 或者 lock,这部分内容在我们后续章节中介绍。 diff --git "a/docs/md/java/interview/2020-11-11-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25417\347\257\207\343\200\212\347\240\201\345\206\234\344\274\232\351\224\201\357\274\214ReentrantLock\344\271\213AQS\345\216\237\347\220\206\345\210\206\346\236\220\345\222\214\345\256\236\350\267\265\344\275\277\347\224\250\343\200\213.md" "b/docs/md/java/interview/2020-11-11-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25417\347\257\207\343\200\212\347\240\201\345\206\234\344\274\232\351\224\201\357\274\214ReentrantLock\344\271\213AQS\345\216\237\347\220\206\345\210\206\346\236\220\345\222\214\345\256\236\350\267\265\344\275\277\347\224\250\343\200\213.md" index 812008543..859cb969b 100755 --- "a/docs/md/java/interview/2020-11-11-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25417\347\257\207\343\200\212\347\240\201\345\206\234\344\274\232\351\224\201\357\274\214ReentrantLock\344\271\213AQS\345\216\237\347\220\206\345\210\206\346\236\220\345\222\214\345\256\236\350\267\265\344\275\277\347\224\250\343\200\213.md" +++ "b/docs/md/java/interview/2020-11-11-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25417\347\257\207\343\200\212\347\240\201\345\206\234\344\274\232\351\224\201\357\274\214ReentrantLock\344\271\213AQS\345\216\237\347\220\206\345\210\206\346\236\220\345\222\214\345\256\236\350\267\265\344\275\277\347\224\250\343\200\213.md" @@ -53,7 +53,7 @@ ReentrantLock 可重入独占锁涉及的知识点较多,为了更好的学习 AQS 是 AbstractQueuedSynchronizer 的缩写,几乎所有 Lock 都是基于 AQS 来实现了,其底层大量使用 CAS 提供乐观锁服务,在冲突时采用自旋方式进行重试,以此实现轻量级和高效的获取锁。 -另外 AbstractQueuedSynchronizer 是一个抽象类,但并没有定义相应的抽象方法,而是提供了可以被字类继承时覆盖的 protected 的方法,这样就可以非常方便的支持继承类的使用。 +另外 AbstractQueuedSynchronizer 是一个抽象类,但并没有定义相应的抽象方法,而是提供了可以被子类继承时覆盖的 protected 的方法,这样就可以非常方便的支持继承类的使用。 ### 2. 写一个简单的 AQS 同步类 diff --git "a/docs/md/java/interview/2020-11-25-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25419\347\257\207\343\200\212Thread.start() \357\274\214\345\256\203\346\230\257\346\200\216\344\271\210\350\256\251\347\272\277\347\250\213\345\220\257\345\212\250\347\232\204\345\221\242\357\274\237\343\200\213.md" "b/docs/md/java/interview/2020-11-25-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25419\347\257\207\343\200\212Thread.start() \357\274\214\345\256\203\346\230\257\346\200\216\344\271\210\350\256\251\347\272\277\347\250\213\345\220\257\345\212\250\347\232\204\345\221\242\357\274\237\343\200\213.md" index 7c925097f..d97b1b8e4 100755 --- "a/docs/md/java/interview/2020-11-25-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25419\347\257\207\343\200\212Thread.start() \357\274\214\345\256\203\346\230\257\346\200\216\344\271\210\350\256\251\347\272\277\347\250\213\345\220\257\345\212\250\347\232\204\345\221\242\357\274\237\343\200\213.md" +++ "b/docs/md/java/interview/2020-11-25-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25419\347\257\207\343\200\212Thread.start() \357\274\214\345\256\203\346\230\257\346\200\216\344\271\210\350\256\251\347\272\277\347\250\213\345\220\257\345\212\250\347\232\204\345\221\242\357\274\237\343\200\213.md" @@ -259,7 +259,7 @@ JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : - `os_linux.cpp`:[https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/os/linux/vm/os_linux.cpp](https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/os/linux/vm/os_linux.cpp) - `os_windows.cpp`:[https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/os/windows/vm/os_windows.cpp](https://github.com/JetBrains/jdk8u_hotspot/blob/master/src/os/windows/vm/os_windows.cpp) -`众所周知,JVM 是个啥!`,所以它的 OS 服务实现,Liunx 还有 Windows 等,都会实现线程的创建逻辑。*这有点像适配器模式* +`众所周知,JVM 是个啥!`,所以它的 OS 服务实现,Linux 还有 Windows 等,都会实现线程的创建逻辑。*这有点像适配器模式* **os_linux -> os::create_thread** diff --git "a/docs/md/java/interview/2020-12-09-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25421\347\257\207\343\200\212\346\211\213\345\206\231\347\272\277\347\250\213\346\261\240\357\274\214\345\257\271\347\205\247\345\255\246\344\271\240ThreadPoolExecutor\347\272\277\347\250\213\346\261\240\345\256\236\347\216\260\345\216\237\347\220\206\357\274\201\343\200\213.md" "b/docs/md/java/interview/2020-12-09-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25421\347\257\207\343\200\212\346\211\213\345\206\231\347\272\277\347\250\213\346\261\240\357\274\214\345\257\271\347\205\247\345\255\246\344\271\240ThreadPoolExecutor\347\272\277\347\250\213\346\261\240\345\256\236\347\216\260\345\216\237\347\220\206\357\274\201\343\200\213.md" index 105d81a76..ac081b7b0 100755 --- "a/docs/md/java/interview/2020-12-09-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25421\347\257\207\343\200\212\346\211\213\345\206\231\347\272\277\347\250\213\346\261\240\357\274\214\345\257\271\347\205\247\345\255\246\344\271\240ThreadPoolExecutor\347\272\277\347\250\213\346\261\240\345\256\236\347\216\260\345\216\237\347\220\206\357\274\201\343\200\213.md" +++ "b/docs/md/java/interview/2020-12-09-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25421\347\257\207\343\200\212\346\211\213\345\206\231\347\272\277\347\250\213\346\261\240\357\274\214\345\257\271\347\205\247\345\255\246\344\271\240ThreadPoolExecutor\347\272\277\347\250\213\346\261\240\345\256\236\347\216\260\345\216\237\347\220\206\357\274\201\343\200\213.md" @@ -81,8 +81,9 @@ threadPoolExecutor.shutdown(); 关于图 21-1,这个手写线程池的实现也非常简单,只会体现出核心流程,包括: 1. 有n个一直在运行的线程,相当于我们创建线程池时允许的线程池大小。 2. 把线程提交给线程池运行。 -3. 如果运行线程池已满,则把线程放入队列中。 -4. 最后当有空闲时,则获取队列中线程进行运行。 +3. 如果运行线程数量大于等于核心线程数,则把线程放入队列中。 +4. 如果队列中容量已添加满,则判断判断当前正在运行的线程数量是否小于设定的最大线程数。若小于则线程池继续创建线程执行线程,若大于则走拒绝策略。 +5. 最后当有空闲时,则获取队列中线程进行运行。 #### 2.2 实现代码 @@ -269,6 +270,9 @@ private static final int TERMINATED = 3 << COUNT_BITS; ![图 22-5 提交线程流程图](https://bugstack.cn/assets/images/2020/interview/interview-21-5.png) +1、图中的左侧的核心线程池是否已满?建议改成是否已达到核心线程数 +2、图中的有车的核心线程池是否已满?建议改成是否已达到最大线程数 + ```java public void execute(Runnable command) { if (command == null) diff --git "a/docs/md/java/interview/2020-12-16-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25422\347\257\207\343\200\212\347\272\277\347\250\213\346\261\240\347\232\204\344\273\213\347\273\215\345\222\214\344\275\277\347\224\250\357\274\214\344\273\245\345\217\212\345\237\272\344\272\216jvmti\350\256\276\350\256\241\351\235\236\345\205\245\344\276\265\347\233\221\346\216\247\343\200\213.md" "b/docs/md/java/interview/2020-12-16-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25422\347\257\207\343\200\212\347\272\277\347\250\213\346\261\240\347\232\204\344\273\213\347\273\215\345\222\214\344\275\277\347\224\250\357\274\214\344\273\245\345\217\212\345\237\272\344\272\216jvmti\350\256\276\350\256\241\351\235\236\345\205\245\344\276\265\347\233\221\346\216\247\343\200\213.md" index 568387360..31f94144f 100755 --- "a/docs/md/java/interview/2020-12-16-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25422\347\257\207\343\200\212\347\272\277\347\250\213\346\261\240\347\232\204\344\273\213\347\273\215\345\222\214\344\275\277\347\224\250\357\274\214\344\273\245\345\217\212\345\237\272\344\272\216jvmti\350\256\276\350\256\241\351\235\236\345\205\245\344\276\265\347\233\221\346\216\247\343\200\213.md" +++ "b/docs/md/java/interview/2020-12-16-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25422\347\257\207\343\200\212\347\272\277\347\250\213\346\261\240\347\232\204\344\273\213\347\273\215\345\222\214\344\275\277\347\224\250\357\274\214\344\273\245\345\217\212\345\237\272\344\272\216jvmti\350\256\276\350\256\241\351\235\236\345\205\245\344\276\265\347\233\221\346\216\247\343\200\213.md" @@ -303,7 +303,7 @@ public class ThreadPoolMonitor extends ThreadPoolExecutor { } ``` -### 2. 基于IVMTI方式监控 +### 2. 基于JVMTI方式监控 这块是监控的重点,因为我们不太可能让每一个需要监控的线程池都来重写的方式记录,这样的改造成本太高了。 diff --git "a/docs/md/java/interview/2021-01-20-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25427\347\257\207\343\200\212JVM \345\210\244\346\226\255\345\257\271\350\261\241\345\267\262\346\255\273\357\274\214\345\256\236\350\267\265\351\252\214\350\257\201GC\345\233\236\346\224\266\343\200\213.md" "b/docs/md/java/interview/2021-01-20-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25427\347\257\207\343\200\212JVM \345\210\244\346\226\255\345\257\271\350\261\241\345\267\262\346\255\273\357\274\214\345\256\236\350\267\265\351\252\214\350\257\201GC\345\233\236\346\224\266\343\200\213.md" index 737c6c271..75a622a41 100755 --- "a/docs/md/java/interview/2021-01-20-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25427\347\257\207\343\200\212JVM \345\210\244\346\226\255\345\257\271\350\261\241\345\267\262\346\255\273\357\274\214\345\256\236\350\267\265\351\252\214\350\257\201GC\345\233\236\346\224\266\343\200\213.md" +++ "b/docs/md/java/interview/2021-01-20-\351\235\242\347\273\217\346\211\213\345\206\214 \302\267 \347\254\25427\347\257\207\343\200\212JVM \345\210\244\346\226\255\345\257\271\350\261\241\345\267\262\346\255\273\357\274\214\345\256\236\350\267\265\351\252\214\350\257\201GC\345\233\236\346\224\266\343\200\213.md" @@ -172,7 +172,7 @@ Heap ![图 27-1 垃圾收集器知识框架](https://bugstack.cn/assets/images/2020/interview/interview-27-1.png) -*原图下载链接:[http://book.bugstack.cn/#s/6jJp2icA](http://book.bugstack.cn/#s/6jJp2icA)* +*原图下载链接:[https://github.com/fuzhengwei/interview/tree/master/docs](https://github.com/fuzhengwei/interview/tree/master/docs)* ### 1. 判断对象已死 diff --git "a/docs/md/java/interview/2021-03-07-\351\235\242\350\257\225\347\216\260\345\234\272\357\274\232\345\260\217\344\274\231\344\274\264\347\276\216\345\233\242\344\270\200\351\235\242\347\232\204\345\210\206\344\272\253\345\222\214\345\210\206\346\236\220[\345\220\253\350\247\243\347\255\224].md" "b/docs/md/java/interview/2021-03-07-\351\235\242\350\257\225\347\216\260\345\234\272\357\274\232\345\260\217\344\274\231\344\274\264\347\276\216\345\233\242\344\270\200\351\235\242\347\232\204\345\210\206\344\272\253\345\222\214\345\210\206\346\236\220[\345\220\253\350\247\243\347\255\224].md" index 738a8cbe8..aa1290573 100644 --- "a/docs/md/java/interview/2021-03-07-\351\235\242\350\257\225\347\216\260\345\234\272\357\274\232\345\260\217\344\274\231\344\274\264\347\276\216\345\233\242\344\270\200\351\235\242\347\232\204\345\210\206\344\272\253\345\222\214\345\210\206\346\236\220[\345\220\253\350\247\243\347\255\224].md" +++ "b/docs/md/java/interview/2021-03-07-\351\235\242\350\257\225\347\216\260\345\234\272\357\274\232\345\260\217\344\274\231\344\274\264\347\276\216\345\233\242\344\270\200\351\235\242\347\232\204\345\210\206\344\272\253\345\222\214\345\210\206\346\236\220[\345\220\253\350\247\243\347\255\224].md" @@ -33,7 +33,7 @@ lock: need **背景** -小伙伴面的是美团,求职方向是团队技术负责人。此次面试以连环追问的方式为主,喜欢刨根问底,非常考研面试者的技术功底。以下是整理出的`部分面试题`,我们看看这些题该如何回答。 +小伙伴面的是美团,求职方向是团队技术负责人。此次面试以连环追问的方式为主,喜欢刨根问底,非常考验面试者的技术功底。以下是整理出的`部分面试题`,我们看看这些题该如何回答。 1. `先让写个线程安全的单例模式` diff --git "a/docs/md/netty/application/2019-09-01-\346\211\213\345\206\231RPC\346\241\206\346\236\266\347\254\254\344\270\200\347\253\240\343\200\212\350\207\252\345\256\232\344\271\211\351\205\215\347\275\256xml\343\200\213.md" "b/docs/md/netty/application/2019-09-01-\346\211\213\345\206\231RPC\346\241\206\346\236\266\347\254\254\344\270\200\347\253\240\343\200\212\350\207\252\345\256\232\344\271\211\351\205\215\347\275\256xml\343\200\213.md" index 235bf2096..e90d35d3e 100644 --- "a/docs/md/netty/application/2019-09-01-\346\211\213\345\206\231RPC\346\241\206\346\236\266\347\254\254\344\270\200\347\253\240\343\200\212\350\207\252\345\256\232\344\271\211\351\205\215\347\275\256xml\343\200\213.md" +++ "b/docs/md/netty/application/2019-09-01-\346\211\213\345\206\231RPC\346\241\206\346\236\266\347\254\254\344\270\200\347\253\240\343\200\212\350\207\252\345\256\232\344\271\211\351\205\215\347\275\256xml\343\200\213.md" @@ -44,7 +44,6 @@ itstack-demo-rpc-01 │ │ │ └── MyNamespaceHandler.java │ │ ├── ConsumerConfig.java │ │ ├── ProviderConfig.java - │ │ └── ProviderConfig.java │ └── resource │ └── META-INF │ ├── rpc.xsd diff --git "a/docs/md/netty/base/2019-07-30-netty\346\241\210\344\276\213\357\274\214netty4.1\345\237\272\347\241\200\345\205\245\351\227\250\347\257\207\351\233\266\343\200\212\345\210\235\345\205\245JavaIO\344\271\213\351\227\250BIO\343\200\201NIO\343\200\201AIO\345\256\236\346\210\230\347\273\203\344\271\240\343\200\213.md" "b/docs/md/netty/base/2019-07-30-netty\346\241\210\344\276\213\357\274\214netty4.1\345\237\272\347\241\200\345\205\245\351\227\250\347\257\207\351\233\266\343\200\212\345\210\235\345\205\245JavaIO\344\271\213\351\227\250BIO\343\200\201NIO\343\200\201AIO\345\256\236\346\210\230\347\273\203\344\271\240\343\200\213.md" index 1e189962a..0f6f85e72 100644 --- "a/docs/md/netty/base/2019-07-30-netty\346\241\210\344\276\213\357\274\214netty4.1\345\237\272\347\241\200\345\205\245\351\227\250\347\257\207\351\233\266\343\200\212\345\210\235\345\205\245JavaIO\344\271\213\351\227\250BIO\343\200\201NIO\343\200\201AIO\345\256\236\346\210\230\347\273\203\344\271\240\343\200\213.md" +++ "b/docs/md/netty/base/2019-07-30-netty\346\241\210\344\276\213\357\274\214netty4.1\345\237\272\347\241\200\345\205\245\351\227\250\347\257\207\351\233\266\343\200\212\345\210\235\345\205\245JavaIO\344\271\213\351\227\250BIO\343\200\201NIO\343\200\201AIO\345\256\236\346\210\230\347\273\203\344\271\240\343\200\213.md" @@ -759,4 +759,8 @@ Process finished with exit code -1 ![微信公众号:bugstack虫洞栈 & NIO案例测试](https://bugstack.cn/assets/images/pic-content/2019/10/netty-1-00-4.png) -微信搜索「**bugstack虫洞栈**」公众号,关注后回复「**Netty专题案例**」获取本文源码&更多原创专题案例! \ No newline at end of file +微信搜索「**bugstack虫洞栈**」公众号,关注后回复「**Netty专题案例**」获取本文源码&更多原创专题案例! + +## 优秀作业 + +- [BIO、NIO、AIO实战练习,绘图总结 @spbreak](https://t.zsxq.com/0fS5wI88J) \ No newline at end of file diff --git "a/docs/md/netty/base/2019-08-06-netty\346\241\210\344\276\213\357\274\214netty4.1\345\237\272\347\241\200\345\205\245\351\227\250\347\257\207\344\270\211\343\200\212NettyServer\345\255\227\347\254\246\344\270\262\350\247\243\347\240\201\345\231\250\343\200\213.md" "b/docs/md/netty/base/2019-08-06-netty\346\241\210\344\276\213\357\274\214netty4.1\345\237\272\347\241\200\345\205\245\351\227\250\347\257\207\344\270\211\343\200\212NettyServer\345\255\227\347\254\246\344\270\262\350\247\243\347\240\201\345\231\250\343\200\213.md" index 622f4eb78..474346d2f 100644 --- "a/docs/md/netty/base/2019-08-06-netty\346\241\210\344\276\213\357\274\214netty4.1\345\237\272\347\241\200\345\205\245\351\227\250\347\257\207\344\270\211\343\200\212NettyServer\345\255\227\347\254\246\344\270\262\350\247\243\347\240\201\345\231\250\343\200\213.md" +++ "b/docs/md/netty/base/2019-08-06-netty\346\241\210\344\276\213\357\274\214netty4.1\345\237\272\347\241\200\345\205\245\351\227\250\347\257\207\344\270\211\343\200\212NettyServer\345\255\227\347\254\246\344\270\262\350\247\243\347\240\201\345\231\250\343\200\213.md" @@ -165,4 +165,8 @@ itstack-demo-netty server start done. {关注公众号:bugstack虫洞栈,获 Process finished with exit code -1 ``` -微信搜索「**bugstack虫洞栈**」公众号,关注后回复「**Netty专题案例**」获取本文源码&更多原创专题案例! \ No newline at end of file +微信搜索「**bugstack虫洞栈**」公众号,关注后回复「**Netty专题案例**」获取本文源码&更多原创专题案例! + +## 优秀作业 + +- [NettyServer字符串解码器 @spbreak](https://t.zsxq.com/0fPjFUQ5e) \ No newline at end of file diff --git "a/docs/md/netty/expand/2019-08-21-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\345\205\255\343\200\212SpringBoot+Netty+Elasticsearch\346\224\266\351\233\206\346\227\245\345\277\227\344\277\241\346\201\257\346\225\260\346\215\256\345\255\230\345\202\250\343\200\213.md" "b/docs/md/netty/expand/2019-08-21-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\345\205\255\343\200\212SpringBoot+Netty+Elasticsearch\346\224\266\351\233\206\346\227\245\345\277\227\344\277\241\346\201\257\346\225\260\346\215\256\345\255\230\345\202\250\343\200\213.md" index 760ca9b38..5e2d6f2c8 100644 --- "a/docs/md/netty/expand/2019-08-21-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\345\205\255\343\200\212SpringBoot+Netty+Elasticsearch\346\224\266\351\233\206\346\227\245\345\277\227\344\277\241\346\201\257\346\225\260\346\215\256\345\255\230\345\202\250\343\200\213.md" +++ "b/docs/md/netty/expand/2019-08-21-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\345\205\255\343\200\212SpringBoot+Netty+Elasticsearch\346\224\266\351\233\206\346\227\245\345\277\227\344\277\241\346\201\257\346\225\260\346\215\256\345\255\230\345\202\250\343\200\213.md" @@ -21,8 +21,8 @@ lock: need 1. jdk1.8【jdk1.7以下只能部分支持netty】 2. Netty4.1.36.Final【netty3.x 4.x 5每次的变化较大,接口类名也随着变化】 3. elasticsearch6.2.2 - 1. [windows环境下安装elasticsearch6.2.2](https://bugstack.cn/?p=149 ) - 2. [elasticsearch-head插件安装](https://bugstack.cn/?p=148) + 1. [windows环境下安装elasticsearch6.2.2](https://bugstack.cn/md/devops/2019-08-12-windows%E7%8E%AF%E5%A2%83%E4%B8%8B%E5%AE%89%E8%A3%85elasticsearch6.2.2.html ) + 2. [elasticsearch-head插件安装](https://bugstack.cn/md/devops/2019-08-13-elasticsearch-head%E6%8F%92%E4%BB%B6%E5%AE%89%E8%A3%85.html) ## 代码示例 ```java diff --git "a/docs/md/netty/expand/2019-08-23-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\345\205\253\343\200\212Netty\345\277\203\350\267\263\346\234\215\345\212\241\344\270\216\346\226\255\347\272\277\351\207\215\350\277\236\343\200\213.md" "b/docs/md/netty/expand/2019-08-23-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\345\205\253\343\200\212Netty\345\277\203\350\267\263\346\234\215\345\212\241\344\270\216\346\226\255\347\272\277\351\207\215\350\277\236\343\200\213.md" index 741cd2ecf..48b0ad6c3 100644 --- "a/docs/md/netty/expand/2019-08-23-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\345\205\253\343\200\212Netty\345\277\203\350\267\263\346\234\215\345\212\241\344\270\216\346\226\255\347\272\277\351\207\215\350\277\236\343\200\213.md" +++ "b/docs/md/netty/expand/2019-08-23-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\345\205\253\343\200\212Netty\345\277\203\350\267\263\346\234\215\345\212\241\344\270\216\346\226\255\347\272\277\351\207\215\350\277\236\343\200\213.md" @@ -106,7 +106,7 @@ public class MyClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("断开链接重连" + ctx.channel().localAddress().toString()); - //使用过程中断线重连 + //使用过程中断线重连;实际场景需要关闭线程池,并且通过ping的方式进行检测 new Thread(new Runnable() { @Override public void run() { diff --git "a/docs/md/netty/expand/2019-08-24-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\344\271\235\343\200\212Netty\351\233\206\347\276\244\351\203\250\347\275\262\345\256\236\347\216\260\350\267\250\346\234\215\345\212\241\347\253\257\351\200\232\344\277\241\347\232\204\350\220\275\345\234\260\346\226\271\346\241\210\343\200\213.md" "b/docs/md/netty/expand/2019-08-24-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\344\271\235\343\200\212Netty\351\233\206\347\276\244\351\203\250\347\275\262\345\256\236\347\216\260\350\267\250\346\234\215\345\212\241\347\253\257\351\200\232\344\277\241\347\232\204\350\220\275\345\234\260\346\226\271\346\241\210\343\200\213.md" index 1bea655d0..0791346df 100644 --- "a/docs/md/netty/expand/2019-08-24-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\344\271\235\343\200\212Netty\351\233\206\347\276\244\351\203\250\347\275\262\345\256\236\347\216\260\350\267\250\346\234\215\345\212\241\347\253\257\351\200\232\344\277\241\347\232\204\350\220\275\345\234\260\346\226\271\346\241\210\343\200\213.md" +++ "b/docs/md/netty/expand/2019-08-24-netty\346\241\210\344\276\213\357\274\214netty4.1\344\270\255\347\272\247\346\213\223\345\261\225\347\257\207\344\271\235\343\200\212Netty\351\233\206\347\276\244\351\203\250\347\275\262\345\256\236\347\216\260\350\267\250\346\234\215\345\212\241\347\253\257\351\200\232\344\277\241\347\232\204\350\220\275\345\234\260\346\226\271\346\241\210\343\200\213.md" @@ -469,7 +469,7 @@ spring: port: 6379 ``` ->index.jap | 页面操作,控制和展示的一些内容 +>index.jsp | 页面操作,控制和展示的一些内容 ```html <%-- diff --git "a/docs/md/netty/source-code/2019-09-10-netty\346\241\210\344\276\213\357\274\214netty4.1\346\272\220\347\240\201\345\210\206\346\236\220\347\257\207\344\270\200\343\200\212NioEventLoopGroup\346\272\220\347\240\201\345\210\206\346\236\220\343\200\213.md" "b/docs/md/netty/source-code/2019-09-10-netty\346\241\210\344\276\213\357\274\214netty4.1\346\272\220\347\240\201\345\210\206\346\236\220\347\257\207\344\270\200\343\200\212NioEventLoopGroup\346\272\220\347\240\201\345\210\206\346\236\220\343\200\213.md" index ffc465e3a..c607ae5b2 100644 --- "a/docs/md/netty/source-code/2019-09-10-netty\346\241\210\344\276\213\357\274\214netty4.1\346\272\220\347\240\201\345\210\206\346\236\220\347\257\207\344\270\200\343\200\212NioEventLoopGroup\346\272\220\347\240\201\345\210\206\346\236\220\343\200\213.md" +++ "b/docs/md/netty/source-code/2019-09-10-netty\346\241\210\344\276\213\357\274\214netty4.1\346\272\220\347\240\201\345\210\206\346\236\220\347\257\207\344\270\200\343\200\212NioEventLoopGroup\346\272\220\347\240\201\345\210\206\346\236\220\343\200\213.md" @@ -42,10 +42,11 @@ private void bing(int port) { ``` ## 类结构树 + NioEventLoopGroup 通过实现Java的并发编程包的方法,来实现自己的相关功能。 -![NioEventLoopGroup类结构树](https://fuzhengwei.github.io/assets/images/pic-content/2019/08/NioEventLoopGroup类结构树.png) ## EventExecutorGroup + EventExecutorGroup 使用next()方法负责提供EventExecutor。除此之外,它还负责处理生命周期,并且可以以一种全局的方式进行关闭。 >EventExecutorGroup.java @@ -213,7 +214,7 @@ public class NioEventLoopGroup extends MultithreadEventLoopGroup { - 在创建Netty服务端的时候,代码中实例化了两个EventLoopGroup分别是parentGroup、childGroup,parentGroup 主要用于接收请求链接,链接成功后交给childGroup处理收发数据等事件。 - - NioEventLoopGroup可以在构造方法中传入需要启动的线程数,默认的情况下他会在采用计算机核心数*2的方式去启动线程数量。另外目前很多计算机采用了超线程技术,那么4核心的机器,超线程后就是8核心,Netty在启动的时候随时会启动8*2=16个线程。 + - NioEventLoopGroup可以在构造方法中传入需要启动的线程数,默认的情况下他会在采用计算机核心数 * 2的方式去启动线程数量。另外目前很多计算机采用了超线程技术,那么4核心的机器,超线程后就是8核心,Netty在启动的时候随时会启动8*2=16个线程。 >超线程(HT, Hyper-Threading)是英特尔研发的一种技术,于2002年发布。超线程技术原先只应用于Xeon 处理器中,当时称为“Super-Threading”。之后陆续应用在Pentium 4 HT中。早期代号为Jackson。 [1] 通过此技术,英特尔实现在一个实体CPU中,提供两个逻辑线程。之后的Pentium D纵使不支持超线程技术,但就集成了两个实体核心,所以仍会见到两个线程。超线程的未来发展,是提升处理器的逻辑线程。英特尔于2016年发布的Core i7-6950X便是将10核心的处理器,加上超线程技术,使之成为20个逻辑线程的产品。 diff --git a/docs/md/other/guide-to-reading.md b/docs/md/other/guide-to-reading.md index 0d51b717e..34083038c 100644 --- a/docs/md/other/guide-to-reading.md +++ b/docs/md/other/guide-to-reading.md @@ -1,31 +1,40 @@ --- -title: 目录 +title: 编码指南 --- # bugstack虫洞栈 | 程序员的编码指南 🔥 ->你好,我是小傅哥,[《重学Java设计模式》](https://item.jd.com/13218336.html) 图书作者,一线互联网 Java 工程师、架构师。 +>你好,我是小傅哥,[《重学Java设计模式》](https://item.jd.com/13218336.html)、[《手写MyBatis:渐进式源码实践》](https://item.jd.com/13811216.html) 图书作者,一线互联网 Java 工程师、架构师。 -一个着迷于技术又喜欢不断折腾的技术活跃者,从13年毕业到进入互联网,开发过交易、营销类项目,实现过运营、活动类项目,设计过中间件,组织过系统重构,编写过技术专利。不仅从事业务系统的开发工作,也经常做一些字节码插桩类的设计和实现,对架构的设计和落地有丰富的经验。在热衷于Java语言的同时,也喜欢研究中继器、I/O板卡、C#和PHP! +我是一个着迷于技术,又喜欢不断折腾的技术活跃者,从13年毕业到进入互联网,开发过交易、营销类项目,实现过运营、活动类项目,设计过中间件,组织过系统重构,发表过数十篇技术专利。不仅从事业务系统的开发工作,也经常做一些字节码插桩类的设计和实现,对架构的设计和落地有丰富的经验。在热衷于Java语言的同时,也喜欢研究中继器、I/O板卡、C#和PHP! 除此之外小傅哥并不只满足于CRUD搬砖,也关心业务、运营、产品、数据、测试、运维等各项知识体系的完善学习,就研发架构设计来讲,更全面的学习会更有利于做出更长远的架构设计。同时完善个人知识体系也更有利于个人成长。 -所以你会看到小傅哥在工作之外的深夜、周末、假期会折腾于写文章、编小册、出书籍,并十分热情于对粉丝的交流、提问、解惑。并不深沉且少许逗比的我,希望能给大家带来最接地气的帮助和成长。 +所以你会看到小傅哥在工作之外的深夜、周末、假期会折腾于写文章、编小册、出书籍,并十分热情于对粉丝的交流、提问、解惑。并不深沉且少许逗比的我,希望我的沉淀,分享,也可以给你带来收获! --- ⏰ **提醒**:在接下来你对本博客的阅读中,如果遇到一些`内容`、`图稿`、`代码`等中的勘误都可以提交Issue或者PR的方式进行反馈,小傅哥会陆续进行完善,感谢您的支持; -- Issue:[https://github.com/fuzhengwei/CodeGuide/issues](https://github.com/fuzhengwei/CodeGuide/issues) -- PR:[https://github.com/fuzhengwei/CodeGuide/pulls](https://github.com/fuzhengwei/CodeGuide/pulls) - 你只需要在阅读文章的最下面找到`在 GitHub 上编辑此页`,即可完成克隆和提交PR -- 本站为公众号往期文章整理的小册,关注`公众号`:[bugstack虫洞栈](https://bugstack.cn/images/personal/qrcode.png) 可以知晓最新推文,避免错过最近正在更新的技术系列文章。 -- 其他:如果你在学习本站内容遇到不能解决的问题,可以联系作者:`小傅哥`,微信:`fustack` - 交个朋友👬🏻,不要错过成为技术同好的机会。 -- 加入星球:[码农会锁](https://t.zsxq.com/jAi2nUf) - 你可以获得本站所有学习内容的**指导**和**帮助**,还可以学习实战项目!`☕️一顿饭钱的支持,突破技术瓶颈` **小妙招**:关注公众号:[bugstack虫洞栈](https://bugstack.cn/images/personal/qrcode.png) 回复:`星球` 可以获取优惠券 + +- 贡献 + - Issue:[https://github.com/fuzhengwei/CodeGuide/issues](https://github.com/fuzhengwei/CodeGuide/issues) + - PR:[https://github.com/fuzhengwei/CodeGuide/pulls](https://github.com/fuzhengwei/CodeGuide/pulls) - 你只需要在阅读文章的最下面找到`在 GitHub 上编辑此页`,即可完成克隆和提交PR + +- 学习 + - 博客 [小傅哥虫洞栈](https://bugstack.cn/) 具备丰富的企业级实战技能,可以综合提高自己的全方面架构、技术、项目、运维,方面知识积累。 + - 项目 [星球「码农会锁」](https://bugstack.cn/md/zsxq/introduce.html) - 具有非常多的实战类业务项目、组件项目、开源项目等,☕️一顿饭钱的支持,突破技术瓶颈`! + - 演示 [嘎嘎强,嘎嘎哒学](https://gaga.plus/) - 应用级实战项目运行演示平台。用户可以体验项目运行效果,并学习每个功能的实现方案。 + +- 其他 + - 考试:[100道八股题考试测验](https://bugstack.cn/md/zsxq/material/exam.html) —— 你可以尝试验证自己的能力,考题范围:数据结构、算法、源码、设计模式、系统架构、中间件、网络通信、实战项目、扩展问题 + - 本站为公众号往期文章整理的小册,关注`公众号`:[bugstack虫洞栈](https://bugstack.cn/images/personal/qrcode.png) 可以知晓最新推文,避免错过最近正在更新的技术系列文章。 + - 其他:如果你在学习本站内容遇到不能解决的问题,可以联系作者:`小傅哥`,微信:`fustack` - 交个朋友👬🏻,不要错过成为技术同好的机会。 ## 一、本站知识阅览 @@ -36,13 +45,14 @@ title: 目录 而这几大块内容也是每一个较贵的 Java 程序员应该掌握的内容,可以包括: -- **Java&Spring**:以讲解Java、Spring核心知识为基础,用数学逻辑思维分析关于Java、Spring、Mybatis、Dubbo等核心源码技术内容。其中如[《Java 面经手册》](https://bugstack.cn/md/project/pdf/2021-01-26-Java%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8CPDF%E4%B8%8B%E8%BD%BD.html)是一本以面试题为入口讲解 Java 核心内容的技术书籍,书中内容极力的向你证实代码是对数学逻辑的具体实现。包括正在编写的[《手撸 Spring》](https://github.com/fuzhengwei/small-spring)通过手写简化版 Spring 框架,了解 Spring 核心原理。在手写的过程中会简化 Spring 源码,摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:IOC、AOP、Bean生命周期、上下文、作用域、资源处理等内容实现。这些都程序员学习技术成长过程中非常重要的知识,如果能深入学习那么对以后的个人成长帮助非常大。 +- **编程路书**:开发一套[《Java简明学习路书》](https://bugstack.cn/md/road-map/road-map.html),通过一个个小案例,为大家讲解这些技术栈的运用。帮助踏上这条路上的伙伴,以更简单明了的方式进入编程学习。最后再通过完整的项目实战,把这些技术栈串联起来运用。 +- **Java&Spring**:以讲解Java、Spring核心知识为基础,用数学逻辑思维分析关于Java、Spring、Mybatis、Dubbo等核心源码技术内容。其中如[《Java 面经手册》](https://bugstack.cn/md/project/pdf/2021-01-26-Java%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8CPDF%E4%B8%8B%E8%BD%BD.html)是一本以面试题为入口讲解 Java 核心内容的技术书籍,书中内容极力的向你证实代码是对数学逻辑的具体实现。包括正在编写的[《手撸 Spring》](https://bugstack.cn/md/spring/develop-spring/2021-05-16-%E7%AC%AC1%E7%AB%A0%EF%BC%9A%E5%BC%80%E7%AF%87%E4%BB%8B%E7%BB%8D%EF%BC%8C%E6%89%8B%E5%86%99Spring%E8%83%BD%E7%BB%99%E4%BD%A0%E5%B8%A6%E6%9D%A5%E4%BB%80%E4%B9%88%EF%BC%9F.html)通过手写简化版 Spring 框架,了解 Spring 核心原理。在手写的过程中会简化 Spring 源码,摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:IOC、AOP、Bean生命周期、上下文、作用域、资源处理等内容实现。这些都程序员学习技术成长过程中非常重要的知识,如果能深入学习那么对以后的个人成长帮助非常大。 - **算法逻辑和数据结构**:这部分内容主要以Java源码为入手,讲解其中的数学知识,包括:扰动函数、负载因子、拉链寻址、开放寻址、斐波那契(Fibonacci)散列法还有黄金分割点的使用等等,这也正是[《Java 面经手册》](https://bugstack.cn/md/project/pdf/2021-01-26-Java%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8CPDF%E4%B8%8B%E8%BD%BD.html)的核心内容所在。 - **面向对象**:[《Java 设计模式》](https://item.jd.com/13218336.html)的知识是在Java基础铺平,数据结构、算法逻辑有了一定的了解后,在深入学习和使用的技术。同样是一个需求在学过设计模式后,也阅读了不少别人优秀的代码,那么在他实现需求的时候,会拆分出很多的接口和接口的继承、抽象类的职责隔离实现、具体业务模块的分层、功能服务组件的细化、具体实现过程中对设计模式的运用等等。这样的代码实现后会非常具有易扩展和可维护的特点,否则一篇的ifelse不是坑自己就是坑下一个人。 - **中间件**:可能很大一部分研发并不会接触到中间件,也不太可能有人告诉你可以使用中间件的方式解决一些实际遇到的问题。因为大部分时候你都会认为中间件只是公司专门部门的人写的,或者是技术大牛搞的,总之与你没关系。但其实代码知识对数学逻辑的具体实现,业务开发有业务开发的方式,[《Spring 中间件和开发》](https://bugstack.cn/md/project/springboot-middleware/2021-03-31-%E3%80%8ASpringBoot%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%BC%80%E5%8F%91%E3%80%8B%E4%B8%93%E6%A0%8F%E5%B0%8F%E5%86%8C%E4%B8%8A%E7%BA%BF%E5%95%A6%EF%BC%81.html)也只是对Spring的关于容器中一些特定接口和类的使用,具体的还是普通的逻辑代码,比如暴露服务、采集日志、监控系统等。但如果你能早些学到这样技术的核心思想,那么对于升值、加薪、跳槽,都是非常有帮助的。 - **通信专题**:其实Netty是一项非常重要的技术,比如在RPC服务实现中的Dubbo、或者MQ、以及很多时候的通信里都是能用到的技术。就连小傅哥的第一次面试大厂也是靠着对Netty的学习,刷进来的!所以小傅哥编写了很多Netty从基础入门讲解到核心原理,告诉你如何处理半包、粘包,怎样定义消息协议,并开发了一个基于Netty的仿微信聊天项目,这些技术内容你都可以在我的博客学习到学习到。 - **字节码编程**:这项技术可能大多数研发,哪怕35岁的,可能也不一定接触到。但这样的技术你却基本都用过,比如你的IDEA是购买的吗,你怎么给让它能用的!你用过一些非入侵的全链路监控系统的,你通过字节码插桩搞过一些事情吗,那你用过Cglib吧,它的底层就是通过ASM字节码框架对字节码进行的一些列操作。 -- **实战项目**:以实战项目的方式学习互联网大厂开发中应用到的技术,通过这样的实践方式把一些学习的技术应用起来,而不是单单的去背资料,但最终不知道该如何把这些技术内容综合起来使用。所以这里小傅哥以实战项目为主,推出:[Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html) +- **实战项目**:以实战项目的方式学习互联网大厂开发中应用到的技术,通过这样的实践方式把一些学习的技术应用起来,而不是单单的去背资料,但最终不知道该如何把这些技术内容综合起来使用。所以这里小傅哥以实战项目为主,推出:[大营销平台系统](https://bugstack.cn/md/project/big-market/big-market.html)、[OpenAi 大模型应用服务](https://bugstack.cn/md/project/chatgpt/chatgpt.html)、[Lottery 分布式抽奖系统](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html)、[透视业务监控系统](https://bugstack.cn/md/zsxq/project/business-behavior-monitor.html)、[动态线程池组件](https://bugstack.cn/md/zsxq/project/dynamic-thread-pool.html)、[API网关](https://bugstack.cn/md/assembly/api-gateway/api-gateway.html)等众多项目。 - **关于**:除了技术学习以外,还有很多伙伴会经常问我一些关于学习、成长以及在职场中怎么活下去。所以我结合我自己在大厂互联网中的学习和成长经历,给读者伙伴写了不少此类的内容。如简历编写、招聘要求、技术资料、代码规范、评审晋升、薪资待遇、副业收入等等。这些内容可能很多会帮助你度过一个安定的职场生涯! ## 二、学习路线参考 @@ -53,14 +63,14 @@ title: 目录 - Java 面经手册:[https://download.csdn.net/download/Yao__Shun__Yu/14932325](https://download.csdn.net/download/Yao__Shun__Yu/14932325) - 重学Java设计模式:[https://download.csdn.net/download/Yao__Shun__Yu/19265731](https://download.csdn.net/download/Yao__Shun__Yu/19265731) -- 手撸 Spring:[https://download.csdn.net/download/Yao__Shun__Yu/21009038](https://download.csdn.net/download/Yao__Shun__Yu/21009038) - SpringBoot 中间件设计和开发:[https://juejin.cn/book/6940996508632219689](https://juejin.cn/book/6940996508632219689) -- 🔥Lottery 抽奖系统 - DDD 分布式实践:[https://t.zsxq.com/jAi2nUf](https://t.zsxq.com/jAi2nUf) +- 🔥Lottery Plus 大营销平台系 - DDD 分布式架构技术实践:[https://t.zsxq.com/199mpn9Lt](https://t.zsxq.com/199mpn9Lt) +- [更多项目](https://gaga.plus) ## 三、算法 -- 地址:[Java 数据结构和算法](https://bugstack.cn/md/algorithm/data-structures/2022-07-22-linked-list.html) -- 介绍:以Java源码为基础,结合使用场景,学习数据结构和算法 +- 地址:[Java 数据结构和算法](https://bugstack.cn/md/algorithm/data-structures/data-structures.html) +- 介绍:以Java源码为基础,结合使用场景,学习数据结构和算法。涵盖4类14种数据结构,包括:链表、数组、队列、堆栈、哈希表、堆、字典树、二分搜索树、平衡二叉树、2-3树、红黑树、并查集、图、布隆过滤器。 ## 四、Java @@ -186,7 +196,38 @@ title: 目录 ## 十、实战项目 -### 1. IM Netty 仿PC端微信 +### 1. 业务类型 + +#### 1.1 大营销平台系统 + +项目:[https://bugstack.cn/md/project/big-market/big-market.html](https://bugstack.cn/md/project/big-market/big-market.html) + +这个新项目,结合小傅哥已经带着大家完成的 OpenAi 大模型应用业务场景,做上层的营销活动。这就像互联网公司中有了电商、外卖、出行等场景一样,在场景之上做营销活动。所以我们的新项目是 **《大营销平台系统》**!因为小傅哥的星球之前做过了一个抽奖,那么这个项目会用新的DDD架构,对抽奖系统进行重构,并扩展出`营销账户`、`用户返利`、`积分兑换`等服务,完成一整套的营销平台功能。💥 + +![](https://bugstack.cn/images/article/project/big-market/roadmap-ddd-stc-05.png) + +#### 1.2 OpenAi 大模型应用服务体系构建 - API-SDK、鉴权、公众号、企业微信、支付服务 + +项目:[https://bugstack.cn/md/project/chatgpt/chatgpt.html](https://bugstack.cn/md/project/chatgpt/chatgpt.html) + +此项目以围绕类似 ChatGPT 生成式服务,构建微服务应用架构体系组件。包括;用户鉴权、公众号、多方支付、企业微信等对接方式,满足不同诉求的使用。并以模块化设计,积木式构建应用,让不同的场景诉求都可以配置化对接。 + +![](https://bugstack.cn/images/article/project/chatgpt/chatgpt-230422-01.png?raw=true) + +#### 1.3 Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践 + +- 地址:[Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html) +- 介绍:`Lottery 抽奖系统` 项目是一款互联网面向C端人群营销活动类的抽奖系统,可以提供抽奖活动玩法策略的创建、参与、记账、发奖等逻辑功能。在使用的过程中运营人员通过创建概率类奖品的抽奖玩法,对用户进行拉新、促活、留存,通常这样的系统会用在电商、外卖、出行、公众号运营等各类场景中。 + +![](https://bugstack.cn/images/article/project/lottery/introduce/system-list.png) + +在此项目中你会学习到互联网公司关于C端项目开发时候用到的一些,技术、架构、规范等内容。由于项目为实战类编程项目,在学习的过程中需要上手操作,小傅哥会把系统的搭建拉不同的分支列为每一个章节进行设计和实现并记录到开发日记中,读者在学习的过程中可以结合这部分内容边看文章边写代码实践。 + +- 技术:SpringBoot、Mybatis、Dubbo、MQ、Redis、Mysql、ELK、分库分表、Otter +- 架构:DDD 领域驱动设计、充血模型、设计模式 +- 规范:分支提交规范、代码编写规范 + +#### 1.4 IM Netty 仿PC端微信 - 地址:[IM Netty 仿PC端微信](https://bugstack.cn/md/project/im/2020-03-04-%E3%80%8ANetty+JavaFx%E5%AE%9E%E6%88%98%EF%BC%9A%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9%E3%80%8B.html) - 介绍:🎭 本项目是作者小傅哥使用JavaFx、Netty4.x、SpringBoot、Mysql等技术栈和偏向于DDD领域驱动设计方式,搭建的仿桌面版微信实现通信核心功能。课程文章已发布到GitChat专栏,欢迎购买。 @@ -202,7 +243,39 @@ title: 目录 **第三章节**:**功能实现**。这部分我们主要将通信中的各项功能逐步实现,包括;登录、添加好友、对话通知、消息发送、断线重连等各项功能。最终完成整个项目的开发,同时也可以让你从实践中学会技能。 -### 2. 《SpringBoot 中间件设计和开发》 +### 2. 组件类型 + +#### 2.1 透视业务流程 - 监控系统 + +项目:[https://bugstack.cn/md/zsxq/project/business-behavior-monitor.html](https://bugstack.cn/md/zsxq/project/business-behavior-monitor.html) + +本次项目会采用基于扩展 logback 日志上报数据进行 ognl 配置节点公式的方式进行采集、计算和可视化渲染。在这套项`小而美,小而精`的组件项目中,你可以学习到非常多的实战技能。 + +这套`透视业务流程的监控系统`,与 `Prometheus + Grafana`、`Skywalking` 有较大的差异。这两款监控都是系统健康度监控,而小傅哥带着大家做的是业务流程监控。*很多中大厂,也都有同类的业务系统* + +![img](https://bugstack.cn/images/article/project/business-behavior-monitor/business-behavior-monitor-03.png) + +#### 2.2 动态线程池组件实现 + +项目:[https://bugstack.cn/md/zsxq/project/dynamic-thread-pool.html](https://bugstack.cn/md/zsxq/project/dynamic-thread-pool.html) + +本次带着大家做的这款动态线程池组件项目,也是各个中大厂中都非常常见的组件能力。通过这样的学习,以中大厂的经验补充自身的技术积累,让自己的简历和职业生涯都有东西可讲。 + +我们知道,线程池(Thread Pool),是一种基于池化思想管理线程的工具,用于降低资源消耗、提高响应速度、提高线程的管理性。池化技术的引入,可以有效的减少线程频繁申请/销毁和调度所带来的额外开销。对于池化思想,我们还能看到;内存池、连接池、化粪池。 + +![](https://bugstack.cn/images/article/project/dynamic-thread-pool/dynamic-thread-pool-01.png) + +#### 2.3 支付SDK设计和开发 + +项目:[https://bugstack.cn/md/zsxq/project/ltzf-sdk-java.html](https://bugstack.cn/md/zsxq/project/ltzf-sdk-java.html) + +在面试中详细了解得知,不少伙伴压根是没有做支付的。而是一个假的模拟支付,修改数据库状态,当做支付而已。这样做有个很大的问题就是没法全面的了解支付流程,包括在`掉单`、`幂等`、`透传`、`回调`等方面的业务是很难清晰的理解的。所以在面试过程中也就压根不知道支付这一块。但不写支付,又觉得整个项目不完整,并且很多公司面试都会问一些支付的内容。那怎么办😰呢? + +小傅哥在带着大家做的项目和小场景中,有做过微信支付,支付宝沙箱支付,但想做真实的支付一般是需要个体户或者公司主体的。不过好在市面还有一些专门提供给个人使用的支付,比如;蓝兔、虎皮椒、PayJS。这些支付中,小傅哥测试验证了蓝兔支付,为它提供了一款SDK,让大家像使用微信支付一样简单的使用蓝兔支付。 + +![](https://bugstack.cn/images/article/project/ltzf-sdk-java/ltzf-sdk-java-04.png) + +#### 2.4 《SpringBoot 中间件设计和开发》 - 地址:[《SpringBoot 中间件设计和开发》](https://bugstack.cn/md/project/springboot-middleware/2021-03-31-%E3%80%8ASpringBoot%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%BC%80%E5%8F%91%E3%80%8B%E4%B8%93%E6%A0%8F%E5%B0%8F%E5%86%8C%E4%B8%8A%E7%BA%BF%E5%95%A6%EF%BC%81.html) - 介绍:说到底,为什么要扒开CRUD的表面,深入到核心源码实践学一些中间件开发技能,还不是希望自己对技术栈学习有一定的深度,免得面试时被人忽悠压薪资。就像人家问你:类的代理、反射调用是在什么场景用到的? 自定义注解是怎么和切面一起获取到信息使用的?你需要的yml配置信息是如何被SpringBoot加载并初始化的?Bean 是如何被注入到 Spring 容器,提供服务的?综上,等等这些技术点可能很多时候你所学到的只能称作为背答案、记结果,因为没有实操所以过后就忘而且也扛不住面试官的接连发问。 @@ -216,29 +289,26 @@ title: 目录 - **服务治理**:熔断、降级、限流、切量、黑白名单以及对现有方法的非入侵式扩展增强等,都可以成为是服务治理类组件,原本这类技术在早期是与业务逻辑代码融合的,后来逐步被拆解出来,开发成对应的组件。所以我们可以学习到,关于这类组件的包装、集成是如何做的。 - **字节码&插件**:在互联网的系统应用运维过程中,你一定会接触到各类的监控系统,而很多监控系统是非入侵的全链路监控,那么这些是如何实现的呢?其实它们是基于字节码插桩,对系统方法的增强,采集相应的运行时信息,进行监控的。再到扩展 JVMTI、IDEA 插件开发,都是为了整个研发过程的可持续交付和上线提高交付质量和降低人效的。 -**综上**,这些贯穿整个互联网系统架构中的各类典型中间件,都会在后续章节中陆续讲解出来,它们是如何设计和实现的,一点点带你解开中间件的神秘面纱,让你的技术栈知识也增加一些有深度的并且是可以亲自操作的内容。 +#### 2.5 API网关 -### 3. Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践 +项目:[https://bugstack.cn/md/assembly/api-gateway/api-gateway.html](https://bugstack.cn/md/assembly/api-gateway/api-gateway.html) -- 地址:[Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html) -- 介绍:`Lottery 抽奖系统` 项目是一款互联网面向C端人群营销活动类的抽奖系统,可以提供抽奖活动玩法策略的创建、参与、记账、发奖等逻辑功能。在使用的过程中运营人员通过创建概率类奖品的抽奖玩法,对用户进行拉新、促活、留存,通常这样的系统会用在电商、外卖、出行、公众号运营等各类场景中。 +API网关系统用于统一管理RPC(Dubbo)通信接口,通过协议解析和泛化调用统一对外提供HTTP服务的系统。这套系统是微服务架构设计,分为核心通信、启动引擎、注册中心、管理平台以及上报接口服务。这套API网关也是随着对公司传统庞大的单体应用(All in one)拆分为众多的微服务(Microservice)以后,所引入的统一通信管理系统。用于运行在外部HTTP请求与内部RPC服务之间的一个流量入口,实现对外部请求的协议转换、参数校验、鉴权、切量、熔断、限流、监控、风控等各类共性的通用服务。 -![](https://bugstack.cn/images/article/project/lottery/introduce/system-list.png) +![img](https://bugstack.cn/images/article/assembly/api-gateway/api-gateway-0-04.png?raw=true) -在此项目中你会学习到互联网公司关于C端项目开发时候用到的一些,技术、架构、规范等内容。由于项目为实战类编程项目,在学习的过程中需要上手操作,小傅哥会把系统的搭建拉不同的分支列为每一个章节进行设计和实现并记录到开发日记中,读者在学习的过程中可以结合这部分内容边看文章边写代码实践。 +这是一整套API网关的核心通信模型结构图,以API网关算力的多套服务注册到网关中心开始,拉取RPC应用接口并完成映射HTTP调用操作。最终允许用户通过 Nginx 访问和路径重写的负载均衡管理,调用到具体的网关算力中执行协议解析和RPC接口的泛化调用并最终返回结果数据。 + +**综上**,这些贯穿整个互联网系统架构中的各类典型中间件,都会在后续章节中陆续讲解出来,它们是如何设计和实现的,一点点带你解开中间件的神秘面纱,让你的技术栈知识也增加一些有深度的并且是可以亲自操作的内容。 -- 技术:SpringBoot、Mybatis、Dubbo、MQ、Redis、Mysql、ELK、分库分表、Otter -- 架构:DDD 领域驱动设计、充血模型、设计模式 -- 规范:分支提交规范、代码编写规范 ## 📚PDF 下载 - Java 面经手册:[https://download.csdn.net/download/Yao__Shun__Yu/14932325](https://download.csdn.net/download/Yao__Shun__Yu/14932325) - 重学Java设计模式:[https://download.csdn.net/download/Yao__Shun__Yu/19265731](https://download.csdn.net/download/Yao__Shun__Yu/19265731) -- 手撸 Spring:[https://download.csdn.net/download/Yao__Shun__Yu/21009038](https://download.csdn.net/download/Yao__Shun__Yu/21009038) - 字节码编程:[https://download.csdn.net/download/Yao__Shun__Yu/12505051](https://download.csdn.net/download/Yao__Shun__Yu/12505051) - IDEA Plugin 开发手册:[https://download.csdn.net/download/Yao__Shun__Yu/77484299](https://download.csdn.net/download/Yao__Shun__Yu/77484299) ## 关于 -关于自己、关于学习、关于职场,👉 如果你是刚入行、在外包、跨语言学习、想跳槽大厂、缺少学习动力等,可以阅读小傅哥的成长故事,这个系列包括了我的个人在外包到大厂的成长、跳槽的过程、互联网的学习经历,那么可以阅读一下。Go -> [https://bugstack.cn/md/about/me/about-me.html](https://bugstack.cn/md/about/me/about-me.html) \ No newline at end of file +关于自己、关于学习、关于职场,👉 如果你是刚入行、在外包、跨语言学习、想跳槽大厂、缺少学习动力等,可以阅读小傅哥的成长故事,这个系列包括了我的个人在外包到大厂的成长、跳槽的过程、互联网的学习经历,那么可以阅读一下。Go -> [https://bugstack.cn/md/about/me/about-me.html](https://bugstack.cn/md/about/me/about-me.html) diff --git a/docs/md/other/road-map.md b/docs/md/other/road-map.md new file mode 100644 index 000000000..dbbc6d931 --- /dev/null +++ b/docs/md/other/road-map.md @@ -0,0 +1,46 @@ +--- +title: 编程路书 +--- + +# bugstack虫洞栈 | 编程路书 v1.0 👣 - Java 程序员的简明教程 👨🏻‍💻 @小傅哥 + +
+ +
+ +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言:授业解惑 + +`我知道,你不知道的还有很多!` + +你了解计算机要从哪里开始学习吗?你清楚为了能找到工作要学习多少知识吗?你知道承接需求到开发交付上线串联了多少个技术栈吗? + +对于这些内容即使从事编程2-3年的研发,大部分人也没有一个全局的概括,而在校大学生更是不清楚自己是从哪开始要到哪里去。收集了几十G到几百G的资料也不知道要从哪开始看,看哪些是对自己当前阶段帮助最大的。 + +**所以**在这个22年的1024程序员👨🏻‍💻节,小傅哥编写了一版**Java 编程路书**,希望帮助到更多从事这一行业的伙伴,可以更好的了解都要学习哪些技术。并且小傅哥会不断地收集、整理、归纳出来优秀的硬核资料,帮助大家学习。 + +## 二、查阅:路书地图 + +- **地址**:[https://github.com/fuzhengwei/RoadMap](https://github.com/fuzhengwei/RoadMap) - 提供了路书仓库,我会在这里更新各项资料,你可以进入后,点击右上角 Star 进行收藏(这样我更新后你会收到通知) +- **介绍**:整个路书以需求承接到开发交付为视角,包括:计算机基础、系统和架构设计、环境搭建、系统开发、常用类库、调试、测试、质量分析、发布部署。通过这样的全局的视角,来告诉你在哪里,要去哪里。*点击各个技术栈可以直接进入内容* +- **说明**:**A**——核心技术,学习完能承担大部分工作、**B**——辅助路线,学习完能更好的完成工作。 + +--- +
+ + + +## 三、收藏:资料仓库 + +- **仓库**:[https://github.com/fuzhengwei/RoadMap](https://github.com/fuzhengwei/RoadMap) +- **介绍**:以编程路书为指导,小傅哥会不断的添加相关的技术内容。也欢迎小伙伴提交 PR/Issue 来完善资料。 + + \ No newline at end of file diff --git a/docs/md/product/book/design-pattern.md b/docs/md/product/book/design-pattern.md new file mode 100644 index 000000000..f2516dff8 --- /dev/null +++ b/docs/md/product/book/design-pattern.md @@ -0,0 +1,130 @@ +--- +title: 2021年:《重学Java设计模式》 +lock: no +--- + +# 久等了,小傅哥的《重学Java设计模式》终于出版了,彩印&纸质! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
原文:[https://mp.weixin.qq.com/s/g9LYQEqzOeiYOpfG_5XFYg](https://mp.weixin.qq.com/s/g9LYQEqzOeiYOpfG_5XFYg) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +`来自延迟满足的幸福` + +可能你的生活里很多时候很多人都在教你怎么快,“一年面上P6”、“一周学会Java”、“一文看懂C++”,似乎这些看上去的快,就是达到终点的最佳路径。而实际上`快`,却让你在本该沉淀的路上,漂浮了起来,一切感觉都美好,就是什么也搞不了。 + +但如果你愿意慢下来,其实可以收获更多。就像我愿意用2年时间每周末和放假都写技术文章,这两年也从不推和技术无关的内容,也一点点把GitHub积累起来,也写了很多专栏。但这些都是慢下来完成的! + +`一度成为字节跳动的人` + +2020年07月12日,小傅哥的《重学Java设计模式》PDF版在公众号首发,但没想到那天起这本设计模式彻底火了,火成什么个鬼样子呢,几乎全网的号主都不知情的情况下被广告主投放过 `字节跳动总结的设计模式 PDF 火了,完整版开放下载!` + +![](https://bugstack.cn/assets/images/story/story-3-01.png) + +1. 那3个月这本书我提供的链接全网下载量一度突破30万次,不能统计到的还有很多! +2. 公众号一天能涨粉600个+ +3. 每天都有人问小傅哥,你是不是字节跳动的 +4. 百度搜小傅哥竟然给我加了热词“小傅哥的设计模式” +5. GitHub 设计模式对应的代码库持续霸榜 GitHub Trending +6. 可能就是从那段时间起,很多人知道了我,虽然当时不太喜欢被这样宣传,但也就这样莫名其妙的火了 + +`方向对了,快,可能是最大的障碍` + +也是从火了那段时间开始,每天都有出版社编辑联系,要不要出书,最开始并没有心动,也觉得自己文笔不好,还很多错字。 + +之所以后来上车了是因为遇到了宋亚东,给我介绍、给我讲解、给我分析,好吧!在20年10月1日放假起,我开始重新整理设计模式稿件,重新整理文章、收集粉丝反馈、绘制技术图稿,一点点的完成所有内容并添加新的章节,于11月左右交给出版社,接下来的路漫漫长.... + +一本书的出版要选题、交稿、审稿、之后是一遍遍的改稿、审核、改稿、审核,终于感觉要完事了又开始了三审三校,说要过不了就`不成功便成仁`!好在是一周左右时间通过了,接下来又申请书号、出版印刷、晾干、装订,嗯多久呢,从提交编辑到今天上架京东,用了7个月时间,在加上我的编辑创建,这本书耗时一年半出版了! + +--- + +截至到今天终于能给**粉丝一个交代了**,那么多伙伴自己喜欢这本书,可算是能让你们拿到一本,`全彩印的`、`没错字的`、`有类图的`、`加内容的`,可以随时在手边查阅的烂代码解决方案型图书! + +## 二、简介 + +![](https://bugstack.cn/assets/images/story/story-3-02.png) + +欢迎再次来到这里,很高兴你`将`拿到这本**纸质书**,如果你能坚持看完并按照书中的例子进行实践,那么在编程开发的世界里,就又多了一个可以写出良好代码的人,同时也为架构师培养储备了一个人才。 + +本书是一本基于互联网真实案例编写的Java设计模式实践图书。全书以解决方案为核心,从实际开发业务中抽离出交易、营销、规则引擎、中间件、框架源码等22个真实场景,对设计模式进行全面、彻底的分析。帮助读者灵活地使用各种设计模式,从容应对复杂变化的业务需求,编写出易维护、可扩展的代码结构。本书融合了生动有趣的动画插图和实践开发的类结构图,让读者不仅能体会设计模式的概念和原理,更能清楚地知晓落地方法。此外,本书还介绍了DDD四层架构、RPC中间件设计、分布式领域驱动设计和设计模式的结合使用等内容。 + +本书适合计算机相关行业的研发人员、高等院校计算机专业的学生阅读。无论是初学者,还是中、高级研发人员都能从本书中有所获益。 + +### 1. 谁发明了设计模式? + +设计模式的概念最早是由 `克里斯托佛·亚历山大` 在其著作 `《建筑模式语言》` 中首次提出的。 本书介绍了城市设计的 “语言”,提供了253个描述城镇、邻里、住宅、花园、房间及西部构造的模式, 而此类 “语言” 的基本单元就是模式。后来,`埃里希·伽玛`、 `约翰·弗利赛德斯`、 `拉尔夫·约翰逊` 和 `理查德·赫尔姆` 这四位作者接受了模式的概念。 1994 年, 他们出版了 `《设计模式: 可复用面向对象软件的基础》` 一书, 将设计模式的概念应用到程序开发领域中。 + +其实有一部分人并没有仔细阅读过设计模式的相关书籍和资料,但依旧可以编写出优秀的代码。这主要是由于在经过众多项目的锤炼和对程序设计的不断追求,从而在多年编程历程上提炼出来的心得体会。而这份经验最终会与设计模式提到的内容几乎一致,同样会要求高内聚、低耦合、可扩展、可复用。你可能也遇到类似的经历,在学习一些框架的源码时,发现它里的某些设计和你在做开发时一样。 + +### 2. 我怎么学不会设计模式? + +钱也花了,书也买了。代码还是一坨一坨的!设计模式是由多年的经验提炼出来开发指导思想。就像我告诉你自行车怎么骑、汽车怎么开,但只要你没跑过几千公里,你能记住的只是理论,想上道依旧很慌! + +**所以**,本书会以互联网真实案例为基础,带着你使用设计模式的思想去优化代码。从而学习设计模式的心得并融入给自己。当然这里还需要多加练习,一定是*人车合一*,才能站在设计模式的基础上构建出更加合理的代码。 + +### 3. 适合人群 + +1. 具备一定编程基础在工作1-3年的研发人员 +2. 希望通过此书提升编码思维,剔除到代码中的坏味道 +3. 有意愿成为架构师,但还处在一定瓶颈期 +4. 学习过设计模式,可是一直想找到一本可以落地真实场景参照的书籍 + +### 4. 我能学到什么 + +1. 优化平时开发中的ifelse语句,让代码更加整洁 +2. 看设计模式不再是用理论生搬硬套,这次可以有点用 +3. 站在更高的角度去看待编程开发,学会更多的面向对象的思维,尤其是;接口、抽象类、多态等使用 +4. 升职、加薪,良好的代码是效能提升的基础,成为本组编码最靓的精神小伙 + +## 三、📚预售5折下单 + +全书彩印、动画图稿、类图添加、内容夯实,是你在手边的可当编码参考的工具型书籍,好看、好用、好香! + +**4.21~4.23 图书节,新书5折预售,可以下单了!** + +![](https://bugstack.cn/assets/images/story/story-3-03.png) + +- 链接:[https://item.jd.com/13218336.html](https://item.jd.com/13218336.html) +- 搜索:[jd.com](https://item.jd.com/13218336.html) 搜索:`重学Java设计模式` +- 直达:公众号点击阅读原文,直接进入购买链接,快! +- 扫码: + ![](https://bugstack.cn/assets/images/story/story-3-04.png) + +## 四、🎉回馈粉丝活动 + +>牛吹完了,接下来回馈粉丝一波奖品,感谢一直以来对小傅哥的支持。 + +### 1. 礼品包括 + +- 一等奖、书籍[5名]:重学Java设计模式,`签名版` +- 二等奖、玩具[5名]:象棋、小颗粒玩具、四驱兄弟四驱车 +- 三等奖、水杯[5名]:京东公仔一只 + +*赞赏内的钱💰当做邮费使用,超出部分进小傅哥裤兜了😄🎁* + +### 2. 得奖规则 + +在本公众号:bugstack虫洞栈,活动原文中:[https://mp.weixin.qq.com/s/g9LYQEqzOeiYOpfG_5XFYg](https://mp.weixin.qq.com/s/g9LYQEqzOeiYOpfG_5XFYg) +- 对文章进行`留言并转发朋友圈`,找伙伴给你的留言点赞 +- 以个人留言`被读者点赞数量`为排名,最高的前15名依次获得一、二、三等奖 +- 仅记录个人攒点最高的留言,多次留言取最多次 + +### 3. 活动说明 + +- **时间范围**:2021-04-23 07:55:00 - 2020-04-25 23:59:59,共计3天计票 +- **公布时间**:2021年04月26日,星期一 +- **公布方式**:小傅哥的朋友圈公布,*记得添加小傅哥微信:`fustack`* +- **领奖方式**:看到小傅哥朋友圈后,主动联系小傅哥。提供;收获人、收获地址、手机号等必要信息。*😄嘿...嘿,我会保密的你的信息!* + +## 五、👣 收个尾 + +这本书的出版算是在技术成长路上的一次打卡,了解了定稿、三审三校、书号、印刷、上架等等流程,而这些其实是我,`就想知道知道我没经历过的风风雨雨后面的彩虹到底有多美`! + +感谢我能在这一路上遇到的人遇到的事,是粉丝伙伴的陪伴、是号主好友的支持、是各平台的服务,让我从一点点做的稍有起色,但也还好初心仍在,这条路上我扔是一直坚持的少年! + +感谢:`cxuan、Guide哥、Hollis总、敖丙大佬、labuladong、帅地、小灰总、小林、张开涛`这些伙伴给力的推荐语,感谢出版社编辑`宋亚东`、`杨中兴`让图书能快速的和大家见面,感谢粉丝的支持让我“敢”出版,感谢所有小伙伴! + diff --git a/docs/md/product/book/mybatis.md b/docs/md/product/book/mybatis.md new file mode 100644 index 000000000..56f02dff7 --- /dev/null +++ b/docs/md/product/book/mybatis.md @@ -0,0 +1,75 @@ +--- +title: 2023年:《手写MyBatis:渐进式源码实践》 +lock: no +--- + +# 《手写MyBatis:渐进式源码实践》—— 小傅哥新书上市!教你把MyBatis当成项目从零开发。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
图书:[https://item.jd.com/13811216.html](https://union-click.jd.com/jdc?e=618%7Cpc%7C&p=JF8BANIJK1olXwUEXVhVAE4UC18IGVIVXQUCU24ZVxNJXF9RXh5UHw0cSgYYXBcIWDoXSQVJQwYAXV5dC0sQHDZNRwYlNlscAS4ZCQByfStdbxN3PkNgUR5ZaEcbM244G1oUXQEKU1hfCHsnA2g4STXN67Da8e9B3OGY1uefK1olXQEEUltZDUkQBW4KHmsSXQ8yIgoCXAhHXjhMK2slXjYFVFdJDjlWUXsOaWslXTYBZF5UAUsXC2oNHEcVXAYLUUJdD00RBmsNGVwQVQUBZFxcCU8eM18) + +>沉淀、分享、成长,让自己和他人都能有所收获! + +**讲屁话没有用**,想学好编程突破阶段瓶颈,为自己的职业生涯续期。就要把时间放在有价值的硬核项目上。因为`硬核项目` + `时间投入` = `价值回报`! + +所以一股技术清流的小傅哥,为你编写了一本新书 **《手写MyBatis:渐进式源码实践》**。全书21章320页耗时2年出版。此书以实现MyBatis项目为目标,进行逐个功能模块渐进式拆解实现。就像手把手带着你敲项目一样,完成MyBatis的源码学习。通过这样的学习方式带你**领略源码级设计思维**,**突破满脑子都是MVC架构的技术瓶颈**。 + +所以千万别觉得开发项目只有 `MVC` 和 `DDD` 架构,否则为什么像 MyBatis 这样的源码不用 MVC架构写呢?—— `相信我,跟着小傅哥学习,会让你的编程思维提升到更高的水平。` + +## 一、为什么学源码? + +`代理`、`反射`、`池化`、`缓存`,MyBatis 给我们的不只是一个 ORM 框架,还包括了它经过深思熟虑所做的分层设计以及对应产生的,行之有效的解决方案。MyBatis 的存在不需要让你再刀耕火种般创建 JDBC,也不需要像使用 Hibernate 那么厚重到还需要增加学习 HQL 语句。同时 MyBatis 还支持通过插件机制扩展;监控、加密、路由等功能。因而如此简单且高效的 MyBatis ORM 框架,备受互联网大厂青睐,也是每一个 Java 程序员必须掌握的技术。 + +除了运用以外,MyBatis 框架也是众多码农,最能最先接触到的一个优质的**源码级别复杂项目**。此源码为了实现如此长周期软件迭代和维护,运用了分治和抽象进行模块设计,使用了**数10种**设计模型进行代码开发。这哪仅仅是一个 ORM 框架,**这简直是学习设计模式的最佳源码级实践资料**。 + +但就是这样已经很牛逼的学习资料,天天使用的技术框架。而且明知道学习它能有巨大的收获,但却是无从下手。因为很大一部分研发伙伴,在没有经历过中间件的设计和开发,满脑子都是 MVC 架构,也没用过几个设计模式的情况下,很难读懂源码级框架的设计。 + +`为此已经在 MyBatis 源码学习中得到受益的小傅哥`,希望把应对这样的硬核项目学习的方式方法,分享给从事编程开发的技术同好。通过我对 MyBatis 框架的理解和多年中间件的开发的经验,把 MyBatis 框架拆解为一个以`需求驱动`、`分支开发`、`渐进实现`的方式,展示给读者。**让即使是编程小白,也能沿着这条路走到终点获得巨大收获。** + +
+ +
+ +## 二、学源码的必要! + +从此你的简历就是可以写一段**《手写MyBatis》**项目学习:”我就是掌握了复杂源码的架构设计能力、我就是吸收了复杂场景分治和抽象的思想、我就是学会了复杂结构中设计模式的运用“。聊 MyBatis 聊的就是你手写的代码,有什么不会的,来你问吧。**以后路,你可以横着走!** 编写到简历上,给简历加分; + +1. 体现在专业技能上,例如;深入学习 MyBaits 核心流程模块,包括;会话、反射、代理、事务、插件等流程,熟练掌握 ORM 框架的设计思想、实现方式和应用价值。并能按需结合 MyBatis 的插件机制,开发属于企业自己所需的功能,包括;数据分页、数据库表路由、监控日志、数据安全等方面。 +2. 体现在项目经验上,例如;`对校招和实习比较有用` 把 MyBatis 当一个学习项目来描述,这是你在离校前,最可能接触到的一个完整的、成型的、知名的,有企业使用的框架。你就按照自己学习并开发了这样一个框架为目标来写项目,并描述出这个项目,你用了什么技术栈,解决了什么问题,学习到了哪些知识。 +3. 体现在项目应用上,例如; 关于MyBatis 的项目,一般都是插件类开发,比如各类的MyBatis 插件,都是基于框架的深入整合类技术解决方案,体现在简历上,非常抓眼球。一看你就是有深度和自研能力的研发人员。—— 一般不让你造轮子,但需要你有造轮子的能力,这样企业中一些软件可以被你进行优化和修改。 +4. 体现在解决问题是上,例如; 在你的自己的业务项目中,深入一些关于解决了原项目使用 MyBatis 时所遇到的问题,因为你学习过源码,所以非常清晰这样的流程,因此解决了一个问题。包括;事务、查询次数、批查询、插件能监听到的四个类(ParameterHandler、ResultSetHandler、StatementHandler、Executor )你给了更好的选择。 + +## 三、下手这本新书! + +
+ +
+ +本书共 22 章: + +- 第 01 ~ 04 章:拆解和实现 ORM 框架的基本功能,构建会话的基本调用流程,初解析 XML 文件,以及串联 DefaultSqlSession 结合解析配置项获取展示信息。 +- 第 05 ~ 08 章:创建和使用数据源,池化技术的实现,完成执行 SQL 语句的操作,同时引入反射工具包,实现对属性信息的获取和设置。 +- 第 09 ~ 12 章:以实现 ORM 框架的基本功能为目的,完善静态 SQL 的标准化解析、参数设置和结果封装,使整个 ORM 框架可以处理基本的增、删、改、查操作。 +- 第 13 ~ 19 章:以完善 ORM 框架的核心功能逻辑为目的,实现注解 SQL 解析、 ResultMap 参数、事务处理自增索引、动态 SQL 解析、插件、一级缓存和二级缓 存等功能。 +- 第 20 ~ 22 章:利用 ORM 框架整合 Spring 和 SpringBoot,并介绍整个核心流程, 同时总结 ORM 框架开发中涉及的 10 种设计模式。 + +本书通过渐进式的开发方式来实现整个 MyBatis 核心源码的开发。每章开头会先列出难度和重点,再介绍要处理的问题、具体设计和实现代码,最后给出测试验证和总结。—— **我希望教会你的不只是MyBatis源码,还有手撕源码的本事!** + +| 书籍样章截图 | +| :--------------------------------------------------------: | +|
| +|
| + +## 四、源码全貌地图! + +这是小傅哥在编写**《手写MyBatis:渐进式源码实践》**图书时,绘制的源码全貌地图。并结合地图的脉络,带着大家逐步实现这里面的功能模块,分章节细化各个模块的实现流程,最终让读者实现出一个丰富、全面、细致的 ORM 框架。 + +![](https://bugstack.cn/images/article/product/book/mybatis-06.png) + +--- + +**感谢图书编辑**:`宋亚东`、`杨中兴` + +**感谢大佬推荐**:`思否CTO-祁宁(@Joyqi)`、`中国科学院大学研究生导师-刘俊明`、`Apipost 创始人-穆红伟`、`京东垂直业务负责人-孙浩`、`京东授信认证业务技术负责人-郭泽渊`、`GitHub开源项目JavaGuide作者-G哥`、`《深入理解高并发编程:核心原理与案例实战》图书作者-冰河` + diff --git a/docs/md/product/book/spring.md b/docs/md/product/book/spring.md new file mode 100644 index 000000000..831942f71 --- /dev/null +++ b/docs/md/product/book/spring.md @@ -0,0 +1,122 @@ +--- +title: 2022年:《手写Spring:渐进式源码实践》 +lock: no +--- + +# 《手写Spring:渐进式源码实践》—— 小傅哥新书上市!教你把Spring当成项目从零开发。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
图书:[https://u.jd.com/4LapTH4](https://u.jd.com/4LapTH4) + +>沉淀、分享、成长,让自己和他人都能有所收获! + +## 一、前言:技术顶峰 + +`IOC`、`AOP`、`SPI`,Spring 给你的不只是一个开发框架,还包括它的设计思想。它通过解耦 Bean 对象的实例化过程,管理 Bean 的生命周期,来维护你在程序开发中所需对象使用过程。让你不需要刀耕火种般 new 一个对象,也不需要如 EJB 一样笨重臃肿的开发维护,而像春风一样润物(`万物皆可Bean`)细无声的使用。因此可以说它完全担得起 **Java 技术设计的顶峰**。 + +除了运用以外,Spring 框架也是众多码农,最能最先接触到的一个**源码级复杂项目**。任何初出新手村蜕变的码农,寻觅苦找的锻炼项目,都不如学习 Spring 源码来的痛快。从架构设计的复杂、从分治抽象的运用、从设计模式的驾驭,Spring 框架都是顶级的,也是最能给你带来丰富收获的。 + +但学习 Spring 源码难吗?难,难到编程个1-2年的研发,也不知道从哪下手学习。看到大部分资料和书籍也都是从一个知识点直接透析到骨头。没有阅读源码经验的小白,根本没法如编写者感同身受般学习,云里雾里一样的看,过眼云烟一样的忘。 + +为啥会这样,因为 Spring 发展太久了,源码太大了,主干核心源码外的繁枝末节有太多太多。就像沙发🛋左移套个套,套上盖罩,罩上铺块小布料。但除了沙发以外的套、罩、布料对初学源码的研发来说,并没有那么重要。我要的就是最初的沙发,最开始的木板,看看它的结构,闻闻它的味道。 + +**所以**,如果你想真的把 Spring 这个源码级复杂框架的设计和实现精髓吸收喽,就应该像开发一个项目一样,跟着小傅哥写一遍。只有这个项目是你写的,你才能知道哪些细节是如何处理的,那些设计是如何驾驭的。 + +## 二、驾驭:复杂源码 + +对于大部分使用 Spring 框架的研发人员来说,可能在遇到 Spring 框架的报错提醒, 以及需要基于 Spring 框架开发 SpringBoot Starter 等技术类组件时,都会尝试阅读 Spring 框架的源码。由于 Spring 框架的源码庞大、复杂,也不像平常的业务流程代码开发一样 具有分层结构,并且其中还使用了大量的设计模式,所以阅读难度较大。研发人员很难厘清其中的调用链路和各个类之间的关系。 + +小傅哥在最初学习Spring时,也阅读了不少关于 Spring 的图书,在反复学习后,仍然不能轻易理解 Spring 框架中各个功能的实现细节。其中一个原因是自己没有手动实现,只阅读了图书,很难完全掌握 Spring 框架的精髓。 + +因此,小傅哥采用从零手写 Spring 的方式,摒弃 Spring 源码中繁杂的内容,选择整体框架中的核心逻辑,简化代码实现过程,保留核心功能,如 IOC、AOP、Bean 的生命周期、上下文、 作用域和资源处理、事务等。在开发过程中,细化功能模块,逐步完成一个简单版的 Spring 框架。 + +在学习过程中,小傅哥对 Spring 框架有了非常深入的了解和认识,也体会了更多精妙的设计原则和设计模式。彻底从实现上搞清楚;上下文如何管理、Aware如何通过SPI机制处理感知对象通知、切面拦截如何设计、三级缓存循环依赖如何实现、ORM框架怎么整合到Spring等等。 + +**所以**,把关于手动实现简单版 Spring 框架的内容编写成书,希望可以帮助更多的研发人员学习 Spring 源码,编写出有价值的源码设计方案。 + +## 三、案例:学以致用 + +《手写Spring》完成后我的能力如何体现?给个案例。 + +1. 体现在专业技能上,例如; +1.1 深入学习 Spring 核心流程模块,包括;IOC、AOP、依赖倒置等流程,掌握Spring解决复杂场景所运用的分治、抽象和知识(设计模式、设计原则),在解决Spring场景问题时,可以从核心原理上给出方案。同时也具备基于 Spring 开发 SpringBoot Starter 技能,为复杂项目减少同类共性需求的开发,凝练通用的技术组件,减少研发成本。 +1.2 深入学习 MyBaits 核心流程模块,包括;会话、反射、代理、事务、插件等流程,熟练掌握 ORM 框架的设计思想、实现方式和应用价值。并能按需结合 MyBatis 的插件机制,开发属于企业自己所需的功能,包括;数据分页、数据库表路由、监控日志、数据安全等方面。 + +2. 体现在项目经验上,例如;—— 对校招和实习比较有用 +把 Spring、MyBatis 当一个学习项目来描述,这是你在离校前,最可能接触到的一个完整的、成型的、知名的,有企业使用的,框架。你就按照自己学习并开发了这样一个框架为目标来写项目,并描述出这个项目,你用了什么技术栈,解决了什么问题,学习到了哪些。 + +3. 体现在项目应用上,例如; +关于 Spring、MyBatis 的项目,一般都是插件类开发,比如各类的 SpringBoot Starter,MyBatis 插件,都是基于框架的深入整合类技术解决方案,体现在简历上,非常抓眼球。一看你就是有深度和自研能力的研发人员。—— 一般不让你造轮子,但需要你有造轮子的能力,这样企业中一些软件可以被你进行优化和修改。 + +4. 体现在解决问题是上,例如; +在你的自己的业务项目中,渗入一些关于解决了原项目使用 Spring 时,关于感知 Aware 方式或者结合 FactoryBean 包装对象等,所遇到的问题,因为你学习过源码,所以非常清晰这样的流程,因此解决了一个问题。通用 MyBatis 也适用于这样的描述方式,包括;事务、查询次数、批查询、插件能监听到的四个类(ParameterHandler、ResultSetHandler、StatementHandler、Executor )你给了更好的选择。 + +## 四、新书:五折下单 + +好!那么独乐不如众乐,我给你掌握源码的机会,也给你名正言顺的初出。 + +从此你的简历就是可以写一段《手写Spring》项目学习;我就是掌握了复杂源码的架构设计能力、我就是吸收了复杂场景分治和抽象的思想、我就是学会了复杂结构中设计模式的运用。聊 Spring 聊的就是你手写的代码,有什么不会的,来你问吧。**以后路,你可以横着走!** + +- 扫码下单,扫码下面👇🏻的二维码进行下单 +- 链接下单:[https://u.jd.com/4LapTH4](https://u.jd.com/4LapTH4) + +
+ +
+ +本书共21章; +- 第1 - 10章:主要介绍IOC 容器,逐步完善一个简单的Spring Bean 容器的相关功能,引入实例化策略、注入属性和依赖、设计应用上下文、处理Bean 对象的生命周期,以及实现感知容器对象的监听等。 +- 第11 - 12章:主要介绍AOP 切面,基于JDK、Cglib 的动态代理、方法拦截、切点表达式等技术,将代理与Spring Bean 容器整合,提供AOP 切面功能。 +- 第13 - 17章:扩展简单版Spring 框架的自动化功能,完成自动扫描注册、注解和代理注入,以及通过三级缓存处理对象的循环依赖等功能。 +- 第18 - 21章:基于简单实现的Spring 框架整合JDBC、事务的功能,开发一个简单版的ORM 框架,并将ORM 框架整合到Spring Bean 容器中,介绍自定义代理对象的扫描和注册过程。 + +本书主要通过渐进式开发功能模块,以实现开发整个Spring 框架的核心源码。首先,每章开头都会列出难度和重点;然后,正文中会介绍要处理的问题、具体设计和实现代码;最后,给出测试验证和本章总结。 + +## 五、地图:框架全貌 + +读者在学习过程中,可以参考书中 Spring 框架地图,通过全局的视角,可以更好地理解和学习 Spring 框架的设计与开发。 + +
+ +
+ +## 六、活动:回馈粉丝 + +>牛吹完了,接下来回馈粉丝签名版《手写Spring》5本,感谢一直以来对小傅哥的支持。 + +### 1. 得奖规则 + +- 对文章进行`留言并转发朋友圈`,找伙伴给你的留言点赞 +- 以个人留言`被读者点赞数量`为排名,取最高的前5名 +- 仅记录个人攒点最高的留言,多次留言取最多次 + +### 2. 活动说明 + +- **时间范围**:2022-11-07 07:55:00 - 2020-11-09 07:55:00,共计2天计票 +- **公布时间**:2022年11月09日,星期三 +- **公布方式**:小傅哥的朋友圈公布,*记得添加小傅哥微信:`fustack`* +- **领奖方式**:看到小傅哥朋友圈后,主动联系小傅哥。提供;收获人、收获地址、手机号等必要信息。*😄嘿...嘿,我会保密的你的信息!* + +## 七、感谢:专家评语 + +感谢;G哥、冰河、CSDN蒋涛总、赖帆、刘俊明国科大导师、孙浩京东科技移动研发部垂直业务负责人、王松、翟永超、Hollis。帮写推荐语。感谢对小傅哥的喜欢和支持。 + +
+ +
+ +## 八、心似:平原走“码” + +若焰晨星,无量黎明。绽燃何须喧闹,灿烂不惧寂寥。我就愿意做一个心似平原走马的人,也更享受那份安静的执着。我也希望以我的经验分享给这条路上的同好。 + + +
+ +
+ +小傅哥,13年毕业,男,热衷于钻研有深度的技术本质。目前担任大厂互联网架构师职责,全网30万粉+编程知识博主,持续分享有干货有质量的技术。 + +一个着迷于技术又喜欢不断折腾的技术活跃者,从13年毕业到进入互联网,开发过交易、营销类项目,实现过运营、活动类项目,设计过中间件,组织过系统重构,编写过技术专利。不仅从事业务系统的开发工作,也经常做一些字节码插桩类的设计和实现,对架构的设计和落地有丰富的经验。在热衷于Java语言的同时,也喜欢研究中继器、I/O板卡、C#和PHP! + +沉淀、分享、成长,让自己和他人都能有所收获! diff --git a/docs/md/product/idea-plugin/vo2dto-v2.5.1.md b/docs/md/product/idea-plugin/vo2dto-v2.5.1.md new file mode 100644 index 000000000..17e00cf9b --- /dev/null +++ b/docs/md/product/idea-plugin/vo2dto-v2.5.1.md @@ -0,0 +1,138 @@ +--- +title: IDEA Plugin vo2dto v2.5.1 +lock: no +--- + +# IDEA Plugin vo2dto —— 这款插件,为开发提效80%,已经有8.1k安装量! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +哈喽,大家好我是技术UP主小傅哥。 + +如果你担心维护成本和性能考量,不想使用 `BeanUtils.copyProperties` 复制对象,也不想大面积的增加配置文件使用 `MapStruct`。而是只想要要手写效果的 `x.set(y.get)` 模型。那么请立刻在 IDEA 中安装插件 **vo2dto v2.5.1** 最新版本。截止到目前小傅哥开发的这款插件已经有 **8.1k 安装量**! + +
+ +IDEA Plugin vo2dto 是一款用于帮助使用 IntelliJ IDEA 编写代码的研发人员,快速生成两个对象转换过程中所需要大量的 `x.set(y.get)` 代码块的插件工具。在最新 v2.5.1 版本中已支持 Lombok.Builder 模式,让使用可丝滑得嘞! + +>文末提供了此插件的源码地址,你可以针对使用优化提交PR,以后那么多人的使用,都会看见你的贡献💐。 + +## ✨ 特性 + +1. 2个对象的转换操作,通过复制 X x 对象,转换给 Y y 对象 +2. 允许使用 lombok 对象转换、lombok 和普通对象转换,对于 serialVersionUID 属性过滤 +3. 支持类继承类,全量的对象转换操作 +4. 含记忆功能的弹窗选择映射关系,支持全量对象、支持匹配对象、也支持空转换,生成一组set但无get的对象 +5. 支持对于引入不同包下的同名类处理 +6. 支持 Lombok.Builder 模式创建转换对象 + +## 🛠️ 安装 + +### 1. 在线安装 + +| IDEA Plugin 搜索vo2dto直接在线安装即可| +|:---:| +|
| + +### 2. 手动安装 + +- 下载:[https://github.com/fuzhengwei/vo2dto/releases/tag/v2.5.1](https://github.com/fuzhengwei/vo2dto/releases/tag/v2.5.1) +- 安装: + +| IDEA Plugin 手动安装,导入下载包 | +|:------------------------:| +|
| + +## 🔨 使用 + +- 视频:[https://www.bilibili.com/video/BV13Y411h7fv](https://www.bilibili.com/video/BV13Y411h7fv) - `视频内有完整的使用介绍和插件设计` +- 描述:你需要复制被转换 X x = new X() 中的 X x 部分,无论它是方法入参还是实例化或者是接口回值,接下来鼠标定位到转换对象 Y y 上,可以定位到`类 大Y`、或者`属性 小y`,这样我就可以知道你要做到是X的对象的属性值,转换到Y对象的属性值上。接下来帮你快速生成全部的 `y.set(x.get)` 代码片段。 + +| IDEA Plugin vo2dto 使用演示图| +|:---:| +|
| + +```java +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +public class ApiTest { + + /** + * 普通模式 + */ + public void test_vo2dto01(UserVO user) { + UserDTO userDTO = new UserDTO(); + userDTO.setUserId(user.getUserId()); + userDTO.setUserNickName(user.getUserNickName()); + userDTO.setUserHead(user.getUserHead()); + } + /** + * lombok Builder 模式 + */ + public void test_vo2dto02(UserVO user) { + UserDTO userDTO = UserDTO.builder() + .userId(user.getUserId()) + .userNickName(user.getUserNickName()) + .userHead(user.getUserHead()) + .build(); + } + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + static class UserDTO { + + private String userId; + private String userIdx; + private String userNickName; + private String userHead; + private int page; + private int rows; + + } + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + static class UserVO { + + private Long id; + private String userId; + private String userNickName; + private String userHead; + private String userPassword; + private Date createTime; + private Date updateTime; + + } + +} +``` + +- 注意;v2.5.1 版本已支持了 Lombok Builder 模式,方便使用 Lombok 的伙伴创建对象。如图使用方式即可完成创建过程。 + +## 🤝 共建 + +**源码**:https://github.com/fuzhengwei/vo2dto - 你可以Fork工程,了解这套组件源码,对使用过程中所需的优化和扩展,提交你的代码。那么下次发版就会带上你的贡献了💐 + +
+ +## 💐 成长 + +这样的一个8000多安装量的开源组件项目,仅仅是小傅哥这篇知识社群「星球:码农会锁」中的一个小小内容。此外还包括了;大营销平台、Api网关、Lottery 抽奖、IM通信、SpringBoot Starter、IDEA Plugin 等内容,也还带着伙伴一起做开源的SDK发布到Maven仓库。 + +如果你想🔜快速的提升技术,是非常有必要跟着小傅哥一起学习。以我在大厂的业务经历、技术经验、落地能力,可以为你提高更高的见识。👣 踩在我的肩膀,你能看的更远! + +>赶紧加入星球,能做到这样的技术项目实战社群,真的不多!你只是投入一顿大麻辣烫💰,就🉐获得超级大的回报! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) \ No newline at end of file diff --git a/docs/md/product/idea-plugin/vo2dto-v2.5.5.md b/docs/md/product/idea-plugin/vo2dto-v2.5.5.md new file mode 100644 index 000000000..d897ddfd9 --- /dev/null +++ b/docs/md/product/idea-plugin/vo2dto-v2.5.5.md @@ -0,0 +1,163 @@ +--- +title: IDEA Plugin vo2dto v2.5.5 +lock: no +--- + +# IDEA Plugin vo2dto —— 这款插件,已经有20k安装量,月增量1000+! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +公司明确要求禁止在项目中使用 `BeanUtils.copyProperties` 复制对象,不仅是因为性能问题,更多是这种方式根本不知道有哪些对象属性被转换了。而手动编码 x.set(y.get) 是最稳定可靠处理方式。 + +但如果有几十个属性怎么办😰!那么你可以立即安装这款免费的 vo2dto v2.5.5 版本插件。截止到目前小傅哥开发的这款插件已经有 20.1k 安装量! + +
+ +IDEA Plugin vo2dto 是一款用于帮助使用 IntelliJ IDEA 编写代码的研发人员,快速生成两个对象转换过程中所需要大量的 `x.set(y.get)` 代码块的插件工具。在最新 v2.5.1 版本中已支持 Lombok.Builder 模式,让使用可丝滑得嘞! + +>文末提供了此插件的源码地址,你可以针对使用优化提交PR,以后那么多人的使用,都会看见你的贡献💐。 + +## ✨ 特性 + +1. 2个对象的转换操作,通过复制 X x 对象,转换给 Y y 对象 +2. 允许使用 lombok 对象转换、lombok 和普通对象转换,对于 serialVersionUID 属性过滤 +3. 支持类继承类,全量的对象转换操作 +4. 含记忆功能的弹窗选择映射关系,支持全量对象、支持匹配对象、也支持空转换,生成一组set但无get的对象 +5. 支持对于引入不同包下的同名类处理 +6. 支持 Lombok.Builder 模式创建转换对象 +7. 支持类的内部类对象,进行转换 + +## 👨🏻‍💻 重构 + +**这次我要说说重构!** + +其实最开始这个项目并不大,简单的建了个抽象模板类定义执行步骤,方法都写在子类里。三下五除二就完成了对象转换功能。 + +但随着小伙伴们不断的提出一些使用诉求后,这里的逻辑变得的复杂了,并且由于本身 IDEA Plugin 的开发,很多时候都要一点点的处理那些对象属性的数据,兼容各种类文件所在的包信息,还有Lombok以及类的内部类。所以,每次维护起来都像是重新写一遍一样。**时间一长,都不认识它了!** + +所以,为了改变这种情况。在 v2.5.5 版本的开发中,做了首次的工程重构,把流水面条的代码,用规则树拆分,让不同的节点实现不同的功能。优雅的效果,如图; + +
+ +这是一套规则树的模型结构,在 vo2dto 插件开发中的实践使用。关于这块的设计模式可以在这里学习;[https://bugstack.cn/md/develop/design-pattern/2024-08-25-chain-tree.html](https://bugstack.cn/md/develop/design-pattern/2024-08-25-chain-tree.html) + +通过节点功能对逻辑边界的拆解,让每一块功能区都可以显而易见的找到和处理,这样即使是过去很长时间,在看这段代码也能很轻松的知道每一块在干什么。其实代码写的最好的目标就是看代码就像看文档,用类划分边界比只单纯的叠加方法要清晰的多。 + +
+ +- 重构前:[https://github.com/fuzhengwei/vo2dto/tree/2.5.4](https://github.com/fuzhengwei/vo2dto/tree/2.5.4) +- 重构后:[https://github.com/fuzhengwei/vo2dto/tree/2.5.5](https://github.com/fuzhengwei/vo2dto/tree/2.5.5) + +如果感兴趣这样一个处理,可以进入到重构前后的代码,看看设计模式如何处理的这部分逻辑,怎么拆分的上下文逻辑。可以说非常优雅! + +## 🛠️ 安装 + +### 1. 在线安装 + +| IDEA Plugin 搜索vo2dto直接在线安装即可| +|:---:| +|
| + +### 2. 手动安装 + +- 下载:[https://github.com/fuzhengwei/vo2dto/releases/tag/v2.5.5](https://github.com/fuzhengwei/vo2dto/releases/tag/v2.5.5) +- 安装: + +| IDEA Plugin 手动安装,导入下载包 | +|:------------------------:| +|
| + +## 🔨 使用 + +- 视频:[https://www.bilibili.com/video/BV13Y411h7fv](https://www.bilibili.com/video/BV13Y411h7fv) - `视频内有完整的使用介绍和插件设计` +- 描述:你需要复制被转换 X x = new X() 中的 X x 部分,无论它是方法入参还是实例化或者是接口回值,接下来鼠标定位到转换对象 Y y 上,可以定位到`类 大Y`、或者`属性 小y`,这样我就可以知道你要做到是X的对象的属性值,转换到Y对象的属性值上。接下来帮你快速生成全部的 `y.set(x.get)` 代码片段。 + +| IDEA Plugin vo2dto 使用演示图| +|:---:| +|
| + +```java +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +public class ApiTest { + + /** + * 普通模式 + */ + public void test_vo2dto01(UserVO user) { + UserDTO userDTO = new UserDTO(); + userDTO.setUserId(user.getUserId()); + userDTO.setUserNickName(user.getUserNickName()); + userDTO.setUserHead(user.getUserHead()); + } + /** + * lombok Builder 模式 + */ + public void test_vo2dto02(UserVO user) { + UserDTO userDTO = UserDTO.builder() + .userId(user.getUserId()) + .userNickName(user.getUserNickName()) + .userHead(user.getUserHead()) + .build(); + } + + /** + * 类的内部类转换 + */ + public void test(UserDTO userDTO) { + UserVO.UserVO2 userVO2 + + } + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + static class UserDTO { + + private String userId; + private String userIdx; + private String userNickName; + private String userHead; + private int page; + private int rows; + + } + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + static class UserVO { + + private Long id; + private String userId; + private String userNickName; + private String userHead; + private String userPassword; + private Date createTime; + private Date updateTime; + + } + +} +``` + +- 注意;v2.5.5 版本已支持了 `类的内部类` 模式,如图使用方式即可完成创建过程。 + +## 💐 成长 + +如果你的简历也能有一个这样的小组件,让那么多的程序员👨🏻‍💻进行使用,那么对你的面试简历来说也是非常亮眼的一笔。 + +包括;这样的组件,还有;OPenAI 代码自动评审、透视业务监控、动态线程池,以及大量的业务项目;大营销平台、小型支付商城、拼团交易等,你都可以跟着小傅哥一起学习。👣 踩在我的肩膀,你能看的更远,走的更快,上的更高! + diff --git a/docs/md/product/idea-plugin/vo2dto.md b/docs/md/product/idea-plugin/vo2dto.md new file mode 100644 index 000000000..df52e66b5 --- /dev/null +++ b/docs/md/product/idea-plugin/vo2dto.md @@ -0,0 +1,103 @@ +--- +title: IDEA Plugin vo2dto v2.4.8 +lock: no +--- + +# IDEA Plugin vo2dto —— 对象转换插件 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +## 一、承认你优秀很难 + +`很多码农,把路走窄了` + +捧一个,喷一个,很多码农都不会多一点思路看问题,总是喜欢用矛和盾在显示自己有点`本事`。Github 你写文章说你不如做开源代码有价值,你写了开源代码说你这没有意义,那你问他贡献了啥,他只贡献了嘴。*聊理论吹的叮当的,写代码搞的稀得囊的* + +![](https://bugstack.cn/images/article/assembly/assembly-211228-01.png) + +- 好在,你这一路上能遇到很多`同好`,他们能真诚的给你意见、提供思路、帮助解决,让你们一群有技术初心的人,不断的成长。 +- 这可能就是技术创作的土壤,如果大家都不施肥,反而还要过来用力的踩踩这块地,那最后大家都只能一起卷死在这,谁也不要创新。*加油,我希望你可以和我一起做点事情* + +## 二、写了个什么插件 + +最近一个月多都在折腾关于 IDEA Plugin 插件开发的案例编写技术总结,在日常编码开发和折腾插件技术过程中发现一个`痛点`。 + +日常编码的过程中有太多的 vo2dto 对象转换操作,尤其是在 DDD 架构下多了不少的防腐层,而这层之间的对象 po、vo、do、dto,总是需要被转换,而使用 BeanUtils 多了,以后增改字段名都不知道影响到哪。 + +当然也有不错的工具 MapStruct 既可以保证性能又有不错的效率,但它需要给每一个转换对象维护对应的转换类,对于接口层的转换还是非常适合的,但那些很小的方法块内,也是如此折腾就显得有些麻烦了。 + +**所以**,小傅哥结合 IDEA Plugin 插件开发的能力,通过鼠标定位到转换对象上,一键织入需要生成一堆的 `x.set(y.get)` 方法,并且在几次优化中以及可以支持父类对象、lombok插件。演示图如下: + +![](https://bugstack.cn/images/article/assembly/assembly-211228-02.png) + +- 支持:复制一个对象,光标定位另外一个对象上,鼠标右键 Generate -> Vo2Dto 一键生成转换代码 +- 支持:不复制对象,直接在转换可以生成空的 set 对象,方便自由添加内容 +- 支持:插件中通过注解检测的方式,允许使用 lombok + +## 三、发布插件的经历 + +`🤔原来不用英文描述,不给我过` + +### 1. 请用英语描述 + +![](https://bugstack.cn/images/article/assembly/assembly-211228-03.png) + +- 这是我第一次发布插件接收到的邮件提醒,告诉我在你的 plugin.xml 中,要用英语描述。*一直没看,以为垃圾邮件* + +### 2. 请用英文截图 + +![](https://bugstack.cn/images/article/assembly/assembly-211228-04.png) + +- 告诉我,你的截图要用英语的,这样我才能给你通过。 + +### 3. 说我截图没用 + +![](https://bugstack.cn/images/article/assembly/assembly-211228-05.png) + +- 问我你确定需要这个截图吗,他觉得没啥意义 + +### 4. 终于发布出去 + +![](https://bugstack.cn/images/article/assembly/assembly-211228-06.png) + +- 改了注释、删了截图,终于迎来曙光。*你说,不发布一次,你会知道遇到这些吗!* + +## 四、把这插件安排上 + +### 1. 安装插件 + +`讲到这我就兴奋了!` + +为啥兴奋,因为这一个插件发布,我整整等了2周,因为一次修改就要2个工作日才能审核,所以上面我犯的错,都是用时间磨出来。 + +不过现在好了,你可以直接在 IDEA 中搜索安装小傅哥写的插件了,哈哈哈,这种没做过的事搞一次,总是让人很兴奋! + +![](https://bugstack.cn/images/article/assembly/assembly-211228-07.png) + +- 看到能搜索到 vo2dto 并顺利安装使用,我的心舒服了。*没有人能阻挡你最技术的热爱,即使你来我这踩两脚* + +### 2. 使用介绍 + +![](https://bugstack.cn/images/article/assembly/assembly-211228-08.png) + +如图所示,你只需要很简单的步骤,既可以快速且准确的帮你生成对应的转换代码,在一些场景里使用还是非常 `Good` 的!操作步骤: + +- 复制对象 `UserDto userDto` 这个是被转换对象,复制后才能便于生成获取属性的代码。*如果你不复制,那么就是生成空代码* +- 定位对象 `User usxxer` 把光标定位到对象或属性上,点击 Generate -> Vo2Dto 这样就可以把你的对象生成出来了。 +- 注意:支持 lombok、支持继承对象,如果你在使用过程中遇到其他需求或者问题,都可以反馈给我 + +### 3. 源码共享 + +![](https://bugstack.cn/images/article/assembly/assembly-211228-09.png) + +- 源码:[https://github.com/fuzhengwei/vo2dto](https://github.com/fuzhengwei/vo2dto) +- 说明:有同好的技术人共建才会让一件小事不断的优秀起来,所以我把这块代码共享出来,我们可以一起做一个非常短小精致的产品,来帮助我们自己完成一些便捷的开发处理。 + +## 五、我确定挺倔强 + +有一种倔强可能也是天生的,我喜欢技术、喜欢折腾、喜欢简单干净的事情,并把我认准的事长久坚持下去。有时候我也知道另外一条路会更轻松、更有钱拿,但那并不是我的内心,只有我认识到的我才是我,否则强加给我的我,始终不会是我。 \ No newline at end of file diff --git "a/docs/md/product/pdf/2020-05-17-\345\260\217\345\202\205\345\223\245\345\207\272\344\271\246\344\272\206\343\200\212\345\255\227\350\212\202\347\240\201\347\274\226\347\250\213\343\200\213\345\205\215\350\264\271\346\213\277\357\274\201.md" "b/docs/md/product/pdf/2020-05-17-\345\260\217\345\202\205\345\223\245\345\207\272\344\271\246\344\272\206\343\200\212\345\255\227\350\212\202\347\240\201\347\274\226\347\250\213\343\200\213\345\205\215\350\264\271\346\213\277\357\274\201.md" new file mode 100644 index 000000000..8d8762d17 --- /dev/null +++ "b/docs/md/product/pdf/2020-05-17-\345\260\217\345\202\205\345\223\245\345\207\272\344\271\246\344\272\206\343\200\212\345\255\227\350\212\202\347\240\201\347\274\226\347\250\213\343\200\213\345\205\215\350\264\271\346\213\277\357\274\201.md" @@ -0,0 +1,89 @@ +--- +layout: post +category: itstack-demo-any +title: 小傅哥出书了《字节码编程》免费拿!既然市面缺少ASM、Javassist、Byte-buddy成体系的学习资料,那我来! +tagline: by 小傅哥 +tag: [java,itstack-demo-any] +excerpt: 让人怪不好意思的,说是出书有点膨胀,毕竟这不是走出版社的流程,选题、组稿、编著、审读、加工到出版发行。但全书共计107页,11万7千字,20个章节涵盖三个字节码框架和JavaAgent使用并附带整套案例源码! +lock: need +--- + +# 小傅哥出书了《字节码编程》免费拿!既然市面缺少ASM、Javassist、Byte-buddy成体系的学习资料,那我来! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
PDF:[https://download.csdn.net/download/Yao__Shun__Yu/12505051](https://download.csdn.net/download/Yao__Shun__Yu/12505051) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +**让人怪不好意思的**,说是出书有点膨胀💥,毕竟这不是走出版社的流程,选题、组稿、编著、审读、加工到出版发行。 + +**但全书共计107页,11万7千字,20个章节涵盖三个字节码框架和JavaAgent使用并附带整套案例源码!** + +![](https://bugstack.cn/assets/images/illustration/让人怪不好意思的.png) + +**讲道理**,市面上以及网络搜索中都基本很少有成体系的关于字节码编程的知识,这主要由于大部分开发人员其实很少接触这部分内容,包括;`ASM`、`Javassist`、`Byte-buddy`以及`JavaAgent`,没有很大的市场也就没有很多的资料。但大家其实已经从其他的框架或者中间件中使用到,就像你用到的;Cglib、混沌工程、非入侵的全链路监控以及你是否使用过`jetbrains-agent.jar`做了某项实验? + +![](https://bugstack.cn/assets/images/illustration/上号Idea.png) + +所以这样的技术栈一直都萦绕在你身边,只是你还没有发现!当有一天面试问到了,那时你已经170斤工作五年。 + +**蹭个车告诉你这个知识的重要性**,阿里云的挑战赛! +![](https://bugstack.cn/assets/images/2020/itstack-demo-bytecode-0-3.png) + +`读不在三更五鼓,功只怕一曝十寒!`,不一定一本书中就能读出个黄金屋,但脚下路的用什么垫都是自己日积月累。 + +## 就这本书他出炉了 + +![](https://bugstack.cn/assets/images/2020/itstack-demo-bytecode-0-2.png) + +## 介绍 + +初识字节码编程是从使用非入侵的全链路监控开始,在这之前我所了解的如果需要监控系统的运行状况,通常需要硬编码埋点或者AOP的方式采集方法执行信息;耗时、异常、出入参等来监控一个系统的运行健康度。而这样的监控方式在大量的系统中去改造非常耗时且不好维护,更不要说去监控一个业务流程的调用链路。 + +在2010年的时候,谷歌发布一篇名为《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》的论文,在文中介绍了谷歌生产环境中大规模分布式系统下的跟踪系统`Dapper`的设计和使用经验。 + +这样的监控系统采用 `Javaagent` 与字节码操作框架结合使用,在应用系统加载时对需要监控的方法进行字节码增强也叫插桩。对方法处理后的结果就和你之前硬编码类似,但这样就可以减轻认为操作,同时可以对多个系统之间定义调用链路ID进行串联业务流程关系。最终,极大减轻了监控成本也提高了线上问题的快速定位和处理。 + +这里面监控系统核心知识也主要是 `Javaagent`和字节码操作,在字节码操作中目前有三个比较常用的框架;`ASM`、`Javassist`、`Byte Buddy`,这几个框架都能进行字节码操作,其中`ASM` 更偏向于底层,需要了解字节码指令以及操作数栈等知识,最好学习过《Java虚拟机规范》等书籍,另外两个框架是对 `ASM` 的封装,提供更加高级的API去操作字节码。 + +在本书中`小傅哥`会分别讲解这三种字节码框架的使用,以及最终与`Javagent`结合完成全链路监控的案例。通过这样的学习让你可以从有抓手的从案例开始,把枯燥的字节码编程融入场景,深化理解和实操应用。也能让你忙于CRUD开发的同时提升自己的知识栈,拓展技术视野。也许不久以后这项技术也能为你带来一些有价值的收获! + +## 作者 + +作者小傅哥多年从事一线互联网 Java 开发,热衷于对学习历程做技术汇总,侧重点更倾向编写 Java 核心内容。旨在为大家提供一个清晰详细的学习教程也帮助自己不断沉淀。所以投入时间学习、整理、编写相关的资料,如果我的文章或书籍能为您提供帮助,请给予**支持**(关注、点赞、分享)! + +**如何支持:** + +- 关注公众号: [`bugstack虫洞栈`](https://bugstack.cn/assets/images/qrcode.png) +- 收藏我博客:[`bugstack.cn`](https://bugstack.cn/) +- 分享给您身边的小伙伴 +- 还可以给我开源的项目点个星星🦍 「`CodeGuide | 程序员编码指南`」- [`https://github.com/fuzhengwei/CodeGuide/wiki`](https://github.com/fuzhengwei/CodeGuide/wiki) + +*如果这些都做了!记得加我`微信(fustack)`*,交个朋友! + +## 下载 + +**内容包括**; + +1. 小傅哥的`《字节码编程》` +2. 一整套书中对应的源码 + ![字节码编程附带源码](https://bugstack.cn/assets/images/2020/itstack-demo-bytecode-0-1.png) + +**下载方式;** + +1. 打开外链分享链接下载书籍: [https://download.csdn.net/download/Yao__Shun__Yu/12505051](https://download.csdn.net/download/Yao__Shun__Yu/12505051) +2. 书中的源码部分在公众号:`bugstack虫洞栈`,回复`源码下载`即可获取 +3. 如果链接失效关注公众号:`bugstack虫洞栈`,回复PDF,获取新的链接下载 +4. 添加作者微信(fustack)获取书籍和源码,也方便做相关技术交流 +5. 公众号用户,直接点击下方阅读原文下载 + +**由于网络兼容直接在线阅读可能有字体错位问题,请下载阅读,体验更好!** + +## 收个尾 + +头一次把系列文章写成书,虽然免费发布,但也可能在获取书籍下载以及学习过程中发现我写错字以及写错某个名称`想喷我`🤮,如果你有此冲动!请添加小傅哥微信(`公众号:bugstack虫洞栈获取`),我会用我的技术魅力和爆炸人品感化你,并修改我的书籍内容,📝记录你的功绩到:[https://github.com/fuzhengwei/CodeGuide/wiki](https://github.com/fuzhengwei/CodeGuide/wiki) + +如果你在阅读本书的过程中有些地方不是很容易理解,不要担心一定作者没有描述的更加清楚。很多知识或者系统建设并不难,只是有时候被描述的麻烦了。所以我也非常愿意与你一起去学习这部分知识,在讨论中不断把问题梳理的更加清晰,用更易懂的方式剖析问题的本质。 + +**最后,希望同好编程开发的你不只是CRUD的工具人,多多拓展技术栈夯实基本功。共勉!加油!** \ No newline at end of file diff --git "a/docs/md/product/pdf/2020-07-12-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217.md" "b/docs/md/product/pdf/2020-07-12-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217.md" new file mode 100644 index 000000000..517fc9c23 --- /dev/null +++ "b/docs/md/product/pdf/2020-07-12-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217.md" @@ -0,0 +1,142 @@ +--- +layout: post +category: itstack-demo-design +title: 《重学 Java 设计模式》PDF 出炉了 - 小傅哥,肝了50天写出18万字271页的实战编程资料 +tagline: by 小傅哥 +tag: [itstack-demo-design] +excerpt: Hello, world of design !你好,设计模式的世界!欢迎来到这里,很高兴你能拿到这本书,如果你能坚持看完并按照书中的例子进行实践,那么在编程开发的世界里,就又多了一个可以写出良好代码的人,同时也为架构师培养储备了一个人才。 +lock: need +--- + +# 《重学 Java 设计模式》PDF 出炉了 - 小傅哥,肝了50天写出18万字271页的实战编程资料 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +**我膨胀了💥**,在编写完上一本PDF《字节码编程》被下载了2000份以后,蠢蠢欲动开始计划第二本。于是从🌹5月20日那天投身实战型设计模式打磨,通过模拟互联网业务开发实际需求作为学习场景,讲解设计模式。 + +**全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、从5月20日开始耗时50天打造完成。** + +![](https://bugstack.cn/assets/images/illustration/swell.png) + +💋`鉴于作者水平有限`,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。 + +## 二、简介 + +![](https://bugstack.cn/assets/images/2020/design/pdflogo.png) + +欢迎来到这里,很高兴你`将`拿到这本电子书,如果你能坚持看完并按照书中的例子进行实践,那么在编程开发的世界里,就又多了一个可以写出良好代码的人,同时也为架构师培养储备了一个人才。 + +可能在此之前你也多少了解过设计模式,但在实际的业务开发中使用却不多,多数时候都是大面积堆积`ifelse`组装业务流程,对于一次次的需求迭代和逻辑补充,只能东拼西凑`Ctrl+C`、`Ctrl+V`。 + +所以为了能让更多的程序员👨‍💻‍更好的接受设计思想和架构思维,并能运用到实际的业务场景。本书的作者`小傅哥`,投入50天时间,从互联网实际业务开发中抽离出,交易、营销、秒杀、中间件、源码等22个真实场景,来学习设计模式实践使用的应用可上手技能。 + +### 1. 谁发明了设计模式? + +设计模式的概念最早是由 `克里斯托佛·亚历山大` 在其著作 `《建筑模式语言》` 中首次提出的。 本书介绍了城市设计的 “语言”,提供了253个描述城镇、邻里、住宅、花园、房间及西部构造的模式, 而此类 “语言” 的基本单元就是模式。后来,`埃里希·伽玛`、 `约翰·弗利赛德斯`、 `拉尔夫·约翰逊` 和 `理查德·赫尔姆` 这四位作者接受了模式的概念。 1994 年, 他们出版了 `《设计模式: 可复用面向对象软件的基础》` 一书, 将设计模式的概念应用到程序开发领域中。 + +其实有一部分人并没有仔细阅读过设计模式的相关书籍和资料,但依旧可以编写出优秀的代码。这主要是由于在经过众多项目的锤炼和对程序设计的不断追求,从而在多年编程历程上提炼出来的心得体会。而这份经验最终会与设计模式提到的内容几乎一致,同样会要求高内聚、低耦合、可扩展、可复用。你可能也遇到类似的经历,在学习一些框架的源码时,发现它里的某些设计和你在做开发时一样。 + +### 2. 我怎么学不会设计模式? + +钱也花了,书也买了。代码还是一坨一坨的!设计模式是由多年的经验提炼出来开发指导思想。就像我告诉你自行车怎么骑、汽车怎么开,但只要你没跑过几千公里,你能记住的只是理论,想上道依旧很慌! + +**所以**,本设计模式专题系列开始,会带着你使用设计模式的思想去优化代码。从而学习设计模式的心得并融入给自己。当然这里还需要多加练习,一定是*人车合一*,才能站在设计模式的基础上构建出更加合理的代码。 + +### 3. 适合人群 + +1. 具备一定编程基础在工作1-3年的研发人员 +2. 希望通过此书提升编码思维,剔除到代码中的坏味道 +3. 有意愿成为架构师,但还处在一定瓶颈期 +4. 学习过设计模式,可是一直想找到一本可以落地真实场景参照的书籍 + +### 4. 我能学到什么 + +1. 优化平时开发中的ifelse语句,让代码更加整洁 +2. 看设计模式不再是用理论生搬硬套,这次可以有点用 +3. 站在更高的角度去看待编程开发,学会更多的面向对象的思维,尤其是;接口、抽象类、多态等使用 +4. 升职、加薪,良好的代码是效能提升的基础,成为本组编码最靓的精神小伙 + +### 5. 阅读建议 + +本书属于实战型而不是理论介绍类书籍,每一章节都有对应的完整代码,学习的过程需要参考书中的章节与代码一起学习,同时在学习的过程中需要了解并运行代码。学习完成后进行知识点的总结,以及思考🤔这样的设计模式在自己的业务场景中需要如何使用。 + +## 三、书中目录 + +设计模式遵循六大原则;单一职责(`一个类和方法只做一件事`)、里氏替换(`多态,子类可扩展父类`)、依赖倒置(`细节依赖抽象,下层依赖上层`)、接口隔离(`建立单一接口`)、迪米特原则(`最少知道,降低耦合`)、开闭原则(`抽象架构,扩展实现`),会在具体的设计模式章节中,进行体现。 + +### 1. 创建型模式 + +**这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。** + +| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 | +| ---- | ------------ | --------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------ | +| 1 | **工厂方法** | ![](https://bugstack.cn/assets/images/2020/design/11.png) | 多种类型商品不同接口,统一发奖服务搭建场景 | 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 | +| 2 | **抽象工厂** | ![](https://bugstack.cn/assets/images/2020/design/12.png) | 替换Redis双集群升级,代理类抽象场景 | 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 | +| 3 | **生成器** | ![](https://bugstack.cn/assets/images/2020/design/13.png) | 各项装修物料组合套餐选配场景 | 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 | +| 4 | **原型** | ![](https://bugstack.cn/assets/images/2020/design/14.png) | 上机考试多套试,每人题目和答案乱序排列场景 | 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 | +| 5 | **单例** | ![](https://bugstack.cn/assets/images/2020/design/15.png) | 7种单例模式案例,Effective Java 作者推荐枚举单例模式 | 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 | + +### 2. 结构型模式 + +**这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。** + +| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 | +| ---- | ---------- | --------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1 | **适配器** | ![](https://bugstack.cn/assets/images/2020/design/21.png) | 从多个MQ消息体中,抽取指定字段值场景 | 将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 | +| 2 | **桥接** | ![](https://bugstack.cn/assets/images/2020/design/22.png) | 多支付渠道(微信、支付宝)与多支付模式(刷脸、指纹)场景 | 将抽象部分与实现部分分离,使它们都可以独立的变化。 | +| 3 | **组合** | ![](https://bugstack.cn/assets/images/2020/design/23.png) | 营销差异化人群发券,决策树引擎搭建场景 | 将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 | +| 4 | **装饰** | ![](https://bugstack.cn/assets/images/2020/design/24.png) | SSO单点登录功能扩展,增加拦截用户访问方法范围场景 | 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。 | +| 5 | **外观** | ![](https://bugstack.cn/assets/images/2020/design/25.png) | 基于SpringBoot开发门面模式中间件,统一控制接口白名单场景 | 为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 | +| 6 | **享元** | ![](https://bugstack.cn/assets/images/2020/design/26.png) | 基于Redis秒杀,提供活动与库存信息查询场景 | 运用共享技术有效地支持大量细粒度的对象。 | +| 7 | **代理** | ![](https://bugstack.cn/assets/images/2020/design/27.png) | 模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景 | 为其他对象提供一种代理以控制对这个对象的访问。 | + +### 3. 行为模式 + +**这类模式负责对象间的高效沟通和职责委派。** + +| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 | +| ---- | ------------ | ---------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1 | **责任链** | ![](https://bugstack.cn/assets/images/2020/design/31.png) | 模拟618电商大促期间,项目上线流程多级负责人审批场景 | 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。 | +| 2 | **命令** | ![](https://bugstack.cn/assets/images/2020/design/32.png) | 模拟高档餐厅八大菜系,小二点单厨师烹饪场景 | 将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。 | +| 3 | **迭代器** | ![](https://bugstack.cn/assets/images/2020/design/33.png) | 模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景 | 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。 | +| 4 | **中介者** | ![](https://bugstack.cn/assets/images/2020/design/34.png) | 按照Mybatis原理手写ORM框架,给JDBC方式操作数据库增加中介者场景 | 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 | +| 5 | **备忘录** | ![](https://bugstack.cn/assets/images/2020/design/35.png) | 模拟互联网系统上线过程中,配置文件回滚场景 | 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 | +| 6 | **观察者** | ![](https://bugstack.cn/assets/images/2020/design/36.png) | 模拟类似小客车指标摇号过程,监听消息通知用户中签场景 | 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 | +| 7 | **状态** | ![](https://bugstack.cn/assets/images/2020/design/37.png) | 模拟系统营销活动,状态流程审核发布上线场景 | 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。 | +| 8 | **策略** | ![](https://bugstack.cn/assets/images/2020/design/38.png) | 模拟多种营销类型优惠券,折扣金额计算策略场景 | 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。 | +| 9 | **模板方法** | ![](https://bugstack.cn/assets/images/2020/design/39.png) | 模拟爬虫各类电商商品,生成营销推广海报场景 | 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 | +| 10 | **访问者** | ![](https://bugstack.cn/assets/images/2020/design/310.png) | 模拟家长与校长,对学生和老师的不同视角信息的访问场景 | 主要将数据结构与数据操作分离。 | + +*以上图稿和部分描述参考;[https://refactoringguru.cn](https://refactoringguru.cn)、[https://www.runoob.com/design-pattern/visitor-pattern.html](https://www.runoob.com/design-pattern/visitor-pattern.html)* + +## 四、PDF📚下载 + +下载前,一点对原创作者的支持请求😬,`点赞`、`在看`、`分享`、`留言`、`赞赏`,完成任何一样都可以获得🉐这本PDF书籍。 + +### 1. 可获得内容包括 + +1. `《重学 Java 设计模式》`PDF 书籍一本 +2. 59个对应的工程案例源码一套 +3. 在线阅读版学习了资料 + +### 2. 获取方式 + +1. 公众号:[`bugstack虫洞栈`](https://bugstack.cn/assets/images/qrcode.png) 内回复:`重学Java设计模式` 可以获取最新下载链接 +2. 添加小傅哥微信(fustack),获取下载链接以及添加读者群 + +![](https://bugstack.cn/assets/images/2020/design/mkt.png) + +## 五、收个尾🎉 + +👣走过的路会留下足迹,👨‍💻‍码过的文会盛满四季。 + +有时候真的很感谢自己还能坚持做原创技术输出,即使再忙再累也给自己一个当下的交代,在写文章的过程中甚至几乎没有过周末,也没有过半夜。但当自己完成每一篇文章后,那份给自己的努力也传播给其他人技术知识。**也希望读者们能给多多点点在看分享和留言,这几乎是支撑我写作的最大动力回馈** + +本书是设计模式实战型书籍📚,编写的过程中常常为找到一个合适并易于理解的场景而抓头发,甚至睡觉中梦到的合适的内容,也要用语音发给自己记录下来。好在50天的坚持终于把这22个设计模式场景写完。如果书中有一些不易于理解的内容,不要担心一定是作者没有描述清楚或找到的案例不适合。可以添加作者小傅哥(fustack)微信,交流相应的技术内容,共同进步。 + +**最后,我想说**:能力,是你前行的最大保障。哪怕你是兢兢业业的工作者,也是拥有`能留下的本事`和`跳出去的能力`,才会相对安稳度过动荡。 \ No newline at end of file diff --git "a/docs/md/product/pdf/2020-10-04-\343\200\212Java\351\235\242\347\273\217\346\211\213\345\206\214\343\200\213PDF\346\225\260\346\215\256\347\273\223\346\236\204\347\257\207\357\274\214 \350\202\235\345\256\214\345\207\272\347\202\211\344\272\206\357\274\201\346\235\245\345\220\247\357\274\214\350\277\231\346\234\254\344\271\246\345\270\256\344\275\240\346\213\277\346\234\200\350\264\265\347\232\204offer\357\274\201.md" "b/docs/md/product/pdf/2020-10-04-\343\200\212Java\351\235\242\347\273\217\346\211\213\345\206\214\343\200\213PDF\346\225\260\346\215\256\347\273\223\346\236\204\347\257\207\357\274\214 \350\202\235\345\256\214\345\207\272\347\202\211\344\272\206\357\274\201\346\235\245\345\220\247\357\274\214\350\277\231\346\234\254\344\271\246\345\270\256\344\275\240\346\213\277\346\234\200\350\264\265\347\232\204offer\357\274\201.md" new file mode 100644 index 000000000..8ecb5dffb --- /dev/null +++ "b/docs/md/product/pdf/2020-10-04-\343\200\212Java\351\235\242\347\273\217\346\211\213\345\206\214\343\200\213PDF\346\225\260\346\215\256\347\273\223\346\236\204\347\257\207\357\274\214 \350\202\235\345\256\214\345\207\272\347\202\211\344\272\206\357\274\201\346\235\245\345\220\247\357\274\214\350\277\231\346\234\254\344\271\246\345\270\256\344\275\240\346\213\277\346\234\200\350\264\265\347\232\204offer\357\274\201.md" @@ -0,0 +1,114 @@ +--- +layout: post +category: interview +title: 《Java面经手册》PDF数据结构篇, 肝完出炉了!来吧,这本书帮你拿最贵的offer! +tagline: by 小傅哥 +tag: [java,interview] +excerpt: 上最快的车🚗,爬最高的坡🛤。唠最狠的嗑🤔,拿最贵的offer!这本面经手册不只是面试,更是对Java的深入学习,扰动函数、负载因子、斐波那契、洗牌算法等等,助你突破阶段性瓶颈期。 +lock: need +--- + +# 《Java面经手册》PDF数据结构篇, 肝完出炉了!来吧,这本书帮你拿最贵的offer! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +### 1. 先祝贺下自己拿下3.8万本下载量! + +![](https://bugstack.cn/assets/images/2020/interview/interview-all-0-01.png) + +[《重学Java设计模式》](https://bugstack.cn/itstack-demo-design/2020/07/12/%E9%87%8D%E5%AD%A6-Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.html)PDF,我的第二本电子书,截止目前为止全网下载量3.8万本。这是一本以实战真实场景编码的方式学习设计模式,全书涉及交易、营销、下单、秒杀、规则引擎以及中间件等22个真实场景,共计18万字271页,耗时50天肝完!*文末提供下载指引* + +当然这本书的推广少不了粉丝的支持,也感谢我的号主朋友帮忙转载分享。也有使用此书籍为自己拉粉的号主,同样也感谢你们为这本书做的宣传,让更多人可以学习到**有用的设计模式**。 + +### 2. 有个好身体才能继续前行! + +**10月4日**,终于从医院🏥驱车250公里从张家口回来了!在医院陪床真心不容易,还好不是麻烦的病,只是做一个腰间盘里的微创。`哈哈哈,什么都还好,只是好几天没洗澡了,睡觉也没脱衣服。` 在医院基本除了简单的看护外,基本都是闲呆。但闲不住的我终于还是把电脑打开了,就像下面这样! + +![小傅哥 & 医院陪床敲代码](https://bugstack.cn/assets/images/2020/interview/interview-all-0-02.png) + +### 3. 为Java面经又肝出一本PDF! + +`做到让懂了就是真的懂!`这绝对不是一本简单的*面试手册*,也不是单纯的*面试题*。而是借着面试的场景深入讲解Java核心知识,就像学习过可以深入理解:HashMap的扰动函数让散列更均匀、ThreadLocal使用斐波那契黄金分割点散列、双端队列的用途、红黑树与2-3树的关系、洗牌算法等等核心知识。 + +## 二、简介 + +![](https://bugstack.cn/assets/images/2020/interview/interview-all-0-03.png) + +**Hello,world of java!** 你好,java编码的世界! + +欢迎来到这里,很高兴你能拿到这本书。如果你能坚持看完并按照书中的实践例子进行撸码学习,那么一定会有非常深刻的收获。 + +这是一本借着面试的名义讲解 java 核心知识点的书籍,很多知识都是你平常在用的,但可能忽略了很多细节部分。就像,HashMap的扰动函数让散列更均匀、除了这种散列还有ThreadLocal可以使用斐波那契黄金分割点散列等等,一系列知识点都有在本书中通过实践的方式向你深度讲解。 + +**让懂了就是真的懂** + +让懂了就是真懂,是本书的核心宗旨。对每一个需要深入了解的知识点,都从最基本的原理进行剖析。再通过可以实践验证的例子,来学习这些核心知识点,让学习内容既可以简单,也可以更深入。 + +### 1. 面试真的只是造火箭吗? + +常说面试造火箭,入职拧螺丝。但你真的有造火箭的本事吗,大部分都是不敢承认自己的知识盲区和技术瓶颈以及经验不足的自嘲。 + +**面试时**: + +- 我希望你懂数据结构,因为这样的你在使用HashMap、ArrayList、LinkedList,更加得心应手。 +- 我希望你懂散列算法,因为这样的你在设计路由时,会有很多选择;`除法散列法`、`平方散列法`、`斐波那契(Fibonacci)散列法`等。 +- 我希望你懂开源代码,因为这样的你在遇到问题时,可以快速定位,还可能创造出一些系统服务的中间件,来更好的解耦系统。 +- 我希望你懂设计模式,因为这样的你可以写出可扩展、易维护的程序,让整个团队都能向更好的方向发展。 + +**所以**,从不是CRUD选择了你,也不是造螺丝让你成为工具人。而是你的技术能力决定你的眼界,眼界又决定了你写出的代码! + +### 2. 适合人群 + +1. 具备一定编程基础,工作2年以上的研发人员 +2. 希望突破自己阶段性的瓶颈期,可以有更深入的技术成长 +3. 有意愿成为架构师,但还找不到一个方向 +4. 面试求职,唠最狠的嗑,拿最贵的offer + +### 3. 我能学到什么 + +1. 学习Java中常用API的数据结构和算法 +2. 深入学习扰动函数、负载因子、红黑树、斐波那契等核心知识点 +3. 打破学习瓶颈,让实践方式的学习思路,给自己增加更多的知识 + +### 4. 阅读建议 + +小傅哥,系列专题文章都偏向于实践落地,每一章节的内容都有对应的案例代码作为验证。所以学习的过程最好也要亲手试验下,这样才可以更加深入的学到书中的精髓部分。事必躬亲,加油! + +## 三、PDF📚下载 + +下载前,一点对原创作者的支持请求😬,`点赞`、`在看`、`分享`、`留言`、`赞赏`,感谢支持! + +### 1. 可获得内容包括 + +1. `《Java 面经手册》`PDF 书籍一本 +2. 全书学习对应源码案例一套 +3. 在线阅读版学习了资料 + +### 2. 获取方式 + + +**我写了三本PDF书,你都可以关注公众号:`bugstack虫洞栈` 回复口令,进行下载!** + +![](https://bugstack.cn/assets/images/qrcode.png) + +1. **《Java 面经手册》**,公众号内回复:`面经手册`,获取下载链接。 +2. **《重学Java设计模式》**,公众号内回复:`设计模式`,获取下载链接。 +3. **《字节码编程》**,公众号内回复:`字节码编程`,获取下载链接。 +4. 也可以添加小傅哥微信(`fustack`)加入学习群,互相交流。 + +## 四、收个尾🎉 + +`让懂了就是真的懂!` + +学习编程最重要的不是背理论、背结果,也不是为了面试就疯狂的搜集资料背答案。这些都不能让你有很大的收获,可能还会适得其反。*万一你背错了,很容易被拆穿!* + +而对于我们编码这一行来说,如果不想被这个行业淘汰,几乎是需要每天都学习!尤其是感觉到自己到了一定的瓶颈期,又很难成长上去的时候。`什么时候是瓶颈期?` 如果你不能过阿里的P7、也不能过京东的T7等等,互联网行业中一些门槛较高的职位时,那么就是处于一个技术瓶颈期。 + +**时间管理?**,想想自己好像也没有什么时间管理,只不过一年没有玩过游戏了。大部分时间都会投入自己学习中,梳理资料、编写文章、技术交流上。尤其是资料的梳理编写博客上,让我自己有了很大的技术进步,同时又有了不错的文笔进步。这是我近一年最大的收获! + +**最后**,这不是面经手册终结,后续还再继续!`沉淀、分享、成长,让自己和他人都能有所收获!` \ No newline at end of file diff --git "a/docs/md/product/pdf/2021-01-26-Java\351\235\242\347\273\217\346\211\213\345\206\214PDF\344\270\213\350\275\275.md" "b/docs/md/product/pdf/2021-01-26-Java\351\235\242\347\273\217\346\211\213\345\206\214PDF\344\270\213\350\275\275.md" new file mode 100755 index 000000000..a064a1a57 --- /dev/null +++ "b/docs/md/product/pdf/2021-01-26-Java\351\235\242\347\273\217\346\211\213\345\206\214PDF\344\270\213\350\275\275.md" @@ -0,0 +1,156 @@ +--- +layout: post +category: interview +title: 《Java 面经手册》PDF,全书 417 页 11.5 万字,完稿&发版! +tagline: by 小傅哥 +tag: [java,interview] +excerpt: 此书并不是单纯的面试题,也不是内卷八股文。而是 150 张手绘图深度讲解 Java 核心技术,给予每一个学习此书的研发人员对待知识都能,让懂了就是真的懂! +lock: need +--- + +# 《Java 面经手册》PDF,全书 417 页 11.5 万字,完稿&发版! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
PDF:[https://download.csdn.net/download/Yao__Shun__Yu/14932325](https://download.csdn.net/download/Yao__Shun__Yu/14932325) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +**我膨胀了** 💥,在看了大部分以面试讲解的 Java 文章后,发现很多内容的讲解都偏向于`翻译`、`抄书`、`说理论`的给答案式讲解,最终把知识弄的云里雾里。 + +![](https://bugstack.cn/assets/images/illustration/swell.png) + +就像我问你: +- HashCode为什么用31作为乘数,你证明过吗? +- 扰动函数的函数作用是什么,它还有什么场景在用? +- 拉链寻址和开放寻址具体是什么表现,怎么解决的碰撞问题? +- ThreadLocal 的实现中还有黄金分割点的使用,你知道吗? +- CLH、MCS,都是怎么实现的公平锁,代码是什么样? +- jvmti 可以用于非入侵的监控线程池状态,你用过吗? + +关于以上的问题,是不有种即使看过 Java 核心 API 的源码,也很难回答出来? + +这是因为 Java 代码本身就是基于数据结构和算法对数学逻辑的具体实现,而那些隐含在代码中的数学知识如果你不会,那么压根你就会忽略掉它,也就因此看不懂源码了。 + +`知识的视觉盲区,就像夜间开车。车灯不够亮你只能看到30%的视野,开的越快越危险!` + +**所以**,此书并不是单纯的面试题,也不是内卷八股文。而是从一个单纯的和程序员有关的数学知识点开始,深入讲解 Java 的核心技术。并且每一章节都配有实践验证的源码,可以对照着一起撸才更有感觉! + +**全书共计5章29节,417页11.5万字,耗时4个月完成。涵盖数据结构、算法逻辑、并发编程、JVM以及简历和互联网大厂面试等内容。** + +💋`鉴于作者水平有限`,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。 + +## 二、简介 + +![](https://bugstack.cn/assets/images/2020/interview/interview-pdf-1.png) + +**Hello, world of java !** 你好,java的世界! + +欢迎来到这里,很高兴你能拿到这本书。如果你能坚持看完书中每章节的内容,那么不仅可以在你的面试求职上有所帮助,也更能让你对 Java 核心技术有更加深入的学习。 + +[《Java 面经手册》](#) 是一本以面试题为入口讲解 Java 核心技术的 PDF 书籍,书中内容也极力的向你证实`代码是对数学逻辑的具体实现`。*为什么这么说?* 当你仔细阅读书籍时,会发现这里有很多数学知识,包括:扰动函数、负载因子、拉链寻址、开放寻址、斐波那契(Fibonacci)散列法还有黄金分割点的使用等等。 + +编码只是在确定了研发设计后的具体实现,而设计的部分包括:数据结构、算法逻辑以及设计模式等,而这部分数据结构和算法逻辑在 Java 的核心 API 中体现的淋漓尽致。那么,也就解释了为什么这些内容成为了热点面试题,虽然可能我们都会觉得这样的面试像是造火箭。 + +那么,🚕汽车75马力就够奔跑了,那你怎么还想要2.0涡轮+9AT呢?大桥两边的护栏你每次走的时候都会去摸吗?那怎么没有护栏的大桥你不敢上呢? + +很多时候,你额外的能力才是自身价值的体现,不要以为你的能力就只是做个业务开发每天CRUD。其实有时候并不是产品让你写CRUD,而是因为你的能力只能产品功能设计成CRUD。 + +就像数据结构、算法逻辑、源码技能,它都是可以为你的业务开发赋能的,也是写出更好、更易扩展程序的根基,所以学好这份知识非常有必要。 + +**所以**,我非常建议你深度阅读此书,如果书中的知识点对你只是抛砖引玉,那么就更好了,你可以继续深入索取,吸纳更多的、更深的内容到自己的头脑中。 + +### 1. 适合人群 + +1. 具备一定编程基础,工作1-3年的研发人员 +2. 想阅读 Java 核心源码,但总感觉看不懂的 +3. 看了太多理论,但没有实践验证的 +4. 求职面试,总被面试题搞的死去活来的 + +### 2. 我能学到什么 + +1. 怎么写简历、怎么面大厂、怎么补充不足 +2. Java 核心API中用到的数据结构和算法逻辑 +3. 必会的数学知识,扰动函数、负载因子、拉链寻址、开放寻址、斐波那契(Fibonacci)散列法等 +4. 学到学习的能力,跟着作者的分析和学习方式,增强自己的学习能力 + +### 3. 阅读建议 + +本书虽然是源码分析、理论实践,但并不会让读者感觉枯燥。作者:小傅哥,在每一篇的知识里都写下了实践验证的结果,对于每一章节都有对应的源码实现。小伙伴在阅读的时候可以对照源码实践,并且在源码中还包括了一些必备的素材(10万单词表验证扰动函数)、工具、图标等,来让大家切身的体会到知识乐趣。也让所有**认真阅读的读者**,看后都能`让懂了就是真的懂`! + +### 4. 书籍截图 + +![](https://bugstack.cn/assets/images/2020/interview/interview-pdf-2.png) + +## 三、目录 + +### 第 1 章 谈谈面试 + +- 第 1 节:面试官都问我啥 +- 第 2 节:认知自己的技术栈盲区 +- 第 3 节:简历该怎么写 +- 第 4 节:大厂都爱聊啥 + +### 第 2 章 数据结构和算法 + +- 第 1 节:HashCode为什么使用31作为乘数 +- 第 2 节:HashMap 源码分析(上) +- 第 3 节:HashMap 源码分析(下) +- 第 4 节:2-3树与红黑树学习(上) +- 第 5 节:2-3树与红黑树学习(下) +- 第 6 节:ArrayList 详细分析 +- 第 7 节:LinkedList、ArrayList,插入分析 +- 第 8 节:双端队列、延迟队列、阻塞队列 +- 第 9 节:java.util.Collections、排序、二分、洗牌、旋转算法 +- 第 10 节:StringBuilder 与 String 对比 +- 第 11 节:ThreadLocal 源码分析 + +### 第 3 章 码农会锁 + +- 第 1 节:volatile +- 第 2 节:synchronized +- 第 3 节:ReentrantLock 和 公平锁 +- 第 4 节:AQS原理分析和实践运用 +- 第 5 节:AQS 共享锁,Semaphore、CountDownLatch + +### 第 4 章 多线程 + +- 第 1 节:Thread.start() 启动原理 +- 第 2 节:Thread,状态转换、方法使用、原理分析 +- 第 3 节:ThreadPoolExecutor +- 第 4 节:线程池讲解以及JVMTI监控 + +### 第 5 章 JVM 虚拟机 + +- 第 1 节:JDK、JRE、JVM +- 第 2 节:JVM 类加载实践 +- 第 3 节:JVM 内存模型 +- 第 4 节:JVM 故障处理工具 +- 第 5 节:GC 垃圾回收 + +## 四、PDF📚下载 + +**版权说明**:`11.5万字`、`417页`,`作者:小傅哥`的原创书籍[《Java 面经手册》](https://download.csdn.net/download/Yao__Shun__Yu/14932325),已上架 CSDN 付费下载平台,享受版权保护。但只设置最低下载价格:`4.9元`,感谢支持和理解。 + +*让人怪不好意思的,没接过广告的号主,只能这样收回点运营博客的服务器成本。更重要的是保护了版权!!!* + +### 1. 可获得内容包括 + +1. 《Java 面经手册》PDF 完整版书籍一本 +2. 完整版源码一份,共 27 个案例 +3. 可以加入面经专栏讨论群,添加我的微信:`fustack`,备注:`面经入群` + +### 2. 下载方式 + +1. 公众号:[bugstack虫洞栈](https://bugstack.cn/assets/images/qrcode.png),回复:`面经手册`,即可获得最新的下载链接。*更新和补充会更换链接* +2. 添加小傅哥微信(fustack),备注:`面经` + +## 五、🎉收尾 + +19年7月 ~ 20年初,是小傅哥做技术号主的*迷糊*状态,`没有粉丝基础`、`没有写作经验`、`没有技术文章沉淀`、当然也没有运营技巧,而一年后这样一个似乎是闯进了技术圈的该溜子终于在粉丝的包容、理解和支持上,慢慢成长起来了。`也有了一本《重学Java设计模式》全网可统计到的 21 万+ 下载量`、`GitHub 项目多次霸榜 Trending`,成为全球热门项目。也积累了属于自己的一窝粉丝,**感谢你们**! + +再说说`《Java 面经手册》`,本书到这里还不是结束,接下来还会继续编写,Spring、SpringBoot、Rpc、Mysql以及中间件相关的`面经`。同样,面经不只是面经,更是核心技术的学习和深入的了解。所有的内容的输出都是一个目的,让更多的人对知识能做到,让懂了就是真的懂! + +祝大家在学习过程中都有自己的收获和能力的提升,提前祝新年快乐,平安吉祥! diff --git "a/docs/md/product/pdf/2021-08-12-\343\200\212\346\211\213\346\222\270 Spring\343\200\213PDF\357\274\214\345\205\250\344\271\246260\351\241\2656.5\344\270\207\345\255\227\357\274\214\345\256\214\347\250\277&\345\217\221\347\211\210\357\274\201.md" "b/docs/md/product/pdf/2021-08-12-\343\200\212\346\211\213\346\222\270 Spring\343\200\213PDF\357\274\214\345\205\250\344\271\246260\351\241\2656.5\344\270\207\345\255\227\357\274\214\345\256\214\347\250\277&\345\217\221\347\211\210\357\274\201.md" new file mode 100755 index 000000000..3f4b92c44 --- /dev/null +++ "b/docs/md/product/pdf/2021-08-12-\343\200\212\346\211\213\346\222\270 Spring\343\200\213PDF\357\274\214\345\205\250\344\271\246260\351\241\2656.5\344\270\207\345\255\227\357\274\214\345\256\214\347\250\277&\345\217\221\347\211\210\357\274\201.md" @@ -0,0 +1,138 @@ +--- +layout: post +category: spring +title: 《手撸 Spring》PDF,全书260页6.5万字,完稿&发版! +tagline: by 小傅哥 +tag: [java] +excerpt: 18个章节清晰全覆盖Bean生命周期核心功能实现,在手写的过程中会简化 Spring 源码,摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:IOC、AOP、Bean生命周期、上下文、作用域、资源处理等内容实现。 +lock: need +--- + +# 《手撸 Spring》PDF,全书260页6.5万字,完稿&发版! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
原文:[https://mp.weixin.qq.com/s/kYio8zIG5UL-To3SV-uRmA](https://mp.weixin.qq.com/s/kYio8zIG5UL-To3SV-uRmA) +
PDF:[https://download.csdn.net/download/Yao__Shun__Yu/21009038](https://download.csdn.net/download/Yao__Shun__Yu/21009038) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +💥**不正经!竟然手撸Spring源码啦🌶?** + +是的,在写了部分关于 `Spring核心源码` 的面经内容后,我决定要去手撸一个Spring了。为啥这么干呢?因为所有我想写的内容,都希望它是以理科思维理解为目的方式学会,而不是靠着硬背记住。而编写面经的过程中涉及到的每一篇Spring源码内容分析,在即使去掉部分非主流逻辑后,依然会显得非常庞大。对有经验的老司机尚可阅读几遍接受,但就新人来讲只能放入收藏夹吃灰啦! + +![](https://bugstack.cn/assets/images/spring/spring-1-02.png) + +`翻译`、`抄书`、`说理论`给答案式填鸭分享知识绝对不可以!!!这样的方式只能把知识弄的云里雾里。所以我希望带着读者一点点手写简化版 Spring 框架,了解 Spring 核心原理,为后续再深入学习 Spring 打下基础。 + +在手写的过程中会剔除 Spring 源码中繁杂的内容,摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:IOC、AOP、Bean生命周期、上下文、作用域、资源处理等内容实现。 + +所有的内容实现都会由简开始,一步步带着大家实现,最终所有的内容完成后,在提供一个相对完整的 [small-spring](https://github.com/fuzhengwei/small-spring),在这个过程中只要你能跟着走下来,那么最后你一定可以**较容易的阅读 Spring 源码了**。 + +💋`鉴于作者水平有限`,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。 + +## 二、简介 + +![](https://bugstack.cn/assets/images/spring/spring-0-04.png) + +**Hello, world of spring !** 你好,spring的世界! + +欢迎来到这里,很高兴你能拿到这本书。如果你能坚持看完书中每章节的内容,那么不仅可以在你的面试求职上有所帮助,也更能让你对 Spring 核心技术有更加深入的学习。 + +本仓库以 Spring 源码学习为目的,通过手写简化版 Spring 框架,了解 Spring 核心原理。 + +在手写的过程中会简化 Spring 源码,摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:IOC、AOP、Bean生命周期、上下文、作用域、资源处理等内容实现。 + +[`小傅哥`](https://bugstack.cn/),之所以开始撸Spring源码,主要就是因为在编写[《面经手册》]([https://bugstack.cn/itstack/interview.html](https://bugstack.cn/md/java/interview/2020-07-28-%E9%9D%A2%E7%BB%8F%E6%89%8B%E5%86%8C%20%C2%B7%20%E5%BC%80%E7%AF%87%E3%80%8A%E9%9D%A2%E8%AF%95%E5%AE%98%E9%83%BD%E9%97%AE%E6%88%91%E5%95%A5%E3%80%8B.html))时,涉及到的Spring源码都会写很多的文字描述、绘制冗长的流程图稿、做不少的内容铺垫,但对于新人来说想直接学习这部分内容仍是非常困难的,那么现在为了让我以及更多的伙伴能有一个学习的`抓手`,我们来一起研究研究什么是快乐星球! + +--- + +1. 此专栏为实战编码类资料,在学习的过程中需要结合文中每个章节里,要解决的**目标**,进行的思路**设计**,带入到编码实操过程。在学习编码的同时也最好理解关于这部分内容为什么这样的实现,它用到了哪样的设计模式,采用了什么手段做了什么样的职责分离。只有通过这样的学习才能更好的理解和掌握 Spring 源码的实现过程,也能帮助你在以后的深入学习和实践应用的过程中打下一个扎实的基础。 + +2. 另外此专栏内容的学习上结合了[设计模式](https://item.jd.com/13218336.html),下对应了[SpringBoot 中间件设计和开发](https://juejin.cn/book/6940996508632219689),所以读者在学习的过程中如果遇到不理解的设计模式可以翻阅相应的资料,在学习完 Spring 后还可以结合中间件的内容进行练习。 + +3. **源码**:此专栏涉及到的源码已经全部整合到当前工程下,可以与章节中对应的案例源码一一匹配上。大家拿到整套工程可以直接运行,也可以把每个章节对应的源码工程单独打开运行。 + +4. 如果你在学习的过程中遇到什么问题,包括:不能运行、优化意见、文字错误等任何问题都可以提交issue,也可以联系作者:`小傅哥` 的微信,`fustack` + +5. 在专栏的内容编写中,每一个章节都提供了清晰的设计图稿和对应的类图,所以学习过程中一定不要只是在乎代码是怎么编写的,更重要的是理解这些设计的内容是如何来的。 + +### 1. 适合人群 + +1. 具备一定编程基础,工作1-3年的研发人员 +2. 想阅读Spring源码,但不知道从哪开始 +3. 对Spring容器中Bean对象的注册管理等生命周期有些模糊 +4. 需要依赖于Spring开发一些中间件,但不知道用哪些接口 +5. 想看看设计模式在Spring框架下的应用 +6. 希望彻底的了解Spring框架,并能在面试过程中占据上风 + +### 2. 我能学到什么 + +1. 看得懂,Bean容器是如何定义和实现的 +2. 学得会,工厂模式、策略模式、观察者模式等都是怎么在Spring中体现的 +3. 搞得清,从应用上下文到Bean对象的创建,是串联出一整套生命周期 +4. 弄得明,IOC、AOP、代理、切面、循环依赖都是如何设计和实现的 + +### 3. 阅读建议 + +此专栏是一本以开发简化版Spring学习其原理和内核的知识内容,不仅是代码编写实现也更注重内容上的需求分析和方案设计,所以在学习的过程要结合这些内容一起来实践,并调试对应的代码。粉丝伙伴在阅读的过程中,**千万不要害怕在学习的过程中遇到问题,这些都是正常的!** 希望你可以一直坚持把这些内容事必躬亲、亲历亲为的学完,加油! + +## 三、目录 + +![上车,带着你把Bean的生命周期搞完!](https://bugstack.cn/assets/images/spring/spring-0-03.png) + +### 容器篇: IOC + +- 第01章:开篇介绍 +- 第02章:创建简单的Bean容器 +- 第03章:实现Bean的定义、注册、获取 +- 第04章:对象实例化策略 +- 第05章:注入属性和依赖对象 +- 第06章:资源加载器解析文件注册对象 +- 第07章:应用上下文 +- 第08章:初始化和销毁方法 +- 第09章:Aware感知容器对象 +- 第10章:对象作用域和FactoryBean +- 第11章:容器事件和事件监听器 + +### 代理篇: AOP + +- 第12章:基于JDK、CGlib实现AOP切面 +- 第13章:把AOP扩展到Bean的生命周期 +- 第14章:自动扫描Bean对象注册 +- 第15章:通过注解注入属性信息 +- `【PDF专属】`第16章:给代理对象设置属性注入 + +### 高级篇:Design + +- `【PDF专属】`第17章:三级缓存处理循环依赖 +- `【PDF专属】`第18章:数据类型转换 + +## 四、PDF📚下载 + +**版权说明**:`6.5万字`、`260页`,`作者:小傅哥`的原创PDF书籍[《手撸 Spring》](#),已上架 CSDN 付费下载平台,享受版权保护,感谢支持和理解。 + +*让人怪不好意思的,没接过广告的号主,只能这样收回点运营博客的服务器成本。更重要的是保护了版权!!!* + +### 1. 可获得内容包括 + +1. 《手撸 Spring》PDF 完整版书籍一本 +2. 完整版源码一份,共 17 个案例 +3. 可以加入`手撸 Spring`专栏讨论群,添加我的微信:`fustack`,备注:`Spring入群` + +### 2. 下载方式 + +1. 通过连接下载:[https://download.csdn.net/download/Yao__Shun__Yu/21009038](https://download.csdn.net/download/Yao__Shun__Yu/21009038) +2. 关注公众号:[bugstack虫洞栈](https://bugstack.cn/assets/images/qrcode.png),回复:`Spring专栏`,即可获得最新的下载链接。*更新和补充会更换链接* +3. 添加小傅哥微信(`fustack`)获取连接,备注:`Spring PDF获取` + +## 五、🎉收尾 + +19.8、20.8、21.8,满满的都算上,小傅哥已经在这条路上摸索了两年多了,从`没有粉丝基础`、`没有写作经验`、`没有技术文章沉淀`、当然也没有运营技巧,一步步走来遇到了很多粉丝伙伴的支持、号主朋友的关心、平台运营的帮助,慢慢的长成了一颗倔强的技术小树! + +两年多来,写了8个专题,22个类别,255篇+文章!并于 `2021年04月23日`,图书节,我的第一本技术书[《重学Java设计模式》](https://item.jd.com/13218336.html)出版了。不久以后我感受过图书冲榜到销量榜第一、在图书馆看到我的技术书、被国外的粉丝伙伴购买带出国门,还看到有个女孩买来作为礼物送给他的男友。也于不久后看到百度百科还有我书籍的信息,还真是蛮开心的,*但不膨胀*! + +也祝大家,能在自己的路上坚持的走下去,长久的沉淀势必会收获到你想要的哪些旅途上的果实。悄悄的,`心怀天下,声色犬“码”。生有热烈,藏于俗常!` + diff --git "a/docs/md/product/pdf/2022-01-23-IDEA Plugin \345\274\200\345\217\221\346\211\213\345\206\214.md" "b/docs/md/product/pdf/2022-01-23-IDEA Plugin \345\274\200\345\217\221\346\211\213\345\206\214.md" new file mode 100644 index 000000000..7036f1a6e --- /dev/null +++ "b/docs/md/product/pdf/2022-01-23-IDEA Plugin \345\274\200\345\217\221\346\211\213\345\206\214.md" @@ -0,0 +1,100 @@ +--- +title: 《IDEA Plugin 开发手册》• 小傅哥.pdf | 4章12节,完稿&发版 +lock: need +--- + +# 《IDEA Plugin 开发手册》• 小傅哥.pdf | 4章12节,完稿&发版 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +`💥为什么?写写快乐的热门文章不好吗!` + +从开始准备成体系的编写 `IDEA Plugin` 知识内容前,我就知道这大概率不会是一个有太多阅读量的文章,因为基本日常的工作开发中几乎也用不到这样的知识。 + +那么为什么还要编写呢?就是因为用的人不多,所以这方面的知识成体系的少,也就导致真的有需要的人根本找不到一个可以上手的资料。*怎么开发*、*什么模式*、*哪种技术*、*如何发布* 等等,这些内容几乎就是空白的,在你有此类需求的时候完全不知道如何上手。 + +所以🌶,又一套**成系列体系**的`《IDEA Plugin 开发手册》`内容已经为有需要的你准备好啦: + +![](https://bugstack.cn/images/article/knowledge/knowledge-220123-01.png) + +- 此开发手册,分为4章12节循序渐进的通过实践案例开发的方式,串联 IDEA Plugin 开发的各项常用技术点,为读者讲解如何开发一个 IDEA 插件。 +- 基本开发类知识点包括:`gradle 工程创建`、`插件发布`、`Swing UI`、`各类窗体`、`菜单配置`、`工程上下文对象`、`向导步骤`、`内容存放`、`配置加载`等,通过这些知识在案例中的逐个使用,而学习如何开发插件。 + +💋`鉴于作者水平有限`,如果PDF中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我来完善,也欢迎与我多一些交互,互相进步共同成长。 + +## 二、能干啥,举个栗子🌰 + +`vo2dto,一个已经被下载1000+的插件` + +![](https://bugstack.cn/images/article/knowledge/knowledge-220123-02.png) + +这是小傅哥开发的一款用于帮助使用 IDEA 编写代码的研发人员,快速生成两个对象转换过程中所需要大量的 `x.set(y.get)` 代码块的 vo2dto 插件工具。*可以直接在 IDEA 中搜索安装使用* + +| `对vo2dto感兴趣的,程序员👨🏻‍💻‍,来自这些国家` | +|:---:| +| ![](https://bugstack.cn/images/article/knowledge/knowledge-220123-03.png) | + +- 插件:[https://plugins.jetbrains.com/plugin/18262-vo2dto](https://plugins.jetbrains.com/plugin/18262-vo2dto) +- 源码:[https://github.com/fuzhengwei/vo2dto](https://github.com/fuzhengwei/vo2dto) +- 视频:[https://www.bilibili.com/video/BV13Y411h7fv](https://www.bilibili.com/video/BV13Y411h7fv) - `讲解插件的整体设计和使用说明` + +## 三、别说了,上干货吧! + +![](https://bugstack.cn/images/article/knowledge/knowledge-220123-04.png) + +**Hello, world of idea plugin !** 你好,IDEA 插件的世界!欢迎来到这里,很高兴你能拿到这本书! + +IDEA 插件开发可以帮助研发人员提升能效,解决一些实际场景中的共性问题。但最近在折腾IDEA插件开发的时候,市面的资料确实不多,也没有成体系完整的开发指导手册,所以就遇到了很多不知道就不会的事情,需要一点点查询搜索源码、验证API接口,最终把各项功能实现,当然在这个过程中也确实踩了不少坑!接下来在这个专栏会把一些关于 IDEA 插件开发用到的各项知识做成案例输出出来,帮助有需要的研发伙伴,一起建设 IDEA Plugin。 + +### 1. 适合人群 + +1. 具备一定编程基础,工作1-3年的研发人员 +2. 有 IDEA Plugin 开发需求的研发人员 +3. 希望可以拓展一些除了业务以外的开发技能 +4. 想做一些开源软件的贡献人员 + +### 2. 我能学到什么 + +1. 看得懂,有很多的案例来串联 IDEA Plugin 插件开发技能 +2. 学得会,通过案例实践的方式学习 IDEA Plugin 开发技巧 +3. 搞得清,不只是实践,还是实际场景的结合 +4. 弄得明,学习完这套插件开发技巧,就可以自己完成一些场景设计和开发了 + +### 3. 阅读建议 + +此专栏是以案例串联 IDEA Plugin 插件开发中常用的技巧,在学习的过程中可以先着重案例实践,在去考虑如何设计和开发,以及已经上手后再去阅读一些核心的API以及如PMD插件的开发,学习各项技术补充自己的知识。 + +粉丝伙伴在阅读的过程中,**千万不要害怕在学习的过程中遇到问题,这些都是正常的!** 希望你可以一直坚持把这些内容事必躬亲、亲历亲为的学完,加油! + +## 四、PDF📚下载 + +**版权说明**:`作者:小傅哥`的原创PDF书籍[《IDEA Plugin 开发手册》](#),已发布知识星球(`码农会锁`)和CSDN下载平台,享受版权保护,感谢支持和理解。 + +### 1. 可获得内容包括 + +1. 《IDEA Plugin 开发手册》PDF 完整版书籍一本 +2. 完整版源码一份,共 12 个案例 +3. 可以加入`IDEA Plugin`专栏讨论群,添加我的微信:`fustack`,备注:`IDEA Plugin` + +### 2. 下载方式 + +- CSDN:[https://download.csdn.net/download/Yao__Shun__Yu/77484299](https://download.csdn.net/download/Yao__Shun__Yu/77484299) - `¥4.9` +- 知识星球(`码农会锁`):[https://t.zsxq.com/ufmQnA2](https://t.zsxq.com/ufmQnA2) - `知识星球用户可直接免费下载,不需要单独付费。此外知识星球还提供了简历优化、实战DDD秒杀项目、架构设计、PPT画架构等内容` +- 添加小傅哥微信(fustack)获取最新下载链接,备注:`IDEA Plugin` + +## 五、🎉收尾 + +`🏃🏻总有些美景,在跑步的早上` + +| 春 | 夏 | 秋 | 冬 | +| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | +| ![](https://bugstack.cn/images/article/knowledge/knowledge-220123-05.png) | ![](https://bugstack.cn/images/article/knowledge/knowledge-220123-06.png) | ![](https://bugstack.cn/images/article/knowledge/knowledge-220123-07.png) | ![](https://bugstack.cn/images/article/knowledge/knowledge-220123-08.png) | + +这是过年前的最后一本 PDF 收尾之作的发布了,这一年在内容输出上包括了:[`《SpringBoot 中间件的设计和开发》`](https://bugstack.cn/md/project/springboot-middleware/2021-03-31-%E3%80%8ASpringBoot%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%BC%80%E5%8F%91%E3%80%8B%E4%B8%93%E6%A0%8F%E5%B0%8F%E5%86%8C%E4%B8%8A%E7%BA%BF%E5%95%A6%EF%BC%81.html)、[`《重学Java设计模式》`出版图书](https://mp.weixin.qq.com/s/g9LYQEqzOeiYOpfG_5XFYg)、[`《手撸 Spring》`](https://mp.weixin.qq.com/s/kYio8zIG5UL-To3SV-uRmA)、[`《Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践》`](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html)、[`《IDEA Plugin 开发手册》`](https://download.csdn.net/download/Yao__Shun__Yu/77484299),哈哈哈,是不是就挺肝的!当一个原创做作者有了粉丝的正向反馈、有了平台的扶持、有了一些收益,就可以非常好的在喜欢的领域上不断的耕作。 + +**当看着一个个从发芽🌱到长大的内容🌲**,真的是非常的开心。这个过程也是我这一年每天能 10:45-11:00 睡觉,6:20 起床(跑步、写作),以此保持一个良好的作息习惯,有了健康的身体、有了内容的沉淀。也希望看到的这你,在22年有一个自己的计划,能落地的计划! diff --git a/docs/md/product/software/walicode.md b/docs/md/product/software/walicode.md new file mode 100644 index 000000000..7524636d8 --- /dev/null +++ b/docs/md/product/software/walicode.md @@ -0,0 +1,115 @@ +--- +title: WaLiCode,AI IDE Coding! +lock: no +--- + +WaLiCode - 小傅哥出品,研发自己人的 AI IDE Coding ! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥! + +得益于 Claude Code 的被动开源 😂 (~~怀疑是故意的~~),市面上出现了非常多的 Claude Code 源码架构设计分析,这也让我们看到了非常精彩的 +AI Agent 运行时设计技巧。按照剧本套路,我们使用的 AI IDE 将迎来一大波迭代升级。**嘿嘿,但这次,我先下手啦!** + +
+ +
+ +**小傅哥的 WaLiCode,AI IDE Coding v0.1.0 发布!** + +市面上优秀的 AI IDE 非常多,但论最好用的还得是 Claude Code。所以借助于这些的“源码”学习,小傅哥基于 Tauri(Rust)桥接 Claude +Code 能力(这个设计也可以桥接其他的,如 OpenCode),在上层做一套 AI IDE 应用服务实现。通过工程、工程、代码块(含图片)对话的方式,驱动代码开发辅助提效处理。 +`也让大家可以通过 WaLiCode 感受到 Claude Code 的巴士体验!` + +
+ +
+ +> WaLiCode 产品功能还在快速迭代中,可以扫码加入群聊,关注最新动态和讨论使用! + +## 一、产品介绍 + +**产品官网**:[https://walicode.xiaofuge.cn/](https://walicode.xiaofuge.cn/) - 提供了下载安装包( +`如果遇到什么bug,可以群里交流`)。 + +WaLiCode,本地运行的 AI 编程助手。基于 Tauri + Claude Code 底层能力桥接,深度集成文件系统与终端,让 AI 真正帮你写代码,而不只是给建议。更为重要的是你可以,你可以直接本地安装 WaLiCode 就能体验到 Claude Code 的编码能力! + +在辅助编码中,可以直接把代码块、文件、图片放入对话框作为上下文,AI 会结合项目结构与依赖关系给出更准确的修改建议,并可通过 +Diff 预览后安全应用到本地代码。 + +但当然,我还是一个配角,小配角!我还有很多功能需要迭代,让研发伙伴使用这款工具,可以在学习源码、了解项目、深入功能、俯瞰架构等方面,都能有所帮助。 +**好吧,让我们一起期待,WaLiCode 的迭代!** + +## 二、使用演示 + +### 1. 模型配置 + +
+ +
+ +- 多厂家模型灵活配置,支持自定义 OpenAI 格式的各类大模型接口,无论是智谱、火山引擎还是本地部署的模型,均可一键接入,满足不同场景的开发需求。 +- 各类的 token/coding plan,支持 OpenAI 格式就可以配置使用。 + +### 2. 产品首页 + +
+ +
+ +- 首先,安装软件后,打开 WaliCode 在初次使用会引导你配置 apikey,可以添加你需要的模型。得益于 Claude Code 和 WaliCode + 的扩展,上下文的使用会非常准确和节省,也就让你的模型消耗也会很低。 +- 之后,你可以打开文件夹选择自己的工程,进行对话框对话,以及智能终端对话。在对话中,你可以把模块、文件、代码以及图片,都放到对话框中,之后进行诉求描述。 + +### 3. 添加技能(Skills) + +
+ +
+ +- 在设置中可以添加你需要扩展的 Skills 技能,支持zip包上传、git地址直接拉取,可以非常方便的扩展 Skills。 +- 同时还为你提供了 SkillMP、ClawHub 官网 Skills 地址,随时去找你需要的技能扩展到工程里。 + +### 4. 添加服务(MCP) + +
+ +
+ +- 在设置中可以添加 MCP 服务能力,支持 stdio、sse、json 多种配置方式。极大的方便用户扩展自己的功能使用场景。 + +### 5. 智能终端 + +
+ +
+ +- 这是一个附属功能,方便用户可以在终端直接使用 Claude Code 能力。属于原生的扩展支持。 + +### 6. Git 能力 + +
+ +
+ +- 内置 Git 操作面板,支持分支切换、代码提交与状态查看。结合 AI 能力,还能自动分析变更并生成规范的提交信息。 + +> 好嘞,欢迎下载体验 [https://walicode.xiaofuge.cn/](https://walicode.xiaofuge.cn/) 遇到问题可以群里交流 😄 + +## 三、扩展学习(AI Agent) + +如果你希望自己不只是一个业务开发工程师,也具备 AI Agent 开发能力,让自己在这个 AI +时代做点什么,那么下面的教程你可以学习下。小傅哥也是在这些项目中积累的经验,可以一次次在 AI 快速迭代中,做一些有意思的事情。 + +
+ +
+ +小傅哥的社群星球「码农会锁」,现已经有20个实战项目,6个AI、5个业务、8个组件 + 1套源码(MyBatis),这6个AI项目,你可以按需选择学习。 + + + diff --git a/docs/md/project/ai-agent-scaffold/ai-agent-scaffold.md b/docs/md/project/ai-agent-scaffold/ai-agent-scaffold.md new file mode 100644 index 000000000..e1bb88f93 --- /dev/null +++ b/docs/md/project/ai-agent-scaffold/ai-agent-scaffold.md @@ -0,0 +1,297 @@ +--- +title: AI Agent 脚手架 + 场景应用 +lock: no +--- + +# 《AI Agent 脚手架 + 场景应用》- 综合 Spring AI、LangChain4j + Google ADK(a2a、mcp、skills),打造全新智能体架构方案。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
项目:[https://t.zsxq.com/a8AJj](https://t.zsxq.com/a8AJj) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +💥炸了!**日更,日更,接下来这套新项目,即刻开启日更!** 是的,小傅哥又一个新 AI 项目来啦。可以说,现阶段 AI 是每个工程师都必备的技术技能。 + +从 `RAG` 到 `MCP`、`A2A` 再到 `Skills`,一系列的 AI 编程技术技能陆续迭代更新, +各个互联网企业也随之跟进开发自身的 AI Agent 智能体,解决业务场景问题。 + +因此你在 Boss 直聘,校招/社招,都能看到大量的 AI 应用开发岗位。`AI 没让程序员工作丢失,反而多了更多的岗位!` + +
+ +
+ +新项目,📅于25年12月初启动,26年2月底完结,将近耗时90天打造精品企业级智能体解决方案项目。 + +在小傅哥社群,`OpenAI 代码自动评审做了`、`OpenAI 应用 + 扫码登录 + 微信支付 + 敏感词过滤 + SDK 开发做了`、`AI Agent 可视化编排也做了`、`AI MCP 网关深入理解 MCP 协议还做了`! + +那为什么还要做一个 AI Agent 呢?🤔🤔🤔 + +因为 **【AI Agent 可视化编排】** 解决的是横向通用性场景方案,而本次要做的 **【AI Agent 脚手架 + 场景方案】** 是纵深业务场景细化方案(也是企业里必备的架构设计,针对细化场景,通用的编排就失效啦!)。`这些东西,不是个架构师带着你,那你就根本理解不到!` + +好,该项目为大家带来一套企业级 AI Agent 智能体脚手架底座及场景应用方案的实战编程项目。项目采用 `Spring AI`、`Langchain4j(对照学习)` + `Google ADK`(a2a 框架 + 工作流编排),融合 `MCP`、`Skills`、`Plugin`、`Session` 等多项智能体开发技术,构建通用智能体脚手架工程。 + +并借助该脚手架,用户能够快速高效地搭建各类智能体应用场景。就像本次项目,既带着你搭建脚手架,也带着你使用脚手架做场景用(价值超级大,还要带着你做手机版大龙虾 MobileOpenClaw 🦞!)。 + +
+ +
+ +**💻 每个工程师,都需要AI应用编程技能!** + +竹外桃花三两枝,春江水暖鸭先知🦆。没有哪个行业,有程序员👨🏻‍💻可以这么快的接触到世界的科技变化。甚至也只有程序员行业,可以驾驭 AI 技术,做出各项 AI 应用软件。`AutoPhone`、`OpenClaw`,码农正在加速改变这个世界! + +> 当前互联网企业,Java 有庞大的市场,如美团、京东、阿里、饿了么、滴滴等。这些业务都要大量的引入 AI 进行提效,如;客服、巡检、运营、监控等。因为这些业务本身也都是基于 Java 构建,背靠 Spring 框架。所以很多公司也就天然的选择了 Spring AI 框架开发智能体应用。包括;阿里还基于 Spring AI 做了 alibaba 版本,谷歌也做了 google adk 框架。现在你懂了,为什么我们选择 Spring AI 实现业务智能体项目了吧!当然,这些框架也都类似,选择一套学习后,其他的也都很容易上手。 + + + +>🧧 文末提供了本套项目的完整工程代码,此外还有其他的业务项目 + 组件项目,共计20个全部可以获取。 + +## 一、我能学到什么 + +首先,这是一整套从0到1,文档 + 视频 + 源码,包含前后端 + DevOps 的综合实战项目。带着大家进行需求分析、底座构建、脚手架设计、应用场景实践。所以,你可以非常完整的学习到关于 AI Agent 智能体的全部内容,让你具备企业级项目开发能力。 + +- 【后端】熟练 Spring AI、Langchain4j(对照学习)框架的使用知识,掌握 api、model、client 的组装构建。 +- 【后端】深入 Spring AI 框架,使用 spring-ai-community 包,引入 Agent Skills 技能。 +- 【后端】设计多种 MCP 加载策略,满足 local、sse、stdio 各种类型的加载操作。 +- 【后端】运用 Google ADK 框架,整合 Spring AI、Langchain4j 分别验证学习,做好技术调研。 +- 【后端】使用 Google ADK 框架,通过提供的 loop(循环)、parallel(并行)、sequential(串行),构建多样性智能体。 +- 【后端】通过 Google ADK 框架,掌握 Runner 运行插件机制,掌握智能体运行中各个节点的数据采集和控制操作。 +- 【后端】设计通用智能体配置 yml 文件,通过配置文件的内容编排,可以配置出复杂的智能体。 +- 【后端】深入 Google ADK 源码,调试源码找到bug,并提供解决方案。已经在google adk 发布了[issue #705 - 小傅哥为 Google ADK 框架,提交的 issue 记录](https://github.com/google/adk-java/issues/705) +- 【后端】拓展设计模式的使用,在智能体构建中,使用规则树模式进行各个节点的编排。编排依赖于 agent.yml 文件的配置。这是非常灵活的设计。 +- 【后端】基于 Maven 脚手架构建方式,对底座工程创建出通用脚手架项目。 +- 【后端】积累 Netty 通信技术,基于 Netty 构建通信网关。 +- 【前端】安卓 Android(Kotlin) 网关终端开发,做智能设备控制。`基于 AutoPhone 9B + OpenClaw 理解,构建 MobileOpenClaw` +- 【前端】使用 React 构建前端工程 + draw.io,实现出一套智能体绘图操作。这部分会结合 AI IDE + prompt 进行编程实现。 +- 【运维】在云服务器环境(Ubuntu 24)安装 Docker 环境 + Protainer 管理面板,以及初始化环境等(提供了一件安装脚本)。 +- 【运维】分别对前后端进行 docker 镜像构建,以及在云服务器上完成项目的部署操作。 +- 【其他】积累应用设计经验,面向对象开发,在整个工程实现中,都有非常干净,清晰,具备高内聚,低耦合,有单一职责的逻辑体现。 + +>小傅哥带着你做的是企业级项目架构和技术积累,通过这些东西的学习,在面试中与面试官交流,才会显得更为专业。 + +## 二、适合哪些伙伴 + +- 需要快速🔜写到简历(每个阶段完成都可以写简历),用于秋招/社招面试(本项目可快速部署验证结果)。 +- 对 AI Agent 智能体感兴趣,但不知道如何自己实现一套的。 +- 希望提高自己的架构设计思维,设计模式运用的。 +- 增强核心竞争力,储备一些非业务的核心技术类知识的。 +- 需要掌握 Spring AI、Langchain4j、Google ADK 框架使用。 + +## 三、项目是否硬核 + +很多小伙伴都害怕学习到一个`(前端)外壳漂亮`,`(后端)代码水货`的项目,满是 CRUD 缺少架构设计,也没有编程思维的体现。这样的项目,在面试后端工程师的时候,很难讲出东西。所以,这里小傅哥先把一些核心的架构设计给大家看看,让大家知道小傅哥带着你学习的东西质量如何。 + +### 1. 智能体整体设计 + +
+ +
+ +- 2025年11月27日,Google 正式在 Maven 仓库管理中心,推送了 0.4.0 版本 ADK,该版本新增加了 Spring AI 的集成。[google-adk-spring-ai](https://central.sonatype.com/artifact/com.google.adk/google-adk-spring-ai) 至此,也因此,小傅哥决定基于这套服务组合,设计智能体脚手架。 +- 首先,Google ADK 是一个智能体框架,他自身也是支持直接对接各类大模型的 API,以及构建 ChatModel 的。但在整合 Spring AI、LangeChain4J 以后,Google ADK 的使用,将会得到已经使用上述组件的公司更大的青睐。 +- 之后,Spring AI 解决的 AI 对接的前半部分,让你可以把 AI API、Model、Prompt、RAG、Tool(Function、MCP)等,非常方便的构建出一个单一的 AI Agent 服务(也可以称之为是一个客户端)。 +- 然后,Google ADK 解决的是,多个 AI Agent 怎么协同工作的问题。这里包括,Sequential 序列顺序执行、Loop 循环执行、Parallel 并行执行,而这些执行方式,又可以组合搭配的配置到一个 Sequential 中进行顺序执行(注意图中颜色)。绿色的是大模型服务,绿色部分可以被深黄色或者浅青色包装,之后在组合到 SequentialAgent - 序列执行中。 +- 最后,Google ADK 提供了记忆上下文 Runner 执行器(也可以自己扩展实现),在这里又提供了钩子插件,你可以对执行过程中的流程,进行拦截。这个过程类似 Spring 容器中对 Bean 对象的处理,before、after 的过程。 + +### 2. 系统的分层结构 + +如图,整体简要架构设计(剥离其他流程,方便理解); + +
+ +
+ +整个应用架构分为3层,包括;基础底座、脚手架、业务场景; + +- 基础底座,负责整 Spring AI + Google ADK 框架的使用,这里的重点在于整个智能体工作流程的设计和使用(第2部分20节课程)。 +- 脚手架,将基础底座使用 maven 抽取出脚手架,脚手架可以让我们快速复刻出一套基础工程(第3部分3节)。 +- 业务场景,结合 draw.io 绘图操作 + ai agent 智能体,做一套AI交互式画图系统(第4部分6节)。 + +### 3. 底座的运行流程 + +
+ +
+ +- 首先,从用户基于脚手架创建完成后,在使用 YML 文件进行智能体的配置,之后在启动项目后,会进行一些利的装配。api、model、agent、workflow、runner,再到 spring 容器。 +- 其中,关于智能体的工作流组装是非常巧妙的,可以自由组合出多种类型智能体。这部分不需要硬编码即可完成。 +- 最后,是整个内容装配完成后,提供了通用的接口能力可以进行对话。 + +### 4. 脚手架配置发布 + +
+ +
+ +- 左侧,对现有工程使用 maven-archetype-plugin 插件,构建工程脚手架。将当前的工程打包成一个可复用的 Archetype 模板。 +- 中间,打包好的脚手架,可以在本地直接使用,也可以发布jar到私服,让大家都可以使用。私服部分,后续在做处理。 +- 右侧,使用方可以基于命令,或者 IntelliJ IDEA 配置 Maven 脚手架的方式,创建和启动工程。这一节,我们先通过命令的方式使用。 + +## 四、应用场景举例 + +### 1. 普通对话 + +
+ +
+ +- 智能体搭建后,可以进行对话操作,基于你配置的 MCP 能力,它可以做很多事项。 + +### 2. draw.io + 画图 + +
+ +
+ +```java +agents: + # 1. 需求分析与检索智能体 + - name: agent_analyst + description: 负责理解用户意图,调用工具检索信息,并决定是请求补充信息还是继续绘图。 + instruction: | + 你是一个专业的需求分析师。你的任务是分析用户的绘图请求。 + 1. 如果用户提供了具体的上下文或需要引用外部知识(如Git仓库、本地文件),请使用可用的工具(MCP)进行检索和分析。 + 2. 分析用户的意图: + - 如果用户的描述模糊、不完整,无法直接生成图表,你需要返回 JSON 格式要求用户补充信息。 + 格式:{"type": "user", "content": "请补充关于...的具体信息"} + - 如果用户意图清晰,请整理出详细的绘图需求(图表类型、节点、关系、布局要求等)。 + 3. 输出你的分析结果。 + output-key: analysis_result + # 2. 绘图执行智能体 + - name: agent_drawer + description: 根据分析结果生成 Draw.io 的 XML 数据。 + instruction: | + 你是一个 Draw.io 绘图专家。请根据输入 {analysis_result} 进行操作: + 1. 如果输入是 {"type": "user", ...},请直接原样输出该 JSON。 + 2. 如果输入是详细的绘图需求: + - 设计图表的结构(UML、流程图、时序图等)。 + - 生成符合 Draw.io 规范的 XML 代码。 + - 确保节点布局合理,逻辑清晰,连线不能交叉等。 + - 输出生成的 XML 内容。 + output-key: draft_diagram + # 3. 检查与优化智能体 + - name: agent_reviewer + description: 检查绘图结果,确保无连线交叉等问题,并格式化最终输出。 + instruction: | + 你是一个图表质量检查员。请审查输入 {draft_diagram}: + 1. 如果输入是 {"type": "user", ...},请直接原样输出。 + 2. 如果输入是 XML 代码: + - 检查连线是否混乱或有严重的交叉(在文本层面尽力优化布局逻辑)。 + - 检查 XML 语法是否正确。 + - 如果有问题,请尝试修正 XML。 + - 最终输出必须严格符合 JSON 格式: + {"type": "drawio", "content": "这里放最终的XML字符串"} + output-key: final_result +``` + +- ai agent + draw.io,可以配置出一套交互式绘图智能体。我们可以把诉求发给 AI,之后 AI 进行分析和决策,让用户补充信息或者直接画图。 +- 在大量的测试和体验中,这套智能体 + gpt 5.1 可以绘制出非常符合企业中真实场景的流程图,效果还是非常不错的。如果你还配置 mcp 可以结合本地代码库,文档库,产品PRD库,那么它还可以更好的绘制出相关的流程图。 + +### 3. AutoPhone 实验性场景 + +智谱发布过一个 [Open-AutoGLM](https://github.com/zai-org/Open-AutoGLM) 类似于豆包手机,可以通过指令发送 AI,AI 操作手机完成一系列动作。目前官网这套产品目前使用的是 ADB 连接手机,数据线调试方式。 + +- 文档(手机 + Agent):[https://bugstack.cn/md/algorithm/model/autoglm-phone-agent.html](https://bugstack.cn/md/algorithm/model/autoglm-phone-agent.html) +- 官网:[https://github.com/zai-org/Open-AutoGLM](https://github.com/zai-org/Open-AutoGLM) + +这里小傅哥在体验了 OpenClaw 大龙虾的设计后,对 AutoPhone 也有了想法。我们可以设计一套安卓版的手机 MobileOpenClaw,在手机端开发一个网关,网关功能具备;`启动应用`、`点击指定坐标`、`输入文本`、`滑动屏幕`等。之后在让 AI 以借助 Socket 通信,对手机设备进行管理。 + +
+ +
+ +- 首先,需要实现一套 MobileOpenClaw 的网关,这部分内容是安卓开发的一个软件,如果 IOS 也还有其他方案。可以在 Github 检索相关资料 [https://github.com/search?q=phone%20agent&type=repositories](https://github.com/search?q=phone%20agent&type=repositories) +- 之后,基于脚手架,开发 MobileOpenClaw 智能体,这部分要通过 Socket 和 手机端进行通信。让 AI 识别用户意图,控制手机端执行相关操作。因为这里大量的视觉识别,所以 gemini-3-pro-preview 效果不错,另外就是 GLM 定制的 [AutoGLM-Phone-9B](https://github.com/zai-org/Open-AutoGLM) 模型,可以自己在 GPU 部署。 + +## 五、课程学习目录 + +全程`视频` + `文档` + `源码`,开局 IntelliJ IDEA + Webstorm + Android Studio,手把手带着你一路狂飙! + +
+ +
+ +>以下2、3、4部分,每部分做完,都可以写简历,也就是最早学习完2部分20节,就可以写一份简历啦! + +### 介绍 + +[AI Agent 脚手架 + 场景应用](#) - 综合 Spring AI、LangChain4j + Google ADK(a2a、mcp、skills),打造全新智能体架构方案。 + +[面试:技能、简历、问题汇总](#) + +### 第1部分:需求与架构 + +- [第1-1节:脚手架需求分析](#) +- [第1-2节:系统架构设计](#) + +### 第2部分:基础底座开发 + +- [第2-1节:工程初始化创建](#) +- [第2-2节:Api功能测试](#) +- [第2-3节:智能体配置表设计](#) +- [第2-4节:装配域结构化定义](#) +- [第2-5节:装配域节点-AiApiNode](#) +- [第2-6节:装配域节点-ChatModelNode](#) +- [第2-7节:装配域节点-AgentNode](#) +- [第2-8节:装配域节点-AgentWorkflowNode](#) +- [第2-9节:装配域节点-Loop、Parallel、Sequential](#) +- [第2-10节:装配域节点-RunnerNode](#) +- [第2-11节:智能体加载使用验证](#) +- [第2-12节:增强装配-RunnerNode](#) +- [第2-13节:增强装配-AgentWorkflowNode](#) +- [第2-14节:增强装配-本地mcp](#) +- [第2-15节:增强装配-回调plugin](#) +- [第2-16节:fix-多模态能力使用](#) +- [第2-17节:会话服务接口实现-service](#) +- [第2-18节:会话服务接口实现-trigger](#) +- [第2-19节:会话服务接口对接-ui](#) +- [第2-20节:增强装配-skills](#) + +### 第3部分:脚手架工程化 + +- [第3-1节:Maven脚手架配置](#) +- [第3-2节:上传jar到maven仓库](#) +- [第3-3节:部署脚手架网页](#) + +### 第4部分:业务场景(ai+draw.io) - `这部分内容非常有实用价值!` + +- [第4-0节:ai + draw.io 产品设计](#) +- [第4-1节:初始化工程搭建](#) +- [第4-2节:在页面嵌入draw.io组件和对话框](#) +- [第4-3节:智能体API接口对接](#) +- [第4-4节:AI+用户+DrawIO,交互式画图](#) +- [第4-5节:ai-draw-io,云服务器部署](#) + +### 第5部分:业务场景(MobileOpenClaw)- `这部分内容非常有意思!` + +- [第5-0节:MobileOpenClaw 产品设计](#) +- [第5-1节:初始化工程搭建](#) +- [第5-2节:手机网关能力设计](#) +- [第5-3节:通过 Netty 进行同步等待通信](#) +- [第5-4节:智能体初步配置使用](#) +- [第5-5节:智能体工作流设计](#) +- [第5-6节:异步结果响应](#) +- [第5-7节:图片位点识别增强](#) +- [第5-8节:多版本安卓版本策略支持](#) +- [第5-9节:会话上下文细化处理](#) + +>星球里另外一套 AI Agent 还对接了 ELK、普罗米修斯、微信公众号等,也可以把 MCP 对接过来进行系统巡检。有了这套脚手架的学习,你可以完成非常多的场景对接使用。 + +## 六、学习路线推荐(AI) + +小傅哥的社群星球「码农会锁」,现已经有20个实战项目,6个AI、5个业务、8个组件 + 1套源码(MyBatis),这6个AI项目,你可以按需选择学习。 + +
+ +
+ +> 综上,所有的实战项目,加入小傅哥社群,全部都可以学习的到! \ No newline at end of file diff --git a/docs/md/project/ai-agent-scaffold/notes.md b/docs/md/project/ai-agent-scaffold/notes.md new file mode 100644 index 000000000..5173d8046 --- /dev/null +++ b/docs/md/project/ai-agent-scaffold/notes.md @@ -0,0 +1,241 @@ +--- +title: 面试:技能、简历、问题汇总 +lock: no +--- + +# 面试:技能、简历、问题汇总 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、项目介绍 + +面试官您好,本套 AI Agent 综合智能体脚手架 项目,主要为降低智能体开发门槛、提升业务系统集成效率而构建。项目涵盖了:`工程脚手架自动生成`、`智能工作流编排`、`多模态交互(文本/绘图/动作)` ,以及`手机自动化网关控制`等核心功能和场景方案。 + +整套项目,核心抽象并拆分了 AI Agent 执行过程所需的各项组件(`Armory 装配域` 、`Runner 执行器` 、 `Skills 技能库`等)到结构化的配置定义中,使其具备自由配置、灵活编排的特性。以此方式,开发者可以结合实际业务场景诉求,像搭积木一样编排复杂的智能体协作流程——例如项目中实现的 "AI + Draw.io 交互式绘图" 和 "Android 手机自动化网关 - MobileOpenClaw" 场景,达到“业务需要什么能力,就组装什么节点”的 AI Auto Agent 效果。 + +该项目在架构设计上,严格遵循 DDD(领域驱动设计) 分层架构进行开发,核心采用了 **组合模式(Composite Pattern)**,构建智能体的执行规则树(支持串行、并行、循环流转),并结合**工厂模式 (创建不同类型的 Agent/Tool 节点)**、 **策略模式 (处理不同的模型调用策略)** 以及 **责任链设计思维模式(这块按照不同的理解,说成规则树也可以) (处理节点间的上下文传递)** 来处理具体流程细节。这种设计不仅解耦了底层模型(支持 Spring AI、Google ADK)与上层业务逻辑,也使得系统在面对未来各类扩展性诉求(如新增 MCP 协议、对接新模型)时,能够更加灵活方便地进行迭代。 + +## 二、简历模板 + +**项目名称**:AI Agent 智能体通用脚手架(可以自己起项目名称) + +**项目架构**:DDD 领域驱动设计(六边形架构)、事件驱动架构、前后端分离、组合模式规则引擎、模块化微服务 + +**核心技术** :Spring Boot 3.4、JDK 17+、Spring AI(LangChain4j)、Google ADK(Application Development Kit)、Kotlin (Android)、MCP (Model Context Protocol)、RAG (Vector Store)、Netty、Websocket、React、Draw.io (mxGraph)、TypeScript、Maven Archetype、Docker、Nginx - `不用都写,可以适当减少。` + +**项目描述** : + +本项目是一套企业级 AI Agent 综合解决方案与标准化脚手架,旨在解决智能体开发中“编排难、落地难、复用难”的痛点。系统基于 DDD 分层架构,创新性地设计了 Armory(装配域) 引擎,将复杂的业务流程抽象为可配置的节点(Node)流转。支持用户通过低代码方式,动态组装 Advisor(顾问) 、 Tools(工具) 和 Workflows(工作流) 。此外基于本套脚手架,实现了从“文本对话”到“自动绘图(Draw.io)”再到“物理设备控制(手机网关)”的全链路智能化。该平台不仅显著降低了 AI 应用的开发门槛,还通过标准化的 MCP 协议,实现了跨生态的能力互通。 + +**核心职责** : + +- 智能体架构设计与 DDD 建模 : + + - 主导系统的领域驱动设计,划分 Armory(装配) 、 Runner(执行) 、 Skill(技能)等核心领域。采用 六边形架构 解耦底层模型依赖(Spring AI / Google ADK),确保系统可无缝切换不同的大模型供应商(OpenAI, Claude, DeepSeek)。 + - 设计基于 组合模式(Composite Pattern) 的执行规则引擎,实现了 Loop(循环优化) 、 Parallel(并行处理) 、 Sequential(串行执行) 等多种编排策略,支持复杂的动态工作流配置。 +- 核心组件抽象与开发 : + + - 拆解并标准化 AI Agent 的核心原子能力,构建了包括 AgentNode (智能体节点)、 AiApiNode (接口节点)、 RouterNode (路由节点)在内的组件库。 + - 设计 DynamicContext(动态上下文) 机制,利用 责任链模式 在节点间传递会话状态与中间结果,解决了多智能体协作时的数据共享与状态一致性问题。 +- 多智能体协作与可视化场景实现(AI + Draw.io)- `这个场景可以独立展开作为一个简历` : + + - 针对业务流程图绘制场景,设计了 Analyst(需求分析) -> Drawer(绘图执行) -> Reviewer(质量审查) 的多智能体协作链路(Multi-Agent System)。 + - 基于 React + Draw.io (mxGraph) 定制前端组件,开发了专门的 JSON 交互协议,使 AI 能够直接生成和修改 XML 图表数据,实现了“一句话生成专业 UML/流程图”的端到端能力。 + - 集成并适配(百度搜索) https://sai.baidu.com 等公开 MCP Server 资源,让绘图可以检索网络资源。 +- 智能体与硬件设备交互(端侧网关建设) - `这个场景可以独立展开作为一个简历` : + + - 开发了 MobileOpenClaw(手机网关) ,通过 Netty 长连接 与 Android 端 AccessibilityService(无障碍服务) 通信,赋予 AI 操作物理设备的能力(点击、滑动、截图)。以 AutoPhone GLM 为参考,做设备通信设计。 + - 使用 auto-glm-9b 模型,分析用户诉求,网关网关入口下达操作指令。让手机端完成用户的行为动作。如;点赞、下单、刷抖音、收藏、发微信,等行为动作。 +- 脚手架工程化与提效 : + + - 设计并发布 Maven Archetype 脚手架,支持一键生成包含完整依赖与最佳实践的 AI 工程代码。 + - 实现了 Agent 预热机制 ,在 Spring 容器启动时动态加载和校验模型配置,支持运营人员在不重启服务的情况下,通过配置中心热更新 Prompt 和编排逻辑。 + +## 三、面试题归档 + +### 项目架构与设计模式 + +#### 1. 请简述本项目的整体架构设计,以及为什么选择 DDD(领域驱动设计)? +**参考答案:** +本项目采用了 **DDD(领域驱动设计)** 配合 **六边形架构(Hexagonal Architecture)**。 +- **分层设计**:将系统划分为 **API 接口层**(Controller/Trigger)、**应用层**(Service/Application)、**领域层**(Domain/Model)和 **基础设施层**(Infrastructure)。 +- **核心优势**: + - **解耦业务与技术**:核心业务逻辑(如 Agent 编排、策略执行)定义在 Domain 层,不依赖具体的 AI 模型(OpenAI/Claude)或数据库实现。通过 **适配器模式(Adapter Pattern)** 在 Infrastructure 层实现具体技术细节,方便随时替换底层 AI 服务商。 + - **高内聚低耦合**:各层职责清晰,便于单元测试和维护。例如,Armory 装配域专注于智能体构建,Chat 领域专注于对话逻辑。 + +#### 2. 在“智能体装配(Armory)”模块中,你是如何处理复杂的配置解析和对象构建的?使用了哪些设计模式? +**参考答案:** +- **设计模式**:核心使用了 **组合模式(Composite Pattern)** 和 **责任链模式(Chain of Responsibility)** 的思想,构建了一个 **节点(Node)处理管道**。 +- **实现逻辑**: + - 将复杂的 Agent 构建流程拆解为一系列独立的 **Node**(如 `AiApiNode` 配置 API、`ChatModelNode` 配置模型、`AgentNode` 实例化智能体、`AgentWorkflowNode` 处理编排)。 + - 定义了统一的 `IArmoryService` 接口和 `AbstractArmorySupport` 抽象类,确保所有节点遵循相同的规范。 + - 使用 **工厂模式(Factory Pattern)**(`DefaultArmoryFactory`)来创建和管理这些节点。 + - 通过 **上下文对象(DynamicContext)** 在节点间传递配置数据,避免了方法参数爆炸,实现了构建逻辑的 **高扩展性**(新增节点只需实现接口并注册即可)。 + +#### 3. 项目中是如何实现 MCP (Model Context Protocol) 的?特别是“本地 MCP”是如何设计的? +**参考答案:** +- **多协议支持**:项目支持 **SSE**(Server-Sent Events)和 **Stdio** 标准协议,通过 `McpClient` 统一封装。 +- **本地 MCP 设计**: + - **策略模式(Strategy Pattern)**:定义了 `ToolMcpCreateService` 策略接口,分别实现了 `SseMcpCreateStrategy`、`StdioMcpCreateStrategy` 和 **`LocalMcpCreateStrategy`**。 + - **反射与 Spring 容器**:**本地 MCP** 的核心是利用 Java 反射机制和 Spring 的 `ApplicationContext`。它根据配置的 `beanName` 和 `methodName` 直接查找并调用本地 Spring Bean 的方法,将其封装为 `FunctionCallback` 提供给 AI 模型。这使得 AI 可以直接调用本地业务代码(如查询数据库、发送邮件),无需走网络协议,**性能更高** 且 **开发更便捷**。 + +--- + +### 二、 并发编程与网络通信 (Netty) + +#### 4. 在“手机网关”模块中,Netty 服务端是如何解决 TCP 粘包/拆包问题的?通信协议是如何设计的? +**参考答案:** +- **粘包/拆包解决方案**:使用了 Netty 自带的 **`LineBasedFrameDecoder`**。 + - **原理**:以换行符 `\n` 作为消息结束的标志。发送端在 JSON 数据后追加换行符,接收端的 Decoder 会自动根据换行符分割出完整的消息帧,从而解决 TCP 粘包和拆包问题。 +- **通信协议设计**:采用 **JSON 文本协议**。 + - **请求**:`{"type": "action", "command": "click", "x": 100, "y": 200}\n` + - **响应**:`{"type": "response", "status": "success", "data": "..."}\n` + - **优势**:协议简单,易于调试(人类可读),且 JSON 解析库成熟。 + +#### 5. Netty 是异步通信的,但业务层调用(如“让手机截图”)通常需要同步等待结果,你是如何实现的? +**参考答案:** +- **核心机制**:使用了 JDK 的 **`CompletableFuture`** 实现 **异步转同步**。 +- **实现步骤**: + 1. **请求映射**:在发送指令前,创建一个 `CompletableFuture` 对象,并将其存入一个线程安全的 `ConcurrentHashMap`(Key 为请求 ID)。 + 2. **同步等待**:业务线程调用 `future.get(timeout)`,进入阻塞等待状态。 + 3. **异步回调**:当 Netty 的 `channelRead` 方法收到客户端响应时,根据响应中的 ID 从 Map 中取出对应的 `future`。 + 4. **唤醒线程**:调用 `future.complete(response)` 将结果填入,此时阻塞的业务线程会被唤醒并获取到结果。 + 5. **超时处理**:如果 `future.get()` 超时,抛出异常并从 Map 中移除该 Future,防止内存泄漏。 + +--- + +### 三、 AI 智能体编排与业务逻辑 + +#### 6. 请介绍一下项目中 Agent 工作流(Workflow)的设计,Loop、Parallel 和 Sequential 节点分别解决了什么问题? +**参考答案:** +项目通过 `AgentWorkflowNode` 实现了类似 **LangChain** 或 **Google ADK** 的编排能力: +- **Sequential(串行编排)**: + - **场景**:任务之间有严格依赖关系。例如:`搜索信息` -> `整理摘要` -> `生成报告`。 + - **实现**:按顺序依次执行 List 中的 Agent,上一个 Agent 的输出作为下一个的输入。 +- **Parallel(并行编排)**: + - **场景**:任务独立且耗时,需要提高效率。例如:同时`搜索技术方案 A`、`搜索技术方案 B`、`搜索技术方案 C`,最后汇总对比。 + - **实现**:使用线程池并发执行多个 Agent,最后通过 `CountDownLatch` 或 `CompletableFuture.allOf` 等待所有任务完成并聚合结果。 +- **Loop(循环编排)**: + - **场景**:需要反复迭代直到满足条件。例如:`生成代码` -> `运行测试` -> `报错` -> `修正代码` -> `再运行测试`... 直到测试通过或达到最大重试次数。 + - **实现**:在 `LoopAgentNode` 中维护一个循环逻辑,根据 **评估函数(Evaluator)** 的结果决定是继续循环还是输出最终结果。 + +#### 7. 在“AI + Draw.io”场景中,如何确保 AI 生成的 XML 是可用的?前后端交互协议是如何设计的? +**参考答案:** +- **质量控制(Reviewer Agent)**: + - 引入了 **多智能体协作(Multi-Agent System)** 机制。 + - 设计了专门的 **Reviewer(审查员)** 智能体。在 **Drawer(绘图员)** 生成 XML 后,Reviewer 会检查 XML 的语法正确性、连线逻辑(如是否交叉、是否有孤立节点)等。如果发现问题,Reviewer 会反馈给 Drawer 进行修正,直到通过审查。 +- **交互协议设计**: + - 定义了包含 `type` 字段的 JSON 协议: + - `type: "user"`:表示 AI 需要用户补充信息(如“请问您想要画什么类型的图?”),前端渲染为对话框。 + - `type: "drawio"`:表示 AI 生成了绘图数据,前端解析 XML 并调用 Draw.io 组件进行渲染。 + - `type: "review"`:表示正在审查中(可选,用于展示状态)。 + - **优势**:明确区分了 **对话交互** 和 **指令执行**,提升了用户体验。 + +#### 8. 你提到的“Spring AI 动态预热”是如何实现的?有什么作用? +**参考答案:** +- **作用**: + - **快速响应**:AI 模型(尤其是加载了大量 Prompt 或上下文的模型)初始化较慢,预热可以避免用户第一次请求时的长延迟。 + - **配置校验**:在应用启动时尽早发现 API Key 错误、网络不通等配置问题,避免上线后报错。 +- **实现**: + - 利用 Spring 的 `ApplicationRunner` 或 `CommandLineRunner` 接口,在 Spring 容器启动完成后自动触发。 + - 读取配置文件中的 `warm-up` 列表,模拟一次简单的 AI 调用(如发送 "Hello"),强制初始化底层的 `ChatModel` 和 `Token` 连接池。 + +--- + +### 进阶与优化 (Spring AI/Skills/性能) + +#### 9. 在 Spring AI 集成中,项目是如何利用 "Skills" 机制来增强 RAG 或替代传统向量检索的? +**参考答案:** +- **Skills 概念**:项目引入 `spring-ai-agent-utils`,将特定领域的知识(如 PDF 处理、系统巡检脚本)封装为 **Skills(技能书)**,这不仅仅是静态文本,还包含了可执行的代码逻辑(Tools)。 +- **与传统 RAG 对比**: + - **传统 RAG**:侧重于 **检索 (Retrieval)**,即从向量数据库中找到相关文本片段喂给 LLM。优点是知识覆盖面广,缺点是不仅准确率受限于检索算法,而且无法直接操作。 + - **Skills**:侧重于 **执行 (Action)**。它将“工具定义 + Prompt 模板 + 执行脚本(Python/Shell)”打包。Agent 识别到意图后,直接加载对应的 Skill 并执行脚本。 + - **优势**:减少了 LLM 的幻觉(执行逻辑是确定性的代码),缩短了 "Prompt -> 分析 -> 找工具 -> 执行" 的决策链路,对于特定任务(如“重启服务器”)比 RAG 更精准、更安全。 + +#### 10. 移动网关系统中,服务端如何处理 Netty 的异步通信与 Agent 同步决策之间的矛盾? +**参考答案:** +- **矛盾点**:Netty 是基于事件驱动的异步框架,而 Agent 的思维链(Chain of Thought)通常是线性的、同步等待每一步结果的(例如:点击按钮 -> 等待截图 -> 分析截图 -> 下一步)。 +- **实现机制**: + - **Future 模式**:服务端维护一个 `Map> pendingResponses`。 + - **同步等待**:Agent 线程在发送指令后,调用 `future.get(timeout)` 阻塞等待。 + - **异步唤醒**:Netty 的 Handler 收到手机端响应后,根据 ID 从 Map 中取出对应的 Future,调用 `future.complete(response)`。 + - **异常处理**:设置超时时间(如 30秒),防止因手机端无响应导致 Agent 线程永久阻塞。 + +#### 11. 项目在装配 MCP (Model Context Protocol) 工具时使用了什么设计模式?解决了什么问题? +**参考答案:** +- **设计模式**:使用了 **策略模式 (Strategy Pattern)** 配合 **工厂模式 (Factory Pattern)**。 +- **解决痛点**:在 `ChatModelNode` 中,如果使用大量的 `if-else` 来判断是创建 SSE 客户端、Stdio 客户端还是本地 Bean 调用,代码会非常臃肿且难以维护。 +- **实现细节**: + - 定义了 `ToolMcpCreateService` 策略接口。 + - 实现了 `SseMcpCreateStrategy`、`StdioMcpCreateStrategy`、`LocalMcpCreateStrategy` 等具体策略类。 + - 工厂类 `DefaultMcpClientFactory` 根据配置自动分发请求到对应的策略实现,从而实现了代码的 **开闭原则(Open/Closed Principle)**——新增一种协议只需增加一个策略类,无需修改原有逻辑。 + +#### 12. 针对智能体服务的异常处理,项目采用了怎样的分层处理策略?如果 Netty 服务端突然宕机,如何保证 Agent 任务的一致性? +**参考答案:** +- **分层异常处理**: + - **Trigger 层**:`AgentServiceController` 使用 `try-catch` 全局捕获异常,并封装为统一的 `Response` 对象(包含错误码和提示信息),确保前端总是收到合法的 JSON 响应。 + - **Service 层**:业务逻辑中抛出自定义 `AppException`(如 `E0001` 智能体不存在),由上层统一处理,避免将底层堆栈信息直接暴露给用户。 +- **宕机恢复方案(进阶)**: + - **现状**:目前架构中 `CompletableFuture` 存储在内存中,宕机会导致任务丢失。 + - **优化思路**:引入 **持久化状态机(State Machine)**。在下发指令前,将 Task 状态(如 `PENDING_SCREENSHOT`)写入 Redis 或数据库。服务重启后,通过 Session ID 恢复上下文,或者手机端实现 **断线重连(Heartbeat)** 机制,重连后主动上报当前状态,Agent 根据最新截图重新规划后续步骤。 + +--- + +### AI Agent 核心八股文 (概念与原理) + +#### 13. 什么是 AI Agent(智能体)?它与传统的 LLM 应用(如 ChatGPT)有什么本质区别? +**参考答案:** +- **AI Agent 定义**:AI Agent 是一个能够感知环境、进行推理规划、主动调用工具并采取行动以实现目标的智能系统。它具备 **感知(Perception)**、**大脑(Brain/LLM)**、**规划(Planning)** 和 **行动(Action/Tools)** 四大核心组件。 +- **与传统 LLM 区别**: + - **被动 vs 主动**:ChatGPT 等 LLM 应用通常是 **被动响应** 用户输入的(Input -> Output),无状态且单次交互。而 AI Agent 具备 **主观能动性**,它能将复杂目标拆解为一系列子任务,并主动调用工具(如搜索、API、代码执行)来获取信息或改变环境状态。 + - **工具使用能力**:传统 LLM 仅依赖训练数据(内部知识),存在幻觉且无法获取实时信息。Agent 通过 **Function Calling** 或 **MCP** 协议连接外部世界(Google、数据库、API),极大地扩展了能力边界。 + - **循环迭代**:Agent 通常采用 **ReAct (Reasoning + Acting)** 范式,即“思考 -> 行动 -> 观察结果 -> 再思考”,形成一个闭环,直到目标达成。 + +#### 14. 什么是 ReAct 范式?它是如何让 Agent 具备推理能力的? +**参考答案:** +- **概念**:ReAct 是 **Reasoning(推理)** 和 **Acting(行动)** 的组合。它是一种 Prompt Engineering 技术,要求 LLM 在执行具体动作前,先生成一段“思维链(Thought)”。 +- **工作流程**: + 1. **Thought(思考)**:LLM 分析当前任务,决定下一步该做什么(例如:“我需要先搜索一下今天的日期”)。 + 2. **Action(行动)**:LLM 生成调用工具的指令(例如:`SearchTool.search("current date")`)。 + 3. **Observation(观察)**:工具执行并返回结果(例如:“2023-10-27”)。 + 4. **Repeat(循环)**:LLM 根据观察结果进行下一轮思考,直到得出最终答案(Final Answer)。 +- **优势**:相比直接生成答案(Zero-shot),ReAct 显著减少了幻觉,增强了解决复杂多步问题的能力,并提供了可解释的推理过程。 + +#### 15. 请解释 RAG(检索增强生成)与 Fine-tuning(微调)的区别及各自适用场景。 +**参考答案:** +- **RAG (Retrieval-Augmented Generation)**: + - **原理**:类似于“开卷考试”。在 LLM 生成答案前,先从外部知识库(向量数据库)中检索相关文档片段,作为上下文(Context)拼接到 Prompt 中喂给 LLM。 + - **适用场景**:需要**实时更新知识**(如新闻)、**私有数据查询**(如企业文档)、**降低成本**(无需训练模型)的场景。 + - **优缺点**:成本低、无遗忘问题,但受限于检索精度和 Context Window 长度。 +- **Fine-tuning (微调)**: + - **原理**:类似于“复习内化”。使用特定领域的数据集对预训练模型进行进一步训练,调整模型参数,使其掌握特定领域的知识或风格。 + - **适用场景**:需要**特定格式输出**(如代码生成、JSON提取)、**特定领域深度理解**(如医疗诊断)、**降低延迟**(无需检索)的场景。 + - **优缺点**:效果好、响应快,但成本高、知识更新困难(需重新训练)。 + +#### 16. 什么是向量数据库(Vector Database)?它在 AI Agent 中起什么作用? +**参考答案:** +- **定义**:专门用于存储和检索高维向量(Vector / Embedding)的数据库。它支持 **近似最近邻搜索(ANN)**,能快速找到与查询向量距离最近的数据。 +- **Embedding(嵌入)**:将文本、图片、音频等非结构化数据转化为数字向量的过程。语义相似的内容,其向量在空间中的距离更近(如 Cosine Similarity)。 +- **在 Agent 中的作用**: + - **长期记忆(Long-term Memory)**:Agent 可以将对话历史、用户偏好存储在向量库中,实现跨会话记忆。 + - **知识库检索(Knowledge Base)**:存储海量文档片段,供 RAG 检索使用,弥补 LLM 训练数据的滞后性。 + +#### 17. 什么是 Chain of Thought (CoT)?它如何提升 LLM 的推理能力? +**参考答案:** +- **概念**:思维链(Chain of Thought)是一种通过引导 LLM 生成**中间推理步骤**来解决复杂问题的技术。 +- **原理**:对于数学题或逻辑推理题,如果直接问答案,LLM 容易出错。但如果提示它“**Let's think step by step(让我们一步步思考)**”,或者在 Few-shot 示例中展示推理过程,LLM 就会模仿这种思维方式,将大问题拆解为小问题逐个击破。 +- **类型**: + - **Zero-Shot CoT**:仅添加提示词 "Let's think step by step"。 + - **Few-Shot CoT**:在 Prompt 中提供包含推理步骤的示例()。 +- **价值**:显著提升了 LLM 在算术、常识推理和符号推理任务上的准确率,是 Agent 实现复杂规划的基础。 + +#### 18. 什么是 MCP (Model Context Protocol)?为什么它对 Agent 生态很重要? +**参考答案:** +- **定义**:MCP 是一个开放标准协议,用于标准化 AI 模型与外部数据/工具的连接方式。它定义了统一的接口规范,使得工具(Tools)、资源(Resources)和提示(Prompts)可以跨平台、跨模型复用。 +- **核心价值**: + - **去碎片化**:解决了每个 AI 应用都需要为 Google Drive、Slack、GitHub 等写独立连接器(Connector)的问题。MCP 提供了统一的“插座”。 + - **即插即用**:开发者只需编写一次 MCP Server,任何支持 MCP 的 Client(如 Claude Desktop、Cursor、IDEA)都可以直接使用该工具。 + - **安全性**:通过标准化的鉴权机制(如 SSE + Token)管理数据访问权限。 diff --git a/docs/md/project/ai-agent-scaffold/part-0/mobile-openclaw.md b/docs/md/project/ai-agent-scaffold/part-0/mobile-openclaw.md new file mode 100644 index 000000000..478d0fc9b --- /dev/null +++ b/docs/md/project/ai-agent-scaffold/part-0/mobile-openclaw.md @@ -0,0 +1,62 @@ +--- +title: 手机小龙虾,这次我比小米下手早! +lock: no +--- + +# 手机小龙虾,这次我比小米下手早! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +🦞 OpenClaw 太火了,火到什么程度,给 Mac Mini 干断货了,各行各业都找程序员👨🏻‍💻安装小龙虾,上门部署 OpenClaw 也成了生意!3月6日,深圳腾讯大厦,公益装机,也排起了长长的队伍。很多父母带着电脑过来,给自己娃的电脑装上小龙虾。 + +
+ +
+ +但当所有人都盯着 OpenClaw 小龙虾时,小米发布了 miclaw 手机版龙虾🦞,对标豆包手机、AutoGLM-Phone,首批支持小米17系列机型。 + +但,嘿嘿,不谋而合。我在看到和体验 OpenClaw 后,也想着,我的手机、平板,也可以来一个 Mobile-OpenClaw 呀!让这些电子设备,帮我完成一些工作不是美滋滋!`下文我会演示通过小龙虾手机的案例视频。` + +
+ +
+ +所以,在小米 miclaw 发布之前,我也已经开始设计实现手机版龙虾。并且还申请了一个手机版龙虾的域名,哈哈哈 死鬼,早有预谋哇! + +
+ +
+ +那手机版龙虾怎么做?🤔 它可以,通过 AI Agent 智能体,分析用户行为。之后下达一些列指令到应用设备的 Socket 网关端点上,完成功能指令动作。`小米 miclaw` 应该也是类似的,以后也可以体验下,但他可以原生态直接从系统上支持,控制整个米家的应用生态 👍🏻。 + +闲言少叙,接下来我们演示下运行效果。我的小米 Pad 7 Pro 也到位了。新的设备测试起来速度更快。😂 最近是一顿花钱呀,Mac Mini(电脑龙虾)、Pad 7 Pro(手机龙虾)、群辉 Nas(数据存储 + qwen3.5:9b)!**这个世界,终究是程序员的!** + +## 一、演示场景 + +小傅哥这里做了2个演示场景,一个是在小红书🍠发帖,一个是汽水音乐🎵自动看广告开会员。 + +### 1. 小红书 - 自动发帖 + +```java +打开小红书,点击下面的红色按钮,发送一个帖子。先写想法为,你好,世界,我是手机版小龙虾 🦞 想法编写后,点击【下一步】继续。在发帖页面,输入标题为;你好,世界,我是手机版小龙虾 🦞 内容为;我是 @小傅哥 正在测试开发的 MobileOpenClaw 欢迎关注我的动向!编写后,点击右上角【发布】。 +``` + +-- + +### 2. 汽水音乐 - 看广告 + +```java +打开汽水音乐,看广告获得VIP时长,操作流程; + +1. 点击右上角【免】按钮。 +2. 点击【免】后会进入后,右上角有倒计时播放广告,播放完成后显示为【领取成功】。这个时候点击【领取成功】,会有一个弹窗,点击弹窗的【领取奖励】,不要点x关闭。 +3. 完成领取后,继续看广告,重复步骤2,一直领取奖励,直至没有。 +``` + + + diff --git "a/docs/md/project/ai-agent-scaffold/part-1/\347\254\2541-1\350\212\202\357\274\232\350\204\232\346\211\213\346\236\266\351\234\200\346\261\202\345\210\206\346\236\220.md" "b/docs/md/project/ai-agent-scaffold/part-1/\347\254\2541-1\350\212\202\357\274\232\350\204\232\346\211\213\346\236\266\351\234\200\346\261\202\345\210\206\346\236\220.md" new file mode 100644 index 000000000..cf8a978c4 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-1/\347\254\2541-1\350\212\202\357\274\232\350\204\232\346\211\213\346\236\266\351\234\200\346\261\202\345\210\206\346\236\220.md" @@ -0,0 +1,46 @@ +--- +title: 【更】第1-1节:脚手架需求分析 +pay: https://t.zsxq.com/l3GtR +--- + +# 《AI Agent 脚手架》第1-1节:脚手架需求分析 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/uNA2K](https://t.zsxq.com/uNA2K) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +今天是我们 **《AI Agent 脚手架》** 项目学习的第1节课程,小傅哥会带着大家,以互联网公司正规的承接产品需求到开发部署上线的流程,带着大家以第一主人公视角的方式进行学习。这样既可以保证你学习到项目内容,也能了解到公司里正规的开发模式,以后进入到公司也可以很好的融入团队,承接项目需求。加油💪🏻!让我们开启新项目之旅! + +## 一、本章诉求 + +站在技术视角,分析 AI Agent 脚手架项目,这样一个产品功能需求的背景、诉求和目的。 + +首先,脚手架你可以理解为是一个工程模板框架,通过简单的配置即可完成初始化项目工程(IntelliJ IDEA)的搭建,并在工程内提供最为常用的基础服务,免去大家每次做新的项目,都要手动搭建工程的过程。 + +**那么,为什么叫技术视角?** + +在互联网公司中,项目通常分为两类:一类是源自产品业务的需求,另一类则是由研发驱动的纯技术项目(或由基架部门专门负责的技术产品)。后者的初衷在于解决研发在响应业务需求时,频繁且重复的工作负担,同时推动需求制定的统一标准化。 + +随着2025年起互联网公司纷纷通过AI赋能业务,推动各场景的效率提升,构建多样化的AI Agent智能体业务应用将成为常态。面对大量复杂的工程搭建需求,亟需一套统一的标准化框架,能够快速创建项目,显著降低用户的使用门槛和开发成本。 + +## 二、项目背景 + +为互联网企业构建业务场景下的 AI Agent 智能体,提供统一标准和便捷易用的脚手架。 + +**为什么要为创建脚手架?** + +回归本质,当前大规模语言模型(LLM)的使用,基本上是通过 HTTP 响应式接口进行对接。随着应用需求的增长,为了简化各自封装 AI HTTP 接口的复杂度,逐渐出现了以 AI SDK 方式进行对接的设计和实现,提升了开发效率和集成体验。 + +随后,AI 服务不仅仅局限于简单的接口调用,还引入了诸如 RAG(检索增强生成)知识库、Tool 工具(如 Function、MCP)等能力。同时,不同厂商的大模型(OpenAI、Google、智谱、阿里千问等)在接口和功能上存在差异,推动了类似 Spring AI 这类产品的诞生,帮助开发者更加便捷地统一调用和管理多样化的 AI 服务。 + +进入下一阶段,随着智能体(AI Agent)概念的兴起,开发者开始设计支持顺序执行、循环执行(分析与决策)、并行执行等复杂流程的智能体方案。Google 发布的 ADK(Agent Development Kit)智能体框架,标志着这一阶段的技术成熟和标准化趋势。 + +
+ +
+ +综上所述,构建一个完整的智能体,需要综合运用上述所有技术和能力。为了统一标准、提升易用性,就很需要搭建一套统一的 AI Agent 智能体开发脚手架,剔除重复且复杂的工作环节,帮助业务快速融合并高效开展智能体开发。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-1/\347\254\2541-2\350\212\202\357\274\232\347\263\273\347\273\237\346\236\266\346\236\204\350\256\276\350\256\241.md" "b/docs/md/project/ai-agent-scaffold/part-1/\347\254\2541-2\350\212\202\357\274\232\347\263\273\347\273\237\346\236\266\346\236\204\350\256\276\350\256\241.md" new file mode 100644 index 000000000..8eab70477 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-1/\347\254\2541-2\350\212\202\357\274\232\347\263\273\347\273\237\346\236\266\346\236\204\350\256\276\350\256\241.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第1-2节:系统架构设计 +pay: https://t.zsxq.com/sSD6u +--- + +# 《AI Agent 脚手架》第1-2节:系统架构设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/v8C8a](https://t.zsxq.com/v8C8a) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +对 AI Agent 智能体脚手架,进行应用技术选型和工程框架设计。 + +这个过程就等同于大家做一个新项目时,分析项目实现过程,以及过程中所需的技术能力,并对未知的技术点进行案例验证。待全部梳理完成后,则进行详细的方案设计。 + +>随着大家进入公司以后,在你承接一个新的项目/需求时,会大量的做这个事情,你会遇到很多没用过却需要使用的技术或者方案。当然,也会随着你的积累,你做这个事情会越来越熟练,越来越高效。而与之相反的,如果你之前的学习总是对照视频cv代码,最好一次就成功运行,害怕出错,不敢排查,没有一个思考和验证的过程,那么其实是一点经验也没积累。所以,要正视自己的学习方法。 + +## 二、架构设计 + +如图,整体简要架构设计; + +
+ +
+ +做这个事情,我们要思考,需要哪些东西来完成这样项目的开发。如下; \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-10\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-RunnerNode.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-10\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-RunnerNode.md" new file mode 100644 index 000000000..331e39a5c --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-10\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-RunnerNode.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第2-10节:装配域节点-RunnerNode +pay: https://t.zsxq.com/pEBw8 +--- + +# 《AI Agent 脚手架》第2-10节:装配域节点-RunnerNode + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/tMfj3](https://t.zsxq.com/tMfj3) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +把构建的智能体,填充到 InMemoryRunner 中,以及注册到 Spring 容器中。这样后续就可以使用 Spring 容器中的对象,进行 AI Agent 对话了。 + +>目前是把最后构建的 SequentialAgent 写入到 InMemoryRunner 中,后续会做成动态的,让配置任何的一个智能体都可以填充到 InMemoryRunner 中。 + +## 二、流程设计 + +如图,智能体构建完成后,则填充到 InMemoryRunner 中; + +
+ +
+ +- 从 SequentialAgentNode 节点,流转到 RunnerNode 节点。 +- 在 RunnerNode 节点,创建 InMemoryRunner 以及注册到 Spring 容器。这样可以任何一个地方获取到执行对象。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-11\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\345\212\240\350\275\275\344\275\277\347\224\250\351\252\214\350\257\201.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-11\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\345\212\240\350\275\275\344\275\277\347\224\250\351\252\214\350\257\201.md" new file mode 100644 index 000000000..7cc2ac0a5 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-11\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\345\212\240\350\275\275\344\275\277\347\224\250\351\252\214\350\257\201.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第2-11节:智能体加载使用验证 +pay: https://t.zsxq.com/CyuXv +--- + +# 《AI Agent 脚手架》第2-11节:智能体加载使用验证 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/kgrzQ](https://t.zsxq.com/kgrzQ) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +启动程序,加载智能体yml配置,完成智能体的装配。以及编写测试案例,验证加载智能体的使用。这部分的内容,会为后续开发对话服务做一个基础。让大家知道对话的过程需要创建会话和处理消息请求。 + +## 二、流程设计 + +如图,智能体全部节点构建完成后,在程序启动的时候进行加载; + +
+ +
+ +- 前面章节已经做好了全部的加载动作,到这可以把调用入口串联起来了。在程序启动的时候,完成自动化的加载处理。 +- 完成加载处理后,我们在做一个初步的验证操作,也就是使用这个智能体,通过这样的方式了解整个会话过程。 diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-12\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-RunnerNode.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-12\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-RunnerNode.md" new file mode 100644 index 000000000..7c73d00ba --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-12\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-RunnerNode.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第2-12节:增强装配-RunnerNode +pay: https://t.zsxq.com/jxbVS +--- + +# 《AI Agent 脚手架》第2-12节:增强装配-RunnerNode + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/Ls0hf](https://t.zsxq.com/Ls0hf) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +基础的智能体装配一个链路做完,并验证了会话的设计,让整个链路跑通。之后我们做一下增强设计操作。让整个智能体可以在后续适应更多的场景使用。 + +这一节,我们先来做下 RunnerNode 的增强,也就是前面提到的,用户如果只是配置一个基础的 Agent 不在配置其他的 loop(循环)、parallel(并行)、sequential(序列),就结束了。那么这里要怎么流转到 RunnerNode。 + +## 二、流程设计 + +如图,从 AgentWorkflowNode 流转到 RunnerNode 设计; + +
+ +
+ +- 首先,在有了前面的内容学习后,我们了解到 RunnerNode 会做 InMemoryRunner 的构建,而这个对象的构建是需要拿到 SequentialAgentNode 作为最后一个节点,构建的 SequentialAgent 写入到上下文,到 RunnerNode 节点使用。它的核心本质是拿到 InMemoryRunner 创建的时候,所需 Agent 智能体。 +- 之后,这个 Agent 智能体,不非得以一个固定的写死,而是可以在配置文件中添加以 runner 配置,指定装配到 runner 中的 agent 名称。之后在 RunnerNode 构建 InMemoryRunner 的时候,则从上下文通过智能体名称获取 agent 装配进来即可。 +- 最后,思考下,AgentWorkflowNode 什么时候流转,这个流转则是根据当前 agentWorkflows 是不为空了,为空则直接流转到 RunnerNode 即可。RunnerNode 会根据配置的关联 agentName 进行配置。`这部分看代码的yml文件会更加清晰` diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-13\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-AgentWorkflowNode.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-13\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-AgentWorkflowNode.md" new file mode 100644 index 000000000..144a44533 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-13\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-AgentWorkflowNode.md" @@ -0,0 +1,417 @@ +--- +title: 【更】第2-13:增强装配-AgentWorkflowNode +pay: https://t.zsxq.com/qjhUb +--- + +# 《AI Agent 脚手架》第2-13:增强装配-AgentWorkflowNode + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/AuQlk](https://t.zsxq.com/AuQlk) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +换种设计方式,增强 AgentWorkflowNode 流转能力,让 LoopAgentNode、ParallelAgentNode、SequentialAgentNode 不在负责判断流转,而是每个流程处理完毕后,都回到 AgentWorkflowNode 中进行流转决策。这样我们就可以组合出更为复杂的智能体编排。 + +## 二、流程设计 + +如图,增强 AgentWorkflowNode 流转能力,每个节点流转完都重新回到 AgentWorkflowNode 节点进行决策; + +
+ +
+ +- 左侧是旧版流程,LoopAgentNode、ParallelAgentNode、SequentialAgentNode,每个节点交叉流转。这次换到新版流程,所有的流转都由 AgentWorkflowNode 负责。这样会让三个功能 Agent 节点的职责更为清晰。 +- 这样,AgentWorkflowNode 就成了分发中心,三个 LoopAgentNode、ParallelAgentNode、SequentialAgentNode 智能体节点处理完业务后都回到 AgentWorkflowNode 即可。 + +## 三、功能实现 + +### 1. 工程结构 + +
+ +
+ +- 修改 LoopAgentNode、ParallelAgentNode、SequentialAgentNode,三个节点中的流转操作,都转移到 AgentWorkflowNode 处理。 +- 在 AgentWorkflowNode 中,要拿到当前 agentWorkflows 配置的列表中,步骤中第N个,把拿到的值作为当前的信息存储到上下文,之后流转到任何一个节点,只负责从上下文取到当前值即可。 + +### 2. 核心模块 + +#### 2.1 定义上下文 + +```java +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public static class DynamicContext { + + /** + * LLM API + */ + private OpenAiApi openAiApi; + + /** + * 对话模型 + */ + private ChatModel chatModel; + + /** + * 原子安全的递进步骤 + */ + private AtomicInteger currentStepIndex = new AtomicInteger(0); + + /** + * 当前的智能体 + */ + private AiAgentConfigTableVO.Module.AgentWorkflow currentAgentWorkflow; + + /** + * 智能体组 + */ + private Map agentGroup = new HashMap<>(); + + private Map dataObjects = new HashMap<>(); +} +``` + +- 首先,去掉 agentWorkflows 列表值,增加一个 currentAgentWorkflow 当前值。 +- 之后,添加 currentStepIndex 步骤完成一个,则迭代+1,从 agentWorkflows 渠道的当前对象存储到 currentAgentWorkflow,这样会更加方便判断。(`这个就是前面提到的另外的一个方案设计,小傅哥在项目里也给大家增加这种演进的迭代设计,可以让大家多一些积累`) + +#### 2.2 增强流转 + +```java +@Slf4j +@Service +public class AgentWorkflowNode extends AbstractArmorySupport { + + @Resource + private LoopAgentNode loopAgentNode; + + @Resource + private ParallelAgentNode parallelAgentNode; + + @Resource + private SequentialAgentNode sequentialAgentNode; + + @Resource + private RunnerNode runnerNode; + + @Override + protected AiAgentRegisterVO doApply(ArmoryCommandEntity requestParameter, DefaultArmoryFactory.DynamicContext dynamicContext) throws Exception { + log.info("Ai Agent 装配操作 - AgentWorkflowNode"); + + AiAgentConfigTableVO aiAgentConfigTableVO = requestParameter.getAiAgentConfigTableVO(); + List agentWorkflows = aiAgentConfigTableVO.getModule().getAgentWorkflows(); + + // 如果未配置 agentWorkflows 则直接流转到 RunnerNode + if (null == agentWorkflows || agentWorkflows.isEmpty() || dynamicContext.getCurrentStepIndex() >= agentWorkflows.size()) { + // 设置结果值 + dynamicContext.setCurrentAgentWorkflow(null); + // 路由下节点 + return router(requestParameter, dynamicContext); + } + + // 设置当前判断流程对象 + dynamicContext.setCurrentAgentWorkflow(agentWorkflows.get(dynamicContext.getCurrentStepIndex())); + + // 步骤值增加 + dynamicContext.addCurrentStepIndex(); + + return router(requestParameter, dynamicContext); + } + + @Override + public StrategyHandler get(ArmoryCommandEntity requestParameter, DefaultArmoryFactory.DynamicContext dynamicContext) throws Exception { + + AiAgentConfigTableVO.Module.AgentWorkflow currentAgentWorkflow = dynamicContext.getCurrentAgentWorkflow(); + + // 没有下一个节点,流转到结束节点 + if (null == currentAgentWorkflow) { + return runnerNode; + } + + String type = currentAgentWorkflow.getType(); + AgentTypeEnum agentTypeEnum = AgentTypeEnum.fromType(type); + + if (null == agentTypeEnum) { + throw new RuntimeException("agentWorkflow type is error!"); + } + + String node = agentTypeEnum.getNode(); + + return switch (node) { + case "loopAgentNode" -> loopAgentNode; + case "parallelAgentNode" -> parallelAgentNode; + case "sequentialAgentNode" -> sequentialAgentNode; + default -> runnerNode; + }; + + } + +} +``` + +- doApply 方法的核心是判断是否配置 agentWorkflows,以及不断的取值(类似for循环),是否取到了最后一个。如果是,则设置 `dynamicContext.setCurrentAgentWorkflow(null);` 并路由走。否则,从 agentWorkflows 获取当前步骤的对象并设置到上下文中,以及给步骤 +1 处理。 +- get 则负责节点流转,判断当前节点是否为null,为null则表示没有要处理的节点,直接进入 runnerNode 即可。如果不是 null 则按照不同的 node 流转到子 agent 智能体节点。 + +#### 2.3 子智能体节点 + +##### 2.3.1 LoopAgentNode + +```java +@Service +public class LoopAgentNode extends AbstractArmorySupport { + + @Override + protected AiAgentRegisterVO doApply(ArmoryCommandEntity requestParameter, DefaultArmoryFactory.DynamicContext dynamicContext) throws Exception { + log.info("Ai Agent 装配操作 - LoopAgentNode"); + + AiAgentConfigTableVO.Module.AgentWorkflow currentAgentWorkflow = dynamicContext.getCurrentAgentWorkflow(); + + List subAgents = dynamicContext.queryAgentList(currentAgentWorkflow.getSubAgents()); + + LoopAgent loopAgent = + LoopAgent.builder() + .name(currentAgentWorkflow.getName()) + .description(currentAgentWorkflow.getDescription()) + .subAgents(subAgents) + .maxIterations(currentAgentWorkflow.getMaxIterations()) + .build(); + + dynamicContext.getAgentGroup().put(currentAgentWorkflow.getName(), loopAgent); + + return router(requestParameter, dynamicContext); + } + + @Override + public StrategyHandler get(ArmoryCommandEntity requestParameter, DefaultArmoryFactory.DynamicContext dynamicContext) throws Exception { + return getBean("agentWorkflowNode"); + } + +} +``` + +- doApply 要修改为从上下文的 `dynamicContext.getCurrentAgentWorkflow()` 获取当前节点的数据,构建 Agent 之后路由。 +- get 则负责流转回 `getBean("agentWorkflowNode")` 让 agentWorkflowNode 继续负责节点的流转判断。 + +##### 2.3.2 ParallelAgentNode + +```java +@Service +public class ParallelAgentNode extends AbstractArmorySupport { + + @Override + protected AiAgentRegisterVO doApply(ArmoryCommandEntity requestParameter, DefaultArmoryFactory.DynamicContext dynamicContext) throws Exception { + log.info("Ai Agent 装配操作 - ParallelAgentNode"); + + AiAgentConfigTableVO.Module.AgentWorkflow currentAgentWorkflow = dynamicContext.getCurrentAgentWorkflow(); + + List subAgents = dynamicContext.queryAgentList(currentAgentWorkflow.getSubAgents()); + + ParallelAgent parallelAgent = + ParallelAgent.builder() + .name(currentAgentWorkflow.getName()) + .subAgents(subAgents) + .description(currentAgentWorkflow.getDescription()) + .build(); + + dynamicContext.getAgentGroup().put(currentAgentWorkflow.getName(), parallelAgent); + + return router(requestParameter, dynamicContext); + } + + @Override + public StrategyHandler get(ArmoryCommandEntity requestParameter, DefaultArmoryFactory.DynamicContext dynamicContext) throws Exception { + return getBean("agentWorkflowNode"); + } +} +``` + +- 代码修改方式同 `LoopAgentNode` + +##### 2.3.3 SequentialAgentNode + +```java +@Service +public class SequentialAgentNode extends AbstractArmorySupport { + + @Override + protected AiAgentRegisterVO doApply(ArmoryCommandEntity requestParameter, DefaultArmoryFactory.DynamicContext dynamicContext) throws Exception { + log.info("Ai Agent 装配操作 - SequentialAgentNode"); + + AiAgentConfigTableVO.Module.AgentWorkflow currentAgentWorkflow = dynamicContext.getCurrentAgentWorkflow(); + + List subAgents = dynamicContext.queryAgentList(currentAgentWorkflow.getSubAgents()); + + SequentialAgent sequentialAgent = + SequentialAgent.builder() + .name(currentAgentWorkflow.getName()) + .description(currentAgentWorkflow.getDescription()) + .subAgents(subAgents) + .build(); + + dynamicContext.getAgentGroup().put(currentAgentWorkflow.getName(), sequentialAgent); + + return router(requestParameter, dynamicContext); + } + + @Override + public StrategyHandler get(ArmoryCommandEntity requestParameter, DefaultArmoryFactory.DynamicContext dynamicContext) throws Exception { + return getBean("agentWorkflowNode"); + } + +} +``` + +- 代码修改方式同 `LoopAgentNode` + +## 四、测试验证 + +### 1. 修改配置文件 + +**parallel_research_app.yml** + +```java +ai: + agent: + config: + tables: + testAgent02: + app-name: ResearchAndSynthesisPipeline + agent: + agent-id: 100002 + agent-name: 测试智能体02 + agent-desc: 并行研究并汇总的智能体管道 + module: + ai-api: + base-url: https://apis.itedus.cn + api-key: sk-Sp2jx3yeq7x7HJ663bDc9bF0D34b4f609f833840271519B1 + completions-path: v1/chat/completions + embeddings-path: v1/embeddings + chat-model: + model: gpt-4.1 + tool-mcp-list: + - sse: + name: baidu-search + base-uri: https://appbuilder.baidu.com/v2/ai_search/mcp/ + sse-endpoint: sse?api_key=bce-v3/ALTAK-3zODLb9qHozIftQlGwez5/2696e92781f5bf1ba1870e2958f239fd6dc822a4 + request-timeout: 5000 + agents: + - name: RenewableEnergyResearcher + description: Researches renewable energy sources. + instruction: | + You are an AI Research Assistant specializing in energy. + Research the latest advancements in 'renewable energy sources'. + Use the Google Search tool provided. + Summarize your key findings concisely (1-2 sentences). + Output *only* the summary. + output-key: renewable_energy_result + - name: EVResearcher + description: Researches electric vehicle technology. + instruction: | + You are an AI Research Assistant specializing in transportation. + Research the latest developments in 'electric vehicle technology'. + Use the Google Search tool provided. + Summarize your key findings concisely (1-2 sentences). + Output *only* the summary. + output-key: ev_technology_result + - name: CarbonCaptureResearcher + description: Researches carbon capture methods. + instruction: | + You are an AI Research Assistant specializing in climate solutions. + Research the current state of 'carbon capture methods'. + Use the Google Search tool provided. + Summarize your key findings concisely (1-2 sentences). + Output *only* the summary. + output-key: carbon_capture_result + - name: SynthesisAgent + description: Combines research findings into a structured report. + instruction: | + You are an AI Assistant responsible for combining research findings into a structured report. + Your primary task is to synthesize the following research summaries, clearly attributing findings to their source areas. Structure your response using headings for each topic. Ensure the report is coherent and integrates the key points smoothly. + **Crucially: Your entire response MUST be grounded *exclusively* on the information provided in the 'Input Summaries' below. Do NOT add any external knowledge, facts, or details not present in these specific summaries.** + **Input Summaries:** + + * **Renewable Energy:** + {renewable_energy_result} + + * **Electric Vehicles:** + {ev_technology_result} + + * **Carbon Capture:** + {carbon_capture_result} + + **Output Format:** + + ## Summary of Recent Sustainable Technology Advancements + + ### Renewable Energy Findings + (Based on RenewableEnergyResearcher's findings) + [Synthesize and elaborate *only* on the renewable energy input summary provided above.] + + ### Electric Vehicle Findings + (Based on EVResearcher's findings) + [Synthesize and elaborate *only* on the EV input summary provided above.] + + ### Carbon Capture Findings + (Based on CarbonCaptureResearcher's findings) + [Synthesize and elaborate *only* on the carbon capture input summary provided above.] + + ### Overall Conclusion + [Provide a brief (1-2 sentence) concluding statement that connects *only* the findings presented above.] + + Output *only* the structured report following this format. Do not include introductory or concluding phrases outside this structure, and strictly adhere to using only the provided input summary content. + agent-workflows: + - type: parallel + name: ParallelWebResearchAgent + description: Runs multiple research agents in parallel to gather information. + sub-agents: + - RenewableEnergyResearcher + - EVResearcher + - CarbonCaptureResearcher + - type: sequential + name: ResearchAndSynthesisPipeline + description: Coordinates parallel research and synthesizes the results. + sub-agents: + - ParallelWebResearchAgent + - SynthesisAgent + runner: + agent-name: ResearchAndSynthesisPipeline +``` + +- agent-workflows 配置下 ParallelWebResearchAgent、ResearchAndSynthesisPipeline,这样可以测试循环处理。 +- runner 则配置 ResearchAndSynthesisPipeline 进行运行体转配。 + +### 2. 测试方法 + +```java +@Test +public void test_handlerMessage_03(){ + AiAgentRegisterVO aiAgentRegisterVO = applicationContext.getBean("100002", AiAgentRegisterVO.class); + String appName = aiAgentRegisterVO.getAppName(); + InMemoryRunner runner = aiAgentRegisterVO.getRunner(); + Session session = runner.sessionService() + .createSession(appName, "xiaofuge") + .blockingGet(); + Content userMsg = Content.fromParts(Part.fromText("你具备哪些能力")); + Flowable events = runner.runAsync("xiaofuge", session.id(), userMsg); + List outputs = new ArrayList<>(); + events.blockingForEach(event -> outputs.add(event.stringifyContent())); + log.info("测试结果:{}", JSON.toJSONString(outputs)); +} +``` + +```java +26-01-01.13:20:04.106 [main ] INFO test_handlerMessage_03 - 测试结果:["我可以帮助你查询和分析可再生能源领域的最新进展,包括太阳能、风能、生物能、地热能、海洋能等各类新能源技术的发展趋势、创新成果及政策动态。同时,我可以利用互联网搜索功能,快速获取最新科研成果、行业动态和相关数据,并将关键信息进行简明总结。","我是专注于电动汽车技术(electric vehicle technology)研究的AI助理,具备以下能力:\n\n1. **新技术检索与总结**:我能利用Google搜索等工具,快速检索最新的电动汽车技术发展、行业动态和科研突破,并进行简洁明了的总结。\n2. **趋势与前沿分析**:能够获取并分析行业趋势,例如电池创新、驱动系统进展、智能网联、电驱动新材料等领域的最新动向。\n3. **政策与市场信息搜集**:可查询全球各地与电动汽车相关的政策、市场增长、补贴政策等信息。\n4. **参考文献和数据追溯**:能帮助定位权威期刊、会议论文、专利等技术文档,提供学术研究支持。\n5. **技术对比与评估**:可对比不同品牌、技术路径或产品,分析其优劣及市场应用前景。\n6. **简明交流和摘要能力**:围绕“电动汽车技术”,可将复杂技术信息压缩为1-2句话的核心摘要,便于快速理解。\n\n如果你有特定方向的需求(如电池、驱动控制、充电技术等),我也能定向进行最新信息搜索和研究。","我具备以下能力,专注于碳捕集(carbon capture)相关的研究与信息获取:\n\n1. 实时网络检索:我可以通过专业搜索工具实时获取最新关于碳捕集方法、技术进展、应用案例、政策法规等公开信息。\n2. 资料梳理与总结:对检索到的信息快速提炼要点,进行结构化、简明扼要的总结,便于决策与参考。\n3. 技术分类与比较:能够对比不同类型的碳捕集技术(如直接空气捕集、点源捕集、碳矿化、生物碳捕集等)的原理、优缺点和应用现状。\n4. 最新动态追踪:跟踪全球范围内碳捕集领域的最新动态、前沿研究和重大项目进展。\n5. FAQ解答:针对碳捕集相关的常见问题(如成本、能效、行业难点等)进行专业、准确回答。\n\n如需获取某一具体问题或领域的最新信息,请直接告诉我!","## Summary of Recent Sustainable Technology ... +``` + +- 运行后可以看到执行的结果。也表示了,我们的装配方式是没问题的。 + +## 五、读者作业 + +- 简单作业:完成本节功能的编写,理解此处的架构设计。对于节点的流转,打开思路,之后活学活用。 +- 复杂作业:尝试配置一个多层嵌套的智能体,来验证这样的装配。 + diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-14\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-\346\234\254\345\234\260mcp.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-14\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-\346\234\254\345\234\260mcp.md" new file mode 100644 index 000000000..64b058b53 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-14\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-\346\234\254\345\234\260mcp.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第2-14节:增强装配-本地mcp +pay: https://t.zsxq.com/KYNyM +--- + +# 《AI Agent 脚手架》第2-14节:增强装配-本地mcp + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/OoPsy](https://t.zsxq.com/OoPsy) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在用户使用 ai agent 脚手架的时候,会有诉求扩展一些自己实现的 mcp 服务。比如,做一些智能体客服场景,或者公司内部的系统巡检场景,这些场景并没有提供统一的 mcp 服务,则可能都要自己扩展,扩展后装配到整个智能体中。 + +同时因为引入了多种类型的 mcp 装配,sse、stdio 又加上这种本地自己实现的,所以这块还要做一些策略处理。 + +## 二、流程设计 + +如图,增强 mcp 服务装配能力设计; + +
+ +
+ +- ChatModelNode 装配 MCP 主要是 sse、stdio 两种方式,通过 if···else 判断的方式进行处理。但现在又要扩展新的方式装配,则不适合继续编写 if···else 代码,而是要通过类来拆分一个大的方法。 +- 这块会使用到工厂 + 策略接口来扩展实现,并区分出 mcp 客户端、服务端。客户端负责策略装配,服务端负责用户自己扩展新的 mcp 服务。 diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-15\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-\345\233\236\350\260\203plugin.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-15\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-\345\233\236\350\260\203plugin.md" new file mode 100644 index 000000000..c5660f157 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-15\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-\345\233\236\350\260\203plugin.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第2-15节:增强装配-回调plugin +pay: https://t.zsxq.com/U1p49 +--- + +# 《AI Agent 脚手架》第2-15节:增强装配-回调plugin + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/tTIdy](https://t.zsxq.com/tTIdy) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +为 InMemoryRunner 运行体扩展 Plugin 插件能力,允许用户在智能体执行的各个阶段获取到上下文信息,并做出一定的监控、调整、治理的动作。 + +如果你有学习过星球「码农会锁」的扳手工程,在设计模式框架章节,有一个 `applyBefore`、`applyAfter`、`applyAfterError` 的处理,它可以让你在使用的阶段做拦截或者打印最后的结果。这些设计都是相同的,所以说学编程,最后都是学的思想。语言就像你的盗抢棍棒,斧钺钩叉,思想才是你的一招一式。 + +## 二、流程设计 + +如图,增强运行体插件配置,可以在各个节点埋入钩子; + +
+ +
+ +- Runner 的 Plugin 通过回调钩子在 AI Agent 运行流程生命周期的各个阶段执行。包括;用户输入信息、调用模型、调用工具、智能体执行等步骤中。这一工具,可以用在日志记录、性能分析、步骤调试、策略执行(权限)、监控对接(普罗米修斯)、请求或响应的调整(如敏感词的处理)等。 +- 这些操作的步骤,就是图上的各个阶段的节点,可以被采集到。就像你设置的任何一个智能体 Agent 都可以拿到它的运行信息,甚至你可以在上下文中做一些拦截操作。 diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-16\350\212\202\357\274\232fix-\345\244\232\346\250\241\346\200\201\350\203\275\345\212\233\344\275\277\347\224\250.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-16\350\212\202\357\274\232fix-\345\244\232\346\250\241\346\200\201\350\203\275\345\212\233\344\275\277\347\224\250.md" new file mode 100644 index 000000000..a2a9a4a3c --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-16\350\212\202\357\274\232fix-\345\244\232\346\250\241\346\200\201\350\203\275\345\212\233\344\275\277\347\224\250.md" @@ -0,0 +1,123 @@ +--- +title: 【更】第2-16节:fix-多模态能力使用 +pay: https://t.zsxq.com/wF32j +--- + +# 《AI Agent 脚手架》第2-16节:fix-多模态能力使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/XqlKu](https://t.zsxq.com/XqlKu) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +为接入 Google ADK 的 Spring AI 提供图片识别的多模态能力。 + +这本应该是一个简单直接使用的功能,但在 `Google ADK 0.5.0`、`Spring AI 1.1.0` 版本上,它还是一个隐藏待处理的 Bug,小傅哥已于 2026年1月6日,提交 [https://github.com/google/adk-java/issues/705](https://github.com/google/adk-java/issues/705) + +Google ADK Java 开发工程师,在 2026年1月7日,处理提交了修复代码,应该会在后续版本更新。 + +不过,对于我们学习来说,这并不是坏事。我们可以借助这样的问题点,深入理解 Google ADK 和 Spring AI 的对接,以及学习如何排查这样的报错。最终,鉴于 Google ADK 版本更新周期,我们目前先在程序中打一个”补丁“实现。 + +## 二、发现问题 + +### 步骤1,功能诉求 + +```java +@Test +public void test_handlerMessage_03() throws IOException { + AiAgentRegisterVO aiAgentRegisterVO = applicationContext.getBean("100003", AiAgentRegisterVO.class); + + String appName = aiAgentRegisterVO.getAppName(); + InMemoryRunner runner = aiAgentRegisterVO.getRunner(); + Session session = runner.sessionService() + .createSession(appName, "xiaofuge") + .blockingGet(); + + Content userMsg = Content.fromParts(Part.fromText("这是什么图片?"), + Part.fromBytes(imageResource.getContentAsByteArray(), MimeTypeUtils.IMAGE_PNG_VALUE)); + + Flowable events = runner.runAsync("xiaofuge", session.id(), userMsg); + + List outputs = new ArrayList<>(); + events.blockingForEach(event -> outputs.add(event.stringifyContent())); + log.info("测试结果:{}", JSON.toJSONString(outputs)); +} +``` + +
+ +
+ +- 在新的章节,需要验证多模态能力的时候,传入了一个图片 byte,但无论如何修改运行,都只是提示无法识别图片。 +- 这个时候猜想,是不是 InMemoryRunner 构建问题,或者 Agent 实例化参数问题。所以,决定把问题缩小,单独验证 Google ADK + Spring AI。 + +### 步骤2,分块验证 + +```java +@Slf4j +public class SpringAiTest { + + @SneakyThrows + public static void main(String[] args) { + + OpenAiApi openAiApi = OpenAiApi.builder() + .baseUrl("https://apis.itedus.cn") + .apiKey("sk-2GQTYTNoQSs7qizlE9F00bD84d254c2994D44d6410B0Ac8f") + .completionsPath("v1/chat/completions") + .embeddingsPath("v1/embeddings") + .build(); + + ChatModel chatModel = OpenAiChatModel.builder() + .openAiApi(openAiApi) + .defaultOptions(OpenAiChatOptions.builder() + .model("gpt-4.1") + .build()) + .build(); + + LlmAgent agent = LlmAgent.builder() + .name("test") + .description("Chess coach agent") + .model(new SpringAI(chatModel)) + .instruction(""" + You are a knowledgeable chess coach + who helps chess players train and sharpen their chess skills. + """) + .build(); + + InMemoryRunner runner = new InMemoryRunner(agent); + + Session session = runner + .sessionService() + .createSession("test", "xiaofuge") + .blockingGet(); + + URL resource = Thread.currentThread().getContextClassLoader().getResource("dog.png"); + + byte[] bytes; + assert resource != null; + try (InputStream inputStream = resource.openStream()) { + bytes = inputStream.readAllBytes(); + } + + List parts = new ArrayList<>(); + parts.add(Part.fromText("这是什么图片")); + parts.add(Part.fromBytes(bytes, MimeTypeUtils.IMAGE_PNG_VALUE)); + + Content content = Content.builder().role("user").parts(parts).build(); + + Flowable events = runner.runAsync("xiaofuge", session.id(), + content + ); + + System.out.print("\nAgent > "); + events.blockingForEach(event -> System.out.println(event.stringifyContent())); + } + +} +``` + +- 效果;运行结果依然是识别不了,告诉我要上传图片,它才能识别。 +- 猜想;这说明单独按照官网案例构建 Agent 并测试依然不行,再细化验证。 diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-17\350\212\202\357\274\232\344\274\232\350\257\235\346\234\215\345\212\241\346\216\245\345\217\243\345\256\236\347\216\260-service.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-17\350\212\202\357\274\232\344\274\232\350\257\235\346\234\215\345\212\241\346\216\245\345\217\243\345\256\236\347\216\260-service.md" new file mode 100644 index 000000000..fdf87ec66 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-17\350\212\202\357\274\232\344\274\232\350\257\235\346\234\215\345\212\241\346\216\245\345\217\243\345\256\236\347\216\260-service.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第2-17节:会话服务接口实现-service +pay: https://t.zsxq.com/lxqLY +--- + +# 《AI Agent 脚手架》第2-17节:会话服务接口实现-service + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/vLd8O](https://t.zsxq.com/vLd8O) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在智能体完成装配以后,接下来我们要做的就是提供标准的会话服务接口能力,使用智能体。这包括了,所装配智能体的列表(agentId)、会话的创建、消息的处理(同步/异步)。 + +## 二、流程设计 + +如图,会话接口服务所在分层; + +
+ +
+ +- 首先,在 Agent 装配后,是要对外提供服务,服务由 trigger 分层下的 http 接口提供。这个 trigger 也就是 mvc 分层里的 controller,但因为在微服务下,接口、消息、定时任务,都是一种触发行为。触发可以理解为,别人打你,可以用拳头,可以用石头,还分可以榔头,这些都可以抽象为一种名称,触发。所以在 ddd 分层下,增加了 trigger 触发器层。 +- 之后,这一节我们先来实现 service 服务层,也就是在 domain 领域下,agent 里除了做装配实现,还要做一层会话处理。在 service 实现后,后面就是包装 trigger 层的对外接口。 diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-18\350\212\202\357\274\232\344\274\232\350\257\235\346\234\215\345\212\241\346\216\245\345\217\243\345\256\236\347\216\260-trigger.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-18\350\212\202\357\274\232\344\274\232\350\257\235\346\234\215\345\212\241\346\216\245\345\217\243\345\256\236\347\216\260-trigger.md" new file mode 100644 index 000000000..61a2e5ca2 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-18\350\212\202\357\274\232\344\274\232\350\257\235\346\234\215\345\212\241\346\216\245\345\217\243\345\256\236\347\216\260-trigger.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第2-18节:会话服务接口实现-trigger +pay: https://t.zsxq.com/IY7j3 +--- + +# 《AI Agent 脚手架》第2-18节:会话服务接口实现-trigger + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/leFf4](https://t.zsxq.com/leFf4) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +对 domain 领域层的 service 服务,进行 trigger 包装,提供 http 接口能力。这样,后续就可以包装页面到调用接口,完整整个会话服务的操作。 + +## 二、流程设计 + +如图,会话接口服务所在分层; + +
+ +
+ +- 如图,这一节主要是完成 trigger http 接口部分的能力处理,对外提供;`智能体列表`、`创建会话 session`、`发起会话(流式/非流式)` +- 注意,在实际业务开发中,接口的提供方式比较多的,也有可能提供 rpc 接口,还有可能对接口进行扩展,包装更多业务流程进去。那么这个时候,可以考虑增加 case 编排层(一个新的module模块),处理复杂流程业务。case 层的能力,主要就是分摊 trigger 层下的压力,让对外的接口层,更加轻量,只是做一些包装即可。 diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-19\350\212\202\357\274\232\344\274\232\350\257\235\346\234\215\345\212\241\346\216\245\345\217\243\345\257\271\346\216\245-ui.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-19\350\212\202\357\274\232\344\274\232\350\257\235\346\234\215\345\212\241\346\216\245\345\217\243\345\257\271\346\216\245-ui.md" new file mode 100644 index 000000000..d2a1a11ce --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-19\350\212\202\357\274\232\344\274\232\350\257\235\346\234\215\345\212\241\346\216\245\345\217\243\345\257\271\346\216\245-ui.md" @@ -0,0 +1,38 @@ +--- +title: 【更】第2-19节:会话服务接口对接-ui +pay: https://t.zsxq.com/gd2Ue +--- + +# 《AI Agent 脚手架》第2-19节:会话服务接口对接-ui + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/38RBe](https://t.zsxq.com/38RBe) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +开发脚手架接口对接 UI,简单展示服务端能力。 + +在业务场景中,使用 AI Agent 脚手架,构建项目后所提供的服务接口,一般是附带着业务应用场景能力的。这些提供的能力接口,可以对接到客服、巡检、数据、量化、风控等各类场景使用。 + +## 二、对接效果 + +这里小傅哥选择使用了 AI IDE 工具,设计 UI 界面,我们可以描述对接话术,让 AI 帮我们设计一套页面。`关于页面设计的话术提示词,我已经放到工程下。` + +### 1. 登录页 + +
+ +
+ +- 登录页,有一个演示账号,可以填充登录。 + +### 2. 对话页 + +
+ +
+ +- 对话,可以填写信息,发送请求。在一个会话ID下,它是可以记录历史上下文信息的。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-1\350\212\202\357\274\232\345\267\245\347\250\213\345\210\235\345\247\213\345\214\226\345\210\233\345\273\272.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-1\350\212\202\357\274\232\345\267\245\347\250\213\345\210\235\345\247\213\345\214\226\345\210\233\345\273\272.md" new file mode 100644 index 000000000..5fef2d62e --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-1\350\212\202\357\274\232\345\267\245\347\250\213\345\210\235\345\247\213\345\214\226\345\210\233\345\273\272.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第2-1节:工程初始化创建 +pay: https://t.zsxq.com/6z3Q1 +--- + +# 《AI Agent 脚手架》第2-1节:工程初始化创建 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/OxC6a](https://t.zsxq.com/OxC6a) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +搭建 AI Agent Scaffold 脚手架项目工程,并 push 代码到课程仓库,以及演示关于 Git 使用,方便后续学习使用。 + +## 二、环境配置 + +- JDK 17 +- Maven 3.8.x - [Maven 教程](https://bugstack.cn/md/road-map/maven.html) +- IntelliJ IDEA 社区版(免费) [IntelliJ IDEA 教程](https://bugstack.cn/md/road-map/intellij-idea.html) +- Git - 安装后会配置到 IntellJ IDEA 这样才能向服务端推送或者拉取代码。学习后可以知道怎么拉取、提交和比对代码。Git 教程:[https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) + +
+ +
+ +>相关软件,在星球课程入口,编程环境中提供了下载链接。编程环境:[https://t.zsxq.com/19Rnk98M0](https://t.zsxq.com/19Rnk98M0) - 提供了已经配置好镜像的 maven 方便直接使用。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-20\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-skills.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-20\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-skills.md" new file mode 100644 index 000000000..d11452c49 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-20\350\212\202\357\274\232\345\242\236\345\274\272\350\243\205\351\205\215-skills.md" @@ -0,0 +1,34 @@ +--- +title: 【更】第2-20节:增强装配-skills +pay: https://t.zsxq.com/7uVom +--- + +# 《AI Agent 脚手架》第2-20节:增强装配-skills + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/7uVom](https://t.zsxq.com/7uVom) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +为对话模型(ChatModelNode)的装配,添加 skills 技能配置,允许用户使用 resource 资源和映射的 path 路径的方式,在构建 AI Agent 智能体时,完成装配。 + +skill 是什么?它像是一本技能书📚,把一阳指(mcp/py/shell/js)和狮吼功(prompt)合成了一整招。缩短了从用户把提示词发给AI客户端,进行分析,决策,再到 mcp 执行的过程,让诉求直达结果,token 减少了,幻觉减少了! + +随着 LLM 大模型能力的不断提升,并与 RAG、MCP、Skill 的结合,使得 Agent 智能体与完整的计算机环境(Computer/Phone)交互成为可能。这个过程中,一方面不断产生新的技术方案,一方面又不断的优化设计。就像 Skill 的出现,它不是替代 MCP,而是更准确的使用 MCP 能力。 + +## 二、技术介绍(skills 和 prompt + mcp) + +如图,演示了一段 skill 的编写案例; + +
+ +
+ +- 场景:案例中体现的是,对电脑性能检测后,用一段下达命令的方式,告知用户如何优化电脑性能。 +- 重点:如果不使用 skill,则需要描述一大段话术,让 ai 自己完成对用户场景诉求的分析,并按照步骤来调用对应的各个 mcp 服务(没有 skill 则需要把各类内容,都包装为 mcp 服务)。这个过程是比较消耗 token 的,也可能有不小的幻觉。现在有了 skill,我们可以适当的完整的写一段诉求文档,文档里嵌入可执行的脚本/mcp服务,让执行更可靠。 +- 用途:那都有哪些场景可以写 skill 技能书呢?🤔 如;互联网公司里的系统巡检,在接收到报警日志后,拿到一个报警的系统和接口信息,之后用 skill 技能书,分别采集出对应的系统配置、上线日志、数据库/缓存情况、运营操作记录、全链路监控上的接口耗时情况等。之后在根据我们日常排查问题的时候经验,编写过程步骤,这样会更加准确。 + +>所以,不是 skill、mcp 谁替代谁,而是 skill 对 mcp 进行增强,让 ai 执行时更加可靠。 diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-2\350\212\202\357\274\232Api\345\212\237\350\203\275\346\265\213\350\257\225.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-2\350\212\202\357\274\232Api\345\212\237\350\203\275\346\265\213\350\257\225.md" new file mode 100644 index 000000000..f19a2cb7e --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-2\350\212\202\357\274\232Api\345\212\237\350\203\275\346\265\213\350\257\225.md" @@ -0,0 +1,44 @@ +--- +title: 【更】第2-2节:Api功能测试 +pay: https://t.zsxq.com/uTPE2 +--- + +# 《AI Agent 脚手架》第2-2节:Api功能测试 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/q2Zug](https://t.zsxq.com/q2Zug) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过功能测试代码,使用 `Spring AI`、`LangChain4J`、`Google ADK` 框架对接 AI 服务,完成功能验证。为后续做 AI Agent 智能体脚手架做准备。 + +## 二、框架介绍 + +### 1. Spring AI + +官网:[https://docs.spring.io/spring-ai/reference/1.0/index.html](https://docs.spring.io/spring-ai/reference/1.0/index.html) + +Spring AI 项目旨在简化集成人工智能功能的应用程序的开发,避免不必要的复杂性。 + +该项目从 LangChain 和 LlamaIndex 等知名的 Python 项目中汲取灵感,但 Spring AI 并非这些项目的直接移植。该项目创立的初衷是,下一代生成式人工智能应用不仅面向 Python 开发者,还将广泛应用于多种编程语言。 + +> 在使用体验下 Spring AI 可以更好的结合 Spring 整个框架,整个接口的定义形式和使用方式,会更符合你对于 Spring 整体的使用习惯。 + +### 2. LangChain4J + +官网:[https://docs.langchain4j.info/](https://docs.langchain4j.info/) + +LangChain4j 的目标是简化将 LLM 集成到 Java 应用程序中的过程。 + +LangChain4j 始于 2023 年初 ChatGPT 热潮期间。 我们注意到与众多 Python 和 JavaScript LLM 库和框架相比,缺少 Java 对应物, 我们必须解决这个问题! 虽然我们的名字中有"LangChain",但该项目是 LangChain、Haystack、 LlamaIndex 和更广泛社区的想法和概念的融合,并加入了我们自己的创新。 + +### 3. Google ADK + +官网:[https://google.github.io/adk-docs/get-started/java/#example](https://google.github.io/adk-docs/get-started/java/#example) + +代理开发工具包 (ADK) 是一个灵活且模块化的框架,用于开发和部署 AI 代理 。ADK 针对 Gemini 和 Google 生态系统进行了优化,但它与模型和部署方式无关,并且是为……而构建的。 与其他框架的兼容性 。ADK 的设计宗旨是让代理开发更像软件开发,使开发人员能够更轻松地创建、部署和编排从简单任务到复杂工作流程的各种代理架构。 + +> 在使用体验上 Google ADK 自身是可以完整构建全部智能体的,但它又兼容了 Spring AI、LangChain4j 两个框架,让这2个框架负责 AI 对接,而 Google ADK 负责 Agent 的编排和插件的处理,这样使用起来非常不错。 diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-3\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\351\205\215\347\275\256\350\241\250\350\256\276\350\256\241.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-3\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\351\205\215\347\275\256\350\241\250\350\256\276\350\256\241.md" new file mode 100644 index 000000000..cd82e542a --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-3\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\351\205\215\347\275\256\350\241\250\350\256\276\350\256\241.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第2-3节:智能体配置表设计 +pay: https://t.zsxq.com/hCtFC +--- + +# 《AI Agent 脚手架》第2-3节:智能体配置表设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/bHRBx](https://t.zsxq.com/bHRBx) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +定义使用工程 YML 文件方式,配置的通用的智能体配置表。允许用户在使用脚手架创建完成智能体后工程后,通过 YML 配置出自己需要的智能体,在结合业务场景做对应的衔接开发。 + +如果,你在思考把 YML 的配置表抽取到数据库中实现,那么就是另外一套项目[《DeepSeek RAG、MCP、Ai Agent 智能体》](https://t.zsxq.com/GwNZp) 通过前端页面拖拉拽的方式进行配置使用。感兴趣也可以扩展学习。 + +## 二、流程设计 + +如图,智能体配置表结构; + +
+ +
+ +- 首先,一个智能体配置所需的最基本信息包括;应用名称、智能体描述、智能体模块,主要的组件类配置都在智能体模块下,AiApi 负责对接 AI 接口,ChatModel 负责模型创建(也会把 AiApi 对接),之后还要创建出 MCP 工具。 +- 之后,就是对于单一智能体(Agent)的构建,这里可以顺序的创建出很多的智能体,之后到 AgentWorkflow 进行编排,构建出一个完整的智能体。**这部分映射了【第2-2节:系统架构设计】中的流程设计以及 YML 设计** \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-4\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\347\273\223\346\236\204\345\214\226\345\256\232\344\271\211.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-4\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\347\273\223\346\236\204\345\214\226\345\256\232\344\271\211.md" new file mode 100644 index 000000000..c37aec914 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-4\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\347\273\223\346\236\204\345\214\226\345\256\232\344\271\211.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第2-4节:装配域结构化定义 +pay: https://t.zsxq.com/pwR4K +--- + +# 《AI Agent 脚手架》第2-4节:装配域结构化定义 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/3GFbQ](https://t.zsxq.com/3GFbQ) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过`单一职责`、`规则树(组合模式)`、`工厂`、`上下文对象`、`泛型`等设计模式手段,结合领域驱动设计思想,定义智能体装配服务结构。 + +## 二、流程设计 + +如图,智能体装配结构设计; + +
+ +
+ +- 首先,最上面的是规则树(组合模式)的设计模块框架,用于各项节点流转实现,如;RootNode、AiApiNode、ChatModelNode 等。 +- 之后,定义了单一职责的 IArmoryService 装配服务接口,并通过工厂管理节点衔接服务,以及定义上下文对象。这个上下文对象,会在各个节点间记录数据并流转使用。 + +> 本节小傅哥先带着大家把功能实现框架结构定义出来,让大家厘清关于这样的框架结构设计(这是一个非常通用的手段)。之后在进行各个节点的编码实现。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-5\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-AiApiNode.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-5\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-AiApiNode.md" new file mode 100644 index 000000000..b46113981 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-5\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-AiApiNode.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第2-5节:装配域节点-AiApiNode +pay: https://t.zsxq.com/wetnI +--- + +# 《AI Agent 脚手架》第2-5节:装配域节点-AiApiNode + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/D3LGP](https://t.zsxq.com/D3LGP) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +从本节装配 AI Agent 智能体各个节点开始,每一节都只负责一小部分独立内容的实现,方便大家可以在回顾的时候,直接找到对应章节进行查看。 + +这一节装配第一个节点 AiApiNode,它的目的是和 AI 接口,建立请求连接。 + +## 二、流程设计 + +如图,智能体装配中 AiApiNode 部分; + +
+ +
+ +AiApiNode 节点的装配,使用的是 Spring AI 框架提供的构建方法。在咱们课程前面也讲解过 LangChain4j,后面如果你想锻炼,也可以更换下,完全都是可以到 Agent 装配的时候做兼容的。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-6\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-ChatModelNode.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-6\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-ChatModelNode.md" new file mode 100644 index 000000000..b738c7cc5 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-6\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-ChatModelNode.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第2-6节:装配域节点-ChatModelNode +pay: https://t.zsxq.com/mnTej +--- + +# 《AI Agent 脚手架》第2-6节:装配域节点-ChatModelNode + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/X0HRO](https://t.zsxq.com/X0HRO) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +这一节从 AiApiNode 节点流转 ChatModelNode 模型对话节点的实例化操作,以及实现 ChatModelNode 装配操作。 + +## 二、流程设计 + +如图,智能体装配中 ChatModelNode 部分; + +
+ +
+ +- 首先,这一个节点是从 AiApiNode 处理完流程,流转过来的节点。节点的流转是在 doApply 处理完成后,执行 router 路由方法。路由方法会调用当前实现类的 get 方法,获取下一个要执行节点。通过这样的方式分离逻辑区和流转区,可以让代码更好维护。 +- 之后,到了 ChatModelNode 使用 Spring AI 处理模型构建。构建前还需要从上下文获取 AiApiNode 中关于 OpenAiApi 实例化对象,用于填充到 ChatModel 实例化中。 +- 此外,ChatModelNode 还要构建关于 MCP 的构建,这里是把 MCP 填充到模型中使用,这一套都是基于 Spring AI 框架处理的。当然,除了可以使用 Spring AI 也可以使用 langchain4j、google adk 来处理关于 mcp 的部分。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-7\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-AgentNode.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-7\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-AgentNode.md" new file mode 100644 index 000000000..9d5236a05 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-7\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-AgentNode.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第2-7节:装配域节点-AgentNode +pay: https://t.zsxq.com/908Fo +--- + +# 《AI Agent 脚手架》第2-7节:装配域节点-AgentNode + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/PFBNl](https://t.zsxq.com/PFBNl) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +这一节从 ChatModelNode 节点流转 AgentNode 节点,做智能体 LlmAgent 的实例化操作,以及实现 AgentNode 装配操作。 + +## 二、流程设计 + +如图,智能体装配中,AgentNode 部分; + +
+ +
+ +- 首先,AgentNode 节点是由 ChatModelNode 节点流转过来的,每个节点的流转都是类似的操作,处理完业务功能后,则路由到下一个节点继续完成其他业务。 +- 之后,AgentNode 是一个多 LlmAgent 装配的过程,我们在配置智能体的时候,有一些复杂的场景则要多个 LlmAgent 分别做不同的事情,来完成一个整体的流程。所以这类的智能体是多个 LlmAgent 配置。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-8\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-AgentWorkflowNode.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-8\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-AgentWorkflowNode.md" new file mode 100644 index 000000000..60bf9e157 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-8\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-AgentWorkflowNode.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第2-8节:装配域节点-AgentWorkflowNode +pay: https://t.zsxq.com/3mjk1 +--- + +# 《AI Agent 脚手架》第2-8节:装配域节点-AgentWorkflowNode + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/3EdmN](https://t.zsxq.com/3EdmN) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在 AiApi、ChatModel、Agent 装配完成后,接下来要进入到智能体工作流编排的操作了,他们的组合可能是 LoopAgent 把几个 LlmAgent 作为子 Agent,也有可能是 ParallelAgent 把几个 LlmAgent 作为并行,最后又会被 SequentialAgent 串行使用。 + +正是因为这块能有多重组合,所以,我们才需要有一个流转判断器的节点,让这些节点的执行可以串联起来。 + +## 二、流程设计 + +如图,智能体装配中,AgentWorkflowNode 部分; + +
+ +
+ +- 首先,这一节重点设计关于 AgentWorkflowNode 到其他3个节点节点的流转操作,包括;LoopAgent、ParallelAgentNode、SequentialAgentNode,并最终由 SequentialAgentNode 作为结束,也就是最后是一个序列化的执行。 +- 注意,LoopAgent、ParallelAgentNode、SequentialAgentNode 这三个节点,本节只关注他们的流转操作,后续在做具体的节点功能实现。 +- 另外,还有一种可能,就是单一智能体 LlmAgent 直接作为结束,没有配置 SequentialAgentNode 包装一层序列化执行,后续在扩展这部分。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-9\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-Loop\343\200\201Parallel\343\200\201Sequential.md" "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-9\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-Loop\343\200\201Parallel\343\200\201Sequential.md" new file mode 100644 index 000000000..4c8f4ef6a --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-2/\347\254\2542-9\350\212\202\357\274\232\350\243\205\351\205\215\345\237\237\350\212\202\347\202\271-Loop\343\200\201Parallel\343\200\201Sequential.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第2-9节:装配域节点-Loop、Parallel、Sequential +pay: https://t.zsxq.com/t29lj +--- + +# 《AI Agent 脚手架》第2-9节:装配域节点-Loop、Parallel、Sequential + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/6NKGA](https://t.zsxq.com/6NKGA) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +从 AgentWorkflowNode 开始,要进入 LoopAgent、ParallelAgent、SequentialAgent,这几个节点都是类似的,我们一起来处理下。 + +## 二、流程设计 + +如图,智能体装配中,LoopAgentNode、ParallelAgentNode、SequentialAgentNode 部分; + +
+ +
+ +- LoopAgentNode - 处理循环操作,如一个用户请求,要进行分析、执行、检测,也可以是一段git提交的代码,进行 diff 获取差异,检索代码匹配召回、做出执行review计划,后面在依次执行分析。这些都是可以做循环处理的。 +- ParallelAgentNode - 处理并行操作,如我们有一些场景,需要同步并行的一起完成数据处理任务,多条链路一起完成数据的获取、分析和决策,这样可以对复杂的流程显著的提高执行效率。 +- SequentialAgentNode - 处理串行操作,主要用于编排子智能体,和 loop 循环、parallel 并行,组合出复杂的智能体流程。当然,你也可以使用 loop 组合 sequence 或者 parallel 等处理过程。 diff --git "a/docs/md/project/ai-agent-scaffold/part-3/\347\254\2543-1\350\212\202\357\274\232Maven\350\204\232\346\211\213\346\236\266\351\205\215\347\275\256.md" "b/docs/md/project/ai-agent-scaffold/part-3/\347\254\2543-1\350\212\202\357\274\232Maven\350\204\232\346\211\213\346\236\266\351\205\215\347\275\256.md" new file mode 100644 index 000000000..d21e779af --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-3/\347\254\2543-1\350\212\202\357\274\232Maven\350\204\232\346\211\213\346\236\266\351\205\215\347\275\256.md" @@ -0,0 +1,34 @@ +--- +title: 【更】第3-1节:Maven脚手架配置 +pay: https://t.zsxq.com/jnqmk +--- + +# 《AI Agent 脚手架》第3-1节:Maven脚手架配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/EwCTb](https://t.zsxq.com/EwCTb) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +对 ai-agent-scaffold 脚手架工程配置 maven-archetype-plugin,生成工程脚手架。以及通过命令使用脚手架(jar)创建出新的工程。 + +脚手架的目的就在于此,我们使用一套通用的代码,按照不同的工程使用新的工程名、包名、版本,来构建一套具有相同基础能力的新的项目。 + +简单来说,maven-archetype-plugin 是 Maven 世界里的“项目模板生成器”。 + +如果把开发一个项目比作盖房子,这个插件的作用就是为你提供一套图纸和预制框架。你不需要每次都从零开始挖地基、垒砖头,只需要选好模板,它就会自动帮你把标准的目录结构和基础配置(pom.xml)搭建好。 + +## 二、流程设计 + +如图,从工程使用 maven 构建脚手架到使用的过程; + +
+ +
+ +- 左侧,对现有工程使用 maven-archetype-plugin 插件,构建工程脚手架。将当前的工程打包成一个可复用的 Archetype 模板。 +- 中间,打包好的脚手架,可以在本地直接使用,也可以发布jar到私服,让大家都可以使用。私服部分,后续在做处理。 +- 右侧,使用方可以基于命令,或者 IntelliJ IDEA 配置 Maven 脚手架的方式,创建和启动工程。这一节,我们先通过命令的方式使用。 diff --git "a/docs/md/project/ai-agent-scaffold/part-3/\347\254\2543-2\350\212\202\357\274\232\344\270\212\344\274\240jar\345\210\260maven\344\273\223\345\272\223.md" "b/docs/md/project/ai-agent-scaffold/part-3/\347\254\2543-2\350\212\202\357\274\232\344\270\212\344\274\240jar\345\210\260maven\344\273\223\345\272\223.md" new file mode 100644 index 000000000..6f3bb6717 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-3/\347\254\2543-2\350\212\202\357\274\232\344\270\212\344\274\240jar\345\210\260maven\344\273\223\345\272\223.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第3-2节:上传jar到maven仓库 +pay: https://t.zsxq.com/GHNso +--- + +# 《AI Agent 脚手架》第3-2节:上传jar到maven仓库 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/6xBLn](https://t.zsxq.com/6xBLn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +发布脚手架工程 Jar 包到阿里云 Maven 私有制品库。 + +在公司里,这块的目的在于,我们设计一款通用的脚手架,让公司里的伙伴都可以使用。那么公司里会把这样的脚手架jar,推送到公司内的私服仓库,之后公司内的用户配置了私服 Maven 地址,就可以使用了。 + +注意,可以检索下 Maven 私服搭建,面试也可能会问,企业里应该怎么做。 + +## 二、流程设计 + +如图,把 jar 发布到 maven 私服仓库; + +
+ +
+ +- 在整个大流程中,我们要上传jar到maven私服仓库,这部分阿里云有提供,可以申请使用。另外,可以尝试检索下 Maven 私服搭建和使用。 +- 这一部分会使用到 IntelliJ IDEA Maven Deploy + Maven Settings.xml 配置阿里云私服。 diff --git "a/docs/md/project/ai-agent-scaffold/part-3/\347\254\2543-3\350\212\202\357\274\232\351\203\250\347\275\262\350\204\232\346\211\213\346\236\266\347\275\221\351\241\265.md" "b/docs/md/project/ai-agent-scaffold/part-3/\347\254\2543-3\350\212\202\357\274\232\351\203\250\347\275\262\350\204\232\346\211\213\346\236\266\347\275\221\351\241\265.md" new file mode 100644 index 000000000..023a3d9ba --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-3/\347\254\2543-3\350\212\202\357\274\232\351\203\250\347\275\262\350\204\232\346\211\213\346\236\266\347\275\221\351\241\265.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-3节:部署脚手架网页 +pay: https://t.zsxq.com/J3YHa +--- + +# 《AI Agent 脚手架》第3-3节:部署脚手架网页 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/UBTWu](https://t.zsxq.com/UBTWu) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过 Nginx 部署网页,把 archetype-catalog.xml 放到网页文件夹下,以便于可以通过 http 配置地址,使用脚手架。 + +这样做的目的,是为了提供一个公用的地址,让所有的用户配置地址后,都可以使用这套脚手架。 + +## 二、流程设计 + +如图,部署网页版后用户使用流程; + +
+ +
+ +- 当你打开 [https://repo.maven.apache.org/maven2/]([https://repo.maven.apache.org/maven2/) 公用脚手架你会发现,在最后面有一个 `archetype-catalog.xml` 文件,也就是这个文件约定了可以使用的脚手架。那么我们就可以依次方式,配置一个自己的网页,在网页下放自己的 `archetype-catalog.xml` 文件,这样我们就可以使用自己实现的脚手架了。 +- 当我们把脚手架这样设计后,所有使用这个网页版的用户,就都可以直接配置使用了。注意使用的过程中,网页版的脚手架配置后,会拉取私服仓库的脚手架jar包。 diff --git "a/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-0\350\212\202\357\274\232ai + draw.io \344\272\247\345\223\201\350\256\276\350\256\241.md" "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-0\350\212\202\357\274\232ai + draw.io \344\272\247\345\223\201\350\256\276\350\256\241.md" new file mode 100644 index 000000000..21afc1d5b --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-0\350\212\202\357\274\232ai + draw.io \344\272\247\345\223\201\350\256\276\350\256\241.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第4-0节:ai + draw.io 产品设计 +pay: https://t.zsxq.com/AAwjy +--- + +# 《AI Agent 场景应用 - ai draw.io》第4-0节:ai + draw.io 产品设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/035hj](https://t.zsxq.com/035hj) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +基于 AI Agent 智能体脚手架,在 draw.io 场景,使用 react 开发一套 ai + draw.io 智能绘图功能场景。 + +当前章节的这套场景功能会比较轻量,不会结合toc用户诉求。但当你越来越多的熟练星球的各个项目以后,你可以在 ai + draw.io 的功能上,添加公众号扫码登录、微信支付购买使用额度 + 拼团购买、数据库记录个人绘图信息、分享绘图数据在统一平台、其他用户可以查看和收藏,以及使用后有抽奖、积分、兑换营销能力等。这些功能在星球的其他项目都有体现,可以陆续学习后扩展补充。 + +## 二、产品效果 + +如图,ai + draw.io 使用 ai agent 脚手架,所能实现的效果; + +
+ +
+ +- 首先,需要结合 [react-drawio](https://github.com/marcveens/react-drawio) 插件,把 draw.io 嵌入到 react 程序里。在 react-drawio 的插件里,提供了相关的 API 操作,这可以让我们把 ai 生成的 xml 文件,让 draw.io 渲染出来。也可以读取到 draw.io 上的内容,再发给 ai 进行分析和调整。 +- 之后,是 ai agent 脚手架开发的程序,编写智能体提示词,让其可以以 xml 的格式返回。当然这部分也可以做一些限定,比如返回的数据是一个带有类型和内容的对象,如果类型 type 是对话,那么可以进行多次交流,如果返回类型是 xml,那么则直接渲染。目前的案例程序,会先直接返回 xml 直接渲染进来。 +- 最后,我们做这类内容,我们先有一个简单的清晰的流程和实现方案,能把整个流程串联起来。当你做的透彻以后,就可以继续扩展迭代功能了。 diff --git "a/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-1\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\345\267\245\347\250\213\346\220\255\345\273\272.md" "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-1\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\345\267\245\347\250\213\346\220\255\345\273\272.md" new file mode 100644 index 000000000..48d967c17 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-1\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\345\267\245\347\250\213\346\220\255\345\273\272.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第4-1节:初始化工程搭建 +pay: https://t.zsxq.com/PDeYZ +--- + +# 《AI Agent 场景应用 - ai draw.io》第4-1节:初始化工程搭建 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/KiliX](https://t.zsxq.com/KiliX) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +初始化搭建 ai draw.io 前端工程,因为 draw.io 的开发插件是 React 语言的,所以我们也要基于 React 语言构建工程。 + +>这部分前端的内容,会使用即可。如果感兴趣,也可以在 TypeScript 官网学习下,它的语言结构也比较清晰,甚至和我们学习的 Java 是有类似的地方的。 + +本节初始化工程搭建开发后,就可以基于 AI IDE 工具,来完成后续的开发了。这部分对后端程序员是比较友好的。当然找工作,一般后端岗位是不会问前端的,所以你可以仅仅是当做使用即可。 + +## 二、开发环境 + +- Node.js v20+ - [https://nodejs.org/zh-cn](https://nodejs.org/zh-cn) - 下载安装即可。 +- WebStorm 2023.1 - 因为自带了开发前端的工具,所以非常好用。星球课程入口,编程环境,软件下载。里面提供了安装包。地址:[https://drive.weixin.qq.com/s?k=ACMA4AfQABU0svnJ27](https://drive.weixin.qq.com/s?k=ACMA4AfQABU0svnJ27) +- 源地址设置;`npm config set registry https://registry.npmmirror.com` - 安装好环境后,设置下源地址。否则在后面构建项目会很慢。 +- TypeScript01:[https://www.runoob.com/typescript/ts-tutorial.html](https://www.runoob.com/typescript/ts-tutorial.html) - 课程资料,简单的菜鸟入门教程。学习之后也能看懂 TypeScript 代码。所有的面向对象语言代码,基本是通用的。 +- TypeScript02:[https://typescript.bootcss.com/namespaces.html](https://typescript.bootcss.com/namespaces.html) - 课程资料,简单的菜鸟入门教程。学习之后也能看懂 TypeScript 代码。所有的面向对象语言代码,基本是通用的。 diff --git "a/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-2\350\212\202\357\274\232\345\234\250\351\241\265\351\235\242\345\265\214\345\205\245draw.io\347\273\204\344\273\266\345\222\214\345\257\271\350\257\235\346\241\206.md" "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-2\350\212\202\357\274\232\345\234\250\351\241\265\351\235\242\345\265\214\345\205\245draw.io\347\273\204\344\273\266\345\222\214\345\257\271\350\257\235\346\241\206.md" new file mode 100644 index 000000000..98b916dcc --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-2\350\212\202\357\274\232\345\234\250\351\241\265\351\235\242\345\265\214\345\205\245draw.io\347\273\204\344\273\266\345\222\214\345\257\271\350\257\235\346\241\206.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第4-2节:在页面嵌入draw.io组件和对话框 +pay: https://t.zsxq.com/JoT4f +--- + +# 《AI Agent 场景应用 - ai draw.io》第4-2节:在页面嵌入draw.io组件和对话框 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/wtWx6](https://t.zsxq.com/wtWx6) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在 React 前端工程中,嵌入 draw.io 组件。以及在整个页面的右侧放入一个对话框,用于后续与服务端的 ai 接口进行对话。 + +这部分的实现,会使用到 ai ide 工具来处理,你常用的任何一款工具都可以。 + +## 二、流程设计 + +如图,前端页面交互的流程设计; + +
+ +
+ +- 这是一个简单的交互,引入 react-drawio 到初始页面,并在右侧设置一个对话框。 diff --git "a/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-3\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223API\346\216\245\345\217\243\345\257\271\346\216\245.md" "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-3\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223API\346\216\245\345\217\243\345\257\271\346\216\245.md" new file mode 100644 index 000000000..0edb53933 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-3\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223API\346\216\245\345\217\243\345\257\271\346\216\245.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第4-3节:智能体API接口对接 +pay: https://t.zsxq.com/hrQVL +--- + +# 《AI Agent 场景应用 - ai draw.io》第4-3节:智能体API接口对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/RnvaG](https://t.zsxq.com/RnvaG) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过脚手架创建 AI Agent 智能体服务端工程,并配置用于绘制 draw.io 流程图的专属提示词。之后在前端工程对接智能体 API 服务接口,包括;创建会话 SessionID、调用对话接口进行绘图(这部分会限制智能体以 drawio 格式返回结果)。 + +## 二、流程设计 + +如图,前端页面与服务端交互的UML流程设计; + +
+ +
+ +- 首先,从用户发起,打开页面开始,则从服务端的智能体列表加载接口,返回智能体列表。 +- 之后,选择智能体进行对话,这个过程要先创建会话ID。也就是所有的本次的交互,会有一个会话ID进行,这样在对话的过程中会记录上下文,输出的结果也会更加准确。 +- 最后,服务端返回智能体结果,是以 draw.io xml 的方式的方式返回的,之后渲染到 draw.io 面板上。 diff --git "a/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-4\350\212\202\357\274\232AI+\347\224\250\346\210\267+DrawIO\357\274\214\344\272\244\344\272\222\345\274\217\347\224\273\345\233\276.md" "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-4\350\212\202\357\274\232AI+\347\224\250\346\210\267+DrawIO\357\274\214\344\272\244\344\272\222\345\274\217\347\224\273\345\233\276.md" new file mode 100644 index 000000000..ed9ecd276 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-4\350\212\202\357\274\232AI+\347\224\250\346\210\267+DrawIO\357\274\214\344\272\244\344\272\222\345\274\217\347\224\273\345\233\276.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第4-4节:AI+用户+DrawIO,交互式画图 +pay: https://t.zsxq.com/BLDCn +--- + +# 《AI Agent 场景应用 - ai draw.io》第4-4节:AI+用户+DrawIO,交互式画图 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[待更新](#) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +增强服务端 ai agent 智能体配置,以分析,绘制,检查的流程处理 draw.io 的图的制作,如果用户信息不准确的时候,则返回给前端让用户补全信息。 + +此外,对于前端的使用也做一些优化处理,包括;增加对话列表页、保存用户绘图信息到浏览器、交互式完成绘图操作、提供案例绘图、携带历史绘图等。 + +## 二、流程设计 + +如图,AI 交互式绘图流程设计; + +
+ +
+ +- 首先,第一个改造点是服务端的智能体流程配置,让 ai agent 对用户的请求进行分析、绘制、检查,如果用户提供的信息不全则让用户继续补充,如果全面则可以直接绘制。那么这个时候,就要给前端返回一个 json 结构,并按照不同的类型返回数据。 +- 之后,因为服务端智能体返回的不再是一个 draw.io 的 xml,所以要对 agent 智能体返回的结果,进行序列化成对象,再返回给前端。前端拿到结果后要对不同的类型的结果,反馈给用户(user)、渲染到 draw.io 页面(xml)。 +- 此外,除了结果的渲染,对前端的对话操作,增加一个历史记录页,保存 draw.io 的绘图内容到浏览器里。这样刷新页面也可以看到历史的画图。 +- 注意,你还可以再此基础上,增加更多的功能。如,学习了星球里的小型支付,那么可以把扫码登录、支付购买额度,对接进来。也可以把大营销的积分,兑换,抽奖对接进来。这些内容,都是可以衔接起来的。 diff --git "a/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-5\350\212\202\357\274\232ai-draw-io\357\274\214\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262.md" "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-5\350\212\202\357\274\232ai-draw-io\357\274\214\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262.md" new file mode 100644 index 000000000..25002cfd5 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-4/\347\254\2544-5\350\212\202\357\274\232ai-draw-io\357\274\214\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262.md" @@ -0,0 +1,34 @@ +--- +title: 【更】第4-5节:ai-draw-io,云服务器部署 +pay: https://t.zsxq.com/5h1K1 +--- + +# 《AI Agent 场景应用 - ai draw.io》第4-5节:ai-draw-io,云服务器部署 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/UaVxl](https://t.zsxq.com/UaVxl) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在云服务器(2c4g)部署 ai-draw-io 前后端项目,后端提供智能体能力,前端提供绘图操作。本次构建的镜像,小傅哥提供了动态更换前端访问服务的IP配置,在不进行构建镜像时,也可以直接使用。 + +>注意,ai-draw-io 绘图操作,模型配置的越好,效果也越好。gpt-5.1 比 gpt-4.1 绘制的效果更好。 + +## 二、部署过程 + +如图,部署过程步骤流程; + +
+ +
+ +- 首先,你需要一台2c4g云服务器(安装 Ubuntu 24),推荐购买 [https://618.gaga.plus](https://618.gaga.plus) - 腾讯云暂时有买1年送3个月,可以配和 [https://bugstack.cn/md/road-map/ssl-httpsok.html](https://bugstack.cn/md/road-map/ssl-httpsok.html) 申请免费 ssl(自动续期)适合后续有域名的时候使用。 +- 之后,在你购买了一台云服务器后,你可以使用小傅哥提供的一键安装脚本,把云服务器部署好 Docker 环境 + Portainer 管理面板。安装脚本:[https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install](https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install) +- 然后,这里还有项目的部署教程,这里是小傅哥录制好的视频,教你如何部署项目。教程:[https://t.zsxq.com/iDuCt](https://t.zsxq.com/iDuCt) - `你可以做为补充学习,本课程也会带着你部署项目。` +- 另外,这里还有一个本地对项目构建好镜像后推送到阿里云docker镜像库,方便大家上传和拉取。其实也可以推送到官网 docker hub,但会需要代理。教程(阿里云docker镜像使用):[https://t.zsxq.com/XdoWr](https://t.zsxq.com/XdoWr) 阿里云镜像库地址:[https://cr.console.aliyun.com/cn-hangzhou/instance/credentials](https://cr.console.aliyun.com/cn-hangzhou/instance/credentials) +- 注意,提前在云服务器安全组开放端口;`9000-docker 管理面板`、`8091-后端接口`、`3000-前端页面` + +> 综上,所有的云服务器操作,在星球「码农会锁」都提供好了学习教程,可以参考总地址:[https://t.zsxq.com/19osWS4qj](https://t.zsxq.com/19osWS4qj) diff --git "a/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-1\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\345\267\245\347\250\213\346\220\255\345\273\272.md" "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-1\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\345\267\245\347\250\213\346\220\255\345\273\272.md" new file mode 100644 index 000000000..783ab8a03 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-1\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\345\267\245\347\250\213\346\220\255\345\273\272.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第5-1节:初始化工程搭建 +pay: https://t.zsxq.com/ELrPx +--- + +# 《AI Agent 场景应用 - MobileOpenClaw》第5-1节:初始化工程搭建 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/dx4wS](https://t.zsxq.com/dx4wS) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +本节初始搭建 MobileOpenClaw - `智能体手机`,服务端工程和安卓网关工程。服务端基于 AI Agent Scaffold 脚手架创建,用于智能体决策部分。安卓作为网关终端,设计接收智能体下发的指令,以及完成一些列动作,如;`启动应用`、`点击指定坐标`、`输入文本`、`滑动屏幕`、`长按`、`双击`、`请求人工接管(登录/验证码)`等。 + +
+ +
+ +> 整个 MobileOpenClaw - 智能体手机,使用的是`安卓`开发的手机端 apk 应用,它可以拿到更高的权限,方便做工程开发测试。 + +## 二、开发环境 + +- JDK 17、Maven 3.8.x、SpringBoot 3.4.3 ~ 4.1.0-M1、Spring AI 1.1.0-M3 ~ 2.0.0-M2、Google ADK +- IntelliJ IDEA CE 2023+ +- Android Studio - 官网(下载):[https://developer.android.com/studio?hl=zh-cn#get-android-studio](https://developer.android.com/studio?hl=zh-cn#get-android-studio) 星球(下载):[https://t.zsxq.com/19Rnk98M0](https://t.zsxq.com/19Rnk98M0) `首次安装时间会比较长` diff --git "a/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-2\350\212\202\357\274\232\346\211\213\346\234\272\347\275\221\345\205\263\345\212\250\344\275\234\350\260\203\345\272\246\350\256\276\350\256\241.md" "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-2\350\212\202\357\274\232\346\211\213\346\234\272\347\275\221\345\205\263\345\212\250\344\275\234\350\260\203\345\272\246\350\256\276\350\256\241.md" new file mode 100644 index 000000000..14bc1e690 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-2\350\212\202\357\274\232\346\211\213\346\234\272\347\275\221\345\205\263\345\212\250\344\275\234\350\260\203\345\272\246\350\256\276\350\256\241.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第5-2节:手机网关动作调度设计 +pay: https://t.zsxq.com/ELrPx +--- + +# 《AI Agent 场景应用 - MobileOpenClaw》第5-2节:手机网关动作调度设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/GdwGE](https://t.zsxq.com/GdwGE) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +初步设计安卓手机端网关服务,通过接收指令,完成一系列动作调度。动作调度指令,由服务端开启一个 socket server,之后由手机端 socket client 连接,并接收指令驱动手机动作。包括;打开、点击、首屏、截图、输入等。 + +后续这套网关调度,由AI Agent 智能体根据用户的诉求,在一些列规划分析后,指令下达。让手机端完成用户诉求。 + +## 二、流程设计 + +如图,通信设备网关调度指令设计; + +
+ +
+ +- 首先,核心的内容是右侧的安卓端,开发一系列控制手机设备的指令动作。之后这些指令动作由 socket 客户端接收 socket 服务端下发的指令完成操作。这里的想法也就是让手机端成为一个指令执行器,具体操作都是由服务端控制,这样就可以满足后续 AI 操作手机的目的。 +- 之后,本节的服务端是编写一个 Socket Server 的测试代码,让手机端连接,之后由服务端下发指令进行测试验证。 diff --git "a/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-3\350\212\202\357\274\232\346\234\215\345\212\241\347\253\257\347\275\221\347\273\234\351\200\232\344\277\241\350\256\276\350\256\241(Netty).md" "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-3\350\212\202\357\274\232\346\234\215\345\212\241\347\253\257\347\275\221\347\273\234\351\200\232\344\277\241\350\256\276\350\256\241(Netty).md" new file mode 100644 index 000000000..e1956f74a --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-3\350\212\202\357\274\232\346\234\215\345\212\241\347\253\257\347\275\221\347\273\234\351\200\232\344\277\241\350\256\276\350\256\241(Netty).md" @@ -0,0 +1,31 @@ +--- +title: 【更】第5-3节:服务端网络通信设计(Netty) +pay: https://t.zsxq.com/IB8jJ +--- + +# 《AI Agent 场景应用 - MobileOpenClaw》第5-3节:服务端网络通信设计(Netty) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/UUvY7](https://t.zsxq.com/UUvY7) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +引入 Netty 框架,设计服务端 Socket Server 通信模型 + Future 等待响应方式获取客户端(手机)反馈结果。 + +这样设计的目的就是为了整个流程设计,从用户端发起请求,到后续章节AI分析决策产生指令,再通过 Socket 服务下发到手机端完成一些列的操作动作。 + +## 二、流程设计 + +如图,从服务端下发指令到客户端(手机)的流程设计; + +
+ +
+ +- 首先,要为整个通信设计一个 Socket 通信模型,以便于服务端和客户端,保持信息数据交互。那么这里在领域层添加了一套 MobileClawService 的服务。 +- 之后,整个通信服务的处理,是由基础设施层完成的,发送给手机端指令后,还需要一个等待,用于达到同步响应的效果。否则 socket 通信是异步的,再从其他入口返回来就不好处理了。 + +> 关于 Netty 这里有一些基础案例教程可以学习[《Netty 基础教程》](https://bugstack.cn/md/netty/base/2019-07-30-netty%E6%A1%88%E4%BE%8B%EF%BC%8Cnetty4.1%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8%E7%AF%87%E9%9B%B6%E3%80%8A%E5%88%9D%E5%85%A5JavaIO%E4%B9%8B%E9%97%A8BIO%E3%80%81NIO%E3%80%81AIO%E5%AE%9E%E6%88%98%E7%BB%83%E4%B9%A0%E3%80%8B.html) diff --git "a/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-4\350\212\202\357\274\232\345\210\235\346\255\245\351\200\232\350\277\207\346\231\272\350\203\275\344\275\223\357\274\214\346\223\215\344\275\234\346\211\213\346\234\272\350\256\276\345\244\207.md" "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-4\350\212\202\357\274\232\345\210\235\346\255\245\351\200\232\350\277\207\346\231\272\350\203\275\344\275\223\357\274\214\346\223\215\344\275\234\346\211\213\346\234\272\350\256\276\345\244\207.md" new file mode 100644 index 000000000..2fbee856a --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-4\350\212\202\357\274\232\345\210\235\346\255\245\351\200\232\350\277\207\346\231\272\350\203\275\344\275\223\357\274\214\346\223\215\344\275\234\346\211\213\346\234\272\350\256\276\345\244\207.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第5-4节:初步通过智能体,操作手机设备 +pay: https://t.zsxq.com/O4m6n +--- + +# 《AI Agent 场景应用 - MobileOpenClaw》第5-4节:初步通过智能体,操作手机设备 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/dqpLb](https://t.zsxq.com/dqpLb) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +初步配置 AI Agent 智能体,分析用户意图,操作手机使用。以及搭配前端操作页面,实时渲染手机当前屏幕最新结果。让用户可以可视化的方式看到手机的动作变化。 + +>可扩展点;将来你可以在智能体功能上对接微信公众号,通过手机端微信通过文字或者语音的方式,操作这台放在家中手机设备完成一些任务的处理。 + +## 二、流程设计 + +如图,从前端用户请求 -> 智能体意图分析和执行 -> 安卓设备指令应答,流程设计; + +
+ +
+ +- 首先,我们先来初步的添加一个智能体配置,以及提供一个页面来串联从用户发起到智能体分析、决策、执行,再传递指令给安卓端的一个操作流程。 +- 之后,整个智能体的操作(轮训处理和拿到决策结果),目前先直接放到 trigger 里执行,后续在细化拆分到编排层。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-5\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\345\267\245\344\275\234\346\265\201\350\256\276\350\256\241.md" "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-5\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\345\267\245\344\275\234\346\265\201\350\256\276\350\256\241.md" new file mode 100644 index 000000000..c05931c0b --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-5\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\345\267\245\344\275\234\346\265\201\350\256\276\350\256\241.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第5-5节:智能体工作流设计 +pay: https://t.zsxq.com/v7M6t +--- + +# 《AI Agent 场景应用 - MobileOpenClaw》第5-5节:智能体工作流设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/dqpLb](https://t.zsxq.com/dqpLb) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过增加 case 层,分摊 trigger 触发器层,对智能体接口(`AgentServiceController#chat`)流程实现中的复杂逻辑处理。这种设计手段,在复杂的业务流程工程实现里是非常场景,我们经过会通过包的职责,来划分功能单元,让整个结构在复杂业务的迭代中,可以保持易于扩展和维护。 + +## 二、流程设计 + +如图,从 trigger 触发器层的功能实现到 case 编排层的设计; + +
+ +
+ +- 首先,一个大的方向,是把图中,上部分 5-4节中的 trigger 内实现的流程,迁移到 5-5 节中 编排层进行处理。以此降低 trigger 层的逻辑功能复杂度。 +- 之后,通过编排逻辑模块,按照功能职责划分不同的类,来实现各个功能逻辑。也就是以前一堆的编排,拆分出不同的逻辑单元,以后看到类就能知道它在做什么。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-6\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\345\274\202\346\255\245\345\223\215\345\272\224\345\261\225\347\244\272\346\211\247\350\241\214\350\277\207\347\250\213.md" "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-6\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\345\274\202\346\255\245\345\223\215\345\272\224\345\261\225\347\244\272\346\211\247\350\241\214\350\277\207\347\250\213.md" new file mode 100644 index 000000000..79126b0a7 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-6\350\212\202\357\274\232\346\231\272\350\203\275\344\275\223\345\274\202\346\255\245\345\223\215\345\272\224\345\261\225\347\244\272\346\211\247\350\241\214\350\277\207\347\250\213.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第5-6节:异步结果响应 +pay: https://t.zsxq.com/PfpTF +--- + +# 《AI Agent 场景应用 - MobileOpenClaw》第5-6节:异步结果响应 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/l1RLL](https://t.zsxq.com/l1RLL) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +修改智能体对话(web控制手机)请求为异步响应式接口,实时反馈到用户端(web)当前的执行动作。那么,这里要把 `Response` 调整为 `ResponseBodyEmitter` + +>**`ResponseBodyEmitter`** 是 Spring Framework(具体来说是 Spring Web MVC)中用于处理异步 HTTP 请求的一个核心类。它的主要作用是**允许服务端在一个 HTTP 请求的生命周期内,异步地、多次向客户端发送数据**。 + +## 二、流程设计 + +如图,在智能体处理流程中添加 ResponseBodyEmitter 异步响应设计; + +
+ +
+ +- 在整个接口调用流程中,穿插进入 ResponseBodyEmitter 进行异步结果的响应处理。 +- 这部分没有逻辑的变动,只是把过程数据渲染回去。如果你还想有一些其他的渲染,也可以通过这样的方式处理。 \ No newline at end of file diff --git "a/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-7\350\212\202\357\274\232\344\275\277\347\224\250AutoGLM-Phone-9B\346\236\204\345\273\272\346\211\213\346\234\272\346\231\272\350\203\275\344\275\223.md" "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-7\350\212\202\357\274\232\344\275\277\347\224\250AutoGLM-Phone-9B\346\236\204\345\273\272\346\211\213\346\234\272\346\231\272\350\203\275\344\275\223.md" new file mode 100644 index 000000000..d5f937f01 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-7\350\212\202\357\274\232\344\275\277\347\224\250AutoGLM-Phone-9B\346\236\204\345\273\272\346\211\213\346\234\272\346\231\272\350\203\275\344\275\223.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第5-7节:使用AutoGLM-Phone-9B构建手机智能体 +pay: https://t.zsxq.com/6qOqE +--- + +# 《AI Agent 场景应用 - MobileOpenClaw》第5-7节:使用AutoGLM-Phone-9B构建手机智能体 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/06kx8](https://t.zsxq.com/06kx8) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +引入智谱发布的 autoglm-phone-9b 专属模型,构建手机智能体。专属模型对手机上的内容操作有更为全面流程控制,比通用视觉模型在处理用户动作时会更加准确。目前这套模型可以使用官网(限时免费 [autoglm-phone](https://docs.bigmodel.cn/cn/guide/models/vlm/autoglm-phone)),也可以在 24G * 2 显卡[自己部署](https://bugstack.cn/md/algorithm/model/autoglm-phone-agent.html)。所以,如果你考虑的是将来在一些场景使用,成本相对也是很低的。 + +## 二、流程设计 + +如图,通过 autoglm-phone-9b 专属模型,构建手机智能体设计; + +
+ +
+ +- 首先,我们把之前的通用模型的实现,定义为 flow 我们要自己编写流程。把专属的手机模型定义为 auto,因为他提供了默认的一些专属特性,可以减少我们流程化上的操作。 +- 之后,对照来看,autoglm-phone-9b 模型,会为我们返回具体的手机的操作指令,如点击、打开、滑动、双击、回到主屏幕等。这部分内容在智谱官网访问也有说明 [AutoGLM-Phone](https://docs.bigmodel.cn/cn/guide/models/vlm/autoglm-phone) +- 那么,现在需要我们改动的内容,主要是围绕着特定官网的给的智能体 prompt 提示词,编写智能体处理过程,与网关通信完成一些列的流程操作。 diff --git "a/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-8\350\212\202\357\274\232\345\244\232\347\211\210\346\234\254\345\256\211\345\215\223\347\211\210\346\234\254\347\255\226\347\225\245\346\224\257\346\214\201.md" "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-8\350\212\202\357\274\232\345\244\232\347\211\210\346\234\254\345\256\211\345\215\223\347\211\210\346\234\254\347\255\226\347\225\245\346\224\257\346\214\201.md" new file mode 100644 index 000000000..21d970242 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-8\350\212\202\357\274\232\345\244\232\347\211\210\346\234\254\345\256\211\345\215\223\347\211\210\346\234\254\347\255\226\347\225\245\346\224\257\346\214\201.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第5-8节:多版本安卓版本策略支持 +pay: https://t.zsxq.com/kpgKo +--- + +# 《AI Agent 场景应用 - MobileOpenClaw》第5-8节:多版本安卓版本策略支持 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/kpgKo](https://t.zsxq.com/kpgKo) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +安卓端的网关终端接收 AI Agent 智能体,通过 Socket 下发指令,完成一系列操作动作。但在一些安卓设备测试中发现,不同的版本安卓它的 API 也是有差异的,如低版本的截图方法在高版本中就没法支持。因此我们需要做一些策略化的处理,自动检测安卓版本,选择不同的 API 进行处理。 + +> 本节的代码调整主要在安卓端(Kotlin),这部分代码直接使用就可以。如果看了不少 Kotlin 语言,也可以编写实现。 + +## 二、流程设计 + +如图,关于安卓端的API策略设计; + +
+ +
+ +- 首先,安卓网关终端的改动主要在于api策略这部分的处理,按照不同的 api 版本来处理。这部分是使用 AI IDE 工具实现的内容。AI 对于非业务的复杂串联的,开发单一工具的会比较容易。很适合有编程思维,但对某个类型的语言,没有太多开发的伙伴。 +- 另外,如果你有安卓设备测试,还可能还有一些其他截屏的限制,这部分后面还要引入录屏截取一帧的操作。 diff --git "a/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-9\350\212\202\357\274\232\344\274\232\350\257\235\344\270\212\344\270\213\346\226\207\347\273\206\345\214\226\345\244\204\347\220\206.md" "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-9\350\212\202\357\274\232\344\274\232\350\257\235\344\270\212\344\270\213\346\226\207\347\273\206\345\214\226\345\244\204\347\220\206.md" new file mode 100644 index 000000000..835785d82 --- /dev/null +++ "b/docs/md/project/ai-agent-scaffold/part-5/\347\254\2545-9\350\212\202\357\274\232\344\274\232\350\257\235\344\270\212\344\270\213\346\226\207\347\273\206\345\214\226\345\244\204\347\220\206.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第5-9节:会话上下文细化处理 +pay: https://t.zsxq.com/eSEVg +--- + +# 《AI Agent 场景应用 - MobileOpenClaw》第5-9节:会话上下文细化处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/kpgKo](https://t.zsxq.com/kpgKo) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +本章要解决2个场景问题;一个是智能体服务端,上下文对话内容超长报错。另外一个是安卓网关端,新版本的安卓API,会限制在隐藏当前app后对其他应用的截屏。 + +## 二、设计方案 + +### 1. 上下文超长 + +首先是 autoglm-phone 模型的上下文token限制 `You requested a total of 26891 tokens: 25867`,因此不能把过多的历史信息都在每次对话的发送给模型。 + +这里一种可以通过 `InMemoryMemoryService` 的方式,自己实现一个记忆上下文,每次只是记录用户请求和最后N条模型处理结果数据。 + +也可以使用另外一种方式是 `MySpringAI` 我们有在前面开发阶段自己实现了一个类,这个类会处理历史信息到模型请求里。因此可以从集合里遍历,拿到必要的信息来处理。 + +### 2. 录屏取一帧 + +鉴于测试验证中发现,不少新版安卓的设备,不限制在我们应用隐藏后,截图打开的应用的。因此这部分我们采用开启视频录制,之后在需要的时候,直接取一帧视频作为截图使用即可。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-0\350\212\202\357\274\232AiAgent \351\241\271\347\233\256\344\273\213\347\273\215\345\222\214\347\263\273\347\273\237\346\274\224\347\244\272.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-0\350\212\202\357\274\232AiAgent \351\241\271\347\233\256\344\273\213\347\273\215\345\222\214\347\263\273\347\273\237\346\274\224\347\244\272.md" new file mode 100644 index 000000000..a2309535b --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-0\350\212\202\357\274\232AiAgent \351\241\271\347\233\256\344\273\213\347\273\215\345\222\214\347\263\273\347\273\237\346\274\224\347\244\272.md" @@ -0,0 +1,124 @@ +--- +title: 【更】第3-0节:Ai Agent 项目介绍和系统演示 +lock: no +--- + +# 《Ai Agent》第3-0节:Ai Agent 项目介绍和系统演示 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/DcL2p](https://t.zsxq.com/DcL2p) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**25年3月初**,小傅哥,带着大家开启了 DeepSeek RAG、MCP 项目,随后兴起 MCP 服务提供的热潮,包括;支付宝、百度、高德,等等大厂开启了 MCP 服务计划。直至现在,市面上已经有了场景非常丰富的 MCP 服务。**那些第一波跟着小傅哥学习的伙伴,早早的让简历上多了一笔 MCP 服务开发和使用的经验!** + +
+ +
+ +**接下来,小傅哥将带着小伙伴,再往前多走一步!** + +RAG 教了,MCP 搞了,那么现在是时候,实现一套自动化的 Ai Agent 服务了。 + +如图,以通过数据库表动态配置的手段,完成相关物料的加载,包括;`模型(gpt-4.1/deepseek)`、`客户端`、`对话预设`、`执行规划(Planning)`、`顾问(记忆、RAG、日志)`、`工具(MCP`)等,在把单个 Client 串联,完成整个 Agent 调用链。这样一个 Agent 调用链可以以对话形式使用或通过 Agent 动态任务自动执行。 + +
+ +
+ +这套项目,小傅哥为它干了`1.87万`行代码(前后端),`14张`数据库表,全程动态化构建模块 Bean 对象,预热装配 Agent 服务。达到随用随配,自主组建出想要的各类的 Agent 功能服务。就以现在的丰富的 MCP 市场来可以说是,广阔天地,大有可为!在这套项目架构下,可以扩展出非常多的东西。 + +接下来,小傅哥就给大家,细致的介绍下这套项目,以及截图演示运行效果。 + +> 🧧 文末提供了全套 AI、RAG、MCP、Agent 项目、开发教程以及工程源码。此外还有非常多的互联网大厂项目(17个),都可以一并获取学习。 + +## 一、项目演示 + +这套项目的功能非常强大,全部都以 Agent 方式进行通信。所有的 Agent 都可以动态化配置,解耦的非常强👍🏻。接下来,小傅哥给大家演示下使用效果。 + +### 1. 前端页面 + +
+ +
+ +- 首先,我为智能对话体(MCP)配置了联网、CSDN自动发帖、文件操作服务。 +- 之后,我们可以通过预设的提示词模板,来调用对应的 MCP 服务,也可以多个 MCP 一起调用。如联网检索文章、生成解答,发布到论坛,在把文章名称记录到本地文件。这一系列操作都是可以的。 +- MCP 服务平台; + + - https://mcpfound.cc/ + - https://mcp.so/ + - https://sai.baidu.com/mcp + +> 结合知识库、MCP、提示词规划、上下文记忆,可以有非常多的场景可以玩。后续小傅哥还会继续分享可玩场景。 + +### 2. 后台页面 + +#### 2.1 配置智能体(动态预热) + +
+ +
+ +#### 2.2 动态任务 + +
+ +
+ +- 系统会自动的把任务加载到系统内执行,完成智能体的调用。 +- 有了这个操作,你配置好的智能体,他就可以连续24小时的工作了。除了自动发文章,你可以配置出各种东西。比如特朗普推特、黄金、股票价格,每天早上出一个文件,邮件方式推送给自己。兼职美滋滋。 + +#### 2.3 MCP管理 + +
+ +
+ +- 市面上有非常的多的 MCP 服务,我们可以选择的这些服务来组装出我们的智能体。 +- 系统支持配置 stdio、sse,两种方式。无论是自己开发的 MCP 还是市面的都可以使用。(课程中有教大家,基于 Spring AI 怎么开发 MCP 服务) + +## 二、系统设计 + +### 1. 功能流程 + +
+ +
+ +- 如图,从上往下,以任务或会话方式,调用 agent 为目标,串联各个 client。形成内部处理 a2a 流程。 +- 之后,对于 client 则由系统都动态的方式创建 bean 对象。运营在 ai agent 后台配置相关数据即可。 + +### 2. 库表设计 + +
+ +
+ +如图,为整个系统对应的数据库表信息; + +- ai_agent_task_schedule,智能体任务调度配置表 +- ai_agent,AI智能体配置表 +- ai_agent_client,智能体-客户端关联表 +- ai_client,AI客户端配置表 +- 模型配置组;ai_client_model、ai_client_model_config、ai_client_model_tool_config +- 工具配置组;ai_client_tool_config、ai_client_tool_mcp +- 顾问配置组;ai_client_advisor、ai_client_advisor_config +- 提示词配置;ai_client_system_prompt、ai_client_system_prompt_config +- 知识库配置;ai_rag_order + +### 3. 系统工程 + +
+ +
+ +- 如图,为整个系统的工程结构,分为 api、app、domain、infrastructure、trigger、types,六边形架构。(现在各个互联网都在落地 DDD,因为 DDD 比最早出来的几年,已经有了非常明确的规范)相关资料;[https://bugstack.cn/md/road-map/ddd-guide-01.html](https://bugstack.cn/md/road-map/ddd-guide-01.html) +- Domain 核心领域层,处理 Agent 的预热、对话、知识库、任务的操作。后续 Agent 相关都维护到这个领域包下。 +- Trigger 触发器层,负责对外提供接口,让外部来调用。当有一些纯 crud 操作的流程时,这个架构下,会在 trigger 层直接调用基础设施层提供数据,而不需要在经过 domain 领域层,重复封装对象。 + +**注意** ai-agent-station 全套代码,可以直接获取后学习(持续更新最新方案)。之后课程会单独起一个 ai-agent-station-study 工程,带着大家从0到1学习。 + diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-10\350\212\202\357\274\232Agent\346\211\247\350\241\214\351\223\276\350\267\257\345\210\206\346\236\220.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-10\350\212\202\357\274\232Agent\346\211\247\350\241\214\351\223\276\350\267\257\345\210\206\346\236\220.md" new file mode 100644 index 000000000..db231e6be --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-10\350\212\202\357\274\232Agent\346\211\247\350\241\214\351\223\276\350\267\257\345\210\206\346\236\220.md" @@ -0,0 +1,38 @@ +--- +title: 【更】第3-10节:Agent执行链路分析 +pay: https://t.zsxq.com/bxh8h +--- + +# 《Ai Agent》第3-10节:Agent执行链路分析 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/ty1Yy](https://t.zsxq.com/ty1Yy) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过现有实现的动态化构建 Ai API、Model、Client、Tool(MCP)、Advisor(记忆、RAG)、Prompt,完成 Ai Agent 服务处理。 + +最早 OpenAi 出来时,我们只是对 Ai 单向询问(含上下文记忆)和提供问题结果。后来开始有了 RAG 知识库,可以让我们每次的提问结合知识库获取更全面的内容。再到后来开始有了 MCP 服务协议,让 AI 具备了调用外部服务的能力。 + +那么,到这再往后开始有了 Ai Agent 的概念,也就是让 Ai 具备环境感知能力、自主决策并执行行动,直至完成最终的结果。 + +这也就是我们目前在使用一些 Ai Agent 的时候,进行一些问题提问的时候,他会根据环境(询问)状态制定行动计划,调用各种工具和API执行具体任务,并在多轮交互中维持上下文状态,输出最终的结果。这也是我们要做的事情。 + +鉴于,整个 Ai Agent 的复杂性,我们不能一上来就直接去编码,这样很多伙伴会比较晕。所以我们先来完成 Agent 单元测试,在结合我们动态实例化的各项服务,处理 Agent 循环制定行动计划和执行多轮会话。 + +## 二、流程设计 + +如图,不同方案实现的 Agent 流程; + +
+ +
+ +Ai Agent 的处理过程也是分为几类的,用于适应不同的场景使用; + +1. 固定N个步骤,这类的一般是配置工作流的,提高任务执行的准确性。如,一些检索资料、发送帖子、处理通知等。 +2. 顺序循环调用,配置 Agent 要执行的多个 Client 端,以此顺序执行。适合一些简单的任务关系,并已经分配好的动作,类似于1的方式。 +3. 智能动态决策,这类是目前市面提供给大家使用的 Agent 比较常见的实现方式,它会动态的规划执行动作,完成行动步骤,观察执行结果,判断完成状态和步骤。并最终给出结果。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-11\350\212\202\357\274\232Agent\346\211\247\350\241\214\351\223\276\350\267\257\350\256\276\350\256\241.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-11\350\212\202\357\274\232Agent\346\211\247\350\241\214\351\223\276\350\267\257\350\256\276\350\256\241.md" new file mode 100644 index 000000000..ef358d38c --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-11\350\212\202\357\274\232Agent\346\211\247\350\241\214\351\223\276\350\267\257\350\256\276\350\256\241.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第3-11节:Agent执行链路设计 +pay: https://t.zsxq.com/eSLoH +--- + +# 《Ai Agent》第3-11节:Agent执行链路设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/hNFqE](https://t.zsxq.com/hNFqE) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +将上一节对 Ai Agent 执行链路的分析,以及对应的 AutoAgentTest 测试代码,使用规则树设计可执行链路节点。 + +本节是其中的一个 Ai Agent Auto 自动执行策略,后续还要把其他的 Ai Agent 执行策略也加入进来实现。 + +## 二、流程设计 + +如图,Auto Ai Agent 动态多轮会话执行流程图; + +
+ +
+ +- 首先,给入口保留一个多策略选择,以适应我们不同场景的多类型 Agent 选择使用,后续会在 agent 配置表增加策略选择属性来区分调用。本节我们先处理一个 AutoAgent 的实现。 +- 之后,进入到关键地方,在上一节 AutoAgentTest 章节,设计了一套自动化 Agent 执行方法,通过 for 循环处理。这里我们通过规则树,分多个多个节点步骤执行,节点间可循环调用,增强整体的灵活性。 +- 最后,以用户提问到所有的步骤执行完成后,进入到结束环节,产生结果。如果你上一节已经高透彻,那么到这里其实会更加容易理解对于节点的拆分。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-12\350\212\202\357\274\232Agent\346\234\215\345\212\241\346\216\245\345\217\243\345\222\214UI\345\257\271\346\216\245.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-12\350\212\202\357\274\232Agent\346\234\215\345\212\241\346\216\245\345\217\243\345\222\214UI\345\257\271\346\216\245.md" new file mode 100644 index 000000000..ceef27a8d --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-12\350\212\202\357\274\232Agent\346\234\215\345\212\241\346\216\245\345\217\243\345\222\214UI\345\257\271\346\216\245.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第3-12节:Agent服务接口和UI对接 +pay: https://t.zsxq.com/T0H6n +--- + +# 《Ai Agent》第3-12节:Agent服务接口和UI对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/eaqbx](https://t.zsxq.com/eaqbx) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +以实现 Ai Auto Agent Server-Sent Events (SSE) 流式响应接口为目的,设计 SSE 异步响应结果对象,对 Step 1~4 步骤的过程数据进行异步流式响应返回。开发好接口后,与Ai实现的前端UI界面进行对接。 + +像是 Ai(Cursor、trae.ai) 对于前端这样没有太多复杂的流程代码,可以很好的实现出来。这对于后端工程师想做一些前端UI产品化的东西,就变得容易的很了! + +## 二、对接效果 + +如图,流式响应(SSE)接口对接UI效果; + +
+ +
+ +- 首先,小傅哥对 **Auto Agent - 自动智能对话体** 进行了数据库表(初始数据)和服务启动时自动装配 Ai Agent 所需的各项配置(客户端、模型、API、MCP等)。以便于可以在接口请求服务时,调用 Auto Agent 智能体。 +- 之后,用于就可以对智能体进行提问,所有的提问信息,会进入到服务端的 Step 1~4 步骤,并进行循环分析、执行、检测,以及最终输出结果。 + +> 接下来,小傅哥带着大家看看 Auto Agent 服务接口和对接是如何处理的。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-13\350\212\202\357\274\214Agent-ELK\346\227\245\345\277\227\345\210\206\346\236\220\345\234\272\346\231\257.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-13\350\212\202\357\274\214Agent-ELK\346\227\245\345\277\227\345\210\206\346\236\220\345\234\272\346\231\257.md" new file mode 100644 index 000000000..bdea85b3c --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-13\350\212\202\357\274\214Agent-ELK\346\227\245\345\277\227\345\210\206\346\236\220\345\234\272\346\231\257.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第3-13节,Agent-ELK日志分析场景 +pay: https://t.zsxq.com/agZ9E +--- + +# 《Ai Agent》第3-13节,Agent-ELK日志分析场景 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/kMjAw](https://t.zsxq.com/kMjAw) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +增加 Agent-ELK 日志分析的实际应用场景,通过 Agent 根据用户诉求,自主分析、规划、执行和输出结果,来帮助我们对日志检索的提效。 + +ELK(或自研) 是各个互联网公司中都有的一套分布式日志设备,以便于研发在遇到线上系统报警和运营反馈事故问题时,快速检索日志。但往往这种检索的日志的方式都是非常耗时的,所以增加 Agent 方式来辅助提效是非常有必要的。 + +注意;面试往往就是需要这样的实际应用场景,而不是坦克大战、贪吃蛇、图书管理系统等一些不着边际的项目(适合练手但不适合写简历)。 + +## 二、功能流程 + +如图,Agent-ELK 的设计使用流程图; + +
+ +
+ +- 首先,虚线框内为模拟的系统的应用日志,部署一套 ELK 之后通过脚本把日志数据写入到 ELK。你也可以通过这套教程实际部署一套 ELK [https://bugstack.cn/md/road-map/elk.html](https://bugstack.cn/md/road-map/elk.html) 另外像星球项目,拼团、大营销等也都有 ELK 分布式日志的对接使用。 +- 之后,要为这套场景增加一套新的 Ai Agent 描述话术,在执行 ELK 日志分析的时候,我们先手动选择出要使用的 Ai Agent 服务。这样它就可以以 ELK 对应的 Prompt 话术分析方式使用了。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-14\350\212\202\357\274\214Agent-Prometheus\347\233\221\346\216\247\345\210\206\346\236\220\345\234\272\346\231\257.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-14\350\212\202\357\274\214Agent-Prometheus\347\233\221\346\216\247\345\210\206\346\236\220\345\234\272\346\231\257.md" new file mode 100644 index 000000000..790b7709c --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-14\350\212\202\357\274\214Agent-Prometheus\347\233\221\346\216\247\345\210\206\346\236\220\345\234\272\346\231\257.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-14节,Agent-Prometheus监控分析场景 +pay: https://t.zsxq.com/8BfkE +--- + +# 《Ai Agent》第3-14节,Agent-Prometheus监控分析场景 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/ymfSm](https://t.zsxq.com/ymfSm) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +上一节我们做了 Ai Agent ELK,这一节扩展增加 Prometheus(普罗米修斯监控) 监控系统,让 Ai Agent 具备智能监控问题分析场景。这种场景东西是在公司里非常重要的,且有实际使用用途的东西。 + +本节基于的是 Ai MCP Prometheus + Agent Prompt(分阶段提示词),来完成自动化分析、规划、执行、检测、输出的智能监控系统。 + +## 二、功能流程 + +如图,Agent-Prometheus 的设计使用流程图; + +
+ +
+ +- 首先,虚线框为模拟的系统监控日志脚本(含运行程序),部署的一套普罗米修斯监控系统。你可以通过这套教程来部署一套普罗米修斯监控 [https://bugstack.cn/md/road-map/grafana.html](https://bugstack.cn/md/road-map/grafana.html) 星球的拼团、大营销、openai应用都有这样的监控系统使用。 +- 之后,要为这套场景增加一套新的 Ai Agent 执行话术,另外还要配置一套对应的 RAG 知识库,来增强分析能力。 diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-15\350\212\202\357\274\232FlowAgent\346\211\247\350\241\214\351\223\276\350\267\257\345\210\206\346\236\220.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-15\350\212\202\357\274\232FlowAgent\346\211\247\350\241\214\351\223\276\350\267\257\345\210\206\346\236\220.md" new file mode 100644 index 000000000..8c10a272a --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-15\350\212\202\357\274\232FlowAgent\346\211\247\350\241\214\351\223\276\350\267\257\345\210\206\346\236\220.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第3-15节:AgentFlow执行链路分析 +pay: https://t.zsxq.com/Ht0o1 +--- + +# 《Ai Agent》第3-15节:AgentFlow执行链路分析 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/u9tjH](https://t.zsxq.com/u9tjH) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +为了打开 Agent 的实现思路,本章我们再增加一种新的 Auto Agent 设计,这种设计方式以通过用户的提问和当前 Agent 配置的 MCP 工具集合,进行执行步骤的规划设计。之后在通过执行步骤按照拆分的步骤顺序号,依次进行执行。有点类似于 [manus](https://manus.im/) 的过程。 + +## 二、流程设计 + +如图,多种 Ai Agent 执行设计流程图; + +
+ +
+ +Ai Agent 的处理过程也是分为几类的,用于适应不同的场景使用; + +1. 固定N个步骤,这类的一般是配置工作流的,提高任务执行的准确性。如,一些检索资料、发送帖子、处理通知等。 +2. 顺序循环调用,配置 Agent 要执行的多个 Client 端,以此顺序执行。适合一些简单的任务关系,并已经分配好的动作,类似于1的方式。 +3. 智能动态决策,这类是目前市面提供给大家使用的 Agent 比较常见的实现方式,它会动态的规划执行动作,完成行动步骤,观察执行结果,判断完成状态和步骤。并最终给出结果。 +4. 【新增】规划分析决策,根据用户输入的信息诉求,以及配置的 MCP 的能力,进行步骤规划。之后把步骤拆分出 1、2、3 具体要做什么,在依次执行这些步骤。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-16\350\212\202\357\274\232FlowAgent\346\211\247\350\241\214\351\223\276\350\267\257\350\256\276\350\256\241.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-16\350\212\202\357\274\232FlowAgent\346\211\247\350\241\214\351\223\276\350\267\257\350\256\276\350\256\241.md" new file mode 100644 index 000000000..e3beecfb2 --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-16\350\212\202\357\274\232FlowAgent\346\211\247\350\241\214\351\223\276\350\267\257\350\256\276\350\256\241.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第3-16节:FlowAgent执行链路设计 +pay: https://t.zsxq.com/Htptt +--- + +# 《Ai Agent》第3-16节:FlowAgent执行链路设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/J68sk](https://t.zsxq.com/J68sk) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在 3-11 节的时候,我们做过一个这样的事情。针对于测试阶段的 Agent Test 代码,使用设计模式拆分出各个执行步骤,便于理解和维护。这一节我们同样需要把上一节分析的 FlowAgent 测试代码,按照模块化的流程进行拆分。 + +## 二、流程设计 + +如图,Flow Ai Agent 动态步骤分析执行流程图; + +
+ +
+ +- 首先,新增加一个 Agent 执行策略,流程步骤拆分执行。这个过程其实比上一 AutoAgent 要简单一些。 +- 之后,分贝设计出 Step1 工具分析、Step2 动作规划、Step3 拆分步骤、Step4 执行节点(循环执行),这四个步骤就是 FlowAgentTest.test_agent 里的步骤。 +- 最后,响应结果。后续章节会使用 sse 将结果响应到前端,这里我们暂时增加了判null操作,先不需要发送 sse 数据。 diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-17\350\212\202\357\274\232\345\242\236\345\212\240\350\260\203\345\272\246\345\231\250\347\255\226\347\225\245\346\211\247\350\241\214Agent\351\223\276\350\267\257.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-17\350\212\202\357\274\232\345\242\236\345\212\240\350\260\203\345\272\246\345\231\250\347\255\226\347\225\245\346\211\247\350\241\214Agent\351\223\276\350\267\257.md" new file mode 100644 index 000000000..0b3a12d46 --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-17\350\212\202\357\274\232\345\242\236\345\212\240\350\260\203\345\272\246\345\231\250\347\255\226\347\225\245\346\211\247\350\241\214Agent\351\223\276\350\267\257.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第3-17节:增加调度器策略执行Agent链路 +pay: https://t.zsxq.com/ayQWR +--- + +# 《Ai Agent》第3-17节:增加调度器策略执行Agent链路 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/44qnU](https://t.zsxq.com/44qnU) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +从ui页面开始,增加 ai agent 分类选择使用。因为这里有不同类型的 ai agent,所以要对调用的过程增加一个策略调度器,按照不同类型的 ai agent 选择不同的执行策略。 + +## 二、流程设计 + +如图,Ai Agent 策略调度器执行过程; + +
+ +
+ +- 首先,我们实现的 ai agent 有多种类型,所以在选择场景选择的时候,要根据不同的场景获取到走那种类型的 ai agent 执行策略。 +- 之后,这里我们就把 AutoAgent、FlowAgent 都放到调度器里执行,另外要在数据库表 ai_agent 中增加一个 strategy 执行策略配置,这样用户提问时候传入的 agent id 就可以获取到对应的策略了。 +- 最后,本节还有一点关于页面 UI 的完善,每次对话,会把对话消息存储到历史对话中。这部分前端的东西使用 ai 开发工具处理的。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-18\350\212\202\357\274\232\345\212\250\346\200\201\346\211\247\350\241\214\346\231\272\350\203\275\344\275\223\344\273\273\345\212\241.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-18\350\212\202\357\274\232\345\212\250\346\200\201\346\211\247\350\241\214\346\231\272\350\203\275\344\275\223\344\273\273\345\212\241.md" new file mode 100644 index 000000000..c4ac6c6bc --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-18\350\212\202\357\274\232\345\212\250\346\200\201\346\211\247\350\241\214\346\231\272\350\203\275\344\275\223\344\273\273\345\212\241.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-18节:动态执行智能体任务 +pay: https://t.zsxq.com/8yPe5 +--- + +# 《Ai Agent》第3-18节:动态执行智能体任务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/j116r](https://t.zsxq.com/j116r) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +本节首先新增一个基于数据库固定配置步骤的简单循环执行智能体策略。随后,结合数据库中配置的智能体定时执行参数,以及工程中引入的扳手工程动态任务组件,实现对智能体动态任务的调度与执行。 + +那么,这里有一个前置学习,《通用技术组件 - 🔧扳手工程》第5节:任务调度组件 [https://t.zsxq.com/bLkoF](https://t.zsxq.com/bLkoF) - 需要先完成下,之后就可以在智能体项目中使用了。 + +## 二、功能流程 + +如图,Ai Agent 动态任务执行过程; + +
+ +
+ +- 首先,我们在项目中引入了扳手工程的任务调度组件。在调度组件内,配置检索出数据库表内可执行的任务以及对应的参数,之后由定时任务管理。 +- 之后,本节我们又增加了一个新的简单的 Agent 执行策略,依次循环数据库 Agent 配置的客户端。这个场景很适合工作流编排,把固定要执行的客户端,依次写入到数据库配置,就可以按照固定步骤执行了。这也是咱们在早期分析 Agent 执行方式的一种。 diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-19\350\212\202\357\274\232\346\213\226\346\213\211\346\213\275\347\274\226\346\216\222\346\225\260\346\215\256\345\255\230\345\202\250.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-19\350\212\202\357\274\232\346\213\226\346\213\211\346\213\275\347\274\226\346\216\222\346\225\260\346\215\256\345\255\230\345\202\250.md" new file mode 100644 index 000000000..6e06b11c7 --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-19\350\212\202\357\274\232\346\213\226\346\213\211\346\213\275\347\274\226\346\216\222\346\225\260\346\215\256\345\255\230\345\202\250.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第3-19节:拖拉拽编排数据存储 +pay: https://t.zsxq.com/kPbFh +--- + +# 《Ai Agent》第3-19节:拖拉拽编排数据存储 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/oxIZg](https://t.zsxq.com/oxIZg) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过 [flowgram.ai](https://flowgram.ai/) 框架,实现项目中 Ai Agent 拖拉拽编排能力,增强使用的易用性。 + +这部分拖拉拽主要完成的就是一个 ai agent 所需的,client、prompt、advisor、model(api)的串联使用。通过拖拉拽方式替代 ai_client_config 中需要手动维护的数据关系链。 + +本节我们先来完成拖拉拽的页面搭建和数据存储处理。提示,对于后端开发人员,不非得学习前端代码,只要能用起来就可以。面试中往往主要考察项目完整性,但不会对后端人员必须会写前端。所以,如果你不会前端代码,也不用太担心。 + +## 二、实现效果 + +如图,是通过拖拉拽实现的 Ai Agent 编排效果; + +
+ +
+ +- 创建过程中,Agent 是用户本次新创建的智能体,这个智能体所需用到的Client、以及 Client 所需的 Advisor、Prompt、Model、MCP 则可以通过拖拉拽的方式进行串联编排。 +- 对于这些节点所拉取的数据,则是从服务端接口进行获取的。如,Client 客户端获取可用数据,Model 获取模型,之后链接的 MCP 工具可以自由组合。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-1\350\212\202\357\274\232Ai Agent \344\270\232\345\212\241\346\265\201\347\250\213\343\200\201\347\263\273\347\273\237\346\236\266\346\236\204\343\200\201\345\272\223\350\241\250\350\256\276\350\256\241\350\257\264\346\230\216.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-1\350\212\202\357\274\232Ai Agent \344\270\232\345\212\241\346\265\201\347\250\213\343\200\201\347\263\273\347\273\237\346\236\266\346\236\204\343\200\201\345\272\223\350\241\250\350\256\276\350\256\241\350\257\264\346\230\216.md" new file mode 100644 index 000000000..1b041207e --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-1\350\212\202\357\274\232Ai Agent \344\270\232\345\212\241\346\265\201\347\250\213\343\200\201\347\263\273\347\273\237\346\236\266\346\236\204\343\200\201\345\272\223\350\241\250\350\256\276\350\256\241\350\257\264\346\230\216.md" @@ -0,0 +1,32 @@ +--- +title: 【更】第3-1节,Ai Agent 业务流程、系统架构、库表设计说明 +pay: https://t.zsxq.com/qUYx0 +--- + +# 《Ai Agent》第3-1节,Ai Agent 业务流程、系统架构、库表设计说明 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/DcL2p](https://t.zsxq.com/DcL2p) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +了解 Ai Agent 构建元素,以 Spring AI 硬编码创建 Agent 过程,分析各个模块用途,并以此设计拆分元素和对应的库表设计。 + +经过本节我们可以了解到,怎么把这些硬编码的过程,以配置数据库表的方式,动态化构建。有了动态化的构建,也就可以按需配置出各种 Agent 服务来满足我们的业务诉求。尤其是现在 MCP 如火如荼的发展,有一套自动化的 Agent 是非常重要的。 + +> 第3-0节,介绍和演示中,涉及了本节的部分内容。本节主要站在开发视角,来讲解如何架构和开发系统。 + +## 二、Agent 介绍 + +AI 智能体是使用 AI 来实现目标并代表用户完成任务的软件系统。其表现出了推理、规划和记忆能力,并且具有一定的自主性,能够自主学习、适应和做出决定。 + +这些功能在很大程度上得益于生成式 AI 和 AI 基础模型的多模态功能。AI 智能体可以同时处理文本、语音、视频、音频、代码等多模态信息;可以进行对话、推理、学习和决策。它们可以随着时间的推移不断学习,并简化事务和业务流程。智能体可以与其他智能体协作,来协调和执行更复杂的工作流。 + +
+ +
+ +>Spring AI 框架,支持大语言模型构建 AI Agent 实现。AI Agent是整合多种技术手段的智能实体 ,其实现依赖于 Tools、MCP、Memory、RAG(Retrieval 增强检索生成) 等技术组件,但不是非得依赖全部组件才叫 AI Agent。 diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-20\350\212\202\357\274\232Agent\347\256\241\347\220\206\345\220\216\345\217\260\345\256\236\347\216\260.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-20\350\212\202\357\274\232Agent\347\256\241\347\220\206\345\220\216\345\217\260\345\256\236\347\216\260.md" new file mode 100644 index 000000000..b88505970 --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-20\350\212\202\357\274\232Agent\347\256\241\347\220\206\345\220\216\345\217\260\345\256\236\347\216\260.md" @@ -0,0 +1,78 @@ +--- +title: 【更】第3-20节:Agent管理后台实现 +pay: https://t.zsxq.com/5Sdfr +--- + +# 《Ai Agent》第3-20节:Agent管理后台实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/z0jRV](https://t.zsxq.com/z0jRV) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +设计实现一套 Ai Agent 管理后台页面,允许用户通过管理后台实现 Ai Agent 的资源配置和拖拉拽方式维护 Ai Agent 智能体。 +第 +本章节没有复杂的逻辑实现,都是 CRUD 操作,把原本在数据库中手动处理的过程,以更符合运营使用方式提供用户使用。所以,本节你只需要了解、看懂,可以跟着继续迭代你想要的内容即可。 + +## 二、实现效果 + +这是一套完整的 Ai Agent 智能体管理后台,包括了所有资源的配置(model、client、mcp、advisor、prompt),以及拖拉拽编排的方式完成 Ai Agent 智能体的构建。 + +以下的截图内容和使用,会在本节课程的视频里演示。也包括如何运行使用。 + +### 1. 登录界面 + +
+ +
+ +- 这一部分在数据库表增加了 admin_user 表,有配置登录账号和密码,可以简单做校验。 + +### 2. 管理界面 + +
+ +
+ +- 管理后台目前提供了,代理管理(拖拉拽编排方式配置智能体),资源管理(model、client、mcp、advisor、prompt) +- 数据分析、系统设置,是样例,你可以继续扩展你所需要的内容。 + +### 3. 代理管理 + +#### 3.1 代理列表 + +
+ +
+ +- 这里的代理列表,就是通过拖拉拽配置的智能体。可以点击【查看】看到明细,也可以【新建】,还可以删除。 +- 点击【加载】则是调用服务端,把数据加载到 Spring 容器,之后就可以使用了。 + +#### 3.2 代理配置 + +
+ +
+ +- 当你点击一个代理配置,则会展示出拖拉拽的数据到页面上。这部分会从数据库读取,之后展示出来,全部可视化。 +- 如果你点击了Save则会做出一份新的,之后对于旧的,你可以自己手动删除。 + +### 4. 资源管理 + +
+ +
+ +- 资源管理下,是配置一个智能体所需的各项资源信息,你可以在这里进行维护。如,MCP 工具管理。 + +### 5. 页面使用 + +
+ +
+ +- 配置后的智能体,可以在智能体选择里进行获取使用。之后进行提问。 +- 效果还不错,这里小傅哥验证了配置的智能体进行提问。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-21\350\212\202\357\274\232\345\234\250\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262\344\270\212\347\272\277.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-21\350\212\202\357\274\232\345\234\250\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262\344\270\212\347\272\277.md" new file mode 100644 index 000000000..13f92fbfb --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-21\350\212\202\357\274\232\345\234\250\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262\344\270\212\347\272\277.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-21节:在云服务器部署上线 +pay: https://t.zsxq.com/HO8sx +--- + +# 《Ai Agent》第3-21节:在云服务器部署上线 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/wfgDS](https://t.zsxq.com/wfgDS) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在云服务器(2c4g)部署 Ai Agent 前端页面(html)、管理页面(react)、后端服务(java)、基础环境(MySql、PG向量库)。 + +## 二、部署过程 + +如图,为本次的部署过程 + +
+ +
+ +- 推荐2c4g云服务器购买,[http://618.gaga.plus/](http://618.gaga.plus/) 选个便宜的购买就可以。 +- 本次会进行本地构建镜像,之后 push 到阿里云私有个人镜像库(免费的可以申请)。这个可以代替 Docker Hub 作为代理使用。教程;[https://t.zsxq.com/XdoWr](https://t.zsxq.com/XdoWr) +- 云服务器部署教程:[https://t.zsxq.com/19osWS4qj](https://t.zsxq.com/19osWS4qj) - 需要云服务器安装 Docker、Portainer,以及配置镜像地址 [https://t.zsxq.com/2DGGY](https://t.zsxq.com/2DGGY) +- 记得在云服务器安全组开放端口 9000(Portainer)、8099(服务端)、3002(管理端)、8899(mysql管理端) 等你需要对外的端口。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-2\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\351\241\271\347\233\256\345\267\245\347\250\213.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-2\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\351\241\271\347\233\256\345\267\245\347\250\213.md" new file mode 100644 index 000000000..3c637aeb3 --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-2\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\351\241\271\347\233\256\345\267\245\347\250\213.md" @@ -0,0 +1,60 @@ +--- +title: 【更】第3-2节:初始化项目工程 +pay: https://t.zsxq.com/3B4vv +--- + +# 《Ai Agent》第3-2节:初始化项目工程 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/K9NWT](https://t.zsxq.com/K9NWT) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +很多小伙伴可能都接触过一些入门级项目,简单地使用 IntelliJ IDEA 创建和开发。但在互联网公司中,如何规范地创建和管理一个新项目,往往缺乏清晰的认知。毕竟公司里有大量新项目需要启动,不可能让每个团队、每个人都随意创建风格各异的工程,这样会大幅增加后续维护和协作的难度。因此,建立统一的工具链和标准化流程显得尤为重要。 + +所以,跟着小傅哥学习,不仅仅是完成一个项目的开发,更是以互联网大型企业的标准化流程和规范,来设计、开发和实现功能,帮助大家掌握更专业、更高效的开发方法。 + +## 一、本章诉求 + +教会小伙伴使用统一的标准脚手架初始化创建项目工程,并了解工程模块的分层用途。 + +课程会循序渐进的从0到1,逐步带着大家完成项目的开发。开局只有一把 IntelliJ IDEA,完成项目后你可以学习到;业务、架构、设计、方案、配置、部署(Linux、Docker)等各项知识。 + +## 二、如何开始 + +### 1. 前置学习 + +小伙伴在学习的时候,可以依照课程的方式进行创建项目、变更配置、启动测试。这里有一些前置学习,包括:Git、Maven、Docker、脚手架,课程已经准备好了,可以刷下;**磨刀不误砍柴工,基础刷完更轻松!** + +- Git:[https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) +- Maven:[https://bugstack.cn/md/road-map/maven.html](https://bugstack.cn/md/road-map/maven.html) +- Docker [https://bugstack.cn/md/road-map/docker-what.html](https://bugstack.cn/md/road-map/docker-what.html) +- 脚手架:[https://bugstack.cn/md/road-map/ddd-archetype-maven.html](https://bugstack.cn/md/road-map/ddd-archetype-maven.html) + +> 另外课程会使用 Java JDK 17、Maven 3.8.x,软件已经提供好,可以直接下载;[https://t.zsxq.com/19Rnk98M0](https://t.zsxq.com/19Rnk98M0) - `附件内含有配置好阿里云镜像的 Maven` + +### 2. 开始学习 + +1. 你需要通过 git clone 命令,或者 IntelliJ IDEA 自动的检出工程方式,把项目工程检出到本地。关于如何使用Git检出项目,在前置学习里提供了教程。 +2. 检出代码后,你可以通过 IntelliJ IDEA 打开项目,并按照每一节最开始说明的本节对应的代码分支,把工程代码切换到对应的这一节。 +3. 接下来你可以通过课程的视频和小册以及提供的代码进行学习,并跟随课程每节要完成的内容,一步步操作。过程中可以参考课程的代码进行学习。如果自己的代码运行出问题的时候,可以运行课程的代码验证是环境问题还是个人代码问题。**另外注意运行课程代码,要修改对应的环境为你的本地环境,mysql、redis等** +4. 对于课程中每节涉及的库表,会放到工程 docs/dev-ops mysql 下。你可以每节学习创建一个新的库名称,之后导入。但要记得在工程 app/application-dev.yml 文件中修改对应的库名称。 + +### 3. 环境安装 + +课程提供了使用 Docker 部署 MySQL、Redis 环境的脚本。因为使用 Docker 可以随时方便卸载,不会污染本地电脑的本机环境。而且后续部署 Linux 云服务器也会非常顺手。 + +关于环境的安装; + +
+ +
+ +1. Windows + wsl2,本地使用 powershell 切换到工程文件夹,执行 `docker-comopse -f docker-compose-environment-aliyun.yml up -d` +2. Mac 电脑的适配性会更好,直接点击这里的绿色箭头即可安装。 +3. 如果本机配置有问题,也可以选择使用云服务器。课程中有云服务器的操作教程,部署起来更方便。云服务器教程:[https://bugstack.cn/md/road-map/linux.html](https://bugstack.cn/md/road-map/linux.html) + +> 环境安装后就可以使用 MySql、Redis 链接工具使用了,也可以手动更新库表。 diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-3\350\212\202\357\274\232AiAgent\346\265\213\350\257\225\346\241\210\344\276\213.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-3\350\212\202\357\274\232AiAgent\346\265\213\350\257\225\346\241\210\344\276\213.md" new file mode 100644 index 000000000..ee56458cf --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-3\350\212\202\357\274\232AiAgent\346\265\213\350\257\225\346\241\210\344\276\213.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第3-3节:Ai Agent 测试案例 +pay: https://t.zsxq.com/JFnzV +--- + +# 《Ai Agent》第3-3节:Ai Agent 测试案例 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/jI0BD](https://t.zsxq.com/jI0BD) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在项目中引入 Spring Ai 1.0.0 框架,通过编写测试案例的方式,了解 Ai Agent 的工作模式。 + +**那为什么要这么做呢?** + +通常对于软件设计的解决方案,我们都有一个这样共识,那就是目标结果驱动,最先搭建可运行的最小执行单元。因为软件设计原则,[康威定律](https://zh.wikipedia.org/wiki/%E5%BA%B7%E5%A8%81%E5%AE%9A%E5%BE%8B),也提到,`大的系统组织总是比小系统更倾向于分解`。当场景问题被拆解的越小以后,也就越容易被理解和处理。所以,我们要优先通过案例的方式,验证 Ai Agent 的工作模型和可执行方案。再通过这些案例,设计详细的流程和库表细节。 + +## 二、功能流程 + +如图,为整个 Ai Agent 的工作模型; + +
+ +
+ +- 概念:**Ai Agent 是整合多种技术手段的智能实体** ,其实现依赖于 Tools、MCP、Memory、RAG(Retrieval-Augmented Generation,检索增强生成) 等技术组件构建的智能体。并且每一个 Agent Client 又可以被连接通信,增强其 Agent 智能体能力。 +- 方案:这里我们基于 Spring AI 框架,通过编码的方式把模型、关键词、顾问角色、工具,放入到 LLM 客户端,构建 LLM 对话智能体。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-4\350\212\202\357\274\232\346\240\271\346\215\256AiAgent\346\241\210\344\276\213\357\274\214\350\256\276\350\256\241\345\272\223\350\241\250.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-4\350\212\202\357\274\232\346\240\271\346\215\256AiAgent\346\241\210\344\276\213\357\274\214\350\256\276\350\256\241\345\272\223\350\241\250.md" new file mode 100644 index 000000000..be57bd6c3 --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-4\350\212\202\357\274\232\346\240\271\346\215\256AiAgent\346\241\210\344\276\213\357\274\214\350\256\276\350\256\241\345\272\223\350\241\250.md" @@ -0,0 +1,39 @@ +--- +title: 【更】第3-4节:根据AiAgent案例,设计库表 +pay: https://t.zsxq.com/0tZhc +--- + +# 《Ai Agent》第3-4节:根据AiAgent案例,设计库表 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/Tx438](https://t.zsxq.com/Tx438) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +根据 Ai Agent 的代码案例,设计用于解耦,硬编码流程的库表。在后续的代码开发中,根据库表配置的 Ai Agent 流程所需的,模型、提示词、顾问、工具等,动态实例化出 Ai Agent 服务。 + +## 二、拆分设计 + +如图,为对应的Ai Agent 案例代码,映射出要拆分的库表设计; + +
+ +
+ +首先,整个代码构建的整个 Ai Agent 最小化单元服务,我们可以根据这样的服务信息设计出库表结构。 + +- 第一步,从上到下,OpenAiApi 是最基础单元结构,可以被多个 OpenAiChatModel 使用,它可以被拆分出第一张表。 +- 第二步,构建 OpenAiChatModel,这个阶段,需要 openAiApi、model对话模型、tool mcp 工具。其中`model对话模型`时一种固定固定资源,可以直接放到 ai_client_model 模型中,而 openAiApi、mcp 工具,都属于复杂配置,则需要额外的外部关联来衔接。也就是后面的 ai_client_config 配置,用于配置衔接关系。 +- 第三步,ChatClient 对话客户端,这部分的实例化过程都是和外部其他的资源关联,本身表设计只要有一个客户端的唯一id和客户端的描述介绍即可。 +- 第四步,给 mcp 增加一个表,mcp 服务是非常重要的,有 mcp 才有 agent 服务。mcp 的启动有 stido、sse 两种方式,每种方式都有对应的配置文件 json 数据。 +- 第五步,defaultSystem 系统提示词,需要单独拆分出来。提示词等于智能体的大脑,也有人说,其实 Ai Agent 就是 prompt 的堆叠,所以写提示词是很重要的。 +- 第六步,advisor 顾问角色,在 Spring Ai 框架中,以顾问的方式,访问记忆上下文,知识库资源,所以这部分也要单独设计库表。 +- 第七步,设计一个 ai_client_config,用于配置;api、model、client、prompt、mcp、advisor的衔接关系。 +- 第八步,设计 ai_agent、ai_agent_flow_config,也就是一个 ai agent,是可以连续调用多个 ai client 客户端的。 +- 第九步,设计 ai_agent_stask_schedule 任务,这是一种触达手段,可以把配置好的任务,让 task 定时执行,如自动发帖、系统异常巡检、舆情风险检测、系统配置变更、运营活动报表等。 +- 第十步,ai_client_rag_order,是知识库表,用于上传知识库做一个记录,这样顾问角色就可以访问知识库内容了。 + +注意;chat_client 客户端的初始化过程中,也可以增加 mcp 服务,这部分在 chat_model 模型构建中,也可以增加 mcp,选择在 chat_model 增加即可。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-5\350\212\202\357\274\232\345\244\232\346\225\260\346\215\256\346\272\220\345\222\214Mapper\351\205\215\347\275\256.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-5\350\212\202\357\274\232\345\244\232\346\225\260\346\215\256\346\272\220\345\222\214Mapper\351\205\215\347\275\256.md" new file mode 100644 index 000000000..b3427644d --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-5\350\212\202\357\274\232\345\244\232\346\225\260\346\215\256\346\272\220\345\222\214Mapper\351\205\215\347\275\256.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第3-5节:多数据源和Mapper配置 +pay: https://t.zsxq.com/099am +--- + +# 《Ai Agent》第3-5节:多数据源和Mapper配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/eYn4R](https://t.zsxq.com/eYn4R) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +为应用程序配置pgvector(向量库)、mysql(业务库)两套数据源,同时基于库表,编写基础设施层 Mapper 操作。 + +对于数据库表的 Mapper 编写,是一种固定的结构化代码,可以通过 MyBatis 工具生成,也可以使用 AI 编码工具处理。不过对于新人学习来说,更建议在这个阶段,通过手动的方式进行配置编写,这样可以更熟悉库表的设计和字段的理解。尤其是报错后,还可以基于报错排查错误增加编程经验。 + +## 二、功能流程 + +如图,两个数据源的配置和使用; + +
+ +
+ +- 首先,为了让应用程序具备多数据源链接,则需要增加一个扩展的 DataSourceConfig 配置类,来自己实现数据源的加载。这部分会替代原本配置到 yml 文件中,由 Spring 加载数据源的过程。 +- 之后,根据不同类型的数据源,注入到 AI 向量库使用场景和 MyBatis 业务使用场景中。这个过程类似于星球中 DB-Router 路由组件的课程。可以参考:[https://bugstack.cn/md/road-map/db-router.html](https://bugstack.cn/md/road-map/db-router.html) + diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-6\350\212\202\357\274\232\346\225\260\346\215\256\345\212\240\350\275\275\346\250\241\345\236\213\350\256\276\350\256\241.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-6\350\212\202\357\274\232\346\225\260\346\215\256\345\212\240\350\275\275\346\250\241\345\236\213\350\256\276\350\256\241.md" new file mode 100644 index 000000000..da40342cb --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-6\350\212\202\357\274\232\346\225\260\346\215\256\345\212\240\350\275\275\346\250\241\345\236\213\350\256\276\350\256\241.md" @@ -0,0 +1,33 @@ +--- +title: 【更】第3-6节:数据加载模型设计 +pay: https://t.zsxq.com/zrK3n +--- + +# 《Ai Agent》第3-6节:数据加载模型设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/USaZS](https://t.zsxq.com/USaZS) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在关于 Ai Agent 的功能实现中,有一个非常重要处理步骤,就是要想办法动态的实例化来自于用户配置的;API、对话模型、MCP、顾问角色以及提示词等。这也就是我们前面为什么要基于 ai agent case 案例,把代码抽象出库表配置。 + +好,那么到这一节,还要思考,怎么让程序来加载和实例化 Ai Agent 所需的各项组件。如,客户端的实例化、对话模型的实例化等。 + +注意;本节会引入星球组件项目《扳手工程》,通用设计模式框架。可以前置学习:[第2节:责任链和规则树通用模型框架](https://t.zsxq.com/o7IBm) + +## 二、功能流程 + +如图,Ai Agent 实现过程,数据加载策略设计; + +
+ +
+ +- 首先,整个 Ai Agent 的实例化过程,就是各项组件的创建和组装的过程。那么,为了让整体的实现代码更易于维护,我们可以把这样的创建过程,通过规则树的方式进行串联实现。而这部分需要的规则树,是不需要重复建设的,因为星球里的[《扳手工程组件项目》](https://t.zsxq.com/o7IBm),已经把这类的共性内容,凝练成了通用的组件,各个业务系统引用使用即可。所以,这部分建议刷下[《扳手工程组件项目》](https://t.zsxq.com/o7IBm),来看[第2节:责任链和规则树通用模型框架](https://t.zsxq.com/veRkQ) +- 之后,本节我们先把目标缩小到关于数据加载部分,因为后续所有的 Ai Agent 组件实例化的过程,都是需要基础数据的提供。所以组装数据就显得尤为重要了。 + + diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-7\350\212\202\357\274\232\345\212\250\346\200\201\345\256\236\344\276\213\345\214\226\345\256\242\346\210\267\347\253\257API.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-7\350\212\202\357\274\232\345\212\250\346\200\201\345\256\236\344\276\213\345\214\226\345\256\242\346\210\267\347\253\257API.md" new file mode 100644 index 000000000..7ca83a2bd --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-7\350\212\202\357\274\232\345\212\250\346\200\201\345\256\236\344\276\213\345\214\226\345\256\242\346\210\267\347\253\257API.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-7节:动态实例化客户端API +pay: https://t.zsxq.com/RdjkP +--- + +# 《Ai Agent》第3-7节:动态实例化客户端API + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/4r5r4](https://t.zsxq.com/4r5r4) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +完善数据加载操作,动态实例化`客户端API`(ai_client_api)并注册到 Spring 容器。 + +这是整个 armory 动态装配 Ai Agent 节点的第一步,涉及到了数据的获取,对象的创建和 Spring 容器的 Bean 对象注册。能看懂本节的操作,基本后续一直到整个 Ai Agent 构建也就都可以看懂了。 + +## 二、功能流程 + +如图,客户端API实例化过程设计; + +
+ +
+ +- 首先,整个 AI Agent 的实例化过程,就是各项组件的创建和组装的过程。为了让整体的实现代码更易于维护,我们把这样的创建过程,通过 规则树的方式 进行串联实现。这种设计模式的优势在于:模块化设计、易于扩展、代码复用度高。 +- 之后,从开始节点看,依次执行,数据构建节点、API构建节点。在 API 构建的过程中,会检查上下文中是否存在已经从数据库获取的数据,之后依次循环构建并注册到 Spring 容器。 diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-8\350\212\202\357\274\232\345\212\250\346\200\201\345\256\236\344\276\213\345\214\226\345\257\271\350\257\235\346\250\241\345\236\213.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-8\350\212\202\357\274\232\345\212\250\346\200\201\345\256\236\344\276\213\345\214\226\345\257\271\350\257\235\346\250\241\345\236\213.md" new file mode 100644 index 000000000..161eba5e9 --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-8\350\212\202\357\274\232\345\212\250\346\200\201\345\256\236\344\276\213\345\214\226\345\257\271\350\257\235\346\250\241\345\236\213.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第3-8节:动态实例化对话模型 +pay: https://t.zsxq.com/zv79l +--- + +# 《Ai Agent》第3-8节:动态实例化对话模型 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/4r5r4](https://t.zsxq.com/4r5r4) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +整个 ChatModel 对话模型,所需的元素包括;ai api、tool mcp 两块内容。那么本节我们就分别通过不同的 Node 节点实现这些元素的实例化和 ChatModel 构建。 + +## 二、功能流程 + +如图,ai api、tool mcp、model,实例化过程; + +
+ +
+ +- 首先,如图 RootNode 负责数据加载,将构建节点元素的数据依次加载到内存中(写入到上下文里) +- 之后,除了上一节完成的 API 节点处理后,开始创建 MCP 服务的创建,之后是 ChatModel 对话模块的创建。因为 ChatModel 创建的时候会需要用到的 api、mcp 两个元素。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/agent/\347\254\2543-9\350\212\202\357\274\232\345\212\250\346\200\201\345\256\236\344\276\213\345\214\226\345\257\271\350\257\235\345\256\242\346\210\267\347\253\257.md" "b/docs/md/project/ai-knowledge/agent/\347\254\2543-9\350\212\202\357\274\232\345\212\250\346\200\201\345\256\236\344\276\213\345\214\226\345\257\271\350\257\235\345\256\242\346\210\267\347\253\257.md" new file mode 100644 index 000000000..92f9ac939 --- /dev/null +++ "b/docs/md/project/ai-knowledge/agent/\347\254\2543-9\350\212\202\357\274\232\345\212\250\346\200\201\345\256\236\344\276\213\345\214\226\345\257\271\350\257\235\345\256\242\346\210\267\347\253\257.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第3-9节:实例化对话客户端 +pay: https://t.zsxq.com/VcM68 +--- + +# 《Ai Agent》第3-9节:实例化对话客户端 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/bePzF](https://t.zsxq.com/bePzF) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +经过前面一系列的准备工作,包括;api、mcp、model,本节我们要进行 advisor 顾问角色的实例化,之后进行 ChatClient 对话客户端的实例化。 + +## 二、功能流程 + +如图,整体 ChatClient 客户端实例化过程; + +
+ +
+ +- 首先,以构建 AiClientNode 的对话客户端为目的,已经完成了相关的元素实例化步骤。本节这里要处理的是,顾问角色的构建,以及构建 AiClientNode 节点。 +- 之后,AiClientNode 的构建,是关联了其他各项元素的,所以在构建时,需要在 AiClientNode 节点,从 Spring 容器通过 getBean 的方式,检索到对应的各项元素。 +- 注意,ai_client_system_prompt 系统提示词,需要修改为 Map 结构数据。这样更方便我们从数据里获取,哪些是属于当前 AiClientNode 构建时所需的元素。 \ No newline at end of file diff --git a/docs/md/project/ai-knowledge/ai-knowledge.md b/docs/md/project/ai-knowledge/ai-knowledge.md new file mode 100644 index 000000000..c98deb917 --- /dev/null +++ b/docs/md/project/ai-knowledge/ai-knowledge.md @@ -0,0 +1,324 @@ +--- +title: AI Agent 拖拉拽 + 动态配置 +lock: no +--- + +# 《AI Agent 拖拉拽 + 动态配置(RAG、MCP、Prompt)》 - 解析文档&Git仓库代码&AI工作流 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +刚上周,老板说:`”把咱们招聘里也加一条,具备AI应用开发能力的优先!“`。是呀,现在越来越多的企业都在用AI开发能力提效了,如;聊天软件增加一键唯独信息归档提取、工作文档资料携AI对话分析、工程SQL语句脚本辅运营自动完成数据处理、代码编写用AI完成自动评审等等。这些都是在AI的基础上在构建应用,以后也会越来越多!所以,具备AI应用开发能力,也是每个工程师最应该具备的基础能力了。 + +并且用不了多久,各大互联网企业都将大量的推进落地,自有 [MCP](https://github.com/modelcontextprotocol) 服务的实现,用于增强企业 AI 应用的提效能力。因为 [MCP](https://github.com/modelcontextprotocol) 的加入,可以让你;一条命令`帮研发`,调用应用系统日志、排查系统CPU负载、自主选择是否调度数据库信息。也可以一条命令`帮运营`,搞定复杂的SQL执行、导出报表、分析数据、完成促活营销券的自动化配置上架。这就是 [MCP](https://github.com/modelcontextprotocol) 的魅力!👍🏻 + +
+ +
+ +**那么牛,MCP 是什么?** + +专业的术语 `MCP = Model Context Protocol` 模型上下文协议,可实现应用与外部数据源和工具之间的无缝集成。无论您是构建 AI 驱动的 IDE、增强聊天界面还是创建自定义 AI 工作流,MCP 都提供了一种标准化的方式来连接他们所需的上下文。 + +`来吧,上图!让你看看它是啥!` + +
+ +
+ +- 首先,站在用户的使用视角,研发或者运营,可以通过话术描述,完成系列的 AI 工作流,并拿到最终的结果。这就是 MCP 最终为你提供的服务。 +- 那么,你可以想象,在日常的工作中,运营、研发、产品、测试等,都有非常多的重复非创作性的工作,占用了大量的时间成本。尤其是研发,写写代码,就有运营过来,帮我查个问题吧,小嘚嘚。但如果有这样的借助于 MCP 实现的 AI 工作流,就可以完成80%以上的工作量。 +- 之后,站在技术的实现视角,MCP 是一个标准结构框架,你可以按照它(Spring AI)提供的 SDK 开发方式,完成本地化 API 的接入开发。让 AI 有明确的方式调用各类 API 服务接口。如果没有 MCP 这会是一件很麻烦的事情。 + +**跟着小傅哥学习,从不走偏!😄** + +- 2022年底,ChatGPT 开始火爆。 +- 2023年2月,小傅哥,开启了第一个基于AI的项目,ChatGPT AI 问答助手项目。让所有伙伴,都能学习到 AI 如何开发应用。 +- 2023年4月,启动OpenAI(ChatGPT/ChatGLM)微服务应用体系构建大型项目,让大家可以用微信登录、微信支付/蓝兔支付,构建自己的可对外付费提供服务的 OpenAI。这一年上车学习的伙伴,很多做了自己的 AI 产品,除了提高编程技能,又小赚了一辆宝马。 +- 2024年7月,结合企业诉求,开启 OpenAI + Github Acitons,实现代码自动化评审。这一年,不少伙伴在自己的公司中都有落地,个人也得到了述职晋升。 +- 2025年3月,咱们再起启航,基于 Ollama 部署 DeepSeek,开发 RAG 知识库,解析文档和Git仓库代码。这个东西,将是企业中构建自己知识库的又一项非常重要的事情。有了知识库,AI 代码的自动评审,会更加精准,也可以辅助分析需求等。 + +那么,接下来小傅哥就细致的介绍下,本次开启的新项目,可以让大家学习到哪些知识,掌握哪些技术。 + +> Spring AI MCP 与 24年末发布,学习此 AI 应用开发项目,你将是第一批具备 Java AI 应用实战开发能力的人。竞争力,嘎嘎滴! + +## 一、能学到啥 + +这是一套综合`前后端 + Dev-Ops`,基于 Spring Ai 框架实现,Ai Agent 智能体。耗时7个多月,38节课程(`视频`+`文档`),从 RAG 到 MCP,再实现出互联网企业级,可编排的 Ai Agent 智能体,现已全部开发完成 + 部署上线。💐 + +该项目是结合当下最火的 Ollama、DeepSeek、SpringAI 等技术构建的 RAG 知识库实现。从前端到后端到 dev-ops 的全栈式功能手把手实现。 + +- 前端,基于 AI 工具,设计前端对话页面,完成 HTML、JS、TailwindCSS 的编码工作。 +- 前端,配置跨域服务接口,前后端分离实现 UI + 服务端接口对接。 +- 后端,构建双层架构,直接面向需求编码。让学习伙伴更轻松完成 RAG 知识库核心知识的学习。 +- 后端,基于 Spring AI 完成 DeepSeek、OpenAI 双模型的策略对接,处理文本向量的解析和存储。 +- 后端,使用 postgresql 存储切割文本向量数据,完成知识库的解析和存储。 +- 后端,处理多样文本`(.md、.sql、.txt、.word...)`的解析储存以及Git克隆代码库遍历切割存储。 +- 后端,使用 Redis 存储知识库标签,用于检索展示使用。 +- 后端,基于 Flux 编写流式会话接口,以及增加知识库检索功能。 +- 运维,基于 Docker 部署 Ollama 环境,完成 DeepSeek 大模型配置。 +- 运维,使用 Linux、Docker、Nginx 完成项目的打包、构建、上线! + +虽然,知识库都有很多现成的工具。但研发的能力不是在于功能应用,而是具备这样的开发技能储备,在有需要的时候,可以举手🙋🏻‍♀️”我会,我来做!“ + +> 此项目,全程视频手把手操作 + 全部的小册文档,你可以轻松上手学会这样一个项目! + +## 二、项目介绍 + +这是一套基于 Ollama DeepSeek 大模型构建的增强 RAG 知识库检索项目,在这套项目上,实现了除普通文档知识解析外,增加了 Git 代码库的拉取和解析,并提供操作接口。为工程师做项目开发时,`需求分析`、`研发设计`、`辅助编码`、`代码评审`、`风险评估`、`上线检测`等,做工程交付提效。 + +
+ +
+ +### 第1期,RAG 我们做了什么 + +在 《DeepSeek RAG 增强知识库》第1阶段,基于 Spring AI 0.8.1 开发了一套可以上传文件和Git仓库进行解析、切割、存储,到使用向量库完成 AI 的知识库问答系统。并最终通过 Docker 部署上线。 + +#### 1. 对话页面 + +
+ +
+ +- 这是全程视频手把手,带着大家通过AI工具,完成的UI设计实现课程会演示这个操作),实现的一款非常简单漂亮的UI效果。 +- 我们可以结合知识库,进行更加有效的提问。像是公司中,会把知识库提供出一个标准接口,给其他各个AI应用平台提供能力。 + +#### 2. 上传知识 + +
+ +
+ +- 上传知识,可以解析不同类型的知识库。 +- 除了课程提供的文档库、代码库,你可以增加其他的知识库,如;网页的解析,与网页内容对话。让我们的UI,增加一个侧边栏,读取当前网页内容,分析对话。这样在公司中的一些工程的日志,错误分析时,可以更快的处理。 + +#### 3. 解析知识 - 后台日志 + +
+ +
+ +- 上传知识后,可以看到日志信息。 +- 一套工程作为知识库是非常具有开发价值的,在我们做提问的时候就不需要,人工的去分析工程,而是直接使用了。 + +### 第2期,MCP 我们要做什么 + +与第2期相比,第1期可以称之为小试牛刀,让小伙伴们以最快、最快的往事,积累,运用 Spring AI 框架,开发自己的 RAG 知识库。~~也是方便有些死鬼,早点写到简历上~~ + +到了第2期,你就开始吃上细糠了,小傅哥会带着你升级 Spring AI 框架为 1.0.0-M6 最新版本,多模型配置和操作 PG 向量库,使用 GPU 搭建响应速度更好的 Ollama DeepSeek 大模型(秒级处理),以及对接官网 DeepSeek 的大模型和统一 one-api 对接方式。 + +但这还只是开始,随着基础框架的升级完成,我们将进入 MCP 服务的开发实现。通过 AI 指令,完成 AI 工作流,调度各项 MCP 处理我们的任务作业。如图,举例操作; + +
+ +
+ +- 基于 MCP 服务的开发和对接,通过 AI 工作流指令,完成数据的采集和存放动作。💡 聪明的小伙伴以及开始联想,基于这样的 AI 开发,可以替代很多的日常工作啦。**没想到吧,也把自己替代了** 但仍然,蠢蠢欲动(我不做,别人也做呀)!~~实现后,晋升又有的讲啦!简历也有东西写啦!~~ +- 有了 MCP 后,相当于把我们需要;在一个网页操作数据库查询数据、打开另外一个网页看天气预报,再手动的创建个文件把以上的信息获取后,复制粘贴到文件里。这一些列操作,都让 AI 通过 MCP 模型上下文协议进行处理。也就是 AI 可以调用后台接口啦! + +### 第3期,Agent 我们要做什么 + +RAG 教了,MCP 搞了,那么现在是时候,实现一套自动化的 Ai Agent 服务了。 + +如图,以通过数据库表动态配置的手段,完成相关物料的加载,包括;`模型(gpt-4.1/deepseek)`、`客户端`、`对话预设`、`执行规划(Planning)`、`顾问(记忆、RAG、日志)`、`工具(MCP`)等,在把单个 Client 串联,完成整个 Agent 调用链。这样一个 Agent 调用链可以以对话形式使用或通过 Agent 动态任务自动执行。 + +
+ +
+ +本项目分为,用户端、管理端和服务端,服务端统一提供接口能力,管理端维护 AI Agent 智能体配置、用户端提供使用服务。 + +#### 1. 登录界面 + +
+ +
+ +- 这一部分在数据库表增加了 admin_user 表,有配置登录账号和密码,可以简单做校验。 + +#### 2. 管理界面 + +
+ +
+ +- 管理后台目前提供了,代理管理(拖拉拽编排方式配置智能体),资源管理(model、client、mcp、advisor、prompt) +- 数据分析、系统设置,是样例,你可以继续扩展你所需要的内容。 + +#### 3. 代理管理 + +##### 3.1 代理列表 + +
+ +
+ +- 这里的代理列表,就是通过拖拉拽配置的智能体。可以点击【查看】看到明细,也可以【新建】,还可以删除。 +- 点击【加载】则是调用服务端,把数据加载到 Spring 容器,之后就可以使用了。 + +##### 3.2 代理配置 + +
+ +
+ +- 当你点击一个代理配置,则会展示出拖拉拽的数据到页面上。这部分会从数据库读取,之后展示出来,全部可视化。 +- 如果你点击了Save则会做出一份新的,之后对于旧的,你可以自己手动删除。 + +#### 4. 资源管理 + +
+ +
+ +- 资源管理下,是配置一个智能体所需的各项资源信息,你可以在这里进行维护。如,MCP 工具管理。 + +#### 5. 页面使用 + +##### 5.1 对话交流 + +
+ +
+ +##### 5.2 场景解析 + +
+ +
+ +##### 5.3 监控分析 + +
+ +
+ +- 配置后的智能体,可以在智能体选择里进行获取使用。之后进行提问。 +- 效果还不错,这里小傅哥验证了配置的智能体进行提问。 + +## 三、关于系统设计 + +本套系统设计,也是花费了非常大的心思。 + +### 1. 执行流程 + +
+ +
+ +在整个 Ai Agent 的实现中,小傅哥带着大家分析设计了4种方案,包括;固定执行的、循环执行的、智能分析决策的还有一个按照步骤规划的。这些流程都有适合于自己业务场景使用。在代码中也都有不同方案的实现,之后通过用户选择后进行动态化的策略调度。 + +### 2. 核心动作 + +#### 2.1 数据装配 + +
+ +
+ +- 首先,以构建 AiClientNode 的对话客户端为目的,已经完成了相关的元素实例化步骤。本节这里要处理的是,顾问角色的构建,以及构建 AiClientNode 节点。 +- 之后,AiClientNode 的构建,是关联了其他各项元素的,所以在构建时,需要在 AiClientNode 节点,从 Spring 容器通过 getBean 的方式,检索到对应的各项元素。 + +#### 2.2 动态调度 + +
+ +
+ +- 这里会根据用户的请求,进行策略路由,找到所需的 Ai Agent 执行策略进行处理。这里小傅哥也有意加入不同的策略,让大家可以看到很多的 Ai Agent 设计思路。 + +#### 2.3 执行策略(01) + +
+ +
+ +- 以程序启动为开始,进行自动化装配。这个过程我们先把一些想预先启动的数据库中的 agent 配置所需的 client 客户端进行服务初始化。之后写入到 Spring 容器,方便在执行 Agent 时进行使用。`前面有伙伴问,为什么把实例化的对象写入到 Spring 容器,这里就是原因` +- 客户端(UI),进行 POST 接口请求,这个过程需要封装一个 SSE 流式响应的接口,让 Step 1~4 各个执行步骤,把过程信息写入到流式接口。这里要注意,需要给接口返回的**对象**添加上对应的类型(什么步骤、什么节点、什么过程),以便于反馈给用户 Agent 在做什么。 + +#### 2.4 执行策略(02) + +
+ +
+ +- 这是其中的一种 Ai Agent 执行策略方式,通过用户的提问进行分析、规划、列出执行步骤,之后依次执行。 +- 所有的这些实现都有相应的代码,带着大家使用规则树框架清晰的实现出来。 + +## 四、课程目录 + +### 1. 课程目录 + +**第1阶段** spring-ai v0.8.1 - RAG 静态知识库(本阶段,需要配置附件的 setting.xml) + +- 第1节:关于 AI RAG 知识库项目介绍 +- 第2节:初始化知识库工程&提交代码 +- 第3节:Ollama DeepSeek 流式应答接口... +- 第4节:Ollama DeepSeek 流式应答页面... +- 第5节:Ollama RAG 知识库上传、解析和验证 +- 第6节:Ollama RAG 知识库接口服务实现 +- 第7节:基于AI工具,设计知识库UI和接口对接 +- 第8节:Git仓库代码库解析到知识库 +- 第9节:扩展OpenAI模型对接,以及完整AI对接 +- 第10节:云服务器部署知识库(Docker、Ngin... + +**第2阶段** spring-ai v1.0.0 - MCP 动态知识库 + +- 第11节:吃上细糠,升级SpringAI框架 +- 第12节:康庄大道,上手 AI MCP 工作... +- 第13节,道山学海,实现MCP自动发帖服务(... +- 第14节,海纳百川,上线MCP自动发帖服务 +- 第15节,川流不息,实现MCP微信公众号消息通知服务 +- 第16节:息息相通,MCP 服务部署上线(sse 模式) + +**第3阶段** spring-ai v1.0.0 - Ai Agent 进行中「如果着急面试,可以直接做3阶段,完成到13节很够面试啦」 + +- 第3-0节:Ai Agent 项目介绍和系统演示【最初版本,含完整代码】 +- 第3-1节,Ai Agent 业务流程、系统架构、库表设计说明 +- 第3-2节:初始化项目工程 +- 第3-3节:Ai Agent 测试案例 +- 第3-4节:根据 Ai Agent 案例,设计库表 +- 第3-5节:多数据源和Mapper配置 +- 第3-6节:数据加载模型设计 +- 第3-7节:动态实例化客户端API +- 第3-8节:动态实例化对话模型 +- 第3-9节:实例化对话客户端 +- 第3-10节:Agent执行链路分析 +- 第3-11节:Agent执行链路设计 +- 第3-12节:Agent服务接口和UI对接(第一版AutoAgent效果) +- 第3-13节,Agent-ELK日志分析场景 +- 第3-14节,Agent-Prometheus监控分析场景 +- 第3-15节:AgentFlow执行链路分析(扩展思路) +- 第3-16节:FlowAgent执行链路设计(扩展思路) +- 第3-17节:增加调度器策略执行Agent链路 +- 第3-18节:动态执行智能体任务 +- 第3-19节:拖拉拽编排数据存储 +- 第3-20节:Agent管理后台实现 +- 第3-21节:在云服务器部署上线 + +### 2. 编程环境 + +- JDK 17 ~ 21 +- Postgresql +- SpringBoot 3.2.3 - Spring AI 0.8.1 ~ 1.0.0+ +- Redis +- Docker +- Ollama + DeepSeek + GPU - +- RAG、MCP、Function Call + +课程包括文档 + 小册,全程视频带着做。课程地址:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +> 综上,你可以看到此套项目的完整的介绍,这些也都是企业里非常实用的技能积累。有希望提高自己的编程能力和面试材料的,可以马上加入学习。 + +--- + +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! + diff --git a/docs/md/project/ai-knowledge/ext/ai-agent-auto.md b/docs/md/project/ai-knowledge/ext/ai-agent-auto.md new file mode 100644 index 000000000..95cbe407e --- /dev/null +++ b/docs/md/project/ai-knowledge/ext/ai-agent-auto.md @@ -0,0 +1,137 @@ +--- +title: Ai Agent VS 字节扣子? +lock: no +--- + +# Ai Agent VS 字节扣子? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**媲美Dify,堪比扣子?🤔** 哈哈哈,经过哐哐的一顿卷,小傅哥这套 Ai Agent 项目,已经做出一版智能对话体(对接UI),在单一方面的功能体验上,确实可以和市面的 Ai Agent 产品握握手啦 🤝!效果很是不错,本文提供了视频和截图。 + +
+ +
+ +**什么是 Ai Agent?** + +做的越多理解的也越透彻,Ai Agent 更像是对人的行为的理解。一个人再有力气,也没法一天完全一年全部的工作,而是需要我们把这些工作,做好规划、定好目标、完成执行、结果检查,循序渐进的执行再到最终交付。 + +那么,Ai Agent 也是一样的,Ai Token 提升的在高(等于人的力气),也没法一次做完所有事情。所以在结合了 `Prompt(提示词)`、`Advisor(记忆、RAG)`、`Tool(MCP)`,也可以把一整个大任务,细分为独立的小块,每一块做好规划、执行、审查和交付。所以 Ai Agent 再趋向一个人的行为。 + +**跟着小傅哥,吃上一碗细糠!** 可能你也做过一些学习的项目,但往往这些项目都是一些功能逻辑的叠加和 CRUD 的拼装 + 好看一些的脸面(UI)。但是跟着小傅哥学习,给你的就不只是一个项目,还会带着你做架构,搞设计,用优雅的方式进行编码,深度积累编程思维和编码能力。 + +>🧧 文末已提供整套 Ai Agent 实战项目的全套代码 + 各个分支章节的文档(含视频),学习起来非常爽! + +## 一、先上效果 - 堪比扣子! + +😂 做的多了,也做的久了。我发现,Ai Agent 所有模块架构好后,剩下的就是 Prompt 提示词质量的对比。**Prompt = Ai Agent 大脑!** 目前小傅哥演示的这套自研的 Ai Agent 就是反复优化 Prompt 的结果。 + +### 1. 对话场景 - 通用场景 + +#### 1.1 截图效果 + +
+ +
+ +左侧是AI思考执行过程,右侧是最终执行结果。通过我们的提问,AI Agent 进行`分析`、`规划`、`执行`、`监督`,再到最终的结果产生。有了这样的步骤,最终的总结阶段数据就会更加准确。 + +#### 1.2 视频效果(对比扣子) + +视频:[https://www.bilibili.com/video/BV1VYbczDER6](https://www.bilibili.com/video/BV1VYbczDER6) + +### 2. 日志分析 - ELK 辅助提效(公司里非常需要这样的场景) + +#### 2.1 采集分析 + +
+ +
+ +如图,是一个系统日志 ELK + Ai Agent 的运行简图,通过 MCP 服务的对接,让 Ai Agent 具备检索日志的能力,再结合分析话术,以此来完成日志的自动化分析。 + +#### 2.2 数据案例 + +
+ +
+ +在互联网公司中,都会有一套类似 ELK 的分布式日志系统,之后各个应用会上报数据。研发在接收到系统报警和运营反馈线上问题的时候,研发就需要进入到 ELK 查看系统日志的情况,以此分析线上问题。 + +#### 2.3 智能分析 + +
+ +
+ +通过 Ai Agent 分析 ELK 应用系统分布式日志,自动排除出限流用户的相关信息,来辅助研发日常工作提效。 + +## 二、系统设计 - 这是一个正经项目! + +Ai Agent 会的模型架构会趋向稳定并形成标准,之后便是 Prompt + MCP + Client 多链路动态执行迭代和优化。所以,把架构定义好,具备强扩展性是非常有必要的,也是程序员👨🏻‍💻工作价值的体现。—— 堆功能只等于 demo 案例,驾驭架构解决复杂问题才是核心价值体现! + +### 1. 库表设计 + +
+ +
+ +
+ +
+ +- 这里小傅哥设计了一套非常灵活的 Ai Agent 库表结构,满足动态配置各项资源,再由程序动态化的随时加载和使用。 +- 有了这样的库表,我们就可以按需配置出多种使用类型的 Ai Agent,之后对话或者 Job 任务方式执行使用。 + +### 2. 数据加载 + +如图,整体 ChatClient 客户端实例化过程; + +
+ +
+ +- 首先,以构建 AiClientNode 的对话客户端为目的,已经完成了相关的元素实例化步骤。本节这里要处理的是,顾问角色的构建,以及构建 AiClientNode 节点。 +- 之后,AiClientNode 的构建,是关联了其他各项元素的,所以在构建时,需要在 AiClientNode 节点,从 Spring 容器通过 getBean 的方式,检索到对应的各项元素。 +- 注意,ai_client_system_prompt 系统提示词,需要修改为 Map 结构数据。这样更方便我们从数据里获取,哪些是属于当前 AiClientNode 构建时所需的元素。 + +### 3. 执行分析 + +如图,不同方案实现的 Agent 流程; + +
+ +
+ +Ai Agent 的处理过程也是分为几类的,用于适应不同的场景使用; + +1. 固定N个步骤,这类的一般是配置工作流的,提高任务执行的准确性。如,一些检索资料、发送帖子、处理通知等。 +2. 顺序循环调用,配置 Agent 要执行的多个 Client 端,以此顺序执行。适合一些简单的任务关系,并已经分配好的动作,类似于1的方式。 +3. 智能动态决策,这类是目前市面提供给大家使用的 Agent 比较常见的实现方式,它会动态的规划执行动作,完成行动步骤,观察执行结果,判断完成状态和步骤。并最终给出结果。 + +### 4. 功能架构 + +如图,从Agent服务的装配到接口调用和响应的关系图; + +
+ +
+ +- 以程序启动为开始,进行自动化装配。这个过程我们先把一些想预先启动的数据库中的 agent 配置所需的 client 客户端进行服务初始化。之后写入到 Spring 容器,方便在执行 Agent 时进行使用。`前面有伙伴问,为什么把实例化的对象写入到 Spring 容器,这里就是原因` +- 客户端(UI),进行 POST 接口请求,这个过程需要封装一个 SSE 流式响应的接口,让 Step 1~4 各个执行步骤,把过程信息写入到流式接口。这里要注意,需要给接口返回的**对象**添加上对应的类型(什么步骤、什么节点、什么过程),以便于反馈给用户 Agent 在做什么。 + +## 三、课程目录 + +整个课程分3个阶段讲解,包括;RAG、MCP,之后进入 Agent 阶段的学习。前面打好基础,后面进入应用。哪怕是小白,也可以跟着一起实战起来,而且每个阶段都有部署运行效果,越学越爽。 + +项目地址:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +
+ +
\ No newline at end of file diff --git a/docs/md/project/ai-knowledge/ext/ai-agent-flowgram.md b/docs/md/project/ai-knowledge/ext/ai-agent-flowgram.md new file mode 100644 index 000000000..b65d81c23 --- /dev/null +++ b/docs/md/project/ai-knowledge/ext/ai-agent-flowgram.md @@ -0,0 +1,84 @@ +--- +title: 有方案了,让自研 Ai Agent 可视化编排! +lock: no +--- + +# 有方案了,让自研 Ai Agent 可视化编排! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +每当一项新技术问世,市场上总会涌现出一批基于该技术的热门项目。以 AI 技术为例,紧随其后便出现了 `n8n`、`dify`、`coze` 等各类相关应用产品(当然,他们很优秀)。 + +然而,随着技术逐渐成熟和稳定,这些通用的解决方案往往会逐渐淡出市场。为什么呢?这是因为,各个企业下场后,都开始基于自身的业务,在细分领域,做自己的 Ai Agent 服务啦!所以,什么才是最重要的呢? + +
+ +
+ +**什么才是最重要的呢?** + +从研发的角度来看,最关键的是;学习业务场景经验、积累技术架构方案、落地应用项目能力。相比市场上层出不穷、功能纷繁复杂的各类产品,真正有价值的是对技术本质的持续储备。只有具备扎实的技术功底,形成闭环的技术体系,才能自身核心价值能力。 + +有了这样的能力积累,你可以在任何一个公司,任何一个场景,架构出一套最为符合的业务系统。所以,你的能力也等同于你的级别和薪资。 + +所以,对于最火的 Ai Agent 不要只是会用就行了,拿个项目过来,部署上就觉得可以了。你要做的,是把全套的业务弄透彻,实现的方案搞下来,怎么编码整明白。好啦,开冲,今天给这套 Ai Agent 加上可视化编排方案。 + +> 🧧 文末提供了全套 AI、RAG、MCP、Agent 项目、开发教程以及工程源码。此外还有非常多的互联网大厂项目(17个),都可以一并获取学习。 + +## 一、拖拉拽效果 + +鉴于整个 Ai Agent 的配置,需要一大堆东西,如;执行频次任务、客户端串联、模型选择、顾问角色知识库、MCP 工具、提示词等,有一套可视化拖拉拽配置的前端页面,就显得非常有必要了。 + +因此小傅哥调研了不少具备图形化编排能力的前端组件,发现一套 [flowgram.ai(官网有文档,可直接阅读)](https://flowgram.ai/) 可以很好的满足当前 Ai Agent 编排能力。😄 并且上手不困难,效果还不错。 + +
+ +
+ +- 首先,我们要基于 [flowgram.ai](https://flowgram.ai/) 框架,开发自己需要的 Node 节点(后面会有代码说明,方便伙伴扩展)。这里增加了;task、agent、client、too-mcp、model。 +- 之后,在页面点击添加节点,并选择好每个节点,应该配置的属性信息,以及连接节点关系。 +- 最后,点击保存,他会给你一个json 对象,按照对象的结构,创建服务端接口即可接收和保存。(相关保存操作,会在后续课程中添加,如果有诉求可以先把前端代码下载下去,对照json开发接口即可) + +> 本套全段代码 [ai-agent-station-front](#) 已经添加到课程中,可以进入获取。地址:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +## 二、怎么添加节点 + +### 1. 下载前端工程 + +首先,你要下载 [ai-agent-station-front](https://gitcode.net/KnowledgePlanet/ai-agent-station-front) 到本地使用 trae.ai 打开。如果没有提示你,要自己手动执行 npm install 初始化工程。 + +
+ +
+ +- 首先,[flowgram.ai](https://flowgram.ai/) 官网提供了各种案例,可以下载一个进行扩展。这里小傅哥就是下载好的一个,之后添加我们需要的节点。 +- 之后,docs 下的 ai-agent-station.sql 为的是让 ai 可以使用,自动创建 node 节点的(下文有演示)。 +- 另外,这里的代码,对于稍微有些 react 编码基础的伙伴,是可以非常方便看懂的。后端工程师,如果不懂也没关系,让 ai 来解答以及操作。 + +### 2. Ai 编码,添加节点 + +
+ +
+ +- 首先,下载一份国际版 trae.ai,这里有使用文档。[https://bugstack.cn/md/road-map/trae.html](https://bugstack.cn/md/road-map/trae.html) 使用 cursor 也可以,其实这东西,重要的就是好用的模型。 +- 之后,nodes 下是各种节点,我们可以拖拽一份让 ai 编码参考。并告诉ai,以哪个库表信息来编写新的节点。(有时候可能有问题,如果有问题,可以手动修改下) + +## 三、点击保存,查看json + +
+ +
+ +
+ +
+ +- 点击保存,就可以拿到节点和链接的关系数据了。这份数据是可以和数据库对应上。 +- 后续课程从0到1的实现过程中,会和后端接口联动,存储数据。现在你也可以先下载前端代码,尝试编码。 + diff --git a/docs/md/project/ai-knowledge/ext/ai-agent-job.md b/docs/md/project/ai-knowledge/ext/ai-agent-job.md new file mode 100644 index 000000000..01634d737 --- /dev/null +++ b/docs/md/project/ai-knowledge/ext/ai-agent-job.md @@ -0,0 +1,97 @@ +--- +title: 阶段性总结,Ai Agent 的重要性 +lock: no +--- + +# 阶段性总结,Ai Agent 的重要性 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +还好,还好早早的就开始了 AI 应用技术的储备,让我吃了互联网职场的第一碗创新类应用的技术饭。 + +其实从最早 OpenAI 发布 ChatGPT 开始,我就开始使用其接口陆续开发了应用,2022年年尾-开发智能问答助手、2023年年初-OpenAI应用项目(可支付买额度对话)、2023年终-OpenAI 代码自动评审。 + +可以说,在 AI 应用类开发这条路上,我一直走的很快也很靠前。甚至,我也思考怎么让 AI 识别接口,具备调用能力,而不是就只是做一些对话类操作。直至2024年他来啦! + +2024年11月份,Anthropic 公司推出了 MCP(Model Context Protocol,模型上下文协议)开放标准协议(JSON-RPC 2.0),其核心目标是通过提供一个标准化的接口,使AI模型能够无缝地访问本地和远程资源。 + +如果没有 MCP 可以说就没有 AI Agent 智能体,也不会有现在那么多的结合于 AI 来为工作提效的场景。 + +所以,当我看到 AI MCP 那一刻,我是很兴奋。也在 AI MCP 协议出了不久,立马筹备新的 AI Agent 项目,让大家都能学习到这一技术。而且我也早早的告诉过大家 **用不了多久,各大互联网企业都将大量的推进落地,自有 MCP 服务的实现,用于增强企业 AI 应用的提效能力。** 随后,我们看到了阿里支付宝 MCP、高德地图 MCP、百度搜索 MCP,等等各类 MCP 服务如雨后春笋一般。再往后各个公司推出了各种的 Ai Agent 能力,因为 AI 可以调用 MCP 服务,真正的帮我们做一些事情。 + +现在,公司里随便一个功能服务接口(RPC、HTTP),都可以通过简单配置走 MCP 网关转换为 MCP 协议接口能力,让 AI Agent 通过配置即可完成调用。也就是说,通过配置接口 + Prompt 提示词,既可以承接绝对部分日常工作,如;客诉排查、日志分析、监控巡检、文档评审、单测开发等等。 + +老板总说,我需要的不只是 AI 开发能力的人才,而且他还要懂得业务,具备应用项目落地能力。这样才能为我们企业的场景提效,而不是天马行空。所以,想学 AI 的伙伴来说,不要只是一头扎入到 AI 里,也要多积累业务场景经验。 + +## 一、市面的开源资料 + +**86.5k Star!** 关注 Ai Agent 实现的人超级多,也都知道这是一个热门方向。项目:[https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools](https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools) + +
+ +
+ +这是一款开源系统提示词项目,包含了市面的热门产品 Trae、Cursor、Claude、v0 等20多款 Ai Agent 项目的提示词 + MCP Tool。非常适合需要做 Ai Agent 项目的伙伴借鉴使用。 + +小傅哥还复制下来 Trae.ai 提示词(不过可能不是最新版)的内容进行本地验证,配置百度搜索 MCP 服务 + 本地文件处理。经过提问后,可以在本地创建出对应的文件夹内容,如果在结合进去 Shell 脚本能力,会更好。如果想要更好的效果,可以完全配置出一套 Trae 所需的工具服务组,那么效果会更好。 + +
+ +
+ +目前 Ai Agent 的开发实现,主要为;`分析`、`规划`、`执行`、`验证`,以及在这个过程中循环检测执行,对结果进行确认校验继续分析。可能将来也会由 OpenAI 主导,设计一套 Ai Agent 框架结构组件,那么 Ai Agent 的开发门口将更低也会更通用(目前就有不少这样的 Auto Agent 框架)。 + +## 二、也快接近尾声啦 + +2025年3月3日,小傅哥带着大家从 RAG 开始,之后进入 MCP,后来又做到 Ai Agent,持续了半年多。这块也快收尾了,核心功能都已经完成,陆续的开始做一些收尾的章节。 + +
+ +
+ +- RAG 10节、MCP 6节、Ai Agent 18节(还有待更新的),目前整体34节,后续整体都做完预计到40节,10.1假期后差不多。 + +## 三、总结下项目内容 + +### 1. 页面效果 + +
+ +
+ +- 如图,用户可以自己选择一个对话场景的智能体,之后后端会根据用户的请求进行动态化策略调度。 +- 目前,已经添加的场景有;CSDN发帖 + 通知(同类小红书也可以做)、智能对话分析、ELK日志检索分析、智能监控分析服务。只要你学习了这套系统,就可以设计出你所需要的 Ai Agent 使用场景。 + +### 2. 执行流程 + +
+ +
+ +在整个 Ai Agent 的实现中,小傅哥带着大家分析设计了4种方案,包括;固定执行的、循环执行的、智能分析决策的还有一个按照步骤规划的。这些流程都有适合于自己业务场景使用。在代码中也都有不同方案的实现,之后通过用户选择后进行动态化的策略调度。 + +### 3. 核心动作 + +#### 3.1 动态调度 + +
+ +
+这里会根据用户的请求,进行策略路由,找到所需的 Ai Agent 执行策略进行处理。这里小傅哥也有意加入不同的策略,让大家可以看到很多的 Ai Agent 设计思路。 + +#### 3.2 执行策略 + +
+ +
+ +- 这是其中的一种 Ai Agent 执行策略方式,通过用户的提问进行分析、规划、列出执行步骤,之后依次执行。 +- 所有的这些实现都有相应的代码,带着大家使用规则树框架清晰的实现出来。 + +好啦,欢迎感兴趣的伙伴一起加入学习,小傅哥的社群,有非常多的实战项目,涵盖;业务、组件、框架、源码、开源、创新等,让你加入后,就等同于加入一个互联网大厂的核心项目组,各项信息都能全部接触到,甚至包括产品 PRD 文档。**因为小傅哥就是大厂架构师,所以也是按照一个我所在的核心的组的方式来给大家建设与之匹配的资料内容。** + diff --git a/docs/md/project/ai-knowledge/ext/ai-agent-mcp-auth.md b/docs/md/project/ai-knowledge/ext/ai-agent-mcp-auth.md new file mode 100644 index 000000000..381acbf09 --- /dev/null +++ b/docs/md/project/ai-knowledge/ext/ai-agent-mcp-auth.md @@ -0,0 +1,450 @@ +--- +title: 给 MCP 服务加上安全认证! +lock: no +--- + +# 给 MCP 服务加上安全认证! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +刚刚过去两个月,市面的 MCP 服务,如雨后春笋一般不断涌现出来,包括;`百度`、`高德`、`网盘`、`支付宝`。这些 MCP 服务,可以让我们基于 Spring AI 框架构建的 Agent 具备非常丰富的使用功能。同时这也说明,程序员👨🏻‍💻,应该具备开发 MCP 服务的能力,Spring AI 让 Java 再次牛逼! + +
+ +
+ +>关于 RAG、MCP、Agent 是什么,这里小傅哥已经编写过了全套的教程,可以进入学习;[https://bugstack.cn/md/project/ai-knowledge/ai-knowledge.html](https://bugstack.cn/md/project/ai-knowledge/ai-knowledge.html) + +本节小傅哥主要给大家分享,关于市面上这些标准的带有验证权限的 MCP 服务,怎么使用 Spring AI 进行对接。同时我们自己开发的 MCP 服务,怎么加上权限校验。 + +## 一、举例,对接高德地图 MCP + +高德地图 MCP Server; + +```java +{ + "mcpServers": { + "amap-amap-sse": { + "url": "https://mcp.amap.com/sse?key=您在高德官网上申请的key" + } + } +} +``` + +- 官网:[https://lbs.amap.com/api/mcp-server/gettingstarted](https://lbs.amap.com/api/mcp-server/gettingstarted) - `官网提供了创建对接 Key` + +### 1. 代码使用示例 + +```java +@Configuration +public class McpConfig { + + @Bean + public List mcpClientTransport() { + McpClientTransport transport = HttpClientSseClientTransport + .builder("https://mcp.amap.com") + .sseEndpoint("/sse?key=") + .objectMapper(new ObjectMapper()) + .build(); + + return Collections.singletonList(new NamedClientMcpTransport("amap", transport)); + } + +} +``` + +- 对接时,需要设定 sseEndpoint 如果不设定个,Spring AI 默认是对 builder 的 baseUrl 值添加 `/sse` 的。 +- 所以,如果你要对接外部带有验证权限的 MCP 服务,需要手动设置下 sseEndpoint 值。 + +### 2. 项目中的配置 + +小傅哥,带着大家做的 Ai Agent,也支持了外部的这些带有权限校验的 MCP 服务。你可以,以多种方式进行配置。如; + +```java +{ + "baseUri":"https://mcp.amap.com", + "sseEndpoint":"/sse?key=801aabf79ed0ff78603cfe85****" +} +``` + +```java +{ + "baseUri":"https://mcp.amap.com", + "sseEndpoint":"/sse?key=801aabf79ed0ff78603cfe85****" +} +``` + +- 以上两种配置方式,在 ai-agent-station 都做了兼容处理。以下是兼容代码,学习这部分项目的伙伴,可以直接阅读课程代码。 + +```java +@Slf4j +@Component +public class AiClientToolMcpNode extends AbstractArmorySupport { + + // ... 省略部分代码 + + protected McpSyncClient createMcpSyncClient(AiClientToolMcpVO aiClientToolMcpVO) { + String transportType = aiClientToolMcpVO.getTransportType(); + + switch (transportType) { + case "sse" -> { + AiClientToolMcpVO.TransportConfigSse transportConfigSse = aiClientToolMcpVO.getTransportConfigSse(); + // http://127.0.0.1:9999/sse?apikey=DElk89iu8Ehhnbu + String originalBaseUri = transportConfigSse.getBaseUri(); + String baseUri; + String sseEndpoint; + + int queryParamStartIndex = originalBaseUri.indexOf("sse"); + if (queryParamStartIndex != -1) { + baseUri = originalBaseUri.substring(0, queryParamStartIndex - 1); + sseEndpoint = originalBaseUri.substring(queryParamStartIndex - 1); + } else { + baseUri = originalBaseUri; + sseEndpoint = transportConfigSse.getSseEndpoint(); + } + + sseEndpoint = StringUtils.isBlank(sseEndpoint) ? "/sse" : sseEndpoint; + + HttpClientSseClientTransport sseClientTransport = HttpClientSseClientTransport + .builder(baseUri) // 使用截取后的 baseUri + .sseEndpoint(sseEndpoint) // 使用截取或默认的 sseEndpoint + .build(); + + McpSyncClient mcpSyncClient = McpClient.sync(sseClientTransport).requestTimeout(Duration.ofMinutes(aiClientToolMcpVO.getRequestTimeout())).build(); + var init_sse = mcpSyncClient.initialize(); + log.info("Tool SSE MCP Initialized {}", init_sse); + return mcpSyncClient; + } + case "stdio" -> { + AiClientToolMcpVO.TransportConfigStdio transportConfigStdio = aiClientToolMcpVO.getTransportConfigStdio(); + Map stdioMap = transportConfigStdio.getStdio(); + AiClientToolMcpVO.TransportConfigStdio.Stdio stdio = stdioMap.get(aiClientToolMcpVO.getMcpName()); + + // https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem + var stdioParams = ServerParameters.builder(stdio.getCommand()) + .args(stdio.getArgs()) + .build(); + var mcpClient = McpClient.sync(new StdioClientTransport(stdioParams)) + .requestTimeout(Duration.ofSeconds(aiClientToolMcpVO.getRequestTimeout())).build(); + var init_stdio = mcpClient.initialize(); + log.info("Tool Stdio MCP Initialized {}", init_stdio); + return mcpClient; + } + } + + throw new RuntimeException("err! transportType " + transportType + " not exist!"); + } + +} +``` + +- 以上代码,是为了自动化构建 MCP 服务的,其中 case sse 的部分,会对 url 进行拆分,如果本身 url 配置了校验权限,则不会从另外一个参数获取,否则从另外参数拼接。这样就可以很好的扩展用户配置时的多样性问题了。 + +>以上是关于带有权限校验的 MCP 服务配置的问题,接下来,我们要说下怎么自己开发一个带有权限校验 + +## 二、实现,带有权限校验的 MCP 服务 + +首先,Spring AI 是有意提供基于自家的 OAuth2 框架,完成 MCP 服务的多样性权限校验的。不过目前提供的方案能用,但不算成熟。 + +
+ +
+ +官网:[https://spring.io/blog/2025/05/19/spring-ai-mcp-client-oauth2](https://spring.io/blog/2025/05/19/spring-ai-mcp-client-oauth2) + +### 1. 基于 OAuth2 认证 + +#### 1.1 工程结构 + +
+ +
+ +- 工程:[https://gitcode.net/KnowledgePlanet/mcp-server-auth](https://gitcode.net/KnowledgePlanet/mcp-server-auth) - `面向于学习 ai-agent-station 的伙伴` +- 使用 OAuth2 基于 Spring MVC 的方式到也简单,知道添加配置即可。 + +#### 1.2 所需的 POM 文件 + +```pom + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + + org.springframework.boot + spring-boot-starter-oauth2-authorization-server + + + + org.springframework.ai + spring-ai-starter-mcp-server-webmvc + +``` + +#### 1.3 测试验证 + +```java +@Slf4j +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "server.shutdown=immediate") +public class ApiTest { + + @LocalServerPort + private int port; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + public void test_access_token() throws IOException, InterruptedException { + String token = obtainAccessToken(); + log.info("token:{}", token); + // eyJraWQiOiJiMWQ0MGIxNi1hOTYzLTQ2NmYtYTVkOC02NGRjMzg0ODljYWEiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJtY3AtY2xpZW50IiwiYXVkIjoibWNwLWNsaWVudCIsIm5iZiI6MTc0ODA1MTc1NiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1ODA5OCIsImV4cCI6MTc0ODA1MjA1NiwiaWF0IjoxNzQ4MDUxNzU2LCJqdGkiOiI5NjY4ZmZkMi0wNjQ2LTRiNmItODQ4Ni1jYzk3ZjMxNTdmOTEifQ.CG4GYai_NYkmfcqmNi-_HYG06Kan04uNSsC2ivn_eC9Ra6xMKYTs9KIT7k5lKxSFRUOPI7K0zJNVvNXrrIe0iFl-csrG2vGukNTGTPMxtUi2hheBMRbnvjvuojW4DeOEE8UOpdA6uow67ucwcymTlDXE-k7OjRZeyp7UdVz2WyoDFQhLB6ihLbDSj5puAZxfNocirRzo36gmW243aW9f1gugPUcpND-oobc2q8xyBG2cX2AlGXUSS-v9PLjHr2W2smFTKHHGwu7FpMMBnJLUT5gZD0llIg6yqro91nFaAFOpGHXjRZYgVjkRlzxx08Zuquva9PbStxbUl2j8hI43_Q + + var client = HttpClient.newHttpClient(); + + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + port + "/sse")) + .header("Accept", "text/event-stream") + .header("Authorization", "Bearer " + token) + .GET() + .build(); + + var responseCode = new AtomicInteger(-1); + var sseRequest = client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply(response -> { + responseCode.set(response.statusCode()); + if (response.statusCode() == 200) { + log.info("response:{}", JSON.toJSONString(response)); + return response; + } + else { + throw new RuntimeException("Failed to connect to SSE endpoint: " + response.statusCode()); + } + }); + + await().atMost(Duration.ofSeconds(1)).until(sseRequest::isDone); + assertThat(sseRequest).isCompleted(); + assertThat(responseCode).hasValue(200); + } + + private String obtainAccessToken() throws IOException, InterruptedException { + var client = HttpClient.newHttpClient(); + + var clearTextCredentials = "mcp-client:secret".getBytes(StandardCharsets.UTF_8); + var credentials = new String(Base64.getUrlEncoder().encode(clearTextCredentials)); + var request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:" + port + "/oauth2/token")) + .header("Authorization", "Basic " + credentials) + .header("Content-Type", "application/x-www-form-urlencoded") + .POST(ofString("grant_type=client_credentials")) + .build(); + + var rawResponse = client.send(request, HttpResponse.BodyHandlers.ofString()).body(); + + Map response = objectMapper.readValue(rawResponse, Map.class); + return response.get("access_token"); + } + +} +``` + +- 加上 OAuth2 以后,就需要获取并设置 accessToken 才能访问 sse 服务了。 + +### 2. 基于网关实现 + +其实我们到不非得依赖于 Spring OAuth2 往 MCP 服务里在添加一些其他的东西。倒不如直接走网关,让网关来管理权限,MCP 服务只做服务的事情就好。 + +这里我们基于 Nginx 来配置验证功能,当然你可以在学习本节的案例后,配置任何其他的网关来管理你的 MCP 服务。 + +注意,这里的前置条件为你已经跟着小傅哥,至少完成了一个 MCP 服务。课程;[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +当我们有了一套基于 sse 形式访问的 mcp 后,我们是可以给这套 mcp 基于 nginx 转发的形式进行访问后面真实的 mcp 服务的。在转发的过程中,拿到用户在地址 `http://127.0.0.1:9999/sse?apikey=DElk89iu8Ehhnbu` mcp 服务后面拼接的 apikey,并对 apikey 进行验证。 + +#### 2.1 配置工程 + +
+ +
+ +- 在 ai-agent-station 项目下,提供了 dev-ops-v2 配置 mcp 服务转发和验证能力。 +- 注意,部署的时候,要把 mcp.localhost.conf 转发的 mcp 服务的地址,更换为你的地址。 +- 另外,每一个 mcp.localhost.conf 还可以配置域名,这样就达到了高德地图 mcp 访问的效果。举例;`https://fatie.mcp.bugstack.cn/sse/apikey=*******` + +#### 2.2 服务转发&校验 + +```java +# 可以负载服务 +upstream backend_servers { + server 192.168.1.108:8101; +} + +server { + listen 80; + + server_name 192.168.1.104; # 修改为你的实际服务器 IP 或域名【域名需要备案】 + + location /sse { + # 验证apikey参数,这个apikey也可以对接服务端接口来处理。 + if ($arg_apikey != "DElk89iu8Ehhnbu") { + return 403; # 如果apikey不正确,返回403禁止访问 + } + + # 重写URL,去掉apikey参数 + rewrite ^(/sse/)\?apikey=.* $1 break; + + proxy_pass http://backend_servers; # 将请求代理到上游服务器组 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /mcp/message { + proxy_pass http://backend_servers; # 将请求代理到上游服务器组 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + +} +``` + +- 特别注意,mcp 服务是有2个步骤的,一个是 sse 访问,还有一个 mcp/message 的处理。我们只需要对 sse 的请求进行验证即可。 +- `/sse` 请求路径,需要会提取 apikey 与 nginx 配置的值进行对比,如果不正确则会返回一个 403 禁止访问,通过则放行。 +- 之后重写 url 地址,让转发到本身 mcp 的地址是干净的。从 `http://127.0.0.1:9999/sse?apikey=DElk89iu8Ehhnbu` 验证转发后为 `http://192.168.1.108:8101/sse` + +#### 2.3 功能验证 + +首先,要确保你的 mcp 服务是可以使用的。如,访问;`http://192.168.1.108:8101/sse` 可以获得到结果。 + +
+ +
+- 如图,验证成功。我们可以通过转发的方式进行验证和使用。 +- 另外,有了转发和验证,你原本的服务,sse 8101 就不用对外了。只有你的网关(nginx)可以访问即可。这样就可以控制权限了。 + +#### 2.4 动态验证 + +那么,目前我们配置的nginx 转发这不是一个固定的权限账号吗,怎么让不同的接入方都申请一个秘钥key来使用呢?这里我们需要使用到 nginx 的 auth 认证模块。 + +```java +# 可以负载服务 +upstream backend_servers { + server 192.168.1.108:8101; +} + +server { + listen 80; + + server_name 192.168.1.104; # 修改为你的实际服务器 IP 或域名【域名需要备案】 + + location /sse { + auth_request /auth; + + # 重写URL,去掉apikey参数 + rewrite ^(/sse/)\?apikey=.* $1 break; + + proxy_pass http://backend_servers; # 将请求代理到上游服务器组 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /mcp/message { + proxy_pass http://backend_servers; # 将请求代理到上游服务器组 + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location = /auth { + # 发送子请求到HTTP服务,验证客户端的凭据,返回响应码 + internal; + # 设置参数 + set $query ''; + if ($request_uri ~* "[^\?]+\?(.*)$") { + set $query $1; + } + # 验证成功,返回200 OK + proxy_pass http://207.246.123.*:8090/auth/token?$query; + # 发送原始请求 + proxy_pass_request_body off; + # 清空 Content-Type + proxy_set_header Content-Type ""; + } + +} + +``` + +- 在访问 `/sse` 的时候,增加 auth 认证,auth 来访问本地一个 http 接口。你可以是 SpringBoot 实现的接口。这个接口负责验证你的秘钥是否正确。同时你的 SpringBoot 服务还可以提供出一个创建秘钥的平台,让接入方使用。 +- 其实类似这样的场景,使用功能更加丰富的 api 网关都是自带的,或者 github 一些专门为 mcp 做网关服务的也都有。 + +## 三、增强,学习 rag、mcp、agent + +小傅哥,已经为你准备好了一套 AI RAG、MCP、Agent 实践编程课程,使用 Java + Spring AI 框架,增强自己的 AI 应用开发能力,迅速囤积编程技能,满足各个公司招聘时对AI应用类开发的要求!如下,课程目录,全程文档小册 + 视频带着你从0到1学习。 + +#### **第1期 RAG Spring AI 0.8.1 - 完结** + +1. 【更】AI RAG 知识库,项目介绍&需求分析&环境说明 +2. 【更】初始化知识库工程&部署模型&提交代码 +3. 【更】Ollama DeepSeek 流式应答接口实现 +4. 【更】Ollama DeepSeek 流式应答页面对接 +5. 【更】Ollama RAG 知识库上传、解析和验证 +6. 【更】Ollama RAG 知识库接口服务实现 +7. 【更】基于AI工具,设计前端UI和接口对接 +8. 【更】Git仓库代码库解析到知识库并完善UI对接 +9. 【更】扩展OpenAI模型对接,以及完整AI对接 +10. 【更】云服务器部署知识库(Docker、Nginx) + +#### **第2期 MCP Spring AI 1.0.0 - 完结** + +1. 【更】吃上细糠,升级SpringAI框架 +2. 【更】康庄大道,上手 AI MCP 工作流 +3. 【更】道山学海,实现MCP自动发帖服务(stdio) +4. 【更】海纳百川,上线MCP自动发帖服务 +5. 【更】川流不息,实现MCP微信公众号消息通知服务 +6. 【更】息息相通,MCP 服务部署上线(sse 模式) + +#### 第3期 Agent Spring AI 1.0.0 - 进行中【全套源码和部署已提供】 + +1. 第3-0节,AiAgent项目介绍和系统演示 +2. 第3-1节,Ai Agent 业务流程、系统架构、库表设计说明 +3. 第3-2节,初始化工程和库表dao等,提交代码,讲解代码库使用 +4. 第3-3节,硬编码方式讲解 Ai Agent 构建,为后续拆分做准备 +5. 第3-4节,引入扳手工程,规则模型,整体设计Agent预热装配 +6. 第3-5节,规则节点,RootNode 异步加载数据 +7. 第3-6节,规则节点,AiClientToolMcpNode 工具MCP服务构建 +8. 第3-7节,规则节点,AiClientAdvisorNode 顾问角色服务构建 +9. 第3-8节,规则节点,AiClientModelNode 模型构建 bean 对象 +10. 第3-9节,规则节点,AiClientNode 客户端构建 +11. 第3-10节,Agent 服务预热和对话接口封装,使用验证 +12. 第3-11节,知识库接口封装和使用 +13. 第3-12节,智能体动态任务构建 +14. 第3-13节,Admin 管理端 API 接口讲解(CRUD) +15. 第3-14节,Ai Agent 对话与 UI 页面对接 +16. 第3-15节,构建镜像,上线云服务器 +17. 第3-16节,Agent 场景玩法分享 +18. ... 更多内容,随着课程开始逐步更新。 + +课程详细介绍:[https://mp.weixin.qq.com/s/j_G32TDfM_l-S76Wo0zPXw](https://mp.weixin.qq.com/s/j_G32TDfM_l-S76Wo0zPXw) + diff --git a/docs/md/project/ai-knowledge/ext/ai-agent-notes.md b/docs/md/project/ai-knowledge/ext/ai-agent-notes.md new file mode 100644 index 000000000..970b543dc --- /dev/null +++ b/docs/md/project/ai-knowledge/ext/ai-agent-notes.md @@ -0,0 +1,84 @@ +--- +title: Ai Agent 新项目,你要的简历模板来啦! +lock: no +--- + +# Ai Agent 新项目,你要的简历模板来啦! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**简历怎么写?简历怎么写?简历怎么写?** 这群死鬼,一直催小傅哥,想让小傅哥把饭🍚喂嘴里。没办法,兄弟们着急秋招写简历了,让自己写总是感觉慌,不知道从哪下手。好啦,它来了,它来了,行了吧! + +
+ +
+ +**你就说吧,各个大厂都在做,它能不重要?** + +Dify、Coze、Claude,京东还开源了 JoyAgent,市面上互联网大厂做的 Ai Agent 产品可以说是如雨后春笋一般,越来越多了。Ai Agent 也成了互联网标配的业务场景,为各个应用业务提效,所以结于业务场景来看,Ai Agent 是有战略意义的。 + +所以,你想面试互联网类公司,在简历上多一笔关于 Ai 类的场景实现,总是能匹配到公司里更多的部门岗位。尤其是具备一些 Ai Agent 开发能力的,在现在看来,可以说是非常亮眼的存在。就趁现在吧,该让自己的简历增强下啦!🌶 + +>接下来,我会站在一个求职者的角度进行项目介绍和简历内容展示。可以作为你的参考。 + +## 一、运行效果 + +先看一眼系统架构和运行效果,之后我们对项目进行介绍和简历编写。—— 面试时,可能也会有面试官看你的项目运行效果,你可以把项目上线,也可以写一个技术博客文章,来介绍你做的项目。 + +
+ +
+ +
+ +
+ +
+ +
+ +- 更多可查看,[关于 Ai Agent 项目介绍](https://mp.weixin.qq.com/s/S3UJY0aWbulQ2OZH9XVYmw) + +## 二、项目介绍 + +面试官您好,本套 Ai Agent 综合智能体项目,主要为业务应用系统提效而构建,包括;需求文档分析、代码评审(可结合 openai 代码自动评审)、文档资料编写(+消息通知)、ELK 日志检索 + 普罗米修斯监控的智能 Ai Agent 分析等功能。 + +整套项目,抽象设计拆分了 Ai Agent 执行过程所需的各项组件(Advisor、Prompt、MCP)能力到数据库表中,使其具备自由配置编排组装的特性。以此方式可结合应用中实际场景诉求,编排类似 Diff 和智能分析 Coze 能力,达到需要什么场景就配置什么场景的 Ai Auto Agent。 + +该项目,在架构设计上使用了 DDD 分层架构进行设计,运用了组合模式的规则引擎构建执行链路,并结合工厂、策略、责任链等方式来处理具体流程细节(多种组合方式的Ai Agent执行过程),以此解耦系统功能的实现。这样就可以更加灵活方便的迭代各类扩展性诉求。 + +## 三、简历模板 + +**注意**:🙅🏻‍♀️不要直接复制粘贴简历模板内容,以此结构和描述方式,可以用你的个人第1学习视角来描述。包括;学习过程中的积累、检索的同类资料,以及对课程的扩展等多方面内容来编写简历。以下简历涵盖了课程 1~3 阶段内容; + +- 项目名称:`Ai Agent 综合应用提效智能体`/`Ai Agent 智能巡检系统`/`Ai Agent 可编排服务系统` - 基于你的实际场景/目的,修改项目名称 +- 项目架构:微服务架构、DDD 领域驱动四色模型、前后端分离设计、Agent 设计模式 +- 核心技术:Spring AI(RAG、MCP、Advisor)、SpringBoot、MyBatis、MySQL、PGVector、Redis、React、flowgram.ai、Nginx、Docker +- 项目描述:本项目是一套面向业务应用系统提效的综合智能体(Ai Agent)解决方案,支持将执行过程中的各项能力(如Advisor、Prompt、MCP)抽象并存储于数据库,实现自由配置和灵活编排。用户可根据实际业务场景,动态组合和调整智能分析、代码评审、日志检索等功能模块,打造定制化的Ai Auto Agent,从而显著提升开发设计、编码、运维效率。 +- 核心职责: + - 以产品(PRD)服务诉求和多方面调研评审,设计出具有可编排能力的 Ai Agent 服务架构。并以 DDD 领域驱动建模,构建系统架构。 + - 拆解 Ai Agent 执行过程所需的能力组件,包括;Advisor 顾问角色记忆上下文和访问RAG知识库、Tool(Function Call、MCP)调用服务端(推文、通知、ELK、普罗米修斯监控等)、Prompt(提示词)、Model(对话模型)、Api(使用 one-api/自研sdk组件,统一转换其他各个模型为 openai 格式) + - 设计通用对话分析模型,完成 Ai Agent 执行过程中所需的,问题分析、自主规划、精准执行、内容判罚(循环执行),直至输出最终结果。—— Ai Agent 可对不同步骤配置不同的 Model + MCP + Prompt 能力。并对执行过程中,通过 Advisor 顾问角色访问知识库和存储上下文数据。 + - 实现 MCP 服务能力,以 stdio/sse 方式,开发,公众号通知 MCP、推文 MCP(可以是内部的文档化服务)、ELK-MCP、普罗米修斯-MCP等。以及使用 MCP 服务平台,检索公用能力 https://sai.baidu.com/zh/(本地文件、Github、搜索引擎),统一配置使用。—— 数据库设计了多类型 MCP 服务的配置操作。 + - 设计通用 MCP Nginx Token 校验能力(也可以设计 MCP-GateWay),以配置化方式进行鉴权使用。增强 MCP 调用过程中,数据传输安全性。 + - 基于 Spring TaskScheduler 扩展实现,智能体任务调度服务,可自动化完成日常系统巡检(客诉、报警)产生 html 格式报告文档。也可以基于报警监听消息,触发巡检动作(公司内,报警信息有 MQ 消息)。 + - 提供 RAG 知识库能力,可自主上传文件 + 解析工程代码库,并对知识库设有标签为 Ai Agent Advisor 访问 RAG 提供数据使用能力,增强准确性。—— 解析的代码库,可以为 openai 代码自动评审,增强评审能力。 + - 设计一键 Ai Agent 预热能力,动态化注入 Spring 容器。支持运营配置服务,随时调整、变更、上线,方便运营配置和使用。 + - 基于 Racet + flowgram.ai 框架,为 Ai Agent 服务提供拖拉拽编排能力,增强运营使用体验。 + +> 此套 Ai Agent 以为企业/平台/系统,上线3个月以来,主动巡检解决数十次系统隐患问题和运营配置错误情况,以及撰写了上万篇有效文档 + 提炼技术关键信息对新人辅导。后续还会继续配置更多方面的 Ai Agent 服务能力,为企业提效。 + +## 四、课程目录 + +整个课程分3个阶段讲解,包括;RAG、MCP,之后进入 Agent 阶段的学习。前面打好基础,后面进入应用。哪怕是小白,也可以跟着一起实战起来,而且每个阶段都有部署运行效果,越学越爽。 + +项目地址:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +
+ +
\ No newline at end of file diff --git a/docs/md/project/ai-knowledge/none.md b/docs/md/project/ai-knowledge/none.md new file mode 100644 index 000000000..f64218b2f --- /dev/null +++ b/docs/md/project/ai-knowledge/none.md @@ -0,0 +1,15 @@ +--- +title: 新章节,编写中 +lock: no +--- + +# 新章节,编写中 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +... 新章节,编写中 \ No newline at end of file diff --git a/docs/md/project/ai-knowledge/notes.md b/docs/md/project/ai-knowledge/notes.md new file mode 100644 index 000000000..a85043fcf --- /dev/null +++ b/docs/md/project/ai-knowledge/notes.md @@ -0,0 +1,133 @@ +--- +title: 面试:技能、简历、问题汇总 +lock: no +--- + +# 《Ai Agent》,关于面试中的技能、简历、问题汇总 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +此部分主要用于向读者提供星球项目之一的《Ai Agent》项目如何体现到简历中,包括;专业技能、项目经验。 + +## 一、项目介绍 + +面试官您好,本套 Ai Agent 综合智能体项目,主要为业务应用系统提效而构建,包括;需求文档分析、代码评审(可结合 openai 代码自动评审)、文档资料编写(+消息通知)、ELK 日志检索 + 普罗米修斯监控的智能 Ai Agent 分析等功能。 + +整套项目,抽象设计拆分了 Ai Agent 执行过程所需的各项组件(Advisor、Prompt、MCP)能力到数据库表中,使其具备自由配置编排组装的特性。以此方式可结合应用中实际场景诉求,编排类似 Diff 和智能分析 Coze 能力,达到需要什么场景就配置什么场景的 Ai Auto Agent。 + +该项目,在架构设计上使用了 DDD 分层架构进行设计,运用了组合模式的规则引擎构建执行链路,并结合工厂、策略、责任链等方式来处理具体流程细节(多种组合方式的Ai Agent执行过程),以此解耦系统功能的实现。这样就可以更加灵活方便的迭代各类扩展性诉求。 + +## 二、简历模板 + +**注意**:🙅🏻‍♀️不要直接复制粘贴简历模板内容,以此结构和描述方式,可以用你的个人第1学习视角来描述。包括;学习过程中的积累、检索的同类资料,以及对课程的扩展等多方面内容来编写简历。以下简历涵盖了课程 1~3 阶段内容; + +- 项目名称:`Ai Agent 综合应用提效智能体`/`Ai Agent 智能巡检系统`/`Ai Agent 可编排服务系统` - 基于你的实际场景/目的,修改项目名称 +- 项目架构:微服务架构、DDD 领域驱动四色模型、前后端分离设计、Agent 设计模式 +- 核心技术:Spring AI(RAG、MCP、Advisor)、SpringBoot、MyBatis、MySQL、PGVector、Redis、React、flowgram.ai、Nginx、Docker +- 项目描述:本项目是一套面向业务应用系统提效的综合智能体(Ai Agent)解决方案,支持将执行过程中的各项能力(如Advisor、Prompt、MCP)抽象并存储于数据库,实现自由配置和灵活编排。用户可根据实际业务场景,动态组合和调整智能分析、代码评审、日志检索等功能模块,打造定制化的Ai Auto Agent,从而显著提升开发设计、编码、运维效率。 +- 核心职责: + - 以产品(PRD)服务诉求和多方面调研评审,设计出具有可编排能力的 Ai Agent 服务架构。并以 DDD 领域驱动建模,构建系统架构。 + - 拆解 Ai Agent 执行过程所需的能力组件,包括;Advisor 顾问角色记忆上下文和访问RAG知识库、Tool(Function Call、MCP)调用服务端(推文、通知、ELK、普罗米修斯监控等)、Prompt(提示词)、Model(对话模型)、Api(使用 one-api/自研sdk组件,统一转换其他各个模型为 openai 格式) + - 设计通用对话分析模型,完成 Ai Agent 执行过程中所需的,问题分析、自主规划、精准执行、内容判罚(循环执行),直至输出最终结果。—— Ai Agent 可对不同步骤配置不同的 Model + MCP + Prompt 能力。并对执行过程中,通过 Advisor 顾问角色访问知识库和存储上下文数据。 + - 实现 MCP 服务能力,以 stdio/sse 方式,开发,公众号通知 MCP、推文 MCP(可以是内部的文档化服务)、ELK-MCP、普罗米修斯-MCP等。以及使用 MCP 服务平台,检索公用能力 https://sai.baidu.com/zh/(本地文件、Github、搜索引擎),统一配置使用。—— 数据库设计了多类型 MCP 服务的配置操作。 + - 设计通用 MCP Nginx Token 校验能力(也可以设计 MCP-GateWay),以配置化方式进行鉴权使用。增强 MCP 调用过程中,数据传输安全性。 + - 基于 Spring TaskScheduler 扩展实现,智能体任务调度服务,可自动化完成日常系统巡检(客诉、报警)产生 html 格式报告文档。也可以基于报警监听消息,触发巡检动作(公司内,报警信息有 MQ 消息)。 + - 提供 RAG 知识库能力,可自主上传文件 + 解析工程代码库,并对知识库设有标签为 Ai Agent Advisor 访问 RAG 提供数据使用能力,增强准确性。—— 解析的代码库,可以为 openai 代码自动评审,增强评审能力。 + - 设计一键 Ai Agent 预热能力,动态化注入 Spring 容器。支持运营配置服务,随时调整、变更、上线,方便运营配置和使用。 + - 基于 React + flowgram.ai 框架,为 Ai Agent 服务提供拖拉拽编排能力,增强运营使用体验。 + +> 此套 Ai Agent 以为企业/平台/系统,上线3个月以来,主动巡检解决数十次系统隐患问题和运营配置错误情况,以及撰写了上万篇有效文档 + 提炼技术关键信息对新人辅导。后续还会继续配置更多方面的 Ai Agent 服务能力,为企业提效。 + +## 三、面试问题 + +### 1. 你们项目采用了DDD领域驱动设计,能简单介绍一下你们的四色模型是如何划分的? + +**回答**:我们项目严格按照DDD四色模型进行架构设计。首先是**值对象**层,主要包含AiClientVO、AiClientAdvisorVO等核心业务对象,承载业务状态和标识;**值对象(Value Object)**层包含各种配置信息如AiClientSystemPromptVO、AiClientModelVO等,保证数据的不可变性;**领域服务(Domain Service)**层实现了复杂的业务逻辑,如AiClientNode、AiClientAdvisorNode等节点服务,负责AI Agent的组装和编排;**聚合根(Aggregate Root)**则通过ExecuteCommandEntity等实体来管理整个AI Agent的生命周期。这种设计让我们的业务逻辑更加清晰,各层职责分明,便于维护和扩展。 + +### 2. 你们的微服务架构是如何设计的,各个模块之间是如何协作的? + +**回答**:我们采用了标准的DDD分层架构,分为六个核心模块:api层定义对外接口契约,app层作为应用启动入口和配置中心,domain层包含核心业务逻辑和领域模型,trigger层处理HTTP请求和事件触发,infrastructure层负责数据持久化和外部服务调用,types层定义通用类型和枚举。各模块通过依赖注入和事件驱动进行协作,domain层作为核心不依赖任何外部模块,infrastructure层实现domain层定义的接口,trigger层调用domain层的服务。这种设计保证了业务逻辑的纯净性和系统的可测试性。 + +### 3. Agent设计模式在你们项目中是如何体现的? + +**回答**:我们的Agent设计模式主要体现在多层次的智能体架构设计上。首先是**策略模式**的应用,通过IExecuteStrategy接口定义执行策略,AutoAgentExecuteStrategy实现自动执行逻辑;其次是**责任链模式**的核心应用,我们设计了四步执行链:RootNode→Step1AnalyzerNode(任务分析)→Step2PrecisionExecutorNode(精准执行)→Step3QualitySupervisorNode(质量监督)→Step4LogExecutionSummaryNode(执行总结),每个节点继承AbstractExecuteAutoSupport并实现特定的业务逻辑;再次是**工厂模式**,通过DefaultAutoAgentExecuteStrategyFactory创建执行策略处理器,管理DynamicContext动态上下文;最后是**模板方法模式**,AbstractExecuteAutoSupport定义了执行模板,各个Step节点实现具体的doApply方法。这种设计实现了"问题分析→自主规划→精准执行→质量监督"的完整AI Agent执行循环,每个步骤都可以独立配置不同的ChatClient、Prompt和Advisor,真正实现了智能体的可编排和可扩展。 + +### 4. Spring AI中的Advisor顾问角色是如何设计和实现的? + +**回答**:Advisor顾问角色是我们AI Agent的核心能力之一,主要负责上下文记忆和知识库访问。我们实现了两种主要的Advisor:PromptChatMemory用于维护对话历史,通过maxMessages参数控制记忆长度;RagAnswerAdvisor用于访问向量知识库,通过topK和filterExpression参数控制检索精度。在实现上,我们继承了Spring AI的BaseAdvisor,重写了aroundCall方法来拦截对话请求,在请求前注入相关上下文信息,在响应后更新记忆状态。这种设计让AI Agent具备了持续学习和知识积累的能力,大大提升了对话的准确性和连贯性。 + +### 5. RAG知识库的分词和向量化是如何设计的? + +**回答**:我们的RAG知识库基于PGVector实现,使用OpenAI的Embedding模型进行向量化。在分词策略上,我们使用TokenTextSplitter按照token数量进行智能分割,既保证了语义的完整性又控制了向量的维度。向量存储采用1536维的向量空间,支持余弦相似度检索。在数据预处理阶段,我们会对上传的文档进行清洗和标准化,提取关键信息并添加元数据标签,这些标签可以用于后续的过滤检索。检索时通过SearchRequest配置topK参数控制返回结果数量,通过filterExpression进行精确过滤,确保检索结果的相关性和准确性。 + +### 6. MCP协议的设计理念和实现方式是什么? + +**回答**:MCP(Model Context Protocol)是我们实现工具调用的核心协议,它定义了AI模型与外部工具之间的标准化通信接口。我们支持两种通信方式:stdio模式适用于本地工具调用,通过标准输入输出进行通信;sse模式适用于远程服务调用,通过Server-Sent Events实现实时通信。在实现上,我们为每个MCP服务定义了标准的Function接口,使用@Tool注解标记可调用的方法,通过MethodToolCallbackProvider将Java方法暴露为MCP工具。比如微信通知MCP通过WeiXinNoticeService提供消息推送能力,CSDN发帖MCP提供自动发布文章的能力。这种设计让AI Agent能够调用各种外部服务,大大扩展了智能体的能力边界。 + +### 7. 你们是如何实现AI Agent的动态编排和热部署的? + +**回答**:我们通过Spring的动态Bean注册机制实现了AI Agent的热部署能力。核心思路是将AI Agent的各个组件(Model、Prompt、Advisor、MCP)抽象为可配置的Bean,存储在数据库中。当配置发生变化时,通过ArmoryCommandEntity触发重新装配流程,使用DefaultArmoryStrategyFactory的策略模式动态创建新的组件实例,然后通过registerBean方法将新组件注册到Spring容器中。整个过程采用责任链模式,依次执行AiClientApiNode、AiClientModelNode、AiClientAdvisorNode、AiClientNode等节点,确保组件的正确装配顺序。这种设计让我们能够在不重启服务的情况下,动态调整AI Agent的能力配置,大大提升了系统的灵活性和运维效率。 + +### 8. 智能体任务调度服务是如何设计的,如何实现自动化巡检? + +**回答**:我们基于Spring TaskScheduler扩展实现了智能体任务调度服务,支持定时和事件驱动两种触发方式。定时巡检通过Cron表达式配置执行周期,自动收集系统指标、日志信息、告警数据等,然后调用专门的巡检Agent进行智能分析,最终生成HTML格式的巡检报告。事件驱动巡检则监听MQ消息,当收到告警信息时立即触发相应的巡检动作。在实现上,我们为每种巡检场景配置了专门的Agent,比如性能巡检Agent配置了Prometheus MCP工具,日志巡检Agent配置了ELK MCP工具。巡检结果会通过微信公众号MCP自动推送给相关人员,实现了从发现问题到通知处理的全自动化流程。 + +### 9. 你们的MCP服务是如何保证安全性的? + +**回答**:我们设计了通用的MCP Nginx Token校验机制来保证数据传输安全性。首先在Nginx层配置了统一的鉴权模块,所有MCP请求都需要携带有效的Token才能通过;Token采用JWT格式,包含用户身份、权限范围、过期时间等信息;在应用层,我们为每个MCP服务配置了独立的访问密钥,支持定期轮换;对于敏感操作如数据库访问、文件操作等,还增加了二次验证机制。此外,我们还实现了请求频率限制、IP白名单、操作审计日志等安全措施。在数据传输过程中,所有敏感信息都进行了加密处理,确保即使在网络传输过程中被截获也无法直接使用。 + +### 10. 前端的拖拽编排功能是如何实现的? + +**回答**:我们基于React和flowgram.ai框架实现了AI Agent的可视化编排功能。前端采用节点式的流程图设计,每个节点代表一个AI Agent组件,用户可以通过拖拽的方式组合不同的Model、Prompt、Advisor、MCP组件。在技术实现上,我们使用React Flow作为基础图形引擎,自定义了各种组件节点的渲染逻辑;通过Context API管理全局状态,实时同步编排配置;使用WebSocket与后端保持连接,支持实时预览和调试。当用户完成编排后,前端会将配置信息序列化为JSON格式发送给后端,后端解析配置并动态创建对应的AI Agent实例。这种设计让非技术人员也能够轻松配置和使用AI Agent,大大降低了使用门槛。 + +### 11. 在实现多模型适配时遇到了什么挑战,是如何解决的? + +**回答**:最大的挑战是不同AI模型的API格式和调用方式差异很大,直接适配会导致代码耦合度过高。我们的解决方案是引入one-api服务作为统一的模型网关,将所有第三方模型的API统一转换为OpenAI格式。在系统内部,我们只需要适配OpenAI的接口规范,通过配置不同的baseUrl和apiKey就能接入各种模型。同时我们还自研了SDK组件,封装了模型调用的通用逻辑,包括重试机制、超时处理、错误码转换等。这种设计不仅简化了开发复杂度,还提升了系统的稳定性和可扩展性,当需要接入新模型时只需要在one-api层配置即可。 + +### 12. 大规模并发场景下,AI Agent的性能是如何保证的? + +**回答**:我们从多个维度进行了性能优化。首先是连接池管理,为每个模型API配置了独立的连接池,避免相互影响;其次是异步处理,所有AI调用都采用异步方式,通过ResponseBodyEmitter实现流式响应,用户可以实时看到AI的思考过程;然后是缓存策略,对于相同的问题我们会缓存AI的回答,减少重复计算;在资源调度上,我们使用自定义的线程池进行任务分发,根据任务类型和优先级进行智能调度。此外,我们还实现了熔断降级机制,当某个模型服务不可用时自动切换到备用模型,保证服务的高可用性。通过这些优化,我们的系统能够支持数千并发用户同时使用。 + +### 13. 如何保证AI Agent执行过程中的数据一致性? + +**回答**:我们采用了事件驱动架构来保证数据一致性。每个AI Agent的执行过程被拆分为多个步骤,每个步骤完成后都会发布相应的事件,下一个步骤监听事件后开始执行。在数据库层面,我们使用了分布式事务来保证跨服务的数据一致性,关键操作都包装在事务中执行。对于长时间运行的AI任务,我们实现了断点续传机制,将执行状态持久化到数据库,即使服务重启也能从断点继续执行。同时我们还建立了完善的监控和告警机制,实时监控AI Agent的执行状态,一旦发现异常立即进行人工干预。这种设计确保了即使在复杂的业务场景下,数据的一致性和完整性也能得到保证。 + +### 14. 这个AI Agent系统给公司带来了什么实际价值? + +**回答**:我们的AI Agent系统在多个方面为公司创造了显著价值。在开发效率方面,代码评审Agent将人工评审时间从平均2小时缩短到15分钟,准确率达到95%以上;在运维效率方面,智能巡检Agent实现了7×24小时自动监控,故障发现时间从小时级缩短到分钟级,运维人员的重复性工作减少了80%;在内容创作方面,文章生成Agent帮助技术团队每月自动产出50+篇高质量技术文章,大大提升了公司的技术影响力。更重要的是,这套系统的可编排特性让我们能够快速响应新的业务需求,从需求提出到Agent上线的周期从原来的2周缩短到2天,大大提升了业务响应速度。 + +### 15. 在项目推广过程中遇到了什么阻力,是如何解决的? + +**回答**:最大的阻力来自于用户对AI准确性的担忧和使用习惯的改变。为了解决这个问题,我们采用了渐进式推广策略。首先选择了代码评审这个相对低风险的场景进行试点,通过大量的测试数据证明AI的准确性;然后我们设计了人机协作的工作模式,AI负责初步分析,人工负责最终决策,让用户逐步建立信任;在用户体验方面,我们提供了详细的操作文档和培训视频,还建立了用户反馈机制,根据用户建议持续优化产品功能。经过3个月的推广,用户接受度从最初的30%提升到了85%,现在已经成为团队日常工作的重要工具。 + +### 16. 未来你们计划如何进一步优化和扩展这个系统? + +**回答**:我们有几个重要的优化方向。首先是增强AI Agent的自主学习能力,通过强化学习让Agent能够从历史执行结果中学习,不断优化自己的决策策略;其次是扩展更多的业务场景,比如客服机器人、数据分析助手、项目管理助手等;在技术架构方面,我们计划引入更先进的向量数据库和图数据库,提升知识检索的准确性和效率;在用户体验方面,我们正在开发移动端应用,让用户能够随时随地使用AI Agent服务。同时我们也在探索多模态AI的应用,让Agent能够处理图片、音频、视频等多种类型的数据,进一步扩展应用场景。预计在未来一年内,我们的AI Agent系统将覆盖公司80%以上的业务流程。 + +### 17. 在设计通用对话分析模型时,你们是如何处理问题分析、自主规划、精准执行这个循环的? + +**回答**:我们设计了一个四阶段的执行循环:问题分析、自主规划、精准执行、内容判罚。在问题分析阶段,AI Agent首先理解用户意图,提取关键信息和上下文;自主规划阶段,Agent根据可用的工具和知识库制定执行计划,选择最优的执行路径;精准执行阶段,按照计划调用相应的MCP工具和Advisor,获取执行结果;内容判罚阶段,Agent评估执行结果是否满足用户需求,如果不满足则重新进入规划阶段。整个过程中,我们为每个阶段配置了不同的Model、MCP、Prompt组合,比如规划阶段使用逻辑推理能力强的模型,执行阶段使用工具调用能力强的模型。这种设计让AI Agent具备了类似人类的思考和执行能力。 + +### 18. 你们的向量数据库检索性能是如何优化的? + +**回答**:我们从多个层面优化了向量检索性能。在索引层面,PGVector使用了HNSW(Hierarchical Navigable Small World)算法构建高效的向量索引,支持近似最近邻搜索;在查询优化方面,我们实现了查询缓存机制,对于相似的查询直接返回缓存结果;在数据分片方面,我们按照业务领域对向量数据进行分片存储,减少检索范围;在并发控制方面,我们使用了连接池和异步查询,提升并发处理能力。此外,我们还实现了智能的预加载机制,根据用户的历史查询模式预先加载可能需要的向量数据到内存中。通过这些优化,我们的向量检索响应时间从原来的500ms优化到了50ms以内,同时支持千级并发查询。 + +### 19. 在实现MCP服务的过程中,stdio和sse两种模式各有什么优缺点? + +**回答**:stdio模式的优点是实现简单,适合本地工具调用,通信开销小,调试方便;缺点是只能用于本地服务,不支持远程调用,扩展性有限。sse模式的优点是支持远程调用,可以跨网络部署,支持实时双向通信,扩展性强;缺点是实现复杂度高,需要处理网络异常和重连机制,通信开销相对较大。在实际应用中,我们根据具体场景选择合适的模式:对于文件系统操作、本地命令执行等场景使用stdio模式;对于微信通知、CSDN发帖、远程API调用等场景使用sse模式。为了统一开发体验,我们封装了通用的MCP客户端,屏蔽了底层通信细节,开发者只需要关注业务逻辑即可。 + +### 20. 你们是如何保证AI Agent在复杂业务场景下的稳定性的? + +**回答**:我们建立了多层次的稳定性保障机制。在架构层面,采用微服务设计,单个服务的故障不会影响整个系统;在服务层面,实现了熔断降级机制,当某个依赖服务不可用时自动切换到备用方案;在数据层面,建立了完善的备份和恢复机制,关键数据都有多副本保护;在监控层面,实现了全链路监控,从请求接入到响应输出的每个环节都有详细的监控指标;在异常处理方面,我们为每种可能的异常场景都设计了对应的处理策略,包括重试、降级、人工介入等。此外,我们还建立了完善的测试体系,包括单元测试、集成测试、压力测试、混沌工程测试等,确保系统在各种极端情况下都能稳定运行。通过这些措施,我们的系统可用性达到了99.9%以上。 + +> 随着课程进展和大家面试遇到的问题,持续更新这部分内容。 + +### 21. AI Agent采用的是什么agent设计模式。以autoagent那套执行链路来说 + +链接:[https://t.zsxq.com/pqrtB](https://t.zsxq.com/pqrtB) + +课程里视频也讲到的,链路的执行分为4类,有固定模式,规划模式,步骤模型等。通过为 ai agent 配置可执行客户端链路,动态规划、执行步骤、审查结果(循环验证)、总结输出来完成。在整个执行过程中,使用到了,提示词prompt、顾问角色访问知识库和记忆上下文、mcp 工具调用等,来完成整个请求会话。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/promotion/AI MCP \345\206\215\350\277\231\344\271\210\347\224\250\344\270\213\345\216\273\357\274\214\345\217\257\345\260\261\350\246\201\342\200\235\345\210\233\344\270\232\342\200\234\345\217\230\347\216\260\344\272\206\345\221\200\357\274\201.md" "b/docs/md/project/ai-knowledge/promotion/AI MCP \345\206\215\350\277\231\344\271\210\347\224\250\344\270\213\345\216\273\357\274\214\345\217\257\345\260\261\350\246\201\342\200\235\345\210\233\344\270\232\342\200\234\345\217\230\347\216\260\344\272\206\345\221\200\357\274\201.md" new file mode 100644 index 000000000..13aa0851b --- /dev/null +++ "b/docs/md/project/ai-knowledge/promotion/AI MCP \345\206\215\350\277\231\344\271\210\347\224\250\344\270\213\345\216\273\357\274\214\345\217\257\345\260\261\350\246\201\342\200\235\345\210\233\344\270\232\342\200\234\345\217\230\347\216\260\344\272\206\345\221\200\357\274\201.md" @@ -0,0 +1,111 @@ +--- +title: AI MCP 再继续这么用,可就要”创业“变现了呀! +lock: no +--- + +# AI MCP 再继续这么用,可就要”创业“变现了呀! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在 [AI MCP 24小时为我”打工“](https://mp.weixin.qq.com/s/sB4EaP6HMtj3VxK-VMD8nQ),两周时间。已产生 `251篇文章`,带来`10万+阅读量`,`涨粉近1000人`!在这么搞下去,不得变现了哇! + +
+ +
+ +嘿嘿,不过这点点成绩根本挡不住我前进的脚步👣! + +AI MCP 的能力远不止于次,或者更准确的说是 AI Agent 的能力,还有非常多的场景可以运用,开玩笑讲,他会让 Java 再次伟大!这也是为什么 Spring AI 那么快速的迭代自己的框架设计,甚至不惜每次迭代还要推翻之前的结构模型,也要更好的支持未来 AI 工程的开发实现。 + +在有了 AI Agent 后,所有的 tob/toc 服务项目,都应该会变得更加智能,这会包括我们现在使用的各项软件,如;电商、出行、外卖等,也包括研发人员使用的各类技术软件。如果大公司还是牛马般的靠体力卷工时,甚至可能会被小公司的不断创新所掀翻。 + +好啦,那么这篇文章小傅哥会给大家介绍下 AI Agent 以及可运用的场景,帮助大家打开思路! + +>文末提供了 AI RAG&MCP 实战编程项目,可以快速掌握 Spring AI 应用开发能力! + +## 一、什么是 AI Agent + +AI Agent 是整合多种技术手段的智能实体,其实现依赖于 Tools、MCP、Memory、RAG(Retrieval 增强检索生成) 等技术组件。但不是非得依赖全部组件才叫 AI Agent。 + +
+ +
+ +AI Agents 是**系统** ,它通过赋予 **大型语言模型(LLMs)** **访问工具** 和**知识** 来扩展其能力,从而使 **LLMs** 能够**执行操作** 。 + +- **系统** :将 Agents 视为一个由许多组件组成的系统,而不仅仅是单个组件,这一点很重要。在基本层面上,AI Agent 的组件包括: + - **环境** - AI Agent 运行的定义空间。例如,如果我们有一个旅行预订 AI Agent,则环境可以是 AI Agent 用来完成任务的旅行预订系统。 + - **传感器** - 环境具有信息并提供反馈。AI Agents 使用传感器来收集和解释有关环境当前状态的信息。在旅行预订 Agent 示例中,旅行预订系统可以提供诸如酒店可用性或航班价格之类的信息。 + - **执行器** - 一旦 AI Agent 接收到环境的当前状态,对于当前任务,Agent 会确定要执行的操作以更改环境。对于旅行预订 Agent,它可能是为用户预订可用房间。 +- **大型语言模型** - Agents 的概念在 LLMs 创建之前就已存在。使用 LLMs 构建 AI Agents 的优势在于它们能够解释人类语言和数据。这种能力使 LLMs 能够解释环境信息并制定改变环境的计划。 +- **执行操作** - 在 AI Agent 系统之外,LLMs 仅限于根据用户提示生成内容或信息的情况。在 AI Agent 系统内部,LLMs 可以通过解释用户请求并使用其环境中可用的工具来完成任务。 +- **访问工具** - LLM 可以访问哪些工具由 1) 它运行的环境和 2) AI Agent 的开发者定义。对于我们的旅行 Agent 示例,Agent 的工具受预订系统中可用操作的限制,开发者可以将 Agent 的工具访问权限限制为航班。 +- **知识** - 除了环境提供的信息外,AI Agents 还可以从其他系统、服务、工具甚至其他 Agents 中检索知识。在旅行 Agent 示例中,此知识可以是位于客户数据库中的用户旅行偏好信息。 + +我们可以以一个人,作为视角来理解 AI Agent,Memory 记忆是人的大脑,Tools 是人的身体和四肢,Rag 知识库是过往的经验储备,MCP 是我们与外部的连接调用。而整个人就是这个智能体 AI Agent。 + +## 二、AI 智能体的工作原理 + +每个智能体都定义了角色、个性和沟通风格,包括具体指令和可用工具的说明。 + +
+ +
+ +- **角色**:角色是智能体的核心特征,定义了其性格和行为方式。一个定义良好的角色可以帮助智能体在与环境和用户互动时保持一致性。随着时间的推移,智能体会通过经验积累和环境互动不断发展和完善其角色。 +- **记忆**:智能体的记忆系统由短期记忆、长期记忆、共识记忆和情景记忆组成。短期记忆用于处理即时互动,长期记忆存储历史数据和对话,情景记忆记录过去的互动细节,而共识记忆则用于在智能体之间共享信息。通过这些记忆系统,智能体能够回忆过去的互动,适应新情况,保持上下文一致性,并从经验中学习以提高性能。 +- **工具**:工具是智能体用来与环境互动并增强其功能的资源。它们可以是函数或外部资源,帮助智能体访问信息、处理数据或控制外部系统以执行复杂任务。工具根据其界面类型进行分类,包括物理界面、图形界面和基于程序的界面。通过工具学习,智能体能够理解工具的功能及其应用场景,从而有效地使用这些工具。 +- **模型**:大语言模型 (LLM) 是构建 AI 智能体的基础,赋予智能体理解、推理和行动的能力。LLM 作为智能体的“大脑”,使其能够处理和生成语言,而其他组件则支持智能体的推理和行动能力。 + +> https://github.com/google/A2A 多个 Agent 间,可以使用 A2A 协议,完成 Agent 和 Agent 对接。 + +## 三、AI Agent 场景应用 + +### 1. BCP 智能巡检 + +
+ +
+ +- 场景:在日常的互联网toc场景的系统中,每天都会有不同程度的客诉,这些客诉问题需要进行大量的系统排查。 +- 方案:将企业内的各项系统服务,开发出 MCP 服务接口(APIs),并为每个核心业务流程提供排查链路计划。在发生系统报警时,以报警为触达手段,排查系统日志、账户、Redis、Dev-Ops 服务等,给给出综合的解决方案。 +- 其他:不同配置的 Agent 是可以连接通信的,一个 Agent 就是一个配置出来的 ChatClient 对话体。 + +### 2. 工具AI化设计 + +
+ +
+ +- 场景:其实现在除了编程方面,其他很多软件并没有那么快速的接入 AI Agent。如 trae.ai\cursor 都是编程的利器,可以更快速的迭代开发代码。那么同样的 SSH 链接云服务器的工具,也可以通过 AI Agent 进行扩展,提高我们的操作服务器部署软件的效率。 +- 方案:设计 Linux SSH MCP Server 服务,同时提供 SFTP 以及云服务器的对接。再加上 Linux RAG 知识库。这样我们就可以以对话和编码的方式操作服务器。如,部署 JDK,安装 NodeJS,执行系统镜像的构建、推送、拉取、部署等操作。 + +### 3. toc场景AI化 + +
+ +
+ +- 场景:目前的大部分购物操作,都是人工的方式自己在平台检索,之后对于不懂的在进行客服提问,最后去下单。那么这里也可以加入 AI 操作,自主的完成购物、下单,以及将来退货的操作。 +- 方案:为产品提供购物话术 RAG 增强检索能力,以及提供货物、下单、结算、物流的 MCP 服务。用户和 AI 对话过程中,完成货品的组装选择和下单。聊聊天就把东西买好了,还能给提供产品的使用和维护。 + +### 4. AI Agent 编排 + +
+ +
+ +- 当我们有大量的 MCP 服务实现、RAG 知识库沉淀后,会催生出 AI Agent 的编排能力实现。你可以按需组装一套自己的对话 AI Agent。 +- 类似的场景实现:[https://github.com/n8n-io/n8n](https://github.com/n8n-io/n8n) - 付费的。不过很多企业会借助之前的 BPMN 实现的低代码,扩展出 AI Agent 编排实现。 + +```java +docker volume create n8n_data +docker run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n registry.cn-hangzhou.aliyuncs.com/xfg-studio/n8n:1.88.0 +``` + diff --git "a/docs/md/project/ai-knowledge/promotion/AI MCP \345\267\262\347\273\217\345\270\256\346\210\221\342\200\234\345\271\262\346\264\273\342\200\235\344\272\206\357\274\201.md" "b/docs/md/project/ai-knowledge/promotion/AI MCP \345\267\262\347\273\217\345\270\256\346\210\221\342\200\234\345\271\262\346\264\273\342\200\235\344\272\206\357\274\201.md" new file mode 100644 index 000000000..88403fbe1 --- /dev/null +++ "b/docs/md/project/ai-knowledge/promotion/AI MCP \345\267\262\347\273\217\345\270\256\346\210\221\342\200\234\345\271\262\346\264\273\342\200\235\344\272\206\357\274\201.md" @@ -0,0 +1,119 @@ +--- +title: AI MCP 已经一天24小时,给我“打工”了! +lock: no +--- + +# AI MCP 已经一天24小时,给我“打工”了! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +一天24篇推文,500+收藏,2.6万+阅读量,我已经让 AI 帮我干活了!这是小傅哥最新实现的一套 `mcp-server-csdn`,完全不需要我上手,就可以定时的执行文章编写和发表。 + +
+ +
+ +
+ +
+ +只要你配置好对应的话题,AI MCP 就会自动创作文章和发表。虽然单篇流量不一定都高,但架不住我根本没出手呀,走的就是一个量大取胜!**这不是自动发帖,这是自动创业呀😂!简直美滋滋!** + +
+ +
+ +MCP 模型上下文协议,核心的作用就在于通过标准的协议设计,让 AI 可以以通用的方式调用各类服务的接口。所以,我们可以通过 MCP 的实现,让 AI 自动化的完成内容的创作和发表。 + +在这样的一个 CSDN 发文章的模型跑通后,我们就可以大批量的复制,完成不同类内容的创作和发表,也可以以这样的方式对接其他各类内容社区平台。甚至你还可以想到,这东西能辅助我们完成很多工作。 + +以前总有人说,AI 来了,可能会让很多程序员失业。但恰恰相反,不仅不会失业,反而 AI 会让程序员如虎添翼,推进改变其他行业的工作模式。毕竟,没有哪个行业不仅能用 AI,还能懂 AI,还能开发 AI 了! + +接下来,小傅哥就给大家,介绍下这套 MCP 的设计和工作模式。 + +> 🧧 文末提供了全套 AI、RAG、MCP 的开发、使用教程以及工程源码。此外还有非常多的互联网大厂项目,都可以一并获取学习。 + +## 一、MCP 的工作模式 + +MCP 服务,可以以工具 Tools 的形式配置到 AI MCP 客户端。当我们向 AI 发送执行指令后,AI 会携带工具 Tools 信息,一起发送给 AI。之后进行语义分析以及调用 AI MCP 执行业务诉求。 + +你可以把 AI MCP 当成你雇来的员工,你有什么想法就告诉他,让他充当那个跑腿和执行的人。而你只负责下达指令和验收结果。 + +
+ +
+ +
+ +
+ +如图,我们可以给程序设定一套话术描述,让它去做指定类型的文章创作和发表。在 AI 完成内容创作后,会调用 MCP 服务,传递接口入参值,再由 MCP 服务,完成文章的发表。 + +如果,我们希望发布的文章具备当前热点。可以增加一个 MCP 服务,这个服务来检索牛客网最新的求职面试信息,再加上各个大厂招聘要求描述信息等。基于这些信息先获取到面试热点话题,在结合话题与设定的面试场景话术一起生成对应的文章。**MCP 就像是你的员工,你可以交代A员工做什么后,交接给B员工继续处理。** + +## 二、MCP 怎么开发的 + +首先,MCP 是一套标准的模型上下文协议,它不限制非得使用那种语言实现。如;NodeJS、Python、Java,都可以实现 MCP 服务。以 Spring AI 框架举例,Java 工程师可以,以非常简单的使用 Java 代码开发普通的业务逻辑,之后配置上 AI MCP 工具类注解和完成 Bean 对象的实例化即可。 + +
+ +
+ +- 如图,正常的搭建 SpringBoot 应用程序,编写对接 CSDN 发帖接口。之后给服务方法配置上 Tool 工具注解。那么它就会被 Spring AI MCP 注册和使用了。 +- 同时,一个工程里可以有很多的这样的服务和工具。比如你要开发一个各大平台集成的发帖/文章的服务,那么也可以增加其他的对接类在同一个工程里。最终配置上 AI MCP 的注册即可。 + +## 三、MCP 怎么对接的 + +把 Java 开发实现的 MCP 服务端,打包成一个 Jar,把这个 Jar 配置到 MCP 客户端工程里。即可完成 MCP 服务的调用。并且一个 MCP 客户端,也可以对接多套 MCP 服务,这些服务可以以 AI 工作流的形式完成自己的工作。 + +
+ +
+ +
+ +
+ +- 如图,在 SpringBoot 工程中,增加MCP 服务的配置对接。这个时候 AI 对话就那可以拿到 MCP 工具进行对话。并将要执行的信息,通过 MCP 服务工具完成处理。 +- 并且,你可以把 MCP 服务,打包构建镜像部署到(服务器/Nas),让它一天24小时的干活。用不了多久,你就成为某个领域内容的专家了! + +## 四、MCP 怎么学习下? + +小傅哥,已经为你准备好了一套 AI RAG、MCP、Function Call 实践编程课程,使用 Java + Spring AI 框架,增强自己的 AI 应用开发能力,迅速囤积编程技能!如下,课程目录,全程文档小册 + 视频带着你从0到1学习。 + +
+ +
+ +#### 第1期 RAG Spring AI 0.8.1 - 完结 + +1. 【更】AI RAG 知识库,项目介绍&需求分析&环境说明 +2. 【更】初始化知识库工程&部署模型&提交代码 +3. 【更】Ollama DeepSeek 流式应答接口实现 +4. 【更】Ollama DeepSeek 流式应答页面对接 +5. 【更】Ollama RAG 知识库上传、解析和验证 +6. 【更】Ollama RAG 知识库接口服务实现 +7. 【更】基于AI工具,设计前端UI和接口对接 +8. 【更】Git仓库代码库解析到知识库并完善UI对接 +9. 【更】扩展OpenAI模型对接,以及完整AI对接 +10. 【更】云服务器部署知识库(Docker、Nginx) + +#### 第2期 MCP Spring AI 1.0.0 - 开冲 + +1. 【新】AI MCP 项目介绍 +2. 【新】吃上细糠,Spring AI 框架升级 + GPU 部署 AI +3. 【新】康庄大道,上手 AI MCP 工作流.md +4. 【新】道山学海,实现MCP自动发帖服务 +5. 【新】海纳百川,MCP 其他服务实现 +6. 【新】川流不息,官网 DeepSeek + open-api 对接 +7. 【新】息兵罢战,服务接口实现,增强 RAG 知识库 + MCP 服务使用 +8. 【新】应用服务接口与前端页面对接 +9. ... 随课程开发提供,包括后续的云服务部署。 + +> AI RAG&MCP 只是小傅哥社群里众多项目的一个,这里还有非常多的牛皮项目,一次加入都可以学习到。并且,不断的更新迭代新项目,社群嘎嘎活跃! \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/\347\254\25410\350\212\202\357\274\232\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262\347\237\245\350\257\206\345\272\223\357\274\210Docker\343\200\201Nginx\357\274\211.md" "b/docs/md/project/ai-knowledge/\347\254\25410\350\212\202\357\274\232\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262\347\237\245\350\257\206\345\272\223\357\274\210Docker\343\200\201Nginx\357\274\211.md" new file mode 100644 index 000000000..88ce48c59 --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\25410\350\212\202\357\274\232\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262\347\237\245\350\257\206\345\272\223\357\274\210Docker\343\200\201Nginx\357\274\211.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第10节:云服务器部署知识库(Docker、Nginx) +pay: https://t.zsxq.com/YUG46 +--- + +# 《DeepSeek RAG 知识库》第10节:云服务器部署知识库(Docker、Nginx) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/cWfL6](https://t.zsxq.com/cWfL6) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +部署项目到公网云服务器,让小伙伴们学习如何打包部署上线和Linux、Docker、Nginx的操作使用。 + +## 二、部署过程 + +如图,为本次的部署过程; + +
+ +
+ +- 首先,购买云服务器,之后搭建云环境。[http://618.gaga.plus/](http://618.gaga.plus/) 需要2c4g部署。 +- 之后,我们在本地构建软件镜像,之后推送到 Docker Hub,完成后再把 dev-ops 的脚本通过 sftp 上传到云服务器,执行安装。 +- 最后,记得部署完成后检查各个软件运行日志,以及开通下相关的服务端口。 diff --git "a/docs/md/project/ai-knowledge/\347\254\25411\350\212\202\357\274\232AIMCP\351\203\250\345\210\206\344\273\213\347\273\215.md" "b/docs/md/project/ai-knowledge/\347\254\25411\350\212\202\357\274\232AIMCP\351\203\250\345\210\206\344\273\213\347\273\215.md" new file mode 100644 index 000000000..0af867e4a --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\25411\350\212\202\357\274\232AIMCP\351\203\250\345\210\206\344\273\213\347\273\215.md" @@ -0,0 +1,109 @@ +--- +title: DeepSeep MCP 动态知识库 - AI工作流&智能体 +lock: no +--- + +# 《DeepSeep MCP 动态知识库》 - AI工作流&智能体 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +用不了多久,各大互联网企业都将大量的推进落地,自有 [MCP](https://github.com/modelcontextprotocol) 服务的实现,用于增强企业 AI 应用的提效能力。因为 [MCP](https://github.com/modelcontextprotocol) 的加入,可以让你;一条命令`帮研发`,调用应用系统日志、排查系统CPU负载、自主选择是否调度数据库信息。也可以一条命令`帮运营`,搞定复杂的SQL执行、导出报表、分析数据、完成促活营销券的自动化配置上架。这就是 [MCP](https://github.com/modelcontextprotocol) 的魅力!👍🏻 + +
+ +
+ +**那么牛,MCP 是什么?** + +专业的术语 `MCP = Model Context Protocol` 模型上下文协议,可实现应用与外部数据源和工具之间的无缝集成。无论您是构建 AI 驱动的 IDE、增强聊天界面还是创建自定义 AI 工作流,MCP 都提供了一种标准化的方式来连接他们所需的上下文。 + +`来吧,上图!让你看看它是啥!` + +
+ +
+ +- 首先,站在用户的使用视角,研发或者运营,可以通过话术描述,完成系列的 AI 工作流,并拿到最终的结果。这就是 MCP 最终为你提供的服务。 +- 那么,你可以想象,在日常的工作中,运营、研发、产品、测试等,都有非常多的重复非创作性的工作,占用了大量的时间成本。尤其是研发,写写代码,就有运营过来,帮我查个问题吧,小嘚嘚。但如果有这样的借助于 MCP 实现的 AI 工作流,就可以完成80%以上的工作量。 +- 之后,站在技术的实现视角,MCP 是一个标准结构框架,你可以按照它(Spring AI)提供的 SDK 开发方式,完成本地化 API 的接入开发。让 AI 有明确的方式调用各类 API 服务接口。如果没有 MCP 这会是一件很麻烦的事情。 + +好啦📢,这就是小傅哥即将为你开启的**《DeepSeek RAG 增强知识库》**第2阶段,`MCP 服务开发和对接`。让你具备 AI 开发能力,遥遥领先于还在扣 CRUD 项目的小伙伴,做面试最牛的仔,最职场最靓的人! + +> Spring AI MCP 与 24年末发布,学习此 AI 应用开发项目,你将是第一批具备 Java AI 应用实战开发能力的人。竞争力,嘎嘎滴! + +## 第1期,RAG 我们做了什么 + +在 《DeepSeek RAG 增强知识库》第1阶段,基于 Spring AI 0.8.1 开发了一套可以上传文件和Git仓库进行解析、切割、存储,到使用向量库完成 AI 的知识库问答系统。并最终通过 Docker 部署上线。 + +
+ +
+ +- 基于,RAG 这样的一套知识库,你可以完成文本和代码库的解析。以及自行扩展提供出 RAG 知识库的接口,对接到 OpenAI 代码自动评审(小傅哥社群的另外一个项目),增强代码评审效果。 +- 在这套项目中,可以学习到 Ollama DeepSeek 本地化&云服务部署,以及流式接口的开发、基于 AI 完成页面与知识库对话接口的处理,Linux、Docker 的部署等。小项目不大,但非常锻炼人! + +## 第2期,MCP 我们要做什么 + +与第2期相比,第1期可以称之为小试牛刀,让小伙伴们以最快、最快的往事,积累,运用 Spring AI 框架,开发自己的 RAG 知识库。~~也是方便有些死鬼,早点写到简历上~~ + +到了第2期,你就开始吃上细糠了,小傅哥会带着你升级 Spring AI 框架为 1.0.0-M6 最新版本,多模型配置和操作 PG 向量库,使用 GPU 搭建响应速度更好的 Ollama DeepSeek 大模型(秒级处理),以及对接官网 DeepSeek 的大模型和统一 one-api 对接方式。 + +但这还只是开始,随着基础框架的升级完成,我们将进入 MCP 服务的开发实现。通过 AI 指令,完成 AI 工作流,调度各项 MCP 处理我们的任务作业。如图,举例操作; + +
+ +
+ +- 基于 MCP 服务的开发和对接,通过 AI 工作流指令,完成数据的采集和存放动作。💡 聪明的小伙伴以及开始联想,基于这样的 AI 开发,可以替代很多的日常工作啦。**没想到吧,也把自己替代了** 但仍然,蠢蠢欲动(我不做,别人也做呀)!~~实现后,晋升又有的讲啦!简历也有东西写啦!~~ +- 有了 MCP 后,相当于把我们需要;在一个网页操作数据库查询数据、打开另外一个网页看天气预报,再手动的创建个文件把以上的信息获取后,复制粘贴到文件里。这一些列操作,都让 AI 通过 MCP 模型上下文协议进行处理。也就是 AI 可以调用后台接口啦! + +## 课程目录计划 💐 + +此课程会扩展很多基于 Java 的 AI 开发能力学习,帮助大家积累相关的场景解决方案。在这个过程中你可以最快的掌握最新的技术,早早的提前别人一步。 + +
+ +
+ +
+ +
+ +- 全课程包括文档 + 小册,全程视频手把手带着做。 +- 课程地址:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) - 含全套项目代码和视频。 + +### 第1期 RAG Spring AI 0.8.1 - 完结 + +1. 【更】AI RAG 知识库,项目介绍&需求分析&环境说明 +2. 【更】初始化知识库工程&部署模型&提交代码 +3. 【更】Ollama DeepSeek 流式应答接口实现 +4. 【更】Ollama DeepSeek 流式应答页面对接 +5. 【更】Ollama RAG 知识库上传、解析和验证 +6. 【更】Ollama RAG 知识库接口服务实现 +7. 【更】基于AI工具,设计前端UI和接口对接 +8. 【更】Git仓库代码库解析到知识库并完善UI对接 +9. 【更】扩展OpenAI模型对接,以及完整AI对接 +10. 【更】云服务器部署知识库(Docker、Nginx) + +### 第2期 MCP Spring AI 1.0.0 - 开冲 + +11. 【新】AI MCP 项目介绍 +12. 【新】吃上细糠,Spring AI 框架升级 + GPU 部署 AI +13. 【新】吃上细糠,官网 DeepSeek + open-api 对接 +14. 【新】MCP 服务的应用类演示和使用 +15. 【新】MCP Spring AI 客户端npx调用,以及资源讲解 +16. 【新】MCP Spring AI 服务端webflux实现 +17. 【新】MCP Spring AI 服务端 + 客户端对接使用 +18. 【新】服务接口实现,增强 RAG 知识库 + MCP 服务使用 +19. 【新】应用服务接口与前端页面对接 +20. ... 随课程开发提供,包括后续的云服务部署。 + +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! + +>课程已经开始,早早加入,早早学习。时间、时机,比任何东西都珍贵!晚点学,只能是工具人。但早点学,就是开发工具的人!加入小傅哥的社群,如图的全套实战项目,都可以学习; \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/\347\254\25411\350\212\202\357\274\232\345\220\203\344\270\212\347\273\206\347\263\240\357\274\214\345\215\207\347\272\247SpringAI\346\241\206\346\236\266.md" "b/docs/md/project/ai-knowledge/\347\254\25411\350\212\202\357\274\232\345\220\203\344\270\212\347\273\206\347\263\240\357\274\214\345\215\207\347\272\247SpringAI\346\241\206\346\236\266.md" new file mode 100644 index 000000000..5413d1ed4 --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\25411\350\212\202\357\274\232\345\220\203\344\270\212\347\273\206\347\263\240\357\274\214\345\215\207\347\272\247SpringAI\346\241\206\346\236\266.md" @@ -0,0 +1,39 @@ +--- +title: 【更】第11节:吃上细糠,升级SpringAI框架 +pay: https://t.zsxq.com/zT6bl +--- + +# 《DeepSeek RAG&MCP 增强检索知识库系统》第11节:吃上细糠,升级SpringAI框架 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/q5rnP](https://t.zsxq.com/q5rnP) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +升级 Spring AI 框架到 1.0.0-M6 版本,以适应于二阶段 MCP(Model Context Protocol 模型上下文协议)服务开发。 + +## 二、本章诉求 + +本节升级包括; +- 提供,快速的 Ollama DeepSeek 部署,支持 1.5b、7b、30b 模型。服务器小时价格很低,速度非常快。 +- 提供,pgAdmin管理工具部署(Docker+本地软件),用于操作 PG 向量库。 +- 提供,向量库自主创建和多模型配置,满足不同模型的向量库使用。 + +## 三、升级框架 + +### 1. POM 配置 + +```pom + + org.springframework.ai + spring-ai-bom + 1.0.0-M6 + pom + import + +``` + +- Spring AI 目前最新版为 1.0.0-M6,[https://spring.io/projects/spring-ai#learn](https://spring.io/projects/spring-ai#learn) diff --git "a/docs/md/project/ai-knowledge/\347\254\25412\350\212\202\357\274\232\345\272\267\345\272\204\345\244\247\351\201\223\357\274\214\344\270\212\346\211\213 AI MCP \345\267\245\344\275\234\346\265\201.md" "b/docs/md/project/ai-knowledge/\347\254\25412\350\212\202\357\274\232\345\272\267\345\272\204\345\244\247\351\201\223\357\274\214\344\270\212\346\211\213 AI MCP \345\267\245\344\275\234\346\265\201.md" new file mode 100644 index 000000000..df9afd82c --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\25412\350\212\202\357\274\232\345\272\267\345\272\204\345\244\247\351\201\223\357\274\214\344\270\212\346\211\213 AI MCP \345\267\245\344\275\234\346\265\201.md" @@ -0,0 +1,23 @@ +--- +title: 【更】第12节:康庄大道,上手 AI MCP 工作流 +pay: https://t.zsxq.com/am5PO +--- + +# 《DeepSeek RAG&MCP 增强检索知识库系统》第12节:康庄大道,上手 AI MCP 工作流 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/GoWka](https://t.zsxq.com/GoWka) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +对接 Spring AI MCP,实现服务端 MCP 和 客户端 MCP,完成功能对接,体验 AI 工作流完成的指令动作。 + +## 二、本节重点 + +本节功能服务包括; +1. 增加,Spring AI MCP 框架接入。 +2. 对接,Spring AI MCP 共用服务端,如;[https://smithery.ai/](https://smithery.ai/)、[https://glama.ai/mcp/servers](https://glama.ai/mcp/servers) +3. 开发,Spring AI MCP 个人客户端,基于 Java 实现客户端。这块是一个独立的新工程组件。 diff --git "a/docs/md/project/ai-knowledge/\347\254\25413\350\212\202\357\274\214\351\201\223\345\261\261\345\255\246\346\265\267\357\274\214\345\256\236\347\216\260MCP\350\207\252\345\212\250\345\217\221\345\270\226\346\234\215\345\212\241.md" "b/docs/md/project/ai-knowledge/\347\254\25413\350\212\202\357\274\214\351\201\223\345\261\261\345\255\246\346\265\267\357\274\214\345\256\236\347\216\260MCP\350\207\252\345\212\250\345\217\221\345\270\226\346\234\215\345\212\241.md" new file mode 100644 index 000000000..0971be32d --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\25413\350\212\202\357\274\214\351\201\223\345\261\261\345\255\246\346\265\267\357\274\214\345\256\236\347\216\260MCP\350\207\252\345\212\250\345\217\221\345\270\226\346\234\215\345\212\241.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第13节,道山学海,实现MCP自动发帖服务 +pay: https://t.zsxq.com/WSM3k +--- + +# 《DeepSeek RAG&MCP 增强检索知识库系统》第13节,道山学海,实现MCP自动发帖服务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/xTjYL](https://t.zsxq.com/xTjYL) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +分析 CSDN 文章发表接口,以 MCP 服务搭建的方式,实现一款 stdio 模式的 CSDN 发帖 MCP 服务。(后续开发 sse 模式) + +**注意**:不限于CSDN,你可以结合本节的学习,在任何一个平台使用它的接口,完成自动发帖服务。如,这里还有一个知识星球的发帖,也可以学习下之后对接实现一个 MCP 服务。地址:[https://bugstack.cn/md/road-map/http.html](https://bugstack.cn/md/road-map/http.html) + +## 二、功能流程 + +如图,实现 CSDN 发帖 MCP 服务流程; + +
+ +
+ +- 首先,无论你是对接任何的平台,都是需要先获得他的接口服务。这种接口一种是平台提供了专门的对接接口,另外就是没有这样的接口,我们是通过浏览器访问网站,获得的接口。哪这些接口通过代码方式完成请求。 +- 之后,基于得到的接口,封装成可以调用的服务 service,这样 MCP 的入口工具,设定好入参信息,就可以调用底层的接口服务了。 +- 最后,当用户提问时,如果你实现了不止一个 CSDN 发帖的 MCP,也包括如星球发帖。那么你的 AI 工作流,是可以顺序的向这些平台自动发帖。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/\347\254\25414\350\212\202\357\274\232\346\265\267\347\272\263\347\231\276\345\267\235\357\274\214\344\270\212\347\272\277MCP\350\207\252\345\212\250\345\217\221\345\270\226\346\234\215\345\212\241.md" "b/docs/md/project/ai-knowledge/\347\254\25414\350\212\202\357\274\232\346\265\267\347\272\263\347\231\276\345\267\235\357\274\214\344\270\212\347\272\277MCP\350\207\252\345\212\250\345\217\221\345\270\226\346\234\215\345\212\241.md" new file mode 100644 index 000000000..89fa1fa42 --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\25414\350\212\202\357\274\232\346\265\267\347\272\263\347\231\276\345\267\235\357\274\214\344\270\212\347\272\277MCP\350\207\252\345\212\250\345\217\221\345\270\226\346\234\215\345\212\241.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第14节,海纳百川,上线MCP自动发帖服务 +pay: https://t.zsxq.com/G5mog +--- + +# 《DeepSeek RAG&MCP 增强检索知识库系统》第14节,海纳百川,上线MCP自动发帖服务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/W0u4s](https://t.zsxq.com/W0u4s) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +以 Jar 包的形式,打包 MCP 自动发帖服务,并以 stdio 方式引入到项目工程。再通过定时任务触达定时自动发帖。 + +## 二、功能流程 + +如图,以 stdio 方式,构建服务打包上线; + +
+ +
+ +- 首先,将 mcp-server-csdn 以 maven 命令方式打一个 jar。IntelliJ IDEA 也可以直接通过界面操作打包 Jar(视频里会演示) +- 之后,将 ai-mcp-knowledge 以 maven 命令方式打一个 jar,并执行 Dockerfile 构建出可部署的镜像。注意,这里额外增加一个阿里云 Docker 镜像仓库,为的是让他提供搭理,方便我们云服务器部署的时候,可以快速拉取下来镜像。此外,如果说你以云服务器当做本机一样使用,在云服务器配置好 maven、git、java jdk 17,那么就可以在云服务器直接构建镜像,也就不需要额外拉取了。(`这部分内容在课程入口-编程环境-云服务器操作中有讲解`) +- 最后,通过 docker-compose 脚本配置上线部署。 diff --git "a/docs/md/project/ai-knowledge/\347\254\25415\350\212\202\357\274\232\345\267\235\346\265\201\344\270\215\346\201\257\357\274\214\345\256\236\347\216\260MCP\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\346\266\210\346\201\257\351\200\232\347\237\245\346\234\215\345\212\241.md" "b/docs/md/project/ai-knowledge/\347\254\25415\350\212\202\357\274\232\345\267\235\346\265\201\344\270\215\346\201\257\357\274\214\345\256\236\347\216\260MCP\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\346\266\210\346\201\257\351\200\232\347\237\245\346\234\215\345\212\241.md" new file mode 100644 index 000000000..561d77da8 --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\25415\350\212\202\357\274\232\345\267\235\346\265\201\344\270\215\346\201\257\357\274\214\345\256\236\347\216\260MCP\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\346\266\210\346\201\257\351\200\232\347\237\245\346\234\215\345\212\241.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第15节,川流不息,实现MCP微信公众号消息通知服务 +pay: https://t.zsxq.com/84IV7 +--- + +# 《DeepSeek RAG&MCP 增强检索知识库系统》第15节,川流不息,实现MCP微信公众号消息通知服务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/Qlqkr](https://t.zsxq.com/Qlqkr) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +AI MCP 是可以让 AI 以工作流方式进行调用的,为了更好的体现这一点,同时也为了增强整体的自动发帖服务链路。本节我们实现一个微信公众号推送消息的 MCP 服务。 + +这一节暂时会先以 stdio 方式开发,之后下一节部署的时候,会把 CSDN、WeiXin 两个 MCP 服务都以 SSE 方式进行部署。让大家学习到不同的开发方式和部署方式。 + +## 二、功能流程 + +如图,自动发帖后,进行微信公众号,消息推送; + +
+ +
+ +- 首先,CSDN 自动发帖是上一节实现的内容,本节要实现一个微信公众号推送模板消息的实现。 +- 之后,AI 调用两套 MCP,可以一次会话,也可以使用 ChatMemory 进行记忆完成2次对话处理 MCP 流程。 +- 最终,实现自动发帖后,完成消息通知给我们自己。点击通知信息可进入具体文章。 + diff --git "a/docs/md/project/ai-knowledge/\347\254\25416\350\212\202\357\274\232\346\201\257\346\201\257\347\233\270\351\200\232\357\274\214MCP \346\234\215\345\212\241\351\203\250\347\275\262\344\270\212\347\272\277\357\274\210sse \346\250\241\345\274\217\357\274\211.md" "b/docs/md/project/ai-knowledge/\347\254\25416\350\212\202\357\274\232\346\201\257\346\201\257\347\233\270\351\200\232\357\274\214MCP \346\234\215\345\212\241\351\203\250\347\275\262\344\270\212\347\272\277\357\274\210sse \346\250\241\345\274\217\357\274\211.md" new file mode 100644 index 000000000..7bd661a88 --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\25416\350\212\202\357\274\232\346\201\257\346\201\257\347\233\270\351\200\232\357\274\214MCP \346\234\215\345\212\241\351\203\250\347\275\262\344\270\212\347\272\277\357\274\210sse \346\250\241\345\274\217\357\274\211.md" @@ -0,0 +1,32 @@ +--- +title: 【更】第16节:息息相通,MCP 服务部署上线(sse 模式) +pay: https://t.zsxq.com/Qexmh +--- + +# 《DeepSeek RAG&MCP 增强检索知识库系统》第16节:息息相通,MCP 服务部署上线(sse 模式) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/J6kpc](https://t.zsxq.com/J6kpc) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +调整 mcp-server-csdn、mcp-server-weixin,两个 MCP 部署方式为 SSE 以及增加 Dockerfile 部署脚本。让服务支持以 sse 方式,被 ai-mcp-knowledge 调用。 + +## 二、功能流程 + +如图,以 sse 方式,构建服务打包上线; + +
+ +
+ +- `SSE` (Server-Sent Events) ,是一种基于 HTTP 的**服务器向客户端单向实时推送数据**的通信技术,常用于实现实时更新功能。 +- 在 Spring AI 框架中,SSE 的实现方式包括 spring-ai-starter-mcp-server-webmvc、spring-ai-starter-mcp-server-webflux 两种框架实现。课程以 webflux 进行使用。 +- SSE 的部署方式,要把每个 mcp 服务,通过 docker 进行部署,提供出可用的接口。之后 ai-mcp-knowledge 工程则配置 sse 方式进行使用。 + +> 接下来我们介绍,如何配置 sse 方式进行对接。这部分主要是配置文件的变化,以及 1.0.0-M6 版本号关于 sse bug 的处理。 + + diff --git "a/docs/md/project/ai-knowledge/\347\254\2541\350\212\202\357\274\232AI RAG \347\237\245\350\257\206\345\272\223\357\274\214\351\241\271\347\233\256\344\273\213\347\273\215.md" "b/docs/md/project/ai-knowledge/\347\254\2541\350\212\202\357\274\232AI RAG \347\237\245\350\257\206\345\272\223\357\274\214\351\241\271\347\233\256\344\273\213\347\273\215.md" new file mode 100644 index 000000000..4ecb2c07a --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\2541\350\212\202\357\274\232AI RAG \347\237\245\350\257\206\345\272\223\357\274\214\351\241\271\347\233\256\344\273\213\347\273\215.md" @@ -0,0 +1,50 @@ +--- +title: 【更】第1节:AI RAG 知识库,项目介绍 +pay: https://t.zsxq.com/bV1QU +--- + +# 《DeepSeek RAG 知识库》第1节:AI RAG 知识库,项目介绍 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/ssFF0](https://t.zsxq.com/ssFF0) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +AI 开发能力,几乎已经是每个工程师必备的能力。在我们去看各个公司招聘要求时,经常会看到具备 AI、AIGC、RAG 能力优先。因为这些东西可以帮助企业提效,结合AI增强自身产品能力。所以,学习一下这样的东西是非常重要的! + +在 AI 的使用场景中,我们考虑过;代码评审、需求开发、智能客服、SQL编写、需求分析等。但这些东西的回答,会随着我们发给 AI 的内容来解答,但受到AI对话的上线文字数限制,不太可能把且有的所有资料都一次发给 AI 进行解答。所以这里引出了企业知识库构建的诉求。它就是 RAG! + +RAG(检索增强生成)技术正在重构互联网知识规则,RAG不仅仅是一个简单的知识存储工具,它是一种将检索与生成相结合的智能系统,能够从海量数据中快速提取相关信息,并结合上下文生成精准、连贯的答案。对于互联网公司而言,构建自己的RAG知识库,意味着能够为用户提供更加个性化、专业化的服务,同时也能够在激烈的市场竞争中,建立起独特的技术壁垒。 + +虽然市面有一些开源的 RAG,但深入使用仍不能解决所有的场景问题,尤其是你想自定义业务诉求,解析更多场景,更复杂的知识库时候,它们就显得不合适了。**并且研发人员,自身应该具备 RAG 开发能力。** + +所以,我们来啦!构建一款属于自己的小型的 RAG 知识库,以此掌握 RAG 相关知识,扩宽自己的技术边界。 + +> 此项目小巧轻量,扩展性好,可以快速学习完成,掌握 RAG 知识库搭建和使用。 + +## 一、项目简述 + +基于 Ollama 部署 DeepSeek 大模型,提供 API 接口。运用 Spring AI 框架承接接口实现 RAG 知识库能力。这款 RAG 知识库支持文本解析,以及 Git 代码库的解析。 + +工程结构采取2层架构,轻量化设计,重点在于突出 RAG 功能实现。以此方式,帮助大家更好的理解,除了工程架构外的 RAG 知识库搭建。方便大家快速上手学习。 + +## 二、什么是 RAG + +[RAG:检索增强生成](https://www.promptingguide.ai/zh/techniques/rag) + +它通用语言模型通过微调就可以完成几类常见任务,比如分析情绪和识别命名实体。这些任务不需要额外的背景知识就可以完成。 + +要完成更复杂和知识密集型的任务,可以基于语言模型构建一个系统,访问外部知识源来做到。这样的实现与事实更加一性,生成的答案更可靠,还有助于缓解“幻觉”问题。 + +Meta AI 的研究人员引入了一种叫做检索增强生成(Retrieval Augmented Generation,RAG)的方法来完成这类知识密集型的任务。RAG 把一个信息检索组件和文本生成模型结合在一起。RAG 可以微调,其内部知识的修改方式很高效,不需要对整个模型进行重新训练。 + +RAG 会接受输入并检索出一组相关/支撑的文档,并给出文档的来源(例如维基百科)。这些文档作为上下文和输入的原始提示词组合,送给文本生成器得到最终的输出。这样 RAG 更加适应事实会随时间变化的情况。这非常有用,因为 LLM 的参数化知识是静态的。RAG 让语言模型不用重新训练就能够获取最新的信息,基于检索生成产生可靠的输出。 + +Lewis 等人(2021)提出一个通用的 RAG 微调方法。这种方法使用预训练的 seq2seq 作为参数记忆,用维基百科的密集向量索引作为非参数记忆(使通过神经网络预训练的检索器访问) + +
+ +
\ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/\347\254\2542\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\347\237\245\350\257\206\345\272\223\345\267\245\347\250\213&\346\217\220\344\272\244\344\273\243\347\240\201.md" "b/docs/md/project/ai-knowledge/\347\254\2542\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\347\237\245\350\257\206\345\272\223\345\267\245\347\250\213&\346\217\220\344\272\244\344\273\243\347\240\201.md" new file mode 100644 index 000000000..3b7af907d --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\2542\350\212\202\357\274\232\345\210\235\345\247\213\345\214\226\347\237\245\350\257\206\345\272\223\345\267\245\347\250\213&\346\217\220\344\272\244\344\273\243\347\240\201.md" @@ -0,0 +1,39 @@ +--- +title: 【更】第2节:初始化知识库工程&提交代码 +pay: https://t.zsxq.com/VZSnk +--- + +# 《DeepSeek RAG 知识库》第2节:初始化知识库工程&提交代码 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/07kXb](https://t.zsxq.com/07kXb) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +教会小伙伴使用 IntelliJ IDEA 创建多模块工程,并介绍分层模块的用途。以及相关基础环境安装。 + +课程会循序渐进的从0到1,逐步带着大家完成项目的开发。开局只有一把 IntelliJ IDEA,完成项目后你可以学习到;业务、架构、设计、方案、配置、部署(Linux、Docker)等各项知识。 + +## 二、如何开始 + +站在读者视角,该怎么学习呢?这里做个必要说明和前置的知识提供。 + +### 1. 前置学习 + +小伙伴在学习的时候,可以依照课程的方式进行创建项目、变更配置、启动测试。这里有一些前置学习,包括:Git、Maven、Docker,课程已经准备好了,可以刷下; + +- Git:[https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) +- Maven:[https://bugstack.cn/md/road-map/maven.html](https://bugstack.cn/md/road-map/maven.html) +- Docker:[https://bugstack.cn/md/road-map/docker-what.html](https://bugstack.cn/md/road-map/docker-what.html) - `入门 Docker 必看` +- Docker 镜像:[https://t.zsxq.com/2DGGY](https://t.zsxq.com/2DGGY) + +另外课程会使用 Java JDK 17、Maven 3.8.x,软件已经提供好,可以直接下载;[https://t.zsxq.com/19Rnk98M0](https://t.zsxq.com/19Rnk98M0) - `附件内含有配置好阿里云镜像的 Maven` + +### 2. 开始学习 + +1. 你需要通过 `git clone https://xxx.git` 命令,或者 IntelliJ IDEA 自动的检出工程方式,把项目工程检出到本地。关于如何使用Git检出项目,在前置学习里提供了教程。 +2. 检出代码后,你可以通过 IntelliJ IDEA 打开项目,并按照每一节最开始说明的本节对应的代码分支,把工程代码切换到对应的这一节。 +3. 接下来你可以通过课程的视频和小册以及提供的代码进行学习,并跟随课程每节要完成的内容,一步步操作。过程中可以参考课程的代码进行学习。如果自己的代码运行出问题的时候,可以运行课程的代码验证是环境问题还是个人代码问题。**另外注意运行课程代码,要修改对应的环境为你的本地环境,postgresql、redis等** \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/\347\254\2543\346\216\245\357\274\232Ollama DeepSeek \346\265\201\345\274\217\345\272\224\347\255\224\346\216\245\345\217\243\345\256\236\347\216\260.md" "b/docs/md/project/ai-knowledge/\347\254\2543\346\216\245\357\274\232Ollama DeepSeek \346\265\201\345\274\217\345\272\224\347\255\224\346\216\245\345\217\243\345\256\236\347\216\260.md" new file mode 100644 index 000000000..a0aff68f5 --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\2543\346\216\245\357\274\232Ollama DeepSeek \346\265\201\345\274\217\345\272\224\347\255\224\346\216\245\345\217\243\345\256\236\347\216\260.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第3节:Ollama DeepSeek 流式应答接口实现 +pay: https://t.zsxq.com/h5VxZ +--- + +# 《DeepSeek RAG 知识库》第3节:Ollama DeepSeek 流式应答接口实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/4DbVB](https://t.zsxq.com/4DbVB) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +引入 Spring AI 框架组件,对接 Ollama DeepSeek 提供服务接口。包括;普通应答接口和流式接口。 + +## 二、技术方案 + +对接 AI 的方式有很多,比如;`AI 官网提供的 SDK`、`自研 SDK 组件`、`one-api 服务类统一包装接口`,其中自研类 SDK 已经在星球 openai 项目对接 chatglm、chatgpt 的时候进行设计,为了差异化学习到不同技术,本项目会采用 Spring AI 框架进行对接。 + +
+ +
+ +官网:[https://spring.io/projects/spring-ai](https://spring.io/projects/spring-ai) + +Spring AI 支持;OpenAI,Microsoft,Amazon,Google和Ollama,大模型的对接。其他不属于这个范围的,可以通过 [one-api](https://github.com/songquanpeng/one-api) 配置,统一转换为 OpenAI 接口服务格式进行使用。 + + diff --git "a/docs/md/project/ai-knowledge/\347\254\2544\350\212\202\357\274\232Ollama DeepSeek \346\265\201\345\274\217\345\272\224\347\255\224\351\241\265\351\235\242\345\257\271\346\216\245.md" "b/docs/md/project/ai-knowledge/\347\254\2544\350\212\202\357\274\232Ollama DeepSeek \346\265\201\345\274\217\345\272\224\347\255\224\351\241\265\351\235\242\345\257\271\346\216\245.md" new file mode 100644 index 000000000..8797a9c43 --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\2544\350\212\202\357\274\232Ollama DeepSeek \346\265\201\345\274\217\345\272\224\347\255\224\351\241\265\351\235\242\345\257\271\346\216\245.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第4节:Ollama DeepSeek 流式应答页面对接 +pay: https://t.zsxq.com/PN4lw +--- + +# 《DeepSeek RAG 知识库》第3节:Ollama DeepSeek 流式应答接口实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/f82uj](https://t.zsxq.com/f82uj) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +实现一款简单的UI界面,与服务端 Ollama DeepSeek AI 进行对接。 + +## 二、技术方案 + +自从有了 AI 工具以后,所有以前头疼前端页面开发的后端程序员👨🏻‍💻,都漏出了友善😊微笑! + +主要我们可以清楚地表达编写页面诉求,AI 工具就可以非常准确且迅速的完成代码的实现。这里我们可以选择的 AI 有很多,包括;OpenAI、DeepSeek、智谱AI等等。 + +- [https://www.deepseek.com/](https://www.deepseek.com/) +- [https://v0.dev/](https://v0.dev/) +- [https://chatglm.cn/](https://chatglm.cn/) +- [https://openai.itedus.cn/](https://openai.itedus.cn/) + +>其他更多也可以网络检索下。总有一款你是可以使用的。 + + diff --git "a/docs/md/project/ai-knowledge/\347\254\2545\350\212\202\357\274\232Ollama RAG \347\237\245\350\257\206\345\272\223\344\270\212\344\274\240\343\200\201\350\247\243\346\236\220\345\222\214\351\252\214\350\257\201.md" "b/docs/md/project/ai-knowledge/\347\254\2545\350\212\202\357\274\232Ollama RAG \347\237\245\350\257\206\345\272\223\344\270\212\344\274\240\343\200\201\350\247\243\346\236\220\345\222\214\351\252\214\350\257\201.md" new file mode 100644 index 000000000..a2521598b --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\2545\350\212\202\357\274\232Ollama RAG \347\237\245\350\257\206\345\272\223\344\270\212\344\274\240\343\200\201\350\247\243\346\236\220\345\222\214\351\252\214\350\257\201.md" @@ -0,0 +1,36 @@ +--- +title: 【更】第5节:Ollama RAG 知识库上传、解析和验证 +pay: https://t.zsxq.com/fTK4R +--- + +# 《DeepSeek RAG 知识库》第5节:Ollama RAG 知识库上传、解析和验证 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/PM15B](https://t.zsxq.com/PM15B) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +以大模型向量存储的方式,提交本地文件到知识库。并在 AI 对话中增强检索知识库符合 AI 对话内容的资料,合并提交问题。 + +## 二、技术方案 + +以 Spring AI 提供的向量模型处理框架,将上传文件以 TikaDocumentReader 方式进行解析,再通过 TokenTextSplitter 拆分文件。完成这些操作后,在遍历文档添加标记。标记的作用是为了可以区分不同的知识库内容。完成这些动作后,把这些拆解并打标的文件存储到 postgresql 向量库中。 + +本技术方案旨在利用 Spring AI 提供的向量模型处理框架,对上传的文件进行解析、拆分、标记,并将处理后的数据存储到 PostgreSQL 向量库中。通过这一流程,可以实现对文件内容的高效管理和检索,特别是在需要区分不同知识库内容的场景下。 + +### 1. 技术组件 + +- **Spring AI**: 提供向量模型处理框架,支持文件的解析、拆分和向量化操作。 +- **TikaDocumentReader**: 用于解析上传的文件,支持多种文件格式(如 MD、TXT、SQL 等)。 +- **TokenTextSplitter**: 用于将解析后的文本内容拆分为更小的片段,便于后续处理和存储。 +- **PostgreSQL 向量库**: 用于存储处理后的文本向量数据,支持高效的相似性搜索和检索。 + +### 2. 方案流程 + +#### 2.1 文件上传与解析 + +1. **文件上传**: 用户通过前端界面或 API 上传文件,文件可以是多种格式(如 MD、TXT、SQL 等)。 +2. **文件解析**: 使用 `TikaDocumentReader` 对上传的文件进行解析,提取出文本内容。`TikaDocumentReader` 能够处理多种文件格式,并提取出结构化的文本数据。 \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/\347\254\2546\350\212\202\357\274\232Ollama RAG \347\237\245\350\257\206\345\272\223\346\216\245\345\217\243\346\234\215\345\212\241\345\256\236\347\216\260.md" "b/docs/md/project/ai-knowledge/\347\254\2546\350\212\202\357\274\232Ollama RAG \347\237\245\350\257\206\345\272\223\346\216\245\345\217\243\346\234\215\345\212\241\345\256\236\347\216\260.md" new file mode 100644 index 000000000..646517066 --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\2546\350\212\202\357\274\232Ollama RAG \347\237\245\350\257\206\345\272\223\346\216\245\345\217\243\346\234\215\345\212\241\345\256\236\347\216\260.md" @@ -0,0 +1,34 @@ +--- +title: 【更】第6节:Ollama RAG 知识库接口服务实现 +pay: https://t.zsxq.com/EvnBN +--- + +# 《DeepSeek RAG 知识库》第6节:Ollama RAG 知识库接口服务实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/yNiZl](https://t.zsxq.com/yNiZl) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +以上一节知识库的测试案例,将这部分功能以接口方式提供。包括;知识库的上传、选择和使用。 + +## 二、技术方案 + +知识库的上传和使用是明确的,但选择哪个知识库是需要把对应的知识库记录起来。这里我们选择 Redis 列表进行记录。如果是公司里大型的知识库,还需要使用 MySQL 数据库进行存储。 + +## 三、功能实现 + +### 1. 工程结构 + +### 2. 引入组件 + +```java + + org.redisson + redisson-spring-boot-starter + 3.44.0 + +``` \ No newline at end of file diff --git "a/docs/md/project/ai-knowledge/\347\254\2547\350\212\202\357\274\232\345\237\272\344\272\216AI\345\267\245\345\205\267\357\274\214\350\256\276\350\256\241\347\237\245\350\257\206\345\272\223UI\345\222\214\346\216\245\345\217\243\345\257\271\346\216\245.md" "b/docs/md/project/ai-knowledge/\347\254\2547\350\212\202\357\274\232\345\237\272\344\272\216AI\345\267\245\345\205\267\357\274\214\350\256\276\350\256\241\347\237\245\350\257\206\345\272\223UI\345\222\214\346\216\245\345\217\243\345\257\271\346\216\245.md" new file mode 100644 index 000000000..974f043ea --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\2547\350\212\202\357\274\232\345\237\272\344\272\216AI\345\267\245\345\205\267\357\274\214\350\256\276\350\256\241\347\237\245\350\257\206\345\272\223UI\345\222\214\346\216\245\345\217\243\345\257\271\346\216\245.md" @@ -0,0 +1,35 @@ +--- +title: 【更】第7节:基于AI工具,设计知识库UI和接口对接 +pay: https://t.zsxq.com/Jc7fE +--- + +# 《DeepSeek RAG 知识库》第7节:基于AI工具,设计知识库UI和接口对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/BeT7k](https://t.zsxq.com/BeT7k) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +基于我们要实现对话和知识的上传使用,使用AI工具完成UI页面的实现。 + +## 二、技术方案 + +找一个合适的喜欢的AI对话页面,截取上传到 AI 工具,并告知基于这样的 UI 效果完成页面的实现。之后在告诉 AI 处理接口的对接。当然也可以把接口一起交给 AI 工具进行处理。 + +- [https://www.deepseek.com/](https://www.deepseek.com/) +- [https://v0.dev/](https://v0.dev/) +- [https://chatglm.cn/](https://chatglm.cn/) +- [https://openai.itedus.cn/](https://openai.itedus.cn/) + +>其他更多也可以网络检索下。总有一款你是可以使用的。 + +## 三、功能实现 + +### 1. 对话页面 + +
+ +
diff --git "a/docs/md/project/ai-knowledge/\347\254\2548\350\212\202\357\274\232Git\344\273\223\345\272\223\344\273\243\347\240\201\345\272\223\350\247\243\346\236\220\345\210\260\347\237\245\350\257\206\345\272\223.md" "b/docs/md/project/ai-knowledge/\347\254\2548\350\212\202\357\274\232Git\344\273\223\345\272\223\344\273\243\347\240\201\345\272\223\350\247\243\346\236\220\345\210\260\347\237\245\350\257\206\345\272\223.md" new file mode 100644 index 000000000..ecf59d99a --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\2548\350\212\202\357\274\232Git\344\273\223\345\272\223\344\273\243\347\240\201\345\272\223\350\247\243\346\236\220\345\210\260\347\237\245\350\257\206\345\272\223.md" @@ -0,0 +1,34 @@ +--- +title: 【更】第8节:Git仓库代码库解析到知识库 +pay: https://t.zsxq.com/ByovV +--- + +# 《DeepSeek RAG 知识库》第8节:Git仓库代码库解析到知识库 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/EwqZH](https://t.zsxq.com/EwqZH) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +对知识库的解析进行扩展,增加Git仓库解析。用户填写Git仓库地址和账密,即可拉取代码并上传到知识库,之后就可以基于这套代码进行使用啦。 + +## 二、技术方案 + +引入 JGit 操作库到工程中,用于执行 Git 命令拉取代码仓库。之后对代码库文件进行遍历,依次解析分割上传到向量库中。 + +## 三、功能实现 + +### 1. 工程结构 + +### 2. 引入组件 + +```java + + org.eclipse.jgit + org.eclipse.jgit + 5.13.0.202109080827-r + +``` diff --git "a/docs/md/project/ai-knowledge/\347\254\2549\350\212\202\357\274\232\346\211\251\345\261\225OpenAI\346\250\241\345\236\213\345\257\271\346\216\245\357\274\214\344\273\245\345\217\212\345\256\214\346\225\264AI\345\257\271\346\216\245.md" "b/docs/md/project/ai-knowledge/\347\254\2549\350\212\202\357\274\232\346\211\251\345\261\225OpenAI\346\250\241\345\236\213\345\257\271\346\216\245\357\274\214\344\273\245\345\217\212\345\256\214\346\225\264AI\345\257\271\346\216\245.md" new file mode 100644 index 000000000..841bf2c1d --- /dev/null +++ "b/docs/md/project/ai-knowledge/\347\254\2549\350\212\202\357\274\232\346\211\251\345\261\225OpenAI\346\250\241\345\236\213\345\257\271\346\216\245\357\274\214\344\273\245\345\217\212\345\256\214\346\225\264AI\345\257\271\346\216\245.md" @@ -0,0 +1,33 @@ +--- +title: 【更】第9节:扩展OpenAI模型对接,以及完整AI对接 +pay: https://t.zsxq.com/YJxRy +--- + +# 《DeepSeek RAG 知识库》第9节:扩展OpenAI模型对接,以及完整AI对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
博客:[https://t.zsxq.com/1JLUq](https://t.zsxq.com/1JLUq) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +基于 Spring AI 扩展 OpenAI 模型对接,这样我们就可以使用一些代理的 ChatGPT 接口完成对话了。最终在完成全部接口与页面的对接。 + +## 二、技术方案 + +Spring AI 框架的好处,就是可以以统一的方式直接配置使用各类大模型。像是一些 Spring AI 没有直接对接的大模型,可以基于 one-api 配置转发,用统一 OpenAI 方式进行对接。 + +## 三、功能实现 + +### 1. 工程结构 + +### 2. 引入组件 + +```java + + org.springframework.ai + spring-ai-openai-spring-boot-starter + +``` \ No newline at end of file diff --git a/docs/md/project/ai-mcp-gateway/ai-mcp-gateway.md b/docs/md/project/ai-mcp-gateway/ai-mcp-gateway.md new file mode 100644 index 000000000..6ac01e671 --- /dev/null +++ b/docs/md/project/ai-mcp-gateway/ai-mcp-gateway.md @@ -0,0 +1,121 @@ +--- +title: AI MCP Gateway 网关服务系统 +lock: no +--- + +# 《AI MCP Gateway 网关服务系统》 - 为各类应用服务接口,便捷转换为MCP服务而设计。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/SNsgH](https://t.zsxq.com/SNsgH) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +两年多了,从2023年1月起,小傅哥就开始关注AI、跟进AI,落地 AI 技术相关的场景项目。`做了 AI 问答自动回复助手`、`OpenAI 应用服务(含支付买额度 + OpenAI SDK 开发)`、`OpenAI 代码评审`、`Ai Agent 智能体(RAG、MCP)`,让一众伙伴积累到了丰富的AI应用开发技术,可以满足当下互联网AI应用开发招聘诉求。**那么接下来,关于 AI 小傅哥还要带着大家什么呢?🤔** + +
+ +
+ +**足够高频,互联网AI应用场景,必备项目!** + +现阶段,众多互联网公司把 AI 作为公司的战略目标,基于 AI 为公司各类场景提效。甚至不少公司要求程序员的编码,AI 占比要有30%以上(来自于某论坛研发分享)。还在各个部门设立 AI 应用创新组。 + +而 AI 的主要提效方式,则是自研实现或基于 Dify 搭建一套 AI Agent 智能体。通过智能体把公司的文档资料转换为 RAG 知识库,再通过 MCP 协议对接各类应用的服务接口。这样我们对 AI Agent 智能体下达命令后,就可以进行分析,规划,执行,直至产出最终的结果。更多关于智能体的实现介绍,可以参考[《AI Agent 智能体设计实现》](https://mp.weixin.qq.com/s/dwsfadYKs7Uy4YvHfLFsVQ) + +**死鬼,重点来啦!** + +公司里为了扩展智能体可提效的场景,就要把现有的公司的各类应用服务接口,`日志的`、`监控的`、`服务的`、`交易的`、`结算的`、`营销的`、`人群的`、`数据的`等等,都要转换为 AI Agent 智能体可识别的 MCP 服务接口。这样就可能有成百上千,成千上完,甚至几十万个接口要做实现。 + +那肯定不能每个接口都写一遍 MCP 服务!所以,对于这样的场景问题,公司里会做一套统一的 MCP Gateway 网关服务系统,其他个各类接口(http、rpc),都可以通过一键配置的方式转换为 MCP 协议类型的接口,被 AI 可以识别和使用。 + +
+ +
+ +而小傅哥这次带着你做的 AI 类项目,就是实现一套这样的 **MCP GateWay 网关服务系统**。小傅哥,会带着你,实现MCP服务,分析 MCP 协议,通过做AI服务代理、网页协议对接、JSON-RPC2标准等方式,把 MCP 协议厘清,之后在手把手的带着你编写响应式的 MCP Gateway 网关能力(鉴权也可以放到网关做)。 + +> 🧧 文末提供了,小傅哥所有编程实战项目获取方式,一次加入即可获得17个已完结的和本次新开展的。 + +## 一、能学到啥 + +该项目是 AI 应用场景下的通用技术服务组件类项目,以解决接口 MCP 协议转换而设计实现。在整个项目中,你可以积累到关于 MCP 协议的深度分析,学习分析协议的技巧和方案,并积累关于设计一个组件解决通用场景问题的能力。 + +- 【前端】基于 html、js、div、css,设计 MCP 协议分析页面。 +- 【前端】基于 html、js、div、css,构建一套服务端管理系统,便于 MCP 协议的录入和使用。 +- 【后端】MCP 协议的分析、理解、运用。掌握 MCP 开发和使用的能力。 +- 【后端】基于 MCP 协议的分析和网关设计诉求,构建网关服务库表。 +- 【后端】运用 DDD 分层架构,设计 MCP 网关服务系统。 +- 【后端】构建AI代理服务,断点调试分析 MCP 协议。 +- 【后端】设计 MCP 分析协议网页服务,链接 MCP 服务,观察 MCP 协议。 +- 【后端】基于 Flux 响应式接口,设计实现 MCP 协议的 sse 连接、initialize 初始化响应、tools 工具的 list 反馈和 call 调用等。 +- 【后端】提供 MCP 协议的动态录入和加载能力,以及提供录入接口组件,便于其他系统可快速录入。 +- 【后端】设计 MCP 网关协议鉴权服务,确保 MCP 服务使用的安全性。 +- 【后端】熟练使用 okhttp3、retrofit2 框架,动态对接 HTTP 服务接口,用于 MCP 协议 toos/call 工具调用。 +- 【后端】扩展学习 rpc 泛化调用,给 MCP 协议提供使用。其实有了这套东西,还可以对接如硬件设备 rs232 串口通信,让 MCP 服务,管理你的硬件设备。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 + +此外,小傅哥对于每个章节还讲解了章节的诉求、流程的设计,之后再到方案实现和功能验证。并在每个章节留有作业让大家练习。当然这还没有完,你知道小傅哥这个架构师画图还是非常牛逼的,所以你还能看到各种画图的技巧,耳濡目染的把这些东西学习成自己的本事!~ + +## 二、项目介绍 + +本项目是 AI Agent 智能体,关于 MCP 协议对接的通用网关服务项目,以解决各类业务接口便捷转换为 MCP 协议而设计实现。通过这样的配置,可以大大的简化从普通http、rpc接口到 MCP 协议的转换操作。这样的项目,也是每个互联网公司在做 AI Agent 智能体时,必备的基础设施项目。 + +### 1. 更新计划 + +本项目目前已经做了基础的筹备和验证,计划于10.26日开始更新课程。整体课程预计在20+节左右。带着你完整的实现一套 AI MCP Gateway 网关服务项目。 + +### 2. 项目资料 + +#### 2.1 协议分析 - 页面 + +
+ +
+ +- 编写了一套网页对接 MCP 服务的页面,把以前直接在 AI Agent 配置 MCP 协议使用的过程,通过页面一步步对接和使用的方式进行展示。 +- 有了这样一个操作过程步骤,你可以更加清晰的了解到 MCP 的执行过程,也能更好的为后续做 MCP 网关服务实现打下基础。 + +#### 2.2 协议分析 - 代理 + +
+ +
+ +- 为了更好的体现出 AI 和 MCP 的交互,这里小傅哥会带着你做一个 AI 的代理接口,来调试观察 MCP 协议的传输。 + +#### 2.3 初始版本 - 案例 + +
+ +
+- 小傅哥这里先做了一个初始的 demo 版本,跑通 MCP 网关,后续会设计整套 DDD 架构,完整整体协议和服务的对接。 + +## 三、课程大纲 + +**不同于网上demo项目。小傅哥带着你做的项目,是一步步,一个个章节的带着大家从0到1的方式,进行分析、设计和开发。是一个纯手把手教大家学习实战技术的项目!** 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。 + +- 第1节:项目功能需求分析 +- 第2节:项目系统架构设计 +- 第3节:MCP 功能服务实现 +- 第4节:AI 服务代理实现 +- 第5节:通过AI 服务代理,分析 MCP 协议 +- 第6节:通过设计网页对接,分析 MCP 协议 +- 第7节:通过 json-rpc2 标准,官网资料,总结 MCP 协议 +- 第8节:设计网关服务端系统,讲解模块关系 +- 第9节:设计拆分领域模型结构 +- 第10节:设计需求服务库表 +- 第11节:网关协议功能编写(1、2、3、4),分步骤设计实现 +- 第12节:网关协议与数据库表对接 +- 第13节:网关协议与http接口对接 +- 第14节:网关协议与rpc接口对接 +- 第15节:网关协议能力管理端编写(1、2、3、4) +- 第16节:MCP 网关服务打包上线 + +随着课程开展,陆续更新课程目录,也会有一些新的内容加入。 + +>课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!! + diff --git a/docs/md/project/ai-mcp-gateway/none.md b/docs/md/project/ai-mcp-gateway/none.md new file mode 100644 index 000000000..f64218b2f --- /dev/null +++ b/docs/md/project/ai-mcp-gateway/none.md @@ -0,0 +1,15 @@ +--- +title: 新章节,编写中 +lock: no +--- + +# 新章节,编写中 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +... 新章节,编写中 \ No newline at end of file diff --git a/docs/md/project/ai-mcp-gateway/promotion/ai-mcp-gateway-stage-completion.md b/docs/md/project/ai-mcp-gateway/promotion/ai-mcp-gateway-stage-completion.md new file mode 100644 index 000000000..91fed7a22 --- /dev/null +++ b/docs/md/project/ai-mcp-gateway/promotion/ai-mcp-gateway-stage-completion.md @@ -0,0 +1,196 @@ +--- +title: AI MCP 网关,可以写简历啦! +lock: no +--- + +# AI MCP 网关,可以写简历啦! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/SNsgH](https://t.zsxq.com/SNsgH) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**都写 AI Agent,怎么拉开差距?** 但现在出去面试,你说简历要是连一点 AI 的都没有,,都不好意思说自己是学技术的。但写完 AI Agent 智能体,又容易被说,你是不就只是会用 API 呀,你有深入了解底层技术吗?了解吗,死鬼! + +
+ +
+ +**不了解呀,那怎么办!** + +所以,除了做 AI Agent 智能体,小傅哥还带着你深入核心技术的设计与实现,手把手带着你搓一套 AI MCP 网关(各个大厂都在做的东西)。有这样一套东西,再配合 AI Agent 智能体,就把技术深度给拉起来了。`最后写简历效果咋样?` 有伙伴反馈,如下; + +
+ +
+ +企业里为啥看重这个? + +现在虽然是 AI 时代,AI 编码,但能驾驭 AI 的人主要是程序员,能更强的驾驭 AI 编码的,肯定编程经验丰富的。所以,你如果能在核心复杂场景里,提供出对应的技术解决方案,那么肯定是会受到企业招聘青睐的。(没有这方面经验,AI 写出来,你都不敢真的对外上线用起来)。 + +那么,为啥是 AI MCP 网关,在企业里那么重要呢? + +当下互联网toc的业务场景,你会看到你用的购物(天猫、京东)、出行(滴滴)、外卖(美团、饿了吗),都有 AI 场景,这些场景辅助用户完成购物、出行、点餐,极大的增加体验。但本质来说,是 AI 对接了业务系统。怎么对接的呢,方式就是通过 MCP 协议对接,把业务项目的接口(http、rpc)转换为可被 AI 识别的接口,以此方式完成对接。但如果全部手动编码实现,是非常麻烦的。所以 AI MCP Gateway 统一解决这样一个场景问题,是非常有价值的。 + +
+ +
+ +
+ +
+ +扫码加入 AI MCP GateWay 项目学习社群,直接拿到课程,立马开学!(简历模板也都提供好了) + +> MCP 解决接口传输协议,Skills 规范流程执行 SOP 标准。 + +## 一、能学到啥 + +该项目是 AI 应用场景下的通用技术服务组件类项目,以解决接口 MCP 协议转换而设计实现。在整个项目中,你可以积累到关于 MCP 协议的深度分析,学习分析协议的技巧和方案,并积累关于设计一个组件解决通用场景问题的能力。 + +- 【前端】基于 html、js、div、css,设计 MCP 协议分析页面。 +- 【前端】基于 html、js、div、css,构建一套服务端管理系统,便于 MCP 协议的录入和使用。 +- 【后端】MCP 协议的分析、理解、运用。掌握 MCP 开发和使用的能力。 +- 【后端】基于 MCP 协议的分析和网关设计诉求,构建网关服务库表。 +- 【后端】运用 DDD 分层架构,设计 MCP 网关服务系统。 +- 【后端】构建AI代理服务,断点调试分析 MCP 协议。 +- 【后端】设计 MCP 分析协议网页服务,链接 MCP 服务,观察 MCP 协议。 +- 【后端】基于 Flux 响应式接口,设计实现 MCP 协议的 sse 连接、initialize 初始化响应、tools 工具的 list 反馈和 call 调用等。 +- 【后端】提供 MCP 协议的动态录入和加载能力,以及提供录入接口组件,便于其他系统可快速录入。 +- 【后端】设计 MCP 网关协议鉴权服务,确保 MCP 服务使用的安全性。 +- 【后端】熟练使用 okhttp3、retrofit2 框架,动态对接 HTTP 服务接口,用于 MCP 协议 toos/call 工具调用。 +- 【后端】扩展学习 rpc 泛化调用,给 MCP 协议提供使用。其实有了这套东西,还可以对接如硬件设备 rs232 串口通信,让 MCP 服务,管理你的硬件设备。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 + +此外,小傅哥对于每个章节还讲解了章节的诉求、流程的设计,之后再到方案实现和功能验证。并在每个章节留有作业让大家练习。当然这还没有完,你知道小傅哥这个架构师画图还是非常牛逼的,所以你还能看到各种画图的技巧,耳濡目染的把这些东西学习成自己的本事!~ + +## 二、项目演示 + +该项目,已完成一整套的 AI MCP 网关服务能力,并提供了完整的后台 UI 实现。可以基于 Swagger OpenAPI 协议动态导入接口,转换为 AI MCP GateWay 网关协议。配置后,可以直接在网关管理后台,完成接口的使用验证。 + +### 1. 登录页面 + +
+ +
+ +### 2. 网关列表 + +
+ +
+ +### 3. 查看网关 + +
+ +
+ +### 4. 协议配置 + +
+ +
+ +### 5. 网关测试 + +
+ +
+ +## 三、简历模板 + +### 1. 项目介绍 + +面试官您好,本套 AI MCP GateWay 网关项目,对标阿里的 higress-ai,以通用方案解决各类业务接口便捷转换为 MCP 协议而设计实现。通过这样的配置,可以大大的简化从普通http、rpc接口到 MCP 协议的转换操作。 + +该项目采用 DDD 领域驱动架构设计,按照 MCP 协议 json-rpc2 标准,构建 AI MCP GateWay 能力服务。因而设计拆分领域模型为;session 域(会话、消息)、协议域(协议、存储)、网关域、鉴权域、LLM(模型测试网关)、管理域(一般公司的大型项目,会单独拆分这个领域为独立系统),来支撑整个服务实现,最终由 case 层编排逻辑,再给 trigger 接口层调用处理。 + +为什么自研?对企业来说,公司需要整合内部完整能力的统一 AI MCP 网关解决方案,随着自身的业务发展而不断迭代处理。在这个过程,就可以快速支撑业务诉求,也可以保证系统的安全和可靠性。如果只是引入外部的,那么可能顺带着要引入一大票的额外系统服务,在遇到一些场景诉求或者问题的时候,也很难做到快速处理。所以我们要自研来实现。另外,对个人来说,我们需要一个技术积累,而不是会用。 + +### 2. 简历模板 + +**项目名称**:AI MCP Gateway 统一网关服务系统 + +**项目架构**:DDD 领域驱动设计(六边形架构)、JSON-RPC2 MCP 上下文协议、前后端分离 + +**核心技术**:SpringBoot 3.4、Spring WebFlux、Retrofit 2.9.0 & OkHttp 4.9.3、JDK 17+、MCP(JSON-RPC2) + +**项目介绍**:本项目是 AI Agent 智能体,关于 MCP 协议对接的通用网关服务项目,以解决各类业务接口便捷转换为 MCP 协议而设计实现。通过这样的配置,可以大大的简化从普通http、rpc接口到 MCP 协议的转换操作。这样的项目,也是每个互联网公司在做 AI Agent 智能体时,必备的基础设施项目。 + +**核心职责**: + +>简历的职责,是你需要通过不同方面的举证,表述出自己的能力储备。所以,以下是各个方面的描述案例,你可以按照此方式解决描述你的简历。 + +- 架构设计: + - 以 DDD 领域驱动设计,四色建模分析领域模型,划分出;session 域(会话、消息)、协议域(协议、存储)、网关域、鉴权域、LLM(模型测试网关)、管理域。 + - 采用六边形架构解耦领域逻辑与基础设施,确保系统可扩展性和可维护性 + - 设计基于 Reactor 的响应式编程模型,实现高性能的 SSE (Server-Sent Events) 通信 + +- 设计模式: + - 通过策略模式处理 MCP(JSON-RPC2)消息多场景类型的处理,包括;初始(Initialize)、资源(Resources)、工具(ToolsList)、调用(ToolsCall)的场景,有利于后续其他动作的扩展。 + - 使用组合模式(规则树)在 case 领域层,编排串联 domain 领域服务,让各类场景便于迭代和维护。 + +- 核心领域(不用都写): + - **Session 领域**:设计会话管理核心模型,包括 HandleMessageCommandEntity、SessionConfigVO 等实体,实现会话创建、消息处理、状态管理 + - **Gateway 领域**:构建网关配置模型,包括 GatewayConfigEntity、GatewayToolConfigEntity 等,支持动态网关配置和工具管理 + - **Protocol 领域**:设计 MCP 协议解析和封装机制,实现 JSON-RPC 消息的序列化和反序列化 + - **Admin 领域**:实现管理后台的领域逻辑,支持网关配置、认证管理等核心功能 + - **Domain 层**:定义核心领域接口和值对象,如 ISessionManagementService、IGatewayConfigService 等 + - **Case 层**:实现用例层逻辑,包括 IMcpSessionService、IMcpMessageService 等服务接口 + - **Trigger 层**:开发 HTTP 触发器,实现 MCP 网关的 RESTful API 接口 + - **Infrastructure 层**:构建基础设施适配器,包括数据库访问、Redis 缓存、网关适配器等 + +- 功能方案: + - 基于 Spring WebFlux 和 Reactor 实现响应式编程,支持高并发 SSE 连接 + - 设计 MCP 协议解析器,实现 JSON-RPC 消息的标准化处理 + - 开发网关配置管理系统,支持动态配置更新和热部署 + - 实现会话管理机制,包括会话创建、消息处理、状态同步等功能 + - 设计基于 API Key 的认证机制,确保网关访问安全 + - 实现 Spring Security 集成,支持细粒度权限控制 + - 开发速率限制和会话过期机制,防止滥用和资源浪费 + +## 四、项目大纲 + +不同于网上Demo项目,这个项目是一步步,一个个章节的带着大家从0到1的方式,进行分析、设计和开发。是一个纯手把手教大家学习实战技术的项目! 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。 + +**第1部分:系统设计** +- 1:《AI MCP Gateway》第1-1节:网关需求分析 +- 5:《AI MCP Gateway》第1-2节:系统建模设计 +- 11:《AI MCP Gateway》第1-3节:网关协议表 +- 16:《AI MCP Gateway》第1-4节:升级网关库表 + +**第2部分:协议分析** +- 2:《AI MCP Gateway》第2-1节:MCP服务实现(用于后续协议分析) +- 3:《AI MCP Gateway》第2-2节:MCP代理调用 +- 4:《AI MCP Gateway》第2-3节:MCP通信协议(json-rpc2) - 调试 Spri... + +**第3部分:网关实现(完成到26节可以写简历啦)** + +- 6:《AI MCP Gateway》第3-1节:工程初始化创建 +- 7:《AI MCP Gateway》第3-2节:会话管理服务实现 +- 8:《AI MCP Gateway》第3-3节:会话接口编排 +- 9:《AI MCP Gateway》第3-4节:会话消息结构设计 +- 10:《AI MCP Gateway》第3-5节:消息协议处理案例 +- 12:《AI MCP Gateway》第3-6节:基础层数据处理(Dao) +- 13:《AI MCP Gateway》第3-7节:协议消息处理-Initialize +- 14:《AI MCP Gateway》第3-8节:协议消息处理-ToolsList +- 15:《AI MCP Gateway》第3-9节:协议消息处理-ToolsCall +- 17:《AI MCP Gateway》第3-10节:评审库表升级代码 +- 18:《AI MCP Gateway》第3-11节:会话内容编排处理 +- 19:《AI MCP Gateway》第3-12节:鉴权功能领域服务 +- 20:《AI MCP Gateway》第3-13节:鉴权功能编排处理 +- 21:《AI MCP Gateway》第3-14节:解析Swagger标准OpenAPI协议 +- 22:《AI MCP Gateway》第3-15节:协议域-协议解析处理 +- 23:《AI MCP Gateway》第3-16节:协议域-协议存储处理 +- 24:《AI MCP Gateway》第3-17节:网关配置域-配置数据存储(CRUD) +- 25:《AI MCP Gateway》第3-18节:管理端-API功能编排串联 +- 26:《AI MCP Gateway》第3-19节:管理端-API与UI对接 +- 27:《AI MCP Gateway》第3-20节:验证服务,LLM对接测试MCP接口 +- 27:《AI MCP Gateway》第3-21节:验证服务,LLM对接测试MCP页面 + +> 欢迎加入小傅哥👏🏻,一起学习企业真正学习的实战技术!让自己的面试,更具有竞争力。 diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2541-1\350\212\202\357\274\232\347\275\221\345\205\263\351\234\200\346\261\202\345\210\206\346\236\220.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2541-1\350\212\202\357\274\232\347\275\221\345\205\263\351\234\200\346\261\202\345\210\206\346\236\220.md" new file mode 100644 index 000000000..bba7f38a4 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2541-1\350\212\202\357\274\232\347\275\221\345\205\263\351\234\200\346\261\202\345\210\206\346\236\220.md" @@ -0,0 +1,64 @@ +--- +title: 【更】第1-1节:网关需求分析 +pay: https://t.zsxq.com/f6fiZ +--- + +# 《AI MCP Gateway 网关服务系统》第1-1节:网关需求分析 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/i8tV1](https://t.zsxq.com/i8tV1) + +>大家好,我是技术UP主小傅哥。 + +今天是我们 **《AI MCP Gateway 网关服务系统》** 项目学习的第1节课程,小傅哥会带着大家,以互联网公司正规的承接产品需求到开发部署上线的流程,带着大家以第一主人公视角的方式进行学习。这样既可以保证你学习到项目内容,也能了解到公司里正规的开发模式,以后进入到公司也可以很好的融入团队,承接项目需求。加油💪🏻!让我们开启新项目之旅! + +## 一、本章诉求 + +站在产品视角,分析 AI MCP Gateway 网关服务系统,这样一个产品功能需求的背景、诉求和目的。 + +需求是怎么来的呢? + +作为互联网公司里的研发,我们通常都是从业务产品经理那承接需求,评审 PRD 文档,之后研发进行详细设计,给出设计文档在评审,评审通过后开始进入编码开发,以及推进后续的测试和上线。 + +那么,还有一类需求是不需要业务产品经理的,往往是纯技术产品经理或者研发和技术一起驱动的,这类的需求包括,系统的重构、组件的设计、框架的升级等。他们虽然也是需求,但不直接影响到产品经理的业务需求,所以往往由技术产品或者研发推动。 + +所以,像是 AI MCP Gateway 网关服务系统,是附属于 AI 应用开发部门的项目,一般是由 AI 技术产品经理推动,这类产品经理,往往比纯业务产品经理要多一些技术储备的。虽然他们编码不熟练,但对技术是有不错的储备,可以很好的规划和推进技术产品。 + +## 二、项目背景 + +MCP 协议的定义和发布,改变了我们使用 AI 的形态。 + +LLM 大模型第一次以 chatgpt 问世时,就足以震撼到我们。它具备各行各业的知识储备,可以识别人类语言,可以完成逻辑关系推理。在很长一段时间里,我们都是把问题抛给大模型,大模型回答后,我们在根据它的回复,复制粘贴到具体的场景进行处理。 + +但大模型不能无中生有,不能根据你的提问,直接帮你完成最终的处理。如;帮我查询最近一天内数据库账户表写入的数据量,这是做不到的,因为大模型并不能直接连接到我们的数据库或者其他任何服务。 + +在 2023 年前,如果想让大模型和程序代码互动,我们经常做的方式是写提示词,告诉大模型我的程序具备什么样的能力,有什么样的接口格式,你需要分析我的问题,并按照最终的执行给出结构化参数。json 格式为 `{"function":"query_user_account","arguments":{"sc":"渠道值"}}` 之后再根据大模型的结果调用对应的程序方法。不过以上方式执行起来的误差较大,经常是需要慢慢微调提示词。 + +到了 2023年6月,OpenAI 发布 gpt-3.5-turbo-0613 模型,给 API 的调用提供了 Function Calling 的能力,只需要在请求 API 的时候传入 functions 参数,告知大模型本地有哪些函数方法可以被调用使用即可。到了 2023年11月,gpt-3.5-turbo-1106 发布,这回开始支持 tools 函数,我们可以在提问的时候,进行网络检索,天气对接。但是这些功能代码, 都被嵌入到大模型调用中,编写起来耦合在一块,维护起来很麻烦。 + +直至到2024年11月,Anthropic 发布了 MCP 协议,将 tools 的封装单独抽离到独立的服务,这种服务称之为 MCP 服务,然后通过远程协议的模式提供给大模型调用。而 MCP 协议的主要作用是将服务转换为可以被大模型识别的格式结构(后面章节会细分析 MCP 协议)。 + +**MCP 为何重要?** + +- MCP 在构建或与 AI 应用程序或代理集成时减少了开发时间和复杂性。 +- MCP 提供对数据源、工具和应用程序生态系统的访问,这将增强功能并改善最终用户体验。 +- MCP 可产生功能更强大的 AI 应用程序或代理,它们可以在必要时访问您的数据并代表您采取行动。 + +
+ +
+ +自从有了 MCP 协议以后,市面上开始出现各类的 AI Agent 智能体服务,如被大家熟知的 `Dify`、`Corz`、`Trae.ai/Cursor` 等。这些都是通用的智能体服务,可以解决市面上大部分通用场景问题。 + +到这以后,各个公司开启了自己的 AI Agent 智能体实现,对公司里自身业务场景进行提效。如账户服务、交易订单、计息计罚、还款计划等,做成智能客户和AI运营工具,都可以为企业提效。那么这里就有一个问题,这些接口想被大模型识别,就要开发为 MCP 协议服务,只有 MCP 协议才能被大模型识别的协议,公司里的各项服务要想被大模型调用,就要编写为一个个的 MCP Server 服务端,这个工作量是非常大的。 + +
+ +
+ +所以,为了解决需要把大量接口开发成 MCP 协议服务的工作,我们可以设计一个 MCP 网关服务系统。它的核心工作原理在于,设计实现一套 MCP 协议的统一服务入口,管理和使用动态化注册的 HTTP/RPC 服务接口。MCP 协议的接口会被接入 MCP 服务的大模型,进行调用,核心过程包括;`建立sse连接创建会话ID`、`初始化服务`、`获取 tools 工具列表`、`响应工具调用`。这些内容,小傅哥会在后续的协议分析中带着大家学习。 + +综上,当我们有了一套 MCP 网关以后,就可以把我们各项所需的接口,快速🔜转换为 MCP 协议,配置给 AI Agent 智能体系统进行使用啦。这也是我们本次课程的最终目的。 + + diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2541-2\350\212\202\357\274\232\347\263\273\347\273\237\345\273\272\346\250\241\350\256\276\350\256\241.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2541-2\350\212\202\357\274\232\347\263\273\347\273\237\345\273\272\346\250\241\350\256\276\350\256\241.md" new file mode 100644 index 000000000..2ad3c04f6 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2541-2\350\212\202\357\274\232\347\263\273\347\273\237\345\273\272\346\250\241\350\256\276\350\256\241.md" @@ -0,0 +1,24 @@ +--- +title: 【更】第1-2节:系统建模设计 +pay: https://t.zsxq.com/FKXfD +--- + +# 《AI MCP Gateway 网关服务系统》第1-2节:系统建模设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/74OwC](https://t.zsxq.com/74OwC) + +>大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +按照 AI MCP Gateway 网关功能实现诉求,对系统服务进行建模设计。包括;用例图、四色建模拆解、工程模型分析。 + +## 二、架构选型 + +此项目会选择 DDD 领域驱动设计的方式,进行系统建模和功能设计。那为啥选择 DDD 架构呢? + +因为 DDD 架构的四色建模方法可以更好的分析场景需求模型,同时它对应的六边形架构设计,非常合理的划分了微服务的各项单元功能。如;http、redis、mysql等都有自己的分层规划,同时又为领域服务与基础设施层的设计做了依赖倒置(这样的思想在Spring源码中很多),当我们在领域模块中实现服务时,就可以专心于各个模块的内聚服务了。 + +关于 DDD 可以在这部分补充学习;[https://bugstack.cn/md/road-map/ddd-guide-01.html](https://bugstack.cn/md/road-map/ddd-guide-01.html) - `有系列的5节课程` \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2541-3\350\212\202\357\274\232\347\275\221\345\205\263\345\215\217\350\256\256\350\241\250.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2541-3\350\212\202\357\274\232\347\275\221\345\205\263\345\215\217\350\256\256\350\241\250.md" new file mode 100644 index 000000000..1e517dffe --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2541-3\350\212\202\357\274\232\347\275\221\345\205\263\345\215\217\350\256\256\350\241\250.md" @@ -0,0 +1,25 @@ +--- +title: 【更】第1-3节:网关协议表 +pay: https://t.zsxq.com/YwZQE +--- + +# 《AI MCP Gateway 网关服务系统》第1-3节:网关协议表 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/pipVW](https://t.zsxq.com/pipVW) + +## 一、本章诉求 + +设计 AI MCP Gateway 网关服务,所需的核心必备的数据库表。以用于存储,mcp 到 http 的协议转换处理,如,用户通过 sse 请求指定的网关 ID 对应的服务,则可以调用到对应的 http 服务接口。 + +## 二、功能设计 + +如图,库表驱动下的业务; + +
+ +
+ +- 首先,以用户为入口,进行网关配置,他关心的是权限,以及配置的 http 怎么映射到 mcp 服务。 +- 之后,mcp 服务对应的 http 接口能力和出入参字段,都需要给出对应的描述,这样 ai 调用 mcp 服务,才能拿到工具列表说明以及进行调用。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2541-4\350\212\202\357\274\232\345\215\207\347\272\247\347\275\221\345\205\263\345\272\223\350\241\250.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2541-4\350\212\202\357\274\232\345\215\207\347\272\247\347\275\221\345\205\263\345\272\223\350\241\250.md" new file mode 100644 index 000000000..c2dfe56ee --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2541-4\350\212\202\357\274\232\345\215\207\347\272\247\347\275\221\345\205\263\345\272\223\350\241\250.md" @@ -0,0 +1,25 @@ +--- +title: 【更】第1-4节:升级网关库表 +pay: https://t.zsxq.com/eeZPc +--- + +# 《AI MCP Gateway 网关服务系统》第1-4节:升级网关库表 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/QCvqV](https://t.zsxq.com/QCvqV) + +## 一、本章诉求 + +增强网关库表设计,拆分出工具(tool)、工具协议类型(http),让网关配置可以支持一个网关下多个工具,工具可以绑定和切换到不同的协议上(1:n)。细节上会在 tool 上设计协议类型,以便于扩展支持不同的协议对接。 + +## 二、升级设计 + +如图,从旧版库表升级到新版库表的结构; + +
+ +
+ +- 旧版的设计中,是有一个 mcp_protocol_registry 协议注册,里面包含了工具描述和 http 接口协议信息。功能理解和编码实现上会比较直观,适合我们最开始让大家上手学习。 +- 新版的设计中,拆分了 tool 工具表,也就是一个网关(mcp_gateway)可以对应多个 tool 表,tool 表可以单独配置对应的协议信息,可以是 http,也可以是其他的。后续扩展的时候增加新的表即可。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2542-1\350\212\202\357\274\232MCP\346\234\215\345\212\241\345\256\236\347\216\260.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2542-1\350\212\202\357\274\232MCP\346\234\215\345\212\241\345\256\236\347\216\260.md" new file mode 100644 index 000000000..f96b7af4d --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2542-1\350\212\202\357\274\232MCP\346\234\215\345\212\241\345\256\236\347\216\260.md" @@ -0,0 +1,46 @@ +--- +title: 【更】第2-1节:MCP服务实现 +pay: https://t.zsxq.com/QdTsG +--- + +# 《AI MCP Gateway 网关服务系统》第2-1节:MCP服务实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/mUHg8](https://t.zsxq.com/mUHg8) + +>大家好,我是技术UP主小傅哥。 + +进入到第二部分开始,小傅哥会带着大家通过各种手段对 MCP 协议进行细致的实践验证的方式进行分析。这部分包括了实现一个 MCP 服务,完成 MCP 服务的对接,通过代理的方式调试对接中 MCP 接口协议的数据,以及通过开发网页测试工具对接协议等。最后在进行 json-rpc2 定义的协议标准讲解。通过这样一套内容串联,你会对 MCP 有非常强的理解,也能为后面做 MCP 网关实现的学习打下良好的基础。 + +## 一、本章诉求 + +基于 Spring AI 框架,实现一个简单的 MCP 服务,为后续做协议的分析和验证进行使用。 + +因为协议分析,主要包括了通信的格式结构,如通信的入参,所以实现这样的一个 MCP 服务,会多增加一些入参类型,便于以后做网关设计时使用。 + +通常来讲,这部分的操作,也可以理解为是技术调研验证阶段。当我们要实现一个大的功能服务时,就要先想办法把复杂的逻辑拆分为独立的细小单元。也就是软件第一设计原则康威定律提到的,问题越小,越容易被理解和处理。所以,当你想在此项目拓展功能,或则自己在公司承接需求的时候,也可以使用这样的方式进行辅助完成系统的详细设计。 + +## 二、协议说明(MCP) + +文档:[https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html) - 可以阅读官网文档,这里就包含了如何实现 MCP 服务,并提供了小案例。 + +```java + + org.springframework.ai + spring-ai-starter-mcp-server + +``` + +Spring AI MCP(模型上下文协议)服务启动器为在 Spring Boot 应用程序中设置 MCP 服务器提供了自动配置功能。它实现了 MCP 服务器功能与 Spring Boot 自动配置系统的无缝集成。 + +MCP 服务器启动器提供: +- MCP 服务器组件的自动配置 +- 支持同步和异步操作模式 +- 多种传输层选项 +- 灵活的工具、资源和提示规范 +- 更改通知功能 + +Spring AI MCP 框架是对 MCP 协议的实现,可以把我们实现的服务功能,以 MCP 格式进行转换处理。与我们要实现的 AI MCP Gateway 不同的是,Spring AI MCP 固定的框架,每一个 MCP 都要独立完成开发,而 AI MCP Gateway 是一个通用协议转换的服务,只需要配置就可以完成从接口(http/rpc)到 MCP 协议的转换。 + +但为了更好的理解 MCP 协议,我们可以先基于 Spring AI MCP 框架,来实现一个简单的 MCP 并陆续完成对接使用,再到协议分析和设计 AI MCP Gateway 网关服务。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2542-2\350\212\202\357\274\232MCP\344\273\243\347\220\206\350\260\203\347\224\250.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2542-2\350\212\202\357\274\232MCP\344\273\243\347\220\206\350\260\203\347\224\250.md" new file mode 100644 index 000000000..697f8a3ec --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2542-2\350\212\202\357\274\232MCP\344\273\243\347\220\206\350\260\203\347\224\250.md" @@ -0,0 +1,25 @@ +--- +title: 【更】第2-2节:MCP代理调用 +pay: https://t.zsxq.com/OkMLy +--- + +# 《AI MCP Gateway 网关服务系统》第2-2节:MCP代理调用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/UAbZd](https://t.zsxq.com/UAbZd) + +>大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +实现一个 MCP 客户端,用于对接 MCP 服务端,并通过代理 AI 接口的方式完成调用。该方案旨在调试 AI 调用 MCP 过程中的通信的请求接口协议,便于查看和分析相关数据。 + +## 二、流程设计 + +
+ +
+ +- 如图,是整个 Ai Client 以 Tools 工具,对接 MCP 的流程结构图。也是 Ai Agent 智能体最基础配置。如果感兴趣 [Ai Agent 项目](https://t.zsxq.com/GwNZp),也可以在星球里学习。 +- 之后,我们要在整个实现过程中,为 Ai 接口,通过 SpringBoot HTTP 方式做一层代理。这样在调用 MCP 的过程中,我们就可以清楚的知道这个过程的协议数据结构了。`代理的方式可以用在很多场景,还有一种是浏览器代理的主动安全扫描技术,甚至你服务器的应用暴漏了数据库密码都可以被扫描出来。` [扩展知识:安全漏洞扫描,他怎么拿到了我的数据库密码?](https://bugstack.cn/md/road-map/13scan-jdumpspider.html) diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2542-3\350\212\202\357\274\232MCP\351\200\232\344\277\241\345\215\217\350\256\256.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2542-3\350\212\202\357\274\232MCP\351\200\232\344\277\241\345\215\217\350\256\256.md" new file mode 100644 index 000000000..ff83dcb39 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2542-3\350\212\202\357\274\232MCP\351\200\232\344\277\241\345\215\217\350\256\256.md" @@ -0,0 +1,39 @@ +--- +title: 【更】第2-3节:MCP通信协议(json-rpc2) +pay: https://t.zsxq.com/9PlXx +--- + +# 《AI MCP Gateway 网关服务系统》第2-3节:MCP通信协议(json-rpc2) - debug 调试 Spring AI 源码 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/wwiTt](https://t.zsxq.com/wwiTt) + +>大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +学习了解 JSON-RPC 2.0 消息协议定义,并通过工程实践调试(debug)验证的方式,分析 MCP 通信协议过程。 + +## 二、通信协议 + +MCP 等同于为 AI 安装上了手和脚,使其这个 AI 大脑具备了行为能力的执行。所以,25年以来,AI Agent 智能体才得以落地。 + +MCP 定义了一种标准化的通信协议,使客户端和服务器能够以一致且可预测的方式交换消息。这种标准化的定义对整个AI和服务的交互性至关重要。 + +而通信就要有数据的交互格式,MCP 采用的是 JSON-RPC 2.0 协议,作为客户端和服务端之间素有的通信消息格式。JSON-RPC 是一种轻量级的远程过程调用协议,采用 JSON 编码,易于阅读和调试、与编程语言无关,支持在任何编程环境中实现(Java、Python、Go、JS...),且成熟完善,规范明确,适合广泛使用。 + +
+ +
+ +> 在本节代码工程下,docs/pdf -> JSON-RPC 2.0 Specification.pdf 详细介绍了 json-rpc 2.0 通信协议,可以查阅。也可以阅读它的官网,但打开会卡一些。[https://www.jsonrpc.org/specification](https://www.jsonrpc.org/specification) + +### 1. 调用协议 + +
+ +
+ +- 首先,很多通信协议,也包括业务工程的流程处理,往往第一步是建立一个验证关系,拿到整个后续链路请求的会话ID,之后以会话ID作为全流程的串联关系进行通信。这和图中的 MCP 协议调用过程是一样的。 +- 之后,MCP 客户端和服务端的交互,分为4个步骤;初始化(连接)、发现工具列表(能力)、执行工具调用、断开连接。也就是说 AI 要拿到你配置的工具的 MCP 的能力,这样才能根据你的请求决定调用哪个 MCP 服务,以及处理 MCP 服务返回的结果。 diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-10\350\212\202\357\274\232\350\257\204\345\256\241\345\272\223\350\241\250\345\215\207\347\272\247\344\273\243\347\240\201.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-10\350\212\202\357\274\232\350\257\204\345\256\241\345\272\223\350\241\250\345\215\207\347\272\247\344\273\243\347\240\201.md" new file mode 100644 index 000000000..d92c1ebf5 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-10\350\212\202\357\274\232\350\257\204\345\256\241\345\272\223\350\241\250\345\215\207\347\272\247\344\273\243\347\240\201.md" @@ -0,0 +1,33 @@ +--- +title: 【更】第3-10节:评审库表升级代码 +pay: https://t.zsxq.com/FxVTE +--- + +# 《AI MCP Gateway 网关服务系统》第3-10节:评审库表升级代码 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/PzIhJ](https://t.zsxq.com/PzIhJ) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +针对升级的库表结构,调整工程代码`基础设施层`(dao、po、mapper),重新设计`领域层值对象`,附带调整InitializeHandler、ToolsListHandler、ToolsCallHandler的数据使用。 + +本节是一个很好的练习篇,原有的功能、流程、结构都不变,只是把库表升级,之后针对这些数据的时候重新定义对象。所以,这一节我们采用互联网公司中的代码评审方式来讲解变更信息,你可以在这个过程中,对比代码变化,来编写你的代码。也可以在学习文档和视频后,自己来编写。可能在这个过程中会遇到错误,但这些错误会驱动你深入的debug调试,快速的积累核心知识。这节学习透彻了以后,后面的章节将会非常好学习。 + +git 教程:[https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) + +## 二、流程设计 + +如图,库表升级对于领域功能的改造; + +
+ +
+ +- InitializeHandler 旧版是通过网关配置和工具两部分拿到基础信息,新版直接从网关配置拿到即可。 +- ToolsListHandler 旧版从 McpGatewayToolConfigVO 定义的工具和映射,拿到 list 数据,之后做的拆分。新版定义了 McpToolConfigVO - 工具部分、McpToolProtocolConfigVO - 协议部分,有工具引入协议信息。 +- ToolsCallHandler 这部分增强了查询,通过 gatewayId 网关ID、toolName 工具名称,来获取到当前当前要调用的协议信息。这里查询的是 http 协议。如果对接了更多的协议,这部分要做策略处理。 + diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-11\350\212\202\357\274\232\344\274\232\350\257\235\345\206\205\345\256\271\347\274\226\346\216\222\345\244\204\347\220\206.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-11\350\212\202\357\274\232\344\274\232\350\257\235\345\206\205\345\256\271\347\274\226\346\216\222\345\244\204\347\220\206.md" new file mode 100644 index 000000000..31e04239c --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-11\350\212\202\357\274\232\344\274\232\350\257\235\345\206\205\345\256\271\347\274\226\346\216\222\345\244\204\347\220\206.md" @@ -0,0 +1,33 @@ +--- +title: 【更】第3-11节:会话内容编排处理 +pay: https://t.zsxq.com/7Oceg +--- + +# 《AI MCP Gateway 网关服务系统》第3-11节:会话内容编排处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/pxAAd](https://t.zsxq.com/pxAAd) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +将目前在 MCP 网关服务接口管理(McpGatewayController)中的 handleMessage 下的逻辑代码,抽取到 case 进行编排处理,减轻 Controller 层的代码压力。 + +
+ +
+ +这里有一个设计思想,Controller 接口实现的控制器层,在处理复杂逻辑的时候,都会调用很多 Service 服务。无论这个服务是贫血模型的 mvc 架构,还是充血模型的 ddd 架构。那么为了减轻 Controller 的职责,不至于让一个 Controller 的代码逻辑过于繁重,因此引入了 case 编排层。上承接 Controller 层的出入参需求,下处理 domian 领域服务的编排处理。这一层甚至不需要额外的对象包,它可以承接 Controller 的 DTO 对象作为出入参,也可以使用领域层的对象。 + +## 二、流程设计 + +如图,会话消息处理流程设计; + +
+ +
+ +- 首先,这部分的重点在于将原本的会话服务接口下的消息处理,直接调用 domain 领域层的部分,重构迁移到 case 层通过规则树的方式分摊 trigger 触发器下的 Controller 的压力。 +- 之后,这部分的 case 编排和会话 Session 处理的架构设计方案是一致的,使用的是星球「码农会锁」扳手工程下的通用设计模式组件。这部分的设计,只要具备编码式的规则树结构,可以划分职责的方式完成节点的拆分,就都可以作为编排设计工具使用。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-12\350\212\202\357\274\232\351\211\264\346\235\203\345\212\237\350\203\275\351\242\206\345\237\237\346\234\215\345\212\241.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-12\350\212\202\357\274\232\351\211\264\346\235\203\345\212\237\350\203\275\351\242\206\345\237\237\346\234\215\345\212\241.md" new file mode 100644 index 000000000..732cc055d --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-12\350\212\202\357\274\232\351\211\264\346\235\203\345\212\237\350\203\275\351\242\206\345\237\237\346\234\215\345\212\241.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第3-12节:鉴权功能领域服务 +pay: https://t.zsxq.com/gLU35 +--- + +# 《AI MCP Gateway 网关服务系统》第3-12节:鉴权功能领域服务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/I9dRQ](https://t.zsxq.com/I9dRQ) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +设计 MCP 网关通信过程中的鉴权领域功能,包括;权限注册、请求限流、权限校验,这样3个主要的服务能力。领域层设计好后,就可以让 case 串联逻辑完成权限功能的使用了。 + +## 二、流程设计 + +如图,关于鉴权功能的领域处理; + +
+ +
+ +- 首先,设计对鉴权领域的功能,校验阶段,判断当前用户传递 api_key 是否为配置的有效key,是否开启认证,是否在有效期。 +- 之后,是注册 api_key 的处理,以及 api_key 的使用限流。数据库表中设计了,速率限制(次/小时) 可以按需设计你的。`这部分值在使用中会转换为多少秒一次,不过程序调用过程中,一般会达到毫秒。所以这个值可以适当放大` \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-13\350\212\202\357\274\232\351\211\264\346\235\203\345\212\237\350\203\275\347\274\226\346\216\222\345\244\204\347\220\206.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-13\350\212\202\357\274\232\351\211\264\346\235\203\345\212\237\350\203\275\347\274\226\346\216\222\345\244\204\347\220\206.md" new file mode 100644 index 000000000..12e169216 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-13\350\212\202\357\274\232\351\211\264\346\235\203\345\212\237\350\203\275\347\274\226\346\216\222\345\244\204\347\220\206.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第3-13节:鉴权功能编排处理 +pay: https://t.zsxq.com/AycUA +--- + +# 《AI MCP Gateway 网关服务系统》第3-13节:鉴权功能编排处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/wO9Cw](https://t.zsxq.com/wO9Cw) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在 case 的 mcp 模块下,将 auth 鉴权功能,串联到 `session 会话创建`、`message 消息处理中`去。让会话创建的时候可以校验 apiKey 的可用性,以及在消息处理中使用限流控制请求频次。 + +## 二、流程设计 + +如图,将鉴权功能串联到case编排中; + +
+ +
+ +- 首先,是 api 入参这部分,都需要添加一个 `apiKey` 作为后缀的请求入参。会话请求阶段,是用户配置的请求连接传递进来的,而后续的消息处理部分,是我们在会话阶段把 apiKey 拼接到请求地址里去的。 +- 之后,消息处理阶段,根据请求的 apiKey 做响应的限流处理。`基本你在各个官网申请的 mcp 服务,如百度搜索,都会让你创建一个 apiKey 针对你的这个 key 做一些列的流程处理。包括还会对你请求的数据做一个日志记录。` \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-14\350\212\202\357\274\232\350\247\243\346\236\220Swagger\346\240\207\345\207\206OpenAPI\345\215\217\350\256\256.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-14\350\212\202\357\274\232\350\247\243\346\236\220Swagger\346\240\207\345\207\206OpenAPI\345\215\217\350\256\256.md" new file mode 100644 index 000000000..d168eb169 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-14\350\212\202\357\274\232\350\247\243\346\236\220Swagger\346\240\207\345\207\206OpenAPI\345\215\217\350\256\256.md" @@ -0,0 +1,34 @@ +--- +title: 【更】第3-14节:解析Swagger标准OpenAPI协议 +pay: https://t.zsxq.com/wvb2k +--- + +# 《AI MCP Gateway 网关服务系统》第3-14节:解析Swagger标准OpenAPI协议 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/hTWme](https://t.zsxq.com/hTWme) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在前面章节,我们实现了关于 AI MCP 网关,解决 HTTP 协议向 MCP 协议的转换处理。相关的协议(HTTP)数据存储在数据库表中。那么现在的另外一个问题就是,数据库表里的协议映射数据应该怎么录入进去。 + +这个录入方案也比较多,比如,提供一个页面,让用户自己手动输入相关的协议映射信息。也可以提供一个 SDK 组件,让 HTTP 接口服务端引入,之后自动上报。之后这里还有一种方案,是通过 Swagger 导出标准 OpenAPI 协议接口 json 文件,以文件数据的方式,录入到数据库表中。 + +本节,我们先来处理关于 Swagger 的使用到提供一个工具包把 OpenAPI 的 JSON 转换为我们的设计的库表对应对象的关系。`本节操作转换的工具类包,不非得手动编码,能理解和使用即可`。 + +## 二、工具介绍(Swagger) + +官网:[https://swagger.io/](https://swagger.io/) + +Swagger 是一个开源的API 设计和文档工具,它可以帮助开发人员更快、更简单地设计、构建、文档化和测试 RESTful API。以及可以导出 [OpenAPI](https://openapi.apifox.cn/) 标准的协议接口 JSON 文件。 + +它的接入方式也非常简单,只要在项目工程中引入 Swagger 相关的 POM 文件,在工程启动后就可以访问 Swagger 页面,查看 HTTP 接口服务。如果希望接口描述信息更为准确,也可以在接口和出入参对象(属性)上添加上相关的注解描述。这个描述信息对我们 MCP 协议更为重要。 + +
+ +
+ +如图,我们需要的就是这份标准的 OpenAPI 接口的 JSON 文件,使用它解析并转换为目前 ai mcp gateway 库表结构中设计的 http 协议和字段映射关系。你可以把这部分的解析,当做一个工具包使用。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-15\350\212\202\357\274\232\345\215\217\350\256\256\345\237\237-\345\215\217\350\256\256\350\247\243\346\236\220\345\244\204\347\220\206.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-15\350\212\202\357\274\232\345\215\217\350\256\256\345\237\237-\345\215\217\350\256\256\350\247\243\346\236\220\345\244\204\347\220\206.md" new file mode 100644 index 000000000..eeb0f4957 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-15\350\212\202\357\274\232\345\215\217\350\256\256\345\237\237-\345\215\217\350\256\256\350\247\243\346\236\220\345\244\204\347\220\206.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-15节:协议域-协议解析处理 +pay: https://t.zsxq.com/dEsG8 +--- + +# 《AI MCP Gateway 网关服务系统》第3-15节:协议域-协议解析处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/wGt6C](https://t.zsxq.com/wGt6C) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +设计**协议域**,定义分层结构,用于承接协议的解析、存储、使用等场景功能。`在整个 DDD 架构下,我们会不断的思考这些内容的功能域设计,而不是一些单一的原则方法。` + +这一节我们把前面做的解析协议的案例代码,按照协议域的分层结构,拆分编写对应的功能逻辑。 + +## 二、流程设计 + +如图,设计协议域,封装解析逻辑(其他流程后续处理); + +
+ +
+ +- 首先,划分出【协议域】,增加协议解析、协议存储,对应的单一职责的接口定义。 +- 之后,把上一节的协议解析的案例代码,按照如图所示的结构,拆分设计。这里解析还增加了 rpc 包,是一个示意,如果后续大家做其他场景的接口对应的协议解析,可以都在这个结构下增加策略实现。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-16\350\212\202\357\274\232\345\215\217\350\256\256\345\237\237-\345\215\217\350\256\256\345\255\230\345\202\250\345\244\204\347\220\206.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-16\350\212\202\357\274\232\345\215\217\350\256\256\345\237\237-\345\215\217\350\256\256\345\255\230\345\202\250\345\244\204\347\220\206.md" new file mode 100644 index 000000000..3d4b804c2 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-16\350\212\202\357\274\232\345\215\217\350\256\256\345\237\237-\345\215\217\350\256\256\345\255\230\345\202\250\345\244\204\347\220\206.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第3-16节:协议域-协议存储处理 +pay: https://t.zsxq.com/3kNZE +--- + +# 《AI MCP Gateway 网关服务系统》第3-16节:协议域-协议存储处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/wGt6C](https://t.zsxq.com/wGt6C) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在**协议域**添加协议存储服务,以及验证存储后的数据,在 AI MCP 网关,整个流程中完成调用处理。其实这部分的内容,就是在串联流程。协议解析、协议存储,存储后的数据到数据库表中的结构,是否还可以按照约定的方式转换为可以被 MCP 协议识别的结构。之后再继续完成调用的处理。 + +## 二、流程设计 + +如图,从协议解析到协议存储流程设计; + +
+ +
+ +- 首先,本节的重点是要验证,协议解析 -> 协议存储 -> 协议使用,是否可以完整整个链路调用处理。 +- 所以,在这一节完成协议存储后,我们就要做一个完整的案例,来验证整个数据解析存储和使用是否可以通过。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-17\350\212\202\357\274\232\347\275\221\345\205\263\345\237\237-\351\205\215\347\275\256\346\225\260\346\215\256\345\255\230\345\202\250(CRUD).md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-17\350\212\202\357\274\232\347\275\221\345\205\263\345\237\237-\351\205\215\347\275\256\346\225\260\346\215\256\345\255\230\345\202\250(CRUD).md" new file mode 100644 index 000000000..5c81f45ba --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-17\350\212\202\357\274\232\347\275\221\345\205\263\345\237\237-\351\205\215\347\275\256\346\225\260\346\215\256\345\255\230\345\202\250(CRUD).md" @@ -0,0 +1,28 @@ +--- +title: 【更】第3-17节:网关配置域-配置数据存储(CRUD) +pay: https://t.zsxq.com/BGsjf +--- + +# 《AI MCP Gateway 网关服务系统》第3-17节:网关配置域-配置数据存储(CRUD) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/wGt6C](https://t.zsxq.com/wGt6C) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +本节我们要新增加一个网关配置域,对网关、工具提供基本的配置操作,也就是 CRUD 的代码从领域层操作数据库的过程。这部分的内容实现,为的就是在后续提供的 AI MCP 网关运营服务上,可以在后台配置出整个网关服务。 + +## 二、流程设计 + +如图,网关配置域功能流程设计; + +
+ +
+ +- 首先,我们知道的,一整套的 AI MCP 网关配置,包括了,协议的解析存储和使用,之后是鉴权,但最开始是有一个网关的配置的,也就是写入基本的网关配置信息。 +- 那么,为了能完整的串联全部流程,我们这里需要增加下网关配置域,处理网关配置、网关工具配置的内容,一个网关可以挂多个工具,一个工具映射了一个协议(http、rpc等) +- 注意,当前我们先实现了最基本的保存和更新,后续随着运营后台的开发,在陆续补充需要的接口。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-18\350\212\202\357\274\232\347\256\241\347\220\206\347\253\257-API\345\212\237\350\203\275\347\274\226\346\216\222\344\270\262\350\201\224.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-18\350\212\202\357\274\232\347\256\241\347\220\206\347\253\257-API\345\212\237\350\203\275\347\274\226\346\216\222\344\270\262\350\201\224.md" new file mode 100644 index 000000000..fa82c77e4 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-18\350\212\202\357\274\232\347\256\241\347\220\206\347\253\257-API\345\212\237\350\203\275\347\274\226\346\216\222\344\270\262\350\201\224.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-18节:管理端-API功能编排串联 +pay: https://t.zsxq.com/qr9CQ +--- + +# 《AI MCP Gateway 网关服务系统》第3-18节:管理端-API功能编排串联 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/MyLpx](https://t.zsxq.com/MyLpx) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +到本章节往下,我们要为 AI MCP Gateway(网关)提供一个管理端页面,维护网关的配置和验证。如配置网关和工具能力,为工具添加协议。协议来自于 Swagger 导出的 openapi json 文件。这些内容,我们陆续的添加。本节主要串联服务功能接口(看看这样的架构下怎么提供的页面功能),以及提供基础UI页面。 + +注意,关于网关后台的管理页面是使用 AI IDE 工具生成的。建议每个人都生成一套自己的,你只要在对话框中,把服务端的 api 接口拖到对话中,并告诉它在什么位置编写 html 代码,以及对接服务端页面。它就可以帮你完成一套页面。 + +## 二、流程设计 + +如图,从管理端页面到服务端的流程设计; + +
+ +
+ +- 首先,整个流程为;从前端页面(管理后台)通过 ajax 请求服务端 trigger 层的 http 接口开始,由 case 层分摊 trigger 层串联逻辑部分的流程。这部分流程,调用 domain 领域层处理,也就是完成 CRUD 操作。最终由基础设施层,依赖倒置于 domain 领域层,完成数据的增删改查操作。 +- 之后,本节我们的重点是完成从 trigger 往下的逻辑处理,对于页面端,先少量提供。后续陆续补充各个功能。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-19\350\212\202\357\274\232\347\256\241\347\220\206\347\253\257-API\344\270\216UI\345\257\271\346\216\245.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-19\350\212\202\357\274\232\347\256\241\347\220\206\347\253\257-API\344\270\216UI\345\257\271\346\216\245.md" new file mode 100644 index 000000000..3610e5ced --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-19\350\212\202\357\274\232\347\256\241\347\220\206\347\253\257-API\344\270\216UI\345\257\271\346\216\245.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第3-19节:管理端-API与UI对接 +pay: https://t.zsxq.com/199zh +--- + +# 《AI MCP Gateway 网关服务系统》第3-19节:管理端-API与UI对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/JoVrf](https://t.zsxq.com/JoVrf) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +结合网关管理后台(UI)功能,在服务端提供相应的接口能力,与管理后台对接。这些功能主要是 CRUD 做页面管理的增删改查操作。 + +此部分功能使用 AI IDE 工具就可以完成,也很建议学习的伙伴使用 AI IDE 工具(opencode、trae.ai、openclaw(QClaw)),做一套属于自己的管理后台,这样你可以在投递简历的时候发送,也可以在面试的时候演示。 + +>提示,可以在你的 AI IDE 安装技能 [https://github.com/fuzhengwei/xfg-ddd-skills](https://github.com/fuzhengwei/xfg-ddd-skills) 这样处理起来更加准确。 + +## 二、流程设计 + +如图,管理端到服务端接口的设计; + +
+ +
+ +- 整个这部分的设计实现都是 CRUD,管理端需要什么,就可以配置什么接口,查询、修改、删除即可。 + diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-1\350\212\202\357\274\232\345\267\245\347\250\213\345\210\235\345\247\213\345\214\226\345\210\233\345\273\272.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-1\350\212\202\357\274\232\345\267\245\347\250\213\345\210\235\345\247\213\345\214\226\345\210\233\345\273\272.md" new file mode 100644 index 000000000..e6bb94615 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-1\350\212\202\357\274\232\345\267\245\347\250\213\345\210\235\345\247\213\345\214\226\345\210\233\345\273\272.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-1节:工程初始化创建 +pay: https://t.zsxq.com/xdnB0 +--- + +# 《AI MCP Gateway 网关服务系统》第3-1节:工程初始化创建 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/5PzN8](https://t.zsxq.com/5PzN8) + +>大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +搭建 AI MCP Gateway 网关工程,并 push 代码到课程仓库,以及演示关于 Git 使用,方便后续学习使用。 + +## 二、环境配置 + +- JDK 17 +- Maven 3.8.x - [Maven 教程](https://bugstack.cn/md/road-map/maven.html) +- IntelliJ IDEA 社区版(免费) [IntelliJ IDEA 教程](https://bugstack.cn/md/road-map/intellij-idea.html) +- Git - 安装后会配置到 IntellJ IDEA 这样才能向服务端推送或者拉取代码。学习后可以知道怎么拉取、提交和比对代码。Git 教程:https://bugstack.cn/md/road-map/git.html + +
+ +
+ +>相关软件,在星球课程入口,编程环境中提供了下载链接。编程环境:[https://t.zsxq.com/19Rnk98M0](https://t.zsxq.com/19Rnk98M0) - 提供了已经配置好镜像的 maven 方便直接使用。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-20\350\212\202\357\274\232\351\252\214\350\257\201\346\234\215\345\212\241\357\274\214LLM\345\257\271\346\216\245\346\265\213\350\257\225MCP\346\216\245\345\217\243.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-20\350\212\202\357\274\232\351\252\214\350\257\201\346\234\215\345\212\241\357\274\214LLM\345\257\271\346\216\245\346\265\213\350\257\225MCP\346\216\245\345\217\243.md" new file mode 100644 index 000000000..a2d3a5f76 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-20\350\212\202\357\274\232\351\252\214\350\257\201\346\234\215\345\212\241\357\274\214LLM\345\257\271\346\216\245\346\265\213\350\257\225MCP\346\216\245\345\217\243.md" @@ -0,0 +1,32 @@ +--- +title: 【更】第3-20节:验证服务,LLM对接测试MCP接口 +pay: https://t.zsxq.com/nKjea +--- + +# 《AI MCP Gateway 网关服务系统》第3-20节:验证服务,LLM对接测试MCP接口 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/VV1Bt](https://t.zsxq.com/VV1Bt) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +为了在配置完 AI MCP 网关以后,方便的验证网关服务SSE接口是否正常,我们需要在服务端增加一个简单的 LLM 大模型能力,通过 AI MCP 网关,来调用 MCP 服务。这样就可以非常方便的验证网关能力了。 + +本节我们先来做一下 LLM 的服务接口,把这部分能力在领域层设计实现出来,并包装一个接口服务。后续在结合这个接口服务,在管理后台配套的做一个页面出来。专门验证 MCP 服务能力。 + +>温馨提示,学习学到这了,就要多思考一些扩展。不只是完成课程的从0到1,也要思考从1-100还能做什么。 + +## 二、功能设计 + +如图,LLM 对接 MCP 服务,提供接口能力设计图; + +
+ +
+ +- 首先,整个操作的核心就是 LLM 服务,这部分内容,咱们在 ai-mcp-gateway-demo-mcp-server-test 工程里,有做过测试验证。现在相当于要把这部分能力迁移过来,放到 domain 领域层,实现 LLM 服务。 +- 之后,这个 LLM 服务的目的,就是可以动态化的构建含有 MCP 服务的对话模型。MCP 的加载,来自于接口层传入的 gatewayId 网关 ID 做动态的加载处理。 +- 注意,因为网关的配置也是不断的动态调整的,所以流程设计上会有一个是否重新加载的操作。重新加载的目的就是把 MCP 动态的刷新配置。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-2\350\212\202\357\274\232\344\274\232\350\257\235\347\256\241\347\220\206\346\234\215\345\212\241\345\256\236\347\216\260.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-2\350\212\202\357\274\232\344\274\232\350\257\235\347\256\241\347\220\206\346\234\215\345\212\241\345\256\236\347\216\260.md" new file mode 100644 index 000000000..ea2d8c14a --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-2\350\212\202\357\274\232\344\274\232\350\257\235\347\256\241\347\220\206\346\234\215\345\212\241\345\256\236\347\216\260.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-2节:会话管理服务实现 +pay: https://t.zsxq.com/c0WoQ +--- + +# 《AI MCP Gateway 网关服务系统》第3-2节:会话管理服务实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/nPibY](https://t.zsxq.com/nPibY) + +>大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +从本章节开始,我们会以**第1-2节:系统建模设计**为标准,进行各个模块的逻辑编码实现。包括;协议、鉴权、会话等。这部分内容完成后,在定义 api 接口,以及在 trigger 的 http 下实现接口,以及调用 case 层,处理 domain 领域的编排。整个实现过程是逐步有`骨架`,之后陆续填充完善的过程,你只要跟住理解清楚了,就可以很容易的完成全部编码。 + +**温馨提示**:尽量不要完全对照视频写代码,可以是先看视频,之后对照工程中分支代码变化(前面一节教程有告诉大家怎么对比),之后来实现功能。这样你会注意到更多的细节。 + +## 二、功能设计 + +如图,会话领域服务功能; + +
+ +
+ +- 首先,我们是把一次连接请求作为一次会话来看。比如数据库查询会话、MyBatis 操作会话、信贷交易会话,会话的作用在于以统一的标识记录用户的操作行为,根据你的会话ID,可以找到你再次过程中所有的行为记录。 +- 那么,在 AI MCP Gateway 中,也是按照 MCP 的协议方式,进行会话请求和消息处理。会话的作用是拿到后续处理消息的请求地址,这个地址是在会话请求阶段进行分配的。`在前面章节进行协议分析的时候,有讲解过 /mcp/message?sessionId= 的获取和使用` \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-3\350\212\202\357\274\232\344\274\232\350\257\235\346\216\245\345\217\243\347\274\226\346\216\222.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-3\350\212\202\357\274\232\344\274\232\350\257\235\346\216\245\345\217\243\347\274\226\346\216\222.md" new file mode 100644 index 000000000..74030075b --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-3\350\212\202\357\274\232\344\274\232\350\257\235\346\216\245\345\217\243\347\274\226\346\216\222.md" @@ -0,0 +1,36 @@ +--- +title: 【更】第3-3节:会话接口编排 +pay: https://t.zsxq.com/Mmuqa +--- + +# 《AI MCP Gateway 网关服务系统》第3-3节:会话接口编排 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/c0ZDk](https://t.zsxq.com/c0ZDk) + +>大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +定义 mcp 通信协议会话服务接口,并在 case 层做领域服务的编排,为接口提供服务能力。 + +## 二、功能设计 + +如图,会话服务编排接口; + +
+ +
+ +- 首先,这一节我们的主要定义并实现 mcp 服务,sse 请求接口,创建会话信息。地址案例:`http://localhost:8777/api-gateway/test10001/mcp/sse?api_key=xxx` + + - test10001 - 是每个MCP网关,申请获得的唯一标识ID + - mcp/sse - 是固定的接口格式,让使用方知道这是 mcp 服务,sse 通信协议。 + - api_key - 是一个服务网路请求时候的安全校验,确保是你申请了唯一的key,才可以访问的。这部分鉴权的操作,会放到后续实现。 + +- 之后,这里我们会看到 trigger 层是实现 MCP 服务接口,接口的实现,需要调用 domain 领域层(domain 是每一个内聚的服务方法)。而为了减轻 trigger 层的编码压力,这里我们引入下 case 层,来处理 trigger 层调用 domain 领域层所做的编排动作。让 trigger 层只负责一个接口的封装操作,如;日志打印、参数校验、对象转换、异常处理、结果封装,而具体的逻辑则有 case 层承接。 + +- 注意,case 层的编排操作,会采用星球中的扳手工程项目下的设计模式框架中的规则树,这个规则树非常适合节点的编排,对于流程的解耦非常有效。 + +> 在互联网大厂中,会有很多这样的解耦设计,架构也不是一成不变的。这些东西都会随着各种业务的迭代,不断的进行演进。所以,做为编码工程师,未来的架构师,都要大量的吸收这些编码设计经验。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-4\350\212\202\357\274\232\344\274\232\350\257\235\346\266\210\346\201\257\347\273\223\346\236\204\350\256\276\350\256\241.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-4\350\212\202\357\274\232\344\274\232\350\257\235\346\266\210\346\201\257\347\273\223\346\236\204\350\256\276\350\256\241.md" new file mode 100644 index 000000000..1dea6c710 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-4\350\212\202\357\274\232\344\274\232\350\257\235\346\266\210\346\201\257\347\273\223\346\236\204\350\256\276\350\256\241.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-4节:会话消息结构设计 +pay: https://t.zsxq.com/02FXE +--- + +# 《AI MCP Gateway 网关服务系统》第3-4节:会话消息结构设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/t0xJC](https://t.zsxq.com/t0xJC) + +>大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +增加 MCP 会话通信,处理请求消息的 HTTP 服务入口方法,完成简单的消息请求接收验证。并根据消息信息,设计会话领域层中消息处理策略。 + +## 二、功能设计 + +如图,会话消息响应设计; + +
+ +
+ +- 首先,这里要设计一个同名接口的不同类型服务,get 用于创建会话服务,建立 sse 连接。而 post 则是处理端点消息,完成会话服务应答。 +- 之后,对于会话消息,我们在前面已经分析过,主要包括;InitializeHandler - 协议握手、ResourcesListHandler - 返回可用资源列表、ToolsCallHandler - 执行指定的工具调用、ToolsListHandler - 返回服务器支持的工具列表。本节我们先把这些策略结构设计出来,方便后续实现具体功能。 +- 重点,本节会先来实现接口和定义整个处理消息的结构,后续再做具体的功能实现,以及服务的编排动作。 +- 此外,本节还会引入 jdk16+ 定义的新语法关键字,**record**、**sealed**、**permits** 来定义对象。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-5\350\212\202\357\274\232\346\266\210\346\201\257\345\215\217\350\256\256\345\244\204\347\220\206\346\241\210\344\276\213.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-5\350\212\202\357\274\232\346\266\210\346\201\257\345\215\217\350\256\256\345\244\204\347\220\206\346\241\210\344\276\213.md" new file mode 100644 index 000000000..4540b8fdc --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-5\350\212\202\357\274\232\346\266\210\346\201\257\345\215\217\350\256\256\345\244\204\347\220\206\346\241\210\344\276\213.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第3-5节:消息协议处理案例 +pay: https://t.zsxq.com/zUvnY +--- + +# 《AI MCP Gateway 网关服务系统》第3-5节:消息协议处理案例 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/kWYZ9](https://t.zsxq.com/kWYZ9) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +整个 MCP 协议的通信调用过程,需要先创建 session 会话,之后通过 handler 接收下发的指令方法,完成整个 MCP 的处理。那么,为了让大家更清楚我们所开发的东西,这里我们要先做一些流程案例,完成整个 MCP 服务的通信过程。后续在把案例的固定的代码,拆分到数据库配置实现。 + +## 二、功能设计 + +如图,会话消息响应设计; + +
+ +
+ +- 首先,MCP 的通信过程,分为了初始化,后去工具列表,调用工具,以及资源和通知等。这里我们要硬编码返参实现这些方法。 +- 之后,我们这里做一个单词小写转换大写的方法,让 AI 通过 MCP 调用到网关服务。做完这部分,你就能联想到,这里既然也可以硬编码操作,那么也可以调用 http、rpc,甚至是 mq,以及还可以是 rs232 串口通信,控制硬件设备。 +- 注意,这一节的实现,还是从 trigger 触发器的 http 层,直接调用到 domain 领域方法,后续在从 case 层进行串联流程。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-6\350\212\202\357\274\232\345\237\272\347\241\200\345\261\202\346\225\260\346\215\256\345\244\204\347\220\206.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-6\350\212\202\357\274\232\345\237\272\347\241\200\345\261\202\346\225\260\346\215\256\345\244\204\347\220\206.md" new file mode 100644 index 000000000..a50901f92 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-6\350\212\202\357\274\232\345\237\272\347\241\200\345\261\202\346\225\260\346\215\256\345\244\204\347\220\206.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第3-6节:基础层数据处理(Dao) +pay: https://t.zsxq.com/LaFBT +--- + +# 《AI MCP Gateway 网关服务系统》第3-6节:基础层数据处理(Dao) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/8xnuo](https://t.zsxq.com/8xnuo) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +将 AI MCP Gateway 库表设计,编写到工程中,映射成 PO、DAO、Mapper 文件,以便于后续章节的使用。 + +这一节,小傅哥会演示如何使用 AI IDE 工具,通过 Prompt 描述,来完成这些文件的生成。 + +## 二、结构介绍 + +如图,从领域层到基础设施层(mysql、redis...)的使用方式; + +
+ +
+ +- 首先,这里有个设计的思想的体现,这类思想也是 Spring、MyBatis 等框架源码中常用的思想,叫做【依赖倒置】。它的设计目标是,让数据使用方,不过渡依赖于提供方。提供方在`升级`、`替换`、`迭代`时候,都不影响使用方。 +- 之后,图里是依赖倒置的具体编码体现,从 domain 领域层,每个会话、鉴权、协议的功能编写时,所需的数据,是通过在 domain 领域层定义接口,之后由infrastructure 基础设施层做具体的功能实现。也就是说,每个功能区需要啥数据,就定义好接口,确定好入参和返回结果的,基础设施层引入 domain 层定义的这个标准接口,做具体的数据封装使用。**也类似于公司的领导,要这,要那,他不关心具体是谁做,最后做好就可以了。** diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-7\350\212\202\357\274\232\345\215\217\350\256\256\346\266\210\346\201\257\345\244\204\347\220\206-Initialize.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-7\350\212\202\357\274\232\345\215\217\350\256\256\346\266\210\346\201\257\345\244\204\347\220\206-Initialize.md" new file mode 100644 index 000000000..abb8afffe --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-7\350\212\202\357\274\232\345\215\217\350\256\256\346\266\210\346\201\257\345\244\204\347\220\206-Initialize.md" @@ -0,0 +1,33 @@ +--- +title: 【更】第3-7节:协议消息处理-Initialize +pay: https://t.zsxq.com/Re6ZW +--- + +# 《AI MCP Gateway 网关服务系统》第3-7节:协议消息处理-Initialize + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/yIOEM](https://t.zsxq.com/yIOEM) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +从本章开始,我们将进入消息协议的处理过程,把原本的硬编码的案例操作,通过网关ID(gatewayId)与数据库配置数据进行关联。 + +今天我们来处理第一个协议消息的处理场景,Initialize 初始化部分。这部分学习时,会附带调试 Spring AI 框架中 modelcontextprotocol 关于协议处理部分的源码,让大家有更多的积累。 + +- [https://github.com/modelcontextprotocol/java-sdk](https://github.com/modelcontextprotocol/java-sdk) +- [https://modelcontextprotocol.io/docs/getting-started/intro](https://modelcontextprotocol.io/docs/getting-started/intro) + +## 二、功能设计 + +如图,Initialize 初始化协议处理; + +
+ +
+ +- 首先,在 session 会话层的 adapter 下,创建调用基础设施层仓储服务的接口,并由基础设施层做功能实现。再通过依赖倒置的方式用于领域层使用。(这部分对于初次接触 DDD,又没学习过一些源码或者设计知识的伙伴,可能感觉有点绕,不过没关系,在看到课程源码以后,会逐步清晰) +- 然后,从数据库获取的数据,要进行协议转换。也就是把之前固定编码的部分,用数据库获取的数据来动态填充。这部分内容只是初次拿到数据并使用的小试牛刀,并不复杂。不过,我们还会增加对 Spring AI 源码的调试,扩展知识学习。 +- 之后,在我们陆续完成这些 handler 消息的处理后,则会在 case 层陆续做编排,以及验证网关ID和token等。 diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-8\350\212\202\357\274\232\345\215\217\350\256\256\346\266\210\346\201\257\345\244\204\347\220\206-ToolsList.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-8\350\212\202\357\274\232\345\215\217\350\256\256\346\266\210\346\201\257\345\244\204\347\220\206-ToolsList.md" new file mode 100644 index 000000000..0f81038e7 --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-8\350\212\202\357\274\232\345\215\217\350\256\256\346\266\210\346\201\257\345\244\204\347\220\206-ToolsList.md" @@ -0,0 +1,37 @@ +--- +title: 【更】第3-8节:协议消息处理-ToolsList +pay: https://t.zsxq.com/eOpMV +--- + +# 《AI MCP Gateway 网关服务系统》第3-8节:协议消息处理-ToolsList + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/U6Shg](https://t.zsxq.com/U6Shg) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +从 HTTP 接口到 MCP 协议的映射,这里要考虑的是怎么把一个完整请求 http 的接口描述录入到数据库,之后在通过数据库的配置,转换为 MCP 协议结构告诉 AI 客户端 tool/list,也就是这套 HTTP 接口到 MCP 以后所提供工具能力。 + +所以,小傅哥在带着大家实现的过程中,先做了基于 HTTP 接口所需存储的信息,做了库表的设计。之后到这一节,我们要把库表的数据转换为 MCP 协议结构数据。 + +拓展,像是 HTTP 可以做,那么 RPC、MQ、数据库等各类资源,你也可以转换为 MCP 服务协议进行使用。 + +## 二、流程设计 + +如图,Tool/List 工具列表协议处理; + +
+ +
+ +- 首先,我们要根据网关ID(gatewayId)从数据库中,获取网关配置和http工具字段配置列表,这部分数据相当于是把 HTTP 请求结构体,拆解喽放到数据库表中,之后再查询出来按照 MCP 协议结构组装使用。 +- 然后,是对 buildTools 工具细节的处理,这部分是对元素的拆分和组装。这部分还有一些细节在下面。 + +
+ +
+ +- 之后,映射数据库表 `mcp_protocol_mapping` 拆解字段的父子关系,一个字段以下的另外一个字段,如;`xxxRequest01 -> xxxRequest01.city` 的映射。所以在 buildProperty 的处理过程中要,要做递归循环,一层一层的找到这些内容,并拆解组装使用。 \ No newline at end of file diff --git "a/docs/md/project/ai-mcp-gateway/\347\254\2543-9\350\212\202\357\274\232\345\215\217\350\256\256\346\266\210\346\201\257\345\244\204\347\220\206-ToolsCall.md" "b/docs/md/project/ai-mcp-gateway/\347\254\2543-9\350\212\202\357\274\232\345\215\217\350\256\256\346\266\210\346\201\257\345\244\204\347\220\206-ToolsCall.md" new file mode 100644 index 000000000..5e4f90eca --- /dev/null +++ "b/docs/md/project/ai-mcp-gateway/\347\254\2543-9\350\212\202\357\274\232\345\215\217\350\256\256\346\266\210\346\201\257\345\244\204\347\220\206-ToolsCall.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第3-9节:协议消息处理-ToolsCall +pay: https://t.zsxq.com/U4Ouo +--- + +# 《AI MCP Gateway 网关服务系统》第3-9节:协议消息处理-ToolsCall + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/Vodcp](https://t.zsxq.com/Vodcp) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +从 AI 客户端,向 AI MCP 发起请求的过程中,小傅哥已经带着大家处理了;InitializeHandler - 初始化、ToolsListHandler - 获取工具列表,接下来再要处理的就是 ToolsCallHandler 的操作了,接收来自 AI 客户端,用户的请求,转换为对应的参数,发起接口请求(这部分我们会调用 http 接口)。 + +## 二、流程设计 + +如图,Tool/Call 工具接口调用协议处理; + +
+ +
+ +- 首先,从 ToolsCallHandler 入口开始,会接收到 AI 请求接口过程中,发过来的格式化参数信息(根据我们提供的工具列表说明提供过来的)。 +- 之后,根据获取的请求信息,解析请求参数(像是 http 接口分为 post、get,如果以后扩展 rpc 接口,就直接泛化调用,这部分就可以根据你想对接哪些东西来看了)并做协议调用。 +- 注意,当前章节还是gateway -> tool 是 `1:1` 的,这部分后续在做细化处理。我们先用一个简单的结构,把整个流程跑通。有了基础后,在深入的理解拆分会更好理解。 diff --git "a/docs/md/project/big-market/api/\347\254\25410\350\212\202\357\274\232\344\270\215\350\266\205\345\215\226\345\272\223\345\255\230\350\247\204\345\210\231\345\256\236\347\216\260.md" "b/docs/md/project/big-market/api/\347\254\25410\350\212\202\357\274\232\344\270\215\350\266\205\345\215\226\345\272\223\345\255\230\350\247\204\345\210\231\345\256\236\347\216\260.md" new file mode 100644 index 000000000..61ffef6e8 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25410\350\212\202\357\274\232\344\270\215\350\266\205\345\215\226\345\272\223\345\255\230\350\247\204\345\210\231\345\256\236\347\216\260.md" @@ -0,0 +1,40 @@ +--- +title: 第10节:不超卖库存规则实现 +pay: https://t.zsxq.com/17Y1QOlfG +--- + +# 《大营销平台系统设计实现》 - 营销服务 第10节:不超卖库存规则实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:通过模板模式,整合责任链、规则树,定义出抽奖的标准过程。以及让子类做具体调用功能实现。 +- **课程视频**:[https://t.zsxq.com/17kdkJw1C](https://t.zsxq.com/17kdkJw1C) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +当通过抽奖策略计算完用户可获得的奖品ID后,接下来就需要对这一条奖品记录进行**库存**的扣减操作。只有奖品库存扣减成功,才可以获得奖品ID对应的奖品,否则将走到兜底奖品。 + +除此之外,本节还需要对上一节实现的规则树节点;次数锁、兜底奖品,都会完善。 + +## 二、流程设计 + +**首先对于库存集中扣减类的业务流程,是不能直接用数据库表抗的。** + +比如数据库表有一条记录是库存,如果是通过锁这一条表记录更新库存为10、9、8的话,就会出现大量的用户在应用用获得数据库的连接后,等待前一个用户更新完库表记录后释放锁,让下一个用户进入在扣减。 + +这样随着用户参与量的增加,就会有非常多的用户处于等待状态,而等待的用户是持久数据库的连接的,这个连接资源非常宝贵,你占用了应用中其他的请求就进不来,最终导致一个请求要几分钟才能得到响应。【前台的用户越着急,越疯狂点击,直至越来越卡到崩溃】 + +所以,对于这样的秒杀场景,我们一般都是使用 redis 缓存来处理库存,它只要不超卖就可以。但也确保一点,不要用一条key加锁和等待释放的方式来处理,这样的效率依然是很低的。所以我们要尽可能的考虑分摊竞争,达到无锁化才是分布式架构设计的核心。 + +
+ +
+ +- 如果在前面章节所实现的规则树中,对于库存节点的操作,开发 decr 方式扣减库存。decr 是原子操作,效率非常高。这样要注意,setnx 加锁是一种兜底手段,避免后续有库存的恢复,导致库存从96消耗后又回到了98重复消费。所以对于每个key加锁,98、97、96... 即使有恢复库存也不会导致超卖。【setnx 在 redisson 是用 trySet 实现】 +- 库存消耗完以后,还需要更新库表的数据量。但这会也不能随着用户消耗奖品库存的速率,对数据库表执行扣减操作。所以这里可以通过 Redisson 延迟队列的 + 定时任务的凡是,缓慢消耗队列数据来更新库表数据变化。 diff --git "a/docs/md/project/big-market/api/\347\254\25411\350\212\202\357\274\232\346\212\275\345\245\226API\346\216\245\345\217\243\345\256\236\347\216\260.md" "b/docs/md/project/big-market/api/\347\254\25411\350\212\202\357\274\232\346\212\275\345\245\226API\346\216\245\345\217\243\345\256\236\347\216\260.md" new file mode 100644 index 000000000..25a36d1a3 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25411\350\212\202\357\274\232\346\212\275\345\245\226API\346\216\245\345\217\243\345\256\236\347\216\260.md" @@ -0,0 +1,38 @@ +--- +title: 第11节:抽奖API接口实现 +pay: https://t.zsxq.com/17q94Lwoe +--- + +# 《大营销平台系统设计实现》 - 营销服务 第11节:抽奖API接口实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:新增 big-market-api 模块,定义对外接口标准,并实现出3个抽奖服务接口。以及调整抽奖策略领域实现方式,满足接口单一职责。 +- **课程视频**:[https://t.zsxq.com/17NyL5gvS](https://t.zsxq.com/17NyL5gvS) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +结合着前面两节(13、14) WEB UI 的开发,以及接口的 Mock 让前端调用。我们可以知道服务端应该提供的接口标准。所以我们本节会按照这样的一个接口标准设计服务端的接口,同时在做一些服务端自身的扩展。 + +关于前后端接口的标准定义,小傅哥在视频中提到到,通常在公司做开发时,这部分是由产品和前后端工程师对照着产品PRD文档进行评审后定义的。前端工程师按照标准先进行Mock接口,随着后端工程逐步开发完成后,在进行对接联调。 + +## 二、流程设计 + +在大营销的系统架构设计中,有一个 trigger 模块,专门用于提供触发操作。这里我们把 `HTTP 调用`、`RPC(Dubbo)调用`、`定时任务`、`MQ监听`等动作,都称为触发操作。触发表示通过一种调用方式,调用到领域的服务上。 + +在大营销系统中,会给大家提供出 HTTP 接口,也会在后续提供 RPC 接口。RPC 就像 Dubbo 这样的框架,它的调用方式是需要对外提供接口描述性Jar,调用方拿到 Jar 包,就像本地调用接口一样,使用 RPC 框架,远程的调用到你的服务上。【关于这部分内容,在星球「码农会锁」的课程入口,基础教程中有 Dubbo 知识的案例讲解,小白可以补充学习】 + +那么因为为了让 HTTP 接口、RPC 接口,都能在一个标准下开发,所以本节会增加一个 `big-market-api` 模块,定义出接口信息和出入参对象。以便于分别可以实现本节所需的 HTTP 接口和后续所需的 RPC 接口。【注意;一般在大厂中,我们只需要定义 RPC 接口即可,因为 HTTP\小程序\APP 的接口,都是通过网关来调用的。网关会把 HTTP 请求转换为对应的 RPC 接口。这部分课程在星球中有**API网关**项目可以学习】 + +
+ +
+ +- 定义 big-market-api 模块,由 big-market-trigger 实现出3个接口;装配策略接口(调用后将抽奖策略装配到缓存)、查询奖品列表、随机抽奖接口。 +- 在 big-market-domain 的抽奖策略新增加 IRaffleAward 策略奖品接口,并调整 IRaffleStock 抽奖库存接口,都直接由子类实现。IRaffleStrategy 抽奖策略接口由抽象类定义抽奖过程【这部分是前面章节实现的】,并有子类实现。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25412\350\212\202\357\274\232\347\224\250\346\210\267\345\217\202\344\270\216\346\212\275\345\245\226\346\264\273\345\212\250\345\272\223\350\241\250\350\256\276\350\256\241.md" "b/docs/md/project/big-market/api/\347\254\25412\350\212\202\357\274\232\347\224\250\346\210\267\345\217\202\344\270\216\346\212\275\345\245\226\346\264\273\345\212\250\345\272\223\350\241\250\350\256\276\350\256\241.md" new file mode 100644 index 000000000..4ebe9abc1 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25412\350\212\202\357\274\232\347\224\250\346\210\267\345\217\202\344\270\216\346\212\275\345\245\226\346\264\273\345\212\250\345\272\223\350\241\250\350\256\276\350\256\241.md" @@ -0,0 +1,37 @@ +--- +title: 第12节:用户参与抽奖活动库表设计 +pay: https://t.zsxq.com/175Y3ypDT +--- + +# 《大营销平台系统设计实现》 - 营销服务 第12节:用户参与抽奖活动库表设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:梳理大营销抽奖业务流程,设计2个配置活动的表和3个用户领取记录的表。 +- **课程视频**:[https://t.zsxq.com/17X4jkyEI](https://t.zsxq.com/17X4jkyEI) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在一个营销场景中,抽奖的流程分为;参与的有效期、整体的预算库存、个人的可参与次数、之后是抽奖策略的计算处理,返回抽奖结果。 + +大营销第1阶段已经完成了抽奖策略的领域模块实现,接下来则需要设计一个外壳,把抽奖策略包装进去。这个外壳就是一个抽奖活动的配置,在活动上有相关的库存、时间、状态,个人在总、日、月分别可进行的参数次数判断。 + +那么在本节我们先来做下抽奖活动库表的设计,分析在这样一个场景中,需要哪些库表和字段才能支持我们的场景诉求。 + +## 二、业务流程 + +以用户参与大营销的抽奖活动为视角,来理解整个抽奖业务流程。 + +
+ +
+ +- 我们可以把用户参与抽奖理解为商城的一次下单,下单后才具备参与抽奖的资格。而下单的过程中,则需要过滤活动的相关信息以及库存数据。 +- 所有的判断流程做完后开始写入库中,库中则是用户一个互动的次数账户记录。记录着用户可以参与的抽奖次数。同时需要把参与活动的记录写一条订单。 +- 此外为了扩展用户在一些场景中,首次【签到/登录】可以赠送一个抽奖次数外,还可以通过购买、做任务、兑换等方式获得新的抽奖次数。这样用户就可以不断地消耗自己的积分兑换抽奖次数来抽奖了。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25413\350\212\202\357\274\232\345\274\225\345\205\245\345\210\206\345\272\223\345\210\206\350\241\250\350\267\257\347\224\261\347\273\204\344\273\266.md" "b/docs/md/project/big-market/api/\347\254\25413\350\212\202\357\274\232\345\274\225\345\205\245\345\210\206\345\272\223\345\210\206\350\241\250\350\267\257\347\224\261\347\273\204\344\273\266.md" new file mode 100644 index 000000000..481b9c0b8 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25413\350\212\202\357\274\232\345\274\225\345\205\245\345\210\206\345\272\223\345\210\206\350\241\250\350\267\257\347\224\261\347\273\204\344\273\266.md" @@ -0,0 +1,42 @@ +--- +title: 第13节:引入分库分表路由组件 +pay: https://t.zsxq.com/18oeNp26S +--- + +# 《大营销平台系统设计实现》 - 营销服务 第13节:引入分库分表路由组件 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:讲解分库分表路由的作用,并对工程配置路由组件以及测试验证。后续章节将在路由组件的继续上继续开发业务流程。 +- **课程视频**:[https://t.zsxq.com/18MieGCE4](https://t.zsxq.com/18MieGCE4) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +为用户的行为数据,使用路由组件将数据散列到分库分表中。 + +分库分表也是分布式架构中一个非常常用的数据存储方案,通常在公司中创建的系统都是直接创建出带有分库分表的系统架构。因为本身本身分库分表就是一个很成熟的方案,系统的分层和开发的熟练度都非常高。如果早期设计为单库单表的,那么后期再想扩展为分库分表则会有非常大的数据迁移和工程改造成本。 + +那么,分库分表以后,早期需要更多的数据库资源吗?其实并不用的,对于早期上线的系统,如果评估没那么大的体量,则可以使用虚拟机的方案安装数据库,也就是原来1台物理机,装1个数据库,现在则是2台物理机拆分为虚拟机,各个应用互相使用【作为主备】。而你占用的都是虚拟资源。也就是原来1台物理机等于5个虚拟机,现在5个虚拟机被分配到各个物理机上。所以你的分库分表并没有额外占用更多的资源。但这样的设计,业务体量上来以后,扩展只需要调整虚拟机的分配就可以了。 + +## 二、功能流程 + +在大营销的系统设计中,有一个配置库(big_market)和两个分库(big_market_01、big_market_02),我们需要对两个分库进行配置路由操作。达到分库分表的目的,而配置库则是一个单库单表存储活动等配置类信息。分库分表调用流程【如图】 + +
+ +
+ +- 以用户对数据库的操作为视角,发生用户类的行为操作时【账户、下单、流水】,则会根据用户ID(userId)进行路由,把数据分配到x库y表中。 +- 路由计算的处理,是以配置了 `@DBRouter`注解的 DAO 方法进行路由切面开始。通过获取用户ID(userId)值进行哈希索引计算。哈希值 & 2从n次幂数量的库表 - 1 得到一个值,在根据这个值计算应该分配到哪个库表上去。比如这个是6,分库分表是2库4表,共计8个,那么6就分配到了1库4+2库2个等于6,也就得到了2库2表。 +- 对于计算得到的分库分表值,存入到 ThreaLocal 中,这个东西的目的是可以在一个线程的调用中,可以随时获取值,而不需要通过方法传递。 +- 最后 Spring 在执行数据库操作前,会获取路由。而路由组件则实现了动态路由,从 ThreadLocal 中获取。此外注意,因为还有分表的操作,比如 table 需要为 table_01 这个动作是由 MyBatis Plugin 插件开发实现的。 + +关于数据库路由组件单独录制了课程,更多细节内容可以学习。[数据库分库分表路由组件](https://bugstack.cn/md/road-map/db-router.html) - 这个小组件足够写个简历项目用。 + +此外 [sharding-jdbc](https://bugstack.cn/md/road-map/sharding-jdbc.html) 也可以做分库分表,但直接使用小伙伴们会错过理解分库分表的核心设计,所以我们这里选择使用星球「码农会锁」里的 DB-Router 进行分库分表。 diff --git "a/docs/md/project/big-market/api/\347\254\25414\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\350\256\242\345\215\225\346\265\201\347\250\213\350\256\276\350\256\241.md" "b/docs/md/project/big-market/api/\347\254\25414\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\350\256\242\345\215\225\346\265\201\347\250\213\350\256\276\350\256\241.md" new file mode 100644 index 000000000..bbfa8d339 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25414\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\350\256\242\345\215\225\346\265\201\347\250\213\350\256\276\350\256\241.md" @@ -0,0 +1,35 @@ +--- +title: 第14节:抽奖活动订单流程设计 +pay: https://t.zsxq.com/18CO46Oux +--- + +# 《大营销平台系统设计实现》 - 营销服务 第14节:抽奖活动订单流程设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:解耦活动库存、个人库存为sku活动商品配置。以此方式支持后续用户发生的签到、打卡、支付等方式获得的抽奖次数。本节是对前面库表的再次处理,以此让读者可以学习到中奖的过程是什么样。 +- **课程视频**:[https://t.zsxq.com/18Sr22S5Z](https://t.zsxq.com/18Sr22S5Z) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +本节我们要设计出,用户参与抽奖活动的流程设计,并可以支持后续满足用户通过不同行为来增加自己的抽奖次数。 + +那么站在本节的诉求上,小傅哥将会带着读者对前面所设计的活动库表做一个解耦操作。来满足后续流程中个人可参与抽奖活动次数的变化处理。 + +## 二、功能流程 + +我们可以把抽奖的行为理解为一个下单过程,用户参与抽奖,也等价于商品下单。只不过这个商品的 sku 是活动信息。 + +
+ +
+ +- 用户的触达行为是后续需要扩展的部分,当我们把大营销结合给其他系统的时候,就可以让支付后的消息推送过来,给用户领取一次抽奖次数。并参与抽奖。【还记得你在商城,或者一些云服务购买后,可以参与抽奖的过程吗?】 +- 在上一节,小傅哥是把活动的可参与库存、用户的库存,都配置到活动本身。那这样就有一个问题,比如不同的场景,所需要在一个活动上给用户分配的抽奖次数不同,那么就不好配置了。所以我们要抽象一下,把活动和个人参与的次数,从活动配置中解耦出来,并通过 sku 商品表的方式配置出这样一组商品信息。 +- 另外,在活动信息表中,还有活动的库存。这里我们把活动的库存也提取出来,放到 sku 上。一个商品的 sku 能下单多少次,由 sku 管理就行了。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25415\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\346\265\201\346\260\264\345\205\245\345\272\223.md" "b/docs/md/project/big-market/api/\347\254\25415\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\346\265\201\346\260\264\345\205\245\345\272\223.md" new file mode 100644 index 000000000..be901a99a --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25415\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\346\265\201\346\260\264\345\205\245\345\272\223.md" @@ -0,0 +1,35 @@ +--- +title: 第15节:抽奖活动流水入库 +pay: https://t.zsxq.com/18mWcNHOp +--- + +# 《大营销平台系统设计实现》 - 营销服务 第15节:抽奖活动流水入库 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:实现抽奖下单对活动账户充值的操作流程,把数据写入到库中,并可以保证幂等性。 +- **课程视频**:[https://t.zsxq.com/18gaoKYEn](https://t.zsxq.com/18gaoKYEn) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在基于活动、次数所组合的活动 sku,用户参与活动就相当于,下单sku给自己的活动账户充值可参与的额度次数。所以本节小傅哥就带着大家,把活动的下单的过程落入到数据库中。 + +本节写库会涉及到分库分表组件切分和开启事务的操作。 + +## 二、功能流程 + +按照上一节我们对活动流程的设计,用户参与抽奖活动就会有一个活动额度账户,而本节则让来实现参与活动对自己的活动额度账户充值的过程。 + +
+ +
+ +- 本节会先实现出领取活动的框架结构代码,并对数据进行落库操作。(落库的过程会有分库分表下事务的操作) +- 活动日期、活动状态、sku库存校验和扣减,这些都是固定的流程。无论创建多少个活动都会走这样的统一流程,所以这里适合添加一个责任链模式的结构。 +- 因为是分库分表设计,所以库表数据的写入需要确定切分键,并在同一个连接下执行 commit 这样才能把用户的活动账户和订单流程,一起写库。(也就是一个事务的特性) \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25416\350\212\202\357\274\232\345\274\225\345\205\245MQ\345\244\204\347\220\206\346\264\273\345\212\250SKU\345\272\223\345\255\230\344\270\200\350\207\264\346\200\247.md" "b/docs/md/project/big-market/api/\347\254\25416\350\212\202\357\274\232\345\274\225\345\205\245MQ\345\244\204\347\220\206\346\264\273\345\212\250SKU\345\272\223\345\255\230\344\270\200\350\207\264\346\200\247.md" new file mode 100644 index 000000000..441cdea21 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25416\350\212\202\357\274\232\345\274\225\345\205\245MQ\345\244\204\347\220\206\346\264\273\345\212\250SKU\345\272\223\345\255\230\344\270\200\350\207\264\346\200\247.md" @@ -0,0 +1,36 @@ +--- +title: 第16节:引入MQ处理活动SKU库存一致性 +pay: https://t.zsxq.com/188e3DFyj +--- + +# 《大营销平台系统设计实现》 - 营销服务 第16节:引入MQ处理活动SKU库存一致性 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:引入 RabbitMQ 消息组件,在活动SKU库存消耗完毕后,触发消息推送更新库存为最终一致。 +- **课程视频**:[https://t.zsxq.com/18UKKWY9x](https://t.zsxq.com/18UKKWY9x) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +完成活动责任链判断,包括;活动的校验【日期、状态、sku库存】,之后是sku 库存的扣减操作。 之后这里会涉及一个缓存库存和数据库库存一致性问题,是本节要处理的重点。一个手段是之前在策略实现阶段采用的延迟队列做趋势更新,另外一个是本节要引入 RabbitMQ 在库存消耗空以后发送 MQ 消息,直接更新清空最终库存,保持缓存与数据库的一致性。 + +**注意**,可能此时缓存消耗库存阶段有失败丢失,不完全等于数据库真实库存。不过没关系,先保证一个统一库存即可。后续可以扫描统计订单数量,来校准库存。 + +## 二、功能流程 + +完善责任链功能,和库存数据一致性的处理; + +
+ +
+ +- 第一步;完成责任链的活动校验,时间、状态、库存。 +- 第二步;对库存的扣减,使用 decr + lock 锁的方式(兜底)进行处理。 +- 第三步;做完库存扣减后,发送延迟队列,由任务调度更新趋势库存,满足最终一致。 +- 第四步;库存消耗为0后,发送MQ消息,驱动变更数据库库存为0 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25417\350\212\202\357\274\232\347\224\250\346\210\267\351\242\206\345\217\226\346\264\273\345\212\250\345\272\223\350\241\250\350\256\276\350\256\241.md" "b/docs/md/project/big-market/api/\347\254\25417\350\212\202\357\274\232\347\224\250\346\210\267\351\242\206\345\217\226\346\264\273\345\212\250\345\272\223\350\241\250\350\256\276\350\256\241.md" new file mode 100644 index 000000000..201668154 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25417\350\212\202\357\274\232\347\224\250\346\210\267\351\242\206\345\217\226\346\264\273\345\212\250\345\272\223\350\241\250\350\256\276\350\256\241.md" @@ -0,0 +1,36 @@ +--- +title: 第17节:用于领取活动库表设计 +pay: https://t.zsxq.com/18Hf0vgQV +--- + +# 《大营销平台系统设计实现》 - 营销服务 第17节:用于领取活动库表设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★★☆ +- **本章重点**:分析抽奖业务流程并设计出对应的库表,本节会新增5张库表。 +- **课程视频**:[https://t.zsxq.com/18R7A8BsN](https://t.zsxq.com/18R7A8BsN) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +设计用于,用户参与活动所需的库表。从用户参与活动,扣减个人活动账户次数,创建活动订单。抽奖完成获得奖品ID后,写入中奖记录和task任务表(发mq补偿使用)。之后发送MQ消息,更新中奖记录。 + +这一套流程,需要本节增加6张表。—— 小傅哥给大家的这些设计,都是来自于真实的场景中,而不是随便一张库表就只写 CRUD 。 + +## 二、业务流程 + +从业务流程的模型结构分析,看整个链路所需的库表; + +
+ +
+ +- 首先,用户抽奖开始,需要领取活动,扣减个人账户额度。生成一个抽奖订单。每个用户都有一个活动账户额度,里面包含了;总可参与次数、月可参与次数、日可参与次数。这样的设计是为了应对复杂的业务需求。那么有这样的表,就不能只是在一个表里扣减额度,因为每天都要扣减额度,但只在一个账户中扣减,日的次数第一天扣减完,第二天相当于回复为原始库存继续扣减。所以这里要生成一个出每日活动账户,当前则在自己的日账户中扣减。而总库存的日,是一种镜像记录,方便查询统计的。 +- 账户,在扣减额度和用户的订单,要在一个事务内完成。但不能和后续的抽奖结果继续做事务,因为抽奖的过程还有很多的操作,而已包括缓存的处理,而他们都不能做事务。所以这部分是分开的。 +- 之后,抽奖策略结果计算完毕后,把奖品ID写入中奖记录表中,同时写一个 task 任务表。任务表是发 MQ 消息的。但在写入完成奖品订单后,则直接发送一个 MQ 消息【发送后更新 task 表状态】,如果发送失败则还有 task 任务表,由 job 任务扫描的方式处理。这样可以尽快的发送 MQ 消息。 +- 最后,接收发送的 MQ 开始发放奖品,本节暂时先不处理奖品的发放。 diff --git "a/docs/md/project/big-market/api/\347\254\25418\350\212\202\357\274\232\351\242\206\345\217\226\346\264\273\345\212\250\346\211\243\345\207\217\350\264\246\346\210\267\351\242\235\345\272\246.md" "b/docs/md/project/big-market/api/\347\254\25418\350\212\202\357\274\232\351\242\206\345\217\226\346\264\273\345\212\250\346\211\243\345\207\217\350\264\246\346\210\267\351\242\235\345\272\246.md" new file mode 100644 index 000000000..fdf28d893 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25418\350\212\202\357\274\232\351\242\206\345\217\226\346\264\273\345\212\250\346\211\243\345\207\217\350\264\246\346\210\267\351\242\235\345\272\246.md" @@ -0,0 +1,36 @@ +--- +title: 第18节:领取活动扣减账户额度 +pay: https://t.zsxq.com/198oqnRQL +--- + +# 《大营销平台系统设计实现》 - 营销服务 第18节:领取活动扣减账户额度 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★★☆ +- **本章重点**:拆分活动领域为额度子域、参与子域、装配子域,完成用户领取活动,创建订单扣减账户(总、月、日)额度。 +- **课程视频**:[https://t.zsxq.com/19FswzbMD](https://t.zsxq.com/19FswzbMD) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +活动领域是一个含有活动部署、活动账户充值、用户参与活动的多个核心子领域组成,也就是一个活动下不只是有一个 service 服务。对于这样的情况,我们需要涉及出不同的子领域来承接这些功能。而不是把所有的功能都平铺到一个模块下。因为平铺写一堆的 service 的方式会让整个逻辑混乱,需求迭代的次数越多,最后的代码也就会越混乱。 + +所以本节我们要在添加扣减额度的需求上,对前面章节实现的活动sku充值,划分下领域。之后在进行新功能的实现。 + +## 二、业务流程 + +用户抽奖的业务流程分为;给自己的活动账户添加额度(购买、兑换、打卡),领取活动(扣减互动账户额度)、执行抽奖策略、抽奖结果落库。本节实现到领取活动部分。 + +在本节实现中先给原有实现额度充值的对象,新增加 quota 额度子领域文件夹,迁移类进去以及调整类名。这样一个活动类下就有 quota、armory 两个子领域了,之后本节在增加一个 partake 参与活动的领域。 + +
+ +
+ +- 原有的`额度账户`功能只是做文件夹的迁移,不会动原有的功能。 +- 本节主要实现参与活动的领域,本部分主要涉及了接口和抽象类定义流程,以及向库表写入数据。库表的写入是一个聚合对象下的事务操作,涵盖了3个账户表(总、月、日)和一个订单表。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25419\350\212\202\357\274\232\345\206\231\345\205\245\344\270\255\345\245\226\350\256\260\345\275\225\345\222\214\344\273\273\345\212\241\350\241\245\345\201\277\345\217\221\351\200\201.md" "b/docs/md/project/big-market/api/\347\254\25419\350\212\202\357\274\232\345\206\231\345\205\245\344\270\255\345\245\226\350\256\260\345\275\225\345\222\214\344\273\273\345\212\241\350\241\245\345\201\277\345\217\221\351\200\201.md" new file mode 100644 index 000000000..e4deecaeb --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25419\350\212\202\357\274\232\345\206\231\345\205\245\344\270\255\345\245\226\350\256\260\345\275\225\345\222\214\344\273\273\345\212\241\350\241\245\345\201\277\345\217\221\351\200\201.md" @@ -0,0 +1,40 @@ +--- +title: 第19节:写入中奖记录和任务补偿发送MQ +pay: https://t.zsxq.com/198Bt1EL8 +--- + +# 《大营销平台系统设计实现》 - 营销服务 第19节:写入中奖记录和任务补偿发送MQ + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★★☆ +- **本章重点**:使用 task表机制,在写入奖品记录时,一个事务下完成 task mq 消息的写入,并可以通过补偿的方式推送 MQ 消息。 +- **课程视频**:[https://t.zsxq.com/19LcGz8jM](https://t.zsxq.com/19LcGz8jM) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +一整个抽奖过程先从活动账户额度充值、活动参与、抽奖执行【策略】,之后就是中奖记录写入和后续的发奖。那么本节要实现的就是这里的奖品记录写入和异步消息发送。 + +本节会用到 task 任务表,在写入奖品记录的时候,写入一条 task 消息发送任务,作为补偿使用。当 mq 发送失败的时候,则由任务扫描 task 消息进行发送。 + +## 二、业务流程 + +从用户中奖到发奖,通常来说我们会做异步解耦,因为一些奖品的方法并不是都是在抽奖系统,而是各种 RPC/HTTP 接口来发放,这些接口有些时候会有超时的问题,需要重试处理。所以需要数据库写入一条记录,之后记录一个状态。之后奖品真正发放完以后,在更新这个这个状态。 + +这样可以让用户快速指导自己已中奖即可,之后点击详情或者奖品列表进入中查看自己的中奖结果。 + +那么这里的写入记录和发送 MQ 消息,不能用事务解决,事务主要是数据库事务,但 MQ 消息不是数据库事务。所以需要写入一个 task 表,通过任务补偿的方式进行处理。 + +**整体流程如下;** + +
+ +
+ +- 抽奖、抽奖策略,在前面已经实现完成,本节主要实现的是蓝色部分,写入中奖结果和任务,以及发送MQ消息。 +- 这里我们会先简单的接收消费MQ,后续做消费奖品发放的其他处理。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\2541\350\212\202\357\274\232\346\212\275\345\245\226\347\255\226\347\225\245\351\242\206\345\237\237\345\222\214\345\272\223\350\241\250\350\256\276\350\256\241.md" "b/docs/md/project/big-market/api/\347\254\2541\350\212\202\357\274\232\346\212\275\345\245\226\347\255\226\347\225\245\351\242\206\345\237\237\345\222\214\345\272\223\350\241\250\350\256\276\350\256\241.md" new file mode 100644 index 000000000..660ec401f --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\2541\350\212\202\357\274\232\346\212\275\345\245\226\347\255\226\347\225\245\351\242\206\345\237\237\345\222\214\345\272\223\350\241\250\350\256\276\350\256\241.md" @@ -0,0 +1,32 @@ +--- +title: 第1节:抽奖策略领域和库表设计 +pay: https://t.zsxq.com/15rGKOAfA +--- + +# 《大营销平台系统设计实现》 - 营销服务 第1节:抽奖策略领域和库表设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:通过分析抽奖的行为逻辑,设计抽奖策略领域模型和对应的库表结构。 +- **课程视频**:[https://t.zsxq.com/15gLHtPaU](https://t.zsxq.com/15gLHtPaU) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +通过分析大营销平台中抽奖的产品功能,设计抽奖策略模型和库表结构。抽奖策略是抽奖中非常核心功能,尤其是本课程中所体现出的抽奖产品功能,需要更好的库表结构支撑着领域模型的实现。 + +## 二、内容说明 + +本节会通过分析、拆解、抛出问题和解决问题的方式,带着大家一起厘清抽奖策略模型和对应的表结构信息。 + +
+ +
+ +- 拆解分析整个大营销平台的核心领域,分析领域功能关联关系。 +- 再聚焦到抽奖策略模型中,通过对产品功能的分析,完成领域功能理解和库表的设计。 diff --git "a/docs/md/project/big-market/api/\347\254\25420\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\346\265\201\347\250\213\344\270\262\350\201\224.md" "b/docs/md/project/big-market/api/\347\254\25420\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\346\265\201\347\250\213\344\270\262\350\201\224.md" new file mode 100644 index 000000000..3e6884456 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25420\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\346\265\201\347\250\213\344\270\262\350\201\224.md" @@ -0,0 +1,39 @@ +--- +title: 第20节:抽奖活动流程串联 +pay: https://t.zsxq.com/19JofVjoa +--- + +# 《大营销平台系统设计实现》 - 营销服务 第20节:抽奖活动流程串联 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:提供抽奖API接口,串联整个抽奖过程的领域模块。 +- **课程视频**:[https://t.zsxq.com/19fVwshWT](https://t.zsxq.com/19fVwshWT) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +到本节以抽奖为流程的各个领域模块已经开发完成,本章我们将串联各个模块提供抽奖API接口。 + +在一些较大规模的系统架构中,DDD 的分层结构中会有一个 case 层,也可以叫 application 层。专门用于对 domain 领域的编排。这一层引入后的优势是为复杂项目提供编排能力,提高领域模块的复用性。但缺点是会额外多出一组对象的转换来防腐,因为开发成本会有所增加。所以这部分要看具体的场景而定,看是否选择增加 case 编排层。 + +## 二、业务流程 + +- 首先,我们在过往的开发中,我们提供了活动装配、策略装配,所以要单独为装配提供好一个接口来使用。 +- 之后,串联抽奖所需的过程模块,从参数校验、参与活动、抽奖策略、存放结果、更新订单,直至最终返回抽奖结果。 + +**整体流程如下** + +
+ +
+ +- 开发内容1;串联整个抽奖流程,提供 API +- 开发内容2;提供以活动为主导的,预热装配动作 +- 开发内容3;抽奖策略模块中,校验账户额度。【之前的一个策略规则,需要根据已经抽奖次数进行解锁】 +- 开发内容4;存放抽奖结果后,更新用户参与活动时的抽奖单状态为已使用。 diff --git "a/docs/md/project/big-market/api/\347\254\25421\350\212\202\357\274\232\346\264\273\345\212\250\344\277\241\346\201\257API\350\277\255\344\273\243\345\222\214\345\212\237\350\203\275\345\256\214\345\226\204.md" "b/docs/md/project/big-market/api/\347\254\25421\350\212\202\357\274\232\346\264\273\345\212\250\344\277\241\346\201\257API\350\277\255\344\273\243\345\222\214\345\212\237\350\203\275\345\256\214\345\226\204.md" new file mode 100644 index 000000000..137ce8d66 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25421\350\212\202\357\274\232\346\264\273\345\212\250\344\277\241\346\201\257API\350\277\255\344\273\243\345\222\214\345\212\237\350\203\275\345\256\214\345\226\204.md" @@ -0,0 +1,34 @@ +--- +title: 第21节:活动信息API迭代和功能完善 +pay: https://t.zsxq.com/19aanGm3U +--- + +# 《大营销平台系统设计实现》 - 营销服务 第21节:活动信息API迭代和功能完善 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:完善接口功能,增加策略加锁key的过期时间,修复一些bug +- **课程视频**:[https://t.zsxq.com/19wIzfZxN](https://t.zsxq.com/19wIzfZxN) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +扩展查询奖品信息接口【queryRaffleAwardList】,以用户活动为视角进行查询增加返回信息。同时修复Redisson序列化、加锁过期时间问题。 + +## 二、业务流程 + +本节实现功能到不复杂,但涉及的代码量也比较多。因为整个过程要在策略的查询进行扩展,提供用`活动ID+用户ID`进行查询。这样才能查询到活动配置配置的信息,用户的抽奖参与次数,以及扩展查询策略的规则(rule_lock)配置。 + +
+ +
+ +1. 提供带有活动和用户属性信息判断的抽奖列表数据,满足后续前端展示抽奖列表时可以渲染出奖品是否被加锁并提示用户还需要抽奖几次才能解锁奖品。 +2. 在以活动为开始的抽奖,对抽奖策略加锁的key设置过期时间为活动结束时间。 +3. 完善 Redis 序列化操作,Redisson 添加序列化设置。 +4. 抽奖sku消耗完毕的队列清空,暂时先去掉。因为目前使用的一套队列,避免都清空喽。这部分不会影响到库存的更新。 diff --git "a/docs/md/project/big-market/api/\347\254\25422\350\212\202\357\274\232\347\224\250\346\210\267\350\241\214\344\270\272\350\277\224\345\210\251\345\205\245\350\264\246.md" "b/docs/md/project/big-market/api/\347\254\25422\350\212\202\357\274\232\347\224\250\346\210\267\350\241\214\344\270\272\350\277\224\345\210\251\345\205\245\350\264\246.md" new file mode 100644 index 000000000..ed29991d3 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25422\350\212\202\357\274\232\347\224\250\346\210\267\350\241\214\344\270\272\350\277\224\345\210\251\345\205\245\350\264\246.md" @@ -0,0 +1,36 @@ +--- +title: 第22节:用户行为返利入账 +pay: https://t.zsxq.com/ICv9z +--- + +# 《大营销平台系统设计实现》 - 营销服务 第22节:用户行为返利入账 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:开发用户行为返利功能,实现入账和异步消息发送。 +- **课程视频**:[https://t.zsxq.com/YpuSy](https://t.zsxq.com/YpuSy) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +按照用户行为返利的需求设计,创建相应的库表,开发 rebate 返利领域,提供返利订单创建接口。并在写入订单后发送 MQ 消息。后续则处理奖励入账。 + +## 二、业务流程 + +用户的行为动作返利,是一种日常的活动类型。比如;你在某个平台创建了新账号,就会给你发一堆的开户优惠券,这些都是日常的返利活动。 + +日常的返利会根据用户所完成的行为动作来触达,这包括;打卡、签到、连签、支付、开户、交易、信贷、拉新等各类的动作。我们本节中所涉及的,主要是日常的日历签到行为,并也从功能实现上扩展出各类型的任务,以便于后续扩展。 + +
+ +
+ +- 一个用户行为可能会给多种奖励,所以在接收到用户信息后,会根据配置组装聚合对象。【聚合的目的就是为了做一个统一的事务】 +- 一个聚合对象中包含了返利的订单实体对象,写入task的实体对象。它们是一个事务入库。 +- 另外是发送MQ消息,在完成入口动作后,会直接发送MQ消息,并且如果发送失败,会有任务兜底。【这样是面试中经常问到的点,如果MQ消息发送失败了,你是怎么处理的。】 + diff --git "a/docs/md/project/big-market/api/\347\254\25423\350\212\202\357\274\232\347\224\250\346\210\267\350\241\214\344\270\272\350\277\224\345\210\251\347\273\223\347\256\227.md" "b/docs/md/project/big-market/api/\347\254\25423\350\212\202\357\274\232\347\224\250\346\210\267\350\241\214\344\270\272\350\277\224\345\210\251\347\273\223\347\256\227.md" new file mode 100644 index 000000000..71c5a62cf --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25423\350\212\202\357\274\232\347\224\250\346\210\267\350\241\214\344\270\272\350\277\224\345\210\251\347\273\223\347\256\227.md" @@ -0,0 +1,33 @@ +--- +title: 第23节:用户行为返利结算 +pay: https://t.zsxq.com/AGmAr +--- + +# 《大营销平台系统设计实现》 - 营销服务 第23节:用户行为返利结算 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:接收异步MQ消息进行结算处理,并提供出日历签到接口,用于后续对接。 +- **课程视频**:[https://t.zsxq.com/8WfLZ](https://t.zsxq.com/8WfLZ) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +上一节对用户的行为根据返利配置进行入账发送MQ消息,这一节将接收MQ消息开始结算返利。这里也就是给用户的活动账户充值。并提供一个日历签到返利的接口,用于后续对接到前端UI使用。 + +## 二、业务流程 + +扩展用户行为流程,增加入账结算; + +
+ +
+ +- 增加接收MQ消息流程,调用活动账户额度入账接口,增加用户的可抽奖次数。这里包括;总、月、日,账户额度更新。 +- 注意 MQ 消息会有过滤,目前只处理返利是 sku 类型的返利。 +- 最后在提供一个日历签到的接口,便于后续对接到前端页面使用。 diff --git "a/docs/md/project/big-market/api/\347\254\25424\350\212\202\357\274\232\350\247\204\345\210\231\345\256\214\345\226\204\345\222\214\345\272\224\347\224\250\346\216\245\345\217\243\345\256\236\347\216\260.md" "b/docs/md/project/big-market/api/\347\254\25424\350\212\202\357\274\232\350\247\204\345\210\231\345\256\214\345\226\204\345\222\214\345\272\224\347\224\250\346\216\245\345\217\243\345\256\236\347\216\260.md" new file mode 100644 index 000000000..318c1f530 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25424\350\212\202\357\274\232\350\247\204\345\210\231\345\256\214\345\226\204\345\222\214\345\272\224\347\224\250\346\216\245\345\217\243\345\256\236\347\216\260.md" @@ -0,0 +1,33 @@ +--- +title: 第24节:规则完善和应用接口实现 +pay: https://t.zsxq.com/BLn04 +--- + +# 《大营销平台系统设计实现》 - 营销服务 第24节:规则完善和应用接口实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:综合实现抽奖所需渲染UI的接口,包括;活动账户额度、签到&查询、权重范围,以及完善权重规则校验接口 +- **课程视频**:[https://t.zsxq.com/HDir9](https://t.zsxq.com/HDir9) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +完善权重规则的校验,用户的总抽奖次数作为规则权重的比对值,一个活动下抽奖总N次后,可获得必中范围奖品。之后提供出,活动账户额度、签到&查询、权重范围接口,用于前端UI渲染使用。 + +## 二、业务流程 + +如图,来看下要实现的接口内容; + +
+ +
+ +- 剩余抽奖次数接口,后端会返回;总、日、月,总额度和剩余额度,这样可以满足前端的各类渲染诉求。 +- 点击签到接口和签到结果查询接口,如果今日已签到,则展示灰色并不可签到。 +- 权重规则范围展示接口,告诉用户总计抽奖多少次后,可以必中指定范围奖品。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25425\350\212\202\357\274\232\347\247\257\345\210\206\345\217\221\345\245\226\346\234\215\345\212\241\345\256\236\347\216\260.md" "b/docs/md/project/big-market/api/\347\254\25425\350\212\202\357\274\232\347\247\257\345\210\206\345\217\221\345\245\226\346\234\215\345\212\241\345\256\236\347\216\260.md" new file mode 100644 index 000000000..db2211827 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25425\350\212\202\357\274\232\347\247\257\345\210\206\345\217\221\345\245\226\346\234\215\345\212\241\345\256\236\347\216\260.md" @@ -0,0 +1,37 @@ +--- +title: 第25节:积分发奖服务实现 +pay: https://t.zsxq.com/K8aTS +--- + +# 《大营销平台系统设计实现》 - 营销服务 第25节:积分发奖服务实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:设计用户活动积分流程,创建用户积分表,并开发用户抽奖积分奖品后,完成奖品发放流程,给用户增加积分。 +- **课程视频**:[https://t.zsxq.com/Jfy0B](https://t.zsxq.com/Jfy0B) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +设计用户活动积分流程,创建用户积分表,并开发用户抽奖积分奖品后,完成奖品发放流程,给用户增加积分。用户的积分是一种中间媒介,抽奖可以获取积分奖品,签到可以获取积分奖品,还可以用积分兑换活动抽奖次数。通过这样的方式把整个抽奖流程闭环。 + +## 二、业务流程 + +如图,嵌入的积分场景; + +
+ +
+ +- 积分的获取目前有4个流程,抽奖中有3个,包括; + 1. 默认抽取到积分奖品。 + 2. 抽取到其他奖品,但无库存时,走积分兜底奖品。 + 3. 配置的黑名单用户,在 0.01 ~ 1 积分,的随机奖品。 + 4. 发起签到,可配置积分奖励,后续实现签到发奖积分流程。 + +- 实现奖品分发服务,发放用户积分奖励,到用户的积分账户表。积分账户表是本节新增加库表。其他奖励后续实现。 diff --git "a/docs/md/project/big-market/api/\347\254\25426\350\212\202\357\274\232\347\247\257\345\210\206\351\242\206\345\237\237\350\260\203\351\242\235\346\234\215\345\212\241.md" "b/docs/md/project/big-market/api/\347\254\25426\350\212\202\357\274\232\347\247\257\345\210\206\351\242\206\345\237\237\350\260\203\351\242\235\346\234\215\345\212\241.md" new file mode 100644 index 000000000..92f6319c6 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25426\350\212\202\357\274\232\347\247\257\345\210\206\351\242\206\345\237\237\350\260\203\351\242\235\346\234\215\345\212\241.md" @@ -0,0 +1,32 @@ +--- +title: 第26节:积分领域调额服务 +pay: https://t.zsxq.com/cXQ61 +--- + +# 《大营销平台系统设计实现》 - 营销服务 第26节:积分领域调额服务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:增加用户积分领域模块,开发积分调额接口。串联行为发奖动作,发放用户积分奖励。 +- **课程视频**:[https://t.zsxq.com/Vy6oX](https://t.zsxq.com/Vy6oX) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +增加用户积分领域模块,开发积分调额接口。串联行为发奖动作,发放用户积分奖励。这是一整条流程,在设计积分领域模块的时候,要考虑这块模型的功能添加都有哪些,将来可能会存在的接口。如;把对积分账户的增加,抽象为调额。将来使用积分还可以定义出积分消费的接口。我们通过不同的接口做职责的划分。 + +## 二、业务流程 + +如图,增加积分领域模块; + +
+ +
+ +- 首先,设计、定义和开发出积分领域模块,增加积分调额接口。 +- 之后,对接返利异步消息,完成积分额度的增加。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25427\350\212\202\357\274\232\347\247\257\345\210\206\346\224\257\344\273\230\345\205\221\346\215\242\345\225\206\345\223\201.md" "b/docs/md/project/big-market/api/\347\254\25427\350\212\202\357\274\232\347\247\257\345\210\206\346\224\257\344\273\230\345\205\221\346\215\242\345\225\206\345\223\201.md" new file mode 100644 index 000000000..ea9c676b5 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25427\350\212\202\357\274\232\347\247\257\345\210\206\346\224\257\344\273\230\345\205\221\346\215\242\345\225\206\345\223\201.md" @@ -0,0 +1,35 @@ +--- +title: 第27节:积分支付兑换商品 +pay: https://t.zsxq.com/iBPBR +--- + +# 《大营销平台系统设计实现》 - 营销服务 第27节:积分支付兑换商品 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:开发扣减积分,兑换sku商品,所需的服务模块。让积分的获取、消费,形成逻辑闭环。 +- **课程视频**:[https://t.zsxq.com/3UHYZ](https://t.zsxq.com/3UHYZ) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +开发扣减积分,兑换sku商品,所需的服务模块。让积分的获取、消费,形成逻辑闭环。 + +积分是衔接营销场景中各个功能模块的一个非常重要的手段,如;各项用户的行为 + 抽奖返利积分,积分兑换商品、积分兑换抽奖次数、积分支付抵扣、积分会员等级折扣等。【这些内容在你的日常使用各类互联网产品中,都会遇到的,可以留意下】 + +## 二、业务流程 + +如图,积分兑换商品交易流程; + +
+ +
+ +- 首先,对 sku 商品库增加积分金额,用于积分支付,而签到兑换类则无需关注金额。 +- 之后,商品下单需要提供出交易策略;无支付交易和有支付交易。 +- 最后,下单完成则进行积分抵扣,以及接收到支付成功消息,进行充值入账。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25428\350\212\202\357\274\232\347\247\257\345\210\206\345\272\224\347\224\250\345\234\272\346\231\257\346\216\245\345\217\243\345\256\236\347\216\260.md" "b/docs/md/project/big-market/api/\347\254\25428\350\212\202\357\274\232\347\247\257\345\210\206\345\272\224\347\224\250\345\234\272\346\231\257\346\216\245\345\217\243\345\256\236\347\216\260.md" new file mode 100644 index 000000000..291bcd807 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25428\350\212\202\357\274\232\347\247\257\345\210\206\345\272\224\347\224\250\345\234\272\346\231\257\346\216\245\345\217\243\345\256\236\347\216\260.md" @@ -0,0 +1,35 @@ +--- +title: 第28节:积分应用场景接口实现 +pay: https://t.zsxq.com/2M4jV +--- + +# 《大营销平台系统设计实现》 - 营销服务 第28节:积分应用场景接口实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:以支持积分使用「商品、账户、兑换」对接前端诉求,在本节开发所需的接口。 +- **课程视频**:[https://t.zsxq.com/X3hfU](https://t.zsxq.com/X3hfU) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +以支持积分使用「商品、账户、兑换」对接前端诉求,在本节开发所需的接口。包括;串联前面章节中实现积分模块功能完成积分兑换case(串联的每一个服务都可以当做一个case看待,只不过我们的场景没有那么多,所以在工程搭建中去掉了单独的case模块,减少对象频繁转换)、sku商品查询、用户积分账户额度查询。 + +## 二、业务流程 + +如图,积分接口在前端UI中的使用; + +
+ +
+ +- 虚线框内为本节要为前端UI开发的接口,两个查询积分和商品,一个是操作兑换的处理。 +- 注意,兑换的操作会对接口进一步完善,创建下单商品时候会返回未支付订单。这是因为从下单到支付是分开的步骤,可能下单完成,但支付的时候金额不足了「多笔下单场景消费积分」,也可能其他异常导致下单失败。这样的场景都需要把未支付的订单查询出来进行支付。 + + + diff --git "a/docs/md/project/big-market/api/\347\254\25429\350\212\202\357\274\232\345\210\206\345\270\203\345\274\217\345\212\250\346\200\201\351\205\215\347\275\256\346\264\273\345\212\250\351\231\215\347\272\247.md" "b/docs/md/project/big-market/api/\347\254\25429\350\212\202\357\274\232\345\210\206\345\270\203\345\274\217\345\212\250\346\200\201\351\205\215\347\275\256\346\264\273\345\212\250\351\231\215\347\272\247.md" new file mode 100644 index 000000000..ef9470f15 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25429\350\212\202\357\274\232\345\210\206\345\270\203\345\274\217\345\212\250\346\200\201\351\205\215\347\275\256\346\264\273\345\212\250\351\231\215\347\272\247.md" @@ -0,0 +1,37 @@ +--- +title: 第29节:分布式动态配置活动降级 +pay: https://t.zsxq.com/mbXeM +--- + +# 《大营销平台系统设计实现》 - 营销服务 第29节:分布式动态配置活动降级 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:基于 Zookeeper 实现分布式动态配置中心服务,用于分布式应用节点系统中的环境属性值变更。 +- **课程视频**:[https://t.zsxq.com/m81oK](https://t.zsxq.com/m81oK) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +基于 Zookeeper 实现分布式动态配置中心服务,用于分布式应用节点系统中的环境属性值变更。这样我们可以让所有分布式系统中,类下的属性值做动态的调整,及时的对系统进行;切量、熔断、降级、黑白名单等用途。 + +在互联网公司中面向C端的应用场景,有非常多的动态配置使用,包括你看到的一些金融场景的额度费率、电商场景的特惠商品、出行场景的促销活动,都会有一些动态配置的使用。那么本节小傅哥就从原理到应用带着大家来实现一下。 + +## 二、业务流程 + +如图,动态配置组件实现方案; + +
+ +
+ +以Zookeeper为配置中心服务,基于 Zookeeper 的节点监听值变更机制,动态修改应用程序中属性值。 + +- 首先,我们需要定义出一个 Zookeeper 监听的配置路径,一般这个路径在配置中心中是申请的系统使用地址,以确保值的唯一。 +- 之后,每个类对应的属性,需要映射出一个监听的节点。比如;Zookeeper 监听了 `/big-market-dcc/config` 那么类中 a 属性可以是 `/big-market-dcc/config/a` 这对这个路径设置的值,就可以被监听拿到了。 +- 最后,把获取到的监听值,通过 Java 反射操作,把值设置到对应的属性上。这样在 SpringBoot 应用程序中,使用某个类的属性值的时候,就可以动态的获取到变化的属性值了。 diff --git "a/docs/md/project/big-market/api/\347\254\2542\350\212\202\357\274\232\345\237\272\347\241\200\345\261\202\346\214\201\344\271\205\345\214\226\346\225\260\346\215\256.md" "b/docs/md/project/big-market/api/\347\254\2542\350\212\202\357\274\232\345\237\272\347\241\200\345\261\202\346\214\201\344\271\205\345\214\226\346\225\260\346\215\256.md" new file mode 100644 index 000000000..6b926eddc --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\2542\350\212\202\357\274\232\345\237\272\347\241\200\345\261\202\346\214\201\344\271\205\345\214\226\346\225\260\346\215\256.md" @@ -0,0 +1,32 @@ +--- +title: 第2节:基础层持久化数据 +pay: https://t.zsxq.com/15Jz9It6s +--- + +# 《大营销平台系统设计实现》 - 营销服务 第2节:基础层持久化数据 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:学会拉分支、合并、提交,完成 ORM 持久化数据库连接使用。并有一些idea编码技巧。 +- **课程视频**:[https://t.zsxq.com/15syTMVdU](https://t.zsxq.com/15syTMVdU) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在这一章节,我们把前面通过分析、设计的库表落到工程中编写,让工程可以对接到数据库。此外本节还有拉分支、合并、提交的处理。小伙伴在学习的过程也能积累这些技能的使用。 + +## 二、内容说明 + +本节小傅哥会带着通过开发 ORM 部分的基本操作,顺带着教会大家完成分支的拉取、合并、提交。 + +
+ +
+ +- 分支的拉取是非常重要的操作,千万不要一个 master 直接梭哈到底。尤其是很多伙伴,写代码的时候,一个功能验证通过,但后面的一堆流程开发后,代码跑不了了。也不知道从哪出的问题。 +- 持久化对象、mapper xml 编写,会有一些 idea 操作的技巧。另外像大厂开发中,并不希望研发通过自动生成的代码进行使用,因为自动生成会有很多无用的代码,有时候有些sql的操作,太通用性也不行,容易出事故。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\25430\350\212\202\357\274\232\345\210\206\345\270\203\345\274\217\345\212\250\346\200\201\351\231\220\346\265\201\345\222\214\347\206\224\346\226\255.md" "b/docs/md/project/big-market/api/\347\254\25430\350\212\202\357\274\232\345\210\206\345\270\203\345\274\217\345\212\250\346\200\201\351\231\220\346\265\201\345\222\214\347\206\224\346\226\255.md" new file mode 100644 index 000000000..2508c5038 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25430\350\212\202\357\274\232\345\210\206\345\270\203\345\274\217\345\212\250\346\200\201\351\231\220\346\265\201\345\222\214\347\206\224\346\226\255.md" @@ -0,0 +1,35 @@ +--- +title: 第30节:分布式动态限流和熔断 +pay: https://t.zsxq.com/8HkZf +--- + +# 《大营销平台系统设计实现》 - 营销服务 第30节:分布式动态限流和熔断 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:增加动态黑名单限流组件,通过访问动态限制将限流用户24H存入本地缓存,通过统一 Dcc 全局配置控制使用。 +- **课程视频**:[https://t.zsxq.com/Uklsj](https://t.zsxq.com/Uklsj) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +增加动态黑名单限流组件,通过访问动态限制将限流用户24H存入本地缓存,通过统一 Dcc 全局配置控制使用。并在方法上引入接口超时熔断组件。这两个东西都是分布式架构场景非常常用的手段。也经常在面试中提问,你的接口是如何保证可用性的,有什么手段对频繁访问的用户做出拦截处理。以及超时后的处理。 + +## 二、业务流程 + +如图,限流熔断组件实现方案; + +
+ +
+ +在上一节通过 Zookeeper 实现的动态配置中心 @DCC 服务下,对新增加 RateLimiter 动态限流黑名单服务提供控制管理。 + +- 首先,因为我们引入切面了,那么上一节 @DCC 直接获取类操作属性的梳理就要考虑代理类的存在了。因为这时候被切面管理的类,在 Spring 中是一个代理对象,而不是原始对象,所以本节还涉及了 DCC 代码的处理。 +- 之后,本节要增加的是 RateLimiter 限流,当一个用户频繁访问超过N次后,则会将这个用户加入黑名单列表,不允许在访问当前服务。直至过了超时时间从黑名单列表移走后才允许访问。 +- 另外,本节还引入了接口超时熔断组件。降级、熔断、限流,这也是一套分布式微服务非常重要的手段。 diff --git "a/docs/md/project/big-market/api/\347\254\25431\350\212\202\357\274\232\345\210\206\345\272\223\345\210\206\350\241\250\346\225\260\346\215\256\345\220\214\346\255\245ES.md" "b/docs/md/project/big-market/api/\347\254\25431\350\212\202\357\274\232\345\210\206\345\272\223\345\210\206\350\241\250\346\225\260\346\215\256\345\220\214\346\255\245ES.md" new file mode 100644 index 000000000..392bca082 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25431\350\212\202\357\274\232\345\210\206\345\272\223\345\210\206\350\241\250\346\225\260\346\215\256\345\220\214\346\255\245ES.md" @@ -0,0 +1,35 @@ +--- +title: 第31节:分库分表数据同步ES +pay: https://t.zsxq.com/wMTQn +--- + +# 《大营销平台系统设计实现》 - 营销服务 第31节:分库分表数据同步ES + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:为分库分表搭建 canal 数据同步 ElasticSearch 文件操作。 +- **课程视频**:[https://t.zsxq.com/5O0SI](https://t.zsxq.com/5O0SI) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +对于C端的场景来说,我们经常会采用分库分表的方案承载数据流量的压力,以用户ID为切分键,让用户的行为数据分散到各个库表中。那么在C端除了给用户提供数据服务以外,还需要给运营提供数据,但分库分表后的数据都已经散列到各个库表了,对于聚合查询就变得复杂,所以我们还需要另外一套方案。就是把分散在各个数据库表的数据,通过使用 canal 组件,基于 binlog 日志,把数据同步到 ElasticSearch 文件服务中再提供使用。 + +## 二、组件介绍 + +
+ +
+ +canal ,译为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。 + +早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。 + +它的工作原理是,canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议。在 MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal ) 这样 canal 再解析 binary log (binlog)进行配置分发,同步到 Elasticsearch 等系统中进行使用。 + +那么有了 canal 就可以把分库分表的数据同步到 Elasticsearch,提供汇总查询和聚合操作,也就不需要把轮训每个分库分表数据了。 diff --git "a/docs/md/project/big-market/api/\347\254\25432\350\212\202\357\274\232ES-ORM\345\244\232\346\225\260\346\215\256\346\272\220\351\205\215\347\275\256\344\275\277\347\224\250.md" "b/docs/md/project/big-market/api/\347\254\25432\350\212\202\357\274\232ES-ORM\345\244\232\346\225\260\346\215\256\346\272\220\351\205\215\347\275\256\344\275\277\347\224\250.md" new file mode 100644 index 000000000..e699a6219 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25432\350\212\202\357\274\232ES-ORM\345\244\232\346\225\260\346\215\256\346\272\220\351\205\215\347\275\256\344\275\277\347\224\250.md" @@ -0,0 +1,32 @@ +--- +title: 第32节:ES-ORM多数据源配置使用 +pay: https://t.zsxq.com/l032Y +--- + +# 《大营销平台系统设计实现》 - 营销服务 第32节:ES-ORM多数据源配置使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:为工程提供多数据源配置,可以同时支持操作 MySQL 和 ElasticSearch +- **课程视频**:[https://t.zsxq.com/q1RFi](https://t.zsxq.com/q1RFi) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +首先是目前已经在 MySQL 分库分表使用的基础上,加入了 canal 组件通过 binlog 日志把分库分表的数据同步 ElasticSearch 文件系统,那么接下来我们就需要让应用程序可以从 ElasticSearch 查询数据。也就是如何处理一个应用中多数据源的使用,同时要简化使用。另外因为工程的基础设施层越来越多内容了,我们要一点小的结构调整。 + +## 二、业务流程 + +让一套应用支持多套数据源使用; + +
+ +
+ +- 分库分表是对C端用户的,所有的C端行为一定是有用户ID的。 +- canal 同步 ElasticSearch 是为了给运营端做数据聚合查询的,一般这类的查询是不做核心业务的,因为同步是有时效性的。 diff --git "a/docs/md/project/big-market/api/\347\254\25433\350\212\202\357\274\232xxl-job\345\210\206\345\270\203\345\274\217\344\273\273\345\212\241\350\260\203\345\272\246.md" "b/docs/md/project/big-market/api/\347\254\25433\350\212\202\357\274\232xxl-job\345\210\206\345\270\203\345\274\217\344\273\273\345\212\241\350\260\203\345\272\246.md" new file mode 100644 index 000000000..b04153e22 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\25433\350\212\202\357\274\232xxl-job\345\210\206\345\270\203\345\274\217\344\273\273\345\212\241\350\260\203\345\272\246.md" @@ -0,0 +1,45 @@ +--- +title: 第33节:xxl-job分布式任务调度 +pay: https://t.zsxq.com/nOA6K +--- + +# 《大营销平台系统设计实现》 - 营销服务 第33节:xxl-job分布式任务调度 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:为工程提供多数据源配置,可以同时支持操作 MySQL 和 ElasticSearch +- **课程视频**:[https://t.zsxq.com/N2eo4](https://t.zsxq.com/N2eo4) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 零、优化调整 + +- fix-240807-xfg-clear-queue-by-sku 分支增加了指定sku的使用,而不是使用一个队列。 +- fix-240608-xfg-decr-zero 优化扣减中0的对比。 +- feat-240809-xfg-zookeeper-enable 增加可对 Zookeeper 关闭的处理,在 yml 中配置(zookeeper.sdk.config.enable)。便于大家在测试时快速启动。 +- dubbo yml 配置,修改为 N/A 屏蔽掉 nacos 注册,便于本地快速测试。 + +## 一、本章诉求 + +增加 xxl-job 分布式任务调度服务,处理大营销中;`发送MQ消息任务队列`、`更新活动sku库存任务`、`更新奖品库存任务`定时任务。同时因为整个大营销是分布式部署,一套 big-market 会被多个应用实例一起部署,那么就会有多个实例上相同的一个任务要执行,这个时候需要增加抢占式锁,避免造成重复执行。重复执行可能导致无效的扫库或者重复发送MQ消息。 + +## 二、业务流程 + +通过xxl-job管理分布式应用任务; + +
+ +
+ +XXL-JOB 是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。 + +- big-market 01、big-market 02、big-market 03,举例分布式部署,也可以是更多的部署实例。 +- 每个应用实例都是相同的任务,这些任务增加了 redis 分布式锁抢占,避免所有任务都被同时执行。加了锁以后,同一时间只能一个实例上执行任务。 +- 但不能为了只有一个执行而部署一套,部署多套的用途是为了互备,如果一个挂了还有其他的任务可以执行。这个就是分布式架构高可用的设计思路。 + +>**对 xxl-job 学习**:[https://bugstack.cn/md/road-map/quartz.html](https://bugstack.cn/md/road-map/quartz.html) - 小白伙伴可以看下这套内容补充学习任务调度,包括;环境安装、执行器管理、任务配置等。 + diff --git "a/docs/md/project/big-market/api/\347\254\2543\350\212\202\357\274\232\347\255\226\347\225\245\346\246\202\347\216\207\350\243\205\351\205\215\345\244\204\347\220\206.md" "b/docs/md/project/big-market/api/\347\254\2543\350\212\202\357\274\232\347\255\226\347\225\245\346\246\202\347\216\207\350\243\205\351\205\215\345\244\204\347\220\206.md" new file mode 100644 index 000000000..5365cbbbb --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\2543\350\212\202\357\274\232\347\255\226\347\225\245\346\246\202\347\216\207\350\243\205\351\205\215\345\244\204\347\220\206.md" @@ -0,0 +1,44 @@ +--- +title: 第3节:策略概率装配处理 +pay: https://t.zsxq.com/153HMmsaI +--- + +# 《大营销平台系统设计实现》 - 营销服务 第3节:策略概率装配处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:通过策略ID检索数据库策略配置,并根据策略配置的概率计算出占比值,初始化到 Redis 服务中。完成简单的随机抽奖。 +- **课程视频**:[https://t.zsxq.com/15px0hyMb](https://t.zsxq.com/15px0hyMb) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +从这一章节开始,小傅哥会带着大家正式进行需求的开发实现。我们会先梳理出业务流程,并不断的实现各个模块流程的功能。这包括本节要实现的抽奖策略的装配,这个过程中会需要用到数据库查询、策略值计算、Redis Map 数据存储。也会在这个过程中体现出 DDD 分层架构下的开发实现方式。 + +## 二、需求介绍 + +### 1. 流程梳理 + +以用户为视角下,先进行整个流程的梳理。【如图】 + +
+ +
+ +整个过程会包括;抽奖策略、策略奖品、策略规则、奖品发放这些核心流程模块的使用。大家在看这个图的时候,可以配合着库表进行思考。在本节小傅哥会带着大家先实现抽奖策略的装配,用于后续抽奖时进行使用。 + +### 2. 算法说明 + +两种抽奖算法方式; + +
+ +
+ +- 可以根据概率值,来创建出累加的范围。如A是占10个,B的范围就是从10+40到50个,就是B。依次类推。当抽奖活动的随机值,就可以在这些区间内循环对比。 +- 另外一种是存放到Map中,用空间换时间。这样在抽奖的时候,把随机值当索引使用,可以直接获取到对应的奖品结果。本节我们来实现第二种方式。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\2544\350\212\202\357\274\232\347\255\226\347\225\245\346\235\203\351\207\215\346\246\202\347\216\207\350\243\205\351\205\215.md" "b/docs/md/project/big-market/api/\347\254\2544\350\212\202\357\274\232\347\255\226\347\225\245\346\235\203\351\207\215\346\246\202\347\216\207\350\243\205\351\205\215.md" new file mode 100644 index 000000000..2f6a4a712 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\2544\350\212\202\357\274\232\347\255\226\347\225\245\346\235\203\351\207\215\346\246\202\347\216\207\350\243\205\351\205\215.md" @@ -0,0 +1,33 @@ +--- +title: 第4节:策略权重概率装配 +pay: https://t.zsxq.com/15sgc1lE4 +--- + +# 《大营销平台系统设计实现》 - 营销服务 第4节:策略权重概率装配 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:增强抽奖策略装配,实现权重策略的处理。用于满足抽奖中支持不同阶梯所能抽奖范围的处理。 +- **课程视频**:[https://t.zsxq.com/15oWhbN0c](https://t.zsxq.com/15oWhbN0c) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在大营销平台的抽奖子模块中,需要满足用户抽奖N积分后,可中奖范围的设定。也就是说你总共消耗了6000积分抽奖了,那么接下来的抽奖就会有圈定到固定的奖品范围,不会让用户再抽到过低价值的奖品。那么这就需要我们在设计系统实现的时候,处理下不同策略规则权重的概率装配。 + +## 二、流程设计 + +在流程实现中,我们需要结合上一节中的整体概率装配,按照接口单一原则进行拆分出装配和使用。之后在装配接口中重构装配操作,满足对权重策略的装配处理。—— 这里会在实体对象中填充充血方法。 + +
+ +
+ +- 集合着梳理的系统设计流程,将后续需要用到的权重抽奖规则,进行提前装配处理。 +- 所有装配的数据都会存放到 Redis Map 数据结构下。对于权重的策略装配为策略ID+权重值组合。 +- 最终用户在从装配的工厂中执行抽奖的时候,则可以通过`策略ID抽奖`和`策略ID+权重值组合`的方式抽奖。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\2545\350\212\202\357\274\232\346\212\275\345\245\226\345\211\215\347\275\256\350\247\204\345\210\231\350\277\207\346\273\244.md" "b/docs/md/project/big-market/api/\347\254\2545\350\212\202\357\274\232\346\212\275\345\245\226\345\211\215\347\275\256\350\247\204\345\210\231\350\277\207\346\273\244.md" new file mode 100644 index 000000000..ba7e9271b --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\2545\350\212\202\357\274\232\346\212\275\345\245\226\345\211\215\347\275\256\350\247\204\345\210\231\350\277\207\346\273\244.md" @@ -0,0 +1,34 @@ +--- +title: 第5节:抽奖前置规则过滤 +pay: https://t.zsxq.com/16kDhiEHg +--- + +# 《大营销平台系统设计实现》 - 营销服务 第5节:抽奖前置规则过滤 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:实现抽奖前置权重和黑名单规则,在用户抽奖前进行规则过滤。本节会使用到模板模式、策略模式、工厂模式,来实现功能。 +- **课程视频**:[https://t.zsxq.com/16dJZhoR0](https://t.zsxq.com/16dJZhoR0) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在我们的流程设计中,用户执行抽奖时会判断是否已经超过N积分,如果超过N积分则可以在限定范围内进行抽奖。同时如果用户是黑名单范围的羊毛党用户,则只返回固定的奖品ID。 + +这一章节的实现中则会涉及到工厂和策略定义出来的规则模型,并满足后续的规则扩展。再通模板模式定义出抽奖的基本过程,来使用抽奖规则。 + +## 二、流程设计 + +在流程实现中,我们需要按照规则和抽奖,两个部分的功能进行边界拆分。这样可以满足后续功能的扩展添加,不至于影响全部流程的。 + +
+ +
+ +- 整个规则来说,分为抽奖前、抽奖中、抽奖后,三个阶段执行。本节我们先来处理抽奖前的规则。 +- 在工程分包上,需要添加 rule 来处理抽奖规则,在添加 raffle 处理抽奖过程。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\2546\350\212\202\357\274\232\346\212\275\345\245\226\345\220\216\347\275\256\350\247\204\345\210\231\350\277\207\346\273\244.md" "b/docs/md/project/big-market/api/\347\254\2546\350\212\202\357\274\232\346\212\275\345\245\226\345\220\216\347\275\256\350\247\204\345\210\231\350\277\207\346\273\244.md" new file mode 100644 index 000000000..3d6a4a4e1 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\2546\350\212\202\357\274\232\346\212\275\345\245\226\345\220\216\347\275\256\350\247\204\345\210\231\350\277\207\346\273\244.md" @@ -0,0 +1,37 @@ +--- +title: 第6节:抽奖中置规则过滤 +pay: https://t.zsxq.com/16URAPPW6 +--- + +# 《大营销平台系统设计实现》 - 营销服务 第6节:抽奖中置规则过滤 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:添加抽奖中规则过滤,主要为处理用户抽中某个奖品后,但该奖品配置有已抽奖n后解锁的限制条件。 +- **课程视频**:[https://t.zsxq.com/16m3EJA0G](https://t.zsxq.com/16m3EJA0G) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在实际的公司级别的项目中,抽奖是一个非常复杂的营销活动过程,尤其是类似拼多多这样的电商平台,对于抽奖的场景应用更是多样。 + +一般产品在运营一段时间抽奖系统后,就会思考🤔怎么能刺激用户更多的消耗手里的积分。频次更高的参与抽奖。这个时候产品就会从抽奖的行为上下手,比如;一个用户最开始前三次抽奖,能抽奖的范围为1-6个奖品,当抽奖3次后解锁1-7、抽奖6次后解锁1-9这样一个处理。那么用户已经抽奖一次,就会想着在抽奖下好增加抽奖奖品的范围。从而更多的消耗用户积分。 + +所以我们在设计抽奖的系统的时候,要时刻记住松耦合。就像 Spring 源码中拆解一个 Bean 对象为不同阶段一样,我们这里也把抽奖拆解为不同时间段的过程,以用于可以在各个节点添加所需的功能流程。这样的设计也就更加便于后续的功能迭代了。否则一些小卡拉米的设计,会导致每次需求迭代山崩地裂一样。 + +## 二、流程设计 + +在流程实现中,设计出抽奖的前中后置过程,并在每个阶段设计对应的操作规则。当你这样设计以后,你会发现整个抽奖功能实现变得非常灵活好扩展。 + +
+ +
+ +- 在上一节中,小傅哥已经带着大家实现了规则实现的框架,本节可以继续在已有的规则模型结构加,添加新的规则。不过我们需要标记出规则的前中后标记【DefaultLogicFactory#LogicModel】,便于使用。 +- 本节扩展如图黄色部分「次数过滤」,这个规则的作用是为任何一个奖品配置抽奖抽奖n次后解锁的操作。这个配置就是图中数据库内的配置。 +- 后续在实现图中抽奖后的规则处理。 diff --git "a/docs/md/project/big-market/api/\347\254\2547\350\212\202\357\274\232\350\264\243\344\273\273\351\223\276\346\250\241\345\274\217\345\244\204\347\220\206\346\212\275\345\245\226\350\247\204\345\210\231.md" "b/docs/md/project/big-market/api/\347\254\2547\350\212\202\357\274\232\350\264\243\344\273\273\351\223\276\346\250\241\345\274\217\345\244\204\347\220\206\346\212\275\345\245\226\350\247\204\345\210\231.md" new file mode 100644 index 000000000..0da29fdf8 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\2547\350\212\202\357\274\232\350\264\243\344\273\273\351\223\276\346\250\241\345\274\217\345\244\204\347\220\206\346\212\275\345\245\226\350\247\204\345\210\231.md" @@ -0,0 +1,33 @@ +--- +title: 第7节:责任链模式处理抽奖规则 +pay: https://t.zsxq.com/16bYtiXK2 +--- + +# 《大营销平台系统设计实现》 - 营销服务 第7节:责任链模式处理抽奖规则 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:通过责任链模式解耦抽奖规则中的前置规则,让代码流程实现更加清晰。因为黑名单、权重、默认抽奖,以及以后添加的各类抽奖方式。 +- **课程视频**:[https://t.zsxq.com/162mRCX5q](https://t.zsxq.com/162mRCX5q) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在前面的章节中,我们运用了策略模式、工厂模式、模板模式,来完成抽奖流程的定义和抽奖过程前、中、后,规则的过滤处理。但在我们规则处理的流程中,因为前置规则的校验含带了抽奖的行为处理,这样绑定到规则流程实现中会显得有些臃肿,让规则负责的事情变得更多。所以在本节小傅哥会带着大家使用责任链模式进行优化完善,让整个代码流程变得更加清晰。 + +## 二、流程设计 + +设计前我们需要思考🤔 ,抽奖的前置规则在抽奖中是一个什么行为。其实它可以被抽象为一种策略行为,比如;黑名单抽奖策略、权重抽奖策略、白名单抽奖策略等。而这些策略规则是一种互斥行为,比如走了黑名单规则,就不应该在继续走权重规则了。那么对于这样的情况,责任链的设计就更加合适了。 + +
+ +
+ +- 抽象原有的抽奖前规则,为责任链处理。 +- 责任链会顺序的将责任节点,通过责任链工厂,从库中读取的到的责任节点进行顺序填充到责任链上。 +- 注意;这样的工厂方式可以更好的根据不同的策略创建出所需的责任链。属于责任链 + 工厂的组合编写方式。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\2548\350\212\202\357\274\232\346\212\275\345\245\226\350\247\204\345\210\231\346\240\221\346\250\241\345\236\213\347\273\223\346\236\204\350\256\276\350\256\241.md" "b/docs/md/project/big-market/api/\347\254\2548\350\212\202\357\274\232\346\212\275\345\245\226\350\247\204\345\210\231\346\240\221\346\250\241\345\236\213\347\273\223\346\236\204\350\256\276\350\256\241.md" new file mode 100644 index 000000000..abea46e07 --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\2548\350\212\202\357\274\232\346\212\275\345\245\226\350\247\204\345\210\231\346\240\221\346\250\241\345\236\213\347\273\223\346\236\204\350\256\276\350\256\241.md" @@ -0,0 +1,37 @@ +--- +title: 第8节:抽奖规则树模型结构设计 +pay: https://t.zsxq.com/16IWY84jr +--- + +# 《大营销平台系统设计实现》 - 营销服务 第8节:抽奖规则树模型结构设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:针对抽奖中到抽奖后的规则衔接过滤的多样性,使用组合模式的规则树结构设计,根据配置自动完成流程调用。 +- **课程视频**:[https://t.zsxq.com/16ICK8eeS](https://t.zsxq.com/16ICK8eeS) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +本章节需要引入新的设计模式结构,解决先阶段中抽奖策略规则的中、后两部分执行问题。通过组合模式的规则引擎,让过滤节点可以满足一颗二叉树的结构,自由的组合和多分支链路的方式完成流程的处理。 + +## 二、流程设计 + +这里有一个矛盾点需要解决。对于抽奖策略的前置规则过滤是顺序一条链的,有一个成功就可以返回。比如;黑名单抽奖、权重人群抽奖、默认抽奖,总之它只能有一种情况,所以这样的流程是适合责任链的。 + +
+ +
+ +那么对于抽奖中到抽奖后的规则,它是一个非多分支情况的规则过滤。单独的责任链是不能满足的,如果是拆分开抽奖中规则和抽奖后规则分阶段处理,中间单独写逻辑处理库存操作。那么是可以实现的。但这样的方式始终不够优雅,配置化的内容较低,后续的规则开发仍需要在代码上改造。所以这里小傅哥会带着大家实现一版组合模式的决策树模型设计。 + +
+ +
+ +注意:设计模式是思想,就和学武术练太极一样,理论和实践是两回事。招式要随着场景因地制宜的变化,而不是生搬硬套一些设计模式的案例学习。 \ No newline at end of file diff --git "a/docs/md/project/big-market/api/\347\254\2549\350\212\202\357\274\232\346\250\241\346\235\277\346\250\241\345\274\217\344\270\262\350\201\224\346\212\275\345\245\226\350\247\204\345\210\231.md" "b/docs/md/project/big-market/api/\347\254\2549\350\212\202\357\274\232\346\250\241\346\235\277\346\250\241\345\274\217\344\270\262\350\201\224\346\212\275\345\245\226\350\247\204\345\210\231.md" new file mode 100644 index 000000000..7a4b3268f --- /dev/null +++ "b/docs/md/project/big-market/api/\347\254\2549\350\212\202\357\274\232\346\250\241\346\235\277\346\250\241\345\274\217\344\270\262\350\201\224\346\212\275\345\245\226\350\247\204\345\210\231.md" @@ -0,0 +1,34 @@ +--- +title: 第9节:模板模式串联抽奖规则 +pay: https://t.zsxq.com/16kAD9g7p +--- + +# 《大营销平台系统设计实现》 - 营销服务 第9节:模板模式串联抽奖规则 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:通过模板模式,整合责任链、规则树,定义出抽奖的标准过程。以及让子类做具体调用功能实现。 +- **课程视频**:[https://t.zsxq.com/xWjte](https://t.zsxq.com/xWjte) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +通过模板模式,把上一节中规则树的结构设计,整合到抽奖过程中。这样整个抽奖策略过程会包括;责任链进行抽奖计算,基于抽奖计算结果对基础抽奖在进行规则树的过滤,最终返回抽奖结果。 + +注意:上一节是做了抽奖规则树的领域模型设计,基于这部分内容的实现本节需要把规则树和数据库关联起来 + +## 二、流程设计 + +业务项目从不是一锤子买卖,而是长周期的软件迭代。所以如果在代码中有非常多的只是配合现在功能实现的代码,那么后续的迭代会非常麻烦。所以我们在这样的功能实现时,是需要非常多的设计考虑,尽可能的拆解出稳定的结构,对于变化的内容从库表配置中读取。 + +
+ +
+ +- 左侧的业务流程,可以通过抽象类定义的出调用顺序,右侧的具体操作可以放到实现了抽象类的子类来做具体实现。通过这样的方式,我们在后续看代码的时候,也能直接通过抽象类的模板结构直接知道这块的代码在做什么。而要看细节则进入到每个功能实现里去。 +- **重点**:学习编程,要从编码对业务的实现中,提炼出模型结构。这些通用的模型结构可以解决很多同类的问题。 diff --git a/docs/md/project/big-market/big-market.md b/docs/md/project/big-market/big-market.md new file mode 100644 index 000000000..1426052cb --- /dev/null +++ b/docs/md/project/big-market/big-market.md @@ -0,0 +1,153 @@ +--- +title: 大营销平台系统 +lock: no +--- + +# 《大营销平台系统》—— 小傅哥第8个项目,前后端 + Dev-Ops 的全栈式综合编程实战DDD项目! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/14gswKIeX](https://t.zsxq.com/14gswKIeX) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +💐又到了启动新项目的时候,死鬼开心嘛。小傅哥的**星球:码农会锁**,第8个应用级实战项目开启啦!—— 在这之前小傅哥已经完结了7个实战项目,可进入 [https://gaga.plus](https://gaga.plus) 嘎嘎强平台,体验项目。 + +
+ +
+ +呐,接下来,小傅哥要带着大家做一个什么项目呢?🤔 + +这个新项目,结合小傅哥已经带着大家完成的 OpenAi 大模型应用业务场景,做上层的营销活动。这就像互联网公司中有了电商、外卖、出行等场景一样,在场景之上做营销活动。所以我们的新项目是 **《大营销平台系统》**!因为小傅哥的星球之前做过了一个抽奖,那么这个项目会用新的DDD架构,对抽奖系统进行重构,并扩展出`营销账户`、`用户返利`、`积分兑换`等服务,完成一整套的营销平台功能。💥 + +小傅哥把互联网中真实的场景、架构、实现,拿出来让你成体系化的学习; + +
+ +
+ +**体验地址**:[https://openai.gaga.plus/](https://openai.gaga.plus/) - 左侧Bar,抽奖进入 + +这里抽奖模块通过RPC接口,对接到大营销平台。这里不只是抽奖,还要串联账户、奖品、返利等各项内容。接下来,小傅哥就着重介绍下这套信息项目的重点,让大家可以知道学习到哪些知识,掌握哪些技术。 + +>文末有加入学习方式,还有优惠券可以使用。先到先得! + +## 一、能学到啥 + +在各大互联网公司中,营销平台都是那个流量最大,场景最复杂的系统,也是需求迭代最多还最快系统。在这个部门的研发伙伴,谁身上都是背着“几个事故”锻炼出来的技术。所以,跟着小傅哥学习这样一套系统,是可以学习到非常多的技术。包括;—— `以往的学习,你可能有很多技术栈使用的缺失,甚至也没接触过有高质量的架构和设计模式编码。那么在折腾学习旅程中,这些内容你都将学到。` + +
+ +
+ +- 【前端】熟练使用 React、Typescript 在前端工程中开发营销活动页。 +- 【前端】熟练掌握,跨域接口请求,以及通过浏览器指纹技术实现防刷。 +- 【前端】熟练使用,Ant Design Pro 开发后台运营管理系统。 +- 【后端】熟练搭建 DDD 工程项目、以及 DDD 脚手架搭建项目。并对 DDD 设计方法有清楚的认知。 +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对框架源码所提供的扩展接口具备运用能力。 +- 【后端】熟练运用分布式技术栈,包括:Dubbo、RocketMQ、Redis、XXL-JOB、Sharding-JDBC、Nacos等。 +- 【后端】熟练使用多种设计模式、设计原则,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。 +- 【后端】深度学习复杂场景的架构设计、编程思维,如果处理系统功能的边界和上下文的维护。—— 这些东西一定是从实践中才能学习到的。 +- 【后端】熟练使用 Mock 单测工具、JMeter 压测工具,增强代码交付质量。 +- 【后端】熟练掌握异常、枚举、错误码的定义和使用,并学习到如何合理打印服务日志,便于问题排查。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练使用 Nginx 配置转发服务,并能申请ssl配置https服务。 +- 【运维】熟练使用 Grafana 监控系统,对系统的 JVM、磁盘、Tomcat、应用(QPS、响应时间、调用量)完整监控。 + +此外,小傅哥会把系统开发过程中的思考、设计、编码,录制⏺成完整的视频,让大家可以学习到的更多、更细、更深! + +>像这样的营销复杂场景项目,势必会引入相关分布式技术栈的使用。并且营销会根据业务流程拆解出对应的微服务系统,包括;券、活动、拉新、抽奖、积分、兑换、灌券、返利等各个平台,这些微服务间通过 RPC 进行通信。又使用 MQ 解耦、任务补偿,以确保微服务内事务一致性,微服务外最终一致性。 + +## 二、项目介绍 + +本次项目是一个包括 `前后端 + Dev-Ops` 的全栈式综合编程实战项目,基于 React + SpringBoot + 分布式技术栈 + Nginx + Grafana + Docker 云服务,开发、部署、上线、监控的《大营销平台系统》项目。 + +大营销平台项目属于营销组最为核心的项目,承接流量最大、系统设计最复杂、需求迭代最多,也是最容易出事故的组。🤨**在这个组1年的技术成长 = 其他组3年!** + +营销组的项目是最早触达用户的,打开页面、优惠选券、组合支付、积分活动、分享收益、拉新返现等,都是先进入营销系统,完成相关的动作才是商品的选择和支付。你几乎能在购物、出行、金融、音视频娱乐中,都有对应的营销玩法活动。所以这块系统`流量最大`、`需求最多`、`功能最复杂`!—— **每个公司都有营销组在加班!💐** + +抖音、京东、滴滴、拼多多,都有这样的项目; + +
+ +
+ +虽然需求杂,需求多,但这样部门组里的项目是非常锻炼人的。所以小傅哥这次也开启了一个 **《大营销平台项目》** 带着你一起学习复杂的场景架构设计和高级编码落地经验。完全从0到1,手把手的带着你思考、设计、编码,完成项目! + + +### 1. 业务核心流程 + +
+ +
+ + +### 2. 模型设计高质量 + +
+ +
+ +### 3. 系统工程规范化 + +
+ +
+ +### 4. 单一职责抽象化 + +
+ +
+ +
+ +
+ +### 5. 设计模式场景化 + +
+ +
+ +### 6. 项目工程列表 + +
+ +
+ +### 7. 业务监控示例 + +
+ +
+ +- 目前是 OpenAi 业务系统监控,大营销平台系统上线后,会把这部分监控一起添加上。 +- 项目,只有上线 。你才会注意到很多的细节,就像 Tomcat 的最大连接数,如果不开发超时熔断,在接口异常超时等待的情况,就有可能把连接数打满。 + +## 三、项目大纲 + +不同于网上的小Demo项目,这个项目的场景来自于互联网真实业务需求,一个个章节、一步步流程的带着大家从0到1,需求分析、工程设计和代码实现。是一个纯手把手教大家学习实战技术的项目! + +
+ +
+ +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! + +## 四、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:OpenAi大模型应用项目、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +>这样一套项目,放在一些平台售卖,一个至少都是几百上千。但小傅哥的星球,只需要100多,就可以获得全部的学习项目 [https://gaga.plus](https://gaga.plus)! + +[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) **加入星球**:下载`星球APP`,从星球【课程入口】进入。里面有完整的学习指引,包括;使用说明、代码仓库、专属项目群、学习路线、往期项目。 + diff --git a/docs/md/project/big-market/ddd.md b/docs/md/project/big-market/ddd.md new file mode 100644 index 000000000..c761a139c --- /dev/null +++ b/docs/md/project/big-market/ddd.md @@ -0,0 +1,216 @@ +--- +title: 架构:DDD 领域驱动设计 +lock: no +--- + +# 架构:DDD 领域驱动设计,战略、战术、战役,落地指引规范。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/14gswKIeX](https://t.zsxq.com/14gswKIeX) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +👨🏻‍💻 经过5.1假期的一顿框框输出,终于完成了[《大营销项目》](https://bugstack.cn/md/project/big-market/big-market.html)第二阶段的开发和上线,体验地址:[https://gaga.plus](https://gaga.plus) 有了这个项目的落地,也终于可以给大家完整的梳理出一套 DDD 落地指引规范。包括;战略、战术、战役,各个阶段都要做什么,`怎么做风暴模型和四色建模`。有了这套东西参考,小白也能目标明确的做 DDD 项目开发啦! + +
+ +
+ +**我一直讲,要先实践,再理论!** + +编程,偏理科的东西要先上手实践,再做理论理解。因为所有的理论提出,也都是建立有了实践结果后,抽象出来的理论。但你上来就要用理论去反推结果,并不是一件容易的事情。就像不少的 DDD 文章,往往会用一个理论,去讲另外一个理论,这也导致很多没有实践过的小白伙伴,压根不知道讲的是什么。最终觉得 DDD 太难! + +所以在近2年的时间里,小傅哥分享了非常多的 DDD 实践内容,包括;DDD 工程结构&脚手架、各项分布式技术栈在 DDD 结构下的使用、DDD 实战项目&小场景训练。这些内容都可以在 [bugstack.cn](https://bugstack.cn) 学习。 + +接下来,小傅哥会带着你走一遍`研发设计评审`,讲解 DDD 落地项目的全部过程。 + +>文末有对应本项目的 DDD 工程代码地址,理论 + 代码 + 视频,学的嘎嘎透彻! + +## 一、战略、战术、战役 + +首先 DDD 是一种软件设计方法,[Domain-driven design (DDD) is a major software design approach.](https://en.wikipedia.org/wiki/Domain-driven_design) 来自维基百科。软件设计方法涵盖了;范式、模型、框架、方法论,主要活动包括建模、测试、工程、开发、部署、维护。来自维基百科的[软件设计](https://en.wikipedia.org/wiki/Software_design)涵盖信息介绍。 + +在 DDD 领域驱动设计中,常提到`战略`、`战术`,和一少部分会讲到`战役`。这3个词主要讲的是不同的开发阶段所需要完成的事项; + +- 战略 - 建模;领域划分、界限上下文、核心领域 +- 战术 - 架构;工程结构、领域对象、领域服务、领域事件 +- 战役 - 编码;设计原则、设计模式 + +DDD 的战略、战术和战役设计相辅相成,战略提供系统的建模作为宏观指导,战术下面有N个战役,两者则关注具体的实现和编码落地。 + +在维基百科中有不少 DDD 非常好的资料,其中一个是关于事件风暴的,讲解了执行战略设计中风暴模型的步骤。 + +
+ +
+ +>有了这基础认知,接下来我们通过《大营销项目》从需求到设计,一步步了解系统的领域驱动设计。 + +## 二、产品需求 + +### 1. 产品诉求 + +如图,是一个复杂的营销抽奖场景玩法需求,涵盖了;`活动配置`、`签到&奖励`、`活动账户`、`抽奖策略「责任链+规则树」`、`库存扣减`、`抽奖满N次后阶梯抽奖`等。面对这样的复杂系统,非常适合使用 DDD 落地。 + +
+ +
+ +分析需求; + +1. 整体概率相加,总和为1或者分值计算,概率范围千分位 +2. 抽奖为免费抽奖次数 + 用户消耗个人积分抽奖 +3. 抽奖活动可给用户分配可抽奖次数,通过点击签到发放 +4. 活动延伸配置用户库存消耗管理,单独提供表配置各类库存 + 用户可用总库存、用户可用日库存 +5. 部分抽奖规则,需要抽奖n次后解锁,才能有机会抽取 +6. 抽奖完成增加(运气值/积分值/抽奖次数)记录,让用户获得奖品。 +7. 奖品对接,自身的积分、内部系统的奖品 +8. 随机积分,发给你积分。 +9. 黑名单用户抽奖,则给予固定的奖品。 + +### 2. 业务流程 + +依照于产品需求,在产品的 PRD 文档中还会绘制出业务流程图。产品的流程图会比较粗一些,研发后期需要根据产品的 PRD 文档做具体的设计。 + +
+ +
+ +- 产品经理会详细的介绍整个系统的功能流程和需要对接接口文档。 +- 以上就是以用户旅程为维度,从点击签到获得活动账户额度,再到一些列抽奖、抽奖策略、中奖结果和奖品发放的流程。 + +## 三、系统架构 + +如果首次承接的是一个新的系统,还需要对系统进行架构设计,是单体架构还是分布式架构,以及所要用到的技术栈。最好在提供好相关的落地案例和DDD脚手架。—— 没有这些东西,就想说点理论,就让团队用DDD写代码,那就是天方夜谭!*你都没写出DDD代码,兄弟👬🏻哪里去复制!* + +**资料**:—— 详细介绍了 DDD 落地的案例和通用的脚手架。 +- DDD 架构:[https://bugstack.cn/md/road-map/ddd.html](https://bugstack.cn/md/road-map/ddd.html) +- MVC2DDD:[https://bugstack.cn/md/road-map/mvc2ddd.html](https://bugstack.cn/md/road-map/mvc2ddd.html) +- DDD 脚手架:[https://bugstack.cn/md/road-map/ddd-archetype-maven.html](https://bugstack.cn/md/road-map/ddd-archetype-maven.html) + +### 1. 分布式架构 + +
+ +
+ +### 2. 分布式技术 + +
+ +
+ +## 四、战略设计 + +不少伙伴,都讲过不知道怎么开始 DDD,主要是拿到一个需求,不知道从哪下手,也不知道那些领域的模型是怎么弄出来的。好,这次小傅哥就给你整个完整的案例,告诉你如何开始。 + +### 1. 用例图 + +根据业务需求画系统用例图; + +
+ +
+ +- 用例图(英语:use case diagram)是用户与系统交互的最简表示形式,展现了用户和与他相关的用例之间的关系。通过用例图,人们可以获知系统不同种类的用户和用例。用例图也经常和其他图表配合使用。 +- 用例图,也可以等同于是用户故事(英语:User story)(软件开发和项目管理中的常用术语),主旨是以日常语言或商务用语撰写句子,是一段简单的功能表述。以客户或使用者的观点撰写下有价值的功能、引导、框架来与使用者进行互动,进而推动工作进程。可以被认为是一种规格文件,但更精确而言,它代表客户的需求与方向。以该用户故事来反应对象在组织内的其工作职责、范围、需要进行的任务等。用户故事在敏捷开发方法中用来定义系统需要提供的功能和实现需求管理。 +- 尽管用例本身会涉及大量细节和各种可能性,用例图却能提纲挈领地让人了解系统概况。它为“系统做什么”提供了简化了的图形表示,因此被誉为“搭建系统的蓝图”。 + +### 2. 事件风暴定义 + +在使用 DDD 的标准对系统建模前,一堆人要先了解 DDD 的操作手段,这样才能让产品、研发、测试、运营等了解业务的伙伴,都能在同一个语言下完成系统建模。 + +
+ +
+ +- 蓝色 - 决策命令,是用户发起的行为动作,如;开始签到、开始抽奖、查看额度等。 +- 黄色 - 领域事件,过去时态描述。如;签到完成、抽奖完成、奖品发放完成。它所阐述的都是这个领域要完成的终态。 +- 粉色 - 外部系统,如你的系统需要调用外部的接口完成流程。 +- 红色 - 业务流程,用于串联决策命令到领域事件,所实现的业务流程。一些简单的场景则直接有决策命令到领域事件就可以了。 +- 绿色 - 只读模型,做一些读取数据的动作,没有写库的操作。 +- 棕色 - 领域对象,每个决策命令的发起,都是含有一个对应的领域对象。 + +**👩🏻‍🏫敲黑板** 综上,左下角的示意图。就是一个用户,通过一个策略命令,使用领域对象,通过业务流程,完成2个领域事件,调用1次外部接口个过程。我们在整个 DDD 建模过程中,就是在寻找这些节点。 + +### 3. 寻找领域事件 + +接下来,大量的时间,都是在挖掘领域事件。这个过程就是一堆人头脑风暴的过程,避免错失流程节点。 + +
+ +
+ +- 根据产品 PRD 文档,一起开会梳理有哪些领域事件。其实大多数领域事件一个人都可以想到,只是有些部分小的场景和将来可能产生的事件不一定覆盖全。所以要通过产品、测试、以及团队的架构师,一起讨论。 +- 像是整个大营销的抽奖会包括如图所列举的事件。在列举这个阶段,你用在乎格式。也可以是每个人准备好黄色便签纸,想到一个就贴到黑板上一个,只是穷举完成。—— 实际做DDD中,也是这样用便签纸贴黑板,所以用不同的颜色做区分。 + +### 4. 识别领域角色和对象 + +在确定了领域事件以后,接下来要做的就是通过决策命令串联领域事件,并填充上所需要的领域对象。这块的操作,新手可以分开处理,如先给领域事件添加决策命令、执行用户和领域对象,最后在串联流程。就像 `事件风暴定义` 中的示意一样。 + +
+ +
+ +- 首先,通过用户的行为动作,也就是决策命令,串联到对应的领域事件上。并对复杂的流程提供出红色的业务流程。 +- 之后,为决策命令添加领域对象,每一个领域在整个流程中都起到了至关重要的作用。 + +### 5. 划分领域边界 + +有了识别出来的领域角色的流程,就可以非常容易的划分出领域边界了。先在事件风暴图上圈出领域边界,之后在单独提供领域划分。 + +#### 5.1 圈出领域 + +
+ +
+ +#### 5.2 领域边界 + +
+ +
+ +- 到这步咱们就可以获得整个项目中 DDD 的领域边界划分了。之后再往下就是具体的每个领域对象的详细设计和流程设计。 + +### 6. 研发详细设计 + +#### 6.1 实体对象 + +
+ +
+ +- 你需要对每一个领域对象进行字段的详细设计。并划分出它们的上下文关系。一般在公司中,这部分设计完成,其他人也能对照你的设计进行代码开发。 + +#### 6.2 流程设计 + +
+ +
+ +- 流程设计,就是更详细的设计了。每一步要调用到哪个系统,哪个接口,要执行什么动作就全部都有了。 + +## 五、工程实现 + +DDD 的战略设计做完,划分出领域边界以后。接下来就是要执行战术和战役了。也就是在工程中做编码实现。但一定要懂得设计原则和设计模式,否则写不出好的代码的。 + +
+ +
+ +- 工程实现,就是在确定的框架结构中编码。可以是洋葱架构、整洁架构、菱形架构等等。这部分内容的可以通过实战项目来锻炼,获得编码技巧。 + +## 六、实战项目 + +注意📢,加入小傅哥的【星球:码农会锁】即可获得大营销项目学习,此外还包括:OpenAI 应用、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。 + +提示🔓,加入星球即可解锁全部项目,后续的新项目也可以学习。这些内容非常适合提高编程思维和编码能力。与小demo项目不同,这些内容的积累都是面试中的利器! + +> [🧧加入](https://t.zsxq.com/14gswKIeX) 这样成体系的全量项目学习,放在一些平台售卖,至少都要上千块。但小傅哥的星球,只需要100多,就可以获得大厂架构师对你手把手教学![https://gaga.plus](https://gaga.plus) - 项目演示地址。 diff --git a/docs/md/project/big-market/dev-ops/openai_big_market.md b/docs/md/project/big-market/dev-ops/openai_big_market.md new file mode 100644 index 000000000..24d9541ea --- /dev/null +++ b/docs/md/project/big-market/dev-ops/openai_big_market.md @@ -0,0 +1,415 @@ +--- +title: 第4节:课程完结,上线部署教程 +lock: no +--- + +# 《大营销平台系统设计实现》 - 开发运维 第4节:课程完结,上线部署教程 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +不少小伙伴在面试中总会被提问,你的微服务是哪两套项目对接,你是分布式体现在哪。好像这么一问,平时做的项目既不是微服务,也不是分布式,都不知道怎么回答啦!咋办!?🤔 + +
+ +
+ +**还是别做玩具项目了!** + +要说这种级别的东西,要不是在公司里真的实践过,要不是跟着小傅哥完完全全的做过。否则还真没有这类经验,很多市面的CRUD项目都是一个个孤岛单体,根本没有微服务对接,也谈不上分布式架构。所有的这类东西,都是真的在职场多年实践,才能一步步带着你学习掌握到! + +这次,我把两套项目完成了微服务对接,分布式架构部署。你可以完全全全的学习到这些东西到底是怎么玩的! + +>上车,这次项目做的很硬核!文末可以获取全部项目课程。 + +## 1. 效果展示 + +这是一次非常庞大的系统开发实践,学习这样一套东西,可以全面的提高个人综合技术实战能力。了解业务、懂得架构、提到思维、锻炼编码。一个个场景的解决方案,一个个设计模式的实践运用。 + +
+ +
+ +- 地址:[https://openai.gaga.plus/](https://openai.gaga.plus/) - 侧边栏,点击兑换、购物、营销。 +- 说明:你可以体验 OpenAI 兑换、抽奖、兑换、签到等各个功能。这些也都各个互联网公司C端场景的产品设计。 + +>接下来是一整套的项目的部署执行过程,公司中大家上线,对于中大型项目和需求,也会列出明确的上线执行步骤。 + +## 2. 部署结构 + +### 2.1 微服务&分布式 + +
+ +
+ +- 微服务:一套 OpenAi chatgpt-data 后端应用,一套 big-market 后端应用,两套微服务。 +- 分布式:Nginx 负载 http,大营销负载 rpc,支持多实例部署,mq 消息解耦流程、xxl-job 任务驱动、redis 缓存预热数据。分库分表、canal 完成数据的分离和聚合。 + +### 2.2 分层部署关系 + +
+ +
+ +- 从底层往上,依次是[服务器(4c16g)](http://618.gaga.plus/)、部署环境、应用环境、上线配置、应用构建、参数配置,之后就是执行应用启动啦。 +- 接下来我们的部署,也会做这些操作。 + +## 3. 环境诉求 + +>为了方便大家完成项目的部署,这里采用了 Linux 服务器直接搭建构建环境的方式进行处理。在以下操作中你可以把 Linux 当做一台本地的电脑进行使用。只不过都是命令方式操作。 + +本节需要在一台服务器上,部署分布式技术栈(nacos、mysql、redis、xxl-job、rabbitmq、Zookeeper等),2套 big-market 大营销后台(rpc 负载)、1套 chatgpt-data 后端、1套 chatgpt-web 前端。 + +1. 推荐 `4c16g` 服务器 3个月:https://618.gaga.plus - `158元(续费同价)`,这个价格比较适合临时做测试验证。【占用在12g+】 +2. docker、portainer - `在 bugstack.cn 路书中有安装教程` +3. jdk 1.8、maven 3.8.8、git,课程提供了安装部署视频 +4. Git使用教程:[https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) +5. 申请微信公众号测试平台:[https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index](https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index) - 需要申请。 +6. 智谱AI,申请Key:[https://bigmodel.cn/usercenter/apikeys](https://bigmodel.cn/usercenter/apikeys) - OpenAI 项目会使用到 + +
+ +
+ +- 脚本:[https://gitcode.net/KnowledgePlanet/big-market/big-market-dev-ops](https://gitcode.net/KnowledgePlanet/big-market/big-market-dev-ops) +- 说明:这里小傅哥已经分各个阶段给大家提供好了部署脚本,你可以参照使用。在 mysql 包下,已经提供了本次项目部署所需的 sql 初始化文件。 + +**docker 镜像推荐** + +```java +# 清空 /etc/docker/daemon.json 文件,准备写入新内容 +echo >/etc/docker/daemon.json + +# 使用 cat 命令将以下内容写入 /etc/docker/daemon.json 文件 +# 配置 Docker 镜像加速器,使用多个镜像源来提高镜像下载速度 +cat >/etc/docker/daemon.json < + + + +- 推荐使用 [termius](https://termius.com/) 连接云服务器,在根目录创建 `/dev-ops` 文件夹。再通过 SFTP 工具,把本地的部署脚本上传到文件夹中。 + +### 2. 执行脚本 + +```shell +cd /dev-ops +# 方式1 +docker-compose -f docker-compose-environment.yml up -d + +# 方式2 +docker-compose -f docker-compose-environment-xfg-studio.yml up -d +``` + +- 方式1;需要配置可用的 docker 镜像,才能拉取。 +- 方式2;是小傅哥准备好的镜像地址,速度更快一些。如果方式2不可用了,可以继续用方式1 + +
+ +
+ +
+ +
+ +```java +- nacos:http://117.72.90.238:8848/nacos - 「账密:nacos/nacos」 +- rabbitmq:http://117.72.90.238:15672/ - 「账密:admin/12qw!@QW」 +- redis-admin:http://117.72.90.238:8081/ - 「账密:admin/12qw!@QW」 +- xxl-job-admin:http://117.72.90.238:9090/xxl-job-admin - 「账密:root/12qw!@QW」 +- phpmyadmin:http://117.72.90.238:8899/ - 「账密:root/12qw!@QW」 +``` + +- 部署完成后,可以依次访问 phpmyadmin、rabbitmq、redis-admin、xxl-job-admin、nacos 这些管理后台。检查数据库表是否已经全部初始化完成。 +- 账号、密码、IP,你可以按照自己的进行修改,密码是配置在 docker-compose-environment/environment-xfg-studio 脚本中。 + +## 5. 应用构建 + +在 /dev-ops/ 目录下创建创建 gitcode 文件夹,里面分别拉取代码; + +### 1. big-market 大营销后端 + +#### 1.1 参数配置 + +1. application.yml 修改 `active: prod` 为上线参数 +2. application-prod.yml 为上线部署配置,其中一些IP的地方被名称替代,如 mysql、zookeeper等,是因为这些IP可以在 docker-compose 部署中,在一个文件夹下,走同一个网络,进行内网通信。 +3. docker-compose-app.yml big-market 注意配置 rabbitmq 地址为你的服务器公网ip地址,并开放端口 5672 +4. docker-compose-app.yml 配置了2套 big-market 应用实例(不同端口)进行负载。 +5. gitcode 检出代码,可参考文章开头提到的 Git 使用教程。 + +#### 1.2 构建脚本 + +```shell +cd /dev-ops/gitcode/ +git clone -b docker-images-v5.0 https://gitcode.net/KnowledgePlanet/big-market/big-market.git + +cd big-market/ +mvn clean install + +cd big-market-app +chmod +x build.sh +./build.sh +``` + +### 2. chatgpt-data OpenAI 后端 + +#### 2.1 参数配置 + +1. application.yml 修改 `active: prod` 为上线参数 +2. application-prod.yml 为上线部署配置,其中一些IP的地方被名称替代,如 mysql、nacos,是因为这些IP可以在 docker-compose 部署中,在一个文件夹下,走同一个网络,进行内网通信。 +3. chatgpt.sdk.config.enabled、wxpay.config.enabled,可以都配置false,如果你有 chatgpt 和 微信支付那么可以配置。星球中还有关于支付宝沙箱、蓝兔支付,也可以更换对接。 + +#### 2.2 构建脚本 + +```shell +cd /dev-ops/gitcode/ +git clone -b docker-images-v5.0 https://gitcode.net/KnowledgePlanet/chatgpt/chatgpt-data.git + +cd chatgpt-data/ +mvn clean install + +cd chatgpt-data-app +chmod +x build.sh +./build.sh +``` + +### 3. chatgpt-web OpenAI 前端 + +#### 3.1 配置 + +**index.tsx** + +```java +const openAIApiHostUrl = "http://117.72.90.238:8091"; +const bigMarketApiHostUrl = "http://117.72.90.238:8092"; +``` + +- 修改为你的IP:PORT地址,如果你有域名也可以统一配置 https 的域名地址,但要全部都是统一的。避免跨域问题。 + +#### 3.2 脚本 + +```shell +cd /dev-ops/gitcode/ +git clone -b docker-images-v5.0 https://gitcode.net/KnowledgePlanet/chatgpt/chatgpt-web.git + +chmod +x build.sh +./build.sh +``` + +## 6. 应用部署 + +### 1. 脚本启动 + +以下为应用部署 docker compose 脚本,部署的时候进入linux此脚本所在文件夹,执行 `docker-compose -f docker-compose-app.yml up -d` 即可。 + +```java +version: '3.8' +# 命令执行 docker-compose -f docker-compose-app.yml up -d +services: + # 大营销后端 + big-market-app-01: + image: fuzhengwei/big-market-app:5.1 + container_name: big-market-app-01 + restart: always + ports: + - "8092:8092" + environment: + - TZ=PRC + - SERVER_PORT=8092 + - APP_CONFIG_API_VERSION=v1 + - APP_CONFIG_CROSS_ORIGIN=* + - ZOOKEEPER_SDK_CONFIG_ENABLE=false + - SPRING_RABBITMQ_ADDRESSES=117.72.90.238 + volumes: + - ./log:/data/log + networks: + - my-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # 大营销后端 + big-market-app-02: + image: fuzhengwei/big-market-app:5.1 + container_name: big-market-app-02 + restart: always + ports: + - "8093:8093" + environment: + - TZ=PRC + - SERVER_PORT=8093 + - APP_CONFIG_API_VERSION=v1 + - APP_CONFIG_CROSS_ORIGIN=* + - ZOOKEEPER_SDK_CONFIG_ENABLE=false + - SPRING_RABBITMQ_ADDRESSES=117.72.90.238 + volumes: + - ./log:/data/log + networks: + - my-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # OpenAI 应用后端 + chatgpt-data-app: + image: fuzhengwei/chatgpt-data-app:5.1 + container_name: chatgpt-data-app + ports: + - "8091:8091" + environment: + - TZ=PRC + - SERVER_PORT=8091 + - APP_CONFIG_API_VERSION=v1 + - APP_CONFIG_CROSS_ORIGIN=* + - APP_CONFIG_LIMIT_COUNT=3 + - APP_CONFIG_WHITE_LIST=ojbZUv18lbmriaTjcCWBYkOrSbHA + - SPRING_DATASOURCE_USERNAME=root + - SPRING_DATASOURCE_PASSWORD=12qw!@QW + - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/openai?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true + - WX_CONFIG_ORIGINALID=gh_e067c267e056 + - WX_CONFIG_APPID=wx5a228ff69e28a91f + - WX_CONFIG_TOKEN=b8b6 + - CHATGPT_SDK_CONFIG_ENABLED=false + - CHATGPT_SDK_CONFIG_API_HOST=https://service-d6wuqy4n-1320869466.cd.apigw.tencentcs.com/ + - CHATGPT_SDK_CONFIG_API_KEY=sk-cVvbudeJq3yQUvcyCeBeB97253C146BaAaC94d70D2890b8e + - CHATGLM_SDK_CONFIG_ENABLED=true + - CHATGLM_SDK_CONFIG_API_HOST=https://open.bigmodel.cn/ + - CHATGLM_SDK_CONFIG_API_KEY=96b1fb042a0b51f334ecfdb28ef95837.6pgWXziv5CdbJdGP + volumes: + - ./log:/data/log + networks: + - my-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + restart: always + + # OpenAI 应用前端 + chatgpt-web-app: + container_name: chatgpt-web-app + image: fuzhengwei/chatgpt-web-app:5.1 + ports: + - "3002:3002" + networks: + - my-network + restart: always + +networks: + my-network: + driver: bridge +``` + +
+ +
+ +- 注意:`SPRING_RABBITMQ_ADDRESSES=117.72.90.238`,需要配置云服务器IP地址,并开放 RabbitMQ 端口。 +- 执行预热活动:`http://117.72.90.238:8092/api/v1/raffle/activity/armory?activityId=100401` +- 本身大营销还有一个后台,但服务器可能不够,如果不部署后台,不做活动上架,那么可以通过执行预热活动接口完成活动预热。 +- 注意微信配置 + +```java +- WX_CONFIG_ORIGINALID=gh_e067c267e056 +- WX_CONFIG_APPID=wx5a228ff69e28a91f +- WX_CONFIG_TOKEN=b8b6 +``` + +
+ +
+- 全部更换为你的配置信息。注意,接口配置信息,需要在服务启动后配置。 + + +### 2. natapp 启动 - 无自己域名 + +#### 2.1 购买隧道 + +
+ +
+ +```java +[default] +authtoken=bcffa08ea0a7dde4 #对应一条隧道的authtoken,你需要更换为你的。否则不能正常启动。 +clienttoken= #对应客户端的clienttoken,将会忽略authtoken,若无请留空, +log=none #log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none +loglevel=ERROR #日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG +http_proxy= #代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空 +``` + +- 打开 natapp/config.ini 文件,复制 authtoken 替换为你的。 + +#### 2.2 执行启动 + +```java +[root@lavm-snw6pd3z62 chatgpt-web]# cd /dev-ops/ +[root@lavm-snw6pd3z62 dev-ops]# ls +canal docker-compose-app.yml gitcode kibana maven nginx redis +canal-adapter docker-compose-environment-xfg-studio.yml grafana log mysql prometheus +curl docker-compose-environment.yml java logstash natapp rabbitmq +[root@lavm-snw6pd3z62 dev-ops]# cd natapp/ +[root@lavm-snw6pd3z62 natapp]# ./natapp +``` + +
+ +
+ +- 启动 Natapp 穿透程序,主要是因为微信公众号配置,需要域名。如果你没有域名就按照这个方式,如果有域名,可以走自己的域名。 +- 在微信公众平台配置 URL 验签地址:[https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index](https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index) 配置地址:`http://xfg-studio.natapp1.cc/api/v1/wx/portal/wxad979c0307864a66` - 更换为你的 natapp 内网穿透地址。 + +### 3. Nginx 配置 - 有自己域名 + +
+ +
+ +- 域名:需要购买,各个云服务器厂商,都可以购买域名。 +- ssl:在 bugstack.cn 路书中有讲解如何配置免费 ssl。 +- 在微信公众平台配置 URL 验签地址:[https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index](https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index) 配置地址:`http://api-test.gaga.plus/api/v1/wx/portal/wxad979c0307864a66` - 更换为你的域名地址。 + +## 7. 应用访问 + +地址:[http://117.72.90.238:3002/](http://117.72.90.238:3002/) - `更换为你的IP地址` + +
+ +
+ +> 登录之后,你就可以愉快的玩耍啦! diff --git "a/docs/md/project/big-market/dev-ops/\347\254\2541\350\212\202\357\274\232\344\275\277\347\224\250\350\204\232\346\211\213\346\236\266\345\210\233\345\273\272\345\267\245\347\250\213.md" "b/docs/md/project/big-market/dev-ops/\347\254\2541\350\212\202\357\274\232\344\275\277\347\224\250\350\204\232\346\211\213\346\236\266\345\210\233\345\273\272\345\267\245\347\250\213.md" new file mode 100644 index 000000000..2192ac83e --- /dev/null +++ "b/docs/md/project/big-market/dev-ops/\347\254\2541\350\212\202\357\274\232\344\275\277\347\224\250\350\204\232\346\211\213\346\236\266\345\210\233\345\273\272\345\267\245\347\250\213.md" @@ -0,0 +1,38 @@ +--- +title: 第1节:使用脚手架创建工程&PUSH代码 +pay: https://t.zsxq.com/14v5mZGIU +--- + +# 《大营销平台系统设计实现》 - 开发运维 第1节:使用脚手架创建工程&PUSH代码 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:运用脚手架创建项目工程,讲解工程分层结构,并把项目推送到代码仓库。 +- **课程视频**:[https://t.zsxq.com/16ap8RWDB](https://t.zsxq.com/16ap8RWDB) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +带着读者伙伴使用工程脚手架完成大营销平台系统的工程创建,并介绍每个模块的作用,同时再完成在代码仓库中创建自己的项目以及推送本地工程到远程仓库中。 + +- 脚手架文档:[https://bugstack.cn/md/road-map/ddd-archetype.html](https://bugstack.cn/md/road-map/ddd-archetype.html) - 本项目会需要用到的脚手架。 +- 脚手架工程:[https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-lite](https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-lite) + +本项目用到的代码推送在后续所有工程中都会需要用到,操作方式是一样的。之后项目的创建会采用最基本的最简单的 lite 版本,后续随着项目所需的分布式技术栈,在逐步引入其他分布式技术栈的使用。 + +## 二、内容说明 + +本节是手把手操作的过程(附带视频),为小白开发伙伴学习编程开路。在本节你会学习到如何完成项目的搭建和提交。 + +此外本节也会涉及一点DDD的理论知识,可以从星球【课程入口】,**基础教程** 补充学习DDD理论和DDD实践。 + +
+ +
+ +- 前置:本地已安装了 Git 并配置到 IntelliJ IDEA 中。 \ No newline at end of file diff --git "a/docs/md/project/big-market/dev-ops/\347\254\2542\350\212\202\357\274\232\347\254\254\344\270\200\351\230\266\346\256\265\345\256\214\346\210\220\346\212\275\345\245\226\351\203\250\347\275\262.md" "b/docs/md/project/big-market/dev-ops/\347\254\2542\350\212\202\357\274\232\347\254\254\344\270\200\351\230\266\346\256\265\345\256\214\346\210\220\346\212\275\345\245\226\351\203\250\347\275\262.md" new file mode 100644 index 000000000..e18833a4b --- /dev/null +++ "b/docs/md/project/big-market/dev-ops/\347\254\2542\350\212\202\357\274\232\347\254\254\344\270\200\351\230\266\346\256\265\345\256\214\346\210\220\346\212\275\345\245\226\351\203\250\347\275\262.md" @@ -0,0 +1,40 @@ +--- +title: 第2节:大营销系统部署 +pay: https://t.zsxq.com/17HkS0CI0 +--- + +# 《大营销平台系统设计实现》 - 开发运维 第2节:大营销系统部署(全部阶段) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +- **本章难度**:★★★☆☆ +- **本章重点**:分别对前后端应用进行构建配置,以及提供完整的本地和云服务器环境部署教程,让读者可以自己部署出一套抽奖服务。 +- **课程视频**:[https://t.zsxq.com/17YfZx6SU](https://t.zsxq.com/17YfZx6SU) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +当前章节是整个课程的第17节,到这一节大营销的抽奖部分功能就完成了,接下来带着小伙伴们做个部署操作。包括;本地部署和云服务环境部署。 + +看到不少伙伴反馈自己部署完服务器上,怎么被挖矿了,还有攻击数据库勒索。在本节小傅哥会教你解决这个问题,不对外暴漏服务本身的数据库、缓存端口,禁止这种情况的部署方式。 + +## 二、部署说明 + +对于工程的部署有较多种方式,如使用 Jenkins、Buddy、Drone 等都是 CI&CD 工具,在星球的课程入口基础教程中有关于此内容的讲解。 + +本节我们会使用更简单的方式进行,进行`本地`和`云服务`的部署。让大家更方便的完成上线操作,如果感兴趣 Jenkins 部署也可以参考星球内的课程来部署。 + +
+ +
+ +- 如果你有需求在云服务器部署的话,本节所需云服务器最低需要2c2g,部署后占用到59%的内存:[https://gaga.plus/yun.html](https://gaga.plus/yun.html) 2c2g 最低50元1年。【后续的其他服务开发后,含分布式环境的整体内存会占用到3G+】 +- 你也可以在本地部署,无论 Windows 还是 Mac 只要正确安装 Docker 环境即可。【注意 Windows 需要开启 wsl2】 +- 本节会在前后端工程中,分别配置 docker 镜像打包,构建应用的镜像文件。部署的时候会用到应用镜像文件。 +- 如果你需要云服务部署,则需要注册 [https://hub.docker.com/](https://hub.docker.com/) 账号,并创建镜像名称和上传你的镜像。docker hub 的作用相当于媒介,上传到 docker hub 后,在云服务器端在从 docker hub 拉取下来镜像部署。这个过程就类似于你在部署一些 Redis、MySQL 环境一样。【docker hub 的方式比较简单一些,如果使用 Jenkins 则不需要 docker hub 作为媒介,直接构建部署就可以】 \ No newline at end of file diff --git "a/docs/md/project/big-market/dev-ops/\347\254\2543\350\212\202\357\274\232\345\274\225\345\205\245Nacos+Dubbo\346\241\206\346\236\266.md" "b/docs/md/project/big-market/dev-ops/\347\254\2543\350\212\202\357\274\232\345\274\225\345\205\245Nacos+Dubbo\346\241\206\346\236\266.md" new file mode 100644 index 000000000..34b4ad63f --- /dev/null +++ "b/docs/md/project/big-market/dev-ops/\347\254\2543\350\212\202\357\274\232\345\274\225\345\205\245Nacos+Dubbo\346\241\206\346\236\266.md" @@ -0,0 +1,36 @@ +--- +title: 第3节:引入Nacos+Dubbo框架 +pay: https://t.zsxq.com/rl0FE +--- + +# 《大营销平台系统设计实现》 - 开发运维 第3节:引入Nacos+Dubbo框架 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:引入分布式技术栈框架 Nacos + Dubbo,用于微服务间调用,提高信息数据传输效率。 +- **课程视频**:[https://t.zsxq.com/bzjMv](https://t.zsxq.com/bzjMv) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +因为分布式技术栈框架 Nacos + Dubbo,用于微服务间调用,提高信息数据传输效率。在本节为大家讲解 Dubbo 的核心模型结构,通信协议配置和对接使用。 + +**前置知识学习**,如果你此前没了解过 RPC 可以通过星球的基础教程进行学习,这里有 RPC 的讲解、简单 RPC 实现和实践运用。地址:[https://bugstack.cn/md/road-map/dubbo.html](https://bugstack.cn/md/road-map/dubbo.html) + +## 二、架构说明 + +以整个互联网电商公司视角来看,它会包括;商品系统、购物系统、营销系统、交易系统、结算系统、清分系统、对账系统、以及各类运营系统。而咱们做的大营销系统就是其中的一个微服务系统,这个系统可以被分布式部署和使用。 + +而把一个系统扩展为分布式系统,会有很多相应的技术栈引入来解决数据传输问题,聚合查询问题,链路监控问题等。这些内容会在这个系列中逐步引入带大家一起学习。 + +
+ +
+ +- 首先,Dubbo 这样的 RPC 框架,是比 HTTP 在微服务间的通信效率更高的,所以各个中等规模以上的互联网公司也是更喜欢引入 RPC 框架来解决微服务间通信问题。 +- 不过像 Dubbo、SpringCloud,都只是个框架而已,一个系统的核心实现不受框架影响,你可以按照实际诉求,更换或者支持多种通信协议。 \ No newline at end of file diff --git "a/docs/md/project/big-market/distributed/\347\254\2541\350\212\202\357\274\232\345\257\271\346\216\245OpenAI\351\241\271\347\233\256\351\242\235\345\272\246\345\245\226\345\223\201\346\216\245\345\217\243.md" "b/docs/md/project/big-market/distributed/\347\254\2541\350\212\202\357\274\232\345\257\271\346\216\245OpenAI\351\241\271\347\233\256\351\242\235\345\272\246\345\245\226\345\223\201\346\216\245\345\217\243.md" new file mode 100644 index 000000000..4b8aa7097 --- /dev/null +++ "b/docs/md/project/big-market/distributed/\347\254\2541\350\212\202\357\274\232\345\257\271\346\216\245OpenAI\351\241\271\347\233\256\351\242\235\345\272\246\345\245\226\345\223\201\346\216\245\345\217\243.md" @@ -0,0 +1,36 @@ +--- +title: 第1节:对接OpenAI项目额度奖品接口 +pay: https://t.zsxq.com/ByrMx +--- + +# 《大营销平台系统设计实现》 - 外部对接 第1节:对接OpenAI项目额度奖品接口 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:新增 OpenAI 账户额度调整接口,增加大营销 openai_use_count 奖品发放实现类,完成发奖对接。 +- **课程视频**:[https://t.zsxq.com/w3Ebn](https://t.zsxq.com/w3Ebn) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +**前置说明**:在学习本章节前,需要对星球内的 [《OpenAI 应用项目》](https://t.zsxq.com/19aSkDvYB) 有所了解。 + +星球的 OpenAI 业务项目是一个以售卖额度提供 ChatGLM、ChatGPT 各类服务为载体的 OpenAI 业务应用。那么大营销项目就可以结合到 OpenAI 应用项目中提供积分、兑换、返利、抽奖这样的服务模块,增强 OpenAI 业务项目的拉新、促活、留存用户的能力。 + +整个对接过程分为2个阶段,一个是 OpenAI 项目提供账户调额能力作为奖品接口给大营销使用,另外一个是 OpenAI 项目,对接大营销的 RPC/HTTP 接口,搭建营销活动页。 + +## 二、对接流程 + +整个对接过程示意; + +
+ +
+ +- 如图,为用户使用 OpenAI 应用项目时候,一个前端,两个后端服务的对接过程。这里你就可以把大营销看做为一个微服务了。 +- 在这一节,我们先来完成,通过http接口,发放额度奖品的操作。 diff --git "a/docs/md/project/big-market/distributed/\347\254\2542\350\212\202\357\274\232\350\220\245\351\224\200\351\241\265\351\235\242\346\216\245\345\217\243\345\260\201\350\243\205.md" "b/docs/md/project/big-market/distributed/\347\254\2542\350\212\202\357\274\232\350\220\245\351\224\200\351\241\265\351\235\242\346\216\245\345\217\243\345\260\201\350\243\205.md" new file mode 100644 index 000000000..788b35f47 --- /dev/null +++ "b/docs/md/project/big-market/distributed/\347\254\2542\350\212\202\357\274\232\350\220\245\351\224\200\351\241\265\351\235\242\346\216\245\345\217\243\345\260\201\350\243\205.md" @@ -0,0 +1,34 @@ +--- +title: 第2节:营销页面接口封装 +pay: https://t.zsxq.com/L2ZdQ +--- + +# 《大营销平台系统设计实现》 - 外部对接 第2节:营销页面接口封装 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:在 big-market 工程,开发适配 openai 项目,前端工程 chatgpt-web 所需的 http 接口。进行页面的开发和接口对接。 +- **课程视频**:[https://t.zsxq.com/A7eoq](https://t.zsxq.com/A7eoq) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在 big-market 工程,开发适配 openai 项目,前端工程 chatgpt-web 所需的 http 接口。进行页面的开发和接口对接。 + +因为 openai 项目,前端工程 chatgpt-web 调用的接口都会在头信息中传递 token,所以我们也需要在 big-market 的接口中,新增加一份带有 token 的接口,对接解析和使用。 + +## 二、对接设计 + +在前端工程 chatgpt-web 中添加营销UI和接口对接。 + +
+ +
+ +- openai 项目后端服务,提供账户额度接口。 +- big-market 大营销服务,提供积分、抽奖、签到、兑换,过程中的查询和使用接口。 diff --git "a/docs/md/project/big-market/distributed/\347\254\2543\350\212\202\357\274\232RPC\346\216\245\345\217\243\345\257\271\346\216\245\346\224\257\344\273\230\350\277\224\345\210\251.md" "b/docs/md/project/big-market/distributed/\347\254\2543\350\212\202\357\274\232RPC\346\216\245\345\217\243\345\257\271\346\216\245\346\224\257\344\273\230\350\277\224\345\210\251.md" new file mode 100644 index 000000000..ebdba028f --- /dev/null +++ "b/docs/md/project/big-market/distributed/\347\254\2543\350\212\202\357\274\232RPC\346\216\245\345\217\243\345\257\271\346\216\245\346\224\257\344\273\230\350\277\224\345\210\251.md" @@ -0,0 +1,35 @@ +--- +title: 第3节:RPC接口对接支付返利 +pay: https://t.zsxq.com/fFntX +--- + +# 《大营销平台系统设计实现》 - 外部对接 第3节:RPC接口对接支付返利 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:通过在大营销 big-market 新增提供的通用返利 RPC 接口,由 OpenAI 服务 chatgpt-data 系统在支付完成接收到回调消息后进行对接完成返利动作。 +- **课程视频**:[https://t.zsxq.com/15gLHtPaU](https://t.zsxq.com/15gLHtPaU) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +通过在大营销 big-market 新增提供的通用返利 RPC 接口,由 OpenAI 服务 chatgpt-data 系统在支付完成接收到回调消息后进行对接完成返利动作。 + +本节会使用到注册到 Nacos 的 Dubbo 服务,完成内部微服务的对接。Dubbo 的优势在于底层的通信协议比 HTTP 更加迅速,适合微服务间通信使用。不过这样的对接方式会强依赖另外一方系统的存在,否则启动会失败。所以后续我们部署的时候,也会把 chatgpt-data 依赖于 big-market 一起部署。 + +前置资料:关于dubbo配置使用的基础教程:[https://bugstack.cn/md/road-map/dubbo.html](https://bugstack.cn/md/road-map/dubbo.html) + +## 二、对接流程 + +通过 RPC 完成微服务的对接; + +
+ +
+ +- 用户下单完成支付后,会接收到支付消息,之后调用大营销提供的返利接口进行返利。返利为;积分和抽奖次数。 \ No newline at end of file diff --git "a/docs/md/project/big-market/distributed/\347\254\2544\350\212\202\357\274\232\346\264\273\345\212\250\344\270\212\346\236\266\345\217\221\345\270\203\351\242\204\347\203\255\345\257\271\346\216\245.md" "b/docs/md/project/big-market/distributed/\347\254\2544\350\212\202\357\274\232\346\264\273\345\212\250\344\270\212\346\236\266\345\217\221\345\270\203\351\242\204\347\203\255\345\257\271\346\216\245.md" new file mode 100644 index 000000000..ac229a9db --- /dev/null +++ "b/docs/md/project/big-market/distributed/\347\254\2544\350\212\202\357\274\232\346\264\273\345\212\250\344\270\212\346\236\266\345\217\221\345\270\203\351\242\204\347\203\255\345\257\271\346\216\245.md" @@ -0,0 +1,35 @@ +--- +title: 第4节:活动上架发布预热对接 +pay: https://t.zsxq.com/Gh1Py +--- + +# 《大营销平台系统设计实现》 - 外部对接 第4节:活动上架发布预热对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:增加活动上架服务,通过运营后台管理上架,前端工程根据渠道sc值查询上架活动。 +- **课程视频**:[https://t.zsxq.com/15gLHtPaU](https://t.zsxq.com/15gLHtPaU) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +本节的内容是一个互联网中对外发布抽奖、发券、拉新等行为活动设计的通用方案。在实际运营作业中,会创建出很多的活动,这些活动会被分配到不同渠道给不同来源进行使用。那么这里就要有一个活动上架平台,让外部调用方根据传入的SC值,也就是渠道和来源调用到这一方可以使用的当前的活动ID。 + +这就有点像你去一个大超市看到的,每个货架上摆放着各类的的商品,这些商品其实是有它的仓库的,但当前你所看到的买到的,都是货架上摆放的。所以为了让用户参与到我们的活动,我们也需要把活动进行上架。 + +## 二、业务流程 + +活动上架流程,涉及到三块,如图; + +
+ +
+ +- 首先,运营负责把活动上架,点击审核为`有效`。有效后,也就是在这个时间点把活动预热到 Redis,在咱们大营销中的预热操作是装配的接口,会在这个过程中调用。 +- 之后,用户从前端页面进入后,程序会查询到当前上架的活动,这样就明确了具体参与到哪个活动上了。 + diff --git "a/docs/md/project/big-market/erp/\347\254\2541\350\212\202\357\274\232\345\210\235\345\247\213\345\220\216\345\217\260\350\277\220\350\220\245\351\241\265\351\235\242.md" "b/docs/md/project/big-market/erp/\347\254\2541\350\212\202\357\274\232\345\210\235\345\247\213\345\220\216\345\217\260\350\277\220\350\220\245\351\241\265\351\235\242.md" new file mode 100644 index 000000000..5a8a1f8d0 --- /dev/null +++ "b/docs/md/project/big-market/erp/\347\254\2541\350\212\202\357\274\232\345\210\235\345\247\213\345\220\216\345\217\260\350\277\220\350\220\245\351\241\265\351\235\242.md" @@ -0,0 +1,47 @@ +--- +title: 第1节:初始后台运营页面 +pay: https://t.zsxq.com/im8ZV +--- + +# 《大营销平台系统设计实现》 - 运营后台 第1节:初始后台运营页面 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:基于蚂蚁集团 Ant Design Pro 初始搭建一套大营销的运营管理后台页面。 +- **课程视频**:[https://t.zsxq.com/iH3gW](https://t.zsxq.com/iH3gW) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +基于蚂蚁集团 Ant Design Pro 初始搭建一套大营销的运营管理后台页面,并带着读者一起初步了解 Ant Design Pro 框架的使用。 + +关于前端的页面开发,会涉及到 React、HTML、Div、CSS,等知识点,并在 Ant Design Pro 框架的包装加下,还会额外提供一些组件。 + +这一阶段并不是面试重点,Java 面试中也不会考察关于前端技术的使用。但对于自身积累,扩展技术栈使用,可以尝试学习下。你只需要能大部分看懂这些代码,并可以基于已有的代码复制出你要的页面即可。 + +初学者可以通过 React 提供的最佳实践教程,了解 React 使用,教程地址:[https://zh-hans.react.dev/learn](https://zh-hans.react.dev/learn) + +## 二、关于后台 + +运营后台的搭建不只有 Ant Design Pro,而是还有非常的框架可以选择。比如你可以在 github 搜索, **react admin**; + +
+ +
+ +以上这些,包括你可以在网上搜索好用的 react/vue 运营管理后台,都可以搜索到非常多,有些是官网开发的,也有个人提供的。咱们之所以选择 Ant Design Pro 不只是为了搭建一个运营后台,也是为了让大家学习到企业中更多的选择方案,这样的锻炼也可以在企业中使用。 + +### 1. 基础环境 + +- 你需要确保自己本机安装了 nodejs 这样才能使用 Ant Design Pro +- 推荐使用 WebStorm 开发运营后台,这样你会基本以使用 IntelliJ IDEA 的习惯使用 WebStorm,对于 Java 程序员来说基本没有跨软件成本。 + +### 2. 框架地址 + +- Ant Design [https://ant-design.antgroup.com/index-cn](https://ant-design.antgroup.com/index-cn) - 是一套设计语言与组件库 +- Ant Design Pro [https://pro.ant.design/zh-CN](https://pro.ant.design/zh-CN) - 是一套解决方案,就类似于小傅哥给大家提供的 DDD 脚手架一样,帮你把基础的东西安装好。 \ No newline at end of file diff --git "a/docs/md/project/big-market/erp/\347\254\2542\350\212\202\357\274\232querys\346\250\241\345\235\227\346\217\220\344\276\233\346\237\245\350\257\242\346\216\245\345\217\243.md" "b/docs/md/project/big-market/erp/\347\254\2542\350\212\202\357\274\232querys\346\250\241\345\235\227\346\217\220\344\276\233\346\237\245\350\257\242\346\216\245\345\217\243.md" new file mode 100644 index 000000000..242c209e1 --- /dev/null +++ "b/docs/md/project/big-market/erp/\347\254\2542\350\212\202\357\274\232querys\346\250\241\345\235\227\346\217\220\344\276\233\346\237\245\350\257\242\346\216\245\345\217\243.md" @@ -0,0 +1,45 @@ +--- +title: 第2节:querys模块提供查询接口 +pay: https://t.zsxq.com/YFckh +--- + +# 《大营销平台系统设计实现》 - 运营后台 第2节:querys模块提供查询接口 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:给 big-market 新增加 querys 模块,提供数据查询操作,并提供接口给前端使用。 +- **课程视频**:[https://t.zsxq.com/ByrMx](https://t.zsxq.com/ByrMx) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在使用 DDD 做工程开发的时候,我们可以通过 domain 包,划分出一个个功能领域进行实现。这样强业务属性的逻辑,可以很好的拆分对应的边界,不至于像是 MVC 中让一堆的 Service 混乱。 + +但当我们遇到一些非强业务逻辑,只是对数据、缓存、ElasticSearch等做查询,给 ERP 运营系统提供数据时,就不时候全部都走一遍 domain 领域了,那样就显得非常重。所以这里我们要新增加一个 querys 模块,只做这类数据的查询操作。 + +在 big-market 提供好 querys 模块和相应的接口后,配置到前端页面使用。 + +## 二、后端实现 + +### 1. 添加 querys 模块 + +
+ +
+ +- 在工程上右键,新增加一个 querys 模块,并对 pom 进行配置。这个 pom 的配置与 domain 中的配置类似。 + +### 2. 实现 querys 接口 + +
+ +
+ +- 在 querys 中定义需要查询的的仓储接口,并由 domain 领域层实现接口。 +- 这个实现的过程也是依赖倒置的方式实现。 +- 另外注意,咱们是把 ElasticSerach 的查询,使用的是 x-pack-jdbc 方式,ElasticSerach 还有还有 SpringBoot 程序通过 `spring-boot-starter-data-elasticsearch` 提供查询的方式,也可以尝试试试。`面试或者做方案中,也有可能会问,你为什么选择 x-pack-jdbc 还有什么其他方式` \ No newline at end of file diff --git a/docs/md/project/big-market/extra/big-market-try-it-out.md b/docs/md/project/big-market/extra/big-market-try-it-out.md new file mode 100644 index 000000000..3b1d9172a --- /dev/null +++ b/docs/md/project/big-market/extra/big-market-try-it-out.md @@ -0,0 +1,97 @@ +--- +title: 大营销平台系统 - 视频试看学习 +lock: no +--- + +# 公司中哪个项目组,系统最复杂?个人成长最快? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/14gswKIeX](https://t.zsxq.com/14gswKIeX) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主,小傅哥。 + +每个互联网中大厂公司中,都有这么一个组。承接流程最大、系统设计最复杂、需求迭代最多,也是最容易出事故的组。**🤨在这个组1年的技术成长 = 其他组3年!** + +
+ +
+ +呐,这个组就是互联网大厂中的`营销组` 😂 我就是被摧残了好多年,才成长成这样! + +营销组的项目是最早触达用户的,打开页面、优惠选券、组合支付、积分活动、分享收益、拉新返现等,都是先进入营销系统,完成相关的动作才是商品的选择和支付。你几乎能在购物、出行、金融、音视频娱乐中,都有对应的营销玩法活动。所以这块系统`流量最大`、`需求最多`、`功能最复杂`!—— **每个公司都有营销组在加班!💐** + +抖音、京东、滴滴、拼多多,都有这样的项目; + +
+ +
+ +虽然需求杂,需求多,但这样部门组里的项目是非常锻炼人的。所以小傅哥这次也开启了一个**《大营销平台项目》**带着你一起学习复杂的场景架构设计和高级编码落地经验。完全从0到1,手把手的带着你思考、设计、编码,完成项目! + +> 文末有加入学习方式,此外小傅哥还提供了这套项目的[B站试看视频5节](#) 「可在文末点击 **阅读原文** 进入试看视频」 + +## 一、技术锻炼一条龙🐲 + +像这样的营销复杂场景项目,势必会引入相关分布式技术栈的使用。并且营销会根据业务流程拆解出对应的微服务系统,包括;券、活动、拉新、抽奖、积分、兑换、灌券、返利等各个平台,这些微服务间通过 RPC 进行通信。又使用 MQ 解耦、任务补偿,以确保微服务内事务一致性,微服务外最终一致性。 + +
+ +
+ +以往的学习,你可能有很多技术栈使用的缺失,甚至也没接触过有高质量的架构和设计模式编码。那么在折腾学习旅程中,这些内容你都将学到。 + +>因为本次项目实战,除了有文档小册,还有手把手视频编码讲解。这样可以把更多的细腻的细节和思考,也全部展示给你。 + +## 二、编码质量两把刀🔪 + +小傅哥带着大家开发的项目,都非常重视架构的设计和编码的质量。因为对于编程来说,越早、越多的吸收有质量的设计和实现方式,越能更好的帮助自己成长。那么这里小傅哥也给大家展示下,在大营销平台系统中一些编码片段。 + +
+ +
+ +>以星球中完成的 openai 大模型应用为场景,将本次的大营销平台项目,进行设计和整合使用。[https://gaga.plus](https://gaga.plus) - 项目演示。 + +### 1. 模型设计高质量 + +
+ +
+ +### 2. 系统工程规范化 + +
+ +
+ +### 3. 单一职责抽象化 + +
+ +
+ +
+ +
+ +### 4. 设计模式场景化 + +
+ +
+ +## 三、项目演示三人行👩🏻‍🏫 + +因为整个项目是一边讲解设计思路,一边说明编码技巧的同时,为大家手写代码。所以每节视频基本都在30分钟~1小时,这样小白伙伴学习这样的项目,也能非常快速的上手。对于老白粉丝伙伴,也可以通过对应的文档学习。 + +
+ +
+ +地址:[https://www.bilibili.com/video/BV1s5411e7Z2](https://www.bilibili.com/video/BV1s5411e7Z2) + +> 这样一套项目跟着学习下来,那技术能力绝对嘎嘎提升!加入学习🧧[知识星球](https://wx.zsxq.com/dweb2/index/group/48411118851818) + diff --git a/docs/md/project/big-market/extra/big-market-v1.md b/docs/md/project/big-market/extra/big-market-v1.md new file mode 100644 index 000000000..bfc3cdbb6 --- /dev/null +++ b/docs/md/project/big-market/extra/big-market-v1.md @@ -0,0 +1,157 @@ +--- +title: 第一阶段交付,可部署上线抽奖模块 +lock: no +--- + +# 《大营销平台系统》第一阶段交付,可部署上线抽奖模块 —— 开局一把IDEA,全程视频手把手。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主,小傅哥。 + +经过这个假期的嘎嘎卷🧨,大营销平台项目第一阶段开发完成并以上线【 在线体验地址:[https://gaga.plus](https://gaga.plus) 】。项目课程,全程视频手把手从0到1开发,**第1阶段仅需要2周学习,即可写到简历冲春招「已提供简历编写模板」**。 + +
+ +
+ +**开局一把IDEA,全程视频手把手!** + +鉴于不少小白伙伴的技术积累偏弱,对照文档学习压力较大,所以这次小傅哥选择了`👨🏻‍💻全程视频手把手敲`的方式进行教学,让小白伙伴跟着课程从0到1走下来,即可完成项目开发+上线。并且小傅哥选择的项目,都是来自于公司中真实场景的业务项目,有着细腻的业务流程和对应的编程实现技巧,这样的项目非常具有竞争力,也是面试官和你有的聊的项目【全是CRUD或者和业务无关的项目,面试官也不知道问啥,只能聊点八股文】。 + +**做项目,就要做的完整!** 完整的经历过,需求 + 开发 + 上线,才能让伙伴们在面试中把东西讲清楚。 + +
+ +
+ +工作了十多年,参与多场的大型校招,面试了几百人,深知考察求职者的标准。接下来小傅哥就站在面试官角度来介绍下这个项目,你应该关注到业务流程和实现技术。 + +>文末有加入学习方式,开年活动送福利 4.0 100万 Tokens 助力学习!🉐 + +## 一、面试介绍 + +**面试介绍举例**,作为学习类项目,你可以参考以下介绍; + +面试官您好,大营销平台的 Raffle 抽奖模块,是我独立负责实现的一个(学习/工作)项目,此项目模块在架构设计上运用了 DDD 分层架构和模板模式、责任链模式、组合模式、工厂模式等,这样的设计模式对业务流程进行解耦和实现。 + +Raffle 抽奖模块的完整开发,让我对 SpringBoot 框架技术,分布式技术栈的运用更加熟练,也把设计模式在实际场景的使用了起来,积累了丰富的设计实现经验。这写技术学习的内容,也可以更好的应对以后的开发工作。非常感谢您给我这次面试机会。 + +## 二、简历编写 + +- 项目名称:大营销平台 - Raffle 抽奖服务 +- 项目架构:微服务架构、DDD 领域驱动模型、前后端分离设计 +- 核心技术:SpringBoot、MyBatis、MySQL、Redis、SpringCloud/Dubbo【按需添加,只是对外的接口形式】、React、TypeScript +- 项目描述:Raffle 抽奖模块是整个大营销平台系统中非常重要的一个模块,也是本次项目中我来负责的设计和实现的模块。此模块主要以支撑各类差异化抽奖流程,如;通用抽奖、黑名单、人群、N消耗积分指定抽奖范围、抽奖N次解锁奖品等各类玩法的支持。在此系统模块的设计中运用到了模板模式、责任链模式、组合模式、工厂模式,解决代码的可扩展性,并对抽奖的计算和秒杀做了设计的优化,可以支撑单机 2c4g 服务器 1500 ~ 2000 TPS 的吞吐量。「不同服务器,带宽,以及是否还配置有环境相关,会有不同的数据效果」 +- 核心职责: + - 以PRD文档诉求和对功能的评审,设计出抽奖的领域模型功能,以及在抽奖的流程抽象上,分为;抽奖前、抽奖中、抽奖后,的节点上扩展各项行为动作。如抽奖前的人群判断、抽奖中库存扣减、抽奖后兜底奖励等。 + - 依赖于领域模型的定义,设计出抽奖库表。抽象抽奖过程为抽奖策略表、策略明细表、规则配置表、规则树动作表,这样会让抽奖更好扩展。 + - 设计模板模式定义抽奖流程标准,再在模板模式中,调用责任链完成抽奖,对于抽奖中和后的动作使用组合模式的规则树进行动态处理【支持库表配置】。 + - 在项目架构中定义统一标准的 api 由触发器层实现,在触发器层定义监听、任务、http、rpc模块,所有的行为动作,都理解为触发行为。 + - 抽奖也是一种峰值流量高的业务场景,因此在设计奖品库存扣减上,采用了 Redis decr 分段消费和加锁兜底的设计,同时对于消费成功的库存,异步队列方式 + 定时任务更新库存。这样可以不超卖的同时,又减少数据库的压力。 + - 在项目开发中熟练运用了 IntelliJ IDEA、WEbStorm、Docker、MySQL、云服务器、SSH工具,并已将项目完整部署到线上【在校伙伴可以提供线上案例版】。 + +## 三、项目介绍 + +本次项目是一个 `前后端 + Dev-Ops` 实践开发真实营销业务场景的,全栈式综合编程项目。此项目会分阶段的交付,目前开发完成的是第一阶段,相当于完整项目的第一个大模块开发完成。这样可以满足小伙伴们快速学习(14天),就可以面试使用。 + +### 1. 涉及技术 + +
+ +
+ +- 在项目的分阶段开发和交付中,会逐步的运用到这些技术栈。 +- 同时对于系统架构和设计模式,也都使用功能的非常丰富。正是因为有这些东西,面试才有的讲。 + +### 2. 项目部署 + +
+ +
+ +- 小傅哥在每个阶段开发完成后,都会带着你手把手的做服务的上线。在本次阶段完成,所需的服务器配置是2c2g 占用 61% 的内存空间。—— 这些东西只有自己做了,才知道! +- 在项目部署的讲解中也会给大家扩展各种CI&CD工具和压测,让你可以从我过往工作10+年的工作积累中,吸收经验。 + +### 3. 展示效果 + +
+ +
+ +- 项目展示地址:[https://gaga.plus](https://gaga.plus) - `嘎嘎强,嘎嘎哒学` 专门展示星球「码农会锁」的实战项目效果【后续还会提供更多的不同UI的展示效果】。 +- 欢迎点击尝试,也可以模拟压测下。这套买了3年的云服务器,就是给星球伙伴部署项目玩的。 + +### 4. 系统模块 + +
+ +
+- big-market-api 定义API标准,big-market-trigger 实现出3个接口;装配策略接口(调用后将抽奖策略装配到缓存)、查询奖品列表、随机抽奖接口。 +- big-market-domain 体现了接口单一职责,抽象类的使用,子类的实现操作。【学过小傅哥的代码,你就知道什么是嘎嘎强!!!】 + +### 5. 核心流程 + +
+ +
+ +
+ +
+ +- 复杂的业务场景,都会涉及模型的拆解、库表的涉及。拆解的部分就是功能的流程的衔接点,做好解耦动作。 +- 从上面的库表中,就能看出,这一套抽奖系统,非常好扩展功能。库表设计也是非常好的经验,在面试中也经常被提起你的库表是如何设计的,新增加的功能怎么做进去。 + +## 四、项目大纲 + +**不同于网上项目,这个项目是一步步,一个个章节的带着大家从0到1的方式,进行分析、设计和开发。是一个纯手把手教大家学习实战技术的项目!** 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。 + +- 介绍 + - [大营销平台系统介绍](https://bugstack.cn/md/project/big-market/big-market.html) + - [面试:技能、简历、问题汇总](https://bugstack.cn/md/project/big-market/extra/big-market-try-it-out.html) +- 第1部分:需求文档 + - [第1节:营销场景的需求设计](https://bugstack.cn/md/project/big-market/prd/第1节:营销场景的需求设计.html) +- 第2部分:开发运维 + - [第1节:使用脚手架创建工程&PUSH代码](https://bugstack.cn/md/project/big-market/dev-ops/第1节:使用脚手架创建工程.html) + - [第2节:第一阶段完成抽奖部署](https://bugstack.cn/md/project/big-market/none.html) 【第1阶段,预计14天可以学习完成】 +- 第3部分:营销服务 + - [第1节:抽奖策略领域和库表设计](https://bugstack.cn/md/project/big-market/api/第1节:抽奖策略领域和库表设计.html) + - [第2节:基础层持久化数据](https://bugstack.cn/md/project/big-market/api/第2节:基础层持久化数据.html) + - [第3节:策略概率装配处理](https://bugstack.cn/md/project/big-market/api/第3节:策略概率装配处理.html) + - [第4节:策略权重概率装配](https://bugstack.cn/md/project/big-market/api/第4节:策略权重概率装配.html) + - [第5节:抽奖前置规则过滤](https://bugstack.cn/md/project/big-market/api/第5节:抽奖前置规则过滤.html) + - [第6节:抽奖中置规则过滤](https://bugstack.cn/md/project/big-market/api/第6节:抽奖后置规则过滤.html) + - [第7节:责任链模式处理抽奖规则](https://bugstack.cn/md/project/big-market/api/第7节:责任链模式处理抽奖规则.html) + - [第8节:抽奖规则树模型结构设计](https://bugstack.cn/md/project/big-market/api/第8节:抽奖规则树模型结构设计.html) + - [第9节:模板模式串联抽奖规则](https://bugstack.cn/md/project/big-market/api/第9节:模板模式串联抽奖规则.html) + - [第10节:不超卖库存规则实现](https://bugstack.cn/md/project/big-market/api/第10节:不超卖库存规则实现.html) + - [第11节:抽奖API接口实现](https://bugstack.cn/md/project/big-market/api/第11节:抽奖API接口实现.html) +- 第4部分:前端页面 + - [第1节:React工程创建和抽奖组件使用](https://bugstack.cn/md/project/big-market/web/第1节:React工程创建和抽奖组件使用.html) + - [第2节:Mock接口对接抽奖页面](https://bugstack.cn/md/project/big-market/web/第2节:Mock接口对接抽奖页面.html) + - [第3节:应用接口对接抽奖页面](https://bugstack.cn/md/project/big-market/web/第3节:应用接口对接抽奖页面.html) + +- 扩展部分:面试问题 + +
+ +
专门为大营销项目整你的常问面试问题,这些问题也是在小册的章节中讲解到的。小傅哥也会在后续继续编写和归档常见的面试题,助大家一臂之力!
+
+ +--- + +
+ +
+ +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。**让你学习后,直接拉开与还在玩具项目,其他求职者的差距。提高竞争力,面试脱颖而出!!!** + +## 五、加入学习 + +**注意**,加入星球「码农会锁」即可学习小傅哥所有实战项目,星球类似于私有技术朋友圈,小傅哥是技术群主,为大家提供1v1的技术学习答疑。项目地址:[https://gaga.plus](https://gaga.plus) - 你可以进入后,查看星球项目。 + +[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) **加入星球**:下载`星球APP`,从星球【课程入口】进入。里面有完整的学习指引,包括;使用说明、代码仓库、专属项目群、学习路线、往期项目。 \ No newline at end of file diff --git a/docs/md/project/big-market/extra/big-market-v2.md b/docs/md/project/big-market/extra/big-market-v2.md new file mode 100644 index 000000000..aa7b0f972 --- /dev/null +++ b/docs/md/project/big-market/extra/big-market-v2.md @@ -0,0 +1,77 @@ +--- +title: 第二阶段启动,分布式架构设计启动 +lock: no +--- + +# 《大营销平台系统》第二阶段启动,分布式架构设计,全面启动! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主,小傅哥。 + +清明假期即将来临,卷王的✋🏻手已经👨🏻‍💻 准备好啦!星球「码农会锁」第8个实战项目,《大营销平台系统》第1阶段用最基本技术栈引导小白入门,第2阶段将引入全体系的分布式技术栈,进行设计实现。—— 你们面试不总缺少分布式技术栈嘛,这回它来啦!😄 + +
+ +
+ +**不少小伙伴做过了小傅哥的实战项目,都反馈:“真牛哇🐂”** + +为啥能很牛呢,为啥和看一些其他项目感觉不一样呢?这是因为小傅哥所做的项目,背后都是数年中在大厂里;架构设计、编程开发、复杂场景问题解决方案的实战能力和经验积累。所以你每学的一个项目,就等于学了这个项目背后所承载的大规模互联网编程经验。 + +
+ +
+ +这样的学习积累,就是为你冲到这个行业腰部以上的底气。因为只要进入腰部以上,无论是实习、校招、社招,但凡走到这个圈子,都可以得到非常大的回报。 + +《大营销平台系统》是小傅哥星球「码农会锁」的第8个实战项目,全程视频手把手,除了带着敲代码,还渗透讲解了非常多的业务知识和架构思想。所以无论小白还是大白,学习起来都特别有收获。不少小伙伴都在催更!正好马上就要到了清明节了,又可以开始卷了。本次将大量开发第二阶段,并引入各项分布式技术栈,让大家知道这些分布式技术栈是如何配合使用的。 + +> 🧧 文末有加入学习方式,嘎嘎优惠! + +## 一、项目介绍 + +大营销平台是一款综合的营销服务系统,可为其他业务系统提供抽奖活动玩法的平台。包括;用户、账户、积分、兑换、抽奖、奖品发放的全体系流程服务。这类场景也是互联网大厂中极其常见的场景,就像;电商、外卖、出行、支付、现金贷等场景中,都会嵌入抽奖模块,对用户进行一个拉新和促活的目的。 + +在这个过程中你可以学习到 `前后端 + Dev-Ops` 的综合技术实战,包括;DDD 架构、设计模式、Spring、SpringBoot、MyBatis、Dubbo、RocketMQ、Redis、XXL-JOB、Sharding-JDBC、Nacos等,以及前端会使用到 React、Typescript、Ant Design Pro 等技术框架。项目开发后还有对应的 Git、Docker 部署、Grafana 监控、系统压测等内容。 + +## 二、架构方案 + +
+ +
+ +- 大营销项目到了当前的第2阶段,会把各项分布式技术栈综合运用到项目中。这些技术栈也是各个中大厂中所选择的技术栈。 +- Dubbo 用于内部微服务间的通信,Nacos 作为它的注册中心,起到负载的作用。 +- xxl-job 是分布式任务调度系统,负责任务的分发。 +- Redis 处理缓存数据、延迟队列、分布式锁。【还有库存类无锁化处理】 +- 数据库采用分布式分表设计,之后通过 binlog 使用 canal 同步数据到 Elasticsearch 提供聚合查询。 +- Zookeeper 作为动态配置使用【也可以使用其他的,这里更多的想基于 Zookeeper 把动态更新的原理体现给大家】 +- MQ 消息作为微服务内的信息流转使用。【Kafka、RocketMQ、RabbitMQ 都可以】 +- 最后是服务的负载部署,和熔断限流配置。 + +>综上这些分布式技术栈,在项目中实战锻炼一遍,也就彻底的搞清楚了分布式架构系统的设计原理和实现手段了。 + +## 三、流程设计 + +与一些网上免费demo项目的不同,大营销系统具有非常细腻业务流程。在公司真实做项目怎么做设计,就把这样细腻的流程设计展示给大家。这样读者以后进入公司做项目开发的时候,也能拿出一手漂亮的图稿、细腻的思考。 + +如图是大营销流程前面2个阶段的业务流程执行过程,从这里就能看出整个系统的设计是含带有设计模式的思考的。所以在你看到小傅哥的代码时,也会发现;“源码代码可以写的这么清晰!” 这也是你学习这样一个系统的价值。 + +
+ +
+ +- 面试中最怕的就是项目没得讲,没有一个不错的架构,也没有技术的创新,还没有设计模式对场景的解决。整个项目就像是 CRUD 把数据库的数据展示到页面,就结束了。所以面试中让讲点东西也都讲不出来。 +- 而星球「码农会锁」全部项目,都在为你灌输高级的编码架构技巧和实战设计方案。让你学习后不仅是面试,也能在工作中拿出漂亮的设计,让领导刮目相看。(这可都是一年年评价晋升、涨薪的技巧) + +> 👏🏻欢迎加入小傅哥的星球一起学习下,感受感受高质量的编码,也让自己那一圈子里腰部以上的人! + +## 四、启动学习 + +小傅哥是把星球「码农会锁」当成互联网公司中一个事业群,所需要开发的技术项目来进行构建。所以在小傅哥的星球既可以学习业务项目,还可以掌握技术组件项目。同时为了大家更好的补充项目学习中欠缺的技术点。小傅哥还把各项技术栈拆成独立的案例分享给大家。 + +>🧧星球「码农会锁」的知识体量是非常成体系的,也非常全面。加入这样一个星球,你的技术就可以稳步提升了!项目预览地址:https://gaga.plus diff --git a/docs/md/project/big-market/extra/big-market-v3.md b/docs/md/project/big-market/extra/big-market-v3.md new file mode 100644 index 000000000..2ddc2617a --- /dev/null +++ b/docs/md/project/big-market/extra/big-market-v3.md @@ -0,0 +1,159 @@ +--- +title: 第二阶段启动,分布式架构进行70% +lock: no +--- + +# 《大营销平台系统》第二阶段启动,分布式架构进行70% + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主,小傅哥。 + +我发现,其实不少伙伴并不具有分布式架构开发的经验,而学生👩🏻‍🎓不懂也是在所难免的,但那些`死鬼面试官`管你是不是学生,只要有卷王在,那就拔尖面。还有一部分伙伴一直在公司做 ERP、做B端的,就是业务流程,长、长、长、长、长,但核心技术基本没咋使用。 + +
+ +
+ +**那微服务&分布式架构,都有哪些知识点呢?要怎么学?🤔** + +微服务是把一个庞大的单体应用,拆分为独立的个体应用,每个个体应用通过分布在各个云服务器上进行分布式部署,再通过统一配置中心进行管理分布式应用节点。`这里有人提到 DDD 架构,DDD 是对每个应用软件开发的设计手段。与微服务和分布式,不相关。你可以用其他任何软件设计方法来实现一个独立的应用。` + +那么因为从单体拆分到微服务,在部署成分布式架构,也就需要了如;RPC、MQ、Redis、任务调度、分库分表、配置中心、数据同步等各项技术栈的配合使用。因而也引出了;`接口幂等`、`一致性事务`、`掉单补偿`、`延迟队列`、`延迟双删`、`任务扫描`、`熔断`、`降级`、`限流`、`全链路监控`、`TTL打标`等一些列的知识点。**如图;是小傅哥带着伙伴们基于分布式架构实践的系统。** + +
+ +
+ +那么,接下来小傅哥就基于这样一套系统,给大家介绍分布式架构中一些场景的解决方案。 + +>文末有整套项目的学习路径,涵盖;视频(每节30分钟~60分钟)、小册、代码,学起来嘎嘎有收获! + +## 一、业务介绍 + +### 1. 场景(字节掘金平台) + +为了方便大家理解这块的业务,我们可以看下字节旗下掘金平台的一款实际使用的抽奖效果。因为目前小傅哥做的这块大营销是非常全面的,完全可以支撑掘金的这套使用的。【皮肤UI随便换,电商、出行、外卖各个场景都能用】 + +
+ +
+ +- 地址:[https://juejin.cn/user/center/lottery](https://juejin.cn/user/center/lottery) + +- 流程:`每次点击签到赠送钻石`、`每天免费抽奖1次`、`200个钻石兑换1次抽奖`、`2000个钻石抽奖10次`、`1次抽奖增加10个幸运值`、`6000幸运值可以抽奖到非钻石的实物奖品`、`部分奖品需要抽奖N次后解锁`。—— 这些都是要在系统设计中考虑的点,看着可能不多,但其实满是流程。这也能看出字节研发的功底👍🏻。 + +### 2. 流程 + +基于业务场景诉求,我们来画一下整体的系统的流程。可能看着不大,但流程非常密集。没有点功底,那代码都得写的稀乱。 + +
+ +
+ +- 首先,以用户发起动作为开始,第1步为运营配置活动装配上线,第2~6步为用户参与抽奖活动动作。 +- 之后,第2~4步,为用户对积分的消耗转换为活动账户。类似于你去抓娃娃那,用钱💰换成了币子,几个币子可以抓一次娃娃。 +- 继续,用户从抽奖开始,就类似于商品下单,要扣减他的活动额度账户,比如今天可以参与1次抓娃娃,那么创建一笔流水记录。这样用户如果中途失败了,还可以用流水记录重新发起抽奖。 +- 往下,就可以进入抽奖策略环节了,也就是抽奖大转盘上体现的抽奖N次解锁、多少积分可以抽奖到哪些范围奖品【其实还有黑名单用户抽奖兜底】,这里还有库存的处理。如果2个人同时抽奖到最后1个库存的奖品,就看谁快了,占用了库存的得到奖品,另外一个只能走兜底奖品。 +- 最后,记录用户的中奖信息,和写一个 task 任务表。推送 MQ 消息的方式处理调用接口发奖和回更发奖记录。 + +>那么整个这样的业务和对应的研发流程设计,中间会有哪些细节的场景问题呢?🤔 接下来分析下。 + +## 二、场景方案 + +### 1. 为什么使用分库分表,做了分库分表数据聚合查询如何处理? + +首先我们要知道,是前期就做好分库分表方案,还是后期在做系统重构分库分表和数据迁移哪个成本高。显然是后面的成本更高。 + +而互联网中大厂中,分库分表的架构设计都是非常熟练的,因为有成熟方案,所以前期就分库分表了。但,为了解释服务器空间。所以把分库分表的库,用服务器虚拟出来机器安装。这样即不过多的占用服务器资源,也方便后续数据量真的上来了,好拆分。 + +那么这里的分库分表后的数据怎么提供汇总、聚合的查询呢? + +这里需要用到阿里的 canal 组件,基于 mysql 的 binlog 日志,把自己伪装为一个从数据库,通过 dump 交换完成数据的接收和处理。最终把数据同步到 Elasticsearch 等文件服务中在提供聚合查询。对于需要实时的查询以及数据的处理,还可以用到 Flink 方式进行流式计算。 + +### 2. 抽奖奖品库存如何处理,怎么保证最终一致性? + +在抽奖秒杀这样的场景下,都需要把库存缓存到 Redis 中进行使用。而不能数据库表加行级锁,否则大量的秒杀进行通过加锁和等待释放,就会夯住数据库链接直至拖垮整个服务。 + +那么使用缓存通过大营销项目中的颗粒度更低的分段锁后,怎么来保证一致性呢。这里需要3个步骤,首先是每次扣减完库存,都会写入到 Redis 延迟队列 / MQ 延迟消息,缓慢更新数据库库存。之后是 Redis 内的预热库存消耗完毕后,发送最终 MQ 消息,更新数据库的剩余库存为 0,最终活动结束后,还有任务补偿,扫描抽奖所产生的的参与记录单,更新最终的库存消耗。这里就可以用订单 MQ 通过 Flink 计算,更新最终库存也是可以的。 + +### 3. 写入中奖记录,发送MQ消息失败如何处理? + +本身发送MQ是可能存在万分之一或者十万分之的失败的,而数据库操作和MQ操作,本身不能做数据库事务。但又要保证失败后的补偿处理。所以要结合中奖记录在写一条发送MQ的任务记录,任务记录上有一个状态,标记是否发送完成,这样就可以通过任务扫描的方式完成 MQ 的补偿发送。 + +但这里要知道,做完本身的中奖记录和任务记录后写库事务后,要顺序的可以是多线程的方式,完成一次MQ发送,并且更新数据库 Task 记录。这里是为了业务流程最快的推进,如果是更新失败也没关系,还有兜底的任务补偿。【任务补偿的数量并不多,但非常需要这个手段】 + +### 4. 生产者可能多次发送同一个MQ,怎么保证奖品不会超发? + +这是一个幂等的设计处理,MQ 的消息是必须含带具有唯一标识的业务ID的。比如订单ID、奖品ID、支付单ID、交易单ID、贷款单ID等等。接收MQ的系统,通过唯一ID业务,更新或者写库的时候可以保证幂等性。这样也就不会产生超发的可能。 + +### 5. 抽奖算法如何提供O(1)时间复杂度,提高抽奖效率? + +在大营销系统中,运营人员配置好抽奖活动后,开始上线对外后,会进行数据的预热数据。这个预热的过程会把活动信息、策略信息、库存信息都存储到 Redis 里进行使用。 + +而抽奖的策略就是记录了一个策略下N个奖品的概率,将概率转换为对应的整数数量,写入到缓存中。那么在抽奖的时候就按照整数数量生成随机数来抽奖。这样用空间换时间的效率是非常高的。 + +### 6. 如何对所有分布式节点的应用,活动信息本地内存更新? + +通常我们会有诉求在不重启系统的时候,就要动态变更所有分布式应用节点中某个属性的值,如开关、缓存、调试日志开启/关闭、熔断、限流、或者抽奖黑名单以及概率等。这些东西通常不是 Redis 存储,而是应用中具体字段的属性值,这样效率更高。 + +而这个操作需要使用到类似于 Zookeeper 组件的临时节点监听,动态变更字段值。详细:[https://bugstack.cn/md/road-map/zookeeper.html](https://bugstack.cn/md/road-map/zookeeper.html) + +### 7. 应用刚启动完成,外部调用过程中发现操作数据库连接池不足,超时断开,过一会又好了?是什么问题? + +因为它是刚开始有问题,过一会又好了,所以很有可能是池化的连接数配置的最小值与最大值不是一个,这样应用就会先初始一个最小范围的连接数,随着调用没了在初始化到最大连接数。所以一般我们会把最小连接数和最大连接数配置为一个,避免使用的时候还需要初始化。因为初始化连接也是需要花费时间的。【再有注意配置链接的超时时间,不要太小,也不要太大】 + +### 8. 消费MQ的过程中,如果使用多线程会遇到什么问题? + +这是一个非常容易产生事故的问题,本身 MQ 消费就是多个应用分别消费,如果有消费失败的,可以抛异常重试。但如果是一个消费 MQ 的应用,里面写了多线程,就可能会出现大量的 MQ 挤压,消费不过来,导致系统瘫痪。而如果你重启,那么这些拉下来的 MQ 消息也就随时丢失了。 + +### 9. 分库分表怎么让任务扫描到指定的库表? + +分库分表以后,需要扫描每个库表中的任务表,则需要手动设定具体要扫描的库和表。如果分库分表的数量比较多,可以用不同的任务配置扫描不同的库表方式来部署,这样可以提高扫描效率。 + +如果说扫描出来的数据需要更低的延迟性,可以考虑做[低延迟任务调度设计](https://bugstack.cn/md/road-map/zookeeper.html)。 + +> 综上都是微服务&分布式架构设计中会出现的场景问题,这些东西是需要实际编码学习才能更好的理解的。更多的问题汇总:[https://bugstack.cn/md/project/big-market/notes.html](https://bugstack.cn/md/project/big-market/notes.html) + +## 三、架构演示 + +小傅哥对于这样一套微服务分布式架构做了非常多的系统方案细节设计,举例如下方便大家参考。—— 这些方案都有平均40-50分钟的视频,由浅入深,循序渐进的讲解。 + +### 1. 活动参与流程 + +
+ +
+ +### 2. 分库分表原理 + +
+ +
+ +### 3. 领取活动流程 + +
+ +
+ +>这套系统是从0到1,手把手的带着设计和编码。地址:[https://bugstack.cn/md/project/big-market/big-market.html](https://bugstack.cn/md/project/big-market/big-market.html) + +## 三、加入学习 + +《大营销平台系统》是一套微服务、分布式技术栈架构,DDD 设计的系统。全程视频手把手的教大家学习,就连 IntelliJ IDEA 快捷键也教你使用。每节视频平均在`40分钟`,课程已经录制完成26节【这一部分学习完,就够写简历面试了】 + +> 小傅哥的星球「码农会锁」有8个实战项目【大营销、OpenAI 应用、API网关、中间件等】,每个都是从0到1开发并提供简历模板和面试题,并且还在继续开发,后续还将有更多!价格嘎嘎实惠,早点加入,早点提升自己。项目地址:https://gaga.plus + +
+ +
+ +
+ +
diff --git a/docs/md/project/big-market/extra/big-market-v4.md b/docs/md/project/big-market/extra/big-market-v4.md new file mode 100644 index 000000000..61c81bf0c --- /dev/null +++ b/docs/md/project/big-market/extra/big-market-v4.md @@ -0,0 +1,138 @@ +--- +title: 第二阶段完结,兄弟们,这种形式的才是编程大课! +lock: no +--- + +# 兄弟们,这种形式的才是编程大课! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +`微服务设计`、`分布式架构`,`前端 + 后端 + 运维`,从需求分析到架构设计,从系统建模到模式设计,**从0到1**,全程视频手把手带着实现。让伙伴即学会项目开发掌握核心技术栈运用,也学会云服务器操作,编译、构建、打包、部署、监控等内容的使用。—— 这一套内容完整做下来,简历的竞争力非常强! + +
+ +
+ +**内容精良、涵盖颇丰,这是一套编程大课!** + +`小`到 IntelliJ IDEA 快捷键,Git 代码提交/合并,`中`到需求拆解编码实现,`大`到系统DDD四色建模,透视业务流程监控。一整条龙🐲的全体系内容,由浅入深,娓娓道来,即使是小白伙伴,也能跟着课程学习下来(平均一节课40分钟视频 + 对应的小册文章)。初次之外,还有简历编写,面试题整理,扩展类内容的讨论和沉淀。这样的学习不仅提升了能力,还提高了薪资待遇! + +
+ +
+ +>文末可加入项目课程学习,一次加入可立即获得11个项目学习、4套基础教程、4套技术小册、1套手写源码和86个场景设计案例。 + +## 一、课程大纲 + +大营销平台是一款综合的营销服务系统,可为其他业务系统提供抽奖活动玩法的平台。包括;用户、账户、积分、兑换、抽奖、奖品发放的全体系流程服务。这类场景也是互联网大厂中极其常见的场景,就像;电商、外卖、出行、支付、现金贷等场景中,都会嵌入抽奖模块,对用户进行一个拉新和促活的目的。 + +在这个过程中你可以学习到 前后端 + Dev-Ops 的综合技术实战,包括;DDD 架构、设计模式、Spring、SpringBoot、MyBatis、Dubbo、RocketMQ、Redis、XXL-JOB、Sharding-JDBC/DB-Router、Nacos等,以及前端会使用到 React、Typescript、Ant Design Pro 等技术框架。项目开发后还有对应的 Git、Docker 部署、Grafana 监控、系统压测等内容。 + +**介绍** + +- 大营销平台系统 +- 架构:DDD 领域驱动设计 +- 面试:技能、简历、问题汇总 + +**第1部分:需求文档** + +- 第1节:营销场景的需求设计 +- 第2节:抽奖活动场景的需求设计 +- 第3节:用户行为奖励需求设计 +- 第4节:用户积分需求设计 + +**第2部分:开发运维** + +- 第1节:使用脚手架创建工程&PUSH代码 +- 第2节:第一阶段完成抽奖部署 + +**第3部分:营销服务** + +- 第1节:抽奖策略领域和库表设计 +- 第2节:基础层持久化数据 +- 第3节:策略概率装配处理 +- 第4节:策略权重概率装配 +- 第5节:抽奖前置规则过滤 +- 第6节:抽奖中置规则过滤 +- 第7节:责任链模式处理抽奖规则 +- 第8节:抽奖规则树模型结构设计 +- 第9节:模板模式串联抽奖规则 +- 第10节:不超卖库存规则实现 +- 第11节:抽奖API接口实现 +- 第12节:用户参与抽奖活动库表设计 +- 第13节:引入分库分表路由组件 +- 第14节:抽奖活动订单流程设计 +- 第15节:抽奖活动流水入库 +- 第16节:引入MQ处理活动SKU库存一致性 +- 第17节:用于领取活动库表设计 +- 第18节:领取活动扣减账户额度 +- 第19节:写入中奖记录和任务补偿发送MQ +- 第20节:抽奖活动流程串联 +- 第21节:活动信息API迭代和功能完善 +- 第22节:用户行为返利入账 +- 第23节:用户行为返利结算 +- 第24节:规则完善和应用接口实现 +- 第25节:积分发奖服务实现 +- 第26节:积分领域调额服务 +- 第27节:积分支付兑换商品 +- 第28节:积分应用场景接口实现 + +**第4部分:前端页面** + +- 第1节:React工程创建和抽奖组件使用 +- 第2节:Mock接口对接抽奖页面 +- 第3节:应用接口对接抽奖页面 +- 第4节:活动信息API迭代和功能完善 +- 第5节:对接联调额度签到权重接口 + +## 二、实施运维 + +项目部署看似简单,但没有操作过云服务器其实并不容易完成项目的完整上线。为此小傅哥专门基于大营销,从0到1录制部署视频,让你从服务器的选配开始了解,再到环境配置、脚本执行、分阶段项目部署,一层层学习到真实的实践技能。 + +
+ +
+ +## 三、透视监控 + +如果只是做项目,那这还不能算是一个完整的全链路系统。所以小傅哥还单独的提供透视业务监控系统的设计和实现,一节课30~40分钟,给伙伴讲解监控系统设计的核心,在带着你手把手的开发一套业务透视系统。`下图就是透视的大营销抽奖流程的全链路流程节点` + +
+ +
+ +> 这些内容,都是一次加入星球就可以学习到的! + +## 四、学习日历 + +你💊知道,小傅哥是很贴心的、很细腻的,总是把内容做到极致,让兄弟们大口学习。这套编写学习日历,可以让伙伴们有目标、有计划的进行项目学习。注意;完成2个阶段就可以投递简历,星球已经提供简历模板和面试题归档。 + +
+ +
+ +## 五、加入学习 + +注意📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:大营销、OpenAI 应用、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发、支付SDK、动态线程组件等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +> 小傅哥的星球「码农会锁」有8个实战项目【大营销、OpenAI 应用、API网关、中间件等】,每个都是从0到1开发并提供简历模板和面试题,并且还在继续开发,后续还将有更多!价格嘎嘎实惠,早点加入,早点提升自己。项目地址:https://gaga.plus + +
+ +
+ +
+ +
+ +
+ +
diff --git a/docs/md/project/big-market/extra/big-market-v5.md b/docs/md/project/big-market/extra/big-market-v5.md new file mode 100644 index 000000000..81fb0f6ba --- /dev/null +++ b/docs/md/project/big-market/extra/big-market-v5.md @@ -0,0 +1,103 @@ +--- +title: 第三阶段完结,已部署上线运行! +lock: no +--- + +# 耗时7个月,47节内容,一个大项目3阶段完成上线啦! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在应对招聘的路上,常常会被面试官问;`功能流程完善吗`、`部署上线运行了吗`、`这项目和公司中的业务有关系吗`。可以说每一条都是绝杀。而这样的项目又不是随便一个小CRUD能搞定的,而是得能学习到公司级别的项目才可以。—— **小傅哥又花费了7个月的时候,完成一个大型项目3个大版本的迭代交付!** + +
+ +
+ +**说实话,这种大型项目非常耗时、耗精力!** + +为了能给粉丝伙伴交付出高质量的项目,我几乎把全部时间都放这上了😂。这样的项目 1:1 开发公司级别项目,同时又把多年的架构经验融入进去,所以在无论是需求的分析、架构的设计、编码的实现,都会看到非常多的可学到手又提升能力的内容。—— 小伙伴们评价,傅哥的这个项目,连 IntelliJ IDEA 快捷键都教!所以这次的项目不只是质量高,内容也是嘎嘎细致! + +>文末可获得项目课程&源码。一次加入可立即获得5个业务项目、6个组件项目、4套基础教程、4套技术小册、1套手写源码和90个场景设计案例。 + +## 一、上线效果 + +- 体验地址:[https://gaga.plus/](https://gaga.plus/) - 🤨 `购入3年2c8g云服务器`,让兄弟查看项目运行效果和压测! +- 同类产品:[https://juejin.cn/user/center/lottery](https://juejin.cn/user/center/lottery) - 字节旗下的掘金平台,也有一套同类的抽奖。`聪明的伙伴,已经想到了简历怎么写!` +- 课程地址:[https://t.zsxq.com/199mpn9Lt](https://t.zsxq.com/199mpn9Lt) - 包括此项目的所有学习内容 + +
+ +
+ +这是一套`功能强大又全面`,`流程复杂又细腻`的营销系统,涵盖了;`积分账户`、`签到返利`、`虚拟商品兑换`、`营销抽奖`、`抽奖解锁`、`权重`、`黑名单`、`阶梯抽奖`等。对于这样的系统可以说,但凡没有点深厚编码功底的架构师,是容易把系统给写的一片乱的。所以前端页面都会包装,但想把后端写好,那就是真功夫了! + +## 二、项目介绍 + +本次项目是一个 `产品/需求/设计 + 前后端 + DevOps` 综合性C端应用级应用场景的实战项目,从需求分析、产品方案、架构设计、编码实现、运维部署,一步步从0到1带着伙伴们学习。即使是小白,也能对照视频写代码,完成项目学习。 + +整个项目分为3个阶段,每一个阶段都等同于公司中的大版本迭代,完成每个阶段都可以独立写简历进行面试使用。 + +### 1. 系统架构 + +
+ +
+ +### 2. 核心技术 + +
+ +
+ +### 3. 项目工程 + +
+ +
+ +### 4. 项目部署 + +
+ +
+ +### 5. 运行日志 + +
+ +
+ +
+ +
+ +### 6. 透视监控 + +
+ +
+ +## 三、关于面试 + +此外,小傅哥还给大家准备了写简历的模板,以及提供简历评审服务。还包括不断归档总结的面试问题,场景方案。 + +### 1. 简历模板 + +
+ +
+ +### 2. 面试问题 + +
+ +
+ +
+ +
\ No newline at end of file diff --git a/docs/md/project/big-market/extra/big-market-v6.md b/docs/md/project/big-market/extra/big-market-v6.md new file mode 100644 index 000000000..a785172c7 --- /dev/null +++ b/docs/md/project/big-market/extra/big-market-v6.md @@ -0,0 +1,192 @@ +--- +title: 第四阶段完结,已部署上线运行! +lock: no +--- + +# 耗时9个月,1.34万行代码,这套分布式微服务架构项目,完结啦! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。、;l.34万行代码,55节课程,全程视频手把手。这套微服务、分布式、DDD架构,涵盖了;抽奖、活动、积分、兑换,运用了分库分表、binlog同步数据、分段非竞态锁、动态配置、熔断、降级、限流、Nginx负载等综合前后端,Dev-Ops大型实战项目,大营销平台系统,终于完结了!💐 + +
+ +
+ +**这次一次分布式架构大课,我希望让你学到更多!** + +在10年+的职业生涯中发现,那些简历很强、发展很顺的伙伴,基本都是在`C端场景(电商、交易、支付、信贷、营销、会员...)`、`分布式微服务`、`工程架构(DDD)`、`高并发场景方案`有着不错积累的伙伴。因为这些内容在各个中大厂,如;淘宝、美团、京东、拼多多、滴滴,都是核心业务场景。 + +所以小傅哥选取了一个大营销场景,为了需求到设计、从场景到方案、从架构到编码,循序渐进的全程视频的,一步步手把手的带着你学习积累这些经验和技能。这套内容学习中,你会获得非常强的编程思维和编码能力的提升,甚至小到 IntelliJ IDEA 快捷键,代码推送、合并、拉取,大到分布式设计和部分全程带着你一起完成! + +**传道受业,虽说很累。** 但每每看到大家的学习到东西又能拿到Offer,我也感觉很爽,又想继续分享; + +
+ +
+ +那么,接下来小傅哥就详细介绍下,本次完结的新项目,可以让大家学习到哪些知识,掌握哪些技术。 + +> 文末有加入学习方式,可以获得整套课程的;视频、文档、代码、面试题、简历模板等。 + +
+ +
+ +## 一、能学到啥 + +这是一套结合着实际应用场景的,纯纯的分布式微服务架构项目,这些技术技能知识的运用也是中大厂里非常常见的场景解决方案。所以在这样一套项目下,你会学习到一系列非常有用的技能,包括; + +- 【前端】熟练使用 React、Typescript 在前端工程中开发营销活动页。 +- 【前端】熟练掌握,跨域接口请求,以及通过浏览器指纹技术实现防刷。 +- 【前端】熟练使用,Ant Design Pro 开发后台运营管理系统。 +- 【后端】熟练搭建 DDD 工程项目、以及 DDD 脚手架搭建项目。并对 DDD 设计方法有清楚的认知。 +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对框架源码所提供的扩展接口具备运用能力。 +- 【后端】熟练运用分布式技术栈,包括:Dubbo、RabbitMQ、Redis、XXL-JOB、DB-Router、Zookeeper、Nacos、Canal、ElasticSearch等。 +- 【后端】熟练使用多种设计模式、设计原则,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。 +- 【后端】深度学习复杂场景的架构设计、编程思维,如果处理系统功能的边界和上下文的维护。—— 这些东西一定是从实践中才能学习到的。 +- 【后端】熟练使用 Mock 单测工具、JMeter 压测工具,增强代码交付质量。 +- 【后端】熟练掌握异常、枚举、错误码的定义和使用,并学习到如何合理打印服务日志,便于问题排查。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练使用 Nginx 配置转发服务,并能申请ssl配置https服务。 +- 【运维】熟练使用 Grafana 监控系统,对系统的 JVM、磁盘、Tomcat、应用(QPS、响应时间、调用量)完整监控。 + +## 二、项目介绍 + +本次项目是一个包括 `前后端 + Dev-Ops` 的全栈式综合编程实战项目,基于 React + SpringBoot + 分布式技术栈 + Nginx + Grafana + Docker 云服务,开发、部署、上线、监控的《大营销平台系统》项目。 + +该项目分为4个阶段进行设计实现,让伙伴学习到任何一个阶段都可以写到简历使用。1阶段为抽奖策略、1阶段为活动设计、3阶段用户积分、4阶段分布式架构。并为课程提供了学习日历📅,可以作为学习时长的参考。 + +>服务器参考;1阶段部署需要2c2g,2、3阶段部署需要2c4g、4阶段分布式部署需要4c16g [https://618.gaga.plus](https://618.gaga.plus) - 4c16g 测试机器1个月29元 + +### 1. 项目工程 + +
+ +
+ +### 2. 工程架构 + +
+ +
+ +### 3. 应用部署 + +
+ +
+ +部署涵盖;MySql、Redis、Zookeeper、ElasticSearch、RabbitMQ、Canal、前后端应用。后端应用部署了2套 + Nginx 负载。 + +### 4. 项目演示 + +
+ +
+ +- 涵盖了互联网C端场景的多种玩法;积分、签到、兑换、抽奖、抽奖次数锁、抽奖阶梯/权重。 +- 同类的在字节的掘金平台、京东支付完成、拼多多,也有这样的抽奖 [https://juejin.cn/user/center/lottery](https://juejin.cn/user/center/lottery) + +### 5. 系统监控 + +
+ +
+ +- 系统监控主要以请求量、QPS、TPS、响应时间、链接、负载、磁盘等为监控目标。知道一个系统运行的健康度。 + +### 6. 业务监控 + +
+ +
+ +
+ +
+ +- 业务监控,主要以用户行为发生链路为监控。通过这样的监控知道系统运行的状况,各个业务流程节点的数据是否挤压。 + +## 三、项目大纲 + +**不同于网上项目,这个项目是一步步,一个个章节的带着大家从0到1的方式,进行分析、设计和开发。是一个纯手把手教大家学习实战技术的项目!** 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。 + +### 第1部分:需求文档 + +- 1:《大营销平台系统设计实现》 - 需求文档 第1节:营销场景的需求设计 - 第1阶段「抽奖策略」开始 +- 18:《大营销平台系统设计实现》 - 需求文档 第2节:抽奖活动场景的需求设计 - 第2阶段「抽奖活动」开始 +- 30:《大营销平台系统设计实现》 - 需求文档 第3节:用户行为奖励需求设计 +- 35:《大营销平台系统设计实现》 - 需求文档 第4节:用户积分需求设计 - 第3阶段「用户积分」开始 + +### 第2部分:开发运维 + +- 2:《大营销平台系统设计实现》 - 开发运维 第1节:使用脚手架创建工程&PUSH代码 +- 17:《大营销平台系统设计实现》 - 开发运维 第2节:第一阶段完成抽奖部署 - 第1阶段完成,可部署上线。其他部署参考下面专门部署部分。 +- 41:《大营销平台系统设计实现》 - 开发运维 第3节:引入Nacos Dubbo框架 - 第4阶段「分布式技术栈」开始 + +### 第2部分:开发运维 - 扩展,从0到1,手把手部署。 + +- 第1节:服务器选配 +- 第2节:Docker安装 +- 第3节;jdk、maven +- 第4节:应用环境安装 +- 第5节:第1阶段部署 +- 第6节:第2阶段部署 +- 第7节:第3阶段部署 +- 第8节:第4阶段部署 + +### 第3部分:营销服务 + +- 3:《大营销平台系统设计实现》 - 营销服务 第1节:抽奖策略领域和库表设计 +- 4:《大营销平台系统设计实现》 - 营销服务 第2节:基础层持久化数据 +- 5:《大营销平台系统设计实现》 - 营销服务 第3节:策略概率装配处理 +- 6:《大营销平台系统设计实现》 - 营销服务 第4节:策略权重概率装配 +- 7:《大营销平台系统设计实现》 - 营销服务 第5节:抽奖前置规则过滤 +- 8:《大营销平台系统设计实现》 - 营销服务 第6节:抽奖中置规则过滤 +- 9:《大营销平台系统设计实现》 - 营销服务 第7节:责任链模式处理抽奖规则 +- 10:《大营销平台系统设计实现》 - 营销服务 第8节:抽奖规则树模型结构设计 +- 11:《大营销平台系统设计实现》 - 营销服务 第9节:模板模式串联抽奖规则 +- 12:《大营销平台系统设计实现》 - 营销服务 第10节:不超卖库存规则实现 +- 15:《大营销平台系统设计实现》 - 营销服务 第11节:抽奖API接口实现 +- 19:《大营销平台系统设计实现》 - 营销服务 第12节:用户参与抽奖活动库表设计 +- 20:《大营销平台系统设计实现》 - 营销服务 第13节:引入分库分表路由组件 +- 21:《大营销平台系统设计实现》 - 营销服务 第14节:抽奖活动订单流程设计 +- 22:《大营销平台系统设计实现》 - 营销服务 第15节:抽奖活动流水入库 +- 23:《大营销平台系统设计实现》 - 营销服务 第16节:引入MQ处理活动SKU库存一致性 +- 24:《大营销平台系统设计实现》 - 营销服务 第17节:用于领取活动库表设计 +- 25:《大营销平台系统设计实现》 - 营销服务 第18节:领取活动扣减账户额度 +- 26:《大营销平台系统设计实现》 - 营销服务 第19节:写入中奖记录和任务补偿发送MQ +- 27:《大营销平台系统设计实现》 - 营销服务 第20节:抽奖活动流程串联 +- 28:《大营销平台系统设计实现》 - 营销服务 第21节:活动信息API迭代和功能完善 +- 31:《大营销平台系统设计实现》 - 营销服务 第22节:用户行为返利入账 +- 32:《大营销平台系统设计实现》 - 营销服务 第23节:用户行为返利结算 +- 33:《大营销平台系统设计实现》 - 营销服务 第24节:规则完善和应用接口实现 +- 36:《大营销平台系统设计实现》 - 营销服务 第25节:积分发奖服务实现 +- 37:《大营销平台系统设计实现》 - 营销服务 第26节:积分领域调额服务 +- 38:《大营销平台系统设计实现》 - 营销服务 第27节:积分支付兑换商品 +- 39:《大营销平台系统设计实现》 - 营销服务 第28节:积分应用场景接口实现 +- 42:《大营销平台系统设计实现》 - 营销服务 第29节:分布式动态配置活动降级 +- 43:《大营销平台系统设计实现》 - 营销服务 第30节:分布式动态限流和熔断 +- 44:《大营销平台系统设计实现》 - 营销服务 第31节:分库分表数据同步ES +- 45:《大营销平台系统设计实现》 - 营销服务 第32节:ES-ORM多数据源配置使用 +- 46:《大营销平台系统设计实现》 - 营销服务 第33节:xxl-job分布式任务调度 + +### 第4部分:前端页面 + +- 13:《大营销平台系统设计实现》 - 前端页面 第1节:React工程创建和抽奖组件使用 +- 14:《大营销平台系统设计实现》 - 前端页面 第2节:Mock接口对接抽奖页面 +- 16:《大营销平台系统设计实现》 - 前端页面 第3节:应用接口对接抽奖页面 +- 29:《大营销平台系统设计实现》 - 前端页面 第4节:活动信息API迭代和功能完善 +- 34:《大营销平台系统设计实现》 - 前端页面 第5节:对接联调额度签到权重接口 +- 40:《大营销平台系统设计实现》 - 前端页面 第6节:对接联调积分流程接口 - 第3阶段完成 + +> 课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! + +
+ +
diff --git a/docs/md/project/big-market/extra/big-market-v7.md b/docs/md/project/big-market/extra/big-market-v7.md new file mode 100644 index 000000000..920eb2c29 --- /dev/null +++ b/docs/md/project/big-market/extra/big-market-v7.md @@ -0,0 +1,86 @@ +--- +title: 第五阶段完结,微服务对接 +lock: no +--- + +# 第五阶段完结,微服务对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +哎,你这一个电商/点评项目,一个营销活动项目,怎么没有两个项目的对接。你的营销活动可以给你的电商提供抽奖、积分、返利、兑换等等,但为啥都是独立存在的?你有思考过他们是怎么对接的吗? + +
+ +
+ +**那微服务的对接,你有经验吗?** + +没有🤔?没关系,小傅哥为你实打实的提供两套微服务项目的对接使用,包括;后端对接、前端对接,以及不同方式的接口形式对接方案。在这个过程中让你搞懂,这些微服务的对接思路、方式和落地手段。有了这些经验后续你也可以把这样的微服务,衔接到各种项目了上了。让你的项目成为一个蜘蛛网🕸一样的微服务布局。 + +本篇文章提到的两套微服务分别是;`OpenAI 应用项目`、`大营销系统服务 - 抽奖、积分、兑换、返利`,这两套微服务都是小傅哥在星球「码农会锁」为大家提供的实战项目,本节为大家提供出对接方案和效果。非常提高综合实战能力。 + +> 文末有加入学习方式,可以获得整套课程的;视频、文档、代码、面试题、简历模板等。 + +## 一、项目简述 + +- **OpenAi 应用项目**:前后端+Dev-Ops,全栈技术实践。以OpenAi场景,运用DDD架构,实现从微信公众号扫码登录,到下单支付对个人账户充值,完成 OpenAi 控制敏感词过滤以及多渠道策略模型的对话消费的全流程。 +- **大营销平台系统**:前后端+Dev-Ops,全栈技术实践。项目将先以最新DDD架构和模式进行重构Lottery项目,扩展用户、账户、积分、兑换、分享模块,以及和 OpenAI 进行微服务整合,RPC/HTTP 通信完成项目综合使用。 + +>项目演示地址:[https://gaga.plus](https://gaga.plus) - 你可以直接在线查看项目的演示运行效果。这些项目都是企业级的设计和实现,远比简单的 CRUD 那种 DEMO 项目要牛皮的多! + +## 二、对接方案 + +OpenAI 应用项目,提供了`支付购买对话额度`的服务。那么我们为了促进用户在 OpenAI 项目上的使用粘性,就可以添加营销模块。让用户在完成`每日签到和支付`后,就可以获得相应的积分,再通过积分兑换抽奖次数完成抽奖。抽奖有机会获得不同级别的`对话额度`赠送。以此完成用户行为旅程循环。 + +
+ +
+ +- OpenAI 前端应用,通过 Nginx 负载,HTTP 方式调用 OpenAI 后端和大营销后端服务。完成前端的UI的业务流程串联。 +- OpenAI 后端应用,在支付完成后,调用大营销的返利接口。大营销可以配置完成某个用户行为动作后对应给到积分或者抽奖次数。这个对接可以是 HTTP 或者 RPC 对接。 +- 大营销平台系统,在抽奖的时候增加 OpenAI 额度奖品。也就是抽奖后可以给一个n次数的对话额度,所以大营销需要对接到 OpenAI 提供的 HTTP 接口,处理额度奖品发放操作。 + +## 三、对接效果 + +OpenAI + 大营销,这是一套综合`前后端 + DevOps`的全体系技术实战项目。采用 DDD 架构设计落地,运用设计模式编写整洁的代码实现。结合 OpenAi 技术、微信支付渠道、敏感词过滤、营销积分、兑换、返利的综合玩法项目。 + +### 1. 营销页 + +
+ +
+ +- 在 OpenAI 页面,增加营销活动模块,展示出用户的额度、积分、签的到、抽奖次数。以及抽奖和奖品兑换操作。 +- 这些内容都是可以通过配置完成的,调整用户的获得积分的大小、以及兑换商品是的价值,再有就是抽奖的概率配置。可以非常灵活的调整,让用户非常喜欢这样一套产品。 + +### 2. 支付页 + +
+ +
+ +
+ +
+ +- 用户可以通过下单支付的方式完成商品的购买,购买后就可以对购买支付的回调,进行返利积分或者抽奖额度。 +- 星球的课程体系还提供了,微信支付、支付宝沙箱、蓝兔支付,多种方式都可以按需使用。 + +> 关于项目的更多了解:[https://bugstack.cn/md/project/chatgpt/review.html](https://bugstack.cn/md/project/chatgpt/review.html) + +## 四、加入学习 + +小傅哥的星球「码农会锁」提供了一整套的实战项目学习进阶路线,从小白到大佬,全程视频手把手带着从0到1,一步步完成项目的设计、开发和上线。在整套内容学习过程中,小傅哥为你提供了非常好的技术交流社群,及时解决学习问题。还包括调试你的问题代码,带你快速🔜出坑! + +
+ +
+ +- 首先,这一整套全体系的学习课程比私教培训实惠,更比培训班上万的培训费便宜。可能也就是培训班1天的💰钱,就能学习到这一整套内容了。 +- 之后,你学习的整套课程,就是小傅哥这个架构师自己全部原创编写的。这也就是说,你所提到的任何问题,小傅哥都能给你解答和讨论。 +- 那么,这么实惠的课程,成体系的课程,还是架构师编写的。还有什么可犹豫的,完全可以撸起来了! diff --git a/docs/md/project/big-market/none.md b/docs/md/project/big-market/none.md new file mode 100644 index 000000000..f64218b2f --- /dev/null +++ b/docs/md/project/big-market/none.md @@ -0,0 +1,15 @@ +--- +title: 新章节,编写中 +lock: no +--- + +# 新章节,编写中 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +... 新章节,编写中 \ No newline at end of file diff --git a/docs/md/project/big-market/notes.md b/docs/md/project/big-market/notes.md new file mode 100644 index 000000000..6cac8d019 --- /dev/null +++ b/docs/md/project/big-market/notes.md @@ -0,0 +1,365 @@ +--- +title: 面试:技能、简历、问题汇总 +lock: no +--- + +# 《大营销平台系统》,关于面试中的技能、简历、问题汇总 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/17gswKIeX](https://t.zsxq.com/17gswKIeX) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +此部分主要用于向读者提供星球项目之一的《大营销平台系统》项目如何体现到简历中,包括;专业技能、项目经验。 + +## 一、项目介绍 + +面试官您好,大营销平台的 Raffle 抽奖/积分/兑换/返利模块,是我独立负责实现的一个(学习/工作)项目,此项目模块在架构设计上运用了 DDD 分层架构和模板模式、责任链模式、组合模式、工厂模式等,这样的设计模式对业务流程进行解耦和实现。 + +Raffle 抽奖/积分/兑换/返利,模块的完整开发,让我对 SpringBoot 框架技术,分布式技术栈的运用更加熟练,也把设计模式在实际场景的使用了起来,积累了丰富的设计实现经验。这些技术学习的内容,也可以更好的应对以后的开发工作。非常感谢您给我这次面试机会。 + +## 二、简历模板 + +**注意**:🙅🏻‍♀️不要直接复制粘贴简历模板内容,以此结构和描述方式,用个人第1学习视角来描述。项目课程还包括;活动、积分、兑换、返利、分布式应用内容。学习到每个阶段后,可以写简历。写好自己的简历后可以使用 [openai 4o模型](https://openai.itedus.cn) 告诉它你是一个 Java 技术专家,请根据描述内容,重新使用专业技术术语编写简历。几次下来,简历内容会非常好。 + +- 项目名称:大营销平台 - Raffle 抽奖/积分/兑换/返利服务 - 按需起名,可以是积分玩法,用户虚拟资产运营平台、积分返利抽奖服务等 +- 项目架构:微服务架构、DDD 领域驱动模型、前后端分离设计 +- 核心技术:SpringBoot、MyBatis、MySQL、Redis、SpringCloud/Dubbo【按需添加,只是对外的接口形式】、React、TypeScript +- 项目描述:Raffle 抽奖模块是整个大营销平台系统中非常重要的一个模块,也是本次项目中我来负责的设计和实现的模块。此模块主要以支撑各类差异化抽奖流程,如;通用抽奖、黑名单、人群、N消耗积分指定抽奖范围、抽奖N次解锁奖品等各类玩法的支持。在此系统模块的设计中运用到了模板模式、责任链模式、组合模式、工厂模式,解决代码的可扩展性,并对抽奖的计算和秒杀做了设计的优化,可以支撑单机 4c16g 服务器 500 ~ 800 TPS 的吞吐量(**参考值,以实际压测为准**),抽奖接口响应时长`45毫秒~100毫秒`左右。「不同服务器,带宽,以及是否还配置有环境相关,会有不同的数据效果」 +- 核心职责: + - 以PRD文档诉求和对功能的评审,设计出抽奖的领域模型功能,以及在抽奖的流程抽象上,分为;抽奖前、抽奖中、抽奖后,的节点上扩展各项行为动作。如抽奖前的人群判断、抽奖中库存扣减、抽奖后兜底奖励等。 + - 依赖于领域模型的定义,设计出抽奖库表。抽象抽奖过程为抽奖策略表、策略明细表、规则配置表、规则树动作表,这样会让抽奖更好扩展。 + - 设计模板模式定义抽奖流程标准,再在模板模式中,调用责任链完成抽奖,对于抽奖中和后的动作使用组合模式的规则树进行动态处理【支持库表配置】。 + - 在项目架构中定义统一标准的 api 由触发器层实现,在触发器层定义监听、任务、http、rpc模块,所有的行为动作,都理解为触发行为。 + - 抽奖也是一种峰值流量高的业务场景,因此在设计奖品库存扣减上,采用了 Redis decr 分段消费和加锁兜底的设计,同时对于消费成功的库存,异步队列方式 + 定时任务更新库存。这样可以不超卖的同时,又减少数据库的压力。 + - 在项目开发中熟练运用了 IntelliJ IDEA、WEbStorm、Docker、MySQL、云服务器、SSH工具,并已将项目完整部署到线上【在校伙伴可以提供线上案例版】。 + - 项目采用分布式架构设计,Dubbo 提供 RPC 接口,Nacos 作为注册中心。多机实例任务由 xxl-job 进行调度管理。分库分表数据通过 canal 同步 binlog 日志到 ElasticSearch 进行聚合查询。 + - 通过 RabbitMQ 解耦系统流程,包括;抽奖发奖流程、异步调账消息、异步行为返利,以及在 Redis 缓存库存消耗完毕后,推送MQ消息更新数据库库存和清空延迟队列。 + +## 三、面试问题 + +小傅哥本身也是一个面试官,面试过实习、校招、社招、架构师,不同的面试诉求会有不同的考察范围。通过这些考察范围的问题让求职者通过案例举证,来阐述自己的能力项。而这些能力项的匹配,则是招聘的要求。这里不得不提到,有些小卡拉米的简历,只言片语的项目描述,浮皮潦草的个人职责。是真的没进入面试就没了! + +接下来是关于大营销平台项目的面试问题解答,可以参考。 + +- 架构设计:[https://www.bilibili.com/video/BV17x4y187nS](https://www.bilibili.com/video/BV17x4y187nS) +- 设计模式:[https://www.bilibili.com/video/BV1f2421M7kc](https://www.bilibili.com/video/BV1f2421M7kc) +- 设计模式(扩展):[https://www.bilibili.com/video/BV1oYs3ejENB](https://www.bilibili.com/video/BV1oYs3ejENB) +- 分布式问题:[https://www.bilibili.com/video/BV1Cr42137de](https://www.bilibili.com/video/BV1Cr42137de) + +### 1. 你的项目工程是怎么搭建的? + +项目的搭建类似于使用 start.spring.io 脚手架创建的,在我们的项目中,使用了统一 maven-archetype-plugin 插件,自定义了一套 DDD 工程骨架脚手架。同类项目都是使用这套脚手架创建项目,因为脚手架定义了统一的版本标准和对应配套的开发环境,所以使用起来更加容易。【如果延伸提问,你还了解哪种搭建脚手架的方式,可以回答 [FreeMarker](http://freemarker.foofun.cn/)】 + +### 2. 在你的这个项目中,都用到了什么开发工具?项目是怎么部署的 + +使用到了 IntelliJ IDEA、WebStorm、Sequel Ace/Navicat、ApiPost、Docker、SwitchHosts、Termius(SSH)等,项目上线的时候使用了2个方式,一个是本地构建前后端镜像,PUSH 到 Docker Hub,再通过编写 docker compose 脚本,在云服务器部署。另外一个是搭建 Jenkins 配置部署流水线的方式进行部署。 + +### 3. 整个项目开发过程中,你熟练的掌握了哪些技术栈 + +在整个项目开发中熟练的使用了 SpringBoot、MyBatis、Redisson 等技术框架,在编程功能实现,熟练的结合与 Spring 容器,通过 Map 自动装配 Java 语言实现的策略模式。以及在项目中较多的使用了 Redisson 框架,向 Redis 写入 key-value、queue、map、lock 等数据类型,实现业务功能。 + +### 4. 在项目开发过程中,你有遇到过哪些运行时异常,怎么排查解决的 + +如刚开始项目开发引入脚手架以外的组件,进行调试的时候,因为Jar版本不同。出现过编译通过,调用的时候方法不存在。通过 Maven Helper 插件,检查到有其他组件多引入了相同Jar包。另外还有一些如开发调试中发现的空指针问题,如查后需要增加空对象判断。此外其他一些更多是功能流程实现细节上,如项目中的规则树节点判断流程问题,抛出一些自定义的异常。这些通过在方法上断点调试逐步的解决。 + +### 5. 因为你的项目是前后端分离的,接口跨域怎么做的? + +首先我们知道,Web跨域(Cross-Origin Resource Sharing,CORS)是一种安全机制,用于限制一个域下的文档或脚本如何与另一个源的资源进行交互。这是一个由浏览器强制执行的安全特性,旨在防止恶意网站读取或修改另一个网站的数据,这种攻击通常被称为“跨站点脚本”(Cross-Site Scripting,XSS)。 + +所以在我的前后端分离项目中,通过配置 @CrossOrigin 注解来解决跨越问题。开发阶段 `Access-Control-Allow-Origin: *`、上线阶段 `Access-Control-Allow-Origin: https://gaga.plus` + +### 6. 看到你的项目用到了 DDD,这也是我们很感兴趣的技术点,你可以介绍下你在使用 DDD 做这个项目时,都运用了 DDD 哪些知识。 + +DDD 是一种软件设计方法,软件设计方法涵盖了范式、模型、框架、方法论等内容,而 DDD 的很多规范约定,都是为了提高工程交付质量。如几个很重要的知识点;框架分层结构、领域、实体、聚合、值对象、依赖倒置等。它所有的手段,都希望以一个功能逻辑的实现为聚合,将功能所需的对象、接口、逻辑,按照领域划分到自己的领域内。 + +就像在这个项目中,我负责实现的抽奖中的策略,就是一个独立的领域模型。在这个领域中我需要提供策略的装载、随机数算法计算、抽奖模板调用(含责任链和规则树)功能,这样一个领域就像划分好的一个独立个体,它拥有属于它的对象信息(实体、值对象、聚合),当需要使用数据库资源、缓存资源,以及外部接口资源的时候,都通过依赖倒置进行调用。也就是说,我的领域不做其他模块的引入,而是领域只负责业务功能实现,所需的所有数据,则有外部接口通过依赖倒置提供。[更多理论知识](https://t.zsxq.com/17tqSU5CE) + +### 7. 你的抽奖流程中,哪些被定义为值对象,哪些被定义为实体对象 + +在 DDD 的规范定义中,值对象通常用于描述对象属性的值,不具备唯一ID,不影响数据变化。如;数据库中字段的枚举值、业务流程中属性对象。如抽奖流程中,RuleLimitTypeVO 规则限定方式的枚举值对象、还有 RuleTreeVO 规则树值对象。而那些实体对象,则具备唯一ID,会影响到最后的写库动作。如;抽奖发起实体、奖品信息实体对象。并且我们可以把一些和实体对象相关的功能聚合到对象内,这样的通用性会更好,避免所有调用方都需要自己编写逻辑。 + +### 8. 关于访问数据层的依赖倒置,是怎么使用的,有什么好处,你可以描述下吗 + +DDD 中的依赖倒置是一个非常好的设计,尤其是与 MVC 结构对比的时候,MVC 的贫血模型结构设计,数据库持久化对象,很容易被当做业务对象使用,这样后期非常难维护。但在 DDD 的分层结构用,是以 domain 领域实现为核心,一个 domain 领域下所需的外部服务,都由领域层定义接口,让基础层做具体实现。而数据库持久化操作,定义的 PO 对象,就被这样的方式被限定在基础层了,外部是没法引入使用的,也就天然的防止了数据库持久化对象进入业务中。 + +### 9. 我看你简历有提到,把抽奖划分为抽奖前、中、后,三个动作。请具体结合场景讲解下,为什么这样设计 + +这个的设计得益于在 Spring/MyBatis 框架源码的学习,在源码中经常会出现对一个流程进行拆分解耦,流程可扩展的点,如 Spring 是 Bean 对象的拆解,MyBatis 是会话流程的拆解。所以在设计大营销的抽奖模块时,对于需求中的各类功能点;黑名单抽奖、权重抽奖、默认抽奖、抽奖N次解锁、兜底抽奖等等情况,是可以拆解为抽奖前、中、后,3个行为动作的,基于这样的考虑后,就可以设计出非常容易扩展的松耦合结构。 + +### 10. 是什么场景下使用了责任链模式,什么场景使用了组合模式,为什么? + +在设计完抽奖前、中、后,搜耦合的结构模型后,对于抽奖前要执行哪种抽奖,但单向选择问题。所以这里使用了责任链模式,进行节点流程判断,从黑名单、权重,最后到默认,走一个单独的具体抽奖,所以使用责任链更为合适。 + +之后是进入抽奖的中和后,这两部的流程是相对复杂的,需要判断用户抽奖了几次,对于不同次会限定是否能获得某个奖品,同时还有库存的扣减,如果库存不足或者不满足n次抽奖得到某个奖品,则会进行兜底。那么这就是一个树规则的交叉流程,所以会使用了组合模式构建一颗规则树,并通过数据库表的动态配置决定在抽奖前完成后,后续的流程要如何进行。 + +[视频讲解](https://www.bilibili.com/video/BV1f2421M7kc) + +### 11. 抽奖也是一种瞬时峰值很高的业务场景,那么对于抽中奖品后的库存扣减是怎么做的? + +关于库存的扣减,是一个非常重要的流程。尤其是这种单独资源竞争的场景,如果设计的不好,很容易把服务打挂。 + +所以在这套系统设计中,为了避免库存扣减直接更新库表的行级锁,而导致大量的用户进行等待状态。所以把数据库表的库存同步到 Redis 缓存中,在通过 decr 扣减(incr 的话就和总量比)的方式进行消费,同时为了确保在临界状态、库存恢复、异常处理等情况下不超卖,而对每一条产生从 incr 值,与抽奖的策略ID组合一个key,进行 setnx 加锁兜底,来保证不超卖。—— 这样的设计是颗粒度更小的锁方案设计,性能接近于无锁化。 + +### 12. 你讲到库存的扣减是通过 Redis 滑块锁实现的👍🏻,那么最终同步库是怎么做的,怎么降低对数据库的压力的? + +关于 redis 缓存和数据库表库存数据的流程,设计了异步更新,保持最终一致性的设计。在执行完库存的扣减操作后(在抽奖中规则树库存节点流程),发送一个扣减完成到 Redis 的异步队列(可以使用MQ+延迟消费),之后通过定时 Schedule Job 来消费队列。这样就可以控制效率速率,降低对数据库的压力。(因为我们不能 Redis 扣减的多快,就直接打到库表上,那样对数据库的压力依然很大,容易打挂) + +### 13. 你提到了接口的单一职责设计,这部分具体讲解下。 + +单一职责原则的核心思想是,一个类应该只有一个引起它变化的原因。也就是说一个类应该只负责一项任务或功能,如果一个类承担了过多的职责,那么这个类就会变得复杂,难以维护和扩展。 + +这样的原则在一些需要长期使用、迭代、维护的功能设计上,是非常重要的。我们要尽可能的让大营销的抽奖领域领域模块具备独立性,所以要使用单一职责原则。在这个原则约束下,设计了3个接口类;抽奖策略接口、奖品信息接口、库存处理接口(异步扣减等),这样3个接口的设计,在将来需要扩展的时候,会非常容易。(可能会问具体编码,问的比较多样性,这部分需要自己阅读代码来学习) + +### 14. 在项目中你提到了可以支持不同场景的抽奖诉求,比如;多少积分后可以抽奖一个固定范围的奖品,或者抽奖n次后,才可以中奖某个奖品。这部分你是怎么做的?库表怎么设计的? + +这块的流程,就是前面关于大营销抽奖领域模型的设计,从而确定的库表设计。也就是常说的领域->驱动设计。 + +库表包括;策略表、策略明细(库存、概率、规则key)、奖品表、规则表、规则树(3个表)—— 这部分在学习项目后,需要具备能在纸上画出库表ER图。 + +### 15. 你的抽奖接口响应时间是多少? + +这样的问题主要考察你是否做了项目的上线,以及了解过接口的响应时间。如果做过就非常好回答,没做过乱说是挺容易被继续提问的。 + +参考数据;2c2g 云服务器,部署项目(含mysql、redis),占用63%内存,抽奖接口响应时间为38~55毫秒(项目有完整的手把手部署教程,还有监控部署教程,可以自己部署验证)。 + +### 16.(开放问题)你在做项目中,什么问题难住你的时间最长,为什么? + +这是一个开放问题,重点考察你对项目的开发中个人的积累。你可以针对自己的学习过程中,有哪个流程的实现,让你最为有感触,即可回答。 + +如;可以对大营销抽奖模型流程的设计和库表设计,最为耗时,因为你不断的在思考如何拆解出一个好扩展的松耦合结构,同时拆解后,还要保证搜耦合下的高内聚。所以这部分是比较耗时的。同时也可以说在设定某个方法的,名称、入参、出参时,做了大量的思考。因为名字的定义非常影响以后的理解。好的代码就是文档,所以对于这样的东西花费不少时间。 + +### 17. 为什么使用分库分表,做了分库分表数据聚合查询如何处理? + +首先我们要知道,是前期就做好分库分表方案,还是后期在做系统重构分库分表和数据迁移哪个成本高。显然是后面的成本更高。 + +而互联网中大厂中,分库分表的架构设计都是非常熟练的,因为有成熟方案,所以前期就分库分表了。但,为了节省服务器空间。所以把分库分表的库,用服务器虚拟出来机器安装。这样即不过多的占用服务器资源,也方便后续数据量真的上来了,好拆分。 + +那么这里的分库分表后的数据怎么提供汇总、聚合的查询呢? + +这里需要用到阿里的 canal 组件,基于 mysql 的 binlog 日志,把自己伪装为一个从数据库,通过 dump 交换完成数据的接收和处理。最终把数据同步到 Elasticsearch 等文件服务中在提供聚合查询。对于需要实时的查询以及数据的处理,还可以用到 Flink 方式进行流式计算。 + +### 18. 抽奖奖品库存如何处理,怎么保证最终一致性? + +在抽奖秒杀这样的场景下,都需要把库存缓存到 Redis 中进行使用。而不能数据库表加行级锁,否则大量的秒杀进行通过加锁和等待释放,就会夯住数据库链接直至拖垮整个服务。 + +那么使用缓存通过大营销项目中的颗粒度更低的分段锁后,怎么来保证一致性呢。这里需要3个步骤,首先是每次扣减完库存,都会写入到 Redis 延迟队列 / MQ 延迟消息,缓慢更新数据库库存。之后是 Redis 内的预热库存消耗完毕后,发送最终 MQ 消息,更新数据库的剩余库存为 0,最终活动结束后,还有任务补偿,扫描抽奖所产生的的参与记录单,更新最终的库存消耗。这里就可以用订单 MQ 通过 Flink 计算,更新最终库存也是可以的。 + +### 18. 写入中奖记录,发送MQ消息失败如何处理? + +本身发送MQ是可能存在万分之一或者十万分之的失败的,而数据库操作和MQ操作,本身不能做数据库事务。但又要保证失败后的补偿处理。所以要结合中奖记录在写一条发送MQ的任务记录,任务记录上有一个状态,标记是否发送完成,这样就可以通过任务扫描的方式完成 MQ 的补偿发送。 + +但这里要知道,做完本身的中奖记录和任务记录后写库事务后,要顺序的可以是多线程的方式,完成一次MQ发送,并且更新数据库 Task 记录。这里是为了业务流程最快的推进,如果是更新失败也没关系,还有兜底的任务补偿。【任务补偿的数量并不多,但非常需要这个手段】 + +### 19. 生产者可能多次发送同一个MQ,怎么保证奖品不会超发? + +这是一个幂等的设计处理,MQ 的消息是必须含带具有唯一标识的业务ID的。比如订单ID、奖品ID、支付单ID、交易单ID、贷款单ID等等。接收MQ的系统,通过唯一ID业务,更新或者写库的时候可以保证幂等性。这样也就不会产生超发的可能。 + +### 20. 抽奖算法如何提供O(1)时间复杂度,提高抽奖效率? + +在大营销系统中,运营人员配置好抽奖活动后,开始上线对外后,会进行数据的预热数据。这个预热的过程会把活动信息、策略信息、库存信息都存储到 Redis 里进行使用。 + +而抽奖的策略就是记录了一个策略下N个奖品的概率,将概率转换为对应的整数数量,写入到缓存中。那么在抽奖的时候就按照整数数量生成随机数来抽奖。这样用空间换时间的效率是非常高的。 + +### 21. 如何对所有分布式节点的应用,活动信息本地内存更新? + +通常我们会有诉求在不重启系统的时候,就要动态变更所有分布式应用节点中某个属性的值,如开关、缓存、调试日志开启/关闭、熔断、限流、或者抽奖黑名单以及概率等。这些东西通常不是 Redis 存储,而是应用中具体字段的属性值,这样效率更高。 + +而这个操作需要使用到类似于 Zookeeper 组件的临时节点监听,动态变更字段值。详细:[https://bugstack.cn/md/road-map/zookeeper.html](https://bugstack.cn/md/road-map/zookeeper.html) + +### 22. 应用刚启动完成,外部调用过程中发现操作数据库连接池不足,超时断开,过一会又好了?是什么问题? + +因为它是刚开始有问题,过一会又好了,所以很有可能是池化的连接数配置的最小值与最大值不是一个,这样应用就会先初始一个最小范围的连接数,随着调用没了在初始化到最大连接数。所以一般我们会把最小连接数和最大连接数配置为一个,避免使用的时候还需要初始化。因为初始化连接也是需要花费时间的。【再有注意配置链接的超时时间,不要太小,也不要太大】 + +### 23. 消费MQ的过程中,如果使用多线程会遇到什么问题? + +这是一个非常容易产生事故的问题,本身 MQ 消费就是多个应用分别消费,如果有消费失败的,可以抛异常重试。但如果是一个消费 MQ 的应用,里面写了多线程,就可能会出现大量的 MQ 挤压,消费不过来,导致系统瘫痪。而如果你重启,那么这些拉下来的 MQ 消息也就随时丢失了。 + +### 24. 分库分表怎么让任务扫描到指定的库表? + +分库分表以后,需要扫描每个库表中的任务表,则需要手动设定具体要扫描的库和表。如果分库分表的数量比较多,可以用不同的任务配置扫描不同的库表方式来部署,这样可以提高扫描效率。 + +如果说扫描出来的数据需要更低的延迟性,可以考虑做[低延迟任务调度设计](https://bugstack.cn/md/road-map/zookeeper.html)。 + +> 综上都是微服务&分布式架构设计中会出现的场景问题,这些东西是需要实际编码学习才能更好的理解的。更多的问题汇总:[https://bugstack.cn/md/project/big-market/notes.html](https://bugstack.cn/md/project/big-market/notes.html) + +### 25. 如果在多机部署的情况下,是不是每台机器都会有这个定时任务,如果它们都捞到同一条发送失败的消息,会不会导致消息的重复发送?怎么避免? + +问题:[https://t.zsxq.com/4uoyl](https://t.zsxq.com/4uoyl) + +1. 幂等的这个是ok的,没问题的。 +2. 一个任务就是要有多机备份,避免一个挂了,就没有人执行了。之后这里的方案是加锁; + - 2.1 设计一个抢占锁,多个任务抢占同一个锁,谁抢占到了,谁可以执行。 + - 2.2 如果抢占的执行失败了,删掉锁,重新执行。 + - 2.3 如果删锁失败,对于是谁抢占的,谁可以做重入锁,继续执行。 + - 2.4 锁有失效时间,如果抢占到的自己挂了,等待锁失效后,重新轮候抢占。 + +### 26. 抽奖服务量化人群指标是什么?拉新 留存 促活吗 感觉这些数据好难算 面试官问留存率 拉新成本 我都不知道怎么答 + +量化人群,是量化分析师,通过 Python、R 语言对历史数据进行建模(R语言有数据分析公式)。目的是为了根据一个人的,人标签和行为数据,跑出谁喜欢xxx,谁爱购买xxx,谁有xxx意向。之后创建出人群包。这样系统会根据人群包做抽奖、发券等的差异化。 + +### 27. 有个问题想确认一下 大营销系统和业务系统 是在同一个注册中心里吗,业务系统是直接调用抽奖服务的dubbo接口么 + +一个公司里,rpc 都是一个注册中心。 + +### 28. 关于幂等和流程解耦 + +- Q1: rabbitMQ判断重复消费的逻辑是什么,是直接在数据库中查询返利记录表是否有相同的订单ID(这个订单ID对应着当天日期?)的记录,如果发现重复就不消费? +- Q2: 感觉“签到返利”这一块也有点像幂等性的实现,即可能会执行多次签到动作,但每天只会执行一次返利操作? +- Q3: 对于“签到返利”这种行为,用MQ解耦是不是太小题大做了。首先它并不是什么高流量的行为,没必要用MQ来削峰填谷;其次,这样会不会导致执行了 签到 操作之后,用户积分得隔较长一段时间才会更新,出现数据不一致的情况? + +回答:[https://t.zsxq.com/zbUUb](https://t.zsxq.com/zbUUb) + +### 29. 前几天面试官问我策略领域和奖品领域为什么要划分开 + +1. 这部分要从建模讲,最开似的系统建模是整理出用户用例图,根据用例图梳理四色建模的领域事件。[四色建模过程](https://bugstack.cn/md/project/big-market/ddd.html) +2. 在领域事件脑暴完成,之后就是识别领域角色和对象,这个过程会显而易见的发现有抽奖领域、发奖领域。他们可以作为解耦设计,独立使用。因为抽奖不一定发奖,抽奖可以独立提供算法结果,由外部其他系统使用。发奖也可以除了抽奖的发奖,还有积分兑换的发奖等。如果抽奖和发奖合并,那么外部调用就会不那么清晰。 + +### 30. 根据抽奖问题问了如果把redis中 滑块锁过期时间设置为活动过期时间的时候,如果活动时间很长导致滑块锁过多怎么解决 + +可以考虑给活动库存的锁的key上年月日,每个日的key,明天就重新从新的key开始了。之后这样就比较好较短时间存储了。 [更多讨论](https://t.zsxq.com/vNy0U) + +### 31. 分布式锁使用场景 + +项目中有两个地方使用了分布式锁,分别来处理库存的抢占竞争和分布式任务调度的抢占。 + +1. 库存的抢占设计的是接近于无锁化的库存编号自增后加锁,做兜底设计,这样的用户的抢占就是 incr 后的结果加锁,降低竞争。 +2. 另外一个是项目是分布式架构,有多个任务执行(补偿mq、流转订单状态等),之后如2个任务,一起补偿发mq,避免发送多了,就会做一个抢占设计。谁先拿到可执行key,那么这个任务就执行。这样确保了,一个任务挂了,也可以有另外任务做处理。 + +## 四、公司面试 + +### 【美团】1. 领域模型是怎么设计的,抽奖过程是什么样,DDD四层架构和职责,以及为什么要这么设计?少卖和超卖。 + +问题:[https://t.zsxq.com/180Qz9WCg](https://t.zsxq.com/180Qz9WCg) + +1. 虽然目前大营销是开发了第1个阶段,但在前面讲解中把所有的流程是都介绍了。领域模型官方话术是头脑风暴,罗列事件和行为,在根据实体来划分领域的。简单说也就是根据业务流程划分的。这个过程包括;活动域、抽奖域、积分域、兑换域。也就是用户通过某种行为记录,发放计算,兑换活动参与资格,完成抽奖获得奖品。 +2. 抽奖的流程可以根据目前的设计实现来描述,举例,掘金的这个抽奖就可以用星球的大营销来实现;https://juejin.cn/user/center/lottery?from=lucky_lottery_menu_bar 可以多体验下,体验透彻了,在结合项目的实现,也就能描述出来了。 +3. 关于 DDD 的分层结构介绍,和每层的关系;https://bugstack.cn/md/road-map/mvc2ddd.html +4. 超卖不会出现,有个保证的点,一个是 decr 值的限制,另外一个是对每个key加锁的兜底设计。确保了不会超卖。少卖是有可能的,核心原因是因为 decr 操作和数据操作不是是一个事务,有可能库存扣减完了,但最终操作库失败了。那么这个库存就丢失了,可能会少卖。但一般并不会对少卖做过多的流程,如果想管理,也可以把少卖的库存异常,加入单独的 redis 队列来重新消费就可以了。 + +### 【淘天】2. 对于数据库和redis的一致性怎么解决 + +问题:[https://t.zsxq.com/18ddtmzqW](https://t.zsxq.com/18ddtmzqW) + +- 第1个手段;每次消费 decr 值,写入延迟队列,趋势更新数据库数据。最终一致。 +- 第2个手段;decr 库存值消耗为0时,发送mq消息,更新最终库存量。(可能不准,比如 decr 值消耗中,少卖的情况) +- 第3个手段;活动到期后,任务扫描活动产生订单量,校准库存。 + +### 【其他】3. 什么情况下使用DDD架构,什么情况下使用mvc架构? + +DDD 是软件设计方法,对复杂的项目更为合适。但这里要清楚,DDD 如果只的是设计方法中的建模工程结构,和 MVC 对比的话。DDD 的结构更为先进,即使不使用 DDD 的软件设计方法,只遵循这套结构,都是可以编写出非常好的代码的。MVC 的约束相对较低,个人开发还好,如果多人协作,会出现腐化严重的问题。 + +### 【其他】4. 设计模式带来了什么好处?举个例子 + +设计模式可以让工程设计的迭代性、扩展性、维护性,都更强,更好。如,抽奖策略计算中,用到了责任链、组合模式的规则树。规则树可以动态的调整配置的节点,来满足各种业务诉求。还可以结合产品需求,迭代的时候添加对应的节点开发就可以。避免了大量if...else的使用,让变动范围缩小到指定的类中。研发成本更低,提测质量更高(测试更快),交付效率更强。这些都是使用设计模式的优势。 + +### 【其他】5. 最近大营销抽奖项目被问到抽奖项目按道理应该不是一个高并发的过程,不是想那种抢优惠券这种属于高并发,那如果抽奖不是高并发,那为啥还要把库存缓存到redis去抗并发呢? + +要好好留心日常的场景,都是面试的话术; +1. 春晚红包是不是抽奖,拼多多一进页面就有各种【转转转】来获得一个券,支付完成又一个转转转。直接领券远没有抽奖来的刺激,即使是发券,也是用抽奖方式更多。 +2. 每秒的请求量如果超过1000tps,打到库上资源竞争,都会出现大量的数据库连接等待。一般一个应用分配的数据库连接池也就那么20来个。如果都打到库上,都能把库打挂。 +3. 面试这么问,大部分是为了通过一个质疑的场景,来看是否有思考过。而不是面试问什么就是什么,反而是问什么就不是什么,但不是什么,要拿出举证理由。 + +### 【其他】6. 抽奖算法提问 + +1. 问:数据库路由算法 答:hashcode保证两次散列结果一致,扰动函数保证散列均匀。 问:除了hashcode数据库路由算法还有其他的吗? +2. 问:抽奖除了加分布式锁还有没有别的思路? 答:这里本质上还是保证库存不超卖,说了低并发下的数据库行锁,redis队列 问:还有其他思路吗? 答:想不出来了(面试官说依赖高性能的中间态,这块没理解) +3. 计次模型,设计一天一次的参与规则,怎么实现? +4. 抽奖算法的实现?这个随机怎么可控,怎么避免高价的奖品一开始就被抽掉了,类似于活动中间阶段才能中一次一等奖 +5. 了解过其他抽奖算法吗? +6. 黑名单如果上到一定规模,比如百万级别,有其他的设计思路吗? +7. 扣减库存的分段竞态锁用 incr 还是 decr,为什么是 incr/decr? +8. 如果活动开始了,要加奖品库存怎么办? + +- 问题1;[https://bugstack.cn/md/algorithm/logic/math/2022-11-05-fibonacci.html](https://bugstack.cn/md/algorithm/logic/math/2022-11-05-fibonacci.html) +- 问题2;还有,预生成中奖结果。比如微信春晚红包,提前把奖品随机好,写入到个人记录。用户开奖只是到点查看结果。【如果提到高性能中间件这个可以再继续让面试聊下,因为有可能问题和听者理解的不一致了】 +- 问题3;这个课程里提供了总、月、日库存。 +- 问题4;有次数锁、有权重,可以避免一开始就中一等奖。也是课程的。 +- 问题5;算法如;线性同余生成器、梅森旋转算法、洗牌算法、加权随机。 +- 问题6;黑名单一般会设定人群标签,写入到 Redis 使用。之后库里只是配置了一个人群标签的标识。 +- 问题7;incr 和总量比,decr 和 0 比。decr 适合过程中不允许补充库存的。incr 可以在过程中添加库存,因为总量可以增加对比。【问题8】 + +### 【携程】7. 我跟他说用户在抽奖系统用积分兑换抽奖机会,我方会向mq发消息,积分微服务那边(不是我们组负责的)拿到消息之后扣减会员的积分,他问我: + +- 你为什么在这种积分扣减用mq?我说流量削峰,他说这不是关键。 +- ️如果用户用脚本频繁兑换抽奖机会,你们怎么应对的。 +- 果下游服务迟迟没有对mq进行消息消费,你们怎么处理的?你假设作为系统的架构者,是怎么监控这种状况的。 + +解答:[https://t.zsxq.com/0jjqr](https://t.zsxq.com/0jjqr) + +1. 当你描述为积分和抽奖是单独的系统,你要生成兑换商品订单,拿订单的唯一单号,rpc/http 调用积分系统扣减积分,返回一个交易单号和时间。【如果是微信、支付宝类支付,就需要等待回调,因为还要走支付渠道很多流程,没法直接返回】 +2. 你通过积分支付接口返回的信息开始变更订单记录,发放抽奖次数。 +3. 用户是用自己的积分兑换的,账户会进行额度拦截。之后接口会配置频次拦截【大营销第4阶段有这个设计】 +4. 系统是有边界管控的,在实际工作中,当前的系统要保证发送mq,下游的系统要保证消费mq。mq的消费会有监控配置,比如日常每分钟100次,如果连续n次在n分钟内,低于80次或者高于140次,则进行报警。这样就可以监控到了。 + +### 【其他】8. 关于上下游交互的问题 + +1. 每次面试官都会问一嘴,基于ratelimiter是做单机限流吗? +实际生产中主要是使用分布式限流吗,为什么要基于分布式做呢,单机限流满足不了需求吗,分布式限流具体实现方式是什么呢,基于redis、zookeeper自己实现还是基于限流框架做呢 +2. 限流、降级有必要做到外部服务粒度的嘛?(例如有3个外部系统,有必要为每个系统单独限流、降级吗,例如只是不提供给某个系统服务) +3. 奖品单状态的变更,我还是有一点模糊,如果是下游发奖,那下游应该是不能访问奖品服务的库吧,那下游发完奖如何更新奖品单状态呢?会有回调接口或者是下游再发一个发奖完毕的消息吗? +4. 抽奖活动会和业务系统绑定吗?是不是得在活动表加一个字段,然后鉴权的时候只能调用当前系统的抽奖活动 + +解答:[https://t.zsxq.com/7uTKz](https://t.zsxq.com/7uTKz) + +### 【网易】9. 在抽奖过程时,需要预热处理,可是不可能让用户点击活动装配呀,这个问题你怎么解决? + +第一个视频里提到了,是在活动运营配置后,可以审核通过就预热了。等待活动到了有效期就可以使用了。 + +### 【网易】10. 在抽奖的结果中,如果一个用户在前端抽到了一个奖品,对用户是可见的。可是在扣减库存时,却发现没有了对应的库存,此时应该怎么办? + +库表设计中,抽奖的每条策略是有库存限制的,抽到后才会展示给用户。如果库存不足直接就是兜底积分了。这个也有人遇到过反过来问的,为什么要给策略上也加库存,直接奖品加库存不就可以。其实是不可以的。 + +### 【其他】11. 既然为了想承受更高并发使用redis做库存扣减,那生成奖品id后续要等中奖订单入库才能给用户展示结果吗?那用redis做库存扣减不就没意义了 + +最核心的其实是,如果没有redis做库存扣减,就要数据库里的一个库存记录做扣减。那么就会有成千上万条的请求在同一个表行记录开始独占竞争加锁,其余的请求进入等待,直至耗尽所有数据库连接。那么整个系统服务也就会被拖垮,一个普通的查询也会从原来的几十毫秒变成到一分钟也拿不到结果了。 + +使用redis,就是为了解决这个事情。之后在写入库里的记录,都是无竞争的。那么就不会让数据库被夯住。可以快速被处理。在大厂的数据库配置,基本这类操作不会被 redis 慢多少。 + +解答:[https://t.zsxq.com/kCjDL](https://t.zsxq.com/kCjDL) + +### 【其他】12. 你说你的系统压测指标是3000QPS(简历上我写了3000QPS压测指标),你认为你做的是这个系统压测到3000的时候,他的瓶颈在哪里? + +1. 你应该是描述的单个接口压测,3000qps,qps 指查询,这个过程没有写库,没有事务的操作。瓶颈往往在于接口串联的多,也包括rpc微服务,层层调用带来的性能损耗。其中性能损耗最大的,一般是风控类接口。之后优化可以用多线程并行调用,之后在统一把数据合并处理。你在回答的时候,可以讲,之前是多少qps,响应时间tp99、tp999、max是多少,之后经过与外部接口的协调,增加redis缓存预热,以及你在把接口通过多线程异步并行查询,提高了接口性能,增加了qps指标。另外,还有是单机还是分布式,每台机器配置的jvm,比如原来的指标配置是什么样,这次配置的是多少(这些可以从普罗米修斯监控得出,之后整理成回答)如果回答数据类的,一定要压测。 + +2. 抽奖用的是你的个人库存,这部分可以加分布式锁,一个活动,一个sc场景,只能一个抽奖进行中活动,这样组合一个key加分布式锁就可以了。 + +3. incr 这个星球里有很多资料了,分段锁接近于无锁化,乐观锁。而以前的直接加锁,锁全活动是独占锁,悲观锁。我们要的目的就是接近于不加锁,并且保证性能。另外 lua 脚本实际压测性能并不好。incr + 锁,尽可能避免超卖。但不加锁,基本等于裸奔,虽然大概率不出问题,但出问题是不可知的。我们做系统设计,就要考虑它可能存在的风险,尽可能降低,而不是等待它发生。资料;https://t.zsxq.com/F1J0L - 这里的话术可以使用「海恩法则、薛定谔的猫、墨菲定律、蝴蝶效应及熵增原理」。 + +- 解答:[https://t.zsxq.com/Gdttm](https://t.zsxq.com/Gdttm) +- 扩展:[https://t.zsxq.com/FWBzl](https://t.zsxq.com/FWBzl) + +>问题1:加分段锁的目的是为了什么? + +答:核心目标是为了防止低概率下集群、主从故障导致的超卖,做一个兜底逻辑。 + +>问题2:如果不需要补库存,分段锁是不是可以不用加? + +答:即使不考虑手动补库存的情况,如果集群、主从故障,不加分段锁还是会可能超卖的,所以这里的分段锁不单单是为了补库存的场景而设计。 + +>问题3:那直接 incr 不可以吗? + +答:在 redis 集群模式下,incr 请求操作可能发生网络抖动超时返回。这个时候 incr 有可能成功,也有可能失败。可能是请求超时,也可能是请求完的应答超时。那么 incr 的值可能就不准。【实际使用中10万次,可能会有10万零1和不足10万】,那么为了这样一个临界状态的可靠性,所以添加 setNx 加锁只有成功和失败。 + +还有一种情况是主从切换的时候,如果主节点的 incr 还没同步到从节点,主节点挂了,丢失了部分未同步的数据,incr 的值从 8 变成 6,如果没有加锁就可能超卖,属于极端情况下的一种兜底策略,有 setNX 锁拦截后,会更加可靠。 + +>问题4:那如果考虑集群故障,机器挂掉的情况,setNX 不也会报错吗? + +答:setNX 如果失败了,就直接报错返回 "活动库存不足" 即可,也就是可能会导致少卖,但是不会导致超卖。并且 incr 和 setNX 的 key 不同,incr 的 key 和滑块锁的 key 大概率不在同一节点上,从而双重保证,如果 senNx 的 key 和库存的 key 节点都 down 机了,那这里确实有超卖的可能,不过这个概率可以低到忽略不计。 + +>问题5:decr 和 incr 两种扣减方式有什么不同? + +答:二种方式都可以,decr 适合固定库存场景,和 0 对比,incr 适合可以补库存的场景,和库存总量对比。 + +>问题6:那为什么要分段,直接对一个 key senNX 不可以吗? + +答:分段锁的话,setNX 因为是非独占锁,所以 key 不存在释放。setNX 的 key 的过期时间可以优化为活动的有效期时间为结束。而独占锁,其实你永远也不好把握释放时间,因为秒杀都是瞬态的,释放的晚了活动用户都走了,释放的早了,流程可能还没处理完。 + +>问题7:incr 扣减模式下,如果同一个用户并发进来,那么缓存中的库存就会+并发数,但实际这个用户只会领取到一条数据,所以就要恢复并发数-1的库存数量。这样种情况并不是 redis 不稳定导致的,而是同一用户并发导致的,应该及时去恢复数据啊,不然的话缓存中的库存直接一下就给一个用户并发干没了,然后再去恢复,效率太低了吧? + +答:不需要恢复,还是回到上面,核心是保证不超卖,关于库存恢复,一般这类抽奖都是瞬态的,且 redis 集群非常稳定。所以很少有需要恢复库存,如果需要恢复库存,那么是把失败的秒杀 incr 对应的值的 key,加入到待消费队列中。等整体库存消耗后,开始消耗队列库存,等补偿恢复,活动已经基本过去了。所以超卖,快速结束是最好的。这个一般是基于运营策略配置何种方式恢复库存,可以失败的专门扫描到恢复库存列表用于消耗,也可以不恢复(因为失败概率很低,也允许不超买即可)。 + +### 【其他】13. decr已经是原子操作了「多个请求一定是串行的」,你为什么还要加setnx锁? + +答:[https://t.zsxq.com/0BC2f](https://t.zsxq.com/0BC2f) \ No newline at end of file diff --git "a/docs/md/project/big-market/prd/\347\254\2541\350\212\202\357\274\232\350\220\245\351\224\200\345\234\272\346\231\257\347\232\204\351\234\200\346\261\202\350\256\276\350\256\241.md" "b/docs/md/project/big-market/prd/\347\254\2541\350\212\202\357\274\232\350\220\245\351\224\200\345\234\272\346\231\257\347\232\204\351\234\200\346\261\202\350\256\276\350\256\241.md" new file mode 100644 index 000000000..cef7dc535 --- /dev/null +++ "b/docs/md/project/big-market/prd/\347\254\2541\350\212\202\357\274\232\350\220\245\351\224\200\345\234\272\346\231\257\347\232\204\351\234\200\346\261\202\350\256\276\350\256\241.md" @@ -0,0 +1,34 @@ +--- +title: 第1节:营销场景的需求设计 +pay: https://t.zsxq.com/14pICuMEs +--- + +# 《大营销平台系统设计实现》 - 需求文档 第1节:营销场景的需求设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:介绍大营销平台的作用,以OpenAi大模型应用的营销活动需求文档设计。 +- **课程视频**:[https://t.zsxq.com/16RMgecZB](https://t.zsxq.com/16RMgecZB) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +介绍大营销平台的作用,编写以OpenAi大模型应用的营销活动需求文档设计。注意,OpenAi大模型应用是星球内的另外一个项目,后续我们将以此项目为业务场景底座,来扩展各类业务产品系统。打造出一整条产品线。 + +OpenAi大模型应用,是为了承载其他项目所做的基础服务,而营销平台不只是可以用在此项目上,其他各类项目都可以。像是;拼多多的砍一刀、电商双11分享群里抽红包、出行打车好友助力得券等,都是营销场景的产品。所以本次项目也是结合这些真实诉求和公司的真实项目,来带着大家学习。 + +## 二、产品概述 + +**产品**:[https://gaga.plus](https://gaga.plus) —— 运营了一个 OpenAi 在线项目。 +**概述**:拆解 OpenAi 业务场景中用户所需的功能点,包括;可以使用的模型、角色、支付,对这些内容具体的功能设定为营销点。最终用户可以通过抽奖获取随机积分、体验卡、模型卡、兑换次数等。 + +
+ +
+ +这里一张图的`体现区域`,涵盖了;账户、抽奖、奖品。三个领域(这三个领域的复用性很高),按照场景划分可以拆解出3微服务系统来开发。不过我们这里为了大家更加方便学习,不能把系统一下扩的很大,所以小傅哥这里先按照一个系统中不同领域来做,后续在讲解大营销平台的微服务拆分。 diff --git "a/docs/md/project/big-market/prd/\347\254\2542\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\345\234\272\346\231\257\347\232\204\351\234\200\346\261\202\350\256\276\350\256\241.md" "b/docs/md/project/big-market/prd/\347\254\2542\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\345\234\272\346\231\257\347\232\204\351\234\200\346\261\202\350\256\276\350\256\241.md" new file mode 100644 index 000000000..a85209f25 --- /dev/null +++ "b/docs/md/project/big-market/prd/\347\254\2542\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\345\234\272\346\231\257\347\232\204\351\234\200\346\261\202\350\256\276\350\256\241.md" @@ -0,0 +1,44 @@ +--- +title: 第2节:抽奖活动场景的需求设计 +pay: https://t.zsxq.com/172PDFbV3 +--- + +# 《大营销平台系统设计实现》 - 需求文档 第2节:抽奖活动场景的需求设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:开始大营销平台系统第2阶段,活动领域需求的设计讲解。 +- **课程视频**:[https://t.zsxq.com/17zKSkRN5](https://t.zsxq.com/17zKSkRN5) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在【需求文档】文档部分的第1节,我们分析了整个营销场景所需实现的流程,之后前面17节设计、开发并上线了第1部分功能,让用户可以在线体验抽奖。 + +在公司的营销部门,抽奖系统的设计会有不同诉求的使用,比如有的调用方只需要给我提供一个算法策略,其余的则由自己搞定。同样也有的调用方是希望提供全部的功能,他没有那么多的个性化诉求。 + +所以针对不同场景的诉求,大营销系统会把这些流程进行功能解耦设计,让外部可以轻松的调用到各个模块服务。这样也就提供了代码功能的复用性,逐步沉淀为统一的营销服务平台。 + +那么为了承接这样的业务诉求,小傅哥也会带着大家逐步的实现各个阶段功能。前面已经做了抽奖策略模块,而这第2阶段,将实现抽奖活动阶段,通过活动来使用策略,并提供相关的有效期、状态、个人参数次数的管理等。 + +这样的设计也是为了方便后续我们扩展用户使用积分兑换抽奖资格的能力,这些功能将逐步开发,而你的角色则像是一个项目组中,被分配了其中一个(微服务/领域)模块的实现。 + +>按照公司不同规模和承载用户体量的大小,系统会被划分为微服务或者一个系统中的不同领域模块。这些模块按需也可以设计为一个个微服务系统。 + +## 二、产品需求 + +为了更好的激发用户抽奖的兴趣,培养用户心智,增强日活时长和转化。在抽奖中增加每天进入,免费赠送N次抽奖。N次免费后,则引导用户使用积分兑换抽奖。 + +但为了先培养用户的抽奖心智,增强免费的概念。我们在这一期实现中,只提供免费抽奖,增强用户分享传播动力。【后续我们可以做分享和积分兑换抽奖】 + +
+ +
+ +- 在本地的诉求中,用户点击抽奖进入后,则直接赠送抽奖次数。这个体现方式也可以是点击登录的方式获取抽奖次数,这个在很多我们使用过的互联网产品中都有所体现。 +- 当然这个领取活动的过程,用户是无感知的。用户的体验上,只是知道自己是点击了抽奖,其余的流程则是服务端来处理。 \ No newline at end of file diff --git "a/docs/md/project/big-market/prd/\347\254\2543\350\212\202\357\274\232\347\224\250\346\210\267\350\241\214\344\270\272\345\245\226\345\212\261\351\234\200\346\261\202\350\256\276\350\256\241.md" "b/docs/md/project/big-market/prd/\347\254\2543\350\212\202\357\274\232\347\224\250\346\210\267\350\241\214\344\270\272\345\245\226\345\212\261\351\234\200\346\261\202\350\256\276\350\256\241.md" new file mode 100644 index 000000000..7be92034a --- /dev/null +++ "b/docs/md/project/big-market/prd/\347\254\2543\350\212\202\357\274\232\347\224\250\346\210\267\350\241\214\344\270\272\345\245\226\345\212\261\351\234\200\346\261\202\350\256\276\350\256\241.md" @@ -0,0 +1,35 @@ +--- +title: 第3节:用户行为奖励需求设计 +pay: https://t.zsxq.com/0PgF4 +--- + +# 《大营销平台系统设计实现》 - 需求文档 第3节:用户行为奖励需求设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:设计用户行为奖励需求,用于后续给用户增加活动账户额度等场景操作。 +- **课程视频**:[https://t.zsxq.com/E2rLP](https://t.zsxq.com/E2rLP) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在设计本节需求之前,小傅哥带着大家做了整个大营销中抽奖活动的玩法,每次参与抽奖的时候会扣减用户的活动账户额度。比如用户总可参与2次,每抽奖一次会扣减账户额度1次。此外我们在`活动领域`中还实现了一个增加账户额度的方法,那么这个方法要由谁来触达增加额度呢?这个就是本节要做的需求设计。 + +本节将要设计出一个`用户行为返利需求`,也就是用户在完成一个行为动作后,奖励一种东西。这个东西可以是我们前面定义出来的 sku,一个 sku 有配置对应的用户可使用的抽奖次数额度。也可以是积分等。目前我们主要做 sku 这部分,但库表设计会预留出扩展点。 + +## 二、用例图 + +用例图(use case diagram),也称为用户故事,以用户为视角,最简单的展示出用户行为动作。由于其简单纯粹的本质,用例图是项目参与者间交流的好工具。因此也被称为搭建系统的蓝图。 + +
+ +
+ +- 用户的2个行为动作,打卡/签到(每天可完成一次),另外一个动作是后续对接 openai 项目的时候,来接收一个支付完成的消息,触达发奖资格。 +- 发奖可以是抽奖资格也可以是给用户积分。积分部分后续实现。 +- 那么这里 openai 支付的对接和赠送积分的场景,虽然要后续实现,但在我们本次做的需求中,要预留出设计,否则后续就不好扩展了。 \ No newline at end of file diff --git "a/docs/md/project/big-market/prd/\347\254\2544\350\212\202\357\274\232\347\224\250\346\210\267\347\247\257\345\210\206\351\234\200\346\261\202\350\256\276\350\256\241.md" "b/docs/md/project/big-market/prd/\347\254\2544\350\212\202\357\274\232\347\224\250\346\210\267\347\247\257\345\210\206\351\234\200\346\261\202\350\256\276\350\256\241.md" new file mode 100644 index 000000000..960bb2acf --- /dev/null +++ "b/docs/md/project/big-market/prd/\347\254\2544\350\212\202\357\274\232\347\224\250\346\210\267\347\247\257\345\210\206\351\234\200\346\261\202\350\256\276\350\256\241.md" @@ -0,0 +1,35 @@ +--- +title: 第4节:用户积分需求设计 +pay: https://t.zsxq.com/TedGs +--- + +# 《大营销平台系统设计实现》 - 需求文档 第4节:用户积分需求设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:积分是营销活动的媒介,我们可以给用户通过各类行为发放积分,积分可以兑换抽奖资格以及其他类型的消费。 +- **课程视频**:[https://t.zsxq.com/KSLjZ](https://t.zsxq.com/KSLjZ) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +分析系统中`积分`的链路,哪里是添加积分,哪里可以消耗积分。以积分作为媒介增加用户的营销活动体验。用户可以知道自己有多少积分,以及在每次完成指定的行为动作后,可以获得积分。 + +在我们使用的各类互联网应用产品中,都有积分的存在。做编程开发的程序员,需要留意这些场景,根据场景的使用,也可以反推出需求的设计以及如何去编码开发。 + +## 二、用例图 + +在积分的获取和使用上,可以看到3个行为动作来增加和使用积分。 + +
+ +
+ +- 抽奖中有随机积分,抽中后可以给用户增加积分。注意黑名单一般会发放一个指定范围的很小的积分值。 +- 用户行为动作返利,当用户在抽奖系统中日历📅签到,以及提供出让外部对接的接口,也可以给用户增加积分。 +- 最后就是积分消耗,目前这里的积分消耗主要是兑换抽奖资格,也就是和活动 sku 进行兑换。这部分会产生订单。 diff --git a/docs/md/project/big-market/qa.md b/docs/md/project/big-market/qa.md new file mode 100644 index 000000000..7e63aa15f --- /dev/null +++ b/docs/md/project/big-market/qa.md @@ -0,0 +1,111 @@ +--- +title: Q&A:常见开发问题错误解答 +lock: no +--- + +# Q&A:常见开发问题错误解答 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/17gswKIeX](https://t.zsxq.com/17gswKIeX) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、断点调试 + +- [排查10个Bug](https://www.bilibili.com/video/BV1F6421w71e) +- [打断点查空指针](https://www.bilibili.com/video/BV1q1421Q7Uv) + +## 二、开发问题 + +### 1. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): top.duain.infrastructure.persistent.dao.IAwardDao.queryAwardList + +- 案例:[https://t.zsxq.com/ENr02](https://t.zsxq.com/ENr02) +- 解答: + - application.dev mybatis 配置,默认是被注释掉的,需要打开。 + - 自建工程结构,Application 启动层的文件夹路径,不是整个包结构的根路径,扫描不到 + - MyBatis Mapper 配置文件填写错误,没有配置对应的路径 + - app 层的 pom 文件,没有引入 infrastructure,所以不能被 Spring 容器管理导致。 + +### 2. Could not create to database server. Attempted reconnect 3 times. Giving up + +- 案例:[https://t.zsxq.com/7i31T](https://t.zsxq.com/7i31T) +- 解答: + - 课程中 docker compose 安装 MySQL,提供的是 13306 端口以及初始的密码(避免和你本地 MySQL 3306 冲突),所以你的 application.dev 数据库连接也要使用 13306 端口。 + - 安装到云服务器的,安全组 13306 端口没有打开。 + +### 3. java.lang.NullPointerException queryStrategyEntityByStrategyId(StrategyRepository.java:71) + +- 案例: + - [https://t.zsxq.com/x3Uz0](https://t.zsxq.com/x3Uz0) + - [https://t.zsxq.com/9QGLX](https://t.zsxq.com/9QGLX) + - [https://t.zsxq.com/BUtmX](https://t.zsxq.com/BUtmX) + - [https://t.zsxq.com/wstu6](https://t.zsxq.com/wstu6) + - [https://t.zsxq.com/P81oT](https://t.zsxq.com/P81oT) +- 解答: + - MyBatis Mapper resultMap 配置成了 resultType,导致数据库中的字段没法和 Java 对象的字段映射。如 `strategy_id -> strategyId` + - 注解忘记配置 + +### 4. java.lang.ArithmeticException: / by zero + +- 案例: + - [https://t.zsxq.com/4oSIt](https://t.zsxq.com/4oSIt) + - [https://t.zsxq.com/JxXhY](https://t.zsxq.com/JxXhY) +- 解答:粗心犯错,test_queryStrategyAwardListByStrategyId 入参为 100002L 不是 10002L。或者 redis 没有数据导致。 + +### 5. java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String + +- 案例:[https://t.zsxq.com/Uu8XA](https://t.zsxq.com/Uu8XA) +- 解答:由于 `getFromMap` 方法里是返回的 `String` 类型,而你设置的结果值为 `Integer` 类型,那么直接返回的值会报错类型转换异常。可以这样;`redisService.getMap("stratrgy_id_100001").get("1")` + +### 6. TooManyResultsExcepton: Expected one result (or null) to be returned by selectOne(), but found: 3 + +- 案例: + - [https://t.zsxq.com/7NUzA](https://t.zsxq.com/7NUzA) + - [https://t.zsxq.com/Di0oC](https://t.zsxq.com/Di0oC) + - [https://t.zsxq.com/gKu1H](https://t.zsxq.com/gKu1H) +- 解答:查询 sql 的条件入参写错,传了空值。用自己new的对象,获取里面的值,写错代码了。参考案例地址中的图片。 + +### 7. Redis 管理工具看错是乱码 + +- 案例:[https://t.zsxq.com/RpgCw](https://t.zsxq.com/RpgCw) +- 解答:RedisClientConfig 类,修改序列化方式 `config.setCodec(JsonJacksonCodec.INSTANCE);` + +### 8. map value can't be null + +- 案例:[https://t.zsxq.com/A4LaY](https://t.zsxq.com/A4LaY) +- 解答:修改对象类信息后,Redis 数据未清空重新测试。 + +### 9. 数据库查询无结果 + +- 案例:[https://t.zsxq.com/2DdEh](https://t.zsxq.com/2DdEh) +- 解答:入参格式化时间写错,导致格式的时间多了个空格。如案例图。 + +### 10. 决策树引擎,nextNode 计算失败,未找到可执行节点 + +- 案例:[https://t.zsxq.com/zNKob](https://t.zsxq.com/zNKob) +- 解答:规则树过滤章节后更细调整。可以跟着课程继续。 + +### 11. Cannot invoke String.split(String) because this.RuleModels is null + +- 案例:[https://t.zsxq.com/nENwa](https://t.zsxq.com/nENwa) +- 解答:可以看案例排查图 + +### 12. Unable to connect to Redis server: localhost/127.0.0.1:6379 + +- 案例:[https://t.zsxq.com/mrpne](https://t.zsxq.com/mrpne) +- 解答:缺少配置 Redis 启动类。 + +### 13. Mock 就走不到黑名单了 + +- 案例:[https://t.zsxq.com/iO5kL](https://t.zsxq.com/iO5kL) +- 解答:Mock 值未调整,参考案例解答图。 + +### 14. 找不到分库下的分表 + +- 解答:Dao 类缺少 `@DBRouterStrategy(splitTable = true)` + +## 三、DevOps + +- Docker 拉取不下来镜像,超时;配置镜像地址;[https://t.zsxq.com/EVDc9](https://t.zsxq.com/EVDc9) +- jdk1.8、maven(含镜像)下载:[https://t.zsxq.com/EVDc9](https://t.zsxq.com/EVDc9) \ No newline at end of file diff --git a/docs/md/project/big-market/system-design-diagram.md b/docs/md/project/big-market/system-design-diagram.md new file mode 100644 index 000000000..43da2080a --- /dev/null +++ b/docs/md/project/big-market/system-design-diagram.md @@ -0,0 +1,132 @@ +--- +title: 设计:架构图、拓扑图、用例图、流程图、建模图 +lock: no +--- + +# 《大营销平台系统》系统设计图:架构图、拓扑图、用例图、流程图、建模图 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/17gswKIeX](https://t.zsxq.com/17gswKIeX) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +😂 现在的面试是不是为难小朋友,怎么刚一毕业的面试,就让现场手画简历项目的系统架构图。面试官说,你不是计算机专业毕业🎓的吗,这不是你的基础吗?你作为一个合格的毕业生,不就应该学习到这些吗? + +
+ +
+ +**为什么毕业生,画不出来这些图?** + +其实不只毕业生,很多工作1-3年的也画不出这些系统的架构图。主要是因为,在校的时候没有学过,市面的一些简单学习项目也都是 CRUD 的 demo 级别项目,工作了以后如果又是一些小厂,大部分就是完成功能需求而已。所以很多人压根就没有画过这些图。 + +但作为程序员👨🏻‍💻这个行业的伙伴,如果还想多职业生涯走的更长,也多赚点米,那么就需要好好的在一个大型项目中锻炼这些画图的技能。因为一图胜前言,有时候图好,前途就好! + +那一个系统都会做出哪些个设计图呢?这里会包括;`系统架构图`、`业务拓扑图`、`用户用例图`、`业务流程图`、`系统建模图`,之后就是细分实现功能的 UML 流程图。这里小傅哥会以此的给大家做绘制和讲解,这趟车🚗你可以学习到很多! + +>文末提供了本次分享系统设计图对应的系统工程全套代码、文档、视频和面试官以及简历资料。 + +## 一、系统的场景 + +这是互联网公司C端场景,峰值流量最高的一套**营销类业务系统**,业务涵盖;`抽奖`、`活动`、`积分`、`返利`、`兑换`,这些核心的业务流程。像是在`抖音商城`、`京东金融`、`滴滴出行`、`拼多多`,都有这样业务场景。如图; + +
+ +
+ +整个系统是一个包括 `前后端 + Dev-Ops` 的全栈式综合编程实战项目,基于 React + SpringBoot + 分布式技术栈 + Nginx + Grafana + Docker 云服务,开发、部署、上线、监控,全体系技术运用构建。 + +> 接下来,小傅哥就给大家分别的展示出各项设计图,包括;`系统架构图`、`业务拓扑图`、`用户用例图`、`业务流程图`、`系统建模图`。 + +## 二、系统架构图 + +**软件架构**是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。软件架构会包括软件组件、组件之间的关系,组件特性以及组件间关系的特性。软件架构可以和建筑物的架构相比拟。软件架构是构建计算机软件,开发系统以及计划进行的基础,可以列出开发团队需要完成的任务。 + +
+ +
+ +- 这是一套大营销系统的分布式架构设计图,从前端到负载,从服务治理到后端分布式技术栈体现,从应用到部署和监控的全体系展示。在这样一套系统架构中,你可以清楚的知道从前到后的流程、各项分布式技术栈的用途、整个系统的脉络关系。所以这样的一个图可以清晰的指导我们做系统的搭建。 +- `佛瑞德·布鲁克斯`在写作《人月神话》一书时提及:软件系统的架构是有关软件系统该作什么以及不该作什么的实体观点。这些观点应和软件的实现分开。架构师的角色是“观点的看守者”,确认系统中增加的部分是符合此架构,因此可以保有概念完整性 +- 另外程序员`马尔文·康威`在1967年论文发表了康威定律,其中提到一个组织开发的软件,其架构会反映其组织架构。佛瑞德·布鲁克斯在写作《人月神话》一书时,就在书上时提到此例子,命名为“康威定律”。 + +## 三、用户用例图 + +用例图(英语:use case diagram)是用户与系统交互的最简表示形式,展现了用户和与他相关的用例之间的关系。通过用例图,人们可以获知系统不同种类的用户和用例。用例图也经常和其他图表配合使用。 + +- 用例图,也可以等同于是用户故事(英语:User story)(软件开发和项目管理中的常用术语),主旨是以日常语言或商务用语撰写句子,是一段简单的功能表述。以客户或使用者的观点撰写下有价值的功能、引导、框架来与使用者进行互动,进而推动工作进程。可以被认为是一种规格文件,但更精确而言,它代表客户的需求与方向。以该用户故事来反应对象在组织内的其工作职责、范围、需要进行的任务等。用户故事在敏捷开发方法中用来定义系统需要提供的功能和实现需求管理。 +- 尽管用例本身会涉及大量细节和各种可能性,用例图却能提纲挈领地让人了解系统概况。它为“系统做什么”提供了简化了的图形表示,因此被誉为“搭建系统的蓝图”。 + +
+ +
+- 用例图是研发进入设计阶段第一个图,通过用例图了解整个系统中发生的流程行为。这个图虽然很粗,但非常适合做后续的设计指导。 + + +## 四、业务拓扑图 + +系统业务拓扑图通常是指系统中各个组件和业务流程的可视化表示。这类图示有助于理解和分析系统如何运作、各组件之间的关系、数据流动的路径以及可能的瓶颈或失败节点。 + +
+ +
+ +- 业务拓扑图,展示出了业务实现中的关系模型,包括整个系统会从哪里展示,包括了哪些核心业务功能和执行的链路。以及数据的走向。 +- 有了系统业务拓扑图,开发者、系统架构师和运维人员可以更直观地理解系统的结构和业务逻辑,从而更有效地进行设计、部署、监控和故障排除。 + +## 五、四色建模图 + +如果系统是 DDD 开发的,还需要做四色建模。在使用 DDD 的标准对系统建模前,一堆人要先了解 DDD 的操作手段,这样才能让产品、研发、测试、运营等了解业务的伙伴,都能在同一个语言下完成系统建模。 + +- 蓝色 - 决策命令,是用户发起的行为动作,如;开始签到、开始抽奖、查看额度等。 +- 黄色 - 领域事件,过去时态描述。如;签到完成、抽奖完成、奖品发放完成。它所阐述的都是这个领域要完成的终态。 +- 粉色 - 外部系统,如你的系统需要调用外部的接口完成流程。 +- 红色 - 业务流程,用于串联决策命令到领域事件,所实现的业务流程。一些简单的场景则直接有决策命令到领域事件就可以了。 +- 绿色 - 只读模型,做一些读取数据的动作,没有写库的操作。 +- 棕色 - 领域对象,每个决策命令的发起,都是含有一个对应的领域对象。 + +
+ +
+ +- 系统建模后可以细分出系统开发中要实现的领域,包括;返利、活动、策略、奖品,兑换可以是单独的领域也可以合并到返利实现。 +- 具体的建模过程可以阅读 [《架构:DDD 领域驱动设计,战略、战术、战役,落地指引规范》](https://bugstack.cn/md/project/big-market/ddd.html) + +## 六、业务流程图 + +系统业务功能流程图(Business Functional Flow Diagram,BFD)是一种用于描述和展示系统内业务功能及其之间关系的图形化工具。它通常用于系统分析和设计阶段,以帮助理解和沟通业务流程、功能要求以及系统的各个组件如何协同工作。 + +
+ +
+ +- 在该流程中,详细的展示出了系统的业务流转关系,从1~6的用户行为,全面的展示出了系统中的流程动作。怎么抽奖、怎么返利、怎么兑换积分。 +- 画这个图到时候,首先,需要识别系统中的主要业务功能。这些业务功能通常可以通过业务需求文档、需求分析会谈以及与业务用户的交流中得出。之后,识别各业务功能之间的关系和依赖性。这包括理解哪些业务功能依赖于哪些其他功能,以及它们如何相互作用。最后,绘制流程图,以图形方式展示业务功能和它们之间的关系。 + +## 七、工程分层图 + +工程的分层有 MVC 三层架构、DDD 四层架构,其实 DDD 不能只是说它是架构,因为用于承载 DDD 的架构是;整洁架构、洋葱架构、六边形架构、菱形架构这些。 + +
+ +
+ +- 六边形架构,会把本身提供到外部的放到trigger,让接口调用、消息监听、任务调度,都可以统一一个入口处理。而对于需要调用外部同类的能力统一放到 infrastructure 基础设施层,包括;数据库、缓存、配置、调用其他方的接口。 +- querys 模块是为了提供查询设计的模块,这样一些基本简单的查询就不需要再走到 domain 领域层了。 +- 更多关于 DDD 的工程模型,可以在 [bugstack.cn](https://bugstack.cn) 进入《路书》中阅读9篇系统架构部分。 + +## 八、扩展类知识 + +这是小傅哥为大家提供的一套综合锻炼编程能力的实战项目大课。全程视频手把手,一个个章节、一步步流程的带着大家从0到1,需求分析、工程设计和代码实现。 + +课程完成后,有可参考简历编写模板、简历面试问题汇总、项目延展性思考和分析,以及项目中的架构、设计模式在场景方案中的运用。 + +- 课程目录:[https://bugstack.cn/md/project/big-market/big-market.html](https://bugstack.cn/md/project/big-market/big-market.html) +- 系统建模:[https://bugstack.cn/md/project/big-market/ddd.html](https://bugstack.cn/md/project/big-market/ddd.html) +- 简历参考:[https://bugstack.cn/md/project/big-market/notes.html](https://bugstack.cn/md/project/big-market/notes.html) +- 面试问题:[https://bugstack.cn/md/project/big-market/notes.html](https://bugstack.cn/md/project/big-market/notes.html) diff --git "a/docs/md/project/big-market/web/\347\254\2541\350\212\202\357\274\232React\345\267\245\347\250\213\345\210\233\345\273\272\345\222\214\346\212\275\345\245\226\347\273\204\344\273\266\344\275\277\347\224\250.md" "b/docs/md/project/big-market/web/\347\254\2541\350\212\202\357\274\232React\345\267\245\347\250\213\345\210\233\345\273\272\345\222\214\346\212\275\345\245\226\347\273\204\344\273\266\344\275\277\347\224\250.md" new file mode 100644 index 000000000..c3ea21f50 --- /dev/null +++ "b/docs/md/project/big-market/web/\347\254\2541\350\212\202\357\274\232React\345\267\245\347\250\213\345\210\233\345\273\272\345\222\214\346\212\275\345\245\226\347\273\204\344\273\266\344\275\277\347\224\250.md" @@ -0,0 +1,32 @@ +--- +title: 第1节:React工程创建和抽奖组件使用 +pay: https://t.zsxq.com/17Jfuonzc +--- + +# 《大营销平台系统设计实现》 - 前端页面 第1节:React工程创建和抽奖组件使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:通过 React 搭建前端抽奖页面工程,涵盖大转盘、九宫格两种样式。 +- **课程视频**:[https://t.zsxq.com/177fNsBdm](https://t.zsxq.com/177fNsBdm) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +大营销平台是一个后端Java系统,核心的重要业务逻辑实现都会在后端代码实现上。但为了保证项目整体的完整性,让读者伙伴知道这套服务端的逻辑是怎么体现给用户的,也会提供出对应使用的前端页面。 + +小傅哥这里会采用 React 技术开发前端页面,对于已经具备一些 Java 编程经验的小伙伴来说,学 React 会比 vue 更加容易,因为 React 更像 Java 那种面向对象的编程方式,也不会额外的定义出非标准的语法结构。你只需要花费48小时阅读官网中文文档,即可理解 React 代码的含义。文档地址:[https://zh-hans.react.dev/learn](https://zh-hans.react.dev/learn) + +## 二、开发环境 + +- `Node.js v18+` - [https://nodejs.org/en/download/](https://nodejs.org/en/download/) +- WebStorm 2023.1 - 对于熟悉 IntelliJ IDEA 开发的伙伴,使用这款工具会非常容易。 +- 镜像源设置【提高下载速度】;官网镜像源为 `https://registry.npmjs.org` 需要切换为镜像源 `npm config set registry https://registry.npmmirror.com/` 【查看源 `npm config get registry`】 +- 课程资料:—— 这样两份资料基本就够入门了😄 + - 官网中文资料 [https://zh-hans.react.dev/learn](https://zh-hans.react.dev/learn) + - 菜鸟教程 [https://www.runoob.com/typescript/ts-tutorial.html](https://www.runoob.com/typescript/ts-tutorial.html) \ No newline at end of file diff --git "a/docs/md/project/big-market/web/\347\254\2542\350\212\202\357\274\232Mock\346\216\245\345\217\243\345\257\271\346\216\245\346\212\275\345\245\226\351\241\265\351\235\242.md" "b/docs/md/project/big-market/web/\347\254\2542\350\212\202\357\274\232Mock\346\216\245\345\217\243\345\257\271\346\216\245\346\212\275\345\245\226\351\241\265\351\235\242.md" new file mode 100644 index 000000000..1afcd6e94 --- /dev/null +++ "b/docs/md/project/big-market/web/\347\254\2542\350\212\202\357\274\232Mock\346\216\245\345\217\243\345\257\271\346\216\245\346\212\275\345\245\226\351\241\265\351\235\242.md" @@ -0,0 +1,35 @@ +--- +title: 第2节:Mock接口对接抽奖页面 +pay: https://t.zsxq.com/17SM9V77G +--- + +# 《大营销平台系统设计实现》 - 前端页面 第2节:Mock接口对接抽奖页面 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:通过 APIPost 工具 Mock 抽奖奖品列表和随机抽奖接口,与 WEB 端进行对接。 +- **课程视频**:[https://t.zsxq.com/17FV8Sx3E](https://t.zsxq.com/17FV8Sx3E) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +在公司中做项目的过程是,产品与前端、后端、UI、测试等人员进行PRD评审后,前后端人员就可以分别进行开发了,并确定一个联调的时间点。 + +那么这里的前端并不会完全等着后端把所有接口开发好后才进行前端开发,而是先根据UI的设计图编写出大部分展示内容,而对于接口的数据则会通过后端定义的接口标准进行Mock。【Mock 的作用是可以提供一些模拟数据让前端调用,进行代码开发。】等到后端接口完全提供好以后,直接更换APIHost即可进行联调对接。 + +那么,本节我们则按照这样的模式,通过 Mock 接口的方式与前端的抽奖页面进行接口对接,完成抽奖的奖品查询和随机抽奖动作。 + +## 二、流程设计 + +对于当前我们在上一节 WEB UI 中引入了抽奖的组件,本节我们则需要在进入抽奖页面的时候从 Mock 的接口中获取奖品列表数据,同时在发起抽奖的时候获取中奖信息。 + +
+ +
+ +- 现在的 Mock 服务接口,都有非常多的随机类字符串、数字、文本内容的产生,非常适合我们来使用。这里小傅哥使用的 ApiPost 工具,使用其他的如 ApiFox 也可以,只是配置上会有一些差异。 \ No newline at end of file diff --git "a/docs/md/project/big-market/web/\347\254\2543\350\212\202\357\274\232\345\272\224\347\224\250\346\216\245\345\217\243\345\257\271\346\216\245\346\212\275\345\245\226\351\241\265\351\235\242.md" "b/docs/md/project/big-market/web/\347\254\2543\350\212\202\357\274\232\345\272\224\347\224\250\346\216\245\345\217\243\345\257\271\346\216\245\346\212\275\345\245\226\351\241\265\351\235\242.md" new file mode 100644 index 000000000..fb270f914 --- /dev/null +++ "b/docs/md/project/big-market/web/\347\254\2543\350\212\202\357\274\232\345\272\224\347\224\250\346\216\245\345\217\243\345\257\271\346\216\245\346\212\275\345\245\226\351\241\265\351\235\242.md" @@ -0,0 +1,34 @@ +--- +title: 第3节:应用接口对接抽奖页面 +pay: https://t.zsxq.com/17mKDd4P0 +--- + +# 《大营销平台系统设计实现》 - 前端页面 第3节:应用接口对接抽奖页面 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:替换掉Mock,对接服务端API接口,形成一个完整的WEB + 后端API的调用链路。 +- **课程视频**:[https://t.zsxq.com/17rL2crj6](https://t.zsxq.com/17rL2crj6) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +前端通过Mock接口的方式开发页面完成,后端服务部署到测试环境能提供出可调用的接口。接下来就到了前后端工程师进行对接和联调测试阶段了。 + +所以本节我们会把后端开发好的接口,与前端页面进行对接,替换掉所有 Mock 接口。形成一个完整的WEB + 后端API的调用链路。 + +## 二、流程设计 + +本节的前端页面展示,还需要在页面开发一个**装配抽奖**的按钮,在点击后调用到后端接口上,把数据写入 Redis 缓存中。这个接口已经在前一节实现完成。 + +
+ +
+ +- 与前端自己 Mock 阶段新增加了一个**装配抽奖**的动作,方便大家测试使用【另外是一般年会的抽奖,也会再有这样一个按钮,类似于重置抽奖,让大家继续抽奖品】。 +- 后续随着项目的开发,装配的动作会迁移到后端的管理页面中,在配置抽奖活动发起上线动作的时候,装配抽奖信息。 \ No newline at end of file diff --git "a/docs/md/project/big-market/web/\347\254\2544\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\351\241\265\351\235\242\350\256\276\350\256\241\345\222\214\345\257\271\346\216\245.md" "b/docs/md/project/big-market/web/\347\254\2544\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\351\241\265\351\235\242\350\256\276\350\256\241\345\222\214\345\257\271\346\216\245.md" new file mode 100644 index 000000000..ca89a502c --- /dev/null +++ "b/docs/md/project/big-market/web/\347\254\2544\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\351\241\265\351\235\242\350\256\276\350\256\241\345\222\214\345\257\271\346\216\245.md" @@ -0,0 +1,38 @@ +--- +title: 第4节:活动信息API迭代和功能完善 +pay: https://t.zsxq.com/Njt3M +--- + +# 《大营销平台系统设计实现》 - 前端页面 第4节:活动信息API迭代和功能完善 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:开发九宫格抽奖页面,对接到大营销第2阶段抽奖活动接口。实现抽奖N次解锁的页面展示逻辑。 +- **课程视频**:[https://t.zsxq.com/escSr](https://t.zsxq.com/escSr) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +开发九宫格抽奖页面,对接到大营销第2阶段抽奖活动接口。实现抽奖N次解锁的页面展示逻辑。 + +本节主要涉及的前端页面的UI的设计和实现,之后对接服务端接口即可。需要注意,数据库表有数据调整。在本节中提供在 docs 下,记得更新。 + +## 二、流程设计 + +本节会改造前端对接接口,增加接口的返回信息,包括;是否解锁奖品、解锁奖品所需的次数、装配抽奖活动和新抽奖接口。 + +
+ +
+ +
+ +
+ +- 从这节开始,就以活动为入口进行抽奖了,策略不会直接对外了。 +- 之所以解耦策略,是因为外部的一些微服务,有些是不需要活动的配置,只是希望提供一个抽奖策略。 \ No newline at end of file diff --git "a/docs/md/project/big-market/web/\347\254\2545\350\212\202\357\274\232\345\257\271\346\216\245\350\201\224\350\260\203\351\242\235\345\272\246\347\255\276\345\210\260\346\235\203\351\207\215\346\216\245\345\217\243.md" "b/docs/md/project/big-market/web/\347\254\2545\350\212\202\357\274\232\345\257\271\346\216\245\350\201\224\350\260\203\351\242\235\345\272\246\347\255\276\345\210\260\346\235\203\351\207\215\346\216\245\345\217\243.md" new file mode 100644 index 000000000..598324fe5 --- /dev/null +++ "b/docs/md/project/big-market/web/\347\254\2545\350\212\202\357\274\232\345\257\271\346\216\245\350\201\224\350\260\203\351\242\235\345\272\246\347\255\276\345\210\260\346\235\203\351\207\215\346\216\245\345\217\243.md" @@ -0,0 +1,36 @@ +--- +title: 第5节:对接联调额度签到权重接口 +pay: https://t.zsxq.com/FahSC +--- + +# 《大营销平台系统设计实现》 - 前端页面 第5节:对接联调额度签到权重接口 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:开发前端UI增加;额度展示、签到按钮、抽奖权重,对接服务端接口渲染UI页面。 +- **课程视频**:[https://t.zsxq.com/MiNib](https://t.zsxq.com/MiNib) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +开发前端UI增加;额度展示、签到按钮、抽奖权重,对接服务端接口渲染UI页面。这个过程也是实际开发中,后端开发好接口后,前端做好了UI,开始联调对接调试接口和页面UI问题的过程。 + +## 二、流程设计 + +如图,本节的目的就是把这些页面效果和接口对接完成; + +
+ +
+ +
+ +
+ +- 前端,开发UI界面,这部分是 React 的代码开发,可以做为了解学习。面试中不会问前端代码,只要能看懂即可。 +- 后端,修改蓝色的类,在提交个 git 记录中可以看到。主要是配置些注解和字段取值,以及判空的操作。 diff --git "a/docs/md/project/big-market/web/\347\254\2546\350\212\202\357\274\232\345\257\271\346\216\245\350\201\224\350\260\203\347\247\257\345\210\206\346\265\201\347\250\213\346\216\245\345\217\243.md" "b/docs/md/project/big-market/web/\347\254\2546\350\212\202\357\274\232\345\257\271\346\216\245\350\201\224\350\260\203\347\247\257\345\210\206\346\265\201\347\250\213\346\216\245\345\217\243.md" new file mode 100644 index 000000000..4c233d435 --- /dev/null +++ "b/docs/md/project/big-market/web/\347\254\2546\350\212\202\357\274\232\345\257\271\346\216\245\350\201\224\350\260\203\347\247\257\345\210\206\346\265\201\347\250\213\346\216\245\345\217\243.md" @@ -0,0 +1,36 @@ +--- +title: 第6节:对接联调积分流程接口 +pay: https://t.zsxq.com/S09wH +--- + +# 《大营销平台系统设计实现》 - 前端页面 第6节:对接联调积分流程接口 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:与服务端接口对接,增加前端UI对积分的渲染,完成积分展示、兑换抽奖、抽奖抽取积分的全流程链路。 +- **课程视频**:[https://t.zsxq.com/53nXf](https://t.zsxq.com/53nXf) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +## 一、本章诉求 + +与服务端接口对接,增加前端UI对积分的渲染,完成积分展示、兑换抽奖、抽奖抽取积分的全流程链路。 + +到本节,对接完成,第三阶段的积分就完成了。小伙伴学习后可以尝试绘制整个功能的流程图。增强学习记忆。 + +## 二、业务流程 + +如图,前端UI的渲染内容; + +
+ +
+ +- 最上面,展示会员卡以及合并签到在会员卡模块。 +- 之后在提供出sku商品,这些商品可以点击兑换。 +- 调整奖品配置,增加抽奖随机积分的概率。 + diff --git a/docs/md/project/chatbot-api/chatbot-api.md b/docs/md/project/chatbot-api/chatbot-api.md new file mode 100644 index 000000000..b09c68773 --- /dev/null +++ b/docs/md/project/chatbot-api/chatbot-api.md @@ -0,0 +1,69 @@ +--- +title: 《ChatGPT AI 问答助手》实战项目 +lock: no +--- + +# 《ChatGPT AI 问答助手》实战项目 - 资料免费、代码开源、视频课程 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +## 1. 项目介绍 + +**《ChatGPT AI 问答助手》** 开源免费项目,涵盖爬虫接口、ChatGPT API对接、DDD架构设计、镜像打包、Docker容器部署,小巧精悍,流程全面。对于Java编程伙伴来说,非常具有学习价值。 + +❤️ 这个项目本身是小傅哥为自己的知识星球开发的一个智能问答回复系统,用于帮助读者解决一些常见的技术问题,提高回答效率也减少小傅哥的对此类问题的时间投入。通过演示我们可以看到,有了这样一个智能AI问答助手,可以大大的减少很多对于这些通用类技术问题的回复,同时也可以把这样的问答内容沉淀到知识星球,方便其他人学习使用。 + +《ChatGPT AI 问答助手》这样一个项目,要用到哪些技术手段呢?它包含;SpringBoot、DDD架构、Github仓库使用、接口爬虫、AI接口对接、定时任务、镜像打包、Docker容器部署等内容。 + +可以说麻雀虽小,五脏俱全。代码量不大但流程很完整,对于正在学习Java的伙伴来说,非常具有学习价值。 + +为了让粉丝伙伴更好的学习这个项目,小傅哥把它免费开源出来,并且是录制好对应的视频课程,一行行带着大家手写代码学习这个项目。 + +包括工程的创建、Github仓库使用、push代码等,因为只有这样才能让更多新人有一条进入学习编程的大门。 + +**注意**: +1. 技术栈:Java、SpringBoot、爬虫、ChatGPT、Job、Docker +2. OpenAi Keys 申请:[https://beta.openai.com/account/api-keys](https://beta.openai.com/account/api-keys) - 用于处理扫码知识星球问题进行调用获取答案。 +3. 在学习的过程中,可以看到每一个章节都有一个对应的代码分支,可以把代码拉取到本地切换到对应的分支进行对照学习。 + +## 2. 课程目录 + +**注意📢** `视频`;课程更新到小傅哥的B站:[]() +**注意📢** `源码`;发布到 Github、Gitcode +- Github:[https://github.com/fuzhengwei/chatbot-api](https://github.com/fuzhengwei/chatbot-api) +- Gitcode:[https://gitcode.net/fuzhengwei/chatbot-api](https://gitcode.net/fuzhengwei/chatbot-api) + +| 目录 - `点击章节进入到学习视频` | Github | Gitcode | +| -------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| [开篇介绍,学习引导](https://www.bilibili.com/video/BV1YT411o7Hb) | | | +| [第1节:SpringBoot DDD 工程创建和 Github/Gitcode 仓库使用](https://www.bilibili.com/video/BV1RR4y1b7UQ) | [【23_xfg_init_project】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_init_project) | [【23_xfg_init_project】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_init_project) | +| [第2节:创建知识星球,爬取接口信息](https://www.bilibili.com/video/BV1L341197x1) | [【23_xfg_zsxq_api】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_zsxq_api) | [【23_xfg_zsxq_api】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_zsxq_api) | +| [第3节:知识星球接口领域服务开发](https://www.bilibili.com/video/BV1Wv4y1671x) | [【23_xfg_zsxq_domain】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_zsxq_domain) | [【23_xfg_zsxq_domain】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_zsxq_domain) | +| [第4节:对接ChatGPT,调用接口](https://www.bilibili.com/video/BV1KT411Z7z3) | [【23_xfg_chatgpt】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_chatgpt) | [【23_xfg_chatgpt】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_chatgpt) | +| [第5节:整合知识星球与ChatGPT,完成自动化回答](https://www.bilibili.com/video/BV1Ny4y1R7EK) | [【23_xfg_job】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_job) | [【23_xfg_job】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_job) | +| [第6节:打包镜像文件,部署服务到 Docker 容器](https://www.bilibili.com/video/BV1gT411C7nn) | [【23_xfg_docker】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_docker) | [【23_xfg_docker】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_docker) | +| [【扩展】第7节:多组任务服务配置](https://www.bilibili.com/video/BV1XR4y1h7JP) | [【230127-xfg-task】](https://github.com/fuzhengwei/chatbot-api/tree/230127-xfg-task) | [【230127-xfg-task】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/230127-xfg-task) | + +## 3. 加入星球【ChatGPT AI 问答助手】 + +你可以通过微信扫码,加入知识星球【ChatGPT AI 问答助手】,在手机端对ChatGPT进行提问。**需要加群交流项目的伙伴**,可以添加微信【`fustack`】备注【`ChatGPT 项目加群`】 + +
+ +
+ +## 4. 版权说明 + +此项目为 Apache License 2.0 开源协议项目,以学习为目的进行创作,禁止培训机构、私人号主、公司组织等以各类收费形式进行销售。如果你有合作诉求,请与小傅哥联系获得书面授权,微信:fustack + +--- + +:bus: 其他项目: [`IM 仿微信`](https://github.com/fuzhengwei/NaiveChat) | [`Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践`](https://github.com/fuzhengwei/Lottery) | [`API网关:中间件设计和实践`](https://github.com/fuzhengwei/api-gateway) | [`手写MyBatis`](https://github.com/fuzhengwei/small-mybatis) | [更多搜索...](https://github.com/fuzhengwei?tab=repositories) + + + diff --git "a/docs/md/project/chatbot-api/\347\254\2541\350\212\202\357\274\232\345\267\245\347\250\213\345\210\233\345\273\272\345\222\214\344\273\223\345\272\223\344\275\277\347\224\250.md" "b/docs/md/project/chatbot-api/\347\254\2541\350\212\202\357\274\232\345\267\245\347\250\213\345\210\233\345\273\272\345\222\214\344\273\223\345\272\223\344\275\277\347\224\250.md" new file mode 100644 index 000000000..ca6afa0fe --- /dev/null +++ "b/docs/md/project/chatbot-api/\347\254\2541\350\212\202\357\274\232\345\267\245\347\250\213\345\210\233\345\273\272\345\222\214\344\273\223\345\272\223\344\275\277\347\224\250.md" @@ -0,0 +1,13 @@ +--- +title: 第1节:工程创建和仓库使用 +lock: no +--- + +# 《ChatGPT AI 问答助手》第1节:工程创建和仓库使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + \ No newline at end of file diff --git "a/docs/md/project/chatbot-api/\347\254\2542\350\212\202\357\274\232\345\210\233\345\273\272\347\237\245\350\257\206\346\230\237\347\220\203\357\274\214\347\210\254\345\217\226\346\216\245\345\217\243\344\277\241\346\201\257.md" "b/docs/md/project/chatbot-api/\347\254\2542\350\212\202\357\274\232\345\210\233\345\273\272\347\237\245\350\257\206\346\230\237\347\220\203\357\274\214\347\210\254\345\217\226\346\216\245\345\217\243\344\277\241\346\201\257.md" new file mode 100644 index 000000000..9edb0078b --- /dev/null +++ "b/docs/md/project/chatbot-api/\347\254\2542\350\212\202\357\274\232\345\210\233\345\273\272\347\237\245\350\257\206\346\230\237\347\220\203\357\274\214\347\210\254\345\217\226\346\216\245\345\217\243\344\277\241\346\201\257.md" @@ -0,0 +1,13 @@ +--- +title: 第2节:创建知识星球,爬取接口信息 +lock: no +--- + +# 《ChatGPT AI 问答助手》第2节:创建知识星球,爬取接口信息 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + \ No newline at end of file diff --git "a/docs/md/project/chatbot-api/\347\254\2543\350\212\202\357\274\232\347\237\245\350\257\206\346\230\237\347\220\203\346\216\245\345\217\243\351\242\206\345\237\237\346\234\215\345\212\241\345\274\200\345\217\221.md" "b/docs/md/project/chatbot-api/\347\254\2543\350\212\202\357\274\232\347\237\245\350\257\206\346\230\237\347\220\203\346\216\245\345\217\243\351\242\206\345\237\237\346\234\215\345\212\241\345\274\200\345\217\221.md" new file mode 100644 index 000000000..5c0d1c345 --- /dev/null +++ "b/docs/md/project/chatbot-api/\347\254\2543\350\212\202\357\274\232\347\237\245\350\257\206\346\230\237\347\220\203\346\216\245\345\217\243\351\242\206\345\237\237\346\234\215\345\212\241\345\274\200\345\217\221.md" @@ -0,0 +1,13 @@ +--- +title: 第3节:知识星球接口领域服务开发 +lock: no +--- + +# 《ChatGPT AI 问答助手》第3节:知识星球接口领域服务开发 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + \ No newline at end of file diff --git "a/docs/md/project/chatbot-api/\347\254\2544\350\212\202\357\274\232\345\257\271\346\216\245ChatGPT\357\274\214\350\260\203\347\224\250\346\216\245\345\217\243.md" "b/docs/md/project/chatbot-api/\347\254\2544\350\212\202\357\274\232\345\257\271\346\216\245ChatGPT\357\274\214\350\260\203\347\224\250\346\216\245\345\217\243.md" new file mode 100644 index 000000000..08cf28b48 --- /dev/null +++ "b/docs/md/project/chatbot-api/\347\254\2544\350\212\202\357\274\232\345\257\271\346\216\245ChatGPT\357\274\214\350\260\203\347\224\250\346\216\245\345\217\243.md" @@ -0,0 +1,13 @@ +--- +title: 第4节:对接ChatGPT,调用接口 +lock: no +--- + +# 《ChatGPT AI 问答助手》第4节:对接ChatGPT,调用接口 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + \ No newline at end of file diff --git "a/docs/md/project/chatbot-api/\347\254\2545\350\212\202\357\274\232\346\225\264\345\220\210\347\237\245\350\257\206\346\230\237\347\220\203\344\270\216ChatGPT\357\274\214\345\256\214\346\210\220\350\207\252\345\212\250\345\214\226\345\233\236\347\255\224.md" "b/docs/md/project/chatbot-api/\347\254\2545\350\212\202\357\274\232\346\225\264\345\220\210\347\237\245\350\257\206\346\230\237\347\220\203\344\270\216ChatGPT\357\274\214\345\256\214\346\210\220\350\207\252\345\212\250\345\214\226\345\233\236\347\255\224.md" new file mode 100644 index 000000000..2bfa695e9 --- /dev/null +++ "b/docs/md/project/chatbot-api/\347\254\2545\350\212\202\357\274\232\346\225\264\345\220\210\347\237\245\350\257\206\346\230\237\347\220\203\344\270\216ChatGPT\357\274\214\345\256\214\346\210\220\350\207\252\345\212\250\345\214\226\345\233\236\347\255\224.md" @@ -0,0 +1,13 @@ +--- +title: 第5节:整合知识星球与ChatGPT,完成自动化回答 +lock: no +--- + +# 《ChatGPT AI 问答助手》第5节:整合知识星球与ChatGPT,完成自动化回答 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + \ No newline at end of file diff --git "a/docs/md/project/chatbot-api/\347\254\2546\350\212\202\357\274\232\351\203\250\347\275\262\346\234\215\345\212\241\345\210\260 Docker \345\256\271\345\231\250.md" "b/docs/md/project/chatbot-api/\347\254\2546\350\212\202\357\274\232\351\203\250\347\275\262\346\234\215\345\212\241\345\210\260 Docker \345\256\271\345\231\250.md" new file mode 100644 index 000000000..4b13298b1 --- /dev/null +++ "b/docs/md/project/chatbot-api/\347\254\2546\350\212\202\357\274\232\351\203\250\347\275\262\346\234\215\345\212\241\345\210\260 Docker \345\256\271\345\231\250.md" @@ -0,0 +1,13 @@ +--- +title: 第6节:部署服务到 Docker 容器 +lock: no +--- + +# 《ChatGPT AI 问答助手》第6节:部署服务到 Docker 容器 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + diff --git "a/docs/md/project/chatbot-api/\347\254\2547\350\212\202\357\274\232\345\244\232\347\273\204\344\273\273\345\212\241\346\234\215\345\212\241\351\205\215\347\275\256.md" "b/docs/md/project/chatbot-api/\347\254\2547\350\212\202\357\274\232\345\244\232\347\273\204\344\273\273\345\212\241\346\234\215\345\212\241\351\205\215\347\275\256.md" new file mode 100644 index 000000000..0f6ada942 --- /dev/null +++ "b/docs/md/project/chatbot-api/\347\254\2547\350\212\202\357\274\232\345\244\232\347\273\204\344\273\273\345\212\241\346\234\215\345\212\241\351\205\215\347\275\256.md" @@ -0,0 +1,13 @@ +--- +title: 第7节:多组任务服务配置 +lock: no +--- + +# 《ChatGPT AI 问答助手》第7节:多组任务服务配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + \ No newline at end of file diff --git "a/docs/md/project/chatgpt/api/\347\254\25410\350\212\202\357\274\232\345\272\224\347\224\250\345\210\206\345\270\203\345\274\217\350\256\276\350\256\241.md" "b/docs/md/project/chatgpt/api/\347\254\25410\350\212\202\357\274\232\345\272\224\347\224\250\345\210\206\345\270\203\345\274\217\350\256\276\350\256\241.md" new file mode 100644 index 000000000..a4463aa8a --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\25410\350\212\202\357\274\232\345\272\224\347\224\250\345\210\206\345\270\203\345\274\217\350\256\276\350\256\241.md" @@ -0,0 +1,39 @@ +--- +title: 第10节:应用分布式设计 +pay: https://t.zsxq.com/13HOWvLJq +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第10节:应用分布式设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:扩展应用为分布式设计,修改Guava内存保存验证码,为Redis服务。满足应用的横向扩展。 +- **课程视频**:[https://t.zsxq.com/13HXsbW76](https://t.zsxq.com/13HXsbW76) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +当前的 OpenAi(ChatGPT、ChatGLM)项目,需要用户登录鉴权,才能登录使用服务。但对于验证码的保存是使用的 Guava 内存存储,如果是需要分布式部署,那么就会出现用户在服务1上生成的验证码,第二次访问到服务2,这样就不能正常验证了。所以我们需要 Redis 服务,来保存验证码。 + +注意:如果你有企业公众号,那么可以对接微信直接扫码登录。 + +## 二、流程改造 + +将原有的 Guava 用于验证码存储和校验的地方,替换为 Redis 服务。 + +
+ +
+ +- 在原有使用 Guava 存储和校验的流程中,替换为 Redis 服务。 +- 此外,已经在初始化开发环境的 compose 中,安装了 redis-admin、phpmyadmin 来管理Redis服务和MySQL服务。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/api/\347\254\25411\350\212\202\357\274\232dall-e\346\226\207\347\224\237\345\233\276.md" "b/docs/md/project/chatgpt/api/\347\254\25411\350\212\202\357\274\232dall-e\346\226\207\347\224\237\345\233\276.md" new file mode 100644 index 000000000..0516450da --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\25411\350\212\202\357\274\232dall-e\346\226\207\347\224\237\345\233\276.md" @@ -0,0 +1,24 @@ +--- +title: 第11节:dall-e 文生图 +pay: https://t.zsxq.com/14r9MLwf7 +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第11节:dall-e 文生图 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:本节为视频手把手开发,增加文生图功能。如果不需要看视频,也可以直接学习分支代码。前后端分支为:231125-xfg-images +- **课程视频**:[https://t.zsxq.com/14r9MLwf7](https://t.zsxq.com/14r9MLwf7) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + diff --git "a/docs/md/project/chatgpt/api/\347\254\2541\350\212\202\357\274\232API\345\267\245\347\250\213\346\220\255\345\273\272\345\222\214\347\256\200\345\215\225\350\256\277\351\227\256\350\256\244\350\257\201.md" "b/docs/md/project/chatgpt/api/\347\254\2541\350\212\202\357\274\232API\345\267\245\347\250\213\346\220\255\345\273\272\345\222\214\347\256\200\345\215\225\350\256\277\351\227\256\350\256\244\350\257\201.md" new file mode 100644 index 000000000..72e6f629a --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\2541\350\212\202\357\274\232API\345\267\245\347\250\213\346\220\255\345\273\272\345\222\214\347\256\200\345\215\225\350\256\277\351\227\256\350\256\244\350\257\201.md" @@ -0,0 +1,33 @@ +--- +title: 第1节:API工程搭建和简单访问认证 +pay: https://t.zsxq.com/0dh0CXAsQ +--- + +# 《ChatGPT 微服务应用体系构建》 - chagpt-api 第1节:API工程搭建和简单访问认证 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:搭建ChatGPT-API工程,作为统一的后端访问入口,提供API能力。这些能力后随着章节的推进,不断地叠加,如;访问验证、OpenAI会话、公众号、企业微信等。 +- **课程视频**:[https://t.zsxq.com/0dzm349us](https://t.zsxq.com/0dzm349us) + +## 一、本章诉求 + +一般我们研发人员在需要完成一个完整需求前,最好是先梳理需求,并把用于完成需求目标的各个功能节点进行单个验证,以确保方案的可行性。有了这些基本功能模块的验证后,就可以逐步再把各个模块像乐高积木一样搭建起来,搭建的过程就是架构和设计模式的运用。 + +那么我们本章的目标就是在 Nginx 访问接口时,做一些权限校验,只有校验通过才能访问接口,否则就直接返回失败。有了这样的控制,你是不是能想象到,你在一些网站购买的一个月有效期的服务,过期就不能使用的场景。 + +那么在本章节小傅哥会带着大家先搭建一个简单的 SpringBoot 工程,并在工程中提供用于与 Nginx 的 auth_request 模块做验证处理的访问接口。在本章节会涉及到 Maven 工程的创建、代码提交、启动发布、Nginx auth 配置等内容。 + +## 二、流程设计 + +整个流程为;以用户视角访问API开始,进入 Nginx 的 auth 认证模块,调用 SpringBoot 提供的认证服务。根据认证结果调用重定向到对应的 API 接口或者 404 页面。 + +
+ +
+ +由于 OpenAI 或者本身自己训练的一套服务,都会有服务器成本。所以基于这样一个模型结构,后续可以通过用户购买 Token 的时效性进行成本回收。这也是其中一种商业变现的思路。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/api/\347\254\2542\350\212\202\357\274\232Shiro\347\231\273\345\275\225\346\216\210\346\235\203\345\217\221\346\224\276\350\256\277\351\227\256token.md" "b/docs/md/project/chatgpt/api/\347\254\2542\350\212\202\357\274\232Shiro\347\231\273\345\275\225\346\216\210\346\235\203\345\217\221\346\224\276\350\256\277\351\227\256token.md" new file mode 100644 index 000000000..a916996f6 --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\2542\350\212\202\357\274\232Shiro\347\231\273\345\275\225\346\216\210\346\235\203\345\217\221\346\224\276\350\256\277\351\227\256token.md" @@ -0,0 +1,31 @@ +--- +title: 第2节:Shiro登录授权发放访问token +pay: https://t.zsxq.com/0d1nGd9yJ +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第2节:Shiro登录授权发放访问token + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:通过 SpringBoot 整合 Shiro + JWT 进行登录验证,发放使用API的准入Token信息。 +- **课程视频**:[https://t.zsxq.com/0dpw1gaHJ](https://t.zsxq.com/0dpw1gaHJ) + +## 一、本章诉求 + +以用户使用 OpenAI 接口,如;`http://localhost/api` 时,需要根据用户身份标识做一些访问的验证和限定。最直接就是在使用 api 的时候把用户的账号和密码一同和访问 api 传递过来,如;`http://localhost/api?userId=xfg&password=123` 但这样就把用户的密码信息给泄漏了,是非常不安全的。 + +所以我们本章节需要根据用户的账密,先通过登录验证的方式,发放一个 token,之后用户再使用这个 token 配置到链接后面使用。如;`http://localhost/api?token=xxxxx` 这样就安全多了。—— 一般 token 是配置到 http 请求头信息中,但这里为了更加方便用户传递参数,所以这样处理了。 + +## 二、流程设计 + +整个流程为;以用户访问一个登录接口,服务端使用用户的账号和密码进行验证,验证通过后发放 Token,之后再使用 Token 访问 OpenAI 地址。 + +
+ +
+ +在本章节的实现中,需要在 SpringBoot 工程中引入进来 Shiro、JWT,进行处理。读者可以在代码验证通过后,根据 Shiro、JWT 这样的关键字在 [ChatGPT](https://itedus.cn) 中进行检索,补充基础知识。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/api/\347\254\2543\350\212\202\357\274\232\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\351\252\214\347\255\276\345\222\214\345\210\235\346\255\245\345\257\271\346\216\245OpenAI.md" "b/docs/md/project/chatgpt/api/\347\254\2543\350\212\202\357\274\232\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\351\252\214\347\255\276\345\222\214\345\210\235\346\255\245\345\257\271\346\216\245OpenAI.md" new file mode 100644 index 000000000..16c4bf094 --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\2543\350\212\202\357\274\232\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\351\252\214\347\255\276\345\222\214\345\210\235\346\255\245\345\257\271\346\216\245OpenAI.md" @@ -0,0 +1,33 @@ +--- +title: 第3节:微信公众号验签和初步对接OpenAI +pay: https://t.zsxq.com/0dJzAYXkG +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第3节:微信公众号验签和初步对接OpenAI + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:微信公众号SDK对接,并通过异步调用的方式处理消息应答。完成开发后使用内网穿透工具做本地的测试验证。 +- **课程视频**:[https://t.zsxq.com/0dRFsWIoD](https://t.zsxq.com/0dRFsWIoD) + +## 一、本章诉求 + +以实际应用为目标,初步尝试把OpenAI对接到公众号上,实现消息自动回复功能。随着 OpenAI 生成式服务的日益完善,以后各个大厂都会有自己品牌的 GPT 服务,并把这些服务对接到你现在通过问答方式回复的场景中。虽然我们本章来引入微信公众号开发的 SDK 和 chatgpt-sdk-java 到 chatgpt-api 做一个简单的对接使用。 + +**注意**:ChatGPT 对接微信公众号,可能会受到一些限制。但目前我们主要以教学为目的,如果以后腾讯自己的 GPT 产品问世后,那么会有更好的体验。所以现在是先学技能! + +## 二、流程设计 + +整个流程为;对接微信公众号,提供验签服务的get请求和处理消息的post请求以及初始化 chatgpt-sdk-java 服务。当验签完成后接收post请求并做应答处理。 + +
+ +
+ +在本章节的学习,你需要准备一些基础内容; +- 微信订阅号申请:[https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN](https://mp.weixin.qq.com/cgi-bin/registermidpage?action=index&lang=zh_CN) - 个人即可申请,也支持基本的开发对接。 +- 内网穿透使用:[https://natapp.cn/](https://natapp.cn/tunnel/buy) - 你可以使用免费的隧道,也可以购买最低配置,更稳定。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/api/\347\254\2544\350\212\202\357\274\232\345\267\245\347\250\213\351\207\215\346\236\204\345\222\214\346\265\201\345\274\217\345\274\202\346\255\245\345\223\215\345\272\224\346\216\245\345\217\243\345\256\236\347\216\260.md" "b/docs/md/project/chatgpt/api/\347\254\2544\350\212\202\357\274\232\345\267\245\347\250\213\351\207\215\346\236\204\345\222\214\346\265\201\345\274\217\345\274\202\346\255\245\345\223\215\345\272\224\346\216\245\345\217\243\345\256\236\347\216\260.md" new file mode 100644 index 000000000..7bd393c11 --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\2544\350\212\202\357\274\232\345\267\245\347\250\213\351\207\215\346\236\204\345\222\214\346\265\201\345\274\217\345\274\202\346\255\245\345\223\215\345\272\224\346\216\245\345\217\243\345\256\236\347\216\260.md" @@ -0,0 +1,76 @@ +--- +title: 第4节:工程重构和流式异步响应接口实现 +pay: https://t.zsxq.com/103bfx3c6 +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第4节:工程重构和流式异步响应接口实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:通过新的 DDD 架构模型重构工程结构,并使用和不使用,设计模式对照开发流式异步应答接口实现,让小伙伴可以学习如何使用这些技术知识来驾驭自己的项目工程。 +- **课程视频**:[https://t.zsxq.com/10Cslo1eZ](https://t.zsxq.com/10Cslo1eZ) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +本章最核心的诉求就是开发一个可以用于后续提供给 ChatGPT-WEB 页面使用的异步响应接口,也就是通常我们使用 ChatGPT 那种打字机的效果。 + +但小傅哥开发代码,绝对不只是做个接口 CRUD 一下就完事了。所以本章**既分高下,也绝生死😂**。因为我们要重构下工程了,使用以下 DDD 架构模式构建工程,同时实现出一个不使用设计模式和使用设计模式的对比,方便大家更好的理解功能需求在代码中的设计实现。 + +## 二、流程设计 + +整个流程为;以DDD工程结构模型,提供 HTTP 响应式接口,调用 OpenAI 应答请求信息。 + +
+ +
+ +- 整个流程其实简单,复杂点在于怎么提供一个异步响应接口,并怎么把这个需要一堆的调用代码的实现,分配到各个类里去处理。 +- 而同时小傅哥也希望把另外一套DDD架构,通过这个项目需求给大家展示出来。 + +## 三、架构讲解 + +### 1. 模型抽象 + +小傅哥提到了一个简单的开发模型。开发代码可以理解为:`“定义属性 -> 创建方法 -> 调用展示”`但这个模型结构过于简单,不太适合运用了各类分布式技术栈以及更多逻辑的 DDD 架构。所以在 DDD 这里,我们把开发代码可以抽象为:`“触发 -> 函数 -> 连接”` 如图; + +
+ +
+ +- DDD 架构常用于微服务场景,因此也一个系统的调用方式就不只是 HTTP 还包括;`RPC 远程`、`MQ 消息`、`TASK 任务`,因此这些种方式都可以理解为触发。 +- 通过触发调用函数方法,我们这里可以把各个服务都当成一个函数方法来看。而函数方法通过连接,调用到其他的接口、数据库、缓存来完成函数逻辑。 + +接下来,小傅哥在带着大家把这些所需的模块,拆分到对应的DDD系统架构中。 + +
+ +
+ +### 2. 架构分层 + +如下是 DDD 架构的一种分层结构,也可以有其他种方式,核心的重点在于适合你所在场景的业务开发。以下的分层结构,是小傅哥在使用 DDD 架构多种的方式开发代码后,做了简化和处理的。右侧的连线是各个模块的依赖关系。接下来小傅哥就给大家做一下模块的介绍。 + +
+ +
+ +- **接口定义 - xfg-frame-api**:因为微服务中引用的 RPC 需要对外提供接口的描述信息,也就是调用方在使用的时候,需要引入 Jar 包,让调用方好能依赖接口的定义做代理。 +- **应用封装 - xfg-frame-app**:这是应用启动和配置的一层,如一些 aop 切面或者 config 配置,以及打包镜像都是在这一层处理。你可以把它理解为专门为了启动服务而存在的。 +- **领域封装 - xfg-frame-domain**:领域模型服务,是一个非常重要的模块。无论怎么做DDD的分层架构,domain 都是肯定存在的。在一层中会有一个个细分的领域服务,在每个服务包中会有【模型、仓库、服务】这样3部分。 +- **仓储服务 - xfg-frame-infrastructure**:基础层依赖于 domain 领域层,因为在 domain 层定义了仓储接口需要在基础层实现。这是依赖倒置的一种设计方式。 +- **领域封装 - xfg-frame-trigger**:触发器层,一般也被叫做 adapter 适配器层。用于提供接口实现、消息接收、任务执行等。所以对于这样的操作,小傅哥把它叫做触发器层。 +- **类型定义 - xfg-frame-types**:通用类型定义层,在我们的系统开发中,会有很多类型的定义,包括;基本的 Response、Constants 和枚举。它会被其他的层进行引用使用。 +- **领域编排【可选】 - xfg-frame-case**:领域编排层,一般对于较大且复杂的的项目,为了更好的防腐和提供通用的服务,一般会添加 case/application 层,用于对 domain 领域的逻辑进行封装组合处理。 + + diff --git "a/docs/md/project/chatgpt/api/\347\254\2545\350\212\202\357\274\232\345\205\254\344\274\227\345\217\267\345\217\221\351\200\201\351\252\214\350\257\201\347\240\201\351\211\264\346\235\203\347\231\273\345\275\225.md" "b/docs/md/project/chatgpt/api/\347\254\2545\350\212\202\357\274\232\345\205\254\344\274\227\345\217\267\345\217\221\351\200\201\351\252\214\350\257\201\347\240\201\351\211\264\346\235\203\347\231\273\345\275\225.md" new file mode 100644 index 000000000..c18c7663e --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\2545\350\212\202\357\274\232\345\205\254\344\274\227\345\217\267\345\217\221\351\200\201\351\252\214\350\257\201\347\240\201\351\211\264\346\235\203\347\231\273\345\275\225.md" @@ -0,0 +1,41 @@ +--- +title: 第5节:公众号发送验证码鉴权登录 +pay: https://t.zsxq.com/10sh5hUmt +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第5节:公众号发送验证码鉴权登录 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★★☆ +- **本章重点**:基于 DDD 架构,设计鉴权、微信公众号、OpenAI三个服务模块的功能实现,完成对公众号的对接下发验证码,以及提供接口登录鉴权和下发Token,再到OpenAI使用Token验证鉴权信息。 +- **课程视频**:[https://t.zsxq.com/10GlIL0ue](https://t.zsxq.com/10GlIL0ue) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +本章的核心点在于登录鉴权,鉴权通过后才允许对 ChatGPT 的使用。一般一个软件服务,都会包括3大块;登录、支付、服务,只提供服务叫开源、提供服务和登录叫吸粉、三个都提供就商业变现。 + +所以当你有明确的商业变现模式后,你就可以把用户引导到一个确定的地方进行登录。比如小傅哥带着大家搞的本章节就是把用户引入到公众号进行登录,这样用户也就关注了你的公众号。好,那么本章节我们做一下这样的开发,让以后此类需求的伙伴,可以学到对应的解决方案和实现途径。 + +## 二、流程设计 + +公众号分为个人和企业,企业的公众号功能更完整,可以简单的通过用户扫码的方式进行登录,因为扫码的时候可以涵盖请求连接上的信息。但个人公众号是不允许这样的做的,所以个人公众号一般有2个方式; +- A - 通过网页的浏览器指纹组件库,生成唯一ID,并在网页轮训公众号服务接口判断 ID 是否写入,并引导用户把 ID 通过公众号回复。这样就可以做到自动登录/解锁了。 +- B - 验证码方式,让用户在公众号回复指定数字,获取一个验证码,此时验证码写入内存/缓存中。并引导用户拿到验证码在网页登录页面输入,输入后由前端页面调用服务端接口校验验证码。此时校验后可以拿到一个 Token 授权使用。 + +
+ +
+ +- 如图,就是用户整个登录到授权通过的旅程,目前本章节主要来实现微信公众号的鉴权接口服务端部分,下一节来对接前端UI。 +- 如果,你有企业公众号,也可以修改为扫码登录方式下发 Token 体验会更好。就不用用户在输入一次验证码了。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/api/\347\254\2546\350\212\202\357\274\232\347\231\275\345\220\215\345\215\225\345\222\214\346\225\217\346\204\237\350\257\215\350\247\204\345\210\231\350\277\207\346\273\244.md" "b/docs/md/project/chatgpt/api/\347\254\2546\350\212\202\357\274\232\347\231\275\345\220\215\345\215\225\345\222\214\346\225\217\346\204\237\350\257\215\350\247\204\345\210\231\350\277\207\346\273\244.md" new file mode 100644 index 000000000..d61f7a829 --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\2546\350\212\202\357\274\232\347\231\275\345\220\215\345\215\225\345\222\214\346\225\217\346\204\237\350\257\215\350\247\204\345\210\231\350\277\207\346\273\244.md" @@ -0,0 +1,41 @@ +--- +title: 第6节:白名单和敏感词规则过滤 +pay: https://t.zsxq.com/12kDZPWYq +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第6节:白名单和敏感词规则过滤 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:通过策略模式 + 工厂服务,实现规则过滤功能。并将这样的功能结合到会话模型中。通过这样的设计,解耦核心流程与旁路分支。—— 重点:你必须要理解,规则是一个随着业务发展频繁变动的流程,但核心的代码并不会总调整。所以我们需要将这两部分分离。 +- **课程视频**:[https://t.zsxq.com/12o12nG8i](https://t.zsxq.com/12o12nG8i) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +生成式服务的调用和响应,只能算是一个半成品,还缺少必备的控制和管理。比如;你部署服务后,外部的用户调用时是要做频次限制的,此外还要做非常重要的敏感词过滤。 + +所以本章节我们设计一个规则过滤模型教会大家来开发这样的功能,此外本章中的规则是做了2个实现,一个频次、一个敏感词。小傅哥建议你学习后,可以再添加一个频率限制。这样做完后,你就彻底学会这套规则的模型设计和实现了。 + +## 二、流程设计 + +频次、频率、白名单、敏感词等,都是用于支撑核心业务之外辅助流程,这些流程都是比较容易随着业务的变动而发生变化。所以我们要把这类东西设计在核心流程之外,而不能直接把规则的代码与核心业务的代码写在一块。因为区分不出边界的代码,会让工程的腐化程度不断加剧。 + +所以这里小傅哥带着你设计一个规则引擎,来扩展这些快内容的实现; + +
+ +
+ +- 在前面章节中,我们把应答的处理设计为一个独立的 openai 领域模型结构,并对应答流程设计了接口和抽象类。 +- 那么现在我们就可以在 openai 领域模型中设计规则模型的实现和调用,来处理流程中的规则内容处理。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/api/\347\254\2547\350\212\202\357\274\232\347\224\250\346\210\267\351\242\235\345\272\246\350\264\246\346\210\267\351\242\206\345\237\237\345\256\236\347\216\260.md" "b/docs/md/project/chatgpt/api/\347\254\2547\350\212\202\357\274\232\347\224\250\346\210\267\351\242\235\345\272\246\350\264\246\346\210\267\351\242\206\345\237\237\345\256\236\347\216\260.md" new file mode 100644 index 000000000..58118012a --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\2547\350\212\202\357\274\232\347\224\250\346\210\267\351\242\235\345\272\246\350\264\246\346\210\267\351\242\206\345\237\237\345\256\236\347\216\260.md" @@ -0,0 +1,39 @@ +--- +title: 第7节:用户额度账户的校验领域实现 +pay: https://t.zsxq.com/12pbCR1IN +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第7节:用户额度账户的校验领域实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★★☆ +- **本章重点**:设计用户额度账户,让整个 OpenAI 产品具备对外提供服务能力,可以让其他用户后续扩展为购买商品的方式进行使用。 +- **课程视频**:【上】[https://t.zsxq.com/12Xc0MhTk](https://t.zsxq.com/12Xc0MhTk)【下】[https://t.zsxq.com/126pzdJTG](https://t.zsxq.com/126pzdJTG) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +OpenAI 服务已经在第一阶段教会大家打包上线对外了,能让自己使用了,也可以让其他用户使用并设置了统一的访问次数控制。 + +那么接下来为了产品化,在后续拓展为,每个用户可以支付购买自己的额度并使用,那么则需要创建出用户账户、商品、订单和支付服务。为了实现这一整条链路功能,在本节我们先来做一个账户的使用,后续在陆续添加其他功能。 + +## 二、流程设计 + +在 openai 领域中,我们设计了规则和工厂🏭的组合使用,而所有的这些频繁变化的行为都可以被抽象为规则。那么这里正好可以把账户的属性用规则的方式进行过滤处理,包括;账户状态、可用模型、账户额度。 + +
+ +
+ +- 基于我们已经做好的规则实现,这里扩展账户相关的内容会非常容易。只要按照规则接口标准,实现出和账户相关的规则即可。 +- 但本章节的复杂在于整个 DDD 分层架构中,全链路流程的设计和实现。这部分小傅哥录制了详细的视频,可以在本节的视频中学习架构和设计以及编码。 diff --git "a/docs/md/project/chatgpt/api/\347\254\2548\350\212\202\357\274\232\345\225\206\345\223\201\344\270\213\345\215\225\345\257\271\346\216\245\345\276\256\344\277\241\346\224\257\344\273\230.md" "b/docs/md/project/chatgpt/api/\347\254\2548\350\212\202\357\274\232\345\225\206\345\223\201\344\270\213\345\215\225\345\257\271\346\216\245\345\276\256\344\277\241\346\224\257\344\273\230.md" new file mode 100644 index 000000000..ce65c1b4f --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\2548\350\212\202\357\274\232\345\225\206\345\223\201\344\270\213\345\215\225\345\257\271\346\216\245\345\276\256\344\277\241\346\224\257\344\273\230.md" @@ -0,0 +1,39 @@ +--- +title: 第8节:商品下单对接微信支付 +pay: https://t.zsxq.com/12jRaQCvC +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第8节:商品下单对接微信支付 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★★★ +- **本章重点**:设计商品、订单、支付的整套服务功能领域,对接微信支付完成支付回调发货商品到个人账户。此节重点包括;业务流程、领域设计、代码实现,一定多学习几遍。 +- **课程视频**:[https://t.zsxq.com/13VcBxNLO](https://t.zsxq.com/13VcBxNLO) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +为用户账户额度充值,设计商品和订单服务。并对接微信支付,通过下单扫码支付的方式完成自动化充值服务。 + +对于 OpenAI 只是其中的一个场景,学习了商品、订单、支付模型,以后你可以用到各类场景中使用。因为这是一次真正的对接支付,走支付流程,提供订单下单支付以及各项的补偿流程,包括;超时关单、下单未生成支付、用户支付完成掉单。所以这些内容学习起来,不只是面试可以回答的游刃有余,即使在以后工作中都是可以作为参考设计和编码方式进行使用。 + +## 二、支付领域 + +整个 ChatGPT 的领域功能已经越来越丰富了,已经逐步成为产品化。读者在学习后,甚至可以做一套自己的【Xxx小商城】系统。 + +
+ +
+ +- 左侧,是整个项目所涉及的领域服务,包括;生成式服务、权限校验、微信支付、用户账户、订单交易、商品。构成整个服务功能。 +- 右侧,以用户视角下,了解各个领域的上下文关系。从微信对接、权限校验、再到生成式服务和账户域使用。以及本节要实现的订单交易域。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/api/\347\254\2549\350\212\202\357\274\232OpenAi\345\244\232\346\270\240\351\201\223\347\255\226\347\225\245\346\250\241\345\274\217.md" "b/docs/md/project/chatgpt/api/\347\254\2549\350\212\202\357\274\232OpenAi\345\244\232\346\270\240\351\201\223\347\255\226\347\225\245\346\250\241\345\274\217.md" new file mode 100644 index 000000000..14a256e29 --- /dev/null +++ "b/docs/md/project/chatgpt/api/\347\254\2549\350\212\202\357\274\232OpenAi\345\244\232\346\270\240\351\201\223\347\255\226\347\225\245\346\250\241\345\274\217.md" @@ -0,0 +1,39 @@ +--- +title: 第9节:OpenAi多渠道策略模式 +pay: https://t.zsxq.com/13sKrl3oY +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-api 第9节:OpenAi多渠道策略模式 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★★☆ +- **本章重点**:在DDD架构下的OpenAi领域模块中,通过策略模式扩展OpenAi多渠道对接使用。 +- **课程视频**:[https://t.zsxq.com/13cy0TKmq](https://t.zsxq.com/13cy0TKmq) + +**版权说明**:©本项目与星球签约合作,受[《中华人民共和国著作权法实施条例》](http://www.gov.cn/zhengce/2020-12/26/content_5573623.htm) 版权法保护,禁止任何理由和任何方式公开(public)源码、资料、视频等内容到Github、Gitee等,违反可追究进一步的法律行动。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +OpenAi 大模型不只是有 ChatGPT,还有如;ChatGLM、讯飞、腾讯、百度、京东各家提供的生成式大模型。并且主要的是在一个模型渠道出现问题的时候,可以让用户方便的切换到另外的模型进行使用。那么本章节的核心目的就是优雅的对接和切换使用除了 ChatGPT 以外的 ChatGLM 生成式大模型,代码的设计实现上会用到策略模式。 + +对接ChatGLM前,可以阅读[ChatGLM 大模型 SDK](https://bugstack.cn/md/project/chatgpt/sdk/chatglm-sdk-java.html)设计实现。 + +## 二、流程设计 + +现有的工程实现中,已经对接了 ChatGPT 大模型通信渠道,那么本节将使用策略模式扩展新的渠道。**设计模式的运用不是对代码的增加,而是对代码的边界切割,合理摆放再进行衔接调用。** + +
+ +
+ +- 如图中,左侧的一列是标准的流程,也是程序的主脉络。这部分通过模板模式来定义调用过程标准。 +- 如图中,右侧的部分,是通过设计模式封装的流程。而本节重点是对原本单条的 OpenAi 渠道对接,扩展为策略模式下的多渠道 OpenAi 对接。 diff --git a/docs/md/project/chatgpt/chatgpt.md b/docs/md/project/chatgpt/chatgpt.md new file mode 100644 index 000000000..4597f78d0 --- /dev/null +++ b/docs/md/project/chatgpt/chatgpt.md @@ -0,0 +1,92 @@ +--- +title: 课程:OpenAi 大模型应用服务体系构建 +lock: no +--- + +# OpenAi 大模型应用服务体系构建 - API-SDK、鉴权、公众号、企业微信、支付服务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
体验:[https://openai.gaga.plus](https://openai.gaga.plus) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +说来奇怪🤔,我们从0到1的事往往较少,但从1到100的`嫁衣神功`却很多也很快。就像 ChatGPT 还没有多成熟,但 ChatGPT 的各种付费模式已经非常成熟。`但说奇怪也不奇怪`,因为本身大部分一样的我,所经历过的不少事,也都是在紧赶慢赶的完成OKR。 这让我们感觉就像陷入了一个旋转飞轮中,不能思考,只能往前跑。 + +不过也不能说各类的付费 ChatGPT 就都是用提前了解到的资料和认知割羊毛,要是没有这些资料、服务、工具、软件,可能大部分外行,也没有机会了解和使用到 ChatGPT 这样的工具。当然也有像小傅哥一样的伙伴,自己花钱购买API Keys和服务器,部署了一套免费的 ChatGPT 服务 - [itedus.cn](https://itedus.cn) 让大家体验。就当为科技做点贡献💐。 + +**不过**,之所以让大家这么体验,也是想让你要知道。不是你能用上 ChatGPT 你就牛了,啥都能干了。**是你强它才强**,你要是对一个行业不了解,没有深度的积累,你问 ChatGPT 的结果,可能也只是 `HelloWord` 级别。 + +## 一、`启动`新项目 + +**那小傅哥想干啥?做一个 ChatGPT 资料社群吗?不,不做。** + +作为一个纯搞技术的号主,我更希望做符合技术人员长期发展的学习事项。如 ChatGPT、文心一言、通义千问、AIGC、Civita,这样的东西会越来越多,而作为研发更应该注重以`生成式服务`所搭建出一套体系化应用微服务。所以小傅哥的星球又要带着大家搞新项目了 **《ChatGPT 微服务应用体系构建》** - 说到又,那小傅哥的星球搞了多少项目🤔? + +除技术小册外,星球所有项目都可以学习:- `加入小傅哥的知识星球,相当于付费1个项目的价格,就可以学习所有过往的项目!` + +- 《ChatGPT 微服务应用体系构建 - API-SDK、鉴权、公众号对接、微信对接、交易支付》 +- [《ChatGPT AI 问答助手》](https://bugstack.cn/md/project/chatbot-api/chatbot-api.html) +- [《API网关 - 中间件设计和实践》](https://bugstack.cn/md/assembly/api-gateway/api-gateway.html) +- [《SpringBoot 中间件设计和开发》](https://bugstack.cn/md/assembly/middleware/2019-12-02-SpringBoot%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B9%8B%E7%BB%9F%E4%B8%80%E7%99%BD%E5%90%8D%E5%8D%95%E9%AA%8C%E8%AF%81.html) +- [《Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践》](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html) +- [《Netty+JavaFx实战:仿桌面版微信聊天》](https://bugstack.cn/md/project/im/2020-03-04-%E3%80%8ANetty+JavaFx%E5%AE%9E%E6%88%98%EF%BC%9A%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9%E3%80%8B.html) + +技术小册、手撕源码、插件开发等更多内容:[https://bugstack.cn/md/zsxq/introduce.html](https://bugstack.cn/md/zsxq/introduce.html) - 加入小傅哥的知识星球,这些内容都可以学习到。`平均一个课程也就10来块!!!—— 公众号【bugstack】回复【星球】优惠加入` + +**说到这**,肯定有小伙伴已经刺激中带着疑虑🤔:”这下终于有号主带着我卷 ChatGPT服务项目开发了,那这个项目到底要开发成啥样呢?“ 接下来,小傅哥就给大家讲讲这个项目的目标和架构。 + +## 二、`项目`的架构 + +- **目标**:此项目以围绕类似 ChatGPT 生成式服务,构建微服务应用架构体系组件。包括;用户鉴权、公众号、多方支付、企业微信等对接方式,满足不同诉求的使用。并以模块化设计,积木式构建应用,让不同的场景诉求都可以配置化对接。 +- **功能**:更直白一些就是通过这套微服务体系,可以构建出;`网页版ChatGPT对接`、`用户鉴权校验接口`、`关注公众号解锁`、`支付付费购买`、`公众号自动回复`、`企业微信聊天对接`、`知识星球对接`等。 + +那么这套系统是以`视频`和`小册`的教程为导向,教会大家开发这些各个模块的技术组件和技术服务。同时这里的组件和服务,都是微服务实现,可以被替换成其他任何一个你所需的内容。比如不是对接 ChatGPT 而是你想对接一个其他的服务也是可以的。 + +**整个系统架构如下**: + +
+ +
+ +如图;以用户请求为入口,通过 `Nginx SSL 443` 校验转发到对应的服务,并做相关的鉴权和服务控制,并完成最终的 token 授权使用。整套微服务包括系统;`chatgpt-api-sdk`、`chatgpt-auth`、`chatgpt-wx`、`chatgpt-pay`、`chatgpt-zsxq`、`chatgpt-admin`、`chatgpt-web` 服务。 + +## 三、`开发`的计划 + +**死鬼**,又到了疯狂的带着星球伙伴卷代码的时候。 + +- OpenAI 接口服务 + Nginx SSL 配置 +- OpenAI SDK +- 公众号回复应答 +- 企业微信对接,开发微信机器人 +- 支付宝交易对接 +- 服务整合等 + +每一个模块和章节都会有对应的视频和小册,每一个模块也都可以独立进行学习和使用。这样小伙伴即使想部分学习积累自己的技术或者用到实际的项目都是非常容易的。并且因为有这样的完整的资料和教程问题服务,粉丝伙伴都是可以学习会的! + +## 四、`你能`得到啥 + +因为此项目是以教程为导向,所以会以从`设计思考`、`工程搭建`、`仓库使用`、`代码提交`、`模块开发`、`服务调试`、`打包构建`、`容器部署`等步骤进行推进。在这个过程中会视频和小册的方式进行讲解。 + +那么你可以在这套项目学习中掌握到; + +1. 掌握一整套标准化,工程架构设计、项目搭建配置、服务打包上线的流程;—— 可能很多新人或者工作中的,完整搭建项目的经验都是缺失的。 +2. 学习微服务架构设计思想和相关的编码经验,以及如何落地相关的技术项目;—— 有思想有高度的项目,才能锻炼编码能力,提升编程技术。 +3. 积累 `Nginx SSL 443 多模型配置和鉴权使用`、`公众号开发`、`企业微信对接`、`支付包对接交易流程`等;—— 这都是锻炼的真实场景经验,每一项技能的积累都是非常宝贵的经验。 +4. 简历服务;对,每开发一个项目,小傅哥都会给这个项目编写简历介绍、简历优化、面试题目汇总,让学习的伙伴享受一条龙服务🐲。 + +**说直白喽,没有这套项目,你可能都不知道 Nginx 怎么配置的 SSL,镜像怎么打包和发布、公众号怎么开发、微信机器人怎么对接的、微服务怎么架构的、DDD如何设计的!所以,上车吧!全是干货!** + +## 五、`早到`享优惠 + +小傅哥致力于把星球【`码农会锁`】开发成**最具互联网应用级实战项目开发学习社群**,让加入的伙伴都能学习到`干刺啦`的硬核干货项目,学习后即可`提升编程思维`也能`锻炼编码能力`。 + +对于星球的运营,我没有任何OKR压力,所以我可以以100%的纯粹的`技术热情`和`技术追求`进行建设和维护。用我多年从事互联网行业里丰富的架构经验和编程能力,开发出每一个高质量的技术项目。—— 做一件喜欢的事,并把一件事做的长久和有价值,让用户值得信赖和认可,是我最大的追求。 + +--- + +好啦,想加入学习的伙伴,记得早些下手。 + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) \ No newline at end of file diff --git "a/docs/md/project/chatgpt/dev-ops/\347\254\2541\350\212\202\357\274\232push\345\267\245\347\250\213\345\210\260\344\273\223\345\272\223.md" "b/docs/md/project/chatgpt/dev-ops/\347\254\2541\350\212\202\357\274\232push\345\267\245\347\250\213\345\210\260\344\273\223\345\272\223.md" new file mode 100644 index 000000000..05c9a1843 --- /dev/null +++ "b/docs/md/project/chatgpt/dev-ops/\347\254\2541\350\212\202\357\274\232push\345\267\245\347\250\213\345\210\260\344\273\223\345\272\223.md" @@ -0,0 +1,31 @@ +--- +title: 第1节:push工程到仓库 +pay: https://t.zsxq.com/0djBLg22a +--- + +# 《ChatGPT 微服务应用体系构建》 - dev-ops 第1节:push工程到仓库 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★☆☆☆☆ +- **本章重点**:通过本章节内容的学习,教会读者如何把一个在本地创建的工程,PUSH到远程仓库【gitcode.net】。在 github\gitee\gitcode 各类仓库中操作是通用的。 +- **课程视频**:[https://t.zsxq.com/0dDDBIssX](https://t.zsxq.com/0dDDBIssX) + +## 一、前言 + +在前面星球中的课程讲解中发现,很多球友伙伴还没有正式进入课程前,就被环境配置、代码提交、项目构建等基础操作问题卡住了。但这些问题本身只要看过一遍就会了,所有小傅哥准备基于这样一个课程,把类似这样的基础操作带着大家完成下。好让哪怕是在校学习的新人伙伴也能顺利进入学习。 + +类似这样的基本操作会包括;云服务器购买、SSH连接、安装容器环境、Nginx配置等。逐个讲解和视频操作每个内容,方便大家随时查阅自己需要的内容。 + +## 二、代码仓库 + +像一些小型互联网公司中,往往并不会自己建设一套 GitLab 仓库,大部分时候都是使用;Github、Gitee、Gitcode 这样的公开仓库来维护代码。但无论是否自建还是使用这些市面的产品,所有的基本操作都是一样的,所以会用一套,也就可以用其它的了。 + +
+ +
+ +- 所有的仓库,再右上角都可以找到创建项目的路径。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/dev-ops/\347\254\2542\350\212\202\357\274\232Docker\347\216\257\345\242\203\345\256\211\350\243\205.md" "b/docs/md/project/chatgpt/dev-ops/\347\254\2542\350\212\202\357\274\232Docker\347\216\257\345\242\203\345\256\211\350\243\205.md" new file mode 100644 index 000000000..ed820615d --- /dev/null +++ "b/docs/md/project/chatgpt/dev-ops/\347\254\2542\350\212\202\357\274\232Docker\347\216\257\345\242\203\345\256\211\350\243\205.md" @@ -0,0 +1,42 @@ +--- +title: 第2节:Docker环境配置 +pay: https://t.zsxq.com/0dgNDOINd +--- + +# 《ChatGPT 微服务应用体系构建》 - dev-ops 第2节:Docker环境配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★☆☆☆☆ +- **本章重点**:通过本章节内容的学习,教会读者如何本地和云服务器安装Docker环境。 +- **课程视频**:[https://t.zsxq.com/0dmmGxI39](https://t.zsxq.com/0dmmGxI39) + +## 一、前言 + +整个课程中所涉及的应用部署都会放到 Docker 环境中启动,所以这里先把 Docker 的配置给球友读者分享下。其实像 Docker 的安装、配置、使用,在网上也可以检索到资料,但鉴于有些伙伴找的资料参差不齐,还容易耽误时间,所以小傅哥这里也统一给大家分享。 + +Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Mac、Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。总之它加快构建、共享和运行现代应用程序的速度。 + +- 官网:[https://www.docker.com](https://www.docker.com/) - Mac、Windows、Linux + +## 二、本地安装 + +优先建议大家本地安装一个 Docker 环境,方便测试验证。毕竟开发阶段我们暂时也不需要云服务器。 + +Docker 的安装非常简单,你只需要选择适合你的机器版本。Mac、Windows、Linux,直接一步步安装即可。 + +
+ +
+ +安装完成后,按照自己的机器配置一个适合的空间占用。 + +
+ +
+ +- 安装后,可以在 Docker 的操作界面拉取镜像、推送镜像、部署程序等,后面会在课程的使用中进行体现。 +- 安装后,还可以在黑窗口终端、IntelliJ IDEA Terminal 中使用 Docker 命令,操作 Docker,如服务部署。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/dev-ops/\347\254\2543\350\212\202\357\274\232Portainer\347\216\257\345\242\203\345\256\211\350\243\205.md" "b/docs/md/project/chatgpt/dev-ops/\347\254\2543\350\212\202\357\274\232Portainer\347\216\257\345\242\203\345\256\211\350\243\205.md" new file mode 100644 index 000000000..46b025cc8 --- /dev/null +++ "b/docs/md/project/chatgpt/dev-ops/\347\254\2543\350\212\202\357\274\232Portainer\347\216\257\345\242\203\345\256\211\350\243\205.md" @@ -0,0 +1,40 @@ +--- +title: 第3节:Portainer环境配置 +pay: https://t.zsxq.com/0dYyw6VNQ +--- + +# 《ChatGPT 微服务应用体系构建》 - dev-ops 第3节:Portainer环境配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★☆☆☆☆ +- **本章重点**:通过本章节内容的学习,教会读者如何本地和云服务器安装Portainer环境。 +- **课程视频**:[https://t.zsxq.com/0dmmGxI39](https://t.zsxq.com/0dmmGxI39) + +## 一、基础安装 + +### 1. 拉取最新的 Portainer + +```java +[root@CodeGuide portainer]# docker pull portainer/portainer +Using default tag: latest +latest: Pulling from portainer/portainer +94cfa856b2b1: Pull complete +49d59ee0881a: Pull complete +a2300fd28637: Pull complete +Digest: sha256:fb45b43738646048a0a0cc74fcee2865b69efde857e710126084ee5de9be0f3f +Status: Downloaded newer image for portainer/portainer:latest +docker.io/portainer/portainer:latest +``` + +- docker pull portainer/portainer +- 拉取 portainer + +### 2. 安装和启动 + +```java +[root@CodeGuide]# docker run -d --restart=always --name portainer -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer +``` \ No newline at end of file diff --git "a/docs/md/project/chatgpt/dev-ops/\347\254\2544\350\212\202\357\274\232Nginx\347\216\257\345\242\203\351\205\215\347\275\256.md" "b/docs/md/project/chatgpt/dev-ops/\347\254\2544\350\212\202\357\274\232Nginx\347\216\257\345\242\203\351\205\215\347\275\256.md" new file mode 100644 index 000000000..954628b54 --- /dev/null +++ "b/docs/md/project/chatgpt/dev-ops/\347\254\2544\350\212\202\357\274\232Nginx\347\216\257\345\242\203\351\205\215\347\275\256.md" @@ -0,0 +1,45 @@ +--- +title: 第4节:Nginx环境配置 +pay: https://t.zsxq.com/0dcdSvckQ +--- + +# 《ChatGPT 微服务应用体系构建》 - dev-ops 第4节:Nginx环境配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★☆☆☆☆ +- **本章重点**:通过本章节内容的学习,教会读者配置Nginx环境 +- **课程视频**:[https://t.zsxq.com/0dEZlWkOC](https://t.zsxq.com/0dEZlWkOC) + +## 一、常用命令 + +- 停止:`docker stop Nginx` +- 重启:`docker restart Nginx` +- 删除服务:`docker rm Nginx` +- 删除镜像:`docker rmi Nginx` +- 进入服务:`docker exec -it Nginx /bin/bash` + +## 二、基础安装 + +
+ +
+ +```java +docker run \ +--restart always \ +--name Nginx \ +-d \ +-p 80:80 \ +nginx +``` + +
+ +
+ +- restart 重启策略,always 是一直保持重启。如果不设置,可以把这条删掉。`never\always` +- 第1个 `80` - 容器端口、第2个 `80` - 服务器端口,这样外部通过80端口即可访问。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/dev-ops/\347\254\2545\350\212\202\357\274\232\346\234\215\345\212\241\351\225\234\345\203\217\346\236\204\345\273\272\345\222\214\345\256\271\345\231\250\351\203\250\347\275\262.md" "b/docs/md/project/chatgpt/dev-ops/\347\254\2545\350\212\202\357\274\232\346\234\215\345\212\241\351\225\234\345\203\217\346\236\204\345\273\272\345\222\214\345\256\271\345\231\250\351\203\250\347\275\262.md" new file mode 100644 index 000000000..bc2f001a3 --- /dev/null +++ "b/docs/md/project/chatgpt/dev-ops/\347\254\2545\350\212\202\357\274\232\346\234\215\345\212\241\351\225\234\345\203\217\346\236\204\345\273\272\345\222\214\345\256\271\345\231\250\351\203\250\347\275\262.md" @@ -0,0 +1,29 @@ +--- +title: 第5节:服务镜像构建和容器部署 +pay: https://t.zsxq.com/0dCxC2pvp +--- + +# 《ChatGPT 微服务应用体系构建》 - dev-ops 第5节:服务镜像构建和容器部署 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:通过在工程中配置 Dockerfile 和相关文件,进行镜像的打包和发布,以及 IDEA 中配置 Docker 连接进行镜像和服务的启动关闭管理。 +- **课程视频**:[https://t.zsxq.com/0dCj8XonL](https://t.zsxq.com/0dCj8XonL) + +## 一、本章诉求 + +基于目前 ChatGPT-API 部分开发到第2节时,小傅哥带着大家完成一下应用服务的镜像打包和部署操作。因为这个时候代码的开发量还不多,也不需要在部署的时候考虑如:MySQL、Redis 以及各个配置文件的操作。但又可以支撑我们验证服务。所以先来完成下这块的内容。 + +## 二、流程设计 + +整个流程为;在工程中添加 Dockerfile 配置文件,对工程进行镜像打包。并把工程推送到本地和远程 Docker 仓库进行部署验证。 + +
+ +
+ +本地的 Docker 直接在 IDEA 配置即可启动,远程的服务仓库一种是配置开启2375端口,另外一种把镜像文件推送到 [https://hub.docker.com](https://hub.docker.com) 再通过拉取的方式使用。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/dev-ops/\347\254\2546\350\212\202\357\274\232\345\211\215\345\220\216\347\253\257\346\236\204\345\273\272\351\225\234\345\203\217\351\203\250\347\275\262.md" "b/docs/md/project/chatgpt/dev-ops/\347\254\2546\350\212\202\357\274\232\345\211\215\345\220\216\347\253\257\346\236\204\345\273\272\351\225\234\345\203\217\351\203\250\347\275\262.md" new file mode 100644 index 000000000..a67fb0541 --- /dev/null +++ "b/docs/md/project/chatgpt/dev-ops/\347\254\2546\350\212\202\357\274\232\345\211\215\345\220\216\347\253\257\346\236\204\345\273\272\351\225\234\345\203\217\351\203\250\347\275\262.md" @@ -0,0 +1,31 @@ +--- +title: 第6节:前后端构建镜像部署 +pay: https://t.zsxq.com/11biMQ6NH +--- + +# 《ChatGPT 微服务应用体系构建》 - dev-ops 第6节:前后端构建镜像部署 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:打包镜像,构建前后端应用,部署服务。同时希望你能把镜像推送到 DockerHub 完成云服务的部署。 +- **课程视频**:[https://t.zsxq.com/11ZKvBknf](https://t.zsxq.com/11ZKvBknf) + +## 一、本章诉求 + +到本章节,chatgpt-sdk-java 第一版开发完了、chatgpt-web 页面第一版开发完了、chatgpt-api 工程重构了DDD,也开完了。接下来干啥,当然要部署上线了。 + +一个工程项目的学习,除了学习开发还要懂得怎么部署到线上,这样你才能观察到调用量、优化点、日志的检查等各方面内容。所以本章节我们就来打包部署。 + +## 二、部署流程 + +整个流程为;将前后端分别配置 dockerfile 进行镜像打包,之后编写 compose.yml 进行部署。部署后配置公众号验签地址。—— 前面我们已经讲过公众号的配置,大家也可以跳过去参考。 + +
+ +
+ +- 镜像打包的时候,如果有 Mac M1 ARM 架构的,需要注意兼容问题。 diff --git "a/docs/md/project/chatgpt/dev-ops/\347\254\2547\350\212\202\357\274\232\347\275\221\347\253\231\346\267\273\345\212\240\347\231\276\345\272\246\347\273\237\350\256\241.md" "b/docs/md/project/chatgpt/dev-ops/\347\254\2547\350\212\202\357\274\232\347\275\221\347\253\231\346\267\273\345\212\240\347\231\276\345\272\246\347\273\237\350\256\241.md" new file mode 100644 index 000000000..845cc8770 --- /dev/null +++ "b/docs/md/project/chatgpt/dev-ops/\347\254\2547\350\212\202\357\274\232\347\275\221\347\253\231\346\267\273\345\212\240\347\231\276\345\272\246\347\273\237\350\256\241.md" @@ -0,0 +1,29 @@ +--- +title: 第7节:网站添加百度统计 +pay: https://t.zsxq.com/128pTgdZU +--- + +# 《ChatGPT 微服务应用体系构建》 - dev-ops 第7节:网站添加百度统计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★☆☆☆☆ +- **本章重点**:给 ChatGPT 站点添加百度统计信息,后续你也可以给自己的其他网站添加这样的统计操作。 +- **课程视频**:[https://t.zsxq.com/12QynVUdN](https://t.zsxq.com/12QynVUdN) + +## 一、本章诉求 + +网站做完上线了,那么怎么看网站有多少的 PV、UV、IP、跳出率、访问时长,以及访问网站的来源和入口呢。因为只有我们能看到这些数据,才能更好的管理和运营自己的站点,并且你后续所有的扩容评估、峰值预估也都会依赖于一个网站的 PV、UV 等数据。 + +所以本章节小傅哥将教会大家怎么来监控这些数据。 + +## 二、方案选择 + +用于统计网站的数据工具有很多,包括;百度统计、友盟统计,还有谷歌的工具,都是可以的。这里小傅哥选择使用百度统计为大家讲解。 + +
+ +
\ No newline at end of file diff --git "a/docs/md/project/chatgpt/dev-ops/\347\254\2548\350\212\202\357\274\232\345\272\224\347\224\250\347\233\221\346\216\247.md" "b/docs/md/project/chatgpt/dev-ops/\347\254\2548\350\212\202\357\274\232\345\272\224\347\224\250\347\233\221\346\216\247.md" new file mode 100644 index 000000000..2734dc704 --- /dev/null +++ "b/docs/md/project/chatgpt/dev-ops/\347\254\2548\350\212\202\357\274\232\345\272\224\347\224\250\347\233\221\346\216\247.md" @@ -0,0 +1,40 @@ +--- +title: 第8节:应用监控 +pay: https://t.zsxq.com/13bOKaiTd +--- + +# 《ChatGPT 微服务应用体系构建》 - dev-ops 第8节:应用监控(Prometheus + Grafana) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:给应用系统配置 SpringBoot Actuator + Prometheus + Grafana +- **课程视频**:[https://t.zsxq.com/13tKAWkcQ](https://t.zsxq.com/13tKAWkcQ) + +## 一、本章诉求 + +OpenAi(ChatGPT、ChatGLM) 微服务应用开发完成了,那么接下来小傅哥带着大家做应用系统的监控配置。有了监控的应用才不是“裸奔”的,你可以随时看到;CPU负载、磁盘空间占用量、接口请求量、接口响应时间等等各项参数数据。 + +并且,现在的面试中也越来越多的问题逐步向实际靠拢,更喜欢问一些实际问题。这些问题不同于八股文,因为做过就是做过,没做过像背都背不出来。 + +## 二、系统监控 + +可用于应用监控的系统有很多,有的需要埋点(切面)、有的需要配置Agent(字节码增强)。如小傅哥前面已经分享过的 [《skywalking 全链路监控》](https://bugstack.cn/md/road-map/skywalking.html) 就是一款非入侵的全链路监控系统,它的配置非常简单。你也可以通过此组件完成本节内容。本节小傅哥教大家另外一个监控系统的使用 —— Grafana + +**Grafana 监控面板** + +
+ +
+ +这套监控主要用到了 SpringBoot Actuator + Prometheus + Grafana 三个模块组合的起来使用的监控。非常轻量好扩展使用。 + +
+ +
+ +- Actuator - 数据上报、Prometheus - 数据采集、Grafana - 数据展示 +- 本章节的内容主要为代码中的配置和监控的配置。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/dev-ops/\347\254\2549\350\212\202\357\274\232\351\203\250\347\275\262\344\270\212\347\272\277.md" "b/docs/md/project/chatgpt/dev-ops/\347\254\2549\350\212\202\357\274\232\351\203\250\347\275\262\344\270\212\347\272\277.md" new file mode 100644 index 000000000..c40ae5519 --- /dev/null +++ "b/docs/md/project/chatgpt/dev-ops/\347\254\2549\350\212\202\357\274\232\351\203\250\347\275\262\344\270\212\347\272\277.md" @@ -0,0 +1,15 @@ +--- +title: 第9节:部署上线 +lock: no +--- + +# 《ChatGPT 微服务应用体系构建》 - dev-ops 第9节:整体部署上线(手把手🤝) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +- 项目地址:[https://t.zsxq.com/143o5FKvc](https://t.zsxq.com/143o5FKvc) \ No newline at end of file diff --git a/docs/md/project/chatgpt/extra/ChatGPT-v1.0.md b/docs/md/project/chatgpt/extra/ChatGPT-v1.0.md new file mode 100644 index 000000000..59fab7fc6 --- /dev/null +++ b/docs/md/project/chatgpt/extra/ChatGPT-v1.0.md @@ -0,0 +1,103 @@ +--- +title: ChatGPT + 仿微信界面,效果好还TM贼漂亮! +lock: no +--- + +# ChatGPT + 仿微信界面,效果好还TM贼漂亮! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +从这套 ChatGPT 工程设计到开发,到现在第一个版本的落地,已经有2个月多了。那为什么这么长时间呢?🤔 因为小傅哥所编写的不只是完成功能,而是从 `Dev-Ops`、`API 工程 DDD 架构设计`、`ChatGPT-SDK 实现`、`ChatGPT-WEB React` 开发一整套的东西,分步骤的逐步实现给大家,教会每个人有需要开发此类内容的,都可以依照工程学会。 + +从前端到后端、从开发到上线、从实施到运维,在这套项目中,你一个人就是全栈工程师!2个月多的时间里,20篇内容编写和20个视频录制,让有需要的伙伴,都能从头到尾的学习会。 + +🌶那,你看。只要你做了一套这样的项目,既可以学习到完整的技术运用,还可以应对面试编写简历。这可是一个非常不错的项目!而且是有效果的全流程上线运行的项目! + +## 一、内容结构 + +全部课程,分为以下4部分; + +
+ +
+ +- Dev-Ops:讲解关于基础环境的配置和使用,以及如何把应用发布部署到线上环境。这包括了镜像的打包和全流程的使用。非常适合没有学习过或者不太梳理此类内容的伙伴学习和掌握。 +- ChatGPT-API:API 是一套统一封装的服务,用于处理外部访问的接口调用。这包括;公众号接口、流式异步响应消息应答接口、Nginx Shiro Auth 认证接口。并且这套工程的开发中,会带着大家做工程重构,代码设计模式重构。让参与学习的伙伴,可以了解什么是好的设计,怎么从泥潭中到好的设计中来。 +- ChatGPT-SDK:此 SDK 的开发,运用了对话模型构建工厂,统一对外提供服务,并在技术实现上运用 okhttp3 封装服务接口,让SDK工程的可维护性为此高。学习这套东西 SDK 开发,以后你在封装其他的接口调用 SDK 就会有架构师的水平! +- ChatGPT-WEB:这是一套 React 开发的 WEB 界面,也是首次带着小伙伴完整的学习一遍前端技术的使用。讲真,React 代码真好看,它的学习完全可以是一种面向对象的学习,只要有 Java 语言基础,基本做3节课,就能跟上 React 的代码开发了! + +**一个工程师的学习路途,到最后总要会写点前端!** 否则你再好的后端代码,也没法用产品化的方式展示给用户。而这将影响你,未来是否能成为自主工作者。 + +## 二、编码展示 + +接下来,小傅哥给大家举例这套课程中的内容,让小伙伴看看干净、漂亮、整洁,有质量的架构和设计到底应该是什么样!也能为你以后的学习有个参考说明! + +### 1. Dev-Ops + +
+ +
+ +
+ +
+ +- 细化的教会读者怎么配置这些镜像打包的文件,一步步带着你搞。 + +### 2. ChatGPT-API + +
+ +
+ +- 大部分研发总以为从一个架构换到另外一个架构,就能改变代码质量。但其实不会的!因为代码的质量主要来自于设计模式,架构的分层只是让结构更合理。 +- 否则很多伙伴开发代码,都是;**一个接口、一个实现,一个实现、代码一片。一片一片、又一片,代码行数、两三千。** 那么这样写代码,不乱才怪!所以小傅哥在此套项目中会引入很多的设计模式帮大家提升设计思维和编码质量。 + +### 3. ChatGPT-SDK + +
+ +
+ +- 代码能不能可维护,全靠工程的结构和设计模式的运用。而这些能力的成长,都需要新人学习其他优秀的高质量代码,才能提高自己的思维。否则全是 CRUD 干几年以后,也还是 CRUD 的思维。 + +### 4. ChatGPT-WEB + +当小傅哥带着大家把 React 的学习细化到每一个步骤,每一个需求,每一个编码,每一个实现的时候,很多小伙伴也都能跟着学习会 React 的开发了。说句最直白的,学习完这套代码,那么你在看一些开源项目的前端代码,也都可以做一些扩展开发来满足自己的需求了。 + +#### 4.1 需求 + +
+ +
+ +#### 4.2 编码 + +
+ +
+ +#### 4.3. 实现 + +
+ +
+ +## 三、加入学习 + +这个 ChatGPT 项目,是小傅哥星球中的一个项目,很多小伙伴加入后都感到震惊😱,这星球的干货太多了也!从技术路线的碎片化学习、到6个应用实战项目的锻炼、再到各个问题场景方案的设计、再到简历编写优化、再到问题1v1解答!这个技术生态社区太香了! + +而目前的价格也仅仅是几瓶扎啤钱!你的加入,就当我们为前程干一杯🍻! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +
+ +
+ + diff --git a/docs/md/project/chatgpt/extra/ChatGPT-v1.1.md b/docs/md/project/chatgpt/extra/ChatGPT-v1.1.md new file mode 100644 index 000000000..6ecf1c243 --- /dev/null +++ b/docs/md/project/chatgpt/extra/ChatGPT-v1.1.md @@ -0,0 +1,91 @@ +--- +title: 把一个ChatGPT项目上线,要折腾多少细节! +lock: no +--- + +# 把一个ChatGPT项目上线,要折腾多少细节! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +于5.1启动,耗时3个多月开发的 OpenAI 项目,**终于到了上线对外的时候**。可能很多伙伴会想到,Github 不是有不少的开源项目吗,为啥还自己做? + +为啥🤔。嗯,好问题!因为我是一个程序员👨🏻‍💻,我需要对各项技术的实现方案有深度的理解,我不能在工作或者面试中被提问的时候,说你去看开源项目吧。 + +此外大部分 Github 的开源项目主要以前端为主,并没有一个前后端结合的开发。而实际上,当我们真的需要在业务场景来使用时,就不只是前端页面,还需要在后端做各项的控制和管理。而这样的架构设计,就势必会涉及到;`前后端分离`、`前端工程架构`、`后端工程架构(DDD)`、`登录授权鉴权`、`大模型SDK实现(okhttp)`、`流式异步响应开发对接`、`库表字段索引精细设计`、`API多渠道路由`、`内网穿透服务测试`、`前后端镜像打包`、`docker-compose 服务部署和回滚策略`、`公众号验签配置`,等等技术知识的运用。 + +**所以**,你跟开源项目学的是简单的应用,跟小傅哥学习;`学的是架构设计的魅力`、`学的是技术实现的巧妙`。学的是,让你在晋升、述职、面试,时交流的底气。**那么现在小傅哥把这样一个上线项目,拆分成一个个章节学习,让你也能具备这样的能力。** + +## 一、先看效果 + +**体验地址:[openai.itedus.cn](https://openai.itedus.cn/)** + +
+ +
+ +--- + +
+ +
+
关注小傅哥的公众号【bugstack虫洞栈】回复【星球】也可以领取专属优惠券 | 加入即可学习星球的全套实战项目(Lottery、API网关、ChatGPT、IM、组件开发、插件开发等)
+
+
+ +## 二、再说流程 + +当一个项目引入后端能力以后,就可以扩展非常多的能力。而所有的控制都是在做用户的行为处理,而这些行为就是业务需求。当不同的码农面对相同的业务,写出的代码可就不一样了。所以小傅哥也是希望让你能学习到**怎么架构和编码出高质量的代码**。 + +📢 我告诉你,当前的需求就是;公众号扫码获取验证码登录,登录后访问 OpenAI 服务,访问后需要根据是用户绑定的APIKey还是系统APIKey分别调用,如果是系统APIKey则需要限制调用次数,而用户自己绑定的则不需要。此外调用的 OpenAI 模型和渠道不同,需要进行判断和处理。那么这样的代码你想怎么写?🤔 会不会写出 if···else 的面条代码? + +如果你不想写出面条代码,那么就看看小傅哥给你设计的流程图结构。如下; + +
+ +
+ +- 首先,在这套流程中,小傅哥将核心业务和支撑核心业务的分支流程进行拆解。一切的分支都是为了支撑主干流程运行,而分支的存在和去除,都应以模块插件的方式进行使用。这样会更好的维护和使用。 +- 之后,我想说。写代码就像擦屁屁的纸,80%的面积都是保护手的。所以这除了那20%的核心点以外,要让那支撑系统运行的 80% 分支逻辑,采用设计模式进行进行分治和抽象的设计实现。 +- 所以,这里有了路由模块和规则引擎的使用。因为他们可以更好的被添加和移除。而每次变动的时候,也都是固定范围的变动,不会让整体流程都陷入测试风险中。 + +**所以**,你还觉得写代码就只是if···else吗,这哪是写代码,这TM是老子在用代码构建一个世界! + +## 三、之后上线 + +一个系统的上线对外,会有太多太多的细节要考虑。而这样的内容,只有实践了才能真的吸收这些知识。尤其是出一些事故时,都是留下一个个技术成长的故事。 + +
+ +
+ +那么我们现在以 OpenAI 个人上线对外一个项目的背景进行思考 🤔,看看会有哪些点需要处理; +1. 技术栈的使用,不宜过多。要尽量减少运维成本。比如你可以使用 Guava 替代 Redis 使用。 +2. 数据库可以考虑单独购买,避免数据丢失。但同时也需要考虑,如果数据丢失,用户可以根据自己的加密卡Key进行重新绑卡。 +3. 因为本身服务器的成本就比较高,所以尽量控制被白piao。比如任何一个Key都可以在你部署的服务上使用,其实意义不大。可以通过只授权加密 Key 的方式进行使用。 +4. 此外要考虑,多种的调用渠道,如果某个挂了。要能替换对应渠道的访问地址,所以还需要把渠道对应的地址做成可配置的,之后用户写入库里,写入的只是渠道码这样就可以方便替换。 +5. 打包部署上线,一定要先在本地测试验证,验证完全没问题在上线。否则你就单台服务器没有负载能力的情况下,基本就要挂了。 +6. 如果你在本地测试仍不放心,可以修改一个端口和一个 docker-compose-pre-v1.0.yml 进行部署测试。可千万别修改原来正式对外的 compose 直接部署,这会出问题的。 +7. 要有一个可以回滚的处理,可以配置 docker-compose-prod-v1.0.yml 版本的添加。如果1.1版本出问题,可以快速使用 1.0 版本重新部署。 +7. 数据库表的使用,要注意字段的设计,索引的设计,否则就你那一卡拉米资源的数据库配置,真的可能因为你没有索引被打挂。此外可得把用户名和密码设计的强悍一些,别被比特币勒索。 +7. 定期导出 MySQL 数据库表数据,或者有自动备份功能的数据库服务器也是可以的。 +8. 因为你的代码,确实是你的代码。这可不是公司的,也不是有别人参与的。所以为了能在迭代或者处理问题的时候,快速解决。一定是要非常好的架构,非常清晰的编码。 + +## 四、系统介绍 + +加入小傅哥的星球【码农会锁】,就可以完整的学习此套项目。另外星球还有,Lottery、API网关、IM、组件开发、插件开发等项目一起学习。 + +
+ +
+ +小傅哥带着大家写的项目,从来不是凑数项目,也从不写一堆的 CRUD 代码。而是按照互联网企业级中所开发项目的模式进行架构、设计和实现。所以你跟着小傅哥学习,学的是编程的思维和编码的能力。如下是系统的架构分层; + +
+ +
+ +- 以上内容,分别包括;API、WEB、SDK,以及 Dev-Ops 部署相关的内容。 +- 所以这套项目的学习,学的是前端、后端、架构、部署、运维等一整套东西,非常具有含金量。 \ No newline at end of file diff --git a/docs/md/project/chatgpt/extra/ChatGPT-v1.2.md b/docs/md/project/chatgpt/extra/ChatGPT-v1.2.md new file mode 100644 index 000000000..c0617cd34 --- /dev/null +++ b/docs/md/project/chatgpt/extra/ChatGPT-v1.2.md @@ -0,0 +1,179 @@ +--- +title: 又完结一个新项目,小而美、小而精! +lock: no +--- + +# 又完结一个新项目,小而美、小而精! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +经历了4个多月`前后端 + Dev-Ops`,全栈式编程。我在[星球:码农会锁](https://t.zsxq.com/09hMHNMEh)的第7个,**手把手&渐进式,逐个章节录制视频 + 编写小册**开发的 —— OpenAI 项目,第1阶段完结啦💐!而且已经上线运行3周! + +
+ +
+ +讲道理,这个项目做起来,花费我好多精力! + +因为众所周知,与开发一个玩具项目相比,要做一个能要上线运行,并稳定对外提供服务的项目,其实并不容易。它不像开发个玩具项目,可以肆无忌惮的引入各种技术栈,炫技于一些新特性的使用。而是当你做任何一块功能的实现时,都要考虑你TM的要给我稳定运行,有错误要抛出准确的异常便于快速排查,给用户的提示要温馨且温暖。同时还要考虑,各个功能的实现要尽可能降低运维成本;如果不引入 MQ,是否可以使用 Redis 的发布订阅。如果不能使用 Redis 又是否可以用 Guava 替代。如果不想自己单独搞登录,又怎么结合微信公众号做鉴权登录。这一些列的真实场景问题,都随着这个项目手把手的渐进式开发,为小伙伴一步步解开技术解决场景问题的面纱。💥 + +**传道受业,虽说很累。** 但每每看到大家的学习到东西,我也感觉很爽; + +
+ +
+ +那么,接下来小傅哥就细致的介绍下,本次完结的新项目,可以让大家学习到哪些知识,掌握哪些技术。 + +>文末有加入学习方式,还有送福利 4.0 50万 Tokens 活动!🉐 + +## 一、能学到啥 + +该项目是当下最火的 OpenAI 场景应用项目,也是各个互联网大厂都开始深入折腾,并在自身业务中逐步应用的技术。你也会渐渐的看到,在招聘要求中有一条是会 ChatGPT 开发。因为本项目是`前后端+Dev-Ops`的全栈式编程,所以这一些列内容你都可以学习到,包括; + +- 【前端】熟练使用 Next.js 构建的 React、Typescript 语言,构建的前端工程。 +- 【前端】熟练使用 React Route 路由子页面的开发技术,以及相应的信息传递。 +- 【前端】熟练掌握,跨域接口的对接使用,以及本地 json 数据加载。 +- 【前端】熟练使用本地浏览器内存,存储 Token、配置、对话等信息。 +- 【前端】熟练掌握前端页面数据的存放、使用以及和后端接口的交互方式。 +- 【后端】熟练构建 DDD 工程架构,分层模块,职责体系。并掌握 DDD 架构的开发模式以及微服务设计思想。 +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对其使用源码所提供的接口、类、SPI标准开发各类组件,有一定的设计思路和落地能力。—— 因为这里包含了 SDK 的设计、开发和使用。 +- 【后端】熟练使用多种设计模式、设计原则,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。—— 同时包括非常重要的会话模型SDK的架构设计。 +- 【后端】熟练对接微信公众号 SDK,属性验签流程和对话流程。以及完成 JWT Token 的生成和校验。 +- 【后端】熟练掌握 Nginx Auth 验证模块的开发和使用,以用于对接口的校验和拦截。 +- 【后端】熟练使用流式异步响应式框架开发应答接口,完成前端动态展示应答数据。 +- 【后端】熟练使用 okhttp3、retrofit2 框架,对接 ChatGPT 完成通用 SDK 的开发。有了这项技能,以后你可以方便的对接任何一个 HTTP 请求服务。 +- 【后端】熟练掌握异常、枚举、错误码的定义和使用,并学习如何合理打印服务日志,便于问题排查。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练申请和使用 SSL 配置 Nginx 域名 HTTPS 服务。 + +此外,小傅哥对于每个章节还讲解了章节的诉求、流程的设计,之后再到方案实现和功能验证。并在每个章节留有作业让大家练习。当然这还没有完,你知道小傅哥这个架构师画图还是非常牛逼的,所以你还能看到各种画图的技巧,耳濡目染的把这些东西学习成自己的本事!~ + +## 二、项目介绍 + +本次项目是一个包括`前后端 + Dev-Ops`,全栈式编程,的硬核项目!基于 React + SpringBoot + Nginx + Docker 云服务部署的 OpenAI 应用项目。并且是能上线对外提供服务使用的项目!`不同于一些开源项目,本项目具备完整的前后端开发和实施部署方案。` + +如果你使用过任何一款 OpenAI 产品,那么就会对这样一个业务场景非常熟悉,但对于产品之下的技术实现可能并不清楚。它是怎么做的异步应答,它是怎么对接的服务接口,它是怎么做的关联上下文数据处理。而这些都是小傅哥要在这次项目里给大家讲解的内容。 + +### 1. 应用部署 + +
+ +
+ +### 2. 项目演示 + +
+ +
+ +### 3. 数据监控(百度统计) + +
+ +
+ +### 4. 热力展示(百度统计) + +
+ +
+ +### 5. 项目流程(核心链路) + +
+ +
+ +### 6. 细节设计(流程举例) + +
+ +
+ +>对项目感兴趣的伙伴,也可以先免费看看其中的课程视频 https://b23.tv/OjYftBl + +## 三、项目大纲 + +**不同于网上项目,这个项目是一步步,一个个章节的带着大家从0到1的方式,进行分析、设计和开发。是一个纯手把手教大家学习实战技术的项目!** 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。 + +- 介绍 + - [课程:ChatGPT 微服务应用体系构建](https://bugstack.cn/md/project/chatgpt/chatgpt.html) + - [引言:开篇介绍](https://bugstack.cn/md/project/chatgpt/引言.html) + +- Dev-Ops + - [第1节:push工程到仓库](https://bugstack.cn/md/project/chatgpt/dev-ops/第1节:push工程到仓库.html) + - [第2节:Docker环境配置](https://bugstack.cn/md/project/chatgpt/dev-ops/第2节:Docker环境安装.html) + - [第3节:Portainer环境配置](https://bugstack.cn/md/project/chatgpt/dev-ops/第3节:Portainer环境安装.html) + - [第4节:Nginx环境配置](https://bugstack.cn/md/project/chatgpt/dev-ops/第4节:Nginx环境配置.html) + - [第5节:服务镜像构建和容器部署](https://bugstack.cn/md/project/chatgpt/dev-ops/第5节:服务镜像构建和容器部署.html) + - [第6节:前后端构建镜像部署](https://bugstack.cn/md/project/chatgpt/dev-ops/第6节:前后端构建镜像部署.html) + - [第7节:网站添加百度统计](https://bugstack.cn/md/project/chatgpt/dev-ops/%E7%AC%AC7%E8%8A%82%EF%BC%9A%E7%BD%91%E7%AB%99%E6%B7%BB%E5%8A%A0%E7%99%BE%E5%BA%A6%E7%BB%9F%E8%AE%A1.html) + - [第8节:应用监控](https://bugstack.cn/md/project/chatgpt/dev-ops/%E7%AC%AC8%E8%8A%82%EF%BC%9A%E5%BA%94%E7%94%A8%E7%9B%91%E6%8E%A7.html) + - [第9节:部署上线](https://bugstack.cn/md/project/chatgpt/dev-ops/%E7%AC%AC9%E8%8A%82%EF%BC%9A%E9%83%A8%E7%BD%B2%E4%B8%8A%E7%BA%BF.html) + +- ChatGPT-API + - [第1节:API工程搭建和简单访问认证](https://bugstack.cn/md/project/chatgpt/api/第1节:API工程搭建和简单访问认证.html) + - [第2节:Shiro登录授权发放访问token](https://bugstack.cn/md/project/chatgpt/api/第2节:Shiro登录授权发放访问token.html) + - [第3节:微信公众号验签和初步对接OpenAI](https://bugstack.cn/md/project/chatgpt/api/第3节:微信公众号验签和初步对接OpenAI.html) + - [第4节:工程重构和流式异步响应接口实现](https://bugstack.cn/md/project/chatgpt/api/第4节:工程重构和流式异步响应接口实现.html) + - [第5节:公众号发送验证码鉴权登录](https://bugstack.cn/md/project/chatgpt/api/第5节:公众号发送验证码鉴权登录.html) + - [第6节:白名单和敏感词规则过滤](https://bugstack.cn/md/project/chatgpt/api/%E7%AC%AC6%E8%8A%82%EF%BC%9A%E7%99%BD%E5%90%8D%E5%8D%95%E5%92%8C%E6%95%8F%E6%84%9F%E8%AF%8D%E8%A7%84%E5%88%99%E8%BF%87%E6%BB%A4.html) + - [第7节:用户额度账户的校验领域实现](https://bugstack.cn/md/project/chatgpt/api/%E7%AC%AC7%E8%8A%82%EF%BC%9A%E7%94%A8%E6%88%B7%E9%A2%9D%E5%BA%A6%E8%B4%A6%E6%88%B7%E9%A2%86%E5%9F%9F%E5%AE%9E%E7%8E%B0.html) + - [第8节:商品下单对接微信支付](https://bugstack.cn/md/project/chatgpt/api/%E7%AC%AC8%E8%8A%82%EF%BC%9A%E5%95%86%E5%93%81%E4%B8%8B%E5%8D%95%E5%AF%B9%E6%8E%A5%E5%BE%AE%E4%BF%A1%E6%94%AF%E4%BB%98.html) + - [第9节:OpenAi多渠道策略模式](https://bugstack.cn/md/project/chatgpt/api/%E7%AC%AC9%E8%8A%82%EF%BC%9AOpenAi%E5%A4%9A%E6%B8%A0%E9%81%93%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F.html) + - [第10节:应用分布式设计](https://bugstack.cn/md/project/chatgpt/api/%E7%AC%AC10%E8%8A%82%EF%BC%9A%E5%BA%94%E7%94%A8%E5%88%86%E5%B8%83%E5%BC%8F%E8%AE%BE%E8%AE%A1.html) + - [第11节:dall-e 文生图](https://bugstack.cn/md/project/chatgpt/api/%E7%AC%AC11%E8%8A%82%EF%BC%9Adall-e%E6%96%87%E7%94%9F%E5%9B%BE.html) + +- ChatGPT-SDK + - [第1节:ChatGPT-SDK组件工程简单功能实现](https://bugstack.cn/md/project/chatgpt/sdk/第1节:ChatGPT-SDK组件工程简单功能实现.html) + - [第2节:流式应答会话设计实现](https://bugstack.cn/md/project/chatgpt/sdk/第2节:流式应答会话设计实现.html) + - [第3节:完善实现各类常用接口](https://bugstack.cn/md/project/chatgpt/sdk/第3节:完善实现各类常用接口.html) +- ChatGLM-SDK + - [第1节:ChatGLM SDK - 智谱Ai v1](https://bugstack.cn/md/project/chatgpt/sdk/chatglm-sdk-java.html) + - [第2节:ChatGLM SDK - 智谱Ai v2](https://bugstack.cn/md/project/chatgpt/sdk/chatglm-sdk-java-v2.html) + +- ChatGPT-WEB + - [第1节:Web页面工程初始化](https://bugstack.cn/md/project/chatgpt/web/第1节:Web页面工程初始化.html) + - [第2节:工具栏面板](https://bugstack.cn/md/project/chatgpt/web/第2节:工具栏面板.html) + - [第3节:按钮定义与事件实现](https://bugstack.cn/md/project/chatgpt/web/第3节:按钮定义与事件实现.html) + - [第4节:对话框列表](https://bugstack.cn/md/project/chatgpt/web/第4节:对话框列表.html) + - [第5节:对话框消息](https://bugstack.cn/md/project/chatgpt/web/第5节:对话框消息.html) + - [第6节:完善对话处理](https://bugstack.cn/md/project/chatgpt/web/第6节:完善对话处理.html) + - [第7节:对话角色设定](https://bugstack.cn/md/project/chatgpt/web/第7节:对话角色设定.html) + - [第8节:流式接口对接](https://bugstack.cn/md/project/chatgpt/web/第8节:流式接口对接.html) + - [第9节:公众号扫码登录](https://bugstack.cn/md/project/chatgpt/web/第9节:公众号扫码登录.html) + - [第10节:商品支付页](https://bugstack.cn/md/project/chatgpt/web/%E7%AC%AC10%E8%8A%82%EF%BC%9A%E5%95%86%E5%93%81%E6%94%AF%E4%BB%98%E9%A1%B5.html) + +- 番外 - 课程阶段产物 + - [ChatGPT + 仿微信界面,效果好还TM贼漂亮!](https://bugstack.cn/md/project/chatgpt/extra/ChatGPT-v1.0.html) + - [把一个ChatGPT项目上线,要折腾多少细节!](https://bugstack.cn/md/project/chatgpt/extra/ChatGPT-v1.1.html) + +--- + +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! + +## 四、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +>这样一套项目,放在一些平台售卖,至少都是几百块。但小傅哥的星球,只需要100多,就可以获得几千元的学习项目! + +- 福利1:所有今天加入的伙伴,前50名都送 4.0 的 `50万Tokens`,打开链接 [openai.itedus.cn](https://openai.itedus.cn/) - 在左下角设置里绑定即可使用。当天加入后,在星球置顶消息添加小傅哥微信,备注你的星球编号。 +- 福利2:对此文章进行留言的伙伴,点赞最高前5名用户,每人送 4.0 的 `50万Tokens` 统计时间:9月6日 7:55 ~ 9月8日 7:55 + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +已经有很多伙伴开始学起来了,还有大家交的作业笔记。有了的项目驱动学习,清晰的目标感,大家冲起来也有了更明确的方向!干干干!!! + +
+ +
diff --git a/docs/md/project/chatgpt/extra/ChatGPT-v1.3.md b/docs/md/project/chatgpt/extra/ChatGPT-v1.3.md new file mode 100644 index 000000000..80ede29b5 --- /dev/null +++ b/docs/md/project/chatgpt/extra/ChatGPT-v1.3.md @@ -0,0 +1,144 @@ +--- +title: 给兄弟们,搞了个“小电商”项目,对接微信扫码支付! +lock: no +--- + +# 给兄弟们,搞了个“小电商”项目,对接微信扫码支付! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +经过10.1国庆假期💐,小傅哥在 [星球:码农会锁](#) 发布的 OpenAI 课程项目中,新嵌入了一个小型电商系统。支持用户商品下单,微信扫码支付,异步额度充值的全流程实现。**而且这套支付已经上线运行,卷的就是真实!** + +
+ +
+ +此项目对接支付开发小电商,会采用DDD领域分层实现,非常具有学习价值。 + +此前,在面试中我看到过众多伙伴写的电商或外卖项目中,写到了支付。也因此拉高了对求职者面试的兴趣。但实际面试下来,大部分校招简历的支付都是模拟的支付,或者说根本没有对接过支付。从而导致面试中,问到;`支付流程`、`掉单处理`、`发货补偿`等真实场景,都没法回答。所以也就高开低走了! + +那么鉴于这样的情况,小傅哥就想在一个项目中,缩小电商场景后,重点突出支付流程的设计实现。并且在整个支付的设计中,采用领域驱动设计的思想进行设计和实现。让你既有丰富的业务流程知识、也有厚实的方案实现。 + +**当我拿出提交记录,阁下又该如何应对!🤨** + +
+ +
+ +此课程内容非常丰富,包括能学习到的技术栈内容、简历编写案例、上线运行和监控都有。[☞可点击查看详细介绍](https://bugstack.cn/md/project/chatgpt/extra/ChatGPT-v1.2.html) + +那么,接下来小傅哥就重点的介绍下,在领域驱动设计下,商品下单支付如何实现。 + +>文末有加入项目学习方式,加入后可以学习工程代码。此外加入还有其他6个项目学习、源码学习、简历优化等服务! + +## 一、商品支付效果演示 + +此项目具有 `前后端 + Dev-Ops` 全栈开发实践,采用 DDD 架构设计落地,运用设计模式编写整洁的代码。并结合 OpenAi 技术、微信支付渠道,做产品化的设计和实现。非常具有学习价值! + +### 1. 商品页 + +
+ +
+ +### 2. 支付页 + +
+ +
+ +### 3. 对话页 + +
+ +
+ +接下来,小傅哥重点介绍下整体的领域模型和商品支付相关的详细设计。也能让伙伴和一些其他项目做个对比,这样小而美、小而精的项目,设计的到底有多赞👍🏻 + +## 一、项目包含的领域 + +星球中的 OpenAI 项目,服务功能已经越来越完善了。既能`满足校招当面试项目`,也能`学习架构当深度扩展`。而且这套 DDD 架构是互联网公司中经过实践,非常好落地的架构设计选择。**支付体验地址:https://openai.itedus.cn/#/mall** + +
+ +
+ +- 左侧,是整个项目所涉及的领域服务,包括;生成式服务、权限校验、微信支付、用户账户、订单交易、商品。构成整个服务功能。 +- 右侧,以用户视角下,了解各个领域的上下文关系。从微信对接、权限校验、再到生成式服务和账户域使用。以及本节要实现的订单交易域。 + +## 二、下单支付,怎么设计? + +做业务功能开发,我们可以先只思考核心的主流程。那么本节要做的功能,最核心的就是用户选择商品下单,之后生成一个支付URL,用户扫码支付。再接收到支付成功回调后,把用户购买的订单发货【额度充值】。 + +
+ +
+ +这是一个非常核心的主流程。有了主流程,我们在思考下可能出现的异常流程。如; + +1. 用户订单创建成功,但创建支付单 HTTP 超时失败。 +2. 支付回调时,系统宕机或者本身服务出问题。 +3. 支付成功后发送MQ消息,消息丢失,用户支付掉单。 +4. 长时间未支付,超时订单。 + +那么,这些就都是可能出现的异常流程。虽然概率很低,但随着使用规模的增加,很低概率的问题,也会产生较大规模的客诉问题。所以要针对这些流程做补偿处理。 + +
+ +
+ +- 针对1~4提到异常流程,一条支付链路就会被扩展为现在的样子,在各个流程中需要穿插进入异常补偿流程。 +- 用户下单,但可能存在之前下的**残单**,那么就要对应给予补充的流程后,再返回回去。 +- 支付回调,仍然可能有异常。所以要有掉单补偿和发货补偿。两条任务处理。 + +## 三、DDD工程结构设计 + +说了业务流程,又提到了 DDD 领域驱动设计,那么接下来咱们看看系统的工程分层结构。有了这个东西,才能感受到 DDD 的设计。 + +
+ +
+ +- 一个小型充值类电商,至少需要3个表;用户账户、商品表、订单表。那么流程则为,用户选择商品下单,支付成功后对个人账户进行充值使用。 +- 基于这样的流程在工程的 domain 领域中,加入 order 领域表。 +- 就说,学习过的项目,看过的工程结构。有如此清晰整洁的吗? + +## 四、代码实现设计和细节体现 + +业务流程说了、工程结构看了。接下来要看看代码在DDD模型结构是怎么体现的。 + +
+ +
+ +
+ +
+ +- 如图,是整个流程支付下单场景在领域中的体现,包括;模型对象、仓储、服务的各层体现。 +- 模型,定义整个订单领域中所需的;聚合、实体和值对象。实体对象为商品实体、订单实体、购物车实体等,之后在聚合中包装商品和订单。 +- 仓储,是对数据库的调用接口,在 domain 层定义接口,在基础层做数据的封装和返回。像是聚合对象基本就是一个事务的体现,交给仓储中做事务处理。 +- 服务,这里才是最终的充血。以前MVC贫血结构中,对象和服务,是远远的被分离的。现在把一组服务所需的对象、数据库操作、事件处理都封装到一个包下。那么实现出来的逻辑才是充血模型。此外,可以重点看下,定义订单接口。是以购物车实体对象为入参,返回支付单为结果的场景订单方法。看似简单,但如果写的很乱的代码,是不会注重这些细节的。但不注意这些细节,等着被持续的时间和迭代的需求逐步放大以后,就会成为严重的代码腐化问题。 + +## 五、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +已经有很多伙伴开始学起来了,还有大家交的作业笔记。有了的项目驱动学习,清晰的目标感,大家冲起来也有了更明确的方向!干干干!!! + +--- + +此课程,目前已经编写了28节课程,每节课程都有对应的视频。手把手带着大家完成一个从0到1的实战项目。需求明确、架构牛皮、代码整洁、还有视频、又能上线的好项目!赶紧学习起来! + +
+ +
diff --git a/docs/md/project/chatgpt/notes.md b/docs/md/project/chatgpt/notes.md new file mode 100644 index 000000000..0fd7cad98 --- /dev/null +++ b/docs/md/project/chatgpt/notes.md @@ -0,0 +1,185 @@ +--- +title: 面试:技能、简历、问题汇总 +lock: no +--- + +# 《ChatGPT 微服务应用体系构建》,关于面试中的技能、简历、问题汇总 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/0d7K7hJ0i](https://t.zsxq.com/0d7K7hJ0i) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +此部分主要用于向读者提供星球项目之一的 ChatGPT 微服务项目如何体现到简历中,包括;专业技能、项目经验。 + +## 一、专业技能 + +- 【前端】熟练使用 Next.js 构建的 React、Typescript 语言,构建的前端工程。 +- 【前端】熟练使用 React Route 路由子页面的开发技术,以及相应的信息传递。 +- 【前端】熟练掌握,跨域接口的对接使用,以及本地 json 数据加载。 +- 【前端】熟练使用本地浏览器内存,存储 Token、配置、对话等信息。 +- 【前端】熟练掌握前端页面数据的存放、使用以及和后端接口的交互方式。 +- 【后端】熟练构建 DDD 工程架构,分层模块,职责体系。并掌握 DDD 架构的开发模式以及微服务设计思想。 +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对其使用源码所提供的接口、类、SPI标准开发各类组件,有一定的设计思路和落地能力。—— 因为这里包含了 SDK 的设计、开发和使用。 +- 【后端】熟练使用多种设计模式、设计原则,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。—— 同时包括非常重要的会话模型OpenAi-SDK的架构设计。 +- 【后端】熟练对接微信公众号 SDK,属性验签流程和对话流程。以及完成 JWT Token 的生成和校验。 +- 【后端】熟练掌握 Nginx Auth 验证模块的开发和使用,以用于对接口的校验和拦截。 +- 【后端】熟练使用流式异步响应式框架开发应答接口,完成前端动态展示应答数据。 +- 【后端】熟悉微信支付渠道对接,掌握商品、交易、下单、发货、掉单补偿等核心流程开发。(这是一个真实支付场景,在DDD架构下实战) +- 【后端】熟练使用 okhttp3、retrofit2 框架,对接 ChatGPT 完成通用 SDK 的开发。有了这项技能,以后你可以方便的对接任何一个 HTTP 请求服务。 +- 【后端】熟练掌握异常、枚举、错误码的定义和使用,并学习如何合理打印服务日志,便于问题排查。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练申请和使用 SSL 配置 Nginx 域名 HTTPS 服务。 + +## 二、简历模板 + +- 项目名称:OpenAI 应用服务 - 辅助工作提效工具开发 +- 项目架构:微服务架构设计,OpenAI-SDK 多模型组件【ChatGLM、ChatGPT】、DDD 应用服务API封装、WEB REACT 前端界面【按需编写】 +- 核心技术:SpringBoot、MyBatis、Redis/Guava、OKHttp3、OpenAI 大模型【可对接ChatGLM、腾讯混元等】、React、TypeScript +- 项目描述:此项目以应用OpenAI技术,对接多种大模型提供生成式服务,为XXX场景提效。项目的架构设计实现以微服务进行拆分,涵盖;OpenAI-SDK、OpenAI-API、公众号鉴权、企业支付【暂时申请中,如果你对接了可以写】等。并以模块化设计,积木式构建应用,让不同的场景诉求都可以配置化对接。 +- 核心职责: + - 首先这是我们xxx公司/实践/实训的第一个OpenAI项目,后续势必会有更多的场景以不同的方式接入。包括 SDK 独立接入、API 接口接入、MQ 消息接入,以及使用提供好的服务类接入。所以在这部分设计的时候,我采用了微服务的架构设计,按照职责边界进行拆分设计。 + - 采用 DDD 架构 API,以及便于不同领域模块的独立设计,一个领域就是一个功能域。在功能域中提供模型、仓储、事件、服务。这样可以更好扩展。 + - 鉴于生成式服务的文本生成可能会有不可靠信息,所以对这部分内容添加了敏感词的过滤。并可根据不同场景选择不同范围的敏感词处理。 + - OpenAI 大模型有多种,这部分在架构上设计独立的 SDK,在实现上采用了 Session 会话模型进行处理,以及通过工厂处理服务。在细节对上,采用了 OKHttp3 框架完成模型对接,这样的方式更好扩展,代码也更易于维护。 + - 在整套工程的设计实现中,采用了较多的分治、抽象的思想和设计模式和设计原则知识的运用,来解决各类场景问题。 + - 对接微信扫码支付,完成从商品库、下单支付、异步发货、掉单补偿等核心流程实现。让用户可以在线购买对话额度。 + - 注意:你还可以根据项目中提到的各类技术和章节,来编写你的职责。 + +## 三、面试问题 + +谢二机同学你好,你做的这个项目我非常感兴趣,我们公司也有这样的OpenAI 业务对接,正好可以结合你做的这个[《OpenAI大模型项目》](https://bugstack.cn/md/project/chatgpt/chatgpt.html)聊一下。 + +### 1. 你的这个项目的背景和需求来自哪里? + +**校招身份举例** + +面试官您好,此项目的最核心背景和诉求,是我希望找到一个可以真实锻炼技术应用的场景。并且可以基于此项目的设计、开发、上线、运维等一系列动作,提高编程思维和落地能力。而此项目的只是一个载体,在项目中我所运用到的微信对接、登录鉴权、异步接口、下单支付、异步发货、账户管理等场景可以运用到其他任何一个项目中使用。所以我选择开发 这样一个项目。 + +并且此项目运用了DDD分层架构,领域驱动设计实现,对于各个场景都遵守了设计原则和设计模式,解决各类复杂场景的实现。如;生成式服务流程中运用模板模式、策略模式、工厂模式,解决对话过程中所需的规则过滤、模型校验、账户状态、账户扣减等开发流程。【项目提问过程中,有时候会让绘制下项目的分层结构、核心流程,之后会基于你画的这些进行提问】 + +通过以上项目的学习我锻炼到了相关项目所用到的核心技术使用,架构设计和落地实现。而此项目的学习,也为我以后在工作中解决实际场问题,打下了牢固的基础。 + +### 2. 项目为什么使用DDD架构,有什么好处? + +首先是 DDD 的结构分层更加清晰(DDD 是一种软件设计方法,软件设计方法涵盖了范式、模型、框架、方法论),与 MVC 相比避免了 PO、VO 对象被乱用的可能。而这也是在 DDD 中将行为和逻辑封装到一个领域内进行处理,也就是常说的充血模型结构。这样的结构方式,就可以更好的做到业务流程松耦合,功能实现高内聚。 + +那么在这个 OpenAI 项目中,按照业务流程涵盖了`鉴权登录`、`OpenAI`、`下单`、`微信`,4个大的核心场景。这4个核心场景恰好是4个领域,每个领域可以独立开发设计,再通过上层进行编排使用。如果是小项目直接由 trigger 触发器层的进行编排,如果是较大型复杂项目可以增加 case 编排层,再由 trigger 层调用。【层之间是防腐,防腐避免了对象和服务被污染】 + +相对比与MVC结构,这样的架构设计,不会因为引入更多的功能,系统的复杂度也随之提高。因为所有的流程都被拆解了,每个功能都聚合到自己的领域内,这样的复杂度不会增加,并且也更好维护。 + +### 3. 充血模型和贫血模型分别合适什么场景? + +在 DDD 架构中,充血模型,主要的价值在于解决具有生命周期的流程。如生成式对话、商品下单支付、用户登录授权等流程。因为这些流程中具有较复杂的场景模型和唯一ID,所以采用充血模型结构,会很适合的将状态和行为封装到同一的领域包中进行聚合开发实现。这样的实现方式,也为以后扩展、迭代、维护做了良好的基建,避免工程过于腐化。—— `注意关于DDD的知识,可以把这里的5个视频都刷下。` [https://bugstack.cn/md/road-map/mvc.html(opens new window)](https://bugstack.cn/md/road-map/mvc.html) + +而贫血模型则比较适合 Querys 场景,因为这些场景只是数据的汇总查询,没有唯一ID和生命周期。所以比较适合在工程中提供 query 模块,使用贫血模型开发。 + +### 4. 项目开发中你是怎么提交代码的? + +首先我是在自己的Gitcode仓库中创建了一个空的项目地址,之后复制项目地址 `https://xxxx.git`。在本地创建 OpenAI SpringBoot 工程,以及工程中 app、domain、infrastructure、trigger、types 5个 modules 模块。在之后在 idea 点开 Git 菜单创建本地仓库,再把仓库中需要提交的内容右键 Add 添加。最后 `Ctrl + Shift + K` 推送代码,推送时配置复制的 git 地址。 + +这个过程中我会注意不要把一些本地文件如 `.idea`、`编译文件` 等提交上去,这些文件可以添加到 `.gitignore` 文件中忽略提交。后续的开发中,会按照`时间-姓名-功能`的规范拉取开发分支,编写代码和提交。开发验证完毕后合并 master 分支进行部署。【如果多人开发,可以把开发分支合并到 test 分支进行部署测试】 + +### 5. 这个项目有部署上线吗?怎么部署的,部署后内存占用如何,请描述下。 + +项目上线了,并且对项目部署了 Prometheus + Grafana 监控组件,以及配置了前端百度统计 PV、UV。部署的方式是先购买了一台`2c4g 5M`带宽的云服务器以及申请备案了域名,之后在前后端应用配置 Dockerfile 打包镜像推送到 DockerHub,之后编写 docker compose 含环境(MySQL、Redis等)脚本,在云服务器执行部署的。因为本项目的登录采用了公众号登录,所以在部署后把服务端接口地址,配置到公众号控制台进行验签和接收验证码登录。 + +整个项目部署完成后,占用了53%~61%的服务器内存,Tomcat 配置了最大连接数是250,OpenAI 调用接口熔断为6秒【OpenAi有时候会有超时】,同时在前端也配置了50秒主动断开。压测过接口的响应,按照目前的服务器可以压测到 50 ~ 80 TPS【随着不同的文本量提问,会有波动。OpenAI 响应有时候会需要7-10秒完成】,日常测试接口响应平均值在3~10秒。—— 这些数据来自于压测后 Grafana 监控的展示【星球内提供了 Grafana 监控配置面板】。 + +这里为什么后端有超时熔断,前端也加了50秒超时主动断开。因为 OpenAI 接口的响应是 ResponseBodyEmitter 异步应答,当返回一个数据块以后,代表已经有响应,则不会计算服务端的超时。但第一次应答后,后面的数据如果卡主了,仍可能一直无应答。所以前端加上主动超时断开会更稳妥。【一般情况下,1~5秒,服务端开始应答】 + +### 6. 你的项目对接了哪些 OpenAI?怎么对接的? + +在项目的开发阶段对接了 ChatGPT 和国内智谱AI ChatGLM,两款AI产品,都有文生文、文生图、多模态的图文理解等功能。对接的方式是采用会话模型(MyBatis Session)流程,使用 retrofit2 + OkHttp3 框架,封装 OpenAI HTTP 接口。提供统一的出入参,并使用工厂模式,构建 OpenAI 启动阶段所需的验证、日志、执行流程。并最终对外提供 OpenAI 会话服务。 + +星球大模型项目的 OpenAI SDK 提供了两种,一种是独立的 ChatGPT-SDK-Java、ChatGLM-SDK-Java,以及一个综合的 OpenAI-SDK-Java 对接了国内外90%的大模型。这里你可以按照自己的学习积累程度来描述。此外这些 SDK 也已经发布到了 Maven 中央仓库,外部人员也可以使用【有不少伙伴参与了 SDK 开发,你也可以讲自己参与了一起开发或者是你独立内部开发的】。 + +### 7. 因为你的项目是前后端分离的,接口跨域怎么做的? + +首先我们知道,Web跨域(Cross-Origin Resource Sharing,CORS)是一种安全机制,用于限制一个域下的文档或脚本如何与另一个源的资源进行交互。这是一个由浏览器强制执行的安全特性,旨在防止恶意网站读取或修改另一个网站的数据,这种攻击通常被称为“跨站点脚本”(Cross-Site Scripting,XSS)。 + +所以在我的前后端分离项目中,通过配置 @CrossOrigin 注解来解决跨越问题。开发阶段 `Access-Control-Allow-Origin: *`、上线阶段 `Access-Control-Allow-Origin: https://gaga.plus` + +### 8. 请描述下商品下单支付场景。以及怎么保证的补偿。 + +下单支付场景在整个项目中是一块非常核心的流程,首先是系统设计采用了 DDD 架构,对商品下单按照业务流程拆解出来了单独的领域模块。在设计实现上,以购物车为下单入参的实体对象,出参为支付单实体。保存的是订单的聚合信息。 + +因为本身下单是一个较为复杂的流程,所以在编码实现上采用了模板模式,定义出下单的标准过程。商品下单的流程需要先查询是否存在`已下单未支付`和`已下单但无支付单`的订单,对这样两种订单分别进行处理。已下单未支付,在有效期内未关单的直接返回给用户直接支付。已下单但无支付单则调用(微信/支付宝沙箱/蓝兔)创建支付订单,保存库表记录后返回给前端用户进行支付。此外的流程则直接执行购物车商品ID查询,组装聚合订单数据,创建支付单,保存库表记录和返回给用户。在用户支付完成后,接收支付回调消息,推送(MQ/Redis发布订阅/Guava事件)信息,由系统中 trigger 模块下的监听处理接收支付成功消息,完成商品的发货处理。 + +那么这里可能发生的调单情况,接收回调消息处理失败。则由定时任务扫描库表订单超过15分钟未支付的订单,查询支付平台(微信/支付宝沙箱/蓝兔)是否已支付,如果支付则发送事件消息走后续的补货流程。另外如果超过15分钟以上未支付则进行本地关单,不对支付平台关单(支付平台有自己的时间),这样可以让用户的体验更加舒服。用户超时后支付,仍可以走后续的发货流程。但如果用户刷新界面,则获取创建新的支付单。 + +### 9. 你的订单表也是一个频繁使用的表,那么库表有哪些核心字段,索引有哪些? + +有用户ID(用的公众号创建的 openid)、商品ID、商品名称、商品金额、订单ID、下单时间、订单状态、支付单类型(微信、支付宝)、支付单号、支付时间、交易单号、交易状态等。表中对订单ID、用户唯一创建了唯一索引,对订单状态和订单时间创建组合索引(提高扫描效率)。 + +### 10. 我看到OpenAI的体验,都是渐进式展示的,这块后端使用了什么接口形式? + +这部分是使用 SpringBoot 应用提供的 HTTP 接口,返回的是 ResponseBodyEmitter 异步请求处理协议。它是 SpringMVC 所提供的一个异步响应提,当你控制器中返回一个 ResponseBodyEmitter 实例时,Spring MVC 会开启一个异步请求处理,这样就可以在单个请求中发送多个 OpenAI 应答数据块。这样的应答方式是非常适合 OpenAI 这样需要大批量应答数据的场景。此外因为服务端的接口需要 Nginx 转发,所以在 Nginx 端还需要关闭分块解码(chunked_transfer_encoding)、关闭转发缓冲(proxy_buffering)、关闭应答缓存(proxy_cache),这样最终才能是一个渐进式的展示效果。 + +### 11. 你的项目中对接了 ChatGPT 和 ChatGLM 两个模型,那么使用了什么设计模式? + +关于 ChatGPT、ChatGLM 两个 OpenAI 服务,在项目中定义了一个通信渠道策略接口,接口方法返回统一的格式数据。两款 OpenAI 服务分别实现自己接口处理。之后两个实现的策略模式注入到 Map 中,Key 是一个枚举值。当前端选择不同的模型进行问答时,则根据模型的枚举值从 Map 中选择出对应的策略处理类。这样即使后续拓展其他的 OpenAI 服务也非常容易扩展。 + +### 12. 你在项目中有支付购买的次数,那么对应就会有额度扣减、账户的校验、还有模型的使用类型,这些是怎么实现的? + +其实除了账户、额度、模型,还有敏感词过滤,在这部分实现中我定义了统一的 ILogicFilter 过滤接口,分别实现不同的过滤诉求。在通过工厂的封装,装配上不同的过滤规则类。用户进行问答后,会对用户的信息分别进行校验。这部分也就是整体 OpenAI 应答中使用的模板、工厂、策略三个设计模式。另外像是这样的调用验证方式,也可以使用责任链的方式处理。 + +之后这里在说下敏感词,敏感词对接了通用的敏感词库,但校验的过于严格同时不是动态的,所以可能不准。后来又对接了[云服务厂商的敏感词过滤服务+图片审核服务](https://bugstack.cn/md/project/ddd-scene-solution/sensitive-word-content-moderation.html)。可以配置广告、舆情、敏感类等都可以配置过滤。 + +### 13. 你是怎么处理异常的? + +项目中在对接 OpenAI 接口、数据库、缓存、下单调用等,是有可能出现超时、数据接口更新、数据库连接、缓存数据问题等异常,这些异常属于功能异常。是在每个领域模型内可能发生的问题情况。为了让异常保持统一,具备业务语意,所以在 types 层了定义业务异常类 ChatGPTException 和对应的异常码枚举【0000 成功、0001 失败、0002 非法参数、0003 未登录、OE001 商品以下架等异常码】这样就可以标准化返回给前端,针对不同的异常进行信息展示。 + +### 14. 对于返回给前端的接口,返回从出参结果怎么定义的? + +定义一个 `Response` 添加 code、info、data(枚举类型)参数,统一封装返回结果。这是一个通用设计,应该不止我开发的系统,在我去F12看各个网站的时候,也都是这样统一标准的接口出参。 + +### 15. 公众号里可以对接 OpenAI 自动回复吗? + +技术上可以对接的,在公众号配置完接口地址验签成功后,就可以接收用户在公众号发送的消息了,之后根据消息内容请求 OpenAI 同步响应接口(也可以是异步接口用 Future 封装)。但这里要注意一点,我是个人公众号开发,不能直接根据用户ID给用户返回信息,只能随着请求一次返回。 + +那么就有可能用户提问后,我调用 OpenAI 接口,因为会需要较长时间返回数据,超过公众号5秒限制,就会得不到数据。所以这块利用公众号的回调机制,5秒+3次,我再后端使用了 CountDownLatch 进行等待,每次都耗时5秒,让公众号第3次在从我的后端获取数据返回。这样可以最大限度保证,一次提问就能获得数据。如果仍然没有获得数据,则提示给用户需要再次提问同样的问题。这个时候我会以问题作为key,获取OpenAI的结果缓存回答给用户。 + +### 16. 应用程序使用了什么数据库连接池,连接数配置如何? + +配置了 SpringBoot 默认提供的 hikari 连接池。在这之前我也有压测过 [c3p0、dbcp、druid、hikari](https://bugstack.cn/md/road-map/connection-pool.html) 这四款连接池,其中 hikari 是效率最高也是最稳定的,dbcp 相对较差,但如果不使用连接池那么会更差。 + +在我的应用中,配置最小空闲连接数(minimum-idle)是15个,最大连接数(maximum-pool-size)是25个。本身下单接口的平均响应时间是48毫秒,那么 `1秒 / 0.048秒/事务 = 约20.83事务/秒/连接` 也就是 `20.83事务/秒/连接 * 25连接 = 约520.75事务/秒` 这个量级是非常够用的。如果不够可以适当调整连接数大小。 + +### 17. 是否支持分布式部署? + +是的,一个项目是否支持分布式部署的标识在于它的数据处理是基于单体的还是基于分布式架构的,当一台机器宕机用户在访问时候轮训到下一台机器是否还可以保证业务进行。像是这套项目使用数据库存储用户、账户、商品、下单,在 Redis 存放用户的登录鉴权,那么就可以基于 Nginx 轮训的方式配置多态应用的负载,以此支持分布式架构。 + +### 18. 登录这个业务过程是什么样? + +企业公众号登录的模型是通过 AccessToken 创建二维码凭证 ticket,并让前端根据凭证创建带参登录二维码。当用户扫码后,对接公众号的服务端会收到回调信息,里面含带了 ticket、openid,那么这个时候就可以创建出 jwt token,前端页面不断通过 ticket 轮训接口获取绑定登录信息即可。 + +另外是个人公众号,它是没有这个权限的。所以只能是让用户在公众号中回复一个固定编号(405),之后服务端接受到固定编号后创建一个唯一登录验证码分配给 openid,用户在通过在页面填写验证码的方式进行登录。填写验证码后,调用服务端接口进行鉴权下发 jwt token 即可。 + +### 19. 如果现在需要让你扩展一个功能,比如接入图片转代码/根据流程图生成代码你会怎么做? + +这种问题是比较偏开放性的,如果做完项目又结合项目扩展了新的功能,那么回答这类问题会更加得心应手。因为如果你扩展了新的功能,那么你就会知道怎么先去了解需要对接功能的文档【技术调研】,之后结合文档来做对应的案例,让案例覆盖80%你要实现的功能。这样你大概知道了这样东西是可以使用的。 + +接下来梳理代码,设计接入方式和流程,并设计这部分的领域功能,开发完成功能以后开始进行测试验证。这部分是单元测试。测试通过后,开始提供对应的服务的接口,如果是旧的功能扩展,则要考虑兼容性,比如增加了新的模型枚举。可参考案例:[OpenAI + TLDraw 设计图转前端代码](https://bugstack.cn/md/project/ddd-scene-solution/openai-tldraw.html) + +### 20.(开放问题)你在做项目中,什么问题难住你的时间最长,为什么? + +这是一个开放问题,重点考察你对项目的开发中个人的积累。你可以针对自己的学习过程中,有哪个流程的实现,让你最为有感触,即可回答。 + +这还是比较多的,这个项目有很多的创新设计让代码更好的维护,也有一些取巧的架构方案。比如提到的个人公众号,没有带参二维码就只能通过让用户主动回复生产验证码绑定openid解决,用户在输入验证码进行登录。 + +另外是一个下单的场景,这块的DDD领域设计,到底要用什么作为入参,什么做为出参,进行下单。经过了大量的思考,应该模拟生活中的超时,推着购物车到收银台,创建支付单,扫码登录。所以在系统设计上,以购物车为入参的实体对象,出参为支付单信息【可以扫码支付】,中间的流程用聚合对象保存订单信息。这块的设计,非常清晰和好维护。所以也是一个记忆非常深刻的点。 + +## 四、公司面试 + +### 1. 面试被问openai项目有被问到为什么要使用okhttp3和retrofit,我回答的是用他们来封装对http的调用,面试官说是为什么要选择okhttp3而不是其他的框架?这块我没有接触过其他框架所以回答的不是很好,想问下小傅哥这个问题应该怎么回答 + +- 问题:[https://t.zsxq.com/xXQZr](https://t.zsxq.com/xXQZr) + +1. 面试官要问的是,对于 http 的封装调用,你都了解哪些框架。如;Apache HttpClient、OkHttp、Retrofit、Spring RestTemplate、Spring WebClient、Feign,甚至 hutool 也提供了封装框架。 +2. 之后你要说明,这些框架的优劣势对比,并重点介绍 OkHttp、Retrofit + - 2.1 OkHttp - OkHttp是一个现代的HTTP客户端,它提供了简洁的API和高效的性能。它支持同步阻塞调用和异步调用,并且能够处理SPDY、HTTP/2和WebSocket等协议。 + - 2.2 Retrofit - Retrofit是一个类型安全的HTTP客户端,它通过注解将HTTP API转换为Java接口。它通常与OkHttp一起使用,提供了一个高层次的抽象来处理网络请求。 +3. 对于2中使用的框架,还可以提到目前在微信支付sdk、支付宝sdk,都有使用现代简洁高效的 okttp 框架,对于一些标准度更高的调用则使用 Retrofit,因为他可以简单配置即可使用,屏蔽了对接细节。这与以往的 httpclient 对接方式相比,代码更加简洁,维护成本更低。 \ No newline at end of file diff --git a/docs/md/project/chatgpt/review.md b/docs/md/project/chatgpt/review.md new file mode 100644 index 000000000..42239a0d5 --- /dev/null +++ b/docs/md/project/chatgpt/review.md @@ -0,0 +1,142 @@ +--- +title: 复盘:OpenAi 项目复盘总结 +lock: no +--- + +# 复盘:OpenAi 项目复盘总结 —— 小傅哥又一个Java项目力作! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/133o5FKvc](https://t.zsxq.com/133o5FKvc) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。星球【[码农会锁](https://wx.zsxq.com/dweb2/index/group/48411118851818)】 OpenAi 可真实上线运行的项目,3个阶段全部完结交付💐🌶 + +前端;埋点监控 pv、uv、ip、跳出率、热力图。后端;普罗米修斯、Grafana 监控 QPS、调用量、响应时长、系统负载。系统;DDD 架构 + 微信支付 + 账户充值 +多渠道路由(ChatGPT、ChatGLM) + React 前端UI。—— 嘎嘎强、嘎嘎强! + +
+ +
+ +**能做一个上线运行的项目,就能多学到一半的知识量**! + +不上线,就不知道系统需要多大的服务器。不上线,就不能拿到系统的监控数据。不上线,就不清楚系统都有哪些异常。不上线,就不记得要给数据库表做索引优化。说的直白了,没有上线的项目,还只能算是开发阶段的`“技术玩具”`。甚至可能很多系统开发中的流程都是不健全的,缺少细节的处理和流程的补偿。所以,从这个项目开始,小傅哥在星球【码农会锁】带着大家做的项目,都将以上线为最终结果。 + +当我们把系统做完,配置上监控。看到系统负载和调用量那一刻,这个系统才是一个完整的系统。 + +
+ +
+ +那么,接下来小傅哥就对整个系统,做一次从需求到技术落地的复盘总结。让大家可以学习到的更多。 + +>文末有加入学习方式。此外在本项目的业务场景底座上,后续将开启新的上线项目,你猜猜会是什么项目!👍🐂 + +## 一、先说你能学到的 + +首先呢,此项目是一个真实运行在线上有用户使用的系统,所以所有的需求设计和落地实现,都以真实情况进行考量。那么也就是说,所有学习此项目的伙伴,都将学习到真实场景的实战技能。这些技能会通过课程的3个阶段来完成,如下; + +
+ +
+ +- 第1阶段【基础】:01节~23节,学习工程的前后端基础框架搭建开发、实现 OpenAi 异步响应式对话、微信公众号鉴权登录。 +- 第2阶段【深度】:24节~29节,运用模板、策略、工厂,添加多规则过滤引擎,处理白名单、敏感词、账户额度、状态、模型的验证,以及完成核心商品下单、微信支付、账户充值流程。这部分的 DDD 领域模型设计的非常漂亮。 +- 第3阶段【广度】:30节~32节,多渠道策略模型OpenAi对接,系统监控(Prometheus + Grafana)、分布式技术栈扩展,让系统具备分布式部署能力。 + +此项目拆成3个阶段,让大家学习不会有太大压力,如果你着急完成并希望面试,那么完成第1阶段即可编写简历中使用。`整体项目预计在3周~4周可学习完成,如果不编写前端预计在2~3周学习完成。` + +## 二、需求是怎么来的 + +OpenAi 项目,最早来自于小傅哥分别部署过的两个前端开源项目,给大家提供生成式服务。从最开始的部署完就能使用,到后来对接公众号登录引流粉丝关注,再到设计加密ApiKey降低资源投入成本,这样一步步形成了整个需求诉求。 + +
+ +
+ +1. **开源项目**,先后部署了2个版本的WEB-UI,也在各个版本中添加了微信公众号登录和加密Key独立使用的功能。一个是为了吸粉,另外一个是为了让常用用户有自己的Key,这样可以适当降低成本。 + - 优势;开源项目可以快速验证市场,完成早期的应用上线。并且开源项目有很多人维护,可以快速迭代。 + - 劣势;这个劣势到不是开源项目的问题,而是自身我们要结合自身需求迭代时所呈现的问题。 + +2. **自研项目**,于4.1日开始启动,10.1日全部切换到新版。对于我们学习来说,OpenAi 生成式服务只是个场景,结合这个场景可以锻炼其他各项模块的学习使用。如;公众号对接、登录、鉴权、规则过滤、商品下单、微信支付、商品发放等各项功能的开发。那么换换成其他场景一样可以做这些内容。 + - 优势;自研项目,容易管理,方便扩展。对各项功能的迭代都会非常方便。 + - 劣势;前期投入陈本大,需要较长的研发周期。 + +其实我很早就想自研一个这样的项目,但也因为个人可支配时间有限,既有日常的技术推文编写,也有星球内的课程开发。所以就想着那么不如把这样一个项目做成星球课程项目,既满足我能上线给大家使用,也能让星球伙伴学习到真实场景的项目,岂不是一举两得! + +> 所以,对于各位来说;你相当于拿到了一个“公司”对外上线的项目,通过渐进式逐个章节+视频的方式,手把手教你学习开发实践应用技能!嘎嘎强、嘎嘎强! + +## 三、系统是怎么做的 + +因为这套系统的第一要点是上线,那可不能胡乱开发,也不能随便的炫技个技术栈就完事了。并且所有的流程被抽象后都应该具备良好的扩展性和迭代性,这样的诉求下完成的交付,才是非常有价值的。 + +那么,我们以一个用户旅程的视角来看下,系统的流程脉络。 + +
+ +
+ +以用户的旅程视角,来看整个系统的模块串联。 + +- 首先,从用户登录 OpenAi-Web-UI 开始,引导关注公众号,获取验证码,并完成登录。这个时候系统会进行验证码与账户的绑定,并通过加锁的方式保证唯一关联关系。 +- 之后,用户开始对话。那么系统会根据对话用户的类型,选择不同的渠道。如系统默认还是用户自身,并根据不同的类型进行校验。如果用户使用完默认的体验次数后,则引导进行账户充值处理。这块会进入整个后台系统商品域的处理,采用DDD架构模型进行落地。 +- 最后,用户充值后使用则进行账户相关的校验,以及校验后根据结果返回给用户对应的信息。此外系统还提供了多模型的支持,对话选择阶段,进行多渠道路由的处理。这块在系统开发中,用到了策略 + 工厂的使用。 + +## 四、项目交付展示下 + +本次项目,是一个具有前后端 + Dev-Ops的综合项目实践,也是最新 DDD 架构模型的落地开发,在这个项目中你会学习到非常干货的技术运用、场景方案,也会学习到系统的部署、运维、监控等知识内容。 + +### 1. 应用部署 - 环境 + +
+ +
+ +### 2. 项目演示 - 支付 + +
+ +
+ +### 3. 前端监控 - 热力图 + +
+ +
+ +### 4. 后端监控 - Grafana + +
+ +
+ +## 五、项目大纲 + +**此项目,是按照互联网公司开发项目模式进行落地,逐个分支,逐步迭代完成。每个章节都会对应一个分支,并配文档和视频,讲解需求、讲解架构、讲解代码。** 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。 + +
+ +
+ +--- + +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! + +## 六、加入学习 + +OpenAi 项目,是小傅哥【星球:[码农会锁](https://wx.zsxq.com/dweb2/index/group/48411118851818)】的第7个完结项目,其他的还包括:Api网关、Lottery 抽奖、IM通信、SpringBoot Starter、IDEA Plugin 等。—— 死鬼,你见过这么多的项目社区吗! + +不过,这还不是最💥炸裂的。最炸裂的是,我们将在 OpenAi 项目的业务底座上,扩展更多项目开发。因为已经有了这个上线对外的项目,有用户、有流量、有行为,接下来才是后续的开发项目,将是全部以上线为目标,挑战技术运用! + +**这是我心中的山河⛰** —— 你猜,小傅哥接下来会做哪个项目?🤔 + +
+ +
+ +在有了 OpenAi 项目以后,这篇山河图中的项目,都将可以落地。哪怕我想做个拼多多的砍一刀,都可以!那么,在 OpenAi 项目完全收尾后,你觉得小傅哥会启动哪个项目?可以留言评论区哦! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) 能做到这样的技术项目实战社群,真的不多!你只是投入一顿大麻辣烫💰,就🉐获得超级大的回报! \ No newline at end of file diff --git a/docs/md/project/chatgpt/sdk/chatglm-sdk-java-v2.md b/docs/md/project/chatgpt/sdk/chatglm-sdk-java-v2.md new file mode 100644 index 000000000..07ebf9cda --- /dev/null +++ b/docs/md/project/chatgpt/sdk/chatglm-sdk-java-v2.md @@ -0,0 +1,515 @@ +--- +title: 第2节:ChatGLM SDK - 智谱Ai v2 +lock: no +--- + +# 《ChatGPT 微服务应用体系构建》 - chatglm-sdk 第2节:ChatGLM SDK - 智谱Ai(3.0、4.0、cogview) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +鉴于智谱AI发布了最新一代 `GLM3.0`、`GLM4.0` 基座大模型,我又要对自己开发的这款开源 [chatglm-sdk-java](https://github.com/fuzhengwei/chatglm-sdk-java) 进行改造了!因为需要做新老接口的模型调用中数据格式兼容,这将是一场`编码设计`与`复杂场景`的对抗挑战。**💐 请看小傅哥如何操刀改造!** + +
+ +
+ +**为什么改造SDK会比较复杂?🤔** + +通常来说,我们开发好一款SDK后,就会投入到项目中使用,而使用的方式会根据SDK的出入参标准进行对接。比如一个接口的入参原本有2个参数,`A String`、`B String` 类型,但现在因为有额外的功能添加,从2个参数调整为3个参数,同时需要对原本的 B 参数 String 类型,扩展为 Object 类型,添加更多的属性信息。同时出参也有对应响应结构变化。 + +那么对于这种线上正在使用又需要改造代码的情况,我们不可能把原有的代码都铲了不要,所以需要做一些优雅的兼容的开发处理。让工程更加好维护、好迭代。 + +**在设计原则和设计模式的锤炼下,写出高质量的代码** + +
+ +
+ +那么,接下来小傅哥就带着大家讲讲这段关于GLM新增模型后 SDK 的重构操作。 + +>文末有整个 SDK 的源码,直接免费获取,拿过去就是嘎嘎学习! + +## 一、需求场景 + +**智谱AI文档**:[https://open.bigmodel.cn/overview](https://open.bigmodel.cn/overview) + +本次文档中新增加了 GLM-3-Turbo、GLM-4、GLM-4v、cogview,这样四个新模型,与原来的旧版 chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro,在接口调用上做了不小的修改。因为新版的模型增加了如插件「联网、知识库、函数库」、画图、图片识别这样的能力,所以出入参也有相应的变化。 + +### 1. curl 旧版 + +```shell +curl -X POST \ + -H "Authorization: Bearer BearerTokenUtils.getToken(获取)" \ + -H "Content-Type: application/json" \ + -H "Accept: text/event-stream" \ + -d '{ + "top_p": 0.7, + "sseFormat": "data", + "temperature": 0.9, + "incremental": true, + "request_id": "xfg-1696992276607", + "prompt": [ + { + "role": "user", + "content": "写个java冒泡排序" + } + ] + }' \ + http://open.bigmodel.cn/api/paas/v3/model-api/chatglm_lite/sse-invoke +``` + +- 注意:旧版的调用方式是把模型放到接口请求中,如;`chatglm_lite` 就是放到请求地址中。 + +### 2. curl 3&4 + +```shell +curl -X POST \ + -H "Authorization: Bearer BearerTokenUtils.getToken(获取)" \ + -H "Content-Type: application/json" \ + -d '{ + "model":"glm-3-turbo", + "stream": "true", + "messages": [ + { + "role": "user", + "content": "写个java冒泡排序" + } + ] + }' \ + https://open.bigmodel.cn/api/paas/v4/chat/completions +``` + +- 注意:新版的模型为入参方式调用,接口是统一的接口。此外入参格式的流式可以通过参数 `"stream": "true"` 控制。 + +### 3. curl 4v + +```shell +curl -X POST \ + -H "Authorization: Bearer BearerTokenUtils.getToken(获取)" \ + -H "Content-Type: application/json" \ + -d '{ + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "这是什么图片" + }, + { + "type": "image_url", + "image_url": { + "url" : "支持base64和图片地址;https://bugstack.cn/images/article/project/chatgpt/chatgpt-extra-231011-01.png" + } + } + ] + } + ], + "model": "glm-4v", + "stream": "true" + }' \ + https://open.bigmodel.cn/api/paas/v4/chat/completions +``` + +- 注意:多模态4v模型,content 字符串升级为对象。这部分与 chatgpt 的参数结构是一致的。所以我们在开发这部分功能的时候,也需要做兼容处理。因为本身它既可以支持对象也可以支持 conten 为字符串。 + +### 4. curl cogview + +```shell +curl -X POST \ + -H "Authorization: Bearer BearerTokenUtils.getToken(获取)" \ + -H "Content-Type: application/json" \ + -d '{ + "model":"cogview-3", + "prompt":"画一个小狗狗" + }' \ + https://open.bigmodel.cn/api/paas/v4/images/generations +``` + +- 注意:图片的文生图是一个新的功能,旧版没有这个接口。所以开发的时候按照独立的接口开发即可。 + +--- + +**综上,这些接口开发还需要注意;** + +1. 首先,小傅哥根据官网文档编写了对应的 curl 请求格式。方便开发时可以参考和验证。 +2. 之后,curl 旧版是文本类处理,curl 3&4 是新版的文档处理。两个可以对比看出,旧版的入参是 prompt,新版是 messages +3. 另外,本次API文档新增加了文生图,和4v(vision)多模态的图片识别。 + +> 接下来,我们就要来设计怎么在旧版的SDK中兼容这些功能实现。 + +## 二、功能实现 + +### 1. 调用流程 + +
+ +
+ +如图,是整个本次 SDK 的实现的核心流程,其中执行器部分是本次重点开发的内容。在旧版的 SDK 中是直接从`会话请求`进入模型的调用,没有执行器的添加。而执行器的引入则是为了解耦调用过程,依照于不同的请求模型(chatglm_std、chatglm_pro、GLM_4...),可以调用到不同的执行器上去。 + +### 2. 执行器「解耦」 + +#### 2.1 接口 + +```java +public interface Executor { + + /** + * 问答模式,流式反馈 + * + * @param chatCompletionRequest 请求信息 + * @param eventSourceListener 实现监听;通过监听的 onEvent 方法接收数据 + * @return 应答结果 + * @throws Exception 异常 + */ + EventSource completions(ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws Exception; + + // ... 省略部分接口 +} +``` + +#### 2.2 旧版 + +```java +public class GLMOldExecutor implements Executor { + + /** + * OpenAi 接口 + */ + private final Configuration configuration; + /** + * 工厂事件 + */ + private final EventSource.Factory factory; + + public GLMOldExecutor(Configuration configuration) { + this.configuration = configuration; + this.factory = configuration.createRequestFactory(); + } + + @Override + public EventSource completions(ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws Exception { + // 构建请求信息 + Request request = new Request.Builder() + .url(configuration.getApiHost().concat(IOpenAiApi.v3_completions).replace("{model}", chatCompletionRequest.getModel().getCode())) + .post(RequestBody.create(MediaType.parse("application/json"), chatCompletionRequest.toString())) + .build(); + + // 返回事件结果 + return factory.newEventSource(request, eventSourceListener); + } + + // ... 省略部分接口 + +} +``` + +#### 2.3 新版 + +```java +public class GLMExecutor implements Executor, ResultHandler { + + /** + * OpenAi 接口 + */ + private final Configuration configuration; + /** + * 工厂事件 + */ + private final EventSource.Factory factory; + /** + * 统一接口 + */ + private IOpenAiApi openAiApi; + + private OkHttpClient okHttpClient; + + public GLMExecutor(Configuration configuration) { + this.configuration = configuration; + this.factory = configuration.createRequestFactory(); + this.openAiApi = configuration.getOpenAiApi(); + this.okHttpClient = configuration.getOkHttpClient(); + } + + @Override + public EventSource completions(ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws Exception { + // 构建请求信息 + Request request = new Request.Builder() + .url(configuration.getApiHost().concat(IOpenAiApi.v4)) + .post(RequestBody.create(MediaType.parse(Configuration.JSON_CONTENT_TYPE), chatCompletionRequest.toString())) + .build(); + + // 返回事件结果 + return factory.newEventSource(request, chatCompletionRequest.getIsCompatible() ? eventSourceListener(eventSourceListener) : eventSourceListener); + } + + + // ... 省略部分接口 + +} +``` + +
+ +
+ +在新版的执行实现中,除了 IOpenAiApi.v4 接口的变动,还有一块是对外结果的封装处理。这是因为在旧版的接口对接中使用的是 EventType「add、finish、error、interrupted」枚举来判断。但在新版中则只是判断 stop 标记符。所以为了让之前的SDK使用用户更少的改动代码,这里做了结果的封装。 + +### 3. 注入配置 + +**源码**:`cn.bugstack.chatglm.session.Configuration` + +```java +public HashMap newExecutorGroup() { + this.executorGroup = new HashMap<>(); + // 旧版模型,兼容 + Executor glmOldExecutor = new GLMOldExecutor(this); + this.executorGroup.put(Model.CHATGLM_6B_SSE, glmOldExecutor); + this.executorGroup.put(Model.CHATGLM_LITE, glmOldExecutor); + this.executorGroup.put(Model.CHATGLM_LITE_32K, glmOldExecutor); + this.executorGroup.put(Model.CHATGLM_STD, glmOldExecutor); + this.executorGroup.put(Model.CHATGLM_PRO, glmOldExecutor); + this.executorGroup.put(Model.CHATGLM_TURBO, glmOldExecutor); + // 新版模型,配置 + Executor glmExecutor = new GLMExecutor(this); + this.executorGroup.put(Model.GLM_3_5_TURBO, glmExecutor); + this.executorGroup.put(Model.GLM_4, glmExecutor); + this.executorGroup.put(Model.GLM_4V, glmExecutor); + this.executorGroup.put(Model.COGVIEW_3, glmExecutor); + return this.executorGroup; +} +``` + +- 对于不同的模型,走哪个执行器,在 Configuration 配置文件中写了这样的配置信息。 +- 这样当你调用 CHATGLM_TURBO 就会走到 glmOldExecutor 模型,调用 GLM_4V 就会走到 glmExecutor 模型。 + +### 4. 参数兼容 + +ChatCompletionRequest 作为一个重要的应答参数对象,在本次的接口变化中也是调整了不少字段。但好在小傅哥之前就提供了一个 toString 对象的方法。在这里我们可以做不同类型参数的处理。 + +```java +public String toString() { + try { + // 24年1月发布新模型后调整 + if (Model.GLM_3_5_TURBO.equals(this.model) || Model.GLM_4.equals(this.model) || Model.GLM_4V.equals(this.model)) { + Map paramsMap = new HashMap<>(); + paramsMap.put("model", this.model.getCode()); + if (null == this.messages && null == this.prompt) { + throw new RuntimeException("One of messages or prompt must not be empty!"); + } + paramsMap.put("messages", this.messages != null ? this.messages : this.prompt); + if (null != this.requestId) { + paramsMap.put("request_id", this.requestId); + } + if (null != this.doSample) { + paramsMap.put("do_sample", this.doSample); + } + paramsMap.put("stream", this.stream); + paramsMap.put("temperature", this.temperature); + paramsMap.put("top_p", this.topP); + paramsMap.put("max_tokens", this.maxTokens); + if (null != this.stop && this.stop.size() > 0) { + paramsMap.put("stop", this.stop); + } + if (null != this.tools && this.tools.size() > 0) { + paramsMap.put("tools", this.tools); + paramsMap.put("tool_choice", this.toolChoice); + } + return new ObjectMapper().writeValueAsString(paramsMap); + } + + // 默认 + Map paramsMap = new HashMap<>(); + paramsMap.put("request_id", requestId); + paramsMap.put("prompt", prompt); + paramsMap.put("incremental", incremental); + paramsMap.put("temperature", temperature); + paramsMap.put("top_p", topP); + paramsMap.put("sseFormat", sseFormat); + return new ObjectMapper().writeValueAsString(paramsMap); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } +} +``` + +- 如果为本次调整的新增模型,则走新的方式装配参数信息。 +- 通过这样的方式可以很轻松的把以前叫做 prompt 的字段调整为 messages 名称。类似的操作可以看具体的代码。`关于字段的出入参处理,但比较同类,就不一一列举了` + +## 三、功能验证 + +**注意**:测试前需要申请ApiKey [https://open.bigmodel.cn/overview](https://open.bigmodel.cn/overview) 有非常多的免费额度。 + +```java +@Before +public void test_OpenAiSessionFactory() { + // 1. 配置文件 + Configuration configuration = new Configuration(); + configuration.setApiHost("https://open.bigmodel.cn/"); + configuration.setApiSecretKey("62ddec38b1d0b9a7b0fddaf271e6ed90.HpD0SUBUlvqd05ey"); + configuration.setLevel(HttpLoggingInterceptor.Level.BODY); + // 2. 会话工厂 + OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); + // 3. 开启会话 + this.openAiSession = factory.openSession(); +} +``` + +- 申请后把你的 ApiKey 替换 setApiSecretKey 就可以使用了。 + +### 1. 文生文「支持联网」 + +```java +@Test +public void test_completions() throws Exception { + CountDownLatch countDownLatch = new CountDownLatch(1); + // 入参;模型、请求信息 + ChatCompletionRequest request = new ChatCompletionRequest(); + request.setModel(Model.GLM_3_5_TURBO); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro + request.setIncremental(false); + request.setIsCompatible(true); // 是否对返回结果数据做兼容,24年1月发布的 GLM_3_5_TURBO、GLM_4 模型,与之前的模型在返回结果上有差异。开启 true 可以做兼容。 + // 24年1月发布的 glm-3-turbo、glm-4 支持函数、知识库、联网功能 + request.setTools(new ArrayList() { + private static final long serialVersionUID = -7988151926241837899L; + { + add(ChatCompletionRequest.Tool.builder() + .type(ChatCompletionRequest.Tool.Type.web_search) + .webSearch(ChatCompletionRequest.Tool.WebSearch.builder().enable(true).searchQuery("小傅哥").build()) + .build()); + } + }); + request.setPrompt(new ArrayList() { + private static final long serialVersionUID = -7988151926241837899L; + { + add(ChatCompletionRequest.Prompt.builder() + .role(Role.user.getCode()) + .content("小傅哥的是谁") + .build()); + } + }); + // 请求 + openAiSession.completions(request, new EventSourceListener() { + @Override + public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) { + ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class); + log.info("测试结果 onEvent:{}", response.getData()); + // type 消息类型,add 增量,finish 结束,error 错误,interrupted 中断 + if (EventType.finish.getCode().equals(type)) { + ChatCompletionResponse.Meta meta = JSON.parseObject(response.getMeta(), ChatCompletionResponse.Meta.class); + log.info("[输出结束] Tokens {}", JSON.toJSONString(meta)); + } + } + @Override + public void onClosed(EventSource eventSource) { + log.info("对话完成"); + countDownLatch.countDown(); + } + @Override + public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) { + log.info("对话异常"); + countDownLatch.countDown(); + } + }); + // 等待 + countDownLatch.await(); +} +``` + +### 2. 文生图 + +```java +@Test +public void test_genImages() throws Exception { + ImageCompletionRequest request = new ImageCompletionRequest(); + request.setModel(Model.COGVIEW_3); + request.setPrompt("画个小狗"); + ImageCompletionResponse response = openAiSession.genImages(request); + log.info("测试结果:{}", JSON.toJSONString(response)); +} +``` + +### 3. 多模态 + +```java +public void test_completions_v4() throws Exception { + CountDownLatch countDownLatch = new CountDownLatch(1); + // 入参;模型、请求信息 + ChatCompletionRequest request = new ChatCompletionRequest(); + request.setModel(Model.GLM_4V); // GLM_3_5_TURBO、GLM_4 + request.setStream(true); + request.setMessages(new ArrayList() { + private static final long serialVersionUID = -7988151926241837899L; + { + // content 字符串格式 + add(ChatCompletionRequest.Prompt.builder() + .role(Role.user.getCode()) + .content("这个图片写了什么") + .build()); + // content 对象格式 + add(ChatCompletionRequest.Prompt.builder() + .role(Role.user.getCode()) + .content(ChatCompletionRequest.Prompt.Content.builder() + .type(ChatCompletionRequest.Prompt.Content.Type.text.getCode()) + .text("这是什么图片") + .build()) + .build()); + // content 对象格式,上传图片;图片支持url、basde64 + add(ChatCompletionRequest.Prompt.builder() + .role(Role.user.getCode()) + .content(ChatCompletionRequest.Prompt.Content.builder() + .type(ChatCompletionRequest.Prompt.Content.Type.image_url.getCode()) + .imageUrl(ChatCompletionRequest.Prompt.Content.ImageUrl.builder().url("https://bugstack.cn/images/article/project/chatgpt/chatgpt-extra-231011-01.png").buil + .build()) + .build()); + } + }); + openAiSession.completions(request, new EventSourceListener() { + @Override + public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) { + if ("[DONE]".equals(data)) { + log.info("[输出结束] Tokens {}", JSON.toJSONString(data)); + return; + } + ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class); + log.info("测试结果:{}", JSON.toJSONString(response)); + } + @Override + public void onClosed(EventSource eventSource) { + log.info("对话完成"); + countDownLatch.countDown(); + } + @Override + public void onFailure(EventSource eventSource, @Nullable Throwable t, @Nullable Response response) { + log.error("对话失败", t); + countDownLatch.countDown(); + } + }); + // 等待 + countDownLatch.await(); +} +``` + +## 四、工程源码 + +- 申请ApiKey:[https://open.bigmodel.cn/usercenter/apikeys](https://open.bigmodel.cn/usercenter/apikeys) - 注册申请开通,即可获得 ApiKey +- 运行环境:JDK 1.8+ +- 支持模型:chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro、chatglm_turbo、glm-3-turbo、glm-4、glm-4v、cogview-3 +- maven pom - `已发布到Maven仓库` + +```pom + + cn.bugstack + chatglm-sdk-java + 2.0 + +``` + +- 源码(Github):[https://github.com/fuzhengwei/chatglm-sdk-java](https://github.com/fuzhengwei/chatglm-sdk-java) +- 源码(Gitee):[https://gitee.com/fustack/chatglm-sdk-java](https://gitee.com/fustack/chatglm-sdk-java) +- 源码(Gitcode):[https://gitcode.net/KnowledgePlanet/road-map/chatglm-sdk-java](https://gitcode.net/KnowledgePlanet/road-map/chatglm-sdk-java) \ No newline at end of file diff --git a/docs/md/project/chatgpt/sdk/chatglm-sdk-java.md b/docs/md/project/chatgpt/sdk/chatglm-sdk-java.md new file mode 100644 index 000000000..ddae09e8e --- /dev/null +++ b/docs/md/project/chatgpt/sdk/chatglm-sdk-java.md @@ -0,0 +1,297 @@ +--- +title: 第1节:ChatGLM SDK - 智谱Ai v1 +lock: no +--- + +# 《ChatGPT 微服务应用体系构建》 - chatglm-sdk 第1节:ChatGLM SDK - 智谱Ai + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +清华大学计算机系的超大规模训练模型 ChatGLM-130B 使用效果非常牛,所以我也想把这样的Ai能力接入到自己的应用中或者做一些 IntelliJ IDEA Plugin 使用。但经过了一晚上的折腾,我决定给它写个对接的SDK开源出来!—— 🤔 智谱Ai不是已经有了一个SDK吗?为啥还要写呢?那你写多少了? + +
+ +
+ +在很早之前就关注了智谱Ai(ChatGLM),也看到官网有一个Java对接的[SDK](https://github.com/zhipuai/zhipuai-sdk-java/)方式。但从前几天开始正式对接发现,这SDK是8月份提交的,10个commit,而且已经2个月没有更新了。所以真的是不少Bug呀,呀,呀!如果不去修改它的SDK代码,就没法对接。如;`ConfigV3类中,拆分ApiKey的操作;String[] arrStr = apiSecretKey.split(".");` 但这里的`.`是正则的关键字,所以根本没法拆分。一起动就报错 `invalid apiSecretKey` 这对于初次对接并且没有看源码的伙伴来说,是不小的炸雷。 + +不过,虽然 SDK 有点赶工,不好用。但不影响`智谱Ai(ChatGLM)`是个好东西。他的官网中有API HTTP 接口对接描述。所以,小傅哥决定跟着按照它的文档写一个能简单对接,代码有干净整洁的 SDK 让大家使用。 + +
+ +
+ +那么,接下来小傅哥就介绍下,如何基于`智谱Ai(ChatGLM)`的开发者文档,开发一个通用的SDK组件。也让后续有想法PR贡献源码的伙伴,一起参与进来。—— 别看东西不大,写到简历上,也是非常精彩的一笔! + +>本文不止有智谱Ai-SDK开发,还有如何在项目中运用SDK开发一个自己的OpenAi服务。文末有SDK链接和OpenAi应用工程。 + +## 一、对接鉴权 + +- 文档:[https://open.bigmodel.cn/dev/api](https://open.bigmodel.cn/dev/api) +- ApiKey:[https://open.bigmodel.cn/usercenter/apikeys](https://open.bigmodel.cn/usercenter/apikeys) - `申请个人授权,创建ApiKey即可` + +智谱Ai的Api文档,与ChatGPT对接有一些差异。如果大家对接过ChatGPT开发,直接获取一个ApiKey就可以使用了。但在对接智谱Ai的Api时,需要把获取的ApiKey按照`.`号分割,并需要进行JWT-Token的创建。而这个Token才是实际传给接口的内容。 + +
+ +
+ +- 因为生成Token会比较耗时,所以这里会使用Guava框架进行本地缓存29分钟,有效期30分钟的Token,确保可以有效的刷新。 +- 在工程中提供了 BearerTokenUtils Token 生成工具类,测试的时候可以使用。 + +## 二、接口处理 + +**文档**:[https://open.bigmodel.cn/dev/api#chatglm_lite](https://open.bigmodel.cn/dev/api#chatglm_lite) - 以Api文档的chatglm_lite模型举例对接 + +| 传输方式 | https | +| ------------ | ------------------------------------------------------------ | +| 请求地址 | https://open.bigmodel.cn/api/paas/v3/model-api/chatglm_lite/sse-invoke | +| 调用方式 | SSE | +| 字符编码 | UTF-8 | +| 接口请求头 | accept: text/event-stream | +| 接口请求格式 | JSON | +| 响应格式 | 标准 Event Stream | +| 接口请求类型 | POST | +| 开发语言 | 任意可发起 HTTP 请求的开发语言 | + +在正式开发代码,要把接口的使用先简单测试运行出来。之后再去编写代码。为此这里小傅哥先根据官网的文档和鉴权使用方式,编写了 curl http 请求; + +```java +curl -X POST \ + -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInNpZ25fdHlwZSI6IlNJR04ifQ.eyJhcGlfa2V5IjoiNGUwODdlNDEzNTMwNmVmNGE2NzZmMGNjZTNjZWU1NjAiLCJleHAiOjE2OTY5OTM5ODIzMTQsInRpbWVzdGFtcCI6MTY5Njk5MjE4MjMxNH0.9nxhRXTJcP4Q_YTQ8w5y0CZOBOu0epP1J56oDaYewQ8" \ + -H "Content-Type: application/json" \ + -H "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" \ + -H "Accept: text/event-stream" \ + -d '{ + "top_p": 0.7, + "sseFormat": "data", + "temperature": 0.9, + "incremental": true, + "request_id": "xfg-1696992276607", + "prompt": [ + { + "role": "user", + "content": "写个java冒泡排序" + } + ] + }' \ + http://open.bigmodel.cn/api/paas/v3/model-api/chatglm_lite/sse-invoke +``` + +
+ +
+ +- **注意**:Authorization: Bearer 后面传的是 JWT Token 不是一个直接从官网复制的 ApiKey - `你可以使用工程中的 BearerTokenUtils 创建。` +- 之后可以直接运行这段脚本(也可以导入到ApiPost工具中),执行后就能获得到运行效果了。—— 速度非常快! + +## 三、组件开发 + +在🤔考虑到抽象和设计原则下,小傅哥这里采用了会话模型结构进行工程框架设计。把程序的调用抽象为一次会话,而会话的创建则交给工厂🏭。通过工厂屏蔽使用细节,在使用上简化调用,尽可能让外部最少知道原则。这样的设计实现方式,既可以满足调用方开心的使用,也可以让SDK贡献者见代码如见文档,容易理解和上手。 + +### 1. 工程结构 + +
+ +
+ +- 工程非常注重会话的设计和使用,因为框架的根基搭建好以后,扩展各项功能就会有迹可循。`大部分代码就是因为早期没有考虑好框架,最后功能来了被填充的很乱。` + +### 2. 会话流程 + +
+ +
+ +- 会话流程以工厂创建 Session 为入口点进行使用,其他的操作都在组件内自己处理好。 + +### 3. 代码举例 + +```java +@Override +public OpenAiSession openSession() { + // 1. 日志配置 + HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(); + httpLoggingInterceptor.setLevel(configuration.getLevel()); + + // 2. 开启 Http 客户端 + OkHttpClient okHttpClient = new OkHttpClient + .Builder() + .addInterceptor(httpLoggingInterceptor) + .addInterceptor(new OpenAiHTTPInterceptor(configuration)) + .connectTimeout(configuration.getConnectTimeout(), TimeUnit.SECONDS) + .writeTimeout(configuration.getWriteTimeout(), TimeUnit.SECONDS) + .readTimeout(configuration.getReadTimeout(), TimeUnit.SECONDS) + .build(); + configuration.setOkHttpClient(okHttpClient); + + // 3. 创建 API 服务 + IOpenAiApi openAiApi = new Retrofit.Builder() + .baseUrl(configuration.getApiHost()) + .client(okHttpClient) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) + .addConverterFactory(JacksonConverterFactory.create()) + .build().create(IOpenAiApi.class); + configuration.setOpenAiApi(openAiApi); + return new DefaultOpenAiSession(configuration); +} +``` + +- 这是一段 DefaultOpenAiSessionFactory 创建工厂开启会话的服务对象。使用方只需要在自己的工程中,创建出一个工厂对象就可以对接使用了。**下文有代码示例** +- 其他更多的代码,直接看小傅哥开发好的 chatglm-sdk-java + +## 四、组件使用 + +### 1. 组件配置 + +- 申请ApiKey:[https://open.bigmodel.cn/usercenter/apikeys](https://open.bigmodel.cn/usercenter/apikeys) - 注册申请开通,即可获得 ApiKey +- 运行环境:JDK 1.8+ +- maven pom - `暂时测试阶段,未推送到Maven中央仓库,需要下载代码本地 install 后使用` + +```pom + + cn.bugstack + chatglm-sdk-java + 1.0-SNAPSHOT + +``` + +- 源码(Github):[https://github.com/fuzhengwei/chatglm-sdk-java](https://github.com/fuzhengwei/chatglm-sdk-java) +- 源码(Gitee):[https://gitee.com/fustack/chatglm-sdk-java](https://gitee.com/fustack/chatglm-sdk-java) +- 源码(Gitcode):[https://gitcode.net/KnowledgePlanet/road-map/chatglm-sdk-java](https://gitcode.net/KnowledgePlanet/road-map/chatglm-sdk-java) + +### 2. 单元测试 + +```java +@Slf4j +public class ApiTest { + + private OpenAiSession openAiSession; + + @Before + public void test_OpenAiSessionFactory() { + // 1. 配置文件 + Configuration configuration = new Configuration(); + configuration.setApiHost("https://open.bigmodel.cn/"); + configuration.setApiSecretKey("4e087e4135306ef4a676f0cce3cee560.sgP2*****"); + // 2. 会话工厂 + OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); + // 3. 开启会话 + this.openAiSession = factory.openSession(); + } + + /** + * 流式对话 + */ + @Test + public void test_completions() throws JsonProcessingException, InterruptedException { + // 入参;模型、请求信息 + ChatCompletionRequest request = new ChatCompletionRequest(); + request.setModel(Model.CHATGLM_LITE); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro + request.setPrompt(new ArrayList() { + private static final long serialVersionUID = -7988151926241837899L; + + { + add(ChatCompletionRequest.Prompt.builder() + .role(Role.user.getCode()) + .content("写个java冒泡排序") + .build()); + } + }); + + // 请求 + openAiSession.completions(request, new EventSourceListener() { + @Override + public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) { + ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class); + log.info("测试结果 onEvent:{}", response.getData()); + // type 消息类型,add 增量,finish 结束,error 错误,interrupted 中断 + if (EventType.finish.getCode().equals(type)) { + ChatCompletionResponse.Meta meta = JSON.parseObject(response.getMeta(), ChatCompletionResponse.Meta.class); + log.info("[输出结束] Tokens {}", JSON.toJSONString(meta)); + } + } + + @Override + public void onClosed(EventSource eventSource) { + log.info("对话完成"); + } + }); + + // 等待 + new CountDownLatch(1).await(); + } + +} +``` + +- 这是一个单元测试类,也是最常使用的流式对话模式。 + +## 五、应用接入 + +### 1. SpringBoot 配置类 + +```java +@Configuration +@EnableConfigurationProperties(ChatGLMSDKConfigProperties.class) +public class ChatGLMSDKConfig { + + @Bean + @ConditionalOnProperty(value = "chatglm.sdk.config.enabled", havingValue = "true", matchIfMissing = false) + public OpenAiSession openAiSession(ChatGLMSDKConfigProperties properties) { + // 1. 配置文件 + cn.bugstack.chatglm.session.Configuration configuration = new cn.bugstack.chatglm.session.Configuration(); + configuration.setApiHost(properties.getApiHost()); + configuration.setApiSecretKey(properties.getApiSecretKey()); + + // 2. 会话工厂 + OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); + + // 3. 开启会话 + return factory.openSession(); + } + +} + +@Data +@ConfigurationProperties(prefix = "chatglm.sdk.config", ignoreInvalidFields = true) +public class ChatGLMSDKConfigProperties { + + /** 状态;open = 开启、close 关闭 */ + private boolean enable; + /** 转发地址 */ + private String apiHost; + /** 可以申请 sk-*** */ + private String apiSecretKey; + +} +``` + +```java +@Autowired(required = false) +private OpenAiSession openAiSession; +``` + +- 注意:如果你在服务中配置了关闭启动 ChatGLM SDK 那么注入 openAiSession 为 null + +### 2. yml 配置 + +```pom +# ChatGLM SDK Config +chatglm: + sdk: + config: + # 状态;true = 开启、false 关闭 + enabled: false + # 官网地址 + api-host: https://open.bigmodel.cn/ + # 官网申请 https://open.bigmodel.cn/usercenter/apikeys + api-key: 4e087e4135306ef4a676f0cce3cee560.sgP2DUs***** +``` + +- 你可以在配置文件中,通过 enabled 参数,启动和关闭 ChatGLM SDK + + diff --git "a/docs/md/project/chatgpt/sdk/\347\254\2541\350\212\202\357\274\232ChatGPT-SDK\347\273\204\344\273\266\345\267\245\347\250\213\347\256\200\345\215\225\345\212\237\350\203\275\345\256\236\347\216\260.md" "b/docs/md/project/chatgpt/sdk/\347\254\2541\350\212\202\357\274\232ChatGPT-SDK\347\273\204\344\273\266\345\267\245\347\250\213\347\256\200\345\215\225\345\212\237\350\203\275\345\256\236\347\216\260.md" new file mode 100644 index 000000000..89dd47413 --- /dev/null +++ "b/docs/md/project/chatgpt/sdk/\347\254\2541\350\212\202\357\274\232ChatGPT-SDK\347\273\204\344\273\266\345\267\245\347\250\213\347\256\200\345\215\225\345\212\237\350\203\275\345\256\236\347\216\260.md" @@ -0,0 +1,31 @@ +--- +title: 第1节:ChatGPT-SDK组件工程简单功能实现 +pay: https://t.zsxq.com/0dLgCYNZY +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-sdk 第1节:ChatGPT-SDK组件工程简单功能实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:创建 ChatGPT-SDK 组件工程,并使用 okhttp3 封装对 OpenAI 的请求处理。本章节暂时只实现简单的对话模型,后续逐步添加其他功能。 +- **课程视频**:[https://t.zsxq.com/0d0baxnt3](https://t.zsxq.com/0d0baxnt3) + +## 一、本章诉求 + +搭建一个 ChatGPT-SDK 组件工程,专门用于封装对 OpenAI 接口的使用。由于 OpenAI 接口本身较多,并有各类配置的设置,所以开发一个共用的 SDK 组件,更合适我们在各类工程中扩展使用。所以我们这个章节以 OpenAI 抽象为会话模型,建立工程结构设计。**其实这也是架构设计的一部分**。并在本章的 ChatGPT-SDK 组件工程中,开发简单的对话功能模块实现。 + +## 二、流程设计 + +整个流程为;以会话模型为出口,驱动整个服务的调用链路。并对外提供会话工厂的创建和使用。 + +
+ +
+ +- 如果有小伙伴学习过[《手写MyBatis》](https://t.zsxq.com/0dGck0sdO),那么你一定会 MyBatis 源码中学习到关于会话模型的设计和实现。而类似这样的场景其实在业务流程中非常多,像是本章要实现的封装 OpenAI 其实也是会话模型结构。所以我们可以把 MyBatis 的设计思想融合到 ChatGPT-SDK 实现中。—— 星球中的[《API网关》](https://t.zsxq.com/0diYdgP5u)核心通信模块也是这样的思想。 +- 在本章中,我们通过工厂模型,开启一个使用 okhttp3 封装的 OpenAI 会话服务,进行流程的调用。同时这里还包括请求拦截的处理,因为我们需要对http请求设置一些必要的参数信息,如;ApiKey、Token 等。 +- 这里还需要用到 Retrofit2 组件,Retrofit2 可以将 HTTP API 转化为 Java 接口,并通过注解的方式描述请求参数和响应结果等信息,从而方便地发送网络请求。具体可以的代码对 IOpenAiApi 的赋值实现。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/sdk/\347\254\2542\350\212\202\357\274\232\346\265\201\345\274\217\345\272\224\347\255\224\344\274\232\350\257\235\350\256\276\350\256\241\345\256\236\347\216\260.md" "b/docs/md/project/chatgpt/sdk/\347\254\2542\350\212\202\357\274\232\346\265\201\345\274\217\345\272\224\347\255\224\344\274\232\350\257\235\350\256\276\350\256\241\345\256\236\347\216\260.md" new file mode 100644 index 000000000..ee029c6c5 --- /dev/null +++ "b/docs/md/project/chatgpt/sdk/\347\254\2542\350\212\202\357\274\232\346\265\201\345\274\217\345\272\224\347\255\224\344\274\232\350\257\235\350\256\276\350\256\241\345\256\236\347\216\260.md" @@ -0,0 +1,30 @@ +--- +title: 第2节:流式应答会话设计实现 +pay: https://t.zsxq.com/0eeUHULAb +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-sdk 第2节:流式应答会话设计实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:由会话工厂模型提供,在 OpenAi Session 会话中,封装渐显效果的流式回答处理。 +- **课程视频**:[https://t.zsxq.com/0e7xBakxe](https://t.zsxq.com/0e7xBakxe) + +## 一、本章诉求 + +以 IOpenAiApi 统一接口、OpenAiSession 统一会话,的2个标准下。封装流式应答操作,流式应答操作以事件实现方式接收应答消息。那么这样的实现,就可以在统一的会话工厂中获得会话接口服务以后,根据接口入参的不同做不同的请求处理。对于使用方来说,这样可以减少口口相传和文档的提示,让代码标准成为更好的文档。 + +## 二、流程设计 + +整个流程为;丰富 OpenAiSession 会话服务接口,增加流式回答的事件监听处理。此过程的实现以 MyBatis 的会话模型为参照。 + +
+ +
+ +- 一个需求的实现分为三个部分;架构、设计、代码。架构是骨架、设计方法、代码是填材料。如果没有设计方法的设计模式运用,就相当于把代码的材料,直接扔到架构里。久而久之代码也就越来越混乱了。 +- 所以本章的重点不只是功能的实现,还包括了如何在会话这个流程下,把流式的事件应答处理,巧妙的封装到同一的会话接口内。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/sdk/\347\254\2543\350\212\202\357\274\232\345\256\214\345\226\204\345\256\236\347\216\260\345\220\204\347\261\273\345\270\270\347\224\250\346\216\245\345\217\243.md" "b/docs/md/project/chatgpt/sdk/\347\254\2543\350\212\202\357\274\232\345\256\214\345\226\204\345\256\236\347\216\260\345\220\204\347\261\273\345\270\270\347\224\250\346\216\245\345\217\243.md" new file mode 100644 index 000000000..7edd19f3c --- /dev/null +++ "b/docs/md/project/chatgpt/sdk/\347\254\2543\350\212\202\357\274\232\345\256\214\345\226\204\345\256\236\347\216\260\345\220\204\347\261\273\345\270\270\347\224\250\346\216\245\345\217\243.md" @@ -0,0 +1,30 @@ +--- +title: 第3节:完善实现各类常用接口 +pay: https://t.zsxq.com/0epU6M6qS +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-sdk 第3节:完善实现各类常用接口 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:鉴于在 chatgpt-sdk 的第2节已经通过手把手的方式带着大家做了一整套接口的设计和开发,所以本章结合前面的内容,我们可以继续完善所有常用的接口了。 +- **课程视频**:[https://t.zsxq.com/0eZo0s8q1](https://t.zsxq.com/0eZo0s8q1) + +## 一、本章诉求 + +ChatGPT 官网除了提供了上一章已实现的问答接口,其实还有很多接口内容,也包括通过一段文字描述就可以绘制出图片的操作。以及;`文本修复`、`向量计算`、`文件上传/检索`、`语音转文字`、`语音翻译`、`账单查询`、`消耗查询`这些接口。所以本章我们就来做一下这样的实现。以及完成一个上下文的应答验证。 + +## 二、流程设计 + +整个流程为;按照整体的会话模型设计,封装本章所提诉求的接口内容。 + +
+ +
+ +- 本章的实现会基于官网的HTTP接口进行封装处理,你也可以打开官网查看:[https://platform.openai.com/docs/api-reference](https://platform.openai.com/docs/api-reference) - 官网中对各个例子都有调用的入参和出参,非常清晰。后续也可以结合官网内容做更新和扩展。例如;微调相关的接口也可以先自行扩展。 +- 只要大家把这样一套接口封装的 SDK 做下来,以后你再封装其他各类 API 都有自己的设计思路和落地经验了。 diff --git "a/docs/md/project/chatgpt/sdk/\347\254\2544\350\212\202\357\274\232\346\224\257\346\214\201\345\244\232\346\270\240\351\201\223\345\257\271\350\257\235.md" "b/docs/md/project/chatgpt/sdk/\347\254\2544\350\212\202\357\274\232\346\224\257\346\214\201\345\244\232\346\270\240\351\201\223\345\257\271\350\257\235.md" new file mode 100644 index 000000000..57c637785 --- /dev/null +++ "b/docs/md/project/chatgpt/sdk/\347\254\2544\350\212\202\357\274\232\346\224\257\346\214\201\345\244\232\346\270\240\351\201\223\345\257\271\350\257\235.md" @@ -0,0 +1,207 @@ +--- +title: 第4节:支持多渠道对话 +pay: https://t.zsxq.com/12pbqTH3P +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-sdk 第4节:支持多渠道对话 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:让 ChatGPT SDK 支持多种渠道的对话,因为 ChatGPT 的 API 除了官网以外,还有很多公司有相应的资质,他们会提供对应的 ApiHost、ApiKey,以及 3.5/4.0 模型。 +- **课程视频**:[https://t.zsxq.com/12BVNBcAe](https://t.zsxq.com/12BVNBcAe) + +## 一、本章诉求 + +原本在 ChatGPT SDK 中,我们想调用一个 ChatGPT 的接口,是在 SDK 初始化阶段设置 ApiKey、ApiHost,那么所有的访问用户都会调用到这一套的 ApiKey、ApiHost 上来。但对于一些特殊场景,需要给每个用户分配不同的 ApiKey 甚至可能还有对应的 ApiHost 时,目前的 SDK 就不好扩展了。所以我们要完善这块的功能,让 ChatGPT SDK 支持不同的渠道对话。 + +另外在本节中会使用 @Deprecated 注释掉 `authToken`,后续不在需要使用。如果你的服务设置了 Token 校验,可以继续保留。 + +## 二、流程设计 + +整个流程为;扩展会话入参信息,在 HTTP 客户端中支持动态参数处理; + +
+ +
+ +- 小傅哥会在原有的 SDK 中增加对参数的透传处理,重点涉及对 OpenAiInterceptor 的改造。相当于你要动态的处理 ApiKey 的参数传递。另外 ApiHost 是已经动态的拼接到 URL 重新组合了。 +- 那么现在的 DefaultOpenAiSession#chatCompletions 接口,就有了动态使用 ApiHost、ApiKey 的能力。 + +## 三、方案实现 + +### 1. 工程结构 + +
+ +
+ +- 如图工程中的蓝色部分,为本次需要修改的代码区域。 +- 学习时候可以,以接口 OpenAiSession#chatCompletions 为入口,会看到整个模块的修改变化。 + +### 2. 接口定义 - 扩展一个新接口 + +**源码**:`cn.bugstack.chatgpt.session.OpenAiSession` + +```java +/** + * 问答模型 GPT-3.5/4.0 & 流式反馈 + * + * @param apiHostByUser 自定义host + * @param apiKeyByUser 自定义Key + * @param chatCompletionRequest 请求信息 + * @param eventSourceListener 实现监听;通过监听的 onEvent 方法接收数据 + * @return 应答结果 + */ +EventSource chatCompletions(String apiHostByUser, String apiKeyByUser, ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws JsonProcessingException; +``` + +- 重载一个 chatCompletions 方法,增加 apiHostByUser、apiKeyByUser 两个字段。便于我们可以使用主机需要的 host、key + +### 3. 接口实现 + +**源码**:`cn.bugstack.chatgpt.session.defaults.DefaultOpenAiSession` + +```java +public EventSource chatCompletions(String apiHostByUser, String apiKeyByUser, ChatCompletionRequest chatCompletionRequest, EventSourceListener eventSourceListener) throws JsonProcessingException { + // 核心参数校验;不对用户的传参做更改,只返回错误信息。 + if (!chatCompletionRequest.isStream()) { + throw new RuntimeException("illegal parameter stream is false!"); + } + + // 动态设置 Host、Key,便于用户传递自己的信息 + String apiHost = Constants.NULL.equals(apiHostByUser) ? configuration.getApiHost() : apiHostByUser; + String apiKey = Constants.NULL.equals(apiKeyByUser) ? configuration.getApiKey() : apiKeyByUser; + + // 构建请求信息 + Request request = new Request.Builder() + // url: https://api.openai.com/v1/chat/completions - 通过 IOpenAiApi 配置的 POST 接口,用这样的方式从统一的地方获取配置信息 + .url(apiHost.concat(IOpenAiApi.v1_chat_completions)) + .addHeader("apiKey", apiKey) + // 封装请求参数信息,如果使用了 Fastjson 也可以替换 ObjectMapper 转换对象 + .post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), new ObjectMapper().writeValueAsString(chatCompletionRequest))) + .build(); + + // 返回结果信息;EventSource 对象可以取消应答 + return factory.newEventSource(request, eventSourceListener); +} +``` + +- 接口的实现有2部分,一个是透传 URL 一个是设置 apiKey 写入进去。 +- 之后需要在【自定义的拦截器】中,设置 URL 和 ApiKey。 + +### 4. 自定义拦截器 + +**源码**:`cn.bugstack.chatgpt.interceptor.OpenAiInterceptor` + +```java +public Response intercept(Chain chain) throws IOException { + // 1. 获取原始 Request + Request original = chain.request(); + + // 2. 读取 apiKey;优先使用自己传递的 apiKey + String apiKeyByUser = original.header("apiKey"); + String apiKey = Constants.NULL.equals(apiKeyByUser) ? apiKeyBySystem : apiKeyByUser; + + // 3. 构建 Request + Request request = original.newBuilder() + .url(original.url()) + .header(Header.AUTHORIZATION.getValue(), "Bearer " + apiKey) + .header(Header.CONTENT_TYPE.getValue(), ContentType.JSON.getValue()) + .method(original.method(), original.body()) + .build(); + + // 4. 返回执行结果 + return chain.proceed(request); +} +``` + +- 因为后续已经不在需要 authToken 所以这里的实现已经去掉了这个字段的使用。 +- 通过判断是否设置了自己的 apiKey 来判断使用系统默认的还是用户自己传的,系统默认的就是我们在创建会话的时候初始化的。【这样就可以满足用户可以体验使用 OpenAI 也可以绑定自己的 APIKey 使用】 + +## 四、功能验证 + +### 1. 必要信息 + +1. 官网原始 apiHost https://api.openai.com/ - 官网的Key可直接使用 +2. 三方公司 apiHost https://pro-share-aws-api.zcyai.com/ - 需要找我获得 Key + +### 2. 单元测试 + +```java +@Before +public void test_OpenAiSessionFactory() { + // 1. 配置文件 [联系小傅哥获取key] + // 1.1 官网原始 apiHost https://api.openai.com/ - 官网的Key可直接使用 + // 1.2 三方公司 apiHost https://pro-share-aws-api.zcyai.com/ - 需要找我获得 Key 【支持3.5\4.0流式问答模型调用,有些模型已废弃不对接使用】 + Configuration configuration = new Configuration(); + configuration.setApiHost("https://pro-share-aws-api.zcyai.com/"); + configuration.setApiKey("sk-8Fpeb11ARRIi5JGQCe480fCcF688436a8d9e3c7a6553Af2b"); + + // 2. 会话工厂 + OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); + + // 3. 开启会话 + this.openAiSession = factory.openSession(); +} +``` + +```java +public void test_chat_completions_stream() throws JsonProcessingException, InterruptedException { + // 1. 创建参数 + ChatCompletionRequest chatCompletion = ChatCompletionRequest + .builder() + .stream(true) + .messages(Collections.singletonList(Message.builder().role(Constants.Role.USER).content("1+1").build())) + .model(ChatCompletionRequest.Model.GPT_3_5_TURBO.getCode()) + .maxTokens(1024) + .build(); + + // 2. 用户配置 【可选参数,支持不同渠道的 apiHost、apiKey】- 方便给每个用户都分配了自己的key,用于售卖场景 + String apiHost = "https://pro-share-aws-api.zcyai.com/"; + String apiKey = "sk-UAOxqX3EnM0zXxrz07CbC9E905E74549B4FdCcFcAd6bFbA3"; + + // 3. 发起请求 + EventSource eventSource = openAiSession.chatCompletions(apiHost, apiKey, chatCompletion, new EventSourceListener() { + @Override + public void onEvent(EventSource eventSource, String id, String type, String data) { + log.info("测试结果 id:{} type:{} data:{}", id, type, data); + } + @Override + public void onFailure(EventSource eventSource, Throwable t, Response response) { + log.error("失败 code:{} message:{}", response.code(), response.message()); + } + }); + + // 等待 + new CountDownLatch(1).await(); +} +``` + +- 首先,在测试中 `@Before` 方法下初始化设置了 ApiHost、ApiKey 这个也就是所有的用户都使用的设置。 +- 之后,在 test_chat_completions_stream 测试中,又设置了用户自己的 apiHost、apiKey,那么就可以在调用的时候根据需要自己设置了,也就是用户购买的配置。 + +### 3. 测试结果 + +```java +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:{"id":"chatcmpl-85SMBxN77xL024C4ExM7kaZAl93IV","object":"chat.completion.chunk","created":1696311335,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]} +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:{"id":"chatcmpl-85SMBxN77xL024C4ExM7kaZAl93IV","object":"chat.completion.chunk","created":1696311335,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"content":"1"},"finish_reason":null}]} +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:{"id":"chatcmpl-85SMBxN77xL024C4ExM7kaZAl93IV","object":"chat.completion.chunk","created":1696311335,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"content":"+"},"finish_reason":null}]} +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:{"id":"chatcmpl-85SMBxN77xL024C4ExM7kaZAl93IV","object":"chat.completion.chunk","created":1696311335,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"content":"1"},"finish_reason":null}]} +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:{"id":"chatcmpl-85SMBxN77xL024C4ExM7kaZAl93IV","object":"chat.completion.chunk","created":1696311335,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"content":" equals"},"finish_reason":null}]} +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:{"id":"chatcmpl-85SMBxN77xL024C4ExM7kaZAl93IV","object":"chat.completion.chunk","created":1696311335,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"content":" "},"finish_reason":null}]} +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:{"id":"chatcmpl-85SMBxN77xL024C4ExM7kaZAl93IV","object":"chat.completion.chunk","created":1696311335,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"content":"2"},"finish_reason":null}]} +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:{"id":"chatcmpl-85SMBxN77xL024C4ExM7kaZAl93IV","object":"chat.completion.chunk","created":1696311335,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{"content":"."},"finish_reason":null}]} +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:{"id":"chatcmpl-85SMBxN77xL024C4ExM7kaZAl93IV","object":"chat.completion.chunk","created":1696311335,"model":"gpt-3.5-turbo-0613","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]} +[OkHttp https://pro-share-aws-api.zcyai.com/...] INFO cn.bugstack.chatgpt.test.ApiTest - 测试结果:[DONE] +``` + +- 流式对话返回结果。 + +## 五、读者作业 + +- 简单作业:学习本章的代码,完成功能的开发和测试。 +- 复杂作业:做到现在的章节,以及有 openai.itedus.cn 在前面引路,你也会有了一些想法。那么你完全可以根据这些内容做一些扩展实现,让你的 OpenAI 即可自己使用,也可以让别人使用。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/web/\347\254\25410\350\212\202\357\274\232\345\225\206\345\223\201\346\224\257\344\273\230\351\241\265.md" "b/docs/md/project/chatgpt/web/\347\254\25410\350\212\202\357\274\232\345\225\206\345\223\201\346\224\257\344\273\230\351\241\265.md" new file mode 100644 index 000000000..da8e86d42 --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\25410\350\212\202\357\274\232\345\225\206\345\223\201\346\224\257\344\273\230\351\241\265.md" @@ -0,0 +1,32 @@ +--- +title: 第10节:商品支付页 +pay: https://t.zsxq.com/13AAi8E3c +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第10节:商品支付页 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:跨域处理,流式应答接口 React 对接和数据渐显处理。这里会用到 fetch 调用接口,ReadableStream 处理流式数据。 +- **课程视频**:[https://t.zsxq.com/13W0FNrfO](https://t.zsxq.com/13W0FNrfO) + +## 一、本章诉求 + +在 ChatGPT-API 模块,[第8节:商品下单对接微信支付](https://t.zsxq.com/12jRaQCvC) 已经完成了整个商品流程的开发,并提供管理对应的HTTP接口。包括;商品查询、商品下单以及回调处理。 + +那么本章节我们则需要开发对应的前端页面,把后端要下单的效果展示在前端页面上。这部分 react 开发内容并不多。大家可以很轻松的搞定。 + +## 二、目标效果 + +本章节我们在侧边栏加一个商城的图标,并开发对应的商品展示页面。让用户可以自己选择所需的商品进行下单支付。 + +
+ +
+ +- 这里我们可以把数据库配置的商品数据,通过服务端接口展示到前端页面上。并提供了对应的点击购买按钮,创建订单返回支付单号。再通过 React 提供好的 QRCode 把支付URL转二维码,让用户可以扫码支付。 +- 这个就是我们本节小傅哥要带着你做出来的目标效果,接下来我们就看下代码是如何实现的。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/web/\347\254\2541\350\212\202\357\274\232Web\351\241\265\351\235\242\345\267\245\347\250\213\345\210\235\345\247\213\345\214\226.md" "b/docs/md/project/chatgpt/web/\347\254\2541\350\212\202\357\274\232Web\351\241\265\351\235\242\345\267\245\347\250\213\345\210\235\345\247\213\345\214\226.md" new file mode 100644 index 000000000..87ca482af --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\2541\350\212\202\357\274\232Web\351\241\265\351\235\242\345\267\245\347\250\213\345\210\235\345\247\213\345\214\226.md" @@ -0,0 +1,51 @@ +--- +title: 第1节:Web页面工程初始化 +pay: https://t.zsxq.com/0eD8hVmSU +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第1节:Web页面工程初始化 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★☆☆☆☆ +- **本章重点**:通过 WebStorm 开发工具,创建 Next.js TypeScript Web 工程。简单介绍工程框架和代码模块职责,以及做简单的框体内容代码开发。 +- **课程视频**:[https://t.zsxq.com/0eceIlPgs](https://t.zsxq.com/0eceIlPgs) + +## 一、本章诉求 + +对于后端开发人员来说,在进入职场以后,因为工作职责的划分,其实就很少做前端的开发内容了。而这些年前端的编程语言也发生了特别多的变化,早已经不是 JQuery 那个年代的代码风格,而是都以面向对象的方式进行开发,再通过编译构建的生成 HTML 代码的方式进行实现。 + +而本章作为 ChatGPT 课程的一部分,是会涉及到前端的页面开发。这对很多后端开发伙伴来说,可能会有一些压力。所以如果你是为了增加这方面技能,或者是为了以后自己做一些东西的时候可以写下页面,那么可以跟着小傅哥一起学习。当然小傅哥在写前端的东西上也只是个`二把刀`,但好在我能写的东西,也基本都够用。另外如果你只是为了工作需求,其实不学习这部分内容也没关系。你可以直接使用一些开源的页面框架,并指导在哪些地方添加、修改、补充即可。 + +## 二、环境诉求 + +- Node.js v16.14.2 - [https://nodejs.org/zh-cn](https://nodejs.org/zh-cn) - 下载安装即可 +- WebStorm 2023.1 - `因为自带了开发前端的工具,所以非常好用。` +- 源地址设置;`npm config set registry https://registry.npm.taobao.org` - 安装好环境后,设置下源地址。否则在后面构建项目,下载文件时候会卡出xiang +- TypeScript:[https://www.runoob.com/typescript/ts-tutorial.html](https://www.runoob.com/typescript/ts-tutorial.html) - 课程资料,简单的菜鸟入门教程。学习之后也能看懂 TypeScript 代码。所有的面向对象语言代码,基本是通用的。 + +
+ +
+ +**注意**:如果你自己没有太多的 HTML、JS、Div+CSS 的编写经验,也可以直接使用小傅哥提供的非常简单的页面,只要你简单改动即可对后端接口调用,完成的 ChatGPT 的响应。源码:[https://gitcode.net/KnowledgePlanet/chatgpt/chatgpt-web/-/tree/230527-xfg-init/docs/html](https://gitcode.net/KnowledgePlanet/chatgpt/chatgpt-web/-/tree/230527-xfg-init/docs/html) + +## 三、工程结构 + +整个工程由 WebStorm 的 Next.js 模板配置创建,创建后会在控制台回车选择执行安装即可。- `注意选择的是含有 src 文件夹的工程结构` + +
+ +
+ +
+ +
+ +- 创建完成后工程会自动的构建,构建完成除了上图中绿色新增部分外,都是工程默认提供好的。 +- package.json 是一个配置类,可以用于启动工程测试,以及这里会把工程所需的模块加载进来。 +- page.tsx、layout.tsx 是这个工程的入口页面,指定了首页和要加载的页面。 +- home.tsx 是小傅哥新增加的一个窗体页面,其他几个绿色文件是为了配合 home.tsx 而存在的 css 文件。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/web/\347\254\2542\350\212\202\357\274\232\345\267\245\345\205\267\346\240\217\351\235\242\346\235\277.md" "b/docs/md/project/chatgpt/web/\347\254\2542\350\212\202\357\274\232\345\267\245\345\205\267\346\240\217\351\235\242\346\235\277.md" new file mode 100644 index 000000000..eb5447b89 --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\2542\350\212\202\357\274\232\345\267\245\345\205\267\346\240\217\351\235\242\346\235\277.md" @@ -0,0 +1,32 @@ +--- +title: 第2节:工具栏面板 +pay: https://t.zsxq.com/0e0jyBF1N +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第2节:工具栏面板 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★☆☆☆☆ +- **本章重点**:设计 ChatGPT WEB 工具栏界面,并通过 typescript 所提供的方法,进行界面操作地址路由。并章节开始会逐步的拆解和完成聊天对话界面的设计和实现,这里会涉及不少的 Typescript 语言特性,以面向对象的方式进行编程设计和实现。 +- **课程视频**:[https://t.zsxq.com/0eZuOvR59](https://t.zsxq.com/0eZuOvR59) + +## 一、本章诉求 + +整个 WEB 界面所需要实现的功能还是比较多的,这里小傅哥会以以往 Swing、JavaFx、HTML 等各类型的界面开发经验进行设计和实现,同时融入面向对象的编程思想,来开发完成整个 ChatGPT Web UI 的工程代码。 + +那么本章节我们先以一个小的侧边栏设计为起始,进行页面的构建和代码实现。这部分内容并不是很多,但侧边栏会牵动着整个页面结构的定义和界面的路由处理。接下来我们就来实现下。 + +## 二、目标效果 + +整个界面小傅哥会以PC端微信的结构作为参考,设计符合我们诉求的UI页面。本章节我们会完成如下图所示的内容。 + +
+ +
+ +- 在侧边栏设计2个按钮,一个聊天,一个角色。聊天用于处理对话,角色用于处理各类场景的选择,包括你可以是【面试官角色、文案写手角色、法务咨询角色等】 +- 通过页面点击2个按钮,右侧的面板部分发生变化。其实这个变化就是在加载不同的 div 模块,以达到切换界面的效果。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/web/\347\254\2543\350\212\202\357\274\232\346\214\211\351\222\256\345\256\232\344\271\211\344\270\216\344\272\213\344\273\266\345\256\236\347\216\260.md" "b/docs/md/project/chatgpt/web/\347\254\2543\350\212\202\357\274\232\346\214\211\351\222\256\345\256\232\344\271\211\344\270\216\344\272\213\344\273\266\345\256\236\347\216\260.md" new file mode 100644 index 000000000..0b07eacf6 --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\2543\350\212\202\357\274\232\346\214\211\351\222\256\345\256\232\344\271\211\344\270\216\344\272\213\344\273\266\345\256\236\347\216\260.md" @@ -0,0 +1,32 @@ +--- +title: 第3节:按钮定义与事件实现 +pay: https://t.zsxq.com/0fAo8tnHc +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第3节:按钮定义与事件实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★☆☆☆ +- **本章重点**:通过引入按钮功能,实现页面的放大/缩小效果。因为有按钮的引入也会逐步添加一些基本的共用功能为后续的章节模块实现时进行使用。 +- **课程视频**:[https://t.zsxq.com/0ftIpvAXH](https://t.zsxq.com/0ftIpvAXH) + +## 一、本章诉求 + +做编程开发通常我们需要结果为导向,以终为始。知道自己的目标,再朝着目标不断的积累所需的物料能量。那么在本章中我们会以实现一个用于`放大/缩小`页面的按钮设计和实现,来完善整体 web-ui 所需基础设施模块。 + +可能在当下章节你会觉得实现功能并不大,代码量也不多。但要在前期这个学习阶段打好基础,因为所有前面所学习到的组件、模块、代码、设计,在后续都会被串联起来使用。所以前面一定打好基础,来保证后续的学习质量。 + +## 二、目标效果 + +虽然就是实现一个`按钮`看似功能很小,但因为引入一系列按钮,所以定义了通用按钮设计以及定义配置存储。当操作按钮时候可以从配置中设置和使用,这样整个界面就放大和缩小了。 + +
+ +
+ +- 按钮是整个页面的一个小功能点,通过小功能点来引出 web-ui 整体架构中的各个模块实现。包括这里的配置存储,就是为后续整个配置设计的。 +- 对于这样功能点的实现,会涉及到;按钮定义、CSS设计、TypeScript语法 `zustand#create、zustand/middleware#persist` 等。可能有有些伙伴对前端积累不多,可以对于各类小的细节点在 ChatGPT 中提问获取答案。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/web/\347\254\2544\350\212\202\357\274\232\345\257\271\350\257\235\346\241\206\345\210\227\350\241\250.md" "b/docs/md/project/chatgpt/web/\347\254\2544\350\212\202\357\274\232\345\257\271\350\257\235\346\241\206\345\210\227\350\241\250.md" new file mode 100644 index 000000000..7afedc42f --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\2544\350\212\202\357\274\232\345\257\271\350\257\235\346\241\206\345\210\227\350\241\250.md" @@ -0,0 +1,32 @@ +--- +title: 第4节:对话框列表 +pay: https://t.zsxq.com/0fdWbDTHS +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第4节:对话框列表 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:重构pages页面也components组件分层,添加 dialog 对话模块,实现对话框列表。并添加对应的测试数据以及在点击`按钮+`的时候,创建新会的会话。 +- **课程视频**:[https://t.zsxq.com/0fv9D4yVK](https://t.zsxq.com/0fv9D4yVK) + +## 一、本章诉求 + +在侧边栏已实现完成的基础上,开发对话框列表窗体。当我们与 ChatGPT 进行对话时,可以有不同的角色出现,包括;普通对话、面试官角色、心里咨询师角色等,那么这些窗体就需要就需要以对话列表的形式在对话框列表中。你可以想象成在微信中,你与不同好友的对话列表的展示形式。 + +在学习本章节之前,你可以对照着微信界面先思考下🤔。实现这部分应该需要哪些信息,比如;与谁在聊天、聊天了多少条、什么时候结束的最后一次对话等,并怎么把这些信息展示到界面上。 + +## 二、目标效果 + +在填表上对话列表后,我们的整个界面漂亮了不少; + +
+ +
+ +- 首先,为了更好的扩展整个页面中的内容实现。小傅哥建了一个 `pages` 包,把以前混在 `components` 中的组件和页面拆分开,这样可以更好的扩展后续的实现内容。 +- 之后,整个对话分为;`1:对话列表`、`2:列表元素`以及后续需要实现的`3:对话窗体`和`4:输入框`。不过我们本章节先来实现前面2部分。以 `dialog-*` 为首的全部都是实现对话列表的内容。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/web/\347\254\2545\350\212\202\357\274\232\345\257\271\350\257\235\346\241\206\346\266\210\346\201\257.md" "b/docs/md/project/chatgpt/web/\347\254\2545\350\212\202\357\274\232\345\257\271\350\257\235\346\241\206\346\266\210\346\201\257.md" new file mode 100644 index 000000000..19336e8a6 --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\2545\350\212\202\357\274\232\345\257\271\350\257\235\346\241\206\346\266\210\346\201\257.md" @@ -0,0 +1,32 @@ +--- +title: 第5节:对话框消息 +pay: https://t.zsxq.com/0fEQfXMYf +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第5节:对话框消息 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:围绕dialog对话消息实现为主,实现对应的;子路由设置、页面跳转、消息透传、界面设计和实现。 +- **课程视频**:[https://t.zsxq.com/0f0rqVfX8](https://t.zsxq.com/0f0rqVfX8) + +## 一、本章诉求 + +在已经有了对话框列表以后,我们需要实现对话框消息面板的内容。也就是当我们点击对话框的列表元素以后,可以对应在右侧有一个对话框消息,同时再增加一个消息的输入。当然目前所有的这些操作仍不会与服务端有关联,都是在前端进行操作处理。 + +在学习本章节之前,你可以先思考下你的对话框消息是怎么和对话框元素关联上的,怎么在点击的时候就能切换到对应的聊天对话框消息上去。以目标结果为导向,带着问题驱动学习会更加明确的让你知道自己要完成什么和学习什么。 + +## 二、目标效果 + +在有了对话框消息的体现后,整个页面又完善了不少; + +
+ +
+ +- 在测试验证的时候,你可以点击绿色的按钮进行创建对话。每一个对话会有一个对应的 ID 这个 ID 来串联对话框消息。当点击各个对话列表的元素,如【心里咨询、面试官】那么就会切换到对应的对话消息上。 +- 右侧工程部分,以`dialog`文件下内容为主,用来实现整个对话框消息部分。同时因为这里有页面的切换,所以在 `home.tsx`、`chat.tsx` 也有对应少量代码的改动。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/web/\347\254\2546\350\212\202\357\274\232\345\256\214\345\226\204\345\257\271\350\257\235\345\244\204\347\220\206.md" "b/docs/md/project/chatgpt/web/\347\254\2546\350\212\202\357\274\232\345\256\214\345\226\204\345\257\271\350\257\235\345\244\204\347\220\206.md" new file mode 100644 index 000000000..7d00d3e54 --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\2546\350\212\202\357\274\232\345\256\214\345\226\204\345\257\271\350\257\235\345\244\204\347\220\206.md" @@ -0,0 +1,41 @@ +--- +title: 第6节:完善对话处理 +pay: https://t.zsxq.com/0fINRUas5 +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第6节:完善对话处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★★☆ +- **本章重点**:存储对话消息对浏览器,减少对服务器的调用。同时扩展 Markdown 的使用以及输入框工具栏的完善,让功能更加完善,UI更加方便使用。 +- **课程视频**:[https://t.zsxq.com/0fZ3VeqmZ](https://t.zsxq.com/0fZ3VeqmZ) + +## 一、本章诉求 + +本章是结合上一章对话框消息的扩展处理。因为这里需要做几个事情,才能让整个对话更”温柔“。而这个温柔包括以下内容; + +
+ +
+ +1. 对话框列表存储到浏览器中 - 这样才能保存用户的对话记录。这样的信息也可以从服务端接口获取。不过这样的方式势必会让服务端的压力更大,尤其是我们这样的非公司的用户想部署一套这样的服务,则希望更少的依赖服务端,让使用方提供的浏览器资源存储,则是非常好的选择。 +2. 同理,我们也把消息信息存储到用户的浏览器端,不必要都放到服务端存储。当然后续也可以扩展为使用服务端存储。 +3. 之后是消息的发送和展现,因为本身 ChatGPT 所返回的数据都是 MD 格式,尤其是代码部分,如果不进行渲染就会非常难看。所以我们还需要扩展 markdown 的方式进行展示。 +4. 此外,我们知道 ChatGPT 提供了一些必备的参数,这些参数可以放到输入框上面方便使用。 +5. 最后,比较多的内容就是UI继续完善,让整个界面更加漂亮。 + +## 二、目标效果 + +本章为实现温柔效果的诉求大家知道了,接下来我们先看最终的效果; + +
+ +
+ +- 为了实现本章内容,扩展了 chat-store.ts 对,对话列表和对话信息的存储能力。那么用户所有的操作,都会被存放到这里。 +- 之后扩展了 markdown.tsx 文件,用于处理 md 的信息,同时也新增了输入框👆🏻上面的控制栏,方便可以最快的处理控制上线文以及模型的选择。最后这些参数都会被一起传到后端接口的接口里。 +- 本章的代码量会稍微有些多,大家可以对比上一章代码一点点扩展实现。 diff --git "a/docs/md/project/chatgpt/web/\347\254\2547\350\212\202\357\274\232\345\257\271\350\257\235\350\247\222\350\211\262\350\256\276\345\256\232.md" "b/docs/md/project/chatgpt/web/\347\254\2547\350\212\202\357\274\232\345\257\271\350\257\235\350\247\222\350\211\262\350\256\276\345\256\232.md" new file mode 100644 index 000000000..0a10e0dc0 --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\2547\350\212\202\357\274\232\345\257\271\350\257\235\350\247\222\350\211\262\350\256\276\345\256\232.md" @@ -0,0 +1,37 @@ +--- +title: 第7节:对话角色设定 +pay: https://t.zsxq.com/0fwAKbbPB +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第7节:对话角色设定 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:存储对话消息对浏览器,减少对服务器的调用。同时扩展 Markdown 的使用以及输入框工具栏的完善,让功能更加完善,UI更加方便使用。 +- **课程视频**:[https://t.zsxq.com/0f87ipWGC](https://t.zsxq.com/0f87ipWGC) + +## 一、本章诉求 + +使用过 ChatGPT 的小伙伴都知道,它可以预设角色,以某种特定的身份与你对话。比如;Java面试官、文案写手、心灵导师,或者是 Linux 服务器等等。所以我们本章节就把这些角色当成是自己的好友,预设到程序中。当点击每个角色的时候,就开启与某个角色的对话操作。 + +
+ +
+ +- 这就是本章节要实现的效果,当点击角色按钮的时候,进入到你的角色列表。每个角色会介绍自己的功能,当点击对话的时候,会创建一个聊天信息到你的对话框中。 +- 接下来我们就来实现下这个功能。截止到本章节小傅哥已经带着小伙伴做了很多的 React 的编码,大家也可以尝试自己实现下。 + +## 二、目标效果 + +本章要实现的结果大家知道了,接下来我们先来看看最终的效果; + +
+ +
+ +- 本章首先需要扩展 app/components/role 角色模块,角色的实现包括;一块角色面板list,以及对应每个角色的介绍和控制开始对话。 +- 之后是把角色的实现,填充到 pages/role/role.tsx 下面。而这个页面的改动还会涉及到 page/home/home.tsx 的点击处理。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/web/\347\254\2548\350\212\202\357\274\232\346\265\201\345\274\217\346\216\245\345\217\243\345\257\271\346\216\245.md" "b/docs/md/project/chatgpt/web/\347\254\2548\350\212\202\357\274\232\346\265\201\345\274\217\346\216\245\345\217\243\345\257\271\346\216\245.md" new file mode 100644 index 000000000..39665e906 --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\2548\350\212\202\357\274\232\346\265\201\345\274\217\346\216\245\345\217\243\345\257\271\346\216\245.md" @@ -0,0 +1,36 @@ +--- +title: 第8节:流式接口对接 +pay: https://t.zsxq.com/10eihD9X3 +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第8节:流式接口对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:跨域处理,流式应答接口 React 对接和数据渐显处理。这里会用到 fetch 调用接口,ReadableStream 处理流式数据。 +- **课程视频**:[https://t.zsxq.com/10tgT0KVe](https://t.zsxq.com/10tgT0KVe) + +## 一、本章诉求 + +在 ChatGPT-API 工程模块下,我们的流式异步响应接口已经开发并测试完成,那么到 ChatGPT-WEB 这里,我们就可以对接上接口,让前端可以以打字机的效果展示出我们与 ChatGPT 的对话内容了。 + +
+ +
+ +- 首先,需要跨域对接后端接口,将发送时的请求信息传递给后端。 +- 之后,接收到后端的应答结果,在依次填充到我们的消息记录里。 + +## 二、目标效果 + +本章节所体现的核心内容则为 react fetch 接口的使用和 ReadableStream 流式填充数据到展示对话框中,直至最终如下效果; + +
+ +
+ +- 那么小傅哥接下来就带着大家一起实现下。看看跨域接口怎么对接、fetch 怎么使用、ReadableStream 流式数据怎么填充。 \ No newline at end of file diff --git "a/docs/md/project/chatgpt/web/\347\254\2549\350\212\202\357\274\232\345\205\254\344\274\227\345\217\267\346\211\253\347\240\201\347\231\273\345\275\225.md" "b/docs/md/project/chatgpt/web/\347\254\2549\350\212\202\357\274\232\345\205\254\344\274\227\345\217\267\346\211\253\347\240\201\347\231\273\345\275\225.md" new file mode 100644 index 000000000..e93ebc583 --- /dev/null +++ "b/docs/md/project/chatgpt/web/\347\254\2549\350\212\202\357\274\232\345\205\254\344\274\227\345\217\267\346\211\253\347\240\201\347\231\273\345\275\225.md" @@ -0,0 +1,35 @@ +--- +title: 第9节:公众号扫码登录 +pay: https://t.zsxq.com/11oLqb285 +--- + +# 《ChatGPT 微服务应用体系构建》 - chatgpt-web 第9节:公众号扫码登录 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★★★☆☆ +- **本章重点**:跨域处理,流式应答接口 React 对接和数据渐显处理。这里会用到 fetch 调用接口,ReadableStream 处理流式数据。 +- **课程视频**:[https://t.zsxq.com/11YKNcb5w](https://t.zsxq.com/11YKNcb5w) + +## 一、本章诉求 + +当你自身的角色发生改变的时候,从使用产品的用户视角到提供产品服务的老板视角,你会有不同的感受。尤其是你提供一款产品的时候,如果没有登录,随便的用,用完就走。其实很难沉淀下来用户,那么做的产品最终也难有价值体现。 + +
+ +
+ +所以本章我们以微信公众号扫码输入验证码为例,做一个登录操作,把用户沉淀到自己所选择的平台中。按照你个人的诉求,可以是手机号注册、邮箱注册、Github注册等等。 + +## 二、目标效果 + +本章所体现的核心内容为开发一个登录页面,并在对话信息中的必要位置进行权限拦截校验。之后对接 chatgpt-data 所提供的登录授权接口。 + +
+ +
+ +- 那么这里小傅哥是把授权的登录操作与公众号结合,首先通过公众号进行验证码获取,之后填写登录就可以使用服务了。 diff --git "a/docs/md/project/chatgpt/\345\274\225\350\250\200.md" "b/docs/md/project/chatgpt/\345\274\225\350\250\200.md" new file mode 100644 index 000000000..49013a3f4 --- /dev/null +++ "b/docs/md/project/chatgpt/\345\274\225\350\250\200.md" @@ -0,0 +1,31 @@ +--- +title: 引言:开篇介绍 +pay: https://t.zsxq.com/0d7K7hJ0i +--- + +# 《ChatGPT 微服务应用体系构建》引言:学习指引 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- **本章难度**:★☆☆☆☆ +- **本章重点**:开篇介绍课程信息以及如何进入学习。后续也会把一些共性的学习问题在这里说明。 +- **课程视频**:[https://t.zsxq.com/0dYzxHlZv](https://t.zsxq.com/0dYzxHlZv) + +## 一、产品形态 + +这趟车🚌,本身的核心是关于**微服务应用体系的构建**,通过讲解配置`Docker`、`Nginx`、`SSL`等环境以及开发出`鉴权`、`认证`、`微信公众号`、`企业微信`、`支付宝交易`等模块的方式,完善体系的物料服务。而 ChatGPT 只是其中的一种产品形态而已,这种产品形态通过 API 的方式与具体的物料服务模块解耦。这样做的方式是因为基础的物料`【物料指SDK和服务】`并不会频繁变化,而离业务最近的 API 会随业务变动发生较多的改动。所以这样的应用架构方式,在互联网大厂中也是非常常见和常用的。 + +这些东西的价值在于架构思维,而我也希望授人以渔,教会大家一些根本的东西,而不是永远的在CV+CRUD。有了这样的学习,学习的就不只是这样一个项目,而是可以把这个项目中所涉及的组件开发,都能进行任意物料模块与需要对接的服务进行关联打通使用。方便`写到简历`、`用到项目`、`实战锻炼`、`积累经验`。 + +## 二、拓扑结构 + +接下来我们再以工程拓扑的视角看下这套需要开发的系统; + +
+ +
+ +如拓扑结构,系统从上到下以不同的产品形态,统一调用封装的服务API进行功能的流转。API系统中所处理的核心动作,会以各个物料模块进行实现。所以这里会拆分出标准的 ChatGPT-API 业务系统,之后再由各个模块系统支撑。到具体的模块中再进行详细的系统设计。 diff --git a/docs/md/project/ddd-scene-solution/alipay-sandbox.md b/docs/md/project/ddd-scene-solution/alipay-sandbox.md new file mode 100644 index 000000000..7f5be2e60 --- /dev/null +++ b/docs/md/project/ddd-scene-solution/alipay-sandbox.md @@ -0,0 +1,594 @@ +--- +title: AliPay 商品下单支付场景 +lock: need +--- + +# 【小场景训练营】商品下单支付场景,DDD设计实现「支付宝沙箱」 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +哈喽,大家伙我是技术UP主小傅哥。 + +经历了半年多💐,**《DDD 技术小册》** 整个系列已完成`理论知识`与`技术实践`两个部分,接下来小傅哥将带着小伙伴们开启`DDD 技术小册之场景方案`的学习旅程。—— 学理论、练技术、找场景锻炼,综合的学习会快速的提高编程技术。 + +
+ +
+ +技术,是承接业务需求提供解决方案的综合运用,而从实际需求中摘取出来的小场景学习,包括;支付、短信、地图、人脸、规则、短信猫、三方登录等,都可以非常好的锻炼思维,提高编码能力。 + +那么,本节我们先来完成下支付场景在 DDD 领域驱动下的场景设计和编码实现。这里选用支付宝的沙箱支付,因为这个支付对接非常好申请,这样可以方便大家一起参与学习。 + +>文末提供了「星球:码农会锁」优惠加入学习方式,不仅可以学习碎片化的即使知识,还可以综合锻炼实战项目。项目演示地址:[https://gaga.plus](https://gaga.plus) + +## 一、场景说明 + +在本节小傅哥会带着大家先以最简单的方式完成对接支付验证。再以商品下单模型,通过 DDD 的领域驱动设计,最终完成对`支付宝沙箱`的对接使用。【如图】 + +
+ +
+ +- 包括;商城、出行、外卖等场景,凡是对接支付,都会先创建一条订单。在基于订单的唯一ID创建支付单。这是因为调用的支付都是外部的接口提供,没法和自己流程做一个统一的事务。那么这里就需要考虑最终一致性问题,要幂等可重试。—— 所以有这样的流程,调单也可以补偿。 +- 商品支付完成后,会收到支付完成的回调。回调调用你提供好的一个接口,用于接收支付完成的通知。这个通知用于变更支付单的状态,以及自己系统内再发一个异步的MQ消息,用于处理后续的流程。 +- 所有的这些流程节点,支付掉单、回调失败、MQ发送失败,在各个流程节点都有唯一ID,所以都可以用定时任务补偿。 + +## 二、支付申请 - Alipay 沙箱支付 + +### 1. 沙箱应用 + +**支付宝|开放平台** 地址:[https://open.alipay.com/develop/manage](https://open.alipay.com/develop/manage) - `任何人都可以申请,不要企业资质` + +
+ +
+ +
+ +
+- 开启沙箱应用以后,还需要下载支付宝秘钥工具。秘钥工具创建的秘钥,需要填写到查看中。 +- 📢 【沙箱账号】,里面提供了后续在网页上支付时,输入的账号、密码和支付密码。 +- 📢 【沙箱工具】,里面提供了安卓版测试软件,可以在手机扫码支付。 + +### 2. 秘钥工具 + +**文档中心 | 开放平台** 地址:[https://opendocs.alipay.com/common/02kipk](https://opendocs.alipay.com/common/02kipk) - `下载支付宝开放平台秘钥工具` 在文档的介绍中,也有很详细的说明。 + +
+ +
+ +
+ +
+ +
+ +
+ +### 3. 秘钥填写 + +
+ +
+ +
+ +
+ +- 在秘钥工具中生成的**应用公钥**,复制到如图中填写保存。填写后就可以看到对应的支付宝公钥了。 + +## 三、简单案例 + +有了前面这些步骤的申请操作,接下来我们就可以先做一个小支付案例了。这个支付案例在工程代码中 xfg-dev-tech-app ApiTest 下。 + +### 1. 后端代码 + +**源码**:`cn.bugstack.xfg.dev.tech.test.ApiTest` + +```java +@Slf4j +public class ApiTest { + + // 「沙箱环境」应用ID - 您的APPID,收款账号既是你的APPID对应支付宝账号。获取地址;https://open.alipay.com/develop/sandbox/app + public static String app_id = "9021000132689924"; + // 「沙箱环境」商户私钥,你的PKCS8格式RSA2私钥 + public static String merchant_private_key = "上文中,【秘钥工具】所创建的公户私钥"; + // 「沙箱环境」支付宝公钥 + public static String alipay_public_key = "上文中,【秘钥填写】后提供给你的支付宝公钥"; + // 「沙箱环境」服务器异步通知页面路径。这里小傅哥用了 natapp.cn 内网穿透工具 + public static String notify_url = "https://xfg.natapp.cn/api/v1/alipay/alipay_notify_url"; + // 「沙箱环境」页面跳转同步通知页面路径 需http://格式的完整路径,必须外网可以正常访问,才会同步跳转 + public static String return_url = "https://gaga.plus"; + // 「沙箱环境」 + public static String gatewayUrl = "https://openapi-sandbox.dl.alipaydev.com/gateway.do"; + // 签名方式 + public static String sign_type = "RSA2"; + // 字符编码格式 + public static String charset = "utf-8"; + + @Test + public void test_AliPay() throws AlipayApiException { + AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl, + app_id, + merchant_private_key, + "json", + charset, + alipay_public_key, + sign_type); + + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); // 发送请求的 Request类 + request.setNotifyUrl(notify_url); + request.setReturnUrl(return_url); + + JSONObject bizContent = new JSONObject(); + bizContent.put("out_trade_no", "daniel82AAAA000032333361X02"); // 我们自己生成的订单编号 + bizContent.put("total_amount", "0.01"); // 订单的总金额 + bizContent.put("subject", "测试商品"); // 支付的名称 + bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); // 固定配置 + request.setBizContent(bizContent.toString()); + + String form = alipayClient.pageExecute(request).getBody(); + log.info("测试结果:{}", form); + +} +``` + +**支付配置**: + +- app_id:应用ID - 您的APPID,收款账号既是你的APPID对应支付宝账号。获取地址;[https://open.alipay.com/develop/sandbox/app](https://open.alipay.com/develop/sandbox/app) +- merchant_private_key:商户私钥,【通过支付宝开放平台秘钥工具】创建出来的私钥。公钥填写到网页上,私钥程序里使用。 +- alipay_public_key:支付宝公钥,在网页上填写公钥后,会给你一个支付宝的公钥。 +- notify_url:服务器异步通知回调地址,也就是你支付完成后,支付宝调用你的地址。因为我们是在本地做测试,外网是访问不到的。所以为了能做这样的测试,可以回调到我们。那么这里需要使用 natapp.cn 做一个内网穿透。 +- return_url:支付完成后跳转的地址 +- gatewayUrl:支付宝沙箱环境的地址,固定的。 +- sign_type:签名方式固定的 +- charset:字符编码固定的 + +**调用配置** + +- out_trade_no:你的单号,用你的单号类生成支付单信息。 +- total_amount:支付金额 +- subject:商品名称 +- product_code:固定值;FAST_INSTANT_TRADE_PAY + +**测试结果** + +
+ +
+ +```java +
+ + +
+ +``` + +- 运行后你会得到一份 HTML 脚本,这份脚本就可以在浏览器打开。 +- 如果你想少量的存储信息,也可以把必要的数据存到数据库,不用全存储。 + +### 2. 前端代码 + +
+ +
+ +
+ +
+ +- 把后端运行生成的代码,粘贴到 form.html 文件中,之后点浏览器按钮运行打开。 +- 打开后,会跳转到支付宝支付页面,之后你可以从【支付宝 | 开放平台】沙箱账号中获取你的账号信息。地址:[https://open.alipay.com/develop/sandbox/account](https://open.alipay.com/develop/sandbox/account) + +>到这支付宝的沙箱申请和简单案例已经做完了,之后我们进入场景应用的训练。 + +## 四、场景介绍 + +支付,可以说是一个对接的渠道,那么结合到我们的实际场景中应该如何使用呢?比如流程的执行、订单的创建、数据的保存、回调的处理等,这些都是实际业务中要做的东西,接下来我们就做下这个场景学习。 + +### 1. 支付界面 + +
+ +
+ +### 2. 业务流程 + +
+ +
+ +### 3. 库表设计 + +
+ +
+ +## 五、工程实现 + +
+ +
+ +此项目为「星球:码农会锁」小场景训练营的一部分 —— 地址:[https://gitcode.net/KnowledgePlanet/ddd-scene-solution](https://gitcode.net/KnowledgePlanet/ddd-scene-solution) - `星球内还包括了大量的实战项目;大营销平台、OpenAI项目、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等` + +### 1. 工程结构 + +
+ +
+ +分层介绍: + +- docs:这里提供了工程所需的资源,docker 安装 msyql、natapp 内网穿透【需要自己申请一个地址】、front 前端页面。 +- app:应用的启动层,处理 config 配置和 resources 资源加载。以及 test 模块的测试。 +- domain:领域层,处理核心业务逻辑。 +- infrastructure:基础设施层,如管理持久化数据。 +- trigger:触发器层,统一管理 http、mq、listener、job 等入口调用。 + +### 2. 环境安装 + +#### 2.1 MySQL + +```java +# 命令执行 docker-compose up -d +version: '3.9' +services: + mysql: + image: mysql:8.0.32 + container_name: mysql + command: --default-authentication-plugin=mysql_native_password + restart: always + environment: + TZ: Asia/Shanghai + MYSQL_ROOT_PASSWORD: 123456 + MYSQL_USER: xfg + MYSQL_PASSWORD: 123456 + depends_on: + - mysql-job-dbdata + ports: + - "13306:3306" + volumes: + - ./sql:/docker-entrypoint-initdb.d + volumes_from: + - mysql-job-dbdata + healthcheck: + test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ] + interval: 5s + timeout: 10s + retries: 10 + start_period: 15s + networks: + - my-network + + # phpmyadmin https://hub.docker.com/_/phpmyadmin + phpmyadmin: + image: phpmyadmin:5.2.1 + container_name: phpmyadmin + hostname: phpmyadmin + ports: + - 8899:80 + environment: + - PMA_HOST=mysql + - PMA_PORT=3306 + - MYSQL_ROOT_PASSWORD=123qwe!@#QWE + depends_on: + mysql: + condition: service_healthy + networks: + - my-network + + # 自动加载数据 + mysql-job-dbdata: + image: alpine:3.18.2 + container_name: mysql-job-dbdata + volumes: + - /var/lib/mysql + +networks: + my-network: + driver: bridge +``` + +- 直接在 docker 下安装环境,安装时会自动的帮你把 dev-ops/mysql/sql 下的脚本创建到数据库中。 +- 并且这里提供了 phpmyadmin 可以让你直接在线上就能管理数据库。 +- 如果你本地有 msyql 8.x 那么可以直接导入数据库脚本。`dev-ops/mysql/sql/xfg-dev-tech-alipay-sandbox.sql` + +#### 2.2 内网穿透 + +登录:[https://natapp.cn/](https://natapp.cn/) - `内网穿透的主要作用就是让你的本地应用地址,可以被支付宝回调调用到` + +1. 开通自己的免费/付费渠道,用于内网穿透。本地内网穿透的地址,就可以被公网访问,也就是可以被支付回调。 +2. 开通后,登录后复制自己的渠道 token 粘贴到 config.ini 文件 authtoken 下。如果是Windows电脑,可以自己在官网下载。地址:[https://natapp.cn/#download](https://natapp.cn/#download) + +### 3. 工程配置 + +**源码**:`cn.bugstack.xfg.dev.tech.config.AliPayConfig` + +```java +@Configuration +@EnableConfigurationProperties(AliPayConfigProperties.class) +public class AliPayConfig { + + @Bean(name = "alipayClient") + @ConditionalOnProperty(value = "alipay.enabled", havingValue = "true", matchIfMissing = false) + public AlipayClient alipayClient(AliPayConfigProperties properties){ + return new DefaultAlipayClient(properties.getGatewayUrl(), + properties.getApp_id(), + properties.getMerchant_private_key(), + properties.getFormat(), + properties.getCharset(), + properties.getAlipay_public_key(), + properties.getSign_type()); + } + +} +``` + +```yml +# application-dev.yml +# 支付宝支付 - 沙箱 https://opendocs.alipay.com/common/02kkv7 +alipay: + enabled: true + app_id: 9021000132689924 + merchant_private_key: + alipay_public_key: + notify_url: http://xfg-studio.natapp1.cc/api/v1/alipay/pay_notify + return_url: https://gaga.plus + gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do +``` + +- 你可以参照【简单案例】配置以上内容,包括;app_id、merchant_private_key、alipay_public_key、notify_url 这些参数。 + +### 4. 代码实现 + +#### 4.1 订单服务 + +**源码**:`cn.bugstack.xfg.dev.tech.domain.service.IOrderService` + +```java +public interface IOrderService { + + /** + * 通过购物车实体对象,创建支付单实体(用于支付)—— 所有的订单下单都从购物车开始触发 + * + * @param shopCartEntity 购物车实体 + * @return 支付单实体 + */ + PayOrderEntity createOrder(ShopCartEntity shopCartEntity) throws Exception; + + /** + * 更新订单状态 + * @param orderId 订单ID + */ + void changeOrderPaySuccess(String orderId); + +} +``` + +#### 4.2 模板模式 + +**源码**:`cn.bugstack.xfg.dev.tech.domain.service.AbstractOrderService` + +```java +@Override +public PayOrderEntity createOrder(ShopCartEntity shopCartEntity) throws Exception{ + // 1. 查询当前用户是否存在掉单和未支付订单 + OrderEntity unpaidOrderEntity = repository.queryUnPayOrder(shopCartEntity); + if (null != unpaidOrderEntity && OrderStatusVO.PAY_WAIT.equals(unpaidOrderEntity.getOrderStatus())) { + log.info("创建订单-存在,已存在未支付订单。userId:{} productId:{} orderId:{}", shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId()); + return PayOrderEntity.builder() + .orderId(unpaidOrderEntity.getOrderId()) + .payUrl(unpaidOrderEntity.getPayUrl()) + .build(); + } else if (null != unpaidOrderEntity && OrderStatusVO.CREATE.equals(unpaidOrderEntity.getOrderStatus())) { + log.info("创建订单-存在,存在未创建支付单订单,创建支付单开始 userId:{} productId:{} orderId:{}", shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getOrderId()); + PayOrderEntity payOrderEntity = this.doPrepayOrder(shopCartEntity.getUserId(), shopCartEntity.getProductId(), unpaidOrderEntity.getProductName(), unpaidOrderEntity.getOrderId(), unpaidOrderEntity.getTotalAmount()); + return PayOrderEntity.builder() + .orderId(payOrderEntity.getOrderId()) + .payUrl(payOrderEntity.getPayUrl()) + .build(); + } + + // 2. 查询商品 & 聚合订单 + ProductEntity productEntity = repository.queryProductByProductId(shopCartEntity.getProductId()); + OrderEntity orderEntity = OrderEntity.builder() + .productId(productEntity.getProductId()) + .productName(productEntity.getProductName()) + .orderId(RandomStringUtils.randomNumeric(16)) + .orderTime(new Date()) + .orderStatus(OrderStatusVO.CREATE) + .build(); + CreateOrderAggregate orderAggregate = CreateOrderAggregate.builder() + .userId(shopCartEntity.getUserId()) + .productEntity(productEntity) + .orderEntity(orderEntity) + .build(); + + // 3. 保存订单 - 保存一份订单,再用订单生成ID生成支付单信息 + this.doSaveOrder(orderAggregate); + + // 4. 创建支付单 + PayOrderEntity payOrderEntity = this.doPrepayOrder(shopCartEntity.getUserId(), productEntity.getProductId(), productEntity.getProductName(), orderEntity.getOrderId(), productEntity.getPrice()); + log.info("创建订单-完成,生成支付单。userId: {} orderId: {} payUrl: {}", shopCartEntity.getUserId(), orderEntity.getOrderId(), payOrderEntity.getPayUrl()); + + return PayOrderEntity.builder() + .orderId(payOrderEntity.getOrderId()) + .payUrl(payOrderEntity.getPayUrl()) + .build(); +} +``` + +- 通过模板模式,定义出整个下单过程的标准流程和抽象方法。 + +#### 4.3 支付接口 + +**源码**:`cn.bugstack.xfg.dev.tech.trigger.http.AliPayController#createParOrder` + +```java +@RequestMapping(value = "create_pay_order", method = RequestMethod.POST) +public Response createParOrder(@RequestParam String userId, @RequestParam String productId) { + try { + log.info("商品下单,根据商品ID创建支付单开始 userId:{} productId:{}", userId, productId); + ShopCartEntity shopCartEntity = ShopCartEntity.builder().userId(userId).productId(productId).build(); + PayOrderEntity payOrderEntity = orderService.createOrder(shopCartEntity); + log.info("商品下单,根据商品ID创建支付单完成 userId:{} productId:{} orderId:{}", userId, productId, payOrderEntity.getOrderId()); + return Response.builder() + .code(ResponseCode.SUCCESS.getCode()) + .info(ResponseCode.SUCCESS.getInfo()) + .data(payOrderEntity.getPayUrl()) + .build(); + } catch (Exception e) { + log.error("商品下单,根据商品ID创建支付单失败 userId:{} productId:{}", userId, productId, e); + return Response.builder() + .code(ResponseCode.UN_ERROR.getCode()) + .info(ResponseCode.UN_ERROR.getInfo()) + .build(); + } +} +``` + +- 地址:[http://localhost:8091/api/v1/alipay/create_pay_order?userId=1001&productId=100001](http://localhost:8091/api/v1/alipay/create_pay_order?userId=1001&productId=100001) +- 说明:这里模拟了一个调用接口进行创建支付单的实现。 + +#### 4.4 回调接口 + +**源码**:`cn.bugstack.xfg.dev.tech.trigger.http.AliPayController#payNotify` + +```java +@RequestMapping(value = "pay_notify", method = RequestMethod.POST) +public String payNotify(HttpServletRequest request) { + try { + log.info("支付回调,消息接收 {}", request.getParameter("trade_status")); + if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) { + Map params = new HashMap<>(); + Map requestParams = request.getParameterMap(); + for (String name : requestParams.keySet()) { + params.put(name, request.getParameter(name)); + } + String tradeNo = params.get("out_trade_no"); + String gmtPayment = params.get("gmt_payment"); + String alipayTradeNo = params.get("trade_no"); + String sign = params.get("sign"); + String content = AlipaySignature.getSignCheckContentV1(params); + boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, alipayPublicKey, "UTF-8"); // 验证签名 + // 支付宝验签 + if (checkSignature) { + // 验签通过 + log.info("支付回调,交易名称: {}", params.get("subject")); + log.info("支付回调,交易状态: {}", params.get("trade_status")); + log.info("支付回调,支付宝交易凭证号: {}", params.get("trade_no")); + log.info("支付回调,商户订单号: {}", params.get("out_trade_no")); + log.info("支付回调,交易金额: {}", params.get("total_amount")); + log.info("支付回调,买家在支付宝唯一id: {}", params.get("buyer_id")); + log.info("支付回调,买家付款时间: {}", params.get("gmt_payment")); + log.info("支付回调,买家付款金额: {}", params.get("buyer_pay_amount")); + log.info("支付回调,支付回调,更新订单 {}", tradeNo); + // 更新订单未已支付 + orderService.changeOrderPaySuccess(tradeNo); + // 推送消息【自己的业务场景中可以使用MQ消息】 + eventBus.post(tradeNo); + } + } + return "success"; + } catch (Exception e) { + log.error("支付回调,处理失败", e); + return "false"; + } +} +``` + +- 地址:[http://xfg-studio.natapp1.cc/api/v1/alipay/pay_notify](http://xfg-studio.natapp1.cc/api/v1/alipay/pay_notify) +- 说明 :回调是支付宝调用我们的接口。默认是 https://localhost:8091 因为使用了 natapp 就可以把本地的地址+端口,映射到一个 natapp 内网穿透地址。所以地址为:http://xfg-studio.natapp1.cc/api/v1/alipay/pay_notify + +## 六、功能验证 + +### 1. 前置 + +1. 安装 MySQL 8.0 数据库 + 初始化库表。如果你是低版本的数据库,需要自己创建下库表。 +2. 打开 natapp.cn 配置一个自己的内网穿透渠道,渠道内配置工程映射地址 8091 如果你是其他端口则配置为其他的地址即可。之后本地启动 natapp +3. 启动 xfg-dev-tech-alipay-sandbox 应用。 + +### 2. 测试 + +#### 2.1 启动 natapp + +
+ +
+ +#### 2.2 启动工程 + +
+ +
+ +#### 2.3 下单页面 + +进入到工程 docs/dev-ops/front 下,打开 index.html 文件。点击下单,这个时候你会进入到支付页面。 + +
+ +
+ +#### 2.4 商品支付 + +
+ +
+ +
+ +
+ +
+ +
+ +#### 2.5 库表数据 + +
+ +
+ +### 3. 日志 + +```java +23-12-17.15:11:55.692 [http-nio-8091-exec-4] INFO AliPayController - 商品下单,根据商品ID创建支付单开始 userId:xiaofuge productId:100010090091 +23-12-17.15:11:55.908 [http-nio-8091-exec-4] INFO AbstractOrderService - 创建订单-完成,生成支付单。userId: xiaofuge orderId: 1620564694789743 payUrl:
+ + +
+ +23-12-17.15:11:55.909 [http-nio-8091-exec-4] INFO AliPayController - 商品下单,根据商品ID创建支付单完成 userId:xiaofuge productId:100010090091 orderId:1620564694789743 +23-12-17.15:12:37.553 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,消息接收 TRADE_SUCCESS +23-12-17.15:12:37.558 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,交易名称: 测试商品 +23-12-17.15:12:37.558 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,交易状态: TRADE_SUCCESS +23-12-17.15:12:37.558 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,支付宝交易凭证号: 2023121722001413370501437659 +23-12-17.15:12:37.558 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,商户订单号: 1620564694789743 +23-12-17.15:12:37.558 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,交易金额: 1.68 +23-12-17.15:12:37.562 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,买家在支付宝唯一id: 2088722024513370 +23-12-17.15:12:37.562 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,买家付款时间: 2023-12-17 15:12:35 +23-12-17.15:12:37.562 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,买家付款金额: 1.68 +23-12-17.15:12:37.562 [http-nio-8091-exec-5] INFO AliPayController - 支付回调,支付回调,更新订单 1620564694789743 +23-12-17.15:12:37.584 [http-nio-8091-exec-5] INFO OrderPaySuccessListener - 收到支付成功消息,可以做接下来的事情了【发货、充值、开会员】orderId:1620564694789743 +``` + +- 以上为整个项目运行过程所产生的日志数据。通过日志可清楚知道每一步的操作过程。 + +## 七、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:大营销平台系统、OpenAI 大模型应用、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +加入后即可解锁全部学习资源,项目演示地址:[https://gaga.plus](https://gaga.plus) \ No newline at end of file diff --git a/docs/md/project/ddd-scene-solution/openai-tldraw.md b/docs/md/project/ddd-scene-solution/openai-tldraw.md new file mode 100644 index 000000000..a84f078d0 --- /dev/null +++ b/docs/md/project/ddd-scene-solution/openai-tldraw.md @@ -0,0 +1,204 @@ +--- +title: OpenAI + TLDraw 设计图转前端代码 +lock: need +--- + +# 【小场景训练营】OpenAI + TLDraw 设计图转前端代码 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +哈喽,大家好我是技术UP主小傅哥。 + +大部分前端程序员是不会写后端代码的,但大部分后端程序员都能写点前端代码。不过虽然能写,但也是照葫芦画瓢,修修改改的二把手刀选手。😂 小傅哥就是这样的二把刀前端,每次写前端都感觉像是屎上雕花,一点点扣哧 DIV、CSS 调整样式和数据。 + +
+ +
+ +但自从有了 OpenAI 以后,我一直惦记着,能不在 Draw.io 这样的工具出设计图后,让 OpenAI 识别自动转换为 HTML 代码。之后把这样的东西做成一整套工具来使用,大大的方便`二把刀前端们`快速完成从UI到HTML的初稿。 + +为此小傅哥基于[开源项目](https://github.com/SawyerHood/draw-a-ui),使用 [React](https://zh-hans.react.dev/learn) + [TLDraw](https://github.com/tldraw/tldraw) + [OpenAI](https://platform.openai.com/docs/api-reference/chat/create)(多模态 gpt-4-vision) 做了一款这样的工具案例; + +
+ +
+ +对于研发工程师来说,只是使用其实没多大意义,重要的是知道这东西是怎么实现的。那么接下来小傅哥就来详细给大家介绍下具体的设计实现过程。 + +>文末提供了该案例的课程地址(含源码)和项目的体验地址。体验地址:https://openai.itedus.cn/#/draw + +## 一、场景说明 + +本节小傅哥会带着大家先以最简单的方式完成 OpenAI 多模态接口与 TLDraw 的对接,再讲解项目中如何使用策略模式拆解不同类型的 OpenAI 服务接口(`文生文`、`文生图`、`多模态`)。 + +
+ +
+ +- 首先,我们知道 OpenAI 的多模态模型,是需要像接口中传一个 BASE64 图片信息,并给予对应的描述性 Prompt 脚本。这样 OpenAI 就会知道我们要基于对这个图片做什么。 +- 那么,我们就需要一款在线画图的工具,比如 [TLDraw](https://github.com/tldraw/tldraw) 或者 [Draw.io](https://github.com/jgraph/drawio) 把这样的绘图能力引入到 React 工程中。通过绘图后生成截图,再把图片转换为 BASE64 就可以使用。 +- 最后,把 OpenAI 传递回来的接口数据,用 React 进行展示和效果渲染。也就是操作完成后,你可以看到 HTML 对应的展示效果。 + +## 二、功能实现 + +### 1. 多模态接口 + +```java +curl -X POST "https://api.openai.com/v1/chat/completions" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer sk-kgUPx1vKDywFbsun7c05Ed5eA4C24d4aA7B06aE9F76e6eFe" \ + -d '{ + "model": "gpt-4-vision-preview", + "max_tokens": 4096, + "messages": [ + { + "role": "system", + "content": "Your-System-Prompt-Here" + }, + { + "role": "user", + "content": [ + { + "type": "image_url", + "image_url": { + "url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg" + } + }, + { + "text": "Turn this into a single html file using tailwind.", + "type": "text" + } + ] + } + ] + }' + +``` + +- 首先,我们做一个东西的时候,要先知道最根本调用是如何处理的。 +- 之后,多模态接口与以往的 gpt-3.5、gpt-4.0 都是用的同一个接口,`v1/chat/completions` 只是用了 `gpt-4-vision-preview` 模型。同时 content 既可以是单独的描述字符串,也可以是对象类型含有 `type`、`text`、`image_url` 的方式进行使用。 + +## 二、前端实现 + +本案例基于 React 构建的前端页面,如果你还是个前端小白可以学习此份教程;[https://zh-hans.react.dev/learn](https://zh-hans.react.dev/learn) —— 全中文官网资料非常适合入门。 + +### 1. 工程介绍 + +
+ +
+ +工程主要分为三块,OpenAI 接口、存储和工具类、TLDraw UI绘制页面 + 调用 OpenAI 接口的实现。 + +### 2. tldraw 组件 + +在开发前端代码的时候,需要按照 `npm install @tldraw/tldraw@2.0.0-alpha.17` 组件。 + +```react +// 引入组件 +const Tldraw = dynamic(async () => (await import('@tldraw/tldraw')).Tldraw, { + ssr: false, +}) + +// 使用组件 + +``` + +- 引入组件后就可以在 page.tsx 中使用这个组件了,目前你启动 react 会看到整个运行出来的 UI 设计页面。 + +### 3. 生成图片 + +```react +// tldraw 可以把当前页面转换为 svg 图片 +const svg = await editor.getSvg(Array.from(editor.currentPageShapeIds)) +// 基于工具把 svg 图片转换为 png图片 +const png = await svg2image(svg, { + type: 'png', + quality: 1, + scale: 1, +}) +// 再把png图片在前端直接转换为 base64 +const dataUrl = (await blobToBase64(png!)) as string +``` + +### 4. 调用接口 + +```react +try { + let json = await getResponseFromAPI(dataUrl, prompt) +} catch (error: any) { + console.log(error) + alert(`Error from open ai: ${JSON.stringify(error.message)}`) + return +} +``` + +- 之后就可以同步调用接口数据等待返回结果,回显到页面即可。 + +## 三、使用体验 + +### 1. 初始动作 + +```react +echo "BASE_API_URL=url-your-proxy like https://xxxxx.proxy.com/v1/chat/completions \r\nOPENAI_API_KEY=sk-your-key" > .env.local +rm -rf node_modules +npm install +npm run dev +``` + +- 推荐使用 WebStorm 打开 openai-tldraw 工程,它会自动的提示你执行 `npm install` 构建项目。 +- 当你进入 openai-tldraw 工程中,需要先执行 echo 教程,创建出 `.env.local` 文件。这个配置里写入你 BASE_API_URL 和 OPENAI_API_KEY +- 最后在工程中 package.json 点击 dev 左侧的绿色按钮或者执行 `npm run dev` 都会启动工程。 +- 启动后访问地址:[http://localhost:3000/](http://localhost:3000/) + +### 2. 生成效果 + +
+ +
+ +### 3. 生成代码 + +
+ +
+ +## 五、项目运用 + +以上还是简单的案例,当我们把这样的接口功能放到项目中开发,就需要考虑到接口的策略调用来让结构更加易于维护。 + +### 1. 流程设计 + +
+ +
+ +此流程为小傅哥星球「码农会锁」OpenAI 项目的的核心流程,最下面三个地方支持;`文生文`、`文生图`、`多模态(图文理解)`,而图文理解本文的案例是同步请求,但前面的`文生文`、`文生图`,是异步响应。那么这样的代码结构怎么设计呢?🤔 + +>欢迎👏🏻加入小傅哥星球「码农会锁」,一起学习这样的有架构、有设计,有高质量编码的项目。演示地址:[https://gaga.plus](https://gaga.plus) + +### 2. 代码举例 + +
+ +
+ +在小傅哥的星球「码农会锁」中,有大量的此类设计,来解决实际场景的问题。如果能参与一个这样的项目学习,那么对编程的架构和设计的理解会提升的非常多。 + +## 六、加入学习 + +**注意📢**,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:正在进行的大营销平台、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,还有开源项目学习。 + +**课程💐**,加入星球「码农会锁」,即可获得本项目源码,以及当前92个课程代码仓库。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +**加入星球**:下载`星球APP`,从星球【课程入口】进入。里面有完整的学习指引,包括;使用说明、代码仓库、专属项目群、学习路线、往期项目。 + diff --git a/docs/md/project/ddd-scene-solution/sensitive-word-content-moderation.md b/docs/md/project/ddd-scene-solution/sensitive-word-content-moderation.md new file mode 100644 index 000000000..531a01e03 --- /dev/null +++ b/docs/md/project/ddd-scene-solution/sensitive-word-content-moderation.md @@ -0,0 +1,436 @@ +--- +title: 敏感词内容审核 +lock: need +--- + +# 【小场景训练营】敏感词内容审核 —— 如果你的论坛被人乱留言怎么办? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +哈喽,大家好我是技术UP主小傅哥。 + +常听到一句话:”你很难赚到你认知以外的钱💰“,屁!不是很难,是压根赚不到。你以为要是你做也能做,但其实除了你能看见的以外,还有很多东西都不知道。 + +
+ +
+ +我看过不少小伙伴自己上线过带有评论功能的博客,或是能进行通信的聊天室。但最后都没运营多久就关停了,除了能花钱解决的服务器成本,还有是自身的研发的系统流程不够健全。其中非常重要的一点是`舆情敏感内容`的审核,如果你做这类应用的处理,一定要对接上相应的内容安全审核。 + +那么,接下来小傅哥就给大家分享下,如何对接内容安全审核,并在 DDD 分层结构下实现一个对应的规则过滤服务。 + +>文末提供了「星球:码农会锁」🧧优惠加入方式,以及本节课程的代码地址。项目演示地址:[https://gaga.plus](https://gaga.plus) + +## 一、场景说明 + +在本节小傅哥会通过 DDD 分层架构设计,开发出一个敏感词、内容安全审核过滤操作的规则处理器。在这个过程大家可以学习到 DDD 分层调用流程、规则模型的搭建、敏感词和内容审核的使用。 + +
+ +
+ +如图,上半部分是业务流程,下半部分是 DDD 分层结构中的实现。 + +1. 业务流程上,以用户发送的提交给服务端的内容进行审核过滤,优先使用敏感词进行替换单词组。过滤后过内容审核,一般各个云平台都有提供内容审核的接口,如;京东云、百度云、腾讯云都有提供。一般价格在 `0.0015 元/条` +2. 系统实现上,以 DDD 分层架构实现一个内容审核的流程。app 配置组件和启动应用、trigger 提供 http 调用、domain 编写核心逻辑和流程、infrastructure 提供 dao 的基础操作。 + +## 二、内容审核 - SDK 使用 + +一般舆情内容审核分为两种,一种是静态配置数据的 SDK 组件,也叫敏感词过滤。另外一种是实时动态的由各个第三方提供的内容审核接口服务。这类的就是前面提到的,在各个云平台都有提供。 + +这里小傅哥先带着大家做下最基本的调用案例,之后再基于 DDD 工程实现整个代码开发。 + +### 1. 敏感词 + +**地址**:[https://github.com/houbb/sensitive-word](https://github.com/houbb/sensitive-word) - 开源的敏感词库组件 + +```pom + + com.github.houbb + sensitive-word + 0.8.0 + +``` + +**案例代码** + +```java +@Test +public void test_sensitive_word() { + boolean contains = sensitiveWordBs.contains("小傅哥喜欢烧烤臭毛蛋,豆包爱吃粑粑,如果想吃订购请打电话:13900901878"); + log.info("是否被敏感词拦截:{}", contains); +} + +@Test +public void test_sensitive_word_findAll() { + List list = sensitiveWordBs.findAll("小傅哥喜欢烧烤臭毛蛋,豆包爱吃粑粑,如果想吃订购请打电话:13900901878"); + log.info("测试结果:{}", JSON.toJSONString(list)); +} + +@Test +public void test_sensitive_word_replace() { + String replace = sensitiveWordBs.replace("小傅哥喜欢烧烤臭毛蛋,豆包爱吃粑粑,如果想吃订购请打电话:13900901878"); + log.info("测试结果:{}", replace); +} +``` + +- 敏感词组件提供了大量的风险词过滤,同时可以基于组件的文档完成自定义敏感词的增改删减操作。`本文在工程中已提供` +- 敏感词组件提供了判断、查找、过滤操作。还有你可以把检测到的敏感词替换为 `*` 或者`空格`。 + +### 2. 内容审核 + +- 京东云:[https://www.jdcloud.com/cn/products/censor](https://www.jdcloud.com/cn/products/censor) +- 百度云:[https://ai.baidu.com/censoring#/strategylist](https://ai.baidu.com/censoring#/strategylist) +- 腾讯云:[https://cloud.tencent.com/product/tms](https://cloud.tencent.com/product/tms) + +这里小傅哥以其中的一个百度云为例,为大家展示内容安全审核的使用。 + +```pom + + + com.baidu.aip + java-sdk + 4.16.17 + +``` + +#### 2.1 配置应用 + +
+ +
+ +- 先领取免费的调用次数,之后创建应用。创建应用后就可以获得连接信息;appid、apikey、secretkey +- 另外是策略配置,如果你在过滤中不需要检测用户发的应用营销信息,那么是可以不检测的。 + +#### 2.2 测试服务 + +```java +//设置APPID/AK/SK +public static final String APP_ID = "{APP_ID}"; +public static final String API_KEY = "{API_KEY}"; +public static final String SECRET_KEY = "{SECRET_KEY}"; +private AipContentCensor client; + +@Before +public void init() { + client = new AipContentCensor(APP_ID, API_KEY, SECRET_KEY); + // 可选:设置网络连接参数 + client.setConnectionTimeoutInMillis(2000); + client.setSocketTimeoutInMillis(60000); +} + +@Test +public void test_textCensorUserDefined() throws JSONException { + for (int i = 0; i < 1; i++) { + JSONObject jsonObject = client.textCensorUserDefined("小傅哥喜欢烧烤臭毛蛋,豆包爱吃粑粑,如果想吃订购请打电话:13900901878"); + if (!jsonObject.isNull("error_code")) { + log.info("测试结果:{}", jsonObject.get("error_code")); + } else { + log.info("测试结果:{}", jsonObject.toString()); + } + } +} +``` + +**测试结果** + +```java +13:41:16.393 [main] INFO com.baidu.aip.client.BaseClient - get access_token success. current state: STATE_AIP_AUTH_OK +13:41:16.396 [main] DEBUG com.baidu.aip.client.BaseClient - current state after check priviledge: STATE_TRUE_AIP_USER +13:41:16.495 [main] INFO cn.bugstack.x.api.test.BaiduAipContentCensorTest - 测试结果:{"conclusion":"合规","log_id":17046060767025067,"isHitMd5":false,"conclusionType":1} +``` + +- 应为过滤掉了营销信息,比如手机号。那么就会返回`合规` + +## 三、应用实现 - DDD 架构 + +做了以上的基本调用案例以后,我们来看下在系统中怎么运用这些基础功能完成业务诉求。 + +### 1. 工程结构 + +
+ +
+ +- docs 下提供了 docker 安装 mysql 以及初始化数据库配置的脚本。因为本文的案例,可以满足你在数据库中增加敏感词配置。 +- app 是应用的启动层,如上我们所需的敏感词和内容审核,都在app层下配置启动处理。 +- domain 领域层通过策略+工厂,实现规则过滤服务。 + +### 2. 数据库表 + +
+ +
+ +- 在docs 提供了数据库初始化的脚本语句,你可以导入到自己的数据库,或者使用 docker 脚本安装测试。—— 注意已经安装过 mysql 占用了 3306 端口的话,记得修改 docker 脚本安装 mysql 的端口。 +- 配置到数据库中的敏感词方便管理和使用,为了性能考虑也可以考虑使用 redis 做一层缓存。 + +### 3. 配置加载 + +#### 3.1 敏感词初始化 + +```java +@Configuration +public class SensitiveWordConfig { + + @Bean + public SensitiveWordBs sensitiveWordBs(IWordDeny wordDeny, IWordAllow wordAllow) { + return SensitiveWordBs.newInstance() + .wordDeny(wordDeny) + .wordAllow(wordAllow) + .ignoreCase(true) + .ignoreWidth(true) + .ignoreNumStyle(true) + .ignoreChineseStyle(true) + .ignoreEnglishStyle(true) + .ignoreRepeat(false) + .enableNumCheck(true) + .enableEmailCheck(true) + .enableUrlCheck(true) + .enableWordCheck(true) + .numCheckLen(1024) + .init(); + } + + @Bean + public IWordDeny wordDeny(ISensitiveWordDao sensitiveWordDao) { + return new IWordDeny() { + @Override + public List deny() { + return sensitiveWordDao.queryValidSensitiveWordConfig("deny"); + } + }; + } + + @Bean + public IWordAllow wordAllow(ISensitiveWordDao sensitiveWordDao) { + return new IWordAllow() { + @Override + public List allow() { + return sensitiveWordDao.queryValidSensitiveWordConfig("allow"); + } + }; + } + +} +``` + +- wordDeny、wordAllow 是两个自定义的拦截和放行的敏感词列表,这里小傅哥设计从数据库中查询。可以方便动态的维护。 + +#### 3.2 内容安全初始化 + +```yml +# 内容安全 +baidu: + aip: + app_id: 46573000 + api_key: XKOalQOgDBUrvgLBplvu**** + secret_key: kwRh1bEhETYWpq9thzyySdFDPKUk**** +``` + +- 自定义一个配置文件类 AipContentCensorConfigProperties + +```java +@Bean +public AipContentCensor aipContentCensor(AipContentCensorConfigProperties properties) { + AipContentCensor client = new AipContentCensor(properties.getApp_id(), properties.getApi_key(), properties.getSecret_key()); + client.setConnectionTimeoutInMillis(2000); + client.setSocketTimeoutInMillis(60000); + return client; +} +``` + +- 这里我们来统一创建 AipContentCensor 对象,用于有需要使用的地方处理内容审核。 + +### 4. 规则实现 + +**源码:** `cn.bugstack.xfg.dev.tech.domain.service.IRuleLogicFilter` + +```java +public interface IRuleLogicFilter { + + RuleActionEntity filter(RuleMatterEntity ruleMatterEntity); + +} +``` + +- 定义一个统一的规则过滤接口 + +#### 4.1 敏感词 + +```java +@Slf4j +@Component +@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.SENSITIVE_WORD) +public class SensitiveWordFilter implements IRuleLogicFilter { + + @Resource + private SensitiveWordBs words; + + @Override + public RuleActionEntity filter(RuleMatterEntity ruleMatterEntity) { + // 敏感词过滤 + String content = ruleMatterEntity.getContent(); + String replace = words.replace(content); + // 返回结果 + return RuleActionEntity.builder() + .type(LogicCheckTypeVO.SUCCESS) + .data(RuleMatterEntity.builder().content(replace).build()) + .build(); + } + +} +``` + +#### 4.2 安全内容 + +```java +@Slf4j +@Component +@LogicStrategy(logicMode = DefaultLogicFactory.LogicModel.CONTENT_SECURITY) +public class ContentSecurityFilter implements IRuleLogicFilter { + + @Resource + private AipContentCensor aipContentCensor; + + @Override + public RuleActionEntity filter(RuleMatterEntity ruleMatterEntity) { + JSONObject jsonObject = aipContentCensor.textCensorUserDefined(ruleMatterEntity.getContent()); + if (!jsonObject.isNull("conclusion") && "不合规".equals(jsonObject.get("conclusion"))) { + return RuleActionEntity.builder() + .type(LogicCheckTypeVO.REFUSE) + .data(RuleMatterEntity.builder().content("内容不合规").build()) + .build(); + } + // 返回结果 + return RuleActionEntity.builder() + .type(LogicCheckTypeVO.SUCCESS) + .data(ruleMatterEntity) + .build(); + } + +} +``` + +### 5. 工厂使用 + +```java +public class DefaultLogicFactory { + + public Map logicFilterMap = new ConcurrentHashMap<>(); + + public DefaultLogicFactory(List logicFilters) { + logicFilters.forEach(logic -> { + LogicStrategy strategy = AnnotationUtils.findAnnotation(logic.getClass(), LogicStrategy.class); + if (null != strategy) { + logicFilterMap.put(strategy.logicMode().getCode(), logic); + } + }); + } + + public RuleActionEntity doCheckLogic(RuleMatterEntity ruleMatterEntity, LogicModel... logics) { + RuleActionEntity entity = null; + for (LogicModel model : logics) { + entity = logicFilterMap.get(model.code).filter(ruleMatterEntity); + if (!LogicCheckTypeVO.SUCCESS.equals(entity.getType())) return entity; + ruleMatterEntity = entity.getData(); + } + return entity != null ? entity : + RuleActionEntity.builder() + .type(LogicCheckTypeVO.SUCCESS) + .data(ruleMatterEntity) + .build(); + } + +} +``` + +- 定义出规则的使用工厂,通过构造函数的方式注入已经实现了接口 IRuleLogicFilter 的 N 个规则,注入到 Map 中 `Map logicFilterMap` +- doCheckLogic 根据入参来过滤需要处理的规则。这里可以看到每过滤一个规则都会把参数继续传递给下一个规则继续筛选。`有点像层层过筛子的感觉` + +## 四、测试验证 + +1. 测试前确保已经初始化了库表 `docs/dev-ops/sql/xfg-dev-tech-content-moderation.sql` +2. `application-dev.yml` 配置百度内容安全参数和数据库连接参数。 + +### 1. 功能测试 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class RuleLogicTest { + + @Resource + private DefaultLogicFactory defaultLogicFactory; + + @Test + public void test() { + RuleActionEntity entity = defaultLogicFactory.doCheckLogic( + RuleMatterEntity.builder().content("小傅哥喜欢烧烤臭毛蛋,豆包爱吃粑粑,如果想吃订购请打电话:13900901878").build(), + DefaultLogicFactory.LogicModel.SENSITIVE_WORD, + DefaultLogicFactory.LogicModel.CONTENT_SECURITY + ); + log.info("测试结果:{}", JSON.toJSONString(entity)); + } + +} +``` + +**测试结果** + +```java +24-01-07.14:17:16.988 [main ] INFO BaseClient - get access_token success. current state: STATE_AIP_AUTH_OK +24-01-07.14:17:17.328 [main ] INFO RuleLogicTest - 测试结果:{"data":{"content":"小傅哥喜欢烧烤***,豆包爱吃**,如果想吃订购请打电话:13900901878"},"type":"SUCCESS"} +``` + +### 2. 接口测试 + +```java +@RequestMapping(value = "sensitive/rule", method = RequestMethod.GET) +public String rule(String content) { + try { + log.info("内容审核开始 content: {}", content); + RuleActionEntity entity = defaultLogicFactory.doCheckLogic(RuleMatterEntity.builder().content(content).build(), + DefaultLogicFactory.LogicModel.SENSITIVE_WORD, + DefaultLogicFactory.LogicModel.CONTENT_SECURITY + ); + log.info("内容审核完成 content: {}", entity.getData()); + return JSON.toJSONString(entity); + } catch (Exception e) { + log.error("内容审核异常 content: {}", content, e); + return "Err!"; + } +} +``` + +接口:`http://localhost:8091/api/v1/content/sensitive/rule?content=小傅哥喜欢烧烤臭毛蛋,豆包爱吃粑粑,如果想吃订购请打电话:13900901878` + +
+ +
+ +- 那么现在就可以对内容进行审核过滤了。 + +## 六、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:大营销平台系统、OpenAI 大模型应用、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +本节案例源码:[https://gitcode.net/KnowledgePlanet/ddd-scene-solution/xfg-dev-tech-content-moderation](https://gitcode.net/KnowledgePlanet/ddd-scene-solution/xfg-dev-tech-content-moderation) + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +## 七、推荐阅读 + +- [商品下单支付场景,DDD设计实现「支付宝沙箱」](https://bugstack.cn/md/project/ddd-scene-solution/alipay-sandbox.html) +- [MVC2DDD - 架构重构](https://bugstack.cn/md/road-map/mvc2ddd.html) +- [Mock 单元测试&插件生成测试代码](https://bugstack.cn/md/road-map/mock.html) +- [《大营销平台系统》—— 小傅哥第8个项目,前后端 + Dev-Ops 的全栈式综合编程实战DDD项目!](https://bugstack.cn/md/project/big-market/big-market.html) diff --git a/docs/md/project/ddd-scene-solution/weixin-login.md b/docs/md/project/ddd-scene-solution/weixin-login.md new file mode 100644 index 000000000..c45e84986 --- /dev/null +++ b/docs/md/project/ddd-scene-solution/weixin-login.md @@ -0,0 +1,420 @@ +--- +title: 网站提示用公众号扫码登录,他们是怎么实现的? +lock: need +--- + +# 【小场景训练营】网站提示用公众号扫码登录,他们是怎么实现的? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +作为一个技术码农,在使用社区、论坛或者各类AI服务的时,经常会看到这样一个提示:“使用微信公众号扫码登录”。那因为这种的登录方式除了登录,还可以让用户沉淀到公众号上,以后还能接收到公众号推广,可谓是一举两得。那它是怎么做的呢?🤔 + +
+ +
+ +小傅哥,先举个这样登录的例子🌰,让大家熟悉下这个业务场景。 + +这是一个 CSDN 微信扫码登录的场景,通过 F12 打开浏览器的控制台,可以看到不断的请求一个网络地址,判断用户是否扫码。当你使用微信扫码后,则会登录成功跳转到网站的首页。 + +
+ +
+ +通过这样的一个页面效果展示,我们粗略的可以知道,用户页面不断的 checkScan 检测,是需要用到一个唯一ID值。而当用户用微信扫码后,这个唯一ID值则可以通过微信公众号获取到并保存,同时创建出唯一ID 和 Token 的映射关系。那么当 checkScan 扫描到服务端有这么一个映射,则可以把 Token 取回来存到浏览器中,让用户登录成功。 + +流程就是这样,那具体的代码实现是如何处理的呢?接下来小傅哥就给大家分享下,怎么来实现一下这个方案。 + +>文末提供了「星球:码农会锁」🧧优惠加入方式,以及本节课程的代码地址。项目演示地址:[https://gaga.plus](https://gaga.plus) + +## 一、流程设计 + +微信扫码登录的流程主要包括;用户、浏览器、后端服务、公众号,这四个部分。我们可以先通过UML流程图,了解下整个调用关系。 + +
+ +
+ +- 首先,由用户发起登录操作。让WEB页面从服务端获取登录凭证。 +- 之后,前端页面拿到登录凭证后,可以使用 Ticket 从公众号服务平台换取二维码。 +- 最后,用户扫码登录。扫码后,服务端会接收到来自公众号的回调消息,服务端再把回调消息中的 openid【用户唯一标识】和 ticket 进行绑定。这个时候你也可以创建出 jwt token 反馈给前端,作为登录成功的存储信息,后续校验 jwt token 就可以了。 + +有了这样一个流程的理解,接下来,我们就可以看下代码是如何实现的了。 + +## 二、对接文档 - 公众号平台 + +- 微信公众号测试平台:[https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index](https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index) - `不需要申请公众号即可完成测试,类似沙箱环境` +- 获取 Access Token 文档:[https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html) +- 生成带参数的二维码:[https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html](https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html) - `最终就是用户扫描的二维码` +- 内网穿透工具,[natapp.cn](https://natapp.cn/) - 因为需要让公众号调用到本地的服务,所需要把你的服务映射到公网上使用。注意;要选择付费的12元,否则不能对接。 + +## 三、功能实现 + +小傅哥这里采用了 DDD 的工程模型结构,开发公众号扫码登录服务端案例。如果你对 DDD 还不是太熟悉,可以看下小傅哥写的系列 DDD 教程;[《Java 简明教程》](https://bugstack.cn/md/road-map/road-map.html) + +### 1. 工程结构 + +
+ +
+ +- xfg-dev-tech-app 是启动应用程序的入口,其他模块也被直接或者间接的引入到 app 模块下,这样才能被 Spring 扫描加载。 +- xfg-dev-tech-infrastructure 是基础设施层,用于对接外部接口、缓存、数据库等相关内容的连接使用。本节主要是对接微信开发平台的接口。采用的是 retrofit2 技术框架,这样对接起来更加方便。 +- xfg-dev-tech-domain 是功能实现层,像是登录的具体实现,就是在 domain 领域层实现的。你将来使用 DDD 做的其他功能,也是放到 domain 领域下实现,每一个功能就是就是一个模块。 +- xfg-dev-tech-types 用于定义基本的类型、枚举、错误码等内容。 + +### 2. 二维码获取 + +从[微信官网文档](https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html)阅读可以知道,为了获取扫码登录的二维码,则需要3步; +1. 先获取 AccessToken,它是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。 +2. 通过 AccessToken 获取 ticket 凭证,凭证用于衔接用户扫码登录和公众号回调后获取凭证,以此关联用户登录信息。 +3. 通过 ticket 传递给前端,前端页面访问微信地址直接获取二维码。 + +#### 2.1 接口对接 - retrofit2 + +```java +public interface IWeixinApiService { + + /** + * 获取 Access token + * 文档:Get_access_token + * + * @param grantType 获取access_token填写client_credential + * @param appId 第三方用户唯一凭证 + * @param appSecret 第三方用户唯一凭证密钥,即appsecret + * @return 响应结果 + */ + @GET("cgi-bin/token") + Call getToken( + @Query("grant_type") String grantType, + @Query("appid") String appId, + @Query("secret") String appSecret + ); + + /** + * 获取凭据 ticket + * 文档:Generating_a_Parametric_QR_Code + * 前端根据凭证展示二维码 + * + * @param accessToken getToken 获取的 token 信息 + * @param weixinQrCodeRequestDTO 入参对象 + * @return 应答结果 + */ + @POST("cgi-bin/qrcode/create") + Call createQrCode(@Query("access_token") String accessToken, @Body WeixinQrCodeRequestDTO weixinQrCodeRequestDTO); + +} +``` + +- 使用 retrofit2 对接接口,它可以以一种面向对象的思维,使用 HTTP 接口,免去自己处理中间的对接过程。 +- 另外 okhttp3 框架对接接口也非常好用,有的时候可以配合一起使用。 + +```java +@Slf4j +@Configuration +public class Retrofit2Config { + + private static final String BASE_URL = "https://api.weixin.qq.com/"; + + @Bean + public Retrofit retrofit() { + return new Retrofit.Builder() + .baseUrl(BASE_URL) + .addConverterFactory(JacksonConverterFactory.create()) + .build(); + } + + @Bean + public IWeixinApiService weixinApiService(Retrofit retrofit) { + return retrofit.create(IWeixinApiService.class); + } + +} +``` + +- 使用 retrofit2 开发好接口后,在再 xfg-dev-tech-app 模块的 config 文件夹下,创建服务。【这有点像 MyBatis 的 Dao 接口一样,只需要定义好接口即可】 + +#### 2.2 ApiPost 请求 + +接口:`https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET` + +
+ +
+ +- APIPost 模拟网页获得的扫码登录的二维码。 +- 接下来程序到测试的时候,产生的 ticket 会放到这里模拟使用。 + +### 3. 登录码开发 + +**源码**:`cn.bugstack.xfg.dev.tech.trigger.http.LoginController` + +```java +@Slf4j +@RestController() +@CrossOrigin("*") +@RequestMapping("/api/v1/login/") +public class LoginController { + + @Resource + private ILoginService loginService; + + @RequestMapping(value = "weixin_qrcode_ticket", method = RequestMethod.GET) + public Response weixinQrCodeTicket() { + try { + String qrCodeTicket = loginService.createQrCodeTicket(); + log.info("生成微信扫码登录 ticket {}", qrCodeTicket); + return Response.builder() + .code(Constants.ResponseCode.SUCCESS.getCode()) + .info(Constants.ResponseCode.SUCCESS.getInfo()) + .data(qrCodeTicket) + .build(); + } catch (Exception e) { + log.info("生成微信扫码登录 ticket 失败", e); + return Response.builder() + .code(Constants.ResponseCode.UN_ERROR.getCode()) + .info(Constants.ResponseCode.UN_ERROR.getInfo()) + .build(); + } + } + + @RequestMapping(value = "check_login", method = RequestMethod.GET) + public Response checkLogin(@RequestParam String ticket) { + try { + String openidToken = loginService.checkLogin(ticket); + log.info("扫描检测登录结果 ticket:{} openidToken:{}", ticket, openidToken); + if (StringUtils.isNotBlank(openidToken)) { + return Response.builder() + .code(Constants.ResponseCode.SUCCESS.getCode()) + .info(Constants.ResponseCode.SUCCESS.getInfo()) + .data(openidToken) + .build(); + } else { + return Response.builder() + .code(Constants.ResponseCode.NO_LOGIN.getCode()) + .info(Constants.ResponseCode.NO_LOGIN.getInfo()) + .build(); + } + } catch (Exception e) { + log.info("扫描检测登录结果失败 ticket:{}", ticket); + return Response.builder() + .code(Constants.ResponseCode.UN_ERROR.getCode()) + .info(Constants.ResponseCode.UN_ERROR.getInfo()) + .build(); + } + } + +} +``` + +开发两个接口; + +1. `/api/v1/login/weixin_qrcode_ticket` - 获取微信 ticket 凭证 +2. `/api/v1/login/check_login` - 轮训验证登录 + +### 4. 公众号开发 + +首先,只要做公众号开发的流程,就必须有公众号的对接。这个对接就是你在自己按照公众号文档开发好对接程序,配置到公众号平台。 + +#### 4.1 配置说明 + +
+ +
+ +如图所示,是你在登录微信公众号测试平台,添加接口配置和JS安全域名以后看到的内容。 + +1. 最顶上,微信号,需要配置到 xfg-dev-tech-weixin-login 的 application-dev.yml 文件中。 +2. 测试号信息 appID、appsecret,也需要配置到 application-dev.yml 文件中。 +3. 接口信息的配置,需要你在启动 xfg-dev-tech-weixin-login,同时在本地测试时启动 natapp 内网穿透工具后。用你的内网穿透地址,和工程的请求地址的 URL 配置到公众号接口里。配置的时候会进行验签,验签成功则配置成功。 +4. 你还要扫描关注`测试号二维码`,这样才能看到测试信息。 + +#### 4.2 验签服务 + +**源码**:`cn.bugstack.xfg.dev.tech.trigger.http.WeixinPortalController` + +```java +@Slf4j +@RestController() +@CrossOrigin("*") +@RequestMapping("/api/v1/weixin/portal/") +public class WeixinPortalController { + + @Value("${weixin.config.originalid}") + private String originalid; + @Resource + private Cache openidToken; + + /** + * 验签,硬编码 token b8b6 - 按需修改 + */ + @GetMapping(value = "receive", produces = "text/plain;charset=utf-8") + public String validate(@RequestParam(value = "signature", required = false) String signature, + @RequestParam(value = "timestamp", required = false) String timestamp, + @RequestParam(value = "nonce", required = false) String nonce, + @RequestParam(value = "echostr", required = false) String echostr) { + try { + log.info("微信公众号验签信息开始 [{}, {}, {}, {}]", signature, timestamp, nonce, echostr); + if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) { + throw new IllegalArgumentException("请求参数非法,请核实!"); + } + boolean check = SignatureUtil.check("b8b6", signature, timestamp, nonce); + log.info("微信公众号验签信息完成 check:{}", check); + if (!check) { + return null; + } + return echostr; + } catch (Exception e) { + log.error("微信公众号验签信息失败 [{}, {}, {}, {}]", signature, timestamp, nonce, echostr, e); + return null; + } + } + + /** + * 回调,接收公众号消息【扫描登录,会接收到消息】 + */ + @PostMapping(value = "receive", produces = "application/xml; charset=UTF-8") + public String post(@RequestBody String requestBody, + @RequestParam("signature") String signature, + @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, + @RequestParam("openid") String openid, + @RequestParam(name = "encrypt_type", required = false) String encType, + @RequestParam(name = "msg_signature", required = false) String msgSignature) { + try { + log.info("接收微信公众号信息请求{}开始 {}", openid, requestBody); + // 消息转换 + MessageTextEntity message = XmlUtil.xmlToBean(requestBody, MessageTextEntity.class); + + // 扫码登录【消息类型和事件】 + if ("event".equals(message.getMsgType()) && "SCAN".equals(message.getEvent())) { + // 实际的业务场景,可以生成 jwt 的 token 让前端存储 + openidToken.put(message.getTicket(), openid); + return buildMessageTextEntity(openid, "登录成功"); + } + + log.info("接收微信公众号信息请求{}完成 {}", openid, requestBody); + return buildMessageTextEntity(openid, "测试本案例,需要请扫码登录!"); + } catch (Exception e) { + log.error("接收微信公众号信息请求{}失败 {}", openid, requestBody, e); + return ""; + } + } + + private String buildMessageTextEntity(String openid, String content) { + MessageTextEntity res = new MessageTextEntity(); + // 公众号分配的ID + res.setFromUserName(originalid); + res.setToUserName(openid); + res.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L)); + res.setMsgType("text"); + res.setContent(content); + return XmlUtil.beanToXml(res); + } + +} +``` + +- 验签和接收公众号回调,是一个固定的代码,同时验签和接收公众号回调也都是同一个接口名字,只是一个是 get 请求,另外一个是 post 请求。验签地址:`http://xfg-studio.natapp1.cc/api/v1/weixin/portal/receive` 你需要更换为你的内网穿透域名地址。 +- 在接收公众号回调中,有一块固定的代码。接收公众号消息类型为事件,事件类型为扫码(SCAN),从中可以获得 ticket 这个唯一凭证。 +- 验证登录时,简单模拟写入到缓存中。`openidToken.put(message.getTicket(), openid);` 实际的业务场景会转换为登录的 jwt token 数据。 + +#### 4.3 内网穿透 + +
+ +
+ +- 内网穿透工具,购买一个12元的付费隧道。[https://natapp.cn/](https://natapp.cn/) +- 购买后,配置你的隧道本地端口为 8091 也就是你本地 SpringBoot 程序的端口。`如果你不是 8091 端口,可以修改为其他的` +- 软件下载,内网穿透需要一个本地的软件。你可以从它的网站下载。[https://natapp.cn/#download](https://natapp.cn/#download) 各个版本也都支持,里面也有相关的使用教程。 +- 安装软件后,启动 natapp 和应用,就可以把你的地址配置到上面了。 + +## 四、功能验证 + +### 1. 启动 SpringBoot 服务 + +```java + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.7.12) +24-02-25.17:13:00.372 [main ] INFO EndpointLinksResolver - Exposing 1 endpoint(s) beneath base path '/actuator' +24-02-25.17:13:00.405 [main ] INFO Http11NioProtocol - Starting ProtocolHandler ["http-nio-8091"] +24-02-25.17:13:00.440 [main ] INFO TomcatWebServer - Tomcat started on port(s): 8091 (http) with context path '' +24-02-25.17:13:00.461 [main ] INFO Application - Started Application in 4.268 seconds (JVM running for 4.941) +``` + +### 2. 启动内网穿透 + +
+ +
+ +- 从 [natapp.cn](https://natapp.cn/tunnel/lists) 你的隧道中获取 authtoken 配置到本地你下载的软件里。【我下载的mac版本】 +- 之后你可以双击启动,也可以通过`./natapp 启动` + +### 3. 获取二维码 + +#### 3.1 获取 ticket 凭证 + +
+ +
+ +访问接口:[http://xfg-studio.natapp1.cc/api/v1/login/weixin_qrcode_ticket](http://xfg-studio.natapp1.cc/api/v1/login/weixin_qrcode_ticket) - 你需要替换为你的地址。 + +#### 3.2 生成二维码 + +
+ +
+ +访问接口:[http://xfg-studio.natapp1.cc/api/v1/login/check_login](http://xfg-studio.natapp1.cc/api/v1/login/check_login) - 你需要替换为你的地址。 + +### 4. 扫码登录 + +使用微信扫描二维码,观察服务端日志和手机提示。 + +
+ +
+ +```java +24-02-25.17:25:09.096 [http-nio-8091-exec-3] INFO LoginController - 生成微信扫码登录 ticket gQHN7zwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAycTRMbnB4TDBjckcxT043cjFCMWoAAgR1B9tlAwQ8AAAA +24-02-25.17:25:18.793 [http-nio-8091-exec-5] INFO WeixinPortalController - 接收微信公众号信息请求or0Ab6ivwmypESVp_bYuk92T6SvU开始 + +1708853118 + + + + + +``` + +- 扫码登录后,可以看见反馈的状态信息以及服务端的日志。 + +### 5. 循环模拟登录 + +
+ +
+ +- 访问接口:[http://xfg-studio.natapp1.cc/api/v1/login/check_login](http://xfg-studio.natapp1.cc/api/v1/login/check_login) - 你需要替换为你的地址。 +- 好,到这表明已经登录成功,并返回openid信息。自己的业务场景,可以返回一个 token 保存到浏览器。 + +## 五、加入学习 + +**注意**,加入星球「码农会锁」即可学习小傅哥所有实战项目,星球类似于私有技术朋友圈,小傅哥是技术群主,为大家提供1v1的技术学习答疑。 + +>🧧项目地址:https://gaga.plus - 进入查看星球8个实战项目,早点学习,冲击Offer! diff --git a/docs/md/project/group-buy-market/group-buy-market.md b/docs/md/project/group-buy-market/group-buy-market.md new file mode 100644 index 000000000..a25927e41 --- /dev/null +++ b/docs/md/project/group-buy-market/group-buy-market.md @@ -0,0 +1,149 @@ +--- +title: 拼团支付平台系统 +lock: no +--- + +# 《拼团交易平台系统》- 为各类交易场景,提供拼团服务。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/Yfbwo](https://t.zsxq.com/Yfbwo) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +**耗时9个月制作,48节课程,微服务分布式架构**,`前后端 + DevOps + AI MCP(场景运用)`,全栈式编程 + 全程视频 + 全套的技术小册,手把手👨🏻‍💻教学,终于完结啦!🎉 + +
+ +
+ +**讲道理,做项目,就得做公司里真实有的业务。** + +我在非常多的互联网业务应用中,都看到过这样一种产品形态,它就是**拼团交易营销场景**,如;`拼多多/京东购物🛒`、`滴滴拼券🧧`、`腾讯开团抢购服务器☁️`、`美团团购☕️`等等,都是以拼团方式增强交易单量的业务场景。所以,如果想面试互联网公司,还是要优先考虑做一些这样的真实场景业务,这样才能和面试官有东西可以讲,而不是什么都没的聊o(╥﹏╥)o。 + +此套项目,小傅哥已提供了对应的`简历模板`、`面试问题(一直梳理中)`,还有 AI MCP 场景的加入,通过 AI MCP 对接 ELK + 普罗米修斯监控,Ai Agent 智能化分析系统的日志和运行情况。 + +**本项目,和你之前学习过的一些项目最大的区别,就是!这是真实的业务,全核心流程覆盖,有非常好的架构和编码设计。这块学习后,进入公司完全可以跟着大家一起做需求,而不是瑟瑟发抖的不知道自己要做啥!** + +
+ +
+ +> 文末有加入学习方式,提供全套的课程代码、文档、视频,此外还有额外的16个实战项目一起获取! + +## 一、能学到啥 + +该项目是互联网toc场景的核心业务流程,以真实业务作为背景,实际可上线,可运行为目标,进行系统的需求分析、架构设计、功能实现,过程涵盖设计模式的运用,解决复杂场景问题。让大家感受到,🐂牛逼的代码,从来不是一顿写CRUD! + +- 【前端】以 Ai Agent 设计前端 UI,包括 HTML、Div、CSS 等前端编程技术。 +- 【前端】掌握 fetch 方式对后端接口的调用,处理相关的逻辑数据。 +- 【后端】熟练搭建项目工程,学习工程分层结构概率和设计思路。掌握更多的六边形、洋葱、整洁架构。`提高简历技术亮点` +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对框架源码所提供的扩展接口具备运用能力。 +- 【后端】熟练使用 SpringCloud 核心微服务分布式技术栈,包括:Fegin、Sentinel、Nacos、熔断、限流、降级等。`先做功能,后面逐步添加。` +- 【后端】熟练使用大厂中常用的设计模式手段和设计原则技术,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。 +- 【后端】深度学习复杂场景的架构设计、编程思维,如果处理系统功能的边界和上下文的维护。—— 这些东西一定是从实践中才能学习到的。 +- 【后端】熟练使用 Mock 单测工具、JMeter 压测工具,增强代码交付质量。 +- 【后端】熟练掌握异常、枚举、错误码的定义和使用,并学习到如何合理打印服务日志,便于问题排查。 +- 【后端】以通用场景设计为目的,提取共性逻辑为通用的设计框架,涵盖;动态配置、设计模式(规则树、责任链)、限流。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练使用 Nginx 配置转发服务,并能申请ssl配置https服务。 +- 【运维】熟练使用 Grafana 监控系统 + AI MCP,对系统的 JVM、磁盘、Tomcat、应用(QPS、响应时间、调用量)完整监控。 + +此外,小傅哥已对全过程都做了需求的分析、功能的设计、编码的精细化处理,让你吃上细糠!也通过这种方式,真正的把你的技术储备整起来,以后进入到公司都是嘎嘎强的选手! + +## 二、运行展示 + +整个拼团交易的流程非常细腻,包括了,一套小型支付 + 拼团营销平台的完整对接,涵盖;`验签`、`扫码/无痕登录`、`试算`、`锁单`、`支付+结算`、`退单+退单`的完整链路流程。如图; + +
+ +
+ +
+ +
+ +
+ +
+ +前端页面体现了全部核心流程,看着前端不是太多,但后端支撑整套系统了,写了**1.38万行代码**! + +>体验地址(部分功能):[http://117.72.38.125:3080/index.html](http://117.72.38.125:3080/index.html) + +## 四、后端设计 + +### 1. 分层架构 + +
+ +
+ +- 整个系统是一个微服务分布式架构设计,通过两套系统的对接,体现微服务的全流程处理关系。 +- 下面是系统中用到的核心技术栈,框架、组件、监控、部署、发布、上线,可以说是非常全面。 + +### 2. 工程结构 + +
+ +
+ +- 拼团和交易系统,以面向对象的思维,划分出领域结构。活动域、标签域、交易域、鉴权域、商品域、订单域。 +- 两套系统通过 http/rpc(可配置对接)、mq(RabbitMQ)进行同步和异步交互,因为配有本地消息表,所以可以保证最终一致性。 +- 这里有非常精妙的编码设计,如;工厂模式、组合模式、策略模式(含枚举策略)、责任链、抽象类等,又提供了 Supplier 函数式编程,可以说是应有尽有! + +### 3. 库表数据 + +
+ +
+ +- 一看库表就知道,这不是小儿科!有标签表,有活动的优惠,组队,订单明细,本地消息表,商品活动配置表,sku表。 + +### 4. 用户旅程 + +
+ +
+ +- 拼团全流程简图,以用户旅程来看各个节点所做的事项。 + +### 5. 场景举例 - 设计模式 + +#### 5.1 试算 + +
+ +
+ +
+ +
+ +为了解决整个交易过程的复杂场景,做了一套通用设计模式框架,并结合一套异步数据加载的多线程设计,来解决加载数据效率问题。让整个框架的灵活性非常高。 + +#### 5.2 退单 + +
+ +
+ +- 第一条退单链路,以工厂🏭方式获取执行责任链,责任链的作用是拆分原有的流程结构,分节点进行逐步处理。之后到退单的具体操作,则根据枚举策略,拿到对应执行的退单策略模式,完成退单动作。退单执行后发送MQ消息,驱动后续流程。 +- 第二条消息消息,从接收 MQ 开始,以 MQ 消息中的策略类型进行库存恢复操作。这部分保持原有的走对应的策略即可。 + +>课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! + +## 四、加入学习 + +跟着小傅哥学习,不会浪费时间,不会走小道弯路。全程做技术兜底,遇到的各种问题都能帮你解决。包括你学习时候的代码bug,可以把代码提交到星球,我来帮你调试。最终把兄弟们送到各个竞争赛道的头部,拿到最牛的薪资待遇 Offer! + +
+ +
+ +>🧧 [加入](https://bugstack.cn/md/zsxq/other/join.html) 每年招聘,都能帮助兄弟们规划好学习路线,卷出最好的 Offer!星球里还有500份评审过的简历记录,看过后,就知道怎么写好简历了。 diff --git a/docs/md/project/group-buy-market/none.md b/docs/md/project/group-buy-market/none.md new file mode 100644 index 000000000..f64218b2f --- /dev/null +++ b/docs/md/project/group-buy-market/none.md @@ -0,0 +1,15 @@ +--- +title: 新章节,编写中 +lock: no +--- + +# 新章节,编写中 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +... 新章节,编写中 \ No newline at end of file diff --git a/docs/md/project/group-buy-market/notes.md b/docs/md/project/group-buy-market/notes.md new file mode 100644 index 000000000..ddaf40e38 --- /dev/null +++ b/docs/md/project/group-buy-market/notes.md @@ -0,0 +1,173 @@ +--- +title: 面试:技能、简历、问题汇总 +lock: no +--- + +# 《拼团交易平台系统》,关于面试中的技能、简历、问题汇总 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/Yfbwo](https://t.zsxq.com/Yfbwo) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +此部分主要用于向读者提供星球项目之一的《拼团交易平台系统》项目如何体现到简历中,包括;专业技能、项目经验。 + +## 一、项目介绍 + +你可以根据是实习、实践、导师任务、学校课程、自己学习,几个方向来描述项目来源。 + +举例; + +面试官你好,拼团交易平台系统,是我在日常使用`拼多多`、`腾讯`、`京东`等服务平台,交易支付时候,了解到这样的一种营销手段。它可以通过用户自传播方式增强交易量,也是拼多多最开始起家形成巨大规模的一个业务逻辑。因此非常感兴趣这样的系统,所以根据大厂分享的资料、与对应的架构师UP进行交流学习了,设计了这样一套系统。 + +该系统采用了 DDD 领域驱动设计进行建模,拆分领域模块边界,形成;活动领域、人群领域、交易领域,来构建拼团营销交易流程,达到试算、锁单、结算等步骤流程。这个过程中提炼了通用设计模式,规则树、责任链,可以非常有效的统一的治理流程编排实现。 + +## 二、简历模板 + +注意:🙅🏻‍ 不要直接复制粘贴简历模板内容!视频提供了 DeepSeek AI 方式编写简历,可以参考。 + +- **项目名称**:`拼团营销服务系统`、`交易营销场景 - 拼团系统`、`营销拼团交易平台`、`仿拼多多/腾讯/京东,拼团玩法系统(以大厂项目为背书)`、`拼团外卖平台系统(结合其他项目一起组合)` - 参考以上方式编写自己的项目名称,尤其拼团还是一个微服务,可以和很多其他系统组合。 + +- **项目架构**:`微服务设计`、`分布式架构`、`DDD 领域驱动设计 + 六边形分层架构实现`、`前后端分离技术` + +- **核心技术**:SpringBoot、MyBatis、MySQL、Guava、Redis、RabbitMQ、动态配置中心(DCC)、普罗米修斯监控、Docker等 - 如果学习了其他技术栈也可以补充。 + +- **项目描述**: + - 方式1(以学习视角介绍):本项目参考`拼多多`交易购物拼团场景,调研中大厂相关营销业务场景和技术架构方案,设计实现了本套拼团营销服务系统,支持各类营销优惠(直减、折扣、N元购)。该系统以面向对象开发,运用 DDD 拆分领域边界,使用设计模式设计服务功能。提高系统的扩展性和可维护性。 + - 方式2(以提供服务介绍):该项目以`拉动/促进/提高`(小型支付商城/外卖点餐/购票出行/...)交易单量为目标,通过设计拼团优惠组队下单为手段,达到增强用户自传播分享私域提高整个交易GMV的结果。三段式描述,`...目标,...手段,...结果` + - 方式3(以实际场景介绍):该项目是以促进Xxx公司Xxx场景的核心营销优惠玩法系统,围绕公司的xxx、yyy、zzz等全部交易业务,设计通用的拼团优惠锁单和组队结算回调服务。此系统分布式架构设计,可支撑单机压测量 xxx tps,tp99 xxx 的数据指标,有效的满足公司的全量的业务场景接入使用。 + +- **核心方案**: + + - **架构设计**,以 DDD 领域驱动设计,四色建模方式,按照系统功能流程,拆解服务边界。包括;活动域、标签域、交易域。 + - **设计模式**,设计并提炼通用的`责任链`、`规则树`模型框架,解决领域场景中多处,需要使用设计模式解耦复杂流程链路的调度(避免过多的if...else判断)。鉴于多处场景的责任链使用,模块框架设计责任链为执行和链路分离组装,便于工厂可以组合出各类执行责任链,不被不同的链路管理影响(以往的责任链,一般是单例的,会被影响)。 + - **规则过滤**; + - (举例)以拼团试算场景举例,运用通用设计模式模型框架,完成试算;根节点、切量开关、营销折扣、人群标签、异常兜底等流程串联。设计这样解耦设计,极大的提到了程序的可扩展性。 + - (举例)以拼团锁单场景举例,拼团锁单场景,使用通用的责任链模型框架,校验活动的有效性(状态、有效期)和用户的参与资格。 + - (举例)以拼团结算场景举例,拼团结算场景,使用通用的责任链模型框架,校验渠道黑名单配置、拼团组队信息、交易时间属性、订单有效状态等。 + - (举例)以拼团试算场景举例,在查询优惠配置数据时候,抽象出模板结构,使用 Supplier 函数式编程,设计动态降级、缓存数据和 dao 的后置执行操作。通用模板的设计让所有场景更容易接入。`ActivityRepository#queryGroupBuyActivityDiscountVO` + + - **异步线程**,为提高用户体验,将拼团优惠试算所需的营销类数据加载,由串行改为异步线程并行执行。此执行方式由通用设计模式模型框架提供。(如果由引入星球的动态线程池,也可以在这里增加线程池的管理描述) + - **功能方案**; + - (举例)通过 Redis 发布订阅模型,结合 Spring AOP 切面和代理,以自定义注解的方式控制属性信息动态配置。减少系统与 Redis 的 IO 交互,提高对高频场景属性值的使用时间效率。 + - (举例)设计拼团组队结算的 HTTP、MQ 双重手段,满足外部应用和内部微服务的不同方式对接,增强系统的适配性。同时为了保证整体方案的可靠性,在结算触达时,先异步多线程方式即时触发回调(HTTP、MQ),再通过业务一致性任务数据补偿校验。(MQ、HTTP,都可能因网络原因导致失败,因此需要重试)任务的触达,还增加多分布式锁,让任务互备抢占方式执行,增强系统的鲁棒性设计。 + - (举例)设计 Redis BitSet/BitMap 人群标签,用于过滤可见和可参与,拼团活动的人群信息。该人群标签可依赖于过往用户数据(交易下单)通过 job 任务完成人群标签的录入。 + - (举例)通过策略模式,设计拼团折扣(MJ、ZJ、NYG)的计算策略。同时折扣的计算也会通过人群标签过滤,以满足运营策略配置,降低活动风险。 + - (举例)运用 retrofit2/okhttp3/spring cloud fegin + nginx 负载,对接拼团交易平台锁单服务,以及通过 http 回调和 MQ 监听来处理交易结算。 + - (举例)`三阶段实现的内容`,通过独占锁处理互备任务抢占执行回调,确保在同一时刻有一个运行的回调任务,提高系统的鲁棒性设计。 + - (举例)`三阶段实现的内容`,设计Redis无锁化拼团库存抢占和恢复库存处理,减轻数据库行锁独占的压力,提高系统吞吐量。 + - (举例)`三阶段实现的内容`,抽象通用函数式缓存分级设计,并结合扳手工程DCC动态配置,处理缓存降级到DB设计。 + - (举例)`三阶段实现的内容`,结合 RateLimiter + DCC 动态配置,实现动态限流配置。 + - (举例)`三阶段实现的内容`,以 Ai MCP + ELK + 普罗米修斯监控,以 Ai Agent 智能体方式,分析错误日志和异常监控,动态化展示监控报表。 + - (举例)`三阶段实现的内容`,通过枚举策略,设计多种类型退单(未支付&未成团、已支付&未成团、已支付&已成团),并通过回调处理退单退款。 + +> 不要局限于以上的描述,可以结合 Ai + 喂进去的信息,给你描述出属于你独一无二的简历描述。这样更有益于你的面试。 + +## 三、面试问题 + +### 1. 为什么需要拼团平台使用http或rpc,mq对接商城平台,而不是二者放到一起? + +http、rpc,属于即时性调用,立即反馈结果的场景。mq 用于异步驱动,流程解耦。而拼团组队的场景,组队是需要多人参与和支付的情形,只有统一完成拼团后,才能 mq 驱动后续流程。而不是一开始 http 请求就能立马拼团组队完成。 + +### 2. 为什么做了微服务的拆分,拆分的依据是什么?为什么拆分了微服务之后还要拆分领域?可不可以每个领域都作为一个微服务? + +微服务的目的是划分大的系统边界,拆分原则可以包括;按业务功能拆分、按数据模型拆分、按团队结构拆分、按技术特性拆分、按变更频率拆分,但不要过度细化的拆分,避免造成分布式系统过度复杂性。 + +而对于拼团场景是一个独立的营销玩法,可以被拆分成一个独立的微服务。这样迭代、维护、上线,也都会更加轻量,也便于与其他平台对接。之后对于拼团内的服务模块,都以支撑拼团为主,符合最新当前的诉求进行设计。当然没有永远的适合,如果将来拼团变得更大,需要支撑的场景更多,也会考虑做模块的微服务拆分。 + +### 3. 优惠试算使用了多线程异步加载,为什么这里不用缓存? + +试算加载的是当前用户行为的最新数据,而缓存则不适用于此场景。当然如果试算中有一些不频繁变化的偏固定的配置类数据,则可以通过缓存处理。另外,在公司(美团、京东、字节、滴滴等)拼团场景则更为复杂,试算时候需要的数据量更多,多线程则会更体现出必要性。 + +### 4. 平台在高并发下的扣减库存和防止超卖是怎么做的? + +这一块的库存扣减使用的是无锁化设计,setnx 兜底。但如果是加分布式锁,则会出现排队问题,达不到最大并发的效果。而因库存使用了 redis 计数 + 锁,以及添加了幂等恢复量,所以不会有超卖问题。 + +### 1. 项目设计与架构 + +1. **为什么选择DDD领域驱动设计?如何划分领域边界?** + **答**:DDD能有效解决复杂业务逻辑的拆解问题,通过四色建模和业务场景分析,划分出活动域(管理拼团规则)、标签域(用户画像和权限过滤)、交易域(订单和结算)。例如,拼团锁单流程属于交易域,而人群标签过滤属于标签域。 +2. **微服务间如何通信?如何保证数据一致性?** + **答**:对内外对接系统,分别采用HTTP(Feign/RestTemplate)和 MQ(RabbitMQ)方案。关键链路(如订单结算)通过MQ保证最终一致性,结合本地事务表+补偿任务(如定时检查未完成的结算请求)。 +3. **六边形架构如何落地?解决了什么问题?** + **答**:通过适配器层隔离核心业务与外部依赖(如数据库、Redis)。例如,订单结算的核心逻辑独立于HTTP回调或MQ监听的具体实现,提升核心代码的稳定性和可测试性。 + +> DDD 是一个亮点,中大厂公司都在推进 DDD 的项目重构。站在技术角度,这样的架构更好维护。站在领导角度,这样的拆分可以更好了解系统设计便于制订KPI。同时有清晰的业务领域划分,AI 开发工具可以更好的结合进来。[招聘里DDD的体现,jump 🏃🏻](https://bugstack.cn/md/zsxq/memorabilia/job-hire-jd.html) + +### 2. 核心技术实现 + +1. **Redis在项目中如何应用?举例说明** + **答**:1)BitMap存储用户标签(如是否参与过某活动);2)分布式锁控制拼团组队结算触达并发;3)缓存活动配置(如有效期、折扣规则),降低数据库压力。 + +2. **责任链模式如何解耦复杂流程?举例说明** + **答**:拼团锁单流程拆解为多个处理器链:活动状态校验→用户资格校验→库存检查。每个处理器独立实现,通过工厂模式动态组装,避免if-else嵌套。 + +3. **异步线程如何优化性能?如何管理线程池?** + **答**:将营销数据加载从串行改为并行(如使用CompletableFuture)。通过动态线程池监控任务队列和拒绝策略,结合普罗米修斯采集指标,避免线程池耗尽。`注意多准备下多线程、线程池的八股` + +### 3. 核心业务场景 + +1. **拼团结算的HTTP和MQ双重回调如何设计?如何保证可靠性?** + **答**:1)结算后同时发送HTTP请求和MQ消息;2)异步线程池处理回调,失败后进入重试队列;3)定时任务补偿未完成回调,配合分布式锁避免重复执行。 +2. **人群标签如何通过BitMap实现?举例说明** + **答**:例如,用户ID哈希后映射到BitMap的某一位。运营配置“仅限新用户”的活动时,Job任务扫描历史订单,将老用户对应位标记为0,查询时通过`BITCOUNT`判断资格。 +3. **策略模式在折扣计算中的应用?如何扩展新策略?** + **答**:定义接口`DiscountStrategy`,实现类 `MJCalculateService`(满减)、`NCalculateService`(N元购)、`ZJCalculateService`(直减)、`ZKCalculateService`(折扣)。新增策略时只需添加实现类并注册到Spring上下文,通过策略工厂按类型调用。 + +### 4. 高并发与容错(这部分会在第3阶段加入) + +1. **如何解决库存超卖问题?** + **答**:1)Redis原子操作(DECR)预扣库存;2)数据库最终扣减时加乐观锁;3)异步补偿任务回滚异常订单。 +2. **分布式锁的实现方案?遇到过哪些坑?** + **答**:基于Redis的Redisson(看门狗机制续期)。注意点:1)锁粒度细化(按活动ID+商品ID);2)避免锁过期后业务未执行完,需结合版本号校验。 +3. **如何设计熔断降级策略?** + **答**:Sentinel监控外部服务(如支付接口)的异常比例,超阈值时熔断,降级为返回默认错误码或缓存数据,并记录日志供补偿任务处理。 + +### 5. 监控与运维(这部分会在第3阶段加入) + +如配置说明接入普罗米修斯监控,同时也可以使用 arthas、dump mat。地址:[https://bugstack.cn/md/road-map/grafana.html](https://bugstack.cn/md/road-map/grafana.html) + +1. **普罗米修斯监控哪些指标?如何定位性能瓶颈?** + **答**:监控接口TP99、线程池活跃度、Redis命中率、MQ堆积量。通过Grafana仪表盘分析慢SQL(MyBatis拦截器采集)或高耗时责任链节点。 +2. **动态配置中心如何实现?如何保证实时性?** + **答**:基于Nacos/Zookeeper,配置变更时通过Spring Cloud Bus通知服务。关键配置(如活动开关)结合本地缓存,通过@RefreshScope实时生效。 +3. **Docker化部署的优化经验?** + **答**:1)多阶段构建减小镜像体积;2)JVM参数调优(-Xmx限制内存);3)健康检查接口 skywalking、artash 探针结合,实现系统监控。 + +### 6. 设计模式与代码规范 + +1. **规则树模式如何实现?举例说明** + **答**:根节点为入口,子节点为具体规则(如切量、标签过滤)。每个节点实现`RuleNode`接口,通过组合模式构建树形结构,支持动态扩展节点。 +2. **如何避免策略模式带来的类膨胀问题?** + **答**:1)将策略实现类定义为无状态Bean,复用实例;2)通过注解+自动扫描注册策略;3)策略参数化配置,减少重复代码。 +3. **AOP在项目中的典型应用场景?** + **答**:1)DCC 动态配置中心;2)@LogTrack 记录核心链路日志(切面加日志,可以让 DeepSeek 写个案例); + +### 7. 扩展性与业务,你怎么设计新功能? + +1. **如何支持多种拼团类型(如老带新、阶梯团)?** + **答**:抽象拼团模板(Template Pattern),定义成团条件接口(如人数满额、金额达标)。新增类型时实现接口,并通过工厂模式注入。 +2. **如何设计活动预热机制?** + **答**:1)活动开始前定时任务加载配置到Redis;2)缓存热门活动的商品信息;3)通过压测工具预热JVM和线程池。 +3. **如何实现灰度发布?** + **答**:1)Apollo配置中心按用户ID百分比切流;2)网关层根据请求头路由到新老服务;3)结合Prometheus监控异常,快速回滚。 + +### 8. 综合问题 + +1. **项目中最大的挑战是什么?如何解决?** + **答**:高并发下Redis雪崩。解决方案:1)缓存分层(本地缓存+Redis);2)热点数据预加载;3)随机过期时间。 +2. **如果让你重构系统,会优化哪些点?** + **答**:1)引入分库分表解决订单表膨胀;2)增加AI + RAG + MCP 提供智能分析和运营服务;3)使用Guava本地缓存,管理复杂数据结构。 +3. **如何向非技术人员解释系统设计?** + **答**:类比“组队购物”,系统像智能管家:1)自动匹配规则(如折扣);2)确保组队不超时;3)失败时自动重试,保证最终成功。 + +### 9. 人群标签 + +人群标签这一部分该怎么回答? + +在互联网公司里,量化数据分析师(经营分析)会把各类系统工程的数据采集到统一大数据集群,之后通过 Python、R 语言,建一个数据模型的方式跑数。比如 xxx 类人群,购物喜好、收藏喜好、用券喜好、高频下单、回购,以及从第三方获取的数据(键盘输入)、浏览器帖子等。根据这些数据定义一个量化模型,做一个跑数作业,之后这些数据,可以写成人群标签。标签为bitmap(小型只要判断不存在的),以及直接记录用户id到redis的,更准确的判断人是否存在。之后研发引入数据包到系统中(也就是 redis 对接)之后使用。 + +>关于系统面试问题,涉及到的监控、数据、指标类,最好使用云服务部署上线 + 普罗米修斯监控完成压测和优化。 diff --git a/docs/md/project/group-buy-market/promotion/group-buy-market-v1.md b/docs/md/project/group-buy-market/promotion/group-buy-market-v1.md new file mode 100644 index 000000000..7164dcd4b --- /dev/null +++ b/docs/md/project/group-buy-market/promotion/group-buy-market-v1.md @@ -0,0 +1,93 @@ +--- +title: 新"拼多多"系项目,编码3千行,进度35%,设计模式拉满! +lock: no +--- + +# 新"拼多多"系项目,编码3千行,进度35%,设计模式拉满! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +有道理,**不能再做大号的CRUD啦!** 这玩意没成长,面试也没得聊呀!不少伙伴在选择学习项目的时候,没有仔细看对应的项目的架构、设计、模型,一上来就是从头到尾的CRUD串流程,写到最后既没有编程思维的提升,也没有编码能力的积累。所以,面试贼吃亏! + +
+ +
+ +**面试官:”我们想要的不是CRUD仔!“** + +能作为面试官的,往往是这个团队里技术不错,级别较高的。这些人面试不只是看你对业务的理解,还包括非常重要的`架构知识`、`模型设计`、`技术技巧`这样内容项的积累。 + +而小傅哥带着大家做项目,也是一直秉承着一贯的高质量项目设计和编码落地。让大家学习时不只是简单的理解业务流程,还包括;怎么拆分业务边界、怎么进行工程建模、怎么驾驭设计模式、怎么处理复杂场景。所以大家学习后的反馈是:`”真爽,就像看爽文一样,每个项目都让我有非常高的提升!”` + +所以,在这次的新项目中,你又会看到不一样的设计模式技巧的运用,来拆解复杂的业务流程。—— 全项目从0到1,全程手把手视频+小册,冲起来非常爽。 + +## 一、类“拼多多”项目 + +本次小傅哥带着大家做的项目,是一个类“拼多多”的《拼团交易平台系统》,这类项目都可以被归类为营销系列系统。在各个互联网公司属于通用类的业务场景,也是流量规模最大,系统最为复杂的系统。所以学习起来收获也是非常大的。 + +
+ +
+ +- 看着页面挺简单,其实服务端一点也不好整。😂 这样一套东西的实现,需要非常好的设计,才能驾驭的了不断的需求迭代。 +- 有时候看到网上简单几行的服务端代码也能实现这样一个类似的流程,不过那东西就是闹玩的小儿科,实际公司里压根不会那么干。学习项目的时候,总得要知道公司里,`真实的业务模型` + `不错的架构设计` + `良好的编码实现`,缺一不可,否则跟没学也差不多。 + +## 二、编码3千行,进度35% + +《拼团交易平台系统》整个项目分为;`系统设计`、`服务实现`、`外部对接`、`开发运维`,目前系统设计3节已完成,服务实现开发了5节。😋 嘿嘿,马上25年的元旦和春节假期来了,又可以嘎嘎往前冲进度了! + +
+ +
+ +- 课程地址:[https://bugstack.cn/md/project/group-buy-market/group-buy-market.html](https://bugstack.cn/md/project/group-buy-market/group-buy-market.html) +- 进度说明:课程于11.11日启动,平均每周更新1节,遇到放假的时候就会加更。基本奔着25年的3-4月交付出一个可运行部署和写简历的版本。现在跟进学习,到时候简历就又有一个新项目啦! + +## 三、不只是完成功能! + +其实每个项目的学习,不只是学个流程,还要学它的系统设计。看看这样一个当前的场景下,做了哪些边界的拆分和功能逻辑的模型处理。从这部分开始才能体现出对项目的深度理解。 + +### 1. 系统建模 + +
+ +
+ +- 项目学习时,在编码前,会进行模型结构的分析「这部分有视频教程」。教大家用面向对象的思维理解系统是如何拆分边界的。 +- 当你有了更高的视角俯视系统,在到后面去开发时,就会有非常爽的编码指引。 + +### 2. 设计模式 + +
+ +
+ +- 每一节功能实现时,会帮大家理解和运用设计模式,让你知道拆分边界的重要性,以及如何做出高质量的编码。 +- 设计模式理解(你可以在这里补充学习,到项目中都是实战):[https://bugstack.cn/md/develop/design-pattern/2024-08-25-chain-tree.html](https://bugstack.cn/md/develop/design-pattern/2024-08-25-chain-tree.html) + +### 3. 工程结构 + +
+ +
+ +- 如工程结构全程视频手把手的教你学习逻辑功能的编码实现,而且这里会运用如模型设计拆分边界并进行编码功能逻辑实现。 +- 注意;编码时会设计出非常合适的模型,拆分功能逻辑边界。有了这些的积累,以后面试可以讲好长时间,在工作中也能运用起来。 + +## 四、加入学习 + +小傅哥的项目是成体系的,有不同的难度进阶。你可以循序渐进的学习,逐步的从小白成长为技术大拿。如图; + +
+ +
+- 首先,这一整套全体系的学习课程比私教培训实惠,更比培训班上万的培训费便宜。也就是培训班1天的💰,就能学习到这一整套内容了。 +- 之后,你学习的整套课程,就是小傅哥这个架构师自己全部原创编写的。这也就是说,你所提到的任何问题,小傅哥都能给你解答和讨论。 +- 那么,这么实惠的课程,成体系的课程,还是架构师编写的。还有什么可犹豫的,完全可以撸起来了! + +> 🧧优惠券,关注小傅哥公众号「码农会锁」回复「星球」获得,数量有限,先到先得! diff --git a/docs/md/project/group-buy-market/promotion/group-buy-market-v2.md b/docs/md/project/group-buy-market/promotion/group-buy-market-v2.md new file mode 100644 index 000000000..2150d69dc --- /dev/null +++ b/docs/md/project/group-buy-market/promotion/group-buy-market-v2.md @@ -0,0 +1,247 @@ +--- +title: 总结通用模型设计提取,设计模式玩的贼6! +lock: no +--- + +# 《拼团交易平台系统》总结通用模型设计提取,设计模式玩的贼6! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是小傅哥。 + +近几周在做公司的一个项目重构,有多复杂呢!`整个工程近40万行代码`,`重构部分单个类超过3000行`。历史最长周期的代码有`快10年以上`。而这东西到我手了,再继续这么写,早晚搞出事故!**所以,我决定改变一下。** + +
+ +
+ +**你不可能停了业务需求再重构!** + +在互联网公司中,产品需求交付永远是第1位的,他们不关心你代码是怎么实现的。但不要出问题,出了问题就是你的问题。 + +而长期需求迭代经历数年的代码,早已腐化的非常严重。但重构又绝对是非常考验工程师的业务经验和技术能力的。**往往重构不佳,就从维护一份屎山,到维护两份啦!** + +但好在,我的工程落地经验积累较多,通过巧妙的设计模式运用,能从复杂的流程中划分出边界结构,从而降低业务功能的理解难度。与此同时,为了让其他的伙伴在不是特别能熟练使用设计模式的情况下,我把设计模式常用的模型结构,独立成一个单独的组件包,主要引入就可以按照这样的结构来开发代码。从一定程度上,让大家的代码都能在不错的水平上。 + +>如果,你也想提高自己的代码能力和工程驾驭能力,那么可以跟着小傅哥一起学习下。 + +## 一、实践出真知 + +编程这东西,最好的学习方式就是实践。拿一个真实项目,结合实际的需求,上手操作一遍,小白也能扎扎实实的学习到实际的技术运用。 + +所以,小傅哥为了让大家能学习到,关于工程中运用设计模式,解决实际场景的重构问题。专门提供了一个新的项目,在项目中解耦出标准的通用设计模型结构,再到需求中运用处理实际问题。 + +这个项目就是小傅哥的技术社群,星球「码农会锁」,最新的第14个项目《拼团交易平台系统》,结合实际场景设计和落地的设计模式方案。—— 这个项目,会给你设计模式更牛的认知,妥妥的让你可以在自己的项目使用上设计模式! + +
+ +
+ +`拼多多`、`京东`、`腾讯`,都有很多这样的拼团场景,之所以选择这样的业务需求,就是让大家可以结合实际的项目来学习真实的技术。**这套项目目前已经进行了13节,全程视频+小册,手把手带着设计和编码。** + +>接下来,我们就看看,小傅哥是怎么带着大家玩转设计模式的! + +## 二、提炼通用模型 + +`条条大路通罗马,但放到项目工程中,就有点受不了了!` + +咋回事呢?🤔 + +只要不加规范,同样是做规则树、责任链、策略模式,都能有非常多的方式。那么,每来一个新人,就可能再多出来一份新的设计方式。这些一类的但非常差异化的东西,在下一个人要迭代需求的时候,就会有非常多的地方,有不同的改法。可能想死的新都有。 + +所以,为了把这些东西规范化,小傅哥设计了一套模型模板,单独提炼于项目需求逻辑之外。所有,有需要使用的场景,都可以引入使用。大家都在一个标准下使用设计模式,维护、迭代、扩展,也都非常容易。 + +
+ +
+ +- 首先,随着拼团项目的开发,定义2套了标准的通用设计模式结构。2种模式的责任链和1套规则树。这套责任链还用到了大家常学的数据结构中的链表设计和实现。 +- 之后,这套模型非常巧妙的运用到业务场景中。举例像是规则树,还提供了多线程数据异步加载,自由化功能逻辑编排。这样的设计模式,可以解决非常多的场景问题。 + +> 为了更好的理解这样的模型运行,接下来,小傅哥甩个图给大家感受下!—— 小傅哥,画图可牛了,见图就知意。 + +## 三、小图很美!💐 + +如图,是拼团的部分流程。营销类场景的系统都是很复杂的,它的流程节点非常多,也经常做扩展和调整。所以要做非常细腻的设计,满足复杂流程的实现。 + +
+ +
+ +- 以上这两部分流程分别包括试算和锁定优惠,这里会使用到规则树和责任链进行实现。 +- 下面,就介绍下关于规则树和责任链的设计和使用。 + +### 1. 规则树 + +`千万别觉得设计模式没有用`,否则也不会那么多源码框架中,会有那么多设计模式使用。MyBatis 一个框架,都用了10种设计模式! + +像是这些设计模式,合理的设计好,是真的可以解决非常复杂的业务流程编码,让代码变得优雅。既不会有那么多的 if···else,也不会让代码逻辑成片的堆到一个类中。 + +
+ +
+ +- 如图,这是一个规则树模型,解决的拼团中实际的场景业务。从根节点、开关节点、营销节点、人群节点,再到最终的正常和异常结束节点。每个节点分别处理自己的业务流程。 +- 你可以想象,如果没有这样的设计模式模型结构,那么在代码中,就是一个大方法中,一堆的逻辑编写,维护的成本是非常高的。 + +**展示一段代码,让兄弟👬🏻看看这是啥样的!** + +```java +@Slf4j +@Service +public class MarketNode extends AbstractGroupBuyMarketSupport { + + @Resource + private ThreadPoolExecutor threadPoolExecutor; + /** + * Spring 注入详细说明 + */ + @Resource + private Map discountCalculateServiceMap; + @Resource + private ErrorNode errorNode; + @Resource + private TagNode tagNode; + + @Override + protected void multiThread(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws ExecutionException, InterruptedException, TimeoutException { + // 异步查询活动配置 + QueryGroupBuyActivityDiscountVOThreadTask queryGroupBuyActivityDiscountVOThreadTask = new QueryGroupBuyActivityDiscountVOThreadTask(requestParameter.getActivityId(), requestParameter.getSource(), requestParameter.getChannel(), requestParameter.getGoodsId(), repository); + FutureTask groupBuyActivityDiscountVOFutureTask = new FutureTask<>(queryGroupBuyActivityDiscountVOThreadTask); + threadPoolExecutor.execute(groupBuyActivityDiscountVOFutureTask); + + // 异步查询商品信息 - 在实际生产中,商品有同步库或者调用接口查询。这里暂时使用DB方式查询。 + QuerySkuVOFromDBThreadTask querySkuVOFromDBThreadTask = new QuerySkuVOFromDBThreadTask(requestParameter.getGoodsId(), repository); + FutureTask skuVOFutureTask = new FutureTask<>(querySkuVOFromDBThreadTask); + threadPoolExecutor.execute(skuVOFutureTask); + + // 写入上下文 - 对于一些复杂场景,获取数据的操作,有时候会在下N个节点获取,这样前置查询数据,可以提高接口响应效率 + dynamicContext.setGroupBuyActivityDiscountVO(groupBuyActivityDiscountVOFutureTask.get(timeout, TimeUnit.MINUTES)); + dynamicContext.setSkuVO(skuVOFutureTask.get(timeout, TimeUnit.MINUTES)); + + log.info("拼团商品查询试算服务-MarketNode userId:{} 异步线程加载数据「GroupBuyActivityDiscountVO、SkuVO」完成", requestParameter.getUserId()); + } + + @Override + public TrialBalanceEntity doApply(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception { + log.info("拼团商品查询试算服务-MarketNode userId:{} requestParameter:{}", requestParameter.getUserId(), JSON.toJSONString(requestParameter)); + + // 获取上下文数据 + GroupBuyActivityDiscountVO groupBuyActivityDiscountVO = dynamicContext.getGroupBuyActivityDiscountVO(); + if (null == groupBuyActivityDiscountVO) { + return router(requestParameter, dynamicContext); + } + + GroupBuyActivityDiscountVO.GroupBuyDiscount groupBuyDiscount = groupBuyActivityDiscountVO.getGroupBuyDiscount(); + SkuVO skuVO = dynamicContext.getSkuVO(); + if (null == groupBuyDiscount || null == skuVO) { + return router(requestParameter, dynamicContext); + } + + // 优惠试算 + IDiscountCalculateService discountCalculateService = discountCalculateServiceMap.get(groupBuyDiscount.getMarketPlan()); + if (null == discountCalculateService) { + log.info("不存在{}类型的折扣计算服务,支持类型为:{}", groupBuyDiscount.getMarketPlan(), JSON.toJSONString(discountCalculateServiceMap.keySet())); + throw new AppException(ResponseCode.E0001.getCode(), ResponseCode.E0001.getInfo()); + } + + // 折扣价格 + BigDecimal deductionPrice = discountCalculateService.calculate(requestParameter.getUserId(), skuVO.getOriginalPrice(), groupBuyDiscount); + dynamicContext.setDeductionPrice(deductionPrice); + + return router(requestParameter, dynamicContext); + } + + @Override + public StrategyHandler get(MarketProductEntity requestParameter, DefaultActivityStrategyFactory.DynamicContext dynamicContext) throws Exception { + // 不存在配置的拼团活动,走异常节点 + if (null == dynamicContext.getGroupBuyActivityDiscountVO() || null == dynamicContext.getSkuVO() || null == dynamicContext.getDeductionPrice()) { + return errorNode; + } + + return tagNode; + } + +} +``` + +这是其中的一个营销节点,处理拼团营销试算的过程; + +1. multiThread 负责异步多线程方式加载数据,节省数据获取时间。 +2. doApply 处理业务流程,最后用 router 路由到下一个节点。 +3. get 是获取到下一个节点,是走到哪里由这块判断。 + +
+ +
+ +> 这套结构模型可以解决非常多的业务场景,而且每一个类都可以非常清晰的表达出具体的业务逻辑。无论以后是维护代码、迭代需求还是排查线上问题都变得很容易。 + +### 2. 责任链 + +规则树与责任链相比,规则树会有很多分叉流程,但责任链比较轻量主要负责单链路流程,在编码过程中不需要考虑流程的流转。相对来说更轻量了。 + +这里小傅哥设计了2套责任链模型,model1 - 单例链、model2 - 多例链(动态组装多套)。如图; + +
+ +
+ +- 如图,ILink 使用了数据结构责任链模型,ILogicHandler 为处理具体的业务功能逻辑。这里巧妙的设计在于把链的管理和业务逻辑的受理进行拆分,之后再由 LinkArmory 进行链路装配。**「当你看到代码的时候会觉得这真爽!原来写代码可以这样有意思」** +- 因为 LinkedList 实现如链表一样的方法,有添加,也有摘除链,所以还可以根据数据库配置动态处理链的实例化。 + +```java +@Service +public class Rule02TradeRuleFactory { + + @Bean("demo01") + public BusinessLinkedList demo01(RuleLogic201 ruleLogic201, RuleLogic202 ruleLogic202) { + + LinkArmory linkArmory = new LinkArmory<>("demo01", ruleLogic201, ruleLogic202); + + return linkArmory.getLogicLink(); + } + + @Bean("demo02") + public BusinessLinkedList demo02(RuleLogic202 ruleLogic202) { + + LinkArmory linkArmory = new LinkArmory<>("demo02", ruleLogic202); + + return linkArmory.getLogicLink(); + } + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class DynamicContext { + private String age; + } + +} +``` + +- 之后就可以动态的使用这套责任链完成功能需求的处理。demo01、demo02,分别组装了自己的责任链。 + +> 以上,在小傅哥的全部项目中也只是冰山一角,如果你加入小傅哥的社群学习,我还会让你感受到更多的关于工程在实际场景下设计的魅力! + +## 四、一套学习进阶路线图! + +**10年互联网大厂的经历,得出**;`最快的成长方式,是不要重复做一件事,而是能成体系的,有阶段性的成长。` + +为此小傅哥做了一整套的实战学习进阶项目,从小白到大佬,全程视频手把手带着从0到1,一步步完成项目的设计、开发和上线。在整套内容学习过程中,小傅哥为你提供了非常好的技术交流社群,及时解决学习问题。还包括调试你的问题代码,带你快速🔜出坑! + +
+ +
+ +**扫码加入即可获得全部项目学习!** + +- 首先,这一整套全体系的学习课程比私教培训实惠,更比培训班上万的培训费便宜。可能也就是培训班1天的💰钱,就能学习到这一整套内容了。 +- 之后,你学习的整套课程,就是小傅哥这个架构师自己全部原创编写的。这也就是说,你所提到的任何问题,小傅哥都能给你解答和讨论。 +- 那么,这么实惠的课程,成体系的课程,还是架构师编写的。还有什么可犹豫的,完全可以撸起来了! + +>🧧 注意,关注小傅哥公众号「bugstack虫洞栈」回复「星球」可以领取加入优惠哦! diff --git a/docs/md/project/group-buy-market/promotion/group-buy-market-v3.md b/docs/md/project/group-buy-market/promotion/group-buy-market-v3.md new file mode 100644 index 000000000..4dce14afc --- /dev/null +++ b/docs/md/project/group-buy-market/promotion/group-buy-market-v3.md @@ -0,0 +1,145 @@ +--- +title: 第1阶段完成上线,可体验! +lock: no +--- + +# 《拼团交易平台系统》第1阶段完成上线,可体验! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +经过春节假期🧨,接近于连续日更的一顿狂卷🌪🤨,第14个最新**拼团项目**,第一阶段已完结上线啦!只要`花费2周左右`的学习,就可以让简历有一个**全网最新的、又很硬核的编程项目**! + +
+ +
+ +**跟着小傅哥学习的好处是什么?** + +凌晨3点睡醒,还能在线回复社群伙伴的技术问题 😂,所有,在学习这方面,有小傅哥这样的卷王,为你保驾护航✈️。你可以平平稳稳的推进项目的进度,并且学习到有质量的、符合实际需求的真实项目。`”伙伴说:学习时候,最希望遇到问题,有人能指点!“` + +
+ +
+ +不少小伙伴反馈,之前学习的一些项目,遇到很多问题都没人解决,很耽误学习进度。尤其是都啃了一半了,放弃觉得浪费时间,不放弃学不下去。所以,在学习的时候,一定要加入有质量的社群,真心为伙伴们成长的社群,有责任心的社群!**把新人编程学习的伙伴当孩子一样培养。谁还不是从小白阶段过来的!** + +
+ +
+ +那么,当你体验过小傅哥的实战项目,就会感受到,这才是真的企业级项目。通过项目的学习,全程带着你从需求分析、功能设计、架构搭建、编码实现、设计模式运用,再到发布上线,全程视频手把手 + 技术小册,一步步带你完成!💐 + +- 项目地址:[https://bugstack.cn/md/project/group-buy-market/group-buy-market.html](https://bugstack.cn/md/project/group-buy-market/group-buy-market.html) +- 在线体验:[https://gaga.plus/](https://gaga.plus/) +- 加入社群:[https://t.zsxq.com/Yfbwo](https://t.zsxq.com/Yfbwo) - 关注公众号「`bugstack虫洞栈`」回复「`星球`」获得优惠券。 + +> 🧧 文末提供了加入红包,一次加入可以拿下14个实战项目,非常划算! + +## 一、能学到啥 + +该项目是互联网中大厂,非常常见的C端场景,营销类促进支付履约类实战项目,拼团交易平台系统。你可以在`美团`、`京东`、`拼多多`、`腾讯`等互联网产品中,都能看到拼团类场景,所以学习一个这样的项目,到面试了才能有和面试官聊的起劲。 + +- 业务,深入理解拼团场景的业务,并从产品 PRD 入手开始进行系统的分析和设计。 +- 前端,本次项目使用了最新的AI大模型DeepSeek,设计拼团UI,并处理UI与服务端接口的对接。效果非常不错。 +- 后端,分析业务场景需求,设计系统领域模型,构建系统分层架构。这些都是一个程序员👨🏻‍💻必须掌握的东西。但不是所有项目都能学到这些东西。 +- 后端,熟练使用 Spring、SpringBoot、MyBatis 等开发框架技术,并理解和使用 Spring 高级技巧进行设计和编码。 +- 后端,提炼和抽象通用的设计模式框架,多种责任链、规则树,让项目业务代码更加轻松的使用设计模式。 +- 后端,通过多线程异步同时加载多类数据,提高拼团营销优惠试算的能力。 +- 后端,熟练使用 okhttp3、retrofit2 框架,封装回调外部数据接口。拼团结算结束后,回调三方服务。 +- 后端,基于 Redis BitMap 设计人群标签,限定用户参与、用户展示。 +- 运维,熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 运维,熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 + +此外,小傅哥会把系统开发过程中的思考、设计、编码,录制⏺成完整的全程手把手带着学习的视频,让大家可以学习到的更多、更细、更深! + +>小傅哥作为面试官,已经把面试中最容易考察的能力项,通过一个个功能的方式渗透到项目中。潜移默化的在面试的时候,就会回答出非常多有力的技术解决方案。 + +## 二、项目展示 + +本次项目是一个包含 `前后端 + Dev-Ops` 的全栈式综合编程实战项目,并且小傅哥的这些项目都是可以真实上线使用的,所以你会看到陆续完成的这些项目,会通过微服务的方式进行对接使用,而这样的方式,才是公司里真实的情况。 + +此次的拼团 UI 设计,运用了 DeepSeek 进行设计; + +
+ +
+ +> 体验地址:[http://117.72.38.125:3080](http://117.72.38.125:3080) - 临时体验,非长期有效 + +### 1. 课程目录 + +
+ +
+ +- 一阶段,24节,视频在12小时多。预计可在2周左右学习完成。 +- 学习后,可以写简历,投递春招或者实习。项目内容量很细腻,业务流程很完善。还有很多创新点,都可以面试讲。 + +### 2. 核心流程 + +
+ +
+ +- 如图,为从营销锁单、营销结算、回调的核心流程。也就是一个拼团的核心正向执行链路。 + +### 3. 库表设计 + +
+ +
+ +- 项目课程开始会带着你分析需求并做库表的设计,这个项目符合实际的业务场景,所以设计的库表也会非常细腻。 + +### 4. 设计模式 + +
+ +
+ +- 如图,这是项目中一个规则树模型,解决的拼团中实际的场景业务。从根节点、开关节点、营销节点、人群节点,再到最终的正常和异常结束节点。每个节点分别处理自己的业务流程。 +- 你可以想象,如果没有这样的设计模式模型结构,那么在代码中,就是一个大方法中,一堆的逻辑编写,维护的成本是非常高的。 + +## 三、学习辅助 + +### 1. 遇到问题怎么办? + +
+ +
+ +- 当你学习项目遇到问题时,小傅哥会为你提供全程的指引,帮你解决问题。 +- 所以跟着小傅哥学习,一点也不用慌,嘎嘎冲就是了! + +### 2. 课程详细改动点 + +
+ +
+ +- 对于复杂的业务流程实现,会把所有的改动点,依次的给你罗列出来。同时还有视频教程,一步步带着你完成。 + +### 3. 全程有视频教程 + +
+ +
+ +- 课程的视频,分为业务讲解、功能设计、编码改动,之后在一步步带着你手写实现。即使你是小白也能跟下来学习。 + +## 四、项目路线 + +跟着小傅哥学习,是成体系的学习成长,有完整的不同难度的进阶路线学习。这里有小白入门,再到小白深造,之后开始进阶,这个过程中有业务,也有组件,可以让你非常全面的成长。 + +
+ +
+ +- 首先,这一整套全体系的学习课程比私教培训实惠,更比培训班上万的培训费便宜。可能也就是培训班1天的💰钱,就能学习到这一整套内容了。 +- 之后,你学习的整套课程,就是小傅哥这个架构师自己全部原创编写的。这也就是说,你所提到的任何问题,小傅哥都能给你解答和讨论。 +- 那么,这么实惠的课程,成体系的课程,还是架构师编写的。还有什么可犹豫的,完全可以撸起来了! diff --git a/docs/md/project/group-buy-market/promotion/group-buy-market-v4.md b/docs/md/project/group-buy-market/promotion/group-buy-market-v4.md new file mode 100644 index 000000000..a00fe7cca --- /dev/null +++ b/docs/md/project/group-buy-market/promotion/group-buy-market-v4.md @@ -0,0 +1,104 @@ +--- +title: 第2阶段完成上线,可查阅! +lock: no +--- + +# 《拼团交易平台系统》第2阶段完成上线,可查阅! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +😂 不少加入小傅哥社群的伙伴,在体验过众多项目后,说:“小傅哥,是在做个小京东吧?”,有`小型支付商城`、`有营销抽奖`、`有积分兑换`、`有各类组件`,这次又有了**拼团营销 + 小型商城**对接,真的牛皮! + +
+ +
+ +**我们做项目,就是这样** + +讲道理,学习编程来说,就是不断地`打破认知`,`拓宽思维`,用实际的贴合企业中的项目,填补自己的知识储备。而那些玩具项目和大号的CRUD,都没法提高我们的编程能力。所以,小傅哥(10年+大厂经验)带着大家做的项目,都是以真实企业场景的业务诉求,设计方案和落地工程,并且会带着你,完成项目的云服务器上线。 + +
+ +
+ +小傅哥社群里的项目,除了`拼团营销 + 小型商城`,还有非常多的项目,都是可以组合使用的,如;openai 应用 + 大营销、动态线程池 + 拼团、IM + ChatGLM SDK、API 网关 + Lottery等。这不就妥妥的公司中一个项目组里在做的事嘛! + +>👨🏻‍💻文末提供了小傅哥社群7套组件项目、7套业务项目、1套手写源码教程,可以🧧加入获取。 + +## 一、两套微服务对接方案 + +介绍下小傅哥技术社群里这两套微服务和对接; + +- **小型支付商城**:接入微信扫码鉴权、支付宝沙箱环境,串联订单创建、唤起收银台,接收支付完成回调,扭转订单状态,模拟发货。完成真实的交易流程。 + +- **拼团交易营销**:提供商品下单支付链路中的营销微服务设计,处理商品优惠试算、支付营销锁单、支付营销结算。促进商品交易履约率。 + +
+ +
+ +如图,是两套微服务的对接链路。 + +- 首先,在小型支付商城,创建订单的过程中,调用拼团营销锁单。这个时候就拿到了当笔订单的优惠金额。之后创建支付订单唤起收银台,之后用户就可以按照最终的优惠金额进行支付了。 +- 之后,在支付完成后,收到回调消息,进行营销拼团进度结算。直至拼团组队进度完成,在回调给支付商城,触达交易结算。 + +这样一整套完整的交易营销流程,是非常真实的实际场景对接处理方案。尤其是营销场景下的复杂的试算、规则的过滤,再到结算的处理,都是使用了非常巧妙的编码操作,使用了非常好的设计模式进行设计。这块非常有的学! + +## 二、对接后,上线效果 + +从用户,`微信扫码登录`、`进入商品详情页`、`点击开团`、`确认支付`、`跳转支付宝`、`支付回调`、`营销结算`、`支付结算`、`模拟发货`。一整套流程完整实现。页面端效果; + +
+ +
+ +- 小傅哥的课程会全程带着你完成两套微服务的对接和云服务器操作上线,你可以体现一整套的小型商城 + 拼团营销的上线使用效果。 +- 并且在这套系统上线中,会设计 Nginx 负载,轮训调用后端拼团服务。 + +> 就这么学东西,才能嘎嘎有收获! + +## 三、课程目录 + +这是一套拼团交易平台,包括了小型支付商城课程 + 拼团营销平台系统; + +
+ +
+此套课程全程视频手把手 + 技术小册文档,想怎么学习就怎么学习,让小白快速入门上手完成。 + + +### 1. 小型支付商城 + +
+ +
+ +- 25节课程,MVC架构写一遍,DDD架构写一遍。非常适合新人上手的一套课程,全程视频手把手学习。 + +### 2. 拼团营销平台系统 + +
+ +
+ +- 28节课程,一阶段对接静态页面,二阶段与小型支付商城微服务对接。 +- 拼团项目,还会继续迭代,增加更多细节。让小伙伴满足当前找实习的前提下,可以继续学习,完善自己的知识储备。 + +## 四、大家的认可! + +小傅哥,做的项目,一向以高质量著称。在每个项目里,都花费大量的时间,将互联网公司里的实战经验,用到项目实现中。所以,你会看到非常多的场景解决方案和极其巧妙的工程设计手段。因此,也得到了大家的认可。 + +
+ +
+ +
+ +
+ +> 就这,我只能说,你越早加入,能力提高的越好! diff --git a/docs/md/project/group-buy-market/promotion/group-buy-market-v5.md b/docs/md/project/group-buy-market/promotion/group-buy-market-v5.md new file mode 100644 index 000000000..bdbbc4c22 --- /dev/null +++ b/docs/md/project/group-buy-market/promotion/group-buy-market-v5.md @@ -0,0 +1,438 @@ +--- +title: Ai MCP + ELK 系统日志,排错提效 100%! +lock: no +--- + +# Ai MCP + ELK 系统日志,排错提效 100%! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +自从 Ai MCP 模型上下文协议,2024年11月,推出开放标准以后。2025年,就进入了全面的 MCP 服务落地。也正因如此,程序员👨🏻‍💻的学习就又多了一项新的应用技能,同时也成了面试热门问题。如:**“你的项目,有使用 AI 方面的能力进行提效吗?”** 解封下码农的双手🙌🏻! + +
+ +
+ +**我能哪些方面使用 Ai 提效呢?** + +在互联网程,序员工程开发方面,关于 Ai 提效最为常见的,也是市面上使用最多的,就是各类的 Ai 编码软件。如 cursor、trae.ai、IntelliJ IDEA 中的各类 Ai 插件,都可以辅助我们完成项目的编码操作。甚至一些简单的 HTML 页面,几乎在刚开始开发阶段,可以快速搭建起来,确实非常提效。 + +不过这些都是固定的软件,他们并没有深度结合到业务场景中,从整个研发的生命周期看,还有非常多的节点可以被 Ai 提效。如;需求评审、研发设计、系统发布、代码评审、单测用例、业务监控、问题排查&故障分析等。都可以深入自身的业务,运用 Ai 开发进行提效。 + +这里尤其是问题排查和故障分析,在互联网企业中,尤其是大厂的业务,几乎每天都要配和运营的反馈的客诉,系统的报警日志,性能的降低反馈,进行人工检索`系统监控`、`日志`、`分析`,这部分要投入大量的人工成本,虽然可能系统并不是真的有问题,但也要一遍遍的排查这些问题。 + +所以,结合这样的场景,小傅哥在带着大家的实战项目,也逐步的引出关于 Ai 在应用项目上的提效处理。今天分享的一个场景就是基于 Ai MCP 分析系统的 ELK 日志。后面还会分析关于监控、数据库一起分析。 + +>🧧 文末提供了小傅哥技术社群全套的17个应用实战项目,一次加入可以获得全部的文档、源码、视频,嘎嘎提高能力! + +## 一、系统说明 + +小傅哥带着社群伙伴,做了一套 **《拼团交易平台系统》**,因为拼团是非常重要的 toc 业务场景,也是拼多多、腾讯、京东等服务平台,交易支付时候,最为常见的一种营销手段。它可以通过用户自传播方式增强交易量,也是拼多多最开始起家形成巨大规模的一个业务逻辑。 + +该系统采用了 DDD 领域驱动设计进行建模,拆分领域模块边界,形成;活动领域、人群领域、交易领域,来构建拼团营销交易流程,达到试算、锁单、结算等步骤流程。这个过程中提炼了通用设计模式,规则树、责任链,可以非常有效的统一的治理流程编排实现。 + +
+ +
+ +
+ +
+ +如图,是两套微服务的对接链路。 + +- 首先,在小型支付商城,创建订单的过程中,调用拼团营销锁单。这个时候就拿到了当笔订单的优惠金额。之后创建支付订单唤起收银台,之后用户就可以按照最终的优惠金额进行支付了。 +- 之后,在支付完成后,收到回调消息,进行营销拼团进度结算。直至拼团组队进度完成,在回调给支付商城,触达交易结算。 + +这样一整套完整的交易营销流程,是非常真实的实际场景对接处理方案。尤其是营销场景下的复杂的试算、规则的过滤,再到结算的处理,都是使用了非常巧妙的编码操作,使用了非常好的设计模式进行设计。这块非常有的学! + +## 二、配置日志 + +### 1. 系统部署(ELK) + +
+ +
+ +- 基于 Docker 部署 ELK 系统,采集上报日志。 + +### 2. 上报配置 + +```java + + + + + + + ${LOG_STASH_HOST}:4560 + + + + + + + + + + + + +``` + +- 通过增加 LogstashTcpSocketAppender 上报系统日志到 ELK。 + +### 3. 生产日志 + +开始下面的操作之前,需要启动 SpringBoot 服务,访问接口,让系统产生一些运行日志。 + +
+ +
+ +```java +25-06-08.08:05:08.441 [http-nio-8091-exec-2] INFO ZJCalculateService -b601d8d5-ff62-4adb-8260-16d8b6af53ef 优惠策略折扣计算:0 +25-06-08.08:05:08.445 [http-nio-8091-exec-2] INFO EndNode -b601d8d5-ff62-4adb-8260-16d8b6af53ef 拼团商品查询试算服务-EndNode userId:xfg04 requestParameter:{"channel":"c01","goodsId":"9890001","source":"s01","userId":"xfg04"} +25-06-08.08:05:08.451 [http-nio-8091-exec-2] INFO MarketIndexController -b601d8d5-ff62-4adb-8260-16d8b6af53ef 查询拼团营销配置完成:xfg04 goodsId:9890001 response:{"code":"0000","data":{"activityId":100123,"goods":{"deductionPrice":20.00,"goodsId":"9890001","originalPrice":100.00,"payPrice":80.00},"teamList":[],"teamStatistic":{"allTeamCompleteCount":0,"allTeamCount":2,"allTeamUserCount":4}},"info":"成功"} +``` + +- 访问的是拼团试算接口,产生一些数据运行日志。在这个过程中,会多次访问系统,触发拼团接口的限流操作。多一些日志也能更好的让 Ai MCP 进行分析。 + +### 4. 查看日志 + +
+ +
+ +- 配置文章后,访问 Kibana 数据可视化后台,这会你可以看到上报上来的日志信息。 +- 到这里,日志配置完成。接下来,我们就可以就 Ai MCP,来访问日志了。 + +## 三、日志分析 + +AI MCP 是小傅哥社群内的另外一套项目,[《DeepSeek RAG、MCP、Agent》](https://t.zsxq.com/GwNZp) 智能体,课程。课程里有讲解如何开发和使用 MCP 服务,以及构建 Agent 智能体。地址:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) - `感兴趣的伙伴可以进入学习` + +这里小傅哥选择一个 ElasticSearch MCP 服务,对接到咱们的 ELK 日志上,通过对接的 MCP 服务帮我们检索日志。 + +### 1. 环境说明 + +- AI 对话客户端;https://claude.ai/download - 不非得使用这个客户端。像是 trae.ai、cursor 等,能配置 mcp 服务的都可以的。 +- 发码平台:https://sms-activate.io/ - `claude.ai 注册需要使用` +- node 环境,需要 v18+ https://nodejs.org/en/download +- mcp 服务01;https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem +- mcp 服务02;https://github.com/elastic/mcp-server-elasticsearch + +### 2. 客户端使用 + +#### 2.1 配置文件 + +
+ +
+ +```java +{ + "mcpServers": { + "elasticsearch-mcp-server": { + "command": "npx", + "args": [ + "-y", + "@elastic/mcp-server-elasticsearch" + ], + "env": { + "ES_URL": "http://127.0.0.1:9200", + "ES_API_KEY": "your-api-key" + } + }, + "filesystem": { + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-filesystem", + "/Users/fuzhengwei/Desktop" + ] + } + } +} +``` + +- 配置后要重启客户端。注意 `/Users/fuzhengwei/Desktop` 更换为你的文件夹。 + +#### 2.2 对话提问 + +
+ +
+ +
+ +
+ +- 对话提问:`获取所有限流用户,并在 /Users/fuzhengwei/Desktop 创建限流文件写入用户。` +- 这样他就可以通过 ES 查询我们产生的日志数据,并给出分析报告。 + +#### 2.3 日志文件 + +
+ +
+ +- 打开产生的日志文件,可以把限流的用户拉取出来。方便我们分析提供问题。 + +### 3. 代码调用 + +这部分涉及到星球的 AI Agent 项目,工程地址:[https://gitcode.net/KnowledgePlanet/ai-agent-station-study](https://gitcode.net/KnowledgePlanet/ai-agent-station-study) + +#### 3.1 直接调用 + +```java +package cn.bugstack.ai.test.spring.ai; + +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class AiAgentMCPESTest { + + private ChatModel chatModel; + + private ChatClient chatClient; + + @Resource + private PgVectorStore vectorStore; + + public static final String CHAT_MEMORY_CONVERSATION_ID_KEY = "chat_memory_conversation_id"; + public static final String CHAT_MEMORY_RETRIEVE_SIZE_KEY = "chat_memory_response_size"; + + @Before + public void init() { + + OpenAiApi openAiApi = OpenAiApi.builder() + .baseUrl("https://apis.itedus.cn") + .apiKey("sk-vo81adWzUz1G0LBQ1cF1B804A1E04aC*****可申请") + .completionsPath("v1/chat/completions") + .embeddingsPath("v1/embeddings") + .build(); + + chatModel = OpenAiChatModel.builder() + .openAiApi(openAiApi) + .defaultOptions(OpenAiChatOptions.builder() + .model("gpt-4.1-mini") + .toolCallbacks(new SyncMcpToolCallbackProvider(stdioMcpClientElasticsearch()).getToolCallbacks()) + .build()) + .build(); + } + + @Test + public void test_chat_model_call() { + Prompt prompt = Prompt.builder() + .messages(new UserMessage( + """ + 有哪些工具可以使用 + """)) + .build(); + + ChatResponse chatResponse = chatModel.call(prompt); + + log.info("测试结果(call):{}", JSON.toJSONString(chatResponse)); + } + + @Test + public void test_chat_model_call_es() { + Prompt prompt = Prompt.builder() + .messages(new UserMessage( + """ + 查询xfg01日志,DSL 语句; + { + `index`: `springboot-logstash-2025.06.07`, + `queryBody`: { + `size`: 10, + `sort`: [ + { + `@timestamp`: { + `order`: `desc` + } + } + ], + `query`: { + `match`: { + `message`: `xfg01` + } + } + } + } + """)) + .build(); + + ChatResponse chatResponse = chatModel.call(prompt); + + log.info("测试结果(call):{}", JSON.toJSONString(chatResponse)); + } + + /** + * https://sai.baidu.com/server/Elasticsearch%2520MCP%2520Server/awesimon?id=02d6b7e9091355b91fed045b9c80dede + * https://github.com/elastic/mcp-server-elasticsearch + */ + public McpSyncClient stdioMcpClientElasticsearch() { + + Map env = new HashMap<>(); + env.put("ES_URL","http://127.0.0.1:9200"); + env.put("ES_API_KEY","none"); + + var stdioParams = ServerParameters.builder("npx") + .args("-y", "@elastic/mcp-server-elasticsearch") + .env(env) + .build(); + + var mcpClient = McpClient.sync(new StdioClientTransport(stdioParams)) + .requestTimeout(Duration.ofSeconds(100)).build(); + + var init = mcpClient.initialize(); + + System.out.println("Stdio MCP Initialized: " + init); + + return mcpClient; + + } + +} +``` + +
+ +
+ +- 以上代码,就是对接的 mcp 服务,以及通过 `查询xfg01日志,DSL 语句;` 来操作 ES。 + +#### 3.2 动态调用(Agent) + +```java +private String buildSystemPrompt() { + return """ + 你是一个专业的日志分析助手,具备以下能力: + 1. 可以查询Elasticsearch索引列表 - 使用list_indices()函数 + 2. 可以获取索引字段映射 - 使用get_mappings(index)函数 + 3. 可以执行Elasticsearch搜索 - 使用search(index, queryBody)函数 + + 当用户询问限流相关问题时,请按以下步骤执行: + + **步骤1:探索数据源** + - 首先调用list_indices()查看所有可用的索引 + - 识别可能包含日志信息的索引(通常包含log、logstash等关键词) + + **步骤2:分析数据结构** + - 对目标索引调用get_mappings()查看字段结构 + - 重点关注message、level、timestamp等字段 + + **步骤3:构建搜索查询** + - 使用多种限流相关关键词搜索:限流、rate limit、throttle、blocked、超过限制、黑名单、超频次 + - 按时间倒序排列结果 + - 示例查询结构: + { + `index`: `group-buy-market-log-2025.06.08`, + `queryBody`: { + `size`: 10, + `sort`: [ + { + `@timestamp`: { + `order`: `desc` + } + } + ], + `query`: { + `match`: { + `message`: `xfg01` + } + } + } + } + + **步骤4:优化搜索策略** + - 如果初始搜索结果不理想,尝试使用wildcard查询 + - 如果需要,使用单一关键词进行精确匹配 + + **步骤5:分析结果** + - 从搜索结果中提取用户信息 + - 识别限流类型(黑名单、超频次等) + - 统计触发次数和时间分布 + - 分析影响的服务和功能 + + **输出格式要求:** + - 明确列出被限流的用户ID + - 说明限流类型和原因 + - 提供触发时间和频率信息 + - 给出分析建议 + + 现在开始执行查询任务。 + """; +} + +@Test +public void queryRateLimitedUsers() { + // 第一步:系统初始化提示词 + String systemPrompt = buildSystemPrompt(); + // 第二步:用户查询提示词 + String userQuery = "查询哪个用户被限流了"; + // 第三步:构建完整的提示词 + String fullPrompt = buildFullPrompt(systemPrompt, userQuery); + // 第四步:调用AI模型 + Prompt prompt = Prompt.builder() + .messages(new UserMessage(fullPrompt)) + .build(); + ChatResponse chatResponse = chatModel.call(prompt); + log.info("测试结果:{}", chatResponse.getResult().getOutput().getText()); +} + +public String queryRateLimitedUsersStepByStep() { + StringBuilder result = new StringBuilder(); + // 步骤1:查询索引列表 + String step1Prompt = buildStepPrompt("步骤1:查询所有可用的Elasticsearch索引", + "请调用list_indices()函数查看所有可用的索引,并识别可能包含日志的索引。"); + result.append(executeStep(step1Prompt)).append("\n\n"); + // 步骤2:获取索引映射 + String step2Prompt = buildStepPrompt("步骤2:获取日志索引的字段映射", + "请对识别出的日志索引调用get_mappings()函数,查看字段结构,重点关注message、level、timestamp等字段。"); + result.append(executeStep(step2Prompt)).append("\n\n"); + // 步骤3:搜索限流日志 + String step3Prompt = buildStepPrompt("步骤3:搜索限流相关日志", + "请使用多种限流相关关键词(限流、rate limit、throttle、blocked、超过限制等)搜索日志,按时间倒序排列。"); + result.append(executeStep(step3Prompt)).append("\n\n"); + // 步骤4:分析结果 + String step4Prompt = buildStepPrompt("步骤4:分析限流用户", + "请分析搜索结果,提取被限流的用户信息,包括用户ID、限流类型、触发次数等,并生成详细报告。"); + result.append(executeStep(step4Prompt)).append("\n\n"); + return result.toString(); +} +``` + +```java +================================================================================ +📝 执行步骤日志: +================================================================================ +步骤 1: 经过索引查询,我们确定了一个名为 `group-buy-market-log-2025.06.08` 的索引,该索引包含了日志记录。在分析索引字段映射之后,发现字段 `message` 是存储日志内容的主要字段。因此,我们构建了一个搜索查询来查找日志中与“限流”相关的记录。 + +在向 Elasticsearch 发送查询请求时,首次尝试做了一个布尔查询,但由于格式问题请求失败。随后,我使用了一个更简洁的 `query_string` 查询,该查询成功返回了 16 条相关记录。这些记录主要显示了不同用户超限流量被拦截的情况。 具体限制用户如下: + +- 用户标识: `xfg01` +- 用户标识: `liergou` +- 用户标识: `xiaofuge` +- 用户标识: `zhangsan` +- 用户标识: `xiefeiji` + +这些记录均包含了时间戳和其他上下文信息,如日志级别、线程名和来源等。 +``` + +- 基于提示词和代码逻辑,可以动态执行调用和结果分析。Ai 提示词是构建 Agent 非常重要的一项技术,因为 提示词 = Ai 大脑! + +## 四、扩展能力 + +
+ +
+ +- Ai MCP 服务,也就是 Agent 能力,还可以用于全面的系统运行分析,帮助我们快速的处理线上运行情况。 + +>好啦,这次我们先分享关于 Ai MCP 如何为日志检索提效。下次我们在分析关于监控处理。 diff --git a/docs/md/project/group-buy-market/promotion/group-buy-market-v6.md b/docs/md/project/group-buy-market/promotion/group-buy-market-v6.md new file mode 100644 index 000000000..d16c0f347 --- /dev/null +++ b/docs/md/project/group-buy-market/promotion/group-buy-market-v6.md @@ -0,0 +1,82 @@ +--- +title: 拼团交易平台逆向流程启动 +lock: no +--- + +# 拼团交易平台逆向流程启动 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +经过小傅哥这么多年的努力,👨🏻‍💻认认真真的做编程项目之下,一点点把来自于互联网真实技术教给社群伙伴。现在也是行了起来。在大学里`优秀毕业生分享上`,也能被伙伴认可,拿出来写到黑板上啦! + +
+ +
+ +**他们是这么评价小傅哥的!** + +小傅哥在大型互联网,`toc业务场景,工作这么多年`,也确实积累了丰富的业务+技术经验。也深知,对于一个从事此行业的编程人员,应该学习哪些项目才能快速成长。 + +
+ +
+ +为此,小傅哥做了一整套项目,`由浅入深,循序渐进`。以不同难度级别的项目,带着小伙伴们稳扎稳打的成长。有了这样的真实场景学习,`既可以`提高自己的编程思维和编码能力,`又能`在面试中讲解出各种场景方案,`还能`为以后入职进入到公司快速融入团队。可以说是,一举多得。 + +> 🧧 文末提供了全套的实战项目,含有;业务项目、组件项目、创新AI项目,各项目对应的文档、视频、代码,全部提供给大家学习使用。 + +## 一、项目介绍 + +**于24年末,小傅哥以线上真实toc业务场景**,设计了一套 **《拼团交易平台系统》**,帮助大家深入学习,`前后端技术`、`分布式架构`、`微服务对接`、`Dev-Ops 发布上线`、`AI MCP + ELK + 普罗米修斯监控`、通用组件扳手工程设计等。可以说学习这样一套项目,就能完整的积累各项互联网toc场景常见方案和通用技术。 + +像是这样的项目,基本大家在使用各个互联网app的时候,都能体会到拼团的应用。如;`美团拼单`、`滴滴拼券`、`京东/拼多多购物`、`腾讯`等,都可以看到。所以这也是面试中面试官非常喜欢聊的业务和实现方案。 + +
+ +
+ +现在这套项目已经讲解了38节课程,所有的拼团正向流程 + 分布式方案 + Dev-Ops 部署和监控,都已完成。接下来将开启逆向退单流程。也就是7月5日,周末开启。 + +## 二、业务流程 + +如图,以用户旅途视角来看整个拼团流程。 + +
+ +
+ +- 如图,以上为用户旅途视角的全流程。从运营配置拼团活动,到用户从「小型支付商城(对接的一个场景)」,开始查看带有拼团优惠的上,进行试算,过滤规则。再到参与拼团,完成下单和一系列的流程处理。之后是拼团对于支付收单的入账计算,达成拼团目标后,回调(HTTP/MQ)商城服务,完成整个交易过程。 +- 之后,我们要考虑的是如何处理逆向流程,也就是退单的过程。退单分为当前过程中,拼团是否完成,未完成则根据是否支付了,取消锁单量和完成量。如果拼团已完成,则取消锁单量和完成量,拼团优惠释放后,则回调商城(refundGroupBuySuccess),完成退单退货服务。 +- 一般,对于已经完成拼团的,有用户退单是不会对其他用户已经完成交易的进行退单的,会造成很差的体验。这部分成本往往由商家和平台分摊,毕竟平台的目的是为了卖货。 + +## 三、详细设计 + +### 1. 服务对接 + +
+ +
+ +### 2. 正向流程 + +
+ +
+ +### 3. 逆向流程 + +
+ +
+ +以上,是整个`拼团系统` + `小型支付`的对接和正逆向流程设计。正向流程,已经全部完成。后面我们就要开启逆向流程啦,带着大家完完整整的把项目全部做完。 + +以上的流程分析,在课程中会有专门的视频讲解,一步步带着大家分析所有过程,以及各项细节。之后在技术社群的伙伴,完成所有的编码。 + + + diff --git a/docs/md/project/group-buy-market/promotion/group-buy-market-v7.md b/docs/md/project/group-buy-market/promotion/group-buy-market-v7.md new file mode 100644 index 000000000..68ad41d06 --- /dev/null +++ b/docs/md/project/group-buy-market/promotion/group-buy-market-v7.md @@ -0,0 +1,182 @@ +--- +title: 第3阶段开发完成,总结复盘 +lock: no +--- + +# 第3阶段开发完成,总结复盘 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**耗时9个月制作,48节课程,微服务分布式架构**,`前后端 + DevOps + AI MCP(场景运用)`,全栈式编程 + 全程视频 + 全套的技术小册,手把手👨🏻‍💻教学,终于完结啦!🎉 + +
+ +
+ +**讲道理,做项目,就得做公司里真实有的业务。** + +我在非常多的互联网业务应用中,都看到过这样一种产品形态,它就是**拼团交易营销场景**,如;`拼多多/京东购物🛒`、`滴滴拼券🧧`、`腾讯开团抢购服务器☁️`、`美团团购☕️`等等,都是以拼团方式增强交易单量的业务场景。所以,如果想面试互联网公司,还是要优先考虑做一些这样的真实场景业务,这样才能和面试官有东西可以讲,而不是什么都没的聊o(╥﹏╥)o。 + +此套项目,小傅哥已提供了对应的`简历模板`、`面试问题(一直梳理中)`,还有 AI MCP 场景的加入,通过 AI MCP 对接 ELK + 普罗米修斯监控,Ai Agent 智能化分析系统的日志和运行情况。 + +**本项目,和你之前学习过的一些项目最大的区别,就是!这是真实的业务,全核心流程覆盖,有非常好的架构和编码设计。这块学习后,进入公司完全可以跟着大家一起做需求,而不是瑟瑟发抖的不知道自己要做啥!** + +
+ +
+ +> 文末有加入学习方式,提供全套的课程代码、文档、视频,此外还有额外的16个实战项目一起获取! + +## 一、能学到啥 + +该项目是互联网toc场景的核心业务流程,以真实业务作为背景,实际可上线,可运行为目标,进行系统的需求分析、架构设计、功能实现,过程涵盖设计模式的运用,解决复杂场景问题。让大家感受到,🐂牛逼的代码,从来不是一顿写CRUD! + +- 【前端】以 Ai Agent 设计前端 UI,包括 HTML、Div、CSS 等前端编程技术。 +- 【前端】掌握 fetch 方式对后端接口的调用,处理相关的逻辑数据。 +- 【后端】熟练搭建项目工程,学习工程分层结构概率和设计思路。掌握更多的六边形、洋葱、整洁架构。`提高简历技术亮点` +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对框架源码所提供的扩展接口具备运用能力。 +- 【后端】熟练使用 SpringCloud 核心微服务分布式技术栈,包括:Fegin、Sentinel、Nacos、熔断、限流、降级等。`先做功能,后面逐步添加。` +- 【后端】熟练使用大厂中常用的设计模式手段和设计原则技术,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。 +- 【后端】深度学习复杂场景的架构设计、编程思维,如果处理系统功能的边界和上下文的维护。—— 这些东西一定是从实践中才能学习到的。 +- 【后端】熟练使用 Mock 单测工具、JMeter 压测工具,增强代码交付质量。 +- 【后端】熟练掌握异常、枚举、错误码的定义和使用,并学习到如何合理打印服务日志,便于问题排查。 +- 【后端】以通用场景设计为目的,提取共性逻辑为通用的设计框架,涵盖;动态配置、设计模式(规则树、责任链)、限流。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练使用 Nginx 配置转发服务,并能申请ssl配置https服务。 +- 【运维】熟练使用 Grafana 监控系统 + AI MCP,对系统的 JVM、磁盘、Tomcat、应用(QPS、响应时间、调用量)完整监控。 + +此外,小傅哥已对全过程都做了需求的分析、功能的设计、编码的精细化处理,让你吃上细糠!也通过这种方式,真正的把你的技术储备整起来,以后进入到公司都是嘎嘎强的选手! + +## 二、运行展示 + +整个拼团交易的流程非常细腻,包括了,一套小型支付 + 拼团营销平台的完整对接,涵盖;`验签`、`扫码/无痕登录`、`试算`、`锁单`、`支付+结算`、`退单+退单`的完整链路流程。如图; + +
+ +
+ +
+ +
+ +
+ +
+ +前端页面体现了全部核心流程,看着前端不是太多,但后端支撑整套系统了,写了**1.38万行代码**! + +## 三、简历模板 + +注意:🙅🏻‍ 不要直接复制粘贴简历模板内容!视频提供了 DeepSeek AI 方式编写简历,可以参考。 + +- **项目名称**:`拼团营销服务系统`、`交易营销场景 - 拼团系统`、`营销拼团交易平台`、`仿拼多多/腾讯/京东,拼团玩法系统(以大厂项目为背书)`、`拼团外卖平台系统(结合其他项目一起组合)` - 参考以上方式编写自己的项目名称,尤其拼团还是一个微服务,可以和很多其他系统组合。 + +- **项目架构**:`微服务设计`、`分布式架构`、`DDD 领域驱动设计 + 六边形分层架构实现`、`前后端分离技术` + +- **核心技术**:SpringBoot、MyBatis、MySQL、Guava、Redis、RabbitMQ、动态配置中心(DCC)、普罗米修斯监控、Docker等 - 如果学习了其他技术栈也可以补充。 + +- **项目描述**: + + - 方式1(以学习视角介绍):本项目参考`拼多多`交易购物拼团场景,调研中大厂相关营销业务场景和技术架构方案,设计实现了本套拼团营销服务系统,支持各类营销优惠(直减、折扣、N元购)。该系统以面向对象开发,运用 DDD 拆分领域边界,使用设计模式设计服务功能。提高系统的扩展性和可维护性。 + - 方式2(以提供服务介绍):该项目以`拉动/促进/提高`(小型支付商城/外卖点餐/购票出行/...)交易单量为目标,通过设计拼团优惠组队下单为手段,达到增强用户自传播分享私域提高整个交易GMV的结果。三段式描述,`...目标,...手段,...结果` + - 方式3(以实际场景介绍):该项目是以促进Xxx公司Xxx场景的核心营销优惠玩法系统,围绕公司的xxx、yyy、zzz等全部交易业务,设计通用的拼团优惠锁单和组队结算回调服务。此系统分布式架构设计,可支撑单机压测量 xxx tps,tp99 xxx 的数据指标,有效的满足公司的全量的业务场景接入使用。 + +- **核心方案**: + + - **架构设计**,以 DDD 领域驱动设计,四色建模方式,按照系统功能流程,拆解服务边界。包括;活动域、标签域、交易域。 + + - **设计模式**,设计并提炼通用的`责任链`、`规则树`模型框架,解决领域场景中多处,需要使用设计模式解耦复杂流程链路的调度(避免过多的if...else判断)。鉴于多处场景的责任链使用,模块框架设计责任链为执行和链路分离组装,便于工厂可以组合出各类执行责任链,不被不同的链路管理影响(以往的责任链,一般是单例的,会被影响)。 + + - **规则过滤**; + + - (举例)以拼团试算场景举例,运用通用设计模式模型框架,完成试算;根节点、切量开关、营销折扣、人群标签、异常兜底等流程串联。设计这样解耦设计,极大的提到了程序的可扩展性。 + - (举例)以拼团锁单场景举例,拼团锁单场景,使用通用的责任链模型框架,校验活动的有效性(状态、有效期)和用户的参与资格。 + - (举例)以拼团结算场景举例,拼团结算场景,使用通用的责任链模型框架,校验渠道黑名单配置、拼团组队信息、交易时间属性、订单有效状态等。 + - (举例)以拼团试算场景举例,在查询优惠配置数据时候,抽象出模板结构,使用 Supplier 函数式编程,设计动态降级、缓存数据和 dao 的后置执行操作。通用模板的设计让所有场景更容易接入。`ActivityRepository#queryGroupBuyActivityDiscountVO` + + - **异步线程**,为提高用户体验,将拼团优惠试算所需的营销类数据加载,由串行改为异步线程并行执行。此执行方式由通用设计模式模型框架提供。(如果由引入星球的动态线程池,也可以在这里增加线程池的管理描述) + + - **功能方案**; + + - (举例)通过 Redis 发布订阅模型,结合 Spring AOP 切面和代理,以自定义注解的方式控制属性信息动态配置。减少系统与 Redis 的 IO 交互,提高对高频场景属性值的使用时间效率。 + - (举例)设计拼团组队结算的 HTTP、MQ 双重手段,满足外部应用和内部微服务的不同方式对接,增强系统的适配性。同时为了保证整体方案的可靠性,在结算触达时,先异步多线程方式即时触发回调(HTTP、MQ),再通过业务一致性任务数据补偿校验。(MQ、HTTP,都可能因网络原因导致失败,因此需要重试)任务的触达,还增加多分布式锁,让任务互备抢占方式执行,增强系统的鲁棒性设计。 + - (举例)设计 Redis BitSet/BitMap 人群标签,用于过滤可见和可参与,拼团活动的人群信息。该人群标签可依赖于过往用户数据(交易下单)通过 job 任务完成人群标签的录入。 + - (举例)通过策略模式,设计拼团折扣(MJ、ZJ、NYG)的计算策略。同时折扣的计算也会通过人群标签过滤,以满足运营策略配置,降低活动风险。 + - (举例)运用 retrofit2/okhttp3/spring cloud fegin + nginx 负载,对接拼团交易平台锁单服务,以及通过 http 回调和 MQ 监听来处理交易结算。 + - (举例)`三阶段实现的内容`,通过独占锁处理互备任务抢占执行回调,确保在同一时刻有一个运行的回调任务,提高系统的鲁棒性设计。 + - (举例)`三阶段实现的内容`,设计Redis无锁化拼团库存抢占和恢复库存处理,减轻数据库行锁独占的压力,提高系统吞吐量。 + - (举例)`三阶段实现的内容`,抽象通用函数式缓存分级设计,并结合扳手工程DCC动态配置,处理缓存降级到DB设计。 + - (举例)`三阶段实现的内容`,结合 RateLimiter + DCC 动态配置,实现动态限流配置。 + - (举例)`三阶段实现的内容`,以 Ai MCP + ELK + 普罗米修斯监控,以 Ai Agent 智能体方式,分析错误日志和异常监控,动态化展示监控报表。 + - (举例)`三阶段实现的内容`,通过枚举策略,设计多种类型退单(未支付&未成团、已支付&未成团、已支付&已成团),并通过回调处理退单退款。 + +>以上仅对部分内容做了简历编写,这里还有非常多的内容可以写到简历,可以根据自己的学习和梳理,以及扩展进行简历编写。 + +## 四、后端设计 + +### 1. 分层架构 + +
+ +
+ +- 整个系统是一个微服务分布式架构设计,通过两套系统的对接,体现微服务的全流程处理关系。 +- 下面是系统中用到的核心技术栈,框架、组件、监控、部署、发布、上线,可以说是非常全面。 + +### 2. 工程结构 + +
+ +
+ +- 拼团和交易系统,以面向对象的思维,划分出领域结构。活动域、标签域、交易域、鉴权域、商品域、订单域。 +- 两套系统通过 http/rpc(可配置对接)、mq(RabbitMQ)进行同步和异步交互,因为配有本地消息表,所以可以保证最终一致性。 +- 这里有非常精妙的编码设计,如;工厂模式、组合模式、策略模式(含枚举策略)、责任链、抽象类等,又提供了 Supplier 函数式编程,可以说是应有尽有! + +### 3. 库表数据 + +
+ +
+ +- 一看库表就知道,这不是小儿科!有标签表,有活动的优惠,组队,订单明细,本地消息表,商品活动配置表,sku表。 + +### 4. 用户旅程 + +
+ +
+ +- 拼团全流程简图,以用户旅程来看各个节点所做的事项。 + +### 5. 场景举例 - 设计模式 + +#### 5.1 试算 + +
+ +
+ +
+ +
+ +为了解决整个交易过程的复杂场景,做了一套通用设计模式框架,并结合一套异步数据加载的多线程设计,来解决加载数据效率问题。让整个框架的灵活性非常高。 + +#### 5.2 退单 + +
+ +
+ +- 第一条退单链路,以工厂🏭方式获取执行责任链,责任链的作用是拆分原有的流程结构,分节点进行逐步处理。之后到退单的具体操作,则根据枚举策略,拿到对应执行的退单策略模式,完成退单动作。退单执行后发送MQ消息,驱动后续流程。 +- 第二条消息消息,从接收 MQ 开始,以 MQ 消息中的策略类型进行库存恢复操作。这部分保持原有的走对应的策略即可。 + +>整套项目,每一节都会有让你有新的感受,各个章节都有亮点设计,让你可以写到简历。 + diff --git a/docs/md/project/group-buy-market/qa.md b/docs/md/project/group-buy-market/qa.md new file mode 100644 index 000000000..648d63a43 --- /dev/null +++ b/docs/md/project/group-buy-market/qa.md @@ -0,0 +1,82 @@ +--- +title: Q&A:常见开发问题错误解答 +lock: no +--- + +# Q&A:常见开发问题错误解答 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/Yfbwo](https://t.zsxq.com/Yfbwo) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、断点调试 + +- [排查10个Bug](https://www.bilibili.com/video/BV1F6421w71e) +- [打断点查空指针](https://www.bilibili.com/video/BV1q1421Q7Uv) + +## 二、开发问题 + +### 1. Parameter 'teamIds' not found. + +如果遇到报错提示,有兼容问题,可以添加 `@Param("teamIds")` 到方法上 `Integer queryAllUserCount(@Param("teamIds") Set teamIds);` + +### 2. this is incompatible with sql_mode=only_full_group_by + +**添加 my.cnf 文件** + +```java +[client] +port = 3306 +default-character-set = utf8mb4 + +[mysqld] +user = mysql +port = 3306 +sql_mode = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES + +default-storage-engine = InnoDB +default-authentication-plugin = mysql_native_password +character-set-server = utf8mb4 +collation-server = utf8mb4_unicode_ci +init_connect = 'SET NAMES utf8mb4' + +slow_query_log +#long_query_time = 3 +slow-query-log-file = /var/log/mysql/mysql.slow.log +log-error = /var/log/mysql/mysql.error.log + +default-time-zone = '+8:00' + +[mysql] +default-character-set = utf8mb4 +``` + +**修改 docker compose** + +```javascript +mysql: + image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/mysql:8.0.32 + container_name: mysql + command: --default-authentication-plugin=mysql_native_password + restart: always + environment: + TZ: Asia/Shanghai + MYSQL_ROOT_PASSWORD: 123456 + ports: + - "13306:3306" + volumes: + - ./mysql/my.cnf:/etc/mysql/conf.d/mysql.cnf:ro + - ./mysql/sql:/docker-entrypoint-initdb.d + healthcheck: + test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ] + interval: 5s + timeout: 10s + retries: 10 + start_period: 15s + networks: + - my-network +``` + +- 修改 MySQL 8.0 安装配置 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2541-1\350\212\202\357\274\232\346\213\274\345\233\242\351\234\200\346\261\202\345\210\206\346\236\220.md" "b/docs/md/project/group-buy-market/\347\254\2541-1\350\212\202\357\274\232\346\213\274\345\233\242\351\234\200\346\261\202\345\210\206\346\236\220.md" new file mode 100644 index 000000000..6a044b56c --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2541-1\350\212\202\357\274\232\346\213\274\345\233\242\351\234\200\346\261\202\345\210\206\346\236\220.md" @@ -0,0 +1,56 @@ +--- +title: 【更】第1-1节:拼团需求分析 +pay: https://t.zsxq.com/Wit9C +--- + +# 《拼团交易平台系统》第1-1节:拼团需求分析 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/1VRZq](https://t.zsxq.com/1VRZq) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +今天是我们《拼团交易平台系统》项目学习的第1节,我们先从需求分析开始理解这样一个项目要做什么,要解决什么场景问题。同时也要知道研发是怎么承接的产品需求。 + +在互联网公司中,一个需求首先是从业务侧发起的盈利目标,拆分为不同的运营策略。再把对应的策略由产品经理设计为可以支撑市场运营操作完成盈利目标的具体项目。所以这里有一般有3个角色,包括;业务人员、运营人员、产品经理。他们分别在自己的岗位产生不同的资料,包括;MRD、BRD、PRD。 + +- 市场需求文档(MRD): MRD是从市场的角度出发,描述目标市场的需求和机会。它通常包括目标客户群、市场趋势、竞争分析、市场机会、产品定位以及产品应该实现的市场目标等。MRD通常由产品经理或市场分析师编写,目的是定义产品应该解决的市场问题和满足的用户需求。 +- 业务需求文档(BRD): BRD更侧重于业务角度,描述业务目标、业务流程、业务规则、业务问题以及业务需求。它是从组织的业务视角来定义需求,包括业务背景、业务目标、影响分析、风险评估等。BRD通常由业务分析师编写,目的是确保项目解决了正确的业务问题,并与公司的业务战略保持一致。 +- 产品需求文档(PRD): PRD是更详细的文档,它根据MRD和BRD中确定的需求,具体描述产品的功能性和非功能性需求。PRD包括用户故事、用例、功能列表、性能要求、界面设计、用户体验等。PRD通常由产品经理编写,目的是为设计团队和开发团队提供一个明确的、详细的产品实现指南。 + +而研发最终看到的就是这份 PRD 文档,根据产品对 PRD 文档与各个负责的业务线研发进行的评审,让研发了解本次项目所需完成的工作。之后研发在会后根据 PRD 文档进行详细的设计和系统建模。这一部分前期的工作几乎占据了整个项目周期的50%以上的时间。所以研发写代码,只是众多环节中的一环。 + +接下来,我们来对本次的《拼团交易平台系统》进行需求分析,让大家细致的了解拼团的场景业务。你可以把这个过程就是当成你自己在承接一次产品需求,在学习的过程中把思考和扩展的点在评论区讨论,互相积累补充。 + +## 一、项目背景 + +>拼团系统可以用于,《小型支付商城》、《OpenAI应用》,这类带有支付场景购买商品的系统。我们这里以这样两个系统作为使用场景作为举例。也可以作用于任何其他的交易类系统。 + +针对目前的`小型支付商城系统`、`OpenAI应用系统`,商品购买交易同比增速放缓,需要引入新的营销策略促进商品交易量。在交易数据统计分析中得到,市场存在同类竞品,商品价格设定低于目前我们的商品定价,所以用户购买意愿偏低。 + +所以为了盘活沉睡用户,需要适当降低商品价格。但为了达到传播的效果,所以需要引入拼团方式,以客带客,靠用户自身传播的方式进行交易拉新。这样的处理方式对比于 KOL,会让利商品价值到用户自身。【KOL 等同于抖音大主播直播卖货】 + +
+ +
+ +另一方面,通过本项目的增加,逐步完善功能产品和运营服务体系,优化整体的产品架构,增强市场竞争力。 + +## 二、产品方案 + +因为我们所实现的是一个平台类系统,可以满足各类交易场景的拼团需求接入。所以在实现这套系统时候,不要与其他系统耦合。并提供相关的研发侧对接标准。 + +此外我们要提供前端案例对接展示,满足后续其他系统,如;《小型支付商城》、《OpenAI应用》对接时候有可参考样例。 + +### 1. 前端页面 + +
+ +
+ +- 进入商品页后,查询是否配置了拼团活动。并进行优惠试算,拼团成团价,最低优惠展示。 +- 参与首次拼团、参与拼团中拼团。拼团完成则不在展示此条拼团。 +- 所有参与中的拼团统计拼团人员。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2541-2\350\212\202\357\274\232\346\213\274\345\233\242\345\272\223\350\241\250\350\256\276\350\256\241.md" "b/docs/md/project/group-buy-market/\347\254\2541-2\350\212\202\357\274\232\346\213\274\345\233\242\345\272\223\350\241\250\350\256\276\350\256\241.md" new file mode 100644 index 000000000..05df83c38 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2541-2\350\212\202\357\274\232\346\213\274\345\233\242\345\272\223\350\241\250\350\256\276\350\256\241.md" @@ -0,0 +1,39 @@ +--- +title: 【更】第1-2节:拼团库表设计 +pay: https://t.zsxq.com/gSHjj +--- + +# 《拼团交易平台系统》第1-2节:拼团库表设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/iteVm](https://t.zsxq.com/iteVm) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +编程的代码是对数据逻辑的呈现,数据流转调度的好坏来自于数据结构设计的是否合理。而库表的设计就是对你过往学习数据结构知识的检测。 + +在库表设计时你需要了解整个业务执行过程,在这些过程中需要哪些数据作为支撑。合理的数据结构的库表设计,会让你的系统逻辑实现容易被人理解。反之,你可能要做大量的代码处理,也就是算法过程复杂度会变得很高。 + +这块知识的积累,最佳的手段就是多做实际的项目。从库表设计到编码实现,从编码实现反推库表合理性。做的多了,自然也就懂了。 + +## 一、本章诉求 + +通过拼团业务,讲解拼团流程实现中,所需的库表结构。包括;运营视角的配置诉求、用户视角的使用诉求。 + +只要你可以看懂库表设计,基本你也可以了解整个业务系统是如何实现的了。有了这样的积累,进入公司接触新的项目时,可以先从库表进行了解。知道它们的流转关系,之后在看系统设计和代码实现会更加清晰。 + +## 二、库表关系 + +在设计一套库表时,需要根据业务流程划分出大块的功能区,知道这些功能区的流转关系。 + +
+ +
+ +- 首先,站在运营的角度,要为这次拼团配置对应的拼团活动。那么就会涉及到;给哪个渠道的什么商品ID配置拼团,这样用户在进入商品页就可以看到带有拼团商品的信息了。之后要考虑,这个拼团的商品所提供的规则信息,包括;折扣、时间、人数等。还要拿到折扣的一个试算金额。这个试算出来的金额,就是告诉用户,通过拼团可以拿到的最低价格。 +- 之后,站在用户的角度,是参与拼团。首次发起一个拼团与参与已存在的拼团进行数据的记录,达成拼团约定拼团人数后,开始进行通知。这个通知的设计站在平台角度可以提供回调,那么任何的系统也就都可以接入了。 +- 另外,为了支撑这套库表,也会有人群的设计。人群是互联网公司中非常常用的手段,比如要把所有符合某个条件的用户ID,全部写入到一个特定的 Redis 记录中,之后就可以专门为这些人做特定的拼团活动了。 +- 那么,拼团活动表,为什么会把折扣拆分出来呢。因为这里的折扣可能有多种迭代到一个拼团上。比如,给一个商品添加了直减10元的优惠,又对符合的人群id的用户,额外打9折,这样就有了2个折扣迭代。所以拆分出来会更好维护。这是对常变的元素和稳定的元素进行设计的思考。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2541-3\350\212\202\357\274\232\347\240\224\345\217\221\347\263\273\347\273\237\350\256\276\350\256\241.md" "b/docs/md/project/group-buy-market/\347\254\2541-3\350\212\202\357\274\232\347\240\224\345\217\221\347\263\273\347\273\237\350\256\276\350\256\241.md" new file mode 100644 index 000000000..140d609ca --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2541-3\350\212\202\357\274\232\347\240\224\345\217\221\347\263\273\347\273\237\350\256\276\350\256\241.md" @@ -0,0 +1,34 @@ +--- +title: 【更】第1-3节:研发系统设计 +pay: https://t.zsxq.com/ESHMx +--- + +# 《拼团交易平台系统》第1-3节:研发系统设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/UoowL](https://t.zsxq.com/UoowL) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +对于程序员👨🏻‍💻来说,虽然写代码是最直接的工作体现,但工作可不是就只写代码。就一个产品功能需求而言,研发在参与评审后,还需要对需求进行功能的研发系统设计。 + +这个过程是比较消耗时间的,一般在2-3天完成,或者大型项目与外部对接的较多的情况,会需要3-5天以上。一般初级的开发在不具有系统的把控能力的时候,会考虑在工程中写伪代码的过程,来梳理系统设计。包括;涉及模块、功能流程、外部对接、接口字段等,全新的系统还要做架构的设计、分层的设计、模块的设计等。 + +那么对于小伙伴们学习的本节来说,就是一个全新的系统,我们可以全部的了解到系统设计所需的内容。 + +## 一、本章诉求 + +通过对拼团需求的理解,进行研发系统设计。包括;用例图、系统建模、工程模型、功能流程、UML时序图。另外像是库表设计已经在前面完成了,它也属于研发系统设计的一部分,提前做了这部分是为了让大家更好的理解系统需求。 + +## 二、设计目的 + +为什么,不上来就写代码? + +在15年刚加入互联网大厂的时候,基本上是一上来就写代码。产品聊完需求,研发简单记录,之后就是打开工程直接编码了。需求是上午写的,代码是下午干的。这样对于刚起步阶段是比较合适的,可以快速迭代。 + +但随着公司的体系化越来越完整,一个小项目也变成一个个独立业务线的大项目,一个人开发也变成了一个团队开发。所有的系统功能的实现,一点点小问题,都可能是一个个大问题。甚至一个bug,一会时间就会被传到微博,之后就是一片的舆情和客诉。 + +所以,到了目前这个阶段,研发不能只是为了功能而直接开发。还要遵守一些列的流程,确保开发迭代的需求,都能平稳的交付。所以要有研发设计、要有评审、要有测试、要有预发、要有黑白名单验证和功能切量。 diff --git "a/docs/md/project/group-buy-market/\347\254\2542-10\350\212\202\357\274\232\350\264\243\344\273\273\351\223\276\346\212\275\350\261\241\346\250\241\346\235\277\350\256\276\350\256\241.md" "b/docs/md/project/group-buy-market/\347\254\2542-10\350\212\202\357\274\232\350\264\243\344\273\273\351\223\276\346\212\275\350\261\241\346\250\241\346\235\277\350\256\276\350\256\241.md" new file mode 100644 index 000000000..736655732 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-10\350\212\202\357\274\232\350\264\243\344\273\273\351\223\276\346\212\275\350\261\241\346\250\241\346\235\277\350\256\276\350\256\241.md" @@ -0,0 +1,37 @@ +--- +title: 【更】第2-10节:责任链抽象模板设计 +pay: https://t.zsxq.com/pUS6P +--- + +# 《拼团交易平台系统》第2-10节:责任链抽象模板设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/Ao3ox](https://t.zsxq.com/Ao3ox) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +做开发了这么久,其实业务类的开发大部分都是串联流程,其实复杂度并不高。但之所以最后非常难理解,是写的人,驾驭能力不强,把工程写的越来越难以理解。 + +而如果想让工程实现的逻辑好理解,就要做好解耦设计。而这些解耦的流程大部分是具有通用性的,所以我们可以设计出能解决大部分通用业务场景的共性模型结构。 + +在之前小傅哥就带着大家设计了一个规则树的模型,本节我们借着项目的开发在设计一款通用的责任链模型结构。 + +## 一、本章诉求 + +在拼团交易的下单锁定优惠的过程中,以及后续的流程,都会有简单的规则串联。所以,我们先来提前做好通用的责任链模型结构,便于后续使用。 + +本节会涉及到链表的基础数据结构知识,可以提前补充学习;[https://bugstack.cn/md/algorithm/data-structures/2022-07-22-linked-list.html](https://bugstack.cn/md/algorithm/data-structures/2022-07-22-linked-list.html) + +## 二、模型设计 + +责任链是一种简单的单链路结构,在工程中会有多个这样的单链,为了可以让不同的场景都能创建出自己的链,则需要解耦责任链的链路和执行,再有执行器处理。在本次实现中小傅哥会给大家体统两种责任链,让大家对照学习。设计如图; + +
+ +
+ +- 如图,这是一种多实例对象责任链的设计结构,会使用到如 Java JDK 源码中 Link 的方式填写链路,之后再有业务链路处理链路执行。而每一个链路都会被填充一个逻辑处理器的实现类(ILogicHandler)来处理具体的业务。 +- 那么,这样就很好的扩展了各种链路的使用诉求。我们可以结合代码来学习。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-11\350\212\202\357\274\232\344\272\244\346\230\223\350\247\204\345\210\231\350\264\243\344\273\273\351\223\276\350\277\207\346\273\244.md" "b/docs/md/project/group-buy-market/\347\254\2542-11\350\212\202\357\274\232\344\272\244\346\230\223\350\247\204\345\210\231\350\264\243\344\273\273\351\223\276\350\277\207\346\273\244.md" new file mode 100644 index 000000000..d08ebba70 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-11\350\212\202\357\274\232\344\272\244\346\230\223\350\247\204\345\210\231\350\264\243\344\273\273\351\223\276\350\277\207\346\273\244.md" @@ -0,0 +1,41 @@ +--- +title: 【更】第2-11节:交易规则责任链过滤 +pay: https://t.zsxq.com/PaSMk +--- + +# 《拼团交易平台系统》第2-11节:交易规则责任链过滤 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/wxTw6](https://t.zsxq.com/wxTw6) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +为什么公司里有些人做了很多事情,写了很多代码,但到最后也没有多大的成绩呢? + +其实很多时候就是太重复了,从刚开始对工程不理解,到能熟练上手以后,就从脑力活变成了体力活,一遍遍的重复自己。所以,就算是工作几年,也和前面刚开始工作差不多,就只是熟练了业务,但技术却没有提升。 + +所以,多提高编程思维,锻炼编码能力,一次次对工程进行抽象,提炼出骨架的核心设计,才是非常重要的。 + +## 一、本章诉求 + +完善拼团交易营销锁单的流程,增加锁单流程中的规则处理。 + +本节的规则过滤,会使用到前面章节设计的统一的设计模式框架中的责任链模式。对这类轻量的场景,一般只需要选择单链的执行模型即可,而与之对比的规则树,是适合于那种节点间的复杂分支流转。 + +## 二、业务流程 + +如图,增加交易规则处理; + +
+ +
+ +
+ +
+ +- 在前面章节,我们实现了拼团锁单中,参数校验、幂等校验、达成校验,之后做了营销试算和营销锁单。 +- 那么在本节,还需要对营销锁单继续完善,过滤拼团活动配置的规则。包括;活动的有效期、状态,以及个人参与拼团的次数。在实际公司中的项目里,还会有更多的规则要被处理。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-12\350\212\202\357\274\232\346\213\274\345\233\242\347\273\204\351\230\237\347\273\223\347\256\227\347\273\237\350\256\241.md" "b/docs/md/project/group-buy-market/\347\254\2542-12\350\212\202\357\274\232\346\213\274\345\233\242\347\273\204\351\230\237\347\273\223\347\256\227\347\273\237\350\256\241.md" new file mode 100644 index 000000000..5bb0a9553 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-12\350\212\202\357\274\232\346\213\274\345\233\242\347\273\204\351\230\237\347\273\223\347\256\227\347\273\237\350\256\241.md" @@ -0,0 +1,38 @@ +--- +title: 【更】第2-12节:拼团组队结算统计 +pay: https://t.zsxq.com/OvyY1 +--- + +# 《拼团交易平台系统》第2-12节:人群标签生成任务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/89N6w](https://t.zsxq.com/89N6w) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +其实互联网大厂也是从草蜢阶段过来的,所以到现在才有这么大的技术债。几乎每年我们都要为工程制定体系化建设的计划,新的工程采用纯心的架构模型实现,旧的工程分为重构计划和工程内编码治理。 + +通过这样的手段,让工程一点点从严重的腐化中拆解出来。所以你在学习咱们的新项目时候,也会感受到不断的抽象和治理,让代码变得容易被理解和迭代。 + +## 一、本章诉求 + +首先,你可以回忆下咱们整个拼团业务的流程。 + +拼团的过程是用户在商城下单,锁定拼团优惠(也就是拼团系统里锁单的过程)。之后就是用户给这笔商品完成支付交易,交易后不会直接发货,直至拼团组队完成后才会发货。 + +那么,这里有一个流程,就是支付完成后,需要做拼团数量的统计结算。如,拼团需要3个用户一起下单,那么每完成一笔支付,就要给拼团的组队加上一笔记录。这个就是本节要实现的流程。 + +## 二、业务流程 + +如图,拼团结算流程。 + +
+ +
+ +- 首先,交易订单的营销结算,核心就是更新拼团队伍的参与人数数量。每完成一笔支付,就有一笔拼团进度数量+1。 +- 之后,这里要知道,更新拼团订单的明细状态(交易完成)和更新拼团进度数量要在一个事务下完成。 +- 另外,更新拼团的进度要判断,当前是否为最后一次拼团完结状态。比如计算剩余1个,即可完成拼团目标量,那么这最后一笔更新完成后,既是整个拼团队伍的进度完成了。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-13\350\212\202\357\274\232\344\272\244\346\230\223\347\273\223\347\256\227\350\264\243\344\273\273\351\223\276\350\277\207\346\273\244.md" "b/docs/md/project/group-buy-market/\347\254\2542-13\350\212\202\357\274\232\344\272\244\346\230\223\347\273\223\347\256\227\350\264\243\344\273\273\351\223\276\350\277\207\346\273\244.md" new file mode 100644 index 000000000..8c60e7133 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-13\350\212\202\357\274\232\344\272\244\346\230\223\347\273\223\347\256\227\350\264\243\344\273\273\351\223\276\350\277\207\346\273\244.md" @@ -0,0 +1,44 @@ +--- +title: 【更】第2-13节:交易结算责任链过滤 +pay: https://t.zsxq.com/I2cmu +--- + +# 《拼团交易平台系统》第2-13节:交易结算责任链过滤 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/ScKNa](https://t.zsxq.com/ScKNa) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在我整个互联网编程的职业生涯中,有一段时间一个人负责了4-5个业务系统,还有3-4个技术类组件。领导看我有这么多系统,就给我招聘了几个人,让我把活分配出去。 + +但最开始我是比较抗拒的,因为这些系统已经非常手到擒来了,想做什么设计实现,也是能非常容易的迭代完成,并且随着每次的迭代也会做细腻的重构设计。 + +而我抗拒的原因是因为中间招聘的伙伴,有几次都是来了半年多又走了,系统又交接回来了。这个时候在对这个系统做实现时候,就会发现很多细节的地方,与预期非常不符。不过,后来也慢慢习惯了,增加了很多的评审、提供了的设计,让工程尽量别走型严重。 + +## 一、本站诉求 + +拼团交易结算的过程,需要一些列的规则过滤。包括;我们上一节提到的校验外部交易单的时间是否在拼团有效时间内,同时还有关于这笔外部交易单是否为有效的拼团锁单订单。另外像是 SC 渠道的有效性也需要在结算时进行校验。 + +所以,本节我们需要实现一套规则链,来处理这些业务规则。因为规则链已经被抽取为通用的模板了,那么本节使用起来会非常容易。 + +## 二、业务流程 + +如图,拼团交易结算流程设计; + +
+ +
+ +- 首先,本节的重点在于新增加结算规则过滤的责任链,处理;SC渠道管控、有效的外部交易单号、结算实现是否为拼团时效内。 +- 那么这里会有一些功能改造点; + - 拼团表,group_buy_order 增加 valid_start_time(有效开始时间)、valid_end_time(有效结束时间) 字段。用于每笔交易结算时候,用结算时间判断是否匹配到拼团有效时间范围内。 + - 拼团明细,group_buy_order_list 增加 out_trade_time(交易时间) 字段,记录每笔结算的订单结算的时间。随着状态更新的时候更新。 + - trade 领域下,lock 锁单。实体对象,修改名称。TradeRuleCommandEntity -> TradeLockRuleCommandEntity,TradeRuleFilterBackEntity -> TradeLockRuleFilterBackEntity 增加了 Lock 标识。便于在添加 TradeSettlementRuleCommandEntity、TradeSettlementRuleFilterBackEntity 时更好理解。 + - PayActivityEntity 添加 validTime,GroupBuyTeamEntity 添加 validStartTime、validEndTime + - trade 领域下,settlement 结算服务中,使用责任链模板,实现营销交易规则的过滤。SCRuleFilter(SC黑名单管控过滤 DCCService 配置新的属性 scBlacklist)、OutTradeNoRuleFilter(外部交易单号有效性过滤)、SettableRuleFilter(交易时间是否在拼团有效时间内过滤)、EndRuleFilter(结束节点封装返回数据) + - 交易服务,TradePaySettlementEntity 调用 tradeSettlementRuleFilter 责任链方法,并返回相关的数据信息。 + - settlementMarketPayOrder 结算一个事务下操作,增加 updateOrderStatus2COMPLETE 更新时候添加 outTradeTime 时间。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-14\350\212\202\357\274\232\346\213\274\345\233\242\345\233\236\350\260\203\351\200\232\347\237\245\344\273\273\345\212\241.md" "b/docs/md/project/group-buy-market/\347\254\2542-14\350\212\202\357\274\232\346\213\274\345\233\242\345\233\236\350\260\203\351\200\232\347\237\245\344\273\273\345\212\241.md" new file mode 100644 index 000000000..ec4f9860d --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-14\350\212\202\357\274\232\346\213\274\345\233\242\345\233\236\350\260\203\351\200\232\347\237\245\344\273\273\345\212\241.md" @@ -0,0 +1,42 @@ +--- +title: 【更】第2-14节:拼团回调通知任务 +pay: https://t.zsxq.com/IWiNu +--- + +# 《拼团交易平台系统》第2-14节:拼团回调通知任务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/ysSyW](https://t.zsxq.com/ysSyW) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +在微服务设计中,当一个微服务系统的流程结束后,要通知下一个微服务系统。这个通知的过程,可以是 RPC、MQ,也可以是 HTTP 方式。 + +RPC、MQ,这一类的都是需要有一个公用的注册中心,它的技术架构比较适合于公司内部的统一系统使用。如果是有和外部其他系统的对接,通常我们会使用 HTTP 这样统一标准协议的接口进行使用。 + +那么,本章节要为拼团组队交易结算完结后,实现一个回调通知的任务处理。告知另外的微服务系统可以进行后续的流程了。 + +注意:微信支付,支付宝支付,也是在完成支付后,做的这样的回调处理。 + +## 二、业务流程 + +如图,拼团结算组队完成,回调通知; + +
+ +
+ +- 首先,本节的重点在拼团成团后,实现回调通知流程。回调的过程,需要在用户锁单时需要增加一个回调的地址,并在拼团完结后发起回调。 +- 那么,这里的一些功能改造点; + - `group_buy_order` 在设计的时候有一个 `notify_url` 回调地址,本节我们修改库表添加上这个字段。并对工程中的 `dao&po&mapper` 操作,增加 `notify_url` 字段。 + - MarketTradeController 营销交易服务,lockMarketPayOrder 锁单接口入参对象,增加 notifyUrl 回调地址。并有 PayDiscountEntity 对象透传到 `TradeRepository#lockMarketPayOrder` 仓储操作。这样写到 `group_buy_order` 表就有回调地址了,等做回调操作的时候,就可以把这个地址写入到回调任务表中。 + - `TradeSettlementOrderService#settlementMarketPayOrder` 结算服务,需要把锁单记录中的 notify_url 拿到,放到 GroupBuyTeamEntity 中,这样在写入 notify_task 表记录的时候就可以把 notify_url 一起写入进去了。 + - 基于 okhttp 框架,封装对 http 接口的调用。用于处理调用外部其他微服务,实现回调通知的处理。因为外部的接口是随着每个服务调用拼团写入进来的 http 请求地址,所以在封装这部分调用的时候,要允许动态透传请求地址。实现类写到 infrastructure 基础设置层的 gateway 调用外部网关层。实现类;`GroupBuyNotifyService` 提供方法;`groupBuyNotify` + - 在交易结算服务类 ITradeSettlementOrderService,定义执行结算回调通知接口,包括;`execSettlementNotifyJob()、execSettlementNotifyJob(String teamId)` 一个是有入参的,一个无入参。这样可以指定给某个拼团队伍做结算。结算的过程就是调用 `GroupBuyNotifyService#groupBuyNotify` 完成回调通知,并根据返回的结果更新 notify_task 表状态记录(成功、失败、重试),并记录回调次数,小于5次的时候都可以继续回调。 + - 回调通知,可以分为两个阶段处理。一个是拼团完成后立即执行,另外一个任务补偿。立即执行是为了提供时效性,但因为远程的 http 调用受网络和服务的影响可能会失败,所以要增加一个任务补偿来做定时检查。其中立即执行在 `TradeSettlementOrderService#settlementMarketPayOrder -> settlementMarketPayOrder` 处理。另外定时任务在 `GroupBuyNotifyJob` 处理。 + - 测试接口,`trigger/http` 下,增加 `TestApiClientController` 接口实现类,提供回调接口服务。这个是模拟的其他的微服务,将来要提供的接口。 diff --git "a/docs/md/project/group-buy-market/\347\254\2542-15\350\212\202\357\274\232\346\240\271\346\215\256UI\345\261\225\347\244\272\345\260\201\350\243\205\346\216\245\345\217\243.md" "b/docs/md/project/group-buy-market/\347\254\2542-15\350\212\202\357\274\232\346\240\271\346\215\256UI\345\261\225\347\244\272\345\260\201\350\243\205\346\216\245\345\217\243.md" new file mode 100644 index 000000000..f036b2760 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-15\350\212\202\357\274\232\346\240\271\346\215\256UI\345\261\225\347\244\272\345\260\201\350\243\205\346\216\245\345\217\243.md" @@ -0,0 +1,33 @@ +--- +title: 【更】第2-15节:根据UI展示封装接口 +pay: https://t.zsxq.com/eQHFl +--- + +# 《拼团交易平台系统》第2-15节:根据UI展示封装接口 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/2C7df](https://t.zsxq.com/2C7df) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +根据在上一节使用 DeepSeek 实现的拼团 UI,设计并实现所需的服务端接口。 + +在互联网公司里的开发过程也是这样,产品在评审期间,会提供 UI 工程师做好的设计图,研发拿到设计图后,提供所需的接口提供相应的字段。 + +## 二、接口分析 + +如图,根据UI分析所需接口数据; + +
+ +
+ +- 紫色圈;10人再抢,是拼团的统计数据。类似的还有总共开多少团、成功的拼团等,如果有展示需求,都可以在拼团统计中给出。 +- 灰色圈;商品信息,商品金额、优惠金额、支付金额等。 +- 绿色圈;参与拼团,UI 调用的操作是锁单的处理。在完整的流程中是调用商城类系统,发起交易,之后由商城类系统进行营销锁单。我们这里模拟,所以从前端开始锁单。 +- 黄色券;这里不是真实对接扫码支付,所以要点支付完成,才能触发拼团结算。所以这里需要调用拼团里的结算接口。 diff --git "a/docs/md/project/group-buy-market/\347\254\2542-16\350\212\202\357\274\232\345\274\225\345\205\245RabbitMQ\345\210\206\345\270\203\345\274\217\345\244\232\347\253\257\346\266\210\350\264\271.md" "b/docs/md/project/group-buy-market/\347\254\2542-16\350\212\202\357\274\232\345\274\225\345\205\245RabbitMQ\345\210\206\345\270\203\345\274\217\345\244\232\347\253\257\346\266\210\350\264\271.md" new file mode 100644 index 000000000..031b7aae5 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-16\350\212\202\357\274\232\345\274\225\345\205\245RabbitMQ\345\210\206\345\270\203\345\274\217\345\244\232\347\253\257\346\266\210\350\264\271.md" @@ -0,0 +1,34 @@ +--- +title: 【更】第2-16节:引入RabbitMQ分布式多端消费 +pay: https://t.zsxq.com/fhRso +--- + +# 《拼团交易平台系统》第2-16节:引入RabbitMQ分布式多端消费 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/mipvF](https://t.zsxq.com/mipvF) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +引入 RabbitMQ 分布式技术框架,实现分布式消息消费和多服务消费的能力。 + +消息,是一种解耦服务间直接(http/rpc)调用的手段,以发送消息和接收消息的模式,完成业务流程的异步化处理。 + +**RabbitMQ 基础教程**:[https://bugstack.cn/md/road-map/rabbitmq.html](https://bugstack.cn/md/road-map/rabbitmq.html) + +## 二、业务流程 + +在互联网公司中,往往一个微服务发送出来的 MQ,除了自己接收消费处理自己的业务流程,也会有很多其他微服务进行消费。那么这里就会有一个 Topic,被多个应用消费的配置。如图; + +
+ +
+ +- 以拼团发送结算完成消息举例,拼团是负载均衡部署了2套服务,发送的MQ消息,由小型支付对接。 +- 那么,负载均衡的拼团服务,发送MQ后,自己的2套微服务,会分别接收到消息。另外一套小型支付,假设只部署了一套,那么这里会消费5个MQ消息。 +- 注意,以 RabbitMQ举例,这里会需要使用到同一套交换机,同一个路由Key,但队列要分别每个服务配置不同的。同时消息支持持久化,也就是拼团发送的MQ消息,即使小型支付服务暂时没有启动,也可以在启动后消费队列里的MQ消息。 diff --git "a/docs/md/project/group-buy-market/\347\254\2542-17\350\212\202\357\274\232\345\217\221\351\200\201MQ\347\273\223\347\256\227\346\266\210\346\201\257.md" "b/docs/md/project/group-buy-market/\347\254\2542-17\350\212\202\357\274\232\345\217\221\351\200\201MQ\347\273\223\347\256\227\346\266\210\346\201\257.md" new file mode 100644 index 000000000..8c18ac900 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-17\350\212\202\357\274\232\345\217\221\351\200\201MQ\347\273\223\347\256\227\346\266\210\346\201\257.md" @@ -0,0 +1,36 @@ +--- +title: 【更】第2-17节:发送MQ结算消息 +pay: https://t.zsxq.com/zvx7v +--- + +# 《拼团交易平台系统》第2-17节:发送MQ结算消息 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/OtfX8](https://t.zsxq.com/OtfX8) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +## 一、本章诉求 + +增加拼团结算完成 MQ 触达方式,HTTP、MQ 触达,由调用方通过入参类型决定。 + +MQ 一般用在企业内的微服务系统间通信,因为企业内的微服务,共用了一套的 MQ 注册中心,MQ 可以更加高效的触达和分布式部署。而对于企业外的调用,与我们完全不是一个公司的系统,那么不再同一个微服务环境内,则需要通过 HTTP 方式这样标准的协议调用。如;支付宝支付完成回调、微信公众号发送消息后的回调,都是基于 HTTP 的方式实现。 + +## 二、业务流程 + +如图,HTTP、MQ,由调用方配置使用那种方式进行处理。 + +
+ +
+ +- 用户创建营销锁单时,选择MQ、HTTP回调方式。这个类型会被写入到对应的拼团订单记录里。 + +
+ +
+ +- 拼团完成结算后,在根据写入到拼团订单的记录,回调的方式,来回调通知结算。 diff --git "a/docs/md/project/group-buy-market/\347\254\2542-18\350\212\202\357\274\232\346\266\210\350\264\271MQ\347\273\223\347\256\227\346\266\210\346\201\257.md" "b/docs/md/project/group-buy-market/\347\254\2542-18\350\212\202\357\274\232\346\266\210\350\264\271MQ\347\273\223\347\256\227\346\266\210\346\201\257.md" new file mode 100644 index 000000000..626547b08 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-18\350\212\202\357\274\232\346\266\210\350\264\271MQ\347\273\223\347\256\227\346\266\210\346\201\257.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第2-18节:消费MQ结算消息 +pay: https://t.zsxq.com/dZEBT +--- + +# 《拼团交易平台系统》第2-18节:消费MQ结算消息 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/rclqJ](https://t.zsxq.com/rclqJ) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +完善小型支付商城对拼团组队结算消息的处理,同时完成小型支付商城中支付交易结算消息的处理。这样我们整个系统就都具备分布式部署的能力了。也就是多个应用实例同时部署,一个MQ在一个应用实例消费宕机,可以被其他应用实例继续拉取消费。 + +## 二、业务流程 + +如图,MQ 在小型支付和拼团的执行流程。 + +
+ +
+ +- MQ 具有解耦、消峰,最终一致性的特性。所以很多的分布式设计中,都会引入 MQ 来解耦复杂的业务流程,除了数据库事务处理外的流程节点,则由 MQ 进行驱动。 +- 从拼团下单到锁单结算,完成后触达MQ消费。之后由小型支付商城消费 MQ 结算消息,变更订单状态,之后触达下一个支付结算的动作。在接收支付结算完成模拟发货。第二个MQ的发送不用写数据库任务来补偿,如果发MQ失败了,就直接抛异常重试继续发就可以。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-19\350\212\202\357\274\232\347\213\254\345\215\240\351\224\201\345\222\214\346\227\240\351\224\201\345\214\226\345\234\272\346\231\257\350\277\220\347\224\250.md" "b/docs/md/project/group-buy-market/\347\254\2542-19\350\212\202\357\274\232\347\213\254\345\215\240\351\224\201\345\222\214\346\227\240\351\224\201\345\214\226\345\234\272\346\231\257\350\277\220\347\224\250.md" new file mode 100644 index 000000000..4057a7ff0 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-19\350\212\202\357\274\232\347\213\254\345\215\240\351\224\201\345\222\214\346\227\240\351\224\201\345\214\226\345\234\272\346\231\257\350\277\220\347\224\250.md" @@ -0,0 +1,27 @@ +--- +title: 【更】第2-19节:独占锁和无锁化场景运用 +pay: https://t.zsxq.com/NxC2G +--- + +# 《拼团交易平台系统》第2-19节:独占锁和无锁化场景运用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/o7toC](https://t.zsxq.com/o7toC) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +以独占锁抢占方式,迭代拼团结算通知互备执行任务。再以无锁化设计,处理用户拼团锁单,库存抢占处理,降低对数据库的行锁压力,提高整体吞吐量。 + +## 二、业务流程 + +如图,两种锁应对的场景; + +
+ +
+ +- 分段锁,颗粒度缩小到库存维度。先加(incr)后锁的操作,是一种无锁化设计。锁的目的只是作为兜底。这类似于我们操作账户,操作完写一条流水。incr 操作是原子的,基本不会产生一样的值。但在实际生产中,遇到过集群的运维配置问题,以及业务运营配置数据问题,导致 incr 得到的值相同。 +- 独占锁,在分布式架构系统设计中,会有多个实例部署。这些实例都会做job任务的执行,为了保障既能让任务互备,同时不要重复执行。这里要加独占锁,谁抢占到谁执行。执行完成后,释放锁,下一轮继续抢占。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-1\350\212\202\357\274\232\345\210\235\345\247\213\345\267\245\347\250\213\346\220\255\345\273\272.md" "b/docs/md/project/group-buy-market/\347\254\2542-1\350\212\202\357\274\232\345\210\235\345\247\213\345\267\245\347\250\213\346\220\255\345\273\272.md" new file mode 100644 index 000000000..0cf736547 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-1\350\212\202\357\274\232\345\210\235\345\247\213\345\267\245\347\250\213\346\220\255\345\273\272.md" @@ -0,0 +1,62 @@ +--- +title: 【更】第2-1节:初始工程搭建 +pay: https://t.zsxq.com/1ZafX +--- + +# 《拼团交易平台系统》第2-1节:初始工程搭建 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/mT399](https://t.zsxq.com/mT399) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +可能不少小伙伴都学习过一些入门项目,简单的使用 IntelliJ IDEA 创建和使用。但对于在互联网公司中如何创建和使用一套新工程,往往是不清楚他们是使用了什么样的统一标准的。因为在公司中,那么多新项目要创建,不太可能让每个组的每个人都创建风格迥异的项目工程,这样对维护成本来说是非常大的。所以这里要建立工具和标准化。 + +所以,跟着小傅哥学习,不只是做项目。而是以互联网中大型公司标准化的方式来开发、设计、实现功能。 + +## 一、本章诉求 + +教会小伙伴使用统一标准脚手架方式创建项目工程,并了解工程模块的分层用途。以及完成2张关于拼团互动库表的创建和使用。 + +课程会循序渐进的从0到1,逐步带着大家完成项目的开发。开局只有一把 IntelliJ IDEA,完成项目后你可以学习到;业务、架构、设计、方案、配置、部署(Linux、Docker)等各项知识。 + +## 二、如何开始 + +站在读者视角,该怎么学习呢?这里做个必要说明和前置的知识提供。 + +### 1. 前置学习 + +小伙伴在学习的时候,可以依照课程的方式进行创建项目、变更配置、启动测试。这里有一些前置学习,包括:Git、Maven、Docker、脚手架,课程已经准备好了,可以刷下; + +- Git:[https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) +- Maven:[https://bugstack.cn/md/road-map/maven.html](https://bugstack.cn/md/road-map/maven.html) +- Docker [https://bugstack.cn/md/road-map/docker-what.html](https://bugstack.cn/md/road-map/docker-what.html) +- 脚手架:[https://bugstack.cn/md/road-map/ddd-archetype-maven.html](https://bugstack.cn/md/road-map/ddd-archetype-maven.html) + +另外课程会使用 Java JDK 1.8、Maven 3.8.x,软件已经提供好,可以直接下载;[https://t.zsxq.com/19Rnk98M0](https://t.zsxq.com/19Rnk98M0) - `附件内含有配置好阿里云镜像的 Maven` + +### 2. 开始学习 + +1. 你需要通过 git clone 命令,或者 IntelliJ IDEA 自动的检出工程方式,把项目工程检出到本地。关于如何使用Git检出项目,在前置学习里提供了教程。 +2. 检出代码后,你可以通过 IntelliJ IDEA 打开项目,并按照每一节最开始说明的本节对应的代码分支,把工程代码切换到对应的这一节。 +3. 接下来你可以通过课程的视频和小册以及提供的代码进行学习,并跟随课程每节要完成的内容,一步步操作。过程中可以参考课程的代码进行学习。如果自己的代码运行出问题的时候,可以运行课程的代码验证是环境问题还是个人代码问题。**另外注意运行课程代码,要修改对应的环境为你的本地环境,mysql、redis等** +4. 对于课程中每节涉及的库表,会放到工程 docs/dev-ops mysql 下。你可以每节学习创建一个新的库名称,之后导入。但要记得在工程 app/application-dev.yml 文件中修改对应的库名称。 + +### 3. 环境安装 + +课程提供了使用 Docker 部署 MySQL、Redis 环境的脚本。因为使用 Docker 可以随时方便卸载,不会污染本地电脑的本机环境。而且后续部署 Linux 云服务器也会非常顺手。 + +关于环境的安装; + +
+ +
+ +1. Windows + wsl2,本地使用 powershell 切换到工程文件夹,执行 `docker-comopse -f docker-compose-environment-aliyun.yml up -d` +2. Mac 电脑的适配性会更好,直接点击这里的绿色箭头即可安装。 +3. 如果本机配置有问题,也可以选择使用云服务器。课程中有云服务器的操作教程,部署起来更方便。云服务器教程:[https://bugstack.cn/md/road-map/linux.html](https://bugstack.cn/md/road-map/linux.html) + +> 环境安装后就可以使用 MySql、Redis 链接工具使用了,也可以手动更新库表。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-20\350\212\202\357\274\232\345\207\275\346\225\260\345\274\217\346\225\260\346\215\256\347\274\223\345\255\230\345\222\214\351\231\215\347\272\247\345\210\260DB\345\244\204\347\220\206.md" "b/docs/md/project/group-buy-market/\347\254\2542-20\350\212\202\357\274\232\345\207\275\346\225\260\345\274\217\346\225\260\346\215\256\347\274\223\345\255\230\345\222\214\351\231\215\347\272\247\345\210\260DB\345\244\204\347\220\206.md" new file mode 100644 index 000000000..576127833 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-20\350\212\202\357\274\232\345\207\275\346\225\260\345\274\217\346\225\260\346\215\256\347\274\223\345\255\230\345\222\214\351\231\215\347\272\247\345\210\260DB\345\244\204\347\220\206.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第2-20节:函数式数据缓存和降级到DB处理 +pay: https://t.zsxq.com/pEE1a +--- + +# 《拼团交易平台系统》第2-20节:函数式数据缓存和降级到DB处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/6IYJ6](https://t.zsxq.com/6IYJ6) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +以查询活动配置为场景,增加缓存处理。同时使用降级服务,控制走缓存还是走DB数据库。并把这部分功能统一抽象成函数式编程。 + +## 二、功能流程 + +如图,缓存和降级的使用; + +
+ +
+ +- 首先,在日常的业务场景中,很多高频使用的数据,都是从 Redis 缓存获取。如果缓存不存在,才会从数据库读取。 +- 之后,也会给缓存配置降级,如果缓存有问题,或者要做一些验证,必须从库里读取,则会动态的配置,让当时的操作从数据库获取。 +- 注意,整个操作过程,缓存、降级、数据库,是一整条代码编程。如果在每个方法里都加这样的内容,就会显得很臃肿,所以一般会抽象一个方法,使用函数式的方式进行编程,降低使用者的编码量。**这个技巧很重要** \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-21\350\212\202\357\274\232\345\274\225\345\205\245\346\211\263\346\211\213\345\267\245\347\250\213.md" "b/docs/md/project/group-buy-market/\347\254\2542-21\350\212\202\357\274\232\345\274\225\345\205\245\346\211\263\346\211\213\345\267\245\347\250\213.md" new file mode 100644 index 000000000..0f6a2d062 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-21\350\212\202\357\274\232\345\274\225\345\205\245\346\211\263\346\211\213\345\267\245\347\250\213.md" @@ -0,0 +1,35 @@ +--- +title: 【更】第2-21节:引入扳手工程 +pay: https://t.zsxq.com/YXJjM +--- + +# 《拼团交易平台系统》第2-21节:引入扳手工程 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/lqZKr](https://t.zsxq.com/lqZKr) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +引入星球内的[《通用技术组件 - 🔧扳手工程》](https://t.zsxq.com/o7IBm)到拼团系统中,替代原本在拼团系统编写的设计模式框架和动态配置(DCC)服务。 + +那为什么要引入这一个扳手工程呢? + +其实你可以想象下,在互联网公司中,有非常多的业务系统。这些业务系统都需要,解决一些非业务场景的共性问题。如,通用的设计模式,共用的错误码,统一的动态配置中心等等。当你进入公司以后,会发现这样的场景非常多。 + +所以,你学习本节不只是完成功能,而是吸收了同类的这样场景的设计方案,以后遇到各类这样的问题,都可以考虑做成一个独立的技术组件,让业务系统引入使用。 + +> 建议,学习扳手工程(1-3节)后在开始下面的内容。这样会很清楚扳手工程的一个设计和实现手段。 + +## 二、功能流程 + +如图,替换拼团工程中的通用能力; + +
+ +
+ +- 首先,要在工程中引入扳手工程的清单pom配置。在本节课程之前,已经把扳手工程发布到 maven 中心仓库,可以直接使用。地址:[https://mvnrepository.com/artifact/cn.bugstack.wrench/xfg-wrench-bom/3.0.0](https://mvnrepository.com/artifact/cn.bugstack.wrench/xfg-wrench-bom/3.0.0) +- 之后,删掉 group-buy-market 工程 types 下;设计模式(design/framework)、dcc(annotations/DccValue + DCCValueBeanFactory)。删除后,工程 install 会报错。这个时候就引入 xfg-wrench 扳手工程内提供的框架和组件即可。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-22\350\212\202\357\274\232\345\212\250\346\200\201\351\231\220\346\265\201\351\205\215\347\275\256.md" "b/docs/md/project/group-buy-market/\347\254\2542-22\350\212\202\357\274\232\345\212\250\346\200\201\351\231\220\346\265\201\351\205\215\347\275\256.md" new file mode 100644 index 000000000..59d507dbd --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-22\350\212\202\357\274\232\345\212\250\346\200\201\351\231\220\346\265\201\351\205\215\347\275\256.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第2-22节:动态限流配置 +pay: https://t.zsxq.com/IQsZl +--- + +# 《拼团交易平台系统》第2-22节:动态限流配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/Iazmx](https://t.zsxq.com/Iazmx) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过在拼团项目系统中,引入的[《通用技术组件 - 🔧扳手工程》](https://t.zsxq.com/o7IBm),使用其带有的动态限流组件,在拼团系统中配置使用。让通过 DCC 动态控制是否限流,让接口支持动态限流配置。 + +本节的代码量不多,主要是因为我们已经在`扳手工程`中完成了动态限流的设计和开发,到拼团系统中直接使用即可。建议学习本节内容时,前置的学习下扳手工程。 + +## 二、功能流程 + +如图,在接口方法中配置动态限流能力; + +
+ +
+ +- 首先,如,动态配置中心,统一设计模式,限流组件,都是在扳手工程内的引入的 bom 清单中。当需要使用限流组件时,直接在项目对应的模块下,引入pom即可。 +- 之后,限流组件的动态控制,依赖的是动态配置中心。所以,在使用限流组件时,则需要顺序的引入下动态配置中心。这里还有另外一种做法,如 Spring AI 发布框架组件时候,提供3层关系结构。包括;功能件、自动装配件、整合 Starter 件。那么我们这里如果想让用户更简单的使用限流组件,而不需要关心是否要引入动态配置中心组件,则可以为限流组件,发布一个带有动态配置中心的pom文件的组件,这样用户就不需要单独引入了。 diff --git "a/docs/md/project/group-buy-market/\347\254\2542-23\350\212\202\357\274\232ELK+AI MCP\346\243\200\347\264\242.md" "b/docs/md/project/group-buy-market/\347\254\2542-23\350\212\202\357\274\232ELK+AI MCP\346\243\200\347\264\242.md" new file mode 100644 index 000000000..2c7f9c307 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-23\350\212\202\357\274\232ELK+AI MCP\346\243\200\347\264\242.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第2-23节:ELK + AI MCP 检索 +pay: https://t.zsxq.com/AV4Jp +--- + +# 《拼团交易平台系统》第2-23节:ELK + AI MCP 检索 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/6BHXt](https://t.zsxq.com/6BHXt) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +为拼团项目增加 ELK 分布式日志采集和查询系统,以及通过 AI MCP 的方式,检索系统运行日志和数据分析。 + +系统的运行日志,是排查系统问题的重要手段,尤其是分布式复杂系统,一次行为请求会执行多个接口查询。这些接口查询会被分布式系统承接,到不同的服务节点上执行。因此分布式日志就非常有用,可以串联出在哪个系统上执行的,并拿到全部日志进行分析。 + +## 二、框架介绍 + +Elastic Stack 技术栈,别是 `Elasticsearch`、`Logstash`、`Kibana` 组成,简称 ELK 是一套针对日志数据做解决方案的框架。它使您能够聚合来自所有系统和应用程序的日志,分析这些日志,并创建可视化来进行应用程序和基础设施监控、更快的故障排除、安全分析等。 + +- E = Elasticsearch:Elasticsearch 是在 Apache Lucene 上构建的分布式搜索和分析引擎。对各种语言、高性能和无架构 JSON 文档的支持使 Elasticsearch 成为各种日志分析和搜索使用案例的理想选择。 +- L = Logstash:Logstash 是一个开源数据摄取工具,允许您从各种来源收集数据,转换数据,并将数据发送到您希望的目标。通过预构建的筛选器和对 200 多种插件的支持,Logstash 使用户能够轻松摄取数据,无论数据源或类型如何。 +- K = Kibana:Kibana 是一种数据可视化和挖掘工具,可以用于日志和时间序列分析、应用程序监控和运营智能使用案例。它提供了强大且易用的功能,例如直方图、线形图、饼图、热图和内置的地理空间支持。此外,付费的 Kibana 还有 x-pack-jdbc 可以使用,让你就像使用 MyBatis 操作 MySQL 数据库一样操作 Elasticsearch 数据。 + +综上,3个组件的组合使用。由 Logstash 将摄取、转换数据并将其发送到 Elasticsearch 为摄取的数据编制索引,并且分析和搜索这些数据。最终 Kibana 会将分析结果可视化。也就是你可以在 Kibana 上实时看到系统的运行日志。 diff --git "a/docs/md/project/group-buy-market/\347\254\2542-24\350\212\202\357\274\232\347\263\273\347\273\237\347\233\221\346\216\247+AIMCP\345\210\206\346\236\220.md" "b/docs/md/project/group-buy-market/\347\254\2542-24\350\212\202\357\274\232\347\263\273\347\273\237\347\233\221\346\216\247+AIMCP\345\210\206\346\236\220.md" new file mode 100644 index 000000000..404513383 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-24\350\212\202\357\274\232\347\263\273\347\273\237\347\233\221\346\216\247+AIMCP\345\210\206\346\236\220.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第2-24节:系统监控 + AI MCP 分析 +pay: https://t.zsxq.com/Gz6PG +--- + +# 《拼团交易平台系统》第2-24节:系统监控 + AI MCP 分析 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/afbln](https://t.zsxq.com/afbln) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +为拼团系统配置`普罗米修斯`监控,并使用 AI MCP 工具,分析监控数据。通过这样的学习,让大家了解到企业里如何使用 AI 在软件工程方面的提效。 + +现在的互联网公司内,在每个大的业务部门,都陆续的开始搭建了 AI 团队。专门为本团队的业务系统做关于 AI 提效方面的开发。像是智能巡检、系统监控、代码开发、PRD文档评审、代码评审、智能客服等,都是 AI 方面的应用。 + +## 二、功能流程 + +如图,整个功能流程如下; + +
+ +
+ +- 首先,拼团应用系统,需要配置对应的监控暴漏端点,这样的目的是为了把数据暴漏出去。注意,对外使用要增加上验证权限,避免数据暴漏出去。 +- 之后,进入普罗米修斯和监控面板的配置文件,修改对应的配置,主要是IP、数据库文件等(课程下有说明),之后通过 Docker 启动,普罗米修斯 + 监控面板 + AI MCP 服务端,这样的目的是为了采集来自于系统的暴漏端点数据。 +- 最后,监控相关配置完成后,调用拼团系统接口,产生一些业务数据。之后通过 AI 客户端,配置 MCP 服务,这样就可以通过 AI 客户端分析系统监控情况了。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-25\350\212\202\357\274\232\351\200\206\345\220\221\346\265\201\347\250\213\345\234\272\346\231\257\345\210\206\346\236\220.md" "b/docs/md/project/group-buy-market/\347\254\2542-25\350\212\202\357\274\232\351\200\206\345\220\221\346\265\201\347\250\213\345\234\272\346\231\257\345\210\206\346\236\220.md" new file mode 100644 index 000000000..e94bea4e6 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-25\350\212\202\357\274\232\351\200\206\345\220\221\346\265\201\347\250\213\345\234\272\346\231\257\345\210\206\346\236\220.md" @@ -0,0 +1,32 @@ +--- +title: 【更】第2-25节:逆向流程场景分析 +pay: https://t.zsxq.com/Batrz +--- + +# 《拼团交易平台系统》第2-25节:逆向流程场景分析 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/afbln](https://t.zsxq.com/afbln) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +对于拼团这样的业务场景,有正向流程的下单,就有逆向流程退单。基本你在各类应用 APP 中,如;商城、外卖、出行、票务等,都是可以下单或者退单的。这也是一个互联网toc业务场景中,非常常见的流程。 + +所以,从这一节开始,我们将进入逆向流程的分析和后续的功能实现。先有这一节的场景分析,让小白伙伴知道这样一个流程,之后在详细设计和编码也会更加容易。 + +建议;学习这一节的时候,可以看看其他的应用类 APP 他们的退单流程都发生了什么动作,这些知识点以及不同的处理方式,都可以作为你学习后的对项目扩展实现的增强点。 + +## 二、功能流程 + +如图,以用户旅途视角来看整个拼团流程。 + +
+ +
+ +- 首先,我们来回顾下前面章节,完成的业务流程。从运营配置拼团活动,到用户从「小型支付商城(对接的一个场景)」,开始查看带有拼团优惠的上,进行试算,过滤规则。再到参与拼团,完成下单和一系列的流程处理。之后是拼团对于支付收单的入账计算,达成拼团目标后,回调(HTTP/MQ)商城服务,完成整个交易过程。 +- 那么,从本节开始,我们要考虑的是如何处理逆向流程,也就是退单的过程。退单分为当前过程中,拼团是否完成,未完成则根据是否支付了,取消锁单量和完成量。如果拼团已完成,则取消锁单量和完成量,拼团优惠释放后,则回调商城(refundGroupBuySuccess),完成退单退货服务。 +- 一般,对于已经完成拼团的,有用户退单是不会对其他用户已经完成交易的进行退单的,会造成很差的体验。这部分成本往往由商家和平台分摊,毕竟平台的目的是为了卖货。 diff --git "a/docs/md/project/group-buy-market/\347\254\2542-26\350\212\202\357\274\232\346\234\252\346\224\257\344\273\230\351\200\200\345\215\225\346\265\201\347\250\213.md" "b/docs/md/project/group-buy-market/\347\254\2542-26\350\212\202\357\274\232\346\234\252\346\224\257\344\273\230\351\200\200\345\215\225\346\265\201\347\250\213.md" new file mode 100644 index 000000000..6282b0fdd --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-26\350\212\202\357\274\232\346\234\252\346\224\257\344\273\230\351\200\200\345\215\225\346\265\201\347\250\213.md" @@ -0,0 +1,32 @@ +--- +title: 【更】第2-26节:未支付退单流程(枚举策略模式应用) +pay: https://t.zsxq.com/S9cnj +--- + +# 《拼团交易平台系统》第2-26节:未支付退单流程(枚举策略模式应用) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/MVr1a](https://t.zsxq.com/MVr1a) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +退单,分为退掉拼团组队记录和外部对接的商城(也可以是其他平台)退单。当前我们在处理的流程为退掉拼团的组队记录,在这部分流程中,还包括;`未支付退单`、`已支付未成团退单`、`已支付已成团退单`。 + +本节我们先来串联`未支付退单`部分,搭建此部分的领域功能服务逻辑,以及编写出退单策略框架结构。整体的功能实现,后续还会引入责任链进行优化处理。可以先对照使用责任链和不使用时候的代码实现方式。 + +## 二、功能流程 + +如图,退单流程设计领域结构。 + +
+ +
+ +- 首先,在 trade 领域层,新增加一个逆向流程接口,之后在领域层增加退单策略。 +- 之后,退单策略分为三个实现类,`未支付退单`、`已支付未成团退单`、`已支付已成团退单`,本节先实现其中未支付退单。由未支付退单,一定是这个人参与了锁单,但拼团未完成。 +- 最后,在仓储层实现实现对数据库的事务操作。更新退单记录,更新拼团锁单量扣减。 +- 此外,本部分在后续章节还要迭代,使用设计模式的方式优化实现逻辑。因为整个退单的步骤也比较多,还包括了后需要发送 MQ 消息的过程。 + diff --git "a/docs/md/project/group-buy-market/\347\254\2542-27\350\212\202\357\274\232\345\267\262\346\224\257\344\273\230\346\234\252\346\210\220\345\233\242\351\200\200\345\215\225.md" "b/docs/md/project/group-buy-market/\347\254\2542-27\350\212\202\357\274\232\345\267\262\346\224\257\344\273\230\346\234\252\346\210\220\345\233\242\351\200\200\345\215\225.md" new file mode 100644 index 000000000..09c490f4a --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-27\350\212\202\357\274\232\345\267\262\346\224\257\344\273\230\346\234\252\346\210\220\345\233\242\351\200\200\345\215\225.md" @@ -0,0 +1,32 @@ +--- +title: 【更】第2-27节:已支付未成团退单 +pay: https://t.zsxq.com/Aw3qI +--- + +# 《拼团交易平台系统》第2-27节:已支付未成团退单 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/319Es](https://t.zsxq.com/319Es) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +继续迭代退单流程,处理已支付未成团退单。 + +而对于已支付未成团的退单,拼团退单完成后,需要驱动退款流程。也就是另外一个项目(小型支付)在用户发起退单后,需要先把优惠逆向处理掉,之后等待逆向完成进行支付退款。 + +那么,为什么不是项目(小型支付)调用完拼团,就直接退款呢。其实是做直接退款的,但有时候会出现一个临界的异常。如,调用拼团退掉优惠完成后,rpc/http 请求返回结果,可能因为网络超时,导致项目(小型支付)拿不到结果。这个时候,一种是可以重试,另外一种是可以等待MQ消息,驱动后续流程。而在大厂中,MQ消息驱动,是更为常见的使用方式。 + +## 二、功能流程 + +如图,退单流程设计领域结构。 + +
+ +
+ +- 首先,整体我们设计了3个退单策略,`未支付退单策略`、`已支付未成团退单策略`、`已支付已成团退单策略`。在这几个章节,会陆续的处理。 +- 之后,结合上一节的`未支付退单策略`,本节继续实现`已支付未成团退单策略`。不过这里有一个差异,需要写一个本地消息表,对于已支付的退单,要发送一个退单完成的MQ消息。其实后续`未支付退单策略`也要补充这个MQ,用于恢复参与拼团量。 +- 另外,因为这里也用到了,MQ 的发送和任务补偿。所以要把之前写到 trade 结算里的任务发送操作,抽取一个接口方法,进行共用。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-28\350\212\202\357\274\232\345\267\262\346\224\257\344\273\230\345\267\262\346\210\220\345\233\242\351\200\200\345\215\225.md" "b/docs/md/project/group-buy-market/\347\254\2542-28\350\212\202\357\274\232\345\267\262\346\224\257\344\273\230\345\267\262\346\210\220\345\233\242\351\200\200\345\215\225.md" new file mode 100644 index 000000000..d3ca3fc9d --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-28\350\212\202\357\274\232\345\267\262\346\224\257\344\273\230\345\267\262\346\210\220\345\233\242\351\200\200\345\215\225.md" @@ -0,0 +1,32 @@ +--- +title: 【更】第2-28节:已支付已成团退单 +pay: https://t.zsxq.com/msTia +--- + +# 《拼团交易平台系统》第2-28节:已支付已成团退单 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/PI7bY](https://t.zsxq.com/PI7bY) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +本章节是退单流程中最后一个步骤,对已支付,已成团状态的订单进行退单操作。 + +通过我们在前面的流程分析,已成团的退单,则只退个人单,不会影响到全局所有人的订单。那么这里如果一个拼团有多人退单,哪怕整个队伍只有一个人没有退单,也是可以的。 + +注意,这类的业务流程,主要受公司的产品设计的流程影响。如果一些特点的任务,一人退单,全队解散。那么就要针对不同类型的退单,做不同的策略实现。这块可以思考下。 + +## 二、功能流程 + +如图,退单流程设计领域结构。 + +
+ +
+ +- 如图,本节扩展实现第3个操作,`已支付已成团退单策略`的处理。 +- 注意,所有的退单策略,都要发MQ,本节把未支付未成团,也添加上MQ的发送。但要注意,添加MQ,他们都属于同一类,task 任务表要增加类型区分和 uuid 字段仿重。 + diff --git "a/docs/md/project/group-buy-market/\347\254\2542-29\350\212\202\357\274\232\351\200\200\345\215\225\351\224\201\345\215\225\351\207\217\346\201\242\345\244\215.md" "b/docs/md/project/group-buy-market/\347\254\2542-29\350\212\202\357\274\232\351\200\200\345\215\225\351\224\201\345\215\225\351\207\217\346\201\242\345\244\215.md" new file mode 100644 index 000000000..f7040d2e1 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-29\350\212\202\357\274\232\351\200\200\345\215\225\351\224\201\345\215\225\351\207\217\346\201\242\345\244\215.md" @@ -0,0 +1,28 @@ +--- +title: 【更】第2-29节:退单锁单量恢复 +pay: https://t.zsxq.com/4p5Vd +--- + +# 《拼团交易平台系统》第2-29节:退单锁单量恢复 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/o8Sz5](https://t.zsxq.com/o8Sz5) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +接收拼团组队退单消息,驱动锁单量redis库存恢复操作。这是一个数据库和缓存数据一致性的设计,在很多场景都非常实用。 + +## 二、功能流程 + +如图,退单恢复库存核心流程; + +
+ +
+ +- 首先,退单恢复库存最重要的就是数据一致性。数据库中已经变更完 lock 锁单记录了,之后就是更新 redis 缓存的量。 +- 那么,这里为了保证更新 redis 是一致性库存的,且成功的。所以需要添加一个分布式锁,来维护,确保不会被重复更新。 +- 所以,当加锁失败则表示重复恢复库存,如果incr 失败,则删掉分布式锁,抛异常,让 MQ 重试。这个概率很低,但也需要添加,以保证整体的可靠性。 diff --git "a/docs/md/project/group-buy-market/\347\254\2542-2\350\212\202\357\274\232\350\257\225\347\256\227\346\250\241\345\236\213\346\212\275\350\261\241\346\250\241\346\235\277\350\256\276\350\256\241.md" "b/docs/md/project/group-buy-market/\347\254\2542-2\350\212\202\357\274\232\350\257\225\347\256\227\346\250\241\345\236\213\346\212\275\350\261\241\346\250\241\346\235\277\350\256\276\350\256\241.md" new file mode 100644 index 000000000..c4d04df77 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-2\350\212\202\357\274\232\350\257\225\347\256\227\346\250\241\345\236\213\346\212\275\350\261\241\346\250\241\346\235\277\350\256\276\350\256\241.md" @@ -0,0 +1,37 @@ +--- +title: 【更】第2-2节:试算模型抽象模板设计 +pay: https://t.zsxq.com/uTLt9 +--- + +# 《拼团交易平台系统》第2-2节:试算模型抽象模板设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/fEB5s](https://t.zsxq.com/fEB5s) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +编程能力突飞猛进的成长,往往都来自于对复杂业务场景的拆分解耦和设计模式运用过程锻炼。虽然有时候是同样的业务,但不同的编程方式,带来的思维锻炼也是不同的。 + +就像 CRUD 的方式写代码,只能让你了解业务流程,但不会提高工程建模能力和编程思维的提升。所以整个小傅哥带着你做的项目,都会循序渐进的为你从模型设计解决业务场景问题出发。既可以提高你的编程思维,也能锻炼你的编码能力。 + +## 一、本章诉求 + +在一个工程中,随着不断地承接业务需求逻辑的实现,会有很多复杂场景需要解决。这个时候就会引入设计模式进行解耦和实现,提高工程代码的扩展性。 + +但随着开发的场景越来越多,在各个service实现中会存在相同的设计模式,如果是不同的人开发,那么一个责任链,一个规则树,也会有非常多的实现方式。那么这样就会导致后面在进入开发的人,对已存在的代码,维护的成本就越来越高了。 + +所以,本节小傅哥带着大家先做设计模式抽象模板的通用结构定义,添加一个 tree规则树抽象模型,在引入到工程中进行使用。这样后续工程中就可以不断的定义通用的设计模式被不同的场景统一使用了。 + +## 二、模型设计 + +这是一种链式的多分支规则树模型结构,由功能节点自行决定后续流程的执行链路。它的设计比责任链的扩展性更好,自由度也更高。 + +
+ +
+ +- 首先,定义抽象的通用的规则树模型结构。涵盖;StrategyMapper - 策略映射器、StrategyHandler - 策略处理器、`AbstractStrategyRouter` - 策略路由抽象类。通过泛型设计允许使用方可以自定义出入参和动态上下文,让抽象模板模型具有通用性。 +- 之后,由使用方自定义出工厂、功能抽象类和一个个流程流转的节点。这些节点可以自由组装进行流转,相比于责任链它的实现方式更具有灵活性。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-30\350\212\202\357\274\232\350\256\276\350\256\241\346\250\241\345\274\217\351\207\215\346\236\204\351\200\200\345\215\225.md" "b/docs/md/project/group-buy-market/\347\254\2542-30\350\212\202\357\274\232\350\256\276\350\256\241\346\250\241\345\274\217\351\207\215\346\236\204\351\200\200\345\215\225.md" new file mode 100644 index 000000000..fdf87674b --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-30\350\212\202\357\274\232\350\256\276\350\256\241\346\250\241\345\274\217\351\207\215\346\236\204\351\200\200\345\215\225.md" @@ -0,0 +1,31 @@ +--- +title: 【更】第2-30节:设计模式重构退单 +pay: https://t.zsxq.com/DJCKB +--- + +# 《拼团交易平台系统》第2-30节:设计模式重构退单 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/TsTt5](https://t.zsxq.com/TsTt5) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过设计模式重构现有退单流程。 + +在整个退单的功能实现的过程中,是没有过多引入设计模式的,更多的是以完成功能流程为主。那么本节我们则使用责任链、抽象类、工厂,来把目前的退单流程设计进行优化调整。 + +这样做的目的是为了让小白伙伴,具备了业务流程的理解和对应代码功能的开发基础上,在学习使用设计模式来优化流程代码,会更加得心应手! + +## 二、功能流程 + +如图,整个退单通过设计模式优化的结构; + +
+ +
+ +- 第一条退单链路,以工厂🏭方式获取执行责任链,责任链的作用是拆分原有的流程结构,分节点进行逐步处理。之后到退单的具体操作,则根据枚举策略,拿到对应执行的退单策略模式,完成退单动作。退单执行后发送MQ消息,驱动后续流程。 +- 第二条消息消息,从接收 MQ 开始,以 MQ 消息中的策略类型进行库存恢复操作。这部分保持原有的走对应的策略即可。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-31\350\212\202\357\274\232\351\200\200\350\256\242\346\216\245\345\217\243\345\222\214\345\256\232\346\227\266\344\273\273\345\212\241.md" "b/docs/md/project/group-buy-market/\347\254\2542-31\350\212\202\357\274\232\351\200\200\350\256\242\346\216\245\345\217\243\345\222\214\345\256\232\346\227\266\344\273\273\345\212\241.md" new file mode 100644 index 000000000..b0cae63ab --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-31\350\212\202\357\274\232\351\200\200\350\256\242\346\216\245\345\217\243\345\222\214\345\256\232\346\227\266\344\273\273\345\212\241.md" @@ -0,0 +1,29 @@ +--- +title: 【更】第2-31节:退订接口和定时任务 +pay: https://t.zsxq.com/S4pdB +--- + +# 《拼团交易平台系统》第2-31节:退订接口和定时任务 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/Ga9uG](https://t.zsxq.com/Ga9uG) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在触发器层实现调度逻辑,一个是退单的 HTTP 服务接口实现,另外一个是 Job 定时任务处理。 + +HTTP 退单接口为的是给外部系统(小型支付)调用使用,Job 任务则是定时检查是否有参与拼团锁单,但超时没有完成支付的进行拼团组队的,则进行自动退单处理。 + +## 二、功能流程 + +如图,退单触发条件流程图; + +
+ +
+ +- 退单的触发流程分为2类,一个是定时任务处理,自动扫描未完成订单,另外一个是提供接口,让外部调用方进行处理。 + diff --git "a/docs/md/project/group-buy-market/\347\254\2542-3\350\212\202\357\274\232\345\244\232\347\272\277\347\250\213\345\274\202\346\255\245\346\225\260\346\215\256\345\212\240\350\275\275.md" "b/docs/md/project/group-buy-market/\347\254\2542-3\350\212\202\357\274\232\345\244\232\347\272\277\347\250\213\345\274\202\346\255\245\346\225\260\346\215\256\345\212\240\350\275\275.md" new file mode 100644 index 000000000..f1ad7ec32 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-3\350\212\202\357\274\232\345\244\232\347\272\277\347\250\213\345\274\202\346\255\245\346\225\260\346\215\256\345\212\240\350\275\275.md" @@ -0,0 +1,36 @@ +--- +title: 【更】第2-3节:多线程异步数据加载 +pay: https://t.zsxq.com/yofwB +--- + +# 《拼团交易平台系统》第2-3节:多线程异步数据加载 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/UoiFa](https://t.zsxq.com/UoiFa) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在互联网公司中的业务功能开发,会非常重视接口的响应效率,一般整体的接口响应要控制在350毫秒,那么在每个细分领域的接口可能会被压缩到50~100毫秒。 + +这样的响应时间对于一些简单的接口到不会有什么影响,但如果是复杂较多的业务流程串联的接口,那么控制接口的响应时长就是一个非常大的挑战了。对于这样的情况,往往会引入异步线程加载数据的方式进行处理,之后在做统一的逻辑处理,这样就可以很好的降低接口响应时间。 + +## 一、本章诉求 + +扩展规则树模型结构,增加异步数据加载区。将用于试算营销优惠的接口使用异步线程进行加载,之后写入上下文,用于后续的逻辑处理。 + +这部分的模型设计是非常巧妙的,通过解耦逻辑和划分功能区,让代码具有了文档属性,看到对应的类和类下的方法区,就可以轻松的理解代码实现方式。这样的处理非常有利于后续功能的迭代。 + +## 二、模型链路 + +如图,为整个模型链路的执行过程图; + +
+ +
+ +- 首先,对通用设计模式树结构扩展出异步数据加载区,这样可以把接口实现中所需的数据前置到异步数据加载区完成加载操作。以此提高接口的响应效率。 +- 之后,本节串联功能节点,并在 MarketNode 节点,添加数据加载操作。 +- 另外,注意本节需要新增加一个表 sku,也就是商品信息表,通过商品信息表获得当前商品的价格配置,以此来做商品的折扣计算。这块在实际生产中有两种实现方式,一种是每次都调用外部接口获取商品,另外一种是有商品统一同步库可以查询。我们这里先通过一个统一的商品库进行处理。那么后续谁要对接这个系统,就调用sku商品库,同步好商品即可。【库表已更新到工程下】 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-4\350\212\202\357\274\232\347\255\226\347\225\245\346\250\241\345\274\217\344\274\230\346\203\240\346\212\230\346\211\243\350\256\241\347\256\227.md" "b/docs/md/project/group-buy-market/\347\254\2542-4\350\212\202\357\274\232\347\255\226\347\225\245\346\250\241\345\274\217\344\274\230\346\203\240\346\212\230\346\211\243\350\256\241\347\256\227.md" new file mode 100644 index 000000000..30ccb4654 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-4\350\212\202\357\274\232\347\255\226\347\225\245\346\250\241\345\274\217\344\274\230\346\203\240\346\212\230\346\211\243\350\256\241\347\256\227.md" @@ -0,0 +1,35 @@ +--- +title: 【更】第2-4节:策略模式优惠折扣计算 +pay: https://t.zsxq.com/NGF2u +--- + +# 《拼团交易平台系统》第2-4节:策略模式优惠折扣计算 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/inWxL](https://t.zsxq.com/inWxL) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +程序员的价值不在于只是完成业务需求,而是怎么架构系统、设计边界、凝练功能,让原本只是 CRUD 串联大量逻辑的代码,分区、分职责、用不同的策略模型承接。以此提高软件的交付质量,才是程序员的价值。 + +所以,我也从不敢说什么东西没有用。比如;DDD、设计模式,否则也不会有那么多我们日常使用源码框架,都有这些思想的体现了。 + +## 一、本章诉求 + +通过策略模式处理多类型折扣方式的逻辑计算,同时设定抽象模板,用于扩展后续人群标签的过滤。 + +不断的拆解功能逻辑边界的过程,比只是编写流水式代码要重要的多。在整个系统实现的过程中,要多体会这些思想。 + +## 二、模型设计 + +如图,继续在执行链路上像乐高积木一样拼装优惠折扣的计算逻辑; + +
+ +
+ +- 首先,MarketNode 节点的数据异步加载工作已经在上一节完成,这一节开始使用这里的数据做折扣计算。 +- 之后,折扣是在数据库中配置的,按照类型包括;ZJ - 直减、MJ - 满减、ZK - 折扣、N - n元购。那么这些不同的类型就可以用策略模型进行包装,每个实现类专门负责自己的逻辑计算。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-5\350\212\202\357\274\232\344\272\272\347\276\244\346\240\207\347\255\276\346\225\260\346\215\256\351\207\207\351\233\206.md" "b/docs/md/project/group-buy-market/\347\254\2542-5\350\212\202\357\274\232\344\272\272\347\276\244\346\240\207\347\255\276\346\225\260\346\215\256\351\207\207\351\233\206.md" new file mode 100644 index 000000000..22146d2c0 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-5\350\212\202\357\274\232\344\272\272\347\276\244\346\240\207\347\255\276\346\225\260\346\215\256\351\207\207\351\233\206.md" @@ -0,0 +1,41 @@ +--- +title: 【更】第2-5节:人群标签数据采集 +pay: https://t.zsxq.com/ovDqo +--- + +# 《拼团交易平台系统》第2-5节:人群标签数据采集 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/sWPg0](https://t.zsxq.com/sWPg0) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在抖音里经常可能会刷到一些视频,说:“你搜白酒,如果出来的都是茅台、五粮液”,那么证明你是有钱人! + +这个东西其实到不一定多准,但这里体现了一个技术上的东西叫做人群标签🏷,所有你的行为所产生的数据,都会被不同的标签类型统计。比如;年龄、性别、购物喜好、品类喜好、下单频次、浏览频次、搜索频次等。这些都会被归类为人群标签。 + +其实,还有一个。怎么感觉自己微信上聊天,过一会在京东、拼多多搜索,就出来了自己聊天时候的商品呢?这里一种是来自于输入法内容的获取,另外一个是广告联盟,这些商家的广告数据是互通的。你在一个地方搜索过,那么在进去到其他商城里就会展示出你搜索的数据。 + +所以,在互联网运营的手里中,你早已被圈定好该给你什么,才能刺激到你做什么。 + +## 一、本章诉求 + +以轻量化的方式构建人群标签数据,将人群数据写入到 Redis BitMap 用于后续使用。 + +在公司中,所有部门产生的业务数据都会回流到数仓,它有一个非常庞大的数据集市系统。之后这些数据会被量化分析师使用,通过 R 语言建模,执行模型任务,把符合模型所需的标签数据跑到一个新的指定表文件中,这些文件在通过加工存放到 Redis BitMap 进行使用。一般一个标签可能会有 50万、100万、500万的数据规模。 + +有了这些标签数据,运营人员就可以精准的对这些用户做定向活动投放,比如;特定的券、特定的通知等。以此达到更加精准的运营效果。 + +## 二、业务流程 + +虽然,我们不能像公司那样有那么大规模的数据量,但我们也可以仅使用拼团商品的数据,做人群标签的实现,让大家了解这样一个场景。 + +
+ +
+ +- 首先,人群标签是通过创建的采集任务所产生的数据。任务里包含了要采集业务中什么类型的数据规则。本项目中会采集拼团交易数据,不过本节还没有这类数据,所以先来模拟这部分数据。 +- 之后,把采集的数据除了放数据库,还需要写入到 Redis 的 BitMap 中,这个数据结构比较适合高并发场景判断用户是否存在。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-6\350\212\202\357\274\232\346\213\206\345\210\206\345\272\223\350\241\250\345\205\263\350\201\224\345\205\263\347\263\273.md" "b/docs/md/project/group-buy-market/\347\254\2542-6\350\212\202\357\274\232\346\213\206\345\210\206\345\272\223\350\241\250\345\205\263\350\201\224\345\205\263\347\263\273.md" new file mode 100644 index 000000000..1f48179c5 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-6\350\212\202\357\274\232\346\213\206\345\210\206\345\272\223\350\241\250\345\205\263\350\201\224\345\205\263\347\263\273.md" @@ -0,0 +1,44 @@ +--- +title: 【更】第2-6节:拆分库表关联关系 +pay: https://t.zsxq.com/SisVe +--- + +# 《拼团交易平台系统》第2-6节:拆分库表关联关系 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/MyFI0](https://t.zsxq.com/MyFI0) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +有伙伴问,看视频写项目,能跟着完成,也能运行出结果。但怎样才算是真正的掌握了这个项目的内容呢? + +多年的工作经验,要想掌握一个项目,只听分享、只看视频、只看文档,都是不行的,这些都只能让你了解但不是深入。只要上手写代码,一行行的完成需求逻辑,才能算是真正的掌握了这个项目。 + +所以,趁着项目不大,本节我们设定一个作业。我会给你讲解诉求和需要改动的功能点,你可以按照这样的流程来做编码和调试。完成后,在继续跟进课程学习,比对代码。当然如果你目前还处于小白阶段,也可以直接跟着课程学习。 + +## 一、本章诉求 + +在系统开始之初,简化功能设计,让拼团活动配置表直接耦合商品ID。也就是一个拼团活动,只关联一个商品ID。那么如果现在需要给10个商品,全配置一个相同的拼团活动,就没法配置了。总不能一个个全配置一遍。所以本节要对这块的内容做拆分解耦。 + +
+ +
+ +- group_buy_activity 拼团活动配置表中,融合了渠道和商品ID,属于和活动配置绑定了。 +- sku 商品信息,已经包含了渠道SC值和商品ID。而商品表咱们前面提到过,它是由接入方同步的商品信息,也可以不走这里,直接用 RPC/HTTP 接口查询数据,所以 sku 表不适合绑定互动ID。 +- 那么,就需要一个新的表来关联活动和商品信息配置,表名为 sc_sku_activity 必备字段;SC渠道、活动ID、商品ID。(自己创建库表后可以和课程的库表对比) +- 你可以根据这样的信息来创建你的库表,同时移除 group_buy_activity 表中 SC渠道值和商品ID。 + +## 二、业务流程 + +如图,整个改动流程的核心为让 MarketNode 节点的查询由原来方式改为先查询SC商品活动配置关联表,获得到活动ID,再查询活动信息。这期间如果有效的活动配置信息无,那么则走到 ErrorNode 节点,返回一个指定的错误码。 + +
+ +
+ +- 首先,你可以先通过如图的调用过程,理解本节要完成的编程动作。包括解耦后的新的库表关联关系。 +- 之后,编码的时候异步多线程查询商品关联配置,如果配置的信息为空,或者不存在有效的活动,那么可以返回一个 null。当拿到null 以后,可以做判断进行路由,走到 ErrorNode 节点。「这部分代码一定自己尝试下,多思考。」 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-7\350\212\202\357\274\232\344\272\272\347\276\244\346\240\207\347\255\276\350\212\202\347\202\271\350\277\207\346\273\244.md" "b/docs/md/project/group-buy-market/\347\254\2542-7\350\212\202\357\274\232\344\272\272\347\276\244\346\240\207\347\255\276\350\212\202\347\202\271\350\277\207\346\273\244.md" new file mode 100644 index 000000000..4f4da3c8f --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-7\350\212\202\357\274\232\344\272\272\347\276\244\346\240\207\347\255\276\350\212\202\347\202\271\350\277\207\346\273\244.md" @@ -0,0 +1,33 @@ +--- +title: 【更】第2-7节:人群标签节点过滤 +pay: https://t.zsxq.com/EL4gD +--- + +# 《拼团交易平台系统》第2-7节:人群标签节点过滤 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/E5VKd](https://t.zsxq.com/E5VKd) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +什么样的代码算好代码?做编程开发这么多年,我一直追求将代码简单化,让代码即是文档,看代码即可知道每一块功能的边界。因为一个功能开发出来,基本就要伴随着业务的发展一直持续下去。那么好的代码,就可以更好的维护,尤其是遇到问题的时候,可以快速定位。 + +## 一、本章诉求 + +在整个首页营销试算流程中,需要添加一个新的人群标签🏷节点 TagNode,来处理人群过滤的操作。 + +在本节你会看到目前的模型结构设计是非常容易添加出一个新的流程节点,同时对原有的功能不会有破坏性。这样既可以让我们更好的维护代码,也能方便持续的需求迭代。 + +## 二、业务流程 + +如图,增加 TagNode 节点,调整节点调用关系; + +
+ +
+ +- 首先,添加一个新的 TagNode 节点,调整营销 MarketNode 节点完成业务功能后,流转到新的 TagNode 节点。 +- 之后,在从个 TagNode 节点流转到 EndNode 结束节点。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-8\350\212\202\357\274\232\345\212\250\346\200\201\351\205\215\347\275\256\345\274\200\345\205\263\346\223\215\344\275\234.md" "b/docs/md/project/group-buy-market/\347\254\2542-8\350\212\202\357\274\232\345\212\250\346\200\201\351\205\215\347\275\256\345\274\200\345\205\263\346\223\215\344\275\234.md" new file mode 100644 index 000000000..e8dd4a7ab --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-8\350\212\202\357\274\232\345\212\250\346\200\201\351\205\215\347\275\256\345\274\200\345\205\263\346\223\215\344\275\234.md" @@ -0,0 +1,39 @@ +--- +title: 【更】第2-8节:动态配置开关操作 +pay: https://t.zsxq.com/wp9GE +--- + +# 《拼团交易平台系统》第2-8节:动态配置开关操作 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/yHvNc](https://t.zsxq.com/yHvNc) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在编程能力积累进阶的过程中,核心框架源码(Spring、MyBatis)的掌握是非常必要的过程之一。这些源码的组件学习后是可以帮助自身做业务功能逻辑的实现。 + +有些伙伴可能会觉得就做个业务开发,是不没有必要学习?🤔 其实不是的,对于源码这样的东西,往往是因为它并没有成为你熟悉的知识,所以也不会成为你设计的方案之一。当你熟练掌握后,会很巧妙的结合源码做自己的技术实现方案。 + +## 一、本章诉求 + +如何不停车就给汽车换个轮子? + +这是互联网应用程序中经常干的事情,在程序运行过程中,直接动态变更某些属性配置。这些动态变更的配置包括降级和切量的开关,也包括一些功能程序的白名单用户测试。 + +那么对于配置中心,有 SpringCloud Config + Event Bus,也有 Nacos,还有各个大厂中会基于各类组件做的自研实现。那么本节我们先来做一个基于 Redis 发布/订阅处理动态配置的自研的实现,之后对于 SpringCloud 的动态配置变更已经有案例,小伙伴也可以学习。 + +案例:[https://bugstack.cn/md/road-map/springcloud-bus.html](https://bugstack.cn/md/road-map/springcloud-bus.html) + +## 二、业务流程 + +如图,基于 Redis 实现一套动态配置中心 DCC 服务;Dynamic Config Control + +
+ +
+ +- 注意,本节会涉及到 Spring源码、Java 动态配置的一些编码操作,属于组件类开发是思想。如本节实现的功能也可以被独立出一个工程组件开发后被业务系统引入使用。 +- 方案,动态配置的处理可以使用 Zookeeper 的节点监听,也可以基于 Redis 的发布/订阅。本节咱们使用 Redis 这套方案,当你学习到后面的大营销项目,还会看到 Zookeeper 的实现方案。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2542-9\350\212\202\357\274\232\346\213\274\345\233\242\344\272\244\346\230\223\350\220\245\351\224\200\351\224\201\345\215\225.md" "b/docs/md/project/group-buy-market/\347\254\2542-9\350\212\202\357\274\232\346\213\274\345\233\242\344\272\244\346\230\223\350\220\245\351\224\200\351\224\201\345\215\225.md" new file mode 100644 index 000000000..23bc3e226 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2542-9\350\212\202\357\274\232\346\213\274\345\233\242\344\272\244\346\230\223\350\220\245\351\224\200\351\224\201\345\215\225.md" @@ -0,0 +1,36 @@ +--- +title: 【更】第2-9节:拼团交易营销锁单 +pay: https://t.zsxq.com/csVlH +--- + +# 《拼团交易平台系统》第2-9节:拼团交易营销锁单 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/csVlH](https://t.zsxq.com/csVlH) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在编程开发实现业务逻辑的过程中,头脑中可以有一个乐高积木的思想,考虑分拆逻辑聚合每一块业务。让这些业务具备良好的复用性。 + +尤其是复杂的下单过程,会有很多模块会被使用,所以这部分要想好你在整个下单过程中,会有哪些逻辑应该被聚合,怎么串联上下文。 + +## 一、本章诉求 + +当商城类系统接入拼团时,则需要在下单过程中使用一笔营销优惠。这里的营销优惠可以为;无券平台营销、有券消费营销、拼团折扣营销、积分抵扣营销等。 + +那么,当商城类系统接入使用下单时,则需要到拼团系统锁定一笔优惠,也就是占用一个名额。完事后,商城类系统继续操作支付交易的过程。 + +## 二、业务流程 + +如图,完善原有业务流程。 + +
+ +
+ +- 首先,团购的商品下单。下单过程分为创建流水单、锁定营销优惠(拼团、积分、券)、创建支付订单、唤起收银台支付、用户扫码支付、支付完成核销优惠等。 +- 那么,这里用户以拼团方式下单,创建流水单完成后,需要与拼团系统交互,锁定营销优惠。更新流水单优惠金额和支付金额。接下来就可以创建支付单了(支付单需要最终的支付金额)。 +- 注意,拼团表 group_buy_order 除了有目标量(target_count)、完成量(complete),还要有一个锁单量(lock_count),当锁单量达到目标量后,用户在此组织下,不能在参与拼团。直至这些用户支付完成达成拼团或者锁单超时回退支付营销,空出可参与锁单量,这样其他用户可以继续参与。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2543-1\350\212\202\357\274\232DeepSeek\350\256\276\350\256\241\346\213\274\345\233\242UI.md" "b/docs/md/project/group-buy-market/\347\254\2543-1\350\212\202\357\274\232DeepSeek\350\256\276\350\256\241\346\213\274\345\233\242UI.md" new file mode 100644 index 000000000..6b35c4ec2 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2543-1\350\212\202\357\274\232DeepSeek\350\256\276\350\256\241\346\213\274\345\233\242UI.md" @@ -0,0 +1,36 @@ +--- +title: 【更】第3-1节:DeepSeek设计拼团UI +pay: https://t.zsxq.com/TnhqL +--- + +# 《拼团交易平台系统》第3-1节:DeepSeek设计拼团UI + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/0ef6r](https://t.zsxq.com/0ef6r) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +国外比较擅长做创新,国内比较适合做应用。但好在这次在 OpenAI 的赛道上,横空出世了一个 DeepSeek,与过往的几个 OpenAI 产品相比,这次是可以和 ChatGPT 掰掰手腕了。 + +那 OpenAI 会抢了程序员的饭碗吗?🤔 首先,我认为是不会。就像 OpenAI 也会给你讲医学知识,那你敢自己开刀吗?OpenAI 也能讲法律知识,你能出去给人家当律师吗?甚至,在你没有某方面知识积累的时候,你也没法提出更专业的问题和完善类的资料。 + +其实都不能,OpenAI 的作用是让强的人更强,厉害的人更厉害。本身在哪一方面有积累,那么就能运用这些积累的知识借助 OpenAI 这个伙伴,把它做的更好。 + +## 一、本章诉求 + +结合 DeepSeek 等同类型 OpenAI 产品设计拼团 UI。 + +在我体验过的整个 OpenAI 产品中,以前 ChatGPT 4o 系列模型对代码的需求的理解是不错的,可以给还原出比较好的 UI 样式。不过这次在体验了 DeepSeek 后,它也可以还原出很不错的 UI 样式。 + +>本节要做一个简单对接的流程设计,关于商品下单支付这部分会模拟,暂时不走商城类系统。 + +## 二、UI 效果展示 + +按照本节的实现,最终你会得到一个这样的效果。 + +
+ +
\ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2543-2\350\212\202\357\274\232DeepSeek\345\244\204\347\220\206UI\344\270\216\346\216\245\345\217\243\345\257\271\346\216\245.md" "b/docs/md/project/group-buy-market/\347\254\2543-2\350\212\202\357\274\232DeepSeek\345\244\204\347\220\206UI\344\270\216\346\216\245\345\217\243\345\257\271\346\216\245.md" new file mode 100644 index 000000000..2180bc079 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2543-2\350\212\202\357\274\232DeepSeek\345\244\204\347\220\206UI\344\270\216\346\216\245\345\217\243\345\257\271\346\216\245.md" @@ -0,0 +1,35 @@ +--- +title: 【更】第3-2节:DeepSeek处理UI与接口对接 +pay: https://t.zsxq.com/LtzHY +--- + +# 《拼团交易平台系统》第3-2节:DeepSeek处理UI与接口对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/qKjOi](https://t.zsxq.com/qKjOi) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +OpenAI 适合什么场景的编程?我觉得前端算一个,为什么呢? + +前端相对于后端,没有数据库、缓存、RPC、MQ、任务调度等等一系列的复杂组件运用,而更多的工作都是在既定路线上的重复处理。所以,它的场景就更适合 OpenAI 一次的加载和理解,并给出正确性更高的编码实现,从而减轻开发人员的工作量。 + +在有这类的 OpenAI 产品后,我在开发前端代码上,确实更加得心应手了。 + +## 一、本章诉求 + +结合 DeepSeek 等同类型 OpenAI 产品,辅助完成 UI 与服务端接口的对接。 + +前端的 UI 已经明确,服务端的接口已经开发完成,他们中间没有什么额外的复杂逻辑了。只要告诉 OpenAI 调用接口,并明确出 UI 中位置的内容展示是来自于接口里的位置,那么就可以完成自动化的对接。 + +## 二、效果展示 + +
+ +
+ +- UI 与 服务端接口完整对接后,可以看到数据接口数据的使用,并渲染到前端页面。 +- 在没有用户参与拼团的时候,首次展示引导用户开启拼团。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2543-3\350\212\202\357\274\232\345\260\217\345\225\206\345\237\216\345\257\271\346\216\245\350\220\245\351\224\200\351\224\201\345\215\225.md" "b/docs/md/project/group-buy-market/\347\254\2543-3\350\212\202\357\274\232\345\260\217\345\225\206\345\237\216\345\257\271\346\216\245\350\220\245\351\224\200\351\224\201\345\215\225.md" new file mode 100644 index 000000000..9d8c71104 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2543-3\350\212\202\357\274\232\345\260\217\345\225\206\345\237\216\345\257\271\346\216\245\350\220\245\351\224\200\351\224\201\345\215\225.md" @@ -0,0 +1,41 @@ +--- +title: 【更】第3-3节:小商城对接营销锁单 +pay: https://t.zsxq.com/x2sHW +--- + +# 《拼团交易平台系统》第3-3节:小商城对接营销锁单 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/1QJTP](https://t.zsxq.com/1QJTP) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +从本章开始,我们会进入到2套系统的对接,陆续完成星球「码农会锁」里的`小型支付商城`与`拼团`在业务流程层面完成接口对接。 + +通过这样的学习,你可以积累到关于微服务间是如何通信和流程衔接的。在这公司里也是非常常见的设计和开发方式。 + +那么,本节我们先来完成商品下单过程中,营销锁单的接口对接。小型支付商城选择的是 `s-pay-mall-ddd` 工程,单独拿出一套命名为 `s-pay-mall-ddd-market` 方便大家学习。 + +**前置学习**;在开始这部分对接前,需要对星球的《小型支付商城系统》有所学习。课程地址:[https://t.zsxq.com/3X9GA](https://t.zsxq.com/3X9GA) + +## 二、业务流程 + +如图,两套微服务对接节点; + +
+ +
+ +- 左侧,小型支付商城,用户下单过程。增加一个营销锁单,从营销锁单中获取拼团营销拿到的优惠价格。之后小型商城继续往下走,创建支付订单。 +- 右侧,拼团交易平台,提供营销锁单流程,锁单目标、优惠试算,规则过滤,最终落库和返回结果(订单ID、原始价格、折扣金额、支付金额、订单状态)。 + +- 功能改造点; + + - 【拼团系统】lockMarketPayOrder 营销拼团锁单,增加返回字段值。原来只返回了优惠金额,为了方便使用方不需要自己在做计算,可以直接返回,原始价格、折扣金额、支付金额。这样的话,拼团就需要在 group_buy_order_list 表增加 pay_price,这样就更方便的拿到这个值了。 + - 【小型商城】基于 Retrofit2,在 infrastructure 基础设施层,封装对拼团接口的对接。`Call> lockMarketPayOrder(@Body LockMarketPayOrderRequestDTO requestDTO);` 并在 app 启动层的配置里,修改 `Retrofit2Config` 提供 `groupBuyMarketService` 服务。 + - 【小型商城】对 AliPayController#createPayOrder 创建订单服务的接口入参类,添加拼团队伍ID、活动ID、营销标识。如果前端前端没有拿到拼团活动信息则传递无营销。 + - 【小型商城】对 AbstractOrderService#createOrder 创建订单的过程中,通过 port 端口,添加 `port.lockMarketPayOrder(userId, teamId, activityId, productId, orderId);` 锁单。 + - 【小型商城】对于锁单返回的支付金额,为拼团交易订单金额。最终营销标识、优惠金额、支付金额,都会更新到 pay_order 表中,这个表要增加这样3个字段。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2543-4\350\212\202\357\274\232\345\260\217\345\225\206\345\237\216\345\257\271\346\216\245\350\220\245\351\224\200\347\273\223\347\256\227.md" "b/docs/md/project/group-buy-market/\347\254\2543-4\350\212\202\357\274\232\345\260\217\345\225\206\345\237\216\345\257\271\346\216\245\350\220\245\351\224\200\347\273\223\347\256\227.md" new file mode 100644 index 000000000..4ce6ccb7a --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2543-4\350\212\202\357\274\232\345\260\217\345\225\206\345\237\216\345\257\271\346\216\245\350\220\245\351\224\200\347\273\223\347\256\227.md" @@ -0,0 +1,37 @@ +--- +title: 【更】第3-4节:小商城对接营销结算 +pay: https://t.zsxq.com/rBemT +--- + +# 《拼团交易平台系统》第3-4节:小商城对接营销结算 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/0V186](https://t.zsxq.com/0V186) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +处理小型支付商城在用户完成支付后,调用拼团营销组队结算,并接收组队完成的回调消息执行后续交易结算发货。—— `这里一个是拼团结算,一个是交易结算` + +也就是说每一笔用户支付完成,拼团组队也就有一人推进了组队的进度,直至组队完成,即可回调交易系统进行通知组队完成,进行后续发货流程。 + +## 二、业务流程 + +如图,拼团交易结算流程; + +
+ +
+ +- 上半部分,是拼团交易过程,进行营销锁单。是咱们上一节实现的。 +- 下办部分,是拼团结算过程,进行营销结算。本节我们要做的新流程。 +- 功能改造点; + + - 基础设施层 infrastructure gateway 下的 IGroupBuyMarketService 增加营销拼团结算方法(顺便加入拼团中DTO对象)。 + - 修改 OrderService#changeOrderPaySuccess 方法,判断订单是否有营销,无营销的继续走老流程。有营销则通过 port 适配调用基础设施层拼团的结算方法。 + - 在 trigger 层,AliPayController 增加 groupBuyNotify 回调方法。并把地址配置到 application-dev.yml 中。这样拼团系统在接收小型商城的用户订单组队完成后,就可以接收回调方法了。 + - domain order 领域服务,增加 changeOrderMarketSettlement 修改订单状态方法,增加一个拼团结算的状态。之后发送MQ消息,这个时候就每一笔分别结算发货了。 + - domain 领域服务,增加 goods 商品领域,模拟发货和修改订单状态。这个方法交给,OrderPaySuccessListener 接收到结算消息后进行调用。 + - 注意;为了方便拼团测试,我们可以把拼团活动的组队人数修改为1个。 diff --git "a/docs/md/project/group-buy-market/\347\254\2543-5\350\212\202\357\274\232\345\260\217\345\225\206\345\237\216UI\344\270\216\346\216\245\345\217\243\345\257\271\346\216\245.md" "b/docs/md/project/group-buy-market/\347\254\2543-5\350\212\202\357\274\232\345\260\217\345\225\206\345\237\216UI\344\270\216\346\216\245\345\217\243\345\257\271\346\216\245.md" new file mode 100644 index 000000000..bab684655 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2543-5\350\212\202\357\274\232\345\260\217\345\225\206\345\237\216UI\344\270\216\346\216\245\345\217\243\345\257\271\346\216\245.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第3-5节:小商城UI与接口对接 +pay: https://t.zsxq.com/Jh698 +--- + +# 《拼团交易平台系统》第3-5节:小商城UI与接口对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/jPjFC](https://t.zsxq.com/jPjFC) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +基于 DeepSeek/OpenAI 等AI产品,把UI代码、接口代码,增加逻辑描述,一起喂给AI,来处理小型支付商城UI与接口的对接。 + +在前面章节我们开发了一款静态页面UI,直接和拼团后端对接。这回走正式的逻辑,下单将走到小型支付商城,由商城后端处理锁单和结算。之后和前端页面UI对接。 + +这是一次微服务的前后端全流程衔接,支付商城和拼团营销,就是2个微服务系统。学到这块,你即可以实践的方式理解微服务,而哪些RPC、MQ、xxl-job、分库分表等,则是为每个微服务提供分布式负载能力的技术。这些东西将在后续章节展开。 + +## 二、效果展示 + +
+ +
+ +- UI 与小型支付商城对接后,可以扫码登录,确认支付时走支付宝沙箱登录。 +- 这样就是一个完整的,商品营销下单支付,再到结算的流程了。 + diff --git "a/docs/md/project/group-buy-market/\347\254\2543-6\350\212\202\357\274\232\351\200\232\350\277\207\346\265\217\350\247\210\345\231\250\346\214\207\347\272\271\350\216\267\345\217\226\347\231\273\345\275\225ticket\346\227\240\347\227\225\347\231\273\345\275\225.md" "b/docs/md/project/group-buy-market/\347\254\2543-6\350\212\202\357\274\232\351\200\232\350\277\207\346\265\217\350\247\210\345\231\250\346\214\207\347\272\271\350\216\267\345\217\226\347\231\273\345\275\225ticket\346\227\240\347\227\225\347\231\273\345\275\225.md" new file mode 100644 index 000000000..f0ae657b7 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2543-6\350\212\202\357\274\232\351\200\232\350\277\207\346\265\217\350\247\210\345\231\250\346\214\207\347\272\271\350\216\267\345\217\226\347\231\273\345\275\225ticket\346\227\240\347\227\225\347\231\273\345\275\225.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第3-6节:通过浏览器指纹获取登录ticket + 无痕登录 +pay: https://t.zsxq.com/cGRlq +--- + +# 《拼团交易平台系统》第3-6节:通过浏览器指纹获取登录ticket + 无痕登录 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/8Oa5K](https://t.zsxq.com/8Oa5K) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +增强登录安全性,使用浏览器指纹,获取唯一 Ticket 作为登录二维码使用。 + +本章节属于知识点的扩展,即使没实现,也不影响整体功能。 + +## 二、业务流程 + +如图,增加浏览器指纹标识; + +
+ +
+ +- 原有的微信扫码登录拿到的是同一个 Tikcet,这样是满足测试,但对于上线给其他人一起使用是不太安全的。所以我们可以通过浏览器指纹的方式进行处理。 +- 浏览器指纹生成唯一标识,通过唯一标识获取ticket,并在轮训验证中使用浏览器指纹加验。确保浏览器指纹缓存的 ticket 和登录后的 ticket 保持一致。 + diff --git "a/docs/md/project/group-buy-market/\347\254\2543-7\350\212\202\357\274\232\347\224\250\346\210\267\350\256\242\345\215\225\345\210\227\350\241\250\345\222\214\351\200\200\345\215\225UI.md" "b/docs/md/project/group-buy-market/\347\254\2543-7\350\212\202\357\274\232\347\224\250\346\210\267\350\256\242\345\215\225\345\210\227\350\241\250\345\222\214\351\200\200\345\215\225UI.md" new file mode 100644 index 000000000..9c02ba5e7 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2543-7\350\212\202\357\274\232\347\224\250\346\210\267\350\256\242\345\215\225\345\210\227\350\241\250\345\222\214\351\200\200\345\215\225UI.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第3-7节:用户订单列表和退单UI +pay: https://t.zsxq.com/8D4SE +--- + +# 《拼团交易平台系统》第3-7节:用户订单列表和退单UI + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/Yng4V](https://t.zsxq.com/Yng4V) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +给现有的商品拼团下单页面,增加一个`我的订单列表页`,这样用户可以查询订单以及申请退单操作。 + +本节的操作主要是配和 UI 页面,增加对应的 CRUD 接口,为了后续对接拼团退单使用。难度不大。关于支付(支付宝沙箱)退单,已经有单测案例,可以参考。后续会使用。 + +## 二、业务流程 + +如图,查询订单列表和退单的流程; + +
+ +
+ +- 订单查询,查询个人订单记录。这样用户就可以知道下了多少订单了,类似于你京东、淘宝、拼多多查询的个人订单记录。 +- 退单流程,在每个订单上,可以点击退单操作。这部分主要为了后续对接拼团进行退单处理。 +- 整个本节的内容其实主要是 CURD 来配和 UI 的,难度不大。对于 UI 界面,可以使用 ai 工具进行设计。前端部分就是为了配和整个完整性的,面试中主要考察后端。所以,如果不太会写前端代码也不要太担心。 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2543-8\350\212\202\357\274\232\351\200\200\345\215\225\351\200\200\346\254\276\346\234\215\345\212\241\345\257\271\346\216\245.md" "b/docs/md/project/group-buy-market/\347\254\2543-8\350\212\202\357\274\232\351\200\200\345\215\225\351\200\200\346\254\276\346\234\215\345\212\241\345\257\271\346\216\245.md" new file mode 100644 index 000000000..9384cdda6 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2543-8\350\212\202\357\274\232\351\200\200\345\215\225\351\200\200\346\254\276\346\234\215\345\212\241\345\257\271\346\216\245.md" @@ -0,0 +1,32 @@ +--- +title: 【更】第3-8节:退单退款服务对接 +pay: https://t.zsxq.com/u7vu9 +--- + +# 《拼团交易平台系统》第3-8节:退单退款服务对接 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/ysPNN](https://t.zsxq.com/ysPNN) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +处理小型支付商城与拼团营销的接口和MQ消息对接,完成退单退款的业务实现。 + +整个过程会包括从小型支付发起退单申请,调用拼团解除组队锁单量/完成量。由拼团发送的MQ,被小型支付监听,发起退款操作。这部分有接口,也有 MQ 的异步处理,是一个综合的链路调度技术运用。这类设计方案,在互联网公司里的业务场景是非常常见的。 + +## 二、页面流程(UI) + +
+ +
+ +
+ +
+ +
+ +
\ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2544-1\350\212\202\357\274\232\347\254\2541\351\230\266\346\256\265\351\203\250\347\275\262\344\272\221\347\216\257\345\242\203.md" "b/docs/md/project/group-buy-market/\347\254\2544-1\350\212\202\357\274\232\347\254\2541\351\230\266\346\256\265\351\203\250\347\275\262\344\272\221\347\216\257\345\242\203.md" new file mode 100644 index 000000000..3f396bb50 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2544-1\350\212\202\357\274\232\347\254\2541\351\230\266\346\256\265\351\203\250\347\275\262\344\272\221\347\216\257\345\242\203.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第4-1节:第1阶段部署云环境 +pay: https://t.zsxq.com/cgBDM +--- + +# 《拼团交易平台系统》第4-1节:第1阶段部署云环境 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/izUPl](https://t.zsxq.com/izUPl) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +将项目上线部署到云服务器环境,提供公网IP地址,可以访问体验。 + +现阶段的很多面试,面试官在面试过程中都比较重视考察求职者对于项目学习的完整性,从设计、开发、测试、迭代,再到部署上线,一整套的内容,全都了解清楚是非常好的。 + +## 二、部署过程 + +如图,为本次的部署过程; + +
+ +
+ +- 首先,购买云服务器,之后搭建云环境。[http://618.gaga.plus/](http://618.gaga.plus/) 2c2g 很便宜,不过更推荐2c4g也不贵,避免以后部署其他的不够用了。 +- 之后,检出项目、构建镜像。以及部署操作。 +- 最后,要记得,开放使用的端口。这个过程会使用到 `9000`、`8091`、`8899` \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2544-2\350\212\202\357\274\232\347\254\2542\351\230\266\346\256\265\351\203\250\347\275\262\344\272\221\347\216\257\345\242\203.md" "b/docs/md/project/group-buy-market/\347\254\2544-2\350\212\202\357\274\232\347\254\2542\351\230\266\346\256\265\351\203\250\347\275\262\344\272\221\347\216\257\345\242\203.md" new file mode 100644 index 000000000..410b5cfe5 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2544-2\350\212\202\357\274\232\347\254\2542\351\230\266\346\256\265\351\203\250\347\275\262\344\272\221\347\216\257\345\242\203.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第4-2节:第2阶段部署云环境 +pay: https://t.zsxq.com/jzoIz +--- + +# 《拼团交易平台系统》第4-2节:第2阶段部署云环境 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/Mjr20](https://t.zsxq.com/Mjr20) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +将支付商城 + 拼团,Nginx 负载部署到云服务器,使用穿透映射提供的域名,进行访问体验。 + +这一阶段部署,就有微服务的体现了。要注意,微服务是微服务,分布式是分布式。不要混淆概念。两个技术方案可以一起使用,也可以单独使用。 + +## 二、部署过程 + +如图,为本次的部署过程; + +
+ +
+ +- 首先,购买云服务器,之后搭建云环境。[http://618.gaga.plus/](http://618.gaga.plus/) 需要2c4g部署,本节需要部署2个项目,并进行负载方式多实例部署。 +- 之后,检出项目、构建镜像。以及部署操作。 +- 最后,要记得,开放使用的端口。这个过程会使用到 `9000`、`8091`、`8092`、`8899`、`9091`等,需要开放的端口 \ No newline at end of file diff --git "a/docs/md/project/group-buy-market/\347\254\2544-3\350\212\202\357\274\232\347\254\2543\351\230\266\346\256\265\351\203\250\347\275\262\344\272\221\347\216\257\345\242\203.md" "b/docs/md/project/group-buy-market/\347\254\2544-3\350\212\202\357\274\232\347\254\2543\351\230\266\346\256\265\351\203\250\347\275\262\344\272\221\347\216\257\345\242\203.md" new file mode 100644 index 000000000..44b193b32 --- /dev/null +++ "b/docs/md/project/group-buy-market/\347\254\2544-3\350\212\202\357\274\232\347\254\2543\351\230\266\346\256\265\351\203\250\347\275\262\344\272\221\347\216\257\345\242\203.md" @@ -0,0 +1,30 @@ +--- +title: 【更】第4-3节:第3阶段部署云环境 +pay: https://t.zsxq.com/cycJ1 +--- + +# 《拼团交易平台系统》第4-3节:第3阶段部署云环境 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/kcqaK](https://t.zsxq.com/kcqaK) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +将支付商城 + 拼团系统,以 Nginx 负载的方式,在云服务器通过 Docker 方式进行部署。 + +部署包括;环境(mysql、redis、rabbitmq)、监控(prometheus、grafana)、分布式日志(elk)、应用(小型支付、拼团),natapp(内网穿透)、微信公众号、支付宝沙箱 + +## 二、部署过程 + +如图,为本次的部署过程; + +
+ +
+ +- 首先,购买云服务器,之后搭建云环境。[http://618.gaga.plus/](http://618.gaga.plus/) 最低2c4g部署(仅部署项目),2c8g全量部署,本节需要部署2个项目,并进行负载方式多实例部署。 +- 之后,检出项目、构建镜像。以及部署操作。 +- 最后,要记得,开放使用的端口。这个过程会使用到 `9000`、`8091`、`8092`、`8899`、`9091`、`4000`、`15672`等,需要开放的端口。 \ No newline at end of file diff --git "a/docs/md/project/im/2020-03-04-\343\200\212Netty+JavaFx\345\256\236\346\210\230\357\274\232\344\273\277\346\241\214\351\235\242\347\211\210\345\276\256\344\277\241\350\201\212\345\244\251\343\200\213.md" "b/docs/md/project/im/2020-03-04-\343\200\212Netty+JavaFx\345\256\236\346\210\230\357\274\232\344\273\277\346\241\214\351\235\242\347\211\210\345\276\256\344\277\241\350\201\212\345\244\251\343\200\213.md" index ebf8a3bc3..933295555 100644 --- "a/docs/md/project/im/2020-03-04-\343\200\212Netty+JavaFx\345\256\236\346\210\230\357\274\232\344\273\277\346\241\214\351\235\242\347\211\210\345\276\256\344\277\241\350\201\212\345\244\251\343\200\213.md" +++ "b/docs/md/project/im/2020-03-04-\343\200\212Netty+JavaFx\345\256\236\346\210\230\357\274\232\344\273\277\346\241\214\351\235\242\347\211\210\345\276\256\344\277\241\350\201\212\345\244\251\343\200\213.md" @@ -70,24 +70,10 @@ excerpt: 本专栏是作者小傅哥使用JavaFx、Netty4.x、SpringBoot、Mysql >专栏共有25篇文章,分别从UI、架构到功能实现逐步讲解,非常适合新人学习提升编码能力和架构思想。 -对于此项目,你可以单独购买,也可以加入小傅哥的知识星球,在星球中除了此项目,还会提供其他项目学习。 +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) -### 1. 加入星球 +## 六、优秀作业 -星球地址:[https://t.zsxq.com/Ja27ujq](https://t.zsxq.com/Ja27ujq) - -
- -
公众号【bugstack虫洞栈】 回复【星球】可获得优惠券
-
-
- -### 2. 单独购买 - -学习链接:[IM 小册](https://blog.csdn.net/generalfu/category_10400631.html) - -
- -
公众号【bugstack虫洞栈】 回复【星球】可获得优惠券
-
-
\ No newline at end of file +- [Java仿微信对接小傅哥ChatGPT-SDK-Java实现智能机器人 @俗人](https://t.zsxq.com/10B7yE8xw) +- [借助 JDK1.8 中的 javapackager.exe 和 IDEA 生成 IM 可运行文件 @俗人](https://t.zsxq.com/10IkHjbpm) +- [JavaFX打包exe @俗人](https://t.zsxq.com/11ybJvpPf) \ No newline at end of file diff --git a/docs/md/project/local-task-message/local-task-message.md b/docs/md/project/local-task-message/local-task-message.md new file mode 100644 index 000000000..5153f1068 --- /dev/null +++ b/docs/md/project/local-task-message/local-task-message.md @@ -0,0 +1,99 @@ +--- +title: 本地任务消息组件 +lock: no +--- + +# 《本地任务消息组件》- 为事务和消息推送(HTTP、MQ),提供最终一致性解决方案 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/WCWAb](https://t.zsxq.com/WCWAb) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**你说气人不**,每每公司`晋升提报`和`加薪`的时候,总是那些手里有俏活的👬🏻兄弟。业务项目虽然是根基,但大家都做也就拉不开差距,而技术类组件、通用服务、功能平台,倒不是所有人都能搞的,这类结合业务场景的提取共性问题,凝练成通用解决方案的项目,可以开发一个就解决了全大部门的问题,所以做这类项目很亮眼! + +
+ +
+ +**这类项目也有门槛!** + +看着好像做个技术组件也没啥,但实际真想做的时候,你会发现你不知道什么服务可以被抽取为通用组件,不具备这样的经验和眼界。同时即使知道做啥了,也不知道如何结合像 Spring、MyBatis 源码能力,开发出一个通用的组件来,能让其他业务项目引入使用。 + +有人讲这东西不就是重复造轮子吗?🤔 还真不是,重复造轮子,指的市面上有的,或者公司里有个基础平台组有的。但这类的轮子往往不是深度结合业务的,而是那种无业务属性的功能逻辑的轮子,比如;rpc、xxl-job、mq 等。但一个业务组,他所需要的是解决通用业务场景问题的轮子,但这类东西又不属于基础平台研发组,所以往往都是业务组自己来解决这类场景问题。 + +所以,让自己具备开发组件的能力,是非常非常重要的,这即是抽象业务也是驾驭源码的能力体现。 + +小傅哥已经为大家提供了非常多的通用组件项目,如;`扳手工程(DCC动态配置中心、设计模式、动态限流、任务调度)`、`BCP 透视业务监控`、`动态线程池`、`支付SDK组件`、`SpringBoot Starter (16个合集)`、`IntelliJ IDEA Plugin 插件开发能力(具备这个的,开发了不少AI类组件)`。 + +这次小傅哥给大家再加一个新的组件《本地任务消息组件》,该组件解决业务场景远程调用HTTP或推送MQ消息,最终一致的问题。 + +## 一、能学到啥 + +- 【架构】掌握 DDD 分层与端口-适配器模式,清晰划分 domain/infrastructure/trigger/config 模块,提升可维护性与扩展性。 +- 【后端】学习注解+AOP 方式受理任务消息,结合事务边界进行统一处理,理解 `@LocalTaskMessage` 与切面配合的落地实践。 +- 【后端】掌握本地消息表设计与分片扫描策略(按门牌号 houseNumber 分片),实现高效拉取与顺序处理,提升系统可靠性。 +- 【后端】熟悉 Spring Event 事件驱动与异步消费,使用 `ApplicationEvent`+`@EventListener`+`@Async` 实现解耦通知链路。 +- 【后端】实践策略模式实现可插拔通知能力,支持 HTTP 与 RabbitMQ 两种通知通道,并在成功/失败时更新任务状态。 +- 【后端】熟练使用 OkHttp3 与 Retrofit2 统一封装 HTTP 网关,掌握动态 URL、Header、Body 的组合与异常处理。 +- 【后端】了解 RabbitMQ 事件发布的可选依赖注入方式,避免未配置 MQ 时的强依赖导致应用启动失败。 +- 【配置】掌握 `@ConfigurationProperties` 驱动的多任务组动态调度配置,支持 cron 与 fixedDelay 两种触发方式,并可配置批次大小 limit。 +- 【运维】学习 `ThreadPoolTaskScheduler` 的线程池化调度管理,合理设置线程名与池大小,提升任务调度的可观测性与稳定性。 +- 【数据】掌握原生 JDBC 访问与 DAO 封装,完成插入、状态更新、分片条件查询、最小游标查询等落地实现。 +- 【测试】通过示例命令对象 `TaskMessageEntityCommand` 的构建与调用,理解入参约定、枚举策略与配置对象的协作。 +- 【实践】提升异常、日志与枚举的综合使用能力,建立稳定的错误处理。 + +## 二、项目介绍 + +**《本地消息任务组件》** 项目,是以自定义注解 AOP 切入或编程的方式,动态化完成数据库表事务和消息推送(HTTP、MQ),达到最终一致性的目的。 + +>在没有这样的组件的时候,为了完成业务流程的同时,在发送一个MQ消息或则远程调用 HTTP 操作,都需要自己写一个本地消息表,之后还要维护消息表的扫描补偿。 + +操作方式如图; + +
+ +
+ +- 用户可以选择通过注解或者直接调用组件服务的方式进行使用。也就不用业务项目工程再维护关于本地消息表的写入和 MQ 或者 HTTP 的处理和补偿了。 +- 注解方式,会自动获取入参,入参需要为 TaskMessageEntityCommand 对象,它可以是某个入参的对象。之后配置 req.command 也可以获取。 + +## 三、产品方案 + +### 1. 产品概述 + +本地任务消息组件基于 Spring 框架能力,设计并实现了通用功能内核,便于集成到各类业务系统中。在业务系统中,组件支持在事务内完成数据写库的同时,写入一条本地消息记录(需在业务系统数据库中创建符合组件规范的本地消息表)。写入完成后,组件同步推送 Spring 事件,触发事务外的异步处理,如 MQ 消息发送或 HTTP 回调。即使异步处理失败,组件内置的本地消息表定时任务(支持自定义配置“门牌号”多任务并行扫描,提升扫描吞吐量)会持续检测并重试通知,确保消息最终一致性和业务流程的可靠执行。 + +### 2. 技术架构 + +
+ +
+ +- 首先,Local Task Message 任务消息组件,是以解决通用业务场景中的,本地数据库事务和外部MQ/HTTP调用一致性问题而设计实现的。让上游业务系统,不需要在每个流程中,都要做大量的重复编码。而是通过注解或者直接调用组件内核服务即可完成消息的通知操作。 +- 之后,Local Task Message 任务消息组件,并不是一个单纯的工具性功能,而是剩余一个领域服务内核,它具备完整的领域功能,具备操作数据库表的能力,以及接收 Spring Event 事件,对接 MQ、HTTP 完成和外部的交互处理。 +- 然后,上游系统在使用这套服务时,只需要配置好对应的本地消息表(一个事务下,连的同一个库),以及引入组件和完成yml配置,即可直接使用。 + +### 3. 功能流程 + +
+ +
+ +- 引入本地消息组件后,以用户开发dao入库操作为开始,可以通过注解或者调用组件服务 `ILocalTaskMessageHandleService` 驱动同一个事务下,进行消息推送。 +- 切面的方式,会更为优雅简洁,不需要用户自己在维护调用关系。 +- 整个操作会由组件自行处理写库操作,基于 Spring Event 的监听和通知,触发消息推送。完成 http、mq 的调用逻辑。同时还有基于门牌号扫描的逻辑,增强吞吐量。 + +## 四、课程目录 + +现课程已全部录制完成,接下来会日更📅项目💐; + +
+ +
+ +- 6节课程,全程视频手把手,带着你分析需求,编写代码。快速完成一个组件项目。 +- 课程代码,以互联网公司方式逐步拉分支开发,你可以学习到正规的编码操作。 diff --git "a/docs/md/project/local-task-message/\347\254\2541\350\212\202\357\274\232\347\273\204\344\273\266\351\234\200\346\261\202\345\210\206\346\236\220.md" "b/docs/md/project/local-task-message/\347\254\2541\350\212\202\357\274\232\347\273\204\344\273\266\351\234\200\346\261\202\345\210\206\346\236\220.md" new file mode 100644 index 000000000..9982faaf3 --- /dev/null +++ "b/docs/md/project/local-task-message/\347\254\2541\350\212\202\357\274\232\347\273\204\344\273\266\351\234\200\346\261\202\345\210\206\346\236\220.md" @@ -0,0 +1,31 @@ +--- +title: 第1节:组件需求分析 +pay: https://t.zsxq.com/Tscyn +--- + +# 《本地任务消息组件》- 第1节:组件需求分析 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
视频:[https://t.zsxq.com/7Dsu2](https://t.zsxq.com/7Dsu2) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +从今天开始,我们将进入一个新的组件项目学习。该组件聚焦于业务功能开发中的通用技术场景,提炼出一个技术解决方案,专门解决在完成数据库事务的同时,还需对外发送消息(MQ)或发起HTTP调用的最终一致性问题。 + +由于MQ消息发送和HTTP调用无法与数据库写操作在同一事务中完成,导致一致性难以保障。若没有统一的组件支持,各业务系统需各自实现这部分逻辑,造成重复开发且难以维护。为此,我们提取并开发了该技术组件,统一解决这一共性难题,提升系统的可靠性和开发效率。 + +## 二、功能案例 + +使用方通过引入本地消息组件,使用服务; + +
+ +
+ +- 用户可以选择通过注解或者直接调用组件服务的方式进行使用。 +- 注解方式,会自动获取入参,入参需要为 TaskMessageEntityCommand 对象,它可以是某个入参的对象。之后配置 req.command 也可以获取。 + +> 这个组件,解决的就是所有需要在完成业务流程时候,还要发送 MQ 或者 http 外部调用的场景。并提供本地消息表,确保最终一致性。 \ No newline at end of file diff --git "a/docs/md/project/local-task-message/\347\254\2542\350\212\202\357\274\232SpringEvent\344\272\213\344\273\266\346\266\210\346\201\257.md" "b/docs/md/project/local-task-message/\347\254\2542\350\212\202\357\274\232SpringEvent\344\272\213\344\273\266\346\266\210\346\201\257.md" new file mode 100644 index 000000000..d394c6592 --- /dev/null +++ "b/docs/md/project/local-task-message/\347\254\2542\350\212\202\357\274\232SpringEvent\344\272\213\344\273\266\346\266\210\346\201\257.md" @@ -0,0 +1,28 @@ +--- +title: 第2节:SpringEvent事件消息 +pay: https://t.zsxq.com/u9vRf +--- + +# 《本地任务消息组件》- 第2节:SpringEvent事件消息 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/ySbTf](https://t.zsxq.com/ySbTf) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +构建本地任务消息组件系统工程框架和对应的测试工程服务,并使用 Spring Event 事件消息,进行行为触达的通知和监听,用于后续处理外部 HTTP、MQ 的调用操作。 + +## 二、功能流程 + +如图,是本节关于 Spring Event 事件消息部分的流程。本节以这样一个简单功能诉求,引导工程创建。 + +
+ +
+ +- 首先,搭建一套基础的消息组件工程框架,提供受理任务消息的请求,进行 Spring Event 事件的发布和监听。让大家初步了解组件类框架的实现和简单案例的验证。 +- 之后,把本地消息组件构建成一个 jar,让测试工程通过 pom 的方式进行引入使用。 +- 注意,本组件是一个核心内核服务,会陆续的完善全部功能,使其具备;切面拦截、数据库操作、Spring Event 监听,Job 任务扫描等。所以类似的场景,以DDD工程架构,构建内核更为合适。内核有点类似于,一个业务项目中,从上到下一整套流程,被单独提取出来做成一个独立的小项目,之后引入到上游使用方的系统,就可以运行。扩展资料:[DDD 工程模型](https://bugstack.cn/md/road-map/ddd-guide-03.html) \ No newline at end of file diff --git "a/docs/md/project/local-task-message/\347\254\2543\350\212\202\357\274\232\344\273\273\345\212\241\350\241\250\350\256\276\350\256\241\345\222\214\346\225\260\346\215\256\345\206\231\345\205\245.md" "b/docs/md/project/local-task-message/\347\254\2543\350\212\202\357\274\232\344\273\273\345\212\241\350\241\250\350\256\276\350\256\241\345\222\214\346\225\260\346\215\256\345\206\231\345\205\245.md" new file mode 100644 index 000000000..10d53cd9d --- /dev/null +++ "b/docs/md/project/local-task-message/\347\254\2543\350\212\202\357\274\232\344\273\273\345\212\241\350\241\250\350\256\276\350\256\241\345\222\214\346\225\260\346\215\256\345\206\231\345\205\245.md" @@ -0,0 +1,28 @@ +--- +title: 第3节:任务表设计和数据写入 +pay: https://t.zsxq.com/CWzsg +--- + +# 《本地任务消息组件》- 第3节:任务表设计和数据写入 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/Pfekb](https://t.zsxq.com/Pfekb) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +设计通用的本地消息任务表和数据的插入处理。此表由引入技术组件的上游系统自行在数据库中配置。之后在调用组件的时候,就可以以同一个数据源对库表进行操作。 + +## 二、功能流程 + +如图,是本节关于写库操作的功能流程。 + +
+ +
+ +- 首先,为了可以在组件中完成数据库表的操作,则需要引入 DataSource 数据源,以便于在处理数据库操作的时候,可以以同一个数据库连接进行操作。 +- 之后,这里我们会直接使用 JDBC 的方式操作数据库,不会单独在引入 MyBatis 框架。这样做的目的是为了上游系统使用组件的时候,避免一些版本问题。最原始的方法,兼容性也是最好的。 +- 另外,本节还需要单独设计一下任务消息表。 \ No newline at end of file diff --git "a/docs/md/project/local-task-message/\347\254\2544\350\212\202\357\274\232\351\200\232\347\237\245\347\255\226\347\225\245\345\244\204\347\220\206.md" "b/docs/md/project/local-task-message/\347\254\2544\350\212\202\357\274\232\351\200\232\347\237\245\347\255\226\347\225\245\345\244\204\347\220\206.md" new file mode 100644 index 000000000..da612f4e3 --- /dev/null +++ "b/docs/md/project/local-task-message/\347\254\2544\350\212\202\357\274\232\351\200\232\347\237\245\347\255\226\347\225\245\345\244\204\347\220\206.md" @@ -0,0 +1,29 @@ +--- +title: 第4节:通知策略处理(HTTP&MQ) +pay: https://t.zsxq.com/AjdgW +--- + +# 《本地任务消息组件》- 第4节:通知策略处理(HTTP&MQ) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/9FgAG](https://t.zsxq.com/9FgAG) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +增加 HTTP、MQ(RabbitMQ)服务,在接收 Spring Event 监听后,以通知行为策略的方式,完成 HTTP 远程调用和 MQ 消息推送。 + +## 二、功能流程 + +如图,是本节关于事件消息回调通知的处理流程(暂时先不处理关于数据库的更新流程); + +
+ +
+ +- 首先,事件消息的监听在 trigger 层的监听,之后调用领域层的通知服务方法。这里需要新增加一个领域方法。 +- 之后,领域服务方法,要以 notifyType 不同类型进行通知操作。如;http、mq,这部分你将来想扩展其他的,就在这里添加。 +- 最后,http 和 mq,都会进入基础设施层完成服务的调用处理。http 使用的是 retrofit2 框架进行封装。mq(RabbitMQ)直接使用 RabbitTemplate 模板 push 消息即可。 + diff --git "a/docs/md/project/local-task-message/\347\254\2545\350\212\202\357\274\232\345\212\250\346\200\201\344\273\273\345\212\241\350\241\245\345\201\277\345\244\204\347\220\206.md" "b/docs/md/project/local-task-message/\347\254\2545\350\212\202\357\274\232\345\212\250\346\200\201\344\273\273\345\212\241\350\241\245\345\201\277\345\244\204\347\220\206.md" new file mode 100644 index 000000000..ec0131d6a --- /dev/null +++ "b/docs/md/project/local-task-message/\347\254\2545\350\212\202\357\274\232\345\212\250\346\200\201\344\273\273\345\212\241\350\241\245\345\201\277\345\244\204\347\220\206.md" @@ -0,0 +1,29 @@ +--- +title: 第5节:动态任务补偿处理 +pay: https://t.zsxq.com/cyeBu +--- + +# 《本地任务消息组件》- 第5节:动态任务补偿处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/9gZ6T](https://t.zsxq.com/9gZ6T) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在 Spring Event 接收到事件消息后,无论是 MQ 的发送,还是 HTTP 的调用,都是有可能失败的。可能是网络超时,可能是服务宕机,可能是线程阻塞,可能是流量洪峰等等原因。 + +所以,我们需要对完成 MQ、HTTP 的处理后,首先更新数据库任务表状态,是成功还是失败。之后再由定时扫描任务来做补偿处理,以确保最终一致性。 + +## 二、功能流程 + +如图,定时扫描任务流程图,以及在 http、mq 通知完成后,也要调用 dao 做变更状态操作; + +
+ +
+ +- 首先,任务补偿,它是一个定时任务扫描库表的过程。这里为了提高整体的扫描效率,设计了门牌号,可以配置多个任务,每个任务扫描自己的门牌号范围。另外扫描库表,还要根据条件获取一个最小的符合的id,之后在 `> id limit x` 获取数据列表。 +- 然后,还要对之前的流程做一些补充。http 调用操作、MQ 推送操作,之后要做数据库表任务记录的更新,成功或者失败。这里看,从 Spring Event 接收到消息后,执行通知操作(http、mq),之后更新数据库。这些都不是一个事务的,也就是说从事务(业务数据+任务表数据写入)往后,都是有可能失败的。所以要做任务补偿操作,那么这里也带来一个问题,补偿就有可能重复,比如 http 重复调用一次,mq 重复发送一次,所以在对这些业务长交给你对接的时候,一定要做幂等操作(比如 OrderId 做唯一索引处理)。 \ No newline at end of file diff --git "a/docs/md/project/local-task-message/\347\254\2546\350\212\202\357\274\232\345\210\207\351\235\242\346\213\246\346\210\252\344\273\273\345\212\241\346\223\215\344\275\234.md" "b/docs/md/project/local-task-message/\347\254\2546\350\212\202\357\274\232\345\210\207\351\235\242\346\213\246\346\210\252\344\273\273\345\212\241\346\223\215\344\275\234.md" new file mode 100644 index 000000000..316f5b0f6 --- /dev/null +++ "b/docs/md/project/local-task-message/\347\254\2546\350\212\202\357\274\232\345\210\207\351\235\242\346\213\246\346\210\252\344\273\273\345\212\241\346\223\215\344\275\234.md" @@ -0,0 +1,29 @@ +--- +title: 第6节:切面拦截任务操作 +pay: https://t.zsxq.com/wcAAq +--- + +# 《本地任务消息组件》- 第6节:切面拦截任务操作 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/RVhlT](https://t.zsxq.com/RVhlT) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +因为我们做的是一个通用组件项目,对于它的使用应该要提供出更简单轻量的方式,让使用方可以轻量化的接入。所以对于 `handleService.acceptTaskMessage(taskMessageEntityCommand)` 编码的方式,可以提供更为优化的处理手段。 + +这里我们选择增加一个本地任务消息的自定义注解,对于配置了此注解的方法进行拦截,并获取入参信息的 taskMessageEntityCommand 对象。之后,开始进行同一个事务或开启新的事物的方式,完成数据的插入操作,并进行 Spring Event 消息推送。这样可以让用户的使用更加简洁。 + +## 二、功能流程 + +如图,通过自定义注解加切面拦截方式,完成本地消息的受理。 + +
+ +
+ +- 首先,我们要添加一个自定义注解,并在 config 配置下编写切面的逻辑,这个逻辑主要是获取出配置自定义注解的方法的入参,从里面拿到任务消息对象。 +- 之后,要判断当前当前是否有事务操作,如果没有则开启一个新的事物,如果有则使用同一个事务,完成数据库表数据的插入。之后在推送 SpringEvent 事件消息。和面的流程就一致了。 \ No newline at end of file diff --git "a/docs/md/project/lottery/Part-2/\347\254\25401\350\212\202\357\274\232\347\216\257\345\242\203\343\200\201\351\205\215\347\275\256\343\200\201\350\247\204\350\214\203.md" "b/docs/md/project/lottery/Part-2/\347\254\25401\350\212\202\357\274\232\347\216\257\345\242\203\343\200\201\351\205\215\347\275\256\343\200\201\350\247\204\350\214\203.md" index a61e41759..ccb437134 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25401\350\212\202\357\274\232\347\216\257\345\242\203\343\200\201\351\205\215\347\275\256\343\200\201\350\247\204\350\214\203.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25401\350\212\202\357\274\232\347\216\257\345\242\203\343\200\201\351\205\215\347\275\256\343\200\201\350\247\204\350\214\203.md" @@ -11,6 +11,11 @@ title: 第01节:环境、配置、规范 接下来的章节,小傅哥会带着大家以DDD架构和设计模式落地实战的方式,进行讲解和实现分布式抽奖系统的代码开发,那么这里会涉及到很多DDD的设计思路和设计模式应用,以及互联网大厂开发中所应用到的技术,包括:SpringBoot、Mybatis、Dubbo、MQ、Redis、Mysql、ELK、分库分表、Otter 等。那么在开始项目之前,你可以仔细阅读如下介绍信息,方便你能更加快速的进入学习。 +## 零、优秀作业 + +- [梳理新听到的一些大厂相关技术知识点(或者听过但没具体了解是什么)名词 @RollDemon](https://t.zsxq.com/063n23B2v) +- [代码学习梳理 @北鸢](https://t.zsxq.com/06bmEy3Nv) + ## 一、开发环境 - JDK 1.8 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25402\350\212\202\357\274\232\346\220\255\345\273\272DDD\345\233\233\345\261\202\346\236\266\346\236\204.md" "b/docs/md/project/lottery/Part-2/\347\254\25402\350\212\202\357\274\232\346\220\255\345\273\272DDD\345\233\233\345\261\202\346\236\266\346\236\204.md" index 833029cfc..35020e2f4 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25402\350\212\202\357\274\232\346\220\255\345\273\272DDD\345\233\233\345\261\202\346\236\266\346\236\204.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25402\350\212\202\357\274\232\346\220\255\345\273\272DDD\345\233\233\345\261\202\346\236\266\346\236\204.md" @@ -15,6 +15,24 @@ pay: https://t.zsxq.com/jUbmeE2 本节是陆续搭建系统和编码的开始,我们会优先完成一个基础工程的创建。一般在互联网企业这部分工作可能不需要反复处理,只需要在承接产品需要后使用脚手架或者直接复制以往工程就可以创建现有需要使用的工程了。例如 Spring 官网也提供了创建工程的脚手架,[https://start.spring.io](https://start.spring.io/) Spring Initializr 本质上也是一个 Web 应用,它可以通过 Web 界面、Spring Tool Suite、IntelliJ IDEA 等方式,构建出一个基本的 Spring Boot 项目结构。**但是**,我们创建的项目结构并不是一个简单的 MVC 结构,而是需要基于 DDD 四层架构进行模块化拆分,并把分布式组件 RPC 结合进行,所以这里我们需要进行框架搭建。 +## 零、优秀作业 + +- [之前还是MVC的思想,整体还是对DDD没有很多头绪 @杭电鬼先生](https://t.zsxq.com/06bqJi6Qr) +- [搭建DDD四层架构 @一点江南](https://t.zsxq.com/06VRfUZR7) +- [抽奖系统搭建(DDD + RPC)架构 @sky是清新色](https://t.zsxq.com/06QnieAmm) +- [搭建DDD+RPC架构总结 @Eʟɪᴀᴜᴋ.](https://t.zsxq.com/06yZ3rBeU) +- [初步学习了DDD思想,跑通了第三节 @大琨](https://t.zsxq.com/06IAmeynm) +- [总结:对于dubbo框架和rpc这些概念一头雾水 @BerserkD](https://t.zsxq.com/06fuZje2V) +- [问题:DDD是要解决什么问题,或者说为什么要使用DDD?](https://t.zsxq.com/06Vzni6yv) +- [入手项目,学习DDD架构的思想,动手写代码构建项目 @Chin](https://t.zsxq.com/06uBuFe2V) +- [疑问:领域层包与包之间是完全隔离的么?通信是不是都要通过应用层(application)编排调用,或发送事件? @在下不才](https://t.zsxq.com/06BMR3RR3) +- [认识DDD:习惯MVC模式,对于DDD有些不理解 @Tong Hui](https://t.zsxq.com/06IIaQNZz) +- [配置项目环境,跑通项目代码、学习DDD架构思想 @HL](https://t.zsxq.com/06ufUBmuB) +- [了解环境,配置,规范,搭建DDD+RPC架构 @xbhog](https://t.zsxq.com/06maiQVNb) +- [遇到的问题如下:(主要出在项目搭建与启动上) @浩](https://t.zsxq.com/07I6AmeeA) +- [完成项目结构的搭建,项目驱动学习的理念确实不错!@ Ad.](https://t.zsxq.com/0cn5EgEBK) +- [个人对DDD架构的一点理解,以及和MVC架构中的对比。@陈晓川](https://t.zsxq.com/0c1iaOI00) + ## DDD 分层架构介绍 >DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25403\350\212\202\357\274\232\350\267\221\351\200\232\345\271\277\346\222\255\346\250\241\345\274\217RPC\350\277\207\347\250\213\350\260\203\347\224\250.md" "b/docs/md/project/lottery/Part-2/\347\254\25403\350\212\202\357\274\232\350\267\221\351\200\232\345\271\277\346\222\255\346\250\241\345\274\217RPC\350\277\207\347\250\213\350\260\203\347\224\250.md" index 4ae874b8d..0d3446e66 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25403\350\212\202\357\274\232\350\267\221\351\200\232\345\271\277\346\222\255\346\250\241\345\274\217RPC\350\277\207\347\250\213\350\260\203\347\224\250.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25403\350\212\202\357\274\232\350\267\221\351\200\232\345\271\277\346\222\255\346\250\241\345\274\217RPC\350\277\207\347\250\213\350\260\203\347\224\250.md" @@ -15,6 +15,27 @@ pay: https://t.zsxq.com/Ia6AUvj 当基础的工程模块创建完成以后,还需要给整个工程注入`灵魂`,就是让它可以跑通。这个过程包括一个简单的 RPC 接口功能实现和测试调用,那么这里为了让功能体现出一个完整度,还会创建出一个库表在 RPC 调用的时候查询出库表中的数据并🔙返回结果。那么在这个分支上我们就先来完成这样一个内容的实现。 +## 零、优秀作业 + +- [整合dubbo远程调用rpc测试的时候配置一开始没改导致报错 @卡布奇诺](https://t.zsxq.com/06JaiyFIm) +- [被广播模式坑了下,放上我最后调通的配置 @蛋蛋🏃₄₂.₁₉₅ *](https://t.zsxq.com/06F2V3FqJ) +- [跑通广播模式RPC过程调用 @一点江南](https://t.zsxq.com/06uB27eIu) +- [跑通广播模式RPC过程调用 @numqin](https://t.zsxq.com/06R3rBEA2) +- [RPC 问题排查:Failed to configure a DataSource: 'url' attribute is not specified @sky是清新色](https://t.zsxq.com/06uRvJema) +- [问题排查:由于我本地的mysql是8.0版本,项目的jdbc版本较低,导致项目运行报错 @有生之年有幸相见](https://t.zsxq.com/06vbUNNbe) +- [问题排查:在项目根目录install时出现“Unable to find main class”编译错误 @404](https://t.zsxq.com/06FaqVneM) +- [问题排查:Error creating bean with name 'cn.itedus.lottery.test.ApiTest' @远航](https://t.zsxq.com/06rj6E6QZ) +- [跑通广播模式RPC过程调用 @Geroge Liu](https://t.zsxq.com/06B2NFMBm) +- [跑通广播模式RPC过程调用 @一行。](https://t.zsxq.com/06rjuNFYR) +- [抽奖系统第3-5打卡学习 @CCAT](https://t.zsxq.com/06VRNZfe2) +- [RPC终于跑通了;启动类加注解、@Reference直连、禁用掉了虚拟网络 @YanL99](https://t.zsxq.com/06EIMR7ee) +- [DDD + RPC 各个分层模块的 POM 配置和依赖关系 @Jachin](https://t.zsxq.com/07EqJqRrN) +- [跑通广播模式RPC过程调用,JDK版本问题 @Cc](https://t.zsxq.com/0cJf5EQIc) +- [第一个问题是扫描不到相对应的bean @A](https://t.zsxq.com/0c9V7T8PT) +- [前三节的学习,下面是详细的步骤,给自己记录也给大家一点帮助。@Yu](https://t.zsxq.com/0etx1mgu2) +- [使用dubbo跑通RPC调用,完整操作步骤流程记录 @夜空的寂静](https://t.zsxq.com/0eh7ysSr6) +- [跑通RPC记录,并记录问题处理 @D77](https://t.zsxq.com/0fPtLUNKP) + ## 一、创建抽奖活动表 在抽奖活动的设计和开发过程中,会涉及到的表信息包括:活动表、奖品表、策略表、规则表、用户参与表、中奖信息表等,这些都会在我们随着开发抽奖的过程中不断的添加出来这些表的创建。 @@ -54,4 +75,4 @@ CREATE TABLE `activity` ( - lottery-interfaces,接口层,引用:`application`、`rpc` - lottery-rpc,RPC接口定义层,引用:`common` -在此分层结构和依赖引用下,各层级模块不能循环依赖,同时 `lottery-interfaces` 作为系统的 war 包工程,在构建工程时候需要依赖于 POM 中配置的相关信息。那这里就需要注意下,作为 Lottery 工程下的主 pom.xml 需要完成对 SpringBoot 父文件的依赖,此外还需要定义一些用于其他模块可以引入的配置信息,比如:jdk版本、编码方式等。而其他层在依赖于工程总 pom.xml 后还需要配置自己的信息。 \ No newline at end of file +在此分层结构和依赖引用下,各层级模块不能循环依赖,同时 `lottery-interfaces` 作为系统的 war 包工程,在构建工程时候需要依赖于 POM 中配置的相关信息。那这里就需要注意下,作为 Lottery 工程下的主 pom.xml 需要完成对 SpringBoot 父文件的依赖,此外还需要定义一些用于其他模块可以引入的配置信息,比如:jdk版本、编码方式等。而其他层在依赖于工程总 pom.xml 后还需要配置自己的信息。 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25404\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\347\255\226\347\225\245\345\272\223\350\241\250\350\256\276\350\256\241.md" "b/docs/md/project/lottery/Part-2/\347\254\25404\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\347\255\226\347\225\245\345\272\223\350\241\250\350\256\276\350\256\241.md" index a83096fd3..4ae635511 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25404\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\347\255\226\347\225\245\345\272\223\350\241\250\350\256\276\350\256\241.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25404\350\212\202\357\274\232\346\212\275\345\245\226\346\264\273\345\212\250\347\255\226\347\225\245\345\272\223\350\241\250\350\256\276\350\256\241.md" @@ -13,6 +13,19 @@ pay: https://t.zsxq.com/bYVfQv7 - 分支:[210808_xfg_tableDesign](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/210808_xfg_tableDesign) - 描述:整体设计抽奖活动所需要的库表信息 +## 零、优秀作业 + +- [抽奖活动策略库表设计 @远航](https://t.zsxq.com/06RrnqJa6) +- [抽奖活动策略库表设计 @Geroge Liu](https://t.zsxq.com/06VRRFaei) +- [更深一步理解DDD,实现抽奖算法,用模板模式实现抽奖流程,使用P3C @Chin](https://t.zsxq.com/06MbMBMnu) +- [将抽奖流程基于模板化设计 @一行。](https://t.zsxq.com/06r7QJyfm) +- [分库,分表,分库分表是三件事 分库:解决qps过高,连接数不够用 分表:解决数据量过大 @御剑听风起](https://t.zsxq.com/06bYni2bY) +- [抽奖活动策略库表设计 @浩](https://t.zsxq.com/07jqneQJI) +- [研究了一下为什么需要分库分表,何时分库 @爱奋斗的小鲨鱼](https://t.zsxq.com/08bPVYcNf) +- [抽奖系统知识结构梳理 @神呢八点半独享](https://t.zsxq.com/09ZnvdhRF) +- [第四章节主要介绍了活动策略库表的设计,我对分表分库以及表依赖相对亲切一些也很好理解 @燃起骚气](https://t.zsxq.com/09M3UIWih) +- [领取活动表梳理图 @A](https://t.zsxq.com/0cbgzGYkX) + ## 一、需要建哪些表 一个满足业务需求的抽奖系统,需要提供抽奖活动配置、奖品概率配置、奖品梳理配置等内容,同时用户在抽奖后需要记录用户的抽奖数据,这就是一个抽奖活动系统的基本诉求。 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25405\350\212\202\357\274\232\346\212\275\345\245\226\347\255\226\347\225\245\351\242\206\345\237\237\346\250\241\345\235\227\345\274\200\345\217\221.md" "b/docs/md/project/lottery/Part-2/\347\254\25405\350\212\202\357\274\232\346\212\275\345\245\226\347\255\226\347\225\245\351\242\206\345\237\237\346\250\241\345\235\227\345\274\200\345\217\221.md" index f3df8c2ea..48f3f01bb 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25405\350\212\202\357\274\232\346\212\275\345\245\226\347\255\226\347\225\245\351\242\206\345\237\237\346\250\241\345\235\227\345\274\200\345\217\221.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25405\350\212\202\357\274\232\346\212\275\345\245\226\347\255\226\347\225\245\351\242\206\345\237\237\346\250\241\345\235\227\345\274\200\345\217\221.md" @@ -13,6 +13,35 @@ pay: https://t.zsxq.com/IeqVjMR - 分支:[210814_xfg_strategy](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/210814_xfg_strategy) - 描述:在domain抽奖领域模块实现两种抽奖策略算法,包括:单项概率抽奖和整体概率抽奖,并提供统一的调用方式 +## 零、优秀作业 + +- [抽奖策略的细节有些疑问思考后记了点笔记 @阿羲⭐️](https://t.zsxq.com/06uN3zrnE) +- [抽奖算法整理 @朝北](https://t.zsxq.com/06RFi666I) +- [抽奖策略领域模块开发 @一点江南](https://t.zsxq.com/06iYVNrj2) +- [第五章策略模式实现抽奖算法 @要学的太多](https://t.zsxq.com/06QvNZvJ2) +- [领域模块开发 @我的旅途](https://t.zsxq.com/0627miMJ6) +- [领域模块的实现 @BerserkD](https://t.zsxq.com/06AEm2Zfe) +- [抽奖活动策略表设计 @Φ](https://t.zsxq.com/06Q3jA6a2) +- [问题:MySQL被入侵、BTC勒索](https://t.zsxq.com/066qzjeq7) +- [开发抽奖领域 @奥斯卡最佳配角](https://t.zsxq.com/06FyRVRvb) +- [抽奖策略领域模块开发 @Geroge Liu](https://t.zsxq.com/06jeIYFeQ) +- [抽奖领域模型分成三部分 @liuc](https://t.zsxq.com/067qr7mmi) +- [抽奖策略,整理下思路 @YOLO](https://t.zsxq.com/063B2zrBE) +- [为什么单项概率需要散列到长度为128的数组中去而不是直接按顺序插入长度为100的数组 @轻舟故人](https://t.zsxq.com/06ZBmQrbu) +- [对DDD和MVC有了一个分层的认知 @浩](https://t.zsxq.com/07Mn23JAQ) +- [根据业务需求与使用场景设计数据库表 @Jachin](https://t.zsxq.com/07iuJU7yB) +- [今天将昨天的05节的代码实现好好读了一遍,并且了大量的注释在代码中理解 @Jachin](https://t.zsxq.com/07Mv3nQBE) +- [在项目结构上总是喜欢和目前公司的mvc进行比较 @稣](https://t.zsxq.com/09Xldk7rL) +- [这里讲讲重点吧,awardID的存储,怎么进行抽奖概率的初始化这些 @爱奋斗的小鲨鱼](https://t.zsxq.com/09uxtaeE7) +- [学习本章,花费了3天时间学习,前两天通过面经手册补充了HashMap和ThreadLocal的知识 @Ad.](https://t.zsxq.com/0ctATk3DZ) +- [深度学习本章节领域架构、设计模式、算法实现 @learningJ](https://t.zsxq.com/0dSlihyWM) +- [什么是斐波那契散列法?@这](https://t.zsxq.com/0dydgtuBj) +- [调试 Bug 调了一下午,经验总结 @twinkler](https://t.zsxq.com/0dq5vd4j2) +- [学习抽奖算法卡了两天 但收获满满 @D77](https://t.zsxq.com/0f6mxTjyu) +- [抽奖策略领域模块开发,功能xmind梳理 @Ken](https://t.zsxq.com/1074H0adK) +- [感觉对DDD架构有了理解,在学习的过程中也见到了许多以前没见过的东西,我把这些记录了一下。 @... ...](https://t.zsxq.com/10nV63bCT) +- [这几天总结下来,每次进入新章节,先看视频,再看wiki,再看大佬们的作业,结合大家的理解和问题,再去写代码,很有收获。@素质男孩](https://t.zsxq.com/10e3wb5vf) + ## 一、需求引出设计 **需求**:在一场营销抽奖活动玩法中,运营人员通常会配置以转盘、盲盒等展现形式的抽奖玩法。例如在转盘中配置12个奖品,每个奖品配置不同的中奖概率,当1个奖品被抽空了以后,那么再抽奖时,是剩余的奖品总概率均匀分配在11个奖品上,还是保持剩余11个奖品的中奖概率,如果抽到为空的奖品则表示未中奖。其实这两种方式在实际的运营过程中都会有所选取,主要是为了配合不同的玩法。 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25406\350\212\202\357\274\232\346\250\241\346\235\277\346\250\241\345\274\217\345\244\204\347\220\206\346\212\275\345\245\226\346\265\201\347\250\213.md" "b/docs/md/project/lottery/Part-2/\347\254\25406\350\212\202\357\274\232\346\250\241\346\235\277\346\250\241\345\274\217\345\244\204\347\220\206\346\212\275\345\245\226\346\265\201\347\250\213.md" index 9e01c0fcc..3fce13f51 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25406\350\212\202\357\274\232\346\250\241\346\235\277\346\250\241\345\274\217\345\244\204\347\220\206\346\212\275\345\245\226\346\265\201\347\250\213.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25406\350\212\202\357\274\232\346\250\241\346\235\277\346\250\241\345\274\217\345\244\204\347\220\206\346\212\275\345\245\226\346\265\201\347\250\213.md" @@ -13,6 +13,26 @@ pay: https://t.zsxq.com/ea6AI23 - 分支:[210828_xfg_subtractionStock](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/210828_xfg_subtractionStock) - 描述:基于模板设计模式,规范化抽奖执行流程。包括:提取抽象类、编排模板流程、定义抽象方法、执行抽奖策略、扣减中奖库存、包装返回结果等,并基于P3C标准完善本次开发涉及到的代码规范化处理。 +## 零、优秀作业 + +- [第六章模板模式跟着一遍印象加深 @YAMIN](https://t.zsxq.com/06aUr7EII) +- [【数据库乐观锁】的方式来控制奖品库存,防止超卖 @拿笔小星](https://t.zsxq.com/06IqJiqBI) +- [6-8节结构图梳理 @阿羲⭐️](https://t.zsxq.com/06AuRNF6E) +- [模板模式处理抽奖流程 @一点江南](https://t.zsxq.com/06Rj66A6A) +- [模板模式处理抽奖流程 @BerserkD](https://t.zsxq.com/066EaYvrn) +- [抽奖系统第6节模板模式处理抽奖流程 @巍](https://t.zsxq.com/06qjeQfm2) +- [模板模式处理抽奖流程 @Geroge Liu](https://t.zsxq.com/06YVFU3Nf) +- [构建活动领取模块,复习路由组件 @Chin](https://t.zsxq.com/06jeyzn6E) +- [学习策略库表的设计、抽奖算法的实现与抽奖流程的设定 @HL](https://t.zsxq.com/06Q3vrbIE) +- [抽奖过程方法实现 @Jachin](https://t.zsxq.com/07myBUJQb) +- [通过两天的学习成功将第五、第六章节跑通并完成了测试,感受最深的是对策略模式和模板模式的进一步了解 @星期一](https://t.zsxq.com/0cbdllIkT) +- [记录一下 第5部分第06节:部署环境 nacos 踩的坑 @灵幻新隆](https://t.zsxq.com/0dbNP5eNh) +- [增加了一个模板方法模板化处理抽奖流程,使得整个抽奖过程结构清晰 @twinkler](https://t.zsxq.com/0duCVEyz3) +- [学习进度06节模板模式处理抽奖流程,好的代码就是好的文档的一部分 @learningJ](https://t.zsxq.com/0dn5PbkgS) +- [这里一个map看起来代码也简洁清晰也没有用到if else,所以这里就是这章节的最大收获 @learningJ](https://t.zsxq.com/0dUkFrWsY) +- [模版模式处理抽奖流程,分享自己的学习方法 @theSunlin](https://t.zsxq.com/0f9OnUVrv) +- [第2章~第6章,学习汇总 @CCAT](https://t.zsxq.com/11meB49dE) + ## 一、开发日志 - 下载安装 IDEA P3C 插件 `Alibaba Java Coding Guidelines`,统一标准化编码方式。*在本次分支涉及到的代码开发中,已调整代码中类、属性、方法对应的注释信息* diff --git "a/docs/md/project/lottery/Part-2/\347\254\25407\350\212\202\357\274\232\347\256\200\345\215\225\345\267\245\345\216\202\346\220\255\345\273\272\345\217\221\345\245\226\351\242\206\345\237\237.md" "b/docs/md/project/lottery/Part-2/\347\254\25407\350\212\202\357\274\232\347\256\200\345\215\225\345\267\245\345\216\202\346\220\255\345\273\272\345\217\221\345\245\226\351\242\206\345\237\237.md" index 14f52e53d..2e59ffcc4 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25407\350\212\202\357\274\232\347\256\200\345\215\225\345\267\245\345\216\202\346\220\255\345\273\272\345\217\221\345\245\226\351\242\206\345\237\237.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25407\350\212\202\357\274\232\347\256\200\345\215\225\345\267\245\345\216\202\346\220\255\345\273\272\345\217\221\345\245\226\351\242\206\345\237\237.md" @@ -13,6 +13,19 @@ pay: https://t.zsxq.com/Vf6Iiai - 分支:[210904_xfg_award](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/210904_xfg_award) - 描述:运用简单工厂设计模式,在 `domain/award` 搭建发奖领域服务。 +## 零、优秀作业 + +- [小小NPE哪能难得到我呢?@YAMIN](https://t.zsxq.com/06fmYBI2F) +- [简单工厂搭建发奖领域 @一点江南](https://t.zsxq.com/06r7aeq7M) +- [DDD架构和设计模式的实战应用 @AhHao](https://t.zsxq.com/06jqvJ6MN) +- [简单工厂模式搭建发奖领域 @BerserkD](https://t.zsxq.com/06imAaMnE) +- [修改数据库规范、完成发奖领域、用简单工厂模式去获取真正实现派发操作的类 @Chin](https://t.zsxq.com/06aEyf6m2) +- [简单工厂模式搭建发奖领域 @Geroge Liu](https://t.zsxq.com/06fq7y7m6) +- [抽奖系统流程结构梳理 @xbhog](https://t.zsxq.com/08Gbdo415) +- [在遇到的问题中,梳理流程,debug调试问题 @星期一](https://t.zsxq.com/0dGV8NGh1) +- [遇到一些功能方法暂时没有时间实现的或者是一些问题待解决的,可以使用TODO标签来标识这些地方 @twinkler](https://t.zsxq.com/0dkWv54X5) +- [代码太优雅了,一下子就上头了。@待佳人晚归](https://t.zsxq.com/0eAFk84M7) + ## 一、开发日志 - 下载安装 [Navicat Premium 15.0.30](http://rjxz.jxhwst.top/index.html) diff --git "a/docs/md/project/lottery/Part-2/\347\254\25408\350\212\202\357\274\232\346\264\273\345\212\250\351\242\206\345\237\237\347\232\204\351\205\215\347\275\256\344\270\216\347\212\266\346\200\201.md" "b/docs/md/project/lottery/Part-2/\347\254\25408\350\212\202\357\274\232\346\264\273\345\212\250\351\242\206\345\237\237\347\232\204\351\205\215\347\275\256\344\270\216\347\212\266\346\200\201.md" index 61e643a8e..e9b256385 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25408\350\212\202\357\274\232\346\264\273\345\212\250\351\242\206\345\237\237\347\232\204\351\205\215\347\275\256\344\270\216\347\212\266\346\200\201.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25408\350\212\202\357\274\232\346\264\273\345\212\250\351\242\206\345\237\237\347\232\204\351\205\215\347\275\256\344\270\216\347\212\266\346\200\201.md" @@ -13,6 +13,19 @@ pay: https://t.zsxq.com/QZnq7IY - 分支:[210911_xfg_activity](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/210911_xfg_activity) - 描述:开发活动领域部分功能,包括:活动创建、活动状态变更。主要以 domain 领域层下添加 activity 为主,并在对应的 service 中添加 deploy(创建活动)、partake(领取活动,待开发)、stateflow(状态流转) 三个模块。以及调整仓储服务实现到基础层。 +## 零、优秀作业 + +- [为什么仓储接口定义在领域层?@拿笔小星](https://t.zsxq.com/06by3jYFe) +- [活动领域的配置与状态 @一点江南](https://t.zsxq.com/06vzNVVfQ) +- [把domain包引入infrastructure包改成infrastructure引入domain,为什么它会更符合DDD领域设计?@我的旅途](https://t.zsxq.com/06mu7qnyJ) +- [活动领域的配置与状态 @BerserkD](https://t.zsxq.com/06qzzvBY7) +- [更改domain层和基础层结构,第八章是活动领域的部分代码,有创建活动功能以及活动状态处理的实现 @Chin](https://t.zsxq.com/067MvRjMR) +- [活动领域的配置与状态 @Geroge Liu](https://t.zsxq.com/06BYZrfq3) +- [开发活动领域部分功能,包括活动创建(涉及到数据落表,用到事务)、活动状态变更(用到设计模式中的状态模式) @liuc](https://t.zsxq.com/06BeEmMJu) +- [在应用层编排抽奖过程 @liuc](https://t.zsxq.com/06eMrzNFu) +- [项目过程中遇到的bug以及经验总结 @锚](https://t.zsxq.com/0ceEsHmkN) +- [活动领域的配置与状态学习总结 @爱幻想](https://t.zsxq.com/0fl3qvi4d) + ## 一、开发日志 - 按照 DDD 模型,调整包引用 lottery-infrastructure 引入 lottery-domain,调整后效果`领域层 domain` 定义仓储接口,`基础层 infrastructure` 实现仓储接口。 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25409\350\212\202\357\274\232ID\347\224\237\346\210\220\347\255\226\347\225\245\351\242\206\345\237\237\345\274\200\345\217\221.md" "b/docs/md/project/lottery/Part-2/\347\254\25409\350\212\202\357\274\232ID\347\224\237\346\210\220\347\255\226\347\225\245\351\242\206\345\237\237\345\274\200\345\217\221.md" index 2cc3f1fd9..469a324d1 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25409\350\212\202\357\274\232ID\347\224\237\346\210\220\347\255\226\347\225\245\351\242\206\345\237\237\345\274\200\345\217\221.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25409\350\212\202\357\274\232ID\347\224\237\346\210\220\347\255\226\347\225\245\351\242\206\345\237\237\345\274\200\345\217\221.md" @@ -13,6 +13,17 @@ pay: https://t.zsxq.com/qR3vRfq - 分支:[210920_xfg_IdGenerator](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/210920_xfg_IdGenerator) - 描述:使用雪花算法、阿帕奇工具包 RandomStringUtils、日期拼接,三种方式生成ID,分别用在订单号、策略ID、活动号的生成上。 +## 零、优秀作业 + +- [ID生成策略领域开发 @一点江南](https://t.zsxq.com/06AmIAUZr) +- [ID生成策略领域开发 @BerserkD](https://t.zsxq.com/06BUvZ7yB) +- [ID生成领域 @Chin](https://t.zsxq.com/06m2Z7MFI) +- [策略模式实现ID生成策略领域开发 @Geroge Liu](https://t.zsxq.com/06MbYJmeq) +- [ID生成策略领域开发,包括雪花算法、日期拼接、随机数生成ID, @liuc](https://t.zsxq.com/06MfQzVrJ) +- [ID生成策略使用策略模式 @Gourdpa](https://t.zsxq.com/06m6Imimy) +- [重点是活动状态变更,使用了状态模式,在抽象类中定义了七种状态 @素质男孩](https://t.zsxq.com/104EiLW10) +- [第7章~第9章,学习汇总 @CCAT](https://t.zsxq.com/11wHIwiK5) + ## 一、开发日志 - 【说明】从本章节开始,我们会陆续的引入一些基础内容的搭建,包括本章节关于ID的生成、以及后续章节需要引入分库分表、vo2dto方法、Redis等,这些会支撑我们继续开发业务领域中一些需要用到的订单号、活动号生成以及个人用户参与到的抽奖信息落库。 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25410\350\212\202\357\274\232\345\256\236\347\216\260\345\222\214\344\275\277\347\224\250\345\210\206\345\272\223\345\210\206\350\241\250.md" "b/docs/md/project/lottery/Part-2/\347\254\25410\350\212\202\357\274\232\345\256\236\347\216\260\345\222\214\344\275\277\347\224\250\345\210\206\345\272\223\345\210\206\350\241\250.md" index 71804fbbe..e46a6bf83 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25410\350\212\202\357\274\232\345\256\236\347\216\260\345\222\214\344\275\277\347\224\250\345\210\206\345\272\223\345\210\206\350\241\250.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25410\350\212\202\357\274\232\345\256\236\347\216\260\345\222\214\344\275\277\347\224\250\345\210\206\345\272\223\345\210\206\350\241\250.md" @@ -14,6 +14,37 @@ pay: https://t.zsxq.com/ZzzFIyn - 组件:[db-router-spring-boot-starter](https://gitcode.net/KnowledgePlanet/db-router-spring-boot-starter) - 描述:开发一个基于 HashMap 核心设计原理,使用哈希散列+扰动函数的方式,把数据散列到多个库表中的组件,并验证使用。 +## 零、优秀作业 + +- [计算数据库位置的时候,用【int dbIdx = idx / dbRouterConfig.getTbCount() + 1】为什么是【dbRouterConfig.getTbCount()】标的数量呢?@拿笔小星](https://t.zsxq.com/066qNRjqf) +- [问题排查 Error creating bean with name 'db-router-point' @归斯](https://t.zsxq.com/06VbuBUfY) +- [实现和使用分库分表 @一点江南](https://t.zsxq.com/067myZrvf) +- [DB路由组件 @L、m](https://t.zsxq.com/06vv76UVr) +- [实现和使用分库分表 @BerserkD](https://t.zsxq.com/063nu3jyV) +- [实现和使用分库分表,开发自研数据库路由 @奥斯卡最佳配角](https://t.zsxq.com/06eQZVvFE) +- [实现自定义路由组件,学会springboot组件开发 @杨杨得亿🙉](https://t.zsxq.com/067euNz7y) +- [这章的目的是自定义路由实现分库分表 @Chin](https://t.zsxq.com/06amAmEau) +- [数据路由组件的实现和引入组件实现分库分表 @Geroge Liu](https://t.zsxq.com/06rfQ33bU) +- [实现和使用分库分表 @liuc](https://t.zsxq.com/06MrzvVz7) +- [通过注解实现数据分散到不同库不同表的操作 @xbhog](https://t.zsxq.com/07VrNjMZn) +- [领取整体流程(图一)和奖品领取信息的操作流程。@xbhog](https://t.zsxq.com/08LJZl91a) +- [分库分表路由组件开发细节流程梳理 @爱奋斗的小鲨鱼](https://t.zsxq.com/09LVJcja0) +- [DBRouter注解写好后,肯定要配备切面类DBRouterJoinPoint @SEN](https://t.zsxq.com/0ck1H9qRq) +- [因为一个用户表包含了几十甚至上百个字段,管理混乱所以需要分表,将该表拆分成多个表 @霍der~](https://t.zsxq.com/0cLlPhvrt) +- [分析分库分表实现原理分析 @布丁迪厄](https://t.zsxq.com/0cmnJ7zNZ) +- [主要对于分库分表路由组件进行了学习,跟着敲并尽力理解了所有的类与接口,理清楚它们之间的流程 @AD钙奶](https://t.zsxq.com/0cDj2xmzB) +- [动态数据源切换的原理 @圈](https://t.zsxq.com/0cy6nh6gB) +- [项目过程中遇到的bug以及经验总结 @锚](https://t.zsxq.com/0cSeFxyi8) +- [在这一章节我们会学习到的技术:AOP、数据源切换、散列算法、哈希寻址、ThreadLocal @星期一](https://t.zsxq.com/0dKxVQDri) +- [总结一下db-Router,不得不说。太牛了。尤其是Java反射。真的是刷新了认知。@陈晓川](https://t.zsxq.com/0dCcY5F1T) +- [路由组件执行流程分析 @twinkler](https://t.zsxq.com/0dtJdw4bi) +- [通过debug理清了路由组件的执行流程 @T](https://t.zsxq.com/0ezh7FlF0) +- [一个简单的路由组件的开发,将之前很多理论落实到了实际中,涉及面很广,但逻辑却不复杂,很是舒服。@错否](https://t.zsxq.com/0edCGCzyn) +- [一张组件执行sql路由的流程图和组件里的bean实例化顺序图 @double](https://t.zsxq.com/0fZELdch7) +- [dbRouter实现分库分表,现在跟着代码一套做下来,很少会有bug, 或者说有bug也能快速地解决。相比于刚开始,一个bug卡半天算是小小的进步啦。](https://t.zsxq.com/10Oc7MlOe) +- [扩展和完善数据库路由组件中编程式事务 @素质男孩](https://t.zsxq.com/10i1fQb5q) +- [梳理了两天路由组件的运行流程和相关的知识,感觉提升很大,提升主要是在架构和基础知识上,其中设计了大量的反射知识和aop的理论 @1](https://t.zsxq.com/14WTMsyv0) + ## 一、开发日志 - 9月22日,新增数据库路由组件开发工程 db-router-spring-boot-starter 这是一个自研的分库分表组件。主要用到的技术点包括:散列算法、数据源切换、AOP切面、SpringBoot Starter 开发等 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25411\350\212\202\357\274\232\345\243\260\346\230\216\344\272\213\345\212\241\351\242\206\345\217\226\346\264\273\345\212\250\351\242\206\345\237\237\345\274\200\345\217\221.md" "b/docs/md/project/lottery/Part-2/\347\254\25411\350\212\202\357\274\232\345\243\260\346\230\216\344\272\213\345\212\241\351\242\206\345\217\226\346\264\273\345\212\250\351\242\206\345\237\237\345\274\200\345\217\221.md" index 1dfccc667..988b182a9 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25411\350\212\202\357\274\232\345\243\260\346\230\216\344\272\213\345\212\241\351\242\206\345\217\226\346\264\273\345\212\250\351\242\206\345\237\237\345\274\200\345\217\221.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25411\350\212\202\357\274\232\345\243\260\346\230\216\344\272\213\345\212\241\351\242\206\345\217\226\346\264\273\345\212\250\351\242\206\345\237\237\345\274\200\345\217\221.md" @@ -14,6 +14,21 @@ pay: https://t.zsxq.com/qzFAAuB - 路由组件:[db-router-spring-boot-starter#211001_xfg_TransactionTemplate `新增硬编码路由,用于支撑声明式事务使用`](https://gitcode.net/KnowledgePlanet/db-router-spring-boot-starter/-/tree/211001_xfg_TransactionTemplate) - 描述:扩展自研数据库路由组件,支持声明式事务处理。用于领取活动领域功能开发中用户领取活动信息,在一个事务下记录多张表数据。 +## 零、优秀作业 + +- [声明事务领取活动领域开发 @一点江南](https://t.zsxq.com/063RJQBaa) +- [声明事务领取活动领域开发 @BerserkD](https://t.zsxq.com/06NNRvbYB) +- [模板模式的核心就是通过抽象类定义抽象方法的执行顺序 @知行人](https://t.zsxq.com/06Aq7MFEa) +- [事务控制_领取活动领域开发 @Geroge Liu](https://t.zsxq.com/06yBAuRzf) +- [声明事务领域、活动领域开发(完成第8节partake部分服务)@liuc](https://t.zsxq.com/06mMrvbyz) +- [同一个事务下,连续操作不同的DAO操作,那么就会涉及到在 DAO 上使用注解 @DBRouter(key = "uId") 反复切换路由的操作。虽然都是一个数据源,但这样切换后,为什么事务就没法处理了? @眼镜](https://t.zsxq.com/0aq44ZGnY) +- [业务接口 + 模板抽象类 + 数据支撑类 + 抽象方法实现类,职责明确 @AD钙奶](https://t.zsxq.com/0c74ctHdI) +- [项目过程中遇到的bug以及经验总结 @锚](https://t.zsxq.com/0dxINxZ34) +- [什么是编程式事务?@星期一](https://t.zsxq.com/0dQWJKBEN) +- [编程式事务,transactionTemplate.execute(status -> {}),代码控制回滚status.setRollbackOnly(); @learningJ](https://t.zsxq.com/0epY0blgW) +- [整体结构比较简单,即使用了模板方法,定义了活动领取流程,难点在于细节 @错否](https://t.zsxq.com/0eI00j0Wa) +- [Spring声明式事务引起的路由失效分析 @布丁](https://t.zsxq.com/11VLJ3lZL) + ## 一、开发日志 - db-router-spring-boot-starter 扩展和完善自研简单版数据库路由组件,拆解路由策略满足声明式路由配合声明式事务一起使用。 @@ -37,4 +52,4 @@ public interface IDBRouterStrategy { } ``` -- 把路由算法拆解出来,无论是切面中还是硬编码,都通过这个方法进行计算路由 \ No newline at end of file +- 把路由算法拆解出来,无论是切面中还是硬编码,都通过这个方法进行计算路由 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25412\350\212\202\357\274\232\345\234\250\345\272\224\347\224\250\345\261\202\347\274\226\346\216\222\346\212\275\345\245\226\350\277\207\347\250\213.md" "b/docs/md/project/lottery/Part-2/\347\254\25412\350\212\202\357\274\232\345\234\250\345\272\224\347\224\250\345\261\202\347\274\226\346\216\222\346\212\275\345\245\226\350\277\207\347\250\213.md" index 5c333c4f3..29ed7b332 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25412\350\212\202\357\274\232\345\234\250\345\272\224\347\224\250\345\261\202\347\274\226\346\216\222\346\212\275\345\245\226\350\277\207\347\250\213.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25412\350\212\202\357\274\232\345\234\250\345\272\224\347\224\250\345\261\202\347\274\226\346\216\222\346\212\275\345\245\226\350\277\207\347\250\213.md" @@ -14,6 +14,20 @@ pay: https://t.zsxq.com/A2FeaEe - 路由组件:[db-router-spring-boot-starter `打包最新路由组件包`](https://gitcode.net/KnowledgePlanet/db-router-spring-boot-starter) - 描述:在 application 应用层调用领域服务功能,编排抽奖过程,包括:领取活动、执行抽奖、落库结果,这其中还有一部分待实现的发送 MQ 消息,后续处理。 +## 零、优秀作业 + +- [应用层编排抽奖流程 @微风](https://t.zsxq.com/06JuV3nmA) +- [在应用层编排抽奖过程 @一点江南](https://t.zsxq.com/06zZZFIuZ) +- [在应用层编排抽奖过程 @BerserkD](https://t.zsxq.com/067uJY3jI) +- [需要将之前写的领域层进行编排,实现完整的抽奖服务 @Chin](https://t.zsxq.com/06aI6Qfu7) +- [应用层抽奖活动过程编排 @Geroge Liu](https://t.zsxq.com/066a2jYv7) +- [应用层编排抽奖流程 @微风](https://t.zsxq.com/06JuV3nmA) +- [对12节进行了学习,因为表内又增添字段了,本来是又有点迷茫的,但是在单元测试的时候出了bug,就一步步地打断点调试,将整个流程又走了一遍,感觉就很清晰了。@在九月](https://t.zsxq.com/09BE0r8ZR) +- [开发application层,对前面章节的流程进行编排,发现并完善之前章节遗留的bug @AD钙奶](https://t.zsxq.com/0cefmYbB3) +- [在这一章节的学习中我首次了解到幂等性的事务 @星期一](https://t.zsxq.com/0doPzYuW3) +- [花了挺多时间梳理了下整个抽奖流程,复习了下前面的内容 @错否](https://t.zsxq.com/0esX76oWF) +- [从六月初学到现在七月中旬,开始逐渐理解整个抽奖系统的设计思路了。对于小白来说,如果咬牙坚持下来,收获也是巨大的。比起做四五个零碎的项目,做一个lottery项目就囊括了所有需要掌握的技巧知识。](https://t.zsxq.com/10Owe1lwg) + ## 一、开发日志 - 分别在两个分库的表 lottery_01.user_take_activity、lottery_02.user_take_activity 中添加 state`【活动单使用状态 0未使用、1已使用】` 状态字段,这个状态字段用于写入中奖信息到 user_strategy_export_000~003 表中时候,两个表可以做一个幂等性的事务。同时还需要加入 strategy_id 策略ID字段,用于处理领取了活动单但执行抽奖失败时,可以继续获取到此抽奖单继续执行抽奖,而不需要重新领取活动。*其实领取活动就像是一种活动镜像信息,可以在控制幂等反复使用* diff --git "a/docs/md/project/lottery/Part-2/\347\254\25413\350\212\202\357\274\232\350\247\204\345\210\231\345\274\225\346\223\216\351\207\217\345\214\226\344\272\272\347\276\244\345\217\202\344\270\216\346\264\273\345\212\250.md" "b/docs/md/project/lottery/Part-2/\347\254\25413\350\212\202\357\274\232\350\247\204\345\210\231\345\274\225\346\223\216\351\207\217\345\214\226\344\272\272\347\276\244\345\217\202\344\270\216\346\264\273\345\212\250.md" index 30c7cee1d..b77e642a8 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25413\350\212\202\357\274\232\350\247\204\345\210\231\345\274\225\346\223\216\351\207\217\345\214\226\344\272\272\347\276\244\345\217\202\344\270\216\346\264\273\345\212\250.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25413\350\212\202\357\274\232\350\247\204\345\210\231\345\274\225\346\223\216\351\207\217\345\214\226\344\272\272\347\276\244\345\217\202\344\270\216\346\264\273\345\212\250.md" @@ -13,6 +13,29 @@ pay: https://t.zsxq.com/qBIa6yZ - 分支:[211008_xfg_rule](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/211008_xfg_rule) - 描述:使用组合模式搭建用于量化人群的规则引擎,用于用户参与活动之前,通过规则引擎过滤性别、年龄、首单消费、消费金额、忠实用户等各类身份来量化出具体可参与的抽奖活动。通过这样的方式控制运营成本和精细化运营。 +## 零、优秀作业 + +- [13-19节结构图梳理 @阿羲⭐️](https://t.zsxq.com/06fufyrzn) +- [规则引擎量化人群参与活动 @一点江南](https://t.zsxq.com/06euz3zVj) +- [规则引擎量化人群参与活动 @BerserkD](https://t.zsxq.com/066iQZJYN) +- [规则引擎量化量化人群参与活动 @杨杨得亿🙉](https://t.zsxq.com/06vRFEA6u) +- [通过用户的年龄和性别对用户进行筛选 @Chin](https://t.zsxq.com/06ayB6Iyv) +- [规则引擎之量化人群参与抽奖 @Geroge Liu](https://t.zsxq.com/06ieyzNbQ) +- [规则引擎量化人群参与活动 @liuc](https://t.zsxq.com/067MJqnQ7) +- [领域量化人群 @微风](https://t.zsxq.com/06FqbyFM3) +- [通过注解配置执行SQL语句 @杨杨得亿🙉](https://t.zsxq.com/07QjqzbIy) +- [打造这样一棵有规则限制的二叉树,筛选出合适的用户。@AD钙奶](https://t.zsxq.com/0dCldrhBF) +- [规则引擎是什么,用在什么地方?@锚](https://t.zsxq.com/0dhUPOvoS) +- [本章节学习到了组合模式的设计模式,组合模式是什么,用在什么地方,优点是什么? @星期一](https://t.zsxq.com/0dRKQE6r0) +- [组合模式在决策树规则引擎场景的使用,最主要是数据库表的设计 @learningJ](https://t.zsxq.com/0eS6ekcEo) +- [在对整个流程梳理下来时,才体会到了组合,并且对组合模式解决的问题有了更深的体会 @错否](https://t.zsxq.com/0edujSAGM) +- [规则引擎决策树学习整理 @爱幻想](https://t.zsxq.com/0flIV5OeL) +- [通过组合模式实现量化人群的规则引擎,整个设计结构梳理 @炸毛的猫](https://t.zsxq.com/10knSHqgD) +- [活动领取、领域编排、规则引擎,功能图总结 @派大星来学习](https://t.zsxq.com/10ipUFkC2) +- [本章节运用了组合模式,组合模式是依据树形结构来组合对象 @素质男孩](https://t.zsxq.com/102mZrEzu) +- [系统功能流程图汇总 @CCAT](https://t.zsxq.com/11vxKGZor) +- [规则引擎这一章节有一些懵,画一个时序图豁然开朗 @1](https://t.zsxq.com/14AV22r8X) + ## 一、开发日志 - 增加规则引擎开发需要的相关的配置类表:rule_tree、rule_tree_node、rule_tree_node_line @@ -35,4 +58,4 @@ CREATE TABLE `rule_tree` ( `update_time` datetime DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=10002 DEFAULT CHARSET=utf8; -``` \ No newline at end of file +``` diff --git "a/docs/md/project/lottery/Part-2/\347\254\25414\350\212\202\357\274\232\351\227\250\351\235\242\346\216\245\345\217\243\345\260\201\350\243\205\345\222\214\345\257\271\350\261\241\350\275\254\346\215\242.md" "b/docs/md/project/lottery/Part-2/\347\254\25414\350\212\202\357\274\232\351\227\250\351\235\242\346\216\245\345\217\243\345\260\201\350\243\205\345\222\214\345\257\271\350\261\241\350\275\254\346\215\242.md" index 0e7817a85..6227d96a6 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25414\350\212\202\357\274\232\351\227\250\351\235\242\346\216\245\345\217\243\345\260\201\350\243\205\345\222\214\345\257\271\350\261\241\350\275\254\346\215\242.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25414\350\212\202\357\274\232\351\227\250\351\235\242\346\216\245\345\217\243\345\260\201\350\243\205\345\222\214\345\257\271\350\261\241\350\275\254\346\215\242.md" @@ -13,6 +13,16 @@ pay: https://t.zsxq.com/NZRFqBQ - 分支:[211016_xfg_vo2dto](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/211016_xfg_vo2dto) - 描述:在 lottery-interfaces 接口层创建 `facade 门面模式` 包装抽奖接口,并在 `assembler 包` 使用 MapStruct 做对象转换操作处理。 +## 零、优秀作业 + +- [门面接口封装和对象转换 @一点江南](https://t.zsxq.com/06vzzVbuV) +- [搭建MQ消息组件Kafka服务环境;使用MQ解耦抽奖发货流程 @一点江南](https://t.zsxq.com/06M7uzJuB) +- [门面接口封装和对象转换 @BerserkD](https://t.zsxq.com/06yBiyN7E) +- [门面接口封装和对象转换 @杨杨得亿🙉](https://t.zsxq.com/06R76aeYf) +- [门面模式接口封装与对象转换 @Geroge Liu](https://t.zsxq.com/06VzjEAqf) +- [封装门面,学习使用MapStruct @Chin](https://t.zsxq.com/06RbMVrRV) +- [门面接口封装和对象转换 @liuc](https://t.zsxq.com/06fuvfMrn) + ## 一、开发日志 - 补充 lottery-application 应用层对规则引擎的调用,添加接口方法 IActivityProcess#doRuleQuantificationCrowd diff --git "a/docs/md/project/lottery/Part-2/\347\254\25415\350\212\202\357\274\232\346\220\255\345\273\272MQ\346\266\210\346\201\257\347\273\204\344\273\266Kafka\346\234\215\345\212\241\347\216\257\345\242\203.md" "b/docs/md/project/lottery/Part-2/\347\254\25415\350\212\202\357\274\232\346\220\255\345\273\272MQ\346\266\210\346\201\257\347\273\204\344\273\266Kafka\346\234\215\345\212\241\347\216\257\345\242\203.md" index ea2599e45..b501375d0 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25415\350\212\202\357\274\232\346\220\255\345\273\272MQ\346\266\210\346\201\257\347\273\204\344\273\266Kafka\346\234\215\345\212\241\347\216\257\345\242\203.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25415\350\212\202\357\274\232\346\220\255\345\273\272MQ\346\266\210\346\201\257\347\273\204\344\273\266Kafka\346\234\215\345\212\241\347\216\257\345\242\203.md" @@ -1,6 +1,6 @@ --- title: 第15节:搭建MQ消息组件Kafka服务环境 -pay: https://t.zsxq.com/Y72naAU +pay: https://t.zsxq.com/0bUpPxYR4 --- # 第15节:搭建MQ消息组件Kafka服务环境 @@ -13,6 +13,17 @@ pay: https://t.zsxq.com/Y72naAU - 分支:[211023_xfg_mq_kafka](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/211023_xfg_mq_kafka) - 描述:搭建MQ消息组件Kafka服务环境,并整合到SpringBoot中,完成消息的生产和消费处理 +## 零、优秀作业 + +- [使用MQ解耦抽奖发货流程 @BerserkD](https://t.zsxq.com/06E2NzrVJ) +- [搭建MQ消息组件Kafka服务环境 @杨杨得亿🙉](https://t.zsxq.com/06Zb6623B) +- [扫描库表补偿发货单MQ消息 @杨杨得亿🙉](https://t.zsxq.com/06v3JmYvr) +- [搭建MQ消息组件Kafka服务环境 @Geroge Liu](https://t.zsxq.com/06fQrVVNJ) +- [搭建MQ消息组件Kafka服务环境 @liuc](https://t.zsxq.com/06VzrvZji) +- [搭建MQ消息组件Kafka服务环境【可视化Kafka】 @张=小红=](https://t.zsxq.com/0ciaaIy9u) +- [打开又一个新世界的大门 —— 消息中间件:Kafka @AD钙奶](https://t.zsxq.com/0dWiE0Eiu) +- [kafka环境并测试,链接失败问题总结 @念](https://t.zsxq.com/0faNgGbaT) + ## 一、开发日志 - 搭建 Kafka 环境,配置消息主题 *注意:MQ 消息的使用不非得局限于 Kafka,也可以使用 RocketMq* @@ -29,4 +40,4 @@ Apache Kafka是一个分布式发布 - 订阅消息系统和一个强大的队 - **耐用性** - Kafka使用分布式提交日志,这意味着消息会尽可能快地保留在磁盘上,因此它是持久的。 - **性能** - Kafka对于发布和订阅消息都具有高吞吐量。 即使存储了许多TB的消息,它也保持稳定的性能。 -Kafka非常快,并保证零停机和零数据丢失。 \ No newline at end of file +Kafka非常快,并保证零停机和零数据丢失。 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25416\350\212\202\357\274\232\344\275\277\347\224\250MQ\350\247\243\350\200\246\346\212\275\345\245\226\345\217\221\350\264\247\346\265\201\347\250\213.md" "b/docs/md/project/lottery/Part-2/\347\254\25416\350\212\202\357\274\232\344\275\277\347\224\250MQ\350\247\243\350\200\246\346\212\275\345\245\226\345\217\221\350\264\247\346\265\201\347\250\213.md" index 742c1235c..fa60d42de 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25416\350\212\202\357\274\232\344\275\277\347\224\250MQ\350\247\243\350\200\246\346\212\275\345\245\226\345\217\221\350\264\247\346\265\201\347\250\213.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25416\350\212\202\357\274\232\344\275\277\347\224\250MQ\350\247\243\350\200\246\346\212\275\345\245\226\345\217\221\350\264\247\346\265\201\347\250\213.md" @@ -13,6 +13,13 @@ pay: https://t.zsxq.com/Y72naAU - 分支:[211030_xfg_AsyncDistributionAward](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/211030_xfg_AsyncDistributionAward) - 描述:使用MQ消息的特性,把用户抽奖到发货到流程进行解耦。这个过程中包括了消息的发送、库表中状态的更新、消息的接收消费、发奖状态的处理等。 +## 零、优秀作业 + +- [在抽奖之后完成用mq发送和接受发奖信息 @Chin](https://t.zsxq.com/06AAqNr3F) +- [使用MQ解耦抽奖发货流程 @Geroge Liu](https://t.zsxq.com/06I6uVjMN) +- [使用MQ解耦抽奖发货的流程 @咖啡苦涩](https://t.zsxq.com/0cAi0mcLa) +- [基于整个DDD架构,对应用层与接口层进行了数据对象的隔离,增加一个DTO对象,利用 MapStruct 来实现对象转换 @错否](https://t.zsxq.com/0ecTVUp53) + ## 一、开发日志 - 在数据库表 `user_strategy_export` 添加字段 `mq_state` 这个字段用于发送 MQ 成功更新库表状态,如果 MQ 消息发送失败则需要通过定时任务补偿 MQ 消息。PS:你可以使用本章节分支下的 sql 更新自己的库表。 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25417\350\212\202\357\274\232\345\274\225\345\205\245xxl-job\345\244\204\347\220\206\346\264\273\345\212\250\347\212\266\346\200\201\346\211\253\346\217\217.md" "b/docs/md/project/lottery/Part-2/\347\254\25417\350\212\202\357\274\232\345\274\225\345\205\245xxl-job\345\244\204\347\220\206\346\264\273\345\212\250\347\212\266\346\200\201\346\211\253\346\217\217.md" index 92ce65c26..0d51f6671 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25417\350\212\202\357\274\232\345\274\225\345\205\245xxl-job\345\244\204\347\220\206\346\264\273\345\212\250\347\212\266\346\200\201\346\211\253\346\217\217.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25417\350\212\202\357\274\232\345\274\225\345\205\245xxl-job\345\244\204\347\220\206\346\264\273\345\212\250\347\212\266\346\200\201\346\211\253\346\217\217.md" @@ -13,6 +13,14 @@ pay: https://t.zsxq.com/N3n6uNF - 分支:[211106_xfg_xxl-job](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/211106_xfg_xxl-job) - 描述:引入XXL-JOB,分布式任务调度平台,处理需要使用定时任务解决的场景。 +## 零、优秀作业 + +- [引入xxl-job处理活动状态扫描 @一点江南](https://t.zsxq.com/06A6AyFIa) +- [引入xxl job处理活动状态扫描 @BerserkD](https://t.zsxq.com/06f2nie2V) +- [学习xxljob,使用xxljob实现扫描活动状态并更新 @Chin](https://t.zsxq.com/06ZzFuVBY) +- [引入xxl-job处理活动状态扫描 @Geroge Liu](https://t.zsxq.com/06eeII2fy) +- [引入xxl-job处理活动状态扫描 @liuc](https://t.zsxq.com/067yvVfIE) + ## 一、开发日志 - 搭建 XXL-JOB 分布式任务调度环境,这里需要在官网:https://github.com/xuxueli/xxl-job/ 下载运行包,按照 Java SpringBoot 修改一些基本配置,项目启动即可。 diff --git "a/docs/md/project/lottery/Part-2/\347\254\25418\350\212\202\357\274\232\346\211\253\346\217\217\345\272\223\350\241\250\350\241\245\345\201\277\345\217\221\350\264\247\345\215\225MQ\346\266\210\346\201\257.md" "b/docs/md/project/lottery/Part-2/\347\254\25418\350\212\202\357\274\232\346\211\253\346\217\217\345\272\223\350\241\250\350\241\245\345\201\277\345\217\221\350\264\247\345\215\225MQ\346\266\210\346\201\257.md" index a5605a6c9..9a90c3fdc 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25418\350\212\202\357\274\232\346\211\253\346\217\217\345\272\223\350\241\250\350\241\245\345\201\277\345\217\221\350\264\247\345\215\225MQ\346\266\210\346\201\257.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25418\350\212\202\357\274\232\346\211\253\346\217\217\345\272\223\350\241\250\350\241\245\345\201\277\345\217\221\350\264\247\345\215\225MQ\346\266\210\346\201\257.md" @@ -13,6 +13,17 @@ pay: https://t.zsxq.com/fmuV7Mn - 分支:[211113_xfg_MQCompensateJob](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/211113_xfg_MQCompensateJobb) - 描述:分布式任务调度,扫描抽奖发货单消息状态,对于未发送MQ或者发送失败的MQ,进行补偿发送处理 +## 零、优秀作业 + +- [扫描库表补偿发货单MQ消息 @一点江南](https://t.zsxq.com/06vbai6qv) +- [扫描库表补偿发货单MQ消息 @BerserkD](https://t.zsxq.com/06fAIEE2R) +- [用xxl-job实现定时补偿发送mq消息 @Chin](https://t.zsxq.com/06rvFQnYN) +- [扫描库表补偿发货单MQ消息 @Geroge Liu](https://t.zsxq.com/06ju3JeuF) +- [扫描库表补偿发货单MQ消息 @liuc](https://t.zsxq.com/06RNNzFUN) +- [扫描库表补偿发货单MQ消息 @咖啡苦涩](https://t.zsxq.com/0c9bNriyF) +- [画一个Lottery的流程图,对整体业务加设计思想进行总结 @小曹不会emo](https://t.zsxq.com/0dJBkauPz) +- [定时任务扫描库表补偿发货单MQ消息,已经是一个完整的流程了 @F+Y](https://t.zsxq.com/107KSNr83) + ## 一、开发日志 - 因为需要扫描库表,也就是循环的方式把每个库下的多张表中的每条用户记录,都进行扫描。所以需要在分库分表组件中,提供出可以设置路由到的库和表,这样就可以满足我们扫描的动作了。*这部分大家可以看最新的 [db-router-spring-boot-starter](https://gitcode.net/KnowledgePlanet/db-router-spring-boot-starter)* diff --git "a/docs/md/project/lottery/Part-2/\347\254\25419\350\212\202\357\274\232\350\256\276\350\256\241\346\273\221\345\212\250\345\272\223\345\255\230\345\210\206\345\270\203\345\274\217\351\224\201\345\244\204\347\220\206\346\264\273\345\212\250\347\247\222\346\235\200.md" "b/docs/md/project/lottery/Part-2/\347\254\25419\350\212\202\357\274\232\350\256\276\350\256\241\346\273\221\345\212\250\345\272\223\345\255\230\345\210\206\345\270\203\345\274\217\351\224\201\345\244\204\347\220\206\346\264\273\345\212\250\347\247\222\346\235\200.md" index 6d9b16a16..628d8ae2e 100644 --- "a/docs/md/project/lottery/Part-2/\347\254\25419\350\212\202\357\274\232\350\256\276\350\256\241\346\273\221\345\212\250\345\272\223\345\255\230\345\210\206\345\270\203\345\274\217\351\224\201\345\244\204\347\220\206\346\264\273\345\212\250\347\247\222\346\235\200.md" +++ "b/docs/md/project/lottery/Part-2/\347\254\25419\350\212\202\357\274\232\350\256\276\350\256\241\346\273\221\345\212\250\345\272\223\345\255\230\345\210\206\345\270\203\345\274\217\351\224\201\345\244\204\347\220\206\346\264\273\345\212\250\347\247\222\346\235\200.md" @@ -13,6 +13,34 @@ pay: https://t.zsxq.com/fayvBIy - 分支:[211120_xfg_redis](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/211120_xfg_redis) - 描述:引入 Redis 到抽奖系统,设计颗粒度更细的滑动库存编号分布式锁,处理活动秒杀流程 +## 零、优秀作业 + +- [设计滑动库存分布式锁处理活动秒杀 @一点江南](https://t.zsxq.com/06qRNBQvz) +- [设计滑动库存分布式锁处理活动秒杀 @BerserkD](https://t.zsxq.com/062r3rRZb) +- [设计滑动库存分布式锁处理秒杀流程 @杨杨得亿🙉](https://t.zsxq.com/06nqJUbEu) +- [设计滑动库存分布式锁处理活动秒杀 @Geroge Liu](https://t.zsxq.com/06mMVjAUb) +- [弃用数据库行锁的方式删减活动库存,改用redis分布式锁,并用mq去删减库存。@Chin](https://t.zsxq.com/06u7yzFei) +- [Redis中的setnx作为分布式锁的使用 @liuc](https://t.zsxq.com/06a66YBII) +- [活动领域Redis分布式滑动库存锁优化 @微风](https://t.zsxq.com/07BUvbE6A) +- [流程图比较清晰,以抽奖流程编排接口为主线,每个功能点为支线;贯穿多个领域层 @xbhog](https://t.zsxq.com/09guPZDAm) +- [把整个抽奖流程 debug 走一遍,记录一下这些章节的核心思想 @爱奋斗的小鲨鱼](https://t.zsxq.com/0ap1a5wRn) +- [1~19 章节总结 目前将组件装在服务器上 后续结合公众号再继续开发 @爱奋斗的小鲨鱼](https://t.zsxq.com/0ao1zuOOY) +- [考虑某个活动如果在同一时间有大量用户参与 @咖啡苦涩](https://t.zsxq.com/0cDCMPrRA) +- [首先非常感谢小傅哥,该项目确实让我感觉学习到很多优秀的内容,以下是我学习的一个阶段性总结 @神经蛙](https://t.zsxq.com/0cIVeIzgI) +- [抽奖系统四层架构设计梳理 @灵幻新隆](https://t.zsxq.com/0ckOJyhdV) +- [滑动库存分布式锁处理活动秒杀流程梳理 @错否](https://t.zsxq.com/0es2kc7rW) +- [抽奖系统学习总结整理 @夜空的寂静](https://t.zsxq.com/0fVxp4LAQ) +- [使用docker部署了lottery,更新docker-compose部署的方式 @小曹不会emo](https://t.zsxq.com/0ficRjoy4) +- [这两天完成了第19节内容引入 Redis 到抽奖系统,设计颗粒度更细的滑动库存编号分布式锁,处理活动秒杀流程 @素质男孩](https://t.zsxq.com/11ixEXFK8) +- [系统功能流程图汇总 @CCAT](https://t.zsxq.com/11f8zv46p) +- [回顾整个抽奖系统和这些天考虑的这些系统性的知识 @王磊](https://t.zsxq.com/12rQz46Yd) +- [系统性整理一下这些天的知识积累,同时展望一下后续的知识补充 @王磊](https://t.zsxq.com/12OitppQW) +- [Lottery-api 抽奖网关集成白名单组件 @俗人](https://t.zsxq.com/12Qmo4Ylf) +- [Lottery-api 抽奖网关继承熔断降级组件 @俗人](https://t.zsxq.com/12NgV1EEW) +- [Lottery-api 抽奖网关集成限流组件 @俗人](https://t.zsxq.com/12plqgJS0) +- [Lottery压测 @俗人](https://t.zsxq.com/12ZxZMb4D) +- [Lottery-api 抽奖网关集成自定义方法拦截组件 @俗人](https://t.zsxq.com/139XngXkm) + ## 一、开发日志 - 由于本章节需要用到 Redis 所以我们在云服务器搭建 Redis 服务,这样可以更加方便的使用。*如果你暂时还没有云服务器,那么在本地搭建 Redis 也可以,只是少了一些云环境的配置练习* [云服务器地址](https://www.aliyun.com/minisite/goods?taskPkg=1111ydsrwb&pkgSid=11388&recordId=1033318&userCode=is4kfbdt) @@ -25,4 +53,4 @@ pay: https://t.zsxq.com/fayvBIy ![](https://gitcode.net/KnowledgePlanet/Lottery/-/raw/master/doc/assets/img/Part-2/19-01.png) - 优化活动领域,活动参与流程中的库存扣减操作,这部分我们原来是使用数据库行级锁🔐 处理的库存扣减,但因为会存在并发问题所以这里优化为 Redis 分布式锁进行处理。 -- 活动领取完成后,其实这个时候只是把缓存的库存扣掉了,但数据库中的库存并没有扣减,所以我们需要发送一个 MQ 消息,来对数据库中的库存进行处理。因为 MQ 可以消峰因此在降低 MQ 分片的情况下,消费效率有所下降,并不会对数据库造成压力,保证最终数据一致性即可。*但也有例外,所以我们提到可以使用定时任务来更新数据库库存* \ No newline at end of file +- 活动领取完成后,其实这个时候只是把缓存的库存扣掉了,但数据库中的库存并没有扣减,所以我们需要发送一个 MQ 消息,来对数据库中的库存进行处理。因为 MQ 可以消峰因此在降低 MQ 分片的情况下,消费效率有所下降,并不会对数据库造成压力,保证最终数据一致性即可。*但也有例外,所以我们提到可以使用定时任务来更新数据库库存* diff --git "a/docs/md/project/lottery/Part-3/\347\254\25401\350\212\202\357\274\232UI\345\267\245\347\250\213\346\220\255\345\273\272.md" "b/docs/md/project/lottery/Part-3/\347\254\25401\350\212\202\357\274\232UI\345\267\245\347\250\213\346\220\255\345\273\272.md" index 22c359ecd..f956ffc3b 100644 --- "a/docs/md/project/lottery/Part-3/\347\254\25401\350\212\202\357\274\232UI\345\267\245\347\250\213\346\220\255\345\273\272.md" +++ "b/docs/md/project/lottery/Part-3/\347\254\25401\350\212\202\357\274\232UI\345\267\245\347\250\213\346\220\255\345\273\272.md" @@ -10,6 +10,8 @@ pay: https://t.zsxq.com/NvfEuVJ >沉淀、分享、成长,让自己和他人都能有所收获! +- [运营后台 @一点江南](https://t.zsxq.com/06aayb6QZ) + ## 一、开发日志 - 在考虑运营后台搭建的时候,想着要哪套UI方案来解决。其实如果公司里有前端人员来开发后台运营页面,那么基本可能是使用 vue、react、angular,但如果没有这样的前端而是需要后端Java人员来开发,就需要考虑一个迭代和维护成本,因为并不是所有的后端研发都需要学习 vue 等前端开发语言的。 diff --git "a/docs/md/project/lottery/Part-4/\347\254\25401\350\212\202\357\274\232\346\220\255\345\273\272\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\347\275\221\345\205\263\346\234\215\345\212\241.md" "b/docs/md/project/lottery/Part-4/\347\254\25401\350\212\202\357\274\232\346\220\255\345\273\272\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\347\275\221\345\205\263\346\234\215\345\212\241.md" index 406a2a1be..92e1d19f7 100644 --- "a/docs/md/project/lottery/Part-4/\347\254\25401\350\212\202\357\274\232\346\220\255\345\273\272\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\347\275\221\345\205\263\346\234\215\345\212\241.md" +++ "b/docs/md/project/lottery/Part-4/\347\254\25401\350\212\202\357\274\232\346\220\255\345\273\272\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\347\275\221\345\205\263\346\234\215\345\212\241.md" @@ -13,6 +13,10 @@ pay: https://t.zsxq.com/uVfAYJm - 网关:[Lottery-API](https://gitcode.net/KnowledgePlanet/Lottery-API) - 描述:创建抽奖系统网关工程,把公众号开发接口写到这个系统中,提供可以在公众号发起抽奖的处理。 +## 零、优秀作业 + +- [听取小傅哥的建议 又捣鼓了一下微信公众号](https://t.zsxq.com/11EeuhM1p) + ## 一、开发日志 - 创建 Lottery-API 网关工程,引入公众号开发的验签 SDK 服务。*因为这里涉及了一些公众号开发的处理,如果有些看不懂,可以参考公众号的开发文档* diff --git "a/docs/md/project/lottery/Part-4/\347\254\25402\350\212\202\357\274\232vue H5 \345\244\247\350\275\254\347\233\230\346\212\275\345\245\226.md" "b/docs/md/project/lottery/Part-4/\347\254\25402\350\212\202\357\274\232vue H5 \345\244\247\350\275\254\347\233\230\346\212\275\345\245\226.md" index db2e48123..a167de38f 100644 --- "a/docs/md/project/lottery/Part-4/\347\254\25402\350\212\202\357\274\232vue H5 \345\244\247\350\275\254\347\233\230\346\212\275\345\245\226.md" +++ "b/docs/md/project/lottery/Part-4/\347\254\25402\350\212\202\357\274\232vue H5 \345\244\247\350\275\254\347\233\230\346\212\275\345\245\226.md" @@ -13,6 +13,10 @@ pay: https://t.zsxq.com/EuN76UV - VUE:[lottery-front](https://gitcode.net/KnowledgePlanet/lottery-front) - 描述:本章节是一个作业章节,我会带着你基于vue搭建初始工程,加入抽奖模块以及一个案例接口。之后需要你在这个基础上完善整个抽奖流程的调用和展示,这样你就可以把整个抽奖过程串联起来了。 +## 零、优秀作业 + +- [vue H5 大转盘抽奖 @俗人](https://t.zsxq.com/12qKmiLWT) + ## 一、开发日志 - 基于 IDEA 创建 vue 工程,并加入新的抽检模块 diff --git "a/docs/md/project/lottery/Part-5/\347\254\25401\350\212\202\357\274\232\345\234\250\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262 Docker.md" "b/docs/md/project/lottery/Part-5/\347\254\25401\350\212\202\357\274\232\345\234\250\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262 Docker.md" index ae6308d58..1c391866c 100644 --- "a/docs/md/project/lottery/Part-5/\347\254\25401\350\212\202\357\274\232\345\234\250\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262 Docker.md" +++ "b/docs/md/project/lottery/Part-5/\347\254\25401\350\212\202\357\274\232\345\234\250\344\272\221\346\234\215\345\212\241\345\231\250\351\203\250\347\275\262 Docker.md" @@ -1,6 +1,6 @@ --- title: 第01节:在云服务器部署 Docker -pay: https://t.zsxq.com/jUbmeE2 +pay: https://t.zsxq.com/08c55XltC --- # 第01节:在云服务器部署 Docker @@ -15,6 +15,28 @@ pay: https://t.zsxq.com/jUbmeE2 - 在云服务器上部署 Docker,并安装 Portainer 运维面板以及汉化。汉化包已放到 [Lotter/doc/asserts/Portainer-CN](https://gitcode.net/KnowledgePlanet/Lottery/-/tree/master/doc/assets/Portainer-CN) - 服务器系统 CentOS 8.x、Docker 20.10.11 - 如果你的云服务器已经安装其他系统,可以停机后更换系统即可,其实这个时候你还可以选择 Docker 镜像,也就是默认帮你安装好了 Docker +- 报错配置 `error response from daemon: Get "https://registry-1.docker.io/v2/` + +```java +{ + "builder": { + "gc": { + "defaultKeepStorage": "20GB", + "enabled": true + } + }, + "experimental": false, + "features": { + "buildkit": true + }, + "registry-mirrors": [ + "https://0dj0t5fb.mirror.aliyuncs.com", + "https://docker.mirrors.ustc.edu.cn", + "https://6kx4zyno.mirror.aliyuncs.com", + "https://registry.docker-cn.com" + ] +} +``` ## 二、手动安装 Docker @@ -30,4 +52,4 @@ Docker 是一个开源的应用容器引擎,让开发者可以打包他们的 ``` - `uname -r` -- x86 64位系统,如果是32位是不能安装 docker 的 \ No newline at end of file +- x86 64位系统,如果是32位是不能安装 docker 的 diff --git "a/docs/md/project/lottery/Part-5/\347\254\25406\350\212\202\357\274\232\351\203\250\347\275\262\347\216\257\345\242\203 nacos.md" "b/docs/md/project/lottery/Part-5/\347\254\25406\350\212\202\357\274\232\351\203\250\347\275\262\347\216\257\345\242\203 nacos.md" index 28556f74a..190a342f3 100644 --- "a/docs/md/project/lottery/Part-5/\347\254\25406\350\212\202\357\274\232\351\203\250\347\275\262\347\216\257\345\242\203 nacos.md" +++ "b/docs/md/project/lottery/Part-5/\347\254\25406\350\212\202\357\274\232\351\203\250\347\275\262\347\216\257\345\242\203 nacos.md" @@ -1,6 +1,6 @@ --- title: 第06节:部署环境 nacos -pay: https://t.zsxq.com/zbUVR7E +pay: https://t.zsxq.com/08kipfRje --- # 第06节:部署环境 nacos diff --git "a/docs/md/project/lottery/introduce/Lottery\346\212\275\345\245\226\347\263\273\347\273\237.md" "b/docs/md/project/lottery/introduce/Lottery\346\212\275\345\245\226\347\263\273\347\273\237.md" index a91198172..ad0f9d7ba 100644 --- "a/docs/md/project/lottery/introduce/Lottery\346\212\275\345\245\226\347\263\273\347\273\237.md" +++ "b/docs/md/project/lottery/introduce/Lottery\346\212\275\345\245\226\347\263\273\347\273\237.md" @@ -41,6 +41,13 @@ title: Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践 So!基于DDD领域驱动设计的四层架构**抽奖系统**,开始啦!有座,这趟车的你跟上! +| 抽奖系统是营销场景重要的微服务之一 | +| ---- | +| ![图 1-0](https://bugstack.cn/images/article/project/lottery/Part-1/1-00.png) | + +1. 营销,是一个非常庞大的系统体系。包括众多的模块组成,其中抽奖只是一个重要的微服务之一。 +2. 包括,营销平台、返利平台、积分账户、抽奖系统、券系统、灌券系统、售卖系统,以及各类玩法的组件系统。它们的关系如图所示。 + ## 二、呀,能学东西! ![图 1-2](/images/article/project/lottery/Part-2/1-02.png) diff --git a/docs/md/project/lottery/notes.md b/docs/md/project/lottery/notes.md new file mode 100644 index 000000000..a9d1c77f8 --- /dev/null +++ b/docs/md/project/lottery/notes.md @@ -0,0 +1,247 @@ +--- +title: 面试:技能、简历、问题汇总 +lock: no +--- + +# 《Lottery 分布式抽奖系统》,关于面试中的技能、简历、问题汇总 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/0d7K7hJ0i](https://t.zsxq.com/0d7K7hJ0i) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +此部分主要用于向读者提供星球项目之一的 Lottery 分布式抽奖系统如何体现到简历中,包括;专业技能、项目经验。 + +## 一、项目介绍 + +面试官您好,Lottery 是我的一个(学习/工作)项目,此项目不只是一个简单单一的抽奖,而是符合营销平台架构设计具备可扩展性的微服务架构设计。核心流程为根据不同人群标签的人群规律,选择不同的抽奖活动,每个活动的参与为一个抽奖单。可以有效的控制参与用户数和异常流程的补偿。领取抽奖单后执行使用了模板、工厂、策略的抽奖玩法设计。在这里设计了分段锁,避免独占锁的竞争,从而挺高效率。最后抽奖完成异步发送 MQ 消息方式进行驱动后续的发奖流程。 + +Lottery 系统的全方面技术栈的使用,多场景的问题的解决方案,让我在这个过程中学习到非常多的内容,这写技术学习的内容,也可以更好的应对以后的开发工作。非常感谢您给我这次面试机会。 + +## 二、简历模板 + +- **项目名称**:营销活动平台 - Lottery 微服务抽奖系统 +- **系统架构**:以 DDD 领域驱动设计开发,微服务拆分的分布式系统架构 +- **核心技术**:SpringBoot、MyBatis、Dubbo、MQ、MySQL、XDB-Router、ES、ZK +- **项目描述**:抽奖系统是营销平台的重要微服务之一,可以满足 C 端人群的需求,例如拉新、促活、留存等。该系统运用抽象、分治和 DDD 知识,拆解服务边界,凝练领域服务功能。围绕抽奖服务建设领域服务,包括规则引擎、抽奖策略、活动玩法、奖品发放等。这可以满足业务产品快速迭代上线的需求,同时减少研发成本,提高交付效率。 +- **核心职责**: + - 【高级】构建以 DDD 分层结构的处理方式,搭建整个抽奖系统架构。运用设计原则和工厂、代理、模板、组合、策略等设计模式的综合使用,搭建易于维护和迭代的系统工程。 + - 【高级】鉴于系统内有较多的规则策略过滤,包括准入、人群、风控、A/BTest等需求,为适应系统规模可快速开发和使用的方式,搭建了去中心化的量化人群规则引擎组件。通过业务需求对逻辑的扩展和内置引擎执行器的使用,完成自由组合的人群过滤服务。这降低了共性功能重复开发所带来的成本问题,并提高了研发效率。 + - 【高级】根据实际秒杀峰值场景 `TPS 5000 ~ 8000` 的需求,开发了统一路由组件。该组件不仅可以满足差异化不同字段的分库分表组合,还支持 Redis 库存分片和秒杀滑动库存分块。而且,开发了统一路由 XDB-Router 的 SpringBoot Starter 技术组件。该套组件已经经历了多次大促活动场景的考验,支持横向扩展,可以满足业务规模的快速增长。 + - 【简单】运用模板、策略、工厂三个设计模式,定义抽奖过程标准和实现对应的多类型抽奖的服务模块。 + - 【简单】因活动秒杀的并发场景,将秒杀从最开始的数据库行级锁优化为Redis Key 加锁,又从 Redis Key 的独占锁,优化为滑块锁。优化后整体秒杀有了非常可观的性能提升。 + - 【简单】解耦抽奖流程,把抽奖和发奖用MQ消息串联起来,避免一个流程太长,导致用户一直等待。 + +## 三、项目问答 + +### 1. 营销架构介绍 + +鉴于有些伙伴在面试时候,不知道怎么叙述抽奖这个微服务所在的位置,特此补充图 + +
+ +
+ +1. 营销,是一个非常庞大的系统体系。包括众多的模块组成,其中抽奖只是一个重要的微服务之一。 +2. 包括,营销平台、返利平台、积分账户、抽奖系统、券系统、灌券系统、售卖系统,以及各类玩法的组件系统。它们的关系如图所示。 + +### 2. 为什么自研路由组件 + +如果面试问:“为什么要自研,市面不是有吗,怎么回答?” 可以从以下3个点解答; +1. 维护性;市面的路由组件比如 shardingsphere 但过于庞大,还需要随着版本做一些升级。而我们需要更少的维护成本。 +2. 扩展性;结合自身的业务需求,我们的路由组件可以分库分表、自定义路由协议,扫描指定库表数据等各类方式。研发扩展性好,简单易用。 +3. 安全性;自研的组件更好的控制了安全问题,不会因为一些额外引入的jar包,造成安全风险。 + +当然,我们的组件主要是为了更好的适应目前系统的诉求,所以使用自研的方式处理。就像shardingsphere 的市场占有率也不是 100% 那么肯定还有很多公司在自研,甚至各个大厂也都自研一整套分布式服务,来让自己的系统更稳定的运行。分库分表基本是单表200万,才分。那么面试怎么说; + +你们为什么分库分表? + +1. 我们分库分表用的非常熟。但不能为了等到系统到了200万数据,才拆。那么工作量会非常大 +2. 我们的做法是,因为有成熟方案,所以前期就分库分表了。但,为了解释服务器空间。所以把分库分表的库,用服务器虚拟出来机器安装。这样即不过多的占用服务器资源,也方便后续数据量真的上来了,好拆分。 +3. 同时,抽奖系统,是瞬时峰值较高的系统,历史数据不一定多。所以我们希望,用户可以快速的检索到个人数据,做最优响应。因为大家都知道,抽奖这东西,push发完,基本就1~3分钟结束,10分钟人都没了。所以我们这也是做了分库分表的理由。 + +### 3. 规则引擎的设计目的 + +1. 面试官您好。关于规则引擎这块,首先;这是一个基于降低重复编码和提高可维护性的,并需要符合当前项目诉求的,同时不过渡的设计和减少运维成本的前提下,在技术调研后所做的微型规则引擎设计实现。 +2. 此规则引擎的主要作用是解决,抽奖业务场景中对个性化运营诉求的处理,如;人群身份标签、交易记录、活动资格、授信状态等规则的可配置化的交叉使用。 +3. 所以基于这样的情况,此规则引擎的设计是一个二叉树判断,实现手段运用到了组合模式、工厂模式等。并为了便于维护和使用,进行了库表对二叉树的抽象设计,树根、节点、子叶,映射为二叉树编码的相关属性信息。同时,也可以基于这样的库表做前端页面的托拉拽配置操作,降低运营成本。 +4. 最后,其实动态的规则引擎配置,其实放大了看就是 BPMN + Drools + Groovy,的一个低代码实现框架。综上,面试官这个就是我在做规则引擎设计的一些思考、调研和落地。如果以后咱公司有其他更大的场景,我也可以扩展为 Reta 算法和低代码方案进行架构落地实现。 + +### 4. 秒杀的滑块锁讲解 + +1. 是针对于用户参与的活动库存加锁的,如果是独占锁是针对于活动ID加锁的。 +2. 滑块锁的核心是去竞态,避免独占影响系统的响应性能。关于此类锁,这里又做了视频做了详细的讲解;[Redis | bugstack 虫洞栈](https://bugstack.cn/md/road-map/redis.html) - 如图。别看提秒杀一堆人说,但如果还讲用独占锁做活动的秒杀场景,就没做过大规模的秒杀。独占是会很大概率出事故的。 +3. 那为什么加一个锁呢,incr 不就可以。加锁是兜底,你不知道什么时候会出现 incr 不对的情况。如;集群配置问题【特例】、出现redis问题,需要恢复库存。如果没有锁,可能会超卖。[https://t.zsxq.com/12sNS4E2J](https://t.zsxq.com/12sNS4E2J) - 第一条评论加了说明。 +4. 对于非交易的活动类场景,要的就是一个快。快速响应、快速释放,可接受容错失败概率。但不要磨磨唧唧影响我的主核心交易链路。但凡在618、双11,营销敢超时,就直接下掉。保证用户可下单可支付。否则这黄金时间点,你耽误1分钟都是几个亿的成交额。所以,这类营销秒杀场景下,根本就是保证不超卖,也不恢复库存。 + +注意:独占锁是加给个人流程的 - 无资源竞争,如贷款单受理。分段/滑块/无锁化,是加给库存的 - 有资源竞争,如秒杀、商品发货等集中资源类。就跟大超时的收银台一样。原来就1个出口,后来一排出口,在后来又有无人化的电子出口。点点那个软件。 + +举例;incr 的速度很快,就像进入了公共的卫生间🚾。一个坑一个门,谁进去谁就锁上。没有就跑到下一个门。你说你不锁门吧,也没问题。但别人不知道,一拽开就比较尴尬。所以要加锁,锁门。 + +## 四、面试刷题 + +- [关于抽奖系统怎么写到简历里](https://t.zsxq.com/07yKZJFkR) +- [抽奖系统,面试被问到表的设计,要怎么答?](https://t.zsxq.com/04EYNRF6m) +- [抽奖系统一般部署多少实例比较合适,系统大概能抗住多大的流量?](https://t.zsxq.com/04zNzZVFa) +- [抽奖系统为什么自研路由组件?](https://t.zsxq.com/04Am6mIqR) +- [抽奖项目分布式事务如何解决?](https://t.zsxq.com/04yzF27UB) +- [抽奖系统 TPS 5000~8000 服务器配置大概情况](https://t.zsxq.com/04fA6meyB) +- [抽奖系统mq重发的时候是怎么保证幂等性](https://t.zsxq.com/04ZrJYBy7) +- [面试时抽奖系统被问到类似qps、tps这些指标如何回答?](https://t.zsxq.com/04eqV7YNf) +- [问分库分表,别给我回答分2个库,每个库4个表,那你分那个毛用,一看就是假的](https://t.zsxq.com/04y3FAmq3) +- [想问问大家在面试的时候自我介绍,是怎么介绍抽奖项目的,个人项目还是公司项目?](https://t.zsxq.com/04BMN7myv) +- [说一下抽奖系统拓扑结构](https://t.zsxq.com/043BiQje6) +- [聚合和聚合根在抽奖系统的体现?](https://t.zsxq.com/04jyRjA6I) +- [抽奖系统DDD和MVC的区别](https://t.zsxq.com/04EAameYz) +- [描述你遇到的一个技术问题-抽奖事故及优化](https://t.zsxq.com/04EuvJe6U) +- [为什么用户领取活动完毕,发送MQ去更新数据库中的库存](https://t.zsxq.com/04vjAq3RR) +- [量化规则引擎是一个组件,如果有一个新的业务进来,如何复用? 它的复用性体现在哪?能否支持风控可A/Btest需求?](https://t.zsxq.com/05zRvbUJ2) +- [抽奖系统自己写成MVC架构怎么讲?](https://t.zsxq.com/05iQN7AU3) +- [抽奖流程阐述](https://t.zsxq.com/053RVFeuZ) +- [抽奖系统配置相关](https://t.zsxq.com/05MJyZ7Yf) +- [Lottery项目包装为实验室项目,面试时被问对接的是什么甲方,为什么数据量会这么大](https://t.zsxq.com/05Nr3rjUf) +- [针对于抽奖系统,面试被问到表的设计,要怎么答?](https://t.zsxq.com/05EYNRF6m) +- [这个项目的业务数据,例如用户量、活跃用户量,以及核心业务数据,例如订单系统的下单量等](https://t.zsxq.com/05UJUFaur) +- [技术和业务19道面试题汇总](https://t.zsxq.com/05e662Vb2) +- [抽奖和发奖关于库存的扣减,防超发漏发,监控和弥补有没有设计思路和流程图之类的,添加库存,扣减库存的操作日志怎么设计](https://t.zsxq.com/05YVjEYny) +- [路由散列算法:idx / tbCount + 1 & idx - tbCount * (dbIdx - 1)](https://t.zsxq.com/05urRRzBi) +- [Lottery项目的抽奖算法,抽奖概率是100万怎么处理?](https://t.zsxq.com/05qFun6Uj) +- [Lottery项目的抽奖算法,抽奖概率是100万的另外一种双色球设计?](https://t.zsxq.com/053ZrZZFU) +- [抽奖码,抽奖策略;类似这种抽奖活动,假定这个抽奖码是随机的6位数,也就是有1-999999这么多的抽签码,用户每次获取都是随机的抽奖码](https://t.zsxq.com/06buB2niu) +- [Lottery向内部应用提供了RPC接口,那么当h5端需要提供一个http的请求,这个接口应该写在哪里呢](https://t.zsxq.com/05UrVfmQB) +- [多种抽象策略是怎么注入使用的 | 手动、ist、注解、Map?](https://t.zsxq.com/05yfAiMji) +- [如果redis作为分布式锁的时候,主节点挂掉了,但是数据还没有同步到从节点,这种情况怎么办?](https://t.zsxq.com/05MjE2f6a) +- [活动库存与奖品库存的配置关系](https://t.zsxq.com/05nQBiUZN) +- [抽奖系统用到了分库分表,那目前市面上还有那些成熟的规则引擎组件?](https://t.zsxq.com/05rVNfurN) +- [抽奖策略&斐波那契散列&活动库存&任务扫描时效性&并发编程场景](https://t.zsxq.com/05rbM7IYr) +- [关于抽奖算法自己整理的一些面试题和回答:采用了什么设计模式、实现了什么抽奖算法](https://t.zsxq.com/05YFQ3byr) +- [我把抽奖系统包装到实习经历里了。今天面试的那个部门是做广告架构的,可能有广告投放的场景,所以对我简历上的规则引擎量化人群参与活动模块比较感兴趣](https://t.zsxq.com/05QVrnE6I) + - 问:抽奖的奖品是优惠券,那金额多大呢?候选集有多大?多少人参与? + - 问:你说你用的决策树,那决策树是什么时候创建的呢?是每个用户参与抽奖就创建一次(个性化),还是一开始就创建好? + - 问:筛选的标签是什么,根据什么来过滤呢? + - 问:那有没有可能你制定的这些标签,数据传进来的时候是丢失的,用户没有某个路径上的数据,是不是就到不了叶子节点了,按你说的就没法领取活动了? + - 问:既然你的这些规则都是确定的,为什么要用决策树?决策树和布尔检索有什么区别知道吗? +- [抽奖系统包装到自己的电商业务中,描述案例](https://t.zsxq.com/05RniU7un) +- [抽奖系统的领域事件有哪些?是如何实现的?](https://t.zsxq.com/05A6qfiun) +- [结合美团DDD抽奖进行扩展](https://t.zsxq.com/05f2zVRfq) +- [想问一下抽奖系统中,各个领域是按照什么划分的?有什么规则或标准么?](https://t.zsxq.com/06MRnYBaI) +- [现在的抽奖系统是可能存在活动刚上线,所有的奖品就被抽完了,问如何避免这种情况发生?](https://t.zsxq.com/06NZNzvrv) +- [面试的时候问到了,抽奖项目里边 接口间的幂等性是如何保证的?](https://t.zsxq.com/06mUvjA2N) +- [像抽奖系统这种ddd架构的项目一般是怎么开发的?开发周期是多久?](https://t.zsxq.com/067aqrFqF) +- [规则条件,跟需要过滤的数据不在一张表中,那实际业务中该怎么根据规则引擎去筛选符合条件的数据?](https://t.zsxq.com/07E6YzZf6) +- [面试问题汇总,问的比较全面。](https://t.zsxq.com/07uzvnurR) + - 抽奖系统设计到了哪些设计模式 + - 为什么不使用自增ID + - Redis滑动库存分布式锁是如何实现的 + - 分布式锁有哪些实现方式?为什么使用Redis实现分布式锁 + - Redis 是单机的吗,如果单机的话 Redis 分布式锁有没有什么问题 + - 保证活动库存最终一致性 + - 为什么使用RocketMQ,而不考虑kafka等其他 MQ 组件呢 + - 项目中哪些地方使用了MQ + - 说说MQ解耦发奖流程,为什么要解耦 + - MQ 解耦发奖后的具体流程 + - 消息丢失怎么办 + - 为什么选择xxl-job执行定时任务,了解其他任务调度组件吗 + - 筛选的标签是什么,根据什么来过滤 + - 如果用户没有定制的标签,数据传进来是丢失的,用户没有某个路径上的数据,是不是无法到达叶子节点,就无法领取活动 + - 规则树是每个用户都有自己的规则树吗 + - 决策树能否复用 + - 抽奖算法是怎么设计的 + - DDD的优势 + - 谈谈整个抽奖系统的总体流程 + - 整个系统的吞吐量,QPS,等相关参数 + - 有遇到过线上问题吗?怎么解决的 +- [为什么在总体概率算法 DefaultRateRandomDrawAlgorithm 这个类中,要使用SecureRandom这个类生成随机数?](https://t.zsxq.com/07Mv3nQBE) +- [抽奖系统秒杀库存分片](https://t.zsxq.com/07qrZjmIu) +- [在抽奖系统中,前端访问的接口是写在网关中的,但是网关一般负责的是一些公共操作,例如鉴权,限流,给白名单等等,写在这里合适吗?](https://t.zsxq.com/07jqm9sJW) +- [为什么使用DDD,主要用于解决什么问题?抽奖系统中有哪些聚合?](https://t.zsxq.com/08QtwM6N5) +- [你的抽奖系统中,包的命名是怎样的?res和vo有什么区别,为什么想着这么分包呢?有什么语义和使用上的区别吗](https://t.zsxq.com/086oIHSzT) +- [抽奖系统,我想在简历上体现微服务,但是,熔断,降级,限流有好几个组件可以同时实现一个功能,我想问一下,该怎么组织这些组件 @面条](https://t.zsxq.com/08HdrYmK3) +- [抽奖系统中 db-router 定义了一个切面又定义了 MyBatis 拦截器,这两个东西有前后执行顺序吗?他们之间的关系是什么呢?](https://t.zsxq.com/08GUhI1kE) +- [抽奖系统的核心域,支撑域和通用域分别对应哪些呢?](https://t.zsxq.com/09z8st5wJ) +- [近期关于抽奖系统面试题汇总](https://t.zsxq.com/0anzjonAp) + 1. 你们项目中的微服务是如何划分的?是只有一个抽奖的微服务吗? + 2. 项目中的分布式是如何理解的? + 3. 项目中的接口幂等是如何实现的? + 4. 为什么要使用到ES? + 5. 项目中用到了哪些Redis数据结构? + 6. 项目会出现Redis缓存穿透击穿的情况吗?是如何解决的? + - [关于抽奖系统,活动配置黑名单,白名单,在获得抽奖次数前看是否存在黑名单,在黑名单用户不能获得次数。对应业务应该怎么逻辑是怎样的](https://t.zsxq.com/0aJ9iPHpr) + - [抽奖系统面试问到2个问题,不知道怎么回答好,傅哥帮我想想话术【把抽奖项目重新做一边,有哪些方面可以做的更好】【项目遇到最大的挑战是什么】](https://t.zsxq.com/0awf3MMkl) + - [抽奖遇到的一些面试题](https://t.zsxq.com/0awVJX07F) + 1. 抽奖系统中遇到什么困难? + 2. 抽奖系统TPS要求5000-8000,用什么技术实现? + 3. 抽奖系统日活多少? + 4. 抽奖系统和其他部门的交互是怎么做的? + 5. 抽奖系统涉及到的路由组件? + 6. 抽奖系统负载均衡怎么做的? +- [面试问我: 这个营销系统是之前就有,你去接手了。还是你从0到1构建的](https://t.zsxq.com/0aFaNx8Cd) +- [今天被一个技术专家问麻了,付哥快出来对波线(下面的记录我听录音回放的)](https://t.zsxq.com/0atvavVVP) + 1. 1500 QPS指的是单接口的嘛?还是怎么来的?我回答单接口。 + 2. 你的抽奖服务有多少台应用机器?我回答4台。 + 3. 既然你1500 QPS,那你是RT是多少? 我回答100ms。 + 4. 接上面那个问题,面试官说那我就不太理解了,你的RT既然是100ms,那峰值是如何支撑到3000 QPS的,因为你只有4台机器啊?(接我的回答 2) + 5. 峰值 3000 QPS 持续 半分钟的话,你 4台机器 100ms 是完全支撑不了的,你可能得去看看你 3000 QPS怎么压出来的。 + 6. 我看你写了DDD的分层架构,那讲下每个领域的实体。(这个我蒙了) + 7. 有哪些表你设置了唯一键?(我一开始以为他说的主键,我给他讲了UUID,他不是很满意) + 8. 既然你是做核心接口的,那肯定其他下游会依赖你的服务吧,那你是怎么解决你和其他服务之间的 分布式事务的? + 9. 你能给我讲讲为什么用kafka吗? 我理解的是kafka业务方面其实很少用,主要在大数据方面,存在消息丢失的场景,kafka丢消息的话 不会造成客诉吗? +- [二面更偏向业务多点,帮我看下怎么回答好,圆润圆润话术](https://t.zsxq.com/0aUPkgzge) + 1. 你们这个项目的人员配备是什么样子的?你是属于核心成员吗? + 2. 你负责的是核心模块,其他系统的模块依赖于你吗?那你是怎么保证你的服务出错,不会影响到别人的服务的呢? + 3. DDD每个领域内有哪些实体,以及实体的职能是做什么的? + 4. 压测的数据你是怎么来的,用了什么工具去压测?压测的机子配置是怎么样的? + 5. 你整个营销系统里还有哪些模板,能跟我讲讲其他模块嘛? + 6. 如果跨部门的项目,你和其他团队的成员意见不一致怎么办? + 7. 讲一讲项目中最能够体现你owner意识的几件事情。 + 8. 讲一讲你入职到现在哪些方面取得成长了,最好能有事例论证。 +- [抽奖后台配置了用户可以获得抽奖次数限制,每天最多3次,每周最多18次,每个月最多70次。如何设计实现。](https://t.zsxq.com/0bUpPAPCX) +- [面试官问了我抽奖系统中MQ的丢失风险有多大](https://t.zsxq.com/0b9LylPxO) +- [面试被问到抽奖项目的问题](https://t.zsxq.com/0bDjq99jG) + 1. 你的DDD是怎么分层的? + 2. 核心域的主要职责有哪些? + 3. 核心域这边有哪些实体,哪些聚合? + 4. 领域层有一个原则,就是尽量保证领域层的一个纯粹,那你如何保证领域层的一个纯粹性? + 5. 防腐层的职责是什么? + 6. 你扣库存的时候,如果库存在缓存扣成功了,但是消息队列没发送成功怎么办? + 7. 你依赖于缓存,那假设Redis全down机了怎么办(虽然概率比较小),虽然有故障恢复,但是故障恢复有可能会数据丢失? +- [Lottery抽奖系统被面试官问穿了...结合项目问了很多场景题:](https://t.zsxq.com/0cnuKOBTQ) + - Q1:规则引擎中,如果有两个同类型的节点怎么办?比如说规则树中有两个节点代表的同一类型的条件,比如考虑一个年龄大于10,一个岁数大于25,位于不同的规则树节点中,在高并发的情况下,通过rpc调用接口,对于用户的年龄数据查询会查两遍,但是实际上查一次就可以,但是调用端相当于耗费了两次的查询,应该怎么解决?(这个问题我听蒙了 + - Q2:如果是千万级的数据,应该会需要怎么优化?可以从数据存储和查询优化的角度? + - Q3:你们的分库分表是怎么做的?(这里回答了基于用户ID分表)在分库分表的场景下,如果想要查询当前这个活动所有的抽奖结果的明细应该怎么去查询呢? + - Q4:怎么保证消息队列的消息不重复消费?或者说是一定被消费了? + - Q5:如果保证系统的幂等性?有没有考虑同一个用户如果抽了两次奖的情况?(回答会做一个重复性的校验) + - Q6:如果前端的用户非常快地点击了两次,也就是说查询这条数据的时候同时都是空的,这种情况下怎么保证一致性? +- [面试官问我,抽奖项目里面的redis减库存,redis如果发生丢失现象,比如一条命令还在路上的时候丢失了,那会不会导致库存超发现象呢?](https://t.zsxq.com/0cOV9vOr0) +- [从无到有进行领域驱动设计,是怎样的一个流程?](https://t.zsxq.com/0cLTx0t9E) +- [对于你的滑动库存锁,假设有1000个库存,有几个或者十几个线程因为某种原因抢到了锁,但是后续流程失败了,也就是说1000个库存还剩下了几个或几十个参与名额,这个时候你怎么去保证所有的库存都能消耗完呢?](https://t.zsxq.com/0dKA27XNz) +- [关于数据库路由组件这个小项目,在面试中,可能被问到的问题大概有什么?可以给个准备方向吗?](https://t.zsxq.com/0e09EDxHu) +- [在使用redis的incr来扣减库存,如果redis宕机了,丢失了一部分数据,是不是就有可能incr得到重复的key,这个时候相应的key滑块锁如果恰好又已经被释放了,是不是就超买了,这时候怎么解决呢?](https://t.zsxq.com/0ekXNK2Qq) +- [Lottery的异常情况有哪些](https://t.zsxq.com/0fxyr6Mb4) +- [有没有考虑到分库分表对运维和后续开发难度造成的影响,这个是怎么考量了,后续会有什么麻烦](https://t.zsxq.com/0fBVqDbtl) +- [请问下大家在面试的时候,如果抽奖系统是1500qps,接口响应300ms,然后每秒的并发量是450个,然后面试官问参与人数的时候怎么回答呢?](https://t.zsxq.com/107LwTD9I) +- [关于抽奖系统的负载均衡和集群问题还有些疑问,假设QPS是50000的话,那服务的应用层和网关层要部署多少个实例,这些实例是都部署在不同的物理机上,还是一台物理机可以有多个实例?还有redis和kafka的话要配置多少实例?](https://t.zsxq.com/10ibGRmAK) +- [MQ有没有做持久化?redis做分布式锁,如果这个过程中redis挂了,怎么处理?分布式事务是怎么实现的?](https://t.zsxq.com/10jP1UY42) +- [抽奖系统 MySql 连接数配置了多少,如何查看和修改](https://t.zsxq.com/10ciJnGDj) +- [项目里面一般什么时候会用到线程池哇,你这个抽奖项目里面可以用线程池吗](https://t.zsxq.com/10XnBD1w8) +- [每次领取活动都要查看是否有未消费的活动单,这个操作要访问db,如果大量请求的话是不是会击垮db?](https://t.zsxq.com/10G4nY9UP) +- [抽奖系统,被问到系统的瓶颈在哪里](https://t.zsxq.com/10WwhYDKy) +- [为什么要选redis,redis 主从集群下潜在的锁失效问题怎么考虑怎么解决。ookeeper 作为分布式锁优缺点](https://t.zsxq.com/107LFRcAH) +- [这个抽奖系统里的聚合、聚合根、实体是具体对应的哪些部分](https://t.zsxq.com/11VOoFhHX) +- [关于db-router的redis路由问题:假设并发量还是很高,那么把库存分摊到不同的stockKey上,不同用户去不同的key上incr。但是这和自研路由有什么关系](https://t.zsxq.com/11j6zWN6k) +- [项目中为什么用dubbo不用fegin,这个问题怎么答](https://t.zsxq.com/115w0DAUw) +- [昨天字节二面被疯狂拷打项目,特来请教一下相关问题](https://t.zsxq.com/11qoK5xep) + 1. 除了使用mq解耦发奖流程外,有没有别的解决方案?(除了使用mq外有没有其他方式解决用户一直等待的问题?或者有没有比mq更优的方式?) + 2. 如果这个项目真正应用于实际中还有哪些地方需要改进? + 3. 如果让你来评估项目的QPS的话,你会用什么方式来评估?(补充:不要做压测,就通过现在的设计以及硬件配置推导QPS应该达到什么水准?) + 4. 比如说:16核64G的机器,普通机械硬盘,这种情况下让你来做秒杀的系统,你会去修改和配置哪些参数?(不考虑redis、kafka等,只考虑springboot的应用) + 我答了要考虑线程数、jvm堆大小,面试官问我具体数值,我答不上来 + 5. JVM堆配置过大的副作用有哪些? + 6. 接上面,SpringBoot和JVM需要配置的参数还有哪些? + 7. 秒杀场景下用哪种垃圾回收器合适? + 8. Full GC卡顿时间长短跟什么有关系?如果堆大小为128G的话,Full GC可能停顿多久? + 9. 写代码时候有没有什么方式尽量减少Full GC的概率? +- [面试官问我100个用户,只有1个库存,怎么控制并发访问?](https://t.zsxq.com/12aAYyPQC) +- [面试被拷打了规则引擎, 感觉这块的内容有点抽象, 问我怎么实现的](https://t.zsxq.com/120qjagAj) +- [我最近几次面试抽奖项目都被问到了redis加锁的滑块锁是针对用户还是针对活动](https://t.zsxq.com/12pc2umy0) diff --git a/docs/md/project/lucky-tackout/lucky-tackout.md b/docs/md/project/lucky-tackout/lucky-tackout.md new file mode 100755 index 000000000..5c61f9429 --- /dev/null +++ b/docs/md/project/lucky-tackout/lucky-tackout.md @@ -0,0 +1,157 @@ +--- +title: 吉祥外卖系统 PLUS 版 +lock: no +--- + +# 吉祥外卖系统 PLUS 版 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
项目:[https://t.zsxq.com/g57ja](https://t.zsxq.com/g57ja) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**是的,我干了一套吉祥外卖系统 PLUS 版本!** + +临近秋招,小傅哥又开始为非常多的小伙伴提供简历评审服务。在这个过程中,发现不少伙伴的简历都有一款**外卖系统**,但在项目描述上,大家把简历写的更像是八股文,缺少了很多核心流程描述。 + +
+ +
+ +这样写简历,其实就等同于没做项目,投递出去肯定是非常吃亏的。即使靠着学历进入到面试阶段,在后续的2、3轮面试中,也非常有风险被其他同届伙伴刷下去。而好的项目,除了有这样的`热点场景`,也要有非常细腻的核心流程。让面试官一看简历,就有非常大的兴趣对你进行提问。 + +不过,都这个时候了,也马上要进入面试了。对于一些非常忙的伙伴,来不及换项目时候。所以,小傅哥这段时间,赶出了一套”**吉祥外卖**“,提供相关的;设计图稿、系统设计、课程资料、章节课程、简历模板、工程代码(可运行展示)等,方便小伙伴结合已经有的外卖项目基础,扩展学习刷课程即可。 + +接下来,小傅哥就介绍下这套项目,方便小伙伴们了解下,运行效果是什么样,都包括哪些东西。 + +> 💐 文末提供了课程的全套源码和资料,以及还有额外的17个实战项目,全程视频手把手做,增强小伙伴们的综合能力。 + +## 一、运行效果 + +该项目是一个基于真实业务场景的**快餐外卖生态系统** ,以互联网ToC场景的核心业务流程为背景,采用分布式架构设计 + 领域驱动建模,完成项目场景功能实现。项目涵盖用户端、商家端、配送端、运营端四大业务场景,构建完整的外卖生态闭环。 + +### 1. 用户端 + +#### 1.1 登录进首页 + +
+ +
+ +#### 1.2 下单到结算 + +
+ +
+ +#### 1.3 支付和我的 + +
+ +
+ +### 2. 配送端 + +
+ +
+ +### 3. 商家端 + +
+ +
+ +### 4. 运营端 + +
+ +
+ +以上UI,用户端完整对接了后端接口。配送端、商家端、运营端,后端接口已提供,后续可对接。 + +综上,是一个完整的从`登录`,`选品`、`下单`、`结算`、`支付`的全流程,非常全面的展示出系统功能。所有的操作都有后台的接口调用和数据库表输入的录入,在这个过程你可以完整的理解到关于外卖系统的全核心流程。那么有这样一套东西,你就可以在学习后,完善自己的简历了。 + +**程序员**,还是要看到可演示运行的东西 + 项目的代码,才好编写自己的简历。 + +## 二、项目信息 + +接下来,小傅哥带着大家在看看关于这套项目所能给大家提供的工程代码和资料。 + +### 1. 工程结构 + +
+ +
+ +- 工程:[https://gitcode.net/KnowledgePlanet/lucky-tackout](https://gitcode.net/KnowledgePlanet/lucky-tackout) - `文末加入小傅哥社群,即可获得全部代码,以及其他17个实战项目` +- 说明:工程提供了前后端代码,设计资料,文档资料等。主要适合于速刷系统,补充简历的伙伴使用。如果需要手把手教程类的项目,可以做小傅哥社群里其他17个实战项目。 +- 另外:本套项目主要为了方便伙伴快速刷来对应面试的,小傅哥也是快速出的,暂时没有相关的视频类资料。后续会录制一些视频讲解。 + +### 2. 功能领域 + +
+ +
+ +- 如图,功能领域涵盖;用户端、商家端、配送端、平台运营,之后这些模块使用核心服务层提供的能力。 +- 注意,关于页面,小傅哥提供了用户端的UI,后续在补充其他端的页面UI。 + +### 3. 领域模型 + +
+ +
+ +- 基于我们所需要的功能场景,进行了领域建模。分为;用户领域、订单领域、商家领域、配送领域、支付领域、产品领域。 +- 这些内容可以体现到简历上,也就有非常细腻的流程可以讲出来了。 + +### 4. 库表设计 + +
+ +
+ +
+ +
+ +- 本套项目设计了非常全面的库表,你可以理解到整体外卖的全部核心体系。 + +## 三、启动说明 + +### 1. 环境说明 + +- JDK 1.8 +- Maven 3.8.x +- MySql 8.x +- SpringBoot 2.7.12 + +### 2. 导入库表 + +在工程路径 `docs/dev-ops/mysql/sql/fast_food_delivery.sql` 提供了库表脚本,要导入到本地数据库中。 + +### 3. 配置链接 + +```java +spring: + datasource: + username: root + password: 12345678 + url: jdbc:mysql://127.0.0.1:3306/fast_food_delivery?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true + driver-class-name: com.mysql.cj.jdbc.Driver +``` + +- 在工程的 yml 文件,配置你的数据库表信息。 + +### 4. 启动项目 + +
+ +
+ +- 配置好相关信息后,启动项目,之后进入到 `nginx/html/h5-food-delivery` 打开 html 页面即可。 + diff --git "a/docs/md/project/s-pay-mall/part-1/\347\254\2541\350\212\202\357\274\232DDD \346\236\266\346\236\204\346\246\202\345\277\265.md" "b/docs/md/project/s-pay-mall/part-1/\347\254\2541\350\212\202\357\274\232DDD \346\236\266\346\236\204\346\246\202\345\277\265.md" new file mode 100644 index 000000000..4ddbbcdc3 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-1/\347\254\2541\350\212\202\357\274\232DDD \346\236\266\346\236\204\346\246\202\345\277\265.md" @@ -0,0 +1,41 @@ +--- +title: 第1-1节 DDD 架构概念 +pay: https://t.zsxq.com/EduDQ +--- + +# 《小型支付商城系统》第1-1节 DDD 架构概念 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +# 第1节:DDD 架构概念 + +DDD 是什么,这应该是每个想使用 DDD 开发项目的研发伙伴,遇到的第一个疑问,只有搞清楚它到底是什么才好上手使用。而 DDD 既不是 MVC 一样的工程结构,也不能直接等同于微服务架构,更不是一种设计模式。 + +## 1. DDD 是什么 + +那 DDD 是什么呢?来自于维基百科的一段定义:["Domain-driven design (DDD) is a major software design approach. "](https://en.wikipedia.org/wiki/Domain-driven_design),DDD 是一种软件设计方法。也就是说 DDD 是指导我们做软件工程设计的一种手段,它提供了用切割工程模型的各类技巧,如;领域、界限上下文、实体、值对象、聚合、工厂、仓储等。通过 DDD 的指导思想,我们可以在前期投入更多的时间,更加合理的规划出可持续迭代的工程设计。 + +在 DDD 中有一套共识的工程两阶段设计手段,包括;战略设计、战术设计。 + +- **战略设计**,主要以应对复杂的业务需求,通过抽象、分治的过程,合理的拆分为独立的多个微服务,从而分而治之。与之评价拆分的是否合理,则是在需求开发上线时候,是否每次都大量操作多个微服务开发和上线。这样的战略设计是一种失败的微服务单体设计。所以少数几个中等规模的单体应用,周围环绕着一个服务生态系统,这更有意义。[你实际上并没有构建微服务 @贾斯汀·埃瑟里奇](https://www.simplethread.com/youre-not-actually-building-microservices/) + +- **战术设计**,在这个范畴下,主要以讨论如何基于面向对象思维,运用领域模型来表达业务概念。通常在不做领域模型设计的架构,也就是通常映射到 MVC 三层架构下,`Service + 数据模型`的开发模式,会让 Service 扁平的、大量的,平铺出非常复杂的业务逻辑代码。再加上行为对象与功能逻辑的分离,贫血模型的开发方式,让行为对象的不断交叉使用,也是让系统不断增加复杂度,并到难以维护的根因。所以这一阶段要设计每一个可以表达领域概念的模型,并运用实体、聚合、领域服务来承载。 + +## 2. DDD 的概念 + +什么是充血模型,领域内都包括什么,实体、聚合、值对象,有什么区别?这样一些"为什么"的概念,也是战术设计过程中非常重要的知识项。搞清楚它们才能做 DDD 设计。 + +### 2.1 充血模型 + +**充血模型**,指将对象的属性信息与行为逻辑聚合到一个类中,常用的手段如在对象内提供属于当前对象的`信息校验`、`拼装缓存Key`、`不含服务接口调用的逻辑处理`等。 + +
+ +
+ +- 这样的方式可以在使用一个对象时,就顺便拿到这个对象的提供的一系列方法信息,所有使用对象的逻辑方法,都不需要自己再次处理同类逻辑。 +- 但不要只是把充血模型,仅限于一个类的设计和一个类内的方法设计。充血还可以是整个包结构,一个包下包括了用于实现此包 Service 服务所需的各类零部件(模型、仓储、工厂),也可以被看做充血模型。 +- 同时我们还会再一个同类的类下,提供对应的内部类,如用户实名,包括了,通信类、实名卡、银行卡、四要素等。它们都被写进到一个用户类下的内部子类,这样在代码编写中也会清晰的看到子类的所属信息,更容易理解代码逻辑,也便于维护迭代。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-1/\347\254\2542\350\212\202\357\274\232DDD \345\273\272\346\250\241\346\226\271\346\263\225.md" "b/docs/md/project/s-pay-mall/part-1/\347\254\2542\350\212\202\357\274\232DDD \345\273\272\346\250\241\346\226\271\346\263\225.md" new file mode 100644 index 000000000..a0239bfa2 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-1/\347\254\2542\350\212\202\357\274\232DDD \345\273\272\346\250\241\346\226\271\346\263\225.md" @@ -0,0 +1,40 @@ +--- +title: 第1-2节 DDD 建模方法 +pay: https://t.zsxq.com/teMSw +--- + +# 《小型支付商城系统》第1-2节 DDD 建模方法 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +四色建模(风暴事件)是整个 DDD 这套软件设计方法中用于工程拆分界限上下文的非常重要的实践手段。通过建模过程快速识别业务领域中的关键事件和核心流程,也是在这个过程中设计出领域对象的,为后面详细设计和代码开发做指导。 + +你可以把整个过程理解为,为工程开发提供面向对象设计,涵盖;领域拆分、界限串联、功能聚合。所以相比`Service + 数据模型`的贫血开发方式,**DDD 前期需要付出更多的设计成本,但对于软件的长周期迭代,这样的好处是非常大的。** + +## 1. 建模目的 + +工程的建模的目的是为了我们做工程开发时提供指导方案,就像一栋大楼的设计蓝图一样,也像一个超市中会有不同品类的货架,需要提前规划好。所以你需要在工程开发时所需的各类核心内容,都会在建模中体现,如;分几个包、有哪些核心对象、要串联什么流程、有哪些核心业务要实现、过程中与外部服务的交互。 + +那么为了达成一个讨论的共识,而不是每个人都有一套的标准和词汇。所以会使用 DDD 提供专门的建模方法和名词进行统一的设计,此外因为 DDD 的统一建模语言,不涉及技术编码,也具有通用性,所以可以在建模过程让产品、研发、测试、架构师等人员一起参与讨论。如;领域、领域模型(实体、聚合、值对象)、领域服务、端口适配器、仓储、界限上下文、领域编排等名词。*这在上一节已经做了相关的解释。* + +## 2. 怎么建模 + +DDD 的建模过程,是以一个用户为起点,通过行为命令,发起行为动作,串联整个业务。而这个用户的起点最初来自于用例图的分析。用例图是用户与系统交互的最简表示形式,展现了用户和与他相关的用例之间的关系。通过用例图,我们可以分析出所有的行为动作。 + +在 DDD 中用于完成用户的行为命令和动作分析的过程,是一个四色建模的过程,也称作风暴模型。在使用 DDD 的标准对系统建模前,一堆人要先了解 DDD 的操作手段,这样才能让产品、研发、测试、运营等了解业务的伙伴,都能在同一个语言下完成系统建模。 + +
+ +
+ +此图是整个四色建模的指导图,通过寻找领域事件,发起事件命令,完成领域事件的过程,完成 DDD 工程建模。 + +- 蓝色 - 决策命令,是用户发起的行为动作,如;开始签到、开始抽奖、查看额度等。 +- 黄色 - 领域事件,过去时态描述。如;签到完成、抽奖完成、奖品发放完成。它所阐述的都是这个领域要完成的终态。 +- 粉色 - 外部系统,如你的系统需要调用外部的接口完成流程。 +- 红色 - 业务流程,用于串联决策命令到领域事件,所实现的业务流程。一些简单的场景则直接有决策命令到领域事件就可以了。 +- 绿色 - 只读模型,做一些读取数据的动作,没有写库的操作。 +- 棕色 - 领域对象,每个决策命令的发起,都是含有一个对应的领域对象。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-1/\347\254\2543\350\212\202\357\274\232DDD \345\267\245\347\250\213\346\250\241\345\236\213.md" "b/docs/md/project/s-pay-mall/part-1/\347\254\2543\350\212\202\357\274\232DDD \345\267\245\347\250\213\346\250\241\345\236\213.md" new file mode 100644 index 000000000..0df2679bc --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-1/\347\254\2543\350\212\202\357\274\232DDD \345\267\245\347\250\213\346\250\241\345\236\213.md" @@ -0,0 +1,54 @@ +--- +title: 第1-3节 DDD 工程模型 +pay: https://t.zsxq.com/iPuAe +--- + +# 《小型支付商城系统》第1-3节 DDD 工程模型 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +**什么是系统的工程结构,工程框架的作用是什么?** + +其实,工程结构的存在作用目的,是为了承载工程系统开发的模型划分,定义工程服务开发过程中实施标准。说白了,就是你在工程实现时,在哪个层访问数据库、哪个层使用缓存、哪个层调用外部接口、哪个层做功能实现,这就是工程框架结构定义的目的。 + +但在 `Service + 贫血模型` 的三层结构开发指导下,是没有细分出面向对象工程结构设计的趋于划分的。所以在通常意义的 MVC 下,开发过程所有需要的内容,都会堆砌到 Service 实现类中。这也是为什么 DDD 领域驱动设计的落地工程结构,会出现;洋葱架构、整洁架构、菱形架构、六边形架构等这些架构模型。因为我们需要更细致的划分,来承载 DDD 设计概念中映射的领域、仓储、适配、编排、触发,并重视面向对象过程。—— 其实你一上学,学Java就开始学面向对象了,只不过一点点在忘记它。 + +## 一、为啥需要架构 + +说到开发代码为啥需要架构,就想买了个房子,为啥要隔出厨房、客厅、卧室、卫生间一样,核心目的就是让不同的职责分配到不同的区域内。虽然在代码中是没有马桶要放卫生间、沙发要放客厅、床要放卧室。但他有一些列的科目信息要引入到工程。 + +**在工程开发时会涉及到的核心科目;** + +
+ +
+ +如;统一的异常、数据库的连接、日志的打印、外部服务的调用、消息的监听、任务的轮训以及服务的实现等一些列的东西要处理,分配到不同的工程包下承载。在 DDD 之前,我们一直用 MVC 的分层结构承接这些内容; + +
+ +
+ +通用的、配置的、组件的、持久化的、内部的、外部的,在以往的单体应用时代开发下,其实是没有这么多东西的,那时候的工程结构都偏向于 Service + 贫血模型实现。 + +但随着微服务的演进,越来越多的内容被填充到工程中,这个时候你细心的查看架构,就会发现原本的 MVC 结构其实已经变的非常混乱了。一个 Service 中为了实现自己的功能,要引入一堆的东西,这些原子的功能与 Service 自身的服务耦合在一块。也导致了工程的维护成本越来越大。 + +>这样的三层工程结构分配方式,对于要承载庞大的分布式技术栈体系显然是有点小马拉大车,三缸机带不动SUV一样。 + +## 二、工程结构设计 + +2004年,Eric Evans 在发表了一部名为《Domain Driven Design》的著作。2005年 Alistair Cockburn 提出的“六边形关系图”理论,2008年 Jeffrey Palermo 提出了洋葱架构。虽然这些架构并不是专门为 DDD 而出,但巧的是这些架构都在 DDD 一书发表之后陆续推出新的架构模型。同时这些架构的分层设计方式也都与 DDD 非常契合,在这些架构下也可以很好的落地 DDD 设计方法。 + +
+ +
+无论是六边形架构,还是洋葱架构,或是[张毅老师](http://zhangyi.xyz/)提到的南向网关/北向网关的菱形架构,他们的目标都是以领域服务为核心,隔离内部实现与外部资源的耦合。 + +在 DDD 分层架构下,以支撑 domain 核心领域实现拆分出基础设施(infrastructure),来承接对外部资源的调用。触发器(trigger)向外部提供服务。之后 app 为应用启动、api 为接口定义、types 为通用信息、case 为编排。 + +在这样一套结构下,用于开发工程的各项科目也可以被优雅的分配到各个分层结构了。相对于 Service + 数据模型的贫血模型结构,现在就主要以 domain 为核心开发业务功能,不会在 domain 工程模块下,引入其他各类外部组件了,这样就可以更加关心业务功能开发。 + +之后是这样的思想映射到工程中,常见的分层结构会有两套,一套是整洁分层,另外一套是六边形分层。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-2/\347\254\2541\350\212\202\357\274\232\345\260\217\345\236\213\346\224\257\344\273\230\345\225\206\345\237\216\351\234\200\346\261\202\350\256\276\350\256\241.md" "b/docs/md/project/s-pay-mall/part-2/\347\254\2541\350\212\202\357\274\232\345\260\217\345\236\213\346\224\257\344\273\230\345\225\206\345\237\216\351\234\200\346\261\202\350\256\276\350\256\241.md" new file mode 100644 index 000000000..5197a4c88 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-2/\347\254\2541\350\212\202\357\274\232\345\260\217\345\236\213\346\224\257\344\273\230\345\225\206\345\237\216\351\234\200\346\261\202\350\256\276\350\256\241.md" @@ -0,0 +1,32 @@ +--- +title: 第2-1节:需求PRD讲解 +pay: https://t.zsxq.com/2iM7V +--- + +# 《小型支付商城系统》第2-1节:需求PRD讲解 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在公司中,研发所做的需求,都是来自于产品对运营以及市场的诉求,进行需求设计。在经过产品PRD评审后,研发人员才能承接这个需求,再进行研发设计直至开发上线。 + +所以,本节我们以产品的视角了解需求,评审PRD要完成的事项。这样可以让一些还没有正式进入公司工作的伙伴了解到整个项目开发的全流程。 + +## 二、需求背景 + +市场运营根据公司发展诉求,为公司的电脑、键盘提供一个简单小巧的自有在线商城,售卖公司的产品。根据运营市场调研,有55%的用户非常有意愿通过厂家直售的方式购买商品,这样即可减少冗余渠道线,直接让利给消费者。 + +## 三、交互效果 + +用户通过小型电商查看购买商品,点击购买校验登录。扫码登录后下单和跳转支付宝收银台。 + +
+ +
+ +- 登录,通过微信公众号扫码登录,降低用户注册登录成本。 +- 支付,对接支付宝交易,通过支付宝完成支付。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-2/\347\254\2542\350\212\202\357\274\232\346\224\257\344\273\230\345\225\206\345\237\216\345\233\233\350\211\262\345\273\272\346\250\241\350\256\276\350\256\241.md" "b/docs/md/project/s-pay-mall/part-2/\347\254\2542\350\212\202\357\274\232\346\224\257\344\273\230\345\225\206\345\237\216\345\233\233\350\211\262\345\273\272\346\250\241\350\256\276\350\256\241.md" new file mode 100644 index 000000000..0bf0dffa8 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-2/\347\254\2542\350\212\202\357\274\232\346\224\257\344\273\230\345\225\206\345\237\216\345\233\233\350\211\262\345\273\272\346\250\241\350\256\276\350\256\241.md" @@ -0,0 +1,31 @@ +--- +title: 第2-2节:工程四色建模设计 +pay: https://t.zsxq.com/XeucE +--- + +# 《小型支付商城系统》第2-2节:工程四色建模设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在公司中实际做项目,会先有产品拉一个评审会,对本地需求的 PRD 进行评审。这个过程会有产品、研发(前后端)、测试,一起参与评审。一般在 `30分钟 ~ 2小时`的会议。评审完成后,研发会对需求进行分工,较大型的项目,会有几个研发同时介入。之后每个研发进行设计阶段,设计会包括新项目的工程框架、技术栈选择、需求的模型设计(DDD)、功能的流程(MVC)实现、要实现和改动点说明等。 + +本节我们就按照这样的一个过程进行需求的研发设计,主要包括;用户用例图、四色建模(领域拆分)、UML流程图、改动点。有这些东西就可以指导研发开发了。 + +本项目会搭建 MVC、DDD 双套工程,四色建模主要给 DDD 工程使用。 + +## 二、画用例图 + +研发可以根据产品PRD提供的业务UI和流程,分析出用户会有的行为,根据行为画出用户用例图; + +
+ +
+ +- 用例图(英语:use case diagram)是用户与系统交互的最简表示形式,展现了用户和与他相关的用例之间的关系。通过用例图,人们可以获知系统不同种类的用户和用例。用例图也经常和其他图表配合使用。 +- 用例图,也可以等同于是用户故事(英语:User story)(软件开发和项目管理中的常用术语),主旨是以日常语言或商务用语撰写句子,是一段简单的功能表述。以客户或使用者的观点撰写下有价值的功能、引导、框架来与使用者进行互动,进而推动工作进程。可以被认为是一种规格文件,但更精确而言,它代表客户的需求与方向。以该用户故事来反应对象在组织内的其工作职责、范围、需要进行的任务等。用户故事在敏捷开发方法中用来定义系统需要提供的功能和实现需求管理。 +- 尽管用例本身会涉及大量细节和各种可能性,用例图却能提纲挈领地让人了解系统概况。它为“系统做什么”提供了简化了的图形表示,因此被誉为“搭建系统的蓝图”。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-2/\347\254\2543\350\212\202\357\274\232\346\224\257\344\273\230\350\256\242\345\215\225\345\234\272\346\231\257\350\241\250\350\256\276\350\256\241.md" "b/docs/md/project/s-pay-mall/part-2/\347\254\2543\350\212\202\357\274\232\346\224\257\344\273\230\350\256\242\345\215\225\345\234\272\346\231\257\350\241\250\350\256\276\350\256\241.md" new file mode 100644 index 000000000..4877256ed --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-2/\347\254\2543\350\212\202\357\274\232\346\224\257\344\273\230\350\256\242\345\215\225\345\234\272\346\231\257\350\241\250\350\256\276\350\256\241.md" @@ -0,0 +1,25 @@ +--- +title: 第2-3节:支付订单场景表设计 +pay: https://t.zsxq.com/MUIJB +--- + +# 《小型支付商城系统》第2-3节:支付订单场景表设计 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +讲解库表设计规范,包括;建表、字段、索引,在中大厂开发中也是非常重视这些规范的。之后我们结合上一节的研发设计所需开发的流程,设计对应的库表。 + +## 二、设计规范 + +为了能让读者更加清晰地看到这些相关规范都是如何体现的,小傅哥这里准备了个大图,把库表字段和规范全部整合在一起,方便学习使用。如下; + +
+ +
+ +如上所列规范包括:建表相关规范、字段相关规范、索引相关规范、使用相关规范。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2541\350\212\202\357\274\232DDD \345\267\245\347\250\213\346\241\206\346\236\266\346\220\255\345\273\272.md" "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2541\350\212\202\357\274\232DDD \345\267\245\347\250\213\346\241\206\346\236\266\346\220\255\345\273\272.md" new file mode 100644 index 000000000..1236bb43d --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2541\350\212\202\357\274\232DDD \345\267\245\347\250\213\346\241\206\346\236\266\346\220\255\345\273\272.md" @@ -0,0 +1,43 @@ +--- +title: 第3-1节:DDD 工程框架搭建 + 基础配置 + Git 使用 +pay: https://t.zsxq.com/bjFkO +--- + +# 《小型支付商城系统》第3-1节:DDD 工程框架搭建 + 基础配置 + Git 使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +手把手的,分别通过手动创建和脚手架创建 DDD 工程结构。以及完成基础配置和 Git 使用。 + +从本节进入 DDD 部分的学习开始,会对照 MVC 进行差异化的对比讲解。可能有时候文字没法直接关的表达两个工程的使用差异,可以先看视频在看文档和代码,这样会更好的理解 MVC 到 DDD 的工程设计实现。 + +首先 DDD 是一种软件设计方法,它的规范标准和思想指导着软件设计实现,而为了更好的承接这样一套思想,有了新的架构结构。也就是我们常提到的;整洁架构、洋葱架构、六边形架构、菱形架构,这些架构的设计,合理的划分出了不同的分层结构,用于承接各项组件、服务、功能领域。在我们做这部分 DDD 实现时,会不断的体现出这些内容。 + +## 二、环境配置 + +- Jdk 1.8 +- Maven 3.x - [Maven 教程](https://bugstack.cn/md/road-map/maven.html) +- IntellJ IDEA 社区版(免费) [IntelliJ IDEA 教程](https://bugstack.cn/md/road-map/intellij-idea.html) 推荐`2023`及以上版本,使用起来更方便。 +- Git - 安装后会配置到 IntellJ IDEA 这样才能向服务端推送或者拉取代码。[Git 教程 ](https://t.zsxq.com/19Rnk98M0) 学习后可以知道怎么拉取、提交和比对代码。 +- 在线版脚手架:[https://bugstack.cn/md/road-map/ddd-archetype-maven.html](https://bugstack.cn/md/road-map/ddd-archetype-maven.html) - 直接配置使用即可。 + +--- + +```xml + + + alimavenrepository + aliyun maven repository + http://maven.aliyun.com/nexus/content/groups/public/ + central + + +``` + +- 如果你的 Maven 拉取 Jar 包速度很慢,可以在 Maven 的 conf 下 settings.xml 添加阿里云镜像。 +- [https://t.zsxq.com/19Rnk98M0](https://t.zsxq.com/19Rnk98M0) 已提供配置好了 阿里云 Maven 镜像的 Maven 压缩包。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2542\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\351\211\264\346\235\203.md" "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2542\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\351\211\264\346\235\203.md" new file mode 100644 index 000000000..ab2d7f9a3 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2542\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\351\211\264\346\235\203.md" @@ -0,0 +1,31 @@ +--- +title: 第3-2节:DDD 重构,微信公众号鉴权 +pay: https://t.zsxq.com/wBobE +--- + +# 《小型支付商城系统》第3-2节:DDD 重构,微信公众号鉴权 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在【第2部分 - 需求设计】[工程四色建模](https://articles.zsxq.com/id_pj4jlidovrpd.html)里,我们已经做了 DDD 部分的建模设计。到这一节学习前,可以回顾下四色建模设计时所做的内容。关于 DDD 的知识,一部分是工程四色建模拆分领域功能的界限上下文战略知识,另外一部分就是本节这里的战术设计指导落地。 + +本节小傅哥会带着大家,对照 MVC 中的微信公众号鉴权代码实现,拆解到 DDD 中进行实现。 + +> 因为在前面的内容讲解中已经介绍了很多的业务,所以在本节的实现过程中,重点会关注 mvc 到 ddd 的功能重构细节差异。 + +## 二、功能分区 + +DDD 映射下的六边形架构,会有非常明确的分层分区,各个模块都在自己的职责范围内实现。 + +
+ +
+ +- trigger 下实现让外部调用我们的接口,这里是微信公众号接口的鉴权和接收消息。 +- types 下加入了 sdk 微信的对接实现。 +- app 是启动层,里面增加 yml 配置信息。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2543\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\347\231\273\345\275\225\345\212\237\350\203\275\345\256\236\347\216\260.md" "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2543\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\347\231\273\345\275\225\345\212\237\350\203\275\345\256\236\347\216\260.md" new file mode 100644 index 000000000..ac5fc99fc --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2543\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\347\231\273\345\275\225\345\212\237\350\203\275\345\256\236\347\216\260.md" @@ -0,0 +1,27 @@ +--- +title: 第3-3节:DDD重构,登录功能设计实现 +pay: https://t.zsxq.com/Vc1Os +--- + +# 《小型支付商城系统》第3-3节:DDD重构,登录功能设计实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过 DDD 领域设计,从 MVC 工程中拆分登录功能到 DDD 工程中实现。 + +本节会涉及到 api、domain、infrastructure、trigger、app,几层模块的使用,有了这一节的功能实现,会更加清楚 MVC 和 DDD 实现差异。不过编码的核心本质是不变的,所有的分层结构都是为了让代码实现逻辑更加清晰,降低后续的迭代成本。 + +## 二、登录模型 + +通过 DDD 领域驱动设计,陆续实现扫描登录、下单等流程模块。这一部分的设计可以参考第2部分中的四色建模设计。 + +
+ +
+ +- 本节我们先来实现登录功能领域,登录凭证、校验凭证以及保存登录状态。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2544\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\345\225\206\345\223\201\344\270\213\345\215\225.md" "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2544\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\345\225\206\345\223\201\344\270\213\345\215\225.md" new file mode 100644 index 000000000..351b57ab8 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2544\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\345\225\206\345\223\201\344\270\213\345\215\225.md" @@ -0,0 +1,31 @@ +--- +title: 第3-4节:DDD重构,商品下单 +pay: https://t.zsxq.com/3X9GA +--- + +# 《小型支付商城系统》第3-4节:DDD重构,商品下单 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过 DDD 领域设计,从 MVC 工程拆分下单中的创建订单到 DDD 工程中实现。 + +相对于 MVC 工程的设计,DDD 的每个领域就是独立的空间,它会仔细考虑请求方法的出入参对象,而这些对象叫做领域对象。 + +在整个实现过程,每一个领域的功能,我们可以想象为;一个人,拿着一把钥匙,开锁到进屋。那么这里的一个人是用户、一把钥匙是入参领域对象、开锁是决策命令,完成开锁进屋是做完了一件事的领域事件。这个过程就是四色建模中的战略设计过程。 + +那么本节的下单,就是这样一个过程,用户通过购物车创建订单到支付。那么这里你可以思考,它的决策命令、领域事件、领域对象都是什么,接下来我们就要做这样一个实现。 + +## 二、下单模型 + +通过 DDD 领域驱动设计,本节实现点击下单,创建订单的处理。这一部分的设计可以参考第2部分中的四色建模设计。 + +
+ +
+ +- 本节我们先来实现用户点击下单的动作,完成商品下单。到下一节在处理关于支付宝沙箱的支付单处理。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2545\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\345\257\271\346\216\245\346\224\257\344\273\230.md" "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2545\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\345\257\271\346\216\245\346\224\257\344\273\230.md" new file mode 100644 index 000000000..ba8a515c5 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2545\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\345\257\271\346\216\245\346\224\257\344\273\230.md" @@ -0,0 +1,27 @@ +--- +title: 第3-5节:DDD 重构,对接支付 +pay: https://t.zsxq.com/NYvtw +--- + +# 《小型支付商城系统》第3-5节:DDD 重构,对接支付 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过 DDD 领域设计,从 MVC 工程拆分创建支付单到 DDD 工程中实现。 + +在我们不断把功能从 MVC 拆分到 DDD 的实现中,也是从面向过程到面向对象的开发思维转变。编程的能力提升就是不断地锻炼各种能力。 + +## 二、下单模型 + +通过 DDD 领域驱动设计,本节实现点击下单到完成支付宝订单创建过程。这一部分的设计可以参考第2部分中的四色建模设计。 + +
+ +
+ +- 本节我们实现点击下单创建出支付宝沙箱订单完成。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2546\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\346\224\257\344\273\230\345\233\236\350\260\203.md" "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2546\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\346\224\257\344\273\230\345\233\236\350\260\203.md" new file mode 100644 index 000000000..7f02c1bfc --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2546\350\212\202\357\274\232DDD \351\207\215\346\236\204\357\274\214\346\224\257\344\273\230\345\233\236\350\260\203.md" @@ -0,0 +1,27 @@ +--- +title: 第3-6节:DDD 重构,支付回调处理 +pay: https://t.zsxq.com/CWVQM +--- + +# 《小型支付商城系统》第3-6节:DDD 重构,支付回调处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过 DDD 领域设计,从 MVC 工程拆分支付回调和接口到 DDD 工程中实现。 + +从 MVC 到 DDD,是编码分层清晰,是逻辑实现整洁,也是后续的维护更加容易。其实也说明 DDD 的工程标准更加强。 + +## 二、回调模型 + +通过 DDD 领域驱动设计,本节实现支付回调过程。这一部分的设计可以参考第2部分中的四色建模设计。 + +
+ +
+ +- 本节我们实现支付回调处理。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2547\350\212\202\357\274\232\345\211\215\347\253\257\351\241\265\351\235\242.md" "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2547\350\212\202\357\274\232\345\211\215\347\253\257\351\241\265\351\235\242.md" new file mode 100644 index 000000000..d94b3891d --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2547\350\212\202\357\274\232\345\211\215\347\253\257\351\241\265\351\235\242.md" @@ -0,0 +1,23 @@ +--- +title: 第3-7节:前端页面 +pay: https://t.zsxq.com/qE34U +--- + +# 《小型支付商城系统》第3-7节:前端页面 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +搭建简单的前端页面,自动轮训检查扫描登录状态,完成登录跳转商品下单页。 + +## 二、业务流程 + +如图,本节可以串联用户UI流程,登录下单到支付完成。 + +
+ +
\ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2548\350\212\202\357\274\232Docker\346\236\204\345\273\272\345\222\214\351\203\250\347\275\262.md" "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2548\350\212\202\357\274\232Docker\346\236\204\345\273\272\345\222\214\351\203\250\347\275\262.md" new file mode 100644 index 000000000..c0adfda52 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-ddd/\347\254\2548\350\212\202\357\274\232Docker\346\236\204\345\273\272\345\222\214\351\203\250\347\275\262.md" @@ -0,0 +1,26 @@ +--- +title: 第3-8节:Docker构建和部署 +pay: https://t.zsxq.com/8vXVI +--- + +# 《小型支付商城系统》第3-8节:Docker构建和部署 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过 Docker 给工程配置 Dockerfile、build.sh 文件对项目进行构建和部署,前端应用采用 Nginx 进行部署。 + +## 二、Docker 是什么? + +Docker 你可以把它当做一个小型的轻量的虚拟机(虚拟的电脑),它可以帮你屏蔽各类软件环境安装时候的复杂性,你只需要一段脚本,即可完成 mysql、redis、nginx 等等各类你能想到的软件的部署和卸载。—— 不知道多少伙伴自己电脑装完 MySQL 就不会卸载了! + +
+ +
+ +- 官网:[https://www.docker.com/](https://www.docker.com/) - 本地电脑直接安装即可。Windows 电脑因为不是 linux 所以需要开启 wsl2(百度/星球搜索有配置教程) +- 云教程:[https://bugstack.cn/md/road-map/docker.html](https://bugstack.cn/md/road-map/docker.html) - 也可以选择云服务器安装。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2541\350\212\202\357\274\232MVC \345\267\245\347\250\213\346\241\206\346\236\266\346\220\255\345\273\272.md" "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2541\350\212\202\357\274\232MVC \345\267\245\347\250\213\346\241\206\346\236\266\346\220\255\345\273\272.md" new file mode 100644 index 000000000..1388ceeff --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2541\350\212\202\357\274\232MVC \345\267\245\347\250\213\346\241\206\346\236\266\346\220\255\345\273\272.md" @@ -0,0 +1,46 @@ +--- +title: 第3-1节:MVC 工程框架搭建 + 基础配置 + Git 使用 +pay: https://t.zsxq.com/K4xTs +--- + +# 《小型支付商城系统》第3-1节:MVC 工程框架搭建 + 基础配置 + Git 使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +手把手搭建,单 model 和多 model 的 mvc 分层架构,并配置 pom、yml 等,以及讲解分层结构的功能职责。最后将工程使用 Git 提交到 gitcode 仓库中。 + +mvc 的优势在于快,这个快是来自于不对系统进行建模、不考虑防腐(多层调用间的对象,不做隔离处理,可互相调用),也没有为分布式的各项连接资源做分层考虑,所以它的实现方式更快。 + +在最早我们实现单体类应用中,一个工程基本就使用2个东西,一个是 mysql 数据库,另外一个是 redis 缓存。这样的 mvc 工程复杂度是很低的,所以也比较好维护。 + +但随着分布式微服务的引入,在一个工程中除了有,数据库、缓存,还有 rpc、mq、任务调度、配置中心、一致性组件、分库分表、ElasticSearch 等等各项技术栈,这样在 mvc 中在实现这些就变得复杂了。所以这也是为什么大家开始逐步往 DDD 迁移,因为一方面是 DDD 的建模思维,另外一方面是 DDD 与新的架构模型,洋葱、整洁、六边形更加契合。新的架构模型,也能更好的承载分布式微服务架构设计实现。 + +不过,所有东西的学习最后学的就是思想。工程框架结构的设计,也就是用合理的分层结构,有效的填充各项资源,提高资源的调配效率。这句你要细细的琢磨下。如果你面试能讲出这么一句,那么对工程的框架理解的非常深入的。 + +## 二、环境配置 + +- Jdk 1.8 +- Maven 3.x - [Maven 教程](https://bugstack.cn/md/road-map/maven.html) +- IntellJ IDEA 社区版(免费) [IntelliJ IDEA 教程](https://bugstack.cn/md/road-map/intellij-idea.html) +- Git - 安装后会配置到 IntellJ IDEA 这样才能向服务端推送或者拉取代码。[Git 教程 ](https://t.zsxq.com/19Rnk98M0) 学习后可以知道怎么拉取、提交和比对代码。 + +--- + +```xml + + + alimavenrepository + aliyun maven repository + http://maven.aliyun.com/nexus/content/groups/public/ + central + + +``` + +- 如果你的 Maven 拉取 Jar 包速度很慢,可以在 Maven 的 conf 下 settings.xml 添加阿里云镜像。 +- [https://t.zsxq.com/19Rnk98M0](https://t.zsxq.com/19Rnk98M0) 已提供配置好了 阿里云 Maven 镜像的 Maven 压缩包。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2542\350\212\202\357\274\232\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\351\211\264\346\235\203.md" "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2542\350\212\202\357\274\232\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\351\211\264\346\235\203.md" new file mode 100644 index 000000000..87d64ec0b --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2542\350\212\202\357\274\232\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\351\211\264\346\235\203.md" @@ -0,0 +1,28 @@ +--- +title: 第3-2节:微信公众号鉴权 +pay: https://t.zsxq.com/ryhqq +--- + +# 《小型支付商城系统》第3-2节:微信公众号鉴权 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +在 mvc 分层框架结构下添加微信公众号鉴权所需的接口,并通过 natapp 内网穿透组件,暴漏本地接口,让微信公众号平台可以配置使用。 + +在前面章节中已经讲解了,关于微信公众号鉴权的代码实现以及配置使用。所以本节我们会把重点放在 mvc 工程结构的使用,把关于微信的公众号鉴权操作流程按需放到所属模块下。 + +## 二、功能分区 + +mvc 工程结构区内,简单的划分了基础包、数据库、逻辑实现、接口实现。我们在这一节先使用到基础包和提供接口。 + +
+ +
+ +- common 承载着对接微信公众号的基础功能。 +- web 下的 controller 用于实现对外提供的接口。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2543\350\212\202\357\274\232\347\231\273\345\275\225\345\212\237\350\203\275\345\256\236\347\216\260.md" "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2543\350\212\202\357\274\232\347\231\273\345\275\225\345\212\237\350\203\275\345\256\236\347\216\260.md" new file mode 100644 index 000000000..57805d808 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2543\350\212\202\357\274\232\347\231\273\345\275\225\345\212\237\350\203\275\345\256\236\347\216\260.md" @@ -0,0 +1,29 @@ +--- +title: 第3-3节:登录功能设计实现 +pay: https://t.zsxq.com/fsEpq +--- + +# 《小型支付商城系统》第3-3节:登录功能设计实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过微信公众号平台提供的 API 接口,做微信公众号扫码登录。 + +扫码登录主要是需要微信公众号平台,提供一个生成带参的二维码,让用户使用微信扫描二维码登录。扫码后我们在微信公众号对接的接口中会接收到扫码完成消息,里面就会含带二维码参数,这样就可以知道到是谁扫描的二维码。我们把扫描后解析的信息和用户做绑定,也就可以完成登录操作了。 + +## 二、登录流程 + +微信扫码登录的流程主要包括;用户、浏览器、后端服务、公众号,这四个部分。我们可以先通过UML流程图,了解下整个调用关系。此流程已在[《第2部分,工程四色建模设计》](https://t.zsxq.com/XeucE)中讲解。 + +
+ +
+ +- 首先,由用户发起登录操作。让WEB页面从服务端获取登录凭证。 +- 之后,前端页面拿到登录凭证后,可以使用 Ticket 从公众号服务平台换取二维码。 +- 最后,用户扫码登录。扫码后,服务端会接收到来自公众号的回调消息,服务端再把回调消息中的 openid【用户唯一标识】和 ticket 进行绑定。这个时候你也可以创建出 jwt token 反馈给前端,作为登录成功的存储信息,后续校验 jwt token 就可以了。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2544\350\212\202\357\274\232\345\225\206\345\223\201\344\270\213\345\215\225.md" "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2544\350\212\202\357\274\232\345\225\206\345\223\201\344\270\213\345\215\225.md" new file mode 100644 index 000000000..afb7fe72d --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2544\350\212\202\357\274\232\345\225\206\345\223\201\344\270\213\345\215\225.md" @@ -0,0 +1,31 @@ +--- +title: 第3-4节:商品下单 +pay: https://t.zsxq.com/g6Szl +--- + +# 《小型支付商城系统》第3-4节:商品下单 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +对接数据库表,完成商品下单。 + +要注意商品下单中,要考虑整个流程中可能存在的失败节点。`做编程开发很多时候都是在处理异常。`商品下单中,或者下单完成都可能存在失败的情况。比如写库失败,或者调用外部支付平台创建支付单失败,也可能存在用户创建完支付单但未支付的情况。 + +这些流程都是要在下单中做的处理,在公司的实际场景中,下单还要过很多规则校验。比如账户状态、风控、营销、试算、锁券等等流程,才会到下单。所以很多这样的场景,都会考虑使用设计模式进行解耦,否则很难完成后续的迭代。 + +## 二、下单流程 + +如图,本节我们先做下单流程。调用 Alipay 支付宝创建支付单和等待回调我们后续章节在处理。 + +
+ +
+ +- 首先,用户在系统中创建订单(流水单),创建过程中需要判断是否存在未支付订单,存在则可以直接返回。另外还有一种可能,创建的订单存在,但没有支付单,也就是【掉单】。这是因为本身的业务系统和外部的支付创建(支付宝)不是一个事务,不能一起成功或失败,所以要做一些流程的校验。比如我们创建订单成功,但创建支付单失败。这个之后用户继续创建订单,就会优先使用这笔订单创建支付单。如果流程中没有存在的掉单,则直接创建支付单即可。 +- 之后,创建完支付宝订单,会由页面跳转到网络收银台,引导用户完成支付操作。 +- 最后,就是接收支付回调消息,更新本地的订单状态,以及推动后续流程。比如;发放商品、驱动物流、虚拟支付等。当然在实际的商城中,还会有逆向流程,比如商品有问题,或者用户主动发起退单。这个时候就要走逆向流程,退单、审核、退款流程。你可以尝试完成。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2545\350\212\202\357\274\232\345\257\271\346\216\245\346\224\257\344\273\230.md" "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2545\350\212\202\357\274\232\345\257\271\346\216\245\346\224\257\344\273\230.md" new file mode 100644 index 000000000..728bb346b --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2545\350\212\202\357\274\232\345\257\271\346\216\245\346\224\257\344\273\230.md" @@ -0,0 +1,30 @@ +--- +title: 第3-5节:对接支付 +pay: https://t.zsxq.com/2TPVn +--- + +# 《小型支付商城系统》第3-5节:对接支付 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过引入支付宝支付 sdk,实例化支付对象,完成支付对接。并提供商品交易下单和回调接口。 + +这里要记住一点,所有不是数据库一次事务提交的操作,都没法保证事务一致性。包括;http接口、rpc接口、mq消息等。这些调用过程,都需要有任务作为补偿处理,保证最终一致性。 + +他们的失败可能是网络超时,导致在调用过程中发生,也可能是消费时发生进行重试。所以这类接口调用除了有任务保持一致性,还有就是要有唯一幂等字段。确保在重复消费的过程中,也只是有一条记录产生或者发生变更。 + +## 二、业务流程 + +如图,本节我们来完成支付宝沙箱对接的流程; + +
+ +
+ +- 创建完成本地订单后,调用支付宝沙箱创建支付订单。 +- 在这个过程,如果发生 http 超时失败,那么会有二次用户请求时,检查到`掉单`重新发起创建动作。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2546\350\212\202\357\274\232\346\224\257\344\273\230\345\233\236\350\260\203.md" "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2546\350\212\202\357\274\232\346\224\257\344\273\230\345\233\236\350\260\203.md" new file mode 100644 index 000000000..43b842772 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2546\350\212\202\357\274\232\346\224\257\344\273\230\345\233\236\350\260\203.md" @@ -0,0 +1,28 @@ +--- +title: 第3-6节:支付回调处理 +pay: https://t.zsxq.com/Ydjmt +--- + +# 《小型支付商城系统》第3-6节:支付回调处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +对支付流程收尾,完成支付回调、掉单补偿、超时关单的处理,以及监听支付成功消息。 + +到本节的处理后,我们就把支付流程全部做完了。其实这也是面试中喜欢的问的场景,只有把一个支付的流程,从下单到支付再到回调处理,以及处理各项补偿和监听。面试官才会觉得你是真的对接了支付,而不是在描述一个 CRUD 流程。 + +## 二、业务流程 + +如图,本节我们来完成支付宝沙箱回调和补偿的流程; + +
+ +
+ +- 本节完成支付成功后的回调处理,更新数据库订单状态。同时发送MQ消息。比如一些发货场景,就是这个MQ发送出来之后做的处理。 +- 另外,我们在完成一些关于超时关单、掉单补偿的流程。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2547\350\212\202\357\274\232\345\211\215\347\253\257\351\241\265\351\235\242.md" "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2547\350\212\202\357\274\232\345\211\215\347\253\257\351\241\265\351\235\242.md" new file mode 100644 index 000000000..c1c84f643 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2547\350\212\202\357\274\232\345\211\215\347\253\257\351\241\265\351\235\242.md" @@ -0,0 +1,23 @@ +--- +title: 第3-7节:前端页面 +pay: https://t.zsxq.com/L1Fpx +--- + +# 《小型支付商城系统》第3-7节:前端页面 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +搭建简单的前端页面,自动轮训检查扫描登录状态,完成登录跳转商品下单页。 + +## 二、业务流程 + +如图,本节可以串联用户UI流程,登录下单到支付完成。 + +
+ +
\ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2548\350\212\202\357\274\232Docker\346\236\204\345\273\272\345\222\214\351\203\250\347\275\262.md" "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2548\350\212\202\357\274\232Docker\346\236\204\345\273\272\345\222\214\351\203\250\347\275\262.md" new file mode 100644 index 000000000..6afea2c72 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-3-mvc/\347\254\2548\350\212\202\357\274\232Docker\346\236\204\345\273\272\345\222\214\351\203\250\347\275\262.md" @@ -0,0 +1,26 @@ +--- +title: 第3-8节:Docker构建和部署 +pay: https://t.zsxq.com/IWhOM +--- + +# 《小型支付商城系统》第3-8节:Docker构建和部署 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +通过 Docker 给工程配置 Dockerfile、build.sh 文件对项目进行构建和部署,前端应用采用 Nginx 进行部署。 + +## 二、Docker 是什么? + +Docker 你可以把它当做一个小型的轻量的虚拟机(虚拟的电脑),它可以帮你屏蔽各类软件环境安装时候的复杂性,你只需要一段脚本,即可完成 mysql、redis、nginx 等等各类你能想到的软件的部署和卸载。—— 不知道多少伙伴自己电脑装完 MySQL 就不会卸载了! + +
+ +
+ +- 官网:[https://www.docker.com/](https://www.docker.com/) - 本地电脑直接安装即可。Windows 电脑因为不是 linux 所以需要开启 wsl2(百度/星球搜索有配置教程) +- 云教程:[https://bugstack.cn/md/road-map/docker.html](https://bugstack.cn/md/road-map/docker.html) - 也可以选择云服务器安装。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-4/\347\254\2541\350\212\202\357\274\232natapp \345\206\205\347\275\221\347\251\277\351\200\217.md" "b/docs/md/project/s-pay-mall/part-4/\347\254\2541\350\212\202\357\274\232natapp \345\206\205\347\275\221\347\251\277\351\200\217.md" new file mode 100644 index 000000000..5853c3a63 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-4/\347\254\2541\350\212\202\357\274\232natapp \345\206\205\347\275\221\347\251\277\351\200\217.md" @@ -0,0 +1,30 @@ +--- +title: 第4-1节:natapp 内网穿透 +pay: https://t.zsxq.com/qM7uL +--- + +# 《小型支付商城系统》第4-1节:natapp 内网穿透 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +教会伙伴们,了解内网穿透和使用内网穿透。 + +## 二、了解穿透 + +说白了,内网穿透,就是想办法让本地这个只有内网IP的服务,被外网访问。 + +曾经有个伙伴问我,我云服务器部署了服务,配置了本地的 192.168.1.109 IP 下的 xxl-job 怎么执行失败呢?这是因为我们本地可以访问公网IP地址,但公网IP没法访问你的内网地址。 + +
+ +
+ +- 内网穿透服务会部署在服务端,之后提供一个内网穿透的客户端,在本地启动。这样本地的frp客户端和frp服务端,就可以建立连接进行通信。 +- 有了这个通信的连接,用户访问到frp服务端,就可以把请求到的内容转发给客户端。客户端在请求本地的服务,把数据回传回去。这样,也就把本地的服务暴漏出去了。具体的详细实现,可以检索frp穿透设计。 + +> 内网穿透是一种技术手段,用于访问位于防火墙或路由器后面的本地网络(内网)中的设备或服务。通常情况下,内网中的设备无法直接通过公网(互联网)进行访问,从而实现隐私保护和安全性。内网穿透技术的目标是突破这一限制,使外部用户能够通过互联网访问内网中的服务或设备。 \ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-4/\347\254\2542\350\212\202\357\274\232\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\346\265\213\350\257\225\350\257\204\345\256\241\347\224\263\350\257\267.md" "b/docs/md/project/s-pay-mall/part-4/\347\254\2542\350\212\202\357\274\232\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\346\265\213\350\257\225\350\257\204\345\256\241\347\224\263\350\257\267.md" new file mode 100644 index 000000000..3a1cfa225 --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-4/\347\254\2542\350\212\202\357\274\232\345\276\256\344\277\241\345\205\254\344\274\227\345\217\267\346\265\213\350\257\225\350\257\204\345\256\241\347\224\263\350\257\267.md" @@ -0,0 +1,27 @@ +--- +title: 第4-2节:微信公众号,测试平台申请使用 +pay: https://t.zsxq.com/iaOEW +--- + +# 《小型支付商城系统》第4-2节:微信公众号,测试平台申请使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +因为课程后面会需要用到微信公众号作为登录、通知、消息发送的载体,所以本节先带着大家做下微信公众号的申请和简单的验签配置,可以满足消息自动回复操作。 + +## 二、平台说明 + +微信公众号,分为个人和企业,个人的公众号会有很多限制,基本只能使用最基础功能。企业的公众号需要有公司主体,可以使用全量功能,但需要一年¥300的认证费用。 + +为了使用到所有的公众号号接口能力,本节我们这里使用的是微信公众号测试平台,它直接支持所有的公众号接口能力。也方面我们学习使用。 + +平台地址:[https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index](https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index) - 每个人都可以申请。进入后会看到官网文档。 + +
+ +
\ No newline at end of file diff --git "a/docs/md/project/s-pay-mall/part-4/\347\254\2543\350\212\202\357\274\232\346\224\257\344\273\230\345\256\235\346\262\231\347\256\261\347\224\263\350\257\267.md" "b/docs/md/project/s-pay-mall/part-4/\347\254\2543\350\212\202\357\274\232\346\224\257\344\273\230\345\256\235\346\262\231\347\256\261\347\224\263\350\257\267.md" new file mode 100644 index 000000000..e9ee9e1ef --- /dev/null +++ "b/docs/md/project/s-pay-mall/part-4/\347\254\2543\350\212\202\357\274\232\346\224\257\344\273\230\345\256\235\346\262\231\347\256\261\347\224\263\350\257\267.md" @@ -0,0 +1,37 @@ +--- +title: 第4-3节:支付宝沙箱申请使用 +pay: https://t.zsxq.com/b0U7F +--- + +# 《小型支付商城系统》第4-3节:支付宝沙箱申请使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、本章诉求 + +申请支付宝沙箱环境,使用支付宝沙箱API,完成简单支付订单的创建、支付和接收回调消息。 + +其实不只支付宝沙箱,包括我们对接微信支付或者其他第三方支付,都是这样一个流程。之后对于没有企业资质的一般只能做支付宝沙箱模拟,或者想真实对接支付,就需要使用虎皮椒、蓝兔支付的三方服务结构。 + +蓝兔支付在星球里提供了对接的 sdk 文档,如果你需要做真实支付可以学习下。[《支付SDK设计和开发》](https://t.zsxq.com/19WqNkhr2) + +## 二、支付申请 - Alipay 沙箱支付 + +### 1. 沙箱应用 + +**支付宝|开放平台** 地址:[https://open.alipay.com/develop/manage](https://open.alipay.com/develop/manage) - `任何人都可以申请,不要企业资质` + +
+ +
+ +
+ +
+ +- 开启沙箱应用以后,还需要下载支付宝秘钥工具。秘钥工具创建的秘钥,需要填写到查看中。 +- 📢 【沙箱账号】,里面提供了后续在网页上支付时,输入的账号、密码和支付密码。 +- 📢 【沙箱工具】,里面提供了安卓版测试软件,可以在手机扫码支付。 diff --git a/docs/md/project/s-pay-mall/s-pay-mall.md b/docs/md/project/s-pay-mall/s-pay-mall.md new file mode 100644 index 000000000..19ced3664 --- /dev/null +++ b/docs/md/project/s-pay-mall/s-pay-mall.md @@ -0,0 +1,155 @@ +--- +title: 小型支付电商系统 +lock: no +--- + +# 小型支付电商系统 - 一套项目2套架构开发(MVC+DDD) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
项目:[https://t.zsxq.com/3X9GA](https://t.zsxq.com/3X9GA) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +🌻 在小傅哥带大家做的众多实战项目中,有些伙伴喜欢看 `DDD` 因为面试有的讲,有些伙伴想做 `MVC` 因为虽然知道 `DDD` 有亮点但初次接触怕学起来难。所以,这次我要一套项目,写出两套架构开发。你可以对比着学习,看看不同架构(`MVC&DDD`)如何设计和编码! + +
+ +
+ +**那咱们做什么项目呢?🤔** + +这是一套`小型的支付电商系统`,提取实际生产中核心的真实模块作为咱们的开发需求,同时也是面试中最为常问的流程。包括;`如何微信扫码鉴权登录` + `模板消息通知`、`怎么做支付宝交易打通`、`商品支付掉单如何处理`、`相关的任务补偿怎么操作`等。把这些需求分别通过 MVC 架构、DDD 架构,进行设计实现。让学习的伙伴,对照出不同架构的设计思路和开发差异,即完成业务需求,也提高编程架构思维。—— 同龄人的差异,就是你比别人站的高的时候,略微出手,就是那个赛道的将相王侯 👍🏻! + +两套架构工程的代码已经写完啦(录制视频中)!接下来小傅哥就来介绍下这套项目,你能获得的技术知识。 + +>文末有加入学习方式,还有优惠券可以使用。先到先得! + +## 一、能学到啥 + +这是一套完整具备核心链路的小型项目,我们不在同类编码上反复重复,只关注核心链路。所以你可以花费很少的时间,积累丰富的架构和编程经验。在这套内容学习中,积累核心技术的运用,包括; + +- 【前端】熟练使用,简单 HTML、DIV、CSS,对扫码登录、商品下单页面的构建。 +- 【前端】掌握 fetch 方式对后端接口的调用,处理相关的逻辑数据。 +- 【后端】熟练搭建 MVC 工程项目、理解各个分层模块作用,对 MVC 的设计方法有清楚的认识。 +- 【后端】熟练搭建 DDD 工程项目、以及 DDD 脚手架搭建项目。并对 DDD 设计方法有清楚的认知。 +- 【后端】理解 DDD 架构设计思维,这部分会有大量的内容进行讲解。再结合后续的实战,会对架构有更深入的认识。 +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对框架源码所提供的扩展接口具备运用能力。 +- 【后端】熟练使用模板设计模式,对商品下单的流程拆解和实现。 +- 【后端】深度理解登录、支付、下单,全流程的核心设计和实现,而不是那种CRUD学习个DEMO,我们对接真实支付! +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练使用 Nginx 配置转发服务,给前端应用在 Nginx 进行部署。 + +此外,小傅哥会把系统开发过程中的思考、设计、编码,录制⏺成完整的视频,让大家可以学习到的更多、更细、更深! + +## 二、项目介绍 + +本次项目是一个包括 `前后端 + Dev-Ops` 且小型的综合实战项目,基于 SpringBoot、MyBatis、Nginx、Docker、微信公众号、支付宝沙箱等开发的项目。非常适合小白伙伴和有DDD学习诉求的伙伴上手! + +本次项目铺设出来的内容并不大,但具备详细的核心流程,你可以通过一条完整链路学习到 MVC 和 DDD 的开发设计与编码差异。这是非常重要的。 + +### 1. 核心流程 + +
+ +
+ +### 2. 项目工程 + +
+ +
+ +- 一套项目需求,用两套架构开发。小傅哥,是真的想帮你提高架构思维! +- 我会在全程视频手把手的编码过程中,为你讲解 MVC 与 DDD 的设计,它们之间的对象设计,思维方式,编码结构。 + +### 3. 流程设计 + +#### 3.1 登录流程 + +
+ +
+ +#### 3.2 下单流程 + +
+ +
+ +### 4. 运行效果 + +
+ +
+ +### 5. 运行日志 + +```java +24-08-04.11:03:13.922 [http-nio-8092-exec-3] INFO LoginController - 生成微信扫码登录 ticket gQGq8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyT01QWXBJTDBjckcxUlFZbWhDMUgAAgR0765mAwQAjScA +24-08-04.11:11:00.291 [http-nio-8092-exec-7] INFO WeixinPortalController - 接收微信公众号信息请求or0Ab6ivwmypESVp_bYuk92T6SvU开始 + +1722741062 + + +3576043305420816385 + + +24-08-04.11:11:00.305 [http-nio-8092-exec-7] INFO WeixinPortalController - 接收微信公众号信息请求or0Ab6ivwmypESVp_bYuk92T6SvU完成 + +1722741062 + + +3576043305420816385 + + +24-08-04.11:11:12.374 [http-nio-8092-exec-9] INFO LoginController - 生成微信扫码登录 ticket gQGY8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyeGpIYm8yTDBjckcxUmktbXhDMU8AAgRS8a5mAwQAjScA +24-08-04.11:11:18.541 [http-nio-8092-exec-1] INFO AliPayController - 商品下单,根据商品ID创建支付单开始 userId:or0Ab6ivwmypESVp_bYuk92T6SvU productId:or0Ab6ivwmypESVp_bYuk92T6SvU +24-08-04.11:11:18.581 [http-nio-8092-exec-1] INFO HikariDataSource - HikariPool-1 - Starting... +24-08-04.11:11:18.778 [http-nio-8092-exec-1] INFO HikariDataSource - HikariPool-1 - Start completed. +24-08-04.11:11:19.177 [http-nio-8092-exec-1] INFO AbstractOrderService - 创建订单-完成,生成支付单。userId: or0Ab6ivwmypESVp_bYuk92T6SvU orderId: 3700032384239341 payUrl:
+ + +
+ +24-08-04.11:11:19.178 [http-nio-8092-exec-1] INFO AliPayController - 商品下单,根据商品ID创建支付单完成 userId:or0Ab6ivwmypESVp_bYuk92T6SvU productId:100010090091 orderId:3700032384239341 +``` + +## 三、项目大纲 + +**课程地址**:[https://t.zsxq.com/3X9GA](https://t.zsxq.com/3X9GA) + +- 第1部分:架构理论 + - 第1节:DDD 架构概念 + - 第2节:DDD 建模方法 + - 第3节:DDD 工程模型(含 MVC 对比) +- 第2部分:需求设计 + - 第1节:小型支付商城需求设计 + - 第2节:工程四色建模设计 + - 第3节:库表设计 +- 第3部分:功能实现 - MVC + - 第1节:MVC 工程框架搭建 + 基础配置 + Git 使用 + - 第2节:微信公众号鉴权 + - 第3节:登录功能设计实现 + - 第4节:商品下单 + - 第5节:对接支付 + - 第6节:支付回调处理 +- 第3部分:功能实现 - DDD + - 第1节:DDD 工程框架搭建 + 基础配置 + Git 使用 + - 第2节:DDD 重构,微信公众号鉴权 + - 第3节:DDD 重构,登录功能设计实现 + - 第4节:DDD 重构,商品下单 + - 第5节:DDD 重构,对接支付 + - 第6节:DDD 重构,支付回调处理 +- 第4部分:开发运维 + - 第1节:natapp 内网穿透 + - 第2节:微信公众号,测试平台申请 + - 第3节:支付宝沙箱申请 + - 第4节:发布上线 + +--- + +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! diff --git a/docs/md/project/xfg-fish-pond/xfg-fish-pond.md b/docs/md/project/xfg-fish-pond/xfg-fish-pond.md new file mode 100644 index 000000000..a07df3449 --- /dev/null +++ b/docs/md/project/xfg-fish-pond/xfg-fish-pond.md @@ -0,0 +1,200 @@ +--- +title: 钓鱼佬-网页游戏项目 +lock: no +--- + +# 钓鱼佬-网页游戏项目 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
项目:[https://t.zsxq.com/g57ja](https://t.zsxq.com/g57ja) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +哈哈哈,不是干项目,就是在干项目的路上。除了正式的全程视频手把手带着做的,小傅哥也陆续的开始更新了大厂的 PRD 资料、市面上火热的外卖,进行DDD重构编写。这次又带来一个轻量基于 SpringBoot + Redis 实现的, 🎮 🐟 **钓鱼佬,网页游戏项目** 。 + +这个项目还在线部署过,让伙伴们体验效果。😄 下面有介绍,你可以自己部署体验。 + +
+ +
+ +**为啥会有这样一个项目呢?🤔** + +在互联网应用场景中,京东、淘宝、拼多多等,我们时常会看到一些游戏化玩法的场景,如;集卡、浇水、养动物,通过这些场景增强用户粘性,以及提高转化率。因为这些游戏化场景的背后,还是一套套的营销玩法,最终把一些券、积分、物品发放给用户。 + +而小傅哥的社群,是按照互联网公司级别的一个项目组进行规划、设计、落地的。在这个社群有非常丰富的项目资源,已经提供的有,业务的 - 交易、拼团、营销、openai应用、ai agent、IM等,组件的 - api网关、扳手工程、动态线程池、透视业务监控、支付sdk组件、IntelliJ IDEA 插件等。 + +那么为了让小傅哥的社群,给大家提供的项目,是一个有立体化分层关系结构的,可以通过对接形成各个环节的流程闭环。所以小傅哥在全程视频手把手带着做以外,又增加了 PRD 文档、吉祥外卖、这次又增加一个钓鱼佬娱乐的项目。 + +**基本你在小傅哥的这个社群,就等同于你在一个一线互联网大厂进行一次实习!** 接下来,小傅哥来介绍下这个小游戏项目。 + +## 一、运行效果 + +该项目是一个轻量的网页游戏应用项目,提供了 Java、Python 2个版本,数据使用 Redis 进行存储。以娱乐化的方式,让伙伴学习到 Redis 的相关技术。此外,你可以把这样的一个项目,与星球里的大营销(积分、兑换、返利、抽奖)进行对接使用。在描述上,是以调研了xxx互联网公司的xxx游戏化场景,进行设计实现的这样一整套系统。 + +### 1. 登录 + +
+ +
+ +### 2. 画鱼 + +
+ +
+ +### 3. 鱼塘 + +
+ +
+ +- 鱼塘页面,可以看到所有登录用户投放进来的鱼,还可以钓鱼🎣。也许会把别人的鱼钓鱼到自己的鱼篓里。当然,钓鱼是需要积分的,你需要点击签到获得积分。 +- 页面里还有一些问题的话术和救赎时间(上班摸鱼等于救赎自己!) +- 本次设计的页面和后端的代码,都比较简单,后端也仅有一层架构。如果想添加功能,可以自己实现,也可以使用 trae.ai、Cursor 等工具,来辅助开发。 +- 哈哈哈,还有伙伴提建议,让这些鱼,可以支持在线售卖。如果你还有很多想法,可以拿到这套代码,做一些扩展。 + +### 4. 居民 + +
+ +
+ +在鱼塘居民下,可以看到各个伙伴画的各种鱼。你可以对这些鱼点一个喜欢或者不喜欢。 + +## 二、项目介绍 + +### 1. 工程代码 + +
+ +
+ +- 地址:[https://gitcode.net/KnowledgePlanet/xfg-fish-pond](https://gitcode.net/KnowledgePlanet/xfg-fish-pond) - `文末加入小傅哥社群,即可获得全部代码,以及其他17个实战项目` +- 说明:项目提供了前端代码,SpringBoot 服务端代码,以及一个 python 版本的代码。 + +### 2. 项目启动 + +
+ +
+- 首先,在 xfg-fish-pond-app 下,resources/application-dev.yml 配置 redis 链接信息。 +- 之后,启动项目后,点击打开页面即可访问了。如果你要部署云服务器,可以通过 docker 构建镜像在云服务器部署即可。 + +> 如果你正在做星球的项目,那么很多都可以与这套项目做微服务对接。如;大营销的积分、兑换、返利、抽奖,也可以把 ai 场景对接进来,还可以把小型支付对接进来,买卖鱼的交易。 + +## 三、技术学习 + +本套项目的数据存储都是基于 Redis 实现的,你可以在这套代码中学习到非常多的 Redis 应用技术。 + +### 1. 用户会话管理(UserController) + +**登录时存储用户信息:** +```java +RMap userMap = redisService.getMap(userKey); +userMap.put("userId", userId); +userMap.put("username", username); +userMap.put("loginTime", LocalDateTime.now().toString()); +userMap.expire(86400, TimeUnit.SECONDS); // 24小时过期 +``` + +**用户列表管理:** +```java +redisService.addSetMember(USER_LIST_KEY, userId); // 添加到在线用户列表 +redisService.removeSetMember(USER_LIST_KEY, userId); // 从在线用户列表移除 +``` + +### 2. 积分系统(PointsController) + +**积分存储和操作:** +```java +// 获取用户积分 +int points = Integer.parseInt(pointsMap.getOrDefault("points", "0")); + +// 更新积分 +pointsMap.put("points", String.valueOf(newPoints)); +pointsMap.put("lastUpdated", LocalDateTime.now().toString()); +``` + +**签到记录:** +```java +String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); +RMap signMap = redisService.getMap(signKey); +signMap.put(today, LocalDateTime.now().toString()); +``` + +**积分变动记录:** +```java +String recordValue = points + "|" + type + "|" + reason + "|" + LocalDateTime.now().toString(); +recordsMap.put(recordId, recordValue); +``` + +### 3. 鱼类管理(FishController) + +**添加鱼到池塘:** +```java +RMap fishMap = redisService.getMap(fishKey); +fishMap.put("name", fishName); +fishMap.put("emoji", emoji); +fishMap.put("userId", userId); +fishMap.put("createTime", LocalDateTime.now().toString()); + +// 添加到鱼类列表 +redisService.addSetMember(FISH_LIST_KEY, fishId); +``` + +### 4. 钓鱼系统(FishingController) + +**钓鱼记录:** +```java +String recordValue = success + "|" + fishEmoji + "|" + fishName + "|" + fishRarity + "|" + + LocalDateTime.now().toString() + "|" + transferred + "|" + + originalOwner + "|" + fishId; +recordsMap.put(recordId, recordValue); +``` + +**鱼篓管理:** +```java +String fishValue = emoji + "|" + name + "|" + rarity + "|" + + LocalDateTime.now().toString() + "|" + originalFishId + "|" + image; +basketMap.put(fishId, fishValue); +``` + +### 5. 投票系统(VoteController) + +**用户投票记录:** +```java +RMap userVotesMap = redisService.getMap(userVotesKey); +userVotesMap.put(fishId, voteType); // "like" 或 "dislike" +``` + +**投票统计:** +```java +fishVotesMap.put("likes", String.valueOf(currentLikes)); +fishVotesMap.put("dislikes", String.valueOf(currentDislikes)); +fishVotesMap.put("score", String.valueOf(currentLikes - currentDislikes)); +``` + +### 6. 系统监控(SystemController) + +**Redis 健康检查:** +```java +// 设置测试值 +redisService.setValue("health_check", "ok"); + +// 读取测试值 +String value = redisService.getValue("health_check"); + +// 删除测试值 +redisService.remove("health_check"); + +// 获取用户总数 +Map userList = redisService.getMapAll("user_list"); +``` + +好啦,如果你现在需要实战项目锻炼自己的能力,积累自己的储备,那么一定要加入小傅哥知识星球(码农会锁)。这里有非常多的类型丰富的项目,带着你一起起飞🛫! + diff --git a/docs/md/road-map/13scan-jdumpspider.md b/docs/md/road-map/13scan-jdumpspider.md new file mode 100644 index 000000000..40fe15f29 --- /dev/null +++ b/docs/md/road-map/13scan-jdumpspider.md @@ -0,0 +1,229 @@ +--- +title: w13scan-jdumpspider 安全漏洞扫描 +lock: need +--- + +# 安全漏洞扫描,他怎么拿到了我的数据库密码? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +你有没有想过,你部署到线上的 SpringBoot 应用,在做互联网`安全人员`的手里,已经成了"小肉鸡"?可怕的攻击不一定是 DDOS 玩命的访问你的网站,消耗你的 CDN 流量。而是拿到你的应用系统的一系列配置,如;数据库账号密码和连接地址、微信公众平台核心配置、OpenAI APIKey。可以说这些东西一暴漏,那你得遭老罪喽。 + +
+ +
+ +**那这是怎么被发现暴漏的呢?🤔** + +做安全的有一套应用漏洞检查扫描的软件,它通过启动被动流量扫描服务,通过代理的方式,挂载到自身的浏览器。日常的访问一些网站时,所有的流量节点都会进入到被动扫描代理服务中,之后消耗这些流量节点,依次检测每个地址下可能发生的;XSS、jsonp信息泄露、sql注入、http smuggling 走私攻击,以及可以根据各类组件提供的漏洞问题做扫描补偿。这样就可以拿到你的网站都有哪些安全漏洞问题了。 + +这里有一个最典型的就是 SpringBoot Actuator 安全点暴漏,这东西是可以拿到你的 heapdump,之后再通过 JDumpSpider 扫描,也就可以拿到你应用中的数据源、配置文件、Redis配置、ShiroKey等信息。此时如果你的数据库恰好没有配置IP访问限制,那么直接远程连接登录进去,一顿操作了。 + +那么整个这样一个流程怎么玩一下呢?这东西只有亲自上下手,才会感受到它的恐怖。接下来,小傅哥就教你做被动安全扫描操作和 heapdump 下载分析提取数据库密码。 + +## 一、流程介绍 + +关于被动扫描系统安全漏洞的服务系统有很多,包括 w13scan 免费开源的,也有 xray 付费的,还有的是公司或者个人独立开发的。本节我们主要使用 w13scan 作为安全漏洞扫描的案例。 + +
+ +
+ +- 首先, w13scan 的流程是这样,你需要先启动一个这样的被动扫描服务,之后再你的浏览器中配置网页请求代理地址。这个代理地址就是 w13scan 服务。 +- 那么,这个时候你所有在浏览器发起的请求,都会经过 w13scan 走一圈。这样,就可以把请求的网址进行一系列安全扫描检查,查看是什么样的漏洞。 +- 另外,w13scan 不只是可以被动扫描还可以主动配置路径扫描一个固定的网站地址检测。 +- 最后,w13scan 会把扫描到的安全漏洞以html方式展示出来,我们可以在这个网页上查看漏洞检测结果。也就可以在进一步对存在的漏洞进行挖局,如拿到 heapdump 在解析它的信息了。 + +## 二、案例工程 + +这里小傅哥为你提供了完整的案例测试工程,你可以直接完整体验。`你需要有 docker 环境或者 Python 3.6+` + +
+ +
+ +地址:[https://github.com/fuzhengwei/xfg-dev-tech-w13scan-jdumpspider](https://github.com/fuzhengwei/xfg-dev-tech-w13scan-jdumpspider) + +- 首先,你需要下载这个工程到本地电脑,便于安装环境和测试。 +- 如图,已经为你描述了案例的功能。 + +## 三、被动扫描 + +**源码**:[https://github.com/fuzhengwei/w13scan](https://github.com/fuzhengwei/w13scan) - 小傅哥 fork 的 w13scan 并做了镜像打包。源码文档里提供了安装和使用说明。 + +### 1. 组件安装 - docker 方式 + +```java +# docker-compose -f docker-compose.yml up -d +version: '3.8' +services: + w13scan: + image: fuzhengwei/w13scan:1.0 + container_name: w13scan + ports: + - "7778:7778" + volumes: + - ./output:/w13scan/W13SCAN/output + entrypoint: ["python3", "w13scan.py", "-s", "0.0.0.0:7778", "--html"] + tty: true +``` + +
+ +
+ +- 你可以通过脚本 `docker-compose -f docker-compose.yml up -d` 脚本执行 w13scan 的服务安装。 +- 另外,如果你不能执行 docker-compose 还可以通过提供的 `docker run -d --name w13scan -p 7778:7778 -v ./output:/w13scan/w13scan/output fuzhengwei/w13scan:1.0 python3 w13scan.py -s 127.0.0.1:7778 --html` + +### 2. 组件安装 - Python 方式 + +这个方式就把代码拉到自己本地,进行构建和启动。 + +
+ +
+ +```java +git clone https://github.com/fuzhengwei/w13scan.git +cd w13scan # 进入git目录 +pip3 install -r requirements.txt +cd W13SCAN # 进入源码目录 +python3 w13scan.py -h +``` + +```java +python3 w13scan.py -s 127.0.0.1:7778 --html # 端口可省略,默认为7778,开启--html即实时生成html报告 +``` + +- 安装完成后启动,也可以看到一个 ` HTTPServer is running at address('0.0.0.0','7778')......` 日志。 + +### 3. 代理配置 + +无论使用那种方式部署,启动完成后,都需要给浏览器配置代理。其实也就是你的电脑的网络哪里配置下代理。 + +
+ +
+ +- 给你的网络配置代理服务,我这里是 mac 电脑点网络wifi那里进入的配置。你可以找到你的位置进行添加。 + +### 4. 下载证书 + +下载:[http://w13scan.ca/](http://w13scan.ca/) + +一定是你服务启动成功,代理配置成功。那么你在点击下载后,才能下载一个 `download.crt` 文件。之后双击点开信任即可。 + +### 5. 开始使用 + +一切配置成功后,你可以在浏览器访问你想检查漏洞的网址,一段时间后就可以看到被动扫描组件检测的数据信息了。之后进入到 `w13scan/W13SCAN/output` 查看检测文件了。这些 html 文件直接用浏览器打开和刷新即可。 + +
+ +
+ +
+ +
+ +- 现在你就看到一些展示的系统安全漏洞了,再根据这些漏洞进行**友好的访问** + +## 四、heapdump 下载 + +测试前你可以先启动 xfg-dev-tech-w13scan-jdumpspider 应用。这样就可以做后面的流程以及获取 heapdump 日志了。 + +### 1. 问题背景 + +测试工程中的 yml 配置。这个配置为了配合普罗米修斯做监控使用的,但要注意如果只是这样配置,是很有风险的。 + +```java +# 监控 +management: + endpoints: + web: + exposure: + include: "*" + endpoint: + health: + show-details: always + metrics: + export: + prometheus: + enabled: true + prometheus: + enabled: true +``` + +- 如果你的 SpringBoot 系统配置了这样的监控,并且没有配置相关的安全校验,Spring Security 那么现在你就暴漏了自己的端点。 + +### 2. 端点访问 + +地址:[http://127.0.0.1:8091/actuator/env](http://127.0.0.1:8091/actuator/env) + +
+ +
+ +- 这个地址,就可以由被动扫描检测出来。不过不同的被动扫描组件检测点不同,有些是没有的。 + +### 3. 下载 dump + +地址:[http://127.0.0.1:8091/actuator/heapdump](http://127.0.0.1:8091/actuator/heapdump) + +
+ +
+ +- 把工程的 heapdump 文件下载下来。下载后可以放到案例工程 docs 里 JDumpSpider-1.1-SNAPSHOT-full.jar 的同层路径下。 + +## 五、heapdump 解析 + +以前小傅哥带着大家做一篇[《Eclipse MAT 分析 Java heap space dump 日志》](https://bugstack.cn/md/road-map/dump-mat.html) 这个也能分析 dump 日志查看系统中的核心数据。不过这里我们选择个更针对性的 JDumpSpider 做日志分析,直接提取核心配置。 + +源码:[https://github.com/whwlsfb/JDumpSpider](https://github.com/whwlsfb/JDumpSpider) - 有相关的使用说明。 + +命令:`java -jar JDumpSpider-1.1-SNAPSHOT-full.jar heapdump` + +```java +/bin/zsh /xiaofuge/develop/github/xfg-dev-tech-w13scan-jdumpspider/docs/jdumpspider.sh +(base) xiaofuge@ZBMac-GV47H1GXD docs % /bin/zsh /Users/xiaofuge/Documents/develop/github/xfg-dev-tech-w13scan-jdumpspider/docs/jdumpspider.sh +=========================================== +SpringDataSourceProperties +------------- +password = 123456 +driverClassName = com.mysql.cj.jdbc.Driver +url = jdbc:mysql://127.0.0.1:13306/road_map?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true +username = root + +=========================================== +WeblogicDataSourceConnectionPoolConfig +------------- +not found! + +=========================================== +MongoClient +------------- +not found! + +=========================================== +AliDruidDataSourceWrapper +------------- +not found! + +=========================================== +HikariDataSource +------------- +not found! +``` + +- 命令执行后可以看到你的工程中连接数据库的配置,密码也会直接暴漏出来。其他的配置如果你有添加,也会一并给你拿出来。**底裤给你扒掉** + +## 六、安全建议 + +- 对于自己上线的应用,尤其独立开发者,一定多进行安全扫描。 +- 不要无密码暴漏自己的应用,包括任何监控、数据采集、以及第三方组件。 +- 数据库、缓存、文件等连接,在云服务器要配置上可访问IP限制。这样就算底裤拔掉了,也还有一个玻璃罩。只能看,不能连。 \ No newline at end of file diff --git a/docs/md/road-map/1panel.md b/docs/md/road-map/1panel.md new file mode 100644 index 000000000..f6252cc43 --- /dev/null +++ b/docs/md/road-map/1panel.md @@ -0,0 +1,85 @@ +--- +title: 1Panel 运维面板 +lock: need +--- + +# 1Panel 云服务器运维面板 | 操作云服务器,这套东西还适合小白的! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在编程的这条路上,大家都经历过小白阶段。在小白阶段,即使是简简单单的环境安装,也都会遇到一堆堆的报错。所以最开始学习编程的时候,我们都希望找到那些简单、方便、好用的工具,让我们线上手✋🏻。 + +
+ +
+ +**那云服务器操作有Easy工具吗?🤔** + +小傅哥,在最早使用云服务器的时候,为了快速简单的搭建博客对外使用,选择过宝塔面板。这个东西虽然不大,但对于最开始云服务器操作能力和对编程的理解都比较偏弱的伙伴还是非常好用的。 + +但当大家开始使用 Linux + Docker,对一些列分布式环境继续安装和项目构建部署时,有些小白就会遇到一些各类小问题了。只有写好了 docker compose 和对应的文档,才能让小白快速完成安装。但有一些小白用到的新的服务安装时,自己不会写 docker compose 就会遇到新问题了。所以,今天我们来体验下一个新的云服务器运维面板,非常好用。 + +## 一、面板介绍 + +1Panel 是新一代的 Linux 服务器运维管理面板,它可以帮助你非常方便管理云服务器上的软件操作。`也不需要再配置 docker 代理了。` + +
+ +
+ +- 用户可以通过 Web 图形界面轻松管理 Linux 服务器,实现主机监控、文件管理、数据库管理、容器管理等功能。 +- 深度集成开源建站软件 WordPress 和 Halo,域名绑定、SSL 证书配置等一键搞定。 +- 精选上架各类高质量的开源工具和应用软件,协助用户轻松安装并升级。 +- 基于容器管理并部署应用,实现最小的漏洞暴露面,同时提供病毒防护、防火墙和日志审计等功能。 +- 支持一键备份和恢复,用户可以将数据备份到各类云端存储介质,永不丢失。 + +> 好啦,介绍的还挺🐂皮的,咱们来体验下! + +## 二、安装软件 + +### 1. 安装脚本 + +```java +curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sh quick_start.sh +``` + +- Centos 一键安装脚本,复制到云服务器直接执行即可。 +- 其他的云服务器系统安装脚本:https://1panel.cn/docs/installation/online_installation/ +- 2c2g 云服务器,活动价,一年26元 [https://618.gaga.plus](https://618.gaga.plus) + +### 2. 安装效果 + +
+ +
+ +- 安装过程中你可以顺序点回车即可,注意是否配置镜像加速,因为我配置过,所以没有配置。你可以选 Y 统一配置镜像加速。 +- 安装完成后,会得到一个外网地址,端口是默认的 19166,同时给会你用户和密码。注意,需要在云服务器开放端口(安全组/防火墙)19166 另外账号密码可以在登录后修改。 + +## 三、软件功能 + +### 1. 查看容器 + +
+ +
+ +- 你的 Linux 安装 Docker 会直接被 1Panel 接管。在 1Panel 会看到你安装过的软件。 +- 同时可以看到每个软件占用的 CPU、内存,这个还是非常好用的。 + +### 2. 安装软件 + +
+ +
+ +- 在 1Panel 的应用商店里,可以按照你需要的各类软件(它的覆盖度还是非常全的),包括常用的:Nginx、MySQL、MQ、Nacos、PHPAdmin等等,都是有的。 +- 如图,是个 OneAPI 它是用于访问兼容访问各类其他 OpenAI 的软件的。比如,文心一言、通义千问、讯飞星火、智谱 ChatGLM、腾讯混元,都可以按照 ChatGPT 的格式进行使用啦。 + +>好啦,你可以美滋滋的去体验下啦!🌶 + diff --git a/docs/md/road-map/a2a.md b/docs/md/road-map/a2a.md new file mode 100644 index 000000000..8974a9fc5 --- /dev/null +++ b/docs/md/road-map/a2a.md @@ -0,0 +1,388 @@ +--- +title: a2a +lock: need +--- + +# A2A,实际跑起来是什么样? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +chatgpt(22年)-> LLM 进阶(23年)-> MCP 协议(24年)-> Skils 技能(25年)-> A2A 标准(25年),这几年 AI 发展的非常迅速,定义了协议,开发了组件,发布了产品。每个阶段,都有非常牛皮的代表性的内容,车速非常快🚌,不知道在坐的各位有没有掉队。 + +
+ +
+ +针对这些 LLM 大模型的相关知识,小傅哥也分别提供了相关实战类项目,包括;**AI 问答助手(23年)**、**OpenAi(ChatGPT/ChatGLM) 微服务应用体系构(23年)**、**OpenAI 代码自动评审组件(24年)**、**AI Agent 智能体 - 拖拉拽(25年)**、**AI MCP Gateway 网关服务系统(25年)**、**AI Agent 脚手架 + 场景应用(26年)**,通过这些实战项目,实践各类标准协议和技术组件(spring ai、google adk...)的使用,以及提供非常出色的解决方案。 + +因为后续的 ai agent 智能体项目,还会涉及到 a2a 协议,所以先给大家做个相关技术的使用方便大家快速实践理解。 + +>🧧 文末提供了20个实战项目(6个AI、5个业务、8个组件、1套源码),以及各类编程技术小册等。欢迎一起加入学习。 + +## 一、A2A 是什么? + +A2A (Agent2Agent) 协议是 Google于2025年4月推出、并由 Linux基金会托管的开源开放标准。它作为AI智能体之间的“通用语言”,旨在实现不同框架、不同厂商构建的智能体(Agent)间跨平台发现、通信与协作。 + +随后25年底,Google 发布了 [a2a adk](https://central.sonatype.com/artifact/com.google.adk/google-adk-a2a/0.4.0) 组件(迭代速度很快)。之后借助代理开发工具包 (Google ADK),我们可以构建复杂的多代理系统,其中不同的代理需要使用代理对代理 (A2A) 协议进行协作和交互!本节提供了一个全面的指南,指导您构建强大的多代理系统,使代理能够安全高效地进行通信和协作。 + +框架:[https://google.github.io/adk-docs/a2a/](https://google.github.io/adk-docs/a2a/) + +源码:[https://github.com/google/adk-java](https://github.com/google/adk-java) + +资料:[https://www.ibm.com/cn-zh/think/topics/agent2agent-protocol](https://www.ibm.com/cn-zh/think/topics/agent2agent-protocol) + +文档:[https://a2aprotocol.ai/docs/](https://a2aprotocol.ai/docs/) + +--- + +如图,整套智能体架构(含A2A); + +
+ +
+ +- 左侧,是 LLM 模型智能体构建,基于 Spring AI 框架实现。这部分包括,AiApi 使用、RAG、MCP、Skills 的组合构建。 +- 右侧,是 AI Agent 智能体编排,基于 Google ADK 框架实现。这套框架,提供了智能体工作流组装,插件回调钩子等各项基于 Google 发布的协议进行构建的。 +- 之后,右上方,是 Google ADK 26年基于 A2A 协议实现的框架组件,方便我们设计出远程的 A2A 服务对接,把远程服务,转换为本地功能组件,编排进智能体中。因此 A2A 协议的作用,就是让一个远程的智能体,可以像构建本地智能体一样,连接起来进行使用。 + +> 基于 a2a 的协议,也有很多三方框架,但我们需要的是一个整体的解决方案,单一一个功能,还不能解决所有问题。所以这里优先选择 google adk 框架。 + +## 二、实践案例 + +### 1. 编程环境 + +- jdk 17 +- google adk 0.8 +- spring ai 1.1.0-M3 + +>相关的版本包,已经在测试工程中的 pom 里引入了。可以打开工程查看。 + +### 2. 工程结构 + +
+ +
+ +- 工程:[https://github.com/fuzhengwei/xfg-dev-tech-google-adk-a2a-server](https://github.com/fuzhengwei/xfg-dev-tech-google-adk-a2a-server) +- 说明:基于 Google ADK 框架,构建 A2A 服务端和客户端。注意,服务端的启动方式为 `quarkus.sh` 方式启动服务,之后使用客户端连接。 +- 注意:这部分还有一些关于关于 Spring AI + Google ADK 的基础使用知识,是在 ApiTest 中有案例。也可以学习项目 [《AI Agent 脚手架 + 场景应用》](https://bugstack.cn/md/project/ai-agent-scaffold/ai-agent-scaffold.html) + +### 3. 测试案例 + +```java +public class ApiTest { + + public static void main(String[] args) throws Exception { + OpenAiApi openAiApi = OpenAiApi.builder() + .baseUrl("https://apis.****.cn") + .apiKey("sk-tIYdEUUnMqKX3bRf756a31449a4942***需要配置你的key") + .completionsPath("v1/chat/completions") + .embeddingsPath("v1/embeddings") + .build(); + + ChatModel chatModel = OpenAiChatModel.builder() + .openAiApi(openAiApi) + .defaultOptions(OpenAiChatOptions.builder() + .model("gpt-4o") + .build()) + .build(); + + // agent 测试 + LlmAgent agent = LlmAgent.builder() + .name("test") + .description("Chess coach agent") + .model(new SpringAI(chatModel)) + .instruction(""" + You are a knowledgeable chess coach + who helps chess players train and sharpen their chess skills. + """) + .build(); + + InMemoryRunner runner = new InMemoryRunner(agent); + + Session session = runner + .sessionService() + .createSession("test", "fzw") + .blockingGet(); + + Flowable events = runner.runAsync("fzw", session.id(), + Content.fromParts(Part.fromText("1+1"))); + + System.out.print("\nAgent > "); + events.blockingForEach(event -> System.out.println(event.stringifyContent())); + } + +} +``` + +- 这是一个 Spring AI + Google ADK 构建的简单智能体,由 LlmAgent 构建时候,创建模型 `new SpringAI(chatModel)` 关联到 Spring AI 框架。 +- 之后,由 Google ADK 构建的智能体,使用内存记忆,创建会话之后进行测试。也可以学习项目 [《AI Agent 脚手架 + 场景应用》](https://bugstack.cn/md/project/ai-agent-scaffold/ai-agent-scaffold.html) 锻炼使用相关内容。 + +### 4. A2A 测试 + +#### 4.1 服务端 + +这部分内容的学习,可以打开案例代码,方便对比。 + +##### 4.1.1 服务提供 + +```java +@ApplicationScoped +public class AgentExecutorProducer { + + @ConfigProperty(name = "my.adk.app.name", defaultValue = "default-app") + String appName; + + @Produces + public AgentExecutor agentExecutor() { + return new com.google.adk.a2a.executor.AgentExecutor.Builder() + .agent(Agent.ROOT_AGENT) + .appName(appName) + .sessionService(new InMemorySessionService()) + .artifactService(new InMemoryArtifactService()) + .agentExecutorConfig(AgentExecutorConfig.builder().build()) + .build(); + } + +} +``` + +- 这部分是把智能体提供出去,通过 google adk 框架,创建 AgentExecutor 实例。 +- 之后 Agent.ROOT_AGENT 类似于上面的测试案例,智能体构建的部分。你可以创建任何之后通过 AgentExecutor 发布出去。 + +##### 4.1.2 卡片提供 + +```java +@ApplicationScoped +public class AgentCardProducer { + + @Produces + @PublicAgentCard + public AgentCard agentCard() { + try (InputStream is = getClass().getResourceAsStream("/agent/agent.json")) { + if (is == null) { + throw new RuntimeException("agent.json not found in resources"); + } + + // Read the JSON file content + String json = new String(is.readAllBytes(), StandardCharsets.UTF_8); + + // Use the SDK's built-in mapper to convert JSON string to AgentCard record + return Utils.OBJECT_MAPPER.readValue(json, AgentCard.class); + + } catch (Exception e) { + throw new RuntimeException("Failed to load AgentCard from JSON", e); + } + } + +} +``` + +**/agent/agent.json** + +```java +{ + "capabilities": {"streaming": true}, + "defaultInputModes": ["text/plain"], + "defaultOutputModes": ["application/json"], + "description": "一个专门检查数字是否为素数的智能体。它可以高效地确定单个数字或数字列表的素性。", + "name": "check_prime_agent", + "skills": [ + { + "id": "prime_checking", + "name": "Prime Number Checking", + "description": "使用高效的数学算法检查列表中的数字是否为素数", + "tags": ["mathematical", "computation", "prime", "numbers"] + } + ], + "preferredTransport": "JSONRPC", + "url": "http://localhost:9090", + "version": "1.0.0" +} + +``` + +- 这里要构建一个智能体卡,A2A 的协议中,把服务包装成一个卡片的概念。 +- agent.json 配置的是 `Agent.ROOT_AGENT` 智能体的信息。 +- 如果你之前学习过小傅哥的 AI MCP 网关项目,会对 JSONRPC 有印象, MCP 协议和 A2A 协议,都是走到 JSONRPC 标准进行的通信。 + +#### 4.2 客户端 + +##### 4.2.1 构建端 + +```java +public final class A2AAgent { + + private static final Random RANDOM = new Random(); + + private static final OpenAiApi openAiApi = OpenAiApi.builder() + .baseUrl("https://apis.****.cn") + .apiKey("sk-tIYdEUUnMqKX3bRf756a31449a4942***需要配置你的key") + .completionsPath("v1/chat/completions") + .embeddingsPath("v1/embeddings") + .build(); + + private static final ChatModel chatModel = OpenAiChatModel.builder() + .openAiApi(openAiApi) + .defaultOptions(OpenAiChatOptions.builder() + .model("gpt-4.1") + .build()) + .build(); + + @SuppressWarnings("unchecked") + public static ImmutableMap rollDie(int sides, ToolContext toolContext) { + ArrayList rolls = + (ArrayList) toolContext.state().computeIfAbsent("rolls", k -> new ArrayList<>()); + int result = RANDOM.nextInt(Math.max(sides, 1)) + 1; + rolls.add(result); + return ImmutableMap.of("result", result); + } + + public static final LlmAgent ROLL_AGENT = + LlmAgent.builder() + .name("roll_agent") + .model(new SpringAI(chatModel)) + .description("处理不同面数骰子的投掷。") + .instruction( + """ + 当被要求掷骰子时,始终调用 roll_die 工具并指定面数(如果未指定,默认为 6)。不要编造结果。 + """) + .tools(ImmutableList.of(FunctionTool.create(A2AAgent.class, "rollDie"))) + .build(); + + public static LlmAgent createRootAgent(String primeAgentBaseUrl) { + // 远程 agent + BaseAgent primeAgent = createRemoteAgent(primeAgentBaseUrl); + + // 本地 agent + return LlmAgent.builder() + .name("root_agent") + .model(new SpringAI(chatModel)) + .instruction( + """ + 你可以在本地掷骰子,并将素数检查委托给远程的 prime_agent。 + 1. 当用户要求掷骰子时,将请求路由给 roll_agent。 + 2. 当用户要求检查素数时,委托给 prime_agent。 + 3. 如果用户要求先掷骰子然后检查,先调用 roll_agent,然后将结果传给 prime_agent。 + 在讨论素数之前,始终先简述骰子结果。 + """) + .subAgents(ImmutableList.of(ROLL_AGENT, primeAgent)) + .build(); + } + + private static BaseAgent createRemoteAgent(String primeAgentBaseUrl) { + + String agentCardUrl = primeAgentBaseUrl + "/.well-known/agent-card.json"; + AgentCard publicAgentCard = + new A2ACardResolver(new JdkA2AHttpClient(), primeAgentBaseUrl, agentCardUrl).getAgentCard(); + + Client a2aClient = + Client.builder(publicAgentCard) + .withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig()) + .clientConfig( + new ClientConfig.Builder() + .setStreaming(publicAgentCard.capabilities().streaming()) + .build()) + .build(); + + return RemoteA2AAgent.builder() + .name(publicAgentCard.name()) + .a2aClient(a2aClient) + .agentCard(publicAgentCard) + .build(); + } + +} +``` + +- createRootAgent 是构建入口,一个是创建 createRemoteAgent 远程智能体,一个是 `LlmAgent.builder()` 构建本地智能体。之后在构建本地智能体的时候,通过 subAgents 把远程智能体填充到本地的智能体里去了。这个编排的方式有多种多样的。 +- createRemoteAgent 远程构建,这是固定协议路径 `/.well-known/agent-card.json`拿到智能体卡片以后(一个卡片上有各类的信息),之后通过 a2aClient 创建客户端,在通过 RemoteA2AAgent 完成本地智能体的转换。 + +##### 4.2.2 使用端 + +```java +public final class A2AAgentRun { + private final String userId; + private final String sessionId; + private final Runner runner; + + public A2AAgentRun(BaseAgent agent) { + this.userId = "test_user"; + String appName = "A2AAgentApp"; + this.sessionId = UUID.randomUUID().toString(); + + InMemoryArtifactService artifactService = new InMemoryArtifactService(); + InMemorySessionService sessionService = new InMemorySessionService(); + this.runner = + new Runner(agent, appName, artifactService, sessionService, /* memoryService= */ null); + + ConcurrentMap initialState = new ConcurrentHashMap<>(); + var unused = + sessionService.createSession(appName, userId, initialState, sessionId).blockingGet(); + } + + // ... 省略部分 + + public static void main(String[] args) throws InterruptedException { + BaseAgent agent = A2AAgent.createRootAgent("http://localhost:9090"); + A2AAgentRun a2aRun = new A2AAgentRun(agent); + + List events = + a2aRun.run("掷一个6面的骰子。").toList().timeout(90, TimeUnit.SECONDS).blockingGet(); + + events.forEach(A2AAgentRun::printOutEvent); + + events = + a2aRun.run("这是素数吗?").toList().timeout(90, TimeUnit.SECONDS).blockingGet(); + + events.forEach(A2AAgentRun::printOutEvent); + } + +} +``` + +- A2AAgentRun 测试入口,连接远程的智能体,之后做相关的调用验证。 + +## 三、测试验证 + +### 1. 前置准备 + +1. IntelliJ IDEA 右侧的 maven 点击执行 clean -> install 构建 +2. 通过命令启动服务,`mvn quarkus:dev -pl xfg-dev-tech-app` + +### 2. 访问服务 + +#### 2.1 服务首页 + +
+ +
+ +- 地址:[http://localhost:9090/q/dev-ui/welcome](http://localhost:9090/q/dev-ui/welcome) +- 说明:访问首页,localhost:9090 会看到上面的地址信息,这个是 a2a 协议的入口。 + +#### 2.2 服务协议 + +
+ +
+ +- 地址:[http://localhost:9090/.well-known/agent-card.json](http://localhost:9090/.well-known/agent-card.json) +- 说明:点击 agent-card.json 可以看到具体的服务协议信息。 + +### 3. 调用验证 + +
+ +
+ +- 点击运行,你可以看到它在进行多个智能体的调用(可能模型原因不准,但核心点在于多个智能体的调用)。 +- 好啦,到这关于 A2A 整个内容就演示完了,可以时刻关注 google adk a2a 协议的迭代,这部分内容后续还会有一些调整。 + diff --git a/docs/md/road-map/agent-skill.md b/docs/md/road-map/agent-skill.md new file mode 100644 index 000000000..123e16dd1 --- /dev/null +++ b/docs/md/road-map/agent-skill.md @@ -0,0 +1,226 @@ +--- +title: agent-skills +lock: need +--- + +# Agent Skill,你记一下,我做如下部署调整! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +skill 是什么?它像是一本技能书📚,把一阳指(`mcp/py/shell/js`)和狮吼功(`prompt`)合成了一整招。缩短了从用户把提示词发给AI客户端,进行分析,决策,再到 mcp 执行的过程,**让诉求直达结果**,token 减少了,幻觉减少了! + +
+ +
+ +随着 LLM 大模型能力的不断提升,并与 RAG、MCP、Skill 的结合,使得 Agent 智能体与完整的计算机环境(Computer/Phone)交互成为可能。这个过程中,一方面不断产生新的技术方案,一方面又不断的优化设计。就像 Skill 的出现,它不是替代 MCP,而是更准确的使用 MCP 能力。 + +接下来,小傅哥就带着大家使用一波 skill,让小伙伴们可以在 opencode、trae.ai,以及基于 Spring AI 也可以使用上 skill 能力。 + +## 一、skill 和 prompt + mcp + +如图,演示了一段 skill 的编写案例; + +
+ +
+ +- 场景:案例中体现的是,对电脑性能检测后,用一段下达命令的方式,告知用户如何优化电脑性能。 +- 重点:如果不使用 skill,则需要描述一大段话术,让 ai 自己完成对用户场景诉求的分析,并按照步骤来调用对应的各个 mcp 服务(没有 skill 则需要把各类内容,都包装为 mcp 服务)。这个过程是比较消耗 token 的,也可能有不小的幻觉。现在有了 skill,我们可以适当的完整的写一段诉求文档,文档里嵌入可执行的脚本/mcp服务,让执行更可靠。 +- 用途:那都有哪些场景可以写 skill 技能书呢?🤔 如;互联网公司里的系统巡检,在接收到报警日志后,拿到一个报警的系统和接口信息,之后用 skill 技能书,分别采集出对应的系统配置、上线日志、数据库/缓存情况、运营操作记录、全链路监控上的接口耗时情况等。之后在根据我们日常排查问题的时候经验,编写过程步骤,这样会更加准确。 + +> 所以,不是 skill、mcp 谁替代谁,而是 skill 对 mcp 进行增强,让 ai 执行时更加可靠。 + +## 二、配置使用 + +首先,像是市面上的 claude code、opencode 这些软件,都是支持了 skill 技能书配置使用的,如果遇到一些软件暂时还不支持 skill,或者自己使用 spring ai、langchain4j、google adk 构建的智能体时候需要使用 skil 技能,则可以通过 skillport-mcp 来使用 skill 配置。 + +这里小傅哥分别演示下 opencode、trae.ai + mcp、spring ai + google adk + mcp 的方式使用 skill; + +### 1. 测试工程(skill) + +
+ +
+ +- 地址:[https://github.com/fuzhengwei/xfg-dev-tech-agent-skills](https://github.com/fuzhengwei/xfg-dev-tech-agent-skills) +- 说明: + - 工程里 `docs/skills` 下面就是一个个技能书,battle-plan、pdf,每一个技能书下都必须有一个 SKILL.md 文件,作为入口。这个文件,可以描述 prompt 提示词,以及在提示词中明确给出可执行的脚本(py\shell\js)和可参考的文档。 + - 此外,在 xfg-dev-tech-app 下,test 里编码的是 SpringAiToolTest 测试技能书案例。 + +### 2. opencode 使用 + +- 文档:[https://opencode.ai/docs/skills/](https://opencode.ai/docs/skills/) +- 安装:[https://bugstack.cn/md/road-map/ai-ssh-opencode.html](https://bugstack.cn/md/road-map/ai-ssh-opencode.html) - `做好了安装脚本,方便小白伙伴使用` + +#### 2.1 配置skill + +```java +fuzhengwei@fuzhengweideMacBook-Pro-2 skill % ls +battle-plan +fuzhengwei@fuzhengweideMacBook-Pro-2 skill % pwd +/Users/fuzhengwei/.opencode/skill +fuzhengwei@fuzhengweideMacBook-Pro-2 skill % cd battle-plan +fuzhengwei@fuzhengweideMacBook-Pro-2 battle-plan % ls +reference.md scripts SKILL.md +fuzhengwei@fuzhengweideMacBook-Pro-2 battle-plan % +``` + +进入到 opencode 配置文件下,如果是 linux 一般会放到 `/root/.config/opencode/` 下。首先你要进入到这个 opencode 配置文件夹,之后在这个文件夹添加一个 skill,再之后就在 skill 下创建你的具体的技能书了。现在你可以把 xfg-dev-tech-agent-skills 案例工程的技能书,battle-plan 放到 skill 里。 + +#### 2.2 开启skill + +```java +fuzhengwei@fuzhengweideMacBook-Pro-2 ~ % cd /Users/fuzhengwei/.opencode +fuzhengwei@fuzhengweideMacBook-Pro-2 .opencode % ls +bin node_modules package.json +bun.lock opencode.json skill +fuzhengwei@fuzhengweideMacBook-Pro-2 .opencode % cat opencode.json +{ + "permission": { + "skill": { + "pr-review": "allow", + "internal-*": "deny", + "experimental-*": "ask", + "*": "allow" + } + }, + "$schema": "https://opencode.ai/config.json" +}% fuzhengwei@fuzhengweideMacBook-Pro-2 .opencode % +``` + +- 注意 `opencode.json` 需要配置下 `"*": "allow"` + +#### 2.3 使用skill + +
+ +
+ +- 提问:`基于 skill 解答,电脑性能优化` +- 说明:这里的`电脑性能优化`就是 skill 工具名称的描述。 + +### 3. trae.ai + mcp + skill + +#### 3.1 工具说明 + +工具:[https://github.com/gotalab/skillport](https://github.com/gotalab/skillport) + +目前还有不少 AI Agent 智能体,在底层设计上,还不支持直接使用 skill,也包括一些 ai 组件框架,也都没有 skill 的直接支持。那么这里要引入一个 skillport-mcp 服务来解决。借助 mcp 能力,使用 skill。 + +#### 3.2 工具安装 + +```java +# 需要安装 python3 +pip3 config set global.index-url http://mirrors.aliyun.com/pypi/simple/ +pip3 install uvx +``` + +- 安装 skillport 前,要确保本地安装了 pyhton/python3 环境。之后有 uvx 的安装。 + +```java +pip3 install skillport +# or: uv tool install skillport +``` + +- 如果安装过程中遇到一些失败的问题,可以用 trae.ai 里面执行安装,之后把报错拖进去提问。 + +#### 3.3 mcp 配置 + +
+ +
+ +```java +{ + "mcpServers": { + "skillport": { + "command": "uvx", + "args": ["skillport-mcp"], + "env": { "SKILLPORT_SKILLS_DIR": "~/.skillport/skills" } + } + } +} +``` + +- 这里你要配置下自己 skill mcp 服务,到你的 trae.ai 中。确保一定安装好了 python 环境,可以执行 `pip3 install skillport` 安装。 + +#### 3.4 工具使用 + +
+ +
+ +当你选择 Builder with MCP(涵盖了skillport-mcp),之后提问 `基于 skill 解答,电脑性能优化` 那么就可以得到上面的命令了。 + +### 4. spring ai + skill + +```java +public class SpringAiToolTest { + + private static final Logger log = LoggerFactory.getLogger(SpringAiToolTest.class); + + public static void main(String[] args) { + OpenAiApi openAiApi = OpenAiApi.builder() + .baseUrl("https://apis.itedus.cn") + .apiKey("sk-efen7WX8Q8vGvBps3f7c9a34578d41BbBc508dC5Df33A9Fb") + .completionsPath("v1/chat/completions") + .embeddingsPath("v1/embeddings") + .build(); + + ChatModel chatModel = OpenAiChatModel.builder() + .openAiApi(openAiApi) + .defaultOptions(OpenAiChatOptions.builder() + .model("gpt-4.1") + .toolCallbacks(new ArrayList<>() {{ + addAll(List.of(sseMcpClient())); + }}) + .build()) + .build(); + +// String call = chatModel.call("你哪有哪些 skill 工具能力"); + String call = chatModel.call("基于 skill 解答,电脑性能优化"); + + log.info("测试结果:{}", call); + + } + + /** + * https://github.com/gotalab/skillport + * pip3 config set global.index-url http://mirrors.aliyun.com/pypi/simple/ + * pip3 config set install.trusted-host mirrors.aliyun.com + * pip3 config list + * pip3 install uvx + */ + public static ToolCallback[] sseMcpClient() { + ServerParameters stdioParams = ServerParameters.builder("uvx") + .args("skillport-mcp") + .env(new HashMap<>() {{ + put("SKILLPORT_SKILLS_DIR", "/Users/fuzhengwei/coding/gitcode/KnowledgePlanet/road-map/xfg-dev-tech-agent-skills/docs/skills"); + }}) + .build(); + + McpSyncClient mcpSyncClient = McpClient.sync(new StdioClientTransport(stdioParams, new JacksonMcpJsonMapper(new ObjectMapper()))) + .requestTimeout(Duration.ofSeconds(35000)).build(); + + McpSchema.InitializeResult initialize = mcpSyncClient.initialize(); + + return SyncMcpToolCallbackProvider.builder().mcpClients(mcpSyncClient).build() + .getToolCallbacks(); + } + +} +``` + +- 在 Spring AI 程序中,添加 `skillport-mcp` 服务,之后在 ChatModel 模型里,使用 mcp 服务。 +- 如果你正在开发一些 AI Agent,那么也可以把 `skillport-mcp` 配置进去使用。如小傅哥带着做 [AI Agent 智能体项目](https://bugstack.cn/md/project/ai-knowledge/ai-knowledge.html) 你现在可以加进去更多的扩展操作了。 + + + diff --git a/docs/md/road-map/ai-ssh-opencode.md b/docs/md/road-map/ai-ssh-opencode.md new file mode 100644 index 000000000..d7c22695a --- /dev/null +++ b/docs/md/road-map/ai-ssh-opencode.md @@ -0,0 +1,229 @@ +--- +title: ai ssh opencode +lock: need +--- + +# ai ssh opencode 命令行工具 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获! + + + +大家好,我是技术UP主小傅哥。 + +天才般的发明,**ai ssh 命令行工具**,让 ai 的使用不只局限在 `ai 对话提问`、`ai idea 辅助编码`,也可以直接让操作系统具备 ai 能力。其实这才是我一直想要的东西!😄 + +
+ +
+ +**ai 正在改变产品设计!** + +2025年,是 ai agent 智能体爆发的一年,它所提效的接入方式可以分三层来看; + +先是中间这一层,是 ai agent 接入服务和软件,像是大家使用的 trae.ai 或者小傅哥分享过的 [draw.io + ai](https://bugstack.cn/md/road-map/draw.io.html),以及各个互联网公司用 ai 提效做的一些场景客服,系统巡检,日志分析等。 + +之后上面这一层,是从用户视角,模拟用户行为使用软件,而不是直接接入到软件api上。它使用的方式是 AutoGLM-Phone-9B 手机模型,以 ADB 或者无障碍模式,让手机可以接收用户指令,完成操作。如;`打开京东,搜索东北老式麻辣烫,加入购物车,以默认地址,进行支付购买。下单支付完成后,微信发给xxx告诉,她预计送达时间。` 如果感兴趣,可以基于这篇文章进行实践。[《手机 + agent,这是要掀桌子!》](https://bugstack.cn/md/algorithm/model/autoglm-phone-agent.html) + +然后底下这一层,是从系统层面,不再依赖于直接对接某个软件,某个api,而是以系统层面直接操作软件,或者完成整个行为动作。如,在云服务器/本地,配置好 jdk、maven、docker 环境,之后帮我拉取 git clone xxx 仓库代码,本地完成 maven 构建和启动。以及在遇到问题时候,让 ai 直接检索并处理都是非常可以的。 + +好,那接下来,小傅哥给大家分享下,如何安装一个 ai ssh 工具(没有这个教程,很多人是安装不上的!)。 + +>🧧 文末提供了小傅哥所有编程实战项目获取方式,一次加入即可获得19个已完结的实战项目,也有非常多的 AI 类项目,一定要补充学习! + +## 一、工具介绍 + +ai ssh 是命令行工具,可以安装到 Mac、Windows、Linux 上,以通过 terminal(终端)直接操作系统进行使用。它的场景也包括你打开的 IntelliJ IDEA 下面的 terminal 终端中使用,这样就天然的嵌入到了 IntelliJ IDEA 中了,非常方便。 + +目前这类的 ai ssh 命令行工具软件也非常多,包括;[opencode.ai](https://opencode.ai/)、[claude](https://claude.com/product/claude-code)、[openai codex](https://github.com/openai/codex)、[阿里千问 - qianwen code](https://github.com/QwenLM/qwen-code),都出了对应的软件。 + +在整体体验后,效果还是都不错的,这里是 opencode 的终端使用截图; + +
+ +
+ +- opencode 默认提供了一些免费模型,可以输入 `/models` 进行选择。 +- 另外还可以通过配置文件,添加其他模型,如 claude 模型、openai 模型、小米模型等。 + +## 二、软件安装 + +以下软件安装,会需要用到 gcc、nodejs 20+ 版本,建议安装 Ubuntu 24 版本,可以不需要折腾系统环境。 + +### 1. 脚本说明 + +这软件好用是挺好用,但它的源都在 Github 上,很多伙伴在执行官网脚本 `curl -fsSL https://opencode.ai/install | bash` 是安装不上的。所以,小傅哥做了一个对应的脚本,方便大家更加简化的安装使用。 + +
+ +
+ +- 脚本(gitcode):[https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install](https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install) +- 脚本(github):[https://github.com/fuzhengwei/xfg-dev-tech-docker-install](https://github.com/fuzhengwei/xfg-dev-tech-docker-install) + +### 2. 脚本下载 + +```java +git clone https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install.git +``` + +- 你可以通过命令的方式把脚本拉取到本地电脑或者云服务器上。 + +### 3. 脚本授权 + +```java +find . -name "*.sh" -type f -exec chmod +x {} \; +``` + +- 全部授权 + +```java +chmod +x terminal.sh +``` + +- 指定授权 + +### 4. 执行安装 + +
+ +
+ +```java +./terminal.sh +``` + +- 执行安装时,选择1,opencode code,这个是比较推荐的。 +- 无论是 Windows、Mac、Linux 都可以使用这个脚本进行安装,我已经做好了对应的脚本。 +- 安装完成后,需要输入 `opencode` 之后 Enter(回车)进入到系统中。如果提示 opencode 不是有效的命令,可以检查是否安装过程中有个提示 `source .../bashrc` 可以自行执行刷新。 + +## 三、软件配置 + +### 1. 命令使用 + +
+ +
+ +
+ +
+ +- `/init` - 创建/更新 Agents.md" +- `/review` - 检查变化" +- `/new` - 创建新的会话" +- `/models` - 选择模型" +- `/agents` - 智能体方式" +- `/session` - 会话列表" +- `/status` - 查看状态" +- `/mcp` - 选择mcp服务" +- `/theme` - 选择主题" +- `/editor` - 编辑" +- `/connect` - 链接模型提供者" +- `/help` - 帮助" +- `/commands` - 命令" +- `/exit` - 调试模式" + +> 这些可能会随着版本更新而调整,你可以依次尝试下。 + +### 2. 配置模型(自定义 - 可选) + +
+ +
+ +
+ +
+ +```java +{ + "$schema": "https://opencode.ai/config.json", + "provider": { + "my-model-openai": { + "npm": "@ai-sdk/openai-compatible", + "name": "OpenAPI", + "options": { + "apiKey": "你的apikey", + "baseURL": "https://你的baseURL/v1" + }, + "models": { + "gpt-4.1": { + "name": "gpt-4.1" + } + } + }, + "my-model-glm": { + "npm": "@ai-sdk/openai-compatible", + "name": "GLMAPI", + "options": { + "apiKey": "你的apikey", + "baseURL": "https://open.bigmodel.cn/api/paas/v4" + }, + "models": { + "glm-4.7": { + "name": "glm-4.7" + } + } + } + }, + "model": "my-model-openai/gpt-4.1" +} +``` + +- 如果你想自己更换下模型,比如使用 openai 的或则 claude 的,那么需要你在脚本下的 terminal/opencode.json 进行更换,之后执行 `./opencode.json.sh` 进行创建。 +- 如果先执行了 ``./opencode.json.sh`` 之后想更换配置的模型,则可以通过 vim 命令,编辑 `/root/.config/opencode/opencode.json` +- 其他的还有一些像是 mcp 的配置,可以参考官网(避免调整了配置错),[https://opencode.ai/docs/mcp-servers/](https://opencode.ai/docs/mcp-servers/) + + +## 三、软件使用 + +### 1. Linux + +#### 1.1 构建项目 + +
+ +
+ +#### 1.2 安装软件 + +
+ +
+ +#### 1.3 巡检系统 + +
+ +
+ +#### 1.4 编写文件 + +
+ +
+ +>通过命令操作云服务器是非常便捷的,尤其是很多小白伙伴,有了这个可以说是如小白虎添翼!打开思路,你可以在更多地方使用上,尤其哪些环境安装都困难的伙伴。 + +### 2. Mac/Windows + IntelliJ IDEA + +
+ +
+ +- 在本地电脑,除了可以像 Linux 举例那种安装和检查各类软件,也可以直接在 IntelliJ IDEA 开启,之后管理你的项目。 +- 尤其是很多伙伴,拿到一个项目,不知道里面都是什么,也不清楚脚本能干啥,那你都可以使用 opencode 帮你解决。 + +### 3. IPad Pro + +
+ +
+ +虽然 IPad Pro 没啥开发类软件,但如果你使用的是云服务器、Nas等,这些软件里安装 opencode,那么你也可以在 IPad Pro 安装 [termius.com](https://termius.com/) SSH 工具,通过 SSH 工具操作部署了 opencode 的 Linux 系统。 + +现在日常出门,带着 IPad Pro 也是可以处理一些小的开发的维护的,非常方便。IPad Pro + Nas 嘎嘎舒服! diff --git a/docs/md/road-map/aigc.md b/docs/md/road-map/aigc.md new file mode 100644 index 000000000..613830d63 --- /dev/null +++ b/docs/md/road-map/aigc.md @@ -0,0 +1,173 @@ +--- +title: AiGC +lock: need +--- + +# 入局AiGC,分币没赚!咋整滴? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +AiGC 入局后,连更一周小说,分币不赚,还被催更`擦边小故事`!我把这当做,不是 AiGC 入局,而是跳河!如果有想做这个方向的,可以先冷静下,看看自己适合不。 + +
+ +
+ +最近一段时间受不少 AiGC 类广告的影响,`文生图`、`文生视频`、`故事成片`,在家动动手就能赚钱。一个视频都几千、几万的点赞。 + +听起来做这个也挺简单,让 DeepSeek 生成口播故事,把故事放到 AiGC 软件(现在一堆),选择故事成片,在把视频上传到抖音、快手、头条,完活!**就是不赚钱!** + +>本文会分享 AiGC 视频制作的过程和注意点。另外作为技术人员,我们可以自己部署一个文生视频的软件服务,以及了解相关的 API 技术,也为后续自己开发 AiGC 做积累。 + +## 选择一个赛道!下场! + +首先,你可以想好一个创作的主题,如果有多个主题,可以分别投放到不同的软件平台,尽量先垂直一个系列更新,如果一直没有流量,后面可以干掉重新换主题来。 + +主题可以包括;`婆媳关系`、`中老年相亲`、`农村故事`、`鬼故事`、`好看的景色+音乐`、`儿童诗歌`,也就是你尽量要找一些高频有大量客群的场景,只要推荐到这样的人群标签下,视频的播放量还是很高的。 + +
+ +
+ +但不要想着这样就能赚钱,如果只是试试水,基于 DeepSeek 生成关键信息的故事(主题、背景、人物、故事线),再把故事放到文生视频完成后上传即可。**但这样做好,基本属于徒劳!** + +在我尝试推了几个鬼故事后,没有看到什么流量,这时候有个哥们加我,说我这样推没用的。我也好奇的去加了下关注,他是专门做鬼故事系列的,每个内容都很精良,而且属于 TM 鬼故事 + 擦边!还备注上(收徒)! + +
+ +
+ +所以,从这块开始,我发现要想让一个内容有较不错的流量,需要以下几点; + +1. 故事主体框架是明确、不能纯用 DeepSeek 来写。故事可以基于一些比较火的小说来制造。看过越多小说的,做这方面越有优势。 +2. 故事内容太干净了也没有人看,在 DeepSeek + 文生视频允许的范围内,你都可以“肆意”创作。 +3. 如果你时间更多,也更想做好,那么要对生成的视频,逐帧剪辑,也可能要多生成几次来使用。并制造好吸引人的封面! +3. 长期主义,1~3,你可以都不用考虑。而是真的一点点来结合 AI 来写长期有意义的故事。 + +**不过,就算以上这样你都做了,也可能一点流量都没有!**😂 + +## AiGC 软件工具 + +在有了内容以后,就是 AI 生成视频了。AI 生成视频的软件有很多,基本也都是付费的。😂 所以说,你不一定赚不赚钱,但 AI 创作课程和 AI 软件是赚钱。 + +我体验了,可灵 AI、录咖 AI,还有抖音剪映自带的 AI 生成类工具。可灵 AI 适合文生图片、数字人口播,剪映 AI 适合文生视频。因为我本身就需要日常剪辑视频,开了剪映会员,所以可以直接使用。可灵 AI 是少量付费体验了下。 + +
+ +
+ +- 剪映 APP 有个 AI 图片成片,进入后选择 AI 故事成片。 +- 你可以把 DeepSeek/AI 生成口播故事(2000字 - 3000字),放进来转换即可。完成后,把视频下载下来,上传到你的各个平台。 + +## 我以为一分钱也赚不到的时候! + +**在我折腾了快一周,以为分币赚不到的时候,来活了!** + +有个“粉丝”,关注了我。给我讲,接活不。我问啥活,她说,关注这个没事主播,点赞视频和收藏,完成后给6块钱。我没想到,她真给! + +
+ +
+ +o(╥﹏╥)o 这是我做 AIGC 的第一笔收入!虽然,它看起来不正常! + +晚上,这个粉丝小姐姐,问我还做不。我说行,她说你要大量做,要下载一个软件“魅信”。我去TM的,我是一个程序员,能信你这!不是“魅信”可怕,是你给我来个自动录屏,整走我点信息不完犊子了。 + +所以,我给这假粉丝讲!我是一个程序员,我对这个不放心,我不做了!没过多久,我就看XXX视频平台给这个假粉丝账号封了,没法查看完整聊天。以至于我只能截图这一丢丢,给大家看。 + +
+ +
+ +- 因为它的账号被封了,已经看不到内容了。只能通过录制视频,在截取一下。不过截取不全了。 +- 所以,如果你遇到这种的,赚个6块就跑吧🏃🏻‍! + +## 回到本质,仍然是技术! + +说到底,其实做各类东西都需要大量的时间精力投入,也有努力 + 运气,才可能有机会做成。想随随便便加个“副业社群”,不一定都能赚到什么钱。 + +就像你自己,如果用了大量的核心知识储备,那么你所用的这些产品就是工具,反之他们就不是工具,而是收割你的工具。 + +所以,作为研发人员,我们储备多的是技术知识,可以依赖于技术底层的积累,增强对工具的实现技术了解。看看这样的 AIGC 工具是如何实现的,能部署一个深入了解不,看看API接口从哪调用的,以后我们自己搞下是不更好。 + +### 1. 软件部署 + +有这么一个开源软件「利用AI大模型,一键生成高清短视频 Generate short videos with one click using AI LLM.」,可以通过 Docker 部署体现。 + +代码:[https://github.com/harry0703/MoneyPrinterTurbo](https://github.com/harry0703/MoneyPrinterTurbo) + +**docker-compose.yml** + +```java +services: + webui: + image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/moneyprinterturbo:latest + container_name: MPTwebui + ports: + - "8501:8501" + restart: unless-stopped + logging: + driver: "json-file" + options: + max-size: "2m" + command: > + streamlit run /MoneyPrinterTurbo/webui/Main.py + --browser.serverAddress=127.0.0.1 + --server.enableCORS=True + --browser.gatherUsageStats=False + api: + image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/moneyprinterturbo:latest + container_name: MPTapi + ports: + - "8080:8080" + restart: unless-stopped + logging: + options: + max-size: "2m" + command: python3 /MoneyPrinterTurbo/main.py +``` + +- 通过 Docker Compose 脚本,执行软件安装。`docker-compose -f docker-compose.yml up -d` 来执行安装。 +- 安装后访问 ip:8501 + +### 2. 访问服务 + +
+ +
+ +
+ +
+ +- 首先,需要点开设置。配置你的 openai 地址服务,之后配置 [Pexels API Key](https://www.pexels.com/api/key/) +- 之后,填写文案,文案也可以由 AI 工具生成复制进来。之后点击生成视频(其他选型也可以尝试更换)。 +- 最后,生成完视频,就可以扔了。这个效果基本就是体验,与剪映那个差太多了。 + +### 3. 访问API + +AIGC 相当于根据你的文案,检索/生成对应图片,好的AI产品会设置分镜头不同视角的检索。在结合这些图片、视频资源合成你最后要生成的视频效果。 + +如:[https://www.pexels.com/](https://www.pexels.com/) 就是一个图片视频资源库,你可以免费申请一个 API,之后使用它来生成图片。它的官网有对应的文档描述使用。举例如下; + +
+ +
+ +```java +curl -H "Authorization: 8aHBe0CyGtMZVMji32RVEvgzIRNx38rrkHlbp9HK*****申请你的" \ + "https://api.pexels.com/v1/search?query=美女&per_page=1" +``` + +
+ +
+ +- 调用接口返回的 JSON 可以查看图片信息,基本你要的内容还是可以大部分时候准确获取的。 +- 如果感兴趣,还可以深入体验下它的 API。 + diff --git a/docs/md/road-map/aliyun-workbench.md b/docs/md/road-map/aliyun-workbench.md new file mode 100644 index 000000000..310ab443e --- /dev/null +++ b/docs/md/road-map/aliyun-workbench.md @@ -0,0 +1,233 @@ +--- +title: Docker 镜像构建 - Aliyun +lock: need +--- + +# Docker 镜像构建 - Aliyun + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +对于搞编程开发的伙伴来说,我一直都推荐使用 Mac 电脑,因为 Windows 电脑除了相对省点钱,其他的什么也不省!为啥?那我 Windows 电脑推沟里? + +
+ +
+ +**大厂更多的都是给程序员👨🏻‍💻配Mac!** + +其实到也不是 Windows 不好,只是在编程开发中的环境配置,要有太多的额外的兼容问题。因为 Windows 是一个系统,这个系统被各个厂商使用,安装到自己家的电脑上。因此一个软件安装后,不同的机器,都可能在一些细节功能上,有自己的差异化处理方案。就像 Docker 安装,Mac 电脑直接安装即可,Windows 不是安装虚拟机,就是搞个 wsl2,不少小白基本就在这崩溃了。 + +相对说,Mac 电脑就好的多,因为人家的系统只给自己家的电脑使用。所以各类软件的安装和使用也不会有那么多的适配问题了。另外 Mac 电脑也等同于是在 Linux 上做开发,你的所有编程中使用的命令,几乎就是无差别的可以在云环境 Linux 执行。因此,Mac 电脑除了不省钱,其他的都省;省时、省心、省力。 + +不过,Windows 电脑也不能推沟里呀!所以,我们要想点办法,让一些如 Docker 的环境安装和镜像构建,让外部系统解决。不就可以了噻! + +>接下来,小傅哥就教大家搞一下这个事情。—— 学到手的全是技术! + +## 一、DevOps 流水线 + +DevOps 流水线,是一种可持续集成交付的手段。用户可以使用流水线自定义编排项目发布过程中所涉及的代码打包、单元测试、自动部署等各项阶段。 通过一系列自动化任务的组合解决日常开发工作中繁琐而重复的任务。 + +
+ +
+ +官网(JD):[https://docs.jdcloud.com/cn/devops/application-scenarios](https://docs.jdcloud.com/cn/devops/application-scenarios) + +--- + +市面上也有很多的免费的可持续交付工具,方便我们自己部署项目使用。如 [Github Actions](https://bugstack.cn/md/road-map/github-actions-workflows.html)、[buddy.works](https://bugstack.cn/md/road-map/buddy.html)、[Jenkins](https://bugstack.cn/md/road-map/jenkins.html),这些已经在 [bugstack.cn](https://bugstack.cn/) 编程路书(发布部署)中分享。今天小傅哥再分享一个阿里云的 DevOps 流水线操作,实际使用体验效果还不错。 + +## 二、云效工作台介绍 + +本次案例会涉及到使用阿里云效平台的 `代码管理`、`流水线配置`、`容器镜像` + +### 1. 代码&流水线 + +
+ +
+ +- 地址:[https://devops.aliyun.com/workbench](https://devops.aliyun.com/workbench) +- 说明:用于管理工程代码和配置流水线。你可以提交本地代码,也可以把其他仓库的代码导入到`代码管理`中,如;Github、Gitee、Gitlab,其他的也可以通过 url 导入。 + +### 2. 容器镜像服务 + +#### 2.1 容器申请 + +
+ +
+ +- 地址:[https://cr.console.aliyun.com/cn-hangzhou/instance/dashboard](https://cr.console.aliyun.com/cn-hangzhou/instance/dashboard) +- 说明:容器镜像,相当于代理的 [Docker Hub](https://hub.docker.com/) 可以把我们通过流水线构建的项目(SpringBoot/React/...)构建的镜像,放到容器镜像服务中。之后就可以在其他云环境或者本地环境拉取使用了。 + +#### 2.2 新建服务 + +
+ +
+ +
+ +
+ +- 这里要新建一个镜像名称,`xfg-dev-tech-aliyun-workbench` 的镜像。这个镜像为后续的流水线构建使用。 +- 创建的时候,要选择对应的仓库。可以是 Codeup 的阿里云代码库,也可以是 Github 关联地址的仓库。 + +> 再往下开始平台,你需要注册一个云校平台和容器镜像服务。 + +## 三、上传代码库 + +这部分会涉及一些 Git 的操作,这里小傅哥准备好了教程可以使用 [https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) + +### 1. 案例工程 + +
+ +
+ +```java +# 基础镜像 openjdk:8-jre-slim 代理地址;registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:8-jre-slim +FROM registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:8-jre-slim + +# 作者 +MAINTAINER xiaofuge + +# 配置 +ENV PARAMS="" + +# 时区 +ENV TZ=PRC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# 在容器内创建目录/home/project 用于存放应用程序和相关文件 +RUN mkdir -p /home/project + +# 设置工作目录为/home/project,即后续的命令都在这个工作目录下执行。 +WORKDIR /home/project + +# 添加应用 +ADD xfg-dev-tech-app/target/xfg-dev-tech-app.jar /home/project/xfg-dev-tech-app.jar + +## 在镜像运行为容器后执行的命令 +ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /home/project/xfg-dev-tech-app.jar $PARAMS"] +``` + +- 地址:[https://github.com/fuzhengwei/xfg-dev-tech-aliyun-workbench](https://github.com/fuzhengwei/xfg-dev-tech-aliyun-workbench) +- 说明:这里小傅哥给你准备好了一个测试工程,你可以使用验证阿里云效流水线操作。 +- 脚本:注意配置的脚本信息,如果你是新的其他的工程,可以参考配置。 + +### 2. 上传项目 + +把案例代码上传到阿里云代码库的方式有很多,可以从 Github 导入,也可以先把代码拉取到本地,在从本地上传到阿里云代码库。但两个方式都建议把代码拉取到本地代码一份,方便进行修改。 + +#### 2.1 导入代码 + +
+ +
+ +- 首先,进入案例工程地址(Github),点击 Fork 克隆到自己的仓库中。 +- 之后,在阿里云效点击导入仓库,这里可以选择从 Github 导入。导入的时候,需要填写 Access Token。创建地址:[https://github.com/settings/tokens](https://github.com/settings/tokens) +- 最后,在确认后会展示你的 Github 仓库列表,选择要导入的工程即可。 + +#### 2.2 上传代码 + +
+ +
+ +- 首先,开始操作之前,你要把提供的案例代码拉取到本地。 +- 之后,在云效代码库,新建一个代码库。新建后可以获得一个 https 提交代码库的地址。另外,要注意你还要在云效仓库个人设置里,创建一个,个人访问令牌(地址:[https://account-devops.aliyun.com/settings/profile](https://account-devops.aliyun.com/settings/profile))。 +- 最后,在自己 Intellij IDEA 菜单栏上,点击 Git 选择 Manage Remotes 添加阿里云效仓库地址,之后就可以把代码提交到阿里云效了。 + +### 3. 添加流水线 + +#### 3.1 构建操作 + +
+ +
+ +- 如图,配置构建信息。注意,☑️ 制品中包含打包路径的目录。 + +#### 3.2 添加步骤;Docker镜像构建 + +
+ +
+ +- 添加新的步骤,选择构建并推送至ACR(个人版)注意配置路径正确。 + +#### 3.3 添加步骤;邮件通知 + +
+ +
+ +- 添加一个邮件通知的插件,构建后会接收到邮件。 + +### 4. 构建镜像 + +#### 4.1 执行构建 + +
+ +
+ +- 点击运行,之后就可以构建镜像了。如果哪个节点失败了,可以点日志查看失败原因。 + +#### 4.2 构建结果 + +##### 4.2.1 邮件通知 + +
+ +
+ +- 你会收到一个来自云效构建的通知邮件。代表着构建的结果。 + +##### 4.2.2 查看镜像 + +
+ +
+ +- 构建完成镜像后,可以进入`容器镜像服务`中找到镜像。地址:[https://cr.console.aliyun.com/cn-hangzhou/instance/repositories](https://cr.console.aliyun.com/cn-hangzhou/instance/repositories) +- 之后还可以通过公网地址拉取镜像,命令:`docker pull registry.cn-hangzhou.aliyuncs.com/fuzhengwei/xfg-dev-tech-aliyun-workbench:1.0.0` + +##### 4.2.3 拉取镜像 + +
+ +
+ +```java +[root@hcss-ecs-decd ~]# docker pull registry.cn-hangzhou.aliyuncs.com/fuzhengwei/xfg-dev-tech-aliyun-workbench:1.0.0 +1.0.0: Pulling from fuzhengwei/xfg-dev-tech-aliyun-workbench +1efc276f4ff9: Already exists +a2f2f93da482: Already exists +1a2de4cc9431: Already exists +d2421c7a4bbf: Already exists +64e0330ea35f: Pull complete +4da07e374896: Pull complete +4f4fb700ef54: Pull complete +eb9d0e194679: Pull complete +Digest: sha256:a7af08fff4ea0b76184a2fad7e7ca639ab4a9ad16573480b526eda672ed8a483 +Status: Downloaded newer image for registry.cn-hangzhou.aliyuncs.com/fuzhengwei/xfg-dev-tech-aliyun-workbench:1.0.0 +registry.cn-hangzhou.aliyuncs.com/fuzhengwei/xfg-dev-tech-aliyun-workbench:1.0.0 +[root@hcss-ecs-decd ~]# docker tag registry.cn-hangzhou.aliyuncs.com/fuzhengwei/xfg-dev-tech-aliyun-workbench:1.0.0 fuzhengwei/xfg-dev-tech-aliyun-workbench:1.0.0 +[root@hcss-ecs-decd ~]# docker images +REPOSITORY TAG IMAGE ID CREATED SIZE +fuzhengwei/xfg-dev-tech-aliyun-workbench 1.0.0 df740ba425bb 24 minutes ago 221MB +``` + +- 镜像构建完成后,就可以在云服务器上拉取镜像了。 +- 好啦,到这你就可以愉快的完善了,前端构建镜像也是一样的操作。 diff --git a/docs/md/road-map/apisix.md b/docs/md/road-map/apisix.md new file mode 100644 index 000000000..af0659430 --- /dev/null +++ b/docs/md/road-map/apisix.md @@ -0,0 +1,128 @@ +--- +title: apisix +lock: need +--- + +# Apisix 云原生架构的开源 API 网关 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +`Higress`、`SpringCloud Gateway`,再到今天这套 `Apisix` 小傅哥就把市面上非常常用的3套 API 网关服务就全部都展示给大家了。其实所有的 API 网关都有一个共同的目的,就是做统一的 API 管理,包括;协议转换、负载均衡、动态路由、灰度发布、服务熔断、统一认证等。 + +像是这些组件化的 API 网关,使用起来其实都挺容易的,就有点像是使用了一个可视化的 Nginx 一样。但这些 API 看着简单,但有时候没有一个不错的资料对照搭建、配置和使用,也是挺难下手的。所以小傅哥把这些内容,成体系的展示给你,让你可以对照学习使用。 + +- 官网:[https://apisix.apache.org/zh/](https://apisix.apache.org/zh/) +- 部署:[https://github.com/apache/apisix-docker](https://github.com/apache/apisix-docker) - 官网提供了 Docker 脚本,但也有一些注意事项才能使用 +- 脚本:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-apisix](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-apisix) - 可执行使用的部署脚本 + +## 一、APISIX 介绍 + +这款老6API,是阿帕奇下的开源项目。最初由 [api7.ai](https://api7.ai/apisix) 创建,于 2019 年开源并捐献给 Apache 软件基金会,使服务全球一半 API 请求的愿景成为可能。自那时起,API7.ai一直投入最优秀的人才和资源来支持 Apache APISIX 及其社区,该社区由来自世界各地的数千名贡献者和用户加入。 + +Apache APISIX 是一个动态、实时、高性能的云原生 API 网关,提供了负载均衡、动态上游、灰度发布、服务熔断、身份认证、可观测性等丰富的流量管理功能。 + +
+ +
+ +为什么选择 Apache APISIX? + +- 简单易用的 Dashboard:Apache APISIX Dashboard 可以让用户尽可能直观、便捷地通过可视化界面操作 Apache APISIX。作为 Apache 软件基金会中持续迭代的开源项目,欢迎你随时提出新的想法。 +- 友好的用户体验:Apache APISIX Dashboard 极大地满足了用户需求,不仅提供了清晰的组织架构以适配二次开发,而且可以借助插件编排能力释放想象力! +- 可视化配置:拒绝重复造轮子。借助 Apache APISIX 内置插件,可以在极短时间内创建灵活、可靠、高性能的网关。无需编写代码,只需在编辑器中拖拽插件、配置条件,便可通过可视化的方式打造专属的 API 管理系统。 +- 极致的性能体验:Apache APISIX 基于 Radixtree Route 和 etcd 提供路由极速匹配与配置快速同步的能力。从路由到插件,所有的设计和实现都是为了极速性能和超低延迟。 +- 阻拦恶意程序:Apache APISIX 提供了多款身份认证与接口验证的插件,我们将稳定、安全放在首位。 + +## 二、环境部署 + +- 云服务器:2c2g 最低,我是用的 2c8g 体验的。[https://yun.xfg.plus](https://yun.xfg.plus/) - 价格实惠。 +- 基础环境:Docker、Portainer、Git 【在小傅哥的 bugstack.cn 路书中都有讲解安装和使用】 + +
+ +
+ +### 1. 脚本配置 + +官网中提供了 docker 的安装脚本,但不能直接使用,需要如图做下复制操作。小傅哥已经把这部分复制的内容,到`脚本`工程里了。 + +
+ +
+ +- 你可以知道有这么一个地方,将来如果版本更新了,也可以做下配置操作。 + +### 2. 可用脚本 - 准备好的 + +
+ +
+ +- 这个是小傅哥帮你准备好的脚本,可以直接安装使用。 +- 这里涉及了 docker、portainer 的使用,在《[bugstack.cn](https://bugstack.cn) 路书》中,已经完整配置提供,可以查看。 + +## 三、网关配置 + +- 网关的登录密码是在 dev-ops/apisix-dashboard/conf.yaml 中配置的。默认的是 `admin/admin` +- 另外注意 conf.yml 中 etcd:2379 的配置,这里从 127.0.0.1 调整为 etch 应用名称。这样能保证在同一个 docker 配置的网络下,可以直接连接使用。如果是分布式部署,则要改为具体的IP。 +- 这套按照学习脚本中提供了 web1、web2 两个 nginx 的配置,可以测试使用。 + +### 1. 访问网关 + +地址:[http://127.0.0.1:9093/](http://127.0.0.1:9093/) + +
+ +
+ +- 访问后就可以看到左侧的目录,可以配置;服务、上游、路由,以及插件等功能。 +- 路由是入口,从路由可以访问到配置的服务,服务是每个应用的访问地址,可以配置权重轮训和一致性哈希。插件主要是提供的是;身份验证、安全防护、流量控制、无服务架构、可观测性功能。 + +### 2. 服务配置 + +
+ +
+ +
+ +
+ +- 配置后,一直点击就可以了。 + +### 3. 配置路由 + +
+ +
+ +
+ +
+ +
+ +
+ +### 4. 插件配置 + +
+ +
+ +- 你可以按需测试各种插件,还可以通过编排让各个要执行的插件串联起使用。 + +## 四、服务验证 + +地址:[http://192.168.1.105:9080/api](http://192.168.1.105:9080/api) + +
+ +
+ +- 刷新网关接口,不停的刷新,会看到接口访问值的变化。 diff --git a/docs/md/road-map/arex-test.md b/docs/md/road-map/arex-test.md new file mode 100755 index 000000000..1f281ac36 --- /dev/null +++ b/docs/md/road-map/arex-test.md @@ -0,0 +1,435 @@ +--- +title: AREX - 流量录制&回放 +lock: need +--- + +# AREX - 流量录制&回放 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在互联网大厂应用系统开发中,有一项非常重要的技术手段,保证新迭代功能的可靠性。虽然,这像技术也不是“银弹(啥都能搞定)”,但缺少这项技术,对于极其复杂的业务场景新上线功能,总感觉心里没底!—— 它就是`流量录制`和`流量回放`! + +
+ +
+ +**如何保证系统的可靠性?** + +互联网大厂toc场景,非常重视交付的可靠性,甚至大于交付的效率性。所以,在整个研发声明周期过程中,要有 PRD 评审、研发设计代码、代码评审、代码分支合并评审,以及在这个过程中还有测试人员进行全流程的验证。 + +但即使这样,面对经历了数十年迭代的系统,单工程甚至几十万行代码,极其复杂的业务系统流程,测试也是可能会出现疏漏。这种疏漏如,虽然非本次需求迭代的场景,但是其他业务场景因当前系统的一个枚举,一个参数的变动,都会影响到数十条业务。哪怕不是代码变动,仅仅是重构了小部分代码块,调整了前后顺序,当前业务无影响,但确影响了某次其他任务的跑批结果! + +一次事故,可能是扣绩效和年终奖,也可能是影响晋升提名,还可能是直接毕业🎓啦!所以,在互联网大厂班组🧱搬砖,会学到很多高级技巧,来保证系统的可靠性。 + +>关注小傅哥 [bugstack.cn](https://bugstack.c) 博客,编程路书,可以学习到非常多的互联网企业使用技能,补全自己的短板!如;系统架构、开发环境、开发技术、授权框架、常用类库、工程测试、质量监控、发布部署等。 + +## 一、说说概念 + +其实简单来讲,流量录制,核心的本质就是把线上运行的应用,在有流量请求到服务接口后,把请求接口的入参和执行的结果,都保存下来。之后流量回放,则是把这些保存下来的入参信息,在开发环境/测试环境/预发环境,进行请求,之后验证请求后的结果是否与线上录制的流量结果一致。整个的这个过程,就是流量的录制和回放。 + +但实际的操作场景要比这个过程复杂,如;录制流量的操作,要无侵入代码的,要把整个调用链路,全部录制下来,而不是单个接口。同时录制的不只是对接口的操作,还有缓存、rpc、数据库(MyBatis)、es,等各类组件兼容,都能确保可正确录制到完整数据,以便在流量回放时,可以mock掉数据接口,以此只验证功能逻辑。 + +说到,逻辑和数据,这里想到了DDD架构的领域层和基础设施层,领域层(domain)专门业务功能逻辑的,而基础设置层(infrastructure)则是提供数据的。这样有了业务功能和数据获取的分离,不让核心业务逻辑又掺杂外部接口的调用,其实维护和迭代起来,是会更加容易的。 + +## 二、讲讲原理 + +为了可以无侵入式的完成系统的流量录制和流量回放,这里引入了一个 Java Agent 字节码增强技术,通过在程序启动阶段,对工程代码的方法进行埋点。这个埋点,就等同于动态的通过 class 文件写入新的代码,新的代码可以采集到方法的出入参信息,并把这些信息发送到流量录制存储方,或者执行时进行mock接口请求结果处理。 + +这部分技术方案,小傅哥于19-20年,编写了先关的技术资料。其中包括了用于处理字节码增强技术的,asm、javassist、byte-buddy 三种框架。 + +
+ +
+ +文档:[https://bugstack.cn/md/bytecode/asm/2020-04-05-%5BASM%E5%AD%97%E8%8A%82%E7%A0%81%E7%BC%96%E7%A8%8B%5DJavaAgent+ASM%E5%AD%97%E8%8A%82%E7%A0%81%E6%8F%92%E6%A1%A9%E9%87%87%E9%9B%86%E6%96%B9%E6%B3%95%E5%90%8D%E7%A7%B0%E4%BB%A5%E5%8F%8A%E5%85%A5%E5%8F%82%E5%92%8C%E5%87%BA%E5%8F%82%E7%BB%93%E6%9E%9C%E5%B9%B6%E8%AE%B0%E5%BD%95%E6%96%B9%E6%B3%95%E8%80%97%E6%97%B6.html](https://bugstack.cn/md/bytecode/asm/2020-04-05-%5BASM%E5%AD%97%E8%8A%82%E7%A0%81%E7%BC%96%E7%A8%8B%5DJavaAgent+ASM%E5%AD%97%E8%8A%82%E7%A0%81%E6%8F%92%E6%A1%A9%E9%87%87%E9%9B%86%E6%96%B9%E6%B3%95%E5%90%8D%E7%A7%B0%E4%BB%A5%E5%8F%8A%E5%85%A5%E5%8F%82%E5%92%8C%E5%87%BA%E5%8F%82%E7%BB%93%E6%9E%9C%E5%B9%B6%E8%AE%B0%E5%BD%95%E6%96%B9%E6%B3%95%E8%80%97%E6%97%B6.html) + +### 1. 增强代码 + +```java +mv.visitVarInsn(LLOAD, startTimeIdentifier); +mv.visitLdcInsn(methodId); +if (parameterTypeList.isEmpty()) { + mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(ProfilingAspect.class), "point", "(JI)V", false); +} else { + mv.visitVarInsn(ALOAD, parameterIdentifier); // 5 + mv.visitVarInsn(ALOAD, localCount); // 6 + mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(ProfilingAspect.class), "point", "(JI[Ljava/lang/Object;Ljava/lang/Object;)V", false); +} +``` + +### 2. 拦截信息 + +```java +ASM类输出路径:/Users/xiaofuge/itstack/git/github.com/SQM/target/test-classes/org/itstack/test/ApiTest$1SQM.class +监控 - Begin +类名:org.itstack.test.ApiTest +方法:queryUserInfo +入参类型:["I","I"] +入数[值]:[111,17] +出参类型:Ljava/lang/String; +出参[值]:"你好,bugstack虫洞栈 | 精神小伙!" +耗时:95(s) +监控 - End + +测试结果:你好,bugstack虫洞栈 | 精神小伙! +``` + +- 以上为简要的案例代码,通过ASM字节码增强框架来对方法修改,增加日志打印信息,在执行方法时,获取出入参。 +- 字节码增强技术应用的方面非常多,如;MapStruct、非入侵的全链路监控、Lombok,甚至你用过的 IntelliJ IDEA 破解也是字节码增强技术。 + +## 三、工具介绍 + +关于流量录制和流量回放的工具是蛮多的,其中包括;[jvm-sandbox-repeater](https://github.com/alibaba/jvm-sandbox-repeater)、[MoonBox](https://github.com/vivo/MoonBox)、[arextest](https://arextest.com/) 等。 + +- jvm-sandbox-repeater,是一个基于 JVM-Sandbox 采用Java来实现的流量录制回放工具,或者可以理解为它是一个基于Java虚拟机的插件,可以直接运行中JVM中,无需对目标应用程序进行任何修改。 +- Moonbox(月光宝盒),是一个无侵入的线上流量录制 和流量回放平台,沿用了jvm-sandbox-repeater的SPI设计,并提供了大量的常用插件,同时也提供数据统计和存储能力。通过Moonbox可以实现自动化测试、线上问题追踪、和业务监控等能力 +- arextest,AREX 通过将真实的在线流量复制到测试环境进行自动化 API 测试来解决自动化测试的挑战。**作为本文分享场景工具使用。** + +**名词解释** + +- **录制**:把一次请求的入参、出参、**下游RPC、DB、缓存**等序列化并存储的过程 +- **回放**:把录制数据还原,重新发起一次或N次请求,对特定的下游节点进行MOCK的过程 +- **入口调用**:入口调用一般是应用的**流量来源**,比如http/dubbo,在调用过程中录制调用入参,返回值。回放时作为流量发起和执行结果对比依据 +- **子调用**:区别于入口调用,子调用是调用执行过程中某次方法调用。子调用在录制时会记录该方法的入参、返回值;回放时用该返回值进行MOCK +- **MOCK**:在回放时,被拦截的子调用**不会发生真实调用**,利用字节码动态干预能力,将录制时的返回值直接返回 +- **降噪**:在回放时,部分回放子调用入参或者回放流量响应结果和原始流量对比不一致字段,对这些非必要字段进行排除对比过程 + +## 四、工具使用 + +### 1. 案例工程 + +
+ +
+ +- 地址:[https://github.com/fuzhengwei/xfg-dev-tech-traffic-agent](https://github.com/fuzhengwei/xfg-dev-tech-traffic-agent) +- 如图:1~6点,说明了各个模块的用途。 +- 提示:建议使用云服务器部署流量录制&回放服务,非常方便。 + +### 2. 安装插件 + +地址:[https://chromewebstore.google.com/detail/arex-chrome-extension/jmmficadjneeekafmnheppeoehlgjdjj](https://chromewebstore.google.com/detail/arex-chrome-extension/jmmficadjneeekafmnheppeoehlgjdjj) + +
+ +
+ +- 安装插件是为了有权限访问接口。 + +### 3. 服务部署(arex) + +**前置条件**,2c4g云服务器,完成初始化Docker环境。这里有一键安装脚本,[https://bugstack.cn/md/road-map/docker-install.html](https://bugstack.cn/md/road-map/docker-install.html) + +```java +[root@iv-jdyun ~]# cd / +[root@iv-jdyun ~]# mkdir dev-ops +[root@iv-jdyun ~]# cd dev-ops +[root@iv-jdyun ~]# sudo yum install git +[root@iv-jdyun ~]# git clone --depth 1 https://github.com/arextest/deployments.git +[root@iv-jdyun ~]# cd deployments +[root@iv-jdyun ~]# docker-compose up -d +``` + +
+ +
+ +- 执行脚本安装完成后,在云服务器安全组开放端口号(出/入规则)。 +- 之后访问:http://ip:8088 首次访问会慢一些,进入后会让设置你的登录账号。 + +### 4. 启动项目 - 录制流量 + +
+ +
+ +- 首先,需要给项目工程加入一个 agent,来录制和回放。arex 的 agent jar,下载后方到工程下,lib 中。目前工程中已经下载了,如果将来有更新,可以重新下载最新的。地址:[https://github.com/arextest/arex-agent-java/releases](https://github.com/arextest/arex-agent-java/releases) +- 之后,本地项目测试验证的时候,可以先 **install**,成功之后使用 `run_application_2_agent.sh` 启动项目,这个操作会把 agent 加载到程序启动中。 + +#### 4.1 本地测试 + +```java +#!/bin/bash + +# AREX Agent 启动脚本 +# 用于在不同环境下启动应用并加载 AREX Agent + +# 设置 AREX Agent 相关参数 +AREX_AGENT_PATH="./lib/arex-agent.jar" +AREX_SERVICE_NAME="xfg-dev-tech-app" +AREX_STORAGE_HOST="115.190.107.206:8093" + +# 应用 JAR 包路径(相对于项目根目录) +APP_JAR="../../xfg-dev-tech-app/target/xfg-dev-tech-app.jar" + +# JVM 参数 +JVM_OPTS="-Xms512m -Xmx1024m" + +# 检查 AREX Agent 文件是否存在 +if [ ! -f "$AREX_AGENT_PATH" ]; then + echo "错误: AREX Agent 文件不存在: $AREX_AGENT_PATH" + echo "请确保 arex-agent.jar 文件已放置在正确位置" + exit 1 +fi + +# 检查应用 JAR 包是否存在 +if [ ! -f "$APP_JAR" ]; then + echo "错误: 应用 JAR 包不存在: $APP_JAR" + echo "请先执行 mvn clean package 构建应用" + exit 1 +fi + +echo "正在启动应用..." +echo "AREX Agent: $AREX_AGENT_PATH" +echo "Service Name: $AREX_SERVICE_NAME" +echo "Storage Host: $AREX_STORAGE_HOST" +echo "Application: $APP_JAR" +echo "" + +echo "执行配置:"$JVM_OPTS \ + -javaagent:"$AREX_AGENT_PATH" \ + -Darex.service.name="$AREX_SERVICE_NAME" \ + -Darex.storage.service.host="$AREX_STORAGE_HOST" \ + -jar "$APP_JAR" + +# 启动应用 +java $JVM_OPTS \ + -javaagent:"$AREX_AGENT_PATH" \ + -Darex.service.name="$AREX_SERVICE_NAME" \ + -Darex.storage.service.host="$AREX_STORAGE_HOST" \ + -jar "$APP_JAR" +``` + +#### 4.2 docker 镜像 + +##### 4.2.1 dockerfile + +```java +# 基础镜像 +FROM openjdk:8-jre-slim + +# 作者 +MAINTAINER xiaofuge + +# 配置 +ENV PARAMS="" + +# AREX Agent 配置 +ENV AREX_SERVICE_NAME="xfg-dev-tech-app" +ENV AREX_STORAGE_HOST="115.190.107.206:8093" + +# 时区 +ENV TZ=PRC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# 添加 AREX Agent +ARG AREX_AGENT_PATH=docs/dev-ops/lib/arex-agent.jar +ADD ${AREX_AGENT_PATH} /arex-agent.jar + +# 添加应用 +ADD target/xfg-dev-tech-app.jar /xfg-dev-tech-app.jar + +ENTRYPOINT ["sh","-c","java $JAVA_OPTS -javaagent:/arex-agent.jar -Darex.service.name=$AREX_SERVICE_NAME -Darex.storage.service.host=$AREX_STORAGE_HOST -jar /xfg-dev-tech-app.jar $PARAMS"] +``` + +##### 4.2.2 docker-compose + +```java +version: '3.8' + +services: + xfg-dev-tech-app: + image: fuzhengwei/xfg-dev-tech-app:traffic-agent-v1 + container_name: xfg-dev-tech-app + ports: + - "8091:8091" + environment: + # AREX Agent 配置 + - AREX_SERVICE_NAME=xfg-dev-tech-app + - AREX_STORAGE_HOST=115.190.107.206:8093 + # JVM 配置 + - JAVA_OPTS=-Xms512m -Xmx1024m + volumes: + # 日志目录挂载(可选) + - ./data/logs:/var/log/app + networks: + - app-network + restart: unless-stopped + +networks: + app-network: + driver: bridge + +volumes: + app-logs: + driver: local +``` + +- 此方式适用于项目部署上线。 + +### 5. 启动程序 + +```java +./bin/bash /xfg-dev-tech-traffic-agent/docs/dev-ops/run_application_2_agent.sh +``` + +```java +(base) fuzhengwei1@ZBMac-GV47H1GXD dev-ops % /bin/bash /Users/fuzhengwei1/Documents/develop/github/xfg-dev-tech-traffic-agent/docs/dev-ops/run_application_2_agent.sh +正在启动应用... +AREX Agent: ./lib/arex-agent.jar +Service Name: xfg-dev-tech-app +Storage Host: 115.190.107.206:8093 +Application: ../../xfg-dev-tech-app/target/xfg-dev-tech-app.jar + +执行配置:-Xms512m -Xmx1024m -javaagent:./lib/arex-agent.jar -Darex.service.name=xfg-dev-tech-app -Darex.storage.service.host=115.190.107.206:8093 -jar ../../xfg-dev-tech-app/target/xfg-dev-tech-app.jar +2025-06-21 17:57:25:792 [AREX] Agent-v0.4.8 starts initialization... +Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.7.12) + +25-06-21.17:57:27.856 [main ] INFO Application - Starting Application v1.0-SNAPSHOT using Java 17.0.7 on ZBMac-GV47H1GXD with PID 96181 (/Users/fuzhengwei1/Documents/develop/github/xfg-dev-tech-traffic-agent/xfg-dev-tech-app/target/xfg-dev-tech-app.jar started by fuzhengwei1 in /Users/fuzhengwei1/Documents/develop/github/xfg-dev-tech-traffic-agent/docs/dev-ops) +25-06-21.17:57:27.858 [main ] INFO Application - The following 1 profile is active: "dev" +25-06-21.17:57:28.635 [main ] INFO TomcatWebServer - Tomcat initialized with port(s): 8091 (http) +25-06-21.17:57:28.641 [main ] INFO Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8091"] +25-06-21.17:57:28.641 [main ] INFO StandardService - Starting service [Tomcat] +25-06-21.17:57:28.642 [main ] INFO StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.75] +25-06-21.17:57:28.683 [main ] INFO [/] - Initializing Spring embedded WebApplicationContext +25-06-21.17:57:28.683 [main ] INFO ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 781 ms +25-06-21.17:57:28.913 [main ] INFO Http11NioProtocol - Starting ProtocolHandler ["http-nio-8091"] +25-06-21.17:57:28.928 [main ] INFO TomcatWebServer - Tomcat started on port(s): 8091 (http) with context path '' +25-06-21.17:57:28.935 [main ] INFO Application - Started Application in 1.575 seconds (JVM running for 3.285) +``` + +- 启动程序,会看到 arex-agent.jar 的加载,这代表是正确✅的,可以上报服务信息。 + +### 6. 流量录制 + +```java +@Slf4j +@RestController +@RequestMapping("/api/traffic") +public class TrafficController { + + @GetMapping("/query_user_age") + public Integer queryUserAge(@RequestParam("userId") String userId) { + log.info("请求参数:{}", userId); +// return getCache(); + return getDB(); + } + + // 模拟从缓存获取数据 + public Integer getCache() { + return 20; + } + + // 模拟从数据库获取数据 + public Integer getDB() { + return 21; + } + +} +``` + +
+ +
+ +- 你可以使用工程下,test-api-curl.sh 脚本访问接口,也可以其他方式请求接口。 +- 当你请求完成接口后,就可以去arex控制台检查上报的信息和流量录制的数据了。 + +### 7. 流量回放 + +#### 7.1 内网穿透 + +流量回放是从云服务器调用本地,但本地没有对外的公网IP,所以需要内网穿透,让云服务器可以调用。这里的内网穿透工具很多,可以选择;[natapp](https://natapp.cn/)、[cpolar.com](https://www.cpolar.com/) 等。 + +
+ +
+ +```java +Powered By NATAPP Please visit https://natapp.cn (Ctrl+C to Quit) + +Tunnel Status Online + +Version 2.3.9 (New Version 2.4.0) + +Forwarding http://xfg-studio.natapp1.cc -> 127.0.0.1:8091 + +Web Interface Disabled + +Total Connections 0 +``` + +- 启动后,会把本地的服务:端口,暴漏出去,通过 http 域名地址访问。 +- 注意,在natapp配置你的应用端口,如 8091,如果是其他就修改下。 + +#### 7.2 回放配置 - 正确回放 + +
+ +
+ +- 如图,填写回放配置信息。 +- 这次我们是正确回放,也就是本地程序没有任何变更。配置后点OK。 +- 点击后,可以在我们本地服务看到回调的日志,但还要稍微等待一会 arex 平台,他要比对回放结果。完成后,Passed 表示回放通过。 + +#### 7.2 回放配置 - 异常回放 + +首先,我们要修改下代码。 + +```java +@RestController +@RequestMapping("/api/traffic") +public class TrafficController { + + @GetMapping("/query_user_age") + public Integer queryUserAge(@RequestParam("userId") String userId) { + log.info("请求参数:{}", userId); +// return getCache(); + return getDB(); + } + + // 模拟从缓存获取数据 + public Integer getCache() { + return 20; + } + + // 模拟从数据库获取数据 + public Integer getDB() { + return 21; + } + +} +``` + +- 这个接口方法,如果录制流量是用的 getCache(),那么这会就修改为 getDB(),让两次结果不一样。 +- 之后,要重新 install 并重新执行 `run_application_2_agent.sh` 启动项目。关闭执行 Ctrl/Command + C + +配置,流量回放操作。 + +
+ +
+ +- 之后,再来一遍回放。这样就是错误的结果了。 + +### 8. 接口请求(Debug) + +
+ +
+ +- arex 工具,还支持线上请求本地接口操作。 +- 也就是说,你从线上录制的流量,可以直接拿流量,调试本地接口。有了这样一个操作,哪些线上请求参数非常复杂的结构,就可以直接一键对本地进行验证了。 diff --git a/docs/md/road-map/arthas.md b/docs/md/road-map/arthas.md new file mode 100644 index 000000000..914830721 --- /dev/null +++ b/docs/md/road-map/arthas.md @@ -0,0 +1,465 @@ +--- +title: arthas +lock: need +--- + +# 面试官:“程序内存溢出,你怎么找出大对虾(像)”? —— arthas 使用教程 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +👬🏻 兄弟,你被看过手相不,你被号过脉相没。一个没与你生活过就知道你的过去和现在,一个不了解你的日常就知道你是否怀孕是男是女。而你自己亲手开发的Java代码,上线后报错都不知道怎么发生的!!!咋办? + +
+ +
+ +**那有什么手段给 Java 代码号脉吗?🤔** + +还真有这么一个工具,也是大多数老司机程序员👨🏻‍💻非常喜欢用的一款组件,很多时候不好排查的 Bug 都用这个工具进行分析。它可以知道哪个对象创建的过多、方法的出入参、系统运行时候JVM的参数等等,让你随时可以知道系统运行的细节状态。 + +它就是 Arthas,但很多伙伴还没有使用过,这篇文章为大家分享下,如何使用这样一款组件。以及怎么使用这个组件监控部署到 Docker 中的 Java 应用。 + +## 一、组件介绍 + +官网:[https://arthas.aliyun.com/doc/](https://arthas.aliyun.com/doc/) + +Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。 + +当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: + +1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception? +2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了? +3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗? +4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现! +5. 是否有一个全局视角来查看系统的运行状况? +6. 有什么办法可以监控到 JVM 的实时运行状态? +7. 怎么快速定位应用的热点,生成火焰图? +8. 怎样直接从 JVM 内查找某个类的实例? + +## 二、快速入门 + +在 Arthas 官网有一个简单🔜快速入门的案例,它提供了2个程序,一个是 Java 的随机数分解的程序代码,另外一个是 arthas-boot.jar 监控组件。这个套代码可以在任何安装了 Java 环境的服务器上使用,包括你本地、云服务器都可以运行。 + +### 1. 步骤1,案例程序 + +```java +curl -O https://arthas.aliyun.com/math-game.jar +java -jar math-game.jar +``` + +### 2. 步骤2,监控程序 + +```java +curl -O https://arthas.aliyun.com/arthas-boot.jar +java -jar arthas-boot.jar +``` + +- 启动后会出来一个 math-game.jar 之后再敲下回车,就进入了 Arthas 的监控程序了。之后你可以使用监控程序提供的各种命令进行使用。 +- 命令列表:[https://arthas.aliyun.com/doc/commands.html](https://arthas.aliyun.com/doc/commands.html) + +## 三、应用使用 + +### 1. 环境准备 + +为了方便大家进行学习验证,小傅哥这里准备好了一个测试工程和相关的环境安装。你可以如下文的说明进行使用和验证。 + +
+ +
+ +- 工程:[https://github.com/fuzhengwei/xfg-dev-tech-arthas](https://github.com/fuzhengwei/xfg-dev-tech-arthas) +- 说明:`dev-ops` 下提供了部署测试工程的 docker compose 脚本,以及 arthas-boot 启动程序的脚本。另外工程提供了一个接口,每次访问都会创建对象。接口在 Application 中,简单实现。地址:[http://localhost:8091/api/exec](http://localhost:8091/api/exec) - 接口内容可详见代码。 +- 前置:如果你需要云服务器部署操作,可以在 [bugstack.cn](https://bugstack.cn) <路书> 中有云服务器操作,可以学习使用。 + +### 2. 应用安装 + +**操作过程预览** + +
+ +
+ +>如图所示,接下来我们会进行部署操作。 + +#### 2.1 构建脚本 + +```java +# 基础镜像 openjdk:8-jre-slim openjdk:8-jdk-alpine 8-jdk-alpine +FROM openjdk:8-jdk + +# 作者 +MAINTAINER xiaofuge + +# 配置 +ENV PARAMS="" + +# 时区 +ENV TZ=PRC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# 添加应用 +ADD target/xfg-dev-tech-app.jar /xfg-dev-tech-app.jar + +# arthas https://hub.docker.com/r/hengyunabc/arthas +COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthas + +## 在镜像运行为容器后执行的命令 +ENTRYPOINT ["sh","-c","java -Djava.security.egd=file:/dev/./urandom -jar $JAVA_OPTS /xfg-dev-tech-app.jar $PARAMS"] + +# 暴露容器的端口 +EXPOSE 8989 +``` + +- `FROM openjdk:8-jdk`:因为 arthas 需要 jps 的相关命令,所以需要指定安装 jdk 版本。`openjdk:8-jre-slim` 是没有这些脚本的。 +- `hengyunabc/arthas:latest`:把 arthas 的镜像打包到程序中,这样我们可以直接在程序中使用 arthas 组件。 +- 构建过程:无论是本地还是云服务器 Docker 启动后,先执行 `maven install` 程序、后执行 `build.sh` 构建应用镜像。[云服务器操作视频](https://bugstack.cn/md/road-map/linux.html) + +#### 2.2 部署项目 + +```java +version: '3.8' +# docker-compose -f docker-compose-app.yml up -d +# 你需要修改system为你自身系统的仓库名 +services: + xfg-dev-tech: + image: xiaofuge/xfg-dev-tech-app:1.0 + container_name: xfg-dev-tech + restart: on-failure + ports: + - "8091:8091" + environment: + - TZ=PRC + - SERVER_PORT=8091 + # 2c4g 配置,4c8g 翻倍,-Xms4096m -Xmx4096m | -Xmx –Xms:指定java堆最大值(默认值是物理内存的1/4(<1GB))和初始java堆最小值(默认值是物理内存的1/64(<1GB)) + - JAVA_OPTS=-Xms2048m -Xmx2048m -XX:MaxMetaspaceSize=128m -XX:MetaspaceSize=128m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps + volumes: + - ./log:/data/log + - ./arthas/profiler:/arthas-output + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + networks: + - my-network + +networks: + my-network: + driver: bridge +``` + +
+ +
+ +- 脚本说明;脚本提供了 docker compose 安装的配置参数,包括端口、jvm、日志和arthas的映射路径等。 +- 执行脚本;`docker-compose -f docker-compose-app.yml up -d`这样会帮助你启动项目。 +- Mac 电脑 + Docker 可以直接在 IntelliJ IDEA 中点击启动。 + +### 3. 监控部署 + +```java +docker exec -it xfg-dev-tech /bin/sh -c "java -jar /opt/arthas/arthas-boot.jar" +``` + +
+ +
+ +- 在案例工程下有一个 dev-ops/arthas-boot.sh 你可以执行启动 arthas +- 启动后有一个 arthas 的控制台,这个控制台可以输入各种 arthas 提供的命令来监控程序。**非常强大** + +### 4. 访问接口 + +在案例工程中提供了一个用于创建对象的程序,方便进行测试。 + +```java +@Slf4j +@SpringBootApplication +@RestController() +@RequestMapping("/api/") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class); + } + + // 创建线程池 + ExecutorService executorService = Executors.newFixedThreadPool(10); + // 用于存储内存占用对象的列表 + private static final List memoryHolder = new ArrayList<>(); + + /** + * http://localhost:8091/api/exec + */ + @RequestMapping(value = "/exec", method = RequestMethod.GET) + public String exec() { + executorService.submit(() -> { + try { + // 创建一个大对象 +// byte[] largeObject = new byte[10 * 1024 * 1024]; // 10MB + byte[] largeObject = new byte[10 * 1024]; // 10MB + + // 将其加入内存占用列表中,防止被gc + synchronized (memoryHolder) { + memoryHolder.add(largeObject); + } + + log.info("模拟填充大对象"); + + // 模拟一些工作 + Thread.sleep(1000); + + } catch (OutOfMemoryError e) { + System.err.println("Out of memory! Halting further allocation."); + // 如果出现OOM,暂停分配以防止程序崩溃 + executorService.shutdown(); + + } catch (InterruptedException e) { + // 捕获并处理睡眠中断 + Thread.currentThread().interrupt(); + System.err.println("Task interrupted"); + } + }); + return "ok"; + } + +} +``` + +
+ +
+ +- 请求地址:[http://localhost:8091/api/exec](http://localhost:8091/api/exec) +- 请求脚本:提供了请求1次和循环多次的脚本,方便测试。 + +## 四、命令使用 + +使用监控命令前,可以先执行上图中的 curl-one.sh 调用程序接口。你还可以在使用命令的过程中调用测试。 + +命令:[https://arthas.aliyun.com/doc/commands.html](https://arthas.aliyun.com/doc/commands.html) + +### 4.1 dashboard - 当前系统的实时数据面板 + +> [arthas@7]$ dashboard + +
+ +
+ +- 运行脚本后,可以查看到当前服务的运行数据信息。另外这里还有一些 JVM 基础知识,补充给大家。 + +#### 4.1.1 jvm 内存空间都有啥 + +在 Java 运行环境中,JVM(Java Virtual Machine, Java 虚拟机)是执行 Java 程序的引擎,而内存管理是 JVM 非常重要的一部分。JDK 1.8 中的 JVM 内存空间主要分为以下几个区域,每个区域都有其特定的用途: + +1. 堆(Heap): + - 用途:堆是 JVM 中内存最大的一块,用于存储所有的对象实例和数组。几乎所有对象都在此分配内存,垃圾收集器也在这里执行。 + - 分代垃圾收集机制:堆内存被进一步划分为新生代和老年代。 + - 新生代(Young Generation):用于存放新创建的对象。其中又分为伊甸园区(Eden Space)和两个幸存者区(Survivor Spaces,通常称为 S0 和 S1)。 + - 老年代(Old Generation):用于存放生命周期较长的对象。 + +2. 方法区(Method Area)(在 JDK 1.8 中已被细化为元空间 Metaspace): + - 用途:存储已被加载的类元数据、常量、静态变量等。在 JDK 1.8 中,用于取代永久代(PermGen)。 + - 元空间(Metaspace):与永久代不同,元空间用的是本地内存而不是堆内存,这避免了 Java 中永久代容量不足导致的内存问题。 + +3. 栈(Stack): + - 用途:每个线程创建时会分配一个栈,它用来存储局部变量表、操作数栈、动态链接、方法出口等。栈内存会自动随着方法的进出而分配和释放。 + - 特性:栈是线程私有的,每个线程都有自己的栈。 + +4. 程序计数器(Program Counter Register): + - 用途:是一个较小的内存空间,用于指示当前线程所执行的字节码的行号。 + - 特性:它是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区。 + +5. 本地方法栈(Native Method Stack): + - 用途:与 Java 栈类似,但它主要用于处理本地方法(Native Method)的执行。 + - 特性:也是线程私有的,负责本地方法的调度和执行。 + +这些内存区的设计是为了支持 Java 的垃圾收集机制和多线程并发运行,确保 Java 应用程序能够高效和稳定地执行。了解这些内存区的功能和作用,可以帮助开发者优化 Java 应用的性能,并有效管理内存使用。 + +#### 4.1.2 垃圾回收算法 + +GC 核心在于通过不同的算法来高效管理内存资源。常用的 GC 算法包括: + +1. 标记-清除算法(Mark and Sweep): + - 标记阶段:遍历所有的对象引用,标记活动的对象。 + - 清除阶段:清理掉未被标记的对象。 + - 缺点:容易造成内存碎片,因为清除后内存是不连续的。 + +2. 复制算法(Copying): + - 将活动的对象从一块内存(From space)复制到另一块(To space),然后清理整块 From space。 + - 优点:内存分配简单,不会产生碎片。 + - 缺点:需要预留多余的内存空间实行复制。 + +3. 标记-整理算法(Mark-Compact): + - 标记阶段:和标记-清除相似。 + - 整理阶段:将所有活动对象移到一端,处理剩余的空间为可用。 + - 减少了碎片化问题,但相比复制算法更复杂。 + +4. 分代收集算法: + - 根据对象的生命周期不同,将堆分为新生代和老年代。 + - 新生代使用复制算法,老年代使用标记-清除 或 标记-整理。 + - 优点:优化了大多数对象短命这一特性带来的效率。垃圾回收算法。 + +#### 4.1.3 垃圾收集器 + +Java 提供了多种垃圾收集器,适用于不同应用场景: + +1. Serial GC: + - 单线程收集器,非常适合单线程环境和小型应用。 + - 效率高但可能造成长时间的停顿。 + +2. Parallel GC (Throughput Collector): + - 多线程收集器,用于最大化 CPU 吞吐量。 + - 适合后台处理任务和高效利用系统资源的场景。 + +3. CMS GC (Concurrent Mark-Sweep Collector): + - 重点在于缩短停顿时间,适合响应时间敏感的应用。 + - 较轻量级的 GC,但处理老年代垃圾时效率不如 Parallel。 + +4. G1 GC (Garbage-First Collector): + - 设计用于大堆,提供可预测的停顿时间。 + - 将堆划分为不同的区域(Region),针对不同老化级别进行回收。 + +5. ZGC / Shenandoah GC: + - 尝试实现低延迟垃圾回收,几乎不会造成任何长时间的停顿。 + +#### 4.1.4 code_cache (代码缓存区)、compressed_class_space (压缩类空间)、metaspace (元空间),他们的用途是什么,有什么要注意的。 + +在 Java 虚拟机(JVM)中,非堆内存部分包括多个区域,每个区域负责存储不同种类的数据,这里重点介绍 Code Cache(代码缓存区)、Compressed Class Space(压缩类空间)和 Metaspace(元空间)的用途及注意事项。 + +##### 1. Code Cache(代码缓存区) + +用途: +- JIT 编译代码:Java 虚拟机的即时编译器(Just-In-Time Compiler,JIT)会将频繁使用的字节码编译成本地机器码,以提高运行性能。编译后的本地代码存储在代码缓存区中。 +- 性能提升:通过存储这些编译后的代码,JVM 可以避免对同一代码片段重复进行解释,从而提升执行速度。 + +注意事项: +- 内存限制:代码缓存区有固定的最大值(可通过 `-XX:ReservedCodeCacheSize` 参数配置),需要注意在长时间运行的应用程序中这个区不会被耗尽,否则可能导致性能退化。 +- 清理与优化:JVM 在启动时和运行过程中可能会执行代码缓存的清理和优化,这个过程会对性能有一定影响。 + +##### 2. Compressed Class Space(压缩类空间) + +用途: +- 存储类元数据:在开启类指针压缩的情况下(默认开启),这个空间用于存储类元数据的压缩指针,以节省内存空间。 +- 支持元空间:压缩类空间是元空间的一部分,专门用来优化类加载器的内存消耗。 + +注意事项: +- 有固定最大限制:虽然是元空间的一部分,但压缩类空间有自己的最大内存限制(默认为 1GB),可以通过 `-XX:CompressedClassSpaceSize` 参数调整。 +- 适配性告诉:32 位 JVM 不使用压缩类空间功能,64 位 JVM 在默认情况下是开启的,因其节省的内存对大型应用尤其重要。 + +##### 3. Metaspace(元空间) + +用途: +- 替换永久代:Java 8 开始,用元空间替代了永久代,用于存储类的元数据(类的结构信息、方法、字段等)。 +- 动态调整大小:与永久代不同,元空间作为物理内存的一部分,不断增长(直到达到物理内存上限),默认不设置上限,灵活性更高。 + +注意事项: +- 内存泄漏风险:尽管更灵活,但如果不加限制,糟糕的类加载策略可能导致元空间内存泄漏。在开发环境中尤其要防止过度类加载。 +- 调整与监控:可以通过 `-XX:MetaspaceSize` 和 `-XX:MaxMetaspaceSize` 进行调节,适当限制,在实际使用中密切监控。 +- 自动释放:JVM 在内存不足时会尝试回收未使用的类加载的内存,虽然一般无需手动干预,了解这方面的配置有助于优化内存使用。 + +这些部分的配置和监控是调优 JVM 的重要组成部分,适当的配置可以防止内存泄漏、排查性能瓶颈,并为应用程序的稳定性提供保障。 + +### 4.2 火焰图 + +#### 4.2.1 生成脚本 + +profiler 命令支持生成应用热点的火焰图。本质上是通过不断的采样,然后把收集到的采样结果生成火焰图。火焰图可以清楚的看到程序运行中对象的创建,这样你就可以发现那些大对虾(像)从哪来的? + +操作过程,开启采集 `profiler start --event alloc` 等待程序执行,关闭采集 `profiler stop --format html` + +我们操作中,开启采集后,可以先执行多次的 curl-one.sh 调用接口,多产生点对象。 + +```java +profiler start --event alloc + +profiler getSamples + +profiler status + +profiler stop --format html +``` + +
+ +
+ +- 执行完成后,你会得到一个火焰图的 html 文件。之后就可以打开了。 + +#### 4.2.2 查看图表 + +
+ +
+ +1. 这里的x轴代表采样总量(也就是此刻所有执行的耗时cpu的方法)。这是注意的是x 轴并不代表时间,而是所有的调用方法合并后,按字母顺序排列。 +2. Y轴代表方法的调用栈深度,每一层都是一个方法。顶部是正在执行的方法。当然调用栈越深,火焰就越高。 +3. 鼠标可以点击的选中的每个框就代表了一个栈里的函数,其宽度可以直接理解为CPU时间占比(其实是采样的数量以及与采样总量的占比)。 +4. 那么,也就是说占比比较宽的框就表示:a.该函数运行时间较长(单次时间长)b.被调用次数较多.(调用频率高)进而被采样的次数比较多,占用的CPU时间多。 +4. 这样你就知道了是哪里在创建大对像。比如一些数据库对象,缓存对象等,特别大,又耗时,一下就监控出来了。 + +> Arthas 提供的功能还有很多,不过咱们要换个更加舒服的方式使用了。如下。 + +## 五、IDEA Plugin Arthas + +### 1. 安装插件 + +
+ +
+ +### 2. 使用插件 + +这些命令你都可以通过 Arthas IDEA Plugin 插件复制出来使用。 + +- [monitor](https://arthas.aliyun.com/doc/monitor.html) - 方法执行监控 +- [stack](https://arthas.aliyun.com/doc/stack.html) - 输出当前方法被调用的调用路径 +- [trace](https://arthas.aliyun.com/doc/trace.html) - 方法内部调用路径,并输出方法路径上的每个节点上耗时 +- [tt](https://arthas.aliyun.com/doc/tt.html) - 方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测 +- [watch](https://arthas.aliyun.com/doc/watch.html) - 方法执行数据观测 + +更多命令 [https://arthas.aliyun.com/doc/commands.html](https://arthas.aliyun.com/doc/commands.html) + +#### 2.1 复制命令 + +
+ +
+ +#### 2.2 使用命令 + +
+ +
+ +- 如图你就可以监控到方法的运行信息了。 + +### 3. 其他命令 + +#### 3.1 查看代码 + +
+ +
+ +- 线上运行的代码,不确定是否是最新的,那么这样就可以一步获取到并对比。 + +#### 3.2 接口参数 + +
+ +
+ +- 有时候预发验证服务,因为不会打印所有的服务接口出入参信息,但遇到问题不好排查。 +- 那么使用 Arthas 插件,就可以直接获取到接口的出入参了,方便排查问题。 + +> 其他更多命令可以通过官网的命令提示和插件一起使用验证了。 diff --git a/docs/md/road-map/buddy.md b/docs/md/road-map/buddy.md new file mode 100644 index 000000000..0902d75f4 --- /dev/null +++ b/docs/md/road-map/buddy.md @@ -0,0 +1,213 @@ +--- +title: Docker 镜像构建 - Buddy +lock: need +--- + +# Docker 镜像构建 - Buddy + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +2c2g 云服务器,你占用了83%的内存空间!**傅哥!Jenkins 用不起呀!** 我好不容易找对象要50块买的一年服务器,要学你的项目。现在都被 Jenkins 吃了! + +
+ + + +
+ +哈哈哈,最近一段时间跟 `CI&CD` 工具杠上了,一路调研和尝试体验了 Jenkins、GitLab、Drone、CircleCI、TeamCity、Bamboo、Travis CI、Codeship、GoCD、Wercker、Semaphore、Nevercode、Spinnaker、Buildbot... 等十多款风格迥异的构建部署工具。可以说 Jenkins 依旧是当家老大。所以小傅哥的第一篇 `CI&CD` 文章则是[《用上了 Jenkins,个人部署项目是真方便!》](https://mp.weixin.qq.com/s/tWuse0ejDOTQho2182iYWw) 但这货也确实是嘎嘎占内存! + +为此,小傅哥今天分享另外一款,轻量、简单、好用,还好看的在线 `CI&CD` 工具。直接点点点,配置下就能使用了。 + +
+ +
+ +咋样,这款工具看着不错吧。它叫 [buddy.works](https://buddy.red/) 是一款付费软件,但提供了免费白皮袄的额度。足够我们个人用户使用。 + +>本文会向小伙伴介绍这块工具的使用操作,方便小白们快速上手。在文末还提供了Java项目学习,让小白从实战中积累变成经验。 + +## 一、工具相关 + +- 官网(中文):[https://buddy.red](https://buddy.red) +- 文档(中文):[https://docs.buddy.red](https://docs.buddy.red) +- 官网(英文):[https://buddy.works](https://buddy.works) + +这款工具号称 **「最易用的CI/CD没有之一」**,体验过后我表示,他说的对! + +## 二、注册账户 + +地址:[https://buddy.works/sign-up?locale=cn&utm_campaign=buddy.red](https://buddy.works/sign-up?locale=cn&utm_campaign=buddy.red) + +
+ +
+ +## 三、添加项目 + +地址:[https://app.buddy.works/](https://app.buddy.works/) + +
+ +
+ +- 首先,需要在首页选择【添加项目】,并根据步骤选择你的仓库。 +- 之后,这里小傅哥选择了专用服务器,填写我的Gitcode仓库地址。「Gitcode的账密就是CSDN的账密」 +- 提示:你可以Fork代码到自己的仓库进行使用 [https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-jenkins](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-jenkins) + +## 四、添加流水线 + +流水线,就跟它的名字一样,用于构建项目所需的一条链路流程。如;Maven 构建、镜像打包&发布、SSH 基本驱动容器启动。也可以是 Maven 构建后直接通过 SFTP 把Jar传递到 Linux 服务器,在通过 SSH 链接执行 Shell 脚本完成 Jar 的镜像打包和部署。 + +所以,接下来我们先来完成一个流水线的最基础 Maven 构建,之后再分别添加不同类型的流水线操作。「Buddy提供了各种插件,你可以分别组合他们进行使用,完成项目的部署。」 + +注意📢:流水线上的节点,你可以在右侧的绿色按钮进行关闭,只执行你需要的节点。比如现在你已经知道 Maven 构建成功了,只需要推送镜像。那么可以去掉上一个节点,这样速度更快。 + +
+ +
+ +### 1. Docker 流水线 + +- 说明:这条流水线的步骤为;Maven 构建、构建Docker镜像、推送Docker镜像、SSH连接服务器拉取镜像和部署。 +- 步骤:你可以点击**小加号+**,选择添加不同的流水线节点。 + +
+ +
+ +#### 1.1 构建Docker镜像 + +
+ +
+ +#### 1.2 推送Docker镜像 + +
+ +
+ +- 注意📢:你已经在 [https://hub.docker.com/](https://hub.docker.com/) 创建了自己的镜像,比如我的是;[fuzhengwei/xfg-dev-tech-jenkins](fuzhengwei/xfg-dev-tech-jenkins) 这样你的镜像才能PUSH进去。 + +#### 1.3 SSH连接服务器拉取镜像和部署 + +
+ +
+ +```shell +# 先删除之前的容器和镜像文件 +if [ "$(docker ps -a | grep xfg-dev-tech-jenkins)" ]; then +docker stop xfg-dev-tech-jenkins +docker rm xfg-dev-tech-jenkins +fi +if [ "$(docker images -q xfg-dev-tech-jenkins)" ]; then +docker rmi xfg-dev-tech-jenkins +fi + +docker pull fuzhengwei/xfg-dev-tech-jenkins:2.0 +docker run -itd -p 8091:8091 --name xfg-dev-tech-jenkins fuzhengwei/xfg-dev-tech-jenkins:2.0 +``` + +- 注意:拉取镜像的速度取决于你的服务器所在的地区,有时候会拉取失败。如果你的云服务器拉取镜像总是失败❌,可以选择第二个方式 SSH 流水线。它不需要走到DockerHub,能减少网络操作。 + +### 2. SSH 流水线 + +说明:这条流水线的步骤为;Maven构建、通过 SFTP 上传构建的Jar到云服务器。之后通过 SSH 执行 Shell 脚本,在云服务器创建出 Dockerfile 「注意路径」,这样 Docker镜像就直接在云服务器的 Docker上了,不需要在通过 DockerHub 拉取镜像。最后通过 SSH 执行 Shell 脚本启动服务即可。 + +
+ +
+ +#### 2.1 SFTP 上传Jar + +
+ +
+ +- 配置好传输地址、目标地址,通过 SSH 连接云服务器进行传输。 +- `/dev-ops`这个是云服务器的地址,你可以自己定义任何的地址。如果文件夹不存在,也会自动创建。 + +#### 2.2 创建Dockerfile + +
+ +
+ +```shell +#!/bin/bash + +cd /dev-ops + +# 定义Dockerfile的路径 +DOCKERFILE_PATH="https://bugstack.cn/images/roadmap/tutorial/Dockerfile" + +# 创建Dockerfile并写入内容 +cat > $DOCKERFILE_PATH << 'EOF' +# 基础镜像 openjdk:8-jre-slim +FROM openjdk:8-jre-slim + +# 作者 +MAINTAINER xiaofuge + +# 配置 +ENV PARAMS="" + +# 时区 +ENV TZ=PRC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# 添加应用 +ADD target/xfg-dev-tech-app.jar /xfg-dev-tech-app.jar + +## 在镜像运行为容器后执行的命令 +ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /xfg-dev-tech-app.jar $PARAMS"] +EOF + +echo "Dockerfile has been created." +``` + +- 因为我们把 Maven 构建的 Jar 传到了云服务器。所以需要在 Jar 对应的目录下,创建出 Dockerfile 这样就可以通过执行 Dockerfile 完成镜像的构建了。 +- 此外如果你的代码库配置有 Dockerfile,也可以通过 SFTP 的方式把 Dockerfile 上传到云服务器对应 Jar 的位置。「我只是帮你选择了一个更稳的方式」 + +#### 2.3 运行服务 + +
+ +
+ +```shell +# 先删除之前的容器和镜像文件 +if [ "$(docker ps -a | grep xfg-dev-tech-jenkins)" ]; then +docker stop xfg-dev-tech-jenkins +docker rm xfg-dev-tech-jenkins +fi +if [ "$(docker images -q xfg-dev-tech-jenkins)" ]; then +docker rmi xfg-dev-tech-jenkins +fi + +cd /dev-ops +docker build -t xiaofuge/xfg-dev-tech-jenkins:1.3 . +docker run -itd -p 8091:8091 --name xfg-dev-tech-jenkins xiaofuge/xfg-dev-tech-jenkins:1.3 +``` + +- `cd /dev-ops`进入到文件路径。之后构建镜像和部署。 + +## 五、运行流水线 + +
+ +
+ +- 点击**运行**,就可以顺序的执行你的配置的流水线了。如果某个执行失败也可以重试。 +- 如果你执行完成3个节点,最后一个失败。那么是可以把前面的流程关闭,只去验证最后一个流程。这样会更快。 + +>怎么样,是不是很方便。但要注意,https://app.buddy.works - 账单中,会记录你的免费额度。 + + diff --git a/docs/md/road-map/cainiao.md b/docs/md/road-map/cainiao.md new file mode 100644 index 000000000..b723195a8 --- /dev/null +++ b/docs/md/road-map/cainiao.md @@ -0,0 +1,96 @@ +--- +title: 菜鸟教程 - 交互式学习 +lock: need +--- + +# 菜鸟教程 - 交互式学习 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +一直以来我都有一个想法,做一套更符合新人上手学习的 **🐦菜鸟教程**,以在线互动交互的方式学习入门编程技术。 + +但原本`互动交互`这东西太耗前端能力了,好在,现在有大量的 Ai Agent 工具,帮我我把想法落地了!—— 我有创新和内容,Ai Agent 出设计和效果!嘎嘎舒服!**美美桑内,一个劲叭叭!** + +
+ +
+ +**这是一个什么样的教程?** + +小傅哥做的教程分为3大类,一类是带着做**实战项目**(拼团、大营销、技术组件等)、一类是**编程路书**(系统架构、开发技术、发布部署等),现在再准备开启一项更基础的,辅助刚入门的小白来学习编程技能。 + +这套教程,专门为小白设计,包括;环境配置(Mac、Windows)、`SpringBoot`、`MyBatis`、`Redis`、`Mysql`、`Maven` 等各项编程技术的入门讲解。后续还会继续迭代新的课程进去。 + +本套教程不只是干巴巴的讲技术,而是做了学习互动,你可以看到如 SpringBoot 课程下模拟的在线 IntelliJ IDEA 打开教程代码,还可以看到 Dev-Ops 云服务器操作时候的互动过程。这样的学习,即使是小白,也可以很好理解自己在做什么,为什么做。 + +## 一、能学到啥 + +此套教程,以入门编程为主,让小卡拉米和小菜鸟,快速补充基础技术知识。全套教程,会以目前最新的环境配置、技术框架、编程技巧等为本,设计基础入门教程。包括; + +- 【框架】22节课程,SpringBoot 入门教程,涵盖;环境搭建与项目初始化、核心概念与配置管理、数据库集成与容器管理、高级特性与性能优化、测试与监控管理、企业级应用特性。 +- 【运维】Dev-Ops 云服务器操作,一键安装脚本、云服务器 SSH 模拟(学习命令)、本地环境 + 云服务器,配置、构建、上线,学习环境基础使用。 +- 【环境】基于 Apache Maven 3.8.9 版本编写,涵盖了 Maven 的核心概念、配置和实际应用。 +- 【后端】MySQL 从基础到高级,掌握 MySQL 数据库开发与管理,完整覆盖 MySQL 基础到高级概念,详细对比 MySQL 5.7 与 8.x 版本差异,涵盖性能优化和高级特性。 +- 【后端】Redis 教程,从入门到精通的完整学习指南。学习 Redis 的核心概念和高性能特性,掌握字符串、哈希、列表、集合等数据类型。 + +> 以上内容,已更新完成 SpringBoot、Dev-Ops 部分,其他内容也更新了目录,后续详细更新内容。 + +## 二、课程展示 + +**课程地址**:[https://gaga.plus/](https://gaga.plus/) + +
+ +
+ +你可以进入到 gaga.plus,之后点击菜鸟教程,开启你的入门编程之旅! + +### 1. 课程目录 + +
+ +
+ +### 2. 教程举例 - SpringBoot + +
+ +
+ +
+ +
+ +- 进入教程,学习章节内容。还可以在线看,模拟 IntelliJ IDEA 打开的工程代码。非常方便学习。 + +### 3. 教程举例 - Dev-Ops + +#### 3.1 云服务器连接 + +地址:[https://gaga.plus/app/dev-ops/tutorials/what-is-a-cloud-server.html](https://gaga.plus/app/dev-ops/tutorials/what-is-a-cloud-server.html) + +
+ +
+ +
+ +
+ +#### 3.2 云服务器使用 + +地址:[https://gaga.plus/app/dev-ops/tutorials/cloud-server-dev-ops.html](https://gaga.plus/app/dev-ops/tutorials/cloud-server-dev-ops.html) + +
+ +
+ +- 你可以点击各个按钮【本地环境搭建、Linux服务器配置、SSH远程连接、本地开发连接、代码部署(1、2)】,这样可以动态的看到各个演示环节。 +- 此外,你可以在 SSH 连接后,基于提供的命令教程进行学习。 + +好啦,如果你还有什么样的基础内容学习诉求,可以在评论区留言,我会优先进行开发上线! diff --git a/docs/md/road-map/canal.md b/docs/md/road-map/canal.md new file mode 100644 index 000000000..b9c5672b8 --- /dev/null +++ b/docs/md/road-map/canal.md @@ -0,0 +1,337 @@ +--- +title: Canal +lock: need +--- + +# 通过MySQL binlog日志,使用canal同步分库分表数据,到 Elasticsearch + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,配置出一套 Canal 工具服务,来同步分库分表的数据到 Elasticsearch 文件夹系统中。同时在 SpringBoot 工程中,配置出两套数据源,一套是 MySQL + MyBatis,一套是 Elasticsearch + MyBatis。【这是非常重要的设计手段】 + +虽然现在有 TiDB 这样的分布式数据库,但对于分库分表 + 数据同步ES,依然是非常主流的方案。同时也有一部分是把分库分表的数据同步到 TiDB 使用。 + +本文涉及的工程: + +- xfg-dev-tech-canal:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-canal](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-canal) +- docs/dev-ops/xfg-dev-tech-canal-docker-compose.yml:提供了所需的环境安装,mysql、canal-server、canal-adapter、elasticsearch、kibana +- Github:[https://github.com/alibaba/canal](https://github.com/alibaba/canal) + +## 一、组件介绍 + +
+ +
+ +canal ,译为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。 + +早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。 + +它的工作原理是,canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议。在 MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal ) 这样 canal 再解析 binary log (binlog)进行配置分发,同步到 Elasticsearch 等系统中进行使用。 + +那么有了 canal 就可以把分库分表的数据同步到 Elasticsearch,提供汇总查询和聚合操作,也就不需要把轮训每个分库分表数据了。 + +## 二、测试预期 + +本文的案例会把MySQL,2库4表的数据,通过 Sharding 分库分表写入数据后,同步到 Elasticsearch。分库分表如下(环境安装中会自动安装数据库和设置库表); + +
+ +
+ +## 三、环境安装 + +为了让读者伙伴更加简单的学习到这一项方案技能,小傅哥这里把所需的环境都配置成一整套的 docker compose 脚本文件(ARM、AMD),你只要执行安装即可。安全前注意,无论是本机还是云服务器都需要安装 [docker-ce](https://bugstack.cn/md/road-map/docker.html) + +### 1. 环境脚本 + +
+ +
+ +- 打开 xfg-dev-tech-canal 工程,下面就是 docker compose 的执行脚本。 +- mac/windows 如果安装了 docker 可以直接点击如图的三角号安装。如果是在 Linux 安装了 docker 可以把 dev-ops 整个文件夹都上传到云服务器,之后通过脚本;`docker-compose -f xfg-dev-tech-canal-docker-compose.yml up -d` 进行安装。 + +#### 1.1 开启 binlog + +mysql 数据同步需要创建一个 canal 的账户,之后还需要开启 binlog 日志 + +
+ +
+ +- 在 mysql 配置文件夹中,设置了初始化授权的账户、导入的库表,以及开启 mysql-bin 和配置要采集的库。 +- 如果你有配置自己其他的库要同步也可以如此配置。 + +#### 1.2 库表采集配置 + +
+ +
+ +- 本文选择的是 es 同步方式,所以需要在 canal-adapter 中 es7 文件夹添加同步的库表 yml 配置。 +- 以及在 application.yml 中配置出需要链接的库表以及同步的目标地址,也就是 es 的地址。【因为本文的案例是在同一个 docker compose 下安装,所以直接用名称 elsticsearch 即可访问】 + +### 2. 运行状态 + +
+ +
+ +- 安装完成后可以进入 portainer 查看各个组件的运行,如果有哪个运行失败了,可以点击那个小文件的图标,它可以查看日志。 + +### 3. 创建索引 + +在 doc/dev-ops/curl 下提供了创建 Elasticsearch 的脚本;你可以点击执行或者直接复制执行,也可以复制导入到 ApiPost 里执行。 + +以上这些脚本是为了创建出数据库表同步到 Elasticsearch 后对应的索引和映射的字段。文章下面会用到。 + +#### 3.1 创建 + +```shell +curl -X PUT "127.0.0.1:9200/xfg_dev_tech.user_order" -H 'Content-Type: application/json' -d' +{ + "mappings": { + "properties": { + "_user_id":{"type": "text"}, + "_user_name":{"type": "text"}, + "_order_id":{"type": "text"}, + "_uuid":{"type": "text"}, + "_create_time":{"type": "date"}, + "_update_time":{"type": "date"} + } + } +}' +``` + +#### 3.2 添加 + +```shell +curl -X PUT "127.0.0.1:9200/xfg_dev_tech.user_order/_mapping" -H 'Content-Type: application/json' -d' +{ + "properties": { + "_sku_name": { + "type": "text" + } + } +}' +``` + +#### 3.3 删除 + +```shell +curl -X DELETE "127.0.0.1:9200/xfg_dev_tech.user_order" +``` + +### 4. 创建索引(Kibana) + +#### 4.1 索引管理 + +**地址**:[http://127.0.0.1:5601/app/management/kibana/indexPatterns](http://127.0.0.1:5601/app/management/kibana/indexPatterns) + +
+ +
+ +- 填写完,点击创建索引模式即可。 + +#### 4.2 数据页面 + +**地址**:[http://127.0.0.1:5601/app/discover](http://127.0.0.1:5601/app/discover) + +
+ +
+ +- 等后面同步数据了以后,直接在这里点刷新就可以看到了。 + +### 5. 许可证 + +kibana 提供了免费30天的试用许可,安装后可以使用 x-pack-sql-jdbc。它的好处是可以让我们通过 MyBatis 的方式查询 Elasticsearch 数据。 + +**地址**:[http://127.0.0.1:5601/app/management/stack/license_management](http://127.0.0.1:5601/app/management/stack/license_management) + +
+ +
+ +Elasticsearch 提供了 x-pack-sql-jdbc,让对 Elasticsearch 的查询也可以像使用 MySQL 数据库一样通过 MyBatis 进行查询。但这个 x-pack-sql-jdbc 是付费的,免费可以使用 30 天。之后你可以选择使用重新安装,破解,或者使用 Elasticsearch 的查询方式。还可以自己开发一个 Elasticsearch JDBC,GitHub 上也有类似的组件。 + +使用时需要引入 POM 配置; + +```pom + + + org.elasticsearch.plugin + x-pack-sql-jdbc + 7.17.14 + +``` + +## 三、工程配置 + +本节涉及到了简明教程中所讲解的 [Sharding 分库分表](https://bugstack.cn/md/road-map/sharding-jdbc.html)的使用,因为我们需要把分库分表的数据通过 canal 同步到 Elasticsearch。(也可以使用其他分库分表组件) + +在工程中配置一套 Sharding 分库分表映射的 MyBatis MyBatis,在配置一套 Elasticsearch x-pack-sql-jdbc 数据源映射的 MyBatis Mapper。这样可以读写分别走自己设定好的 Mapper 对象了。 + +
+ +
+ +### 1. 创建数据源 + +```java +@Configuration +public class DataSourceConfig { + + @Configuration + @MapperScan(basePackages = "cn.bugstack.xfg.dev.tech.infrastructure.dao.elasticsearch", sqlSessionFactoryRef = "elasticsearchSqlSessionFactory") + static class ElasticsearchMyBatisConfig { + + @Bean("elasticsearchDataSource") + @ConfigurationProperties(prefix = "spring.elasticsearch.datasource") + public DataSource igniteDataSource(Environment environment) { + return new EsDataSource(); + } + + @Bean("elasticsearchSqlSessionFactory") + public SqlSessionFactory elasticsearchSqlSessionFactory(DataSource elasticsearchDataSource) throws Exception { + SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); + factoryBean.setDataSource(elasticsearchDataSource); + factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mybatis/mapper/elasticsearch/*.xml")); + return factoryBean.getObject(); + } + } + + @Configuration + @MapperScan(basePackages = "cn.bugstack.xfg.dev.tech.infrastructure.dao.mysql", sqlSessionFactoryRef = "mysqlSqlSessionFactory") + static class MysqlMyBatisConfig { + + @Bean("mysqlDataSource") + @ConfigurationProperties(prefix = "spring.mysql.datasource") + public DataSource mysqlDataSource(Environment environment) { + return DataSourceBuilder.create() + .url(environment.getProperty("spring.mysql.datasource.url")) + .driverClassName(environment.getProperty("spring.mysql.datasource.driver-class-name")) + .build(); + } + + @Bean("mysqlSqlSessionFactory") + public SqlSessionFactory mysqlSqlSessionFactory(DataSource mysqlDataSource) throws Exception { + SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); + factoryBean.setDataSource(mysqlDataSource); + factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mybatis/mapper/mysql/*.xml")); + return factoryBean.getObject(); + } + } + +} +``` + +- ElasticsearchMyBatisConfig 使用 EsDataSource 创建数据源和映射 MyBatis Mapper 文件。 +- MysqlMyBatisConfig 使用 DataSourceBuilder 创建 Sharding 提供的数据源和映射 MyBatis Mapper 文件。 + +### 2. Mapper 映射 + +```xml + + + + + + + + + + + + + + + + +``` + +这个是 Elasticsearch 映射的 Mapper 文件,映射的字段就是前面安装环境的时候设置的索引和字段。现在你使用 Elasticsearch 就不用在工程中硬编码查询语句了,变得非常方便。 + +## 四、工程测试 + +### 1. 写入数据 + +```java +@Test +public void test_insert() throws InterruptedException { + for (int i = 0; i < 3; i++) { + UserOrderPO userOrderPO = UserOrderPO.builder() + .userName("小傅哥") + .userId("xfg_" + RandomStringUtils.randomAlphabetic(6)) + .userMobile("+86 13521408***") + .sku("13811216") + .skuName("《手写MyBatis:渐进式源码实践》") + .orderId(RandomStringUtils.randomNumeric(11)) + .quantity(1) + .unitPrice(BigDecimal.valueOf(128)) + .discountAmount(BigDecimal.valueOf(50)) + .tax(BigDecimal.ZERO) + .totalAmount(BigDecimal.valueOf(78)) + .orderDate(new Date()) + .orderStatus(0) + .isDelete(0) + .uuid(UUID.randomUUID().toString().replace("-", "")) + .ipv4("127.0.0.1") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334".getBytes()) + .extData("{\"device\": {\"machine\": \"IPhone 14 Pro\", \"location\": \"shanghai\"}}") + .build(); + userOrderDao.insert(userOrderPO); + Thread.sleep(100); + } +} +``` + +- 循环插入3条数据,按需你可以设置更多条数据。 +- 这里的用户编号 userId 是随机的,也是切分键的 ID,所以会在不同的库表写入数据。 + +### 2. 数据验证 + +- MySQL:[http://127.0.0.1:8899/](http://127.0.0.1:8899/) docker compose 配置的管理后台,可以 root/123456 登录 +- Kibana:[http://127.0.0.1:5601/app/discover](http://127.0.0.1:5601/app/discover) 查询写入的数据。 + +
+ +
+ +### 3. 查询数据 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class UserOrderDaoTest { + + @Resource + private IElasticsearchUserOrderDao userOrderDao; + + @Test + public void test() { + List userOrderPOS = userOrderDao.selectByUserId(); + log.info("测试结果:{}", JSON.toJSONString(userOrderPOS)); + } + +} +``` + +
+ +
+ +- 通过 Elasticsearch 走 x-pack-sql-jdbc 的方式再把数据查询出来。 \ No newline at end of file diff --git a/docs/md/road-map/cloud-server.md b/docs/md/road-map/cloud-server.md new file mode 100644 index 000000000..c12df0158 --- /dev/null +++ b/docs/md/road-map/cloud-server.md @@ -0,0 +1,98 @@ +--- +title: CloudServer 云服务器是什么? +lock: need +--- + +# CloudServer 云服务器是什么? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +可以这么说,云服务器的使用,是每个程序员在编程路上的必备技能。因为我们所有开发的代码,最终都将部署到云服务器运行,为用户提供运算能力。包括;`京东`、`淘宝`、`拼多多`、`滴滴`、`美团`,他们的服务,都是在云服务器上运行。**不过,你会使用云服务器吗?🤔** + +
+ +
+ +**你是否对云服务器比较陌生?** + +其实不少伙伴没怎么使用过云服务器,一方面是因为没有意识到项目最终是在云服务器部署(也没有主动虚席),另一方面是因为云服务器基本是使用 Linux 系统,不像是 Windows/Mac 电脑一样,基本是没有图形化界面的,所以对于云服务器的使用就有了一道**障碍**。 + +不过,鉴于云服务器的技能这么重要。所以,小傅哥准备做一套基础的**免费教程**,来教会小白伙伴上手云服务器使用。这套教程,通过在线网站提供图形化界面 + 模拟体验的方式,教会小白伙伴上手云服务器。 + +> 📢 本套课程,是小傅哥多年的沉淀的经验,转化最易接受和上手学习的方式,让小白伙伴可以轻松的掌握全套关于云服务器的知识和实践运用。 + +## 一、课程地址 + +**地址**:[https://gaga.plus/](https://gaga.plus/) + +
+ +
+ +> 如图,教程地址,会陆续更新在线课程。可以收藏地址,关注后续课程更新(更新内容会在公众号「bugstack虫洞栈」发布通知)。 + +## 二、我能学到什么? + +通过这套教程,你可以完成的理解云服务器的用途,掌握云服务器的操作技能。从零基础到熟练运用云服务器 + 本地环境完成项目的开发、测试和部署的全流程。教程采用在线演示体验,互动式学习方式,以及一键安装的脚本,让复杂的云服务器操作,变得简单和易于理解。 + +- 🏗️理解云服务器的概念、选择、配置、管理,以及通过 shell 脚本,完成自动化的初始化环境操作。 +- ☕ 在云服务器部署 Git、Java、Maven 环境,可以通过云服务器完成项目的构建。 +- 🐳 Docker 容器化技术,完成项目的构建镜像、操作部署等。 +- 🔐 远程连接与安全,学习 SSH 链接云服务器和 Linux 命令操作云服务器。 +- 🌿 版本控制与协作,可以在云服务器通过 Git 检出项目进行维护使用。 +- 🚀 部署与运维,以代码开发、Git Push、Git Pull、mvn clean install、docker,一些列命令,完成项目的打包和上线。 + +这套教程的特色在于完全可视化学习,注重理论与实践,不仅让您了解云服务器的基本概念,更重要的是通过交互式演示和自动化脚本,让您能够快速上手并在实际项目中应用这些技术。无论您是初学者还是有一定经验的开发者,都能从中获得有价值的知识和技能提升。 + +## 三、小傅哥为你准备了什么? + +小傅哥,今天为你准备了第一套在线教程**《第1节:云服务器,是什么?》** + +在这套教程,你可以直接直观的理解到云服务器相对于本地电脑,其实就等同于你的另外一个电脑而已。并且,小傅哥这里做了模拟的链接,**你可以在线体验通过 SSH 连接云服务器,并执行 Linux 脚本运行**。关于这些写脚本,已经写好了相关的执行动作和说明,很适合小白伙伴学习使用。 + +### 1. 云服务器链接 + +
+ +
+ +- 首先,云服务器,就是你在互联网上的另一台电脑,随时随地访问使用,任意安装和卸载软件。尤其对软件学习来说,可以避免windows电脑的兼容问题。 +- 之后,你可以点击建立 SSH 链接,这个时候会弹出 SSH 链接操作。让你真的的体验到如何通过 SSH 链接云服务器。 + +### 2. Linux 命令模拟 + +#### 2.1 建立链接(SSH) + +
+ +
+ +- 如图,IP、用户名、密码、端口号,就是你在获得一个云服务器后,可以拿到的连接信息。在你真实使用云服务器时,也是需要获得这些信息进行连接操作。 +- 之后,通过点击建立SSH连接,模拟登录云服务器。**没有云服务器的伙伴,也能随时体验云服务器操作** + +#### 2.2 体验登录 + +
+ +
+ +- 我知道很多小伙伴,不太熟悉 Linux 命令。所以,小傅哥这里做了模拟操作,你可以点击命令,之后就可以看到执行结果,以及告诉你命令的用途。此外还有 Git、Maven、Docker 常见命令的学习。 +- 通过这套东西的学习,对于云服务器的使用,基本就可以有一个操作的使用认知了。后续在看到要执行命令,也都能理解,是怎么的一个操作过程。 + +#### 2.3 常用命令 + +**如下,这些命令都已经加入到体验区 👨🏻‍💻** + +
+ +
+ +- 你可以点击各个命令,学习对应的场景用途。 +- 如果,小伙伴还有其他命令想学习,也可以留言。我会进行补充。 + +> 后续,还有更炸裂的在线模拟学习课,让你身临其境的学习云服务器的操作。我们下一节再见👋🏻 diff --git a/docs/md/road-map/connection-pool.md b/docs/md/road-map/connection-pool.md new file mode 100644 index 000000000..4ab840cb4 --- /dev/null +++ b/docs/md/road-map/connection-pool.md @@ -0,0 +1,157 @@ +--- +title: ConnectionPool +lock: need +--- + +# MySQL 连接池 c3p0、dbcp、druid、hikari + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式,向读者展示 SpringBoot 应用程序对接 MySQL 时,在使用不同连接池以及不使用连接池时,在增删改查的一个性能对比。这也包括更新和查询时,索引字段的关键性。 + +内容开始之前,你知道1张21个字段的表,存放100万数据,大于会占用多少空间容量吗?如果这100万数据在不使用连接池的方式,10个并发一条条插入,要多少时间? + +问题1需要`350M`左右、问题2需要`2-3`小时。可能你会说,这字段不一定都多长,这插入不知道的机器配置。但其实这些并不是重要的,如果你做过一次你肯定能说出自己一个所在机器配置下的数据验证结果。而本文则借着对 MySQL 连接池的 ApacheBench 压测验证,让读者伙伴可以学习到相关的知识。 + +本文涉及的工程: + +- xfg-dev-tech-connection-pool:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-connection-pool](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-connection-pool) - 工程内含有环境的安装脚本;`mysql-docker-compose.yml`、`apachebench-docker-compose.yml`、`road_map_8.0.sql` + +## 一、案例背景 + +`拿100万订单数据,压到数据库中!` + +本章节小傅哥会带着大家初始化一个空的数据库表,并向数据库表中写入100万数据。之后在分别不使用`连接池`和使用不同的连接池(c3p0、dbcp、druid、hikari)写入数据,测试各个连接池的性能。这也能让大家知道,日常我们应该选择哪个连接池。 + +## 二、环境配置 + +因为本章节很偏实操,所以需要大家做下提前安装好 Docker 环境,以便于执行本章节工程中的脚本和代码。可参考Java简明教程系列内容 Docker、Portainer 学习安装和使用。 + +
+ +
+ +- 在 IntelliJ IDEA 打开 xfg-dev-tech-connection-pool 分别点开 mysql-docker-compose、apachebench-docker-compose,之后点击安装即可。 +- 执行完脚本,你可以得到一份安装好的 MySQL 8.0 并安装了数据库表。另外一份是用于压测使用的 [ApacheBench](https://httpd.apache.org/docs/2.4/programs/ab.html) +- 连接 MySQL 的工具,推荐使用开源免费的 [Sequel Ace](https://apps.apple.com/us/app/sequel-ace/id1518036000?ls=1) + +## 三、工程说明 + +在 xfg-dev-tech-connection-pool 工程中提供了不同连接池的配置和一些非常常用的 SQL 操作,以及提供了对应的接口进行压测使用; + +| 序号 | 接口 | 说明 | +| ---- | ------------------------------------------------------------ | -------------------------------------------------- | +| 1 | http://127.0.0.1:8091/api/mysql/cacheData | 用于缓存数据的接口,拿缓存好的数据压测更新、查询 | +| 2 | http://127.0.0.1:8091/api/mysql/insert | 插入数据接口 | +| 3 | http://127.0.0.1:8091/api/mysql/updateOrderStatusByUserId | 使用索引字段 userId 更新订单状态 | +| 4 | http://127.0.0.1:8091/api/mysql/updateOrderStatusByUserMobile | 使用无索引字段 userMobile 更新订单状态 | +| 5 | http://127.0.0.1:8091/api/mysql/updateOrderStatusByOrderId | 使用索引字段 orderId 更新订单状态 | +| 6 | http://127.0.0.1:8091/api/mysql/selectByUserId | 使用索引字段 userId 查询订单 | +| 7 | http://127.0.0.1:8091/api/mysql/selectByUserMobile | 使用无索引字段 userMobile 查询订单,测试中添加索引 | +| 8 | http://127.0.0.1:8091/api/mysql/selectByOrderId | 使用有索引字段 orderId 查询订单 | +| 9 | http://127.0.0.1:8091/api/mysql/selectByOrderIdAndUserId | 区分度高的索引字段在前,区分度低的索引字段在后 | +| 10 | http://127.0.0.1:8091/api/mysql/selectByUserIdAndOrderId | 区分度低的索引字段在前,区分度高的索引字段在后 | + +- 具体代码实现可以直接对照工程来看,以及按需添加SQL语句进行性能压测验证。 + +## 四、库表语句 + +SQL:`xfg-dev-tech-connection-pool/docs/sql/road_map_8.0.sql` + +
+ +
+ +- 这是本节所需要测试的一个订单表和测试前所建的索引字段。以及初始化了100万数据,占用350M空间。 +- 接下来,我们就可以做测试验证了。 + +## 五、压测指令 + +ApacheBench 官网教程:[https://httpd.apache.org/docs/2.4/programs/ab.html](https://httpd.apache.org/docs/2.4/programs/ab.html) + +
+ +
+ +常用的如:`ab -c 20 -n 1000 http://127.0.0.1:8091/hi` - 20个并发1000次 + +## 六、压测验证 + +首先在测试前,正式测试前,你大概需要花费几十分钟来初始化100万数据。执行脚本;`ab -c 20 -n 1000000 http://127.0.0.1:8091/api/mysql/insert` - 如果你在工程中配置了 no-pool 大概要花费几个小时才能跑完,这就使用和不使用连接池的差距。 + +### 1. 连接池比对 + +**条件**; + +1. 插入1万条数据 +2. 连接池配置 initialPoolSize=5、minPoolSize=5、maxPoolSize=20 +2. 此时数据库已经有100万数据,分别用几个链接方式插入数据。hikari 放到最后,它是 SpringBoot 的默认连接池。 +2. 脚本;`ab -c 20 -n 10000 http://127.0.0.1:8091/api/mysql/insert` + +| | no-pool | c3p0 | dbcp | druid | hikari | +| :--: | :------------: | :------------: | :------------: | :------------: | :------------: | +| 耗时 | 88.990 seconds | 24.228 seconds | 33.656 seconds | 25.971 seconds | 25.002 seconds | +| 50% | 155ms | 39ms | 60ms | 45ms | 43ms | +| 80% | 223ms | 61ms | 86ms | 64ms | 64ms | +| 90% | 291ms | 75ms | 103ms | 75ms | 76ms | + +- 通过对比可以发现,如果不使用连接池,会比使用连接池,要占用更多的时间连接数据库使用数据库。 +- c3p0、hikari 的性能还是非常不错的,dbcp 相对是弱一些的。所以这可以给你在使用连接池时有一个参考。也可以结合你的机器再次进行压测验证。 + +### 2. 更新对比 + +**条件**; + +1. 使用接口,向内存加入600条数据。每个测试方式,分别消耗200条。`ab -c 10 -n 600 http://127.0.0.1:8091/api/mysql/cacheData` +2. 之后使用无索引字段、有索引但区分度不高的字段以及使用有索引区分度非常好的字段来更新。 +3. 脚本; + 1. `ab -c 20 -n 200 http://127.0.0.1:8091/api/mysql/updateOrderStatusByUserMobile` + 2. `ab -c 20 -n 200 http://127.0.0.1:8091/api/mysql/updateOrderStatusByUserId` + 3. `ab -c 20 -n 200 http://127.0.0.1:8091/api/mysql/updateOrderStatusByOrderId` + + +| | 无索引 | 有索引-区分度不高 | 有索引-区分度很高 | +| :--: | :-----: | :---------------: | :---------------: | +| 耗时 | 24小时+ | 24小时+ | 0.432 seconds | +| 50% | 24小时+ | 24小时+ | 35ms | +| 80% | 24小时+ | 24小时+ | 48ms | +| 90% | 24小时+ | 24小时+ | 67ms | + +- 无索引的时候;会把整个表的这个记录,全部锁上。那么越执行越慢,最后拖垮数据库。甚至可能1天都执行不完。 +- 有索引-区分度不高;几乎是一样的,如果你批量的对一个用户的所有数据都更新,也会锁很多记录。 +- 有索引-区分度很高;只要你锁的是自己的一条记录,就与别人没有影响。效率也会非常高。 + +### 3. 查询对比 + +**条件**; + +1. 查询100万加的数据库表记录,每次缓存记录5000条数据id;`ab -c 10 -n 5000 http://127.0.0.1:8091/api/mysql/cacheData` +2. userId 有索引、orderId 有索引、userMobie 无索引。 +2. 每次查询的时候,都要关闭服务重启,避免有缓存干扰结果。 +3. 脚本: + 1. `ab -c 20 -n 5000 http://127.0.0.1:8091/api/mysql/selectByUserMobile` + 2. `ab -c 20 -n 5000 http://127.0.0.1:8091/api/mysql/selectByUserId` + 3. `ab -c 20 -n 5000 http://127.0.0.1:8091/api/mysql/selectByOrderId` + 4. `ab -c 20 -n 5000 http://127.0.0.1:8091/api/mysql/selectByOrderIdAndUserId` + 5. `ab -c 20 -n 5000 http://127.0.0.1:8091/api/mysql/selectByUserIdAndOrderId` + +| | 无索引 | 有索引-区分度不高 | 有索引区分度高 | 高在前 | 低在前 | +| ---- | :----: | :---------------: | :------------: | :-----------: | :-----------: | +| 耗时 | 6小时+ | 8.343 seconds | 2.051 seconds | 2.168 seconds | 3.279 seconds | +| 50% | 7s | 13ms | 7ms | 7ms | 11ms | +| 80% | 9s | 20ms | 10ms | 11ms | 17ms | +| 90% | 15s | 26ms | 13ms | 13ms | 22ms | + +- 无索引,还是查询字段的。非常危险。 +- 不要在一些区分度不高的字段建索引。当然本案例中,userId 最多也就1000来个用户所产生的1百万数据,这样的情况更适合分库分表。 +- 区分度很高的字段,查询效率会非常好。 +- 把高区分度的索引字段放在前面,更有利于查询。—— 注意不要测试完上一个,直接测试下一个。有缓存的情况下,会影响对比结果。 + +--- + +这就是整个数据库表的压测过程了。如果你有使用的诉求,需要做技术调研,那么一定要做一些这样的压测处理。这样有真实数据才好讲道理。另外需要面试的伙伴,你不是总没有数据吗,来吧,按照这个压一压! diff --git a/docs/md/road-map/db-router.md b/docs/md/road-map/db-router.md new file mode 100644 index 000000000..5d38a5ede --- /dev/null +++ b/docs/md/road-map/db-router.md @@ -0,0 +1,288 @@ +--- +title: db-router +lock: need +--- + +# 数据库分库分表路由组件 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,如何实现一个分库分表的路由组件。这里包括;AOP、路由散列算法、动态数据源切换等相关知识。 + +本文涉及的工程: + +- xfg-dev-tech-db-router:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-db-router](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-db-router) + +## 一、需求分析 + +`如果要做一个数据库路由,都需要做什么技术点?` + +首先我们要知道为什么要用分库分表,其实就是由于业务体量较大,数据增长较快,所以需要把用户数据拆分到不同的库表中去,减轻数据库压力。 + +分库分表操作主要有垂直拆分和水平拆分: +- 垂直拆分:指按照业务将表进行分类,分布到不同的数据库上,这样也就将数据的压力分担到不同的库上面。最终一个数据库由很多表的构成,每个表对应着不同的业务,也就是专库专用。 +- 水平拆分:如果垂直拆分后遇到单机瓶颈,可以使用水平拆分。相对于垂直拆分的区别是:垂直拆分是把不同的表拆到不同的数据库中,而水平拆分是把同一个表拆到不同的数据库中。如:user_001、user_002 + +而本章节我们要实现的也是水平拆分的路由设计,如图 1-1 + +![图 1-1](https://bugstack.cn/assets/images/middleware/blog-4-1.png) + +那么,这样的一个数据库路由设计要包括哪些技术知识点呢? + +- 是关于 AOP 切面拦截的使用,这是因为需要给使用数据库路由的方法做上标记,便于处理分库分表逻辑。 +- 数据源的切换操作,既然有分库那么就会涉及在多个数据源间进行链接切换,以便把数据分配给不同的数据库。 +- 数据库表寻址操作,一条数据分配到哪个数据库,哪张表,都需要进行索引计算。在方法调用的过程中最终通过 ThreadLocal 记录。 +- 为了能让数据均匀的分配到不同的库表中去,还需要考虑如何进行数据散列的操作,不能分库分表后,让数据都集中在某个库的某个表,这样就失去了分库分表的意义。 + +综上,可以看到在数据库和表的数据结构下完成数据存放,我需要用到的技术包括:`AOP`、`数据源切换`、`散列算法`、`哈希寻址`、`ThreadLoca`l以及`SpringBoot的Starter开发方式`等技术。而像`哈希散列`、`寻址`、`数据存放`,其实这样的技术与 HashMap 有太多相似之处,**那么学完源码造火箭的机会来了** 如果你有过深入分析和学习过 HashMap 源码、Spring 源码、中间件开发,那么在设计这样的数据库路由组件时一定会有很多思路的出来。*接下来我们一起尝试下从源码学习到造火箭!* + +## 二、技术调研 + +在 JDK 源码中,包含的数据结构设计有:数组、链表、队列、栈、红黑树,具体的实现有 ArrayList、LinkedList、Queue、Stack,而这些在数据存放都是顺序存储,并没有用到哈希索引的方式进行处理。而 HashMap、ThreadLocal,两个功能则用了哈希索引、散列算法以及在数据膨胀时候的拉链寻址和开放寻址,所以我们要分析和借鉴的也会集中在这两个功能上。 + +### 1. ThreadLocal + +![](https://bugstack.cn/assets/images/middleware/blog-4-2.png) + +```java +@Test +public void test_idx() { + int hashCode = 0; + for (int i = 0; i < 16; i++) { + hashCode = i * 0x61c88647 + 0x61c88647; + int idx = hashCode & 15; + System.out.println("斐波那契散列:" + idx + " 普通散列:" + (String.valueOf(i).hashCode() & 15)); + } +} + +斐波那契散列:7 普通散列:0 +斐波那契散列:14 普通散列:1 +斐波那契散列:5 普通散列:2 +斐波那契散列:12 普通散列:3 +斐波那契散列:3 普通散列:4 +斐波那契散列:10 普通散列:5 +斐波那契散列:1 普通散列:6 +斐波那契散列:8 普通散列:7 +斐波那契散列:15 普通散列:8 +斐波那契散列:6 普通散列:9 +斐波那契散列:13 普通散列:15 +斐波那契散列:4 普通散列:0 +斐波那契散列:11 普通散列:1 +斐波那契散列:2 普通散列:2 +斐波那契散列:9 普通散列:3 +斐波那契散列:0 普通散列:4 +``` + +- **数据结构**:散列表的数组结构 +- **散列算法**:斐波那契(Fibonacci)散列法 +- **寻址方式**:Fibonacci 散列法可以让数据更加分散,在发生数据碰撞时进行开放寻址,从碰撞节点向后寻找位置进行存放元素。公式:`f(k) = ((k * 2654435769) >> X) << Y对于常见的32位整数而言,也就是 f(k) = (k * 2654435769) >> 28 `,黄金分割点:`(√5 - 1) / 2 = 0.6180339887` `1.618:1 == 1:0.618` +- **学到什么**:可以参考寻址方式和散列算法,但这种数据结构与要设计实现作用到数据库上的结构相差较大,不过 ThreadLocal 可以用于存放和传递数据索引信息。 + +### 2. HashMap + +![](https://bugstack.cn/assets/images/middleware/blog-4-3.png) + +```java +public static int disturbHashIdx(String key, int size) { + return (size - 1) & (key.hashCode() ^ (key.hashCode() >>> 16)); +} +``` + +- **数据结构**:哈希桶数组 + 链表 + 红黑树 +- **散列算法**:扰动函数、哈希索引,可以让数据更加散列的分布 +- **寻址方式**:通过拉链寻址的方式解决数据碰撞,数据存放时会进行索引地址,遇到碰撞产生数据链表,在一定容量超过8个元素进行扩容或者树化。 +- **学到什么**:可以把散列算法、寻址方式都运用到数据库路由的设计实现中,还有整个数组+链表的方式其实库+表的方式也有类似之处。 + +## 三、设计实现 + +### 1. 定义路由注解 + +**定义** + +```java +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface DBRouter { + + String key() default ""; + +} +``` + +**使用** + +```java +@Mapper +public interface IUserDao { + + @DBRouter(key = "userId") + User queryUserInfoByUserId(User req); + + @DBRouter(key = "userId") + void insertUser(User req); + +} +``` + +- 首先我们需要自定义一个注解,用于放置在需要被数据库路由的方法上。 +- 它的使用方式是通过方法配置注解,就可以被我们指定的 AOP 切面进行拦截,拦截后进行相应的数据库路由计算和判断,并切换到相应的操作数据源上。 + +### 2. 解析路由配置 + +![](https://bugstack.cn/assets/images/middleware/blog-4-4.png) + +- 以上就是我们实现完数据库路由组件后的一个数据源配置,在分库分表下的数据源使用中,都需要支持多数据源的信息配置,这样才能满足不同需求的扩展。 +- 对于这种自定义较大的信息配置,就需要使用到 `org.springframework.context.EnvironmentAware` 接口,来获取配置文件并提取需要的配置信息。 + +**数据源配置提取** + +```java +@Override +public void setEnvironment(Environment environment) { + String prefix = "router.jdbc.datasource."; + + dbCount = Integer.valueOf(environment.getProperty(prefix + "dbCount")); + tbCount = Integer.valueOf(environment.getProperty(prefix + "tbCount")); + + String dataSources = environment.getProperty(prefix + "list"); + for (String dbInfo : dataSources.split(",")) { + Map dataSourceProps = PropertyUtil.handle(environment, prefix + dbInfo, Map.class); + dataSourceMap.put(dbInfo, dataSourceProps); + } +} +``` + +- prefix,是数据源配置的开头信息,你可以自定义需要的开头内容。 +- dbCount、tbCount、dataSources、dataSourceProps,都是对配置信息的提取,并存放到 dataSourceMap 中便于后续使用。 + +### 3. 数据源切换 + +在结合 SpringBoot 开发的 Starter 中,需要提供一个 DataSource 的实例化对象,那么这个对象我们就放在 DataSourceAutoConfig 来实现,并且这里提供的数据源是可以动态变换的,也就是支持动态切换数据源。 + +**创建数据源** + +```java +@Bean +public DataSource dataSource() { + // 创建数据源 + Map targetDataSources = new HashMap<>(); + for (String dbInfo : dataSourceMap.keySet()) { + Map objMap = dataSourceMap.get(dbInfo); + targetDataSources.put(dbInfo, new DriverManagerDataSource(objMap.get("url").toString(), objMap.get("username").toString(), objMap.get("password").toString())); + } + + // 设置数据源 + DynamicDataSource dynamicDataSource = new DynamicDataSource(); + dynamicDataSource.setTargetDataSources(targetDataSources); + return dynamicDataSource; +} +``` + +- 这里是一个简化的创建案例,把基于从配置信息中读取到的数据源信息,进行实例化创建。 +- 数据源创建完成后存放到 `DynamicDataSource` 中,它是一个继承了 AbstractRoutingDataSource 的实现类,这个类里可以存放和读取相应的具体调用的数据源信息。 + +### 4. 切面拦截 + +在 AOP 的切面拦截中需要完成;数据库路由计算、扰动函数加强散列、计算库表索引、设置到 ThreadLocal 传递数据源,整体案例代码如下: + +```java +@Around("aopPoint() && @annotation(dbRouter)") +public Object doRouter(ProceedingJoinPoint jp, DBRouter dbRouter) throws Throwable { + String dbKey = dbRouter.key(); + if (StringUtils.isBlank(dbKey)) throw new RuntimeException("annotation DBRouter key is null!"); + + // 计算路由 + String dbKeyAttr = getAttrValue(dbKey, jp.getArgs()); + int size = dbRouterConfig.getDbCount() * dbRouterConfig.getTbCount(); + + // 扰动函数 + int idx = (size - 1) & (dbKeyAttr.hashCode() ^ (dbKeyAttr.hashCode() >>> 16)); + + // 库表索引 + int dbIdx = idx / dbRouterConfig.getTbCount() + 1; + int tbIdx = idx - dbRouterConfig.getTbCount() * (dbIdx - 1); + + // 设置到 ThreadLocal + DBContextHolder.setDBKey(String.format("%02d", dbIdx)); + DBContextHolder.setTBKey(String.format("%02d", tbIdx)); + logger.info("数据库路由 method:{} dbIdx:{} tbIdx:{}", getMethod(jp).getName(), dbIdx, tbIdx); + + // 返回结果 + try { + return jp.proceed(); + } finally { + DBContextHolder.clearDBKey(); + DBContextHolder.clearTBKey(); + } +} +``` + +- 简化的核心逻辑实现代码如上,首先我们提取了库表乘积的数量,把它当成 HashMap 一样的长度进行使用。 +- 接下来使用和 HashMap 一样的扰动函数逻辑,让数据分散的更加散列。 +- 当计算完总长度上的一个索引位置后,还需要把这个位置折算到库表中,看看总体长度的索引因为落到哪个库哪个表。 +- 最后是把这个计算的索引信息存放到 ThreadLocal 中,用于传递在方法调用过程中可以提取到索引信息。 + +## 四. 测试验证 + +### 1. 库表创建 + +```java +create database `bugstack_01`; +DROP TABLE user_01; +CREATE TABLE user_01 ( id bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID', userId varchar(9) COMMENT '用户ID', userNickName varchar(32) COMMENT '用户昵称', userHead varchar(16) COMMENT '用户头像', userPassword varchar(64) COMMENT '用户密码', createTime datetime COMMENT '创建时间', updateTime datetime COMMENT '更新时间', PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +DROP TABLE user_02; +CREATE TABLE user_02 ( id bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID', userId varchar(9) COMMENT '用户ID', userNickName varchar(32) COMMENT '用户昵称', userHead varchar(16) COMMENT '用户头像', userPassword varchar(64) COMMENT '用户密码', createTime datetime COMMENT '创建时间', updateTime datetime COMMENT '更新时间', PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +DROP TABLE user_03; +CREATE TABLE user_03 ( id bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID', userId varchar(9) COMMENT '用户ID', userNickName varchar(32) COMMENT '用户昵称', userHead varchar(16) COMMENT '用户头像', userPassword varchar(64) COMMENT '用户密码', createTime datetime COMMENT '创建时间', updateTime datetime COMMENT '更新时间', PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +DROP TABLE user_04; +CREATE TABLE user_04 ( id bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID', userId varchar(9) COMMENT '用户ID', userNickName varchar(32) COMMENT '用户昵称', userHead varchar(16) COMMENT '用户头像', userPassword varchar(64) COMMENT '用户密码', createTime datetime COMMENT '创建时间', updateTime datetime COMMENT '更新时间', PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; +``` + +- 创建相同表结构的多个库存信息,bugstack_01、bugstack_02 + +### 2. 语句配置 + +```java + + + + insert into user_${tbIdx} (id, userId, userNickName, userHead, userPassword,createTime, updateTime) + values (#{id},#{userId},#{userNickName},#{userHead},#{userPassword},now(),now()) + +``` + +- 在 MyBatis 的语句使用上,唯一变化的需要在表名后面添加一个占位符,`${tbIdx}` 用于写入当前的表ID。 + +### 3. 注解配置 + +```java +@DBRouter(key = "userId") +User queryUserInfoByUserId(User req); + +@DBRouter(key = "userId") +void insertUser(User req); +``` + +- 在需要使用分库分表的方法上添加注解,添加注解后这个方法就会被 AOP 切面管理。 + +### 4. 单元测试 + +```java +22:38:20.067 INFO 19900 --- [ main] c.b.m.db.router.DBRouterJoinPoint : 数据库路由 method:queryUserInfoByUserId dbIdx:2 tbIdx:3 +22:38:20.594 INFO 19900 --- [ main] cn.bugstack.middleware.test.ApiTest : 测试结果:{"createTime":1615908803000,"id":2,"userHead":"01_50","userId":"980765512","userNickName":"小傅哥","userPassword":"123456"} +22:38:20.620 INFO 19900 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'1 + +``` + +- 以上就是我们使用自己的数据库路由组件执行时的一个日志信息,可以看到这里包含了路由操作,在2库3表:`数据库路由 method:queryUserInfoByUserId dbIdx:2 tbIdx:3` diff --git a/docs/md/road-map/ddd-archetype-maven.md b/docs/md/road-map/ddd-archetype-maven.md new file mode 100644 index 000000000..38913d073 --- /dev/null +++ b/docs/md/road-map/ddd-archetype-maven.md @@ -0,0 +1,361 @@ +--- +title: DDD 脚手架【Maven 在线版】 +lock: need +--- + +# 我把DDD脚手架,发布到了Maven仓库,大家都能用了! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主,小傅哥。 + +这篇文章将帮助粉丝伙伴们更高效地利用小傅哥构建的`DDD(领域驱动设计)脚手架`,搭建工程项目,增强使用的便捷性。让👬🏻兄弟们直接在 IntelliJ IDEA 配置个在线的链接,就能直接用上这款脚手架!—— **你就说猛不猛!🤨** + +
+ +
+ +- 配置地址 https://gaga.plus - `备用地址:[https://maven.xiaofuge.cn](https://maven.xiaofuge.cn)` +- ddd-scaffold-lite 提供了默认版本和 v2 版本。v2 做了少量调整。将 repository、port 统一归属到 adapter 下管理。 + +--- + +**那小傅哥搞的这个在线版脚手架是怎么做到的呢?** 🤔 想学吗,想学我教你呀? + +在23年的时候小傅哥发布了 DDD 两款脚手架,一个轻量版 lite ,一个标准版 std。通过这两款脚手架,小伙伴们在学习小傅哥写的技术教程和实战项目的时候,可以把脚手架代码下载到本地,通过 `maven install` 在本地构建出脚手架再配置到上图中的地址后使用。 + +我的理想情况是大家都能很顺利的构建、配置,美滋滋的使用,但实际情况却是,编码的路上,错误是层出不穷! + +所以,我动手了。我要为你们提供更Easy的方式!但也差点难住我。接下来我就给伙伴们分享下,这个东西是如何搞的。 + +>文末有8个实战项目,可以加入学习。🉐 项目:[https://gaga.plus](https://gaga.plus) + +## 一、思考的开始 🤔 + +我发布过自己的 IntelliJ IDEA vo2dto 到插件市场,也推送过 openai sdk、chatglm sdk、db-router 等组件到 Maven 中央仓库。 + +那我就寻思了,这脚手架也是个 Jar 包,应该也能发布到 Maven 中央仓库呀。要不Maven Central 自己那个脚手架是怎么发上去的?在编程开发这个事上,我一直秉承着,只要我能看见的,就都应该能复刻出来。 + +但你知道这里有一点,发布到 Maven 仓库的是 Jar 包,那我配置脚手架的地址哪里来呢(地址里是脚手架的定义)?我应该也没办法把脚手架的定义推送到人家 maven.apache.org 下面去。毕竟那是人家的老巢。如果能推送,那我现在打开的 Maven Central 应该有一堆脚手架,而不只是 Maven Central 所发布自己的。 + +所以,我灵机一动😁,打开了 [https://repo.maven.apache.org/maven2](https://repo.maven.apache.org/maven2) + +
+ +
+ +这个 `archetype-catalog.xml` 就是在构建 Maven 项目的脚手架时所产生的脚手架定义文件,配置到 IntelliJ IDEA 中,才会展示出脚手架列表,并可以选择使用。 + +那我知道了,虽然我不能推送到 maven 的老巢里去。但我可以自己提供个地址呀,把 `archetype-catalog.xml` 推送进去。经过最开始的验证,确实可以,完全没问题!Easy! + +但接下来的问题变得麻烦了,Jar 怎么推送到 Maven 仓库呢。**傅哥,你不是推过吗,你咋不行了?** 死鬼!tnnd,推送 Jar 到 Maven 仓库从24年2月改版了!!!全编程界的鸡鸭鹅狗🦆,都没有人发过教程! + +
+ +
+ +因为这个推送 Jar 在最开始的两天时间里,我一度怀疑是自己超限额了,不能创建了。也曾想过要不就只推送到 阿里云的私有仓库吧,大家配置个 阿里云 Maven 镜像地址,也能用。但这样的情况始终觉得不爽🙅🙅🏻‍,所以我走上了研究新版推送 Jar 的方式,直至最终成功啦💐!!!舒服! + +>接下来我就给小伙伴们分享下,这东西是怎么推送上去的。 + +## 二、觉得我也行 🤨 + +### 1. 卡卡两脚 + +做一个新的技术东西之前呢,先要检索下资料,看看有啥坑不。这个阶段也叫技术调研。卡卡一定能搜 `发布Jar到maven仓库`,全是以前的旧版本方案,没有一个能用的! + +
+ +
+ +还有一个我写的,卡卡上去给两脚!不行了,确实没啥关于新版上传 Jar 到 Maven 仓库的资料,自己去趟坑吧。 + +### 2. 冒烟测试 + +完成目标最快的方式是什么?兄弟们!当然是结果驱动,先干一脚,看它嚎不嚎。遇到问题再解决问题。所以我准备先无脑上传一波,看看都给我什么信息。 + +
+ +
+ +- 地址:[https://central.sonatype.com/publishing](https://central.sonatype.com/publishing) +- 看着这个上传组件的小图,还挺简单的。不过在这里它是一句有用的话都不写。那我就写个名字和上传个Jar进去。*心里笑嘻嘻,难度,它会根据我的 Jar 自动分析 POM?* + +但发现我想多了,第一次上传全是报错! + +
+ +
+ +- 什么 pom 文件没传。所以我又机智的把 Jar 和 POM 打包了 zip 上传。 +- 紧接着报错 `Invalid 'md5' checksum for file: scaffold-lite-1.0-sources.jar`,缺少 md5、sha1 验证。好在我以前发布过 maven 仓库,知道这些配置。 +- 基本上知道要怎么传了,接下来,细看文档。遇到什么类错误,优先看什么内容。 + +### 3. 操作步骤 + +在 [https://central.sonatype.com/publishing](https://central.sonatype.com/publishing) 首页有一个 Help 帮助文档,[https://central.sonatype.org/register/central-portal/#producers](https://central.sonatype.org/register/central-portal/#producers) 这里有非常详细的操作说明。接下来我讲一些核心的步骤,如果操作有失败,可以参考官网资料。 + +
+ +
+ +开始前,登录注册 [https://central.sonatype.com](https://central.sonatype.com) - 可以选择 github 登录。 + +#### 3.1 配置 NameSpace + +如果选择 github 登录,你会有一个默认配置的 NameSpace(io.github.fuzhengwei),这个东西的作用就是要和本地工程名 groupId 保持一致的。如工程是 cn.bugstack、plus.gaga、com.liergou,那么你在的 NameSpace 就需要配置一个这样的调过来的域名。 + +
+ +
+ +
+ +
+ +- 如图配置完添加验证即可,最后验证成功就可以使用了。 + +#### 3.2 上传要求 + +文档:[https://central.sonatype.org/publish/publish-portal-upload/](https://central.sonatype.org/publish/publish-portal-upload/) + +
+ +
+ +- 如文档上传要求,你需要把jar、pom、doc、sources 全部打包到 zip 包,同时每个文件的 asc、md5、sha1 也需要打包进来。 +- 这些文件也都是在旧版上传 maven 中央仓库的时候,所需提供的内容。 + +#### 3.3 项目配置 + +**源码**:[https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-lite/-/tree/master/scaffold-lite](https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-lite/-/tree/master/scaffold-lite) + +```pom + + + 4.0.0 + + io.github.fuzhengwei + ddd-scaffold-lite + 1.0 + maven-archetype + + ddd-scaffold-lite + + + 1.8 + 3.2.0 + 3.2.1 + 1.6 + 1.10 + + + + + + org.apache.maven.archetype + archetype-packaging + 3.2.0 + + + + + + net.nicoulaj.maven.plugins + checksum-maven-plugin + ${maven-checksum-plugin.version} + + + create-checksums + + artifacts + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + UTF-8 + true + UTF-8 + UTF-8 + + + + attach-javadocs + + jar + + + -Xdoclint:none + + /Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/bin/javadoc + + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + true + false + release + deploy + + + + + + + + release + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.3.1 + + + attach-javadocs + + jar + + + + + + + + + + ddd scaffold lite by xiaofuge + + https://spring.io/projects/spring-boot/xfg-frame-archetype + + + + fuzhengwei + 184172133@qq.com + fuzhengwei + https://github.com/fuzhengwei + + + + +``` + +- 注意 groupId、artifactId 名字,如果你有发布诉求,需要和你自己的一致。 +- maven-javadoc-plugin:生成 doc 文档。这里要注意,因为我们脚手架不是代码文件,没有doc的,所以要在工程中加一个任意类名文件。工程中小傅哥加了个 Api 类。 +- maven-source-plugin:生成 source 文件。 +- maven-gpg-plugin:是签名的加密文件,需要本地安装过 gpg 包。注意;`checksum-maven-plugin` 需要在 `maven-gpg-plugin` 执行,这样就不用做下面的手动 md5 逻辑了。 +- checksum-maven-plugin:生成 md5、sha1 文件,但这里不会对 pom 生成此文件,还需要单独命令处理。 + +```shell +md5 ddd-scaffold-lite-1.0.pom > ddd-scaffold-lite-1.0.pom.md5 +shasum ddd-scaffold-lite-1.0.pom > ddd-scaffold-lite-1.0.pom.sha1 +``` + +- 检查生成后的文件,去掉不需要的内容。 + +#### 3.4 构建项目 + +**第1次构建** + +
+ +
+ +**第2次构建** + +
+ +
+ +**执行脚本** + +
+ +
+ +#### 3.5 上传 archetype-catalog.xml + +把 archetype-catalog.xml 文件,上传到域名可访问云服务器的根目录中。 + +
+ +
+ +#### 3.6 上传打包文件到 maven 仓库 + +
+ +
+ +- 你需要按照你的工程结构也是 namespace 创建出文件夹结构,并把工程 target 打包的文件全部复制进来。 +- 最后把 io 这个文件夹,打包一个 zip 包。就可以了。 + +#### 3.7 上传 maven 仓库 + +
+ +
+ +#### 3.8 成功啦!💐 + +
+ +
+ +好啦,这就是整个脚手架的操作过程!现在你可以体验使用了。 + +## 三、开始卷项目 🚴🏻 + +小傅哥是一个大厂的架构师,经常会带着伙伴们,卷这些实际场景中非常有必要的技术。也会带着伙伴实战项目,这些项目也都是来自于互联网大厂中真实的业务场景,所有学习这样的项目无论是实习、校招、社招,都是有非常强的竞争力。别人还在玩玩具,而你已经涨能力! + +>🧧这样的项目学习在小傅哥星球「码农会锁」有8个,每个都是从0到1开发并提供简历模板和面试题,并且还在继续开发,后续还将有更多!价格嘎嘎实惠,早点加入,早点提升自己。项目地址:[https://gaga.plus](https://gaga.plus) + +在星球学习中,你可以把项目组合使用。用业务的+技术的+组件的,会非常有竞争力。 + +
+ +
diff --git a/docs/md/road-map/ddd-archetype.md b/docs/md/road-map/ddd-archetype.md new file mode 100644 index 000000000..9cfae10c8 --- /dev/null +++ b/docs/md/road-map/ddd-archetype.md @@ -0,0 +1,189 @@ +--- +title: DDD 脚手架【Maven 本地版】 +lock: need +--- + +# DDD 工程脚手架 + 一键安装分布式技术栈环境! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +写了那么多案例工程,开发了那么多技术项目。那小傅哥做的这些案例和项目是每一个都要手动创建一遍吗?🤔 如果不是一个个都手动创建,那么有什么提效工具吗? + +
+ +
+ +不用,根本不用。因为小傅哥有一套**神器**! + +对于 DDD 项目的多模块化工程搭建,其实真的是一个挺耗时的工作,尤其是分布式工程的搭建更是耗时。不过工程模块的创建还不算太耗时,主要耗时在各个分布式组件的整合使用上,包括:MySQL、Redis、RocketMQ、Dubbo、shardingjdbc、XXL-JOB 等一堆技术框架。如果你做过这样的事情,一定会被他们的使用和各类版本的配合,折腾过服服帖帖。 + +一次工程和环境搭建,没有个6~8小时都搞不完!但今天我想你动动手指就搞定这些! + +
+ +
+ +那么,接下来小傅哥就介绍下这套带有配套环境安装的工程脚手架,让小伙伴可以熟悉使用,快速搭建自己的学习工程。 + +>文末有相关工程脚手架的获取地址,还有对应工程脚手架的学习项目。 + +## 一、效果展示 + +这是一套完整的工程级框架搭建标准,提供工程的脚手架搭建以及对应的完整环境初始化。可以让开发项目的伙伴,快速🔜完成基本工作,减少6~8小时的折腾。先来展示下整体教授的内容。 + +### 1. 工程框架 + +
+ +
+ +### 2. 环境展示 + +
+ +
+ +### 3. Redis 管理 + +
+ +
+ +### 4. MySql 管理 + +
+ +
+ +### 5. MQ 管理 + +
+ +
+ +### 6. JOB 管理 + +
+ +
+ +## 二、框架介绍 + +小傅哥这里提供了2套工程脚手架,一套轻量版的无任何分布式技术栈的使用,另外一套是标准版的全系使用分布式技术栈。因为很多小型项目并不需要依赖太多的分布式技术栈,而且轻量化的设计开发更能有效的提高开发效率。所以,读者可以按需选择你需要的脚手架进行学习、验证和生产。 + +### 1. 轻量版 + +
+ +
+ +轻量版 DDD 框架,主要以提供 HTTP 服务为主。开发效率高,适合中小业务场景。 +- 缓存,使用 Guava。如果有特定场景的情况下,可以补充 Redis 使用。 +- 任务,使用 Quartz。 +- 消息,使用 Spring/Guava 替代。 + +### 2. 标准版 + +
+ +
+ +标准版 DDD 架构,以解决中大型场景业务开发为目的,综合使用分布式技术栈进行项目构建。 +- Dubbo 提供 RPC 接口,Nacos 作为注册中心使用。 +- Redis 提供缓存、加锁、数据处理服务。 +- SharedingJdbc 提供分库分表服务。 +- XXL-JOB 提供分布式任务调度服务。 +- RocketMQ 提供异步消息服务。 + +## 三、使用说明 + +这套脚手架使用了 `maven-archetype-plugin` 使用命令(archetype:create-from-project)进行工程的脚手架创建,创建后再进行一些内容的修改,最终完成脚手架的模板。 + +读者在使用这套脚手架的时候,可以在本地下载工程代码后,进入工程下的 `scaffold-lite/std` 模块执行 README.md 中 `mvn clean install` 脚本进行安装。安装后即可在使用 IntelliJ IDEA 创建工程时候,选择 Maven 创建,添加本地仓库地址使用。—— 下面👇🏻会介绍具体操作步骤。 + +### 1. 工程结构 + +
+ +
+ +如图,就是两套 DDD 脚手架工程,每一套工程下,都有一个 scaffold 模块。这部分是对当下对应工程的脚手架模块。另外 docs 下有 dev-ops 文件夹,是环境安装包,直接执行 docker-compose.yml 即可一次安装完全部环境。 + +读者,在使用的时候,也可以先不看 scaffold 模块。因为除了这个模块外,其他的就是整个 DDD 工程,作为你最开始熟悉使用。熟悉后再进入“黑圈” README.md 中执行 mvc clean install 这样就能在本地 maven 仓库安装上脚手架了。 + +### 2. 框架安装 + +#### 2.1 安装 + +
+ +
+ +- 进入工程脚手架模块下的 README.md 点击执行 mvn clean install + +#### 2.2 配置 + +
+ +
+ +- 首先是点击创建工厂,之后进入 Maven Archetype 下。 +- 之后你需要自己配置好 maven 工程脚手架地址,有可能默认的路径地址,不生效。 + +#### 2.3 使用 + +
+ +
+ +- 进入 Maven Archetype 如图方式创建工程。 + +#### 2.4 效果 + +
+ +
+ +- 自动生成工程,之后你就可以通过 docs 文件夹下的 docker-compose.yml 安装环境并使用了。**注意本机已安装了 Docker** + +### 3. 环境安装 + +文件:`docs/dev-ops/docker-compose.yml` + +
+ +
+ +- Mac 电脑安装 Docker 后,可以直接点击绿色的小按钮,一步安装所有配置好的环境。 +- 云服务器,可以通过 `docker-compose -f docker-compose.yml up -d`进行安装。 +- 资源访问; + + - 配置中心nacos:http://127.0.0.1:8848/nacos - 【账号:nacos 密码:nacos】 + - 消息rocketmq:http://127.0.0.1:8080/#/ - 【账号:admin 密码:admin】 + - 任务调度xxl-job-admin:http://127.0.0.1:9090/xxl-job-admin/ - 【账号:admin 密码:123456】 + - 缓存redis:http://127.0.0.1:8081/ + - 数据库MySQL:http://127.0.0.1:8899/ - 登录数据库信息,在线直接管理数据库 + +## 四、项目学习 + +有了工程脚手架,最好再有一套对应的实战项目学习。这样加起来锻炼,看看各个项目的模块都是如何调度的,才能更好的学习这套工程。而小傅哥的 **星球:码农会锁** 就有这样的项目锻炼,包括:OpenAi 应用项目、Lottery 抽奖项目、Api网关项目、IM通信项目、SpringBoot Starter 组件项目等。 + +>这样一套项目,放在一些平台售卖,至少都是几百块。但小傅哥的星球,只需要100多,就可以获得几千元的学习项目! + +- 脚手架(轻量版):[https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-lite](https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-lite) +- 脚手架(标准版):[https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-std](https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-std) + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +在星球的实战项目中,都是以这样的企业级标准进行架构设计和落地,学习这样的项目就是学习公司的项目。—— 不做 CRUD 小项目,要做真的深度积累! + +
+ +
diff --git a/docs/md/road-map/ddd-dev-account.md b/docs/md/road-map/ddd-dev-account.md new file mode 100644 index 000000000..ee4b8a181 --- /dev/null +++ b/docs/md/road-map/ddd-dev-account.md @@ -0,0 +1,15 @@ +--- +title: DDD 架构场景 - 账户域 +lock: need +--- + +# 架构的本质之 DDD 架构 - 账户域 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +- 项目地址:[https://t.zsxq.com/14pbCR1IN](https://t.zsxq.com/14pbCR1IN) \ No newline at end of file diff --git a/docs/md/road-map/ddd-dev-pay.md b/docs/md/road-map/ddd-dev-pay.md new file mode 100644 index 000000000..51b7e27fa --- /dev/null +++ b/docs/md/road-map/ddd-dev-pay.md @@ -0,0 +1,15 @@ +--- +title: DDD 架构场景 - 交易域 +lock: need +--- + +# 架构的本质之 DDD 架构 - 交易域 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +- 项目地址:[https://t.zsxq.com/14jRaQCvC](https://t.zsxq.com/14jRaQCvC) \ No newline at end of file diff --git a/docs/md/road-map/ddd-guide-00.md b/docs/md/road-map/ddd-guide-00.md new file mode 100644 index 000000000..c09cbd1be --- /dev/null +++ b/docs/md/road-map/ddd-guide-00.md @@ -0,0 +1,77 @@ +--- +title: DDD 架构演进 +lock: need +--- + +# DDD 架构演进,从单层、三层,四层,工程分层演进过程! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +`定义接口`、`创建方法`、`调用展示`,其实编程写代码说到底也就这3步,人人都是程序员👨🏻‍💻。公司老板都觉得,它有个AI工具,它都能写代码。但现在的系统工程的分层结构,可不只是一层就写个 Controller,甚至是3层(Model-View-Controller),也有可能是4层(DDD)架构。这样的分层架构怎么理解呢? + +
+ +
+ +**刚入行,直接就开大吗?** + +在我最早接触编程的时候,还是9大内置对象,servlet、jsp、前端也是刚接触《锋利的JQuery》的时代。甚至很多流程代码都堆到了 jsp 页面,后端也就是连库做 CRUD 操作。多好的时代,学一点东西,就能上班赚钱了。现在要学的可就多了,仅专业技能部分,都能在写满简历 1/3 篇幅了! + +但没办法,人嘛,总是要向钱低头的,向前!毕竟,互联网公司都是飞速迭代发展的,所以,要想混个能在群里喊【收到、收到】的资格,也得加倍学习。 + +所以,小傅哥就给大家分享下,关于系统分层架构的演进过程,看看这东西是怎么从简简单单变得复复杂杂的。 + +## 一、单层架构 + +单层架构并不算一个规范的架构定义,只是在早期 MVC 三层架构(模型、试图、控制器)还没有那么普及,以及国内开发的项目程序还没有那么规范的时候,用于快速搭建简单网页功能的一种设计。 + +
+ +
+ +所有的分层结构的设计,都是以承接功能实现诉求为目的,这一阶段仅仅是完成网页的数据展示,也几乎没有用户交互。所以,很多时候是有多少个页面,就有多少个 Controller 提供接口,以及编写好调用数据库查询数据的操作。 + +## 二、三层架构 + +1978年,MVC模式最早由 [Trygve Reenskaug](https://zh.wikipedia.org/w/index.php?title=Trygve_Reenskaug&action=edit&redlink=1) 提出,**MVC模式**的目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。除此之外,此模式透过对复杂度的简化,使程序结构更加直观。 + +
+ +
+ +- 当 Controller 里承载业务功能逐步变多以后,我们开始思考怎么把这些东西分区存放。那么可以把各类对象归类到一个文件夹,之后把 Controller 里的复杂逻辑拆分为一个个原子的 service 服务,让 Controller 的接口负责调用。 +- 目前的分层架构,就可以适合开发一些复杂逻辑的场景诉求了,不过整体系统的承载量仍然是偏弱一些的。 + +## 三、三层架构(分布式) + +3层架构出现的很早,其实本身的设计没有考虑复杂的分布式场景。在从三层架构演变到可以支持分布式架构的时候,是没有一个统一的标准规范的,更多的各个公司里自己直接从旧工程里添加了新的 module 分层模块。 + +
+ +
+ +- 首先,一个分布式架构要,对接 rpc、提供 rpc、消费 mq、生产 mq,还要做定时任务(最终一致性补偿),单一个 rpc 的使用,就需要在程序定义出一个 export 层,要打包对外的服务接口,让使用方式引入使用。关于为什么需要这样的方式,可以在 [bugstack.cn](https://bugstack.cn/md/road-map/dubbo.html) 编程路书中,开发技术部分 dubbo 学习。 +- 之后,原本的 MVC 三层结构就不够用了。只要添加上,export、rpc 这样的分层,原本 service 直接处理数据库的操作,也要考虑拆分出来。可能这部分每个公司还不一样,其实即使一个公司,不同组,也是有很大的差异。这个也是目前互联网公司中的项目里的一个现状问题。 +- 思考,现在的工程模型结构其实很杂,装填了太多的东西进去了。除此之外,因为都是原子化编写,没有在做分区。一个系统里 service 都有上千个类,每个类里又有上千行代码。在坐的各位,应该都会在这些屎山中赚钱。 + +## 四、四层架构 + +DDD 是思想,六边形/菱形/整洁架构是分层,DDD 通过建模思想,指导我们以从用例图(use case diagram)出发,与产品、研发、测试一起在一个规范下,脑暴建模。在这个过程中,以结果为导向,分析出可能存在的领域服务。这些领域服务,如登录完成,下单完成,支付完成,收货完成,根据结果态,分析支撑这样的服务所需的对象(实体)、流程、规则等。这样我们可以更加清晰的构建一套系统。 + +而,六边形(常用的)架构,则是用于承接 DDD 领域驱动设计对系统分析后的编码实现。六边形可以说是专门为 DDD 做的配套架构,虽然也可以用 MVC 来编写,但这样是会失去面向对象设计和编码的优势,让代码逻辑混乱在一起。所以,这也是各个互联网公司开始往 DDD 架构切换的目的。这件事,我已经干了好几年了! + +
+ +
+ +- 首先,六边形架构,以 DDD 领域驱动实际为指引,为 domai 层,设计充血模型结构,如;登录、下单、支付,在每个模块下,包含完整的服务、模型、适配。适配的目的是这个领域里所需的数据,都通过适配的方式从外部调用进来,比如;数据库、缓存、接口等。这是一种 ACL 防腐设计,将来外部的接口变化了,也不会影响我们的领域服务,只要按照领域服务的适配标准提供即可。 +- 之后,围绕着领域 domain 开始,需要啥就让外部的基础设施层实现领域层的接口来提供。而接口要提供啥能力,就调用 case 编排 domain 层,或则简单的由 domain 层直接提供也可以。 +- 最后,也就是 trigger 触发器,我们把接口、任务、mq等都理解为一种触发,之后让 trigger 调用 case 层。case 或者 domain 的目的,就是分摊 trigger 以前 Controller 编写逻辑代码的压力。让 trigger 只是负责对外逻辑的封装,错误码,异常即可。 + +综上,就是关于架构分层的一个演进过程,现在还有 AI 的加入,AI 也会逐步成为整个工程架构中的一块。关于这部分的架构设计,与业务工程的结合,小傅哥也会在 bugstack.cn 博客中陆续更新。 + diff --git a/docs/md/road-map/ddd-guide-01.md b/docs/md/road-map/ddd-guide-01.md new file mode 100755 index 000000000..1b8868b28 --- /dev/null +++ b/docs/md/road-map/ddd-guide-01.md @@ -0,0 +1,186 @@ +--- +title: DDD 概念理论 +lock: need +--- + +# DDD 是什么?—— 你以前只会用 Service + 贫血模型! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +DDD 是什么,这应该是每个想使用 DDD 开发项目的研发伙伴,遇到的第一个疑问,只有搞清楚它到底是什么才好上手使用。而 DDD 既不是 MVC 一样的工程结构,也不能直接等同于微服务架构,更不是一种设计模式。 + +## 1. DDD 是什么 + +那 DDD 是什么呢?来自于维基百科的一段定义:["Domain-driven design (DDD) is a major software design approach. "](https://en.wikipedia.org/wiki/Domain-driven_design),DDD 是一种软件设计方法。也就是说 DDD 是指导我们做软件工程设计的一种手段,它提供了用切割工程模型的各类技巧,如;领域、界限上下文、实体、值对象、聚合、工厂、仓储等。通过 DDD 的指导思想,我们可以在前期投入更多的时间,更加合理的规划出可持续迭代的工程设计。 + +在 DDD 中有一套共识的工程两阶段设计手段,包括;战略设计、战术设计。 + +- **战略设计**,主要以应对复杂的业务需求,通过抽象、分治的过程,合理的拆分为独立的多个微服务,从而分而治之。与之评价拆分的是否合理,则是在需求开发上线时候,是否每次都大量操作多个微服务开发和上线。这样的战略设计是一种失败的微服务单体设计。所以少数几个中等规模的单体应用,周围环绕着一个服务生态系统,这更有意义。[你实际上并没有构建微服务 @贾斯汀·埃瑟里奇](https://www.simplethread.com/youre-not-actually-building-microservices/) + +- **战术设计**,在这个范畴下,主要以讨论如何基于面向对象思维,运用领域模型来表达业务概念。通常在不做领域模型设计的架构,也就是通常映射到 MVC 三层架构下,`Service + 数据模型`的开发模式,会让 Service 扁平的、大量的,平铺出非常复杂的业务逻辑代码。再加上行为对象与功能逻辑的分离,贫血模型的开发方式,让行为对象的不断交叉使用,也是让系统不断增加复杂度,并到难以维护的根因。所以这一阶段要设计每一个可以表达领域概念的模型,并运用实体、聚合、领域服务来承载。 + +## 2. DDD 的概念 + +什么是充血模型?领域内都包括什么?实体、聚合、值对象,有什么区别?这样一些"为什么"的概念,也是战术设计过程中非常重要的知识项。搞清楚它们才能做 DDD 设计。 + +### 2.1 充血模型 + +**充血模型**,指将对象的属性信息与行为逻辑聚合到一个类中,常用的手段如:在对象内提供属于当前对象的`信息校验`、`拼装缓存Key`、`不含服务接口调用的逻辑处理`等。 + +
+ +
+ +- 这样的方式可以在使用一个对象时,就顺便拿到这个对象的提供的一系列方法信息,所有使用对象的逻辑方法,都不需要自己再次处理同类逻辑。 +- 但不要只是把充血模型,仅限于一个类的设计和一个类内的方法设计。充血还可以是整个包结构,一个包下包括了用于实现此包 Service 服务所需的各类零部件(模型、仓储、工厂),也可以被看做充血模型。 +- 同时我们还会再一个同类的类下,提供对应的内部类,如用户实名,包括了,通信类、实名卡、银行卡、四要素等。它们都被写进到一个用户类下的内部子类,这样在代码编写中也会清晰的看到子类的所属信息,更容易理解代码逻辑,也便于维护迭代。 + +### 2.2 领域模型 + +**领域模型**,指特定业务领域内,业务规则、策略以及业务流程的抽象和封装。在设计手段上,通过风暴模型拆分领域模块,形成界限上下文。最大的区别在于把原有的`众多 Service + 数据模型`的方式,拆分为独立的有边界的领域模块。每个领域内创建自身所属的;领域对象(实体、聚合、值对象)、仓储服务(DAO 操作)、工厂、端口适配器Port(调用外部接口的手段)等。 + +那么,现在这里有几个概念:领域服务、领域对象、仓储定义、事件消息、端口适配器。我们先来看他们是怎么从贫血模型演变过来的,在细分讲解每个概念。 + +
+ +
+ +- 在原本的 Service + 贫血的数据模型开发指导下,Service 串联调用每一个功能模块。这些基础设施(对象、方法、接口)是被相互调用的。这也是因为贫血模型并没有面向对象的设计,所有的需求开发只有详细设计。 +- 换到充血模型下,现在我们以一个领域功能为聚合,拆分一个领域内所需的 Service 为领域服务,VO、Req、Res 重新设计为领域对象,DAO、Redis 等持久化操作为仓储等。举例:一套账户服务中的,授信认证、开户、提额降额等,每一个都是一个独立的领域,在每个独立的领域内,创建自身领域所需的各项信息。 +- 领域模型还有一个特点,它自身只关注业务功能实现,不与外部任何接口和服务直连。如;不会直接调用 DAO 操作库,也不会调用缓存操作 Redis,更不会直接引入 RPC 连接其他微服务。而是通过仓库和端口适配器,定义调用外部数据的含有出入参对象的接口标准,让基础设施层做具体的调用实现——通过这样的方式让领域只关心业务实现,同时做好防腐。 + +### 2.3 实体、聚合、值对象 + +原本在贫血模型下的开发,常常是不会特别在意一个方法的出入参对象的,也经常是很多个服务共用一个VO对象作为入参,只要这个对象能把我需要的属性信息带进来就可以了。 + +但在 DDD 的领域模型设计下,领域对象的设计是非常面向对象的。而且在整个风暴事件的四色建模过程也是在以领域对象为驱动进行的。 + +实体、聚合、值对象,三者位于每个领域下的领域对象内,服务于领域内的领域服务。三个对象定义具体如下: + +
+ +
+ +1. 胳膊、腿、身体,都是实体对象,和这个人生命有关。 +2. 帽子、手套、裤衩,都是值对象,是这个人穿戴的,不具有生命属性。 +3. 聚合,就是要写库一个事务,一次形成,可以聚合。把胳膊、腿、身体,放一块,一起让数据库执行事务。 + +> **实体** + +是依托于持久化层数据以领域服务功能目标为指导设计的领域对象。持久化PO对象是原子类对象,不具有业务语义,而实体对象是具有业务语义且有唯一标识的对象,跟随于领域服务方法的全生命周期对象。如:用户PO持久化对象,会涵盖,用户的开户实体、授信实体、额度实体对象。也包括如商品下单时候的购物车实体对象。这个对象也通常是领域服务方法的入参对象。 + +- 概念:实体 = 唯一标识 + 状态属性 + 行为动作(功能),是DDD中的一个基本构建块,它代表了具有唯一标识的领域对象。实体不仅仅包含数据(状态属性),还包含了相关的行为(功能),并且它的标识在整个生命周期中保持不变。 +- 特征: + - **唯一标识**:实体具有一个可以区分其他实体的标识符。这个标识符可以是一个ID、一个复合键或者是一个自然键,关键是它能够唯一地标识实体实例。 + - **领域标识**:实体的标识通常来源于业务领域,例如用户ID、订单ID等。这些标识符在业务上有特定的含义,并且在系统中是唯一的。 + - **委派标识**:在某些情况下,实体的标识可能是由ORM(对象关系映射)框架自动生成的,如数据库中的自增主键。这种标识符虽然可以唯一标识实体,但它并不直接来源于业务领域。 +- 用途: + - **表达业务概念**:实体用于在软件中表达具体的业务概念,如用户、订单、交易等。通过实体的属性和行为,可以描述这些业务对象的特征和能力。 + - **封装业务逻辑**:实体不仅仅承载数据,还封装了业务规则和逻辑。这些逻辑包括验证数据的有效性、执行业务规则、计算属性值等。这样做的目的是保证业务逻辑的集中和一致性。 + - **保持数据一致性**:实体负责维护自身的状态和数据一致性。它确保自己的属性和关联关系在任何时候都是正确和完整的,从而避免数据的不一致性。 +- 实现手段: + - **定义实体类**:在代码中定义一个类,该类包含实体的属性、构造函数、方法等。 + - **实现唯一标识**:为实体类提供一个唯一标识的属性,如ID,并确保在实体的生命周期中这个标识保持不变。 + - **封装行为**:在实体类中实现业务逻辑的方法,这些方法可以操作实体的状态,并执行相关的业务规则。 + - **使用ORM框架**:利用ORM框架将实体映射到数据库表中,这样可以简化数据持久化的操作。 + - **实现领域服务**:对于跨实体或跨聚合的操作,可以实现领域服务来处理这些操作,而不是在实体中直接实现。 + - **使用领域事件**:当实体的状态发生变化时,可以发布领域事件,这样可以通知其他部分的系统进行相应的处理。 + +> **值对象** + +这个对象在领域服务方法的生命周期过程内是不可变对象,也没有唯一标识。它通常是配合实体对象使用。如为实体对象提供对象属性值的描述,比如:一个公司雇员的级别值对象,一个下单的商品收货的四级地址信息对象。所以在开发值对象的时候,通常不会提供 setter 方法,而是提供构造函数或者 Builder 方法来实例化对象。这个对象通常不会独立作为方法的入参对象,但做可以独立作为出参对象使用。 + +- 概念:值对象是由一组属性组成的,它们共同描述了一个领域概念。与实体(Entity)不同,值对象不需要有一个唯一的标识符来区分它们。值对象通常是不可变的,这意味着一旦创建,它们的状态就不应该改变。 +- 特征: + - **不可变性(Immutability)**:值对象一旦被创建,它的状态就不应该发生变化。这有助于保证领域模型的一致性和线程安全性。 + - **等价性(Equality)**:值对象的等价性不是基于身份或引用,而是基于对象的属性值。如果两个值对象的所有属性值都相等,那么这两个对象就被认为是等价的。 + - **替换性(Replaceability)**:由于值对象是不可变的,任何需要改变值对象的操作都会导致创建一个新的值对象实例,而不是修改现有的实例。 + - **侧重于描述事物的状态**:值对象通常用来描述事物的状态,而不是事物的唯一身份。 + - **可复用性(Reusability)**:值对象可以在不同的领域实体或其他值对象中重复使用。 +- 用途: + - 金额和货币(如价格、工资、费用等) + - 度量和数据(如重量、长度、体积等) + - 范围或区间(如日期范围、温度区间等) + - 复杂的数学模型(如坐标、向量等) + - 任何其他需要封装的属性集合 +- 实现手段: + - **定义不可变类**:确保类的所有属性都是私有的,并且只能通过构造函数来设置。 + - **重写equals和hashCode方法**:这样可以确保值对象的等价性是基于它们的属性值,而不是对象的引用。 + - **提供只读访问器**:只提供获取属性值的方法,不提供修改属性值的方法。 + - **使用工厂方法或构造函数创建实例**:这有助于确保值对象的有效性和一致性。 + - **考虑序列化支持**:如果值对象需要在网络上传输或存储到数据库中,需要提供序列化和反序列化的支持。 + +> **聚合** + +当你对数据库的操作需要使用到多个实体时,可以创建聚合对象。一个聚合对象,代表着一个数据库事务,具有事务一致性。聚合中的实体可以由聚合提供创建操作,实体也被称为聚合根对象。一个订单的聚合,会涵盖:下单用户实体对象、订单实体、订单明细实体和订单收货四级地址值对象。而那个作为入参的购物车实体对象,已经被转换为实体对象了。—— 聚合内事务一致性,聚合外最终一致性。 + +- 概念:聚合是领域模型中的一个关键概念,它是一组具有内聚性的相关对象的集合,这些对象一起工作以执行某些业务规则或操作。聚合定义了一组对象的边界,这些对象可以被视为一个单一的单元进行处理。 +- 特征: + - **一致性边界**:聚合确保其内部对象的状态变化是一致的。当对聚合内的对象进行操作时,这些操作必须保持聚合内所有对象的一致性。 + - **根实体**:每个聚合都有一个根实体(Aggregate Root),它是聚合的入口点。根实体拥有一个全局唯一的标识符,其他对象通过根实体与聚合交互。 + - **事务边界**:聚合也定义了事务的边界。在聚合内部,所有的变更操作应该是原子的,即它们要么全部成功,要么全部失败,以此来保证数据的一致性。 +- 用途: + 1. **封装业务逻辑**:聚合通过将相关的对象和操作封装在一起,提供了一个清晰的业务逻辑模型,有助于业务规则的实施和维护。 + 2. **保证一致性**:聚合确保内部状态的一致性,通过定义清晰的边界和规则,聚合可以在内部强制执行业务规则,从而保证数据的一致性。 + 3. **简化复杂性**:聚合通过组织相关的对象,简化了领域模型的复杂性。这有助于开发者更好地理解和扩展系统。 +- 实现手段: + - **定义聚合根**:选择合适的聚合根是实现聚合的第一步。聚合根应该是能够代表整个聚合的实体,并且拥有唯一标识。 + - **限制访问路径**:只能通过聚合根来修改聚合内的对象,不允许直接修改聚合内部对象的状态,以此来维护边界和一致性。 + - **设计事务策略**:在聚合内部实现事务一致性,确保操作要么全部完成,要么全部回滚。对于聚合之间的交互,可以采用领域事件或其他机制来实现最终一致性。 + - **封装业务规则**:在聚合内部实现业务规则和逻辑,确保所有的业务操作都遵循这些规则。 + - **持久化**:聚合根通常与数据持久化层交互,以保存聚合的状态。这通常涉及到对象-关系映射(ORM)或其他数据映射技术。 + + +### 2.4 仓储和适配器 + +在 DDD 的设计方法中,领域层做到了只关心领域服务实现。最能体现这样设计的就是仓库和适配器的设计。通常在 `Service + 数据模型`的设计中,会在 Service 中引入 Redis、RPC、配置中心等各类其他外部服务。但在 DDD 中,通过仓储和适配器以及基础设施层的定义,解耦了这部分内容。 + +
+ +
+ +- 特征: + - 封装持久化操作:Repository负责封装所有与数据源交互的操作,如创建、读取、更新和删除(CRUD)操作。这样,领域层的代码就可以避免直接处理数据库或其他存储机制的复杂性。 + - 领域对象的集合管理:Repository通常被视为领域对象的集合,提供了查询和过滤这些对象的方法,使得领域对象的获取和管理更加方便。 + - 抽象接口:Repository定义了一个与持久化机制无关的接口,这使得领域层的代码可以在不同的持久化机制之间切换,而不需要修改业务逻辑。 + +- 用途: + - 数据访问抽象:Repository为领域层提供了一个清晰的数据访问接口,使得领域对象可以专注于业务逻辑的实现,而不是数据访问的细节。 + - 领域对象的查询和管理:Repository使得对领域对象的查询和管理变得更加方便和灵活,支持复杂的查询逻辑。 + - 领域逻辑与数据存储分离:通过Repository模式,领域逻辑与数据存储逻辑分离,提高了领域模型的纯粹性和可测试性。 + - 优化数据访问:Repository实现可以包含数据访问的优化策略,如缓存、批处理操作等,以提高应用程序的性能。 + +- 实现手段: + - 定义Repository接口:在领域层定义一个或多个Repository接口,这些接口声明了所需的数据访问方法。 + - 实现Repository接口:在基础设施层或数据访问层实现这些接口,具体实现可能是使用ORM(对象关系映射)框架,如MyBatis、Hibernate等,或者直接使用数据库访问API,如JDBC等。 + - 依赖注入:在应用程序中使用依赖注入(DI)来将具体的Repository实现注入到需要它们的领域服务或应用服务中。这样做可以进一步解耦领域层和数据访问层,同时也便于单元测试。 + - 使用规范模式(Specification Pattern):有时候,为了构建复杂的查询,可以结合使用规范模式,这是一种允许将业务规则封装为单独的业务逻辑单元的模式,这些单元可以被Repository用来构建查询。 + +>Repository模式是DDD(领域驱动设计)中的一个核心概念,它有助于保持领域模型的聚焦和清晰,同时提供了灵活、可测试和可维护的数据访问策略。 + +仓储解耦的手段使用了依赖倒置的设计,所有领域需要的外部服务,不在直接引入外部的服务,而是通过定义接口的方式,让基础设施层实现领域层接口(仓储/适配器)的方式来处理。 + +那么也就是基础设置层负责原则对接`数据库`、`缓存`、`配置中心`、`RPC接口`、`HTTP接口`、`MQ推送`等各项资源,并承接领域服务的接口调用各项服务为领域层提供数据能力。 + +同时这也会体现出,领域层的实现是具有业务语义的,而到了基础设置层则没有业务语义,都是原子的方法。通过原子方法的组合为领域业务语义提供支撑。 + +### 2.5 领域编排 + +在 DDD 中,每一个领域都是界限上下文拆分的独立结果,而实现业务流程的功能则需要串联各个领域模块提供一整条链路的完整服务。所以也常说领域内事务一致性,领域外最终一致性。 + +同时这些领域模块因为是独立的,所以也可以被复用。在不同的场景功能诉求下,可以选择不同的领域模块进行组装,这个过程就像搭积木一样。 + +但这里有一个取舍,如果项目相对来说并不大,也没有太多的编排处理。那么可以直接让触发器层对接领域层,减少编排层后,编码会更加便捷。 + +### 2.6 触发器 + +在所有的模型都定义完成后,领域业务被串联了。那么接下来则是使用,而使用的方式可以包括;接口(http/rpc)、消息监听、定时任务等方式,这些方式统一被定义为触发动作。 + +由触发发起对编排功能的调用处理,如:定时任务做信贷的计息、开户成功消息通知返利优惠券、提供接口让外部调用授信逻辑等——这些都是触发动作。 diff --git a/docs/md/road-map/ddd-guide-02.md b/docs/md/road-map/ddd-guide-02.md new file mode 100755 index 000000000..c74c5e72a --- /dev/null +++ b/docs/md/road-map/ddd-guide-02.md @@ -0,0 +1,138 @@ +--- +title: DDD 建模方法 +lock: need +--- + +# DDD 建模 —— 架构师总说的风暴模型是什么? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +四色建模(风暴事件)是整个 DDD 这套软件设计方法中用于工程拆分界限上下文的非常重要的实践手段。通过建模过程快速识别业务领域中的关键事件和核心流程,也是在这个过程中设计出领域对象的,为后面详细设计和代码开发做指导。 + +你可以把整个过程理解为,为工程开发提供面向对象设计,涵盖;领域拆分、界限串联、功能聚合。所以相比`Service + 数据模型`的贫血开发方式,**DDD 前期需要付出更多的设计成本,但对于软件的长周期迭代,这样的好处是非常大的。** + +## 1. 建模目的 + +工程的建模的目的是为了我们做工程开发时提供指导方案,就像一栋大楼的设计蓝图一样,也像一个超市中会有不同品类的货架,需要提前规划好。所以你需要在工程开发时所需的各类核心内容,都会在建模中体现,如:分几个包、有哪些核心对象、要串联什么流程、有哪些核心业务要实现、过程中与外部服务的交互。 + +为了达成讨论的共识,避免每个人都有不同的标准和词汇,我们通常会使用 DDD (领域驱动设计)提供的专门建模方法和统一的名词进行设计。DDD 的统一建模语言不涉及具体的技术编码,具有较强的通用性,因此可以让产品、研发、测试、架构师等人员一起参与讨论建模过程,促进跨职能的沟通和协作。如:领域、领域模型(实体、聚合、值对象)、领域服务、端口适配器、仓储、界限上下文、领域编排等名词。*这在上一节已经做了相关的解释。* + +## 2. 如何建模 + +DDD 的建模过程,是以一个用户为起点,通过行为命令,发起行为动作,串联整个业务。而这个用户的起点最初来自于用例图的分析。用例图是用户与系统交互的最简表示形式,展现了用户和与他相关的用例之间的关系。通过用例图,我们可以分析出所有的行为动作。 + +在 DDD 中用于完成用户的行为命令和动作分析的过程,是一个四色建模的过程,也称作风暴模型。在使用 DDD 的标准对系统建模前,一定要先了解 DDD 的操作手段,这样才能让产品、研发、测试、运营等了解业务的伙伴,都能在同一个语言下完成系统建模。 + +
+ +
+ +上图是整个四色建模的指导图,通过寻找领域事件,发起事件命令,完成领域事件的过程,完成 DDD 工程建模。 + +- 蓝色 - 决策命令,是用户发起的行为动作,如:开始签到、开始抽奖、查看额度等。 +- 黄色 - 领域事件,过去时态描述。如:签到完成、抽奖完成、奖品发放完成。它所阐述的都是这个领域要完成的终态。 +- 粉色 - 外部系统,如你的系统需要调用外部的接口完成流程。 +- 红色 - 业务流程,用于串联决策命令到领域事件,所实现的业务流程。一些简单的场景则直接由决策命令到领域事件就可以了。 +- 绿色 - 只读模型,做一些读取数据的动作,没有写库的操作。 +- 棕色 - 领域对象,每个决策命令的发起,都是含有一个对应的领域对象。 + +**👩🏻‍🏫敲黑板** 综上,上图左下部分的示意图表达的是:“一个用户,通过一个策略命令,使用领域对象,通过业务流程,完成2个领域事件,调用1次外部接口”的过程。我们在整个 DDD 建模过程中,就是在寻找这些节点。 + +## 3. 超市举例 + +我们先通过一个真实场景的案例,代入下 DDD 四色建模的术语,这样可以更有益大家对四色建模理解。这个场景是一个在超市购物的场景。想象下,一个男人,得到了老婆的"命令",去大超市拿着空的购物车,推进去一圈圈的走,最终完成购物打车回家。 + +
+ +
+ +- 脑子中的想法,是媳妇给下达的指令。我们可以理解为决策命令。行为人带着决策命令来到超市。 +- 购物车是一种实体,需要填充数据的实体。行为人,带着实体,进入到超市中从各个货架选购商品,装入购物车。选购商品为业务流程,装满的购物车为领域事件。也就是最终态,完成媳妇交代的任务。而手里的烟,则是另外一个领域事件。也就是说,一次的行为动作可以完成多个领域事件。 +- 最终购物完成后,打车回家。则是下一个领域流程。通过把从加入出门、做地铁、进超市、采购、打车回家,一整条领域串联起来就是领域编排。 + +综上,DDD 的领域建模过程,就是一种真实的场景头脑风暴过程,所以可以让更多人同时讨论,拆分出各项领域的边界和领域模型。 + +## 4. 业务案例 + +接下来,我们在以一个真实的业务场景来分析系统的四色建模过程: + +### 1. 产品诉求 + +如图,是一个复杂的营销抽奖场景玩法需求,涵盖了;`活动配置`、`签到&奖励`、`活动账户`、`抽奖策略「责任链+规则树」`、`库存扣减`、`抽奖满N次后阶梯抽奖`等。面对这样的复杂系统,非常适合使用 DDD 落地。 + +
+ +
+ +分析需求; + +1. 整体概率相加,总和为1或者分值计算,概率范围千分位 +2. 抽奖为免费抽奖次数 + 用户消耗个人积分抽奖 +3. 抽奖活动可给用户分配可抽奖次数,通过点击签到发放 +4. 活动延伸配置用户库存消耗管理,单独提供表配置各类库存 + 用户可用总库存、用户可用日库存 +5. 部分抽奖规则,需要抽奖n次后解锁,才能有机会抽取 +6. 抽奖完成增加(运气值/积分值/抽奖次数)记录,让用户获得奖品。 +7. 奖品对接,自身的积分、内部系统的奖品 +8. 随机积分,发给你积分。 +9. 黑名单用户抽奖,则给予固定的奖品。 + +### 2. 用例图 + +根据业务需求画系统用例图; + +
+ +
+ +- 用例图(英语:use case diagram)是用户与系统交互的最简表示形式,展现了用户和与他相关的用例之间的关系。通过用例图,人们可以获知系统不同种类的用户和用例。用例图也经常和其他图表配合使用。 +- 用例图,也可以等同于是用户故事(英语:User story)(软件开发和项目管理中的常用术语),主旨是以日常语言或商务用语撰写句子,是一段简单的功能表述。以客户或使用者的观点撰写下有价值的功能、引导、框架来与使用者进行互动,进而推动工作进程。可以被认为是一种规格文件,但更精确而言,它代表客户的需求与方向。以该用户故事来反应对象在组织内的其工作职责、范围、需要进行的任务等。用户故事在敏捷开发方法中用来定义系统需要提供的功能和实现需求管理。 +- 尽管用例本身会涉及大量细节和各种可能性,用例图却能提纲挈领地让人了解系统概况。它为“系统做什么”提供了简化了的图形表示,因此被誉为“搭建系统的蓝图”。 + +### 3. 寻找领域事件 + +接下来,大量的时间,都是在挖掘领域事件。这个过程就是一堆人头脑风暴的过程,避免错失流程节点。 + +
+ +
+ +- 根据产品 PRD 文档,一起开会梳理有哪些领域事件。其实大多数领域事件一个人都可以想到,只是有些部分小的场景和将来可能产生的事件不一定覆盖全。所以要通过产品、测试、以及团队的架构师,一起讨论。 +- 像是整个大营销的抽奖会包括如图所列举的事件。在列举这个阶段,你用在乎格式。也可以是每个人准备好黄色便签纸,想到一个就贴到黑板上一个,只是穷举完成。—— 实际做DDD中,也是这样用便签纸贴黑板,所以用不同的颜色做区分。 + +### 4. 识别领域角色和对象 + +在确定了领域事件以后,接下来要做的就是通过决策命令串联领域事件,并填充上所需要的领域对象。这块的操作,新手可以分开处理,如先给领域事件添加决策命令、执行用户和领域对象,最后再串联流程。就像 `事件风暴定义` 中的示意一样。 + +
+ +
+ +- 首先,通过用户的行为动作,也就是决策命令,串联到对应的领域事件上。并对复杂的流程提供出红色的业务流程。 +- 之后,为决策命令添加领域对象,每一个领域在整个流程中都起到了至关重要的作用。 + +### 5. 划分领域边界 + +有了识别出来的领域角色的流程,就可以非常容易的划分出领域边界了。先在事件风暴图上圈出领域边界,之后在单独提供领域划分。 + +#### 5.1 圈出领域 + +
+ +
+ +#### 5.2 领域边界 + +
+ +
+ +- 到这步咱们就可以获得整个项目中 DDD 的领域边界划分了。之后再往下就是具体的每个领域对象的详细设计和流程设计。 + diff --git a/docs/md/road-map/ddd-guide-03.md b/docs/md/road-map/ddd-guide-03.md new file mode 100755 index 000000000..84602a710 --- /dev/null +++ b/docs/md/road-map/ddd-guide-03.md @@ -0,0 +1,509 @@ +--- +title: DDD 工程模型 +lock: need +--- + +# DDD 工程模型 - 在分布式微服务架构下,MVC比DDD,要乱的多! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +凡是做到架构师岗位的,都是具有一定技术思维敏感性的,不会主观评价好和坏,但能推演出业务与技术的迭代发展被动熵增与减熵增的意识。就像拿MVC与DDD对比,也能确切的感受到,在架构方面对比于单体应用的分布式架构,是要额外引入非常多的技术栈使用。但这些模块在 MVC 下并没有设计好如何"安置"他们,就像一个老城区里突然引入了很多外来人口,不知道怎么安置一样。**不过 DDD 就像雄安新区做了整体规划!** + +
+ +
+ +**什么是系统的工程结构,工程框架的作用是什么?** + +其实,工程结构的存在作用目的,是为了承载工程系统开发的模型划分,定义工程服务开发过程中实施标准。说白了,就是你在工程实现时,在哪个层访问数据库、哪个层使用缓存、哪个层调用外部接口、哪个层做功能实现,这就是工程框架结构定义的目的。 + +但在 `Service + 贫血模型` 的三层结构开发指导下,是没有细分出面向对象工程结构设计的趋于划分的。所以在通常意义的 MVC 下,开发过程所有需要的内容,都会堆砌到 Service 实现类中。这也是为什么 DDD 领域驱动设计的落地工程结构,会出现;洋葱架构、整洁架构、菱形架构、六边形架构等这些架构模型。因为我们需要更细致的划分,来承载 DDD 设计概念中映射的领域、仓储、适配、编排、触发,并重视面向对象过程。—— 其实你一上学,学Java就开始学面向对象了,只不过一点点在忘记它。 + +>本节是DDD概率系列的第3节,讲解 DDD 架构设计,在 [bugstack.cn](https://bugstack.cn) 路书中有完整系列内容,可以查阅。 + +## 一、为啥需要架构 + +说到开发代码为啥需要架构,就想买了个房子,为啥要隔出厨房、客厅、卧室、卫生间一样,核心目的就是让不同的职责分配到不同的区域内。虽然在代码中是没有马桶要放卫生间、沙发要放客厅、床要放卧室。但他有一些列的科目信息要引入到工程。 + +**在工程开发时会涉及到的核心科目;** + +
+ +
+ +如;统一的异常、数据库的连接、日志的打印、外部服务的调用、消息的监听、任务的轮训以及服务的实现等一些列的东西要处理,分配到不同的工程包下承载。在 DDD 之前,我们一直用 MVC 的分层结构承接这些内容; + +
+ +
+ +通用的、配置的、组件的、持久化的、内部的、外部的,在以往的单体应用时代开发下,其实是没有这么多东西的,那时候的工程结构都偏向于 Service + 贫血模型实现。 + +但随着微服务的演进,越来越多的内容被填充到工程中,这个时候你细心的查看架构,就会发现原本的 MVC 结构其实已经变的非常混乱了。一个 Service 中为了实现自己的功能,要引入一堆的东西,这些原子的功能与 Service 自身的服务耦合在一块。也导致了工程的维护成本越来越大。 + +>这样的三层工程结构分配方式,对于要承载庞大的分布式技术栈体系显然是有点小马拉大车,三缸机带不动SUV一样。 + +## 二、工程结构设计 + +2004年,Eric Evans 在发表了一部名为《Domain Driven Design》的著作。2005年 Alistair Cockburn 提出的“六边形关系图”理论,2008年 Jeffrey Palermo 提出了洋葱架构。虽然这些架构并不是专门为 DDD 而出,但巧的是这些架构都在 DDD 一书发表之后陆续推出新的架构模型。同时这些架构的分层设计方式也都与 DDD 非常契合,在这些架构下也可以很好的落地 DDD 设计方法。 + +
+ +
+无论是六边形架构,还是洋葱架构,或是 张毅老师 - http://zhangyi.xyz/ 提到的南向网关/北向网关的菱形架构,他们的目标都是以领域服务为核心,隔离内部实现与外部资源的耦合。 + +在 DDD 分层架构下,以支撑 domain 核心领域实现拆分出基础设施(infrastructure),来承接对外部资源的调用。触发器(trigger)向外部提供服务。之后 app 为应用启动、api 为接口定义、types 为通用信息、case 为编排。 + +在这样一套结构下,用于开发工程的各项科目也可以被优雅的分配到各个分层结构了。相对于 Service + 数据模型的贫血模型结构,现在就主要以 domain 为核心开发业务功能,不会在 domain 工程模块下,引入其他各类外部组件了,这样就可以更加关心业务功能开发。 + +之后是这样的思想映射到工程中,常见的分层结构会有两套,一套是整洁分层,另外一套是六边形分层。 + +### 1. 整洁架构 - 工程结构 + +
+ +
+ +- 整洁架构的分包形式,会将所有的外部依赖使用和工程内要对外的,统一定义到适配器层。这里可以理解为对外适配和对内适配。 +- 阿里的 cola 偏整洁架构 [阿里架构 cola 的相关内容](https://www.aliyun.com/sswb/610119.html) + +### 2. 六边形架构 - 工程结构 + +
+ +
+ +六边形架构,会把本身提供到外部的放到trigger,让接口调用、消息监听、任务调度,都可以统一一个入口处理。而对于需要调用外部同类的能力统一放到 infrastructure 基础设施层,包括;数据库、缓存、配置、调用其他方的接口。 + +## 三、领域模型设计 + +虽然大家用的都是 DDD,也都有对应的模块和分包,但在细节之处还是会有一些差异。就像家里的家庭成员,衣服、裤子、鞋子,是所有人的衣服都放一起,还是每个人有独立的衣柜只放自己的。这块是有差异的。另外这东西没有绝对的好和坏,就像厨房里的碗筷是是放一起的,卫生间的马桶也是共用的,这说明分包也是需要按照最符合自己所需来设定。 + +### 1. 分包方式 + +如下,是两种分包方式; + +
+ +
+ +- 方式1;DDD 领域科目类型分包,类型之下写每个业务逻辑。 +- 方式2;业务领域分包,每个业务领域之下有自己所需的 DDD 领域科目。 + +比如,你现在一个工程下有用户、积分、抽奖、支付,(紧凑的聚合类微服务有时候更易于维护),那么这些包一种是分为独立的业务包方式2这种,另外一种就是大家都在一个坛子里吃饭,要啥去各个地方找。所以你更倾向于那种呢? + +### 2. 领域模型 + +DDD 领域驱动设计的中心,主要在于领域模型的设计,以领域所需驱动功能实现和数据建模。一个领域服务下面会有多个领域模型,每个领域模型都是一个充血结构。**一个领域模型 = 一个充血结构** + +
+ +
+ +- model 模型对象; + - aggreate:聚合对象,实体对象、值对象的协同组织,就是聚合对象。 + - entity:实体对象,大多数情况下,实体对象(Entity)与数据库持久化对象(PO)是1v1的关系,但也有为了封装一些属性信息,会出现1vn的关系。 + - valobj:值对象,通过对象属性值来识别的对象 By 《实现领域驱动设计》 +- repository 仓储服务;从数据库等数据源中获取数据,传递的对象可以是聚合对象、实体对象,返回的结果可以是;实体对象、值对象。因为仓储服务是由基础层(infrastructure) 引用领域层(domain),是一种依赖倒置的结构,但它可以天然的隔离PO数据库持久化对象被引用。 +- service 服务设计;这里要注意,不要以为定义了聚合对象,就把超越1个对象以外的逻辑,都封装到聚合中,这会让你的代码后期越来越难维护。聚合更应该注重的是和本对象相关的单一简单封装场景,而把一些重核心业务方到 service 里实现。**此外;如果你的设计模式应用不佳,那么无论是领域驱动设计、测试驱动设计还是换了三层和四层架构,你的工程质量依然会非常差。** +- 对象解释 + - DTO 数据传输对象 (data transfer object),DAO与业务对象或数据访问对象的区别是:DTO的数据的变异子与访问子(mutator和accessor)、语法分析(parser)、序列化(serializer)时不会有任何存储、获取、序列化和反序列化的异常。即DTO是简单对象,不含任何业务逻辑,但可包含序列化和反序列化以用于传输数据。 + +## 四、分层调用链路 + +接下来我们把 DDD 的分层架构平铺展开,看看从一个接口的实现到各个模块分层中的调用链路关系是什么样的。这样在做自己的代码开发中也可以参考到应该把什么的功能分配到哪个模块中处理。 + +![](https://bugstack.cn/images/roadmap/tutorial/road-map-ddd-05.png) + +从APP层、触发器层、应用层,这三块主要对领域层的上下文逻辑封装、触发式(MQ、HTTP、JOB)使用,并最终在应用层中打包发布上线。这一部分的都是使用的处理,所以也不会有太复杂的操作。 + +当进入领域层开始,也是智力集中体现的开始了。所有你对工程的抽象能力,都在这一块区域体现。 + +**关于对象定义**;vo、po、dto、entity、aggregate、req、res、response + +以下描述使用场景为,在 DDD 领域驱动架构下(六边形、整洁、COLA) + +domain 领域层; +- entity,实体对象,如雇员用户的基本信息、订单信息、配送信息 +- vo(value object),值对象,用于描述实体对象信息。如一个人,这个雇员用户的基本 level 枚举值对象、这个人居住地址四级信息对象。这些对象不具有唯一性,也就是不具有生命特征。就像你,之后你的衣服,你的胡子。 +- aggregate,聚合对象,当我们要写一笔订单入口的时候,需要做事务,事务如果需要一组对象;订单记录、账户记录、库存记录等,这些实体对象+值对象,写入到聚合对象内,一起提交过去。 + +* 以前的 MVC 下的 XXXVo、XxxReq、XxxRes,现在被领域细分成各个模块下的,实体、聚合、值对象了。* + +infrastructure 基础设施层; +- po 数据库持久化对象,用于映射数据库表字段的。这个对象不要做业务流程,只提供数据库数据 +- dto 数据传输对象,这个对象也会在基础设施层出现,用于与外部的接口对接。比如 rpc 接口、http 接口,出入参的对象,都叫做数据传输对象。命名为 XxxRequestDTO、XxxResponseDTO 支付包的sdk包里就是这样命名的。 + +api 层: +- dto 对象,接口的出入参,数据传输对象。命名为 XxxRequestDTO、XxxResponseDTO +- response 对象,包装结果对象,提供 code、info、data,准确描述错误码以及data数据,data数据是泛型,用于包装 XxxResponseDTO 结果。当你f12浏览器,一些互联网的web服务,观察他的接口,就会看到这样的结构。 + +case 编排层: +- 这一层承接 web 的接口编排动作,串联 domain 领域流程。通常2个方案,一个是引入 api 层,定义的对象。另外一个就是多一层转换,在一层定义 api 层对应的 XxxRequestDTO -> XxxRequest、XxxResponseDTO -> XxxResponse + +## 五、工程架构案例 + +### 1. 环境 + +- JDK 1.8 +- Maven 3.8.6 +- SpringBoot 2.7.2 +- MySQL 5.7 - 如果你使用 8.0 记得更改 pom.xml 中的 mysql 引用 +- Dubbo - [https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/registry/multicast/](https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/registry/multicast/) 文档&广播模式地址说明 + +### 2. 架构 + +- **源码**:[`https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-ddd`](https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-ddd) +- **树形**:`安装 brew install tree` `IntelliJ IDEA Terminal 使用 tree` + +```java +. +├── README.md +├── docs +│   ├── dev-ops +│   │   ├── environment +│   │   │   └── environment-docker-compose.yml +│   │   ├── siege.sh +│   │   └── skywalking +│   │   └── skywalking-docker-compose.yml +│   ├── doc.md +│   ├── sql +│   │   └── road-map.sql +│   └── xfg-frame-ddd.drawio +├── pom.xml +├── xfg-frame-api +│   ├── pom.xml +│   ├── src +│   │   └── main +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── api +│   │   ├── IAccountService.java +│   │   ├── IRuleService.java +│   │   ├── model +│   │   │   ├── request +│   │   │   │   └── DecisionMatterRequest.java +│   │   │   └── response +│   │   │   └── DecisionMatterResponse.java +│   │   └── package-info.java +│   └── xfg-frame-api.iml +├── xfg-frame-app +│   ├── Dockerfile +│   ├── build.sh +│   ├── pom.xml +│   ├── src +│   │   ├── main +│   │   │   ├── bin +│   │   │   │   ├── start.sh +│   │   │   │   └── stop.sh +│   │   │   ├── java +│   │   │   │   └── cn +│   │   │   │   └── bugstack +│   │   │   │   └── xfg +│   │   │   │   └── frame +│   │   │   │   ├── Application.java +│   │   │   │   ├── aop +│   │   │   │   │   ├── RateLimiterAop.java +│   │   │   │   │   └── package-info.java +│   │   │   │   └── config +│   │   │   │   ├── RateLimiterAopConfig.java +│   │   │   │   ├── RateLimiterAopConfigProperties.java +│   │   │   │   ├── ThreadPoolConfig.java +│   │   │   │   ├── ThreadPoolConfigProperties.java +│   │   │   │   └── package-info.java +│   │   │   └── resources +│   │   │   ├── application-dev.yml +│   │   │   ├── application-prod.yml +│   │   │   ├── application-test.yml +│   │   │   ├── application.yml +│   │   │   ├── logback-spring.xml +│   │   │   └── mybatis +│   │   │   ├── config +│   │   │   │   └── mybatis-config.xml +│   │   │   └── mapper +│   │   │   ├── RuleTreeNodeLine_Mapper.xml +│   │   │   ├── RuleTreeNode_Mapper.xml +│   │   │   └── RuleTree_Mapper.xml +│   │   └── test +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── test +│   │   └── ApiTest.java +│   └── xfg-frame-app.iml +├── xfg-frame-ddd.iml +├── xfg-frame-domain +│   ├── pom.xml +│   ├── src +│   │   └── main +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── domain +│   │   ├── order +│   │   │   ├── model +│   │   │   │   ├── aggregates +│   │   │   │   │   └── OrderAggregate.java +│   │   │   │   ├── entity +│   │   │   │   │   ├── OrderItemEntity.java +│   │   │   │   │   └── ProductEntity.java +│   │   │   │   ├── package-info.java +│   │   │   │   └── valobj +│   │   │   │   ├── OrderIdVO.java +│   │   │   │   ├── ProductDescriptionVO.java +│   │   │   │   └── ProductNameVO.java +│   │   │   ├── repository +│   │   │   │   ├── IOrderRepository.java +│   │   │   │   └── package-info.java +│   │   │   └── service +│   │   │   ├── OrderService.java +│   │   │   └── package-info.java +│   │   ├── rule +│   │   │   ├── model +│   │   │   │   ├── aggregates +│   │   │   │   │   └── TreeRuleAggregate.java +│   │   │   │   ├── entity +│   │   │   │   │   ├── DecisionMatterEntity.java +│   │   │   │   │   └── EngineResultEntity.java +│   │   │   │   ├── package-info.java +│   │   │   │   └── valobj +│   │   │   │   ├── TreeNodeLineVO.java +│   │   │   │   ├── TreeNodeVO.java +│   │   │   │   └── TreeRootVO.java +│   │   │   ├── repository +│   │   │   │   ├── IRuleRepository.java +│   │   │   │   └── package-info.java +│   │   │   └── service +│   │   │   ├── engine +│   │   │   │   ├── EngineBase.java +│   │   │   │   ├── EngineConfig.java +│   │   │   │   ├── EngineFilter.java +│   │   │   │   └── impl +│   │   │   │   └── RuleEngineHandle.java +│   │   │   ├── logic +│   │   │   │   ├── BaseLogic.java +│   │   │   │   ├── LogicFilter.java +│   │   │   │   └── impl +│   │   │   │   ├── UserAgeFilter.java +│   │   │   │   └── UserGenderFilter.java +│   │   │   └── package-info.java +│   │   └── user +│   │   ├── model +│   │   │   └── valobj +│   │   │   └── UserVO.java +│   │   ├── repository +│   │   │   └── IUserRepository.java +│   │   └── service +│   │   ├── UserService.java +│   │   └── impl +│   │   └── UserServiceImpl.java +│   └── xfg-frame-domain.iml +├── xfg-frame-infrastructure +│   ├── pom.xml +│   ├── src +│   │   └── main +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── infrastructure +│   │   ├── dao +│   │   │   ├── IUserDao.java +│   │   │   ├── RuleTreeDao.java +│   │   │   ├── RuleTreeNodeDao.java +│   │   │   └── RuleTreeNodeLineDao.java +│   │   ├── package-info.java +│   │   ├── po +│   │   │   ├── RuleTreeNodeLinePO.java +│   │   │   ├── RuleTreeNodePO.java +│   │   │   ├── RuleTreePO.java +│   │   │   └── UserPO.java +│   │   └── repository +│   │   ├── RuleRepository.java +│   │   └── UserRepository.java +│   └── xfg-frame-infrastructure.iml +├── xfg-frame-trigger +│   ├── pom.xml +│   ├── src +│   │   └── main +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── trigger +│   │   ├── http +│   │   │   ├── Controller.java +│   │   │   └── package-info.java +│   │   ├── mq +│   │   │   └── package-info.java +│   │   ├── rpc +│   │   │   ├── AccountService.java +│   │   │   ├── RuleService.java +│   │   │   └── package-info.java +│   │   └── task +│   │   └── package-info.java +│   └── xfg-frame-trigger.iml +└── xfg-frame-types + ├── pom.xml + ├── src + │   └── main + │   └── java + │   └── cn + │   └── bugstack + │   └── xfg + │   └── frame + │   └── types + │   ├── Constants.java + │   ├── Response.java + │   └── package-info.java + └── xfg-frame-types.iml +``` + +以上是整个🏭工程架构的 tree 树形图。整个工程由 xfg-frame-app 模的 SpringBoot 驱动。这里小傅哥在 domain 领域模型下提供了 order、rule、user 三个领域模块。并在每个模块下提供了对应的测试内容。这块是整个模型的重点,其他模块都可以通过测试看到这里的调用过程。 + +### 3. 领域 + +一个领域模型中包含3个部分;model、repository、service 三部分; +- model 对象的定义 【含有;valobj = VO、entity、Aggregate】 +- repository 仓储的定义【含有PO】 +- service 服务实现 + +以上3个模块,一般也是大家在使用 DDD 时候最不容易理解的分层。比如 model 里还分为;valobj - 值对象、entity 实体对象、aggregates 聚合对象; +- **值对象**:表示没有唯一标识的业务实体,例如商品的名称、描述、价格等。 +- **实体对象**:表示具有唯一标识的业务实体,例如订单、商品、用户等; +- **聚合对象**:是一组相关的实体对象的根,用于保证实体对象之间的一致性和完整性; + +关于model中各个对象的拆分,尤其是聚合的定义,会牵引着整个模型的设计。当然你可以在初期使用 DDD 的时候不用过分在意领域模型的设计,可以把整个 domain 下的一个个包当做充血模型结构,这样编写出来的代码也是非常适合维护的。 + +### 4. 环境(开发/测试/上线) + +**源码**:`xfg-frame-ddd/pom.xml` + +```pom + + dev + + true + + + dev + + + + test + + test + + + + prod + + prod + + +``` + +- 定义环境;开发、测试、上线。 + +**源码**:`xfg-frame-app/application.yml` + +```java +spring: + config: + name: xfg-frame + profiles: + active: dev # dev、test、prod +``` + +- 除了 pom 的配置,还需要在 application.yml 中指定环境。这样就可以对应的加载到;`application-dev.yml`、`application-prod.yml`、`application-test.yml` 这样就可以很方便的加载对应的配置信息了。尤其是各个场景中切换会更加方便。 + +### 5. 切面 + +一个工程开发中,有时候可能会有很多的统一切面和启动配置的处理,这些内容都可以在 xfg-frame-app 完成。 + +
+ +
+ +**源码**:`cn.bugstack.xfg.frame.aop.RateLimiterAop` + +```java +@Slf4j +@Aspect +public class RateLimiterAop { + + private final long timeout; + private final double permitsPerSecond; + private final RateLimiter limiter; + + public RateLimiterAop(double permitsPerSecond, long timeout) { + this.permitsPerSecond = permitsPerSecond; + this.timeout = timeout; + this.limiter = RateLimiter.create(permitsPerSecond); + } + + @Pointcut("execution(* cn.bugstack.xfg.frame.trigger..*.*(..))") + public void pointCut() { + } + + @Around(value = "pointCut()", argNames = "jp") + public Object around(ProceedingJoinPoint jp) throws Throwable { + boolean tryAcquire = limiter.tryAcquire(timeout, TimeUnit.MILLISECONDS); + if (!tryAcquire) { + Method method = getMethod(jp); + log.warn("方法 {}.{} 请求已被限流,超过限流配置[{}/秒]", method.getDeclaringClass().getCanonicalName(), method.getName(), permitsPerSecond); + return Response.builder() + .code(Constants.ResponseCode.RATE_LIMITER.getCode()) + .info(Constants.ResponseCode.RATE_LIMITER.getInfo()) + .build(); + } + return jp.proceed(); + } + + private Method getMethod(JoinPoint jp) throws NoSuchMethodException { + Signature sig = jp.getSignature(); + MethodSignature methodSignature = (MethodSignature) sig; + return jp.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes()); + } + +} +``` + +**使用** + +```java +# 限流配置 +rate-limiter: + permits-per-second: 1 + timeout: 5 +``` + +- 这样你所有的通用配置,又和业务没有太大的关系的,就可以直接写到这里了。—— 具体可以参考代码。 + +## 六、工程测试验证 + +- 首先;整个工程由 SpringBoot 驱动,提供了 road-map.sql 测试 SQL 库表语句。你可以在自己的本地mysql上进行执行。它会创建库表。 +- 之后;在 application.yml 配置数据库链接信息。 +- 之后就可以打开 ApiTest 进行测试了。你可以点击 Application 类的绿色箭头启动工程,使用触发器里的接口调用测试,或者单元测试RPC接口,小傅哥也提供了泛化调用的方式。 + +
+ +
+ +- 如果你正常获取了这样的结果信息,那么说明你已经启动成功。接下来就可以对照着DDD的结构进行学习,以及使用这样的工程结构开发自己的项目。 diff --git a/docs/md/road-map/ddd-model.md b/docs/md/road-map/ddd-model.md new file mode 100644 index 000000000..d4d443a2b --- /dev/null +++ b/docs/md/road-map/ddd-model.md @@ -0,0 +1,15 @@ +--- +title: DDD 建模案例 +lock: need +--- + +# 架构的本质之 DDD 四色建模 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +- 项目地址:[https://t.zsxq.com/199mpn9Lt](https://t.zsxq.com/199mpn9Lt) \ No newline at end of file diff --git a/docs/md/road-map/ddd.md b/docs/md/road-map/ddd.md new file mode 100644 index 000000000..e1eb279cc --- /dev/null +++ b/docs/md/road-map/ddd.md @@ -0,0 +1,438 @@ +--- +title: DDD 架构设计 +lock: need +--- + +# 架构的本质之 DDD 架构 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +从最早接触 DDD 架构,到后来用 DDD 架构不断的承接项目开发,一次次在项目开发中的经验积累。对 DDD 有了不少的理解。DDD 是一种思想,落地的形态和结构会有不同的方式,甚至在编码上也会有风格的差异。但终期目标就一个;”提供代码的可维护性,降低迭代开发成本。“也是康威定律所述:”任何组织在设计一套系统时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。“ + +但 DDD 与 MVC 相比差异较大,贸然用理论驱动代码开发,会让整个工程变得非常混乱,甚至可能虽然是用的 DDD 但最后写出来了一片四不像的 MVC 代码。所以对于程序员👨🏻‍💻来说,先能上手一个工程,在从工程了解理论会更加容易。为此小傅哥想以此文,通过实战编码的方式向大家分享 DDD 架构,并能让大家上手的 DDD 架构。 + +## 一、问题碰撞 + +`你用 MVC 写代码,遇到过最大的问题是什么?`🤔 + +简单、容易、好理解,是 MVC 架构的特点,但也正因为简单的分层逻辑,在适配较复杂的场景并且需要长周期的维护时,代码的迭代成本就会越来越高。如图; + +
+ +
+ +- 如果你接触过较大型且已经长期维护项目的 MVC 架构,你就会发现这里的 DAO、PO(持久化)、VO(业务对象) 对象,在 Service 层相互调用。那么长期开发后,就导致了各个 VO 里的属性字段数量都被撑的特别大。这样的开发方式,将`”状态”`、`“行为“`分离到不同的对象中,代码的意图渐渐模糊,膨胀、臃肿和不稳定的架构,让迭代成本增加。 +- 而 DDD 架构首先以解决此类问题为主,将各个属于自己领域范围内的行为和逻辑封装到自己的领域包下处理。这也是 DDD 架构设计的精髓之一。它希望在分治层面合理切割问题空间为更小规模的若干子问题,而问题越小就容易被理解和处理,做到高内聚低耦合。这也是康威定律所提到的,解决复杂场景的设计主要分为:分治、抽象和知识。 + +## 二、简化理解 + +在给大家讲解 MVC 架构的时候,小傅哥提到了一个简单的开发模型。开发代码可以理解为:`“定义属性 -> 创建方法 -> 调用展示” `但这个模型结构过于简单,不太适合运用了各类分布式技术栈以及更多逻辑的 DDD 架构。所以在 DDD 这里,我们把开发代码可以抽象为:`“触发 -> 函数 -> 连接”` 如图; + +
+ +
+ +- DDD 架构常用于微服务场景,因此也一个系统的调用方式就不只是 HTTP 还包括;`RPC 远程`、`MQ 消息`、`TASK 任务`,因此这些种方式都可以理解为触发。 +- 通过触发调用函数方法,我们这里可以把各个服务都当成一个函数方法来看。而函数方法通过连接,调用到其他的接口、数据库、缓存来完成函数逻辑。 + +接下来,小傅哥在带着大家把这些所需的模块,拆分到对应的DDD系统架构中。 + +## 三、架构分层 + +如下是 DDD 架构的一种分层结构,也可以有其他种方式,核心的重点在于适合你所在场景的业务开发。以下的分层结构,是小傅哥在使用 DDD 架构多种的方式开发代码后,做了简化和处理的。右侧的连线是各个模块的依赖关系。接下来小傅哥就给大家做一下模块的介绍。 + +
+ +
+ +- **接口定义 - xfg-frame-api**:因为微服务中引用的 RPC 需要对外提供接口的描述信息,也就是调用方在使用的时候,需要引入 Jar 包,让调用方好能依赖接口的定义做代理。 +- **应用封装 - xfg-frame-app**:这是应用启动和配置的一层,如一些 aop 切面或者 config 配置,以及打包镜像都是在这一层处理。你可以把它理解为专门为了启动服务而存在的。 +- **领域封装 - xfg-frame-domain**:领域模型服务,是一个非常重要的模块。无论怎么做DDD的分层架构,domain 都是肯定存在的。在一层中会有一个个细分的领域服务,在每个服务包中会有【模型、仓库、服务】这样3部分。 +- **仓储服务 - xfg-frame-infrastructure**:基础层依赖于 domain 领域层,因为在 domain 层定义了仓储接口需要在基础层实现。这是依赖倒置的一种设计方式。 +- **领域封装 - xfg-frame-trigger**:触发器层,一般也被叫做 adapter 适配器层。用于提供接口实现、消息接收、任务执行等。所以对于这样的操作,小傅哥把它叫做触发器层。 +- **类型定义 - xfg-frame-types**:通用类型定义层,在我们的系统开发中,会有很多类型的定义,包括;基本的 Response、Constants 和枚举。它会被其他的层进行引用使用。 +- **领域编排【可选】 - xfg-frame-case**:领域编排层,一般对于较大且复杂的的项目,为了更好的防腐和提供通用的服务,一般会添加 case/application 层,用于对 domain 领域的逻辑进行封装组合处理。 + +## 四、领域分层 + +DDD 领域驱动设计的中心,主要在于领域模型的设计,以领域所需驱动功能实现和数据建模。一个领域服务下面会有多个领域模型,每个领域模型都是一个充血结构。**一个领域模型 = 一个充血结构** + +
+ +
+ +- model 模型对象; + + - aggreate:聚合对象,实体对象、值对象的协同组织,就是聚合对象。 + - entity:实体对象,大多数情况下,实体对象(Entity)与数据库持久化对象(PO)是1v1的关系,但也有为了封装一些属性信息,会出现1vn的关系。 + - valobj:值对象,通过对象属性值来识别的对象 By 《实现领域驱动设计》 +- repository 仓储服务;从数据库等数据源中获取数据,传递的对象可以是聚合对象、实体对象,返回的结果可以是;实体对象、值对象。因为仓储服务是由基础层(infrastructure) 引用领域层(domain),是一种依赖倒置的结构,但它可以天然的隔离PO数据库持久化对象被引用。 +- service 服务设计;这里要注意,不要以为定义了聚合对象,就把超越1个对象以外的逻辑,都封装到聚合中,这会让你的代码后期越来越难维护。聚合更应该注重的是和本对象相关的单一简单封装场景,而把一些重核心业务方到 service 里实现。**此外;如果你的设计模式应用不佳,那么无论是领域驱动设计、测试驱动设计还是换了三层和四层架构,你的工程质量依然会非常差。** + +- 对象解释 + - DTO 数据传输对象 (data transfer object),DAO与业务对象或数据访问对象的区别是:DTO的数据的变异子与访问子(mutator和accessor)、语法分析(parser)、序列化(serializer)时不会有任何存储、获取、序列化和反序列化的异常。即DTO是简单对象,不含任何业务逻辑,但可包含序列化和反序列化以用于传输数据。 + +## 五、架构源码 + +### 1. 环境 + +- JDK 1.8 +- Maven 3.8.6 +- SpringBoot 2.7.2 +- MySQL 5.7 - 如果你使用 8.0 记得更改 pom.xml 中的 mysql 引用 +- Dubbo - [https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/registry/multicast/](https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/reference-manual/registry/multicast/) 文档&广播模式地址说明 + +### 2. 架构 + +- **源码**:[`https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-ddd`](https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-ddd) +- **树形**:`安装 brew install tree` `IntelliJ IDEA Terminal 使用 tree` + +```java +. +├── README.md +├── docs +│   ├── dev-ops +│   │   ├── environment +│   │   │   └── environment-docker-compose.yml +│   │   ├── siege.sh +│   │   └── skywalking +│   │   └── skywalking-docker-compose.yml +│   ├── doc.md +│   ├── sql +│   │   └── road-map.sql +│   └── xfg-frame-ddd.drawio +├── pom.xml +├── xfg-frame-api +│   ├── pom.xml +│   ├── src +│   │   └── main +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── api +│   │   ├── IAccountService.java +│   │   ├── IRuleService.java +│   │   ├── model +│   │   │   ├── request +│   │   │   │   └── DecisionMatterRequest.java +│   │   │   └── response +│   │   │   └── DecisionMatterResponse.java +│   │   └── package-info.java +│   └── xfg-frame-api.iml +├── xfg-frame-app +│   ├── Dockerfile +│   ├── build.sh +│   ├── pom.xml +│   ├── src +│   │   ├── main +│   │   │   ├── bin +│   │   │   │   ├── start.sh +│   │   │   │   └── stop.sh +│   │   │   ├── java +│   │   │   │   └── cn +│   │   │   │   └── bugstack +│   │   │   │   └── xfg +│   │   │   │   └── frame +│   │   │   │   ├── Application.java +│   │   │   │   ├── aop +│   │   │   │   │   ├── RateLimiterAop.java +│   │   │   │   │   └── package-info.java +│   │   │   │   └── config +│   │   │   │   ├── RateLimiterAopConfig.java +│   │   │   │   ├── RateLimiterAopConfigProperties.java +│   │   │   │   ├── ThreadPoolConfig.java +│   │   │   │   ├── ThreadPoolConfigProperties.java +│   │   │   │   └── package-info.java +│   │   │   └── resources +│   │   │   ├── application-dev.yml +│   │   │   ├── application-prod.yml +│   │   │   ├── application-test.yml +│   │   │   ├── application.yml +│   │   │   ├── logback-spring.xml +│   │   │   └── mybatis +│   │   │   ├── config +│   │   │   │   └── mybatis-config.xml +│   │   │   └── mapper +│   │   │   ├── RuleTreeNodeLine_Mapper.xml +│   │   │   ├── RuleTreeNode_Mapper.xml +│   │   │   └── RuleTree_Mapper.xml +│   │   └── test +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── test +│   │   └── ApiTest.java +│   └── xfg-frame-app.iml +├── xfg-frame-ddd.iml +├── xfg-frame-domain +│   ├── pom.xml +│   ├── src +│   │   └── main +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── domain +│   │   ├── order +│   │   │   ├── model +│   │   │   │   ├── aggregates +│   │   │   │   │   └── OrderAggregate.java +│   │   │   │   ├── entity +│   │   │   │   │   ├── OrderItemEntity.java +│   │   │   │   │   └── ProductEntity.java +│   │   │   │   ├── package-info.java +│   │   │   │   └── valobj +│   │   │   │   ├── OrderIdVO.java +│   │   │   │   ├── ProductDescriptionVO.java +│   │   │   │   └── ProductNameVO.java +│   │   │   ├── repository +│   │   │   │   ├── IOrderRepository.java +│   │   │   │   └── package-info.java +│   │   │   └── service +│   │   │   ├── OrderService.java +│   │   │   └── package-info.java +│   │   ├── rule +│   │   │   ├── model +│   │   │   │   ├── aggregates +│   │   │   │   │   └── TreeRuleAggregate.java +│   │   │   │   ├── entity +│   │   │   │   │   ├── DecisionMatterEntity.java +│   │   │   │   │   └── EngineResultEntity.java +│   │   │   │   ├── package-info.java +│   │   │   │   └── valobj +│   │   │   │   ├── TreeNodeLineVO.java +│   │   │   │   ├── TreeNodeVO.java +│   │   │   │   └── TreeRootVO.java +│   │   │   ├── repository +│   │   │   │   ├── IRuleRepository.java +│   │   │   │   └── package-info.java +│   │   │   └── service +│   │   │   ├── engine +│   │   │   │   ├── EngineBase.java +│   │   │   │   ├── EngineConfig.java +│   │   │   │   ├── EngineFilter.java +│   │   │   │   └── impl +│   │   │   │   └── RuleEngineHandle.java +│   │   │   ├── logic +│   │   │   │   ├── BaseLogic.java +│   │   │   │   ├── LogicFilter.java +│   │   │   │   └── impl +│   │   │   │   ├── UserAgeFilter.java +│   │   │   │   └── UserGenderFilter.java +│   │   │   └── package-info.java +│   │   └── user +│   │   ├── model +│   │   │   └── valobj +│   │   │   └── UserVO.java +│   │   ├── repository +│   │   │   └── IUserRepository.java +│   │   └── service +│   │   ├── UserService.java +│   │   └── impl +│   │   └── UserServiceImpl.java +│   └── xfg-frame-domain.iml +├── xfg-frame-infrastructure +│   ├── pom.xml +│   ├── src +│   │   └── main +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── infrastructure +│   │   ├── dao +│   │   │   ├── IUserDao.java +│   │   │   ├── RuleTreeDao.java +│   │   │   ├── RuleTreeNodeDao.java +│   │   │   └── RuleTreeNodeLineDao.java +│   │   ├── package-info.java +│   │   ├── po +│   │   │   ├── RuleTreeNodeLinePO.java +│   │   │   ├── RuleTreeNodePO.java +│   │   │   ├── RuleTreePO.java +│   │   │   └── UserPO.java +│   │   └── repository +│   │   ├── RuleRepository.java +│   │   └── UserRepository.java +│   └── xfg-frame-infrastructure.iml +├── xfg-frame-trigger +│   ├── pom.xml +│   ├── src +│   │   └── main +│   │   └── java +│   │   └── cn +│   │   └── bugstack +│   │   └── xfg +│   │   └── frame +│   │   └── trigger +│   │   ├── http +│   │   │   ├── Controller.java +│   │   │   └── package-info.java +│   │   ├── mq +│   │   │   └── package-info.java +│   │   ├── rpc +│   │   │   ├── AccountService.java +│   │   │   ├── RuleService.java +│   │   │   └── package-info.java +│   │   └── task +│   │   └── package-info.java +│   └── xfg-frame-trigger.iml +└── xfg-frame-types + ├── pom.xml + ├── src + │   └── main + │   └── java + │   └── cn + │   └── bugstack + │   └── xfg + │   └── frame + │   └── types + │   ├── Constants.java + │   ├── Response.java + │   └── package-info.java + └── xfg-frame-types.iml +``` + +以上是整个🏭工程架构的 tree 树形图。整个工程由 xfg-frame-app 模的 SpringBoot 驱动。这里小傅哥在 domain 领域模型下提供了 order、rule、user 三个领域模块。并在每个模块下提供了对应的测试内容。这块是整个模型的重点,其他模块都可以通过测试看到这里的调用过程。 + +### 3. 领域 + +一个领域模型中包含3个部分;model、repository、service 三部分; +- model 对象的定义 【含有;valobj = VO、entity、Aggregate】 +- repository 仓储的定义【含有PO】 +- service 服务实现 + +以上3个模块,一般也是大家在使用 DDD 时候最不容易理解的分层。比如 model 里还分为;valobj - 值对象、entity 实体对象、aggregates 聚合对象; +- **值对象**:表示没有唯一标识的业务实体,例如商品的名称、描述、价格等。 +- **实体对象**:表示具有唯一标识的业务实体,例如订单、商品、用户等; +- **聚合对象**:是一组相关的实体对象的根,用于保证实体对象之间的一致性和完整性; + +关于model中各个对象的拆分,尤其是聚合的定义,会牵引着整个模型的设计。当然你可以在初期使用 DDD 的时候不用过分在意领域模型的设计,可以把整个 domain 下的一个个包当做充血模型结构,这样编写出来的代码也是非常适合维护的。 + +### 4. 环境(开发/测试/上线) + +**源码**:`xfg-frame-ddd/pom.xml` + +```pom + + dev + + true + + + dev + + + + test + + test + + + + prod + + prod + + +``` + +- 定义环境;开发、测试、上线。 + +**源码**:`xfg-frame-app/application.yml` + +```java +spring: + config: + name: xfg-frame + profiles: + active: dev # dev、test、prod +``` + +- 除了 pom 的配置,还需要在 application.yml 中指定环境。这样就可以对应的加载到;`application-dev.yml`、`application-prod.yml`、`application-test.yml` 这样就可以很方便的加载对应的配置信息了。尤其是各个场景中切换会更加方便。 + +### 5. 切面 + +一个工程开发中,有时候可能会有很多的统一切面和启动配置的处理,这些内容都可以在 xfg-frame-app 完成。 + +
+ +
+ +**源码**:`cn.bugstack.xfg.frame.aop.RateLimiterAop` + +```java +@Slf4j +@Aspect +public class RateLimiterAop { + + private final long timeout; + private final double permitsPerSecond; + private final RateLimiter limiter; + + public RateLimiterAop(double permitsPerSecond, long timeout) { + this.permitsPerSecond = permitsPerSecond; + this.timeout = timeout; + this.limiter = RateLimiter.create(permitsPerSecond); + } + + @Pointcut("execution(* cn.bugstack.xfg.frame.trigger..*.*(..))") + public void pointCut() { + } + + @Around(value = "pointCut()", argNames = "jp") + public Object around(ProceedingJoinPoint jp) throws Throwable { + boolean tryAcquire = limiter.tryAcquire(timeout, TimeUnit.MILLISECONDS); + if (!tryAcquire) { + Method method = getMethod(jp); + log.warn("方法 {}.{} 请求已被限流,超过限流配置[{}/秒]", method.getDeclaringClass().getCanonicalName(), method.getName(), permitsPerSecond); + return Response.builder() + .code(Constants.ResponseCode.RATE_LIMITER.getCode()) + .info(Constants.ResponseCode.RATE_LIMITER.getInfo()) + .build(); + } + return jp.proceed(); + } + + private Method getMethod(JoinPoint jp) throws NoSuchMethodException { + Signature sig = jp.getSignature(); + MethodSignature methodSignature = (MethodSignature) sig; + return jp.getTarget().getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes()); + } + +} +``` + +**使用** + +```java +# 限流配置 +rate-limiter: + permits-per-second: 1 + timeout: 5 +``` + +- 这样你所有的通用配置,又和业务没有太大的关系的,就可以直接写到这里了。—— 具体可以参考代码。 + +## 六、测试验证 + +- 首先;整个工程由 SpringBoot 驱动,提供了 road-map.sql 测试 SQL 库表语句。你可以在自己的本地mysql上进行执行。它会创建库表。 +- 之后;在 application.yml 配置数据库链接信息。 +- 之后就可以打开 ApiTest 进行测试了。你可以点击 Application 类的绿色箭头启动工程,使用触发器里的接口调用测试,或者单元测试RPC接口,小傅哥也提供了泛化调用的方式。 + +
+ +
+ +- 如果你正常获取了这样的结果信息,那么说明你已经启动成功。接下来就可以对照着DDD的结构进行学习,以及使用这样的工程结构开发自己的项目。 diff --git a/docs/md/road-map/disruptor.md b/docs/md/road-map/disruptor.md new file mode 100644 index 000000000..73362e753 --- /dev/null +++ b/docs/md/road-map/disruptor.md @@ -0,0 +1,183 @@ +--- +title: Disruptor +lock: need +--- + +# Disruptor 高性能环形消息队列应用,Log4j 2 也用到了这套技术。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +说到底,无论是晋升述职还是面试考察,编程技能的展现总是在那些技术的横向对比和深度的了解运用。知其一,也知其二。一个场景的问题,往往也会对应着多种的解决方案,从没有绝对的好和不好,都是是否适合而已。所以,往往技术越好的,也越低调,不那么咋咋呼呼的。 + +
+ +
+ +**什么是柔性事务?** + +在分布式软件系统架构设计中,所有的并发资源的竞争,都会往`无锁化`、`非独占竞争`,以及`柔性事务`设计。柔性事务用于替代传统事务管理中(如ACID属性:原子性、一致性、隔离性、持久性),在分布式架构系统中的使用场景。通过消息、补偿,协调不同服务间的一致性。 + +
+ +
+ +那么在消息的使用中,除了有 MQ 消息,使用于微服务之间。还有本地消息,可以作用在各个领域间驱动流程。关于本地消息可以用,Spring 的监听、Redis 发布订阅、Guava EventBus 事件总线,这些内容在小傅哥博客 [bugstack.cn](https://bugstack.cn) 《路书》中有相关的案例。之后本节咱们介绍一个新的高性能组件 Disruptor 的使用。 + +## 一、关于 Disruptor + +Disruptor 是一种高性能的并发框架,最初由 LMAX 开发,用于解决高吞吐量、低延迟的消息处理问题。它提供了一种无锁的、有序的事件处理模型,非常适合处理需要高性能的场景。Disruptor 本身并不是用于实现事务的框架,而是一个事件处理器。因此,要在 Disruptor 上实现柔性事务,需要结合其事件处理能力与柔性事务的模式。 + +- 源码:[https://github.com/LMAX-Exchange/disruptor](https://github.com/LMAX-Exchange/disruptor) +- 文档:[https://lmax-exchange.github.io/disruptor/](https://lmax-exchange.github.io/disruptor/) - 谷歌浏览器右键点翻译为中文。 + +## 二、实战案例 + +### 1. 工程结构 + +小傅哥准备好了一份基于 Disruptor 事件消息的使用案例工程,你可以直接上手体现。 + +
+ +
+ +- app 是使用的启动层、trigger 是提供接口、监听消息、处理任务的触发器层。 +- 在这里我们通过 trigger 下的 event 包,监听事件消息。之后把这个 XxxEventHandler 让 app 层下的 Disruptor 进行实例化。 + +### 2. 引入POM + +```pom + + + com.lmax + disruptor + 3.4.4 + +``` + +- 引入 disruptor pom 包。 + +### 3. 监听消息 + +```java +@Slf4j +public class XxxEventHandler implements EventHandler { + + @Override + public void onEvent(Message longEvent, long l, boolean b) throws Exception { + log.info("接收消息:{}", longEvent.getValue()); + } + + @Data + public static class Message { + private String value; + } + +} +``` + +- 在 trigger 下 event 包内,加一个实现了 disruptor EventHandler 的监听实现类,消息体类型我们定义到 XxxEventHandler 中,也就是 Message。具体生产使用的时候,按需调整。 +- 这个接收消息的过程和使用 MQ 的方式是一样的。 + +### 4. 实例化监听 + +```java +@Configuration +public class DisruptorConfig { + + private final ExecutorService executor = Executors.newCachedThreadPool(); + + @Bean("xxxEventDisruptor") + public Disruptor disruptor() { + // 环形队列的大小,注意要是2的幂 + int bufferSize = 1024; + + // 创建Disruptor + Disruptor disruptor = new Disruptor<>(XxxEventHandler.Message::new, bufferSize, executor); + + // 连接事件处理器 + disruptor.handleEventsWith(new XxxEventHandler()); + + // 开始Disruptor + disruptor.start(); + + return disruptor; + } + +} +``` + +- 在 App 模块下,有一个 config 专门的配置类,在这里配置下消息监听。这个过程和我们之前使用的 Redis 发布订阅是一样的。 + +### 5. 推送消息(Test) + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest(classes = Application.class) +public class DisruptorTest { + + @Resource + private Disruptor xxxEventDisruptor; + + @Test + public void test_publishEvent() throws InterruptedException { + + for (int i = 0; i < 10; i++) { + xxxEventDisruptor.publishEvent((event, sequence) -> event.setValue("你好,我是 Disruptor Message")); + } + + // 暂停 - 测试完手动关闭程序 + new CountDownLatch(1).await(); + } + +} +``` + +```java +24-10-26.11:55:55.827 [main ] INFO DisruptorTest - Starting DisruptorTest using Java 1.8.0_311 on MacBook-Pro.local with PID 92827 (started by fuzhengwei in /Users/fuzhengwei/1024/KnowledgePlanet/road-map/xfg-dev-tech-disruptor/xfg-dev-tech-app) +24-10-26.11:55:55.829 [main ] INFO DisruptorTest - The following 1 profile is active: "dev" +24-10-26.11:55:57.749 [main ] INFO DisruptorTest - Started DisruptorTest in 2.526 seconds (JVM running for 3.741) +24-10-26.11:55:58.125 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +24-10-26.11:55:58.128 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +24-10-26.11:55:58.128 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +24-10-26.11:55:58.128 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +24-10-26.11:55:58.128 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +24-10-26.11:55:58.128 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +24-10-26.11:55:58.128 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +24-10-26.11:55:58.128 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +24-10-26.11:55:58.128 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +24-10-26.11:55:58.128 [pool-2-thread-1 ] INFO XxxEventHandler - 接收消息:你好,我是 Disruptor Message +``` + +- 提供一个单测来测试消息推送,这样你就可以监听到消息了。 + +## 三、总结 + +在美团、京东、阿里,等各个大厂中都有很多这样的组件使用,在美团发布过的文章中[《高性能队列——Disruptor》](https://tech.meituan.com/2016/11/18/disruptor.html) 还有一个对应的压测数据。CPU:Intel Core i7-2720QM,JVM:Java 1.6.0_25 64-bit,OS:Ubuntu 11.04 + +| - | ABQ | Disruptor | +| :----------------- | :-------- | :--------- | +| Unicast: 1P – 1C | 4,057,453 | 22,381,378 | +| Pipeline: 1P – 3C | 2,006,903 | 15,857,913 | +| Sequencer: 3P – 1C | 2,056,118 | 14,540,519 | +| Multicast: 1P – 3C | 260,733 | 10,860,121 | +| Diamond: 1P – 3C | 2,082,725 | 15,295,197 | + +- 依据并发竞争的激烈程度的不同,Disruptor比ArrayBlockingQueue吞吐量快4~7倍。 + +--- + +另外,Log4j 2 采用了 Disruptor(一种无锁的线程间通信库),提高吞吐量降低延迟。在生产使用中,大并发的系统注意 Log4j 版本。官网说明:[https://logging.apache.org/log4j/2.12.x/manual/async.html](https://logging.apache.org/log4j/2.12.x/manual/async.html) + +
+ +
+ +- **异步 Logger**是 Log4j 2 中的新增功能。其目的是尽快从对 Logger.log 的调用返回到应用程序。您可以选择使所有 Logger 异步,或使用同步和异步 Logger 的混合。使所有 Logger 异步将提供最佳性能,而混合使用则可为您提供更大的灵活性。 +- **LMAX Disruptor 技术**。异步记录器内部使用 [Disruptor(](https://logging.apache.org/log4j/2.12.x/manual/async.html#UnderTheHood)一种无锁的线程间通信库)而不是队列,从而实现更高的吞吐量和更低的延迟。 +- 作为异步日志记录器工作的一部分,**异步附加器**已得到增强,可以在批处理结束时(当队列为空时)刷新到磁盘。这会产生与配置“immediateFlush=true”相同的结果,即所有收到的日志事件始终在磁盘上可用,但效率更高,因为它不需要在每个日志事件上都接触磁盘。(异步附加器在内部使用 ArrayBlockingQueue,不需要类路径上的 Disruptor jar。) diff --git a/docs/md/road-map/docker-deploy-project.md b/docs/md/road-map/docker-deploy-project.md new file mode 100644 index 000000000..27fec75b6 --- /dev/null +++ b/docs/md/road-map/docker-deploy-project.md @@ -0,0 +1,623 @@ +--- +title: Docker 部署项目 +lock: need +--- + +# Docker 部署项目 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +在实践中发现,不少伙伴不了解云服务器是什么,搞不清楚云服务器和本地电脑的关系,也不知道怎么把一个项目部署到云服务器。但作为程序员学习编程,又离不开云服务器,逃避不了一点! + +
+ +
+ +**看看是不你也遇到了这些问题** + +云服务器选什么系统、云服务器怎么安装环境、Intellij IDEA 怎么连接云服务器、云服务器安装了 Docker 本地电脑还用安装吗、SpringBoot 项目怎么部署到云服务器、构建的镜像推送不到 Docker Hub 怎么办? + +等等,一些列的问题,把小白拦截在云服务器的门外。不过,没关系,今天小傅哥就帮你把这接腿🦵🏻的事办啦,让你以后畅通无阻。 + +>文末提供了,5个AI项目、5个业务项目、8个组件项目、1套源码项目,还有非常多的学习资料,你可以全部获取学习,积累来自互联网大厂的经验。 + +## 零、所需资源 + +- [购买云服务器 2c4g](http://618.gaga.plus/) +- [工程案例代码 xfg-dev-tech-docker-deploy](https://github.com/fuzhengwei/xfg-dev-tech-docker-deploy) +- [云服务器安装脚本](https://gaga.plus/app/dev-ops/index.html) +- [云服务器连接工具](https://origin.bugstack.cn/md/road-map/tool.html) +- [阿里云docker镜像仓库](https://cr.console.aliyun.com/cn-hangzhou/instance/repositories) + +## 一、云服务器是什么? + +在使用云服务器之前,我们先来理解下云服务器是什么,把定位搞清楚,后面的事才好办。如图; + +
+ +
+ +- 云服务器,你可以理解为是你的书桌上摆着的2台电脑。你可以通过其中一台电脑,以 SSH 方式连接另外一台电脑,并在另外一台电脑安装 MySQL、Redis、Ollama 等,以减轻当前这台电脑的压力。让手里的这台电脑只负责代码开发,需要什么资源就连接另外一台电脑的上软件即可。 +- 现在为了方便的让每个人都可以拥有自己的另外一台电脑,有一些公司就把电脑集中起来搭建成了服务器机房,在通过提供公网IP让你在购买云服务器后,可以用你的电脑连接上云服务器电脑,进行软件的部署和程序对软件的连接。 + +## 二、云服务器咋操作(命令) + +当然,云服务器的操作也需要一点 Linux 命令的知识,它不是那种`点点点`的方式使用电脑,而是通过命令行。比如,`ls` 查看文件列表、`mkdir` 创建文件夹、`chmod +x` 给文件授予执行权限,当然也有些死鬼告诉你执行 `rm -rf /*` 今天一天都不用干活! + +为了,让小伙伴们,先不需要看非常的多的资料,就能快速的使用起这些命令。这里小傅哥做了个模拟 Linux 服务器,进行命令练习的网页,你可以通过页面的模拟建立 SSH 连接,之后学习这些命令。 + +
+ +
+ +地址:[https://gaga.plus/app/dev-ops/tutorials/what-is-a-cloud-server.html](https://gaga.plus/app/dev-ops/tutorials/what-is-a-cloud-server.html) + +说明:你每点击一个右侧的命令,他就会在左侧进行输入,之后提示命令的用途和效果。这些命令包括了常用的文件、目录、系统、用户的操作,也给大家增加了 Git、Docker、Maven 的命令(右侧往下翻即可) + +## 三、云系统环境安装 + +### 1. 环境选择 + +在你首次购买和设置云服务器的时候,它会让你选择需要安装的系统(也可以重置重新安装),安装完成后你就可以通过命令操作,部署 Docker 以及安装软件了。 + +
+ +
+ +这里比较推荐 Centos 7.6、7.9 或则 Ubuntu 24+,这两个系统,我已经做好了一键安装脚本,可以让你非常方便的把 Docker 以及一些常用的软件,全部安装完成。 + +### 2. 安装脚本 + +#### 2.1 Git 安装 + +**Centos** + +```java +sudo yum install git +``` + +**Ubuntu** + +```java +# sudo apt update +# sudo apt install nodejs npm +# node -v +# npm -v +``` + + +```java +apt-get install git +``` + +#### 2.2 Docker + Portainer + +```java +root@iv-ydw2iok0lcbw80bxaha0:~# ls +root@iv-ydw2iok0lcbw80bxaha0:~# cd / +root@iv-ydw2iok0lcbw80bxaha0:/# mkdir dev-ops +root@iv-ydw2iok0lcbw80bxaha0:/# ls +bin cdrom etc lib64 media proc sbin sys var +bin.usr-is-merged dev home lib.usr-is-merged mnt root sbin.usr-is-merged tmp +boot dev-ops lib lost+found opt run srv usr +root@iv-ydw2iok0lcbw80bxaha0:/# cd dev-ops/ +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops# ls +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops# git clone https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install.git +Cloning into 'xfg-dev-tech-docker-install'... +remote: Enumerating objects: 181, done. +remote: Counting objects: 100% (35/35), done. +remote: Compressing objects: 100% (21/21), done. +remote: Total 181 (delta 20), reused 25 (delta 14), pack-reused 146 (from 1) +Receiving objects: 100% (181/181), 12.51 MiB | 1.26 MiB/s, done. +Resolving deltas: 100% (75/75), done. +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops# ls +xfg-dev-tech-docker-install +``` + +- 推荐;git clone https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install.git +- 备用;git clone https://github.com/fuzhengwei/xfg-dev-tech-docker-install.git + +- 在 Linux 系统的根目录,通过命令创建一个空文件夹,进入文件夹,通过 git clone 检出代码。 +- 这里有2个地址,如果 github.com 检出很慢,可以使用 gitcode.com 地址检出。 + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install# chmod +x ubuntu_run_install_docker_local.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install# ./ubuntu_run_install_docker_local.sh +[INFO] 使用本地Docker安装脚本: ubuntu_install_docker.sh +[INFO] 设置可执行权限... +[INFO] 开始执行Docker安装脚本... +[INFO] 注意:安装过程可能需要root权限,如果需要会自动请求 +----------------------------------------------------------- +[INFO] docker 环境安装脚本 By xiaofuge,建议使用 https://618.gaga.plus 优惠购买服务器,安装 Ubuntu 24.04 LTS 系统。 +[INFO] 开始安装 Docker 环境... +[INFO] 检查系统信息... +内核版本: 6.8.0-55-generic +操作系统: Ubuntu 24.04 LTS +[INFO] 检测网络连接状态... +[INFO] 网络连接正常,可以访问: https://www.baidu.com +[INFO] 网络连接检测通过 +... +Status: Downloaded newer image for registry.cn-hangzhou.aliyuncs.com/xfg-studio/portainer:latest +1c2d6211944ced359ebec2e5d28b9cf30eec55bc781b386838c54abc944ed1f3 +[INFO] Portainer安装成功! +[WARNING] 重要提示:请确保您的云服务器已开放9000端口! +----------------------------------------------------------- +Portainer访问方式: +1. 通过公网访问:http://您的服务器公网IP:9000 +2. 首次访问需要设置管理员账号和密码 +3. 登录后即可通过Web界面管理Docker容器 +----------------------------------------------------------- +[INFO] 您可以使用Portainer来方便地管理Docker容器、镜像、网络和卷等资源 +``` + +- Centos 执行 `chmod +x centos_run_install_docker_local.sh` `./centos_run_install_docker_local.sh` +- Ubuntu 执行 `chmod +x ubuntu_run_install_docker_local.sh` `./ubuntu_run_install_docker_local.sh` + +执行的中间,他会有一些提示你的操作,你可以按照提示输入之后继续运行就好。安装到最后,Docker + Portainer 就安装完成了。注意,云服务器的访问,需要开放对应的端口在云服务器的安全组中找到出入站配置(也可以在云服务器官网搜,他都会提供文档)。 + +#### 2.3 软件安装(JDK、Maven)可选 + +在 xfg-dev-tech-docker-install 一键安装脚本中,还有提供 JDK、Maven 的安装。这个软件安全是为了在云服务器直接构建 Java 项目,如果你不需要,是完全可以不用安装的。 + +**Centos 安装** + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install# ls +centos_install_docker.sh help.md software +centos_run_install_docker_local.sh README.md ubuntu_install_docker.sh +environment run_install_software.sh ubuntu_run_install_docker_local.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install# cd environment/ +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment# ls +jdk maven +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment# cd jdk/ +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/jdk# ls +install-java.sh remove-java.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/jdk# chmod +x install-java.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/jdk# ./install-java.sh +[INFO] 开始JDK安装程序... +[INFO] 检测到系统: Ubuntu 24.04 + +[INFO] 请选择要安装的JDK版本: +1. JDK 8 (1.8.0_202) +2. JDK 17 (17.0.14) + +请选择版本 (1/2): 1 +[INFO] 已选择JDK 8 +[INFO] JDK配置: 版本=1.8.0_202, 包名=jdk-8u202-linux-x64.tar.gz +[INFO] 检查并安装依赖包... +[INFO] JDK包已存在: ./jdk-8u202-linux-x64.tar.gz +[SUCCESS] JDK包验证通过 +[INFO] 开始安装JDK 1.8.0_202 到: /usr/local/java +[INFO] 解压JDK包... +[SUCCESS] JDK解压完成 +... +\033[0;34m环境变量:\033[0m +JAVA_HOME=/usr/local/java +PATH=$JAVA_HOME/bin:$PATH +CLASSPATH=$JAVA_HOME/jre/lib/ext:$JAVA_HOME/lib/tools.jar +是否删除下载的JDK包? (y/N): N +[SUCCESS] JDK 1.8.0_202 安装完成! +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/jdk# java -version +``` + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment# ls +jdk maven +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment# cd maven/ +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/maven# ls +apache-maven-3.8.8.zip install-maven.sh remove-maven.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/maven# chmod +x install-maven.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/maven# ./install-maven.sh +[INFO] 开始Maven安装程序... +[INFO] 检测到系统: Ubuntu 24.04 +[INFO] 检测到Java版本: 1.8.0_472 +[INFO] 检查并安装依赖包... +[SUCCESS] Maven包验证通过 +[INFO] 开始安装Maven 3.8.8 到: /usr/local/maven +[INFO] 解压Maven包... +[SUCCESS] Maven解压完成 +[INFO] 配置环境变量... +[SUCCESS] 环境变量配置完成 +[INFO] 环境变量已在当前会话中生效 +[INFO] 验证安装... +[SUCCESS] Maven安装成功! +``` + +**Ubuntu 安装** + +```java +sudo apt update +sudo apt install openjdk-8-jdk + +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment# java -version +openjdk version "1.8.0_472" +OpenJDK Runtime Environment (build 1.8.0_472-8u472-ga-1~24.04-b08) +OpenJDK 64-Bit Server VM (build 25.472-b08, mixed mode) +``` + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/maven# apt install maven +Reading package lists... Done +Building dependency tree... Done +Reading state information... Done +The following packages were automatically in + +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/maven# mvn --version +Apache Maven 3.8.7 +Maven home: /usr/share/maven +... +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/environment/maven# cd /usr/share/maven +root@iv-ydw2iok0lcbw80bxaha0:/usr/share/maven# ls +bin boot conf lib man +root@iv-ydw2iok0lcbw80bxaha0:/usr/share/maven# cd conf +root@iv-ydw2iok0lcbw80bxaha0:/usr/share/maven/conf# ls +logging m2.conf settings.xml toolchains.xml +``` + +Ubuntu 基本通过 apt 就可以完整这些软件的安装了。注意进入 conf 替换 settings.xml 文件带有阿里云 maven 镜像的地址。 + +地址:[https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install/blob/main/environment/maven/apache-maven-3.8.8/conf/settings.xml](https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install/blob/main/environment/maven/apache-maven-3.8.8/conf/settings.xml) + +#### 2.4 软件安装(MySQL、Redis) + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install# cd software/ +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/software# ls +docker-compose-software-aliyun.yml grafana logstash pgvector rabbitmq +docker-compose-software.yml kibana mysql prometheus redis +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install/software# cd .. +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install# ls +centos_install_docker.sh help.md software +centos_run_install_docker_local.sh README.md ubuntu_install_docker.sh +environment run_install_software.sh ubuntu_run_install_docker_local.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install# chmod +x run_install_software.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-install# ./run_install_software.sh +[INFO] 当前磁盘空间信息:总空间 40G,已使用 4.3G,可用 34G,使用率 12% +----------------------------------------------------------- +[HEADER] 选择配置文件: +----------------------------------------------------------- +1. 使用原始配置文件 (推荐,但可能需要从Docker Hub拉取镜像) +2. 使用阿里云镜像配置文件 (国内网络环境推荐) +----------------------------------------------------------- +请选择配置文件 [1/2] (默认: 1): 1 +[INFO] 已选择使用原始配置文件 +----------------------------------------------------------- +[HEADER] 可安装的软件列表: +----------------------------------------------------------- +1. nacos (预计占用空间: 500MB) +2. mysql (预计占用空间: 600MB) +3. phpmyadmin (预计占用空间: 100MB) +4. redis (预计占用空间: 50MB) +5. redis-admin (预计占用空间: 50MB) +6. rabbitmq (预计占用空间: 300MB) +7. elasticsearch (预计占用空间: 500MB) +8. logstash (预计占用空间: 300MB) +9. kibana (预计占用空间: 200MB) +10. xxl-job-admin (预计占用空间: 150MB) +11. prometheus (预计占用空间: 100MB) +12. grafana (预计占用空间: 100MB) +13. ollama (预计占用空间: 200MB) +14. pgvector (预计占用空间: 150MB) +15. pgvector-admin (预计占用空间: 50MB) +----------------------------------------------------------- +请选择要安装的软件(多选,用空格分隔,如:1 3 5): +2 3 +----------------------------------------------------------- +[HEADER] 您选择了以下软件: +- phpmyadmin (预计占用空间: 100MB) +- mysql (预计占用空间: 600MB) +总计预计占用空间: 700MB +----------------------------------------------------------- +----------------------------------------------------------- +[HEADER] MySQL初始化提示: +----------------------------------------------------------- +[INFO] 您选择了安装MySQL,安装完成后可以使用phpmyadmin进行管理 +[INFO] 如果您希望在初始化时创建数据库和表,可以将SQL脚本放在以下目录: + /dev-ops/xfg-dev-tech-docker-install/software/mysql/sql/ +[INFO] 目前该目录已包含以下SQL文件: + - init.sql + - nacos.sql + - xxl_job.sql +[INFO] 您可以添加自己的SQL文件到该目录,它们将在MySQL初始化时自动执行 +----------------------------------------------------------- +确认安装以上软件?(y/n): y +[INFO] 正在检查服务依赖关系... +[INFO] 开始安装选中的软件... +[+] Running 34/34 + ✔ phpmyadmin 21 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿] 0B/0B Pulled 499.9s + ✔ 858a356e9dad Pull complete 1.7s + ✔ Container phpmyadmin Started 5.8s +[INFO] 软件安装完成! +----------------------------------------------------------- +[HEADER] 已安装的软件及访问信息: +- phpmyadmin: 访问地址:http://服务器IP:8899 (连接到MySQL) +- mysql: 账号:root 密码:123456 端口:13306 +[INFO] MySQL已安装成功,您可以使用phpmyadmin进行管理 +[INFO] 初始化SQL脚本已自动执行,包括: + - init.sql + - nacos.sql + - xxl_job.sql +----------------------------------------------------------- +[INFO] 如需修改账号密码,请编辑 /dev-ops/xfg-dev-tech-docker-install/software/docker-compose-software.yml 文件 +[INFO] 修改后,重新运行此脚本即可更新配置 +``` + +- 执行提供好的脚本,即可完成各项软件的安装。其他的软件,也可以搜索教程安装。 + +### 3. 应用配置 + +
+ +
+ +- 云服务器环境 Docker + Portainer(管理docker的软件) 以及软件安装完成后,你就可以在你的 IntelliJ IDEA 的程序里配置对应的连接信息了。这样你的本地就可以连接到云服务器上的 Docker 里安装的 MySQL 了。 +- Docker 是非常常用的软件,也是非常好用的软件,对于开发学习部署和卸载各类软件都非常方便,不会破坏本地环境。 + +## 四、项目部署 + +Docker 部署项目需要的是程序的镜像,镜像的目的是把一个程序运行所需的各类环境都给打包进行,等运行部署的时候直接执行即可。 + +所以,这里我们会通过各种方式来完成镜像的构建和部署,包括; +| 方案 | 复杂度 | +| ------------------------------------------------------------ | ------ | +| 本地打包Jar + 云上构建镜像。 | ⭐️⭐️ | +| 云上打包Jar + 云上构建镜像。 | ⭐️⭐️⭐️ | +| 本地打包Jar + 本地构建镜像 + 上传到阿里云 Docker 镜像仓库 + 云上拉取构建 | ⭐️⭐️⭐️⭐️ | + +### 1. 文件配置 + +#### 1.1 dockerfile + +```java +# 基础镜像 openjdk:8-jre-slim +FROM registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:8-jre-slim + +# 作者 +MAINTAINER xiaofuge + +# 配置 +ENV PARAMS="" + +# 时区 +ENV TZ=PRC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# 添加应用 +ADD target/xfg-dev-tech-docker-deploy-app.jar /xfg-dev-tech-docker-deploy-app.jar + +ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /xfg-dev-tech-docker-deploy-app.jar $PARAMS"] +``` + +- 这里配置了构建镜像的地址信息。 + +#### 1.2 build.sh + +```java +# 普通镜像构建,随系统版本构建 amd/arm +docker build -t system/xfg-dev-tech-docker-deploy-app:1.0-SNAPSHOT -f ./Dockerfile . + +# 兼容 amd、arm 构建镜像 +# docker buildx build --load --platform liunx/amd64,linux/arm64 -t xiaofuge/xfg-frame-archetype-app:1.0 -f ./Dockerfile . --push +``` + +- 配置了构建镜像的名称。 + +### 2. 构建镜像 + +#### 2.1 本地打包Jar + 云上构建镜像 + +##### 2.1.1 打包jar + +
+ +
+ +- 在本地 install 构建项目 jar,target 目录下可以只保留项目的 jar 其他的删掉就可以。 + +##### 2.1.2 上传jar + +
+ +
+ +- 把本地构建的jar,通过 sftp 把整个 xfg-dev-tech-deploy-app 上传到云服务器。 + +##### 2.1.3 构建镜像 + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops# ls +xfg-dev-tech-docker-deploy-app xfg-dev-tech-docker-install +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops# cd xfg-dev-tech-docker-deploy-app/ +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy-app# ls +build.sh Dockerfile pom.xml src target +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy-app# chmod +x build.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy-app# ./build.sh +[+] Building 64.5s (8/8) FINISHED docker:default + => [internal] load build definition from Dockerfile 0.0s + => => transferring dockerfile: 484B 0.0s + => WARN: MaintainerDeprecated: Maintainer instruction is deprecated in favor of using label (line 5 0.0s + => [internal] load metadata for registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:8-jre-slim 0.7s + => [internal] load .dockerignore 0.0s + => => transferring context: 2B 0.0s +... + => => unpacking to docker.io/system/xfg-dev-tech-docker-deploy-app:1.0-SNAPSHOT 0.1s + + 1 warning found (use docker --debug to expand): + - MaintainerDeprecated: Maintainer instruction is deprecated in favor of using label (line 5) +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy-app# docker images + i Info → U In Use +IMAGE ID DISK USAGE CONTENT SIZE EXTRA +mysql:8.0.32 f496c25da703 728MB 157MB U +phpmyadmin:5.2.1 6e75aa8f767c 798MB 193MB U +registry.cn-hangzhou.aliyuncs.com/xfg-studio/portainer:latest + 11384457b374 374MB 86.8MB U +system/xfg-dev-tech-docker-deploy-app:1.0-SNAPSHOT 14e1cd401829 332MB 98.2MB +``` + +- 通过这样的方式就把整个镜像构建出来,`docker images` 可以查看镜像列表,system/xfg-dev-tech-docker-deploy-app:1.0-SNAPSHOT 就是咱们构建的镜像。 + +#### 2.2 云上打包Jar + 云上构建镜像 + +##### 2.2.1 拉取项目 + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy# ls +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy# git clone https://github.com/fuzhengwei/xfg-dev-tech-docker-deploy.git +Cloning into 'xfg-dev-tech-docker-deploy'... +remote: Enumerating objects: 176, done. +remote: Counting objects: 100% (176/176), done. +remote: Compressing objects: 100% (87/87), done. +remote: Total 176 (delta 23), reused 175 (delta 22), pack-reused 0 (from 0) +Receiving objects: 100% (176/176), 75.84 KiB | 40.00 KiB/s, done. +Resolving deltas: 100% (23/23), done. +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy# ls +xfg-dev-tech-docker-deploy +``` + +- 通过 git clone 命令,拉取项目。 + +##### 2.2.2 打包jar + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy# ls +docs pom.xml README.md xfg-dev-tech-docker-deploy-app +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy# mvn clean install +[INFO] Scanning for projects... +Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/2.7.12/spring-boot-starter-parent-2.7.12.pom +... +[INFO] ------------------------------------------------------------------------ +[INFO] Reactor Summary for xfg-dev-tech-docker-deploy 1.0-SNAPSHOT: +[INFO] +[INFO] xfg-dev-tech-docker-deploy ......................... SUCCESS [ 0.219 s] +[INFO] xfg-dev-tech-docker-deploy-app ..................... SUCCESS [05:29 min] +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 05:29 min +[INFO] Finished at: 2025-12-21T10:56:57+08:00 +[INFO] ------------------------------------------------------------------------ +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy# ls +docs pom.xml README.md xfg-dev-tech-docker-deploy-app +``` + +- 进入到项目,通过 `mvn clean install` 打包项目 jar + +##### 2.2.3 构建镜像 + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy-app# ls +build.sh Dockerfile pom.xml src target +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy-app# chmod +x build.sh +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy-app# cat build.sh +# 普通镜像构建,随系统版本构建 amd/arm +docker build -t system/xfg-dev-tech-docker-deploy-app:1.1-SNAPSHOT -f ./Dockerfile . + +# 兼容 amd、arm 构建镜像 +# docker buildx build --load --platform liunx/amd64,linux/arm64 -t xiaofuge/xfg-frame-archetype-app:1.0 -f ./Dockerfile . --pushroot@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy-app# ./build.sh +[+] Building 1.3s (8/8) FINISHED docker:default + => [internal] load build definition from Dockerfile 0.0s + => => transferring dockerfile: 484B 0.0s + => WARN: MaintainerDeprecated: Maintainer instruction is deprecated in favor of using label (line 5) 0.0s + => [internal] load metadata for registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:8-jre-slim 0.4s + => [internal] load .dockerignore 0.0s + => => transferring context: 2B 0.0s + => [1/3] FROM registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:8-jre-slim@sha256:285c61a1e5e6b7b3709729b69558670148c5fdc6eb7104fae 0.0s + => => resolve registry.cn-hangzhou.aliyuncs.com/xfg-studio/openjdk:8-jre-slim@sha256:285c61a1e5e6b7b3709729b69558670148c5fdc6eb7104fae 0.0s + => [internal] load build context 0.1s + => => transferring context: 26.15MB 0.1s + => CACHED [2/3] RUN ln -snf /usr/share/zoneinfo/PRC /etc/localtime && echo PRC > /etc/timezone 0.0s + => [3/3] ADD target/xfg-dev-tech-docker-deploy-app.jar /xfg-dev-tech-docker-deploy-app.jar 0.0s + => exporting to image 0.7s + => => exporting layers 0.6s + => => exporting manifest sha256:14e4dd6822541669e1ac4c0c6957c74be06f401556ffca134fa42133df9dacb1 0.0s + => => exporting config sha256:f4581c52995e78750c432c2bd3c7bcaa393398ee7d7d62c331ccb69c2825a724 0.0s + => => exporting attestation manifest sha256:5c7a6af7000015669c2e5dccd10d74b68357359994f42a5e211883ab2b0d7335 0.0s + => => exporting manifest list sha256:335d04f3bc894e32e70b2c126a6e0abccd4c6136d44e6b9c1f0060e492816419 0.0s + => => naming to docker.io/system/xfg-dev-tech-docker-deploy-app:1.1-SNAPSHOT 0.0s + => => unpacking to docker.io/system/xfg-dev-tech-docker-deploy-app:1.1-SNAPSHOT 0.1s + + 1 warning found (use docker --debug to expand): + - MaintainerDeprecated: Maintainer instruction is deprecated in favor of using label (line 5) +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy/xfg-dev-tech-docker-deploy-app# docker images + i Info → U In Use +IMAGE ID DISK USAGE CONTENT SIZE EXTRA +mysql:8.0.32 f496c25da703 728MB 157MB U +phpmyadmin:5.2.1 6e75aa8f767c 798MB 193MB U +registry.cn-hangzhou.aliyuncs.com/xfg-studio/portainer:latest 11384457b374 374MB 86.8MB U +system/xfg-dev-tech-docker-deploy-app:1.0-SNAPSHOT 14e1cd401829 332MB 98.2MB +system/xfg-dev-tech-docker-deploy-app:1.1-SNAPSHOT 335d04f3bc89 332MB 98.2MB +``` + +- 这样,就在云服务器把项目镜像构建出来了。 + +#### 2.3 阿里云 DockerHub + +##### 2.3.1 账号申请 + +官网:[https://cr.console.aliyun.com/cn-hangzhou/instance/dashboard](https://cr.console.aliyun.com/cn-hangzhou/instance/dashboard) + +
+ +
+ +- 如图所示,申请账号,创建个人的命名空间。步骤3、4可选。 +- 之后你这里可以手动操作上传镜像,也可以走下面的流程自动操作。 + +##### 2.3.2 上传镜像 + +
+ +
+ +- 在项目下,有一个 push.sh 文件,可以把镜像推送到阿里云 DockerHub 镜像库。这样操作的目的是有个统一地方来维护镜像,各个云服务器都可以拉取部署了。 +- 推送后,可以到仓库查看镜像 [https://cr.console.aliyun.com/cn-hangzhou/instance/repositories](https://cr.console.aliyun.com/cn-hangzhou/instance/repositories) + +### 3. 项目部署 + +#### 3.1 脚本说明 + +
+ +
+ +- 这里的镜像名称,要修改成你的镜像名称。 + +#### 3.2 上传脚本 + +
+ +
+ +- 上传项目下的部署脚本到云服务器,dev-ops 创建个文件夹。 + +#### 3.3 执行部署 + +```java +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/docker-compose# ls +app docker-compose-app.yml docker-compose-environment-aliyun.yml docker-compose-environment.yml mysql +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/docker-compose# docker-compose -f docker-compose-app.yml up -d +[+] Running 6/6 + ✔ xfg-dev-tech-docker-deploy 5 layers [⣿⣿⣿⣿⣿] 0B/0B Pulled 205.7s + ✔ f355a2e0723d Pull complete 34.0s + ✔ f7ec5a41d630 Pull complete 65.9s + ✔ b747e468795b Pull complete 203.7s + ✔ 88f5ee065d77 Pull complete 0.3s + ✔ faf4c47c8c61 Pull complete 36.0s +[+] Running 1/2 + ⠋ Network docker-compose_my-network Created 2.0s + ✔ Container xfg-dev-tech-docker-deploy Started 1.9s +root@iv-ydw2iok0lcbw80bxaha0:/dev-ops/docker-compose# +``` + +
+ +
+ +- 部署后,就可以访问服务看最终的效果了!** diff --git a/docs/md/road-map/docker-idea.md b/docs/md/road-map/docker-idea.md new file mode 100644 index 000000000..69892f4e4 --- /dev/null +++ b/docs/md/road-map/docker-idea.md @@ -0,0 +1,169 @@ +--- +title: Docker IntelliJ IDEA +lock: need +--- + +# Docker WEB IntelliJ IDEA —— 随时随地打开浏览器就能写代码! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +我遇到过不少粉丝伙伴因为自己是老 Windows 电脑,部署项目费劲的问题。因为本身 Java 开发完成以后就是部署到 Linux 服务器的,而 Windows 压根不是 Linux 系统。所以不是装虚拟机,就是装 Docker 来模拟这些环境,但很多时候都差强人意,尤其是配置低的 Windows 又跑不动,这咋办! + +
+ +
+ +**死鬼,我就知道你有很多办法!** + +嘿嘿,是的,小傅哥又给你带来一个新方法:”在 Linux 安装一个 IntelliJ IDEA!“ 并且可以在线执行代码、构建项目、打包程序。这不美滋滋,尤其是对有学习项目诉求的伙伴,不就可以随时随地学习自己的项目了吗! + +>好啦,接下来小傅哥就带着你安装下。文末提供了本次安装的相关脚本。 + +## 一、前置说明 + +- 一台云服务器,最低 2c4g [https://618.gaga.plus](https://618.gaga.plus ) —— 价格比较实惠。 +- 安装 Docker、Docker Compose 教程:[https://bugstack.cn/md/road-map/docker.html](https://bugstack.cn/md/road-map/docker.html) + +```java +sudo mkdir -p /etc/docker +sudo tee /etc/docker/daemon.json <<-'EOF' +{ + "registry-mirrors": ["https://dc.j8.work", "https://docker.1panel.live"] +} +EOF +sudo systemctl daemon-reload +sudo systemctl restart docker +``` + +- 如果拉取镜像失败,可以配置下以上地址。 + +## 二、安装脚本 + +
+ +
+ +- 地址:[https://gitcode.net/KnowledgePlanet/xfg-dev-tech-docker-idea](https://gitcode.net/KnowledgePlanet/xfg-dev-tech-docker-idea) +- 说明:小傅哥这里为你提供了搭建 Docker IDEA 的操作脚本,并配有 JDK、Maven,这样可以更加方便我们构建项目。 + +## 三、执行安装 + +### 1. 上传脚本 + +
+ +
+ +- 下载项目后,把本地的配置文件传到云服务器端。注意是 root 目录下。也就是 `~` 这里。 + +### 2. 解压文件 + +#### 2.1 jdk + +
+ +
+ +```java +[root@lavm-aqhgp9nber java]# tar -zxvf jdk-8u202-linux-x64.tar.gz +``` + +- 这个解压后是为了映射到 Docker IntelliJ IDEA 下的。 + +#### 2.2 maven + +
+ +
+ +```java +[root@lavm-aqhgp9nber maven]# unzip apache-maven-3.8.8.zip +``` + +- 可选不非得加压,因为我们可以直接把 `maven .m2` 下的 `settings.xml` 映射到 Docker IntelliJ IDEA 下。 + +
+ +
+ +```java +[root@lavm-aqhgp9nber maven]# ls -a +. .. apache-maven-3.8.8.zip install-maven.sh .m2 remove-maven.sh +``` + +### 3. 授权文件 + +
+ +
+ +```java +chmod -R 777 projector-user/ +``` + +- 给整个文件夹包括文件夹下的其他文件一起授权。 +- 授权后检查当前文件夹 `projector-user` 和文件夹以下的 java、maven 文件夹。 + +### 4. 执行安装 + +```java +# 命令执行 docker-compose -f docker-compose.yml up -d +# 以下这些都可以做 WEB IDEA 安装。在下面替换就可以。 +# docker pull registry.jetbrains.team/p/prj/containers/projector-clion +# docker pull registry.jetbrains.team/p/prj/containers/projector-datagrip +# docker pull registry.jetbrains.team/p/prj/containers/projector-goland +# docker pull registry.jetbrains.team/p/prj/containers/projector-idea-c +# docker pull registry.jetbrains.team/p/prj/containers/projector-idea-u +# docker pull registry.jetbrains.team/p/prj/containers/projector-phpstorm +# docker pull registry.jetbrains.team/p/prj/containers/projector-pycharm-c +# docker pull registry.jetbrains.team/p/prj/containers/projector-pycharm-p + +version: '3.9' +services: + intellij-idea: + image: registry.jetbrains.team/p/prj/containers/projector-idea-c + container_name: intellij-idea + ports: + - "8887:8887" + volumes: + - ~/projector-user:/home/projector-user + - ~/projector-user/maven/.m2/settings.xml:/home/projector-user/.m2/settings.xml + tty: true + stdin_open: true + restart: unless-stopped # 这将确保容器在失败时自动重启 +``` + +
+ +
+ +- 执行脚本 `docker-compose -f docker-compose.yml up -d` +- 安装完成后记得在云服务器开放端口 `8887` + +### 5. 测试项目 + +
+ +
+ +- 通过 `git clone` 检出项目地址 github/gitee/gitcode ,这样在projector-user 下就可以看到你的项目了。 +- 这个是用于 WEB IDEA 打开的项目,当然也可以在 WEB IDEA 创建信息项目。 + +## 四、访问测试 + +地址:[http://117.72.37.243:8887/](http://117.72.37.243:8887/) + +
+ +
+ +- 接下来你就可以在网页端,操作自己的项目了,想怎么玩就怎么玩,和本地效果是一样的。 +- 这对于新人伙伴学习编程太有用了,直接在云服务器都能打包部署了。 + diff --git a/docs/md/road-map/docker-install.md b/docs/md/road-map/docker-install.md new file mode 100644 index 000000000..86979b0c8 --- /dev/null +++ b/docs/md/road-map/docker-install.md @@ -0,0 +1,350 @@ +--- +title: Docker 环境配置(一键安装) +lock: need +--- + +# Docker 环境配置(一键安装) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +说实话,做项目不上线,等于吃面不配蒜🧄,效果少一半!面试官也说:“所有做Java编程项目,没有上线云服务器的,一律当玩具看!” 是呀,做完项目不上线,是不你做的项目没法运行,是个小卡拉米练手的?🤔 那怎么办? + +其实,上线云服务器非常非常简单,而且云服务器价格也非常非常便宜!趁着618活动月,**28块钱**,都能买一年的云服务器☁️,干嘛不上车! + +
+ +
+ +**啥是云服务器?** + +云服务器,就等同于自己的另外一个电脑💻,在另外一台电脑部署 redis、mysql、mq等,本地电脑连接过去使用。尤其是 Windows 电脑用户,真心建议搞个云服务器,否则你会浪费非常多的时间这套 Windows 适配问题。 + +
+ +
+ +这样有了云服务器,就可以不用嚯嚯本地电脑了,安装了卸,卸了安装,把自己本机电脑环境弄的乱码起糟,全是费时费力的事。有这精力,不如用一台云服务器部署环境,开发完成项目后,再上线云服务器。既节省本地电脑资源,又锻炼了云服务器操作,起步一举两得! + +
+ +
+ +不过,放心!别担心你不会用云服务器,因为小傅哥已经给你准备了一件安装云服务器环境的脚本,和各类部署环境和构建项目的视频。**即使是小卡拉米,也能跟着学习下来。** + +> 🧧小傅哥还提供了非常多的编程实战项目,包括;业务的、组件的、AI的、源码的、轮子的,可以关注公众号「bugstack虫洞栈」回复「星球」加入。 + +## 一、优惠云服务器地址 + +
+ +
+ +- 购买地址:[https://618.gaga.plus](https://618.gaga.plus) +- 购买地址:[https://618.gaga.plus](https://618.gaga.plus) +- 购买地址:[https://618.gaga.plus](https://618.gaga.plus) + +**我适合买哪个服务器?** + +- 2c2g 1年,28¥,可部署一套 docker、mysql、redis、SpringBoot 单体项目,用于替代本地电脑的环境部署。 +- 2c4g 1年(非常推荐3年),109¥,可部署一套 docker、mysql、redis、rabbitmq、xxl-job、SpringBoot 分布式微服务项目。 +- 2c8g 1年,328¥,适合部署小傅哥星球社群[大部分项目](https://bugstack.cn/md/zsxq/material/student-learn-advanced.html),可以完成多个微服务项目部署。 + +注意📢:购买选择系统时,推荐系统镜像,**centos 7.9** + +>如果自己账号不是新人身份,可以自己注册个新账号,用家里人JD扫码认证一下即可。 + +🎁 礼物赠送,购买2c4g 3年的,赠送Joy公仔,邮寄到家!购买后,联系小傅哥(微信:fustack) + +## 二、一键部署脚本 + +小傅哥,这里为你准备一键安装 Docker 环境的脚本文件,你可以非常省心的完成 Docker 部署。使用方式如下。 + +
+ +
+ +- **地址**: +- **地址**: + +本文档介绍如何执行项目中的各个脚本,包括权限设置和执行步骤。 + +### 1. 上传脚本&设置权限 + +
+ +
+ +- ssh 工具,推荐 termius。[https://bugstack.cn/md/road-map/tool.html](https://bugstack.cn/md/road-map/tool.html) 免费的就够用,带有 sftp 工具。 +- 左侧是云服务器空间 root 下,右侧是本地环境。可以把整个文件夹全部拖到云服务器。 +- 另外,也可以在云服务器执行 `sudo yum install git` 安装 Git,通过 git clone 拉取脚本。 + +在执行任何脚本之前,需要先为脚本文件添加可执行权限: + +``` +# 为所有脚本添加可执行权限 +chmod +x environment/jdk/install-java.sh +chmod +x environment/jdk/remove-java.sh +chmod +x run_install_docker_local.sh +chmod +x run_install_software.sh +chmod +x install-maven.sh +chmod +x remove-maven.sh + +``` +或者一次性为所有脚本添加权限: + +``` +find . -name "*.sh" -type f -exec chmod +x {} \; +``` + +### 2. JDK 安装脚本 + +#### 2.1 安装 JDK + +脚本位置: environment/jdk/install-java.sh + +功能: 支持安装 JDK 8 和 JDK 17 + +执行方式: + +``` +# 交互式安装(推荐) +sudo ./environment/jdk/install-java.sh + +# 指定版本安装 +sudo ./environment/jdk/install-java.sh -v 8 # 安装 JDK 8 +sudo ./environment/jdk/install-java.sh -v 17 # 安装 JDK 17 + +# 强制安装(覆盖已有安装) +sudo ./environment/jdk/install-java.sh -f -v 8 + +# 静默安装 +sudo ./environment/jdk/install-java.sh -q -v 8 + +# 自定义安装目录 +sudo ./environment/jdk/install-java.sh -d /opt/java -v 8 +``` +注意事项: + +- 需要 root 权限执行 +- 脚本会提示手动下载 JDK 包到 /dev-ops/java 目录 +- 支持的版本:JDK 8 (1.8.0_202) 和 JDK 17 (17.0.14) +- 安装完成后环境变量会自动配置 + +#### 2.2 卸载 JDK + +脚本位置: environment/jdk/remove-java.sh + +功能: 彻底清理 JDK 安装和环境配置 + +执行方式: + +``` +# 交互式删除(推荐) +sudo ./environment/jdk/remove-java.sh + +# 强制删除 +sudo ./environment/jdk/remove-java.sh -f + +# 静默删除 +sudo ./environment/jdk/remove-java.sh -f -q + +# 指定安装目录删除 +sudo ./environment/jdk/remove-java.sh -d /opt/java + +# 删除时不备份配置文件 +sudo ./environment/jdk/remove-java.sh --no-backup +``` +注意事项: + +- 需要 root 权限执行 +- 会自动备份配置文件(除非使用 --no-backup) +- 清理系统和用户级环境变量配置 + +### 2.3 Maven 安装脚本 + +#### 2.3.1 安装 Maven + +脚本位置:`environment/maven/install-maven.sh` + +功能:自动安装 Apache Maven 3.8.8 + +执行方式: + +```bash +# 交互式安装(推荐) +sudo ./environment/maven/install-maven.sh + +# 自定义安装目录 +sudo ./environment/maven/install-maven.sh -d /opt/maven + +# 使用本地Maven包 +sudo ./environment/maven/install-maven.sh -p /path/to/apache-maven-3.8.8.zip + +# 强制安装(覆盖已有安装) +sudo ./environment/maven/install-maven.sh -f + +# 静默安装 +sudo ./environment/maven/install-maven.sh -q + +# 强制静默安装 +sudo ./environment/maven/install-maven.sh -f -q +``` + +### 3. Docker 安装脚本 + +脚本位置: run_install_docker_local.sh + +功能: 使用本地的 install_docker.sh 脚本安装 Docker + +执行方式: + +``` +# 执行 Docker 安装 +./run_install_docker_local.sh +``` +注意事项: + +- 脚本会自动检查 install_docker.sh 文件是否存在 +- 如果需要 root 权限会自动请求 +- 安装完成后会询问是否安装 Portainer 容器管理界面 +- Portainer 访问地址: http://服务器IP:9000 + +### 4. 软件安装脚本 + +脚本位置: run_install_software.sh + +功能: 使用 Docker Compose 安装各种开发软件 + +执行方式: + +``` +# 执行软件安装 +sudo ./run_install_software.sh +``` + +支持的软件: + +- nacos - 服务注册与发现 +- mysql - 数据库 +- phpmyadmin - MySQL 管理界面 +- redis - 缓存数据库 +- redis-admin - Redis 管理界面 +- rabbitmq - 消息队列 +- elasticsearch - 搜索引擎 +- logstash - 日志处理 +- kibana - 日志分析界面 +- xxl-job-admin - 任务调度 +- prometheus - 监控系统 +- grafana - 监控面板 +- ollama - AI 模型服务 +- pgvector - 向量数据库 +- pgvector-admin - 向量数据库管理界面 + 注意事项: + +- 需要 root 权限执行 +- 需要先安装 Docker 和 docker-compose +- 脚本会检查磁盘空间并显示预计占用 +- 支持选择原始配置或阿里云镜像配置 +- 可以多选软件进行批量安装 + +### 5. 常见问题 + +#### 5.1 权限问题 + +如果遇到权限拒绝错误: + +``` +# 确保脚本有执行权限 +ls -la *.sh +# 如果没有 x 权限,重新添加 +chmod +x script_name.sh +``` + +#### 5.2 环境变量生效 + +JDK 安装后,环境变量在当前会话中已生效,新开终端需要: + +``` +# 重新加载配置 +source /etc/profile +# 或者重新登录 +``` + +#### 5.3 Docker 相关 + +确保 Docker 服务正在运行: + +``` +# 检查 Docker 状态 +sudo systemctl status docker +# 启动 Docker 服务 +sudo systemctl start docker +``` + +### 6. 执行顺序建议 + +1. 首先安装 JDK (如果需要): + + ``` + sudo ./environment/jdk/install-java.sh -v 8 + ``` + +2. 然后安装 Docker : + + ``` + ./run_install_docker_local.sh + ``` + +3. 然后安装 Docker : + + ``` + ./install-maven.sh + ``` + +4. 最后安装开发软件 : + + ``` + sudo ./run_install_software.sh + ``` + 按照以上步骤,您就可以成功执行所有脚本并搭建完整的开发环境。 + +### 7. 安装演示 + +#### 7.1 docker 安装 + +
+ +
+ +```java +[root@xiaofuge ~]# ls +run_install_docker_local.sh +[root@xiaofuge ~]# chmod +x run_install_docker_local.sh +[root@xiaofuge ~]# ./run_install_docker_local.sh +``` + +- 执行完 `./run_install_docker_local.sh ` 全程会自动化安装,如果已经过 docker 会提示是否卸载。使用起来非常方便。 + +#### 7.2 软件安装 + +```java +[root@xiaofuge dev-ops]# ls +install_docker.sh README.md run_install_docker_local.sh run_install_docker.sh run_install_software.sh software +[root@xiaofuge dev-ops]# chmod +x run_install_software.sh +[root@xiaofuge dev-ops]# ls +install_docker.sh README.md run_install_docker_local.sh run_install_docker.sh run_install_software.sh software +``` + +
+ +
+ +- 安装时候可以选择需要安装的软件,重复安装也会提示卸载。 + diff --git a/docs/md/road-map/docker-what.md b/docs/md/road-map/docker-what.md new file mode 100644 index 000000000..24d2d8ea9 --- /dev/null +++ b/docs/md/road-map/docker-what.md @@ -0,0 +1,76 @@ +--- +title: Docker 是什么? +lock: need +--- + +# Docker 是什么? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**好用,真的好用!** 自从使用 Docker 后,我可以非常简单的`安装`、`使用`、`卸载`各类所需的软件,如;MySql、Redis、RabbitMQ、XXL-Job、FRP,等一些列开发环境和我自己开发的好的项目进行上线发布。以前一个MySql安装到卸载,可能半天时间都没了。但有 Docker 后,我自己既可以是开发工程师也是可以是软件实施工程师。因为他可以一行脚本即可完成所有的操作,脚本在,环境就在,服务就在。 + +
+ +
+ +**那 Docker 是什么呢?** + +Docker 是什么,其实来自于 Docker 的图标已经说明。Docker 的图标是一条鲸鱼 🐳一样的大船,上面摆放了很多集装箱。你可以把这些集装箱当做是一个个不同的应用程序,虽然不同但可以统一用一套集装箱(脚本命令)承载,并承放在统一一条大船上(环境上)。而且这些集装箱都有自己的编号ID(有自己的IP)互相隔离,不受影响。 + +Docker 是一个用于开发、发布和运行应用程序的开放平台。Docker 可让您将应用程序与基础架构分离,以便快速交付软件。借助 Docker,您可以像管理应用程序一样管理基础架构。通过利用 Docker 的发布、测试和部署代码方法,您可以显著减少编写代码和在生产中运行代码之间的延迟。 + +官网:[https://www.docker.com/](https://www.docker.com/) +文档:[https://docs.docker.com/get-started/docker-overview/](https://docs.docker.com/get-started/docker-overview/) + +## 一、Docker 安装在哪 + +Docker 可以安装在 Windows + wsl2、Mac、Linux,支持 ARM、AMD 架构。它可以通过软件下载安装和执行脚本命令安装,也可以通过云服务器提供的镜像直接购买云服务器时选择使用。我们可以看下 Docker 和虚拟机安装在操作系统上的关系来了解 Docker 的安装位置。 + +
+ +
+ +与虚拟机的安装使用相比,Docker 会把应用所需的依赖、函数库、甚至其他的软件应用可以一起打包成一个镜像,这样在应用程序运行时,就可以直接调用本地函数库,然后和 Linux 内核进行通信。有了这样的设计,你也就不需要关心每一个应用所需的环境都是啥了,也不用为每一个应用安装各类环境到 Linux 或者虚拟机了。也就做到了跨系统的运行。这有点类似于 Java 的 JVM 虚拟机。 + +- 本地安装:[https://www.docker.com/](https://www.docker.com/) - 下载 Mac、Windows 你需要的版本进行安装。 +- 云服务器:[https://bugstack.cn/md/road-map/docker.html](https://bugstack.cn/md/road-map/docker.html) - centos 7.9 其他的也都类似操作即可。 + +>安装完成后都可以通过命令操作,安装、部署、卸载软件。文末提供了教程链接。 + +## 二、Docker 怎么工作 + +Docker 为了屏蔽软件使用差异,会统一对这些软件进行镜像打包,把一个软件所需的各类环境都打包到镜像中。我们在使用的时候,就是使用各类平台提供好的软件镜像,进行服务部署。同样的我们也可以作为镜像提供方,把我们的应用程序 SpringBoot、React、VUE 等,打包成镜像,让我们在其他地方,如云服务器进行部署。或者提供给全网的人员,进行部署使用。 + +
+ +
+ +如图,这是一整套的,本地拉取镜像、部署环境、开发代码、发布镜像,再到云服务器拉取镜像、部署项目和环境的过程。在这个过程中,我们也可以借助于如 Github Action 完成镜像的构建和发布,还可以在云服务器上直接构建镜像,减少了拉取拉取的过程。 + +> 你可以理解为,Docker 就是一个中心和一个客户端,中心管理镜像,客户端拉取使用或者构建发布镜像。 + +## 三、Docker 命令说明 + +Docker 的操作是通过脚本命令配置和执行完成使用,不过不用害怕命令😱,命令是最简单、直接、可靠的方式。当你习惯命令以后,你会很喜欢它,并且知道只要执行这个命令就一定会有结果,而不像被软件包装后,不确定是软件的问题还是命令的问题。 + +Docker 安装软件分为直接使用和通过 Docker Compose 脚本。直接使用如;`docker run -d --restart=always --name portainer -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer` 这条命令会自动拉取和执行脚本。不过对于更大的项目部署,Docker Compose 脚本更好用。 + +
+ +
+ +- 如图,是整个 Docker Compose 在配置一套执行脚本后,各个脚本的核心用途。当然 Docker Compose 还有不少的命令,比如容量的限制、端口的处理、默认的命令等,都是可以操作的,凡是你想的合理的,它都可以支持。 +- 特别注意,安装到云服务器的docker部署的应用,外部访问需要走公网IP:Port端口,之后这个端口要在安全组打开。这样就类似于你再本地自己的电脑发,访问另外一台电脑上的服务了。 +- 如果在使用中遇到其他命令不理解的,可以让 [openai](https://openai.itedus.cn) 解释。 + +## 四、Docker 实操教程 + +此外,为了大家更好的使用 Docker 搭建各类环境,小傅哥为大家准备好了文档和视频。可以放心食用。 + +- 文档:[https://bugstack.cn/md/road-map/docker.html](https://bugstack.cn/md/road-map/docker.html) - 这是一个系列,你可以从左侧目录选择阅读。 +- 视频:[https://www.bilibili.com/video/BV1xw411W7xf](https://www.bilibili.com/video/BV1xw411W7xf) - 对应一整套的视频,手把手操作。 \ No newline at end of file diff --git a/docs/md/road-map/docker.md b/docs/md/road-map/docker.md new file mode 100644 index 000000000..3fb609b15 --- /dev/null +++ b/docs/md/road-map/docker.md @@ -0,0 +1,386 @@ +--- +title: Docker 环境配置(手动安装) +lock: need +--- + +# Docker 环境配置(手动安装) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +- 官网:[https://www.docker.com](https://www.docker.com/) - Mac、Windows、Linux +- 介绍:Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Mac、Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。总之它加快构建、共享和运行现代应用程序的速度。 + +## 一、基础安装 + +### 1. 查看系统的内核版本 + +```shell +uname -r +``` + +```java +4.18.0-80.11.2.el8_0.x86_64 +``` + +- `uname -r` +- x86 64位系统,如果是32位是不能安装 docker 的 + +### 2. yum 更新到最新版本 + +```shell +sudo yum update +``` + + +```java +Last metadata expiration check: 1:15:10 ago on Sat 27 Nov 2021 04:22:53 PM CST. +Dependencies resolved. +Nothing to do. +Complete! +``` + +- `sudo yum update` +- 看到显示 `Complete` 就代表完成了,整个过程需要 5-10 分钟左右 + +注意:可能会更新失败,那就操作以下指令 + +~~~~bash +## 建议备份当前的 yum 源配置,以防万一需要恢复 +sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak + +## 从阿里云下载 CentOS 7 的 yum 源配置文件并替换现有的配置 +sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo + +## 清理旧的缓存并生成新的缓存 +sudo yum clean all +sudo yum makecache + +## 再次更新 +sudo yum update +~~~~ + + + +### 3. 安装Docker所需的依赖包 + +```java +[root@CodeGuide ~]# sudo yum install -y yum-utils device-mapper-persistent-data lvm2 +Last metadata expiration check: 1:16:16 ago on Sat 27 Nov 2021 04:22:53 PM CST. +Package yum-utils-4.0.21-3.el8.noarch is already installed. +Package device-mapper-persistent-data-0.9.0-4.el8.x86_64 is already installed. +Package lvm2-8:2.03.12-10.el8.x86_64 is already installed. +Dependencies resolved. +Nothing to +``` + +- `sudo yum install -y yum-utils device-mapper-persistent-data lvm2` +- 看到显示 `Complete` 就代表完成了,整个过程需要 1-3 分钟左右 + +### 4. 设置Docker的yum的源 + +```shell +sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo +``` + +```java +Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo +``` + +- sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo +- 鉴于国内网络问题,强烈建议使用国内源。以下是阿里云的软件源。**如果是海外如AWS云就不要设置yum源** + +```bash +sudo yum-config-manager \ + --add-repo \ + https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo +sudo sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo +``` + +### 5. 查看仓库所有Docker版本 + +```java +[root@CodeGuide ~]# yum list docker-ce --showduplicates | sort -r +Installed Packages +docker-ce.x86_64 3:20.10.9-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.8-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.7-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.6-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.5-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.4-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.3-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.2-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.1-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.11-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.11-3.el8 @docker-ce-stable +docker-ce.x86_64 3:20.10.10-3.el8 docker-ce-stable +docker-ce.x86_64 3:20.10.0-3.el8 docker-ce-stable +docker-ce.x86_64 3:19.03.15-3.el8 docker-ce-stable +docker-ce.x86_64 3:19.03.14-3.el8 docker-ce-stable +docker-ce.x86_64 3:19.03.13-3.el8 docker-ce-stable +Docker CE Stable - x86_64 7.1 kB/s | 3.5 kB 00:00 +Available Packages +``` + +- yum list docker-ce --showduplicates | sort -r +- 这里可以看到你能安装的最新版本 + +### 6. 安装Docker + +```java +[root@CodeGuide ~]# sudo yum install docker-ce +[root@CodeGuide ~]# 推荐;sudo yum install -y docker-ce-25.0.5 docker-ce-cli-25.0.5 containerd.io +``` + +- 安装默认最新版本的 Docker `最新版本可能有坑,最好指定版本安装` + +```java +[root@CodeGuide ~]# sudo yum install +``` + +- 安装指定版本,例如:`yum install -y docker-ce-25.0.5 docker-ce-cli-25.0.5 containerd.io` + +### 7. 安装Docker-Compose + +#### 7.1 正常安装 + +**官网地址** + +```shell +sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +sudo chmod +x /usr/local/bin/docker-compose +``` + +**镜像地址** + +```shell +# 指定路径【推荐】 +sudo curl -L https://gitee.com/fustack/docker-compose/releases/download/v2.24.1/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose +# 设置权限 +sudo chmod +x /usr/local/bin/docker-compose +``` + +#### 7.2 离线安装 + +```shell +# 下载;docker-compose-`uname -s`-`uname -m` 查看版本;https://github.com/docker/compose/releases/tag/v2.18.1 +# 重命名 +mv docker-compose-linux-x86_64 docker-compose +# 加入执行权限 +sudo chmod +x /usr/local/bin/docker-compose +# 查看docker-compose版本 +docker-compose -v +``` + +```java +sudo chmod +x /usr/local/bin/docker-compose +docker-compose -v +Docker Compose version v2.18.1 +``` + +- 安装后就可以使用 compose 命令了;`docker-compose -f environment-docker-compose.yml up -d` + +### 8. 启动Docker并添加开机自启动 + +```java +[root@CodeGuide ~]# sudo systemctl start docker +``` + +- 启动 Docker + +```java +[root@CodeGuide ~]# systemctl enable docker +``` + +- 设置开机启动 Docker + +```java +sudo systemctl restart docker +``` + +- 重启 Docker 命令 + +### 9. 查看 Docker 版本 + +```java +[root@CodeGuide ~]# docker --version +Docker version 20.10.11, build dea9396 +``` + +### 10. 卸载 Docker + +```java +[root@CodeGuide ~]# sudo yum remove docker \ + docker-client \ + docker-client-latest \ + docker-common \ + docker-latest \ + docker-latest-logrotate \ + docker-logrotate \ + docker-selinux \ + docker-engine-selinux \ + docker-engine +``` + +### 11. Docker 常用命令 + +```java +[root@CodeGuide ~]# docker --help #Docker帮助 +[root@CodeGuide ~]# docker --version #查看Docker版本 +[root@CodeGuide ~]# docker search #搜索镜像文件,如:docker search mysql +[root@CodeGuide ~]# docker pull #拉取镜像文件, 如:docker pull mysql +[root@CodeGuide ~]# docker images #查看已经拉取下来的所以镜像文件 +[root@CodeGuide ~]# docker rmi #删除指定镜像文件 +[root@CodeGuide ~]# docker run --name -p 80:8080 -d #发布指定镜像文件 +[root@CodeGuide ~]# docker ps #查看正在运行的所有镜像 +[root@CodeGuide ~]# docker ps -a #查看所有发布的镜像 +[root@CodeGuide ~]# docker rm #删除执行已发布的镜像 +``` + +### 12. 设置国内源 + +1. 【推荐】1panel 提供了镜像源 [https://status.1panel.top/status/docker](https://status.1panel.top/status/docker) - `直接进去就可以找到最新的镜像源` +2. 阿里云提供了镜像源:[https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors](https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors) - 登录后你会获得一个专属的地址。 + +使用以下命令来设置 Docker 国内源:- 或者你可以通过 `vim /etc/docker/daemon.json` 进入修改添加 registry-mirrors 内容后重启 Docker + +```yaml +sudo mkdir -p /etc/docker +sudo tee /etc/docker/daemon.json <<-'EOF' +{ + "registry-mirrors": ["https://***替换为你的地址***.mirror.aliyuncs.com"] +} +EOF +sudo systemctl daemon-reload +sudo systemctl restart docker +``` + +可用镜像检测:[https://status.1panel.top/status/docker](https://status.1panel.top/status/docker) + +#### 12.1 本地Docker软件配置 + +
+ +
+ +```yaml +{ "registry-mirrors" : [ + "https://docker.1panel.live", + "https://dc.j8.work", + "https://docker.m.daocloud.io" + ], + "builder": { + "gc": { + "enabled": true, + "defaultKeepStorage": "20GB" + } + }, + "experimental": false, + "features": { + "buildkit": true + } +} +``` + +这个命令会创建一个 `/etc/docker/daemon.json` 文件,并将国内源的配置写入其中。然后你只需要重启 Docker 服务即可使配置生效,可以通过运行 `sudo systemctl restart docker` 命令来重启 Docker 服务。 + +**解决目前Docker Hub国内无法访问方法** + +自从2023年5月中旬,著名Docker 容器平台:`hub.docker.com` “不知” 何种原因国内均无法正常访问了。 + +这样对国内服务器拉取Docker仓库影响比较大。不过得亏国内有Docker Hub镜像平台。有付费的,有免费的! + +#### 12.2 Linux 配置 Docker 镜像 + +```yaml +sudo mkdir -p /etc/docker +sudo tee /etc/docker/daemon.json <<-'EOF' +{ +"registry-mirrors": [ + "https://docker.1panel.live", + "https://dc.j8.work", + "https://docker.m.daocloud.io", + "https://dockerproxy.com", + "https://docker.mirrors.ustc.edu.cn", + "https://docker.nju.edu.cn" +] +} +EOF +sudo systemctl daemon-reload +sudo systemctl restart docker +``` + +- 参考链接:https://cloud.tencent.com/developer/article/2301228 + +### 13. 远程连接 - 不推荐 + +```shell script +vim /lib/systemd/system/docker.service +``` + +```shell script +[Unit]Description=Docker Application Container Engine +Documentation=http://docs.docker.com +After=network.target +Wants=docker-storage-setup.service +Requires=docker-cleanup.timer + +[Service] +Type=notify +NotifyAccess=main +EnvironmentFile=-/run/containers/registries.conf +EnvironmentFile=-/etc/sysconfig/docker +EnvironmentFile=-/etc/sysconfig/docker-storage +EnvironmentFile=-/etc/sysconfig/docker-network +Environment=GOTRACEBACK=crash +Environment=DOCKER_HTTP_HOST_COMPAT=1 +Environment=PATH=/usr/libexec/docker:/usr/bin:/usr/sbin +ExecStart=/usr/bin/dockerd-current \ + --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current \ + --default-runtime=docker-runc \ + --exec-opt native.cgroupdriver=systemd \ + --userland-proxy-path=/usr/libexec/docker/docker-proxy-current \ + --init-path=/usr/libexec/docker/docker-init-current \ + --seccomp-profile=/etc/docker/seccomp.json \ + -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock \ + $OPTIONS \ + $DOCKER_STORAGE_OPTIONS \ + $DOCKER_NETWORK_OPTIONS \ + $ADD_REGISTRY \ + $BLOCK_REGISTRY \ + $INSECURE_REGISTRY \ + $REGISTRIES +ExecReload=/bin/kill -s HUP $MAINPID +LimitNOFILE=1048576 +LimitNPROC=1048576 +LimitCORE=infinity +TimeoutStartSec=0 +Restart=on-abnormal +KillMode=process + +[Install] +WantedBy=multi-user.target +``` + +- 添加:`-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock \` + +```shell script +# 加载 +systemctl daemon-reload +# 重启 +systemctl restart docker +# 测试 +curl http://127.0.0.1:2375/info +``` + +- 之后你就可以打开 IDEA 的 Services 配置 Docker 了;`tcp://180.76.138.**:2375` 验证连接后就可以使用了。 + +### 14. 卸载Docker + +```shell +/Applications/Docker.app/Contents/MacOS/Docker --uninstall +``` diff --git a/docs/md/road-map/draw.io.md b/docs/md/road-map/draw.io.md new file mode 100644 index 000000000..4ffdac94a --- /dev/null +++ b/docs/md/road-map/draw.io.md @@ -0,0 +1,140 @@ +--- +title: draw.io + ai agent +lock: need +--- + +# draw.io + ai Agent,确实打开新思路 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获! + + + +大家好,我是技术UP主小傅哥。 + +又到年底了,又要必不可少的开始画图做PPT述职啦 😂,述职的好坏和年终奖都有可能挂钩!这对天天写代码大部分的程序员👨🏻‍💻来说是一件非常头疼的事,但好在这次可以使用个**神器**! + +
+ +
+ +**Ai Agent 会结合到各个场景!** + +可以想象,将来我们使用的各种工具,都会很方便的以标准的协议方式结合到 AI Agent 智能体。这就像小傅哥,25年,年初开始做 Ai Agent 时,对一个智能体的执行过程,是需要自己进行分析,设计,实现的(`那时候我就在讲,Agent 也会出标准框架`)。到了年中的时候 Google ADK 除了 0.1.0 最初的版本,年尾发布了 0.4.0,这个框架提供了构建智能体的最基本框架,可以整合 Spring AI、Langchain4j 快速构建智能体。 + +>Spring AI、Langchain4j 提供的是 AI 封装能力,Google ADK 提供的是 Agent 执行能力(并行、循环、串行)的组装和使用。 + +未来(甚至不会太久),各个软件与 Ai Agent 的对接会变得非常容易,你手里用到的各项软件,都可以很方便的添加上智能体的能力。就像,我们现在用到的 `trae.ai`、`Cursor`,在以后 Ai Agent 基础能力成熟后,在结合 `Visual Studio Code` 给出软件对接标准协议,那么任何一个人都可以快速的搭建出一个智能体编码工具。除此之外,其他的软件也都会逐步提供出这样的能力。 + +扩展的思路分析完毕,小傅哥这次先带着大家了解下,draw.io 是怎么与 Ai 结合使用的。 + +>🧧 文末提供了小傅哥所有编程实战项目获取方式,一次加入即可获得19个已完结的和本次新开展的。 + +## 一、关于 draw.io + +draw.io 是一个用于绘制通用图表的 JavaScript 客户端编辑器。 + +官网:[https://www.drawio.com/](https://www.drawio.com/) +代码:[https://github.com/jgraph/drawio](https://github.com/jgraph/drawio) + +
+ +
+ +我所有的这类的绘图,都来自于使用 draw.io 完成([bugstack.cn](https://bugstack.cn) 还有很多绘图)。它可以下载到本地使用,也可以在网页使用。如果你是开发人员,还可以使用它的开发组件引入到自己的程序中进行使用。也正因如此,有了 Ai Agent 能力的结合,一种是通过 MCP,另外一种是直接通过扩展功能,直接在 draw.io 二开上完成。接下来小傅哥会分别介绍这两种方式以及使用。 + +## 二、MCP 方式对接 + +MCP(Model Context Protocol)是用于解决 AI 与服务接口通信的标准模型上下文协议,该协议通过标准化通信方式,使AI应用程序能够访问和使用实时数据、执行操作,从而克服了模型训练数据的局限性。 你可以将MCP想象成AI应用程序的“USB-C接口”,它提供了一个标准化的方法,让AI模型能够连接各种外部资源。 + +**Draw.io Model Context Protocol (MCP) Server**,就是基于此协议实现的对接 Draw.io 的 MCP 服务。我们可以使用这样一套服务,把 AI 与 Draw.io 建立起使用链接。 + +源码:[https://github.com/lgazo/drawio-mcp-server](https://github.com/lgazo/drawio-mcp-server) + +### 1. 插件安装 + +- 谷歌浏览器:[https://chromewebstore.google.com/detail/drawio-mcp-extension/okdbbjbbccdhhfaefmcmekalmmdjjide](https://chromewebstore.google.com/detail/drawio-mcp-extension/okdbbjbbccdhhfaefmcmekalmmdjjide) +- 火狐浏览器:[https://addons.mozilla.org/en-US/firefox/addon/drawio-mcp-extension/](https://addons.mozilla.org/en-US/firefox/addon/drawio-mcp-extension/) + +
+ +
+ +安装完成后,可以看到这样一个小插件(右侧插件文件里可以点击让常驻浏览器),之后后面可以等着使用(继续下面的步骤)。 + +### 2. 配置服务 + +```java +{ + "mcpServers": { + "drawio": { + "command": "npx", + "args": [ + "-y", + "drawio-mcp-server" + ] + } + } +} +``` + +#### 2.1 trae.ai - 也可以其他的 + +
+ +
+ +#### 2.2 spring ai + +
+ +
+ +### 3. 使用服务 + +首先打开:[https://app.diagrams.net/#](https://app.diagrams.net/) + +
+ +
+ +- 对接上之后,你就可以在右侧提问了。之后他就可以实时的出设计图。`受限于MCP服务对接,它不是在 Draw.io 上扩展,会缺少一些交互和判断,画图有时候会有重叠。` +- 不过,这个方式的好处是,你可以结合自己的代码,拖进去,让它直接出架构图、流程图、模型图等。之后在自己做一些调整。 + +## 三、软件方式使用 + +现在 Github 上有很多基于 Draw.io 的二次开发,结合了 Ai 的能力,这里小傅哥找到一个做这块功能比较早的,next-ai-draw-io 来演示给大家。感兴趣的伙伴,还可以结合代码做二次开发。 + +这是一个基于 Next.js 的 Web 应用程序,它将 AI 功能与 draw.io 图表集成在一起。该应用程序允许您通过自然语言命令和 AI 辅助可视化来创建、修改和增强图表。 + +### 1. 代码下载 + +代码:[https://github.com/DayuanJiang/next-ai-draw-io](https://github.com/DayuanJiang/next-ai-draw-io) + +```java +git clone https://github.com/DayuanJiang/next-ai-draw-io.git +``` + +
+ +
+ +下载代码后,注意修改配置文件。 + +### 2. 启动工程 + +
+ +
+ +- 进入 package.json 点击绿色箭头执行启动。 + +### 3. 访问项目 + +
+ +
+ +- 访问 http://localhost:6002 这样你就可以提问来让它绘制图了。这个效果还不错。 \ No newline at end of file diff --git a/docs/md/road-map/dubbo.md b/docs/md/road-map/dubbo.md new file mode 100644 index 000000000..aa011e1f8 --- /dev/null +++ b/docs/md/road-map/dubbo.md @@ -0,0 +1,429 @@ +--- +title: Dubbo +lock: need +--- + +# Dubbo 使用教程和原理分析 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,为什么要使用Dubbo、怎么使用Dubbo、Dubbo通信的原理是什么。在学习本文后,你可以避开很多关于 Dubbo 使用时的坑,也能更清楚自己的编码是在做什么。 + +本文涉及的工程: +- xfg-dev-tech-dubbo:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-dubbo](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-dubbo) +- xfg-dev-tech-dubbo-test:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-dubbo-test](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-dubbo-test) + +## 一、为什么使用 + +随着互联网场景中所要面对的用户规模和体量的增加,系统也需要做相应的拆分设计和实现。随之而来的,以前的一套系统,现在成了多个微服务。如:电商系统,以前就在一个工程中写就可以了,现在需要拆分出,用户、支付、商品、配送、活动、风控等各个模块。那么这些模块拆分后,如何高效的通信呢? + +
+ +
+ +- 关于通信,就引入了 RPC 框架,而 Dubbo 就是其中的一个实现方式。 +- 那为啥用 Dubbo 呢?其实核心问题就一个,为了提高通信效率。因为 Dubbo 的底层通信是 Socket 而不是 HTTP 所以通信的性能会更好。同时 Dubbo 又有分布式的高可用设计,在一组部署了交易服务的实例宕机后,会被从注册中心摘除,之后流量会打到其他服务上。 + +## 二、要怎么使用 + +
+ +
+ +Dubbo 的使用分为2方,一个是接口的提供方,另外一个是接口的调用方。接口的提供方需要提供出被调用方使用接口的描述性信息。这个信息包括:接口名称、接口入参、接口出参,只有让调用方拿到这些信息以后,它才能依托于这样的接口信息做一个代理操作,并在代理类中使用 Socket 完成双方的信息交互。 + +所以你看上去调用 RPC 接口好像和使用 HTTP 也没啥区别,无非就是引入了 POM 配置,之后再配置了注解就可以使用了。但其实,它是把你的 Jar 当做代理的必要参数使用了。**本文也会介绍,具体是怎么代理的** + +## 三、使用的案例 + +对于编程的学习来说,其实最开始的那一下,不是搞明白所有原理,而是先让自己可以看到运行出来的效果。哎,之后就去分析原理,这样会舒服的多。 + +所以小傅哥这里提供了一套简单的 Dubbo 使用案例,只要你满足最基本的配置条件,就可以运行出效果; +1. JDK 1.8 +1. Maven 3.x - jdk1.8支持的就可以 +2. Dubbo 3.1.4 - POM 中已经配置,与2.x最大的使用上的区别就是一些注解的使用 +3. Zookeeper 3.4.x - 如果你只是按照本文中的直连模式测试,那么不安装 Zookeeper 也可以 + +### 1. 接口提供方 + +工程案例创建结构,采用的是 DDD 结构。但和 DDD 一点关系没有。如果你对工程创建有疑惑,可以参考 [《Java 简明教程》之 DDD 架构](https://bugstack.cn/md/road-map/ddd.html) + +
+ +
+ +#### 1.1 接口定义 + +**源码**:`cn.bugstack.dev.tech.dubbo.api.IUserService` + +```java +public interface IUserService { + + Response queryUserInfo(UserReqDTO reqDTO); + +} +``` + +- 接口定义平平无奇,但第1个坑暗藏玄机! +- 也就是,所有的 Dubbo 接口,出入参,默认都需要继承 Serializable 接口。也就是 UserReqDTO、UserResDTO、Response 这3个类,都得继承 Serializable 序列化接口。 + +#### 1.2 接口实现 + +**源码**:`cn.bugstack.dev.tech.dubbo.trigger.rpc.UserService` + +```java +@Slf4j +@DubboService(version = "1.0.0") +public class UserService implements IUserService { + + @Override + public Response queryUserInfo(UserReqDTO reqDTO) { + log.info("查询用户信息 userId: {} reqStr: {}", reqDTO.getUserId(), JSON.toJSONString(reqDTO)); + try { + // 1. 模拟查询【你可以从数据库或者Redis缓存获取数据】 + UserResDTO resDTO = UserResDTO.builder() + .userId(reqDTO.getUserId()) + .userName("小傅哥") + .userAge(20) + .build(); + + // 2. 返回结果 + return Response.builder() + .code(Constants.ResponseCode.SUCCESS.getCode()) + .info(Constants.ResponseCode.SUCCESS.getInfo()) + .data(resDTO).build(); + } catch (Exception e) { + log.error("查询用户信息失败 userId: {} reqStr: {}", reqDTO.getUserId(), JSON.toJSONString(reqDTO), e); + return Response.builder() + .code(Constants.ResponseCode.UN_ERROR.getCode()) + .info(Constants.ResponseCode.UN_ERROR.getInfo()) + .build(); + } + } + +} +``` + +- 接口实现平平无奇,但第2个坑暗藏玄机! +- Dubbo 的实现接口,需要被 Dubbo 自己管理。所以 Dubbo 提供了 @DubboService 注解。有些小卡拉米,使用的是不是 Spring 的 @Service 呀?尤其是以前的 Dubbo 版本 `2.7.*` 它的注解也是 @Service 也不留神就用成了 Spring 的 @Service。一个小bug,又调了一上午。 + +#### 1.3 工程配置 + +**application.yml** + +```java +dubbo: + application: + name: xfg-dev-tech-dubbo + version: 1.0.0 + registry: + address: zookeeper://127.0.0.1:2181 # N/A - 无zookeeper可配置 N/A 走直连模式测试 + protocol: + name: dubbo + port: 20881 + scan: + base-packages: cn.bugstack.dev.tech.dubbo.api +``` + +- 配置信息平平无奇,但第3个坑暗藏玄机! +- base-packages 扫描的是哪里配置了 Dubbo 的 API 入口,给它入口就行,它会自己找到实现类。但!你要知道 Java 的 Spring 应用能扫描到,能被 Spring 管理,那么 pom 要**直接或者间接**的引导到定义了 Dubbo 的模块。 +- 再有一个问题,Spring 应用开发,讲究约定大于配置。你 Application 应用的包名,应该是可以覆盖到其他包名的。比如 Application 都配置到 `cn.bugstack.dev.tech.dubbo.a.b.c.d.*` 去了,它默认就扫不到 `cn.bugstack.dev.tech.dubbo.api` 了。一个小bug,一下午又过去了。 +- 注意:address:如果配置的是 N/A 就是不走任何注册中心,就是个直连,主要用于本地验证的。如果你配置了 zookeeper://127.0.0.1:2181 就需要先安装一个 zookeeper 另外,即使你配置了注册中心的方式,也可以直连测试。 + +#### 1.4 应用构建 + +以上信息都准备了,一群小卡拉米开始掉到第4个坑里了! + +你有2个应用,一个Dubbo接口提供方、一个Dubbo接口使用方。那么你在给你另外一个应用使用接口的时候,你在 InelliJ IDEA 的 Maven 中执行 Install 了吗? + +Install 是干啥的?它是为了让你使用了同一个本地 Maven 配置的应用,可以引入到对方提供的 Jar 包。你 Install 以后,这个 Jar 包就会进入到本地 Maven 仓库了。如果是公司里开发,会有专门的自己家部署的,私有Maven中心仓库,就可以通过 deploy 把本地 Jar 发布上去,那么公司里的伙伴,也就都可以引用了。 + +
+ +
+ +- 你要先点击 root 下的 install 操作,这样就会自动构建了。 +- 如果你电脑配置有点低,也会出现一些`气人怪相`,比如就刷不进去,install 了也引用不了。记得要 clean 清空下,也可以直接到 maven 文件夹去清空。 + +### 2. 接口使用方 + +有些小卡拉米觉得前面的坑都扫干净了,就完事了。没有接下来还有坑,让你一搞搞一天,半夜也睡不好。 + +#### 2.1 POM 引入 + +```xml + + cn.bugstack + xfg-dev-tech-dubbo-api + 1.0-SNAPSHOT + +``` + +- POM 的配置,就是把 Jar 包给引用进来。因为 Dubbo 需要根据这个接口,做一个代理操作。**不引入,你代码就爆红啦!爆红啦!🌶** + +#### 2.2 消费配置 + +**源码**:`application.yml` + +```java +dubbo: + application: + name: xfg-dev-tech-dubbo + version: 1.0.0 + registry: + address: zookeeper://127.0.0.1:2181 +# address: N/A + protocol: + name: dubbo + port: 20881 +``` + +- 配置了 zookeeper 你就用第一个,代码中对应 `@DubboReference(interfaceClass = IUserService.class, version = "1.0.0")` +- 配置了 N/A 你就用第二个,代码中必须指定直连。`@DubboReference(interfaceClass = IUserService.class, url = "dubbo://127.0.0.1:20881", version = "1.0.0")` + +#### 2.3 代码配置 + +**源码**:`cn.bugstack.dev.tech.dubbo.consumer.test.ApiTest` + +```java +// 直连模式;@DubboReference(interfaceClass = IUserService.class, url = "dubbo://127.0.0.1:20881", version = "1.0.0") +@DubboReference(interfaceClass = IUserService.class, version = "1.0.0") +private IUserService userService; + +@Test +public void test_userService() { + UserReqDTO reqDTO = UserReqDTO.builder().userId("10001").build(); + Response resDTO = userService.queryUserInfo(reqDTO); + log.info("测试结果 req: {} res: {}", JSON.toJSONString(reqDTO), JSON.toJSONString(resDTO)); +} +``` + +**测试结果** + +```java +2023-07-08 15:37:22.291 INFO 62481 --- [ main] c.b.d.tech.dubbo.consumer.test.ApiTest : 测试结果 req: {"userId":"10001"} res: {"code":"0000","data":{"userAge":20,"userId":"10001","userName":"小傅哥"},"info":"成功"} +2023-07-08 15:37:22.324 INFO 62481 --- [tor-Framework-0] o.a.c.f.imps.CuratorFrameworkImpl : backgroundOperationsLoop exiting +``` + +- 如果不出啥意外,到这你就可以直接启动运行了。并看到测试结果。 +- 但别忘记了,你启动的时候,需要先启动 xfg-dev-tech-dubbo 让接口提供方跑起来。 + +## 四、原理的分析 + +都说 Jar 是提供可描述性信息的,对方才能代理调用。那么这个过程是怎么干的呢,总不能一问这个,就让小卡拉米们去手写 Dubbo 呀!所以小傅哥会通过最简单模型结构,让你了解这个 Dubbo 通信的原理,方便小卡拉米们上手。 + +
+ +
+ +- 如果所示,接口使用方,对接口进行代理。什么是代理呢,代理就是用一个包装的结构,代替原有的操作。在这个包装的结构里,你可以自己扩展出任意的方法。 +- 那么,这里的代理。就是根据接口的信息,创建出一个代理对象,在代理对象中,提供 Socket 请求。当调用这个接口的时候,就可以对接口提供方的,发起 Socket 请求了。 +- 而 Socket 接收方,也就是接口提供方。他收到信息以后,根据接口的描述性内容,进行一个反射调用。这下就把信息给请求出来,之后再通过 Socket 返回回去就可以了。 + +好,核心的原理就这么点。接下来,我们从代码中看看。 + +### 1. 接口反射 - 提供方 + +**源码**:`cn.bugstack.dev.tech.dubbo.trigger.socket.RpcServerSocket` + +```java +@Slf4j +@Service +public class RpcServerSocket implements Runnable { + + private ApplicationContext applicationContext; + + public RpcServerSocket(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + new Thread(this).start(); + } + + @Override + public void run() { + EventLoopGroup bossGroup = new NioEventLoopGroup(); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .option(ChannelOption.SO_BACKLOG, 128) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel channel) { + channel.pipeline().addLast(new ObjectEncoder()); + channel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null))); + channel.pipeline().addLast(new SimpleChannelInboundHandler>() { + + @Override + protected void channelRead0(ChannelHandlerContext channelHandlerContext, Map request) throws Exception { + // 解析参数 + Class clazz = (Class) request.get("clazz"); + String methodName = (String) request.get("methodName"); + Class[] paramTypes = (Class[]) request.get("paramTypes"); + Object[] args = (Object[]) request.get("args"); + + // 反射调用 + Method method = clazz.getMethod(methodName, paramTypes); + Object invoke = method.invoke(applicationContext.getBean(clazz), args); + + // 封装结果 + Map response = new HashMap<>(); + response.put("data", invoke); + + log.info("RPC 请求调用 clazz:{} methodName:{}, response:{}", clazz.getName(), methodName, JSON.toJSON(response)); + // 回写数据 + channelHandlerContext.channel().writeAndFlush(response); + } + }); + } + }); + + ChannelFuture f = b.bind(22881).sync(); + f.channel().closeFuture().sync(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } + +} +``` + +这段代码主要提供的功能包括: +1. Netty Socket 启动一个服务端。 +2. 注入 ApplicationContext applicationContext 用于在接收到请求接口信息后,获取对应的 Bean 对象。 +3. 根据请求来的 Bean 对象,以及参数的必要信息。进行接口的反射调用。 +4. 最后一步,就是把接口反射请求的信息,再通过 Socket 返回回去。 + +### 2. 接口代理 - 调用方 + +打开工程:[xfg-dev-tech-dubbo-test](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-dubbo-test) + +**源码**:`cn.bugstack.dev.tech.dubbo.consumer.config.RPCProxyBeanFactory` + +```java +@Slf4j +@Component("rpcProxyBeanFactory") +public class RPCProxyBeanFactory implements FactoryBean, Runnable { + + private Channel channel; + + // 缓存数据,实际RPC会对每次的调用生成一个ID来标记获取 + private Object responseCache; + + public RPCProxyBeanFactory() throws InterruptedException { + new Thread(this).start(); + while (null == channel) { + Thread.sleep(150); + log.info("Rpc Socket 链接等待..."); + } + } + + @Override + public IUserService getObject() throws Exception { + + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Class[] classes = {IUserService.class}; + InvocationHandler handler = new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + + if (Object.class.equals(method.getDeclaringClass())) { + return method.invoke(this, args); + } + + Map request = new HashMap<>(); + request.put("clazz", IUserService.class); + request.put("methodName", method.getName()); + request.put("paramTypes", method.getParameterTypes()); + request.put("args", args); + + channel.writeAndFlush(request); + + // 模拟超时等待,一般RPC接口请求,都有一个超时等待时长。 + Thread.sleep(350); + + return responseCache; + } + }; + + return (IUserService) Proxy.newProxyInstance(classLoader, classes, handler); + } + + @Override + public Class getObjectType() { + return IUserService.class; + } + + @Override + public void run() { + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + Bootstrap b = new Bootstrap(); + b.group(workerGroup) + .channel(NioSocketChannel.class) + .option(ChannelOption.AUTO_READ, true) + .handler(new ChannelInitializer() { + + @Override + protected void initChannel(SocketChannel channel) throws Exception { + channel.pipeline().addLast(new ObjectEncoder()); + channel.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null))); + channel.pipeline().addLast(new SimpleChannelInboundHandler>() { + + @Override + protected void channelRead0(ChannelHandlerContext channelHandlerContext, Map data) throws Exception { + responseCache = data.get("data"); + } + }); + } + }); + ChannelFuture channelFuture = b.connect("127.0.0.1", 22881).syncUninterruptibly(); + this.channel = channelFuture.channel(); + channelFuture.channel().closeFuture().syncUninterruptibly(); + } finally { + workerGroup.shutdownGracefully(); + } + } + +} +``` + +这段代码主要提供的功能包括: +1. 实现 `FactoryBean` 为的是把这样一个代理对象,交给 Spring 容器管理。 +2. 实现 Runnable 接口,并在接口中,创建 Netty 的 Socket 客户端。客户端中接收来自服务端的消息,并临时存放到缓存中。**注意 Dubbo 中这块的处理会复杂一些,以及请求同步响应通信,这样才能把各个接口的调动记录下来** +3. `getObject()` 对象中,提供代理操作。代理里,就可以自己想咋搞咋搞了。而 Dubbo 也是在代理里,提供了如此的操作,对接口提供方发送请求消息,并在超时时间内返回接口信息。因为反射调用,需要你`提供类`、`方法`、`入参类型`、`入参内容`,所以我们要把这些信息传递给接口提供方。 + +### 3. 服务测试 - 消费验证 + +- 启动 xfg-dev-tech-dubbo +- 测试 xfg-dev-tech-dubbo-test + +```java +@Resource(name = "rpcProxyBeanFactory") +private IUserService proxyUserService; + +@Test +public void test_proxyUserService(){ + UserReqDTO reqDTO = UserReqDTO.builder().userId("10001").build(); + Response resDTO = proxyUserService.queryUserInfo(reqDTO); + log.info("测试结果 req: {} res: {}", JSON.toJSONString(reqDTO), JSON.toJSONString(resDTO)); +} +``` + +**测试结果** + +```java +2023-07-08 16:14:51.322 INFO 74498 --- [ main] c.b.d.tech.dubbo.consumer.test.ApiTest : 测试结果 req: {"userId":"10001"} res: {"code":"0000","data":{"userAge":20,"userId":"10001","userName":"小傅哥"},"info":"成功"} +``` + +- 这里我们给 IUserService 注入一个自己代理好的对象,之后就可以调用验证了。 +- 好啦,到这我们就把关于 Dubbo 的事交代明白了,以上内容较多。小卡拉米需要细细的品味吸收! diff --git a/docs/md/road-map/dump-mat.md b/docs/md/road-map/dump-mat.md new file mode 100644 index 000000000..15b94bd5e --- /dev/null +++ b/docs/md/road-map/dump-mat.md @@ -0,0 +1,239 @@ +--- +title: dump eclipse mat +lock: need +--- + +# Eclipse MAT 分析 Java heap space dump 日志 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +写了这么久Java代码,操作了那么多发布上线,那你看到过`java.lang.OutOfMemoryError: Java heap space`吗?如果有幸看到了,你是怎么解决的呢?是束手无策,还是有排查工具。如果这样的问题是被面试问的,没做过就很难回答了。那么怎么学习一下呢? + +
+ +
+ +**什么场景才会有 OutOfMemoryError** + +能写出 OutOfMemoryError 的不是编码不精,就是故意埋坑。其实很多时候我们很难在正常编码写出一个 OutOfMemoryError,因为这个过程你需要大量的往内存加数据,逐步把 JVM 的内存耗尽。而只是1G内存容量(-Xmx1G),仅订单数据就要300多万条记录,谁又能写个 MyBatis SQL 操作,要一次直接把`300万`数据查询到程序内存里呢。 + +不过,还真可能有!但这个不是程序员故意编码查询300万,而是在做数据导出的时候,处理分页的加法计算有问题,导致每次都是 limit 0,n,n 不断的加大。正确的应该是 limit m,n 这样的查询。这样的 `OutOfMemoryError`,在过往工作中就遇到过,最终经过排查到一次要从数据库获取几百万条数据,导致服务宕机。 + +那么,为了更好的让大家学习到这样的场景以及使用工具排查,小傅哥这里专门做了案例。可以一起学习下。 + +## 一、环境准备 + +为了方便大家进行学习验证,小傅哥这里准备好了一个测试工程和相关的环境安装。你可以在安装完工程后,执行 `ApiTest#test_insert` 向数据库写入250万数据,便于测试。 + +
+ +
+ +- 工程:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-dump](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-dump) +- 说明:`dev-ops` 下提供了 docker 安装 mysql 以及初始化表。如果你没有 docker 也可以直接本地数据库导入库表。 + +## 二、软件安装 + +### 1. 分析软件 + +对于 OutOfMemoryError 的错误排查,需要让工程导出 dump 日志文件,之后通过软件工具分析。分析 dump 的软件有2个常见的;免费的 Eclipse MAT、付费的 JProfiler(可短期体验) + +- Eclipse MAT:[https://eclipse.dev/mat/downloads.php](https://eclipse.dev/mat/downloads.php) +- JProfiler:[https://www.ej-technologies.com/download/jprofiler/files](https://www.ej-technologies.com/download/jprofiler/files) - `学会 MAT 这个可以自己体验` + +### 2. Eclipse MAT 安装配置 + +
+ +
+ +#### 2.1 加大内存 + +
+ +
+ +```java +-startup +../Eclipse/plugins/org.eclipse.equinox.launcher_1.6.600.v20231106-1826.jar +--launcher.library +../Eclipse/plugins/org.eclipse.equinox.launcher.cocoa.macosx.aarch64_1.2.800.v20231003-1442 +-vmargs +--add-exports=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED +-Xms4096m +-Xmx4096m +-Dorg.eclipse.swt.internal.carbon.smallFonts +-XstartOnFirstThread +``` + +- 需要配置 `-Xms4096m`、`-Xmx4096m` 否则过大的 dump 日志,就不能加载进去分析了。大一点配置分析的更快。 + +#### 2.2 `does not contain the JNI_CreateJavaVM symbol.` 报错处理 + +
+ +
+ +```java + + + -vm + /Library/Java/JavaVirtualMachines/jdk1.8.0_341.jdk/Contents/Home/bin + + -keyring + ~/.eclipse_keyring + + +``` + +- 如果遇到 `The JVM shared library "/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/../lib/server/libjvm.dylib" does not contain the JNI_CreateJavaVM symbol.` 可以打开 Info.plist 添加 -vm 和 jdk 路径。 + +## 三、产生 dump 案例 + +首先你需要为你要运行的方法添加 VM Options 当你运行一个方法后,添加 JVM 配置,这样才能到处 dump + +
+ +
+ +- `-Xms128M -Xmx128M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=../xfg-dev-tech-dump/docs/dump` +- HeapDumpPath 为你的工程完成路径,到出到 dump 文件夹下。 +- 运行方法后,就可以在 docs/dump 就可以看到产生的日志了。 + +### 1. 数据库查询 + +```java + + +@Test +public void test_java_heap_space_sql() throws InterruptedException { + while (true){ + userOrderDao.queryPage(); + } +} +``` + +### 2. 线程池过大 + +```java +@Test +public void test_thread_pool_java_heap_space() { + // 创建一个固定大小的线程池 + ExecutorService executorService = Executors.newFixedThreadPool(10); + try { + // 不断提交占用大量内存的任务 + for (int i = 0; i < Integer.MAX_VALUE; i++) { + executorService.submit(new MemoryHogTask()); + } + } catch (OutOfMemoryError e) { + System.out.println("Caught OutOfMemoryError: " + e.getMessage()); + } finally { + // 关闭线程池 + executorService.shutdown(); + } +} + +static class MemoryHogTask implements Runnable { + @Override + public void run() { + try { + // 分配一个大数组来占用内存 + int[] memoryHog = new int[1000000]; // 大约占用 4MB 内存 + // 模拟一些计算以避免 JIT 优化掉内存分配 + for (int i = 0; i < memoryHog.length; i++) { + memoryHog[i] = i; + } + // 保持任务在一定时间内占用内存 + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} +``` + +### 3. list 添加过多数据 + +```java +@Test +public void test_java_heap_space_list() throws InterruptedException { + List list = new ArrayList<>(); + while (true) { + byte[] bytes = new byte[1024 * 1024 * 1024]; + list.add(bytes); + TimeUnit.SECONDS.sleep(1); + } +} +``` + +- 这类场景是比较多的,比如我们做营销活动,要把很多的活动数据预热到缓存,如果 JVM 配置的比较低,是可能会出现 `Java heap space` 的。 + +## 四、Eclipse MAT 分析 + +### 1. 导入 dump 文件 + +
+ +
+ +
+ +
+ +### 2. 查看统计树 + +
+ +
+ +### 3. 排序对象 + +
+ +
+ +### 4. 引用关系 + +
+ +
+ +- 点击查看,被谁引用了。 + +### 5. 逐层分析 - 进入对象详情 + +
+ +
+ +- 看看这个 Object 值装的是什么。 + +### 6. 发现问题 + +
+ +
+ +- 看到了在检索数据库数据。其实前面就已经定义到哪里的方法导致,这里可以具体看到细节。 + +### 7. 其他分析 + +
+ +
+ +- 我们本案例采用的是 MySql 8.x 如果你使用其他线程池工具,还可能会返回具体的 SQL 语句一起打印出来。方便分析。 + +好啦,有了这样一个分析过程,你也可以尝试熟悉下工具,分析分析其他的 Java heap space 场景。几次玩下来也就熟悉这个工具了。 + +--- + +其他工具;[https://visualvm.github.io/](https://visualvm.github.io/) diff --git a/docs/md/road-map/dump-visualvm.md b/docs/md/road-map/dump-visualvm.md new file mode 100644 index 000000000..fc2d6eb42 --- /dev/null +++ b/docs/md/road-map/dump-visualvm.md @@ -0,0 +1,533 @@ +--- +title: dump VisualVM +lock: need +--- + +# VisualVM 分析 Java heap space dump 日志 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +没毕业前以为学编程,以为工作后就只是写代码。工作后才发现,写代码只是一少部分工作。 + +`JMeter 压测`、`Remote JVM Debug - 远程调试`、`AREX - 流量录制&回放`、`ELK - 分布式日志`、`普罗米修斯监控`、`Arthas`、`Dump日志分析`等,但凡一样不会,基本就会在某一个场景踩坑。**小则是报警异常,大则是线上事故!** + +
+ +
+ +**👨🏻‍💻程序员,也是高危行业呀!** + +从互联网草蜢时代,到现在工作了这么多年,也是见证了很多程序员因为写Bug毕业啦🎓,即使不是被开除,往往重大的事故也会影响未来的绩效、加薪和晋升。这些事故按;照影响时长、影响用户量、造成的资损、解决的时长等,会被定级为 P0、P1、P2、P3 不同级别的事故。 + +所以,到目前有越来越多的辅助工具,来帮助研发提高代码交付质量,以及各类系统异常分析工具,提高问题排查效率。类似这样的系统、服务、组件,小傅哥已经在 [bugstack.cn 编程路书](https://bugstack.cn/md/road-map/road-map.html) 做了大量的案例讲解。今天小傅哥在给大家分享一个关于 VisualVM 的使用。 + +## 一、关于 VisualVM + +VisualVM 是一款可视化 Java 故障排除工具,集成了 JDK 命令行工具和轻量级性能分析功能。专为开发和生产环境设计。 + +
+ +
+ +下载:[https://visualvm.github.io/download.html](https://visualvm.github.io/download.html) + +> 接下来,小傅哥会结合 VisualVM 做一些常用的案例,方便伙伴学习。 + +## 二、案例 - 分析大对象 + +### 1. 测试工程 + +
+ +
+ +- 地址:[https://github.com/fuzhengwei/xfg-dev-tech-visualvm](https://github.com/fuzhengwei/xfg-dev-tech-visualvm) +- 说明:这是一个简单的测试工程,通过访问接口产生大对象。之后在通过 JmapDumpController 接口,执行命令,产生 Dump 文件。之后在使用 VisualVM 分析产生的 Dump 日志,定位是哪个对象导致的问题。 + +### 2. 执行程序 + +首先,启动 xfg-dev-tech-visualvm 应用程序。之后执行 visualvm-test.sh 脚本,Windows 用户需要在 powershell 里执行,Mac 电脑可以直接在 IntelliJ IDEA 点击绿色箭头执行。 + +
+ +
+ +- 首先,点击启动程序,本地运行即可。一般公司里线上的应用,也有专门下载 dump 日志的地方。 +- 之后,执行 `./visualvm-test.sh` 这部分写了测试程序的脚本和获取 dump 日志的操作。 + +#### 2.1 接口;创建对象 + +```java +@RestController +@RequestMapping("/api/memory") +public class MemoryTestController { + + // 用于存储大对象的静态变量,模拟内存泄漏 + private static final Map MEMORY_CACHE = new ConcurrentHashMap<>(); + private static final List BIG_OBJECTS = new ArrayList<>(); + + /** + * 大对象接口 - 创建大量对象占用内存 + */ + @GetMapping("/big-object") + public Map bigObjectApi() { + // 创建大对象(10MB的字节数组) + byte[] bigData = new byte[10 * 1024 * 1024]; // 10MB + for (int i = 0; i < bigData.length; i++) { + bigData[i] = (byte) (i % 256); + } + + // 将大对象存储到静态集合中,模拟内存泄漏 + BIG_OBJECTS.add(bigData); + + Map result = new HashMap<>(); + result.put("status", "success"); + result.put("message", "创建了一个大对象(10MB)"); + result.put("timestamp", System.currentTimeMillis()); + result.put("bigObjectsCount", BIG_OBJECTS.size()); + result.put("totalMemoryUsed", BIG_OBJECTS.size() * 10 + "MB"); + + return result; + } + + /** + * 内存泄漏接口 - 持续创建对象并缓存 + */ + @GetMapping("/memory-leak") + public Map memoryLeakApi() { + String key = "data_" + System.currentTimeMillis(); + + // 创建大量小对象并缓存 + List dataList = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + dataList.add("这是第" + i + "个数据对象,包含一些文本内容用于占用内存空间"); + } + + MEMORY_CACHE.put(key, dataList); + + Map result = new HashMap<>(); + result.put("status", "success"); + result.put("message", "创建了10000个小对象并缓存"); + result.put("timestamp", System.currentTimeMillis()); + result.put("cacheSize", MEMORY_CACHE.size()); + result.put("cacheKey", key); + + return result; + } + + /** + * 超大对象接口 - 创建超大对象 + */ + @GetMapping("/huge-object") + public Map hugeObjectApi() { + // 创建超大对象(100MB的字节数组) + byte[] hugeData = new byte[100 * 1024 * 1024]; // 100MB + + // 填充数据 + for (int i = 0; i < hugeData.length; i++) { + hugeData[i] = (byte) (Math.random() * 256); + } + + BIG_OBJECTS.add(hugeData); + + Map result = new HashMap<>(); + result.put("status", "success"); + result.put("message", "创建了一个超大对象(100MB)"); + result.put("timestamp", System.currentTimeMillis()); + result.put("bigObjectsCount", BIG_OBJECTS.size()); + + return result; + } + +} +``` + +#### 2.2 接口;获取日志(dump) + +```java +@RestController +@RequestMapping("/api/jmap") +public class JmapDumpController { + + // 使用相对路径,基于项目根目录 + private static final String DUMP_DIR = "docs/dump"; + + /** + * 获取绝对路径的dump目录 + */ + private String getDumpDirectory() { + // 获取项目根目录 + String userDir = System.getProperty("user.dir"); + // 如果当前目录是xfg-dev-tech-app,则需要回到上级目录 + if (userDir.endsWith("xfg-dev-tech-app")) { + userDir = new File(userDir).getParent(); + } + return userDir + File.separator + DUMP_DIR; + } + + /** + * 生成堆转储文件 + */ + @GetMapping("/dump") + public Map generateHeapDump() { + Map result = new HashMap<>(); + + try { + // 获取dump目录的绝对路径 + String dumpDir = getDumpDirectory(); + + // 确保目录存在 + File dir = new File(dumpDir); + if (!dir.exists()) { + dir.mkdirs(); + } + + // 获取当前进程的PID + String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; + + // 生成文件名(包含时间戳) + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss"); + String timestamp = sdf.format(new Date()); + String fileName = "heap_dump_" + timestamp + ".hprof"; + String filePath = dumpDir + File.separator + fileName; + + // 执行jmap命令生成堆转储 + String command = "jmap -dump:format=b,file=" + filePath + " " + pid; + Process process = Runtime.getRuntime().exec(command); + int exitCode = process.waitFor(); + + if (exitCode == 0) { + result.put("status", "success"); + result.put("message", "堆转储文件生成成功"); + result.put("filePath", filePath); + result.put("fileName", fileName); + } else { + result.put("status", "error"); + result.put("message", "堆转储文件生成失败"); + result.put("exitCode", exitCode); + } + + } catch (IOException | InterruptedException e) { + result.put("status", "error"); + result.put("message", "生成堆转储文件时发生异常: " + e.getMessage()); + } + + result.put("timestamp", System.currentTimeMillis()); + return result; + } + +} +``` + +#### 2.3 脚本;统一执行 + +```java +#!/bin/bash + +# VisualVM 内存测试自动化脚本 +# 作者: xiaofuge +# 用途: 自动化测试内存接口并生成dump文件 + +# 配置参数 +BASE_URL="http://localhost:8091" +DUMP_DIR="../dump" +LOG_FILE="$DUMP_DIR/test_log_$(date +%Y%m%d_%H%M%S).txt" + +# 颜色输出 +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# 日志函数 +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" +} + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE" +} + +# 检查应用是否启动 +check_app_status() { + log_info "检查应用状态..." + response=$(curl -s -o /dev/null -w "%{http_code}" "$BASE_URL/api/memory/status") + if [ "$response" = "200" ]; then + log_success "应用已启动,状态正常" + return 0 + else + log_error "应用未启动或状态异常 (HTTP: $response)" + return 1 + fi +} + +# 等待应用启动 +wait_for_app() { + log_info "等待应用启动..." + for i in {1..30}; do + if check_app_status > /dev/null 2>&1; then + log_success "应用启动成功" + return 0 + fi + log_info "等待中... ($i/30)" + sleep 2 + done + log_error "应用启动超时" + return 1 +} + +# 调用API接口 +call_api() { + local endpoint=$1 + local description=$2 + local count=${3:-1} + + log_info "调用接口: $description" + for ((i=1; i<=count; i++)); do + response=$(curl -s "$BASE_URL$endpoint") + status=$(echo "$response" | grep -o '"status":"[^"]*"' | cut -d'"' -f4) + if [ "$status" = "success" ]; then + log_success "[$i/$count] $description - 成功" + else + log_error "[$i/$count] $description - 失败: $response" + fi + sleep 1 + done +} + +# 显示内存状态 +show_memory_status() { + log_info "获取内存状态..." + response=$(curl -s "$BASE_URL/api/memory/status") + echo "$response" | python3 -m json.tool 2>/dev/null || echo "$response" + echo "" +} + +# 生成dump文件 +generate_dump() { + log_info "生成堆转储文件..." + response=$(curl -s "$BASE_URL/api/jmap/dump") + status=$(echo "$response" | grep -o '"status":"[^"]*"' | cut -d'"' -f4) + if [ "$status" = "success" ]; then + filename=$(echo "$response" | grep -o '"fileName":"[^"]*"' | cut -d'"' -f4) + log_success "堆转储文件生成成功: $filename" + else + log_error "堆转储文件生成失败: $response" + fi +} + +# 生成内存信息文件 +generate_memory_info() { + log_info "生成内存信息文件..." + response=$(curl -s "$BASE_URL/api/jmap/memory-info") + status=$(echo "$response" | grep -o '"status":"[^"]*"' | cut -d'"' -f4) + if [ "$status" = "success" ]; then + filename=$(echo "$response" | grep -o '"fileName":"[^"]*"' | cut -d'"' -f4) + log_success "内存信息文件生成成功: $filename" + else + log_error "内存信息文件生成失败: $response" + fi +} + +# 清理缓存 +clear_cache() { + log_info "清理缓存..." + response=$(curl -s "$BASE_URL/api/memory/clear-cache") + status=$(echo "$response" | grep -o '"status":"[^"]*"' | cut -d'"' -f4) + if [ "$status" = "success" ]; then + log_success "缓存清理成功" + else + log_error "缓存清理失败: $response" + fi +} + +# 主测试流程 +run_test() { + log_info "开始VisualVM内存测试" + + # 检查dump目录 + if [ ! -d "$DUMP_DIR" ]; then + log_info "创建dump目录: $DUMP_DIR" + mkdir -p "$DUMP_DIR" + fi + + # 等待应用启动 + if ! wait_for_app; then + log_error "应用启动失败,退出测试" + exit 1 + fi + + # 显示初始内存状态 + log_info "=== 初始内存状态 ===" + show_memory_status + + # 测试普通接口 + call_api "/api/memory/normal" "普通接口测试" 5 + + # 显示内存状态 + log_info "=== 普通接口调用后内存状态 ===" + show_memory_status + + # 测试大对象接口 + call_api "/api/memory/big-object" "大对象接口测试" 10 + + # 显示内存状态 + log_info "=== 大对象创建后内存状态 ===" + show_memory_status + + # 生成第一次dump + generate_dump + generate_memory_info + + # 测试内存泄漏接口 + call_api "/api/memory/memory-leak" "内存泄漏接口测试" 20 + + # 显示内存状态 + log_info "=== 内存泄漏测试后内存状态 ===" + show_memory_status + + # 测试超大对象接口 + call_api "/api/memory/huge-object" "超大对象接口测试" 5 + + # 显示内存状态 + log_info "=== 超大对象创建后内存状态 ===" + show_memory_status + + # 生成第二次dump + generate_dump + generate_memory_info + + # 清理缓存 + clear_cache + + # 显示清理后内存状态 + log_info "=== 缓存清理后内存状态 ===" + show_memory_status + + # 生成第三次dump + generate_dump + generate_memory_info + + log_success "VisualVM内存测试完成" + log_info "日志文件: $LOG_FILE" + log_info "dump文件目录: $DUMP_DIR" +} + +# 显示帮助信息 +show_help() { + echo "VisualVM 内存测试脚本" + echo "" + echo "用法: $0 [选项]" + echo "" + echo "选项:" + echo " test 运行完整测试流程" + echo " check 检查应用状态" + echo " status 显示内存状态" + echo " dump 生成堆转储文件" + echo " memory-info 生成内存信息文件" + echo " clear 清理缓存" + echo " help 显示帮助信息" + echo "" + echo "示例:" + echo " $0 test # 运行完整测试" + echo " $0 check # 检查应用状态" + echo " $0 dump # 生成dump文件" +} + +# 主程序 +case "${1:-test}" in + "test") + run_test + ;; + "check") + check_app_status + ;; + "status") + show_memory_status + ;; + "dump") + generate_dump + ;; + "memory-info") + generate_memory_info + ;; + "clear") + clear_cache + ;; + "help") + show_help + ;; + *) + log_error "未知选项: $1" + show_help + exit 1 + ;; +esac +``` + +- 整个脚本,会帮助我们执行接口请求以及获取 dump 日志。 + +### 3. Dump 分析 + +
+ +
+ +- 首先,先通过 VisualVM load dump 日志文件,之后点击 Instances By Size 大的文件。 +- 之后,对大的文件对象,点击 references 这样就可以看到是哪个对象影响的问题了。很快的就能帮你分析出程序内产生大的对象的问题原因。 + +## 三、案例;GC 插件 + +VisualVM 还有类似于[普罗米修斯](https://bugstack.cn/md/road-map/grafana.html)一样的监控,可以查看到 JVM 运行情况。也可以帮助我们分析程序运行情况。 + +### 1. 安装插件 - VisualVM + +
+ +
+ +- 在 VisualVM 安装 Visual GC 插件。 + +### 2. 安装插件 - IntelliJ IDEA + +
+ +
+ +- 也可以给 Intellij IDEA 安装一个 VisualVM Launcher 插件,启动程序可以直接使用。 + +### 3. 进入监控 + +
+ +
+ +- 打开 VisualVM 看到本地启动的程序,之后打开 Visual GC +- 这里还可以看见 Monitor、Threads、Profiler,方便我们分析程序 + +### 4. GC 说明 + +
+ +
+ +- 如图,各个模块展示了 JVM 运行状况,从这里可以看到程序占用内存的情况。如果是压测验证,可以打开辅助分析。 + diff --git a/docs/md/road-map/elk.md b/docs/md/road-map/elk.md new file mode 100644 index 000000000..9bbf93118 --- /dev/null +++ b/docs/md/road-map/elk.md @@ -0,0 +1,229 @@ +--- +title: ELK +lock: need +--- + +# ELK 使用教程采集系统日志 Elasticsearch、Logstash、Kibana + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过易于上手实操的方式,教会读者完成系统ELK日志采集的对接和使用。那你知道对于一个系统的上线考察,必备的几样东西是什么吗?其实这也是面试中考察求职者,是否真的做过系统开发和上线的必备问题。包括:[服务治理(熔断/限流)](https://bugstack.cn/md/road-map/ratelimiter.html)、[监控](https://bugstack.cn/md/road-map/grafana.html)和日志,如果你做的系统里没有这样几个东西,一种是说明系统是玩具项目,另外一种就是压根没做过或者没关心过。前面的已经写完了,所以今天来给大家写ELK日志。 + +本文涉及的工程: + +- xfg-dev-tech-elk:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-elk](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-elk) +- 官网:[https://www.elastic.co/cn/](https://www.elastic.co/cn/) + +## 一、简要说明 + +Elastic Stack 技术栈,别是 `Elasticsearch`、`Logstash`、`Kibana` 组成,简称 ELK 是一套针对日志数据做解决方案的框架。它使您能够聚合来自所有系统和应用程序的日志,分析这些日志,并创建可视化来进行应用程序和基础设施监控、更快的故障排除、安全分析等。 + +- E = Elasticsearch:Elasticsearch 是在 Apache Lucene 上构建的分布式搜索和分析引擎。对各种语言、高性能和无架构 JSON 文档的支持使 Elasticsearch 成为各种日志分析和搜索使用案例的理想选择。 +- L = Logstash:Logstash 是一个开源数据摄取工具,允许您从各种来源收集数据,转换数据,并将数据发送到您希望的目标。通过预构建的筛选器和对 200 多种插件的支持,Logstash 使用户能够轻松摄取数据,无论数据源或类型如何。 +- K = Kibana:Kibana 是一种数据可视化和挖掘工具,可以用于日志和时间序列分析、应用程序监控和运营智能使用案例。它提供了强大且易用的功能,例如直方图、线形图、饼图、热图和内置的地理空间支持。此外,付费的 Kibana 还有 x-pack-jdbc 可以使用,让你就像使用 MyBatis 操作 MySQL 数据库一样操作 Elasticsearch 数据。 + +综上,3个组件的组合使用。由 Logstash 将摄取、转换数据并将其发送到 Elasticsearch 为摄取的数据编制索引,并且分析和搜索这些数据。最终 Kibana 会将分析结果可视化。也就是你可以在 Kibana 上实时看到系统的运行日志。 + +## 二、环境配置 + +这里小傅哥做了个工程案例,并配有对应的环境安装、日志上报,你只需要跟随接下来的文章说明,即可知道 ELK 如何配置和使用。 + +
+ +
+ +- 环境;jdk 1.8、Maven 3.6.x、Docker +- 组件;ELK version 7.17.14 支持 ARM&AMD +- 代码;在 xfg-dev-tech-elk 测试工程中提供了测试代码和环境安装,绿色按钮点击即可安装【确保你已经本地安装了 Docker】 + +### 1. 环境配置 + +
+ +
+ +- docker-compose.yml 运行时会加载下面的 kibana、logstash 配置信息。 +- kibana.yml 设置了资源的基本信息,包括 ES 的连接,中文汉化。 +- logstash.conf 设置了日志的格式,上报到 es:9200 的地址信息。这些都可以保持默认不用修改。 + +### 2. 安装环境 + +```java +version: '3' +# 执行脚本;docker-compose -f docker-compose.yml up -d +# 控制台;GET _cat/indices - 查看 springboot-logstash- 是否存在,上报后存在,则表示接入成功 +services: + elasticsearch: + image: elasticsearch:7.17.14 + ports: + - '9200:9200' + - '9300:9300' + container_name: elasticsearch + restart: always + environment: + - 'cluster.name=elasticsearch' # 设置集群名称为elasticsearch + - 'discovery.type=single-node' # 以单一节点模式启动 + - "cluster.name=docker-cluster" # 设置名称 + - 'ES_JAVA_OPTS=-Xms512m -Xmx512m' # 设置使用jvm内存大小 + networks: + - elk + + logstash: + image: logstash:7.17.14 + container_name: logstash + restart: always + volumes: + - /etc/localtime:/etc/localtime + - ./logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf + ports: + - '4560:4560' + - '50000:50000/tcp' + - '50000:50000/udp' + - '9600:9600' + environment: + LS_JAVA_OPTS: -Xms1024m -Xmx1024m + TZ: Asia/Shanghai + MONITORING_ENABLED: false + links: + - elasticsearch:es # 可以用es这个域名访问elasticsearch服务 + networks: + - elk + depends_on: + - elasticsearch # 依赖elasticsearch启动后在启动logstash + + kibana: + image: kibana:7.17.14 + container_name: kibana + restart: always + volumes: + - /etc/localtime:/etc/localtime + - ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml + ports: + - '5601:5601' + links: + - elasticsearch:es #可以用es这个域名访问elasticsearch服务 + environment: + - ELASTICSEARCH_URL=http://elasticsearch:9200 #设置访问elasticsearch的地址 + - 'elasticsearch.hosts=http://es:9200' #设置访问elasticsearch的地址 + - I18N_LOCALE=zh-CN + networks: + - elk + depends_on: + - elasticsearch + +networks: + elk: + driver: bridge #网络 +``` + +
+ +
+ +
+ +
+ +- 这是 docker compose 执行脚本,如果你本地已经安装了 Docker 可以直接执行 安装即可。 +- 安装完成后,当你看到如上截图,则表示已经运行。注意 Quick Actions 下可以进入日志和控制台。如果启动失败,可以检查日志。 + +### 3. 日志配置 + +#### 3.1 引入POM - logstash + +```pom + + net.logstash.logback + logstash-logback-encoder + 7.3 + +``` + +这个Jar是为了上报应用日志用的。工程的根目录下引入是定义版本,在 xfg-dev-tech-app 模块下引入是具体的引入。 + +#### 3.2 logback 采集 + +```yml +# logstash部署的服务器IP +logstash: + host: 127.0.0.1 +``` + +```xml + + + + + + ${LOG_STASH_HOST}:4560 + + + + + + +``` + +- LOG_STASH_HOST 通过占位符可以使用 yml 配置,这样方便后期动态调整。 + +## 四、应用测试 + +### 1. 启动应用&检测上报 + +```java +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class); + } + + /** + * curl http://localhost:8091/api/ratelimiter/login?fingerprint=uljpplllll01009&uId=1000&token=8790 + */ + @RequestMapping(value = "login", method = RequestMethod.GET) + public String login(String fingerprint, String uId, String token) { + log.info("模拟登录 login fingerprint:{}", fingerprint); + return "模拟登录:登录成功 " + uId; + } + +} +``` + +
+ +
+ +- 地址:[http://0.0.0.0:5601/app/dev_tools#/console](http://0.0.0.0:5601/app/dev_tools#/console) +- 命令:`GET _cat/indices` - 通过命令检测日志上报 + +### 2. 配置日志 + +地址:[http://0.0.0.0:5601/app/discover](http://0.0.0.0:5601/app/discover) + +#### 2.1 创建索引 + +
+ +
+ +
+ +
+ +- 当你的应用启动后,会上报数据。这个时候在点击 Discover 会提示你有可用的数据。 +- 如图创建索引即可。 + +#### 2.2 回到监控 + +创建索引后,回到 Discover 即可查看监控日志。在这个阶段,你可以访问应用程序接口,查看上报日志信息;[http://localhost:8091/api/ratelimiter/login?fingerprint=uljpplllll01009&uId=1000&token=8790](http://localhost:8091/api/ratelimiter/login?fingerprint=uljpplllll01009&uId=1000&token=8790) + +
+ +
+ +- 当你不断的访问接口,就可以看到上报的日志数据信息了。 \ No newline at end of file diff --git a/docs/md/road-map/fastjson.md b/docs/md/road-map/fastjson.md new file mode 100644 index 000000000..aa2c4ab8c --- /dev/null +++ b/docs/md/road-map/fastjson.md @@ -0,0 +1,136 @@ +--- +title: fastjson +lock: need +--- + +# fastjson 使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +本文的宗旨在于通过简单干净实践的方式教会读者,使用 fastjson 的一些常用操作方法。这些方法也是日常使用 fastjson 时最为常用的方法,如果你在使用中还有一些案例和特性,或者踩坑经验也可以在本文提交PR + +本文涉及的工程: + +- xfg-dev-tech-fastjson:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-fastjson](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-fastjson) +- Github:[https://github.com/alibaba/fastjson](https://github.com/alibaba/fastjson) + +## 一、常用方法 + +### 1. 序列化和反序列化 + +```java +String strJson = JSON.toJSONString(UserEntity.builder().build()); +UserEntity userEntity = JSON.parseObject(strJson, UserEntity.class); +``` + +### 2. 配置序列化字段 + +```java +// 不被序列化 +@JSONField(name="amount", serialize=false) +private Double amount; +// 序列化格式 +@JSONField(name="createTime", format="dd/MM/yyyy", ordinal = 3) +private Date createTime; + +@JsonProperty("top_p") +private Double topP = 1d; +@JsonProperty("max_tokens") +private Integer maxTokens = 2048; +``` + +- 对象的属性上添加 `@JSONField`、`@JsonProperty` 都可以改变序列化字段的名字。同时还可以扩展是否被序列化和格式化。 + +### 3. 排除序列化字段 + +```java +UserEntity userEntity = UserEntity.builder() + .amount(100D) + .userName("xfg") + .password("abc000") + .createTime(new Date()) + .build(); + +SimplePropertyPreFilter filter = new SimplePropertyPreFilter(); +Collections.addAll(filter.getExcludes(), "password"); +log.info(JSON.toJSONString(userEntity, filter)); +``` + +- 因为有些时候不是你能修改被序列化的对象,如你引入了别人的 JAR 之后需要对某个类进行序列化,但因为有些对象不能被序列化或者不要序列化。那么这个时候就可以通过 filter 过滤的方式进行处理。 + +### 4. json2map 转换 + +```java +@Test +public void test_map2json() { + Map map = new HashMap<>(); + map.put("key1", "xfg"); + map.put("key2", 123); + map.put("key3", false); + log.info(JSON.toJSONString(map)); +} + +@Test +public void test_json2map() { + String jsonString = "{\"key1\":\"xfg\",\"key2\":123,\"key3\":false}"; + Map map = JSON.parseObject(jsonString, Map.class); + for (Map.Entry entry : map.entrySet()) { + log.info("{} : {}", entry.getKey(), entry.getValue()); + } +} +``` + +- 有些时候我们接收的对象就是个 Map 那么你可以使用 fastjson 来对对象进行 map 的转换或者序列化 + +### 5. toString 处理 + +```java +@Test +public void testToString2Bean() throws Exception { + UserEntity userEntity = UserEntity.builder() + .amount(100D) + .userName("xfg") + .password("abc000") + .createTime(new Date()) + .build(); + log.info(userEntity.toString()); + log.info(JSON.toJSONString(ToString2Bean.toObject(userEntity.toString(), UserEntity.class))); +} + +public static T toObject(String str, Class clazz) throws Exception { + // 创建一个新的对象 + T obj = clazz.getDeclaredConstructor().newInstance(); + // 获取类对象 + Class objClass = obj.getClass(); + // 解析字符串 + String[] fields = str.substring(str.indexOf("{") + 1, str.indexOf("}")).split(", "); + // 遍历成员变量 + for (String field : fields) { + // 获取成员变量名和值 + String[] parts = field.split("="); + // 获取成员变量对象 + Field objField = objClass.getDeclaredField(parts[0].trim()); + // 设置成员变量可以访问 + objField.setAccessible(true); + // 设置成员变量的值 + objField.set(obj, convertValue(objField.getType(), parts[1].trim())); + // 设置成员变量不可访问 + objField.setAccessible(false); + } + return obj; +} +``` + +```java +06:03:46.302 [main] INFO cn.bugstack.xfg.dev.tech.test.ApiTest - UserEntity{userName='xfg', password='abc000', amount=100.0, createTime=2023-09-21 20:03:46} +06:03:46.670 [main] INFO cn.bugstack.xfg.dev.tech.test.ApiTest - {"password":"'abc000'","userName":"'xfg'","createTime":"21/09/2023"} + +Process finished with exit code 0 +``` + +- 有一些在方法入参的时候需要用日志打印入参信息。大部分时候都是直接用 json 打印对象,但对于一些较大对象就比较耗时。所以阿里的开发手册是建议这个场景使用 toString 操作。 +- 但是 toString 操作后的日志不太便于,在本地进行测试验证。因为不好转对象。所以这里我们写个 toString2Bean 对象的方法。 + diff --git a/docs/md/road-map/frp.md b/docs/md/road-map/frp.md new file mode 100644 index 000000000..c3c29d2b8 --- /dev/null +++ b/docs/md/road-map/frp.md @@ -0,0 +1,273 @@ +--- +title: Frp 内网穿透 +lock: need +--- + +# Frp 内网穿透,用云服务器IP,教你搭一套内网穿透服务! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +作为一个研发人员,我们经常有诉求把本机正在开发阶段的应用,通过本地部署的方式让外部其他人进行访问验证。尤其是一些给远程远程客户演示的时候,也是非常需要这样的服务。但本机并不是公网IP,都是内网的,怎么样外部访问呢? + +
+ +
+ +**FRP 内网穿透是什么?** + +内网穿透是一种技术手段,用于访问位于防火墙或路由器后面的本地网络(内网)中的设备或服务。通常情况下,内网中的设备无法直接通过公网(互联网)进行访问,从而实现隐私保护和安全性。内网穿透技术的目标是突破这一限制,使外部用户能够通过互联网访问内网中的服务或设备。 + +虽然市面也有一些内网穿透的服务,包括; `natapp`、`coplar`、`花生壳`等,如果你不想折腾,也可以直接使用。但折腾一下,往往会省钱! + +## 1. 搭建脚本 + +这里小傅哥使用了 `fatedier/frp` 0.60 版本进行搭建,提供了相关的运行脚本。 + +
+ +
+ +- 地址:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-frp](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-frp) +- 官网:[https://github.com/fatedier/frp](https://github.com/fatedier/frp) + +### 1.1 frps - 服务端 + +```java +# https://github.com/fatedier/frp/blob/dev/conf/frps_full_example.toml +[common] +# 监听端口 +bind_port = 7000 +# 面板端口 +dashboard_port = 7500 +# 登录面板的账号密码(修改成自己的) +dashboard_user = admin +dashboard_pwd = admin +# token = +``` + +- token 如果设定了,需要保持客户端和服务端一致。 + +```java +# 命令执行 docker-compose -f docker-compose.yml up -d +version: '3.9' +services: + frps: + image: fatedier/frps:v0.60.0 + hostname: frps + container_name: frps + volumes: + - "./config/frps.toml:/frps.toml" + command: + - "-c" + - "/frps.toml" + network_mode: "host" +``` + +- docker compose 按照脚本,走的是 `network_mode: "host"` 不需要额外指定端口。 +- 你需要在云服务器开放 7000、7500 以及需要映射出去的端口。比如客户端配置了 8080、9001 也需要在云服务器开放端口,这样才能访问进来。 +- 官网配置:[https://github.com/fatedier/frp/blob/dev/conf/frps_full_example.toml](https://github.com/fatedier/frp/blob/dev/conf/frps_full_example.toml) - 这里有很全的配置可以参考。 + +### 1.2 frpc - 客户端 + +```java +# 服务端地址 https://github.com/fatedier/frp/blob/dev/conf/frpc_full_example.toml +serverAddr = "117.72.37.243" +# 服务端配置的bindPort +serverPort = 7000 +# token = + +[[proxies]] +# 代理应用名称,根据自己需要进行配置 +name = "xfg-dev-tech-01" +# 代理类型 有tcp\udp\stcp\p2p +type = "tcp" +# 客户端代理应用IP +localIP = "127.0.0.1" +# 客户端代理应用端口 +localPort = 8080 +# 服务端反向代理端口;提供给外部访问 +remotePort = 8080 + +[[proxies]] +# 代理应用名称,根据自己需要进行配置 +name = "xfg-dev-tech-02" +# 代理类型 有tcp\udp\stcp\p2p +type = "tcp" +# 客户端代理应用IP +localIP = "127.0.0.1" +# 客户端代理应用端口 +localPort = 9001 +# 服务端反向代理端口;提供给外部访问 +remotePort = 9001 +``` + +- `localPort = 8080` 是本地应用的端口。 +- `remotePort = 8080` 是远程服务器要暴漏出去的端口,这个端口你可以按需调整。 + +```java +# 命令执行 docker-compose -f docker-compose.yml up -d +version: '3.9' +services: + frpc: + image: fatedier/frpc:v0.60.0 + hostname: frpc + container_name: frpc + volumes: + - "./config/frpc.toml:/frpc.toml" + command: + - "-c" + - "/frpc.toml" + network_mode: "host" +``` + +- docker compose 安装,同样需要指定 `network_mode: "host"` + +## 2. 执行安装 + +搭建FRP内网穿透需要一台云服务器,其实主要用的就是它的公网IP。不过现在的云服务器都非常便宜,36元就可以购买1年,还能抽取京豆。折算下来也就26元1年了! + +
+ +
+ +- 专属价:[http://618.gaga.plus/](http://618.gaga.plus/) +- 云服务器使用:[https://bugstack.cn/md/road-map/docker.html](https://bugstack.cn/md/road-map/docker.html) - 已录制了相关 Docker、Portainer 环境安装,你可以直接对照操作。 + +### 2.1 frps - 云服务器安装 + +#### 2.1.1 上传文件 + +
+ +
+ + +#### 2.1.2 执行脚本 + +```java +[root@lavm-aqhgp9nber frps]# docker-compose -f docker-compose.yml up -d +[+] Running 1/1 + ✔ Container frps Started +``` + +
+ +
+ +你可以进入📃查看启动日志; + + +```java +WARNING: ini format is deprecated and the support will be removed in the future, please use yaml/json/toml format instead! +2024-09-08 09:51:50.581 [I] [frps/root.go:105] frps uses config file: /frps.toml +2024-09-08 09:51:50.674 [I] [server/service.go:237] frps tcp listen on 0.0.0.0:7000 +2024-09-08 09:51:50.674 [I] [frps/root.go:114] frps started successfully +2024-09-08 09:51:50.674 [I] [server/service.go:351] dashboard listen on 0.0.0.0:7500 +2024-09-08 09:51:52.429 [I] [server/service.go:576] [cd9f610f66475f3a] client login info: ip [223.72.84.77:10816] version [0.60.0] hostname [] os [linux] arch [amd64] +2024-09-08 09:51:52.447 [I] [proxy/tcp.go:82] [cd9f610f66475f3a] [xfg-dev-tech-02] tcp proxy listen port [9001] +2024-09-08 09:51:52.448 [I] [server/control.go:399] [cd9f610f66475f3a] new proxy [xfg-dev-tech-02] type [tcp] success +2024-09-08 09:51:52.448 [I] [proxy/tcp.go:82] [cd9f610f66475f3a] [xfg-dev-tech-01] tcp proxy listen port [8080] +2024-09-08 09:51:52.448 [I] [server/control.go:399] [cd9f610f66475f3a] new proxy [xfg-dev-tech-01] type [tcp] success +``` + +#### 2.1.3 开放端口 + +
+ +
+ +- 进入云服务器防火墙,开放访问端口。 + +### 2.2 frpc - 本地安装客户端 + +#### 2.2.1 执行脚本 + +
+ +
+ +- 注意 frpc.toml 是配置服务器的ip和本机应用的ip信息。你需要修改 serverAddr 的 IP 地址,为你的服务器公网 IP 地址。 +- 如果你的 IntelliJ IDEA 带有绿色箭头,且本机也安装了 Docker 那么可以直接安装 frpc 客户端。 + +#### 2.2.2 客户端日志 + +
+ +
+ +```java +2024-09-08 10:00:16.472 [I] [sub/root.go:142] start frpc service for config file [/frpc.toml] +2024-09-08 10:00:16.473 [I] [client/service.go:295] try to connect to server... +2024-09-08 10:00:16.533 [I] [client/service.go:287] [adab2679b41d410f] login to server success, get run id [adab2679b41d410f] +2024-09-08 10:00:16.534 [I] [proxy/proxy_manager.go:173] [adab2679b41d410f] proxy added: [xfg-dev-tech-01 xfg-dev-tech-02] +2024-09-08 10:00:16.550 [I] [client/control.go:168] [adab2679b41d410f] [xfg-dev-tech-01] start proxy success +2024-09-08 10:00:16.550 [I] [client/control.go:168] [adab2679b41d410f] [xfg-dev-tech-02] start proxy success +``` + +#### 2.2.3 服务端日志 + +
+ +
+ +
+ +
+ +- 客户端启动后,你可以进入到服务端 frps 查看日志,这个时候会注册进来客户端信息。 + +**你还可以进入 frp 管理后台查看** + +地址:[http://117.72.37.243:7500/static/#/](http://117.72.37.243:7500/static/#/) - 修改为你的服务器ip进行访问。 + +
+ +
+ +- 这里也可以看到你注册上来的各项信息。 + +## 3. 测试验证 + +### 3.1 部署应用 + +在工程中提供了 xfg-dev-tech-app 的应用,你可以本地执行 docker 脚本进行镜像构建和部署。 + +
+ +
+ +
+ +
+ +- 如图,执行1、2、3步骤,打包、构建和部署应用。 + +### 3.2 访问验证 + +#### 3.2.1 本地访问 + +地址:[http://127.0.0.1:8080/api/test](http://127.0.0.1:8080/api/test) + +
+ +
+ +#### 3.2.2 穿透访问 + +地址:[http://117.72.37.243:8080/api/test](http://117.72.37.243:8080/api/test) + +## 4. 同类产品 + +- [EasyTier 一个简单、安全、去中心化的内网穿透 VPN 组网方案](https://www.easytier.top/) + +
+ +
+ diff --git a/docs/md/road-map/git.md b/docs/md/road-map/git.md new file mode 100644 index 000000000..a033ebd6c --- /dev/null +++ b/docs/md/road-map/git.md @@ -0,0 +1,488 @@ +--- +title: Git +lock: need +--- + +# Git 使用说明和配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +**不会Git操作的伙伴,轻则写不了代码,重则误操作搞丢自己的代码或者干掉别人的代码。** 因为进入公司后,就不只是你一个人在一个工程上写代码,而是所有这个项目组的伙伴都需要在这个工程上写代码,大家要在统一的Git的规范完成代码开发和提交。—— 🤨 不信的话,进入公司乱删个Git分支或者随便任何一个分支提交代码试试。 + +
+ +
+ +**Git的作用是什么?** + +你可以想象下,当你有10个小伙伴都需要在一个 txt 文档里,写一份老师👩🏻‍🏫上课的笔记📒,把信息进行汇总、互相完善、用于课后学习。那么怎么保证大家做的课堂内容都能顺利的写在一份 txt 呢,而且不要互相删除,也不要丢失谁的信息呢? + +这个就是 Git 的用途。Git 是一种分布式版本控制系统,用于高效地管理和跟踪源代码和文件的历史记录,支持多人协作开发及分支管理,提供可靠的版本回退和合并机制,从而提高开发效率和代码质量。 + +## 1. Git 起源故事 + +讲到 Git 就不得不提一下 Linux,因为如果没有 Linux 也就没有 Git 的诞生,这里是有一段 **10天** 写出 Git 的故事! + +众所周知,Linus 于 1991年 创建了开源的 Linux,从此 Linux 系统不断发展壮大。但 Linux 的壮大是靠全世界热心的开发者参与的,不过这么多人在世界各地为 Linux 编写代码,那代码是如何管理的呢?事实是,2002 之前,合并代码的操作都是 Linux 老爷子自己手动合并的! + +但 2000 年的时候,不是已经 [SVN](https://zh.wikipedia.org/wiki/Subversion) 可以使用了吗?但 Linus 坚决反对 CVS、SVN 这些集中式的管理工具,不仅速度差还得联网操作。虽然有商用版的功能好一些,但这与 Linux 开源精神不符。所以 Linus 就不用,看人家这开源精神! + +不过到了 2002年,Linux 都发展10年了,在这么手动的合并代码,让社区的兄弟也苦不堪言,强烈反对。为此,林纳斯·托瓦兹(Linus Torvalds)决定使用 BitKeeper 作为 Linux 内核主要的版本控制系统用以维护代码。BitKeeper 的东家 BitMover 公司,看 Linux 开源的不容易,授权 Linux 社区免费使用这个版本控制系统。 + +哈哈哈,但好景不长!2005年,安德鲁·垂鸠(Andrew Tridgell)写了一个简单程序,可以连接 BitKeeper 的仓库,BitKeeper 著作权拥有者拉里·麦沃伊认为安德鲁·垂鸠对 BitKeeper 内部使用的协议进行逆向工程,决定收回无偿使用 BitKeeper 的许可。Linux 内核开发团队与 BitMover 公司进行磋商,但无法解决他们之间的歧见。林纳斯·托瓦兹决定自行开发版本控制系统替代 BitKeeper ,以10天的时间编写出 git 第一个版本!—— 牛皮! + +有了 Git 以后,GitHub 平台也于2007年10月1日开始开发。网站于2008年2月以beta版本开始上线,4月份正式上线。GitHub 里面的项目可以通过标准的 Git 命令进行访问和操作。—— 这就是 Linux、Git、Github 的故事。 + +## 2. 软件安装 + +Git 是一个软件工具,在使用前需要进行安装。 + +- 地址:[https://git-scm.com/downloads](https://git-scm.com/downloads) - `选择需要的版本下载` 网盘资源:[https://www.alipan.com/s/LqwsNfHRx54](https://www.alipan.com/s/LqwsNfHRx54) - `也提供了 Git 软件,在 dev-ops -> 环境 -> git 下` +- 演示:[https://learngitbranching.js.org/](https://learngitbranching.js.org/) - `可直接查看动画效果,引导式学习Git命令操作。` + +**配置代理** + +```java +git config --global http.proxy ip:port +git config --global https.proxy ip:port +``` + +- 如果你有代理地址可以配置 + +### 2.1 Mac + +```java +# 如果你没有 brew 命令,则需要先安装下;https://brew.sh/index_zh-cn +brew install git +``` + +### 2.2 Windows + +- [32-bit Git for Windows Setup](https://github.com/git-for-windows/git/releases/download/v2.41.0.windows.1/Git-2.41.0-32-bit.exe) +- [64-bit Git for Windows Setup](https://github.com/git-for-windows/git/releases/download/v2.41.0.windows.1/Git-2.41.0-64-bit.exe) + +### 2.3 Linux + +#### Debian/Ubuntu + +获取适用于您的 Debian/Ubuntu 版本的最新稳定版本 + +**如果没有安装npm可以安装** + +``` +# sudo apt update +# sudo apt install nodejs npm +# node -v +# npm -v +``` + +**安装git** + +``` +# apt-get install git +``` + +对于 Ubuntu,此 PPA 提供最新的稳定上游 Git 版本 + +``` +# add-apt-repository ppa:git-core/ppa +# apt update; apt install git +``` + +#### Centos + +```java +sudo yum install git +``` + +## 3. 配置账户 + +当你拿到一个Git的仓库(Github、Gitcode、Gitee、GitLab),让你克隆(git clone)代码的时候,不要`虾呵呵`的上去就点个zip下载。 + +
+ +
+ +- zip 下载的代码,只是当前分支的,下载后用 IntelliJ IDEA 打开也没有 Git 标识,不能进行仓库的pull、push、checkout等操作。 +- 另外对于一些工程内容比较大的项目,直接 zip 下载还可能会失败。 + +>你要做都是配置账户 ssh 秘钥,或者下载的时候输入 git 仓库账号密码(目前很多仓库为了安全,不在支持账号密码方式下载代码了。) + +**注意**:gitcode.com、github.com 都需要创建访问令牌当做账号密码的密码使用。而不是直接使用账号里你设置的那个密码。 + +如 [https://gitcode.com/setting/token-classic](https://gitcode.com/setting/token-classic) + +
+ +
+ +### 1. 配置git账户 + +```java +[root@lavm-aqhgp9nber ~]# git config --global user.name "fuzhengwei" +[root@lavm-aqhgp9nber ~]# git config --global user.email "184172133@qq.com" +``` + +- 用户名和邮箱,更换为你的代码库注册使用的名称和邮箱📮。 + +### 2. 创建ssh秘钥 + +```java +[root@lavm-aqhgp9nber ~]# ssh-keygen -t rsa -C "184172133@qq.com" +Generating public/private rsa key pair. +Enter file in which to save the key (/root/.ssh/id_rsa): +Enter passphrase (empty for no passphrase): +Enter same passphrase again: +Your identification has been saved in /root/.ssh/id_rsa. +Your public key has been saved in /root/.ssh/id_rsa.pub. +The key fingerprint is: +SHA256:lgf+3a1srOvDD0znv+/jiR3iwMaZtxW94k9FI1HzJAk 184172133@qq.com +The key's randomart image is: ++---[RSA 2048]----+ +| Eoo+.| +| ooo| +| . . .o| +| . o ..o| +| S . . ..o| +| . oo+o+ .+| +| .O+=o=.| +| . Bo@+o| +| .+@*BB| ++----[SHA256]-----+ +``` + +- 命令:`ssh-keygen -t rsa -C "184172133@qq.com"` +- 操作:一路敲回车同意就可以了。 + +### 3. 获取ssh秘钥 + +```java +[root@lavm-aqhgp9nber ~]# cat /root/.ssh/id_rsa.pub +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7eYQiDe46Jq5CJ6vXtIiM0hxUKKgCoSFIkSQucsZoLjhW53guxmZ0aR+gUZb/M8Xgrk9WMxc32KqQasFy0xPo86Hxagd40fPz+XdwOyokEJC5He6F1********CkATF2YgEdtcRW2RPICjtLr*******DWkS8ez 184172133@qq.com +``` + +- 命令:`ssh-keygen -t rsa -C "184172133@qq.com"` +- 操作:`Your public key has been saved in /root/.ssh/id_rsa.pub.` 按照提示地址,获取 pub 公钥。如果是linux就通过 cat 获取。如果是本机电脑就按照路径打开。如,我的地址(你要更换为你的):`cat /Users/fuzhengwei/.ssh/id_rsa.pub` +- 注意:`.ssh` 点开头的文件夹是隐藏文件,Windows/Mac 都可以设置查看隐藏文件。Windows 是设置里配置,Mac 是通过 `Shift + Command + .` 开启和关闭查看隐藏文件。 + +### 4. 配置ssh秘钥 + +#### 4.1 gitcode.net + +
+ +
+ +- 秘钥配置地址:[https://gitcode.net/-/profile/keys](https://gitcode.net/-/profile/keys) + +#### 4.2 github.com + +
+ +
+ +- 秘钥配置地址:[https://github.com/settings/keys](https://github.com/settings/keys) + +--- + +其他的各个代码库平台也都类似,找到配置 ssh 秘钥的地方,把 id_rsa.pub 内容填充进去即可。 + +## 4. 检出代码 - 命令 + +通过在公司工作,大家会说检出代码,不会说下载。因为检出对应的是 git clone 命令,而下载是 http 直接点击链接。在我们使用一些图形化的 git 界面操作代码检出的时候,走的也是 git clone 命令。 + +### 4.1 检出地址 + +
+ +
+ +- 地址:选择工程的 ssh 地址,这个是你配置 ssh 秘钥后的检出方式。 + +### 4.2 检出命令 + +#### 4.2.1 默认检出(master) + +```java +git clone git@github.com:fuzhengwei/openai-code-review.git +``` + +- 你可以复制你需要的工程地址进行检出。 + +#### 4.2.2 指定分支 + +```java +git clone -b 240720-xfg-init-project git@github.com:fuzhengwei/openai-code-review.git +``` + +- 添加 `-b 分支名`,可以指定分支名称检出。 + +#### 4.2.3 其他命令 + +- git branch:查看当前工程分支 +- git branch -r:查看远程分支列表 +- git pull:拉取分支最新代码 +- git push:推送本地变更的代码。【这样的操作有图形化界面,更方便】 +- git fetch:获取远程仓库最新分支变动,这个很有用。在你想操作分支创建新或者看其他人的分支时候,先操作下 git fetch 看下最新的分支变动。 +- git merge origin/master:将远程分支的 master 代码合并到本地分支 master 上。【类似这样的操作有图形化界面,点击即可】 + +## 5. 检出代码 - IntelliJ IDEA + +IntelliJ IDEA 本身就提供了 Git 的图形化操作,也是最简单最常用的方式。只要你的代码是通过 Git 检出的,那么通过配置 Git 的 IntelliJ IDEA 打开工程,就会自动的被 Git 管理。 + +### 5.1 配置 Git + +
+ +
+ +- 像是 Mac 电脑会自动默认的配置好 IntelliJ IDEA Git 直接使用即可,如果你的电脑在 IntelliJ IDEA 打开工程后,提示没有 Git 则可进入设置手动配置。 +- 配置后,点击 Test 可以测试出 Git 的版本。 + +### 5.2 检出代码 + +#### 5.2.1 方式1;打开工程前 + +
+ +
+ +#### 5.2.2 方式2;打开工程后 + +
+ +
+ +- 打开工程后,也可以通过菜单栏中的 Git 进行检出操作。 + +### 5.3 图形界面 + +#### 5.3.1 分支使用 + +
+ +
+ +#### 5.3.2 提交代码 + +
+ +
+ +#### 5.3.3 查看记录 + +
+ +
+ +#### 5.3.4 查看对比 + +
+ +
+ +- 这个代码对比非常适合代码评审,也可以用于自己学习代码。可以知道任意两个分支的代码差异,也就知道了代码上一次是什么样,这一次是做了什么开发。 + +#### 5.3.5 合并分支 + +在公司中很多时候是大家一起在一个工程开发代码,那么这个时候就会涉及合并代码的。如果有多人共同开发一个接口方法,就会在合并的时候产生冲突。所以要特别注意。 + +注意,📢在Git中,`merge`和`rebase`是两种用于合并分支变更的方法。每种方法都有其特定的用途和行为: + +Merge + +- **概念**: `merge`命令用于将一个分支的更改合并到当前分支,通常会生成一次新的合并提交(commit),从而保留分支历史。 +- **历史记录**: 使用`merge`会保留所有开发历史记录,包括分支点,这使得历史记录图呈现出一个分叉和合并的树状结构。 +- **使用场景**: + - 当你想保留完整的历史记录,了解如何以及何时进行了分支。 + - 通常用于将分支合并到主分支(如`main`或`master`)时,以保留分支的历史上下文。 +- **命令**: + ```bash + git checkout main + git merge feature-branch + ``` + +Rebase + +- **概念**: `rebase`命令用于重新定位一系列的提交,可以将分支上的更改申请到另一分支的顶端,因而能得到更线性的历史。 +- **历史记录**: Rebase重新写了提交历史(改变了提交的父提交),这会导致更线性的提交历史,从而更清晰,但会丢失分支合并信息。 +- **使用场景**: + - 当你希望提交历史更加简洁和线性。 + - 合并特性分支到主分支前用以避免杂乱的分支合并历史。 + - 需要小心在共享分支上使用,因为重写的提交历史会影响他人。 +- **命令**: + ```bash + git checkout feature-branch + git rebase main + ``` + +选择何时使用 + +- **Merge**适合用于保持完整的提交历史,不改变现有提交的哈希。 +- **Rebase**适合用于清理提交历史,通常在提交尚未推送至共享存储库时使用。 + +一般情况下,对于已对外分享的分支,尤其是共享存储库中的`main`或`master`分支,尽量避免使用`rebase`。而对于个人开发的特性分支,`rebase`可以帮助维护一个清晰的提交历史。 + +**普通合并** + +
+ +
+ +- 在实际的开发中,大家承接新的需求,会从master拉一个新的分支。拉取后,开始编写代码,完成开发后提交。 +- 之后切换到master分支,通过把自己的开发的分支合并回master分支进行提交。 +- 注意:如果多人开发,同时修改一个类,可能会引起合并冲突,这个审核要点开类,查看冲突进行合并,不要把自己和他人的代码合并丢失。 + +**冲突合并** + +
+ +
+ +1. 选择,你要从哪个分支合并到 test 分支。右键选择 Merge into test +2. 如果你合并到test分支的代码,有其他人也在同一行做了改变或者格式化了代码,就会弹出一个合并冲突。这个时候你需要点 Merge 进行合并。 +3. 在点击 Merge 后,你会看到具体冲突的代码是什么,你可以有选择的从左右合并到中,最后点击 Apply。这个时候要注意不要把让别人的代码合并丢喽。 +4. 合并完的代码,不要直接 push,你要先本地 install 看是否可以打包。以及如果可以运行的话,可以本地先跑一下。 最后 push 提交合并代码即可。 + +#### 5.3.6 回滚分支 + +回滚提交和回滚push + +
+ +
+ +如果出现了合并代码冲突后,丢失了代码,那么这个时候一般要进行回滚操作,重新合并。 + +虽然 Git 提供了回滚代码的功能,但一定要谨慎使用。怎么谨慎?第一个谨慎就是 push 的代码一定确保可以构建和运行,否则不要 push!第二个谨慎是要回滚代码,需要和团队中对应的伙伴打招呼,避免影响别人测试或者上线。 + +
+ +
+ +1. 先选择要在哪个分支的哪次提交上进行回滚。这里选择的是 test 分支上的提交进行回滚。 +2. 这里选择 Hard 回滚。因为我们所有的都是合并到 test 分支,所以 test 分支丢失也没问题。可以重新合并。但要和同组伙伴提前说明。 +3. 回滚后,你会看到代码只剩下从回滚往下的提交内容了。 +4. 回滚后,你不能直接 push 提交了,这个之后会报错;`fast-forward` 因为此时本地分支落后于远程分支。 +5. 所以要通过 `git push origin HEAD --force` 进行强制提交。或者你可以把 test 的远程分支删掉,之后在提交。 + +## 6. 提交工程 - IntelliJ IDEA + +那么首次创建的工程怎么提交到代码库呢?🤔 + +### 6.1 创建本地仓库 + +
+ +
+ +- Create Git repository 创建一个本地的暂存库。你可以把开发的内容暂时提交到本地仓库中。 + +### 6.3 提交本地代码 + +
+ +
+ +- 把本地仓库提交到本地代码库待提交列表中。 +- 后续你在创建的代码,会默认自动加入进来。*如果你是通过打开文件夹复制进去的,不会被添加,需要手动 +Add* +- 添加后就可以参考 5.3 中的操作提交代码了。 + +### 6.4 创建远程仓库 + +你可以选择 GitHub/GitCode/Gitee/GitLab 任意地方创建自己的工程库。教程; +- [https://bugstack.cn/md/road-map/github.html](https://bugstack.cn/md/road-map/github.html) +- [https://bugstack.cn/md/road-map/gitcode.html](https://bugstack.cn/md/road-map/gitcode.html) +- [https://bugstack.cn/md/road-map/gitee.html](https://bugstack.cn/md/road-map/gitee.html) + +
+ +
+ +### 6.5 推送本地工程 + +
+ +
+ +
+ +
+ +- 最后推送完成就可以看到自己的代码进入仓库中了! + +## 7. 操作界面 - git-gui + +Git 附带了用于提交 ( [git-gui](https://git-scm.com/docs/git-gui) ) 和浏览 ( [gitk](https://git-scm.com/docs/gitk) ) 的内置 GUI 工具,但也有一些第三方工具可供用户寻求特定于平台的体验。 + +
+ +
+ +- Git 提供了操作的客户端界面,你可以按需下载使用。 +- 地址:[https://git-scm.com/docs/git-gui](https://git-scm.com/docs/git-gui) + +## 8. 分支命名 + +不同公司中对Git的使用分支命名规范也略有差异,不过整体都会分为;`上线`、`预发`、`开发`、`测试`,这样几个分支。如图是一种比较简单使用的拉取分支方式。 + +
+ +
+ +- master/main 作为主分支,不可直接修改代码代码,只能从分支合并到主分支进行进行提交。同时,master 分支的合并需要进行审批,审批后才能合并。 +- 开发前,先从 master 分支,拉一个开发分支。`2024/10/11/xfg-xxx` 使用带有斜线的分支命名会自动创建文件夹,对于多人开发的项目,可以直接归档。 +- 后开发,也就是研发已经完成了本地的验证。进行测试时,可以把研发的开发分支合并到 test 分支,提交、部署、测试。遇到测试bug,需要回到可发分支修改代码,之后合并到 test 分支部署验证。 +- pre/release 预发分支,用于测试完成后,把研发的开发分支合并到预发分支进行预发上线,上线后测试人员进行验证。最终完成验证后,把开发分支合并到 master 分支,并需要由架构师对合并代码审批通过。最后进行上线开量验证。 +- 如果是修复bug的,可以添加一个 `fix-用户名缩写-具体功能` + +## 9. 提交规范 + +保持一个标准的统一的规范提交代码,在后续的评审、检查、合并,都会非常容易处理。 + +
+ +
+ +**提交规范**:`type:【需求名】desc #id` 如:`feat:【抽奖算法】O1、Ologn 时间复杂度算法实现 #需求id(github pr/行云等会有自动关联)` *参考Commit message 规范* + +```java +# 主要type +feat: 增加新功能 +fix: 修复bug + +# 特殊type +docs: 只改动了文档相关的内容 +style: 不影响代码含义的改动,例如去掉空格、改变缩进、增删分号 +build: 构造工具的或者外部依赖的改动,例如webpack,npm +refactor: 代码重构时使用 +revert: 执行git revert打印的message + +# 暂不使用type +test: 添加测试或者修改现有测试 +perf: 提高性能的改动 +ci: 与CI(持续集成服务)有关的改动 +chore: 不修改src或者test的其余修改,例如构建过程或辅助工具的变动 +``` + +> 前提通知,谨慎回滚。如果是回滚 Master 更要特别小心,别把工作回滚丢了。 + +## 10. 操作手册 + +- 文档:[https://git-scm.com/book/zh/v2](https://git-scm.com/book/zh/v2) - `可以直接对照着操作,练习命令` +- PDF:[https://github.com/progit/progit2-zh/releases/download/2.1.62/progit.pdf](https://github.com/progit/progit2-zh/releases/download/2.1.62/progit.pdf) +- 演示:[https://learngitbranching.js.org/](https://learngitbranching.js.org/) - `可直接查看动画效果,引导式学习Git命令操作。` + +--- + +
+
+ +- 中文PDF,直接深度学习Git操作!下载:**微信公众号「bugstack虫洞栈」回复「gitbook」** diff --git a/docs/md/road-map/gitcode.md b/docs/md/road-map/gitcode.md new file mode 100644 index 000000000..015a9dd70 --- /dev/null +++ b/docs/md/road-map/gitcode.md @@ -0,0 +1,148 @@ +--- +title: Gitcode +lock: need +--- + +# Gitcode 使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +Gitcode 也是一个非常优秀的代码仓库管理工具,同时还他有很多其他功能。他是 CSDN 平台提供的仓库管理工具。也是非常不错的仓库。 + +- 仓库配置 SSH 参考:[https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) + +## 一、账号注册 + +- 官网:[https://gitcode.net](https://gitcode.net) +- 注册:[https://gitee.com/signup?redirect_to_url=%2F](https://gitee.com/signup?redirect_to_url=%2F) + +
+ +
+ +- 它的注册方式直接使用会跳转到 CSDN 直接使用即可。 +- 注册后你就会有一个自己的 Gitee,如:[https://gitcode.com/fuzhengwei](https://gitcode.com/fuzhengwei) + +## 二、仓库创建 + +### 1. 创建类型 + +
+ +
+ +- 创建仓库,操作步骤与 Github 基本一致。 +- 在新建项目的时候,同样也可以从其他仓库导入项目。 + +### 2. 创建操作 + +#### 2.1 创建项目 + +**创建选项** + +
+ +
+ +- 项目名称;也就是你最后的项目名称 +- URL&标识:选择把项目创建到你的那个组织下,以及路径。一般路径和仓库名称最好保持一致。 +- 项目介绍:描述这个项目的。 +- 创建类型:你可以选择私有和公开。 + +**创建完成** + +
+ +
+ +- 示例:[https://gitcode.net/fuzhengwei/xfg-frame-ddd](https://gitcode.net/fuzhengwei/xfg-frame-ddd) + +#### 2.2 创建组织 + +
+ +
+ +- 组织相当于是一个额外的空间,而你是这个组织的管理者。它必须全局唯一。 +- 之后你可以在创建项目的时候,选择到组织空间,这样项目就会创建到组织里了。 +## 三、代码提交 + +代码提交到仓库,核心在于把本地代码和远程仓库关联起来。如果你是直接从远程仓库地址,检出到本地的,那么就不需要再次关联了。本场景适用于,你本地有代码库,需要首次提交到远程仓库中。 + +### 1. 创建本地仓库 + +
+ +
+ +- 首先你需要打开 IntelliJ IDEA 工程,选择 VCS 创建一个本地仓库的Git仓库。Create Git Repository + +### 2. 关联远程仓库 + +
+ +
+ +- 打开你的 Gitee 仓库对应的项目,复制项目 HTTPS 地址,粘贴到 IntelliJ IDEA 项目的远程仓库配置中,点击 OK 即可。 +- Manage Remotes 可以设置多个仓库,在push代码的时候,选择对应的地址提交即可。 + +### 3. 项目代码提交 + +#### 3.1 添加 + +
+ +
+ +- 在工程上右键,把你需要提交的文件,选择 +Add 注意不要把一些不需要提交的文件,也提交上去。 +- 你可以先创建一个 `Add to .gitignore` 它可以屏蔽掉一些不要的文件。—— 在 Github 创建工程的时候也有此文件。你也可以先更新 pull 拉取下来。 + +#### 3.2 提交 + +
+ +
+ +- 提交代码快捷键:`Ctrl + K` - 提交到本地仓库 + +#### 3.3 推送 + +
+ +
+ +- 推送代码快捷辑:`Ctrl + Shift + K` —— 提交到远程仓库 + +## 四、提交规范 + +**分支命名**:`日期_姓名首字母缩写_功能单词`,如:`230708_xfg_buildFramework` + +**提交规范**:`作者,type: desc` 如:`小傅哥,fix:修复查询用户信息逻辑问题` *参考Commit message 规范* + +``` +# 主要type +feat: 增加新功能 +fix: 修复bug + +# 特殊type +docs: 只改动了文档相关的内容 +style: 不影响代码含义的改动,例如去掉空格、改变缩进、增删分号 +build: 构造工具的或者外部依赖的改动,例如webpack,npm +refactor: 代码重构时使用 +revert: 执行git revert打印的message + +# 暂不使用type +test: 添加测试或者修改现有测试 +perf: 提高性能的改动 +ci: 与CI(持续集成服务)有关的改动 +chore: 不修改src或者test的其余修改,例如构建过程或辅助工具的变动 +``` + +--- + +**此外**,Github 是一个大宝藏,你可以通过检索,找到特别多的优质项目、代码、设计。如:[https://gitcode.net/fuzhengwei](https://gitcode.net/fuzhengwei) diff --git a/docs/md/road-map/gitee.md b/docs/md/road-map/gitee.md new file mode 100644 index 000000000..108a50739 --- /dev/null +++ b/docs/md/road-map/gitee.md @@ -0,0 +1,152 @@ +--- +title: Gitee +lock: need +--- + +# Gitee 使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +Gitee 是国内的 Github,也是一个非常优秀的代码仓库管理工具,同时还他有很多其他功能。比如;项目协同、代码扫描、持续集成、测试管理、多云部署以及能效相关的功能。这些功能其实更适合一些中小厂的企业来使用,可以减少自身的建设成本。 + +- 仓库配置 SSH 参考:[https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) + +## 一、账号注册 + +- 官网:[https://gitee.com/](https://gitee.com/) +- 注册:[https://gitee.com/signup?redirect_to_url=%2F](https://gitee.com/signup?redirect_to_url=%2F) + +
+ +
+ +- 你可以按照Gitee的注册提示,一步一步的填写信息就可以完成账号注册了。 +- 注册后你就会有一个自己的 Gitee,如:[https://gitee.com/fustack](https://gitee.com/fustack) + +## 二、仓库创建 + +### 1. 创建类型 + +
+ +
+ +- 创建仓库,操作步骤与 Github 基本一致。 +- 从 Github/GitLab 导入仓库,这个功能非常好用。如果你拿到了一个 Github 的地址,但访问不了。这个时候你可以通过这个功能,把项目导入到 Gitee 就可以访问了。 + +### 2. 创建操作 + +#### 2.1 创建项目 + +**创建选项** + +
+ +
+ +- 仓库名称;也就是你最后的项目名称 +- 归属&路径:选择把项目创建到你的那个组织下,以及路径。一般路径和仓库名称最好保持一致。 +- 仓库介绍:描述这个项目的。 +- 创建类型:默认情况下你只能创建一个私有项目,在审核后,在修改为公开。 +- 其他设置;你可以选择一些默认的信息初始化仓库,如;gitignore、开源许可协议、模板、分支等。 + +**创建完成** + +
+ +
+ +- 示例:[https://gitee.com/fustack/xfg-frame-ddd](https://gitee.com/fustack/xfg-frame-ddd) + +#### 2.2 创建组织 + +
+ +
+ +- 组织相当于是一个额外的空间,而你是这个组织的管理者。它必须全局唯一。 +- 之后你可以在创建项目的时候,选择到组织空间,这样项目就会创建到组织里了。 + +## 三、代码提交 + +代码提交到仓库,核心在于把本地代码和远程仓库关联起来。如果你是直接从远程仓库地址,检出到本地的,那么就不需要再次关联了。本场景适用于,你本地有代码库,需要首次提交到远程仓库中。 + +### 1. 创建本地仓库 + +
+ +
+ +- 首先你需要打开 IntelliJ IDEA 工程,选择 VCS 创建一个本地仓库的Git仓库。Create Git Repository + +### 2. 关联远程仓库 + +
+ +
+ +- 打开你的 Gitee 仓库对应的项目,复制项目 HTTPS 地址,粘贴到 IntelliJ IDEA 项目的远程仓库配置中,点击 OK 即可。 +- Manage Remotes 可以设置多个仓库,在push代码的时候,选择对应的地址提交即可。 + +### 3. 项目代码提交 + +#### 3.1 添加 + +
+ +
+ +- 在工程上右键,把你需要提交的文件,选择 +Add 注意不要把一些不需要提交的文件,也提交上去。 +- 你可以先创建一个 `Add to .gitignore` 它可以屏蔽掉一些不要的文件。—— 在 Github 创建工程的时候也有此文件。你也可以先更新 pull 拉取下来。 + +#### 3.2 提交 + +
+ +
+ +- 提交代码快捷键:`Ctrl + K` - 提交到本地仓库 + +#### 3.3 推送 + +
+ +
+ +- 推送代码快捷辑:`Ctrl + Shift + K` —— 提交到远程仓库 + +## 四、提交规范 + +**分支命名**:`日期_姓名首字母缩写_功能单词`,如:`230708_xfg_buildFramework` + +**提交规范**:`作者,type: desc` 如:`小傅哥,fix:修复查询用户信息逻辑问题` *参考Commit message 规范* + +``` +# 主要type +feat: 增加新功能 +fix: 修复bug + +# 特殊type +docs: 只改动了文档相关的内容 +style: 不影响代码含义的改动,例如去掉空格、改变缩进、增删分号 +build: 构造工具的或者外部依赖的改动,例如webpack,npm +refactor: 代码重构时使用 +revert: 执行git revert打印的message + +# 暂不使用type +test: 添加测试或者修改现有测试 +perf: 提高性能的改动 +ci: 与CI(持续集成服务)有关的改动 +chore: 不修改src或者test的其余修改,例如构建过程或辅助工具的变动 +``` + +--- + +**此外**,Github 是一个大宝藏,你可以通过检索,找到特别多的优质项目、代码、设计。如:[https://github.com/fuzhengwei](https://github.com/fuzhengwei) + + diff --git a/docs/md/road-map/github-actions-workflows.md b/docs/md/road-map/github-actions-workflows.md new file mode 100644 index 000000000..6d75985b6 --- /dev/null +++ b/docs/md/road-map/github-actions-workflows.md @@ -0,0 +1,227 @@ +--- +title: Docker 镜像构建 - Github Action +lock: need +--- + +# Docker 镜像构建 - Github Action + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +最近看到不少研发伙伴,因为 DockerHub 拉取镜像失败,不能愉快的「👨🏻‍💻凿代码」了而头疼。但这种也就只能拦住`小卡拉米`程序员,高阶的码农有太多种方式让自己愉快的敲代码。就像 Maven 很慢,我可以自建 Maven 仓库。GitHub 很慢,我们可以自建 Gitlab。同样 Docker 很慢,我们还是可以自建 Docker 仓库哇🤩。—— 一套免费自建方案,满足个人开发诉求。 + +
+ +
+ +**小白基础很差,每次收获都很大!** + +很多我们自己不能直接访问的服务,在很多互联网公司中都是可以直接访问的,包括;GitHub、谷歌,因为这些公司都是有专门备案的可信的专线网络。这也就是为什么很多机智的伙伴,有 github 地址,但访问很慢的时候,会直接在 gitee 仓库,通过导入 github 地址的方式进行访问。因为 gitee 是有对应的网络专线的,他们可以来拉取代码(并做安全审核),让我们可以放心使用。 + +嘿嘿,那么就有办法了。同样的访问不了 DockerHub,拉取不了进项。但阿里云有镜像仓库呀。阿里云可以访问 GitHub,GitHub 也可以访问阿里云。那么简单了,我们可以基于 GitHub 的 Actions 服务,执行构建和推送镜像的脚本,把需要的镜像推送到阿里云 Docker 镜像仓库,这不就可以满足自己使用了吗! + +>接下来,小傅哥就教大家搞一下这个事情。—— 学到手的全是技术! + +## 一、GitHub Actions 是什么 + +我把 GitHub Actions 当成一种免费的云服务器,我估计不少程序员也都这么理解!因为它可以执行出非常多的骚操作! + +**正经的!** GitHub Actions 是 GitHub 提供的一项持续集成和持续交付(CI/CD)服务。它允许开发者在代码库中自动化各种任务,例如构建、测试和部署代码。通过定义工作流(workflow),开发者可以在特定事件(如代码推送、拉取请求等)发生时自动触发这些任务。 + +嘿嘿,你看,其实还是一台可以执行任意脚本的云服务器!除了说的 CI/CD 持续交付,也包括它可以定时的执行的任意脚本,比如;定时执行 Java 的 Main 函数,调用下 openai 接口,给自己的公众号每天推送一个最新的技术信息。或者是各类签到就送东西的平台,定时+随机时间签到获取金币/京豆/积分。 + +## 二、GitHub Actions 的使用 + +小傅哥不只是一个后端Java工程师,同时也能开发点前端,再搞点实施运维的事情。这就让我对很多问题,都有了很多的解决方案。 + +所以在小傅哥星球「码农会锁」带着大家实战的大营销平台项目,就提供了多种的 Docker 部署方式。如;本地构建再推送镜像到 DockerHub,鉴于不少伙d伴本地安装 Docker 费劲,又提供了直接把一台云服务器配置成开发机器,在云服务器直接 Maven 编译和构建镜像,这样就满足了大部分伙伴的开发部署诉求。 + +当为了让伙伴得到更全面的学习,小傅哥最近又开始提供了 GitHub Actions CI/CD 持续交付的部署方式。只要你提交代码,即可自动进行构建和完成镜像的推送,并且是同时把镜像推送到 DockerHub 和 阿里云的 Docker 镜像中。接下来小傅哥就告诉大家是怎么操作的。 + +### 1. 阿里云镜像仓库配置 + +#### 1.1 创建个人实例 + +- 地址:[https://cr.console.aliyun.com/cn-hangzhou/instances](https://cr.console.aliyun.com/cn-hangzhou/instances) + +
+ +
+ +#### 1.2 查看连接凭证 + +- 地址:[https://cr.console.aliyun.com/cn-hangzhou/instance/credentials](https://cr.console.aliyun.com/cn-hangzhou/instance/credentials) + +
+ +
+ +#### 1.3 镜像使用说明 - 推送后即可使用 + +- 地址:[https://cr.console.aliyun.com/repository/cn-hangzhou/fuzhengwei/big-market-app/details](https://cr.console.aliyun.com/repository/cn-hangzhou/fuzhengwei/big-market-app/details) - `修改为你的地址进行登录` +- 使用:`docker pull registry.cn-hangzhou.aliyuncs.com/xfg-studio/big-market-app:3.0` + +
+ +
+ +### 2. GitHub 仓库 Actions CI/CD 配置 + +
+ +
+ +- 首先,点开你的 GitHub 中需要使用 Actions 的项目,点击 Settings 设置。 +- 之后,添加阿里云镜像仓库配置,包括用户名和密码。[https://cr.console.aliyun.com/cn-hangzhou/instance/credentials](https://cr.console.aliyun.com/cn-hangzhou/instance/credentials) +- 最后,配置 DockerHub 用户名和 `New Access Token`。[https://hub.docker.com/settings/security](https://hub.docker.com/settings/security) + +### 3. 工程脚本配置 + +#### 3.1 工程目录 + +
+ +
+ +- 新增加了一个 ActionDockerfile 方便管理路径。 + +#### 3.2 Dockerfile + +```java +# 基础镜像 +FROM openjdk:8-jre-slim + +# 作者 +MAINTAINER xiaofuge + +# 配置 +ENV PARAMS="" + +# 时区 +ENV TZ=PRC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# 添加应用 +ADD ./big-market-app/target/big-market-app.jar /big-market-app.jar + +ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /big-market-app.jar $PARAMS"] +``` + +#### 3.3 workflows + +```java +name: Maven Build and Docker Image CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + distribution: 'adopt' + java-version: '8' + + - name: Dependies Cache + uses: actions/cache@v2 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Build with Maven + run: | + mvn clean package -Dmaven.test.skip=true + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Login to DockerAlibaba + run: | + docker login --username=${{ secrets.ALIYUN_REGISTRY_USER }} --password=${{ secrets.ALIYUN_REGISTRY_PASSWORD }} registry.cn-hangzhou.aliyuncs.com + + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: ./big-market-app/ActionDockerfile + # 所需要的体系结构,可以在 Available platforms 步骤中获取所有的可用架构 https://docs.docker.com/build/building/multi-platform/ + platforms: linux/amd64 + # 镜像推送时间 + push: ${{ github.event_name != 'pull_request' }} + # 给清单打上多个标签 + tags: | + fuzhengwei/big-market-app:3.0 + fuzhengwei/big-market-app:latest + + - name: Tag image for Alibaba Cloud + run: | + docker tag fuzhengwei/big-market-app:3.0 registry.cn-hangzhou.aliyuncs.com/fuzhengwei/big-market-app:3.0 + docker tag fuzhengwei/big-market-app:latest registry.cn-hangzhou.aliyuncs.com/fuzhengwei/big-market-app:latest + + - name: Push to Alibaba Cloud Container Registry + run: | + docker push registry.cn-hangzhou.aliyuncs.com/fuzhengwei/big-market-app:3.0 + docker push registry.cn-hangzhou.aliyuncs.com/fuzhengwei/big-market-app:latest +``` + +- 这里就是 GitHub 的 Actions 执行脚本,包括;监听 master 分支、拉取代码、设置环境、Maven 打包、构建镜像、推送镜像分别到 DockerHub 和 阿里云镜像仓库。 +- 这样我们每次需要新的打包镜像时,可以直接在这里修改下版本号,提交即可。 + +### 4. 启动运行 + +GitHub 的 Actions 是自动执行的,只要监听到 Master 分支有变化,即可自动执行并推送镜像到仓库中。 + +
+ +
+ +
+ +
+ +> 运行到这里,你就可以在 DockerHub、阿里云仓库同时看到自己的镜像文件了。想咋用就咋用。 + +## 三、GitHub Actions 其他玩法 + +### 1. 同步镜像文件 + +- 地址:[https://github.com/fuzhengwei/docker-image-pusher](https://github.com/fuzhengwei/docker-image-pusher) + +``` +mysql:8.0.32 +phpmyadmin:5.2.1 +redis:6.2 +spryker/redis-commander:0.8.0 +rabbitmq:3.12.9 +``` + +1. 在 `images.txt` 添加你需要的镜像(PR方式提交),你可以从 https://hub.docker.com/ 搜索需要的镜像后添加。 +2. 新添加镜像,需要等待1分钟同步。之后通过命令 `docker pull registry.cn-hangzhou.aliyuncs.com/xfg-studio/mysql` 拉取你需要的镜像,如果有版本号,可以添加。如;`mysql:8.0.32` + +### 2. 自动部署博客 + +- 地址:[https://github.com/fuzhengwei/xfg-dev-tech-blog](https://github.com/fuzhengwei/xfg-dev-tech-blog) +- 视频:[https://www.bilibili.com/video/BV1ri421a7Aj/](https://www.bilibili.com/video/BV1ri421a7Aj/) + +
+ +
diff --git a/docs/md/road-map/github-models.md b/docs/md/road-map/github-models.md new file mode 100644 index 000000000..08fbfcac4 --- /dev/null +++ b/docs/md/road-map/github-models.md @@ -0,0 +1,212 @@ +--- +title: Github Models +lock: need +--- + +# Github Models(AI接口),申请使用教程 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +现在做 AI 应用开发的小伙伴越来越多了,随之而来的是对 LLM 大模型接口的使用依赖。一般的模型,可能会让程序出现幻觉。而高质量的模型,大部分是需要付费,比如 `openai/gpt-5`、`azureml-xai/grok3`、`azureml/Phi-4` 等。**那怎么办!?🤔** + +
+ +
+ +这些免费的不错的大模型(LLM),也都是有对应的频次和最大出入/输出token的限制。虽然不会让咱们玩命的调用测试。但在程序开发初期,对于单个功能的验证还是非常方便的。接下来,小傅哥就给大家演示下,关于 Github 免费提供的大模型如何配置使用。 + +## 一、模型说明 + +### 1. 官网地址 + +官网:[https://github.com/marketplace?type=models](https://github.com/marketplace?type=models) + +
+ +
+ +在 Github marketplace 下提供了非常多的免费的 LLM 使用,gpt-5、gpt-4.1 都是有的,具备完整的测试功能。有时候还会新增加其他模型。 + +### 2. 频次限制 + +地址:[https://docs.github.com/en/github-models/use-github-models/prototyping-with-ai-models#rate-limits](https://docs.github.com/en/github-models/use-github-models/prototyping-with-ai-models#rate-limits) + +
+ +
+ +- 每日150次、每分钟15次,每次token,8000进、4000出。对于单个模型的验证是没问题的,不过对于复杂的 ai agent 智能体是跑不动的。所以,这块测试的话,要分开验证。之后对于全流程的,在购买 api 进行测试即可。 +- 如果小伙伴还觉得,每分钟15次,有点不够干的,那你可以弄10个账号,通过 nginx 负载下,这样基本每分钟150次,一天1500次还是很够用的。 + +## 二、模型使用 + +### 1. 测试工程 + +
+ +
+ +- 地址:[https://github.com/fuzhengwei/xfg-dev-tech-github-models](https://github.com/fuzhengwei/xfg-dev-tech-github-models) +- 说明:这里小傅哥准备了一套基于 Spring AI 的测试工程,来验证 Github LLM 的使用。你可以下载后,替换 token/apikey 即可调用验证。 +- 环境:jdk 17、maven 3.8.x、spring ai 1.1.0 + +### 2. 对接申请 + +#### 2.1 申请token + +地址:https://github.com/settings/tokens/new + +
+ +
+ +- 在 Tokens 下,添加 Note 描述和过期时间,完事后不用加任何其他权限,拉到最后设置点击 `Generate token` 生成令牌即可。 +- 生成后复制你的 Token,复制后关闭就看不见了。只有删除和创建新的。 + +#### 2.2 查看curl(api) + +地址:[https://github.com/marketplace/models/azure-openai/gpt-4-1/playground](https://github.com/marketplace/models/azure-openai/gpt-4-1/playground) + +
+ +
+ +在左侧你可以选择需要使用的模型,之后进行对话验证。之后,在右侧有一个 `Use this model` 这里可以获得模型 API 请求信息。 + +
+ +
+ +你现在单独验证 api,在整个程序测试前,要确保你的 api 是可用的。 + +### 3. 测试验证 + +```java +public class ApiTest { + + public static void main(String[] args) throws Exception { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Resource resource = new ClassPathResource("dog.png", classLoader); + + OpenAiApi openAiApi = OpenAiApi.builder() + .baseUrl("https://models.github.ai/inference") + // apikey 要替换为你的,在 https://github.com/settings/tokens 创建即可。 + .apiKey("ghp_FfiwBkVunYDQyhIwaQSccVw******") + .completionsPath("/chat/completions") + .embeddingsPath("/embeddings") + .build(); + + ChatModel chatModel = OpenAiChatModel.builder() + .openAiApi(openAiApi) + .defaultOptions(OpenAiChatOptions.builder() + .model("openai/gpt-4.1") + .build()) + .build(); + + // 模型测试,没问题可以识别图片 + ChatResponse response = chatModel.call(new Prompt( + UserMessage.builder() + .text("请描述这张图片的主要内容,并说明图中物品的可能用途。") + .media(Media.builder() + .mimeType(MimeType.valueOf(MimeTypeUtils.IMAGE_PNG_VALUE)) + .data(resource) + .build()) + .build())); + + System.out.println("测试结果" + JSON.toJSONString(response)); + + } + +} +``` + +```java +测试结果{"metadata":{"empty":false,"id":"chatcmpl-CzHhVYaXPv3HjJ3oKE4YKPamPQRQB","model":"gpt-4.1-2025-04-14","rateLimit":{"requestsLimit":1000,"requestsRemaining":996,"tokensLimit":1000000,"tokensRemaining":980423},"usage":{"completionTokens":0,"nativeUsage":{},"promptTokens":0,"totalTokens":0}},"result":{"metadata":{"contentFilters":[],"empty":true,"finishReason":"STOP"},"output":{"media":[],"messageType":"ASSISTANT","metadata":{"role":"ASSISTANT","messageType":"ASSISTANT","finishReason":"STOP","refusal":"","index":0,"annotations":[{"$ref":"$.metadata.rateLimit.usage.nativeUsage"}],"id":"chatcmpl-CzHhVYaXPv3HjJ3oKE4YKPamPQRQB"},"text":"这张图片的主要内容是一只卡通风格的小狗。小狗是棕色的,脸部较大,眼睛圆而突出,表情看起来有些疑惑或惊讶,嘴巴微微张开,尾巴翘起。整个形象简洁可爱,线条和颜色运用都比较简单。\n\n**物品的可能用途:**\n1. **图标或表情包**:这种卡通形象常用于聊天软件中的表情包或头像,传达惊讶、困惑等情感。\n2. **儿童绘本或动画角色**:可以作为儿童图书或动画中的形象角色,吸引小朋友的注意力。\n3. **宠物相关宣传设计**:可用于宠物店、宠物产品的宣传海报、包装或logo,增加亲和力。\n4. **教育材料**:在教学课件或教育类APP中,用于引导、示范或者增加趣味性。\n\n整体而言,这是一种很常见且可爱的卡通动物形象,主要用于与宠物、儿童或者情感表达相关的领域。","toolCalls":[]}},"results":[{"$ref":"$.metadata.rateLimit.usage.nativeUsage.result"}]} +``` + +- 这是一段基于 Spring AI 配置 github llm api的使用方式,尤其注意,baseUrl、apiKey、completionsPath、embeddingsPath、openai/gpt-4.1,这些配置信息和你直接使用管的 gpt 配置是有差异的。 +- 本案例,是识别图片的用途,你也可以验证其他功能。这样的项目在小傅哥的 [《AI Agent 智能体》](https://bugstack.cn/md/project/ai-knowledge/ai-knowledge.html) 都有讲解。 + +## 三、使用拓展 + +### 1. 部署脚本 + +
+ +
+ +- 这是一套 nginx 转发脚本。你需要有自己的域名(各个云服务器厂商都可以购买)以及申请 ssl 后才可以配置使用。 +- ssl 可以从 [httpok](https://bugstack.cn/md/road-map/ssl-httpsok.html) 申请使用。地址:[https://bugstack.cn/md/road-map/ssl-httpsok.html](https://bugstack.cn/md/road-map/ssl-httpsok.html) + +### 2. 转发说明(nginx) + +如果你希望在使用的过程中,让模型是以你之前使用 openai 一样的方式,那么可以配置一套 Nginx 转发; + +```java +server { + listen 80; + listen [::]:80; + server_name 需要配置域名(api.xxx.cn); + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name 需要配置域名(api.xxx.cn); + + ssl_certificate /etc/nginx/ssl/_.itedus.cn.pem; + ssl_certificate_key /etc/nginx/ssl/_.itedus.cn.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + root /usr/share/nginx/html; + index index.html index.htm; + } + + location /v1/ { + # 去掉路径中的/v1前缀 + rewrite ^/v1/(.*) /$1 break; + proxy_pass https://models.inference.ai.azure.com; + proxy_http_version 1.1; + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +- nginx 的方式是,当访问有 v1/ 路径的时候,在进行路径重写 `rewrite ^/v1/(.*) /$1 break;` 之后转发到 `https://models.inference.ai.azure.com` - `这个也是 gpt 里的一种模型对接地址` +- 转发后,你就可以直接使用模型服务了。 + +### 3. curl 验证 + +
+ +
+ +- 配置转发后,就可以使用 curl_nginx.sh 脚本进行验证了。 + +好啦,本文到这,你就可以初步使用免费的 LLM 进行一些一些初始功能验证。2025年,ai agent 开始崛起,2026年,必然是 ai agent 爆发的时候。一定要多学习这类实战项目,企业里 spring 是大盘,所以结合 spring ai 做的项目也必然会非常多。 + diff --git a/docs/md/road-map/github.md b/docs/md/road-map/github.md new file mode 100644 index 000000000..564b90acb --- /dev/null +++ b/docs/md/road-map/github.md @@ -0,0 +1,154 @@ +--- +title: Github +lock: need +--- + +# Github 使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +没用过 Github 不能算是一个正经码农! + +Github 本身是的核心职责是一个在线软件源代码托管服务平台,也就是说你的本地开发的代码可以提交到 Github 让多人参与维护和迭代。但除了这个核心的功能外,还提供了不少的社交功能,如;点赞、关注、评论、复刻等,而且 Github 男性用户居多,为此也让Github 被称呼为 Gayhub 社群。 + +在使用Github时,你需要注册一个账号,之后创建一个用于存放代码的仓库,之后就可以把你的代码提交到仓库了。而其他参与此项目的伙伴,也可以通过你的仓库地址检出项目。当然你也可以检出其他人的项目。 + +- 仓库配置 SSH 参考:[https://bugstack.cn/md/road-map/git.html](https://bugstack.cn/md/road-map/git.html) + +## 一、账号注册 + +- 官网:[https://github.com](https://github.com/) +- 注册:[https://github.com/signup?source=login](https://github.com/signup?source=login) + +
+ +
+ +- 你可以按照Github的注册提示,一步一步的填写信息就可以完成账号注册了。 +- 注册后你就会有一个自己的 Github,如:[https://github.com/fuzhengwei](https://github.com/fuzhengwei) + +## 二、仓库创建 + +### 1. 创建类型 + +
+ +
+ +- New repository 创建仓库 +- Import repository 导入仓库 +- New organization 创建组织,一个组织下可以创建多个项目。 + +### 2. 创建操作 + +#### 2.1 创建项目 + +**创建选项** + +
+ +
+ +- 项目名称:不重复即可,一般是小写的项目名称和中划线。 +- 项目介绍:介绍写核心信息。 +- 项目类型:公开的和私有的,私有的别人不能被搜索到,也看不到。 +- 详细介绍:README 是用于写详细介绍的,你可以把它当成是项目本身的简历。 +- .gitignore:是一个模板,哪里不被 Java 项目提交到仓库。 +- License:协议,比如 Apache License 协议。 + +**创建完成** + +
+ +
+ +- 示例:[https://github.com/fuzhengwei/xfg-frame-ddd](https://github.com/fuzhengwei/xfg-frame-ddd) + +#### 2.2 创建组织 + +
+ +
+ +- 组织相当于是一个额外的空间,而你是这个组织的管理者。它必须全局唯一。 +- 之后你可以在创建项目的时候,选择到组织空间,这样项目就会创建到组织里了。 + +## 三、代码提交 + +代码提交到仓库,核心在于把本地代码和远程仓库关联起来。如果你是直接从远程仓库地址,检出到本地的,那么就不需要再次关联了。本场景适用于,你本地有代码库,需要首次提交到远程仓库中。 + +### 1. 创建本地仓库 + +
+ +
+ +- 首先你需要打开 IntelliJ IDEA 工程,选择 VCS 创建一个本地仓库的Git仓库。Create Git Repository +- 这里还有一个分享到仓库,Share Project On GitHub 你也可以选择使用,直接把本地代码分享创建到远程仓库。 + +### 2. 关联远程仓库 + +
+ +
+ +- 打开你的Github 仓库对应的项目,复制项目 HTTPS 地址,粘贴到 IntelliJ IDEA 项目的远程仓库配置中,点击 OK 即可。 + +### 3. 项目代码提交 + +#### 3.1 添加 + +
+ +
+ +- 在工程上右键,把你需要提交的文件,选择 +Add 注意不要把一些不需要提交的文件,也提交上去。 +- 你可以先创建一个 `Add to .gitignore` 它可以屏蔽掉一些不要的文件。—— 在 Github 创建工程的时候也有此文件。你也可以先更新 pull 拉取下来。 + +#### 3.2 提交 + +
+ +
+ +- 提交代码快捷键:`Ctrl + K` - 提交到本地仓库 + +#### 3.3 推送 + +
+ +
+ +- 推送代码快捷辑:`Ctrl + Shift + K` —— 提交到远程仓库 + +## 四、提交规范 + +**分支命名**:`日期_姓名首字母缩写_功能单词`,如:`230708_xfg_buildFramework` + +**提交规范**:`作者,type: desc` 如:`小傅哥,fix:修复查询用户信息逻辑问题` *参考Commit message 规范* + +``` +# 主要type +feat: 增加新功能 +fix: 修复bug + +# 特殊type +docs: 只改动了文档相关的内容 +style: 不影响代码含义的改动,例如去掉空格、改变缩进、增删分号 +build: 构造工具的或者外部依赖的改动,例如webpack,npm +refactor: 代码重构时使用 +revert: 执行git revert打印的message + +# 暂不使用type +test: 添加测试或者修改现有测试 +perf: 提高性能的改动 +ci: 与CI(持续集成服务)有关的改动 +chore: 不修改src或者test的其余修改,例如构建过程或辅助工具的变动 +``` + +--- + +**此外**,Github 是一个大宝藏,你可以通过检索,找到特别多的优质项目、代码、设计。如:[https://github.com/fuzhengwei](https://github.com/fuzhengwei) diff --git a/docs/md/road-map/google-adk.md b/docs/md/road-map/google-adk.md new file mode 100644 index 000000000..805cf6e95 --- /dev/null +++ b/docs/md/road-map/google-adk.md @@ -0,0 +1,344 @@ +--- +title: google adk +lock: need +--- + +# Google Agent ADK,20分钟,帮你搭建一个简单智能体 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +💐 从22年至今,小傅哥已经带着大家做了5个AI类项目,包括;`(22年)问答助手`、`(23年)OpenAI应用(含支付、敏感词过滤)`、`(24年)AI 代码自动评审`、`(25年)Ai Agent 智能体`、`(25年)Ai MCP Gateway 网关`。 + +这些项目也都是结合这,AI 这一年最新的技术动向和应用方向,而做的设计和落地。所以,每次小傅哥都给大家讲了,接下来 AI 将影响的一些场景,也都陆续的发生了。就像,24年11月发布 MCP 协议后,我给大家说,所有互联网企业都将大量的落地 MCP 服务,并开始 Ai Agent 智能体实现(别看市面有 dify、扣子,各个且用还是要做自己的业务智能体)。 + +随后,25年年初,小傅哥就带着大家开始了 RAG、MCP、Ai Agent 智能体的开发,并告诉大家,以后 Ai Agent 智能体也会出标准的框架,让开发更加容易。这不,**谷歌的 ADK 就来了**。并且这哥们👬🏻还定义A2A协议。这会让不是那么大型的互联网公司,也会具备 Ai Agent 智能体开发的能力。 + +接下来的几年,所有的业务项目,都会以 Ai Agent 智能体翻一遍,程序员新增的岗位和工作量仍然会很多。因为在咱们这,你做的越快,你就得做的越多! + +>接下来,小傅哥就带着大家做一下 Google ADK 搭建 AI Agent。如果你感兴趣 AI 类项目,还可以在文末获取全部实战项目源码,深度积累此类技术内容。**文末有Google ADK 运行效果,交互式对话智能体,对 ELK 进行巡检分析。** + +## 一、官网资料 + +- 官网:[https://google.github.io/adk-docs/](https://google.github.io/adk-docs/) +- 搭建:[https://google.github.io/adk-docs/get-started/](https://google.github.io/adk-docs/get-started/) + +
+ +
+ +- ADK 以轻便化构建 Ai Agent 智能体,解决智能体开发的复杂流程而设计。目前支持 Python、Java、Go 3种语言对应的技术框架。 +- 整个文档完整的描述了,智能体的创建和运行、工具的调用(tools、function、mcp)、可观测性以及 A2A 协议等。 +- Agent Development Kit (ADK) is designed to empower developers to quickly build, manage, evaluate and deploy AI-powered agents. These quick start guides get you set up and running a simple agent in less than **20 minutes**. + +## 二、工程实践 + +### 1. 前置说明 + +本次的 Ai Agent 实践,是以 Google ADK 框架为基础,配和 Github [system-prompts-and-models-of-ai-tools](https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools) 开源提示词项目中的 claude-code-system-prompt 作为必要描述。来实验,ELK 系统日志智能分析场景。 + +- API Key:[https://ai.google.dev/gemini-api](https://ai.google.dev/gemini-api) 需要申请开发 API 秘钥,是免费的。 +- Docker 环境,本项目部署了一套 ELK 日志服务,基于 Docker 部署,之后对 ELK 模拟写入日志,让 Ai Agent 智能体进行分析。`如果暂时配置不了,可以在测试的时候去掉这部分 mcp 服务` +- `JDK 17+`、`Maven 3.8.x` + +### 2. 工程说明 + +
+ +
+ +- 工程地址:[https://github.com/fuzhengwei/xfg-dev-tech-google-adk](https://github.com/fuzhengwei/xfg-dev-tech-google-adk) +- 这是一套引入了 Google ADK 0.1.0 版本的 Agent 最基础智能体测试。如果需要扩展,还要额外增加很多东西,可以参考 [《Ai Agent 智能体项目》](https://bugstack.cn/md/project/ai-knowledge/ai-knowledge.html) + +#### 2.1 YML 配置 + +```java +server: + port: 8901 + +# 可申请API秘钥;https://ai.google.dev/gemini-api +google: + api: + base-url: https://generativelanguage.googleapis.com + key: AIzaSyDF6JnvFx7xWEsARS*******可以自己申请免费的 + cloud: + project: xfg-google-adk + +logging: + level: + root: info + config: classpath:logback-spring.xml +``` + +- 在 `application-dev.yml` 修改你的参数配置,配置你的 Google api key + +#### 2.2 ADK 单测 + +```java +import com.google.adk.agents.LlmAgent; +import com.google.adk.events.Event; +import com.google.adk.models.Gemini; +import com.google.adk.runner.InMemoryRunner; +import com.google.adk.sessions.Session; +import com.google.genai.Client; +import com.google.genai.types.Content; +import com.google.genai.types.HttpOptions; +import com.google.genai.types.Part; +import io.reactivex.rxjava3.core.Flowable; + +public class ApiTest { + + /** + * 可申请免费测试api + * https://ai.google.dev/gemini-api/docs/quickstart?hl=zh-cn#apps-script + */ + public static void main(String[] args) { + LlmAgent agent = LlmAgent.builder() + .name("test") + .description("test agent help user do work") + .model(Gemini.builder() + .apiClient(Client.builder() + .apiKey("AIzaSyDF6JnvFx7xWEsARSGosNmvTU3ZoCwo-mc") + .httpOptions(HttpOptions + .builder() + .baseUrl("https://generativelanguage.googleapis.com") + .timeout(500000) + .build()) + .build()) + .modelName("gemini-2.0-flash") + .build()) + .build(); + + InMemoryRunner runner = new InMemoryRunner(agent); + + Session session = runner + .sessionService() + .createSession("test", "xiaofuge") + .blockingGet(); + + Flowable events = runner.runAsync("xiaofuge", session.id(), Content.fromParts(Part.fromText("hi agent can you help me"))); + + System.out.print("\nAgent > "); + events.blockingForEach(event -> System.out.println(event.stringifyContent())); + + } + +} +``` + +```java +Agent > Hi there! Yes, I'm here to help. To best assist you, could you tell me what you need help with? The more information you give me, the better I can understand your request and provide a useful response. +``` + +- 这是一套最基础的 Ai Agent 智能体 ADK 的测试使用代码。其实它的配置和调用与 Spring AI 框架是有类似之处的,基本上这类框架也都是这样的使用模式。 +- LlmAgent 提供了一整套构建智能体的方式,可以设置客户端(Gemini),并设置相关的 baseUrl、apiKey 参数,以及模型和超时时间等。 +- InMemoryRunner 的用途是把 Agent 放入一个记忆执行里,让一整个 Session 会话下的执行都被记录下,这样才能记录上下文。 +- 之后就是放到响应框架进行之后和拿到最后结果啦。 + +#### 2.3 Agent 智能体测试 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class AutoAgentTest { + + @Value("${google.api.base-url}") + private String baseUrl; + + @Value("${google.api.key}") + private String apiKey; + + private static final String USER_ID = "xiaofuge"; + private static final String NAME = "multi_tool_agent"; + + public static BaseAgent agent; + + @Before + public void init() { + List mcpTools = new ArrayList<>(); + mcpTools.addAll(mcp_elk()); + mcpTools.addAll(mcp_filesystem()); + + agent = LlmAgent.builder() + .name(NAME) + .model(Gemini.builder() + .apiClient(Client.builder() + .apiKey(apiKey) + .httpOptions(HttpOptions + .builder() + .baseUrl(baseUrl) + .timeout(500000) + .build()) + .build()) + .modelName("gemini-2.0-flash") + .build()) + .description("You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.") + .instruction( + """ + You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user. + + 可以在这里复制全部提示词;https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools/blob/main/Claude%20Code/claude-code-system-prompt.txt + """) + .tools(mcpTools) + .build(); + } + + /** + * - 需要配置后,才能在单测控制台输入内容 + * IntelliJ IDEA Help -> Edit Custom VM Options -> -Deditable.java.test.console=true + *
+ * - ai.google.dev/api + */ + @Test + public void test_agent() { + InMemoryRunner runner = new InMemoryRunner(agent); + + Session session = + runner + .sessionService() + .createSession(NAME, USER_ID) + .blockingGet(); + + try (Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8)) { + while (true) { + System.out.print("\nYou > "); + String userInput = scanner.nextLine(); + + if ("quit".equalsIgnoreCase(userInput)) { + break; + } + + Content userMsg = Content.fromParts(Part.fromText(userInput)); + Flowable events = runner.runAsync(USER_ID, session.id(), userMsg); + + System.out.print("\nAgent > "); + events.blockingForEach(event -> System.out.println(event.stringifyContent())); + } + } + + } + + private List mcp_elk() { + Map env = new HashMap<>(); + env.put("ES_HOST", "http://127.0.0.1:9200"); + env.put("ES_API_KEY", "none"); + + ServerParameters mcp_elk = ServerParameters.builder("npx") + .args(List.of( + "-y", + "@awesome-ai/elasticsearch-mcp" + )) + .env(env) + .build(); + + CompletableFuture futureResult = + McpToolset.fromServer(mcp_elk, JsonBaseModel.getMapper()); + + McpToolset.McpToolsAndToolsetResult result = futureResult.join(); + + return result.getTools(); + } + + private List mcp_filesystem() { + ServerParameters mcp_filesystem = ServerParameters.builder("npx") + .args(List.of( + "-y", + "@modelcontextprotocol/server-filesystem", + "/Users/fuzhengwei/Desktop" + )) + .build(); + + CompletableFuture futureResult = + McpToolset.fromServer(mcp_filesystem, JsonBaseModel.getMapper()); + + McpToolset.McpToolsAndToolsetResult result = futureResult.join(); + + return result.getTools(); + } + +} +``` + +- AutoAgentTest 智能体,配置了两个 mcp,mcp_elk、mcp_filesystem 服务。其实 AI Agent 智能体,都少不了 MCP 服务的加持,这样才能让这个大脑有手脚可以行动起来。 +- init 初始化阶段,instruction 配置了对应的智能体提示词,这部分很重要,有点像智能体的神经。[https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools/blob/main/Claude%20Code/claude-code-system-prompt.txt](https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools/blob/main/Claude%20Code/claude-code-system-prompt.txt) - 可以选择这里的提示词配置到智能体进行测试。 +- 这部分也可以运行测试,之后会你可以进行提问。注意,如果你没有配置 elk,那么可以删掉 `mcpTools.addAll(mcp_elk());` 代码。 +- mcp_filesystem() 配置了,`/Users/fuzhengwei/Desktop` 你需要修改为你的本地的有权限访问的地址。 + +#### 2.4 Agent 智能体服务 + +有了前面的案例测试基础,就可以把服务配置成对应的接口,以及开发个简单的页面进行对接验证了。 + +##### 2.4.1 服务接口 + +```java +@Slf4j +@RestController +@RequestMapping("/trigger") +@CrossOrigin(origins = "*") +public class AgentController { + + private final AgentService agentService; + + public AgentController(AgentService agentService) { + this.agentService = agentService; + } + + @PostMapping(path = "/session", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public CreateSessionResponse createSession(@RequestBody CreateSessionRequest req) { + String sessionId = agentService.createOrGetSession(req.getUserId()); + log.info("创建会话ID:{}", sessionId); + return new CreateSessionResponse(sessionId); + } + + @PostMapping(path = "/chat", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ChatResponse chat(@RequestBody ChatRequest req) { + String sessionId = req.getSessionId(); + if (sessionId == null || sessionId.isEmpty()) { + sessionId = agentService.createOrGetSession(req.getUserId()); + } + log.info("使用会话ID:{}", sessionId); + List outputs = agentService.chat(req.getUserId(), sessionId, req.getMessage()); + return new ChatResponse(sessionId, String.join("\n", outputs)); + } +} +``` + +- 这里就是把单测的服务,包装成接口。详细的部分可以直接看工程代码。 + +##### 2.4.2 页面对接 + +
+ +
+ +- 把服务端的接口对接到页面。 + +## 三、功能测试 + +### 1. 部署 elk 日志服务 + +
+ +
+ +- 整个 Ai Agent 案例,配置了 ELK 作为日志分析的基础,所以最好配置下。 +- 这里也有云服务器的部署操作,也可以参考教程来部署。教程:[https://bugstack.cn/md/road-map/docker-install.html](https://bugstack.cn/md/road-map/docker-install.html) +- 注意配置完成后,要执行 `elk-blacklist-data.sh` 模拟的写入进去一些日志,这样才能用于分析使用。 + +### 2. 启动服务访问页面 + +
+ +
+ +
+ +
+ +- 以上演示了,使用 Ai Agent 进行 ELK 日志分析,通过对话可以看到,最终我们可以拿到系统的日志数据。 +- 你还可以尝试配置其他的 MCP 服务,之后验证各种场景功能。担任如果你希望更细化的做 Ai Agent,还是要看下 [《Ai Agent 智能体项目》](https://bugstack.cn/md/project/ai-knowledge/ai-knowledge.html) 这样可以更细腻的做出这样的系统,以及对应的可视化编排操作。 diff --git a/docs/md/road-map/grafana.md b/docs/md/road-map/grafana.md new file mode 100644 index 000000000..fa2b2e393 --- /dev/null +++ b/docs/md/road-map/grafana.md @@ -0,0 +1,253 @@ +--- +title: Grafana 可视化监控面板 +lock: need +--- + +# Prometheus + Grafana 监控,验证 Hystrix 超时熔断 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,使用 Prometheus + Grafana 部署监控,同时结合监控了解到为什么需要使用 Hystrix 对接口进行超时熔断处理。 + +很多伙伴在面试的时候,都可能会被问到;你的应用接口响应时间多少,Tomcat 配置了多少连接数,如果接口超时了怎么办,会不会把服务拖垮。那会不会呢,其实会的,对于一些接口不稳定容易超时但又不熔断的接口,在用户大量请求的情况下,是很容易把Tomcat连接数打满,直至拖垮整个服务,让服务的任何接口都没有响应。所以本节小傅哥会带着大家,来模拟这样的场景,让大家学习下。 + +本文涉及的工程: +- xfg-dev-tech-grafana:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-grafana](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-grafana) - `测试工程、监控Docker安装脚本、Grafana监控面板配置JSON` + +## 一、你是怎么挂的? + +Tomcat 可分配的连接数就像厕所的坑位,一堆用户来上大号。本来4个坑位也够用了,因为用户来了也可以快速释放请求,不会长时间占用。但突然有这么一天,用户都拉肚子,一个进去就1个小时候,其余人都排队。最后给压垮了! + +
+ +
+ +那是不是,增加了连接数(WC 更多的坑位就好了呢),其实也好不哪去,你总不能给所有的用户都建一个坑位,而且坑位越多,距离也越长了,这就会涉及到线程的切换,也是不小的资源消耗。 + +所以,为了保护我方Tomcat(WC 坑位),则需要快速熔断,而不是让它一直占用着链接不释放。 + +## 二、工程环境配置 + +这里小傅哥做一个 SpringBoot 工程测试案例,并配合添加 Hystrix 熔断组件,以及使用 Grafana 监控来观察简单压测时连接数的消耗和接口性能的反馈。 + +
+ +
+ +- 环境;jdk 1.8、Maven 3.6.x、Docker 环境 +- 代码;在 xfg-dev-tech-grafana 测试工程中提供了测试代码和环境安装 + +### 1. 配置修改 + +
+ +
+ +- 首先,你需要打开 prometheus.yml 修改监控采集应用的IP地址。这个配置还可以监控例如 MySQL 和其他应用。 + +### 2. 安装监控 + +文件:`docker-compose.yml` + +```java +version: '3' +# 执行脚本;docker-compose -f docker-compose.yml up -d +# 拷贝配置;docker container cp grafana:/etc/grafana/ ./docs/dev-ops/ +services: + # 数据采集 + prometheus: + image: bitnami/prometheus:2.47.2 + container_name: prometheus + restart: always + ports: + - 9090:9090 + volumes: + - ./etc/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + # 监控界面 + grafana: + image: grafana/grafana:10.2.0 + container_name: grafana + restart: always + ports: + - 4000:4000 + depends_on: + - prometheus + volumes: + - ./etc/grafana:/etc/grafana +``` + +
+ +
+ +
+ +
+ +- 这是 docker compose 执行脚本,如果你本地已经安装了 Docker 可以直接执行 安装即可。 +- 注意;如果你是生产使用,则需要修改 `etc/grafana/grafana.ini` 中 datasources 为 mysql 这样在后面迁移的时候也会非常容易。 + +### 3. 监控配置 + +- 安装完监控环境以后,可以先打开 Grafana 地址:[http://127.0.0.1:4000](http://127.0.0.1:4000) - `admin/admin` + +- 地址:[http://127.0.0.1:4000/dashboards](http://127.0.0.1:4000/dashboards) - 打开仪表盘,导入监控面板配置。 + +
+ +
+ +
+ +
+ +- 打开导入面板后,把案例工程中的 JSON 复制到导入面板的 JSON 里,点击 Load 这样就配置进去了。 +- 不过这会你还看不见数据,因为工程还没有启动,没有往里写入数据。 + +## 三、测试应用说明 + +### 1. 熔断配置 + +```pom + + com.netflix.hystrix + hystrix-javanica + 1.5.18 + +``` + +- 这个熔断的组件,不需要引入 SpringCloud 一堆的东西,使用起来更加容易。 + +```java +@Configuration +public class HystrixConfig { + + @Bean + public HystrixCommandAspect hystrixCommandAspect() { + return new HystrixCommandAspect(); + } + +} +``` + +- 引入 POM 后,添加 HystrixConfig 熔断配置。 + +### 2. 接口配置 + +这里小傅哥提供了2个接口,一个普通的查询数据接口,一个是 OpenAi 中服务的给前端异步响应结果的接口。尤其是 OpenAi 异步接口,我们在实际使用的时候,也总会有超时熔断,所以这里给大家添加上。 + +#### 2.1 普通接口 + +```java +/** + * curl http://localhost:8091/api/hystrix/query_order_info + */ +@HystrixCommand(commandProperties = { + @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "50") +}, fallbackMethod = "queryOrderInfo_error" +) +@RequestMapping(value = "query_order_info", method = RequestMethod.GET) +public String queryOrderInfo() throws InterruptedException { + new CountDownLatch(1).await(); + return "您的订单信息查询完毕"; +} + +private String queryOrderInfo_error() throws InterruptedException { + return "Fallback Hello"; +} +``` + +- 测试过程中,超时熔断时间,可以设置的较大一些,也可以先不添加超时熔断的注解。 + +#### 2.2 异步接口 + +```java +/** + * curl http://localhost:8091/api/hystrix/stream + */ +@HystrixCommand(commandProperties = { + @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "150") +}, fallbackMethod = "stream_error" +) +@RequestMapping(value = "/stream", method = RequestMethod.GET) +public ResponseBodyEmitter stream(HttpServletResponse response) throws Exception { + response.setContentType("text/event-stream"); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Cache-Control", "no-cache"); + ResponseBodyEmitter emitter = new ResponseBodyEmitter(); + emitter.send("异步响应"); + new Thread(() -> { + for (int i = 0; i < 200; i++) { + try { + emitter.send("hi xfg-dev-tech-grafana\r\n" + i); + Thread.sleep(250); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + emitter.complete(); + }).start(); + return emitter; +} + +public ResponseBodyEmitter stream_error(HttpServletResponse response) throws IOException { + response.setContentType("text/event-stream"); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Cache-Control", "no-cache"); + ResponseBodyEmitter emitter = new ResponseBodyEmitter(); + emitter.send("Err"); + emitter.complete(); + return emitter; +} +``` + +- 另外一个是一步响应接口,如果响应时间较长,则进入超时熔断方法中。 + +## 四、服务压测验证 + +### 1. 配置信息 + +**application.yml** + +```yml +server: + port: 8091 + # 1核2G内存,可默认配置 200;4核8G内存【accept-count=1000、max-threads=800、max-connections=10000】,线程数经验值800。线程池过大,cpu调度会消耗大量时间 + tomcat: + mbeanregistry: + enabled: true + max-connections: 20 # 最大连接数 + threads: + max: 20 # 设定处理客户请求的线程的最大数目,决定了服务器可以同时响应客户请求的数,默认200 + min-spare: 10 # 初始化线程数,最小空闲线程数,默认是10 + accept-count: 10 # 等待队列长度 +``` + +- 我们在 YML 文件中指定了 tomcat 的连接数配置为 20 个,处理线程最多也是20个。 +- 同时还添加了监控空你暴露为true,这样 Grafana 才能监控到。 + +### 2. 压测工具 + +压测工具有很多,包括;[JMeter、ApacheBench、Siege](https://bugstack.cn/md/road-map/jmeter.html) 小傅哥已经在前面写过文章,也可以去参考使用。 + +不过这里为了让大家更加简单的压测下,小傅哥使用了 ApiPost 工具自带的压测,这样不需要自己再安装其他工具就可以简单压测下了。 + +
+ +
+ +- 你只要把2个接口配置到 ApiPost 就可以用一键压测工具进行压测。 + +### 3. 压测观察 + +
+ +
+ +- 这里小傅哥压测了下接口 `http://localhost:8091/api/hystrix/stream` 接口。因为这个接口设置了超时,可以看见,快速的就把连接数给占满了。 +- 所以如果你的应用配置的 Tomcat 连接数不合理,之后接口又容易超时,超时后又没有熔断,那么很容易就会把你的服务拖垮。很多新人在做一些对外的应用时,如果没有注意到这些,那么也是很容易宕机的。 diff --git a/docs/md/road-map/guava.md b/docs/md/road-map/guava.md new file mode 100644 index 000000000..e069bde8c --- /dev/null +++ b/docs/md/road-map/guava.md @@ -0,0 +1,143 @@ +--- +title: guava +lock: need +--- + +# Guava 使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +本文的宗旨在于通过简单干净实践的方式教会读者,使用 Guava 的一些常用操作方法。这些方法也是日常使用 Guava 时最为常用的方法,如果你在使用中还有一些案例和特性,或者踩坑经验也可以在本文提交PR + +本文涉及的工程: + +- xfg-dev-tech-guava:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-guava](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-guava) +- Github Guava:[https://github.com/google/guava/wiki](https://github.com/google/guava/wiki) + +## 一、组件介绍 + +Guava 是 Google 的一组核心 Java 库,其中包括新的集合类型(例如 multimap 和 multiset)、不可变集合、图形库以及用于并发、I/O、哈希、原语、字符串等的实用程序!它被广泛用于 Google 内部的大多数 Java 项目,也被许多其他公司广泛使用。—— 来自于 Guava 的介绍。 + +在小傅哥的小项目中使用 Guava,最常用也是非常喜欢用的就是它可以替代 Redis、MQ,在小项目中使用,这样我就不用一个项目引入过多的技术栈,包括; +1. 它有一个有时效性的缓存,可以替代 Redis 使用。 +2. 他有一个 EventBus 消息总线,可以替代 MQ 使用。 + +此外,他还有布隆过滤器、简化的Java反射,以及一些集合和并发的操作。也是可以使用的,非常方便。 + +## 二、常用功能 + +### 1. 本地缓存 + +```java +@Test +public void test_cache() { + Cache cache = CacheBuilder.newBuilder() + // 最大存储条数,缓存将尝试逐出最近或不经常使用的条目 + .maximumSize(10000) + // 可以设定删除时候的权重判断 + .weigher((Weigher) (x, y) -> x.length() - y.length()) + // 有效时间 + .expireAfterWrite(3, TimeUnit.SECONDS) + // 记录次数 + .recordStats() + .build(); + cache.put("xfg", "bugstack.cn"); + log.info("测试结果:{}", cache.getIfPresent("xfg")); + cache.invalidate("xfg"); // cache.invalidateAll(); 也可以全部删除 + log.info("测试结果:{}", cache.getIfPresent("xfg")); + log.info("测试结果:{}", cache.stats()); +} +``` + +- 你可以自己设定有时效性的缓存对象,还可以记录次数、权重和最大条数。 +- 如果你的项目不大,也不想自己实现有过期时间的缓存,那么 Guava 非常适合使用。 + +### 2. 事件总线 + +```java +@Test +public void test_eventbus() { + EventBus eventBus = new EventBus(); + eventBus.register(new Listener()); + // 可以由其他服务推送消息,之后就可以在监听中收到了 + eventBus.post("消息总线,订单号:100001"); +} +static class Listener { + @Subscribe + public void handleEvent(String orderId) { + log.info("测试结果:{}", orderId); + } +} +``` + +- 事件总线和MQ消息实现的效果是一致的,都是为了解耦功能流程。但 Guava 的这个组件,非常适合在自己的小项目中使用,接入成本非常低。 + +### 3. 并发回调 + +```java +@Test +public void test_ListenableFuture() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); + ListenableFuture explosion = executorService.submit(() -> "finished"); + ExecutorService callBackService = Executors.newFixedThreadPool(1); + Futures.addCallback(explosion, new FutureCallback() { + public void onSuccess(String explosion) { + System.out.println("onSuccess"); + countDownLatch.countDown(); + } + public void onFailure(Throwable thrown) { + System.out.println("onFailure"); + countDownLatch.countDown(); + } + }, callBackService); + countDownLatch.await(); +} +``` + +### 4. 布隆过滤器 + +```java +@Test +public void test_BloomFilter() { + BloomFilter bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), + 1000, + 0.01); + // 向布隆过滤器中添加元素 + bloomFilter.put("apple"); + bloomFilter.put("banana"); + bloomFilter.put("orange"); + // 检查元素是否存在于布隆过滤器中 + System.out.println(bloomFilter.mightContain("apple")); // true + System.out.println(bloomFilter.mightContain("banana")); // true + System.out.println(bloomFilter.mightContain("orange")); // true + System.out.println(bloomFilter.mightContain("grape")); // false + // 输出布隆过滤器的统计信息 + System.out.println("Expected FPP: " + bloomFilter.expectedFpp()); + System.out.println("Number of Inserted Elements: " + bloomFilter.approximateElementCount()); +} +``` + +### 5. 反射工具包 + +```java +@Test +public void test_Invokable() throws NoSuchMethodException { + Method method = UserEntity.class.getMethod("getUserName"); + Invokable invokable = Invokable.from(method); + log.info("测试结果 - 方法名称:{}", invokable.getName()); + log.info("测试结果 - 参数类型:{}", JSON.toJSONString(invokable.getTypeParameters())); + log.info("测试结果 - 静态判断:{}", invokable.isStatic()); + // !(Modifier.isFinal(method.getModifiers()) || Modifiers.isPrivate(method.getModifiers()) || Modifiers.isStatic(method.getModifiers()) || Modifiers.isFinal(method.getDeclaringClass().getModifiers())) + log.info("测试结果 - isOverridable:{}", invokable.isOverridable()); +} +``` + +- 如果你开发一些组件,有不少的操作都是需要判断方法的权限范围、包的权限范围等,使用 Guava 的插件也会非常方便。 + +--- + +其他更多的操作可以参考仓库代码和官网文档学习,基本都是非常完整的案例。 diff --git a/docs/md/road-map/higress-ai.md b/docs/md/road-map/higress-ai.md new file mode 100644 index 000000000..47319e42e --- /dev/null +++ b/docs/md/road-map/higress-ai.md @@ -0,0 +1,322 @@ +--- +title: Higress-Ai +lock: need +--- + +# AI(+MCP)网关,快速集成(LLM API/HTTP/RPC/Nacos) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**傻,是不傻?** 有些伙伴问,Dify、Coze,市面都有了,怎么公司里还要基于 Spring AI 框架,开发自己的智能体,主要原因是什么呢?🤔 + +
+ +
+ +**何止 Dify,Java JDK 都有公司自己干一套的!为啥呢?** + +如果你还纠结这个事,说明你还是个小白,小卡拉米。公司干一套新的,对于企业来说,这东西叫技术资产,人才储备,所有开发的出来东西,既可以申请著作权,又可以申请技术专利。转而,就把这些专利作为企业的注册资金当做投资款抵扣了,也可以用于企业所得税的抵扣。 + +从公司大目标再往下看,随着 LLM 大模型的发展,以及各类框架的完善,AI 应用场景的智能体开发,会越来越标准化,且越来也有容易被实现。不再需要像最早的一些 Dify、Coze 方案那样笨重。 + +再者,企业需要沉淀 AI 技术能力(+人才),为公司适应各类 AI 应用场景开发做储备。而直接使用市面的产品,很多时候都没法做到快速迭代新的需求,甚至要在公司发展后期遇到极端问题,甚至还要考虑重新做技术选型以及重建。这个成本就非常大了。 + +所以,你能理解为啥公司都要做自己的服务平台了吧。而且,公司也非常需要一个像你一样,具备 AI 方面开发知识的研发人员。这也是为什么小傅哥,要带着你做这么多 AI 类的项目。今天分享的这套 AI MCP 网关服务,小傅哥也正在带着大家做。 + +>接下来,小傅哥就带着大家实践下阿里旗下 Higress AI 网关能力的使用。 + +## 一、网关介绍 + +官网:[https://higress.io/](https://higress.io/) +源码:[https://github.com/alibaba/higress](https://github.com/alibaba/higress) +文档:[https://higress.cn/ai/quick-start](https://higress.cn/ai/quick-start) + +Higress AI 网关快速集成 LLM API,通过 AI 网关、API 网关及Himarket,解决模型、工具及Agent统一代理问题,并助力企业构建AI中台,加速AI落地。 + +这套网关服务,可以统一管理你的 LLM API 服务接口,如你访问的 OpenAI、智谱、千问、文心,都可以在Higress AI 网关做一个统一管理。另外,就是 MCP 网关能力,可以把注册的 HTTP 服务、Nacos 服务等,都可以转换为 MCP 接口服务能力,让 AI 客户端使用。 + +恰好,这个事小傅哥也在带着大家做一套 [《AI MCP Gateway 网关服务系统》](https://bugstack.cn/md/project/ai-mcp-gateway/ai-mcp-gateway.html) 如果想在 AI 开发方面做一些储备积累,那么一定要学习这套项目。 + +> 本案例会使用 docker 进行部署,如果你不了解 docker 是什么,可以阅读系列教程 [https://bugstack.cn/md/road-map/docker-what.html](https://bugstack.cn/md/road-map/docker-what.html) + +## 二、网关使用 + +### 1. 案例工程 + +
+ +
+ +- 地址:[https://github.com/fuzhengwei/xfg-dev-tech-higress-ai](https://github.com/fuzhengwei/xfg-dev-tech-higress-ai) +- 说明:案例工程,提供了部署 Higress AI 网关脚本(+redis环境),以及包括工程提供了一个测试 http 接口,并配置了 swagger api 文档。网关可以支持从 swagger api json 转换为 MCP 协议结构。 +- 环境:如果你是使用云服务器部署 Higress AI 网关,那么可以使用提供好的一键部署脚本安装环境 [https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install](https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install) + +### 2. 接口说明 + +#### 2.1 swagger api + +```java + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.5.0 + + + + org.springdoc + springdoc-openapi-starter-webmvc-api + 2.5.0 + +``` + +- 官网:[https://swagger.io/](https://swagger.io/) +- 添加 swagger api pom,项目启动后可以访问;[http://localhost:8091/swagger-ui/index.html](http://localhost:8091/swagger-ui/index.html) - 就可以看到项目的 http 接口信息了。 + +#### 2.2 接口开发 + +```java +@RestController +@CrossOrigin("*") +@RequestMapping("/api/v1/") +@Tag(name = "测试服务", description = "示例接口") +public class TestServiceController { + + private static final Logger logger = LoggerFactory.getLogger(TestServiceController.class); + + /** + * API文档;swagger
+ * API测试;http://127.0.0.1:8091/api/v1/to_upper_case?word=xiaofuge + */ + @GetMapping("to_upper_case") + @Operation(summary = "字符串转大写", description = "将传入 word 转换为大写") + public String toUpperCase(@RequestParam String word) { + logger.info("接收信息:{}", word); + return word.toUpperCase(); + } + +} +``` + +- 在接口上增加描述信息,这些信息可以用作于 mcp 协议结构中方法和字段上的介绍。如下就是 mcp 接口的 @Tool 工具配置。 + +```java +@Tool(description = "获取公司雇员信息") +public XxxResponse getCompanyEmployee(XxxRequest01 xxxRequest01, XxxRequest02 xxxRequest02) { + log.info("根据公司和雇员,查询工资和工作工时。{} {}",xxxRequest01.getCompany(), xxxRequest02.getEmployeeName()); + // 这部分可以实际调用你需要的接口,比如调用http接口获取个数据或者做一些操作等。 + XxxResponse xxxResponse = new XxxResponse(); + xxxResponse.setSalary(new Random().longs(10000).toString()); + xxxResponse.setDayManHour(String.valueOf(new Random().nextInt(24))); + return xxxResponse; +} +``` + +- 所以,即使是我们自己开发 AI MCP 网关,也是在找这样的衔接点。怎么把 http 接口,借住什么标准的桥梁,转换为 mcp 协议,让用户使用起来更加简单。而 swagger api 就是中间的标准 open api 桥梁。 + +### 3. 网关部署 + +#### 3.1 脚本说明 + +```java +# 命令执行 docker-compose -f docker-compose-environment-aliyun.yml up -d +# docker 代理和使用文档;https://bugstack.cn/md/road-map/docker.html +version: '3.9' +services: + # https://github.com/alibaba/higress + # docker run -d --rm --name higress-ai -v ${PWD}:/data \ + # -p 8001:8001 -p 8080:8080 -p 8443:8443 \ + # higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/all-in-one:latest + higress-ai: + image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/all-in-one:latest + container_name: higress-ai + hostname: higress-ai + ports: + - 8001:8001 + - 8080:8080 + - 8443:8443 + volumes: + - ./higress-ai/data:/data + networks: + - my-network + + # Redis + redis: + image: redis:6.2 + container_name: redis + restart: always + hostname: redis + privileged: true + ports: + - 16379:6379 + volumes: + - ./redis/redis.conf:/usr/local/etc/redis/redis.conf + command: redis-server /usr/local/etc/redis/redis.conf + networks: + - my-network + healthcheck: + test: [ "CMD", "redis-cli", "ping" ] + interval: 10s + timeout: 5s + retries: 3 + +networks: + my-network: + driver: bridge +``` + +- 这里把 higress-ai 通过 docker-compose 脚本进行部署,因为部署后还需要配置 Redis 环境,基于 Redis 的 pub/sub 发布订阅能力,让网关具备分布式部署能力,所以在 docker-compose 脚本里,提供了 higress-ai、redis 的部署。 +- 部署;`docker-compose -f docker-compose-environment-aliyun.yml up -d` + +#### 3.2 部署网关 + +##### 3.2.1 部署脚本 + +
+ +
+ +- 如图说明,进行部署。Mac 电脑本地测试即可。如果 Windows 电脑有兼容问题,可以考虑 Linux 云服务器。[https:618.gaga.plus](https:618.gaga.plus) + +##### 3.2.2 部署结果 + +
+ +
+ +- 部署后,访问 portainer(这个是 docker 的管理后台) + +##### 3.2.3 访问网关 + +
+ +
+ +- 地址:[http://127.0.0.1:8001/](http://127.0.0.1:8001/) +- 说明:首次登录需要设置下登录账号和密码,设置后登录即可。 + +### 4. 获取接口 + +#### 4.1 启动服务(springboot) + +
+ +
+ +- 测试:[http://127.0.0.1:8091/api/v1/to_upper_case?word=xiaofuge](http://127.0.0.1:8091/api/v1/to_upper_case?word=xiaofuge) - `把小写的xiaofuge转换为大写` +- 接口:[http://localhost:8091/swagger-ui/index.html#/%E6%B5%8B%E8%AF%95%E6%9C%8D%E5%8A%A1/toUpperCase](http://localhost:8091/swagger-ui/index.html#/%E6%B5%8B%E8%AF%95%E6%9C%8D%E5%8A%A1/toUpperCase) + +#### 4.2 获取接口 + +
+ +
+ +- 地址:[http://localhost:8091/swagger-ui/index.html](http://localhost:8091/swagger-ui/index.html) +- 说明:启动服务后,访问 swagger 地址,你就可以拿到接口文档了。这里主要需要的就是这份json结构。 + +### 5. 网关配置 + +#### 5.1 开启网关(mcp) + +
+ +
+ +- 注意,在系统设置里,需要开启 mcp 网关服务。enable = true +- 另外,redis 地址,需要配置上。默认本文给的案例,是支持直接使用 redis 别名链接的,如果 redis 不是本案例安装的,则可以用 ip:port 连接。如果有密码,记得填写密码。 + +#### 5.2 服务来源 + +
+ +
+ +- 可以在网关页,配置服务来源。这里的服务来源支持多种类型,Nacos、Eureka、Zookeeper,以及我们图中这种固定地址的案例,都是可以的。 +- 注意下面配置的服务协议,这里选择的是 HTTP,也支持 HTTPS,GRPC 的格式。 + +#### 5.3 创建服务(MCP) + +
+ +
+ +在 AI网关管理下,MCP 管理中,添加一个 MCP 服务。这里的用途是建一个 MCP 网关和后端服务建立起连接。 + +#### 5.4 添加工具(协议转换) + +
+ +
+ +
+ +
+ +- 接下来,还需要添加下工具。也就是 http 接口的具体能力。这里可以把 Swagger 的 json 文件复制过来,添加进去即可。 + +#### 5.5 验证接口(sse) + +
+ +
+ +
+ +
+ +在添加接口的地方,你会看到一个网关服务的地址。也就是 SSE 接入点,用于配置到 AI 客户端里。访问地址;http://127.0.0.1:8080/mcp-servers/toUpperCase/sse + +**好了,到这你的 AI MCP 网关就配置成功了,你可以把各类服务端的接口转换为 MCP 协议结构进行使用。** + +### 6. 接口转发(AI) + +
+ +
+ +AI 网关管理里,还提供了关于 AI 接口的配置。它可以进行统一管理,适合项目的接入使用。做 AI 类的项目,可以把 AI 请求,统一用这样一套东西管理起来。 + +## 三、调用测试 + +
+ +
+ +```java +13:55:19.581 [main] INFO cn.bugstack.xfg.dev.tech.test.ApiTest -- 测试结果:字符串 "xiaofuge" 转换为大写后是 "XIAOFUGE"。 +``` + +- 调用后可以看到把小写的字符串 `xiaofuge` 转换为大写的 `XIAOFUGE` +- 你也可以进入到 TestServiceController 看到调用的日志。 + +## 四、深入学习 + +目前,小傅哥也正在带着粉丝伙伴学习 AI 项目,[《AI MCP 网关》](https://origin.bugstack.cn/md/project/ai-mcp-gateway/ai-mcp-gateway.html),就是目前在持续更新的一个实战类项目。 + +该项目是 AI Agent 智能体,关于 MCP 协议对接的通用网关服务项目,以解决各类业务接口便捷转换为 MCP 协议而设计实现。通过这样的配置,可以大大的简化从普通http、rpc接口到 MCP 协议的转换操作。这样的项目,也是每个互联网公司在做 AI Agent 智能体时,必备的基础设施项目。 + +### 1. 项目架构 + +
+ +
+ +### 2. 工程模块 + +
+ +
+ +### 3. 协议转换 + +
+ +
+ +在此项目中,小傅哥一遍带着你分析调试 Spring AI MCP 上下文源码,一边带着你写 AI MCP 网关代码。让你全面的厘清系统设计,又能亲手实现出一套 AI MCP 网关。 + diff --git a/docs/md/road-map/higress.md b/docs/md/road-map/higress.md new file mode 100644 index 000000000..5316e16b2 --- /dev/null +++ b/docs/md/road-map/higress.md @@ -0,0 +1,169 @@ +--- +title: Higress +lock: need +--- + +# 体验下,大厂在使用功能的API网关! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +还是在22年的时候,小傅哥做了一套基于 Netty 协议转换和通信的 API网关,分享给伙伴们学习使用,增加一些业务开发以外的知识积累。不过很多伙伴都问过小傅哥,为啥要自研网关呢?SpringCloud Gateway 不就可以用吗?🤔 那你知道为什么自研吗? + +
+ +
+ +不少伙伴问为啥自研,😂 但其实从我进入互联网大厂,核心的分布式技术框架,几乎就全部都是公司自研。从 rpc、mq、缓存组件(配合redis集群)、配置中心、分库分表、任务调度、全链路监控,再到我们提到的 API 网关,全部的都是自研。 + +后来才知道,因为之前用过一些开源组件,在流程承载方面发生过重大事故。因为是开源的组件,没法对每一个细节进行把控。而全部的自研,就会有非常强的把控力度,各个细节实现都可以做具体的优化方案,同时所有的组件自研,还可以更好的串联起来使用。另外还有一个点,就是这些开源的组件,更容易被攻击,如果有漏洞要升级,那公司全升级一遍的成本,不亚于一次大规模裁员的赔偿!当然,一些中小厂还是用市面开源的就好,因为自研的成本并不低! + +那么为了让大家更好的了解下大厂的API,今天我们就来体验一款大厂开发的元原生API网关。有了这样一个学习,在看API网关项目,也会更清楚自己在做什么。 + +- 官网:[https://higress.io/](https://higress.io/) +- 源码:[https://github.com/alibaba/higress](https://github.com/alibaba/higress) +- 部署:[https://github.com/higress-group/higress-standalone](https://github.com/higress-group/higress-standalone) - `独立运行版,用于测试` +- 案例:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-higress](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-higress) - `部署测试` + +>Higress 是一套比较庞大的工程,为了让大家可以方便的体验到,小傅哥会教给大家怎么做一个独立的部署和配置网关负载。此外文末还提供了基于 Netty 的 API网关学习教程。 + +## 一、Higress 介绍 + +Higress 是基于阿里内部两年多的 Envoy Gateway 实践沉淀,以开源 [Istio](https://github.com/istio/istio) 与 [Envoy](https://github.com/envoyproxy/envoy) 为核心构建的云原生 API 网关。Higress 实现了安全防护网关、流量网关、微服务网关三层网关合一,可以显著降低网关的部署和运维成本。 + +
+ +
+ +- **生产等级**;支持每秒请求量达数十万级的大规模场景。彻底摆脱 reload 引起的流量抖动,配置变更毫秒级生效且业务无感。 +- **平滑演进**;支持 Nacos/Zookeeper/Eureka 等多种注册中心,可以不依赖 K8s Service 进行服务发现,支持非容器架构平滑演进到云原生架构。同时支持 Nginx、ServiceMesh。这些方面是非常重要的,也就是你之前部署的到各个方面的服务,都可以被 Higress 统一管理,这也是我们设计网关的目的。 +- **便于扩展**;提供 Wasm、Lua、进程外三种插件扩展机制,支持多语言编写插件,生效粒度支持全局级、域名级,路由级。插件支持热更新,变更插件逻辑和配置都对流量无损。 + +>接下来,小傅哥就带着大家安装部署体验下。什么东西都是上手了才有感觉。 + +## 二、环境部署 + +- 云服务器:2c4g 最低,我是用的 2c8g 体验的。[https://yun.xfg.plus](https://yun.xfg.plus) - 价格实惠。 +- 基础环境:Docker、Portainer、Git 【在小傅哥的 bugstack.cn 路书中都有讲解安装和使用】 + +
+ +
+ +- 整体安装完会如图所示。 + +### 1. 软件准备 + +#### 1.1 方式一 + +在你的 Linux 服务器,通过 Git 命令检出安装项目; + +```java +git clone https://github.com/higress-group/higress-standalone.git +``` + +#### 1.2 方式二 + +手动下载;https://github.com/higress-group/higress-standalone/archive/refs/heads/main.zip 在通过 ssh 的 sftp 工具上传到云服务器。之后解压 unzip + +### 2. 安装 higress + +#### 2.1 执行 configure.sh + +```java +[root@lavm-aqhgp9nber github]# cd higress-standalone-main/bin/ +[root@lavm-aqhgp9nber bin]# ls +base.sh configure.sh logs.sh reset.sh shutdown.sh startup.sh status.sh update.sh +[root@lavm-aqhgp9nber bin]# ./configure.sh +``` + +- 执行 ./configure.sh 后,注意选择 nacos 其他的默认值就可以,直接回车。 +- 执行过程会自动检测,nacos 的安装和删掉。这些不用操作。 +- 如果执行中遇到了失败或者自己选择错了,可以重新执行 ./configure.sh -r + +#### 2.2 执行 startup.sh + +```java +[root@lavm-aqhgp9nber bin]# ./startup.sh +``` + +- 这一步就傻瓜式的了,直接就可以安装完成。 + +### 3. 安装 nginx + +通过安装 Nginx 模拟出2个请求服务地址,如果你部署 SpringBoot 提供出 HTTP 接口也是可以。 + +
+ +
+ +- 上传到服务器端执行脚本 `docker-compose -f nginx-docker-compose.yml up -d` + +## 三、网关配置 + +### 1. 服务来源 + +这里要配置的是,通过 https://xxx/api 访问到网关服务后,要访问到哪些服务来源上。 + +
+ +
+ +- 服务来源支持非常多的类型,包括;Nacos、Zookeeper、Consul、Eureka、固定地址、DNS 域名。这里小傅哥选择固定地址配置。 +- 分别配置了 nginx-01、nginx-02 这样我们配置路由的时候可以负载到这2个地址。 + +### 2. 路由配置 + +路由配置的作用就是指定你通过网关地址负载到对应的目标服务上,这里我们会让 http://117.72.37.243/api/ 请求负载到2个 nginx 上。 + +
+ +
+ +- 通过路由api地址,访问到目标服务。 +- 这里可以配置的玩法还有很多,可以自己在尝试下。 + +### 3. 策略配置 + +
+ +
+ +- 你可以为访问自己的路由接口配置对应的插件,比如重写URl、跨域、限流等各项功能。 + +## 四、服务验证 + +- 地址:[http://117.72.37.243/api/](http://117.72.37.243/api/) - 你需要换成自己的IP地址 + +
+ +
+ +- 首先,我在 Nginx 的 HTML 中,配置了2个不同的请求结果,一个 01、一个 02 +- 之后,访问网关地址加上 /api 接下来访问就会看到结果的变化了。 + +>有了这个大厂网关的体验,大家就了解了一套网关是如何使用的,作用是什么啦。接下来,如果感兴趣技术的积累,想扩展下自己,也可以学习一套网关代码的实现。 + +## 五、网关学习 + +除了业务开发,小傅哥自己也是非常感兴趣于这样的网关技术组件的实现,所以在日常的工作中也积累了很多网关的设计。后来在22年做了一套轻量的网关系统,把核心的内核逻辑实现出来让大家学习。帮助了很多伙伴学习项目后找到了不错的工作。 + +
+ +
+ +整个**API网关**设计核心内容分为这么五块; + +- `第一块`:是关于通信的协议处理,也是网关最本质的处理内容。这里需要借助 NIO 框架 Netty 处理 HTTP 请求,并进行协议转换泛化调用到 RPC 服务返回数据信息。 +- `第二块`:是关于注册中心,这里需要把网关通信系统当做一个算力,每部署一个网关服务,都需要向注册中心注册一个算力。而注册中心还需要接收 RPC 接口的注册,这部分可以是基于 SDK 自动扫描注册也可以是人工介入管理。当 RPC 注册完成后,会被注册中心经过AHP权重计算分配到一组网关算力上进行使用。 +- `第三块`:是关于路由服务,每一个注册上来的Netty通信服务,都会与他对应提供的分组网关相关联,例如:wg/(a/b/c)/user/... a/b/c 需要匹配到 Nginx 路由配置上,以确保不同的接口调用请求到对应的 Netty 服务上。PS:如果对应错误或者为启动,可能会发生类似B站事故。 +- `第四块`:责任链下插件模块的调用,鉴权、授信、熔断、降级、限流、切量等,这些服务虽然不算是网关的定义下的内容,但作为共性通用的服务,它们通常也是被放到网关层统一设计实现和使用的。 +- `第五块`:管理后台,作为一个网关项目少不了一个与之对应的管理后台,用户接口的注册维护、mock测试、日志查询、流量整形、网关管理等服务。 + +>项目学习地址:[https://bugstack.cn/md/assembly/api-gateway/api-gateway.html](https://bugstack.cn/md/assembly/api-gateway/api-gateway.html) \ No newline at end of file diff --git a/docs/md/road-map/http.md b/docs/md/road-map/http.md new file mode 100644 index 000000000..ff004b0fc --- /dev/null +++ b/docs/md/road-map/http.md @@ -0,0 +1,279 @@ +--- +title: http +lock: need +--- + +# HTTP 框架使用和场景实战 - 结合ChatGLM自动回帖! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,HTTP的常用框架使用,HTTP接口快速对接方式。以及在编码实战中练习 HTTP 对数据的采集、ChatGLM对接、问题回答。这样的场景学习,非常适合以后大家在做一些智能化问答进行参考使用。 + +本文涉及的工程: +- xfg-dev-tech-http:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-http](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-http) +- chatglm-sdk-java:[https://bugstack.cn/md/project/chatgpt/sdk/chatglm-sdk-java.html](https://bugstack.cn/md/project/chatgpt/sdk/chatglm-sdk-java.html) + +## 一、案例背景 + +虽然在分布式架构的微服务内部是RPC调用,但在对外的Web/H5等场景下,则需要使用HTTP协议进行调用。因而我们在对接公司以外的其他服务时,通常都是HTTP协议,包括你对接微信支付、ChatGLM等。当然也有少部分使用 websocket 协议。 + +但众所周知HTTP的调用,会涉及较多的信息配置。包括;请求头、入参、出参,而这些内容都是非对象化的设计。很多人在对接特别多的 HTTP 请求以后,自己的工程代码就会变得非常混乱。所以我们需要用一些 HTTP 框架,来解决这个场景问题,让 HTTP 的调用更加优雅。 + +那么本章节小傅哥会结合知识星球的接口进行案例场景学习; + +
+ +
+ +1. **星球免费加入**:[https://wx.zsxq.com/dweb2/index/group/28885518425541](https://wx.zsxq.com/dweb2/index/group/28885518425541) +2. 本章节所提供的课程源码,即可复现截图中的展示内容。自动化Ai回复问答。 + +## 二、接口提取 + +接下来,小傅哥会告诉如何非常快速的使用Java代码对接上HTTP调用。—— 把🐘大象装冰箱统共分3步; + +### 1. 第一步 + +
+ +
+ +- 首先,在PC端谷歌浏览器,打开知识星球首页:[https://wx.zsxq.com/dweb2/index/group/28885518425541](https://wx.zsxq.com/dweb2/index/group/28885518425541) +- 之后,摁F12打开控制台,选择网络模块。 +- 最后,找到 topics 接口查询。在接口上鼠标右键。**以cURL格式复制** + +### 2. 第二步 + +
+ +
+ +- 首先,打开 [ApiPost](https://www.apipost.cn/) 工具中,如果没有可以下载一个。 +- 之后,点击左侧的导入接口,这个里面可以直接把 cURL 格式接口导入进去。 +- 最后,点击**立即导入** - 导入后你就可以点击**发送**按钮测试验证了。 + +### 3. 第三步 + +
+ +
+ +- 首先,点击生成代码,会弹出一个各类语言对接代码案例。 +- 之后,在你需要的类型代码上复制生成的代码。 +- 最后,把代码粘贴到Java工程中测试。这部分代码可以参考案例工程 `cn.bugstack.xfg.dev.tech.test#HttpClientTest|OKHttpTest|Retrofit2Test` + +--- + +除此之外,你还可以使用Ai工具,来生成对接的测试代码。 + +
+ +
+ +- 这样的方式也是非常好用的,而且如果有运行问题,你还可以继续提问。 +- 用编程经验的人用 OpenAi 会更舒服,没有编程经验的人完全依赖 OpenAi 也会遇到各种错误。 + +## 三、玩个场景 + +### 1. 需求说明 + +接下来,我结合所学技术锻炼下。结合星球完成一个自动Ai回贴的功能,通过定时任务扫描星球接口帖子,并对未回答且圈【@小傅哥】的帖子进行采集回答。 + +
+ +
+ +
+ +
+ +- 这里的接口,需要用到2个:一个扫描帖子的HTTP接口,一个回复帖子的HTTP接口。 +- ChatGLM部分,小傅哥已经做了 SDK 非常好对接。[https://github.com/fuzhengwei/chatglm-sdk-java](https://github.com/fuzhengwei/chatglm-sdk-java) + +### 2. 工程结构 + +
+ +
+ +- 首先,你可以下载工程代码,按照这个结构来看代码。 +- 之后,工程中对接了 ChatGLM SDK 并做了相关的配置在YML中。此外 YML中的 ChatGLM SDK 需要的 ApiSecretKey 可以通过官网申请。之后星球的 cookie 可以通过 F12 请求头中复制 Cookie 信息。 +- 最后,是 ZSXQJob 的开发使用。 + +### 3. 代码实现 + +#### 4.1 ChatGLM SDK 对接 + +**源码**:`cn.bugstack.xfg.dev.tech.config.ChatGLMSDKConfig` + +```java +@Bean +@ConditionalOnProperty(value = "chatglm.sdk.config.enabled", havingValue = "true", matchIfMissing = false) +public OpenAiSession openAiSession(ChatGLMSDKConfigProperties properties) { + // 1. 配置文件 + cn.bugstack.chatglm.session.Configuration configuration = new cn.bugstack.chatglm.session.Configuration(); + configuration.setApiHost(properties.getApiHost()); + configuration.setApiSecretKey(properties.getApiSecretKey()); + // 2. 会话工厂 + OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); + // 3. 开启会话 + return factory.openSession(); +} +``` + +- 因为我们需要一个自动的智能Ai回帖,所以需要使用到 [ChatGLM](https://github.com/fuzhengwei/chatglm-sdk-java)。当然你也可以对接其他大厂开发的 OpenAi SDK 使用。 + +#### 4.2 流程串联 + +**源码**:`cn.bugstack.xfg.dev.tech.job.ZSXQJob` + +```java +@Scheduled(cron = "0/10 * * * * ?") +public void exec() throws Exception { + Response response = getResponse(cookie); + RespData respData = response.getRespData(); + List topics = respData.getTopics(); + for (TopicsItem topicsItem : topics) { + // 是否回答过判断 + if (!isCommentDone(topicsItem)) continue; + // 找到圈我我帖子 + long topicId = topicsItem.getTopicId(); + Talk talk = topicsItem.getTalk(); + // " 提问 java 冒泡排序" + String text = talk.getText(); + // 正在匹配处理 + String regex = " (.*)"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + String uid = matcher.group(1); + String remainingText = matcher.group(3); + if ("241858242255511".equals(uid)) { + if (null == openAiSession) { + log.info("你没有开启 ChatGLM 参考yml配置文件来开启"); + // 你可以使用 ChatGLM SDK 进行回答,回复问题; + comment(cookie, topicId, "【测试,只回答圈我的帖子】对接 ChatGLM SDK https://bugstack.cn/md/project/chatgpt/sdk/chatglm-sdk-java.html 回答:" + remainingText); + } else { + log.info("ChatGLM 进入回答 {} {}", topicId, remainingText); + if (topicIds.contains(topicId)){ + continue; + } else { + topicIds.add(topicId); + } + new Thread(() -> { + + // 入参;模型、请求信息 + ChatCompletionRequest request = new ChatCompletionRequest(); + request.setModel(Model.CHATGLM_LITE); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro + request.setPrompt(new ArrayList() { + private static final long serialVersionUID = -7988151926241837899L; + { + add(ChatCompletionRequest.Prompt.builder() + .role(Role.user.getCode()) + .content(remainingText) + .build()); + } + }); + + // 请求 + try { + StringBuilder content = new StringBuilder(); + openAiSession.completions(request, new EventSourceListener() { + @Override + public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) { + ChatCompletionResponse chatCompletionResponse = com.alibaba.fastjson.JSON.parseObject(data, ChatCompletionResponse.class); + log.info("测试结果 onEvent:{}", chatCompletionResponse.getData()); + // type 消息类型,add 增量,finish 结束,error 错误,interrupted 中断 + if (EventType.finish.getCode().equals(type)) { + ChatCompletionResponse.Meta meta = com.alibaba.fastjson.JSON.parseObject(chatCompletionResponse.getMeta(), ChatCompletionResponse.Meta.class); + log.info("[输出结束] Tokens {}", com.alibaba.fastjson.JSON.toJSONString(meta)); + } + content.append(chatCompletionResponse.getData()); + } + @Override + public void onClosed(EventSource eventSource) { + log.info("对话完成"); + // 你可以使用 ChatGLM SDK 进行回答,回复问题; + comment(cookie, topicId, "ChatGLM 回答:" + content); + topicIds.remove(topicId); + } + }); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }).start(); + } + } + } + } +} +``` + +- 以上代码就是自动智能Ai回贴的流程代码,因为是做示例,所以没有按照职责边界做拆分。 +- 这段代码中会扫码帖子,并对符合流程需要我【@小傅哥】回答的帖子,进行采集和回答。 +- 回答帖子会创建一个线程,调用 ChatGLM 并对返回的流式数据最封装。最后在完成时候,进行回答操作。 +- 注意,这里因为限定的判断了 uId = 241858242255511 所以只有@小傅哥,才会回答。你也可以通过回复帖子,查看自己的 uId 替换。 +- 此外,更多的细节代码,可以参考工程。 + +## 四、测试验证 + +### 1. 环境配置 + +```pom +chatglm: + sdk: + config: + # 状态;true = 开启、false 关闭 + enabled: true + # 官网地址 + api-host: https://open.bigmodel.cn/ + # 官网申请 https://open.bigmodel.cn/usercenter/apikeys + api-secret-key: 4e087e4135306ef4a676f0cce*****.sgP2D***** + # 知识星球 Cookie 你需要获取你的 cookie 登录 + cookie: zsxq_access_token=86EB23***** +``` + +- 首先你需要在 application-dev.yml 配置相关的信息,这些信息可以从下面的说明中获取。 +- 如果你暂时又申请不到 ChatGLM 还想测试,可以把 enabled 配置为 false + +#### 1.1 获取 ChatGLM ApiSecretKey + +
+ +
+ +- 地址:[https://open.bigmodel.cn/usercenter/apikeys](https://open.bigmodel.cn/usercenter/apikeys) +- 备注:申请开通即可,很快就可以使用。 + +#### 1.2 获取星球 Cookie + +
+ +
+ +- 星球:[https://wx.zsxq.com/dweb2/index/group/28885518425541](https://wx.zsxq.com/dweb2/index/group/28885518425541) +- 获取:你可以在进入后,点击任意一个调用的接口,找到 Cookie 信息。 +- 注意:如果你对接了模拟登录接口,还可以自动的获取 Cookie 信息。 + +### 2. 启动程序和回贴 + +#### 2.1 发个帖子 + +
+ +
+ +- 注意要圈**小傅哥**发帖子,如果你程序中修改了uId 为自己,那么可以圈你提问。 + +#### 2.2 回复帖子 + +
+ +
+- 这个是你就会看到后台会快速运行检测帖子和回答。 +- 好啦,运行到这就全部完成了。你也可以去做做测试了。 diff --git a/docs/md/road-map/ignite.md b/docs/md/road-map/ignite.md new file mode 100644 index 000000000..e86baa8dc --- /dev/null +++ b/docs/md/road-map/ignite.md @@ -0,0 +1,217 @@ +--- +title: Ignite +lock: need +--- + +# Apache Ignite —— 一种支持SQL语句的纯内存数据库! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式,向读者介绍一款基于内存的分布式SQL数据库Apache Ignite的部署、使用和性能测试。 + +那有了Redis这样优秀的NoSql数据库,为啥还会用到Apache Ignite呢? + +不知道你是否有想过一个事情,就是Redis这样的内存数据库,如果能支持SQL语句,是不是就更牛了。这样一来本身存在MySQL数据库里的数据,就可以原封不动的封到内存中使用。既保留了原有的业务逻辑,又使用上了内存读取高性能。 + +所以,它来了。Apache Ignite是一个兼容ANSI-99、水平可扩展以及容错的分布式SQL数据库,作为一个SQL数据库,Ignite支持所有的DML指令,包括SELECT、UPDATE、INSERT和DELETE,它还实现了一个与分布式系统有关的DDL指令的子集。Ignite的一个突出特性是完全支持分布式的SQL关联,Ignite支持并置和非并置的数据关联。并置时,关联是在每个节点的可用数据集上执行的,而不需要在网络中移动大量的数据,这种方式在分布式数据库中提供了最好的扩展性和性能。 + +
+ +
+ +本文涉及的工程: + +- xfg-dev-tech-ignite:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-ignite](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-ignite)- `docs/dev-ops 提供了 mysql、ignite 安装脚本,和数据初始化操作。` +- 官网站点:[https://ignite.apache.org/](https://ignite.apache.org/) - `官网 docs 可以阅读安装和使用` +- 中文文档:[https://ignite-service.cn/doc/2.7.0/sql/](https://ignite-service.cn/doc/2.7.0/sql/) - `这是一个 Ignite 的中文站点` +- 管理工具:[DBeaver](https://dbeaver.io/download/) - `安装最新版,直接可以连接 Ignite 数据库` + +## 一、案例说明 + +本案例中为了对比MySQL和Ignite的性能差异,以及如何同时使用两套数据库,这里小傅哥会在一个工程中分别配置出不同的数据库对应数据源的创建和MyBatis的配置用。如果说你做过小傅哥的 [DB-Router](https://bugstack.cn/md/road-map/db-router.html) 组件开发,那么也可以在组件中添加对Ignite内存数据库的路由配置。这样的使用会更加方便,也可以自动的通过注解来切换数据源的使用。 + +
+ +
+ +- SpringBoot应用的yml配置,本身默认是配置一个数据源的。但我们这里需要把Ignite也配置出数据源并让它可以结合MyBatis进行使用。所以需要做一点编码的扩展使用。`具体可以参考源码` +- 与此同时还需要考虑对 Dao、Mapper 分不同的路径进行加载使用。因为本身来说,他们就是一套东西的不同数据源使用方式。 + +## 二、环境安装 + +在安装执行 docker-compose.yml 脚本之前,你需要先在本地安装 [docker](https://bugstack.cn/md/road-map/docker.html)之后 IntelliJ IDEA 打开 docker-compose.yml 文件,如图操作即可安装。 + +
+ +
+ +- 在 docker-compose.yml 中会先安装 MySQL 并执行 sql 文件夹里的 SQL 语句初始化数据库表。之后会安装 Ignite 环境,安装后需要用到 [DBeaver](https://dbeaver.io/download/) 连接使用。同时 compose 中还安装了一个 ApacheBench 压测工具。 + +## 三、连接配置 + +首先确保你已经安装过 [DBeaver](https://dbeaver.io/download/) ,之后就可以连接和创建表了。 + +### 1. 选择 Ignite + +
+ +
+ +### 2. 验证链接 + +
+ +
+ +### 3. 创建库表 + +
+ +
+ +### 4. 创建完成 + +
+ +
+ +- 之后你所有做的修改,包括你自己手动创建表、字段、索引,都需要点保存。否则它是红色的,不生效。 + +## 四、功能配置 + +### 1. 工程结构 + +
+ +
+ +- app层;application-dev.yml 配置多套数据源,并在 DataSourceConfig 中实现多套数据源的加载。 +- infrastructure层;用于提供数据的 dao 层,这里分开2套分别提供。 +- trigger;触发器层,提供了调用 Ignite、MySQL 的测试验证接口。 + +### 2. 数据源创建 + +```xml + + + org.apache.ignite + ignite-core + 2.15.0 + + + + org.apache.ignite + ignite-spring + 2.15.0 + +``` + +- 注意引入 ignite 的 pom 配置 + +#### 2.1 Ignite + +**源码**:`cn.bugstack.xfg.dev.tech.config.DataSourceConfig#IgniteMyBatisConfig` + +```java +@Configuration +@MapperScan(basePackages = "cn.bugstack.xfg.dev.tech.infrastructure.ignite.dao", sqlSessionFactoryRef = "igniteSqlSessionFactory") +static class IgniteMyBatisConfig { + @Bean("igniteDataSource") + @ConfigurationProperties(prefix = "spring.ignite.datasource") + public DataSource igniteDataSource(Environment environment) { + IgniteConfiguration igniteConfig = new IgniteConfiguration(); + DataStorageConfiguration dataStorageConfig = new DataStorageConfiguration(); + DataRegionConfiguration defaultDataRegionConfig = new DataRegionConfiguration(); + defaultDataRegionConfig.setPersistenceEnabled(false); + dataStorageConfig.setDefaultDataRegionConfiguration(defaultDataRegionConfig); + igniteConfig.setDataStorageConfiguration(dataStorageConfig); + ConnectorConfiguration configuration = new ConnectorConfiguration(); + configuration.setIdleTimeout(6000); + configuration.setThreadPoolSize(100); + configuration.setIdleTimeout(60000); + igniteConfig.setConnectorConfiguration(configuration); + return DataSourceBuilder.create() + .url(environment.getProperty("spring.ignite.datasource.url")) + .driverClassName(environment.getProperty("spring.ignite.datasource.driver-class-name")) + .build(); + } + @Bean("igniteSqlSessionFactory") + public SqlSessionFactory igniteSqlSessionFactory(DataSource igniteDataSource) throws Exception { + SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); + factoryBean.setDataSource(igniteDataSource); + factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mybatis/mapper/ignite/*.xml")); + return factoryBean.getObject(); + } +} +``` + +- 创建 Ignite 的数据源,以及对应的 igniteSqlSessionFactory 工厂。这样就把 MyBatis 给关联起来了。 + +#### 2.2 MySQL + +**源码**:`cn.bugstack.xfg.dev.tech.config.DataSourceConfig#MysqlMyBatisConfig` + +```java +@Configuration +@MapperScan(basePackages = "cn.bugstack.xfg.dev.tech.infrastructure.mysql.dao", sqlSessionFactoryRef = "mysqlSqlSessionFactory") +static class MysqlMyBatisConfig { + @Bean("mysqlDataSource") + @ConfigurationProperties(prefix = "spring.mysql.datasource") + public DataSource mysqlDataSource(Environment environment) { + return DataSourceBuilder.create() + .url(environment.getProperty("spring.mysql.datasource.url")) + .driverClassName(environment.getProperty("spring.mysql.datasource.driver-class-name")) + .build(); + } + @Bean("mysqlSqlSessionFactory") + public SqlSessionFactory mysqlSqlSessionFactory(DataSource mysqlDataSource) throws Exception { + SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); + factoryBean.setDataSource(mysqlDataSource); + factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:/mybatis/mapper/mysql/*.xml")); + return factoryBean.getObject(); + } +} +``` + +- 同样,创建 MySQL 的数据源以及 MyBatis 的关联操作。 + +## 五、性能测试 + +小傅哥提供了 IgniteController、MySQLController 两个 HTTP 访问类,分别提供了两个数据库的压测操作。 + +
+ +
+ +- 这里提供了 Ignite、MySQL 的 HTTP 访问接口,分别进行压测。 + +### 1. Ignite 压测 + +- 初始化ID值:`ab -c 1 -n 1 http://127.0.0.1:8091/api/ignite/start` +- 写入数据:`ab -c 20 -n 50000 http://127.0.0.1:8091/api/ignite/insert` +- 随机加载内存1000条数据:`ab -c 20 -n 1000 http://127.0.0.1:8091/api/ignite/cacheData` +- 根据加载到内存的数据查询Ignite:`ab -c 20 -n 1000 http://127.0.0.1:8091/api/ignite/selectByOrderId` - 记得给 OrderId 加索引 + +
+ +
+ +### 2. MySQL 压测 + +- 初始化ID值:`ab -c 1 -n 1 http://127.0.0.1:8091/api/ignite/start` +- 写入数据:`ab -c 20 -n 50000 http://127.0.0.1:8091/api/ignite/insert` +- 随机加载内存1000条数据:`ab -c 20 -n 1000 http://127.0.0.1:8091/api/ignite/cacheData` +- 根据加载到内存的数据查询MySQL:`ab -c 20 -n 1000 http://127.0.0.1:8091/api/ignite/selectByOrderId` - 记得给 OrderId 加索引 + +
+ +
+ +--- + +**综上**,Ignite 略胜一筹,确实纯内存的数据库会更快一些。也适合在一些需要内存计算的场景中,并且不改变MySQL表结构的情况下,做一些优化的是使用。 \ No newline at end of file diff --git a/docs/md/road-map/intellij-idea-remote-jvm-debug.md b/docs/md/road-map/intellij-idea-remote-jvm-debug.md new file mode 100644 index 000000000..185282c0f --- /dev/null +++ b/docs/md/road-map/intellij-idea-remote-jvm-debug.md @@ -0,0 +1,225 @@ +--- +title: IntelliJ IDEA Remote JVM Debug +lock: need +--- + +# IntelliJ IDEA Remote JVM Debug - 本地远程调试服务器运行代码 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +我遇到过一种bug场景,本地没什么问题,部署到服务器上就不行了。往往遇到这样的问题,要花费大量的时间检查程序逻辑,一个个方法 mock 测试验证。那有人会问了,你怎么不在本地 debug 调试下呢? + +
+ +
+ +**你的代码本地没法启动!** + +其实并不是所有的工程代码,都能本地启动运行的。尤其复杂的工程,与外部对接非常多,甚至还有一些是风险控制的问题,本地是不能启动直接调用的。也就是控制研发人员,不允许本地程序调用其他程序的接口。那么对于这样的工程,研发的自测就要通过编写 [mock](https://bugstack.cn/md/road-map/mock.html) 方式进行单元化测试。 + +不过这里会有一个问题,单元化的测试,mock 的数据是不会随着外部所有程序的调整动态的变更的,而是随着研发编写需求,一次写好后,后续如果这个功能没有被调整,那么 mock 测试也不会在调整了。同时还因为 mock 覆盖的场景不全,不知道引入的外部那么多接口都有哪些新增的逻辑。因而,你可能本地运行没问题,但部署到测试环境,就会有一些不缺性的报错。 + +对于这里的报错,当你没有 debug 手段的时候,就要把前后的报错数据,都要复制到本地,通过 mock 的方式验证程序逻辑,一点点排查。不过,这个过程也要花费好长时间,尤其是一些复杂的逻辑与外部交互又非常多的时候,调试起来很耗费时间。 + +所以,程序员👨🏻‍💻对于实在难以调试的代码,还有一种方式就是远程调试。把代码部署到服务器,通过请求服务器的接口,本地的 IntelliJ IDEA 打开的工程,就可以调试对应的运行数据。可以非常高效的解决 bug! + +接下来,小傅哥就带着大家做一个这样的案例。如果你是一个新手小白,就更有必要学习一下这样的手段了。 + +> 🧧 文末还提供了17个实战项目,全套的文档、视频、代码,都可以获取。还有非常清晰的学习路线!嘎嘎冲! + +## 一、测试工程 + +这里小傅哥为你准备好了一个测试工程,你可以直接下载验证。 + +
+ +
+ +- 地址:[https://github.com/fuzhengwei/xfg-dev-tech-remote-jvm-debug](https://github.com/fuzhengwei/xfg-dev-tech-remote-jvm-debug) +- 环境:JDK 1.8、SpringBoot 2.7 +- 如图,工程提供了简单的 TestApiController 一个 http 测试入口。之后通过 Dockerfie 方式构建镜像,以及提供了 docker-compose-app.yml 启动工程。重要的是 JAVA_REMOTE_DEBUG 的配置。这是一种 JavaAgent 技术,如果感兴趣可以进入小傅哥的博客,[https://bugstack.cn/](https://bugstack.cn/) 字节码编程中学习。 + +### 1. 测试接口 + +```java +@Slf4j +@RestController() +@CrossOrigin("*") +@RequestMapping("/api/v1/test/") +public class TestApiController { + + /** + * curl --request POST \ + * --url 'http://127.0.01:8091/xfg/api/v1/test/group_buy_notify?requestDTO=1111' + * + * 注意,yml 里配置了应用根目录;server.servlet.context-path: /xfg + */ + @RequestMapping(value = "group_buy_notify", method = RequestMethod.POST) + public String groupBuyNotify(@RequestParam String requestDTO) { + log.info("请求参数 {}", JSON.toJSONString(requestDTO)); + + return "success"; + } + +} +``` + +- 一个简单的测试接口,访问接口地址为;`http://127.0.01:8091/xfg/api/v1/test/group_buy_notify?requestDTO=1111` +- 你可以复制 curl 部分,导入到 apipost/apifox 等工具里使用。 + +### 2. Dockerfile + +```java +# 基础镜像 +FROM openjdk:8-jre-slim + +# 作者 +MAINTAINER xiaofuge + +# 配置 +ENV PARAMS="" + +# 时区 +ENV TZ=PRC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# 添加应用 +ADD target/xfg-dev-tech-remote-jvm-debug-app.jar /xfg-dev-tech-remote-jvm-debug-app.jar +EXPOSE 8091 5005 + +ENTRYPOINT ["sh","-c","java $JAVA_REMOTE_DEBUG -jar $JAVA_OPTS /xfg-dev-tech-remote-jvm-debug-app.jar $PARAMS"] +``` + +- ENTRYPOINT,添加了一个 $JAVA_REMOTE_DEBUG 的动态入参,其实这个位置要填入的就是 `-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005` 但并不是上线的所有程序都要做这样的事情,一般只有测试环境才需要。 +- EXPOSE 8091 5005 对外暴漏应用程序所需的 8091 5005 端口。 + +### 3. DockerCompose 部署脚本 + +```java +# /usr/local/bin/docker-compose -f /docs/dev-ops/environment/environment-docker-compose-2.4.yml up -d +version: '3.8' +# docker-compose -f docker-compose-app.yml up -d +services: + xfg-dev-tech-remote-jvm-debug-app: + image: fuzhengwei/xfg-dev-tech-remote-jvm-debug-app:1.0 + container_name: xfg-dev-tech-remote-jvm-debug-app + restart: on-failure + ports: + - "5005:5005" + - "8091:8091" + environment: + - TZ=PRC + - SERVER_PORT=8091 + # 2c4g 配置,4c8g 翻倍,-Xms4096m -Xmx4096m | -Xmx –Xms:指定java堆最大值(默认值是物理内存的1/4(<1GB))和初始java堆最小值(默认值是物理内存的1/64(<1GB)) + - JAVA_OPTS=-Xms2048m -Xmx2048m + - JAVA_REMOTE_DEBUG=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005 + volumes: + - ./log:/data/log + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + networks: + - my-network + +networks: + my-network: + driver: bridge +``` + +- 部署服务注意,要对外提供 5005、8091 两个映射端口。对于这种端口的暴漏说明,我已经提供好了基础教程,可以在这里扩展学习;[https://bugstack.cn/md/road-map/docker-what.html](https://bugstack.cn/md/road-map/docker-what.html) +- 另外在 environment 环境中,配置 `- JAVA_REMOTE_DEBUG=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:5005` 这样就可以把远侧调试的服务给启动起来了。相当于你在自己的电脑的 IntelliJ IDEA 与服务器上的应用,通过 5005 端口进行通信完成测试debug验证。 + +## 二、部署程序 + +确保你自己本地已经安装好了 [Docker](https://www.docker.com/) Mac、Windows 电脑都可以安装。 + +### 1. 打包程序 + +
+ +
+ +- 通过 IntelliJ Install 打包程序。其实就是 Maven 命令,mvn clean install 的操作。 + + +### 2. 构建镜像 + +
+ +
+ +1. mac 电脑可以点绿色箭头。 +2. windows 电脑,可以通过 powershell 执行 ./build.sh + +### 3. 发布项目 + +
+ +
+ +
+ +
+ +- mac 点击绿色箭头启动程序即可。 +- windows 打开 powershell 执行脚本 `docker-compose -f docker-compose-app.yml up -d` + +## 三、调试程序 + +### 1. IntelliJ IDEA 配置 + +
+ +
+ +- 首先,在运行调试按钮那,点击下拉框。增加一个新的调试配置。 +- 之后,点击+号,添加 `Remote JVM Debug` +- 最后,填写 IP 地址和端口,点击 Apply OK 即可。 + +### 2. 导入接口 + +
+ +
+ +- 导入接口到 ApiPost 中调用。其他的工具也可以,这样接口可以保存起来,方便以后调试。 + +### 3. 远程调试 + +#### 3.1 启动程序 + +
+ +
+ +- 以新建的远程 debug 方式,启动程序。 + +#### 3.2 添加断点 + +
+ +
+ +- 添加断点。注意,这会的程序部署的要一致,不能动代码。否则和远程部署的不一致,是不能调试的。 + +#### 3.3 调用接口 + +
+ +
+ +
+ +
+ +- 首先,在 ApiPost 中点击【发送】按钮。 +- 这会,你可以在打了断点的程序中,查看到请求的调用会进来。最终执行完,远程的部署的程序也会执行完。 + +🌶 好啦,到这你就已经学会了远程调试了。以后自己的简历中,对于项目的描述,此类问题,你也可以讲自己是如何做的远程调试。 diff --git a/docs/md/road-map/intellij-idea.md b/docs/md/road-map/intellij-idea.md new file mode 100644 index 000000000..cbf900a70 --- /dev/null +++ b/docs/md/road-map/intellij-idea.md @@ -0,0 +1,297 @@ +--- +title: IntelliJ IDEA +lock: need +--- + +# IntelliJ IDEA 开发工具使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +工欲善其事必先利其器,Java 技术再牛皮,也不能用 TXT 写代码,那效率太低。而开发 Java 的工具也不少,比如;Eclipse、MyEclipse、NetBeans、JDeveloper、BlueJ、JCreator 但在众多工具中,目前高校和企业较为常用的主要是 [Intellij IDEA](https://www.jetbrains.com.cn/idea/) 系列的开发工具。 + +所以小傅哥本文会优先介绍 Intellij IDEA 这款 Java 开发工具,方便伙伴更好的使用开发工具编写项目。 + +## 一、工具安装 + +### 1. 下载 + +**下载**:[https://www.jetbrains.com.cn/idea/download/?section=mac](https://www.jetbrains.com.cn/idea/download/?section=mac) - `Windows`/`macOS(Intel/Apple Silicon)`/`Linux` + +
+ +
+ +- IntelliJ IDEA Ultimate 旗舰版 - 付费的,功能更多。如果需要使用可以购买或者XX。 +- IntelliJ IDEA Community 社区版 - 免费的,对于大部人来说,免费的社区版就够用了。 + +### 2. 安装 + +
+ +
+ +1. 无论是 Windows 还是 Mac 都是双击下载的应用程序进行安装。 +2. Mac 电脑需要在弹出框后,把应用程序拖入到 Applications 中,这样就可以自动执行安装了。非常傻白甜的操作。 + +### 3. 主题 + +
+ +
+ +安装完成后,打开工具会提示你有一个新的UI主题,这里你可以选择 Enable New UI 点击后软件会重新启动。新的 UI 非常简洁。 + +### 4. 效果 + +
+ +
+ +这就是 IntelliJ IDEA 打开项目后的效果,分为左中右布局;四边栏为工具栏操作。 +- 左;展示项目 +- 中;编写代码,可以通过拖拉的方式,同时打开多个文件。这样在写代码的时候可以做个对比参考。 +- 右;Maven/Gradle 数据库等操作区域,你可以托拉拽一些其他窗口到右侧。 + +这些功能随着你的点击使用也就逐步了解了。 + +## 二、常用插件 + +IntelliJ IDEA 除了自身的功能强大以外,还可以安装各类提效的插件。这些插件可以辅助的帮助你完成代码开发。 + +
+ +
+ +- Jump to Line:调试代码,一步定位📌。 +- Maven Helper:Maven POM 依赖,查看、分析和排除相互冲突的依赖项。简单高效。 +- Statistis:代码统计插件,可以看到整个工程中你写了多少代码量。 +- Sequence Diagram:用于查看你的 工程中,代码执行流程的 UML 图,可以非常方便的熟悉一些默认代码。 +- FastRequest:类似于 Postman 的 IDEA 插件。它是一个强大的 restful api 工具包插件 +- Coverage:单元测试覆盖率统计插件 +- Squaretest:自动生成Mock测试插件 +- [CodeGeeX 智能AI编程助手](https://codegeex.cn/zh-CN/downloadGuide#vscode) + +此外,你也可以自己开发一些自己的 IDEA 插件,这里小傅哥写了教程[《IDEA Plugin 插件开发》](https://bugstack.cn/md/assembly/idea-plugin/2021-08-27-%E6%8A%80%E6%9C%AF%E8%B0%83%E7%A0%94%EF%BC%8CIDEA%20%E6%8F%92%E4%BB%B6%E6%80%8E%E4%B9%88%E5%BC%80%E5%8F%91%EF%BC%9F.html) vo2dto 这款用于对象转换的插件,就是小傅哥开发的。如果你有非常好用的插件,也可以在本文下修改提交。 + +## 三、快捷命令 + +- Shift 双击,搜索 +- Command/Ctrl + Shift + F 文件搜搜 +- Option + Command + L 格式化代码 —— 你可以格式化;整个工程、默认当前打开的文件、选中的代码区域 +- Control + Option + O 排掉当前类中,不需要的引入包 +- Shift + Option + 上/下 可以把当前行的代码,上移或者下移 + +**更多指令** Mac\Windows 有一些差别,但基本一样。 + +### Ctrl + +| 快捷键 | 介绍 | +| :--------------------- | :----------------------------------------------------------- | +| Ctrl + F | 在当前文件进行文本查找 `(必备)` | +| Ctrl + R | 在当前文件进行文本替换 `(必备)` | +| Ctrl + Z | 撤销 `(必备)` | +| Ctrl + Y | 删除光标所在行 或 删除选中的行 `(必备)` | +| Ctrl + X | 剪切光标所在行 或 剪切选择内容 | +| Ctrl + C | 复制光标所在行 或 复制选择内容 | +| Ctrl + D | 复制光标所在行 或 复制选择内容,并把复制内容插入光标位置下面 `(必备)` | +| Ctrl + W | 递进式选择代码块。可选中光标所在的单词或段落,连续按会在原有选中的基础上再扩展选中范围 `(必备)` | +| Ctrl + E | 显示最近打开的文件记录列表 `(必备)` | +| Ctrl + N | 根据输入的 **类名** 查找类文件 `(必备)` | +| Ctrl + G | 在当前文件跳转到指定行处 | +| Ctrl + J | 插入自定义动态代码模板 `(必备)` | +| Ctrl + P | 方法参数提示显示 `(必备)` | +| Ctrl + Q | 光标所在的变量 / 类名 / 方法名等上面(也可以在提示补充的时候按),显示文档内容 | +| Ctrl + U | 前往当前光标所在的方法的父类的方法 / 接口定义 `(必备)` | +| Ctrl + B | 进入光标所在的方法/变量的接口或是定义处,等效于 `Ctrl + 左键单击` `(必备)` | +| Ctrl + K | 版本控制提交项目,需要此项目有加入到版本控制才可用 | +| Ctrl + T | 版本控制更新项目,需要此项目有加入到版本控制才可用 | +| Ctrl + H | 显示当前类的层次结构 | +| Ctrl + O | 选择可重写的方法 | +| Ctrl + I | 选择可继承的方法 | +| Ctrl + + | 展开代码 | +| Ctrl + - | 折叠代码 | +| Ctrl + / | 注释光标所在行代码,会根据当前不同文件类型使用不同的注释符号 `(必备)` | +| Ctrl + [ | 移动光标到当前所在代码的花括号开始位置 | +| Ctrl + ] | 移动光标到当前所在代码的花括号结束位置 | +| Ctrl + F1 | 在光标所在的错误代码处显示错误信息 `(必备)` | +| Ctrl + F3 | 调转到所选中的词的下一个引用位置 `(必备)` | +| Ctrl + F4 | 关闭当前编辑文件 | +| Ctrl + F8 | 在 Debug 模式下,设置光标当前行为断点,如果当前已经是断点则去掉断点 | +| Ctrl + F9 | 执行 Make Project 操作 | +| Ctrl + F11 | 选中文件 / 文件夹,使用助记符设定 / 取消书签 `(必备)` | +| Ctrl + F12 | 弹出当前文件结构层,可以在弹出的层上直接输入,进行筛选 | +| Ctrl + Tab | 编辑窗口切换,如果在切换的过程又加按上delete,则是关闭对应选中的窗口 | +| Ctrl + End | 跳到文件尾 | +| Ctrl + Home | 跳到文件头 | +| Ctrl + Space | 基础代码补全,默认在 Windows 系统上被输入法占用,需要进行修改,建议修改为 `Ctrl + 逗号` `(必备)` | +| Ctrl + Delete | 删除光标后面的单词或是中文句 `(必备)` | +| Ctrl + BackSpace | 删除光标前面的单词或是中文句 `(必备)` | +| Ctrl + 1,2,3...9 | 定位到对应数值的书签位置 `(必备)` | +| Ctrl + 左键单击 | 在打开的文件标题上,弹出该文件路径 `(必备)` | +| Ctrl + 光标定位 | 按 Ctrl 不要松开,会显示光标所在的类信息摘要 | +| Ctrl + 左方向键 | 光标跳转到当前单词 / 中文句的左侧开头位置 `(必备)` | +| Ctrl + 右方向键 | 光标跳转到当前单词 / 中文句的右侧开头位置 `(必备)` | +| Ctrl + 前方向键 | 等效于鼠标滚轮向前效果 `(必备)` | +| Ctrl + 后方向键 | 等效于鼠标滚轮向后效果 `(必备)` | + +### Alt + +| 快捷键 | 介绍 | +| :-------------- | :----------------------------------------------------------- | +| Alt + ` | 显示版本控制常用操作菜单弹出层 `(必备)` | +| Alt + Q | 弹出一个提示,显示当前类的声明 / 上下文信息 | +| Alt + F1 | 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择 `(必备)` | +| Alt + F2 | 对于前面页面,显示各类浏览器打开目标选择弹出层 | +| Alt + F3 | 选中文本,逐个往下查找相同文本,并高亮显示 | +| Alt + F7 | 查找光标所在的方法 / 变量 / 类被调用的地方 | +| Alt + F8 | 在 Debug 的状态下,选中对象,弹出可输入计算表达式调试框,查看该输入内容的调试结果 | +| Alt + Home | 定位 / 显示到当前文件的 `Navigation Bar` | +| Alt + Enter | IntelliJ IDEA 根据光标所在问题,提供快速修复选择,光标放在的位置不同提示的结果也不同 `(必备)` | +| Alt + Insert | 代码自动生成,如生成对象的 set / get 方法,构造函数,toString() 等 `(必备)` | +| Alt + 左方向键 | 切换当前已打开的窗口中的子视图,比如Debug窗口中有Output、Debugger等子视图,用此快捷键就可以在子视图中切换 `(必备)` | +| Alt + 右方向键 | 按切换当前已打开的窗口中的子视图,比如Debug窗口中有Output、Debugger等子视图,用此快捷键就可以在子视图中切换 `(必备)` | +| Alt + 前方向键 | 当前光标跳转到当前文件的前一个方法名位置 `(必备)` | +| Alt + 后方向键 | 当前光标跳转到当前文件的后一个方法名位置 `(必备)` | +| Alt + 1,2,3...9 | 显示对应数值的选项卡,其中 1 是 Project 用得最多 `(必备)` | + +### Shift + +| 快捷键 | 介绍 | +| :------------------- | :----------------------------------------------------------- | +| Shift + F1 | 如果有外部文档可以连接外部文档 | +| Shift + F2 | 跳转到上一个高亮错误 或 警告位置 | +| Shift + F3 | 在查找模式下,查找匹配上一个 | +| Shift + F4 | 对当前打开的文件,使用新Windows窗口打开,旧窗口保留 | +| Shift + F6 | 对文件 / 文件夹 重命名 | +| Shift + F7 | 在 Debug 模式下,智能步入。断点所在行上有多个方法调用,会弹出进入哪个方法 | +| Shift + F8 | 在 Debug 模式下,跳出,表现出来的效果跟 `F9` 一样 | +| Shift + F9 | 等效于点击工具栏的 `Debug` 按钮 | +| Shift + F10 | 等效于点击工具栏的 `Run` 按钮 | +| Shift + F11 | 弹出书签显示层 `(必备)` | +| Shift + Tab | 取消缩进 `(必备)` | +| Shift + ESC | 隐藏当前 或 最后一个激活的工具窗口 | +| Shift + End | 选中光标到当前行尾位置 | +| Shift + Home | 选中光标到当前行头位置 | +| Shift + Enter | 开始新一行。光标所在行下空出一行,光标定位到新行位置 `(必备)` | +| Shift + 左键单击 | 在打开的文件名上按此快捷键,可以关闭当前打开文件 `(必备)` | +| Shift + 滚轮前后滚动 | 当前文件的横向滚动轴滚动 `(必备)` | + +### Ctrl + Alt + +| 快捷键 | 介绍 | +| :-------------------- | :----------------------------------------------------------- | +| Ctrl + Alt + L | 格式化代码,可以对当前文件和整个包目录使用 `(必备)` | +| Ctrl + Alt + O | 优化导入的类,可以对当前文件和整个包目录使用 `(必备)` | +| Ctrl + Alt + I | 光标所在行 或 选中部分进行自动代码缩进,有点类似格式化 | +| Ctrl + Alt + T | 对选中的代码弹出环绕选项弹出层 `(必备)` | +| Ctrl + Alt + J | 弹出模板选择窗口,将选定的代码加入动态模板中 | +| Ctrl + Alt + H | 调用层次 | +| Ctrl + Alt + B | 在某个调用的方法名上使用会跳到具体的实现处,可以跳过接口 | +| Ctrl + Alt + V | 快速引进变量 | +| Ctrl + Alt + Y | 同步、刷新 | +| Ctrl + Alt + S | 打开 IntelliJ IDEA 系统设置 `(必备)` | +| Ctrl + Alt + F7 | 显示使用的地方。寻找被该类或是变量被调用的地方,用弹出框的方式找出来 | +| Ctrl + Alt + F11 | 切换全屏模式 | +| Ctrl + Alt + Enter | 光标所在行上空出一行,光标定位到新行 `(必备)` | +| Ctrl + Alt + Home | 弹出跟当前文件有关联的文件弹出层 | +| Ctrl + Alt + Space | 类名自动完成 | +| Ctrl + Alt + 左方向键 | 退回到上一个操作的地方 `(必备)` | +| Ctrl + Alt + 右方向键 | 前进到上一个操作的地方 `(必备)` | +| Ctrl + Alt + 前方向键 | 在查找模式下,跳到上个查找的文件 | +| Ctrl + Alt + 后方向键 | 在查找模式下,跳到下个查找的文件 | + +### Ctrl + Shift + +| 快捷键 | 介绍 | +| :----------------------- | :----------------------------------------------------------- | +| Ctrl + Shift + F | 根据输入内容查找整个项目 或 指定目录内文件 `(必备)` | +| Ctrl + Shift + R | 根据输入内容替换对应内容,范围为整个项目 或 指定目录内文件 `(必备)` | +| Ctrl + Shift + J | 自动将下一行合并到当前行末尾 `(必备)` | +| Ctrl + Shift + Z | 取消撤销 `(必备)` | +| Ctrl + Shift + W | 递进式取消选择代码块。可选中光标所在的单词或段落,连续按会在原有选中的基础上再扩展取消选中范围 `(必备)` | +| Ctrl + Shift + N | 通过文件名定位 / 打开文件 / 目录,打开目录需要在输入的内容后面多加一个正斜杠 `(必备)` | +| Ctrl + Shift + U | 对选中的代码进行大 / 小写轮流转换 `(必备)` | +| Ctrl + Shift + T | 对当前类生成单元测试类,如果已经存在的单元测试类则可以进行选择 `(必备)` | +| Ctrl + Shift + C | 复制当前文件磁盘路径到剪贴板 `(必备)` | +| Ctrl + Shift + V | 弹出缓存的最近拷贝的内容管理器弹出层 | +| Ctrl + Shift + E | 显示最近修改的文件列表的弹出层 | +| Ctrl + Shift + H | 显示方法层次结构 | +| Ctrl + Shift + B | 跳转到类型声明处 `(必备)` | +| Ctrl + Shift + I | 快速查看光标所在的方法 或 类的定义 | +| Ctrl + Shift + A | 查找动作 / 设置 | +| Ctrl + Shift + / | 代码块注释 `(必备)` | +| Ctrl + Shift + [ | 选中从光标所在位置到它的顶部中括号位置 `(必备)` | +| Ctrl + Shift + ] | 选中从光标所在位置到它的底部中括号位置 `(必备)` | +| Ctrl + Shift + + | 展开所有代码 `(必备)` | +| Ctrl + Shift + - | 折叠所有代码 `(必备)` | +| Ctrl + Shift + F7 | 高亮显示所有该选中文本,按Esc高亮消失 `(必备)` | +| Ctrl + Shift + F8 | 在 Debug 模式下,指定断点进入条件 | +| Ctrl + Shift + F9 | 编译选中的文件 / 包 / Module | +| Ctrl + Shift + F12 | 编辑器最大化 `(必备)` | +| Ctrl + Shift + Space | 智能代码提示 | +| Ctrl + Shift + Enter | 自动结束代码,行末自动添加分号 `(必备)` | +| Ctrl + Shift + Backspace | 退回到上次修改的地方 `(必备)` | +| Ctrl + Shift + 1,2,3...9 | 快速添加指定数值的书签 `(必备)` | +| Ctrl + Shift + 左键单击 | 把光标放在某个类变量上,按此快捷键可以直接定位到该类中 `(必备)` | +| Ctrl + Shift + 左方向键 | 在代码文件上,光标跳转到当前单词 / 中文句的左侧开头位置,同时选中该单词 / 中文句 `(必备)` | +| Ctrl + Shift + 右方向键 | 在代码文件上,光标跳转到当前单词 / 中文句的右侧开头位置,同时选中该单词 / 中文句 `(必备)` | +| Ctrl + Shift + 前方向键 | 光标放在方法名上,将方法移动到上一个方法前面,调整方法排序 `(必备)` | +| Ctrl + Shift + 后方向键 | 光标放在方法名上,将方法移动到下一个方法前面,调整方法排序 `(必备)` | + +### Alt + Shift + +| 快捷键 | 介绍 | +| :--------------------- | :----------------------------------------------------------- | +| Alt + Shift + N | 选择 / 添加 task `(必备)` | +| Alt + Shift + F | 显示添加到收藏夹弹出层 / 添加到收藏夹 | +| Alt + Shift + C | 查看最近操作项目的变化情况列表 | +| Alt + Shift + I | 查看项目当前文件 | +| Alt + Shift + F7 | 在 Debug 模式下,下一步,进入当前方法体内,如果方法体还有方法,则会进入该内嵌的方法中,依此循环进入 | +| Alt + Shift + F9 | 弹出 `Debug` 的可选择菜单 | +| Alt + Shift + F10 | 弹出 `Run` 的可选择菜单 | +| Alt + Shift + 左键双击 | 选择被双击的单词 / 中文句,按住不放,可以同时选择其他单词 / 中文句 `(必备)` | +| Alt + Shift + 前方向键 | 移动光标所在行向上移动 `(必备)` | +| Alt + Shift + 后方向键 | 移动光标所在行向下移动 `(必备)` | + +### Ctrl + Shift + Alt + +| 快捷键 | 介绍 | +| :--------------------- | :-------------------------- | +| Ctrl + Shift + Alt + V | 无格式黏贴 `(必备)` | +| Ctrl + Shift + Alt + N | 前往指定的变量 / 方法 | +| Ctrl + Shift + Alt + S | 打开当前项目设置 `(必备)` | +| Ctrl + Shift + Alt + C | 复制参考信息 | + +### 其他 + +| 快捷键 | 介绍 | +| :------------ | :----------------------------------------------------------- | +| F2 | 跳转到下一个高亮错误 或 警告位置 `(必备)` | +| F3 | 在查找模式下,定位到下一个匹配处 | +| F4 | 编辑源 `(必备)` | +| F7 | 在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则进入当前方法体内,如果该方法体还有方法,则不会进入该内嵌的方法中 | +| F8 | 在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则不进入当前方法体内 | +| F9 | 在 Debug 模式下,恢复程序运行,但是如果该断点下面代码还有断点则停在下一个断点上 | +| F11 | 添加书签 `(必备)` | +| F12 | 回到前一个工具窗口 `(必备)` | +| Tab | 缩进 `(必备)` | +| ESC | 从工具窗口进入代码文件窗口 `(必备)` | +| 连按两次Shift | 弹出 `Search Everywhere` 弹出层 | + +官网:[https://blog.jetbrains.com/zh-hans/idea/2022/11/intellij-idea-3/](https://blog.jetbrains.com/zh-hans/idea/2022/11/intellij-idea-3/) +其他:[https://yuzhigang.gitbooks.io/intellij-idea-tutorial/content/introduce.html](https://yuzhigang.gitbooks.io/intellij-idea-tutorial/content/introduce.html) + +## 四、更多官方学习信息 + +
+ +
+ +- IntelliJ IDEA 主要特性介绍 1:[https://www.jetbrains.com/idea/features/](https://www.jetbrains.com/idea/features/) +- IntelliJ IDEA 主要特性介绍 2:[https://www.jetbrains.com/idea/features/editions_comparison_matrix.html](https://www.jetbrains.com/idea/features/editions_comparison_matrix.html) +- 官方快速入门:[http://confluence.jetbrains.com/display/IntelliJIDEA/Quick+Start](http://confluence.jetbrains.com/display/IntelliJIDEA/Quick+Start) +- 官方在线帮助文档:[http://www.jetbrains.com/idea/webhelp/getting-help.html](http://www.jetbrains.com/idea/webhelp/getting-help.html) +- 官方 wiki:[http://wiki.jetbrains.net/intellij](http://wiki.jetbrains.net/intellij) diff --git a/docs/md/road-map/introduce.md b/docs/md/road-map/introduce.md new file mode 100644 index 000000000..dde24d375 --- /dev/null +++ b/docs/md/road-map/introduce.md @@ -0,0 +1,167 @@ +--- +title: 小册介绍 +lock: need +--- + +# 小傅哥 DDD 编程开发小册,帮助大家更好的落地实践 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +如果在面试的时候,面试官问你DDD是什么,你怎么解释?是不是感觉DDD的资料也看了不少,但好像还没有一个定义给DDD,所以炸一听这有的问题的时候,还真有点慌。 + +
+ +
+ +那DDD是什么呢?🤔 + +**关于DDD是什么,在维基百科有一个明确的定义。"Domain-driven design (DDD) is a major software design approach." 也就是说DDD是一种主要的软件设计方法。而软件设计涵盖了;范式、模型、框架、方法论。** + +- 范式(paradigm)指的是一种编程思想。 +- 模型(model)指的是对现实世界或者问题的抽象描述。 +- 框架(framework)指的是提供了一系列通用功能和结构的软件工具。 +- 方法论(methodology)指的是一种系统的、有组织的解决问题的方法。 + +所以,DDD不只是只有指导思想,伴随的DDD的还包括框架结构分层。但说到底,这些仍然是理论讨论。在没有一个DDD落地项目物参考下,其实大部分码农是没法完成DDD开发的。所以小傅哥今年花费了5个月假期/周末的时间,完成的《DDD简明开发教程》,帮助大家落地DDD编码。 + +>文末可以获取到基于DDD架构开发的实战项目。 + +## 一、能学到啥 + +因为DDD本身有很多的方法论,指导着框架结构的设计,从而控制着工程模块间的平衡。这种平衡的约束,起到了很好的防腐作用,天然的控制了职责的分离。不过也因为有了这些约束,很多初次上手DDD的研发伙伴不知道怎么下手了。所以以下这些内容,则是帮你进入DDD开发; + +1. 为你,提供整个DDD简单版、DDD标准版,脚手架使用,并配合提供 docker compose 一键开发环境安装。这样你再使用的时候就可以更加容易进入了。 +2. 为你,深度讲解DDD分层结构、DDD全模块调用链路、MVC2DDD的升级关闭和对比、DDD领域模型(账户/支付)的设计和编码。 +3. 为你,拆分独立案例,讲解应用启动、配置加载、各类技术框架和分布式技术栈,在DDD分层结构下具体到每个模块中的使用手段。如;MyBatis、Dubbo、RocketMQ、Redis、Zookeeper、Sharding-JDBC、XXL-Job等。—— 这些案例都来自于业务场景中的常用使用方式,非常具有代表性。 +4. 为你,讲解工程开发中常用类库和工程的测试压测处理方案。如;Guava、OKHTTP、Mock、JMeter、AB、Siege等。这些东西是为了更好的完成代码交付。 +5. 为你,工程开发最后的阶段,提供;Docker、Portainer 云服务操作,以及包括对工程的全链路监控学习。此外基础的 IntelliJ IDEA、Maven、Git、Github/Gitcode/Gitee,的使用教程。 + +以上,这些内容的学习,可以让你既掌握DDD架构的开发使用,又能快速的学会各项基础技术栈和分布式技术栈的运用,还能学习到监控、压测、云服务操作。可以说是一举多得!嘎嘎强! + +## 二、小册演示 + +可以这么说,这套DDD的小册课程是非常基础入门,又覆盖得极其全面。哪怕是小白研发,也能完全跟着走下来。既有文章又有视频,每一个小知识点都是一个独立的案例讲解。以下就是DDD小册内的一些截图,可以参考看下。 + +### 1. 工程结构模型 + +
+ +
+ +### 2. 架构对比参照 + +
+ +
+ +### 3. 模块调用链路 + +
+ +
+ +### 4. 领域模型设计 + +
+ +
+ +### 5. 系统压测讲解 + +
+ +
+ +### 6. 应用监控部署 + +
+ +
+ +>对此DDD小册感兴趣的伙伴,也可以进入小傅哥的B站进行视频学习。 + +## 三、小册大纲 + +小傅哥在初学阶段也看过网上的案例,可以说是鱼龙混杂,如果刚好自己是初学还不太懂,那么几乎要花费整个1天的时间,一遍遍百度搜索出各种有毛病的案例。才能完成自己的学习。所以基于这样的经历,小傅哥提供了完整的、前面的、成体系化的一整套学习案例。 + +- 工程脚手架(1) + - DDD 脚手架 + +- 系统架构(5) + - [MVC 架构](#) + - [DDD 架构](#) + - [MVC2DDD - 架构重构](#) + - [DDD 架构 - 账户域](#) + - [DDD 架构 - 交易域](#) + +- 开发环境(6) + - [IntelliJ IDEA](#) + - [Maven](#) + - [Git](#) + - [Github](#) + - [Gitcode](#) + - [Gitee](#) + +- 开发技术(11) + - [MyBatis](#) + - [Dubbo](#) + - [RocketMQ](#) + - [Quartz & XXL-Job](#) + - [MySQL](#) + - [db-router](#) + - [sharding-jdbc](#) + - [ConnectionPool](#) + - [Zookeeper](#) + - [Redis](#) + - [Ignite](#) + +- 常用类库(3) + - [fastjson](#) + - [guava](#) + - [http](#) + +- 工程测试(2) + - [Mock](#) + - [JMeter](#) + +- 质量监控(1) + - [skywalking 全链路监控](#) + +- 发布部署(2) + - [Docker](#) + - [Portainer](#) + +--- + +全小册目前发布了31节内容,分为脚手架、架构、环境、技术、类库、测试、监控、部署,8部分内容。这样整个一条龙🐲的学习,可以让大家对于技术的积累更为扎实。以后还会结合实际场景所需,扩展小册内容。如果有小伙伴需要用到的内容,不在这里,也可以提出建议或者贡献。💐 + +## 四、编程经验 + +1. 并不一定非得需要所有人一起做领域设计。一个研发有时候就够。`因为铁打的研发,流水的产品`。最熟悉系统和业务的,往往是最核心的研发。尤其是大公司变动较多的情况下,每每产品更替,都需要与研发请教整体流程。 +2. DDD难落地,不是DDD有多难,是没经验的人做DDD,不清楚范式、模型、框架、方法论,只能抱着理论给团队伙伴讲,并且在很多细节使用上没法把控,导致越来越难维护。`上DDD==上一坨屎`。 +3. 不是非得多复杂的系统才能用DDD,简单系统使用DDD,也会更好的维护。因为很多没彻底做过DDD的开发的,甚至并不知道domain的核心是什么,眼里只有聚合、聚合、聚合。 +4. 能在MVC结构下写代码不错的人,到DDD可以写的更好。DDD只是软件设计方法的改变,但不决定原本代码垃圾的人,换个结构代码就牛逼了。 +5. 不具备DDD结构设计和调整的研发,会生搬硬套DDD。所以没法根据自身业务需求调整分层的结构,如在domain领域编排复用极低的场景,可以去掉application/case层,这样会减少大量的对象转换。 +6. 千万别说设计模式没用,好好看看一些高质量的框架源码,对于;分治、抽象、知识(设计原则、设计模式)的运行,是有多好。没用设计模式的DDD,就像没有家具的四居室,卧室里可以安装马桶。厨房里可以放个床。 + +## 五、加入学习 + +注意📢,DDD开发小册为小傅哥提供的免费学习资料,提供了基础内容的讲解。此外加入小傅哥【星球:码农会锁】还有DDD应用级实战项目课程,通过这些课程的实操,对实际业务场景进行架构、设计、编码、上线,更加扎实的掌握DDD领域驱动设计的运用。项目体验地址:[https://gaga.plus](https://gaga.plus) - 小傅哥为星球项目部署的演示平台。 + +- 【免费】- DDD开发小册;[https://bugstack.cn/md/road-map/road-map.html](https://bugstack.cn/md/road-map/road-map.html) +- 【付费】- DDD实战项目;[https://t.zsxq.com/14NGs0bGU](https://t.zsxq.com/14NGs0bGU) - `OpenAi 大模型应用`、`Lottery 分布式抽奖`、`Api网关`、`IM通信`等 + +>这样一套全体系的实战项目,放在一些平台售卖,至少都是几百块。但小傅哥的星球,只需要100来块钱,就可以获得几千元的学习项目! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +小傅哥的【星球:码农会锁】,是一条龙🐲成体系的学习,因为这些内容都是小傅哥原创编写,所以学习起来的衔接性会非常强,有紧有收、循序渐进!欢迎加入,可别错过! + +
+ +
diff --git a/docs/md/road-map/jenkins.md b/docs/md/road-map/jenkins.md new file mode 100644 index 000000000..79e943532 --- /dev/null +++ b/docs/md/road-map/jenkins.md @@ -0,0 +1,281 @@ +--- +title: Jenkins 发布部署 +lock: need +--- + +# 在 Docker 中部署 Jenkins,并完成项目的构建和发布 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,如何在 Docker 中部署 Jenkins,并通过 Jenkins 完成对项目的打包构建并在 Docker 容器中部署。 + +Jenkins 的主要作用是帮助你,把需要在本地机器完成的 Maven 构建、Docker 镜像发布、云服务器部署等系列动作全部集成在一个服务下。简化你的构建部署操作过程,因为 Jenkins 也被称为 CI&CD(持续集成&持续部署) 工具。提供超过 1000 个插件(Maven、Git、NodeJs)来支持构建、部署、自动化, 满足任何项目的需要。 + +官网: +- 英文:[https://www.jenkins.io/](https://www.jenkins.io/) +- 中文:[https://www.jenkins.io/zh/](https://www.jenkins.io/zh/) + +本文涉及的工程: +- xfg-dev-tech-jenkins:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-jenkins](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-jenkins) +- 提示: + - 推荐使用云服务器做本节的案例 [5.5元/1个月,50元/1年](https://gaga.plus/yun.html) + - 本节会需要用到的环境 [Docker&Portainer](https://bugstack.cn/md/road-map/docker.html) + +## 一、操作说明 + +本节小傅哥会带着大家完成 Jenkins 环境的安装,以及以最简单的方式配置使用 Jenkins 完成对 [xfg-dev-tech-jenkins](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-jenkins) 案例项目的部署。部署后可以访问 [xfg-dev-tech-jenkins](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-jenkins) 项目提供的接口进行功能验证。整个部署操作流程如下; + +
+ +
+ +- 左侧竖列为核心配置部署流程,右侧是需要在配置过程中处理的细节。 +- 通过把本地对项目打包部署的过程拆解为一个个模块,配置到 Jenkins 环境中。这就是 Jenkins 的作用。 + +## 二、环境配置 + +1. 确保你已经在(云)服务器上配置了 [Docker](https://bugstack.cn/md/road-map/docker.html) 环境,以及安装了 docker-compose。同时最好已经安装了 [Portainer](https://bugstack.cn/md/road-map/portainer.html) 管理界面这样更加方便操作。 +2. 在配置和后续的验证过程中,会需要访问(云)服务的地址加端口。如果你在云服务配置的,记得开放端口;`9000 - portainer`、`9090 - jenkins`、`8091 - xfg-dev-tech-app 服务` + +### 1. Jenkins 部署 + +#### 1.1 上传文件 + +
+ +
+ +- 如图;以上配置内容已经放到 [xfg-dev-tech-jenkins](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-jenkins) 工程中,如果你是云服务器部署则需要将 dev-ops 部分全部上传到服务器的根目录下。 +- compose-down.sh 是 [docker-compose](https://bugstack.cn/md/road-map/docker.html#_7-%E5%AE%89%E8%A3%85docker-compose) 下载文件,只有你安装了 docker-compose 才能执行 `docker-compose -f docker-compose-v1.0.yml up -d` +- jdk-down.sh 是 jdk1.8 下载路径,以及解压脚本。如果你在云服务器下载较慢,也可以本地搜索 jdk1.8 下载,并上传到云服务器上解压。注意:本步骤是可选的,如果你的项目不强依赖于 jdk1.8 也可以使用 Jenkins 默认自带的 JDK17。可以通过在安装后的 Jenkins 控制台执行 `which java` 找到 JDK 路径。 +- maven 下的 settings.xml 配置,默认配置了阿里云镜像文件,方便在 Jenkins 构建项目时,可以快速地拉取下载下来包。 + +#### 1.2 脚本说明 + +```shell +version: '3.8' +# 执行脚本;docker-compose -f docker-compose-v1.0.yml up -d +services: + jenkins: + image: jenkins/jenkins:2.439 + container_name: jenkins + privileged: true + user: root + ports: + - "9090:8080" + - "50001:50000" + volumes: + - ./jenkins_home:/var/jenkins_home # 如果不配置到云服务器路径下,则可以配置 jenkins_home 会创建一个数据卷使用 + - /var/run/docker.sock:/var/run/docker.sock + - /usr/bin/docker:/usr/local/bin/docker + - ./maven/conf/settings.xml:/usr/local/maven/conf/settings.xml # 这里只提供了 maven 的 settings.xml 主要用于修改 maven 的镜像地址 + - ./jdk/jdk1.8.0_202:/usr/local/jdk1.8.0_202 # 提供了 jdk1.8,如果你需要其他版本也可以配置使用。 + environment: + - JAVA_OPTS=-Djenkins.install.runSetupWizard=false # 禁止安装向导「如果需要密码则不要配置」docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword + restart: unless-stopped + +volumes: + jenkins_home: +``` + +Jenkins Docker 执行安装脚本。 +- `./jenkins_home:/var/jenkins_home` 是在云服务器端挂一个映射路径,方便可以重新安装后 Jenkins 依然存在。你也可以配置为 `jenkins_home:/var/jenkins_home` 这样是自动挂在 `volumes jenkins_home` 数据卷下。 +- `docker` 两个 docker 的配置是为了可以在 Jenkins 中使用 Docker 命令,这样才能在 Docker 安装的 Jenkins 容器内,使用 Docker 服务。 +- `./maven/conf/settings.xml:/usr/local/maven/conf/settings.xml` 为了在 Jenkins 中使用映射的 Maven 配置。 +- `./jdk/jdk1.8.0_202:/usr/local/jdk1.8.0_202` 用于在 Jenkins 中使用 jdk1.8 +- `JAVA_OPTS=-Djenkins.install.runSetupWizard=false` 这个是一个禁止安装向导,配置为 false 后,则 Jenkins 不会让你设置密码,也不会一开始就安装一堆插件。如果你需要安装向导可以注释掉这个配置。并且当提示你获取密码时,你可以执行;`docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword` 获取到登录密码。 + +#### 1.3 执行安装 + +
+ +
+ +```shell +[root@lavm-aqhgp9nber dev-ops]# docker-compose -f docker-compose-v1.0.yml up -d +[+] Building 0.0s (0/0) +[+] Running 1/0 + ✔ Container jenkins Running +``` + +执行脚本 `docker-compose -f docker-compose-v1.0.yml up -d` 后,这样执行完毕后,则表明已经安装成功了💐。 + +### 2. 插件安装 + +地址:[http://localhost:9090/](http://localhost:9090/) - `登录Jenkins` + +
+ +
+ +
+ +
+ +- 1~2步,设置镜像源,设置后重启一下 Jenkins。 + 镜像源地址:https://mirrors.huaweicloud.com/jenkins/updates/update-center.json +- 3~4步,下载插件,先下载安装 chinese 汉化插件,方便不太熟悉 Jenkins 的伙伴更好的知道页面都是啥内容。 +- 5步,所有的插件安装完成后,都需要重启才会生效。`安装完 chinese 插件,重启在进入到 Jenkins 就是汉化的页面了` +- 除了以上步骤,你还需要同样的方式安装 maven、git、docker 插件。 +- 注意,因为网络问题你可以再做过程中,提示失败。没关系,你可以再搜这个插件,再重新下载。它会把失败的继续下载。 + +### 3. 全局工具配置 + +地址:[http://localhost:9090/manage/configureTools/](http://localhost:9090/manage/configureTools/) + +
+ +
+ +用于构建部署的 SpringBoot 应用的环境,都需要在全局工具中配置好。包括;Maven、JDK、Git、Docker。注意这里的环境路径配置,如果配置了是会提示你没有对应的路径文件夹。 + +### 4. 添加凭证 + +地址:[http://localhost:9090/manage/credentials/store/system/domain/_/](http://localhost:9090/manage/credentials/store/system/domain/_/) + +
+ +
+ +- 配置了Git仓库的连接凭证,才能从Git仓库拉取代码。 +- 如果你还需要操作如 ssh 也需要配置凭证。 + +## 三、新建任务 + +一个任务就是一条构建发布部署项目的操作。 + +### 1. 配置任务 + +
+ +
+ +``` +xfg-dev-tech-jenkins +``` + +### 2. 配置Git + +
+ +
+ +```java +# 你可以 fork 这个项目,到自己的仓库进行使用 +https://gitcode.net/KnowledgePlanet/ddd-scene-solution/xfg-dev-tech-content-moderation.git +``` + +### 3. 配置Maven + +
+ +
+ +- 在高级中设置 Maven 配置的路径 `/usr/local/maven/conf/settings.xml`。这样才能走自己配置的阿里云镜像仓库。 + +```shell +clean install -Dmaven.test.skip=true +``` + +### 3. 配置Shell + +```shell +# 先删除之前的容器和镜像文件 +if [ "$(docker ps -a | grep xfg-dev-tech-app)" ]; then +docker stop xfg-dev-tech-app +docker rm xfg-dev-tech-app +fi +if [ "$(docker images -q xfg-dev-tech-app)" ]; then +docker rmi xfg-dev-tech-app +fi + +# 重新生成 +cd /var/jenkins_home/workspace/xfg-dev-tech-jenkins/xfg-dev-tech-app +docker build -t xiaofuge/xfg-dev-tech-app . +docker run -itd -p 8091:8091 --name xfg-dev-tech-app xiaofuge/xfg-dev-tech-app +``` + +
+ +
+ +- 当你熟悉后还可以活学活用,比如这里只是做build 但不做run执行操作。具体的部署可以通过 docker compose 执行部署脚本。 +- 另外如果你有发布镜像的诉求,也可以在这里操作。 + +## 四、测试验证 + +### 1. 工程准备 + +**工程**:`https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-jenkins` 你可以fork到自己的仓库进行使用,你的账号密码就是 CSDN 的账号密码。 + +```java +@SpringBootApplication +@RestController() +@RequestMapping("/api/") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class); + } + + /** + * http://localhost:8091/api/test + */ + @RequestMapping(value = "/test", method = RequestMethod.GET) + public ResponseBodyEmitter test(HttpServletResponse response) { + response.setContentType("text/event-stream"); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Cache-Control", "no-cache"); + + ResponseBodyEmitter emitter = new ResponseBodyEmitter(); + + String[] words = new String[]{"嗨,臭宝。\r\n", "恭喜💐 ", "你的", " Jenkins ", " 部", "署", "测", "试", "成", "功", "了啦🌶!"}; + new Thread(() -> { + for (String word : words) { + try { + emitter.send(word); + Thread.sleep(250); + } catch (IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + }).start(); + + return emitter; + } + +} +``` + +- 工程中提供了接口;`http://localhost:8091/api/test` + +### 2. CI&CD - 构建发布 + +**地址**:[http://localhost:9090/job/xfg-dev-tech-jenkins/](http://localhost:9090/job/xfg-dev-tech-jenkins/) + +
+ +
+ +- 点击构建项目,最终会完成构建和部署成功。运行到这代表你全部操作完成了。 + +### 3. 验证结果 + +**地址**:[http://localhost:9000/#!/2/docker/containers](http://localhost:9000/#!/2/docker/containers) + +
+ +
+ +**访问**:[http://localhost:8091/api/test](http://localhost:8091/api/test) + +
+ +
+ +- 运行到这代表着你已经完整的走完了 Jenkins CI&CD 流程。 diff --git a/docs/md/road-map/jmeter.md b/docs/md/road-map/jmeter.md new file mode 100644 index 000000000..a7a0c4c2e --- /dev/null +++ b/docs/md/road-map/jmeter.md @@ -0,0 +1,191 @@ +--- +title: JMeter +lock: need +--- + +# JMeter 压测工具的配置和使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +本文的宗旨在于通过简单干净实践的方式教会读者,如何使用[JMeter](https://jmeter.apache.org)进行工程的压测测试。也同时会介绍到;ApacheBench、Siege 两个更简单压测工具的使用。 + +压测是开发完成正式上线对外使用前非常重要的一环,尤其是各个互联网大厂,上线的核心应用和在618、双11以及各种大促时间节点下的应用,都需要进行压测摸底,知道一个系统的最大承载量,并基于这样的一个量的安全范围值内设置熔断、限流和降级的指标。—— 而且这也是面试过程中评估你是否真的做过上线系统的能力考察项;`你的系统负载量多大、部署多少台服务器、响应时间怎么样、峰值是多少` + +本文涉及的工程: + +- xfg-dev-tech-jmeter:[xfg-dev-tech-jmeter](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-jmeter) + +## 一、工具安装 + +这个安装分为本地安装和云服务安装,一般正规的玩法是本地安装后来写测试计划看效果。之后把jmx文件放到云服务器来执行压测。这样才能不受GUI和本地的限制,压测的比较大。**本文提供了Docker部署和执行 JMX 压测脚本** + +### 1. 本地安装 + +- 环境:JDK 1.8+ - `因为这是一个纯Java开发的软件,所以需要JDK环境` +- 官网:[https://jmeter.apache.org/download_jmeter.cgi](https://jmeter.apache.org/download_jmeter.cgi) - `Mac 下载 apache-jmeter-5.6.2.tgz`、`Windows 下载 apache-jmeter-5.6.2.zip` 注意官网还有对应的文档,很清晰的介绍了使用方法。 +- 镜像:[https://mirrors.aliyun.com/apache/jmeter/binaries/](https://mirrors.aliyun.com/apache/jmeter/binaries/) - `如果官网下载比较慢,也可以通过阿里云镜像下载` + +### 2. 云服务安装 + +**脚本**:`xfg-dev-tech-jmeter/docs/dev-ops/docker-compose.yml` + +```java +version: '3' +services: + # JMeter是一个功能强大的性能测试工具,可以模拟多种类型的负载,并提供详细的测试报告 + # 官网:https://jmeter.apache.org/ + # 脚本:jmeter -n -t one.jmx -l one.jtl + Jmeter: + image: justb4/jmeter:5.5 + container_name: jmeter + restart: always + environment: + - DISPLAY=:0 + - TZ=Europe/Paris + volumes: + - https://bugstack.cn/images/roadmap/tutorial/jmx/:/opt/apache-jmeter-5.5/jmx/ +``` + +- 如果你本地已经安装 Docker 那么直接执行 docker-compose.yml 即可完成安装。 +- 注意,`xfg-dev-tech-jmeter/docs/dev-ops/jmx` 下是 JMeter 所保存的压测脚本。 + +### 1. 解压启动 + +
+ +
+ +进入到 apache-jmeter-5.5/bin 目录下; + +- Mac 电脑,在 jmeter 上右键,选择终端启动。 +- Windows 电脑,直接点 jmeter.bat 启动。 + +### 2. 配置语言 + +
+ +
+ +修改语言有2个方式; + +- 进入 apache-jmeter-5.5/bin/jmeter.properties 设置 language=zh_CN +- 如图,进入页面,手动选择。 + +## 二、配置说明 + +
+ +
+ +这是 JMeter 压测配置中非常简单的一个**测试计划**,在这个测试计划下需要包括`线程组 - 负责运行`、`取样器(压测的接口) - 负责调接口`和`至少一个监听器 - 负责看结果`。这样才能完成压测并获得结果。 + +### 1. 线程组 + +
+ +
+ +通过线程组开启对HTTP接口的请求循环操作方式。它可以模拟配置出流量的负载均值请求、峰值请求、逐步加量等场景。 + +#### 1.1 函数线程组 + +
+ +
+ +- Open Mode Thread Group 支持配置简单的配置和 Groovy 脚本 如;`${__groovy((1..5).collect { "rate(" + it*10 + "/sec) random_arrivals(10 sec) pause(1 sec)" }.join(" "))}` - 请求10次,每次都递进。 +- 你还可以配置这些参数;`rate(0/min) random_arrivals(10 min) rate(100/min)`、`rate(0/min) random_arrivals(5 min) rate(100/min)random_arrivals(100 min)rate(100/min) random_arrivals(5 min) rate(0/min)` - 通过这样的规律,就可以找到如何配置了。 +- 此外还支持 JMeter 函数:`pause(2 min) rate(${__Random(10,50,)}/min)random_arrivals(${__Random(10,100,)} min) rate(${__Random(10,1000,)}/min)` - 也可以多行配置。 +- 负载举例;总时长为1分10秒。前10秒内,速率达到10/s,然后,在1分钟内吞吐量将保持在10/s。最大吞吐量为600个/分钟。配置:`rate(0/s) random_arrivals(20 s) rate(10/s) random_arrivals(1 m) rate(10/s)` + +#### 1.2 简单线程组 + +
+ +
+ +简单线程组配置起来更简单,也适合一些循环压测的场景。 +- 线程数:一个用户相当于一个线程。 +- Ramp-Up:预期线程组的所有线程从启动-运行-释放的总时间。ramp up=0时,表示瞬时加压,启动线程的时间无限趋近于0。在负载测试的时候,尽量把ramp up设置大一些,让性能曲线平缓,容易找到瓶颈点。 +- 循环次数:线程组的循环次数,如果不设置,则表示在调度时间范围内一直循环(jmeter不停的发请求)。 +- 调度器:执行的时间设置。 + +--- + +此外,JMeter 还可以安装插件,设置更多的线程组模型来压测。 + +### 2. 取样器 + +JMeter 把对压测的内容,抽象为取样器。包括HTTP接口、FTP服务等。 + +
+ +
+ +如图你可以通过这样的方式,在创建好的线程组下,创建一个取样器(HTTP压测接口)。不过这里小傅哥更建议你使用 cURL 方式导入使用。 + +#### 2.1 复制 cURL + +
+ +
+ +#### 2.2 导入 cURL + +
+ +
+ +- 导入以后,可以把HTTP请求拖到线程组下面。 + +### 3. 监听器 + +线程组是各类方式的模拟压测调用,取样器HTTP是压测的接口。那么监听器就是看线程组对取样器HTTP的压测结果。 + +
+ +
+ +## 三、工程准备 + +为了让大家更加方便的测试,不用自己在折腾,可以直接使用测试工程。测试工程内提供了测试的接口,以及对应的 jmx 脚本。启动后就可以执行测试。 + +
+ +
+ +- docker-compose.yml 是部署 JMeter 到 Docker 的脚本。并在脚本中映射了本地的压测脚本。 +- one.jmx 是在使用 JMeter 时导出的脚本,你可以直接复制 JMeter 脚本,也可以让 JMeter 保存脚本的时候选择到这个路径下。 +- 注意 one.jmx 有压测对应接口的 IP,测试的时候需要修改为你的服务器/本机IP才可以。`127.0.0.1` + +## 四、压测验证 + +### 1. 本地压测 + +- 开启服务:xfg-dev-tech-jmeter +- 启动压测:JMeter + +
+ +
+ +
+ +
+ +### 2. 脚本压测 + +
+ +
+ +压测命令:`jmeter -n -t one.jmx -l one.jtl` + +## 五、其他工具 + +- [ApacheBench](https://httpd.apache.org/docs/2.4/programs/ab.html):一个轻量级的HTTP性能测试工具,可以模拟多种类型的负载,并提供详细的测试报告。脚本:`ab -n 10 -c 2 http://localhost:8091/api/jmeter/query_order_info?orderId=100001` +- [Siege](#):是常用的HTTP性能测试工具,可以模拟多个并发用户发送请求。脚本:`siege -c10 -r1 -p http://localhost:8091/api/jmeter/query_order_info?orderId=100001` +- [ApiPost](https://www.apipost.cn/):自带接口简单压测模拟,安装更加简单,适合初步压测验证。 \ No newline at end of file diff --git a/docs/md/road-map/joycode.md b/docs/md/road-map/joycode.md new file mode 100644 index 000000000..4bdf9bb1f --- /dev/null +++ b/docs/md/road-map/joycode.md @@ -0,0 +1,209 @@ +--- +title: JoyCode +lock: need +--- + +# JoyCode - 代码洪流 一念即成! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**🧨25年以来**,先是 RAG 知识库,再到发布 MCP 协议。到这,各个互联网公司哇哇进入了 AI Agent 智能体开发,在各个应用场景探索提效,做出了很多的智能体产品,如;`Dify`、`Coze`、`Claude`,以及 `AI IDEA` 产品辅助编码(`Trae.ai`、`Cursor`等)。 + +曾经还以为 AI 出了这么多产品,是不程序员的工作岗位会变少🤔?**好家伙,一点没少不说,还变多了**。因为互联网的思维是,**你干的越快,你活tm越多**。甚至不少公司已经要求,程序员的 AI 编码量要在50%以上。**说白了就是,你得快点整 😂**。 + +
+ +
+ +**但,凡事不是坏事!** + +其实我们公司也要求了 AI 编码量,他所编写的代码提交是有 Git 记录和 AI IDEA 记录的,以此进行分析使用情况。公司里这么做也能理解,大家都用才能整体提高编码效率。而且这事多老程序员是有好处的,老程序员的`架构思维厚实`、`编码经验充足`,`邪修技巧多样`,把这些思维转换为提示词,可以节省大量的编码量(毕竟老码农体力不如新生代)。 + +此外,像是这些 AI IDEA 除了企业的编码外,对程序员的学习是非常有好处的。比如对代码的理解,对模块的分析、对bug的检查处理,以及通过提问的方式拓展式的增强理解工程代码都是有好处的。**新人一定使用!** 因为不只是学习,而是进入企业后还要使用。 + +那么有哪些不错的 AI IDEA 吗?有的,Cursor、Trae.ai 都不错的,之后,还有一个可以免费使用的 Joycode 京东出的,使用起来也不错,支持很多模型。地址:[https://joycode.jd.com/docs/start/intro/](https://joycode.jd.com/docs/start/intro/) + +
+ +
+ +> 接下来,小傅哥就分享下关于 JoyCode 的使用。让小伙伴们更好的使用 AI 辅助编码和学习代码。 + +## 一、软件下载 + +官网地址:[https://joycode.jd.com/](https://joycode.jd.com/) + +
+ +
+ +- 代码洪流 一念即成。JoyCode,专为应对企业级复杂任务而设计的智能编码工具。 +- 在使用的时候,你可以一个工程同时在 IntelliJ IDEA 打开,也使用 Trae 打开。😂 因为习惯 IntelliJ IDEA,各类调试还是很舒服的。 + +> 同类软件 [Cursor](https://www.cursor.com/cn),不过这个想使用好的模型得付费,Joycode 暂时是不需要付费的!其实我们需要百家争鸣,各家都用一些才好。 + +## 二、使用场景 + +### 1. 提问代码 + +拖拽代码/文件夹/工程 + +
+ +
+ +
+ +
+ +- 对于需要的代码进行选择式的提问,选中后的代码,在对话框中会标注出来。如果你在对话中还要告诉AI用其他部分的代码,则把对应位置的代码,写到对话框里就可以,如;`cn.bugstack.domain.auth.service.WeixinLoginService` +- 所有我们编程过程中,可以做的操作,都可以让 AI 来做,但尽量描述准确。这也就是老码农,为啥能更准确的使用 AI IDEA 工具,因为可以知道描述的内容最终可以产出的结果。 + +### 2. 完善注释 + +
+ +
+ +
+ +
+ +对于类、方法、字段属性,都可以让 AI 来补全注释说明,辅助我们学习使用。如果你对某一快逻辑厘不清的时候,也可以使用 AI 这样理解代码。 + +### 3. 开发功能 + +#### 3.1 curl 接口 + +
+ +
+ +#### 3.2 开发接口 + +
+ +
+ +**描述的话术,尽量准确** + +```java +在 s-pay-mall-ddd-infrastructure 模块下, cn.bugstack.infrastructure.gateway 文件包下,编写 IJueJinService 类,以 retrofit2 方式包装 curl 请求接口。接口入参仅为必要参数,如;cookie,发文的 Request 对象。 + +curl 如下; + +curl 'https://api.juejin.cn/content_api/v1/article_draft/create?aid=2608&uuid=7058897578062890496' \ + -H 'accept: */*' \ + -H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8' \ + -H 'content-type: application/json' \ + -b 'store-region=cn-bj; store-region-src=uid; __tea_cookie_tokens_2608=%******a51b13e5; uid_tt_ss=7aff2f2394310b3b71f07006a51b13e5; sid_tt=3813572f48984e4b5b3cc7f90733f2e9; sessionid=3813572f48984e4b5b3cc7f90733f2e9; sessionid_ss=3813572f48984e4b5b3cc7f90733f2e9; sid_ucp_v1=1.0.0-KDU4N2NlM2Q2NjFiODhjOGNhZGE5YzVlOTM4ZWZkY2U3ZTc3MzVjMjAKFwjemIC67ozUAxDT_ci_BhiwFDgCQPEHGgJsZiIgMzgxMzU3MmY0ODk4NGU0YjViM2NjN2Y5MDczM2YyZTk; ssid_ucp_v1=1.0.0-KDU4N2NlM2Q2NjFiODhjOGNhZGE5YzVlOTM4ZWZkY2U3ZTc3MzVjMjAKFwjemIC67ozUAxDT_ci_BhiwFDgCQPEHGgJsZiIgMzgxMzU3MmY0ODk4NGU0YjViM2NjN2Y5MDczM2YyZTk' \ + -H 'dnt: 1' \ + -H 'origin: https://juejin.cn' \ + -H 'priority: u=1, i' \ + -H 'referer: https://juejin.cn/' \ + -H 'sec-ch-ua: "Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"' \ + -H 'sec-ch-ua-mobile: ?0' \ + -H 'sec-ch-ua-platform: "macOS"' \ + -H 'sec-fetch-dest: empty' \ + -H 'sec-fetch-mode: cors' \ + -H 'sec-fetch-site: same-site' \ + -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' \ + -H 'x-secsdk-csrf-token: 00010000000152d838f2c09c65e4feef033c275a8cff940c12228719074fe36f9ce2d18b4d251833ae62ea768117' \ + --data-raw '{"category_id":"0","tag_ids":[],"link_url":"","cover_image":"","title":"测试文章","brief_content":"","edit_type":10,"html_content":"deprecated","mark_content":"","theme_ids":[],"pics":[]}' + +接口返回结果; + +{ + "err_no": 0, + "err_msg": "success", + "data": { + "id": "7489358088379531291", + "article_id": "0", + "user_id": "2058727733595230", + "category_id": "0", + "tag_ids": [], + "link_url": "", + "cover_image": "", + "is_gfw": 0, + "title": "测试文章", + "brief_content": "", + "is_english": 0, + "is_original": 1, + "edit_type": 10, + "html_content": "deprecated", + "mark_content": "", + "ctime": "1743929226", + "mtime": "1743929226", + "status": 0, + "original_type": 0, + "theme_ids": [] + } +} + +根据接口信息封装 DTO 对象,放到 gateway dto 下。 +``` + +### 4. 单元测试 + +
+ +
+ +
+ +
+ +- 编写单测试一个很好用的场景,因为单测即使写错了也不会影响到线上的业务流程。 + +### 5. 系统分析 + +
+ +
+ +- 让 ai idea 工具分析系统工程,它分析的还是很不错的。这非常有利于伙伴对项目的学习、理解、重构,是非常提效的。 + +## 三、AI 来了吗? + +刷”知乎“的时候,有伙伴问,AI 真的来了吗?这何止是来了,我已经感受到它来的太快了,21年 AI 可以对话、22 年各种代理的服务、23 年大家结合AI 做各种服务,24年推出 MCP 协议,25年大家进入了 AI Agent 智能体,谷歌发布 A2A 协议。照这样发展,明年、后年,将会有更加成熟的 AI 场景使用,企业里也会开启大量的 AI 应用开发岗位,为各个场景提效。 + +
+ +
+ +你会在各个公司的招聘里看到 AI 应用开发岗,我以后也是会回天津的,顺手搜了下。附近就有联想公司的这样的岗位。早点储备能力! + +关于 AI 小傅哥已完成项目包括;OpenAI 代码自动评审、OpenAI 应用(含支付)、AI Agent 智能体,以及正在开展的 AI MCP Gateway 网关服务项目。感兴趣的伙伴,可以一起加入学习。 + +### 1. AI Agent 智能体 + +
+ +
+ +
+ +
+ +### 2. OpenAI 应用(含支付) + +
+ +
+ +
+ +
+ +### 3. AI MCP GateWay + +
+ +
+ +> 很多能力就是这样,早早的储备起来,以备不时之需!程序员也是一个一直学习的行业!春江水暖鸭先知🦆,码农需要学知识! diff --git a/docs/md/road-map/kafka.md b/docs/md/road-map/kafka.md new file mode 100644 index 000000000..303f75fef --- /dev/null +++ b/docs/md/road-map/kafka.md @@ -0,0 +1,314 @@ +--- +title: Kafka +lock: need +--- + +# 在 DDD 中优雅的发送 Kafka 消息 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +本文的宗旨在于通过简单干净实践的方式教会读者,使用 Docker 部署 Kafka 以及 Kafka 的管理后台,同时基于 DDD 工程使用 Kafka 消息。这里有一个非常重要的点,就是怎么优雅的在 DDD 工程结构下使用 MQ 消息。 + +在整个《Java简明教程》已经讲解过 [RocketMQ](https://bugstack.cn/md/road-map/rocketmq.html)、[RabbitMQ](https://bugstack.cn/md/road-map/rabbitmq.html) 的使用,本文是对 MQ 系列的一个补充,基本大家在选择使用 MQ 组件时,也就这三类。 + +本文涉及的工程: +- xfg-dev-tech-kafka:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-kafka](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-kafka) +- Kafka Docker 安装:[docs/dev-ops/docker-compose.yml](#) +- SwitchHost: [https://switchhosts.vercel.app/zh](https://switchhosts.vercel.app/zh) - 下载安装 + +## 一、环境安装 + +### 1. host 映射 + +
+ +
+ +下载 SwitchHost 配置一个映射地址。点击 `+` 添加一个本地环境,之后配置你的 IP kafka 这样就能找这个地址了。IP 为你本地的IP,如果是云服务器就是公网IP地址。 + +### 2. 安装脚本 + +本案例涉及了 Kafka 的使用,环境的安装脚本已经放到工程下,可以直接点击安装即可。—— 需要前置条件已安装 [Docker](https://bugstack.cn/md/road-map/docker.html) 环境。 + +
+ +
+ +
+ +
+ +- Mac 电脑会比较好安装一些,直接在 IntelliJ IDEA 点击小绿色按钮即可完成安装。安装完成后进入 [http://localhost:9000/#!/2/docker/containers](http://localhost:9000/#!/2/docker/containers) 可看到 zookeeper、kafka、kafka-eagle 运行啦。 +- Windows 电脑安装 Docker 需要折腾下 +- Linux 服务器,需要上传整个 dev-ops 后在云服务器执行脚本安装;`docker-compose -f docker-compose.yml up -d` +- 如图29行,有一个 kafka:9092 这个 kafka 是个 host 地址,就是 SwitchHost 打开后配置本地的 ip地址映射 kafka +- 另外推荐 [kafka king](https://github.com/Bronya0/Kafka-King/releases) 作为管理界面 + +### 3. 访问地址 + +- 地址:[http://127.0.0.1:8048/](http://127.0.0.1:8048/) +- 账密:admin/123456 + +#### 3.1 首页 + +
+ +
+ +#### 3.2 大屏 + +
+ +
+ +#### 3.3 主题 + +
+ +
+ +- 你可以通过 Create 创建主题消息,填写后点击 Submit 保存。 + +## 二、消息流程 + +本节的重点内容在于如何优雅的发送 MQ 消息,让消息聚合到领域层中,并在发送的时候可以不需要让使用方关注过多的细节。【如图】 + +
+ +
+ +- 在领域层中提供一个 event 包,定义事件消息。也就是一个领域中要发什么消息,就定义什么消息。这个消息只归属于当前领域中。 +- 定义的消息则由仓储继承实现【一个领域如果拆分的合理,一般只会有一 +- 个事件驱动,也就有一个事件消息】,如果是有多个消息一种是拆分领域,另外一种是提供多个仓储,还有一种是由仓储层注入实现。 +- 这里我们先有个影响,之后在到代码部分再看下就会更加清楚是怎么实现的了。 + +## 三、代码实现 + +### 1. 工程结构 + +
+ +
+ +- domain 是领域层,提供一个个领域服务。如果一个工程有多个领域,则有不同的 a、b、c 领域包,每个包下有一套【event、model、repository、service】。 +- 在领域层定义的 event 事件,里面涵盖了事件消息。而这个事件消息可以让 UserRepository 继承实现。最终完成消息发送。 +- 最后是 trigger 触发器层,所有的 http、rpc、job、mq 都是一种触发行为。通过触发器的 listener 监听,来接收 mq 消息。 + +### 2. 环境配置 + +application-dev.yml + +```yml +spring: + kafka: + bootstrap-servers: localhost:9092 + producer: + # 发生错误后,消息重发的次数。 + retries: 1 + #当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。 + batch-size: 16384 + # 设置生产者内存缓冲区的大小。 + buffer-memory: 33554432 + # 键的序列化方式 + key-serializer: org.apache.kafka.common.serialization.StringSerializer + # 值的序列化方式 + value-serializer: org.apache.kafka.common.serialization.StringSerializer + # acks=0 : 生产者在成功写入消息之前不会等待任何来自服务器的响应。 + # acks=1 : 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。 + # acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。 + acks: 1 + +... + +# 配置主题 +kafka: + topic: + group: xfg-group + user: xfg-topic +``` + +- 完整配置可参考源码。 +- 需要注意的配置,`bootstrap-servers: localhost:9092` +- user: xfg-topic 是发送消息的主题,可以在 kafka 后台创建。 +- group: xfg-group 任意即可。 + +### 2. 代码实现 + +#### 2.1 配置发送事件 + +**源码**:`cn.bugstack.xfg.dev.tech.infrastructure.event.EventPublisher` + +```java +@Slf4j +@Component +public class EventPublisher { + + @Resource + private KafkaTemplate kafkaTemplate; + + public void publish(String topic, BaseEvent.EventMessage eventMessage) { + try { + String messageJson = JSON.toJSONString(eventMessage); + kafkaTemplate.send(topic, messageJson); + log.info("发送MQ消息 topic:{} message:{}", topic, messageJson); + } catch (Exception e) { + log.error("发送MQ消息失败 topic:{} message:{}", topic, JSON.toJSONString(eventMessage), e); + throw e; + } + } + +} +``` + +- 这个是一个启动 kafka 消息的模板。我们把它放到基础层中。 + +#### 2.2 事件消息定义 + +**源码**:`cn.bugstack.xfg.dev.tech.domain.event.UserMessageEvent` + +```java +public class UserMessageEvent extends BaseEvent { + + @Value("${kafka.topic.user}") + private String topic; + + @Override + public EventMessage buildEventMessage(UserMessage data) { + return EventMessage.builder() + .id(RandomStringUtils.randomNumeric(11)) + .timestamp(new Date()) + .data(data) + .build(); + } + + @Override + public String topic() { + return topic; + } + + /** + * 要推送的事件消息,聚合到当前类下。 + */ + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class UserMessage { + private String userId; + private String userName; + private String userType; + } + +} +``` + +- 首先,BaseEvent 是一个基类,定义了消息中必须的 id、时间、泛型数据。每一个要发送的消息都按照这个结构来发。 +- 关于消息的发送,这是一个非常重要的设计手段,事件消息的发送,消息体的定义,聚合到一个类中来实现。可以让代码更加整洁。 + +#### 2.3 事件消息发送 + +**源码**:`cn.bugstack.xfg.dev.tech.infrastructure.repository.UserRepository` + +```java +@Service +public class UserRepository extends UserMessageEvent implements IUserRepository { + + @Resource + private EventPublisher publisher; + + @Override + public void doSaveUser(UserEntity userEntity) { + // 推送消息 + publisher.publish(this.topic(), this.buildEventMessage(UserMessageEvent.UserMessage.builder() + .userId(userEntity.getUserId()) + .userName(userEntity.getUserName()) + .userType(userEntity.getUserTypeVO().getDesc()) + .build())); + } + +} +``` + +- 用仓储实现类继承事件消息,在完成数据的操作后,推送消息。 + +#### 2.4 事件消息监听 + +**源码**:`cn.bugstack.xfg.dev.tech.trigger.listener.KafkaMessageListener` + +```java +@Slf4j +@Component +public class KafkaMessageListener { + + @KafkaListener(topics = "${kafka.topic.user}", groupId = "${kafka.topic.group}", concurrency = "1") + public void topic_test(ConsumerRecord record, Acknowledgment ack, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) { + Optional message = Optional.ofNullable(record.value()); + if (message.isPresent()) { + Object msg = message.get(); + try { + // 逻辑处理 + + // 确认消息消费完成,如果抛异常消息会进入重试 + ack.acknowledge(); + log.info("Kafka消费成功! Topic:" + topic + ",Message:" + msg); + } catch (Exception e) { + e.printStackTrace(); + log.error("Kafka消费失败!Topic:" + topic + ",Message:" + msg, e); + } + } + } + +} +``` + +- 在触发器层监听消息,来完成解耦的业务流程。 + +## 三、测试验证 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class UserServiceTest { + + @Resource + private IUserService userService; + + @Test + public void test_register() throws InterruptedException { + while (true) { + UserEntity userEntity = new UserEntity(); + userEntity.setUserId("10001"); + userEntity.setUserName("小傅哥"); + userEntity.setUserTypeVO(UserTypeVO.T8); + + userService.register(userEntity); + Thread.sleep(1500); + } + + } + +} +``` + +```java +24-03-17.12:28:58.308 [main ] INFO EventPublisher - 发送MQ消息 topic:xfg-topic message:{"data":{"userId":"10001","userName":"小傅哥","userType":"架构师"},"id":"45672247343","timestamp":1710649737803} +24-03-17.12:28:59.811 [main ] INFO EventPublisher - 发送MQ消息 topic:xfg-topic message:{"data":{"userId":"10001","userName":"小傅哥","userType":"架构师"},"id":"18572935390","timestamp":1710649739809} +24-03-17.12:29:01.294 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO ConsumerCoordinator - [Consumer clientId=consumer-xfg-group-1, groupId=xfg-group] Successfully joined group with generation Generation{generationId=1, memberId='consumer-xfg-group-1-f1c1ab73-b72d-4296-809b-d951f88a49dd', protocol='range'} +24-03-17.12:29:01.297 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO ConsumerCoordinator - [Consumer clientId=consumer-xfg-group-1, groupId=xfg-group] Finished assignment for group at generation 1: {consumer-xfg-group-1-f1c1ab73-b72d-4296-809b-d951f88a49dd=Assignment(partitions=[xfg-topic-0])} +24-03-17.12:29:01.314 [main ] INFO EventPublisher - 发送MQ消息 topic:xfg-topic message:{"data":{"userId":"10001","userName":"小傅哥","userType":"架构师"},"id":"15051699480","timestamp":1710649741313} +24-03-17.12:29:01.334 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO ConsumerCoordinator - [Consumer clientId=consumer-xfg-group-1, groupId=xfg-group] Successfully synced group in generation Generation{generationId=1, memberId='consumer-xfg-group-1-f1c1ab73-b72d-4296-809b-d951f88a49dd', protocol='range'} +24-03-17.12:29:01.334 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO ConsumerCoordinator - [Consumer clientId=consumer-xfg-group-1, groupId=xfg-group] Notifying assignor about the new Assignment(partitions=[xfg-topic-0]) +24-03-17.12:29:01.341 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO ConsumerCoordinator - [Consumer clientId=consumer-xfg-group-1, groupId=xfg-group] Adding newly assigned partitions: xfg-topic-0 +24-03-17.12:29:01.354 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO ConsumerCoordinator - [Consumer clientId=consumer-xfg-group-1, groupId=xfg-group] Found no committed offset for partition xfg-topic-0 +24-03-17.12:29:01.380 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO SubscriptionState - [Consumer clientId=consumer-xfg-group-1, groupId=xfg-group] Resetting offset for partition xfg-topic-0 to position FetchPosition{offset=0, offsetEpoch=Optional.empty, currentLeader=LeaderAndEpoch{leader=Optional[kafka:9092 (id: 1 rack: null)], epoch=0}}. +24-03-17.12:29:01.381 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO KafkaMessageListenerContainer - xfg-group: partitions assigned: [xfg-topic-0] +24-03-17.12:29:01.631 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO KafkaMessageListener - Kafka消费成功! Topic:xfg-topic,Message:{"data":{"userId":"10001","userName":"小傅哥","userType":"架构师"},"id":"45672247343","timestamp":1710649737803} +24-03-17.12:29:01.642 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO KafkaMessageListener - Kafka消费成功! Topic:xfg-topic,Message:{"data":{"userId":"10001","userName":"小傅哥","userType":"架构师"},"id":"18572935390","timestamp":1710649739809} +24-03-17.12:29:01.647 [org.springframework.kafka.KafkaListenerEndpointContainer#0-0-C-1] INFO KafkaMessageListener - Kafka消费成功! Topic:xfg-topic,Message:{"data":{"userId":"10001","userName":"小傅哥","userType":"架构师"},"id":"15051699480","timestamp":1710649741313} +``` + +- 运行测试,可以看到消息的推送和接收。 diff --git a/docs/md/road-map/linux.md b/docs/md/road-map/linux.md new file mode 100644 index 000000000..6c8e992af --- /dev/null +++ b/docs/md/road-map/linux.md @@ -0,0 +1,437 @@ +--- +title: Linux 开发环境 +lock: need +--- + +# 我把云服务器,搭建成开发环境使用! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +在几年前刚入手 Mac 电脑的时候,有不少伙伴问过我,这电脑有什么优势吗?又不能打游戏!是呀,能想到的就是没有广告、APP安装简单、UI风格细腻。但这些和 Windows 电脑好像也没有太大的差别,各有所好罢了。而且同等配置 Mac 还要贵不少。 + +
+ +
+ +但直到深入编程开发以后,一会搞后端、一些写前端、一会安装Docker环境。Mac 电脑是真的好用,你已经写了一天的代码了,Windows 伙伴还在适配环境。我觉得直到这会它的优势才体现出来,因为 Mac == 云服务器(Linux)! + +那么本文,就是为了 Windows 伙伴所写,提供一套非常容易的,在云服务器就能搭建出来的开发环境和构建前后端项目的方案。让大家可以少一些折腾就能快速启动开发,完成项目的构建和部署操作。—— 其实你早晚也需要使用Linux环境! + +本文涉及的工程; + +- xfg-dev-tech-linux:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-linux](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-linux) 注意 jdk 从下面的地址单独下载。 +- 云服务器配置要求:2c2g [https://618.gaga.plus](https://618.gaga.plus) +- 工程提供了git、 java、maven、redis、mysql 的环境安装,可以让本地连接云服务器的配置,也可以在云服务器拉取项目进行构建操作。 +- 环境软件下载:[https://drive.weixin.qq.com/s?k=ACMA4AfQABUYiIGUXy](https://drive.weixin.qq.com/s?k=ACMA4AfQABUYiIGUXy) - 所需的 `jdk-8u202-linux-x64.tar.gz` 从这里下载。 + +>文末有加入学习方式,提供了8个Java实战项目,非常有东西可以学! + +## 一、工程说明 + +在折腾工程中提供了开发必备的环境安装,可以通过执行脚本的方式安装和卸载 Java、Maven 以及通过 Docker Compose 安装 Redis、MySQL。 + +
+ +
+ +- 首先,确保你的 Linux 已安装了 Docker + Compose、Portainer —— 这部分内容在小傅哥的Java简明教程中以及提供。https://bugstack.cn/md/road-map/road-map.html +- java、maven 执行脚本即可,执行前可以先通过 chmod +x install-java.sh 附上执行权限(其他sh文件类似) +- docker-compose-environment.yml 是负责安装 mysql、redis 的,云服务器安装后要配置端口开放,这样本地才能访问。mysql 的密码注意要设置的复杂一些。 +- 你可以把自己的项目的sql创建库表语句,放到 mysql/sql 的文件夹下。 + +## 二、环境安装 + +推荐使用 [termius](https://termius.com/) 工具连接你的云服务,这个工具自带 SFPT 可以方便的上传文件。 + +把 dev-ops 文件夹上传到云服务器上。*我是上传到了 / 根目录,不是 root 下!* + +
+ +
+ +在你把 xfg-dev-tech-linux 克隆到本地以后,把 dev-ops 整个文件夹上传到云服务器的根目录即可。之后就可以执行下面的环境安装操作了。 + +常用命令; +- `rm -rf ...` 删掉文件夹。安装的文件会进入 `/user/local/java`、`/user/local/maven`,可以进入后删掉旧版的文件。 +- `cat /etc/profile` 查看配置文件 +- `vim /etc/profile` 进入后输入 i 后开始编辑,编辑后点击 esc 之后输入 :wq 退出。如果你有安装多次,但这里 Java、Maven 配置有多份,可以删掉。 +- `git clone https://xxx.git` 检出项目 +- `git branch` 列出项目分支 +- `git checkout master` 检出 master 分支 + +### 1. 安装 Git + +```java +sudo yum install git +``` + +安装后通过 `git --version` 查看版本 + +### 2. 安装 Java JDK + +```java +cd /dev-ops/java +chmod +x install-java.sh +./install-java.sh +``` + +
+ +
+ +- 验证:`java -version` 此时会输出 Java 的版本。 +- 提示:如果未输出 java,可以手动执行 `source /etc/profile` +- 卸载:如果不需要 Java 环境了,可以同样方式执行 `./remove-java.sh` + +**也可以手动安装** + +1. 解压jdk + +```java +tar -zxvf jdk-8u202-linux-x64.tar.gz +``` + +2.修改解压后文件的文件名 + +```java +mv jdk1.8.0_202 jdk-8u202 +``` + +3.配置Java环境,编辑/etc/profile文件 + +```java +vim /etc/profile +``` + +4.打开以后将下面三句输入到文件里面并保存退出 + +```java +export JAVA_HOME=/software/jdk-8u202 +export JRE_HOME=${JAVA_HOME}/jre +export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib:$CLASSPATH +export JAVA_PATH=${JAVA_HOME}/bin:${JRE_HOME}/bin +export PATH=$PATH:${JAVA_PATH} +``` + +5.生效profile + +```java +source /etc/profile +``` + +6.查询Java版本,出现的版本是自己下载的版本,则安装成功。 + +```java +java -version +``` + +### 3. 安装 Maven + +```java +cd /dev-ops/maven +chmod +x install-maven.sh +./install-maven.sh +``` + +
+ +
+ +- 验证:`mvn -version` 此时会输出 maven 的版本。 +- 提示:如果未输出 Maven,可以手动执行 `source /etc/profile` +- 卸载:如果不需要 Maven 环境了,可以同样方式执行 `./remove-maven.sh` + +### 4. 安装 MySQL、Redis + +```java +cd /dev-ops +docker-compose -f docker-compose-environment.yml up -d +``` + +
+ +
+ +- 访问:[http://117.72.37.243:9000/#!/2/docker/containers](http://117.72.37.243:9000/#!/2/docker/containers) - 访问你的 Portainer,就可以看见安装内容了。 +- 13306、16379、8081、8899,这几个端口,需要在云服务器打开。 +- 实际自己使用的时候,MySQL 数据库的密码可以配置的更强一些。 + +## 三、项目构建 + +以上这些环境的安装就可以支撑我们部署项目了,这里以小傅哥最近带着大家做的大营销平台项目(前后端)进行打包构建举例。 + +开始执行之前进入到 /dev-ops 通过命令创建 github 文件夹 `mkdir github`,后续把项目检出到这个文件夹。 + +### 1. 后端构建 + +#### 1.1 克隆项目 + +```java +[root@lavm-aqhgp9nber dev-ops]# cd github/ +[root@lavm-aqhgp9nber github]# ls +[root@lavm-aqhgp9nber github]# git clone https://gitcode.net/KnowledgePlanet/big-market/big-market.git +Cloning into 'big-market'... +Username for 'https://gitcode.net': Yao__Shun__Yu +Password for 'https://Yao__Shun__Yu@gitcode.net': +remote: Enumerating objects: 424, done. +remote: Counting objects: 100% (424/424), done. +remote: Compressing objects: 100% (222/222), done. +remote: Total 1494 (delta 113), reused 283 (delta 71), pack-reused 1070 +Receiving objects: 100% (1494/1494), 229.88 KiB | 0 bytes/s, done. +Resolving deltas: 100% (418/418), done. +``` + +- git clone 项目,需要输入你的账号密码。也可以配置 ssh 秘钥方式,就不用输入账号密码了。 + +#### 1.2 构建项目 + +```java +[root@lavm-aqhgp9nber big-market]# cd /dev-ops/ +[root@lavm-aqhgp9nber dev-ops]# ls +docker-compose-app.yml docker-compose-environment.yml github java maven mysql redis +[root@lavm-aqhgp9nber dev-ops]# cd github/ +[root@lavm-aqhgp9nber github]# ls +big-market +[root@lavm-aqhgp9nber github]# cd big-market/ +[root@lavm-aqhgp9nber big-market]# ls +big-market-api big-market-domain big-market-trigger docs README.md +big-market-app big-market-infrastructure big-market-types pom.xml +[root@lavm-aqhgp9nber big-market]# mvn clean install +[INFO] Scanning for projects... +[INFO] ------------------------------------------------------------------------ +[INFO] Reactor Build Order: +[INFO] +[INFO] big-market [pom] +[INFO] big-market-types [jar] +[INFO] big-market-domain [jar] +[INFO] big-market-api [jar] +[INFO] big-market-trigger [jar] +[INFO] big-market-infrastructure [jar] +[INFO] big-market-app [jar] +[INFO] +[INFO] -----------------------< cn.bugstack:big-market >----------------------- +[INFO] Building big-market 1.0-SNAPSHOT [1/7] +[INFO] Reactor Summary for big-market 1.0-SNAPSHOT: +[INFO] +[INFO] big-market ......................................... SUCCESS [ 0.594 s] +[INFO] big-market-types ................................... SUCCESS [ 4.069 s] +[INFO] big-market-domain .................................. SUCCESS [ 2.759 s] +[INFO] big-market-api ..................................... SUCCESS [ 0.775 s] +[INFO] big-market-trigger ................................. SUCCESS [ 3.834 s] +[INFO] big-market-infrastructure .......................... SUCCESS [ 2.423 s] +[INFO] big-market-app ..................................... SUCCESS [ 3.730 s] +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 18.696 s +[INFO] Finished at: 2024-03-31T13:22:04+08:00 +[INFO] ------------------------------------------------------------------------ +``` + +- 通过 `mvn clean install` 命令先构建项目。 + +#### 1.3 打包镜像 + +```java +[root@lavm-aqhgp9nber big-market]# cd big-market-app/ +[root@lavm-aqhgp9nber big-market-app]# ls +build.sh Dockerfile pom.xml src target +[root@lavm-aqhgp9nber big-market-app]# chmod +x build.sh +[root@lavm-aqhgp9nber big-market-app]# ./build.sh +[+] Building 2.1s (8/8) FINISHED docker:default + => [internal] load build definition from Dockerfile 0.0s + => => transferring dockerfile: 372B 0.0s + => [internal] load metadata for docker.io/library/openjdk:8-jre-slim 0.0s + => [internal] load .dockerignore 0.0s + => => transferring context: 2B 0.0s + => [1/3] FROM docker.io/library/openjdk:8-jre-slim 0.0s + => [internal] load build context 0.5s + => => transferring context: 64.71MB 0.5s + => CACHED [2/3] RUN ln -snf /usr/share/zoneinfo/PRC /etc/localtime && echo PRC > /etc/ 0.0s + => [3/3] ADD target/big-market-app.jar /big-market-app.jar 1.2s + => exporting to image 0.3s + => => exporting layers 0.3s + => => writing image sha256:aee0181546cccf3e6677aa8de6fae13d87c4cf812f0871a994c2ba1eaca 0.0s + => => naming to docker.io/fuzhengwei/big-market-app:1.0 0.0s +``` + +- 现在可以看到 `fuzhengwei/big-market-app:1.0` 的镜像已经打包出来来了。 + +### 2. 前端构建 + +#### 2.1 克隆项目 + +```java +[root@lavm-aqhgp9nber big-market-app]# cd /dev-ops/github/ +[root@lavm-aqhgp9nber github]# ls +big-market +[root@lavm-aqhgp9nber github]# git clone https://gitcode.net/KnowledgePlanet/big-market/big-market-front.git +Cloning into 'big-market-front'... +Username for 'https://gitcode.net': Yao__Shun__Yu +Password for 'https://Yao__Shun__Yu@gitcode.net': +remote: Enumerating objects: 93, done. +remote: Counting objects: 100% (93/93), done. +remote: Compressing objects: 100% (49/49), done. +remote: Total 93 (delta 33), reused 87 (delta 31), pack-reused 0 +Unpacking objects: 100% (93/93), done. +``` + +- git clone 项目,需要输入你的账号密码。也可以配置 ssh 秘钥方式,就不用输入账号密码了。 + +#### 2.2 打包镜像 + +```java +[root@lavm-aqhgp9nber github]# cd big-market-front/ +[root@lavm-aqhgp9nber big-market-front]# ls +next.config.js package-lock.json public src tsconfig.json +package.json postcss.config.js README.md tailwind.config.ts +[root@lavm-aqhgp9nber big-market-front]# ll +total 328 +-rw-r--r-- 1 root root 92 Mar 31 13:26 next.config.js +-rw-r--r-- 1 root root 587 Mar 31 13:26 package.json +-rw-r--r-- 1 root root 299024 Mar 31 13:26 package-lock.json +-rw-r--r-- 1 root root 82 Mar 31 13:26 postcss.config.js +drwxr-xr-x 2 root root 4096 Mar 31 13:26 public +-rw-r--r-- 1 root root 1383 Mar 31 13:26 README.md +drwxr-xr-x 3 root root 4096 Mar 31 13:26 src +-rw-r--r-- 1 root root 507 Mar 31 13:26 tailwind.config.ts +-rw-r--r-- 1 root root 599 Mar 31 13:26 tsconfig.json +[root@lavm-aqhgp9nber big-market-front]# git branch +* 240211-xfg-init-project +[root@lavm-aqhgp9nber big-market-front]# git checkout main +Branch main set up to track remote branch main from origin. +Switched to a new branch 'main' +[root@lavm-aqhgp9nber big-market-front]# ls +build.sh next.config.js package-lock.json public src tsconfig.json +Dockerfile package.json postcss.config.js README.md tailwind.config.ts +[root@lavm-aqhgp9nber big-market-front]# chmod +x build.sh +[root@lavm-aqhgp9nber big-market-front]# ls +build.sh next.config.js package-lock.json public src tsconfig.json +Dockerfile package.json postcss.config.js README.md tailwind.config.ts +[root@lavm-aqhgp9nber big-market-front]# ./build.sh + => [internal] load build definition from Dockerfile 0.0s + => => transferring dockerfile: 697B 0.0s + => [internal] load metadata for docker.io/library/node:18-alpine 37.0s + => [internal] load .dockerignore 0.0s + => => transferring context: 2B 0.0s + => [internal] load build context 0.1s + => => transferring context: 599.89kB 0.1s + => [base 1/1] FROM docker.io/library/node:18-alpine@sha256:c698ffe060d198dcc6647be78e 63.1s + => => resolve docker.io/library/node:18-alpine@sha256:c698ffe060d198dcc6647be78ea16833 0.0s + => => sha256:2694e4502e2414f1f0ecb8d3216bd3dd8fdd19fc9edeef31ac653b 39.81MB / 39.81MB 60.7s + => => sha256:f8ecf2fb4bd9a228128a2638a8bd59e1a2b1348019ff7d4ea6de2431 2.34MB / 2.34MB 16.8s + => => sha256:d3da4a73e4df700cd719baa5e2d175a2cda03f72b893c73a4c7063ae89685 450B / 450B 0.3s + => => sha256:c698ffe060d198dcc6647be78ea1683363f12d5d507dc5ec9855f1c55 1.43kB / 1.43kB 0.0s + => => sha256:62ce0df0c57930a42a9f6025b33f165c73217159dcf7681148ecee10c 1.16kB / 1.16kB 0.0s + => => sha256:ed7d7e5a958009e493ebfa4f476c77dcb03013caa7730a0fef7e6aa3a 7.14kB / 7.14kB 0.0s + => => extracting sha256:2694e4502e2414f1f0ecb8d3216bd3dd8fdd19fc9edeef31ac653b250fe11e 2.0s + => => extracting sha256:f8ecf2fb4bd9a228128a2638a8bd59e1a2b1348019ff7d4ea6de2431a76179 0.1s + => => extracting sha256:d3da4a73e4df700cd719baa5e2d175a2cda03f72b893c73a4c7063ae896855 0.0s + => [deps 1/5] RUN apk add --no-cache libc6-compat 230.3s + => [builder 1/4] WORKDIR /app 0.7s + => [deps 2/5] WORKDIR /app 0.0s + => [deps 3/5] COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ 0.1s + => [deps 4/5] RUN yarn config set registry 'https://registry.npmmirror.com/' 0.7s + => [deps 5/5] RUN yarn install 151.6s + => [builder 2/4] COPY --from=deps /app/node_modules ./node_modules 9.9s + => [builder 3/4] COPY . . 0.2s + => [builder 4/4] RUN yarn build 47.4s + => [runner 2/5] COPY --from=builder /app/public ./public 0.1s + => [runner 3/5] COPY --from=builder /app/.next/standalone ./ 0.5s + => [runner 4/5] COPY --from=builder /app/.next/static ./.next/static 0.1s + => [runner 5/5] COPY --from=builder /app/.next/server ./.next/server 0.1s + => exporting to image 0.4s + => => exporting layers 0.4s + => => writing image sha256:cdaae841aad5a14c8e8befbdab54827a4005a42bbe7904b9b0291e46342 0.0s + => => naming to docker.io/fuzhengwei/big-market-front-app:1.4 0.0s +``` + +- 检出项目后,没有看到 Dockerfile 文件,所以执行了 git branch 检查当前分支。之后执行 git checkout main 切换到 main 分支上。 +- 接下来给 build.sh 加上权限,后执行构建脚本。 + +```java +sudo mkdir -p /etc/docker +sudo tee /etc/docker/daemon.json <<-'EOF' +{ + "registry-mirrors": [ + "https://h1log1d5.mirror.aliyuncs.com", + "http://docker.mirrors.ustc.edu.cn", + "http://hub-mirror.c.163.com" + ] +} +EOF +sudo systemctl daemon-reload +sudo systemctl restart docker +``` + +- 如果构建失败,提示 `https://registry-1.docker.io/v2/: net/http: request canceled` 可以安装下镜像。 + +## 四、项目部署 + +完成第3步以后,你的镜像文件就已经在云服务上了。按照你自己的项目进行docker compose 编写,部署就可以了。如果你学习了小傅哥星球「码农会锁」的大营销实战项目,可以按照以下文件进行部署。 + +```java +version: '3.8' +# 命令执行 docker-compose -f docker-compose-app.yml up -d +services: + big-market-app: + image: fuzhengwei/big-market-app:1.0 + container_name: big-market-app + restart: always + ports: + - "8091:8091" + environment: + - TZ=PRC + - SERVER_PORT=8091 + - APP_CONFIG_API_VERSION=v1 + - APP_CONFIG_CROSS_ORIGIN=* + - SPRING_DATASOURCE_USERNAME=root + - SPRING_DATASOURCE_PASSWORD=123456 + - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/big_market?serverTimezone=UTC&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Shanghai + - SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver + - SPRING_HIKARI_POOL_NAME=Retail_HikariCP + - REDIS_SDK_CONFIG_HOST=redis + - REDIS_SDK_CONFIG_PORT=6379 + volumes: + - ./log:/data/log + networks: + - my-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + big-market-front-app: + container_name: big-market-front-app + image: fuzhengwei/big-market-front-app:1.4 + restart: always + networks: + - my-network + ports: + - 3000:3000 + environment: + - PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + healthcheck: + test: [ "CMD", "wget", "--spider", "-q", "http://0.0.0.0:3000/" ] + interval: 1m + timeout: 10s + retries: 3 + +networks: + my-network: + driver: bridge +``` + +执行;`docker-compose -f docker-compose-app.yml up -d` 脚本即可完成部署。 + +## 五、项目学习 + +小傅哥是把星球「**码农会锁**」当成互联网公司中一个事业群,所需要开发的技术项目来进行构建。所以在小傅哥的星球既可以学习业务项目,还可以掌握技术组件项目。同时为了大家更好的补充项目学习中欠缺的技术点。小傅哥还把各项技术栈拆成独立的案例分享给大家。 + +> 🧧星球「码农会锁」的知识体量是非常成体系的,也非常全面。加入这样一个星球,你的技术就可以稳步提升了!项目预览地址:https://gaga.plus + diff --git a/docs/md/road-map/mac.md b/docs/md/road-map/mac.md new file mode 100644 index 000000000..555c13981 --- /dev/null +++ b/docs/md/road-map/mac.md @@ -0,0 +1,102 @@ +--- +title: MacBook Pro +lock: need +--- + +# MacBook Pro - 关于我是不是该买Mac电脑? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +不少程序员👨🏻‍💻伙伴,一到618、双11和开学季的时候,总会问买个 Windows 电脑,还是搞个 MacBook Pro?网上搜的很多对比,都是说同配置的苹果更贵,但心里总痒痒也好奇,MacBook Pro 是不更好用还能装个B! + +
+ +
+ +**如果你是程序员👨🏻‍💻,我更推荐苹果电脑。为啥?** + +首先,Windows 和 Mac 没有好坏之分,只不过 Windows 有 Windows 的使用场景、Mac 有 Mac 的应用领域。你喜欢打游戏、折腾些硬件对接、综合工作和娱乐,那 Windows 就是最强的选择。 + +但如果只在程序员这一行业对比编程写代码,一台笔记本,灵敏的触控板让你忘记鼠标的存在,苹果 IOS、IPad、Mac 全家桶式的多设备互通,可以做到极致的体验。 + +而更重要的是,Java 写好的代码是要放到 Linux 运行,而不是 Windows,所有你在所需的环境、所编写的脚本,可以无差别从 Mac 转移到 Linux 使用。这是 Windows 没法做到的,甚至 Windows 要在这块浪费掉你很多的时间。*不是不能用,就是不丝滑!* + +>接下来,小傅哥分享下 Mac 电脑的使用经验。新入坑伙伴可以参考。 + +## 一、我的经历 - 入坑 Mac! + +2019年,在这之前我只用过 Windows 电脑,包括;台式机、笔记本,还有自己DIY的台式机。Windows 给我感觉就是抗造,能自己折腾,加个内存,换个固态硬盘。打起来游戏方便,无论是澄海3c还是lol英雄联盟。 + +但在这之前,我也就只有一台 thinkstation 的台式机,每次长假期赔媳妇出门,我都想着要是有个笔记本电脑,我就可以在外面写代码。要不多没意思。所以,我的理由就有了! + +2019年,趁着618活动,搞了第一台 Mac 电脑 i5 8G 256G SSD。说实话这个配置一点也不高,而且价格也不便宜 `¥10189.00` 元。好在送一个 `Beats Solo3 Wireless`耳麦。 + +
+ +
+ +不过 `Beats Solo3 Wireless` 被我在咸鱼转手卖掉回血了。 + +
+ +
+**但谁能想得到,这台电脑在5个月以后,还能给我返回来 5000 元!** + +2019年,双11备战誓师大会。抽取幸运备战人员,清空购物车!最高清空额度,就是同年 618 期间消费的额度,上限 5000 元! + +
+ +
+ +这不巧了🐴,这不巧了🐴!618,我呀,我买了电脑,` ¥10189.00`!Duang、Duang、Duang,大转盘一转,还真抽中我了!那一刻,我决定做个抽奖系统! + +
+ +
+ +从此开启了苹果全家桶系列,苹果手机、IPad mini、IPad Pro ...,讲真,确实好用!那有人问,你 Mac 电脑,也打不了游戏呀?嗯,我媳妇给我买了PS5,还中了一台 Switch 😂 + +> 是的,后来我做了 Lottery 抽奖系统,还有大营销系统(Lottery Plus)! + +## 二、体验对比 - Mac 优势 + +这里我不谈那些打游戏、或者和一些硬件的适配。因为你有那些诉求,直接选择 Windows 就可以。比如;外星人也不错。 + +- 续航基本在10个小时左右,你是不需要带着充电电源的。像是在公司,双周需求评审,要占用一整个下午,这个期间电脑不需要插电源。 +- 不需要每天早上来开机,晚上回去关机。只要它不死机,甚至半年不都不关机。合上就走,打开就用。 +- 对于常用的技术编程环境类软件,直接下载就安装。因为这类软件是按照 Linux 系统做的,不是按照 Windows 系统做的。很多时候 Windows 要做兼容。比如执行脚本、安装 Docker,Windows 都要折腾一下。 +- 不需要安装各类杀毒软件,安装和卸载软件十分方便。有一些 Mac 的软件是付费的软件,但很多是一次付费终身使用,一点广告没有。不付费也可以破解使用,很多 Mac 软件都提供了破解版。 +- 尤其是 M2、M3 系列的 Mac,性能很炸裂,同时开几个 IDEA + 一堆环境类软件,以及一堆的打开的网页也很丝滑。 +- 切屏极其容易,多屏幕工作在 Mac 上是利器,可以不同的屏幕处理不同的事项。但在 Windows 切屏就挺费力气! +- Mac 电脑可以外接显示器,这个 Windows 也有。但 Mac 还可以和 IPad 联动,功能叫随行。 +- Mac 上复制的内容,或者苹果手机上复制内容,可以互相粘贴。也可以极其简单的方式互传文件。 +- Mac 电脑的触控板,一周内可习惯,习惯后不需要使用鼠标了。感受过 Mac 的触控板,会感觉鼠标是个鸡肋。间接的你不会有一个鼠标手「长时间用鼠标会做病」。 +- 和其他的软件设备,比如 nas 可以直接链接操作。你可以 Mac 电脑打开 idea,但代码放到 nas 上。这个很爽!并且 nas 可以直接给你提供 docker 环境。下一期可以介绍下 nas 的使用。 + +>其实,我认为还有一点是最重要的。做软件开发的,应该体验最好的软件逻辑。这可以让你的编程思维得到提升。虽然 Mac 比 Windows 贵,但折腾 Windows 浪费的时间也很贵。 + +## 三、装机分享 - 我的软件 + +Mac 电脑的软件非常好安装,双击打卡,之后交给它! + +
+ +
+ +安装软件资源库; + +- 推荐:[https://aerolite.dev/applite](https://aerolite.dev/applite) +- 其他:[https://macwk.cn/](https://macwk.cn/) +- 其他:[https://macapp.org.cn/](https://macapp.org.cn/) + +## 四、避坑经验 - 别浪费钱 + +- 钱少,但想体现Mac,可以考虑 Mac mini,教育版。尽量在16G起。这样价格和一台 Windows 笔记本差不多。 +- 做编程开发,别买二手的Mac。这些软件都是不断的升级的,太老的 Mac 一般都很卡,升级 Mac 系统,就跟升级 苹果手机 IOS 一样,越升级越卡。 +- 推荐买上一年的版本,价格合适。版本也不旧。但别买太低配置或者年限太老的。其实我们天天用的东西,价格高点也合适。 +- Mac 的 M4 也快出了。我下一个会考虑等下 M4。 \ No newline at end of file diff --git a/docs/md/road-map/maven-central.md b/docs/md/road-map/maven-central.md new file mode 100644 index 000000000..75ad2eee9 --- /dev/null +++ b/docs/md/road-map/maven-central.md @@ -0,0 +1,1001 @@ +--- +title: Maven Central +lock: no +--- + +# Maven Central 微服务包发布管理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +诶,`你八股背的挺好,怎么实操这么差?`哎,`你算法刷的挺多,怎么编码这么烂?`啊,`你不是做过微服务,怎么不知道咋引包?`这是越来越多的面试官在招聘完的真实反馈,很多伙伴的经验不是来自于实践而是背诵! + +
+ +
+ +**有些东西没做过就是不知道!** + +所有那些背的东西,都是对结果的单一描述,而不是用大量的实践验证得出的结果,那么就会缺少各种可能的回答。以及对结果的各种论点/观点。 + +如,很多伙伴都提到做的是微服务项目,但对于基础的 Maven 在微服务中的使用,几乎是不知道的。那么就反向说明,你没有真的做过这些。所以你才不知道。 + +所以,如果你想真的学习下来这些经验,就要对这些知识点一个个的实践、验证、积累。在小傅哥的编程路书中以及积累了大量的知识内容,可以学习;[https://bugstack.cn/md/road-map/road-map.html](https://bugstack.cn/md/road-map/road-map.html) + +案例工程; +- [https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a](https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a) +- [https://github.com/fuzhengwei/xfg-dev-tech-micro-service-b](https://github.com/fuzhengwei/xfg-dev-tech-micro-service-b) + +> 接下来,我们再分享下关于微服务 Maven 发包的实践知识。 + +## 一、我为什么报错? + +“傅哥,这怎么报错了,我怎么引不到这个包?” + +
+ +
+ +对于这样的报错,我估计不少伙伴碰到了都有点慌。怎么我刚下载下来的工程,什么都做没做就报错了。这个包在仓库里也没有呀?之所以你会感觉很慌,是因为没有微服务项目的开发经验。 + +
+ +
+只要你碰到这样一个报错,那么在“作者/开发者/公司”的仓库中,一定会有这样一个其他的微服务,只要下载到自己本地,用 IntelliJ IDEA 打开,点击右侧的 maven install 就可以初始化包到你的本地 Maven 仓库了,这样你就能引入了。 + +那关于微服务的包的构建和使用有什么管理和规范吗?🤔 有的! + +## 二、微服务包管理 + +无论是我们个人开发还是在公司开发,都是有一套微服务包的开发和推送以及使用的规范。大家默认统一遵守,所以开发中才会非常协调的进行。 + +
+ +
+ +首先,公司中有很多业务,很多需求,这些东西不能都在一个工程里开发。那么势必就会拆分为多个微服务系统以及通用的技术解决方案组件。那么这些微服务和组件之间就会存在引入调用关系。尤其是一些大的互联网公司,更是会采用 RPC 的通信解决内部微服务之间的高效调用,而 RPC 通信不同于 HTTP 是需要引入接口定义包的,所以必然存在一个微服务中引入另外一个微服务的 Jar 也就是 POM 配置。 + +那么,如果是你个人验证阶段,是先在自己本地开发验证。这个时候不会发包到 maven 公司统一仓库或者中心仓库,那么就需要你自己本地在 A 服务中进行 install,把包发到个人的 maven 本地仓库文件夹中,之后你的 B 服务就可以引入使用了。 + +之后,站在整个公司的视角。你开发完的组件,不只是你使用,还有其他很多人使用。但跨部门,跨组的别人又不一定都有你的代码库权限,这个时候就需要把本地微服务的 Jar 发包到公司的 Maven 镜像库中。一般稍微大一点的公司都会有自己的 Maven 镜像库,如果公司比较小则会使用阿里云这类云厂商提供的的制品库,如;[https://packages.aliyun.com/](https://packages.aliyun.com/) 让公司的所有人 Maven setting 配置文件都配置这套制品库,这样就解决了一个人发包,其他人也能拉取到使用。 + +另外,如果你想你发的包,不只是你自己或公司里的人使用,那么就要把包发到 Maven 统一的中心仓库。[https://central.sonatype.com/publishing](https://central.sonatype.com/publishing) 这个过程稍微有点漫长,会持续 `4-12` 个小时才能完全同步好。 + +> 下文会提到如何发包到阿里云制品库和 Maven 中心仓库。 + +## 三、微服发包规范 + +工程发包会分为开发阶段的 snapshot 包和正式上线使用的 release 包,snapshot 包同一个版本发包后可以被替代,release 包,只能每次更换版本号。 + +```pom + + cn.bugstack + xfg-dev-tech-api + 0.0.0-SNAPSHOT + + + + cn.bugstack + xfg-dev-tech-api + 1.0 + +``` + +- 0.0.0-SNAPSHOT 以 SNAPSHOT 结尾是开发阶段包,都是 0.0.0-SNAPSHOT 命名。 +- release 包,只需要命名版本号即可。 + +另外这样的微服务包的版本号,在一个工程中每个模块都需要在发包的时候一起操作修改。如果是手动的修改就容易遗漏导致问题,所以这里我们要使用 Maven 提供的插件管理包。如; + +``` + + org.codehaus.mojo + versions-maven-plugin + 2.7 + +``` + +
+ +
+ +- 配置好 `versions-maven-plugin` 点击 `version:set` 就可以统一管理工程的版本号了。 + +## 四、阿里云效发包 - 研发组内用 + +### 1. 阿里云制品库 + +地址:[https://packages.aliyun.com/](https://packages.aliyun.com/) - 填写个人信息申请即可。 + +
+ +
+ +申请后,你会看到制品仓库配置,包括生产库和非生产库,他们主要负责给你提供 release、snapshot 发包和拉取。 + +### 2. 发包配置 + +分别进入 release、snapshot,都可以获得一份 Maven settings.xml 配置文件,把两份文件夹下载后可以找到 release、snapshot 的差异,合并成一份文件。这样你就可以在本地发 release、snapshot 包了。 + +
+ +
+ +**配置文件** + +
+ +
+ +**样例文件** + +```pom + + + + + /Users/fuzhengwei/Applications/apache-maven-3.8.4/repository + + + + mirror + central,jcenter,!2452122-snapshot-XqjwfN + mirror + https://maven.aliyun.com/nexus/content/groups/public + + + + + 2452122-snapshot-XqjwfN + 65b081c2242105ca211dd310 + 每个人会有自己的密码 + + + 2452122-release-dbuebF + 65b081c2242105ca211dd310 + 每个人会有自己的密码 + + + + + rdc + + + + + 2452122-snapshot-XqjwfN::default::https://packages.aliyun.com/65b081d4076e069afe3d2f50/maven/2452122-snapshot-xqjwfn + + + + 2452122-release-dbuebF::default::https://packages.aliyun.com/65b081d4076e069afe3d2f50/maven/2452122-release-dbuebf + + + + + + central + https://maven.aliyun.com/nexus/content/groups/public + + true + + + false + + + + snapshots + https://maven.aliyun.com/nexus/content/groups/public + + false + + + true + + + + 2452122-snapshot-XqjwfN + https://packages.aliyun.com/65b081d4076e069afe3d2f50/maven/2452122-snapshot-xqjwfn + + false + + + true + + + + 2452122-release-dbuebF + https://packages.aliyun.com/65b081d4076e069afe3d2f50/maven/2452122-release-dbuebf + + true + + + false + + + + + + central + https://maven.aliyun.com/nexus/content/groups/public + + true + + + false + + + + snapshots + https://maven.aliyun.com/nexus/content/groups/public + + false + + + true + + + + 2452122-snapshot-XqjwfN + https://packages.aliyun.com/65b081d4076e069afe3d2f50/maven/2452122-snapshot-xqjwfn + + false + + + true + + + + 2452122-release-dbuebF + https://packages.aliyun.com/65b081d4076e069afe3d2f50/maven/2452122-release-dbuebf + + true + + + false + + + + + + + rdc + + + +``` + +### 3. 本地发包 + +
+ +
+ +### 4. 查看仓库 + +
+ +
+ +- 发包后,可以到你的仓库查看是否已经将本地包发到了仓库中。 +- 到这里,你的整个研发组,都配置好来自 [https://packages.aliyun.com/](https://packages.aliyun.com/) Maven 配置下的下载链接,就可以统一使用这套包拉取到自己本来进行使用了。 + +## 五、中心仓库发包 - 全球研发用 + +官网:[https://central.sonatype.com/publishing](https://central.sonatype.com/publishing) +文档:[https://central.sonatype.org/publish/publish-portal-upload/](https://central.sonatype.org/publish/publish-portal-upload/) + +### 1. GPG 安装 + +我们需要一个GPG环境,用来对上传的文件进行加密和签名,保证你的jar包不被篡改。这也是发包到 Maven 中心仓库必须的操心。 + +>1991年,程序员Phil Zimmermann为了避开政府监视,开发了加密软件PGP。这个软件非常好用,迅速流传开来,成了许多程序员的必备工具。但是,它是商业软件,不能自由使用。所以,自由软件基金会决定,开发一个PGP的替代品,取名为GnuPG。这就是GPG的由来。 + +#### 1.1 windows + +1. 下载地址:[https://gpg4win.org/download.html](https://gpg4win.org/download.html) +2. 下载完成后直接安装即可,比较傻瓜式安装很简单,记得选中文(如果你英文硬也可以不选) +3. 生成密钥(可以使用命令行生成,也可以直接在操作界面生成) + 1. 文件>新建密钥对(Ctrl+N) -- 创建个人 OpenPGP 密钥对 + ![](https://bugstack.cn/assets/images/pic-content/2019/11/itstack-middleware-schedule-1.png) + 2. 填写个人信息姓名和邮箱 并点击到 新建 + ![](https://bugstack.cn/assets/images/pic-content/2019/11/itstack-middleware-schedule-2.png) + 3. 填写密钥密码 + ![](https://bugstack.cn/assets/images/pic-content/2019/11/itstack-middleware-schedule-3.png) + 4. 将公钥上传到目录服务{如果上传失败,则通过:设置(S)->配置Kleopatra(C),修改 OpenPGP密钥服务器为:hkp://keyserver.ubuntu.com:80} + ![](https://bugstack.cn/assets/images/pic-content/2019/11/itstack-middleware-schedule-4.png) + + +#### 1.2 mac + +1. 下载地址:[https://gpgtools.org/](https://gpgtools.org/) + +
+ +
+ +
+ +
+ +
+ +
+ +- 下载后安装,打开后。点新建,即可创建新的秘钥。记得点击上传公钥,如果没有点击后续也可以在秘钥上右键上传。 +- 注意新秘钥的名称(姓名)是后续配置到 setting.xml、工程 pom 内的名称。 + +### 2. 命名空间配置 + +
+ +
+ +Maven 中心仓库发包需要验证你的域名,确保全世界的唯一性。如果选择 github 登录,你会有一个默认配置的 NameSpace(io.github.fuzhengwei),这个东西的作用就是要和本地工程名 groupId 保持一致的。如工程是 cn.bugstack、plus.gaga、com.liergou,那么你在的 NameSpace 就需要配置一个这样的调过来的域名。 + +
+ +
+ +- 如图配置完添加验证即可,最后验证成功就可以使用了。 + +### 3. maven setting.xml + +```java + + + + + /Users/fuzhengwei/Applications/apache-maven-3.8.4/repository + + + + 填写,你的秘钥密码 + l4Mw+E3f + 你在 + + + + + + ossrh + + true + + + gpg + 填写,你的秘钥密码 + /Users/fuzhengwei/.gnupg/ + + + + + + ossrh + + + +```` + +
+ +
+ +- 注意,打开你的 maven setting.xml 配置文件,完善以上配置内容。 +- servers 下的 username、password,来自于 [https://central.sonatype.com/account](https://central.sonatype.com/account) +- 官网使用说明:[https://central.sonatype.org/publish/generate-portal-token/](https://central.sonatype.org/publish/generate-portal-token/) + +### 4. 手动上传 + +#### 4.1 上传要求 + +文档:[https://central.sonatype.org/publish/publish-portal-upload/](https://central.sonatype.org/publish/publish-portal-upload/) + +
+ +
+ +- 如文档上传要求,你需要把jar、pom、doc、sources 全部打包到 zip 包,同时每个文件的 asc、md5、sha1 也需要打包进来。 +- 这些文件也都是在旧版上传 maven 中央仓库的时候,所需提供的内容。 + +#### 4.2 项目配置 + +**源码**:[https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-lite/-/tree/master/scaffold-lite](https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-archetype-lite/-/tree/master/scaffold-lite) + +```pom + + + 4.0.0 + + + + + + + + cn.bugstack + xfg-dev-tech-api + 0.0.0-SNAPSHOT + + + 1.8 + UTF-8 + 1.8 + 1.8 + 2.9.0 + 2.0.6 + 3.2.0 + 3.2.1 + 1.6 + 1.10 + + + xfg-dev-tech-api + ce API . Copyright © 2023 bugstack虫洞栈 All rights reserved. 版权所有(C)小傅哥 https://github.com/fuzhengwei + https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a + + + + Apache License + https://opensource.org/license/apache-2-0/ + repo + + + + + + Xiaofuge + Xiaofuge + 184172133@qq.com + https://github.com/fuzhengwei + xfg-dev-tech-micro-service-a + https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a + + architect + developer + + Asia/Shanghai + + + + + scm:git:https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a.git + scm:git:https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a.git + HEAD + https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.0 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.12.4 + + true + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + UTF-8 + true + UTF-8 + UTF-8 + + + + attach-javadocs + + jar + + + -Xdoclint:none + + /Library/Java/JavaVirtualMachines/jdk1.8.0_311.jdk/Contents/Home/bin/javadoc + + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + true + false + release + deploy + + + + net.nicoulaj.maven.plugins + checksum-maven-plugin + ${maven-checksum-plugin.version} + + + create-checksums + + artifacts + + + + + + + + + + release + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + -Xdoclint:none + + /Library/Java/JavaVirtualMachines/jdk1.8.0_311.jdk/Contents/Home/bin/javadoc + + + + + + + + + + + +``` + +- 注意,`cn.bugstack` 这里不能是继承。 +- maven-javadoc-plugin:生成 doc 文档。这里要注意,因为我们脚手架不是代码文件,没有doc的,所以要在工程中加一个任意类名文件。工程中小傅哥加了个 Api 类。 +- maven-source-plugin:生成 source 文件。 +- maven-gpg-plugin:是签名的加密文件,需要本地安装过 gpg 包。注意;`checksum-maven-plugin` 需要在 `maven-gpg-plugin` 执行,这样就不用做下面的手动 md5 逻辑了。 +- checksum-maven-plugin:生成 md5、sha1 文件,但这里不会对 pom 生成此文件,还需要单独命令处理。 +- `/Library/Java/JavaVirtualMachines/jdk1.8.0_311.jdk/Contents/Home/bin/javadoc` 配置你自己的 jdk 地址。 + +```shell +md5 ddd-scaffold-lite-1.0.pom > ddd-scaffold-lite-1.0.pom.md5 +shasum ddd-scaffold-lite-1.0.pom > ddd-scaffold-lite-1.0.pom.sha1 +``` + +- 检查生成后的文件,去掉不需要的内容。 + +#### 4.3 构建项目 + +##### 1. 首次构建 + +
+ +
+ +- 需要构建 Release 包。 + +##### 2. 执行命令 + +
+ +
+ +```java +md5 xfg-dev-tech-api-1.0.pom > xfg-dev-tech-api-1.0.pom.md5 +shasum xfg-dev-tech-api-1.0.pom > xfg-dev-tech-api-1.0.pom.sha1 +``` + +- 构建后,执行命令。增加新的校验文件。 + +##### 3. 打包文件 + +
+ +
+ +- 创建一个和构建一样路径的文件夹,mac/linux 操作命名;`mkdir -p cn/bugstack/xfg-dev-tech-api/1.0/` +- 之后把文件复制到文件夹中打一个zip包。 + +#### 6. 发包 + +##### 6.1 提交 + +
+ +
+ +- 提交你的压缩包。 + +##### 6.2 等待通过&发包 + +
+ +
+ +- 稍微等待,验证通过后可以点击 Publish 发布。 +- 预计4-12小时后会同步到 maven 中心仓库以及阿里云的仓库。 + +##### 6.3 发布效果 + +
+ +
+ +- 地址:[https://mvnrepository.com/search?q=cn.bugstack](https://mvnrepository.com/search?q=cn.bugstack) + +### 5. 自动发包(推荐) + +#### 5.1 pom 配置 + +为你的工程配置 pom; + +```java + + + 4.0.0 + + + cn.bugstack + xfg-dev-tech-micro-service-a + 1.0.0 + + + xfg-dev-tech-api + + + 1.8 + UTF-8 + 1.8 + 1.8 + 2.9.0 + 2.0.6 + 3.2.0 + 3.2.1 + 1.6 + 1.10 + + + xfg-dev-tech-api + ce API . Copyright © 2023 bugstack虫洞栈 All rights reserved. 版权所有(C)小傅哥 https://github.com/fuzhengwei + https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a + + + + Apache License + https://opensource.org/license/apache-2-0/ + repo + + + + + + Xiaofuge + Xiaofuge + 184172133@qq.com + https://github.com/fuzhengwei + xfg-dev-tech-micro-service-a + https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a + + architect + developer + + Asia/Shanghai + + + + + scm:git:https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a.git + scm:git:https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a.git + HEAD + https://github.com/fuzhengwei/xfg-dev-tech-micro-service-a + + + + + com.alibaba + fastjson + 2.0.28 + + + org.apache.commons + commons-lang3 + 3.9 + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + + + + src/main/resources + true + + **/** + + + + + + src/test/resources + true + + **/** + + + + + + org.apache.maven.plugins + maven-resources-plugin + 3.2.0 + + UTF-8 + + + src/main/resources + false + + + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + UTF-8 + UTF-8 + UTF-8 + -Xdoclint:none + + + + attach-javadocs + package + + jar + + + -Xdoclint:none + + ${java.home}${file.separator}..${file.separator}bin${file.separator}javadoc + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + ossrh + + + + sign-artifacts + verify + + sign + + + + + + + net.nicoulaj.maven.plugins + checksum-maven-plugin + 1.10 + + + create-checksums + + artifacts + + + + MD5 + SHA-1 + + true + + + + create-pom-checksums + + files + + + + + ${project.build.directory} + + *.pom + + + + + MD5 + SHA-1 + + + + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.4.0 + true + + ossrh + true + true + + + + + + + +``` + +##### Maven 插件 + +项目使用了多个 Maven 插件来支持构建、打包和发布流程: +1. maven-resources-plugin (v3.2.0) + - 用于资源文件处理,确保 UTF-8 编码 +2. maven-source-plugin (v2.2.1) + - 生成源码 jar 包,便于开发者查看源码 +3. maven-javadoc-plugin (v2.9.1) + - 生成 JavaDoc 文档 + - 配置了 UTF-8 编码 + - 禁用了文档检查 (-Xdoclint:none) + 4 . maven-shade-plugin (v3.2.4) + - 用于创建可执行 jar 包 + - 配置了过滤器排除签名文件 +5. maven-gpg-plugin (v1.5) + - 用于对构建产物进行 GPG 签名 + - 指定了签名密钥名称: ossrh +6. checksum-maven-plugin (v1.10) + - 生成构建产物的校验和 (MD5, SHA-1) + - 为 jar 包和 pom 文件创建校验和 +7. central-publishing-maven-plugin (v0.4.0) + - 用于将项目发布到 Maven Central 仓库 + - 配置了自动发布功能 + +##### 发布配置 + +项目配置了发布到 Maven Central 仓库的相关信息: + +- 发布服务 ID : ossrh +- 认证方式 : 令牌认证 (tokenAuth=true) +- 自动发布 : 启用 (autoPublish=true) + +文档:[https://central.sonatype.org/publish/publish-portal-maven/#wait-for-publishing](https://central.sonatype.org/publish/publish-portal-maven/#wait-for-publishing) + +>每个插件和配置项都为项目的构建、发布和管理提供了特定的功能支持。 + +#### 5.2 发布 + +##### 点击发布 + +
+ +
+ +##### 查看结果 + +
+ +
+ +>发布完成后,需要1-3天,才能同步到 [https://mvnrepository.com/](https://mvnrepository.com/) + diff --git a/docs/md/road-map/maven.md b/docs/md/road-map/maven.md new file mode 100644 index 000000000..06bfc7edf --- /dev/null +++ b/docs/md/road-map/maven.md @@ -0,0 +1,177 @@ +--- +title: Maven +lock: no +--- + +# Maven 使用说明和配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +[Apache Maven](https://maven.apache.org/) 是一个软件项目管理、构建和依赖工具。基于项目对象模型 (POM) 的概念,Maven 可以通过中央信息来管理项目的构建、报告和文档。 + +简单来说,Apache Maven 最大的核心功能是帮你管理 Jar 包。不知道你是否在最开始学习 Java 开发时,如果有需要依赖其他 Jar 包,则需要把那个 Jar 复制到工程中,并且还需要在 Eclipse 里配置加载 Jar 包才能使用。而现在有 Maven 这样的工具后,一切都变得简单了。工程所需的 Jar 包,只需要配置 POM 就可以直接使用拉取和使用。 + +
+ +
+ +- **官网**:[https://maven.apache.org/](https://maven.apache.org/) +- **仓库**:[https://mvnrepository.com/](https://mvnrepository.com/) +- **镜像**:[https://developer.aliyun.com/mvn/guide](https://developer.aliyun.com/mvn/guide) - 阿里云提供的公共代理仓库,方便研发伙伴使用,速度更快更稳定。 + +## 一、软件下载 + +Maven 3.8+ 需要 JDK 1.7 或更高版本才能执行。 —— 较为常用的版本 + +**下载**:[https://maven.apache.org/download.cgi](https://maven.apache.org/download.cgi) + +
+ +
+ +
+ +
+ +- 下载后直接解压就可以,它就是文件,不需要做安装操作。 +- 提醒;如果你是在 Linux 等服务器上有使用需求,而不是配置到 IntelliJ IDEA 则需要配置对应的环境变量。这样才能使用 mvn 命令。 + +--- + +编辑 **/etc/profile** 文件 **sudo vim /etc/profile**,在文件末尾添加如下代码: + +``` +export MAVEN_HOME=/usr/local/apache-maven-3.8.8 +export PATH=${PATH}:${MAVEN_HOME}/bin +``` + +保存文件,并运行如下命令使环境变量生效: + +\# source /etc/profile + +在控制台输入如下命令,如果能看到 Maven 相关版本信息,则说明 Maven 已经安装成功: + +``` +# mvn -v +``` + +## 二、环境配置 + +Maven /conf/settings.xml 最常用的配置一个是仓库Jar存放的位置,另外一个为了提高拉取Jar包的速度,需要配置阿里云镜像地址。—— 注意配置前,先复制一个 `settings.xml.bak` 做个备份。 + +
+ +
+ +### 1. 存储地址 + +```java +/Users/fuzhengwei/dev-ops/apache-maven-3.8.8/repository +``` + +### 2. 镜像仓库 + +```java + + + aliyunmaven + * + 阿里云公共仓库 + https://maven.aliyun.com/repository/public + + +``` + +- 配置上阿里云的仓库以后,Maven 的下载速度就嗖嗖的了! + +## 三、使用配置 + +打开你的 IntelliJ IDEA 如图配置 Maven; + +**首次打开时:** + +
+ +
+ +**打开工程时:** + +
+ +
+ +- 配置好你的 Maven 地址就可以使用了。 + +## 四、命令操作 + +IntelliJ IDEA Maven 面板提供了操作控制; + +
+ +
+ +- clean:清理,执行该命令会删除项目路径下的target文件,但是不会删除本地的maven仓库已经生成的jar文件。 +- validate:验证,验证工程正确性,所需信息完整否。 +- compile:编译,大伙都知道java的识别文件是.class,编译生成class文件,编译命令,只编译选定的目标,不管之前是否已经编译过,会在你的项目路径下生成一个target目录,在该目录中包含一个classes文件夹,里面全是生成的class文件及字节码文件。与build区别:只编译选定的目标,不管之前是否已经编译过。 +- test:测试,单元测试。 +- package:打包,将工程文件打包为指定的格式,例如JAR,WAR等(看你项目的pom文件,里面的packaging标签就是来指定打包类型的)。这个命令会在你的项目路径下一个target目录,并且拥有compile命令的功能进行编译,同时会在target目录下生成项目的jar/war文件。如果a项目依赖于b项目,打包b项目时,只会打包到b项目下target下,编译a项目时就会报错,因为找不到所依赖的b项目,说明a项目在本地仓库是没有找到它所依赖的b项目,这时就用到install命令。 +- verify:核实,主要是对package检查是否有效、符合标准。 +- install:安装,将包安装至本地仓库,以让其它项目依赖。该命令包含了package命令功能,不但会在项目路径下生成class文件和jar包,同时会在你的本地maven仓库生成jar文件,供其他项目使用(如果没有设置过maven本地仓库,一般在用户/.m2目录下。如果a项目依赖于b项目,那么install b项目时,会在本地仓库同时生成pom文件和jar文件,解决了上面打包package出错的问题)。 +- build:建造,功能类似compile,区别是对整个项目进行编译。与compile区别及特点:是对整个工程进行彻底的重新编译,而不管是否已经编译过。Build过程往往会生成发布包,这个具体要看对IDE的配置了,Build在实际中应用很少,因为开发时候基本上不用,发布生产时候一般都用ANT等工具来发布。Build因为要全部编译,还要执行打包等额外工 作,因此时间较长。 +- site:站点,生成项目的站点文档。 +- deploy:配置部署,复制到远程仓库。前提需要在工程 POM 和 Maven 里配置上相关的信息以及账号。 + +## 五、构建配置 + +```java + + 项目名称 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.0 + + 1.8 + 1.8 + UTF-8 + + + + org.apache.maven.plugins + maven-resources-plugin + 2.5 + + UTF-8 + + + + + org.codehaus.mojo + versions-maven-plugin + 2.7 + + + +``` + +
+ +
+ +这是一个基本的构建配置; +- maven-compiler-plugin 指定构建版本 +- maven-resources-plugin 构建资源 +- versions-maven-plugin 用于统一设定工程的版本工具【如图】 + +## 六、实战技巧 + +公司中会有很多的内部工程,开发时是怎么管理Jar的呢? +1. 如果是自己本地验证,那么你需要把你的N个工程,只要是需要被M工程引用的,就需要在N个工程里都操作 Install 操作,让Jar包进入到本地仓库。这样你的其他工程就可以引入了。 +2. 如果你的本地工程已经定义好接口,需要给其他人使用。那么你可以点击deploy部署。这个部署是把你的Jar发布到内部的私有仓库,只公司可见。这个操作之前需要在 POM 里配置发布部署的信息以及账号。 + diff --git a/docs/md/road-map/mock.md b/docs/md/road-map/mock.md new file mode 100644 index 000000000..fe0c2dcf2 --- /dev/null +++ b/docs/md/road-map/mock.md @@ -0,0 +1,310 @@ +--- +title: Mock +lock: need +--- + +# Mock 单元测试&插件生成测试代码 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,如何使用 [Mock](https://zh.wikipedia.org/wiki/%E6%A8%A1%E6%8B%9F%E5%AF%B9%E8%B1%A1) 进行工程的单元测试,以便于验证系统中的独立模块功能的健壮性。 + +从整个工程所处不同阶段的测试手段包括;单元测试、集成测试、系统测试、验收测试、性能测试、安全测试、回归测试,以及兼容、可靠、可用性测试。 + +而单元测试的重点在于,对工程开发中的代码,进行流程中的单元化测试。如一整个下单流程中,需要调用各项外部的接口(风控、账户、营销、试算、支付),才能完成整个下单流程。但在本地开发过程中,不太能将所有的外部接口都调试为开发环境可用状态,所有这个时候要做单元化测试,对于一些不能随时提供服务的接口进行 [Mock](https://zh.wikipedia.org/wiki/%E6%A8%A1%E6%8B%9F%E5%AF%B9%E8%B1%A1) 处理。 + +本文涉及的工程: + +- xfg-dev-tech-mock:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-mock](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-mock) +- chatglm-sdk-java:[https://gitcode.net/KnowledgePlanet/road-map/chatglm-sdk-java](https://gitcode.net/KnowledgePlanet/road-map/chatglm-sdk-java) + +## 一、案例背景 + +因为 Mock 单元测试的重点,主要体现在;功能流程较长、调用外部接口稳定性较差、测试过程中希望可以不启动 SpringBoot 应用就能对单个功能模块进行测试验证。 + +所以本章节带着这样一个案例背景的情况,小傅哥带着大家把 [《HTTP 框架使用和场景实战 - 结合ChatGLM自动回帖!》](https://bugstack.cn/md/road-map/http.html) 做一个小重构。来对 Mock 框架进行验证使用。 + +
+ +
+ +- 首先,这里使用 DDD 工程模型结构,搭建出测试工程。—— DDD 是一种软件设计方法,而软件的设计方法涵盖了;范式、模型、框架、方法论。所以通常下 MVC 与 DDD 的对比先从模型、框架在到思想设计和方法论。 +- 之后,我们在这样的一个模型结构下,实现出自动回帖的领域功能。而这个模型的实现恰好需要调用外部的接口和 ChatGLM SDK,这与我们要做的 Mock 测试正好符合,因为在大部分开发场景下,远程的 HTTP 调用可能不不会一直可用,所以可以用 Mock 方式进行模拟。 + +## 二、功能实现 + +### 1. 工程结构 + +
+ +
+ +- 在 domain 中实现一个zsxq的自动回帖领域,而它所需的要调用的接口则由基础设施层提供。 +- 另外在 app 中还有 ChatGLM SDK 的配置启动,也会被注入到 AiReply 实现类中。 + +### 2. Ai模块启动 + +```yml +# ChatGLM SDK Config +chatglm: + sdk: + config: + # 状态;true = 开启、false 关闭 + enabled: false + # 官网地址 + api-host: https://open.bigmodel.cn/ + # 官网申请 https://open.bigmodel.cn/usercenter/apikeys + api-secret-key: d570f7c5d289cdac2abdfdc562e39f3f.trqz1dH8ZK6ED7Pg +``` + +```java +@Bean +@ConditionalOnProperty(value = "chatglm.sdk.config.enabled", havingValue = "true", matchIfMissing = false) +public OpenAiSession openAiSession(ChatGLMSDKConfigProperties properties) { + // 1. 配置文件 + cn.bugstack.chatglm.session.Configuration configuration = new cn.bugstack.chatglm.session.Configuration(); + configuration.setApiHost(properties.getApiHost()); + configuration.setApiSecretKey(properties.getApiSecretKey()); + // 2. 会话工厂 + OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); + // 3. 开启会话 + return factory.openSession(); +} +``` + +- 所有的这些配置类的服务,都可以放到 app下的 config 模块中。 +- ChatGLM 可以直接在官网申请,默认会赠送18元的额度,对于它所提供的模型,还是非常够测试使用的。 + +### 3. 基础设置 - 接口调用 + +#### 3.1 接口 - 防腐对接 + +**源码**:`cn.bugstack.xfg.dev.tech.infrastructure.gateway.api.IZSXQApi` + +```java +public interface IZSXQApi { + + /** + * 查询知识星球帖子内容 + * + * @return 帖子数据 + * @throws IOException 异常 + */ + ResponseDTO topics() throws IOException; + + /** + * 回复帖子 + * + * @param topicId 帖子ID + * @param content 回复内容 + */ + void comment(long topicId, String content); + +} +``` + +- 在基础设置层中定义 gateway 网关接口调用,对于外部的接口使用,中间要做一层防腐,不要直接把外部的接口暴露出去使用。 + +#### 3.2 使用 - 依赖倒置 + +**源码**:`cn.bugstack.xfg.dev.tech.infrastructure.gateway.adapter.ZSXQAdapter` + +```java +public class ZSXQAdapter implements IZSXQAdapter { + + @Resource + private IZSXQApi zsxqApi; + + @Override + public List queryTopics() { + try { + ResponseDTO responseDTO = zsxqApi.topics(); + RespData respData = responseDTO.getRespData(); + List topics = respData.getTopics(); + List topicsItemVOList = new ArrayList<>(); + + for (TopicsItem topicsItem : topics) { + TopicsItemVO topicsItemVO = TopicsItemVO.builder() + .topicId(topicsItem.getTopicId()) + .talk(topicsItem.getTalk().getText()) + .showCommentsItems(topicsItem.getShowComments() != null ? topicsItem.getShowComments().stream() + .map(showCommentsItem -> { + TopicsItemVO.ShowCommentsItem item = new TopicsItemVO.ShowCommentsItem(); + item.setUserId(showCommentsItem.getOwner().getUserId()); + return item; + }) + .collect(Collectors.toList()) : new ArrayList<>()) + .build(); + + topicsItemVOList.add(topicsItemVO); + } + + return topicsItemVOList; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean comment(long topicId, String content) { + zsxqApi.comment(topicId, content); + return true; + } + +} +``` + +- 注意,TopicsItemVO 对象来自于 domain 下领域中模型下的 VO 对象。因为是依赖倒置的,所以 infrastructure 引用的是 domain 并对其接口做实现处理。 +- 并且,TopicsItemVO 只是需要获取自己需要的对象,还可以做简单的封装处理。这样可以衔接外部接口和内部逻辑中间的桥梁,不做强关联。 + +### 4. 任务调度 + +**源码**:`cn.bugstack.xfg.dev.tech.job.ReplyJob` + +```java +public class ReplyJob { + + @Resource + private IAiReply aiReply; + + @Scheduled(cron = "0/10 * * * * ?") + public void exec() throws Exception { + log.info("自动回帖任务开始执行..."); + aiReply.doAiReply(); + } + +} +``` + +- 现在在 trigger 触发器层中的 job 下,就可以调用我们已经实现好的 AiReply 自动回帖功能了。 +- 此外,注意 Application 中 `@EnableScheduling` 注解是开启的,否则任务不能执行。 + +## 三、系统测试 + +### 1. 集成测试 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApiTest { + + @Resource + private IAiReply aiReply; + + @Test + public void test_IAiReply() { + aiReply.doAiReply(); + } + +} +``` + +- 通常情况下这种测试是最多的,写多少功能,就直接测试调用。如功能中所用到的;HTTP接口、RPC接口、数据库、Redis等资源,都会需要使用到。有时候也因为这样,所以不好测试。那么单元测试就出现了。 + +### 2. 单元测试 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class MockTest { + + @Resource + private IAiReply aiReply; + + @MockBean + private IZSXQAdapter izsxqAdapter; + + @Test + public void test_doAiReply() throws InterruptedException, JsonProcessingException { + Mockito.when(izsxqAdapter.queryTopics()).thenReturn(new ArrayList() {{ + TopicsItemVO topicsItemVO = new TopicsItemVO(); + topicsItemVO.setTopicId(10001L); + topicsItemVO.setTalk(" 提问 java 冒泡排序"); + add(topicsItemVO); + }}); + + Mockito.when(izsxqAdapter.comment(Mockito.anyLong(), Mockito.anyString())).thenReturn(true); + + aiReply.doAiReply(); + + // 等待;ChatGLM 异步回复 + new CountDownLatch(1).await(); + } + +} +``` + +
+ +
+ +- 在基于使用 SpringBoot 的启动,以及一部分功能需要走真实调用的情况下,另外一部分功能的接口可能没法调用时。可以使用这样的一种 MockBean 的方式进行处理,并对整条链路上调用到的接口方法进行 Mock 处理。`Mockito.when(调用到的接口).thenReturn(返回的结果); +- 那么现在在测试方法中,做了2个Mock操作,把查询帖子和回复帖子,都给处理掉。也就是有了 Mock 以后,程序调用到这里,就直接走 Mock 里设置的结果信息了。 + +### 3. 功能测试 + +```java +@Slf4j +@RunWith(MockitoJUnitRunner.class) +public class ZSXQAdapterTest { + + @Mock + private IZSXQApi mockZsxqApi; + + @InjectMocks + private ZSXQAdapter zsxqAdapterUnderTest; + + @Test + public void testQueryTopics() throws Exception { + // Setup + final List expectedResult = Arrays.asList(TopicsItemVO.builder() + .topicId(0L) + .talk("talk") + .showCommentsItems(Arrays.asList(TopicsItemVO.ShowCommentsItem.builder() + .userId(0L) + .build())) + .build()); + + // Configure IZSXQApi.topics(...). + final ResponseDTO responseDTO = new ResponseDTO(); + final RespData respData = new RespData(); + final TopicsItem topicsItem = new TopicsItem(); + final ShowCommentsItem showCommentsItem = new ShowCommentsItem(); + final Owner owner = new Owner(); + owner.setUserId(0L); + showCommentsItem.setOwner(owner); + topicsItem.setShowComments(Arrays.asList(showCommentsItem)); + final Talk talk = new Talk(); + talk.setText("talk"); + topicsItem.setTalk(talk); + topicsItem.setTopicId(0L); + respData.setTopics(Arrays.asList(topicsItem)); + responseDTO.setRespData(respData); + when(mockZsxqApi.topics()).thenReturn(responseDTO); + + // Run the test + final List result = zsxqAdapterUnderTest.queryTopics(); + + // Verify the results + assertEquals(expectedResult, result); + + log.info("测试结果:{}", JSON.toJSONString(result)); + } + +} +``` + +
+ +
+ +- 除了前面两种测试,我们在开发功能的时候,还有场景测试;不启动 SpringBoot 但希望对实现的功能进行测试。 +- 那么这里所体现的就是这样的测试,主要使用;`@RunWith(MockitoJUnitRunner.class)`、`@Mock`、`@InjectMocks` 相当于模拟了一个启动的过程,只不过都是 Mock 的信息。但你可以根据这些信息来调试你的接口。 +- 提示:你可以安装 `IDEA Plugin Squaretest` 它能自动的帮你生成Mock单元测试。`这个插件是收费的,但还好不贵。` diff --git a/docs/md/road-map/mvc.md b/docs/md/road-map/mvc.md new file mode 100644 index 000000000..8f3d6027e --- /dev/null +++ b/docs/md/road-map/mvc.md @@ -0,0 +1,140 @@ +--- +title: MVC 架构设计 +lock: need +--- + +# 架构的本质之 MVC 架构 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +如果我们尝试把编程的复杂架构缩小到最容易理解的程度,那么编程开发其实只做3件事:”`定义属性`、`创建方法`、`调用展示`“。但因为同类所需的内容较多,如一系列的属性,一堆的方法实现,一组的接口封装,那么就需要合理的把这些内容分配到不同的层次中去实现,因此有了分层架构的设计。 + +那么本文小傅哥会向大家介绍一套MVC架构的分层设计以及如何创建使用,并提供相应的简单的案例。你可以复制这套架构在自己的场景中使用,也更能方便编程的小白可以更快的上手开发。 + +**注意**:此套MVC架构模型适合提供HTTP服务的工程架构,适合简单的小场景开发使用。特点;轻便、简单、学习成本低。 + +## 一、编程三步 + +如果说你是一个特别小的`玩具项目`,你甚至可以把编程的3步写到一个类里。但因为你做的是正经项目,你的各种类;对象类、库表类、方法类,就会成群结队的来。如果你想把这些成群结队的类的内容,都写到一个类里去,那么就是几万行的代码了。—— 当然你也可以吹牛逼,你一个人做过一个项目,这项目大到啥程度呢。就是有一个类里有上万行代码。 + +
+ +
+ +所以,为了不至于让一个类撑到爆💥,需要把黄色的对象、绿色的方法、红色的接口,都分配到不同的包结构下。这就是你编码人生中所接触到的第一个解耦操作。 + +## 二、分层框架 + +MVC 是一种非常常见且常用的分层架构,主要包括;M - mode 对象层,封装到 domain 里。V - view 展示层,但因为目前都是前后端分离的项目,几乎不会在后端项目里写 JSP 文件了。C - Controller 控制层,对外提供接口实现类。DAO 算是单独拿出来用户处理数据库操作的层。 + +
+ +
+ +- 如图,在 MVC 的分层架构下。我们编程3步的所需各类对象、方法、接口,都分配到 MVC 的各个层次中去。 +- 因为这样分层以后,就可以很清晰明了的知道各个层都在做什么内容,也更加方便后续的维护和迭代。 +- 对于一个真正的项目来说,是没有一锤子买卖的,最开始的开发远不是成本所在。最大的开发成本是后期的维护和迭代。而架构设计的意义更多的就是在解决系统的反复的维护和迭代时,如何降低成本,这也是架构分层的意义所在。 + + +## 三、调用流程 + +接下来我们再看下一套 MVC 架构中各个模块在调用时的串联关系; + +
+ +
+ +- 以用户发起 HTTP 请求开始,Controller 在接收到请求后,调用由 Spring 注入到类里的 Service 方法,进入 Service 方法后有些逻辑会走数据库,有些逻辑是直接内部自己处理后就直接返回给 Controller 了。最后由 Controller 封装结果返回给 HTTP 响应。 +- 同时我们也可以看到各个对象在这些请求间的一个作用,如;请求对象、库表对象、返回对象。 + +## 四、架构源码 + +### 1. 环境 + +- JDK 1.8 +- Maven 3.8.6 - 下载安装maven后,本地记得配置阿里云镜像,方便快速拉取jar包。源码中 `docs/maven/settings.xml` 有阿里云镜像地址。 +- SpringBoot 2.7.2 +- MySQL 5.7 - 如果你使用 8.0 记得更改 pom.xml 中的 mysql 引用 + +### 2. 架构 + +- **源码**:[`https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-mvc`](https://gitcode.net/KnowledgePlanet/road-map/xfg-frame-mvc) +- **树形**:`安装 brew install tree` `IntelliJ IDEA Terminal 使用 tree` + +```java +. +├── docs +│   └── mvc.drawio - 架构文档 +├── pom.xml +├── src +│   ├── main +│   │   ├── java +│   │   │   └── cn +│   │   │   └── bugstack +│   │   │   └── xfg +│   │   │   └── frame +│   │   │   ├── Application.java +│   │   │   ├── common +│   │   │   │   ├── Constants.java +│   │   │   │   └── Result.java +│   │   │   ├── controller +│   │   │   │   └── UserController.java +│   │   │   ├── dao +│   │   │   │   └── IUserDao.java +│   │   │   ├── domain +│   │   │   │   ├── po +│   │   │   │   │   └── User.java +│   │   │   │   ├── req +│   │   │   │   │   └── UserReq.java +│   │   │   │   ├── res +│   │   │   │   │   └── UserRes.java +│   │   │   │   └── vo +│   │   │   │   └── UserInfo.java +│   │   │   └── service +│   │   │   ├── IUserService.java +│   │   │   └── impl +│   │   │   └── UserServiceImpl.java +│   │   └── resources +│   │   ├── application.yml +│   │   └── mybatis +│   │   ├── config +│   │   │   └── mybatis-config.xml +│   │   └── mapper +│   │   └── User_Mapper.xml +│   └── test +│   └── java +│   └── cn +│   └── bugstack +│   └── xfg +│   └── frame +│   └── test +│   └── ApiTest.java +└── road-map.sql +``` + +以上是整个🏭工程架构的 tree 树形图。整个工程由 SpringBoot 驱动。 +- Application.java 是启动程序的 SpringBoot 应用 +- common 是额外添加的一个层,用于定义通用的类 +- controller 控制层,提供接口实现。 +- dao 数据库操作层 +- domain 对象定义层 +- service 服务实现层 + +## 五、测试验证 + +- 首先;整个工程由 SpringBoot 驱动,提供了 road-map.sql 测试 SQL 库表语句。你可以在自己的本地mysql上进行执行。它会创建库表。 +- 之后;在 application.yml 配置数据库链接信息。 +- 之后就可以打开 ApiTest 进行测试了。你可以点击 Application 类的绿色箭头启动工程,使用 UserController 类提供接口的方式调用程序;http://localhost:8089/queryUserInfo + +
+ +
+- 如果你正常获取了这样的结果信息,那么说明你已经启动成功。接下来就可以对照着MVC的结构进行学习,以及使用这样的工程结构开发自己的项目。 + + + diff --git a/docs/md/road-map/mvc2ddd.md b/docs/md/road-map/mvc2ddd.md new file mode 100644 index 000000000..c938986fc --- /dev/null +++ b/docs/md/road-map/mvc2ddd.md @@ -0,0 +1,285 @@ +--- +title: MVC2DDD - 架构重构 +lock: need +--- + +# MVC2DDD - 架构重构 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
项目:[大营销平台系统](https://bugstack.cn/md/project/big-market/ddd.html) - DDD 领域驱动设计,战略、战术、战役,落地指引规范。 + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。**MVC讲解了,DDD讲解了。接下来这个章节,我们讲讲从MVC到DDD的重构!** + +为了避免概念的混淆和下文内容方便讲解,先进行DDD概念认知确认: + +1. [Domain-driven design (DDD) is a major software design approach.](https://en.wikipedia.org/wiki/Domain-driven_design) 来自维基百科。DDD 是一种软件设计方法。 +2. 软件的设计方法涵盖了:范式、模型、框架、方法论,主要活动包括建模、测试、工程、开发、部署、维护。来自维基百科的[软件设计](https://en.wikipedia.org/wiki/Software_design)涵盖信息介绍。 +3. 所以在当前语境下,MVC与DDD的对比,只是对比软件落地的框架结构,并不是软件设计思想和思维建模的全方面对比。所以如果读者再给一些其他伙伴阐述MVC和DDD的对比时,也是有必要说明语境的,避免没必要的纠结于理论,而忽略了交流讨论的意义。 + +那么,接下来在此语境下,我们进行MVC和DDD的重构讲解: + +**MVC 旧工程腐化严重,迭代成本太高。DDD 新工程全部重构,步子扯的太大。** 这是现阶段在工程体系化治理中,我们所面临的最大问题:`既想运用 DDD 的思想循序渐进重构现有工程,又想不破坏原有的工程体系结构以保持新需求的承接效率。` + +
+ +
+ +经过实践得知,DDD 架构能解决,现阶段 MVC 贫血结构中所遇到的众多问题。 + +众所周知,MVC 分层结构是一种贫血模型设计,它将”状态“和”行为“分离到不同的包结构中进行开发使用。domain 里写 po、vo、enum 对象,service 里写功能逻辑实现。也正因为 MVC 结构没有太多的约束,让前期的交付速度非常快。但随着系统工程的长期迭代,贫血对象开始被众多 serivice 交叉使用,而 service 服务也是相互调用。这样缺少一个上下文关系的开发方式,让长期迭代的 MVC 工程逐步腐化到严重腐化。 + +**MVC 工程的腐化根本**,就在于对象、服务、组件的交叉混乱使用。时间越长,腐化的越严重。 + +
+ +
+ +在 MVC 的分层结构就像家里所有人的衣服放一个大衣柜、所有人的裤子放一个大库柜。衣服裤子(对象),很少的时候很节省空间,**因为你的裤子别人可能也拿去穿,复用一下开发速度很快**。但时间一长,就越来越乱了。🤨 一条裤子被加肥加大,所有人都穿。 + +而 DDD 架构的模型分层,则是以人为视角,**一个人就是一个领域,一个领域内包括他所需的衣服、裤子、袜子、鞋子**。虽然刚开始有点浪费空间,但随着软件的长周期发展,后续的维护成本就会降低。 + +那么,接下来我们就着重看一下,从 MVC 到 DDD 的轻量化重构应该怎么做。🍻 + +>文章后面,含有 MVC 到 DDD 重构编码实践讲解。此文也是 MVC、DDD 的架构编码指导经验说明。 + +## 一、能学到啥 + +本文是偏实战可落地的 DDD 知识分享,也是从 MVC 到 DDD 的可落地方案讲解。在本文中会介绍 DDD 架构下的分层结构、调用全景图以及非常重要的 MVC 到 DDD 应该如何映射和编码。所以如下这一系列内容都是你能获得的知识: + +1. DDD 领域驱动设计,对应的分层结构讲解。涵盖调用关系、依赖关系、对象转换以及各层的功能划分。—— 简单且清晰。 +2. DDD 调用全景图,以一张全方位的结构关系调用视图,展开 DDD 的血脉流转关系。有了这一张视图,你会更加清楚的知道 DDD 的调用链路结构和各个代码都要写到那一层。 +3. MVC 映射 DDD 后的调整方案,在尽可能低的成本下,让 MVC 结构具备 DDD 领域驱动设计的实现思想。这样的调整,可以在一定程度上,阻止旧工程的腐化程度,提高编码质量。同时也为后续从 MVC 到 DDD 的迁移,做好基础。 +4. MVC、DDD 是工程设计骨架,设计原则、设计模式是工程实现血肉。所以设计模式也是本文要展示的重点内容。 +5. 一整套实战开源课程;讲解在 DDD 架构中,各项技术栈:Dubbo、MQ、Redis、Zookeeper - 配置中心等的分层使用。—— 否则你可能都不知道一个 MQ 消息发送要放在哪里。有了 DDD 分层架构,这些东西会被归类的特别清晰。 + +此外,除了这些碎片化的知识学习,还有应用级实战项目锻炼:Lottery DDD 架构设计、ChatGPT 新DDD架构设计、API网关 会话设计 - 学习架构能力和编程思维,以及高端的编码技巧。 + +## 二、架构分层(DDD) + +在 DDD 架构分层中,domain 模块最重要的,也是最大的那个。所有的其他模块都要围着它转。所有 domain 下的各个领域模块,都包含着一组完整的:model - 模型对象、service - 服务处理,以及在有需要操作数据库时,再引入对应的 IRepository - 仓储服务。这个 domain 的实现,就像是实现了一个炸药包,炸药包的火药、引线、包布等都是一个个物料被封装到一起使用。 + +如下是 DDD 架构所呈现出的一种四层架构分层,可能和一些其他的 DDD 分层略有差异,但核心的重点结构是不变的。尤其是 domain 领域、infrastructure 基础,是任何一个 DDD 架构分层都需要有的分层模块。 + +
+ +
+ +- **应用封装 - app**:这是应用启动和配置的一层,如一些 aop 切面或者 config 配置,以及打包镜像都是在这一层处理。你可以把它理解为专门为了启动服务而存在的。 +- **接口定义 - api**:因为微服务中引用的 RPC 需要对外提供接口的描述信息,也就是调用方在使用的时候,需要引入 Jar 包,让调用方好能依赖接口的定义做代理。 +- **领域封装 - trigger**:触发器层,一般也被叫做 adapter 适配器层。用于提供接口实现、消息接收、任务执行等。所以对于这样的操作,这里把它叫做触发器层。 +- **领域编排【可选】 - case**:领域编排层,一般对于较大且复杂的的项目,为了更好的防腐和提供通用的服务,一般会添加 case/application 层,用于对 domain 领域的逻辑进行封装组合处理。但对于一些小项目来说,完全可以去掉这一层。少量一层对象转换,代码的维护成本会降低很多。 +- **领域封装 - domain**:领域模型服务,是一个非常重要的模块。无论怎么做DDD的分层架构,domain 都是肯定存在的。在一层中会有一个个细分的领域服务,在每个服务包中会有【模型、仓库、服务】这样3部分。 +- **仓储服务 - infrastructure**:基础层依赖于 domain 领域层,因为在 domain 层定义了仓储接口需要在基础层实现。这是依赖倒置的一种设计方式。所有的仓储、接口、事件消息,都可以通过依赖倒置的方式进行调用。 +- **外部接口 - gateway**:对于外部接口的调用,也可以从基础设施层分离一个专门的 gateway 网关层,来封装外部 RPC/HTTP 等类型接口的调用。 +- **类型定义 - types**:通用类型定义层,在我们的系统开发中,会有很多类型的定义,包括:基本的 Response、Constants 和枚举。它会被其他的层进行引用使用。(这一层没有画到图中) + +综上就是 DDD 架构思想下的工程分层模型结构,DDD 架构的领域驱动设计的重点包括:结构边界更加清晰、重视上下文调用、分离业务功能与基础支撑。总之一句话,就是各司其职。那么鉴于如此清晰工程结构,该如何将旧存工程,MVC 转向 DDD 呢?接下来就重点介绍下。 + +## 三、工程重构(MVC->DDD) + +经过实践验证,不需要太高成本,MVC 就可以天然的向 DDD 工程分层的模型结构转变。重点是不改变原有的工程模块的依赖关系,将贫血的 domain 对象层,设计为充血的结构。**对于 domain 原本在 MVC 分层结构中,就是一个被依赖层,恰好可以与其他层做依赖倒置的设计方案处理**。具体如图所示: + +
+ +
+ +左侧是我们常见的 MVC 分层结构,右侧是给大家上文讲解过的 DDD 分层结构。从 MVC 到 DDD 的映射,使用了相同颜色进行标注。之后我来介绍一些细节: + +在 MVC 分层结构中,所有的逻辑都集中在 service 层,也是文中提到的腐化最严重的层,要治理的也是这一层。所以首先我们要将 service 里的功能进行拆解。 + +1. service 中具备领域特性的服务实现,抽离到原本贫血模型的 domain 中。在 domain 分层中添加 xxx、yyy、zzz 分层领域包,分别实现不同功能。**注意每个分层领域包内都具备完整的 DDD 领域服务内所需的模块** +2. service 中的基础功能组件,如:缓存Redis、配置中心等,迁移到 dao 层。这里我们把 dao 层看做为基础设施层。它与 domain 领域层的调用关系,为依赖倒置。也就是 domain 层定义接口,dao 层依赖于 domain 定义的接口,做依赖倒置实现接口。 +3. service 本身最后被当做 application/case 层,来调用 domain 层做服务的编排处理。 + +因为恰好,MVC 分层结构中,也是 service 和 dao 依赖于 domain,这和 DDD 分层结构是一致的。所以经过这样的映射拆分代码实现调用结构后,并不会让工程结构发生变化。那么只要工程结构不发生变化,我们的改造成本就只剩下代码编写风格和旧代码迁移成本。 + +MVC 分层结构中的 export 层是 RPC 接口定义层,由 web 层实现。web 是对 service 的调用。也就是 DDD 分层结构中调用 application 编排好的服务。这部分无需改动。**但如果你原有工程把 domain 也暴露出去了,则需要把对应的包迁移到 export** 因为 domain 包有太多的核心对象和属性,还包括数据库持久化对象。这些都不应该被暴露。 + +MVC 分层中,因为有需要对外部 RPC 接口的调用,所以会单独有一层 RPC 来封装其他服务的接口。这一层被 domain 领域层使用,可以定义 adapter 适配器接口,通过依赖倒置,在 rpc 层实现 domain 层定义的调用接口。 + +此外 dao 层,在 MVC 结构中原本是比较单一的。但经过改造后会需要把基础的 Redis 使用、配置中使用,都迁移到 dao 层。因为原本在 service 层的话,domain 层是调用不到的这些基础服务的,而且也不符合服务功能边界的划分。 + +**综上**,就是从 MVC 到 DDD 重构架构的拆解实现方案。这是一种最低成本的最佳实施策略,完全可以保证 MVC 的结构,又可以应用上 DDD 的架构分层优势。也能运用 DDD 领域驱动设计思想,重构旧代码,增加可维护性。 + +到这里,分层结构问题我们说清楚了。从 MVC 调整结构到 DDD 后,工程模型中的调用链路关系是什么样呢?接下来我们再展开架构,看细节关系。 + +## 四、分层调用链路 + +接下来我们把 DDD 的分层架构平铺展开,看看从一个接口的实现到各个模块分层中的调用链路关系是什么样的。这样在做自己的代码开发中也可以参考到应该把什么的功能分配到哪个模块中处理。 + +
+ +
+ +从APP层、触发器层、应用层,这三块主要对领域层的上下文逻辑封装、触发式(MQ、HTTP、JOB)使用,并最终在应用层中打包发布上线。这一部分的都是使用的处理,所以也不会有太复杂的操作。 + +当进入领域层开始,也是智力集中体现的开始了。所有你对工程的抽象能力,都在这一块区域体现。 + +接下来我们着重介绍下领域层和基础层的模块职责功能;**图中下方是对象的流转,可以注意下。** + +### 1. 领域服务层 + +我们可以当 domain 领域层为一个充血模型结构,在一个 domain 领域层中,可以有多个领域包。当然理想状态下,如果你的 DDD 拆分的特别干净的新工程,那么可能一个 domain 就一个领域。但大部分时候微服务的拆分鉴于成本考虑不会那么细,还有一些老工程的重构,都是一个工程内有多个领域,对应的解决方案是在一个工程下建多个同级分层包。比如:账户领域包、授信领域包、结算领域包等,每个包内聚合实现不同的功能。 + +每一个 domain 下的领域包内,都包括:model 模型、仓储、接口、事件和服务的处理。 + +model 模型对象 + +- aggreate:聚合对象,实体对象、值对象的协同组织,就是聚合对象。 +- entity:实体对象,大多数情况下,实体对象(Entity)与数据库持久化对象(PO)是1v1的关系,但也有为了封装一些属性信息,会出现1vn的关系。 +- valobj:值对象,通过对象属性值来识别的对象 By 《实现领域驱动设计》 + +repository 仓储服务:从数据库等数据源中获取数据,传递的对象可以是聚合对象、实体对象,返回的结果可以是:实体对象、值对象。因为仓储服务是由基础层(infrastructure) 引用领域层(domain),是一种依赖倒置的结构,但它可以天然的隔离PO数据库持久化对象被引用。 + +adapter 接口服务:是依赖于外包的其他 HTTP/RPC 接口的封装调用,通过在 domain 领域层定义适配器接口,再由依赖于 domain 的基础层设施层或者一个单独的专门处理接口的额外分层,来实现 domain 定义的适配器接口,完成对依赖的 HTTP/RPC 进行封装处理。 + +event 事件消息:在服务实现中,经常会有业务完成后,对外发送消息的情况。这个时候,可以在领域模型中定义事件消息的接口,再由基础设施层完成消息的推送。 + +service 服务设计:这里要注意,不要以为定义了聚合对象,就把超越1个对象以外的逻辑,都封装到聚合中,这会让你的代码后期越来越难维护。聚合更应该注重的是和本对象相关的单一简单封装场景,而把一些重核心业务放到 service 里实现。**此外,如果你的设计模式应用不佳,那么无论是领域驱动设计、测试驱动设计还是换了三层和四层架构,你的工程质量依然会非常差。** + +### 2. 基础设施层 + +`提供数据库持久化`、`提供Redis和配置中心数据支撑`、`提供事件消息推送`、`提供外部服务接口封装`。总之这一层的核心目的就是更好的辅助 domain 领域层完成领域功能的开发。 + +而调用方式则为依赖倒置,也就是`领域服务层`定义接口,`基础设施层`做功能实现。这样可以有效的避免基础设施层中的对象被对外暴露,如数据库持久化对象,在这样的分层结构中,天然的被保护在基础设施层中,外部是没法引入的,否则就循环依赖了。 + +有了这一层以后,domain 层不会关心数据的细节处理。传递给基础设施层的方法中,会把聚合对象或实体对象通过接口方法传递下来。之后在基础设施层中完成数据事务的操作。也会含有事务处理后,写入Redis缓存和发送MQ消息。如果说有跨领域的事务,一般可能就是跨库表,这个时候要使用 MQ 事件的方式进行驱动。 + +### 3. 类型对象层 + +这一层就比较简单了,只是一些通用的出入参对象 Response,还有枚举对象、异常对象等。供给于对外的接口层使用。但如果是 RPC 这样的接口,建议同 RPC 对外提供的接口描述包中提供,因为对外只提供1个轻量化的包且不依赖于任何其他包,是最好维护管理的。 + +## 五、只是换了别墅 + +从 MVC 到 DDD,我们有一点是必须清楚的认知的。 + +
+ +
+从 MVC 到 DDD 我们只是换了一个更大、格局更清晰的房子🏡,但并不能决定你从 MVC 到 DDD 代码就变得非常干净、漂亮、整洁了。因为从 MVC 到 DDD 只是骨架变了,但骨架之下的血肉并没有改变。 + +如果你仍是把原有的烂代码平移到新的分层架构中,就相当于把老房子里的破旧家具衣物鞋帽搬过来而已。所以依照于软件设计的原则:分治、抽象和知识,但知识是设计原则和设计模式的运用。所以要想把代码写好,就一定是要把`DDD + 设计模式`,才能真的把代码写好。接下来,小傅哥再给大家举个使用模式在 DDD 分层结构中重构的案例。 + +## 六、重构现有代码 + +软件设计第一原则,康威定律所提到的,分治、抽象和知识,是用于系统设计和实现的指导说明。分治和抽象,我们可以用 DDD 思想映射的分层架构来处理,但知识则是设计原则和设计模式的运用。 + +所以,如果没有合理的运用设计知识来对代码进行细化处理,那么即使拆分出流程边界再清晰的架构,也很难做出好维护的代码。而通常最常用的设计模式,无外乎:工厂、策略、模板的组合使用,少部分会用到责任链、建造者、组合模式。那么接下来,再分享一个带有流程的设计模式使用,让大家可以有一份可参考的工程代码设计。 + +### 1. 场景设定 + +这里我们做一个提额场景的设定。估计大家都用过信用卡💳,它有一个初始的额度,在后续的使用中会随着信用的积累和消费的增加,进行提高额度。而额度的提高则需要一系列的校验判断并最终做出提额处理。流程如下: + +
+ +
+ +这样的流程图,是我们做业务开发的小伙伴,经常看到的。做一系列的流程判断处理,之后完成一个具体的功能。简单来说,就是 if···else 写代码,一条条的校验。但写着写着,时间一长就会发现代码变得特别混乱。最主要的原因就是,那些为了支撑完成业务的各类判断是不稳定因素,会随着业务的变化不断的调整。甚至有时候就直接下掉了。但你的代码中就多了一条 `// 业务说暂时不使用,你也不敢删!`就像有首歌唱的🎤:**“需求依旧停在旷野上,你的代码被越拉越长。直到远去的马蹄声响,呼唤你的Bug传四方。”** + +所以对于这样的功能流程设计,怎么办呢?总不能让旷野的马蹄,一直拉着你的bug在奔袭。 + +### 2. 代码现状 + +`一个接口一个实现,一个实现代码一片。` +`一片一片,又一片,代码行数,两三千。` + +
+ +
+ + +大部分我们在 MVC 工程分层结构下,参与开发的代码,基本都是定义一个接口,就写一片功能实现。功能实现中,如果看到有现成的接口,直接拿来复用。所有的实现并不会基于接口、抽象、模板等进行,所以最终这样的代码腐化的非常严重。 + +### 3. 重新分层 + +重构前,先说明下新的分层处理;如图 + +
+ +
+ +- 首先,在原有的 domain 贫血模型中,添加一个对应的领域包。credit 你可以是自己的其他的领域包。之后的 domain 则为充血模型设计。 +- 之后,在领域包内实现自己的业务逻辑,注意这里需要用到设计模式来实现。代码实现中需要用到的数据查询、缓存使用、接口调用,全部采用依赖倒置的方式让基础层/接口层,来提供具体的实现逻辑。而 domain 层只是定义接口和使用 Spring 的注入进行使用。 + + +### 4. 重构代码 + +抽象类,是一个非常好用的类。一种是可以定义出流程结构,让代码变得清晰干净。再有一种是定义共用方法,让其他实现类可复用。 + +那么这里,我们就使用抽象类定义模板 + 策略和工厂实现的规则引擎处理频繁变动的校验类流程,完成代码开发。如图我们先设计下代码的实现结构。 + +
+ +
+ + +- 首先,定义一个受理调额的接口。因为额度的调整,包括:提额、降额。所以不要把名字写的太死。 +- 之后,由抽象类实现接口。在抽象类中定义出整个调用链路关系,并把一些公用的数据类支撑逻辑,提到支撑类里。这和 Spring 的设计很像。 +- 之后,因为规则校验这东西是为了支撑核心流程走下去的,而且还是随着业务频繁变动的。那就没必要在主线业务流程中,用 if···else 贴膏药的写代码,而是应该拆解出来。所以这里设计一个策略模式实现的规则校验,并通过工厂对外提供服务。 +- 最后,这些零件类的东西都处理好后。就可以在抽象类的子类实现中进行调用处理了。 + +### 5. 代码呈现 + +经过设计模式的重构处理,现在的代码就以如下形式体现了。—— 拆解出来的伪代码,具体可以参考过往的一些设计模式运用。 + +```java +public AdjustAssetOrderEntity acceptAdjustAssetApply(AdjustAssetApplyEntity adjustAssetApplyEntity) { + // 1. 参数校验 + this.parameterVerification(adjustAssetApplyEntity); + + // 2. 查询申请单数据,如已经存在则直接返回 + AdjustAssetOrderEntity orderEntity = queryAssetLog(adjustAssetApplyEntity.getPin(), adjustAssetApplyEntity.getAccountType(), adjustAssetApplyEntity.getTaskNo(), adjustAssetApplyEntity.getAdjustType()); + if (null != orderEntity) { + log.info("pin={} taskNo={} 受理申请,检索到任务存在进行中的申请单。", adjustAssetApplyEntity.getUserId(), adjustAssetApplyEntity.getTaskNo()); + return orderEntity; + } + + // 3. 以下流程放到分布式锁内处理【避免相同请求二次进入】 + String lockId = genLockId(adjustAssetApplyEntity.getAdjustType(), adjustAssetApplyEntity.getUserId()); + try { + // 3.1 分布式锁:加锁 + long state = lock(lockId); + if (0 == state) { + throw new AccountRuntimeException(BizResultCodeEm.DISTRIBUTED_LOCK_EXCEPTION.getCode(), "分布式锁异常,当前用户行为处理中。"); + } + + // 3.2 账户查询 + UserAccountInfoDTO userAccountInfoDTO = queryJtAccount(adjustAssetApplyEntity.getUserId(), adjustAssetApplyEntity.getAccountType()); + + // 3.3 基础校验:(1)账户类型、(2)状态状态、(3)额度类型、(4)账户逾期、(5)费率类型【暂无】 + LogicCheckResultEntity logicCheckResultEntity = doCheckLogic(adjustAssetApplyEntity, userAccountInfoDTO, + DefaultLogicFactory.LogicModel.ACCOUNT_TYPE_FILTER.getCode(), + DefaultLogicFactory.LogicModel.ACCOUNT_STATUS_FILTER.getCode(), + DefaultLogicFactory.LogicModel.ACCOUNT_QUOTA_FILTER.getCode(), + DefaultLogicFactory.LogicModel.ACCOUNT_OVERDUE_FILTER.getCode() + ); + + if (!AssetCycleQuotaAlterCodeEnum.E0000.getCode().equals(logicCheckResultEntity.getCode())) { + log.info("userId={} taskNo={} 规则校验过滤拦截。code:{} info:{}", adjustAssetApplyEntity.getUserId(), adjustAssetApplyEntity.getTaskNo(), logicCheckResultEntity.getCode(), logicCheckResultEntity.getInfo()); + throw new AccountRuntimeException(logicCheckResultEntity.getCode(), logicCheckResultEntity.getInfo()); + } + + // 3.4 受理调额 + return this.acceptAsset(adjustAssetApplyEntity, userAccountInfoDTO); + } finally { + // 3.1 分布式锁:解锁 + this.unlock(lockId); + } +} +``` + +这样的处理后,代码就变得非常清晰了。 + +1. 先是做基础的校验和数据的查询判断,之后加锁避免一个人超时申请。而后,进行规则引擎的调用和处理,根据不同的诉求,开发不同的规则,并配置的方式进行使用。 +2. 最后所有的这些东西处理完成后,就是做最终的调额处理了。 + +## 七、编程经验 + +- 重构,是一直都在发生的事情,不能积累到最后才重构。那只有重做的可能。 +- 工厂、模板、策略,这3个设计模式,就可以解决80%的场景问题。 +- 小傅哥的编码标准也会成为伙伴参考的案例,所以小傅哥会更严格要求自己的标准。 diff --git a/docs/md/road-map/mybatis.md b/docs/md/road-map/mybatis.md new file mode 100644 index 000000000..37ec011c2 --- /dev/null +++ b/docs/md/road-map/mybatis.md @@ -0,0 +1,488 @@ +--- +title: MyBatis +lock: need +--- + +# MyBatis 使用教程和插件开发 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,使用 SpringBoot 配置 MyBatis 并完成对插入、批量插入、修改、查询以及注解事务和编程事务的使用,通过扩展插件开发对指定字段进行加解处理。 + +此外本文也通过此案例,渗透讲解 DDD 模型中的聚合对象、实体对象和值对象在领域模型中的实践。 + +本文涉及的工程: + +- xfg-dev-tech-mybatis:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-mybatis](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-mybatis) +- 导入测试库表:[road-map.sql](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-mybatis/-/blob/master/road-map.sql) + +## 一、案例背景 + +`说一千道一万,给小卡拉米写的教程,得简单还好看!` + +为了更好的把 MyBatis 常用的各项功能体现的清晰明了,小傅哥这里设定了公司雇员和对应薪酬关系的一个开发场景。 + +
+ +
+ +- 首先,雇员员工和对应的薪资待遇,是一个1v1的关系。 +- 之后,薪资表与调薪表,是一个1vn的关系。每次晋升、普调,都会有一条对应的调薪记录。 +- 最后,有了这样3个表,我们就可以很好的完成,员工的插入、批量插入,和事务操作调薪。 + +## 二、领域模型 + +🌶 **模型定义**:[https://bugstack.cn/md/road-map/ddd.html](https://bugstack.cn/md/road-map/ddd.html) - 你可以先参考小傅哥的 [DDD](https://bugstack.cn/md/road-map/ddd.html) 篇,这样可以更好的理解模型概念和设计原则。 + +
+ +
+ +
+ +
+ +此场景的业务用于对指定的用户进行**晋升加薪调幅**,但因为加薪会需要操作3个表,包括:雇员表 - 修改个人Title、薪资表 - 修改薪酬、调薪记录表 - 每一次加薪都写一条记录。 + +### 1. model + +#### 1.1 值对象 + +```java +public enum EmployeePostVO { + + T1("T-1", "初级工程师"), + T2("T-2", "初级工程师"), + T3("T-3", "中级工程师"), + T4("T-4", "中级工程师"), + T5("T-5", "高级工程师"), + T6("T-6", "高级工程师"), + T7("T-7", "架构师"), + T8("T-8", "架构师"); + + private final String code; + private final String desc; + + // 省略部分 + +} +``` + +- 当一个实体对象中的一个值,是有多个范围时候,则需要定义出值对象。由于此类的值对象更贴近于当前的场景业务,所以一般不会被定义为共用的枚举。如此此类值范围,都会被定义为值对象。 + +#### 1.2 实体对象 + +```java +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EmployeeEntity { + + /** 雇员级别 */ + private EmployeePostVO employeeLevel; + /** 雇员岗位Title */ + private EmployeePostVO employeeTitle; + +} + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class EmployeeSalaryAdjustEntity { + + /** 总额调薪 */ + private BigDecimal adjustTotalAmount; + /** 基础调薪 */ + private BigDecimal adjustBaseAmount; + /** 绩效调薪 */ + private BigDecimal adjustMeritAmount; + +} +``` + +- 实体对象是对数据库对象的抽象,大多数时候是 1:1 的关系结构,在一些复杂的模型场景中会是1:n的结构。 + +#### 1.3 聚合对象 + +```java +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AdjustSalaryApplyOrderAggregate { + + /** 雇员编号 */ + private String employeeNumber; + /** 调薪单号 */ + private String orderId; + /** 雇员实体 */ + private EmployeeEntity employeeEntity; + /** 雇员调薪实体 */ + private EmployeeSalaryAdjustEntity employeeSalaryAdjustEntity; + +} +``` + +- 聚合对象是对实体对象和值对象的封装,代表着一类业务的聚合。通常是作为 service 服务层中入参出现。 + +### 2. repository + +```java +public interface ISalaryAdjustRepository { + + String adjustSalary(AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate); + +} +``` + +- 仓储在 DDD 中的设计,是一种依赖倒置关系,由 domain 定义接口,之后由引入 domain 包的基层层 infrastructure 实现功能。 +- 此外,因为是依赖倒置,所以天然的隔离了 PO 数据库持久化对象,不会被对外使用。这个设计是非常巧妙的。当我们从结构上定义了原则,就不会有人乱引用对象了。 + +### 3. service + +```java +public interface ISalaryAdjustApplyService { + + String execSalaryAdjust(AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate); + +} +``` + +- 当前场景简单,所以不需要额外的设计模式使用。但如果是复杂场景,必须考虑设计模式,否则代码都写到 SalaryAdjustApplyService 实现类里,那么将非常难维护。 +- 不要只是把聚合对象当充血模型,你的充血结构是整个 domain 下的每一个领域包,也就是让这里的状态与行为看做为一整个结构。 + +📢 **综上**,有了这样的模型结构设计定义,相信你也可以很好的拆分自己的业务对象并完成领域功能实现了。 + +## 三、配置文件 + +
+ +
+ +- 工程中关于 MyBatis 的使用,在 xfg-dev-tech-app 下进行统一配置。因为所有配置信息都放到一起,比较方便管理,也利于线上上线后,提取配置文件。 + +## 四、功能实现 + +接下来我们介绍一些关于 MyBatis 的使用功能,但你可以带着 DDD 的思想来看这些内容实现时所在的位置,这会让你不只是学习 MyBatis 也能学会一些 DDD 的设计。 + +### 1. 插入&批量插入 + +**源码**:`cn.bugstack.xfg.dev.tech.infrastructure.dao.IEmployeeDAO` + +```java +@Mapper +public interface IEmployeeDAO { + + void insert(EmployeePO employee); + + void insertList(List list); + + void update(EmployeePO employeePO); + + EmployeePO queryEmployeeByEmployNumber(String employNumber); + +} +``` + +**xml**:`employee_mapper.xml` + +```xml + + INSERT INTO employee(employee_number, employee_name, employee_level, employee_title, create_time, update_time) + VALUES(#{employeeNumber}, #{employeeName}, #{employeeLevel}, #{employeeTitle}, now(), now()) + + + + INSERT INTO employee(employee_number, employee_name, employee_level, employee_title, create_time, update_time) + VALUES + + (#{item.employeeNumber}, #{item.employeeName}, #{item.employeeLevel}, #{item.employeeTitle}, now(), now()) + + +``` + +- 使用配置文件的方式比较好维护,当然也可以尝试使用 MyBatis 提供的注解方式,完成数据的操作。 + +### 2. 事务&注解编程 + +Spring 提供的事务分为注解事务和编程事务,编程事务可以更细粒度的控制。 + +Spring Boot 事务管理的级别可以通过 `@Transactional` 注解的 `isolation` 属性进行配置。常见的事务隔离级别有以下几种: + +1. `DEFAULT`:使用底层数据库的默认隔离级别。MySQL 默认为 `REPEATABLE READ`,Oracle 默认为 `READ COMMITTED`。 +2. `READ_UNCOMMITTED`:最低的隔离级别,允许读取未提交的数据变更,可能会导致脏读、不可重复读和幻读问题。 +3. `READ_COMMITTED`:允许读取已经提交的数据变更,可以避免脏读问题,但可能会出现不可重复读和幻读问题。 +4. `REPEATABLE_READ`:保证同一事务中多次读取同一数据时,结果始终一致,可以避免脏读和不可重复读问题,但可能会出现幻读问题。 +5. `SERIALIZABLE`:最高的隔离级别,可以避免脏读、不可重复读和幻读问题,但会影响并发性能。 + +在 Spring Boot 中,默认的事务隔离级别为 `DEFAULT`。如果没有特殊需求,建议使用默认隔离级别。 + +SpringBoot 事务的传播行为可以通过 `@Transactional` 注解的 `propagation` 属性进行配置。常用的传播行为有以下几种: + +1. `Propagation.REQUIRED`:默认的传播行为,如果当前存在事务,则加入该事务,否则新建一个事务; +2. `Propagation.SUPPORTS`:如果当前存在事务,则加入该事务,否则以非事务的方式执行; +3. `Propagation.MANDATORY`:如果当前存在事务,则加入该事务,否则抛出异常; +4. `Propagation.REQUIRES_NEW`:无论当前是否存在事务,都会新建一个事务,如果当前存在事务,则将当前事务挂起; +5. `Propagation.NOT_SUPPORTED`:以非事务的方式执行操作,如果当前存在事务,则将当前事务挂起; +6. `Propagation.NEVER`:以非事务的方式执行操作,如果当前存在事务,则抛出异常; +7. `Propagation.NESTED`:如果当前存在事务,则在该事务的嵌套事务中执行,否则新建一个事务。嵌套事务是独立于外部事务的,但是如果外部事务回滚,则嵌套事务也会回滚。 + +除了传播行为,`@Transactional` 注解还可以配置其他属性,例如隔离级别、超时时间、只读等。 + +#### 2.1 注解事务 + +**源码**:`cn.bugstack.xfg.dev.tech.infrastructure.repository.SalaryAdjustRepository` + +```java +@Transactional(rollbackFor = Exception.class, timeout = 350, propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT) +public String adjustSalary(AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate) { + String employeeNumber = adjustSalaryApplyOrderAggregate.getEmployeeNumber(); + String orderId = adjustSalaryApplyOrderAggregate.getOrderId(); + EmployeeEntity employeeEntity = adjustSalaryApplyOrderAggregate.getEmployeeEntity(); + EmployeeSalaryAdjustEntity employeeSalaryAdjustEntity = adjustSalaryApplyOrderAggregate.getEmployeeSalaryAdjustEntity(); + EmployeePO employeePO = EmployeePO.builder() + .employeeNumber(employeeNumber) + .employeeLevel(employeeEntity.getEmployeeLevel().getCode()) + .employeeTitle(employeeEntity.getEmployeeTitle().getDesc()).build(); + // 更新岗位 + employeeDAO.update(employeePO); + EmployeeSalaryPO employeeSalaryPO = EmployeeSalaryPO.builder() + .employeeNumber(employeeNumber) + .salaryTotalAmount(employeeSalaryAdjustEntity.getAdjustTotalAmount()) + .salaryMeritAmount(employeeSalaryAdjustEntity.getAdjustMeritAmount()) + .salaryBaseAmount(employeeSalaryAdjustEntity.getAdjustBaseAmount()) + .build(); + // 更新薪酬 + employeeSalaryDAO.update(employeeSalaryPO); + EmployeeSalaryAdjustPO employeeSalaryAdjustPO = EmployeeSalaryAdjustPO.builder() + .employeeNumber(employeeNumber) + .adjustOrderId(orderId) + .adjustTotalAmount(employeeSalaryAdjustEntity.getAdjustTotalAmount()) + .adjustBaseAmount(employeeSalaryAdjustEntity.getAdjustMeritAmount()) + .adjustMeritAmount(employeeSalaryAdjustEntity.getAdjustBaseAmount()) + .build(); + // 写入流水 + employeeSalaryAdjustDAO.insert(employeeSalaryAdjustPO); + return orderId; +} +``` + +- 这个事务所做的内容,就是前面小傅哥提到的调整薪资的处理。它的具体操作就是放到仓储层实现。 +- 注意事务注解的配置。 + +#### 2.2 编程事务 + +##### 2.2.1 事务模板 + +
+ +
+ +- 使用编程事务,需要在这里创建出一个事务模板,当然你不创建也可以使用。只不过这样统一的配置会更加方便。 + +##### 2.2.2 事务使用 + +```java +private TransactionTemplate transactionTemplate; +@Override +public void insertEmployeeInfo(EmployeeInfoEntity employeeInfoEntity) { + transactionTemplate.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus status) { + try { + EmployeePO employeePO = EmployeePO.builder() + .employeeNumber(employeeInfoEntity.getEmployeeNumber()) + .employeeName(employeeInfoEntity.getEmployeeName()) + .employeeLevel(employeeInfoEntity.getEmployeeLevel()) + .employeeTitle(employeeInfoEntity.getEmployeeTitle()) + .build(); + employeeDAO.insert(employeePO); + EmployeeSalaryPO employeeSalaryPO = EmployeeSalaryPO.builder() + .employeeNumber(employeeInfoEntity.getEmployeeNumber()) + .salaryTotalAmount(employeeInfoEntity.getSalaryTotalAmount()) + .salaryMeritAmount(employeeInfoEntity.getSalaryMeritAmount()) + .salaryBaseAmount(employeeInfoEntity.getSalaryBaseAmount()) + .build(); + employeeSalaryDAO.insert(employeeSalaryPO); + } catch (Exception e) { + status.setRollbackOnly(); + e.printStackTrace(); + } + } + }); +} +``` + +- 之后就可以手动处理事务了,因为手动的处理可以更细节的控制,也可以根据返回的结果,手动回滚。而不非得异常回滚。 + +### 3. 插件&数据加密 + +使用 MyBatis 时,也会经常会用到插件开发。尤其是做一些数据的加解密、路由、日志等,都可以基于插件实现。 + +那么这里小傅哥就带着你实现一个对指定字段加解密的处理,比如雇员的姓名、薪资、级别是可以隐藏的,避免被有心之人盗取。 + +**源码**:`cn.bugstack.xfg.dev.tech.plugin.FieldEncryptionAndDecryptionMybatisPlugin` + +```java +@Intercepts({ + @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), + @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) +}) +public class FieldEncryptionAndDecryptionMybatisPlugin implements Interceptor { + + /** + * 密钥,必须是16位 + */ + private static final String KEY = "1898794876567654"; + /** + * 偏移量,必须是16位 + */ + private static final String IV = "1233214566547891"; + + @Override + public Object intercept(Invocation invocation) throws Throwable { + Object[] args = invocation.getArgs(); + MappedStatement mappedStatement = (MappedStatement) args[0]; + Object parameter = args[1]; + String sqlId = mappedStatement.getId(); + + if (parameter != null && (sqlId.contains("insert") || sqlId.contains("update")) ) { + String columnName = "employeeName"; + if (parameter instanceof Map) { + List parameterList = (List) ((Map) parameter).get("list"); + for (Object obj : parameterList) { + if (hasField(obj, columnName)) { + String fieldValue = BeanUtils.getProperty(obj, columnName); + String encryptedValue = encrypt(fieldValue); + BeanUtils.setProperty(obj, columnName, encryptedValue); + } + } + } else { + if (hasField(parameter, columnName)) { + String fieldValue = BeanUtils.getProperty(parameter, columnName); + String encryptedValue = encrypt(fieldValue); + BeanUtils.setProperty(parameter, columnName, encryptedValue); + } + } + } + + Object result = invocation.proceed(); + + if (result != null && sqlId.contains("query")) { + // 查询操作,解密 + String columnName = "employeeName"; + if (result instanceof List) { + List resultList = (List) result; + for (Object obj : resultList) { + if (!hasField(obj, columnName)) continue; + String fieldValue = BeanUtils.getProperty(obj, columnName); + if (StringUtils.isBlank(fieldValue)) continue; + String decryptedValue = decrypt(fieldValue); + BeanUtils.setProperty(obj, columnName, decryptedValue); + } + } + } + + return result; + } + + public String encrypt(String content) throws Exception { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + byte[] raw = KEY.getBytes(); + SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES"); + IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec); + byte[] encrypted = cipher.doFinal(content.getBytes()); + return Base64.getEncoder().encodeToString(encrypted); + } + + /** + * AES解密 + * + * @param content 密文 + * @return 明文 + * @throws Exception 异常 + */ + public String decrypt(String content) throws Exception { + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + byte[] raw = KEY.getBytes(); + SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES"); + IvParameterSpec ivParameterSpec = new IvParameterSpec(IV.getBytes()); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); + byte[] encrypted = Base64.getDecoder().decode(content); + byte[] original = cipher.doFinal(encrypted); + return new String(original); + } + + public boolean hasField(Object obj, String fieldName) { + Class clazz = obj.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + return true; + } catch (NoSuchFieldException e) { + clazz = clazz.getSuperclass(); + } + } + return false; + } + +} +``` + +
+ +
+ +- 首先通过注解配置,拦截指定范围内的信息 `Intercepts` 之后在 intercept 接口实现方法中,获取 MappedStatement 这个 MyBatis的映射核心类。[**《手写MyBatis:渐进式源码实践》**](https://item.jd.com/13811216.html) - 跟小傅哥学MyBatis,从零手写源码级复杂项目,提升架构思维与设计逻辑,锻炼编码能力! +- 有了 AES 的加解密,就可以对指定的字段 employeeName 对插入数据库的字段进行加密,同时还可以在读取的时候解密。 + +## 五、测试验证 + +### 1. 调薪 + +```java +@Test +public void test_execSalaryAdjust() { + AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate = AdjustSalaryApplyOrderAggregate.builder() + .employeeNumber("10000001") + .orderId("100908977676001") + .employeeEntity(EmployeeEntity.builder().employeeLevel(EmployeePostVO.T3).employeeTitle(EmployeePostVO.T3).build()) + .employeeSalaryAdjustEntity(EmployeeSalaryAdjustEntity.builder() + .adjustTotalAmount(new BigDecimal(100)) + .adjustBaseAmount(new BigDecimal(80)) + .adjustMeritAmount(new BigDecimal(20)).build()) + .build(); + String orderId = salaryAdjustApplyService.execSalaryAdjust(adjustSalaryApplyOrderAggregate); + log.info("调薪测试 req: {} res: {}", JSON.toJSONString(adjustSalaryApplyOrderAggregate), orderId); +} +``` + +```java +23-07-15.13:23:11.514 [main ] INFO HikariDataSource - HikariPool-1 - Start completed. +23-07-15.13:23:11.910 [main ] INFO ISalaryAdjustApplyServiceTest - 调薪测试 req: {"employeeEntity":{"employeeLevel":"T3","employeeTitle":"T3"},"employeeNumber":"10000001","employeeSalaryAdjustEntity":{"adjustBaseAmount":80,"adjustMeritAmount":20,"adjustTotalAmount":100},"orderId":"100908977676002"} res: 100908977676002 +``` + +### 2. 查询 + +```java +@Test +public void test_queryEmployInfo() { + EmployeeInfoEntity employeeInfoEntity = employeeService.queryEmployInfo("10000001"); + log.info("测试结果:{}", JSON.toJSONString(employeeInfoEntity)); +} +``` + +```java +23-07-15.13:24:54.000 [main ] INFO HikariDataSource - HikariPool-1 - Start completed. +23-07-15.13:24:54.490 [main ] INFO IEmployeeServiceTest - 测试结果:{"employeeLevel":"T-3","employeeName":"小傅哥","employeeNumber":"10000001","employeeTitle":"中级工程师","salaryBaseAmount":5200.00,"salaryMeritAmount":5200.00,"salaryTotalAmount":5200.00} +``` + +- 执行完调薪后,就可以来看下这个用户的薪资待遇是多少了。 diff --git a/docs/md/road-map/mysql-time-zone.md b/docs/md/road-map/mysql-time-zone.md new file mode 100644 index 000000000..4cf76975e --- /dev/null +++ b/docs/md/road-map/mysql-time-zone.md @@ -0,0 +1,172 @@ +--- +title: MySQL Time Zone +lock: need +--- + +# MySQL 8.0.22 引发的时区错误问题,应该如何正确的配置 TimeZone? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在实际的工作场景中有时候就是一个小小的问题,就可能引发出一个大大的bug。而且工作这么多年,看到的线上事故,往往也都是这些小的细节问题,所以学习这些具有实际经验的细节非常重要。 + +
+ +
+ +**有些事故隐藏的很深!** + +其实很多时候事故也不是一开始就有的,而是随着需求的迭代,达到某一个条件后触达到事故的发生条件了才出现的。就像 MySQL 的时区配置问题,它既有不同版本 JDBC 连接引擎的不同,又有数据库设置的时区,还有服务端设置的时区,还包括在使用数据库配置时指定的时区。这些条件综合发生时才会出现事故。 + +接下来,小傅哥就给大家分享下为啥是 8.0.22 版本才会引发时区错误问题。 + +## 一、问题场景 + +这是一条很普通的SQL语句; + +```java + + INSERT INTO employee(employee_number, employee_name, employee_level, employee_title, create_time, update_time) + VALUES(#{employeeNumber}, #{employeeName}, #{employeeLevel}, #{employeeTitle}, now(), now()) + +``` + +修改下这条普通的SQL语句; + +```java + + INSERT INTO employee(employee_number, employee_name, employee_level, employee_title, create_time, update_time) + VALUES(#{employeeNumber}, #{employeeName}, #{employeeLevel}, #{employeeTitle}, #{createTime}, now()) + +``` + +接下来在执行插入SQL语句; + +
+ +
+ +- 原本直接使用数据库语句 now() 的并没有问题,而改为由程序透传的时间 createTime 后,日期时间发生了错误。差了8个小时。 +- 通常一般我们操作数据库的时候,写入的时间,往往都是 now()。但有时候比如要外部透传用户下单时间做本系统做一个返利活动,在什么时间内才返利,要记录时间。这个时候发现写入数据库的时间就不对了。 +- 因为原本你的系统都是走的数据库时间,现在突然多了一个来自系统的透传时间,那么你可能是注意不到的。另外由于本机的开发环境与服务器配置不一样,所以最终直至上线开始跑数据了,才发现问题。这个就是一般出现事故的原因。 + +## 二、排查配置 + +### 1. mysql jdbc 版本 + +```java + + mysql + mysql-connector-java + 8.0.22 + +``` + +- 8.0.22 版本,官网提示有bug;`https://dev.mysql.com/doc/relnotes/connector-j/en/news-8-0-23.html` + +### 2. 链接参数配置 + +```java +jdbc:mysql://127.0.0.1:3306/road-map?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=true +``` + +- 注意首次没有配置时区。配置时区需要增加参数;`&serverTimezone=Asia/Shanghai` + +### 3. mysql time-zone 配置 + +```java +show variables like '%time_zone%'; + ++------------------+--------+ +| Variable_name | Value | ++------------------+--------+ +| system_time_zone | CST | +| time_zone | SYSTEM | ++------------------+--------+ +``` + +- 命令修改时区;`SET time_zone = 'SYSTEM';` +- 命令修改时区;`SET time_zone = '+8:00';` +- 注意CST配置,不是中国时区。默认是美国中部时间。 + - 美国中部时间 Central Standard Time (USA) UTC-05:00 或 UTC-06:00 + - 澳大利亚中部时间 Central Standard Time (Australia) UTC+09:30 + - 中国标准时 China Standard Time UTC+08:00 + - 古巴标准时 Cuba Standard Time UTC-04:00 + + +### 4. linux 服务器时间 + +```java +[root@lavm-aqhgp9nber ~]# timedatectl + Local time: Sat 2024-08-31 13:57:07 CST + Universal time: Sat 2024-08-31 05:57:07 UTC + RTC time: Sat 2024-08-31 05:57:06 + Time zone: Asia/Shanghai (CST, +0800) + NTP enabled: yes +NTP synchronized: yes + RTC in local TZ: no + DST active: n/a +``` + +- 命令修改时区;`sudo timedatectl set-timezone Asia/Shanghai` +- 命令修改时区;`sudo timedatectl set-timezone America/New_York` + +## 三、源码问题 - MySQL JDBC + +### 1. 8.0.22 版本问题 + +在 8.0.0 ~ 8.0.22 版本中,如果未配置时区,`serverTimezone=Asia/Shanghai` 则会取服务端时区,所以如果服务端配置的是 CST 时区,则会有问题。调试源码; + +**com.mysql.cj.protocol.a.NativeProtocol#configureTimezone** + +
+ +
+ +- 在 8.0.22 版本中,获取时区的方法,如果本地为配置 jdbc 时区,则会获取服务端时区。也就是 CST 美国中部时间。 +- 所以,如果你要使用的是 8.0.22 就必须指定时区。`jdbc:mysql://IP:13306/road-map?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai` + +### 2. 8.0.23 + 版本 + +在 8.0.23 版本以后,如果未配置时区,调整为获取客户端时区。 + +```java + + mysql + mysql-connector-java + 8.0.23 + +``` + +- 切换到 8.0.23 版本。 + +**com.mysql.cj.protocol.a.NativeProtocol#configureTimezone** + +
+ +
+ +- 在使用 8.0.23 TimeZone.getDefault() 注释 `Gets the default TimeZone of the Java virtual machine.` 获取 Java 虚拟机默认时区。这个方法也是 Java 本身代码的方法。 +- 你可以通过 Java Main 函数执行 `System.*out*.println("Default Time Zone: " + TimeZone.getDefault().getID());` 获取默认时区。打印结果为 `Default Time Zone: Asia/Shanghai` + +### 3. 官网说明 + +地址:[https://dev.mysql.com/doc/relnotes/connector-j/en/news-8-0-23.html](https://dev.mysql.com/doc/relnotes/connector-j/en/news-8-0-23.html) + +**Bugs Fixed** + +```java +After upgrading from Connector/J 5.1 to 8.0, the results of saving and then retrieving DATETIME and TIMESTAMP values became different sometimes. It was because while Connector/J 5.1 does not preserve a time instant by default, Connector/J 8.0.22 and earlier tried to do so by converting a timestamp to the server's session time zone before sending its value to the server. In this release, new mechanisms for controlling timezone conversion has been introduced—see Preserving Time Instants for details. Under this new mechanism, the default behavior of Connector/J 5.1 in this respect is preserved by setting the connection property preserveInstants=false. (Bug #30962953, Bug #98695, Bug #30573281, Bug #95644) +``` + +从 Connector/J 5.1 升级到 8.0 后,保存和检索 DATETIME 和 TIMESTAMP 值的结果有时会有所不同。这是因为,虽然 Connector/J 5.1 默认不保留时间点,但 Connector/J 8.0.22 及更早版本尝试通过在将时间戳的值发送到服务器之前将其转换为服务器的会话时区来保留时间点。在此版本中,引入了用于控制时区转换的新机制 - 有关详细信息,请参阅保留时间点。在这种新机制下,通过设置连接属性 retainInstants=false 来保留 Connector/J 5.1 在这方面的默认行为。(错误 #30962953、错误 #98695、错误 #30573281、错误 #95644) + +## 四、综上总结 + +在使用MySQL的时候,确保服务器时区、MySQL时区、Java应用链接MySQL JDBC的参数配置,都指定到具体的时区上。MySQL JDBC 使用 8.0.23+ 版本,不要使用 8.0.0 ~ 8.0.22 版本,尤其是5.1升级要升级到 8.0.23 以及往后的版本。 + +正确配置;`url: jdbc:mysql://127.0.0.1:3306/road-map?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=Asia/Shanghai` diff --git a/docs/md/road-map/mysql.md b/docs/md/road-map/mysql.md new file mode 100644 index 000000000..bb6fd5693 --- /dev/null +++ b/docs/md/road-map/mysql.md @@ -0,0 +1,315 @@ +--- +title: MySQL +lock: need +--- + +# MySQL 开发规范和使用约束 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,如何更好地使用 MySQL 数据库。这包括;库表创建规范、字段的创建规范、索引的创建规范以及SQL使用的相关规范,通过这些内容的讲解,让读者更好使用 MySQL 数据库,创建出符合规范的表和字段以及建出合适的索引。 + +如果你还想学习更深入的 MySQL 知识,建议可以阅读下官网的参考手册,这比任何一个资料都要有权威性。 + +- [MySQL 5.7 参考手册](https://dev.mysql.com/doc/refman/5.7/en/create-index.html) +- [MySQL 8.0 参考手册](https://dev.mysql.com/doc/refman/8.0/en/commit.html) + +本文涉及的工程【导表语句】: + +- [road_map_5.6.sql](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-mysql/-/tree/master/docs) +- [road_map_8.0.sql](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-mysql/-/tree/master/docs) + +## 一、案例背景 + +此案例背景定位于使用使用一个简单的订单表,来讲解 MySQL 使用的相关规范。包括;表的引擎、命名约束、字段长度、金额类型、更新时间、索引字段、组合索引等内容,方便大家学习以后,可以基于这些字段的规范演示讲解,在自己创建库表的时候有个参考对照,尽可能创建出性能更佳的库表和索引。 + +## 二、库表规范 + +为了能让读者更加清晰地看到这些相关规范都是如何体现的,小傅哥这里准备了个大图,把库表字段和规范全部整合在一起,方便学习使用。如下; + +
+ +
+ +如上所列规范包括:建表相关规范、字段相关规范、索引相关规范、使用相关规范。 + +### 1. 建表相关规范 + +1. 库名、表名、字段名,使用小写和下划线 _ 分割 +2. 库名、表名、字段名,不超过12个字符。默认支持64个字符。 +3. 库名、表名、字段名,见名知意,建议使用名词而不是动词。 +4. 使用 InnoDB 存储引擎。支持;事务、锁、高并发 性能好。 +5. 推荐使用 utf8mb4 可以存emoji +6. 单表字段数,建议不超过40个 + +### 2. 字段相关规范 + +1. 整型定义中不显示设置长度,如使用 INT,而不是INT(4) +2. 存储精度浮点数,使用 DECIMAL 替代 FLOAT、DOUBLE +3. 所有字段,都要有 Comment 描述 +4. 所有字段应定义为 NOT NULL +5. 超过2038年,用DATETIME存储 +6. 短数据类型 0~80 选用 TINYINT 存储 +7. UUID 有全局唯一统一字段属性,适合做同步ES使用。 +8. IPV4,用无符号 INT 存储 +9. IPV6,用VARBINARY存储 +10. JSON MySql 8.x 新增特性 +11. update_time 设置 on update 更新属性 + +### 3. 索引相关规范 + +1. 要求有自增ID作为主键,不要使用随机性较强的 order_id 作为主键,会导致innodb内部page分裂和大量随机I/O,性能下降。 +2. 单表索引建议控制在5个以内,单索引字段数不超过5个。注意:已有idx(a, b)索引,又有idx(a)索引,可以把idx(a)删了,浪费空间,降低更新、写入性能。* 单个索引中,每个索引记录的长度不能超过64KB +3. 利用覆盖索引来进行查询操作,避免回表。另外建组合索引的时候,区分度最高的在最左边。 +4. `select(count(distinct(字段)))/count(id) = 1` 的区分度,更适合建索引。在一些低区分度的字段,例如type、status上建立独立索引几乎没意义,降低更新、写入性能。 +5. 防止因字段不同造成的隐式转换,导致索引失效。 +6. 更新频繁的字段,不要建索引。 + +### 4. 使用相关规范 + +1. 单表数据量不超过500万行,ibc 文件大小不超过 2G +2. 水平分表用取模,日志、报表类,可以用日期 +3. 单实例表数目小于 500 +4. alter表之前,先判断表数据量,对于超过100W行记录的表进行alter table,必须在业务低峰期执行。因为alter table会产生表锁,期间阻塞对于该表的所有写入 +5. SELECT语句必须指定具体字段名称,禁止写成 `“*”select *` 会将不需要读的数据也从MySQL里读出来,造成网卡压力,数据表字段一旦更新,但model层没有来得及更新的话,系统会报错 +6. insert语句指定具体字段名称,不要写成 `insert into t1 values(…)` +7. `insert into…values(XX),(XX),(XX)..` 这里XX的值不要超过5000个,值过多会引起主从同步延迟变大。 +8. `union all` 和 `union`,不要超过5个子句,如果没有去重的需求,使用union all性能更好。 +9. in 值列表限制在500以内,例如 `select… where userid in(….500个以内…)`,可以减少底层扫描,减轻数据库压力。 +10. 除静态表或小表(100行以内),DML语句必须有where条件,且尽量使用索引查找 +11. 生产环境禁止使用 hint,如 sql_no_cache,force index,ignore key,straight join等。 +要相信MySQL优化器。hint是用来强制SQL按照某个执行计划来执行,但随着数据量变化我们无法保证自己当初的预判是正确的。 +12. where条件里,等号左右字段类型必须一致,否则会造成隐式的类型转化,可能导致无法使用索引 +13. 生产数据库中强烈不推荐在大表执行全表扫描,查询数据量不要超过表行数的25%,否则可能导致无法使用索引 +14. where子句中禁止只使用全模糊的LIKE条件进行查找,如like ‘%abc%’,必须有其他等值或范围查询条件,否则可能导致无法使用索引 +15. 索引列不要使用函数或表达式,如 `where length(name)=10` 或 `where user_id+2=1002`,否则可能导致无法使用索引 +16. 减少使用or语句 or有可能被 mysq l优化为支持索引,但也要损耗 mysql 的 cpu 性能。可将or语句优化为union,然后在各个where条件上建立索引。如 `where a=1 or b=2` 优化为 `where a=1… union …where b=2, key(a),key(b)` 某些场景下,也可优化为 `in` +17. 分页查询,当limit起点较高时,可先用过滤条件进行过滤。如 `select a,b,c from t1 limit 10000,20`; 优化为 `select a,b,c from t1 where id>10000 limit 20`; +18. 同表的字段增删、索引增删等,合并成一条DDL语句执行,提高执行效率,减少与数据库的交互。 +19. `replace into` 和 `insert on duplicate key update` 在并发环境下执行都可能产生死锁(后者在5.6版本可能不报错,但数据有可能产生问题),需要catch异常,做事务回滚,具体的锁冲突可以关注`next key lock`和`insert intention lock` +20. TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 无事务且不触发 trigger ,有可能造成事故,故不建议在开发代码中使用此语句。说明: TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。 + +## 三、建表语句 + +**环境说明**; + +- MySQL 8.0.32 - 可使用 Docker 安装,脚本放到本案例仓库了。 +- [Sequel Ace](https://www.sequelpro.com/) + +```sql +# ************************************************************ +# Sequel Ace SQL dump +# 版本号: 20050 +# +# https://sequel-ace.com/ +# https://github.com/Sequel-Ace/Sequel-Ace +# +# 主机: localhost (MySQL 8.0.32) +# 数据库: road_map +# 生成时间: 2023-08-12 07:19:03 +0000 +# ************************************************************ + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +SET NAMES utf8mb4; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE='NO_AUTO_VALUE_ON_ZERO', SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + + +# 转储表 user_order +# ------------------------------------------------------------ + +DROP TABLE IF EXISTS `user_order`; + +CREATE TABLE `user_order` ( + `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID;【必须保留自增ID,不要将一些有随机特性的字段值设计为主键,例如order_id,会导致innodb内部page分裂和大量随机I/O,性能下降】int 大约21亿左右,超过会报错。bigint 大约9千亿左右。', + `user_name` varchar(64) NOT NULL COMMENT '用户姓名;', + `user_id` varchar(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户编号;', + `user_mobile` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户电话;使用varchar(20)存储手机号,不要使用整型。手机号不会做数学计算、涉及到区号或者国家代号,可能出现+-()、支持模糊查询,例如:like“135%”', + `sku` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品编号', + `sku_name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品名称', + `order_id` varchar(64) NOT NULL COMMENT '订单ID', + `quantity` int NOT NULL DEFAULT '1' COMMENT '商品数量;整形定义中不显示规定显示长度,比如使用 INT,而不使用 INT(4)', + `unit_price` decimal(10,2) NOT NULL COMMENT '商品价格;小数类型为 decimal,禁止使用 float、double', + `discount_amount` decimal(10,2) NOT NULL COMMENT '折扣金额;', + `tax` decimal(4,2) NOT NULL COMMENT '费率金额;', + `total_amount` decimal(10,2) NOT NULL COMMENT '支付金额;(商品的总金额 - 折扣) * (1 - 费率)', + `order_date` datetime NOT NULL COMMENT '订单日期;timestamp的时间范围在1970-01-01 00:00:01到2038-01-01 00:00:00之间', + `order_status` tinyint(1) NOT NULL COMMENT '订单状态;0 创建、1完成、2掉单、3关单 【不要使用 enum 要使用 tinyint 替代。0-80 范围,都可以使用 tinyint】', + `is_delete` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删单;0未删除,1已删除 【表达是否概念的字段必须使用is_】', + `uuid` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '唯一索引;分布式下全局唯一,用于binlog 同步 ES 方便使用', + `ipv4` int unsigned NOT NULL DEFAULT '2130706433' COMMENT '设备地址;存储IPV4地址,通过MySQL 函数转换,inet_ntoa、inet_aton 示例;SELECT INET_ATON(‘209.207.224.40′); 3520061480 SELECT INET_NTOA(3520061480); 209.207.224.40所有字段定义为NOT NULL,并设置默认值,因为null值的字段会导致每一行都占用额外存储空间\\n数据迁移容易出错,在聚合函数计算结果偏差(如count结果不准)并且null的列使索引/索引统计/值比较都更加复杂,MySQL内部需要进行特殊处理,表中有较多空字段的时候,数据库性能下降严重。开发中null只能采用is null或is not null检索,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!=’abc’,如果存在name为null值的记录,查询结果就不会包含name为null值的记录', + `ipv6` varbinary(16) NOT NULL COMMENT '设备地址;存储IPV6地址,VARBINARY(16) 插入:INET6_ATON(''2001:0db8:85a3:0000:0000:8a2e:0370:7334'') 查询:SELECT INET6_NTOA(ip_address) ', + `ext_data` json NOT NULL COMMENT '扩展数据;记录下单时用户的设备环境等信息(核心业务字段,要单独拆表)。【select user_name, ext_data, ext_data->>''$.device'', ext_data->>''$.device.machine'' from `user_order`;】', + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uq_orderid` (`order_id`), + UNIQUE KEY `uq_uuid` (`uuid`), + KEY `idx_order_date` (`order_date`), + KEY `idx_sku_unit_price_total_amount` (`sku`,`unit_price`,`total_amount`) +) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; + +LOCK TABLES `user_order` WRITE; +/*!40000 ALTER TABLE `user_order` DISABLE KEYS */; + +INSERT INTO `user_order` (`id`, `user_name`, `user_id`, `user_mobile`, `sku`, `sku_name`, `order_id`, `quantity`, `unit_price`, `discount_amount`, `tax`, `total_amount`, `order_date`, `order_status`, `is_delete`, `uuid`, `ipv4`, `ipv6`, `ext_data`, `update_time`, `create_time`) +VALUES + (1,'小傅哥','U001','13512345678','SKU001','Mac Pro M2 贴膜','ORD001',2,10.99,2.00,0.50,19.48,'2023-08-12 10:00:00',0,0,'uuid001',2130706433,X'20010DB885A3000000008A2E03707334','{\"device\": {\"machine\": \"IPhone 14 Pro\", \"location\": \"shanghai\"}}','2023-08-12 10:00:00','2023-08-12 10:00:00'), + (2,'福禄娃','U002','13698765432','SKU002','IPad mini4 外套','ORD002',1,25.99,0.00,1.50,24.49,'2023-08-12 11:30:00',1,0,'uuid002',2130706433,X'20010DB885A3000000008A2E03707334','{\"device\": {\"machine\": \"PC Windows\", \"location\": \"BeiJing\"}}','2023-08-12 11:30:00','2023-08-12 11:30:00'), + (3,'拎瓢冲','U003','13755555555','SKU003','数据线','ORD003',3,9.99,1.50,0.00,26.97,'2023-08-12 13:45:00',0,0,'uuid003',2130706433,X'20010DB885A3000000008A2E03707334','{\"device\": {\"machine\": \"PC Windows\", \"location\": \"BeiJing\"}}','2023-08-12 13:45:00','2023-08-12 13:45:00'), + (4,'熏5null','U004','13812345678','SKU004','U盘','ORD004',1,15.99,0.00,0.75,15.24,'2023-08-12 14:20:00',1,0,'uuid004',2130706433,X'20010DB885A3000000008A2E03707334','{\"device\": {\"machine\": \"PC Windows\", \"location\": \"BeiJing\"}}','2023-08-12 14:20:00','2023-08-12 14:20:00'), + (5,'温柔一刀','U005','13999999999','SKU005','坐垫','ORD005',2,12.50,1.25,0.25,23.75,'2023-08-12 15:55:00',0,0,'uuid005',2130706433,X'20010DB885A3000000008A2E03707334','{\"device\": {\"machine\": \"PC Windows\", \"location\": \"BeiJing\"}}','2023-08-12 15:55:00','2023-08-12 15:55:00'); + +/*!40000 ALTER TABLE `user_order` ENABLE KEYS */; +UNLOCK TABLES; + +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +``` + +## 三、语句操作 + +### 1. 插入操作 + +```sql +INSERT INTO `user_order` (`id`, `user_name`, `user_id`, `user_mobile`, `sku`, `sku_name`, `order_id`, `quantity`, `unit_price`, `discount_amount`, `tax`, `total_amount`, `order_date`, `order_status`, `is_delete`, `uuid`, `ipv4`, `ipv6`, `ext_data`, `update_time`, `create_time`) +VALUES + (9,'小傅哥','U001','13512345678','SKU001','Mac Pro M2 贴膜','ORD0101',2,10.99,2.00,0.50,19.48,'2023-08-12 10:00:00',0,0,'uuid010',INET_ATON('127.0.0.1'),INET6_ATON('2001:0db8:85a3:0000:0000:8a2e:0370:7334'),'{\"device\": {\"machine\": \"IPhone 14 Pro\", \"location\": \"shanghai\"}}','2023-08-12 10:00:00','2023-08-12 10:00:00'); +``` + +- 其实列了这个 SQL 主要让大家注意到 IPV4、IPV6 的存储需要用到转换函数。也就是 MySQL 自己提供的 `INET_ATON`、`INET6_ATON` 转换和对应的 `INET_NTON`、`INET6_NTON` 解析。 +- 此外你还可以单独测试这个函数;`select INET6_NTOA(INET6_ATON('2001:0db8:85a3:0000:0000:8a2e:0370:7334'))` + +### 2. 查询操作 + +#### 2.1 IP 查询 + +```sql +select user_name, sku, INET_NTOA(ipv4), INET6_NTOA(ipv6) from `user_order`; + +小傅哥 SKU001 127.0.0.1 2001:db8:85a3::8a2e:370:7334 +福禄娃 SKU002 127.0.0.1 2001:db8:85a3::8a2e:370:7334 +拎瓢冲 SKU003 127.0.0.1 2001:db8:85a3::8a2e:370:7334 +熏5null SKU004 127.0.0.1 2001:db8:85a3::8a2e:370:7334 +温柔一刀 SKU005 127.0.0.1 2001:db8:85a3::8a2e:370:7334 +``` + +#### 2.2 JSON 查询 + +```sql +select user_name, ext_data, ext_data->>'$.device', ext_data->>'$.device.machine' from `user_order`; + +小傅哥 {"device": {"machine": "IPhone 14 Pro", "location": "shanghai"}} {"machine": "IPhone 14 Pro", "location": "shanghai"} IPhone 14 Pro +福禄娃 {"device": {"machine": "PC Windows", "location": "BeiJing"}} {"machine": "PC Windows", "location": "BeiJing"} PC Windows +拎瓢冲 {"device": {"machine": "PC Windows", "location": "BeiJing"}} {"machine": "PC Windows", "location": "BeiJing"} PC Windows +熏5null {"device": {"machine": "PC Windows", "location": "BeiJing"}} {"machine": "PC Windows", "location": "BeiJing"} PC Windows +温柔一刀 {"device": {"machine": "PC Windows", "location": "BeiJing"}} {"machine": "PC Windows", "location": "BeiJing"} PC Windows +``` + +- MySQL 8.0 提供了 JSON 这样的专属存放方式,你可以通过 JSON 字段的内容来读取对应的信息。 + +#### 2.3 索引使用 + +```sql +# 使用 order_id 唯一索引 +EXPLAIN select user_name, sku, INET_NTOA(ipv4), INET6_NTOA(ipv6) from `user_order` where order_id = 'ORD002'; +``` + +
+ +
+ +```sql +# 使用组合索引 +EXPLAIN select sku,total_amount,order_date from `user_order` where total_amount > 10 and order_date between '2023-08-09 00:00:00' and '2023-08-09 23:59:59'; +``` + +
+ +
+ +#### 2.4 数量统计 + +```sql +select count(*) from `user_order` +``` + +- 不要使用 count(列名) 或 count(常量) 来替代 `count(*)` ,`count(*)` 是 SQL 92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。 + +#### 2.5 for update + +```sql +START TRANSACTION; + +SELECT user_name, sku, total_amount, order_date, order_status FROM `user_order` WHERE order_id = 'ORD002' FOR UPDATE; + +-- 在这里执行其他操作,其他会话无法修改 order_id 为 ORD002 的订单信息 + +COMMIT; +``` + +#### 2.6 行级锁 + +```sql +UPDATE `user_order` SET order_status = 0 WHERE order_id = 'ORD002' AND order_status = 3 FOR UPDATE; +``` + +- order_id 是唯一索引,如果没有索引,将会执行全表扫描。在这种情况下,MySQL会对整个user_order 表进行锁定,而不仅仅是符合条件的行。 +- 即使你没有显式地添加 `FOR UPDATE` 语句,更新语句仍会锁定符合条件的行。这是因为MySQL 默认会使用行级锁来保证并发事务的一致性。 + +#### 2.7 表锁 + +- ALTER TABLE语句:当执行ALTER TABLE语句修改表结构时,MySQL会自动获取一个排它锁(X锁),这会阻塞其他会话对该表的读写操作,直到ALTER TABLE操作完成。 + +- LOCK TABLES语句:当使用LOCK TABLES语句手动锁定表时,会对被锁定的表使用表级别的锁,阻塞其他会话对该表的读写操作。 + +- TRUNCATE TABLE语句:TRUNCATE TABLE语句会获取一个排它锁(X锁),阻塞其他会话对该表的读写操作,直到TRUNCATE TABLE操作完成。 + +## 四、其他配置 + +### 1. 监控活动和性能: + +在MySQL中,你可以使用以下命令来监控MySQL服务器的活动和性能: + +- SHOW PROCESSLIST;:该命令用于显示当前正在运行的所有MySQL连接和查询。它将显示每个连接的ID、用户、主机、数据库、执行时间和当前执行的查询。 +- SHOW STATUS;:该命令用于显示MySQL服务器的各种状态信息,例如连接数、线程状态、查询缓存命中率等。 +- SHOW ENGINE INNODB STATUS;:该命令用于显示InnoDB存储引擎的详细状态信息,包括死锁信息、事务信息和缓冲池状态等。 +- EXPLAIN:在查询语句前加上EXPLAIN关键字,可以获取查询执行计划的详细信息。这将显示查询的表访问顺序、使用的索引和可能的性能问题。 +- mysqladmin extended-status:该命令用于显示MySQL服务器的扩展状态信息,包括各种计数器和性能指标。 + +### 2. 连接数查询和配置 + +查看MySQL服务器的可用连接数和设置连接数,可以使用以下方法: + +1. 查看当前可用连接数: + - 使用命令行客户端登录到MySQL服务器。 + - 执行以下SQL查询语句:`SHOW VARIABLES LIKE 'max_connections';` + - 这将显示MySQL服务器当前配置的最大连接数。 +2. 设置连接数: + - 编辑MySQL服务器的配置文件(通常是`my.cnf`或`my.ini`)。 + - 找到`[mysqld]`部分。 + - 添加或修改以下行:`max_connections = ` + - 将``替换为你希望设置的连接数。 + - 保存并关闭配置文件。 + - 重启MySQL服务器,以使更改生效。 + +设置连接数需要权衡服务器的可用资源和性能。如果设置的连接数过高,可能会导致服务器负载过重,影响性能。建议根据服务器的硬件规格和预期的负载量来调整连接数。另外,某些MySQL版本或发行版可能对最大连接数有特定的限制,请确保你的设置在允许范围内。 + +**注意**:1核1G可配置300个连接、2核4G可配置1000个连接、4核16G可配置4000个连接、8核32G可配置8000个连接。 + +--- + +👏🏻 欢迎小伙伴点击文章下面的`在 GitHub 上编辑此页`,提交更多的MySQL实践应用技巧。 diff --git a/docs/md/road-map/nas.md b/docs/md/road-map/nas.md new file mode 100644 index 000000000..15a9e6b2e --- /dev/null +++ b/docs/md/road-map/nas.md @@ -0,0 +1,126 @@ +--- +title: Nas +lock: need +--- + +# Nas - 这是我犹豫了很久,才买的设备! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在这之前我犹豫🦑了好久,一台不错的 Nas 四五千块,买这个东西能帮助我啥呢 🤔?但后来发现,这台 2c8g 双盘位 4TB Nas,等同于个人拥有了一台永久可用的,软件丰富的,具备远程访问能力的,终身 Linux 服务器。 + +
+ +
+ +**谁更适合买一台 Nas?** + +虽然 Nas 确实很不错,但也不是所有人都有诉求,也不用都买很高配置很贵的。那谁有 Nas 诉求呢?家里有娃的,有大量的照片、视频、文件,需要家人一起存储使用。另外是喜欢看4k蓝光电影,可以用 Nas 搭建一套家庭影院。很多 Nas 也是主打这些功能。 + +但其实我买 Nas,有上面的功能诉求,但还有作为程序员👨🏻‍💻的编程诉求。我希望能在这台 Nas 上安装 Docker 部署环境,并能通过 SSH 客户端,以使用 Linux 的方式,连接 Nas 完成项目的构建、镜像打包和部署。 + +>嘿嘿,使用起来,它还真可以!不过,别买错 Nas,有些并不支持! + +## 一、Nas 是啥? + +NAS(Network Attached Storage,网络附加存储)是一种通过标准的网络拓扑结构(例如以太网)连接到一群计算机上的存储设备。它是一种具有很大存储容量的电脑外敷设备,通过集线器或交换机直接连在网络上,提供跨平台文件共享功能。 + +更简单的来讲,你就把它当做一台部署在家里的 Linux 服务器就好,只不过它的体积不大,但有很大的硬盘容量。所有开通了权限的用户,都可以使用这台共享服务器。并且它提供了很多的软件资源和网络访问服务。 + +## 二、买的哪家的 Nas? + +市面的 Nas 有很多,如;群晖、威联通、绿联、极空间、华为。它们到没有绝对的谁家好,只不过有会一些目标人群的不同。有一些网上的对比,可以参考; + +| 品牌 | 优势 | 劣势 | +|----------|----------------------------------------------------------------------|----------------------------------------------------------------------| +| 群晖 | - 软件生态丰富,DSM系统用户友好 | - 价格较高 | +| | - 稳定性和可靠性强 | - 硬件规格在同价位上可能不如竞争对手 | +| | - 拥有庞大的用户社区和丰富的在线资源 | | +| | - 提供多种数据保护和备份解决方案 | | +| 威联通 | - 硬件性能强,提供更高的硬件规格 | - 软件复杂性较高,新手用户可能较难上手 | +| | - 支持虚拟化、容器化应用和多媒体功能 | - 某些情况下软件更新可能导致系统不稳定 | +| | - 提供丰富的扩展选项 | | +| 绿联 | - 价格实惠,适合预算有限的用户 | - 功能较为基础,适合简单存储需求 | +| | - 简单易用,适合家庭用户和小型办公室 | - 在NAS市场的知名度和用户基础不如群晖和威联通 | +| 极空间 | - 性价比高,适合中小企业和个人用户 | - 软件生态和用户界面不如群晖和威联通成熟 | +| | - 提供较为合理的硬件配置 | - 相对较小的用户社区和技术支持资源 | + + +我买的是一台 `群晖 DS723+` 并配了`8G内存` + `2*2TB`硬盘互备,因为咱们要做 Docker 安装各类软件,内存大一些更好。选择群晖主要就是想着这东西资源丰富,可以像使用 Linux 服务器一样使用 Nas 满足开发和存储需求。 + +
+ +
+ +> 用了群晖 Nas 有5个月了,越用越爽。电脑里的东西基本都搬到群晖了,电脑只成为一个工具了,不在搞那么多存储。 + +## 三、Nas 的编程用途 + +群晖 Nas 提供了 群晖管家 APP,只要开启 QuickConnect,无论在家内网还是外网都可以访问和管理 Nas。另外群晖还提供了专门做照片和视频同步管理的 Photos Mobile APP 你可以直接下载使用。 + +它所有的这些东西,只要对照提供的说明书都可以下载使用。难度极低,很好上手。这里小傅哥主要给大家分享下编程的用途。 + +这里首先,对于使用 Windows 的小伙伴,如果你有台 Nas,就不需要本地做那么多让 Windows 兼容 Linux 的事情了。你可以直接在这个上面安装提供的 Docker 套件,并完成对应用程序的打包、构建以及 PUSH 镜像到 DockerHub。 + +另外就是很多小伙伴的电脑配置不高,容量也不大,可以考虑购买 Nas 分摊一部分本身电脑的压力。就像我,如果开启多个 IntelliJ IDEA,在开启 Docker 提供分布式软件环境、在搞一堆网页,以及开启视频录制。这个时候就会非常卡顿了。所以我搞了台 Nas 来解决这个事情。 + +### 1. 有很多编程软件 + +群晖 Nas 提供了非常多的配套软件,适合于不同场景诉求的伙伴使用。所以很多买群晖的伙伴都说,是买软件送设备。嘿嘿,不过我更喜欢这部分编程相关的,尤其是这个 Docker、Git Server,嗖的就安装好。 + +
+ +
+ +- Nas 还支持配置定时开机关机、休眠,这样会比较省电,也不用一直在那打开着。 +- 另外可以自建frp,让自己的nas服务可以直接被公网访问。一年¥48 2c2g 服务器即可。[https://618.gaga.plus](https://618.gaga.plus) 专属地址。[frp](https://bugstack.cn/md/road-map/frp.html) 教程在 bugstack.cn - 路书中。 + +### 2. Docker 使用 + +
+ +
+ +
+ +
+ +- 你可以在提供的操作界面配置 Docker Compose,完成项目文件的配置和启动。之后的体验就和正常 Linux 安装 Docker 部署软件一样了。 +- 不过我通常把这里都只是作为配置后的操作界面,配置的操作,我会在用SSH工具直接链接到Nas上。 + +### 3. SSH 连接 Nas + +因为本身 Nas 也是一台 Linux 服务器,所以可以直接用 SSH 连接使用。推荐使用 [https://termius.com/](https://termius.com/) 工具链接,很好用。 + +- 账号:你的 Nas 登录账号 +- 密码:你的 Nas 登录密码 + +
+ +
+ +登录以后,你就可以像使用 Linux 一样操作了,不过大部分执行类命令,要加上 sudo,比如 `sudo docker images` + +### 4. 构建项目 + +有了 Nas 有个很大的好处就是,你可以本地直接当 Nas 的硬盘是本地的一个文件夹,直接打开就操作。里面的文件直接复制粘贴进去或者拿出来就可以。 + +
+ +
+ +通过 IntelliJ IDEA 直接打开 Nas 中的项目即可,之后你可以用它上面的 Docker 对项目进行构建了。如下; + +
+ +
+ +- build.sh 中执行的就是 docker 命令;`docker build -t system/s-pay-mall-mvc-app:1.0 -f ./Dockerfile .` +- 构建完,就可以部署项目了。Linux 上怎么用,这里你就怎么用。 + +> 当然 Nas 还有很多其他的用途,喜欢折腾的还可以搭建一些你需要的。 diff --git a/docs/md/road-map/nginx.md b/docs/md/road-map/nginx.md new file mode 100644 index 000000000..35ee7c714 --- /dev/null +++ b/docs/md/road-map/nginx.md @@ -0,0 +1,393 @@ +--- +title: Nginx 环境配置 +lock: need +--- + +# Nginx 环境配置 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- 停止:`docker stop Nginx` +- 重启:`docker restart Nginx` +- 删除服务:`docker rm Nginx` +- 删除镜像:`docker rmi Nginx` +- 进入服务:`docker exec -it Nginx /bin/bash` +- 配置文件:[nginx - conf/html/logs/ssl](https://github.com/fuzhengwei/RoadMap/tree/main/10-%E5%8F%91%E5%B8%83%E9%83%A8%E7%BD%B2/103-%E6%9C%8D%E5%8A%A1%E5%99%A8/1-Nginx/data) + +## 一、基础安装 + +```java +docker run \ +--restart always \ +--name Nginx \ +-d \ +-p 80:80 \ +nginx +``` + +![](https://bugstack.cn/images/article/devops/dev-ops-nginx-230418-01.png) + +- restart 重启策略,always 是一直保持重启。如果不设置,可以把这条删掉。`never\always` +- `8090` - 容器端口、`80` - 服务器端口,这样外部通过80端口即可访问。 + +## 二、管理配置 + +首次部署 nginx 后,其实我们还不好操作配置文件。也就是 Nginx 的配置文件是在 Docker 容器的程序下,只有把它拷贝到服务器上才好操作。 + +### 1. 进入 Nginx + +进入程序:docker exec -it Nginx /bin/bash - 退出程序:exit + +```java +[root@vultr ~]# docker exec -it Nginx /bin/bash +root@ed8dc07f2ae6:/# ls +bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var +root@ed8dc07f2ae6:/# cd etc/nginx/ +root@ed8dc07f2ae6:/etc/nginx# ls +conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params +root@ed8dc07f2ae6:/etc/nginx# pwd +/etc/nginx +root@ed8dc07f2ae6:/# cd /usr/share/nginx/html +root@ed8dc07f2ae6:/usr/share/nginx/html# ls +50x.html index.html +root@ed8dc07f2ae6:/usr/share/nginx/html# cat index.html + + + +Welcome to nginx! + + + +

Welcome to nginx!

+

If you see this page, the nginx web server is successfully installed and +working. Further configuration is required.

+ +

For online documentation and support please refer to +nginx.org.
+Commercial support is available at +nginx.com.

+ +

Thank you for using nginx.

+ + +root@ed8dc07f2ae6:/usr/share/nginx/html# +root@ed8dc07f2ae6:/usr/share/nginx/html# exit +exit +``` + +- 配置:`/etc/nginx` +- 网页:`/usr/share/nginx/html` + +### 2. 拷贝 Nginx + +创建目录 + +```shell +[root@vultr ~]# mkdir -p /data/nginx/conf +[root@vultr ~]# mkdir -p /data/nginx/html +``` + +拷贝文件 + +```shell +[root@vultr ~]# docker container cp Nginx:/etc/nginx/nginx.conf /data/nginx/conf +[root@vultr ~]# docker container cp Nginx:/usr/share/nginx/html/index.html /data/nginx/html +``` + +查看信息 + +```shell +[root@vultr ~]# ls /data/nginx/conf/ +nginx.conf +[root@vultr ~]# ls /data/nginx/html/ +index.html +``` + +### 3. 部署 Nginx + +```shell +docker run \ +--restart always \ +--name Nginx \ +-d \ +-v /data/nginx/html:/usr/share/nginx/html \ +-v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ +-p 80:80 \ +nginx +``` + +- 重启:`sudo service nginx restart` + + +## 三、证书安装 + +### 4.1 创建证书 + +SSL 免费的证书,一种是 [ssl - 支持自动续期](https://bugstack.cn/md/road-map/ssl-httpsok.html) 另外各个云服务厂商都有提供,可以自己申请。这里以阿里云/京东云举例; + +- 阿里云免费域名证书:[https://yundun.console.aliyun.com/?p=cas#/certExtend/free/cn-hangzhou](https://yundun.console.aliyun.com/?p=cas#/certExtend/free/cn-hangzhou) +- 京东云免费域名证书:[https://certificate-console.jdcloud.com/jsecssl/create?fastConfig=false&certBrand=TrustAsia&certType=domainType&protectionType=DV-1&gDomainCount=0](https://certificate-console.jdcloud.com/jsecssl/create?fastConfig=false&certBrand=TrustAsia&certType=domainType&protectionType=DV-1&gDomainCount=0) - 选择 TrustAsia 单域名 3个月 0元 + +![](https://bugstack.cn/images/article/devops/dev-ops-nginx-230418-02.png) + +- 步骤1;通过免费的方式创建 SSL,之后通过引导的 DNS 方式进行验证。其实就是在你的域名里配置下验证信息。 +- 步骤2;申请后,3-5分钟左右 DNS 会验证通过,这个时候你直接下载 Nginx 的 SSL 包即可。里面有2个文件【x.key、x.pem】 + +### 4.2 准备内容 + +#### 4.2.1 单个证书 + +- 把下载好的 SSL 文件解压到桌面,你会得到一个文件夹,里面含有 x.key、x.pem 两个文件。 +- 创建一个 default.conf 这个文件配置的 SSL 信息 + +```conf +server { + listen 80; + listen [::]:80; + server_name openai.xfg.im; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name openai.xfg.im; + + ssl_certificate /etc/nginx/ssl/9740289_openai.xfg.im.pem; + ssl_certificate_key /etc/nginx/ssl/9740289_openai.xfg.im.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +- 你可以复制这份文件,在自己本地创建。注意修改域名和SSL文件路径。 + +#### 4.2.2 多个证书 + +如果你需要给1个以上的域名配置SSL,那么可以配置多组 server 如下; + +```shell script +server { + listen 80; + listen [::]:80; + server_name itedus.cn; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name itedus.cn; + + ssl_certificate /etc/nginx/ssl/9750021_itedus.cn.pem; + ssl_certificate_key /etc/nginx/ssl/9750021_itedus.cn.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} + +server { + listen 80; + listen [::]:80; + server_name chatgpt.itedus.cn; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name chatgpt.itedus.cn; + + ssl_certificate /etc/nginx/ssl/9749920_chatgpt.itedus.cn.pem; + ssl_certificate_key /etc/nginx/ssl/9749920_chatgpt.itedus.cn.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_pass http://180.76.119.100:3002; + proxy_http_version 1.1; + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +### 4.3 上传文件 + +你可以通过 `SFTP` 工具或者 `mkdir -p`、`touch` 命令创建一些服务器本地用于映射的文件夹和文件,这里小傅哥使用了 [Termius](https://www.termius.com/) 工具进行创建操作。 + +![](https://bugstack.cn/images/article/devops/dev-ops-nginx-230418-03.png) + +- 文件1;html +- 文件2;ssl - 把本地的 ssh 文件上传进来 +- 文件3;conf - 在 conf 下有个 `conf.d` 的文件夹,把 `default.conf` 上传进去。而 nginx.conf 传到 conf 中。 +- 文件4;logs - 创建日志 + +### 4.4 启动服务 + +在 nginx.conf 的配置文件有这么一句;`include /etc/nginx/conf.d/*.conf;` 那么只要是 conf.d 文件夹下的文件都会被加载。所以直接在 conf.d/default.conf 配置 SSL 就会被加载。接下来重新安装 Nginx 即可。`安装前记得删除 Nginx 你可以用命令【docker stop Nginx、docker rm Nginx】或者在 Portainer 中操作即可` + +```shell +docker run \ +--name Nginx \ +-p 443:443 -p 80:80 \ +-v /data/nginx/logs:/var/log/nginx \ +-v /data/nginx/html:/usr/share/nginx/html \ +-v /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \ +-v /data/nginx/conf/conf.d:/etc/nginx/conf.d \ +-v /data/nginx/ssl:/etc/nginx/ssl/ \ +--privileged=true -d --restart=always nginx +``` + +![](https://bugstack.cn/images/article/devops/dev-ops-nginx-230418-04.png) + +## 五、重定向 + +### 1. default.conf + +在 default.conf 中添加如下配置后重启 Nginx 即可; + +```shell +location /d5fe/ { + rewrite ^/d5fe/(.*)$ /$1 break; + proxy_pass https://api.x.com; + proxy_ssl_server_name on; + proxy_set_header Host api.x.com; + proxy_set_header Connection ''; + proxy_http_version 1.1; + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; +} +``` + +### 2. auth_request + +```shell +server { + listen 80; + listen [::]:80; + server_name api.xfg.im; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name api.xfg.im; + + ssl_certificate /etc/nginx/ssl/9877497_api.xfg.im.pem; + ssl_certificate_key /etc/nginx/ssl/9877497_api.xfg.im.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + root /usr/share/nginx/html; + index index.html index.htm; + } + + location /abc/ { + auth_request /auth; + rewrite ^/abc/(.*)$ /$1 break; + proxy_pass https://api.x.com; + proxy_ssl_server_name on; + proxy_set_header Host api.x.com; + proxy_set_header Connection ''; + proxy_http_version 1.1; + chunked_transfer_encoding off; + proxy_buffering off; + proxy_cache off; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location = /auth { + # 发送子请求到HTTP服务,验证客户端的凭据,返回响应码 + internal; + # 设置参数 + set $query ''; + if ($request_uri ~* "[^\?]+\?(.*)$") { + set $query $1; + } + # 验证成功,返回200 OK + proxy_pass http://207.246.123.*:8090/auth/token?$query; + # 发送原始请求 + proxy_pass_request_body off; + # 清空 Content-Type + proxy_set_header Content-Type ""; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +--- + +**其他资料**:[Nginx 简明教程 @dunwu](https://dunwu.github.io/nginx-tutorial/#/nginx-quickstart) - 非常适合学习Nginx配置。 + diff --git a/docs/md/road-map/none.md b/docs/md/road-map/none.md new file mode 100644 index 000000000..ab7023bb1 --- /dev/null +++ b/docs/md/road-map/none.md @@ -0,0 +1,15 @@ +--- +title: 编写中 +lock: no +--- + +# 编写中 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +... 编写中 \ No newline at end of file diff --git a/docs/md/road-map/ollama.md b/docs/md/road-map/ollama.md new file mode 100644 index 000000000..753ad3832 --- /dev/null +++ b/docs/md/road-map/ollama.md @@ -0,0 +1,242 @@ +--- +title: Nas + Ollama + DeepSeek +lock: need +--- + +# 【教程】在Nas上部署Ollama,搭建DeepSeek、配置PageAssist AI、提供API调用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在 OpenAI 刚兴起的时候,一个非算法的外行Java,想在个人电脑上部署个 GPT2 都费老鼻子👃🏻劲了。现在 DeepSeek 开源以后,拿这 Ollama 直接就能部署,兼职比程序员👨🏻‍💻安装 Java JDK 都容易。😂Java 二狗,也能过上算法的日子。 + +
+ +
+ +**自己部署的 DeepSeek 功能还挺多!** + +自己基于 Ollama 部署的一套 DeepSeek,可以提供独属于你自己一套的 AI,并且可以做图片识别、联网、知识库。而且如果你是一个需要使用 DeepSeek 接口做开发的码农,还可以直接使用自己提供的这套 API 做开发。 + +接下来,小傅哥就带着你使用 Docker 完成 Ollama 的安装和 DeepSeek 模型的部署。Docker 可以在任何环境执行,小傅哥自己是放到 Nas 环境里部署。部署完成后,安装谷歌浏览器插件 Page Assist 使用 Ollama 部署的 DeepSeek 模型。 + +## 一、部署脚本 + +小傅哥这里为你提供了执行安装的脚本,以及测试API的方法; + +
+ +
+ +- 代码:[https://github.com/fuzhengwei/xfg-dev-tech-ollama](https://github.com/fuzhengwei/xfg-dev-tech-ollama) +- dev-ops,提供了 docker-compose.yml 部署 ollama 脚本。这个文件你可以放到任何安装了 Docker 的环境里执行。 +- src 代码,提供的是测试这套 Ollama 下的大模型。除了你本节部署的 DeepSeek,以后部署其他的也可以这样使用。 + +## 二、部署安装 + +### 1. 执行脚本 + +
+ +
+ +```java +# docker compose -f docker-compose.yml up -d +version: '3.8' +services: + ollama: +# image: ollama/ollama:0.5.10 + image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/ollama:0.5.10 + container_name: ollama + restart: unless-stopped + ports: + - "11434:11434" +``` + +- 原始镜像 `image: ollama/ollama:0.5.10` 代理镜像 `registry.cn-hangzhou.aliyuncs.com/xfg-studio/ollama:0.5.10` +- Nas 可以通过界面操作执行启动,如果你是 Linux 服务器,安装了 Docker,可以使用命令执行 `docker compose -f docker-compose.yml up -d` + +### 2. 模型说明 - DeepSeek + +| 模型 | 内存 | 存储 | 特点 | +| ---------------- | ---- | ------ | ---------------------------------------------------- | +| deepseek-r1:1.5b | 8G | 12GB | 轻量级模型,运行速度快,性能有限。 | +| deepseek-r1:7b | 16G | 80GB | 平衡型模型,性能较好,硬件需求适中。 | +| deepseek-r1:14b | 32G | 200GB | 高性能模型,擅长复杂任务(数学推理,代码生成) | +| deepseek-r1:32b | 64G | 320GB | 专业级模型,性能强大,适合高精度任务 | +| deepseek-r1:70b | 128G | 500GB+ | 顶级模型,性能最强,适合大规模计算和高复杂任务执行。 | + +- 这里小傅哥选择的是 1.5b 模型,你可以按照自己的机器配置进行选择。 + +### 3. 模型安装 - DeepSeek + +你需要进入到 Ollama 管理后台执行安装模型脚本; + +
+ +
+ +```java +# 拉取模型 +ollama pull deepseek-r1:1.5b + +# 运行模型 +ollama run deepseek-r1:1.5b + +# 联网模型 +ollama pull nomic-embed-text +``` + +- 命令:`docker exec -it ollama /bin/bash` 也可以进入控制台 +- 首先,安装完成后,可以执行运行之后在后台进行对话。对话完成需要关闭的话,运行 Ctrl + D 关闭。 +- 之后,安装联网模型。这个过程要持续一会。另外不要一下就选很大的模型,怕你扛不住。 + +## 三、配置插件 + +官网:[https://github.com/n4ze3m/page-assist](https://github.com/n4ze3m/page-assist) + +### 1. 搜索安装 + +为了更方便的使用 DeepSeek 模型,这里可以在谷歌浏览器安装一个 Page Assist 插件。 + +
+ +
+ +- 点击安装 Page Assist 插件 + +### 2. 链接地址 + +
+ +
+ +### 3. 设置中文 + +
+ +
+ +### 4. 配置知识库 - RAG + +
+ +
+ +### 5. 添加知识库 - PDF/MD + +
+ +
+ +## 四、对话使用 + +### 1. ai对话 + +
+ +
+ +- 你可以选择模型、联网、图片识别和自己设定的知识库。 + +### 2. 页面对话 + +
+ +
+ +- 你可以在插件上右键,之后就可以与你需要的另外的网页进行对话。理解网页内容做解答。 + +## 五、API 对接 + +### 1. curl 接口 + +```java +curl http://192.168.1.109:11434/api/generate \ + -H "Content-Type: application/json" \ + -d '{ + "model": "deepseek-r1:1.5b", + "prompt": "1+1", + "stream": false + }' + +``` + +- 这是请求 Ollama DeepSeek 模型的 curl 操作。 + +### 2. 代码请求 + +#### 2.1 配置接口 + +```java +@Configuration +public class OllamaConfig { + + @Bean + public OllamaApi ollamaApi() { + Retrofit retrofit = new Retrofit.Builder() + .baseUrl("http://192.168.1.109:11434") + .addConverterFactory(JacksonConverterFactory.create()).build(); + + return retrofit.create(OllamaApi.class); + } + + public interface OllamaApi { + @POST("/api/generate") + Call generate(@Body OllamaRequest request); + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class OllamaRequest { + private String model; + private String prompt; + private boolean stream; + } + + // ... 省略部分代码 +} +``` + +- 这里我们使用 retrofit2 框架封装对模型API的访问。 + +#### 2.2 访问接口 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class OllamaTest { + + @Resource + private OllamaConfig.OllamaApi api; + + @Test + public void test_chat() throws IOException { + OllamaConfig.OllamaRequest request = new OllamaConfig.OllamaRequest( + "deepseek-r1:1.5b", + "1+1", + false + ); + + Call generate = api.generate(request); + Response execute = generate.execute(); + OllamaConfig.OllamaResponse response = execute.body(); + + log.info("测试结果:{}", JSON.toJSONString(response.getResponse())); + } + +} +``` + +
+ +
+ +- 如图,运行结果可以看到调用API没问题啦。 +- 另外,SpringAI 也提供了访问 ollama 的 Jar,也可以使用。后续会提供这块的内容。 \ No newline at end of file diff --git a/docs/md/road-map/openclaw.md b/docs/md/road-map/openclaw.md new file mode 100644 index 000000000..85fc8b974 --- /dev/null +++ b/docs/md/road-map/openclaw.md @@ -0,0 +1,347 @@ +--- +title: OpenClaw +lock: need +--- + +# 🦞OpenClaw,你是不还没有安装上?这个中文版,可以试试! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +🦞OpenClaw,大龙虾,看着挺复杂,其实一点也不简单。都火成这德行了,但也还是有很多伙伴在安装这一块就挂壁了。想体验呀,咋整? + +
+ +
+ +**怎么理解 OpenClaw 🦞** + +OpenClaw 是一款开源 AI 助手,不到2个月时间 [Github](https://github.com/openclaw/openclaw) 点赞量已经超过 170k Star ⭐️,它的出现为我们提供了更俏的思路使用 LLM 大模型。从24年底,定义了 MCP 协议,25年爆发了 Ai Agent 的落地,随后出现 [GLM AutoPhone](https://bugstack.cn/md/algorithm/model/autoglm-phone-agent.html) 控制手机,现在又来了大龙虾 OpenClaw 控制电脑。 + +你可以把 OpenClaw 理解为一个从你到电脑设备,需要敲键盘的过程,**设计了一个中间网关层**。你的指令可以通过各类 APP/Web 对话的方式,下发给 OpenClaw 完成电脑的一些列操作。如;`帮我安装个Docker`、`在Docker中部署下Redis`、`告诉我现在运行状况和剩余可用资源`等等操作。 + +
+ +
+ +综上,这让我们离`数字员工`又近了一步,后续可以拷贝个人工作技能到 skills 编写,如一些高频重复的场景,对现存系统运行的巡检,对运营类数据的排查,对活动经营情况的分析等。都可以被制作为 skills 让 OpenClaw 使用。 + +接下来,小傅哥就带着大家安装下 OpenClaw + 飞书配置,这套组合还是蛮好用的。 + +## 一、安装介绍 + +### 1. 软件 + +官网:[https://openclaw.ai/](https://openclaw.ai/) +源码:[https://github.com/openclaw/openclaw](https://github.com/openclaw/openclaw) + +中文社区: +- [https://clawd.org.cn/](https://clawd.org.cn/) - `新人伙伴比较推荐实用这个,全是中文提示,安装起来更友好。` +- [https://openclaw.qt.cool/](https://openclaw.qt.cool/) +- [https://1panel.cn](https://1panel.cn/) + +> 除了官网的 OpenClaw 资源,还有不少中文社区,对 OpenClaw 做了镜像和汉化的处理。安装方式也都一样,小白伙伴也可以体验下,全是中文对小白更友好。另外,这部分汉化是基于 openclaw 源码来做的,如果你有一些预安装诉求,以及汉化或者有自身公司想做些扩展也可以基于源码 fork 来改造。 + +### 2. 环境 + +- nodejs 22+ (不少安装包里会提供检测和安装)[https://nodejs.org/zh-cn/download](https://nodejs.org/zh-cn/download) +- 2c4g 云服务器/本地电脑 [https://618.gaga.plus](https://618.gaga.plus) - `推荐买1年送3个月的` + Ubuntu 24 系统。 +- 环境初始化脚本 [https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install](https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install) - `执行 sudo ./openclaw.sh 会帮你在云服务器安装 nodejs 22+` +- 镜像资源 `--registry https://registry.npmmirror.com` 如使用 `sudo npm install -g openclaw@latest --registry https://registry.npmmirror.com` + +### 3. 资源 + +- GLM ApiKey:[https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys](https://www.bigmodel.cn/glm-coding?ic=KFLKGANUMO) - 可以换账号新注册下,获取2000万token,可以提前申请好,复制到自己的文档里(你可能会失败好多次,反复使用)。 +- 飞书:[https://open.feishu.cn/app](https://open.feishu.cn/app) - `企业自建应用(下载个桌面版更好用)`,先发布一次(拿到App ID、App Secret)让 OpenClaw 对接上,之后再飞书机器人配置上**事件配置**,之后在发布一次应用。这样就建立起链接了,下文教程有详细处理过程。 + +## 二、部署介绍 + +### 1. 部署流程 + +OpenClaw 的安装过程中,会涉及一些列命令的使用,我把这些放到一个图里,方便大家理解; + +
+ +
+ +- 首先,关于安装可以使用 curl 向导式一次安装,也可以使用 npm 安装软件后,在启动配置向导。在 [xfg-dev-tech-docker-install](https://gitcode.com/Yao__Shun__Yu/xfg-dev-tech-docker-install) 一键安装脚本中,提供了 `sudo ./openclaw.sh` 安装脚本,附带的会帮助你完成 nodejs 22+ 的检测和安装,以及飞书组件的提示,会简化一些你的操作。 +- 然后,在执行 `sudo openclaw onboard` 运行向导后,会告诉你这个软件的安全提示,你可以通过`←` `→`选择 yes 安装即可。再往后,会进行模型的选择,这里支持好多模型,本案例使用的 [glm apikey](https://www.bigmodel.cn/glm-coding?ic=KFLKGANUMO) +- 之后,一系列选择后(不分可以跳过),包括通信渠道(飞书)也可以先跳过,即可完成安装。安装后可以通过 `sudo openclaw gateway` 启动网关。这样 openclaw 也就正式启动起来了。 +- 再后,安装飞书插件 `sudo openclaw plugins install @m1heng-clawd/feishu --registry https://registry.npmmirror.com` 这东西的目的是让 openclaw 可以对接上飞书。 + +### 2. 常用命令 + +| 命令 | 作用 | 备注 / 参数 | +| :----------------------------------------------------------- | :---------------------- | :-------------------------------------------- | +| `npm install -g openclaw@latest --registry https://registry.npmmirror.com` | 安装 openclaw | | +| `openclaw onboard` | 安装引导 | | +| `openclaw status` | 查看 Gateway 状态 | 检查网关是否可达及运行状况 | +| `openclaw health` | 健康检查 | 主要检测 core 运行和依赖情况 | +| `openclaw doctor` | 综合诊断与修复建议 | 可配合 `--yes` / `--non-interactive` 自动执行 | +| `openclaw configure` | 交互式配置向导 | 用于设置模型、通道、凭据等 | +| `openclaw config get ` | 获取配置值 | 指定路径提取配置 | +| `openclaw config set ` | 设置配置项 | 支持 JSON5/raw 文本 | +| `openclaw config unset ` | 清除配置项 | 移除单个键值 | +| `openclaw channels list` | 列出已登录通道 | 可观察 WhatsApp/Telegram 等登录状态 | +| `openclaw channels login` | 登录新的通道账号 | 用于扫描/授权链接 | +| `openclaw skills list` | 列出技能 | 查看可用/已安装的技能 | +| `openclaw skills info ` | 技能详情 | 观察某项技能参数或版本 | +| `openclaw plugins list` | 列出插件 | 查看已安装插件 | +| `openclaw plugins install ` | 安装插件 | 例如 @openclaw/voice-call | +| `openclaw plugins enable ` | 启用插件 | 之后通常需要重启网关 | +| `openclaw logs --follow` | 显示日志 | `--json / --plain / --limit` 等组合使用 | +| `openclaw gateway` | 启动 Gateway 网关 | | +| `openclaw gateway install` | 安装系统服务 | 根据平台注册 Gateway 守护进程 | +| `openclaw gateway start` | 启动 Gateway 网关 | 系统服务模式下启动 | +| `openclaw gateway stop` | 停止 Gateway 网关 | 同上 | +| `openclaw gateway restart` | 重启 Gateway 网关 | 适合配置变更后应用 | +| `openclaw gateway status` | 网关系统服务状态 | 不同于 `openclaw status`,会探测服务单元 | +| `openclaw uninstall` | 卸载 Gateway 服务及数据 | 官方推荐使用 | +| `openclaw uninstall --all --yes --non-interactive` | 全自动卸载 | 包含状态、workspace、插件等 | +| `openclaw uninstall --state` | 删除状态文件 | 不删除 workspace/CLI | +| `openclaw uninstall --workspace` | 删除工作区 | 移除 agent/workspace 文件 | +| `openclaw uninstall --service` | 仅卸载服务 | 不删除数据 | +| `openclaw uninstall --dry-run` | 模拟卸载 | 显示结果但不实际执行 | + +## 三、部署操作 + +### 1. 创建飞书 + +
+ +
+ +#### 1.1 创建应用 + +
+ +
+ +**批量导入,权限列表** + +```java +{ + "scopes": { + "tenant": [ + "aily:file:read", + "aily:file:write", + "application:application.app_message_stats.overview:readonly", + "application:application:self_manage", + "application:bot.menu:write", + "cardkit:card:write", + "contact:contact.base:readonly", + "contact:user.employee_id:readonly", + "corehr:file:download", + "docs:document.content:read", + "event:ip_list", + "im:chat", + "im:chat.access_event.bot_p2p_chat:read", + "im:chat.members:bot_access", + "im:message", + "im:message.group_at_msg:readonly", + "im:message.group_msg", + "im:message.p2p_msg:readonly", + "im:message:readonly", + "im:message:send_as_bot", + "im:resource", + "sheets:spreadsheet", + "wiki:wiki:readonly" + ], + "user": [ + "aily:file:read", + "aily:file:write", + "im:chat.access_event.bot_p2p_chat:read" + ] + } +} +``` + +- 创建应用后,你可以获得到应用凭证(appid、appsecret)这个信息是用于 openclaw 对接使用的。 + +#### 1.2 发布应用 + +
+ +
+ +- 创建应用后,点击发布应用。这样你创建的应用才是有效的。 + +#### 1.3 事件配置(机器人)- openclaw 配置之前 + +
+ +
+ +- 飞书应用创建后,还需要配置机器人。但这会配置也是失败的,因为 openclaw 还没有关联上来。 +- 这里需要在 openclaw 配置飞书通信渠道,之后重启 openclaw 网关,这里点击保存并配置权限(`接收消息`、`通讯录基本信息`),点击确认开通权限。在发布一个新应用,再和飞书机器人对话就可以使用了。 + +### 2. 安装应用(openclaw) + +#### 2.1 执行脚本(含带引导) + +```java +curl -fsSL https://openclaw.ai/install.sh | sudo bash -s -- --registry https://registry.npmmirror.com +``` + +- openclaw 官网版 + +```java +curl -fsSL https://clawd.org.cn/install.sh | sudo bash -s -- --registry https://registry.npmmirror.com +``` + +- openclaw-cn 中文社区版,适合新人伙伴使用。熟练后,使用官网版即可。他们操作的方式是一样的。 +- 安装后的使用差异,一个是 `sudo openclaw config` 一个是 `sudo openclaw-cn config` + +#### 2.2 引导配置 + +```java +sudo npm install -g openclaw@latest --registry https://registry.npmmirror.com +sudo npm install -g openclaw-cn@latest --registry https://registry.npmmirror.com + +# 运行向导 +sudo openclaw onboard +sudo openclaw-cn onboard +``` + +- 注意 `-cn` 为中文版,如果你不是执行的 curl 一键安装版本,是采用了 安装 `openclaw@latest` 程序,那么需要自己执行引导配置。 + +##### 2.2.1 安全提示 + +
+ +
+ +##### 2.2.2 配置方式 + +
+ +
+ +##### 2.2.3 模型配置 + +
+ +
+ +
+ +
+ +##### 2.2.4 通信通道(飞书) + +**选择渠道** + +
+ +
+ +**安装插件** + +
+ +
+ +- 如果未安装过飞书插件,可以选择安装,也可以跳过后,后续在安装。 +- 单独安装插件 `sudo openclaw-cn plugins install @m1heng-clawd/feishu` + +**配置凭证** + +
+ +
+ +- 把你的应用凭证,配置到 openclaw 应用程序里。之后继续回车。 +- 注意,一会要会到飞书页面,配置事件关联到 openclaw 上。 + +##### 2.2.5 技能配置 + +
+ +
+ +##### 2.2.6 部署完成 + +
+ +
+ +
+ +
+ +```java +ubuntu@VM-0-2-ubuntu:~$ sudo openclaw-cn gateway +``` + +- 配置完成后,要启动下网关。能看到以上信息,表示运行没问题了。 +- 现在要到 3.1 接着配置飞书。 + +### 3. 配置飞书 + +#### 3.1 事件配置(机器人) + +
+ +
+ +- 注意,完成 2.2.6 步骤后,再回来操作。 +- openclaw 关联上飞书以后,点击**添加事件**,以及添加上相关权限。完成后,会提示你发布一个新应用。 + +#### 3.2 发布应用 + +
+ +
+ +- 点击发布应用,版本号 +1 + +#### 3.3 打开应用 + +
+ +
+ +- 这会对话飞书,会提示执行一个命令,类似于验证签名。 +- 命令 `sudo openclaw-cn pairing approve feishu KF2BRAXW` + +#### 3.4 验证前面(+重启网关) + +
+ +
+ +```java +ubuntu@VM-0-2-ubuntu:~$ sudo openclaw-cn pairing approve feishu KF2BRAXW + +🦞 Clawdbot-CN 0.1.4 (b161cdd) — 唯一不能在你的私信上训练的机器人Mark。 + +Approved feishu sender ou_762197513d3d1cb907f4c0d2ed3c3b2b. +ubuntu@VM-0-2-ubuntu:~$ sudo openclaw-cn gateway + +🦞 Clawdbot-CN 0.1.4 (b161cdd) — 我不是魔法——我只是在重试和应对策略上极其执着。 + +06:39:38 [canvas] host mounted at http://127.0.0.1:18789/__clawdbot__/canvas/ (root /root/clawd/canvas) +06:39:38 [heartbeat] started +06:39:38 [gateway] agent model: zai/glm-4.7 +06:39:38 [gateway] listening on ws://127.0.0.1:18789 (PID 20687) +06:39:38 [gateway] listening on ws://[::1]:18789 +06:39:38 [gateway] log file: /tmp/clawdbot/clawdbot-2026-02-08.log +06:39:38 [browser/server] Browser control listening on http://127.0.0.1:18791/ +06:39:38 [feishu] [default] starting Feishu provider (你的应用名称) +06:39:38 [info]: [ 'event-dispatch is ready' ] +``` + +- 如上方式,执行你的签名验证,并重启网关。 +- 到这,就配置完成了,可以使用了。 + +## 四、使用体验 + +
+ +
+ +现在你可以发挥想象的让 openclaw 帮你做事情了,很多运维的活,他也都可以完成。美滋滋! diff --git a/docs/md/road-map/portainer.md b/docs/md/road-map/portainer.md new file mode 100644 index 000000000..05872d792 --- /dev/null +++ b/docs/md/road-map/portainer.md @@ -0,0 +1,78 @@ +--- +title: Docker 管理面板(Portainer) +lock: need +--- + +# Docker 管理面板(Portainer) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +- 官网:[https://www.portainer.io/](https://www.portainer.io/) +- 介绍:在任何数据中心、云、网络边缘或 IIOT 设备的 Kubernetes、Docker、Swarm 和 Nomad 上,在几分钟内部署、配置、故障排除和保护容器。 + +## 一、基础安装 + +### 1. 拉取最新的 Portainer + +```java +[root@CodeGuide portainer]# docker pull portainer/portainer +Using default tag: latest +latest: Pulling from portainer/portainer +94cfa856b2b1: Pull complete +49d59ee0881a: Pull complete +a2300fd28637: Pull complete +Digest: sha256:fb45b43738646048a0a0cc74fcee2865b69efde857e710126084ee5de9be0f3f +Status: Downloaded newer image for portainer/portainer:latest +docker.io/portainer/portainer:latest +``` + +- 默认镜像:`docker pull portainer/portainer` +- 代理镜像:`docker pull registry.cn-hangzhou.aliyuncs.com/xfg-studio/portainer:latest` +- 拉取 portainer + +### 2. 安装和启动 + +**注意**:如果是阿里云服务器,还需要先执行 `docker volume crete portainer_data` + +```java +[root@CodeGuide]# docker run -d --restart=always --name portainer -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer +``` + +代理安装(推荐):`docker run -d --restart=always --name portainer -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock registry.cn-hangzhou.aliyuncs.com/xfg-studio/portainer:latest` + +### 3. 访问 Portainer + +- 地址:[http://39.96.*.*:9000/](#) +- 操作:登录后设置你的用户名和密码,并设置本地Docker即可,设置完成后,如下 + +
+ +
+ +
+ +
+ +## 二、链接服务 + +地址:[http://180.76.119.142:9000/#!/wizard/endpoints/create?envType=dockerStandalone](http://180.76.119.142:9000/#!/wizard/endpoints/create?envType=dockerStandalone) + +
+ +
+ +```shell script +docker run -d \ + -p 9001:9001 \ + --name portainer_agent \ + --restart=always \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v /var/lib/docker/volumes:/var/lib/docker/volumes \ + portainer/agent:2.16.2 +``` + diff --git a/docs/md/road-map/private-docker-hub.md b/docs/md/road-map/private-docker-hub.md new file mode 100644 index 000000000..ed3a35484 --- /dev/null +++ b/docs/md/road-map/private-docker-hub.md @@ -0,0 +1,380 @@ +--- +title: Docker 私有仓库 +lock: need +--- + +# 构建 DockerHub 私有镜像仓库,通过 GitHub Actions 推送镜像 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +这不稳,那不行。这鲜族人过年,要了狗命了!但凡真正进入开发快车道的伙伴,就几乎离不开 DockerHub 的使用,包括拉取 mysql、redis、kafka、nacos 等环境镜像,也包括自己的应用构建镜像。不少伙伴都哭喊,这没法用,写代码都不香啦! + +
+ +
+ +**有没有那种,风雨无阻都很稳的办法呢!** + +6月份的时候小傅哥写了一篇基于 GitHub Actions 推送镜像到阿里云私有镜像仓库的教程,但最近看到阿里云要对自身的私有仓库做出调整,以后也不在为所有环境全量的提供镜像加速了。不过目前还能用,但不知道还能用多久。所以,未雨绸缪啊!万一又用不了了,怪麻烦的。 + +嘿嘿,不过也不用太担心。因为我们还有方案!那就是自建私有镜像仓库,替代阿里云。在这套方案中,虽然我们的云服务器不能直接拉取 Docker Hub 仓库中镜像,但是 GitHub Actions 可以呀,不仅可以还能让 GitHub Actions 执行脚本把拉取下来的镜像推送到我们在自己云服务器上搭建的私有镜像仓库。这不美滋滋了吗!任何时候你想用就用,而且你可以只给自己用。 + +>接下来,小傅哥就教你怎么做这个事情。—— 学到手的全是技术! + +## 一、私有镜像仓库 + +如果你是一个小公司,或者是一个小组织,那么 Docker Hub 私有镜像仓库是非常适合你使用的,它可以避免你的应用镜像对外,也可以固定范围的拉取可靠镜像。并且私有镜像仓库的搭建也是非常简单的,就一行代码的事。 + +### 1. 安装脚本 + +```java +# 命令执行 docker-compose -f docker-compose.yml up -d +version: '3.8' +services: + # docker run -dit --restart=always --name=docker-registry -p 5000:5000 -v /docker/var/lib/registry:/var/lib/registry library/registry:latest + registry: + image: library/registry:latest + container_name: docker-registry + restart: always + ports: + - "5000:5000" + volumes: + - /docker/var/lib/registry:/var/lib/registry +``` + +**安装方式** - 这个过程还是需要拉取一次镜像的,可以找一些镜像仓库或者让其他伙伴提供下它的私有镜像仓库地址。 + +- 方式1;`docker-compose -f docker-compose.yml up -d` +- 方式2;`docker run -dit --restart=always --name=docker-registry -p 5000:5000 -v /docker/var/lib/registry:/var/lib/registry library/registry:latest` + +### 2. 镜像配置 + +```java +sudo mkdir -p /etc/docker +sudo tee /etc/docker/daemon.json <<-'EOF' +{ + "registry-mirrors": [ + "https://dc.j8.work" + ], + "insecure-registries":["116.198.201.187:5000"] +} +EOF +sudo systemctl daemon-reload +sudo systemctl restart docker +``` + +- IP 为你的云服务器 IP 地址,5000 端口为默认端口,记得云服务器要防火墙开放。 +- 如上,`116.198.201.187:5000` 替换为你的地址。 + +### 3. 常用命令 + +#### 3.1 查看私有镜像仓库中存在的镜像文件 + +```java +[root@lavm-cnqkgk85q4 ~]# curl 116.198.201.187:5000/v2/_catalog +{"repositories":["kafka","kafka-eagle","mysql","phpmyadmin","redis","registry"]} +``` + +#### 3.2 查看指定的镜像版本 + +```java +[root@lavm-cnqkgk85q4 ~]# curl 116.198.201.187:5000/v2/redis/tags/list +{"name":"redis","tags":["6.2","latest"]} +``` + +#### 3.3 拉取镜像 + +```java +docker pull 116.198.201.187:5000/redis +docker pull 116.198.201.187:5000/redis:6.2 +docker pull 116.198.201.187:5000/redis:latest +``` + +- 从私有的镜像仓库拉取镜像文件。 + +#### 3.4 推送镜像 + +```java +docker push 116.198.201.187:5000/mysql:latest +``` + +- 推送镜像,这个命令很有用,后面在 GitHub Actions 中会使用到。 + +## 二、GitHub Actions 脚本 + +
+ +
+ +- 地址:[https://github.com/fuzhengwei/docker-image-pusher](https://github.com/fuzhengwei/docker-image-pusher) - 你可以 fork 使用。 +- 注意:需要配置 Actions,下文中会说明。 + +### 1. 简单示意 + +```java +name: Pull and Push MySQL Docker Image + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Log in to Docker Hub + run: echo "${{ secrets.DOCKER_HUB_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_HUB_USERNAME }}" --password-stdin + + - name: Configure Docker to use HTTP for private registry + run: | + echo '{"insecure-registries":["116.198.201.187:5000"]}' | sudo tee /etc/docker/daemon.json + sudo systemctl restart docker + + - name: Pull MySQL image from Docker Hub + run: docker pull mysql:latest + + - name: Tag MySQL image for private registry + run: docker tag mysql:latest 116.198.201.187:5000/mysql:latest + + - name: Push MySQL image to private registry + run: docker push 116.198.201.187:5000/mysql:latest +``` + +- 这是一段 GitHub Actions 脚本,用于配置到 GitHub 仓库使用。下文会有让你 fork 工程和配置使用的方式。 +- 此脚本的作用在于从GitHub 仓库拉取镜像推送到我们自建的私有镜像仓库中。—— 受带宽和网络影响,推送过程会稍微慢一些。 +- 那么,有了这么一个可以配置镜像 `docker pull mysql:latest` 拉取和推送的操作,我们就也可以配置一个 images.txt 文件放到工程下,只要修改这个里的文件,就自动完成推送。岂不是美滋滋! + +### 2. 动态脚本 + +在之前小傅哥看到一个 `@技术爬爬虾` 的大佬分享了个 GitHub Actions 推送镜像到阿里云私有仓库,小傅哥修改为推送到自己的仓库了。一些相关配置,也可以从 `@技术爬爬虾` 这里学习下。[https://github.com/tech-shrimp/docker_image_pusher](https://github.com/tech-shrimp/docker_image_pusher) + +```java +name: Docker + +on: + workflow_dispatch: + push: + branches: [ main ] + +env: + PRIVATE_REGISTRY: "${{ secrets.PRIVATE_REGISTRY }}" + +jobs: + + build: + name: Pull + runs-on: ubuntu-latest + steps: + - name: Before freeing up disk space + run: | + echo "Before freeing up disk space" + echo "==============================================================================" + df -hT + echo "==============================================================================" + + # 增加可用磁盘空间 + - name: Maximize build space + uses: easimon/maximize-build-space@master + with: + root-reserve-mb: 2048 + swap-size-mb: 128 + remove-dotnet: 'true' + remove-haskell: 'true' + # 如果空间还是不够用,可以把以下开启,清理出更多空间 + # remove-android: 'true' + # remove-codeql: 'true' + build-mount-path: '/var/lib/docker/' + + - name: Restart docker + run: sudo service docker restart + + - name: Free up disk space complete + run: | + echo "Free up disk space complete" + echo "==============================================================================" + df -hT + echo "==============================================================================" + + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Docker Setup Buildx + uses: docker/setup-buildx-action@v3 + + - name: Configure Docker to use HTTP for private registry + run: | + echo "{\"insecure-registries\":[\"$PRIVATE_REGISTRY\"]}" | sudo tee /etc/docker/daemon.json + sudo systemctl restart docker + + - name: Build and push image to private registry + run: | + # 数据预处理,判断镜像是否重名 + declare -A duplicate_images + declare -A temp_map + while IFS= read -r line || [ -n "$line" ]; do + # 忽略空行与注释 + [[ -z "$line" ]] && continue + if echo "$line" | grep -q '^\s*#'; then + continue + fi + + # 获取镜像的完整名称,例如kasmweb/nginx:1.25.3(命名空间/镜像名:版本号) + image=$(echo "$line" | awk '{print $NF}') + # 将@sha256:等字符删除 + image="${image%%@*}" + echo "image $image" + # 获取镜像名:版本号 例如nginx:1.25.3 + image_name_tag=$(echo "$image" | awk -F'/' '{print $NF}') + echo "image_name_tag $image_name_tag" + # 获取命名空间 例如kasmweb, 这里有种特殊情况 docker.io/nginx,把docker.io当成命名空间,也OK + name_space=$(echo "$image" | awk -F'/' '{if (NF==3) print $2; else if (NF==2) print $1; else print ""}') + echo "name_space: $name_space" + # 这里不要是空值影响判断 + name_space="${name_space}_" + # 获取镜像名例如nginx + image_name=$(echo "$image_name_tag" | awk -F':' '{print $1}') + echo "image_name: $image_name" + + # 如果镜像存在于数组中,则添加temp_map + if [[ -n "${temp_map[$image_name]}" ]]; then + # 如果temp_map已经存在镜像名,判断是不是同一命名空间 + if [[ "${temp_map[$image_name]}" != $name_space ]]; then + echo "duplicate image name: $image_name" + duplicate_images[$image_name]="true" + fi + else + # 存镜像的命名空间 + temp_map[$image_name]=$name_space + fi + done < images.txt + + while IFS= read -r line || [ -n "$line" ]; do + + # 忽略空行与注释 + [[ -z "$line" ]] && continue + if echo "$line" | grep -q '^\s*#'; then + continue + fi + + echo "docker pull $line" + docker pull $line + platform=$(echo "$line" | awk -F'--platform[ =]' '{if (NF>1) print $2}' | awk '{print $1}') + echo "platform is $platform" + # 如果存在架构信息 将架构信息拼到镜像名称前面 + if [ -z "$platform" ]; then + platform_prefix="" + else + platform_prefix="${platform//\//_}_" + fi + echo "platform_prefix is $platform_prefix" + # 获取镜像的完整名称,例如kasmweb/nginx:1.25.3(命名空间/镜像名:版本号) + image=$(echo "$line" | awk '{print $NF}') + + # 获取 镜像名:版本号 例如nginx:1.25.3 + image_name_tag=$(echo "$image" | awk -F'/' '{print $NF}') + # 获取命名空间 例如kasmweb 这里有种特殊情况 docker.io/nginx,把docker.io当成命名空间,也OK + name_space=$(echo "$image" | awk -F'/' '{if (NF==3) print $2; else if (NF==2) print $1; else print ""}') + # 获取镜像名例 例如nginx + image_name=$(echo "$image_name_tag" | awk -F':' '{print $1}') + + name_space_prefix="" + # 如果镜像名重名 + if [[ -n "${duplicate_images[$image_name]}" ]]; then + #如果命名空间非空,将命名空间加到前缀 + if [[ -n "${name_space}" ]]; then + name_space_prefix="${name_space}_" + fi + fi + + # 将@sha256:等字符删除 + image_name_tag="${image_name_tag%%@*}" + new_image="$PRIVATE_REGISTRY/$platform_prefix$name_space_prefix$image_name_tag" + latest_image="$PRIVATE_REGISTRY/$platform_prefix$name_space_prefix$image_name:latest" + + echo "docker tag $image $new_image" + docker tag $image $new_image + echo "docker push $new_image" + docker push $new_image + + echo "docker tag $image $latest_image" + docker tag $image $latest_image + echo "docker push $latest_image" + docker push $latest_image + + echo "开始清理磁盘空间" + echo "==============================================================================" + + df -hT + echo "==============================================================================" + docker rmi $image + docker rmi $new_image + echo "磁盘空间清理完毕" + echo "==============================================================================" + df -hT + echo "==============================================================================" + + done < images.txt +``` + +- 此脚本的用途在于把从 Docker Hub 拉取的镜像推送到自己的私有仓库中。 +- 这里有一个 PRIVATE_REGISTRY 就是你的私有仓库地址 `116.198.201.187:5000`,你需要配置到 GitHub Actions 中。 + +## 三、安装使用 + +### 1. 工程 Fork + +
+ +
+ +### 2. 配置 Actions secret + +
+ +
+ +- key:PRIVATE_REGISTRY +- value:你的镜像仓库地址 + +### 3. images.txt + +在 images.txt 提交你需要 push 的镜像,如; + +```java +phpmyadmin:5.2.1 +redis:6.2 +``` + +### 4. 观察执行 + +
+ +
+ +- 推送成功后,你可以在 Actions 中查看镜像推送过程。 + +### 5. 镜像使用 + +
+ +
+ +```java +docker pull 116.198.201.187:5000/redis +docker tag 116.198.201.187:5000/redis my-redis +``` + +- 在上文中,有使用私有镜像仓库的脚本,现在你可以使用了。 +- 拉取的镜像会带有前缀,116.198.201.187:5000 这个时候你可以重新 tag 下,这样就和你的 docker compose 符合了。 diff --git a/docs/md/road-map/quartz.md b/docs/md/road-map/quartz.md new file mode 100644 index 000000000..ec731f695 --- /dev/null +++ b/docs/md/road-map/quartz.md @@ -0,0 +1,429 @@ +--- +title: Quartz & XXL-Job +lock: need +--- + +# Quartz、Spring-Schedule、XXL-Job 使用教程和扩展开发 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,多种类型的任务执行组件使用案例,包括:Quartz 使用、扩展 Spring-Schedule 自动增加任务、XXL-Job 分布式任务调度。其中像 Spring-Schedule 小傅哥还添加了一些 Spring 组件开发的能力可自动扩展任务、对 XXL-Job 的配置引入了 Docker Compose 自动化安装和自动初始化 MySQL 数据库 xxl-job.sql 库表数据。这些都是为了让你在不同的场景选择合适的框架,同时也能更简单的使用这些框架。 + +本章节的任务调度组件会放到 DDD 的 Trigger 模块中,也就是触发器层。我们认为所有的调用行为,HTTP、RPC、MQ、任务,都是一个触发的入口,所以对于任务调度也放到这一层使用。 + +本文涉及的工程: + +- xfg-dev-tech-quartz:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-quartz](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-quartz) +- xxl-job-docker.compose.yml 安装:[xxl-job-docker.compose.yml](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-quartz/-/blob/master/docs/xxl-job/xxl-job-docker-compose.yml) - `xxl-job 已提供了最简化安装自动导入库表操作` + +## 一、案例背景 + +任务调度是一个非常重要的功能组件,常作用于:定时清理数据 - 冷数据迁移、活动状态扫描 - 过期活动关闭、消息发送补偿 - MQ失败重发、支付掉单补偿 - 支付幂等重试,等各类场景都会用到任务调度组件。它可以帮我们执行确定规则的业务或功能流程。 + +
+ +
+ +- 以整个 DDD 分层架构中,以触发器层为入口编写任务调度方法。任务的实现方式有多种,如果你的场景较为简单,则使用 Spring 或者 Quartz 提供的任务实现方式即可。如果你的场景较为复杂,需要分布式任务管理,那么最好配置一套如 XXL-Job 这样的分布式任务调度组件来使用。 +- 所有的触发器中的任务,都只是固定时间频次下的执行入口,最终需要调用领域层所提供的方法完成具体的业务逻辑。如果你使用 DDD 分层有 case/application 防腐处理,则会调用这一编排层,而不是 domain 领域层。 + +## 二、任务模型 + +当你的微服务应用是一组较小的模型结构时,其实任务与服务结合在一起即可,让它与自己的领域绑定。但如果微服务的体量很大,那么这组微服务所对应的任务也会较多,同时需要一些分布式的能力,让调度的算力可以更快更好的运用起来。 + +所以一般这个时候就需要引入把任务单独拆分出一个微服务系统,一般可以叫做 xxx-worker 系统,他们就是专门处理任务的一个个执行器。把这些执行器注册到任务调度中心,由任务调度中心统一管理各项任务的执行。这样如果有一个任务在一个算力执行器上失败或者说执行器宕机了,那么可以把任务迁移到其他算力执行器上执行。这就是分布式的好处。 + +
+ +
+ +- 如图,就是分布式架构下。执行器系统被任务调度中心管理,调用微服务提供的接口,完成对微服务接口的调用。 +- 一般分布式引用的微服务接口,也都是 RPC 接口,这样就已经具备了负载能力。 +- 任务调度与 MQ 消息是一组非常常用的技术栈组合,MQ 失败的消息,经常是由任务扫描补偿,继续发送MQ消息,驱动流程的执行。 + +## 三、环境安装 + +本案例所需安装的环境主要是 XXL-Job 的一套 MySQL 库和 XXL-Job 应用以及对应的库表初始化。为了让大家使用起来更加简单,小傅哥这里提供了一套 compose.yml 支持 AMD 和 ARM 架构使用。 + +
+ +
+ +- 在此位置找到执行文件,如果你本机已经安装过 [Docker](https://bugstack.cn/md/road-map/docker.html) 那么在 IntelliJ IDEA 中直接执行即可。 + +### 1. 执行 compose.yml + +文件:[docs/xxl-job/xxl-job-docker-compose.yml](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-quartz/-/blob/master/docs/xxl-job/xxl-job-docker-compose.yml) + +```yml +# 命令执行 docker-compose up -d +version: '3.9' +services: + # http://127.0.0.1:9090/xxl-job-admin admin/123456 - 安装后稍等会访问即可 + # 官网镜像为 xuxueli/xxl-job-admin 但不支持ARM架构【需要自己打包】,所以找了一个 kuschzzp/xxl-job-aarch64:2.4.0 镜像支持 AMD/ARM + xxl-job-admin: + image: kuschzzp/xxl-job-aarch64:2.4.0 + container_name: xxl-job-admin + restart: always + depends_on: + - mysql + ports: + - "9090:9090" + links: + - mysql + volumes: + - ./data/logs:/data/applogs + - ./data/xxl-job/:/xxl-job + environment: + - SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/xxl_job?serverTimezone=UTC&characterEncoding=utf8&autoReconnect=true&serverTimezone=Asia/Shanghai + - SPRING_DATASOURCE_USERNAME=root + - SPRING_DATASOURCE_PASSWORD=123456 + - SERVER_PORT=9090 + + # MySQL 8.0.32 支持 AMD/ARM + mysql: + image: mysql:8.0.32 + container_name: mysql + command: --default-authentication-plugin=mysql_native_password + restart: always + environment: + TZ: Asia/Shanghai + # MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' 可配置无密码,注意配置 SPRING_DATASOURCE_PASSWORD= + MYSQL_ROOT_PASSWORD: 123456 + MYSQL_USER: xfg + MYSQL_PASSWORD: 123456 + depends_on: + - mysql-job-dbdata + ports: + - "13306:3306" # 如果你无端口占用,可以直接使用 3306 + volumes: + - ./sql:/docker-entrypoint-initdb.d + volumes_from: + - mysql-job-dbdata + + # 自动加载数据 + mysql-job-dbdata: + image: alpine:3.18.2 + container_name: mysql-job-dbdata + volumes: + - /var/lib/mysql +``` + +
+ +
+ +- 在 IDEA 中打开 xxl-job-docker-compose.yml 你会看到一个绿色的按钮在左侧侧边栏,点击即可安装。或者你也可以使用命令安装:`# /usr/local/bin/docker-compose -f /docs/xxl-job/xxl-job-docker-compose.yml up -d` - 比较适合在云服务器上执行。 +- 在 compose 中提供了 xxl-job 所需要的库的依赖安装,以及自动加载文件下的初始化库表数据。这个库表数据来自于 xxl-job sql:[https://gitee.com/xuxueli0323/xxl-job/blob/master/doc/db/tables_xxl_job.sql](https://gitee.com/xuxueli0323/xxl-job/blob/master/doc/db/tables_xxl_job.sql) - `这里小傅哥把 SQL 文件下载到了本地,用于初始化安装使用` +- 标签:`depends_on` - 依赖于谁先安装、`MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'` - 可以设置MySQL无密码安装、`mysql-job-dbdata` - 一个启动安装数据库初始化脚本的镜像。并且需要在 MySQL 安装时使用 `volumes_from` 标签引入。 + +### 2. 访问 xxl-job + +**地址**:[http://127.0.0.1:9090/xxl-job-admin](http://127.0.0.1:9090/xxl-job-admin) - admin/123456 - 安装后稍等启动完成,就可以访问啦。 + +
+ +
+ +- 默认的账号 admin 密码 123456 + +### 3. 执行器管理 + +执行器的作用,就是让 xxl-job-admin 这个任务调度系统,调用注册上来的执行器完成任务的执行。客户端需要配置好这里的执行器名称才能注册上来。你可以根据自己的需要新增新的执行器,也可以在测试的时候使用默认的这个执行器名称。 + +
+ +
+ +- 本地服务启动后,会注册进来一个执行器的地址,OnLine 机器地址会显示。 + +### 4. 任务配置 + +任务的作用,就是执行器下具体的执行方法,按照配置的时间下发到任务中执行。 + +```java +@Slf4j +@Component +public class XXLJob { + + @XxlJob("demoJobHandler") + public void doJob() { + // 可以在任务中,调用一些业务方法逻辑的实现,如定时扫描超时未支付订单为关单处理,恢复库存 + log.info("执行任务 - XXL-Job - 01"); + } + +} +``` + +
+ +
+ +- 一个执行器下管理的任务一般会有很多,所以你在测试的时候也可以尝试新增一些任务来测试。 + +## 四、工程实现 + +### 1. 工程结构 + +
+ +
+ +- 首先,trigger 触发器模块下有3类任务,分别是 Quartz、Schedule 和 XXL-Job 分布式任务。XXL-Job 所需的配置会多一些,需要 application-dev.yml 配置 xxl-job 参数,之后配置 Config 启动任务。最后是 XXL-Job 使用任务。 +- QuartzJob 就是一个直接使用的案例,但像 XXL-Job 也是基于 Quartz 扩展的,小傅哥也做了一个分布式任务调度的中间件,如果感兴趣也可以学习。[https://bugstack.cn/md/assembly/middleware/%E7%AC%AC%2015%20%E7%AB%A0%20%E5%88%86%E5%B8%83%E5%BC%8F%E4%BB%BB%E5%8A%A1%E8%B0%83%E5%BA%A6.html](https://bugstack.cn/md/assembly/middleware/%E7%AC%AC%2015%20%E7%AB%A0%20%E5%88%86%E5%B8%83%E5%BC%8F%E4%BB%BB%E5%8A%A1%E8%B0%83%E5%BA%A6.html) +- ScheduleJob 是基于 Spring 提供的 Schedule 进行的扩展,可以根据自定义注解添加任务和自动启动。这个方式也是非常好用的。 + +### 2. 配置文件 + +**引入POM** + +```pom + + + org.quartz-scheduler + quartz + 2.3.2 + + + + + org.springframework.boot + spring-boot-starter-quartz + 3.1.2 + + + + + com.xuxueli + xxl-job-core + 2.4.0 + +``` + +- 分别包括:Quartz、XXL-Job 两个组件 + +**添加配置** + +```yml +# xxl-job https://www.xuxueli.com/xxl-job/#%E6%AD%A5%E9%AA%A4%E4%B8%80%EF%BC%9A%E8%B0%83%E5%BA%A6%E4%B8%AD%E5%BF%83%E9%85%8D%E7%BD%AE%EF%BC%9A +xxl: + job: + # 验证信息 官网Bug https://github.com/xuxueli/xxl-job/issues/1951 + accessToken: default_token + # 注册地址 + admin: + addresses: http://localhost:9090/xxl-job-admin + # 注册执行器 + executor: + # 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。 + address: + appname: xxl-job-executor-sample + # 执行器IP 配置为本机IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; + ip: + # 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; + port: 9999 + # 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; + logpath: ./data/applogs/xxl-job/jobhandler + # 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能; + logretentiondays: 30 +``` + +xxl-job 有一些必要的配置信息 +- accessToken,默认是一个 default_token - 它的官网也有 issue 提到这个bug,如果未配置,应该为空。[https://github.com/xuxueli/xxl-job/issues/1951](https://github.com/xuxueli/xxl-job/issues/1951) +- addresses,是一个注册地址,也就是我们访问 xxl-job 的地址 +- appname,我们这里使用了官网默认提供的执行器的名称 xxl-job-executor-sample 你可以新增或者修改。 +- ip、port 意思是你把本地的执行器注册到调度中心,如果你的 XXL-Job 部署到云服务器,而本地启动服务的时候,你是可以注册到服务端的,但调度中心没法调用到你本地的服务,因为你本地没有公网IP。这个时候你可以使用 natapp 做映射内网穿透进行测试。 + +### 3. 任务配置 + +#### 3.1 Quartz 任务 + +**源码**:`cn.bugstack.xfg.dev.tech.job.QuartzJob` + +```java +@Slf4j +@Component() +public class QuartzJob { + + @Scheduled(cron = "0/3 * * * * ?") + public void execute01() { + // 可以在任务中,调用一些业务方法逻辑的实现,如定时扫描超时未支付订单为关单处理,恢复库存 + log.info("执行任务 - Quartz - 01"); + } + + @Scheduled(cron = "0/3 * * * * ?") + public void execute02() { + // 可以在任务中,调用一些业务方法逻辑的实现,如定时扫描超时未支付订单为关单处理,恢复库存 + log.info("执行任务 - Quartz - 02"); + } + +} +``` + +- Quartz 支持在一个类中,配置多个任务,每个任务方法都可以配置自己执行策略。 +- 此类方式非常适合一些不需要统一任务调度的简单场景使用。 + +#### 3.2 Spring-Schedule 扩展任务 + +**配置任务注册器** - 在 app config 下 + +```java +@Slf4j +@Configuration +@EnableScheduling +public class JobRegistrarAutoConfig implements SchedulingConfigurer { + + private final ApplicationContext applicationContext; + + public JobRegistrarAutoConfig(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Override + public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { + Map jobBeanMap = applicationContext.getBeansOfType(ExtScheduleJob.class); + Collection jobBeans = jobBeanMap.values(); + for (ExtScheduleJob job : jobBeans) { + ExtScheduleJobConfig extScheduleJobConfig = AnnotationUtils.findAnnotation(job.getClass(), ExtScheduleJobConfig.class); + if (extScheduleJobConfig == null || !extScheduleJobConfig.state()) continue; + + log.info("启动任务 {} {}", extScheduleJobConfig.jobName(), extScheduleJobConfig.cronExpression()); + taskRegistrar.addCronTask(job, extScheduleJobConfig.cronExpression()); + } + } + +} +``` + +- JobRegistrarAutoConfig 实现了 SchedulingConfigurer 的类,可以自己自动化的根据所有实现了 ExtScheduleJob 类进行任务扩展添加。 +- 这是一种扩展方式,有了这样的扩展方式,如果你是做同类的任务需求,只是配置不同的话,那么还可以基于 yml 配置,来创建出不同的代理任务。 + +#### 3.3 XXL-Job + +**源码**:`cn.bugstack.xfg.dev.tech.job.XXLJob` + +```java +@Slf4j +@Component +public class XXLJob { + + @XxlJob("demoJobHandler") + public void doJob() { + // 可以在任务中,调用一些业务方法逻辑的实现,如定时扫描超时未支付订单为关单处理,恢复库存 + log.info("执行任务 - XXL-Job - 01"); + } + +} +``` + +
+ +
+ +- 注意到这里的 demoJobHandler 就是在 [http://127.0.0.1:9090/xxl-job-admin/jobinfo](http://127.0.0.1:9090/xxl-job-admin/jobinfo) 配置的执行方法名称。 +- 你用配置了什么注解的名称,就新增对应的名称即可。 + +## 五、工程测试 + +- QuartzJob - 启动工程即可运行 +- ScheduleJob - 注解中,state = false 是不运行,否则直接运行。 +- XXLJob - 需要由 [http://127.0.0.1:9090/xxl-job-admin/jobinfo](http://127.0.0.1:9090/xxl-job-admin/jobinfo) 进行测试或者启动任务。 + +
+ +
+ +```java +23-08-05.14:19:42.003 [pool-2-thread-1 ] INFO QuartzJob - 执行任务 - Quartz - 01 +23-08-05.14:19:42.003 [pool-2-thread-1 ] INFO ScheduleJob - 执行任务 - Schedule - 01 +23-08-05.14:19:42.060 [xxl-job, JobThread-1-1691216327906] INFO XXLJob - 执行任务 - XXL-Job - 01 +23-08-05.14:19:45.003 [pool-2-thread-1 ] INFO QuartzJob - 执行任务 - Quartz - 02 +23-08-05.14:19:45.003 [pool-2-thread-1 ] INFO QuartzJob - 执行任务 - Quartz - 01 +23-08-05.14:19:45.004 [pool-2-thread-1 ] INFO ScheduleJob - 执行任务 - Schedule - 01 +23-08-05.14:19:45.041 [xxl-job, JobThread-1-1691216327906] INFO XXLJob - 执行任务 - XXL-Job - 01 +``` + +- 注意编辑任务的执行时间,`0/3 * * * * ?` 这样才能当下执行。另外如果你要测试的话,可以点**执行一次**。 +- 现在是启动了多个测试任务,所以测试中可以看到各类任务的打印。读者在做测试的时候,可以适当关闭,方便学习。 + +## 六、扩展学习 JobRunr + +官网:[jobrunr](https://github.com/jobrunr/jobrunr) - `一种在 Java 中执行后台处理的巧妙简单的方法。由持久存储支持。开放并免费用于商业用途。` + +### 1. 安装部署 + +```yml +version: '3' +services: + jobrunr: + image: jobrunr/server:latest + ports: + - 8000:8000 + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/jobrunrdb + - SPRING_DATASOURCE_USERNAME=jobrunr + - SPRING_DATASOURCE_PASSWORD=jobrunr + depends_on: + - postgres + networks: + - jobrunr-network + + postgres: + image: postgres:latest + environment: + - POSTGRES_USER=jobrunr + - POSTGRES_PASSWORD=jobrunr + - POSTGRES_DB=jobrunrdb + volumes: + - ./pgdata:/var/lib/postgresql/data + networks: + - jobrunr-network + +networks: + jobrunr-network: +``` + +## 2. 使用案例 + +```java +// 即发即忘任务 +BackgroundJob.enqueue(() -> System.out.println("Simple!")); + +// 延迟的任务 +BackgroundJob.schedule(Instant.now().plusHours(5), () -> System.out.println("Reliable!")); + +// 重复的任务 +BackgroundJob.scheduleRecurrently("my-recurring-job", Cron.daily(), () -> service.doWork()); + +// 配置的任务 +@Component +public class MyJobService { + + private final JobScheduler jobScheduler; + + @Autowired + public MyJobService(JobScheduler jobScheduler) { + this.jobScheduler = jobScheduler; + } + + public void scheduleJob() { + jobScheduler.enqueue(job -> job + .setJobDetails(MyJob.class) + .withName("My Job") + .withArgument("arg1", "value1") + .withArgument("arg2", "value2") + ); + } + + @Job(name = "My Job") + public void processJob(String arg1, String arg2) { + // 处理作业的逻辑 + System.out.println("Processing job with arguments: " + arg1 + ", " + arg2); + } +} +``` diff --git a/docs/md/road-map/rabbitmq.md b/docs/md/road-map/rabbitmq.md new file mode 100644 index 000000000..933335817 --- /dev/null +++ b/docs/md/road-map/rabbitmq.md @@ -0,0 +1,260 @@ +--- +title: RabbitMQ +lock: need +--- + +# RabbitMQ 使用教程 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +本文的宗旨在于通过简单干净实践的方式教会读者,使用 Docker 配置 RabbitMQ 在 DDD 结构下验证使用。关于 MQ 的场景内容已经在 [RocketMQ](https://bugstack.cn/md/road-map/rocketmq.html) 一节中做了讲解,本文只要为大家扩展另外一种 MQ 的使用。方便有需要的伙伴可以做技术栈替换。 + +[RabbitMQ](https://www.rabbitmq.com/) 是一个由 Erlang 开发的 AMQP (Advanced Message Queuing Protocol) 的开源实现。非常轻量,用于部署,有自己提供好的管理后台,非常容易上手使用。在功能上支持订阅、广播、路由和通配符,可以适合各类场景诉求。 + +本文涉及的工程: +- xfg-dev-tech-rabbitmq:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-rabbitmq](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-rabbitmq) +- RabbitMQ Docker 安装:[docs/dev-ops/docker-compose.yml](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-rabbitmq/-/blob/master/docs/dev-ops/docker-compose.yml) + +## 一、环境安装 + +因为本文是在 [RocketMQ](https://bugstack.cn/md/road-map/rocketmq.html) 一节的扩展,所以只讲解下技术使用即可。 + +本案例涉及了 RabbitMQ 的使用,都已经在工程中提供了安装脚本,可以按需执行。—— 前置条件已安装 [Docker](https://bugstack.cn/md/road-map/docker.html) 环境。 + +
+ +
+ +
+ +
+ +- Mac 电脑会比较好安装一些,直接在 IntelliJ IDEA 点击小绿色按钮即可完成安装。安装完成后进入 [http://localhost:9000/#!/2/docker/containers](http://localhost:9000/#!/2/docker/containers) - 可看到 RabbitMQ 运行。 +- Windows 电脑安装 Docker 需要折腾下 +- Linux 服务器,需要上传整个 dev-ops 后在云服务器执行脚本安装;`docker-compose -f docker-compose.yml up -d` + +## 二、配置主题 + +登录 RabbitMQ 管理后台:[http://127.0.0.1:15672/#/](http://127.0.0.1:15672/#/) - `账密:admin/admin` + +
+ +
+ +进入到后台以后,先如图配置个主题消息,后面会使用到这个主题发送和监听消息信息。 + +## 三、测试案例 + +### 1. yml 配置 + +**文件**:`application-dev.yml` + +```yml +spring: + # RabbitMQ 配置 + rabbitmq: + addresses: 127.0.0.1 + port: 5672 + username: admin + password: admin + listener: + simple: + prefetch: 1 # 每次投递n个消息,消费完在投递n个 +``` + +- 测试前,需要在工程中添加 RabbitMQ 连接配置信息。 +- prefetch 是消息投递的数量,实际场景可以适当配置的大一些。 + +### 2. 消费配置 + +进入到 `xfg-dev-tech-trigger` 是监听 MQ 消息的地方。 + +#### 2.1 普通消息 + +```java +@Slf4j +@Component +public class Customer { + + /** + * queuesToDeclare:支持多个队列,将队列绑定到默认交换机上,routeKey为队列名称。 + * + * @param msg 接收到的消息 + */ + @RabbitListener(queuesToDeclare = @Queue(value = "testQueue")) + public void listener(String msg) { + log.info("接收消息:{}", msg); + // 通过抛异常,验证消息重试 +// throw new RuntimeException("Err"); + } + +} +``` + +- 异常可以随着你的测试开启,开启后会接收到重试的消息。 + +#### 2.2 广播消息 + +```java +@Slf4j +@Component +public class FanoutCustomer { + + @RabbitListener( + bindings = @QueueBinding( + value = @Queue(value = "fanoutCustomer"), + exchange = @Exchange( + value = "fanoutExchange", + type = ExchangeTypes.FANOUT + ) + ) + ) + public void listener(String msg) { + log.info("接收消息【广播模式】:{}", msg); + } + +} +``` + +- 广播模式,所有的消费放都监听到消息。 + +#### 2.3 路由消息 + +```java +@Slf4j +@Component +public class RouteCustomer { + + @RabbitListener( + bindings = @QueueBinding( + value = @Queue(value = "routeQueue1"), + exchange = @Exchange(value = "routeExchange", type = ExchangeTypes.DIRECT), + key = "routeKey1" + ) + ) + public void listener01(String msg) { + log.info("接收消息【路由模式】:{}", msg); + } + + @RabbitListener( + bindings = @QueueBinding( + value = @Queue(value = "routeQueue2"), + exchange = @Exchange(value = "routeExchange", type = ExchangeTypes.DIRECT), + key = "routeKey2" + ) + ) + public void listener02(String msg) { + log.info("接收消息【路由模式】:{}", msg); + } + +} +``` + +- 路由模式,会根据实际发送消息时候路由选择配置,让指定的消费方接收消息。比如实际场景中有监听订单的消息,但订单有很多种,比如自营、三方以及不同支付渠道,那么可以让不同的监听者只收取自己的消息信息。 + +#### 2.3 通配符消息 + +```java +@Slf4j +@Component +public class TopicCustomer { + + @RabbitListener( + bindings = @QueueBinding( + value = @Queue(value = "topicQueue1"), + exchange = @Exchange(value = "topicExchange", type = ExchangeTypes.TOPIC), + key = "topic.*" // `*`:匹配一个单词,就只有一个单词 + ) + ) + public void listener01(String msg) { + log.info("接收消息【通配符模式】listener01:{}", msg); + } + + @RabbitListener( + bindings = @QueueBinding( + value = @Queue(value = "topicQueue2"), + exchange = @Exchange(value = "topicExchange", type = ExchangeTypes.TOPIC), + key = "topic.#" // `#`:匹配一个或多个词 + ) + ) + public void listener02(String msg) { + log.info("接收消息【通配符模式】listener02:{}", msg); + } + + @RabbitListener( + bindings = @QueueBinding( + value = @Queue(value = "topicQueue3"), + exchange = @Exchange(value = "topicExchange", type = ExchangeTypes.TOPIC), + key = "topic.y.#" // `#`:匹配一个或多个词 + ) + ) + public void listener03(String msg) { + log.info("接收消息【通配符模式】listener03:{}", msg); + } + +} +``` + +- 通配符可以起到过滤的作用,比如在实际场景中,你需要根据过往mq的类型,做部分的监听。那么可以根据通配符配置来搞定。 + +## 四、测试验证 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApiTest { + + @Autowired + private RabbitTemplate rabbitTemplate; + + @Test + public void test_product() throws InterruptedException { + // 发送消息 + rabbitTemplate.convertAndSend("testQueue", "基本消息"); + // 等待 + new CountDownLatch(1).await(); + } + + @Test + public void test_product_fanout() throws InterruptedException { + rabbitTemplate.convertAndSend("fanoutExchange", "", "广播消息"); + // 等待 + new CountDownLatch(1).await(); + } + + @Test + public void test_product_route() throws InterruptedException { + rabbitTemplate.convertAndSend("routeExchange", "routeKey1", "路由模式,消息1"); + rabbitTemplate.convertAndSend("routeExchange", "routeKey2", "路由模式,消息2"); + // 等待 + new CountDownLatch(1).await(); + } + + @Test + public void test_product_topic() throws InterruptedException { + rabbitTemplate.convertAndSend("topicExchange", "topic.x", "通配符模式,消息1"); + rabbitTemplate.convertAndSend("topicExchange", "topic.y.z", "通配符模式,消息2"); + // 等待 + new CountDownLatch(1).await(); + } + +} +``` + +```java +22:29:46.792 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#0-1] INFO Customer - 接收消息:基本消息 +22:30:40.525 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#1-1] INFO FanoutCustomer - 接收消息【广播模式】:广播消息 +22:31:27.117 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#3-1] INFO RouteCustomer - 接收消息【路由模式】:路由模式,消息2 +22:31:27.117 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#2-1] INFO RouteCustomer - 接收消息【路由模式】:路由模式,消息1 +10:32:08.359 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#6-1] INFO TopicCustomer - 接收消息【通配符模式】listener03:通配符模式,消息2 +10:32:08.359 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#4-1] INFO TopicCustomer - 接收消息【通配符模式】listener01:通配符模式,消息1 +10:32:08.359 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#5-1] INFO TopicCustomer - 接收消息【通配符模式】listener02:通配符模式,消息1 +10:32:08.372 [org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#5-1] INFO TopicCustomer - 接收消息【通配符模式】listener02:通配符模式,消息2 +``` + +- 以上案例,分别测试;基本消息、广播消息、路由消息、通配符消息。 + diff --git a/docs/md/road-map/ratelimiter.md b/docs/md/road-map/ratelimiter.md new file mode 100644 index 000000000..df5721ff0 --- /dev/null +++ b/docs/md/road-map/ratelimiter.md @@ -0,0 +1,225 @@ +--- +title: RateLimiter +lock: need +--- + +# RateLimiter 限流 —— 通过切面对单个用户进行限流和黑名单处理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过对实际场景的案例进行抽复现,教会读者如何对应用的接口以`浏览器指纹ID`为维度的限流操作,同时对于频繁限流拦截的ID加入黑名单,不需要限流计算就🈲禁止对应用接口访问。通过这样的方式来保护应用的可用性。 + +本文涉及的工程: +- xfg-dev-tech-ratelimiter:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-ratelimiter](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-ratelimiter) + +## 一、场景说明 + +关于登录的安全性管理有较多的手段,包括;设备信息、IP信息、绑定的信息、验证码登各类方式,不过在一些网页版的登录中,手机验证码方式都会有一个对应的提醒:"请勿向他人泄露验证码信息" + +
+ +
+ +也就是说,如果你把你的验证码给我,我就可以登录你的账户,查看你的数据。对于一些不法分子通过让你进入某些应用的录屏会议后(XXX退货返现),就能拿到你的验证码,并做登录操作。还有一些是完全流氓式做法,就玩命的一些快递📦手机号+验证码频繁的撞接口,也是有概率成功登录的。 + +所以,本节的案例我们来考虑下该如何做这样的防护处理。 + +## 二、方案设计 + +我们可以考虑在登录的阶段必须加一些恶心的图片比对码,或者滑块验证码。这也是一种方式,能尽可能降低登录的撞接口操作。之后再考虑添加一个指纹ID,对于验证码的生成与用户从浏览器设备过来的指纹做绑定。这样即使对方通过录屏拿到你的验证码,也仍然没有做登录操作。 + +```js + +``` + +有了上面这个方案,我们至少可以做一些安全的管控了。但还有臭不要脸的,一直刷你接口。这既有安全风险,又有对服务器的压力。所以我们要考虑对于这样的恶意用户进行`限流和自动化黑名单`处理。 + +
+ +
+ +浏览器指纹的方案只需要做一个验证码绑定即可,之后`限流和自动化黑名单`,则需要做一些代码的开发。通过配置的方式为每一个需要做此类功能的接口添加上**服务治理**。*通常我们把对应用的熔断、降级、限流、切量、黑白名单、人群等,都称为服务治理* + +## 三、功能实现 + +### 1. 工程结构 + +
+ +
+ +- 工程中,提供了一个 AOP 切面专门用于处理使用了自定义注解 `AccessInterceptor` 接口方法。 +- 这里的自定义注解,在 DDD 分层架构中,要放到 Types 层中,这样其他层才能引入使用。 + +### 2. 限流拦截 + +#### 2.1 切面定义 + +```java +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface AccessInterceptor { + + /** 用哪个字段作为拦截标识,未配置则默认走全部 */ + String key() default "all"; + + /** 限制频次(每秒请求次数) */ + double permitsPerSecond(); + + /** 黑名单拦截(多少次限制后加入黑名单)0 不限制 */ + double blacklistCount() default 0; + + /** 拦截后的执行方法 */ + String fallbackMethod(); + +} + +@Pointcut("@annotation(cn.bugstack.xfg.dev.tech.annotation.AccessInterceptor)") +public void aopPoint() { +} +``` + +- 自定义切面注解,提供了拦截的key、限制频次、黑名单处理、拦截后的回调方法。再通过 @Pointcut 切入配置了自定义注解的接口方法 + +#### 2.2 切面拦截 + +```java +// 个人限频记录1分钟 +private final Cache loginRecord = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.MINUTES) + .build(); + +// 个人限频黑名单24h - 自身的分布式业务场景,可以记录到 Redis 中 +private final Cache blacklist = CacheBuilder.newBuilder() + .expireAfterWrite(24, TimeUnit.HOURS) + .build(); + +@Around("aopPoint() && @annotation(accessInterceptor)") +public Object doRouter(ProceedingJoinPoint jp, AccessInterceptor accessInterceptor) throws Throwable { + String key = accessInterceptor.key(); + if (StringUtils.isBlank(key)) { + throw new RuntimeException("annotation RateLimiter uId is null!"); + } + + // 获取拦截字段 + String keyAttr = getAttrValue(key, jp.getArgs()); + log.info("aop attr {}", keyAttr); + + // 黑名单拦截 + if (!"all".equals(keyAttr) && accessInterceptor.blacklistCount() != 0 && null != blacklist.getIfPresent(keyAttr) && blacklist.getIfPresent(keyAttr) > accessInterceptor.blacklistCount()) { + log.info("限流-黑名单拦截(24h):{}", keyAttr); + return fallbackMethodResult(jp, accessInterceptor.fallbackMethod()); + } + + // 获取限流 -> Guava 缓存1分钟 + RateLimiter rateLimiter = loginRecord.getIfPresent(keyAttr); + if (null == rateLimiter) { + rateLimiter = RateLimiter.create(accessInterceptor.permitsPerSecond()); + loginRecord.put(keyAttr, rateLimiter); + } + + // 限流拦截 + if (!rateLimiter.tryAcquire()) { + if (accessInterceptor.blacklistCount() != 0) { + if (null == blacklist.getIfPresent(keyAttr)) { + blacklist.put(keyAttr, 1L); + } else { + blacklist.put(keyAttr, blacklist.getIfPresent(keyAttr) + 1L); + } + } + log.info("限流-超频次拦截:{}", keyAttr); + return fallbackMethodResult(jp, accessInterceptor.fallbackMethod()); + } + // 返回结果 + return jp.proceed(); +} +``` + +- 通过自定义注解中配置的拦截字段,获取对应的值。这里的值作为用户的标识使用,只对这个用户进行拦截。【也可以是一些列的信息确认,包括用户IP、设备等。】 +- 这段代码流程中会根据自定义注解中的配置,对访问的用户进行限流拦截,当拦击次数达到加入黑名单的次数后,则直接存起来(Guava/Redis)在24h内直接走黑名单。—— 实际的场景中还会有风控的手段介入,以及人工来操作黑名单。 + +#### 2.3 回调处理 + +```java +/** + * 调用用户配置的回调方法,当拦截后,返回回调结果。 + */ +private Object fallbackMethodResult(JoinPoint jp, String fallbackMethod) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + Signature sig = jp.getSignature(); + MethodSignature methodSignature = (MethodSignature) sig; + Method method = jp.getTarget().getClass().getMethod(fallbackMethod, methodSignature.getParameterTypes()); + return method.invoke(jp.getThis(), jp.getArgs()); +} +``` + +- 最终如果判定为拦截,则会走用户配置的回调方法。如 login 配置一个 loginErr,出入参都一样,只是名字不一样。这样才方便反射调用。 + +## 四、测试验证 + +### 1. 接口配置 + +```java +@AccessInterceptor(key = "fingerprint", fallbackMethod = "loginErr", permitsPerSecond = 1.0d, blacklistCount = 10) +@RequestMapping(value = "login", method = RequestMethod.GET) +public String login(String fingerprint, String uId, String token) { + log.info("模拟登录 fingerprint:{}", fingerprint); + return "模拟登录:登录成功 " + uId; +} + +public String loginErr(String fingerprint, String uId, String token) { + return "频次限制,请勿恶意访问!"; +} +``` + +给你需要拦截的方法,添加上自定义注解。 +- key: 以用户ID作为拦截,这个用户访问次数限制 +- fallbackMethod:失败后的回调方法,方法出入参保持一样 +- permitsPerSecond:每秒的访问频次限制。1秒1次 +- blacklistCount:超过10次都被限制了,还访问的,扔到黑名单里24小时 + +### 2. 测试验证 + +访问:[http://localhost:8091/api/ratelimiter/login?fingerprint=uljpplllll01009&uId=1000&token=8790](http://localhost:8091/api/ratelimiter/login?fingerprint=uljpplllll01009&uId=1000&token=8790) + +
+ +
+ +
+ +
+ +```java +22:34:47.518 [http-nio-8091-exec-6] INFO RateLimiterAOP - 限流-超频次拦截:uljpplllll01009 +22:34:47.669 [http-nio-8091-exec-7] INFO RateLimiterAOP - aop attr uljpplllll01009 +22:34:49.121 [http-nio-8091-exec-6] INFO RateLimiterAOP - aop attr uljpplllll01009 +22:34:49.122 [http-nio-8091-exec-6] INFO RateLimiterAOP - 限流-黑名单拦截(24h):uljpplllll01009 +22:34:57.647 [http-nio-8091-exec-8] INFO RateLimiterAOP - aop attr uljpplllll01009 +22:34:57.650 [http-nio-8091-exec-8] INFO RateLimiterAOP - 限流-黑名单拦截(24h):uljpplllll01009 +``` + +- 好啦,到这,我们就可以看到,用户的访问已经被拦截了。 +- 赶紧到自己的应用加一下吧,一个指纹ID,一个用户维护限流访问。让自己的应用更加可靠! + +--- + +>这些各项实际场景的内容,在小傅哥的【星球:码农会锁】有7个完结的项目和1个进行的项目,都有大量的实践运用。可以扫码加入,项目体验地址;[https://gaga.plus](https://gaga.plus) \ No newline at end of file diff --git a/docs/md/road-map/redis.md b/docs/md/road-map/redis.md new file mode 100644 index 000000000..9771df1c3 --- /dev/null +++ b/docs/md/road-map/redis.md @@ -0,0 +1,404 @@ +--- +title: Redis +lock: need +--- + +# Redis 缓存、加锁(独占/分段)、发布/订阅,常用特性的使用和高级编码操作 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式,向读者介绍 Redis 的安装部署,以及使用 Redisson 框架完成 Redis 常用核心功能的场景使用。 + +这些场景包括;Redis 的基本缓存使用、Redis 加锁(Redisson 提供了很多锁的方式,这里我们会展示独占锁和无锁化的性能测试)。之后还有一个非常重要的场景是关于 Redis 的发布和订阅。 + +本节内容会涉及到结合 Spring 框架进行自定义 Bean 对象的注入容器操作,以满足尽可能减少配置的情况下,完成对象的实例化和注入使用。这样的操作非常具有高级编码的实战性,值得大家折腾一下,也能顺便补充 Spring 源码的运用。 + +本文涉及的工程: + +- xfg-dev-tech-redis:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-redis](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-redis)- `docs/dev-ops 提供了 mysql、redis 安装脚本,和数据初始化操作` +- 官网:[https://redis.io/](https://redis.io/) - `开源内存数据存储,被数百万开发人员用作数据库、缓存、流引擎和消息代理。` +- 工具:[RedisDesktopManager](https://github.com/qishibo/AnotherRedisDesktopManager/releases)、[insight](https://redis.io/docs/ui/insight/) + +## 一、案例背景 + +在互联网应用开发中,Redis 缓存的使用,大部分都是为了保护数据库的。让应用对于非必要的情况下,尽可能减少对数据库的调用。比如一份固定的数据可以放到 Redis 缓存中提供查询,或者需要数据库唯一索引仿重拦截 insert 操作先进行 Redis 布隆过滤器校验,也或者是分布式场景下的加锁处理。这样可以减少了对数据库资源的占用,也提供了接口的响应性能。 + +同时也还有一些专门针对 Redis 做的技术方案,来提高系统的响应吞吐量和响应性能。如;基于 Redis 内存存储实现的规则引擎、基于 Redis 队列实现的低延迟任务调度、基于 Redis 发布和订阅实现的流程解耦操作等等,都是互联网需求场景中非常常用的技术方案。那么本节小傅哥会模拟出一个订单下单场景,来使用 Redis 缓存、加锁、发布/订阅等功能,为大家展示 Redis 的使用。 + +
+ +
+ +- 整个案例在DDD分层架构下,通过领域层调用仓储,完成订单的写库操作。在写库的时候,添加了不同类型锁的处理,以验证性能的差异。之后写入缓存和发布 Redis 消息。让监听端可以收取到发布的信息。 +- 通过这样一个非常常见的订单创建和查询的场景,来学习 Redis 的使用。在使用中,我们用到了 Redisson 框架,由它来处理 Redis 的调用。 + +## 二、环境安装 + +在安装执行 docker-compose.yml 脚本之前,你需要先在本地安装 [docker](https://bugstack.cn/md/road-map/docker.html) 之后 IntelliJ IDEA 打开 docker-compose.yml 文件,如图操作即可安装。 + +
+ +
+ +- 在 docker-compose.yml 中会先安装 MySQL 并执行 sql 文件夹里的 SQL 语句初始化数据库表。之后会安装 Redis 环境,Redis 的配置内容放在了 redis.conf 中,里面有 Redis 的连接密码。 + +## 三、功能实现 + +接下来小傅哥会带着大家在模拟的订单场景中,把 Redis 的缓存、加锁、发布/订阅的相关功能依次实现下。 + +### 1. 工程结构 + +
+ +
+ +- 这是一套 DDD 工程模型,也可以说整个教程其实都是 DDD 工程模型的拆解讲解,将各个模块嵌入到 DDD 分层架构中,看看他们是如何使用的。 +- 工程分为,app、domain、infrastructure、trigger 这样的四层,其实还有一个 types 通用层。 + + - app;用于配置 Redis 的相关启动操作,鉴于 SpringBoot 以及 Redis 版本问题,这里我们自己来创建客户端,更好兼容版本的差异。同时也可以扩展一些额外的功能。 + - domain;是领域服务层,order 可以看做是一个订单域,包括订单的创建、支付、查询,都可以在这个领域实现。*这个订单领域涉及的表就是前面章节,所压测的表 [【压测】MySQL 连接池 c3p0、dbcp、druid、hikari](https://bugstack.cn/md/road-map/connection-pool.html)* + - infrastructure;基础层是对 domain 依赖倒置的实现,具体到库的操作、缓存的操作,都是用这一层来实现。所以我们操作 Redis 的加锁、缓存,也会放到这里来处理。 + - trigger;触发器层,一般也有叫接口层。一般 http、rpc、job、mq、listener 都是在这一层进来使用。所以我们订阅 Redis 的消息也是放到这一层中处理。 + - types;工程中还有一个通用类型层,定义一些非专属 domain 领域内的公共资源。如配置一个自定义注解,来处理一些类的动态加载和组件开发。本章中我们就定义了一个这样的注解,来动态注入实例化的 Bean 对象。**这块非常值得学习一下,因为它是解决此类场景的高级编码** + +### 2. 配置缓存 + +在 app 模块下的 config 中,创建 RedisClientConfigProperties 配置类和 RedisClientConfig 客户端启动类。用于通过 Redisson 创建 Redis 的连接客户端。 + +```yml +redis: + sdk: + config: + host: localhost + port: 6379 + password: 123456 + pool-size: 10 + min-idle-size: 5 + idle-timeout: 30000 + connect-timeout: 5000 + retry-attempts: 3 + retry-interval: 1000 + ping-interval: 60000 + keep-alive: true +``` + +- 本身 Spring 也提供了 Redis 的配置,但鉴于兼容问题和后续的功能拓展,还是比较建议自己添加配置。 +- 关于代码的实现部分,可以参考 RedisClientConfigProperties、RedisClientConfig + +### 3. 数据缓存 + +Redis 的大部分操作其实都是缓存数据,提高系统的 QPS,在插入、更新、删除(逻辑删)、查询的时候,依赖于 Redis 进行提速操作。 + +
+ +
+ +```java +// 设置到缓存,在创建订单完成后写入缓存 +redissonService.setValue(orderId, orderEntity); + +@Override +public OrderEntity queryOrder(String orderId) { + OrderEntity orderEntity = redissonService.getValue(orderId); + if (null == orderEntity) { + UserOrderPO userOrderPO = userOrderDao.selectByOrderId(orderId); + orderEntity = new OrderEntity(); + orderEntity.setUserName(userOrderPO.getUserName()); + orderEntity.setUserId(userOrderPO.getUserId()); + // 设置到缓存 + redissonService.setValue(orderId, orderEntity); + } + return orderEntity; +} +``` + +- 在插入数据的时候,可以一并切入缓存。如果有更新操作,可以考虑删除缓存,在查询更新。因为更新操作,很多时候都是部分字段更新,这个时候直接更新缓存容易不准。 +- 最后就是查询时,用缓存拦截,避免所有的查询都打到库上。这样可以提高系统的 QPS +- 另外关于缓存击穿,说的就是你本来要在缓存存放大量数据的,但存放偏差或者漏了,那么这个时候大量请求都打到库上,导致把数据库拖垮。尤其是那种需要做事务加锁有资源竞争的,会更严重。 + +### 4. 加锁处理 + +使用 Redis 加分布式锁,也是分布式架构设计中非常常用的手段。常用于的场景包括;流程较长,耗时较多的个人开户、下单行为。也包括;一些资源竞争时加分布式锁,排队处理请求。但对于资源竞争的这类库存占用,如果加分布式锁是非常影响系统的吞吐量的,因为所有的用户都在等待上一个用户做完流程后释放锁的处理,相当于你即使系统是分布式的了,但这里的分布式锁依然会把性能拖慢。所以如图,我们要考虑两种场景不同的加锁方式。 + +
+ +
+ +```java +/** 独占锁 */ +@Override +public String createOrderByLock(OrderAggregate orderAggregate) { + RLock lock = redissonService.getLock("create_order_lock_".concat(orderAggregate.getSkuEntity().getSku())); + try { + lock.lock(); + long decrCount = redissonService.decr(orderAggregate.getSkuEntity().getSku()); + if (decrCount < 0) return "已无库存[初始化的库存和使用库存,保持一致。orderService.initSkuCount(\"13811216\", 10000);]"; + return createOrder(orderAggregate); + } finally { + lock.unlock(); + } +} + +/** 分段锁,也可以称为无锁化 */ +@Override +public String createOrderByNoLock(OrderAggregate orderAggregate) { + UserEntity userEntity = orderAggregate.getUserEntity(); + SKUEntity skuEntity = orderAggregate.getSkuEntity(); + // 模拟锁商品库存 + long decrCount = redissonService.decr(skuEntity.getSku()); + if (decrCount < 0) return "已无库存[初始化的库存和使用库存,保持一致。orderService.initSkuCount(\"13811216\", 10000);]"; + String lockKey = skuEntity.getSku().concat("_").concat(String.valueOf(decrCount)); + RLock lock = redissonService.getLock(lockKey); + try { + lock.lock(); + return createOrder(orderAggregate); + } finally { + lock.unlock(); + } +} +``` + +- 对于第1类的场景,主要是为了避免用户在一次操作后,又反复申请。系统上避免重复受理,所以添加分布式锁的方式进行拦截。如果不加分布式锁,就会进入到库表中通过唯一的索引拦截,这样对数据库的压力就比较大。 +- 对于第2类的场景,是采用了分段或者自增滑块的锁方式进行处理,减少对同一个锁的等待,而是生成一堆的锁,让用户去使用。**也就是最开始案例背景的图中,一个个⭕️圆圈的分段锁** + +### 5. 发布/订阅 + +此场景的案例会涉及到如何向 Spring 动态注入已经实例化后的 Bean 对象。为什么会出现这个场景呢? + +首先 Redis 的发布订阅,简单案例代码如下; + +```java +// 创建Redisson客户端 +RedissonClient redisson = Redisson.create(); + +// 获取RTopic对象 +RTopic topic = redisson.getTopic("myTopic"); + +// 发布消息 +topic.publish("Hello, Redisson!"); + +// 添加监听器 +topic.addListener(String.class, (channel, msg) -> { + System.out.println("Received message: " + msg); +}); + +// 关闭Redisson客户端 +redisson.shutdown(); +``` + +- 发布和订阅,是我们需要对同一个 Topic 进行发布和监听操作。但这个操作的代码是一种手动编码,但在我们实际使用中,如果所有的都是手动编码,**一个是非常麻烦,再有一个是非常累人**。 +- 所以这里小傅哥决定给你来个高级编码,通过自定义注解,来完成动态监听和将对象动态注入到 Spring 容器中,让需要注入的属性,可以被动态注入。 + +#### 5.1 自定义注解 + +```java +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@Documented +public @interface RedisTopic { + + String topic() default ""; + +} +``` + +- 起到了一种标识作用。`快捷键;你可以在 IDEA 工程中,摁2下 Shift 搜索这个类。` + +#### 5.2 注解使用 + +**源码**:`cn.bugstack.xfg.dev.tech.infrastructure.trigger.mq#RedisTopicListener02` + +```java +@Slf4j +@Service +@RedisTopic(topic = "testRedisTopic02") +public class RedisTopicListener02 implements MessageListener { + + @Override + public void onMessage(CharSequence channel, String msg) { + log.info("02-监听消息(Redis 发布/订阅): {}", msg); + } + +} +``` + +- 对需要监听 RedisTopic 的类,进行注解配置。之后在下面的代码中使用。 + +#### 5.3 动态注入 + +**源码**:`cn.bugstack.xfg.dev.tech.config.RedisClientConfig#redissonClient` + +```java +// 添加监听 +String[] beanNamesForType = applicationContext.getBeanNamesForType(MessageListener.class); +for (String beanName : beanNamesForType) { + MessageListener bean = applicationContext.getBean(beanName, MessageListener.class); + Class beanClass = bean.getClass(); + if (beanClass.isAnnotationPresent(RedisTopic.class)) { + RedisTopic redisTopic = beanClass.getAnnotation(RedisTopic.class); + + RTopic topic = redissonClient.getTopic(redisTopic.topic()); + topic.addListener(String.class, bean); + + // 动态创建 bean 对象,注入到 spring 容器,bean 的名称为 redisTopic,对象为 RTopic + ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); + beanFactory.registerSingleton(redisTopic.topic(), topic); + } +} +``` + +- 通过 applicationContext 获取所有实现了 MessageListener 接口的类,并对这个类的注解进行识别。 +- 所有使用了咱们定义注解的类,都进行获取获取 Topic 和添加监听操作。获取的 bean 对象,就是要监听的类。 +- 最后一步,再把这个类,通过 Spring 的 BeanFactory 工厂,进行注册。这样你再其他类中,就可以自动注入 topic 对象了,并进行 push 消息操作。 + +#### 5.4 使用对象 + +```java + +@Slf4j +@Repository +public class OrderRepository implements IOrderRepository { + + @Resource + private IRedisService redissonService; + @Resource + private IUserOrderDao userOrderDao; + + @Resource + private RTopic testRedisTopic; + + @Resource(name = "testRedisTopic02") + private RTopic testRedisTopic02; + + @Resource(name = "testRedisTopic03") + private RTopic testRedisTopic03; + + + @Override + public String createOrder(OrderAggregate orderAggregate) { + + // 省略... + + testRedisTopic02.publish(JSON.toJSONString(orderEntity)); + testRedisTopic03.publish(JSON.toJSONString(orderEntity)); + + return orderId; + } +} +``` + +- testRedisTopic 是我们硬编码创建的 Bean 对象,testRedisTopic02、testRedisTopic03 是我们通过自定义注解动态创建的 Bean 对象。 +- 之后就可以在需要 push 消息的方法中,使用 publish 发布你的消息内容了,并可以在监听中获取到消息。 + +## 四、功能测试 + +### 1. 分布式锁压测 + +**源码**:`cn.bugstack.xfg.dev.tech.test.domain.OrderServiceTest` + +```java +@Test +public void test_createOrder() throws InterruptedException { + String sku = RandomStringUtils.randomNumeric(9); + int count = 10000; + orderService.initSkuCount(sku, count); + + for (int i = 0; i < count; i++) { + threadPoolExecutor.execute(() -> { + UserEntity userEntity = UserEntity.builder() + .userId("小傅哥") + .userName("xfg".concat(RandomStringUtils.randomNumeric(3))) + .userMobile("+86 13521408***") + .build(); + SKUEntity skuEntity = SKUEntity.builder() + .sku(sku) + .skuName("《手写MyBatis:渐进式源码实践》") + .quantity(1) + .unitPrice(BigDecimal.valueOf(128)) + .discountAmount(BigDecimal.valueOf(50)) + .tax(BigDecimal.ZERO) + .totalAmount(BigDecimal.valueOf(78)) + .build(); + DeviceVO deviceVO = DeviceVO.builder() + .ipv4("127.0.0.1") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334".getBytes()) + .machine("IPhone 14 Pro") + .location("shanghai") + .build(); + long threadBeginTime = System.currentTimeMillis(); // 记录线程开始时间 + // 耗时:4毫秒 + String orderId = orderService.createOrder(new OrderAggregate(userEntity, skuEntity, deviceVO)); + // 耗时:106毫秒 + String orderId = orderService.createOrderByLock(new OrderAggregate(userEntity, skuEntity, deviceVO)); + // 耗时:4毫秒 + String orderId = orderService.createOrderByNoLock(new OrderAggregate(userEntity, skuEntity, deviceVO)); + long took = System.currentTimeMillis() - threadBeginTime; + totalExecutionTime.addAndGet(took); // 累加线程耗时 + log.info("写入完成 {} 耗时 {} (ms)", orderId, took / 1000); + }); + } + new Thread(() -> { + while (true) { + if (threadPoolExecutor.getActiveCount() == 0) { + log.info("执行完毕,总耗时:{} (ms)", (totalExecutionTime.get() / 1000)); + log.info("执行完毕,总耗时:{}", "\r\033[31m" + (totalExecutionTime.get() / 1000) + "\033[0m"); + break; + } + try { + Thread.sleep(350); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + }).start(); + // 等待 + new CountDownLatch(1).await(); +} +``` + +- 测试前,记得修改代码 count 值,代表这要初始化内存多少个容量。另外是环境记得先执行安装。 +- 接下来,我们进入了压测环节。createOrder 不使用锁、createOrderByLock 使用独占锁、createOrderByNoLock 是分段锁,也可以当做无锁处理。 +- 测试结果为,createOrderByLock 会占用较长的耗时。createOrderByNoLock 分段锁无锁接近于直接操作库。 +- 测试的过程中,还会看到监听订阅的消息,在控制台打印。 + +### 2. 其他测试 + +除了以上这结合业务的功能测试以外,本章还提供了;读写锁、异步锁、信号量、队列、延迟队列的相关测试。 + +```java +/** + * 延迟队列场景应用;https://mp.weixin.qq.com/s/jJ0vxdeKXHiYZLrwDEBOcQ + */ +@Test +public void test_getDelayedQueue() throws InterruptedException { + RBlockingQueue blockingQueue = redissonService.getBlockingQueue("xfg-dev-tech-task"); + RDelayedQueue delayedQueue = redissonService.getDelayedQueue(blockingQueue); + new Thread(() -> { + try { + while (true){ + Object take = blockingQueue.take(); + log.info("测试结果 {}", take); + Thread.sleep(10); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + }).start(); + int i = 0; + while (true){ + delayedQueue.offerAsync("测试" + ++i, 100L, TimeUnit.MILLISECONDS); + Thread.sleep(1000L); + } +} +``` + +- 详见源码:`cn.bugstack.xfg.dev.tech.test.infrastructure.redis.RedisTest` + +--- + +Redis 的使用还有很多有意思、有价值的场景,如果读者还有好的案例,也可以在源码中提交PR。 diff --git a/docs/md/road-map/road-map.md b/docs/md/road-map/road-map.md new file mode 100644 index 000000000..a8a6d4283 --- /dev/null +++ b/docs/md/road-map/road-map.md @@ -0,0 +1,58 @@ +--- +title: 编程路书 +lock: no +--- + +# bugstack虫洞栈 | Java 编程路书 v1.1 👣 + +
+ +
+ +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言:授业解惑 + +`我知道,你并不知道从哪开始!` + +你了解要成为一个软件开发工程师,要从哪里开始学习吗?你清楚为了能找到工作要学习多少知识吗?你知道从承接需求到开发交付上线需要多少个技术栈吗? + +对于这些内容即使从事编程2-3年的研发,大部分人也没有一个全局的概括,而在校大学生更是不清楚自己是从哪开始要到哪里去。收集了几十G到几百G的资料也不知道要从哪开始看,看哪些是对自己当前阶段帮助最大的。 + +**所以**小傅哥,准备开发一套《Java简明学习路书》,帮助踏上这条路上的伙伴,以更简单明了的方式进入编程学习。计划:通过一个个小案例,为大家讲解这些技术栈的运用。最后再通过完整的项目实战,把这些技术栈串联起来运用。 + +- **课程**:计划以视频方式陆续更新 +- **目标**:让小白学习者,有个简单明了的学习资料 +- **结业**:当所有内容掌握后,可以参与实战项目锻炼,Go -> [星球:码农会锁 - 学习应用级实战项目](https://bugstack.cn/md/zsxq/introduce.html) + +## 二、简明:学习路线 + + + +- **仓库**:[https://gitcode.net/KnowledgePlanet/road-map](https://gitcode.net/KnowledgePlanet/road-map) - 实战教程 +- **介绍**:通过一个个小案例,为大家讲解这些技术栈的运用。最后再通过完整的项目实战,把这些技术栈串联起来运用。 + +## 三、全貌:路书地图 + +- **地址**:[https://github.com/fuzhengwei/RoadMap](https://github.com/fuzhengwei/RoadMap) - 提供了路书仓库,我会在这里更新各项资料,你可以进入后,点击右上角 Star 进行收藏(这样我更新后你会收到通知) +- **介绍**:整个路书以需求承接到开发交付为视角,包括:计算机基础、系统和架构设计、环境搭建、系统开发、常用类库、调试、测试、质量分析、发布部署。通过这样的全局的视角,来告诉你在哪里,要去哪里。*点击各个技术栈可以直接进入内容* +- **说明**:**A**——核心技术,学习完能承担大部分工作、**B**——辅助路线,学习完能更好的完成工作。 + +--- + +
+ + + +--- + +如果你需要;`简明学习路线`、`实战项目锻炼`、`帮你学习辅导`、`教你简历优化` - 来应对招聘,那么可以扫码加入小傅哥的知识星球【码农会锁】- 我会带着走捷径直击目标,完成实战项目,提高编程思维,锻炼编码能力。 + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) \ No newline at end of file diff --git a/docs/md/road-map/rocketmq.md b/docs/md/road-map/rocketmq.md new file mode 100644 index 000000000..59e4a36a6 --- /dev/null +++ b/docs/md/road-map/rocketmq.md @@ -0,0 +1,395 @@ +--- +title: RocketMQ +lock: need +--- + +# RocketMQ 使用教程和模型结构 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式教会读者,使用 Docker 配置 RocketMQ 并在基于 DDD 分层结构的 SpringBoot 工程中使用 RocketMQ 技术。因为大部分 MQ 的发送都是基于特定业务场景的,所以本章节也是基于 [《MyBatis 使用教程和插件开发》](https://bugstack.cn/md/road-map/mybatis.html) 章节的扩展。 + +本章也会包括关于 MQ 消息的发送和接收应该处于 DDD 的哪一层的实践讲解和使用。 + +本文涉及的工程: + +- xfg-dev-tech-rocketmq:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-rocketmq](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-rocketmq) +- RocketMQ Docker 安装:[rocketmq-docker-compose-mac-amd-arm.yml](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-rocketmq/-/blob/master/docs/rocketmq/rocketmq-docker-compose-mac-amd-arm.yml) +- 导入测试库表 [road-map.sql](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-rocketmq/-/blob/master/docs/mysql/road-map.sql) + +## 一、案例背景 + +首先我们要知道,MQ 消息的作用是用于:`解耦过长的业务流程`和`应对流量冲击的消峰`。如:用户下单支付完成后,拿到支付消息推动后续的发货流程。也可以是我们基于 [《MyBatis 使用教程和插件开发》](https://bugstack.cn/md/road-map/mybatis.html) 中的案例场景,给雇员提升级别和薪资的时候,也发送一条MQ消息,用于发送邮件通知给用户。 + +
+ +
+ +- 从薪资调整到邮件发送,这里是2个业务流程,通过 MQ 消息的方式进行连接。 +- 其实MQ消息的使用场景特别多,原来你可能使用多线程的一些操作,现在就扩展为多实例的操作了。发送 MQ 消息出来,让应用的各个实例接收并进行消费。 + +## 二、领域事件 + +因为我们本章所讲解的内容是把 RocketMQ 放入 DDD 架构中进行使用,那么也就引申出领域事件定义。所以我们先来了解下,什么是领域事件。 + +领域事件,可以说是解耦微服务设计的关键。领域事件也是领域模型中非常重要的一部分内容,用于标示当前领域模型中发生的事件行为。一个领域事件会推进业务流程的进一步操作,在实现业务解耦的同时,也推动了整个业务的闭环。 + +
+ +
+ +- 首先,我们需要在领域模型层,添加一块 event 区域。它的存在是为了定义出在当前领域下所需的事件消息信息。信息的类型可以是model 下的实体对象、聚合对象。 +- 之后,消息的发送是放在基础设置层。本身基础设置层就是依赖倒置于模型层,所以在模型层所定义的 event 对象,可以很方便的在基础设置层使用。而且大部分开发的时候,MQ消息的发送与数据库操作都是关联的,采用的方式是,做完数据落库后,推送MQ消息。所以定义在仓储中实现,会更加得心应手、水到渠成。 +- 最后,就是 MQ 的消费,MQ 的消费可以是自身服务所发出的消息,也可以是外部其他微服务的消息。就在小傅哥所整体讲述的这套简明教程中 DDD 部分的触发器层。 + +## 三、环境安装 + +本案例涉及了数据库和RocketMQ的使用,都已经在工程中提供了安装脚本,可以按需执行。 + +
+ +
+ +这里主要介绍 RocketMQ 的安装: + +### 1. 执行 compose yml + +**文件**:[docs/rocketmq/rocketmq-docker-compose-mac-amd-arm.yml](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-rocketmq/-/blob/master/docs/rocketmq/rocketmq-docker-compose-mac-amd-arm.yml) - 关于安装小傅哥提供了不同的镜像,包括Mac、Mac M1、Windows 可以按需选择使用。 + +```yml +version: '3' +services: + # https://hub.docker.com/r/xuchengen/rocketmq + # 注意修改项: + # 01:data/rocketmq/conf/broker.conf 添加 brokerIP1=127.0.0.1 + # 02:data/console/config/application.properties server.port=9009 - 如果8080端口被占用,可以修改或者添加映射端口 + rocketmq: + image: livinphp/rocketmq:5.1.3 + container_name: rocketmq + ports: + - 9009:9009 + - 9876:9876 + - 10909:10909 + - 10911:10911 + - 10912:10912 + volumes: + - ./data:/home/app/data + environment: + TZ: "Asia/Shanghai" + NAMESRV_ADDR: "rocketmq:9876" +``` + +- 在 IDEA 中打开 rocketmq-docker-compose-mac-amd-arm.yml 你会看到一个绿色的按钮在左侧侧边栏,点击即可安装。或者你也可以使用命令安装:`# /usr/local/bin/docker-compose -f /docs/dev-ops/environment/environment-docker-compose.yml up -d` - 比较适合在云服务器上执行。 +- 首次安装可能使用不了,一个原因是 brokerIP1 未配置IP,另外一个是默认的 8080 端口占用。可以按照如下小傅哥说的方式修改。 + +### 2. 修改默认配置 + +1. 打开 `data/rocketmq/conf/broker.conf` 添加一条 `brokerIP1=127.0.0.1` 在结尾 + +```java +# 集群名称 +brokerClusterName = DefaultCluster +# BROKER 名称 +brokerName = broker-a +# 0 表示 Master, > 0 表示 Slave +brokerId = 0 +# 删除文件时间点,默认凌晨 4 点 +deleteWhen = 04 +# 文件保留时间,默认 48 小时 +fileReservedTime = 48 +# BROKER 角色 ASYNC_MASTER为异步主节点,SYNC_MASTER为同步主节点,SLAVE为从节点 +brokerRole = ASYNC_MASTER +# 刷新数据到磁盘的方式,ASYNC_FLUSH 刷新 +flushDiskType = ASYNC_FLUSH +# 存储路径 +storePathRootDir = /home/app/data/rocketmq/store +# IP地址 +brokerIP1 = 127.0.0.1 +``` + +2. 打开 ``data/console/config/application.properties` 修改 `server.port=9009` 端口。 + +```java +server.address=0.0.0.0 +server.port=9009 +``` + +- 修改配置后,重启服务。 + +### 3. RockMQ登录与配置 + +#### 3.1 登录 + +RocketMQ 此镜像,会在安装后在控制台打印登录账号信息,你可以查看使用。 + +
+ +
+ +
+ +
+ +登录:[http://localhost:9009/](http://localhost:9009/) + +#### 3.2 创建Topic + +
+ +
+ +- 也可以使用命令创建:`docker exec -it rocketmq sh /home/app/rocketmq/bin/mqadmin updateTopic -n localhost:9876 -c DefaultCluster -t xfg-mq` + +#### 3.3 创建消费者组 + +
+ +
+ +- 也可以使用命令创建:`docker exec -it rocketmq sh /home/app/rocketmq/bin/mqadmin updateSubGroup -n localhost:9876 -c DefaultCluster -g xfg-group` + +## 四、工程实现 + +### 1. 工程结构 + +
+ +
+ +- MQ 的使用无论是 RocketMQ 还是 Kafka 等,都很简单。但在使用之前,要考虑好怎么在架构中合理的使用。如果最初没有定义好这些,那么胡乱的任何地方都能发送和接收MQ,最后的工程将非常难以维护。 +- 所以这里整个MQ的生产和消费,是按照整个 DDD 领域事件结构进行设计。分为在 domain 使用基础层生产消息,再由 trigger 层接收消息。 + +### 2. 配置文件 + +**引入POM** + +```xml + + + org.apache.rocketmq + rocketmq-client-java + 5.0.4 + + + org.apache.rocketmq + rocketmq-spring-boot-starter + 2.2.0 + +``` + +**添加配置** + +```yml +# RocketMQ 配置 +rocketmq: + name-server: 127.0.0.1:9876 + consumer: + group: xfg-group + # 一次拉取消息最大值,注意是拉取消息的最大值而非消费最大值 + pull-batch-size: 10 + producer: + # 发送同一类消息的设置为同一个group,保证唯一 + group: xfg-group + # 发送消息超时时间,默认3000 + sendMessageTimeout: 10000 + # 发送消息失败重试次数,默认2 + retryTimesWhenSendFailed: 2 + # 异步消息重试次数,默认2 + retryTimesWhenSendAsyncFailed: 2 + # 消息最大长度,默认1024 * 1024 * 4(默认4M) + maxMessageSize: 4096 + # 压缩消息阈值,默认4k(1024 * 4) + compressMessageBodyThreshold: 4096 + # 是否在内部发送失败时重试另一个broker,默认false + retryNextServer: false +``` + +### 3. 定义领域事件 + +**源码**:`cn.bugstack.xfg.dev.tech.domain.salary.event.SalaryAdjustEvent` + +
+ +
+ +```java +@EqualsAndHashCode(callSuper = true) +@Data +public class SalaryAdjustEvent extends BaseEvent { + + public static String TOPIC = "xfg-mq"; + + public static SalaryAdjustEvent create(AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate) { + SalaryAdjustEvent event = new SalaryAdjustEvent(); + event.setId(RandomStringUtils.randomNumeric(11)); + event.setTimestamp(new Date()); + event.setData(adjustSalaryApplyOrderAggregate); + return event; + } + +} +``` + +- 每个领域的消息,都有领域自己定义。发送的时候再交给基础设施层来发送。 + +### 4. 消息发送 + +**源码**:`cn.bugstack.xfg.dev.tech.infrastructure.event.EventPublisher` + +
+ +
+ +```java +@Component +@Slf4j +public class EventPublisher { + + @Setter(onMethod_ = @Autowired) + private RocketMQTemplate rocketmqTemplate; + + /** + * 普通消息 + * + * @param topic 主题 + * @param message 消息 + */ + public void publish(String topic, BaseEvent message) { + try { + String mqMessage = JSON.toJSONString(message); + log.info("发送MQ消息 topic:{} message:{}", topic, mqMessage); + rocketmqTemplate.convertAndSend(topic, mqMessage); + } catch (Exception e) { + log.error("发送MQ消息失败 topic:{} message:{}", topic, JSON.toJSONString(message), e); + // 大部分MQ发送失败后,会需要任务补偿 + } + } + + /** + * 延迟消息 + * + * @param topic 主题 + * @param message 消息 + * @param delayTimeLevel 延迟时长 + */ + public void publishDelivery(String topic, BaseEvent message, int delayTimeLevel) { + try { + String mqMessage = JSON.toJSONString(message); + log.info("发送MQ延迟消息 topic:{} message:{}", topic, mqMessage); + rocketmqTemplate.syncSend(topic, MessageBuilder.withPayload(message).build(), 1000, delayTimeLevel); + } catch (Exception e) { + log.error("发送MQ延迟消息失败 topic:{} message:{}", topic, JSON.toJSONString(message), e); + // 大部分MQ发送失败后,会需要任务补偿 + } + } + +} +``` + +- 在基础设施层提供 event 事件的处理,也就是 MQ 消息的发送。 + + +**源码**:`cn.bugstack.xfg.dev.tech.infrastructure.repository.SalaryAdjustRepository` + +```java +@Resource +private EventPublisher eventPublisher; + +@Override +@Transactional(rollbackFor = Exception.class, timeout = 350, propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT) +public String adjustSalary(AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate) { + + // ... 省略部分代码 + + eventPublisher.publish(SalaryAdjustEvent.TOPIC, SalaryAdjustEvent.create(adjustSalaryApplyOrderAggregate)); + return orderId; +} +``` + +在 SalaryAdjustRepository 仓储的实现中,做完业务流程开始发送 MQ 消息。这里有2点要注意: +1. 消息发送,不要写在数据库事务中。因为事务一直占用数据库连接,需要快速释放。 +2. 对于一些强MQ要求的场景,需要在发送MQ前,写入一条数据库 Task 记录,发送消息后更新 Task 状态为成功。如果长时间未更新数据库状态或者为失败的,则需要由任务补偿进行处理。 + +### 5. 消费消息 + +**源码**:`cn.bugstack.xfg.dev.tech.trigger.mq.SalaryAdjustMQListener` + +
+ +
+ +```java +@Component +@Slf4j +@RocketMQMessageListener(topic = "xfg-mq", consumerGroup = "xfg-group") +public class SalaryAdjustMQListener implements RocketMQListener { + + @Override + public void onMessage(String s) { + log.info("接收到MQ消息 {}", s); + } + +} +``` + +- 消费消息,配置消费者组合消费的主题,之后就可以接收到消息了。接收以后你可以做自己的业务,如果抛出异常,消息会进行重新接收处理。 + +## 六、测试验证 + +### 1. 单独发送消息测试 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class RocketMQTest { + + @Setter(onMethod_ = @Autowired) + private RocketMQTemplate rocketmqTemplate; + + @Test + public void test() throws InterruptedException { + while (true) { + rocketmqTemplate.convertAndSend("xfg-mq", "我是测试消息"); + Thread.sleep(3000); + } + } + +} +``` + +- 这里方便你来发送消息,验证流程。 + +### 2. 业务流程消息验证 + +```java +@Test +public void test_execSalaryAdjust() throws InterruptedException { + AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrderAggregate = AdjustSalaryApplyOrderAggregate.builder() + .employeeNumber("10000001") + .orderId("100908977676003") + .employeeEntity(EmployeeEntity.builder().employeeLevel(EmployeePostVO.T3).employeeTitle(EmployeePostVO.T3).build()) + .employeeSalaryAdjustEntity(EmployeeSalaryAdjustEntity.builder() + .adjustTotalAmount(new BigDecimal(100)) + .adjustBaseAmount(new BigDecimal(80)) + .adjustMeritAmount(new BigDecimal(20)).build()) + .build(); + String orderId = salaryAdjustApplyService.execSalaryAdjust(adjustSalaryApplyOrderAggregate); + log.info("调薪测试 req: {} res: {}", JSON.toJSONString(adjustSalaryApplyOrderAggregate), orderId); + Thread.sleep(Integer.MAX_VALUE); +} +``` + +```java +23-07-29.15:40:52.307 [main ] INFO HikariDataSource - HikariPool-1 - Start completed. +23-07-29.15:40:52.445 [main ] INFO EventPublisher - 发送MQ消息 topic:xfg-mq message:{"data":{"employeeEntity":{"employeeLevel":"T3","employeeTitle":"T3"},"employeeNumber":"10000001","employeeSalaryAdjustEntity":{"adjustBaseAmount":80,"adjustMeritAmount":20,"adjustTotalAmount":100},"orderId":"100908977676004"},"id":"98117654515","timestamp":"2023-07-29 15:40:52.425"} +23-07-29.15:40:52.517 [main ] INFO ISalaryAdjustApplyServiceTest - 调薪测试 req: {"employeeEntity":{"employeeLevel":"T3","employeeTitle":"T3"},"employeeNumber":"10000001","employeeSalaryAdjustEntity":{"adjustBaseAmount":80,"adjustMeritAmount":20,"adjustTotalAmount":100},"orderId":"100908977676004"} res: 100908977676004 +23-07-29.15:40:52.520 [ConsumeMessageThread_1] INFO SalaryAdjustMQListener - 接收到MQ消息 {"data":{"employeeEntity":{"employeeLevel":"T3","employeeTitle":"T3"},"employeeNumber":"10000001","employeeSalaryAdjustEntity":{"adjustBaseAmount":80,"adjustMeritAmount":20,"adjustTotalAmount":100},"orderId":"100908977676004"},"id":"98117654515","timestamp":"2023-07-29 15:40:52.425"} +``` + +- 当执行一次加薪调整后,就会接收到MQ消息了。 diff --git a/docs/md/road-map/sharding-jdbc.md b/docs/md/road-map/sharding-jdbc.md new file mode 100644 index 000000000..d9460f692 --- /dev/null +++ b/docs/md/road-map/sharding-jdbc.md @@ -0,0 +1,234 @@ +--- +title: sharding-jdbc +lock: need +--- + +# sharding-jdbc 使用教程和模型结构 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +本文的宗旨在于通过简单干净实践的方式教会读者,快速 Easy 的使用上 sharding-jdbc 这个笨重的大家伙! + +之所以说笨重,是因为 [Apache ShardingSphere](https://shardingsphere.apache.org/index_zh.html) 不只是简单意义上的路由组件,而是一款分布式 SQL 事务和查询引擎,可通过数据分片、弹性伸缩、加密等能力对任意数据库进行增强。同时它又在迭代过程中,衍生出了很多的版本,以及对应了不同的使用方式。并在 ShardingSphere 5.3 以后又做了不小的架构调整。所以很多伙伴在使用的时候,经常是找了一上午的资料,到下午下班还没对接上。 + +本文涉及的工程: + +- xfg-dev-tech-shardingjdbc:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-shardingjdbc](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-shardingjdbc) +- 官网:[https://shardingsphere.apache.org/index_zh.html](https://shardingsphere.apache.org/index_zh.html) + +## 一、路由本质 + +分库分表的本质是数据的散列,分摊数据库资源压力。如把原本在一台机器上的数据库存放1000万数据,分摊到n台机上,拆分这1000万的数据和后续的增量。让每个数据库资源来分摊原本需要一台数据库所提供的服务。 + +
+ +
+ +- 当使用分库分表以后,并确定如使用`用户ID`作为路由分片键。那么所做的CRUD操作,都是需要使用到这个用户ID,并根据ID做路由库表计算。 +- 在大厂中,开发项目。并不会说目前这个业务需求规模不大,就不使用分库分表,而是分库分表都是非常成熟的方案,并不会因为使用了就降低很大的开发效率。所以基本就是默认就使用了。 +- 那么使用了分库分表,就会很占用资源吗。也不会,因为刚开始业务体量不大的时候,都是虚拟机交叉使用,你的1台物理机虚出来10个虚拟机,大家交叉使用主备。这样你只是使用了分库分表,但库表的实际资源没占用那么多。 + +## 二、使用案例 + +- jdk 1.8 + +- ShardingSphere 5.3+ + +```pom + + + org.apache.shardingsphere + shardingsphere-jdbc-core + 5.4.1 + + + + org.yaml + snakeyaml + 1.33 + +``` + +- 因为需要解析 yaml 但默认的 SpringBoot 提供版本不支持 shardingsphere-jdbc-core 使用。 + +### 1. 工程结构 + +
+ +
+ +- 工程中,提供了 docker 配置数据库环境操作,并提供了对应的建表测试语句。如果你本机已经安装了数据库,那么只做库表语句导入以及 yml 配置数据库连接信息就可以。 +- sharding-jdbc-dev.yaml 配置了详细的分库分表路由信息,在 algorithms 下配置的是库表的路由算法。这里的算法要根据实际自己使用中库表数量来设置&设计,避免发生较大的数据偏移。 +- 配置完 sharding-jdbc-dev.yaml 需要在 application-dev.yml 中配置上 sharding-jdbc-dev.yaml 路径,这样才能正确加载。 +- 如果你还需要定义出自己特定的路由算法,它还支持自己写个实现类的方式处理。 + +### 2. 算法配置 + +**sharding/sharding-jdbc-dev.yaml** + +```yml +# https://shardingsphere.apache.org/index_zh.html +mode: + # 运行模式类型。可选配置:内存模式 Memory、单机模式 Standalone、集群模式 Cluster - 目前为单机模式 + type: Standalone + +dataSources: + ds_0: + dataSourceClassName: com.zaxxer.hikari.HikariDataSource + driverClassName: com.mysql.cj.jdbc.Driver + jdbcUrl: jdbc:mysql://127.0.0.1:13306/xfg_dev_tech_db_00?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true + username: root + password: 123456 + connectionTimeoutMilliseconds: 30000 + idleTimeoutMilliseconds: 60000 + maxLifetimeMilliseconds: 1800000 + maxPoolSize: 15 + minPoolSize: 5 + + ds_1: + dataSourceClassName: com.zaxxer.hikari.HikariDataSource + driverClassName: com.mysql.cj.jdbc.Driver + jdbcUrl: jdbc:mysql://127.0.0.1:13306/xfg_dev_tech_db_01?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true + username: root + password: 123456 + connectionTimeoutMilliseconds: 30000 + idleTimeoutMilliseconds: 60000 + maxLifetimeMilliseconds: 1800000 + maxPoolSize: 15 + minPoolSize: 5 + +rules: + - !SHARDING + # 库的路由 + defaultDatabaseStrategy: + standard: + shardingColumn: user_id + shardingAlgorithmName: database_inline + # 表的路由 + tables: + user_order: + actualDataNodes: ds_$->{0..1}.user_order_$->{0..3} + tableStrategy: + standard: + shardingColumn: user_id + shardingAlgorithmName: user_order_inline + # 路由算法 + shardingAlgorithms: + # 库-路由算法 2是两个库,库的数量。库的数量用哈希模2来计算。 + database_inline: + type: INLINE + props: + algorithm-expression: ds_$->{Math.abs(user_id.hashCode()) % 2} + + # 表-路由算法 4是一个库里,表的数量。4 - 1 为了获得 011 这样的二进制值。不推荐 user_order_$->{Math.abs(user_id.hashCode()) % 2} 作为表的路由 + user_order_inline: + type: INLINE + props: + algorithm-expression: user_order_$->{(user_id.hashCode() ^ (user_id.hashCode()) >>> 16) & (4 - 1)} + +props: + # 是否在日志中打印 SQL。 + # 打印 SQL 可以帮助开发者快速定位系统问题。日志内容包含:逻辑 SQL,真实 SQL 和 SQL 解析结果。 + # 如果开启配置,日志将使用 Topic ShardingSphere-SQL,日志级别是 INFO。 false + sql-show: true + # 是否在日志中打印简单风格的 SQL。false + sql-simple: true + # 用于设置任务处理线程池的大小。每个 ShardingSphereDataSource 使用一个独立的线程池,同一个 JVM 的不同数据源不共享线程池。 + executor-size: 20 + # 查询请求在每个数据库实例中所能使用的最大连接数。1 + max-connections-size-per-query: 1 + # 在程序启动和更新时,是否检查分片元数据的结构一致性。 + check-table-metadata-enabled: false + # 在程序启动和更新时,是否检查重复表。false + check-duplicate-table-enabled: false +``` + +- mode:运行模式,默认就单机模式。 +- dataSources:数据库连接信息。 +- rules:路由算法。defaultDatabaseStrategy 库的路由、tables 表的路由。之后在 shardingAlgorithms 中配置具体的路由算法。这里的名称都是关联的,不要配置错。 +- props:一些属性信息,包括是否打印日志等。 + +### 3. 配置引入 + +**application-dev.yml** + +```yml +spring: + datasource: + driver-class-name: org.apache.shardingsphere.driver.ShardingSphereDriver + url: jdbc:shardingsphere:classpath:sharding/sharding-jdbc-dev.yaml +``` + +- 和之前的一些 sharding 版本不同,这里是需要使用具体的 ShardingSphereDriver 和 url 地址,才能加载上我们配置的路由信息。 + +## 三、测试验证 + +
+ +
+ +- 基于工程中 docs/dev-ops/mysql/sql 创建库表。已经提供了库名、表和测试数据。 +- 无论你使用哪种方式,都可以安装MySql 并使用可视化工具链接。这里小傅哥用的是 [Sequel Ace](https://apps.apple.com/us/app/sequel-ace/id1518036000?ls=1) + +### 1. 写入数据 + +```java +@Test +public void test_insert() { + for (int i = 0; i < 1000; i++) { + UserOrderPO userOrderPO = UserOrderPO.builder() + .userName("小傅哥") + .userId("xfg_" + RandomStringUtils.randomAlphabetic(6)) + .userMobile("+86 13521408***") + .sku("13811216") + .skuName("《手写MyBatis:渐进式源码实践》") + .orderId(RandomStringUtils.randomNumeric(11)) + .quantity(1) + .unitPrice(BigDecimal.valueOf(128)) + .discountAmount(BigDecimal.valueOf(50)) + .tax(BigDecimal.ZERO) + .totalAmount(BigDecimal.valueOf(78)) + .orderDate(new Date()) + .orderStatus(0) + .isDelete(0) + .uuid(UUID.randomUUID().toString().replace("-", "")) + .ipv4("127.0.0.1") + .ipv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334".getBytes()) + .extData("{\"device\": {\"machine\": \"IPhone 14 Pro\", \"location\": \"shanghai\"}}") + .build(); + userOrderDao.insert(userOrderPO); + } +} +``` + +- 测试数据写入,你可以写入1000条数据,观察散列效果。并可以在这个过程中,调试修改 sharding-jdbc-dev.yaml 文件对库表路由的计算方式。 + +### 2. 查询数据 + +```java +@Test +public void test_selectByUserId() { + List list = userOrderDao.selectByUserId("xfg_PrmgwQ"); + log.info("测试结果:{}", JSON.toJSONString(list)); +} +``` + +- 查询的用户ID是已经写入到数据库表里的数据,查询的时候会根据用户ID继续路由计算。 + +### 3. 散列算法 + +```java +@Test +public void test_idx() { + for (int i = 0; i < 50; i++) { + String user_id = "xfg_" + RandomStringUtils.randomAlphabetic(6); + log.info("测试结果 {}", (user_id.hashCode() ^ (user_id.hashCode()) >>> 16) & 3); + } +} +``` + +- 你可以尝试验证和编写新的散列算法,最终目的都是让数据尽可能散列到库表。 +- 此外,关于算法的好坏,可以基于[雪崩测试](https://bugstack.cn/md/algorithm/logic/math/2022-11-05-fibonacci.html)计算 + diff --git a/docs/md/road-map/skywalking.md b/docs/md/road-map/skywalking.md new file mode 100644 index 000000000..5d50c7937 --- /dev/null +++ b/docs/md/road-map/skywalking.md @@ -0,0 +1,248 @@ +--- +title: skywalking 全链路监控 +lock: need +--- + +# skywalking 全链路监控 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +这位同学,你比上一位面试者好多了,你的简历中做的几个项目都不错。既有业务项目,也有技术项目,看得出你对编程开发是有一定的经验积累的。那么我还想了解下,这些项目在运行中的一个数据效果是怎么样的。比如;tps、qps、响应时间、数据库负载等,都是什么情况,你用的什么监控工具。另外你这里还些了微服务的架构,那么微服务间的链路调用是怎么监控的。 + +咋样,是不一下就慌了。张口就喊:“java 崩盘!” 以前靠背题吹牛逼就能入职,现在得把吹的牛逼落地了。而越来越多的面试官也更喜欢用结果推过程,从过程中再考察细节。一上来就问八股文的越来越少了。 + +**所以**,做完项目,最好在配上对应的数据,这样才更有说服力。—— 所以本文小傅哥会教会你,如何配置一套全链路监控系统,并完成测试获取系统运行的数据。此外这是整套[《@小傅哥 Java 简明教程》](https://bugstack.cn/md/road-map/road-map.html)其中的一节,更多内容可以进入这里学习;[https://bugstack.cn/md/road-map/road-map.html](https://bugstack.cn/md/road-map/road-map.html) + +## 一、章节目的 + +
+ +
+ +本章节通过 Docker 方式部署一套 skywalking 非入侵的全链路监控系统,并在对应的测试工程中通过 skywalking-agent 字节码增强组件,采集系统运行时的各项信息到 skywalking-ui 监控平台观察数据。 + +- **官网**:[https://skywalking.apache.org/](https://skywalking.apache.org/) - 如果你想了解更多关于此类系统的设计和实现,可以阅读小傅哥的[《字节码编程》](https://bugstack.cn/md/bytecode/asm/2020-03-25-%5BASM%E5%AD%97%E8%8A%82%E7%A0%81%E7%BC%96%E7%A8%8B%5D%E5%A6%82%E6%9E%9C%E4%BD%A0%E5%8F%AA%E5%86%99CRUD%EF%BC%8C%E9%82%A3%E8%BF%99%E7%A7%8D%E6%8A%80%E6%9C%AF%E4%BD%A0%E6%B0%B8%E8%BF%9C%E7%A2%B0%E4%B8%8D%E5%88%B0.html) +- **源码**:[https://gitcode.net/KnowledgePlanet/road-map/skywalking](https://gitcode.net/KnowledgePlanet/road-map/skywalking) - 这是小傅哥整理好的一套可运行的监控和系统,读者可以下载后对照本文进行验证使用。 + +## 二、基本环境 + +- Docker version 1.13.1 +- Docker compose - 用于在云服务器环境中执行的 docker-compose 文件 +- Portainer Docker 容器管理面板 + +以上内容安装,参考[【Java简明教程/发布部署】](https://bugstack.cn/md/road-map/road-map.html):[https://bugstack.cn/md/road-map/road-map.html](https://bugstack.cn/md/road-map/road-map.html) - 发布部署 + +## 三、监控配置 + +skywalking 的安装,需要 elasticsearch - 存放数据、skywalking-oap 接收数据、skywalking-ui 界面展示。以及还需要一个 skywalking-agent 用于配置到应用程序中,采集监控数据。注意这些内容在官网中,都已提供,地址:[https://skywalking.apache.org/downloads/](https://skywalking.apache.org/downloads/) + +因为小傅哥这里提供了Docker的自动部署以及下载好了 skywalking-agent 所以你就不需要一个个去下载安装了。接下来小傅哥会分别介绍在`本地环境`和`云服务器`两套环境安装,这样可以更加方便小伙伴做测试验证。 + +在进行下面的步骤前,请先下载 skywalking 监控工程;[https://gitcode.net/KnowledgePlanet/road-map/skywalking](https://gitcode.net/KnowledgePlanet/road-map/skywalking) + +
+ +
+ +### 1. 本地环境 + +**脚本**:`/road-map/skywalking/docs/dev-ops/skywalking` - 你可以打开工程找到这个位置,查看最新脚本。 + +```java +version: '3.8' +services: + elasticsearch: + image: elasticsearch:7.16.2 + container_name: elasticsearch + ports: + - "9200:9200" + healthcheck: + test: [ "CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1" ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + environment: + - discovery.type=single-node + - bootstrap.memory_lock=true + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + ulimits: + memlock: + soft: -1 + hard: -1 + volumes: + - ./data/es_data:/usr/share/elasticsearch/data + + oap: + image: apache/skywalking-oap-server:8.9.0 + container_name: oap + depends_on: + elasticsearch: + condition: service_healthy + links: + - elasticsearch + ports: + - "11800:11800" + - "12800:12800" + healthcheck: + test: [ "CMD-SHELL", "/skywalking/bin/swctl ch" ] + interval: 30s + timeout: 10s + retries: 3 + start_period: 10s + environment: + SW_STORAGE: elasticsearch + SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200 + SW_HEALTH_CHECKER: default + SW_TELEMETRY: prometheus + JAVA_OPTS: "-Xms1024m -Xmx1024m" + + skywalking-ui: + image: apache/skywalking-ui:8.9.0 + container_name: skywalking-ui + depends_on: + oap: + condition: service_healthy + links: + - oap + ports: + - "9090:8080" + environment: + SW_OAP_ADDRESS: http://oap:12800 +``` + +
+ +
+ +- 在 Docker 安装并正确✅启动后,你就可以点击这个按钮了。它会帮你自动运行安装出整套的 skywalking 监控系统。非常方便。 +- 如果你点击红圈下面的单个按钮,那么代表的是只安装当前一个应用。 +- 你可以通过命令执行 `skywalking-docker-compose.yml` 的安装:`/usr/local/bin/docker-compose -f /docs/dev-ops/skywalking/skywalking-docker-compose.yml up -d` - 在云服务器端也是使用这个命令安装。 + +访问验证:[http://localhost:9090/](http://localhost:9090/) - 我设置的端口是9090,如果你是其他的则需要修改。 + +### 2. 云服务器 + +- 准备一台2核4G的云服务器,整个服务启动后会占用2-3G左右 +- 下载 ssh 工具,用于连接云服务。这里小傅哥推荐使用 Termius 非常好用! +- docker-compose 安装,参考:[https://bugstack.cn/md/road-map/road-map.html](https://bugstack.cn/md/road-map/road-map.html) - 发布部署,Docker#7 + +#### 2.1 文件上传 + +通过 ssh 的 sftp 工具,把 skywalking/docs 全部上传到云服务器。 + +
+ +
+ +#### 2.2 执行脚本 + +
+ +
+ +```shell script +[root@dev-ops ~]# cd /docs/ +[root@dev-ops docs]# ls +dev-ops skywalking-agent sql +[root@dev-ops docs]# cd dev-ops/ +[root@dev-ops dev-ops]# ls +environment skywalking +[root@dev-ops dev-ops]# cd skywalking/ +[root@dev-ops skywalking]# ls +skywalking-docker-compose.yml +[root@dev-ops skywalking]# /usr/local/bin/docker-compose -f /docs/dev-ops/skywalking/skywalking-docker-compose.yml up -d +[+] Building 0.0s (0/0) +[+] Running 3/3 + ✔ Container elasticsearch Healthy 0.5s + ✔ Container oap Healthy 1.0s + ✔ Container skywalking-ui Running 0.0s +``` + +安装完成记得开放端口; +- 9090 端口;skywalking-ui 界面端口 +- 11800 端口;监控数据上报端口 + +安装完成后就可以访问监控界面了;[http://180.76.138.**:9090/](http://180.76.138.**:9090/) - 替换为你的IP地址 + +## 四、数据上报 + +监控数据的上报使用的是 Javaagent 技术,在程序加载时候通过字节码增强技术,在需要监控的位置自动加上额外的监控代码,来采集系统的运行数据。所以我们这里可以把 Javaagent 配置到程序启动上,也可以配置到 Docker 镜像打包上。 + +### 1. 程序启动 - 加入探针 + +配置到 IDEA 程序启动中,VM Options 参数:`-javaagent:/Users/fuzhengwei/1024/KnowledgePlanet/road-map/skywalking/docs/skywalking-agent/skywalking-agent.jar +-Dskywalking.agent.service_name=skywalking-app-dev +-Dskywalking.collector.backend_service=127.0.0.1:11800` + +
+ +
+ +- 注意修改;`地址`、`应用名`、`IP`【如果是云服务,就配置云服务的IP地址】 + +### 2. 镜像打包 - 加入探针 + +当程序需要运行在云服务的 Docker 容器了,就不能这样配置了,需要把配置打包到镜像里,更加方便执行。 + +```java +# 基础镜像 +FROM openjdk:8-jre-slim +# 作者 +MAINTAINER xiaofuge +# 配置 +ENV PARAMS="" +# 时区 +ENV TZ=PRC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +# 添加应用 +ADD /target/skywalking-app.jar /skywalking-app.jar +## 在镜像运行为容器后执行的命令 +ENTRYPOINT ["java", "-Xmx512m", "-javaagent:/docs/skywalking-agent/skywalking-agent.jar", "-Dskywalking.agent.service_name=skywalking-app", "-Dskywalking.collector.backend_service=180.76.138.41:11800", "-jar", "/skywalking-app.jar"] +``` + +- 注意;`/docs/skywalking-agent/skywalking-agent.jar` 这个是通过 SFTP 工具上传到云服务器端的。但不在 Docker 容器里,等部署程序的时候还需要做一次文件映射。 + +## 五、启动服务 + +如果你希望在把应用程序部署到云服务端,一种是通过 IDEA 连接 Docker 服务,另外一种是把应用程序的镜像发布到Docker Hub。这里我们通过 IDEA 连接 Docker 服务。参考:[https://bugstack.cn/md/road-map/road-map.html](https://bugstack.cn/md/road-map/road-map.html) - 开通 2375 端口,用完记得关闭。 + +**脚本**:`skywalking/src/bin/main/start.sh` + +```java +CONTAINER_NAME=skywalking-app +IMAGE_NAME=fuzhengwei/skywalking-app:1.0 +PORT=9091 + +echo "容器部署开始 ${CONTAINER_NAME}" + +# 停止容器 +docker stop ${CONTAINER_NAME} + +# 删除容器 +docker rm ${CONTAINER_NAME} + +# 启动容器 skywalking-agent 下载:https://archive.apache.org/dist/skywalking/java-agent/8.9.0/apache-skywalking-java-agent-8.9.0.tgz +docker run --name ${CONTAINER_NAME} \ +-p ${PORT}:${PORT} \ +-v /docs/skywalking-agent/:/docs/skywalking-agent/ \ +-d ${IMAGE_NAME} + +#docker run --name skywalking-app \ +#-p 9091:9091 \ +#-v /docs/skywalking-agent/:/docs/skywalking-agent/ \ +#-d fuzhengwei/skywalking-app:1.2 + +echo "容器部署成功 ${CONTAINER_NAME}" + +docker logs -f ${CONTAINER_NAME} +``` + +- 你可以在云服务执行 start.sh 脚本,或者直接复制 docker run 命令,去执行启动。 +- 注意;`-v /docs/skywalking-agent/:/docs/skywalking-agent/ \` 是你的映射地址,只有这样才能拿到 skywalking-agent +- 另外记得按照 MySQL【environment-docker-compose.yml】 到云服务以及执行 road-map.sql 文件。 diff --git a/docs/md/road-map/spring-ai.md b/docs/md/road-map/spring-ai.md new file mode 100644 index 000000000..89d85372b --- /dev/null +++ b/docs/md/road-map/spring-ai.md @@ -0,0 +1,191 @@ +--- +title: Spring AI +lock: need +--- + +# spring ai + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。今天来带大家体验一下 Spring AI 的 Easy 开发! + +现在的 OpenAI 对接开发真的是越来越容易了,直接引入 Spring AI 这个组件包,配置上可调用的 ApiHost、ApiKey 就可以完成对 OpenAI(ChatGPT)接口的调用。在此之前也有那么多各类的接口服务,还没有像 OpenAI 这样,被 Spring 如此重视,直接提供专属的 SDK 包,封装所有大模型(`暂无国内的`)。这也说明 OpenAI 的趋势力量多么庞大! + +
+ +
+ +**OpenAI 结合业务才更有价值** + +虽然 OpenAI 的接口对接是越来越简单了,但我们要知道,一个实际的项目就不只是对接 OpenAI 接口,还需要完成一系列的业务流程封装。就比如目前我们看到国内的各类套壳或自研类的 OpenAI 服务,都会包括;`用户`、`鉴权`、`账户`、`支付`、`场景`等,一套东西来支撑整个流程跑通,同时还有系统工程背后的`舆情监控`、`敏感词过滤`、`数据存储`、`行为分析`等各类操作。这些东西也就是常说的,不能只会一个技术点,而是要结合场景,用技术支撑业务落地。 + +接下来,小傅哥会分享 Spring AI + Gpt-4o 的对接使用,以及介绍如何开发应用级的 OpenAI 项目。 + +## 一、简单介绍 + +Spring AI 项目,是为开发 AI 应用程序提供了 Spring 友好的 API 和抽象。所有的大模型对接(OpenAI、Ollama、Azure OpenAI、Amazon Bedrock、HuggingFace、Google VertexAI、Mistral AI)都以一种统一标准的形式进行。这样就减少了大家再额外开发对接的成本了,也不用维护和兼容各个 OpenAI 的接口迭代。 + +- 官网:[https://docs.spring.io/spring-ai/reference/index.html](https://docs.spring.io/spring-ai/reference/index.html) +- 文档:[https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/index.html](https://docs.spring.io/spring-ai/reference/1.0-SNAPSHOT/index.html) +- 源码:[https://github.com/spring-projects/spring-ai](https://github.com/spring-projects/spring-ai) + +## 二、工程对接 + +### 1. 工程说明 + +- 环境:JDK 17、SpringBoot 3.2.3、spring-ai 0.8.0 +- 源码:[xfg-dev-tech-spring-ai](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-spring-ai) + +
+ +
+ +在此工程中完成了 SpringAI 模块的引入,以及使用提供的 API 接口,完成同步应答、流式应答、图片绘制功能。 + +### 2. 引入模块 + +```pom + + org.springframework.ai + spring-ai-bom + 0.8.0 + pom + import + + + + org.springframework.ai + spring-ai-openai-spring-boot-starter + +``` + +- spring-ai 也在不断的发展,2024年5月22日 发布了 1.0.0 M1 并对代码做了一些调整。在生产级别使用需要注意📢版本的迭代。 + +### 2. 接口对接 + +测试之前需要在 application-dev.yml 配置对接的信息。 + +```yaml +spring: + ai: + openai: + base-url: https://api.aws-*****/ + api-key: sk-oLkakcax33mJl628D3A533Fd67A24602Ac37D6***** +``` + +注意 spring-ai 提供了各类大模型的对接,都可以按需配置到这里。*在它的源码 autoConfig 类里会有要配置参数的名称,可以参考。* + +#### 2.1 功能测试 + +```java +@Resource +private OpenAiChatClient chatClient; +@Resource +private OpenAiImageClient openaiImageClient; + +@Test +public void test_generate() { + ChatResponse chatResponse = chatClient.call( + new Prompt( + "1+1", + OpenAiChatOptions.builder() + .withModel("gpt-4o") + .build() + )); + log.info("测试结果:{}", JSON.toJSONString(chatResponse)); +} + +@Test +public void test_generate_stream() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + Flux stream = chatClient.stream(new Prompt("1+1")); + stream.subscribe( + chatResponse -> { + AssistantMessage output = chatResponse.getResult().getOutput(); + log.info("测试结果: {}", JSON.toJSONString(output)); + }, + Throwable::printStackTrace, + () -> { + countDownLatch.countDown(); + System.out.println("Stream completed"); + } + ); + countDownLatch.await(); +} + +@Test +public void test_generate_image() { + ImageResponse imageResponse = openaiImageClient.call( + new ImagePrompt("画个小狗", + OpenAiImageOptions.builder() + .withModel("dall-e-3") + .withQuality("hd") + .withN(1) + .withHeight(1024) + .withWidth(1024) + .build() + ) + ); + log.info("测试结果: {}", JSON.toJSONString(imageResponse)); +} +``` + +**测试结果** + +```java +24-05-26.16:46:46.287 [ForkJoinPool.commonPool-worker-1] INFO OpenAITest - 测试结果: {"content":"","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}} +24-05-26.16:46:46.288 [ForkJoinPool.commonPool-worker-1] INFO OpenAITest - 测试结果: {"content":"","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}} +24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO OpenAITest - 测试结果: {"content":"1","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}} +24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO OpenAITest - 测试结果: {"content":"+","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}} +24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO OpenAITest - 测试结果: {"content":"1","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}} +24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO OpenAITest - 测试结果: {"content":" equals","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}} +24-05-26.16:46:46.289 [ForkJoinPool.commonPool-worker-1] INFO OpenAITest - 测试结果: {"content":" ","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}} +24-05-26.16:46:46.290 [ForkJoinPool.commonPool-worker-1] INFO OpenAITest - 测试结果: {"content":"2","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}} +24-05-26.16:46:46.290 [ForkJoinPool.commonPool-worker-1] INFO OpenAITest - 测试结果: {"content":"","messageType":"ASSISTANT","properties":{"role":"ASSISTANT"}} +Stream completed +``` + +- 测试结果为 test_generate_stream 流式返回 `1+1` 的应答结果。 + +#### 2.2 接口服务 + +```java +@RestController() +@CrossOrigin("*") +@RequestMapping("/api/v1/openai/") +public class OpenAiController { + + @Resource + private OpenAiChatClient chatClient; + + /** + * curl http://localhost:8090/api/v1/openai/generate?message=1+1 + */ + @RequestMapping(value = "generate", method = RequestMethod.GET) + public ChatResponse generate(@RequestParam String message) { + return chatClient.call( + new Prompt( + message, + OpenAiChatOptions.builder() + .withModel("gpt-4o") + .build() + )); + } + + /** + * curl http://localhost:8090/api/v1/openai/generate_stream?message=1+1 + */ + @RequestMapping(value = "generate_stream", method = RequestMethod.GET) + public Flux generateStream(@RequestParam String message) { + return chatClient.stream(new Prompt(message)); + } + +} +``` + +在 OpenAiController 中提供了对接两个接口的方法,以及同步和流式返回。*流式返回还可以使用 ResponseBodyEmitter 进行流式返回* + +Spring AI 提供的 OpenAI 大模型对接是 Easy 的很,减去了自己开发代码对接大模型。但仅仅是这样一个简单还不够,我们还需要学习积累的更多!如;微信鉴权登录怎么做、支付怎么对接、账户额度怎么扣减、限流熔断怎么操作等等,实际场景问题才是有价值的核心内容,所以,咱们要上个大菜!☞ [OpenAi 大模型应用服务体系构建 - API-SDK、鉴权、公众号、企业微信、支付服务](https://bugstack.cn/md/project/chatgpt/chatgpt.html) \ No newline at end of file diff --git a/docs/md/road-map/spring-dependency-injection.md b/docs/md/road-map/spring-dependency-injection.md new file mode 100644 index 000000000..a8743931d --- /dev/null +++ b/docs/md/road-map/spring-dependency-injection.md @@ -0,0 +1,445 @@ +--- +title: Spring Dependency Injection +lock: need +--- + +# Spring Dependency Injection - 依赖注入使用技巧 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +`@Autowired` 注入会用,`@Resource` 注入知道,但在项目看到一个没使用这2个注解的,直接在构造函数写了个两个入参 `public AwardController(List awardServices, Map awardServiceMap) { ...` 就不知道是怎么注入的了。我猜可能是以前一直写 CRUD 流水席代码,导致 Spring 的一些高级注入特性从来没接触过。所以小傅哥今天来给大家专门总结下 Spring 的各种注入的使用,方便小伙伴们可以运用这些特性,写出优雅的代码。 + +
+ +
+ +**你可以不用,但不能不会!** + +在小傅哥带着大家做的实战项目中「 [https://gaga.plus](https://gaga.plus) 」,有着大量的高级编程技巧和设计模式结合运用的手段,帮你打开能”拿捏“写代码的强悍能力。这些能力都是越早积累越好,否则你以为,为啥一边有人喊着寒冬,一边有人拿着高薪。 + +>文末提供了整合案例源码,以及5个业务应用级项目,5个技术组件项目。🛫 + +## 一、工程说明 + +为了让大家更好的理解和学习 Spring 各类依赖注入的相关使用,这里小傅哥专门提供了案例工程,把相关的注入特性都整合进去。学习中可以下载工程对照验证。 + +**源码**:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-spring-dependency-injection](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-spring-dependency-injection) + +
+ +
+ +- config:工程所需的配置类对象的使用案例。 +- domain:一个接口多个实现类、实现类优先级创建、原型对象、Spring 加载的对象。 +- infrastructure:仓储注解实例化对象的使用。 +- trigger:list注入、map注入、依赖注入,对象初始和销毁的监听。 +- ApiTest:测试各类注入案例的验证。 + +## 二、实践案例 + +**实例化注解**: + +- `@Component`:组件注解 +- `@Service`:服务注解 +- `@Repository`:仓储注解,提供对持久化类数据的操作的服务。 +- `@Controller/@RestController()`:对外提供服务的注解。 + +### 1. 构造注入&List、Map + +```java +private final List awardServices; +private final Map awardServiceMap; + +public AwardController(List awardServices, Map awardServiceMap) { + this.awardServices = awardServices; + this.awardServiceMap = awardServiceMap; +} + +public Response distributeAward(@RequestParam String userId, @RequestParam String awardKey) { + try { + log.info("发放奖品服务 userId:{} awardKey:{}", userId, awardKey); + awardServiceMap.get(awardKey); + return Response.builder() + .code("0000") + .info("调用成功") + .data("发奖完成") + .build(); + } catch (Exception e) { + return Response.builder() + .code("0001") + .info("调用失败") + .build(); + } +} +``` + +- **场景**:IAwardService 接口有多个实现类,可以通过 @Resource、@Autowired 注解注入,也可以通过构造函数注入。在 Spring 官网文档中,是推荐使用构造函数注入的:`The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null.` [https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html](https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html) +- **用途**:Map 注入是一个非常好的注入手段,我们可以把每个 IAwardService 实现类设定好 Bean 的名称为数据库中的奖品 awardKey。在发奖的时候,可以直接根据 awardKey 从 Map 中获取到对应的 Bean 对象,这样也就省去了 `if···else` 大量的判断操作。 + +### 2. 空注入判断 + +```java +public class NullAwardService implements IAwardService { + + @Override + public void doDistributeAward(String userId) { + + } + +} + +@Autowired(required = false) +private NullAwardService nullAwardService; +``` + +- **场景**:NullAwardService 没有配置 @Service 注册,或者在程序中手动实例化的这个 Bean 对象,根据不同诉求,在没有创建的时候。可以使用 `@Autowired(required = false)` 进行注入。这样就不会报错 nullAwardService 空指针异常。 +- **用途**:当我们在使用支付、openai外部接口对接测试阶段,可能有些时候是需要关闭服务的,也就是不实例化对象。那么这个时候就配置 `@Autowired(required = false)` 避免注入空指针 + +### 3. 优先实例化 + +```java +@Slf4j +@Service("openai_model") +// Primary 首选 Bean 对象标记 +@Primary +@Order(1) +public class OpenAIModelAwardService implements IAwardService { + + @Override + public void doDistributeAward(String userId) { + log.info("发奖服务,OpenAI 模型奖励 {}", userId); + } + +} + +@Resource +private IAwardService awardService; + +@Test +public void test_awardService_primary() { + log.info("测试结果 {}", awardService.getClass()); +} + +// 测试结果 class cn.bugstack.xfg.dev.tech.domain.impl.OpenAIModelAwardService +``` + +- **场景**:一个 IAwardService 有多个实现类的时候,如果还想用 `@Resource 注入 awardService` 的时候是会报错说 `NoUniqueBeanDefinitionException` 异常了。这个时候使用 @Primary 就会标记为首选对象,注入的时候会注入这个对象。另外这里的 `@Order(1)` 是对象的加载顺序。 +- **用途**:当我们为一组接口提供实现类,并需要提供默认的注入的时候,就可以使用 `@Primary` 注解来限定首选注入项。 + +### 4. 检测创建,避免重复 + +```java +@Bean("redisson01") +// 当 Spring 应用上下文中不存在某个特定类型的 Bean 时,才会创建和配置标注了 @ConditionalOnMissingBean 的 Bean 对象 +@ConditionalOnMissingBean +public String redisson01() { + return "模拟的 Redis 客户端 01"; +} +@Bean("redisson02") +// 当 Spring 应用上下文中不存在某个特定类型的 Bean 时,才会创建和配置标注了 @ConditionalOnMissingBean 的 Bean 对象 +@ConditionalOnMissingBean +public String redisson02() { + return "模拟的 Redis 客户端 02"; +} +``` + +- **场景**:`@Bean` 可以用于在方法,创建出对象。这有点类似于使用 Spring 的 FactoryBean 接口创建对象一样,这里可以直接使用方法创建。之后 `@ConditionalOnMissingBean` 注解的目的是为了避免重复创建,判断应用上下文中存在这个对象,则不会重复创建。 +- **用途**:通常我们在做一些组件的时候,会加入这样一个注解,避免在业务工程中引入同类的组件的时候,会导致创建出相同对象而报错。 + +### 5. 配置是否创建对象 + +```java +@Data +@ConfigurationProperties(prefix = "sdk.config", ignoreInvalidFields = true) +public class AutoConfigProperties { + + /** 状态:open = 开启、close 关闭 */ + private boolean enable; + /** 转发地址 */ + private String apiHost; + /** 可以申请 sk-*** */ + private String apiSecretKey; + +} + +@Bean +@ConditionalOnProperty(value = "sdk.config.enabled", havingValue = "true", matchIfMissing = false) +public String createTopic(@Qualifier("redisson01") String redisson, AutoConfigProperties properties) { + log.info("redisson {} {} {}", redisson, properties.getApiHost(), properties.getApiSecretKey()); + return redisson; +} + +sdk: + config: + enabled: false + apiHost: https://open.bigmodel.cn/ + apiSecretKey: d570f7c5d289cdac2abdfdc562e39f3f.trqz1dH8ZK6ED7Pg +``` + +- **场景**:模拟创建 createTopic,入参的对象为注入的操作,@Qualifier 注解可以指定要注入哪个名字的对象。之后 `@ConditionalOnProperty` 注解可以通过配置的 enabled 值,来确定是否实例化对象。 +- **用途**:这个场景是非常使用的,比如你做了一个组件,或者业务中要增加一些配置。启动或关闭某些服务,就可以使用了。而不需要把 pom 中引入的组件注释掉。 + +### 6. 自定义Condition,判断是否实例化对象 + +```java +public class BeanCreateCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + String active = System.getProperty("isOpenWhitelistedUsers"); + return null != active && active.equals("true"); + } + +} + +@Bean +@Conditional(BeanCreateCondition.class) +public List whitelistedUsers() { + return new ArrayList() {{ + add("user001"); + add("user002"); + add("user003"); + }}; +} + +static { + // BeanCreateCondition 会检测这个值,确定是否创建对象 + System.setProperty("isOpenWhitelistedUsers", "false"); +} + +@Autowired(required = false) +@Qualifier("whitelistedUsers") +private List whitelistedUsers; +``` + +- **场景**:是一个案例中使用到了 `@ConditionalOnProperty` 注解,我们也可以自定义一个 Conditional 的实现类,之后把这个实现类配置到需要实例化的对象上面,通过 matches 匹配条件方法的实现,决定是否实例化。 +- **用途**:这个场景的用途和 `@ConditionalOnProperty` 是一样的,只不过我们可以更好的自定义控制。 + +### 7. 根据环境配置实例化对象 + +```java +@Slf4j +@Service +// 用于根据配置环境实例化 Bean 对象 +@Profile({"prod", "test"}) +@Lazy +public class AliPayAwardService implements IAwardService { + + public AliPayAwardService() { + log.info("如一些支付场景,必须指定上线后才能实例化"); + } + + @Override + public void doDistributeAward(String userId) { + log.info("红包奖励 {}", userId); + } + +} + +spring: + config: + name: xfg-dev-tech-spring-dependency-injection + profiles: + active: dev +``` + +- **场景**:`@Profile({"prod", "test"})` 注解可以配置你是在什么时候实例化这个对象,我们可以指定 application.yml 中配置的 `active: dev/prod/test` 来确定是在开发、测试还是上线才实例化这个对象。 +- **用途**:一些只有到线上才能实例化对象的时候,就可以配置 `@Profile({"prod", "test"})` 注解,注意这个需要配合 `@Autowired(required = false)` 进行注入,否则会出现注入为空指针的异常。 + +### 8. 引入 Spring 配置 + +```java +@Slf4j +@SpringBootApplication +@Configurable +@PropertySource("classpath:properties/application.properties") +@ImportResource("classpath:spring/spring.xml") +@EnableScheduling +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class); + } + +} + + + + + + + + +@Slf4j +public class SpringBeanTest { + + public SpringBeanTest() { + log.info("我是通过 Spring 配置文件实例化的 Bean 对象"); + } + +} +``` + +- **场景**:在 SpringBoot 工程中,可以通过 `@ImportResource`、`@PropertySource` 引入对应的配置文件,完成对象的初始化。 +- **用途**:在实际的开发中,虽然使用 SpringBoot 工程,但为了兼容一些老的项目或者一些还没有升级到 SpringBoot Starter 的组件,则需要单独引入 Spring 配置文件来创建对象。 + +### 9. 原型对象 + +```java +@Component +@Scope("prototype") +public class LogicChain { +} + +@Resource +private ApplicationContext applicationContext; + +@Test +public void test_prototype() { + log.info("测试结果: {}", applicationContext.getBean(LogicChain.class).hashCode()); + log.info("测试结果: {}", applicationContext.getBean(LogicChain.class).hashCode()); +} +``` + +- **场景**:`@Scope("prototype")` 可以设定对象类型为原型对象,每次获得的对象都是一个新的实例化对象。 +- **用途**:对于动态,不同责任链创建,可以使用这个配置,确保每个对象都是自己的。 + +### 10. 其他注解 + +- `@EnableScheduling`:允许启动任务的注解,放到 Application 上,确保任务启动执行。 +- `@DependsOn({"openai_model", "openai_use_count", "user_credit_random"})` Bean 对象实例化中,依赖于哪些对象。 +- `@Autowired private Environment env;` 环境配置注入,可以获取到 application.yml 中的配置数据 `env.getProperty("app.name"), env.getProperty("app.version")` +- `@Async` 异步方法注解,可以用于调用某个方法后,让下面的具体逻辑方法为异步执行,主方法直接返回结果。可以用于一些申请导出数据到文件的场景。 + +## 三、源码分析 + +这里的 Map 注入比较有特点,小傅哥把它的流程和核心代码给大家描述下,方便感兴趣源码的伙伴,可以去看下源码调试跟进。 + +
+ +
+ +在 Spring 框架中,依赖注入(DI)是通过一系列的步骤和组件来实现的。对于构造函数注入,特别是注入 `Map` 类型的依赖,Spring 需要处理以下几个关键步骤: + +1. **Bean Definition 解析**:Spring 解析配置文件或注解,生成 BeanDefinition 对象。 +2. **Bean 实例化**:Spring 根据 BeanDefinition 创建 Bean 实例。 +3. **依赖注入**:Spring 将所需的依赖注入到 Bean 中。 + +### 1. 核心源码 + +具体到构造函数注入 `Map` 类型的依赖,Spring 主要通过以下源码来处理: + +#### 1.1 `AutowiredAnnotationBeanPostProcessor` + +```java +public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor { + // 省略其他代码 + + @Override + public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { + InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); + try { + metadata.inject(bean, beanName, pvs); + } catch (Throwable ex) { + throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); + } + return pvs; + } +} +``` + +`AutowiredAnnotationBeanPostProcessor` 是处理依赖注入的核心类之一。它会扫描 Bean 的构造函数、字段和方法上的 `@Autowired` 注解,并进行相应的依赖注入。 + +#### 1.2 `ConstructorResolver` + +```java +public class ConstructorResolver { + // 省略其他代码 + + public BeanWrapper autowireConstructor( + final String beanName, final RootBeanDefinition mbd, Constructor[] chosenCtors, final Object[] explicitArgs) { + + // 省略其他代码 + + Constructor constructorToUse = null; + ArgumentsHolder argsHolderToUse = null; + Object[] argsToUse = null; + + // 省略其他代码 + + for (Constructor candidate : candidates) { + Class[] paramTypes = candidate.getParameterTypes(); + if (argsToUse == null) { + // 省略其他代码 + argsHolder = createArgumentArray( + beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring); + } + // 省略其他代码 + } + + // 省略其他代码 + + BeanWrapperImpl bw = new BeanWrapperImpl(beanInstance); + initBeanWrapper(bw); + + return bw; + } +} +``` + +`ConstructorResolver` 是负责解析和调用构造函数的类。它会根据 BeanDefinition 和构造函数的参数类型,选择合适的构造函数并进行实例化。 + +#### 1.3 `DefaultListableBeanFactory` + +```java +public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory + implements ConfigurableListableBeanFactory, BeanDefinitionRegistry { + + // 省略其他代码 + + @Override + protected Map findAutowireCandidates(String beanName, Class requiredType, DependencyDescriptor descriptor) { + String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType); + Map result = new LinkedHashMap<>(candidateNames.length); + for (String candidate : candidateNames) { + if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { + result.put(candidate, getBean(candidate)); + } + } + if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) { + DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); + for (String candidate : candidateNames) { + if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) { + result.put(candidate, getBean(candidate)); + } + } + } + return result; + } +} +``` + +`DefaultListableBeanFactory` 是 Spring 中最常用的 BeanFactory 实现类。它负责管理 Bean 的定义和生命周期,并提供依赖查找和注入的功能。 + +### 2. 具体流程 + +1. **解析 BeanDefinition**: + Spring 解析配置文件或注解,生成 `AwardController` 的 `BeanDefinition` 对象。 +2. **选择构造函数**: + `AutowiredAnnotationBeanPostProcessor` 会扫描 `AwardController` 的构造函数,发现它有一个 `Map` 类型的参数。 +3. **查找依赖**: + `ConstructorResolver` 会根据构造函数参数的类型,查找 Spring 容器中所有 `IAwardService` 类型的 Bean,并将它们放入一个 `Map` 中。这个 `Map` 的键是 Bean 的名称,值是对应的 `IAwardService` 实例。 +4. **实例化 Bean**: + `ConstructorResolver` 使用找到的依赖,调用 `AwardController` 的构造函数,创建 `AwardController` 实例。 +5. **注入依赖**: + `DefaultListableBeanFactory` 将创建好的 `Map` 注入到 `AwardController` 的构造函数中。 diff --git a/docs/md/road-map/spring-oauth2-sso-01.md b/docs/md/road-map/spring-oauth2-sso-01.md new file mode 100644 index 000000000..9e029ad50 --- /dev/null +++ b/docs/md/road-map/spring-oauth2-sso-01.md @@ -0,0 +1,502 @@ +--- +title: OAuth2 SSO +lock: need +--- + +# OAuth2 SSO - 前后端分离,单点登录案例 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +当你进入一个较大一些的中大厂互联网公司以后,你会发现自己参与的业务系统开发,好像从来没有关心过关于用户的身份鉴权,而是直接拿到用户的ID就做业务了。那这里的鉴权跑到哪里去了呢?🤔 + +
+ +
+ +其实在公司里是一套统一的授权服务和组件的,并且维护用户的ID、用户的关联绑定也都是这套系统来处理的。之后这套系统会和 API 网关进行对接,等网关下发到你的后端服务系统时,在内部微服间流转就是真实的用户ID啦。 + +那么为了让伙伴们更好的理解关于 OAuth2 SSO 统一单点登录的前后端分离服务,小傅哥这里做了一个结合 Spring Security OAuth2很容易理解案例工程。学习后就可以扩展使用 SSO 到你自己的系统了,比如可以做一个统一的用户鉴权中心。 + +## 一、单点登录 + +单点登录(Single Sign-On,SSO)是一种认证技术,用户只需进行一次身份验证,就可以访问多个相互信任的应用系统,而无需再次输入凭证。SSO的主要目的是简化用户的登录过程,提高用户体验和安全性,同时减少管理多个用户名和密码的复杂性。 + +
+ +
+ +SSO的工作原理通常涉及以下几个步骤: + +1. **身份验证**:用户在第一次访问SSO系统时输入用户名和密码等凭据进行验证。 +2. **创建会话**:成功验证后,系统创建一个会话,可以是令牌、票证或其他凭据,以证明用户的身份。 +3. **访问授权**:当用户访问不同的应用时,SSO系统将会话信息传递给这些应用,以确认用户的身份并授予访问权限。 +4. **信任机制**:应用之间需要建立信任关系,通常通过共享密钥或使用公钥基础设施(PKI)来实现验证和授权。 + +SSO的优点包括: + +- **提高用户体验**:用户只需记住一个用户名和密码,减少了填写登录信息的次数。 +- **增强安全性**:集中管理用户身份,方便监控和保护密码策略。 +- **降低管理成本**:减少IT部门处理密码重置等事务的工作量。 + +## 二、案例工程 + +### 1. 编程环境 + +- JDK 1.8 + +- SpringBoot 2.6.2 + +- Maven 3.8.1 + +- Docker - 负责安装 Nginx,如果没有 Docker 就本地直接安装 Nginx + +- SwitchHosts - 切换host,映射自定义域名地址,可以避免跨域问题。如果没有就直接修改本地的 host 文件。你可以配置自己的。 + +```java +192.168.1.107 sso.xfg.com +192.168.1.107 client1.xfg.com +192.168.1.107 client2.xfg.com +``` + +- 工程:[https://github.com/fuzhengwei/xfg-dev-tech-oauth2-sso](https://github.com/fuzhengwei/xfg-dev-tech-oauth2-sso) + +### 2. 工程结构 + +
+ +
+ +- xfg-dev-tech-app 是 SSO Auth 的鉴权服务。 +- test 模块下有2个 client,方便验证一个登录成功后,另外一个不会再跳转登录了。 +- docs/dev-ops 下提供了 docker compose 脚本,用于部署 Nginx 以及配合的前后端分离的前端页面。 + +### 3. 鉴权服务 + +```yml +server: + port: 8091 + application: + name: xfg-dev-tech-sso + servlet: + context-path: /auth + session: + cookie: + name: OAuth2SSOToken + +``` + +- yml 配置了 auth 路径和一个 session 名称。 + +#### 3.1 鉴权配置 + +**AuthorizationServerConfig** + +```java +@Bean +public ClientDetailsService inMemoryClientDetailsService() throws Exception { + return new InMemoryClientDetailsServiceBuilder() + // client1 mall + .withClient("client1") + .secret(passwordEncoder.encode("client1_secret")) + .scopes("all") + .authorizedGrantTypes("authorization_code", "refresh_token") + .redirectUris("http://client1.xfg.com/client1/login") + .accessTokenValiditySeconds(7200) + .autoApprove(true) + .and() + + // client2 lottery + .withClient("client2") + .secret(passwordEncoder.encode("client2_secret")) + .scopes("all") + .authorizedGrantTypes("authorization_code", "refresh_token") + .redirectUris("http://client2.xfg.com/client2/login") + .accessTokenValiditySeconds(7200) + .autoApprove(true) + .and() + .build(); +} +``` + +- 配置鉴权信息,这里配置了两个客户端信息。 + +#### 3.2 验证入口 + +```java +@Component("unauthorizedEntryPoint") +public class AppUnauthorizedEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { + Map paramMap = request.getParameterMap(); + StringBuilder param = new StringBuilder(); + paramMap.forEach((k, v) -> { + param.append("&").append(k).append("=").append(v[0]); + }); + + param.deleteCharAt(0); + String isRedirectValue = request.getParameter("isRedirect"); + + if (!StringUtils.isEmpty(isRedirectValue) && Boolean.parseBoolean(isRedirectValue)) { + response.sendRedirect("http://sso.xfg.com/authPage/#/login?" + param); + return; + } + + String authUrl = "http://sso.xfg.com/auth/oauth/authorize?" + param + "&isRedirect=true"; + + Map result = new HashMap<>(); + result.put("code", 800); + result.put("msg", "授权地址"); + result.put("data", authUrl); + + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + PrintWriter writer = response.getWriter(); + ObjectMapper mapper = new ObjectMapper(); + writer.print(mapper.writeValueAsString(result)); + writer.flush(); + writer.close(); + } + +} +``` + +- 需要实现 AuthenticationEntryPoint 接口,配置一个转发的地址。 + +### 4. 客户端 - client1/client2 + +#### 4.1 client1 + +```java +server: + port: 8001 + servlet: + context-path: /client1 + +security: + oauth2: + client: + client-id: client1 + preEstablishedRedirectUri: + client-secret: client1_secret + access-token-uri: http://sso.xfg.com/auth/oauth/token + user-authorization-uri: http://sso.xfg.com/auth/oauth/authorize + resource: + user-info-uri: http://sso.xfg.com/auth/user + token-info-uri: http://sso.xfg.com/auth/oauth/check_token +``` + +```java +@RestController +public class Client01Controller { + + @GetMapping("/create_pay_order") + public Result createPayOrder() { + Result result = new Result(); + result.setCode(0); + result.setData("下单完成"); + return result; + } + + @GetMapping("/") + public void callback(HttpServletResponse response) throws IOException { + response.sendRedirect("http://client1.xfg.com/client1Page/#/home"); + } + +} +``` + +- 模拟下单,和 callback 地址配置。 + +#### 4.2 client2 + +```java +server: + port: 8002 + servlet: + context-path: /client2 + +security: + oauth2: + client: + client-id: client2 + client-secret: client2_secret + preEstablishedRedirectUri: + access-token-uri: http://sso.xfg.com/auth/oauth/token + user-authorization-uri: http://sso.xfg.com/auth/oauth/authorize + resource: + user-info-uri: http://sso.xfg.com/auth/user + token-info-uri: http://sso.xfg.com/auth/oauth/check_token +``` + +```java +@RestController +public class Client02Controller { + + @GetMapping("/lottery") + public Result lottery() { + Result result = new Result(); + result.setCode(0); + result.setData("下单红包,金额:" + RandomStringUtils.randomNumeric(10) + "元"); + return result; + } + + @GetMapping("/") + public void callback(HttpServletResponse response) throws IOException { + response.sendRedirect("http://client2.xfg.com/client2Page/#/home"); + } + +} +``` + +- 模拟另外一个微服务获取红包,以及 callback 地址服务。 + +### 5. 前端页面 + +#### 5.1 校验 + +```html + + + + +``` + +- 登录跳转操作,这里会走到 Nginx 中进行转发。 + +#### 5.2 客户端01 + +```html +
+ +

下单结果:

+
+ + + +``` + +- 下单的时候会检查是否登录,否则会被调整到 auth 校验。 + +#### 5.2 客户端02 + +```html +
+ +

红包结果:

+
+ + + +``` + +- 与 client1 的操作是一样的,但这里只要有一个登录了,另外一个就不会调整到 auth 页面登录了。 + +### 6. Nginx 配置 + +
+ +
+ +- Nginx 配置结构,docker compose 启动的时候会进行安装。 + +#### 6.1 sso.conf + +```java +server { + listen 80; + server_name sso.xfg.com; + + location /auth/ { + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://192.168.1.107:8091/auth/; + } + + location /authPage/ { + alias /usr/share/nginx/html/; + index auth.html; + } + + location ~ .*\.(js|css)$ { + alias /usr/share/nginx/html/; + index auth.html; + } + +} +``` + +#### 6.2 client1.conf + +```java +server { + listen 80; + server_name client1.xfg.com; + + location /client1/ { + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://192.168.1.107:8001/client1/; + } + + location /client1Page/ { + alias /usr/share/nginx/html/; + index client1.html; + } + + location ~ .*\.(js|css)$ { + alias /usr/share/nginx/html/; + index client1.html; + } +} +``` + +#### 6.3 client2.conf + +```java +server { + listen 80; + server_name client2.xfg.com; + + location /client2/ { + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_pass http://192.168.1.107:8002/client2/; + } + + location /client2Page/ { + alias /usr/share/nginx/html/; + index client2.html; + } + + location ~ .*\.(js|css)$ { + alias /usr/share/nginx/html/; + index client2.html; + } +} +``` + +> 更多的代码从工程中阅读即可,复杂度不高。 + +## 三、测试验证 + +### 1. 启动服务 + +
+ +
+ +
+ +
+ +- 你需要启动 Docker 的 Nginx,之后顺序启动 SSO 服务和2个客户端服务。 +- 另外要配置好 host,这样访问你的自定义域名地址,才会正确的跳转。(这东西在日常公司开发中会用到的很频繁) + +### 2. 访问客户端 + +
+ +
+ +你可以访问地址1进行验证,登录之后也可以进入地址2进行验证; + +- [http://client1.xfg.com/client1Page/#/home](http://client1.xfg.com/client1Page/#/home) +- [http://client2.xfg.com/client2Page/#/home](http://client2.xfg.com/client2Page/#/home) + +测试过程: + +- 首次登录,点击开始下单。会跳转登录,账密(xiaofuge/123456)。 +- 登录后再点击开始下单,则会看到下单完成,领红包。 +- 点击领红包,会跳转到 client2 的页面,点击随机红包,会进行服务端授权校验,之后就可以点击随机红包了。 diff --git a/docs/md/road-map/spring-oauth2.md b/docs/md/road-map/spring-oauth2.md new file mode 100644 index 000000000..fa0d1e13b --- /dev/null +++ b/docs/md/road-map/spring-oauth2.md @@ -0,0 +1,327 @@ +--- +title: OAuth2 +lock: need +--- + +# OAuth2 - 图解+案例,理解和实战OAuth2认证授权 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +你知道互联网大厂最怕的是什么吗?但凡有点这样的风吹草动,我们就要花费大量的时间进行修复和上线。一点都不敢耽误,对于紧急类型的,基本当天发现,当天就要升级上线。那是什么问题呢?🤔 + +
+ +
+ +**其实最怕的就是各类组件漏洞!** + +有这么一个东西,[13scan - 安全漏洞扫描](https://bugstack.cn/md/road-map/13scan-jdumpspider.html) 它可以扫描出系统组件的各项存在的漏洞,给出整改建议。因为这些漏洞的存在,就可能让不法用户通过接口调用到系统数据。比如,随意输入个订单号,就知道是谁,什么时间、购买的什么、地址在哪。这是非常可怕的。 + +所以,在互联网大厂中,会有统一的安全授权认证服务 OAuth2。这样即使有外部对接的系统确实需要授权获得用户的数据,也可以在可靠的范围内进行授权和使用。 + +那么,OAuth2 是个啥呢?🤔 本节我们来分享下并做个代码案例运行验证。 + +## 一、OAuth2 是啥? + +**OAuth 2.0 的标准 RFC 6749,解释了 OAuth 是什么。** + +
+ +
+ +**官网**:[https://datatracker.ietf.org/doc/html/rfc6749](https://datatracker.ietf.org/doc/html/rfc6749) + +OAuth 2.0 本身是一种开放标准,不是一个具体的服务类组件,而是一种标准。旨在为用户提供授权,允许第三方应用程序访问用户在某个服务提供者(如社交网络或云服务)上的信息,而无需将用户的凭证(如用户名和密码)透露给这些应用程序。OAuth 2.0 主要用于授权,而不是身份验证。 + +而 Spring 中 OAuth2 就是对这套标准的具体实现,但这不是唯一实现,你甚至可以通过这套标准做一套自己的 OAuth2 授权框架。 + +## 二、举个例子 + +大家在日常的生活中使用互联网类的产品,包括;购物、视频、出行等,都可能收到活动类的短信,问你是否要参与一个这样的活动,如果参与则需要点击授权允许。那么这个过程就有 OAuth2 的授权使用。如图; + +
+ +
+ +- 这是一套用户参与小傅哥分享的星球用户活动页面。用户点击参与后,会引导进入授权验证。显示进入微信登录,之后跳转到用户数据授权使用页。 +- 用户允许授权后,小傅哥的这套活动页就可以拿到用户个人的数据,通过个人的数据为唯一标识,允许用户参与本次活动。这些活动可以是一些抽奖、礼包领取、代码仓库授权等。这些场景的使用,就是 OAuth2 的授权框架作用。 + +## 三、授权方式 + +在看 OAuth2 之前,可以代入的思考下,如果是你做一个认证授权框架,你会怎么做。其实你在最开始学习编程使用账号密码在数据库里匹配验证,完成后生成一个 Token 让前端保存到 Cookie 里,之后每次请求后端都携带上这个 Cookie 进行校验。 + +其实这个模型就是认证授权框架。认证;使用账密证明你是你,授权,则通过账密分配一个Token,让使用放通过 Token 进行数据访问。 + +那么,OAuth2 作为认证授权框架,提供了四种授权访问,包括; +- 授权模型(authorization-code) +- 隐藏模式(简单授权)(implicit) +- 密码模式(password) +- 客户端凭证模式(client credentials) + +这四种授权方式,逐渐减弱。不过,无论那种授权方式,在第三方应用申请可调用数据的令牌前,都需要先完成系统备案,验明自身身份。包括客户端 ID、客户端秘钥 Client Secret。 + +### 1. 授权模型 + +授权模式:指第三方应用先申请一个授权码,之后再使用该码获得令牌。授权码模式通常用于具有浏览器界面的应用程序,尤其是在需要用户交互的场景下,例如传统的Web应用。由于使用了重定向和授权码,维护了更高的安全性。 + +
+ +
+ +**工作流程:** + +1. 用户在客户端(第三方应用)上点击登录。 +2. 客户端将用户重定向到授权服务器,携带其注册的客户端ID、重定向URI和请求的权限范围。 +3. 用户在授权服务器上验证身份,并同意授权后,授权服务器将用户重定向回客户端,附带一个授权码。 +4. 客户端使用该授权码向授权服务器请求访问令牌,同时发送客户端ID、客户端密钥和重定向URI。 +5. 授权服务器验证请求,并返回访问令牌(和可选的刷新令牌)。 +6. 客户端使用访问令牌访问受保护的资源。 + +### 2. 隐藏模式 + +隐式模式主要适用于在Web浏览器中运行的单页应用(SPA)等不安全的客户端环境,因为不需要后台服务器交换授权码,简化了流程。然而,隐式模式由于直接暴露令牌,安全性较低,不建议用于敏感操作。 + +
+ +
+ +**工作流程:** + +1. 用户在客户端上点击登录。 +2. 客户端将用户重定向到授权服务器,携带客户端ID、重定向URI及请求的权限范围。 +3. 用户在授权服务器进行身份验证,并同意授权后,授权服务器立即将访问令牌作为URI片段重定向回客户端。 +4. 客户端在接收到重定向后,解析URI以获取访问令牌,随后可直接使用该令牌访问受保护的资源。 + +### 3. 密码模式 + +密码模式适用于用户信任客户端的情况,如用户通过原生应用(移动应用)访问服务。在此情况下,客户端直接处理用户的凭据,使用时要确保应用的安全性。 + +
+ +
+ +**工作流程:** + +1. 用户在客户端直接输入其用户名和密码。 +2. 客户端将用户的凭据(用户名和密码)发送到授权服务器,请求访问令牌。 +3. 授权服务器验证凭据并返回访问令牌(和可选的刷新令牌)。 +4. 客户端使用访问令牌访问受保护的资源。 + +### 4. 客户端凭证模式 + +客户端凭证模式主要用于服务器与服务器之间的通信,如后台服务相互访问API,或者服务自身需要访问其资源。适用于没有用户上下文的场景,更多用于机器对机器(M2M)通信。 + +
+ +
+ +**工作流程** + +1. 客户端向授权服务器发送包含其客户端ID和客户端密钥的请求,请求访问令牌。 +2. 授权服务器验证客户端身份,并返回访问令牌。 +3. 客户端使用访问令牌访问受保护的资源,通常是与服务器本身相关的资源。 + +## 四、授权代码 + +有了上面的概念,我们再来看个实际的案例工程,验证四种授权模式。环境信息如下; + +- JDK 1.8 +- Maven 3.8.* +- MySQL 5.x ~ 8.x,案例使用的是 8.x + +### 1. 工程结构 + +
+ +
+ +- 首先,案例工程提供了 OAuth2 的授权框架 + 数据库配置实现。 +- docs 下提供了 docker compose 安装 MySQL 的脚本和导入库表的操作,这套库表就是授权框架的库表。 +- 验证功能的时候需要使用到 ApiPost,你可以下载使用,并到 api-json 导入。 + +### 2. 核心实现 + +除了 OAuth2 关于 Spring Security 部分已经在前面的课程讲解过,可以补充学习。[https://bugstack.cn/md/road-map/spring-security.html](https://bugstack.cn/md/road-map/spring-security.html) + +#### 2.1 账户认证 + +```java +@Override +public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String clientId; + if (authentication != null) { + Object principal = authentication.getPrincipal(); + if (principal instanceof User) { + User clientUser = (User) principal; + clientId = clientUser.getUsername(); + } else if (principal instanceof OauthAccountUserDetails) { + getClientIdByRequest(); + return (OauthAccountUserDetails) principal; + } else { + throw new UnsupportedOperationException(); + } + } else { + clientId = getClientIdByRequest(); + } + // 校验用户 - 直接从数据库查询 + OauthAccount account = oauthAccountDao.loadUserByUsername(clientId, username); + if (account == null || !account.getAccountNonDeleted()) { + throw new UsernameNotFoundException("err user is not found!"); + } + return new OauthAccountUserDetails(account, new ArrayList<>()); +} +``` + +#### 2.2 刷新授权 + +```java +@Bean +public TokenEnhancer additionalInformationTokenEnhancer() { + return (accessToken, authentication) -> { + Map information = new HashMap<>(8); + Authentication userAuthentication = authentication.getUserAuthentication(); + if (userAuthentication instanceof UsernamePasswordAuthenticationToken) { + UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) userAuthentication; + Object principal = token.getPrincipal(); + if (principal instanceof OauthAccountUserDetails) { + OauthAccountUserDetails userDetails = (OauthAccountUserDetails) token.getPrincipal(); + OauthAccount oauthAccount = userDetails.getOauthAccount(); + information.put("account_info", UserAccountVO.builder() + .id(oauthAccount.getId()) + .clientId(oauthAccount.getClientId()) + .username(oauthAccount.getUsername()) + .mobile(oauthAccount.getMobile()) + .email(oauthAccount.getEmail()) + .build()); + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(information); + } + } + return accessToken; + }; +} +``` + +#### 2.3 添加账户 + +
+ +
+ +```java +@Resource +private PasswordEncoder passwordEncoder; +@Test +public void test_passwordEncoder() { + log.info("测试结果:{}", passwordEncoder.encode("123456")); +} +``` + +- 这里测试可以生成一个需要的密码,账户填写到数据库中使用。 + +### 3. 测试验证 + +在测试之前,你要启动服务,确保运行没问题。启动前配置数据库连接。 + +```java +spring: + datasource: + username: root + password: 123456 + url: jdbc:mysql://192.168.1.109:13306/xfg-dev-tech-oauth2?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&useSSL=true + driver-class-name: com.mysql.cj.jdbc.Driver +``` + +- 关于 ApiPost 的测试,你可以直接从工程中的 json 导入的你的 ApiPost 就可以使用了。 + +#### 3.1 客户端凭证 + +
+ +
+ +```java +{ + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYmlnLW1hcmtldC1hcHAiXSwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImV4cCI6MTczNjY3OTA4MCwiYXV0aG9yaXRpZXMiOlsidXNlciJdLCJqdGkiOiI4NWY0YjY2Ni1mNDliLTRiNGEtOTM1Ni0xYjRiMTVmZmI5MWEiLCJjbGllbnRfaWQiOiJ4Zmctc3R1ZGlvIn0.CqMOMbBkHMnQicpkBEeqMyJEp9HbSiGgXoYUke_PWtI", + "token_type": "bearer", + "expires_in": 7198, + "scope": "read write", + "jti": "85f4b666-f49b-4b4a-9356-1b4b15ffb91a" +} +``` + +- 请求:[http://127.0.0.1:8091/oauth/token?grant_type=client_credentials](http://127.0.0.1:8091/oauth/token?grant_type=client_credentials) +- 认证:`xfg-studio/123456` + +#### 3.2 用户密码模式 + +
+ +
+ +```java +{ + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYmlnLW1hcmtldC1hcHAiXSwiZXhwIjoxNzM2Njc5MTQxLCJ1c2VyX25hbWUiOiJ4aWFvZnVnZSIsImp0aSI6ImVhZWMzZmQ0LTViOTAtNGRhNy1hODQ1LTA2MDFmMjJiNDc2ZCIsImNsaWVudF9pZCI6InhmZy1zdHVkaW8iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.JgUxx6_aHqCBxuvYXvekw-ZW5pPnSw5LEKlfsd4qVyI", + "token_type": "bearer", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYmlnLW1hcmtldC1hcHAiXSwidXNlcl9uYW1lIjoieGlhb2Z1Z2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiYXRpIjoiZWFlYzNmZDQtNWI5MC00ZGE3LWE4NDUtMDYwMWYyMmI0NzZkIiwiZXhwIjoxNzM5MjYzOTQyLCJqdGkiOiI5ZDc4ZjVjZS0xZTMwLTRiZTYtYWUyNi01NjY1NWQ4YjYzZjIiLCJjbGllbnRfaWQiOiJ4Zmctc3R1ZGlvIn0.8gMfqhBnc4wI9BsRENu_16RmZFqeCWVSyWcF4B9nA1I", + "expires_in": 7198, + "scope": "read write", + "account_info": { + "id": null, + "clientId": "xfg-studio", + "username": "xiaofuge", + "mobile": "13500002222", + "email": "523088136@qq.com" + } +} +``` + +- 请求:[http://127.0.0.1:8091/oauth/token](http://127.0.0.1:8091/oauth/token) +- 认证:`xfg-studio/123456` +- 参数:`grant_type = password`、`username = xiaofuge`、`password = 123456` + +#### 3.3 授权模式 + +##### 3.3.1 登录认证 + +
+ +
+ +```java +{ + "status": 200, + "message": "hi login success!" +} +``` + +- 请求:[http://127.0.0.1:8091/login](http://127.0.0.1:8091/login) +- 认证:`xfg-studio/123456` +- 参数:`username = xiaofuge`、`password = 123456` +- 说明:你会拿到一个 Cookie `JSESSIONID=9000E64733AA6E947054AC4326C91AF8` 这个 cookie 用于获取授权码 + +##### 3.3.2 获取授权码&跳转 + +
+ +
+ +
+ +
+ +
+ +
+ +- 请求:[http://127.0.0.1:8091/oauth/authorize?client_id=xfg-studio&response_type=code&grant_type=authorization_code](http://127.0.0.1:8091/oauth/authorize?client_id=xfg-studio&response_type=code&grant_type=authorization_code) +- 认证:无 +- 参数:`client_id = xfg-studio`、`response_type = code`、`grant_type=authorization_code` +- 注意:如果 oauth_client_details 表字段配置 autoapprove = false 则不会直接跳转页面,会进行让用户确认。 + +之后刷新令牌、检查令牌,就可以单独测试了。如果部署到云服务器,那么还可以走浏览器访问,单独有一个获取令牌的操作,之后再跳转地址。 diff --git a/docs/md/road-map/spring-security.md b/docs/md/road-map/spring-security.md new file mode 100644 index 000000000..2994d05bc --- /dev/null +++ b/docs/md/road-map/spring-security.md @@ -0,0 +1,777 @@ +--- +title: Spring Security +lock: need +--- + +# Spring Security + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +在互联网大厂这些年做研发这么多年,有一个非常指导性的开发原则就是;你做的这个东西是否能让整个大组内的其他系统使用。所以,从15年入职开始,我有的各种创新的想法都落地实现了,一直被使用到现在。`那些组件也都成了一个个技术专利 👍🏻` + +
+ +
+ +但其实这样对通用逻辑的共性凝练,开发成统一的组件,在 Spring 框架中是有非常多的,这些东西也都在我们日常使用 Spring 时有所涉猎。包括我们本节要讲解的 Spring Security 组件。 + +当你想让系统实现登录校验的服务,那么你不可能在每个系统里都写认证和授权服务,那么这个时候就要做一套统一的认证框架。这里 Spring Security 就是专注于为 Java 应用程序提供身份验证和授权的框架。提供;验证、授权、防止会话固定、点击劫持、跨域请求等。 + +## 一、为啥需要安全框架 + +举个例子;SpringBoot 有一个 actuator 框架,可以配合普罗米修斯做系统监控。包括采集系统的接口运行数据、JVM 信息、负载等各项指标。 + +```java +# 监控 +management: + endpoints: + web: + exposure: + include: "*" + endpoint: + health: + show-details: always + metrics: + export: + prometheus: + enabled: true + prometheus: + enabled: true +``` + +虽然很好用,但如果你一点安全都不加就直接在工程中配置,那么在网安人员的手里,基本就是一台肉鸡。一套 w13scan 正向代理,就能把你的工程底裤拔掉,拿出你的数据库账号密码。 + +操作教程:[https://bugstack.cn/md/road-map/13scan-jdumpspider.html](https://bugstack.cn/md/road-map/13scan-jdumpspider.html) + +```java +/bin/zsh /xiaofuge/develop/github/xfg-dev-tech-w13scan-jdumpspider/docs/jdumpspider.sh +(base) xiaofuge@ZBMac-GV47H1GXD docs % /bin/zsh /Users/xiaofuge/Documents/develop/github/xfg-dev-tech-w13scan-jdumpspider/docs/jdumpspider.sh +=========================================== +SpringDataSourceProperties +------------- +password = 123456 +driverClassName = com.mysql.cj.jdbc.Driver +url = jdbc:mysql://127.0.0.1:13306/road_map?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC&useSSL=true +username = root +``` + +所以,任何你暴漏出去对外使用的服务,都要做安全框架的认证和授权。 + +## 二、认证和授权的原理 + +有不少伙伴在初次使用 Spring Security 框架的时候,会觉得复杂度有些高。其实在之前没有 SpringBoot 之前,Security 这个框架使用是更复杂的。这也主要是因为 Security 支持的灵活性更高,所以抽象的也更复杂。但其实能做一个完整的小案例,也就不会觉得有多复杂了。 + +
+ +
+ +- 其实 Spring Security 要做也就2件事,认证(Authentication)你是谁,授权(Authorization)你干啥。其实就算你不使用 Spring Security 你自己做一个登录的功能,以及允许登录的用户可以操作的流程,也要做这样的事情。 +- Spring Security 在内部维护一个过滤器链,其中每个过滤器都有特定的职责,并且根据所需的服务在配置中添加或删除过滤器。过滤器的顺序很重要,因为它们之间存在依赖关系。 +- 文档:[https://docs.spring.io/spring-security/site/docs/3.0.x/reference/security-filter-chain.html](https://docs.spring.io/spring-security/site/docs/3.0.x/reference/security-filter-chain.html) - Web 应用程序安全过滤链 + +## 三、一个简单的小案例 + +Spring Security 提供了最简单的登录访问校验配置,如果你只是想给一些页面添加一个登录页面。那么只需要配置下 pom 和 yml 中提供的用户账密就可以登录校验了。 + +源码:[https://github.com/fuzhengwei/xfg-dev-tech-spring-security-demo](https://github.com/fuzhengwei/xfg-dev-tech-spring-security-demo) + +### 1. 引入 pom + +```java + + org.springframework.boot + spring-boot-starter-security + +``` + +### 2. 配置yml + +```java +server: + port: 8099 + +spring: + security: + user: + name: xiaofuge + password: 123456 +``` + +- security 的账号密码,就是你访问一个链接需要登录的账密。 +- 如果不配置账号,它会有一个固定的 user 加上在启动程序时控制台会随机生成密码。 + +### 3. 测试接口 + +```java +@RestController +@RequestMapping("/api/auth/") +public class TestController { + + @GetMapping("hi") + public String hi(){ + return "hi security"; + } +} +``` + +### 4. 访问验证 + +地址:[http://localhost:8099/api/test/hi](http://localhost:8099/api/test/hi) + +
+ +
+ +- 首次访问地址后,会自动跳转到一个登录页面。属于我们配置的账密进行登录。登录完成后,这里会写入一个 cookie,之后就可以访问我们的接口信息了。 + +## 四、正式工程案例对接 + +### 1. 工程结构 + +
+ +
+ +- 地址:[https://github.com/fuzhengwei/xfg-dev-tech-spring-security](https://github.com/fuzhengwei/xfg-dev-tech-spring-security) +- 这是一套在 DDD 六边形分层结构中添加的 Spring Security 认证框架。如图,介绍了分层模块的使用。 +- 接下来我们注册讲解这些代码块的用途。 + +### 2. GuavaConfig - 本地缓存模拟用户 + +```java +@Slf4j +@Configuration +public class GuavaConfig { + + @Bean(name = "userCache") + public Cache userCache(PasswordEncoder passwordEncoder) { + Cache cache = CacheBuilder.newBuilder() + .expireAfterWrite(365, TimeUnit.DAYS) + .build(); + + UserEntity userEntity01 = UserEntity.builder() + .userName("xiaofuge") + .password(passwordEncoder.encode("123456")) + .roles(Arrays.asList(RoleTypeEnum.ADMIN)) + .build(); + + UserEntity userEntity02 = UserEntity.builder() + .userName("liergou") + .password(passwordEncoder.encode("123456")) + .roles(Arrays.asList(RoleTypeEnum.USER)) + .build(); + + log.info("测试账密01 xiaofuge/123456 权限;admin"); + log.info("测试账密02 liergou/123456 权限;user"); + + cache.put(userEntity01.getUserName(), userEntity01); + cache.put(userEntity02.getUserName(), userEntity02); + return cache; + } + +} +``` + +- 程序启动后,模拟注册完成的用户用户测试验证。用户也可以在测试中自己在注册用户。 + +### 3. UserDetails 用户身份信息 + +#### 3.1 身份实现 + +```java +public class UserDetailAuthSecurity implements UserDetails { + + @Serial + private static final long serialVersionUID = 931859819772024712L; + + private final UserEntity userEntity; + + public UserDetailAuthSecurity(UserEntity userEntity) { + this.userEntity = userEntity; + } + + @Override + public Collection getAuthorities() { + return userEntity.getRoles() + .stream() + .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getCode())) + .collect(Collectors.toList()); + } + + @Override + public String getPassword() { + return userEntity.getPassword(); + } + + @Override + public String getUsername() { + return userEntity.getUserName(); + } + + // ... + +} +``` + +- 做授权校验是基于用户的 UserDetails 详细身份进行的。这东西就是一个依赖倒置,Spring 定义好接口标准,之后由使用方实现。 + +#### 3.2 身份获取 + +```java +@Service +public class UserDetailsServiceImpl implements UserDetailsService { + + @Resource + private Cache userCache; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + UserEntity userEntity = userCache.getIfPresent(username); + if (null == userEntity) return null; + return new UserDetailAuthSecurity(userEntity); + } + +} +``` + +- 这里还需要对 UserDetails 包装一层提供一个 UserDetailsService 接口的实现类。 + +### 4. 授权&校验处理 + +#### 4.1 JwtAuthenticationProvider - 验证账密 + +```java +public class JwtAuthenticationProvider implements AuthenticationProvider { + + private final PasswordEncoder passwordEncoder; + private final UserDetailsService userDetailsService; + + public JwtAuthenticationProvider(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) { + this.passwordEncoder = passwordEncoder; + this.userDetailsService = userDetailsService; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String username = String.valueOf(authentication.getPrincipal()); + String password = String.valueOf(authentication.getCredentials()); + + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + if (passwordEncoder.matches(password, userDetails.getPassword())) { + return new UsernamePasswordAuthenticationToken(username, password, userDetails.getAuthorities()); + } + + throw new BadCredentialsException("Auth Error!"); + } + + @Override + public boolean supports(Class authentication) { + return UsernamePasswordAuthenticationToken.class.equals(authentication); + } + +} +``` + +- 这一部分是获取用户名和密码,通过 userDetailsService 获取信息进行密码比对。这个就和我们自己要做一个登录校验的方式是一样的。 + +#### 4.2 JwtAuthenticationTokenFilter - 校验登录 + +```java +@Slf4j +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { + + private final static String AUTH_HEADER = "Authorization"; + private final static String AUTH_HEADER_TYPE = "Bearer"; + + private final UserDetailsService userDetailsService; + + public JwtAuthenticationTokenFilter(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String authHeader = request.getHeader(AUTH_HEADER); + if (Objects.isNull(authHeader) || !authHeader.startsWith(AUTH_HEADER_TYPE)){ + filterChain.doFilter(request,response); + return; + } + + String authToken = authHeader.split(" ")[1]; + log.info("authToken:{}" , authToken); + + if (!JWTUtil.verify(authToken, "key".getBytes(StandardCharsets.UTF_8))) { + filterChain.doFilter(request,response); + return; + } + + final String userName = (String) JWTUtil.parseToken(authToken).getPayload("username"); + UserDetails userDetails = userDetailsService.loadUserByUsername(userName); + + UsernamePasswordAuthenticationToken authentication = + new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + + SecurityContextHolder.getContext().setAuthentication(authentication); + + filterChain.doFilter(request, response); + } + +} +``` + +```java +fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` // Include the token in the request headers + } +}) +``` + +- 这一部分是对 http 请求信息中的 Authorization Bearer 后面带有的 token 信息进行解析校验。如代码中提供了一部分前端请求代码,就是这里的 Token + +### 5. 认证&授权配置 + +```java +@Configuration +@EnableWebSecurity +@EnableMethodSecurity(prePostEnabled = true) +public class SpringSecurityConfig { + + // 不拦截的 URL + private final String[] requestMatchers = {"/api/auth/login", "/api/auth/register", "/api/auth/query_user_name", "/test/**"}; + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception { + return authConfig.getAuthenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(UserDetailsService userDetailsService) { + return new JwtAuthenticationTokenFilter(userDetailsService); + } + + @Bean + public JwtAuthenticationProvider jwtAuthenticationProvider(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) { + return new JwtAuthenticationProvider(passwordEncoder, userDetailsService); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity httpSecurity, + JwtAuthenticationProvider jwtAuthenticationProvider, + JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter, + AppUnauthorizedHandler appUnauthorizedHandler, + AppAccessDeniedHandler appAccessDeniedHandler + ) throws Exception { + // 使用JWT,可屏蔽csrf防护 + httpSecurity.csrf(CsrfConfigurer::disable) + // 基于token存储到浏览器,不需要session + .sessionManagement(sessionManagementConfigurer -> sessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(authorizationRegistry -> authorizationRegistry + // 允许对于网站静态资源的无授权访问 + .requestMatchers(HttpMethod.GET, "/", "/*.html").permitAll() + // 对登录注册允许匿名访问 + .requestMatchers(requestMatchers).permitAll() + // 访问授权,所有 /user/** 路径下的请求需要 ADMIN 角色。注意;Spring Security在处理角色时,会自动为角色名添加"ROLE_"前缀。因此,"ADMIN"角色实际上对应权限"ROLE_ADMIN"。 + .requestMatchers("/api/mall/**").permitAll() + // 跨域请求会先进行一次options请求 + .requestMatchers(HttpMethod.OPTIONS).permitAll() + // 对所有请求开启授权保护 + .anyRequest() + // 已认证的请求自动被授权 + .authenticated() + ) + // 禁用缓存 + .headers(headersConfigurer -> headersConfigurer + .cacheControl(HeadersConfigurer.CacheControlConfig::disable) + ) + // 使用自定义 provider + .authenticationProvider(jwtAuthenticationProvider) + // 添加 JWT filter + .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) + // 添加自定义未授权和未登录结果返回 + .exceptionHandling(exceptionConfigure -> exceptionConfigure + .accessDeniedHandler(appAccessDeniedHandler) + .authenticationEntryPoint(appUnauthorizedHandler)); + + return httpSecurity.build(); + } + +} +``` + +- 那么这里所做的就是认证授权的配置,对哪些URL进行放行,哪些是要做拦截。 +- appAccessDeniedHandler、appUnauthorizedHandler,是自定义的鉴权拦截,如果登录不通过,可以统一返回给前端一个固定的错误码,便于跳转登录。 + +### 6. 注册登录 + +```java +@Service +public class AuthService implements IAuthService { + + @Autowired + private PasswordEncoder passwordEncoder; + + @Resource + private Cache userCache; + + @Autowired + private AuthenticationManager authenticationManager; + + @Override + public void register(String userName, String password) { + + UserEntity userEntity = UserEntity.builder() + .userName(userName) + .password(passwordEncoder.encode(password)) + .roles(Arrays.asList(RoleTypeEnum.USER, RoleTypeEnum.ADMIN)) + .build(); + + userCache.put(userName, userEntity); + } + + @Override + public String login(String userName, String password) { + // 登录验证 + authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, password)); + // 验证通过,获取 token + String token = JWT.create() + .setExpiresAt(new Date(System.currentTimeMillis() + (1000 * 30))) + .setPayload("username", userName) + .setKey("key".getBytes(StandardCharsets.UTF_8)) + .sign(); + + return token; + } + +} +``` + +- 在 domain 模块中提供了一个简单的注册&登录服务。注册就是简单的像本地缓存 Guava 写入数据。登录校验会调用登录密码校验处理。在登录成功后返回 JWT 生成的 token 信息。 + +### 7. 访问拦截 + +#### 1. 认证授权 + +```java +@Slf4j +@CrossOrigin("*") +@RestController +@RequestMapping("/api/auth/") +public class AuthController { + + @Resource + private IAuthService authService; + + @Autowired + private AuthenticationManager authenticationManager; + + @PostMapping("query_user_name") + public Response queryUserName() { + try { + // 获取当前认证的用户信息 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + Object principal = authentication.getPrincipal(); + return Response.builder() + .code(Response.ResponseCode.SUCCESS.getCode()) + .info(Response.ResponseCode.SUCCESS.getInfo()) + .data(principal.toString()) + .build(); + } catch (Exception e) { + return Response.builder() + .code(Response.ResponseCode.UN_ERROR.getCode()) + .info(Response.ResponseCode.UN_ERROR.getInfo()) + .build(); + } + } + + @PostMapping("register") + public Response register(@RequestParam String userName, @RequestParam String password) { + try { + log.info("注册用户:{}", userName); + authService.register(userName, password); + return Response.builder() + .code(Response.ResponseCode.SUCCESS.getCode()) + .info(Response.ResponseCode.SUCCESS.getInfo()) + .data(true) + .build(); + } catch (Exception e) { + log.info("注册用户失败:{}", userName); + return Response.builder() + .code(Response.ResponseCode.UN_ERROR.getCode()) + .info(Response.ResponseCode.UN_ERROR.getInfo()) + .build(); + } + } + + @PostMapping("login") + public Response login(@RequestParam String userName, @RequestParam String password) { + try { + log.info("登录用户:{}", userName); + // 登录获取 token + String token = authService.login(userName, password); + + return Response.builder() + .code(Response.ResponseCode.SUCCESS.getCode()) + .info(Response.ResponseCode.SUCCESS.getInfo()) + .data(token) + .build(); + } catch (Exception e) { + log.info("登录用户失败:{}", userName); + return Response.builder() + .code(Response.ResponseCode.UN_ERROR.getCode()) + .info(Response.ResponseCode.UN_ERROR.getInfo()) + .build(); + } + } + +} +``` + +- 提供注册、登录和查询用户信息接口。 +- 查询用户有些场景是会通过路径地址获取用户id,再根据用户id查询。但一些安全级别较高的,甚至不会透彻用户id,而是校验登录token,之后缓存用户id在使用。 + +#### 2. 角色权限 + +```java +@Slf4j +@CrossOrigin("*") +@RestController +@RequestMapping("/api/mall/") +public class MallController { + + @PreAuthorize("hasRole('ADMIN')") +// @PreAuthorize("hasRole('USER')") + @RequestMapping(value = "create_pay_order", method = RequestMethod.POST) + public Response createPayOrder(@RequestBody CreatePayRequestDTO createPayRequestDTO) { + try { + // 获取当前认证的用户信息 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + Object principal = authentication.getPrincipal(); + + String userName = (String) principal; + String productId = createPayRequestDTO.getProductId(); + + log.info("商品下单,根据商品ID创建支付单开始 userName:{} productId:{}", userName, productId); + + return Response.builder() + .code(Response.ResponseCode.SUCCESS.getCode()) + .info(Response.ResponseCode.SUCCESS.getInfo()) + .data(userName + " 下单成功。单号:" + RandomStringUtils.randomAlphabetic(12)) + .build(); + } catch (Exception e) { + log.error("商品下单,根据商品ID创建支付单开始 productId:{}", createPayRequestDTO.getProductId(), e); + return Response.builder() + .code(Response.ResponseCode.UN_ERROR.getCode()) + .info(Response.ResponseCode.UN_ERROR.getInfo()) + .build(); + } + } + +} +``` + +- 用户登录完成后,提供一个下单接口。 +- 注意,接口上有;ADMIN、USER 权限注解,我们在配置默认账号的时候,给xiaofuge是 ADMIN权限,liergou 是USER权限。配置不同的注解,会导致下单成功或者失败。 + +## 五、通过页面验证逻辑 + +这里小傅哥提供了一套前端简单的验证页面,方便大家学习; + +
+ +
+ +- 如果感兴趣前端代码的调用,也可以进入源码学习。 + +```java + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v3.1.6) + +24-12-15.11:22:10.786 [main ] INFO Application - Starting Application using Java 17.0.12 with PID 3656 (/Users/fuzhengwei/1024/KnowledgePlanet/road-map/xfg-dev-tech-spring-security/xfg-dev-tech-app/target/classes started by fuzhengwei in /Users/fuzhengwei/1024/KnowledgePlanet/road-map/xfg-dev-tech-spring-security) +24-12-15.11:22:10.789 [main ] INFO Application - The following 1 profile is active: "dev" +24-12-15.11:22:12.330 [main ] INFO TomcatWebServer - Tomcat initialized with port(s): 8091 (http) +24-12-15.11:22:12.340 [main ] INFO Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8091"] +24-12-15.11:22:12.343 [main ] INFO StandardService - Starting service [Tomcat] +24-12-15.11:22:12.343 [main ] INFO StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.16] +24-12-15.11:22:12.466 [main ] INFO [/] - Initializing Spring embedded WebApplicationContext +24-12-15.11:22:12.466 [main ] INFO ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1615 ms +24-12-15.11:22:12.705 [main ] INFO GuavaConfig - 测试账密01 xiaofuge/123456 权限;admin +24-12-15.11:22:12.705 [main ] INFO GuavaConfig - 测试账密02 liergou/123456 权限;user +24-12-15.11:22:13.188 [main ] INFO DefaultSecurityFilterChain - Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@1e225820, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@8aeab9e, org.springframework.security.web.context.SecurityContextHolderFilter@3332c7a5, org.springframework.security.web.header.HeaderWriterFilter@5099c59b, org.springframework.security.web.authentication.logout.LogoutFilter@6ac4c3f7, cn.bugstack.xfg.dev.tech.config.security.JwtAuthenticationTokenFilter@65eb76cd, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@3e39baf0, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@25a94b55, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2a2dc0a, org.springframework.security.web.session.SessionManagementFilter@3635099, org.springframework.security.web.access.ExceptionTranslationFilter@5eb5da12, org.springframework.security.web.access.intercept.AuthorizationFilter@654e6a90] +24-12-15.11:22:13.372 [main ] INFO Http11NioProtocol - Starting ProtocolHandler ["http-nio-8091"] +24-12-15.11:22:13.419 [main ] INFO TomcatWebServer - Tomcat started on port(s): 8091 (http) with context path '' +24-12-15.11:22:13.432 [main ] INFO Application - Started Application in 3.597 seconds (process running for 4.098) +``` + +- 测试前启动 SpringBoot 服务。 + +### 1. 首次登录 + +地址:[login.html](login.html) + +```java +function login() { + const username = document.getElementById('username').value; + const password = document.getElementById('password').value; + + fetch('http://127.0.0.1:8091/api/auth/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: new URLSearchParams({ + userName: username, + password: password + }) + }) + .then(response => response.json()) + .then(data => { + if (data.code === '0000') { + // Store token in localStorage on successful login + localStorage.setItem('xfg-dev-tech-spring-security-token', data.data); + window.location.href = 'index.html'; // 假设登录成功后跳转到首页 + } else { + alert('登录失败: ' + data.info); + } + }) + .catch(error => { + console.error('Error during login:', error); + alert('登录失败'); + }); +} +``` + +
+ +
+ +- 测试账号;`xiaofuge/123456`、`liergou/123456`,xiaofuge 是 admin 权限,liergou 是 user 权限,你可以分别测试验证。 +- 你还可以自己注册新的账号进行验证。 + +### 2. 首页下单 + +```java +document.addEventListener("DOMContentLoaded", function () { + var token = localStorage.getItem('xfg-dev-tech-spring-security-token'); + if (!token) { + window.location.href = "login.html"; // Redirect to the login page + return; + } + + var productId = "100010090091"; + var url = 'http://127.0.0.1:8091/api/auth/query_user_name'; + + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` // Include the token in the request headers + } + }) + .then(response => response.json()) // Parse the JSON response + .then(json => { + const userNameDisplay = document.getElementById('userNameDisplay'); + if (json.code === "0000") { + userNameDisplay.textContent = json.data; + } else { + userNameDisplay.textContent = '未登录'; + } + }) + .catch(error => { + console.error('Error fetching user name:', error); + document.getElementById('userNameDisplay').textContent = '未登录'; + }); + + }); + +document.getElementById('orderButton').addEventListener('click', function() { + var token = localStorage.getItem('xfg-dev-tech-spring-security-token'); + if (!token) { + window.location.href = "login.html"; // Redirect to the login page + return; + } + + var productId = "100010090091"; + var url = 'http://127.0.0.1:8091/api/mall/create_pay_order'; + + var requestBody = { + productId: productId + }; + + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` // Include the token in the request headers + }, + body: JSON.stringify(requestBody) // Convert the request body to a JSON string + }) + .then(response => response.json()) // Parse the JSON response + .then(json => { + if (json.code === "0000") { // Assume success code is "0000" + alert(json.data); + } else { + alert("code:"+json.code +" "+json.info) + console.error('Error:', json.info); // Output error information + } + }) + .catch(error => console.error('Error:', error)); +}); +``` + +
+ +
+ +- 登录成功后可以通过浏览器 F12 查看到登录的 Token,如果要取消登录,可以操作代码把 Token 删掉。 +- 登录成功后就可以点击下单了。默认代码的权限配置的是只有 xiaofuge 可以下单,liergou不能下单。 + +#### 2.1 下单通过 + +
+ +
+ +```java +24-12-15.11:26:48.606 [http-nio-8091-exec-6] INFO MallController - 商品下单,根据商品ID创建支付单开始 userName:xiaofuge productId:100010090091 +24-12-15.11:26:57.721 [http-nio-8091-exec-7] INFO JwtAuthenticationTokenFilter - authToken:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MzQyMzMwMDYsInVzZXJuYW1lIjoieGlhb2Z1Z2UifQ.Dm1Mi_lmm-O9MTte77252ATzNca_2s5bZG_90mdq-48 +24-12-15.11:26:59.069 [http-nio-8091-exec-8] INFO JwtAuthenticationTokenFilter - authToken:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MzQyMzMwMDYsInVzZXJuYW1lIjoieGlhb2Z1Z2UifQ.Dm1Mi_lmm-O9MTte77252ATzNca_2s5bZG_90mdq-48 +``` + +#### 2.2 下单拒绝 + +
+ +
+ +```java +24-12-15.11:26:59.074 [http-nio-8091-exec-8] INFO MallController - 商品下单,根据商品ID创建支付单开始 userName:xiaofuge productId:100010090091 +24-12-15.11:27:50.157 [http-nio-8091-exec-9] INFO AuthController - 登录用户:liergou +24-12-15.11:27:50.393 [http-nio-8091-exec-1] INFO JwtAuthenticationTokenFilter - authToken:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MzQyMzMzMDAsInVzZXJuYW1lIjoibGllcmdvdSJ9.mVRzMx9tLj6A-oEDRElZwfRslhmP5AE0gQGeA6oI0N4 +24-12-15.11:27:51.438 [http-nio-8091-exec-3] INFO JwtAuthenticationTokenFilter - authToken:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MzQyMzMzMDAsInVzZXJuYW1lIjoibGllcmdvdSJ9.mVRzMx9tLj6A-oEDRElZwfRslhmP5AE0gQGeA6oI0N4 +24-12-15.11:27:51.449 [http-nio-8091-exec-3] ERROR AppAccessDeniedHandler - access error +org.springframework.security.access.AccessDeniedException: Access Denied +``` + +- 如日志,当权限拦截后,就会弹出指定的错误码信息。 +- 权限拦截是在 AppAccessDeniedHandler 实现类中指定的错误码。 diff --git a/docs/md/road-map/springcloud-bus.md b/docs/md/road-map/springcloud-bus.md new file mode 100644 index 000000000..2d1942874 --- /dev/null +++ b/docs/md/road-map/springcloud-bus.md @@ -0,0 +1,422 @@ +--- +title: SpringCloud Bus +lock: need +--- + +# SpringCloud Bus 消息总线 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在互联网公司中开发的项目经常有一种场景,是在不重启应用的情况下,变更应用中某个属性信息的值。比如,我们为系统新增加允许外部调用接入的SC渠道值,测试阶段验证名单PIN、持续发布上线后的切量。这些东西都是不重启应用的情况下,动态做配置变更,那这样的东西在 SpringCloud 有什么现成的组件可以使用呢? + +
+ +
+ +**在大厂这个组件叫什么?** + +这个东西它不同于 Redis,而是把配置写到本地类对应的属性上。而不是像 Redis 那样从一个统一的地方每次去取使用。在大厂中我们管这个组件叫统一配置中,专门应对分布式工程中类对应属性的值的控制。 + +在小傅哥的大营销项目中,也带着大家实现过这样一款组件,叫[DCC](https://bugstack.cn/md/project/big-market/api/%E7%AC%AC29%E8%8A%82%EF%BC%9A%E5%88%86%E5%B8%83%E5%BC%8F%E5%8A%A8%E6%80%81%E9%85%8D%E7%BD%AE%E6%B4%BB%E5%8A%A8%E9%99%8D%E7%BA%A7.html),基于 Zookeeper + AOP 切面实现。 + +那么我们本节来看看 SpringCloud 是如何来处理这样一个场景的。 + +## 一、组件介绍 + +Spring Cloud Bus 将分布式系统的节点与轻量级消息代理相链接。然后可以使用它来广播状态更改(例如配置更改)或其他管理指令。该项目包含 AMQP 和 Kafka 代理实现。 + +
+ +
+ +在微服务架构中,通常使用轻量级的消息代理来创建一个共享的消息主题,让所有微服务实例都可以连接到这个主题上。因为这个主题中的消息会被所有实例监听和消费,因此通常称之为“消息总线”。连接到总线的每个实例都可以轻松地广播消息,以便所有其他连接该主题的实例能够接收到这些信息。 + +## 二、测试工程 + +小傅哥这里搭建了一套用于测试验证 SpringCloud Bus 消息总线的服务。 + +
+ +
+**工程**:[https://github.com/fuzhengwei/xfg-dev-tech-springcloud-bus](https://github.com/fuzhengwei/xfg-dev-tech-springcloud-bus) - `你可以把工程fork到你的github仓库,之后做后面的操作。` + +- 环境要求;jdk 1.8、maven 3.8.x、kafka - 提供了 docker 安装脚本在 docs 下。之后还有一个 [natapp](https://natapp.cn/) 做内网穿透。 +- 模块职责;config-bus 配置了整套消息总线所需的服务模块,一个是 eureka 的 registry 注册中心,一个是 SpringCloud Bus 消息总线的服务 server。kafka 是通用的模块,便于统一配置。xfg-dev-tech-app 是测试工程模块。 + +## 三、环境安装 + +本节的案例工程会需要用到 Kafka、RabbitMQ,所以需要安装这两套环境。 + +
+ +
+ +
+ +
+ +- Mac 电脑会比较好安装一些,直接在 IntelliJ IDEA 点击小绿色按钮即可完成安装。安装完成后进入 [http://localhost:9000/#!/2/docker/containers](http://localhost:9000/#!/2/docker/containers)- 可看到 Kafka、RabbitMQ 运行。 +- Windows 需要开启 wsl2 在安装 Docker 之后就可以安装 docker 使用了。 +- 如果本机电脑配合低或者比较旧不好安装,推荐使用云服务器进行操作。云服务器就相当于你的一个远程电脑了,非常适合部署这些环境,同时怎么这套都不会影响你的本地环境。[https://618.gaga.plus - 推荐2c4g云服务。 + +## 四、功能实现 + +### 1. config-bus-kafka + +```java +@Configuration +@PropertySource("classpath:system.properties") +public class KafkaConfig { +} +``` + +```java +spring.kafka.bootstrap-servers=127.0.0.1:9092 + +spring.kafka.producer.retries=0 +spring.kafka.producer.batch-size=16384 +spring.kafka.producer.buffer-memory=33554432 +spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer +spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer + +spring.kafka.consumer.group-id=springcloud-config-bus-group +spring.kafka.consumer.auto-offset-reset=earliest +spring.kafka.consumer.enable-auto-commit=false +spring.kafka.consumer.auto-commit-interval=100 +spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer +spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer +``` + +- 做一个统一的 kafka 配置 model,让其他模块引入。 + +### 2. config-bus-registry + +```java +package cn.bugstack.xfg.dev.tech; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +@SpringBootApplication +@EnableEurekaServer +public class ConfigBusRegistryApplication { + + public static void main(String[] args) { + SpringApplication.run( ConfigBusRegistryApplication.class, args ); + } + +} +``` + +```java +server: + port: 7397 + +spring: + application: + name: eureka-server + +eureka: + instance: + # 使用 ip 代替实例名 + prefer-ip-address: true + # 实例的主机名 + hostname: ${spring.cloud.client.ip-address} + # 实例的 ID 规则 + instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} + client: + # 是否向注册中心注册自己 + registerWithEureka: false + # 是否向注册中心获取注册信息 + fetchRegistry: false + serviceUrl: + # 注册中心地址 + defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ +``` + +- 这部分是一个 eureka 的服务端,让注册中心和客户端,都被 eureka 管理。 + +### 3. config-bus-server + +```java +package cn.bugstack.xfg.dev.tech; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.config.server.EnableConfigServer; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; + +@SpringBootApplication +@EnableConfigServer +@EnableEurekaClient +public class ConfigBusServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConfigBusServerApplication.class, args); + } + +} +``` + +```java +# 端口 +server: + port: 8000 + +spring: + application: + name: config-bus-server + cloud: + config: + server: + git: + # 仓库地址 + uri: https://github.com/fuzhengwei/xfg-dev-tech-springcloud-bus + # 对应 {label} 部分,即 Git 的分支 + label: master + # 仓库文件夹名称,多个以逗号分隔 + search-paths: config-bus/config-repo + # git 仓库用户名(公开库可以不用填写) + username: + # git 仓库密码(公开库可以不用填写) + password: + bus: + # 开启消息跟踪 + enabled: true + trace: + enabled: true + kafka: + consumer: + group-id: config-bus-server-group + +eureka: + instance: + prefer-ip-address: true + hostname: ${spring.cloud.client.ip-address} + instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} + client: + serviceUrl: + defaultZone: http://${eureka.instance.hostname}:7397/eureka/ + +management: + endpoints: + web: + exposure: + # 开启刷新端点 + include: bus-refresh +``` + +- git 部分的配置,如注释说明。之后你要修改为自己的 Github 地址,这样你在修改配置时候,才能做 webhook 调用变更。 +- kafka 是默认的消费id,不需要修改。 +- management 需要开启 bus-refresh 刷新断点。 + +### 4. config-repo + +system-dev.properties + +```java +hello=I'm xfg dev config 09 +hi=I'm xfg dev config 08 +``` + +- 这一层是配置文件,后面在你提交代码修改的时候,工程里也会一起修改。 + +### 5. xfg-dev-tech-app + +#### 5.1 动态配置 + +```java +@RestController +@RefreshScope +public class ConfigClientController { + + @Value("${hello}") + private String hi; + + @RequestMapping("/hi") + public String hi() { + return this.hi; + } + +} +``` + +验证时访问地址;[http://127.0.0.1:9000/hi](http://127.0.0.1:9000/hi) + +#### 5.2 刷新配置 + +```java +package cn.bugstack.xfg.dev.tech.trigger; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; + +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +@Slf4j +@RestController +public class GitHubWebhookController { + + @PostMapping("/webhook") + public String handleGitWebhook(@RequestBody String payload) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode content = mapper.readTree(payload); + + log.info("收到 webhook {} 更新配置通知", content.get("pusher")); + + // 创建URL对象 + URL url = new URL("http://127.0.0.1:8000/actuator/bus-refresh"); + + // 打开连接 + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + + // 设置请求方法为POST + connection.setRequestMethod("POST"); + + // 开启输入输出流 + connection.setDoOutput(true); + + // 设置请求头,如果需要,可以设置Content-Type等 + connection.setRequestProperty("Content-Type", "application/json"); + + // 获取输出流 + try (OutputStream os = connection.getOutputStream()) { + // 如果有请求体数据,也可以在这里写入 + // String jsonInputString = "{\"key\": \"value\"}"; + // os.write(jsonInputString.getBytes("utf-8")); + os.flush(); + } + + // 发送请求并获取响应码 + int responseCode = connection.getResponseCode(); + + log.info("调用 actuator/bus-refresh 更新全局配置完成 code:{}", responseCode); + + } catch (Exception e) { + e.printStackTrace(); + } + + return "done"; + } + +} +``` + +- 做一个 webhook 接口,github 回调后,调用 `http://127.0.0.1:8000/actuator/bus-refresh` 刷新配置。 +- 也可以手动访问 `http://127.0.0.1:8000/actuator/bus-refresh` 自己刷新配置验证。 + +## 五、功能验证 + +### 1. 前置配置 + +#### 1.1 内网穿透 + +获取 natapp 免费隧道 authtoken,[https://natapp.cn/tunnel/lists](https://natapp.cn/tunnel/lists) 配置到工程中。 + +
+ +
+ +- 注意:免费隧道配置端口为9000,因为是要把本地这个 9000 端口的服务,映射出去。 + +
+ +
+ +- 启动 natapp 后,会得到一个公网域名地址。这个地址免费的会不断地变化,测试的时候注意。 + +#### 1.2 webhook 配置 + +进入 GitHub 工程中,Settings -> Webhooks 页面。地址:[https://github.com/fuzhengwei/xfg-dev-tech-springcloud-bus/settings/hooks/517530722](https://github.com/fuzhengwei/xfg-dev-tech-springcloud-bus/settings/hooks/517530722) - `你的和我的不同` + +
+ +
+ +- 拿到公网地址后,配置 webhook。如图配置完点击下面完成。更新的时候点击 update webhook。 + +### 2. 启动服务 + +陆续的启动;config-bus-registry、config-bus-server、xfg-dev-tech-app。 + +### 3. 服务测试 + +#### 3.1 第1次,访问配置接口 + +地址:[http://127.0.0.1:9000/hi](http://127.0.0.1:9000/hi) + +```java +I'm xfg dev config 09 +``` + +#### 3.2 更新线上配置 + +
+ +
+ +- 你可以在线更新配置,也可以本地更新配置后提交代码到 github。 +- 变更后点击 commit changes + +
+ +
+ +- 查看到 webhook 推送的记录。是成功的。 + +
+ +
+ +- 查看日志变更记录。`webhook {"name":"fuzhengwei","email":"fuzhengwei@users.noreply.github.com"} 更新配置通知` + +#### 3.2 第2次,访问配置接口 + +地址:[http://127.0.0.1:9000/hi](http://127.0.0.1:9000/hi) + +```java +I'm xfg dev config 10 +``` + +- 配置已经从09变更为10,代表测试成功了。 +- 另外你还可以访问 [http://127.0.0.1:8000/system/dev](http://127.0.0.1:8000/system/dev) 查看整体的配置信息。 + +```java +{ + "name": "system", + "profiles": [ + "dev" + ], + "label": null, + "version": "fccaf3233af6d0ae16571d2c907ff87eaf1c8946", + "state": null, + "propertySources": [ + { + "name": "https://github.com/fuzhengwei/xfg-dev-tech-springcloud-bus/config-bus/config-repo/system-dev.properties", + "source": { + "hello": "I'm xfg dev config 10", + "hi": "I'm xfg dev config 08" + } + } + ] +} +``` + + + + + diff --git a/docs/md/road-map/springcloud-feign.md b/docs/md/road-map/springcloud-feign.md new file mode 100644 index 000000000..f72cdef72 --- /dev/null +++ b/docs/md/road-map/springcloud-feign.md @@ -0,0 +1,229 @@ +--- +title: SpringCloud Feign +lock: need +--- + +# SpringCloud Feign + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在过往几年中在帮助大家学习编程中,我会看到不少新人伙伴在项目的时候会纠结,这个是RPC(Dubbo)的、这个是 SpringCloud(Feign)的,这个是 MVC 的、这个是 DDD 的。但其实不用纠结一点都。 + +
+ +
+ +**其实这些东西都是一通百通** + +一个能把 Dubbo 用的透彻的人,换成 Feign 就是小儿科。一个能把 MVC 搞的明明白白的人,换成 DDD 那就是手到擒来。之所以有人会觉得换一下就不会了,是因为原本另外一个就没用明白。各类的工具、框架、组件,在编程中都有非常多的同类替代品。就算即使是 RPC 也是有非常多的产品,尤其中大厂中还有很多自研的组件。 + +那么今天小傅哥就再分享下 SpringCloud Feign 结合到 DDD 战术设计六边形架构中的使用方式。 + +## 一、组件介绍 + +官网:[https://spring.io/projects/spring-cloud](https://spring.io/projects/spring-cloud) + +Spring Cloud 为开发人员提供了一系列工具,用于快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、微服务和契约测试)。分布式系统的协调产生了样板模式,使用 Spring Cloud,开发人员可以快速建立实现这些模式的服务和应用程序。它们可以在任何分布式环境中很好地工作,包括开发人员自己的笔记本电脑、裸机数据中心和 Cloud Foundry 等托管平台。 + +本节会涉及到 Eureka 注册中心、Feign 简化微服务 HTTP 调用组件; + +
+ +
+ +- Eureka 是一个由 Netflix 开发的服务发现工具,主要用于云端分布式系统中。它允许各个服务在启动时注册自己的信息,并能够动态地发现其他服务的位置和状态,从而实现负载均衡和故障转移。Eureka 在微服务架构中扮演着重要角色,帮助提高系统的可用性和弹性。其易于集成的特性使其成为许多企业在构建复杂分布式应用时的首选工具之一。 +- Feign 是一个受欢迎的 Java HTTP 客户端库,主要用于简化服务间的 HTTP 通信。它通过使用注解来定义 HTTP 请求接口,使得开发者可以更直观地调用远程服务。Feign 提供了可插拔的编码器和解码器,支持多种数据格式,并且可以与 Spring Cloud 集成,方便地实现负载均衡和服务发现。其简洁的 API 和高度的可扩展性,使得 Feign 成为微服务架构中常用的工具之一。 + +## 二、测试工程 + +小傅哥这里给搭建了一套测试 Feign 案例的六边形系统架构; + +
+ +
+ +**工程**:[https://github.com/fuzhengwei/xfg-dev-tech-springcloud-feign](https://github.com/fuzhengwei/xfg-dev-tech-springcloud-feign) + +- eureka 模块,包括;server、client 端,server 端是注册中心,用于接收注册上来的服务接口,统一管理负载。client 端是为了模拟提供一个服务接口。你可以任何其他工程来提供 feign 客户端接口,也就是接口的提供方。这样消费方就可以通过注册中心调用了。 +- infrastructure 是基础设施层,在六边形架构中,用于处理调用外部的接口,内部的数据库,缓存等这样的基础功能。在 DDD 的软件设计方法中,会把这部分基础的东西从功能实现中拆分出来。 +- domain 和 infrastructure 是依赖倒置关系,所有 domain 要实现的服务需要的基础数据,都可以通过依赖倒置方式处理。也就是 domain 领域层定义接口,之后由基础设置层做功能实现。在通过 Spring 注入到 domain 领域中 service 具体的类中,这样就可以使用。 +- trigger 名词为触发器,用于承载给外部提供的服务能力,包括;http接口、rpc接口、job任务等,这些要调用我们服务能力的方式,都可以通过 trigger 层来实现。 + +>更多的关于 DDD 六边形架构,可以从编程路书中学习;[https://bugstack.cn/md/road-map/ddd-guide-03.html](https://bugstack.cn/md/road-map/ddd-guide-03.html) + +## 三、功能实现 + +### 1. 引入 spring cloud + +```java + + org.springframework.boot + spring-boot-starter-parent + 2.0.6.RELEASE + + + + + + + org.springframework.cloud + spring-cloud-dependencies + Finchley.SR2 + pom + import + + + com.alibaba + fastjson + 2.0.28 + + + +``` + +- 在官网中有对应版本关系可以参考:[https://spring.io/projects/spring-cloud](https://spring.io/projects/spring-cloud) + +### 2. eureka-server + +在公司中 eureka-server 是统一一套的,我们个人学习需要搭建一个这样的工程。 + +#### 2.1 yml 配置 + +```java +server: + port: 7397 + +eureka: + instance: + hostname: localhost + client: + registerWithEureka: false + fetchRegistry: false + serviceUrl: + defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ + +spring: + application: + name: eureka-server +``` + +- 添加 eureka 配置信息。这个端口 7397 后面其他的客户端调用就连接这个端口。 + +#### 2.2 启动类 + +```java +package cn.bugstack.xfg.dev.tech; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; + +@SpringBootApplication +@EnableEurekaServer +public class EurekaServerApplication { + + public static void main(String[] args) { + SpringApplication.run( EurekaServerApplication.class, args ); + } + +} +``` + +### 3. Eureka-client + +#### 3.1 yml 配置 + +```java +server: + port: 8002 + +spring: + application: + name: eureka-client-api + +eureka: + client: + serviceUrl: + defaultZone: http://localhost:7397/eureka/ +``` + +- 模拟启动一个客户端接口,并填写注册中心地址。 + +#### 3.2 api 接口 + +```java +@EnableEurekaClient +@RestController +public class TestApiController { + + @Value("${server.port}") + private int port; + + @RequestMapping(path = "/api/queryUserInfo", method = RequestMethod.GET) + public String queryUserInfo(@RequestParam String userId) { + return "Hi 小傅哥,微信公众号:bugstack虫洞栈 | " + userId + " >: from eureka client port: " + port; + } + +} +``` + +- 添加一个客户端的接口,这里需要注意的是添加了一个 `@EnableEurekaClient` 注解。这样这个接口就可以被 eureka 注册中心管理。 +- 如果你有学习过小傅哥的[API网关](https://bugstack.cn/md/assembly/api-gateway/api-gateway.html)项目,那么就可以了解这样的设计,是如何完成接口注册的。 + +### 4. 接口使用 - infrastructure + +在工程的基础设置层配置对外部的接口调用; + +
+ +
+ +- 如果没有 fegin 我们最开始调用外部的 http 接口时候,就需要写很多的关于 http 的调用,这个过程是比较复杂的。在使用 feign 后,这个事就变得简单了,同时还增加了负载和故障迁移的能力。 + +- 当然现在调用http的方式不只是以前的刀耕火种了,可以用 okttp、retrofit2 这样的框架处理 http 调用过程。如下这样的调用方式也是非常好维护的。 + +```java +@GET("cgi-bin/token") +Call getToken(@Query("grant_type") String grantType, +@Query("appid") String appId, +@Query("secret") String appSecret); +``` + +## 四、测试验证 + +### 1. eureka 启动 + +
+ +
+ +
+ +
+ +- 分别启动 eureka 的服务端和测试的客户端,提供接口能力。 +- 启动后访问 eureka 服务端:[http://127.0.0.1:7397/](http://127.0.0.1:7397/) + +### 2. 测试工程启动 + +
+ +
+ +```java +fuzhengwei@MacBook-Pro xfg-dev-tech-springcloud-feign % curl http://127.0.0.1:8091/api/v1/query_user_info +Hi 小傅哥,微信公众号:bugstack虫洞栈 | xfg >: from eureka client port: 8002% +``` + +```java +]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@680aded0 +24-11-24.10:32:49.968 [PollingServerListUpdater-0] INFO ChainedDynamicProperty - Flipping property: eureka-client-api.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647 +``` + +- 启动应用测试工程,启动后可以访问验证。 +- 验证接口:`http://127.0.0.1:8091/api/v1/query_user_info` \ No newline at end of file diff --git a/docs/md/road-map/springcloud-gateway.md b/docs/md/road-map/springcloud-gateway.md new file mode 100644 index 000000000..ed787c576 --- /dev/null +++ b/docs/md/road-map/springcloud-gateway.md @@ -0,0 +1,329 @@ +--- +title: SpringCloud-Gateway +lock: need +--- + +# 中小厂,其实选这套网关就够用了。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +我发现了一个很有意思的缩写单词 `gw`、`wg`,都是网关的意思。因为 `gw = gateway`、`wg = wangguan`,所以在各个系统开发中,既有 gw 也有 wg 的存在。而网关也是各个互联网中用于统一对外的核心系统,当然使用网关的手段也不同,有中大厂自研,也有中小厂使用开源的组件。所以小傅哥的这个系列会陆续的分享出各个类型的网关,让大家了解以及按需选择使用。 + +
+ +
+ +其实只要一个公司有拆分较多的微服务,有很多的应用都要对外提供接口,就需要引入网关系统。否则就会有非常多共性功能重复在各个系统开发。比如;负载、熔断、降级、限流、切量、统一登录、地址转发等功能。这些东西要是让每个系统实现一遍,后续是非常难管理的。 + +那么这么多开源网关选择哪个,或者如何自研网关呢,小傅哥会陆续的分享出各个网关的介绍和使用,以及自研的教程,让大家可以积累自己的知识体系以及做技术选型。 + +前面已经分享了一篇 [Higress](https://bugstack.cn/md/road-map/higress.html) 今天分享的是 SpringCloud Gateway + +- 官网:[https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#glossary](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#glossary) +- 案例:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-springcloud-gateway](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-springcloud-gateway) + +>Spring Cloud Gateway 是一套非常容易使用的网关服务,通过yml配置或者代码编程的方式实现网关功能。对于网关你可以理解在接收一个 http 请求后,通过网关的配置过滤、替换、拦截、转发等操作到指定的请求地址上去。 + +## 一、SpringCloud Gateway 介绍 + +Spring Cloud Gateway 是一个基于 Spring Framework 和 Spring Boot 提供的网关解决方案。可以帮助开发者轻松地构建出具有动态路由、限流、熔断等特性的 API 网关。 + +
+ +
+ +1. **基于异步非阻塞模型**:Spring Cloud Gateway 基于 Project Reactor 和 WebFlux,采用了异步非阻塞的 API,可以提供更高的吞吐量和更低的延迟。 +2. **动态路由**:可以通过配置文件或者 API 动态地添加、修改或删除路由规则,不需要重启服务。 +3. **路由断言**:可以根据 HTTP 请求的各种参数(如路径、头部、请求参数等)来匹配路由。 +4. **过滤器**:提供了一系列内置的 GatewayFilter 工厂,可以在请求被路由前或后执行各种操作,如修改请求头、增加参数、限流等。同时也支持自定义过滤器。 +5. **集成与安全**:可以与 Spring Cloud 的服务发现和断路器等组件无缝集成,同时也可以集成 Spring Security 实现安全控制。 +6. **限流与熔断**:可以集成如 Resilience4J 等库来实现限流和熔断功能,保护后端服务不被过多的请求压垮。 +7. **监控与跟踪**:可以与 Spring Cloud Sleuth 和 Zipkin 等组件集成,实现请求的跟踪和监控。 + +Spring Cloud Gateway 的工作原理是将进入的 HTTP 请求根据配置的路由规则转发到对应的后端服务。它在转发请求的过程中可以执行一系列的过滤器链,这些过滤器可以修改请求和响应,或者根据特定的逻辑决定是否继续处理请求。 + +## 二、环境部署 + +### 1. 测试工程 + +
+ +
+ +- 注意;本机安装了 docker 或者云服务器安装了 docker + compose。在小傅哥的 [bugstack.cn](https://bugstack.cn/md/road-map/docker.html) 路书系列教程中,有云服务器操作。 +- 在小傅哥提供的案例工程中,包括;环境配置(nacos - 注册中心、redis - 限流使用)、curl 测试访问网关地址、app 是网关配置、provider-01\02 是2个测试工程,提供了2个接口,方便验转发。 +- 最后的 webflux 是使用这项技术来模拟开发网关,让大家了解到 SpringCloud Gateway 简单运行机制。 + +### 2. 基础环境 + +- 开发环境:JDK 17 +- 云服务器:2c4g 最低,我是用的 2c8g 体验的。[https://yun.xfg.plus](https://yun.xfg.plus/)- 价格实惠。 +- 基础环境:Docker、Portainer、Git 【在小傅哥的 bugstack.cn 路书中都有讲解安装和使用】 + +
+ +
+ +- docker 安装 mysql 会自动根据 docker compose 脚本配置,把 nacos 需要的 sql 导入进去。 +- Phpmyadmin - MySQL 后台管理工具、redis-admin - Redis 后台管理工具。 + +### 3. 生产接口 + +xfg-dev-tech-gateway-provider-01、xfg-dev-tech-gateway-provider-02,分别提供了2个生产的 http 接口。你可以启动服务后单独访问接口测试。我们这里主要的用途是通过网关来使用这2个接口。 + +📢 注意以下测试,都要先启动这2个接口提供者工程。 + +#### 3.1 生产者01 + +```java +@RestController() +@CrossOrigin("*") +@RequestMapping("/api/user/") +public class HiGatewayController { + + /** + * curl http://127.0.0.1:8091/api/user/hi + */ + @RequestMapping(value = "hi", method = RequestMethod.GET) + public String hi() { + return "hello gateway,provider 01"; + } + +} +``` + +#### 3.1 生产者01 + +```java +@RestController() +@CrossOrigin("*") +@RequestMapping("/api/user/") +public class HiGatewayController { + + /** + * curl http://127.0.0.1:8092/api/user/hi + */ + @RequestMapping(value = "hi", method = RequestMethod.GET) + public String hi() { + return "hello gateway,provider 02!"; + } + +} +``` + +## 三、模拟网关 - webflux + +基于 webflux 开发 api网关,不要引入 spring web 组件,而是引入以下组件; + +```pom + + org.springframework.boot + spring-boot-starter-webflux + + + io.netty + netty-all + 4.1.97.Final + +``` + +### 1. 简单路由 + +```java +@Configuration +public class GatewayRouter { + + @Bean + public RouterFunction routeToService() { + return RouterFunctions + .route(GET("/service1").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), + request -> ServerResponse.ok().bodyValue("Response from Service 1")) + .andRoute(GET("/service2").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), + request -> ServerResponse.ok().bodyValue("Response from Service 2")); + } + +} +``` + +- 这是一个请求地址和返回接口的简单路由。 +- 你可以启动服务后,访问地址:`http://localhost:9091/service1` `http://localhost:9091/service2` + +### 2. 接口路由 + +```java +@Configuration +public class ApiGatewayConfiguration { + + private final WebClient.Builder webClientBuilder; + + public ApiGatewayConfiguration(WebClient.Builder webClientBuilder) { + this.webClientBuilder = webClientBuilder; + } + + /** + * curl http://localhost:9091/wg/service1/8091 + * curl http://localhost:9091/wg/service2/8092 + * @return + */ + @Bean + public RouterFunction routerFunction() { + return route(GET("/wg/service1/{id}"), this::service1Handler) + .andRoute(GET("/wg/service2/{id}"), this::service2Handler); + } + + public Mono service1Handler(ServerRequest request) { + String id = request.pathVariable("id"); + Mono response = webClientBuilder.build() + .get() + .uri("http://127.0.0.1:8091/api/user/hi?" + id) + .retrieve() + .bodyToMono(String.class); + return ServerResponse.ok().body(response, String.class); + } + + public Mono service2Handler(ServerRequest request) { + String id = request.pathVariable("id"); + Mono response = webClientBuilder.build() + .get() + .uri("http://127.0.0.1:8092/api/user/hi?" + id) + .retrieve() + .bodyToMono(String.class); + return ServerResponse.ok().body(response, String.class); + } + +} +``` + +
+ +
+ +- 通过 routerFunction 对不同的请求地址进行转发操作。 +- 如图 `curl http://localhost:9091/wg/service1/8091`、`curl http://localhost:9091/wg/service2/8092` 可以得到不同的响应结果。 + +## 四、网关测试 - SpringCloud Gateway + +
+ +
+ +- 文档:[https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html) +- 使用:你可以右键翻译文档,根据文档的说明来配置各个场景验证网关使用。 + +### 1. yml配置 + +```java +spring: + redis: + host: 127.0.0.1 + port: 16379 + database: 0 + lettuce: + pool: + max-active: 10 + max-wait: 1000 + max-idle: 5 + min-idle: 3 + application: + name: xfg-dev-tech-springcloud-gateway + cloud: + nacos: + discovery: + server-addr: 127.0.0.1:8848 + username: nacos + password: nacos + locator: + enabled: true + gateway: + discovery: + locator: + enabled: true + globalcors: + cors-configurations: + '[/**]': + allowedOrigins: "*" + allowedMethods: "*" + alloedHeaders: "*" + routes: + - id: route_01 + uri: lb://provider-01 + order: 1 + predicates: + - Path=/gw/** + - Weight=group1, 1 + filters: + - StripPrefix=1 + - id: route_02 + uri: lb://provider-02 + order: 1 + predicates: + - Path=/gw/** + - Weight=group1, 9 + filters: + - StripPrefix=1 + - name: RequestRateLimiter + args: + key-resolver: "#{@ipKeyResolver}" # 限流方式:Bean名称 + redis-rate-limiter.replenishRate: 1 # 生成令牌速率:个/秒 + redis-rate-limiter.burstCapacity: 3 # 令牌桶容量 + redis-rate-limiter.requestedTokens: 1 # 每次消费的Token数量 +``` + +- 配置 Redis 是为了使用限流组件,同时要配置 RequestRateLimiter 类,配置对应的限流 bean 名称。 +- nacos 是注册中心,网关走的是 nacos 注册中心里的服务。这些服务是生产者接口配置了 nacos 注册到注册中心了。这样就可以通过 lb://provider-02 进行访问。lb = nacos,provider-02 是生产者配置的服务名称。 +- 在 yml 配置中,一组 id: route_01 下面是对应的网关配置,以这个距离,访问 `Path=/gw/**` 路径,filters 过滤掉 `StripPrefix=1` 1个路径 gw 其余的打到 provider-01 服务上,也就是可以访问具体的服务了。另外 `Weight=group1, 1` 是权重配置,group1 代表这一组的,1 表示权重比。*如果你不用 nacos,uri 也可以配置一个具体的 http 地址测试* +- 这些内容在 SpringCloud Gateway 官网有对应的介绍,直接按照文档配置使用即可。 + +### 2. 代码配置 + +**源码**:`cn.bugstack.xfg.dev.tech.config.RouteConfiguration` + +```java +@Bean +public RouteLocator route(RouteLocatorBuilder builder, UriConfiguration uriConfiguration) { + String httpUri = uriConfiguration.getHttp(); + return builder.routes() + .route(p -> p.path("/baidu").uri("https://www.baidu.com/")) + .route(p -> p.path("/bugstack").uri("https://bugstack.cn/md/road-map/road-map.html")) + .route(p -> p.path("/error").uri("forward:/fallback")) + .route(p -> p.path("/get").filters(f -> f.addRequestHeader("Hello", "World")).uri(httpUri)) + .build(); +} +``` + +- 除了 yml 中的配置,还可以使用代码配置。有代码配置是非常重要的,这样就可以根据写到数据库中的数据,以及提供一个管理后台来操作网关的配置了,在网关启动的时候从数据库读取数据来动态实例化所有的配置内容。 + +### 3. 测试验证 + +启动服务后,访问地址:`curl http://localhost:8090/gw/api/user/hi`、`curl http://localhost:8090/error` + +
+ +
+ +
+ +
+ +- 访问后,分别可以看到不通的响应结果。 + +## 五、网关学习 + +除了业务开发,小傅哥自己也是非常感兴趣于这样的网关技术组件的实现,所以在日常的工作中也积累了很多网关的设计。后来在22年做了一套轻量的网关系统,把核心的内核逻辑实现出来让大家学习。帮助了很多伙伴学习项目后找到了不错的工作。 + +![img](https://bugstack.cn/images/article/assembly/api-gateway/api-gateway-220809-02.png) + +整个**API网关**设计核心内容分为这么五块; + +- `第一块`:是关于通信的协议处理,也是网关最本质的处理内容。这里需要借助 NIO 框架 Netty 处理 HTTP 请求,并进行协议转换泛化调用到 RPC 服务返回数据信息。 +- `第二块`:是关于注册中心,这里需要把网关通信系统当做一个算力,每部署一个网关服务,都需要向注册中心注册一个算力。而注册中心还需要接收 RPC 接口的注册,这部分可以是基于 SDK 自动扫描注册也可以是人工介入管理。当 RPC 注册完成后,会被注册中心经过AHP权重计算分配到一组网关算力上进行使用。 +- `第三块`:是关于路由服务,每一个注册上来的Netty通信服务,都会与他对应提供的分组网关相关联,例如:wg/(a/b/c)/user/... a/b/c 需要匹配到 Nginx 路由配置上,以确保不同的接口调用请求到对应的 Netty 服务上。PS:如果对应错误或者为启动,可能会发生类似B站事故。 +- `第四块`:责任链下插件模块的调用,鉴权、授信、熔断、降级、限流、切量等,这些服务虽然不算是网关的定义下的内容,但作为共性通用的服务,它们通常也是被放到网关层统一设计实现和使用的。【这块内容可以自行扩展】 +- `第五块`:管理后台,作为一个网关项目少不了一个与之对应的管理后台,用户接口的注册维护、mock测试、日志查询、流量整形、网关管理等服务。 + +> 项目学习地址:https://bugstack.cn/md/assembly/api-gateway/api-gateway.html diff --git a/docs/md/road-map/springcloud-stream.md b/docs/md/road-map/springcloud-stream.md new file mode 100644 index 000000000..52e735683 --- /dev/null +++ b/docs/md/road-map/springcloud-stream.md @@ -0,0 +1,436 @@ +--- +title: SpringCloud Stream +lock: need +--- + +# SpringCloud Stream + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在我们日常开发中,有很多的同类共性功能组件,如;MQ 的有 Kafka、RabbitMQ,RPC 的有 GRpc、Dubbo。那如果我们想让服务可以平滑的从一套组件切换到另外一套,应该如何处理呢?🤔 + +
+ +
+ +**这样的东西我也做过** + +在我工作的公司,近10年的发展中,Redis 的缓存服务组件陆续的变换了3、4款,目前有2套最终稳定共用的。那么我为此开发了一款缓存中间件,可以做到动态切换、读写控制、监控管理,可以非常方便的迁移和升级。 + +那么,在我们使用 MQ 的时候,如果在不改变系统工程代码的情况下,该怎样优雅的从一套MQ迁移到另外一套呢?今天小傅哥就带着大家来办这样一个事。 + +## 一、组件介绍 + +官网:[https://spring.io/projects/spring-cloud-stream](https://spring.io/projects/spring-cloud-stream) + +Spring Cloud Stream 是一个用于构建与共享消息系统连接的高度可扩展的事件驱动微服务的框架。 + +该框架提供了一个灵活的编程模型,该模型建立在已建立且熟悉的 Spring 习语和最佳实践之上,包括对持久发布/订阅语义、消费者组和有状态分区的支持。 + +
+ +
+ +Spring Cloud Stream 支持对接的 MQ 包括:RabbitMQ、Kafka、RocketMQ、Azure Service Bus 等。 + +## 二、测试工程 + +小傅哥这里搭建了一套测试 MQ 案例的六边形架构; + +
+ +
+ +**工程**:[https://github.com/fuzhengwei/xfg-dev-tech-springcloud-stream](https://github.com/fuzhengwei/xfg-dev-tech-springcloud-stream) + +- docs 提供了使用 docker 安装 kafka、rabbitmq 的环境脚本。docker 安装和使用教程:[https://bugstack.cn/md/road-map/docker.html](https://bugstack.cn/md/road-map/docker.html) +- trigger 是六边形架构的触发器层,用于接收 MQ 消息。接收后就可以通过调用 domain 领域服务,完成功能的串联。另外 trigger 作为触发器,不只是可以接收 MQ 消息,还可以提供HTTP接口、RPC接口,Job任务调度。 +- domain 领域层是具体的业务逻辑实现层,当业务逻辑中有需要发MQ消息的时候,则可以通过 infrastructure 基础设施层通过依赖倒置实现 domain 领域层 adapter 适配器中的接口,完成消息的发送。 + +## 三、环境安装 + +本节的案例工程会需要用到 Kafka、RabbitMQ,所以需要安装这两套环境。 + +
+ +
+ +
+ +
+ +- Mac 电脑会比较好安装一些,直接在 IntelliJ IDEA 点击小绿色按钮即可完成安装。安装完成后进入 [http://localhost:9000/#!/2/docker/containers](http://localhost:9000/#!/2/docker/containers)- 可看到 Kafka、RabbitMQ 运行。 +- Windows 需要开启 wsl2 在安装 Docker 之后就可以安装 docker 使用了。 +- 如果本机电脑配合低或者比较旧不好安装,推荐使用云服务器进行操作。云服务器就相当于你的一个远程电脑了,非常适合部署这些环境,同时怎么这套都不会影响你的本地环境。[https://618.gaga.plus](https://618.gaga.plus) - 推荐2c4g云服务。 + +## 四、功能验证 + +在做项目的案例前,我们可以先做下 SpringCloud Stream 对接 Kafka、RabbitMQ 的案例,有了这个基础在做整个工程的案例就更容易了。 + +### 1. pom 配置 + +```java + + org.springframework.cloud + spring-cloud-dependencies + Finchley.SR2 + pom + import + + + + org.springframework.cloud + spring-cloud-stream-binder-kafka + + + + org.springframework.cloud + spring-cloud-stream-binder-rabbit + +``` + +- 首先在 [spring-cloud-stream](https://spring.io/projects/spring-cloud-stream) 组件官网中是有一套对应的 SpringBoot、SpringCloud 版本匹配的关系的。如果你需要升级版本可以参考官网中的关系。 +- 这里的 kafka、rabbitmq,使用的时候要分别使用,把另外一套不使用的注释掉就可以。 +- 这里我们注释掉 rabbit 的引入,只测试 kafka 部分。 + +### 2. yml 配置 + +```java +spring: +# rabbitmq: +# addresses: 192.168.1.108 +# port: 5672 +# username: admin +# password: admin +# listener: +# simple: +# prefetch: 10 # 每次投递n个消息,消费完在投递n个 + kafka: + bootstrap-servers: 192.168.1.105:9092 + producer: + # 发生错误后,消息重发的次数。 + retries: 1 + #当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。 + batch-size: 16384 + # 设置生产者内存缓冲区的大小。 + buffer-memory: 33554432 + acks: 1 + consumer: + # 自动提交的时间间隔 在spring boot 2.X 版本中这里采用的是值的类型为Duration 需要符合特定的格式,如1S,1M,2H,5D + auto-commit-interval: 1S + # 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理: + # latest(默认值)在偏移量无效的情况下,消费者将从最新的记录开始读取数据(在消费者启动之后生成的记录) + # earliest :在偏移量无效的情况下,消费者将从起始位置读取分区的记录 + auto-offset-reset: earliest + # 是否自动提交偏移量,默认值是true,为了避免出现重复数据和数据丢失,可以把它设置为false,然后手动提交偏移量 + enable-auto-commit: false + cloud: + stream: + bindings: + output: + destination: ${mq.topic.user} + input: + destination: ${mq.topic.user} + myoutput: + destination: ${mq.topic.user02} + myinput: + destination: ${mq.topic.user02} + +mq: + topic: + user: xfg-topic + user02: xfg-topic-02 +``` + +- SpringCloud Stream,支持你以统一一套的方式配置不同的 MQ 渠道。需要使用 kafka、rabbitmq,就可以分别配置。 +- 之后你可以看到,cloud.stream.bindings 可以指定 input、output,这个既可以使用本身 SpringCloud Stream 提供的,也可以自定义。通过这样的一个方式,让所有的 MQ 都以这样的方式进行输入、输出对接。 +- 这里我们注释掉 rabbitmq 只测试 kafka + +### 3. 案例代码 + +#### 3.1 自定义输入输出key + +```java +public interface MyProcessor { + + String INPUT = "myinput"; + String OUTPUT = "myoutput"; + + @Input(INPUT) + SubscribableChannel input(); + + @Output(OUTPUT) + MessageChannel output(); +} +``` + +#### 3.2 单测类 + +##### 3.2.1 默认方式 + +```java +Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class StreamTest01 { + + @Autowired + private MessageProducer producer; + + @Test + public void test_publish() throws InterruptedException { + for (int i = 0; i < 2; i++) { + producer.send("mq 消息,哈喽哇!"); + } + + new CountDownLatch(1).await(); + } + + @Component + @EnableBinding(Source.class) + static class MessageProducer { + + @Autowired + private Source source; + + public void send(String message) { + source.output().send(MessageBuilder.withPayload(message).build()); + } + + } + + @Component + @EnableBinding({Sink.class}) + static class MessageConsumer { + + @StreamListener(Sink.INPUT) + public void onMessage(String message) { + System.out.println("@测试 -> " + message); + } + + } + +} +``` + +```java +@测试 -> mq 消息,哈喽哇! +@测试 -> mq 消息,哈喽哇! +@测试 -> mq 消息,哈喽哇! +@测试 -> mq 消息,哈喽哇! +``` + +- 使用组件中提供的 Source、Sink 带有的 input、output 方式处理消息。 + +##### 3.2.2 自定义方式 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class StreamTest02 { + + @Autowired + private MessageProducer producer; + + @Test + public void test_publish() throws InterruptedException { + for (int i = 0; i < 2; i++) { + producer.send("mq 消息,哈喽哇!"); + } + + new CountDownLatch(1).await(); + } + + @Component + @EnableBinding(MyProcessor.class) + static class MessageProducer { + + @Autowired + private MyProcessor source; + + public void send(String message) { + source.output().send(MessageBuilder.withPayload(message).build()); + } + + } + + @Component + @EnableBinding({MyProcessor.class}) + static class MessageConsumer { + @StreamListener(MyProcessor.INPUT) + public void onMessage(String message) { + System.out.println("@测试 -> " + message); + } + + } + +} +``` + +```java +@测试 -> mq 消息,哈喽哇! +@测试 -> mq 消息,哈喽哇! +@测试 -> mq 消息,哈喽哇! +@测试 -> mq 消息,哈喽哇! +``` + +- 自定义了 MyProcessor 可以自定设定 input、output 的名称。我这里设定的是 myinput、myoutput + +## 五、工程案例 - 六边形架构 + +### 1. 消息事件定义 + +```java +public class UserMessageEvent extends BaseEvent { + + @Value("${mq.topic.user}") + private String topic; + + @Override + public EventMessage buildEventMessage(UserMessage data) { + return EventMessage.builder() + .id(RandomStringUtils.randomNumeric(11)) + .timestamp(new Date()) + .data(data) + .build(); + } + + @Override + public String topic() { + return topic; + } + + /** + * 要推送的事件消息,聚合到当前类下。 + */ + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class UserMessage { + private String userId; + private String userName; + private String userType; + } + +} +``` + +- 在 domain 领域层,adapter 适配器中定义消息事件。这也就是一个规范,当有基础层需要发送消息的时候,则需要通过定义的消息结构来处理。也就是我们在领域层定义标准,之后由基础设施层完成处理。 + +### 2. 基础层添加发送工具 + +```java +@Slf4j +@Component +@EnableBinding(Source.class) +public class EventPublisher { + + @Autowired + @Qualifier(Source.OUTPUT) + private MessageChannel messageChannel; + + @Autowired + private Source source; + + public void publish(String topic, BaseEvent.EventMessage eventMessage) { + try { + String messageJson = JSON.toJSONString(eventMessage); + source.output().send(MessageBuilder.withPayload(messageJson).build()); + log.info("发送MQ消息 topic:{} message:{}", topic, messageJson); + } catch (Exception e) { + log.error("发送MQ消息失败 topic:{} message:{}", topic, JSON.toJSONString(eventMessage), e); + throw e; + } + } + +} +``` + +- 工程中所有的消息发送方式都是统一一套的,所以我们在基础层定义出发送消息的方法。各个模块需要使用的时候,直接调用就可以了。 + +### 3. 发送事件消息 + +```java +@Service +public class UserRepository extends UserMessageEvent implements IUserRepository { + + @Resource + private EventPublisher publisher; + + @Override + public void doSaveUser(UserEntity userEntity) { + // 推送消息 + publisher.publish(this.topic(), this.buildEventMessage(UserMessageEvent.UserMessage.builder() + .userId(userEntity.getUserId()) + .userName(userEntity.getUserName()) + .userType(userEntity.getUserTypeVO().getDesc()) + .build())); + } + +} +``` + +- 在基础设施层实现了领域层定义的方法后,就可以在完成业务领域服务功能后,开始推送消息了。 + +### 4. 监听消费 + +```java +@Slf4j +@Component +@EnableBinding({Sink.class}) +public class MessageListener { + + @StreamListener(Sink.INPUT) + public void onMessage(String message) { + log.info("接收消息:{}", message); + } + +} +``` + +- 监听消息,配置一个 INPUT,这样就可以接收到消息了。 + +### 5. 测试验证 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class UserServiceTest { + + @Resource + private IUserService userService; + + @Test + public void test_register() throws InterruptedException { + while (true) { + UserEntity userEntity = new UserEntity(); + userEntity.setUserId("10001"); + userEntity.setUserName("小傅哥"); + userEntity.setUserTypeVO(UserTypeVO.T8); + + userService.register(userEntity); + Thread.sleep(1500); + } + + } + +} +``` + +```java +24-12-01.13:33:53.003 [main ] INFO AppInfoParser - Kafka version : 1.0.2 +24-12-01.13:33:53.003 [main ] INFO AppInfoParser - Kafka commitId : 2a121f7b1d402825 +24-12-01.13:33:53.043 [main ] INFO EventPublisher - 发送MQ消息 topic:xfg-topic message:{"data":{"userId":"10001","userName":"小傅哥","userType":"架构师"},"id":"89743057693","timestamp":1733031232650} +24-12-01.13:33:54.549 [main ] INFO EventPublisher - 发送MQ消息 topic:xfg-topic message:{"data":{"userId":"10001","userName":"小傅哥","userType":"架构师"},"id":"80224746522","timestamp":1733031234546} +``` + +- 模拟测试持续发送消息。发送后就可以在监听消息的 MessageListener 收到具体的消息数据。 + diff --git a/docs/md/road-map/ssl-httpsok.md b/docs/md/road-map/ssl-httpsok.md new file mode 100644 index 000000000..acf051569 --- /dev/null +++ b/docs/md/road-map/ssl-httpsok.md @@ -0,0 +1,294 @@ +--- +title: 域名证书 ssl - httpsok +lock: need +--- + +# 又一款!免费的SSL,还能自动续期,支持CDN/OSS! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +从今年开始,越来越多的云服务厂商开始限制了 ssl 1年期的申请,只提供3个月有效期并且限制数量申请。而购买一个 ssl 证书,一年都至少几千块。那没有 ssl 不行吗?🤔 如果你不害怕自己的小网站被嵌入流量污染变成`小yellow广告网站`,还是建议你加下 ssl! + +
+ +
+ +当你手里有超过1个以上的域名,如 `a.com`、`b.com`,之后你还对域名使用了多级域名部署不同的服务,如;`api.a.com`、`openai.b.com`、`blog.b.com` 那么这个时候一种是对每个域名都申请 ssl 证书,另外一种就是购买更贵的泛域名,支持 `*.a.com`。 + +除了对域名证书的申请还包括了这些域名证书的管理工作,你得知道他们什么时候到期,什么时候要更新。我见过有些互联网公司也有忘记更新域名的情况,从 https 跳转到了访问微信提示的页面。所以我需要一款 `自动续期`、`支持泛域名`、`可视化所有证书时效性`、`可配置CDN`的一款工具!好在,我找到了! + +## 一、产品介绍 + +[https://httpsok.com/?p=4kMR](https://httpsok.com/?p=4kMR) - 让证书续期更简单,一行命令,轻松搞定SSL证书自动续期!这一款工具的老板,竟然认识小傅哥!😂 + +
+ +
+ +已亲自体验,确实挺好用!像傻瓜相机一样,贼简单。 + +## 二、ssl证书 + +### 步骤1;点击申请 + +
+ +
+ +### 步骤2;填写域名 + +
+ +
+ +### 步骤3;域名验证 + +
+ +
+ +### 步骤4;验证成功 + +
+ +
+ +- 如果验证不成功,可以修改下自己的域名 DNS:`ns1.alidns.com` + +### 步骤5;等待下发 + +
+ +
+ +### 步骤6;下载证书 + +
+ +
+ +- 这里我们下载 Nginx 的,因为我们的服务是部署 Nginx 的。 + +## 三、域名配置 + +
+ +
+ +- 配置域名解析,把你要使用的域名和云服务器配置上A记录。这样请求到域名的时候就会解析到服务器的IP了,以及对应的 Nginx 转发。 + +## 四、Nginx配置 + +### 1. 脚本工程 + +
+ +
+ +- 工程:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-ssl](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-ssl) +- 说明:工程中提供了Nginx配置的初始信息,你只需要参考修改为自己的域名后,执行 `docker-compose.yml` 即可安装 Nginx + +#### 1.1 修改域名 + +```java +server { + listen 80; + listen [::]:80; + server_name ssl01.xiaofuge.tech; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name ssl01.xiaofuge.tech; + + ssl_certificate /etc/nginx/ssl/_.xiaofuge.tech.pem; + ssl_certificate_key /etc/nginx/ssl/_.xiaofuge.tech.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +- 首先,修改 `ssl01.xiaofuge.tech` 为你的域名。 +- 之后,修改 ssl 地址信息。这个地址是 docker-compose 映射的地址。如果你是 linux 直接安装,则可以直接使用 acme.sh 安装的时候生成的地址。 +- 注意,更多 Nginx 配置 [https://bugstack.cn/md/road-map/nginx.html](https://bugstack.cn/md/road-map/nginx.html) + +#### 1.2 修改证书 + +#### 方式1 + +```java +version: '3' +# docker-compose -f docker-compose.yml up -d +services: + nginx: + image: nginx:alpine + container_name: nginx + ports: + - '443:443' + - '80:80' + volumes: + - ./nginx/logs:/var/log/nginx + - ./nginx/html:/usr/share/nginx/html + - ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf + - ./nginx/conf/conf.d:/etc/nginx/conf.d + - ./nginx/ssl:/etc/nginx/ssl/ + privileged: true + restart: always +``` + +- 脚本映射了 ssl、conf.d 等文件内容。 + +#### 方式2 + +```java +# 命令执行 docker-compose up -d +# docker-compose -f docker-compose-nginx.yml up -d +# 自动部署 https https://httpsok.com/doc/faq/docker-nginx.html +version: '3.9' +services: + # yum install -y httpd-tools + nginx: + image: registry.cn-hangzhou.aliyuncs.com/xfg-studio/nginx:1.28.0-alpine # 原镜像 httpsok/nginx:1.28.0-alpine + container_name: nginx + restart: always + ports: + - '443:443' + - '80:80' + environment: + HTTPSOK_TOKEN=https://httpsok.com/console/dashboard 写你的 token curl -s https://get.httpsok.com/ | bash -s 【这里的值】 + volumes: + - ./nginx/logs:/var/log/nginx + - ./nginx/html:/usr/share/nginx/html + - ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf + - ./nginx/conf/conf.d:/etc/nginx/conf.d + - ./nginx/ssl:/etc/nginx/ssl/ + privileged: true +``` + +- 官网说明:[https://httpsok.com/doc/course/deploy-to-nginx-indocker.html](https://httpsok.com/doc/course/deploy-to-nginx-indocker.html) + +### 2. 上传文件 + +
+ +
+ +- 配置信息全部修改后,上传到你的云服务器上。 + +### 3. 执行脚本 + +```java +[root@lavm-aatjb98slj httpsok]# docker-compose up -d +[+] Running 1/1 + ✔ Container nginx Started +``` + +
+ +
+ +## 五、访问验证 + +
+ +
+ +- 访问地址;`https://ssl01.xiaofuge.tech/`、`https://ssl02.xiaofuge.tech/` 验证证书是否部署成功。 +- 如图,验证结果符合预期。 + +## 六、自动部署 + +这个自动部署非常重要,有了它我们就不用操心是什么时候过期,手动处理了! + +如果执行 `crontab -l `缺失命令,则按照下面安装; + +1. 进入容器【确保你的Nginx名称是nginx,如果是Nginx更换下】: + + ```sh + docker exec -it nginx /bin/bash + ``` + +2. 编辑 `/etc/apt/sources.list` 文件。你可以使用 `nano` 或 `vim`,如果这些编辑器没有安装,可以使用 `echo` 和 `cat` 命令手动编辑。例如: + + ```sh + echo 'deb http://deb.debian.org/debian bookworm main' > /etc/apt/sources.list + echo 'deb http://security.debian.org/debian-security bookworm-security main' >> /etc/apt/sources.list + echo 'deb http://deb.debian.org/debian bookworm-updates main' >> /etc/apt/sources.list + ``` + +3. 安装 `cron`: + + ```sh + apt-get update + apt-get install -y cron + ``` + +### 1. 获取脚本 + +
+ +
+ +
+ +
+ +### 2. 执行脚本 + +
+ +
+ +
+ +
+ +- 从 Docker Portainer 中的 Nginx 进入执行脚本。 +- 如果提示 `crontab not exits` 可以执行 crontab 安装。 + +### 3. 安装查看 + +
+ +
+ +- 如上表示自动部署正常,当证书剩余15天过期后,会自动部署。 +- 服务器查看 + + ```java + root@9c0f0d45b3e6:/# crontab -l + 50 11 * * * '/root/.httpsok/httpsok.sh' -m -r >> '/root/.httpsok/httpsok.log' 2>&1 + ``` + +### 4. 证书监控 + +
+ +
+ +- 这里还能看到每个证书剩余的有效期,还可以看到是哪个IP的服务器在使用。 diff --git a/docs/md/road-map/ssl.md b/docs/md/road-map/ssl.md new file mode 100644 index 000000000..b8b8cbc87 --- /dev/null +++ b/docs/md/road-map/ssl.md @@ -0,0 +1,269 @@ +--- +title: 域名证书 ssl - freessl +lock: need +--- + +# 免费的SSL,还能自动续期! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +兄弟👬🏻,当你手里有不少域名,每个域名又配置子域名,那么ssl将是一笔不小的费用。当然各个云厂商,也都有提供免费的ssl证书,但这里有一个问题,就是基本都不提供免费的泛域名证书(免费20个单域名证书),比如 `x.gaga.plus`、`b.gaga.plus` 这就要申请2个域名证书了!并且3个月就过期,一年就折腾这,都能折腾哭😭!那咋办? + +
+ +
+ +**跟着小傅哥学习,教会你的都是实战经验!** + +像我这样做技术分享的,再把一些学习项目部署上线让大家学习体验,站点;[https://gaga.plus](https://gaga.plus) 就会拆分出不少的子域名,做独立的项目展示。那么就非常需要一款好用的、免费的、泛域名证书,最好还能支持到期自动续期,否则有时候都容易忘记哪个域名忘记更换证书。好在后来找到了一款 [https://freessl.cn/](https://freessl.cn/) 不过这东西很多伙伴可能不会部署,所以今天给大家分享下,如何配置和使用这块免费的ssl。 + +>文末还有实战项目学习,早早的积累一些各类场景的编程经验,让自己立于不败之地,面试也就不心慌了! + +## 一、产品介绍 + +[freessl.cn](https://freessl.cn/) 一个提供免费HTTPS证书申请的网站。已实测体验,确实挺好用。另外还有一套 [httpsok.com](https://httpsok.com/?p=4kMR) 也提供了免费的 SSL 自动续签证书。 + +
+ +
+ +## 二、ssl证书 + +### 1. 泛域名证书创建 + +
+ +
+ +- `*.xiaofuge.tech` 不匹配 `xiaofuge.tech` 如果有根域名访问,需要单独再申请。 + +### 2. dcv 验证(cname) + +#### 2.1 获取验证标识 + +
+ +
+ +- 选择 cname 方式进行验证。 + +#### 2.2 域名验证配置 + +
+ +
+ +- 进入你的域名,配置解析。选择 CNAME 记录类型,填写 freessl 提供给你的记录值进行填写。 + +#### 2.3 获得部署命令 + +
+ +
+ +- 点击【配置完成,立即检测】即可获得到部署命令。如果验证失败一种是域名时间未生效,另外就是手残党复制错了。 +- 这里主要会用到 `acme.sh` 脚本进行部署。*下文部署会进行操作* + +### 3. 证书部署 + +#### 3.1 acme.sh 安装 + +acme.sh 是一个用于自动化管理和获取SSL/TLS证书的UNIX shell脚本。它使用ACME协议(自动证书管理环境)与证书颁发机构(如Let's Encrypt)进行交互,以便为网站自动颁发、续签和安装SSL/TLS证书。 + +**文档**:[https://blog.freessl.cn/acme-quick-start/](https://blog.freessl.cn/acme-quick-start/) + +```java +[root@lavm-aqhgp9nber ~]# curl https://get.acme.sh | sh -s email=xiaofuge@qq.com % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 1032 0 1032 0 0 1337 0 --:--:-- --:--:-- --:--:-- 1336 + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed +100 218k 100 218k 0 0 1338 0 0:02:46 0:02:46 --:--:-- 8442 +[Wed Apr 24 22:52:39 CST 2024] Installing from online archive. +[Wed Apr 24 22:52:39 CST 2024] Downloading https://github.com/acmesh-official/acme.sh/archive/master.tar.gz +[Wed Apr 24 22:52:46 CST 2024] Extracting master.tar.gz +[Wed Apr 24 22:52:46 CST 2024] It is recommended to install socat first. +[Wed Apr 24 22:52:46 CST 2024] We use socat for standalone server if you use standalone mode. +[Wed Apr 24 22:52:46 CST 2024] If you don't use standalone mode, just ignore this warning. +[Wed Apr 24 22:52:46 CST 2024] Installing to /root/.acme.sh +[Wed Apr 24 22:52:46 CST 2024] Installed to /root/.acme.sh/acme.sh +[Wed Apr 24 22:52:46 CST 2024] Installing alias to '/root/.bashrc' +[Wed Apr 24 22:52:46 CST 2024] OK, Close and reopen your terminal to start using acme.sh +[Wed Apr 24 22:52:46 CST 2024] Installing alias to '/root/.cshrc' +[Wed Apr 24 22:52:46 CST 2024] Installing alias to '/root/.tcshrc' +[Wed Apr 24 22:52:46 CST 2024] Installing cron job +no crontab for root +no crontab for root +[Wed Apr 24 22:52:46 CST 2024] Good, bash is found, so change the shebang to use bash as preferred. +[Wed Apr 24 22:52:48 CST 2024] OK +[Wed Apr 24 22:52:48 CST 2024] Install success! +``` + +- 注意安装完成后执行;`source ~/.bashrc` 这样才能生效 acme.sh 配置 +- 安装过程有点慢,需要等待下 Install success! 安装完成。目前官网提供的镜像地址不太好用。 + +**另外一种安装方式** + +```java +git clone https://gitee.com/neilpang/acme.sh.git +cd acme.sh +./acme.sh --install -m xiaofuge@qq.com +``` + +#### 3.2 证书安装 + +用你在 2.3 步骤获取的命令进行安装。 + +```java +# 手动更新 Nginx +acme.sh --issue -d *.xiaofuge.tech --dns dns_dp --server https://acme.freessl.cn/v2/DV90/directory/ko9v932ceuu2kr06pe68 + +# 自动更新 - Docker 版本 `--force` 重写命令,如果之前安装过需要重新安装,需要添加。 +acme.sh --issue -d *.xfg.plus --dns dns_dp --server https://acme.freessl.cn/v2/DV90/directory/bfa9mbfe2w8r2nhr5w6o --reloadcmd "docker exec nginx nginx -s reload" --force +``` + +
+ +
+ +- 执行命令后,完成安装即可获得 `*.cer`、`*.key` 两个文件地址。这个就是用于配置 ssl 域名证书的。 + +**注意自动更新,需要添加脚本** + +```java +acme.sh --issue -d gaga.plus -d *.xiaofuge.tech --dns dns_dp --server [https://acme.freessl.cn/v2/DV90/directory/bfa9mbfe2w8r2nhr5w6o](https://acme.freessl.cn/v2/DV90/directory/ko9v932ceuu2kr06pe68) \ +--key-file /dev-ops/nginx/ssl/key.pem \ +--fullchain-file /dev-ops/nginx/ssl/cert.pem \ +--reloadcmd "docker exec nginx nginx -s reload" +``` + +- `-d gaga.plus -d *.xiaofuge.tech` 可以添加一组,专门申请下 `gaga.plus ssl` +- docker 使用 `docker exec nginx nginx -s reload` 如果是直接 nginx 也可以配置 nginx 命令即可。 + +## 三、域名配置 + +
+ +
+ +- 配置域名解析,把你要使用的域名和云服务器配置上A记录。这样请求到域名的时候就会解析到服务器的IP了,以及对应的 Nginx 转发。 + +## 四、Nginx 配置 + +### 1. 脚本工程 + +
+ +
+ +- 工程:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-ssl](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-ssl) +- 说明:工程中提供了Nginx配置的初始信息,你只需要参考修改为自己的域名后,执行 `docker-compose.yml` 即可安装 Nginx + +#### 1.1 修改域名 + +```java +server { + listen 80; + listen [::]:80; + server_name ssl01.xiaofuge.tech; + + rewrite ^(.*) https://$server_name$1 permanent; + +} + +server { + listen 443 ssl; + server_name ssl01.xiaofuge.tech; + + ssl_certificate /root/.acme.sh/*.xiaofuge.tech_ecc/*.xiaofuge.tech.cer; + ssl_certificate_key /root/.acme.sh/*.xiaofuge.tech_ecc/*.xiaofuge.tech.key; + + ssl_session_cache shared:SSL:1m; + ssl_session_timeout 5m; + + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_prefer_server_ciphers on; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $http_host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + root /usr/share/nginx/html; + index index.html index.htm; + } + + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} +``` + +- 首先,修改 `ssl01.xiaofuge.tech` 为你的域名。 +- 之后,修改 ssl 地址信息。这个地址是 docker-compose 映射的地址。如果你是 linux 直接安装,则可以直接使用 acme.sh 安装的时候生成的地址。 +- 注意,更多 Nginx 配置 [https://bugstack.cn/md/road-map/nginx.html](https://bugstack.cn/md/road-map/nginx.html) + +#### 1.2 修改ssl + +```java +version: '3' +# docker-compose -f docker-compose.yml up -d +services: + nginx: + image: nginx:1.25.3 + container_name: nginx + ports: + - '443:443' + - '80:80' + volumes: + - './nginx/logs:/var/log/nginx' + - './nginx/html:/usr/share/nginx/html' + - './nginx/conf/nginx.conf:/etc/nginx/nginx.conf' + - './nginx/conf/conf.d:/etc/nginx/conf.d' + - '/root/.acme.sh/*.xiaofuge.tech_ecc/*.xiaofuge.tech.cer:/root/.acme.sh/*.xiaofuge.tech_ecc/*.xiaofuge.tech.cer' + - '/root/.acme.sh/*.xiaofuge.tech_ecc/*.xiaofuge.tech.key:/root/.acme.sh/*.xiaofuge.tech_ecc/*.xiaofuge.tech.key' + privileged: true + restart: always +``` + +- 修改 docker-compose 文件中 cer和 key 的地址。基本就是 `xiaofuge.tech` 名称的变化 + +### 2. 上传文件 + +
+ +
+ +- 配置信息全部修改后,上传到你的云服务器上。有些东西还是要云服务器锻炼,这里推荐 [https://yun.xfg.plus](https://yun.xfg.plus) 2c2g 50元一年。 + +### 3. 执行脚本 + +```java +[root@lavm-aqhgp9nber ~]# cd dev-ops/ +[root@lavm-aqhgp9nber dev-ops]# chmod +x docker-compose.yml +[root@lavm-aqhgp9nber dev-ops]# docker-compose -f docker-compose.yml up -d +[+] Running 1/1 + ✔ Container nginx Started +``` + +
+ +
+ +- 安装完成后,可以看到 443、80 端口正常启动。如果安装失败,则可以检查下日志。 + +## 五、访问验证 + +
+ +
+ +- 访问地址;`https://ssl01.xiaofuge.tech/`、`https://ssl02.xiaofuge.tech/` 验证证书是否部署成功。 +- 如图,验证结果符合预期。 + + diff --git a/docs/md/road-map/tool.md b/docs/md/road-map/tool.md new file mode 100644 index 000000000..603327e7a --- /dev/null +++ b/docs/md/road-map/tool.md @@ -0,0 +1,36 @@ +--- +title: Tool +lock: need +--- + +# Tool 工具整理 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## SSH 客户端工具 + +1. [nuoshell](https://www.nuoshell.com/) - 一款免费的专门为 Mac OSX 系统打造的,集多功能于一身的 SSH 客户端工具。 +2. [termius](https://termius.com/) - 含付费Plus功能,但基础功能已够用。可以支持电脑和移动设备使用,包括 SFTP 功能。 +3. [WindTerm](https://github.com/kingToolbox/WindTerm) - 专业的跨平台SSH/Sftp/Shell/Telnet/串口终端。完全免费,可用于商业和非商业用途,无任何限制。 +4. [SSH Config Editor](http://hejki.tilda.ws/ssheditor) - 一款用于管理OpenSSH ssh客户端配置文件的工具。 +5. [ZOC](https://www.emtec.com/zoc/) - ZOC • 适用于 macOS 和 Windows 的 SSH 客户端和终端仿真器。 +6. [SecureCRT](https://mobaxterm.mobatek.net/) - 带有 X11 服务器、选项卡式 SSH 客户端、网络工具等的增强型 Windows 终端。 +7. [Electerm](https://github.com/electerm/electerm) - 开源终端/ssh/telnet/serialport/sftp客户端(linux, mac, win)。 +8. [PuTTY](https://www.putty.org/) - PuTTY 是一个 SSH 和 telnet 客户端,最初由 Simon Tatham 为 Windows 平台开发。PuTTY 是开源软件,提供源代码,由一群志愿者开发和支持。 +9. [FinalShell SSH工具](http://www.hostbuf.com/t/988.html) - FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还是功能强大的开发,运维工具,充分满足开发,运维需求. +10. [Xterminal](https://www.terminal.icu/) - 不仅是强大的SSH工具,更提供本地控制台,以及更多即将推出的开发相关功能,让您专注于创造卓越的代码。 + +## MySql 客户端工具 + +1. [DBeaver Community - Free Universal Database Tool](https://dbeaver.io/download/) - `MySQL、Azure SQL Server、Apache Ignite...` +2. [Sequel Pro](https://www.sequelpro.com/) - `不太推荐了,有bug` +3. [Sequel Ace](https://apps.apple.com/us/app/sequel-ace/id1518036000?ls=1) - `推荐,是 Sequel Pro 的扩展维护版。免费好用!` +4. [Navicat](https://www.navicat.com.cn/products) - `同好用的就是收费,破解的在公司用可不行` +5. [IntelliJ Datagrip](https://www.jetbrains.com/zh-cn/datagrip/) - `免费使用30天` + +--- + +个人在使用 nuoshell、termius 挺稳又好用。如果你还有好用的客户端,可以点击文末的 `在 GitHub 上编辑此页` 进行提交。这样会记录你的贡献。 diff --git a/docs/md/road-map/trae.md b/docs/md/road-map/trae.md new file mode 100644 index 000000000..3294a349b --- /dev/null +++ b/docs/md/road-map/trae.md @@ -0,0 +1,210 @@ +--- +title: Trae.ai +lock: need +--- + +# Trae.ai - 真好用,码农必备工具! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +有人说程序员这个行业👨🏻‍💻总是在革自己的命,一个个编码 AI 工具的出现,是不已经不需要那么多初级 CRUD 程序员了🤔。但恰恰相反,AI 工具的出现,是把初级程序员,迅速培养到了高级工程师! + +
+ +
+ +`其实 AI 让程序员更牛!` + +以前初学编程看不懂的代码,现在使用AI能动态做解释和提供案例,以前跨语言(java->vue/react)不能做的事情,现在可以搞了。以前不能写的插件(IntelliJ IDEA、谷歌浏览器),现在能实现了。以前写不出的高级感代码,现在随手就拿到全球最优秀的编码方案。 + +今天介绍一款 AI 开发工具 [Trae.ai](https://www.trae.ai/) ,让刚入门编程的找到学习方法,让初级工程也能写出优秀的代码,让高级研发嘎嘎提高效率! + +>文末提供了 AI 应用开发实战项目,想提高 AI 应用编程能力的伙伴,可以获取学习项目。 + +## 一、软件下载 + +官网地址:[https://www.trae.ai/](https://www.trae.ai/) + +
+ +
+ +- Trae 是一个自适应的 AI IDE,它可以改变您的工作方式,与您协作以更快地运行。 +- 在使用的时候,你可以一个工程同时在 IntelliJ IDEA 打开,也使用 Trae 打开。😂 `因为习惯 IntelliJ IDEA,各类调试还是很舒服的。` + +> 同类软件 [Cursor](https://www.cursor.com/cn),不过这个想使用好的模型得付费,Trae 是不需要付费的! + +## 二、使用体验 + +### 1. 提问代码 + +**拖拽代码/文件夹/工程** + +
+ +
+ +**添加代码到对话** + +
+ +
+ +**通过#号,选择要对话的内容** + +
+ +
+ +> 之所以分享这个软件,也是因为很多小白初学编程,拿到一个项目不知道从哪里看。现在你有了这个软件,就可以非常简单的对工程的代码进行提问了。也不需要复制到任何 AI 对话工具了,并且这套软件是免费的。 + +### 2. 生成代码 - 后端 + +这里我们举例,复制掘金发文接口,之后让 Trae.ai 帮我包装; + +#### 2.1 复制接口 + +
+ +
+ +#### 2.2 研发设计 - 话术 + +
+ +
+ +**生成话术,类似于研发设计** + +```java +在 gateway 文件夹下,编写 IJueJinService 类,以 retrofit2 方式包装 curl 请求接口。接口入参仅为必要参数,如;cookie,发文的 Request 对象。 + +curl 如下; + +curl 'https://api.juejin.cn/content_api/v1/article_draft/create?aid=2608&uuid=7058897578062890496' \ + -H 'accept: */*' \ + -H 'accept-language: zh-CN,zh;q=0.9,en;q=0.8' \ + -H 'content-type: application/json' \ + -b 'store-region=cn-bj; store-region-src=uid; __tea_cookie_tokens_2608=%******a51b13e5; uid_tt_ss=7aff2f2394310b3b71f07006a51b13e5; sid_tt=3813572f48984e4b5b3cc7f90733f2e9; sessionid=3813572f48984e4b5b3cc7f90733f2e9; sessionid_ss=3813572f48984e4b5b3cc7f90733f2e9; sid_ucp_v1=1.0.0-KDU4N2NlM2Q2NjFiODhjOGNhZGE5YzVlOTM4ZWZkY2U3ZTc3MzVjMjAKFwjemIC67ozUAxDT_ci_BhiwFDgCQPEHGgJsZiIgMzgxMzU3MmY0ODk4NGU0YjViM2NjN2Y5MDczM2YyZTk; ssid_ucp_v1=1.0.0-KDU4N2NlM2Q2NjFiODhjOGNhZGE5YzVlOTM4ZWZkY2U3ZTc3MzVjMjAKFwjemIC67ozUAxDT_ci_BhiwFDgCQPEHGgJsZiIgMzgxMzU3MmY0ODk4NGU0YjViM2NjN2Y5MDczM2YyZTk' \ + -H 'dnt: 1' \ + -H 'origin: https://juejin.cn' \ + -H 'priority: u=1, i' \ + -H 'referer: https://juejin.cn/' \ + -H 'sec-ch-ua: "Not(A:Brand";v="99", "Google Chrome";v="133", "Chromium";v="133"' \ + -H 'sec-ch-ua-mobile: ?0' \ + -H 'sec-ch-ua-platform: "macOS"' \ + -H 'sec-fetch-dest: empty' \ + -H 'sec-fetch-mode: cors' \ + -H 'sec-fetch-site: same-site' \ + -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' \ + -H 'x-secsdk-csrf-token: 00010000000152d838f2c09c65e4feef033c275a8cff940c12228719074fe36f9ce2d18b4d251833ae62ea768117' \ + --data-raw '{"category_id":"0","tag_ids":[],"link_url":"","cover_image":"","title":"测试文章","brief_content":"","edit_type":10,"html_content":"deprecated","mark_content":"","theme_ids":[],"pics":[]}' + +接口返回结果; + +{ + "err_no": 0, + "err_msg": "success", + "data": { + "id": "7489358088379531291", + "article_id": "0", + "user_id": "2058727733595230", + "category_id": "0", + "tag_ids": [], + "link_url": "", + "cover_image": "", + "is_gfw": 0, + "title": "测试文章", + "brief_content": "", + "is_english": 0, + "is_original": 1, + "edit_type": 10, + "html_content": "deprecated", + "mark_content": "", + "ctime": "1743929226", + "mtime": "1743929226", + "status": 0, + "original_type": 0, + "theme_ids": [] + } +} + +根据接口信息封装 DTO 对象,放到 gateway dto 下。 +``` + +#### 2.3 编码效果 + +
+ +
+ +- 生成后,代码会直接写入到你说明的文件夹下,创建好相关的类信息。之后你可以点击全部接受,他会审查代码,如果代码有问题还会检修编写。 + +### 3. 生成代码 - 前端 + +
+ +
+ +- 这是小傅哥实现的一套 AI RAG&MCP 知识库项目,设计的一套页面。这套页面使用了 AI 做了大量的编码。`以前能写前端,但总感觉是屎上雕花,不出活!`这回有 AI 工具,省心多了。 +- AI 对于这类既定的,没有复杂的逻辑和各类框架综合使用的,真的描述一句优化UI,它就`苦吃苦吃`的干活了! + +> 你发现没,你只要写一些描述,它就能很好的干活了。这是因为 AI Agent 智能体会包括;规划、记忆、召回、工具(MCP),来把需求转换为可以看到的代码。 + +## 三、实战项目 + +这是小傅哥最近带着大家做的一套 AI 应用实战项目,《DeepSeek RAG&MCP 增强检索知识库系统》 - 解析文档&Git仓库代码&AI工作流,全程视频手把手教学。 + +RAG:实现了除普通文档知识解析外,增加了 Git 代码库的拉取和解析,并提供操作接口。为工程师做项目开发时,`需求分析`、`研发设计`、`辅助编码`、`代码评审`、`风险评估`、`上线检测`等,做工程交付提效。 + +MCP:用不了多久,各大互联网企业都将大量的推进落地,自有 MCP 服务的实现,用于增强企业 AI 应用的提效能力。因为 MCP 的加入,可以让你;一条命令`帮研发`,调用应用系统日志、排查系统CPU负载、自主选择是否调度数据库信息。也可以一条命令`帮运营`,搞定复杂的SQL执行、导出报表、分析数据、完成促活营销券的自动化配置上架。这就是 MCP的魅力!👍🏻 + +**RAG + MCP = 智能AI工作流,如智能客服,智能编码助手,智能运维工程师等。** + +
+ +
+ +```java +@Test +public void test_weixinNotice_chatMemory() { + System.out.println("\n>>> ASSISTANT: " + chatClient + .prompt(""" + 我需要你帮我生成一篇文章,要求如下; + + 1. 场景为互联网大厂java求职者面试 + 2. 面试管提问 Java 核心知识、JUC、JVM、多线程、线程池、HashMap、ArrayList、Spring、SpringBoot、MyBatis、Dubbo、RabbitMQ、xxl-job、Redis、MySQL、Linux、Docker、设计模式、DDD等不限于此的各项技术问题。 + 3. 按照故事场景,以严肃的面试官和搞笑的水货程序员谢飞机进行提问,谢飞机对简单问题可以回答,回答好了面试官还会夸赞。复杂问题胡乱回答,回答的不清晰。 + 4. 每次进行3轮提问,每轮可以有3-5个问题。这些问题要有技术业务场景上的衔接性,循序渐进引导提问。最后是面试官让程序员回家等通知类似的话术。 + 5. 提问后把问题的答案,写到文章最后,最后的答案要详细讲述出技术点,让小白可以学习下来。 + + 根据以上内容,不要阐述其他信息,请直接提供;文章标题、文章内容、文章标签(多个用英文逗号隔开)、文章简述(100字) + + 将以上内容发布文章到CSDN。 + """) + .advisors(advisor -> advisor + .param(CHAT_MEMORY_CONVERSATION_ID_KEY, "1001") + .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)) + .call() + .content()); + + + System.out.println("\n>>> ASSISTANT: " + chatClient + .prompt(""" + 之后进行,微信公众号消息通知,平台:CSDN、主题:为文章标题、描述:为文章简述、跳转地址:从发布文章到CSDN获取 url + """) + .advisors(advisor -> advisor + .param(CHAT_MEMORY_CONVERSATION_ID_KEY, "1001") + .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)) + .call() + .content()); +} +``` + +- 两轮对话,发文到CSDN,之后推送消息到公众号平台。这两轮对话是配置有记忆功能的,所以可以衔接上下文。 +- 这样的技术,还是非常有必要积累的,因为很多中大厂也都开了 AI 开发工程师的岗位。 diff --git a/docs/md/road-map/zookeeper.md b/docs/md/road-map/zookeeper.md new file mode 100644 index 000000000..2fc45ae39 --- /dev/null +++ b/docs/md/road-map/zookeeper.md @@ -0,0 +1,442 @@ +--- +title: Zookeeper +lock: need +--- + +# Zookeeper 的使用和配置中心实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +本文的宗旨在于通过简单干净实践的方式,向读者介绍 Zookeeper 的安装配置,学习 SpringBoot 整合使用,以及基于 Zookeeper 开发一个简单的配置中心功能内核。通过这样的实践方式,让读者入门和掌握 Zookeeper 以应对后续需要此技术栈的相关开发项目。 + +本文的重点是基于 Zookeeper 实现的配置中心,那配置中心是啥呢?🤔 + +配置中心在大厂系统开发中是一个非常常用的功能,它的核心功能在于不需要上线系统的情况下,改变系统中对象或者属性的值。是属性的值,也就是你在通过类获取某个属性,判断;功能开关、渠道地址、人群名单、息费费率、切量占比等等,这些可能随时动态调整的值,都是通过配置中心实现的。所以在本章节的案例中,小傅哥基于 Zookeeper 组件的功能特性,来设计这样一个配置中心,方便大家学习。 + +本文涉及的工程: + +- xfg-dev-tech-zookeeper:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-zookeeper](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-zookeeper) - `docs/dev-ops 提供了 Zookeeper 安装脚本` + +## 一、配置中心 + +`Zookeeper 有什么特性,动态配置中心怎么做?` + +技术是支撑解决方案实现的,有了各个技术栈组件的自身特点,才好实现出我们所需的各类功能。那么这样的一个能让,各个服务都可以动态变更配置的配置中心,就要用到 Zookeeper 的节点监听和节点值的变化来动态设置 Java 类中属性的变化。如图 + +
+ +
+ +- 首先,我们需要定义出一个 Zookeeper 监听的配置路径,一般这个路径在配置中心中是申请的系统使用地址,以确保值的唯一。 +- 之后,每个类对应的属性,需要映射出一个监听的节点。比如;Zookeeper 监听了 `/xfg-dev-tech/config` 那么类中 a 属性可以是 `/xfg-dev-tech/config/a` 这对这个路径设置的值,就可以被监听拿到了。 +- 最后,把获取到的监听值,通过 Java 反射操作,把值设置到对应的属性上。这样在 SpringBoot 应用程序中,使用某个类的属性值的时候,就可以动态的获取到变化的属性值了。 + +## 二、环境配置 + +在安装执行 docker-compose.yml 脚本之前,你需要先在本地安装 [docker](https://bugstack.cn/md/road-map/docker.html) 之后 IntelliJ IDEA 打开 docker-compose.yml 文件,如图操作即可安装。 + +
+ +
+ +
+ +
+ +- 另外,如果你是在服务器上安装,则需要执行 `docker-compose -f docker-compose.yml up -d` 并且是你已经安装了 Docker-Compose 包。—— 这些内容在小傅哥的[《Java 简明教程》](https://bugstack.cn/md/road-map/road-map.html)中都有讲解,可以进入学习。 + +## 三、基本使用 + +**连接脚本**: + +```shell +docker exec -it zookeeper bash +zkCli.sh -server IP(替换为你自己的):2181 +``` + +**常用命令**: + +```shell +1. 创建节点:create /path data +2. 创建临时节点:create -e /path data +3. 创建顺序节点:create -s /path data +4. 创建临时顺序节点:create -e -s /path data +5. 获取节点数据:get /path +6. 获取节点子节点列表:ls /path +7. 更新节点数据:set /path data +8. 删除节点:delete /path +9. 删除节点及其子节点:deleteall /path +10. 监听节点变化:get -w /path +11. 查看节点状态:stat /path +12. 查看节点ACL权限:getAcl /path +13. 设置节点ACL权限:setAcl /path acl +14. 查看节点子节点数量:count /path +15. 查看节点子节点数量并监听变化:count -w /path +``` + +```shell +root@4365b68d50d6:/apache-zookeeper-3.9.0-bin# ls +bin conf docs lib LICENSE.txt NOTICE.txt README.md README_packaging.md +root@4365b68d50d6:/apache-zookeeper-3.9.0-bin# zkCli.sh -server 10.253.6.71:2181 + +[zk: 192.168.1.101:2181(CONNECTED) 1] ls /xfg-dev-tech +[config, configdowngradeSwitch] +[zk: 192.168.1.101:2181(CONNECTED) 2] +``` + +执行完链接 Zookeeper 以后,就可以执行这些常用命令了。你也可以尝试着练习下这些命令。 + +## 四、功能实现 + +### 1. 工程结构 + +
+ +
+ +工程结构分为2个部分: +- app 启动层的 config 包下,用于提供 Zookeeper 服务的启动配置。以及小傅哥在这里新添加的功能 DCCValue 配置中心模块。 +- trigger 是触发器,这里吧 http 请求、listener 监听,都是放到这里使用。另外像 MQ、JOB、RPC 也是放到这一层,以这一层触发,来调用我们的领域服务。 + +### 2. 启动 Zookeeper 服务 + +#### 2.1 自定配置 + +```java +@Data +@ConfigurationProperties(prefix = "zookeeper.sdk.config", ignoreInvalidFields = true) +public class ZookeeperClientConfigProperties { + + private String connectString; + private int baseSleepTimeMs; + private int maxRetries; + private int sessionTimeoutMs; + private int connectionTimeoutMs; + +} +``` + +#### 2.2 使用配置 + +```xml +zookeeper: + sdk: + config: + connect-string: 10.253.6.71:2181 + base-sleep-time-ms: 1000 + max-retries: 3 + session-timeout-ms: 1800000 + connection-timeout-ms: 30000 +``` + +#### 2.3 配置服务 + +```java +@Configuration +@EnableConfigurationProperties(ZookeeperClientConfigProperties.class) +public class ZooKeeperClientConfig { + + /** + * 多参数构建ZooKeeper客户端连接 + * + * @return client + */ + @Bean(name = "zookeeperClient") + public CuratorFramework createWithOptions(ZookeeperClientConfigProperties properties) { + ExponentialBackoffRetry backoffRetry = new ExponentialBackoffRetry(properties.getBaseSleepTimeMs(), properties.getMaxRetries()); + CuratorFramework client = CuratorFrameworkFactory.builder() + .connectString(properties.getConnectString()) + .retryPolicy(backoffRetry) + .sessionTimeoutMs(properties.getSessionTimeoutMs()) + .connectionTimeoutMs(properties.getConnectionTimeoutMs()) + .build(); + client.start(); + return client; + } + +} +``` + +- 这样我们就可以启动一个 Zookeeper 的客户端了,自定义可以更好的控制和使用。 + +### 3. 定义注解 + +就功能来讲,我们需要对类中的属性进行赋值操作。那么就需要使用自定义注解进行标记。所以这里我们先自定义一个注解。 + +```java +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +@Documented +public @interface DCCValue { + + String value() default ""; + +} +``` + +- 这样所有使用了 `@DCCValue` 的注解的字段就都可以被我扫描到了。 + +### 4. 监听变化 + +#### 4.1 获取属性 + +**源码**:`cn.bugstack.xfg.dev.tech.config.DCCValueBeanFactory#postProcessAfterInitialization` + +```java +@Override +public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + Class beanClass = bean.getClass(); + Field[] fields = beanClass.getDeclaredFields(); + for (Field field : fields) { + if (field.isAnnotationPresent(DCCValue.class)) { + DCCValue dccValue = field.getAnnotation(DCCValue.class); + try { + if (null == client.checkExists().forPath(BASE_CONFIG_PATH.concat("/").concat(dccValue.value()))) { + client.create().creatingParentsIfNeeded().forPath(BASE_CONFIG_PATH.concat("/").concat(dccValue.value())); + log.info("DCC 节点监听 listener node {} not absent create new done!", BASE_CONFIG_PATH.concat("/").concat(dccValue.value())); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + dccObjGroup.put(BASE_CONFIG_PATH.concat("/").concat(dccValue.value()), bean); + } + } + return bean; +} +``` + +- DCCValueBeanFactory 实现了 BeanPostProcessor 接口的 postProcessAfterInitialization 方法。 +- 在实现中,通过对 bean 对象的解析获取到使用 DCCValue 注解属性,并判断这个属性拼接的地址是否在 Zookeeper 中创建,如果没有则创建。之后保存对象到内存中。 + +#### 4.2 设置属性 + +**源码**:`cn.bugstack.xfg.dev.tech.config.DCCValueBeanFactory#DCCValueBeanFactory` + +```java +curatorCache.listenable().addListener((type, oldData, data) -> { + switch (type) { + case NODE_CHANGED: + String dccValuePath = data.getPath(); + Object objBean = dccObjGroup.get(dccValuePath); + try { + // 1. getDeclaredField 方法用于获取指定类中声明的所有字段,包括私有字段、受保护字段和公共字段。 + // 2. getField 方法用于获取指定类中的公共字段,即只能获取到公共访问修饰符(public)的字段。 + Field field = objBean.getClass().getDeclaredField(dccValuePath.substring(dccValuePath.lastIndexOf("/") + 1)); + field.setAccessible(true); + field.set(objBean, new String(data.getData())); + field.setAccessible(false); + } catch (Exception e) { + throw new RuntimeException(e); + } + break; + default: + break; + } +}); +``` + +- 基于 Zookeeper 对节点的监听,只要这个节点上有值发生变化。就可以立刻检测到对应的路径信息和值信息。 +- 那么拿到这个值信息,就可以把值写入到对应的属性上了。如`类.A = Zookeeper 获取到的值` + +## 五、功能使用 + +**源码**:`cn.bugstack.xfg.dev.tech.trigger.http.ConfigController` + +```java +@RestController +public class ConfigController { + + @DCCValue("downgradeSwitch") + private String downgradeSwitch; + + @DCCValue("userWhiteList") + private String userWhiteList; + + @Resource + private CuratorFramework curatorFramework; + + /** + * curl http://localhost:8091/getConfig/downgradeSwitch + */ + @RequestMapping("/getConfig/downgradeSwitch") + public String getConfigDowngradeSwitch() { + return downgradeSwitch; + } + + /** + * curl http://localhost:8091/getConfig/userWhiteList + */ + @RequestMapping("/getConfig/userWhiteList") + public String getConfigUserWhiteList() { + return userWhiteList; + } + + /** + * curl -X GET "http://localhost:8091/setConfig?downgradeSwitch=false&userWhiteList=xfg,user2,user3" + */ + @GetMapping("/setConfig") + public void setConfig(Boolean downgradeSwitch, String userWhiteList) throws Exception { + curatorFramework.setData().forPath("/xfg-dev-tech/config/downgradeSwitch", (downgradeSwitch ? "开" : "关").getBytes(StandardCharsets.UTF_8)); + curatorFramework.setData().forPath("/xfg-dev-tech/config/userWhiteList", userWhiteList.getBytes(StandardCharsets.UTF_8)); + } + +} +``` + +这里的核心验证就是让 downgradeSwitch、userWhiteList 这2个属性值可以动态变化; +1. 在两个属性上添加注解后,就会被扫描和管理。 +2. 获取值方法:[http://localhost:8091/getConfig/downgradeSwitch](http://localhost:8091/getConfig/downgradeSwitch)、[http://localhost:8091/getConfig/userWhiteList](http://localhost:8091/getConfig/userWhiteList) +3. 设置值方法:[http://localhost:8091/setConfig?downgradeSwitch=false&userWhiteList=xfg,user2,user3](http://localhost:8091/setConfig?downgradeSwitch=false&userWhiteList=xfg,user2,user3) - `这里的设置值操作不非得在这里,可以是一个单独的控制后台来操作。这里的方式主要是演示作用` + +
+ +
+ +你可以按照如图的操作顺序,进行验证属性值的变化。 + +## 六、其他测试 + +```java +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApiTest { + + @Resource + private CuratorFramework curatorFramework; + + @Test + public void test_all() throws Exception { + String path = "/xfg-dev-tech/config/downgradeSwitch"; + String data = "0"; + curatorFramework.create().withMode(CreateMode.EPHEMERAL).forPath(path, data.getBytes(StandardCharsets.UTF_8)); + + for (int i = 0; i < 2; i++) { + curatorFramework.setData().forPath(path, String.valueOf(i).getBytes(StandardCharsets.UTF_8)); + } + } + + /** + * 创建永久节点 + */ + @Test + public void createNode() throws Exception { + String path = "/xfg-dev-tech/config/downgradeSwitch/test/a"; + String data = "0"; + if (null == curatorFramework.checkExists().forPath(path)) { + curatorFramework.create().creatingParentsIfNeeded().forPath(path); + } + } + + /** + * 创建临时节点 + */ + @Test + public void createEphemeralNode() throws Exception { + String path = "/xfg-dev-tech/config/epnode"; + String data = "0"; + curatorFramework.create().withMode(CreateMode.EPHEMERAL).forPath(path, data.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 创建临时有序节点 + */ + @Test + public void crateEphemeralSequentialNode() throws Exception { + String path = "/xfg-dev-tech/config/epsnode"; + String data = "0"; + curatorFramework.create() + .withMode(CreateMode.EPHEMERAL_SEQUENTIAL) + .forPath(path, data.getBytes(StandardCharsets.UTF_8)); + } + + /** + * 往节点种设置数据 + */ + @Test + public void setData() throws Exception { + curatorFramework.setData().forPath("/xfg-dev-tech/config/downgradeSwitch", "111".getBytes(StandardCharsets.UTF_8)); + curatorFramework.setData().forPath("/xfg-dev-tech/config/userWhiteList", "222".getBytes(StandardCharsets.UTF_8)); + } + + @Test + public void getData() throws Exception { + String downgradeSwitch = new String(curatorFramework.getData().forPath("/xfg-dev-tech/config/downgradeSwitch"), StandardCharsets.UTF_8); + log.info("测试结果: {}", downgradeSwitch); + String userWhiteList = new String(curatorFramework.getData().forPath("/xfg-dev-tech/config/userWhiteList"), StandardCharsets.UTF_8); + log.info("测试结果: {}", userWhiteList); + } + + /** + * 异步修改数据 + */ + @Test + public void setDataAsync() throws Exception { + String path = "/xfg-dev-tech/config/downgradeSwitch"; + String data = "0"; + CuratorListener listener = (client, event) -> { + Stat stat = event.getStat(); + log.info("stat=" + JSON.toJSONString(stat)); + CuratorEventType eventType = event.getType(); + log.info("eventType=" + eventType.name()); + }; + curatorFramework.getCuratorListenable().addListener(listener); + curatorFramework.setData().inBackground().forPath(path, data.getBytes(StandardCharsets.UTF_8)); + } + + + /** + * 删除节点 + */ + @Test + public void deleteData() throws Exception { + String path = "/xfg-dev-tech/config/downgradeSwitch"; + curatorFramework.delete().deletingChildrenIfNeeded().forPath(path); + } + + /** + * 安全删除节点 + */ + @Test + public void guaranteedDeleteData() throws Exception { + String path = "/xfg-dev-tech/config/downgradeSwitch"; + curatorFramework.delete().guaranteed().forPath(path); + } + + /** + * 获取子节点下的全部子节点路径集合 + */ + @Test + public void watchedGetChildren() throws Exception { + String path = "/xfg-dev-tech"; + List children = curatorFramework.getChildren().watched().forPath(path); + log.info("测试结果:{}", JSON.toJSONString(children)); + } + + + /** + * 获取节点数据 + */ + @Test + public void getDataByPath() throws Exception { + String path = "/xfg-dev-tech/config/downgradeSwitch"; + String fullClassName = ""; + String jsonStr = new String(curatorFramework.getData().forPath(path), StandardCharsets.UTF_8); + Class clazz = Class.forName(fullClassName); + log.info("测试结果:{}", JSON.parseObject(jsonStr, clazz)); + } + +} +``` + +- 这些功能也都可以测试验证,也是平常用的较多的东西。 + +## 七、其他资料 + +- Zookeeper Web UI:[https://zoonavigator.elkozmon.com/en/latest/](https://zoonavigator.elkozmon.com/en/latest/) +- 官网文档:[https://zookeeper.apache.org/doc/r3.5.0-alpha/zookeeperAdmin.html](https://zookeeper.apache.org/doc/r3.5.0-alpha/zookeeperAdmin.html) \ No newline at end of file diff --git "a/docs/md/spring/develop-mybatis/2022-03-27-\347\254\2542\347\253\240\357\274\232\345\210\233\345\273\272\347\256\200\345\215\225\347\232\204\346\230\240\345\260\204\345\231\250\344\273\243\347\220\206\345\267\245\345\216\202.md" "b/docs/md/spring/develop-mybatis/2022-03-27-\347\254\2542\347\253\240\357\274\232\345\210\233\345\273\272\347\256\200\345\215\225\347\232\204\346\230\240\345\260\204\345\231\250\344\273\243\347\220\206\345\267\245\345\216\202.md" index a91079be2..dfbceecc0 100644 --- "a/docs/md/spring/develop-mybatis/2022-03-27-\347\254\2542\347\253\240\357\274\232\345\210\233\345\273\272\347\256\200\345\215\225\347\232\204\346\230\240\345\260\204\345\231\250\344\273\243\347\220\206\345\267\245\345\216\202.md" +++ "b/docs/md/spring/develop-mybatis/2022-03-27-\347\254\2542\347\253\240\357\274\232\345\210\233\345\273\272\347\256\200\345\215\225\347\232\204\346\230\240\345\260\204\345\231\250\344\273\243\347\220\206\345\267\245\345\216\202.md" @@ -185,4 +185,12 @@ Process finished with exit code 0 - 本章节我们初步对 Mybatis 框架中的数据库 DAO 操作接口和映射器通过代理类的方式进行链接,这一步也是 ORM 框架里非常核心的部分。有了这块的内容,就可以在代理类中进行自己逻辑的扩展了。 - 在框架实现方面引入简单工厂模式包装代理类,屏蔽创建细节,这些也是大家在学习过程中需要注意的设计模式的点。 -- 目前内容还比较简单的,可以手动操作练习,随着我们内容的增加,会有越来越多的包和类引入,完善 ORM 框架功能。 \ No newline at end of file +- 目前内容还比较简单的,可以手动操作练习,随着我们内容的增加,会有越来越多的包和类引入,完善 ORM 框架功能。 + +## 七、优秀作业 + +- [使用动态代理为接口生成代理对象 @W](https://t.zsxq.com/0662Nzr3j) +- [学会动态代理的原理及使用 @Alpha](https://t.zsxq.com/063ZvJubq) +- [通过jdk动态代理的方式,将生成代理对象 @](https://t.zsxq.com/06aei2NfA) +- [在mybatis中都是使用Mapper接口,直接调用查询方法 @灬](https://t.zsxq.com/08DRpPAav) +- [创建简单的映射器工厂 @liuc](https://t.zsxq.com/08ldxyf2S) \ No newline at end of file diff --git "a/docs/md/spring/develop-mybatis/2022-04-04-\347\254\2543\347\253\240\357\274\232\345\256\236\347\216\260\346\230\240\345\260\204\345\231\250\347\232\204\346\263\250\345\206\214\345\222\214\344\275\277\347\224\250.md" "b/docs/md/spring/develop-mybatis/2022-04-04-\347\254\2543\347\253\240\357\274\232\345\256\236\347\216\260\346\230\240\345\260\204\345\231\250\347\232\204\346\263\250\345\206\214\345\222\214\344\275\277\347\224\250.md" index ad62a8d63..c07ceedee 100644 --- "a/docs/md/spring/develop-mybatis/2022-04-04-\347\254\2543\347\253\240\357\274\232\345\256\236\347\216\260\346\230\240\345\260\204\345\231\250\347\232\204\346\263\250\345\206\214\345\222\214\344\275\277\347\224\250.md" +++ "b/docs/md/spring/develop-mybatis/2022-04-04-\347\254\2543\347\253\240\357\274\232\345\256\236\347\216\260\346\230\240\345\260\204\345\231\250\347\232\204\346\263\250\345\206\214\345\222\214\344\275\277\347\224\250.md" @@ -40,7 +40,7 @@ lock: need ![图 3-1 映射器的注册和使用](https://bugstack.cn/images/article/spring/mybatis-220404-01.png) - 以包装接口提供映射器代理类为目标,补全映射器注册机 `MapperRegistry`,自动扫描包下接口并把每个接口类映射的代理类全部存入映射器代理的 HashMap 缓存中。 -- 而 SqlSession、SqlSessionFactory 是在此注册映射器代理的上次层使用标准定义和对外服务提供的封装,便于用户使用。*我们把使用方当成用户* 经过这样的封装就就可以更加方便我们后续在框架上功能的继续扩展了,也希望大家可以在学习的过程中对这样的设计结构有一些思考,它可以帮助你解决一些业务功能开发过程中的领域服务包装。 +- 而 SqlSession、SqlSessionFactory 是在此注册映射器代理的上层使用标准定义和对外服务提供的封装,便于用户使用。*我们把使用方当成用户* 经过这样的封装就就可以更加方便我们后续在框架上功能的继续扩展了,也希望大家可以在学习的过程中对这样的设计结构有一些思考,它可以帮助你解决一些业务功能开发过程中的领域服务包装。 ## 四、实现 @@ -305,3 +305,13 @@ Process finished with exit code 0 - 首先要从设计结构上了解工厂模式对具体功能结构的封装,屏蔽过程细节,限定上下文关系,把对外的使用减少耦合。 - 从这个过程上读者伙伴也能发现,使用 SqlSessionFactory 的工厂实现类包装了 SqlSession 的标准定义实现类,并由 SqlSession 完成对映射器对象的注册和使用。 - 本章学习要注意几个重要的知识点,包括:映射器、代理类、注册机、接口标准、工厂模式、上下文。这些工程开发的技巧都是在手写 Mybatis 的过程中非常重要的部分,了解和熟悉才能更好的在自己的业务中进行使用。 + +## 七、优秀作业 + +- [实现映射器的注册和使用 @liuc](https://t.zsxq.com/08y9Q7qPd) +- [需要将dao层接口与mapper 映射器关联起来,需要创建一个注册器 @Alpha](https://t.zsxq.com/06IaAMZvF) +- [通过对应mapper的映射器工厂可以生成相应的映射器对象 @W](https://t.zsxq.com/06uBau7Qr) +- [创建MapperProxyFactory的缓存器,并且为了解放双手 @W](https://t.zsxq.com/06uBau7Qr) +- [完成映射器的注册以及代码调试 @巍](https://t.zsxq.com/06AMrzv3j) +- [我们虽然帮Mapper类接口进行代理,交给代理工厂去实现 @灬](https://t.zsxq.com/086IbEDtl) +- [通过动态代理的方式实现 Spring 核心的 AOP 功能。@Homage](https://t.zsxq.com/08OZaj17G) diff --git "a/docs/md/spring/develop-mybatis/2022-04-09-\347\254\2544\347\253\240\357\274\232XML\347\232\204\350\247\243\346\236\220\345\222\214\346\263\250\345\206\214\344\275\277\347\224\250.md" "b/docs/md/spring/develop-mybatis/2022-04-09-\347\254\2544\347\253\240\357\274\232XML\347\232\204\350\247\243\346\236\220\345\222\214\346\263\250\345\206\214\344\275\277\347\224\250.md" index f437f862d..1e9b9c4bc 100644 --- "a/docs/md/spring/develop-mybatis/2022-04-09-\347\254\2544\347\253\240\357\274\232XML\347\232\204\350\247\243\346\236\220\345\222\214\346\263\250\345\206\214\344\275\277\347\224\250.md" +++ "b/docs/md/spring/develop-mybatis/2022-04-09-\347\254\2544\347\253\240\357\274\232XML\347\232\204\350\247\243\346\236\220\345\222\214\346\263\250\345\206\214\344\275\277\347\224\250.md" @@ -300,3 +300,8 @@ Process finished with exit code 0 - SqlSessionFactoryBuilder 的引入包装了整个执行过程,包括:XML 文件的解析、Configuration 配置类的处理,让 DefaultSqlSession 可以更加灵活的拿到对应的信息,获取 Mapper 和 SQL 语句。 - 另外从整个工程搭建的过程中,可以看到有很多工厂模式、建造者模式、代理模式的使用,也有很多设计原则的运用,这些技巧都可以让整个工程变得易于维护和易于迭代。*这也是研发人员在学习源码的过程中,非常值得重点关注的地方。* +## 七、优秀作业 + +- [Mapper XML的解析和注册使用 @liuc](https://t.zsxq.com/08wDxcVV4) +- [其实中心点在于Configuration这个类。Resources,XMLConfigBuilder是为了获取Configuration做准备。@echo](https://t.zsxq.com/081RvQF4B) +- [定义SqlSessionFactoryBuilder工厂建造者模式类 @谁动了我的辣条](https://t.zsxq.com/0cWqIjDR6) diff --git "a/docs/md/spring/develop-mybatis/2022-04-17-\347\254\2545\347\253\240\357\274\232\346\225\260\346\215\256\346\272\220\347\232\204\350\247\243\346\236\220\343\200\201\345\210\233\345\273\272\345\222\214\344\275\277\347\224\250.md" "b/docs/md/spring/develop-mybatis/2022-04-17-\347\254\2545\347\253\240\357\274\232\346\225\260\346\215\256\346\272\220\347\232\204\350\247\243\346\236\220\343\200\201\345\210\233\345\273\272\345\222\214\344\275\277\347\224\250.md" index c1f7ecf54..6b8195882 100644 --- "a/docs/md/spring/develop-mybatis/2022-04-17-\347\254\2545\347\253\240\357\274\232\346\225\260\346\215\256\346\272\220\347\232\204\350\247\243\346\236\220\343\200\201\345\210\233\345\273\272\345\222\214\344\275\277\347\224\250.md" +++ "b/docs/md/spring/develop-mybatis/2022-04-17-\347\254\2545\347\253\240\357\274\232\346\225\260\346\215\256\346\272\220\347\232\204\350\247\243\346\236\220\343\200\201\345\210\233\345\273\272\345\222\214\344\275\277\347\224\250.md" @@ -21,7 +21,7 @@ lock: need 而作为一个本身就很理科的程序员来说,如果都是被泛知识充斥,花费着自己的精力和时间,没有经过足够的脑力思考所吸收的泛技术内容,长期以往是很难有所成长的。 -以为我个人的成长经历来看,我更愿意花很多的实际来解决一个问题,而不是一片问题。当一个问题解决的足够透彻、清晰、明确以后,再结合着这个知识点所需要的内容继续扩展和深挖。很庆幸当年没有那么多的泛知识内容推送,否则可能我也会被弄的很焦虑! +以为我个人的成长经历来看,我更愿意花很多的时间来解决一个问题,而不是一片问题。当一个问题解决的足够透彻、清晰、明确以后,再结合着这个知识点所需要的内容继续扩展和深挖。很庆幸当年没有那么多的泛知识内容推送,否则可能我也会被弄的很焦虑! ## 二、目标 @@ -505,5 +505,8 @@ Process finished with exit code 0 ## 七、优秀作业 -- [学习数据源的创建和使用 By 🐱](https://t.zsxq.com/05a2zjYJM) -- [第五章 数据源的创建、解析和使用 By 杨杨得亿🙉](https://t.zsxq.com/05QJYjEIE) \ No newline at end of file +- [学习数据源的创建和使用 @🐱](https://t.zsxq.com/05a2zjYJM) +- [数据源的创建、解析和使用 @杨杨得亿🙉](https://t.zsxq.com/05QJYjEIE) +- [提供数据源供SQL执行时真正调用 @W](https://t.zsxq.com/0627ImaiM) +- [数据源的解析、创建、使用 @liuc](https://t.zsxq.com/08dNbsT2N) +- [依赖上一节的基础上,实现对我们Mybatis-config.xml中环境节点的配置文件的解析 @谁动了我的辣条](https://t.zsxq.com/0cNRLoPVr) diff --git "a/docs/md/spring/develop-mybatis/2022-04-23-\347\254\2546\347\253\240\357\274\232\346\225\260\346\215\256\346\272\220\346\261\240\345\214\226\346\212\200\346\234\257\345\256\236\347\216\260.md" "b/docs/md/spring/develop-mybatis/2022-04-23-\347\254\2546\347\253\240\357\274\232\346\225\260\346\215\256\346\272\220\346\261\240\345\214\226\346\212\200\346\234\257\345\256\236\347\216\260.md" index 452a18672..a6afe6ff6 100644 --- "a/docs/md/spring/develop-mybatis/2022-04-23-\347\254\2546\347\253\240\357\274\232\346\225\260\346\215\256\346\272\220\346\261\240\345\214\226\346\212\200\346\234\257\345\256\236\347\216\260.md" +++ "b/docs/md/spring/develop-mybatis/2022-04-23-\347\254\2546\347\253\240\357\274\232\346\225\260\346\215\256\346\272\220\346\261\240\345\214\226\346\212\200\346\234\257\345\256\236\347\216\260.md" @@ -584,4 +584,7 @@ public void test_SqlSessionFactory() throws IOException { - [池化连接池执行过程 @JabesYang](https://t.zsxq.com/05AiEAMfu) - [为什么要进行数据库的池化呢? @晓伟](https://t.zsxq.com/05JUnEYrR) - [重新设置数据源属性的时候,强制关闭活跃连接和空闲连接 @欧阳宇](https://t.zsxq.com/05JujAQZn) -- [完成Mybatis 中自己的数据源实现,包括无池化的 UnpooledDataSource 实现方式和有池化的 PooledDataSource 实现方式。](https://t.zsxq.com/06Y7Uniia) \ No newline at end of file +- [完成Mybatis 中自己的数据源实现,包括无池化的 UnpooledDataSource 实现方式和有池化的 PooledDataSource 实现方式。](https://t.zsxq.com/06Y7Uniia) +- [实现Mybatis自己的数据库连接池 @W](https://t.zsxq.com/07KmJnHXs) +- [数据源池化技术实现 @liuc](https://t.zsxq.com/08NdOTppd) +- [实现Mybatis 自己的数据源,包括无池化的 UnpooledDataSource 实现方式和有池化的 PooledDataSource 实现方式 @小猪碎碎念](https://t.zsxq.com/0bOLO8aNq) \ No newline at end of file diff --git "a/docs/md/spring/develop-mybatis/2022-04-28-\347\254\2547\347\253\240\357\274\232SQL\346\211\247\350\241\214\345\231\250\347\232\204\345\256\232\344\271\211\345\222\214\345\256\236\347\216\260.md" "b/docs/md/spring/develop-mybatis/2022-04-28-\347\254\2547\347\253\240\357\274\232SQL\346\211\247\350\241\214\345\231\250\347\232\204\345\256\232\344\271\211\345\222\214\345\256\236\347\216\260.md" index b7f3ebc7b..f5656022b 100644 --- "a/docs/md/spring/develop-mybatis/2022-04-28-\347\254\2547\347\253\240\357\274\232SQL\346\211\247\350\241\214\345\231\250\347\232\204\345\256\232\344\271\211\345\222\214\345\256\236\347\216\260.md" +++ "b/docs/md/spring/develop-mybatis/2022-04-28-\347\254\2547\347\253\240\357\274\232SQL\346\211\247\350\241\214\345\231\250\347\232\204\345\256\232\344\271\211\345\222\214\345\256\236\347\216\260.md" @@ -493,4 +493,10 @@ Process finished with exit code 0 ## 七、优秀作业 -- [为什么要定义SQL执行器?](https://t.zsxq.com/05bYB6iqb) \ No newline at end of file +- [为什么要定义SQL执行器?](https://t.zsxq.com/05bYB6iqb) +- [将重复的步骤(装载sql,设置参数,封装返回结果) 以一个执行器的方式来进行封装 @Alpha](https://t.zsxq.com/0662fuB62) +- [调用关系类整体流程梳理 @空白](https://t.zsxq.com/07zEwbY91) +- [之前对数据源的使用、执行SQL、封装结果,都耦合在DefaultSqlSession#selectOne方法中 @liuc](https://t.zsxq.com/08svosiof) +- [Sql执行器的定义和实现 @小猪碎碎念](https://t.zsxq.com/0bXbxKHvd) +- [在以后写代码需要考虑到代码的解耦,能够把Excutor这种设计思想应用到实际的业务代码中](https://t.zsxq.com/0ceTgynZc) +- [解耦,对于流程的解耦 @AD钙奶](https://t.zsxq.com/107WusqQD) \ No newline at end of file diff --git "a/docs/md/spring/develop-mybatis/2022-05-03-\347\254\2548\347\253\240\357\274\232\346\212\212\345\217\215\345\260\204\347\224\250\345\210\260\345\207\272\347\245\236\345\205\245\345\214\226.md" "b/docs/md/spring/develop-mybatis/2022-05-03-\347\254\2548\347\253\240\357\274\232\346\212\212\345\217\215\345\260\204\347\224\250\345\210\260\345\207\272\347\245\236\345\205\245\345\214\226.md" index 83078120a..b5f3de781 100644 --- "a/docs/md/spring/develop-mybatis/2022-05-03-\347\254\2548\347\253\240\357\274\232\346\212\212\345\217\215\345\260\204\347\224\250\345\210\260\345\207\272\347\245\236\345\205\245\345\214\226.md" +++ "b/docs/md/spring/develop-mybatis/2022-05-03-\347\254\2548\347\253\240\357\274\232\346\212\212\345\217\215\345\260\204\347\224\250\345\210\260\345\207\272\347\245\236\345\205\245\345\214\226.md" @@ -627,4 +627,9 @@ public void test_SqlSessionFactory() throws IOException { ## 八、优秀作业 -- [完成mybatis自己实现的MetaObject反射工具类。这块包括:元对象、对象包装器、对象工厂、对象包装工厂以及Reflector反射器。 @杨杨得亿🙉](https://t.zsxq.com/0666qVFqj) \ No newline at end of file +- [完成mybatis自己实现的MetaObject反射工具类。这块包括:元对象、对象包装器、对象工厂、对象包装工厂以及Reflector反射器。 @杨杨得亿🙉](https://t.zsxq.com/0666qVFqj) +- [了解了SecurityManager的使用和反射获取泛型的方式 @狗哥](https://t.zsxq.com/07jLbAB1V) +- [本章就是要自己动手实现一下反射工具包,并用它来改造原数据源工厂的部分代码 @小猪碎碎念](https://t.zsxq.com/0bfxapOQB) +- [为了改造这样的硬编码方式,我们就可以基于JDK反射功能封装一个工具包 @liuc](https://t.zsxq.com/08Zdjyfd8) +- [将创建DataSource时的硬编码部分修改为使用反射工具类进行赋值,熟悉反射工具类的使用以及构建流程。@高高的飞起来](https://t.zsxq.com/10tbST0Gg) +- [这一章内容超级的多,再也不敢说自己会反射了,学无止境捂😂【梳理的很细腻】@AD钙奶](https://t.zsxq.com/10LV8eY5X) \ No newline at end of file diff --git "a/docs/md/spring/develop-mybatis/2022-05-18-\347\254\2549\347\253\240\357\274\232\347\273\206\345\214\226XML\350\257\255\345\217\245\346\236\204\345\273\272\345\231\250\357\274\214\345\256\214\345\226\204\351\235\231\346\200\201SQL\350\247\243\346\236\220.md" "b/docs/md/spring/develop-mybatis/2022-05-18-\347\254\2549\347\253\240\357\274\232\347\273\206\345\214\226XML\350\257\255\345\217\245\346\236\204\345\273\272\345\231\250\357\274\214\345\256\214\345\226\204\351\235\231\346\200\201SQL\350\247\243\346\236\220.md" index cff677a3a..bfe402c2c 100644 --- "a/docs/md/spring/develop-mybatis/2022-05-18-\347\254\2549\347\253\240\357\274\232\347\273\206\345\214\226XML\350\257\255\345\217\245\346\236\204\345\273\272\345\231\250\357\274\214\345\256\214\345\226\204\351\235\231\346\200\201SQL\350\247\243\346\236\220.md" +++ "b/docs/md/spring/develop-mybatis/2022-05-18-\347\254\2549\347\253\240\357\274\232\347\273\206\345\214\226XML\350\257\255\345\217\245\346\236\204\345\273\272\345\231\250\357\274\214\345\256\214\345\226\204\351\235\231\346\200\201SQL\350\247\243\346\236\220.md" @@ -505,3 +505,9 @@ Process finished with exit code 0 - 本章节我们就像是去把原本 CRUD 的代码,通过设计原则进行拆分和解耦,运用不用的类来承担不同的职责,完整整个功能的实现。这包括;映射构建器、语句构建器、源码构建器的综合使用,以及对应的引用;脚本语言驱动和脚本构建器解析,处理我们的 XML 中的 SQL 语句。 - 通过这样的重构代码,也能让我们对平常的业务开发中的大片面向过程的流程代码有所感悟,当你可以细分拆解职责功能到不同的类中去以后,你的代码会更加的清晰并易于维护。 - 后续我们将继续按照现在的扩展结构底座,完成其他模块的功能逻辑开发,因为了这些基础内容的建造,再继续补充功能也会更加容易。*当然这些代码还是需要你熟悉以后才能驾驭,在学习的过程中可以尝试断点调试,看看每一个步骤都在完成哪些工作。* + +## 七、优秀作业 + +- [引入映射构建器XMLMapperBuilder和语句映射器XMLStatementBuilder细化XML语句构建器。@杨杨得亿🙉](https://t.zsxq.com/06ZFQF2zb) +- [细化XML语句构建器,完善静态SQL解析 @liuc](https://t.zsxq.com/09BrVRsSa) +- [核心类UML类图梳理 @小理想](https://t.zsxq.com/09ldlXf8A) \ No newline at end of file diff --git "a/docs/md/spring/develop-mybatis/2022-05-26-\347\254\25410\347\253\240\357\274\232\344\275\277\347\224\250\347\255\226\347\225\245\346\250\241\345\274\217\357\274\214\350\260\203\347\224\250\345\217\202\346\225\260\345\244\204\347\220\206\345\231\250.md" "b/docs/md/spring/develop-mybatis/2022-05-26-\347\254\25410\347\253\240\357\274\232\344\275\277\347\224\250\347\255\226\347\225\245\346\250\241\345\274\217\357\274\214\350\260\203\347\224\250\345\217\202\346\225\260\345\244\204\347\220\206\345\231\250.md" index 06bad87c0..0e0d0f6ba 100644 --- "a/docs/md/spring/develop-mybatis/2022-05-26-\347\254\25410\347\253\240\357\274\232\344\275\277\347\224\250\347\255\226\347\225\245\346\250\241\345\274\217\357\274\214\350\260\203\347\224\250\345\217\202\346\225\260\345\244\204\347\220\206\345\231\250.md" +++ "b/docs/md/spring/develop-mybatis/2022-05-26-\347\254\25410\347\253\240\357\274\232\344\275\277\347\224\250\347\255\226\347\225\245\346\250\241\345\274\217\357\274\214\350\260\203\347\224\250\345\217\202\346\225\260\345\244\204\347\220\206\345\231\250.md" @@ -528,4 +528,10 @@ public void test_queryUserInfo() { - 到本章节,我们算是把一个 ORM 框架的基本流程串联起来了,不要硬编码也能完成简单 SQL 的处理。读者伙伴可以仔细阅读下当前框架中,所包含的分包结构。比如:构建、绑定、映射、反射、执行、类型、事务、数据源等等,尝试画一画他们的链接关系,这样会让你更加清晰现在的代码解耦结构。 - 此章节中比较重要的体现是关于参数类型的策略化设计,通过策略解耦,模板定义流程,让我们整个参数设置变得更加清晰,也就不需要硬编码了。 -- 除此之外也有一些细节的功能点,如;MapperMethod 中添加方法签名、类型处理器创建和使用时候,都使用了 MetaObject 这样的反射器工具类进行处理。这些细节的功能点,读者需要在学习的过程中,进行调试验证才能更好的吸收此类编码设计的技巧和经验。 \ No newline at end of file +- 除此之外也有一些细节的功能点,如;MapperMethod 中添加方法签名、类型处理器创建和使用时候,都使用了 MetaObject 这样的反射器工具类进行处理。这些细节的功能点,读者需要在学习的过程中,进行调试验证才能更好的吸收此类编码设计的技巧和经验。 + +## 七、优秀作业 + +- [针对不同类型的参数,使用了对应的类型处理器完成参数值的注入 @liuc](https://t.zsxq.com/09DAtfS4T) +- [手写Mybatis源码第一章到第十章总览 @小理想](https://t.zsxq.com/0e8CESFcV) +- [这一节核心就是把参数化的功能,由硬编码变成使用类型处理器进行设置。运用策略模式,在构建参数时根据类型,选择对应的策略类型处理器。](https://t.zsxq.com/11bIcdvEs) \ No newline at end of file diff --git "a/docs/md/spring/develop-mybatis/2022-06-02-\347\254\25411\347\253\240\357\274\232\346\265\201\347\250\213\350\247\243\350\200\246\357\274\214\345\260\201\350\243\205\347\273\223\346\236\234\351\233\206\345\244\204\347\220\206\345\231\250.md" "b/docs/md/spring/develop-mybatis/2022-06-02-\347\254\25411\347\253\240\357\274\232\346\265\201\347\250\213\350\247\243\350\200\246\357\274\214\345\260\201\350\243\205\347\273\223\346\236\234\351\233\206\345\244\204\347\220\206\345\231\250.md" index 90352bf9e..a64cc3682 100644 --- "a/docs/md/spring/develop-mybatis/2022-06-02-\347\254\25411\347\253\240\357\274\232\346\265\201\347\250\213\350\247\243\350\200\246\357\274\214\345\260\201\350\243\205\347\273\223\346\236\234\351\233\206\345\244\204\347\220\206\345\231\250.md" +++ "b/docs/md/spring/develop-mybatis/2022-06-02-\347\254\25411\347\253\240\357\274\232\346\265\201\347\250\213\350\247\243\350\200\246\357\274\214\345\260\201\350\243\205\347\273\223\346\236\234\351\233\206\345\244\204\347\220\206\345\231\250.md" @@ -424,4 +424,11 @@ Process finished with exit code 0 - 这一章节的整个功能实现,都在围绕流程的解耦进行处理,将对象的参数解析和结果封装都进行拆解,通过这样的方式来分配各个模块的单一职责,不让一个类的方法承担过多的交叉功能。 - 那么我们在结合这样的思想和设计,反复阅读和动手实践中,来学习这样的代码设计和开发过程,都能为我们以后实际开发业务代码时候带来参考建议,避免总是把所有的流程都写到一个类或者方法中。 -- 到本章节全核心流程基本就串联清楚了,再有的就是一些功能的拓展,比如支持更多的参数类型,以及添加除了 Select 以外的其他操作,还有一些缓存数据的使用等,后面章节将在这些内容中,摘取一些核心的设计和实现进行讲解,让读者吸收更多的设计技巧。 \ No newline at end of file +- 到本章节全核心流程基本就串联清楚了,再有的就是一些功能的拓展,比如支持更多的参数类型,以及添加除了 Select 以外的其他操作,还有一些缓存数据的使用等,后面章节将在这些内容中,摘取一些核心的设计和实现进行讲解,让读者吸收更多的设计技巧。 + +## 七、优秀作业 + +- [承接上一章节使用策略模式对参数的处理,这一章节对执行完查询的结果进行封装处理。@杨杨得亿🙉](https://t.zsxq.com/07RFEEyBI) +- [将原来在XMLStatementBuilder中保存MapperStatement对象改变到在MapperBuilderAssistant中生产 @空空](https://t.zsxq.com/084fyLjUU) +- [对全部类进行分类并梳理整体流程 @狗狗](https://t.zsxq.com/08RMH5y82) +- [关键步骤是封装数据,这里需要获取每一行的记录 @AD钙奶](https://t.zsxq.com/110088i2N) \ No newline at end of file diff --git "a/docs/md/spring/develop-mybatis/2022-06-10-\347\254\25412\347\253\240\357\274\232\345\256\214\345\226\204ORM\346\241\206\346\236\266\357\274\214\345\242\236\345\210\240\346\224\271\346\237\245\346\223\215\344\275\234.md" "b/docs/md/spring/develop-mybatis/2022-06-10-\347\254\25412\347\253\240\357\274\232\345\256\214\345\226\204ORM\346\241\206\346\236\266\357\274\214\345\242\236\345\210\240\346\224\271\346\237\245\346\223\215\344\275\234.md" index 4ba8dbd1e..ed32883a3 100644 --- "a/docs/md/spring/develop-mybatis/2022-06-10-\347\254\25412\347\253\240\357\274\232\345\256\214\345\226\204ORM\346\241\206\346\236\266\357\274\214\345\242\236\345\210\240\346\224\271\346\237\245\346\223\215\344\275\234.md" +++ "b/docs/md/spring/develop-mybatis/2022-06-10-\347\254\25412\347\253\240\357\274\232\345\256\214\345\226\204ORM\346\241\206\346\236\266\357\274\214\345\242\236\345\210\240\346\224\271\346\237\245\346\223\215\344\275\234.md" @@ -689,3 +689,9 @@ Process finished with exit code 0 - 到本章节我们就把 Mybatis 的全部主干流程串联实现完成了,可以执行对数据库的增删改查操作,读者伙伴也可以发现,本章节在原有的内容下,进行扩展的时候也是非常方便的,甚至不要多大的代码改动。这主要也得益于框架在设计实现过程中,合理运用设计原则和设计模式的好处。 - 读者在学习的过程中,可以调试源码中的一些参数,比如像事务是否自动提交,查询出来的参数是否可以添加其他类型,在增删改查中,是否还有其他情况的处理。这些小的功能点,都可以进行添加练习,如果你能正确的添加并走完流程验证公国,那么说明你是真的学习会了。 - 在本章节全部基础功能链路串联完毕以后,关于 Mybatis 的框架中,还有一些额外扩展的知识点,比如:插入时返回当前ID、Map 类型映射、一级二级缓存、插件模块等,后续的章节中我们会找一些有代表性的内容,进行扩展开发学习。读者伙伴也可以按照目前的框架结构,自行添加这写模块进行练习学习。 + +## 七、优秀作业 + +- [串联现在所有的主干流程 @杨杨得亿🙉](https://t.zsxq.com/07MVb6aqz) +- [基于前面各章节的解析MAPPER_XML、处理参数、封装结果,增加insert、update、delete以及select返回集合的操作 @liuc](https://t.zsxq.com/09u8LJtrZ) +- [完善ORM框架,增删改查操作 @AD钙奶](https://t.zsxq.com/11hc2GWF4) \ No newline at end of file diff --git "a/docs/md/spring/develop-mybatis/2022-06-14-\347\254\25413\347\253\240\357\274\232\351\200\232\350\277\207\346\263\250\350\247\243\351\205\215\347\275\256\346\211\247\350\241\214SQL\350\257\255\345\217\245.md" "b/docs/md/spring/develop-mybatis/2022-06-14-\347\254\25413\347\253\240\357\274\232\351\200\232\350\277\207\346\263\250\350\247\243\351\205\215\347\275\256\346\211\247\350\241\214SQL\350\257\255\345\217\245.md" index cec01c45c..7cfa6861c 100644 --- "a/docs/md/spring/develop-mybatis/2022-06-14-\347\254\25413\347\253\240\357\274\232\351\200\232\350\277\207\346\263\250\350\247\243\351\205\215\347\275\256\346\211\247\350\241\214SQL\350\257\255\345\217\245.md" +++ "b/docs/md/spring/develop-mybatis/2022-06-14-\347\254\25413\347\253\240\357\274\232\351\200\232\350\277\207\346\263\250\350\247\243\351\205\215\347\275\256\346\211\247\350\241\214SQL\350\257\255\345\217\245.md" @@ -11,6 +11,12 @@ pay: https://articles.zsxq.com/id_cushlx5xx5wj.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [做了以下修改,生成了4个注解,在读取核心配置文件的时候读到resource的时候继续解析Mapper文件中的内容 @Liuliuliu](https://t.zsxq.com/08lyBSRqq) +- [通过注解配置执行SQL语句 @liuc](https://t.zsxq.com/09QD8QCKL) +- [只要对于流程熟悉,就能很容易知道每一步做什么,在扩展的时候也清楚上下文如何对接。发现越到后面章节,越有种柳暗花明的感觉。@AD钙奶](https://t.zsxq.com/11hp9XMGK) + ## 一、前言 `谁提出问题,就要解决问题吗?` diff --git "a/docs/md/spring/develop-mybatis/2022-06-21-\347\254\25414\347\253\240\357\274\232\350\247\243\346\236\220\345\222\214\344\275\277\347\224\250ResultMap\346\230\240\345\260\204\345\217\202\346\225\260\351\205\215\347\275\256.md" "b/docs/md/spring/develop-mybatis/2022-06-21-\347\254\25414\347\253\240\357\274\232\350\247\243\346\236\220\345\222\214\344\275\277\347\224\250ResultMap\346\230\240\345\260\204\345\217\202\346\225\260\351\205\215\347\275\256.md" index fdafcce02..2e633abf2 100644 --- "a/docs/md/spring/develop-mybatis/2022-06-21-\347\254\25414\347\253\240\357\274\232\350\247\243\346\236\220\345\222\214\344\275\277\347\224\250ResultMap\346\230\240\345\260\204\345\217\202\346\225\260\351\205\215\347\275\256.md" +++ "b/docs/md/spring/develop-mybatis/2022-06-21-\347\254\25414\347\253\240\357\274\232\350\247\243\346\236\220\345\222\214\344\275\277\347\224\250ResultMap\346\230\240\345\260\204\345\217\202\346\225\260\351\205\215\347\275\256.md" @@ -11,6 +11,12 @@ pay: https://articles.zsxq.com/id_lszxhhridkrl.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [理解如何根据ResultMap映射参数配置设置值 @杨杨得亿🙉](https://t.zsxq.com/07aQrRBUf) +- [在解析Mapper文件的时候不仅解析select、insert、update、delete等标签还会解析ResultMap标签 @Liuliuliu](https://t.zsxq.com/080Lv2uGP) +- [将数据库中user表字段user_name转为User对象实体中的userName @liuc](https://t.zsxq.com/0aExg5nNl) + ## 一、前言 `学源码只是为了快速定位错误?` diff --git "a/docs/md/spring/develop-mybatis/2022-06-25-\347\254\25415\347\253\240\357\274\232\350\277\224\345\233\236Insert\346\223\215\344\275\234\350\207\252\345\242\236\347\264\242\345\274\225\345\200\274.md" "b/docs/md/spring/develop-mybatis/2022-06-25-\347\254\25415\347\253\240\357\274\232\350\277\224\345\233\236Insert\346\223\215\344\275\234\350\207\252\345\242\236\347\264\242\345\274\225\345\200\274.md" index b706edf5d..443dc69e9 100644 --- "a/docs/md/spring/develop-mybatis/2022-06-25-\347\254\25415\347\253\240\357\274\232\350\277\224\345\233\236Insert\346\223\215\344\275\234\350\207\252\345\242\236\347\264\242\345\274\225\345\200\274.md" +++ "b/docs/md/spring/develop-mybatis/2022-06-25-\347\254\25415\347\253\240\357\274\232\350\277\224\345\233\236Insert\346\223\215\344\275\234\350\207\252\345\242\236\347\264\242\345\274\225\345\200\274.md" @@ -11,6 +11,11 @@ pay: https://articles.zsxq.com/id_gwfa0x8nciwn.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [在解析INSERT SELECT UPDATE DELETE标签的时候进行生产 @Liuliuliu](https://t.zsxq.com/08nJotW1f) +- [执行insert操作后,返回该插入语句的自增索引值(即主键) @liuc](https://t.zsxq.com/0bdLqAP8V) + ## 一、前言 `技术的把控,往往都是体现在细节上!` diff --git "a/docs/md/spring/develop-mybatis/2022-06-28-\347\254\25416\347\253\240\357\274\232\350\247\243\346\236\220\345\220\253\346\240\207\347\255\276\347\232\204\345\212\250\346\200\201SQL\350\257\255\345\217\245.md" "b/docs/md/spring/develop-mybatis/2022-06-28-\347\254\25416\347\253\240\357\274\232\350\247\243\346\236\220\345\220\253\346\240\207\347\255\276\347\232\204\345\212\250\346\200\201SQL\350\257\255\345\217\245.md" index 2c4af3128..2431172c1 100644 --- "a/docs/md/spring/develop-mybatis/2022-06-28-\347\254\25416\347\253\240\357\274\232\350\247\243\346\236\220\345\220\253\346\240\207\347\255\276\347\232\204\345\212\250\346\200\201SQL\350\257\255\345\217\245.md" +++ "b/docs/md/spring/develop-mybatis/2022-06-28-\347\254\25416\347\253\240\357\274\232\350\247\243\346\236\220\345\220\253\346\240\207\347\255\276\347\232\204\345\212\250\346\200\201SQL\350\257\255\345\217\245.md" @@ -11,6 +11,10 @@ pay: https://articles.zsxq.com/id_1rzspttev8ls.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [解析动态SQL标签等,拼接动态SQL @liuc](https://t.zsxq.com/0b6bqeAoT) + ## 一、前言 `你手里还有几张能打的牌?` @@ -25,4 +29,4 @@ pay: https://articles.zsxq.com/id_1rzspttev8ls.html ![图 16-1 根据判断 activityId 是否为 null 拼装到 SQL 语句上](https://bugstack.cn/images/article/spring/mybatis-220628-01.png) -而这样一种基于拼装 SQL 的方式,在 Mybatis 框架中是非常常用的,所以我们需要在现有对 Mapper XML 静态 SQL 的解析上,在 XML 脚本构建器中扩充对动态 SQL 的处理,最终让我们的 ORM 框架可以配置拼装 SQL 语句。 \ No newline at end of file +而这样一种基于拼装 SQL 的方式,在 Mybatis 框架中是非常常用的,所以我们需要在现有对 Mapper XML 静态 SQL 的解析上,在 XML 脚本构建器中扩充对动态 SQL 的处理,最终让我们的 ORM 框架可以配置拼装 SQL 语句。 diff --git "a/docs/md/spring/develop-mybatis/2022-07-01-\347\254\25417\347\253\240\357\274\232Plugin\346\217\222\344\273\266\345\212\237\350\203\275\345\256\236\347\216\260.md" "b/docs/md/spring/develop-mybatis/2022-07-01-\347\254\25417\347\253\240\357\274\232Plugin\346\217\222\344\273\266\345\212\237\350\203\275\345\256\236\347\216\260.md" index d5e323ec0..a989a6dbd 100644 --- "a/docs/md/spring/develop-mybatis/2022-07-01-\347\254\25417\347\253\240\357\274\232Plugin\346\217\222\344\273\266\345\212\237\350\203\275\345\256\236\347\216\260.md" +++ "b/docs/md/spring/develop-mybatis/2022-07-01-\347\254\25417\347\253\240\357\274\232Plugin\346\217\222\344\273\266\345\212\237\350\203\275\345\256\236\347\216\260.md" @@ -11,6 +11,11 @@ pay: https://articles.zsxq.com/id_k7havmcaefdk.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [处理动态SQL,在处理Element标签的时候 @Liuliuliu](https://t.zsxq.com/08ghPOtG5) +- [为Mybatis写一个插件模块,使得分页、数据库表路由、日志监控都可以通过插件形式插入Mybatis框架 @liuc](https://t.zsxq.com/0bSxhXfwG) + ## 一、前言 `各类框架诞生的核心是什么?` diff --git "a/docs/md/spring/develop-mybatis/2022-07-04-\347\254\25418\347\253\240\357\274\232\344\270\200\347\272\247\347\274\223\345\255\230.md" "b/docs/md/spring/develop-mybatis/2022-07-04-\347\254\25418\347\253\240\357\274\232\344\270\200\347\272\247\347\274\223\345\255\230.md" index 76191b987..954a8557b 100644 --- "a/docs/md/spring/develop-mybatis/2022-07-04-\347\254\25418\347\253\240\357\274\232\344\270\200\347\272\247\347\274\223\345\255\230.md" +++ "b/docs/md/spring/develop-mybatis/2022-07-04-\347\254\25418\347\253\240\357\274\232\344\270\200\347\272\247\347\274\223\345\255\230.md" @@ -11,6 +11,10 @@ pay: https://articles.zsxq.com/id_8d0o471we1ig.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [在数据库的一次会话周期内,如果需要反复执行同一个查询语句,就可以引入一级缓存的概念,避免重复查询数据库 @liuc](https://t.zsxq.com/0bMBSeOge) + ## 一、前言 `为什么使用缓存?` diff --git "a/docs/md/spring/develop-mybatis/2022-07-05-\347\254\25419\347\253\240\357\274\232\344\272\214\347\272\247\347\274\223\345\255\230.md" "b/docs/md/spring/develop-mybatis/2022-07-05-\347\254\25419\347\253\240\357\274\232\344\272\214\347\272\247\347\274\223\345\255\230.md" index 33c94badd..bc4cab368 100644 --- "a/docs/md/spring/develop-mybatis/2022-07-05-\347\254\25419\347\253\240\357\274\232\344\272\214\347\272\247\347\274\223\345\255\230.md" +++ "b/docs/md/spring/develop-mybatis/2022-07-05-\347\254\25419\347\253\240\357\274\232\344\272\214\347\272\247\347\274\223\345\255\230.md" @@ -11,6 +11,13 @@ pay: https://articles.zsxq.com/id_gviwjzv5h4wp.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [Mybatis学习完成总结 @Liuliuliu](https://t.zsxq.com/09hp37Bn1) +- [为啥二级缓存需要 sqlsession commit/close 等操作呢 @echo](https://t.zsxq.com/099y9Gv2t) +- [一级缓存实现了,在一次session会话生命周期内,首次查询会将结果保存至缓存 @liuc](https://t.zsxq.com/0bqVDMF5F) +- [学习整理图稿 @CCAT](https://t.zsxq.com/0estiRTTq) + ## 一、前言 `没有深度,就不会有广度!` diff --git "a/docs/md/spring/develop-mybatis/2022-07-06-\347\254\25420\347\253\240\357\274\232\346\225\264\345\220\210Spring.md" "b/docs/md/spring/develop-mybatis/2022-07-06-\347\254\25420\347\253\240\357\274\232\346\225\264\345\220\210Spring.md" index f121e9459..37c0ff8ac 100644 --- "a/docs/md/spring/develop-mybatis/2022-07-06-\347\254\25420\347\253\240\357\274\232\346\225\264\345\220\210Spring.md" +++ "b/docs/md/spring/develop-mybatis/2022-07-06-\347\254\25420\347\253\240\357\274\232\346\225\264\345\220\210Spring.md" @@ -11,6 +11,11 @@ pay: https://articles.zsxq.com/id_2pwtkmjixi73.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [对mybatis模块进行了区分 @狗哥](https://t.zsxq.com/08VV8iKef) +- [对MyBatis架构梳理 @领走你的小可爱](https://t.zsxq.com/09DY5gjWk) + ## 一、前言 `技术迁移在实现中间件中的运用` diff --git "a/docs/md/spring/develop-mybatis/2022-07-07-\347\254\25421\347\253\240\357\274\232\345\256\214\347\273\223.md" "b/docs/md/spring/develop-mybatis/2022-07-07-\347\254\25421\347\253\240\357\274\232\345\256\214\347\273\223.md" index 72cfa0647..3a54553d6 100644 --- "a/docs/md/spring/develop-mybatis/2022-07-07-\347\254\25421\347\253\240\357\274\232\345\256\214\347\273\223.md" +++ "b/docs/md/spring/develop-mybatis/2022-07-07-\347\254\25421\347\253\240\357\274\232\345\256\214\347\273\223.md" @@ -92,7 +92,7 @@ lock: need 其实我特别喜欢这样耐着性子,慢慢汇总,慢慢输出的感觉,因为只要在正确的路上,不反复的横跳,坚持着完成一件需要3~4个月的事情,一定比仅仅是只学习几天更有价值。 -而这样的事我已经做了好多,包括:面经手册,Netty4.x,手写Spring,用Java实现JVM,重学Java设计模式,SpringBoot中间件开发,IDEA插件开发,DDD系统架构项目开发,字节码编程,手写Spring、Lottery分布式项目... +而这样的事我已经做了好多,包括:面经手册,Netty4.x,手写Spring,用Java实现JVM,重学Java设计模式,SpringBoot中间件开发,IDEA插件开发,DDD系统架构项目开发,字节码编程,手写Mybatis、Lottery分布式项目... 云山苍苍,江水泱泱,先生之风,山高水长!加油! diff --git a/docs/md/spring/develop-mybatis/2024-04-28-mybatis-source-code-analysis-diagram.md b/docs/md/spring/develop-mybatis/2024-04-28-mybatis-source-code-analysis-diagram.md new file mode 100644 index 000000000..dbc4fa69c --- /dev/null +++ b/docs/md/spring/develop-mybatis/2024-04-28-mybatis-source-code-analysis-diagram.md @@ -0,0 +1,112 @@ +--- +title: 【番外】花了19周,终于把这套源码解析图,全部画完了! +lock: need +--- + +# 【番外】花了19周,终于把这套源码解析图,全部画完了! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家伙,我是技术UP主小傅哥。 + +每周画一张MyBatis源码图,渐进式学习,19周,终于画完啦!💐 再配合每一张图都来一个深度的视频讲解,完美!终于是可以让粉丝伙伴们,嘎嘎稳的吸收到源码的设计精髓。—— 一套源码的学习,收益于整个编程生涯! + +
+ +
+ +**🥷认真做技术,让每个关注我的技术伙伴受益!** + +小傅哥学过的源码有很多,包括;Spring、Dubbo、MQ、任务调度框架等,但最让我受益的是 MyBatis 源码。因为其他的大部分源码框架都是解决技术问题,但日常的开发中,做技术组件造轮子的时候并不多,大部分是做业务相关的组件或者解决业务场景问题。而 MyBatis 源码就是一个“双边生意”,处理 dao 和 mysql 的关联映射,这中间抽象出了;解析、会话、数据源、池化、工厂、代理、执行器等各类高内聚和低耦合的设计。 + +如果说 Spring 是卧龙,那 MyBatis 就一定可以称之为凤雏!我的感受是;Spring 复杂精妙,设计的高。MyBatis 大开大和,设计的硬。两个都学习下来,那你的代码写出来可谓是`黄四郎,黄老爷的代码了!` + +接下来,小傅哥就给大家展示下这19章源码分析图,告诉小伙伴们怎么学习,让它成为自己的知识。 + +>🎁文末可以获得源码图原搞,以及5个业务项目,4个组件项目。 + +## 一、源码设计图 + +这一套图的创作,来自于小傅哥在公司层面分享的技术公开课,每周固定时间分享。每次都从听众的反馈中,细腻的调整讲课视频的内容和涉及的图稿。让大家更有渐进式的感觉来学习源码,从0到1,也就可以很好的接受框架源码中的设计技巧,来辅助日常的业务开发。 + +以下是部分截取,完整的图稿可在文末获取。 + +### 1. 代理和工厂 + +
+ +
+ +### 2. 会话数据源 + +
+ +
+ +### 3. 动态上下文 + +
+ +
+ +### 4. 一二级缓存 + +
+ +
+ +> 单看这些图小伙伴们就会知道小傅哥需要花费好大的经历!但能帮助大家更加有效的学习,我也会非常有成就感! + +## 二、全程有视频 + +每一张图的背后都有一次源码讲解的视频课,这些视频与之前的录制的MyBatis视频不同,这次主要以源码的设计思想为核心进行讲解,让大家彻底感受到框架源码的设计魅力和高级编码的技巧手段。**有了这样的积累,你以后也能做出非常漂亮的工程结构和代码实现。** + +
+ +
+ +> 这种一点点🤏🏻带着你学习的方式,用架构师多年积累的经验给你讲解,要远比自己看吸收的更多。否则一头扎进去源码,根本出不来。 + +## 三、课程的目录 + +- 第1章:开篇介绍,我要带你撸 Mybatis 啦! +- 第2章:创建简单的映射器代理工厂 +- 第3章:实现映射器的注册和使用 +- 第4章:Mapper XML的解析和注册使用 +- 第5章:数据源的解析、创建和使用 +- 第6章:数据源池化技术实现 +- 第7章:SQL执行器的定义和实现 +- 第8章:把反射用到出神入化 +- 第9章:细化XML语句构建器,完善静态SQL解析 +- 第10章:使用策略模式,调用参数处理器 +- 第11章:流程解耦,封装结果集处理器 +- 第12章:完善ORM框架,增删改查操作 +- 第13章:通过注解配置执行SQL语句 +- 第14章:解析和使用ResultMap映射参数配置 +- 第15章:返回Insert操作自增索引值 +- 第16章:解析含标签的动态SQL语句 +- 第17章:Plugin插件功能实现 +- 第18章:一级缓存 +- 第19章:二级缓存 +- 第20章:【作业】整合Spring + +## 四、学完写简历 + +实习,校招,简历有一个业务项目 + 组件/一个源码学习项目,可以说竞争力是非常强的,小傅哥带的不少伙伴都是这样配合,斩获了非常多的大厂Offer!最后都纠结选哪家了! + +
+ +
+ +## 五、加入学习吧! + +**《手写MyBatis:渐进式源码实践》** 课程地址:[https://t.zsxq.com/xMQ6W](https://t.zsxq.com/xMQ6W) 涵盖;文档、视频、图稿。 + +注意📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:大营销、OpenAI 应用、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。`🌶加入后,从星球课程入口进入,源码学习 - 手写MyBatis` + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +> 这样成体系的全量项目学习,放在一些平台售卖,至少都要上千块。但小傅哥的星球,只需要100多,就可以获得大厂架构师对你手把手教学!https://gaga.plus - 项目演示地址。 \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-05-16-\347\254\2541\347\253\240\357\274\232\345\274\200\347\257\207\344\273\213\347\273\215\357\274\214\346\211\213\345\206\231Spring\350\203\275\347\273\231\344\275\240\345\270\246\346\235\245\344\273\200\344\271\210\357\274\237.md" "b/docs/md/spring/develop-spring/2021-05-16-\347\254\2541\347\253\240\357\274\232\345\274\200\347\257\207\344\273\213\347\273\215\357\274\214\346\211\213\345\206\231Spring\350\203\275\347\273\231\344\275\240\345\270\246\346\235\245\344\273\200\344\271\210\357\274\237.md" index 14c98c993..1acd69699 100644 --- "a/docs/md/spring/develop-spring/2021-05-16-\347\254\2541\347\253\240\357\274\232\345\274\200\347\257\207\344\273\213\347\273\215\357\274\214\346\211\213\345\206\231Spring\350\203\275\347\273\231\344\275\240\345\270\246\346\235\245\344\273\200\344\271\210\357\274\237.md" +++ "b/docs/md/spring/develop-spring/2021-05-16-\347\254\2541\347\253\240\357\274\232\345\274\200\347\257\207\344\273\213\347\273\215\357\274\214\346\211\213\345\206\231Spring\350\203\275\347\273\231\344\275\240\345\270\246\346\235\245\344\273\200\344\271\210\357\274\237.md" @@ -10,11 +10,6 @@ lock: need # 《Spring 手撸专栏》第 1 章:开篇介绍,我要带你撸 Spring 啦! - - 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn)
原文:[https://mp.weixin.qq.com/s/g7YdIe_FSrk-WE8nQRO3TA](https://mp.weixin.qq.com/s/g7YdIe_FSrk-WE8nQRO3TA) diff --git "a/docs/md/spring/develop-spring/2021-05-20-\347\254\2542\347\253\240\357\274\232\345\260\217\350\257\225\347\211\233\345\210\200\357\274\214\345\256\236\347\216\260\344\270\200\344\270\252\347\256\200\345\215\225\347\232\204Bean\345\256\271\345\231\250.md" "b/docs/md/spring/develop-spring/2021-05-20-\347\254\2542\347\253\240\357\274\232\345\260\217\350\257\225\347\211\233\345\210\200\357\274\214\345\256\236\347\216\260\344\270\200\344\270\252\347\256\200\345\215\225\347\232\204Bean\345\256\271\345\231\250.md" index fc2f9843d..ea58b34f1 100755 --- "a/docs/md/spring/develop-spring/2021-05-20-\347\254\2542\347\253\240\357\274\232\345\260\217\350\257\225\347\211\233\345\210\200\357\274\214\345\256\236\347\216\260\344\270\200\344\270\252\347\256\200\345\215\225\347\232\204Bean\345\256\271\345\231\250.md" +++ "b/docs/md/spring/develop-spring/2021-05-20-\347\254\2542\347\253\240\357\274\232\345\260\217\350\257\225\347\211\233\345\210\200\357\274\214\345\256\236\347\216\260\344\270\200\344\270\252\347\256\200\345\215\225\347\232\204Bean\345\256\271\345\231\250.md" @@ -187,3 +187,13 @@ Process finished with exit code 0 - 整篇关于 Spring Bean 容器的一个雏形就已经实现完成了,相对来说这部分代码并不会难住任何人,只要你稍加尝试就可以接受这部分内容的实现。 - 但对于一个知识的学习来说,写代码只是最后的步骤,往往整个思路、设计、方案,才更重要,只要你知道了因为什么、所以什么,才能让你有一个真正的理解。 - 下一章节会在此工程基础上扩容实现,要比现在的类多一些。不过每一篇的实现上,我都会以一个需求视角进行目标分析和方案设计,让大家在学习编码之外更能注重更多技术价值的学习。 + +## 七、优秀作业 + +- [模拟Spring,创建简单的Bean容器 @刘小白](https://t.zsxq.com/07Or409lv) +- [定义一个简单的 Spring 容器,用于定义、存放和获取 Bean 对象。@水中捞月](https://t.zsxq.com/08cTtlygk) +- [实现一个简单的 Spring 容器 @微风](https://t.zsxq.com/081RfLAlL) +- [单例的Bean注册表接口 - 主要是提供 获取单例Bean和注册单例对象方法 @刘溜溜](https://t.zsxq.com/08sg1lCFX) +- [创建一个简单的spring容器,需要实现bean的定义、注册、获取 @Doraemon](https://t.zsxq.com/09cnRyTu2) +- [将对象和代表对象的实例信息(BeanDefinition)进行区分 @Liuliuliu](https://t.zsxq.com/09mIgDsm0) +- [)修改beanDefinition中Object对象为Class对象,将创建对象工作交给容器实现 @小汤哥](https://t.zsxq.com/0a2mcHw7w) \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-05-23-\347\254\2543\347\253\240\357\274\232\345\210\235\346\230\276\350\272\253\346\211\213\357\274\214\350\277\220\347\224\250\350\256\276\350\256\241\346\250\241\345\274\217\357\274\214\345\256\236\347\216\260 Bean \347\232\204\345\256\232\344\271\211\343\200\201\346\263\250\345\206\214\343\200\201\350\216\267\345\217\226.md" "b/docs/md/spring/develop-spring/2021-05-23-\347\254\2543\347\253\240\357\274\232\345\210\235\346\230\276\350\272\253\346\211\213\357\274\214\350\277\220\347\224\250\350\256\276\350\256\241\346\250\241\345\274\217\357\274\214\345\256\236\347\216\260 Bean \347\232\204\345\256\232\344\271\211\343\200\201\346\263\250\345\206\214\343\200\201\350\216\267\345\217\226.md" index 550e63f9d..7d6ad2e06 100755 --- "a/docs/md/spring/develop-spring/2021-05-23-\347\254\2543\347\253\240\357\274\232\345\210\235\346\230\276\350\272\253\346\211\213\357\274\214\350\277\220\347\224\250\350\256\276\350\256\241\346\250\241\345\274\217\357\274\214\345\256\236\347\216\260 Bean \347\232\204\345\256\232\344\271\211\343\200\201\346\263\250\345\206\214\343\200\201\350\216\267\345\217\226.md" +++ "b/docs/md/spring/develop-spring/2021-05-23-\347\254\2543\347\253\240\357\274\232\345\210\235\346\230\276\350\272\253\346\211\213\357\274\214\350\277\220\347\224\250\350\256\276\350\256\241\346\250\241\345\274\217\357\274\214\345\256\236\347\216\260 Bean \347\232\204\345\256\232\344\271\211\343\200\201\346\263\250\345\206\214\343\200\201\350\216\267\345\217\226.md" @@ -121,7 +121,7 @@ public interface SingletonBeanRegistry { - 这个类比较简单主要是定义了一个获取单例对象的接口。 -**cn.bugstack.springframework.beans.factory.config.DefaultSingletonBeanRegistry** +**cn.bugstack.springframework.beans.factory.support.DefaultSingletonBeanRegistry** ```java public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry { @@ -287,3 +287,20 @@ Process finished with exit code 0 - 相对于前一章节对 Spring Bean 容器的简单概念实现,本章节中加强了功能的完善。在实现的过程中也可以看到类的关系变得越来越多了,如果没有做过一些稍微复杂的系统类系统,那么即使现在这样9个类搭出来的容器工厂也可以给你绕晕。 - 在 Spring Bean 容器的实现类中要重点关注类之间的职责和关系,几乎所有的程序功能设计都离不开接口、抽象类、实现、继承,而这些不同特性类的使用就可以非常好的隔离开类的功能职责和作用范围。而这样的知识点也是在学习手写 Spring Bean 容器框架过程非常重要的知识。 - 最后要强调一下关于整个系列内容的学习,可能在学习的过程中会遇到像第二章节那样非常简单的代码实现,但要做一个有成长的程序员要记住代码实现只是最后的落地结果,而那些设计上的思考才是最有价值的地方。*就像你是否遇到过,有人让你给一个内容做个描述、文档、说明,你总觉得太简单了没什么可写的,即使要动笔写了也不知道要从哪开始!其实这些知识内容都来源你对整体功能的理解,这就不只是代码开发还包括了需求目标、方案设计、技术实现、逻辑验证等等过程性的内容。所以,不要只是被看似简单的内容忽略了整体全局观,要学会放开视野,开放学习视角。* + +## 七、优秀作业 + +- [创建简单的Bean容器 @liuc](https://t.zsxq.com/06Q3V7mUR) +- [创建最基本的容器,并实现Bean的定义,注册以及使用 @Homage](https://t.zsxq.com/07Tyh8oBa) +- [手撸Spring第三章,实现 Bean 的定义、注册、获取 @傅哥后援会会长](https://t.zsxq.com/07DNokn9Z) +- [实现 Bean 的定义、注册、获取 @刘小白](https://t.zsxq.com/07gFfvoRj) +- [spring容器,满足bean注册和取出的一种数据结构(hashmap)@轻舟故人](https://t.zsxq.com/07dQcfDAo) +- [对于为什么会有某个类, 某个接口还是有点迷糊, 阅读了一下 Spring 关于这些类的注释 @小田要努力](https://t.zsxq.com/08hhLXE5u) +- [考虑单例,二次获取直接从内存获取 @水中捞月](https://t.zsxq.com/08vZHndzD) +- [基于Cglib实现构造函数的类实例化策略【上手是最快的学习方式】@微风](https://t.zsxq.com/08KzGmGk7) +- [学习了通过模板模式去定义Bean的注册与获取策略 @十里平湖霜满天](https://t.zsxq.com/08Cv9nNPD) +- [因第二章实现没考虑到有参数的构造方法,如带参则会报错。这章主要解决带参数的实例化对象创建 @刘溜溜](https://t.zsxq.com/08NJPZUbj) +- [增加了策略模式,在创建对象时候根据传入的参数选择构造函数默认使用Cglib的方式创建对象 @Liuliuliu](https://t.zsxq.com/09wzj469l) +- [结合工程结构和给出的类图进行梳理结构,然后根据结构顺序创建空白类 @在九月](https://t.zsxq.com/09pJ2M1SU) +- [Bean的定义信息,spring就是根据BeanDefiniton创建Bean对象 @止水](https://t.zsxq.com/09HXnxvOO) +- [实现的 BeanFactory 就是使用 ConcurrentHashMap 简单的注册 bean @李昌祥](https://t.zsxq.com/0a9YBNMl8) diff --git "a/docs/md/spring/develop-spring/2021-05-30-\347\254\2544\347\253\240\357\274\232\345\264\255\351\234\262\345\244\264\350\247\222\357\274\214\345\237\272\344\272\216Cglib\345\256\236\347\216\260\345\220\253\346\236\204\351\200\240\345\207\275\346\225\260\347\232\204\347\261\273\345\256\236\344\276\213\345\214\226\347\255\226\347\225\245.md" "b/docs/md/spring/develop-spring/2021-05-30-\347\254\2544\347\253\240\357\274\232\345\264\255\351\234\262\345\244\264\350\247\222\357\274\214\345\237\272\344\272\216Cglib\345\256\236\347\216\260\345\220\253\346\236\204\351\200\240\345\207\275\346\225\260\347\232\204\347\261\273\345\256\236\344\276\213\345\214\226\347\255\226\347\225\245.md" index a80a34f02..f850cdaf2 100755 --- "a/docs/md/spring/develop-spring/2021-05-30-\347\254\2544\347\253\240\357\274\232\345\264\255\351\234\262\345\244\264\350\247\222\357\274\214\345\237\272\344\272\216Cglib\345\256\236\347\216\260\345\220\253\346\236\204\351\200\240\345\207\275\346\225\260\347\232\204\347\261\273\345\256\236\344\276\213\345\214\226\347\255\226\347\225\245.md" +++ "b/docs/md/spring/develop-spring/2021-05-30-\347\254\2544\347\253\240\357\274\232\345\264\255\351\234\262\345\244\264\350\247\222\357\274\214\345\237\272\344\272\216Cglib\345\256\236\347\216\260\345\220\253\346\236\204\351\200\240\345\207\275\346\225\260\347\232\204\347\261\273\345\256\236\344\276\213\345\214\226\347\255\226\347\225\245.md" @@ -374,4 +374,12 @@ public void test_cglib() { ## 七、优秀作业 -- [第四章和第五章都是增加创建bean的流程学习和梳理图 By W](https://t.zsxq.com/05FAIUNvZ) \ No newline at end of file +- [第四章和第五章都是增加创建bean的流程学习和梳理图 @W](https://t.zsxq.com/05FAIUNvZ) +- [基于Cglib或JDK实现含构造函数的类的实例化 @liuc](https://t.zsxq.com/06ZbeMJay) +- [解决实例化含构造函数的对象的异常 @Homage](https://t.zsxq.com/07SVXPNwY) +- [实现含构造函数的类实例化策略 @傅哥后援会会长](https://t.zsxq.com/07wAiUBi6) +- [引入代理工具对Bean 对象在含有构造函数进行实例化进行进一步处理 @刘小白](https://t.zsxq.com/074qqLOfD) +- [优化对象有构造函数实例化时实例化失败的问题 @轻舟故人](https://t.zsxq.com/07D6zLFCg) +- [实例化一个含有构造函数的对象。@水中捞月](https://t.zsxq.com/08l43H6aW) +- [对于实例的Bean有属性和依赖对象需要注入进去 @刘溜溜](https://t.zsxq.com/098qbx2c1) +- [为了对含构造函数的Bean对象进行实例化,04章采取策略模式,通过Cglib和JDK自带两种方式动态代理对象 @在九月](https://t.zsxq.com/09R5XZ7PC) \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-06-02-\347\254\2545\347\253\240\357\274\232\344\270\200\351\270\243\346\203\212\344\272\272\357\274\214\344\270\272Bean\345\257\271\350\261\241\346\263\250\345\205\245\345\261\236\346\200\247\345\222\214\344\276\235\350\265\226Bean\347\232\204\345\212\237\350\203\275\345\256\236\347\216\260.md" "b/docs/md/spring/develop-spring/2021-06-02-\347\254\2545\347\253\240\357\274\232\344\270\200\351\270\243\346\203\212\344\272\272\357\274\214\344\270\272Bean\345\257\271\350\261\241\346\263\250\345\205\245\345\261\236\346\200\247\345\222\214\344\276\235\350\265\226Bean\347\232\204\345\212\237\350\203\275\345\256\236\347\216\260.md" index b82e60e7f..19cc79f07 100755 --- "a/docs/md/spring/develop-spring/2021-06-02-\347\254\2545\347\253\240\357\274\232\344\270\200\351\270\243\346\203\212\344\272\272\357\274\214\344\270\272Bean\345\257\271\350\261\241\346\263\250\345\205\245\345\261\236\346\200\247\345\222\214\344\276\235\350\265\226Bean\347\232\204\345\212\237\350\203\275\345\256\236\347\216\260.md" +++ "b/docs/md/spring/develop-spring/2021-06-02-\347\254\2545\347\253\240\357\274\232\344\270\200\351\270\243\346\203\212\344\272\272\357\274\214\344\270\272Bean\345\257\271\350\261\241\346\263\250\345\205\245\345\261\236\346\200\247\345\222\214\344\276\235\350\265\226Bean\347\232\204\345\212\237\350\203\275\345\256\236\347\216\260.md" @@ -341,4 +341,15 @@ Process finished with exit code 0 ## 七、优秀作业 -- [学习笔记 By Chin](https://t.zsxq.com/05AiaUJuV) \ No newline at end of file +- [学习笔记 By Chin](https://t.zsxq.com/05AiaUJuV) +- [给bean注入属性和依赖对象(例:给UserService注入uId和UserDao)@liuc](https://t.zsxq.com/06nUNZJIA) +- [对spring的2-4章节进行总体的梳理(工厂方法模式,模板模式,策略模式)@喂橙子](https://t.zsxq.com/07Nsp9m64) +- [属性填充的时机(要在类实例化创建之后)@刘小白](https://t.zsxq.com/07IAklHLs) +- [主要目标就是解决属性填充问题,具体来说又细分为(属性相关类的设计、填充属性的时机) @Homage](https://t.zsxq.com/07WTcflJE) +- [注入属性和依赖对象 @傅哥后援会会长](https://t.zsxq.com/073LNvrkV) +- [获取bean后,增加填充属性流程 @mkii](https://t.zsxq.com/08x3zDtYQ) +- [spring框架还不能完成实现对象属性值的填充 @考小拉คิดถึง](https://t.zsxq.com/08fb1tWcp) +- [循环获取创建bean对象BeanReference(类引用类) @小汤哥](https://t.zsxq.com/0akd1q9b6) +- [资源加载器解析文件注册对象 @李昌祥](https://t.zsxq.com/0awh9UUvp) +- [实现应用上下文,自动识别,资源加载 @爱奋斗的小鲨鱼](https://t.zsxq.com/0b1v9zM2X) +- [区分Bean工厂中两个对象:BeanDefinition和Object,其中BeanDefinition成员对象中保存的是class类 @L.ast](https://t.zsxq.com/0cpTuoSNs) \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-06-09-\347\254\2546\347\253\240\357\274\232\346\260\224\345\220\236\345\261\261\346\262\263\357\274\214\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\350\265\204\346\272\220\345\212\240\350\275\275\345\231\250\357\274\214\344\273\216Spring.xml\350\247\243\346\236\220\345\222\214\346\263\250\345\206\214Bean\345\257\271\350\261\241.md" "b/docs/md/spring/develop-spring/2021-06-09-\347\254\2546\347\253\240\357\274\232\346\260\224\345\220\236\345\261\261\346\262\263\357\274\214\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\350\265\204\346\272\220\345\212\240\350\275\275\345\231\250\357\274\214\344\273\216Spring.xml\350\247\243\346\236\220\345\222\214\346\263\250\345\206\214Bean\345\257\271\350\261\241.md" index 66c2620b1..f2993d01e 100755 --- "a/docs/md/spring/develop-spring/2021-06-09-\347\254\2546\347\253\240\357\274\232\346\260\224\345\220\236\345\261\261\346\262\263\357\274\214\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\350\265\204\346\272\220\345\212\240\350\275\275\345\231\250\357\274\214\344\273\216Spring.xml\350\247\243\346\236\220\345\222\214\346\263\250\345\206\214Bean\345\257\271\350\261\241.md" +++ "b/docs/md/spring/develop-spring/2021-06-09-\347\254\2546\347\253\240\357\274\232\346\260\224\345\220\236\345\261\261\346\262\263\357\274\214\350\256\276\350\256\241\344\270\216\345\256\236\347\216\260\350\265\204\346\272\220\345\212\240\350\275\275\345\231\250\357\274\214\344\273\216Spring.xml\350\247\243\346\236\220\345\222\214\346\263\250\345\206\214Bean\345\257\271\350\261\241.md" @@ -601,4 +601,13 @@ Process finished with exit code 0 ## 七、优秀作业 - [关于BeanFactory这里多了很多接口,感觉马上复杂度要飙升! @W](https://t.zsxq.com/05e2z7uNV) -- [资源注入流程学习 @Chin](https://t.zsxq.com/05FeqVJiy) \ No newline at end of file +- [资源注入流程学习 @Chin](https://t.zsxq.com/05FeqVJiy) +- [在上面的章节的测试类中,Bean的注册、属性绑定都是通过代码的方式完成的 @刘小白](https://t.zsxq.com/07u3XPzSY) +- [从Spring.xml解析和注册Bean对象 @傅哥后援会会长](https://t.zsxq.com/07vGffvF9) +- [接口和对应相关类的增加了不少,理清关系有难度,理解设计思想更有难度 @Homage](https://t.zsxq.com/07KONGKAH) +- [对 Spring 的配置文件解析注册Bean,Spring 应用上下文 @微风](https://t.zsxq.com/08AepNIhn) +- [实现应用上下文,感觉本章才是Spring基本框架搭建好的一章 @Liuliuliu](https://t.zsxq.com/098CtQxzE) +- [实现从Spring.xml解析和注册Bean对象 @在九月](https://t.zsxq.com/095PDEcLv) +- [通过 DefaultResourceLoader来使用不同的解析方式解析资源 @小汤哥](https://t.zsxq.com/0aeHfY69U) +- [实现资源加载类和实现资源解析和注册 @Keendy](https://t.zsxq.com/0bHOnbjl9) +- [实现 Bean 初始化以及销毁,虚拟机注册钩子 @爱奋斗的小鲨鱼](https://t.zsxq.com/0bPFcugeY) \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-06-17-\347\254\2547\347\253\240\357\274\232\346\211\200\345\220\221\346\212\253\351\235\241\357\274\214\345\256\236\347\216\260\345\272\224\347\224\250\344\270\212\344\270\213\346\226\207\357\274\214\350\207\252\345\212\250\350\257\206\345\210\253\343\200\201\350\265\204\346\272\220\345\212\240\350\275\275\343\200\201\346\211\251\345\261\225\346\234\272\345\210\266.md" "b/docs/md/spring/develop-spring/2021-06-17-\347\254\2547\347\253\240\357\274\232\346\211\200\345\220\221\346\212\253\351\235\241\357\274\214\345\256\236\347\216\260\345\272\224\347\224\250\344\270\212\344\270\213\346\226\207\357\274\214\350\207\252\345\212\250\350\257\206\345\210\253\343\200\201\350\265\204\346\272\220\345\212\240\350\275\275\343\200\201\346\211\251\345\261\225\346\234\272\345\210\266.md" index 2d0068fee..f1807efec 100755 --- "a/docs/md/spring/develop-spring/2021-06-17-\347\254\2547\347\253\240\357\274\232\346\211\200\345\220\221\346\212\253\351\235\241\357\274\214\345\256\236\347\216\260\345\272\224\347\224\250\344\270\212\344\270\213\346\226\207\357\274\214\350\207\252\345\212\250\350\257\206\345\210\253\343\200\201\350\265\204\346\272\220\345\212\240\350\275\275\343\200\201\346\211\251\345\261\225\346\234\272\345\210\266.md" +++ "b/docs/md/spring/develop-spring/2021-06-17-\347\254\2547\347\253\240\357\274\232\346\211\200\345\220\221\346\212\253\351\235\241\357\274\214\345\256\236\347\216\260\345\272\224\347\224\250\344\270\212\344\270\213\346\226\207\357\274\214\350\207\252\345\212\250\350\257\206\345\210\253\343\200\201\350\265\204\346\272\220\345\212\240\350\275\275\343\200\201\346\211\251\345\261\225\346\234\272\345\210\266.md" @@ -47,7 +47,7 @@ lock: need ![](https://bugstack.cn/assets/images/spring/spring-7-02.png) -- 满足于对 Bean 对象扩展的两个接口,其实也是 Spring 框架中非常具有重量级的两个接口:`BeanFactoryPostProcess` 和 `BeanPostProcessor`,也几乎是大家在使用 Spring 框架额外新增开发自己组建需求的两个必备接口。 +- 满足于对 Bean 对象扩展的两个接口,其实也是 Spring 框架中非常具有重量级的两个接口:`BeanFactoryPostProcessor` 和 `BeanPostProcessor`,也几乎是大家在使用 Spring 框架额外新增开发自己组建需求的两个必备接口。 - BeanFactoryPostProcessor,是由 Spring 框架组建提供的容器扩展机制,允许在 Bean 对象注册后但未实例化之前,对 Bean 的定义信息 `BeanDefinition` 执行修改操作。 - BeanPostProcessor,也是 Spring 提供的扩展机制,不过 BeanPostProcessor 是在 Bean 对象实例化之后修改 Bean 对象,也可以替换 Bean 对象。这部分与后面要实现的 AOP 有着密切的关系。 - 同时如果只是添加这两个接口,不做任何包装,那么对于使用者来说还是非常麻烦的。我们希望于开发 Spring 的上下文操作类,把相应的 XML 加载 、注册、实例化以及新增的修改和扩展都融合进去,让 Spring 可以自动扫描到我们的新增服务,便于用户使用。 @@ -656,4 +656,14 @@ Process finished with exit code 0 ## 七、优秀作业 - [应用上下文全貌图 @W](https://t.zsxq.com/0537UNFuj) -- [学这章之前我对上下文一点都不知道,学完之后感觉豁然开朗,BeanFactory是内部的东西是IOC容器化的基础,而这些是要给人用的 @Chin](https://t.zsxq.com/053vN3r3V) \ No newline at end of file +- [学这章之前我对上下文一点都不知道,学完之后感觉豁然开朗,BeanFactory是内部的东西是IOC容器化的基础,而这些是要给人用的 @Chin](https://t.zsxq.com/053vN3r3V) +- [创建上下文,不得不说 Spring 的类的设计和设计模式的使用都是教科书级别的 @Weido](https://t.zsxq.com/06VJ6YNJU) +- [实现应用上下文,把对Class类、Bean对象的扩展机制功能和对Spring框架上下文的包装融合起来,对外提供统一完整的服务 @liuc](https://t.zsxq.com/07EMFubmA) +- [本节解密了ClassPathXmlApplicationContext @赛博丁真](https://t.zsxq.com/07FcGEAPe) +- [需要理解上下文refresh的流程、BeanDefinition修改时机、Bean实例修改时机 @傅哥后援会会长](https://t.zsxq.com/073XQ9isg) +- [Situation 本章将继续针对测试用例中的BeanFactory加载spring.xml这个地方来做进一步的整合。@刘小白](https://t.zsxq.com/08WokFbXV) +- [基于 Spring 的组件设计时,需要继承或实现 Spring 对外暴露的类或接口 @水中捞月](https://t.zsxq.com/082fgaWPw) +- [在检测是否配置了init-method方法,如果进行了配置进行处理进行后置处理 @Liuliuliu](https://t.zsxq.com/09P5Yiykq) +- [看见ApplicationContext时引起了我的高度重视,因为此前在spring中和其打了太多交道 @在九月](https://t.zsxq.com/09GICoLkC) +- [在执行完注册 BeanDefinition 后,执行此接口的实现类对象的方法 @mkii](https://t.zsxq.com/0aVHv70Z2) +- [ApplicationContext 接口满足自动识别、资源加载、容器事件、监听器等功能 @小汤哥](https://t.zsxq.com/0a32WbY7N) \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-06-23-\347\254\2548\347\253\240\357\274\232\351\276\231\350\241\214\346\234\211\351\243\216\357\274\214\345\220\221\350\231\232\346\213\237\346\234\272\346\263\250\345\206\214\351\222\251\345\255\220\357\274\214\345\256\236\347\216\260Bean\345\257\271\350\261\241\347\232\204\345\210\235\345\247\213\345\214\226\345\222\214\351\224\200\346\257\201\346\226\271\346\263\225.md" "b/docs/md/spring/develop-spring/2021-06-23-\347\254\2548\347\253\240\357\274\232\351\276\231\350\241\214\346\234\211\351\243\216\357\274\214\345\220\221\350\231\232\346\213\237\346\234\272\346\263\250\345\206\214\351\222\251\345\255\220\357\274\214\345\256\236\347\216\260Bean\345\257\271\350\261\241\347\232\204\345\210\235\345\247\213\345\214\226\345\222\214\351\224\200\346\257\201\346\226\271\346\263\225.md" index 1ab7bd600..6712ca65e 100755 --- "a/docs/md/spring/develop-spring/2021-06-23-\347\254\2548\347\253\240\357\274\232\351\276\231\350\241\214\346\234\211\351\243\216\357\274\214\345\220\221\350\231\232\346\213\237\346\234\272\346\263\250\345\206\214\351\222\251\345\255\220\357\274\214\345\256\236\347\216\260Bean\345\257\271\350\261\241\347\232\204\345\210\235\345\247\213\345\214\226\345\222\214\351\224\200\346\257\201\346\226\271\346\263\225.md" +++ "b/docs/md/spring/develop-spring/2021-06-23-\347\254\2548\347\253\240\357\274\232\351\276\231\350\241\214\346\234\211\351\243\216\357\274\214\345\220\221\350\231\232\346\213\237\346\234\272\346\263\250\345\206\214\351\222\251\345\255\220\357\274\214\345\256\236\347\216\260Bean\345\257\271\350\261\241\347\232\204\345\210\235\345\247\213\345\214\226\345\222\214\351\224\200\346\257\201\346\226\271\346\263\225.md" @@ -570,4 +570,13 @@ Process finished with exit code 0 ## 七、优秀作业 - [InitializingBean和BeanPostProcessor 作用域梳理 @W](https://t.zsxq.com/05uFuFyZv) -- [之前特地空了一个初始化方法出来,这章就是来实现这个功能以及销毁功能 @Chin](https://t.zsxq.com/05MFeyNnq) \ No newline at end of file +- [之前特地空了一个初始化方法出来,这章就是来实现这个功能以及销毁功能 @Chin](https://t.zsxq.com/05MFeyNnq) +- [定义、执行初始化和销毁方法 @liuc](https://t.zsxq.com/07fUsGksF) +- [spring第8节,对spring越来越清晰了 @赛博丁真](https://t.zsxq.com/07TERLkE7) +- [在Shutdown钩子触发时,触发close方法,遍历DisposableBean Map,执行destory方法。 @傅哥后援会会长](https://t.zsxq.com/07bvUo57L) +- [实现 Bean 的初始化,可以用来提前加载数据等。@Homage](https://t.zsxq.com/08RcFtX8f) +- [感知对象容器笔记 @傅哥后援会会长](https://t.zsxq.com/08la8B8lf) +- [在 Bean 初始化时,需要执行一些如数据加载、连接注册中心暴露 RPC 解耦等操作 @水中捞月](https://t.zsxq.com/08ntSyZsk) +- [在实例化对象完成,属性填充后,对象的前置处理之前进行感知容器判断 @Liuliuliu](https://t.zsxq.com/09ALmVSwy) +- [本章节主要是进一步扩展对Bean的操作,主要是提供Bean初始化和销毁的方法 @在九月](https://t.zsxq.com/09ZM4qr33) +- [补充bean的生命周期处理 @止水](https://t.zsxq.com/0a7g0rNuW) \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-06-28-\347\254\2549\347\253\240\357\274\232\350\231\216\350\241\214\346\234\211\351\233\250\357\274\214\345\256\232\344\271\211\346\240\207\350\256\260\347\261\273\345\236\213Aware\346\216\245\345\217\243\357\274\214\345\256\236\347\216\260\346\204\237\347\237\245\345\256\271\345\231\250\345\257\271\350\261\241.md" "b/docs/md/spring/develop-spring/2021-06-28-\347\254\2549\347\253\240\357\274\232\350\231\216\350\241\214\346\234\211\351\233\250\357\274\214\345\256\232\344\271\211\346\240\207\350\256\260\347\261\273\345\236\213Aware\346\216\245\345\217\243\357\274\214\345\256\236\347\216\260\346\204\237\347\237\245\345\256\271\345\231\250\345\257\271\350\261\241.md" index c2f479d6c..fc635d207 100755 --- "a/docs/md/spring/develop-spring/2021-06-28-\347\254\2549\347\253\240\357\274\232\350\231\216\350\241\214\346\234\211\351\233\250\357\274\214\345\256\232\344\271\211\346\240\207\350\256\260\347\261\273\345\236\213Aware\346\216\245\345\217\243\357\274\214\345\256\236\347\216\260\346\204\237\347\237\245\345\256\271\345\231\250\345\257\271\350\261\241.md" +++ "b/docs/md/spring/develop-spring/2021-06-28-\347\254\2549\347\253\240\357\274\232\350\231\216\350\241\214\346\234\211\351\233\250\357\274\214\345\256\232\344\271\211\346\240\207\350\256\260\347\261\273\345\236\213Aware\346\216\245\345\217\243\357\274\214\345\256\236\347\216\260\346\204\237\347\237\245\345\256\271\345\231\250\345\257\271\350\261\241.md" @@ -528,4 +528,12 @@ Process finished with exit code 0 ## 七、优秀作业 -- [Aware感知容器对象,实现自定义扩展框架 By Ray](https://t.zsxq.com/05vZ76aYr) \ No newline at end of file +- [Aware感知容器对象,实现自定义扩展框架 @Ray](https://t.zsxq.com/05vZ76aYr) +- [Aware感知容器对象 @liuc](https://t.zsxq.com/07D3i4lRJ) +- [xml解析时新增scope属性,用来注明该BeanDefinitions是否为单例。若为原型模式,则不放入Bean Map缓存。@傅哥后援会会长](https://t.zsxq.com/08DptauLn) +- [实现 Bean 的感知功能,即可以访问Spring容器并获取对应的属性。@Homage](https://t.zsxq.com/087b5hc0O) +- [向虚拟机注册钩子方法在虚拟机退出前做一些操作(如监控服务器宕机,备机启动等)@微风](https://t.zsxq.com/082KHHaer) +- [对象作用域,增加了scop的注入 通过scop的值判断是否将对象注入进单例的集合中 @Liuliuliu](https://t.zsxq.com/091KGjqpP) +- [了解了Aware接口的核心功能与大致的实现方式 @在九月](https://t.zsxq.com/09DZdKCfV) +- [当使用第三方框架或者库时,有时候是无法去 new 一个对象的 @lucien](https://t.zsxq.com/0bTXvgV9i) +- [Spring 依赖注入的最大特点就是所有Bean 对象 @爱奋斗的小鲨鱼](https://t.zsxq.com/0bDzEXoMY) diff --git "a/docs/md/spring/develop-spring/2021-06-30-\347\254\25410\347\253\240\357\274\232\346\250\252\345\210\200\350\267\203\351\251\254\357\274\214\345\205\263\344\272\216Bean\345\257\271\350\261\241\344\275\234\347\224\250\345\237\237\344\273\245\345\217\212FactoryBean\347\232\204\345\256\236\347\216\260\345\222\214\344\275\277\347\224\250.md" "b/docs/md/spring/develop-spring/2021-06-30-\347\254\25410\347\253\240\357\274\232\346\250\252\345\210\200\350\267\203\351\251\254\357\274\214\345\205\263\344\272\216Bean\345\257\271\350\261\241\344\275\234\347\224\250\345\237\237\344\273\245\345\217\212FactoryBean\347\232\204\345\256\236\347\216\260\345\222\214\344\275\277\347\224\250.md" index 4a06e0c27..6113d86d4 100755 --- "a/docs/md/spring/develop-spring/2021-06-30-\347\254\25410\347\253\240\357\274\232\346\250\252\345\210\200\350\267\203\351\251\254\357\274\214\345\205\263\344\272\216Bean\345\257\271\350\261\241\344\275\234\347\224\250\345\237\237\344\273\245\345\217\212FactoryBean\347\232\204\345\256\236\347\216\260\345\222\214\344\275\277\347\224\250.md" +++ "b/docs/md/spring/develop-spring/2021-06-30-\347\254\25410\347\253\240\357\274\232\346\250\252\345\210\200\350\267\203\351\251\254\357\274\214\345\205\263\344\272\216Bean\345\257\271\350\261\241\344\275\234\347\224\250\345\237\237\344\273\245\345\217\212FactoryBean\347\232\204\345\256\236\347\216\260\345\222\214\344\275\277\347\224\250.md" @@ -552,7 +552,7 @@ Process finished with exit code 0 ``` - 从测试结果来看,我们的代理类 ProxyBeanFactory 已经完美替换掉了 UserDao 的功能。 -- 虽然看上去这一点实现并不复杂,甚至有点简单。但就是这样一点点核心内容的设计了,解决了所有需要和 Spring 结合的其他框架交互链接问题。*如果对此类内容感兴趣,也可以阅读小傅哥[《中间件设计和开发》](https://bugstack.cn/itstack-ark-middleware/2021/03/31/SpringBoot-%E4%B8%AD%E9%97%B4%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%BC%80%E5%8F%91-%E4%B8%93%E6%A0%8F%E5%B0%8F%E5%86%8C%E4%B8%8A%E7%BA%BF%E5%95%A6.html)* +- 虽然看上去这一点实现并不复杂,甚至有点简单。但就是这样一点点核心内容的设计了,解决了所有需要和 Spring 结合的其他框架交互链接问题。*如果对此类内容感兴趣,也可以阅读小傅哥[《中间件设计和开发》](https://bugstack.cn/md/assembly/middleware/2021-03-31-%E3%80%8ASpringBoot%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%BC%80%E5%8F%91%E3%80%8B%E4%B8%93%E6%A0%8F%E5%B0%8F%E5%86%8C%E4%B8%8A%E7%BA%BF%E5%95%A6%EF%BC%81.html)* ## 六、总结 @@ -564,4 +564,11 @@ Process finished with exit code 0 - [单例判断以及通过用户创建的FactoryBean实现复杂Bean对象的创建 @Ray](https://t.zsxq.com/057IyZj2r) - [Bean 对象作用域范围全貌梳理 @W](https://t.zsxq.com/05eiIMFMj) -- [另开一中额外的prototype类型的bean,并进行特殊处理 @Chin](https://t.zsxq.com/05MFubAA6) \ No newline at end of file +- [另开一中额外的prototype类型的bean,并进行特殊处理 @Chin](https://t.zsxq.com/05MFubAA6) +- [起初对于 FactoryBean 模块其实理解的不是很充分,联想到 mybatis 的使用确实稍微能 get 到其设计的意义所在 @Weirdo](https://t.zsxq.com/067ufmeaE) +- [对象作用域和BeanFactory @liuc](https://t.zsxq.com/07cc83Mh2) +- [AOP(面向切面编程)是指通过预编译的方式和运行期间动态代理实现程序功能的统一维护 @liuc](https://t.zsxq.com/0828L68ac) +- [不仅仅只由 Spring 本身类创建 Bean,仿照 Mybatis 框架,通过接口定义 @水中捞月](https://t.zsxq.com/08Hv720j8) +- [ApplicationEventMulticaster关键类,主要是通过该类发布的事件 @Liuliuliu](https://t.zsxq.com/09Mibk2Uj) +- [事件监听器,照字面意思,这个是一个机制 @lucien](https://t.zsxq.com/0buWRXssk) +- [Spring IoC 容器通过反射或字节码增强的方式实例化 Bean @爱奋斗的小鲨鱼](https://t.zsxq.com/0blxKjTp3) diff --git "a/docs/md/spring/develop-spring/2021-07-07-\347\254\25411\347\253\240\357\274\232\346\233\264\344\270\212\345\261\202\346\245\274\357\274\214\345\237\272\344\272\216\350\247\202\345\257\237\350\200\205\345\256\236\347\216\260\357\274\214\345\256\271\345\231\250\344\272\213\344\273\266\345\222\214\344\272\213\344\273\266\347\233\221\345\220\254\345\231\250.md" "b/docs/md/spring/develop-spring/2021-07-07-\347\254\25411\347\253\240\357\274\232\346\233\264\344\270\212\345\261\202\346\245\274\357\274\214\345\237\272\344\272\216\350\247\202\345\257\237\350\200\205\345\256\236\347\216\260\357\274\214\345\256\271\345\231\250\344\272\213\344\273\266\345\222\214\344\272\213\344\273\266\347\233\221\345\220\254\345\231\250.md" index 8ab0bda7c..726a6ddb4 100755 --- "a/docs/md/spring/develop-spring/2021-07-07-\347\254\25411\347\253\240\357\274\232\346\233\264\344\270\212\345\261\202\346\245\274\357\274\214\345\237\272\344\272\216\350\247\202\345\257\237\350\200\205\345\256\236\347\216\260\357\274\214\345\256\271\345\231\250\344\272\213\344\273\266\345\222\214\344\272\213\344\273\266\347\233\221\345\220\254\345\231\250.md" +++ "b/docs/md/spring/develop-spring/2021-07-07-\347\254\25411\347\253\240\357\274\232\346\233\264\344\270\212\345\261\202\346\245\274\357\274\214\345\237\272\344\272\216\350\247\202\345\257\237\350\200\205\345\256\236\347\216\260\357\274\214\345\256\271\345\231\250\344\272\213\344\273\266\345\222\214\344\272\213\344\273\266\347\233\221\345\220\254\345\231\250.md" @@ -227,7 +227,7 @@ public class ContextRefreshedEvent extends ApplicationContextEvent{ } ``` -- ApplicationContextEvent 是定义事件的抽象类,所有的事件包括关闭、刷新,以及用户自己实现的事件,都需要继承这个类。 +- ApplicationEvent 是定义事件的抽象类,所有的事件包括关闭、刷新,以及用户自己实现的事件,都需要继承这个类。 - ContextClosedEvent、ContextRefreshedEvent,分别是 Spring 框架自己实现的两个事件类,可以用于监听刷新和关闭动作。 ### 3. 事件广播器 @@ -403,7 +403,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader i } } -``` +``` - 在抽象应用上下文 AbstractApplicationContext#refresh 中,主要新增了 `初始化事件发布者`、`注册事件监听器`、`发布容器刷新完成事件`,三个方法用于处理事件操作。 - 初始化事件发布者(initApplicationEventMulticaster),主要用于实例化一个 SimpleApplicationEventMulticaster,这是一个事件广播器。 @@ -437,7 +437,7 @@ public class CustomEvent extends ApplicationContextEvent { // ...get/set } -``` +``` - 创建一个自定义事件,在事件类的构造函数中可以添加自己的想要的入参信息。这个事件类最终会被完成的拿到监听里,所以你添加的属性都会被获得到。 @@ -453,7 +453,7 @@ public class CustomEventListener implements ApplicationListener { } } -``` +``` - 这个是一个用于监听 CustomEvent 事件的监听器,这里你可以处理自己想要的操作,比如一些用户注册后发送优惠券和短信通知等。 - 另外是关于 `ContextRefreshedEventListener implements ApplicationListener`、`ContextClosedEventListener implements ApplicationListener` 监听器,这里就不演示了,可以参考下源码。 @@ -490,7 +490,7 @@ public class ApiTest { } } -``` +``` - 通过使用 applicationContext 新增加的发布事件接口方法,发布一个自定义事件 CustomEvent,并透传了相应的参数信息。 @@ -503,10 +503,10 @@ public class ApiTest { 关闭事件:cn.bugstack.springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$f4d4b18d Process finished with exit code 0 -``` +``` - 从测试结果可以看到,我们自己定义的事件和监听,以及监听系统的事件信息,都可以在控制台完整的输出出来了。你也可以尝试增加一些其他事件行为,并调试代码学习观察者模式。 - + ## 六、总结 - 在整个手写 Spring 框架的学习过程中,可以逐步看到很多设计模式的使用,比如:简单工厂BeanFactory、工厂方法FactoryBean、策略模式访问资源,现在又实现了一个观察者模式的具体使用。所以学习 Spring 的过程中,要更加注意关于设计模式的运用,这是你能读懂代码的核心也是学习的重点。 @@ -519,3 +519,9 @@ Process finished with exit code 0 - [SpringIOC XML复习 By 编程小菜鸡](https://t.zsxq.com/05zVRjeea) - [当容器内部完成一些操作时,比如说容器完成刷新后需要扩展一些新的功能,并没有留给开发者更多的扩展接口](https://t.zsxq.com/063BiaYNb) - [通过这一章我也回顾了一下前面的知识,发现前面的知识是可以串起来的](https://t.zsxq.com/06fq7QN7u) +- [容器事件和事件监听器 @liuc](https://t.zsxq.com/078oU4547) +- [基于观察者模式,实现 Spring 中的事件机制 @Homage](https://t.zsxq.com/08cC5bszS) +- [通过观察者模式的方式,设计和实现 Spring Event 的容器事件和事件监听器功能 @水中捞月](https://t.zsxq.com/084TO0b4V) +- [手撸Spring之IOC部分流程梳理 @CCAT](https://t.zsxq.com/09QUq9PDs) +- [在ico剩下的两章中我们来做对象作用域的处理,工厂bean,和spring事件功能的实现 @止水]([止水](https://t.zsxq.com/0aprAozce)) +- [AOP,首先弄明白是什么意思,aspect oriented programming @lucien](https://t.zsxq.com/0bi3RPi1V) diff --git "a/docs/md/spring/develop-spring/2021-07-13-\347\254\25412\347\253\240\357\274\232\347\202\211\347\201\253\347\272\257\351\235\222\357\274\214\345\237\272\344\272\216JDK\345\222\214Cglib\345\212\250\346\200\201\344\273\243\347\220\206\357\274\214\345\256\236\347\216\260AOP\346\240\270\345\277\203\345\212\237\350\203\275.md" "b/docs/md/spring/develop-spring/2021-07-13-\347\254\25412\347\253\240\357\274\232\347\202\211\347\201\253\347\272\257\351\235\222\357\274\214\345\237\272\344\272\216JDK\345\222\214Cglib\345\212\250\346\200\201\344\273\243\347\220\206\357\274\214\345\256\236\347\216\260AOP\346\240\270\345\277\203\345\212\237\350\203\275.md" index d6d3948fa..3a09bad9b 100755 --- "a/docs/md/spring/develop-spring/2021-07-13-\347\254\25412\347\253\240\357\274\232\347\202\211\347\201\253\347\272\257\351\235\222\357\274\214\345\237\272\344\272\216JDK\345\222\214Cglib\345\212\250\346\200\201\344\273\243\347\220\206\357\274\214\345\256\236\347\216\260AOP\346\240\270\345\277\203\345\212\237\350\203\275.md" +++ "b/docs/md/spring/develop-spring/2021-07-13-\347\254\25412\347\253\240\357\274\232\347\202\211\347\201\253\347\272\257\351\235\222\357\274\214\345\237\272\344\272\216JDK\345\222\214Cglib\345\212\250\346\200\201\344\273\243\347\220\206\357\274\214\345\256\236\347\216\260AOP\346\240\270\345\277\203\345\212\237\350\203\275.md" @@ -294,6 +294,8 @@ public interface MethodMatcher { **实现切点表达式类** +**cn.bugstack.springframework.aop.aspectj.AspectJExpressionPointcut** + ```java public class AspectJExpressionPointcut implements Pointcut, ClassFilter, MethodMatcher { @@ -585,4 +587,8 @@ Process finished with exit code 0 ## 七、优秀作业 -- [jdk和cglib动态代理](https://t.zsxq.com/06feIU3fe) \ No newline at end of file +- [jdk和cglib动态代理 @Chin](https://t.zsxq.com/06feIU3fe) +- [在创建对象之前,需要前判断对象是否需要代理 @Liuliuliu](https://t.zsxq.com/09CbW8E6P) +- [前期在Spring当中涉及到了JDK和Cglib的动态代理,今天详细地进行了一个学习 @在九月](https://t.zsxq.com/0aPKwaowf) +- [把 AOP 扩展到 Bean 的生命周期里 @lucien](https://t.zsxq.com/0berIUtme) +- [AOP 是什么?什么是动态代理、静态代理? @爱奋斗的小鲨鱼](https://t.zsxq.com/0bkVGZTiH) diff --git "a/docs/md/spring/develop-spring/2021-07-22-\347\254\25413\347\253\240\357\274\232\350\241\214\344\272\221\346\265\201\346\260\264\357\274\214\346\212\212AOP\345\212\250\346\200\201\344\273\243\347\220\206\357\274\214\350\236\215\345\205\245\345\210\260Bean\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237.md" "b/docs/md/spring/develop-spring/2021-07-22-\347\254\25413\347\253\240\357\274\232\350\241\214\344\272\221\346\265\201\346\260\264\357\274\214\346\212\212AOP\345\212\250\346\200\201\344\273\243\347\220\206\357\274\214\350\236\215\345\205\245\345\210\260Bean\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237.md" index 67ba5f8f0..ce356bf63 100755 --- "a/docs/md/spring/develop-spring/2021-07-22-\347\254\25413\347\253\240\357\274\232\350\241\214\344\272\221\346\265\201\346\260\264\357\274\214\346\212\212AOP\345\212\250\346\200\201\344\273\243\347\220\206\357\274\214\350\236\215\345\205\245\345\210\260Bean\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237.md" +++ "b/docs/md/spring/develop-spring/2021-07-22-\347\254\25413\347\253\240\357\274\232\350\241\214\344\272\221\346\265\201\346\260\264\357\274\214\346\212\212AOP\345\212\250\346\200\201\344\273\243\347\220\206\357\274\214\350\236\215\345\205\245\345\210\260Bean\347\232\204\347\224\237\345\221\275\345\221\250\346\234\237.md" @@ -64,7 +64,7 @@ small-spring-step-12 │ │ │ ├── adapter │ │ │ │ └── MethodBeforeAdviceInterceptor.java │ │ │ ├── autoproxy - │ │ │ │ └── MethodBeforeAdviceInterceptor.java + │ │ │ │ └── DefaultAdvisorAutoProxyCreator.java │ │ │ ├── AopProxy.java │ │ │ ├── Cglib2AopProxy.java │ │ │ ├── JdkDynamicAopProxy.java @@ -286,6 +286,14 @@ public class MethodBeforeAdviceInterceptor implements MethodInterceptor { this.advice = advice; } + public MethodBeforeAdvice getAdvice() { + return advice; + } + + public void setAdvice(MethodBeforeAdvice advice) { + this.advice = advice; + } + @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { this.advice.before(methodInvocation.getMethod(), methodInvocation.getArguments(), methodInvocation.getThis()); @@ -380,6 +388,67 @@ public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPos - 获取了 advisors 以后就可以遍历相应的 AspectJExpressionPointcutAdvisor 填充对应的属性信息,包括:目标对象、拦截方法、匹配器,之后返回代理对象即可。 - 那么现在调用方获取到的这个 Bean 对象就是一个已经被切面注入的对象了,当调用方法的时候,则会被按需拦截,处理用户需要的信息。 +### 7. 融入到Bean的生命周期 + +**cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory** + +```java +public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { + + private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy(); + + @Override + protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException { + Object bean = null; + try { + // 判断是否返回代理 Bean 对象 + bean = resolveBeforeInstantiation(beanName, beanDefinition); + if (null != bean) { + return bean; + } + + bean = createBeanInstance(beanDefinition, beanName, args); + // 给 bean 填充属性 + applyPropertyValues(beanName, bean, beanDefinition); + // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法 + bean = initializeBean(beanName, bean, beanDefinition); + } catch (Exception e) { + throw new BeansException("Instantiation of bean failed.", e); + } + + // 注册实现了 DisposableBean 接口的 Bean 对象 + registerDisposableBeanIfNecessary(beanName, bean, beanDefinition); + + // 判断 SCOPE_SINGLETON,SCOPE_PROTOTYPE + if (beanDefinition.isSingleton()) { + registerSingleton(beanName, bean); + } + return bean; + } + + protected Object resolveBeforeInstantiation(String beanName, BeanDefinition beanDefinition) { + Object bean = applyBeanPostProcessorBeforeInstantiation(beanDefinition.getBeanClass(), beanName); + if (null != bean) { + bean = applyBeanPostProcessorAfterInitialization(bean, beanName); + } + return bean; + } + + // 注意,此方法为新增方法,与 “applyBeanPostProcessorBeforeInitialization” 是两个方法 + public Object applyBeanPostProcessorBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + for (BeanPostProcessor processor : getBeanPostProcessors()) { + if (processor instanceof InstantiationAwareBeanPostProcessor) { + Object result = ((InstantiationAwareBeanPostProcessor)processor).postProcessBeforeInstantiation(beanClass, beanName); + if (null != result) return result; + } + } + return null; + } +} +``` + +- 因为创建的是代理对象不是之前流程里的普通对象,所以我们需要前置于其他对象的创建,即需要在 AbstractAutowireCapableBeanFactory#createBean 优先完成 Bean 对象的判断,是否需要代理,有则直接返回代理对象。 + ## 五、测试 ### 1. 事先准备 @@ -485,4 +554,9 @@ Process finished with exit code 0 ## 七、优秀作业 - [定义 Advice通知 拦截器链 @Ray](https://t.zsxq.com/05B2vRvzr) -- [已经通过一些包装类将切点信息进行包装,使用jdk和cglib进行动态代理 @W](https://t.zsxq.com/06Ybq3fE2) \ No newline at end of file +- [已经通过一些包装类将切点信息进行包装,使用jdk和cglib进行动态代理 @W](https://t.zsxq.com/06Ybq3fE2) +- [借着 BeanPostProcessor(该接口的实现类提供的方法可以作用于Bean对象执行初始化前后,修改Bean对象的扩展信息) 把动态代理融入到 Bean 的生命周期中 @liuc](https://t.zsxq.com/081HcrZMA) +- [把 AOP 融入到 Bean 的生命周期中。@Homage](https://t.zsxq.com/08jTKRDFD) +- [整合 AOP 核心功能到 Spring 框架,达到可以通过 Spring 配置的方式完成切面操作。@水中捞月](https://t.zsxq.com/08tlg2pKA) +- [AOP融入Bean生命周期 @在九月](https://t.zsxq.com/0aMY674Df) +- [过扫描包来获取所有使用注解的 bean @lucien](https://t.zsxq.com/0bs96PAkO) \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-07-27-\347\254\25414\347\253\240\357\274\232\347\254\221\345\202\262\346\261\237\346\271\226\357\274\214\351\200\232\350\277\207\346\263\250\350\247\243\351\205\215\347\275\256\345\222\214\345\214\205\350\207\252\345\212\250\346\211\253\346\217\217\347\232\204\346\226\271\345\274\217\345\256\214\346\210\220Bean\345\257\271\350\261\241\347\232\204\346\263\250\345\206\214.md" "b/docs/md/spring/develop-spring/2021-07-27-\347\254\25414\347\253\240\357\274\232\347\254\221\345\202\262\346\261\237\346\271\226\357\274\214\351\200\232\350\277\207\346\263\250\350\247\243\351\205\215\347\275\256\345\222\214\345\214\205\350\207\252\345\212\250\346\211\253\346\217\217\347\232\204\346\226\271\345\274\217\345\256\214\346\210\220Bean\345\257\271\350\261\241\347\232\204\346\263\250\345\206\214.md" index c045d4caf..78c26128e 100755 --- "a/docs/md/spring/develop-spring/2021-07-27-\347\254\25414\347\253\240\357\274\232\347\254\221\345\202\262\346\261\237\346\271\226\357\274\214\351\200\232\350\277\207\346\263\250\350\247\243\351\205\215\347\275\256\345\222\214\345\214\205\350\207\252\345\212\250\346\211\253\346\217\217\347\232\204\346\226\271\345\274\217\345\256\214\346\210\220Bean\345\257\271\350\261\241\347\232\204\346\263\250\345\206\214.md" +++ "b/docs/md/spring/develop-spring/2021-07-27-\347\254\25414\347\253\240\357\274\232\347\254\221\345\202\262\346\261\237\346\271\226\357\274\214\351\200\232\350\277\207\346\263\250\350\247\243\351\205\215\347\275\256\345\222\214\345\214\205\350\207\252\345\212\250\346\211\253\346\217\217\347\232\204\346\226\271\345\274\217\345\256\214\346\210\220Bean\345\257\271\350\261\241\347\232\204\346\263\250\345\206\214.md" @@ -525,4 +525,8 @@ Process finished with exit code 0 ## 七、优秀作业 - [利用自定义注解 实现Bean的自动化扫描注册 @Rechie](https://t.zsxq.com/05ZjYbyJA) -- [解决包扫描问题以及${}字符映射 @Chin](https://t.zsxq.com/06URR3ZBq) \ No newline at end of file +- [解决包扫描问题以及${}字符映射 @Chin](https://t.zsxq.com/06URR3ZBq) +- [目前需要在spring.xml中配置,才可以使用使用Spring容器完成Bean的生命周期 @liuc](https://t.zsxq.com/089S6Zf5P) +- [这一章节比较简单,主要是进一步简化配置文件操作,扫描指定路径并通过注解完成 Bean 的注册。 @Homage](https://t.zsxq.com/08gaH0bTk) +- [在实现自动扫描带 @Component 注解的对象自动装配和注册的基础上,额可以使用 @Autowired、@Value 注解,完成对属性和对象的注入操作。@水中捞月](https://t.zsxq.com/084AWdKED) +- [在创建 Bean 对象之前,要先获取到 Bean 的属性信息组装成 BeanDefinition @lucien](https://t.zsxq.com/0bb6gydsQ) \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-08-03-\347\254\25415\347\253\240\357\274\232\344\270\207\344\272\272\344\271\213\346\225\214\357\274\214\351\200\232\350\277\207\346\263\250\350\247\243\347\273\231\345\261\236\346\200\247\346\263\250\345\205\245\351\205\215\347\275\256\345\222\214Bean\345\257\271\350\261\241.md" "b/docs/md/spring/develop-spring/2021-08-03-\347\254\25415\347\253\240\357\274\232\344\270\207\344\272\272\344\271\213\346\225\214\357\274\214\351\200\232\350\277\207\346\263\250\350\247\243\347\273\231\345\261\236\346\200\247\346\263\250\345\205\245\351\205\215\347\275\256\345\222\214Bean\345\257\271\350\261\241.md" index c392cbb19..9c2a7368d 100755 --- "a/docs/md/spring/develop-spring/2021-08-03-\347\254\25415\347\253\240\357\274\232\344\270\207\344\272\272\344\271\213\346\225\214\357\274\214\351\200\232\350\277\207\346\263\250\350\247\243\347\273\231\345\261\236\346\200\247\346\263\250\345\205\245\351\205\215\347\275\256\345\222\214Bean\345\257\271\350\261\241.md" +++ "b/docs/md/spring/develop-spring/2021-08-03-\347\254\25415\347\253\240\357\274\232\344\270\207\344\272\272\344\271\213\346\225\214\357\274\214\351\200\232\350\277\207\346\263\250\350\247\243\347\273\231\345\261\236\346\200\247\346\263\250\345\205\245\351\205\215\347\275\256\345\222\214Bean\345\257\271\350\261\241.md" @@ -443,7 +443,7 @@ public class UserDao { static { hashMap.put("10001", "小傅哥,北京,亦庄"); hashMap.put("10002", "八杯水,上海,尖沙咀"); - hashMap.put("10003", "阿毛,香港,铜锣湾"); + hashMap.put("10003", "阿毛,天津,东丽区"); } public String queryUserName(String uId) { @@ -547,4 +547,9 @@ Process finished with exit code 0 - [利用 BeanPostProcessor 实现Bean属性的注解注入](https://t.zsxq.com/06zV7mA2Z) - [解决属性注入,@Value和@Autowired注解 @Chin](https://t.zsxq.com/06emaAAyf) -- [使用注解来进行自动注入 @W](https://t.zsxq.com/06yFieYJu) \ No newline at end of file +- [使用注解来进行自动注入 @W](https://t.zsxq.com/06yFieYJu) +- [将原来在spring.xml中配置bean,改为在在类上添加@Component注解扫描 @liuc](https://t.zsxq.com/08jh9bRRu) +- [在实现自动扫描带 @Component 注解的对象自动装配和注册的基础上,额可以使用 @Autowired、@Value 注解,完成对属性和对象的注入操作。@水中捞月](https://t.zsxq.com/086EpbbNg) +- [Component-scan定义扫描的包 @Liuliuliu](https://t.zsxq.com/0aOUfSjv1) +- [对自定义注解的相关属性进行了回顾与整理 @在九月](https://t.zsxq.com/0avuEX4HC) +- [把代理对象的创建融入到 Bean 的生命周期中 @lucien](https://t.zsxq.com/0bH1pwMUE) \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-08-05-\347\254\25416\347\253\240\357\274\232\346\210\230\346\227\240\344\270\215\350\203\234\357\274\214\347\273\231\344\273\243\347\220\206\345\257\271\350\261\241\347\232\204\345\261\236\346\200\247\350\256\276\347\275\256\345\200\274.md" "b/docs/md/spring/develop-spring/2021-08-05-\347\254\25416\347\253\240\357\274\232\346\210\230\346\227\240\344\270\215\350\203\234\357\274\214\347\273\231\344\273\243\347\220\206\345\257\271\350\261\241\347\232\204\345\261\236\346\200\247\350\256\276\347\275\256\345\200\274.md" index 24f288671..7a9156820 100644 --- "a/docs/md/spring/develop-spring/2021-08-05-\347\254\25416\347\253\240\357\274\232\346\210\230\346\227\240\344\270\215\350\203\234\357\274\214\347\273\231\344\273\243\347\220\206\345\257\271\350\261\241\347\232\204\345\261\236\346\200\247\350\256\276\347\275\256\345\200\274.md" +++ "b/docs/md/spring/develop-spring/2021-08-05-\347\254\25416\347\253\240\357\274\232\346\210\230\346\227\240\344\270\215\350\203\234\357\274\214\347\273\231\344\273\243\347\220\206\345\257\271\350\261\241\347\232\204\345\261\236\346\200\247\350\256\276\347\275\256\345\200\274.md" @@ -1,16 +1,24 @@ --- -title: 【付费】第16章:给代理对象的属性设置值 -pay: https://articles.zsxq.com/id_fdl4zpdvwv68.html +title: 第16章:给代理对象的属性设置值 +lock: need --- -# 【付费】第 16 章:战无不胜,给代理对象的属性设置值 +# 第 16 章:战无不胜,给代理对象的属性设置值 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) -
星球:[https://articles.zsxq.com/id_fdl4zpdvwv68.html](https://articles.zsxq.com/id_fdl4zpdvwv68.html) +
星球:[https://articles.zsxq.com/id_w629m13v0hni.html](https://articles.zsxq.com/id_w629m13v0hni.html) > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [调整 AOP 代理对象生成的时机 实现其属性注入 @Rechie](https://t.zsxq.com/06v7aIQRV) +- [解决代理对象的属性注入,把代理对象加入生命周期 @Chin](https://t.zsxq.com/06niaEAYz) +- [给代理对象的属性设置值 @liuc](https://t.zsxq.com/084lgJpDk) +- [MyBatis 就是主要使用代理类,因此 Spring 就需要支持代理类的初始化。@水中捞月](https://t.zsxq.com/08X7yWOw4) +- [调整AOP代理对象的结构,使之可以被注入属性 @在九月](https://t.zsxq.com/0a8bAchto) + ## 一、前言 `怎么了,运行的好好的放在别人电脑上就出错?` @@ -25,7 +33,355 @@ pay: https://articles.zsxq.com/id_fdl4zpdvwv68.html 所以本章节中我们要把代理对象的创建融入到 Bean 的生命周期中,也就是需要把创建代理对象的逻辑迁移到 Bean 对象执行初始化方法之后,在执行代理对象的创建。 -## 三、优秀作业 +## 三、方案 -- [调整 AOP 代理对象生成的时机 实现其属性注入 @Rechie](https://t.zsxq.com/06v7aIQRV) -- [解决代理对象的属性注入,把代理对象加入生命周期 @Chin](https://t.zsxq.com/06niaEAYz) \ No newline at end of file +按照创建代理对象的操作 `DefaultAdvisorAutoProxyCreator` 实现的 `InstantiationAwareBeanPostProcessor` 接口,那么原本在 Before 中的操作,则需要放到 After 中处理。整体设计如下: + +![](https://bugstack.cn/assets/images/spring/spring-16-01.png) + +- 在创建 Bean 对象 `createBean` 的生命周期中,有一个阶段是在 Bean 对象属性填充完成以后,执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理,例如:感知 Aware 对象、处理 init-method 方法等。那么在这个阶段的 `BeanPostProcessor After` 就可以用于创建代理对象操作。 +- 在 DefaultAdvisorAutoProxyCreator 用于创建代理对象的操作中,需要把创建操作从 postProcessBeforeInstantiation 方法中迁移到 postProcessAfterInitialization,这样才能满足 Bean 属性填充后的创建操作。 + +## 四、实现 + +### 1. 工程结构 + +```java +small-spring-step-15 +└── src + ├── main + │ └── java + │ └── cn.bugstack.springframework + │ ├── aop + │ │ ├── aspectj + │ │ │ └── AspectJExpressionPointcut.java + │ │ │ └── AspectJExpressionPointcutAdvisor.java + │ │ ├── framework + │ │ │ ├── adapter + │ │ │ │ └── MethodBeforeAdviceInterceptor.java + │ │ │ ├── autoproxy + │ │ │ │ └── MethodBeforeAdviceInterceptor.java + │ │ │ ├── AopProxy.java + │ │ │ ├── Cglib2AopProxy.java + │ │ │ ├── JdkDynamicAopProxy.java + │ │ │ ├── ProxyFactory.java + │ │ │ └── ReflectiveMethodInvocation.java + │ │ ├── AdvisedSupport.java + │ │ ├── Advisor.java + │ │ ├── BeforeAdvice.java + │ │ ├── ClassFilter.java + │ │ ├── MethodBeforeAdvice.java + │ │ ├── MethodMatcher.java + │ │ ├── Pointcut.java + │ │ ├── PointcutAdvisor.java + │ │ └── TargetSource.java + │ ├── beans + │ │ ├── factory + │ │ │ ├── annotation + │ │ │ │ ├── Autowired.java + │ │ │ │ ├── AutowiredAnnotationBeanPostProcessor.java + │ │ │ │ ├── Qualifier.java + │ │ │ │ └── Value.java + │ │ │ ├── config + │ │ │ │ ├── AutowireCapableBeanFactory.java + │ │ │ │ ├── BeanDefinition.java + │ │ │ │ ├── BeanFactoryPostProcessor.java + │ │ │ │ ├── BeanPostProcessor.java + │ │ │ │ ├── BeanReference.java + │ │ │ │ ├── ConfigurableBeanFactory.java + │ │ │ │ ├── InstantiationAwareBeanPostProcessor.java + │ │ │ │ └── SingletonBeanRegistry.java + │ │ │ ├── support + │ │ │ │ ├── AbstractAutowireCapableBeanFactory.java + │ │ │ │ ├── AbstractBeanDefinitionReader.java + │ │ │ │ ├── AbstractBeanFactory.java + │ │ │ │ ├── BeanDefinitionReader.java + │ │ │ │ ├── BeanDefinitionRegistry.java + │ │ │ │ ├── CglibSubclassingInstantiationStrategy.java + │ │ │ │ ├── DefaultListableBeanFactory.java + │ │ │ │ ├── DefaultSingletonBeanRegistry.java + │ │ │ │ ├── DisposableBeanAdapter.java + │ │ │ │ ├── FactoryBeanRegistrySupport.java + │ │ │ │ ├── InstantiationStrategy.java + │ │ │ │ └── SimpleInstantiationStrategy.java + │ │ │ ├── support + │ │ │ │ └── XmlBeanDefinitionReader.java + │ │ │ ├── Aware.java + │ │ │ ├── BeanClassLoaderAware.java + │ │ │ ├── BeanFactory.java + │ │ │ ├── BeanFactoryAware.java + │ │ │ ├── BeanNameAware.java + │ │ │ ├── ConfigurableListableBeanFactory.java + │ │ │ ├── DisposableBean.java + │ │ │ ├── FactoryBean.java + │ │ │ ├── HierarchicalBeanFactory.java + │ │ │ ├── InitializingBean.java + │ │ │ ├── ListableBeanFactory.java + │ │ │ └── PropertyPlaceholderConfigurer.java + │ │ ├── BeansException.java + │ │ ├── PropertyValue.java + │ │ └── PropertyValues.java + │ ├── context + │ │ ├── annotation + │ │ │ ├── ClassPathBeanDefinitionScanner.java + │ │ │ ├── ClassPathScanningCandidateComponentProvider.java + │ │ │ └── Scope.java + │ │ ├── event + │ │ │ ├── AbstractApplicationEventMulticaster.java + │ │ │ ├── ApplicationContextEvent.java + │ │ │ ├── ApplicationEventMulticaster.java + │ │ │ ├── ContextClosedEvent.java + │ │ │ ├── ContextRefreshedEvent.java + │ │ │ └── SimpleApplicationEventMulticaster.java + │ │ ├── support + │ │ │ ├── AbstractApplicationContext.java + │ │ │ ├── AbstractRefreshableApplicationContext.java + │ │ │ ├── AbstractXmlApplicationContext.java + │ │ │ ├── ApplicationContextAwareProcessor.java + │ │ │ └── ClassPathXmlApplicationContext.java + │ │ ├── ApplicationContext.java + │ │ ├── ApplicationContextAware.java + │ │ ├── ApplicationEvent.java + │ │ ├── ApplicationEventPublisher.java + │ │ ├── ApplicationListener.java + │ │ └── ConfigurableApplicationContext.java + │ ├── core.io + │ │ ├── ClassPathResource.java + │ │ ├── DefaultResourceLoader.java + │ │ ├── FileSystemResource.java + │ │ ├── Resource.java + │ │ ├── ResourceLoader.java + │ │ └── UrlResource.java + │ ├── stereotype + │ │ └── Component.java + │ └── utils + │ ├── ClassUtils.java + │ └── StringValueResolver.java + └── test + └── java + └── cn.bugstack.springframework.test + ├── bean + │ ├── IUserService.java + │ └── UserService.java + └── ApiTest.java +``` + +**工程源码**:`公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码` + +在Bean的生命周期中创建代理对象的类关系,如图 16-2 + +![图 16-2](https://bugstack.cn/assets/images/spring/spring-16-02.png) + +- 虽然本章节要完成的是关于代理对象中属性的填充问题,但实际解决的思路是处理在 Bean 的生命周期中合适的位置(`初始化 initializeBean`)中处理代理类的创建。 +- 所以以上的改动并不会涉及太多内容,主要包括:DefaultAdvisorAutoProxyCreator 类创建代理对象的操作放置在 postProcessAfterInitialization 方法中以及对应在 AbstractAutowireCapableBeanFactory 完成初始化方法的调用操作。 +- 另外还有一点要注意,就是目前我们在 Spring 框架中,AbstractAutowireCapableBeanFactory 类里使用的是 CglibSubclassingInstantiationStrategy 创建对象,所以有需要判断对象获取接口的方法中,也都需要判断是否为 CGlib创建,否则是不能正确获取到接口的。如:`ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;` + +### 2. 判断CGlib对象 + +**cn.bugstack.springframework.aop.TargetSource** + +```java +public class TargetSource { + + private final Object target; + + /** + * Return the type of targets returned by this {@link TargetSource}. + *

Can return null, although certain usages of a + * TargetSource might just work with a predetermined + * target class. + * + * @return the type of targets returned by this {@link TargetSource} + */ + public Class[] getTargetClass() { + Class clazz = this.target.getClass(); + clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz; + return clazz.getInterfaces(); + } + +} +``` + +- 在 TargetSource#getTargetClass 是用于获取 target 对象的接口信息的,那么这个 target 可能是 `JDK代理` 创建也可能是 `CGlib创建`,为了保证都能正确的获取到结果,这里需要增加判读 `ClassUtils.isCglibProxyClass(clazz)` + +### 3. 迁移创建AOP代理方法 + +**cn.bugstack.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator** + +```java +public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware { + + private DefaultListableBeanFactory beanFactory; + + @Override + public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException { + return null; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + + if (isInfrastructureClass(bean.getClass())) return bean; + + Collection advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values(); + + for (AspectJExpressionPointcutAdvisor advisor : advisors) { + ClassFilter classFilter = advisor.getPointcut().getClassFilter(); + // 过滤匹配类 + if (!classFilter.matches(bean.getClass())) continue; + + AdvisedSupport advisedSupport = new AdvisedSupport(); + + TargetSource targetSource = new TargetSource(bean); + advisedSupport.setTargetSource(targetSource); + advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice()); + advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher()); + advisedSupport.setProxyTargetClass(false); + + // 返回代理对象 + return new ProxyFactory(advisedSupport).getProxy(); + } + + return bean; + } + +} +``` + +- 关于 DefaultAdvisorAutoProxyCreator 类的操作主要就是把创建 AOP 代理的操作从 postProcessBeforeInstantiation 移动到 postProcessAfterInitialization 中去。 +- 通过设置一些 AOP 的必备参数后,返回代理对象 `new ProxyFactory(advisedSupport).getProxy()` 这个代理对象中就包括间接调用了 TargetSource 中对 getTargetClass() 的获取。 + +### 4. 在Bean的生命周期中初始化执行 + +**cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory** + +```java +public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { + + private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy(); + + @Override + protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException { + Object bean = null; + try { + // ... + + // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法 + bean = initializeBean(beanName, bean, beanDefinition); + } catch (Exception e) { + throw new BeansException("Instantiation of bean failed", e); + } + // ... + return bean; + } + + private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) { + + // ... + + wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName); + return wrappedBean; + } + + @Override + public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { + Object result = existingBean; + for (BeanPostProcessor processor : getBeanPostProcessors()) { + Object current = processor.postProcessAfterInitialization(result, beanName); + if (null == current) return result; + result = current; + } + return result; + } + +} +``` + +- 在 AbstractAutowireCapableBeanFactory#createBean 方法中,其实关注点就在于 initializeBean -> applyBeanPostProcessorsAfterInitialization 这一块逻辑的调用,最终完成 AOP 代理对象的创建操作。 + +## 五、测试 + +### 1. 事先准备 + +**UserService 添加属性字段** + +```java +public class UserService implements IUserService { + + private String token; + + public String queryUserInfo() { + try { + Thread.sleep(new Random(1).nextInt(100)); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return "小傅哥,100001,深圳," + token; + } + +} +``` + +- token 是在 UserService 中新增的属性信息,用于测试代理对象的属性填充操作。 + +### 2. 属性配置文件 + +```xml + + + + + + + + + + + + + + + + + + + + + +``` + +- 与我们对 AOP 的测试来说,唯一新增加的就是 property 的配置:`` + +### 3. 单元测试 + +```java +@Test +public void test_autoProxy() { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml"); + IUserService userService = applicationContext.getBean("userService", IUserService.class); + System.out.println("测试结果:" + userService.queryUserInfo()); +} +``` + +![](https://bugstack.cn/assets/images/spring/spring-16-03.png) + +**测试结果** + +```java +拦截方法:queryUserInfo +测试结果:小傅哥,100001,深圳,RejDlI78hu223Opo983Ds + +Process finished with exit code 0 +``` + +- 从测试结果可以看到,通过对 Bean 生命周期的调整,在创建 AOP 代理对象就可以把代理对象的属性信息填充进去了。 +- 另外这里还有一块是关于在 TargetSource#getTargetClass 中关于是否为 CGlib 的方法判断,只有这样操作才可以获取到争取的类信息。 + +## 六、总结 + +- 本章节的核心知识内容主要是完善了 Bean 的生命周期,在创建类的操作中完成代理对象的创建,通过这样的方式就可以让代理对象中的属性也可以随着创建过程被填充进去。 +- 除了核心功能的实现外也要关注到对象的初始化操作是 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy,这两种方式中的 CGlib 创建对象,会影响到很多地方用于接口获取的操作,因为 CGlib 创建对象走的是 ASM 字节码生成的操作,所以和普通的 JDK 代理生成对象是不一样,需要注意。 +- 程序的Bug往往是对需求的使用场景理解不足,功能的完善是对一个细化场景的程序精雕,开发程序的过程远远不只是写代码那么回事,更重要的是思考`这是什么场景`、`遇到了哪些问题`、`要怎么解决`、`可以学到什么`中不断的锤炼自己的程序逻辑。 \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-08-07-\347\254\25417\347\253\240\357\274\232\346\224\273\346\227\240\344\270\215\345\205\213\357\274\214\351\200\232\350\277\207\344\270\211\347\272\247\347\274\223\345\255\230\350\247\243\345\206\263\345\276\252\347\216\257\344\276\235\350\265\226.md" "b/docs/md/spring/develop-spring/2021-08-07-\347\254\25417\347\253\240\357\274\232\346\224\273\346\227\240\344\270\215\345\205\213\357\274\214\351\200\232\350\277\207\344\270\211\347\272\247\347\274\223\345\255\230\350\247\243\345\206\263\345\276\252\347\216\257\344\276\235\350\265\226.md" index b4831c932..4ce262e5d 100644 --- "a/docs/md/spring/develop-spring/2021-08-07-\347\254\25417\347\253\240\357\274\232\346\224\273\346\227\240\344\270\215\345\205\213\357\274\214\351\200\232\350\277\207\344\270\211\347\272\247\347\274\223\345\255\230\350\247\243\345\206\263\345\276\252\347\216\257\344\276\235\350\265\226.md" +++ "b/docs/md/spring/develop-spring/2021-08-07-\347\254\25417\347\253\240\357\274\232\346\224\273\346\227\240\344\270\215\345\205\213\357\274\214\351\200\232\350\277\207\344\270\211\347\272\247\347\274\223\345\255\230\350\247\243\345\206\263\345\276\252\347\216\257\344\276\235\350\265\226.md" @@ -1,9 +1,9 @@ --- -title: 【付费】第17章:通过三级缓存解决循环依赖 -pay: https://articles.zsxq.com/id_w629m13v0hni.html +title: 第17章:通过三级缓存解决循环依赖 +lock: need --- -# 【付费】第 17 章:攻无不克,通过三级缓存解决循环依赖 +# 第 17 章:攻无不克,通过三级缓存解决循环依赖 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) @@ -11,6 +11,17 @@ pay: https://articles.zsxq.com/id_w629m13v0hni.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [三级缓存处理循环依赖 @Rechie](https://t.zsxq.com/06jEynIE2) +- [手撕Spring-解决循环依赖加属性类型转化 @Chin](https://t.zsxq.com/06AyBeiYN) +- [三级缓存循环依赖梳理 @赛博丁真](https://t.zsxq.com/08x6okqWE) +- [循环依赖主要分为三种:自身依赖自身、互相循环依赖、多组循环依赖。@水中捞月](https://t.zsxq.com/08USMN0DG) +- [在实例化其他bean对象之前对ConversionServiceFactoryBean对象提前注册 @Liuliuliu](https://t.zsxq.com/0aifC9zGU) +- [循环依赖是 Spring 里面很重要的一个设计,因为它用到了三级缓存 @lucien](https://t.zsxq.com/0bRmz6qty) +- [需要实现一个 ConversionServiceFactoryBean 来对类型转换服务进行操作 @lucien](https://t.zsxq.com/0bukMhjB7) +- [通过代理对象判断,ClassUtils.isCglibProxyClass(aClass)? aClass.getSuperclass():aClass; 处理Cglib代理使用](https://t.zsxq.com/0bf6vuTJS) + ## 一、前言 `嘎哈呀,又不是不能用!` @@ -33,7 +44,466 @@ pay: https://articles.zsxq.com/id_w629m13v0hni.html - 但无论循环依赖的数量有多少,循环依赖的本质是一样的。就是你的完整创建依赖于我,而我的完整创建也依赖于你,但我们互相没法解耦,最终导致依赖创建失败。 - 所以需要 Spring 提供了除了构造函数注入和原型注入外的,setter 循环依赖注入解决方案。 -## 三、优秀作业 +## 三、设计 -- [三级缓存处理循环依赖 @Rechie](https://t.zsxq.com/06jEynIE2) -- [手撕Spring-解决循环依赖加属性类型转化 @Chin](https://t.zsxq.com/06AyBeiYN) \ No newline at end of file +按照 Spring 框架的设计,用于解决循环依赖需要用到三个缓存,这三个缓存分别存放了`成品对象`、`半成品对象(未填充属性值)`、`代理对象`,分阶段存放对象内容,来解决循环依赖问题。 + +**那么**,这里我们需要知道一个核心的原理,就是用于解决循环依赖就必须是三级缓存呢,二级行吗?一级可以不?其实都能解决,只不过 Spring 框架的实现要保证几个事情,如只有一级缓存处理流程没法拆分,复杂度也会增加,同时半成品对象可能会有空指针异常。而将半成品与成品对象分开,处理起来也更加优雅、简单、易扩展。另外 Spring 的两大特性中不仅有 IOC 还有 AOP,也就是基于字节码增强后的方法,该存放到哪,而三级缓存最主要,要解决的循环依赖就是对 AOP 的处理,但如果把 AOP 代理对象的创建提前,那么二级缓存也一样可以解决。但是,这就违背了 Spring 创建对象的原则,Spring 更喜欢把所有的普通 Bean 都初始化完成,在处理代理对象的初始化。 + +**不过**,没关系我们可以先尝试仅适用一级缓存来解决循环依赖,通过这样的方式从中学习到处理循环依赖的最核心原来,也就是那20%的地方。 + +![](https://bugstack.cn/assets/images/spring/spring-17-02.png) + +- 如果仅以一级缓存解决循环依赖,那么在实现上可以通过在A对象 newInstance 创建且未填充属性后,直接放入缓存中。 +- 在`A对象`的属性填充`B对象`时,如果缓存中不能获取到`B对象`,则开始创建`B对象`,同样创建完成后,把`B对象`填充到缓存中去。 +- 接下来就开始对`B对象`的属性进行填充,恰好这会可以从缓存中拿到`半成品的A对象`,那么这个时候`B对象`的属性就填充完了。 +- 最后返回来继续完成`A对象`的属性填充,把实例化后并填充了属性的`B对象`赋值给A对象的`b属性`,这样就完成了一个循环依赖操作。 + +**代码实现** + +```java +private final static Map singletonObjects = new ConcurrentHashMap<>(256); + +private static T getBean(Class beanClass) throws Exception { + String beanName = beanClass.getSimpleName().toLowerCase(); + if (singletonObjects.containsKey(beanName)) { + return (T) singletonObjects.get(beanName); + } + // 实例化对象入缓存 + Object obj = beanClass.newInstance(); + singletonObjects.put(beanName, obj); + // 属性填充补全对象 + Field[] fields = obj.getClass().getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + Class fieldClass = field.getType(); + String fieldBeanName = fieldClass.getSimpleName().toLowerCase(); + field.set(obj, singletonObjects.containsKey(fieldBeanName) ? singletonObjects.get(fieldBeanName) : getBean(fieldClass)); + field.setAccessible(false); + } + return (T) obj; +} +``` + +- 使用一级缓存存放对象的方式,就是这样简单的实现过程,只要是创建完对象,立马塞到缓存里去。这样就可以在其他对象创建时候获取到属性需要填充的对象了。 + +**测试结果** + +```java +public static void main(String[] args) throws Exception { + System.out.println(getBean(B.class).getA()); + System.out.println(getBean(A.class).getB()); +} + +cn.bugstack.springframework.test.A@49476842 +cn.bugstack.springframework.test.B@78308db1 + +Process finished with exit code 0 +``` + +![](https://bugstack.cn/assets/images/spring/spring-17-03.png) + +- 从测试效果和截图依赖过程中可以看到,一级缓存也可以解决简单场景的循环依赖问题。 +- 其实 `getBean`,是整个解决循环依赖的核心内容,A 创建后填充属性时依赖 B,那么就去创建 B,在创建 B 开始填充时发现依赖于 A,但此时 A 这个半成品对象已经存放在缓存到`singletonObjects` 中了,所以 B 可以正常创建,在通过递归把 A 也创建完整了。 + +--- + +有了以上这部分关于循环依赖的处理内容,在理解循环依赖就没那么复杂了。接下来我们带着这个`感觉`去思考如果有对象不只是简单的对象,还有代理对象,还有AOP应用,要怎么处理这样的依赖问题。整体设计结构如下图: + +![](https://bugstack.cn/assets/images/spring/spring-17-04.png) + +- 关于循环依赖在我们目前的 Spring 框架中扩展起来也并不会太复杂,主要就是对于创建对象的`提前暴露`,如果是工厂对象则会使用 getEarlyBeanReference 逻辑提前将工厂🏭对象存放到三级缓存中。等到后续获取对象的时候实际拿到的是工厂对象中 getObject,这个才是最终的实际对象。 +- 在创建对象的 `AbstractAutowireCapableBeanFactory#doCreateBean` 方法中,提前暴露对象以后,就可以通过接下来的流程,getSingleton 从三个缓存中以此寻找对象,一级、二级如果有则直接取走,如果对象是三级缓存中则会从三级缓存中获取后并删掉工厂对象,把实际对象放到二级缓存中。 +- 最后是关于单例的对象的注册操作,这个注册操作就是把真实的实际对象放到一级缓存中,因为此时它已经是一个成品对象了。 + +## 四、实现 + +### 1. 工程结构 + +```java +small-spring-step-16 +└── src + ├── main + │ └── java + │ └── cn.bugstack.springframework + │ ├── aop + │ │ ├── aspectj + │ │ │ └── AspectJExpressionPointcut.java + │ │ │ └── AspectJExpressionPointcutAdvisor.java + │ │ ├── framework + │ │ │ ├── adapter + │ │ │ │ └── MethodBeforeAdviceInterceptor.java + │ │ │ ├── autoproxy + │ │ │ │ └── MethodBeforeAdviceInterceptor.java + │ │ │ ├── AopProxy.java + │ │ │ ├── Cglib2AopProxy.java + │ │ │ ├── JdkDynamicAopProxy.java + │ │ │ ├── ProxyFactory.java + │ │ │ └── ReflectiveMethodInvocation.java + │ │ ├── AdvisedSupport.java + │ │ ├── Advisor.java + │ │ ├── BeforeAdvice.java + │ │ ├── ClassFilter.java + │ │ ├── MethodBeforeAdvice.java + │ │ ├── MethodMatcher.java + │ │ ├── Pointcut.java + │ │ ├── PointcutAdvisor.java + │ │ └── TargetSource.java + │ ├── beans + │ │ ├── factory + │ │ │ ├── annotation + │ │ │ │ ├── Autowired.java + │ │ │ │ ├── AutowiredAnnotationBeanPostProcessor.java + │ │ │ │ ├── Qualifier.java + │ │ │ │ └── Value.java + │ │ │ ├── config + │ │ │ │ ├── AutowireCapableBeanFactory.java + │ │ │ │ ├── BeanDefinition.java + │ │ │ │ ├── BeanFactoryPostProcessor.java + │ │ │ │ ├── BeanPostProcessor.java + │ │ │ │ ├── BeanReference.java + │ │ │ │ ├── ConfigurableBeanFactory.java + │ │ │ │ ├── InstantiationAwareBeanPostProcessor.java + │ │ │ │ └── SingletonBeanRegistry.java + │ │ │ ├── support + │ │ │ │ ├── AbstractAutowireCapableBeanFactory.java + │ │ │ │ ├── AbstractBeanDefinitionReader.java + │ │ │ │ ├── AbstractBeanFactory.java + │ │ │ │ ├── BeanDefinitionReader.java + │ │ │ │ ├── BeanDefinitionRegistry.java + │ │ │ │ ├── CglibSubclassingInstantiationStrategy.java + │ │ │ │ ├── DefaultListableBeanFactory.java + │ │ │ │ ├── DefaultSingletonBeanRegistry.java + │ │ │ │ ├── DisposableBeanAdapter.java + │ │ │ │ ├── FactoryBeanRegistrySupport.java + │ │ │ │ ├── InstantiationStrategy.java + │ │ │ │ └── SimpleInstantiationStrategy.java + │ │ │ ├── support + │ │ │ │ └── XmlBeanDefinitionReader.java + │ │ │ ├── Aware.java + │ │ │ ├── BeanClassLoaderAware.java + │ │ │ ├── BeanFactory.java + │ │ │ ├── BeanFactoryAware.java + │ │ │ ├── BeanNameAware.java + │ │ │ ├── ConfigurableListableBeanFactory.java + │ │ │ ├── DisposableBean.java + │ │ │ ├── FactoryBean.java + │ │ │ ├── HierarchicalBeanFactory.java + │ │ │ ├── InitializingBean.java + │ │ │ ├── ListableBeanFactory.java + │ │ │ ├── ObjectFactory.java + │ │ │ └── PropertyPlaceholderConfigurer.java + │ │ ├── BeansException.java + │ │ ├── PropertyValue.java + │ │ └── PropertyValues.java + │ ├── context + │ │ ├── annotation + │ │ │ ├── ClassPathBeanDefinitionScanner.java + │ │ │ ├── ClassPathScanningCandidateComponentProvider.java + │ │ │ └── Scope.java + │ │ ├── event + │ │ │ ├── AbstractApplicationEventMulticaster.java + │ │ │ ├── ApplicationContextEvent.java + │ │ │ ├── ApplicationEventMulticaster.java + │ │ │ ├── ContextClosedEvent.java + │ │ │ ├── ContextRefreshedEvent.java + │ │ │ └── SimpleApplicationEventMulticaster.java + │ │ ├── support + │ │ │ ├── AbstractApplicationContext.java + │ │ │ ├── AbstractRefreshableApplicationContext.java + │ │ │ ├── AbstractXmlApplicationContext.java + │ │ │ ├── ApplicationContextAwareProcessor.java + │ │ │ └── ClassPathXmlApplicationContext.java + │ │ ├── ApplicationContext.java + │ │ ├── ApplicationContextAware.java + │ │ ├── ApplicationEvent.java + │ │ ├── ApplicationEventPublisher.java + │ │ ├── ApplicationListener.java + │ │ └── ConfigurableApplicationContext.java + │ ├── core.io + │ │ ├── ClassPathResource.java + │ │ ├── DefaultResourceLoader.java + │ │ ├── FileSystemResource.java + │ │ ├── Resource.java + │ │ ├── ResourceLoader.java + │ │ └── UrlResource.java + │ ├── stereotype + │ │ └── Component.java + │ └── utils + │ ├── ClassUtils.java + │ └── StringValueResolver.java + └── test + └── java + └── cn.bugstack.springframework.test + ├── bean + │ ├── Husband.java + │ ├── HusbandMother.java + │ ├── IMother.java + │ ├── SpouseAdvice.java + │ └── Wife.java + ├── ApiTest.java + └── CircleTest.java +``` + +**工程源码**:`公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码` + +处理循环依赖核心流程的类关系的操作过程包括: +- 循环依赖的核心功能实现主要包括 DefaultSingletonBeanRegistry 提供三级缓存:`singletonObjects`、`earlySingletonObjects`、`singletonFactories`,分别存放成品对象、半成品对象和工厂对象。同时包装三个缓存提供方法:getSingleton、registerSingleton、addSingletonFactory,这样使用方就可以分别在不同时间段存放和获取对应的对象了。 +- 在 AbstractAutowireCapableBeanFactory 的 doCreateBean 方法中,提供了关于提前暴露对象的操作,`addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean));`,以及后续获取对象和注册对象的操作,` exposedObject = getSingleton(beanName);`、`registerSingleton(beanName, exposedObject);`,经过这样的处理就可以完成对复杂场景循环依赖的操作。 +- 另外在 DefaultAdvisorAutoProxyCreator 提供的切面服务中,也需要实现接口 InstantiationAwareBeanPostProcessor 新增的 getEarlyBeanReference 方法,便于把依赖的切面对象也能存放到三级缓存中,处理对应的循环依赖。 + +### 2. 设置三级缓存 + +**cn.bugstack.springframework.beans.factory.support.DefaultSingletonBeanRegistry** + +```java +public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry { + + // 一级缓存,普通对象 + private Map singletonObjects = new ConcurrentHashMap<>(); + + // 二级缓存,提前暴漏对象,没有完全实例化的对象 + protected final Map earlySingletonObjects = new HashMap(); + + // 三级缓存,存放代理对象 + private final Map> singletonFactories = new HashMap>(); + + private final Map disposableBeans = new LinkedHashMap<>(); + + @Override + public Object getSingleton(String beanName) { + Object singletonObject = singletonObjects.get(beanName); + if (null == singletonObject) { + singletonObject = earlySingletonObjects.get(beanName); + // 判断二级缓存中是否有对象,这个对象就是代理对象,因为只有代理对象才会放到三级缓存中 + if (null == singletonObject) { + ObjectFactory singletonFactory = singletonFactories.get(beanName); + if (singletonFactory != null) { + singletonObject = singletonFactory.getObject(); + // 把三级缓存中的代理对象中的真实对象获取出来,放入二级缓存中 + earlySingletonObjects.put(beanName, singletonObject); + singletonFactories.remove(beanName); + } + } + } + return singletonObject; + } + + public void registerSingleton(String beanName, Object singletonObject) { + singletonObjects.put(beanName, singletonObject); + earlySingletonObjects.remove(beanName); + singletonFactories.remove(beanName); + } + + protected void addSingletonFactory(String beanName, ObjectFactory singletonFactory){ + if (!this.singletonObjects.containsKey(beanName)) { + this.singletonFactories.put(beanName, singletonFactory); + this.earlySingletonObjects.remove(beanName); + } + } + + public void registerDisposableBean(String beanName, DisposableBean bean) { + disposableBeans.put(beanName, bean); + } + +} +``` + +- 在用于提供单例对象注册的操作的 DefaultSingletonBeanRegistry 类中,共有三个缓存对象的属性;singletonObjects、earlySingletonObjects、singletonFactories,如它们的名字一样,用于存放不同类型的对象(单例对象、早期的半成品单例对象、单例工厂对象)。 +- 紧接着在这三个缓存对象下提供了获取、添加和注册不同对象的方法,包括:getSingleton、registerSingleton、addSingletonFactory,其实后面这两个方法都比较简单,主要是 getSingleton 的操作,它是在一层层处理不同时期的单例对象,直至拿到有效的对象。 + +### 3. 提前暴露对象 + +**cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory** + +```java +public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { + + protected Object doCreateBean(String beanName, BeanDefinition beanDefinition, Object[] args) { + Object bean = null; + try { + // 实例化 Bean + bean = createBeanInstance(beanDefinition, beanName, args); + + // 处理循环依赖,将实例化后的Bean对象提前放入缓存中暴露出来 + if (beanDefinition.isSingleton()) { + Object finalBean = bean; + addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, beanDefinition, finalBean)); + } + + // 实例化后判断 + boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(beanName, bean); + if (!continueWithPropertyPopulation) { + return bean; + } + // 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值 + applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition); + // 给 Bean 填充属性 + applyPropertyValues(beanName, bean, beanDefinition); + // 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法 + bean = initializeBean(beanName, bean, beanDefinition); + } catch (Exception e) { + throw new BeansException("Instantiation of bean failed", e); + } + + // 注册实现了 DisposableBean 接口的 Bean 对象 + registerDisposableBeanIfNecessary(beanName, bean, beanDefinition); + + // 判断 SCOPE_SINGLETON、SCOPE_PROTOTYPE + Object exposedObject = bean; + if (beanDefinition.isSingleton()) { + // 获取代理对象 + exposedObject = getSingleton(beanName); + registerSingleton(beanName, exposedObject); + } + return exposedObject; + + } + + protected Object getEarlyBeanReference(String beanName, BeanDefinition beanDefinition, Object bean) { + Object exposedObject = bean; + for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) { + if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) { + exposedObject = ((InstantiationAwareBeanPostProcessor) beanPostProcessor).getEarlyBeanReference(exposedObject, beanName); + if (null == exposedObject) return exposedObject; + } + } + + return exposedObject; + } + + // ... +} +``` + +- 在 AbstractAutowireCapableBeanFactory#doCreateBean 的方法中主要是扩展了对象的提前暴露`addSingletonFactory`了,和单例对象的获取`getSingleton`以及注册操作`registerSingleton`。 +- 这里提到一点 getEarlyBeanReference 就是定义在如 AOP 切面中这样的代理对象,可以参考源码中接口 InstantiationAwareBeanPostProcessor#getEarlyBeanReference 方法的实现。 + +## 五、测试 + +因为是要测试循环依赖,我们找一个比较贴近的场景来做测试,*我说过我是一个喜欢从生活中发现面向对象编程的人* 我们的案例场景人物包括:老公和媳妇互相依赖、婆婆是一个模拟成代理妈妈职责、在加上一个切面来关心家庭生活👪 + +### 1. 事先准备 + +**老公,类** + +```java +public class Husband { + + private Wife wife; + + public String queryWife(){ + return "Husband.wife"; + } + +} +``` + +**媳妇,类** + +```java +public class Wife { + + private Husband husband; + private IMother mother; // 婆婆 + + public String queryHusband() { + return "Wife.husband、Mother.callMother:" + mother.callMother(); + } + +} +``` + +**婆婆,代理了媳妇原来妈妈的职责的类** + + +```java +public class HusbandMother implements FactoryBean { + + @Override + public IMother getObject() throws Exception { + return (IMother) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IMother.class}, (proxy, method, args) -> "婚后媳妇妈妈的职责被婆婆代理了!" + method.getName()); + } + +} +``` + +**切面,类** + +```java +public class SpouseAdvice implements MethodBeforeAdvice { + + @Override + public void before(Method method, Object[] args, Object target) throws Throwable { + System.out.println("关怀小两口(切面):" + method); + } + +} +``` + +### 2. 属性配置文件 + +**spring.xml** + +```java + + + + + + + + + + + + + + + + + + + + + + + + +``` + +- 这个里的配置就很简单了,配置husband依赖wife,配置wife依赖husband和mother,最后是关于AOP切面的依赖使用。 + +### 3. 单元测试 + +```java +@Test +public void test_circular() { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml"); + Husband husband = applicationContext.getBean("husband", Husband.class); + Wife wife = applicationContext.getBean("wife", Wife.class); + System.out.println("老公的媳妇:" + husband.queryWife()); + System.out.println("媳妇的老公:" + wife.queryHusband()); +} +``` + +**测试结果** + +![](https://bugstack.cn/assets/images/spring/spring-17-06.png) + +```java +老公的媳妇:Husband.wife +关怀小两口(切面):public java.lang.String cn.bugstack.springframework.test.bean.Wife.queryHusband() +媳妇的老公:Wife.husband、Mother.callMother:婚后媳妇妈妈的职责被婆婆代理了!callMother + +Process finished with exit code 0 +``` + +- 从测试结果可以看到,无论是简单对象依赖 *老公依赖媳妇、媳妇依赖老公*,还是代理工程对象或者 AOP 切面对象都可以在三级缓存下解决循环依赖的问题了。 +- 此外从运行截图 `DefaultSingletonBeanRegistry#getSingleton` 中也可以看到凡事需要三级缓存存放工厂对象的类,都会使用到 getObject 获取真实对象,并随后存入半成品对象 earlySingletonObjects 中以及移除工厂对象。 + +## 六、总结 + +- Spring 中所有的功能都是以解决 Java 编程中的特性而存在的,就像我们本章节处理的循环依赖,如果没有 Spring 框架的情况下,可能我们也会尽可能避免写出循环依赖的操作,因为在没有经过加工处理后,这样的依赖关系肯定会报错的。*那么这也就是程序从能用到好用的升级* +- 在解决循环依赖的核心流程中,主要是提前暴露对象的设计,以及建立三级缓存的数据结构来存放不同时期的对象,如果说没有如切面和工厂中的代理对象,那么二级缓存也就可以解决了,哪怕是只有一级缓存。但为了设计上的合理和可扩展性,所以创建了三级缓存来放置不同时期的对象。 +- 通过这样的学习也可以思考🤔我们在做程序设计时,将要上线的功能是否能全面支撑起业务的拓展和频繁变化的特性,有时候这些设计思路是可以帮我们拓宽更多的技术设计视野。*记得要多加练习!* \ No newline at end of file diff --git "a/docs/md/spring/develop-spring/2021-08-09-\347\254\25418\347\253\240\357\274\232\346\214\202\345\215\260\345\260\201\345\210\200\357\274\214\346\225\260\346\215\256\347\261\273\345\236\213\350\275\254\346\215\242\345\267\245\345\216\202\350\256\276\350\256\241\345\256\236\347\216\260.md" "b/docs/md/spring/develop-spring/2021-08-09-\347\254\25418\347\253\240\357\274\232\346\214\202\345\215\260\345\260\201\345\210\200\357\274\214\346\225\260\346\215\256\347\261\273\345\236\213\350\275\254\346\215\242\345\267\245\345\216\202\350\256\276\350\256\241\345\256\236\347\216\260.md" index e8eb4d0dd..eea0feb21 100644 --- "a/docs/md/spring/develop-spring/2021-08-09-\347\254\25418\347\253\240\357\274\232\346\214\202\345\215\260\345\260\201\345\210\200\357\274\214\346\225\260\346\215\256\347\261\273\345\236\213\350\275\254\346\215\242\345\267\245\345\216\202\350\256\276\350\256\241\345\256\236\347\216\260.md" +++ "b/docs/md/spring/develop-spring/2021-08-09-\347\254\25418\347\253\240\357\274\232\346\214\202\345\215\260\345\260\201\345\210\200\357\274\214\346\225\260\346\215\256\347\261\273\345\236\213\350\275\254\346\215\242\345\267\245\345\216\202\350\256\276\350\256\241\345\256\236\347\216\260.md" @@ -1,9 +1,9 @@ --- -title: 【付费】第18章:数据类型转换工厂设计实现 -pay: https://articles.zsxq.com/id_d0cpbs31880x.html +title: 第18章:数据类型转换工厂设计实现 +lock: need --- -# 【付费】挂印封刀,数据类型转换工厂设计实现 +# 第18章:挂印封刀,数据类型转换工厂设计实现 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) @@ -11,6 +11,13 @@ pay: https://articles.zsxq.com/id_d0cpbs31880x.html > 沉淀、分享、成长,让自己和他人都能有所收获!😄 +## 零、优秀作业 + +- [类型转换机制设计 @Rechie](https://t.zsxq.com/06jiAub2n) +- [Spring 全核心类图总结 @NibNait](https://t.zsxq.com/07vFe2vJ6) +- [逐渐完善Spring全体系流程图 @Alex](https://t.zsxq.com/08YsTAwMZ) +- [类型转换也就是数据转换,从 String 转为 Integer、String 转为 Date、Double 转为 Long 都是很常用的功能 @水中捞月](https://t.zsxq.com/09SaqdeaX) + ## 一、前言 `值得的,总是在精雕细琢!` @@ -25,6 +32,432 @@ pay: https://articles.zsxq.com/id_d0cpbs31880x.html 类型转换也可以叫做数据转换,比如从String到Integer、从String到Date、从Dubbo到Long等等,但这些操作不能在已经使用框架的情况下还需要手动处理,所以我们要把这样的功能扩展到Spring框架中。 -## 三、优秀作业 +## 三、设计 + +如果我们来把只是看上去一个简单的类型转换操作抽象成框架,那么它就需要一个标准的接口,谁实现这个接口就具备类型转换的具体实现,提供类型转换的能力。那么在有了这样接口后,还需要类型转换服务的注册、工厂等内容,才可以把类型转换抽象成一个组件服务。整体设计结构如下图: + +![](https://bugstack.cn/assets/images/spring/spring-18-01.png) + +- 首先从工厂出发我们需要实现一个 `ConversionServiceFactoryBean` 来对类型转换服务进行操作。 +- 而实现类型转换的服务,需要定义 `Converter` 转换类型、`ConverterRegistry` 注册类型转换功能,另外转换类型的操作较多,所以这里也会需要定义一个类型转换工厂 `ConverterFactory` 各个具体的转换操作来实现这个工厂接口。 + +## 四、实现 + +### 1. 工程结构 + +```java +small-spring-step-17 +└── src + ├── main + │ └── java + │ └── cn.bugstack.springframework + │ ├── aop + │ │ ├── aspectj + │ │ │ └── AspectJExpressionPointcut.java + │ │ │ └── AspectJExpressionPointcutAdvisor.java + │ │ ├── framework + │ │ │ ├── adapter + │ │ │ │ └── MethodBeforeAdviceInterceptor.java + │ │ │ ├── autoproxy + │ │ │ │ └── MethodBeforeAdviceInterceptor.java + │ │ │ ├── AopProxy.java + │ │ │ ├── Cglib2AopProxy.java + │ │ │ ├── JdkDynamicAopProxy.java + │ │ │ ├── ProxyFactory.java + │ │ │ └── ReflectiveMethodInvocation.java + │ │ ├── AdvisedSupport.java + │ │ ├── Advisor.java + │ │ ├── BeforeAdvice.java + │ │ ├── ClassFilter.java + │ │ ├── MethodBeforeAdvice.java + │ │ ├── MethodMatcher.java + │ │ ├── Pointcut.java + │ │ ├── PointcutAdvisor.java + │ │ └── TargetSource.java + │ ├── beans + │ │ ├── factory + │ │ │ ├── annotation + │ │ │ │ ├── Autowired.java + │ │ │ │ ├── AutowiredAnnotationBeanPostProcessor.java + │ │ │ │ ├── Qualifier.java + │ │ │ │ └── Value.java + │ │ │ ├── config + │ │ │ │ ├── AutowireCapableBeanFactory.java + │ │ │ │ ├── BeanDefinition.java + │ │ │ │ ├── BeanFactoryPostProcessor.java + │ │ │ │ ├── BeanPostProcessor.java + │ │ │ │ ├── BeanReference.java + │ │ │ │ ├── ConfigurableBeanFactory.java + │ │ │ │ ├── InstantiationAwareBeanPostProcessor.java + │ │ │ │ └── SingletonBeanRegistry.java + │ │ │ ├── support + │ │ │ │ ├── AbstractAutowireCapableBeanFactory.java + │ │ │ │ ├── AbstractBeanDefinitionReader.java + │ │ │ │ ├── AbstractBeanFactory.java + │ │ │ │ ├── BeanDefinitionReader.java + │ │ │ │ ├── BeanDefinitionRegistry.java + │ │ │ │ ├── CglibSubclassingInstantiationStrategy.java + │ │ │ │ ├── DefaultListableBeanFactory.java + │ │ │ │ ├── DefaultSingletonBeanRegistry.java + │ │ │ │ ├── DisposableBeanAdapter.java + │ │ │ │ ├── FactoryBeanRegistrySupport.java + │ │ │ │ ├── InstantiationStrategy.java + │ │ │ │ └── SimpleInstantiationStrategy.java + │ │ │ ├── support + │ │ │ │ └── XmlBeanDefinitionReader.java + │ │ │ ├── Aware.java + │ │ │ ├── BeanClassLoaderAware.java + │ │ │ ├── BeanFactory.java + │ │ │ ├── BeanFactoryAware.java + │ │ │ ├── BeanNameAware.java + │ │ │ ├── ConfigurableListableBeanFactory.java + │ │ │ ├── DisposableBean.java + │ │ │ ├── FactoryBean.java + │ │ │ ├── HierarchicalBeanFactory.java + │ │ │ ├── InitializingBean.java + │ │ │ ├── ListableBeanFactory.java + │ │ │ ├── ObjectFactory.java + │ │ │ └── PropertyPlaceholderConfigurer.java + │ │ ├── BeansException.java + │ │ ├── PropertyValue.java + │ │ └── PropertyValues.java + │ ├── context + │ │ ├── annotation + │ │ │ ├── ClassPathBeanDefinitionScanner.java + │ │ │ ├── ClassPathScanningCandidateComponentProvider.java + │ │ │ └── Scope.java + │ │ ├── event + │ │ │ ├── AbstractApplicationEventMulticaster.java + │ │ │ ├── ApplicationContextEvent.java + │ │ │ ├── ApplicationEventMulticaster.java + │ │ │ ├── ContextClosedEvent.java + │ │ │ ├── ContextRefreshedEvent.java + │ │ │ └── SimpleApplicationEventMulticaster.java + │ │ ├── support + │ │ │ ├── AbstractApplicationContext.java + │ │ │ ├── AbstractRefreshableApplicationContext.java + │ │ │ ├── AbstractXmlApplicationContext.java + │ │ │ ├── ApplicationContextAwareProcessor.java + │ │ │ ├── ClassPathXmlApplicationContext.java + │ │ │ └── ConversionServiceFactoryBean.java + │ │ ├── ApplicationContext.java + │ │ ├── ApplicationContextAware.java + │ │ ├── ApplicationEvent.java + │ │ ├── ApplicationEventPublisher.java + │ │ ├── ApplicationListener.java + │ │ └── ConfigurableApplicationContext.java + │ ├── core + │ │ ├── convert + │ │ │ ├── converter + │ │ │ │ ├── Converter.java + │ │ │ │ ├── ConverterFactory.java + │ │ │ │ ├── ConverterRegistry.java + │ │ │ │ └── GenericConverter.java + │ │ │ ├── support + │ │ │ │ ├── DefaultConversionService.java + │ │ │ │ ├── GenericConversionService.java + │ │ │ │ └── StringToNumberConverterFactory.java + │ │ │ └── ConversionService.java + │ │ └── io + │ │ ├── ClassPathResource.java + │ │ ├── DefaultResourceLoader.java + │ │ ├── FileSystemResource.java + │ │ ├── Resource.java + │ │ ├── ResourceLoader.java + │ │ └── UrlResource.java + │ ├── stereotype + │ │ └── Component.java + │ └── utils + │ ├── ClassUtils.java + │ └── StringValueResolver.java + └── test + └── java + └── cn.bugstack.springframework.test + ├── bean + │ └── Husband.java + ├── bean + │ ├── ConvertersFactoryBean.java + │ ├── StringToIntegerConverter.java + │ └── StringToLocalDateConverter.java + └── ApiTest.java +``` + +**工程源码**:`公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码` + +### 2. 定义类型转换接口 + +**包:cn.bugstack.springframework.core.convert.converter** + +**类型转换处理接口** + +```java +public interface Converter { + + /** Convert the source object of type {@code S} to target type {@code T}. */ + T convert(S source); + +} +``` + +**类型转换工厂** + +```java +public interface ConverterFactory{ + + /** + * Get the converter to convert from S to target type T, where T is also an instance of R. + * @param the target type + * @param targetType the target type to convert to + * @return a converter from S to T + */ + Converter getConverter(Class targetType); + +} +``` + +**类型转换注册接口** + +```java +public interface ConverterRegistry { + + /** + * Add a plain converter to this registry. + * The convertible source/target type pair is derived from the Converter's parameterized types. + * @throws IllegalArgumentException if the parameterized types could not be resolved + */ + void addConverter(Converter converter); + + /** + * Add a generic converter to this registry. + */ + void addConverter(GenericConverter converter); + + /** + * Add a ranged converter factory to this registry. + * The convertible source/target type pair is derived from the ConverterFactory's parameterized types. + * @throws IllegalArgumentException if the parameterized types could not be resolved + */ + void addConverterFactory(ConverterFactory converterFactory); + +} +``` + +- Converter、ConverterFactory、ConverterRegistry,都是用于定义类型转换操作的相关接口,后续所有的实现都需要围绕这些接口来实现,具体的代码功能可以进行调试验证。 + +### 3. 实现类型转换服务 + +**cn.bugstack.springframework.core.convert.support.DefaultConversionService** + +```java +public class DefaultConversionService extends GenericConversionService{ + + public DefaultConversionService() { + addDefaultConverters(this); + } + + public static void addDefaultConverters(ConverterRegistry converterRegistry) { + // 添加各类类型转换工厂 + converterRegistry.addConverterFactory(new StringToNumberConverterFactory()); + } + +} +``` + +- DefaultConversionService 是继承 GenericConversionService 的实现类,而 GenericConversionService 实现了 ConversionService, ConverterRegistry 两个接口,用于 canConvert 判断和转换接口 convert 操作。 + +### 4. 创建类型转换工厂 + +**cn.bugstack.springframework.context.support.ConversionServiceFactoryBean** + +```java +public class ConversionServiceFactoryBean implements FactoryBean, InitializingBean { + + @Nullable + private Set converters; + + @Nullable + private GenericConversionService conversionService; + + @Override + public ConversionService getObject() throws Exception { + return conversionService; + } + + @Override + public Class getObjectType() { + return conversionService.getClass(); + } + + @Override + public boolean isSingleton() { + return true; + } + + @Override + public void afterPropertiesSet() throws Exception { + this.conversionService = new DefaultConversionService(); + registerConverters(converters, conversionService); + } + + private void registerConverters(Set converters, ConverterRegistry registry) { + if (converters != null) { + for (Object converter : converters) { + if (converter instanceof GenericConverter) { + registry.addConverter((GenericConverter) converter); + } else if (converter instanceof Converter) { + registry.addConverter((Converter) converter); + } else if (converter instanceof ConverterFactory) { + registry.addConverterFactory((ConverterFactory) converter); + } else { + throw new IllegalArgumentException("Each converter object must implement one of the " + + "Converter, ConverterFactory, or GenericConverter interfaces"); + } + } + } + } + + public void setConverters(Set converters) { + this.converters = converters; + } + +} +``` + +- 有了 FactoryBean 的实现就可以完成工程对象的操作,可以提供出转换对象的服务 GenericConversionService,另外在 afterPropertiesSet 中调用了注册转换操作的类。最终这个类会被配置到 spring.xml 中在启动的过程加载。 + +### 5. 类型转换服务使用 + +**cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory** + +```java +protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) { + try { + PropertyValues propertyValues = beanDefinition.getPropertyValues(); + for (PropertyValue propertyValue : propertyValues.getPropertyValues()) { + String name = propertyValue.getName(); + Object value = propertyValue.getValue(); + if (value instanceof BeanReference) { + // A 依赖 B,获取 B 的实例化 + BeanReference beanReference = (BeanReference) value; + value = getBean(beanReference.getBeanName()); + } + // 类型转换 + else { + Class sourceType = value.getClass(); + Class targetType = (Class) TypeUtil.getFieldType(bean.getClass(), name); + ConversionService conversionService = getConversionService(); + if (conversionService != null) { + if (conversionService.canConvert(sourceType, targetType)) { + value = conversionService.convert(value, targetType); + } + } + } + // 反射设置属性填充 + BeanUtil.setFieldValue(bean, name, value); + } + } catch (Exception e) { + throw new BeansException("Error setting property values:" + beanName + " message:" + e); + } +} +``` + +- 在 AbstractAutowireCapableBeanFactory#applyPropertyValues 填充属性的操作中,具体使用了类型转换的功能。 +- 在 AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues 也有同样的属性类型转换操作。 + +## 五、测试 + +### 1. 事先准备 + +```java +public class Husband { + + private String wifiName; + + private Date marriageDate; // 添加一个日期类的转换操作 + + // ... get/set +} +``` + +**转换时间的操作类** + +```java +public class StringToLocalDateConverter implements Converter { + + private final DateTimeFormatter DATE_TIME_FORMATTER; + + public StringToLocalDateConverter(String pattern) { + DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(pattern); + } + + @Override + public LocalDate convert(String source) { + return LocalDate.parse(source, DATE_TIME_FORMATTER); + } + +} +``` + +- Husband 是一个基础对象类设置了时间属性,之后再添加一个类型转换的操作用于转换时间信息。 + +### 2. 属性配置文件 + +**spring.xml** + +```xml + + + + + + + + + + +``` + +- 配置基础Bean对象,设置属性的日期,同时再添加类型转换的服务和自己实现的 `ConvertersFactoryBean` + +### 3. 单元测试 + +```java +@Test +public void test_convert() { + ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml"); + Husband husband = applicationContext.getBean("husband", Husband.class); + System.out.println("测试结果:" + husband); +} + +@Test +public void test_StringToIntegerConverter() { + StringToIntegerConverter converter = new StringToIntegerConverter(); + Integer num = converter.convert("1234"); + System.out.println("测试结果:" + num); +} + +@Test +public void test_StringToNumberConverterFactory() { + StringToNumberConverterFactory converterFactory = new StringToNumberConverterFactory(); + Converter stringToIntegerConverter = converterFactory.getConverter(Integer.class); + System.out.println("测试结果:" + stringToIntegerConverter.convert("1234")); + Converter stringToLongConverter = converterFactory.getConverter(Long.class); + System.out.println("测试结果:" + stringToLongConverter.convert("1234")); +} +``` + +**测试结果** + +```java +测试结果:Husband{wifiName='你猜', marriageDate=Sun Aug 08 00:00:00 CST 2021} + +Process finished with exit code 0 +``` + +- 这个测试内容还是比较简单的,可以自行验证结果,虽然最终的结果看上去比较简单,但整个框架结构实现设计还是蛮复杂的,把这么一个转换操作抽象为接口适配、工厂模型等方式,还是很值得借鉴的。 + +## 六、总结 + +- 本章节实现的类型转换操作如果只是功能性的开发,就像你自己承接的需求那样,可能只是简单的if判断就搞定了,但放在一个成熟的框架要中要考虑的是可复用性、可扩展性,所以会看到接口的定义、工厂的使用等等设计模式在这里体现。 +- 最后非常感谢你能坚持学习到这个章节,如果你在学习的过程也是每一个章节都是对着文章、写着代码代码、调试着bug,感悟着设计,那么你一定会在这个过程中得到很多很多,以后再阅读Spring的源码也就不会感觉那么难了。 -- [类型转换机制设计 @Rechie](https://t.zsxq.com/06jiAub2n) \ No newline at end of file diff --git "a/docs/md/spring/spring-cloud/2019-11-01-Spring Cloud\344\270\200\343\200\212\346\234\215\345\212\241\351\233\206\347\276\244\346\263\250\345\206\214\344\270\216\345\217\221\347\216\260 Eureka\343\200\213.md" "b/docs/md/spring/spring-cloud/2019-11-01-Spring Cloud\344\270\200\343\200\212\346\234\215\345\212\241\351\233\206\347\276\244\346\263\250\345\206\214\344\270\216\345\217\221\347\216\260 Eureka\343\200\213.md" index abe8bc82f..683756cad 100644 --- "a/docs/md/spring/spring-cloud/2019-11-01-Spring Cloud\344\270\200\343\200\212\346\234\215\345\212\241\351\233\206\347\276\244\346\263\250\345\206\214\344\270\216\345\217\221\347\216\260 Eureka\343\200\213.md" +++ "b/docs/md/spring/spring-cloud/2019-11-01-Spring Cloud\344\270\200\343\200\212\346\234\215\345\212\241\351\233\206\347\276\244\346\263\250\345\206\214\344\270\216\345\217\221\347\216\260 Eureka\343\200\213.md" @@ -151,7 +151,7 @@ eureka: ``` ## 测试验证 -1. 配置host;127.0.0.1 node1 node2 node3 +1. 配置host;127.0.0.1 node01 node02 node03 2. 分别启动node1、node2、node3 3. 访问;http://localhost:8081/ ![微信公众号:bugstack虫洞栈 & Eureka集群](https://bugstack.cn/assets/images/pic-content/2019/11/SpringCloud-1-2.jpg) diff --git a/docs/md/zsxq/booklet/bytecode.md b/docs/md/zsxq/booklet/bytecode.md index 3bb775494..7ddad690e 100644 --- a/docs/md/zsxq/booklet/bytecode.md +++ b/docs/md/zsxq/booklet/bytecode.md @@ -7,6 +7,7 @@ lock: no 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0eHOK4Ad2](https://t.zsxq.com/0eHOK4Ad2) - 课程入口 > 沉淀、分享、成长,让自己和他人都能有所收获!😄 @@ -14,22 +15,31 @@ lock: no **但全书共计107页,11万7千字,20个章节涵盖三个字节码框架和JavaAgent使用并附带整套案例源码!** -![](https://bugstack.cn/assets/images/illustration/让人怪不好意思的.png) +

+ +
**讲道理**,市面上以及网络搜索中都基本很少有成体系的关于字节码编程的知识,这主要由于大部分开发人员其实很少接触这部分内容,包括;`ASM`、`Javassist`、`Byte-buddy`以及`JavaAgent`,没有很大的市场也就没有很多的资料。但大家其实已经从其他的框架或者中间件中使用到,就像你用到的;Cglib、混沌工程、非入侵的全链路监控以及你是否使用过`jetbrains-agent.jar`做了某项实验? -![](https://bugstack.cn/assets/images/illustration/上号Idea.png) +
+ +
所以这样的技术栈一直都萦绕在你身边,只是你还没有发现!当有一天面试问到了,那时你已经170斤工作五年。 **蹭个车告诉你这个知识的重要性**,阿里云的挑战赛! -![](https://bugstack.cn/assets/images/2020/itstack-demo-bytecode-0-3.png) + +
+ +
`读不在三更五鼓,功只怕一曝十寒!`,不一定一本书中就能读出个黄金屋,但脚下路的用什么垫都是自己日积月累。 ## 就这本书他出炉了 -![](https://bugstack.cn/assets/images/2020/itstack-demo-bytecode-0-2.png) +
+ +
## 介绍 diff --git a/docs/md/zsxq/booklet/data-structures.md b/docs/md/zsxq/booklet/data-structures.md new file mode 100644 index 000000000..0dcd67e1c --- /dev/null +++ b/docs/md/zsxq/booklet/data-structures.md @@ -0,0 +1,85 @@ +--- +title: 倚天村 • 图解数据结构 +lock: need +--- + +# 倚天村 • 图解数据结构 + + + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0er8jP4Zc](https://t.zsxq.com/0er8jP4Zc) - 课程入口 + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言:灭绝面试我? + +`为啥,面试官那么喜欢让你聊聊 HashMap?` + +
+ +
+ +**为啥?** 为的是考察你对数据结构的掌握程度(**张无忌不会数据结构,都没法和周芷若在一起**)。因为 HashMap 涉及的东西广,用到的数据结构多,问题延展性好,一个 HashMap 就能聊下来80%的数据结构了。如果你面试遇到灭绝,她会问你; + +1. HashMap 是基于什么样的数据结构实现? +2. HashMap 的哈希桶为什么不用链表实现? +3. HashMap 的哈希计算如何尽可能降低元素的碰撞? +4. HashMap 用到的什么散列算法;平方散列?斐波那契散列?哈希散列(扰动函数)? +5. HashMap 链表 + 红黑树来装填碰撞元素,AVL树和二叉堆也是树形结构可以替换红黑树吗,为什么? +6. HashMap 的红黑树与2-3树有什么关系,B-树都包括哪些?Binary Search Tree 是B-树吗? +7. HashMap 的红黑树什么时候左旋、什么时候右旋、什么时候染色? +8. HashMap 的哈希桶,散列元素和布隆过滤器有相似的地方? +9. 除以上谈到的数据结构之外,你还了解并查集和图吗? + +--- + +以上这些来自**灭绝**的问题,你工作几年了?回答的上来吗? + +在最初从事编程的开发头2年,小傅哥也没法系统的回答这些问题。但自从加入互联网以后,在面对一些高并发场景的系统性能压榨以后,逐步意识到;代码是对数学逻辑的具体实现。而对涉及了数学的**数据结构**了解不深,那么写出来的代码,只能说是**能用**,但不一定**好用**。 + +为啥说不好用?因为你可能没有使用并查集而使用了非常高时间复杂度的算法统计用户关系、也可能错误的使用了非自平衡的二叉查找树替代B-树导致运营配置规则引擎时候二叉树退化成链表、还可能为了判断用户是否存在直接搂数据库查询没有使用布隆过滤器!等等,这样的做法,它不能让你的程序不能运行,只是不健康的运行。当有大量的流量洪峰过来时,势必会让你的系统瘫痪。 + +**所以**,为了让更多的研发伙伴透彻的学会这些内容,小傅哥编写了这本《新手村 · 图解数据结构》,用我的**学习套路**向你讲解数据结构是如此可以承上启下,循序渐进的学习并掌握的。 + +**全书共计4章14节,215页4.2万字100+张图片,耗时3个月完成。涵盖4类14种数据结构,包括:链表、数组、队列、堆栈、哈希表、堆、字典树、二分搜索树、平衡二叉树、2-3树、红黑树、并查集、图、布隆过滤器** + +💋`鉴于作者水平有限`,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。 + +## 二、介绍:小傅哥新书! + +
+ + +
+
免费下载PDF:关注公众号【bugstack虫洞栈】回复【数据结构】
+
+
+ +**Hello, world of java data-structures!** 你好,Java 数据结构的世界! + +欢迎来到这里,很高兴你能拿到这本书。如果你能坚持看完书中每章节的内容,那么不仅可以在你的面试求职上有所帮助,也更能让你对 Java 数据结构有更加深入的学习。 + +**《倚天村 • 图解数据结构》** 是一本通过 Java 语言渐进式的讲解数据结构的书籍,通过循序渐进的方式介绍每一个数据的结构的设计和实现。本书分为4章14节,分别介绍了:链表、数组、队列、堆栈、哈希表、堆、字典树、二分搜索树、平衡二叉树、2-3树、红黑树、并查集、图、布隆过滤器。在学习的过程中不要只看文档,一定要对照源码进行学习,这样才能更好的掌握这些知识。 + +### 1. 适合人群 + +1. 具备基本编程技能,在校大学生和工作的研发人员 +2. 对数据结构感兴趣,但总感觉看不懂的 +3. 看了太多理论,但没有实践验证的 +4. 求职面试,总被面试题搞的死去活来的 + +### 2. 阅读建议 + +本书虽然是源码分析、理论实践,但并不会让读者感觉枯燥。作者:小傅哥,在每一篇的知识里都通过对数据结构的实践和配图来讲解。小伙伴在阅读的时候可以对照源码实践,并且在源码中还包括了一些必备的原图稿件方便做笔记。希望这本书彻底教会你数据结构,也让所有**认真阅读的读者**,学习后都能`让懂了就是真的懂`! + +### 3. 下载PDF📚 + +- **下载**:[https://t.zsxq.com/06bMjiiy7](https://t.zsxq.com/06bMjiiy7) +- **源码**:[https://gitcode.net/KnowledgePlanet/algorithms/java-algorithms](https://gitcode.net/KnowledgePlanet/algorithms/java-algorithms) + +**每一本原创资料的PDF输出,都要在1~3个月甚至半年时间;整理资料、编写文章、开发代码,再到PDF的封面的设计和内容的归纳。** 也因此希望读者伙伴可以在获取资料的同时,**留言**、**分享**、**点赞**支持,我非常需要你的帮忙!非常感谢! \ No newline at end of file diff --git a/docs/md/zsxq/booklet/idea-plugin.md b/docs/md/zsxq/booklet/idea-plugin.md index 95f5f9b1b..69f2ade71 100644 --- a/docs/md/zsxq/booklet/idea-plugin.md +++ b/docs/md/zsxq/booklet/idea-plugin.md @@ -7,6 +7,7 @@ lock: no 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0eXUe12Vr](https://t.zsxq.com/0eXUe12Vr) - 课程入口 >沉淀、分享、成长,让自己和他人都能有所收获!😄 @@ -20,7 +21,9 @@ lock: no 所以🌶,又一套**成系列体系**的`《IDEA Plugin 开发手册》`内容已经为有需要的你准备好啦: -![](https://bugstack.cn/images/article/knowledge/knowledge-220123-01.png) +
+ +
- 此开发手册,分为4章12节循序渐进的通过实践案例开发的方式,串联 IDEA Plugin 开发的各项常用技术点,为读者讲解如何开发一个 IDEA 插件。 - 基本开发类知识点包括:`gradle 工程创建`、`插件发布`、`Swing UI`、`各类窗体`、`菜单配置`、`工程上下文对象`、`向导步骤`、`内容存放`、`配置加载`等,通过这些知识在案例中的逐个使用,而学习如何开发插件。 @@ -31,7 +34,9 @@ lock: no `vo2dto,一个已经被下载1000+的插件` -![](https://bugstack.cn/images/article/knowledge/knowledge-220123-02.png) +
+ +
这是小傅哥开发的一款用于帮助使用 IDEA 编写代码的研发人员,快速生成两个对象转换过程中所需要大量的 `x.set(y.get)` 代码块的 vo2dto 插件工具。*可以直接在 IDEA 中搜索安装使用* @@ -45,7 +50,9 @@ lock: no ## 三、别说了,上干货吧! -![](https://bugstack.cn/images/article/knowledge/knowledge-220123-04.png) +
+ +
**Hello, world of idea plugin !** 你好,IDEA 插件的世界!欢迎来到这里,很高兴你能拿到这本书! @@ -83,8 +90,7 @@ IDEA 插件开发可以帮助研发人员提升能效,解决一些实际场景 ### 2. 下载方式 -- CSDN:[https://download.csdn.net/download/Yao__Shun__Yu/77484299](https://download.csdn.net/download/Yao__Shun__Yu/77484299) - `¥4.9` -- 知识星球(`码农会锁`):[https://t.zsxq.com/ufmQnA2](https://t.zsxq.com/ufmQnA2) - `知识星球用户可直接免费下载,不需要单独付费。此外知识星球还提供了简历优化、实战DDD秒杀项目、架构设计、PPT画架构等内容` +- 下载:[https://t.zsxq.com/ufmQnA2](https://t.zsxq.com/ufmQnA2) - `知识星球用户可直接免费下载,不需要单独付费。此外知识星球还提供了简历优化、实战DDD秒杀项目、架构设计、PPT画架构等内容` ## 五、🎉收尾 diff --git a/docs/md/zsxq/booklet/java-design.md b/docs/md/zsxq/booklet/java-design.md new file mode 100644 index 000000000..3963e1fac --- /dev/null +++ b/docs/md/zsxq/booklet/java-design.md @@ -0,0 +1,123 @@ +--- +title: 重学 Java 设计模式 +lock: no +--- + +# 重学 Java 设计模式 - 18万字271页的实战编程资料 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0eoDwY8Kd](https://t.zsxq.com/0eoDwY8Kd) - 课程入口 + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +**全书共计22个真实业务场景对应59组案例工程、编写了18万字271页的PDF、从5月20日开始耗时50天打造完成。** + +
+ +
+ +💋`鉴于作者水平有限`,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。 + +## 二、简介 + +
+ +
+ +欢迎来到这里,很高兴你`将`拿到这本电子书,如果你能坚持看完并按照书中的例子进行实践,那么在编程开发的世界里,就又多了一个可以写出良好代码的人,同时也为架构师培养储备了一个人才。 + +可能在此之前你也多少了解过设计模式,但在实际的业务开发中使用却不多,多数时候都是大面积堆积`ifelse`组装业务流程,对于一次次的需求迭代和逻辑补充,只能东拼西凑`Ctrl+C`、`Ctrl+V`。 + +所以为了能让更多的程序员👨‍💻‍更好的接受设计思想和架构思维,并能运用到实际的业务场景。本书的作者`小傅哥`,投入50天时间,从互联网实际业务开发中抽离出,交易、营销、秒杀、中间件、源码等22个真实场景,来学习设计模式实践使用的应用可上手技能。 + +### 1. 谁发明了设计模式? + +设计模式的概念最早是由 `克里斯托佛·亚历山大` 在其著作 `《建筑模式语言》` 中首次提出的。 本书介绍了城市设计的 “语言”,提供了253个描述城镇、邻里、住宅、花园、房间及西部构造的模式, 而此类 “语言” 的基本单元就是模式。后来,`埃里希·伽玛`、 `约翰·弗利赛德斯`、 `拉尔夫·约翰逊` 和 `理查德·赫尔姆` 这四位作者接受了模式的概念。 1994 年, 他们出版了 `《设计模式: 可复用面向对象软件的基础》` 一书, 将设计模式的概念应用到程序开发领域中。 + +其实有一部分人并没有仔细阅读过设计模式的相关书籍和资料,但依旧可以编写出优秀的代码。这主要是由于在经过众多项目的锤炼和对程序设计的不断追求,从而在多年编程历程上提炼出来的心得体会。而这份经验最终会与设计模式提到的内容几乎一致,同样会要求高内聚、低耦合、可扩展、可复用。你可能也遇到类似的经历,在学习一些框架的源码时,发现它里的某些设计和你在做开发时一样。 + +### 2. 我怎么学不会设计模式? + +钱也花了,书也买了。代码还是一坨一坨的!设计模式是由多年的经验提炼出来开发指导思想。就像我告诉你自行车怎么骑、汽车怎么开,但只要你没跑过几千公里,你能记住的只是理论,想上道依旧很慌! + +**所以**,本设计模式专题系列开始,会带着你使用设计模式的思想去优化代码。从而学习设计模式的心得并融入给自己。当然这里还需要多加练习,一定是*人车合一*,才能站在设计模式的基础上构建出更加合理的代码。 + +### 3. 适合人群 + +1. 具备一定编程基础在工作1-3年的研发人员 +2. 希望通过此书提升编码思维,剔除到代码中的坏味道 +3. 有意愿成为架构师,但还处在一定瓶颈期 +4. 学习过设计模式,可是一直想找到一本可以落地真实场景参照的书籍 + +### 4. 我能学到什么 + +1. 优化平时开发中的ifelse语句,让代码更加整洁 +2. 看设计模式不再是用理论生搬硬套,这次可以有点用 +3. 站在更高的角度去看待编程开发,学会更多的面向对象的思维,尤其是;接口、抽象类、多态等使用 +4. 升职、加薪,良好的代码是效能提升的基础,成为本组编码最靓的精神小伙 + +### 5. 阅读建议 + +本书属于实战型而不是理论介绍类书籍,每一章节都有对应的完整代码,学习的过程需要参考书中的章节与代码一起学习,同时在学习的过程中需要了解并运行代码。学习完成后进行知识点的总结,以及思考🤔这样的设计模式在自己的业务场景中需要如何使用。 + +## 三、书中目录 + +设计模式遵循六大原则;单一职责(`一个类和方法只做一件事`)、里氏替换(`多态,子类可扩展父类`)、依赖倒置(`细节依赖抽象,下层依赖上层`)、接口隔离(`建立单一接口`)、迪米特原则(`最少知道,降低耦合`)、开闭原则(`抽象架构,扩展实现`),会在具体的设计模式章节中,进行体现。 + +### 1. 创建型模式 + +**这类模式提供创建对象的机制, 能够提升已有代码的灵活性和可复用性。** + +| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 | +| ---- | ------------ | --------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------ | +| 1 | **工厂方法** | ![](https://bugstack.cn/assets/images/2020/design/11.png) | 多种类型商品不同接口,统一发奖服务搭建场景 | 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 | +| 2 | **抽象工厂** | ![](https://bugstack.cn/assets/images/2020/design/12.png) | 替换Redis双集群升级,代理类抽象场景 | 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 | +| 3 | **生成器** | ![](https://bugstack.cn/assets/images/2020/design/13.png) | 各项装修物料组合套餐选配场景 | 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。 | +| 4 | **原型** | ![](https://bugstack.cn/assets/images/2020/design/14.png) | 上机考试多套试,每人题目和答案乱序排列场景 | 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 | +| 5 | **单例** | ![](https://bugstack.cn/assets/images/2020/design/15.png) | 7种单例模式案例,Effective Java 作者推荐枚举单例模式 | 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 | + +### 2. 结构型模式 + +**这类模式介绍如何将对象和类组装成较大的结构, 并同时保持结构的灵活和高效。** + +| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 | +| ---- | ---------- | --------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1 | **适配器** | ![](https://bugstack.cn/assets/images/2020/design/21.png) | 从多个MQ消息体中,抽取指定字段值场景 | 将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 | +| 2 | **桥接** | ![](https://bugstack.cn/assets/images/2020/design/22.png) | 多支付渠道(微信、支付宝)与多支付模式(刷脸、指纹)场景 | 将抽象部分与实现部分分离,使它们都可以独立的变化。 | +| 3 | **组合** | ![](https://bugstack.cn/assets/images/2020/design/23.png) | 营销差异化人群发券,决策树引擎搭建场景 | 将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 | +| 4 | **装饰** | ![](https://bugstack.cn/assets/images/2020/design/24.png) | SSO单点登录功能扩展,增加拦截用户访问方法范围场景 | 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。 | +| 5 | **外观** | ![](https://bugstack.cn/assets/images/2020/design/25.png) | 基于SpringBoot开发门面模式中间件,统一控制接口白名单场景 | 为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 | +| 6 | **享元** | ![](https://bugstack.cn/assets/images/2020/design/26.png) | 基于Redis秒杀,提供活动与库存信息查询场景 | 运用共享技术有效地支持大量细粒度的对象。 | +| 7 | **代理** | ![](https://bugstack.cn/assets/images/2020/design/27.png) | 模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景 | 为其他对象提供一种代理以控制对这个对象的访问。 | + +### 3. 行为模式 + +**这类模式负责对象间的高效沟通和职责委派。** + +| 序号 | 类型 | 图稿 | 业务场景 | 实现要点 | +| ---- | ------------ | ---------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1 | **责任链** | ![](https://bugstack.cn/assets/images/2020/design/31.png) | 模拟618电商大促期间,项目上线流程多级负责人审批场景 | 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。 | +| 2 | **命令** | ![](https://bugstack.cn/assets/images/2020/design/32.png) | 模拟高档餐厅八大菜系,小二点单厨师烹饪场景 | 将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。 | +| 3 | **迭代器** | ![](https://bugstack.cn/assets/images/2020/design/33.png) | 模拟公司组织架构树结构关系,深度迭代遍历人员信息输出场景 | 提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。 | +| 4 | **中介者** | ![](https://bugstack.cn/assets/images/2020/design/34.png) | 按照Mybatis原理手写ORM框架,给JDBC方式操作数据库增加中介者场景 | 用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 | +| 5 | **备忘录** | ![](https://bugstack.cn/assets/images/2020/design/35.png) | 模拟互联网系统上线过程中,配置文件回滚场景 | 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 | +| 6 | **观察者** | ![](https://bugstack.cn/assets/images/2020/design/36.png) | 模拟类似小客车指标摇号过程,监听消息通知用户中签场景 | 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 | +| 7 | **状态** | ![](https://bugstack.cn/assets/images/2020/design/37.png) | 模拟系统营销活动,状态流程审核发布上线场景 | 允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。 | +| 8 | **策略** | ![](https://bugstack.cn/assets/images/2020/design/38.png) | 模拟多种营销类型优惠券,折扣金额计算策略场景 | 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。 | +| 9 | **模板方法** | ![](https://bugstack.cn/assets/images/2020/design/39.png) | 模拟爬虫各类电商商品,生成营销推广海报场景 | 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 | +| 10 | **访问者** | ![](https://bugstack.cn/assets/images/2020/design/310.png) | 模拟家长与校长,对学生和老师的不同视角信息的访问场景 | 主要将数据结构与数据操作分离。 | + +*以上图稿和部分描述参考;[https://refactoringguru.cn](https://refactoringguru.cn)、[https://www.runoob.com/design-pattern/visitor-pattern.html](https://www.runoob.com/design-pattern/visitor-pattern.html)* + +## 四、收个尾🎉 + +👣走过的路会留下足迹,👨‍💻‍码过的文会盛满四季。 + +有时候真的很感谢自己还能坚持做原创技术输出,即使再忙再累也给自己一个当下的交代,在写文章的过程中甚至几乎没有过周末,也没有过半夜。但当自己完成每一篇文章后,那份给自己的努力也传播给其他人技术知识。**也希望读者们能给多多点点在看分享和留言,这几乎是支撑我写作的最大动力回馈** + +本书是设计模式实战型书籍📚,编写的过程中常常为找到一个合适并易于理解的场景而抓头发,甚至睡觉中梦到的合适的内容,也要用语音发给自己记录下来。好在50天的坚持终于把这22个设计模式场景写完。如果书中有一些不易于理解的内容,不要担心一定是作者没有描述清楚或找到的案例不适合。可以添加作者小傅哥(fustack)微信,交流相应的技术内容,共同进步。 + +**最后,我想说**:能力,是你前行的最大保障。哪怕你是兢兢业业的工作者,也是拥有`能留下的本事`和`跳出去的能力`,才会相对安稳度过动荡。 \ No newline at end of file diff --git a/docs/md/zsxq/booklet/java-interview.md b/docs/md/zsxq/booklet/java-interview.md index f8be98ebb..46525d23f 100644 --- a/docs/md/zsxq/booklet/java-interview.md +++ b/docs/md/zsxq/booklet/java-interview.md @@ -7,6 +7,7 @@ lock: no 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0efhkcpaq](https://t.zsxq.com/0efhkcpaq) - 课程入口 > 沉淀、分享、成长,让自己和他人都能有所收获!😄 @@ -14,7 +15,9 @@ lock: no **我膨胀了** 💥,在看了大部分以面试讲解的 Java 文章后,发现很多内容的讲解都偏向于`翻译`、`抄书`、`说理论`的给答案式讲解,最终把知识弄的云里雾里。 -![](https://bugstack.cn/assets/images/illustration/swell.png) +
+ +
就像我问你: - HashCode为什么用31作为乘数,你证明过吗? @@ -38,13 +41,15 @@ lock: no ## 二、简介 -![](https://bugstack.cn/assets/images/2020/interview/interview-pdf-1.png) +
+ +
**Hello, world of java !** 你好,java的世界! 欢迎来到这里,很高兴你能拿到这本书。如果你能坚持看完书中每章节的内容,那么不仅可以在你的面试求职上有所帮助,也更能让你对 Java 核心技术有更加深入的学习。 -[《Java 面经手册》](#) 是一本以面试题为入口讲解 Java 核心技术的 PDF 书籍,书中内容也极力的向你证实`代码是对数学逻辑的具体实现`。*为什么这么说?* 当你仔细阅读书籍时,会发现这里有很多数学知识,包括:扰动函数、负载因子、拉链寻址、开放寻址、斐波那契(Fibonacci)散列法还有黄金分割点的使用等等。 +[《Java 面经手册》](https://bugstack.cn/md/zsxq/booklet/java-interview.html) 是一本以面试题为入口讲解 Java 核心技术的 PDF 书籍,书中内容也极力的向你证实`代码是对数学逻辑的具体实现`。*为什么这么说?* 当你仔细阅读书籍时,会发现这里有很多数学知识,包括:扰动函数、负载因子、拉链寻址、开放寻址、斐波那契(Fibonacci)散列法还有黄金分割点的使用等等。 编码只是在确定了研发设计后的具体实现,而设计的部分包括:数据结构、算法逻辑以及设计模式等,而这部分数据结构和算法逻辑在 Java 的核心 API 中体现的淋漓尽致。那么,也就解释了为什么这些内容成为了热点面试题,虽然可能我们都会觉得这样的面试像是造火箭。 @@ -76,7 +81,9 @@ lock: no ### 4. 书籍截图 -![](https://bugstack.cn/assets/images/2020/interview/interview-pdf-2.png) +
+ +
## 三、目录 @@ -126,11 +133,8 @@ lock: no ## 四、PDF📚下载 -1. 《Java 面经手册》PDF 完整版书籍一本 - - CSDN:[https://download.csdn.net/download/Yao__Shun__Yu/14932325](https://download.csdn.net/download/Yao__Shun__Yu/14932325) - `¥4.9` - - 知识星球(`码农会锁`):[https://t.zsxq.com/05YZZjq7m](https://t.zsxq.com/05YZZjq7m) - `知识星球用户可直接免费下载,不需要单独付费。此外知识星球还提供了简历优化、实战DDD秒杀项目、架构设计、PPT画架构等内容` +1. 下载:[https://t.zsxq.com/05YZZjq7m](https://t.zsxq.com/05YZZjq7m) - `知识星球用户可直接免费下载,不需要单独付费。此外知识星球还提供了简历优化、实战DDD秒杀项目、架构设计、PPT画架构等内容` 2. 完整版源码一份,共 27 个案例 -3. 可以加入面经专栏讨论群,添加我的微信:`fustack`,备注:`面经入群` ## 五、🎉收尾 diff --git a/docs/md/zsxq/introduce.md b/docs/md/zsxq/introduce.md index 1c13a597e..e76dd8cdd 100644 --- a/docs/md/zsxq/introduce.md +++ b/docs/md/zsxq/introduce.md @@ -13,52 +13,106 @@ lock: no ## 一、前言:星球介绍 -知识星球是 [小傅哥](https://bugstack.cn/md/zsxq/about/xiaofuge.html) 的 **付费** 编程学习圈,不同于网上的攒出来几百个T的免费资料,在星球内小傅哥会对你的问题 **1v1** 解答,帮你制定学习方案和规划职业路线。目标是针对性的帮每一个同学,通过星球内的有深度的高质量原创项目,提升编程思维、领悟架构经验、吸收设计模式、掌握开发技巧,不只是为了面试,更是为了给自己的职业生涯续期。 +知识星球是 [小傅哥](https://bugstack.cn/md/zsxq/about/xiaofuge.html) 的技术社群,不同于网上的拼凑出来几百个G的免费资料,而是专门为大家的学习诉求进行的原创。在星球内小傅哥会对你的问题 **1v1** 解答,帮你制定学习方案和规划职业路线。目标是针对性的帮每一个同学,通过星球内的有深度的[高质量原创项目](https://bugstack.cn/md/zsxq/other/join.html),提升编程思维、领悟架构经验、吸收设计模式、掌握开发技巧,不只是为了面试,更是为了给自己的职业生涯续期。 如果你正在迷茫,找不到除了CRUD以外的深度项目、不知道该从哪学习、怎么更好的提升自己、如何让自己更有竞争力,以及怎么把自己的能力在简历上秀出来,星球会给你答案,帮你铺路,让你不在做一个躺平仔!—— *身为码农,多半穷苦,谁想躺平!* 知识星球支持 APP【[安卓](https://a.app.qq.com/o/simple.jsp?pkgname=com.unnoo.quan)/[IOS](https://apps.apple.com/cn/app/xiao-mi-quan/id904106920?l=cn)】、[网页端](https://wx.zsxq.com/dweb2/index/group/48411118851818)和小程序,可随时随地使用。每天早/晚坐地铁花几分钟刷一刷,就能收获学习的幸福感。*可能就是某一次的学习,就能帮助你在面试、述职、答辩中脱颖而出!* +1. 从你加入开始,所有过往的课程和积累内容,你都可以学习。从21年的积累到现在。 +2. 加入有效期1年内再新创作项目,都可以学习。 +3. 过期后,星球内从过期日到过去的内容,全部可以学习。 +4. 即使续费也是有效期内5折续费,续费只是相当于续费新项目和小傅哥的服务。 + **星球 APP / 小程序截图**:
- +
**星球 PC 网页版截图**:
- +
-大家可以下载一个知识星球 APP 来提前体验~ +**星球 APP/PC 课程入口** -## 二、为啥:加入星球? +
+ +
-加入星球,你可以获得已经在这条路上摸爬滚打多年的架构师,为你提供的如下服务。 +大家可以下载一个知识星球 APP 来提前体验~ -### 1. 学习项目 +## 二、步骤;使用指引 -很多来自中小公司、外包公司或者尚未毕业的本科生、研究生,手里都没有什么能拿的出手的项目,到大厂去刚一下。 +
+ +
-而加入星球就可以学习到小傅哥为你编写的在大厂水平以上的实战项目,包括你会学习到:分布式架构、微服务设计、领域驱动设计、设计模式时间、规则引擎决策树、秒杀分段锁、API网关通信等等,只有加入大厂才能学习到的核心技能。 +## 三、为啥:加入星球? -#### 1.1 实战项目 +加入星球,你可以获得已经在这条路上摸爬滚打多年的架构师,为你提供的如下服务。 -- [Lottery 分布式抽奖系统 - 基于领域驱动设计的四层架构实践](https://bugstack.cn/md/zsxq/project/lottery.html) -- [Netty+JavaFx实战:仿桌面版微信聊天](https://bugstack.cn/md/zsxq/project/im.html) -- [API网关:中间件设计和实践](https://bugstack.cn/md/zsxq/project/api-gateway.html) +
+ +
-#### 1.2 技术小册 +这是一整套的实战项目学习进阶路线,从小白到大佬,全程视频手把手带着从0到1,一步步完成项目的设计、开发和上线。在整套内容学习过程中,小傅哥为你提供了非常好的技术交流社群,及时解决学习问题。还包括调试你的问题代码,带你快速🔜出坑! -- [Java 面经手册](https://bugstack.cn/md/zsxq/booklet/java-interview.html) -- [IDEA Plugin 开发手册](https://bugstack.cn/md/zsxq/booklet/idea-plugin.html) -- [字节码编程 | ASM、Javassist、Byte-Buddy](https://bugstack.cn/md/zsxq/booklet/bytecode.html) +- 首先,这一整套全体系的学习课程比私教培训实惠,更比培训班上万的培训费便宜。可能也就是培训班1天的💰钱,就能学习到这一整套内容了。 +- 之后,你学习的整套课程,就是小傅哥这个架构师自己全部原创编写的。这也就是说,你所提到的任何问题,小傅哥都能给你解答和讨论。 +- 那么,这么实惠的课程,成体系的课程,还是架构师编写的。还有什么可犹豫的,完全可以撸起来了! -#### 1.3 手撕源码 +### 1. 学习项目 + +很多来自中小公司、外包公司或者尚未毕业的本科生、研究生,手里都没有什么能拿的出手的项目,到大厂去刚一下。 -- [手写Spring:渐进式源码实践](https://bugstack.cn/md/zsxq/source-code/develop-spring.html) -- [手写Mybatis:渐进式源码实践](https://bugstack.cn/md/zsxq/source-code/develop-mybatis.html) +而加入星球就可以学习到小傅哥为你编写的在大厂水平以上的实战项目,包括你会学习到:分布式架构、微服务设计、领域驱动设计、设计模式实现、规则引擎决策树、秒杀分段锁、API网关通信等等,只有加入大厂才能学习到的核心技能。 + +#### 1.1 课程资源 + +**课程链接**:- 整个课程路线参考【课程日历图📅】,对应的资源链接汇总如下; + +- 创新项目(AI) + - 【3星】[(1.20w) AI MCP Gateway 网关服务系统](https://bugstack.cn/md/project/ai-mcp-gateway/ai-mcp-gateway.html) + - 【3星】[(1.87w) Deepseek RAG、MCP、Agent 智能体](https://t.zsxq.com/Zq7hV) + - 【2星】[(0.11w) OpenAI 代码自动评审组件](https://t.zsxq.com/gYEVX) + - 【3星】[(0.77w) OpenAi(ChatGPT\ChatGLM) 微服务应用体系构建](https://t.zsxq.com/19aSkDvYB) + - 【1星】[(0.12w) ChatGPT AI问答助手](https://t.zsxq.com/zGj7F) + +- 业务项目 + - 【3星】[(1.15w) 拼团交易平台系统 - 微服务&分布式](https://t.zsxq.com/3X9GA) + - 【2星】[(0.6w) 小型支付商城系统 - 双架构开发(带小白入门)](https://t.zsxq.com/BDFJJ) + - 【4星】[(1.41w) 大营销平台系统 - 微服务&分布式](https://t.zsxq.com/199mpn9Lt) + - 【4星】[(1.28w) Lottery DDD分布式抽奖系统](https://t.zsxq.com/qrUSd) + - 【2星】[(0.78w) IM实战(Netty+JavaFx):仿桌面版微信聊天](https://t.zsxq.com/NcPgw) + +- 组件项目 + - 【3星】[(0.21w)本地任务消息组件](https://bugstack.cn/md/zsxq/project/local-task-message.html) + - 【3星】[(0.36w) 通用技术组件 - 🔧扳手工程](https://bugstack.cn/md/zsxq/project/xfg-wrench.html) + - 【2星】[(0.23w) BCP透视业务流程 - 监控系统](https://t.zsxq.com/CVzpL) + - 【2星】[(0.15w) 动态线程池组件](https://t.zsxq.com/nSebo) + - 【2星】[(0.31w) 支付SDK设计和开发](https://t.zsxq.com/19WqNkhr2) + - 【5星】[(0.78w) API网关:中间件设计和实践](https://t.zsxq.com/xIe9E) + - 【3星】[(0.38w) SpringBoot 中间件设计和开发](https://t.zsxq.com/LZ82D) + - 【2星】[(0.39w) IDEA Plugin 插件开发](https://t.zsxq.com/VMaSW) + +- 其他项目(源码 + 基础) + + - 【5星】[(1.06w) 手写Mybatis:渐进式源码实践](https://t.zsxq.com/xMQ6W) + - 【3星】[分布式技术栈基础教程](https://t.zsxq.com/Hl0W4) + +#### 1.2 课程收获 + +1. 熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对其使用源码所提供的接口、类、SPI标准开发各类组件,有一定的设计思路和落地能力。 +2. 熟练使用 RPC(Dubbo)、MQ(RabbitMQ、Kafka)、Redis、分库分表、XXL-JOB、Zookeeper、等分布式技术栈,在各个场景的运用。 +3. 深入积累 Spring AI、AI SDK、AI 应用、RAG、MCP、Agent 开发技能和实际项目运用能力 +4. 深度了解 MVC、DDD 架构知识,和框架搭建技巧以及微服务设计思想。 +5. 熟练使用设计模式、设计原则,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。 +6. 熟练运用 Docker、Portainer 等Dev-Ops手段,部署和运维 Nginx、Skywalking、Otter、Canal、ELK 等技术框架。 +7. 具备多类项目的开发能力,业务项目、技术项目、组件项目,以及对应的架构模型和设计思路。 +8. 积累八股问题、项目问题、场景问题的解答技巧,同时会有辅助简历编编写优化和评审,提高面试通过率。 ### 2. 问题解答 @@ -80,6 +134,32 @@ lock: no
+- 提交你的简历:[《面试简历;优化、完善、辅导》简历作业太阳](https://t.zsxq.com/0epLHDzcD) + +#### 3.1 简历模板 + +有好经历,有不错的项目,还需要一套上档次的描述方式,这样才能更加完美的展示出自己的实力!这里小傅哥给大家提供了一个描述经历的结构,这样写的简历会更好的突出自己的能力,提高筛选通过率。 + +
+ +
+ +- 首先,个人信息,可以突出下过往的经验积累,包括;在过往的XXX经历中,积累了XXX场景的经验,使个人具备了XXX方面不错的能力。同时兼具着对(技术/场景)的(喜好/热忱),长期(学习/从事/钻研)某项技术,并做出了对应的(开源)(产品/服务/组件/插件)。此个人XXX发布到XXX市场以后,得到了用户(下载量/点赞量/使用数)的规模,获得了非常好的认可。地址:http://github.com/xxxx/xxxx +- 之后,描述个人`教育背景`、`实习/实践经历`、`专业技能`和项目的编写。专业技能,要注意描述顺序和内容量,不宜过多,但要把招聘中核心技术栈体现出来。紧接着到项目描述,这部分最好有1~2个业务项目 + 1个组件类型项目,这样的组合简历内容,还是非常好用的。 +- 最后,补充一个自我评价。把自己描述成有技术追求的、有团队精神、有奋斗品质的优秀好青年。 + +#### 3.2 编写案例 + +
+ +
+ +
+ +
+ +- 在每个大项目下,提供了简历编写案例和问题汇总。举例;[《拼团交易平台系统》,关于面试中的技能、简历、问题汇总](https://bugstack.cn/md/project/group-buy-market/notes.html) + ### 4. 学习氛围 当然,被这样的“鼓励”下,我的周末😭全用在星球上了,不是录制课程视频,就是手写源码,也因为大家的图越画越牛,我也跟着让自己的图更上层楼。点名:课代表阿曦、新晋卷王奥斯卡最佳配角、画图侠诏无言、优秀作业无名氏🧐等等。 @@ -98,10 +178,28 @@ lock: no - 经过一年的积累,星球中的优秀内容还远不止这些。无论是学习方法、课程资料、学习氛围,都已经建设的非常全面且完整,后续会更加全面。 - 星球中很多的这样的伙伴,都是来自于985、211名校,他们更知道如何节省时间,找到更快学习到干货的路径。*与这样的伙伴做同学,即可节省你的时间,还可以跟着一个有氛围的圈子一起成长。* +- 如果你是学习小白,有不少基础欠缺。可以在这里提交:[《小白学习补给站计划》—— 帮助小白快速补充基础知识!🔥](https://t.zsxq.com/0eE3cp7uK) -## 三、加入:知识星球 +### 5. 用户反馈 -星球价格 **¥129** 一年,老用户续费 **5折** 一年(星球每年都会开发新的学习项目和技术小册等资料)。 +经过小傅哥这么多年的努力,👨🏻‍💻认认真真的做编程项目之下,一点点把来自于互联网真实技术教给社群伙伴。也越来越多的被高校老师、在校学生、企业公司、社群粉丝、海外伙伴的认可。 + +
+ +
+ +- [@待佳人晚归](https://t.zsxq.com/0eAFk84M7):`写Lottery项目前,因为之前没有接触ddd,也没看过傅哥的视频,想先理解各个模块,然后再编写,傅哥讲的很好,代码太优雅了,一下子就上头了。` +- [@念](https://t.zsxq.com/0euNUBW9H):`对Lottery项目分支的开发过程需要多看几遍小傅哥的视频,会有更好的理解.特别是写完之后再去回顾视频,思路会清晰的多` +- [@星期一](https://t.zsxq.com/0chHS92j5):`通过两天的学习成功将第五、第六章节跑通并完成了测试,感受最深的是对策略模式和模板模式的进一步了解,之前在看重学java设计模式的时候,对于很多设计模式都处于一知半解的状态,现在结合这个抽奖系统,让我对这两个设计模式有了不一样的理解与认识,感觉真的很棒。这种边记笔记,边学习的感觉真的很好,以前可能看视频学,一天能看很多章节,但其实很多东西都没有弄懂,现在是一天一个章节,慢慢去理解代码怎么编写,怎么完成。这些天虽然还有些地方理解的不是很清楚,我还是继续接着学,到时候回头再来温习一遍。` +- [@星期一](https://t.zsxq.com/0cmdaowpB):`本身我自己学习是没有画图,记录学习遇到问题的解决方法的习惯,但加入了知识星球,看到里面许多像我一样的学员都开始跟着小傅哥去画图,去记录学习中遇到的问题,我自己也尝试着这样去做,发现学习的思路更加清晰,且记忆更深` +- [@神经蛙](https://t.zsxq.com/0cIVeIzgI):`断断续续花了2个星期终于感觉完整学完了抽奖系统,首先非常感谢小傅哥,该项目确实让我感觉学习到很多优秀的内容,以下是我学习的一个阶段性总结` +- [@L.ast](https://t.zsxq.com/0cJSH02ju):`花费了十天时间把整个抽奖系统的基本内容全部动手实现,对个人而言设计模式、xxljob,kafka,以及路由组件设计这部分收获很多。打算抽一天时间回顾总结项目细节,尤其是对一些订单id和日期id这部分设计的作用,解决什么问题。` +- [@BookSea](https://t.zsxq.com/0cxV12kki):`加入傅哥的星球是我做过最好的投资之一,2022年3月,加入了傅哥的星球。2023年3月,迎来了我的职业生涯的第一次跳槽。` +- [@素质男孩](https://t.zsxq.com/10e3wb5vf):`现在一天差不多只能写一节(我是小卡拉米...)比之前看着视频一步步做项目感觉更充实,比如今天在最后跑单元测试的时候,报了个空指针,然后打了断点,一步一步的往下走,很清晰感受到了整个流程,完整的走下来后,真的感觉不一样,最后发现是queryAwardInfoByAwardId(awardId)没有查到对应的id,查看数据库发现award表比strategy_detail中少了一个奖品。` + +## 四、加入:知识星球 + +星球价格 **¥159** 一年(全网最实惠社群),老用户续费 **5折** 一年(星球每年都会开发新的学习项目和技术小册等资料)。[查看星球项目](https://bugstack.cn/md/zsxq/other/join.html) >加入 3 天内可以全额退款,感兴趣的同学可以先加入体验,自己判断是否有价值。 @@ -114,12 +212,16 @@ lock: no **注意**:加入星球后,阅读`星球🔝置顶消息` [https://t.zsxq.com/05VB66uzz](https://t.zsxq.com/05VB66uzz) - 项目小册、权限申请、资料文件、简历批阅等。 -## 四、星球:适合我吗? +## 五、星球:适合我吗? 那么,小傅哥的码农会锁,私有技术朋友圈,适合什么样的你加入呢?
- + +
+ +
+
1. 即将大学毕业、研究生毕业,但苦于手里没有什么干货项目,都是一堆 CRUD 案例,没有分布式复杂设计,撑不起简历的。 @@ -129,10 +231,14 @@ lock: no **综上**,如果你想多赚一些钱,也希望自己的职业生涯长久一些,有一定的硬核技术积累,在往后的职场中更稳一些,那么可以加入小傅哥的星球。这些足够硬核的内容,可以帮助你更加平稳的度过往后的路,企业不会永远一帆风顺,但只要你手里技术硬,就会相对平稳。 -## 五、关于:星球定价 +## 六、指南:使用星球 + +- [指南:🔜快速了解,开启学习之旅!](https://bugstack.cn/md/zsxq/material/guide.html) + +## 七、关于:星球定价 1. 星球内的服务和实战项目都是小傅哥本人提供和**原创**,相信能够给大家带来**超过该价格的价值** 。举个例子,渐进式手把手带大家做**进大厂才可能看得见的项目**、有笔记有源码、有问题可以提,这比单独买一个课程或一套源码要值得多。其实都不到大城市一节补习班的钱,**哪怕把我的课程时长换算成培训机构的课时,也是便宜的超级多**。 -2. 持续的内容创作 + 回答问题 + 知识星球的运营(简历批阅、就业指导、架构设计) 需要小傅哥每个早上6点-8点以及周末/假期持续维护。如果不提高门槛(本次提价¥20),真的是维护不过来。也希望加入星球的同学都是真的下定了决心想要进步,而不是像免费的交流群和社区一样 “闲聊扯淡”。 +2. 持续的内容创作 + 回答问题 + 知识星球的运营(简历批阅、就业指导、架构设计) 需要小傅哥每个早上6点-8点以及周末/假期持续维护。也希望加入星球的同学都是真的下定了决心想要进步,而不是像免费的交流群和社区一样 “闲聊扯淡”。 3. 希望加入进来的同学能够利用好星球来坚持学习。如果这个星球真的帮助你达成了目标(比如晋升加了薪、跳槽诺了坑、校招进了厂),回过头来你会发现,这绝对是你 最值得的一笔投资 !(免费的东西往往不会珍惜,别问我为什么知道!) 4. 星球仍将随着人数和项目的增加会每次提价,感谢理解!—— 但已付费的加入的用户,续费折扣会很大,相当于只续费小傅哥的服务和新项目费用,没有什么比这更爽的了!**如果当年有人这样对我,我会买它个10年!** diff --git a/docs/md/zsxq/material/architecture_design.md b/docs/md/zsxq/material/architecture_design.md index 1c7d15f60..f2d943523 100644 --- a/docs/md/zsxq/material/architecture_design.md +++ b/docs/md/zsxq/material/architecture_design.md @@ -30,6 +30,10 @@ lock: no - [优惠活动和优惠券过滤条件展示设计](https://t.zsxq.com/05Jau7Ybe) - [设计秒杀流程时,优化锁的颗粒度力度](https://t.zsxq.com/05EujY7IY) - [为什么要采用异步扣减库存呢?](https://t.zsxq.com/05FUVJqjq) +- [邀请新人活动,活动规则和邀请记录怎么维护比较好?](https://t.zsxq.com/07qNBm2V3) +- [电商交易给其他用户打赏设计](https://t.zsxq.com/07euV7a2j) +- [优惠券快过期了,该怎么短信提醒用户,用户优惠券表数据量大的话怎么做合适](https://t.zsxq.com/08Mc52AFC) +- [口令领取红包,并发秒杀场景设计](https://t.zsxq.com/0bkZTurk1) ## 三、电商场景 @@ -43,10 +47,12 @@ lock: no ## 四、外卖场景 - [外卖点餐项目](https://t.zsxq.com/05Ey3fIUN) +- [就是假如有一个外卖项目,项目有单品、套餐两个实体,套餐就是多个单品的集合,然后要实现一个单品停售的功能,要求单品停售时,包含该单品的套餐也要一起停售](https://t.zsxq.com/08YnnhVxn) ## 五、支付交易 - [重构简单版支付中心](https://t.zsxq.com/05MVjmMFI) +- [我支付接口里面有点复杂,里面参杂了一些其它合作商相关需求,可以怎么去设计优化这一块](https://t.zsxq.com/07c20Wpwr) ## 六、功能服务 @@ -59,6 +65,7 @@ lock: no - [导出大量Excel](https://t.zsxq.com/05aIaqjyF) - [现在有没有比较好用的分布式哈希表(DHT,Distributed Hash Table)](https://t.zsxq.com/05QZZjiiq) - [物联网平台数据推送设计](https://t.zsxq.com/05fAUfi2N) +- [我现在要做一个接口计费和计数,接口调用一次 就要计算一次,接口调用之前也要校验是否达到接口调用次数上限了](https://t.zsxq.com/07BQFA6AI) ## 七、通用设计 @@ -84,6 +91,12 @@ lock: no - [动态导出字段设计——类似问卷表](https://t.zsxq.com/056QFaQvj) - [文件传输OSS](https://t.zsxq.com/05ii2fyJu) - [分库分表场景设计](https://t.zsxq.com/05rNniu72) +- [SSO单点登录](https://t.zsxq.com/07j5YTfDg) +- [运营平台需与各线下系统交互](https://t.zsxq.com/07xHH1EZE) +- [PDF 部分在线预览功能](https://t.zsxq.com/07HImi4T3) +- [想问一下各位大佬是如何学习权限的,感觉现在学的完全不够安全](https://t.zsxq.com/08y2ZygbU) +- [A方推送数据到我们平台,我们平台调用B方接口存数据,同时对数据做一些留痕等。](https://t.zsxq.com/08tmSlDOA) +- [咨询个小时维度库存设计问题;现在有这么个场景,下单占用库存,还车释放库存。](https://t.zsxq.com/08VmJAsho) ## 八、性能调优 @@ -94,6 +107,7 @@ lock: no - [慢查询拖垮数据库](https://t.zsxq.com/05y3zvji2) - [海量数据处理](https://t.zsxq.com/05BIei2Fq) - [token太长3000+位往cookie写的时候会有问题](https://t.zsxq.com/052JImmuV) +- [有张表数据量为8000w字段有100多个,这样的表查询效率很慢,如何处理得以提升效率](https://t.zsxq.com/074yWu3X6) ## 九、架构方案 @@ -114,11 +128,16 @@ lock: no - [使用DDD重构的优势是什么?](https://t.zsxq.com/05eyJqjeI) - [分布式任务调度设计](https://t.zsxq.com/05uBa2VVF) - [简化的DDD结构](https://t.zsxq.com/05vnuZfIu) +- [开发一个大系统,包括条码系统、MES系统、OA系统、客户管理系统、供应商管理系统等,架构分层思路](https://t.zsxq.com/06NBAeaA6) +- [微服务项目中MQ有必要单独搞个服务去处理消息吗?](https://t.zsxq.com/07H0diIGB) +- [类似钉钉文档协同编辑的功能](https://t.zsxq.com/088NBXEzT) +- [“当前流水表”的前一天的所有记录 导入到 “历史流水表”,成功后把这些记录从“当前流水表”中删除。](https://t.zsxq.com/08UGtaEda) ## 十、系统重构 - [系统重构经验](https://t.zsxq.com/05vz3VJa2) - [部门最近想把几个产品线的相同业务模块抽出来复用](https://t.zsxq.com/05ZNfyNju) +- [用户将数据推送到公司系统,公司系统将处理后的数据回调到用户系统,这个时候是集群中的单服务回调,此时服务器的算力资源无法充分利用](https://t.zsxq.com/07vz3rzNr) ## 十一、其他场景 @@ -141,4 +160,10 @@ lock: no - [java有没有什么办法能远程监控服务器的一些性能指标,比如cpu、内存使用率之类的?](https://t.zsxq.com/05YvVvNvN) - [老项目原来是用weblogic部署的项目,Hibernate 速度慢优化](https://t.zsxq.com/06Fuv7amq) - [业界在设计业务系统时,如何选择数据库的隔离级别,有没有什么标准,最常用的可重复读RR和读已提交RC使用场景是怎样的!目前我只感受到交易系统使用RC更合适,(又或者说什么场景使用RR)业界对这些场景有没有什么通用的选择方案吗](https://t.zsxq.com/06qBaaEUf) -- [云原生是什么?](https://t.zsxq.com/06VnIeeUv) \ No newline at end of file +- [云原生是什么?](https://t.zsxq.com/06VnIeeUv) +- [公司也有个需求合作方传过来的值和我们这边的值不一样,要做一一对应要求可扩展性强](https://t.zsxq.com/06qz7aunu) +- [服务多租户场景使用](https://t.zsxq.com/06ZfmqnmY) +- [加强产品配置信息的校验,不使用 if···else、validation 可以怎么做?](https://t.zsxq.com/06fMvf6M7) +- [目前有14个库,每个库其中有一个用户表,里面存储着用户信息,其中包括id,city等信息,14个库总共14亿数据(总之数据量很大),怎么统计出各个城市有多少用户,该怎么设计?](https://t.zsxq.com/07MZZBQfq) +- [系统的分支维护](https://t.zsxq.com/08VTJabHq) +- [设计完成一个全链路监控系统需要具备哪些技术栈和能力?](https://t.zsxq.com/097do563T) \ No newline at end of file diff --git a/docs/md/zsxq/material/dialogue-skills.md b/docs/md/zsxq/material/dialogue-skills.md new file mode 100644 index 000000000..d21a75324 --- /dev/null +++ b/docs/md/zsxq/material/dialogue-skills.md @@ -0,0 +1,167 @@ +--- +title: 面试:对话技巧 +lock: no +--- + +# 面试:对话技巧 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +曾经的一个业务领导Boss,对着我们的分享资料说;**你们做技术的要说人话** 炸一听到这样的话,我...,要不是为了我的工资,趁着还年轻气盛,我真想给你一个农夫三拳。 + +
+ +
+ +不过,在评审200来份简历后,发现大部分程序员的简历内容确实"不说人话"。 + +其实程序员呢,也不是不说人话,而是不会表达。大部分时候写的东西,不仅外行看不懂,其实同行也挺难看懂。不仅同行难看懂,有时候写的久了,自己也看不懂。这是为啥呢🤔?其实主要是因为写的东西太少了,所以在写;简历、述职、汇报等材料时,往往对内容的把控是不够的,没有衔接性,也没有因果关系,甚至还缺少专业的技术术语。所以总是写出一些片段、琐碎、无重点的只言片语。 + +**就像**,你要写风,就不能只写风。你要写;弯曲的树梢、波纹的湖面、抚平的柔甲、飞起的纸鸢。—— `柔甲就是小草,小傅哥就想把你小草一样的简历/面试话术修成抚平的柔甲。` + +
+ +
+ +那么接下来小傅哥就分别从`自我介绍`、`项目描述`、`项目介绍`、`面试问题`,这些场景分别举例应该怎么有高度、有技术、有内容的来更好的表达自己。 + +>文末有加入社群方式;解锁🔓150份+简历指导建议 + 6个实战项目一起学习。 + +## 一、自我介绍 + +在简历中,有一个非常重要且明显的区域,你可以能遗漏了! + +
+ +
+ +在简历编写的开头,个人基本信息编写完以后,大部分程序员伙伴就直接开始正文的编写了。但这块区域非常重要,如果你能通过一段话术来简明扼要的突出自己的技术价值,那么在简历的筛选中绝对可以提高筛选通过率。 + +**描述结构**: + +在过往的XXX经历中,积累了XXX场景的经验,使个人具备了XXX方面不错的能力。同时兼具着对(技术/场景)的(喜好/热忱),长期(学习/从事/钻研)某项技术,并做出了对应的(开源)(产品/服务/组件/插件)。此个人XXX发布到XXX市场以后,得到了用户(下载量/点赞量/使用数)的规模,获得了非常好的认可。地址:http://github.com/xxxx/xxxx + +**校招举例**: + +在大学/实习期间,参与过多次的技术类赛事项目,熟练使用各类技术框架,积累了丰富的开发经验。同时兼具着对技术的喜爱,长期对技术源码进行钻研学习吸收其中的设计精髓。尤其对 MyBatis 源码学习后,开发了一款监控 SQL 慢查询以及优化建议插件。并将此插件发布到了 IDEA Plugin 插件市场,半年获得了3000次下载量,获得了非常好的认可,个人也得到了非常多的成长。地址:http://github.com/xxxx/xxxx + +**社招举例**: + +个人从事互联网金融类场景开发3年,对账户、账务、交易、息费、费率等相关的业务有较深的积累,具备相关场景良好的架构设计和开发经验。同时个人的技术储备不止局限于业务类场景开发,同时对 Spring、MyBatis、Dubbo 等源码有较深的研究,可以根据实际场景需求,将同类共性逻辑的功能,凝练成通用的组件服务。提高开发效率和降低维护成本。 + +>这就是描述的技巧,也是话术的力量。通过这样的一个描述,将自己的知识体系进行结构化的展示,提供核心竞争力。如果你仍不会组织和编写,那么可以加入星球【码农会锁】小傅哥会帮你体力提炼话术。 + +## 二、项目描述 + +一个项目的描述,就是你个人在过往项目经历上的一个能力举证。通过你对一个项目的阐述,来表达你在某些方面所积累的业务、技术、问题处理经验。所以项目的描述,要具有;项目名称、系统架构、核心技术、项目介绍、核心职责、个人成绩【可选】,通过这样一个结构来完整的展示自己在此项目上所展现出的能力举证。 + +**校招举例**: + +校招生写简历,要注重个人实现场景时的细节处理,多有一些个人的思考路径和结果说明。不用说一些特别大的话,也不用考虑研发成本。 + +- **项目名称**:Lottery 活动抽奖系统 +- **系统架构**:分布式技术架构,DDD 领域驱动分层结构 +- **核心技术**:SpringBoot、MyBatis、Dubbo、MQ、MySQL、XDB-Router、ES、ZK +- **项目描述**:Lottery 是我的一个学习项目,此项目不只是一个简单单一的抽奖,而是符合营销平台架构设计具备可扩展性的微服务架构设计。Lottery 系统的全方面技术栈的使用,多场景的问题的解决方案,让我在这个过程中学习到非常多的内容,这写技术学习的内容,也可以更好的应对以后的开发工作。 +- **核心职责**: + - 通过领域驱动设计的分层结构,和领域功能服务的设计,来实现整个 Lottery 的业务流程。涵盖;参与活动、可幂等重试的活动抽奖单、活动参与、结果异步处理再到最终的发奖流程。 + - 运用模板、策略、工厂三个设计模式,定义抽奖过程标准和实现对应的多类型抽奖的服务模块。 + - 通过组合模式设计简单规则引擎,满足不同类型活动对差异化人群标签的过滤。让不同的用户可以参与不同的活动。我在也这块的实现也感受到,为什么以前有发现,我和其他伙伴参与的某些电商活动,为什么有差异。 + - 因活动秒杀的并发场景,将秒杀从最开始的数据库行级锁优化为 Redis Key 加锁,又从 Redis Key 的独占锁,优化为滑块锁。优化后整体秒杀有了非常可观的性能提升。 + - 解耦抽奖流程,把抽奖和发奖用MQ消息串联起来,避免一个流程太长,导致用户一直等待。 + +**社招举例**: + +- **项目名称**:营销活动平台 - Lottery 微服务抽奖系统 +- **系统架构**:以 DDD 领域驱动设计开发,微服务拆分的分布式系统架构 +- **核心技术**:SpringBoot、MyBatis、Dubbo、MQ、MySQL、XDB-Router、ES、ZK +- **项目描述**:抽奖系统是营销平台的重要微服务之一,可以满足 C 端人群的需求,例如拉新、促活、留存等。该系统运用抽象、分治和 DDD 知识,拆解服务边界,凝练领域服务功能。围绕抽奖服务建设领域服务,包括规则引擎、抽奖策略、活动玩法、奖品发放等。这可以满足业务产品快速迭代上线的需求,同时减少研发成本,提高交付效率。 +- **核心职责**: + - 构建以 DDD 分层结构的处理方式,搭建整个抽奖系统架构。运用设计原则和工厂、代理、模板、组合、策略等设计模式的综合使用,搭建易于维护和迭代的系统工程。 + - 抽象活动、策略、奖品三层领域上下文关系结构,由用户的活动总数和用户可参与数并加油人群过滤的方式做精细化运营控制,并在实现上解耦活动与策略的关系,一个活动可以配置多组策略,每组策略有不同的奖品或奖品包。 + - 鉴于系统内有较多的规则策略过滤,包括准入、人群、风控、A/BTest等需求,为适应系统规模可快速开发和使用的方式,搭建了去中心化的量化人群规则引擎组件。通过业务需求对逻辑的扩展和内置引擎执行器的使用,完成自由组合的人群过滤服务。这降低了共性功能重复开发所带来的成本问题,并提高了研发效率。 + - 根据实际秒杀峰值场景 TPS 3000 ~ 5000 的需求,开发了统一路由组件。该组件不仅可以满足差异化不同字段的分库分表组合,还支持 Redis 库存分片和秒杀滑动库存分块。而且,开发了统一路由 XDB-Router 的 SpringBoot Starter 技术组件。该套组件已经经历了多次大促活动场景的考验,支持横向扩展,可以满足业务规模的快速增长。 + +## 三、项目介绍 + +在面试中,除了看你的项目简历编写外,还会让你介绍下你的项目。这个时候你要很熟悉的项目,而且能提炼关键话术来回答。总不能面试官拿着简历看,你就拿着简历照着读对吧。这也是一种面试的考察方式,考的就是你的真实能力水平,你写的和你说的一样不。 + +这里我们举例2个小傅哥星球里的项目《Lottery 分布式抽奖系统》、《API网关》,因为伙伴也竟然面试被提问,不知道怎么回答。 + +**校招举例** + +面试官您好,Lottery 是我的一个(学习/工作)项目,此项目不只是一个简单单一的抽奖,而是符合营销平台架构设计具备可扩展性的微服务架构设计。核心流程为根据不同人群标签的人群规律,选择不同的抽奖活动,每个活动的参与为一个抽奖单。可以有效的控制参与用户数和异常流程的补偿。领取抽奖单后执行使用了模板、工厂、策略的抽奖玩法设计。在这里设计了分段锁,避免独占锁的竞争,从而挺高效率。最后抽奖完成异步发送 MQ 消息方式进行驱动后续的发奖流程。 + +Lottery 系统的全方面技术栈的使用,多场景的问题的解决方案,让我在这个过程中学习到非常多的内容,这写技术学习的内容,也可以更好的应对以后的开发工作。非常感谢您给我这次面试机会。 + +**社招举例** + +面试官您好,这是我所设计和实现的一套统一《API网关》系统,该系统的核心目的是用于解决公司中所有各类服务的统一出口问题。将非业务功能的共性服务进行统一封装使用,这包括;鉴权、熔断、限流、风控、切量等。通过API网关可以将内部的 RPC 服务以及可扩展的 MQ、SQL、任务等资源,通过 HTTP 对外提供调用,让APP、WEB、H5、小程序等有一个统一标准的接入方式,降低公司在此同类功能模块的重复建设问题。 + +我作为项目的架构师和核心开发人员,在项目架构设计上,将工程拆分为Netty实现的核心通信模块、通信封装模块、通信引擎模块,以及注册中心、上报服务的SDK组件和后台管理系统。并通过 Nginx 动态负载驱动算力的集群使用,可以支持横向的扩展,满足高并发的接入。好的,面试者就是我做的API网关核心实现的介绍。 + +>综上,首先你能把项目介绍的漂亮,是项目本身也要不错。否则都是 CRUD 真的没法讲。之后站在不同的视角下,要有不同的阐述方式,来突出你的核心能力。 + +## 四、问题解答 + +你要知道,面试呢,有时候也是质疑的过程。以为我对你不理解,我要对一些感兴趣的,又能考察到你的,有意思的技术点。对你进行质疑提问。 + +这里我们举例;"你的秒杀设计,为什么不使用独占锁?锁的范围是什么?" + +对于这个问题,如果你理解的不够透彻,一定会觉得应该加独占锁,之后用户排队。但其实做过大体量的秒杀活动的话,就不会加独占锁。加,基本就是在3个999可用率之外出事故。具体描述给面试官如下; + +1. 锁,是针对于用户参与的活动库存加锁的,如果是独占锁是针对于活动ID加锁的。 +2. 滑块锁的核心是去竞态,避免独占影响系统的响应性能。关于此类锁,这里又做了视频做了详细的讲解;[Redis | bugstack 虫洞栈](https://bugstack.cn/md/road-map/redis.html)- 如图。如果还用独占锁做活动的秒杀场景,那么大概率会出事故的。因为独占会竞争资源,等待释放。只要一个波动,就会导致锁释放失败,而无论多久自动释放锁,都会直接导致客诉发生。 +3. 那为什么在incr后加一个锁呢,incr 不就可以。加锁是兜底,你不知道什么时候会出现 incr 不对的情况。如;集群配置问题【特例】、出现redis问题,需要恢复库存。如果没有锁,可能会超卖。[https://t.zsxq.com/12sNS4E2J](https://t.zsxq.com/12sNS4E2J)- 第一条评论加了说明。 +4. 对于非交易的活动类场景,要的就是一个快。快速响应、快速释放,可接受容错失败概率。但不要磨磨唧唧影响我的主核心交易链路。但凡在618、双11,营销敢超时,就直接下掉。保证用户可下单可支付。否则这黄金时间点,你耽误1分钟都是几个亿的成交额。所以,这类营销秒杀场景下,根本就是保证不超卖,也不恢复库存。 + +注意:独占锁是加给个人流程的 - 无资源竞争,如贷款单受理。分段/滑块/无锁化,是加给库存的 - 有资源竞争,如秒杀、商品发货等集中资源类。就跟大超时的收银台一样。原来就1个出口,后来一排出口,在后来又有无人化的电子出口。点点那个软件。 + +举例;incr 的速度很快,就像进入了公共的卫生间🚾。一个坑一个门,谁进去谁就锁上。没有就跑到下一个门。你说你不锁门吧,也没问题。但别人不知道,一拽开就比较尴尬。所以要加锁,锁门。 + +>此项目的解答来自于星球 Lottery 项目,这是一个技术度非常高的项目,也有非常多的技术点可以在面试中讲解。 + +## 五、简历评审 + +以上所有的这些核心资料,都来自于对简历和面试的评审解答,所以在星球【码农会锁】的伙伴,学习的都是这样的实战技术。 + +### 1. 24届实习 + +
+ +
+ +### 2. 23届校招 + +
+ +
+ +### 3. 3年社招 + +
+ +
+ +>不同的应聘诉求,对应的简历编写也会有不同的描述方式。这也是小傅哥在刷过几千份简历招聘和帮【星球:码农会锁】伙伴修改200份+简历所积累的经验。加入星球,当你一个个刷这样的简历的评价后,你也能写出优质的简历。直接看简历,看点评和建议,还有比这更给力的吗! + +## 六、加入学习 + +**注意📢**,小傅哥的【星球:码农会锁】,提供了很多的技术服务,包括;简历优化、面试解答、1v1提问帮助、职业规划,此外还有[180天完整Java学习路线](#)和6个实战项目,包括;API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 等。而且还不只这些,还有很多其他的开源项目学习、源码学习、对各类场景的架构方案学习。 + +>这样一套项目,放在一些平台售卖,至少都是几百块。但小傅哥的星球,只需要100多,就可以获得总价几千元的学习项目和技术服务! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +【星球:码农会锁】里的伙伴日常学习的都是非常真实实战的技术,因为有小傅哥给大家源源不断的输入来自大厂的架构设计思想和问题解决方案。所以,这些伙伴会有着非常快的技术成长和话术解答能力。 + +如图举例,有些星球伙伴反馈想了解整个营销架构。那么小傅哥就画一个完整的体系,让你来学习了解。**就问这样的资源学习,和你在网上浪费时间找来的拼凑资源,是一个级别的吗!!!** + +
+ +
diff --git a/docs/md/zsxq/material/guide.md b/docs/md/zsxq/material/guide.md index d2c43f044..85e41efe1 100644 --- a/docs/md/zsxq/material/guide.md +++ b/docs/md/zsxq/material/guide.md @@ -12,6 +12,8 @@ lock: no 欢迎👏🏻 VIP 伙伴的加入,为了帮助大家更好的使用星球、获得知识,请大家 **务必** 花 **5-10** 分钟阅读这份指南! +**踏上学习旅途前**,先对星球[【码农会锁】](https://wx.zsxq.com/dweb2/index/group/48411118851818)有一个全貌了解,这可以更好的帮助你学习这些项目。[https://bugstack.cn/md/zsxq/introduce.html](https://bugstack.cn/md/zsxq/introduce.html) + ## 一、前言:准备开始 1. 首先为了更好的使用知识星球,你需要下载一个知识星球支持 APP【[安卓](https://a.app.qq.com/o/simple.jsp?pkgname=com.unnoo.quan)/[IOS](https://apps.apple.com/cn/app/xiao-mi-quan/id904106920?l=cn)】这样能方便你在APP上接收推送、阅览资料、发起问题、学习打卡等事项。此外如果你在电脑前学习,也可以直接预览[网页版](https://wx.zsxq.com/dweb2/index/group/48411118851818) 这样将更加方便的操作。**建议大家关注【知识星球】官方微信公众号,便于接受星球内的消息。** @@ -28,7 +30,7 @@ lock: no
-3. 请新同好加入时发布一个帖子来介绍自己,包括但不限于毕业时间、学历信息、工作经历、技术栈、学习方向、兴趣爱好、加入星球的原因等。**必须** 打上 **#自我介绍** 标签、且 必须 包含 **【个人计划】**,可以参考下面这些不错的介绍:—— 一份打卡介绍也是鉴证你学习的开始 +3. 请新同好加入时发布一个帖子来介绍自己,方便我来帮助你做一些指引。包括但不限于毕业时间、学历信息、工作经历、技术栈、学习方向、兴趣爱好、加入星球的原因等。**必须** 打上 **#自我介绍** 标签、且 必须 包含 **【个人计划】**,可以参考下面这些不错的介绍:—— 一份打卡介绍也是鉴证你学习的开始 - [小蜗牛](https://t.zsxq.com/057ufQBq3) - [小杨](https://t.zsxq.com/05vjEIyNZ) @@ -37,25 +39,6 @@ lock: no 4. 当你加入星球后,遇到任何问题都可以在星球中搜索(`很多问题可能已经回答过了`)。如果没找到答案,请先阅读 [《提问的智慧》](https://t.zsxq.com/qZ7urNf),来学习如何正确高效地提问,然后再向小傅哥 **免费提问**(在星球中提问,**不是私信** 哦~ 可匿名提问),小傅哥会按照提问顺序依次在固定的时间回答大家的问题。 -⚠️ 注意!个人所在公司参与的项目bug请给项目组长处理,因为这类非技术包含众多业务流程和你所在公司环境的没问题,没法介入解答。一些很基础能百度、谷歌、GitHub检索到的问题,尽量不要提问。如果实在搞不定了,也可以在星球发帖子添加标签 🏷**技术问答** 求助,星球里的其他朋友都会帮忙回答。*这也是一个相互学习成长的过程*,比如:[https://t.zsxq.com/biuNju3](https://t.zsxq.com/biuNju3) - -5. 星球提供免费批阅简历的服务,建议先阅读 [写简历技巧](https://mp.weixin.qq.com/s/ry0flp0c3iHm9XSErPepyg) 参照修改,然后将简历(最好是word版本,我可以直接给你做批注)发送到小傅哥的邮箱 184172133@qq.com,邮件标题为:`【星球】编号xx,yy同学简历优化(xx 为你的星球编号,yy为你的星球昵称,别写错哦)`,并适当介绍你的简历内容、求职方向、个人计划。【简历模板:[https://t.zsxq.com/rf2JUfY](https://t.zsxq.com/rf2JUfY)】 - -6. 欢迎所有同好伙伴在星球中积极分享内容,小傅哥会定期在每个月度,对优秀的内容输出者,提供周边福利奖励。**禁止灌水、禁止发布违规内容、禁止使用不文明用语 !** - - - 此外,发布内容时,请大家 **务必** 给帖子 **打上** **合适** **的标签**,比如:技术问答、八股题库、日积笔记、技术讨论等,便于大家搜索。 - - 分享文件时,如果是 `50 M`以下的文件,可以直接上传到星球中,不需要挂到网盘上再分享链接。 - -7. 欢迎大家通过发帖来结交好友,或者是组队学习、合作项目、一起刷题、互阅代码等,遇到和自己志同道合的同好,可以主动私聊他。 - -8. 小傅哥会定期给星球读者伙伴组织直播分享,后续的直播会提前发出公告。 - -9. 星球内有 **打卡活动** ,这些都是为了帮助大家实现目标而设立的(比如:**每日学习习惯养成打卡**),大家可以按需参与,记录和分享自己的进步,参与了就要坚持哦(每日打卡截止时间为 0 点)! - - - 可参考的往期优秀的打卡:[👣DDD分布式抽奖秒杀系统,学习打卡!](https://wx.zsxq.com/mweb/views/checkins/checkin.html?checkinid=4515152288&groupid=48411118851818) - -10. 除了以上的知识分享、打卡送书、周边礼物,今年还会组织球友线下聚会,建议大家日常打开 **APP** 看一看,收获知识的同时、顺便白嫖一点小奖励。 - --- 最后,希望所有加入星球的朋友都能在这里学到知识、交到同好、收获成长、突破瓶颈,养成分享的习惯,和星球一起无限进步!🥰 @@ -72,32 +55,67 @@ lock: no 申请加入仓库后,小傅哥会按照你的申请信息进行审核授权,授权完成后就可以访问项目地址,看到完整的星球课程学习项目了。地址:[https://gitcode.net/KnowledgePlanet](https://gitcode.net/KnowledgePlanet) +**仓库使用**:`2种使用方式` + + 1. 密码方式:登录的用户ID为 CSDN 个人中心的用户ID,[https://i.csdn.net/#/user-center/profile](https://i.csdn.net/#/user-center/profile) 密码为 CSDN 登录密码。如果没有密码或者忘记,可以在 CSDN 登录页找回密码。 + 2. SSH 秘钥免登录方式,设置:[https://gitcode.net/-/profile/keys](https://gitcode.net/-/profile/keys) 文档:[生成 SSH 密钥](https://gitcode.net/codechina/help-docs/-/wikis/docs/ssh#%E7%94%9F%E6%88%90-ssh-%E5%AF%86%E9%92%A5) +
-## 三、课程:包括内容 - -目前星球:码农会锁 | 包括的课程内容有; -- 实战项目:[Lottery 分布式抽奖系统](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html)、[Netty+JavaFx实战:仿桌面版微信聊天](https://bugstack.cn/md/project/im/2020-03-04-%E3%80%8ANetty+JavaFx%E5%AE%9E%E6%88%98%EF%BC%9A%E4%BB%BF%E6%A1%8C%E9%9D%A2%E7%89%88%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9%E3%80%8B.html)、[Api网关【进行Ing】](https://bugstack.cn/md/assembly/api-gateway/2022-08-12-%E5%BC%80%E7%AF%87%EF%BC%9A%E5%A6%82%E6%9E%9C%E8%AE%A9%E6%88%91%E8%AE%BE%E8%AE%A1%E4%B8%80%E5%A5%97%EF%BC%8CTPS%E7%99%BE%E4%B8%87%E7%BA%A7API%E7%BD%91%E5%85%B3.html) -- 技术小册:[Java 面经手册](https://t.zsxq.com/05YZZjq7m)、[IDEA Plugin 开发手册](https://t.zsxq.com/05ufmQnA2)、[字节码编程](https://bugstack.cn/md/bytecode/asm/2020-03-25-%5BASM%E5%AD%97%E8%8A%82%E7%A0%81%E7%BC%96%E7%A8%8B%5D%E5%A6%82%E6%9E%9C%E4%BD%A0%E5%8F%AA%E5%86%99CRUD%EF%BC%8C%E9%82%A3%E8%BF%99%E7%A7%8D%E6%8A%80%E6%9C%AF%E4%BD%A0%E6%B0%B8%E8%BF%9C%E7%A2%B0%E4%B8%8D%E5%88%B0.html)、[Java 数据结构和算法【进行Ing】](https://bugstack.cn/md/algorithm/data-structures/2022-07-22-linked-list.html) -- 手撕源码:[手写Spring:渐进式源码实践](https://t.zsxq.com/05Vvbmq7a)、[手写Mybatis:渐进式源码实践](https://t.zsxq.com/05bmqNFQ7) +课程和视频,使用说明; -**提示**:由于[《SpringBoot 中间件设计和开发》](https://bugstack.cn/md/assembly/middleware/2021-03-31-%E3%80%8ASpringBoot%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%92%8C%E5%BC%80%E5%8F%91%E3%80%8B%E4%B8%93%E6%A0%8F%E5%B0%8F%E5%86%8C%E4%B8%8A%E7%BA%BF%E5%95%A6%EF%BC%81.html)是在星球创建前与掘金合作,所以暂时不能搬迁到星球,但在星球内提供了购买的优惠券。 +1. 申请一次仓库后(星球,课程入口,指引,仓库申请),就可以拉取课程代码(git clone),之后idea打开项目,可以切换分支。教程;https://bugstack.cn/md/road-map/git.html +2. 文档;课程入口,每个项目,进入后,往下翻,会有一个【目录】。目录里的文章,进入后,是评论区,在点击链接进入文章详情。 +3. 视频;每个文章上面会有,视频地址,以及源码地址(审核仓库后直接点击就可以看) -## 四、课程:学习路线 +## 三、课程:学习路线 星球学习项目较多,建议学习路线如下; -1. **为了面试**:Java面经手册(八股)、Lottery 分布式抽奖(项目)、SpringBoot Starter 中间件设计和开发(拉伸技术)、手写Spring/手写Mybatis(2选1) -2. **夯实能力**:重学Java设计模式、Lottery 分布式抽奖、手写Spring、手写Mybatis +1. **为了面试**:Java面经手册(八股)、Ai Agent、拼团交易、大营销(Lottery Plus)/Lottery 分布式抽奖(项目)、SpringBoot Starter 中间件设计和开发(拉伸技术)、手写Spring/手写Mybatis(2选1) +2. **夯实能力**:重学Java设计模式、拼团交易、大营销(Lottery Plus)/Lottery 分布式抽奖(项目)、手写Spring、手写Mybatis、OpenAi 应用项目 3. **拉伸技术**:字节码、IDEA Plugin 开发手册、SpringBoot Starter、API网关 4. **网络编程**:Netty 4.x 小册、IM(Netty 4.x + JavaFx) -5. **应届小白**:重学Java设计模式、Java面经手册、手写Spring/手写Mybatis(2选1)、Lottery 分布式抽奖、SpringBoot Starter 中间件设计和开发,之后学习拉伸技术、网络编程 +5. **应届小白**:重学Java设计模式、Java面经手册、手写Spring/手写Mybatis(2选1)、小型支付商城、SpringBoot Starter 中间件设计和开发,之后学习拉伸技术、网络编程 +6. **创新学习**:IDEA Plugin 插件开发、ChatGPT 微服务应用体系 —— `星球项目较多,也可以自行组合。如 IM + ChatGPT,开发角色智能AI。` + +--- + +**详细介绍**:[路线:实习、校招、社招,学习路线指引](https://bugstack.cn/md/zsxq/material/student-learn-all.html) + +## 四、简历:项目组合 + +1. 建议的项目组合是;a【业务】拼团/大营销、IM + b【技术】API 网关 + c【组件】SpringBoot Starter + d【创新】ChatGPT 项目 + e【开源】IDEA Plugin 插件发布到 IDEA 插件市场。 + 1. 基础组合;a、c + 2. 中级组合;a、c、d/e + 3. 高级组合;a、b、e + 4. 超级组合;a、b、c、e【并有一定规模的下载量】 + +2. 组合后,简历编写参考:[https://bugstack.cn/md/zsxq/material/notes.html](https://bugstack.cn/md/zsxq/material/notes.html) + +3. 手写源码系列,也可以单独一个小项目,比较适合应届生。社招就更适合因为学到了这样的源码,运用到了哪里。比如学习MyBatis源码,技术迁移设计了 API 网关通信模型【我视频里有讲可以看;[https://www.bilibili.com/video/BV1iv4y1L78e](https://www.bilibili.com/video/BV1iv4y1L78e)】。 + +**手写源码类项目体现到简历参考如下** + +1. 体现在专业技能上,例如; + +- 1.1 深入学习 Spring 核心流程模块,包括;IOC、AOP、依赖倒置等流程,掌握Spring解决复杂场景所运用的分治、抽象和知识(设计模式、设计原则),在解决Spring场景问题时,可以从核心原理上给出方案。同时也具备基于 Spring 开发 SpringBoot Starter 技能,为复杂项目减少同类共性需求的开发,凝练通用的技术组件,减少研发成本。 +- 1.2 深入学习 MyBaits 核心流程模块,包括;会话、反射、代理、事务、插件等流程,熟练掌握 ORM 框架的设计思想、实现方式和应用价值。并能按需结合 MyBatis 的插件机制,开发属于企业自己所需的功能,包括;数据分页、数据库表路由、监控日志、数据安全等方面。 + +2. 体现在项目经验上,例如;—— 对校招和实习比较有用 +把 Spring、MyBatis 当一个学习项目来描述,这是你在离校前,最可能接触到的一个完整的、成型的、知名的,有企业使用的,框架。你就按照自己学习并开发了这样一个框架为目标来写项目,并描述出这个项目,你用了什么技术栈,解决了什么问题,学习到了哪些。 + +3. 体现在项目应用上,例如; +关于 Spring、MyBatis 的项目,一般都是插件类开发,比如各类的 SpringBoot Starter,MyBatis 插件,都是基于框架的深入整合类技术解决方案,体现在简历上,非常抓眼球。一看你就是有深度和自研能力的研发人员。—— 一般不让你造轮子,但需要你有造轮子的能力,这样企业中一些软件可以被你进行优化和修改。 + +4. 体现在解决问题是上,例如; +在你的自己的业务项目中,渗入一些关于解决了原项目使用 Spring 时,关于感知 Aware 方式或者结合 FactoryBean 包装对象等,所遇到的问题,因为你学习过源码,所以非常清晰这样的流程,因此解决了一个问题。通用 MyBatis 也适用于这样的描述方式,包括;事务、查询次数、批查询、插件能监听到的四个类(ParameterHandler、ResultSetHandler、StatementHandler、Executor )你给了更好的选择。 ## 五、求助:各类问题 星球的伙伴在学习成长的过程中,肯定会遇到一些问题,包括;技术、晋升、跳槽、述职等,当你有遇到这些问题的时; 1. 简单的技术问题,优先发到星球的vip群,这里的技术伙伴很多,可能有人就正在处理完与你遇到的同样问题。 2. 复杂的技术问题,发到群里没有解决掉,这个时候不用私信我,直接在星球中对我发起提问即可,我会在下班后进行统一回复处理。但你的问题尽量描述清楚,有前言后语,如果有学习的对应章节也要给出链接,这样我能更加快速的帮你解决。 -3. 一些晋升、答辩、述职、规划等,这类问题直接发到星球即可,如果你不希望别人看见你的问题,也可以在提问的时候告诉我只回复给你。 \ No newline at end of file +3. 一些晋升、答辩、述职、规划等,这类问题直接发到星球即可,如果你不希望别人看见你的问题,也可以在提问的时候告诉我只回复给你。 diff --git a/docs/md/zsxq/material/interview.md b/docs/md/zsxq/material/interview.md index 6ea2ba319..744f0eb36 100644 --- a/docs/md/zsxq/material/interview.md +++ b/docs/md/zsxq/material/interview.md @@ -22,56 +22,14 @@ lock: no ## 三、面试问题 -### 1. 抽奖系统 - -- [抽奖系统,面试被问到表的设计,要怎么答?](https://t.zsxq.com/04EYNRF6m) -- [抽奖系统一般部署多少实例比较合适,系统大概能抗住多大的流量?](https://t.zsxq.com/04zNzZVFa) -- [抽奖系统为什么自研路由组件?](https://t.zsxq.com/04Am6mIqR) -- [抽奖项目分布式事务如何解决?](https://t.zsxq.com/04yzF27UB) -- [抽奖系统 TPS 5000~8000 服务器配置大概情况](https://t.zsxq.com/04fA6meyB) -- [抽奖系统mq重发的时候是怎么保证幂等性](https://t.zsxq.com/04ZrJYBy7) -- [面试时抽奖系统被问到类似qps、tps这些指标如何回答?](https://t.zsxq.com/04eqV7YNf) -- [问分库分表,别给我回答分2个库,每个库4个表,那你分那个毛用,一看就是假的](https://t.zsxq.com/04y3FAmq3) -- [想问问大家在面试的时候自我介绍,是怎么介绍抽奖项目的,个人项目还是公司项目?](https://t.zsxq.com/04BMN7myv) -- [说一下抽奖系统拓扑结构](https://t.zsxq.com/043BiQje6) -- [聚合和聚合根在抽奖系统的体现?](https://t.zsxq.com/04jyRjA6I) -- [抽奖系统DDD和MVC的区别](https://t.zsxq.com/04EAameYz) -- [描述你遇到的一个技术问题-抽奖事故及优化](https://t.zsxq.com/04EuvJe6U) -- [为什么用户领取活动完毕,发送MQ去更新数据库中的库存](https://t.zsxq.com/04vjAq3RR) -- [量化规则引擎是一个组件,如果有一个新的业务进来,如何复用? 它的复用性体现在哪?能否支持风控可A/Btest需求?](https://t.zsxq.com/05zRvbUJ2) -- [抽奖系统自己写成MVC架构怎么讲?](https://t.zsxq.com/05iQN7AU3) -- [抽奖流程阐述](https://t.zsxq.com/053RVFeuZ) -- [抽奖系统配置相关](https://t.zsxq.com/05MJyZ7Yf) -- [Lottery项目包装为实验室项目,面试时被问对接的是什么甲方,为什么数据量会这么大](https://t.zsxq.com/05Nr3rjUf) -- [针对于抽奖系统,面试被问到表的设计,要怎么答?](https://t.zsxq.com/05EYNRF6m) -- [这个项目的业务数据,例如用户量、活跃用户量,以及核心业务数据,例如订单系统的下单量等](https://t.zsxq.com/05UJUFaur) -- [技术和业务19道面试题汇总](https://t.zsxq.com/05e662Vb2) -- [抽奖和发奖关于库存的扣减,防超发漏发,监控和弥补有没有设计思路和流程图之类的,添加库存,扣减库存的操作日志怎么设计](https://t.zsxq.com/05YVjEYny) -- [路由散列算法:idx / tbCount + 1 & idx - tbCount * (dbIdx - 1)](https://t.zsxq.com/05urRRzBi) -- [Lottery项目的抽奖算法,抽奖概率是100万怎么处理?](https://t.zsxq.com/05qFun6Uj) -- [Lottery项目的抽奖算法,抽奖概率是100万的另外一种双色球设计?](https://t.zsxq.com/053ZrZZFU) -- [Lottery向内部应用提供了RPC接口,那么当h5端需要提供一个http的请求,这个接口应该写在哪里呢](https://t.zsxq.com/05UrVfmQB) -- [多种抽象策略是怎么注入使用的 | 手动、ist、注解、Map?](https://t.zsxq.com/05yfAiMji) -- [如果redis作为分布式锁的时候,主节点挂掉了,但是数据还没有同步到从节点,这种情况怎么办?](https://t.zsxq.com/05MjE2f6a) -- [活动库存与奖品库存的配置关系](https://t.zsxq.com/05nQBiUZN) -- [抽奖系统用到了分库分表,那目前市面上还有那些成熟的规则引擎组件?](https://t.zsxq.com/05rVNfurN) -- [抽奖策略&斐波那契散列&活动库存&任务扫描时效性&并发编程场景](https://t.zsxq.com/05rbM7IYr) -- [关于抽奖算法自己整理的一些面试题和回答:采用了什么设计模式、实现了什么抽奖算法](https://t.zsxq.com/05YFQ3byr) -- [我把抽奖系统包装到实习经历里了。今天面试的那个部门是做广告架构的,可能有广告投放的场景,所以对我简历上的规则引擎量化人群参与活动模块比较感兴趣](https://t.zsxq.com/05QVrnE6I) - - 问:抽奖的奖品是优惠券,那金额多大呢?候选集有多大?多少人参与? - - 问:你说你用的决策树,那决策树是什么时候创建的呢?是每个用户参与抽奖就创建一次(个性化),还是一开始就创建好? - - 问:筛选的标签是什么,根据什么来过滤呢? - - 问:那有没有可能你制定的这些标签,数据传进来的时候是丢失的,用户没有某个路径上的数据,是不是就到不了叶子节点了,按你说的就没法领取活动了? - - 问:既然你的这些规则都是确定的,为什么要用决策树?决策树和布尔检索有什么区别知道吗? -- [抽奖系统包装到自己的电商业务中,描述案例](https://t.zsxq.com/05RniU7un) -- [抽奖系统的领域事件有哪些?是如何实现的?](https://t.zsxq.com/05A6qfiun) -- [结合美团DDD抽奖进行扩展](https://t.zsxq.com/05f2zVRfq) -- [想问一下抽奖系统中,各个领域是按照什么划分的?有什么规则或标准么?](https://t.zsxq.com/06MRnYBaI) -- [现在的抽奖系统是可能存在活动刚上线,所有的奖品就被抽完了,问如何避免这种情况发生?](https://t.zsxq.com/06NZNzvrv) - -### 2. 通用问题 +- [《Lottery 分布式抽奖系统》,关于面试中的技能、简历、问题汇总](https://bugstack.cn/md/project/lottery/notes.html) +- [《API网关》,关于面试中的技能、简历、问题汇总](https://bugstack.cn/md/assembly/api-gateway/notes.html) +- [《ChatGPT 微服务应用体系构建》,关于面试中的技能、简历、问题汇总](https://bugstack.cn/md/project/chatgpt/notes.html) + +## 四、通用问题 - [你工作中遇到过什么技术难题,是怎么解决的?](https://t.zsxq.com/05AuFmUz7) +- [你是如何快速上手一门新技术的?](https://t.zsxq.com/06FAQR3Rv) - [如何合理的配置mysql的连接数?](https://t.zsxq.com/053Fmaeyn) - [最近面试老被问一些开放场景题:](https://t.zsxq.com/05M3BAeIu) 1. 公司当业务量上来,是选择分库还是选择分表需要看那些指标?有哪些场景是分库分表不能解决的? @@ -83,8 +41,16 @@ lock: no - [redis cluster模式下会通过一致性hash算法拆分成16384个slots槽,那这样,当其中某个节点挂了, 它上面存放的数据是不是就丢失了?](https://t.zsxq.com/052zB6Qf2) - [为什么mq用文件系统存储消息,不选择数据库等方式进行持久化,不同的方式有什么优缺点](https://t.zsxq.com/05qV3nMbe) - [想问一下各位,hr面问道你的缺点是什么,一般怎么回答?](https://t.zsxq.com/06qf6AqjM) +- [小傅哥,校招面试的时候,面试官经常会问你的未来规划是什么? 这一块该怎么回答比较好呢?](https://t.zsxq.com/067a237u7) +- [请问Spring中AbstractBeanDefinition的beanClass属性为什么用volatile修饰](https://t.zsxq.com/06uFA2FYZ) +- [Redis能替代Mysql吗?简单说说为什么不能?](https://t.zsxq.com/06nQJEM72) +- [手撸spring、手撸MyBatis如何体现在简历上?小傅哥可以给个Demo吗](https://t.zsxq.com/06IU3FEEA) +- [面试被问如何设计生成8位纯数字的优惠券码,每次生成8000个,需要不重复](https://t.zsxq.com/07f27mi2n) +- [两个线程同时查看一行数据后并进行操作怎么加锁能保证并发性?](https://t.zsxq.com/07RZNFiaU) +- [使用分库分表组件将订单信息散列到不同库表中了 B端商家如果需要查看某个活动/某一天的订单信息。](https://t.zsxq.com/08Vxe6lpN) +- [我怎么去拦截执行的sql语句,然后根据不同添加在sql上面去加点条件](https://t.zsxq.com/08xh5q0Mr) -### 3. 公司面试 +## 五、公司面试 - [中软国际1面A组](https://t.zsxq.com/05Jmyfiuj) - [中软国际1面B组](https://t.zsxq.com/05zRjuzVj) @@ -93,16 +59,14 @@ lock: no - [23届的暑期实习面试【腾讯、字节、阿里(云、支付宝、钉钉)】](https://t.zsxq.com/05AeMjmiq) - [滴滴面试](https://t.zsxq.com/05f6eu3RZ) -### 4. 简历包装 +### 六、简历包装 - [只有增删改查项目怎么办?](https://t.zsxq.com/05u7M7YrF) - [我现在专科大二,马上要准备找实习工作了,面试的时候对于实习生问什么类型的问题居多呢?](https://t.zsxq.com/05UrzbuNB) - [小傅哥,感觉今年的面试,已经不是背背八股文就可以了,需要有实战项目经验。](https://t.zsxq.com/06RbYZzf2) -### 5. 上岸记录 +### 七、上岸记录 - [实习末班车上岸字节【北京某211大学本科2023届】](https://t.zsxq.com/05RfemUFu) - -### 6. 读者经验 - -- [重入锁ReentrantLock源码解析](https://articles.zsxq.com/id_h5xtekb7zbig.html) +- [秋招面试经历](https://t.zsxq.com/06AaI2r7q) +- [拉拉扯扯一个多月后总算收到阿里的offer啦!](https://t.zsxq.com/0ehzrKF1I) diff --git a/docs/md/zsxq/material/notes.md b/docs/md/zsxq/material/notes.md new file mode 100644 index 000000000..42d0c2352 --- /dev/null +++ b/docs/md/zsxq/material/notes.md @@ -0,0 +1,197 @@ +--- +title: 简历:项目描述 +lock: no +--- + +# 简历:项目描述 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +## 个人信息 + +- 姓名 +- 电话 +- 邮箱 +- 毕业时间 +- 工作时间 +- Github:https://github.com/fuzhengwei +- 开源项目:[vo2dto](https://bugstack.cn/md/product/idea-plugin/vo2dto.html) - IDEA Plugin 自动转换对象插件,5.4k 下载使用 + +## 毕业院校 + +- 时间、院校、专业、学位 +- 荣誉、成绩 + +## 专业技能 + +- 熟练掌握 Java 核心知识、JUC、HashMap、斐波那契散列等,具备良好的面向对象编程思想。 +- 熟练掌握 Java 设计模式,如工厂、代理、组合、策略等设计模式,并善用设计原则构建可复用代码。 +- 熟练使用 IDEA、Eclipse、Visual Studio Code、Navicat、PostMan、Git、Maven、SVN 等开发工具。 +- 深入理解 JVM 底层原理,熟悉 JVM 各类垃圾收集器的使用及核心参数的调优,具备一定的 JVM 调优能力。 +- `深入学习 Spring 核心流程模块,如IOC、AOP、依赖倒置等,掌握Spring解决复杂场景所需的分治、抽象和知识(设计模式、设计原则),能从核心原理上解决Spring场景问题。同时,具备基于 Spring 开发 SpringBoot Starter 的技能,减少研发成本,为复杂项目提供通用技术组件。` +- `深入学习 MyBatis 核心流程模块,包括会话、反射、代理、事务和插件,熟练掌握 ORM 框架的设计思想、实现方式和应用价值。并根据需求结合 MyBatis 插件机制,开发企业所需的功能,如数据分页、数据库表路由、监控日志和数据安全等。` +- 深入理解 Spring Boot,Spring Cloud 等微服务框架的设计原理及底层架构,熟悉各种微服务架构设计比如服务注册与发现,服务降级,限流,熔断,服务网关路由设计,服务安全认证架构。 +- 熟悉 Dubbo、Zookeeper 等分布式服务协调与治理等技术。 +- 熟练掌握 MySql,掌握 MySQL 主从同步,读写分离技术以及集群的搭建,具备一定的 SQL 调优能力。 +- 深入理解 Redis 线程模型,熟练掌握 Redis 的核心数据结构的使用场景,熟悉各种缓存高并发的使用场景,比如缓存雪崩,缓存穿透等。 +- 熟练掌握分布式场景中的常见的技术问题及解决,比如分布式锁,分布式事务,分布式 session,分布式任务调度。 +- 熟悉 RabbitMQ、Kafka 等常用的消息中间件进行消息的异步数据处理。 +- 了解分布式搜索引擎 ElasticSearch,并能基于 ELK+Kafka 搭建分布式日志收集系统,以及 x-pack-jdbc 的扩展使用。 +- 熟悉 docker 常用命令,能够实现基于 docker+Jenkins 实现自动化部署 +- 掌握 Linux 常用命令,了解 Nginx 服务器的反向代理、负载均衡、动静分离等。 +- 熟练运用 DDD 四层架构领域驱动设计,构建出易于迭代和维护的工程架构,遵守整洁代码、洋葱架构设计思想。 + +## 工作经历【在校生无】 + +- 公司 +- 岗位 +- 时间 +- 职责【可选】 +- 成绩【可选】 + +## 项目经验 + +### 1. 业务项目 + +#### 1.1 Lottery + +- **项目地址**:[https://bugstack.cn/md/project/lottery/introduce/Lottery抽奖系统.html](https://bugstack.cn/md/project/lottery/introduce/Lottery抽奖系统.html) +- **项目名称**:营销活动平台 - Lottery 微服务抽奖系统 +- **系统架构**:以 DDD 领域驱动设计开发,微服务拆分的分布式系统架构 +- **核心技术**:SpringBoot、MyBatis、Dubbo、MQ、MySQL、XDB-Router、ES、ZK +- **项目描述**:抽奖系统是营销平台的重要微服务之一,可以满足 C 端人群的需求,例如拉新、促活、留存等。该系统运用抽象、分治和 DDD 知识,拆解服务边界,凝练领域服务功能。围绕抽奖服务建设领域服务,包括规则引擎、抽奖策略、活动玩法、奖品发放等。这可以满足业务产品快速迭代上线的需求,同时减少研发成本,提高交付效率。 +- **核心职责**: + - 【高级】构建以 DDD 分层结构的处理方式,搭建整个抽奖系统架构。运用设计原则和工厂、代理、模板、组合、策略等设计模式的综合使用,搭建易于维护和迭代的系统工程。 + - 【高级】鉴于系统内有较多的规则策略过滤,包括准入、人群、风控、A/BTest等需求,为适应系统规模可快速开发和使用的方式,搭建了去中心化的量化人群规则引擎组件。通过业务需求对逻辑的扩展和内置引擎执行器的使用,完成自由组合的人群过滤服务。这降低了共性功能重复开发所带来的成本问题,并提高了研发效率。 + - 【高级】根据实际秒杀峰值场景 `TPS 5000 ~ 8000` 的需求,开发了统一路由组件。该组件不仅可以满足差异化不同字段的分库分表组合,还支持 Redis 库存分片和秒杀滑动库存分块。而且,开发了统一路由 XDB-Router 的 SpringBoot Starter 技术组件。该套组件已经经历了多次大促活动场景的考验,支持横向扩展,可以满足业务规模的快速增长。 + - 【简单】运用模板、策略、工厂三个设计模式,定义抽奖过程标准和实现对应的多类型抽奖的服务模块。 + - 【简单】因活动秒杀的并发场景,将秒杀从最开始的数据库行级锁优化为Redis Key 加锁,又从 Redis Key 的独占锁,优化为滑块锁。优化后整体秒杀有了非常可观的性能提升。 + - 【简单】解耦抽奖流程,把抽奖和发奖用MQ消息串联起来,避免一个流程太长,导致用户一直等待。 + +#### 1.2 ChatGPT AI 问答助手 + +- **项目地址**:[https://bugstack.cn/md/project/chatbot-api/chatbot-api.html](https://bugstack.cn/md/project/chatbot-api/chatbot-api.html) +- **项目名称**:ChatGPT AI 服务化问答中心 +- **系统架构**:DDD 领域驱动设计,构建问答模型 +- **核心技术**:SpringBoot、MyBatis【项目中没有使用,你可以扩展落库了】、MySQL、XXL_JOB、Docker +- **项目描述**:此项目用于通过ChatGPT的能力,解决日常研发学习过程中的通用共性类问题信息处理,并根据所积累的提问进行资料汇总。此项目对接了知识星球提供问答服务,也提供了对接公众号和企业微信的接口,可以按需扩展。 +- **核心职责**: + - 负责项目的整体架构设计,包括选择适合的框架和技术,设计领域模型和服务,保证系统具有高可用性、可扩展性和易维护性。 + - 针对项目中出现的技术问题进行分析和解决,如接口爬虫时遇到的反爬机制、AI接口调用时的异常处理等,同时需要根据用户反馈及时修复bug和改进系统功能。 + - 熟悉SpringBoot框架、DDD架构设计思想、Github仓库管理工具、定时任务调度工具等,掌握Docker容器部署和镜像打包的技术,能够灵活运用不同的技术手段解决实际问题。 + - 整合 ChatGPT API 和网页问答系统,使用 Spring Boot 框架将 ChatGPT API 集成到网页问答系统中。让用户可以通过问答网页或软件向系统提问,并获得 ChatGPT 的回复。 + - 应用 DDD 架构进行领域隔离,通过将不同功能的代码分组到不同的领域中,实现更好的模块化和解耦,使项目易于维护和扩展。在此过程中,我学习到了 DDD 领域驱动设计的模型设计和架构分层,以及领域中聚合、实体、值对象的使用。 + - 使用爬虫获取网页提问并自动回复,用于从网页上获取用户的提问。然后,将这些提问通过任务扫描的方式采集到系统,再转发到 ChatGPT,并将 ChatGPT 给出的答案自动回复给用户。同时对任务扫描的组件进行了扩展,可以支持配置,自动创建出不同的任务类型来扫描所需的数据信息。 + - 此项目使用Git维护分支版本,这样可以更好地组织各个功能模块的迭代,并使不同功能的开发过程更加独立和可控。我也掌握到了很多 Git 的操作实践能力,并且可以结合各类仓库使用。为后续与其他多人开发打下了良好的基础。 + - 最后使用 Docker 打包部署项目并进行部署。通过对 Docker 的使用,熟练的掌握了容器化环境的配置以及项目的部署和维护。 + +### 2. 技术项目 + +- **项目地址**:[https://bugstack.cn/md/assembly/api-gateway/api-gateway.html](https://bugstack.cn/md/assembly/api-gateway/api-gateway.html) +- **项目名称**:API 网关 +- **系统架构**:微服务架构设计、SpringBoot Starter 组件设计、DDD 领域驱动设计 +- **核心技术**:SpringBoot、SpringBoot Starter、Netty、NGINX、SHIRO、JWT、Redis、负载均衡、RateLimiter +- **项目描述**:API网关系统用于统一管理RPC(Dubbo)通信接口,通过协议解析和泛化调用统一对外提供HTTP服务的系统。这套系统是微服务架构设计,分为核心通信、启动引擎、注册中心、管理平台以及上报接口服务。这套API网关也是随着对公司传统庞大的单体应用(All in one)拆分为众多的微服务(Microservice)以后,所引入的统一通信管理系统。用于运行在外部HTTP请求与内部RPC服务之间的一个流量入口,实现对外部请求的协议转换、参数校验、鉴权、切量、熔断、限流、监控、风控等各类共性的通用服务。 +- **核心职责**: + - 构建 API 网关整体核心架构分层设计,拆分出核心通信、服务助手、启动引擎、注册中心、上报服务、管理后台,这样6个工程模块。便于后续的高效迭代和维护工作。 + - 分治处理会话流程,将复杂的会话流程划分为多个阶段,以提高处理效率;将连接(RPC\HTTP\其他)抽象为数据源,为数据的读取和写入提供支持;实现HTTP请求参数解析,确保请求参数的正确处理;引入执行器封装服务调用,提供对各种服务的调用支持;集成权限认证组件(Shiro+Jwt),确保请求的合法性和安全性;实现网关会话鉴权处理,为会话的安全管理提供支持;实现网络通信配置提取,将网络通信的配置信息抽象为可配置的模块,提高配置的灵活性。 + - 设计并实现服务发现组件搭建和注册网关连接、服务配置拉取和组件使用验证、核心通信组件管理和处理服务映射、容器关闭监听和异常管理、订阅服务注册消息驱动网关映射、网关Nginx负载模型配置、动态刷新网关Nginx负载均衡配置和实现网关算力节点动态负载功能。 + +### 3. 框架源码 + +注意:此类内容更适合校招生编写使用。如果你是社招生,更应该参考这样几条方式; + +1. 体现在专业技能上,例如; + +- 1.1 深入学习 Spring 核心流程模块,包括;IOC、AOP、依赖倒置等流程,掌握Spring解决复杂场景所运用的分治、抽象和知识(设计模式、设计原则),在解决Spring场景问题时,可以从核心原理上给出方案。同时也具备基于 Spring 开发 SpringBoot Starter 技能,为复杂项目减少同类共性需求的开发,凝练通用的技术组件,减少研发成本。 +- 1.2 深入学习 MyBaits 核心流程模块,包括;会话、反射、代理、事务、插件等流程,熟练掌握 ORM 框架的设计思想、实现方式和应用价值。并能按需结合 MyBatis 的插件机制,开发属于企业自己所需的功能,包括;数据分页、数据库表路由、监控日志、数据安全等方面。 + +2. 体现在项目经验上,例如;—— 对校招和实习比较有用 +把 Spring、MyBatis 当一个学习项目来描述,这是你在离校前,最可能接触到的一个完整的、成型的、知名的,有企业使用的,框架。你就按照自己学习并开发了这样一个框架为目标来写项目,并描述出这个项目,你用了什么技术栈,解决了什么问题,学习到了哪些。 + +3. 体现在项目应用上,例如; +关于 Spring、MyBatis 的项目,一般都是插件类开发,比如各类的 SpringBoot Starter,MyBatis 插件,都是基于框架的深入整合类技术解决方案,体现在简历上,非常抓眼球。一看你就是有深度和自研能力的研发人员。—— 一般不让你造轮子,但需要你有造轮子的能力,这样企业中一些软件可以被你进行优化和修改。 + +4. 体现在解决问题是上,例如; +在你的自己的业务项目中,渗入一些关于解决了原项目使用 Spring 时,关于感知 Aware 方式或者结合 FactoryBean 包装对象等,所遇到的问题,因为你学习过源码,所以非常清晰这样的流程,因此解决了一个问题。通用 MyBatis 也适用于这样的描述方式,包括;事务、查询次数、批查询、插件能监听到的四个类(ParameterHandler、ResultSetHandler、StatementHandler、Executor )你给了更好的选择。 + +#### 3.1 手写MyBatis + +- 项目名称:手写 MyBatis +- 项目架构:Object Relational Mapping +- 项目描述:MyBatis 是 Java 应用编程开发中,非常常用的技术框架。需要较深入的学习和使用,具备 MyBatis 插件开发能力,解决日常场景问题。才能更好更高效的工作。为此,深度学习了《手写MyBatis:渐进式源码实践》一书,完整实现了一个 MyBatis ORM 框架。技术积累颇深,提高了设计思维和编码能力。 +- 个人收获: + - 首先学习到为什么日常使用 MyBatis 时,可以仅提供 IDao 接口,就可以关联对应配置的SQL语句,完成数据库操作。这是因为做了代理与映射,封装调用逻辑到 SqlSession 会话功能实现中。 + - 掌握了数10种设计模式的运用,其中感触最大的是 MyBatis 如何管理多边服务的设计。如创建会话模型,统一调度执行器使用和数据的封装,并把这些复杂的操作,通过 SqlSessionFactory 工厂统一对外提供 SqlSession 服务。 + - 彻底了解并掌握了 MyBatis Plugin 设计模型,这个技术可以让我基于 MyBatis 做出日志监控、库表路由、字段加解密等操作。为以后编程工作,提供更多的解决方案。 + - 除此之外,还有一级缓存、二级缓存的装饰器模式使用,MyBatis 开发后与 Spring、SpringBoot 整合的实现,让我对 MyBatis 的全体系开发流程和对应的技术整合,已经非常清晰,便于以后应用和从根上快速排查解决问题,并给出合理的技术方案。 + +#### 3.2 手写Spring + +- 项目名称:手写 Spring +- 项目架构:Spring Framework +- 项目描述:Spring 是 Java 应用编程开发中,非常常用的技术框架。在 Spring 框架下,其他的组件都需要进行扩展依赖注入到 Spring 容器进行统一管理。同时日常的开发中,80%的问题场景,都需要调试到 Spring 的源码才能更好的解决,为此深度学习 Spring 源码非常有必要。 +- 个人收获: + - 首先对 Spring 源码的学习,是 Spring 对整个 Bean 对象声明周期所需节点的拆解,把一个对象细化到这样的一个程度,才能让我们在使用 Spring 的时候,基于对外暴露的接口和类,更好的扩展各个功能节点。 + - 掌握了最重要的模板模式,Spring Bean 周期容器的使用,就是流程复杂的大模板,通过模板定义了全系的调用结构。这样的设计也为我以后在开发业务代码时候提供了参考建议,为我们自身复杂的业务定义出模板结构。 + - 分治、抽象、知识,Spring 源码中,有非常多的接口的实现、抽象类的定义、功能类的继承,这是非常重要的设计手段,因为它可以让我们的代码具有立体化,分层化,把各个职责放到不同的类维护可以让以后的迭代成本更低。 + - 细节的学习,Spring 源码中有非常多的技术细节,尤其是像Event事件设计、Aware依赖倒置、AOP切面实现、Bean循环依赖等,都是在特定复杂场景中优秀的设计方案。这些设计模式、设计原则,都可以指导业务代码开发。 + +### 4. 组件项目 + +#### 4.1 组件项目01 + +- **项目地址**:[https://bugstack.cn/md/assembly/middleware/%E7%AC%AC%201%20%E7%AB%A0%20%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%AD%E9%97%B4%E4%BB%B6.html](https://bugstack.cn/md/assembly/middleware/%E7%AC%AC%201%20%E7%AB%A0%20%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%AD%E9%97%B4%E4%BB%B6.html) +- **项目名称**:自动化API提取和交付质量分析服务 +- **系统架构**:基于 IDEA Plugin 插件开发与 ASM 字节码增强技术,采集工程运行信息 +- **核心技术**:IDEA Plugin SDK、ASM、Swing、MySQL、SpringBoot、TTL +- **项目描述**:这是一款用于帮助研发与测试,建立起标准可调试的动态自动化 API 服务,提升交付质量的系统。插件以 IntelliJ IDEA 为底座,通过 SDK 的方式结合字节码增强技术,采集 IntelliJ IDEA 单元测试运行时的接口信息,包括:工程名称、开发分支、开发人员、接口名称、出参、入参、异常、耗时等,并把这些通过 Socket 回传到数据中心,处理为整个工程接口的全地图,让整个工程从开发、调试、提测到交付都成透明化,提高整体的交付质量。 +- **核心职责**: + - 以架构师的职责调研如何降低每次开发到提测阶段,所需要人工编写接口文档的成本。以及通过什么方式无侵入式的处理接口文档的生产和维护。 + - 通过对 ASM、Javassist、Byte-Buddy 等字节码框架的调研和测试,找到符合当前场景所需要的字节码增强框架,其中 Byte-Buddy 上手难度更小,更易于后续其他研发共同开发。 + - 运用 IDEA Plugin SDK 插件的开发技术,拦截运行动作,插入字节码增强组件包,将采集信息回传到 API 数据中心进行分析、处理和提供最终的 API 文档。 + +#### 4.2 组件项目02 + +- **项目地址**:[https://bugstack.cn/md/assembly/middleware/%E7%AC%AC%201%20%E7%AB%A0%20%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%AD%E9%97%B4%E4%BB%B6.html](https://bugstack.cn/md/assembly/middleware/%E7%AC%AC%201%20%E7%AB%A0%20%E4%BB%80%E4%B9%88%E6%98%AF%E4%B8%AD%E9%97%B4%E4%BB%B6.html) +- **项目名称**:ES-ORM 框架开发 +- **系统架构**:技术迁移 ORM 开发到 ES 使用上,建设 SpringBoot Starter +- **核心技术**:SpringBoot、ES、MyBatis、dom4j、x-pack-jdbc +- **项目描述**:开发 ES-ORM 框架,降低研发使用 ES 数据查询硬编码方式的维护和迭代成本。通过 ORM 框架对 XML 和注解配置的方式,解析和映射语句处理器,代理 IESDAO 接口为具体的处理对象,并把对象通过扫描符合的路径和注解运用 BeanDefinitionRegistryPostProcessor 把代理对象注册到 Spring Bean 容器中进行统一管理和使用,最终完成 ORM 框架的数据查询和封装操作。 +- **核心职责**: + - 负责组内的脚手架和提效工具的建设,对系统中通用共性的功能进行摘取凝练成统一的组件进行使用。这其中不只包括ES-ORM框架,还包括缓存组件、秒杀组件、服务治理、全链路监控等。 + - 对 ES-ORM 框架功能模块进行拆解和实现,分为;解析入口、数据源、代理层、绑定层、执行模块、异常处理、缓存服务等。 + - 分阶段功能验证和使用,逐步本地化,将内部的服务通过 SPI 的机制进行包装整合,允许组内其他场景诉求的扩展。 + +#### 4.3 组件项目03 + +- **项目地址**:[https://bugstack.cn/md/assembly/middleware/%E7%AC%AC%203%20%E7%AB%A0%20%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86%EF%BC%8C%E7%BB%9F%E4%B8%80%E7%99%BD%E5%90%8D%E5%8D%95%E6%8E%A7%E5%88%B6.html](https://bugstack.cn/md/assembly/middleware/%E7%AC%AC%203%20%E7%AB%A0%20%E6%9C%8D%E5%8A%A1%E6%B2%BB%E7%90%86%EF%BC%8C%E7%BB%9F%E4%B8%80%E7%99%BD%E5%90%8D%E5%8D%95%E6%8E%A7%E5%88%B6.html) +- **项目名称**:服务治理 SpringBoot 中间件 - 凝练通用共性功能,降低开发成本,提高交付效率 +- **系统架构**:SpringBoot Starter 组件开发 +- **核心技术**:熔断、降级、限流、切量、白名单、人群控制 +- **项目描述**:该SpringBoot Starter中间件实现了熔断、降级、限流、切量、白名单等服务治理功能,减少了开发工作量和出错风险。利用SpringBoot的自动化配置机制简化了集成和使用,并提供了可扩展接口,以满足不同场景的需求。 +- **核心职责**: + - 鉴于组内同类需求的重复开发,设计并实现服务治理 SpringBoot Starter 中间件,提高开发效率和降低重复开发成本。 该中间件的核心功能包括服务治理中的熔断、降级、限流、切量和白名单等。 + - 通过利用SpringBoot的自动化配置机制,该中间件可以简化集成和使用,同时提供足够的配置选项以满足不同场景的需求。 + - 此外,该中间件还提供了可扩展的接口,方便用户根据自身需求扩展功能,从而更好地满足不同的业务需求。 + +#### 4.4 组件项目04 + +- **项目地址**:[https://bugstack.cn/md/project/lottery/Part-2/%E7%AC%AC10%E8%8A%82%EF%BC%9A%E5%AE%9E%E7%8E%B0%E5%92%8C%E4%BD%BF%E7%94%A8%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8.html](https://bugstack.cn/md/project/lottery/Part-2/%E7%AC%AC10%E8%8A%82%EF%BC%9A%E5%AE%9E%E7%8E%B0%E5%92%8C%E4%BD%BF%E7%94%A8%E5%88%86%E5%BA%93%E5%88%86%E8%A1%A8.html) +- **项目名称**:DB-Router 数据库路由组件 +- **系统架构**:基于 AOP、Spring 动态数据源切换、MyBatis 插件开发、散列算法等技术,实现的 SpringBoot Starter 数据库路由组件 +- **核心技术**:AOP、AbstractRoutingDataSource、MyBatis Plugin StatementHandler、扰动函数、哈希散列、ThreadLocal +- **项目描述**:此组件项目是为了解决在分库分表场景下,开发一款可以应对自身业务场景多变特性,即支持个性的分库分表、只分库或者只分表以及双字段控制分库和分表,也可以自定义扩展监控、扫描、策略等规则,同时又能满足简单维护迭代的数据库路由组件。这块路由组件在设计实现上除核心技术外,还进行了严格雪崩标准(SAC) 测试,确保数据的散列效果。 +- **我的职责**: + - 设计分库分表数据库路由组件的架构模型结构,运用设计模式对这块组件进行功能的分治和实现。 + - 调研平方散列、除法散列、乘法散列、哈希散列以及斐波那契散列,并结合雪崩测试,选择了一块适合数据库路由的散列算法,并做功能的开发实现。 + - 引入 MyBatis Plugin 插件开发功能,对执行的 SQL 语句动态变更表信息,做到执行对应表的策略设计。同时扩展了监控和日志功能,方便在调试和验证时,可以打印相关SQL语句。 + +## 自我评价 + +- 把自己描述成有技术追求的、有团队精神、有奋斗品质的优秀好青年。 diff --git a/docs/md/zsxq/material/openai.md b/docs/md/zsxq/material/openai.md new file mode 100644 index 000000000..2e7384050 --- /dev/null +++ b/docs/md/zsxq/material/openai.md @@ -0,0 +1,93 @@ +--- +title: 问答:ChatGPT 回答星球问题 +lock: no +--- + +# 知识星球 + ChatGPT,自动回答星球用户提问 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +2022年12月25日,我把 ChatGPT 通过 API Keys 的对接方式,拉倒了微信群。经过群友热情噼里啪啦的一顿提问后,ChatGPT $18 的配额就被干没了🤨!而且整理群友的提问,其实大部分都是没啥意义的,比如问:“一天吃几顿饭🍚能撑死?” 所以小傅哥希望用这个东西做点有意义的事! + +## 一、前言:我要干啥? + +经过对 ChatGPT 的了解和使用,尤其是对技术问题的广度和深度回答,某些时候甚至比在浏览器检索还要有用,ChatGPT 可以更精准、更简单、更直接。 + +**所以**,小傅哥研究着把 ChatGPT 接入到知识星球,当粉丝伙伴需要提问一些常见技术问题时可以直接提问给星球中的 ChatGPT 来回答。而那些星球中的项目学习问题和需要参考小傅哥的过往经验来处理的问题,再提问给小傅哥。 + +这样一方面可以提高粉丝伙伴的问题回答的时效性,另外一方面也可以帮助小傅哥减少一定的工作量。岂不美哉!说干就干,搞! + +## 二、爬虫:要怎么干? + +### 1. 设计 + +🤔 我要开发一个程序,把**知识星球**与**ChatGPT**连接起来! + +
+ +
+ +左侧是**知识星球**,右侧是**ChatGPT**的**OpenAI**。我希望通过我开发的这个应用程序,从知识星球拉取用户提给我的问题,之后把问题塞给**OpenAI**,得到答案以后再推给知识星球中提问者。 + +### 2. 开发 + +
+ +
+ +反手就搭建了一个 ZSXQ-Api SpringBoot 领域驱动设计 DDD 架构工程,在工程中封装知识星球 API 以及 OpenAI 调用 API,当然这里还得有一个定时的随机任务来处理需要回答的问题。 + +哈哈哈,小傅哥这里留了个心眼。不能让程序一直有规律的跑,也不能半夜还在跑。首先这样的调用可能会触发风控机制,也会让你自己的 OpenAI 大量消耗。虽然知识星球或者任何一个网站你都可以只用自己的信息模拟浏览器行为,但也不能作死。 + +### 3. 部署 + +接下里就是打包镜像文件和部署 Docker 了,你可以在自己的 Docker中跑,有钱的也可以放到云服务器上跑。 + +```java +# 基础镜像 +FROM openjdk:8-jre-slim +# 作者 +MAINTAINER xiaofuge +# 配置 +ENV PARAMS="" +# 时区 +ENV TZ=PRC +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +# 添加应用 +ADD target/zsxq-api.jar /zsxq-api.jar +# 执行镜像 +ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS /zsxq-api.jar $PARAMS"] +``` + +
+ +
+ +## 三、机器:回答问题! + +在小傅哥的知识星球【码农会锁】中添加了一个叫 @ChatGPT 的机器人,只有对它的提问才会被 OpenAI 回答,其他的提问仍旧是小傅哥来回答。—— **希望用这样的技术手段,帮助到很多小白学习**。 + +
+ +
+ +--- + +
+ +
+ +
+ +
+ +
+ +
+ +🤔 考虑到 OpenAI 回答问题的频繁性,以及很多问题可能比较初级,所以设定为不提醒。**只回答给提问的用户可见**,所以你对他提问只有你自己会收到回复。 + + diff --git a/docs/md/zsxq/material/speaking-skills.md b/docs/md/zsxq/material/speaking-skills.md new file mode 100644 index 000000000..f38d86aba --- /dev/null +++ b/docs/md/zsxq/material/speaking-skills.md @@ -0,0 +1,290 @@ +--- +title: 面试:嘴笨问题 +lock: no +--- + +# 面试:嘴笨问题 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn/) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +面试时介绍,怼着简历读,感觉不像自己做的。面试官提问,东一句,西一句,讲不出重点。为啥?一方面是不熟悉,一方面是不会结构化表达,导致这脸上的**死嘴**就像不会动了一样。 + +
+ +
+ +**我也想成为 Offer 收割机!** + +同样的一份项目,同样的一个积累,在面试中,”嘴笨“的会吃很的亏。但嘴笨是有治疗办法的,我们可以刻意的锻炼自己的表述能力,形成一个结构化的过程阐述模型,让听众在框架内清晰的理解阐述方所要表达的内容。表述的清晰了,也就更容易交流了。 + +曾经,我们组有个兄弟,2次晋升述职失败。第3次的时候,评委都着急了。你就说,你到底是做什么的,你的价值在哪,你做和其他人做有什么不同? + +好在,调整部门后,他这次的直属领导是一个很会表达的leader,几句话把大家对他的认可度拉回来了。大致如下,他说; + +各位评委好,我来补充几句。坦白说,他在之前的述职中可能因为紧张,表达上没有完全展现出他应有的价值和担当。但我想强调的是,他确实是我们XXX支付营销组的第一负责人,肩负着极其重要的职责。这个场景每年处理数以XXX亿的交易规模,交易量巨大且复杂,任何细微的失误都可能带来严重影响。 + +在他的带领下,系统全年无重大事故,稳定性和可靠性得到了充分保障;同时,团队100%完成了所有需求迭代,确保业务能够快速响应市场和用户的变化。他不仅技术能力过硬,更在跨部门合作中赢得了广泛认可,展现了极强的责任心和团队精神。 + +我相信,随着他逐渐适应和沉淀,他的表达和展示能力会越来越好,但他的核心价值和贡献是毋庸置疑的。希望大家能够看到他背后的实力和潜力,给予他应有的认可和支持。 + +--- + +那一场述职后,他晋升了,第一次晋升。而我也是那年参加述职,我也晋升了,100%全票通过。因为领导给我们培训了很多关于如何阐述自己的价值,怎么做到结论先行,如何承上启下,有因有果的,框架式的循序渐进的铺开一个故事,让评委了解并认可你的能力。 + +## 一、我嘴臭,不会说话? + +面试官让我介绍项目,我对着简历读了介绍,又说了几条核心实现。面试官问我项目是自己做的吗,有做过总结吗,为什么还需要看着简历来讲?不过面试官并没有问我,而是不感兴趣一样,开始转到下一个话题,之后再问了几句八股文,好像凑够了时间一样,说今天的面试到这里,感谢你的参加。 + +其实很多时候,程序员在面试/述职的时候,并没有一个清晰的逻辑来表述自己所做的事项,甚至往往找不出所做项目的核心价值。就像; + +- 问:你先介绍下项目。答:我做了一个拼团系统,可以让用户参与拼团购买商品。拼团里有组队,完成拼团组队成团,之后再mq通知,让商城系统发货。大概这样。 +- 问:这项目的难点是什么?答:我都做出来了能有什么难点,我做不出来才难。 +- 问:这项目最大价值是什么?答:需求就是这样呀,价值不是产品关心的吗? +- 问:这项目有什么亮点?答:好像也没什么亮点,就是写数据返回结果。 +- 问:你在这个项目,发挥的最大价值是什么?答:写代码,改bug呗。 + +综上,这是一个反例,让人举得好像也没做什么,也没有聊下去的想法。尤其是面试官,他也不是你,他不知道你到底做了什么,所以你阐述不出亮点和价值的时候,也就没法对你进行有效提问。 + +面试官您好,我的项目是基于对市面同类竞品的调研,包括;拼多多、京东、美团、滴滴等大厂对拼团场景的使用,综合了产品实际功能诉求和未来的发展趋势,设计了一套拼团营销服务系统。 + +该项目目标是通过拼团优惠组队下单,提升交易GMV和用户自传播能力。系统采用微服务架构,基于DDD领域驱动设计拆分活动、人群、交易等核心领域,运用责任链和规则树设计模式,实现试算、锁单、结算等关键流程的解耦和高扩展性。 + +同时为保证系统高可用和性能,设计了异步线程并行处理、Redis缓存与BitMap人群标签过滤、MQ与HTTP双重结算回调机制,以及分布式锁和无锁化库存抢占方案。整体方案有效支撑高并发交易,提升了系统的鲁棒性和维护效率。 + +这样的一个STAR模型阐述,面试官就会理解`情景`、`任务`、`行动`、`结果`,分别都是什么,但 STAR 法则不非得一定顺序的阐述,也可以结论先行,突出重点结果和情景,在阐述任务和行动。往往结果先行的表述,会让面试官更为感兴趣,第一句就是重点,之后再讲支撑重点的过程条件,条件又可以框架式思维、总分思维、线性思维来阐述。 + +**注意阐述时观察面试官的眼神,重点的地方,眼睛一亮,可以多讲一点** + +## 二、你需要,逻辑思维! + +什么是逻辑思维?简单说,就是有条理、有层次、有逻辑的思考和表达方式。而在面试中,最有效的逻辑思维模型就是**金字塔结构**。 + +金字塔结构,是由线性思维组成的结构化思维模型。当你把结构化的思维聚象成一幅画后,会形成类似三角形结构的树状图。它直观地体现了由结论、论点、论据组成的"先总后分"结构。 + +就像盖房子一样,你得先有个框架,再往里面填砖头。字塔结构就是你表达的框架,让面试官能够清晰地跟上你的思路。 + +> 金字塔原理是*由巴巴拉·明托提出的逻辑学术语*,*指通过层次化、结构化的思维组织方式提升信息传递效率的沟通技术*。 + +### 1. 结论先行 - 别让面试官猜谜 + +面试官脑子不行,一天面那么多人,脑子都僵化了屁的。如果我们不结论先行,在面试官大脑里已经塞满了各种业务、流程、背景、冲突、要素、论点、论证等信息,就没有空间接受我们阐述的重要观点了。 + +**程序员汇报问题对比:** + +**反面例子:** + +> 领导,我们这个项目遇到了一些问题,昨天测试那边反馈说登录模块有bug,然后我查了一下发现是数据库连接的问题,但是运维说服务器配置没问题,后来发现是代码里面有个地方写错了,改了之后又发现另一个接口也有类似问题,然后我又改了一遍,现在基本上差不多了,但是可能还需要再测试一下... + +听到这里,领导是不是已经骂你,让说人话了?这就是典型的流水账式汇报。 + +**正面例子:** + +> 领导,项目已解决登录bug,预计今天下午可以正常上线。问题原因是代码逻辑错误导致数据库连接异常,已修复相关代码并完成回归测试。具体详细的问题和修复过程,我会出一份事故报告,在下次周例会进行分享。 + +看到区别了吧?结论先行,一句话就知道结果,后面的都是支撑理由。 + +**程序员简历案例对比:** + +**反面例子:** + +> 我们系统之前用的是单体架构,随着业务发展,用户量增加,系统响应越来越慢,经常出现超时,数据库压力很大,运维也很困难,代码耦合严重,一个小改动可能影响整个系统,测试周期很长,发布风险很高,所以我们决定进行微服务改造... + +**正面例子:** + +> 我主导了公司核心交易系统的微服务架构改造,将系统响应时间从3秒优化到300ms,支撑了10倍业务增长。改造背景是单体架构已无法满足业务快速发展需求,存在性能瓶颈、扩展困难、发布风险高等问题。 + +### 2. 以上统下 - 让逻辑有层次 + +以上统下就是在金字塔的纵向结构中,上一层是下一层的核心观点或结论,上一层统领下一层。简单说,就是大观点管小观点,小观点支撑大观点。 + +**程序员技能展示案例:** + +✅ **结构化表达:** +> 我具备全栈开发能力,能够独立完成项目从0到1的建设: + +- **后端技术栈:** 精通Java Spring生态,熟练使用MySQL、Redis、MQ等中间件 +- **前端技术栈:** 熟练React/Vue框架,具备移动端H5开发经验 +- **架构设计:** 具备微服务架构设计经验,了解分布式系统常见问题及解决方案 +- **项目管理:** 有敏捷开发经验,能够进行需求分析和技术方案设计 + +这样的表达,层次清晰,每个小点都在支撑"全栈开发能力"这个大结论。 + +### 3. 归类分组 - 让信息有序 + +归类分组是根据信息的共同属性分组的,共同属性包括但不限于性质、功能、方向、层次、对象、时间等。分组的基本原则符合"MECE原则",即"相互独立,又完全穷尽",各要素信息之间"不重不漏"。 + +**程序员项目需求讨论案例:** + +**反面例子:** + +> 产品:这个拼团系统你看一下,用户要能登录,然后可以参与拼团,还要有试算功能,对了还要锁单,组队成功后要结算,如果不成功要退单,还要有个页面能查看拼团数据,另外要支持微信登录,还有就是要有拼团规则配置,对了忘了说,还要有库存管理,还有就是要支持分布式锁... +> 程序员:等等,你能不能整理一下,我有点乱了... + +这样的需求沟通,东一句西一句,没有章法。程序员跟产品讨论一天,产品需求出来了,程序员一天没干活!打回去重写去! + +**✅ 结构化的表达方式:** + +> 产品:拼团营销系统需求如下: +- **核心交易:** 试算、锁单、组队结算、退单处理 +- **数据管理:** 拼团数据查看、库存管理、规则配置 +- **用户体验:** 微信登录、页面展示、状态通知 + +✅ **按业务领域分组:** +> 我有3年多项目开发经验,涉及多个业务领域: + +- **电商领域:** 负责商品管理系统、订单系统、支付系统的开发和维护 +- **金融领域:** 参与风控系统、清算系统的核心模块开发 +- **营销领域:** 主导优惠券系统、积分系统、拼团系统的架构设计和实现 + +✅ **按技术栈分组:** +> 我的技术能力覆盖前后端及运维: + +- **后端开发:** Java、Spring Boot、MyBatis、MySQL、Redis +- **前端开发:** React、Vue、TypeScript、Webpack +- **运维部署:** Docker、K8s、Jenkins、监控告警 + +### 4. 逻辑递进 - 让思路有顺序 + +逻辑递进是金字塔结构的最后一个原则,是指按照一定的顺序进行排序,这个顺序需要遵守逻辑性原则。 + +#### 4.1 时间顺序 +又称为步骤顺序,是指按照事物发展的流程进行排序。 + +**程序员项目开发流程案例:** +> 我负责的拼团系统开发,严格按照标准流程推进: + +- **第一阶段:** 需求调研和技术方案设计(1周) +- **第二阶段:** 核心功能开发和单元测试(3周) +- **第三阶段:** 联调测试和性能优化(1周) +- **第四阶段:** 灰度发布和全量上线(1周) + +#### 4.2 空间顺序 +又称为结构顺序,按照事物的结构进行排序。 + +**程序员系统架构案例:** +> 我设计的微服务架构采用分层设计: + +- **接入层:** 网关负责路由、限流、鉴权 +- **业务层:** 用户服务、商品服务、订单服务、支付服务 +- **数据层:** MySQL主从、Redis集群、MQ消息队列 +- **基础层:** 监控、日志、配置中心、注册中心 + +#### 4.3 重要性顺序 +按照重要程度进行排序,从最重要到最不重要。 + +**程序员核心能力案例:** +> 我认为一个优秀程序员最重要的能力排序是: + +- **第一:** 学习能力和解决问题的能力(技术更新快,这是根本) +- **第二:** 系统设计和架构思维(决定系统的可扩展性和稳定性) +- **第三:** 编码规范和工程化能力(保证代码质量和团队协作) +- **第四:** 沟通协作和业务理解(技术服务于业务) + +#### 4.4 演绎顺序 +经过一定的逻辑推演出结论,通常用到的演绎表达方式:大前提-小前提-结论,提出问题-找到原因-解决方案。 + +**程序员问题解决案例:** +- **问题:** 系统在高并发场景下出现频繁超时 +- **原因分析:** 通过监控发现数据库连接池耗尽,慢SQL导致连接长时间占用 +- **解决方案:** 优化慢SQL、增加数据库连接池、引入Redis缓存热点数据 +- **结果:** 系统响应时间从5秒降低到500ms,支撑10倍并发量 + +--- + +掌握了字塔结构的四个原则,你就有了表达的框架。但光有框架还不够,还需要在实际面试中灵活运用。记住,**结构化思维不是为了炫技,而是为了让面试官更容易理解你的价值**。 + +下次面试时,试试用字塔结构来组织你的回答,你会发现面试官的眼神都不一样了。 + +## 三、套路,总得人心 + +掌握了字塔结构的四个原则,你已经有了表达的基本框架。但在实际面试中,光有框架还不够,你还需要一些具体的思考工具来快速组织语言。这些工具就像是你的"套路",但这些套路不是为了忽悠,而是为了让你的表达更加清晰、有逻辑。 + +好的套路,总能得人心。因为它让沟通变得高效,让听众更容易理解你的价值。 + +### 1. 5W1H分析法 - 全面思考问题 + +5W1H是一个经典的思考框架:Who(谁)、What(什么)、When(何时)、Where(何地)、Why(为什么)、How(如何)、How much(多少)。 + +**程序员面试介绍项目经验案例:** + +> 面试官,关于我负责的用户登录模块优化项目,我用5W1H来介绍一下: + +- **Who(谁):** 我作为后端负责人,与前端工程师和测试工程师协作 +- **What(什么):** 优化登录响应速度,从平均3秒降到500ms以内 +- **When(何时):** 项目周期2周,已成功上线运行3个月 +- **Where(何地):** 主要涉及登录服务、Redis缓存层和数据库层 +- **Why(为什么):** 用户反馈登录太慢,日活跃用户流失率达到15% +- **How(如何):** 引入Redis缓存、优化SQL索引、异步处理用户信息 +- **How much(多少):** 最终用户满意度提升30%,登录成功率从85%提升到99.5% + +这样的介绍,信息全面,逻辑清晰,面试官能快速了解你的项目价值和技术能力。 + +### 2. 流程思维 - 按时间线思考 + +流程思维是按照时间顺序,将一个事件进行分解,可以简单地分为:事前、事中、事后。 + +**程序员向产品汇报线上bug处理案例:** + +> 产品,关于昨天的支付异常问题,我按流程汇报一下: + +- **事前(预防):** 我们有监控告警,但这次是新场景,监控覆盖不到 +- **事中(处理):** 收到用户反馈后,10分钟定位问题,30分钟修复上线 +- **事后(改进):** 已补充监控规则,增加异常场景的单元测试 + +这种表达方式让产品清楚地了解整个处理过程,也体现了你的专业性。 + +### 3. 故事思维 - 让技术有温度 + +一个好的故事必备四个要素:背景、冲突、问题、解决方案。程序员往往只会说技术,不会讲故事,但故事思维能让你的技术更有说服力。 + +**程序员向面试官介绍项目亮点案例:** + +- **背景:** 我们电商系统在双11期间面临10倍流量冲击 +- **冲突:** 原有单体架构无法支撑,系统频繁宕机,用户投诉激增 +- **问题:** 如何在有限时间内快速提升系统承载能力? +- **解决方案:** 我主导了核心交易链路的微服务拆分,引入Redis集群和MQ削峰,最终支撑了双11零故障运行,GMV同比增长300% + +这样的表达,有情节、有冲突、有结果,比单纯的技术介绍更吸引人。 + +### 4. 经营分析法 - 用数据说话 + +经营分析报告的表述四段论:陈述事实、作出判断、寻找归因、改进方案。这个方法特别适合向领导汇报工作。 + +**程序员向领导汇报系统性能优化成果案例:** + +> 领导,关于本月系统性能优化成果汇报: + +- **陈述事实:** 系统响应时间从平均2秒降到500ms,环比提升75%,同比提升80% +- **作出判断:** 这个数据超出了我们的预期目标(目标是降到1秒以内) +- **寻找归因:** 主要得益于数据库索引优化和Redis缓存策略调整,其中缓存命中率提升到95% +- **改进方案:** 下一步计划引入CDN加速静态资源,预计还能再提升20%性能 + +这种汇报方式,有数据、有分析、有规划,领导听了会很满意。 + +### 5. PDCA循环 - 持续改进思维 + +PDCA是Plan(计划)、Do(执行)、Check(检查)、Action(行动)的循环。这个思维模型特别适合展示你的项目管理能力。 + +**程序员向测试汇报代码质量提升计划案例:** + +> 测试同学,关于提升代码质量,我有个PDCA计划: + +- **Plan(计划):** 制定代码规范,引入SonarQube静态检查,目标是bug率降低50% +- **Do(执行):** 已配置好检查规则,团队开始按规范开发,每次提交都会自动检查 +- **Check(检查):** 运行两周后,发现bug率确实下降了30%,但还有改进空间 +- **Action(行动):** 下个循环计划增加单元测试覆盖率要求,从60%提升到80% + +这样的表达,体现了你的系统性思维和持续改进意识。 + +--- + +这五个思考工具,就像是你的"武器库"。不同的场景用不同的工具,让你的表达更有针对性。记住,**套路不是为了套路,而是为了更好地传达你的想法和价值**。 + +在面试中,当面试官问你项目经验时,你可以用故事思维;当问你如何解决问题时,你可以用5W1H;当问你项目管理经验时,你可以用PDCA。灵活运用这些工具,你的表达会更加专业和有说服力。 + +最后,关于面试、述职,都要提前写好逐字稿,反复的练习,让自己形成阐述过程记忆。这样在回答面试官问题也不会因为紧张而导致没法正常表达。加油! diff --git a/docs/md/zsxq/material/student-learn-advanced.md b/docs/md/zsxq/material/student-learn-advanced.md new file mode 100644 index 000000000..b6c42b78d --- /dev/null +++ b/docs/md/zsxq/material/student-learn-advanced.md @@ -0,0 +1,87 @@ +--- +title: 路线:从小白到大佬,实战项目进阶路线 +lock: no +--- + +# 路线:从小白到大佬,实战项目进阶路线 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +经过🇨🇳10.1假期的狂卷,小傅哥的星球,又一个新业务项目《MVC + DDD 双架构 - 小型支付商城》完结啦。至此已交付6套业务、7套组件以及源码学习和基础教程,**共300节+课程!** 这也代表着,从小白到大佬的一整条进阶路线,小傅哥全部给你搭建出来了! + +
+ +
+ +**从不废话,强的可怕!** + +在小傅哥给大家搭建的原创体系化成长路线学习内容中,可以系列的掌握,MVC 架构、DDD 架构,以及设计模式的实际场景巧妙运用,也有全体系的 Spring、SpringBoot、MyBatis 等基础框架的使用,综合微服务分布式架构 RPC、MQ、分库分表、任务调度、本地缓存、分布式环境、熔断、降级、限流、动态配置中心等的使用。以及普罗米修斯 + Grafana 监控、前端热力图,在结合 Docker、Portainer、Nginx、Jenkins、Github Actions 等运维实施能力的掌握。 + +可以这么说,小傅哥在大厂用到的技术体系,会全程在项目中陆续体现出来,循序渐进的教给你使用。等你进入公司中,做这些内容都不会陌生了。甚至你还可以给公司提出可靠性解决方案,让领导对你刮目相看! + +## 一、进阶路线 - 300+节课程 + +### 1. 课程目录 + +这是一整套的实战项目学习进阶路线,从小白到大佬,全程视频手把手带着从0到1,一步步完成项目的设计、开发和上线。在整套内容学习过程中,小傅哥为你提供了非常好的技术交流社群,及时解决学习问题。还包括调试你的问题代码,带你快速🔜出坑! + +
+ +
+ +- 首先,这一整套全体系的学习课程比私教培训实惠,更比培训班上万的培训费便宜。可能也就是培训班1天的💰钱,就能学习到这一整套内容了。 +- 之后,你学习的整套课程,就是小傅哥这个架构师自己全部原创编写的。这也就是说,你所提到的任何问题,小傅哥都能给你解答和讨论。 +- 那么,这么实惠的课程,成体系的课程,还是架构师编写的。还有什么可犹豫的,完全可以撸起来了! + +### 2. 课程资源 + +**课程链接**:- 整个课程路线参考【课程日历图📅】,对应的资源链接汇总如下; + +- 【3星】[(1.87w) Deepseek RAG、MCP、Agent 智能体](https://t.zsxq.com/Zq7hV) +- 【2星】[(0.6W) 小型支付商城系统 - 双架构开发(带小白入门)](https://t.zsxq.com/3X9GA) +- 【4星】[(1.41W) 大营销平台系统 - 微服务&分布式](https://t.zsxq.com/199mpn9Lt) +- 【3星】[(0.77W) OpenAi(ChatGPT\ChatGLM) 微服务应用体系构建](https://t.zsxq.com/19aSkDvYB) +- 【4星】[(1.28w) Lottery DDD分布式抽奖系统](https://t.zsxq.com/qrUSd) +- 【2星】[(0.78w) IM实战(Netty+JavaFx):仿桌面版微信聊天](https://t.zsxq.com/NcPgw) +- 【2星】[(0.11W) OpenAI 代码自动评审组件](https://t.zsxq.com/gYEVX) +- 【2星】[(0.23W) BCP透视业务流程 - 监控系统](https://t.zsxq.com/CVzpL) +- 【2星】[(0.15W) 动态线程池组件](https://t.zsxq.com/nSebo) +- 【2星】[(0.31w) 支付SDK设计和开发](https://t.zsxq.com/19WqNkhr2) +- 【5星】[(0.78w) API网关:中间件设计和实践](https://t.zsxq.com/xIe9E) +- 【3星】[(0.38w) SpringBoot 中间件设计和开发](https://t.zsxq.com/LZ82D) +- 【2星】[(0.39w) IDEA Plugin 插件开发](https://t.zsxq.com/VMaSW) +- 【5星】[手写Mybatis:渐进式源码实践](https://t.zsxq.com/xMQ6W) +- 【3星】[分布式技术栈基础教程](https://t.zsxq.com/Hl0W4) + +### 3. 课程收获 + +1. 熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对其使用源码所提供的接口、类、SPI标准开发各类组件,有一定的设计思路和落地能力。 +2. 熟练使用 RPC(Dubbo)、MQ(RabbitMQ、Kafka)、Redis、分库分表、XXL-JOB、Zookeeper、等分布式技术栈,在各个场景的运用。 +3. 深度了解 MVC、DDD 架构知识,和框架搭建技巧以及微服务设计思想。 +4. 熟练使用设计模式、设计原则,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。 +5. 熟练运用 Docker、Portainer 等Dev-Ops手段,部署和运维 Nginx、Skywalking、Otter、Canal、ELK 等技术框架。 +6. 具备多类项目的开发能力,业务项目、技术项目、组件项目,以及对应的架构模型和设计思路。 +7. 积累八股问题、项目问题、场景问题的解答技巧,同时会有辅助简历编编写优化和评审,提高面试通过率。 + +## 二、简历使用 + +编程这一行,要想真的掌握些东西,就是需要大量的做有质量的项目,提高自己的编程思维,锻炼自己的编码能力!学习完的项目,陆续完善到自己简历,让自己的竞争力越来越强👍🏻。像六边形战士一样! + +### 举例1 - 大营销 + +
+ +
+ +### 举例2 - Api网关 + +
+ +
+ +说实话,有如此`清晰的架构设计`、有如此`规模的工程拆分`、有如此`运用的设计模式`,这样的实战项目,又是本身一个大厂架构师,所编码、写文档、录课程,其实真的不多。但这样的项目在小傅哥的星球【码农会锁】**已有10多个啦** !你可以想象,你加入这样的技术社群,所能学习到多少硬核的技术东西。 diff --git a/docs/md/zsxq/material/student-learn-all.md b/docs/md/zsxq/material/student-learn-all.md new file mode 100644 index 000000000..c561dc28f --- /dev/null +++ b/docs/md/zsxq/material/student-learn-all.md @@ -0,0 +1,86 @@ +--- +title: 路线:实习、校招、社招,学习路线指引 +lock: no +--- + +# 路线:实习、校招、社招,学习路线指引 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +这篇文章的核心,主要就把一个事交代清楚:"小傅哥的星球 [**【码农会锁】**](https://t.zsxq.com/09hMHNMEh),有这么多项目和资源,我应该怎么学习"。—— `我是在校生想做个小项目找实习`,`我是应届生想做个大项目冲校招`,`我是社招生想深度积累技术栈`。今天就给你画一个清晰的路线。 + +
+ +
+ +**注意**:小傅哥的星球,已经形成了一个技术矩阵。不是一个单独的项目教程,而是成体系化的学习成长。 + +说实话写了这么多各类型的项目,确实花费了我好多的精力,在这个过程也耐得住了不少的诱惑。我没有去蹭各类热点和软文、也没有追逐营销和广告,而是花费了2~3年的时间,打磨这些课程项目。形成业务、技术、组件的多种类型项目组合,对比不同类型架构和思维的落地,综合锻炼各项技术栈的实践使用。只要你能完整的跟着学习下来,就能开阔自己的技术视野和积累不错的技术厚度。 + +
+ +
+ +那么,这些项目对于处在不同阶段的伙伴;实习、校招、社招,要从哪个开始学呢?接下来小傅哥就重点的介绍下项目的重点和学习的路线。 + +>文末有加入学习方式,小傅哥编写的所有项目和资料,只要加入就都可以学习。 + +## 一、项目视图 + +接下来小傅哥把星球已有的项目类课程平铺展开,对比项目的`难度`、`介绍`、`系统的架构`以及`运用到的技术栈`。这些内容的对比,对于需要学习项目的伙伴非常重要,可以针对性的选择有需要的课程内容补充自己的技术欠缺。 + +
+ +
+ +前四个,为业务类型的项目。后3个位技术类型的项目。 + +业务类项目,主要解决场景化的业务流程为主,运用架构、设计原则、设计模式,开发出便于迭代和易于维护的工程代码。现在越来越多的大厂开始重视工程质量,因为草莽时代的开发之追求快速上线,但存量市场的竞争,会更要求体验、稳定、可靠、成本。所以不能一个需求的开发就一堆的问题需要处理。 + +而一般技术类型的都以开发框架、组件、插件为主,用于解决公司项目组中同类共性功能的凝练,减少重复建设问题。一般会有人说不要重复造轮子,但往往面试时又会提到你是否能具备这样的深度组件设计开发落地能力。所以学习的过程是什么?我认为是多方面场景的锻炼,综合提升自身综合实力为主。让自己具备竞争力才是王道! + +## 二、学习路线 + +小傅哥深知,所处不通阶段的伙伴,对于学习的诉求也是不一样的。`着急找实习的`、`稳扎卷春招的`、`拓展肝深度的`,大家都有着自己当前阶段的目标。所以小傅哥基于大家的诉求来划分出不同的学习路线,让各位有个选择的参考。 + +- 路线A:简单一些,能较快周期完成,偏速成类 +- 路线B:复杂一些,要较长周期完成,竞争力强 + +
+ +
+ +整个学习的时间周期消耗,是小傅哥平局下来偏基础较弱的学习耗时。如果你基础积累较多,那么完成的更快。接下来,重点在讲下,实习、校招、社招,学习的路线说明。 + +### 1. 实习生 + +在实习生路线中,提供了A、B两条路线; + +- A路线,打一个IM、Netty、OpenAi综合技术使用。如果IM部分不手动编码UI部分,预计可2周完成。对接OpenAi预计需要2-3天左右。整体耗时,3~5周时间。 +- B路线,稳扎稳打路线,DDD架构、分布式技术栈、多场景设计实现的项目。同时配合一个SpringBoot Starter组件,非常亮眼【AOP切面使用,对实习生来说很好】。整体耗时,4~7周时间,适合早点加入星球就开始学习的伙伴。 + +### 2. 校招生 + +在校招生路线中,提供了A、B两条路线; + +与简单一些的实习面试相比,应届校招的竞争会更强一些。所以两条路线都有分布式技术栈的学习,也建议简历中有2个项目再加一个你的实习项目,来应对校招。如果你没有实习过,那么最好完成2个项目。 + +- A路线,综合分布式技术栈的双场景实战,一个C端的运营类场景,另外一个是结合OpenAi做;鉴权登录、下单、支付(微信)、充值、消耗。这2个项目会让你面试的时候,非常有场景可以讲。 +- B路线,难度较大,除了一个业务项目,还增加了一个纯技术类项目。API网关。这个项目的难度是5星,非常有挑战。你会吸收到很多高级的编码和高级架构的设计思路。比较适合之前有不错的技术积累的伙伴选择。 + +### 3. 社招生 + +到社招开始,远不是实习、校招时,有那么多大门敞开。虽然都说校招卷,但也有个卷的途径。 + +社招的卷是无形的,每错过一个年龄段的职业生涯成长,就需要很多时间弥补。尤其是我们所在行业的这趟列车,所在的年龄达不到对应的岗位,就有可能会被动下车。虽然领导可能会给你1次、2次、3次的晋升机会,但如果都错过后面的路就会比较难走了。 + +所以,破解之法是广度&深度的积累自己的综合实力,让自己技术所长,远超公司所需。所有的学习都不是为了公司所学,都是为了自己在各方面的积累。这样才有更多的跳槽、涨薪、议价机会。 + +那么,到这就不用纠结于学哪个,从哪个开始。陆续的都吸收给自己吧! + +🧧 [点此领优惠券加入星球](https://bugstack.cn/md/zsxq/other/join.html) diff --git a/docs/md/zsxq/material/student-learn-line.md b/docs/md/zsxq/material/student-learn-line.md new file mode 100644 index 000000000..a6eef8e78 --- /dev/null +++ b/docs/md/zsxq/material/student-learn-line.md @@ -0,0 +1,122 @@ +--- +title: 路线:180天,小卡拉米 - 编程路线,学习计划! +lock: no +--- + +# 路线:180天,小卡拉米 - 编程路线,学习计划! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +`职业生涯这条路,我在前面10年开的路,将让你少走很多弯路👣!` + +工作了这么多年,也遇到了很多领导对我的`OneByOne`:"你觉得是业务重要,还是技术重要?"。我每次的回答都是;业务、业务,绝对是业务,因为是业务驱动了技术的使用,没有业务,也就不可能有人给你发工资。 + +但如果你只关注业务,忽略技术成长。**那么虽然业务重要,但做这块业务的人,不一定是像小卡拉米一样技术能力的你。** + +尤其是工作越久越会发现,业务只是当下市场适合适宜的规则范围圈定,但规则时刻都会调整。所以我经历了很多不同场景业务的生生落落,业务没了,领导他们也都走了。**而我积累了业务和技术的经验。** + +所以,我认为;**技术是盆,业务是雨**。没有一个又大又结实的盆,那么即使下的是瓢泼大雨,也接不到几滴水。**那么问题来了,你的盆现在够大吗?** 如果你的盆还不够大,那么小傅哥接下来为你准备的**180天学习日历📅**,就非常能帮助你把盆做大了! + +## 一、把盆做大 🥣 + +`编程怎么学,才更行之有效?` + +学习编程这么多年,积累了很多经验,也尝试用了很多种方式学习。甚至还在最初学习的时候,编写了首小诗; + +红尘世界几个王,
+我自不服迎头上。
+日敲代码两百行,
+冲进世界五百强。 + +后来靠着这每天200行,毕业🎓时积累的20多万行代码量,顺利的找到了工作。也成为了那个时期的面霸。与现在不同,我没有背任何八股文,所有的技术积累都是依靠项目开发实战项目锻炼而来。而且我也发现,做项目是最快的学习方式。当你以完成项目为目标,运行结果为导向,遇山开山,解决各种技术问题。那么到最后,你的技术能力也将飞快的成长。 + +但,不是所有的项目和学习方式,都能让有如此的成长。比如;CRUD项目 - 适合练手,不适合成长。因为这种项目基本就是对数据库的操作,缺少场景的解决方案和设计模式的运用,也没有对流程的分治和抽象设计。所以同等时间下,做一些有业务场景和技术深度的复杂项目,是更适合成长的。同时,强烈不建议已经渡过了新手村的伙伴,仍然需要看视频CV代码,这样的学习方式很浪费时间。你可以看一下视频里的运行结果,但如果完成照抄,那么你的几乎成长是0。—— **不自己上上手,不完成一套清晰的学习路线,不遇到几个烧脑的报错,怎么提升编程能力!🤨** + +## 二、学习路线 - 180天 📅 + +`有些小卡拉米的盆呐,窟窿很大!` —— 当你的窟窿很大,哪哪都需要补充。那么可以看下这套专门为你准备的学习路线。**180天,让你成为一个技术强者!** + +小傅哥的星球[【码农会锁】](https://t.zsxq.com/09hMHNMEh),有3个业务项目、3个组件项目,还有开源项目和一堆技术小册,也包括几十种业务场景下的上百个解决方案。这些资料对于`大卡拉米来`说,简单的指导说明就可以开始学习了。但对于小卡拉米上手还是有些难度,总是不知道要从哪块开始,从哪开始都是欠缺。所以小傅哥专门为这样的小卡拉米,罗列了一套专属`小卡拉米`的**180天**学习路线,祝你在编程路上起飞🛫!!! + +### 1. 课程内容 + +**《启动180天,编程学习日历计划》** + +**内容介绍**:这是一套适合小白成长的完整体系的学习路线指引,涵盖;数据结构、设计模式、编程技术、业务项目、技术项目、组件项目、八股刷题、简历编写的全体系学习。此内容专门为小白准备,小白可以根据此内容的路线,加入星球累计180天完成学习打卡。 + +
+ +
+ +- 首先,这一整套全体系的学习课程比私教培训实惠,更比培训班上万的培训费便宜。可能也就是培训班1天的💰钱,就能学习到这一整套内容了。 +- 之后,你学习的整套课程,就是小傅哥这个架构师自己全部原创编写的。这也就是说,你所提到的任何问题,小傅哥都能给你解答和讨论。 +- 那么,这么实惠的课程,成体系的课程,还是架构师编写的。还有什么可犹豫的,完全可以撸起来了! +- 注明:如果你已经有一些基础的储备,那么你的学习时长可能是120天、90天、60天这样! + +### 2. 课程内容 + +**课程链接**:- 整个课程路线参考【课程日历图📅】基础知识中,深色为重点学习。对应的资源链接汇总如下; + +- 数据结构(14 天):[https://t.zsxq.com/10r8jP4Zc](https://t.zsxq.com/10r8jP4Zc) +- 设计模式(23 天):[https://t.zsxq.com/10oDwY8Kd](https://t.zsxq.com/10oDwY8Kd) +- 编程技术(23 天):[https://t.zsxq.com/10E3cp7uK](https://t.zsxq.com/10E3cp7uK) +- 业务项目(30 天):[https://t.zsxq.com/10zlIyK5Z](https://t.zsxq.com/10zlIyK5Z) +- 组件项目(37 天):[https://t.zsxq.com/10iYdgP5u](https://t.zsxq.com/10iYdgP5u) +- 轮子项目(23 天):[https://t.zsxq.com/107qkNTdA](https://t.zsxq.com/107qkNTdA) +- 面经手册(07 天):[https://t.zsxq.com/10fhkcpaq](https://t.zsxq.com/10fhkcpaq) +- 项目刷题(07 天):[https://bugstack.cn/md/zsxq/material/interview.html](https://bugstack.cn/md/zsxq/material/interview.html) +- 架构方案(07 天):【从星球课程入口进入,查看架构方案】 +- 简历优化(09 天):[https://t.zsxq.com/10pLHDzcD - 最后的阶段留个准备简历、投递、优化、备战](https://t.zsxq.com/10pLHDzcD) + +### 3. 课程收获 + +1. 熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对其使用源码所提供的接口、类、SPI标准开发各类组件,有一定的设计思路和落地能力。 +2. 熟练使用 RPC(Dubbo)、MQ(RocketMQ、Kafka)、Redis、分库分表、XXL-JOB、Zookeeper、等分布式技术栈,在各个场景的运用。 +3. 深度了解 MVC、DDD 架构知识,和框架搭建技巧以及微服务设计思想。 +4. 熟练使用设计模式、设计原则,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。 +5. 熟练运用 Docker、Portainer 等Dev-Ops手段,部署和运维 Nginx、Skywalking、Otter、Canal、ELK 等技术框架。 +6. 具备多类项目的开发能力,业务项目、技术项目、组件项目,以及对应的架构模型和设计思路。 +7. 积累八股问题、项目问题、场景问题的解答技巧,同时会有辅助简历编编写优化和评审,提高面试通过率。 + +## 三、课程举例 🌰 + +`180天学习内容,课程怎么样,有没有竞争力!` —— 啥是竞争力🤔,竞争力就是你做完一个实战项目,敢写也能写到简历上,它能为你的简历加分。而不是写一个CRUD项目,让人觉得是在凑数。以下就是学习路线中的一个技术项目《API网关》和源码学习《Mybatis》,写到简历的效果。 + +
+ +
+ +**以上就是你参与180天学习计划后的一个最终简历效果举例。** 当你决定参与一个学习计划或做一个项目,锻炼自己的技术的时候,先问问自己;`这个项目有什么架构`、`这个项目解决了什么场景问题`、`这个项目运用了什么设计模式`、`这个项目的编码质量如何`、`这个项目与企业实战中的差距有多大`、`这个项目的编写者是否有足够的实力`等。如果每一样都没有,稀里糊涂的就做,那就是花时间坑自己!🤔 + +以下是**180天学习日历📅**中的一个应用级实战技术项目的设计和代码举例,可以作为你的学习参考,看看这套路线有多硬核; + +| 项目架构 | +|:-----:| +|
| +| 工程代码 | +|
| +| 项目视频 | +|
| + +说实话,有如此`清晰的架构设计`、有如此`规模的工程拆分`、有如此`运用的设计模式`,这样的实战项目,又是本身一个大厂架构师,所编码、写文档、录课程,其实真的不多。但这样的项目在小傅哥的星球【码农会锁】**已有6个** !你可以想象,你加入这样的技术社群,所能学习到多少硬核的技术东西。 + +## 四、回本计划 - 拿捏傅哥!🤏 + +`累计180天`,也就是半年的时间都在学习,其实并不容易。为此小傅哥会为大家提供一个学习打卡,提高学习氛围。同时累计打卡180天的前100个用户,会送一本小傅哥出版的签名技术图书,留作纪念。这一本技术图书也就可以回本门票了!加油!**我在山峰⛰等你!** + +
+ +
+ +**学习说明**; + +1. 加入星球后,可以扫码阅读180天学习说明。或者复制链接在PC端打开:[https://t.zsxq.com/10iTcY7DK](https://t.zsxq.com/10iTcY7DK) +2. 打开链接后,可以看到学习日历中的各项学习资源连接、资料、视频。 +3. 学习课程时,遇到的所有的课程内的技术问题,都可以在星球提问,我都会帮你兜底解答。**我就是你的1v1课程导师。** +4. 180为累计打卡,只要陆续完成180天打卡学习即可。如果时间充足的,可以多往前冲冲! + + + diff --git a/docs/md/zsxq/material/student-learn-recruit.md b/docs/md/zsxq/material/student-learn-recruit.md new file mode 100644 index 000000000..7fb1ae7a7 --- /dev/null +++ b/docs/md/zsxq/material/student-learn-recruit.md @@ -0,0 +1,71 @@ +--- +title: 路线:35天、65天、85天,突击学习路线 +lock: no +--- + +# 路线:35天、65天、85天,突击学习路线 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家伙,我是技术UP主小傅哥。 + +这些年,为公司做招聘工作。在我手里筛过的简历,没有上万份也得有几千份了。一眼扫过去简历内容,基本就知道面试时的效果如何。所以,我所在的小组招聘效率还是蛮高的。🤔 那什么样的简历容易被面试官看中呢? + +
+ +
+ +**筛简历,我们更喜欢什么项目?** + +不同的公司往往对简历筛选时的侧重点是不同的,这个和工作时长没关系。有些工作6~7年的伙伴,简历全部B端项目,但这个时候投递C端类业务基本是没法匹配的。所以我们无论是校招还是社招,都要积累一些和自己想去的公司匹配的项目。 + +而互联网公司,更喜欢;C端的、通用的、分布式、创新的,因为这些东西也是互联网公司日常最常用场景,招聘这样的伙伴进来也不会特别耗力的培训。如;`电商`、`支付`、`信贷`、`营销`、`组件`、`源码`、`本地生活`,这些都是非常喜欢拿的简历。拿到简历就是细节的面试了,所以写的这些项目再有吸引力的同时,还要保证流程的完整性,不要防空炮。因为面试是会对简历进行评价打分,其他部门面试也会看见历史记录。 + +像这样的项目,市面有很多 `CRUD 案例类型的`,`比较适合入门`,`但面试还有些不够`。 + +所以小傅哥这里为你准备好了由小傅哥一行行代码从0到1带着学习的实战项目,把大厂的架构&研发经验搬过来,让你掌握。这样你的简历的内容就会更加丰富且细腻了,面试也有很多场景解决方案讲出来。 + +
+ +
+ +> 小傅哥,会为你提供一个个对标企业应用级的实战项目,积累丰富经验。学习后嘎嘎乱杀!文末可以获取全部学习项目「视频、文档、代码」 + +## 一、项目规模 + +小傅哥社区,组织开发了非常多的实战项目,目前包括5个业务类型、7个组件类型。并且这些项目是可以部署上线运行的项目「[https://gaga.plus](https://gaga.plus) 」 也是面试过程中,面试官喜欢问的点:”你的项目上线了吗“? + +
+ +
+ +> 这样一套内容可以说是成体系的帮助你完成Java学习,并且有了这些内容的指导你可以更有目标、更节省时间的学习。 + +## 二、组合学习 + +东西如此多,内容如此干。不知道从哪下手了。死鬼! + +所以,小傅哥这里专门给大家列出一套突击学习路线图,分为;5周、8周、12周,大家可以按需选择。并且在每一个周期内,又提供了3套方案共大家选择。 + +
+ +
+ +- 首先,你可以按照你的面试时间,进行路线选择。如果是一个月突击学习去面试,可以选择5周的路线,按照3个方案选择一套组合项目学习。如果时间比较充裕,准备的比较早,那么就建议按照12周学习来冲,这样竞争力是更强的。 +- 之后,在学习的过程中,推荐以项目驱动学习,完成项目为导向。在这个过程中遇到不会的内容,可以从基础教程中补充。这些各类技术技术内容,小傅哥都已经在星球编写好了文档和录制了视频,你可以从0到1轻松上车学习。 + +## 三、简历模板 + +有好经历,有不错的项目,还需要一套上档次的描述方式,这样才能更加完美的展示出自己的实力!这里小傅哥给大家提供了一个描述经历的结构,这样写的简历会更好的突出自己的能力,提高筛选通过率。 + +
+ +
+ +- 首先,个人信息,可以突出下过往的经验积累,包括;在过往的XXX经历中,积累了XXX场景的经验,使个人具备了XXX方面不错的能力。同时兼具着对(技术/场景)的(喜好/热忱),长期(学习/从事/钻研)某项技术,并做出了对应的(开源)(产品/服务/组件/插件)。此个人XXX发布到XXX市场以后,得到了用户(下载量/点赞量/使用数)的规模,获得了非常好的认可。地址:http://github.com/xxxx/xxxx +- 之后,描述个人`教育背景`、`实习/实践经历`、`专业技能`和项目的编写。专业技能,要注意描述顺序和内容量,不宜过多,但要把招聘中核心技术栈体现出来。紧接着到项目描述,这部分最好有1~2个业务项目 + 1个组件类型项目,这样的组合简历内容,还是非常好用的。 +- 最后,补充一个自我评价。把自己描述成有技术追求的、有团队精神、有奋斗品质的优秀好青年。 + diff --git a/docs/md/zsxq/material/study-experience.md b/docs/md/zsxq/material/study-experience.md index f5b01c1eb..c4c4d9c28 100644 --- a/docs/md/zsxq/material/study-experience.md +++ b/docs/md/zsxq/material/study-experience.md @@ -14,8 +14,91 @@ lock: no 汇总星球中关于个人、学习、成长、述职、晋升、跳槽相关的经验分享,帮助在这条路上的伙伴快速前进。 -## 二、个人介绍 +## 一、傅哥经验 +- [大厂T8架构师の职场分享 · 聊聊天](https://t.zsxq.com/05UJAqZVv) —— 小傅哥是13年大学毕业,从最初的工作2年写C#到15年跳槽互联网写Java,接项目、做监控、弄组件、发专利、出版书,一路走来的到架构师的职场经历分享。 +- [傅哥,能否分享一下高级Java工程师、架构师需要掌握的技能树或者学习进阶路径?](https://t.zsxq.com/05QzNV7Iu) +- [给在校学生👩🏻‍🎓和工作5年以内的程序员,总结的完整分阶段学习的技术路线,知识全面、分段学习、突破瓶颈](https://t.zsxq.com/05rrf6imy) +- [傅哥,你觉得2年应该要会那些东西?定位是中级程序员了吗?我感觉我经常写出bug,很多时候都需要搜索引擎帮助,还和个初级程序员一样](https://t.zsxq.com/05y7mAMju) +- [我工作已经5年了,但是感觉对技术的学习一直很模糊,也看了各种视频,但是看完就忘了](https://t.zsxq.com/053JeeemE) +- [互联网中测试,开发,算法,运维岗位哪个比较好呢?](https://t.zsxq.com/05YFiYFiI) +- [刚毕业进入一家互联网公司,如何挖掘出项目的亮点和技术点呢?最后是在工作之外如何提升自己,如何将星球的内容和工作相结合呢?](https://t.zsxq.com/10CZ7iIMs) + +## 二、架构之路 + +- [架构师成长之路有吗?](https://t.zsxq.com/05VJiQf66) +- [如果是架构师的话,那么平常架构师的工作主要做什么](https://t.zsxq.com/05mY7QrJE) +- [傅哥,将来意向是架构师方向,该怎么入门?有没有推荐?没人带,难搞](https://t.zsxq.com/056AUzFqr) +- [架构师最重要的能力是什么?目前处在高级阶段想往架构师转型,网上学了学架构师的课程,发现都是扩大技术面并深入了解的,但是还是觉得没有触及架构师的全貌](https://t.zsxq.com/05jAaQnAu) + +## 三、述职答辩 + +- [现在对于职业规划有些迷茫,希望能够得到傅哥的指点。](https://t.zsxq.com/05qnM76aI) +- [关于升职加薪:公司半年考核有时领导会发起面谈](https://t.zsxq.com/05euBiUrN) +- [大佬们,晋升PPT如何写。第一次写,头疼!有木有一些建议](https://t.zsxq.com/05EiY7QVn) +- [请教傅哥转正答辩和晋升答辩都问些什么?该如何准备呢?](https://t.zsxq.com/05iiEyzbE) +- [小傅哥你好,今天被TL通知,准备年底的晋级答辩,我们晋级答辩的要求是入职满一年(我9月满一年)](https://t.zsxq.com/05Bm6EYVj) +- [小傅哥想请教一下关于答辩的问题,最近刚通知职级晋升答辩,第一次答辩没有啥经验,也不知道写啥](https://t.zsxq.com/05rjqZNB6) +- [上就要校招试用期转正答辩了,想请问下您在转正的这个阶段,leader较为看重的点在哪?](https://t.zsxq.com/06QzZJ6Yz) +- [述职晋升P/T7及以上,所需要的面对的问题挑战【工作5年以上需要认真了解,如果想多赚些钱,就不能只是会搬砖】](https://t.zsxq.com/06rzrJA2V) + +## 四、个人介绍 + +- [大家好。今天是我第一天加入这个星球,我爱玩 Strbound,游戏寓意是在不同的星球之间探索。如同小博哥的封面一样:“探索未知”,这让我感到十分的兴奋。@Pucl](https://t.zsxq.com/105s3h5nk) +- [大家好,我是23年5月份在美国50名左右大学毕业的cs硕士。我本科是湖南大学的应用数学,然后出国读了个统计的硕士 @一飞](https://t.zsxq.com/0elKeVEoz) +- [我在互金从业也有7年了,目前负责风控相关的工作。今天第一天加入码农会锁 @易安](https://t.zsxq.com/0dE25FK0g) +- [大家好,小傅哥好。我目前是一名双非本的大三在校生,只是跟着视频案例敲,很不熟练,没有深入,不能熟练使用,更没有融会贯通 @就这样](https://t.zsxq.com/0chQ0UW8s) +- [22年9月毕业,在英国读了一年硕,即将入职一家国企。@D午](https://t.zsxq.com/0cTpHdC7B) +- [大家好,小傅哥好,我是一个16年毕业的大专生,通过自学和培训半路转行的Java开发工程师,Java工作经验3年半 @觉不够睡](https://t.zsxq.com/0ctX9TYwQ) +- [今年22,工作经验一年,从培训机构出来的,因为之前沉迷于游戏,高中辍学了!从开始接触编程,只知道钱多到现在的热爱编程。@hao](https://t.zsxq.com/0cORcLIMU) +- [大家好,我目前是研一在读,本硕科班。看到小傅哥星球的抽奖系统、api网关这些项目让我眼前一亮,希望学习后可以对一些组件使用、框架底层原理的理解都有更进一步的提升。@L.ast](https://t.zsxq.com/0cnatJLK9) +- [大家好,我是一名普通的二本大三学生,和大多数大三学生一样,正在焦虑的找实习中 @撑死的](https://t.zsxq.com/0bP0RnxuW) +- [大家好,我是非计算机专业的大专生,今年28岁,2015年毕业,2018年通过机构培训入行Java,入职的两家公司都非互联网 @村花( -`ω-)](https://t.zsxq.com/0bQyvm8Fi) +- [大家好,本人软件工程专业,大二在读,目标学习java后端,目前学过的技术栈只有web、ssm、mysql,springboot和vue仅会简单使用,加入傅哥的星球为了拓宽一下眼界和知识面 @葡萄](https://t.zsxq.com/0bZlqV0LH) +- [我是22年毕业,方向是Java后端,21年秋招签了一家公司,不太满意,现已离职 @四叶草](https://t.zsxq.com/0bWZA39oI) +- [本人985软件工程研一在读,毕业后目标是杭州互联网大厂Java后端岗位。目前学习完的知识有:Java基础、JavaWeb、SSM、SpringBoot、MyBatis-Plus、MySQL。LeetCode刷题200+。@一米阳光](https://t.zsxq.com/0b2NpN3TP) +- [大家好,我双非研二在读,电子信息专业,加入星球是因为在b站看到小傅哥的项目视频,顿时觉得是真大佬。@一只鱼鱼鱼🐟](https://t.zsxq.com/0bD4haCfA) +- [大家好,我大四在读,电子信息工程专业,大三开始转的Java,练习时长不到一年,非科班。@渐渐](https://t.zsxq.com/0biRoztFd) +- [大家好,我研二在读,专业控制科学与工程,目前研究方向偏向深度学习算法,纯纯非科班 @Leonora.](https://t.zsxq.com/0bPLfUDf5) +- [大家好,我是今年应届毕业的二本生,因为一战失败停滞了一年技术,之前掌握到了ssm,但也不是很扎实 @赖伯](https://t.zsxq.com/0bPZqV8Ae) +- [大家好,我是98年出生,16年上的大专,19年上的双非本科(21年毕业),计算机科学与技术专业~因为一些原因 @荒小北](https://t.zsxq.com/0bbNl1zkG) +- [大家好,目前双非计算机技术 研二 在读,本科非计算机专业 ,2024年毕业 @heart and *](https://t.zsxq.com/0bWVu5Nsn) +- [大家好,我是一名转行小白,本硕学的材料,跟计算机一点边不沾,5个月前从本行业辞职,进了一家互联网公司做Java @蛮大人](https://t.zsxq.com/0bzRtutRs) +- [2022年毕业,普通双非二本计算机专业,希望跟着小傅哥开启源码学习之路。@沙漠之舟](https://t.zsxq.com/0bWijW2kX) +- [大家好,我是19年毕业,双非一本,计算机专业,毕业之后一直在北京 @一切像泡影](https://t.zsxq.com/0bMNe4tBJ) +- [本人2023届毕业生,某二本信息与计算科学专业(这个系偏数学系,但是有很多计算机,代码的东西可以捣鼓) @一周也就摆烂七天](https://t.zsxq.com/0aRM850J9) +- [大家好,我是19年专科毕业的,在广州工作,21年在一家做医疗项目的公司,22年进了现在这家做外包项目的公司 @改变世界的小宇](https://t.zsxq.com/0ai4Yyxe8) +- [大家好,我是2020年二本毕业,电气工程及其自动化专业,非科班大四自学java,毕业后在北京一家第三方支付公司做java开发 @追夢人](https://t.zsxq.com/0aLczdtkQ) +- [大家好,我是普通本科2023届的软件工程专业应届生 @包面白](https://t.zsxq.com/0a88w3JN2) +- [大家好,我校招在阿里工作了两年,22年五月份跳槽到了外企,平时时间比较多,期望通过抽奖这个项目能够让自己技术能力 @hardworkin*](https://t.zsxq.com/0a3AYYfsy) +- [毕业四五年了,但是从事程序员是从去年九月份,去年大部分时间在培训机构培训转行做java开发 @Jerry](https://t.zsxq.com/0aTOgBGmg) +- [各位大佬好,我是dk,常用尔等同学行走江湖,上次找日常实习看了小傅哥的博客深有所获,现在选择加入星球向大佬们一起学习](https://t.zsxq.com/09DJwJ6vz) +- [大家好,我叫余温,自学Java进入开发行业目前撸码4年+;参与过外卖自动派单系统开发,负责解决业务与报表解耦、落地分库分表等业务;](https://t.zsxq.com/098gd6g9d) +- [大家好,作为一名小公司的crud熟练工,虽然平时也经常看一些技术文章,但由于平时工作没有机会实际应用](https://t.zsxq.com/09nbCXOi1) +- [大家好,我目前大四在读,刚入职一家小公司实习,目前学习了部分spring cloud知识和一些中间件](https://t.zsxq.com/09goM98YS) +- [大家好,2018年某211非科班研究生。之前带过一个 0-1 的项目,技术栈 vue + nginx + spring cloud + redis + oracle + mysql](https://t.zsxq.com/08wlnHY1Z) +- [大家好,我叫九月,是某985科班专业,毕业4年,一直从事体制内工作。](https://t.zsxq.com/081k67eQX) +- [大家好,我是巫十二,工作四年多,非科班出身的javaboy。目前做过金融产品、灵活用工,社交电商等项目](https://t.zsxq.com/08HZR6IK8) +- [大家好,我叫明天过后。20年毕业,在跨境电商公司做Java开发,最近刚领了大礼包](https://t.zsxq.com/08KIygBpA) +- [大家好,我是heathen,来自西安,2018年毕业于一所普通二本,目前主要从事java开发](https://t.zsxq.com/09JwH6LBz) +- [大家好 我叫奥特慢 22年6月毕业 7月入职3C家电事业群其中一部门](https://t.zsxq.com/082xYpZGd) +- [大家好,我是本科广州航海 计算机科学与技术专业的,明年就实习了](https://t.zsxq.com/08SjB2OMr) +- [大家好,目前东北某985研二 非科班 java后端目前在学习](https://t.zsxq.com/07MAybjcz) +- [大家好,我是新来的云尘,练习时常两年半的Java开发,公司业务ToC端](https://t.zsxq.com/075LhCsvu) +- [大家好, 我是一名两年多java开发, 公司业务是ToC 端.](https://t.zsxq.com/0740o20PD) +- [大家好,我是一名非科班的大专Java开发,在工厂实习几个月后,突然开窍跑去培训班学习到转行,工作三年,就职与一家教育类软件公司做后端开发。](https://t.zsxq.com/07nUaSb9e) +- [大家好,计算机专硕研一在读学生一枚,在公众号看到了小傅哥的抽奖系统想学习一下,同时想好好提升一下自己的技术水平](https://t.zsxq.com/07uzMbhQP) +- [大家好,我目前是两年开发经验,现在在一家芯片类公司做ERP相关的项目,主要就是一些业务逻辑性开发](https://t.zsxq.com/071a2C7fp) +- [大牛们好,我20年本科毕业,专业是电子信息工程,毕业后在Java培训班学了半年](https://t.zsxq.com/07P6iMAzi) +- [本人20年毕业,现就业于一个做自来水业务的公司,因为常常会跟协议打交道;想学习深入学习一下netty,看见小傅哥关于netty讲的非常好,于是加入这里。](https://t.zsxq.com/07QrIXMix) +- [各位大牛们你们好,我在朋友的引导下有幸能加入到小傅哥的我们这个大家族中,首次加入。希望大家多多关照哈。](https://t.zsxq.com/07LPtW2Qb) +- [大家好,本人是21届毕业,大学前三年基本在摆烂中度过,自学java,今年五月份从公司离职](https://t.zsxq.com/06F66EyBA) +- [大家好,作为一个22届的软件工程毕业生,从去年10月开始自学java,春招找了份实习。偶然间点进了小傅哥网站,仿佛发现了新大陆](https://t.zsxq.com/06fufm6mq) +- [大家好,作为一个从事java开发三个月的新人,对于Spring全家桶、mybatis也局限于使用,不懂原理](https://t.zsxq.com/06EeMbUrR) +- [小傅哥好,大家好,我是24年毕业的软工本科生。](https://t.zsxq.com/06iIu7ujU) +- [祝各位国庆节快乐哈!本身17年普通二本大学科班毕业,主要从事Java方面的开发](https://t.zsxq.com/06AiQnYfU) +- [我是2015年7月份毕业的,计算机毕业,刚开始第一年在深圳的银行做外包项目](https://t.zsxq.com/06FIEmEey) +- [大家好,我是2015年本科毕业,网络工程专业,选了编程的方向](https://t.zsxq.com/066a6mQJq) - [我是某双非一本在读大三,软件工程专业。目前在准备找实习](https://t.zsxq.com/06Vv3VfAu) - [大家好,小傅哥好,我是19年毕业的专科生,专业是机械设计,后来机缘巧合之下报名培训班转了java](https://t.zsxq.com/06NNvBQFQ) - [小傅哥,可以帮我分析下,我目前最需要做什么吗,我有点理不清](https://t.zsxq.com/063R7ay7y) @@ -143,28 +226,10 @@ lock: no - [20届本科非科班专业(在学校也有Java编程相关的课程)毕业生](https://t.zsxq.com/05mEyvnAm) - [18年工作的Java渣渣 到现在一直在同个外包公司](https://t.zsxq.com/05yNnQzfA) -## 二、傅哥经验 - -- [大厂T8架构师の职场分享 · 聊聊天](https://t.zsxq.com/05UJAqZVv) —— 小傅哥是13年大学毕业,从最初的工作2年写C#到15年跳槽互联网写Java,接项目、做监控、弄组件、发专利、出版书,一路走来的到架构师的职场经历分享。 -- [傅哥,能否分享一下高级Java工程师、架构师需要掌握的技能树或者学习进阶路径?](https://t.zsxq.com/05QzNV7Iu) -- [给在校学生👩🏻‍🎓和工作5年以内的程序员,总结的完整分阶段学习的技术路线,知识全面、分段学习、突破瓶颈](https://t.zsxq.com/05rrf6imy) -- [傅哥,你觉得2年应该要会那些东西?定位是中级程序员了吗?我感觉我经常写出bug,很多时候都需要搜索引擎帮助,还和个初级程序员一样](https://t.zsxq.com/05y7mAMju) -- [我工作已经5年了,但是感觉对技术的学习一直很模糊,也看了各种视频,但是看完就忘了](https://t.zsxq.com/053JeeemE) -- [互联网中测试,开发,算法,运维岗位哪个比较好呢?](https://t.zsxq.com/05YFiYFiI) - -## 三、架构之路 - -- [架构师成长之路有吗?](https://t.zsxq.com/05VJiQf66) -- [如果是架构师的话,那么平常架构师的工作主要做什么](https://t.zsxq.com/05mY7QrJE) -- [傅哥,将来意向是架构师方向,该怎么入门?有没有推荐?没人带,难搞](https://t.zsxq.com/056AUzFqr) -- [架构师最重要的能力是什么?目前处在高级阶段想往架构师转型,网上学了学架构师的课程,发现都是扩大技术面并深入了解的,但是还是觉得没有触及架构师的全貌](https://t.zsxq.com/05jAaQnAu) - -## 四、述职答辩 +## 五、其他建议 -- [现在对于职业规划有些迷茫,希望能够得到傅哥的指点。](https://t.zsxq.com/05qnM76aI) -- [关于升职加薪:公司半年考核有时领导会发起面谈](https://t.zsxq.com/05euBiUrN) -- [大佬们,晋升PPT如何写。第一次写,头疼!有木有一些建议](https://t.zsxq.com/05EiY7QVn) -- [请教傅哥转正答辩和晋升答辩都问些什么?该如何准备呢?](https://t.zsxq.com/05iiEyzbE) -- [小傅哥你好,今天被TL通知,准备年底的晋级答辩,我们晋级答辩的要求是入职满一年(我9月满一年)](https://t.zsxq.com/05Bm6EYVj) -- [小傅哥想请教一下关于答辩的问题,最近刚通知职级晋升答辩,第一次答辩没有啥经验,也不知道写啥](https://t.zsxq.com/05rjqZNB6) -- [上就要校招试用期转正答辩了,想请问下您在转正的这个阶段,leader较为看重的点在哪?](https://t.zsxq.com/06QzZJ6Yz) +- [想请教小傅哥一个问题,晚上十点半睡觉,早上早早的起来跑步。然后比如说节假日还在疯狂输出,是热爱编码,编码就像玩游戏么?还是有什么自律的方法](https://t.zsxq.com/06aUb2nYJ) +- [小傅哥你好,作为工作5年,目标是java高级开发岗位。从面试官角度来看,会看重候选人哪些素养呢?](https://t.zsxq.com/07Amrqp2p) +- [想问下抽奖系统这个项目,适合我去学习吗?吃透后能适应未来工作流程和内容吗?](https://t.zsxq.com/07tpabzyV) +- [今日入职新公司第一天:我的经验和感觉分享给大家!](https://t.zsxq.com/07udHwQBk) +- [小傅哥,本人双9 Java后端选手,秋招最后几家决赛圈offer求给点建议](https://t.zsxq.com/08lQizSqr) \ No newline at end of file diff --git a/docs/md/zsxq/memorabilia/110000-lines-of-code.md b/docs/md/zsxq/memorabilia/110000-lines-of-code.md new file mode 100644 index 000000000..f19e096dc --- /dev/null +++ b/docs/md/zsxq/memorabilia/110000-lines-of-code.md @@ -0,0 +1,129 @@ +--- +title: 从码农小白到P6+,我为你编写了11万行项目代码! +lock: no +--- + +# 从码农小白到P6+,我为你编写了11万行项目代码! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主,小傅哥。 + +截止到今天3.18日,我已为加入星球「码农会锁」的伙伴,提供了**总计11万行**代码数量的实战项目,涵盖了;`业务项目`、`组件项目`、`插件项目`、`源码学习`、`基础教程`,这5类内容。更重要的是这11万行代码,没有CRUD,只统计 Java 核心编码数量。🤨👍🏻 + +
+ +
+ +可以说,这11万行的代码,耗尽了我所有的周末和假期! + +众所周知小傅哥在技术圈子里是一股清流,写技术这么多年,依然想个生产队等卷王一样,源源不断的给粉丝👬🏻兄弟们创作出高质量的技术资料。业务项目5个、组件项目3个、源码学习2套、基础教程3套。再加上 [bugstack.cn](https://bugstack.cn) 开源的技术文章,除了11万行的源码,技术博客的文字创作也有300多万字。💥 + +**不用藏着掖着了**,是时候给兄弟们看一下了! + +
+ +
+ +这11万行的代码,也就等于;5个业务项目、3个组件项目、2套源码学习、1套基础教程(分布式技术栈),当你有这样的一个积累后,你的个人技术积累将有非常大的提升。程序员的0-1年找工作,2-3年跳槽/晋升、3-5年晋升高级,都需要有夯实的基本功,才能在职场中得到更高的职级和更多的薪水。 + +那这些项目的学习路线是什么样的呢?小傅哥接下来就给大家介绍下,让大家的学习可以事半功倍。 + +> 以上这些内容的门票价🎫,只需要100多元!文末有🧧优惠加入方式。 + +## 一、项目视图 + +这11万行的 Java 代码项目,都能学到啥呢,包括;场景、架构、技术?我们先来个全局视角俯瞰下,做到心中有数。 + +### 1. 大营销平台系统 + +前后端+Dev-Ops,全栈技术实践。项目将先以最新DDD架构和设计模式进行重构Lottery项目,扩展用户、账户、积分、兑换、分享模块,以及和 OpenAI 进行微服务整合,dubbo 通信完成项目综合使用。 + +
+ +
+ +### 2. OpenAI 微服务应用体系构建 + +前后端+Dev-Ops,全栈技术实践。以OpenAi场景,运用DDD架构,**实现从微信公众号扫码登录,到下单支付对个人账户充值,完成OpenAi对话消费的全流程**。 + +
+ +
+ +### 3. Lottery 分布式抽奖系统 + +抽奖系统是互联网C端运营场景核心微服务之一。该系统从用户进入抽奖开始,进行**差异化的人群决策,筛选可参与的活动ID,并领取活动处罚抽奖以及异步发货**。其中重点设计了,滑块锁🔐降低独占性,**增强秒杀并发度**。 + +
+ +
+ +### 4. IM(Netty + JavaFx):仿桌面版微信聊天 + +这是一个通过JavaFx技术开发桌面版UI,并在Java通信客户端集成使用的前后端分离设计。再通过服务端进行**Netty多协议通信交互**,完成通信处理。 + +
+ +
+ +### 5. ChatGPT Ai 问答助手 - 小型,自动回帖机器人 + +通过搭建简单DDD工程,自动采集星球问题和帖子,再通过数据分析整理,**调用ChatGLM等开放OpenAi接口**自动回复。 + +
+ +
+ +### 6. API网关:中间件设计和实践 + +以承载Nginx负载为入口,接收并处理HTTP协议进入会话流程模型,**通过“源码级”设计实现;协议的转换、关系的绑定、动作的执行、泛化的调用再到结果的返回等一系列动作**。并支持扩展各类其他服务,如MQ、Redis、JOB、MySQL等资源为HTTP服务。这套系统设计非常强劲。 + +
+ +
+ +### 7. SpringBoot Starter 中间件设计和开发 + +全小册包括16个中间件对应30个代码库提供给读者学习使用。场景涵盖:**技术框架、数据服务、数据组件、分布式技术、服务治理、字节码、IDEA插件**七个方面,贯穿整个互联网系统架构中常用的核心内容。非常值得了解、学习、实践到掌握。 + +
+ +
+ +### 8. IDEA Plugin 插件开发 + +全手册,分为4章12节循序渐进的通过实践案例开发的方式,**串联 IDEA Plugin 开发的各项常用技术点**,为读者讲解如何开发一个 IDEA 插件。 + +
+ +
+ +初次之外还有手写Spring、手写MyBatis,和一套基础教程。基础教程可以让小白伙伴扎扎实实的学习 SpringBoot 应用的分布式技术栈使用。这套基础教程完全是小傅哥从0到1编写,紧密配合星球实战项目。把各项所需的东西都拆解为一个个独立的小案例,让大家学习起来更加容易。 + +## 二、学习路线 + +星球的项目比较多,我来帮你明确一个学习路线,可以作为参考。这个路线分为3个阶段,分别对应到实习、校招、社招不同的能力要求。一般卷王的视角是,实习卷校招能力,校招卷社招能力,社招卷全部能力! + +
+ +
+ +- 就实习来说,基本要求是了解一部分分布式技术栈,完整运用常用技术栈做过项目开发。如果能交付上线会更好。 +- 从实习完成,在到校招难度会加大一些。简历最好有2个项目。可以是2个业务项目,或者2个业务+1个技术的组合。技术可以是组件或者手写源码类。 +- 最后是社招,社招可以摘取1、2阶段的实战项目,综合在学习下源码应用的项目,这样简历会更有的拼。尤其是那种本身自己业务实在没啥讲的,那么就可以做个API网关,这类项目没有啥业务属性,学习后也更容易写到简历。【不少校招也写了 API网关,冲到阿阿里、美团、字节,还是很好用的】 + +
+ +
+ +> 小傅哥星球的这些项目,是站在一个架构师的视角,培养新人的综合技术能力而设计的。所以建议,可以有计划的完成学习积累。每一个项目都有自己的架构特色和设计的技巧,学到手对自己的帮助都非常大。让你不只是一个CRUD仔,面试/述职,都更有东西可以讲出来。 + +## 三、加入学习 + +小傅哥是一个大厂的架构师,经常会带着伙伴们,卷这些实际场景中非常有必要的技术。也会带着伙伴实战项目,这些项目也都是来自于互联网大厂中真实的业务场景,所有学习这样的项目无论是实习、校招、社招,都是有非常强的竞争力。别人还在玩玩具,而你已经涨能力! + +>这样的项目学习在小傅哥星球「码农会锁」有8个,每个都是从0到1开发并提供简历模板和面试题,并且还在继续开发,后续还将有更多!价格嘎嘎实惠,早点加入,早点提升自己。项目地址:[https://gaga.plus](https://gaga.plus) \ No newline at end of file diff --git a/docs/md/zsxq/memorabilia/biographical-notes.md b/docs/md/zsxq/memorabilia/biographical-notes.md new file mode 100644 index 000000000..18e85cfe6 --- /dev/null +++ b/docs/md/zsxq/memorabilia/biographical-notes.md @@ -0,0 +1,73 @@ +--- +title: 2023年7月25日,校招简历应该怎么写 +lock: need +--- + +# 2023年7月25日,校招简历应该怎么写 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +**怕兄弟👬🏻苦,又怕兄弟开路虎🚗!这马上都要秋招了,自己的简历还写不好,但我兄弟那命运的齿轮 ⚙ 都快转飞起来?🤨 傅哥,我咋办!!** 🤨 + +简历写的好不好,往小了说就只是啥时候拿到Offer。但往远了看,你现在所有的选择,在将来都会被放大。放大后来看就是职业生涯的路线,虽然也不至于说一步错,步步错,但往往一个点没踩对,后面必然会花费更多的时间和精力来修正路线。 + +那么为了不至于在以后不断耗费经历的的修正路线,也为了让命运的齿轮飞起来,就要把简历写好,抓住这次招聘机会。但说到写简历,其实对很多在校面临秋招的小卡拉米来说,是不太会的写的。基本问题体现在;`不知道怎么展示出自己的能力`、`不晓得专业技能都要覆盖啥`、`不明白简历中项目的重点怎么讲述`,`还有更多的是简历的模板难看的像粑粑戒子!` + +所以,小傅哥这里准备好了;实战项目、简历模板、1v1优化提建议等方式,助力于小伙伴们编写简历应对招聘。那么你准备好上车了吗? + +## 一、简历模板 + +不同的应聘诉求,会有不同的简历编写侧重点。比如校招,那么要看你在校的经历、实习的积累、项目的实践,通过这些内容来考察你的能力是否可以匹配到公司的岗位。而社招生,则需要更强的技术思维体现,相关业务场景经验的厚度积累,做到既言之有物,又有技术深度。 + +但无论校招、社招,面试官在面试时,越来越倾向于对应聘者从实际做的内容开始提问,考察各个知识科目。当然如果你的简历实在没啥可聊的,也会给你一顿八股文输出,最后大概率会被送走。 + +
+ +
+ +现在你知道大厂的面试官是怎么面试人了吧,那么小傅哥结合这样的**起承转和**式提问,编写出对应的简历模板给大家参考使用。简历上所体现的项目,都是来自于小傅哥的星球【码农会锁】中的实战项目。进入星球的伙伴也可以让小傅哥帮忙对自己的简历进行评审,我会帮你找出简历中的问题、优化方式以及会问到什么问题。好啦🌶,我们来看看简历! + +**注意**:`高质量的简历模板` + `有深度的技术项目` + `编写后的评审指导` = 一条龙服务🐲,**加入星球马上就能获取到**。为你的应聘加把劲,这一丢丢投资可能就换来一个 Offer 和不错薪资! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +### 1. 校招生 + +
+ +
+ +
+ +
+ +
+ +
+ +### 2. 社招生 + +
+ +
+ +
+ +
+ +
+ +
+ +## 二、体系内容 + +选择加入一个社区最重要的参考指标什么?🤔 是成体系化、有技术厚度,所学能所用,所用有结果。当你遇到一个技术问题的时候,可以在社群获取别人多年经验下的指点,让你少走弯路。才是有价值的。 + +而小傅哥的星球 **【码农会锁】** 就是这样的技术社群,有在校生、有实习生、有工作几年的大牛,也有不少海外的伙伴 - 他们经常半夜出来卷码!但他们的笔记作业真的优秀,可以看看 `C9`、`985`、`211` 的伙伴,是怎么样一个学习方式。人嘛,总得看到一些已经在未来的人,才能让自己的目标更加清晰。而之所以小傅哥的星球不仅有小白,还有不少大牛。这是因为小傅哥的星球体现的不仅是小白所需的知识,还有我自身工作10年做积累下的技术厚度,为你提供一个非常值得加入的的,有技术内容、有技术氛围的技术社群。 + +
+ +
diff --git a/docs/md/zsxq/memorabilia/campus-recruitment-offer.md b/docs/md/zsxq/memorabilia/campus-recruitment-offer.md new file mode 100644 index 000000000..321aa476a --- /dev/null +++ b/docs/md/zsxq/memorabilia/campus-recruitment-offer.md @@ -0,0 +1,132 @@ +--- +title: 秋招总结,6家大厂,5个SSP级别Offer!最终选择了最赚钱的方向。 +lock: need +--- + +# 2023年7月25日,校招简历应该怎么写 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在互联网大厂,我写过11年的代码,带过团队,培养过新人,经历了非常多的业务场景设计和实现。总结一句,我是一位经验丰富的软件开发&架构师! + +
+ +
+ +**我清楚的知道,技术人怎么从小兵走到“头部”** + +15年入职大厂,23年走到T8架构师,连续晋升。无论是;承接项目、晋升述职,还是技术分享和培养新人,我都可以完成的非常漂亮。并在晋升T8时,以全票通过,获得领导的认可。 + +所以,在个人技术成长和职业成长方面,我是有非常多的经验的。因为有这样的经历和这些经验,小傅哥于2021年,建设了一个自己运营的技术社群**「星球:码农会锁」**,直至今日已有1.6万人加入,帮助了超级多的伙伴完成了技术的成长和职业的进阶。 + +`有伙伴辣评:“裸辞半年,我是被小傅哥,抬着进入的这家公司!”` + +
+ +
+ +为了,让伙伴们更好的知道小傅哥有多值得,今天给大家分享一个在小傅哥社群学习成长的伙伴,拿到 Offer 的自我总结。这样的经验,可以非常好的帮助大家了解到如何学习、怎么有价值的学习! + +>接下来,是这位星球伙伴的自述。 + + +秋招顺利拿到offer,并且这几天毕业论文盲审顺利通过了,总算尘埃落定。感谢小傅哥的知识星球陪伴我走完了硕士生涯,写个帖子记录下我的学习历程以及我个人的秋招面试总结,希望对星球里的大家有些帮助~ + +## 1. 我的背景 + +先简单介绍下本人背景,211本985软工硕,一段美团日常实习一段淘天暑期实习,秋招拿到了字节ssp、美团sp😭、快手ssp、淘天ssp、蚂蚁ssp、京东ssp,都是交易/支付方向的offer,最终通过综合比较选择去了淘天。 + +## 2. 学习的碎碎叨叨 + +以下是碎碎叨叨的开始,长文⚠️,不想看的小伙伴可以直接下滑到「秋招总结」,有我的一些面试经验总结~ + +我加入小傅哥的知识星球已经快近2年了,还记得当时刚开始学Java是在2023年的1月,那个时候我几乎天天去市图书馆,从最基础的Java基础开始学起,从早上学习到傍晚,学习时间其实不是很长,23年的寒假算是学了一些Java基础和SSM(虽然本科也学过一点皮毛,不过后来都忘记了)。 + +寒假结束回到学校之后,我开始从早到晚的疯狂学习,一周稳定学习6天,大概每天除去吃饭、休息、睡觉的时间可以学习八个小时以上,每天大概有2-3小时来干科研,其余时间都学Java相关的。 + +这段时间有种每天都在过期末周的感觉……在23年的3月份,我看到了小傅哥的知识星球,加入其中,并坚持每天打卡了几个月,这几个月,我主要集中学习的还是Java的八股,看了很多优质八股的网站,例如 `JavaGuide`、`Java进阶之路`、`小林coding`等,结合牛客的面经,综合学习了操作系统、计算机网络、`JUC`、`JVM`、`Redis`、`MySQL`、`Kafka`、`RocketMQ`、`zookeeper`、`分布式`、`Dubbo`等。 + +这些八股知识我一直学习、补充完善、复习到秋招为止,也是这样反复的整理、理解、压缩、记忆,在面美团和秋招面试的时候,面试官问的八股问题我基本都能在2秒内快速回忆起,且问的问题90%都是我已经知道的。 + +此外,在算法方面,我当时系统刷完了左程云的算法视频课程,这个课程比较的难,算是培养了我的算法思维吧(真实情况是感觉针对性不高……),以及后续我跟随代码随想录系统刷了2遍,代码随想录的算法题给我的感觉比左程云的课程更适合用来应对面试,其总结的方法非常实用。 + +23年的6-8月份,由于毕业方向调整,我去学习了一段时间的Java Performance,这段时间了解了一些性能调优方面的基础知识,对于常规Java那块的知识就耽搁了。 + +在9月份,我重新调整了毕业方向,再次投入到常规的Java学习路线中来,这个时候我是没有做实践项目的,在对比了几个知识星球的项目之后,我选择做了小傅哥的lottery抽奖系统(大营销)。小傅哥不仅录制了视频,让我可以跟着一步一步操作,而且也详细写了项目手册,这对于我这个鲜有实践经验的人来说是非常宝贵的。 + +后面的10-11月份,我对lottery抽奖系统(大营销)可能会问的问题都进行了整理思考,为此我把小傅哥的知识星球上关于lottery相关的帖子翻了个遍,收获巨大。后面在面试美团的时候,我真切的感受到了吃透自己做的项目的重要性,面试官真的会问的很细很具体,如果能答上来,并据此进行不足之处的展望,那是非常加分的。 + +在之后,23年的12月份,我开始投简历面试,月底各大公司招聘的机会不多,幸运的是美团招聘的岗位相对比较多,于是我最终选择去了美团。之后24年的1月到8月,我分别在美团和阿里实习,在9月系统准备秋招面试,1个月时间基本面完了,10月-11月写毕业论文,12月花了一点时间修改论文,大部分时间用来玩了,算是毕业入职前最后的放松😊。 + +回看我这一路的学习历程,中间有过曲折,但总体都是顺顺利利的,这种顺利我认为一部分原因是我选对了学习的路径,从学习八股到学习项目,我选的都是非常优质的内容,这帮助我少走了很多的弯路。真的感觉非常幸运在正确的时间加入了小傅哥的知识星球,并学习收获了如此硬核的知识。后面我仍然会跟随小傅哥继续学习,并尝试将所学的内容写成文章沉淀下来~ + +## 3. 秋招总结 + +秋招可以斩获那么多的offer,我认为可以归因于以下几个方面,分别是学历与奖项、八股、算法、项目、实习、简历书写、面试表现。 + +### 1、学历与奖项 + +学历和奖项这块,只能从大一大二还处于大学较早期的时候开始发力,包括卷绩点保研、考研、参加大赛、参加科研项目等。目前学历和奖项是有些大厂能否过简历筛选的硬性标准,比如淘天会筛掉双非(非强校)的同学,但大部分厂还是可以放低要求的。学历这块我感觉只是起到了敲门砖的作用,面试官看重的还是同学面试时候展现出来的技术能力。 + +### 2、八股 + +虽然有了项目以及实习经历,但我在面试的时候,还是会问一些八股,因为公司的招聘规则里面需要考察候选人的基础,这个时候还是会问一些基础八股的。此外,八股往浅了说是一些基础的八股知识,往深了说,从项目、实习经历延伸开来的问题也可以归类为八股,例如一些架构题、三高性能等。 + +八股的学习也可以从浅到深,浅的包括了面试常见的Java基础、JUC、JVM、Redis、MySQL、计算机网络、操作系统、MQ,如果时间有限,这些常规的八股题利用【JavaGuide+牛客面经+ChatGPT】可以速成一下。 + +八股学习方式,我个人会采用【问答】的方式先进行整体的理解,每个知识点的问答题目可能会非常多,如果记忆那么多文字负担也会非常重,所以为了简化记忆的难度,我会用【思维导图】进行知识的压缩,利用思维导图记录知识的【关键词】,在回答的时候,可以利用这些关键词来补全上下文。这个方法总结来说就是我们常说的把书读薄再读厚,读薄是提取关键点,读厚是进行扩充以及建立知识点之间的关联性。 + +更加深层的八股例如限流、熔断、高并发这些,可以结合自己的项目、实习经历进行针对性的学习,这些深层次的八股面试官问的时候也是从项目和实习经历展开。 + +### 3、算法 + +算法这块,日常时候可以跟随「代码随想录」系统刷上2遍,学习方法;然后反复地来刷leetCode的hot100以及top250,题目出现的频次可以查阅「CodeTop题库」。 + +我在面试的过程中,面试官出的题基本都在这个范围内,哪怕不在这个范围内,也是这些题进行小的一些修改。在面试的时候,面试官给的做题时间比较短,这需要平时多刷几遍才能做到面试的时候可以立刻想到思路并写出代码。 + +### 4、项目 + +个人项目在面试第一份实习岗位的时候,非常重要。项目需要使用到一些常见的技术,例如MQ、MySQL、Redis等,并需要适当的复杂性。 + +这里小傅哥写的项目就非常适合拿来写在简历上。在跟着学完一遍项目之后,还需要考虑到以下几个方面: + +1. 项目的难点与重点,也是需要在简历上呈现的点 +2. 项目一段话概括性介绍以及详细流程介绍 +3. 项目为什么用这个技术,例如为什么用Redis的分段锁,如果不用会怎么样,能否用其他技术代替,其他技术与该技术的区别等 +4. 项目使用了这个技术,会不会出现某些异常情况,例如Redis的锁超时释放了。是否会出现一些极端情况以及应对极端情况的措施,我在面试的时候会经常被询问到,面试官也比较看重这方面思考的周到性,一定要多准备这块。 +5. 项目技术相关的一些基础八股,例如Redis的锁的看门狗机制 +6. 项目的不足之处,对这些不足之处你认为可以从哪些方面进行优化完善 +7. 项目取得的一些技术性指标,例如耗时、QPS、节省人力等 + +>小傅哥,有非常多的实战项目,包括不同难度的业务类型和组件类型的项目,可以按个人学习积累选择学习。 + +![](https://bugstack.cn/images/article/zsxq/zsxq-241007-02.png) + +### 5、实习 + +实习需要做的就是能拿一个项目写到简历上,项目需要有些复杂度,如果实习干的是打杂的活,那可能需要去看一些组里别的项目,吃透之后写到简历上。 + +实习除了问项目之外,面试官也会问到实习期间学了什么技术,可以在实习期间再跟着学一些项目用到的技术原理。如果是日常实习,推荐带着学习的目的性去实习,毕竟后面很大概率是要跳到别的公司的;如果是暑期实习,并且感觉实习部门氛围不错的话,需要花点心思在转正上。 + +### 6、简历书写与面试表现 + +简历书写如果经历比较丰富的话,个人感觉可以写2页。简历上一般都会有的模块:教育经历(学校不错且成绩不错可以放开头)、实习经历、项目经历、个人技能、个人评价。简历书写的时候注重文字之间的排版以及不要写错别字,表达去除大白话,尽量写的精简专业一些。 + +还有非常重要的是,很多星球都提供免费的简历修改服务,可以多修改几个版本。简历模板我用的是「简历本」,需要付费,市面上也有很多免费的网站,大家可以去搜索。 + +在面试过程中,面对面试官问的问题,可以尽量回答的久一些,将一些可以联系的知识点也说出来,这样不仅可以减少面试提问的数量,也可以放一个🪝,引导面试官从自己熟悉的这块知识提问。还有面试表达的时候,最好能有条理,如果记忆的是知识点关键词的话,可以先说关键词,再对此进行补充。 + +除了上面这些,还有非常重要的一点是运气🍀,祝愿大家都能遇到一个对口的面试官,顺利斩获offer~ + +以上的秋招面试经验源自我个人经历的一些感悟,不同的人可能会不一样,仅供大家参考一下~最后祝愿大家都能有个好的offer🍀🍀🍀~ + +>好啦🌶,以上就是来自小傅哥社群伙伴的分享,我认为非常有价值,非常值得大家参考。以这样的认知和学习,将来职业发展会走的非常好。 + +
+ +
\ No newline at end of file diff --git a/docs/md/zsxq/memorabilia/interview-zijie.md b/docs/md/zsxq/memorabilia/interview-zijie.md new file mode 100644 index 000000000..c175c24b9 --- /dev/null +++ b/docs/md/zsxq/memorabilia/interview-zijie.md @@ -0,0 +1,125 @@ +--- +title: 2023年8月25日,字节面试,管你是不是刚毕业! +lock: no +--- + +# 字节面试,管你是不是刚毕业! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、他,上来打我! + +
+ +
+ +- 【字节】`除了MQ解耦发奖,是否还有比MQ更优的解决方案?` +- 【字节】`你现在是学习的项目,但如果部署上线,还有哪些地方需要改进?` +- 【字节】`如果让你来评估项目的QPS的话,你会用什么方式来评估?(补充;不要做压测,就根据设计、硬件配置推导QPS)` +- 【字节】`比如说:16核64G的机器,做秒杀系统,你会修改和配置哪些东西?(不考虑 Redis、Kafka等)` +- 【字节】`接着上面,SpringBoot、JVM 配置参数还有哪些。你提到 JVM 堆大小,如果配置过大会有什么副作用。` +- 【字节】`秒杀场景下用哪种垃圾回收器合适` +- 【字节】`写代码时有没有什么方式,尽量减少FullGC` +- 【字节】`实现短信验证码的生成和校验,只使用Java语言,不能使用Redis等存储工具` +- 【百度】`说一下AbstractRoutingDataSource,它的核心方法?` +- 【百度】`多数据源如何配置?` +- 【百度】`对MyBatis的拦截器有了解吗?` +- 【京东】`在这个纸上,画一下你刚才说的策略模式、模板模式和工厂模式的代码实现结构` +- 【京东】`你提到的DDD架构中,依赖倒置做了防腐(ACL)隔离。请详细说明下` +- 【小红书】`如果让你从零到一设计一个配置中心你怎么设计,还有es的底层是怎么实现的?` +- 更多... [https://bugstack.cn/md/zsxq/material/interview.html](https://bugstack.cn/md/zsxq/material/interview.html) + +这些问题已经一一做了解答,并且还有星球伙伴的讨论。这也是大家加入小傅哥星球【码农会锁】除了做实战项目以外的技术价值,因为你所有的技术成长问题,我都可以为你兜底。解答链接:[https://t.zsxq.com/11PQTlMvA](https://t.zsxq.com/11PQTlMvA) - 星球中还有各种各样的场景问题解决方案,都可以学习。 + +--- + +**说实话**,就这么面。没有个2~3年开发经验,根本扛不住。但就这么面,如果拿Offer了,薪资待遇也是贼高,`起步即巅峰!`薪资基本能达到普码工作3年-4年的待遇。 + +对于越来越多这样有难度的面试方式,我也想了想🤔为啥? + +其实最大的原因就是大厂掐尖,面这样的问题,是说明真的有人能回答的出来。能回来的出来的,基本也是研究生或者很牛的本科生,在校期间就非常积极主动的做了这样复杂的项目锻炼,还做了不少上线部署对外提供服务的事情。虽然回答的可能有些不足,但往往也能看出求职者的深度积累到底有多少,深度越深,薪资越高! + +大部分校招生也会直呼:"`太TM卷了,学不完,根本学不完`"。但对于喜欢编程的人会觉得挺爽,写代码还能赚钱,美滋滋。 + +其实说到根上,在对编程有那么一点喜欢且不是谋生手段的情况下。用大学4年或者加研究生2~3年,也就是4~7年时间,完全可以锻炼出一个非常优秀的研发工程师。 + +差距是从每一天积累拉开的!那如果你想把这差距缩小点,哪怕先面个不是字节这类的大厂,走个中小厂的话,**那就要多实战**!!! + +## 二、请,正确学习! + +### 1. 多犯错误 + +强烈不建议已经渡过了编程新手村的伙伴,仍需要对照着视频,一行一行的跟着写代码。而且对照视频抄代码,其实比有思考的写代码,要累的多。这样的学代码,非常缺少自己的思考,也会很少地遇到错误。感觉挺良好,其实啥也没学到。 + +学习编程时,一个编程错误。其实就可能让你检索出一堆的犯这个错的各种情况,你需要一个个验证。你到的错误到底是怎么导致的。好啦,反反复复的折腾。最后,你吸收了一堆的编程经验和调试技巧。这就是多犯错的重要性。 + +### 2. 实践先行 + +编程不需要过早的看特别的多的理论资料,因为所有的理论也都是来自于实践后的提炼总结。但提炼的总结往往会有些抽象,同时又伴有各种的名词定义。这就像买了台自行车🚴🏻,买回来拆了研究,还是先骑着走走。 + +而学习编程最好的方式是能先看到结果,把一个个大项目的所涉及的小知识点,碎片化的验证运行输出结果。有了结果,在分析细节,无论是加日志、打断点、补功能,也都变得顺利了起来。当这些实践都验证的充足了,在去看理论就会恍然大悟,原来他说的这个是意思啊!八戒! + +### 3. 结果导向 + +缺少目标感是不少伙伴学习编程时遇到的问题,每当面对一项需要学习的内容时,也会缺少启动方向。对于这样的情况,往往我的做法是目标驱动,结果导向。先明确我要做的东西的最终结果,以最终结果来驱动我的学习路径。 + +在这个过程,不需要事先就把所有条件都准备好。比如我学习一个项目,但项目里A不会、B不懂、C不行。没关系,先动起来。哪怕是开始一点点,也比一直站着想不动强。只要开始了,剩下的就是遇山开山⛰罢了! + +### 4. 缩小问题 + +每次遇到什么编程不会的情况,先尝试把问题缩小。别一下把问题搞的很大,尤其是别写了一堆的代码的才测试。 + +当你学会把问题缩小,缩最容易解决的范围后,其实大问题也就变成了小问题。这也就编程中第一条编程原则,康威定律提到的。问题越小越容易理解和被处理。就像我的博客 [bugstack.cn](https://bugstack.cn/) 3年来,有几百万字的文字输出,但对于我来说,其实只是每天都积累一点,有时候可能就是几十字。而这些也都是碎片化的时间来一个个解决我要表达的内容。 + +### 5. 提高认知 + +其实编码能力也是思维能力,思维的强弱有认知决定。而这份编程认知,一方面是来自于自己的多种实践,解决过不同场景的各类技术问题,同时具有创造性的设计了优秀的方案,从而积累的能力。这也就是我前面提到的,你要多犯错,你不犯错,你就没机会创造。而另外一方面的提高认知,是做很多优秀的项目,这就跟去大厂工作一样,要不就不是里面的项目经验和大牛的技巧吗?! + +第二呢,是来自于对优秀框架源码和项目的学习。在这里我想说,MyBatis 源码,真是贼可以学习一下。一个框架,以会话模型为入口,进入后。运用数十种设计模式,拆分数据源、连接池、执行器、解析器等,兼职是学习的宝藏类代码。在者这些东西都锻炼了,之后呢,要进行实践,要做项目,要把各种所学和项目结合起来锻炼。通过这样的方式,提高编程认知,锻炼编程能力。 + +### 6. 拷贝优秀 + +学习建议、职业指导、技术分享、述职答辩,每一项来自大牛的分享,都是非常有意义的信息积累,你可以针对性的借鉴和参考。尤其是能顺利的走到这个阶段的大牛,每一个都有很强的一面,否则这风风雨雨这些年中,早被职场淘汰了。 + +此外还有那些优秀大学的毕业生和海外就业/工作伙伴的学习方法,真的可以开阔出你很多的学习思路。而这些东西,不会有书、也不会有笔记、也不会有博客。因为比较琐碎,有时候就是一句一句零散的话,但却可能可以改变你命运的齿轮。 + +### 7. 保持热情 + +如果你对编程有好奇感,做出东西有兴奋劲,那么一定保持下去。长期坚持,你会扫出来很多编程路上的宝藏,让你兴奋的睡不着。当你有热情的学习编程后,会发现这东西太有意思了,兼职像一个额外的世界。而是你这个世界里小小的建造者,用着一个新的语言在构建新的世界。 + +### 8. 长期学习 + +入了编程这一行,基本就是要能长期学习了。没有那个技术的语言或者工作工具能长期使用的,都可能被替代,也都每年有新的东西出来。如果你想靠着混职场,不提高自己的技术,那35拿到砍,真的可能会对你下手!而长期学习,也能让你有底气,随时能走的出去,想留也能留的下来。别听有些maimai上的扯淡,只会技术不行啥的。要是不会技术,才不行,不够深入,才不行。技术永远是编程人员的立身之本。 + +### 9. 知识闭环 + +可能一开始你只是个小小的Java小程序,但随时工作年限的增加,你会逐步的学习到其他各个技术技能,甚至还会包括前端语言以及运维能力。再往后,还可能会夸1~3个编程语言来完成你的开发诉求。而我,也是这样一路经历过来的10年编程。 + +在工作到3~5年的时候,时而就在想,自己是否具备一个完整的技术栈体系,如果脱离公司的框架,我是否能构建出我的应用。从那个时候开始,不断的积累自己对各项技术栈的运用,用市面的技术栈替代各项公司里用过的技术栈,把他们的应用学会,把他们的原理搞透。一点点的,也就把各项技术栈体系打通了,最后也就走到了架构师的岗位。 + +### 10. 氛围圈子 + +讲道理,要不买点书看、要不加入些技术圈子,用不了多少钱,真的用不了多少钱。给自己点技术投资,多开开事业,多一些技术见闻。别太让自己的技术成长闭塞,做做自己以前可能没做过的选择,也许你命运的齿轮就开始转动了! + +**以下这个技术圈子强烈建议加入**,因为你可以学习到来自10年编程经验的架构师所编写的各项浅浅深深不同梯度难度的项目,也可以见闻到几千人的技术交流和知识积累,还可以学习到国内高校、海外留学伙伴的学习方式方法。这远比你看过一本书、听过一句话,都实在的多! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +--- + +**祝大家早日封神!** + +- ① 写够03万行代码 = 后天武者/炼体:炼肉筋骨 +- ② 写够05万行代码 = 先天武者/明劲:破空炸响 +- ③ 写够10万行代码 = 武师/暗劲:产生元气 +- ④ 写够20万行代码 = 大师/化劲:全身布气 +- ⑤ 写够30万行代码 = 宗师/丹劲:气集一点 +- ⑥ 写够50万行代码 = 武圣/罡劲:透气成罡 +- ⑧ 写够100万行代码 = 武神/破虚:罡气破空 +- ⑨ 写够500万行代码 = 陆地神仙/见神:内视身体 +- ⑩ 写够1000万行代码 = 星河大帝/成神:知晓未来 + diff --git a/docs/md/zsxq/memorabilia/java-interview-experience.md b/docs/md/zsxq/memorabilia/java-interview-experience.md new file mode 100644 index 000000000..dc3c604e6 --- /dev/null +++ b/docs/md/zsxq/memorabilia/java-interview-experience.md @@ -0,0 +1,100 @@ +--- +title: 这21套大厂Java资料,写够1万行,校招够了。写够10万行,可以冲P7了! +lock: no +--- + +# 这21套大厂Java资料,写够1万行,校招够了。写够10万行,可以冲P7了! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +你见过面试评价表吗?`常用框架应用经验不足`、`缺少分布式项目经验`、`无C端场景开发经验`、`项目流程不完善`、`CRUD偏多,无架构和设计思考`、`复杂问题思考不足`、`伪场景需求,无实际应用`等。看完是不有点🥶冷汗直流。那怎么提升这些方面的知识? + +
+ +
+ +**项目不行,编码量还不够!** + +之所以遇到这些面试问题,就是因为很多伙伴从毕业🎓到工作,以及在工作中基本都没做过什么好项目。不是毕业前做了一堆CRUD流程的Demo项目,就是工作后组装API接口的。往往这些过程都是有连带关系的,因为起步做的项目弱,缺少技术的积累和技术视野的认知,所以面试中表现的不够强,导致最终找工作的坑也会差。 + +当然,聪明一些有想法的伙伴,肯定会想办法去找一些在大厂真实工作的架构师进行`"偷师"`。把来自中大厂的`业务经验`、`架构经验`、`编程思维`、`编码技巧`,学习下来在用于实践验证和增强简历竞争力。所以跟谁学习很重要! + +**注意**:📢 你的有效代码量,与你的能力息息相关。尤其是代码中含架构设计、设计模式、高级编码、场景问题的指标量占比越高越好。当你的积累远超过这一阶段诉求时候,你的能力也会很容易匹配到工作岗位。 + +
+ +
+ +>小傅哥准备了21套内容,帮你做有规划的、成体系的、可高效的,成为面试中、职场中,有竞争力的人!文末可以获取全部内容。 + +## 一、初学踩坑 + +`程序员👨🏻‍💻,大量的编码,才能锻炼出大量的经验。` + +在我初学编程以及到了每个阶段的瓶颈期,需要提升自己的时候,也尝试过在网上找资料。但往往这些资料都是不成体系的,很碎片化,后来才知道那都是一些博主为了博取流量,四处整合的资料,打包在给你。 + +关于内容的质量、内容的连贯性、博主是不知道的,因为博主自己也没有学习过这些资料。但我们拿到后在花大量的时间看,而且还可能是看了一些过时不用的,就非常耗费精力。 + +如图,19年的时候,级别T6,再上一层就是架构师。于是为了扩展知识学习,我找到2个T资料(后来又删了很多!) + +
+ +
+ +- 首先,是想锻炼一些架构思维、编码思维、逻辑思维,但往往市面这类资料几乎是没有。大部分都是基础教程,以及一些简单的管理类项目。代码质量还不及公司同事写的。 +- 之后,这些资料缺少连贯性,学习时候的前置知识说明和文档说明,都是缺失的(毕竟都是免费的)。 +- 最后,遇到问题是真没有人能问到,别说提升能力了,自己的那点能力都被薅干净,去解决课程bug! + +## 二、新的计划 + +`19年,因为自身就有晋升的诉求,预期可哪找资料,不如公司里找人!` + +本身自己就在互联网大厂,身边这么多资源,还都是认识了很久的一群高级架构师。那还可哪找啥人学习,直接拉着架构师,让他们做各类分享,从分享的干货中学习多好。后来,我也把不断积累到的高级能力运用到了项目和编码中,还在后来开始做了技术分享,也就是大家看到的一整套、一整套的内容。 + +我不图快,我只想认真的想把自己的技术,从基础到应用,一点点的全部分享出来。让伙伴们学习基础可以运用到项目,学习项目可以从基础中查阅到相关的案例。这样循序渐进,有紧有收的方式,才是正确的学习方式。 + +**想写好这些东西,确实耗精力!5年了!** + +
+ +
+ +- 这是小傅哥,为大家陆续编写的编程技术资料。包括;基础教程、技术小册、源码学习,再到业务项目6套、组件项目7套。并且还在继续更新中,后续还有更多项目加入。—— 我要把大厂中做的项目,全部陆续的做成一个个学习项目,让大家可以提高自己的能力。 +- 业务项目6个,代码行数5万。组件项目7个,代码行数2.35万。源码学习,代码行数1.06万,整体将近10万行。每个项目都具有独特的架构、场景、设计、编码。学习任何一个都能带来非常多的技术收获,而且会学习到耳目一新的东西! + +## 三、成绩认可 + +这5年来,我用大厂锻炼的一身技能,帮助了非常多的伙伴,实打实的提到了编程技术。还为星球伙伴做简历评审,现已积累了将近500份简历+评审记录(看过之后都会写简历了!)。在学习后,更有伙伴坦言:”我是被傅哥的技术,抬着进的这家公司!“ + +### 1. 斩获Offer + +
+ +
+ +
+ +
+ +
+ +
+ +### 2. 简历优化 + +
+ +
+ +
+ +
+ +>这些都是可以在小傅哥的社群学习到的内容和获得到的服务,有大厂架构师这样手把手的教,可以说是非常实在。并且你在以后工作中遇到的实际场景问题,也都可以给予解答,**让你顺利度过试用期!🍻** + diff --git a/docs/md/zsxq/memorabilia/java-resume-Project.md b/docs/md/zsxq/memorabilia/java-resume-Project.md new file mode 100644 index 000000000..a526aa4b7 --- /dev/null +++ b/docs/md/zsxq/memorabilia/java-resume-Project.md @@ -0,0 +1,71 @@ +--- +title: 都做过外卖和点评,怎么和别人拉开差距? +lock: no +--- + +# 都做过外卖和点评,怎么和别人拉开差距? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在参与过多次的实习、校招、社招,面试,以及帮助几百人评审简历提供优化建议的经验积累下,一份简历要想脱颖而出,是要学会内容组合编写的。那怎么来组合简历内容呢?今天来给大家分享下。 + +
+ +
+ +**好的简历,就是用不同的内容做举证,展示自己的技术实力** + +`点评`、`外卖`、`电商/售票`,不少小傅哥的粉丝伙伴都做过这些项目,这些内容可以帮助大家快速的入门编程,也是非常不错的学习资料。在5年前,写这么任意一份项目内容,都可以成为那个时期的Offer收割机了。但现在的简历大部分都是相似模板的内容描述,筛选简历的时候拉不开差距。不过往往坏事和好事是相对的,大部分的都一样的时候,只要有人做出差异化,就会非常亮眼。 + +接下来,小傅哥就告诉你怎么做差异化,让你的简历脱颖而出! + +## 一、组合技巧 + +>写简历就跟排兵布阵打仗一样,要搭配不同的兵种进行作战。 + +首先大家面试的目标是进入到职场,那么你的简历中项目内容和结构,越贴合于公司的标准,在筛选和面试中都是非常占优势的。那么在公司中是这样的,业务项目会包括;`用户`、`账户`、`信贷`、`商品库`、`交易`、`营销`(`积分、玩法、活动、券包、抽奖`)、`结算`、`清分`、`台账`、`收银台`等,之后是各个业务组里还会做一些所谓的轮子,这些轮子既不是运维类的,也不是组件里的,就单纯是为了解决同类共性的业务问题所做的开发,如;对接很多支付方,会提供自己的支付SDK来聚合支付。有自身的众多业务一致性监控,会有最终的环路/旁路监控系统。 + +那这你就知道了,如果你做的是外卖或者点评或者其他的主业务流程项目,他们都属于业务系统的一部分,之后所有的这些项目都可以配合营销项目。比如 `外卖 + 抽奖`、`点评 + 抽奖`、`支付完成 + 抽奖`,把这些项目组合起来使用,建立一个衔接性。这样在看下简历内容的整体排布。 + +
+ +
+ +- 四方面内容;积累展示、专业技能、业务项目、技术项目。 +- 看过有一些比较机智的伙伴,会在个人信息下面用2行左右的话术介绍出自己的能力。你不写这一句话,面试的时候也会让你做个自我介绍,那样突然的总结往往比较慌,说不来几句,就结束了。不如就提前写好,精炼话术写到简历上。 +- 之后是专业技能的覆盖度,不少简历这方面体现的是比较弱的,有的人全写精通,有的人全写了解,或者写的内容和项目用到的不匹配/没覆盖到,以及写的内容和招聘的诉求不匹配。这块要注意,没用过的可以学习。 +- 业务项目,就是上面咱们提到的组合,之后组件项目是一个亮点。可以是一个1个牛的业务 + 1个组件,也可以是2个业务 + 1个组件。来搭配着写简历。如果感觉写的多了,可以把组件的项目,不单独写项目,只是写到个人的介绍中,或者融合到业务项目里去。 + +>有了这样的组合意识,你会知道学习的时候要对那些内容下手。也会有意识的补充自己的短板。 + +## 二、项目搭配 + +小傅哥的星球有10个项目,5个业务 + 5个技术,你可以组合的选择学习,搭配着写到简历。这些项目都是小傅哥花费大量的时间创作,每一个都有自己的特色和亮点,做这些内容不只是完成项目,还会学到;思想、架构、场景解决方案、设计模式运用、高级编程技巧等。 + +
+ +
+ +- 所有这些内容,**加入星球「码农会锁」**,全部解锁学习。并不断的持续加入新的内容。 +- 小白也能跟着学习,所有项目用到的内容,都已经被小傅哥单独拆解一个个小的技术案例,让大家学习补充(如图最下面部分,非常全面成体系)。 +- 在学习完项目编写简历投递的前,还可以把简历发出来,小傅哥会帮你做简历评审和指导。 + +## 三、加入学习 + +>我用工作10年+的经验告诉你,一个好的Offer起步,至少可以避免花费3年时间走弯路! + +星球「码农会锁」的这种公司级别的实战项目,全程是需求分析、架构设计、几十张表的把控、众多场景设计模式的解决方案和高级编码的技巧运用,都是让你拉倒面试竞争力的资本。—— 当你不在做 CRUD 的 demo 项目,你会发现自己的技术是质的改变,提升的非常快! + +在今年的面试中,星球帮助众多伙伴拿到`微信支付`、`京东科技`、`度小满`、`蚂蚁金服`、`Lazada(电商优惠营销)`、`快手`、`美团到店`等Offer,还有的校招生薪资最高年包到45w,这个级别已经超过工作4年-5年的伙伴了!如果学历那么高,但技术不错的,也能拿到中厂Offer。所以说,一个好的Offer起步,真的可以少走3年的弯路! + +
+ +
+ +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) diff --git a/docs/md/zsxq/memorabilia/job-hire-jd.md b/docs/md/zsxq/memorabilia/job-hire-jd.md new file mode 100644 index 000000000..8ed6688ed --- /dev/null +++ b/docs/md/zsxq/memorabilia/job-hire-jd.md @@ -0,0 +1,140 @@ +--- +title: 不废话,公司需要什么能力,我们就安排什么项目! +lock: no +--- + +# 不废话,公司需要什么能力,我们就安排什么项目! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +“小傅哥,是真想把我教会!”,跟着小傅哥学习的越久,越发现很多以前单点的碎片知识,都被成体系的串联起来了。并且学习的这些东西,是真的匹配到了招聘的岗位,同时在面试中与面试官有的聊。**这是真的爽!** + +
+ +
+ +**匹配大厂招聘诉求学习!** + +就编程类的知识、项目、方向来讲有非常非常多,但如果你全心的投入到;`servlet`、`jsp`、`Struts2` 的过时技术,`坦克大战`、`学生管理系统`、`图书管理系统` 的大CRUD流水账项目,那么在投递简历的时候,真的很难匹配到招聘要求。 + +而在我做互联网大厂架构师这么多年,经历过的众多各类型的项目,以及在这个过程中完成的技术调研、技术选型、方案设计、架构设计、项目交付,可以明确的给你讲,C端、高并发、分布式、微服务、DDD(越来越多中大厂使用)、AIGC(开始结合),这样的项目是可以更多的匹配到招聘诉求,也是更能提升个人能力的编程项目。**举例;如图,去哪网招聘** + +
+ +
+ +- 首先,如招聘诉求描述,活动、营销、交易、AIGC、DDD、C端、抽象、拆分、以及常用分布式技术栈,都是各类互联网大厂中,最常做的业务类型和所需的技术方案。 +- 之后,你会发现右侧的内容,嘎嘎的匹配到了左侧的招聘诉求上!哈哈哈,这些内容都是小傅哥为你提供的实战编程项目,它们出身名门大厂,自带高级架构和巧妙的逻辑设计。在这些项目上,你可以学习到互联网大厂 + 大架构师的编码(`小公司出来的根本写不出这样的代码`),这不只是一场课程,也是一次优雅的编程旅行,携助你去期待的大厂入职。✈️ + +## 他们都要DDD! + +
+ +
+网图,排名不一定准确,但在搜索官网的招聘中,会看到很多核心C端的岗位,都有;DDD、营销、交易、AIGC、高并发、分布式、微服务、设计模式的相关经验优先。举例;京东、小红书、百度。 + + +### 1. 京东 + +
+ +
+ +官网:[https://zhaopin.jd.com/web/job_info_list/3?isHunterFlag=false](https://zhaopin.jd.com/web/job_info_list/3?isHunterFlag=false) + +### 2. 小红书 + +
+ +
+ +官网:[https://job.xiaohongshu.com/](https://job.xiaohongshu.com/) + +### 3. 百度 + +
+ +
+ +官网:[https://talent.baidu.com/jobs/social-list](https://talent.baidu.com/jobs/social-list) + +>所以,这也是为什么小傅哥,把这些知识体系,拆开了、揉碎了,一口口的喂给你!并且小傅哥是真的复刻大厂的工作方式,把这些知识传授给你。 + +## 1:1 复刻大厂流程 + +**图,是对业务流程的深入理解,是对系统架构的逻辑抽象。** + +项目的承接,并不是一上来就直接干代码,而是要做一列的BRD评审、PRD评审,再到研发做系统的架构设计、库表设计、流程设计、外部对接,这个过程中会产生出很多的各类型图稿。这些东西都是非常有价值的内容,让我们可以更好的、更深入、更全面的理解一套系统。而且,在你工作以后,你很多时候都要和这些图打交道,这样你才能更好的完成工作的交付。 + +那么,这些东西在小傅哥的社群里,都是随着项目一点点的渗透给你的。潜移默化的你也就学会了分析需求和设计系统。举例; + +### 1. 系统架构图 + +**软件架构**是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。软件架构会包括软件组件、组件之间的关系,组件特性以及组件间关系的特性。软件架构可以和建筑物的架构相比拟。软件架构是构建计算机软件,开发系统以及计划进行的基础,可以列出开发团队需要完成的任务。 + +
+ +
+ +- 这是一套大营销系统的分布式架构设计图,从前端到负载,从服务治理到后端分布式技术栈体现,从应用到部署和监控的全体系展示。在这样一套系统架构中,你可以清楚的知道从前到后的流程、各项分布式技术栈的用途、整个系统的脉络关系。所以这样的一个图可以清晰的指导我们做系统的搭建。 +- `佛瑞德·布鲁克斯`在写作《人月神话》一书时提及:软件系统的架构是有关软件系统该作什么以及不该作什么的实体观点。这些观点应和软件的实现分开。架构师的角色是“观点的看守者”,确认系统中增加的部分是符合此架构,因此可以保有概念完整性 +- 另外程序员`马尔文·康威`在1967年论文发表了康威定律,其中提到一个组织开发的软件,其架构会反映其组织架构。佛瑞德·布鲁克斯在写作《人月神话》一书时,就在书上时提到此例子,命名为“康威定律”。 + +### 2. 四色建模图 + +如果系统是 DDD 开发的,还需要做四色建模。在使用 DDD 的标准对系统建模前,一堆人要先了解 DDD 的操作手段,这样才能让产品、研发、测试、运营等了解业务的伙伴,都能在同一个语言下完成系统建模。 + +- 蓝色 - 决策命令,是用户发起的行为动作,如;开始签到、开始抽奖、查看额度等。 +- 黄色 - 领域事件,过去时态描述。如;签到完成、抽奖完成、奖品发放完成。它所阐述的都是这个领域要完成的终态。 +- 粉色 - 外部系统,如你的系统需要调用外部的接口完成流程。 +- 红色 - 业务流程,用于串联决策命令到领域事件,所实现的业务流程。一些简单的场景则直接有决策命令到领域事件就可以了。 +- 绿色 - 只读模型,做一些读取数据的动作,没有写库的操作。 +- 棕色 - 领域对象,每个决策命令的发起,都是含有一个对应的领域对象。 + +
+ +
+ +- 系统建模后可以细分出系统开发中要实现的领域,包括;返利、活动、策略、奖品,兑换可以是单独的领域也可以合并到返利实现。 +- 具体的建模过程可以阅读 [《架构:DDD 领域驱动设计,战略、战术、战役,落地指引规范》](https://bugstack.cn/md/project/big-market/ddd.html) + +### 3. 设计模式图 + +设计模式是解耦系统流程非常重要的手段,通过设计模式把复杂的流程切割成可以被容易处理的执行单元,在通过设计模式的创建、行为、结构,三类模型完成执行动作的处理。 + +
+ +
+ +- 如图,这是一个规则树模型,解决的拼团中实际的场景业务。从根节点、开关节点、营销节点、人群节点,再到最终的正常和异常结束节点。每个节点分别处理自己的业务流程。 +- 你可以想象,如果没有这样的设计模式模型结构,那么在代码中,就是一个大方法中,一堆的逻辑编写,维护的成本是非常高的。 + +### 4. 源码解析图 + +各类的框架源码中有非常多的设计技巧,也有很多的架构思维模型。如大家常用的 MyBatis 框架,拆解后的设计,就可以看出有多少东西可以学习。 + +
+ +
+ +这是整个《手写 Mybatis》的全貌地图,小傅哥会带着大家逐步实现这里面的功能模块,分章节细化各个模块的实现流程,最终让读者实现出一个丰富、全面、细致的 ORM 框架。在学习的过程中,大家也可以参考这张图来对照手写的代码以及 Mybatis 的源码,这样更加有利于对 Mybatis 框架的理解。 + +## 我是小白怎么开始 + +我的学习伙伴有在校的,有实习的,有毕业的,大家处于不同阶段。所以,小傅哥花费近4年时间,以大厂晋级能力项的要求,拆分出14个项目,以不同难度方式从小白到大白,一路培养,直至成长为高端的码农!在这个过程,你不只是学习到了想法,还学习到了一整套方法论。 + +如图,这是一整套的实战项目学习进阶路线,从小白到大佬,全程视频手把手带着从0到1,一步步完成项目的设计、开发和上线。在整套内容学习过程中,小傅哥为你提供了非常好的技术交流社群,及时解决学习问题。还包括调试你的问题代码,带你快速🔜出坑! + +
+ +
+ +- 首先,这一整套全体系的学习课程比私教培训实惠,更比培训班上万的培训费便宜。可能也就是培训班1天的💰钱,就能学习到这一整套内容了。 +- 之后,你学习的整套课程,就是小傅哥这个架构师自己全部原创编写的。这也就是说,你所提到的任何问题,小傅哥都能给你解答和讨论。 +- 那么,这么实惠的课程,成体系的课程,还是架构师编写的。还有什么可犹豫的,完全可以撸起来了! diff --git a/docs/md/zsxq/memorabilia/overall.md b/docs/md/zsxq/memorabilia/overall.md new file mode 100644 index 000000000..3621fe1e4 --- /dev/null +++ b/docs/md/zsxq/memorabilia/overall.md @@ -0,0 +1,166 @@ +--- +title: 2023年6月26日,回答:"小傅哥的星球怎么样?" +lock: no +--- + +# 他们问:“小傅哥的星球怎么样?” + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +`如果当年我遇到自己,我会更早的成为我?` + +你觉得一瞬间的技术能力提升,是必须经**历过过吃苦**或者**浪费掉时间**去摸索找路吗?🤔 可能都不是!任何成长都不是一个孤立的过程,你不可能凭空创造成长。而成长的本质是提升认知,只有吸收更有价值的知识和见识更高人的本事,才是快速成长的关键。 + +就像15年我跳槽进入互联网,结识了更多的`技术大牛`,倾听了更多的`技术分享`、参与了更多的`技术项目`,才是让我进入快速成长的关键。虽然吃苦和摸索也是成长过程中的一部分,但那并不是必须经历的过程。有时候,只有跟对人做对事,**才能少走弯路,避免犯错**,让你的成长之路更加顺畅。 + +**当我用10年时间**,逐步成为我仰慕的有技术高度人以后,我也开始把我的经验、本领、心得,汇总成一个个项目、小册、知识,分享给在座的各位。我会让各位看到并学习到;符合大厂开发诉求,满足招聘、晋升、跳槽,本领的**成体系化的知识**都有哪些,都该如何学习。因为这些内容都是原创,所以保质保量。就像商城的自营一样,售后杠杠的!—— **小傅哥会让你成为优先上岸的人!** + +
+ +
+ +- 星球的内容主要分为四部分;`学习路线 - 帮你简明扼要的规划学习`、`项目实战 - 助你提升编程经验锻炼开发能力`、`辅导学习 - 1v1的问题解答直接了当节省时间`、`应对面试 - 手把手优化简历提高建议,让你收割Offer!` +- 接下来小傅哥就分别介绍下这部分内容,让小伙伴可以放心食用! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) +## 一、学习路线 + +Java 编写学习,首先要明确从哪开始到哪结束。但这个过程以我的学习经验可以告诉你,你并不需要一个特别大的,特别杂的学习路线。因为那样只会让你疲惫😫在学习路线的路上,没到终点就累嘎了。 + +所以小傅哥以互联网公司中实际开发所需技术为目标,编写了一套直击目标的**《 Java 简明教程》**,全教程以简单明了的学习方式,应用当下主流的技术栈,原创各项技术知识的小册和视频。通过这样一个个小的案例,为大家讲解各类技术栈的运用。**最后在通过星球的实战项目,把各项的技术栈给串联起来使用,直至把大家平稳的扶到这条编程开发路上**。 + +
+ +
+ +
+ +
+ +- 地址:[https://t.zsxq.com/0fE3cp7uK](https://t.zsxq.com/0fE3cp7uK) +- 目标:此课程以教会小白伙伴实战为主,学习后可以参与项目开发。 + +## 二、项目实战 + +学习路线,学习的相当于**技术零件**,但这些零件怎么使用呢?其实想熟练使用这些零件,就需要在一个个项目上锻炼自己的编程能力,同时还需要在各个场景架构设计提高编程思维。所以小傅哥在星球里准备了4类内容;业务项目、组件项目、开源项目和架构设计。接下来小傅哥就告诉你下,这几类内容,都能让你学习到什么。 + +### 1. 业务项目 + +#### 1.1 ChatGPT 微服务应用体系构建 👣 + +
+ +
+ +#### 1.2 Lottery 分布式抽奖系统 + +
+ +
+ +#### 1.3 Netty+JavaFx实战:仿桌面版微信聊天 + +
+ +
+ +### 2. 组件项目 + +#### 2.1 API 网关:中间件设计和实践 + +
+ +
+ +#### 2.2 SpringBoot 中间件设计和开发 + +
+ +
+ +#### 2.3 IDEA Plugin 开发手册 + +
+ +
+ +### 3. 架构设计 + +知识星球【码农会锁】中此模块主要用于帮助小伙伴提升,各个场景中的架构设计经验。这里很多的场景都是小伙伴在实际工作中所遇到的场景设计问题,小傅哥以及星球伙伴在各个方向有相关经验都会分享。所以加入星球的小伙伴,你可以非常方便的掌握到这些实战技能。 + +
+ +
+ +
+ +
+ +### 4. 开源项目 + +除了以上这些小傅哥编写开发的项目让大家学习以外,星球内还开放【开源项目】计划,此计划用于帮助星球伙伴自己做一个独立的项目。小傅哥帮你搭建框架、编写文档、提供支持,也就是一个项目的所需各类物料零件,都帮你准备好。之后你就可以做一个自己的项目了。 + +▍开展形式 + +- 第一步:需求来源 + - 1. 星球伙伴都可以提交有意思的项目思路,或者自己做好一份简单的需求文档,通过此作业提交上来。 + - 2. 小傅哥也会检索好的项目,并编写成需求文档,大家可以自行认领开发。 +- 第二步:系统设计 + - 1. 认领项目的伙伴,可以做一个简单的系统设计。 + - 2. 之后小傅哥会对你的系统设计,通过视频直播的方式进行评审,指导需要修复的问题点。 +- 第三步:功能开发 + - 1. 小傅哥会提供MVC和DDD两种架构模型,按照不同场景进行选择 + - 2. 此外小傅哥会对各自项目开发中,要用到各类技术应用案例,陆续编写《Java简明教程》,帮助你完成项目 + - 3. 项目中的代码定期由开发者进行分享,也可以在项目完成进行分享。并对代码进行评审,提出可优化的逻辑和设计 +- 第四步:项目交付 + - 1. 此阶段为交付阶段,小傅哥会邀开发者视频演示和分享项目成果物。 + - 2. 也可以让大家进行访问项目,以及做一些测试。 + - 3. 此外小傅哥也会对优秀的项目开发者提供奖品鼓励。 + +▍交付结果 + +- [手写了一个RPC框架 @~Return°](https://t.zsxq.com/0fiesJEkX) +- [GPT Terminal 终端网站 @ltyzzz](https://t.zsxq.com/0f0lGMEoa) +- [一个复杂的Excel报表导出工具 @飞一站](https://t.zsxq.com/0flvV2tQo) +- [IDEA 插件vo2dto,快速生成两个对象转换代码 @小傅哥 5.1k 下载安装](https://t.zsxq.com/0f6GXFSmD) +- [Art - 从0到1搭建微服务开发脚手架 @fxz - gitee 520+ Star ⭐️](https://t.zsxq.com/0fPgiUXhB) + +## 三、辅导学习 + +对于编程学习最重要的是什么?其实不是搜索到一堆资料甚至几个T就完事了,而是能明确知道一个项目,一个路线在讲解什么,遇到问题时有人可以回复,学习时候可以有人讨论。这样的资料才是有价值的。 + +所以小傅哥星球以个人原创为核心,所编写的任何一个资料,一个课程,一章节内容、一行代码,都可以随时给你解释清楚。让你少走弯路、少浪费时间! + +
+ +
+ +
+ +
+ +近2年来,回复了1500个1v1的技术问题,获赞1.1万次。这些回答包括;技术问题、方案设计、系统架构、求职招聘、工作交流、成长计划、学习心得等等。每一个回答,小傅哥都会认真回复,帮助读者伙伴成长。 + +## 四、应对面试 + +为了更好的辅助星球的伙伴应对面试,小傅哥做了这样4个事情,做到一条龙服务🐲。包括;**【1-面试问题】**、**【2-简历模板】**、**【3-简历优化】**、**【4-上岸总结】** + +
+ +
+ +- 每早找到一份符合心意的工作就多赚一天钱,每多学习意义核心的技术都多一份求职的底气。 +- 而小傅哥的星球,带你直击这些内容,不让你浪费时间去瞎折腾。所以这是一笔非常好的投资! + +--- + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +1. 星球内的服务和实战项目都是小傅哥本人提供和**原创**,相信能够给大家带来**超过该价格的价值** 。举个例子,渐进式手把手带大家做**进大厂才可能看得见的项目**、有笔记有源码、有问题可以提,这比单独买一个课程或一套源码要值得多。其实都不到大城市一节补习班的钱,**哪怕把我的课程时长换算成培训机构的课时,也是便宜的超级多**。 +2. 持续的内容创作 + 回答问题 + 知识星球的运营(简历批阅、就业指导、架构设计) 需要小傅哥每个早上6点-8点以及周末/假期持续维护。也希望加入星球的同学都是真的下定了决心想要进步,而不是像免费的交流群和社区一样 “闲聊扯淡”。 +3. 希望加入进来的同学能够利用好星球来坚持学习。如果这个星球真的帮助你达成了目标(比如晋升加了薪、跳槽诺了坑、校招进了厂),回过头来你会发现,这绝对是你最值得的一笔投资 ! +4. 星球仍将随着人数和项目的增加会每次提价,感谢理解!—— 但已付费的加入的用户,续费折扣会很大,相当于只续费小傅哥的服务和新项目费用,没有什么比这更爽的了!**如果当年有人这样对我,我会买它个10年!** diff --git a/docs/md/zsxq/memorabilia/project-plan-v2406.md b/docs/md/zsxq/memorabilia/project-plan-v2406.md new file mode 100644 index 000000000..b59c191fb --- /dev/null +++ b/docs/md/zsxq/memorabilia/project-plan-v2406.md @@ -0,0 +1,71 @@ +--- +title: 24年6月,项目开发计划 +lock: no +--- + +# 你想看到小傅哥,卷哪个项目? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥! + +4年前,在 GitChat 出了第1个实战项目`《Netty + JavaFx,IM 仿桌面版微信》`,后与掘金合作出了`《SpringBoot Starter 中间件开发》`。而后在星球「码农会锁」陆续推出;`Lottery`、`API网关`、`OpenAI 应用`、`大营销`、`支付SDK`、`动态线程池组件`、`IDEA Plugin 插件`等,数十个项目的完成,也证明了小傅哥是这个行业的技术卷王,对,**是技术 + 卷王!** + +
+ +
+ +**小傅哥的项目,后劲真大!** + +因为工作久了,也经历过各种业务部门,还造过各种技术轮子。可以说这10年互联网,积累了丰富的业务场景经验和技术落地能力。所以我在做的,也是按照公司中一个项目组所需的各种项目,进行设计、开发和落地。让跟着小傅哥学习的伙伴,能学习到成体系的,完整的能力积累。 + +目前小傅哥的带着大家卷的方式是,`一个耗时较长的业务主线项目` + `一个陪跑的技术组件项目`。业务项目是综合技术和业务场景的解决方案处理,运用架构、设计原则、设计模式、高级编码技巧进行落地。而陪跑的技术组件,则是打一个技术深度和亮点。所以,学习起来既可以写简历面试,又可以在职场走到团队腰部以上(绩效、晋升都是你的)。 + +**那接下来,这些项目,你想先看到哪个?** + +>应用级的实战项目,有小傅哥的陪跑训练,才是快速🔜挺高能力的手段![https://gaga.plus](https://gaga.plus) + +## 项目列表 + +星球「码农会锁」,在进行的《大营销平台》项目已进入第3阶段,用户积分场景的设计和实现。完成后项目就要对接到 OpenAI 应用上,把两个微服务项目联合起来使用。并且在大营销进行时,小傅哥还带着大家做了一个《动态线程池》组件项目。 + +所以小傅哥又给大家计划,后续的业务项目和技术组件项目。组件项目会先行,与大营销第3阶段陪跑,6月份开启。那么接下来我会介绍一些业务和技术组件的开发,让大家投票看想选择哪个。 + +### 1. 业务项目 + +`*` 表示项目难度 + 代码量 + 开发周期。 + +- 【2星】问卷系统 - 技术点:动态列示属性设计,提供自定义配置和问卷收集。 +- 【3星】礼账系统 - 技术点:创建活动(升学、婚宴、乔迁)礼账单支付收款,完成分享和支付流程。 +- 【4星】优惠券系统 - 技术点:满减、直减、优惠试算,营销抽奖优惠券,支付优惠。 +- 【4星】阶梯拉新活动系统 - 技术点:通过邀请分享完成一些列动作,或者阶梯奖励,为系统获客提高便利性。 + +### 2. 组件项目 + +- 【2星】业务透视监控 - 技术点:通过执行数据采集,分析、计算、渲染,可视化业务流程图。 +- 【2星】工程方法链路地图 - 技术点:模仿 Sequence Diagram 采集工程执行链,通过 Marmaid 渲染出工程的类图、流程图等。 + +--- + +当你做过一个小傅哥的大营销或者其他项目以后,就知道虽然可能也有抽奖,但看过小傅哥的就知道,高级架构师写出的代码,是真TM优雅!😄 群友反馈:能学到的东西太多了! + +🤔 那么,接下来你想看到小傅哥先开发哪个项目,可以文章下留言 `给你想看到的项目进行排序`。*如果想看到其他项目也可以加入进行* + +>我们最宝贵的是有效的时间内,多学到一些开眼界、开思维、开能力的技术,而不是天天 CRUD! + +## 加入学习 + +🦋 一次加入星球「码农会锁」,可获得内容; +1. 5个业务项目,大营销、OpenAI应用、Lottery、IM、AI 问答助手 +2. 5个技术项目,动态线程池、支付SDK、API网关、SpringBoot Starter、IDEA Plugin +3. 3套基础教程,DDD 技术小册【上、中、下】 +4. 1套云服务器视频课,Linux、Docker、Portainer、MySQL、Redis、Nginx、Buddy +5. 4套技术小册,字节码、设计模式、面经手册、程序员数学 +6. 以及开源项目、架构方案、350份简历评审记录(对照着看完,在写简历,非常强!) + +--- + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) 大厂架构师的经验,原创内容的编写,广度、深度,内容的衔接性都是非常强的。这样的学习,必然是会让你得到不一样的成长。 diff --git a/docs/md/zsxq/memorabilia/seven-thousand.md b/docs/md/zsxq/memorabilia/seven-thousand.md new file mode 100644 index 000000000..f1408812f --- /dev/null +++ b/docs/md/zsxq/memorabilia/seven-thousand.md @@ -0,0 +1,72 @@ +--- +title: 2023年5月28日,星球7000人 +lock: no +--- + +# 7千人!—— “傅哥这种教学模式绝了!” + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +**风起于青萍之末,浪成于微澜之间**。承于风浪,一晃我已在职场10年,虽不曾积累太多的安世之道,但却磨砺出一身抗打的技术本领。常言说风浪越大,鱼越贵。那你我宁可成为兴风作浪之人,也不要成为案板之鱼。而这“兴风作浪”的本事,就是我想传承给你的技术,用我多年的积累,绘出一条明确目标的成长路线。 + +这也就是为什么很多学习完小傅哥技术项目的伙伴,都感叹:`“这死鬼,早点遇到就好了!”`、`“太TM爽了,代码质量真高”`、`“1年半、阿里P6+ 感谢傅哥”`... 接下来小傅哥就给大家介绍下到底是什么样的项目代码,能给大家送上岸,并在岸上能坐住! + +## 一、他们,斩获Offer + +5月份,不是金三银四,但5月份却有不少伙伴斩获Offer💐!找工作除了一点必备的运气外,再有就是硬核的技术实力和项目经历。而小傅哥的知识星球提供了6个实战项目,3个业务项目、3个组件项目。并且让你学习完任何一个项目,都能有非常多的技术方案和技术亮点可以拿出来和面试官聊。 + +
+ +
+ +因为星球项目较多,所以小傅哥也针对不同的人群做了项目的划分,你可以按照自己的诉求进行学习并组合到简历中使用,如;a【业务】Lottery、IM + b【技术】API 网关 + c【组件】SpringBoot Starter + d【创新】ChatGPT + e【开源】IDEA Plugin 插件发布到 IDEA 插件市场。 + +- 基础组合;a、c +- 中级组合;a、c、d/e +- 高级组合;a、b、e +- 超级组合;a、b、c、e【并有一定规模的下载量】 + +所以无论你是哪个阶段的求职诉求,都可以组合出自己所需的项目学习计划,同时在你编写完简历后,小傅哥还提供了简历优化服务,帮你更好的应对招聘。 + +此外在这些项目学习中,还有在校生小伙伴把星球里的项目 IM + ChatGPT-SDK-Java 进行组合,做了一个智能的AI场景机器人,可以充当`Java面试官`、`网络写手`、`心里咨询`、`案件分析`等角色。👍🏻 找工作简直牛皮的很! + +## 二、他们,兴奋Coding + +这么多年,小傅哥都在大厂从事ToC高并发场景的设计和开发,从最开始接触的大规模流量的,营销、活动、抽奖、量化、券中心、分摊结算再到监控、组件、平台,再到现金贷业务等。小傅哥几乎是把互联网中核心的系统都参与了。所以在做星球项目的时候,也都是基于这些积累进行代码设计和实现。 + +
+ +
+ +那么小伙伴在深入进入项目,学习这些内容的时候,就会感受到。架构、设计、编码、思路,都是那么的让人舒爽,也是那么的让人能学习到东西。小傅哥的项目不会有什么用 CRUD 来写核心逻辑,因为最核心的源码假如用 CRUD 代码编写,那可太难维护了,甚至可能出现一堆堆的事故。所以各个大厂也都在做技术项目的体系化建设,DDD架构和设计模式也都被高度重视。 + +### 1. 设计模式 + +
+ +
+ +把设计模式运用到实际的项目中,这是每个研发人员都需要锻炼的能力,而不只是用瓶瓶罐罐的例子学习设计模式。 + +### 2. 会话模型 + +
+ +
+ +会话模式是非常常用的设计结构,MyBatis 源码就是这样的结构。小傅哥会用很多的源码思想来设计工作中的系统开发,所以跟着小傅哥给你开的路,你也会学习到这些内容。 + +## 三、我们,邀你Join + +小傅哥的星球是以开发应用级实战项目为目标,公司需要什么样的技术,什么样的项目,我们就做什么样的内容。而这些课程内容只要加入星球就可以全部都学习到。**💐小傅哥的星球付费价格,你相当于只是买了1个课程的钱,但却可以获得6个项目+6个技术小册!** + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +**综上**,加入小傅哥的知识星球,这些内容都是你可以学习的。这些足够硬核的内容,可以帮助你更加平稳的度过往后的路,企业不会永远一帆风顺,但只要你手里技术硬,就会相对平稳。 + +**🍻路怎么走,让各位架构师自己选,祝大家在职场一帆风顺!** + diff --git a/docs/md/zsxq/memorabilia/sideline.md b/docs/md/zsxq/memorabilia/sideline.md new file mode 100755 index 000000000..1f4179c38 --- /dev/null +++ b/docs/md/zsxq/memorabilia/sideline.md @@ -0,0 +1,101 @@ +--- +title: 啊?小傅哥的技术"副业",已经发展到了海外! +lock: no +--- + +# 啊?小傅哥的技术"副业",已经发展到了海外! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +😱一觉醒来我的社群地域范围又扩大了,从国内扩展到了海外。虽然以前也有海外的伙伴,但基本都是过去留学的,他们知道国内的各类论坛资料和公众号推文,所以也更容易关注到小傅哥。但这回不一样啦,直接微信加上来了“歪国仁”:**“hi brother, can you chat by english. i'm not in china.”** + +
+ +
+ +**brother 我,给👬🏻兄弟们建了一个很全面的技术社群** + +做技术号主,做久了才发现,其实并不非得一直追热点,也不非得张牙舞爪的玩活宣传。而是踏踏实实的把技术做好,服务好每一个加入社群的用户,把他的问题当做自己的问题一样处理。就能形成良好的口碑,那么自然就有这些兄弟伙伴帮你做宣传。 + +- 高校老师👩🏻‍🏫:"去学小傅哥应用级别的项目经验,看看互联网公司都需要什么技术" +- 哈工大师妹:"如获至宝呀,终于不用再做烂大街项目了" +- C9 f硕:"组里师姐极力推荐您的知识星球,全面有细致" +- 小红书用户:"最近跟着小傅哥,原来真的会有大佬手把手教着写代码,规划学习路线,还能每天答疑解惑,甚至能教简历怎么写" +- linux.do 用户:"搜小傅哥,把上面的都学了,不敢说大厂,其他的你都能进" +- 知乎用户:"没有吹牛,不是托,那个 OpenAI 大模型和营销项目看的我头皮发麻,确实学到了很多的东西。" + +>还有来自海外的伙伴,也加入了小傅哥技术社群学习实战项目。@kirito、@arthurshby 👏🏻 + +
+ +
+ +
+ +
+ +按照这样的发展势头,以后要给 [bugstack.cn](https://bugstack.cn/) 做中英文两个版本了 😂。小傅哥社群的伙伴反馈说,我是来学习技术的,没想到还顺便学习了英文。 + +
+ +
+ +小傅哥的社群有不少清北、北邮、哈工大以及很多C9、211、985的伙伴,还有很多来自阿里、美团、字节、京东、腾讯,等互联网大厂公司中的伙伴,这些人伙伴很多都能做英文交流,还能分享来自大厂的技术。这个社群已经形成了正向循环,不断探讨和积累实用技术,这让加入的伙伴只要认真学习,都会有非常大的提升。 + +## 一、学习能力 + +小傅哥,非常重视社群内伙伴的学习培养,从基础入门到进阶提升,花费了数年编写了大量的资料。这些成体系的内容,能把一个Java编程基础小白,抬着进入到高级开发。可以说跟着小傅哥学习,你会节省非常多的时间。并且目标感十足,有小傅哥为你解答,你会清楚的知道学习多少能走到哪。因为小傅哥的发展,也基本是这个行业的普通人打工人天花板呢了。 + +
+ +
+ +另外,小傅哥截取了社群伙伴的学习分享,你可以看到正是因为社区内有良好的学习氛围,又有可以深入研究的课程,所以大家也才更有东西,也真的能学习到东西。 + +
+ +
+ +
+ +
+ + +>小傅哥的社群内有大量的技术积累,这里面搜索🔍的都是真实有价值的东西;大量的场景问题方案学习后就够面试讲的了,500多份简历评审记录就够为你做简历编写的蓝本了。 + +## 二、手把手教 + +就学习来说,只要你是个新人,就一定会遇到各种各样的问题。不少伙伴在加入前,给小傅哥说:"我是新人,我装环境可能都会遇到问题,可以问你吗"。当然没问题,你加入我的社群,这都是我应该服务你的。不会你加入后,我就不管了,那还是人吗! + +所以,无论是编程环境问题、业务理解问题、编码实现问题,都有小傅哥一次次解答的身影。还专门为场景问题建立 Q&A 帮助大家快速排查处理。如;[https://bugstack.cn/md/project/big-market/qa.html](https://bugstack.cn/md/project/big-market/qa.html) + +
+ +
+ +- 类似这样的问题排查解答,星球里有非常非常多。主要就是帮助伙伴排查问题代码,引导学习并提高解决问题的思维。 +- 因为小傅哥的经验是非常清楚的知道的,编程这东西是过程经验积累学习,不是结果答案学习。错误是千千万的,但排查错误的过程方式是固定的。所以要学习根本的东西。那么这样一次次引导,慢慢也就积累了解决问题的能力。 + +## 三、找到工作 + +跟着小傅哥学习,不会浪费时间,不会走小道弯路。全程做技术兜底,遇到的各种问题都能帮你解决。包括你学习时候的代码bug,可以把代码提交到星球,我来帮你调试。最终把兄弟们送到各个竞争赛道的头部,拿到最牛的薪资待遇 Offer! + +
+ +
+ +## 四、实战项目 + +这一年,干了近900万字!包括;小傅哥的社群干了一个大营销项目、一个小型支付、4个技术组件,还有一系列的技术文章和回答大家的技术类问题等。**更新频次嘎嘎高,知识积累相当大!** + +
+ +
+ +>🧧[加入学习](https://bugstack.cn/md/zsxq/other/join.html) diff --git a/docs/md/zsxq/memorabilia/student-offer.md b/docs/md/zsxq/memorabilia/student-offer.md new file mode 100644 index 000000000..fece7cf8d --- /dev/null +++ b/docs/md/zsxq/memorabilia/student-offer.md @@ -0,0 +1,112 @@ +--- +title: 群聊截图!校招能拿到40W年薪Offer,不是没道理的! +lock: no +--- + +# 群聊截图!校招能拿到40W年薪Offer,不是没道理的! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +在陆续的助力星球伙伴学习项目,拿到百度、字节、美团、京东、腾讯、饿了吗、快手等Offer后,我越来发现,只要把大家的能力积累,稍稍拉倒新手村之上,接近于实际工作经验,那么面试就会有非常强竞争力。而在新手村内的面试,就很卷了,因为拉不开差距。 + +在学编程的路上,其实每个人都要走出新手村,踏上技术飞跃的旅程。因为 CRUD 的`XXX管理系统`经验的积累,并不会拿到什么好的 Offer 也赚不了多少钱。但往往很多伙伴,在基础技术积累到高级进阶阶段要花费几年时间走出去,一方面是是缺少指点和眼界,另一方面是没有对应的可成长的资料。往往是自己周围环境越差,自己也跟着越往下,所以要学会跳出包围圈! + +
+ +
+ +**我选择做了一件很慢的事!** + +我看到过网上有很多的学习路线指引,或者网盘几个T的连接资料,以及一些付费教程也是拼凑的内容。在我最开始学习编程的时候也收藏过这类资料,但往往是看不进去,因为这些资料很多是过时的,甚至是错误的,少量的一些对的资料也没有衔接性,其实学习起来非常浪费时间,收获甚微! + +所以我选择了慢下来,用10年的编程经验,成体系的输出各项经验,包括;数据结构、设计模式、分布式技术栈、应用项目开发等。这样做虽然很耗时,但我可以保证每一个学习我资料的伙伴,问到任何一个点的时候,我都能给回答出来,这样是对自己和他人的负责! + +这样的内容创作虽然花费了大量的时间和精力,但我也看到了大批伙伴的快速成长!心里美滋滋的! + +>文末提供了获取整个学习内容的链接🔗,涵盖;数据结构、算法、设计模式、专业技能、实战项目等。 + +## 一、聊天记录 + +要想知道一个社群能不能学到东西,那就得看看群里这群人都聊啥呢。有没有让你一看到就兴奋的技术点,又没有让你一听到就开心的知识项。如果能不断的打开技术的事业,开眼界,那么就非常值得加入。接下来要**暴图啦!** + +
+ +
+ +
+ +
+ +
+ +
+ +从聊天截图就可以看出,大家的学习热情非常高&收获嘎嘎哒,也能看出对项目和小傅哥的认可。这是因为大营销这样的实战项目,串联了非常全面的流程,每个功能点都做了非常强的设计实现,而且是渐进式的迭代,这样就能让大家对比出工程代码都是一个怎样的优化过程,学习起来也能吸收的更多。—— 如果都是 CRUD 项目,断然是聊不出东西的! + +>小傅哥花费了很大的精力维护技术社群,除了有日常的技术群,还有星球伙伴专门的VIP群和每个项目的项目群,让一期学习的伙伴,可以同步交流成长。 + +## 二、学习路线 + +小傅哥的星球是`成体系+原创`的打造技术学习内容,让加入的伙伴既掌握扎实的基本功底,也能在高阶进阶阶段游刃有余。—— 没有好的技术底子,后面的技术路也就很困难了。只会用API。 + +
+ +
+ +这是小傅哥给大家搭建的小白成长路线图,所有的内容都是小傅哥凭借着在大厂十年来的经验编写,所以在学习这些内容时你会感受到那些高级的思维和硬核的编码。 + +**3条举例;** + +- 第2点的设计模式学习,我会在项目中真真切切的运用起来,让你感受到设计之美。 +- 专业技能里的各项技术栈,都是来自于实战项目中一个个小小的独立案例的拆解,让你通过小而美、小而精的技术点,把自己的专业技能补全。而且这里的小技术点同样会在项目中有所体现。 +- 到了项目,会分为业务项目、技术项目,让你学习后简历内容更加丰富,综合实力更强。 + +
+ +
+ +>按照这套路线学习,其实就是打将来,可以冲击中大厂做准备。谁也别指望在一个公司干一辈子,总得要自己提升自己! + +## 三、读者学习 + +实际工作的人才知道,绝大多数做业务开发的程序员进入职场后,`算法`、`计网`、`操作系统`,都和自己的工作没关系。而有关系的,能晋升、能涨薪的,是`核心业务的经验积累`、`庞大规模的架构设计`、`复杂场景的解决方案`,还有画图能力。所以这些内容也是我在星球培养高阶研发的重点,因为这些大部分内容都录制了非常全面的视频,所以即使是小白也能跟进学习。 + +很多时候不是一个知识多复杂,多难理解,而是没有接触过。如果早点在这些方面打开视野,那么会对自己的职业生涯非常有好处!举例,星球伙伴的作业,学习大营销做的项目笔记,就知道锻炼的多牛! + +### 1. 球友ID:狗子我大哥 + +
+ +
+ +连接:[https://t.zsxq.com/sNtmO](https://t.zsxq.com/sNtmO) + +### 2. 球友ID:kitie + +
+ +
+ +连接:[https://t.zsxq.com/IgO4m](https://t.zsxq.com/IgO4m) + +--- + +以上两个球友的总结都是星球内大营销项目的全链路梳理,从图中就能看出项目的全面性有多好,用到了哪些设计模式。而且图做的都非常漂亮 👍🏻除此之外还有;`进击的程序员`、`以雷霆击碎黑暗`、`灰喵` 等在这个项目上都贡献出了非常好的笔记。—— 互相学习,互相讨论,进步更快! + +## 四、加入学习 + +> 我用工作10年+的经验告诉你,一个好的Offer起步,至少可以避免花费3年时间走弯路! + +星球「码农会锁」的这种公司级别的实战项目,全程是需求分析、架构设计、几十张表的把控、众多场景设计模式的解决方案和高级编码的技巧运用,都是让你拉倒面试竞争力的资本。—— 当你不在做 CRUD 的 demo 项目,你会发现自己的技术是质的改变,提升的非常快! + +在今年的面试中,星球帮助众多伙伴拿到`微信支付`、`京东科技`、`度小满`、`蚂蚁金服`、`Lazada(电商优惠营销)`、`快手`、`美团到店`等Offer,还有的校招生薪资最高年包到45w,这个级别已经超过工作4年-5年的伙伴了!如果学历那么高,但技术不错的,也能拿到中厂Offer。所以说,一个好的Offer起步,真的可以少走3年的弯路! + +> 这样成体系的全量项目学习,放在一些平台售卖,一个内容都要上千块。但小傅哥的星球,只需要100多,就可以获得大厂架构师对你手把手教学! + +🧧 [点此领优惠券加入星球](https://bugstack.cn/md/zsxq/other/join.html) diff --git a/docs/md/zsxq/memorabilia/ten-thousand.md b/docs/md/zsxq/memorabilia/ten-thousand.md new file mode 100644 index 000000000..a10d75fe0 --- /dev/null +++ b/docs/md/zsxq/memorabilia/ten-thousand.md @@ -0,0 +1,149 @@ +--- +title: 2023年12月24日,星球10000人 +lock: no +--- + +# 小傅哥知识星球好吗,值得买吗? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +13年毕业,10年+的互联网大厂工作经历,T8级架构师。从事过`交易`、`信贷`、`账务`、`营销`、`平台`、`中间件`等各类场景的架构设计和编程实现。—— **💐 这就是我能给各位创建一个技术社群「星球:码农会锁」的技术根基!** + +
+ +
+ +小傅哥的技术社群,是以实战应用级编程项目为主线,附带提供项目开发过程中所需的碎片化技术栈案例和小场景应用锻炼,这样即使是编程小白伙伴也能通过一个个案例快速上手学习并完成项目开发。此外星球还提供了`架构方案`、`开源项目`、`源码学习`、`应对招聘`、`简历优化`和`职场经验`等内容,来全流程的帮助加入星球的伙伴目标清晰且明确的成长,积累自己的技术栈体系。 + +>星球「码农会锁」目前有5个业务项目、3个组件项目,皆为小傅哥基于互联网大厂所需,原创的应用级实战项目。这样的东西不是拼凑,也不是CRUD,所以学习后无论还是面试还是工作中,都有非常大的价值。项目演示平台:[https://gaga.plus](https://gaga.plus) —— 你可以进入看到这些项目演示和介绍。 + +
+ +
+
关注小傅哥的公众号【bugstack虫洞栈】回复【星球】可以领取专属优惠券
+
+ +## 一、星球项目 + +在我所面试招聘和与众多粉丝伙伴的交流中了解到,很多伙伴的简历项目往往是套了个很大的场景壳子,但每个细节的业务流程实现都是很缺失的。或者压根就没有好的项目、也没有核心的流程模式设计,能写到简历撑场子。 + +其实有些东西并不是有多难,但只要没见到过,没打开过这份心智和视野,就很难积累出这份能力。所以加入小傅哥星球学习项目,可以撇开XXX管理系统、XXXCRUD流程实现,而是大量🔜见识到大厂中项目的架构设计、编码原则、高级技巧。 + +
+ +
+ + + +这些项目每一个都是从0到1的带着你一步步渐进式的分析需求实现功能,所以这样的过程会让你吸收到大量的设计思考和编程技巧。再者因为小傅哥一直在大厂也从事着大量的开发,在复杂的项目中迭代需求,所以我也能把这样的思想通过星球的项目锻炼让你逐步积累出这些实践技能。 + +>🧧 文末领优惠券加入星球 + +## 二、学习指导 1v1 + +星球内包括已经工作几年的大佬,也有刚毕业的应届,还有在校的学生伙伴。但无论是复杂的场景设计问题,还是小白的编程学习问题。都可以在星球发帖,所有的问题,我都会进行一一解答,帮助大家成长。 + +### 1. 工作选择 - 建议 + +
+ +
+ +### 2. 技术问题 - 指导 + +
+ +
+ +### 3. 公司选择 - 帮助 + +
+ +
+ +### 4. 问题解答 - 归档 + +
+ +
+ +### 5. 学习路线 - 指引 + +
+ +
+ +> 星球内这样的解答、建议、指导,有1.2万+条,专项回答1.6k+条。我用着自己在大厂10年的工作经历,帮助伙伴们成长。加入小傅哥的星球「码农会锁」,你就比别人更快一步积累个人的技术成长。 + +## 三、简历编写 + +星球内提供了简历优化&评审服务,线上+线上共给300+份简历做出优化指导,这也是日常我们在检索简历,面试时所积累下的经验;`什么样的简历让更容易进入面试`、`简历上的项目怎么编写更能体现出你的技术积累`。 + +### 1. 实习简历 - 优化 + +
+ +
+ +### 2. 校招简历 - 优化 + +
+ +
+ +### 3. 社招简历 - 优化 + +
+ +
+ +> 在星球中已经有在线的200份真实简历的优化评审建议,单单只看完50份简历和评审,都可以很好的写出自己的简历。尤其是很多伙伴的项目是有东西的,但缺少非常专业的技术术语从而没法专业的表达,那么就非常适合来让小傅哥帮你做简历评审指导,这样你的简历会更加具有竞争力。 + +## 四、学习氛围 + +以前你的学习可能是孤军奋战,容易疲惫。一个正确可用的资料,甚至都要找好久好久,更别说能看到非常有价值的技术方案了。但在星球「码农会锁」中,是有完整的成体系的资料、视频、小册、文档,还能与一群专门学习技术的伙伴交流。 + +### 1. 技术社群 + +
+ +
+ +### 2. 优秀作业 - 学习经验 + +
+ +
+ +### 3. 课程视频 - 带你开发 + +
+ +
+ +>星球内的伙伴可以加入VIP技术交流群,还可以进入每个项目的专属项目群。在这个过程中吸收项目经验,学习各个大佬的编程思维和开发技巧。此外在新项目中加大了视频带着写代码的部分,让读者可以知道小傅哥是如何编写代码的。—— 嘎嘎牛批! + +## 五、加入星球 + +最后,欢迎👏🏻伙伴们加入小傅哥的星球「码农会锁」,与1万+的伙伴们一起开卷! + +星球现在的价格为~~159~~元(-20)一年,老用户第二年续费是价格为5折。首次加入即可获🉐星球所有的项目和学习资料,第二年的续费只相当于续费新项目和我的服务。嘎嘎实惠!**可以说,你买小傅哥10年,都不到1000元!但却可以获得他全部的在大厂的积累的各种能力!** + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +伙伴说:“加入小傅哥是这一年最正确的✅的决定!” 因为我让我很多伙伴提升了技术、卷到了Offer、晋升了职级,获得了更多的收入! + +
+ +
diff --git a/docs/md/zsxq/memorabilia/xiaofuge-team.md b/docs/md/zsxq/memorabilia/xiaofuge-team.md new file mode 100644 index 000000000..f2aff8846 --- /dev/null +++ b/docs/md/zsxq/memorabilia/xiaofuge-team.md @@ -0,0 +1,90 @@ +--- +title: 加入小傅哥 = 加入大厂一个项目组? +lock: no +--- + +# 加入小傅哥 = 加入大厂一个项目组? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**🌻 19年7月开始 - 至今,5年多时间**,一心搞技术、做项目、写推文。现在的小傅哥已经可以匹配互联网大厂中,一个`开发项目组`所积累的内容体量了。在小傅哥的这套成体系的知识内容下学习,完全可以达到从小白到高级开发工程师的进阶! + +
+ +
+ +**小傅哥都做了哪些内容?** + +依照于互联网公司所需的各项内容,小傅哥编写了;数据结构、应用算法、设计模式、系统架构、开发技术、工程测试、质量监控、Dev-Ops,在到实战项目,包括;6个业务项目、7个组件项目、手写源码。一步步的带着伙伴们跟着小傅哥从0到1的成长。这其中还涵盖了小傅哥的成长经验、晋升经验、架构经验,可以说是作业已经准备好了,你照着写就行了。 + +
+ +
+ +与网上拼凑的内容不同🙅🏻‍♀️。这些各项知识,全部都是小傅哥一行行敲代码,一句句写文章,这样即保证了内容的新鲜度,同时还具有非常强的学习连贯性。如,前面学习的系统框架、开发技术,在后面的实战项目中就会全部体现出来,并且会加深在实际场景的运用技巧。 + +> 文末可获得全部的学习内容,还有🧧优惠券可以使用。**先到先得!** + +## 一、编程基础 + +### 1. 数据结构 + +
+ +
+ +### 2. 应用数学 + +
+ +
+ +### 3. 设计模式 + +
+ +
+ +### 4. 八股面试 + +
+ +
+ +## 二、实战项目 + +
+ +
+这些实战项目在你加入小傅哥社群后,全部都可以学习。星球的内容也是永久可看的,即使你加入后过期了,在过期时间前的内容也都是可以看的。那么,你可以想下,你现在加入,就是获得过往所有的和一年内创作的新的。嘎嘎爽! + + +## 三、学习社群 + +
+ +
+ +
+ +
+ +
+ +
+ +星球「码农会锁」,选择了最耗费小傅哥精力的方式,建立各个项目的社群。让每个伙伴都能快速的学习交流。虽然这样比较费小傅哥,但大家的学习成长是有目共睹的! + +## 四、面试能力 + +
+ +
+ +小傅哥的星球会为每个加入的伙伴,根据自身的自我介绍提供专属的学习路线。星球「码农会锁」内的知识量很大,项目也很多。我会为你提供一个适合的学习组合,让你更加容易快速提高自身能力。 diff --git a/docs/md/zsxq/other/join.md b/docs/md/zsxq/other/join.md index 4b2c81421..16dc38e31 100644 --- a/docs/md/zsxq/other/join.md +++ b/docs/md/zsxq/other/join.md @@ -5,24 +5,24 @@ lock: no # 加入星球 -星球价格 **¥129** 一年,老用户续费 **5折** 一年(星球每年都会开发新的学习项目和技术小册等资料)。 +星球嘎嘎实惠,星球一直不断的开发新的学习项目和技术小册等资料! -优惠名额有限、先到先得,微信扫描下方二维码领券加入:加入后阅读[使用指南:🔜快速了解,开启学习之旅!](https://bugstack.cn/md/zsxq/material/guide.html) - ->加入 3 天内可以全额退款,感兴趣的同学可以先加入体验,自己判断是否有价值。 +>感兴趣的同学可以先加入体验,自己判断是否有价值,不满意可退出。
- +
关注小傅哥的公众号【bugstack虫洞栈】回复【星球】也可以领取专属优惠券

-🌹加入后,这些内容都是你的,**这片鱼塘**都给你了!—— 目前已有3800+伙伴在星球学习! +🌹加入后,这些内容都是你的,**这片鱼塘**都给你了!—— 目前已有1万+伙伴在星球学习!
- - + +
+
关注小傅哥的公众号【bugstack虫洞栈】回复【星球】也可以领取专属优惠券
+
**注意**:加入星球后,阅读`星球🔝置顶消息` [https://t.zsxq.com/047EQfQfY](https://t.zsxq.com/047EQfQfY) diff --git a/docs/md/zsxq/project/ai-agent-scaffold.md b/docs/md/zsxq/project/ai-agent-scaffold.md new file mode 100644 index 000000000..6c9c050fd --- /dev/null +++ b/docs/md/zsxq/project/ai-agent-scaffold.md @@ -0,0 +1,295 @@ +--- +title: AI Agent 脚手架 + 场景应用 +lock: no +--- + +# 《AI Agent 脚手架 + 场景应用》- 综合 Spring AI、LangChain4j + Google ADK(a2a、mcp、skills),打造全新智能体架构方案。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
项目:[https://t.zsxq.com/a8AJj](https://t.zsxq.com/a8AJj) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +💥炸了!**日更,日更,接下来这套新项目,即刻开启日更!** 是的,小傅哥又一个新 AI 项目来啦。可以说,现阶段 AI 是每个工程师都必备的技术技能。 + +从 `RAG` 到 `MCP`、`A2A` 再到 `Skills`,一系列的 AI 编程技术技能陆续迭代更新, +各个互联网企业也随之跟进开发自身的 AI Agent 智能体,解决业务场景问题。 + +因此你在 Boss 直聘,校招/社招,都能看到大量的 AI 应用开发岗位。`AI 没让程序员工作丢失,反而多了更多的岗位!` + +
+ +
+ +新项目,📅于25年12月初启动,26年2月底完结,将近耗时90天打造精品企业级智能体解决方案项目。 + +在小傅哥社群,`OpenAI 代码自动评审做了`、`OpenAI 应用 + 扫码登录 + 微信支付 + 敏感词过滤 + SDK 开发做了`、`AI Agent 可视化编排也做了`、`AI MCP 网关深入理解 MCP 协议还做了`! + +那为什么还要做一个 AI Agent 呢?🤔🤔🤔 + +因为 **【AI Agent 可视化编排】** 解决的是横向通用性场景方案,而本次要做的 **【AI Agent 脚手架 + 场景方案】** 是纵深业务场景细化方案(也是企业里必备的架构设计,针对细化场景,通用的编排就失效啦!)。`这些东西,不是个架构师带着你,那你就根本理解不到!` + +好,该项目为大家带来一套企业级 AI Agent 智能体脚手架底座及场景应用方案的实战编程项目。项目采用 `Spring AI`、`Langchain4j(对照学习)` + `Google ADK`(a2a 框架 + 工作流编排),融合 `MCP`、`Skills`、`Plugin`、`Session` 等多项智能体开发技术,构建通用智能体脚手架工程。 + +并借助该脚手架,用户能够快速高效地搭建各类智能体应用场景。就像本次项目,既带着你搭建脚手架,也带着你使用脚手架做场景用(价值超级大,还要带着你做手机版大龙虾 MobileOpenClaw 🦞!)。 + +
+ +
+ +**💻 每个工程师,都需要AI应用编程技能!** + +竹外桃花三两枝,春江水暖鸭先知🦆。没有哪个行业,有程序员👨🏻‍💻可以这么快的接触到世界的科技变化。甚至也只有程序员行业,可以驾驭 AI 技术,做出各项 AI 应用软件。`AutoPhone`、`OpenClaw`,码农正在加速改变这个世界! + +> 当前互联网企业,Java 有庞大的市场,如美团、京东、阿里、饿了么、滴滴等。这些业务都要大量的引入 AI 进行提效,如;客服、巡检、运营、监控等。因为这些业务本身也都是基于 Java 构建,背靠 Spring 框架。所以很多公司也就天然的选择了 Spring AI 框架开发智能体应用。包括;阿里还基于 Spring AI 做了 alibaba 版本,谷歌也做了 google adk 框架。现在你懂了,为什么我们选择 Spring AI 实现业务智能体项目了吧!当然,这些框架也都类似,选择一套学习后,其他的也都很容易上手。 + + + +>🧧 文末提供了本套项目的完整工程代码,此外还有其他的业务项目 + 组件项目,共计20个全部可以获取。 + +## 一、我能学到什么 + +首先,这是一整套从0到1,文档 + 视频 + 源码,包含前后端 + DevOps 的综合实战项目。带着大家进行需求分析、底座构建、脚手架设计、应用场景实践。所以,你可以非常完整的学习到关于 AI Agent 智能体的全部内容,让你具备企业级项目开发能力。 + +- 【后端】熟练 Spring AI、Langchain4j(对照学习)框架的使用知识,掌握 api、model、client 的组装构建。 +- 【后端】深入 Spring AI 框架,使用 spring-ai-community 包,引入 Agent Skills 技能。 +- 【后端】设计多种 MCP 加载策略,满足 local、sse、stdio 各种类型的加载操作。 +- 【后端】运用 Google ADK 框架,整合 Spring AI、Langchain4j 分别验证学习,做好技术调研。 +- 【后端】使用 Google ADK 框架,通过提供的 loop(循环)、parallel(并行)、sequential(串行),构建多样性智能体。 +- 【后端】通过 Google ADK 框架,掌握 Runner 运行插件机制,掌握智能体运行中各个节点的数据采集和控制操作。 +- 【后端】设计通用智能体配置 yml 文件,通过配置文件的内容编排,可以配置出复杂的智能体。 +- 【后端】深入 Google ADK 源码,调试源码找到bug,并提供解决方案。已经在google adk 发布了[issue #705 - 小傅哥为 Google ADK 框架,提交的 issue 记录](https://github.com/google/adk-java/issues/705) +- 【后端】拓展设计模式的使用,在智能体构建中,使用规则树模式进行各个节点的编排。编排依赖于 agent.yml 文件的配置。这是非常灵活的设计。 +- 【后端】基于 Maven 脚手架构建方式,对底座工程创建出通用脚手架项目。 +- 【后端】积累 Netty 通信技术,基于 Netty 构建通信网关。 +- 【前端】安卓 Android(Kotlin) 网关终端开发,做智能设备控制。`基于 AutoPhone 9B + OpenClaw 理解,构建 MobileOpenClaw` +- 【前端】使用 React 构建前端工程 + draw.io,实现出一套智能体绘图操作。这部分会结合 AI IDE + prompt 进行编程实现。 +- 【运维】在云服务器环境(Ubuntu 24)安装 Docker 环境 + Protainer 管理面板,以及初始化环境等(提供了一件安装脚本)。 +- 【运维】分别对前后端进行 docker 镜像构建,以及在云服务器上完成项目的部署操作。 +- 【其他】积累应用设计经验,面向对象开发,在整个工程实现中,都有非常干净,清晰,具备高内聚,低耦合,有单一职责的逻辑体现。 + +>小傅哥带着你做的是企业级项目架构和技术积累,通过这些东西的学习,在面试中与面试官交流,才会显得更为专业。 + +## 二、适合哪些伙伴 + +- 需要快速🔜写到简历(每个阶段完成都可以写简历),用于秋招/社招面试(本项目可快速部署验证结果)。 +- 对 AI Agent 智能体感兴趣,但不知道如何自己实现一套的。 +- 希望提高自己的架构设计思维,设计模式运用的。 +- 增强核心竞争力,储备一些非业务的核心技术类知识的。 +- 需要掌握 Spring AI、Langchain4j、Google ADK 框架使用。 + +## 三、项目是否硬核 + +很多小伙伴都害怕学习到一个`(前端)外壳漂亮`,`(后端)代码水货`的项目,满是 CRUD 缺少架构设计,也没有编程思维的体现。这样的项目,在面试后端工程师的时候,很难讲出东西。所以,这里小傅哥先把一些核心的架构设计给大家看看,让大家知道小傅哥带着你学习的东西质量如何。 + +### 1. 智能体整体设计 + +
+ +
+ +- 2025年11月27日,Google 正式在 Maven 仓库管理中心,推送了 0.4.0 版本 ADK,该版本新增加了 Spring AI 的集成。[google-adk-spring-ai](https://central.sonatype.com/artifact/com.google.adk/google-adk-spring-ai) 至此,也因此,小傅哥决定基于这套服务组合,设计智能体脚手架。 +- 首先,Google ADK 是一个智能体框架,他自身也是支持直接对接各类大模型的 API,以及构建 ChatModel 的。但在整合 Spring AI、LangeChain4J 以后,Google ADK 的使用,将会得到已经使用上述组件的公司更大的青睐。 +- 之后,Spring AI 解决的 AI 对接的前半部分,让你可以把 AI API、Model、Prompt、RAG、Tool(Function、MCP)等,非常方便的构建出一个单一的 AI Agent 服务(也可以称之为是一个客户端)。 +- 然后,Google ADK 解决的是,多个 AI Agent 怎么协同工作的问题。这里包括,Sequential 序列顺序执行、Loop 循环执行、Parallel 并行执行,而这些执行方式,又可以组合搭配的配置到一个 Sequential 中进行顺序执行(注意图中颜色)。绿色的是大模型服务,绿色部分可以被深黄色或者浅青色包装,之后在组合到 SequentialAgent - 序列执行中。 +- 最后,Google ADK 提供了记忆上下文 Runner 执行器(也可以自己扩展实现),在这里又提供了钩子插件,你可以对执行过程中的流程,进行拦截。这个过程类似 Spring 容器中对 Bean 对象的处理,before、after 的过程。 + +### 2. 系统的分层结构 + +如图,整体简要架构设计(剥离其他流程,方便理解); + +
+ +
+ +整个应用架构分为3层,包括;基础底座、脚手架、业务场景; + +- 基础底座,负责整 Spring AI + Google ADK 框架的使用,这里的重点在于整个智能体工作流程的设计和使用(第2部分20节课程)。 +- 脚手架,将基础底座使用 maven 抽取出脚手架,脚手架可以让我们快速复刻出一套基础工程(第3部分3节)。 +- 业务场景,结合 draw.io 绘图操作 + ai agent 智能体,做一套AI交互式画图系统(第4部分6节)。 + +### 3. 底座的运行流程 + +
+ +
+ +- 首先,从用户基于脚手架创建完成后,在使用 YML 文件进行智能体的配置,之后在启动项目后,会进行一些利的装配。api、model、agent、workflow、runner,再到 spring 容器。 +- 其中,关于智能体的工作流组装是非常巧妙的,可以自由组合出多种类型智能体。这部分不需要硬编码即可完成。 +- 最后,是整个内容装配完成后,提供了通用的接口能力可以进行对话。 + +### 4. 脚手架配置发布 + +
+ +
+ +- 左侧,对现有工程使用 maven-archetype-plugin 插件,构建工程脚手架。将当前的工程打包成一个可复用的 Archetype 模板。 +- 中间,打包好的脚手架,可以在本地直接使用,也可以发布jar到私服,让大家都可以使用。私服部分,后续在做处理。 +- 右侧,使用方可以基于命令,或者 IntelliJ IDEA 配置 Maven 脚手架的方式,创建和启动工程。这一节,我们先通过命令的方式使用。 + +## 四、应用场景举例 + +### 1. 普通对话 + +
+ +
+ +- 智能体搭建后,可以进行对话操作,基于你配置的 MCP 能力,它可以做很多事项。 + +### 2. draw.io + 画图 + +
+ +
+ +```java +agents: + # 1. 需求分析与检索智能体 + - name: agent_analyst + description: 负责理解用户意图,调用工具检索信息,并决定是请求补充信息还是继续绘图。 + instruction: | + 你是一个专业的需求分析师。你的任务是分析用户的绘图请求。 + 1. 如果用户提供了具体的上下文或需要引用外部知识(如Git仓库、本地文件),请使用可用的工具(MCP)进行检索和分析。 + 2. 分析用户的意图: + - 如果用户的描述模糊、不完整,无法直接生成图表,你需要返回 JSON 格式要求用户补充信息。 + 格式:{"type": "user", "content": "请补充关于...的具体信息"} + - 如果用户意图清晰,请整理出详细的绘图需求(图表类型、节点、关系、布局要求等)。 + 3. 输出你的分析结果。 + output-key: analysis_result + # 2. 绘图执行智能体 + - name: agent_drawer + description: 根据分析结果生成 Draw.io 的 XML 数据。 + instruction: | + 你是一个 Draw.io 绘图专家。请根据输入 {analysis_result} 进行操作: + 1. 如果输入是 {"type": "user", ...},请直接原样输出该 JSON。 + 2. 如果输入是详细的绘图需求: + - 设计图表的结构(UML、流程图、时序图等)。 + - 生成符合 Draw.io 规范的 XML 代码。 + - 确保节点布局合理,逻辑清晰,连线不能交叉等。 + - 输出生成的 XML 内容。 + output-key: draft_diagram + # 3. 检查与优化智能体 + - name: agent_reviewer + description: 检查绘图结果,确保无连线交叉等问题,并格式化最终输出。 + instruction: | + 你是一个图表质量检查员。请审查输入 {draft_diagram}: + 1. 如果输入是 {"type": "user", ...},请直接原样输出。 + 2. 如果输入是 XML 代码: + - 检查连线是否混乱或有严重的交叉(在文本层面尽力优化布局逻辑)。 + - 检查 XML 语法是否正确。 + - 如果有问题,请尝试修正 XML。 + - 最终输出必须严格符合 JSON 格式: + {"type": "drawio", "content": "这里放最终的XML字符串"} + output-key: final_result +``` + +- ai agent + draw.io,可以配置出一套交互式绘图智能体。我们可以把诉求发给 AI,之后 AI 进行分析和决策,让用户补充信息或者直接画图。 +- 在大量的测试和体验中,这套智能体 + gpt 5.1 可以绘制出非常符合企业中真实场景的流程图,效果还是非常不错的。如果你还配置 mcp 可以结合本地代码库,文档库,产品PRD库,那么它还可以更好的绘制出相关的流程图。 + +### 3. AutoPhone 实验性场景 + +智谱发布过一个 [Open-AutoGLM](https://github.com/zai-org/Open-AutoGLM) 类似于豆包手机,可以通过指令发送 AI,AI 操作手机完成一系列动作。目前官网这套产品目前使用的是 ADB 连接手机,数据线调试方式。 + +- 文档(手机 + Agent):[https://bugstack.cn/md/algorithm/model/autoglm-phone-agent.html](https://bugstack.cn/md/algorithm/model/autoglm-phone-agent.html) +- 官网:[https://github.com/zai-org/Open-AutoGLM](https://github.com/zai-org/Open-AutoGLM) + +这里小傅哥在体验了 OpenClaw 大龙虾的设计后,对 AutoPhone 也有了想法。我们可以设计一套安卓版的手机 MobileOpenClaw,在手机端开发一个网关,网关功能具备;`启动应用`、`点击指定坐标`、`输入文本`、`滑动屏幕`等。之后在让 AI 以借助 Socket 通信,对手机设备进行管理。 + +
+ +
+ +- 首先,需要实现一套 MobileOpenClaw 的网关,这部分内容是安卓开发的一个软件,如果 IOS 也还有其他方案。可以在 Github 检索相关资料 [https://github.com/search?q=phone%20agent&type=repositories](https://github.com/search?q=phone%20agent&type=repositories) +- 之后,基于脚手架,开发 MobileOpenClaw 智能体,这部分要通过 Socket 和 手机端进行通信。让 AI 识别用户意图,控制手机端执行相关操作。因为这里大量的视觉识别,所以 gemini-3-pro-preview 效果不错,另外就是 GLM 定制的 [AutoGLM-Phone-9B](https://github.com/zai-org/Open-AutoGLM) 模型,可以自己在 GPU 部署。 + +## 五、课程学习目录 + +全程`视频` + `文档` + `源码`,开局 IntelliJ IDEA + Webstorm + Android Studio,手把手带着你一路狂飙! + +
+ +
+ +>以下2、3、4部分,每部分做完,都可以写简历,也就是最早学习完2部分20节,就可以写一份简历啦! + +### 介绍 + +[AI Agent 脚手架 + 场景应用](#) - 综合 Spring AI、LangChain4j + Google ADK(a2a、mcp、skills),打造全新智能体架构方案。 + +[面试:技能、简历、问题汇总](#) + +### 第1部分:需求与架构 + +- [第1-1节:脚手架需求分析](#) +- [第1-2节:系统架构设计](#) + +### 第2部分:基础底座开发 + +- [第2-1节:工程初始化创建](#) +- [第2-2节:Api功能测试](#) +- [第2-3节:智能体配置表设计](#) +- [第2-4节:装配域结构化定义](#) +- [第2-5节:装配域节点-AiApiNode](#) +- [第2-6节:装配域节点-ChatModelNode](#) +- [第2-7节:装配域节点-AgentNode](#) +- [第2-8节:装配域节点-AgentWorkflowNode](#) +- [第2-9节:装配域节点-Loop、Parallel、Sequential](#) +- [第2-10节:装配域节点-RunnerNode](#) +- [第2-11节:智能体加载使用验证](#) +- [第2-12节:增强装配-RunnerNode](#) +- [第2-13节:增强装配-AgentWorkflowNode](#) +- [第2-14节:增强装配-本地mcp](#) +- [第2-15节:增强装配-回调plugin](#) +- [第2-16节:fix-多模态能力使用](#) +- [第2-17节:会话服务接口实现-service](#) +- [第2-18节:会话服务接口实现-trigger](#) +- [第2-19节:会话服务接口对接-ui](#) +- [第2-20节:增强装配-skills](#) + +### 第3部分:脚手架工程化 + +- [第3-1节:Maven脚手架配置](#) +- [第3-2节:上传jar到maven仓库](#) +- [第3-3节:部署脚手架网页](#) + +### 第4部分:业务场景(ai+draw.io) - `这部分内容非常有实用价值!` + +- [第4-0节:ai + draw.io 产品设计](#) +- [第4-1节:初始化工程搭建](#) +- [第4-2节:在页面嵌入draw.io组件和对话框](#) +- [第4-3节:智能体API接口对接](#) +- [第4-4节:AI+用户+DrawIO,交互式画图](#) +- [第4-5节:ai-draw-io,云服务器部署](#) + +### 第5部分:业务场景(MobileOpenClaw)- `这部分内容非常有意思!` + +- [第5-0节:MobileOpenClaw 产品设计](#) +- [第5-1节:初始化工程搭建](#) +- [第5-2节:手机网关能力设计](#) +- [第5-3节:通过 Netty 进行同步等待通信](#) +- [第5-4节:智能体初步配置使用](#) +- [第5-5节:智能体工作流设计](#) +- [第5-6节:异步结果响应](#) +- [第5-7节:图片位点识别增强](#) +- [第5-8节:多版本安卓版本策略支持](#) +- [第5-9节:会话上下文细化处理](#) + +>星球里另外一套 AI Agent 还对接了 ELK、普罗米修斯、微信公众号等,也可以把 MCP 对接过来进行系统巡检。有了这套脚手架的学习,你可以完成非常多的场景对接使用。 + +## 六、学习路线推荐(AI) + +小傅哥的社群星球「码农会锁」,现已经有20个实战项目,6个AI、5个业务、8个组件 + 1套源码(MyBatis),这6个AI项目,你可以按需选择学习。 + +
+ +
+ +> 综上,所有的实战项目,加入小傅哥社群,全部都可以学习的到! \ No newline at end of file diff --git a/docs/md/zsxq/project/ai-knowledge.md b/docs/md/zsxq/project/ai-knowledge.md new file mode 100644 index 000000000..f1f141032 --- /dev/null +++ b/docs/md/zsxq/project/ai-knowledge.md @@ -0,0 +1,167 @@ +--- +title: AI Agent 拖拉拽 + 动态配置 +lock: no +--- + +# 《AI Agent 拖拉拽 + 动态配置(RAG、MCP、Prompt)》 - 解析文档&Git仓库代码&AI工作流 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +刚上周,老板说:`”把咱们招聘里也加一条,具备AI应用开发能力的优先!“`。是呀,现在越来越多的企业都在用AI开发能力提效了,如;聊天软件增加一键唯独信息归档提取、工作文档资料携AI对话分析、工程SQL语句脚本辅运营自动完成数据处理、代码编写用AI完成自动评审等等。这些都是在AI的基础上在构建应用,以后也会越来越多!所以,具备AI应用开发能力,也是每个工程师最应该具备的基础能力了。 + +并且用不了多久,各大互联网企业都将大量的推进落地,自有 [MCP](https://github.com/modelcontextprotocol) 服务的实现,用于增强企业 AI 应用的提效能力。因为 [MCP](https://github.com/modelcontextprotocol) 的加入,可以让你;一条命令`帮研发`,调用应用系统日志、排查系统CPU负载、自主选择是否调度数据库信息。也可以一条命令`帮运营`,搞定复杂的SQL执行、导出报表、分析数据、完成促活营销券的自动化配置上架。这就是 [MCP](https://github.com/modelcontextprotocol) 的魅力!👍🏻 + +
+ +
+ +**那么牛,MCP 是什么?** + +专业的术语 `MCP = Model Context Protocol` 模型上下文协议,可实现应用与外部数据源和工具之间的无缝集成。无论您是构建 AI 驱动的 IDE、增强聊天界面还是创建自定义 AI 工作流,MCP 都提供了一种标准化的方式来连接他们所需的上下文。 + +`来吧,上图!让你看看它是啥!` + +
+ +
+ +- 首先,站在用户的使用视角,研发或者运营,可以通过话术描述,完成系列的 AI 工作流,并拿到最终的结果。这就是 MCP 最终为你提供的服务。 +- 那么,你可以想象,在日常的工作中,运营、研发、产品、测试等,都有非常多的重复非创作性的工作,占用了大量的时间成本。尤其是研发,写写代码,就有运营过来,帮我查个问题吧,小嘚嘚。但如果有这样的借助于 MCP 实现的 AI 工作流,就可以完成80%以上的工作量。 +- 之后,站在技术的实现视角,MCP 是一个标准结构框架,你可以按照它(Spring AI)提供的 SDK 开发方式,完成本地化 API 的接入开发。让 AI 有明确的方式调用各类 API 服务接口。如果没有 MCP 这会是一件很麻烦的事情。 + +**跟着小傅哥学习,从不走偏!😄** + +- 2022年底,ChatGPT 开始火爆。 +- 2023年2月,小傅哥,开启了第一个基于AI的项目,ChatGPT AI 问答助手项目。让所有伙伴,都能学习到 AI 如何开发应用。 +- 2023年4月,启动OpenAI(ChatGPT/ChatGLM)微服务应用体系构建大型项目,让大家可以用微信登录、微信支付/蓝兔支付,构建自己的可对外付费提供服务的 OpenAI。这一年上车学习的伙伴,很多做了自己的 AI 产品,除了提高编程技能,又小赚了一辆宝马。 +- 2024年7月,结合企业诉求,开启 OpenAI + Github Acitons,实现代码自动化评审。这一年,不少伙伴在自己的公司中都有落地,个人也得到了述职晋升。 +- 2025年3月,咱们再起启航,基于 Ollama 部署 DeepSeek,开发 RAG 知识库,解析文档和Git仓库代码。这个东西,将是企业中构建自己知识库的又一项非常重要的事情。有了知识库,AI 代码的自动评审,会更加精准,也可以辅助分析需求等。 + +那么,接下来小傅哥就细致的介绍下,本次开启的新项目,可以让大家学习到哪些知识,掌握哪些技术。 + +> Spring AI MCP 与 24年末发布,学习此 AI 应用开发项目,你将是第一批具备 Java AI 应用实战开发能力的人。竞争力,嘎嘎滴! + +## 一、能学到啥 + +该项目是结合当下最火的 Ollama、DeepSeek、SpringAI 等技术构建的 RAG 知识库实现。从前端到后端到 dev-ops 的全栈式功能手把手实现。 + +- 前端,基于 AI 工具,设计前端对话页面,完成 HTML、JS、TailwindCSS 的编码工作。 +- 前端,配置跨域服务接口,前后端分离实现 UI + 服务端接口对接。 +- 后端,构建双层架构,直接面向需求编码。让学习伙伴更轻松完成 RAG 知识库核心知识的学习。 +- 后端,基于 Spring AI 完成 DeepSeek、OpenAI 双模型的策略对接,处理文本向量的解析和存储。 +- 后端,使用 postgresql 存储切割文本向量数据,完成知识库的解析和存储。 +- 后端,处理多样文本`(.md、.sql、.txt、.word...)`的解析储存以及Git克隆代码库遍历切割存储。 +- 后端,使用 Redis 存储知识库标签,用于检索展示使用。 +- 后端,基于 Flux 编写流式会话接口,以及增加知识库检索功能。 +- 运维,基于 Docker 部署 Ollama 环境,完成 DeepSeek 大模型配置。 +- 运维,使用 Linux、Docker、Nginx 完成项目的打包、构建、上线! + +虽然,知识库都有很多现成的工具。但研发的能力不是在于功能应用,而是具备这样的开发技能储备,在有需要的时候,可以举手🙋🏻‍♀️”我会,我来做!“ + +> 此项目,全程视频手把手操作 + 全部的小册文档,你可以轻松上手学会这样一个项目! + +## 二、项目介绍 + +这是一套基于 Ollama DeepSeek 大模型构建的增强 RAG 知识库检索项目,在这套项目上,实现了除普通文档知识解析外,增加了 Git 代码库的拉取和解析,并提供操作接口。为工程师做项目开发时,`需求分析`、`研发设计`、`辅助编码`、`代码评审`、`风险评估`、`上线检测`等,做工程交付提效。 + +
+ +
+ +### 第1期,RAG 我们做了什么 + +在 《DeepSeek RAG 增强知识库》第1阶段,基于 Spring AI 0.8.1 开发了一套可以上传文件和Git仓库进行解析、切割、存储,到使用向量库完成 AI 的知识库问答系统。并最终通过 Docker 部署上线。 + +#### 1. 对话页面 + +
+ +
+ +- 这是全程视频手把手,带着大家通过AI工具,完成的UI设计实现课程会演示这个操作),实现的一款非常简单漂亮的UI效果。 +- 我们可以结合知识库,进行更加有效的提问。像是公司中,会把知识库提供出一个标准接口,给其他各个AI应用平台提供能力。 + +#### 2. 上传知识 + +
+ +
+ +- 上传知识,可以解析不同类型的知识库。 +- 除了课程提供的文档库、代码库,你可以增加其他的知识库,如;网页的解析,与网页内容对话。让我们的UI,增加一个侧边栏,读取当前网页内容,分析对话。这样在公司中的一些工程的日志,错误分析时,可以更快的处理。 + +#### 3. 解析知识 - 后台日志 + +
+ +
+ +- 上传知识后,可以看到日志信息。 +- 一套工程作为知识库是非常具有开发价值的,在我们做提问的时候就不需要,人工的去分析工程,而是直接使用了。 + +### 第2期,MCP 我们要做什么 + +与第2期相比,第1期可以称之为小试牛刀,让小伙伴们以最快、最快的往事,积累,运用 Spring AI 框架,开发自己的 RAG 知识库。~~也是方便有些死鬼,早点写到简历上~~ + +到了第2期,你就开始吃上细糠了,小傅哥会带着你升级 Spring AI 框架为 1.0.0-M6 最新版本,多模型配置和操作 PG 向量库,使用 GPU 搭建响应速度更好的 Ollama DeepSeek 大模型(秒级处理),以及对接官网 DeepSeek 的大模型和统一 one-api 对接方式。 + +但这还只是开始,随着基础框架的升级完成,我们将进入 MCP 服务的开发实现。通过 AI 指令,完成 AI 工作流,调度各项 MCP 处理我们的任务作业。如图,举例操作; + +
+ +
+ +- 基于 MCP 服务的开发和对接,通过 AI 工作流指令,完成数据的采集和存放动作。💡 聪明的小伙伴以及开始联想,基于这样的 AI 开发,可以替代很多的日常工作啦。**没想到吧,也把自己替代了** 但仍然,蠢蠢欲动(我不做,别人也做呀)!~~实现后,晋升又有的讲啦!简历也有东西写啦!~~ +- 有了 MCP 后,相当于把我们需要;在一个网页操作数据库查询数据、打开另外一个网页看天气预报,再手动的创建个文件把以上的信息获取后,复制粘贴到文件里。这一些列操作,都让 AI 通过 MCP 模型上下文协议进行处理。也就是 AI 可以调用后台接口啦! + +### 课程目录 + +#### 第1期 RAG Spring AI 0.8.1 - 完结 + +1. 【更】AI RAG 知识库,项目介绍&需求分析&环境说明 +2. 【更】初始化知识库工程&部署模型&提交代码 +3. 【更】Ollama DeepSeek 流式应答接口实现 +4. 【更】Ollama DeepSeek 流式应答页面对接 +5. 【更】Ollama RAG 知识库上传、解析和验证 +6. 【更】Ollama RAG 知识库接口服务实现 +7. 【更】基于AI工具,设计前端UI和接口对接 +8. 【更】Git仓库代码库解析到知识库并完善UI对接 +9. 【更】扩展OpenAI模型对接,以及完整AI对接 +10. 【更】云服务器部署知识库(Docker、Nginx) + +#### 第2期 MCP Spring AI 1.0.0 - 开冲 + +11. 【新】AI MCP 项目介绍 +12. 【新】吃上细糠,Spring AI 框架升级 + GPU 部署 AI +13. 【新】吃上细糠,官网 DeepSeek + open-api 对接 +14. 【新】MCP 服务的应用类演示和使用 +15. 【新】MCP Spring AI 客户端npx调用,以及资源讲解 +16. 【新】MCP Spring AI 服务端webflux实现 +17. 【新】MCP Spring AI 服务端 + 客户端对接使用 +18. 【新】服务接口实现,增强 RAG 知识库 + MCP 服务使用 +19. 【新】应用服务接口与前端页面对接 +20. ... 随课程开发提供,包括后续的云服务部署。 + + +### 课程计划 + +课程已全部录制完成,计划在3月3日开更,3月16日之前全部剪辑更新完成。 + +
+ +
+ +- 全课程包括文档 + 小册,全程视频手把手带着做。 +- 课程地址:[https://t.zsxq.com/GwNZp](https://t.zsxq.com/GwNZp) + +--- + +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! \ No newline at end of file diff --git a/docs/md/zsxq/project/ai-mcp-gateway.md b/docs/md/zsxq/project/ai-mcp-gateway.md new file mode 100644 index 000000000..6ac01e671 --- /dev/null +++ b/docs/md/zsxq/project/ai-mcp-gateway.md @@ -0,0 +1,121 @@ +--- +title: AI MCP Gateway 网关服务系统 +lock: no +--- + +# 《AI MCP Gateway 网关服务系统》 - 为各类应用服务接口,便捷转换为MCP服务而设计。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/SNsgH](https://t.zsxq.com/SNsgH) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +两年多了,从2023年1月起,小傅哥就开始关注AI、跟进AI,落地 AI 技术相关的场景项目。`做了 AI 问答自动回复助手`、`OpenAI 应用服务(含支付买额度 + OpenAI SDK 开发)`、`OpenAI 代码评审`、`Ai Agent 智能体(RAG、MCP)`,让一众伙伴积累到了丰富的AI应用开发技术,可以满足当下互联网AI应用开发招聘诉求。**那么接下来,关于 AI 小傅哥还要带着大家什么呢?🤔** + +
+ +
+ +**足够高频,互联网AI应用场景,必备项目!** + +现阶段,众多互联网公司把 AI 作为公司的战略目标,基于 AI 为公司各类场景提效。甚至不少公司要求程序员的编码,AI 占比要有30%以上(来自于某论坛研发分享)。还在各个部门设立 AI 应用创新组。 + +而 AI 的主要提效方式,则是自研实现或基于 Dify 搭建一套 AI Agent 智能体。通过智能体把公司的文档资料转换为 RAG 知识库,再通过 MCP 协议对接各类应用的服务接口。这样我们对 AI Agent 智能体下达命令后,就可以进行分析,规划,执行,直至产出最终的结果。更多关于智能体的实现介绍,可以参考[《AI Agent 智能体设计实现》](https://mp.weixin.qq.com/s/dwsfadYKs7Uy4YvHfLFsVQ) + +**死鬼,重点来啦!** + +公司里为了扩展智能体可提效的场景,就要把现有的公司的各类应用服务接口,`日志的`、`监控的`、`服务的`、`交易的`、`结算的`、`营销的`、`人群的`、`数据的`等等,都要转换为 AI Agent 智能体可识别的 MCP 服务接口。这样就可能有成百上千,成千上完,甚至几十万个接口要做实现。 + +那肯定不能每个接口都写一遍 MCP 服务!所以,对于这样的场景问题,公司里会做一套统一的 MCP Gateway 网关服务系统,其他个各类接口(http、rpc),都可以通过一键配置的方式转换为 MCP 协议类型的接口,被 AI 可以识别和使用。 + +
+ +
+ +而小傅哥这次带着你做的 AI 类项目,就是实现一套这样的 **MCP GateWay 网关服务系统**。小傅哥,会带着你,实现MCP服务,分析 MCP 协议,通过做AI服务代理、网页协议对接、JSON-RPC2标准等方式,把 MCP 协议厘清,之后在手把手的带着你编写响应式的 MCP Gateway 网关能力(鉴权也可以放到网关做)。 + +> 🧧 文末提供了,小傅哥所有编程实战项目获取方式,一次加入即可获得17个已完结的和本次新开展的。 + +## 一、能学到啥 + +该项目是 AI 应用场景下的通用技术服务组件类项目,以解决接口 MCP 协议转换而设计实现。在整个项目中,你可以积累到关于 MCP 协议的深度分析,学习分析协议的技巧和方案,并积累关于设计一个组件解决通用场景问题的能力。 + +- 【前端】基于 html、js、div、css,设计 MCP 协议分析页面。 +- 【前端】基于 html、js、div、css,构建一套服务端管理系统,便于 MCP 协议的录入和使用。 +- 【后端】MCP 协议的分析、理解、运用。掌握 MCP 开发和使用的能力。 +- 【后端】基于 MCP 协议的分析和网关设计诉求,构建网关服务库表。 +- 【后端】运用 DDD 分层架构,设计 MCP 网关服务系统。 +- 【后端】构建AI代理服务,断点调试分析 MCP 协议。 +- 【后端】设计 MCP 分析协议网页服务,链接 MCP 服务,观察 MCP 协议。 +- 【后端】基于 Flux 响应式接口,设计实现 MCP 协议的 sse 连接、initialize 初始化响应、tools 工具的 list 反馈和 call 调用等。 +- 【后端】提供 MCP 协议的动态录入和加载能力,以及提供录入接口组件,便于其他系统可快速录入。 +- 【后端】设计 MCP 网关协议鉴权服务,确保 MCP 服务使用的安全性。 +- 【后端】熟练使用 okhttp3、retrofit2 框架,动态对接 HTTP 服务接口,用于 MCP 协议 toos/call 工具调用。 +- 【后端】扩展学习 rpc 泛化调用,给 MCP 协议提供使用。其实有了这套东西,还可以对接如硬件设备 rs232 串口通信,让 MCP 服务,管理你的硬件设备。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 + +此外,小傅哥对于每个章节还讲解了章节的诉求、流程的设计,之后再到方案实现和功能验证。并在每个章节留有作业让大家练习。当然这还没有完,你知道小傅哥这个架构师画图还是非常牛逼的,所以你还能看到各种画图的技巧,耳濡目染的把这些东西学习成自己的本事!~ + +## 二、项目介绍 + +本项目是 AI Agent 智能体,关于 MCP 协议对接的通用网关服务项目,以解决各类业务接口便捷转换为 MCP 协议而设计实现。通过这样的配置,可以大大的简化从普通http、rpc接口到 MCP 协议的转换操作。这样的项目,也是每个互联网公司在做 AI Agent 智能体时,必备的基础设施项目。 + +### 1. 更新计划 + +本项目目前已经做了基础的筹备和验证,计划于10.26日开始更新课程。整体课程预计在20+节左右。带着你完整的实现一套 AI MCP Gateway 网关服务项目。 + +### 2. 项目资料 + +#### 2.1 协议分析 - 页面 + +
+ +
+ +- 编写了一套网页对接 MCP 服务的页面,把以前直接在 AI Agent 配置 MCP 协议使用的过程,通过页面一步步对接和使用的方式进行展示。 +- 有了这样一个操作过程步骤,你可以更加清晰的了解到 MCP 的执行过程,也能更好的为后续做 MCP 网关服务实现打下基础。 + +#### 2.2 协议分析 - 代理 + +
+ +
+ +- 为了更好的体现出 AI 和 MCP 的交互,这里小傅哥会带着你做一个 AI 的代理接口,来调试观察 MCP 协议的传输。 + +#### 2.3 初始版本 - 案例 + +
+ +
+- 小傅哥这里先做了一个初始的 demo 版本,跑通 MCP 网关,后续会设计整套 DDD 架构,完整整体协议和服务的对接。 + +## 三、课程大纲 + +**不同于网上demo项目。小傅哥带着你做的项目,是一步步,一个个章节的带着大家从0到1的方式,进行分析、设计和开发。是一个纯手把手教大家学习实战技术的项目!** 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。 + +- 第1节:项目功能需求分析 +- 第2节:项目系统架构设计 +- 第3节:MCP 功能服务实现 +- 第4节:AI 服务代理实现 +- 第5节:通过AI 服务代理,分析 MCP 协议 +- 第6节:通过设计网页对接,分析 MCP 协议 +- 第7节:通过 json-rpc2 标准,官网资料,总结 MCP 协议 +- 第8节:设计网关服务端系统,讲解模块关系 +- 第9节:设计拆分领域模型结构 +- 第10节:设计需求服务库表 +- 第11节:网关协议功能编写(1、2、3、4),分步骤设计实现 +- 第12节:网关协议与数据库表对接 +- 第13节:网关协议与http接口对接 +- 第14节:网关协议与rpc接口对接 +- 第15节:网关协议能力管理端编写(1、2、3、4) +- 第16节:MCP 网关服务打包上线 + +随着课程开展,陆续更新课程目录,也会有一些新的内容加入。 + +>课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!! + diff --git a/docs/md/zsxq/project/api-gateway.md b/docs/md/zsxq/project/api-gateway.md index d092b8159..60dec2374 100644 --- a/docs/md/zsxq/project/api-gateway.md +++ b/docs/md/zsxq/project/api-gateway.md @@ -1,6 +1,6 @@ --- title: API网关:中间件设计和实践 -lock: need +lock: no --- # API网关:中间件设计和实践 @@ -12,6 +12,7 @@ lock: need 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0diYdgP5u](https://t.zsxq.com/0diYdgP5u) - 课程入口 >沉淀、分享、成长,让自己和他人都能有所收获!😄 @@ -114,5 +115,4 @@ lock: need ![](https://bugstack.cn/images/article/assembly/api-gateway/api-gateway-220809-07.png) - [x] [第2章:代理RPC泛化调用](https://bugstack.cn/md/assembly/api-gateway/2022-08-20-%E7%AC%AC2%E7%AB%A0%EF%BC%9A%E4%BB%A3%E7%90%86RPC%E6%B3%9B%E5%8C%96%E8%B0%83%E7%94%A8.html) - [x] [第3章:分治处理会话流程](https://bugstack.cn/md/assembly/api-gateway/2022-08-27-%E7%AC%AC3%E7%AB%A0%EF%BC%9A%E5%88%86%E6%B2%BB%E5%A4%84%E7%90%86%E4%BC%9A%E8%AF%9D%E6%B5%81%E7%A8%8B.html) -- [ ] [第4章:方法执行器封装](#) -- [ ] 梳理中 ... 每周更新 +- 现已全部更新完成 diff --git a/docs/md/zsxq/project/big-market.md b/docs/md/zsxq/project/big-market.md new file mode 100644 index 000000000..f5f8e544f --- /dev/null +++ b/docs/md/zsxq/project/big-market.md @@ -0,0 +1,129 @@ +--- +title: 大营销平台系统 +lock: no +--- + +# 《大营销平台系统》—— 小傅哥第8个项目,前后端 + Dev-Ops 的全栈式综合编程实战DDD项目! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/14gswKIeX](https://t.zsxq.com/14gswKIeX) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +大家好,我是技术UP主小傅哥。 + +💐又到了启动新项目的时候,死鬼开心嘛。小傅哥的**星球:码农会锁**,第8个应用级实战项目开启啦!—— 在这之前小傅哥已经完结了7个实战项目,可进入 [https://gaga.plus](https://gaga.plus) 嘎嘎强平台,体验项目。 + +
+ +
+ +呐,接下来,小傅哥要带着大家做一个什么项目呢?🤔 + +这个新项目,结合小傅哥已经带着大家完成的 OpenAi 大模型应用业务场景,做上层的营销活动。这就像互联网公司中有了电商、外卖、出行等场景一样,在场景之上做营销活动。所以我们的新项目是 **《大营销平台系统》**!因为小傅哥的星球之前做过了一个抽奖,那么这个项目会用新的DDD架构,对抽奖系统进行重构,并扩展出`营销账户`、`用户返利`、`积分兑换`等服务,完成一整套的营销平台功能。💥 + +小傅哥把互联网中真实的场景、架构、实现,拿出来让你成体系化的学习; + +
+ +
+ +这里抽奖模块通过RPC接口,对接到大营销平台。这里不只是抽奖,还要串联账户、奖品、返利等各项内容。接下来,小傅哥就着重介绍下这套信息项目的重点,让大家可以知道学习到哪些知识,掌握哪些技术。 + +>文末有加入学习方式,还有优惠券可以使用。先到先得! + +## 一、能学到啥 + +在各大互联网公司中,营销平台都是那个流量最大,场景最复杂的系统,也是需求迭代最多还最快系统。在这个部门的研发伙伴,谁身上都是背着“几个事故”锻炼出来的技术。所以,跟着小傅哥学习这样一套系统,是可以学习到非常多的技术。包括; + +- 【前端】熟练使用 React、Typescript 在前端工程中开发营销活动页。 +- 【前端】熟练掌握,跨域接口请求,以及通过浏览器指纹技术实现防刷。 +- 【前端】熟练使用,Ant Design Pro 开发后台运营管理系统。 +- 【后端】熟练搭建 DDD 工程项目、以及 DDD 脚手架搭建项目。并对 DDD 设计方法有清楚的认知。 +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对框架源码所提供的扩展接口具备运用能力。 +- 【后端】熟练运用分布式技术栈,包括:Dubbo、RocketMQ、Redis、XXL-JOB、Sharding-JDBC、Nacos等。 +- 【后端】熟练使用多种设计模式、设计原则,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。 +- 【后端】深度学习复杂场景的架构设计、编程思维,如果处理系统功能的边界和上下文的维护。—— 这些东西一定是从实践中才能学习到的。 +- 【后端】熟练使用 Mock 单测工具、JMeter 压测工具,增强代码交付质量。 +- 【后端】熟练掌握异常、枚举、错误码的定义和使用,并学习到如何合理打印服务日志,便于问题排查。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练使用 Nginx 配置转发服务,并能申请ssl配置https服务。 +- 【运维】熟练使用 Grafana 监控系统,对系统的 JVM、磁盘、Tomcat、应用(QPS、响应时间、调用量)完整监控。 + +此外,小傅哥会把系统开发过程中的思考、设计、编码,录制⏺成完整的视频,让大家可以学习到的更多、更细、更深! + +## 二、项目介绍 + +本次项目是一个包括 `前后端 + Dev-Ops` 的全栈式综合编程实战项目,基于 React + SpringBoot + 分布式技术栈 + Nginx + Grafana + Docker 云服务,开发、部署、上线、监控的《大营销平台系统》项目。 + +### 1. 核心流程 + +
+ +
+ +### 2. 项目工程 + +
+ +
+ +### 3. 业务监控 - 监控示例 + +
+ +
+ +- 目前是 OpenAi 业务系统监控,大营销平台系统上线后,会把这部分监控一起添加上。 +- 项目,只有上线 。你才会注意到很多的细节,就像 Tomcat 的最大连接数,如果不开发超时熔断,在接口异常超时等待的情况,就有可能把连接数打满。 + +## 三、项目大纲 + +不同于网上的小Demo项目,这个项目的场景来自于互联网真实业务需求,一个个章节、一步步流程的带着大家从0到1,需求分析、工程设计和代码实现。是一个纯手把手教大家学习实战技术的项目! 大纲会分为5个部分,以需求驱动,讲解功能实现。 + +- 第1部分:需求文档 + + - 第1节:营销场景的需求设计 + - 第2节: + +- 第2部分:开发运维 + + - 第1节:使用脚手架创建工程 + - 第2节:创建仓库提交代码 + - 第3节: + +- 第3部分:营销服务 + + - 第1节:模块化工程搭建 + - 第2节:营销账户设计 + - 第3节:返利服务设计 + - 第4节: + +- 第4部分:前端页面 + + - 第1节:在 OpenAi 项目中,开发抽奖页面。调用Lottery服务。 + - 第2节: + +- 第5部分:后台管理 + + - 第1节:配置抽奖活动管理 + - 第2节: + +--- + +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! + +## 四、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:OpenAi大模型应用项目、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) 这样一套项目,放在一些平台售卖,一个至少都是几百块。但小傅哥的星球,只需要100多,就可以获得全部的学习项目! + +**加入星球**:下载`星球APP`,从星球【课程入口】进入。里面有完整的学习指引,包括;使用说明、代码仓库、专属项目群、学习路线、往期项目。 + diff --git a/docs/md/zsxq/project/bug-code.md b/docs/md/zsxq/project/bug-code.md new file mode 100644 index 000000000..bcf387bf4 --- /dev/null +++ b/docs/md/zsxq/project/bug-code.md @@ -0,0 +1,48 @@ +--- +title: Bug-Code:异常模拟复现 +lock: no +--- + +# Bug-Code —— 收集实际开发中所遇到的异常进行模拟复现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +## 一、前言 + +看到星球VIP群,读者伙伴分享的面试真实场景问题和讨论,发现很多没经过太多编码的研发伙伴,可能并没有看到过这样的异常,所以理解起来也有些困难。那么为了让大家更好的吸收这些实战经验,小傅哥特此创建这样一个 Bug-Code 仓库,用于给星球读者积累一些工作中常见的异常复现,积累编程经验。 + +- **工程仓库**:[https://gitcode.net/KnowledgePlanet/CodeTutorial/Bug-Code](https://gitcode.net/KnowledgePlanet/CodeTutorial/Bug-Code) +- **加入项目**:[https://t.zsxq.com/jAi2nUf](https://t.zsxq.com/jAi2nUf) - 加入后在星球置顶消息可以申请加入项目组,`公众号:bugstack虫洞栈 回复:星球 可以获得加入优惠券` + +## 二、异常问题 + +### 1. Transaction rolled back because it has been marked as rollback-only + +- 问题:rollback-only +- 异常:线程执行某个定时任务,在事务提交时抛出了异常。看到rollback-only字样,这个是什么原因引起的。写代码要注意什么能避免产生这一种情况。 +- 测试:用数据库表防重做插入测试,触发异常; + - 1. 两个方法都加了事务注解,两个方法都会受到到事务管理的拦截器增强,并且事务传播的方式都是 REQUIRED,当已经存在事务的时候就加入事务,没有就创建事务。这里A和B都受事务控制,并且是处于同一个事务的。 + - 2. A调用B,A中抓了B的异常,当B发生异常的时候,B的操作应该回滚,但是A吃了异常,A方法中没有产生异常,所以A的操作又应该提交,二者是相互矛盾的。 + - 3. Spring的事务关联拦截器在抓到B的异常后就会标记rollback-only为true,当A执行完准备提交后,发现rollback-only为true,也会回滚,并抛出异常告诉调用者。 + +- 复现:[https://gitcode.net/KnowledgePlanet/CodeTutorial/Bug-Code/-/blob/master/src/test/java/cn/bugstack/guide/test/RollbackOnlyTest.java](https://gitcode.net/KnowledgePlanet/CodeTutorial/Bug-Code/-/blob/master/src/test/java/cn/bugstack/guide/test/RollbackOnlyTest.java) + +### 2. Transaction rolled back because it has been marked as rollback-only + +- 问题:死锁 +- 异常:Deadlock found when trying to get lock; try restarting transaction +- 测试:多线程模拟并发下,一个事务未提交完成,又来一个事务。 +- 复现:[https://gitcode.net/KnowledgePlanet/CodeTutorial/Bug-Code/-/blob/master/src/test/java/cn/bugstack/guide/test/DeadlockTest.java](https://gitcode.net/KnowledgePlanet/CodeTutorial/Bug-Code/-/blob/master/src/test/java/cn/bugstack/guide/test/DeadlockTest.java) + +### 3. Spring 线程安全处理 + +- 问题:串号 +- 异常:在一个单例的 OrderService 中,使用多线程调用,对一个属性设置值后再获取,那么会出现 OrderNo 串号的问题。 +- 测试:分别使用普通的单例方式和加入 ThreadLocal 根据线程获取单号 +- 复现:[https://gitcode.net/KnowledgePlanet/CodeTutorial/Bug-Code/-/blob/master/src/test/java/cn/bugstack/guide/test/ThreadLocalTest.java](https://gitcode.net/KnowledgePlanet/CodeTutorial/Bug-Code/-/blob/master/src/test/java/cn/bugstack/guide/test/ThreadLocalTest.java) \ No newline at end of file diff --git a/docs/md/zsxq/project/business-behavior-monitor.md b/docs/md/zsxq/project/business-behavior-monitor.md new file mode 100644 index 000000000..3fe36a96a --- /dev/null +++ b/docs/md/zsxq/project/business-behavior-monitor.md @@ -0,0 +1,103 @@ +--- +title: 透视业务流程 - 监控系统 +lock: no +--- + +# 透视业务流程 - 监控系统 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/CVzpL](https://t.zsxq.com/CVzpL) + +>沉淀、分享、成长,让自己和他人都能有所收获! + + + +大家好,我是技术UP主小傅哥。 + +可以这么说,作为一个想卷到好一些公司,拿到高薪资Offer的程序员,就没有一个只是做CRUD业务流程开发的。他们的简历上,基本都是业务项目不错,又具备开发轮子的手段。轮子的意思就是一个个独立于业务,对于同类功能凝练而成的通用技术类组件,解决同类的共性问题。因此减少业务系统重复功能建设,提高开发效率。`所以一般简历上业务 + 技术,往往是很亮眼的。` + +
+ +
+ +>🧧 文末有优惠加入学习二维码,可以获得除了本文《业务透视监控》外,还有5个业务项目 + 5个组件项目。 + +## 一、能学到啥 + +业务项目与组件项目是两种不同的思考模式,不只是工程结构不一样,在编码逻辑实现上,组件项目也会更多的思考`兼容性`、`可靠性`、`共用性`、`扩展性`。而这些内容也是业务项目没法学习到的,所以我们需要不同的实战类内容填充自己的知识积累。 + +- IntelliJ IDEA 使用以及快捷键技巧。创建组件工程、代码库使用、代码提交、拉取、合并等操作 +- logback 自定义日志采集组件实现,统一过滤和格式化日志数据,进行数据上报推送。 +- ognl 表达式解析复杂对象数据,这样的组件在MyBatis源码中也有用到。 +- 设计数据处理中心,承接自定义日志组件推送的数据。 +- 业务动态链路节点可配置化设计,解析监控日志数据并做缓存计算和数据库存储。 +- 节点关联关系设计和渲染,动态可视化展示业务实时监控数据信息。 +- 前端gojs渲染技术,展示和修改监控链路图,以及监控日志查询。 + +## 二、项目介绍 + +本次项目会采用基于扩展 logback 日志上报数据进行 ognl 配置节点公式的方式进行采集、计算和可视化渲染。在这套项`小而美,小而精`的组件项目中,你可以学习到非常多的实战技能。 + +这套`透视业务流程的监控系统`,与 `Prometheus + Grafana`、`Skywalking` 有较大的差异。这两款监控都是系统健康度监控,而小傅哥带着大家做的是业务流程监控。*很多中大厂,也都有同类的业务系统* + +业务流程监控,以展示用户行为维度的业务流程为核心,透视系统工程中业务的流转。1:1 还原产品 PRD 流程图为可视化动态效果,实时展示系统调用执行数据信息。`可参考下面的图` + +### 1. 监控透视图 - 以星球大营销系统为例 + +
+ +
+ +### 2. 监控的日志 + +
+ +
+ +### 3. 库表的设计 + +
+ +
+ +### 4. 组件化工程 + +
+ +
+ +>有了这一套组件的学习,你可以搭配到任何一个你做的业务项目中使用。无论是在公司中,还是在面试写简历里。都能让你展示出非常不错的技术体现度。 + +## 三、课程大纲 + +**不同于网上项目,这个项目是一步步,一个个章节的带着大家从0到1的全程视频的方式,进行分析、设计和开发。是一个纯手把手教大家学习实战技术的项目!** 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。项目地址:[https://t.zsxq.com/CVzpL](https://t.zsxq.com/CVzpL) + +
+ +
+ +- 第1节:监控业务,介绍、设计、演示 +- 第2节:组件工程初始化和代码提交 +- 第3节:监控采集日志组件SDK设计实现 +- 第4节:前端渲染gojs组件案例讲解「基于渲染可视化所需信息设计后续库表」 +- 第5节:流程可视化监控,库表设计 +- 第6节:监控中心工程ORM等基础配置 +- 第7节:监控中心接收测试工程上报数据解析 +- 第8节:监控列表接口实现和配置使用 +- 第9节:监控渲染链路实现和配置使用 +- 第10节:监控日志数据查询和配置使用 +- 第11节:监控链路动态更新接口和配置使用 +- 第12节:大营销业务系统链路配置「可以是其他任何业务系统」 +- 第13节:大营销工程引入监控组件「可以是其他任何业务系统」 +- 第14节:课程总结(对课程中伙伴提到的问题进行总结以及可扩展点讲解) + +## 四、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:大营销、OpenAI 应用、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发、支付SDK、动态线程组件等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +> [🧧加入](https://bugstack.cn/md/zsxq/other/join.html) 这样成体系的全量项目学习,放在一些平台售卖,至少都要上千块。但小傅哥的星球,只需要100多,就可以获得大厂架构师对你手把手教学! + +**在今年的面试中,星球帮助众多伙伴拿到**`微信支付`、`京东科技`、`度小满`、`蚂蚁金服`、`Lazada(电商优惠营销)`、`快手`、`美团到店`等Offer,还有的校招生薪资最高年包到45w! diff --git a/docs/md/zsxq/project/chatbot-api.md b/docs/md/zsxq/project/chatbot-api.md new file mode 100644 index 000000000..d7d76809d --- /dev/null +++ b/docs/md/zsxq/project/chatbot-api.md @@ -0,0 +1,70 @@ +--- +title: ChatGPT AI 问答助手 +lock: no +--- + +# ChatGPT AI 问答助手 - 小型,对接知识星球 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0emixGs2c](https://t.zsxq.com/0emixGs2c) - 课程入口 + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +## 1. 项目介绍 + +**《ChatGPT AI 问答助手》** 开源免费项目,涵盖爬虫接口、ChatGPT API对接、DDD架构设计、镜像打包、Docker容器部署,小巧精悍,流程全面。对于Java编程伙伴来说,非常具有学习价值。 + +❤️ 这个项目本身是小傅哥为自己的知识星球开发的一个智能问答回复系统,用于帮助读者解决一些常见的技术问题,提高回答效率也减少小傅哥的对此类问题的时间投入。通过演示我们可以看到,有了这样一个智能AI问答助手,可以大大的减少很多对于这些通用类技术问题的回复,同时也可以把这样的问答内容沉淀到知识星球,方便其他人学习使用。 + +《ChatGPT AI 问答助手》这样一个项目,要用到哪些技术手段呢?它包含;SpringBoot、DDD架构、Github仓库使用、接口爬虫、AI接口对接、定时任务、镜像打包、Docker容器部署等内容。 + +可以说麻雀虽小,五脏俱全。代码量不大但流程很完整,对于正在学习Java的伙伴来说,非常具有学习价值。 + +为了让粉丝伙伴更好的学习这个项目,小傅哥把它免费开源出来,并且是录制好对应的视频课程,一行行带着大家手写代码学习这个项目。 + +包括工程的创建、Github仓库使用、push代码等,因为只有这样才能让更多新人有一条进入学习编程的大门。 + +**注意**: +1. 技术栈:Java、SpringBoot、爬虫、ChatGPT、Job、Docker +2. OpenAi Keys 申请:[https://beta.openai.com/account/api-keys](https://beta.openai.com/account/api-keys) - 用于处理扫码知识星球问题进行调用获取答案。 +3. 在学习的过程中,可以看到每一个章节都有一个对应的代码分支,可以把代码拉取到本地切换到对应的分支进行对照学习。 + +## 2. 课程目录 + +**注意📢** `视频`;课程更新到小傅哥的B站:[]() +**注意📢** `源码`;发布到 Github、Gitcode +- Github:[https://github.com/fuzhengwei/chatbot-api](https://github.com/fuzhengwei/chatbot-api) +- Gitcode:[https://gitcode.net/fuzhengwei/chatbot-api](https://gitcode.net/fuzhengwei/chatbot-api) + +| 目录 - `点击章节进入到学习视频` | Github | Gitcode | +| -------------------------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| [开篇介绍,学习引导](https://www.bilibili.com/video/BV1YT411o7Hb) | | | +| [第1节:SpringBoot DDD 工程创建和 Github/Gitcode 仓库使用](https://www.bilibili.com/video/BV1RR4y1b7UQ) | [【23_xfg_init_project】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_init_project) | [【23_xfg_init_project】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_init_project) | +| [第2节:创建知识星球,爬取接口信息](https://www.bilibili.com/video/BV1L341197x1) | [【23_xfg_zsxq_api】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_zsxq_api) | [【23_xfg_zsxq_api】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_zsxq_api) | +| [第3节:知识星球接口领域服务开发](https://www.bilibili.com/video/BV1Wv4y1671x) | [【23_xfg_zsxq_domain】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_zsxq_domain) | [【23_xfg_zsxq_domain】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_zsxq_domain) | +| [第4节:对接ChatGPT,调用接口](https://www.bilibili.com/video/BV1KT411Z7z3) | [【23_xfg_chatgpt】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_chatgpt) | [【23_xfg_chatgpt】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_chatgpt) | +| [第5节:整合知识星球与ChatGPT,完成自动化回答](https://www.bilibili.com/video/BV1Ny4y1R7EK) | [【23_xfg_job】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_job) | [【23_xfg_job】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_job) | +| [第6节:打包镜像文件,部署服务到 Docker 容器](https://www.bilibili.com/video/BV1gT411C7nn) | [【23_xfg_docker】](https://github.com/fuzhengwei/chatbot-api/tree/23_xfg_docker) | [【23_xfg_docker】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/23_xfg_docker) | +| [【扩展】第7节:多组任务服务配置](https://www.bilibili.com/video/BV1XR4y1h7JP) | [【230127-xfg-task】](https://github.com/fuzhengwei/chatbot-api/tree/230127-xfg-task) | [【230127-xfg-task】](https://gitcode.net/fuzhengwei/chatbot-api/-/tree/230127-xfg-task) | + +## 3. 加入星球【ChatGPT AI 问答助手】 + +你可以通过微信扫码,加入知识星球【ChatGPT AI 问答助手】,在手机端对ChatGPT进行提问。**需要加群交流项目的伙伴**,可以添加微信【`fustack`】备注【`ChatGPT 项目加群`】 + +
+ +
+ +## 4. 版权说明 + +此项目为 Apache License 2.0 开源协议项目,以学习为目的进行创作,禁止培训机构、私人号主、公司组织等以各类收费形式进行销售。如果你有合作诉求,请与小傅哥联系获得书面授权,微信:fustack + +--- + +:bus: 其他项目: [`IM 仿微信`](https://github.com/fuzhengwei/NaiveChat) | [`Lottery 抽奖系统 - 基于领域驱动设计的四层架构实践`](https://github.com/fuzhengwei/Lottery) | [`API网关:中间件设计和实践`](https://github.com/fuzhengwei/api-gateway) | [`手写MyBatis`](https://github.com/fuzhengwei/small-mybatis) | [更多搜索...](https://github.com/fuzhengwei?tab=repositories) + + + diff --git a/docs/md/zsxq/project/chatgpt.md b/docs/md/zsxq/project/chatgpt.md new file mode 100644 index 000000000..b205af19b --- /dev/null +++ b/docs/md/zsxq/project/chatgpt.md @@ -0,0 +1,93 @@ +--- +title: OpenAi 大模型应用服务体系构建 +lock: no +--- + +# OpenAi 大模型应用服务体系构建 - API-SDK、鉴权、公众号、微信支付 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0d3o5FKvc](https://t.zsxq.com/0d3o5FKvc) - 课程入口 + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + + + +## 一、产品形态 + +这趟车🚌,本身的核心是关于**微服务应用体系的构建**,通过讲解配置`Docker`、`Nginx`、`SSL`等环境以及开发出`鉴权`、`认证`、`微信公众号`、`企业微信`、`支付宝交易`等模块的方式,完善体系的物料服务。而 ChatGPT 只是其中的一种产品形态而已,这种产品形态通过 API 的方式与具体的物料服务模块解耦。这样做的方式是因为基础的物料`【物料指SDK和服务】`并不会频繁变化,而离业务最近的 API 会随业务变动发生较多的改动。所以这样的应用架构方式,在互联网大厂中也是非常常见和常用的。 + +这些东西的价值在于架构思维,而我也希望授人以渔,教会大家一些根本的东西,而不是永远的在CV+CRUD。有了这样的学习,学习的就不只是这样一个项目,而是可以把这个项目中所涉及的组件开发,都能进行任意物料模块与需要对接的服务进行关联打通使用。方便`写到简历`、`用到项目`、`实战锻炼`、`积累经验`。 + +## 二、项目架构 + +- **目标**:此项目以围绕类似 ChatGPT 生成式服务,构建微服务应用架构体系组件。包括;用户鉴权、公众号、多方支付、企业微信等对接方式,满足不同诉求的使用。并以模块化设计,积木式构建应用,让不同的场景诉求都可以配置化对接。 +- **功能**:更直白一些就是通过这套微服务体系,可以构建出;`网页版ChatGPT对接`、`用户鉴权校验接口`、`关注公众号解锁`、`支付付费购买`、`公众号自动回复`、`企业微信聊天对接`、`知识星球对接`等。 + +那么这套系统是以`视频`和`小册`的教程为导向,教会大家开发这些各个模块的技术组件和技术服务。同时这里的组件和服务,都是微服务实现,可以被替换成其他任何一个你所需的内容。比如不是对接 ChatGPT 而是你想对接一个其他的服务也是可以的。 + +**整个系统架构如下**: + +
+ +
+ +如图;以用户请求为入口,通过 `Nginx SSL 443` 校验转发到对应的服务,并做相关的鉴权和服务控制,并完成最终的 token 授权使用。整套微服务包括系统;`chatgpt-api-sdk`、`chatgpt-auth`、`chatgpt-wx`、`chatgpt-pay`、`chatgpt-zsxq`、`chatgpt-admin`、`chatgpt-web` 服务。 + +## 三、拓扑结构 + +接下来我们再以工程拓扑的视角看下这套需要开发的系统; + +
+ +
+ +如拓扑结构,系统从上到下以不同的产品形态,统一调用封装的服务API进行功能的流转。API系统中所处理的核心动作,会以各个物料模块进行实现。所以这里会拆分出标准的 ChatGPT-API 业务系统,之后再由各个模块系统支撑。到具体的模块中再进行详细的系统设计。 + +## 四、技术使用 + +此项目会使用到 `SpringBoot`、`MyBatis`、`MySQL`、`Redis` 等技术栈,但因本项目主要以小成本,轻量维护的实际使用为主,所以不会过多引入分布式技术栈。所以在设计实现上,主要以小而美、小而精,且能匹配到真实场景的使用为主。—— 分布式技术栈是为了更大规模的体量使用,但也会为此付出运维和应用服务器成本。所以一些中小厂的项目或者创业类型的项目,都会优先更轻量级技术栈使用,以此减少这部分成本。 + +除技术栈的使用外,涉及到开发工具包括;`IntelliJ IDEA`、`WebStorm`、`Docker`、`Protainter`、`Nginx`、`Git`、`Maven`、`Navicat`、`SSH工具`等,以满足开发代码中的使用。 + +关于此项目可能还会涉及少部分 Next.js、Typescript 等前端知识,方便做 Web UI 的开发。 + +## 五、课程计划 + +
+ +
+ +此课程会包括,基础设置、模块开发、API服务等几块内容,而基础设施主要为一些基本操作,小傅哥会把一些这样的内容都放到这块,方便很多新人伙伴学习使用。这里会分为3个大的步骤; + +1. 在基础设置的基本必备服务搭建后,会进入接口鉴权的简单开发,这个模块开发后,大家就可以简单的使用了小傅哥提供的 OpenAI 了接口了。—— 当然你如果自己有 OpenAI 接口,也可以直接使用。**像 [https://huggingface.co/](https://huggingface.co/) 也提供了一些可以免费使用的简单 Open-API** +2. 有了这部分内容的使用,后续会进入 API-SDK 的开发,以及网页的简单开发。通过这样的开发构成一套基本的模块服务。ChatGPT-WEB-UI -> API-SDK -> 鉴权 -> OpenAI 的使用。 +3. ChatGPT-WEB-UI 流程🏃🏻跑通后,就可以逐步扩展其他服务模块。让业务与场景结合,如关注公众号、公众号回复、企业微信机器人、交易支付购买授权Token。这个过程可以让 ChatGPT-WEB-UI 与各个模块结合使用。 + +## 六、如何开始 + +这套课程会以`视频`、`小册`、`代码`、`作业`的方式进行推进,视频主要以演示操作、讲解核心的方式进行,并在小册和对应的代码中细化细节展示。鉴于星球有些在校的编程新手,所以关于`工程的创建`、`代码的提交`、`镜像的打包`、`容器的部署`等这些基础操作,也都会在视频中进行演示,方便大家更容易的上车。 + +### 1. 开通权限 + +星球课程涉及的代码部分会通过 [gitcode.net](https://gitcode.net/KnowledgePlanet) 提供,你只需要申请一次就可以授权到星球所有课程的代码仓库。授权申请地址:[https://t.zsxq.com/0dS1kW2r9](https://t.zsxq.com/0dS1kW2r9) - 审核后即可访问星球的项目仓库了。 + +### 2. 加群交流 + +小傅哥为星球伙伴创建了专属的VIP技术交流群,你可以通过扫码添加小傅哥的微信,备注上`你的星球编号`。我会给你拉到专属的微信群。 + +
+ +
+ +### 3. 推荐工具 + +推荐工具;- 随着课程讲解会引导大家安装这些工具。 +- [Termisu](https://termius.com/):云服务器链接工具,自带一套 SFTP 工具,很好用。 +- IDEA 插件:`Sequence Diagram` - 用于方法上右键查看代码流程的,主要帮助大家理解代码。 +- [Docker](https://www.docker.com/) - 可本地安装,不需要非得购买云服务器,也可以满足学习诉求。 +- [natapp](https://natapp.cn/) - 内网穿透工具,相当于你开启后,可以分配给你一个域名,让外网访问到你的服务。 + +--- + +好啦🌶,我们的课程列车🚄😆🙈💃🙆🏃🏻‍♀️即将启动,你准备好了吗🐴! \ No newline at end of file diff --git a/docs/md/zsxq/project/dynamic-thread-pool.md b/docs/md/zsxq/project/dynamic-thread-pool.md new file mode 100644 index 000000000..f139ec434 --- /dev/null +++ b/docs/md/zsxq/project/dynamic-thread-pool.md @@ -0,0 +1,97 @@ +--- +title: 动态线程池组件实现 +lock: no +--- + +# 动态线程池组件实现 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/nSebo](https://t.zsxq.com/nSebo) + +>沉淀、分享、成长,让自己和他人都能有所收获! + + + +大家好,我是技术UP主小傅哥。 + +时至今日,小傅哥已经在职场工作了`10年+`了,最近的一次职场合同也签了终身合同。工作这么多年,一直都很顺利,既没有受到卷影响,也没有受到每年都喊的”寒冬“影响。因为我学会了如何走到腰部以上! + +
+ +
+ +**走到腰部以上有什么秘诀?** + +说白了,咱们这么多人的地方,哪个行业都挺卷的。不拿出点本事走到腰部以上,自然就是感受到寒冬、不好过。而我看过的大部分伙伴,在职场中只是完成业务开发,那么1年、2年、3年,真到要晋升、跳槽、述职、答辩的时候,会发现自己哪哪都讲不出东西。而这就是我的「法宝」,从一开始工作,就不只是完成业务诉求,还不断地创新和做各类的组件,在一次次锻炼中让自己具备承接任何事项的架构和开发能力。 + +就像我们工作中所需的`JSON序列化组件`、`监控组件`、`熔断组件`、`缓存扩展组件`、`ES-ORM组件`、`慢查询监控组件`、`IDEA Plugin创建`、`数据压缩组件`等等,一开始根本不是有专门的团队做的,而是有一个喜欢创新的人自己独立完成的,在扩展到各个部门使用。那晋升怎么会少的了这个人,又怎么会让他感受寒冬。 + +这样的技术类组件小傅哥已经做了很多,今天在给启动一个新的 **《动态线程池组件》**,美团到店2020年,就有一款这样的组件分享,今天小傅哥教你如何手把手开发。 + +>文末获得5个业务项目 + 5个组件项目,1个源码学习,4套基础教程,帮助大家提升编程能力。 + +## 一、能学到啥 + +工作这么多年我发现,那些有晋升的,加薪多的,没有一个是不造轮子的!一直写业务的,踏实写业务的,就只能排队等着安排了。是因为做一些组件轮子,解决的是所有人的共性问题,这个价值非常大。并且在组件项目中你能掌握很多知识,如; + +- SpringBoot Starter 的设计和实现手段,以一个什么入口方式加载和启动组件。 +- 线程池核心信息的获取,核心线程数、最大线程数、活跃线程数、队列类型、队列任务数、队列剩余可用任务数,通过这些信息的学习和开发,也会更多的了解线程池。 +- 设计 Redis 版本的注册中心,通过 SDK 上报方式到 Redis 注册中心,做统一的管理,让各个接入动态线程池的组件都能统一管理。 +- 掌握 Redis 的发布订阅能力,通过推送指定的主题,让对应的应用上的 sdk 监听和处置线程池。 +- 定时任务 Scheduling 在组件中的启动和使用。*组件项目与日常业务项目不同,不能全都是硬编码* +- 简单前端页面的设计和开发,管理动态线程池。以及开发测试工程验证线程池使用。 + +## 二、项目介绍 + +本次带着大家做的这款动态线程池组件项目,也是各个中大厂中都非常常见的组件能力。通过这样的学习,以中大厂的经验补充自身的技术积累,让自己的简历和职业生涯都有东西可讲。 + +我们知道,线程池(Thread Pool),是一种基于池化思想管理线程的工具,用于降低资源消耗、提高响应速度、提高线程的管理性。池化技术的引入,可以有效的减少线程频繁申请/销毁和调度所带来的额外开销。对于池化思想,我们还能看到;内存池、连接池、化粪池。 + +但在实际的工作中,线程池使用的场景非常多,但线程池的参数并不好一次就配置好,同时需要做监控处理,知道整个线程的消耗情况。根据IO密集型,CPU密集型不通过的任务差异,做压测验证调整。所以有一款动态线程池是非常重要的。 + +### 1. 查看线程池 + +
+ +
+ +### 2. 修改线程池 + +
+ +
+ +### 3. 线程池工程 + +
+ +
+ +>有了这一套组件的学习,你可以搭配到任何一个你做的业务项目中使用。无论是在公司中,还是在面试写简历里。都能让你展示出非常不错的技术体现度。 + +## 三、课程大纲 + +**不同于网上项目,这个项目是一步步,一个个章节的带着大家从0到1的全程视频的方式,进行分析、设计和开发。是一个纯手把手教大家学习实战技术的项目!** 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。 + +
+ +
+ +- 第1节:动态线程池需求分析 + 演示 +- 第2节:从0开始,搭建组件工程 +- 第3节:采集线程池配置数据 +- 第4节:线程池数据上报(Redis 注册中心) +- 第5节:订阅发布消息,变更线程池 +- 第6节:管理端工程搭建,提供接口 +- 第7节:开发前端页面,对接接口 + +## 四、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:大营销、OpenAI 应用、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发、支付SDK等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +> [🧧加入](https://bugstack.cn/md/zsxq/other/join.html) 这样成体系的全量项目学习,放在一些平台售卖,至少都要上千块。但小傅哥的星球,只需要100多,就可以获得大厂架构师对你手把手教学! + +**在今年的面试中,星球帮助众多伙伴拿到**`微信支付`、`京东科技`、`度小满`、`蚂蚁金服`、`Lazada(电商优惠营销)`、`快手`、`美团到店`等Offer,还有的校招生薪资最高年包到45w! diff --git a/docs/md/zsxq/project/group-buy-market.md b/docs/md/zsxq/project/group-buy-market.md new file mode 100644 index 000000000..7cf2b9928 --- /dev/null +++ b/docs/md/zsxq/project/group-buy-market.md @@ -0,0 +1,132 @@ +--- +title: 拼团支付平台系统 +lock: no +--- + +# 《拼团交易平台系统》- 为各类交易场景,提供拼团服务。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/Yfbwo](https://t.zsxq.com/Yfbwo) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +🌻 讲道理,为了提升能力冲击面试,不做点C端场景,不搞透业务流程,只是包个壳的小demo项目,真的进入面试官眼不了一点。面试官更喜欢的是他自己做过的真实的业务场景,这些东西都是非常成熟的业务和可上线使用的系统设计。所以,多做点这样的东西,才是提升能力的东西。 + +
+ +
+ +**那咱们做什么项目呢?🤔** + +在小傅哥的项目版图中,为大家提供了;`下单支付(支付宝、微信、蓝兔)`、`抽奖活动`、`积分兑换`、`行为返利`等,那么咱这次再做一个与支付相关的,中大厂都有的,互联网中必备的项目 **《拼团交易平台系统》** - 为各类交易场景,提供拼团服务。 + +在你看到的`拼多多`、`京东`、`腾讯`,都有很多的拼团场景。因为拼团可以在,设置折扣低价的情况下,激励用户分享到自身的圈子范围,完成拉新一起成单,提高交易完成量。所以这是一个互联网中必备的营销类系统。 + +
+ +
+ +跟着小傅哥(大厂架构师)学习,早些了解真正的真实业务项目,成为这个圈子里的技术佼佼者。横卷Offer! + +>🧧 文末提供了获取项目课程和代码。包括;已完结的6个业务项目、7个组件项目、以及各类基础教程。 + +## 一、能学到啥 + +小傅哥带着学习的项目,主打一个真实。全程从0到1,带着学习。直至完成开发再部署上线,并提供上线系统的域名配置、前端埋点、后端监控等一些列的真实实操技能。所以你能学习到的也是非常丰富的。 + +- 【前端】熟练使用,HTML、Div、CSS、React、Typescript 等前端编程技术。 +- 【前端】掌握 fetch 方式对后端接口的调用,处理相关的逻辑数据。 +- 【后端】熟练搭建项目工程,学习工程分层结构概率和设计思路。掌握更多的六边形、洋葱、整洁架构。`提高简历技术亮点` +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对框架源码所提供的扩展接口具备运用能力。 +- 【后端】熟练使用 SpringCloud 核心微服务分布式技术栈,包括:Fegin、Sentinel、Nacos、熔断、限流、降级等。`先做功能,后面逐步添加。` +- 【后端】熟练使用大厂中常用的设计模式手段和设计原则技术,对各类场景的方案设计和落地能力,深度提高自身编码思维和开发技术能力。 +- 【后端】深度学习复杂场景的架构设计、编程思维,如果处理系统功能的边界和上下文的维护。—— 这些东西一定是从实践中才能学习到的。 +- 【后端】熟练使用 Mock 单测工具、JMeter 压测工具,增强代码交付质量。 +- 【后端】熟练掌握异常、枚举、错误码的定义和使用,并学习到如何合理打印服务日志,便于问题排查。 +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练使用 Nginx 配置转发服务,并能申请ssl配置https服务。 +- 【运维】熟练使用 Grafana 监控系统,对系统的 JVM、磁盘、Tomcat、应用(QPS、响应时间、调用量)完整监控。 + +此外,小傅哥会把系统开发过程中的思考、设计、编码,录制⏺成完整的**全程手把手带着学习的视频**,让大家可以学习到的更多、更细、更深! + +> 学项目,就要学一些有深度的、流程完善的、而不是连业务流程都串不下来的项目。这样的小demo项目,面试的时候,也会很容易被面试官攻击。 + +## 二、项目介绍 + +本次项目是一个包含 `前后端 + Dev-Ops` 的全栈式综合编程实战项目,完成后会与小傅哥星球「码农会锁」的其他项目进行对接使用,以此学习真实的微服务对接。如;之前的`大营销 + OpenAI` 完成上线部署,体验地址:[https://openai.gaga.plus](https://openai.gaga.plus) + +C端场景项目的特点,就是感觉前端展示的很少,但后端往往要有一大的流程等着实现。如;你要认领任务,邀请用户、完成既定目标、驱动流程流转、结算结果或逆向结果。所以这也是面试中,面试官喜欢扣场景细节的部分。 + +在我们完成`拼团`项目后,会在星球的小型支付单独一个分支进行验证,以及对接到 OpenAI 业务项目上。如图示意(后续会做更漂亮的UI); + +
+ +
+ +>体验地址:[https://openai.gaga.plus](https://openai.gaga.plus) + +### 1. 产品流程 - 核心流程 + +
+ +
+ +### 2. 功能流程 - UML + +如图这是一套拼团业务功能的研发设计流程图,涵盖了功能的流转细节。后续的功能实现会以这个为参考进行设计。 + +
+ +
+ +> 很多的学习就是这样,要把细节夯实。不用贪多,但要求精。 + +## 三、课程计划 + +课程在`24年11月16日`开更,预计在30-40节课程。全程视频手把手带着做,即使是小白也能跟着学习下来。`实习/校招生也是小白,在公司也是直接快速进入项目理解和承接需求。` + +课程地址:[https://t.zsxq.com/Yfbwo](https://t.zsxq.com/Yfbwo) + +- 拼团支付营销平台介绍 +- 第1部分:系统设计 + - 第1-1节:拼团需求分析 + - 第1-2节:拼团库表设计(7~10张表) + - 第1-3节:研发系统设计(建模、架构) +- 第2部分:服务实现 + - 第2-1节:初始工程搭建 + - 第2-2节:拼团活动实现 + - 第2-3节:拼团规则设计 - 切量、黑白名单等、免拼、最低成团、次数限制、达成目标 + - 第2-4节:折扣计算规则 + - 第2-5节:人群标签设计 - 自动跑数据服务 + - 第2-6节:拼团规则服务 + - 第2-7节:参与拼团设计 + - 第2-8节:拼团记账实现 + - 第2-9节:拼团回调实现 + - 第2-10节:拼团结果记录 - 接收外部拼单数据 + - 第2-11节:拼团超时失败检测任务 - 退单流程 + - 第2-12节:人群标签生成任务 + - 第2-13节:外部接口设计 + - 更多随课程揭晓 ... +- 第3部分:外部对接 + - 第3-1节:简易对接案例 + - 第3-2节:对接小型支付,拼团下单 + - 第3-3节:对接OpenAI大营销,拼团下单 +- 第4部分:开发运维 + - 第4-1节:构建打包部署 + - 第4-2节:HTTPS(ssl)、后端监控、前端监控 + +>课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! + +## 四、加入学习 + +跟着小傅哥学习,不会浪费时间,不会走小道弯路。全程做技术兜底,遇到的各种问题都能帮你解决。包括你学习时候的代码bug,可以把代码提交到星球,我来帮你调试。最终把兄弟们送到各个竞争赛道的头部,拿到最牛的薪资待遇 Offer! + +
+ +
+ +>🧧 [加入](https://bugstack.cn/md/zsxq/other/join.html) 每年招聘,都能帮助兄弟们规划好学习路线,卷出最好的 Offer!星球里还有500份评审过的简历记录,看过后,就知道怎么写好简历了。 diff --git a/docs/md/zsxq/project/im.md b/docs/md/zsxq/project/im.md index 1122bc2a1..364ab13f0 100644 --- a/docs/md/zsxq/project/im.md +++ b/docs/md/zsxq/project/im.md @@ -7,6 +7,7 @@ lock: no 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0dbJRLamf](https://t.zsxq.com/0dbJRLamf) - 课程入口 >沉淀、分享、成长,让自己和他人都能有所收获! diff --git a/docs/md/zsxq/project/local-task-message.md b/docs/md/zsxq/project/local-task-message.md new file mode 100644 index 000000000..0bd33540a --- /dev/null +++ b/docs/md/zsxq/project/local-task-message.md @@ -0,0 +1,99 @@ +--- +title: 本地任务消息组件 +lock: no +--- + +# 《本地任务消息组件》- 为事务和消息推送(HTTP、MQ),提供最终一致性解决方案 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/Pfekb](https://t.zsxq.com/Pfekb) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +**你说气人不**,每每公司`晋升提报`和`加薪`的时候,总是那些手里有俏活的👬🏻兄弟。业务项目虽然是根基,但大家都做也就拉不开差距,而技术类组件、通用服务、功能平台,倒不是所有人都能搞的,这类结合业务场景的提取共性问题,凝练成通用解决方案的项目,可以开发一个就解决了全大部门的问题,所以做这类项目很亮眼! + +
+ +
+ +**这类项目也有门槛!** + +看着好像做个技术组件也没啥,但实际真想做的时候,你会发现你不知道什么服务可以被抽取为通用组件,不具备这样的经验和眼界。同时即使知道做啥了,也不知道如何结合像 Spring、MyBatis 源码能力,开发出一个通用的组件来,能让其他业务项目引入使用。 + +有人讲这东西不就是重复造轮子吗?🤔 还真不是,重复造轮子,指的市面上有的,或者公司里有个基础平台组有的。但这类的轮子往往不是深度结合业务的,而是那种无业务属性的功能逻辑的轮子,比如;rpc、xxl-job、mq 等。但一个业务组,他所需要的是解决通用业务场景问题的轮子,但这类东西又不属于基础平台研发组,所以往往都是业务组自己来解决这类场景问题。 + +所以,让自己具备开发组件的能力,是非常非常重要的,这即是抽象业务也是驾驭源码的能力体现。 + +小傅哥已经为大家提供了非常多的通用组件项目,如;`扳手工程(DCC动态配置中心、设计模式、动态限流、任务调度)`、`BCP 透视业务监控`、`动态线程池`、`支付SDK组件`、`SpringBoot Starter (16个合集)`、`IntelliJ IDEA Plugin 插件开发能力(具备这个的,开发了不少AI类组件)`。 + +这次小傅哥给大家再加一个新的组件《本地任务消息组件》,该组件解决业务场景远程调用HTTP或推送MQ消息,最终一致的问题。 + +## 一、能学到啥 + +【架构】掌握 DDD 分层与端口-适配器模式,清晰划分 domain/infrastructure/trigger/config 模块,提升可维护性与扩展性。 +【后端】学习注解+AOP 方式受理任务消息,结合事务边界进行统一处理,理解 `@LocalTaskMessage` 与切面配合的落地实践。 +【后端】掌握本地消息表设计与分片扫描策略(按门牌号 houseNumber 分片),实现高效拉取与顺序处理,提升系统可靠性。 +【后端】熟悉 Spring Event 事件驱动与异步消费,使用 `ApplicationEvent`+`@EventListener`+`@Async` 实现解耦通知链路。 +【后端】实践策略模式实现可插拔通知能力,支持 HTTP 与 RabbitMQ 两种通知通道,并在成功/失败时更新任务状态。 +【后端】熟练使用 OkHttp3 与 Retrofit2 统一封装 HTTP 网关,掌握动态 URL、Header、Body 的组合与异常处理。 +【后端】了解 RabbitMQ 事件发布的可选依赖注入方式,避免未配置 MQ 时的强依赖导致应用启动失败。 +【配置】掌握 `@ConfigurationProperties` 驱动的多任务组动态调度配置,支持 cron 与 fixedDelay 两种触发方式,并可配置批次大小 limit。 +【运维】学习 `ThreadPoolTaskScheduler` 的线程池化调度管理,合理设置线程名与池大小,提升任务调度的可观测性与稳定性。 +【数据】掌握原生 JDBC 访问与 DAO 封装,完成插入、状态更新、分片条件查询、最小游标查询等落地实现。 +【测试】通过示例命令对象 `TaskMessageEntityCommand` 的构建与调用,理解入参约定、枚举策略与配置对象的协作。 +【实践】提升异常、日志与枚举的综合使用能力,建立稳定的错误处理。 + +## 二、项目介绍 + +**《本地消息任务组件》** 项目,是以自定义注解 AOP 切入或编程的方式,动态化完成数据库表事务和消息推送(HTTP、MQ),达到最终一致性的目的。 + +>在没有这样的组件的时候,为了完成业务流程的同时,在发送一个MQ消息或则远程调用 HTTP 操作,都需要自己写一个本地消息表,之后还要维护消息表的扫描补偿。 + +操作方式如图; + +
+ +
+ +- 用户可以选择通过注解或者直接调用组件服务的方式进行使用。也就不用业务项目工程再维护关于本地消息表的写入和 MQ 或者 HTTP 的处理和补偿了。 +- 注解方式,会自动获取入参,入参需要为 TaskMessageEntityCommand 对象,它可以是某个入参的对象。之后配置 req.command 也可以获取。 + +## 三、产品方案 + +### 1. 产品概述 + +本地任务消息组件基于 Spring 框架能力,设计并实现了通用功能内核,便于集成到各类业务系统中。在业务系统中,组件支持在事务内完成数据写库的同时,写入一条本地消息记录(需在业务系统数据库中创建符合组件规范的本地消息表)。写入完成后,组件同步推送 Spring 事件,触发事务外的异步处理,如 MQ 消息发送或 HTTP 回调。即使异步处理失败,组件内置的本地消息表定时任务(支持自定义配置“门牌号”多任务并行扫描,提升扫描吞吐量)会持续检测并重试通知,确保消息最终一致性和业务流程的可靠执行。 + +### 2. 技术架构 + +
+ +
+ +- 首先,Local Task Message 任务消息组件,是以解决通用业务场景中的,本地数据库事务和外部MQ/HTTP调用一致性问题而设计实现的。让上游业务系统,不需要在每个流程中,都要做大量的重复编码。而是通过注解或者直接调用组件内核服务即可完成消息的通知操作。 +- 之后,Local Task Message 任务消息组件,并不是一个单纯的工具性功能,而是剩余一个领域服务内核,它具备完整的领域功能,具备操作数据库表的能力,以及接收 Spring Event 事件,对接 MQ、HTTP 完成和外部的交互处理。 +- 然后,上游系统在使用这套服务时,只需要配置好对应的本地消息表(一个事务下,连的同一个库),以及引入组件和完成yml配置,即可直接使用。 + +### 3. 功能流程 + +
+ +
+ +- 引入本地消息组件后,以用户开发dao入库操作为开始,可以通过注解或者调用组件服务 `ILocalTaskMessageHandleService` 驱动同一个事务下,进行消息推送。 +- 切面的方式,会更为优雅简洁,不需要用户自己在维护调用关系。 +- 整个操作会由组件自行处理写库操作,基于 Spring Event 的监听和通知,触发消息推送。完成 http、mq 的调用逻辑。同时还有基于门牌号扫描的逻辑,增强吞吐量。 + +## 四、课程目录 + +现课程已全部录制完成,接下来会日更📅项目💐; + +
+ +
+ +- 6节课程,全程视频手把手,带着你分析需求,编写代码。快速完成一个组件项目。 +- 课程代码,以互联网公司方式逐步拉分支开发,你可以学习到正规的编码操作。 diff --git a/docs/md/zsxq/project/lottery.md b/docs/md/zsxq/project/lottery.md index f71fc1b3d..8b3e9770d 100644 --- a/docs/md/zsxq/project/lottery.md +++ b/docs/md/zsxq/project/lottery.md @@ -7,7 +7,7 @@ lock: no 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) -
星球:[https://t.zsxq.com/05zj2niYR](https://t.zsxq.com/05zj2niYR) +
星球:[https://t.zsxq.com/0dzlIyK5Z](https://t.zsxq.com/0dzlIyK5Z) - 课程入口 > 沉淀、分享、成长,让自己和他人都能有所收获!😄 diff --git a/docs/md/zsxq/project/ltzf-sdk-java.md b/docs/md/zsxq/project/ltzf-sdk-java.md new file mode 100644 index 000000000..65faee124 --- /dev/null +++ b/docs/md/zsxq/project/ltzf-sdk-java.md @@ -0,0 +1,94 @@ +--- +title: 蓝兔支付SDK设计和开发 +lock: no +--- + +# 又完结一个新项目!手把手,教你开发支付SDK和对接使用 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/19WqNkhr2](https://t.zsxq.com/19WqNkhr2) + +>沉淀、分享、成长,让自己和他人都能有所收获! + +大家好,我是技术UP主小傅哥。 + +经历了近一个月的实习招聘中,看到了不少伙伴的简历项目描述中(商城、外卖、配送等)都有支付模块,但在接下来的职责描述里又一点都没体现支付的相关内容。这是为啥呢?🤨 `我胃口都起来了,你说没有!` + +
+ +
+ +**求职者:“我坦白,我其实没做支付!”** + +在面试中详细了解得知,不少伙伴压根是没有做支付的。而是一个假的模拟支付,修改数据库状态,当做支付而已。这样做有个很大的问题就是没法全面的了解支付流程,包括在`掉单`、`幂等`、`透传`、`回调`等方面的业务是很难清晰的理解的。所以在面试过程中也就压根不知道支付这一块。但不写支付,又觉得整个项目不完整,并且很多公司面试都会问一些支付的内容。那怎么办😰呢? + +小傅哥在带着大家做的项目和小场景中,有做过微信支付,支付宝沙箱支付,但想做真实的支付一般是需要个体户或者公司主体的。不过好在市面还有一些专门提供给个人使用的支付,比如;蓝兔、虎皮椒、PayJS。这些支付中,小傅哥测试验证了蓝兔支付,为它提供了一款SDK,让大家像使用微信支付一样简单的使用蓝兔支付。 + +所以,小傅哥要带着大家做个`一举两得`的事。不是对接支付吗,那我就带着你手把手的基于蓝兔支付文档,开发一款SDK再对接使用。让你一个简历中既有支付模块,也有支付 SDK 开发经验编写。 + +>文末有加入学习方式,可以获得9个实战项目学习,包括5个业务项目、4个组件项目。 + +## 一、能学到啥 + +现在的简历,玩的都是综合实力。有业务项目再加一个技术组件项目,分别举证自己在不同方面的能力积累,这样的简历筛选中比较容易通过,面试中也能让面试官刮目相看。因为组件项目非常好体现设计能力和设计模式的运用; + +- 蓝兔支付官网API接口分析,包括;扫码支付、H5支付、公众号支付、小程序支付、订单退款、查询订单、支付通知等功能。 +- 通过 okhttp3、retrofit2 框架,对接官网 http api 接口。框架的能力可以让支付对接更加标准、清晰,易扩展。 +- 不同的支付方式会有自己的签名字段,所以通过聚合支付对象,提供签名处理。 +- 每个支付方式提供独立的接口实现,保证单一职责,不同的支付就是每一个单独的策略实现。 +- 提供工厂服务,封装支付 API 服务的创建,统一管理。 +- 通过 SpringBoot 工程,配置支付 sdk 对接使用。 +- 使用 natapp 内网穿透工具,验证支付回调。 +- 熟练使用 Git、Gitcode 提交代码、合并代码、切换分支等操作。 + +综上,所有的编码小傅哥全程录制视频,一行行带着实现。你可以非常清楚的学习到这些设计思考、编码经验的实战技能。学习了这样一套技术,以后在开发同类的内容都是游刃有余的。 + +## 二、项目介绍 + +本次项目采用全程视频手把手的方式进行教学,包括;官网文档阅读分析、运用 openai 写案例代码、工具转换对象、IntelliJ IDEA 快捷键使用、工程的搭建、场景设计模式分析和运用、工程代码提交合并等实战技能。 + +### 1. sdk工程 + +
+ +
+ +### 2. sdk使用 + +
+ +
+ +### 3. 交易订单 + +
+ +
+ +>这是一套完整的SDK的开发和使用流程,有了这样的学习,你的项目中就可以真实的对接上支付了! + +## 三、课程大纲 + +**不同于网上项目,这个项目是一步步,一个个章节的带着大家从0到1的全程视频的方式,进行分析、设计和开发。是一个纯手把手教大家学习实战技术的项目!** 大家可以先看看课程的大纲,就知道可以学习到哪些东西了。 + +- 第1节:支付SDK需求分析和初始化工程 +- 第2节:分析和验证支付接口 +- 第3节:支付API对接设计 +- 第4节:支付API工程设计 +- 第5节:支付订单信息处理API +- 第6节:SpringBoot 引入支付 SDK + +
+ +
+ +> 在小傅哥的星球有,有 openai 应用项目,对接了微信支付。可以下单购买对话额度。这些项目可以综合来学习,让大家快速提高应用实战技能。 + +## 四、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括:大营销、OpenAI 应用、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +> [🧧加入](https://bugstack.cn/md/zsxq/other/join.html) 这样成体系的全量项目学习,放在一些平台售卖,至少都要上千块。但小傅哥的星球,只需要100多,就可以获得大厂架构师对你手把手教学! diff --git a/docs/md/zsxq/project/openai-code-review.md b/docs/md/zsxq/project/openai-code-review.md new file mode 100644 index 000000000..35a446461 --- /dev/null +++ b/docs/md/zsxq/project/openai-code-review.md @@ -0,0 +1,218 @@ +--- +title: OpenAI 代码自动评审组件 +lock: no +--- + +# OpenAI 代码自动评审组件 —— 小傅哥写的代码,会自动评审啦! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/gYEVX](https://t.zsxq.com/gYEVX) - 课程入口 + +>沉淀、分享、成长,让自己和他人都能有所收获! + +大家好,我是技术UP主小傅哥。 + +👬🏻哥们,你写的代码有没有Bug?有Bug?那`小卡拉米`测试没覆盖到,都整出线上事故了😂!其实强如大厂架构师,开发的代码也会存在一些遗漏的地方,所以要有代码评审、测试、预发验证等环境来保证交付质量。但小傅哥只有自己,还维护了 [bugstack.cn](https://bugstack.cn) 社群,上百个工程代码,也花点钱雇个人评审代码?—— **但这玩意,花钱,不行!我得整点技术活!** + +
+ +
+ +**工欲善其事,必先利其器。** + +其实我想要的,就是这么简单!当我提交合并分支的代码,则触发代码评审,并写入评审日志文件。完成后发送公众号模板消息通知,点击<详情>查看评审细节。这样我就知道本次开发的代码是否有问题啦,可以说是美滋滋! + +其实这样一套东西,不只是小傅哥需要,就连企业中也是非常有需要的。通过自动化评审来辅助人工评审,可以把代码的交付质量拉倒一个更高的层次,也能尽可能的减少线上事故。~~没有事故 == 开猿节流~~ + +
+ +
+ +> 👣 接下来,小傅哥就来介绍下这套组件的配置使用,同时想学习这样组件开发的伙伴也可以加入小傅哥的社群。 + +## 一、使用方法 + +本套组件是小傅哥基于 `GitHub Actions` + `OpenAI(ChatGLM)` + `Git/GitHub` + `公众号模板消息` 串联出从代码提交获取通知,Git 检出分支变化,在使用 OpenAI 进行代码和写入日志,再发送消息通知完成整个链路。 + +好,那接下来,小傅哥就带着你做下 OpenAI Code Review 的配置,整体配置如下图;—— 下面👇🏻会告诉你在哪配置。 + +
+ +
+ +### 1. 申请 ChatGLM + +- CHATGLM_APIKEYSECRET: [https://open.bigmodel.cn/usercenter/apikeys](https://open.bigmodel.cn/usercenter/apikeys) - 申请方便&还挺好用。也可以对接其他模型。 +- CHATGLM_APIHOST:https://open.bigmodel.cn/api/paas/v4/chat/completions + +### 2. 申请 GitHub 仓库 + +组件是基于 Github Actions 实现的,所以要提供一个你的 Github 工程库和一个评审 Github 工程库写入日志的日志库。如果你有其他代码库,也可以按照对应代码库的 CI/CD 标准进行实现。 + +- 工程库:[https://github.com/xfg-studio-project/openai-code-review-test](https://github.com/xfg-studio-project/openai-code-review-test) - 你创建一个自己的,并提交代码。 +- 日志库:[https://github.com/xfg-studio-project/openai-code-review-log](https://github.com/xfg-studio-project/openai-code-review-log) - 你创建一个自己的。 + +### 3. 申请 GitHub Token + +地址:[https://github.com/settings/tokens](https://github.com/settings/tokens) + +
+ +
+ +- 创建后,保存生成的 Token,用于配置到 GitHub Actions 参数中 + +### 4. 微信公众号配置 + +- 申请地址 [https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index](https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index) + +
+ +
+ +- 这个测试公众号等同于企业公众号,有对应的模板消息。 +- 申请后,你就会获得 appID、appsecret、tourse - 就是谁关注了公众号,就会展示一个分配的微信号,推送模板消息就是给这个用户推送。 +- 模板消息,自己新建一个。之后就获得ID。消息格式如下; + +```java +项目:{{repo_name.DATA}} 分支:{{branch_name.DATA}} 作者:{{commit_author.DATA}} 说明:{{commit_message.DATA}} +``` + +### 5. GitHub Actions 配置 + +#### 5.1 配置参数 + +地址:[https://github.com/xfg-studio-project/openai-code-review-test/settings/secrets/actions](https://github.com/xfg-studio-project/openai-code-review-test/settings/secrets/actions) - 换成你的项目工程,进入到 Setting -> Secrets and variables -> Actions -> Repository secrets -> New repository secret + +
+ +
+ +
+ +
+ +| Name | Secret | +| -------------------- | ------------------------------------------------------------ | +| CHATGLM_APIHOST | https://open.bigmodel.cn/api/paas/v4/chat/completions | +| CHATGLM_APIKEYSECRET | `39580e34e175019c230fdd519817b381.F*****pzqiRDcAk` - 使用你的 | +| CODE_REVIEW_LOG_URI | [https://github.com/xfg-studio-project/openai-code-review-log](https://github.com/xfg-studio-project/openai-code-review-log) - 使用你的 | +| CODE_TOKEN | `ghp_KWBsnzwoQR4OXO4o3XjIJjVU****GsS1` - 使用你的 | +| WEIXIN_APPID | `wx5a228ff69e2****1f` - 使用你的 | +| WEIXIN_SECRET | `0bea03aa1310bac050a******8703928` - 使用你的 | +| WEIXIN_TEMPLATE_ID | `l2HTkntHB71R4NQTW77UkcqvSOIFqE_bss1DAVQSybc` - 使用你的 | +| WEIXIN_TOUSER | `or0Ab6ivwmypESVp_bYuk92T****` - 使用你的 | + + +#### 5.2 配置脚本 + +
+ +
+ +```java +name: OpenAiCodeReview + +on: + push: + branches: + - '*' + pull_request: + branches: + - '*' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + fetch-depth: 2 # 检出最后两个提交,以便可以比较 HEAD~1 和 HEAD + + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + + - name: Create libs directory + run: mkdir -p ./libs + + - name: Download openai-code-review-sdk JAR + run: wget -O ./libs/openai-code-review-sdk-1.1.jar https://github.com/xfg-studio-project/openai-code-review-log/releases/download/v1.1/openai-code-review-sdk-1.1.jar + + - name: Get repository name + id: repo-name + run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV + + - name: Get branch name + id: branch-name + run: echo "BRANCH_NAME=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV + + - name: Get commit author + id: commit-author + run: echo "COMMIT_AUTHOR=$(git log -1 --pretty=format:'%an <%ae>')" >> $GITHUB_ENV + + - name: Get commit message + id: commit-message + run: echo "COMMIT_MESSAGE=$(git log -1 --pretty=format:'%s')" >> $GITHUB_ENV + + - name: Print repository, branch name, commit author, and commit message + run: | + echo "Repository name is ${{ env.REPO_NAME }}" + echo "Branch name is ${{ env.BRANCH_NAME }}" + echo "Commit author is ${{ env.COMMIT_AUTHOR }}" + echo "Commit message is ${{ env.COMMIT_MESSAGE }}" + + - name: Run Code Review + run: java -jar ./libs/openai-code-review-sdk-1.1.jar + env: + # Github 配置;GITHUB_REVIEW_LOG_URI「https://github.com/xfg-studio-project/openai-code-review-log」、GITHUB_TOKEN「https://github.com/settings/tokens」 + GITHUB_REVIEW_LOG_URI: ${{ secrets.CODE_REVIEW_LOG_URI }} + GITHUB_TOKEN: ${{ secrets.CODE_TOKEN }} + COMMIT_PROJECT: ${{ env.REPO_NAME }} + COMMIT_BRANCH: ${{ env.BRANCH_NAME }} + COMMIT_AUTHOR: ${{ env.COMMIT_AUTHOR }} + COMMIT_MESSAGE: ${{ env.COMMIT_MESSAGE }} + # 微信配置 「https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index」 + WEIXIN_APPID: ${{ secrets.WEIXIN_APPID }} + WEIXIN_SECRET: ${{ secrets.WEIXIN_SECRET }} + WEIXIN_TOUSER: ${{ secrets.WEIXIN_TOUSER }} + WEIXIN_TEMPLATE_ID: ${{ secrets.WEIXIN_TEMPLATE_ID }} + # OpenAi - ChatGLM 配置「https://open.bigmodel.cn/api/paas/v4/chat/completions」、「https://open.bigmodel.cn/usercenter/apikeys」 + CHATGLM_APIHOST: ${{ secrets.CHATGLM_APIHOST }} + CHATGLM_APIKEYSECRET: ${{ secrets.CHATGLM_APIKEYSECRET }} +``` + +- 把以上脚本粘贴到你的 GitHub Actions 中,之后保存。 +- 接下来你提交代码就会自动触发代码评审啦。💐 赶紧玩一下吧!看看智能的AI评审能力! + +>对于这里实现的内容,小傅哥会通过代码实践课程,手把手的给你讲解。从方案设计、代码串联、重构编码,一步步的带着你全部搞懂! + +## 二、能学到啥 + +公司里其实也总有人做一些这样的创新组件,这些东西和业务没关系,也不是个人的KPI压力,但往往做这样的东西的伙伴就有更多的晋升资格和加薪待遇。因为我就是这个人 哈哈哈 死鬼! + +这样的组件项目,本身就是一种技术创新应用来解决实际业务问题,提高交付质量。而不是那种野蛮的一遍遍重写RPC框架。所以学习一套这样的东西是非常有用的。那么在这套东西你可以学习到; + +- 一整套的设计方案分析和相应的技术问题处理手段,这个思考方式很重要。 +- GitHub Actions 的使用机制,它的一些超级强大的用途和使用方式。 +- OpenAI ChatGLM 对接使用,用AI来做代码评审。 +- 微信公众号的模板配置和API对接使用,运用 API 完成消息触达。 +- 通过代码完成 Git 命令使用,检出代码分支,在通过 OPenAI 完成代码评审。整个过程先使用大家常用的流水账方式开发代码,再带着大家重构设计,让代码变得更加清晰。 +- 打包 Jar 包,并把相关的组件一起打包,之后让 GitHub Actions 分别通过 Main 函数调用、mvn 构建使用、Jar 下载使用,多种方式学习整个过程(而不是只最终的结果,过程非常重要)。 + +> 初次之外,小傅哥还会在整个过程教会你 IntelliJ IDEA 操作技巧、快捷键使用、编码思维等。赶紧加入学习下! + +## 三、加入学习 + +**星球「码农会锁」** 实战项目中有非常多的运用。还包括:大营销、OpenAI 应用、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发、支付SDK、动态线程组件、透视业务监控等,并还有开源项目学习。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) 这样一套项目,放在一些平台售卖,一个至少都是几百块。但小傅哥的星球,只需要100多,就可以获得全部的学习项目! + +**加入星球**:下载`星球APP`,从星球【课程入口】进入。里面有完整的学习指引,包括;使用说明、代码仓库、专属项目群、学习路线、往期项目。本项目地址:[https://t.zsxq.com/gYEVX](https://t.zsxq.com/gYEVX) diff --git a/docs/md/zsxq/project/openai-sdk-java.md b/docs/md/zsxq/project/openai-sdk-java.md new file mode 100644 index 000000000..763213d1e --- /dev/null +++ b/docs/md/zsxq/project/openai-sdk-java.md @@ -0,0 +1,103 @@ +--- +title: OpenAi SDK 组件项目 +lock: no +--- + +# OpenAi SDK 组件项目 - 我来编写需求文档,再写30%+的代码,剩下的与你一起开发! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/14EPV92wK](https://t.zsxq.com/14EPV92wK) - 课程入口 + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +今天小傅哥将开启一个新计划,带着大家一起干"开源"💐。要说编程能力什么时候提升的最快,就是从开始`承接需求`并思考着`怎么落地`,到最后`开发实现`时,才是成长最快的时候。 + +
+ +
+ +呐,这是一个什么样的开源项目呢?🤔 + +这个开源项目是 **《OpenAI SDK》** 统一大模型标准化对接的技术组件项目,此项目以解决实际市面上的场景为诉求,将 OpenAI、Claude、PalM、文心一言、通义千问、讯飞星火、智谱 ChatGLM、腾讯混元等这些大模型做一个统一的 SDK 对接组件。 + +这个组件项目需要解决很多的差异化问题,又要提供统一的对接标准,既有设计的难点又有落地的复杂。但这样千差万别的统一对接,恰好又是日常开发中所面临的问题。 + +小傅哥这里已经编写了30%+的代码,定义基础框架、对接标准和实现手段; + +
+ +
+ +那么,接下来小傅哥就告诉能学习到哪些设计思维、设计原则、设计模式以及怎么进入学习。—— 这个项目涵盖了很多高级的编码思想。 + +>文末有加入学习方式,以及全体系的技术成长路线说明。 + +## 一、能学到啥 + +讲道理,大部分能在职场中脱颖而出述职晋升的,都不是只写业务代码的,而是解决能团队中同类的场景诉求,凝练共性开发技术组件,让大家可以使用项目。而这样的项目往往工程结构偏小,代码量不大,但很少有 if···else,有的全都是高级编码技巧,所以才会这么有价值。 + +- 【设计思维】:抽象 OpenAI 对接调用过程为会话模型结构,统一管理会话生命周期。 +- 【设计思维】:为所有 OpenAI 类型设计统一对接标准;入参、方法、出参,以 ChatGPT 为标准设计其他对接行为。 +- 【设计原则】:最少知道原则,让调用 SDK 的用户,不要因为调用不同的模型而关心具体的细节。 +- 【设计原则】:迪米特法则,在实现的过程中通过使用执行器衔接会话与各类模型的对接,起到中介者的作用,让会话可以扩展更多的调用方式的同时又不需要过多的了解模型的具体细则。 +- 【设计原则】:单一职责原则,一个具体的模型实现类,只负责当下模型的具体职责。 +- 【设计模式】:工厂模式,封装会话过程中所需的;配置信息(ApiHost、ApiKey)、HTTPClient、模型执行器。 +- 【设计模式】:策略模式,统一定义标准的大模型接口、参数处理器、监听见过处理器,让各类大模型做各自的封装实现。 +- 【设计模式】:在通过单例、模板、建造者,解决实现过程中的细节处理,让整个 SDK 的实现更容易被扩展和迭代。 + +基于这样的 SDK 组件开发实践,在小场景中大量的运用高级编码技巧,可以非常好的帮助大家提高编程思维、锻炼编码能力。 + +## 二、组件设计 + +本次的组件项目,具有非常多的高级编码技巧,同时完成后还可以让很多人使用到这个项目,非常具有成就感。各位参与这个项目的伙伴,都会成为项目代码贡献者。💐 + +### 1. 核心设计 + +
+ +
+ +### 2. 会话模型 - 核心代码 + +
+ +
+ +### 3. 接口实现 - 统一标准 + +
+ +
+ +--- + +很多伙伴在没看过小傅哥的“代码操盘”前,都是;一个接口、一个实现,一个实现,代码一片。一片一片、又一片,代码行数、两三千。所以跟随小傅哥学习你会得到非常多的技术成长,沉淀出自己的核心技术能力。 + +## 三、学习说明 + +加入星球开源项目学习后,Fork 代码到自己的仓库。熟悉工程模型和代码,并调试运行理解整个框架的设计实现。之后开始承接需求并提交代码到自己的仓库。对于自己已经完成运行的调试的代码,可以提交 PR 代码。小傅哥在评审后,会合并你的提交。这样你就成为一个贡献者了,并记录在文档。 + +
+ +
+ +1. 【简单】工程中有标记 `TODO` 标签待开发点,此类的功能比如在A模型中实现了,B、C 模型未实现,可以参考代码开发。 +2. 【中等】阅读模型API文档,补全功能。这部分会从会话的调用,一直到执行器包下对应的实现,开发具体实现。 +3. 【复杂】对未实现对接的模型,阅读API文档,添加对接。 + +以上的所有代码实现,都会以当下工程所提到的设计思维、设计原则、设计模式,来编写具体的代码实现,非常锻炼人。 + +以上开发内容,小傅哥会陆续的提交代码,你可以赶在我的前面实现,这样可以很好和我的开发进行对比,学习设计思想和落地实现。 + +## 四、加入学习 + +**注意**📢,本项目也只是【星球:码农会锁】众多项目中的1个,其他的项目还包括已完结的:OpenAi大模型应用项目、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并且还包括正在进行的**大营销平台系统**。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) 这样一套项目,放在一些平台售卖,一个至少都是几百块。但小傅哥的星球,只需要100多,就可以获得全部的学习项目! + +**加入星球**:下载`星球APP`,从星球【课程入口】进入。里面有完整的学习指引,包括;使用说明、代码仓库、专属项目群、学习路线、往期项目。本项目地址:[https://t.zsxq.com/14EPV92wK](https://t.zsxq.com/14EPV92wK) \ No newline at end of file diff --git a/docs/md/zsxq/project/s-pay-mall.md b/docs/md/zsxq/project/s-pay-mall.md new file mode 100644 index 000000000..1fe387716 --- /dev/null +++ b/docs/md/zsxq/project/s-pay-mall.md @@ -0,0 +1,154 @@ +--- +title: 小型支付电商系统 +lock: no +--- + +# 小型支付电商系统 - 一套项目2套架构开发(MVC+DDD) + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +🌻 在小傅哥带大家做的众多实战项目中,有些伙伴喜欢看 `DDD` 因为面试有的讲,有些伙伴想做 `MVC` 因为虽然知道 `DDD` 有亮点但初次接触怕学起来难。所以,这次我要一套项目,写出两套架构开发。你可以对比着学习,看看不同架构(`MVC&DDD`)如何设计和编码! + +
+ +
+ +**那咱们做什么项目呢?🤔** + +这是一套`小型的支付电商系统`,提取实际生产中核心的真实模块作为咱们的开发需求,同时也是面试中最为常问的流程。包括;`如何微信扫码鉴权登录` + `模板消息通知`、`怎么做支付宝交易打通`、`商品支付掉单如何处理`、`相关的任务补偿怎么操作`等。把这些需求分别通过 MVC 架构、DDD 架构,进行设计实现。让学习的伙伴,对照出不同架构的设计思路和开发差异,即完成业务需求,也提高编程架构思维。—— 同龄人的差异,就是你比别人站的高的时候,略微出手,就是那个赛道的将相王侯 👍🏻! + +两套架构工程的代码已经写完啦(录制视频中)!接下来小傅哥就来介绍下这套项目,你能获得的技术知识。 + +>文末有加入学习方式,还有优惠券可以使用。先到先得! + +## 一、能学到啥 + +这是一套完整具备核心链路的小型项目,我们不在同类编码上反复重复,只关注核心链路。所以你可以花费很少的时间,积累丰富的架构和编程经验。在这套内容学习中,积累核心技术的运用,包括; + +- 【前端】熟练使用,简单 HTML、DIV、CSS,对扫码登录、商品下单页面的构建。 +- 【前端】掌握 fetch 方式对后端接口的调用,处理相关的逻辑数据。 +- 【后端】熟练搭建 MVC 工程项目、理解各个分层模块作用,对 MVC 的设计方法有清楚的认识。 +- 【后端】熟练搭建 DDD 工程项目、以及 DDD 脚手架搭建项目。并对 DDD 设计方法有清楚的认知。 +- 【后端】理解 DDD 架构设计思维,这部分会有大量的内容进行讲解。再结合后续的实战,会对架构有更深入的认识。 +- 【后端】熟练掌握 Spring、SpringBoot、MyBatis 等开发框架技术,并对框架源码所提供的扩展接口具备运用能力。 +- 【后端】熟练使用模板设计模式,对商品下单的流程拆解和实现。 +- 【后端】深度理解登录、支付、下单,全流程的核心设计和实现,而不是那种CRUD学习个DEMO,我们对接真实支付! +- 【运维】熟练使用 Docker 在本地和服务端的配置和部署应用,以及在本地构建前后端镜像。 +- 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 +- 【运维】熟练使用 Nginx 配置转发服务,给前端应用在 Nginx 进行部署。 + +此外,小傅哥会把系统开发过程中的思考、设计、编码,录制⏺成完整的视频,让大家可以学习到的更多、更细、更深! + +## 二、项目介绍 + +本次项目是一个包括 `前后端 + Dev-Ops` 且小型的综合实战项目,基于 SpringBoot、MyBatis、Nginx、Docker、微信公众号、支付宝沙箱等开发的项目。非常适合小白伙伴和有DDD学习诉求的伙伴上手! + +本次项目铺设出来的内容并不大,但具备详细的核心流程,你可以通过一条完整链路学习到 MVC 和 DDD 的开发设计与编码差异。这是非常重要的。 + +### 1. 核心流程 + +
+ +
+ +### 2. 项目工程 + +
+ +
+ +- 一套项目需求,用两套架构开发。小傅哥,是真的想帮你提高架构思维! +- 我会在全程视频手把手的编码过程中,为你讲解 MVC 与 DDD 的设计,它们之间的对象设计,思维方式,编码结构。 + +### 3. 流程设计 + +#### 3.1 登录流程 + +
+ +
+ +#### 3.2 下单流程 + +
+ +
+ +### 4. 运行效果 + +
+ +
+ +### 5. 运行日志 + +```java +24-08-04.11:03:13.922 [http-nio-8092-exec-3] INFO LoginController - 生成微信扫码登录 ticket gQGq8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyT01QWXBJTDBjckcxUlFZbWhDMUgAAgR0765mAwQAjScA +24-08-04.11:11:00.291 [http-nio-8092-exec-7] INFO WeixinPortalController - 接收微信公众号信息请求or0Ab6ivwmypESVp_bYuk92T6SvU开始 + +1722741062 + + +3576043305420816385 + + +24-08-04.11:11:00.305 [http-nio-8092-exec-7] INFO WeixinPortalController - 接收微信公众号信息请求or0Ab6ivwmypESVp_bYuk92T6SvU完成 + +1722741062 + + +3576043305420816385 + + +24-08-04.11:11:12.374 [http-nio-8092-exec-9] INFO LoginController - 生成微信扫码登录 ticket gQGY8DwAAAAAAAAAAS5odHRwOi8vd2VpeGluLnFxLmNvbS9xLzAyeGpIYm8yTDBjckcxUmktbXhDMU8AAgRS8a5mAwQAjScA +24-08-04.11:11:18.541 [http-nio-8092-exec-1] INFO AliPayController - 商品下单,根据商品ID创建支付单开始 userId:or0Ab6ivwmypESVp_bYuk92T6SvU productId:or0Ab6ivwmypESVp_bYuk92T6SvU +24-08-04.11:11:18.581 [http-nio-8092-exec-1] INFO HikariDataSource - HikariPool-1 - Starting... +24-08-04.11:11:18.778 [http-nio-8092-exec-1] INFO HikariDataSource - HikariPool-1 - Start completed. +24-08-04.11:11:19.177 [http-nio-8092-exec-1] INFO AbstractOrderService - 创建订单-完成,生成支付单。userId: or0Ab6ivwmypESVp_bYuk92T6SvU orderId: 3700032384239341 payUrl:
+ + +
+ +24-08-04.11:11:19.178 [http-nio-8092-exec-1] INFO AliPayController - 商品下单,根据商品ID创建支付单完成 userId:or0Ab6ivwmypESVp_bYuk92T6SvU productId:100010090091 orderId:3700032384239341 +``` + +## 三、项目大纲 + +**课程地址**:[https://t.zsxq.com/3X9GA](https://t.zsxq.com/3X9GA) + +- 第1部分:架构理论 + - 第1节:DDD 架构概念 + - 第2节:DDD 建模方法 + - 第3节:DDD 工程模型(含 MVC 对比) +- 第2部分:需求设计 + - 第1节:小型支付商城需求设计 + - 第2节:工程四色建模设计 + - 第3节:库表设计 +- 第3部分:功能实现 - MVC + - 第1节:MVC 工程框架搭建 + 基础配置 + Git 使用 + - 第2节:微信公众号鉴权 + - 第3节:登录功能设计实现 + - 第4节:商品下单 + - 第5节:对接支付 + - 第6节:支付回调处理 +- 第3部分:功能实现 - DDD + - 第1节:DDD 工程框架搭建 + 基础配置 + Git 使用 + - 第2节:DDD 重构,微信公众号鉴权 + - 第3节:DDD 重构,登录功能设计实现 + - 第4节:DDD 重构,商品下单 + - 第5节:DDD 重构,对接支付 + - 第6节:DDD 重构,支付回调处理 +- 第4部分:开发运维 + - 第1节:natapp 内网穿透 + - 第2节:微信公众号,测试平台申请 + - 第3节:支付宝沙箱申请 + - 第4节:发布上线 + +--- + +课程包括;视频、小册、1 对 1 答疑解惑、专属VIP项目交流群,并且提供简历编写模板结构的一条龙🐲服务。让你学习后,直接拉开与还在玩具项目其他人的差距,面试脱颖而出提高竞争力!!! \ No newline at end of file diff --git a/docs/md/zsxq/project/springboot-starter.md b/docs/md/zsxq/project/springboot-starter.md new file mode 100644 index 000000000..9088e131b --- /dev/null +++ b/docs/md/zsxq/project/springboot-starter.md @@ -0,0 +1,118 @@ +--- +title: SpringBoot 中间件设计和开发 +lock: no +--- + +# SpringBoot 中间件设计和开发 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
星球:[https://t.zsxq.com/0d7qkNTdA](https://t.zsxq.com/0d7qkNTdA) - 课程入口 + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +## 一、前言 + +`年纪轻轻,为什么要搞中间件开发?` + +![](https://bugstack.cn/assets/images/middleware/0-0.jpeg) + +五年前,香河`大厂`村,开张大吉。我和弟兄们雄心壮志,坐公交车去面试,谁知道求职不到半个月,每天平均1.3个人挂在八股文造火箭,一年内6个兄弟去了外包。 + +佛祖保佑!算命的说我是“CRUD搬砖996”,不过我不同意。我认为出来混的,是`20K`是`40K`,要由自已决定。 + +你们跟着我的日子最短,底子最薄,路怎么走,让你们自已挑。 + +好了,祝你们,在大厂,一帆风顺! 干杯各位架构师! + +--- + +说到底,为什么要扒开CRUD的表面,深入到核心源码实践学一些中间件开发技能,还不是希望自己对技术栈学习有一定的深度,免得面试时被人忽悠压薪资。就像人家问你: +- 类的代理、反射调用是在什么场景用到的? +- 自定义注解是怎么和切面一起获取到信息使用的? +- 你需要的yml配置信息是如何被SpringBoot加载并初始化的? +- Bean 是如何被注入到 Spring 容器,提供服务的? +- ORM 框架是怎么解决不需要写接口的实现类就能执行CRUD操作的? +- 扰动函数和数据库路由实现中的数据散列有什么关系? +- 分布式任务调度与zookeeper配置中心是怎么联动的? +- 字节码插桩对方法增强怎么拦截程序方法运行时信息? + +**综上**,等等这些技术点可能很多时候你所学到的只能称作为`背答案`、`记结果`,因为没有实操所以过后就忘而且也扛不住面试官的接连发问。 + +**那么**,为了让所有对需要对自己技术栈知识加深,拓展相关技能的实战经验,同时也让感兴趣于薪资高的中间件开发的小伙伴,有一个能入门并上手的教程。特此准备了专栏小册`《SpringBoot 中间件设计和开发》`,欢迎大家加入! + +**全小册19个章节,包括16个中间件的设计和开发,包括测试案例共30个代码库提供给读者学习使用。小册实现的中间件场景涵盖:技术框架、数据服务、数据组件、分布式技术、服务治理、字节码、IDEA插件七个方面,贯穿整个互联网系统架构中常用的核心内容。非常值得了解、学习、实践到掌握。** + +💋`鉴于作者水平有限`,如果书中含有不易理解的内容,一定是作者在编写的过程中缺少必要的描述和严格的校准,感谢把你的意见或者疑问提交给我,也欢迎与我多一些交互,互相进步共同成长。 + +## 二、中间件开发技术 + +如果平常只是更多的做一些业务代码的开发,那么接触的技术一般是在各类组件的 API 使用上,以及对不同接口的包装。而中间件开发会涉及到各类框架的源码和原理,以及相应的技术迁移和复用。那么在我们这次中间件的设计和实现中,会学到框架、数据、治理、分布式以及字节码的相关技术栈知识,整体包括如下: + +![图 2-1](https://bugstack.cn/assets/images/middleware/2-1.png) + +- **技术框架**:包括 Spring、SpringBoot 配置加载、自定义注解、扫描注册Bean等,以及 ORM 框架设计原理和实现。这部分技术主要是把开发的中间件与框架结合,开发相应的组件或者包装为各类 SpringBoot Starter 的能力学习。 +- **数据服务**:Mysql、Redis、Elasticsearch,都是数据服务,通常需要开发各类组件对数据服务的使用进行封装,Mysql 我们知道有 JDBC,Redis 我们知道有 Jedis,但 Elasticsearch 有 x-pack 你是否了解。 +- **数据组件**:这类组件的开发就是为了简化对数据服务的使用,Mysql+JDBC+ORM,可以非常方便的使用数据库服务,那么 Elasticsearch 是否也可以做相应的组件研发,让它的查询也能像使用 MyBatis 一样呢?二折页的技术能力就需要对 MyBatis 等 ORM 框架的实现原理熟悉,同时需要了解 JDBC 的概念。 +- **分布式技术**:RPC 框架、注册中心、分布式任务,都是现有互联网分布式架构中非常重要的技术,而对于如何实现一个 RPC 框架,也技术是研发人员要掌握的重点,同时如何使用注册中心、怎么下发分布式调度任务,等等,这些技术的学习能让对现有的框架使用有更深入的认识。 +- **服务治理**:熔断、降级、限流、切量、黑白名单以及对现有方法的非入侵式扩展增强等,都可以成为是服务治理类组件,原本这类技术在早期是与业务逻辑代码融合的,后来逐步被拆解出来,开发成对应的组件。所以我们可以学习到,关于这类组件的包装、集成是如何做的。 +- **字节码&插件**:在互联网的系统应用运维过程中,你一定会接触到各类的监控系统,而很多监控系统是非入侵的全链路监控,那么这些是如何实现的呢?其实它们是基于字节码插桩,对系统方法的增强,采集相应的运行时信息,进行监控的。再到扩展 JVMTI、IDEA 插件开发,都是为了整个研发过程的可持续交付和上线提高交付质量和降低人效的。 + +**综上**,这些贯穿整个互联网系统架构中的各类典型中间件,都会在后续章节中陆续讲解出来,它们是如何设计和实现的,一点点带你解开中间件的神秘面纱,让你的技术栈知识也增加一些有深度的并且是可以亲自操作的内容。 + +## 三、中间件设计和实现列表 + +| 序号 | 图标 | 名称 | 描述 | +| :--: | :--: | ---- | ---- | +| 1 | ![](https://bugstack.cn/assets/images/middleware/3-0.png) | 服务治理,统一白名单控制 | 解决上线验证风险,白名单特定用户开量验证 | +| 2 | ![](https://bugstack.cn/assets/images/middleware/4-0.png) | 服务治理,超时熔断 | 包装超时调用熔断,降低业务系统接入成本 | +| 3 | ![](https://bugstack.cn/assets/images/middleware/5-0.png) | 服务治理,调用限流 | 包装接口调用限流,降低业务系统接入成本 | +| 4 | ![](https://bugstack.cn/assets/images/middleware/6-0.png) | 服务治理,自定义拦截方法 | 不破坏现有方法,增强方法服务能力 | +| 5 | ![](https://bugstack.cn/assets/images/middleware/7-0.png) | ORM 框架实现 | 学习 ORM 框架核心设计,实现简单版 MyBatis | +| 6 | ![](https://bugstack.cn/assets/images/middleware/8-0.png) | ORM 框架与 Spring 集合 | 熟悉 Bean 扫描、代理、注册、管理等,以及对 ORM 的包装 | +| 7 | ![](https://bugstack.cn/assets/images/middleware/9-0.png) | 结合 SpringBoot 开发 ORM Starter | ORM、Spring 与 SpringBoot 结合,自动化记载初始配置,开发 Starter | +| 8 | ![](https://bugstack.cn/assets/images/middleware/10-0.png) | ES-JDBC 查询引擎 | 了解 Elasticsearch JDBC 组件的源码实现,x-pack-jdbc | +| 9 | ![](https://bugstack.cn/assets/images/middleware/11-0.png) | ES SpringBoot Starter 服务框架 | 运用 ORM 技术迁移,开发 ES 类的 ORM 框架,解决查询映射复杂性,做面向对象开发包装 | +| 10 | ![](https://bugstack.cn/assets/images/middleware/12-0.png) | RPC 框架实现 | 学习 RPC 框架的设计和开发,了解通信原理和实现 | +| 11 | ![](https://bugstack.cn/assets/images/middleware/13-0.png) | 数据库路由组件 | 把散列算法、切面处理、数据源切换、自定义配置结合在一起实践,开发路由组件 | +| 12 | ![](https://bugstack.cn/assets/images/middleware/14-0.png) | Redis 简化使用封装 | 处理 Redis 的二次包装,简化为接口代理方式使用,降低应用成本,以及增加升级容易度 | +| 13 | ![](https://bugstack.cn/assets/images/middleware/15-0.png) | 分布式任务调度 | 在注册中、任务、控制台,多方内容组合下开发分布式任务调度 | +| 14 | ![](https://bugstack.cn/assets/images/middleware/16-0.png) | 非入侵监控设计,ASM 字节码插桩 | 了解字节码插桩技术,学习 Javaagent 处理的非入侵监控方式 | +| 15 | ![](https://bugstack.cn/assets/images/middleware/17-0.png) | 非入侵监控设计,JVMTI 定位代码 | 了解 JVMTI 的技术能力,开发 C++ dll 组件,增强监控能力 | +| 16 | ![](https://bugstack.cn/assets/images/middleware/18-0.png) | IDEA插件与字节码插桩结合 | 结合 IDEA 插件开发与字节码增强技术,采集代码研发运行过程中的执行信息,分析和提升交付质量 | + +--- + +**小册16个中间件实现,包括测试工程等共计30个代码库**,每一章节都会对应有一个中间件的设计和实现,为了便于读者快速有效的学习小册中的技术内容,这里介绍下小册中章节的内容结构,涵盖以下5方面内容: +1. **开篇引导**,在技术、经验、成长等各方面汇总的内容,帮助大家扩宽知识面和增加成长经验。 +2. **需求背景**,讲述此中间件会因为什么场景、什么需求下用于解决什么痛点而提出的。 +3. **方案设计**,针对需求背景的痛点问题,做中间件架构方案设计,包括设计图稿和实现描述。 +4. **技术实现**,主要是对方案设计的具体实现落地,这个过程会包括完整的实现源码以及所有核心代码的讲解。保证大家在学习的过程中也能完成中间件的设计和开发。 +5. **测试验证**,每一个中间件的实现都有一个对应的测试工程,例如:`whitelist-spring-boot-starter` 与 `whitelist-spring-boot-starter-test`。通过测试工程对中间件实现预期的验证,可以让大家更加容易的理解一个需求的背景、设计、实现到交付验证的过程。 +6. **文末总结**,是对每一篇文章的概要汇总,也是给读者在文末针对此篇文章的学习的一个帮助提醒,也希望你学到的信息要远比站在作者视角总结的内容还要完善。 + +## 四、你会学到什么? + +- Spring 对配置文件的加载、Bean 扫描、定义、注册等 +- Spring Boot 关于 Starter 开发的常用技术手段和技巧 +- ORM、RPC、数据库路由、服务治理、系统监控、IDEA插件等各类场景下的中间件设计 +- 类的代理、反射调用、切面处理、字节码插桩、扰动函数增强散列以及JVMTI等核心技术的实际运用 +- 30个代码库让你对中间件的设计、实现、验证,有清晰的认识 + +## 五、适宜人群 + +- 具备 Java 编程基础的研发人员,略懂部分框架源码,经常使用各类技术组件 +- 需要提升个人的核心技术能力 +- 对中间件开发感兴趣,但不知道从哪入手 +- 有在 SpringBoot 开发 Starter 的技术需求 + +## 六、项目课程📚 + +- 地址:[https://t.zsxq.com/0c7qkNTdA](https://t.zsxq.com/0c7qkNTdA) + +## 七、🎉收尾感谢 + +谢谢掘金平台和运营`优弧`对小册校对审核到上架的帮助,谢谢`粉丝伙伴`对小傅哥技术内容的认可和期待,也谢谢家人在过年和周末期间给我提供的时间`只干饭不洗完😄哈哈哈哈,专心码文章`。 + +**好嘛**,就是在大家的帮助、支持、认可、鼓励中,你希望看到的`中间件设计和开发`小册和大家见面了!这是一个程序员成长阶段突破技术瓶颈和提升技术认知,都应该了解和学习的内容,加油!*记住在专栏学习过程中遇到任何问题,请联系这个优秀的男人:小傅哥,微信:fustack* + diff --git a/docs/md/zsxq/project/xfg-wrench.md b/docs/md/zsxq/project/xfg-wrench.md new file mode 100644 index 000000000..1d0fe1144 --- /dev/null +++ b/docs/md/zsxq/project/xfg-wrench.md @@ -0,0 +1,111 @@ +--- +title: 通用技术组件 - 🔧扳手工程 +lock: no +--- + +# 通用技术组件 - 🔧扳手工程,凝练共性功能,实现通用组件。为各个业务系统赋能! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) +
课程:[https://t.zsxq.com/o7IBm](https://t.zsxq.com/o7IBm) + +>沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +哈哈哈,死鬼!又带来了一个新项目,**《扳手工程》** 一个通用场景解决方案项目,嘎嘎的冲过来啦!`这已经是小傅哥社群里,第17个项目啦!😱` + +
+ +
+ +**这是一个什么项目?** + +在互联网公司真实业务开发场景下,往往会有很多的业务工程系统进行落地,如;电商、交易、信贷、营销、分润、清分/结算等等,这些业务场景会落地非常多的业务系统。而这些业务系统中,会有很多的相同的非业务逻辑的共性技术诉求的处理,如;动态属性配置、熔断、限流、异常、数据采集监控、增强的mock服务、流量录制回放、切面拦截日志、模型设计框架等等。 + +这些共性的组件东西,不可能让每个系统全部自己实现一遍,既耗费人力,最后又实现的五花八门非常难维护。所以,在互联网公司中,会单独把这类通用的逻辑凝练成共用的技术组件,让所需的业务场景进行引入使用。 + +
+ +
+ +那么,这就是小傅哥要带着你做的一款通用《扳手工程》 SpringBoot Starter 系列组件,用于星球的业务项目中使用。**😂 星球里也确实有非常多的业务项目了,不开一个这样东西,那么就要很多业务项目都重复的实现!** + +> 💐 文末提供了17套实战项目源码,8套业务(含最新 AI RAG MCP)、8套组件,还有1套源码教程。 + +## 一、能学到啥 + +该项目是以真实的互联网企业架构方案为指导,建设星球业务项目通用技术组件支撑平台。全程手把手分析需求、架构工程、从0到1编码,让大家可以深入理解和学习组件化设计和落地。 + +- 深入学习 Spring、SpringBoot、MyBatis 框架源码,掌握 SPI 机制实例化组件入口,理解 Spring Bean 生命周期与 BeanPostProcessor 的实际应用,实现属性级别的动态注入与代理。 +- 掌握 Redisson 客户端的配置与使用,基于 Redis 实现分布式发布/订阅消息机制。 +- 理解自定义注解实现配置属性的自动注入与动态刷新,学习分布式系统中配置中心的设计与实现,提升组件化开发与扩展能力。 +- 深入学习 Spring AOP 切面编程,掌握注解驱动的方法拦截与增强技术。 +- 熟练运用 Guava RateLimiter 实现令牌桶算法的接口限流保护机制。 +- 掌握自定义注解与反射机制结合,实现灵活的功能配置与参数提取,精通本地缓存设计与管理,提升系统性能与资源利用率。 +- 深入理解分布式系统限流与降级策略,构建高可用服务保障体系。 +- 熟练应用代理模式与策略模式,提升代码的可扩展性与可维护性,掌握 Java 反射机制在属性获取与方法调用中的高级应用。 +- 精通动态配置管理与功能开关设计,实现系统的灵活控制,深入理解异常处理与日志记录的最佳实践,提升系统可观测性。 +- 掌握面向接口编程思想,设计支持分布式场景扩展的组件架构,深入掌握责任链模式在复杂业务流程中的应用,提升系统解耦与扩展能力。 +- 学习策略模式在动态业务路由与决策中的实现,增强系统灵活性,掌握泛型与函数式接口在设计模式中的高阶用法,提升代码复用性与类型安全。 +- 理解链式调用与链路装配技术,实现业务处理链的灵活组装与动态扩展,掌握多线程与异步策略路由的设计与实现,提升系统并发处理能力。 +- 学习接口+抽象类+实现类的分层设计思想,为后续 SPI 等机制扩展预留入口。 + +以上,仅是关于组件技术开发的一部分,如果你想把一些之前硬背过的关于核心技术组件场景解决方案的东西彻底学会,而不是简简单单的背,那么这趟技术列车千万别错过。 + +## 二、项目介绍 + +本次项目你会学习到非常多的共性场景解决方案,这些内容可以引入到你做过的业务项目中进行使用。全过程以实际问题场景触发,设计解决方案,手把手编码。组件项目完成后,还会带着你发布自己的Jar到共有平台(https://mvnrepository.com/) 其他人也能引入使用。 + +### 1. 背景举例 + +
+ +
+ +当你学习大营销、拼团、OpenAI 应用等业务系统时,会遇到很多同类的功能诉求,如;规则树的设计模式、DCC 动态配置中心、接口限流配置等。这些东西其实都可以被抽象凝练成一个通用的技术组件,引入后直接配置使用即可,而不需要在每个业务系统中都开发一遍。 + +### 2. 架构设计 + +设计通用的统一规范的扳手工程; + +
+ +
+ +- 首先,我们会搭建一套标准的扳手框架 Spring Starter 工程。在工程内,以模块化方式陆续实现,动态配置、限流服务、通用设计模块框架。 +- 之后,以 Redis 作为简单注册中心使用,管理动态配置、限流服务等,再以 admin 管理端,下达配置命令,来动态操作工程配置。 + +### 3. 工程模型 + +
+ +
+ +- 定义 xfg-wrench 扳手工程,以 xfg-wrench-starter 为前缀,命名各项服务组件。如;`xfg-wrench-starter-dynamic-config-center` +- xfg-wrench-test 为测试工程,用于验证各个模块的功能实现。 +- xfg-wrench-admin 为管理后台,后续陆续创建完成。以及其他模块组件陆续迭代开发。 + +### 4. 流程设计(举例 DCC 动态配置) + +如图,动态配置中心功能流程设计; + +
+ +
+ +- 以 SPI 机制,实现组件入口类的注册。驱动注册中心和 Spring 容器工作。也就是工程的 `META-INF/spring.factories` 文件里配置的类,会被引入此组件的其他 SpringBoot 应用进行调用启动。 +- 接下来,注册配置会链接 Redis 当做注册中心使用。因为 Redis 具备了存储和发布订阅的功能,如 Dubbo 也可以使用 Redis 作为简单注册中心使用一样。而后出初始化动态配置中心的服务,以及监听订阅消息。 +- 在之后,拦截 Spring 容器实例化的 Bean 对象,找到使用了自定义注解 `@DCCValue` 的属性,对其拦截动态读取 Redis 中配置属性值(如无则首次设置值),之后对属性进行反射调用,设置变更后端 属性值。 +- 最后是,我们操作推送 Redis 发布订阅消息,则可以被消息回调,变更属性值。 + +## 三、加入学习 + +课程采用 `文档` + `视频`教程,全流程带着你分析和实现,并且会把组件也业务项目结合。小傅哥带着你做的,就是互联网中真实场景的运用。 + +
+ +
+**注意📢**,本项目也只是**【星球:码农会锁】**众多项目中的1个,其他的项目还包括:拼团、大营销、小型支付商城、AI RAG MCP、OpenAI 代码自动评审、动态线程池、支付SDK、API网关、Lottery抽奖、IM通信、SpringBoot Starter 组件开发、IDEA Plugin 插件开发等,并还有开源项目学习。 + +> 这将是一个新的技术旅程,在这趟车上🚗,你会了解到互联网公司大厂🏭是如何对这类场景的方案落地的。 diff --git a/otter.md b/otter.md new file mode 100644 index 000000000..a2e91fe39 --- /dev/null +++ b/otter.md @@ -0,0 +1,15 @@ +**💐🧧活动:** - 完成后私聊我(备注星球编号)获取奖励。 + +1. 所有今天加入的伙伴,都赠送 `100万 Token 4.0` 助力编程学习。 +2. 推荐其他伙伴加入,私聊发送加入者星球编号给我,你可以获得 `100万 Token 4.0` + +
+ +
加入星球后从【课程入口】进入即可看见课程归档
+
+ +**伙伴说:“加入小傅哥是这一年最正确的✅的决定!” 因为我让我很多伙伴提升了技术、卷到了Offer、晋升了职级,获得了更多的收入!** + +
+ +
\ No newline at end of file diff --git a/package.json b/package.json index 40ef97668..356fbb2af 100755 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "@vssue/vuepress-plugin-vssue": "^1.4.8", "@vuepress/core": "^1.8.2", "browserslist": "^4.16.3", - "caniuse-lite": "^1.0.30001196", "screenfull": "^5.1.0", "vuepress-plugin-baidu-autopush": "^1.0.1", "vuepress-plugin-code-copy": "^1.0.6", @@ -30,4 +29,4 @@ "vuepress-plugin-img-lazy": "^1.0.4", "vuepress-plugin-table-of-contents": "^1.1.7" } -} \ No newline at end of file +}