diff --git a/README.md b/README.md index de94514f0..f903eb7c5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ - :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) | [`手写Spring`](https://github.com/fuzhengwei/small-spring) | [`手写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) +- :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) 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 dfbfe877f..cbebdc22f 100644 --- a/docs/.vuepress/components/LockArticle.vue +++ b/docs/.vuepress/components/LockArticle.vue @@ -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/RoadMap.vue b/docs/.vuepress/components/RoadMap.vue index 700d00053..71cd5ab96 100644 --- a/docs/.vuepress/components/RoadMap.vue +++ b/docs/.vuepress/components/RoadMap.vue @@ -1867,7 +1867,7 @@ src="https://bugstack.cn/images/roadmap/08-测试/082-性能测试/1-Jemeter/images/Jemeter.png"> - Jemeter + JMeter diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 0b60a8613..c19b5b394 100755 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -33,7 +33,7 @@ module.exports = { "/": { lang: "zh-CN", title: "小傅哥 bugstack 虫洞栈", - description: "包含: Java 基础,面经手册,Netty4.x,手写Spring,用Java实现JVM,重学Java设计模式,SpringBoot中间件开发,IDEA插件开发,Lottery抽奖系统,字节码编程..." + 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 虫洞栈, DDD抽奖系统,数据结构,重学Java设计模式, 字节码编程, 中间件, 手写Spring, 手写MyBatis,Java基础, 面经手册,Java面试题,API网关,SpringBoot Stater, ChatGPT" + 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', @@ -185,7 +185,10 @@ module.exports = { text: '导读', link: '/md/other/guide-to-reading.md' }, { - text: '路书', link: '/md/road-map/road-map.md' + text: '编程路书', link: '/md/road-map/road-map.md' + }, + { + text: 'AI Agent', link: '/md/ai/spring-ai.md' }, { text: '算法', @@ -205,176 +208,185 @@ module.exports = { ] }, { - 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: '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: 'Spring Cloud', - link: '/md/spring/spring-cloud/2019-10-31-Spring Cloud零《总有一偏概述告诉你SpringCloud是什么》.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: '源码分析(Mybatis、Quartz)', - link: '/md/spring/source-code/2019-12-25-[源码分析]Mybatis接口没有实现类为什么可以执行增删改查.md' - } - ] - }, - { - text: '面向对象', - items: [ - { - text: '重学Java设计模式', + text: 'Spring', items: [ { - text: '创建型模式', - link: '/md/develop/design-pattern/2020-05-20-重学Java设计模式《实战工厂方法模式》.md' + text: 'Spring 手撸专栏', + link: '/md/spring/develop-spring/2021-05-16-第1章:开篇介绍,手写Spring能给你带来什么?.md' }, { - text: '结构型模式', - link: '/md/develop/design-pattern/2020-06-02-重学 Java 设计模式《适配器模式》.md' + text: 'MyBatis 手撸专栏', + link: '/md/spring/develop-mybatis/2022-03-20-第1章:开篇介绍,手写Mybatis能给你带来什么?.md' }, { - text: '行为型模式', - link: '/md/develop/design-pattern/2020-06-18-重学 Java 设计模式《实战责任链模式》.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: 'DDD 专题', + 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: '工程框架', + text: '系统架构-工程框架', link: '/md/develop/framework/frame/2019-12-22-架构框架搭建一《单体应用服务之SSM整合:Spring4 + SpringMvc + Mybatis》.md' }, { - text: '架构方案', + text: '系统架构-架构方案', link: '/md/develop/framework/scheme/2021-02-04-基于IDEA插件开发和字节码插桩技术,实现研发交付质量自动分析.md' + }, + { + text: '标准-开发规范&事故', + link: '/md/develop/standard/2020-09-14-一次代码评审,差点过不了试用期!.md' } ] }, { - text: '标准', + text: 'Netty 4.x', items: [ { - text: '开发规范&事故', - link: '/md/develop/standard/2020-09-14-一次代码评审,差点过不了试用期!.md' - } + 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: '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/api-gateway.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/2023-04-18-tool.md' - }, { text: '💯实战项目', items: [ { - text: '业务类型', items: [ + text: '创新类型(AI)', items: [ { - text: 'ChatGPT 微服务应用体系构建', + 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' @@ -382,15 +394,31 @@ module.exports = { { text: 'IM Netty 仿PC端微信', link: '/md/project/im/2020-03-04-《Netty+JavaFx实战:仿桌面版微信聊天》.md' - }, - { - text: 'ChatGPT AI 问答助手', - link: '/md/project/chatbot-api/chatbot-api.md' } ] }, { 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' @@ -399,6 +427,26 @@ module.exports = { text: 'API网关:中间件设计和实践', 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' + }, ] }, ] @@ -408,7 +456,7 @@ module.exports = { link: '/md/zsxq/introduce.md' }, { - text: '📝产品', + text: '📝产品(ai ide)', items: [ { text: '出版物', items: [ @@ -425,20 +473,16 @@ module.exports = { { 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' - }, - { - text: '免费《倚天村 • 图解数据结构》(密码:0SjbAlPa)', - link: 'http://pan.bugstack.cn/?dl=dbed614f318bf9fc9d3b034ba9502a3c' + link: 'https://drive.weixin.qq.com/s?k=ACMA4AfQABUg04LF5X' }, { - text: '免费《手写 Spring》(密码:uke3Eauf)', - link: 'http://pan.bugstack.cn/?dl=dff9e469d550cadba830ab748322e54e' + text: '免费《倚天村 • 图解数据结构》', + link: 'https://drive.weixin.qq.com/s?k=ACMA4AfQABUm2EZtFm' }, { text: '付费《Java 面经手册》', @@ -454,7 +498,15 @@ module.exports = { text: '插件', items: [ { text: '💱 IDEA Plugin vo2dto —— 对象转换插件', - link: '/md/product/idea-plugin/vo2dto.md' + 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' }, ] }, @@ -464,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'} ] }, @@ -476,6 +531,7 @@ module.exports = { text: '源码', items: [ {text: '开源项目 - Github', link: 'https://github.com/fuzhengwei'}, + {text: '开源项目 - Gitcode', link: 'https://gitcode.net/fuzhengwei'}, {text: '付费项目 - Gitcode', link: 'https://gitcode.net/KnowledgePlanet'}, ] } @@ -508,9 +564,18 @@ module.exports = { "/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() } } @@ -533,66 +598,160 @@ function genBarOther() { ] } +function genBarAI() { + return [ + { + title: "框架", + collapsable: false, + sidebarDepth: 0, + children: [ + "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", + ] + }, + ] +} + function genBarGuide() { return [ { - title: "简明教程(1)", + title: "简明教程(3)", collapsable: false, - sidebarDepth: 2, + sidebarDepth: 3, + children: [ + "road-map.md", + "introduce.md", + "cainiao.md", + ] + }, + { + title: "工程脚手架(2)", + collapsable: false, + sidebarDepth: 0, children: [ - "road-map.md" + "ddd-archetype.md", + "ddd-archetype-maven.md", ] }, { - title: "系统架构(5)", + title: "系统架构(10)", collapsable: false, sidebarDepth: 0, children: [ "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: "开发环境(6)", + title: "开发环境(12)", collapsable: false, sidebarDepth: 0, children: [ + "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: "开发技术(10)", + title: "开发技术(19)", collapsable: true, sidebarDepth: 0, children: [ + "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: "常用类库(3)", + title: "常用类库(5)", collapsable: true, sidebarDepth: 0, children: [ "fastjson.md", "guava.md", "http.md", + "ratelimiter.md", + "disruptor.md", + ] + }, + { + title: "智能组件(8)", + collapsable: true, + sidebarDepth: 0, + children: [ + "spring-ai.md", + "google-adk.md", + "agent-skill.md", + "a2a.md", + "draw.io.md", + "ai-ssh-opencode.md", + "github-models.md", + "openclaw.md", ] }, { @@ -600,24 +759,62 @@ function genBarGuide() { collapsable: true, sidebarDepth: 0, children: [ - "none.md" + "mock.md", + "jmeter.md", + "intellij-idea-remote-jvm-debug.md", + "arex-test.md", ] }, { - title: "质量监控(1)", + title: "质量监控(7)", collapsable: true, sidebarDepth: 0, children: [ - "skywalking.md" + "skywalking.md", + "grafana.md", + "elk.md", + "dump-mat.md", + "dump-visualvm.md", + "arthas.md", + "13scan-jdumpspider.md", ] }, { - title: "发布部署(2)", + title: "发布部署(20)", collapsable: true, sidebarDepth: 0, children: [ + "cloud-server.md", + "linux.md", + "1panel.md", + "docker-what.md", "docker.md", - "portainer.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", ] } ] @@ -736,7 +933,7 @@ function genAlgorithmLogic() { } // algorithm/model -function genAlgorithmModel(){ +function genAlgorithmModel() { return [ { title: "机器学习", @@ -746,6 +943,7 @@ function genAlgorithmModel(){ "2023-02-12-chat-gpt.md", "2023-02-18-gpt2-chitchat.md", "2023-05-21-chatglm-6b.md", + "autoglm-phone-agent.md", ] } ] @@ -942,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", ] } ] @@ -1050,6 +1249,7 @@ function genBarDevelopDesignPattern() { sidebarDepth: 0, children: [ "2022-03-12-重学Java设计模式B站视频.md", + "2024-08-25-chain-tree.md", ] }, { @@ -1110,6 +1310,7 @@ function genBarDevOPS() { "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", ] @@ -1130,6 +1331,7 @@ function genBarDevOPS() { "2021-11-07-关于怎么使用 webhooks 自动部署博客,详细教程文档!.md", "2022-03-04-教小白使用 docsify,搭建一个贼简单的所见即所得博客!.md", "2023-03-25-免费部署部署ChatGPT.md", + "2024-01-30-vuepress-resume-blog.md", ] } ] @@ -1171,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", ] } ] @@ -1673,7 +1876,10 @@ function getBarZSXQ() { 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", ] }, { @@ -1684,20 +1890,36 @@ function getBarZSXQ() { "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", - "project/chatbot-api.md", ] }, { @@ -1705,6 +1927,11 @@ function getBarZSXQ() { 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", @@ -1727,7 +1954,7 @@ function getBarZSXQ() { sidebarDepth: 0, children: [ "source-code/develop-mybatis.md", - "source-code/develop-spring.md", + // "source-code/develop-spring.md", ] }, { @@ -1760,10 +1987,20 @@ function getBarZSXQ() { 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", ] } ] @@ -1771,6 +2008,14 @@ function getBarZSXQ() { function getBarProduct() { return [ + { + title: "AI IDE", + collapsable: false, + sidebarDepth: 0, + children: [ + "software/walicode.md", + ] + }, { title: "出版物", collapsable: false, @@ -1789,7 +2034,6 @@ function getBarProduct() { "pdf/2020-07-12-重学 Java 设计模式.md", "pdf/2020-10-04-《Java面经手册》PDF数据结构篇, 肝完出炉了!来吧,这本书帮你拿最贵的offer!.md", "pdf/2021-01-26-Java面经手册PDF下载.md", - "pdf/2021-08-12-《手撸 Spring》PDF,全书260页6.5万字,完稿&发版!.md", "pdf/2022-01-23-IDEA Plugin 开发手册.md", ] }, @@ -1799,8 +2043,11 @@ function getBarProduct() { sidebarDepth: 0, children: [ "idea-plugin/vo2dto.md", + "idea-plugin/vo2dto-v2.5.1.md", + "idea-plugin/vo2dto-v2.5.5.md", ] }, + ] } @@ -1976,6 +2223,7 @@ function getBarProjectChatGPT() { "chatgpt.md", "引言.md", "notes.md", + "review.md", ] }, { @@ -1990,6 +2238,8 @@ function getBarProjectChatGPT() { "dev-ops/第5节:服务镜像构建和容器部署.md", "dev-ops/第6节:前后端构建镜像部署.md", "dev-ops/第7节:网站添加百度统计.md", + "dev-ops/第8节:应用监控.md", + "dev-ops/第9节:部署上线.md", ] }, { @@ -2005,6 +2255,9 @@ function getBarProjectChatGPT() { "api/第6节:白名单和敏感词规则过滤.md", "api/第7节:用户额度账户领域实现.md", "api/第8节:商品下单对接微信支付.md", + "api/第9节:OpenAi多渠道策略模式.md", + "api/第10节:应用分布式设计.md", + "api/第11节:dall-e文生图.md", ] }, { @@ -2024,6 +2277,7 @@ function getBarProjectChatGPT() { sidebarDepth: 0, children: [ "sdk/chatglm-sdk-java.md", + "sdk/chatglm-sdk-java-v2.md", ] }, { @@ -2057,6 +2311,597 @@ function getBarProjectChatGPT() { ] } +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 [ @@ -2147,15 +2992,26 @@ function getBarProjectLottery() { function genBarAbout() { return [ { - title: "关于自己", + title: "年终总结", collapsable: false, sidebarDepth: 0, children: [ - "me/about-me.md", - "me/2020-03-31-大学四年到毕业工作5年的学习路线资源汇总.md", "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, + sidebarDepth: 0, + 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", @@ -2172,6 +3028,10 @@ function genBarAbout() { "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", ] }, { @@ -2197,6 +3057,7 @@ function genBarAbout() { "study/2023-04-02-国外码农,会卷八股文吗?.md", "study/2023-05-14-卧龙、凤雏!两源码学得一,代码质量都不会差!.md", "study/2023-06-04-后端码农,怎么写好前端代码?.md", + "study/2024-03-03-到5万就好了.md", ] }, { @@ -2216,6 +3077,9 @@ function genBarAbout() { "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/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/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/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/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/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/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-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-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/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/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/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/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-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-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/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/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/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-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-ddd-05.png b/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-05.png index c7b8ec9f7..0b5603cc3 100644 Binary files a/docs/.vuepress/public/images/roadmap/tutorial/road-map-ddd-05.png 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-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-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-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-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-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-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-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-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/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/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/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/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-booklet.png b/docs/.vuepress/public/images/system/zsxq/zsxq-booklet.png index 4237c1a03..fb53fe268 100644 Binary files a/docs/.vuepress/public/images/system/zsxq/zsxq-booklet.png and b/docs/.vuepress/public/images/system/zsxq/zsxq-booklet.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/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/Page.vue b/docs/.vuepress/theme/components/Page.vue index b9a0e950b..ed7f4b914 100755 --- a/docs/.vuepress/theme/components/Page.vue +++ b/docs/.vuepress/theme/components/Page.vue @@ -59,6 +59,9 @@ + diff --git a/docs/.vuepress/theme/components/PageSidebar.vue b/docs/.vuepress/theme/components/PageSidebar.vue index efdbd1c7d..74aae8f48 100755 --- a/docs/.vuepress/theme/components/PageSidebar.vue +++ b/docs/.vuepress/theme/components/PageSidebar.vue @@ -13,6 +13,15 @@
+ + + + + + + + + @@ -58,14 +67,14 @@ 左栏 -
+
星球
- 实战项目「DDD+RPC分布式抽奖系统」、专属小册、问题解答、简历指导、架构图稿、视频课程 + 带你学习,实战项目。提高编程思维,锻炼编码能力!17个实战项目,增强面试竞争力! 知识星球:码农会锁
@@ -81,7 +90,7 @@
添加小傅哥微信(fustack)进虫洞栈学习交流圈「无任何套路」 - + PS:添加时请备注读者加群,谢谢!
@@ -103,22 +112,42 @@
-
- - 赞赏我 -
-
-
-
- 鼓励/支持/赞赏我 - -
1. 不靠它生存但仍希望得到你的鼓励; -
2. 时刻警醒自己保持技术人的初心,沉淀,分享,成长; -
-
-
+
+ + 做项目 +
+ +
+ + 八股文 + + + + + + + + + +
+ + + + + + + + + + + + + + + +
diff --git a/docs/README.md b/docs/README.md index fb1bfbbab..a1e629d10 100755 --- a/docs/README.md +++ b/docs/README.md @@ -5,12 +5,24 @@ actionLinks: - link: /md/other/guide-to-reading.md text: 开始阅读 → class: primary -- link: https://t.zsxq.com/09hMHNMEh +#- link: https://wx.zsxq.com/group/48411118851818 +# text: 知识星球 +# class: secondary +- link: /md/zsxq/introduce.html text: 知识星球 class: secondary - link: /md/road-map/road-map.md - text: 路书 👣 + text: 编程路书 👣 class: secondary +- link: https://gaga.plus + text: 项目(+菜鸟教程) 💐 + class: secondary +#- link: https://618.gaga.plus +# text: 9.9元/月(云服务器) ☁️ +# class: secondary +- link: https://walicode.xiaofuge.cn/ + text: WaLiCode 🤖 + class: secondary features: - title: 沉淀 details: 承遇朝霞,年少正恰。整装戎马,刻印风华。 @@ -18,43 +30,68 @@ features: details: 八表流云澄夜色,九霄华月动春城。 - title: 成长 details: 心怀天下,声色犬码。生有热烈,藏与俗常。 -footer: 京ICP备19031103号-1 |

京公网安备 11030102010881号

| GPL Licensed | Copyright © 2019 小傅哥,All rights reserved. +footer: 京ICP备19031103号 |

京公网安备 11030102010881号

| GPL Licensed | Copyright © 2019 小傅哥,All rights reserved. --- --- -## 更新计划 - -1. [Java 简明教程 - `进行中👣`](https://bugstack.cn/md/road-map/road-map.html) - `将项目所需的知识点进行碎片化的拆解,形成一套整套以实战为目的地的Java简明教程,方便小白清晰准确的学习编程知识。` -2. [ChatGPT 微服务应用体系构建 - `进行中👣`](https://bugstack.cn/md/project/chatgpt/chatgpt.html) - `应用级OpenAI实战项目,包括;前端、后端、运维等全体系技术学习。` - -## 大厂项目 - -- [Lottery 抽奖系统](https://bugstack.cn/md/project/lottery/introduce/Lottery%E6%8A%BD%E5%A5%96%E7%B3%BB%E7%BB%9F.html):以贯穿整个互联网所需的分布式技术栈,基于DDD领域驱动设计的四层架构与设计模式逻辑的实践项目,非常适合手里没有大项目的读者进行学习。 -- [API网关](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):是互联网大厂必备的系统,承接着:打车、外卖、购物、支付等多场景的使用,更是大促期间千万级访问量的核心服务。 -- [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):结合互联网所需解决的方案场景,开发 SpringBoot Starter,涵盖:技术框架、数据服务、数据组件、分布式技术、服务治理、字节码、IDEA插件七个方面,16种中间件的设计和开发。 -- [IM Netty](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领域驱动设计方式,搭建的仿桌面版微信聊天工程实现通信核心功能。 - -## 必看专栏 - -- [重学Java设计模式](https://bugstack.cn/md/develop/design-pattern/2020-05-20-%E9%87%8D%E5%AD%A6Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E3%80%8A%E5%AE%9E%E6%88%98%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95%E6%A8%A1%E5%BC%8F%E3%80%8B.html):从互联网真实业务中抽离出,交易、营销、秒杀、中间件、源码等22个真实场景,学习设计模式的实践技巧和落地方案。 -- [面经手册](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):此专栏并不是单纯的面试题,也不是内卷八股文。而是从一个单纯的和程序员有关的数学知识点开始,深入讲解 Java 的核心技术。 -- [手写 Mybatis](https://bugstack.cn/md/spring/develop-mybatis/2022-03-20-%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%99Mybatis%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):在手写的过程中学习 Mybatis 从解析、绑定、反射、缓存,到会话和事务操作,以及如何与 Spring 进行关联注册 Bean 对象,完成整合部分功能逻辑。 - -## 面试必备 +### 我是小傅哥 + +- :dog: 13年毕业,互联网大厂架构师,全网40万+粉编程知识博主。 +- :man_technologist: 成长:[关于我,从小白到架构师的成长经历](https://www.bilibili.com/video/BV1FF41137q5) +- :bus: 作品:[`CodeGuide | 程序员编码指南`](https://github.com/fuzhengwei/CodeGuide) | [`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) | [`chatglm-sdk-java`](https://github.com/fuzhengwei/chatglm-sdk-java) | [`docker-image-pusher`](https://github.com/fuzhengwei/docker-image-pusher) | [`💱IDEA Plugin vo2dto —— 对象转换插件(15.8k+安装)`](https://bugstack.cn/md/product/idea-plugin/vo2dto.html) | [更多搜索...](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) + +### 开发的项目 + +- 创新项目「AI」(6) + - [AI MCP Gateway 网关服务系统](https://bugstack.cn/md/project/ai-mcp-gateway/ai-mcp-gateway.html) + - [AI Agent 脚手架 + 场景应用(draw.io、手机龙虾)](https://bugstack.cn/md/project/ai-agent-scaffold/ai-agent-scaffold.html) - 综合 Spring AI、LangChain4j + Google ADK(a2a、mcp、skills),打造全新智能体架构方案。 + - [AI Agent 拖拉拽 + 动态配置(RAG、MCP、Prompt)](https://bugstack.cn/md/project/ai-knowledge/ai-knowledge.html) + - [OpenAI 代码自动评审组件](https://bugstack.cn/md/zsxq/project/openai-code-review.html) + - [OpenAI 大模型微服务应用体系构建 - API-SDK、鉴权、公众号、微信支付](https://bugstack.cn/md/zsxq/project/chatgpt.html) + - [ChatGPT AI 问答助手 - 小型,对接知识星球](https://bugstack.cn/md/zsxq/project/chatbot-api.html) + +- 业务项目(5) + - [拼团交易平台系统](https://bugstack.cn/md/project/group-buy-market/group-buy-market.html) + - [小型支付电商系统 - 一套项目2套架构开发(MVC+DDD)](https://bugstack.cn/md/project/s-pay-mall/s-pay-mall.html) + - [大营销平台系统 - 前后端 + Dev-Ops 的全栈式综合编程实战DDD项目!](https://bugstack.cn/md/project/big-market/big-market.html) + - [Lottery 分布式抽奖系统 - 基于领域驱动设计的四层架构实践](https://bugstack.cn/md/zsxq/project/lottery.html) + - [Netty+JavaFx实战:仿桌面版微信聊天](https://bugstack.cn/md/zsxq/project/im.html) + +- 组件项目(8) + - [本地任务消息组件](https://bugstack.cn/md/project/local-task-message/local-task-message.html) + - [通用技术组件 - 🔧扳手工程](https://bugstack.cn/md/zsxq/project/xfg-wrench.html) + - [透视业务流程 - 监控系统](https://bugstack.cn/md/zsxq/project/business-behavior-monitor.html) + - [动态线程池组件](https://bugstack.cn/md/zsxq/project/dynamic-thread-pool.html) + - [蓝兔支付SDK设计和开发](https://bugstack.cn/md/zsxq/project/ltzf-sdk-java.html) + - [API网关:中间件设计和实践](https://bugstack.cn/md/zsxq/project/api-gateway.html) + - [SpringBoot Starter 中间件设计和开发](https://bugstack.cn/md/zsxq/project/springboot-starter.html) + - [IDEA Plugin 开发手册](https://bugstack.cn/md/zsxq/booklet/idea-plugin.html) + +### 我的出版物 + +- 2021年出版[《重学Java设计模式》](https://u.jd.com/4I2CXeO) —— 涵盖一线互联网众多真实案例;交易、营销、秒杀、规则引擎等场景 +- 2023年出版[《手写MyBatis:渐进式源码实践》](https://u.jd.com/wssnicY) —— 从零手写源码级复杂项目,提升架构思维与设计逻辑。锻炼编码能力。 + +### 我的电子书 + +- [小傅哥的《Java 面经手册》](https://download.csdn.net/download/Yao__Shun__Yu/14932325) —— 全书共计 5 章 29 节,417页11.5万字,耗时 4 个月完成。涵盖数据结构、算法逻辑、并发编程、JVM以及简历和互联网大厂面试等内容。但此书并不是单纯的面试题,也不是内卷八股文。而是从一个单纯的和程序员有关的数学知识点开始,深入讲解 Java 的核心技术。并且每一章节都配有实践验证的源码,可以对照着一起撸才更有感觉! +- [小傅哥的《倚天村·图解数据结构》](https://drive.weixin.qq.com/s?k=ACMA4AfQABUm2EZtFm#/) —— 全书共计4章14节,215页4.2万字100+张图片,耗时3个月完成。涵盖4类14种数据结构,包括:链表、数组、队列、堆栈、哈希表、堆、字典树、二分搜索树、平衡二叉树、2-3树、红黑树、并查集、图、布隆过滤器。 +- [小傅哥的《字节码编程》](https://drive.weixin.qq.com/s?k=ACMA4AfQABUJWQ0P92#/) —— 全书共计107页,11万7千字,20个章节涵盖三个字节码框架(ASM、Javassist、Byte-budy)和JavaAgent使用并附带整套案例源码! +- [小傅哥的《IDEA Plugin 开发手册》](https://download.csdn.net/download/Yao__Shun__Yu/77484299) —— 此开发手册,分为4章12节循序渐进的通过实践案例开发的方式,串联 IDEA Plugin 开发的各项常用技术点,为读者讲解如何开发一个 IDEA 插件。 + +### 面试必备 - [100道八股题考试测验](https://bugstack.cn/md/zsxq/material/exam.html) —— 考题范围:数据结构、算法、源码、设计模式、系统架构、中间件、网络通信、实战项目、扩展问题 - [面试题汇总](https://bugstack.cn/md/zsxq/material/interview.html):汇总关于面试问题、简历编写、上岸总结、招聘信息等内容 -- [简历模板](http://pan.bugstack.cn/?dl=0599585a4e691adc7137ea9cb25c087f):你可以在学习掌握博客中的技术以及大厂项目,按照简历模板的格式,包装和调整成你需要的简历内容,提高面试通过率。 - -## PDF - -- [《Java 面经手册》](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/12505051) -- [《IDEA Plugin 开发手册》](https://download.csdn.net/download/Yao__Shun__Yu/77484299) +- [简历模板](https://t.zsxq.com/19hxv0cgY):你可以在学习掌握博客中的技术以及大厂项目,按照简历模板的格式,包装和调整成你需要的简历内容,提高面试通过率。 -## 公众号 +### 公众号 - 关注获得新通知 最新文章会首发公众号,强烈推荐读者伙伴关注一波!另外,在公众号:bugstack虫洞栈 回复 `1024` 可以下载更多资源内容。 @@ -64,13 +101,13 @@ footer: 京ICP备19031103号-
-## 联系我 +### 联系我 - 可加技术交流群 -沉淀、分享、成长,让自己和他人都能有所收获!你也可以添加本站作者小傅哥的微信:`fustack` —— 加入作者技术交流群 +沉淀、分享、成长,让自己和他人都能有所收获!你也可以扫码添加本站作者小傅哥的微信(记得备注从博客来的)
- -
微信:fustack
+ +
提示:如果添加失败,可以先扫码图中的客服,也可以找到我

diff --git "a/docs/md/about/job/2023-02-04-\351\241\271\347\233\256\350\277\231\344\271\210\351\227\256\357\274\214\346\212\212\344\275\240\346\260\264\345\210\206\346\214\244\345\271\262.md" "b/docs/md/about/job/2023-02-04-\351\241\271\347\233\256\350\277\231\344\271\210\351\227\256\357\274\214\346\212\212\344\275\240\346\260\264\345\210\206\346\214\244\345\271\262.md" index 49d8cfa76..48c356464 100644 --- "a/docs/md/about/job/2023-02-04-\351\241\271\347\233\256\350\277\231\344\271\210\351\227\256\357\274\214\346\212\212\344\275\240\346\260\264\345\210\206\346\214\244\345\271\262.md" +++ "b/docs/md/about/job/2023-02-04-\351\241\271\347\233\256\350\277\231\344\271\210\351\227\256\357\274\214\346\212\212\344\275\240\346\260\264\345\210\206\346\214\244\345\271\262.md" @@ -155,9 +155,7 @@ Lottery 的设计把这事给办了; 如果你手里确实没有什么像样的实战项目能摆到简历里给面试官看。那么我非常建议加入小傅哥的知识星球。因为在知识星球里,小傅哥提供了:`《Lottery-抽奖系统》`、`《API网关》`、`《IM通信》`、`《手写MyBatis》`以及各类技术小册,这些项目学习完任何一个,都能和面试官聊到心花怒放,知道你是个有技术深度值得招聘进来的小伙。 -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) 1. 星球内的服务和实战项目都是小傅哥本人提供和**原创**,相信能够给大家带来**超过该价格的价值** 。举个例子,渐进式手把手带大家做**进大厂才可能看得见的项目**、有笔记有源码、有问题可以提,这比单独买一个课程或一套源码要值得多。其实都不到大城市一节补习班的钱,**哪怕把我的课程时长换算成培训机构的课时,也是便宜的超级多**。 2. 持续的内容创作 + 回答问题 + 知识星球的运营(简历批阅、就业指导、架构设计) 需要小傅哥每个早上6点-8点以及周末/假期持续维护。也希望加入星球的同学都是真的下定了决心想要进步,而不是像免费的交流群和社区一样 “闲聊扯淡”。 diff --git "a/docs/md/about/job/2023-03-19-\344\275\240\347\256\200\345\216\206\346\262\241\351\241\271\347\233\256\357\274\214\344\275\240\345\276\227\351\201\255\350\200\201\347\275\252\345\226\275\357\274\201.md" "b/docs/md/about/job/2023-03-19-\344\275\240\347\256\200\345\216\206\346\262\241\351\241\271\347\233\256\357\274\214\344\275\240\345\276\227\351\201\255\350\200\201\347\275\252\345\226\275\357\274\201.md" index 29e7a8981..df77c80e3 100644 --- "a/docs/md/about/job/2023-03-19-\344\275\240\347\256\200\345\216\206\346\262\241\351\241\271\347\233\256\357\274\214\344\275\240\345\276\227\351\201\255\350\200\201\347\275\252\345\226\275\357\274\201.md" +++ "b/docs/md/about/job/2023-03-19-\344\275\240\347\256\200\345\216\206\346\262\241\351\241\271\347\233\256\357\274\214\344\275\240\345\276\227\351\201\255\350\200\201\347\275\252\345\226\275\357\274\201.md" @@ -71,11 +71,7 @@ lock: need 当然我也不扯淡,这确实需要花一点钱💰,但你跟着我就是在跟着一个架构师在学习。并且我也相信能够给大家带来超过该价格的价值 。举个例子,渐进式手把手带大家做进大厂才可能看得见的项目、有笔记有源码、有问题可以提,这比单独买一个课程或一套源码要值得多。其实都不到大城市一节补习班的钱,哪怕把我的课程时长换算成培训机构的课时,也是便宜的超级多。 -**扫码加入星球开通会员,你将获得小傅哥所有编写的高质量付费项目课程** - -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) - **星球**:码农会锁 - 一个学习实战项目,锻炼编码能力,增强编程经验的技术社群。 - **地址**:[https://t.zsxq.com/0cLItY5jY](https://t.zsxq.com/0cLItY5jY) diff --git "a/docs/md/about/job/2023-07-11-\351\235\242\350\257\225\345\256\230\351\203\275\351\227\256\344\275\240\345\225\245\344\272\206.md" "b/docs/md/about/job/2023-07-11-\351\235\242\350\257\225\345\256\230\351\203\275\351\227\256\344\275\240\345\225\245\344\272\206.md" index eb1c254c4..b321a3cb5 100644 --- "a/docs/md/about/job/2023-07-11-\351\235\242\350\257\225\345\256\230\351\203\275\351\227\256\344\275\240\345\225\245\344\272\206.md" +++ "b/docs/md/about/job/2023-07-11-\351\235\242\350\257\225\345\256\230\351\203\275\351\227\256\344\275\240\345\225\245\344\272\206.md" @@ -88,6 +88,6 @@ lock: need 好啦,需要迎接校招的伙伴,赶紧加入吧!这么实惠、干净、给力的技术圈子并不多! -![](https://bugstack.cn/images/system/zsxq/coupon.png) +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + -关注公众号【bugstack虫洞栈】回复【星球】可以获得优惠券! diff --git "a/docs/md/about/job/2024-09-08-\351\230\277\351\207\214P7\357\274\214\345\260\261\346\230\257\345\276\210\345\244\232\344\272\272\347\232\204\345\244\251\350\212\261\346\235\277\345\220\227\357\274\237.md" "b/docs/md/about/job/2024-09-08-\351\230\277\351\207\214P7\357\274\214\345\260\261\346\230\257\345\276\210\345\244\232\344\272\272\347\232\204\345\244\251\350\212\261\346\235\277\345\220\227\357\274\237.md" new file mode 100644 index 000000000..f175ebbc0 --- /dev/null +++ "b/docs/md/about/job/2024-09-08-\351\230\277\351\207\214P7\357\274\214\345\260\261\346\230\257\345\276\210\345\244\232\344\272\272\347\232\204\345\244\251\350\212\261\346\235\277\345\220\227\357\274\237.md" @@ -0,0 +1,87 @@ +--- +title: 阿里P7,就是很多人的天花板吗? +lock: no +--- + +# 阿里P7,就是很多人的天花板吗? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +有人说:”阿里的P7就是很多人的天花板?“,其实他说的没错。这个级别对应研发就是高级架构师,或者M序列管理一个组长的岗位。前面冲到P7靠你的学历进入大厂和技术积累冲击岗位,但从这往后再想晋升靠命了! + +
+ +
+ +**天花板级别的收入咋样?🤔** + +`字节的 2-2 ~ 3-1`、`腾讯的10级 ~ 11级`、`阿里的 P7~P8`、`京东的 T7~T8`,他们的待遇和基本收入是差不多的。在这些级别中平级跳槽的一般不多,除非有是干的实在不爽了。一般是手里有干货的,从任何一家调另外一家,普通会级别+1,收入也会有所增加。来自 [duibiao.info](https://duibiao.info/) 的一份收入数据对比。 + +
+ +
+ +举例🌰,在一个大部门中,T9级别极少,年龄最大,T8少量、主力在 T6~T7,校招新入职在 T2~T3。不过往往C9级别高等院校的学生 + 技术积累很不错,拿到的校招 Offer 会倒挂社招T5级别的薪资范围。前两年一般需要积累,无晋升名额,从做出成绩后,1~2年可能会提名一次晋升。当然还有一些靠命的东西,会走的很快。比如公司组织的技术赛事获奖、A级项目入围等。**当然如果你连续发生事故,造成重大影响,也会让你走的很快。** + +> 接下来,小傅哥分享一些,修炼一身,即走的出去加薪,也留的下来晋升的本事! + +## 一、不要过早冒头!- 做人 + +`纵无显效亦藏拙,若有所成甘守株。` + +在武侠电影🎬《剑雨》中有这一段,陆竹在破庙中与细雨经过一番过招之后,指出细雨的剑法漏洞。并通过`藏拙于巧(藏巧于拙)`,`用晦而明`,`寓清于浊`,`以屈为伸`,破解辟水剑。 + +
+ +
+ +这里有一句,藏拙于巧,出自于 自贻 - 唐·罗隐 中的诗句`纵无显效亦藏拙,若有所成甘守株。`。意思在于古往成就大事者,都要善于藏锋守拙、低调隐忍。 + +在我最开始入职的时候,就不善于这个。往往都是横冲直撞,斗业务、怼产品、干测试!要不是得益于早年的互联网更纯粹,加上领导的偏爱。可能在某次过招后,我也就被送走了。直到现在我的电脑里还保留有和当时产品 battle 的截图 😂。当时的性格就是,你错了,你不对,我指出你不认,那我就再上一招!但往往每一招背后,我也都有漏洞,就像那剑法一样。所以一直我也没有走管理岗! + +所以无论是校招还是社招,进入一家公司后。都不要想着一进来就做的多大,也不要想着多快的晋升。所有看似风平浪静的组织,往往背后也都牵扯着一堆的`"利益权衡"`。先做好自己,完成好交付。 + +## 二、不要只做业务!- 做事 + +业务是程序员👨🏻‍💻进入公司后完成的第一个交付动作,以目前看所有的人都是为了业务的发展而存在的。如果业务不赚钱,第一个被送走的就是程序员。但这里就有一个很重要的事情,你还记得你是怎么进来的吗?要不是校招学历、要不是技术积累、要不是项目经验、要不是背了八股刷了算法。但终究不是说你手速多快,能敲多少 CRUD 进来的。 + +所以,在公司中不能只是完成业务交付即可,要在业务交付多积累成绩。因为同样一个项目不同的人设计实现,就会出现不一样的成绩。有些会把架构、模型、方案、扩展、都在一次次交付中积累出来,并在需求的时候将这一套标准沉淀并复用。这些东西落地可为其他组做标准,上升可为述职讲成绩。同时,你观察🔎还会发现,组内总有人会对同类的技术工作,凝练总结设计开发成通用的技术组件。在归属上这不是他的强职责范围,但在年终总结和后续晋升比拼上可是实打实的成绩。差距也是这样一点点拉开的。 + +我常听到有人说,DDD 复杂、设计模式浪费时间。不,你要知道,不是那东西复杂,也不是那东西浪费时间。是你,是你复杂,是你浪费时间!你不觉得当让你来做分享的时候,你甚至没有办法用技术的话术阐述自己的过程,也很难总结出过往的成绩。总感觉自己就是在拼装接口、组织逻辑、完成交付。 + +所以你只是扮演着劳苦,但不功高的角色。每每提到晋升加薪,就会反问到,你这个人有什么成绩! + +## 三、不要持续趟平!- 自己 + +`趟平就是工作懈怠吗?其实过渡的工作也是一种趟平。` + +不知道你是否意识到,公司和个人是两条发展线,是不能顾此失彼的。更好的个人能力是可以更好的把工作做出成绩。而往往有些人一脑门子的只是一个状态下同一个方式的完成工作,其实往往是没有多少成绩的。 + +一个程序员如果只是会写代码,其实是偏安一隅。业务怎么样、运营怎么做、产品怎么设计、一整套的开发工具实施运维是怎么运行的、各个组件都有哪些原理等等,这些东西都是都需要掌握的。在整体的学习过程中,综合的锻炼出自己成体系的知识结构,才是长久有价值的。 + +尤其是可以跟着一些有大厂经验的人学习他的方法论,学习他的业务经验、学习他的编码技巧,都是可以快速的提高自己的能力的。 + +## 四、小傅哥的经验!- 参照 + +小傅哥在职场发展、业务经验、场景方案、技术经验、实战项目等多方面都有来自于大厂锻炼出来的经验编写。可以让伙伴们学习,快速提高自己的。还为500多人修改过简历,不少伙伴也都被小傅哥的经验,抬进了非常不错的公司。各个大厂也都有小傅哥的伙伴。 + +在这方面我总结了很多的经验,你可以阅读; + +
+ +
+ +地址:[https://bugstack.cn/md/zsxq/material/study-experience.html](https://bugstack.cn/md/zsxq/material/study-experience.html) + +--- + +加入小傅哥的星球「码农会锁」阅读450+份简历和评审,学习6个业务项目;`MVC+DDD,双架构开发小型电商`、`大营销(超级大课)`、`OpenAI 大模型应用`、`Lottery`、`IM`、`AI 问答助手`。7个组件项目;`OpenAI 代码评审`、`BCP 透视业务监控`、`动态线程池`、`支付SDK设计和开发`、`API网关`、`SpringBoot Starter`、`IDEA Plugin 插件开发`。1套源码课程、1套基础教程、1到云服务器教程以及各类场景解决方案。 + +如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 + +> 🧧加入学习这样一套项目,放在一些平台售卖,一个至少都是上千块。但小傅哥的星球,只需要100多,就可以获得全部的学习项目!重要的是学习小傅哥的经验! diff --git "a/docs/md/about/job/2025-11-16-\347\216\260\345\234\250\350\275\254AI\345\272\224\347\224\250\345\274\200\345\217\221\357\274\214\346\230\257\344\270\215\346\230\257\344\270\252\346\234\272\344\274\232\357\274\237.md" "b/docs/md/about/job/2025-11-16-\347\216\260\345\234\250\350\275\254AI\345\272\224\347\224\250\345\274\200\345\217\221\357\274\214\346\230\257\344\270\215\346\230\257\344\270\252\346\234\272\344\274\232\357\274\237.md" new file mode 100644 index 000000000..24d0e7e42 --- /dev/null +++ "b/docs/md/about/job/2025-11-16-\347\216\260\345\234\250\350\275\254AI\345\272\224\347\224\250\345\274\200\345\217\221\357\274\214\346\230\257\344\270\215\346\230\257\344\270\252\346\234\272\344\274\232\357\274\237.md" @@ -0,0 +1,102 @@ +--- +title: 现在转AI应用开发,是不是个机会? +lock: no +--- + +# 现在转AI应用开发,是不是个机会? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是小傅哥。 + +最近 AI 实在是太火了,不是以前那种火在互联网上,而是火到互联网公司里头。之前是铺在 AI 算法上,现在是扎进 AI 应用里。一天天我们都下班了,AI Agent 应用项目组的小伙伴还在加班!🤨 + +
+ +
+ +**其实这是个机会!** + +如果你的学历将将够得到中大厂,但总是被刷。或者之前在一些传统行业,但想进入互联网。那么,一定要在简历上添加 AI 应用类场景项目。 + +
+ +
+ +此类项目以 AI Agent 智能体和 OpenAI 应用为主,无需掌握底层算法或复杂调优能力。只要具备任一主流 AI 框架(如 Spring AI、Google ADK)使用经验,或有自主开发 AI SDK 及应用的实践,都能显著提升你的竞争力。 + +
+ +
+ +如果你最近在搜索招聘岗位,一定会发现各个公司都新增加了 AI 应用开发岗位,而且是非常多。因为公司里的各个部门,都需要基于 AI 做场景提效,因此所有的业务项目,都会被 AI 翻一遍。而且各个大厂的里的研发都比较倾向于参与到一些 AI 场景开发里,因为这必然后面做晋升述职可以讲的一个亮点。 + +**为啥以前没那么 AI 应用开发的岗位?** + +2024年11月25日,AI MCP 协议发布,这东西就是 AI 的手脚,有了他以后 AI 进入了 Agent 智能体时代。以前我们都是问它,之后按照回答的流程,操作我们自己的场景。但现在,我们不是问,而是让它直接做。AI Agent 智能体,可以基于询问,把内容拆分,执行,判断,输出。给出最终的结果。 + +所以,公司里目前大量的业务场景,都开始做相关业务类型的智能体服务,也因此有了非常多的 AI Agent 智能体岗位。当你在Boss直聘,检索这些岗位后,就会知道他们都在哪些场景落地了(AI Agent + 业务)。 + +## 一、学习路线 + +从22年年尾至今,小傅哥一直在做 AI 应用方面的实战编程项目,从最初的 AI 类的接口开发和使用,到 SDK 的封装,再到 OpenAI 应用结合微信支付、公众号、多模态、敏感词做线上服务。随后24年出了 MCP 协议,又紧跟着最新技术做 AI Agent 智能体。现在又开发 AI MCP Gateway 网关。可以说每一步都很超前,企业里需要的技术,小傅哥就带着你最先最快的补充起来。 + +
+ +
+如图所示,这是一套 AI 应用开发路线,按照你的诉求可以选择学习; + +- 时间充足;可以从 OpenAI 代码自动评审开始,之后到 AI Agent 智能体,再到 OpenAI 项目开发。后面可以跟随二阶段项目。 +- 时间紧张;可以先做 AI Agent 智能体,这个项目分为3部分,第三部分是智能体部分。学习后写完简历,后面在学其他部分。 +- 时间很紧;适合于找实习或者校招想加点AI的,那么可以做个 OpenAI 代码自动评审,项目不大,但也是一个两眼的技术点。随着写完简历面试,后续可以继续补充其他 AI 内容。 + +> AI MCP Gateway 正在更新,这也是互联网中必备的 AI 类项目,之后还会继续更细其他 AI 项目。 + +## 二、项目介绍 + +### 1. OpenAI 代码自动评审 + +本套组件是小傅哥基于 `GitHub Actions` + `OpenAI(ChatGLM)` + `Git/GitHub` + `公众号模板消息` 串联出从代码提交获取通知,Git 检出分支变化,在使用 OpenAI 进行代码和写入日志,再发送消息通知完成整个链路。 + +
+ +
+ +
+ +
+### 2. AI Agent 智能体 + +这是一套综合`前后端 + Dev-Ops`,基于 Spring Ai 框架实现,Ai Agent 智能体。耗时7个多月,38节课程(`视频`+`文档`),从 RAG 到 MCP,再实现出互联网企业级,可编排的 Ai Agent 智能体,现已全部开发完成 + 部署上线。💐 + +
+ +
+ +
+ +
+### 3. OpenAI 应用(含支付) + +此项目以应用OpenAI技术,对接多种大模型提供生成式服务,为XXX场景提效。项目的架构设计实现以微服务进行拆分,涵盖;OpenAI-SDK、OpenAI-API、公众号鉴权、微信支付等。并以模块化设计,积木式构建应用,让不同的场景诉求都可以配置化对接。 + +
+ +
+ +
+ +
+### 4. AI MCP GateWay + +该项目是 AI 应用场景下的通用技术服务组件类项目,以解决接口 MCP 协议转换而设计实现。在整个项目中,你可以积累到关于 MCP 协议的深度分析,学习分析协议的技巧和方案,并积累关于设计一个组件解决通用场景问题的能力。 + +
+ +
+ +> 很多能力就是这样,早早的储备起来,以备不时之需!程序员也是一个一直学习的行业!春江水暖鸭先知🦆,码农需要学知识! + diff --git "a/docs/md/about/job/2026-03-21-\345\217\244\346\263\225\347\274\226\347\250\213\357\274\214\346\230\257\345\220\246\350\277\230\351\207\215\350\246\201\357\274\237.md" "b/docs/md/about/job/2026-03-21-\345\217\244\346\263\225\347\274\226\347\250\213\357\274\214\346\230\257\345\220\246\350\277\230\351\207\215\350\246\201\357\274\237.md" new file mode 100644 index 000000000..b28f7f71f --- /dev/null +++ "b/docs/md/about/job/2026-03-21-\345\217\244\346\263\225\347\274\226\347\250\213\357\274\214\346\230\257\345\220\246\350\277\230\351\207\215\350\246\201\357\274\237.md" @@ -0,0 +1,131 @@ +# 纳闷?老板,为什么还不开除你! + +作者:小傅哥 +博客:[https://bugstack.cn](https://bugstack.cn/) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +AI 的迅猛迭代,好像让很多伙伴都开始焦虑,是否会被 AI 替代。今天小傅哥,站在一个老码农的视角,参与过众多复杂项目需求的场景下,帮你分析 AI 是否可以完全取代我们。 + +
+ +
+ +**古法编程!** + +2025年是 AI Agent 工具爆发的时代,AI IDE 也在这一年兴起。从这一年往前,称之为古法编程。AI 都来了,还用学代码吗,反正问 AI 就行了,他可以帮我写好。 + +但 AI 真的能帮你把所有的代码都写好吗?古法编程也真的不重要吗?好!咱们接下来就讨论下! + +## 老板,为什么还不开除你? + +🤔古法编程,是否还重要?如果不重要了,那老板现在应该立马把你开了才对。资本是逐利,不可能这会不拿起屠刀,进行开“猿”节流。 + +那,为什么,我们还在岗位上。用着 AI 和自己,一起写代码呢? + +或者说,软件开发岗位的企业招聘也不用限制学历和专业了,土木的、经管的、美院的、学医的,都可以过来写代码呀。那这到底为什么呢?不是说,现在学编程是49年入国军吗? + +啊!其实,原来有一部分人在扯淡。 + +他们刻意在描述上降低了软件交付的复杂度,并把 AI 编码等同于软件交付,等价于程序员👨🏻‍💻的工作就只是写代码。从而忽视了,一个复杂项目需求,在前期的调研、讨论、分析,产出 PRD 文档,在进行细节的讨论,确保流程的可靠行。最后才是研发设计和编码实现,以及测试验证、预发验证、流量回放等,这才是一整个需求交付的过程。编码,只是其中的一小部分而已。 + +
+ +
+ +**所以**,目前老板还不敢开除你!只会让你使用 AI 加快生产效率。 + +## 外行,为什么也没做出大项目? + +不少外行,特别兴奋于,一句话 AI 写个贪吃蛇,或者搞出个坦克大战。 + +那你就在这个实现的贪吃蛇上,在完成以下诉求; +1. 完整的登录体系,涵盖;手机号验证、公众号扫码、APP 应用可人脸、浏览器要有指纹,登录后同平台服务免登录。 +2. 可微信/支付宝多渠道,支付购买各类场景增强VIP道具,可给任务角色换装。涵盖完整商城体系,用户可在平台售卖个人游戏时获得的特殊道具。 +3. 给用户提供,积分、兑换、返利,以及在活动时间,有转盘抽奖、老虎机🎰、扭蛋等各类配合场景的玩法,增强用户心智和提高用户体验。并提供给运营自主配置使用。 +4. 支持联机对战、定期赛事、总/区排行榜、组队房间、语音对话(YY功能)、打字聊天(注意敏感词过滤)、视频录制(精彩时刻)。 +5. 支持种树、种菜乐园,为贪吃蛇提供家园归属,及其N天种菜,果实成熟后,可获得xxx商城对应sku商品兑换卡,0元支付可配送到家。 +6. 综上,确保数据库事务,以及最终一致性。并支持 MQ、RPC、xxl-job 等分布式架构设计。 + +
+ +
+ +以上内容,可不是胡编乱写,这就是现实中的互联网场景里的业务。也是你天天使用的`淘宝`、`京东`、`滴滴`、`美团`、`腾讯`里面的场景。如果你觉得 AI 可以替代程序员,那么你现在就可以对以上应用全面截图之后让 AI 帮你帮所有内容都写出来。 + +真相就是,AI 没办法,在你自己不清楚详细逻辑的情况,为你全量的完成所有内容的编写。甚至它可能为了实现 A 功能,而导致 B 功能连带不可用。 + +所以,外行们,**燃骚你的 Token 吧,让 API 中转站哇哇赚钱!** + +## 自己,跟着AI屁股后面评审代码 + +Duang、Duang、Duang,写出一堆。但你看不懂,你敢提交吗?出了事故是开除你,还是开除 AI? + +就代码来说,它是对产品功能详细逻辑的具体数字化体现,个人的对产品功能的理解和架构、编码、设计等经验的积累,决定了产出代码的正确性和可靠性。 + +注意,千万不要简单的理解为,你只是在一个新工程或者代码量很少的工程中完成需求。你现在可能要打开2、3个工程,每个工程在历史沉淀中,有几十万行代码。还要与其他部门的伙伴配合,完成接口的开发、对接和联调。 + +那么,这么复杂的业务理解和需求交付情况下,程序员为了更细致的理解细节,也常常会在通过编写伪代码来梳理逻辑,并与产品经理确认临界细节。这个过程是非常重要的。 + +如果,现在换成 AI 全部完成逻辑的开发,那么细节分析的时候,你要写的就是所有开发内容的提示词,这个工作成本并不低(甚至还会漏逻辑,因为不是你持续的写,就可能发现不了)。但对于有经验的高级开发来说,AI 编码的质量,并不一定多高,甚至比如上一些高级开发。所以,他们往往是在一些已经明确自己完全可以写,也可以把控 AI 写的代码时候,会让 AI 来完成这部分内容的编写,之后再进行审查和提交。 + +
+ +
+ +所以,复杂项目场景,并不是一句话 AI 就能全干了。甚至在整个工作来看,AI 只是完成了一少部分编码而已。 + +## 关于,有AI后岗位是否减少? + +减少?反而增多了! + +没有 AI 的时候,业务需求交付就完事了。现在有 AI 了,各种服务都要支持 AI 的接入,又是监控、又是巡检、又是客服,甚至点奶茶都要走个 AI 玩一圈。也因此为 AI 建基础技术设施,包括;AI 网关、AI 脚手架、AI 通用智能体(A2A)、AI 测试标准化验证等等。 + +
+ +
+ +与此同时,各行业也都会被程序员写的 AI 类服务,洗一圈的。春江水暖鸭先知,没有哪个行业有程序员能这么早的感受这个世界的变化了!即使就放在现在,AI 都火成这个德行,部分焦虑的程序员还害怕没工作的时候,其实外行人想装个 OpenClaw 都要付费请程序员帮忙。 + +所以,别傻了。你已经踩在这个时代的风口上了!没有人比你的机会更多。你看现在的 Boss 上有多少新上的 AI 应用开发工程师岗位,哪些做过的 Java 项目,现在都要用 Spring AI + Google ADK(a2a、mcp、skills)在翻盖。 + +
+ +
+ +## 那么,请正确看待AI的价值! + +AI 是这个时代非常牛的工具,也改变了很多的工作协作方式,还为各类场景带来了智能体赋能。如;豆包、OpenClaw、AI IDE、NotebookLM..., + +- 可以使用豆包,做一些内容编写,询问知识,还可以给小朋友讲故事。 +- 可以使用 OpenClaw 完成一些日常工作,整理素材,编写资料,剪辑视频。 +- 可以使用 AI IDE,学习编程,处理开发工作(明确范围的内容实现) +- 可以使用 NotebookLM 整理笔记,学习知识。 + +有太多的好用AI产品,在改变我们的生活。 + +其实整个时代来看,不是学古法编程重要不,也不是 AI 重要不,而是我们走过的这条路,增长的见识,积累的经验,锻炼的思维,重要!没有这些,即使有个工具也没法驾驭的起来。 + +
+ +
+ +之后,我们在说回本质;llm、rag、mcp、skills、a2a、agent work flow,这也是程序员设计出来的呀。所以,ai 的上限,由人决定。 + +--- + +**综上;** + +
+ +
+ +1. 首先,全局视角来看,所有行业,没有哪个行业有程序员这个行业具备最早接触世界上最新技术的了。即使是普通的 gpt 使用,OpenClaw 体验,程序员行业可以折腾,但其他行业直到现在都没法接触、使用和开发。 +2. 之后,说回写代码,写代码是交付的一部分,但交付不等于写代码。交付包括需求的讨论、分析、设计、编码、测试、上线、维护等全流程的操作。之后编码来说,有架构经验,丰富业务知识积累,复杂场景解决方案的程序员和只是初级程序员,使用 AI 也完全不一样。AI 是辅助提效的,但AI 没法完全替代人来写代码。否则互联网公司,现在就可以把人全部开除了,而不是让人又去使用 AI 提效。 +3. 并且35也不是终点,企业里现在也很喜欢老员工,老员工稳定性好,工资相对新人甚至是有点偏低(新人往往好学校的,工作几年培养起来,薪资很不错)。那么老员工的收入偏低,企业的成本不高,而且经验丰富好用。 +4. 如果这,如果那,但不如每一天都进步一点,每一年都有个变化。没有哪个绝对的好,也没有绝对的差,每一个行业,领域,都需要走到腰部以上,深入的积累,才可能让自己具备随时走的出去,也有留下来的本事。 + + + diff --git "a/docs/md/about/me/2020-12-27-2020\346\200\273\347\273\223\357\274\214\344\275\234\344\270\272\346\212\200\346\234\257\345\217\267\344\270\273\347\232\204\344\270\200\345\271\264\357\274\201.md" "b/docs/md/about/me/2020-12-27-2020\346\200\273\347\273\223\357\274\214\344\275\234\344\270\272\346\212\200\346\234\257\345\217\267\344\270\273\347\232\204\344\270\200\345\271\264\357\274\201.md" index b34a64b10..e508c7ad4 100644 --- "a/docs/md/about/me/2020-12-27-2020\346\200\273\347\273\223\357\274\214\344\275\234\344\270\272\346\212\200\346\234\257\345\217\267\344\270\273\347\232\204\344\270\200\345\271\264\357\274\201.md" +++ "b/docs/md/about/me/2020-12-27-2020\346\200\273\347\273\223\357\274\214\344\275\234\344\270\272\346\212\200\346\234\257\345\217\267\344\270\273\347\232\204\344\270\200\345\271\264\357\274\201.md" @@ -1,14 +1,14 @@ --- layout: post category: itstack-code-life -title: 2020总结 | 作为技术号主的一年! +title: 2020年,小傅哥の年终总结! tagline: by 小傅哥 tag: [java,itstack-code-life] excerpt: 快到年底了,写个总结吧!关注我的粉丝朋友,谢谢你!滴水之恩,永不相忘!我没照顾到的伙伴,对不起!我不是有意忽略了你。致我相识的每一位同好,所求皆如愿、所行化坦途。再见2020,迎接2021! lock: need --- -# 2020总结 | 作为技术号主的一年! +# 2020年,小傅哥の年终总结! 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) diff --git "a/docs/md/about/me/2023-01-02-2022\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" "b/docs/md/about/me/2023-01-02-2022\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" index c550b08bc..c3c89b1f3 100644 --- "a/docs/md/about/me/2023-01-02-2022\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" +++ "b/docs/md/about/me/2023-01-02-2022\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" @@ -108,9 +108,7 @@ Github:[https://github.com/fuzhengwei](https://github.com/fuzhengwei) 如果你也愿意成为23年在职场更有竞争力的码农。相信我,加入小傅哥一起学习,是最不亏的决定! -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) **加入链接**:[https://t.zsxq.com/09S1kW2r9](https://t.zsxq.com/09S1kW2r9) **获取优惠**:公众号【bugstack虫洞栈】回复【星球】获取专属粉丝优惠券 diff --git "a/docs/md/about/me/2023-05-07-51\345\201\207\346\234\237\344\273\243\347\240\201\346\227\205\346\270\270.md" "b/docs/md/about/me/2023-05-07-51\345\201\207\346\234\237\344\273\243\347\240\201\346\227\205\346\270\270.md" index 8bd126640..a37c5ffec 100644 --- "a/docs/md/about/me/2023-05-07-51\345\201\207\346\234\237\344\273\243\347\240\201\346\227\205\346\270\270.md" +++ "b/docs/md/about/me/2023-05-07-51\345\201\207\346\234\237\344\273\243\347\240\201\346\227\205\346\270\270.md" @@ -61,9 +61,7 @@ lock: need 小傅哥致力于把星球`码农会锁`开发成`最具互联网应用级实战项目开发学习社群`,让加入的伙伴都能学习到`干刺啦`的硬核干货项目,学习后即可提升编程思维也能锻炼编码能力。 -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) --- diff --git "a/docs/md/about/me/2024-01-09-\344\273\216T4\345\210\260T8\357\274\2144\345\271\264\346\227\266\351\227\264\357\274\2144\346\254\241\346\231\213\345\215\207\343\200\202\346\212\200\346\234\257\346\217\220\345\215\207\346\234\200\345\277\253\347\232\204\351\202\243\345\207\240\345\271\264\357\274\214\346\210\221\345\201\232\344\272\206\344\273\200\344\271\210\357\274\237.md" "b/docs/md/about/me/2024-01-09-\344\273\216T4\345\210\260T8\357\274\2144\345\271\264\346\227\266\351\227\264\357\274\2144\346\254\241\346\231\213\345\215\207\343\200\202\346\212\200\346\234\257\346\217\220\345\215\207\346\234\200\345\277\253\347\232\204\351\202\243\345\207\240\345\271\264\357\274\214\346\210\221\345\201\232\344\272\206\344\273\200\344\271\210\357\274\237.md" new file mode 100644 index 000000000..e1863be05 --- /dev/null +++ "b/docs/md/about/me/2024-01-09-\344\273\216T4\345\210\260T8\357\274\2144\345\271\264\346\227\266\351\227\264\357\274\2144\346\254\241\346\231\213\345\215\207\343\200\202\346\212\200\346\234\257\346\217\220\345\215\207\346\234\200\345\277\253\347\232\204\351\202\243\345\207\240\345\271\264\357\274\214\346\210\221\345\201\232\344\272\206\344\273\200\344\271\210\357\274\237.md" @@ -0,0 +1,94 @@ +--- +title: 从T4到T8,4年时间,4次晋升。技术提升最快的那几年,我做了什么? +lock: need +--- + +# 从T4到T8,4年时间,4次晋升。技术提升最快的那几年,我做了什么? + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +哈喽,大家好我是技术UP主小傅哥。 + +**从T4到T8,4年时间,4次晋升**。这四年间的快速成长,主要来自认知的改变。我开始有意识的圈定自己的`技术地盘`,形成一套自己的技术体系壁垒,让自己有更多的可能接住更多的机会。而认知的提升是见人、见事,以及能在不确中坚持并获得收益。 + +
+ +
+ +成绩的结果只是我自己的,但过往的经历却可以帮助很多人! + +在这段过往中我清楚地认识到,千万别让自己只成为别人手里的螺丝钉或工具包。`CRUD仔`、`SQLBoy` 这些工作,只能一个季度一个季度、一年一年的消耗你的时间和精力。而那些看似有难度的东西,如;`编程范式`、`设计原则`、`架构模式`、`架构风格`等,也只是因为你接触的少,所以感觉难。就像不少伙伴都觉得落地 DDD 复杂,使用 MVC 简单。但如果你最开始接触的就是 DDD 呢?有人带着你搞 DDD 的项目呢?让你一次次从`创新的架构`、`高级的编码`、`场景的处理`、`方案的手段`中,吸收这些能快速提升能力的知识,你会进步很慢吗? + +那么,接下来小傅哥就给你分享下整体架构的成长路线,以及要提升能力需要做哪些类型项目。 + +>文末有获取资料方式,还有送福利 4.0 50万 Tokens 活动!🉐 + +## 一、我是怎么想的? + +4年多时间我在 [bugstack.cn](https://bugstack.cn/) 博客,写了`几百万字`技术内容,涵盖;系统架构、设计模式、源码、JVM、中间件、IDEA Plugin、字节码增强,以及各类实战项目。那你觉得这些技术内容都能帮助到工作提效?不一定,甚至可能有些工作中都不会使用。 + +就比如;手写JVM,但几乎2~3年也没有一次处理过JVM的问题。做字节码增强 + JavaAgent,但公司里也有专门的团队运维着全链路监控系统。懂得域名申请、备案、Nginx配置、前后端镜像打包部署发布,但公司有全流水线的CI/CD持续交付工具。 + +**那都用不上,不白折腾了?** + +
+ +
+ +首先,这不白折腾,通过这些看似也用不上的学习,却构建了个人的技术栈体系逐步的闭环。就像当你要设计系统架构和遇到复杂问题时,其实这些看似没用上的知识,都是环环相扣的。他们可以让你对一件事想的深,看地更远。 + +然后,当我们来表述自己完成的工作时,往往需要站在当下的工作,先从上往下看,我为什么做这个事。之后从这个事往远看,做这个事能带来什么?这些技能是你在跳槽、面试、述职、分享时,非常重要的本领。否则你只能做一个执行者,让做啥做啥的工具。 + +最后,工作和个人,本身就是两条路线。当个人的能力远超工作所需的时候,也就是你能进入另外一个圈层的时候,且这个行为是不断正向循环的。而且个人的能力提高后,对于工作上的输出往往会有时候做出精彩一击,让你也能站在聚光灯下。 + +>感受:做长期规划,脱离平台,仍旧具备个人价值。 + +## 二、有价值的学习 + +其实技术个人的成长往往是很快的,可能一年前还是`CURD仔`,一年后就能独立架构系统,承接复杂需求。这样的伙伴成长速度,在大厂中非常常见。那是大厂的伙伴就很聪明嘛,很猛吗?其实也不是,而是什么环境,就容易锻炼什么样的素质。就像小傅哥在大厂的多年工作经验,编写的相关资料和实战项目,都是能帮助你锻炼出这样的能力,包括; + +- 【思维】清楚地理解系统的演变、微服务的拆分、分布式设计的设计,而不是一讲到高并发就来说多线程。 +- 【技术】熟练的运用 Spring、SpringBoot、MyBatis 等开发框架技术,并对其使用源码所提供的接口、类、SPI标准开发各类组件,有一定的设计思路和落地能力。 +- 【技术】具备核心技术组件的使用能力,okhttp3、Guava、RateLimiter、Hystrix 等,设计出可靠的系统。 +- 【技术】具备分布式技术栈的选型和使用,RPC、MQ、任务调度、分库分表,并基于分布式技术栈设计和落地系统。 +- 【技术】可以熟练构建系统的 ELK 日志服务、分库分表数据同步、skywalking 全链路监控、Prometheus + Grafana 监控面板。 +- 【架构】逐步了解并掌握,领域驱动设计(DDD)、微服务、微内核的架构模式 +- 【架构】学习组件化设计,懂得系统边界的拆分,理解并能驾驭高内聚低耦合的编码经验。 +- 【架构】通过结构化、函数化的面向对象思维,合理的设计系统,并开发出功能与框架分离,最小化复杂度的工程代码。 +- 【设计】熟练运用工厂、策略、组合、模板等设计模式,编写出具备良好扩展性的代码。 +- 【设计】通过单一职责、接口隔离、依赖倒置的设计原则手段,让代码更加清晰。从而做到整体的业务复杂不会影响局部的处理。 +- 【方案】学习众多场景解决方案,如;三方支付对接、支付掉单补偿、大模型SDK设计和对接、低延迟任务调度、营销平台复杂性玩法等。 +- 【其他】具备组件的设计开发能力,基于对同类共性需求的能力,开发出通用的技术组件。 + +以上,这些方面的内容举例,都可以从小傅哥所编写的文档和项目中学习到。此外还有非常多的细节架构方案,都是大家可以参与学习的。 + +## 三、一张架构成长地图 + +为了让我的粉丝伙伴,更加清楚地了解到架构师所需的技术类技能成长,这里小傅哥根据这份非常专业权威的资料 [developer-roadmap By Kamran Ahmed](https://github.com/kamranahmedse/developer-roadmap) 做了中文版的内容梳理。 + +
+ +
+ +原搞下载:[https://www.alipan.com/s/LqwsNfHRx54](https://www.alipan.com/s/LqwsNfHRx54) + +以上这份资料不非得作为架构师指导路线,也是编程技能高级进阶类参考说明。这是一份理论资料,点开`小圆点⭕️`会看到每条内容的具体明细介绍。 + +>成长的路线地图能明确方向,但在这个过程中还需要配套的资料和项目,让伙伴们下场锻炼。而小傅哥就是想通过自己在大厂的积累,编写出相应的成长锻炼项目,让各位得到快速的技术积累。 + +## 四、技术学习资料 + +为了能让粉丝伙伴能像小傅哥一样得到全方面的成长,这里小傅哥在建设星球「码农会锁」的资料库时,分出;基础教程、业务项目、组件组件,以及开源项目、源码学习、架构方案这样几个大的模块。日常中小傅哥会把大厂的设计思想和技术手段,分享给小伙伴们。并在每个周末更新实战项目,目前【如图】星球已经完成了7个项目,以及1个在进行中的。 + +
+ +
+ +>这样一套来自还在一线编码的架构师,编写的成体系的原创项目,在任何平台都是很难找到的! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +关于星球更多了解:[1万人社群的小傅哥知识星球好吗,值得买吗?](https://bugstack.cn/md/zsxq/memorabilia/ten-thousand.html) diff --git "a/docs/md/about/me/2024-01-28-\345\244\247\345\216\202\346\236\266\346\236\204\345\270\210\345\260\217\345\202\205\345\223\245\357\274\214\344\270\212\345\255\246\346\227\266\351\203\275\345\201\232\350\277\207\345\223\252\344\272\233\351\241\271\347\233\256\357\274\237.md" "b/docs/md/about/me/2024-01-28-\345\244\247\345\216\202\346\236\266\346\236\204\345\270\210\345\260\217\345\202\205\345\223\245\357\274\214\344\270\212\345\255\246\346\227\266\351\203\275\345\201\232\350\277\207\345\223\252\344\272\233\351\241\271\347\233\256\357\274\237.md" new file mode 100644 index 000000000..9552cfdd3 --- /dev/null +++ "b/docs/md/about/me/2024-01-28-\345\244\247\345\216\202\346\236\266\346\236\204\345\270\210\345\260\217\345\202\205\345\223\245\357\274\214\344\270\212\345\255\246\346\227\266\351\203\275\345\201\232\350\277\207\345\223\252\344\272\233\351\241\271\347\233\256\357\274\237.md" @@ -0,0 +1,183 @@ +--- +title: 大厂架构师小傅哥,上学时都做过哪些项目? +lock: need +--- + +# 大厂架构师小傅哥,上学时都做过哪些项目?—— 10年+过往阶段成长历程 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +常听兄弟们👬🏻提到一堆烂大街CRUD项目;`图书管理系统`、`考试管理系统`、`成绩管理系统`等等。这些项目小傅哥上学时候也练手做过,还创新出了😂`彩礼管理系统`,共计9个!**呐,既然兄弟们喜欢CRUD,今天就全部分享给大家!💐** + +
+ +
+ +**这9个项目,全是上学阶段的原创!** + +考试抽题存题系统、分数计算器、即时仿QQ聊天、学生成绩管理系统、在线考试系统、图书馆系统、彩礼管理系统、串口通信聊天、物流配送系统。还有C++ MFC 开发的播放器。 + +整个的这一堆项目,也是我在上学阶段的学习历程,通过各个项目锻炼常用各类技术栈的使用。如果一些小白伙伴感兴趣小傅哥10年前的代码,也可以参考下。通过这样一个基础类学习参考,让自己度过小白阶段。 + +
+ +
+ +那么,接下来小傅哥就分别展示下,这些过往**青涩项目的成果**,以及代码片段。当然还有现在小傅哥带着大家做项目的设计和实现手段,这样你就知道10年码农的一个成长历程! + +>🧧文末有加入学习链接,可以获得9个CRUD项目,8个高级编码实战项目。 + +## 一、CRUD 项目展示 + +小傅哥,13年毕业🎓,在以前上学阶段是没有像现在这么多学习资源的,也很少能在网上检索到学习资料,大部分都是想着自己能做点什么项目,锻炼下学习的各类技术栈,如:Java、Swing、Servlet、JSP、Spring、SpringMVC、MyBatis、Hibernate、Socket、HTML、Div+CSS、JQuery等。所以也就有了这样一个个锻炼的学习项目。 + +这些练习项目的编码难度都是非常低的,更多是想让小伙伴们了解下小傅哥一走学习编程的经历,给小伙伴们一些学习&成长参考。这些项目的源码也可以帮助纯小白伙伴,了解最简单流程的实现方式。 + +### 1. 彩礼系统 + +在我的项目开发笔记中有这么一条 **2012年2月11日 13:17:35 小付彩礼系统制作完成**。12年前的那天寒假下午,在家开发完了这个锻炼的项目。 + +
+ +
+ +好多年后,我才知道。那年的彩礼账单,真的在多年后发到了我的手上。不过不是买楼,而是22年出手卖掉一套房子。以前觉得自己想着彩礼大家收着不好意思,不如一套在线的彩礼系统。现在才知道,是做了一套账单系统!如果这套东西在加上微信支付,真的就可以做成在线收彩礼的系统了。 + +**那些年记录的笔记** + +```java +2012年2月5日 13:22:18 +1:有汉字的url图片不显示 +2:截图的img不显示 +3:背景类型的添加图片显示 +4:img scr类型的设置图片不显示 +5:在ClUser_login.jsp里面引入css样式不好使 + 所以直接把css样式写到jsp页面里面 + 如果所使用源码用户,工具不出bug可以直接引用 login到jsp页面里面。login.css在css文件里面 +2012年2月7日 11:30:48 +6:当使用hql查询时候一定要注意顺序 + select from where group by + 类似这样的顺序 +7:在struts2+hibernate后 在使用hql语句的时候 + 不能使用? setParameter(0,"");的形式 + 这样会得不到结果 +8:在本项目中max 和 min 的使用 有时候会出现 bug 不一定max 就是最大的结果 min就是最小的 结果 +9:有时候有的jsp页面对引入的jq不起作用,所以还 需要用js+dom解决 +10:对于登录要获取list的页面,使用了分组查询语 句以控制重复的内容出现 +2012年2月8日 13:36:26 +11:当list类型为数据库类型时候,在写hql语句的 时候不能使用Select 具体某个信息,这样会得不 到所查询的信息。最后导致无法遍历 + 所以直接写from * ... ...既可 +2012年2月9日 13:14:58 +12:因为兼容性视图的设置,会把原来css样式搞乱 + 所以不要乱改兼容性视图设置(浏览器设置) +2012年2月10日 11:50:52 +13:当浏览本系统的时候,目前建议用MyEclipse自带的浏览器。否则css,js,jq部分会不支持。 +2012年2月11日 13:17:35 +小付彩礼系统制作完成 +后期有待更新05版 +2012年2月18日 13:11:06 +后期补写 +因为在login页面每次连接到新页面,都是在原页面填充,所以在a标签加入这个属性解决问题target="_blank" +``` + +### 2. 在线考试 + +在做彩礼项目前,我还下手干过一个在线考试系统。在大二那年,2011年11月09日 13:39:04 我开始着手设计在线考试的功能流程。 + +
+ +
+ +以前做的一些锻炼练手项目,都是直接打开工程就撸代码,但逐步发现这么没有规划路线的干不太行。所以从在线考试项目开始,知道做这样的项目要好好的规划下,整体的流程是什么样,要的功能有哪些,怎么做一些设计。有了文档在编码,就变得清晰多了。 + +所以,一些新人伙伴在学习的时候,也不要一上来就直接撸代码。思考、设计,是编码前非常重要的流程。虽然可能刚开始还做不出什么完善的设计内容,但这个过程很重要。逐步的就会锻炼出那些强劲的设计思维。 + +### 3. 物流配送 + +临近毕业前又做了一个更大的 CRUD 物流配送系统,包括前端页面、后台管理,全部流程都是查库、写库。可以说是把 CRUD 又拉了一个新高度。要是放在现在,这样一个项目都不够面试用的了! + +
+ +
+ +所有这些项目都可以加一句:“本项目纯属虚构,如有雷同请把雷同项目删掉”。 + +出门在外,身份是自己给的。项目需求也是自己给的,这边造完需求,那边打开 IntelliJ IDEA 锻炼。到这个项目可以说是把 CRUD 升级了,将近20几个流程管理全是 CRUD 查库、写库、改库。 + +>这些项目也就仅能看看流程,纯流程的 CRUD 代码是价值不大了。如果能对照这些文档和UI页面按照现在的方式编写还是有价值的。其他的项目可以从文末获取链接下载查看。 + +## 二、高质量学习 + +10多年的成长经历得出;其实就学习来说,CURD 项目有那么 1~2 个,锻炼下就可以了。如果早些年我就能接触到,现在这样的我提供的学习和路线,我会更快的提升。 + +接下来我再介绍3个项目,可以让大家感受到更核心的东西是如何设计的。 + +### 1. OpenAI 大模型项目 + +项目:《OpenAi 大模型应用服务体系构建》 +架构:微服务架构设计,OpenAI-SDK 多模型组件【ChatGLM、ChatGPT】、DDD 应用服务API封装、WEB REACT 前端界面 +地址:[https://bugstack.cn/md/project/chatgpt/chatgpt.html](https://bugstack.cn/md/project/chatgpt/chatgpt.html) + +
+ +
+ +项目是一个包括`前后端 + Dev-Ops`,全栈式编程,的硬核项目!基于 React + SpringBoot + Nginx + Docker 云服务部署的 OpenAI 应用项目。并且是能上线对外提供服务使用的项目!`不同于一些开源项目,本项目具备完整的前后端开发和实施部署方案。` + + +### 2. 大营销平台系统 + +项目:《大营销平台系统》 +架构:SpringBoot、DDD、微服务架构【Dubbo、RabbitMQ、Redis、Sharding-JDBC】 +地址:[https://bugstack.cn/md/project/big-market/big-market.html](https://bugstack.cn/md/project/big-market/big-market.html) + +
+ +
+ +这个新项目,结合小傅哥已经带着大家完成的 OpenAi 大模型应用业务场景,做上层的营销活动。这就像互联网公司中有了电商、外卖、出行等场景一样,在场景之上做营销活动。所以我们的新项目是 **《大营销平台系统》**!因为小傅哥的星球之前做过了一个抽奖,那么这个项目会用新的DDD架构,对抽奖系统进行重构,并扩展出`营销账户`、`用户返利`、`积分兑换`等服务,完成一整套的营销平台功能。💥 + +### 3. API网关系统 + +项目:《API网关架构设计,从单体服务到微服务的架构演进》 +架构:微服务架构设计、SpringBoot Starter 组件设计、DDD 领域驱动设计 +地址:[https://bugstack.cn/md/assembly/api-gateway/api-gateway.html](https://bugstack.cn/md/assembly/api-gateway/api-gateway.html) + +
+ +
+ +API网关系统用于统一管理RPC(Dubbo)通信接口,通过协议解析和泛化调用统一对外提供HTTP服务的系统。这套系统是微服务架构设计,分为核心通信、启动引擎、注册中心、管理平台以及上报接口服务。这套API网关也是随着对公司传统庞大的单体应用(All in one)拆分为众多的微服务(Microservice)以后,所引入的统一通信管理系统。用于运行在外部HTTP请求与内部RPC服务之间的一个流量入口,实现对外部请求的协议转换、参数校验、鉴权、切量、熔断、限流、监控、风控等各类共性的通用服务。 + +## 三、获取项目 + +小傅哥的**星球「码农会锁」**主要以带着大家做公司`应用级实战项目`为主,目前已经交付完成7个(项目演示:[https://gaga.plus](https://gaga.plus)),正在进行中的1个(大营销平台系统)。除此之外还有;开源项目、技术小册、基础教程、架构方案、应对招聘、职场晋升经验等分享。 + +**项目地址**:https://gitcode.net/KnowledgePlanet/crud - 加入星球后可置顶消息,申请仓库权限。 + +
+ +
+ +本次内容小傅哥再把以前做过的CRUD项目分享给大家,作为加入星球的附带品,当个玩具让大家参考。星球中更多价值在于那些高级的实战项目,这些东西的学习才能帮助大家更快速的成长,在面试中提高竞争力。 + +>星球中的高级项目,对于需求场景的设计和编码技巧都非常多,贼适合贼面试中秀出能力,让面试官眼前一亮,斩获Offer! + +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) + +--- + +- [从T4到T8,4年时间,4次晋升。技术提升最快的那几年,我做了什么?](https://mp.weixin.qq.com/s/xr4A6yk_HxwJo06P8VdcHw) +- [小傅哥知识星球咋样,值得加入吗?](https://mp.weixin.qq.com/s/KWJFPQSa69TxFGYPWDJ69g) +- [小傅哥自研插件,为开发提效80%,已经有8.1k安装量!](https://mp.weixin.qq.com/s/kAk09Onhw7sHonhXdn0QzA) +- [从 MVC 到 DDD 重构,我们有了新想法!](https://mp.weixin.qq.com/s/UyqhQrHyBTDujfv8w4bhXQ) + + + + + diff --git "a/docs/md/about/me/2024-02-07-2023\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" "b/docs/md/about/me/2024-02-07-2023\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" new file mode 100644 index 000000000..37de2c6dd --- /dev/null +++ "b/docs/md/about/me/2024-02-07-2023\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" @@ -0,0 +1,88 @@ +--- +title: 2023年,小傅哥の年终总结! +lock: need +--- + +# 2023年,小傅哥の年终总结!—— 慢下来不投机取巧,也能发展的挺好。 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +你穿的是地,你批的是天,走的是阳关道,奔的是日子甜。😄又是一年过去了,截止到2023年结束,总计发表366篇原创技术文章,完结了7个编程项目。累计公众号粉丝12万,星球付费用户1万。3年前的一个慢下来的决定,让我在3年后有了不错的收获。**原来慢下来不投机取巧,也能发展的挺好。** + +
+ +
+ +>这一年经历了好多,在职场、在副业、在家庭,都有不同的故事发生。以此文记录,留作多年后来看。 + +## 一、在职场,异动走了 + +15年加入互联网大厂,先后经历了;消金、生态、校园、中台、用户,等部门。兜兜转转了一圈,23年4月1日(愚人节),又回到消金。 + +这一圈走下来经历了好多,也几乎是完美匹配互联网发展的周期,从早期的野蛮蓬勃,到圈地创新,再到硬卷PPT,最后回到核心根基。记忆深刻的是,在这一圈中有一段经历是总在写PPT,把词语一遍遍完善,找出一个个可能的创新,形成一套套好讲的故事。如果你也遇到这样的情况,那么最好赶紧异动或跑路,因为这个组真的没啥业务的需求了! + +这样的一个组除了绩效低,还受整组的影响,对个人年终总评也是比较差的。比如给你一个`XXX标签`打分不及格😂。 + +所以,后来不少拿到Offer的伙伴,问小傅哥是否能去的时候。我一定给出这样一个参考,你去的这个部门是否舍得给钱,一般现在不舍得给钱的,后面也别想赚到更多的钱。因为那可能是个很边缘的部门。网传腾讯有个部门叫【五香鸡】年终奖嘎嘎给力,还有个部门叫【惨死鸡】,那个叫五香鸡的部门是微信事业部(WXG)。 + +当我重新回到原来的部门,工作了一个季度,与新领导1v1的时候。"我说,终于可以安心的做些技术的事了。不在急功近利的今天做,明天就要PPT,后天就找别的部门推广!也不再非得造成一些词包装成绩,而是可以用原本的本质描述结果。" + +23年来说我的异动算晚的,与我一起的伙伴早早的就离职或异动了,因为刀砍到他们那更快(绩效低、无加薪、无晋升)。但是这些伙伴能力不好吗。`一个去了字节`、`一个去了核心业务线还承担核心职责`。**所以记住,有时候不是你不行,是平台差。** + +23年4月往后的职场也逐步趋势向好,不只是我所在的组,也是整个公司。不再要求管理者以外的研发编写周报,也不需要年中和年末PPT述职,也去掉了非高T的测评。大家在评审的时候,也都用文字资料和有必要的图来阐述等。这些都给研发这一层做了减负,把心放在做事上。当然如果还是有的组在玩命卷汇报话术,像美工一样精修PPT图,那一定是和那个领导有关,与公司的整体价值观背道而驰。 + +当然,工作嘛,就是用时间换取固定的钱。不可能太完美符合你所有心意。而在职场生涯要有意识的知道,这有2条路线,一条是工作本身,一条是自我成长。没有永远稳定的工作,只有自身的能力远超工作所需,并在不同年龄阶段都能匹配到对应的能力范围,才可能相对的稳定。**记住,技术嘎嘎重要,那是你作为研发的立身之本** + +## 二、在副业,有成绩了 + +我常听到过一句话:"提前给你一张清华大学的录取通知书,条件是未来两年里,你要坚持每天6点起床,每天坚持背30个单词,每天坚持做一张试卷,每天坚持比其他同学多学习两个小时。" + +但其实在我们的生活里,并不会有人给你一个明确的目标,告诉你做什么,你就可以获得哪些。反而普通人的努力,大部分都是不确定的`结果努力`。甚至是需要长期在不确定中,坚持做确定的事。但这个确定的坚持成本并不低,可能是1年、3年,也可能是5年。并且坚持也不是你一个人的事,尤其是结婚有娃,那就是整个家庭的事。这个过程很需要家庭的支持,否则你将没有太多的时间投入到你坚持的事情上。 + +我于19年7月开始正式编写技术文章,不去蹭热点、不去造故事、也不接广告。只是默默的写出一篇又一篇的技术文章,开发出一个又一个的实战项目(`大营销平台`、`OpenAi大模型应用、Lottery`、`IM`、`AI问答助手`、`API网关`、`中间件`、`IDEA Plugin`)。这样的实战项目,每一个都要写半年多,从0到1的方式一节节的完成。这样虽然慢但没关系,因为我所具备的技术经历是极其充足的,这是很多的小培训机构不具有的。他们也招聘不到大厂的真材实料的`研发&架构师`,可以让培训讲师面试个大厂的高级架构师试试看,工资远比当培训讲师来到多。 + +这也是我的优势所在,所以我宁愿慢下来,慢慢输出。这样可以让跟随我的粉丝伙伴,能学习到有价值的技术项目,而不是那些自导自演的demo案例。因为进入公司真的不需要那一堆堆的小demo。所以公司需要啥样的技术,我就会慢慢的一个个写下来,并做出一个个项目让大家学习。 + +**直至23年,全网累计40万读者关注、公众号粉丝12万、星球付费用户1万。这我深刻的感受到,原来慢下来不投机取巧,也能发展的挺好。** + +那怎么做,才能把一件事做成呢?🤔 + +我举个例子;在这些年的创作中,我从不认为某个技术不好,而应该是不同的场景有不同的选择技巧。不少小伙伴时而看到一些分享,专为喷一些技术而做的视频/文章,会比较激动。但你要知道,他喷是为了流量(一个阅读5毛到1块),而你信就是耽误自己了。一个人如果不能坚定自己的想法,是很容易被一些碎片化带有情绪的内容所左右的,甚至还要对喷几句。或是容易和别人/同事发生矛盾。那说明你现在自身的内耗是很大的,应该要尝试改变。 + +我们用3年学会了说话,但却要用一生学会闭嘴。当你努力做好自己的事,不再试图叫醒一些人时,你才可能有机会走到最前面。所以你不非得聚众,因为往往跷跷板轻的那一面,才会站的更高。当大家都说好的时候,机会也就没了。 + +
+ +
+ +>🧧[加入星球](#) 关注公众号「bugstack虫洞栈」回复「星球」可以获得优惠券。项目展示:[https://gaga.plus](https://gaga.plus) + +## 三、在家庭,新添成员 + +在职场中那个`XXX标签`打分,差点影响了我生活的幸福指数!为啥呢?🤔 + +23年2月,嘿嘿,我的女儿👧🏻出生了💐🧨。当过爸爸妈妈的小伙伴会知道,小孩出生后会需要晒黄疸,如果屋子没有阳光是一件挺麻烦的事。最开始我租住的房子🏡就是那种一天只有早上不到1小时的光照,但日常和媳妇也都是上班/下班,大多数周末和放假的时候都会回廊坊(后来被我卖掉了)。所以也光照不多也没啥影响,夏天还比较凉爽。这样屋子就这样住了6年多。 + +所以在女儿快出生前,媳妇说要不换个房子🏡吧(很聪明)。我一想也是,我都工作这么多年了,绩效还不错,排队公司的公租房也是应该可以的。之后就开始申请排队,进去一看1%,说我排名比较靠前。嘎嘎开心,好消息是1个月左右就排到了,差一点的消息是5.1才能入住。哈,好家伙。这中间还差了几个月,所以又临时在外面租了3个月的房子。后来在5.1的时候👪一家人搬入了新家。 + +这中间如果我晚申请一个月,我就排不上了。因为`XXX标签`打分不及格,后来再看排名跑到了5%。所以媳妇很聪明😂,好在早点申请,听媳妇话能发家致富。我也就是从那时候,决心异动🤨,影响我生活运势的团队,不能再继续了! + +😂5.1搬进新家做的第一件事,是老妈小区捡到一些床的木板,以前是木匠师傅的老爸用木板做了新的厨房灶台。 + +在我们入住这个新房子的时候,厨房的灶台面板是塌陷的,怕它突然断掉。所以和物业维修报备后,嘎嘎快的就给换了一套全新的(很给力)。但我们忘记了,这新的有甲醛。聪明的媳妇几乎买变了所有的甲醛测试仪,测试屋内甲醛超标,厨房更是报表。所以,老爸来了以后,拆了台面,用老妈捡来的木板搭建了新的厨房。—— 后来,我对用嘴对着甲醛测试仪吹起,它也超标。也不知道甲醛测试仪准不准了,反正厨房是拆了!至少这样不担心影响娃就行。 + +有了老爸、老妈在,我和媳妇也能安心上班。有聪明的媳妇在,也总是做出机智的决定。有娃在,多了很多故事,让原本快节奏的生活,在家里能有一份慢下来的时光。 + +## 四、在未来,一些计划 + +一路在互联网工作,看到了很多部门、小组,对应的产品的设定、落地、发展到解散。可以看得到的是,很多意想出来的非真实用户诉求的,大部分都很难存活下去。就像一个产品的主线核心功能都还有用户体验不佳的时候,但却花费大力气做一些完全不相关的功能【我们用过的很多互联网产品,都有这样的情况】。有时候那些没用的产品功能,很多时候都是因为好汇报,好讲故事,好晋升用。 + +所以,对于我在职场发展、在技术积累、在作品创作,都会保持做长期有价值的事。就像编写的很多技术文章,不一定有多炸裂的阅读量,但对于研发的技术体系成长一定是有价值的。 + +电影🎬《疯狂动物城》有个闪电,办事很慢,一个字一个字的。但最后出现开车的时候,一溜烟的就不见了。因为它踩下去油门,抬脚也很慢。有时候慢下来才是快,在这个社会中,当大家都急功近利的图快,你只需要慢下来,用慢下来的一年对比求快的一个月,你会发展的非常漂亮。 + +好啦,祝大家2024,身体健康,开开心心 🍻💐😄 + diff --git a/docs/md/about/me/2024-11-17-gold-content.md b/docs/md/about/me/2024-11-17-gold-content.md new file mode 100644 index 000000000..5c0e6528f --- /dev/null +++ b/docs/md/about/me/2024-11-17-gold-content.md @@ -0,0 +1,90 @@ +--- +title: 小傅哥的含金量,还在上升! +lock: need +--- + +# 小傅哥的含金量,还在上升! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +嘿嘿 😈,有时候我也会去网上对我评论,或者爬爬楼看看群里有人提到我时候的评价。当然无论什么内容,我基本不会去参与。毕竟让人怪不好意思的。不过我偷偷截图下来,当做对自己所做事情的鼓励。**你也想知道大家对我怎么评价的不?🤔** + +
+ +
+ +**不装了,我是大厂架构师,我摊牌了!** + +能在一家互联网大厂公司工作近10年,一直在一线核心业务场景开发&架构,又不断的承担高并发项目的架构和设计落地。那么他的广度、深度都会有非常不错的积累。如果他在能陆续的输出这些内容让人能学习到,可以说是做了非常有意义的事情!而小傅哥就是那个其中的他。 + +
+ +
+ +在我编写的博客量内容越来越多以后,我得到了很多的正反馈,包括;非常多的公司把这些内容作为`架构指导建议`、`编码行为规范`、`场景方案示例`,也有高校老师直接点名学生跟着小傅哥学项目,还有伙伴在各个论坛留言的认可。嘿嘿 那可以刻我觉得做有价值的技术分享真不错。 + +## 一、老师说;先跟着小傅哥做项目! + +我的大学导员时而也会和我聊,现在大学生要学习哪些技术,才能和公司招聘要求匹配。因为在校的学生👩🏻‍🎓做的项目,很多都是 CRUD 的,这些内容刚入门的时候做1-2次还好,但再重复就没有意义了,缺少广度和深度锻炼。所以非常需要像做公司里的项目一样成长。 + +没想到类似这样的对话,在其他高校的老师和高校的学生也给他的学生和伙伴分享。 + +
+ +
+ +
+ +
+ +也是因为有这样多的信任和认可,我也变得"越来越卷"😂,不断的卷出新的实战项目!这些项目的不是就只完成功能,而是把大厂中的经验整合进来,把业务、架构、设计、编码的技巧,逐步的呈现到项目中。所以跟着小傅哥学习,会吸收的非常多。 + +## 二、评论区;这群死鬼还真认可我! + +有时候我的粉丝伙伴会把一些评论截图给我,告诉大家对我的认可。还把我引流到 linux.do 注册了个账号😂。 + +
+ +
+ +
+ +
+ +- 其实当你有非常强的积累,有足够的广度和深度,用大量的时间做技术积累,其实是会得到很多伙伴的认可的。 +- 像是 OpenAI 大模型、大营销这样的大项目每个都花费1年的时间进行打磨输出。而且我也会有意的把互联网大厂中的技术方案,随着项目输入进去。这就像你吃一个东西一样,层层都有口感。演示:[https://openai.gaga.plus/](https://openai.gaga.plus/) - 当你看到小傅哥编码的项目,会让你对系统架构和工程编码都有非常高的认知。 + +## 三、产品线;为开源社区做贡献! + +写开源组件、做工具插件,又发表过十多个专利。小傅哥不只是带着大家做项目,日常也会搞一些小东西,让开发工程师可以使用,提高编码效率。 + +地址:[https://plugins.jetbrains.com/plugin/18262-vo2dto/versions](https://plugins.jetbrains.com/plugin/18262-vo2dto/versions) +源码:[https://github.com/fuzhengwei/vo2dto](https://github.com/fuzhengwei/vo2dto) + +
+ +
+ +💱 vo2dto IDEA Plugin 1.89万下载量,解决对象转换问题。选定对象批量织入“x.set(y.get)”代码,帮助开发者自动生成vo2dto转换代码。 + +✨ 特性 + +1. 2个对象的转换操作,通过复制 X x 对象,转换给 Y y 对象 +2. 允许使用 lombok 对象转换、lombok 和普通对象转换,对于 serialVersionUID 属性过滤 +3. 支持类继承类,全量的对象转换操作 +4. 含记忆功能的弹窗选择映射关系,支持全量对象、支持匹配对象、也支持空转换,生成一组set但无get的对象 +5. 支持对于引入不同包下的同名类处理 +6. 支持 Lombok.Builder 模式创建转换对象 + +## 四、卷工作;用强悍的实力拿下大厂Offer + +跟着小傅哥学习,不会浪费时间,不会走小道弯路。全程做技术兜底,遇到的各种问题都能帮你解决。包括你学习时候的代码bug,可以把代码提交到星球,我来帮你调试。最终把兄弟们送到各个竞争赛道的头部,拿到最牛的薪资待遇 Offer! + +
+ +
diff --git "a/docs/md/about/me/2025-01-04-2025\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" "b/docs/md/about/me/2025-01-04-2025\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" new file mode 100644 index 000000000..5c07b93af --- /dev/null +++ "b/docs/md/about/me/2025-01-04-2025\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223.md" @@ -0,0 +1,145 @@ +--- +title: 2025年,小傅哥の年终总结! +lock: need +--- + +# 2025年,小傅哥の年终总结! + +作者:小傅哥 +博客:[https://bugstack.cn](https://bugstack.cn/) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +喜悦、兴奋、困惑、焦虑、释然、强大。2025年,是我的本命年,12年的一个轮回,好像所有做的事情,在这一年都有了一个阶段性的表现。**就像因果不是你一拳我一脚,而是蝴蝶振翅一念换天。** + +
+ +
+ +12年一个轮回,也和【道】符合。所以,今年的年度总结,换个行文方式,用 [《道德经》](https://daodejing.org/) 第42章比较热门的内容,做为引子,来串联故事线,希望小伙伴们喜欢😄。**好啦,2025年的,年终总结正式开始!** + +
+ +
+ +>道生一,一生二,二生三,三生万物。万物负阴而抱阳,冲气以为和。人之所恶,唯孤寡不毂,而王公以为称。故物,或损之而益,或益之而损。人之所教,我亦教之。强梁者不得其死,吾将以为教父。道德经,资料:[https://daodejing.org/](https://daodejing.org/) + +## 一、副业,道生一 + +>道生一,一生二,二生三,三生万物。 + +起因动念,决定做的事,想好走的路,就是道。开始执行便有了一,之后又用了6~7年时间,开枝散叶完成了一个自己的IP品牌建设。 + +6年时间里,从最初的写技术博客,到编写技术小册和出书,再到带着读者粉丝做实战项目。我用10年+互联网大厂的技术经验积累和产品业务认知,帮助读者构建一个1:1还原真实工作环境的超大技术资料库,覆盖全部的核心应用技术,以及截止到今年有19个应用级实战编程项目。 + +我的目标是,让加入小傅哥的伙伴,就等同于进入一个互联网大厂的项目组。在这里可以学习到非常有价值的编程架构经验,也能学习到丰富的应用产品业务知识。不少伙伴都感慨:“进入到公司才发现,小傅哥教的东西有多重要!” + +2025年,新增完成了,1个业务项目、2个AI项目(含1个进行中)、2个组件项目、2个自学项目、5套基础教程; + +### 1. 实战项目 + +- 2024-11-11《拼团交易平台系统》 - 24年末启动,25年中完成。这套项目业务流程非常细腻,可以让学习的伙伴,掌握细腻的业务知识、大厂的架构设计和解决方案,也可以学习到微服务&分布式的设计和实现,同时又有非常有价值的场景解决方案。 +- 2025-03-03《DeepSeek RAG、MCP、Ai Agent 智能体》 - 这几乎是全网最早启动的智能体项目,分3个阶段教学,直至完成一个可拖拉拽配置的智能体。这套内容是基于 Spring AI 框架实现,做 Java 业务项目的公司,也都是使用 Java 语言编写智能体,来为业务项目提效。而 Python 类则偏向底层或者数据以及量化多一些。 +- 2025-04-19《通用技术组件 - 🔧扳手工程》 - 这是一套互联网大厂里的通用解决方案,他们会把共性的诉求,凝练成通用的组件,让各个系统可以复用。像是拼团项目、Ai Agent 项目,都有使用这里的组件能力,让大家真实感受到一个互联网公司级的项目,是怎么做这样的事情的。 +- 2025-10-20《AI MCP Gateway 网关服务系统》 - 这是25年10月,新启动的一个 AI 类项目,也是互联网公司中必备的 AI 类项目。因为公司里需要把各类的业务接口,转换为 AI MCP 协议的服务能力,让 AI 客户端可以调用使用。而这个项目又是深入理解 json-rpc2 mcp 协议的一个项目,非常具有学习价值。 +- 2025-11-23《本地任务消息组件》- 在互联网公司中,所做的微服务项目,都要解决一致性(MQ、RPC、HTTP)问题,这里会设计一个通用的技术组件,来完成任务补充操作。 +- 2025-09-15《钓鱼佬-网页游戏项目》- 自学扩展思路类型项目,Java & Python 双版本代码 +- 2025-09-08《吉祥外卖系统 PLUS 版》 - 自学扩展思路类型项目,方便伙伴拓宽互联网场景视野,增强自身学习过外卖,补充外卖知识的项目。 + +### 2. 基础知识 + +- 2024-12-17《技术术语》- 陆续编写,防腐、对象(vo、po、dto...)、微服务、埋点、幂等... +- 2025-08-03《云服务器操作课程》- 购买配置、SSH连接使用、Docker 一键安装脚本、JDK、Maven、MySQL、Redis等,Docker 部署项目、ai ssh命令工具 +- 2025-08-24《产品文档》- 滴滴特惠、国家电网营销 +- 2025-07-28《菜鸟教程》- 小白基础教程,基础环境配置、Java基础、并发编程、线程池、JVM 调优、Spring、MyBatis、MySQL、Redis、技术架构、互联网场景、技术关键词、Dev-Ops 环境配置等。 +- 2025-01-01《编程路书》长期维护项目,今年更新内容,今年新增;oauth2、ddd、ollama、aigc、google adk、joycode、docker 一键安装脚本&部署项目教程、流量录制和回放、远程调试、draw.io + ai 提效等。 + +
+ +
+ +**怎么样,加入小傅哥的社群,学起来是不很爽。你得到的哪是一个项目,而是完整的小傅哥本人!** + +## 二、职场,以为和 + +>万物负阴而抱阳,冲气以为和。 + +生阴而向阳,冲气乃万物,万物是道之所念,意之所行的结果。万物又皆以循环往复,起落升降周而复始。但因我道心已改😂,所以也只是坦然的看待当前这个循环。 + +因为我在一个公司的时间足够长,所以我现在看待职场,看像是看待一个缩影的人生。从3-5年蓄力、到5-10年爆发、再到10-15年淬炼到下个赛道(高级技术架构、T转M),后面在逐步缓慢退场。就像这一年, 我有好几个饭搭子离职了,也有被动毕业的,还有一些是领导换成了员工,员工换成了领导。更有之前的伙伴,已经走到了更高的顶层M级别,光彩照人。 + +放到以前我也会很羡慕晋升到更高级别,甚至不在意钱,而是在意晋升。但现在是站在了循环之外,来看待这些事情。更关心的不是那些晋升,而是在意于把多的时间留给家人,留个自己,留给身体。整个职场生涯到了这个阶段的很多伙伴,应该算是在“控分”,做的不是特别突出,但也不落后。 + +**为啥会“控分”呢?** + +刚工作时我绝不会意识到这一点,但到了结婚生子,上有老,下有小的时候,就会感受到了。 + +举例,很多北京工作的伙伴,是没有北京户口的。周五晚上一堆拼车的,要回天津了,周一早上又突突的跑来。有时候又要调休办理这个流程,办理那个手续,那你的精力自然就会被分配出去,很难有自己时间,或者全都扑给工作。类似这样的工作的伙伴,可以说非常消耗自己的,直至控分越低,最后也就只能做其他的去了。 + +伙伴说:“北京社保我已缴满10年+了,但我好像也从没细致的看看北京,当我办完手续,想先在北京好好玩一周,再退租回家。” 哼,这死鬼,变现不少股票,也拿了顶格 n+1,当然满意离场了。 + +潮汕人说,工字不出头,在小也要做生意。不过,像是我们这种家里没有做生意的,可能也没有啥做生意的基因,所以只能一小小点的尝试,在一个长期的时间里进行验证,找到自己的【道】。 + +## 三、心态,求不毂 + +>人之所恶,唯孤、寡、不毂(gu 车轱辘,圆满),而王公以为称。 + +反着干,求圆,则求不圆(不毂)。以放下的心态拿起来,上班就是为了离职的,面试就是为了面不上的。把期待放到最低,我不要了,爱咋地咋地。 + +生活中那么多事,不可能每件事都做到尽善尽美,所有的事情都玩命投入,那么每一件事情都可能做的乱码起糟。所以,要有第一性原则,要有自己的主线任务,找到那些事务的本质,才能有更好的心态完成这件事。 + +就像很多985/211的伙伴找实习的时候,学校就近找了个小公司,干着一些打杂的事情,那么临近毕业写到简历就非常难看。 + +找实习的第一个点,是验证你的能力,可以够得上当前这家公司。那么第一性目标,就应该奔着你的学校和你的积累,匹配未来你要去的城市和公司级别来找实习。 + +而写简历,是通过你的实习经历(或导师实验室)、项目经验、技术积累、赛事活动等,举证你的能力项。这样才能让你再毕业的时候找到一份更好的工作。 + +而我做的事情也一样,很多事情都是很慢,很慢的推进,不图热点,也不去接广告。朝着不骄不矜,虚怀处下的心态做事,把时间和精力都放在构建整个技术体系上,逐步形成一个完整的闭环,这样它的价值也会在后续逐步的变大。 + +## 四、结果,益和损 + +>故物或损之而益,或益之而损。 + +放在当下可能有困惑或不甘,但拉长时间线看,未来的某一天你都可以释然般的讲出当年的事,而那些事为你涨的经验,又能驱动着你走到了更高的一个台阶。往往,我们就是在这些不确定性中获益,动心忍性,曾益其所不能。 + +上一个12年,租房被骗,合租不爽,入职时间拉长(毕业了也不给办正式入职,多实习了3个月),电脑进水,手机丢了,出差没钱租满屋子都是床的一张床... + +后来,所有的事情都让我有了新的想法和计划; +- 租房被骗,合租不爽;后来,再也不合租了,就自己住,有了更多的自己的时间,也能做一些自己事情。从13年,就开始折腾各种技术。 +- 入职时间拉长;后来,第二年以入职不满一年为理由不给涨薪,所以多方面储备,1年后离职进入互联网,才有了现在的生活方式。 +- 电脑进水,手机丢了;后来,花500元修了电脑,暂时用一个临时的小手机打电话用。接了第一份私活,转了5000块,立马买了新手机。 +- 出差没钱;后来,我会想,什么公司、什么工作、什么领导,饿肚子的时候,只有自己感受到。 + +所以,很多事情都是这样,没有什么一定的好或则一定的不好。这一个12年,也有很多杂七杂八的事情,它们也驱动着我做了新的规划,未来会逐步体现出来。 + +你呢,是不也有很多事情,让你有了新的计划和不错的结果。 + +## 五、分享;亦教之 + +>人之所教,我亦教之。 + +站在我自己来看,这些年一直秉承着;**沉淀、分享、成长,让自己和他人都能有所收获。** + +我看待我自己便是一个知识的桥梁或者媒介,通过的我工作储备,把这些内容转换成`文字`、`视频`、`代码`,让关注到我的伙伴,可以成体系的学习积累编程相关的经验。 + +一年一年来说,其实我都在构建这个业务场景和技术实践的体系结构,并紧跟技术热点,甚至要早于很多互联网公司的动向,把这些内容提前给大家。这也让很伙伴进入职场后可以受益:`“小傅哥,我们公司在做 DDD 重构,我可以主导很多东西”`、`“我们公司也开始了 MCP 的开发,还有 AI MCP 网关”`、`“真快,现在公司都在做 AI Agent 提效业务的了”`、`“小傅哥是真教东西呀,要不是学了这些,我进入公司肯定跟不上”`,等等。 + +
+ +
+ +可能多年以后,小伙伴会说一句:**“傅哥,对得起我!”** + +## 六、共勉:强梁者 + +>强梁者不得其死,吾将以为教父(始/本/规矩)。 + +强梁者,亦是强良者,过分的强横,亦或过分的善良,都不会得到太好的结果。所以,不惹事,不怕事,多积累自己,储备自己,让自己具备随时脱身的本事,亦或是能站住脚的能力,才是做人的始末规矩。 + +当在校生有丰富的技术积累就不害怕面试、当工作多年的你有很好的项目经验就不用担心跳槽、当你有了不错的资金储备就不担心职场pua,当你有了工作以外的提前规划和实施就不担心未来的发展。 + +斜杠、多面手,让自己有更多的可能,不让自己陷入任何一个环境里,作为梁者承重,或为良者受欺。打开思路,多关注事务的本质和运行规律,我们都可以过一个更舒服的生活。加油,每个少年们! + +**2025年,再见啦,2026年开启啦!祝每一个伙伴,每一年都有一个新的认知,来过好每一天的生活。** diff --git "a/docs/md/about/me/2025-01-05-2024\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223\357\274\201.md" "b/docs/md/about/me/2025-01-05-2024\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223\357\274\201.md" new file mode 100644 index 000000000..d45e24404 --- /dev/null +++ "b/docs/md/about/me/2025-01-05-2024\345\271\264\357\274\214\345\260\217\345\202\205\345\223\245\343\201\256\345\271\264\347\273\210\346\200\273\347\273\223\357\274\201.md" @@ -0,0 +1,79 @@ +--- +title: 2024年,小傅哥の年终总结! +lock: need +--- + +# 2024年,小傅哥の年终总结! + +作者:小傅哥 +博客:[https://bugstack.cn](https://bugstack.cn/) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +仿佛24年的元旦聚餐还在昨天,一觉酒醒就到了25年的元旦😂。要不总说,幸福不折腾的日子总让人感觉过的很快,还真是快。 + +同时我也满怀期待,临近过年的3周,有伙伴聚餐、有单位团建,喜庆氛围逐步拉满,之后快乐过年!**今年应该能放炮了🧨,庆祝下这一年做的大量上百万字的内容输出!** + +
+ +
+ +>这是我东北的老家,每每过年都会想起他。从13年毕业到现在,整个家里人就一起“北漂”了,也就没在东北过过年。看看2025年可以回去一趟。 + +## 疯狂输出 + +这一年的输出也是卷的飞起,包括;一套`大营销项目60节+`、一套`小型支付双架构MVC+DDD`、三套`技术组件项目`和一套`基础教程70节的《编程路书》`,同时配套的录制大量的视频教程。合计输出至少在200万字。—— 键盘膜都敲碎了!😱 + +- 编程路书,是一套为了帮助小白伙伴学习编程开发的实战案例指导手册。包括;脚手架、系统架构、开发环境、开发技术、常用类库、工程测试、质量监控、发布部署、应用网关,这样几个大块。这也是我看过很多网上都是碎片化的不成体系的教程,甚至都没法运行,所以构建了这样一套免费教程,帮助大家学习。 +- 业务项目,大营销是一套非常大型的实战业务项目,含有丰富的业务场景,全程视频手把手的教程,带着大家做技术成长。随着大营销的落地,又开启了小型支付项目,用 MVC 和 DDD 架构分别实现一遍,让小伙伴可以快速理解 DDD 并上手开发。 +- 技术组件,其实小傅哥深知,简历要想漂亮点,必须是有点轮子一样的技术组件。虽然公司不一定让你哐哐哐的造轮子,但是得有这样的技术,因为这是你技术深度的体现。 + +与此同时,为了让学习编程的伙伴可以成体系化的成长,所学内容有连贯性、进阶型,而不是东一榔头,西一棒子。所以小傅哥构建了一整套项目进阶路线。这套项目进阶路线,在25年会继续迭代。让大家一次加入就学到爽! + +
+ +
+ +>🧧关注公众号「bugstack虫洞栈」回复「星球」可以领取加入优惠券。 + +## 长期主义 + +`这一年胖东来火了,雷军的含金量也在不断提升。` + +但其实说白了,别骗人、别忽悠、做长期且有价值的事,长期坚持做,达到一定的规模体量了,就一定会有成绩。 + +而那些靠大量推广和包装起来的,总会在后面得到一堆堆流量的反噬。而长期坚持虽然看着慢,但完完整整的做下来一个个硬核项目的,总能在后面得到好的反馈。 + +如果有些伙伴想自己做点什么,只要在你喜欢的能长期做的方向上,一直做下去,积累大量的生产资料,最后也都可以做成。不用图快,有时候图快就会成韭菜。 + +工作了这么多年,看到一个个同事从最初入职到10年的发展,会感觉多少有点“命”在里面😂。有些一起的同期的伙伴,职位都已经到达了顶峰。而以前的他的领导,都成了他下面,下面,下面的部门职员。 + +但其实这个“命”也是个人长期在某些方向积累的结果,比如你持续积累的业务经验甚多,做的项目也都次次拿奖,又具备良好的表达能力。自然就会平步青云。而如果你在部门偏创新,反复的调整折腾,别说往上走,别被开除都算不错的。 + +当然,所有的这些,没有啥可能一直持续的平稳的,都可能在不确定性中发展。即使你工作很久在一个项目上,但这个项目也不是你的。随时都可能被拿走。如果你的被反复拿走,那么就要多积累自己。如果你的不被拿走,而且把别人的拿走给你,那你就可以好好做项目,慢慢项目多了,管的人也会增多。所以,你要知道自己处于的是什么路线,有没有“命”里的正财或偏财,之后选择性的发展。让其他分支路线,保持个60分及格就行了。 + +但命呢,也是过往信息、技能、知识、逻辑的积累所产生的行为判断。如果你做的哪些事向好,但又不符合自己的性格就是运。所以,一个人正向的积累越多,越能让自己有非常好的命。 + +## 卷和不卷 + +`500万-800万,买一套房,你会选择留在北京吗?🐂🐴` + +不会,为什么?因为小孩晚上不爱睡觉!啊?这两者有什么关系? + +我呢,是来自于东北的村里,小的时候总是天一黑,跟着大人看会电视📺,没等到7:00的新闻联播演完,就困的睡着了。第二天,又跟着大人,早早的起床。因为大人要下地干活了,晚起就没人管了。 + +但现在的小孩,能整整熬到10点、11点才睡觉,他们就像有很多精力一直没释放完一样。和自己小时候一对比,突然想到。是呀,自己小时候,开门出去就是一个非常大的院子,再跑出去就是跟着一帮的伙伴在野地里疯跑,晚上自然累的呼呼就睡。 + +而现在的小孩,基本一整天都在水泥楼房里,每次下个楼都要换一堆的衣服,出去跑一会又回来继续脱衣服。等在过几年,就开始上幼儿园、小学、中学,一直被卷到毕业再到进厂当牛马。虽然我们现在也做这牛马的工作,但好在累了还能回忆下小时候有个不错的童年。 + +所以,在整个时代机会那么大的时候,你都没有卷出结果,怎么就能觉得在培养一个小牛马,就能继续卷赢。当然,这只是我的想法,如果都是我的想法,想必不卷也会过的不好。所以一定要有人卷。 + +当下,我们都已经这么明白这个社会的运行规则了,要卷就先卷自己吧。**走暗路、更瘦天、进窄门** + +## 未来输出 + +25年,必然也会很忙的一年。因为我的计划是交付出一套全体系的应用级项目,让你在小傅哥这里学习就等于进入一个互联网大厂学习。 + +目前已有6个业务项目、7个组件项目交付完成,未来一年保守估计将会再交付2套业务,2套组件。不过虽然有这么大量的交付和高频的更新,但小傅哥的星球依旧是价格上最实惠的。仅仅用一个项目的价格,就可以学习全部项目! \ No newline at end of file diff --git "a/docs/md/about/me/2025-07-03-\345\230\216\345\230\216\345\274\272\357\274\214\345\230\216\345\230\216\345\223\222\345\255\246v2.0.md" "b/docs/md/about/me/2025-07-03-\345\230\216\345\230\216\345\274\272\357\274\214\345\230\216\345\230\216\345\223\222\345\255\246v2.0.md" new file mode 100644 index 000000000..a5d3a0223 --- /dev/null +++ "b/docs/md/about/me/2025-07-03-\345\230\216\345\230\216\345\274\272\357\274\214\345\230\216\345\230\216\345\223\222\345\255\246v2.0.md" @@ -0,0 +1,115 @@ +--- +title: 帮助大家学编程,嘎嘎大学 v2.0 上线! +lock: need +--- + +# 帮助大家学编程,嘎嘎大学 v2.0 上线! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +五年啦📢,从最初只有一个 `IM 仿微信项目` + `编写 Netty` + `字节码编程`文章小傅哥,终于靠一己之力构建出了完整的toc业务场景,实战项目训练社群。感谢这么多年一直陪伴着小傅哥的粉丝伙伴!体验地址:[https://gaga.plus](https://gaga.plus) + +
+ +
+ +**文末说说下个五年** + +**gaga.plus 嘎嘎强,嘎嘎哒(大)学,小傅哥的私有技术朋友圈 👬🏻** + +嘎嘎强,嘎嘎哒学,是帮助从事软件开发行业的伙伴,搭建起一条与实际公司场景项目最近的桥廊。在这里你不需要臆想出虚假的项目,也不需要全是 CRUD 的空洞编程。而是实实在在的来自于企业的真实业务流程,进行编程思维的锻炼和编码能力的提升。 + +嘎嘎哒学,是以一个互联网公司中的真实项目组,进行业务场景需求的讲解和项目的开发。再这样的一个项目组,有业务项目,有通用组件项目,有创新应用项目。还有项目的分析、设计、部署、上线、监控、压测,可以说你在小傅哥这里学习,就等于你在真实大厂的实际工作。 + +
+ +
+ +**注意,实习生不算,因为实习生进入工作大部分是做边角料的活。** 所以实习生,更要学习,否则还远不如在小傅哥技术社群学习积累的多。 + +## 一、嘎嘎哒学 + +**嘎嘎强,嘎嘎哒学!**我把这作为一个`在线的技术大学`进行维护。社群,以最实惠的价格,最丰富的项目,带着粉丝伙伴一起成长。并且提供全程从0到1的教程,让小白伙伴也能上手学习,互联网真实场景的实战项目。 + +地址:[https://gaga.plus/](https://gaga.plus/) + +
+ +
+ +🎓 欢迎来到技术社群,星球「码农会锁」!这里是你的 🏫 编程学习殿堂。体验真实业务场景项目,掌握大厂核心编程技术。 全程视频手把手,增强编程思维,锻炼编码能力 👨🏻‍💻 + +跟着小傅哥学习,你可以积累到非常丰富的实用性实战技能; + +- 【系统架构】微服务、DDD 领域驱动设计、SpringBoot Starter、IDEA Plugin +- 【设计模式】工厂、策略、模板、外观、组合、状态、代理、门面等 —— 这些设计模式都在项目中使用,解决实际场景问题。 +- 【框架技术】Spring、SpringBoot、SpringMVC、MyBatis、MySQL、Guava、JWT、Shiro、OKHTTP3、Schedule、Netty、字节码(ASM\Javassist\Byte-Buddy)、JavaFx +- 【分布式类】Dubbo、MQ(Kafka\RocketMQ)、Redis、XXL-Job、Zookeeper、Sharding-JDBC、DB-Router、Elasticsearch、Zookeeper、Canal、Otter、ELK、Hystrix、RateLimiter +- 【前端技术】React、Next.js、Typescript +- 【运维知识】Docker、Portainer、Git、Compose、运维脚本 +- 【创新技术】Spring AI、RAG、MCP、Agent +- 【通用组件】结合 Spring、Redis、MyBatis、AOP技术等,实现的通用技术组件 +- 【技术方案】营销、电商、外卖、支付、组件、重构,以及性能优化等各类场景的技术方案归档。 + +这些技术都是在各个项目中运用到的内容,也是中大互联网公司必备的技术技能,从面试招聘的要求中就能看到这些知识项要求熟练掌握。那么,这些内容也是小傅哥星球内项目能让大家学习掌握的技术。此外,还有一项隐藏技能就是绘画能力。 + +进入公司后,免不了要述职、分享、答辩,那么绘画就是一些非常重要的技能,加入星球就能看到各类项目中的高级绘图,学习这些能力。 + +## 二、项目举例 + +### 1. 业务项目 + +
+ +
+ +### 2. 组件项目 + +
+ +
+ +### 3. 其他项目 + +
+ +
+ +## 三、运维操作 + +其实学项目,就不只是写代码,还有很多的其他操作。就像公司里一样,写代码只是编程工作的一部分。因此,小傅哥也带着大家学习整个 DevOps 的各项技术。如,Docker + 软件,在云服务器的操作,提供好运维脚本。 + +
+ +
+ +### 1. 一键安装脚本 + +
+ +
+ +### 2. 云服务器教程 + +
+ +
+ +- 地址:[https://bugstack.cn/md/road-map/docker-install.html](https://bugstack.cn/md/road-map/docker-install.html) + +## 四、下个五年 + +地址:[https://github.com/fuzhengwei/CodeGuide/issues/111](https://github.com/fuzhengwei/CodeGuide/issues/111) + +让人怪不好意思的,上山⛰的路走了,巅峰的山也看了,后面也快下山了。**所以下个五年,感觉有点像离职倒计时。😂** + +在公司中我们每隔3年,可以申请一次换新的电脑,领取到手后,会有一个残值,不断的降低,最终到3年降低到0元,之后就可以换新的了。 + +有时候我们一起聊天的伙伴,也会感觉自己的残值在逐渐的降低。一方面是年级越来越大,部门的新人越来越多。另外一方面是自己的家庭,娃也都快要上小学,离开也是陆续的事。 + +那么,下个五年走完后。那时候,应该彻底的在做自己的事了。今年5年的利率 `1.6%`,五年后正好取定期存款,拿利息带着家人一起旅游!🚗 ⛰ diff --git "a/docs/md/about/study/2023-05-14-\345\215\247\351\276\231\343\200\201\345\207\244\351\233\217\357\274\201\344\270\244\346\272\220\347\240\201\345\255\246\345\276\227\344\270\200\357\274\214\344\273\243\347\240\201\350\264\250\351\207\217\351\203\275\344\270\215\344\274\232\345\267\256\357\274\201.md" "b/docs/md/about/study/2023-05-14-\345\215\247\351\276\231\343\200\201\345\207\244\351\233\217\357\274\201\344\270\244\346\272\220\347\240\201\345\255\246\345\276\227\344\270\200\357\274\214\344\273\243\347\240\201\350\264\250\351\207\217\351\203\275\344\270\215\344\274\232\345\267\256\357\274\201.md" index c33503bca..e6ebe0020 100644 --- "a/docs/md/about/study/2023-05-14-\345\215\247\351\276\231\343\200\201\345\207\244\351\233\217\357\274\201\344\270\244\346\272\220\347\240\201\345\255\246\345\276\227\344\270\200\357\274\214\344\273\243\347\240\201\350\264\250\351\207\217\351\203\275\344\270\215\344\274\232\345\267\256\357\274\201.md" +++ "b/docs/md/about/study/2023-05-14-\345\215\247\351\276\231\343\200\201\345\207\244\351\233\217\357\274\201\344\270\244\346\272\220\347\240\201\345\255\246\345\276\227\344\270\200\357\274\214\344\273\243\347\240\201\350\264\250\351\207\217\351\203\275\344\270\215\344\274\232\345\267\256\357\274\201.md" @@ -61,10 +61,7 @@ Spring 和 MyBatis 可以说是 Java 行业的卧龙凤雏,其他同类的框 除了这本技术图书,**小傅哥的知识星球里**,涵盖了;`业务项目(4)`、`组件项目(3)`、`技术小册(4)`、`手撕源码(2)`、以及`简历辅导`、`小白辅助`的各类课程内容。加入后建议学习路线如下; -
- -
知识星球:码农会锁 - 应用级实战项目学习社群
-
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) 1. **为了面试**:Java面经手册(八股)、Lottery 分布式抽奖(项目)、SpringBoot Starter 中间件设计和开发(拉伸技术)、手写Spring/手写Mybatis(2选1) 2. **夯实能力**:重学Java设计模式、Lottery 分布式抽奖、手写Spring、手写Mybatis、ChatGPT 应用项目 diff --git "a/docs/md/about/study/2023-06-04-\345\220\216\347\253\257\347\240\201\345\206\234\357\274\214\346\200\216\344\271\210\345\206\231\345\245\275\345\211\215\347\253\257\344\273\243\347\240\201\357\274\237.md" "b/docs/md/about/study/2023-06-04-\345\220\216\347\253\257\347\240\201\345\206\234\357\274\214\346\200\216\344\271\210\345\206\231\345\245\275\345\211\215\347\253\257\344\273\243\347\240\201\357\274\237.md" index aba2a7ac6..5efb3eb02 100644 --- "a/docs/md/about/study/2023-06-04-\345\220\216\347\253\257\347\240\201\345\206\234\357\274\214\346\200\216\344\271\210\345\206\231\345\245\275\345\211\215\347\253\257\344\273\243\347\240\201\357\274\237.md" +++ "b/docs/md/about/study/2023-06-04-\345\220\216\347\253\257\347\240\201\345\206\234\357\274\214\346\200\216\344\271\210\345\206\231\345\245\275\345\211\215\347\253\257\344\273\243\347\240\201\357\274\237.md" @@ -68,6 +68,4 @@ ChatGPT 是小傅哥新增的一个星球项目,做了 Dev-Ops、API、ChatGPT **扫码加入,一次加入就可以学习6个项目,死鬼,这么个价格可不多哦。** 项目有学习群、简历编写案例、面试问题汇总、读者作业分享,总之就是让大家实惠!—— 跟着架构师学习,学习的段位也会非高。这就像拜师学艺一样,小傅哥能给你写出的代码质量,绝对不是凑代码。 -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) diff --git "a/docs/md/about/study/2024-03-03-\345\210\2605\344\270\207\345\260\261\345\245\275\344\272\206.md" "b/docs/md/about/study/2024-03-03-\345\210\2605\344\270\207\345\260\261\345\245\275\344\272\206.md" new file mode 100644 index 000000000..84b10d13c --- /dev/null +++ "b/docs/md/about/study/2024-03-03-\345\210\2605\344\270\207\345\260\261\345\245\275\344\272\206.md" @@ -0,0 +1,156 @@ +--- +title: 到5万就好了! +lock: need +--- + +# 到5万就好了! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +🤔 有小伙伴问傅哥,什么时候写项目,才能有自己的思路。`可以读懂代码`、`可以调试bug`、`可以扩展功能`。现在比较慌,现在遇到个报错,只想扔给傅哥。`🎙️叠个大bug,再发一串报错,愿傅哥的周末不寂寞😂。 ` + +
+ +
+ +**答案是,不多,当你有`5万行,有效代码编写`的时候,你甚至可以横着走🦀。** + +以我为例,在整个技术学习积累阶段,新人小白时,是最容易犯错的。那些明明看来很简单的`环境配置`,`空指针异常`,`断点调试`,`数据库连接密码错误`等,都需要花费很长的时间来解决。当需要寻求帮助的时候,对错误的描述也不够准确。甚至也不会想到要告诉对方,自己是在什么场景、什么环境、执行了什么流程、发生的什么错误。 + +针对这样的情况,小傅哥给新人伙伴分享一些常见简单错误的处理手段和正确的把错误信息描述出来,同时再分享个从小白到入门进阶的学习路线。这个路线,也是将来冲击P7(50k)综合工资的路线。 + +>🧧文末有8个,互联网公司级实战项目,提高面试竞争力! + +## 1. 常见错误 + +
+ +
+ +在小傅哥的星球「码农会锁」中,有这么一个标签 **#Bug笔记** 专门记录一些常见的错误信息和处理手段。这些错误来自于伙伴们,在实战项目学习中,所遇到的一些常见问题的解答。对新人伙伴的帮助是非常大的。示例如下; + +### 1. java.lang.ClassFormatError + +
+ +
+ +**场景**:星球伙伴在运行API网关项目时,报错,java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module + +
+ +
+ +**原因**:Dubbo 通过反射 Api 访问 defineClass ,但是在 JDK 9 以上,JPMS 模块化,就不让访问了。 + +**方案**:可以通过添加配置「`--add-opens java.base/java.lang=ALL-UNNAMED`」临时解决,但可能会引起其他问题,所以切换为 JDK 1.8 比较稳妥。 + +
+ +
+ +### 2. Unable to find a @SpringBootConfiguration + +
+ +
+ +**场景**:星球伙伴在学习项目,搭建模块化module工程的后,运行测试代码,时长遇到; Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test + +**原因**:其中一个原因是没有注意到包的路径问题,因为默认情况下 Application 扫描 Bean 对象,需要保持一个上下层的关系。如;Application 在 cn.bugstack 下,那么其他对象就要在 cn.bugstack.xxx 下面。这样才能扫描。在 Spring 中讲究约定大于配置,如果不保持包的结构,就会引出很多其他的配置问题。与其额外增加配置,不如遵守开发约定。 + +### 3. Failed to configure a DataSource: 'url' attribute is not specified + +
+ +
+ +**场景**:星球伙伴在学习OpenAI项目中,自己对照工程搭建项目,启动运行 Application 后在控制台发现报错如下「这也是很多伙伴在搭建项目中常见的一个错误」【图1】; + +```java +Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. + +Reason: Failed to determine a suitable driver class + +Action: + +Consider the following: + If you want an embedded database (H2, HSQL or Derby), please put it on the classpath. + If you have database settings to be loaded from a particular profile you may need to activate it (the profiles dev are currently active). +``` + +**原因**:因为小伙伴在搭建项目的时候,【如图】引入了 MyBatis、MySQL 的数据库连接配置,那么 SpringBoot 在自动装配 DataSourceAutoConfiguration 的时候会找相关的 MyBatis 连接数据源配置。但此时 yml 中又没有提供相关的数据源配置,所以会报错「一种是自己学习忘记配置,另外一种是课程本节未使用到数据源,但自己先引入了 MyBatis 也没配置对应的连接信息」。 + +
+ +
+ +**处理**: +1. 早期项目如果没有使用到 MyBatis、MySQL,可以从工程中排出。pom 根目录可以做版本引入,但不在 app 中做具体的引入。这样项目就不会加载了。 +2. 通过 `@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})`排出掉数据源的自动装配【如图】。 + +
+ +
+ +### 4. java.lang.IllegalArgumentException: Result Maps collection does not contain + +
+ +
+ +**场景**:星球伙伴在做大营销项目的时候,编写第一个查询数据库接口,配置完 MyBatis 的时候,遇到这样一个报错; + +```java +java.lang.IllegalArgumentException: Result Maps collection does not contain value for org.example.infrastructure.persistent.dao.IStrategyDao.dataMap +``` + +**心里**:心里处于崩溃状态,怎么可能,代码已经编写了,这也没有错,为啥呢。【图1】 + +**原因**:如图2,因为字母写错了。这也是一个手敲代码很容易写错的。 + +**处理**:检查 MyBatis Mapper 中,所有写错 dataMap 的地方进行修改。 + +
+ +
+ +## 2. 学习路线 + +编程不是看的,经验不是背的。最好的学习方式是下手实践,一个个编程所需学习的技术,用案例锻炼,在结合案例到项目中实践。以项目完成为导向,用项目驱动学习,有就可以少走很多弯路了。 + +看到不少伙伴还在死扣 Servlet、JSP、Hibernate、Struts2,真的,这类项目别做了,这类技术也先放放吧,面试不问的,工作也不需要的。我按照互联网企业常用的技术,给大家梳理一套清晰的学习教程。 + +
+ +
+ +- **基础教程**,这部分也就是大家简历中所体现的`专业技能`部分。这些技能覆盖到以后,基本也就匹配上市面所有Java类的互联网公司了。 +- **实战项目**,不只是简历,也包括企业,项目主要分为这样4个类型。业务、技术、组件、插件,还有一类是源码。互联网公司中不少是根据源码做的二次封装,开发了一个符合企业所需的软件。在这条路线学习上,给纯小白伙伴准备了一个既有深度,又手把手带着做的**《大营销项目》**。在这个项目中,小傅哥把业务流程、架构设计、脚手架使用、设计模式、场景方案、工程测试、接口对接、工程打包、服务上线、简单压测等,全部都给体现出来了。有了这样一个积累,在去看其他的项目(也是从0到1的)会更加得心应手。之后在一个简历中,你可以用`业务项目 + 技术项目/组件项目`,来组合使用,这样可以让自己的竞争力更强一些。 +- **技术方案**,这是一种技术视野扩展的手段。小傅哥的内容体系是衔接着公司实践级别的,这样也就能让加入小傅哥星球的伙伴,具备学习到真实场景的设计技术。这些东西可以让你有更多内容的了解。 +- **面试相关**,职场、简历、招聘、八股,这是在我们有了核心技术和实战项目这个大基座上面的`生活`,我会用这么多年的实践能力,帮助你成长。无论你是处于哪个阶段。 + +## 3. 出成绩了! + +我从没想过做这些成体系的内容,只单纯的为了满足大家面试。面试只是职业生涯中一个很小的过程,而更重要的是想办法把大家的技术能力拉升到腰部以上,这也是任何一个行业的本质逻辑。腰部以上赚钱,腰部以下求温饱。 + +所以小傅哥的**星球「码农会锁」**,是小傅哥按照一个中大型互联网企业的标准,进行教程的开发。让一个小白沿着这条路线,可以从基础内容、理论内容、项目内容、架构内容,一步步成长起来。星球中现有的伙伴,在经过这些内容的学习,可以说是得到了非常大的进步。 + +
+ +
+ +- 来自于星球「码农会锁」伙伴们的学习记录。 +- 小傅哥本身也是互联网大厂的架构师,在工作中也有架构设计、业务开发、组件开发、培养新人、技术分享等工作。所以在小傅哥的星球你就相当于在小傅哥所在公司,接受架构师对你的培训,是一样一样滴。 + +## 4. 加入学习 + +跟着`小傅哥学习项目`可以获得的收获;我可以按照所在互联网大厂的标准,从`需求设定`、`框架搭建`、`领域设计`、`库表设计`、`功能开发`、`项目打包`、`发布部署`、`运维监控`、`简历编写`、`面试解析`,这样一整条线的内容,全部交给你!有了这样一套的组合,那你学习完找份工作还不是嘎嘎滴容易! + +> 这样的项目学习在小傅哥星球「码农会锁」有8个,每个都是从0到1开发并提供简历模板和面试题,并且还在继续开发,后续还将有更多!价格嘎嘎实惠,早点加入,早点提升自己。项目地址:[https://gaga.plus](https://gaga.plus) + diff --git a/docs/md/ai/a2a.md b/docs/md/ai/a2a.md new file mode 100644 index 000000000..8974a9fc5 --- /dev/null +++ b/docs/md/ai/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/ai/agent-skill.md b/docs/md/ai/agent-skill.md new file mode 100644 index 000000000..123e16dd1 --- /dev/null +++ b/docs/md/ai/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/ai/ai-ssh-opencode.md b/docs/md/ai/ai-ssh-opencode.md new file mode 100644 index 000000000..d7c22695a --- /dev/null +++ b/docs/md/ai/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/ai/draw.io.md b/docs/md/ai/draw.io.md new file mode 100644 index 000000000..4ffdac94a --- /dev/null +++ b/docs/md/ai/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/ai/github-models.md b/docs/md/ai/github-models.md new file mode 100644 index 000000000..08fbfcac4 --- /dev/null +++ b/docs/md/ai/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/ai/google-adk.md b/docs/md/ai/google-adk.md new file mode 100644 index 000000000..805cf6e95 --- /dev/null +++ b/docs/md/ai/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/ai/openclaw.md b/docs/md/ai/openclaw.md new file mode 100644 index 000000000..85fc8b974 --- /dev/null +++ b/docs/md/ai/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/ai/qclaw.md b/docs/md/ai/qclaw.md new file mode 100644 index 000000000..5d5bf04dd --- /dev/null +++ b/docs/md/ai/qclaw.md @@ -0,0 +1,242 @@ +--- +title: QClaw +lock: need +--- + +# 🦞站起来蹬,每天2亿token,我已经会用 OpenClaw 了! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +深度体验后,**云服务器 + OpenClaw 的工作属性,基本等于零,白费蜡🕯!** 所以,于3月初,购入 Mac Mini(丐版16G)。部署 `OpenClaw`(+`QClaw`)+ `编程环境`(`jdk`、`maven`...) + `Docker`,`配合 Nas 环境做存储` + `Mac Pro`(随行控制设备)+ UU 远程(没事看看它)等,我才体验到**它能给我干活啦**!😄 + +
+ +
+ +❤ 心得体会! + +最早部署的是 [OpenClaw](https://openclaw.ai/)(这哥们也原生就支持中文啦)+ 飞书,部署起来也很简单。但因为使用的都是自己的 Token,有点舍不得站起来蹬 🚴🏻,感觉每一句话都在烧钱💰。相比较下 OpenClaw 同样一个事的消耗程度,是其他 AI IDE(Trae.ai)的上百倍,那直接用其他的不也可以吗!?🤔 + +但不深度玩,不干它几亿 Token,就不能体验到花钱的快乐! + +好在呀,好在 [**QClaw**](https://qclaw.qq.com/) 每天一个登录的账号,赠送 4000 万 Token。换5个微信(还得家里人多),就是2亿 Token!就算干山崎大队,也是tm有富余的呀! + +
+ +
+ +几十亿 Token 下去后,我的体验是;它能干活,能想一个你的员工一样,控制这台电脑干活。包括,可以按照需求写代码,完成编译、构建、部署,打开浏览器验证功能逻辑,对于错误的编码可以继续完善。并且你可以随时随地的通过手机/Pad,通过`微信`/`企微`/`飞书`等方式,与它交流。 + +不过,它也不是那么省心的。三千六百颗手榴弹,给老李能干山崎大队,给咱也就能炸个鱼塘。所以,OpenClaw 你要想让它准确的给咱干活,就像写出来的代码,不仅它能看懂,我也得能认识呀。 + +那么,就需要上技能 Skills,这也是我在深度体验 OpenClaw 的最大感受,也是最先干的一个活。先拿10亿 Token,写2个 Skills 之后再说(先把🦵🏻腿接上)。—— 把你工作的方式,训练成技能,让 AI 懂你! + +
+ +
+ +> 接下来,小傅哥就分享下,用 OpenClaw(QClaw)做的一些事。 + +## 一、产品上线(skills) + +发布到了官网(clawhub):[https://clawhub.ai/u/fuzhengwei](https://clawhub.ai/u/fuzhengwei) + +
+ +
+ +[**xfg-zsxq-skills**](https://clawhub.ai/fuzhengwei/xfg-zsxq-skills) 一款星球社群运营服务的技能,便于所有加入社群的龙虾🦞主,可以让龙虾自主看帖、回帖,根据自己工作信息发帖/文章,以此方式龙虾形成一个自己的社群,吸收 OpenClaw 使用经验。这是一个自我成长的过程。 + +[**Xfg-ddd-skills**](https://clawhub.ai/fuzhengwei/xfg-ddd-skills) 一款 DDD 架构(六边形)指导编码的技能,因为在实际使用 AI 编码中,如果不加限制,他可能每次实现的功能逻辑,工程结构、分包方式、实现过程,都有非常大的差异。这会导致我们在拿到这份代码后,后续迭代也会非常吃力。所以,设计此技能,按照通用的 DDD 架构(六边形),限定 AI 编码方式。此技能初上线,已经用户体用。 + +## 二、安装技能 + +
+ +
+ +| 用途 | 地址 | +| -------- | --------------------------------------------- | +| 星球社群 | https://github.com/fuzhengwei/xfg-zsxq-skills | +| 编码架构 | https://github.com/fuzhengwei/xfg-ddd-skills | + +OpenClaw、QClaw、OpenCode等,你都可以直接把技能**连接**告诉它,它可以帮你直接安装。如果不能直接安装也可以下载安装包,本地到底即可。 + +## 三、运行效果(zsxq-skills) + +### 1. 主动发帖 + +
+ +
+ +- 当你部署 zsxq-skills 技能后开始时,他会询问你,要对哪个星球地址进行操作,以及对应的 cookie 信息。如果 cookie 过期了,它还会主动询问你进行替换。 +- 整体配置完成后,你就可以对星球进行发帖以及查看帖子等操作。不过不要弄的特别频繁! +- 像是在 OpenClaw(QClaw)还可以让添加上定时的任务处理。 + +### 2. 龙虾社群🦞 + +
+ +
+ +- 地址:[https://wx.zsxq.com/group/48885154455258](https://wx.zsxq.com/group/48885154455258) +- 说明:现在这个[《OpenClaw 养虾社区🦞》](https://wx.zsxq.com/group/48885154455258)就已经入住了很多小龙虾,欢迎👏🏻一起来玩下。 + +## 四、编程效果(ddd-skills) + +### 1. 技能说明 + +如果不对 ai 做编码结构和实现方式限定,ai 会每次都给你“惊喜”,但工程交付需要的是在确定的结构下,持续的迭代输出。所以,需要增加规范技能,限定 ai 编码输出方式的统一。 + +
+ +
+ +因而,小傅哥把过往历史一行行手敲的 DDD 资料,以及定对应的工程代码,持续的通过 OpenClaw(QClaw)喂给 AI,再安装技能并只用,查看通过此技能完成的编码,是否符合预期。经过仅2周的折腾,目前发布了 [xfg-ddd-skills v2.2.1](https://github.com/fuzhengwei/xfg-ddd-skills) 版本,可以满足 DDD 六边形架构设计和编码实现。 + +地址:[https://github.com/fuzhengwei/xfg-ddd-skills](https://github.com/fuzhengwei/xfg-ddd-skills) - 欢迎使用,也感谢给点个 Star 支持!更建议,帮忙一起维护迭代。 + +### 2. 技能设计 + +```java +xfg-ddd-skills/ +├── SKILL.md # 技能入口文件 +├── README.md # 本文件 +├── assets/ # 资源文件 +├── scripts/ # 脚本工具 +└── references/ # 参考文档 + ├── architecture.md # 六边形架构概述 + ├── entity.md # 实体设计规范 + ├── aggregate.md # 聚合根设计规范 + ├── value-object.md # 值对象设计规范 + ├── repository.md # 仓储模式规范 + ├── port-adapter.md # 端口与适配器规范 + ├── case-layer.md # 业务编排层规范 + ├── project-structure.md # Maven 多模块结构 + ├── naming.md # 命名规范 + └── docker-images.md # Docker 镜像配置 +``` + +- 当你需要以下场景时,使用本技能: + + - 设计或实现 DDD 架构的项目,可以直接帮你创建准确的、统一的、可用的工程架构 + - 需要六边形架构、端口与适配器模式 + - 创建 Entity(实体)、Aggregate(聚合根)、Value Object(值对象) + - 设计 Repository(仓储)模式 + - 业务编排层(Case Layer)设计 + - 触发层(Trigger Layer:HTTP/MQ/Job)设计 + - 构建富领域模型(Rich Domain Model) + +- 注意,此技能还在持续迭代中,陆续还会增强使用效果。 + +```java +┌─────────────────────────────────────────────────────────────┐ +│ 触发层 Trigger │ +│ (HTTP Controller / MQ Listener / Job) │ +└─────────────────────────┬───────────────────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ API 层 │ +│ (DTO / Request / Response) │ +└─────────────────────────┬───────────────────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 案例层 Case │ +│ (业务编排 / 流程串联 / 组合调用) │ +└─────────────────────────┬───────────────────────────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ 领域层 Domain │ +│ (Entity / Aggregate / VO / Domain Service) │ +└─────────────────────────┬───────────────────────────────────┘ + ▲ +┌─────────────────────────────────────────────────────────────┐ +│ 基础设施层 Infrastructure │ +│ (Repository Impl / Port Adapter / DAO / PO) │ +└─────────────────────────────────────────────────────────────┘ +``` + +- 此技能符合六边形架构设计标准,相关文档:[https://bugstack.cn/md/road-map/ddd-guide-03.html](https://bugstack.cn/md/road-map/ddd-guide-03.html) + +### 3. 场景应用 + +#### 3.1 表单开发 + +小傅哥这里选择的一个【表单】功能开发的场景,类似通讯 doc 里有一个表单的功能,设置表单后,让用户填写; + +由运营配置表单,填写相应的输入信息(字段),有文本、数字、日期、下拉选择和图片上传等类型。以及增强了表单数据场景运用,填表的表单可以直接关联到用途上,如数据做权限功能审核,或者数据做发券等功能。最后分享链接给用户填写提交,提交时候可以选择让用户是否鉴权(如github登录、星球登录、公众号登录)。 + +
+ +
+ +全程零参与代码实现,只提供必要的信息。对于开发后的功能,都是让 OpenClaw(QClaw)自己做浏览器访问,填写数据,验证功能。如果有问题,则进行优化功能的处理。 + +不过,做这类东西,还是需要懂场景,懂产品,懂架构,懂技术。否则,它真运行起来,也是会有不少问题的。 + +#### 3.2 产品演示 + +##### 3.2.1 编辑表单(关联数据场景) + +
+ +
+ +##### 3.2.2 表单数据(全局展示信息) + +
+ +
+ +##### 3.2.3 能力配置(数据场景配置) + +
+ +
+ +- 这个的目的,就在于配置完表单,用户填写后,那么这些数据,要干啥用。我们可以把一些字段关联起来,直接使用。 + +##### 3.2.4 填写记录(展示收集信息) + +
+ +
+ +##### 3.2.5 分享表单(用户填写数据) + +
+ +
+ +- 分享表单后,用户填写提交即可。 + +### 4. 工程演示 + +
+ +
+ +- 这个结构很重要,通过 xfg-ddd-skills 的限定,它可以按照约定的方式来编码。不要有ai幻觉和惊喜,而是要有小傅哥的“味道”。 +- 不过在整个设计实现的“灵魂”上,还是差了点,不够老辣! + +## 五、体验总结 + +1. 这种大量的工程化的,AI 写的代码,我不想碰。既不能给它修,也不想给它改。只要有 Token 就想让 AI 继续搞。软件工程交付,需要的不只是代码,还包括完整的理解代码,代码与产品PRD完全匹配,从而形成代码资产。 +2. 使用AI做项目,仍然需要懂技术的,你的能力多强,你驾驭AI的本事就多大。 +3. 纯新项目,或者小公司快速场景验证,可以使用AI快速搭建出来一套。 +4. 公司里几十万行的老项目,如果靠 AI 这样迭代编写,可能会出事故。审查要严格。程序员编码的过程,是一行行逻辑的确认和产品功能的完整匹配。 +5. 当一个需求过于复杂的时候,AI 会有过程中崩溃问题。这个对于个人编码项目没啥所谓,但公司级别的需求的迭代的。它的编码速度,有时候会低于人。n个类中,n行代码中,迭代功能,程序员已经有历史经验。ai 需要全量识别(或者你提前都编写好了技能) +6. skills 很有用,希望使用 ai 迭代项目的,需要持续的为工程迭代 skills 的各项技能。相当于为工程写技能路书。 +7. OpenClaw(QClaw)属于一种网关入口,来控制设备。如果你更熟练于编码,那么纯 AI IDE 会更适合。如果你往各类的服务,接管电脑,那么 OpenClaw 更为合适。 + +--- + +
+ +
\ No newline at end of file diff --git a/docs/md/ai/spring-ai.md b/docs/md/ai/spring-ai.md new file mode 100644 index 000000000..89d85372b --- /dev/null +++ b/docs/md/ai/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/ai/trae.md b/docs/md/ai/trae.md new file mode 100644 index 000000000..3294a349b --- /dev/null +++ b/docs/md/ai/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/algorithm/data-structures/2022-08-06-queue.md b/docs/md/algorithm/data-structures/2022-08-06-queue.md index c97ddc52c..a7a0122b9 100644 --- a/docs/md/algorithm/data-structures/2022-08-06-queue.md +++ b/docs/md/algorithm/data-structures/2022-08-06-queue.md @@ -95,12 +95,12 @@ private void siftUpComparable(int k, E x) { int parent = (k - 1) >>> 1; logger.info("【入队】寻找当前节点的父节点位置。k:{} parent:{}", k, parent); Object e = queue[parent]; - // 如果当前位置元素,大于父节点元素,则退出循环 + // 如果当前位置元素大于父节点元素,则退出循环 if (key.compareTo((E) e) >= 0) { logger.info("【入队】值比对,父节点:{} 目标节点:{}", JSON.toJSONString(e), JSON.toJSONString(key)); break; } - // 相反父节点位置大于当前位置元素,则进行替换 + // 相反当前位置元素小于父节点位置,则进行替换 logger.info("【入队】替换过程,父子节点位置替换,继续循环。父节点值:{} 存放到位置:{}", JSON.toJSONString(e), k); queue[k] = e; k = parent; @@ -183,7 +183,7 @@ private void siftDownComparable(int k, E x) { if (key.compareTo((E) c) <= 0) { break; } - // 目标值小于c值,位置替换,继续比较 + // 目标值大于c值,位置替换,继续比较 logger.info("【出队】替换过程,节点的值比对。上节点:{} 下节点:{} 位置替换", JSON.toJSONString(queue[k]), JSON.toJSONString(c)); queue[k] = c; k = child; @@ -305,4 +305,4 @@ public E poll() { - 简述延迟队列/优先队列的实现方式 - 二叉堆插入/弹出元素的过程 - 延迟队列的使用场景 -- 延迟队列为什么添加信号量 \ No newline at end of file +- 延迟队列为什么添加信号量 diff --git a/docs/md/algorithm/data-structures/2022-08-27-hash-table.md b/docs/md/algorithm/data-structures/2022-08-27-hash-table.md index 39656372d..49bbb8f6e 100644 --- a/docs/md/algorithm/data-structures/2022-08-27-hash-table.md +++ b/docs/md/algorithm/data-structures/2022-08-27-hash-table.md @@ -63,7 +63,7 @@ lock: need ### 1. 哈希碰撞 -**说明**:通过模拟简单 HashMap 实现,去掉拉链寻址等设计,验证元素哈新索引位置碰撞。 +**说明**:通过模拟简单 HashMap 实现,去掉拉链寻址等设计,验证元素哈希索引位置碰撞。 ```java public class HashMap01 implements Map { @@ -569,4 +569,4 @@ Process finished with exit code 0 - 为什么使用散列表 - 拉链寻址和开放寻址的区别 - 还有其他什么方式可以解决散列哈希索引冲突 -- 对应的Java源码中,对于哈希索引冲突提供了什么样的解决方案 \ No newline at end of file +- 对应的Java源码中,对于哈希索引冲突提供了什么样的解决方案 diff --git a/docs/md/algorithm/data-structures/2022-09-26-tree-avl.md b/docs/md/algorithm/data-structures/2022-09-26-tree-avl.md index 3fa2fb006..24437ac35 100644 --- a/docs/md/algorithm/data-structures/2022-09-26-tree-avl.md +++ b/docs/md/algorithm/data-structures/2022-09-26-tree-avl.md @@ -183,6 +183,8 @@ if (factor(node.left) >= 0) {
+- fix:中间图为6 + **代码实现** ```java diff --git a/docs/md/algorithm/data-structures/2022-10-01-tree-2-3.md b/docs/md/algorithm/data-structures/2022-10-01-tree-2-3.md index f81cc073e..344dc8bdc 100644 --- a/docs/md/algorithm/data-structures/2022-10-01-tree-2-3.md +++ b/docs/md/algorithm/data-structures/2022-10-01-tree-2-3.md @@ -39,9 +39,11 @@ lock: need
- 2-3 树的插入过程与 BST 树类似,会通过树的左右节点大小,找到自己的插入位置。 -- 一个节点可以右1-3个元素,但当元素个数为3时,则需要调衡。把三个节点的中间节点晋升上来,其余两个节点为子节点。 +- 一个节点可以有1-2个元素(注意:不是1-3个),但当元素个数为3时,则需要调衡。把三个节点的中间节点晋升上来,其余两个节点为子节点。 - 如果进行一次调衡后,上一层父节点达到3个元素,则需要2次调衡,来满足2-3树的规则。 +**注意**:2-3树的定义是每个节点可以有1-2个元素,当插入导致节点有3个元素时需要立即调衡。例如在插入节点9之前的树结构中,父节点6应该只有一个元素,其左子树为节点5。这样的结构符合2-3树的定义,也便于后续插入节点9时的调衡操作。 + **咋样**,是不看过这个图之后对于2-3树的实现已经有感觉了,想动手写写试试了? - 源码地址:[https://github.com/fuzhengwei/java-algorithms](https://github.com/fuzhengwei/java-algorithms) - 本章源码:[https://github.com/fuzhengwei/java-algorithms/tree/main/data-structures/src/main/java/tree](https://github.com/fuzhengwei/java-algorithms/tree/main/data-structures/src/main/java/tree) diff --git a/docs/md/algorithm/data-structures/2022-10-02-tree-red-black.md b/docs/md/algorithm/data-structures/2022-10-02-tree-red-black.md index e221ba11c..a69266ff5 100644 --- a/docs/md/algorithm/data-structures/2022-10-02-tree-red-black.md +++ b/docs/md/algorithm/data-structures/2022-10-02-tree-red-black.md @@ -1,9 +1,9 @@ --- -title: 红黑树 Red Back Tree +title: 红黑树 Red Black Tree lock: need --- -# 数据结构:红黑树 Red Back Tree +# 数据结构:红黑树 Red Black Tree 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) @@ -134,7 +134,7 @@ grandParent.color = Node.Color.RED; super.rotateLeft(grandParent); ``` -- 当你把红黑树对照理解成2-3树,如图中第1步骤下的左侧小图,新增的节点5倒置2-3树不平衡。 +- 当你把红黑树对照理解成2-3树,如图中第1步骤下的左侧小图,新增的节点5导致2-3树不平衡。 - 那么这个时候需要把2-3树中节点4提起来,而对应红黑树则需要先进行染色,待操作的节点4为黑色,两个孩子节点为红色。 - 最后是把节点3进行一次左旋操作,完成树的平衡。对应步骤3中的左侧小图是2-3树调衡后的结果。 @@ -176,7 +176,7 @@ grandParent.color = Node.Color.RED; super.rotateRight(grandParent); ``` -- 当你把红黑树对照理解成2-3树,如图中第1步骤下的右侧小图,新增的节点1倒置2-3树不平衡。 +- 当你把红黑树对照理解成2-3树,如图中第1步骤下的右侧小图,新增的节点1导致2-3树不平衡。 - 那么这个时候需要把2-3树中节点2提起来,而对应红黑树则需要先进行染色,待操作的节点2为黑色,两个孩子节点为红色。 - 最后是把节点2进行一次右旋操作,完成树的平衡。对应步骤3中的右侧小图是2-3树调衡后的结果。 diff --git a/docs/md/algorithm/logic/math/2022-11-05-fibonacci.md b/docs/md/algorithm/logic/math/2022-11-05-fibonacci.md index 2bc37e7d4..d5ebaa990 100644 --- a/docs/md/algorithm/logic/math/2022-11-05-fibonacci.md +++ b/docs/md/algorithm/logic/math/2022-11-05-fibonacci.md @@ -138,7 +138,7 @@ public double fibonacciClosedForm(long position) { 在数据库路由实现方面,通常我们都是使用整数模除法散列求模的方式进行元素的索引计算。那既然乘法散列效率高,斐波那契散列分散均匀,为什么不使用这样的方式处理数据库路由算法呢? -在检索的资料中并没有一个专门的文章来说明这一事项,这也倒置很多在学习过 HashMap、ThreadLocal 源码的研发人员尝试把这两种源码中的乘法散列算法搬到数据库路由算法中使用。在保证每次扩容数据库表都是2的次幂的情况下,并没有出现什么样的问题。那么对于这样情况下,是否隐藏着什么潜在的风险呢? +在检索的资料中并没有一个专门的文章来说明这一事项,这也导致很多在学习过 HashMap、ThreadLocal 源码的研发人员尝试把这两种源码中的乘法散列算法搬到数据库路由算法中使用。在保证每次扩容数据库表都是2的次幂的情况下,并没有出现什么样的问题。那么对于这样情况下,是否隐藏着什么潜在的风险呢? 那么为了证实斐波那契散列是否可以用在数据库路由散列算法中,我们可以尝试使用 **严格雪崩标准(SAC)** 进行验证测试。 diff --git a/docs/md/algorithm/model/autoglm-phone-agent.md b/docs/md/algorithm/model/autoglm-phone-agent.md new file mode 100644 index 000000000..c5e1dac01 --- /dev/null +++ b/docs/md/algorithm/model/autoglm-phone-agent.md @@ -0,0 +1,326 @@ +--- +title: AutoGLM Phone Agent +lock: need +--- + +# 手机 + agent,这是要掀桌子! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +`这 + agent`,`那 + agent`,都是赋能,辅助提效。但**手机 + agent**,要掀桌子呀,这是要改变现有手机和APP厂商入口的格局。就像你开了个超市,别在你家开了个【超市入口】! + +
+ +
+ +**智能体时代,谁也阻挡不住!** + +先是有豆包手机,之后GLM推出 AutoGLM-Phone-9B 模型,可以通知命令、API、语音等方式,以多模态形式,帮助用户完成手机任务。如,`打开xxxAPP,搜索xxx商品,完成下单,之后通知给xxx微信伙伴,在xxx时间,进行收货`。`也可以,告诉手机,定时定点完成xxxAPP的签到、领券,刷票抢票`。`还可以,为老年人对于手机的xxx业务复杂的操作,进行一些列的自动化完成处理`。 + +这将是下一代手机的使用体验,也是各大厂即将争夺的智能入口。接下来,小傅哥带着大家部署下 AutoGLM 模型,以及讲解如何配置使用和最终的效果。 + +目前 AutoGLM 还是面向研发使用的阶段,不是直接可以调用的 API,所以要自己部署。不过以后肯定会更加方便,也会附带的提供对应的产品。也有可能出新安卓/IOS+agent的手机系统。路已经开了,看谁跑的快吧! + +## 一、模型介绍 + +官网:[https://github.com/zai-org/Open-AutoGLM](https://github.com/zai-org/Open-AutoGLM) + +
+ +
+ +- Phone Agent 是一个基于 AutoGLM 构建的手机端智能助理框架,它能够以多模态方式理解手机屏幕内容,并通过自动化操作帮助用户完成任务。系统通过 ADB(Android Debug Bridge)来控制设备,以视觉语言模型进行屏幕感知,再结合智能规划能力生成并执行操作流程。用户只需用自然语言描述需求,如“打开小红书搜索美食”,Phone Agent 即可自动解析意图、理解当前界面、规划下一步动作并完成整个流程。系统还内置敏感操作确认机制,并支持在登录或验证码场景下进行人工接管。同时,它提供远程 ADB 调试能力,可通过 WiFi 或网络连接设备,实现灵活的远程控制与开发。 +- 其他资料:[https://mp.weixin.qq.com/s/wRp22dmRVF23ySEiATiWIQ](https://mp.weixin.qq.com/s/wRp22dmRVF23ySEiATiWIQ) + +## 二、安装教程 + +### 1. 环境要求 + +- Python 3.10+ - `如果你是手动配置环境,可以参考` [https://bugstack.cn/md/algorithm/model/2023-05-21-chatglm-6b.html](https://bugstack.cn/md/algorithm/model/2023-05-21-chatglm-6b.html) +- GPU 24G * 2 显卡(单卡24G部署,成功概率低)推荐具备 AI 能力的云服务器,可以减少很多基础环境的配置。如:[autodl.com](https://www.autodl.com) +- 安卓手机一台 + 数据线(不能只是充电功能的线),`设置-关于手机-版本号` 然后连续快速点击 10 次左右,直到弹出弹窗显示“开发者模式已启用”。启用开发者模式之后,会出现 `设置-开发者选项-USB 调试`,勾选启用。(如果手机不是这样的,可以百度搜下设置) +- AutoGLM-Phone-9B/AutoGLM-Phone-9B-Multilingual 模型镜像地址:[https://huggingface.co/zai-org/AutoGLM-Phone-9B/tree/main](https://huggingface.co/zai-org/AutoGLM-Phone-9B/tree/main) +- ADB (Android Debug Bridge) 桥接测试包 下载地址:[https://developer.android.com/tools/releases/platform-tools?hl=zh-cn](https://developer.android.com/tools/releases/platform-tools?hl=zh-cn) - `下载后配置路径 export PATH=${PATH}:~/Downloads/platform-tools` Windows 电脑参考第三方教程:[https://blog.csdn.net/x2584179909/article/details/108319973](https://blog.csdn.net/x2584179909/article/details/108319973) +- ADB Keyboard 安卓手机输入法,用于连接桥接测试包 下载地址:[https://github.com/senzhk/ADBKeyBoard/blob/master/ADBKeyboard.apk](https://github.com/senzhk/ADBKeyBoard/blob/master/ADBKeyboard.apk) - 安装后在手机`设置-输入法` 或者 `设置-键盘列表` 中启用 `ADB Keyboard` 才能生效 + +> 接下来介绍,各个环境配置以及验证使用。只想看效果的,可以翻看到最后(使用效果)。 + +### 2. 算力部署 + +目前具备 AI 算力的服务器(支持小时购买的,关机不收费); + +- [www.autodl.com](https://www.autodl.com/home) - `推荐 vGPUT-48GB,推荐 717机、708机` +- [www.ucloud.cn](https://passport.ucloud.cn/?cps_code=5704U7oL0BOGDYCoSmU0pj) +- [https://gcs-console.jdcloud.com/instance/create?region=cn-central-xy1](https://gcs-console.jdcloud.com/instance/create?region=cn-central-xy1) - `2*24GB` + +
+ +
+ +> 注意,选择2卡,单卡24G的,否则大概率会失败。 + +#### 2.1 创建实例(autodl) + +##### 2.1.1 创建选择 + +地址:[https://www.autodl.com/create](https://www.autodl.com/create) + +
+ +
+ +##### 2.1.2 创建完成 + +地址:[https://www.autodl.com/console/instance/list](https://www.autodl.com/console/instance/list) - `控制台` + +
+ +
+ +- 创建并开机后,稍等即可看到GPU服务运行。 +- 注意,蓝色字 JupyterLab 是你登录地址,可以进入控制台部署模型。 + +> 其他的 GPU 服务器也都类似,如果使用的纯白 GPU 服务器,需要自己安装各种环,可参考;境。[https://bugstack.cn/md/algorithm/model/2023-05-21-chatglm-6b.html](https://bugstack.cn/md/algorithm/model/2023-05-21-chatglm-6b.html) + +#### 2.2 模型部署 + +##### 2.2.1 进入终端 + +
+ +
+ +- 注意,进入后,要把服务和软件安装到 `/root/autodl-tmp` 下,否则系统盘安装满了,就不能运行了。 + +##### 2.2.2 拉取代码 + +
+ +
+ +- 进入到 `/root/autodl-tmp` 拉取项目工程代码 `git clone https://github.com/zai-org/Open-AutoGLM.git` + +##### 2.2.3 更新文件(requirements.txt) + +
+ +
+ +```java +Pillow>=12.0.0 +openai>=2.9.0 + +# For Model Deployment +# Linux 云服务器环境下可以正常安装这些包 +transformers>=4.30.0 +vllm>=0.12.0 + +# Optional: sglang (如果需要的话) +# sglang>=0.5.6.post1 + +# Optional: for development +pytest>=7.0.0 +pre-commit>=4.5.0 +black>=23.0.0 +mypy>=1.0.0 +``` + +- 这份文件需要更新下,默认是关闭的。 + +##### 2.2.4 安装依赖 + +
+ +
+ +```java +pip install -r requirements.txt +pip install -e . +``` + +- 设置镜像源;`pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple` +- `cd Open-AutoGLM` 以此执行安装脚本。这个过程需要一段时间。如果有失败,可以重复执行脚本。 + +##### 2.2.5 下载模型 + +下载模型是为了启动 `Open-AutoGLM`,如果不手动下载,默认执行脚本速度比较慢,同时可能把文件安装到系统盘中。 + +
+ +
+ +- 官网地址:[https://huggingface.co/zai-org/AutoGLM-Phone-9B/tree/main](https://huggingface.co/zai-org/AutoGLM-Phone-9B/tree/main) + +- 镜像地址:[https://modelscope.cn/models/ZhipuAI/AutoGLM-Phone-9B](https://modelscope.cn/models/ZhipuAI/AutoGLM-Phone-9B) - `【下载模型】里面有对应的使用说明` + +
+ +
+ +使用说明: + +- 在下载前,请先通过如下命令安装ModelScope `pip install modelscope` +- 指定路径,下载完整模型库 `modelscope download --model ZhipuAI/AutoGLM-Phone-9B --local_dir ./AutoGLM-Phone-9B` + +##### 2.2.6 模型部署 + +**脚本说明** - `固定大小的参数不能修改` + +```java +python3 -m vllm.entrypoints.openai.api_server \ + --served-model-name autoglm-phone-9b \ + --allowed-local-media-path / \ + --mm-encoder-tp-mode data \ + --mm_processor_cache_type shm \ + --mm_processor_kwargs "{\"max_pixels\":5000000}" \ + --max-model-len 25480 \ + --chat-template-content-format string \ + --limit-mm-per-prompt "{\"image\":10}" \ + --model /root/autodl-tmp/Open-AutoGLM/AutoGLM-Phone-9B \ + --port 6008 +``` + +
+ +
+ +- model 由 Open-AutoGLM 默认的 `zai-org/AutoGLM-Phone-9B` 从 `https://huggingface.co/zai-org/AutoGLM-Phone-9B/tree/main` 下载,修改为已经下载好的本地的路径地址。 +- port 修改为 6008 端口,因为 `autodl.com` 算力指定的自定义服务,对外暴漏的端口有 6008、6006 部署后,用 `https://uu835267-800d-24be97d2.westc.gpuhub.com:8443` 访问服务。 + +**执行脚本** + +
+ +
+ +##### 2.2.7 验证模型 + +
+ +
+ +```java +curl https://cb869967ef619cf1-8000.cn-south-1.gpu-instance.ppinfra.com/v1/chat/completions -H "Content-Type: application/json" -d '{ + "model": "autoglm-phone-9b", + "messages": [ + { + "role": "user", + "content": "打开抖音,连刷5个视频,给第4个视频点赞,第5个视频收藏" + } + ] +}' +``` + +- 这个API和 GPT 一样的格式,可以访问接口。 + +```java +# 交互模式 +python main.py --base-url https://cb869967ef619cf1-8000.cn-south-1.gpu-instance.ppinfra.com/v1 --model "autoglm-phone-9b" + +# 指定模型端点 +python main.py --base-url https://cb869967ef619cf1-8000.cn-south-1.gpu-instance.ppinfra.com/v1 "打开美团搜索附近的火锅店" + +# 使用英文 system prompt +python main.py --lang en --base-url https://cb869967ef619cf1-8000.cn-south-1.gpu-instance.ppinfra.com/v1 "Open Chrome browser" + +# 列出支持的应用 +python main.py --list-apps +``` + +- 根据你部署的模型, 设置 --base-url 和 --model 参数,之后本地就可以验证模型了。 + +### 3. ADB (Android Debug Bridge) - 电脑安装 + +#### 3.1 安装环境 + +1. 下载官方 ADB [安装包](https://developer.android.com/tools/releases/platform-tools?hl=zh-cn),并解压到自定义路径,地址:[https://developer.android.com/tools/releases/platform-tools?hl=zh-cn](https://developer.android.com/tools/releases/platform-tools?hl=zh-cn) +2. 配置环境变量 + +- MacOS 配置方法:在 `Terminal` 或者任何命令行工具里 + +``` +# 假设解压后的目录为 ~/Downlaods/platform-tools。如果不是请自行调整命令。 +export PATH=${PATH}:~/Downloads/platform-tools +``` + +- Windows 配置方法:可参考 [第三方教程](https://blog.csdn.net/x2584179909/article/details/108319973) 进行配置。 + +#### 3.2 验证脚本 + +```java +(base) fuzhengwei@ZBMac-GV47H1GXD Open-AutoGLM % adb devices +List of devices attached +94343646 device + +(base) fuzhengwei@ZBMac-GV47H1GXD Open-AutoGLM % +``` + +- 此时你的电脑USB,链接了手机,会显示出设备ID(如果没显示,检查下数据线等) + +### 4. Android 7.0+ 的设备或模拟器 + +#### 4.1 开启调试模式 + +1. 开发者模式启用:通常启用方法是,找到 `设置-关于手机-版本号` 然后连续快速点击 10 次左右,直到弹出弹窗显示“开发者模式已启用”。不同手机会有些许差别,如果找不到,可以上网搜索一下教程。 + +2. USB 调试启用:启用开发者模式之后,会出现 `设置-开发者选项-USB 调试`,勾选启用 + +3. 部分机型在设置开发者选项以后, 可能需要重启设备才能生效. 可以测试一下: 将手机用USB数据线连接到电脑后, `adb devices` 查看是否有设备信息, 如果没有说明连接失败. + +#### 4.2 安装 ADB Keyboard(用于文本输入) + +下载 [安装包](https://github.com/senzhk/ADBKeyBoard/blob/master/ADBKeyboard.apk) 并在对应的安卓设备中进行安装。 注意,安装完成后还需要到 `设置-输入法` 或者 `设置-键盘列表` 中启用 `ADB Keyboard` 才能生效 + +
+ +
+ +安装包地址:[https://github.com/senzhk/ADBKeyBoard/blob/master/ADBKeyboard.apk](https://github.com/senzhk/ADBKeyBoard/blob/master/ADBKeyboard.apk) - `可以下载好传到手机也可以` + +## 三、测试验证 + +### 1. 使用说明 + +
+ +
+ +- 整个的过程为,你通过命令调用LLM Phone,之后模型返回的结果,通过 ADB 方式,调用`调试模式`的安卓机,完成各项 APP 应用的操作。 + +### 2. 支持应用 + +
+ +
+ +### 3. 使用效果 + +
+ +
+ +```java +python main.py --device-id 94343646 --base-url https://uu835267-800d-0124cb32.westc.gpuhub.com:8443/v1 --model "autoglm-phone-9b" "打开抖音,刷视频" +``` + +- `device-id 94343646` 就是 adb 列出来的设备ID,`base-url 是你的服务地址`,之后可以自行验证,测试各种 APP 的启动,使用等。 +- 像是一些没有的预设的应用以及应用里的流程,他还会截图屏幕,自动分析和使用。也可以是打开其他APP,并执行一些列的流程操作。 + +## 四、其他资料 + +📢 **接下来**,在phone + agent 这个方向,将有越来越多的模型和产品。检索:[https://github.com/search?q=phone%20agent&type=repositories](https://github.com/search?q=phone%20agent&type=repositories) + +
+ +
+ +- https://github.com/mobile-next/mobile-mcp +- https://device-farm.com/doc/ +- https://droidrun.ai/ +- https://github.com/CherryHQ/cherry-studio-app +- https://github.com/minitap-ai/mobile-use \ No newline at end of file diff --git "a/docs/md/assembly/api-gateway/2023-06-10-API \347\275\221\345\205\263 - \345\252\262\347\276\216\347\276\216\345\233\242\350\277\231\345\245\227Shepherd\347\275\221\345\205\263\346\236\266\346\236\204\357\274\201.md" "b/docs/md/assembly/api-gateway/2023-06-10-API \347\275\221\345\205\263 - \345\252\262\347\276\216\347\276\216\345\233\242\350\277\231\345\245\227Shepherd\347\275\221\345\205\263\346\236\266\346\236\204\357\274\201.md" index 1443e1cda..608451821 100644 --- "a/docs/md/assembly/api-gateway/2023-06-10-API \347\275\221\345\205\263 - \345\252\262\347\276\216\347\276\216\345\233\242\350\277\231\345\245\227Shepherd\347\275\221\345\205\263\346\236\266\346\236\204\357\274\201.md" +++ "b/docs/md/assembly/api-gateway/2023-06-10-API \347\275\221\345\205\263 - \345\252\262\347\276\216\347\276\216\345\233\242\350\277\231\345\245\227Shepherd\347\275\221\345\205\263\346\236\266\346\236\204\357\274\201.md" @@ -244,8 +244,6 @@ public class MapperProxyFactory { -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) **所以**,这么多成体系的项目,你加入小傅哥的知识星球,**相当于加入了一个小型的技术公司**。你需要的各类项目,这里都可以学习到的。`技术的`、`业务的`、`组件的`,`还包括运维实施的`,而每一个项目都是以企业级标准进行设计和实现,学习这样的项目才是真正的奔着能工作学习的! diff --git a/docs/md/assembly/api-gateway/api-gateway.md b/docs/md/assembly/api-gateway/api-gateway.md index cc298050b..a31b644eb 100644 --- a/docs/md/assembly/api-gateway/api-gateway.md +++ b/docs/md/assembly/api-gateway/api-gateway.md @@ -84,11 +84,9 @@ API网关除了基础的功能模块以外,还需要重点考虑负载均衡 ### 1. 加入学习 -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) -- **加入**:微信扫码加入即可,加入后点击阅读原文直达`API网关项目学习`。或进入链接:[https://t.zsxq.com/0ciYdgP5u](https://t.zsxq.com/0ciYdgP5u) +- **加入**:加入后点击阅读原文直达`API网关项目学习`。或进入链接:[https://t.zsxq.com/0ciYdgP5u](https://t.zsxq.com/0ciYdgP5u) - **说明**:加入小傅哥的知识星球【`码农会锁`】,得到的不只是一个API网关项目,还有`Lottery - 分布式抽奖系统`、`IM - 仿微信项目`、`手撕源码`、`技术小册`等内容。 - **价值**:星球内的服务和实战项目都是小傅哥本人提供和原创,相信能够给大家带来超过该价格的价值 。举个例子,手把手带大家做进大厂才可能看得见的项目、有笔记有源码、有问题可以提,这比单独买一个课程或一套源码要值得多。其实都不到大城市一节补习班的钱,哪怕把我的课程时长换算成培训机构的课时,也是便宜的超级多。 diff --git "a/docs/md/assembly/middleware/2019-12-07-\345\217\221\345\270\203Jar\345\214\205\345\210\260Maven\344\270\255\345\244\256\344\273\223\345\272\223\357\274\214\344\270\272\345\274\200\345\217\221\345\274\200\346\272\220\344\270\255\351\227\264\344\273\266\345\201\232\345\207\206\345\244\207.md" "b/docs/md/assembly/middleware/2019-12-07-\345\217\221\345\270\203Jar\345\214\205\345\210\260Maven\344\270\255\345\244\256\344\273\223\345\272\223\357\274\214\344\270\272\345\274\200\345\217\221\345\274\200\346\272\220\344\270\255\351\227\264\344\273\266\345\201\232\345\207\206\345\244\207.md" index f0aea15c1..b11591800 100644 --- "a/docs/md/assembly/middleware/2019-12-07-\345\217\221\345\270\203Jar\345\214\205\345\210\260Maven\344\270\255\345\244\256\344\273\223\345\272\223\357\274\214\344\270\272\345\274\200\345\217\221\345\274\200\346\272\220\344\270\255\351\227\264\344\273\266\345\201\232\345\207\206\345\244\207.md" +++ "b/docs/md/assembly/middleware/2019-12-07-\345\217\221\345\270\203Jar\345\214\205\345\210\260Maven\344\270\255\345\244\256\344\273\223\345\272\223\357\274\214\344\270\272\345\274\200\345\217\221\345\274\200\346\272\220\344\270\255\351\227\264\344\273\266\345\201\232\345\207\206\345\244\207.md" @@ -23,7 +23,7 @@ lock: need | | 内容 | 备注 | |:--------:|:-------------|:-------------| | 1 | 申请Github帐号: https://github.com |用于上传开源代码:https://github.com/fuzhengwei/schedule-spring-boot-starter | -| 2 | GPG生成密钥工具: https://gpg4win.org/download.html | 在后续流程中下载安装生成密钥并上传置服务器,本文使用的服务器是:hkp://keyserver.ubuntu.com:80 | +| 2 | GPG生成密钥工具: Windows:https://gpg4win.org/download.html Mac:https://gpgtools.org/ | 在后续流程中下载安装生成密钥并上传置服务器,本文使用的服务器是:hkp://keyserver.ubuntu.com:80 | | 3 | 工单系统: https://issues.sonatype.org | 负责申请上传资格及完成第一次上传,后续更新不需要使用,相当于一个启动装置 | | 4 | 构件仓库: https://oss.sonatype.org | 上传的jar包会先存放到这里,在这里进行 Release 后即可发布到maven中央仓库,也可以本地设置自动发布 | | 5 | 镜像仓库: http://search.maven.org | 最终成功发布的jar可以在这里搜到 | @@ -34,6 +34,7 @@ lock: need ## 跟着节奏 ### 1. 下载安装Gpg生成密钥 + 我们需要一个GPG环境,用来对上传的文件进行加密和签名,保证你的jar包不被篡改 >1991年,程序员Phil Zimmermann为了避开政府监视,开发了加密软件PGP。这个软件非常好用,迅速流传开来,成了许多程序员的必备工具。但是,它是商业软件,不能自由使用。所以,自由软件基金会决定,开发一个PGP的替代品,取名为GnuPG。这就是GPG的由来。 @@ -218,6 +219,8 @@ lock: need -Xdoclint:none + + /Library/Java/JavaVirtualMachines/jdk1.8.0_311.jdk/Contents/Home/bin/javadoc @@ -277,3 +280,10 @@ lock: need - 整体流程还是很长的,如果第一次尝试去弄,嗯,你可能有一个不眠夜 - 中间可能会遇到各种异常错误,包括密钥、打包、发版等等,注意仔细阅读本文细节以及自己多次尝试,总归是会成功的 - 比较常识性的问题;同一个RELEASE版本只能上传一次否则会失败、老外真的是半夜回复比较快因为咱们半夜他们正好天亮了 + +--- + +23年12月3日,看到2个不错的同类教程。如果有推送失败的,也可以参考; + +- [【maven】手把手教你如何把自己的Jar上传到maven中央仓库](https://blog.csdn.net/lovexiaotaozi/article/details/121989407) +- [【拥抱开源】发布自己的项目到maven中央仓库](https://blog.csdn.net/csdnerM/article/details/128610930) \ No newline at end of file diff --git "a/docs/md/assembly/middleware/2021-03-31-\343\200\212SpringBoot \344\270\255\351\227\264\344\273\266\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\343\200\213\344\270\223\346\240\217\345\260\217\345\206\214\344\270\212\347\272\277\345\225\246\357\274\201.md" "b/docs/md/assembly/middleware/2021-03-31-\343\200\212SpringBoot \344\270\255\351\227\264\344\273\266\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\343\200\213\344\270\223\346\240\217\345\260\217\345\206\214\344\270\212\347\272\277\345\225\246\357\274\201.md" index a0cedffc8..5b6294295 100755 --- "a/docs/md/assembly/middleware/2021-03-31-\343\200\212SpringBoot \344\270\255\351\227\264\344\273\266\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\343\200\213\344\270\223\346\240\217\345\260\217\345\206\214\344\270\212\347\272\277\345\225\246\357\274\201.md" +++ "b/docs/md/assembly/middleware/2021-03-31-\343\200\212SpringBoot \344\270\255\351\227\264\344\273\266\350\256\276\350\256\241\345\222\214\345\274\200\345\217\221\343\200\213\344\270\223\346\240\217\345\260\217\345\206\214\344\270\212\347\272\277\345\225\246\357\274\201.md" @@ -52,7 +52,7 @@ lock: need - **技术框架**:包括 Spring、SpringBoot 配置加载、自定义注解、扫描注册Bean等,以及 ORM 框架设计原理和实现。这部分技术主要是把开发的中间件与框架结合,开发相应的组件或者包装为各类 SpringBoot Starter 的能力学习。 - **数据服务**:Mysql、Redis、Elasticsearch,都是数据服务,通常需要开发各类组件对数据服务的使用进行封装,Mysql 我们知道有 JDBC,Redis 我们知道有 Jedis,但 Elasticsearch 有 x-pack 你是否了解。 -- **数据组件**:这类组件的开发就是为了简化对数据服务的使用,Mysql+JDBC+ORM,可以非常方便的使用数据库服务,那么 Elasticsearch 是否也可以做相应的组件研发,让它的查询也能像使用 MyBatis 一样呢?二折页的技术能力就需要对 MyBatis 等 ORM 框架的实现原理熟悉,同时需要了解 JDBC 的概念。 +- **数据组件**:这类组件的开发就是为了简化对数据服务的使用,Mysql+JDBC+ORM,可以非常方便的使用数据库服务,那么 Elasticsearch 是否也可以做相应的组件研发,让它的查询也能像使用 MyBatis 一样呢?而这样的技术能力就需要对 MyBatis 等 ORM 框架的实现原理熟悉,同时需要了解 JDBC 的概念。 - **分布式技术**:RPC 框架、注册中心、分布式任务,都是现有互联网分布式架构中非常重要的技术,而对于如何实现一个 RPC 框架,也技术是研发人员要掌握的重点,同时如何使用注册中心、怎么下发分布式调度任务,等等,这些技术的学习能让对现有的框架使用有更深入的认识。 - **服务治理**:熔断、降级、限流、切量、黑白名单以及对现有方法的非入侵式扩展增强等,都可以成为是服务治理类组件,原本这类技术在早期是与业务逻辑代码融合的,后来逐步被拆解出来,开发成对应的组件。所以我们可以学习到,关于这类组件的包装、集成是如何做的。 - **字节码&插件**:在互联网的系统应用运维过程中,你一定会接触到各类的监控系统,而很多监控系统是非入侵的全链路监控,那么这些是如何实现的呢?其实它们是基于字节码插桩,对系统方法的增强,采集相应的运行时信息,进行监控的。再到扩展 JVMTI、IDEA 插件开发,都是为了整个研发过程的可持续交付和上线提高交付质量和降低人效的。 diff --git "a/docs/md/develop/design-pattern/2020-05-20-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\267\245\345\216\202\346\226\271\346\263\225\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-05-20-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\267\245\345\216\202\346\226\271\346\263\225\346\250\241\345\274\217\343\200\213.md" index d23445e38..35817e157 100755 --- "a/docs/md/develop/design-pattern/2020-05-20-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\267\245\345\216\202\346\226\271\346\263\225\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-05-20-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\267\245\345\216\202\346\226\271\346\263\225\346\250\241\345\274\217\343\200\213.md" @@ -67,7 +67,7 @@ lock: need 为了可以让整个学习的案例更加贴近实际开发,这里模拟互联网中在营销场景下的业务。由于营销场景的复杂、多变、临时的特性,它所需要的设计需要更加深入,否则会经常面临各种紧急CRUD操作,从而让代码结构混乱不堪,难以维护。 -在营销场景中经常会有某个用户做了一些操作;打卡、分享、留言、邀请注册等等,进行返利积分,最后通过积分在兑换商品,从而促活和拉新。 +在营销场景中经常会有某个用户做了一些操作;打卡、分享、留言、邀请注册等等,进行返利积分,最后通过积分再兑换商品,从而促活和拉新。 那么在这里我们模拟积分兑换中的发放多种类型商品,假如现在我们有如下三种类型的商品接口; @@ -251,7 +251,7 @@ public void test_awardToUser() { Process finished with exit code 0 ``` -- 运行结果正常,满足当前所有业务产品需求,写的还很快。但!实在难以为维护! +- 运行结果正常,满足当前所有业务产品需求,写的还很快。但!实在难以维护! ## 六、工厂模式优化代码 @@ -381,7 +381,7 @@ public class CardCommodityService implements ICommodity { - 从上面可以看到每一种奖品的实现都包括在自己的类中,新增、修改或者删除都不会影响其他奖品功能的测试,降低回归测试的可能。 - 后续在新增的奖品只需要按照此结构进行填充即可,非常易于维护和扩展。 -- 在统一了入参以及出参后,调用方不在需要关心奖品发放的内部逻辑,按照统一的方式即可处理。 +- 在统一了入参以及出参后,调用方不再需要关心奖品发放的内部逻辑,按照统一的方式即可处理。 #### 2.3 创建商店工厂 @@ -400,7 +400,7 @@ public class StoreFactory { ``` - 这里我们定义了一个商店的工厂类,在里面按照类型实现各种商品的服务。可以非常干净整洁的处理你的代码,后续新增的商品在这里扩展即可。如果你不喜欢`if`判断,也可以使用`switch`或者`map`配置结构,会让代码更加干净。 -- 另外很多代码检查软件和编码要求,不喜欢if语句后面不写扩展,这里是为了更加干净的向你体现逻辑。在实际的业务编码中可以添加括号。 +- 另外很多代码检查软件和编码要求,不喜欢if语句后面不写括号,这里是为了更加干净的向你体现逻辑。在实际的业务编码中可以添加括号。 ### 3. 测试验证 diff --git "a/docs/md/develop/design-pattern/2020-05-24-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-05-24-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217\343\200\213.md" index ca1ec828a..3b015817a 100755 --- "a/docs/md/develop/design-pattern/2020-05-24-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-05-24-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217\343\200\213.md" @@ -29,7 +29,7 @@ lock: need `学习设计模式的心得有哪些,怎么学才会用!` -设计模式书籍,有点像考驾驶证的科一、家里装修时的手册、或者单身狗的恋爱宝典。但!你只要不实操,一定能搞的**乱`码`七糟**。因为这些指导思想都是从实际经验中提炼的,没有经过提炼的小白,很难驾驭这样的知识。所以在学习的过程中首先要有案例,之后再结合案例与自己实际的业务,尝试重构改造,慢慢体会其中的感受,从而也就学会了如果搭建出优秀的代码。 +设计模式书籍,有点像考驾驶证的科一、家里装修时的手册、或者单身狗的恋爱宝典。但!你只要不实操,一定能搞的**乱`码`七糟**。因为这些指导思想都是从实际经验中提炼的,没有经过提炼的小白,很难驾驭这样的知识。所以在学习的过程中首先要有案例,之后再结合案例与自己实际的业务,尝试重构改造,慢慢体会其中的感受,从而也就学会了如何搭建出优秀的代码。 ## 二、开发环境 @@ -106,7 +106,7 @@ itstack-demo-design-2-00 ![Redis单机服务](https://bugstack.cn/assets/images/2020/itstack-demo-design-2-04.png) - 模拟Redis功能,也就是假定目前所有的系统都在使用的服务 -- 类和方法名次都固定写死到各个业务系统中,改动略微麻烦 +- 类和方法名称都固定写死到各个业务系统中,改动略微麻烦 #### 2.2 模拟集群 EGM @@ -174,9 +174,9 @@ public class CacheServiceImpl implements CacheService { ## 五、用一坨坨代码实现 -`讲道理没有ifelse解决不了的逻辑,不行就在加一行!` +`讲道理没有ifelse解决不了的逻辑,不行就再加一行!` -此时的实现方式并不会修改类结构图,也就是与上面给出的类层级关系一致。通过在接口中添加类型字段区分当前使用的是哪个集群,来作为使用的判断。可以说目前的方式非常难用,其他使用方改动颇多,这里只是做为例子。 +此时的实现方式并不会修改类结构图,也就是与上面给出的类层级关系一致。通过在接口中添加类型字段区分当前使用的是哪个集群,来作为使用的判断。可以说目前的方式非常难用,其他使用方改动颇多,这里只是作为例子。 ### 1. 工程结构 @@ -398,7 +398,7 @@ public static T getProxy(Class interfaceClass, ICacheAdapter cacheAdapter } ``` -- 这里主要的作用就是完成代理类,同时对于使用哪个集群有外部通过入参进行传递。 +- 这里主要的作用就是完成代理类,同时对于使用哪个集群由外部通过入参进行传递。 **JDKInvocationHandler** @@ -464,4 +464,4 @@ Process finished with exit code 0 - 抽象工厂模式,所要解决的问题就是在一个产品族,存在多个不同类型的产品(Redis集群、操作系统)情况下,接口选择的问题。而这种场景在业务开发中也是非常多见的,只不过可能有时候没有将它们抽象化出来。 - `你的代码只是被ifelse埋上了!`当你知道什么场景下何时可以被抽象工程优化代码,那么你的代码层级结构以及满足业务需求上,都可以得到很好的完成功能实现并提升扩展性和优雅度。 -- 那么这个设计模式满足了;单一职责、开闭原则、解耦等优点,但如果说随着业务的不断拓展,可能会造成类实现上的复杂度。但也可以说算不上缺点,因为可以随着其他设计方式的引入和代理类以及自动生成加载的方式降低此项缺点。 \ No newline at end of file +- 那么这个设计模式满足了;单一职责、开闭原则、解耦等优点,但如果说随着业务的不断拓展,可能会造成类实现上的复杂度。但也可以说算不上缺点,因为可以随着其他设计方式的引入和代理类以及自动生成加载的方式降低此项缺点。 diff --git "a/docs/md/develop/design-pattern/2020-05-26-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\273\272\351\200\240\350\200\205\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-05-26-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\273\272\351\200\240\350\200\205\346\250\241\345\274\217\343\200\213.md" index 1a234e652..03701a569 100755 --- "a/docs/md/develop/design-pattern/2020-05-26-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\273\272\351\200\240\350\200\205\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-05-26-\351\207\215\345\255\246Java\350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\273\272\351\200\240\350\200\205\346\250\241\345\274\217\343\200\213.md" @@ -359,9 +359,9 @@ public class MarcoPoloTile implements Matter { ## 五、用一坨坨代码实现 -`讲道理没有ifelse解决不了的逻辑,不行就在加一行!` +`讲道理没有ifelse解决不了的逻辑,不行就再加一行!` -每一个章节中我们都会使用这样很直白的方式去把功能实现出来,在通过设计模式去优化完善。这样的代码结构也都是非常简单的,没有复杂的类关系结构,都是直来直去的代码。除了我们经常强调的这样的代码不能很好的扩展外,做一些例子demo工程还是可以的。 +每一个章节中我们都会使用这样很直白的方式去把功能实现出来,再通过设计模式去优化完善。这样的代码结构也都是非常简单的,没有复杂的类关系结构,都是直来直去的代码。除了我们经常强调的这样的代码不能很好的扩展外,做一些例子demo工程还是可以的。 ### 1. 工程结构 @@ -637,7 +637,7 @@ public class DecorationPackageMenu implements IMenu { } ``` -- 装修包的实现中每一个方法都会了 `this`,也就可以非常方便的用于连续填充各项物料。 +- 装修包的实现中每一个方法都返回了 `this`,也就可以非常方便的用于连续填充各项物料。 - 同时在填充时也会根据物料计算平米数下的报价,吊顶和涂料按照平米数适量乘以常数计算。 - 最后同样提供了统一的获取装修清单的明细方法。 @@ -728,7 +728,7 @@ public void test_Builder(){ Process finished with exit code 0 ``` -- 测试结果是一样的,调用方式也基本类似。但是目前的代码结构却可以让你很方便的很有调理的进行扩展业务开发。而不是像以往一样把所有代码都写到`ifelse`里面。 +- 测试结果是一样的,调用方式也基本类似。但是目前的代码结构却可以让你很方便的很有条理的进行扩展业务开发。而不是像以往一样把所有代码都写到`ifelse`里面。 ## 七、总结 diff --git "a/docs/md/develop/design-pattern/2020-05-28-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\216\237\345\236\213\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-05-28-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\216\237\345\236\213\346\250\241\345\274\217\343\200\213.md" index d04efb4d6..7b9fe24ea 100755 --- "a/docs/md/develop/design-pattern/2020-05-28-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\216\237\345\236\213\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-05-28-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\216\237\345\236\213\346\250\241\345\274\217\343\200\213.md" @@ -23,9 +23,9 @@ lock: need 程序员这份工作里有两种人;一类是热爱喜欢的、一类是仅当成工作的。而喜欢代码编程的这部分人会极其主动学习去丰富自己的羽翼,也非常喜欢对技术探索力求将学到的知识赋能到平时的业务需求开发中。对于这部分小伙伴来说上班写代码还能赚钱真的是幸福! -`怎么成为喜欢编码都那部分人` +`怎么成为喜欢编码的那部分人` -无论做哪行那业你都喜欢,往往来自从中持续不断都获取成就感。就开发编程而言因为你的一行代码影响到了千千万万的人、因为你的一行代码整个系统更加稳定、因为你的一行代码扛过了所有秒杀等等,这样一行行的代码都是你日积月累学习到的经验。那如果你也想成为这样有成就感的程序员就需要不断的学习,不断的用更多的技能知识把自己编写的代码运用到更核心的系统。 +无论做哪行哪业你都喜欢,往往来自从中持续不断地获取成就感。就开发编程而言因为你的一行代码影响到了千千万万的人、因为你的一行代码整个系统更加稳定、因为你的一行代码扛过了所有秒杀等等,这样一行行的代码都是你日积月累学习到的经验。那如果你也想成为这样有成就感的程序员就需要不断的学习,不断的用更多的技能知识把自己编写的代码运用到更核心的系统。 `方向不对努力白费` @@ -837,5 +837,5 @@ Process finished with exit code 0 - 以上的实际场景模拟了原型模式在开发中重构的作用,但是原型模式的使用频率确实不是很高。如果有一些特殊场景需要使用到,也可以按照此设计模式进行优化。 - 另外原型设计模式的优点包括;便于通过克隆方式创建复杂对象、也可以避免重复做初始化操作、不需要与类中所属的其他类耦合等。但也有一些缺点如果对象中包括了循环引用的克隆,以及类中深度使用对象的克隆,都会使此模式变得异常麻烦。 -- 终究设计模式是一整套的思想,在不同的场景合理的运用可以提升整体的架构的质量。永远不要想着去硬凑设计模式,否则将会引起过渡设计,以及在承接业务反复变化的需求时造成浪费的开发和维护成本。 -- 初期是代码的优化,中期是设计模式的使用,后期是把控全局服务的搭建。不断的加强自己对全局能力的把控,也加深自己对细节的处理。可上可下才是一个程序员最佳处理方式,选取做合适的才是最好的选择。 \ No newline at end of file +- 终究设计模式是一整套的思想,在不同的场景合理的运用可以提升整体的架构的质量。永远不要想着去硬凑设计模式,否则将会引起过度设计,以及在承接业务反复变化的需求时造成浪费的开发和维护成本。 +- 初期是代码的优化,中期是设计模式的使用,后期是把控全局服务的搭建。不断的加强自己对全局能力的把控,也加深自己对细节的处理。可上可下才是一个程序员最佳处理方式,选取做合适的才是最好的选择。 diff --git "a/docs/md/develop/design-pattern/2020-05-31-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\215\225\344\276\213\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-05-31-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\215\225\344\276\213\346\250\241\345\274\217\343\200\213.md" index 0a1953379..17eb5136c 100644 --- "a/docs/md/develop/design-pattern/2020-05-31-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\215\225\344\276\213\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-05-31-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\215\225\344\276\213\346\250\241\345\274\217\343\200\213.md" @@ -75,8 +75,8 @@ public class Singleton_00 { } ``` -- 以上这种方式在我们平常的业务开发中非常常见,这样静态类的方式可以在第一次运行的时候直接初始化Map类,同时这里我们也不需要到延迟加载在使用。 -- 在不需要维持任何状态下,仅仅用于全局访问,这个使用使用静态类的方式更加方便。 +- 以上这种方式在我们平常的业务开发中非常常见,这样静态类的方式可以在第一次运行的时候直接初始化Map类,同时这里我们也不需要到延迟加载再使用。 +- 在不需要维持任何状态下,仅仅用于全局访问,这个使用静态类的方式更加方便。 - 但如果需要被继承以及需要维持一些特定状态的情况下,就适合使用单例模式。 ### 1. 懒汉模式(线程不安全) @@ -162,7 +162,7 @@ public class Singleton_04 { } ``` -- 使用类的静态内部类实现的单例模式,既保证了线程安全有保证了懒加载,同时不会因为加锁的方式耗费性能。 +- 使用类的静态内部类实现的单例模式,既保证了线程安全又保证了懒加载,同时不会因为加锁的方式耗费性能。 - 这主要是因为JVM虚拟机可以保证多线程并发访问的正确性,也就是一个类的构造方法在多线程环境下可以被正确的加载。 - 此种方式也是非常推荐使用的一种单例模式 @@ -250,7 +250,7 @@ public void test() { Singleton_07.INSTANCE.test(); ``` -*这种写法在功能上与共有域方法相近,但是它更简洁,无偿地提供了串行化机制,绝对防止对此实例化,即使是在面对复杂的串行化或者反射攻击的时候。虽然这中方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。* +*这种写法在功能上与共有域方法相近,但是它更简洁,无偿地提供了串行化机制,绝对防止对此实例化,即使是在面对复杂的串行化或者反射攻击的时候。虽然这种方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。* 但也要知道此种方式在存在继承场景下是不可用的。 diff --git "a/docs/md/develop/design-pattern/2020-06-02-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-02-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217\343\200\213.md" index 39dea1783..3d3a47fe5 100644 --- "a/docs/md/develop/design-pattern/2020-06-02-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-02-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\351\200\202\351\205\215\345\231\250\346\250\241\345\274\217\343\200\213.md" @@ -27,7 +27,7 @@ lock: need `框架中用到了设计模式吗?` -框架中不仅用到设计模式还用了很多,而且有些时候根本不是一个模式的单独使用,而是多种设计模式的综合运用。与大部分小伙伴平时开发的CRUD可就不一样了,如果都是if语句从上到下,也就算得不上什么框架了。就像你到Spring的源码中搜关键字`Adapter`,就会出现很多实现类,例如;`UserCredentialsDataSourceAdapter`。而这种设计模式就是我们本文要介绍的适配器模式。 +框架中不仅用到设计模式还用了很多,而且有些时候根本不是一个模式的单独使用,而是多种设计模式的综合运用。与大部分小伙伴平时开发的CRUD可就不一样了,如果都是if语句从上到下,也就算得不上什么框架了。就像你到Spring的源码中搜关键字`Adapter`,就会出现很多实现类,例如 `UserCredentialsDataSourceAdapter`。而这种设计模式就是我们本文要介绍的适配器模式。 `适配器在生活里随处可见` @@ -65,7 +65,7 @@ lock: need 随着公司的业务的不断发展,当基础的系统逐步成型以后。业务运营就需要开始做用户的拉新和促活,从而保障`DAU`的增速以及最终`ROI`转换。 -而这时候就会需要做一些营销系统,大部分常见的都是裂变、拉客,例如;你邀请一个用户开户、或者邀请一个用户下单,那么平台就会给你返利,多邀多得。同时随着拉新的量越来越多开始设置每月下单都会给首单奖励,等等,各种营销场景。 +而这时候就会需要做一些营销系统,大部分常见的都是裂变、拉客,例如:你邀请一个用户开户、或者邀请一个用户下单,那么平台就会给你返利,多邀多得。同时随着拉新的量越来越多开始设置每月下单都会给首单奖励,等等,各种营销场景。 那么这个时候做这样一个系统就会接收各种各样的MQ消息或者接口,如果一个个的去开发,就会耗费很大的成本,同时对于后期的拓展也有一定的难度。此时就会希望有一个系统可以配置一下就把外部的MQ接入进行,这些MQ就像上面提到的可能是一些注册开户消息、商品下单消息等等。 @@ -234,9 +234,9 @@ itstack-demo-design-6-02 ├── impl │ ├── InsideOrderService.java │ └── POPOrderAdapterServiceImpl.java - ├── MQAdapter,java - ├── OrderAdapterService,java - └── RebateInfo,java + ├── MQAdapter.java + ├── OrderAdapterService.java + └── RebateInfo.java ``` @@ -244,7 +244,7 @@ itstack-demo-design-6-02 ![适配器模型结构](https://bugstack.cn/assets/images/2020/itstack-demo-design-6-04.png) -- 这里包括了两个类型的适配;接口适配、MQ适配。之所以不只是模拟接口适配,因为很多时候大家都很常见了,所以把适配的思想换一下到MQ消息体上,增加大家多设计模式的认知。 +- 这里包括了两个类型的适配:接口适配、MQ适配。之所以不只是模拟接口适配,因为很多时候大家都很常见了,所以把适配的思想换一下到MQ消息体上,增加大家多设计模式的认知。 - **先是做MQ适配**,接收各种各样的MQ消息。当业务发展的很快,需要对下单用户首单才给奖励,在这样的场景下再增加对**接口的适配**操作。 ### 2. 代码实现(MQ消息适配) @@ -287,7 +287,7 @@ public class MQAdapter { } ``` -- 这个类里的方法非常重要,主要用于把不同类型MQ种的各种属性,映射成我们需要的属性并返回。就像一个属性中有`用户ID;uId`,映射到我们需要的;`userId`,做统一处理。 +- 这个类里的方法非常重要,主要用于把不同类型MQ种的各种属性,映射成我们需要的属性并返回。就像一个属性中有`用户ID;uId`,映射到我们需要的 `userId`,做统一处理。 - 而在这个处理过程中需要把映射管理传递给`Map link`,也就是准确的描述了,当前MQ中某个属性名称,映射为我们的某个属性名称。 - 最终因为我们接收到的`mq`消息基本都是`json`格式,可以转换为MAP结构。最后使用反射调用的方式给我们的类型赋值。 @@ -333,7 +333,7 @@ public void test_MQAdapter() throws NoSuchMethodException, IllegalAccessExceptio ``` - 在这里我们分别模拟传入了两个不同的MQ消息,并设置字段的映射关系。 -- 等真的业务场景开发中,就可以配这种映射配置关系交给配置文件或者数据库后台配置,减少编码。 +- 等真的业务场景开发中,就可以把这种映射配置关系交给配置文件或者数据库后台配置,减少编码。 ##### 2.3.2 测试结果 @@ -356,7 +356,7 @@ Process finished with exit code 0 因为增加了只有首单用户才给奖励,也就是你一年或者新人或者一个月的第一单才给你奖励,而不是你之前每一次下单都给奖励。 -那么就需要对此种方式进行限制,而此时MQ中并没有判断首单的属性。只能通过接口进行查询,而拿到的接口如下; +那么就需要对此种方式进行限制,而此时MQ中并没有判断首单的属性。只能通过接口进行查询,而拿到的接口如下: | 接口 | 描述 | | ------------------------------------------------------------ | ------------------------- | diff --git "a/docs/md/develop/design-pattern/2020-06-04-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\346\241\245\346\216\245\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-04-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\346\241\245\346\216\245\346\250\241\345\274\217\343\200\213.md" index c3d943be6..1a9d09d43 100755 --- "a/docs/md/develop/design-pattern/2020-06-04-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\346\241\245\346\216\245\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-04-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\346\241\245\346\216\245\346\250\241\345\274\217\343\200\213.md" @@ -25,7 +25,7 @@ lock: need `设计模式是可以帮助你改善代码` -很多时候你写出来的`ifelse`都是没有考虑使用设计模式优化,就像;同类服务的不同接口适配包装、同类物料不同组合的建造、多种奖品组合的营销工厂等等。它们都可以让你代码中原本使用`if`判断的地方,变成一组组类和面向对象的实现过程。 +很多时候你写出来的`ifelse`都是没有考虑使用设计模式优化,就像:同类服务的不同接口适配包装、同类物料不同组合的建造、多种奖品组合的营销工厂等等。它们都可以让你代码中原本使用`if`判断的地方,变成一组组类和面向对象的实现过程。 `怎么把设计模式和实际开发结合起来` @@ -56,7 +56,7 @@ JDBC多种驱动程序的实现、同品牌类型的台式机和笔记本平板 ## 四、案例场景模拟 -![场景模拟;多种支付和模式](https://bugstack.cn/assets/images/2020/itstack-demo-design-7-02.png) +![场景模拟:多种支付和模式](https://bugstack.cn/assets/images/2020/itstack-demo-design-7-02.png) 随着市场的竞争在支付服务行业出现了微信和支付宝还包括一些其他支付服务,但是对于商家来说并不希望改变用户习惯。就像如果我的地摊只能使用微信或者只能使用支付宝付款,那么就会让我顾客伤心,鸡蛋灌饼也卖不动了。 @@ -121,7 +121,7 @@ public class PayController { } ``` -- 上面的类提供了一个支付服务功能,通过提供的必要字段;`用户ID`、`交易ID`、`金额`、`渠道`、`模式`,来控制支付方式。 +- 上面的类提供了一个支付服务功能,通过提供的必要字段:`用户ID`、`交易ID`、`金额`、`渠道`、`模式`,来控制支付方式。 - 以上的`ifelse`应该是最差的一种写法,即使写`ifelse`也是可以优化的方式去写的。 ### 3. 测试验证 @@ -132,26 +132,26 @@ public class PayController { @Test public void test_pay() { PayController pay = new PayController(); - System.out.println("\r\n模拟测试场景;微信支付、人脸方式。"); + System.out.println("\r\n模拟测试场景:微信支付、人脸方式。"); pay.doPay("weixin_1092033111", "100000109893", new BigDecimal(100), 1, 2); - System.out.println("\r\n模拟测试场景;支付宝支付、指纹方式。"); - pay.doPay("jlu19dlxo111","100000109894",new BigDecimal(100), 2, 3); + System.out.println("\r\n模拟测试场景:支付宝支付、指纹方式。"); + pay.doPay("jlu19dlxo111", "100000109894", new BigDecimal(100), 2, 3); } ``` -- 以上分别测试了两种不同的支付类型和支付模式;微信人脸支付、支付宝指纹支付 +- 以上分别测试了两种不同的支付类型和支付模式:微信人脸支付、支付宝指纹支付 #### 3.2 测试结果 ```java -模拟测试场景;微信支付、人脸方式。 +模拟测试场景:微信支付、人脸方式。 23:05:59.152 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟微信渠道支付划账开始。uId:weixin_1092033111 tradeId:100000109893 amount:100 23:05:59.155 [main] INFO o.i.demo.design.pay.mode.PayCypher - 人脸支付,风控校验脸部识别 23:05:59.155 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟微信渠道支付风控校验。uId:weixin_1092033111 tradeId:100000109893 security:true 23:05:59.155 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟微信渠道支付划账成功。uId:weixin_1092033111 tradeId:100000109893 amount:100 -模拟测试场景;支付宝支付、指纹方式。 +模拟测试场景:支付宝支付、指纹方式。 23:05:59.156 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟支付宝渠道支付划账开始。uId:jlu19dlxo111 tradeId:100000109894 amount:100 23:05:59.156 [main] INFO o.i.demo.design.pay.mode.PayCypher - 指纹支付,风控校验指纹信息 23:05:59.156 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟支付宝渠道支付风控校验。uId:jlu19dlxo111 tradeId:100000109894 security:true @@ -197,8 +197,8 @@ itstack-demo-design-7-02 ![桥接模式模型结构](https://bugstack.cn/assets/images/2020/itstack-demo-design-7-03.png) -- 左侧`Pay`是一个抽象类,往下是它的两个支付类型实现;微信支付、支付宝支付。 -- 右侧`IPayMode`是一个接口,往下是它的两个支付模型;刷脸支付、指纹支付。 +- 左侧`Pay`是一个抽象类,往下是它的两个支付类型实现:微信支付、支付宝支付。 +- 右侧`IPayMode`是一个接口,往下是它的两个支付模型:刷脸支付、指纹支付。 - 那么,`支付类型` × `支付模型` = 就可以得到相应的组合。 - **注意**,每种支付方式的不同,刷脸和指纹校验逻辑也有差异,可以使用适配器模式进行处理,这里不是本文重点不做介绍,可以看适配器模式章节。 @@ -222,7 +222,7 @@ public abstract class Pay { } ``` -- 在这个类中定义了支付方式的需要实现的划账接口:`transfer`,以及桥接接口;`IPayMode`,并在构造函数中用户方自行选择支付方式。 +- 在这个类中定义了支付方式的需要实现的划账接口:`transfer`,以及桥接接口:`IPayMode`,并在构造函数中用户方自行选择支付方式。 - 如果没有接触过此类实现,可以重点关注 `IPayMode payMode`,这部分是桥接的核心。 #### 2.2 两个支付类型的实现 @@ -275,7 +275,7 @@ public class ZfbPay extends Pay { } ``` -- 这里分别模拟了调用第三方的两个支付渠道;微信、支付宝,当然作为支付综合平台可能不只是接了这两个渠道,还会有其很跟多渠道。 +- 这里分别模拟了调用第三方的两个支付渠道:微信、支付宝,当然作为支付综合平台可能不只是接了这两个渠道,还会有其很跟多渠道。 - 另外可以看到在支付的时候分别都调用了风控的接口进行验证,也就是不同模式的支付(`刷脸`、`指纹`),都需要过指定的风控,才能保证支付安全。 #### 2.3 定义支付模式接口 @@ -288,7 +288,7 @@ public interface IPayMode { } ``` -- 任何一个支付模式;刷脸、指纹、密码,都会过不同程度的安全风控,这里定义一个安全校验接口。 +- 任何一个支付模式:刷脸、指纹、密码,都会过不同程度的安全风控,这里定义一个安全校验接口。 #### 2.4 三种支付模式风控(刷脸、指纹、密码) @@ -346,30 +346,30 @@ public class PayCypher implements IPayMode{ ```java @Test public void test_pay() { - System.out.println("\r\n模拟测试场景;微信支付、人脸方式。"); + System.out.println("\r\n模拟测试场景:微信支付、人脸方式。"); Pay wxPay = new WxPay(new PayFaceMode()); wxPay.transfer("weixin_1092033111", "100000109893", new BigDecimal(100)); - System.out.println("\r\n模拟测试场景;支付宝支付、指纹方式。"); + System.out.println("\r\n模拟测试场景:支付宝支付、指纹方式。"); Pay zfbPay = new ZfbPay(new PayFingerprintMode()); zfbPay.transfer("jlu19dlxo111","100000109894",new BigDecimal(100)); } ``` -- 与上面的ifelse实现方式相比,这里的调用方式变得整洁、干净、易使用;`new WxPay(new PayFaceMode())`、`new ZfbPay(new PayFingerprintMode())` +- 与上面的ifelse实现方式相比,这里的调用方式变得整洁、干净、易使用:`new WxPay(new PayFaceMode())`、`new ZfbPay(new PayFingerprintMode())` - 外部的使用接口的用户不需要关心具体的实现,只按需选择使用即可。 -- 目前以上优化主要针对桥接模式的使用进行重构`if`逻辑部分,关于调用部分可以使用`抽象工厂`或`策略模式`配合map结构,将服务配置化。因为这里主要展示`桥接模式`,所以就不在额外多加代码,避免喧宾夺主。 +- 目前以上优化主要针对桥接模式的使用进行重构`if`逻辑部分,关于调用部分可以使用`抽象工厂`或`策略模式`配合map结构,将服务配置化。因为这里主要展示`桥接模式`,所以就不再额外多加代码,避免喧宾夺主。 #### 3.2 测试结果 ```java -模拟测试场景;微信支付、人脸方式。 +模拟测试场景:微信支付、人脸方式。 23:14:40.911 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟微信渠道支付划账开始。uId:weixin_1092033111 tradeId:100000109893 amount:100 23:14:40.914 [main] INFO o.i.demo.design.pay.mode.PayCypher - 人脸支付,风控校验脸部识别 23:14:40.914 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟微信渠道支付风控校验。uId:weixin_1092033111 tradeId:100000109893 security:true 23:14:40.915 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟微信渠道支付划账成功。uId:weixin_1092033111 tradeId:100000109893 amount:100 -模拟测试场景;支付宝支付、指纹方式。 +模拟测试场景:支付宝支付、指纹方式。 23:14:40.915 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟支付宝渠道支付划账开始。uId:jlu19dlxo111 tradeId:100000109894 amount:100 23:14:40.915 [main] INFO o.i.demo.design.pay.mode.PayCypher - 指纹支付,风控校验指纹信息 23:14:40.915 [main] INFO o.i.demo.design.pay.channel.Pay - 模拟支付宝渠道支付风控校验。uId:jlu19dlxo111 tradeId:100000109894 security:true @@ -384,7 +384,7 @@ Process finished with exit code 0 - 通过模拟微信与支付宝两个支付渠道在不同的支付模式下,`刷脸`、`指纹`、`密码`,的组合从而体现了桥接模式的在这类场景中的合理运用。简化了代码的开发,给后续的需求迭代增加了很好的扩展性。 - 从桥接模式的实现形式来看满足了单一职责和开闭原则,让每一部分内容都很清晰易于维护和拓展,但如果我们是实现的高内聚的代码,那么就会很复杂。所以在选择重构代码的时候,需要考虑好整体的设计,否则选不到合理的设计模式,将会让代码变得难以开发。 -- 任何一种设计模式的选择和使用都应该遵顼符合场景为主,不要刻意使用。而且统一场景因为业务的复杂从而可能需要使用到多种设计模式的组合,才能将代码设计的更加合理。但这种经验需要从实际的项目中学习经验,并提不断的运用。 +- 任何一种设计模式的选择和使用都应该遵循符合场景为主,不要刻意使用。而且同一场景因为业务的复杂从而可能需要使用到多种设计模式的组合,才能将代码设计的更加合理。但这种经验需要从实际的项目中学习经验,并能不断的运用。 diff --git "a/docs/md/develop/design-pattern/2020-06-08-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\273\204\345\220\210\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-08-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\273\204\345\220\210\346\250\241\345\274\217\343\200\213.md" index 39116d894..9bac7731e 100644 --- "a/docs/md/develop/design-pattern/2020-06-08-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\273\204\345\220\210\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-08-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\273\204\345\220\210\346\250\241\345\274\217\343\200\213.md" @@ -21,7 +21,7 @@ lock: need `小朋友才做选择题,成年人我都要` -头几年只要群里一问我该学哪个开发语言,哪个语言最好。群里肯定聊的特别火热,有人支持PHP、有人喊号Java、也有C++和C#。但这几年开始好像大家并不会真的`刀枪棍棒、斧钺钩叉`般讨论了,大多数时候都是开玩笑的闹一闹。于此同时在整体的互联网开发中很多时候是一些开发语言公用的,共同打造整体的生态圈。而大家选择的方式也是更偏向于不同领域下选择适合的架构,而不是一味地追求某个语言。这可以给很多初学编程的新人一些提议,不要刻意的觉得某个语言好,某个语言不好,只是在适合的场景下选择最需要的。而你要选择的那个语言可以参考招聘网站的需求量和薪资水平决定。 +头几年只要群里一问我该学哪个开发语言,哪个语言最好。群里肯定聊的特别火热,有人支持PHP、有人喊号Java、也有C++和C#。但这几年开始好像大家并不会真的`刀枪棍棒、斧钺钩叉`般讨论了,大多数时候都是开玩笑的闹一闹。与此同时在整体的互联网开发中很多时候是一些开发语言公用的,共同打造整体的生态圈。而大家选择的方式也是更偏向于不同领域下选择适合的架构,而不是一味地追求某个语言。这可以给很多初学编程的新人一些提议,不要刻意的觉得某个语言好,某个语言不好,只是在适合的场景下选择最需要的。而你要选择的那个语言可以参考招聘网站的需求量和薪资水平决定。 `编程开发不是炫技` @@ -60,7 +60,7 @@ lock: need 以上是一个非常简化版的营销规则`决策树`,根据`性别`、`年龄`来发放不同类型的优惠券,来刺激消费起到精准用户促活的目的。 -虽然一部分小伙伴可能并没有开发过营销场景,但你可能时时刻刻的被营销着。比如你去经常浏览男性喜欢的机械键盘、笔记本电脑、汽车装饰等等,那么久给你推荐此类的优惠券刺激你消费。那么如果你购物不多,或者钱不在自己手里。那么你是否打过车,有一段时间经常有小伙伴喊,为什么同样的距离他就10元,我就15元呢?其实这些都是被营销的案例,一般对于不常使用软件的小伙伴,经常会进行稍微大力度的促活,增加用户粘性。 +虽然一部分小伙伴可能并没有开发过营销场景,但你可能时时刻刻的被营销着。比如你去经常浏览男性喜欢的机械键盘、笔记本电脑、汽车装饰等等,那么就给你推荐此类的优惠券刺激你消费。那么如果你购物不多,或者钱不在自己手里。那么你是否打过车,有一段时间经常有小伙伴喊,为什么同样的距离他就10元,我就15元呢?其实这些都是被营销的案例,一般对于不常使用软件的小伙伴,经常会进行稍微大力度的促活,增加用户粘性。 那么在这里我们就模拟一个类似的决策场景,体现出组合模式在其中起到的重要性。另外,组合模式不只是可以运用于规则决策树,还可以做服务包装将不同的接口进行组合配置,对外提供服务能力,减少开发成本。 @@ -415,7 +415,7 @@ public class TreeEngineHandle extends EngineBase { } ``` -- 这里对于决策引擎的实现就非常简单了,通过传递进来的必要信息;决策树信息、决策物料值,来做具体的树形结构决策。 +- 这里对于决策引擎的实现就非常简单了,通过传递进来的必要信息:决策树信息、决策物料值,来做具体的树形结构决策。 ### 3. 测试验证 @@ -540,7 +540,7 @@ public void init() { ![树形结构的组织关系](https://bugstack.cn/assets/images/2020/itstack-demo-design-8-04.png) - **重要**,这一部分是组合模式非常重要的使用,在我们已经建造好的决策树关系下,可以创建出树的各个节点,以及对节点间使用链路进行串联。 -- 及时后续你需要做任何业务的扩展都可以在里面添加相应的节点,并做动态化的配置。 +- 即使后续你需要做任何业务的扩展都可以在里面添加相应的节点,并做动态化的配置。 - 关于这部分手动组合的方式可以提取到数据库中,那么也就可以扩展到图形界面的进行配置操作。 #### 3.2 编写测试类 @@ -574,11 +574,11 @@ public void test_tree() { Process finished with exit code 0 ``` -- 从测试结果上看这与我们使用`ifelse`是一样的,但是目前这与的组合模式设计下,就非常方便后续的拓展和修改。 +- 从测试结果上看这与我们使用`ifelse`是一样的,但是目前这一种组合模式设计下,就非常方便后续的拓展和修改。 - 整体的组织关系框架以及调用决策流程已经搭建完成,如果阅读到此没有完全理解,可以下载代码观察结构并运行调试。 ## 七、总结 -- 从以上的决策树场景来看,组合模式的主要解决的是一系列简单逻辑节点或者扩展的复杂逻辑节点在不同结构的组织下,对于外部的调用是仍然可以非常简单的。 +- 从以上的决策树场景来看,组合模式主要解决的是一系列简单逻辑节点或者扩展的复杂逻辑节点在不同结构的组织下,对于外部的调用是仍然可以非常简单的。 - 这部分设计模式保证了开闭原则,无需更改模型结构你就可以提供新的逻辑节点的使用并配合组织出新的关系树。但如果是一些功能差异化非常大的接口进行包装就会变得比较困难,但也不是不能很好的处理,只不过需要做一些适配和特定化的开发。 - 很多时候因为你的极致追求和稍有倔强的工匠精神,即使在面对同样的业务需求,你能完成出最好的代码结构和最易于扩展的技术架构。`不要被远不能给你指导提升能力的影响到放弃自己的追求!` diff --git "a/docs/md/develop/design-pattern/2020-06-09-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\243\205\351\245\260\345\231\250\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-09-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\243\205\351\245\260\345\231\250\346\250\241\345\274\217\343\200\213.md" index 7f63e4f0d..53e9b59f4 100755 --- "a/docs/md/develop/design-pattern/2020-06-09-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\243\205\351\245\260\345\231\250\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-09-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\243\205\351\245\260\345\231\250\346\250\241\345\274\217\343\200\213.md" @@ -49,7 +49,7 @@ lock: need - 图片来自:[https://refactoringguru.cn/design-patterns/decorator](https://refactoringguru.cn/design-patterns/decorator) -初看上图感觉装饰器模式有点像俄罗斯套娃、某众汽车🚕,而装饰器的核心就是再不改原有类的基础上给类新增功能。**不改变原有类**,可能有的小伙伴会想到继承、AOP切面,当然这些方式都可以实现,但是使用装饰器模式会是另外一种思路更为灵活,可以避免继承导致的子类过多,也可以避免AOP带来的复杂性。 +初看上图感觉装饰器模式有点像俄罗斯套娃、某众汽车🚕,而装饰器的核心就是在不改原有类的基础上给类新增功能。**不改变原有类**,可能有的小伙伴会想到继承、AOP切面,当然这些方式都可以实现,但是使用装饰器模式会是另外一种思路更为灵活,可以避免继承导致的子类过多,也可以避免AOP带来的复杂性。 **你熟悉的场景很多用到装饰器模式** @@ -197,7 +197,7 @@ Process finished with exit code 0 装饰器主要解决的是直接继承下因功能的不断横向扩展导致子类膨胀的问题,而是用装饰器模式后就会比直接继承显得更加灵活同时这样也就不再需要考虑子类的维护。 -在装饰器模式中有四个比较重要点抽象出来的点; +在装饰器模式中有四个比较重要点抽象出来的点: 1. 抽象构件角色(Component) - `定义抽象接口` 2. 具体构件角色(ConcreteComponent) - `实现抽象接口,可以是一组` 3. 装饰角色(Decorator) - `定义抽象类并继承接口中的方法,保证一致性` @@ -246,7 +246,7 @@ public abstract class SsoDecorator implements HandlerInterceptor { } ``` -- 在装饰类中有两个重点的地方是;1)继承了处理接口、2)提供了构造函数、3)覆盖了方法`preHandle`。 +- 在装饰类中有三个重点的地方是:1)继承了处理接口、2)提供了构造函数、3)覆盖了方法`preHandle`。 - 以上三个点是装饰器模式的核心处理部分,这样可以踢掉对子类继承的方式实现逻辑功能扩展。 #### 2.2 装饰角色逻辑实现 diff --git "a/docs/md/develop/design-pattern/2020-06-11-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\244\226\350\247\202\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-11-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\244\226\350\247\202\346\250\241\345\274\217\343\200\213.md" index 6eb611655..15864be4b 100755 --- "a/docs/md/develop/design-pattern/2020-06-11-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\244\226\350\247\202\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-11-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\244\226\350\247\202\346\250\241\345\274\217\343\200\213.md" @@ -25,11 +25,11 @@ lock: need `没有最好的编程语言,语言只是工具` -刀枪棍棒、斧钺钩叉、包子油条、盒子麻花,是**语言**。五郎八卦棍、十二路弹腿、洪家铁线拳,是**设计**。记得叶问里有一句台词是:`金山找:今天我北方拳术,输给你南方拳术了。叶问:你错了,不是南北拳的问题,是你的问题。`所以当你编程开发写的久了,就不会再特别在意用的语言,而是为目标服务,用最好的设计能力也就是编程的智慧做出做最完美的服务。这也就是编程人员的价值所在! +刀枪棍棒、斧钺钩叉、包子油条、盒子麻花,是**语言**。五郎八卦棍、十二路弹腿、洪家铁线拳,是**设计**。记得叶问里有一句台词是:`金山找:今天我北方拳术,输给你南方拳术了。叶问:你错了,不是南北拳的问题,是你的问题。`所以当你编程开发写的久了,就不会再特别在意用的语言,而是为目标服务,用最好的设计能力也就是编程的智慧做出最最完美的服务。这也就是编程人员的价值所在! -`设计与反设计以及过渡设计` +`设计与反设计以及过度设计` -设计模式是解决程序中不合理、不易于扩展、不易于维护的问题,也是干掉大部分`ifelse`的利器,在我们常用的框架中基本都会用到大量的设计模式来构建组件,这样也能方便框架的升级和功能的扩展。但!如果不能合理的设计以及乱用设计模式,会导致整个编程变得更加复杂难维护,也就是我们常说的;`反设计`、`过渡设计`。而这部分设计能力也是从实践的项目中获取的经验,不断的改造优化摸索出的最合理的方式,应对当前的服务体量。 +设计模式是解决程序中不合理、不易于扩展、不易于维护的问题,也是干掉大部分`ifelse`的利器,在我们常用的框架中基本都会用到大量的设计模式来构建组件,这样也能方便框架的升级和功能的扩展。但!如果不能合理的设计以及乱用设计模式,会导致整个编程变得更加复杂难维护,也就是我们常说的;`反设计`、`过度设计`。而这部分设计能力也是从实践的项目中获取的经验,不断的改造优化摸索出的最合理的方式,应对当前的服务体量。 ## 二、开发环境 @@ -129,7 +129,7 @@ public class HelloWorldApplication { } ``` -- 这里是通用的`SpringBoot`启动类。需要添加的是一个配置注解`@Configuration`,为了后续可以读取白名单配置。 +- 这里是通用的`SpringBoot`启动类。 ## 五、用一坨坨代码实现 @@ -177,7 +177,7 @@ public class HelloWorldController { ## 六、外观模式重构代码 -`接下来使用外观器模式来进行代码优化,也算是一次很小的重构。` +`接下来使用外观模式来进行代码优化,也算是一次很小的重构。` 这次重构的核心是使用外观模式也可以说门面模式,结合`SpringBoot`中的自定义`starter`中间件开发的方式,统一处理所有需要白名单的地方。 @@ -281,7 +281,7 @@ public class StarterAutoConfigure { } ``` -- 以上代码是对配置的获取操作,主要是对注解的定义;`@Configuration`、`@ConditionalOnClass`、`@EnableConfigurationProperties`,这一部分主要是与SpringBoot的结合使用。 +- 以上代码是对配置的获取操作,主要是对注解的定义:`@Configuration`、`@ConditionalOnClass`、`@EnableConfigurationProperties`,这一部分主要是与SpringBoot的结合使用。 #### 2.4 切面注解定义 @@ -486,4 +486,4 @@ public UserInfo queryUserInfo(@RequestParam String userId) { - 以上我们通过中间件的方式实现外观模式,这样的设计可以很好的增强代码的隔离性,以及复用性,不仅使用上非常灵活也降低了每一个系统都开发这样的服务带来的风险。 - 可能目前你看这只是非常简单的白名单控制,是否需要这样的处理。但往往一个小小的开始会影响着后续无限的扩展,实际的业务开发往往也要复杂的很多,不可能如此简单。因而使用设计模式来让代码结构更加干净整洁。 -- 很多时候不是设计模式没有用,而是自己编程开发经验不足导致即使学了设计模式也很难驾驭。毕竟这些知识都是经过一些实际操作提炼出来的精华,但如果你可以按照本系列文章中的案例方式进行学习实操,还是可以增强这部分设计能力的。 \ No newline at end of file +- 很多时候不是设计模式没有用,而是自己编程开发经验不足导致即使学了设计模式也很难驾驭。毕竟这些知识都是经过一些实际操作提炼出来的精华,但如果你可以按照本系列文章中的案例方式进行学习实操,还是可以增强这部分设计能力的。 diff --git "a/docs/md/develop/design-pattern/2020-06-14-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\272\253\345\205\203\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-14-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\272\253\345\205\203\346\250\241\345\274\217\343\200\213.md" index 0b8fc8b95..78d5162e2 100644 --- "a/docs/md/develop/design-pattern/2020-06-14-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\272\253\345\205\203\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-14-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\272\253\345\205\203\346\250\241\345\274\217\343\200\213.md" @@ -21,7 +21,7 @@ lock: need `程序员👨‍💻‍的上下文是什么?` -很多时候一大部分编程开发的人员都只是关注于功能的实现,只要自己把这部分需求写完就可以了,有点像被动的交作业。这样的问题一方面是由于很多新人还不了解程序员的职业发展,还有一部分是对于编程开发只是工作并非兴趣。但在程序员的发展来看,如果不能很好的处理上文(`产品`),下文(`测试`),在这样不能很好的了解业务和产品发展,也不能编写出很有体系结构的代码,日久天长,1到3年、3到5年,就很难跨越一个个技术成长的分水岭。 +很多时候一大部分编程开发的人员都只是关注于功能的实现,只要自己把这部分需求写完就可以了,有点像被动的交作业。这样的问题一方面是由于很多新人还不了解程序员的职业发展,还有一部分是对于编程开发只是工作并非兴趣。但在程序员的发展来看,如果不能很好的处理上文(`产品`),下文(`测试`),这样不能很好的了解业务和产品发展,也不能编写出很有体系结构的代码,日久天长,1到3年、3到5年,就很难跨越一个个技术成长的分水岭。 `拥有接受和学习新知识的能力` @@ -29,7 +29,7 @@ lock: need `编程能力怎样会成长的最快` -工作内容往往有些像在工厂🏭拧螺丝,大部分内容是重复的,也可以想象过去的一年你有过多少创新和学习了新的技能。那么这时候一般为了多学些内容会买一些技术书籍,但!技术类书籍和其他书籍不同,只要不去用看了也就只是轻描淡写,很难接纳和理解。就像设计模式,虽然可能看了几遍,但是在实际编码中仍然很少会用,大部分原因还是没有认认真真的跟着实操。事必躬亲才是学习编程的最好是方式。 +工作内容往往有些像在工厂🏭拧螺丝,大部分内容是重复的,也可以想象过去的一年你有过多少创新和学习了新的技能。那么这时候一般为了多学些内容会买一些技术书籍,但!技术类书籍和其他书籍不同,只要不去用看了也就只是轻描淡写,很难接纳和理解。就像设计模式,虽然可能看了几遍,但是在实际编码中仍然很少会用,大部分原因还是没有认认真真的跟着实操。事必躬亲才是学习编程的最好方式。 ## 二、开发环境 @@ -50,7 +50,7 @@ lock: need 享元模式,主要在于共享通用对象,减少内存的使用,提升系统的访问效率。而这部分共享对象通常比较耗费内存或者需要查询大量接口或者使用数据库资源,因此统一抽离作为共享对象使用。 -另外享元模式可以分为在服务端和客户端,一般互联网H5和Web场景下大部分数据都需要服务端进行处理,比如数据库连接池的使用、多线程线程池的使用,除了这些功能外,还有些需要服务端进行包装后的处理下发给客户端,因为服务端需要做享元处理。但在一些游戏场景下,很多都是客户端需要进行渲染地图效果,比如;树木、花草、鱼虫,通过设置不同元素描述使用享元公用对象,减少内存的占用,让客户端的游戏更加流畅。 +另外享元模式可以分为在服务端和客户端,一般互联网H5和Web场景下大部分数据都需要服务端进行处理,比如数据库连接池的使用、多线程线程池的使用,除了这些功能外,还有些需要服务端进行包装后的处理下发给客户端,因为服务端需要做享元处理。但在一些游戏场景下,很多都是客户端需要进行渲染地图效果,比如:树木、花草、鱼虫,通过设置不同元素描述使用享元公用对象,减少内存的占用,让客户端的游戏更加流畅。 在享元模型的实现中需要使用到享元工厂来进行管理这部分独立的对象和共享的对象,避免出现线程安全的问题。 @@ -70,7 +70,7 @@ lock: need `逻辑很简单,就怕你写乱。一片片的固定内容和变化内容的查询组合,CV的哪里都是!` -其实这部分逻辑的查询在一般情况很多程序员都是先查询固定信息,在使用过滤的或者添加if判断的方式补充变化的信息,也就是库存。这样写最开始并不会看出来有什么问题,但随着方法逻辑的增加,后面就越来越多重复的代码。 +其实这部分逻辑的查询在一般情况很多程序员都是先查询固定信息,再使用过滤的或者添加if判断的方式补充变化的信息,也就是库存。这样写最开始并不会看出来有什么问题,但随着方法逻辑的增加,后面就越来越多重复的代码。 ### 1. 工程结构 diff --git "a/docs/md/develop/design-pattern/2020-06-16-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\273\243\347\220\206\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-16-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\273\243\347\220\206\346\250\241\345\274\217\343\200\213.md" index c3e40ba68..90762855b 100755 --- "a/docs/md/develop/design-pattern/2020-06-16-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\273\243\347\220\206\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-16-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\273\243\347\220\206\346\250\241\345\274\217\343\200\213.md" @@ -29,7 +29,7 @@ lock: need `除了业务中的CRUD开发,有些技术你真的很难接触到!` -可能很多小伙伴认为技术开发就是承接下产品需求,写写CRUD,不会的百度一下,就完事了,总觉得别人问的东西像再造火箭一样。但在高体量、高并发的业务场景下,每一次的压测优化,性能提升,都像在研究一道数学题一样,反复的锤炼,压榨性能。不断的深究,找到最合适的设计。除了这些优化提升外,还有那么广阔的技术体系栈,都可能因为你只是注重CRUD而被忽略;字节码编程、领域驱动设计架构、代理模式中间件开发、JVM虚拟机实现原理等等。 +可能很多小伙伴认为技术开发就是承接下产品需求,写写CRUD,不会的百度一下,就完事了,总觉得别人问的东西像在造火箭一样。但在高体量、高并发的业务场景下,每一次的压测优化,性能提升,都像在研究一道数学题一样,反复的锤炼,压榨性能。不断的深究,找到最合适的设计。除了这些优化提升外,还有那么广阔的技术体系栈,都可能因为你只是注重CRUD而被忽略;字节码编程、领域驱动设计架构、代理模式中间件开发、JVM虚拟机实现原理等等。 ## 二、开发环境 @@ -48,9 +48,9 @@ lock: need - 图片来自:[https://refactoringguru.cn/design-patterns/proxy](https://refactoringguru.cn/design-patterns/proxy) -代理模式有点像老大和小弟,也有点像分销商。主要解决的是问题是为某些资源的访问、对象的类的易用操作上提供方便使用的代理服务。而这种设计思想的模式经常会出现在我们的系统中,或者你用到过的组件中,它们都提供给你一种非常简单易用的方式控制原本你需要编写很多代码的进行使用的服务类。 +代理模式有点像老大和小弟,也有点像分销商。主要解决的问题是为某些资源的访问、对象的类的易用操作上提供方便使用的代理服务。而这种设计思想的模式经常会出现在我们的系统中,或者你用到过的组件中,它们都提供给你一种非常简单易用的方式控制原本你需要编写很多代码的进行使用的服务类。 -类似这样的场景可以想到; +类似这样的场景可以想到: 1. 你的数据库访问层面经常会提供一个较为基础的应用,以此来减少应用服务扩容时不至于数据库连接数暴增。 2. 使用过的一些中间件例如;RPC框架,在拿到jar包对接口的描述后,中间件会在服务启动的时候生成对应的代理类,当调用接口的时候,实际是通过代理类发出的socket信息进行通过。 3. 另外像我们常用的`MyBatis`,基本是定义接口但是不需要写实现类,就可以对`xml`或者自定义注解里的`sql`语句进行增删改查操作。 @@ -235,7 +235,7 @@ public void test_IUserDao() { ``` - 测试的过程比较简单,通过加载Bean工厂获取我们的代理类的实例对象,之后调用方法返回结果。 -- 那么这个过程你可以看到我们是没有对接口先一个实现类的,而是使用代理的方式给接口生成一个实现类,并交给spring管理。 +- 那么这个过程你可以看到我们是没有对接口写一个实现类的,而是使用代理的方式给接口生成一个实现类,并交给spring管理。 #### 3.2 测试结果 diff --git "a/docs/md/develop/design-pattern/2020-06-18-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\264\243\344\273\273\351\223\276\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-18-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\264\243\344\273\273\351\223\276\346\250\241\345\274\217\343\200\213.md" index 2d6090024..92ccb5316 100755 --- "a/docs/md/develop/design-pattern/2020-06-18-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\264\243\344\273\273\351\223\276\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-18-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\264\243\344\273\273\351\223\276\346\250\241\345\274\217\343\200\213.md" @@ -21,7 +21,7 @@ lock: need `场地和场景的重要性` -射击🏹需要去靶场学习、滑雪🏂需要去雪场体验、开车🚗需要能上路实践,而编程开发除了能完成产品的功能流程,还需要保证系统的可靠性能。就像你能听到的一些系统监控指标;`QPS`、`TPS`、`TP99`、`TP999`、`可用率`、`响应时长`等等,而这些指标的总和评估就是一个系统的健康度。但如果你几乎没有听到这样的技术术语,也没接触过类似高并发场景,那么就很像驾驶证的科目1考了100分,但不能上路。没有这样的技术场景给你训练,让你不断的体会系统的脾气秉性,即便你有再多的想法都没法实现。所以,如果真的想学习一定要去一个有实操的场景,下水试试才能学会狗刨。 +射击🏹需要去靶场学习、滑雪🏂需要去雪场体验、开车🚗需要能上路实践,而编程开发除了能完成产品的功能流程,还需要保证系统的可靠性能。就像你能听到的一些系统监控指标:`QPS`、`TPS`、`TP99`、`TP999`、`可用率`、`响应时长`等等,而这些指标的总和评估就是一个系统的健康度。但如果你几乎没有听到这样的技术术语,也没接触过类似高并发场景,那么就很像驾驶证的科目1考了100分,但不能上路。没有这样的技术场景给你训练,让你不断的体会系统的脾气秉性,即便你有再多的想法都没法实现。所以,如果真的想学习一定要去一个有实操的场景,下水试试才能学会狗刨。 `你的视觉盲区有多大` @@ -51,7 +51,7 @@ lock: need **击鼓传雷**,看上图你是否想起周星驰有一个电影,大家坐在海边围成一个圈,拿着一个点燃的炸弹,互相传递。 -责任链模式的核心是解决一组服务中的先后执行处理关系,就有点像你没钱花了,需要家庭财务支出审批,10块钱以下找闺女审批,100块钱先闺女审批在媳妇审批。你可以理解想象成当你要跳槽的时候被安排的明明白白的被各个领导签字放行。 +责任链模式的核心是解决一组服务中的先后执行处理关系,就有点像你没钱花了,需要家庭财务支出审批,10块钱以下找闺女审批,100块钱先闺女审批再媳妇审批。你可以理解想象成当你要跳槽的时候被安排的明明白白的被各个领导签字放行。 ## 四、案例场景模拟 @@ -203,7 +203,7 @@ Process finished with exit code 0 ## 六、责任链模式重构代码 -`接下来使用装饰器模式来进行代码优化,也算是一次很小的重构。` +`接下来使用责任链模式来进行代码优化,也算是一次很小的重构。` 责任链模式可以让各个服务模块更加清晰,而每一个模块间可以通过`next`的方式进行获取。而每一个`next`是由继承的统一抽象类实现的。最终所有类的职责可以动态的进行编排使用,编排的过程可以做成可配置化。 @@ -255,7 +255,7 @@ public class AuthInfo { } ``` -- 这个类的是包装了责任链处理过程中返回结果的类,方面处理每个责任链的返回信息。 +- 这个类是包装了责任链处理过程中返回结果的类,方便处理每个责任链的返回信息。 #### 2.2 链路抽象类定义 @@ -420,7 +420,7 @@ public void test_AuthLink() throws ParseException { ``` - 这里包括最核心的责任链创建,实际的业务中会包装到控制层;` AuthLink authLink = new Level3AuthLink("1000013", "王工") .appendNext(new Level2AuthLink("1000012", "张经理") .appendNext(new Level1AuthLink("1000011", "段总")));` 通过把不同的责任节点进行组装,构成一条完整业务的责任链。 -- 接下里不断的执行查看审核链路`authLink.doAuth(...)`,通过返回结果对数据进行3、2、1级负责人审核,直至最后审核全部完成。 +- 接下来不断的执行查看审核链路`authLink.doAuth(...)`,通过返回结果对数据进行3、2、1级负责人审核,直至最后审核全部完成。 #### 3.2 测试结果 @@ -443,4 +443,4 @@ Process finished with exit code 0 - 从上面代码从if语句重构到使用责任链模式开发可以看到,我们的代码结构变得清晰干净了,也解决了大量if语句的使用。并不是if语句不好,只不过if语句并不适合做系统流程设计,但是在做判断和行为逻辑处理中还是非常可以使用的。 - 在我们前面学习结构性模式中讲到过组合模式,它像是一颗组合树一样,我们搭建出一个流程决策树。其实这样的模式也是可以和责任链模型进行组合扩展使用,而这部分的重点在于如何关联**链路的关联**,最终的执行都是在执行在中间的关系链。 -- 责任链模式很好的处理单一职责和开闭原则,简单了耦合也使对象关系更加清晰,而且外部的调用方并不需要关心责任链是如何进行处理的*(以上程序中可以把责任链的组合进行包装,在提供给外部使用)*。但除了这些优点外也需要是适当的场景才进行使用,避免造成性能以及编排混乱调试测试疏漏问题。 \ No newline at end of file +- 责任链模式很好的处理单一职责和开闭原则,简单了耦合也使对象关系更加清晰,而且外部的调用方并不需要关心责任链是如何进行处理的*(以上程序中可以把责任链的组合进行包装,再提供给外部使用)*。但除了这些优点外也需要是适当的场景才进行使用,避免造成性能以及编排混乱调试测试疏漏问题。 diff --git "a/docs/md/develop/design-pattern/2020-06-21-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\221\275\344\273\244\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-21-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\221\275\344\273\244\346\250\241\345\274\217\343\200\213.md" index 5b8f78738..32462cab5 100644 --- "a/docs/md/develop/design-pattern/2020-06-21-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\221\275\344\273\244\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-21-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\221\275\344\273\244\346\250\241\345\274\217\343\200\213.md" @@ -25,11 +25,11 @@ lock: need `学习方法的重要性` -不会学习往往会耽误很多时间,又没有可观的收成。但不会学习有时候是因为**懒**造成的,尤其是学习视频、书籍资料、技术文档等,如果只是看了却不是实际操作验证,那么真的很难把别人的知识让自己吸收,即使是当时感觉会了也很快就会忘记。时而也经常会有人找到你说;“这个我不知道,你先告诉我,过后我就学。”但过后你学了吗? +不会学习往往会耽误很多时间,又没有可观的收成。但不会学习有时候是因为**懒**造成的,尤其是学习视频、书籍资料、技术文档等,如果只是看了却不是实际操作验证,那么真的很难把别人的知识让自己吸收,即使是当时感觉会了也很快就会忘记。时而也经常会有人找到你说:“这个我不知道,你先告诉我,过后我就学。”,但过后你学了吗? `你愿意为一个知识盲区付出多长时间` -你心里时而会蹦出这样的词吗;`太难了我不会`、`找个人帮一下吧`、`放弃了放弃了`,其实谁都可能遇到很不好解决的问题,也是可以去问去咨询的。但,如果在这之前你没有在自己的大脑中反复的寻找答案,那么你的大脑中就不会形成一个凸点的知识树,缺少了这个学习过程也就缺少了查阅各种资料给自己大脑填充知识的机会,哪怕是问到了答案最终也会因时间流逝而忘记。 +你心里时而会蹦出这样的词吗:`太难了我不会`、`找个人帮一下吧`、`放弃了放弃了`,其实谁都可能遇到很不好解决的问题,也是可以去问去咨询的。但,如果在这之前你没有在自己的大脑中反复的寻找答案,那么你的大脑中就不会形成一个凸点的知识树,缺少了这个学习过程也就缺少了查阅各种资料给自己大脑填充知识的机会,哪怕是问到了答案最终也会因时间流逝而忘记。 ## 二、开发环境 @@ -50,13 +50,13 @@ lock: need 命令模式在我们通常的互联网开发中相对来说用的比较少,但这样的模式在我们的日常中却经常使用到,那就是`Ctrl+C`、`Ctrl+V`。当然如果你开发过一些桌面应用,也会感受到这样设计模式的应用场景。从这样的模式感受上,可以想到这是把逻辑实现与操作请求进行分离,降低耦合方便扩展。 -命令模式是行为模式中的一种,以数据驱动的方式将`命令对象`,可以使用构造函数的方式传递给调用者。调用者再提供相应的实现为命令执行提供操作方法。可能会感觉这部分有一些饶,可以通过对代码的实现进行理解,在通过实操来熟练。 +命令模式是行为模式中的一种,以数据驱动的方式将`命令对象`,可以使用构造函数的方式传递给调用者。调用者再提供相应的实现为命令执行提供操作方法。可能会感觉这部分有一些饶,可以通过对代码的实现进行理解,再通过实操来熟练。 -在这个设计模式的实现过程中有如下几个比较重要的点; -1. 抽象命令类;声明执行命令的接口和方法 -2. 具体的命令实现类;接口类的具体实现,可以是一组相似的行为逻辑 -3. 实现者;也就是为命令做实现的具体实现类 -4. 调用者;处理命令、实现的具体操作者,负责对外提供命令服务 +在这个设计模式的实现过程中有如下几个比较重要的点: +1. 抽象命令类:声明执行命令的接口和方法 +2. 具体的命令实现类:接口类的具体实现,可以是一组相似的行为逻辑 +3. 实现者:也就是为命令做实现的具体实现类 +4. 调用者:处理命令、实现的具体操作者,负责对外提供命令服务 ## 四、案例场景模拟 @@ -130,7 +130,7 @@ public class XiaoEr { ``` - 在这个类的实现中提供了两个方法,一个方法用于点单添加菜品`order()`,另外一个方法展示菜品的信息`placeOrder()`。 -- 从上面可以看到有比较多的if语句判断类型进行添加菜品,那么对于这样的代码后续就需要大量的经历进行维护,同时可能实际的逻辑要比这复杂的多。都写在这样一个类里会变得耦合的非常严重。 +- 从上面可以看到有比较多的if语句判断类型进行添加菜品,那么对于这样的代码后续就需要大量的精力进行维护,同时可能实际的逻辑要比这复杂的多。都写在这样一个类里会变得耦合的非常严重。 ## 六、命令模式重构代码 @@ -171,8 +171,8 @@ itstack-demo-design-14-02 ![命令模式模型结构](https://bugstack.cn/assets/images/2020/itstack-demo-design-14-03.png) -- 从上图可以看到整体分为三大块;命令实现(菜品)、逻辑实现(厨师)、调用者(小二),以上这三面的实现就是命令模式的核心内容。 -- 经过这样的拆解就可以非常方面的扩展菜品、厨师,对于调用者来说这部分都是松耦合的,在整体的框架下可以非常容易加入实现逻辑。 +- 从上图可以看到整体分为三大块:命令实现(菜品)、逻辑实现(厨师)、调用者(小二),以上这三面的实现就是命令模式的核心内容。 +- 经过这样的拆解就可以非常方便的扩展菜品、厨师,对于调用者来说这部分都是松耦合的,在整体的框架下可以非常容易加入实现逻辑。 ### 2. 代码实现 @@ -351,7 +351,7 @@ public class SiChuanCook implements ICook { ``` - 这里是四类不同菜品的厨师👩‍🍳,在这个实现的过程是模拟打了日志,相当于通知了厨房里具体的厨师进行菜品烹饪。 -- 从以上可以看到,当我们需要进行扩从的时候是可以非常方便的进行添加的,每一个类都具备了单一职责原则。 +- 从以上可以看到,当我们需要进行扩充的时候是可以非常方便的进行添加的,每一个类都具备了单一职责原则。 #### 2.5 调用者(小二) @@ -424,6 +424,6 @@ Process finished with exit code 0 ## 七、总结 -- 从以上的内容和例子可以感受到,命令模式的使用场景需要分为三个比较大的块;`命令`、`实现`、`调用者`,而这三块内容的拆分也是选择适合场景的关键因素,经过这样的拆分可以让逻辑具备单一职责的性质,便于扩展。 +- 从以上的内容和例子可以感受到,命令模式的使用场景需要分为三个比较大的块:`命令`、`实现`、`调用者`,而这三块内容的拆分也是选择适合场景的关键因素,经过这样的拆分可以让逻辑具备单一职责的性质,便于扩展。 - 通过这样的实现方式与if语句相比,降低了耦合性也方便其他的命令和实现的扩展。但同时这样的设计模式也带来了一点问题,就是在各种命令与实现的组合下,会扩展出很多的实现类,需要进行管理。 - 设计模式的学习一定要勤加练习,哪怕最开始是模仿实现也是可以的,多次的练习后再去找到一些可以优化的场景,并逐步运用到自己的开发中。提升自己对代码的设计感觉,让代码结构更加清晰易扩展。 diff --git "a/docs/md/develop/design-pattern/2020-06-23-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\277\255\344\273\243\345\231\250\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-23-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\277\255\344\273\243\345\231\250\346\250\241\345\274\217\343\200\213.md" index 45d3ba21a..438435e22 100644 --- "a/docs/md/develop/design-pattern/2020-06-23-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\277\255\344\273\243\345\231\250\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-23-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\277\255\344\273\243\345\231\250\346\250\241\345\274\217\343\200\213.md" @@ -47,13 +47,13 @@ lock: need - 图片来自:[https://refactoringguru.cn/design-patterns/iterator](https://refactoringguru.cn/design-patterns/iterator) -迭代器模式,常见的就是我们日常使用的`iterator`遍历。虽然这个设计模式在我们的实际业务开发中的场景并不多,但却几乎每天都要使用`jdk`为我们提供的`list`集合遍历。另外增强的for循环虽然是循环输出数据,但是他不是迭代器模式。迭代器模式的特点是实现`Iterable`接口,通过`next`的方式获取集合元素,同时具备对元素的删除等操作。而增强的for循环是不可以的。 +迭代器模式,常见的就是我们日常使用的`iterator`遍历。虽然这个设计模式在我们的实际业务开发中的场景并不多,但却几乎每天都要使用`jdk`为我们提供的`list`集合遍历。另外增强的for循环虽然是循环输出数据,但是它不是迭代器模式。迭代器模式的特点是实现`Iterable`接口,通过`next`的方式获取集合元素,同时具备对元素的删除等操作。而增强的for循环是不可以的。 -这种设计模式的优点是可以让我们以相同的方式,遍历不同的数据结构元素,这些数据结构包括;`数组`、`链表`、`树`等,而用户在使用遍历的时候并不需要去关心每一种数据结构的遍历处理逻辑,从让使用变得统一易用。 +这种设计模式的优点是可以让我们以相同的方式,遍历不同的数据结构元素,这些数据结构包括:`数组`、`链表`、`树`等,而用户在使用遍历的时候并不需要去关心每一种数据结构的遍历处理逻辑,从让使用变得统一易用。 ## 四、案例场景模拟 -![场景模拟;公司树形组织架构](https://bugstack.cn/assets/images/2020/itstack-demo-design-15-02.png) +![场景模拟:公司树形组织架构](https://bugstack.cn/assets/images/2020/itstack-demo-design-15-02.png) **在本案例中我们模拟迭代遍历输出公司中树形结构的组织架构关系中雇员列表** @@ -63,10 +63,10 @@ lock: need ## 五、迭代器模式遍历组织结构 -在实现迭代器模式之前可以先阅读下`java`中`list`方法关于`iterator`的实现部分,几乎所有的迭代器开发都会按照这个模式来实现,这个模式主要分为以下几块; -1. Collection,集合方法部分用于对自定义的数据结构添加通用方法;`add`、`remove`、`iterator`等核心方法。 +在实现迭代器模式之前可以先阅读下`java`中`list`方法关于`iterator`的实现部分,几乎所有的迭代器开发都会按照这个模式来实现,这个模式主要分为以下几块: +1. Collection,集合方法部分用于对自定义的数据结构添加通用方法:`add`、`remove`、`iterator`等核心方法。 2. Iterable,提供获取迭代器,这个接口类会被`Collection`继承。 -3. Iterator,提供了两个方法的定义;`hasNext`、`next`,会在具体的数据结构中写实现方式。 +3. Iterator,提供了两个方法的定义:`hasNext`、`next`,会在具体的数据结构中写实现方式。 除了这样通用的迭代器实现方式外,我们的组织关系结构树,是由节点和节点间的关系链构成,所以会比上述的内容多一些入参。 @@ -118,7 +118,7 @@ public class Employee { } ``` -- 这是一个简单的雇员类,也就是公司员工的信息类,包括必要的信息;id、姓名、备注。 +- 这是一个简单的雇员类,也就是公司员工的信息类,包括必要的信息:id、姓名、备注。 #### 2.2 树节点链路 @@ -150,7 +150,7 @@ public interface Iterator { ``` - 这里的这个类和`java`的`jdk`中提供的是一样的,这样也方面后续读者可以对照`list`的`Iterator`进行源码学习。 -- 方法描述;`hasNext`,判断是否有下一个元素、`next`,获取下一个元素。这个在`list`的遍历中是经常用到的。 +- 方法描述:`hasNext`,判断是否有下一个元素、`next`,获取下一个元素。这个在`list`的遍历中是经常用到的。 #### 2.4 可迭代接口定义 @@ -182,7 +182,7 @@ public interface Collection extends Iterable { } ``` -- 这里我们定义集合操作接口;`Collection`,同时继承了另外一个接口`Iterable`的方法`iterator()`。这样后续谁来实现这个接口,就需要实现上述定义的一些基本功能;`添加元素`、`删除元素`、`遍历`。 +- 这里我们定义集合操作接口:`Collection`,同时继承了另外一个接口`Iterable`的方法`iterator()`。这样后续谁来实现这个接口,就需要实现上述定义的一些基本功能:`添加元素`、`删除元素`、`遍历`。 - 同时你可能注意到这里定义了两个泛型``,因为我们的数据结构一个是用于添加元素,另外一个是用于添加树节点的链路关系。 #### 2.6 (核心)迭代器功能实现 @@ -193,7 +193,7 @@ public class GroupStructure implements Collection { private String groupId; // 组织ID,也是一个组织链的头部ID private String groupName; // 组织名称 private Map employeeMap = new ConcurrentHashMap(); // 雇员列表 - private Map> linkMap = new ConcurrentHashMap>(); // 组织架构关系;id->list + private Map> linkMap = new ConcurrentHashMap>(); // 组织架构关系:id->list private Map invertedMap = new ConcurrentHashMap(); // 反向关系链 public GroupStructure(String groupId, String groupName) { @@ -284,7 +284,7 @@ public class GroupStructure implements Collection { ``` - 以上的这部分代码稍微有点长,主要包括了对元素的添加和删除。另外最重要的是对遍历的实现` new Iterator`。 -- 添加和删除元素相对来说比较简单,使用了两个map数组结构进行定义;`雇员列表`、`组织架构关系;id->list`。当元素添加元素的时候,会分别在不同的方法中向`map`结构中进行填充**指向关系(A->B)**,也就构建出了我们的树形组织关系。 +- 添加和删除元素相对来说比较简单,使用了两个map数组结构进行定义:`雇员列表`、`组织架构关系:id->list`。当元素添加元素的时候,会分别在不同的方法中向`map`结构中进行填充**指向关系(A->B)**,也就构建出了我们的树形组织关系。 **迭代器实现思路** @@ -342,13 +342,13 @@ public void test_iterator() { Process finished with exit code 0 ``` -- 从遍历的结果可以看到,我们是顺着树形结构的深度开始遍历,一直到右侧的节点**3**;`雇员 Id:2、雇员 Id:4...雇员 Id:3` +- 从遍历的结果可以看到,我们是顺着树形结构的开始深度遍历,一直到右侧的节点**3**:`雇员 Id:2、雇员 Id:4...雇员 Id:3` ## 六、总结 - 迭代器的设计模式从以上的功能实现可以看到,满足了单一职责和开闭原则,外界的调用方也不需要知道任何一个不同的数据结构在使用上的遍历差异。可以非常方便的扩展,也让整个遍历变得更加干净整洁。 - 但从结构的实现上可以看到,迭代器模式的实现过程相对来说是比较复杂的,类的实现上也扩增了需要外部定义的类,使得遍历与原数据结构分开。虽然这是比较麻烦的,但可以看到在使用java的jdk时候,迭代器的模式还是很好用的,可以非常方便扩展和升级。 -- 以上的设计模式场景实现过程可能对新人有一些不好理解点,包括;迭代器三个接口的定义、树形结构的数据关系、树结构深度遍历思路。这些都需要反复实现练习才能深入的理解,事必躬亲,亲历亲为,才能让自己掌握这些知识。 +- 以上的设计模式场景实现过程可能对新人有一些不好理解点,包括:迭代器三个接口的定义、树形结构的数据关系、树结构深度遍历思路。这些都需要反复实现练习才能深入的理解,事必躬亲,亲力亲为,才能让自己掌握这些知识。 diff --git "a/docs/md/develop/design-pattern/2020-06-27-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\270\255\344\273\213\350\200\205\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-27-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\270\255\344\273\213\350\200\205\346\250\241\345\274\217\343\200\213.md" index 3b225e8f5..f40b2b0f7 100755 --- "a/docs/md/develop/design-pattern/2020-06-27-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\270\255\344\273\213\350\200\205\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-27-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\344\270\255\344\273\213\350\200\205\346\250\241\345\274\217\343\200\213.md" @@ -25,11 +25,11 @@ lock: need `思想没有产品高才写出一片的ifelse` -当你承接一个需求的时候,比如;交易、订单、营销、保险等各类场景。如果你不熟悉这个场景下的业务模式,以及将来的拓展方向,那么很难设计出良好可扩展的系统。再加上产品功能初建,说老板要的急,尽快上线。作为程序员的你更没有时间思考,整体一看现在的需求也不难,直接上手开干(`一个方法两个if语句`),这样确实满足了当前需求。但老板的想法多呀,产品也跟着变化快,到你这就是改改改,加加加。当然你也不客气,回首掏就是1024个if语句! +当你承接一个需求的时候,比如:交易、订单、营销、保险等各类场景。如果你不熟悉这个场景下的业务模式,以及将来的拓展方向,那么很难设计出良好可扩展的系统。再加上产品功能初建,说老板要的急,尽快上线。作为程序员的你更没有时间思考,整体一看现在的需求也不难,直接上手开干(`一个方法两个if语句`),这样确实满足了当前需求。但老板的想法多呀,产品也跟着变化快,到你这就是改改改,加加加。当然你也不客气,回首掏就是1024个if语句! `日积月累的技术沉淀是为了厚积薄发` -粗略的估算过,如果从上大学开始每天写`200`行,一个月是`6000`行,一年算10个月话,就是6万行,第三年出去实习的是时候就有`20`万行的代码量。如果你能做到这一点,找工作难?有时候很多事情就是靠时间积累出来的,想走捷径有时候真的没有。你的技术水平、你的业务能力、你身上的肉,都是一点点积累下来的,不要浪费看似很短的时间,一年年坚持下来,留下印刻青春的痕迹,多给自己武装上一些能力。 +粗略的估算过,如果从上大学开始每天写`200`行,一个月是`6000`行,一年算10个月话,就是6万行,第三年出去实习的时候就有`20`万行的代码量。如果你能做到这一点,找工作难?有时候很多事情就是靠时间积累出来的,想走捷径有时候真的没有。你的技术水平、你的业务能力、你身上的肉,都是一点点积累下来的,不要浪费看似很短的时间,一年年坚持下来,留下印刻青春的痕迹,多给自己武装上一些能力。 ## 二、开发环境 @@ -103,7 +103,7 @@ public class JDBCUtil { ResultSet resultSet = stmt.executeQuery("SELECT id, name, age, createTime, updateTime FROM user"); //4. 如果有数据 resultSet.next() 返回true while (resultSet.next()) { - logger.info("测试结果 姓名:{} 年龄:{}", resultSet.getString("name"),resultSet.getInt("age")); + logger.info("测试结果 姓名:{} 年龄:{}", resultSet.getString("name"), resultSet.getInt("age")); } } @@ -167,9 +167,9 @@ itstack-demo-design-16-02 ![中介者模式模型结构](https://bugstack.cn/assets/images/2020/itstack-demo-design-16-03.png) - 以上是对ORM框架实现的核心类,包括了;加载配置文件、对xml解析、获取数据库session、操作数据库以及结果返回。 -- 左上是对数据库的定义和处理,基本包括我们常用的方法;` T selectOne`、` List selectList`等。 +- 左上是对数据库的定义和处理,基本包括我们常用的方法:` T selectOne`、` List selectList`等。 - 右侧蓝色部分是对数据库配置的开启session的工厂处理类,这里的工厂会操作`DefaultSqlSession` -- 之后是红色地方的`SqlSessionFactoryBuilder`,这个类是对数据库操作的核心类;处理工厂、解析文件、拿到session等。 +- 之后是红色地方的`SqlSessionFactoryBuilder`,这个类是对数据库操作的核心类:处理工厂、解析文件、拿到session等。 接下来我们就分别介绍各个类的功能实现过程。 @@ -431,11 +431,11 @@ public class SqlSessionFactoryBuilder { **parseConfiguration(解析配置)** -是对xml中的元素进行获取,这里主要获取了;`dataSource`、`mappers`,而这两个配置一个是我们数据库的链接信息,另外一个是对数据库操作语句的解析。 +是对xml中的元素进行获取,这里主要获取了:`dataSource`、`mappers`,而这两个配置一个是我们数据库的链接信息,另外一个是对数据库操作语句的解析。 **connection(Map dataSource) (链接数据库)** -链接数据库的地方和我们常见的方式是一样的;`Class.forName(dataSource.get("driver"));`,但是这样包装以后外部是不需要知道具体的操作。同时当我们需要链接多套数据库的时候,也是可以在这里扩展。 +链接数据库的地方和我们常见的方式是一样的:`Class.forName(dataSource.get("driver"));`,但是这样包装以后外部是不需要知道具体的操作。同时当我们需要链接多套数据库的时候,也是可以在这里扩展。 **mapperElement (解析sql语句)** @@ -602,7 +602,7 @@ public void test_queryUserInfoById() { } ``` -- 这里的使用方式和`Mybatis`是一样的,都包括了;资源加载和解析、`SqlSession`工厂构建、开启`SqlSession`以及最后执行查询操作`selectOne` +- 这里的使用方式和`Mybatis`是一样的,都包括了:资源加载和解析、`SqlSession`工厂构建、开启`SqlSession`以及最后执行查询操作`selectOne` **测试结果** @@ -650,10 +650,10 @@ public void test_queryUserList() { Process finished with exit code 0 ``` -- 测试验证集合的结果也是正常的,目前位置测试全部通过。 +- 测试验证集合的结果也是正常的,目前为止测试全部通过。 ## 七、总结 - 以上通过中介者模式的设计思想我们手写了一个ORM框架,隐去了对数据库操作的复杂度,让外部的调用方可以非常简单的进行操作数据库。这也是我们平常使用的`Mybatis`的原型,在我们日常的开发使用中,只需要按照配置即可非常简单的操作数据库。 - 除了以上这种组件模式的开发外,还有服务接口的包装也可以使用中介者模式来实现。比如你们公司有很多的奖品接口需要在营销活动中对接,那么可以把这些奖品接口统一收到中台开发一个奖品中心,对外提供服务。这样就不需要每一个需要对接奖品的接口,都去找具体的提供者,而是找中台服务即可。 -- 在上述的实现和测试使用中可以看到,这种模式的设计满足了;`单一职责`和`开闭原则`,也就符合了`迪米特原则`,即越少人知道越好。外部的人只需要按照需求进行调用,不需要知道具体的是如何实现的,复杂的一面已经有组件合作服务平台处理。 +- 在上述的实现和测试使用中可以看到,这种模式的设计满足了:`单一职责`和`开闭原则`,也就符合了`迪米特原则`,即越少人知道越好。外部的人只需要按照需求进行调用,不需要知道具体的是如何实现的,复杂的一面已经有组件合作服务平台处理。 diff --git "a/docs/md/develop/design-pattern/2020-06-28-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\244\207\345\277\230\345\275\225\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-28-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\244\207\345\277\230\345\275\225\346\250\241\345\274\217\343\200\213.md" index 1f2efab68..ee0ba1348 100755 --- "a/docs/md/develop/design-pattern/2020-06-28-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\244\207\345\277\230\345\275\225\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-28-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\345\244\207\345\277\230\345\275\225\346\250\241\345\274\217\343\200\213.md" @@ -29,7 +29,7 @@ lock: need `临阵的你好像一直很着急!` -经常的听到;`老师明天就要了你帮我弄弄吧`、`你给我写一下完事我就学这次着急`、`现在这不是没时间学吗快给我看看`。其实看到的类似的还有很多,很纳闷你的着急怎么来的,不太可能,人在家中坐,祸从天上落。老师怎么就那个时间找你了,老板怎么就今天管你要了,还不是日积月累你没有学习,临时抱佛脚乱着急!即使后来真的有人帮你了,但最好不要放松,要尽快学会,躲得过初一还有初二呢! +经常的听到:`老师明天就要了你帮我弄弄吧`、`你给我写一下完事我就学这次着急`、`现在这不是没时间学吗快给我看看`。其实看到的类似的还有很多,很纳闷你的着急怎么来的,不太可能,人在家中坐,祸从天上落。老师怎么就那个时间找你了,老板怎么就今天管你要了,还不是日积月累你没有学习,临时抱佛脚乱着急!即使后来真的有人帮你了,但最好不要放松,要尽快学会,躲得过初一还有初二呢! ## 二、开发环境 @@ -59,7 +59,7 @@ lock: need **在本案例中我们模拟系统在发布上线的过程中记录线上配置文件用于紧急回滚** -在大型互联网公司系统的发布上线一定是易用、安全、可处理紧急状况的,同时为了可以隔离线上和本地环境,一般会把配置文件抽取出来放到线上,避免有人误操作导致本地的配置内容发布出去。同时线上的配置文件也会在每次变更的时候进行记录,包括;版本号、时间、MD5、内容信息和操作人。 +在大型互联网公司系统的发布上线一定是易用、安全、可处理紧急状况的,同时为了可以隔离线上和本地环境,一般会把配置文件抽取出来放到线上,避免有人误操作导致本地的配置内容发布出去。同时线上的配置文件也会在每次变更的时候进行记录,包括:版本号、时间、MD5、内容信息和操作人。 在后续上线时如果发现紧急问题,系统就会需要回滚操作,如果执行回滚那么也可以设置配置文件是否回滚。因为每一个版本的系统可能会随着带着一些配置文件的信息,这个时候就可以很方便的让系统与配置文件一起回滚操作。 @@ -261,5 +261,5 @@ Process finished with exit code 0 ## 六、总结 - 此种设计模式的方式可以满足在不破坏原有属性类的基础上,扩充了备忘录的功能。虽然和我们平时使用的思路是一样的,但在具体实现上还可以细细品味,这样的方式在一些源码中也有所体现。 -- 在以上的实现中我们是将配置模拟存放到内存中,如果关机了会导致配置信息丢失,因为在一些真实的场景里还是需要存放到数据库中。那么此种存放到内存中进行回复的场景也不是没有,比如;Photoshop、运营人员操作ERP配置活动,那么也就是即时性的一般不需要存放到库中进行恢复。另外如果是使用内存方式存放备忘录,需要考虑存储问题,避免造成内存大量消耗。 -- 设计模式的学习都是为了更好的写出可扩展、可管理、易维护的代码,而这个学习的过程需要自己不断的尝试实际操作,理论的知识与实际结合还有很长一段距离。切记多多上手! \ No newline at end of file +- 在以上的实现中我们是将配置模拟存放到内存中,如果关机了会导致配置信息丢失,因为在一些真实的场景里还是需要存放到数据库中。那么此种存放到内存中进行恢复的场景也不是没有,比如;Photoshop、运营人员操作ERP配置活动,那么也就是即时性的一般不需要存放到库中进行恢复。另外如果是使用内存方式存放备忘录,需要考虑存储问题,避免造成内存大量消耗。 +- 设计模式的学习都是为了更好的写出可扩展、可管理、易维护的代码,而这个学习的过程需要自己不断的尝试实际操作,理论的知识与实际结合还有很长一段距离。切记多多上手! diff --git "a/docs/md/develop/design-pattern/2020-06-30-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\247\202\345\257\237\350\200\205\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-06-30-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\247\202\345\257\237\350\200\205\346\250\241\345\274\217\343\200\213.md" index 48c1686a7..2db1d6e46 100755 --- "a/docs/md/develop/design-pattern/2020-06-30-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\247\202\345\257\237\350\200\205\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-06-30-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\247\202\345\257\237\350\200\205\346\250\241\345\274\217\343\200\213.md" @@ -121,7 +121,7 @@ itstack-demo-design-18-01 └── LotteryServiceImpl.java ``` -- 这段代码接口中包括了三部分内容;返回对象(`LotteryResult`)、定义接口(`LotteryService`)、具体实现(`LotteryServiceImpl`)。 +- 这段代码接口中包括了三部分内容:返回对象(`LotteryResult`)、定义接口(`LotteryService`)、具体实现(`LotteryServiceImpl`)。 ### 2. 代码实现 @@ -146,7 +146,7 @@ public class LotteryServiceImpl implements LotteryService { } ``` -- 从以上的方法实现中可以看到,整体过程包括三部分;摇号、发短信、发MQ消息,而这部分都是顺序调用的。 +- 从以上的方法实现中可以看到,整体过程包括三部分:摇号、发短信、发MQ消息,而这部分都是顺序调用的。 - 除了`摇号`接口调用外,后面的两部分都是非核心主链路功能,而且会随着后续的业务需求发展而不断的调整和扩充,在这样的开发方式下就非常不利于维护。 ### 3. 测试验证 @@ -203,8 +203,8 @@ itstack-demo-design-18-02 ![观察者模式模型结构](https://bugstack.cn/assets/images/2020/itstack-demo-design-18-04.png) -- 从上图可以分为三大块看;`事件监听`、`事件处理`、`具体的业务流程`,另外在业务流程中 `LotteryService` 定义的是抽象类,因为这样可以通过抽象类将事件功能屏蔽,外部业务流程开发者不需要知道具体的通知操作。 -- 右下角圆圈图表示的是核心流程与非核心流程的结构,一般在开发中会把主线流程开发完成后,再使用通知的方式处理辅助流程。他们可以是异步的,在MQ以及定时任务的处理下,保证最终一致性。 +- 从上图可以分为三大块看:`事件监听`、`事件处理`、`具体的业务流程`,另外在业务流程中 `LotteryService` 定义的是抽象类,因为这样可以通过抽象类将事件功能屏蔽,外部业务流程开发者不需要知道具体的通知操作。 +- 右下角圆圈图表示的是核心流程与非核心流程的结构,一般在开发中会把主线流程开发完成后,再使用通知的方式处理辅助流程。它们可以是异步的,在MQ以及定时任务的处理下,保证最终一致性。 ### 2. 代码实现 @@ -307,7 +307,7 @@ public class EventManager { } ``` -- 整个处理的实现上提供了三个主要方法;订阅(`subscribe`)、取消订阅(`unsubscribe`)、通知(`notify`)。这三个方法分别用于对监听时间的添加和使用。 +- 整个处理的实现上提供了三个主要方法:订阅(`subscribe`)、取消订阅(`unsubscribe`)、通知(`notify`)。这三个方法分别用于对监听事件的添加和使用。 - 另外因为事件有不同的类型,这里使用了枚举的方式进行处理,也方便让外部在规定下使用事件,而不至于乱传信息(`EventType.MQ`、`EventType.Message`)。 #### 2.4 业务抽象类接口 @@ -391,7 +391,7 @@ Process finished with exit code 0 ## 七、总结 -- 从我们最基本的过程式开发以及后来使用观察者模式面向对象开发,可以看到设计模式改造后,拆分出了核心流程与辅助流程的代码。一般代码中的核心流程不会经常变化。但辅助流程会随着业务的各种变化而变化,包括;`营销`、`裂变`、`促活`等等,因此使用设计模式架设代码就显得非常有必要。 +- 从我们最基本的过程式开发以及后来使用观察者模式面向对象开发,可以看到设计模式改造后,拆分出了核心流程与辅助流程的代码。一般代码中的核心流程不会经常变化。但辅助流程会随着业务的各种变化而变化,包括:`营销`、`裂变`、`促活`等等,因此使用设计模式架设代码就显得非常有必要。 - 此种设计模式从结构上是满足开闭原则的,当你需要新增其他的监听事件或者修改监听逻辑,是不需要改动事件处理类的。但是可能你不能控制调用顺序以及需要做一些事件结果的返回继续操作,所以使用的过程时需要考虑场景的合理性。 - 任何一种设计模式有时候都不是单独使用的,需要结合其他模式共同建设。另外设计模式的使用是为了让代码更加易于扩展和维护,不能因为添加设计模式而把结构处理更加复杂以及难以维护。这样的合理使用的经验需要大量的实际操作练习而来。 diff --git "a/docs/md/develop/design-pattern/2020-07-02-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\212\266\346\200\201\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-07-02-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\212\266\346\200\201\346\250\241\345\274\217\343\200\213.md" index 3584da8a8..fec14285b 100755 --- "a/docs/md/develop/design-pattern/2020-07-02-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\212\266\346\200\201\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-07-02-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\212\266\346\200\201\346\250\241\345\274\217\343\200\213.md" @@ -61,7 +61,7 @@ lock: need **在本案例中我们模拟营销活动审核状态流转场景(一个活动的上线是多个层级审核上线的)** -在上图中也可以看到我们的流程节点中包括了各个状态到下一个状态扭转的关联条件,比如;审核通过才能到活动中,而不能从编辑中直接到活动中,而这些状态的转变就是我们要完成的场景处理。 +在上图中也可以看到我们的流程节点中包括了各个状态到下一个状态扭转的关联条件,比如:审核通过才能到活动中,而不能从编辑中直接到活动中,而这些状态的转变就是我们要完成的场景处理。 大部分程序员基本都开发过类似的业务场景,需要对活动或者一些配置需要审核后才能对外发布,而这个审核的过程往往会随着系统的重要程度而设立多级控制,来保证一个活动可以安全上线,避免造成资损。 @@ -80,7 +80,7 @@ itstack-demo-design-19-00 └── ActivityService.java ``` -- 在这个模拟工程里我们提供了三个类,包括;状态枚举(`Status`)、活动对象(`ActivityInfo`)、活动服务(`ActivityService`),三个服务类。 +- 在这个模拟工程里我们提供了三个类,包括:状态枚举(`Status`)、活动对象(`ActivityInfo`)、活动服务(`ActivityService`),三个服务类。 - 接下来我们就分别介绍三个类包括的内容。 ### 2. 代码实现 @@ -113,7 +113,7 @@ public enum Status { } ``` -- 活动的枚举;1创建编辑、2待审核、3审核通过(任务扫描成活动中)、4审核拒绝(可以撤审到编辑状态)、5活动中、6活动关闭、7活动开启(任务扫描成活动中) +- 活动的枚举:1创建编辑、2待审核、3审核通过(任务扫描成活动中)、4审核拒绝(可以撤审到编辑状态)、5活动中、6活动关闭、7活动开启(任务扫描成活动中) #### 2.3 活动服务接口 @@ -196,7 +196,7 @@ itstack-demo-design-19-01 └── Result.java ``` -- 整个实现的工程结构比较简单,只包括了两个类;`ActivityExecStatusController`、`Result`,一个是处理流程状态,另外一个是返回的对象。 +- 整个实现的工程结构比较简单,只包括了两个类:`ActivityExecStatusController`、`Result`,一个是处理流程状态,另外一个是返回的对象。 ### 2. 代码实现 @@ -671,7 +671,7 @@ public void test_Refuse2Revoke() { Process finished with exit code 0 ``` -- 测试测试结果(拒绝To撤审),的状态流转。 +- 测试拒绝To撤审,的状态流转。 - **综上**以上四个测试类分别模拟了不同状态之间的`有效流转`和`拒绝流转`,不同的状态服务处理不同的服务内容。 diff --git "a/docs/md/develop/design-pattern/2020-07-05-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\255\226\347\225\245\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-07-05-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\255\226\347\225\245\346\250\241\345\274\217\343\200\213.md" index 685c8ca60..c7518342b 100644 --- "a/docs/md/develop/design-pattern/2020-07-05-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\255\226\347\225\245\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-07-05-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\347\255\226\347\225\245\346\250\241\345\274\217\343\200\213.md" @@ -29,7 +29,7 @@ lock: need `🙉提升自己的眼界交往更多同好` -有时候圈子很重要,就像上学期间大家都会发现班里有这样一类学生👩‍🎓不怎么听课,但是就是学习好。那假如让他回家呆着,不能在课堂里呢?类似的圈子还有;图书馆、网吧、车友群、技术群等等,都可以给你带来同类爱好的人所分享出来的技能或者大家一起烘托出的氛围帮你成长。 +有时候圈子很重要,就像上学期间大家都会发现班里有这样一类学生👩‍🎓不怎么听课,但是就是学习好。那假如让他回家呆着,不能在课堂里呢?类似的圈子还有:图书馆、网吧、车友群、技术群等等,都可以给你带来同类爱好的人所分享出来的技能或者大家一起烘托出的氛围帮你成长。 ## 二、开发环境 @@ -48,11 +48,11 @@ lock: need - 图片来自:[https://refactoringguru.cn/design-patterns/strategy](https://refactoringguru.cn/design-patterns/strategy) -策略模式是一种行为模式,也是替代大量`ifelse`的利器。它所能帮你解决的是场景,一般是具有同类可替代的行为逻辑算法场景。比如;不同类型的交易方式(信用卡、支付宝、微信)、生成唯一ID策略(UUID、DB自增、DB+Redis、雪花算法、Leaf算法)等,都可以使用策略模式进行行为包装,供给外部使用。 +策略模式是一种行为模式,也是替代大量`ifelse`的利器。它所能帮你解决的是场景,一般是具有同类可替代的行为逻辑算法场景。比如:不同类型的交易方式(信用卡、支付宝、微信)、生成唯一ID策略(UUID、DB自增、DB+Redis、雪花算法、Leaf算法)等,都可以使用策略模式进行行为包装,供给外部使用。 ![诸葛亮锦囊妙计](https://bugstack.cn/assets/images/2020/itstack-demo-design-20-02.png) -策略模式也有点像三国演义中诸葛亮给刘关张的锦囊; +策略模式也有点像三国演义中诸葛亮给刘关张的锦囊: - 第一个锦囊:见乔国老,并把刘备娶亲的事情搞得东吴人尽皆知。 - 第二个锦囊:用谎言(曹操打荆州)骗泡在温柔乡里的刘备回去。 - 第三个锦囊:让孙夫人摆平东吴的追兵,她是孙权妹妹,东吴将领惧她三分。 @@ -383,6 +383,6 @@ Process finished with exit code 0 ## 七、总结 -- 以上的策略模式案例相对来说不并不复杂,主要的逻辑都是体现在关于不同种类优惠券的计算折扣策略上。结构相对来说也比较简单,在实际的开发中这样的设计模式也是非常常用的。另外这样的设计与命令模式、适配器模式结构相似,但是思路是有差异的。 +- 以上的策略模式案例相对来说并不复杂,主要的逻辑都是体现在关于不同种类优惠券的计算折扣策略上。结构相对来说也比较简单,在实际的开发中这样的设计模式也是非常常用的。另外这样的设计与命令模式、适配器模式结构相似,但是思路是有差异的。 - 通过策略设计模式的使用可以把我们方法中的if语句优化掉,大量的if语句使用会让代码难以扩展,也不好维护,同时在后期遇到各种问题也很难维护。在使用这样的设计模式后可以很好的满足隔离性与和扩展性,对于不断新增的需求也非常方便承接。 - `策略模式`、`适配器模式`、`组合模式`等,在一些结构上是比较相似的,但是每一个模式是有自己的逻辑特点,在使用的过程中最佳的方式是经过较多的实践来吸取经验,为后续的研发设计提供更好的技术输出。 diff --git "a/docs/md/develop/design-pattern/2020-07-07-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\346\250\241\346\235\277\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-07-07-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\346\250\241\346\235\277\346\250\241\345\274\217\343\200\213.md" index 22228b832..8f85af5c1 100755 --- "a/docs/md/develop/design-pattern/2020-07-07-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\346\250\241\346\235\277\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-07-07-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\346\250\241\346\235\277\346\250\241\345\274\217\343\200\213.md" @@ -1,14 +1,14 @@ --- layout: post category: itstack-demo-design -title: 重学 Java 设计模式:实战模版模式「模拟爬虫各类电商商品,生成营销推广海报场景」 +title: 重学 Java 设计模式:实战模板模式「模拟爬虫各类电商商品,生成营销推广海报场景」 tagline: by 小傅哥 tag: [itstack-demo-design] excerpt: 键盘侠⌨、网络喷壶🤩,不要被哪些根本不能让你成长的人影响你奋斗的方向。黎明前的坚守是最后的冲锋,岁月不会辜负任何一个努力拼搏的人。也许有一天你会万分感谢自己以前自己坚持下来的决定,没有任何人知道你能行。 lock: need --- -# 重学 Java 设计模式:实战模版模式「模拟爬虫各类电商商品,生成营销推广海报场景」 +# 重学 Java 设计模式:实战模板模式「模拟爬虫各类电商商品,生成营销推广海报场景」 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) @@ -25,7 +25,7 @@ lock: need `不要轻易被洗脑` -`键盘侠⌨`、`网络喷壶`,几乎当你努力坚持一件事的时候,在这条路上会遇到形形色色的人和事。有时候接收建议完善自己是有必要的,但不能放弃自己的初心和底线,有时候只坚持自己也是难能可贵的。`子路之勇,子贡之辩,冉有之智,此三子者,皆天下之所谓难能而可贵者也`。阳光和努力是这个世界最温暖的东西,加油坚持好自己的选的路。 +`键盘侠⌨`、`网络喷壶`,几乎当你努力坚持一件事的时候,在这条路上会遇到形形色色的人和事。有时候接收建议完善自己是有必要的,但不能放弃自己的初心和底线,有时候只坚持自己也是难能可贵的。`子路之勇,子贡之辩,冉有之智,此三子者,皆天下之所谓难能而可贵者也`。阳光和努力是这个世界最温暖的东西,加油坚持好自己选的路。 `有时还好坚持了` @@ -41,9 +41,9 @@ lock: need | ------------------------ | --------------------------------- | | itstack-demo-design-21-00 | 场景模拟工程;模拟爬虫商品生成海报场景 | -## 三、模版模式介绍 +## 三、模板模式介绍 -![模版模式,图片来自 refactoringguru.cn](https://bugstack.cn/assets/images/2020/itstack-demo-design-21-01.png) +![模板模式,图片来自 refactoringguru.cn](https://bugstack.cn/assets/images/2020/itstack-demo-design-21-01.png) - 图片来自:[https://refactoringguru.cn/design-patterns/template-method](https://refactoringguru.cn/design-patterns/template-method) @@ -59,22 +59,22 @@ lock: need **在本案例中我们模拟爬虫各类电商商品,生成营销推广海报场景** -关于模版模式的核心点在于由抽象类定义抽象方法执行策略,也就是说父类规定了好一系列的执行标准,这些标准的串联成一整套业务流程。 +关于模板模式的核心点在于由抽象类定义抽象方法执行策略,也就是说父类规定好了一系列的执行标准,这些标准串联成一整套业务流程。 在这个场景中我们模拟爬虫爬取各类商家的商品信息,生成推广海报(`海报中含带个人的邀请码`)赚取商品返利。*声明,这里是模拟爬取,并没有真的爬取* -而整个的爬取过程分为;模拟登录、爬取信息、生成海报,这三个步骤,另外; -1. 因为有些商品只有登录后才可以爬取,并且登录可以看到一些特定的价格这与未登录用户看到的价格不同。 +而整个的爬取过程分为:模拟登录、爬取信息、生成海报,这三个步骤,另外: +1. 因为有些商品只有登录后才可以爬取,并且登录可以看到一些特定的价格,这与未登录用户看到的价格不同。 2. 不同的电商网站爬取方式不同,解析方式也不同,因此可以作为每一个实现类中的特定实现。 -3. 生成海报的步骤基本一样,但会有特定的商品来源标识。所以这样三个步骤可以使用模版模式来设定,并有具体的场景做子类实现。 +3. 生成海报的步骤基本一样,但会有特定的商品来源标识。所以这样三个步骤可以使用模板模式来设定,并有具体的场景做子类实现。 -## 五、模版模式搭建工程 +## 五、模板模式搭建工程 -模版模式的业务场景可能在平时的开发中并不是很多,主要因为这个设计模式会在抽象类中定义逻辑行为的执行顺序。一般情况下,我们用的抽象类定义的逻辑行为都比较轻量级或者没有,只是提供一些基本方法公共调用和实现。 +模板模式的业务场景可能在平时的开发中并不是很多,主要因为这个设计模式会在抽象类中定义逻辑行为的执行顺序。一般情况下,我们用的抽象类定义的逻辑行为都比较轻量级或者没有,只是提供一些基本方法公共调用和实现。 -但如果遇到适合的场景使用这样的设计模式也是非常方便的,因为他可以控制整套逻辑的执行顺序和统一的输入、输出,而对于实现方只需要关心好自己的业务逻辑即可。 +但如果遇到适合的场景使用这样的设计模式也是非常方便的,因为它可以控制整套逻辑的执行顺序和统一的输入、输出,而对于实现方只需要关心好自己的业务逻辑即可。 -而在我们这个场景中,只需要记住这三步的实现即可;`模拟登录`、`爬取信息`、`生成海报` +而在我们这个场景中,只需要记住这三步的实现即可:`模拟登录`、`爬取信息`、`生成海报` ### 1. 工程结构 @@ -96,9 +96,9 @@ itstack-demo-design-21-00 └── ApiTest.java ``` -**模版模式模型结构** +**模板模式模型结构** -![模版模式模型结构](https://bugstack.cn/assets/images/2020/itstack-demo-design-21-04.png) +![模板模式模型结构](https://bugstack.cn/assets/images/2020/itstack-demo-design-21-04.png) - 以上的代码结构还是比较简单的,一个定义了抽象方法执行顺序的核心抽象类,以及三个模拟具体的实现(`京东`、`淘宝`、`当当`)的电商服务。 @@ -312,6 +312,6 @@ Process finished with exit code 0 ## 六、总结 -- 通过上面的实现可以看到`模版模式`在定义统一结构也就是执行标准上非常方便,也就很好的控制了后续的实现者不用关心调用逻辑,按照统一方式执行。那么类的继承者只需要关心具体的业务逻辑实现即可。 -- 另外模版模式也是为了解决子类通用方法,放到父类中设计的优化。让每一个子类只做子类需要完成的内容,而不需要关心其他逻辑。这样提取公用代码,行为由父类管理,扩展可变部分,也就非常有利于开发拓展和迭代。 -- 但每一种设计模式都有自己的特定场景,如果超过场景外的建设就需要额外考虑🤔其他模式的运用。而不是非要生搬硬套,否则自己不清楚为什么这么做,也很难让后续者继续维护代码。而想要活学活用就需要多加练习,有实践的经历。 \ No newline at end of file +- 通过上面的实现可以看到`模板模式`在定义统一结构也就是执行标准上非常方便,也就很好的控制了后续的实现者不用关心调用逻辑,按照统一方式执行。那么类的继承者只需要关心具体的业务逻辑实现即可。 +- 另外模板模式也是为了解决子类通用方法,放到父类中设计的优化。让每一个子类只做子类需要完成的内容,而不需要关心其他逻辑。这样提取公用代码,行为由父类管理,扩展可变部分,也就非常有利于开发拓展和迭代。 +- 但每一种设计模式都有自己的特定场景,如果超过场景外的建设就需要额外考虑🤔其他模式的运用。而不是非要生搬硬套,否则自己不清楚为什么这么做,也很难让后续者继续维护代码。而想要活学活用就需要多加练习,有实践的经历。 diff --git "a/docs/md/develop/design-pattern/2020-07-09-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\256\277\351\227\256\350\200\205\346\250\241\345\274\217\343\200\213.md" "b/docs/md/develop/design-pattern/2020-07-09-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\256\277\351\227\256\350\200\205\346\250\241\345\274\217\343\200\213.md" index 2cd7e4f74..d598be20e 100755 --- "a/docs/md/develop/design-pattern/2020-07-09-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\256\277\351\227\256\350\200\205\346\250\241\345\274\217\343\200\213.md" +++ "b/docs/md/develop/design-pattern/2020-07-09-\351\207\215\345\255\246 Java \350\256\276\350\256\241\346\250\241\345\274\217\343\200\212\345\256\236\346\210\230\350\256\277\351\227\256\350\200\205\346\250\241\345\274\217\343\200\213.md" @@ -29,7 +29,7 @@ lock: need `数学好才能学编码吗` -往往很多时候学编程的初学者都会问数学不好能学会吗?其实可以想想那为什么数学不好呢?在这条没学好的路上,你为它们付出了多少时间呢?如果一件事情你敢做到和写自己名字一样熟悉,还真的有难的东西吗。从大学到毕业能写出40万行代码的,还能愁找不到工作吗,日积月累,每一天并没有多难。难的你想用最后一个月的时间学完人家四年努力的成绩的。学习,要趁早。 +往往很多时候学编程的初学者都会问数学不好能学会吗?其实可以想想那为什么数学不好呢?在这条没学好的路上,你为它们付出了多少时间呢?如果一件事情你敢做到和写自己名字一样熟悉,还真的有难的东西吗。从大学到毕业能写出40万行代码的,还能愁找不到工作吗,日积月累,每一天并没有多难。难的是你想用最后一个月的时间学完人家四年努力的成绩的。学习,要趁早。 ## 二、开发环境 @@ -61,15 +61,15 @@ lock: need 这个案例场景我们模拟校园中有学生和老师两种身份的用户,那么对于家长和校长关心的角度来看,他们的视角是不同的。家长更关心孩子的成绩和老师的能力,校长更关心老师所在班级学生的人数和升学率{`此处模拟的`}。 -那么这样`学生`和`老师`就是一个固定信息的内容,而想让不同视角的用户获取关心的信息,就比较适合使用观察者模式来实现,从而让实体与业务解耦,增强扩展性。**但观察者模式的整体类结构相对复杂,需要梳理清楚再开发** +那么这样`学生`和`老师`就是一个固定信息的内容,而想让不同视角的用户获取关心的信息,就比较适合使用访问者模式来实现,从而让实体与业务解耦,增强扩展性。**但访问者模式的整体类结构相对复杂,需要梳理清楚再开发** ## 五、访问者模式搭建工程 -访问者模式的类结构相对其他设计模式来说比较复杂,但这样的设计模式在我看来更加`烧气有魅力`,它能阔开你对代码结构的新认知,用这样思维不断的建设出更好的代码架构。 +访问者模式的类结构相对其他设计模式来说比较复杂,但这样的设计模式在我看来更加`烧气有魅力`,它能开阔你对代码结构的新认知,用这样思维不断的建设出更好的代码架构。 -关于这个案例的核心逻辑实现,有以下几点; -1. 建立用户抽象类和抽象访问方法,再由不同的用户实现;老师和学生。 -2. 建立访问者接口,用于不同人员的访问操作;校长和家长。 +关于这个案例的核心逻辑实现,有以下几点: +1. 建立用户抽象类和抽象访问方法,再由不同的用户实现:老师和学生。 +2. 建立访问者接口,用于不同人员的访问操作:校长和家长。 3. 最终是对数据的看板建设,用于实现不同视角的访问结果输出。 ### 1. 工程结构 @@ -129,8 +129,8 @@ public abstract class User { } ``` -- 基础信息包括;姓名、身份、班级,也可以是一个业务用户属性类。 -- 定义抽象核心方法,`abstract void accept(Visitor visitor)`,这个方法是为了让后续的用户具体实现者都能提供出一个访问方法,共外部使用。 +- 基础信息包括:姓名、身份、班级,也可以是一个业务用户属性类。 +- 定义抽象核心方法,`abstract void accept(Visitor visitor)`,这个方法是为了让后续的用户具体实现者都能提供出一个访问方法,供外部使用。 #### 2.2 实现用户信息(老师和学生) @@ -194,11 +194,11 @@ public interface Visitor { ``` - 访问的接口比较简单,相同的方法名称,不同的入参用户类型。 -- 让具体的访问者类,在实现时可以关注每一种用户类型的具体访问数据对象,例如;升学率和排名。 +- 让具体的访问者类,在实现时可以关注每一种用户类型的具体访问数据对象,例如:升学率和排名。 #### 2.4 实现访问类型(校长和家长) -**访问者;校长** +**访问者:校长** ```java public class Principal implements Visitor { @@ -216,7 +216,7 @@ public class Principal implements Visitor { } ``` -**访问者;家长** +**访问者:家长** ```java public class Parent implements Visitor { @@ -235,8 +235,8 @@ public class Parent implements Visitor { ``` - 以上是两个具体的访问者实现类,他们都有自己的视角需求。 -- 校长关注;学生的名称和班级,老师对这个班级的升学率 -- 家长关注;自己家孩子的排名,老师的班级和教学水平 +- 校长关注:学生的名称和班级,老师对这个班级的升学率。 +- 家长关注:自己家孩子的排名,老师的班级和教学水平。 #### 2.5 数据看版 @@ -267,7 +267,7 @@ public class DataView { ``` - 首先在这个类中初始化了基本的数据,学生和老师的信息。 -- 并提供了一个展示类,通过传入不同的`观察者(校长、家长)`而差异化的打印信息。 +- 并提供了一个展示类,通过传入不同的`访问者(校长、家长)`而差异化的打印信息。 ### 3. 测试验证 @@ -316,12 +316,12 @@ Process finished with exit code 0 ``` - 通过测试结果可以看到,家长和校长的访问视角同步,数据也是差异化的。 -- 家长视角看到学生的排名;`排名:62`、`排名:51`、`排名:16`、`排名:98`。 -- 校长视角看到班级升学率;`升学率:70.62`、`升学率:23.15`、`升学率:70.98`、`升学率:90.14`。 +- 家长视角看到学生的排名:`排名:62`、`排名:51`、`排名:16`、`排名:98`。 +- 校长视角看到班级升学率:`升学率:70.62`、`升学率:23.15`、`升学率:70.98`、`升学率:90.14`。 - 通过这样的测试结果,可以看到访问者模式的初心和结果,在适合的场景运用合适的模式,非常有利于程序开发。 ## 六、总结 - 从以上的业务场景中可以看到,在嵌入访问者模式后,可以让整个工程结构变得容易添加和修改。也就做到了系统服务之间的解耦,不至于为了不同类型信息的访问而增加很多多余的`if`判断或者类的强制转换。也就是通过这样的设计模式而让代码结构更加清晰。 -- 另外在实现的过程可能你可能也发现了,定义抽象类的时候还需要等待访问者接口的定义,这样的设计首先从实现上会让代码的组织变得有些难度。另外从设计模式原则的角度来看,违背了迪米特原则,也就是最少知道原则。因此在使用上一定要符合场景的运用,以及提取这部分设计思想的精髓。 -- 好的学习方式才好更容易接受知识,学习编程的更需要的不单单是看,而是操作。二十多种设计模式每一种都有自己的设计技巧,也可以说是巧妙之处,这些巧妙的地方往往是解决复杂难题的最佳视角。亲力亲为,才能为所欲为,为了自己的欲望而努力! \ No newline at end of file +- 另外在实现的过程中你可能也发现了,定义抽象类的时候还需要等待访问者接口的定义,这样的设计首先从实现上会让代码的组织变得有些难度。另外从设计模式原则的角度来看,违背了迪米特原则,也就是最少知道原则。因此在使用上一定要符合场景的运用,以及提取这部分设计思想的精髓。 +- 好的学习方式才好更容易接受知识,学习编程的更需要的不单单是看,而是操作。二十多种设计模式每一种都有自己的设计技巧,也可以说是巧妙之处,这些巧妙的地方往往是解决复杂难题的最佳视角。亲力亲为,才能为所欲为,为了自己的欲望而努力! diff --git a/docs/md/develop/design-pattern/2024-08-25-chain-tree.md b/docs/md/develop/design-pattern/2024-08-25-chain-tree.md new file mode 100644 index 000000000..3560cd3b7 --- /dev/null +++ b/docs/md/develop/design-pattern/2024-08-25-chain-tree.md @@ -0,0 +1,380 @@ +--- +title: 干掉if...else,最好用的3种设计模式! +lock: need +--- + +# 干掉if...else,最好用的3种设计模式! + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + + + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家伙,我是技术UP主小傅哥。 + +在我们做Java业务代码开发时,经常会碰到大量的流程判断,`验证场景渠道`、`验证用户身份`、`验证会员级别`等等10来个甚至几十个分支节点。对于这样的编码80%的T2码农都是 if...else 编码。那除了贴膏药一样的写代码还有别的什么办法吗? + +
+ +
+ +**if...else 写代码有什么问题吗?** + +往近了看,if...else 写的代码交付的快,但工程腐化的也快。有点像买的米面粮油不区分,全倒入一个桶里。让后面的兄弟在迭代需求时,都想铲了重写。但实际铲的成本太高,所以就挑挑拣拣、复制粘贴。 + +往远了看,是个人发展。如果昨天做的事、今天做的事和明天做的事,都是一个事。反复的重复,没有脑力思考,只是提高手速。那面试、述职、分享的时候真没的讲,你总不能告诉面试官我就写 if...else 了,遇到问题查百度。那离毕业🎓也不远了! + +>接下来,小傅哥就给大家分享一个链路执行的设计模式,看看都有哪些方式处理。 + +## 一、场景问题 + +Java 开发,尤其是 Java 业务开发的,就离不开大量的流程分支处理。产品给的需求,也是在编码中调用一系列的接口做流程验证处理。而且需求是频繁变化的,这也就间接的导致了程序员的 if...else 代码也要跟着一次次调整,从原来的几十行编程几百几千行。这个过程中还有一些要去掉的、要根据流程类型选择切换的、要覆盖所有的之前的需求不能出问题的。所以腐化越来越严重,开发成本越来越高。 + +
+ +
+ +```java +一个接口、一个实现, +一个实现,代码一片。 +一片一片、又一片, +代码行数、两三千。 +``` + +这是我们在承接产品需求时候经验能看到的一种流程图,在没有太强的代码驾驭能力时,很多伙伴的都只能写 if...else 一个个分支节点判断,所有的代码都平铺到一个类中。如果注意到你会发现,不具有设计模式能力的程序员,代码是没有立体化的。都是扁平平铺下来的。 + +那么对于这样的场景,我们完全可以通过设计模式的知识进行分治和抽象,这也是软件设计的第一原则,康威定律所倡导的。通过设计模式解耦流程,让编码的呈现出立体化,通过类来划分职责和执行过程。 + +## 二、设计模式 - 链&树 + +对于大量的有衔接关系的 if...else 判断流程,有两种设计手段对应这3种编码方式。包括;责任链和规则树。 + +
+ +
+ +- 责任链是一条单向执行链,没有过程中的分支流转,适合简单的单一规则校验。 +- 规则树是一条多向执行链,可以在过程中根据不同的分支判断走到对应的节点。而规则树的实现可以有两种方式,一种是通过数据库配置决定要加载的节点和执行的过程。另外一种是类似于责任链方式,让每个类自己根据业务条件决定下一个执行链路,这个设计巧妙的结合了责任链的思路,非常有意思。 + +>接下来,小傅哥就分别举例下这样的代码实现。程序员还是看代码,学习起来更有感觉。 + +## 三、编码实现 + +### 1. 责任链 + +#### 1.1 业务场景 + +抽奖规则过滤场景,分为黑名单用户、权重抽奖和默认抽奖,三个节点。如果一个用户是黑名单范围用户,则直接返回兜底奖品。而权重用户是一个用户已完成了N次抽奖后,在权重范围内可以获得一个固定的奖品。最后是兜底抽奖,这两个条件都不是,则进行默认兜底流程。 + +#### 1.2 设计类图 + +
+ +
+ +- 首先,定义出责任链装配接口和责任链逻辑接口,之后由抽象类实现接口,做链的封装实现。 +- 之后,实现3个责任链实现类。黑名单、权重、兜底。处理各自的逻辑。 +- 最后,由工厂装配责任链。后续可以按需扩展需要的责任链。这样业务流程就可以动态的拼装了。 + +#### 1.3 核心代码 + +
+ +
+ +```java +public abstract class AbstractLogicChain implements ILogicChain{ + + private ILogicChain next; + + @Override + public ILogicChain next() { + return next; + } + + @Override + public ILogicChain appendNext(ILogicChain next) { + this.next = next; + return next; + } + + protected abstract String ruleModel(); + +} + +@Slf4j +@Component("rule_default") +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class DefaultLogicChain extends AbstractLogicChain { + + @Resource + protected IStrategyDispatch strategyDispatch; + + @Override + public DefaultChainFactory.StrategyAwardVO logic(String userId, Long strategyId) { + Integer awardId = strategyDispatch.getRandomAwardId(strategyId); + log.info("抽奖责任链-默认处理 userId:{} strategyId:{} ruleModel:{} awardId:{}", userId, strategyId, ruleModel(), awardId); + return DefaultChainFactory.StrategyAwardVO.builder() + .awardId(awardId) + .logicModel(ruleModel()) + .build(); + } + + @Override + protected String ruleModel() { + return DefaultChainFactory.LogicModel.RULE_DEFAULT.getCode(); + } + +} + +public ILogicChain openLogicChain(Long strategyId) { + ILogicChain cacheLogicChain = strategyChainGroup.get(strategyId); + if (null != cacheLogicChain) return cacheLogicChain; + StrategyEntity strategy = repository.queryStrategyEntityByStrategyId(strategyId); + String[] ruleModels = strategy.ruleModels(); + // 如果未配置策略规则,则只装填一个默认责任链 + if (null == ruleModels || 0 == ruleModels.length) { + ILogicChain ruleDefaultLogicChain = applicationContext.getBean(LogicModel.RULE_DEFAULT.getCode(), ILogicChain.class); + // 写入缓存 + strategyChainGroup.put(strategyId, ruleDefaultLogicChain); + return ruleDefaultLogicChain; + } + // 按照配置顺序装填用户配置的责任链;rule_blacklist、rule_weight 「注意此数据从Redis缓存中获取,如果更新库表,记得在测试阶段手动处理缓存」 + ILogicChain logicChain = applicationContext.getBean(ruleModels[0], ILogicChain.class); + ILogicChain current = logicChain; + for (int i = 1; i < ruleModels.length; i++) { + ILogicChain nextChain = applicationContext.getBean(ruleModels[i], ILogicChain.class); + current = current.appendNext(nextChain); + } + // 责任链的最后装填默认责任链 + current.appendNext(applicationContext.getBean(LogicModel.RULE_DEFAULT.getCode(), ILogicChain.class)); + // 写入缓存 + strategyChainGroup.put(strategyId, logicChain); + return logicChain; +} +``` + +- 抽象类实现接口责任链的装配逻辑接口,并定义责任链链路衔接的方法。 +- DefaultLogicChain 是其中一个链的实现,同类的也都是做自己的逻辑。 +- openLogicChain 是工厂方法,用于组装责任链,提供服务。 + +### 2. 规则树 - 动态配置 + +#### 2.1 业务场景 + +当我们在实现业务流程编码时看到有些流程是带有判断和分支走向的,那么就不太适合用单一的责任链处理。比如一个流程中需要对抽奖的奖品进行交叉判断,抽中后判断是否满足中奖条件,满足后走库存处理,不满足走兜底处理。另外库存不足则也要走兜底处理。那么这样就是一个分叉的流程了。可以使用规则树进行实现。 + +#### 2.2 设计类图 + +
+ +
+ +
+ +
+ +- 首先,定义出规则树接口,并实现出对应的业务逻辑节点。包括;次数锁、库存扣减、兜底奖品。—— 次数锁判断为抽奖时,必须抽奖N次才可以获得某个奖品。 +- 之后,设计执行器,负责完成规则节点的执行分支,如从A到B,如果B的条件满足XXX,则走到另外一个节点。而执行器中的节点来自于数据库的配置,这样就可以动态的调整各个节点的走向了。 +- 最后,交给规则树工厂,完成执行器的服务提供。 + +#### 2.3 核心代码 + +
+ +
+ +```java +public interface ILogicTreeNode { + + DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId, String ruleValue, Date endDateTime); + +} + +@Component("rule_lock") +public class RuleLockLogicTreeNode implements ILogicTreeNode { + + @Resource + private IStrategyRepository repository; + + @Override + public DefaultTreeFactory.TreeActionEntity logic(String userId, Long strategyId, Integer awardId, String ruleValue, Date endDateTime) { + log.info("规则过滤-次数锁 userId:{} strategyId:{} awardId:{}", userId, strategyId, awardId); + + long raffleCount = 0L; + try { + raffleCount = Long.parseLong(ruleValue); + } catch (Exception e) { + throw new RuntimeException("规则过滤-次数锁异常 ruleValue: " + ruleValue + " 配置不正确"); + } + + // 查询用户抽奖次数 - 当天的;策略ID:活动ID 1:1 的配置,可以直接用 strategyId 查询。 + Integer userRaffleCount = repository.queryTodayUserRaffleCount(userId, strategyId); + + // 用户抽奖次数大于规则限定值,规则放行 + if (userRaffleCount >= raffleCount) { + log.info("规则过滤-次数锁【放行】 userId:{} strategyId:{} awardId:{} raffleCount:{} userRaffleCount:{}", userId, strategyId, awardId, userRaffleCount, userRaffleCount); + return DefaultTreeFactory.TreeActionEntity.builder() + .ruleLogicCheckType(RuleLogicCheckTypeVO.ALLOW) + .build(); + } + + log.info("规则过滤-次数锁【拦截】 userId:{} strategyId:{} awardId:{} raffleCount:{} userRaffleCount:{}", userId, strategyId, awardId, userRaffleCount, userRaffleCount); + + // 用户抽奖次数小于规则限定值,规则拦截 + return DefaultTreeFactory.TreeActionEntity.builder() + .ruleLogicCheckType(RuleLogicCheckTypeVO.TAKE_OVER) + .build(); + } + +} + +@Override +public DefaultTreeFactory.StrategyAwardVO process(String userId, Long strategyId, Integer awardId, Date endDateTime) { + DefaultTreeFactory.StrategyAwardVO strategyAwardData = null; + // 获取基础信息 + String nextNode = ruleTreeVO.getTreeRootRuleNode(); + Map treeNodeMap = ruleTreeVO.getTreeNodeMap(); + // 获取起始节点「根节点记录了第一个要执行的规则」 + RuleTreeNodeVO ruleTreeNode = treeNodeMap.get(nextNode); + while (null != nextNode) { + // 获取决策节点 + ILogicTreeNode logicTreeNode = logicTreeNodeGroup.get(ruleTreeNode.getRuleKey()); + String ruleValue = ruleTreeNode.getRuleValue(); + // 决策节点计算 + DefaultTreeFactory.TreeActionEntity logicEntity = logicTreeNode.logic(userId, strategyId, awardId, ruleValue, endDateTime); + RuleLogicCheckTypeVO ruleLogicCheckTypeVO = logicEntity.getRuleLogicCheckType(); + strategyAwardData = logicEntity.getStrategyAwardVO(); + log.info("决策树引擎【{}】treeId:{} node:{} code:{}", ruleTreeVO.getTreeName(), ruleTreeVO.getTreeId(), nextNode, ruleLogicCheckTypeVO.getCode()); + // 获取下个节点 + nextNode = nextNode(ruleLogicCheckTypeVO.getCode(), ruleTreeNode.getTreeNodeLineVOList()); + ruleTreeNode = treeNodeMap.get(nextNode); + } + // 返回最终结果 + return strategyAwardData; +} +``` + +- 定义规则树的接口,并根据业务实现相应的业务逻辑节点。举例中是规则过滤-次数锁节点实现,用于的抽奖次数大于限定才会发放奖品,否则就会流转到下一个节点。 +- 节点的调用在 DecisionTreeEngine#process 方法中执行,它会从数据库获取数据执行节点链路。 + +### 3. 规则树 - 代码控制 + +#### 3.1 业务场景 + +在我们的业务场景中,有时候既不是走责任链,也不是走配置到库上的规则树,而是介于两者直接。由代码控制的节点走向,根据每个节点实现逻辑,动态处理下一个节点的实现。 + +如,一个流程中进入总人口,之后判断是否开量、账户数据、之后从账户数据开始又有3个级别判断。这3级别是根据账户数据的结果判断的。 + +最后,这里还要有一个上下文数据记录,所有的节点完成后填充数据。 + +#### 3.2 设计类图 + +
+ +
+ +- 首先,定义2个接口,一个是策略的执行接口 StrategyHandler,这个接口除了手里逻辑执行外,还要做一个兜底的上线文参数填充方法,也就是接口的默认方法。一个是策略映射接口 StrategyMapper。映射接口的作用是为了让每个节点实现类,可以动态的控制当前节点走到下一个节点的逻辑处理。 +- 之后,按照业务诉求实现各个节点,每个节点都是继承抽象类(定义通用方法,和受理执行下一个节点的操作)。这些节点自己决定下一个节点走到哪里。 + +#### 3.3 核心代码 + +
+ +
+ +```java +public interface StrategyMapper { + + /** + * 获取策略处理器 + */ + StrategyHandler get(DefaultStrategyFactory.MaterialVO materialVO, DefaultStrategyFactory.DynamicContext dynamicContext); + +} + +public interface StrategyHandler { + + /** + * 处理最终返回结果 + */ + StrategyHandler DEFAULT = (materialVO, dynamicContext) -> { + DefaultStrategyFactory.DecisionOutcomeVO decisionOutcomeVO = new DefaultStrategyFactory.DecisionOutcomeVO(); + decisionOutcomeVO.setLevel(dynamicContext.getLevel()); + return decisionOutcomeVO; + }; + + /** + * 受理策略处理 + */ + DefaultStrategyFactory.DecisionOutcomeVO apply(DefaultStrategyFactory.MaterialVO materialVO, DefaultStrategyFactory.DynamicContext dynamicContext) throws Exception; + +} + +public abstract class AbstractStrategyRouter implements StrategyMapper, StrategyHandler { + + @Getter + @Setter + protected StrategyHandler defaultStrategyHandler = StrategyHandler.DEFAULT; + + /** + * 行为路由 + */ + public DefaultStrategyFactory.DecisionOutcomeVO router(DefaultStrategyFactory.MaterialVO materialVO, DefaultStrategyFactory.DynamicContext dynamicContext) throws Exception { + StrategyHandler strategyHandler = get(materialVO, dynamicContext); + if (null != strategyHandler) return strategyHandler.apply(materialVO, dynamicContext); + return defaultStrategyHandler.apply(materialVO, dynamicContext); + } + +} + +@Component +public class AccountNode extends AbstractStrategyRouter { + + private final MemberLevel0Node memberLevel0Node; + private final MemberLevel1Node memberLevel1Node; + private final MemberLevel2Node memberLevel2Node; + + public AccountNode(MemberLevel0Node memberLevel0Node, MemberLevel1Node memberLevel1Node, MemberLevel2Node memberLevel2Node) { + this.memberLevel0Node = memberLevel0Node; + this.memberLevel1Node = memberLevel1Node; + this.memberLevel2Node = memberLevel2Node; + } + + @Override + public DefaultStrategyFactory.DecisionOutcomeVO apply(DefaultStrategyFactory.MaterialVO materialVO, DefaultStrategyFactory.DynamicContext dynamicContext) throws Exception { + log.info("【账户节点】规则决策树 userId:{}", materialVO.getUserId()); + + // 1. 模拟查询用户级别 + int level = new Random().nextInt(3); + log.info("模拟查询用户级别 level:{}",level); + + dynamicContext.setLevel(level); + + return router(materialVO, dynamicContext); + } + + @Override + public StrategyHandler get(DefaultStrategyFactory.MaterialVO materialVO, DefaultStrategyFactory.DynamicContext dynamicContext) { + switch (dynamicContext.getLevel()){ + case 0: + return memberLevel0Node; + case 1: + return memberLevel1Node; + case 2: + return memberLevel2Node; + default: + return defaultStrategyHandler; + } + } + +} +``` + +- 通过最后的实现类可以看到,节点的执行是通过在本节点注入下一个要实现的节点,之后由get这个StrategyMapper接口的方法判断要走哪个节点去。 +- apply 受理执行方法执行完毕后,则调用路由方法,路由方法是抽象类中的方法,用于操作执行下一个节点的处理。 +- 所有执行链路上的数据,都有 DynamicContext 动态上下文进行收集,最后由 StrategyHandler 的 default 方法进行最终的结果数据拼装返回。 + +>这样的编码是不很爽,设计上即防腐又仿佛开启了新世界的大门!代码原来还能写的这么优雅!加入小傅哥即可获得整套项目代码学习。 diff --git "a/docs/md/develop/framework/ddd/2019-10-15-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\270\200\343\200\212\345\210\235\350\257\206\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241DDD\350\220\275\345\234\260\343\200\213.md" "b/docs/md/develop/framework/ddd/2019-10-15-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\270\200\343\200\212\345\210\235\350\257\206\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241DDD\350\220\275\345\234\260\343\200\213.md" index d254b9443..4537018f0 100644 --- "a/docs/md/develop/framework/ddd/2019-10-15-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\270\200\343\200\212\345\210\235\350\257\206\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241DDD\350\220\275\345\234\260\343\200\213.md" +++ "b/docs/md/develop/framework/ddd/2019-10-15-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\270\200\343\200\212\345\210\235\350\257\206\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241DDD\350\220\275\345\234\260\343\200\213.md" @@ -18,6 +18,9 @@ lock: need 领域驱动设计DDD{Domain-Driven Design}历史较长但随着微服务的兴起DDD又活跃到人们的视线,它提供的是一套架构设计思想,我们可以使用这套方法论将架构设计的尽可能做到高内聚、低耦合、可扩展性强的应用服务。本专题以DDD实战落地为根本,分章节设计不同的架构模型。学习并实战是奔入应用级最快的方法,Hi HelloWorld!我来了。 +- 本节案例代码:[https://github.com/fuzhengwei/itstack-demo-ddd](https://github.com/fuzhengwei/itstack-demo-ddd) +- 新版DDD讲解:[架构的本质之 DDD 架构](https://bugstack.cn/md/road-map/ddd.html) + ## 前言介绍 >DDD(Domain-Driven Design 领域驱动设计)是由Eric Evans最先提出,目的是对软件所涉及到的领域进行建模,以应对系统规模过大时引起的软件复杂性的问题。整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而复始,构建出一套符合当前领域的模型。 diff --git "a/docs/md/develop/framework/ddd/2019-10-16-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\272\214\343\200\212\351\242\206\345\237\237\345\261\202\345\206\263\347\255\226\350\247\204\345\210\231\346\240\221\346\234\215\345\212\241\350\256\276\350\256\241\343\200\213.md" "b/docs/md/develop/framework/ddd/2019-10-16-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\272\214\343\200\212\351\242\206\345\237\237\345\261\202\345\206\263\347\255\226\350\247\204\345\210\231\346\240\221\346\234\215\345\212\241\350\256\276\350\256\241\343\200\213.md" index d229e6c15..d8fd8c73b 100644 --- "a/docs/md/develop/framework/ddd/2019-10-16-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\272\214\343\200\212\351\242\206\345\237\237\345\261\202\345\206\263\347\255\226\350\247\204\345\210\231\346\240\221\346\234\215\345\212\241\350\256\276\350\256\241\343\200\213.md" +++ "b/docs/md/develop/framework/ddd/2019-10-16-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\272\214\343\200\212\351\242\206\345\237\237\345\261\202\345\206\263\347\255\226\350\247\204\345\210\231\346\240\221\346\234\215\345\212\241\350\256\276\350\256\241\343\200\213.md" @@ -17,13 +17,18 @@ lock: need > 沉淀、分享、成长,让自己和他人都能有所收获!😄 ## 前言介绍 + 在上一章节介绍了领域驱动设计的基本概念以及按照领域驱动设计的思想进行代码分层,但是仅仅只是从一个简单的分层结构上依然没法理解DDD以及如何去开发这样的微服务。另外往往按照这样分层后依然感觉和MVC也没有什么差别,也没有感受到带来什么非常大的好处。那么问题出在哪呢?我个人觉得DDD学起来更像是一套指导思想,不断的将学习者引入到领域触发的思维中去,而这恰恰也是最难学习的地方。时而感觉会了,而实际开发中又不对,本来已经拆解清晰了,但怎么又那么像MVC了。甚至怀疑自己,我在干嘛? 无论是DDD、MVC,他们更像是家里三居或者四局的格局,每一种格局方式都是为了更好的实现对应架构下的设计思想。但,不是说给你一个通用的架构模式,你就能开发出干净(高内聚)、整洁(低耦合)、漂亮(模块化)的代码。这就像是你家住三居、他家也住三居,但是你们屋子的舒适情况就一样吗?{再有,你家里会把厕所安在厨房吗?但你的代码是否这么干过,不合理的摆放导致重构延期。} 另外DDD之所以看着简单但又不那么好落地,个人认为很重要就是领域思想,DDD只是指导但是不能把互联网天下每一个业务行为开发都拿出来举例子给你看,每个领域需要设计。所以需要一些领域专家{产品+架构+不是杠精的程序猿}来讨论梳理,将业务形态设计出合理的架构&代码。 +- 本节案例代码:[https://github.com/fuzhengwei/itstack-demo-ddd](https://github.com/fuzhengwei/itstack-demo-ddd) +- 新版DDD讲解:[架构的本质之 DDD 架构](https://bugstack.cn/md/road-map/ddd.html) + ## 案例目标 + 本案例通过一个商品下单规则的场景来进行演示DDD; 1. 假设产品需求业务运行人员可以对不同的商品配置一些规则,这些规则可以满足不同用户类型可以下单不同商品。 2. 另外一些行为规则是会随着业务发展而增加或者变动的,所以不能写死{写死太吓人了}。 @@ -246,9 +251,20 @@ public interface EngineFilter { * 论坛:http://bugstack.cn * Create by 付政委 on @2019 */ -public interface EngineFilter { +@Repository("ruleRepository") +public class RuleRepository implements IRuleRepository { - EngineResult process(final DecisionMatter matter) throws Exception; + @Resource(name = "ruleMysqlRepository") + private RuleMysqlRepository ruleMysqlRepository; + @Resource(name = "ruleCacheRepository") + private RuleCacheRepository ruleCacheRepository; + + @Override + public TreeRuleRich queryTreeRuleRich(Long treeId) { + TreeRuleRich treeRuleRich = ruleCacheRepository.queryTreeRuleRich(treeId); + if (null != treeRuleRich) return treeRuleRich; + return ruleMysqlRepository.queryTreeRuleRich(treeId); + } } ``` diff --git "a/docs/md/develop/framework/ddd/2019-10-17-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\270\211\343\200\212\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241\346\236\266\346\236\204\345\237\272\344\272\216SpringCloud\346\220\255\345\273\272\345\276\256\346\234\215\345\212\241\343\200\213.md" "b/docs/md/develop/framework/ddd/2019-10-17-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\270\211\343\200\212\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241\346\236\266\346\236\204\345\237\272\344\272\216SpringCloud\346\220\255\345\273\272\345\276\256\346\234\215\345\212\241\343\200\213.md" index 86e5a1789..d6dba1dd2 100644 --- "a/docs/md/develop/framework/ddd/2019-10-17-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\270\211\343\200\212\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241\346\236\266\346\236\204\345\237\272\344\272\216SpringCloud\346\220\255\345\273\272\345\276\256\346\234\215\345\212\241\343\200\213.md" +++ "b/docs/md/develop/framework/ddd/2019-10-17-DDD\344\270\223\351\242\230\346\241\210\344\276\213\344\270\211\343\200\212\351\242\206\345\237\237\351\251\261\345\212\250\350\256\276\350\256\241\346\236\266\346\236\204\345\237\272\344\272\216SpringCloud\346\220\255\345\273\272\345\276\256\346\234\215\345\212\241\343\200\213.md" @@ -29,6 +29,9 @@ lock: need 如图,是微服务数据库使用的一种思想,我们希望路由层从最开始就被执行,用户分群动态扩展 ![微信公众号:bugstack虫洞栈 & 微服务数据库路由](https://bugstack.cn/assets/images/pic-content/2019/10/ddd-03-1.png) +- 本节案例代码:[https://github.com/fuzhengwei/itstack-demo-ddd](https://github.com/fuzhengwei/itstack-demo-ddd) +- 新版DDD讲解:[架构的本质之 DDD 架构](https://bugstack.cn/md/road-map/ddd.html) + ## 案例目标 本案例通过使用SpringCloud将我们的服务架构扩展为通过路由调用的微服务 1. 首先通过Eureka作为服务注册与发现中心 diff --git "a/docs/md/develop/framework/scheme/2024-06-19-\351\200\232\350\277\207\345\217\257\351\207\215\345\205\245\351\224\201\346\200\235\346\203\263\357\274\214\350\256\276\350\256\241MQ\350\277\201\347\247\273\346\226\271\346\241\210.md" "b/docs/md/develop/framework/scheme/2024-06-19-\351\200\232\350\277\207\345\217\257\351\207\215\345\205\245\351\224\201\346\200\235\346\203\263\357\274\214\350\256\276\350\256\241MQ\350\277\201\347\247\273\346\226\271\346\241\210.md" new file mode 100755 index 000000000..60a91410e --- /dev/null +++ "b/docs/md/develop/framework/scheme/2024-06-19-\351\200\232\350\277\207\345\217\257\351\207\215\345\205\245\351\224\201\346\200\235\346\203\263\357\274\214\350\256\276\350\256\241MQ\350\277\201\347\247\273\346\226\271\346\241\210.md" @@ -0,0 +1,47 @@ +--- +title: 通过可重入锁思想,设计MQ迁移方案 +lock: need +--- + +# 通过可重入锁思想,设计MQ迁移方案 + +作者:小傅哥 +
博客:[https://bugstack.cn](https://bugstack.cn) + +> 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +大家好,我是技术UP主小傅哥。 + +如果你的MQ消息要从Kafka切换到RocketMQ且不停机,怎么做?在让这个MQ消息调用第三方发奖接口,但无幂等字段又怎么处理?今天小傅哥就给大家分享一个关于MQ消息在这样的场景中的处理手段。 + +这是一种比较特例的场景,需要保证切换的MQ消息不被两端同时消费,并且还需要在一段消费失败后的MQ还可以继续重试。并且这一端消费的MQ消息,也要保证自身的幂等。 + +我们知道一般通用场景下,MQ消息都会有一个业务唯一ID值,用于接收方做仿重处理。但除此之外还应该有一个MQ消息本身的ID,这个ID也要全局唯一,每一条消息都要有一个ID,这是因为MQ是可能重复发送的(发送MQ成功,但获取MQ发送结果响应超时或更新库表消息状态失败,则重复发送),如果没有消息的唯一ID也就没法确保是哪一条消息了。 + +这个ID可以用于;唯一标识、去重、链路追踪、幂等性、事务以及安装性等,但可能有些伙伴在做MQ消息发送的时候,是容易忽略而没有在MQ中添加这个ID,或者随意用时间戳来当ID用,这样都是不合理的。会影响一些场景的代码健壮性设计。 + +>需求背景描述好了,接下来,我们看看这样的场景怎么设计。 + +## 1. 场景问题 + +将原本使用 Kafka 的MQ方式,迁移到 RocketMQ,同时部分场景的 MQ 消息调用三方接口是没有幂等字段的,需要做好程序兼容处理。 + +## 2. 场景思考 + +首先我们要知道在分布式架构下,我们每做的技术方案都要考虑顺序性和临界状态。像是MQ的生产和消费都是多套应用实例部署的,那么生产端发送出来的MQ消息到不同的队列中也是有延迟和存放顺序以及拉取消费不同的情况。如;生产端发送MQ为A、B、C、D,但到Kafka/RocketMQ以及不同的消费端拉取时,不一定是A、B、C、D的顺序,那么直接做切量开关,是可能导致一个A消息在Kafka队列中消费完,点击切换开关(一种切量哈希计算手段,如消息{A}哈希值最后两位当做百分比用),正好RocketMQ也会把A消费掉。这样同一个消息就被重复消费了。 + +## 3. 方案设计 + +在整个方案设计中,我们要考虑几个非常重要的点。如图; + +
+ +
+ +1. 一个是切换的两端MQ消费是抢占式加锁,避免重复消费。这是因为切量开关,切换过程中,两个消息队列中的MQ并不是顺序可靠的,可能存在重复消费,所以要加分布式锁。 +2. 一段MQ消费失败要进行重试,但这个时候不能在消费失败后删分布式锁,因为MQ消费都是很快的,可能导致删锁后另外一端MQ进行了相同的消费。那可能有些伙伴会说,那也没关系呀,反正失败的这段没有消费成功。当往往失败并不一定是直接的结果失败,可能是网络失败,可能是超时失败等。也就是实际成功了,但超时反馈了。所以不能被其他端重复消费,并且要保证自己这一端消费失败后可重试。所以这块要设计可重入锁,也就是 setnx 加锁的值,为自身一段的 mq 类型,这样自己在接收mq消息以后,检查锁为自身加锁值可重试。这样也就保证了一端消费重试,不会让另外一端把MQ也跟着消费掉,因为setnx存在,并且有加锁值判断,所以不能进入。 +3. 另外MQ消息还可能存在同一个MQ发送多次的场景,这个是非常正常的。比如,你再发送MQ的时候,超时网络抖动失败(1万次会有1次),那么就会补偿重发。但这个MQ已经发送过了,所以会接收2条MQ消息。那么在消费的时候,不能让2个MQ消息都进入消费中,因为多台实例消费,可能都去调用发奖了。那么这里还需要给MQ的ID进行幂等加锁。确保一个MQ消息,失败后,顺序轮训重试。也就保证了,发奖的过程中不会出现超发奖品。*大部分三方接口还是有幂等字段的,有的话会更好。* +4. 另外还有2个开关,一个是`消费开关`,一个是`切量开关`。消费开关要在整个新的MQ改造工程工程全部上线后开启,但还要被切量开关限定消费。开启后,切量开关才会生效。切量是一种哈希值的百分比比对,比如一个哈希值最后两位是10,那么切量配置小于等于10%则这个MQ则可以被切量后消费,另外一段则不消费这个MQ。 +5. 另外,为了方便测试线上功能,还会加入白名单。不过大部分时候这类东西会用通用组件能力解决。 + +这样的场景方案设计,是非常值得积累的,同类的思想也可以帮我们解决很多共性问题。 diff --git a/docs/md/devops/2023-04-18-nginx.md b/docs/md/devops/2023-04-18-nginx.md index fa9235c7a..59ce13887 100644 --- a/docs/md/devops/2023-04-18-nginx.md +++ b/docs/md/devops/2023-04-18-nginx.md @@ -293,18 +293,18 @@ docker run \ ![](https://bugstack.cn/images/article/devops/dev-ops-nginx-230418-04.png) -## 五、OpenAI 访问 +## 五、重定向 -### 1. 重定向 +### 1. default.conf 在 default.conf 中添加如下配置后重启 Nginx 即可; ```shell location /d5fe/ { rewrite ^/d5fe/(.*)$ /$1 break; - proxy_pass https://api.openai.com; + proxy_pass https://api.x.com; proxy_ssl_server_name on; - proxy_set_header Host api.openai.com; + proxy_set_header Host api.x.com; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; @@ -351,9 +351,9 @@ server { location /abc/ { auth_request /auth; rewrite ^/abc/(.*)$ /$1 break; - proxy_pass https://api.openai.com; + proxy_pass https://api.x.com; proxy_ssl_server_name on; - proxy_set_header Host api.openai.com; + proxy_set_header Host api.x.com; proxy_set_header Connection ''; proxy_http_version 1.1; chunked_transfer_encoding off; diff --git a/docs/md/devops/2023-04-18-tool.md b/docs/md/devops/2023-04-18-tool.md index 48e212bcb..b48153b94 100644 --- a/docs/md/devops/2023-04-18-tool.md +++ b/docs/md/devops/2023-04-18-tool.md @@ -21,6 +21,7 @@ lock: need 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 客户端工具 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-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-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/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-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 4c4fef78f..34083038c 100644 --- a/docs/md/other/guide-to-reading.md +++ b/docs/md/other/guide-to-reading.md @@ -4,29 +4,37 @@ 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 -- 考试:[100道八股题考试测验](https://bugstack.cn/md/zsxq/material/exam.html) —— 你可以尝试验证自己的能力,考题范围:数据结构、算法、源码、设计模式、系统架构、中间件、网络通信、实战项目、扩展问题 -- 本站为公众号往期文章整理的小册,关注`公众号`:[bugstack虫洞栈](https://bugstack.cn/images/personal/qrcode.png) 可以知晓最新推文,避免错过最近正在更新的技术系列文章。 -- 其他:如果你在学习本站内容遇到不能解决的问题,可以联系作者:`小傅哥`,微信:`fustack` - 交个朋友👬🏻,不要错过成为技术同好的机会。 -- 加入星球:[码农会锁](https://bugstack.cn/md/zsxq/introduce.html) - 你可以获得本站所有学习内容的**指导**和**帮助**,还可以学习实战项目!`☕️一顿饭钱的支持,突破技术瓶颈` **小妙招**:关注公众号:[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` - 交个朋友👬🏻,不要错过成为技术同好的机会。 ## 一、本站知识阅览 @@ -37,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)等众多项目。 - **关于**:除了技术学习以外,还有很多伙伴会经常问我一些关于学习、成长以及在职场中怎么活下去。所以我结合我自己在大厂互联网中的学习和成长经历,给读者伙伴写了不少此类的内容。如简历编写、招聘要求、技术资料、代码规范、评审晋升、薪资待遇、副业收入等等。这些内容可能很多会帮助你度过一个安定的职场生涯! ## 二、学习路线参考 @@ -55,7 +64,8 @@ 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) - 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) ## 三、算法 @@ -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,20 +289,18 @@ 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 下载 @@ -240,4 +311,4 @@ title: 编码指南 ## 关于 -关于自己、关于学习、关于职场,👉 如果你是刚入行、在外包、跨语言学习、想跳槽大厂、缺少学习动力等,可以阅读小傅哥的成长故事,这个系列包括了我的个人在外包到大厂的成长、跳槽的过程、互联网的学习经历,那么可以阅读一下。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/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 index 16e8454e9..df52e66b5 100644 --- a/docs/md/product/idea-plugin/vo2dto.md +++ b/docs/md/product/idea-plugin/vo2dto.md @@ -1,5 +1,5 @@ --- -title: IDEA Plugin vo2dto +title: IDEA Plugin vo2dto v2.4.8 lock: no --- 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/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\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 index 0257cb8a2..4597f78d0 100644 --- a/docs/md/project/chatgpt/chatgpt.md +++ b/docs/md/project/chatgpt/chatgpt.md @@ -1,12 +1,13 @@ --- -title: 课程:ChatGPT 微服务应用体系构建 +title: 课程:OpenAi 大模型应用服务体系构建 lock: no --- -# ChatGPT 微服务应用体系构建 - API-SDK、鉴权、公众号、企业微信、支付服务 +# OpenAi 大模型应用服务体系构建 - API-SDK、鉴权、公众号、企业微信、支付服务 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) +
体验:[https://openai.gaga.plus](https://openai.gaga.plus) >沉淀、分享、成长,让自己和他人都能有所收获!😄 @@ -24,9 +25,9 @@ lock: no 作为一个纯搞技术的号主,我更希望做符合技术人员长期发展的学习事项。如 ChatGPT、文心一言、通义千问、AIGC、Civita,这样的东西会越来越多,而作为研发更应该注重以`生成式服务`所搭建出一套体系化应用微服务。所以小傅哥的星球又要带着大家搞新项目了 **《ChatGPT 微服务应用体系构建》** - 说到又,那小傅哥的星球搞了多少项目🤔? -除技术小册外,星球历经项目5个:- `加入小傅哥的知识星球,相当于付费1个项目的价格,就可以学习所有过往的项目!` +除技术小册外,星球所有项目都可以学习:- `加入小傅哥的知识星球,相当于付费1个项目的价格,就可以学习所有过往的项目!` -- 《ChatGPT 微服务应用体系构建 - API-SDK、鉴权、公众号对接、微信对接、交易支付》- **5.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) @@ -54,7 +55,7 @@ lock: no ## 三、`开发`的计划 -**死鬼**,5.1 假期即将来临,又到了疯狂的带着星球伙伴卷代码的时候。所以从 5.1 放假开始,小傅哥会逐步更新课程内容,粗略计划; +**死鬼**,又到了疯狂的带着星球伙伴卷代码的时候。 - OpenAI 接口服务 + Nginx SSL 配置 - OpenAI SDK @@ -86,8 +87,6 @@ lock: no --- -好啦,想加入学习的伙伴,记得早些下手。因为每增加一个新项目,星球会提价20元。此项目在5.1正式启动后,会对星球提价20元。**所以现在加入是最爽的!** +好啦,想加入学习的伙伴,记得早些下手。 -
- -
\ No newline at end of file +>[🧧加入学习](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\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 index d6dd475fc..59fab7fc6 100644 --- a/docs/md/project/chatgpt/extra/ChatGPT-v1.0.md +++ b/docs/md/project/chatgpt/extra/ChatGPT-v1.0.md @@ -94,9 +94,7 @@ lock: no 而目前的价格也仅仅是几瓶扎啤钱!你的加入,就当我们为前程干一杯🍻! -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html)
diff --git a/docs/md/project/chatgpt/extra/ChatGPT-v1.2.md b/docs/md/project/chatgpt/extra/ChatGPT-v1.2.md index b67f6c34b..c0617cd34 100644 --- a/docs/md/project/chatgpt/extra/ChatGPT-v1.2.md +++ b/docs/md/project/chatgpt/extra/ChatGPT-v1.2.md @@ -106,6 +106,7 @@ lock: no - 介绍 - [课程: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) @@ -113,16 +114,31 @@ lock: no - [第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) @@ -133,6 +149,8 @@ lock: no - [第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) @@ -152,9 +170,7 @@ lock: no - 福利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 index f7478a88e..80ede29b5 100644 --- a/docs/md/project/chatgpt/extra/ChatGPT-v1.3.md +++ b/docs/md/project/chatgpt/extra/ChatGPT-v1.3.md @@ -131,9 +131,7 @@ lock: no 如果大家希望通过做有价值的编程项目,提高自己的编程思维和编码能力,可以加入小傅哥的【星球:码农会锁】。加入后解锁🔓所有往期项目,还可以学习后续新开发的项目。 -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) 已经有很多伙伴开始学起来了,还有大家交的作业笔记。有了的项目驱动学习,清晰的目标感,大家冲起来也有了更明确的方向!干干干!!! diff --git a/docs/md/project/chatgpt/notes.md b/docs/md/project/chatgpt/notes.md index 54afeb3fc..0fd7cad98 100644 --- a/docs/md/project/chatgpt/notes.md +++ b/docs/md/project/chatgpt/notes.md @@ -33,7 +33,7 @@ lock: no - 【运维】熟练掌握 Git、GitCode,对工程代码的管理,推送、拉取、切换分支、合并代码等操作。 - 【运维】熟练申请和使用 SSL 配置 Nginx 域名 HTTPS 服务。 -## 二、项目经验 +## 二、简历模板 - 项目名称:OpenAI 应用服务 - 辅助工作提效工具开发 - 项目架构:微服务架构设计,OpenAI-SDK 多模型组件【ChatGLM、ChatGPT】、DDD 应用服务API封装、WEB REACT 前端界面【按需编写】 @@ -47,21 +47,139 @@ lock: no - 在整套工程的设计实现中,采用了较多的分治、抽象的思想和设计模式和设计原则知识的运用,来解决各类场景问题。 - 对接微信扫码支付,完成从商品库、下单支付、异步发货、掉单补偿等核心流程实现。让用户可以在线购买对话额度。 - 注意:你还可以根据项目中提到的各类技术和章节,来编写你的职责。 - -## 三、项目问答 -### 1. 此项目的背景和需求来自哪里? +## 三、面试问题 + +谢二机同学你好,你做的这个项目我非常感兴趣,我们公司也有这样的OpenAI 业务对接,正好可以结合你做的这个[《OpenAI大模型项目》](https://bugstack.cn/md/project/chatgpt/chatgpt.html)聊一下。 + +### 1. 你的这个项目的背景和需求来自哪里? **校招身份举例** 面试官您好,此项目的最核心背景和诉求,是我希望找到一个可以真实锻炼技术应用的场景。并且可以基于此项目的设计、开发、上线、运维等一系列动作,提高编程思维和落地能力。而此项目的只是一个载体,在项目中我所运用到的微信对接、登录鉴权、异步接口、下单支付、异步发货、账户管理等场景可以运用到其他任何一个项目中使用。所以我选择开发 这样一个项目。 -并且此项目运用了DDD分层架构,领域驱动设计实现,对于各个场景都遵守了设计原则和设计模式,解决各类复杂场景的实现。如;生成式服务流程中运用模板模式、策略模式、工厂模式,解决对话过程中所需的规则过滤、模型校验、账户状态、账户扣减等开发流程。 +并且此项目运用了DDD分层架构,领域驱动设计实现,对于各个场景都遵守了设计原则和设计模式,解决各类复杂场景的实现。如;生成式服务流程中运用模板模式、策略模式、工厂模式,解决对话过程中所需的规则过滤、模型校验、账户状态、账户扣减等开发流程。【项目提问过程中,有时候会让绘制下项目的分层结构、核心流程,之后会基于你画的这些进行提问】 通过以上项目的学习我锻炼到了相关项目所用到的核心技术使用,架构设计和落地实现。而此项目的学习,也为我以后在工作中解决实际场问题,打下了牢固的基础。 -### 2. 充血模型和贫血模型分别合适什么场景? +### 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而不是其他的框架?这块我没有接触过其他框架所以回答的不是很好,想问下小傅哥这个问题应该怎么回答 -在 DDD 架构中,充血模型,主要的价值在于解决具有生命周期的流程。如生成式对话、商品下单支付、用户登录授权等流程。因为这些流程中具有较复杂的场景模型和唯一ID,所以采用充血模型结构,会很适合的将状态和行为封装到同一的领域包中进行聚合开发实现。这样的实现方式,也为以后扩展、迭代、维护做了良好的基建,避免工程过于腐化。—— `注意关于DDD的知识,可以把这里的5个视频都刷下。` [https://bugstack.cn/md/road-map/mvc.html](https://bugstack.cn/md/road-map/mvc.html) +- 问题:[https://t.zsxq.com/xXQZr](https://t.zsxq.com/xXQZr) -而贫血模型则比较适合 Querys 场景,因为这些场景只是数据的汇总查询,没有唯一ID和生命周期。所以比较适合在工程中提供 query 模块,使用贫血模型开发。 \ No newline at end of file +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 index 556493703..ddae09e8e 100644 --- a/docs/md/project/chatgpt/sdk/chatglm-sdk-java.md +++ b/docs/md/project/chatgpt/sdk/chatglm-sdk-java.md @@ -1,5 +1,5 @@ --- -title: 第1节:ChatGLM SDK - 智谱Ai +title: 第1节:ChatGLM SDK - 智谱Ai v1 lock: no --- 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 89272cb19..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,27 +70,7 @@ excerpt: 本专栏是作者小傅哥使用JavaFx、Netty4.x、SpringBoot、Mysql >专栏共有25篇文章,分别从UI、架构到功能实现逐步讲解,非常适合新人学习提升编码能力和架构思想。 -对于此项目,你可以单独购买,也可以加入小傅哥的知识星球,在星球中除了此项目,还会提供其他项目学习。 - -### 1. 加入星球 - -星球地址:[https://t.zsxq.com/Ja27ujq](https://t.zsxq.com/Ja27ujq) - -
- -
公众号【bugstack虫洞栈】 回复【星球】可获得优惠券
-
-
- -### 2. 单独购买 - -学习链接:[IM 小册](https://blog.csdn.net/generalfu/category_10400631.html) - -
- -
公众号【bugstack虫洞栈】 回复【星球】可获得优惠券
-
-
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) ## 六、优秀作业 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\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 dc357cf02..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" @@ -22,7 +22,6 @@ pay: https://t.zsxq.com/ea6AI23 - [模板模式处理抽奖流程 @BerserkD](https://t.zsxq.com/066EaYvrn) - [抽奖系统第6节模板模式处理抽奖流程 @巍](https://t.zsxq.com/06qjeQfm2) - [模板模式处理抽奖流程 @Geroge Liu](https://t.zsxq.com/06YVFU3Nf) -- [问题:DrawStrategySupport 抽取有些疑惑 @CCAT](https://t.zsxq.com/06nIAIe2V) - [构建活动领取模块,复习路由组件 @Chin](https://t.zsxq.com/06jeyzn6E) - [学习策略库表的设计、抽奖算法的实现与抽奖流程的设定 @HL](https://t.zsxq.com/06Q3vrbIE) - [抽奖过程方法实现 @Jachin](https://t.zsxq.com/07myBUJQb) 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 628fb1067..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" @@ -43,6 +43,7 @@ pay: https://t.zsxq.com/ZzzFIyn - [一张组件执行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) ## 一、开发日志 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 92f67a0fd..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" @@ -34,6 +34,7 @@ pay: https://t.zsxq.com/qBIa6yZ - [活动领取、领域编排、规则引擎,功能图总结 @派大星来学习](https://t.zsxq.com/10ipUFkC2) - [本章节运用了组合模式,组合模式是依据树形结构来组合对象 @素质男孩](https://t.zsxq.com/102mZrEzu) - [系统功能流程图汇总 @CCAT](https://t.zsxq.com/11vxKGZor) +- [规则引擎这一章节有一些懵,画一个时序图豁然开朗 @1](https://t.zsxq.com/14AV22r8X) ## 一、开发日志 diff --git a/docs/md/project/lottery/notes.md b/docs/md/project/lottery/notes.md index 35256066c..a9d1c77f8 100644 --- a/docs/md/project/lottery/notes.md +++ b/docs/md/project/lottery/notes.md @@ -229,7 +229,6 @@ Lottery 系统的全方面技术栈的使用,多场景的问题的解决方案 - [抽奖系统,被问到系统的瓶颈在哪里](https://t.zsxq.com/10WwhYDKy) - [为什么要选redis,redis 主从集群下潜在的锁失效问题怎么考虑怎么解决。ookeeper 作为分布式锁优缺点](https://t.zsxq.com/107LFRcAH) - [这个抽奖系统里的聚合、聚合根、实体是具体对应的哪些部分](https://t.zsxq.com/11VOoFhHX) -- [百度提前批被问了一些关于路由组件的问题(我把路由组件单独作为一个小项目)](https://t.zsxq.com/11ckhUMeW) - [关于db-router的redis路由问题:假设并发量还是很高,那么把库存分摊到不同的stockKey上,不同用户去不同的key上incr。但是这和自研路由有什么关系](https://t.zsxq.com/11j6zWN6k) - [项目中为什么用dubbo不用fegin,这个问题怎么答](https://t.zsxq.com/115w0DAUw) - [昨天字节二面被疯狂拷打项目,特来请教一下相关问题](https://t.zsxq.com/11qoK5xep) 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/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 index 2fdcfab2f..ee4b8a181 100644 --- a/docs/md/road-map/ddd-dev-account.md +++ b/docs/md/road-map/ddd-dev-account.md @@ -1,5 +1,5 @@ --- -title: DDD 架构 - 账户域 +title: DDD 架构场景 - 账户域 lock: need --- @@ -11,3 +11,5 @@ lock: need > 沉淀、分享、成长,让自己和他人都能有所收获!😄 + +- 项目地址:[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 index 19357c9e8..51b7e27fa 100644 --- a/docs/md/road-map/ddd-dev-pay.md +++ b/docs/md/road-map/ddd-dev-pay.md @@ -1,5 +1,5 @@ --- -title: DDD 架构 - 交易域 +title: DDD 架构场景 - 交易域 lock: need --- @@ -10,4 +10,6 @@ lock: need > 沉淀、分享、成长,让自己和他人都能有所收获!😄 - \ No newline at end of file + + +- 项目地址:[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 index b0dfff8f8..e1eb279cc 100644 --- a/docs/md/road-map/ddd.md +++ b/docs/md/road-map/ddd.md @@ -1,5 +1,5 @@ --- -title: DDD 架构 +title: DDD 架构设计 lock: need --- @@ -14,7 +14,7 @@ lock: need 从最早接触 DDD 架构,到后来用 DDD 架构不断的承接项目开发,一次次在项目开发中的经验积累。对 DDD 有了不少的理解。DDD 是一种思想,落地的形态和结构会有不同的方式,甚至在编码上也会有风格的差异。但终期目标就一个;”提供代码的可维护性,降低迭代开发成本。“也是康威定律所述:”任何组织在设计一套系统时,所交付的设计方案在结构上都与该组织的沟通结构保持一致。“ -但 DDD 与 MVC 相比的概率较多,贸然用理论驱动代码开发,会让整个工程变得非常混乱,甚至可能虽然是用的 DDD 但最后写出来了一片四不像的 MVC 代码。所以对于程序员👨🏻‍💻来说,先能上手一个工程,在从工程了解理论会更加容易。为此小傅哥想以此文,通过实战编码的方式向大家分享 DDD 架构,并能让大家上手的 DDD 架构。 +但 DDD 与 MVC 相比差异较大,贸然用理论驱动代码开发,会让整个工程变得非常混乱,甚至可能虽然是用的 DDD 但最后写出来了一片四不像的 MVC 代码。所以对于程序员👨🏻‍💻来说,先能上手一个工程,在从工程了解理论会更加容易。为此小傅哥想以此文,通过实战编码的方式向大家分享 DDD 架构,并能让大家上手的 DDD 架构。 ## 一、问题碰撞 @@ -26,7 +26,7 @@ lock: need -- 如果你接触过较大型且已经长期维护项目的 MVC 架构,你就会发现这里的 DAO、PO、VO 对象,在 Service 层相互调用。那么长期开发后,就导致了各个 PO 里的属性字段数量都被撑的特别大。这样的开发方式,将`”状态”`、`“行为“`分离到不同的对象中,代码的意图渐渐模糊,膨胀、臃肿和不稳定的架构,让迭代成本增加。 +- 如果你接触过较大型且已经长期维护项目的 MVC 架构,你就会发现这里的 DAO、PO(持久化)、VO(业务对象) 对象,在 Service 层相互调用。那么长期开发后,就导致了各个 VO 里的属性字段数量都被撑的特别大。这样的开发方式,将`”状态”`、`“行为“`分离到不同的对象中,代码的意图渐渐模糊,膨胀、臃肿和不稳定的架构,让迭代成本增加。 - 而 DDD 架构首先以解决此类问题为主,将各个属于自己领域范围内的行为和逻辑封装到自己的领域包下处理。这也是 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 index e0ed3aa8c..3fb609b15 100644 --- a/docs/md/road-map/docker.md +++ b/docs/md/road-map/docker.md @@ -1,15 +1,17 @@ --- -title: Docker +title: Docker 环境配置(手动安装) lock: need --- -# Docker 环境配置 +# Docker 环境配置(手动安装) 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) > 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + - 官网:[https://www.docker.com](https://www.docker.com/) - Mac、Windows、Linux - 介绍:Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Mac、Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。总之它加快构建、共享和运行现代应用程序的速度。 @@ -17,8 +19,11 @@ lock: need ### 1. 查看系统的内核版本 +```shell +uname -r +``` + ```java -[root@CodeGuide ~]# uname -r 4.18.0-80.11.2.el8_0.x86_64 ``` @@ -27,8 +32,12 @@ lock: need ### 2. yum 更新到最新版本 +```shell +sudo yum update +``` + + ```java -[root@CodeGuide ~]# sudo yum update Last metadata expiration check: 1:15:10 ago on Sat 27 Nov 2021 04:22:53 PM CST. Dependencies resolved. Nothing to do. @@ -38,6 +47,25 @@ 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 @@ -55,8 +83,11 @@ Nothing to ### 4. 设置Docker的yum的源 +```shell +sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo +``` + ```java -[root@CodeGuide ~]# sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo Adding repo from: https://download.docker.com/linux/centos/docker-ce.repo ``` @@ -70,7 +101,6 @@ sudo yum-config-manager \ sudo sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo ``` - ### 5. 查看仓库所有Docker版本 ```java @@ -102,32 +132,44 @@ Available Packages ### 6. 安装Docker ```java -[root@CodeGuide ~]# sudo yum install docker +[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 +- 安装默认最新版本的 Docker `最新版本可能有坑,最好指定版本安装` ```java [root@CodeGuide ~]# sudo yum install ``` -- 安装指定版本,例如:sudo yum install docker-ce-20.10.11.ce +- 安装指定版本,例如:`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 +mv docker-compose-linux-x86_64 docker-compose # 加入执行权限 sudo chmod +x /usr/local/bin/docker-compose # 查看docker-compose版本 @@ -135,8 +177,8 @@ docker-compose -v ``` ```java -[root@dev-ops bin]# sudo chmod +x /usr/local/bin/docker-compose -[root@dev-ops bin]# docker-compose -v +sudo chmod +x /usr/local/bin/docker-compose +docker-compose -v Docker Compose version v2.18.1 ``` @@ -156,6 +198,12 @@ Docker Compose version v2.18.1 - 设置开机启动 Docker +```java +sudo systemctl restart docker +``` + +- 重启 Docker 命令 + ### 9. 查看 Docker 版本 ```java @@ -195,7 +243,8 @@ Docker version 20.10.11, build dea9396 ### 12. 设置国内源 -阿里云提供了镜像源:[https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors](https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors) - 登录后你会获得一个专属的地址。 +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 @@ -210,13 +259,19 @@ 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://h1log1d5.mirror.aliyuncs.com", - "http://docker.mirrors.ustc.edu.cn", - "http://hub-mirror.c.163.com" + "https://docker.1panel.live", + "https://dc.j8.work", + "https://docker.m.daocloud.io" ], "builder": { "gc": { @@ -233,7 +288,35 @@ sudo systemctl restart docker 这个命令会创建一个 `/etc/docker/daemon.json` 文件,并将国内源的配置写入其中。然后你只需要重启 Docker 服务即可使配置生效,可以通过运行 `sudo systemctl restart docker` 命令来重启 Docker 服务。 -### 13. 远程连接 +**解决目前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 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 index 4a089034e..aa011e1f8 100644 --- a/docs/md/road-map/dubbo.md +++ b/docs/md/road-map/dubbo.md @@ -20,7 +20,7 @@ lock: need ## 一、为什么使用 -随着互联网场景中所要面对的用户规模和体量的增加,系统的也需要做相应的拆分设计和实现。随之而来的,以前的一套系统,现在成了多个微服务。如;电商系统,以前就在一个工程中写就可以了,现在需要拆分出,用户、支付、商品、配送、活动、风控等各个模块。那么这些模块拆分后,如何高效的通信呢? +随着互联网场景中所要面对的用户规模和体量的增加,系统也需要做相应的拆分设计和实现。随之而来的,以前的一套系统,现在成了多个微服务。如:电商系统,以前就在一个工程中写就可以了,现在需要拆分出,用户、支付、商品、配送、活动、风控等各个模块。那么这些模块拆分后,如何高效的通信呢?
@@ -35,7 +35,7 @@ lock: need
-Dubbo 的使用分为2方,一个是接口的提供方,另外一个是接口的调用方。接口的提供方需要提供出被调用方使用接口的描述性信息。这个信息包括;接口名称、接口入参、接口出参,只有让调用方拿到这些信息以后,它才能依托于这样的接口信息做一个代理操作,并在代理类中使用 Socket 完成双方的信息交互。 +Dubbo 的使用分为2方,一个是接口的提供方,另外一个是接口的调用方。接口的提供方需要提供出被调用方使用接口的描述性信息。这个信息包括:接口名称、接口入参、接口出参,只有让调用方拿到这些信息以后,它才能依托于这样的接口信息做一个代理操作,并在代理类中使用 Socket 完成双方的信息交互。 所以你看上去调用 RPC 接口好像和使用 HTTP 也没啥区别,无非就是引入了 POM 配置,之后再配置了注解就可以使用了。但其实,它是把你的 Jar 当做代理的必要参数使用了。**本文也会介绍,具体是怎么代理的** @@ -132,7 +132,7 @@ dubbo: - 配置信息平平无奇,但第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,一下午又过去了。 +- 再有一个问题,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 应用构建 @@ -148,11 +148,11 @@ Install 是干啥的?它是为了让你使用了同一个本地 Maven 配置 - 你要先点击 root 下的 install 操作,这样就会自动构建了。 -- 如果你电脑配置有点低,也会出现一些`气人怪相`,比如就刷不进去,install 了也引用不了。记得要 clean 清空下,也可以直接到 maven 文件件去清空。 +- 如果你电脑配置有点低,也会出现一些`气人怪相`,比如就刷不进去,install 了也引用不了。记得要 clean 清空下,也可以直接到 maven 文件夹去清空。 ### 2. 接口使用方 -有些小卡拉米觉得前面的抗都扫干净了,就完事了。没有接下来还有坑,让你一搞搞一天,半夜也睡不好。 +有些小卡拉米觉得前面的坑都扫干净了,就完事了。没有接下来还有坑,让你一搞搞一天,半夜也睡不好。 #### 2.1 POM 引入 @@ -227,7 +227,7 @@ public void test_userService() { 好,核心的原理就这么点。接下来,我们从代码中看看。 -### 1. 接口代理 - 提供方 +### 1. 接口反射 - 提供方 **源码**:`cn.bugstack.dev.tech.dubbo.trigger.socket.RpcServerSocket` @@ -297,13 +297,13 @@ public class RpcServerSocket implements Runnable { } ``` -这段代码主要提供的功能包括; -1. Netty Socket 启动一个服务端 +这段代码主要提供的功能包括: +1. Netty Socket 启动一个服务端。 2. 注入 ApplicationContext applicationContext 用于在接收到请求接口信息后,获取对应的 Bean 对象。 3. 根据请求来的 Bean 对象,以及参数的必要信息。进行接口的反射调用。 4. 最后一步,就是把接口反射请求的信息,再通过 Socket 返回回去。 -### 2. 接口反射 - 调用方 +### 2. 接口代理 - 调用方 打开工程:[xfg-dev-tech-dubbo-test](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-dubbo-test) @@ -397,7 +397,7 @@ public class RPCProxyBeanFactory implements FactoryBean, Runnable } ``` -这段代码主要提供的功能包括; +这段代码主要提供的功能包括: 1. 实现 `FactoryBean` 为的是把这样一个代理对象,交给 Spring 容器管理。 2. 实现 Runnable 接口,并在接口中,创建 Netty 的 Socket 客户端。客户端中接收来自服务端的消息,并临时存放到缓存中。**注意 Dubbo 中这块的处理会复杂一些,以及请求同步响应通信,这样才能把各个接口的调动记录下来** 3. `getObject()` 对象中,提供代理操作。代理里,就可以自己想咋搞咋搞了。而 Dubbo 也是在代理里,提供了如此的操作,对接口提供方发送请求消息,并在超时时间内返回接口信息。因为反射调用,需要你`提供类`、`方法`、`入参类型`、`入参内容`,所以我们要把这些信息传递给接口提供方。 @@ -426,4 +426,4 @@ public void test_proxyUserService(){ ``` - 这里我们给 IUserService 注入一个自己代理好的对象,之后就可以调用验证了。 -- 好啦,到这我们就把关于 Dubbo 的事交代明白了,以上内容较多。小卡拉米需要细细的品味吸收! \ No newline at end of file +- 好啦,到这我们就把关于 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/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 index cb7d19e62..a033ebd6c 100644 --- a/docs/md/road-map/git.md +++ b/docs/md/road-map/git.md @@ -1,6 +1,6 @@ --- title: Git -lock: no +lock: need --- # Git 使用说明和配置 @@ -10,7 +10,23 @@ lock: no > 沉淀、分享、成长,让自己和他人都能有所收获!😄 -# Git 使用教程 + + +大家好,我是技术UP主小傅哥。 + +**不会Git操作的伙伴,轻则写不了代码,重则误操作搞丢自己的代码或者干掉别人的代码。** 因为进入公司后,就不只是你一个人在一个工程上写代码,而是所有这个项目组的伙伴都需要在这个工程上写代码,大家要在统一的Git的规范完成代码开发和提交。—— 🤨 不信的话,进入公司乱删个Git分支或者随便任何一个分支提交代码试试。 + +
+ +
+ +**Git的作用是什么?** + +你可以想象下,当你有10个小伙伴都需要在一个 txt 文档里,写一份老师👩🏻‍🏫上课的笔记📒,把信息进行汇总、互相完善、用于课后学习。那么怎么保证大家做的课堂内容都能顺利的写在一份 txt 呢,而且不要互相删除,也不要丢失谁的信息呢? + +这个就是 Git 的用途。Git 是一种分布式版本控制系统,用于高效地管理和跟踪源代码和文件的历史记录,支持多人协作开发及分支管理,提供可靠的版本回退和合并机制,从而提高开发效率和代码质量。 + +## 1. Git 起源故事 讲到 Git 就不得不提一下 Linux,因为如果没有 Linux 也就没有 Git 的诞生,这里是有一段 **10天** 写出 Git 的故事! @@ -24,28 +40,51 @@ lock: no 有了 Git 以后,GitHub 平台也于2007年10月1日开始开发。网站于2008年2月以beta版本开始上线,4月份正式上线。GitHub 里面的项目可以通过标准的 Git 命令进行访问和操作。—— 这就是 Linux、Git、Github 的故事。 -## 一、软件安装 +## 2. 软件安装 -地址:[https://git-scm.com/downloads](https://git-scm.com/downloads) - `选择需要的版本下载` +Git 是一个软件工具,在使用前需要进行安装。 -### 1. Mac +- 地址:[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. Windows +### 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) -### 3. Linux +### 2.3 Linux -### Debian/Ubuntu +#### Debian/Ubuntu 获取适用于您的 Debian/Ubuntu 版本的最新稳定版本 +**如果没有安装npm可以安装** + +``` +# sudo apt update +# sudo apt install nodejs npm +# node -v +# npm -v +``` + +**安装git** + ``` # apt-get install git ``` @@ -57,63 +96,329 @@ brew install git # apt update; apt install git ``` -### Red Hat Enterprise Linux, Oracle Linux, CentOS, Scientific Linux, et al. +#### Centos + +```java +sudo yum install git +``` + +## 3. 配置账户 -RHEL 及其衍生版本通常提供旧版本的 git。您可以[下载 tarball](https://www.kernel.org/pub/software/scm/git/)并从源代码构建,或者使用第 3 方存储库(例如[IUS 社区项目)](https://ius.io/)来获取更新版本的 git。 +当你拿到一个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 -# 安装完成后,配置账户,在命令行输入: -$ git config --global user.name "Your Name" -$ git config --global user.email "email@example.com" +[root@lavm-aqhgp9nber ~]# git config --global user.name "fuzhengwei" +[root@lavm-aqhgp9nber ~]# git config --global user.email "184172133@qq.com" ``` -## 三、生成SSH +- 用户名和邮箱,更换为你的代码库注册使用的名称和邮箱📮。 + +### 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 用于本地仓库和远程进行链接验证,Github、Gitee、Gitcode 都可以使用这种安全方式操作仓库 +- 命令:`ssh-keygen -t rsa -C "184172133@qq.com"` +- 操作:一路敲回车同意就可以了。 -### 1. 创建 +### 3. 获取ssh秘钥 ```java -# 记得换成你的邮箱 -ssh-keygen -t rsa -C "184172133@qq.com" +[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) + +--- -### 2. 查看 +其他的各个代码库平台也都类似,找到配置 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 -fuzhengwei@MacBook-Pro ~ % cd ~/.ssh -fuzhengwei@MacBook-Pro .ssh % ls -184172133@qq.com-gitcode__net config -184172133@qq.com-gitcode__net.pub fuzhengwei-GitHub -fuzhengwei@MacBook-Pro .ssh % pwd -/Users/fuzhengwei/.ssh +git clone -b 240720-xfg-init-project git@github.com:fuzhengwei/openai-code-review.git ``` -进入 `/Users/fuzhengwei/.ssh` 找到 rsa.pub 文件 +- 添加 `-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!第二个谨慎是要回滚代码,需要和团队中对应的伙伴打招呼,避免影响别人测试或者上线。 -注意:如果你进入 Mac 电脑文件夹,看不见这个隐藏文件。则可以通过 `Shift + Command + .` 打开隐藏文件,就看见 `.ssh` 文件夹了。 +
+ +
+ +1. 先选择要在哪个分支的哪次提交上进行回滚。这里选择的是 test 分支上的提交进行回滚。 +2. 这里选择 Hard 回滚。因为我们所有的都是合并到 test 分支,所以 test 分支丢失也没问题。可以重新合并。但要和同组伙伴提前说明。 +3. 回滚后,你会看到代码只剩下从回滚往下的提交内容了。 +4. 回滚后,你不能直接 push 提交了,这个之后会报错;`fast-forward` 因为此时本地分支落后于远程分支。 +5. 所以要通过 `git push origin HEAD --force` 进行强制提交。或者你可以把 test 的远程分支删掉,之后在提交。 -### 3. 使用 +## 6. 提交工程 - IntelliJ IDEA -打开 ras.pub 文件,复制内容到仓库; +那么首次创建的工程怎么提交到代码库呢?🤔 -- Github:[https://github.com/settings/ssh/new](https://github.com/settings/ssh/new) -- Gitcode:[https://gitcode.net/-/profile/keys](https://gitcode.net/-/profile/keys) -- Gitee:[https://gitee.com/profile/sshkeys](https://gitee.com/profile/sshkeys) +### 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 工具,但也有一些第三方工具可供用户寻求特定于平台的体验。 @@ -122,150 +427,62 @@ Git 附带了用于提交 ( [git-gui](https://git-scm.com/docs/git-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命令操作。` + +--- -### 1. [起步](https://git-scm.com/book/zh/v2/起步-关于版本控制) - -- 1.1 [关于版本控制](https://git-scm.com/book/zh/v2/起步-关于版本控制) -- 1.2 [Git 简史](https://git-scm.com/book/zh/v2/起步-Git-简史) -- 1.3 [Git 是什么?](https://git-scm.com/book/zh/v2/起步-Git-是什么?) -- 1.4 [命令行](https://git-scm.com/book/zh/v2/起步-命令行) -- 1.5 [安装 Git](https://git-scm.com/book/zh/v2/起步-安装-Git) -- 1.6 [初次运行 Git 前的配置](https://git-scm.com/book/zh/v2/起步-初次运行-Git-前的配置) -- 1.7 [获取帮助](https://git-scm.com/book/zh/v2/起步-获取帮助) -- 1.8 [总结](https://git-scm.com/book/zh/v2/起步-总结) - -### 2. [Git 基础](https://git-scm.com/book/zh/v2/Git-基础-获取-Git-仓库) - -- 2.1 [获取 Git 仓库](https://git-scm.com/book/zh/v2/Git-基础-获取-Git-仓库) -- 2.2 [记录每次更新到仓库](https://git-scm.com/book/zh/v2/Git-基础-记录每次更新到仓库) -- 2.3 [查看提交历史](https://git-scm.com/book/zh/v2/Git-基础-查看提交历史) -- 2.4 [撤消操作](https://git-scm.com/book/zh/v2/Git-基础-撤消操作) -- 2.5 [远程仓库的使用](https://git-scm.com/book/zh/v2/Git-基础-远程仓库的使用) -- 2.6 [打标签](https://git-scm.com/book/zh/v2/Git-基础-打标签) -- 2.7 [Git 别名](https://git-scm.com/book/zh/v2/Git-基础-Git-别名) -- 2.8 [总结](https://git-scm.com/book/zh/v2/Git-基础-总结) - -### 3. [Git 分支](https://git-scm.com/book/zh/v2/Git-分支-分支简介) - -- 3.1 [分支简介](https://git-scm.com/book/zh/v2/Git-分支-分支简介) -- 3.2 [分支的新建与合并](https://git-scm.com/book/zh/v2/Git-分支-分支的新建与合并) -- 3.3 [分支管理](https://git-scm.com/book/zh/v2/Git-分支-分支管理) -- 3.4 [分支开发工作流](https://git-scm.com/book/zh/v2/Git-分支-分支开发工作流) -- 3.5 [远程分支](https://git-scm.com/book/zh/v2/Git-分支-远程分支) -- 3.6 [变基](https://git-scm.com/book/zh/v2/Git-分支-变基) -- 3.7 [总结](https://git-scm.com/book/zh/v2/Git-分支-总结) - -### 4. [服务器上的 Git](https://git-scm.com/book/zh/v2/服务器上的-Git-协议) - -- 4.1 [协议](https://git-scm.com/book/zh/v2/服务器上的-Git-协议) -- 4.2 [在服务器上搭建 Git](https://git-scm.com/book/zh/v2/服务器上的-Git-在服务器上搭建-Git) -- 4.3 [生成 SSH 公钥](https://git-scm.com/book/zh/v2/服务器上的-Git-生成-SSH-公钥) -- 4.4 [配置服务器](https://git-scm.com/book/zh/v2/服务器上的-Git-配置服务器) -- 4.5 [Git 守护进程](https://git-scm.com/book/zh/v2/服务器上的-Git-Git-守护进程) -- 4.6 [Smart HTTP](https://git-scm.com/book/zh/v2/服务器上的-Git-Smart-HTTP) -- 4.7 [GitWeb](https://git-scm.com/book/zh/v2/服务器上的-Git-GitWeb) -- 4.8 [GitLab](https://git-scm.com/book/zh/v2/服务器上的-Git-GitLab) -- 4.9 [第三方托管的选择](https://git-scm.com/book/zh/v2/服务器上的-Git-第三方托管的选择) -- 4.10 [总结](https://git-scm.com/book/zh/v2/服务器上的-Git-总结) - -### 5. [分布式 Git](https://git-scm.com/book/zh/v2/分布式-Git-分布式工作流程) - -- 5.1 [分布式工作流程](https://git-scm.com/book/zh/v2/分布式-Git-分布式工作流程) -- 5.2 [向一个项目贡献](https://git-scm.com/book/zh/v2/分布式-Git-向一个项目贡献) -- 5.3 [维护项目](https://git-scm.com/book/zh/v2/分布式-Git-维护项目) -- 5.4 [总结](https://git-scm.com/book/zh/v2/分布式-Git-总结) - -### 6. [GitHub](https://git-scm.com/book/zh/v2/GitHub-账户的创建和配置) - -- 6.1 [账户的创建和配置](https://git-scm.com/book/zh/v2/GitHub-账户的创建和配置) -- 6.2 [对项目做出贡献](https://git-scm.com/book/zh/v2/GitHub-对项目做出贡献) -- 6.3 [维护项目](https://git-scm.com/book/zh/v2/GitHub-维护项目) -- 6.4 [管理组织](https://git-scm.com/book/zh/v2/GitHub-管理组织) -- 6.5 [脚本 GitHub](https://git-scm.com/book/zh/v2/GitHub-脚本-GitHub) -- 6.6 [总结](https://git-scm.com/book/zh/v2/GitHub-总结) - -### 7. [Git 工具](https://git-scm.com/book/zh/v2/Git-工具-选择修订版本) - -- 7.1 [选择修订版本](https://git-scm.com/book/zh/v2/Git-工具-选择修订版本) -- 7.2 [交互式暂存](https://git-scm.com/book/zh/v2/Git-工具-交互式暂存) -- 7.3 [贮藏与清理](https://git-scm.com/book/zh/v2/Git-工具-贮藏与清理) -- 7.4 [签署工作](https://git-scm.com/book/zh/v2/Git-工具-签署工作) -- 7.5 [搜索](https://git-scm.com/book/zh/v2/Git-工具-搜索) -- 7.6 [重写历史](https://git-scm.com/book/zh/v2/Git-工具-重写历史) -- 7.7 [重置揭密](https://git-scm.com/book/zh/v2/Git-工具-重置揭密) -- 7.8 [高级合并](https://git-scm.com/book/zh/v2/Git-工具-高级合并) -- 7.9 [Rerere](https://git-scm.com/book/zh/v2/Git-工具-Rerere) -- 7.10 [使用 Git 调试](https://git-scm.com/book/zh/v2/Git-工具-使用-Git-调试) -- 7.11 [子模块](https://git-scm.com/book/zh/v2/Git-工具-子模块) -- 7.12 [打包](https://git-scm.com/book/zh/v2/Git-工具-打包) -- 7.13 [替换](https://git-scm.com/book/zh/v2/Git-工具-替换) -- 7.14 [凭证存储](https://git-scm.com/book/zh/v2/Git-工具-凭证存储) -- 7.15 [总结](https://git-scm.com/book/zh/v2/Git-工具-总结) - -### 8. [自定义 Git](https://git-scm.com/book/zh/v2/自定义-Git-配置-Git) - -- 8.1 [配置 Git](https://git-scm.com/book/zh/v2/自定义-Git-配置-Git) -- 8.2 [Git 属性](https://git-scm.com/book/zh/v2/自定义-Git-Git-属性) -- 8.3 [Git 钩子](https://git-scm.com/book/zh/v2/自定义-Git-Git-钩子) -- 8.4 [使用强制策略的一个例子](https://git-scm.com/book/zh/v2/自定义-Git-使用强制策略的一个例子) -- 8.5 [总结](https://git-scm.com/book/zh/v2/自定义-Git-总结) - -### 9. [Git 与其他系统](https://git-scm.com/book/zh/v2/Git-与其他系统-作为客户端的-Git) - -- 9.1 [作为客户端的 Git](https://git-scm.com/book/zh/v2/Git-与其他系统-作为客户端的-Git) -- 9.2 [迁移到 Git](https://git-scm.com/book/zh/v2/Git-与其他系统-迁移到-Git) -- 9.3 [总结](https://git-scm.com/book/zh/v2/Git-与其他系统-总结) - -### 10. [Git 内部原理](https://git-scm.com/book/zh/v2/Git-内部原理-底层命令与上层命令) - -- 10.1 [底层命令与上层命令](https://git-scm.com/book/zh/v2/Git-内部原理-底层命令与上层命令) -- 10.2 [Git 对象](https://git-scm.com/book/zh/v2/Git-内部原理-Git-对象) -- 10.3 [Git 引用](https://git-scm.com/book/zh/v2/Git-内部原理-Git-引用) -- 10.4 [包文件](https://git-scm.com/book/zh/v2/Git-内部原理-包文件) -- 10.5 [引用规范](https://git-scm.com/book/zh/v2/Git-内部原理-引用规范) -- 10.6 [传输协议](https://git-scm.com/book/zh/v2/Git-内部原理-传输协议) -- 10.7 [维护与数据恢复](https://git-scm.com/book/zh/v2/Git-内部原理-维护与数据恢复) -- 10.8 [环境变量](https://git-scm.com/book/zh/v2/Git-内部原理-环境变量) -- 10.9 [总结](https://git-scm.com/book/zh/v2/Git-内部原理-总结) - -### A1. [附录 A: 在其它环境中使用 Git](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-图形界面) - -- A1.1 [图形界面](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-图形界面) -- A1.2 [Visual Studio 中的 Git](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-Visual-Studio-中的-Git) -- A1.3 [Visual Studio Code 中的 Git](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-Visual-Studio-Code-中的-Git) -- A1.4 [Eclipse 中的 Git](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-Eclipse-中的-Git) -- A1.5 [IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-IntelliJ-%2F-PyCharm-%2F-WebStorm-%2F-PhpStorm-%2F-RubyMine-中的-Git) -- A1.6 [Sublime Text 中的 Git](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-Sublime-Text-中的-Git) -- A1.7 [Bash 中的 Git](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-Bash-中的-Git) -- A1.8 [Zsh 中的 Git](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-Zsh-中的-Git) -- A1.9 [Git 在 PowerShell 中使用 Git](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-Git-在-PowerShell-中使用-Git) -- A1.10 [总结](https://git-scm.com/book/zh/v2/附录-A%3A-在其它环境中使用-Git-总结) - -### A2. [附录 B: 在你的应用中嵌入 Git](https://git-scm.com/book/zh/v2/附录-B%3A-在你的应用中嵌入-Git-命令行-Git-方式) - -- A2.1 [命令行 Git 方式](https://git-scm.com/book/zh/v2/附录-B%3A-在你的应用中嵌入-Git-命令行-Git-方式) -- A2.2 [Libgit2](https://git-scm.com/book/zh/v2/附录-B%3A-在你的应用中嵌入-Git-Libgit2) -- A2.3 [JGit](https://git-scm.com/book/zh/v2/附录-B%3A-在你的应用中嵌入-Git-JGit) -- A2.4 [go-git](https://git-scm.com/book/zh/v2/附录-B%3A-在你的应用中嵌入-Git-go-git) -- A2.5 [Dulwich](https://git-scm.com/book/zh/v2/附录-B%3A-在你的应用中嵌入-Git-Dulwich) - -### A3. [附录 C: Git 命令](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-设置与配置) - -- A3.1 [设置与配置](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-设置与配置) -- A3.2 [获取与创建项目](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-获取与创建项目) -- A3.3 [快照基础](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-快照基础) -- A3.4 [分支与合并](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-分支与合并) -- A3.5 [项目分享与更新](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-项目分享与更新) -- A3.6 [检查与比较](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-检查与比较) -- A3.7 [调试](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-调试) -- A3.8 [补丁](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-补丁) -- A3.9 [邮件](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-邮件) -- A3.10 [外部系统](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-外部系统) -- A3.11 [管理](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-管理) -- A3.12 [底层命令](https://git-scm.com/book/zh/v2/附录-C%3A-Git-命令-底层命令) +
+
+- 中文PDF,直接深度学习Git操作!下载:**微信公众号「bugstack虫洞栈」回复「gitbook」** 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/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 index 73109eb15..e069bde8c 100644 --- a/docs/md/road-map/guava.md +++ b/docs/md/road-map/guava.md @@ -14,8 +14,8 @@ lock: need 本文涉及的工程: -- xfg-dev-tech-guava:[https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-fastjson](https://gitcode.net/KnowledgePlanet/road-map/xfg-dev-tech-guava) -- Github:[https://github.com/google/guava/wiki](https://github.com/google/guava/wiki) +- 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) ## 一、组件介绍 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 index 46a1d9460..ff004b0fc 100644 --- a/docs/md/road-map/http.md +++ b/docs/md/road-map/http.md @@ -10,7 +10,7 @@ lock: need > 沉淀、分享、成长,让自己和他人都能有所收获!😄 - + 本文的宗旨在于通过简单干净实践的方式教会读者,HTTP的常用框架使用,HTTP接口快速对接方式。以及在编码实战中练习 HTTP 对数据的采集、ChatGLM对接、问题回答。这样的场景学习,非常适合以后大家在做一些智能化问答进行参考使用。 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 index a64747fe3..cbf900a70 100644 --- a/docs/md/road-map/intellij-idea.md +++ b/docs/md/road-map/intellij-idea.md @@ -70,6 +70,8 @@ IntelliJ IDEA 除了自身的功能强大以外,还可以安装各类提效的 - 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 这款用于对象转换的插件,就是小傅哥开发的。如果你有非常好用的插件,也可以在本文下修改提交。 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/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 index 9ab8dbcd9..8f3d6027e 100644 --- a/docs/md/road-map/mvc.md +++ b/docs/md/road-map/mvc.md @@ -1,5 +1,5 @@ --- -title: MVC 架构 +title: MVC 架构设计 lock: need --- diff --git a/docs/md/road-map/mvc2ddd.md b/docs/md/road-map/mvc2ddd.md index 135b45938..c938986fc 100644 --- a/docs/md/road-map/mvc2ddd.md +++ b/docs/md/road-map/mvc2ddd.md @@ -7,6 +7,7 @@ lock: need 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) +
项目:[大营销平台系统](https://bugstack.cn/md/project/big-market/ddd.html) - DDD 领域驱动设计,战略、战术、战役,落地指引规范。 > 沉淀、分享、成长,让自己和他人都能有所收获!😄 @@ -14,7 +15,15 @@ lock: need 大家好,我是技术UP主小傅哥。**MVC讲解了,DDD讲解了。接下来这个章节,我们讲讲从MVC到DDD的重构!** -**MVC 旧工程腐化严重,迭代成本太高。DDD 新工程全部重构,步子扯的太大。** 这是现阶段在工程体系化治理中,我们所面临的最大问题;`既想运用 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 的思想循序渐进重构现有工程,又想不破坏原有的工程体系结构以保持新需求的承接效率。`
@@ -34,25 +43,25 @@ lock: need 而 DDD 架构的模型分层,则是以人为视角,**一个人就是一个领域,一个领域内包括他所需的衣服、裤子、袜子、鞋子**。虽然刚开始有点浪费空间,但随着软件的长周期发展,后续的维护成本就会降低。 -那么,接下来我们就着重看以下,从 MVC 到 DDD 的轻量化重构应该怎么做。🍻 +那么,接下来我们就着重看一下,从 MVC 到 DDD 的轻量化重构应该怎么做。🍻 >文章后面,含有 MVC 到 DDD 重构编码实践讲解。此文也是 MVC、DDD 的架构编码指导经验说明。 ## 一、能学到啥 -本文是偏实战可落地的 DDD 知识分享,也是从 MVC 到 DDD 的可落地方案讲解。在本文中会介绍 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 分层架构,这些东西会被归类的特别清晰。 +5. 一整套实战开源课程;讲解在 DDD 架构中,各项技术栈:Dubbo、MQ、Redis、Zookeeper - 配置中心等的分层使用。—— 否则你可能都不知道一个 MQ 消息发送要放在哪里。有了 DDD 分层架构,这些东西会被归类的特别清晰。 -此外,除了这些碎片化的知识学习,还有应用级实战项目锻炼;Lottery DDD 架构设计、ChatGPT 新DDD架构设计、API网关 会话设计 - 学习架构能力和编程思维,以及高端的编码技巧。 +此外,除了这些碎片化的知识学习,还有应用级实战项目锻炼:Lottery DDD 架构设计、ChatGPT 新DDD架构设计、API网关 会话设计 - 学习架构能力和编程思维,以及高端的编码技巧。 ## 二、架构分层(DDD) -在 DDD 架构分层中,domain 模块最重要的,也是最大的那个。所有的其他模块都要围着它转。所有 domian 下的各个领域模块,都包含着一组完整的;model - 模型对象、service - 服务处理,以及在有需要操作数据库时,再引入对应的 IRepository - 仓储服务。这个 domain 的实现,就像是实现了一个炸药包,炸药包的火药、引线、包布等都是一个个物料被封装到一起使用。 +在 DDD 架构分层中,domain 模块最重要的,也是最大的那个。所有的其他模块都要围着它转。所有 domain 下的各个领域模块,都包含着一组完整的:model - 模型对象、service - 服务处理,以及在有需要操作数据库时,再引入对应的 IRepository - 仓储服务。这个 domain 的实现,就像是实现了一个炸药包,炸药包的火药、引线、包布等都是一个个物料被封装到一起使用。 如下是 DDD 架构所呈现出的一种四层架构分层,可能和一些其他的 DDD 分层略有差异,但核心的重点结构是不变的。尤其是 domain 领域、infrastructure 基础,是任何一个 DDD 架构分层都需要有的分层模块。 @@ -66,38 +75,38 @@ lock: need - **领域编排【可选】 - case**:领域编排层,一般对于较大且复杂的的项目,为了更好的防腐和提供通用的服务,一般会添加 case/application 层,用于对 domain 领域的逻辑进行封装组合处理。但对于一些小项目来说,完全可以去掉这一层。少量一层对象转换,代码的维护成本会降低很多。 - **领域封装 - domain**:领域模型服务,是一个非常重要的模块。无论怎么做DDD的分层架构,domain 都是肯定存在的。在一层中会有一个个细分的领域服务,在每个服务包中会有【模型、仓库、服务】这样3部分。 - **仓储服务 - infrastructure**:基础层依赖于 domain 领域层,因为在 domain 层定义了仓储接口需要在基础层实现。这是依赖倒置的一种设计方式。所有的仓储、接口、事件消息,都可以通过依赖倒置的方式进行调用。 -- **类型定义 - gateway**:对于外部接口的调用,也可以从基础设施层分离一个专门的 gateway 网关层,来封装外部 RPC/HTTP 等类型接口的调用。 -- **类型定义 - types**:通用类型定义层,在我们的系统开发中,会有很多类型的定义,包括;基本的 Response、Constants 和枚举。它会被其他的层进行引用使用。(这一层没有画到图中) +- **外部接口 - gateway**:对于外部接口的调用,也可以从基础设施层分离一个专门的 gateway 网关层,来封装外部 RPC/HTTP 等类型接口的调用。 +- **类型定义 - types**:通用类型定义层,在我们的系统开发中,会有很多类型的定义,包括:基本的 Response、Constants 和枚举。它会被其他的层进行引用使用。(这一层没有画到图中) -综上就是 DDD 架构思想下的工程分层模型结构,DDD 架构的领域驱动设计的重点包括;结构边界更加清晰、重视上下文调用、分离业务功能与基础支撑。总之一句话,就是各司其职。那么鉴于如此清晰工程结构,该如何将旧存工程,MVC 转向 DDD 呢?接下来就重点介绍下。 +综上就是 DDD 架构思想下的工程分层模型结构,DDD 架构的领域驱动设计的重点包括:结构边界更加清晰、重视上下文调用、分离业务功能与基础支撑。总之一句话,就是各司其职。那么鉴于如此清晰工程结构,该如何将旧存工程,MVC 转向 DDD 呢?接下来就重点介绍下。 ## 三、工程重构(MVC->DDD) -经过实践验证,不需要太高成本,MVC 就可以天然的向 DDD 工程分层的模型结构转变。重点是不改变原有的工程模块的依赖关系,将贫血的 domain 对象层,设计为充血的结构。**对于 domain 原本在 MVC 分层结构中,就是一个被依赖层,恰好可以与其他层做依赖倒置的设计方案处理**。具体如图所示; +经过实践验证,不需要太高成本,MVC 就可以天然的向 DDD 工程分层的模型结构转变。重点是不改变原有的工程模块的依赖关系,将贫血的 domain 对象层,设计为充血的结构。**对于 domain 原本在 MVC 分层结构中,就是一个被依赖层,恰好可以与其他层做依赖倒置的设计方案处理**。具体如图所示:
-左侧是我们常见的 MVC 分层结构,右侧是给大家上文讲解过的 DDD 分层结构。从 MVC 到 DDD 的映射,使用了相同颜色进行标注。之后我来介绍一些细节; +左侧是我们常见的 MVC 分层结构,右侧是给大家上文讲解过的 DDD 分层结构。从 MVC 到 DDD 的映射,使用了相同颜色进行标注。之后我来介绍一些细节: 在 MVC 分层结构中,所有的逻辑都集中在 service 层,也是文中提到的腐化最严重的层,要治理的也是这一层。所以首先我们要将 service 里的功能进行拆解。 1. service 中具备领域特性的服务实现,抽离到原本贫血模型的 domain 中。在 domain 分层中添加 xxx、yyy、zzz 分层领域包,分别实现不同功能。**注意每个分层领域包内都具备完整的 DDD 领域服务内所需的模块** -2. service 中的基础功能组件,如;缓存Redis、配置中心等,迁移到 dao 层。这里我们把 dao 层看做为基础设施层。它与 domain 领域层的调用关系,为依赖倒置。也就是 domain 层定义接口,dao 层依赖于 domain 定义的接口,做依赖倒置实现接口。 +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 分层结构中的 export 层是 RPC 接口定义层,由 web 层实现。web 是对 service 的调用。也就是 DDD 分层结构中调用 application 编排好的服务。这部分无需改动。**但如果你原有工程把 domain 也暴露出去了,则需要把对应的包迁移到 export** 因为 domain 包有太多的核心对象和属性,还包括数据库持久化对象。这些都不应该被暴露。 -MVC 分层中,因为有需要对外部 RPC 接口的调用,所以会单独有一层 RPC 来封装其他服务的接口。这一层被 domain 领域使用层,可以定义 adapter 适配器接口,通过依赖倒置,在 rpc 层实现 domain 层定义的调用接口。 +MVC 分层中,因为有需要对外部 RPC 接口的调用,所以会单独有一层 RPC 来封装其他服务的接口。这一层被 domain 领域层使用,可以定义 adapter 适配器接口,通过依赖倒置,在 rpc 层实现 domain 层定义的调用接口。 此外 dao 层,在 MVC 结构中原本是比较单一的。但经过改造后会需要把基础的 Redis 使用、配置中使用,都迁移到 dao 层。因为原本在 service 层的话,domain 层是调用不到的这些基础服务的,而且也不符合服务功能边界的划分。 **综上**,就是从 MVC 到 DDD 重构架构的拆解实现方案。这是一种最低成本的最佳实施策略,完全可以保证 MVC 的结构,又可以应用上 DDD 的架构分层优势。也能运用 DDD 领域驱动设计思想,重构旧代码,增加可维护性。 -到这里,分层结构问题我们说清楚了。从 MVC 调整结构到 DDD 后,工程模型中的调用链路关系是什么样呢?接下来我们在展开架构,看细节关系。 +到这里,分层结构问题我们说清楚了。从 MVC 调整结构到 DDD 后,工程模型中的调用链路关系是什么样呢?接下来我们再展开架构,看细节关系。 ## 四、分层调用链路 @@ -111,35 +120,35 @@ MVC 分层中,因为有需要对外部 RPC 接口的调用,所以会单独 当进入领域层开始,也是智力集中体现的开始了。所有你对工程的抽象能力,都在这一块区域体现。 -接下来我们着种介绍下领域层和基础层的模块职责功能;**图中下方是对象的流转,可以注意下。** +接下来我们着重介绍下领域层和基础层的模块职责功能;**图中下方是对象的流转,可以注意下。** ### 1. 领域服务层 -我们可以当 domain 领域层为一个充血模型结构,在一个 domain 领域层中,可以有多个领域包。当然理想状态下,如果你的 DDD 拆分的特别干净的新工程,那么可能一个 domain 就一个领域。但大部分时候微服务的拆分鉴于成本考虑不会那么细,还有一些老工程的重构,都是一个工程内有多个领域,对应的解决方案是在一个工程下建多个同级分层包。比如;账户领域包、授信领域包、结算领域包等,每个包内聚合实现不同的功能。 +我们可以当 domain 领域层为一个充血模型结构,在一个 domain 领域层中,可以有多个领域包。当然理想状态下,如果你的 DDD 拆分的特别干净的新工程,那么可能一个 domain 就一个领域。但大部分时候微服务的拆分鉴于成本考虑不会那么细,还有一些老工程的重构,都是一个工程内有多个领域,对应的解决方案是在一个工程下建多个同级分层包。比如:账户领域包、授信领域包、结算领域包等,每个包内聚合实现不同的功能。 -每一个 domain 下的领域包内,都包括;model 模型、仓储、接口、事件和服务的处理。 +每一个 domain 下的领域包内,都包括:model 模型、仓储、接口、事件和服务的处理。 -model 模型对象; +model 模型对象 - aggreate:聚合对象,实体对象、值对象的协同组织,就是聚合对象。 - entity:实体对象,大多数情况下,实体对象(Entity)与数据库持久化对象(PO)是1v1的关系,但也有为了封装一些属性信息,会出现1vn的关系。 - valobj:值对象,通过对象属性值来识别的对象 By 《实现领域驱动设计》 -repository 仓储服务;从数据库等数据源中获取数据,传递的对象可以是聚合对象、实体对象,返回的结果可以是;实体对象、值对象。因为仓储服务是由基础层(infrastructure) 引用领域层(domain),是一种依赖倒置的结构,但它可以天然的隔离PO数据库持久化对象被引用。 +repository 仓储服务:从数据库等数据源中获取数据,传递的对象可以是聚合对象、实体对象,返回的结果可以是:实体对象、值对象。因为仓储服务是由基础层(infrastructure) 引用领域层(domain),是一种依赖倒置的结构,但它可以天然的隔离PO数据库持久化对象被引用。 -adapter 接口服务;是依赖于外包的其他 HTTP/RPC 接口的封装调用,通过在 domain 领域层定义适配器接口,再有依赖于 domain 的基础层设施层或者一个单独的专门处理接口的额外分层,来实现 domain 定义的适配器接口,完成对依赖的 HTTP/RPC 进行封装处理。 +adapter 接口服务:是依赖于外包的其他 HTTP/RPC 接口的封装调用,通过在 domain 领域层定义适配器接口,再由依赖于 domain 的基础层设施层或者一个单独的专门处理接口的额外分层,来实现 domain 定义的适配器接口,完成对依赖的 HTTP/RPC 进行封装处理。 -event 事件消息;在服务实现中,进行会有业务完成后,对外发送消息的情况。这个时候,可以在领域模型中定义事件消息的接口,再有基础设施层完成消息的推送。 +event 事件消息:在服务实现中,经常会有业务完成后,对外发送消息的情况。这个时候,可以在领域模型中定义事件消息的接口,再由基础设施层完成消息的推送。 -service 服务设计;这里要注意,不要以定义了聚合对象,就把超越1个对象以外的逻辑,都封装到聚合中,这会让你的代码后期越来越难维护。聚合更应该注重的是和本对象相关的单一简单封装场景,而把一些重核心业务方到 service 里实现。**此外;如果你的设计模式应用不佳,那么无论是领域驱动设计、测试驱动设计还是换了三层和四层架构,你的工程质量依然会非常差。** +service 服务设计:这里要注意,不要以为定义了聚合对象,就把超越1个对象以外的逻辑,都封装到聚合中,这会让你的代码后期越来越难维护。聚合更应该注重的是和本对象相关的单一简单封装场景,而把一些重核心业务放到 service 里实现。**此外,如果你的设计模式应用不佳,那么无论是领域驱动设计、测试驱动设计还是换了三层和四层架构,你的工程质量依然会非常差。** ### 2. 基础设施层 `提供数据库持久化`、`提供Redis和配置中心数据支撑`、`提供事件消息推送`、`提供外部服务接口封装`。总之这一层的核心目的就是更好的辅助 domain 领域层完成领域功能的开发。 -而调用方式则为依赖倒置,也就是`领域服务层`定义接口,`基础设施层`做功能实现。这样可以有效的避免基础基础设施层中的对象被对外暴漏,如数据库持久化对象,在这样的分层结构中,天然的被保护在基础设置层中,外部是没法引入的,否则就循环依赖了。 +而调用方式则为依赖倒置,也就是`领域服务层`定义接口,`基础设施层`做功能实现。这样可以有效的避免基础设施层中的对象被对外暴露,如数据库持久化对象,在这样的分层结构中,天然的被保护在基础设施层中,外部是没法引入的,否则就循环依赖了。 -有了这一层以后,domain 层不会关系数据的细节处理。传递给基础设施层的方法中,会把聚合对象或实体对象通过接口方法传递下来。之后在基础设施层中完成数据事务的操作。也会含有事务处理后,写入Redis缓存和发送MQ消息。如果说有夸领域的事务,一般可能就是跨库表,这个时候要使用 MQ 事件的方式进行驱动。 +有了这一层以后,domain 层不会关心数据的细节处理。传递给基础设施层的方法中,会把聚合对象或实体对象通过接口方法传递下来。之后在基础设施层中完成数据事务的操作。也会含有事务处理后,写入Redis缓存和发送MQ消息。如果说有跨领域的事务,一般可能就是跨库表,这个时候要使用 MQ 事件的方式进行驱动。 ### 3. 类型对象层 @@ -154,23 +163,23 @@ service 服务设计;这里要注意,不要以定义了聚合对象,就把
从 MVC 到 DDD 我们只是换了一个更大、格局更清晰的房子🏡,但并不能决定你从 MVC 到 DDD 代码就变得非常干净、漂亮、整洁了。因为从 MVC 到 DDD 只是骨架变了,但骨架之下的血肉并没有改变。 -如果你仍是把原有的烂代码平移到新的分层架构中,就相当于把老房子里的破旧家具衣物鞋帽搬过来而已。所以依照与软件设计的原则;分治、抽象和知识,中的知识是设计原则和设计模式的运用。所以要想把代码写好,就一定是要把`DDD + 设计模式`,才能真的把代码写好。接下来,小傅哥再给大家举个使用模式在 DDD 分层结构中重构的案例。 +如果你仍是把原有的烂代码平移到新的分层架构中,就相当于把老房子里的破旧家具衣物鞋帽搬过来而已。所以依照于软件设计的原则:分治、抽象和知识,但知识是设计原则和设计模式的运用。所以要想把代码写好,就一定是要把`DDD + 设计模式`,才能真的把代码写好。接下来,小傅哥再给大家举个使用模式在 DDD 分层结构中重构的案例。 ## 六、重构现有代码 软件设计第一原则,康威定律所提到的,分治、抽象和知识,是用于系统设计和实现的指导说明。分治和抽象,我们可以用 DDD 思想映射的分层架构来处理,但知识则是设计原则和设计模式的运用。 -所以,如果没有合理的运用设计知识来对代码进行细化处理,那么即使拆分出流程边界在清晰的架构,也很难做出好维护的代码。而通常最常用的设计模式,无外乎;工厂、策略、模板的组合使用,少部分会用到责任链、建造者、组合模式。那么接下来,在分享一个带有流程的设计模式使用,让大家可以有一份可参考的工程代码设计。 +所以,如果没有合理的运用设计知识来对代码进行细化处理,那么即使拆分出流程边界再清晰的架构,也很难做出好维护的代码。而通常最常用的设计模式,无外乎:工厂、策略、模板的组合使用,少部分会用到责任链、建造者、组合模式。那么接下来,再分享一个带有流程的设计模式使用,让大家可以有一份可参考的工程代码设计。 ### 1. 场景设定 -这里我们做一个提额场景的设定。估计大家都用过信用卡💳,它有一个初始的额度,在后续的使用中会随着信用的积累和消费的增加,进行提高额度。而额度的提高则需要一系列的校验判断并最终做出提额处理。流程如下; +这里我们做一个提额场景的设定。估计大家都用过信用卡💳,它有一个初始的额度,在后续的使用中会随着信用的积累和消费的增加,进行提高额度。而额度的提高则需要一系列的校验判断并最终做出提额处理。流程如下:
-这样的流程图,是我们做业务开发的小伙伴,经常看到的。做一系列的流程判断处理,之后完成一个具体的功能。简单来说,就是 if···else 写代码,一条条的校验。但写着写着,时间一长就会发现代码变得特别混乱。最主要的原因就是,那些为了支撑完成业务的各类判断是不稳定因素,会随着业务的变化不断的调整。甚至有时候就直接下掉了。但你的代码就中多就了一条 `// 业务说暂时不使用,你也不敢删!`就像有首歌唱的🎤:**“需求依旧停在旷野上,你的代码被越拉越长。直到远去的马蹄声响,呼唤你的Bug传四方。”** +这样的流程图,是我们做业务开发的小伙伴,经常看到的。做一系列的流程判断处理,之后完成一个具体的功能。简单来说,就是 if···else 写代码,一条条的校验。但写着写着,时间一长就会发现代码变得特别混乱。最主要的原因就是,那些为了支撑完成业务的各类判断是不稳定因素,会随着业务的变化不断的调整。甚至有时候就直接下掉了。但你的代码中就多了一条 `// 业务说暂时不使用,你也不敢删!`就像有首歌唱的🎤:**“需求依旧停在旷野上,你的代码被越拉越长。直到远去的马蹄声响,呼唤你的Bug传四方。”** 所以对于这样的功能流程设计,怎么办呢?总不能让旷野的马蹄,一直拉着你的bug在奔袭。 @@ -209,10 +218,10 @@ service 服务设计;这里要注意,不要以定义了聚合对象,就把 -- 首先,定义一个受理调额的接口。因为额度的调整,包括;提额、降额。所以不要把名字写的太死。 +- 首先,定义一个受理调额的接口。因为额度的调整,包括:提额、降额。所以不要把名字写的太死。 - 之后,由抽象类实现接口。在抽象类中定义出整个调用链路关系,并把一些公用的数据类支撑逻辑,提到支撑类里。这和 Spring 的设计很像。 - 之后,因为规则校验这东西是为了支撑核心流程走下去的,而且还是随着业务频繁变动的。那就没必要在主线业务流程中,用 if···else 贴膏药的写代码,而是应该拆解出来。所以这里设计一个策略模式实现的规则校验,并通过工厂对外提供服务。 -- 最后,这些东西零件类的东西都处理好后。就可以在抽象类的子类实现中进行调用处理了。 +- 最后,这些零件类的东西都处理好后。就可以在抽象类的子类实现中进行调用处理了。 ### 5. 代码呈现 @@ -242,7 +251,7 @@ public AdjustAssetOrderEntity acceptAdjustAssetApply(AdjustAssetApplyEntity adju // 3.2 账户查询 UserAccountInfoDTO userAccountInfoDTO = queryJtAccount(adjustAssetApplyEntity.getUserId(), adjustAssetApplyEntity.getAccountType()); - // 3.3 基础校验;(1)账户类型、(2)状态状态、(3)额度类型、(4)账户逾期、(5)费率类型【暂无】 + // 3.3 基础校验:(1)账户类型、(2)状态状态、(3)额度类型、(4)账户逾期、(5)费率类型【暂无】 LogicCheckResultEntity logicCheckResultEntity = doCheckLogic(adjustAssetApplyEntity, userAccountInfoDTO, DefaultLogicFactory.LogicModel.ACCOUNT_TYPE_FILTER.getCode(), DefaultLogicFactory.LogicModel.ACCOUNT_STATUS_FILTER.getCode(), diff --git a/docs/md/road-map/mybatis.md b/docs/md/road-map/mybatis.md index 1b3838a1d..37ec011c2 100644 --- a/docs/md/road-map/mybatis.md +++ b/docs/md/road-map/mybatis.md @@ -12,7 +12,7 @@ lock: need -本文的宗旨在于通过简单干净实践的方式教会读者,使用 SpringBoot 配置 MyBatis 并完成对插入、批量插入、修改、查询以及注解事务和编程事务的使用,通过扩展插件开发对置顶字段进行加解处理。 +本文的宗旨在于通过简单干净实践的方式教会读者,使用 SpringBoot 配置 MyBatis 并完成对插入、批量插入、修改、查询以及注解事务和编程事务的使用,通过扩展插件开发对指定字段进行加解处理。 此外本文也通过此案例,渗透讲解 DDD 模型中的聚合对象、实体对象和值对象在领域模型中的实践。 @@ -47,7 +47,7 @@ lock: need -此场景的业务用于对指定的用户进行**晋升加薪调幅**,但因为加薪会需要操作3个表,包括;雇员表 - 修改个人Title、薪资表 - 修改薪酬、调薪记录表 - 每一次加薪都写一条记录。 +此场景的业务用于对指定的用户进行**晋升加薪调幅**,但因为加薪会需要操作3个表,包括:雇员表 - 修改个人Title、薪资表 - 修改薪酬、调薪记录表 - 每一次加薪都写一条记录。 ### 1. model @@ -124,7 +124,7 @@ public class AdjustSalaryApplyOrderAggregate { private String orderId; /** 雇员实体 */ private EmployeeEntity employeeEntity; - /** 雇员实体 */ + /** 雇员调薪实体 */ private EmployeeSalaryAdjustEntity employeeSalaryAdjustEntity; } @@ -208,7 +208,7 @@ public interface IEmployeeDAO { ``` -- 使用配置文件的方式比较好维护,当然如果也可以尝试使用 MyBatis 提供的注解方式,完成数据的操作。 +- 使用配置文件的方式比较好维护,当然也可以尝试使用 MyBatis 提供的注解方式,完成数据的操作。 ### 2. 事务&注解编程 @@ -438,7 +438,7 @@ public class FieldEncryptionAndDecryptionMybatisPlugin implements Interceptor { ```
- +
- 首先通过注解配置,拦截指定范围内的信息 `Intercepts` 之后在 intercept 接口实现方法中,获取 MappedStatement 这个 MyBatis的映射核心类。[**《手写MyBatis:渐进式源码实践》**](https://item.jd.com/13811216.html) - 跟小傅哥学MyBatis,从零手写源码级复杂项目,提升架构思维与设计逻辑,锻炼编码能力! 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/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/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 index 198531a1c..05872d792 100644 --- a/docs/md/road-map/portainer.md +++ b/docs/md/road-map/portainer.md @@ -1,15 +1,17 @@ --- -title: Portainer +title: Docker 管理面板(Portainer) lock: need --- -# Portainer 环境配置 +# Docker 管理面板(Portainer) 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) > 沉淀、分享、成长,让自己和他人都能有所收获!😄 + + - 官网:[https://www.portainer.io/](https://www.portainer.io/) - 介绍:在任何数据中心、云、网络边缘或 IIOT 设备的 Kubernetes、Docker、Swarm 和 Nomad 上,在几分钟内部署、配置、故障排除和保护容器。 @@ -29,15 +31,20 @@ Status: Downloaded newer image for portainer/portainer:latest docker.io/portainer/portainer:latest ``` -- docker pull portainer/portainer +- 默认镜像:`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/](#) 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 index 4da9828ce..ec731f695 100644 --- a/docs/md/road-map/quartz.md +++ b/docs/md/road-map/quartz.md @@ -12,7 +12,7 @@ lock: need -本文的宗旨在于通过简单干净实践的方式教会读者,多种类型的任务执行组件使用案例,包括;Quartz 使用、扩展 Spring-Schedule 自动增加任务、XXL-Job 分布式任务调度。其中像 Spring-Schedule 小傅哥还添加了一些 Spring 组件开发的能力可自动扩展任务、对 XXL-Job 的配置引入了 Docker Compose 自动化安装和自动初始化 MySQL 数据库 xxl-job.sql 库表数据。这些都是为了让你在不同的场景选择合适的框架,同时也能更简单的使用这些框架。 +本文的宗旨在于通过简单干净实践的方式教会读者,多种类型的任务执行组件使用案例,包括:Quartz 使用、扩展 Spring-Schedule 自动增加任务、XXL-Job 分布式任务调度。其中像 Spring-Schedule 小傅哥还添加了一些 Spring 组件开发的能力可自动扩展任务、对 XXL-Job 的配置引入了 Docker Compose 自动化安装和自动初始化 MySQL 数据库 xxl-job.sql 库表数据。这些都是为了让你在不同的场景选择合适的框架,同时也能更简单的使用这些框架。 本章节的任务调度组件会放到 DDD 的 Trigger 模块中,也就是触发器层。我们认为所有的调用行为,HTTP、RPC、MQ、任务,都是一个触发的入口,所以对于任务调度也放到这一层使用。 @@ -23,7 +23,7 @@ lock: need ## 一、案例背景 -任务调度是一个非常重要的功能组件,常作用于;定时清理数据 - 冷数据迁移、活动状态扫描 - 过期活动关闭、消息发送补偿 - MQ失败重发、支付掉单补偿 - 支付幂等重试,等各类场景都会用到任务调度组件。它可以帮我们执行确定规则的业务或功能流程。 +任务调度是一个非常重要的功能组件,常作用于:定时清理数据 - 冷数据迁移、活动状态扫描 - 过期活动关闭、消息发送补偿 - MQ失败重发、支付掉单补偿 - 支付幂等重试,等各类场景都会用到任务调度组件。它可以帮我们执行确定规则的业务或功能流程。
@@ -118,9 +118,9 @@ services:
-- 在 IDEA 中打开 rocketmq-docker-compose-mac-amd-arm.yml 你会看到一个绿色的按钮在左侧侧边栏,点击即可安装。或者你也可以使用命令安装:`# /usr/local/bin/docker-compose -f /docs/xxl-job/xxl-job-docker-compose.yml up -d` - 比较适合在云服务器上执行。 +- 在 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` 标签引入。 +- 标签:`depends_on` - 依赖于谁先安装、`MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'` - 可以设置MySQL无密码安装、`mysql-job-dbdata` - 一个启动安装数据库初始化脚本的镜像。并且需要在 MySQL 安装时使用 `volumes_from` 标签引入。 ### 2. 访问 xxl-job @@ -205,7 +205,7 @@ public class XXLJob { ``` -- 分别包括;Quartz、XXL-Job 两个组件 +- 分别包括:Quartz、XXL-Job 两个组件 **添加配置** 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/road-map.md b/docs/md/road-map/road-map.md index f6ceb93eb..a8a6d4283 100644 --- a/docs/md/road-map/road-map.md +++ b/docs/md/road-map/road-map.md @@ -55,4 +55,4 @@ lock: no 如果你需要;`简明学习路线`、`实战项目锻炼`、`帮你学习辅导`、`教你简历优化` - 来应对招聘,那么可以扫码加入小傅哥的知识星球【码农会锁】- 我会带着走捷径直击目标,完成实战项目,提高编程思维,锻炼编码能力。 -![](https://bugstack.cn/images/article/zsxq/zsxq-xuanchuan.png) +>[🧧加入学习](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 index 56dd28b16..59e4a36a6 100644 --- a/docs/md/road-map/rocketmq.md +++ b/docs/md/road-map/rocketmq.md @@ -24,7 +24,7 @@ lock: need ## 一、案例背景 -首先我们要知道,MQ 消息的作用是用于;`解耦过长的业务流程`和`应对流量冲击的消峰`。如;用户下单支付完成后,拿到支付消息推动后续的发货流程。也可以是我们基于 [《MyBatis 使用教程和插件开发》](https://bugstack.cn/md/road-map/mybatis.html) 中的案例场景,给雇员提升级别和薪资的时候,也发送一条MQ消息,用于发送邮件通知给用户。 +首先我们要知道,MQ 消息的作用是用于:`解耦过长的业务流程`和`应对流量冲击的消峰`。如:用户下单支付完成后,拿到支付消息推动后续的发货流程。也可以是我们基于 [《MyBatis 使用教程和插件开发》](https://bugstack.cn/md/road-map/mybatis.html) 中的案例场景,给雇员提升级别和薪资的时候,也发送一条MQ消息,用于发送邮件通知给用户。
@@ -43,9 +43,9 @@ lock: need
-- 首先,我们需要在领域模型层,添加一块 event 区域。它的存在是为了定义出于当前领域下所需的事件消息信息。信息的类型可以是model 下的实体对象、聚合对象。 +- 首先,我们需要在领域模型层,添加一块 event 区域。它的存在是为了定义出在当前领域下所需的事件消息信息。信息的类型可以是model 下的实体对象、聚合对象。 - 之后,消息的发送是放在基础设置层。本身基础设置层就是依赖倒置于模型层,所以在模型层所定义的 event 对象,可以很方便的在基础设置层使用。而且大部分开发的时候,MQ消息的发送与数据库操作都是关联的,采用的方式是,做完数据落库后,推送MQ消息。所以定义在仓储中实现,会更加得心应手、水到渠成。 -- 最后,就是 MQ 的消息,MQ 的消费可以是自身服务所发出的消息,也可以是外部其他微服务的消息。就在小傅哥所整体讲述的这套简明教程中 DDD 部分的触发器层。 +- 最后,就是 MQ 的消费,MQ 的消费可以是自身服务所发出的消息,也可以是外部其他微服务的消息。就在小傅哥所整体讲述的这套简明教程中 DDD 部分的触发器层。 ## 三、环境安装 @@ -55,7 +55,7 @@ lock: need -这里主要介绍 RocketMQ 的安装; +这里主要介绍 RocketMQ 的安装: ### 1. 执行 compose yml @@ -65,11 +65,11 @@ lock: need 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.0 + image: livinphp/rocketmq:5.1.3 container_name: rocketmq ports: - 9009:9009 @@ -87,7 +87,7 @@ services: - 在 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. 修改默认配合 +### 2. 修改默认配置 1. 打开 `data/rocketmq/conf/broker.conf` 添加一条 `brokerIP1=127.0.0.1` 在结尾 @@ -162,7 +162,7 @@ RocketMQ 此镜像,会在安装后在控制台打印登录账号信息,你 - MQ 的使用无论是 RocketMQ 还是 Kafka 等,都很简单。但在使用之前,要考虑好怎么在架构中合理的使用。如果最初没有定义好这些,那么胡乱的任何地方都能发送和接收MQ,最后的工程将非常难以维护。 -- 所以这里整个MQ的生产和消费,是按照整个 DDD 领域事件结构进行设计。分为在 domain 使用基础层生产消息,再有 trigger 层接收消息。 +- 所以这里整个MQ的生产和消费,是按照整个 DDD 领域事件结构进行设计。分为在 domain 使用基础层生产消息,再由 trigger 层接收消息。 ### 2. 配置文件 @@ -199,7 +199,7 @@ rocketmq: sendMessageTimeout: 10000 # 发送消息失败重试次数,默认2 retryTimesWhenSendFailed: 2 - # 异步消息重试此处,默认2 + # 异步消息重试次数,默认2 retryTimesWhenSendAsyncFailed: 2 # 消息最大长度,默认1024 * 1024 * 4(默认4M) maxMessageSize: 4096 @@ -311,7 +311,7 @@ public String adjustSalary(AdjustSalaryApplyOrderAggregate adjustSalaryApplyOrde } ``` -在 SalaryAdjustRepository 仓储的实现中,做完业务流程开始发送 MQ 消息。这里有2点要注意; +在 SalaryAdjustRepository 仓储的实现中,做完业务流程开始发送 MQ 消息。这里有2点要注意: 1. 消息发送,不要写在数据库事务中。因为事务一直占用数据库连接,需要快速释放。 2. 对于一些强MQ要求的场景,需要在发送MQ前,写入一条数据库 Task 记录,发送消息后更新 Task 状态为成功。如果长时间未更新数据库状态或者为失败的,则需要由任务补偿进行处理。 @@ -392,4 +392,4 @@ public void test_execSalaryAdjust() throws InterruptedException { 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消息了。 \ No newline at end of file +- 当执行一次加薪调整后,就会接收到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/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/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-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 75cf8e09e..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)* ## 六、总结 diff --git a/docs/md/zsxq/introduce.md b/docs/md/zsxq/introduce.md index 399541174..e76dd8cdd 100644 --- a/docs/md/zsxq/introduce.md +++ b/docs/md/zsxq/introduce.md @@ -13,64 +13,106 @@ lock: no ## 一、前言:星球介绍 -知识星球是 [小傅哥](https://bugstack.cn/md/zsxq/about/xiaofuge.html) 的 **付费** 编程学习圈,不同于网上的攒出来几百个T的免费资料,在星球内小傅哥会对你的问题 **1v1** 解答,帮你制定学习方案和规划职业路线。目标是针对性的帮每一个同学,通过星球内的有深度的[高质量原创项目](http://localhost:8080/md/zsxq/other/join.html),提升编程思维、领悟架构经验、吸收设计模式、掌握开发技巧,不只是为了面试,更是为了给自己的职业生涯续期。 +知识星球是 [小傅哥](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 课程入口**
- +
-### 1. 学习项目 +大家可以下载一个知识星球 APP 来提前体验~ -很多来自中小公司、外包公司或者尚未毕业的本科生、研究生,手里都没有什么能拿的出手的项目,到大厂去刚一下。 +## 二、步骤;使用指引 -而加入星球就可以学习到小傅哥为你编写的在大厂水平以上的实战项目,包括你会学习到:分布式架构、微服务设计、领域驱动设计、设计模式实现、规则引擎决策树、秒杀分段锁、API网关通信等等,只有加入大厂才能学习到的核心技能。 +
+ +
-#### 1.1 业务项目 +## 三、为啥:加入星球? -- [ChatGPT 微服务应用体系构建 - API-SDK、鉴权、公众号、企业微信、支付服务](https://bugstack.cn/md/zsxq/project/chatgpt.html) - 👣 进行中 -- [Lottery 分布式抽奖系统 - 基于领域驱动设计的四层架构实践](https://bugstack.cn/md/zsxq/project/lottery.html) -- [Netty+JavaFx实战:仿桌面版微信聊天](https://bugstack.cn/md/zsxq/project/im.html) -- [ChatGPT AI 问答助手 - 小型,对接知识星球](https://bugstack.cn/md/zsxq/project/chatbot-api.html) +加入星球,你可以获得已经在这条路上摸爬滚打多年的架构师,为你提供的如下服务。 -#### 1.2 组件项目 +
+ +
-- [API网关:中间件设计和实践](https://bugstack.cn/md/zsxq/project/api-gateway.html) -- [SpringBoot Starter 中间件设计和开发](https://bugstack.cn/md/zsxq/project/springboot-starter.html) -- [IDEA Plugin 开发手册](https://bugstack.cn/md/zsxq/booklet/idea-plugin.html) +这是一整套的实战项目学习进阶路线,从小白到大佬,全程视频手把手带着从0到1,一步步完成项目的设计、开发和上线。在整套内容学习过程中,小傅哥为你提供了非常好的技术交流社群,及时解决学习问题。还包括调试你的问题代码,带你快速🔜出坑! -#### 1.3 技术小册 +- 首先,这一整套全体系的学习课程比私教培训实惠,更比培训班上万的培训费便宜。可能也就是培训班1天的💰钱,就能学习到这一整套内容了。 +- 之后,你学习的整套课程,就是小傅哥这个架构师自己全部原创编写的。这也就是说,你所提到的任何问题,小傅哥都能给你解答和讨论。 +- 那么,这么实惠的课程,成体系的课程,还是架构师编写的。还有什么可犹豫的,完全可以撸起来了! -- [字节码编程 - ASM、Javassist、Byte-Buddy](https://bugstack.cn/md/zsxq/booklet/bytecode.html) -- [重学Java设计模式](https://bugstack.cn/md/zsxq/booklet/java-design.html) -- [Java 面经手册](https://bugstack.cn/md/zsxq/booklet/java-interview.html) -- [倚天村 • 图解数据结构](https://bugstack.cn/md/zsxq/booklet/data-structures.html) +### 1. 学习项目 + +很多来自中小公司、外包公司或者尚未毕业的本科生、研究生,手里都没有什么能拿的出手的项目,到大厂去刚一下。 -#### 1.4 手撕源码 +而加入星球就可以学习到小傅哥为你编写的在大厂水平以上的实战项目,包括你会学习到:分布式架构、微服务设计、领域驱动设计、设计模式实现、规则引擎决策树、秒杀分段锁、API网关通信等等,只有加入大厂才能学习到的核心技能。 -- [手写Spring:渐进式源码实践](https://bugstack.cn/md/zsxq/source-code/develop-spring.html) -- [手写Mybatis:渐进式源码实践](https://bugstack.cn/md/zsxq/source-code/develop-mybatis.html) +#### 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. 问题解答 @@ -94,6 +136,30 @@ 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. 学习氛围 当然,被这样的“鼓励”下,我的周末😭全用在星球上了,不是录制课程视频,就是手写源码,也因为大家的图越画越牛,我也跟着让自己的图更上层楼。点名:课代表阿曦、新晋卷王奥斯卡最佳配角、画图侠诏无言、优秀作业无名氏🧐等等。 @@ -116,6 +182,12 @@ lock: no ### 5. 用户反馈 +经过小傅哥这么多年的努力,👨🏻‍💻认认真真的做编程项目之下,一点点把来自于互联网真实技术教给社群伙伴。也越来越多的被高校老师、在校学生、企业公司、社群粉丝、海外伙伴的认可。 + +
+ +
+ - [@待佳人晚归](https://t.zsxq.com/0eAFk84M7):`写Lottery项目前,因为之前没有接触ddd,也没看过傅哥的视频,想先理解各个模块,然后再编写,傅哥讲的很好,代码太优雅了,一下子就上头了。` - [@念](https://t.zsxq.com/0euNUBW9H):`对Lottery项目分支的开发过程需要多看几遍小傅哥的视频,会有更好的理解.特别是写完之后再去回顾视频,思路会清晰的多` - [@星期一](https://t.zsxq.com/0chHS92j5):`通过两天的学习成功将第五、第六章节跑通并完成了测试,感受最深的是对策略模式和模板模式的进一步了解,之前在看重学java设计模式的时候,对于很多设计模式都处于一知半解的状态,现在结合这个抽奖系统,让我对这两个设计模式有了不一样的理解与认识,感觉真的很棒。这种边记笔记,边学习的感觉真的很好,以前可能看视频学,一天能看很多章节,但其实很多东西都没有弄懂,现在是一天一个章节,慢慢去理解代码怎么编写,怎么完成。这些天虽然还有些地方理解的不是很清楚,我还是继续接着学,到时候回头再来温习一遍。` @@ -125,9 +197,9 @@ lock: no - [@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) +星球价格 **¥159** 一年(全网最实惠社群),老用户续费 **5折** 一年(星球每年都会开发新的学习项目和技术小册等资料)。[查看星球项目](https://bugstack.cn/md/zsxq/other/join.html) >加入 3 天内可以全额退款,感兴趣的同学可以先加入体验,自己判断是否有价值。 @@ -140,7 +212,7 @@ lock: no **注意**:加入星球后,阅读`星球🔝置顶消息` [https://t.zsxq.com/05VB66uzz](https://t.zsxq.com/05VB66uzz) - 项目小册、权限申请、资料文件、简历批阅等。 -## 四、星球:适合我吗? +## 五、星球:适合我吗? 那么,小傅哥的码农会锁,私有技术朋友圈,适合什么样的你加入呢? @@ -159,14 +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/dialogue-skills.md b/docs/md/zsxq/material/dialogue-skills.md index 57bab2463..d21a75324 100644 --- a/docs/md/zsxq/material/dialogue-skills.md +++ b/docs/md/zsxq/material/dialogue-skills.md @@ -156,9 +156,7 @@ Lottery 系统的全方面技术栈的使用,多场景的问题的解决方案 >这样一套项目,放在一些平台售卖,至少都是几百块。但小傅哥的星球,只需要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 0eb060b82..85e41efe1 100644 --- a/docs/md/zsxq/material/guide.md +++ b/docs/md/zsxq/material/guide.md @@ -56,6 +56,7 @@ 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) @@ -63,20 +64,30 @@ lock: no +课程和视频,使用说明; + +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【业务】Lottery、IM + b【技术】API 网关 + c【组件】SpringBoot Starter + d【创新】ChatGPT 项目 + e【开源】IDEA Plugin 插件发布到 IDEA 插件市场。 +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 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-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/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 index f5042abf8..18e85cfe6 100644 --- a/docs/md/zsxq/memorabilia/biographical-notes.md +++ b/docs/md/zsxq/memorabilia/biographical-notes.md @@ -32,10 +32,7 @@ lock: need **注意**:`高质量的简历模板` + `有深度的技术项目` + `编写后的评审指导` = 一条龙服务🐲,**加入星球马上就能获取到**。为你的应聘加把劲,这一丢丢投资可能就换来一个 Offer 和不错薪资! - +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) ### 1. 校招生 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 index 01651b4c6..c175c24b9 100644 --- a/docs/md/zsxq/memorabilia/interview-zijie.md +++ b/docs/md/zsxq/memorabilia/interview-zijie.md @@ -107,9 +107,7 @@ lock: no **以下这个技术圈子强烈建议加入**,因为你可以学习到来自10年编程经验的架构师所编写的各项浅浅深深不同梯度难度的项目,也可以见闻到几千人的技术交流和知识积累,还可以学习到国内高校、海外留学伙伴的学习方式方法。这远比你看过一本书、听过一句话,都实在的多! -
- -
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) --- 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 index 120e9470f..3621fe1e4 100644 --- a/docs/md/zsxq/memorabilia/overall.md +++ b/docs/md/zsxq/memorabilia/overall.md @@ -26,11 +26,7 @@ lock: no - 星球的内容主要分为四部分;`学习路线 - 帮你简明扼要的规划学习`、`项目实战 - 助你提升编程经验锻炼开发能力`、`辅导学习 - 1v1的问题解答直接了当节省时间`、`应对面试 - 手把手优化简历提高建议,让你收割Offer!` - 接下来小傅哥就分别介绍下这部分内容,让小伙伴可以放心食用! -
- -
知识星球【码农会锁】- T8级别架构师,帮你提升编程能力,少走弯路技术圈子!
-
- +>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) ## 一、学习路线 Java 编写学习,首先要明确从哪开始到哪结束。但这个过程以我的学习经验可以告诉你,你并不需要一个特别大的,特别杂的学习路线。因为那样只会让你疲惫😫在学习路线的路上,没到终点就累嘎了。 @@ -162,10 +158,7 @@ Java 编写学习,首先要明确从哪开始到哪结束。但这个过程以 --- -
- -
知识星球【码农会锁】- T8级别架构师,帮你提升编程能力,少走弯路技术圈子!
-
+>[🧧加入学习](https://bugstack.cn/md/zsxq/other/join.html) 1. 星球内的服务和实战项目都是小傅哥本人提供和**原创**,相信能够给大家带来**超过该价格的价值** 。举个例子,渐进式手把手带大家做**进大厂才可能看得见的项目**、有笔记有源码、有问题可以提,这比单独买一个课程或一套源码要值得多。其实都不到大城市一节补习班的钱,**哪怕把我的课程时长换算成培训机构的课时,也是便宜的超级多**。 2. 持续的内容创作 + 回答问题 + 知识星球的运营(简历批阅、就业指导、架构设计) 需要小傅哥每个早上6点-8点以及周末/假期持续维护。也希望加入星球的同学都是真的下定了决心想要进步,而不是像免费的交流群和社区一样 “闲聊扯淡”。 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 index 8bcd2853a..f1408812f 100644 --- a/docs/md/zsxq/memorabilia/seven-thousand.md +++ b/docs/md/zsxq/memorabilia/seven-thousand.md @@ -64,12 +64,7 @@ lock: no 小傅哥的星球是以开发应用级实战项目为目标,公司需要什么样的技术,什么样的项目,我们就做什么样的内容。而这些课程内容只要加入星球就可以全部都学习到。**💐小傅哥的星球付费价格,你相当于只是买了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 60dc61fac..16dc38e31 100644 --- a/docs/md/zsxq/other/join.md +++ b/docs/md/zsxq/other/join.md @@ -5,20 +5,18 @@ lock: no # 加入星球 -星球价格 **¥159** 一年,老用户续费 **5折** 一年(星球每年都会开发新的学习项目和技术小册等资料)。 +星球嘎嘎实惠,星球一直不断的开发新的学习项目和技术小册等资料! -优惠名额有限、先到先得,微信扫描下方二维码领券加入:加入后阅读[使用指南:🔜快速了解,开启学习之旅!](https://bugstack.cn/md/zsxq/material/guide.html) - ->加入 3 天内可以全额退款,感兴趣的同学可以先加入体验,自己判断是否有价值。 +>感兴趣的同学可以先加入体验,自己判断是否有价值,不满意可退出。
- +
关注小傅哥的公众号【bugstack虫洞栈】回复【星球】也可以领取专属优惠券

-🌹加入后,这些内容都是你的,**这片鱼塘**都给你了!—— 目前已有8700+伙伴在星球学习! +🌹加入后,这些内容都是你的,**这片鱼塘**都给你了!—— 目前已有1万+伙伴在星球学习!
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 fbe30900d..60dec2374 100644 --- a/docs/md/zsxq/project/api-gateway.md +++ b/docs/md/zsxq/project/api-gateway.md @@ -115,5 +115,4 @@ lock: no ![](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/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/chatgpt.md b/docs/md/zsxq/project/chatgpt.md index 26a573287..b205af19b 100644 --- a/docs/md/zsxq/project/chatgpt.md +++ b/docs/md/zsxq/project/chatgpt.md @@ -1,9 +1,9 @@ --- -title: ChatGPT 微服务应用体系构建 +title: OpenAi 大模型应用服务体系构建 lock: no --- -# ChatGPT 微服务应用体系构建 - API-SDK、鉴权、公众号、企业微信、支付服务 +# OpenAi 大模型应用服务体系构建 - API-SDK、鉴权、公众号、微信支付 作者:小傅哥
博客:[https://bugstack.cn](https://bugstack.cn) 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/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/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/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 +}