已归档
快来分享你的内容吧~
- 2024-04-29
- 2024-01-16·后端
【用户中心项目后端】【球内后端报错/踩坑汇总大合集及项目补充关键前置知识】
<html> <head></head> <body> <div class="content ql-editor"> <p><br></p> <p>上周学习了用户中心项目,目前已经把后端代码完成了,由于是看完后端视频后,直接独自复现,然后加上本人为蒟蒻只学过一点点Spring boot,基础也不扎实,踩坑无数。</p> <p><br></p> <p>为了让后续🐟友不用像我一样痛苦,现在把我自己踩的坑和原来发布在球内的贴子报错解决一同合并,方便后续大家学习。</p> <p><br></p> <p>再补上一些视频里没有细讲的知识点,让和我一样的小白能更好理解项目。</p> <p><br></p> <p><br></p> <p><strong>说明一下:报错合集只是为了减少痛苦,并不赞成大家一上来就直接查看,优先自己想办法解决,看一下鱼皮发的报错解决手册,消耗过多时间无法解决再来查看有无类似解决方法。</strong></p> <p><br></p> <p>本人学艺不精,如果写的有误,欢迎大家批评指正。</p> <p><br></p> <p>也欢迎大家评论补充或者提问,方便一贴解决所有问题。</p> <hr class="article-hr" style="height: 0px"> <p>先贴上一下用户中心后端的前置学习知识点,然后再下面才是报错合集。</p> <p><br></p> <p><br></p> <p><strong style="font-size: 18px;">前置学习知识点:</strong></p> <p><br></p> <p>1. <strong style="font-size: 17px;">什么是断言</strong></p> <p><br></p> <p><span style="background-color: rgb(255, 255, 255);">断言是</span><span style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51);">用于测试我们认为是正确的代码来帮助检测错误的一种方式</span></p> <p><span style="background-color: rgb(255, 255, 255); font-size: 14px; color: rgb(51, 51, 51);"><br></span></p> <p><a href="https://blog.csdn.net/qq_19734597/article/details/88062760" target="_blank">Junit单元测试之断言(Assert)_assert.asserttrue-CSDN博客</a></p> <p><br></p> <p>简单使用Junit进行断言测试:</p> <p>如果两者一致, 程序继续往下运行.</p> <p>如果两者不一致, 中断测试方法, 抛出异常信息</p> <p><br></p> <p>Junit断言的方法</p> <p><strong>assertEquals方法</strong>:如果预期值与真实值相等,则运行success,反之Failure。</p> <p>assertEquals 运行Failure会有错误提示,提示预期值是xxx,而实际值是xxx。容易调式</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-comment">/**</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment">Asserts that two ints are equal. 断言两个int是相等的</span> </div> <div class="ql-code-block"><span class="ql-token hljs-comment"> */</span> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">static</span> <span class="ql-token hljs-keyword">public</span> <span class="ql-token hljs-keyword">void</span> <span class="ql-token hljs-title">assertEquals(int expected, int actual)</span> { </div> <div class="ql-code-block"> assertEquals(<span class="ql-token hljs-literal">null</span>, expected, actual); </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p><strong>assertTrue方法</strong>:</p> <p>assertTrue 如果为 true,则运行 success,反之 Failure</p> <p>没有错误提示</p> <p><br></p> <p>实际运用:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> Assertions.assertTrue(result); </div> </div> <p><br></p> <p><br></p> <p><strong style="font-size: 17px;">2. 对于工具类的使用</strong></p> <p><strong style="font-size: 17px;"><br></strong></p> <p>我使用的是的Hutool,也可以替换成其他类似的。</p> <p>使用需导入hutool的依赖,Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率。</p> <p>导入如下依赖:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> <!--hutool --> </div> <div class="ql-code-block"> <dependency> </div> <div class="ql-code-block"> <groupId>cn.hutool</groupId> </div> <div class="ql-code-block"> <artifactId>hutool-all</artifactId> </div> <div class="ql-code-block"> <version><span class="ql-token hljs-number">5.7.16</span></version> </div> <div class="ql-code-block"> </dependency> </div> </div> <p><br></p> <p><strong>2.1 Hutool进行</strong><strong style="font-size: 17px;">值为“空”的判断:</strong></p> <p><span style="font-size: 17px;"><br></span></p> <p>common lang3库常用有的StringUtils、ObjectUtils用来做非空判断</p> <p><br></p> <p>这里用StrUtil.isAllNotEmpty()方法同时判断多个字符串a,b,c是否为空:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> StrUtil.isAllNotEmpty(a,b,c)<span class="ql-token hljs-comment">//hutool带的isAllNotEmpty方法,返回一个bool类型</span> </div> </div> <p><br></p> <p><strong>2.2 使用Hutool的SecureUtil类进行摘要算法加密(加密密码):</strong></p> <p><br></p> <p>使用SecureUtil.sha256()方法加密</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> String Ciphertext= SecureUtil.sha256(noise+password);<span class="ql-token hljs-comment">//这里同时传入混淆的噪声和需要加密的字符</span> </div> </div> <p><br></p> <p>因为SHA256()既有运算速度又保证了安全性的平衡,所以选择该方法</p> <p><br></p> <p>hutool还可以使用:md5(),sha1()等方法加密</p> <p><br></p> <p><br></p> <p><strong>3. Mybatis Plus之条件构造器queryWrapper进行查询</strong></p> <p><br></p> <p>下面几篇文章说得还可以:</p> <p><a href="https://blog.csdn.net/bird_tp/article/details/105587582" target="_blank">条件构造器queryWrapper学习</a></p> <p><br></p> <p><a href="https://blog.csdn.net/ddd295569371/article/details/117296110" target="_blank">Mybatis-Plus项目中使用eq方法</a></p> <p><br></p> <p><a href="https://wenku.csdn.net/answer/2ffcf63904464af4905ab2e4c154dac8" target="_blank">queryWrapper查询表中所有数据</a></p> <p><br></p> <p><br></p> <p>其中视频中用到的Mybatis-plus的IService的save方法:</p> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-comment">// 作用是插入一条记录,T是实体类</span> </div> <div class="ql-code-block"><span class="ql-token hljs-type">boolean</span> <span class="ql-token hljs-title">save(T entity)</span>; </div> </div> <p><br></p> <p>还有用的this.save()也是相同方法:</p> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-type">boolean</span> <span class="ql-token hljs-variable">res</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-built_in">this</span>.save(newUser); </div> </div> <p><br></p> <p>使用继承ServiceImpl类中实现的IService接口中封装的save()方法,成功/失败后会返回bool值</p> <p><br></p> <p><br></p> <p>将输入参数进行比对:</p> <p><br></p> <p><a href="https://blog.csdn.net/q7w8e9r4/article/details/133638230" target="_blank">MyBatisPlus查询方法selectById、selectOne、selectBatchIds、selectByMap、selectPage的使用</a></p> <p><br></p> <p><br></p> <p><br></p> <p style=""><strong>4. 正则表达式的使用</strong></p> <p style=""><strong><br></strong></p> <p style="">什么是正则表达式:</p> <blockquote> <div class="blockquote-item"> 正则表达式(regular expression)描述了一种字符串匹配的模式,可以用来检查一个串是否含有某种子串、将匹配的子串做替换或者从某个串中取出符合某个条件的子串等。构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与运算符可以将小的表达式结合在一起来创建更大的表达式。正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。 </div> </blockquote> <p><br></p> <p>如何使用正则表达式过滤特殊字符:</p> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-keyword">private</span> <span class="ql-token hljs-keyword">static</span> <span class="ql-token hljs-keyword">final</span> <span class="ql-token hljs-type">String</span> <span class="ql-token hljs-variable">ACCOUNT_PATTERN</span> <span class="ql-token hljs-operator">=</span> <span class="ql-token hljs-string">"^[^\\W_]+$"</span>;<span class="ql-token hljs-comment">//我的过滤字符的正则表达式</span> </div> <div class="ql-code-block"><span class="ql-token hljs-type">Pattern</span> <span class="ql-token hljs-variable">pattern</span> <span class="ql-token hljs-operator">=</span> Pattern.compile(ACCOUNT_PATTERN);<span class="ql-token hljs-comment">//输入正则表达式ACCOUNT_PATTERN</span> </div> <div class="ql-code-block"><span class="ql-token hljs-type">Matcher</span> <span class="ql-token hljs-variable">matcher</span> <span class="ql-token hljs-operator">=</span> pattern.matcher(userAccount);<span class="ql-token hljs-comment">//输入要匹配内容</span> </div> <div class="ql-code-block"><span class="ql-token hljs-keyword">if</span>(matcher.matches())<span class="ql-token hljs-keyword">return</span> -<span class="ql-token hljs-number">1</span>;<span class="ql-token hljs-comment">//进行匹配</span> </div> <div class="ql-code-block"><br> </div> </div> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>Pattern类的作用在于编译正则表达式后创建一个匹配模式.</li> <li data-list="ordered"><span class="ql-ui"></span>Matcher类使用Pattern实例提供的模式信息对正则表达式进行匹配</li> <li data-list="ordered"><span class="ql-ui"></span>compile方法用于将给定的正则表达式编译成一个模式(输入正则表达式)。</li> <li data-list="ordered"><span class="ql-ui"></span>matcher方法创建一个匹配器,将给定的输入与该模式进行匹配(输入要匹配的字符串)。</li> <li data-list="ordered"><span class="ql-ui"></span>matches方法用于尝试将整个区域与模式匹配。如果匹配成功,则可以通过start、end和group方法获取更多信息。 该函数返回一个布尔值,如果整个区域序列与匹配器的模式匹配,则返回true,否则返回false。</li> </ol> <p><br></p> <p><br></p> <p><strong>5. 设置登录状态,关于session</strong></p> <p><br></p> <p>可以先看下面几篇文章理解:</p> <p><a href="https://blog.csdn.net/yiminghd2861/article/details/116120766" target="_blank">Java Web之HttpSession详解</a></p> <p><br></p> <p><a href="https://zhuanlan.zhihu.com/p/356843248" target="_blank">JavaWeb核心之HttpSession</a></p> <p></p> <p><a href="https://www.liaoxuefeng.com/wiki/1545956031987744/1545956933763105" target="_blank">实现HttpSession</a></p> <p><br></p> <p><a href="https://blog.csdn.net/whitek387/article/details/114838684" target="_blank">session.setAttribute()和session.getAttribute()的使用</a></p> <p><br></p> <p>设置登录状态步骤:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>连接上服务器为默认匿名游客session态,返回前端</li> <li data-list="ordered"><span class="ql-ui"></span>登录成功后,得到成功登录session态存储带有信息值,并给予前端设置cookie命令</li> <li data-list="ordered"><span class="ql-ui"></span>前端接收命令后,设置cookie</li> <li data-list="ordered"><span class="ql-ui"></span>前端请求后端时,带上cookie</li> <li data-list="ordered"><span class="ql-ui"></span>后端通过cookie,找到session</li> <li data-list="ordered"><span class="ql-ui"></span>后端基于session存储该用户脱敏信息</li> </ol> <p><br></p> <p>设置session:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> httpReq.getSession()<span class="ql-token hljs-comment">//获取Session</span> </div> <div class="ql-code-block"> .setAttribute(<span class="ql-token hljs-string">"user"</span>,user);<span class="ql-token hljs-comment">//放入Session记录状态</span> </div> </div> <p>这里“user”设置为常量更好</p> <p><br></p> <p><br></p> <p></p> <p><strong>6. 对于序列化Serializable</strong></p> <p><br></p> <p>什么是序列化:</p> <p>把对象转换为字节序列的过程称为对象的序列化。</p> <p>把字节序列恢复为对象的过程称为对象的反序列化。</p> <p><br></p> <p>为什么要生成一个序列号uid:</p> <p><a href="https://blog.csdn.net/wujialv/article/details/108547358" target="_blank">为什么实现了Serializable接口还要指定serialVersionUID值呢</a></p> <p>【总结】指定序列号uid为了反序列化从硬盘恢复到内存时能保持正常</p> <p><br></p> <p>还有右键找不到生成序列号uid快捷键的解决方法:</p> <p><a href="https://cloud.tencent.com/developer/article/1869036" target="_blank">Intellij IDEA用快捷键自动生成序列化id</a></p> <p><br></p> <p><br></p> <p><strong>7. 编写完成后接口的测试</strong></p> <p><br></p> <p>我是使用Apipost软件去测试的</p> <p><br></p> <p><br></p> <p>对于Post请求:主要将参数放入请求体内,没有JSON化的直接Body传入即可</p> <p><br></p> <p>对于Get请求:是将参数放入url内进行请求,要在Body中指定参数进行的传递</p> <p><br></p> <p>对于Cookie则在请求头中进行传递</p> <p><br></p> <p><a href="https://v7-wiki.apipost.cn/docs/3" target="_blank">快速上手 | Apipost</a></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h1 class="ql-align-center"><strong style="font-size: 18px;">报错大合集</strong></h1> <p><br></p> <p><br></p> <p>先放炎佬的笔记:<a href="https://wx.zsxq.com/dweb2/index/topic_detail/184522182814122" target="_blank">知识星球 </a></p> <p>还有其他的笔记汇总入口:<a href="https://articles.zsxq.com/id_j57k4o6c8ewv.html" target="_blank">用户中心项目-笔记汇总 (</a><a href="http://zsxq.com" target="_blank">zsxq.com</a><a href="https://articles.zsxq.com/id_j57k4o6c8ewv.html" target="_blank">)</a></p> <p><br></p> <p><br></p> <p>我自己碰见的</p> <p><br></p> <p><strong>1.导入Mybaits Plus然后测试引用后按照官方样例测试报错</strong></p> <p>报错:java.lang.IllegalStateException: Failed to load ApplicationContext</p> <p>解决方法:重启spring</p> <p>分析是Mybaits和Mybaits Plus版本冲突,<s>其实不需要修改版本 </s></p> <p><br></p> <p><br></p> <p><strong>2.报错:Error creating bean with name 'userMapper' defined in file/项目文件名爆红</strong></p> <p>解决:最好不要直接移动代码生成的包和类到项目,通过手动创建软件包再复制粘贴项目,修改导入的类路径,就可以避免。</p> <p><br></p> <p><br></p> <p><strong>3.一般版本冲突解决方法:</strong></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/evbpdnxk.jpeg"></p> <p><br></p> <p>不知为何再次出现前面相同错误,这回确定是Mybatis和Mybatis Plus版本问题了。</p> <p><br></p> <p>解决方法:</p> <p>使用Maven Helper插件排除冲突</p> <p><br></p> <p>打开<a href="http://pom.xml" target="_blank">pom.xml</a>,点击下方的Dependency Analyzer</p> <p><br></p> <p>对冲突解决,右击低版本依赖,选择Exclude排除</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/hw8wvkrj.jpeg"></p> <p><br></p> <p><br></p> <p><strong>4.报错空指针异常:</strong></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/7z9rm98u.jpeg"></p> <p><br></p> <p>解决方法:加Resource注解注入</p> <div class="ql-code-block-container"> <div class="ql-code-block"> @Resource </div> <div class="ql-code-block"> UserServiceImpl userService = new UserServiceImpl(); </div> </div> <p><br></p> <p><strong>5.尝试使用不兼容的返回值类型</strong></p> <p>报错:extends ServiceImpl '<a href="http://com.baomidou.mybatisplus.extension.service.impl.ServiceImpl" target="_blank">com.baomidou.mybatisplus.extension.service.impl.ServiceImpl</a>' 中的 'getBaseMapper()' 与 'com.baomidou.mybatisplus.extension.service.IService' 中的 'getBaseMapper()' 冲突;</p> <p>解决方法:未修改代码生成器导入的实体类,仔细查看一下文件导入,修改一下导入即可</p> <p><br></p> <p><strong>6.查询数据库出错</strong></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/t49ui40o.jpeg"></p> <p><br></p> <p>解决方法:修改一下未对应的表名或者其他为未对应内容</p> <p><br></p> <p>(数据库不是必须填入字段,设计表时一定要加上默认NULL填充,不然插入数据没有进行处理会报错。)</p> <p><br></p> <p><br></p> <p><strong>7.MyBatis和MyBatis-Plus依赖冲突</strong></p> <p><strong> 会呈现两种形式的报错</strong></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/vda80j4v.jpeg"></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/mnd1aj0t.jpeg"></p> <p>解决方法:依旧是MyBatis和MyBatis-Plus依赖冲突,这也是同时解决上面报错1和3的最终解决办法。这两个依赖最好不要同时导入,因为是按照鱼皮idea相同方法构建的,直接把所有MyBatis去掉即可。为了排除这个错误痛苦好久,卡了我大半天,快难受死了,尝试各种方法,最后发现只需要把<strong>MyBatis依赖去掉</strong>就可以解决问题。导入了<strong>MyBatis-Plus</strong>就不要再导入<strong>MyBatis</strong>了,血泪的教训。</p> <p><br></p> <p>当然,只引入MyBatis也一样可能会出现这个错误,具体参考下面这个文章</p> <p><br></p> <p><a href="https://blog.csdn.net/yy12345_6_/article/details/123896812" target="_blank">Springboot整合Mybatis报错大集合</a></p> <p><br></p> <p><br></p> <p>下面其余🐟友发星球里的,在笔记或者其余地方的就只能大家自己去查找了:</p> <p><br></p> <p><br></p> <p>8.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/icnhvj42.jpeg"></p> <p><br></p> <p><br></p> <p>9.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/zjnnigjv.jpeg"></p> <p><br></p> <p>10.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/zm5qt1qq.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/8mup5emo.jpeg"></p> <p><br></p> <p>11.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/iu8t1qo6.jpeg"></p> <p><br></p> <p>12.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/x7mlc5lw.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/0m198h7q.jpeg"></p> <p><br></p> <p>13.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/qycnruq1.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/gq7hbnl2.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/2tn7ozzk.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/azbrixvs.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/putbdyxd.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/600gzbnl.jpeg"></p> <p><br></p> <p><br></p> <p><br></p> <p>14.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/wmod6prc.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/3a4c4rnr.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/6of4vmjt.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/iie2eopi.jpeg"></p> <p><a href="https://blog.csdn.net/qq_49619863/article/details/128047256?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3-128047256-blog-127906923.pc_relevant_multi_platform_whitelistv3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-3-128047256-blog-127906923.pc_relevant_multi_platform_whitelistv3&utm_relevant_index=6" target="_blank">Springbot启动报错-类文件具有错误的版本 61.0, 应为 52.0_/d:/java/apache-maven-3.8.6/repository/org/springf-CSDN博客</a></p> <p><br></p> <p>15.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/cd6oi0cg.jpeg"></p> <p><br></p> <p>16.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/b6nrhf5y.jpeg"></p> <p><br></p> <p>17.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/6cmx3ffp.jpeg"></p> <p><br></p> <p>18.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/zhsje7f8.jpeg"></p> <p><br></p> <p>19.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/nt17wm0w.jpeg"></p> <p><br></p> <p>20.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/pjqmqxpv.jpeg"></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/w0ymvz8k.jpeg"></p> <p><br></p> <p>21.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/mqmsxkgw.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/ldsb4vyp.jpeg"></p> <p><br></p> <p><br></p> <p>22.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/iv7mqsrs.jpeg"></p> <p><br></p> <p>23.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/i5ko2dgr.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/4e667l36.jpeg"></p> <p><br></p> <p>24.</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1799327314692616194/rtq1it3h.jpeg"></p> <p><br></p> <p>23年之后的大概筛选了一遍,没有具体解决方法,内容重复的都去掉了,还可能有遗漏的帖子,剩下靠大伙补充了。</p> <p><br></p> </div> </body> </html>
鱼皮的 Bug 修复手册上线
<html> <head></head> <body> <div class="content ql-editor"> <p>鱼友们好,我的 Bug 修复手册上线啦!</p> <p></p> <p><strong>当然是免费的</strong> ,放在了我们编程导航网站上:<a href="https://www.codefather.cn/course/bug" target="_blank">https://www.codefather.cn/course/bug</a></p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/cvra6lfx.jpeg" alt=""></p> <p></p> <p>下面简单介绍一下这个手册。</p> <p></p> <h2>为什么要做 Bug 修复手册?</h2> <p></p> <p>鱼皮带大家做项目多年,发现大家遇到的很多编程方面的问题其实都是 <strong>通用</strong> 的。</p> <p></p> <p>比如为什么前端网页打不开啦!为什么没办法登录了!为什么后端项目启动不了啦!为什么数据查询不出来啦!等等等等这一系列的问题。对于刚开始学做项目的同学来说,这些 Bug 属实让人头疼。</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/xtq2f0p4.jpeg" alt=""></p> <p></p> <p>但如果每次遇到 Bug,都去找人求助,其实你自身的能力并不会得到有效的提升,时间久了,反而会养成 “伸手党” 的习惯。所以,最好在遇到 Bug 前,先尝试自己解决。</p> <p></p> <p>授人以鱼不如授人以渔,为提高 Bug 的解决效率、带大家掌握解决 Bug 的套路、提升自主解决问题的能力,鱼皮决定打造了这份《程序员 Bug 修复手册》。</p> <p></p> <h2>手册大纲</h2> <p></p> <p>手册主要分为如下 3 部分:</p> <p></p> <h4>1、解决 Bug 的流程套路</h4> <p></p> <p>这部分不涉及具体的 Bug,是一些方法论和经验,做项目前必读。</p> <p></p> <p>比如解决 Bug 的流程和套路:</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/49ereyq0.jpeg" alt=""></p> <p></p> <p>常见的请求错误码解释及解决方案:</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/nod13j9h.jpeg" alt=""></p> <p></p> <h4>2、经典 Bug 解决方案</h4> <p></p> <p>根据大家过去向我发起的项目相关的提问,我整理了一些常见的、经典的 Bug,大家可以根据需要查找和阅读。</p> <p></p> <p>比如前端框架初始化错误、数据查询为空等等:</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/107p2cfc.jpeg" alt=""></p> <p></p> <h4>3、Bug 案例分享</h4> <p></p> <p>收录了鱼皮本人和一些博主分享的真实 Bug 踩坑和排查过程,感兴趣的同学可以阅读长见识。看别人解决 Bug 还是很爽的哈哈!</p> <p></p> <p>目前这部分文章不多,还在持续更新中,大家有遇到有趣的 Bug 或者好的文章也欢迎推荐。</p> <p></p> <hr> <p></p> <p>Bug 修复手册:<a href="https://www.codefather.cn/course/bug" target="_blank">https://www.codefather.cn/course/bug</a></p> <p></p> <p>祝大家都能成长为 Bug 解决小能手~(当然没 Bug 更好哈哈)</p> <p>#开发经验#</p> </div> </body> </html>
复用 - 快速开发新产品的秘诀
<html> <head></head> <body> <div class="content ql-editor"> <p>在我最近发布的 <a href="https://www.bilibili.com/video/BV1iD421p7gP/" target="_blank">鱼厂一周年复盘总结</a> 中,提到了我们团队注重技术沉淀的理念。其中重要的一点就是:在开发产品时,我们尽可能地保证重复的功能只开发一次,以提高效率、降低开发成本。</p> <p></p> <p>有好学的小伙伴问:具体是怎么实现的呢?如何践行这种设计呢?</p> <p></p> <p>这个问题的确非常好,给大家简单分享一下我们快速开发新产品的秘诀。</p> <p></p> <p>其实有很多种提高开发效率、避免重复开发的方法,本质上都是 “复用”,我按照复用的粒度,从小到大依次给大家举例讲解。</p> <p></p> <p>1)代码级复用:对重复的代码进行复用,也许只是简单的几行代码。</p> <p></p> <p>举个例子,下面这段通过本地加锁让用户串行操作的逻辑,可以复用到很多逻辑中:</p> <p></p> <div class="ql-code-block-container"> <div class="ql-code-block"><span class="ql-token hljs-comment">// 锁必须要包裹住事务方法</span> <span class="ql-token hljs-type">QuestionFavourService</span> <span class="ql-token hljs-variable">questionFavourService</span> <span class="ql-token hljs-operator">=</span> (QuestionFavourService) AopContext.currentProxy(); <span class="ql-token hljs-keyword">synchronized</span> (String.valueOf(userId).intern()) { <span class="ql-token hljs-keyword">return</span> questionFavourService.doQuestionFavourInner(userId, questionId); } </div> </div> <p></p> <p>针对这种逻辑单独封装方法又有些 “小题大做” 了,所以一般我们会给可复用的关键代码打上注释,或者用 IDEA 的 region 注释进行标识,要用到的时候复制粘贴即可。</p> <p></p> <p>可以使用我们开发的 <a href="http://jianqiezhushou.com/" target="_blank">剪切助手工具</a> 保留所有复制过的代码,并进行分组,下次要粘贴时就能轻松找回。</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/m8bpqki6.jpeg" alt=""></p> <p></p> <p>2)函数级复用:将常用的功能逻辑封装成函数(方法)。比较典型的是 Service 类中的方法,比如 UserService 中编写一个获取登录用户信息的方法,很多类都可以调用;也可以将该方法复制粘贴到其他项目中。</p> <p></p> <p>3)类级别复用:将常用的一组功能封装为类或服务。比较典型的是工具类、数据访问层的 Mapper 和通用业务逻辑 Manager 类。比如获取网络请求的工具类 NetUtils、操作文件存储的服务等。</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/ciyhbwjy.jpeg" alt=""></p> <p></p> <p>4)模块级复用:遵循领域驱动设计(DDD)的思想,很多项目都是可以拆分成多个模块的,每个模块都是可复用的功能单元。说人话,其实模块就是多个相关的类的集合。比如 AI 模块,可能包含了 AI 相关的 model 数据实体、调用 AI 的 API 请求工具类、组合上下文的历史管理类等等。</p> <p></p> <p>我们在做某个新功能时,首先会思考这个功能以后有没有可能会被复用,对于评估下来可复用的功能,我们会考虑首次在项目中开发时,就把和这个功能相关的代码都放到一个目录(包)下,而不是按层级分到 Controller、Service、Model 等包中。</p> <p></p> <p>刚开始这个模块可能只有当前项目使用,并不会做的很完善,其他项目也要用时可能是先通过复制粘贴的模式;等模块逐渐完善后,再考虑是否需要将该模块独立出来,供其他项目复用。</p> <p></p> <p>5)插件和 SDK 级复用:其实就是把模块单独抽离出来,封装为可直接引入项目的插件或者 SDK 依赖包。比如我们公司内封装了自己的 AI SDK、短信 SDK、企微消息 SDK、动态公告系统插件等。(动态公告系统是我们实习生开发的,在星球内分享了教程哦:<a href="https://t.zsxq.com/19491FHYf" target="_blank">https://t.zsxq.com/19491FHYf</a> )</p> <p></p> <p>对于大一点的公司,一般会有自己的私有 Maven 和 Npm 仓库,公司内的开发同学可以将制作好的 SDK 或插件上传到私有库,其他同事通过依赖管理工具,就能直接下载。</p> <p></p> <p>6)API 级复用:区别于必须要手动引入到项目中的 SDK 或插件,将功能封装为 API 接口供其他项目调用,会是一种更灵活、更解耦合的方式。比如我们将在线执行代码的能力单独部署到了一些服务器上,供所有项目一行代码调用。</p> <p></p> <p>有同学可能会问了:什么情况下要提供 API?什么情况下提供 SDK 呢?</p> <p></p> <p>一般在企业中,API 常用于跨团队开发的场景,典型的就是获取其他业务的数据、或者复用其他业务的能力。比如我们想在编程导航项目中获取到代码小抄项目中的代码数据,完全没必要把获取代码小抄数据的功能抽出来封装为 SDK,这个代价太大了,不如让代码小抄项目提供一个返回代码数据的接口即可。而 SDK 更适用于封装工具库,或者和数据无关的、业务解耦的能力。</p> <p></p> <p>当然,很多时候,我们会同时提供 API 和 SDK。比如腾讯云,不仅提供给开发者在线调用的 API,还提供了 SDK 来简化 API 的调用。</p> <p></p> <p>7)系统级复用:这是成本最高的、也是收益最大的复用,比如前端项目万用模板、后端项目万用模板等,每个新项目都可以基于模板快速得到很多基础能力,大幅提高开发效率。相信大家使用编程导航内的万用模板做项目时,也能感受到这点。</p> <p></p> <p>也建议大家在做项目的过程中,注重积累,沉淀属于自己的弹药库,成为代码复用大师!</p> <p>#开发#</p> </div> </body> </html>
用户中心项目总结(不完全踩坑记录)
<html> <head></head> <body> <div class="content ql-editor"> <p>#用户中心项目总结</p> <p><br></p> <p>花了两个星期大致完成了在星球的第一个项目,中间过了个年,鸽了好几天😢,下面分享一下个人的踩坑记录和一些心得。</p> <p><br></p> <p>因为之前没有用过类似的框架,这个项目的ant design pro前端属实让我琢磨了好一阵。</p> <p><br></p> <p>首先是跟着鱼皮大大安装初始化脚手架,创建前端项目。</p> <p><br></p> <p>Mybatis 新增用户时出现</p> <p><br></p> <p><a href="http://about:blank" target="_blank">java.sql.SQLSyntaxErrorException:</a> Unknown column 'user_account' in</p> <p>'field list'的bug,这是因为数据库字段没有下划线,mybatis默认会采用驼峰命名法,在配置关掉即可</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/alhuzp56.jpeg"></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/778rpudd.jpeg"></p> <p>然后还学会了通过idea自带的测试工具发送请求。</p> <p><br></p> <p>还有一个新的知识点(对我来说),</p> <p><span style="background-color: white; color: rgb(79, 79, 79);">session的校验机制--</span>HttpServletRequest request,详细可以看鱼总的视频。</p> <p><br></p> <p>之前数据库操作都是mybatis,这次运用了一些mybatis-plus的操作。如selectOne、removeByid等。</p> <p><br></p> <p>#bug</p> <p><br></p> <p>前端页面出现umibundling50%</p> <p>error -</p> <p>./src/.umi/core/<a href="http://about:blank" target="_blank">route.tsx:109:23-115</a></p> <p><br></p> <p>Module</p> <p>not found: Error: [CaseSensitivePathsPlugin] `D:\java\YUPI\用户中心\myapp\src\pages\User\register\<a href="http://index.tsx" target="_blank">index.tsx</a>`</p> <p>does not match the corresponding path on disk `Register`</p> <p><br></p> <p>错误信息中提到的路径是<span style="color: rgb(36, 41, 47);">D:\java\YUPI\用户中心\myapp\src\pages\User\register\</span><a href="http://index.tsx" target="_blank" style="color: rgb(36, 41, 47);">index.tsx</a>,而对应的路径名应该是<span style="color: rgb(36, 41, 47);">Register</span>。根据错误信息,可以猜测是因为路径大小写不匹配导致了这个错误。</p> <p><br></p> <p><br></p> <p>后端</p> <p>控制台输出报错:Note: further occurrences of</p> <p>HTTP request parsing errors will be logged at DEBUG level.</p> <p><br></p> <p>看来是在解析HTTP请求行时出现了问题。具体地,错误消息指出在方法名中发现了无效字符,导致无法正确解析HTTP请求。</p> <p><br></p> <p><span style="background-color: rgb(244, 246, 248);">根据HTTP协议规范,HTTP方法名必须是令牌(token),也就是说,它们必须由ASCII可打印字符组成,且不能包含空格或特殊字符。</span></p> <p><br></p> <p>所以在vscode全局搜索token,找到应该是requestErrorConfig这里,注释掉</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/6nx2ub6q.jpeg"></p> <p><br></p> <p></p> <p><br></p> <p>然后还是报一样的错:</p> <p><br></p> <p>Note: further</p> <p>occurrences of HTTP request parsing errors will be logged at DEBUG level.</p> <p><br></p> <p>发现原来是在<a href="http://proxy.ts" target="_blank">proxy.ts</a>这里http后多加了“s”,删掉后问题解决</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/1hlcaobt.jpeg"></p> <p><br></p> <p>复现3.25笔记时出现了bug:子路由无法渲染出来。</p> <p><br></p> <p>找了很久,折腾了好久呜呜呜,在github的issue区<a href="https://github.com/ant-design/ant-design-pro/issues/11142" target="_blank">🧐[问题 | question] 子路由渲染问题 · Issue #11142 ·</a></p> <p><a href="https://github.com/ant-design/ant-design-pro/issues/11142" target="_blank">ant-design/ant-design-pro · GitHub</a>找到答案,</p> <p><br></p> <p>只需要在父级路由使用<Outlet/>渲染子路由,手动导入'umi',即可成功渲染出右侧./Admin/UserManage</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/owuei7e6.jpeg"></p> <p><br></p> <p>部署时,我尝试了从0到1和宝塔两种方式,特别是第一种,中间遇到了很多问题,比如</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/gvx2m7m1.jpeg"></p> <p><span style="color: rgb(250, 0, 0);">修改url为url: jdbc:mysql://120.26.218.238:3306/user_center</span>,然后重新package打成jar包即可。</p> <p><br></p> <p>部署前端时遇到一些端口问题,知道网站默认端口号是80,要改成自己在nginx.conf设置的端口号,才可以正确渲染。可以参考这篇文章:</p> <p><a href="https://ww.dandelioncloud.cn/article/details/1510953291075932161" target="_blank">https://ww.dandelioncloud.cn/article/details/1510953291075932161</a></p> <p><br></p> <p>后端部署安装java,参考</p> <p><a href="https://blog.csdn.net/LiuLiYanSha/article/details/132494473?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170791190616800197057740%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170791190616800197057740&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-132494473-null-null.142%5ev99%5epc_search_result_base9&utm_term=%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%AE%89%E8%A3%85java17&spm=1018.2226.3001.4187" target="_blank">https://blog.csdn.net/LiuLiYanSha/article/details/132494473?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170791190616800197057740%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=170791190616800197057740&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-132494473-null-null.142^v99^pc_search_result_base9&utm_term=%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%AE%89%E8%A3%85java17&spm=1018.2226.3001.4187</a></p> <p>然后就是结合大大们的笔记和视频</p> <p><br></p> <p>不过有个疑问:进程进行中,当然可以连接到后端,如果服务器开着,但是没有人为执行进程,也可以正常连接到后端吗?</p> <p><br></p> <p>##宝塔部署</p> <p><br></p> <p>在添加站点,输入网址出现bug</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/g7hgpi2b.jpeg"></p> <p><br></p> <p>Npm run build 构建前端项目</p> <p>找了一通发现原来是上传dist文件时出错,应该上传dist文件夹内的所有文件,不用把dist这个文件夹也上传。</p> <p><br></p> <p><span style="color: rgb(204, 0, 0);">部署后端时,有个现象让我百思不得其解,有懂的小伙伴麻烦告诉我!!!感谢!</span></p> <p><span style="color: rgb(204, 0, 0);">这个是在本地vscode运行的,直接发送请求给后端了,相当于postman,但是端口号理论上应该是8989(我后端端口号是8989),而不是8000(居然也能成功获得返回结果)!?</span></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/q7uchgho.jpeg"></p> <p>部署前端时,直接输入公网ip可以渲染出界面,但是刷新一下或者手动输入/user/login,则会报错404 Not found</p> <p>在配置文件加入以下代码</p> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span><span style="color: rgb(51, 51, 51); background-color: white;">location / {</span></li> <li data-list="ordered"><span class="ql-ui"></span><span style="color: rgb(51, 51, 51); background-color: white;">try_files $uri $uri/ @router;</span></li> <li data-list="ordered"><span class="ql-ui"></span><span style="color: rgb(51, 51, 51); background-color: white;">index </span><a href="http://index.html" target="_blank" style="color: rgb(51, 51, 51); background-color: white;">index.html</a><span style="color: rgb(51, 51, 51); background-color: white;">;</span></li> <li data-list="ordered"><span class="ql-ui"></span><span style="color: rgb(51, 51, 51); background-color: white;">}</span></li> <li data-list="ordered"><span class="ql-ui"></span><span style="color: rgb(51, 51, 51); background-color: white;">location @router {</span></li> <li data-list="ordered"><span class="ql-ui"></span><span style="color: rgb(51, 51, 51); background-color: white;">rewrite ^.*$ /</span><a href="http://index.html" target="_blank" style="color: rgb(51, 51, 51); background-color: white;">index.html</a><span style="color: rgb(51, 51, 51); background-color: white;"> last;</span></li> <li data-list="ordered"><span class="ql-ui"></span><span style="color: rgb(51, 51, 51); background-color: white;">}</span></li> </ol> <p>则不会出现以上问题辣。</p> <p><br></p> <p>##docker安装镜像</p> <p><br></p> <p>Docker部署</p> <p><br></p> <p>在宝塔安装docker后,</p> <p><br></p> <p>在后端新建Dockerfile文件(鱼皮大大版本)</p> <p><br></p> <p>FROM</p> <p>maven:3.5-jdk-8-alpine as builder</p> <p><br></p> <p># Copy local code to</p> <p>the container image.</p> <p><br></p> <p>WORKDIR /app</p> <p><br></p> <p>COPY <a href="http://pom.xml" target="_blank">pom.xml</a> .</p> <p><br></p> <p>COPY src ./src</p> <p><br></p> <p># Build a release</p> <p>artifact.</p> <p><br></p> <p>RUN mvn package</p> <p>-DskipTests</p> <p><br></p> <p># Run the web</p> <p>service on container startup.</p> <p><br></p> <p>CMD</p> <p>["java","-jar","/app/target/<a href="http://user-center-backend-0.0.1-SNAPSHOT.jar" target="_blank">user-center-backend-0.0.1-SNAPSHOT.jar</a>","<a href="http://--spring.profiles.active=prod" target="_blank">--spring.profiles.active=prod</a>"]</p> <p><br></p> <p>然后重新git上传后,宝塔终端拉下来</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/wpd5jo3c.jpeg"></p> <p><br></p> <p><span style="color: rgb(204, 0, 0);">但是部署后查不到对应的docker,或者说报错了应该是maven的问题</span></p> <p><br></p> <p>找不到解决方案后我重新找了一份dockerfile文件</p> <p><br></p> <p>FROM</p> <p>openjdk:8-jdk-alpine</p> <p><br></p> <p># Copy local code to</p> <p>the container image.</p> <p><br></p> <p>WORKDIR /app</p> <p><br></p> <p>COPY</p> <p>./target/<a href="http://user-center-0.0.1-SNAPSHOT.jar" target="_blank">user-center-0.0.1-SNAPSHOT.jar</a> /app/<a href="http://user-center.jar" target="_blank">user-center.jar</a></p> <p><br></p> <p># Build a release</p> <p>artifact.</p> <p><br></p> <p>EXPOSE 8989</p> <p><br></p> <p># Run the web</p> <p>service on container startup.</p> <p><br></p> <p>CMD</p> <p>["java","-jar","<a href="http://user-center.jar" target="_blank">user-center.jar</a>","<a href="http://--spring.profiles.active=prod" target="_blank">--spring.profiles.active=prod</a>"]</p> <p><br></p> <p></p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/u9hgchgm.jpeg"></p> <p>构建成功!</p> <p><br></p> <p><br></p> <p>这里我又遇到个bug</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/nr5fo2ac.jpeg"></p> <p><br></p> <p>这应该是jdk版本问题 服务器java版本是jdk17</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/qtilkn61.jpeg"></p> <p><br></p> <p>而我刚开始dockerfile中版本是jdk8,所以docker拉取不到java运行版本的镜像,所以更新dockerfile即可。</p> <p><br></p> <p>FROM openjdk:<span style="color: rgb(250, 0, 0);">17</span>-jdk-alpine</p> <p><br></p> <p># Copy local code to</p> <p>the container image.</p> <p><br></p> <p>WORKDIR /app</p> <p><br></p> <p>COPY</p> <p>./target/<a href="http://user-center-0.0.1-SNAPSHOT.jar" target="_blank">user-center-0.0.1-SNAPSHOT.jar</a> /app/<a href="http://user-center.jar" target="_blank">user-center.jar</a></p> <p><br></p> <p># Build a release</p> <p>artifact.</p> <p><br></p> <p>EXPOSE 8989</p> <p><br></p> <p># Run the web</p> <p>service on container startup.</p> <p><br></p> <p>CMD</p> <p>["java","-jar","<a href="http://user-center.jar" target="_blank">user-center.jar</a>","<a href="http://--spring.profiles.active=prod" target="_blank">--spring.profiles.active=prod</a>"]</p> <p><br></p> <p>测试一下,访问后端,成功</p> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/nuqruivg.jpeg"></p> <p><br></p> <p><span style="color: rgb(204, 0, 0);">这里我又想向大佬们提问:</span></p> <p><span style="color: rgb(204, 0, 0);">①宝塔面板需要在纯净环境下安装软件,那如果在服务器终端安装某些软件,那在宝塔软件商店也可以重复安装吗,会不会有冲突呢?</span></p> <p><span style="color: rgb(204, 0, 0);">②还有宝塔面板的“安全“一栏,有显示开放的端口号,这和云服务器的安全组一致吗,服务器的防火墙所罗列的端口号又有什么关系呢</span></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/tfm7uwvm.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/rmj4bxc6.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/w8gs9q2e.jpeg"></p> <p><span style="color: rgb(204, 0, 0);">③部署前端时,我发现在前端的proxy,ts有对‘api’代理,但是宝塔的站点也有专门的反向代理,甚至可以在“配置文件”和“反向代理”一栏更改,那会不会冲突呢?</span><img src="https://pic.code-nav.cn/planet_post_image/1750768007606444034/xd1qgr61.jpeg"></p> <p>以上不完全记录,感谢大家浏览,大家晚安!</p> <p><br></p> <p>#用户中心项目 #项目总结</p> </div> </body> </html>
用户中心项目 笔记
交作业啦~ 因为以前有过项目经历,但是都是自己摸索,所以有很多地方没有像鱼总一样来写,但是真的学到了很多东西。比如:全局异常的封装,自定义异常的封装之类的。👻 因为是第一次用React来写东西所以写了很多天,不过比起Vue来说,确实更优雅了,程序员不就是要优雅吗~ 说一下目前用到的技术栈 前端: - react - pro-components 组件库 - ant design pro 脚手架 后端: - redis - sa-token 轻量级鉴权框架 - mybatis 、 mybatis-plus 通过使用Sa-token实现了轻量级的注解鉴权,优雅。(因为系统不大,所以也没有采用RBAC模型 SpringSecurity来实现)再通过Ant Design Pro提供的Access权限插件实现了对按钮级别的鉴权。该后台系统因为目前没有需求就只添加了最基本的用户管理,但是“麻雀虽小,五脏俱全”,前端分页查询,按钮鉴权,Api鉴权,忘记密码的验证码邮箱发送,注册验证码使用Hutool实现......登录两个账号可以体验鉴权功能。(别把管理员给删了,我还没做限制,全凭大家自己哈) 测试账号:test 测试密码:12345678 管理员账号:admin 管理员密码:66666666 线上地址:https://joy-admin.ojason.top 仓库地址:http://www.codefather.cn/2650498254/joy-admin-end 期待与各位共同学习的未来,共勉! 
智谱AI-SDK入门实践之Java(保姆级教程)
<html> <head></head> <body> <div class="content ql-editor"> <p>大家好,我是影子,一名以Java学习为主,其余时间探索AI+编程的程序猿</p> <p><br></p> <p>今天跟大家分享一下智谱AI-SDK的入门用法,话不多说,我们直接开始</p> <p><br></p> <p><br></p> <h2>一、什么是智谱AI</h2> <p><br></p> <p>智谱AI(Zhipu AI)是一家致力于人工智能技术研发和应用的公司。该公司由清华大学背景的团队创立,专注于大</p> <p>模型技术的研究与推广。智谱AI在人工智能领域取得了显著成就,其发布的自研大模型GLM-4等产品。</p> <p><br></p> <p><br></p> <h2>二、SDK玩法</h2> <h3>(一) 注册账号</h3> <p><br></p> <p>进入官网(<a href="https://maas.aminer.cn" target="_blank">https://maas.aminer.cn/</a>),注册账号实名后,将会赠送<strong>有效期一个月</strong>的体验包。</p> <p><br></p> <p></p> <p><br></p> <p><br></p> <h3>(二) 查看自己的API Key</h3> <p><br></p> <p></p> <p><br></p> <p></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p>注意:我们常见的<strong><em>API_KEY和API_SECRET,这里采用了统一为API key,使用 .这个符号进行划分。</em></strong></p> <p>举个栗子:yingzix688.编程导航真不错。 (本人微信号:yingzix688,欢迎大家交流探讨)</p> <p>那么,<strong><em>API_KEY:yingzix688</em></strong></p> <p><strong><em>API_SECRET:编程导航真不错</em></strong></p> <p>大家只需要看自己的API key进行分割出来即可。</p> <p><br></p> <p><br></p> <h3>(三) 查阅官方github</h3> <p><br></p> <p>官方github地址:<a href="https://github.com/zhipuai/zhipuai-sdk-java-v4" target="_blank">https://github.com/zhipuai/zhipuai-sdk-java-v4</a></p> <p><br></p> <h4>引入依赖</h4> <div class="ql-code-block-container"> <div class="ql-code-block"> <dependency> </div> <div class="ql-code-block"> <groupId>cn.bigmodel.openapi</groupId> </div> <div class="ql-code-block"> <artifactId>oapi-java-sdk</artifactId> </div> <div class="ql-code-block"> <version>release-V4-2.0.0</version> </div> <div class="ql-code-block"> </dependency> </div> </div> <p><br></p> <h4>官方示例代码</h4> <div class="ql-code-block-container"> <div class="ql-code-block"> package com.zhipu.oapi.demo; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> import com.alibaba.fastjson.JSON; </div> <div class="ql-code-block"> import com.fasterxml.jackson.annotation.JsonInclude; </div> <div class="ql-code-block"> import com.fasterxml.jackson.core.JsonProcessingException; </div> <div class="ql-code-block"> import com.fasterxml.jackson.core.type.TypeReference; </div> <div class="ql-code-block"> import com.fasterxml.jackson.databind.DeserializationFeature; </div> <div class="ql-code-block"> import com.fasterxml.jackson.databind.ObjectMapper; </div> <div class="ql-code-block"> import com.fasterxml.jackson.databind.PropertyNamingStrategy; </div> <div class="ql-code-block"> import com.zhipu.oapi.ClientV4; </div> <div class="ql-code-block"> import com.zhipu.oapi.Constants; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.embedding.EmbeddingApiResponse; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.embedding.EmbeddingRequest; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.file.FileApiResponse; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.file.QueryFileApiResponse; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.file.QueryFilesRequest; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.fine_turning.*; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.image.CreateImageRequest; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.image.ImageApiResponse; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.model.*; </div> <div class="ql-code-block"> import io.reactivex.Flowable; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> import java.util.ArrayList; </div> <div class="ql-code-block"> import java.util.HashMap; </div> <div class="ql-code-block"> import java.util.List; </div> <div class="ql-code-block"> import java.util.Map; </div> <div class="ql-code-block"> import java.util.concurrent.atomic.AtomicBoolean; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public class V4OkHttpClientTest { </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final String API_KEY = ""; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final String API_SECRET = ""; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final ClientV4 client = new ClientV4.Builder(API_KEY,API_SECRET).build(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final ObjectMapper mapper = defaultObjectMapper(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static ObjectMapper defaultObjectMapper() { </div> <div class="ql-code-block"> ObjectMapper mapper = new ObjectMapper(); </div> <div class="ql-code-block"> mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); </div> <div class="ql-code-block"> mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); </div> <div class="ql-code-block"> mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); </div> <div class="ql-code-block"> mapper.addMixIn(ChatFunction.class, ChatFunctionMixIn.class); </div> <div class="ql-code-block"> mapper.addMixIn(ChatCompletionRequest.class, ChatCompletionRequestMixIn.class); </div> <div class="ql-code-block"> mapper.addMixIn(ChatFunctionCall.class, ChatFunctionCallMixIn.class); </div> <div class="ql-code-block"> return mapper; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 请自定义自己的业务id </div> <div class="ql-code-block"> private static final String requestIdTemplate = "mycompany-%d"; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) throws Exception { </div> <div class="ql-code-block"> System.setProperty("org.slf4j.simpleLogger.logFile", "System.out"); </div> <div class="ql-code-block"> // 1. sse-invoke调用模型,使用标准Listener,直接返回结果 </div> <div class="ql-code-block"> testSseInvoke(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 2. invoke调用模型,直接返回结果 </div> <div class="ql-code-block"> // testInvoke(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 3. 异步调用 </div> <div class="ql-code-block"> // String taskId = testAsyncInvoke(); </div> <div class="ql-code-block"> // 4.异步查询 </div> <div class="ql-code-block"> // testQueryResult(taskId); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 5.文生图 </div> <div class="ql-code-block"> // testCreateImage(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 6. 图生文 </div> <div class="ql-code-block"> // testImageToWord(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 7. 向量模型 </div> <div class="ql-code-block"> // testEmbeddings(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 8.微调-上传微调数据集 </div> <div class="ql-code-block"> // testUploadFile(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 9.微调-查询上传文件列表 </div> <div class="ql-code-block"> // testQueryUploadFileList(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 10.微调-创建微调任务 </div> <div class="ql-code-block"> // testCreateFineTuningJob(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 11.微调-查询微调任务事件 </div> <div class="ql-code-block"> // testQueryFineTuningJobsEvents(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 12.微调-查询微调任务 </div> <div class="ql-code-block"> // testRetrieveFineTuningJobs(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 13.微调-查询个人微调任务 </div> <div class="ql-code-block"> // testQueryPersonalFineTuningJobs(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 14.微调-调用微调模型(参考模型调用接口,并替换成要调用模型的编码model) </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static void testQueryPersonalFineTuningJobs() { </div> <div class="ql-code-block"> QueryPersonalFineTuningJobRequest queryPersonalFineTuningJobRequest = new QueryPersonalFineTuningJobRequest(); </div> <div class="ql-code-block"> queryPersonalFineTuningJobRequest.setLimit(1); </div> <div class="ql-code-block"> QueryPersonalFineTuningJobApiResponse queryPersonalFineTuningJobApiResponse = client.queryPersonalFineTuningJobs(queryPersonalFineTuningJobRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(queryPersonalFineTuningJobApiResponse)); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static void testQueryFineTuningJobsEvents() { </div> <div class="ql-code-block"> QueryFineTuningJobRequest queryFineTuningJobRequest = new QueryFineTuningJobRequest(); </div> <div class="ql-code-block"> queryFineTuningJobRequest.setJobId("ftjob-20240119114544390-zkgjb"); </div> <div class="ql-code-block"> // queryFineTuningJobRequest.setLimit(1); </div> <div class="ql-code-block"> // queryFineTuningJobRequest.setAfter("1"); </div> <div class="ql-code-block"> QueryFineTuningEventApiResponse queryFineTuningEventApiResponse = client.queryFineTuningJobsEvents(queryFineTuningJobRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(queryFineTuningEventApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 查询微调任务 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testRetrieveFineTuningJobs() { </div> <div class="ql-code-block"> QueryFineTuningJobRequest queryFineTuningJobRequest = new QueryFineTuningJobRequest(); </div> <div class="ql-code-block"> queryFineTuningJobRequest.setJobId("ftjob-20240119114544390-zkgjb"); </div> <div class="ql-code-block"> // queryFineTuningJobRequest.setLimit(1); </div> <div class="ql-code-block"> // queryFineTuningJobRequest.setAfter("1"); </div> <div class="ql-code-block"> QueryFineTuningJobApiResponse queryFineTuningJobApiResponse = client.retrieveFineTuningJobs(queryFineTuningJobRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(queryFineTuningJobApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 创建微调任务 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testCreateFineTuningJob() { </div> <div class="ql-code-block"> FineTuningJobRequest request = new FineTuningJobRequest(); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"> request.setRequestId(requestId); </div> <div class="ql-code-block"> request.setModel("chatglm3-6b"); </div> <div class="ql-code-block"> request.setTraining_file("file-20240118082608327-kp8qr"); </div> <div class="ql-code-block"> CreateFineTuningJobApiResponse createFineTuningJobApiResponse = client.createFineTuningJob(request); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(createFineTuningJobApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 微调文件上传列表查询 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testQueryUploadFileList() { </div> <div class="ql-code-block"> QueryFilesRequest queryFilesRequest = new QueryFilesRequest(); </div> <div class="ql-code-block"> QueryFileApiResponse queryFileApiResponse = client.queryFilesApi(queryFilesRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(queryFileApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 微调上传数据集 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testUploadFile() { </div> <div class="ql-code-block"> String filePath = "/Users/wujianguo/Downloads/transaction-data.jsonl"; </div> <div class="ql-code-block"> String purpose = "fine-tune"; </div> <div class="ql-code-block"> FileApiResponse fileApiResponse = client.invokeUploadFileApi(purpose, filePath); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(fileApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static void testEmbeddings() { </div> <div class="ql-code-block"> EmbeddingRequest embeddingRequest = new EmbeddingRequest(); </div> <div class="ql-code-block"> embeddingRequest.setInput("hello world"); </div> <div class="ql-code-block"> embeddingRequest.setModel(Constants.ModelEmbedding2); </div> <div class="ql-code-block"> EmbeddingApiResponse apiResponse = client.invokeEmbeddingsApi(embeddingRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(apiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 图生文 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testImageToWord() { </div> <div class="ql-code-block"> List<ChatMessage> messages = new ArrayList<>(); </div> <div class="ql-code-block"> List<Map<String, Object>> contentList = new ArrayList<>(); </div> <div class="ql-code-block"> Map<String, Object> textMap = new HashMap<>(); </div> <div class="ql-code-block"> textMap.put("type", "text"); </div> <div class="ql-code-block"> textMap.put("text", "图里有什么"); </div> <div class="ql-code-block"> Map<String, Object> typeMap = new HashMap<>(); </div> <div class="ql-code-block"> typeMap.put("type", "image_url"); </div> <div class="ql-code-block"> Map<String, Object> urlMap = new HashMap<>(); </div> <div class="ql-code-block"> urlMap.put("url", "https://cdn.bigmodel.cn/enterpriseAc/3f328152-e15c-420c-803d-6684a9f551df.jpeg?attname=24.jpeg"); </div> <div class="ql-code-block"> typeMap.put("image_url", urlMap); </div> <div class="ql-code-block"> contentList.add(textMap); </div> <div class="ql-code-block"> contentList.add(typeMap); </div> <div class="ql-code-block"> ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), contentList); </div> <div class="ql-code-block"> messages.add(chatMessage); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() </div> <div class="ql-code-block"> .model(Constants.ModelChatGLM4V) </div> <div class="ql-code-block"> .stream(Boolean.FALSE) </div> <div class="ql-code-block"> .invokeMethod(Constants.invokeMethod) </div> <div class="ql-code-block"> .messages(messages) </div> <div class="ql-code-block"> .requestId(requestId) </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> ModelApiResponse modelApiResponse = client.invokeModelApi(chatCompletionRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(modelApiResponse)); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static void testCreateImage() { </div> <div class="ql-code-block"> CreateImageRequest createImageRequest = new CreateImageRequest(); </div> <div class="ql-code-block"> createImageRequest.setModel(Constants.ModelCogView); </div> <div class="ql-code-block"> // createImageRequest.setPrompt("画一个温顺可爱的小狗"); </div> <div class="ql-code-block"> ImageApiResponse imageApiResponse = client.createImage(createImageRequest); </div> <div class="ql-code-block"> System.out.println("imageApiResponse:" + JSON.toJSONString(imageApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * sse调用 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testSseInvoke() { </div> <div class="ql-code-block"> List<ChatMessage> messages = new ArrayList<>(); </div> <div class="ql-code-block"> ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "ChatGLM和你哪个更强大"); </div> <div class="ql-code-block"> // ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "你能帮我查询2024年1月1日从北京南站到上海的火车票吗?"); </div> <div class="ql-code-block"> messages.add(chatMessage); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"> // 函数调用参数构建部分 </div> <div class="ql-code-block"> List<ChatTool> chatToolList = new ArrayList<>(); </div> <div class="ql-code-block"> ChatTool chatTool = new ChatTool(); </div> <div class="ql-code-block"> chatTool.setType(ChatToolType.FUNCTION.value()); </div> <div class="ql-code-block"> ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters(); </div> <div class="ql-code-block"> chatFunctionParameters.setType("object"); </div> <div class="ql-code-block"> Map<String, Object> properties = new HashMap<>(); </div> <div class="ql-code-block"> properties.put("departure", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "出发城市或车站"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> properties.put("destination", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "目的地城市或车站"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> properties.put("date", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "要查询的车次日期"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> List<String> required = new ArrayList<>(); </div> <div class="ql-code-block"> required.add("departure"); </div> <div class="ql-code-block"> required.add("destination"); </div> <div class="ql-code-block"> required.add("date"); </div> <div class="ql-code-block"> chatFunctionParameters.setProperties(properties); </div> <div class="ql-code-block"> ChatFunction chatFunction = ChatFunction.builder() </div> <div class="ql-code-block"> .name("query_train_info") </div> <div class="ql-code-block"> .description("根据用户提供的信息,查询对应的车次") </div> <div class="ql-code-block"> .parameters(chatFunctionParameters) </div> <div class="ql-code-block"> .required(required) </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> chatTool.setFunction(chatFunction); </div> <div class="ql-code-block"> chatToolList.add(chatTool); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() </div> <div class="ql-code-block"> .model(Constants.ModelChatGLM4) </div> <div class="ql-code-block"> .stream(Boolean.TRUE) </div> <div class="ql-code-block"> .messages(messages) </div> <div class="ql-code-block"> .requestId(requestId) </div> <div class="ql-code-block"> .tools(chatToolList) </div> <div class="ql-code-block"> .toolChoice("auto") </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> ModelApiResponse sseModelApiResp = client.invokeModelApi(chatCompletionRequest); </div> <div class="ql-code-block"> if (sseModelApiResp.isSuccess()) { </div> <div class="ql-code-block"> AtomicBoolean isFirst = new AtomicBoolean(true); </div> <div class="ql-code-block"> ChatMessageAccumulator chatMessageAccumulator = mapStreamToAccumulator(sseModelApiResp.getFlowable()) </div> <div class="ql-code-block"> .doOnNext(accumulator -> { </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> if (isFirst.getAndSet(false)) { </div> <div class="ql-code-block"> System.out.print("Response: "); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if (accumulator.getDelta() != null && accumulator.getDelta().getTool_calls() != null) { </div> <div class="ql-code-block"> String jsonString = mapper.writeValueAsString(accumulator.getDelta().getTool_calls()); </div> <div class="ql-code-block"> System.out.println("tool_calls: " + jsonString); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if (accumulator.getDelta() != null && accumulator.getDelta().getContent() != null) { </div> <div class="ql-code-block"> System.out.print(accumulator.getDelta().getContent()); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> .doOnComplete(System.out::println) </div> <div class="ql-code-block"> .lastElement() </div> <div class="ql-code-block"> .blockingGet(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> Choice choice = new Choice(chatMessageAccumulator.getChoice().getFinishReason(), 0L, chatMessageAccumulator.getDelta()); </div> <div class="ql-code-block"> List<Choice> choices = new ArrayList<>(); </div> <div class="ql-code-block"> choices.add(choice); </div> <div class="ql-code-block"> ModelData data = new ModelData(); </div> <div class="ql-code-block"> data.setChoices(choices); </div> <div class="ql-code-block"> data.setUsage(chatMessageAccumulator.getUsage()); </div> <div class="ql-code-block"> data.setId(chatMessageAccumulator.getId()); </div> <div class="ql-code-block"> data.setCreated(chatMessageAccumulator.getCreated()); </div> <div class="ql-code-block"> data.setRequestId(chatCompletionRequest.getRequestId()); </div> <div class="ql-code-block"> sseModelApiResp.setFlowable(null); </div> <div class="ql-code-block"> sseModelApiResp.setData(data); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(sseModelApiResp)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static Flowable<ChatMessageAccumulator> mapStreamToAccumulator(Flowable<ModelData> flowable) { </div> <div class="ql-code-block"> return flowable.map(chunk -> { </div> <div class="ql-code-block"> return new ChatMessageAccumulator(chunk.getChoices().get(0).getDelta(), null, chunk.getChoices().get(0), chunk.getUsage(), chunk.getCreated(), chunk.getId()); </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 同步调用 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testInvoke() { </div> <div class="ql-code-block"> List<ChatMessage> messages = new ArrayList<>(); </div> <div class="ql-code-block"> ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "ChatGLM和你哪个更强大"); </div> <div class="ql-code-block"> messages.add(chatMessage); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"> // 函数调用参数构建部分 </div> <div class="ql-code-block"> List<ChatTool> chatToolList = new ArrayList<>(); </div> <div class="ql-code-block"> ChatTool chatTool = new ChatTool(); </div> <div class="ql-code-block"> chatTool.setType(ChatToolType.FUNCTION.value()); </div> <div class="ql-code-block"> ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters(); </div> <div class="ql-code-block"> chatFunctionParameters.setType("object"); </div> <div class="ql-code-block"> Map<String, Object> properties = new HashMap<>(); </div> <div class="ql-code-block"> properties.put("location", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "城市,如:北京"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> properties.put("unit", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("enum", new ArrayList<String>() {{ </div> <div class="ql-code-block"> add("celsius"); </div> <div class="ql-code-block"> add("fahrenheit"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> chatFunctionParameters.setProperties(properties); </div> <div class="ql-code-block"> ChatFunction chatFunction = ChatFunction.builder() </div> <div class="ql-code-block"> .name("get_weather") </div> <div class="ql-code-block"> .description("Get the current weather of a location") </div> <div class="ql-code-block"> .parameters(chatFunctionParameters) </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> chatTool.setFunction(chatFunction); </div> <div class="ql-code-block"> chatToolList.add(chatTool); </div> <div class="ql-code-block"> ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() </div> <div class="ql-code-block"> .model(Constants.ModelChatGLM4) </div> <div class="ql-code-block"> .stream(Boolean.FALSE) </div> <div class="ql-code-block"> .invokeMethod(Constants.invokeMethod) </div> <div class="ql-code-block"> .messages(messages) </div> <div class="ql-code-block"> .requestId(requestId) </div> <div class="ql-code-block"> .tools(chatToolList) </div> <div class="ql-code-block"> .toolChoice("auto") </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest); </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> System.out.println("model output:" + mapper.writeValueAsString(invokeModelApiResp)); </div> <div class="ql-code-block"> } catch (JsonProcessingException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 异步调用 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static String testAsyncInvoke() { </div> <div class="ql-code-block"> List<ChatMessage> messages = new ArrayList<>(); </div> <div class="ql-code-block"> ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "ChatLM和你哪个更强大"); </div> <div class="ql-code-block"> messages.add(chatMessage); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"> // 函数调用参数构建部分 </div> <div class="ql-code-block"> List<ChatTool> chatToolList = new ArrayList<>(); </div> <div class="ql-code-block"> ChatTool chatTool = new ChatTool(); </div> <div class="ql-code-block"> chatTool.setType(ChatToolType.FUNCTION.value()); </div> <div class="ql-code-block"> ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters(); </div> <div class="ql-code-block"> chatFunctionParameters.setType("object"); </div> <div class="ql-code-block"> Map<String, Object> properties = new HashMap<>(); </div> <div class="ql-code-block"> properties.put("location", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "城市,如:北京"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> properties.put("unit", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("enum", new ArrayList<String>() {{ </div> <div class="ql-code-block"> add("celsius"); </div> <div class="ql-code-block"> add("fahrenheit"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> chatFunctionParameters.setProperties(properties); </div> <div class="ql-code-block"> ChatFunction chatFunction = ChatFunction.builder() </div> <div class="ql-code-block"> .name("get_weather") </div> <div class="ql-code-block"> .description("Get the current weather of a location") </div> <div class="ql-code-block"> .parameters(chatFunctionParameters) </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> chatTool.setFunction(chatFunction); </div> <div class="ql-code-block"> chatToolList.add(chatTool); </div> <div class="ql-code-block"> ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() </div> <div class="ql-code-block"> .model(Constants.ModelChatGLM4) </div> <div class="ql-code-block"> .stream(Boolean.FALSE) </div> <div class="ql-code-block"> .invokeMethod(Constants.invokeMethodAsync) </div> <div class="ql-code-block"> .messages(messages) </div> <div class="ql-code-block"> .requestId(requestId) </div> <div class="ql-code-block"> .tools(chatToolList) </div> <div class="ql-code-block"> .toolChoice("auto") </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(invokeModelApiResp)); </div> <div class="ql-code-block"> return invokeModelApiResp.getData().getTaskId(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 查询异步结果 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * @param taskId </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testQueryResult(String taskId) { </div> <div class="ql-code-block"> QueryModelResultRequest request = new QueryModelResultRequest(); </div> <div class="ql-code-block"> request.setTaskId(taskId); </div> <div class="ql-code-block"> QueryModelResultResponse queryResultResp = client.queryModelResult(request); </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> System.out.println("model output:" + mapper.writeValueAsString(queryResultResp)); </div> <div class="ql-code-block"> } catch (JsonProcessingException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h5>(1) 填充自己的信息</h5> <p><br></p> <p></p> <p><br></p> <p><br></p> <p><br></p> <h5>(2) 启动示例</h5> <p><br></p> <p></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p>这里解释一下四个的区别</p> <ol> <li data-list="number"><span class="ql-ui"></span>testSseInvoke 使用的是逐渐输出,AI回答的结果是一段一段的展示。</li> <li data-list="number"><span class="ql-ui"></span>testInvoke 使用的是同步执行,当AI全部的回答都输出后才会展示出来。</li> <li data-list="number"><span class="ql-ui"></span>testAsyncInvoke 与testQueryResult 搭配使用,先通过testAsyncInvoke 让AI去执行,直接返回一个成功或者失败,之后通过获得的taskId,再用testQueryResult去查询获得结果即可。这个过程实践过Bi项目的小伙伴应该深有体会。</li> </ol> <p><br></p> <p><br></p> <h5>(3) 结果展示</h5> <p><br></p> <p></p> <p><br></p> <p></p> <p><br></p> <p><br></p> <p><span style="color: rgb(216, 57, 49);">这里补充下,如果你要修改问题,只需要修改content参数中的值即可。相当于问AI问题。</span></p> <p><br></p> <p><br></p> <p><br></p> <h4>参数阅读</h4> <p></p> <p><br></p> <p>其实最关键的就是前三个</p> <p><strong>A. model </strong></p> <p>你要选择哪个模型, 例如选择GLM-4 还是GLM-3-Turbo</p> <p></p> <p><br></p> <p><strong>B. messages</strong></p> <p>这里需要考虑两个值,一个是role,一般为user即可。role的值官方已经给我们枚举了,只需要调用即可。</p> <p>剩下的则是我们需要自己填入的content</p> <p></p> <p><br></p> <p><br></p> <p><strong>C. request_id</strong></p> <p>这个是区分我们每次上传的任务,保证唯一性,可以自己上传一个类似于雪花算法的ID,用户端不传的话平台也会自动生成。</p> <p><br></p> <p>其他参数目前影子测试完前五个方法后发现使用官方默认的即可。只需要你调整好代码的位置以及content的值即可。</p> <p></p> <p><br></p> <p>剩下的参数,如果你需要使用微调或者向量知识库等高阶玩法,则根据官方文档调整即可。很多地方已经自带了枚举值,只需要直接选择填充。</p> <p><br></p> <p>最后,大家可以用这一个月的免费额度,打造一个属于自己的AI小工具使用,更多玩法,由大家一起探索。</p> <p><br></p> <p>我是影子,一名以Java为主,其余时间探索AI+编程的程序猿。</p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> </div> </body> </html>
用户中心项目 笔记
用户中心 项目# 完成的第一个星球项目,用户中心,跟着录播学,学到了很多东西,有一种越学越有劲的感觉,前端部分我自己之前了解很少,看完直播之后使用Ant Design pro真的感觉很方便,看前端的源代码也能学到前端的知识,后端部分算是巩固了之前学的Javaweb,还学到mybatis-plus使用idea直接生成xml文件很方便,自定义异常处理和全局异常处理,自己也遇到了些许bug也都解决了。剩下部署和上线还没有完成,下一步计划是跟随录播完成伙伴匹配项目,之后尝试着去找实习了. 
【项目】星球项目学习建议(包括学习顺序和所需技术栈)
<html> <head></head> <body> <div class="content ql-editor"> <p>学完至少一门前端(比如 Vue / React)或后端框架(比如 Spring Boot)后,可以开始鱼皮原创项目系列学习。</p> <h2>推荐学习顺序</h2> <ol> <li data-list="ordered"><span class="ql-ui"></span><a href="https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd#BZhNdGqsboK2s2xswdJcoAu0nSf" target="_blank">用户中心项目</a>:适合新手入门,系统学习完整的项目开发流程和上线方法。</li> <li data-list="ordered"><span class="ql-ui"></span><a href="https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd#HSlAdvdIyo2L4txIdrec6sLbnJh" target="_blank">伙伴匹配系统</a>:巩固开发流程,学习 Redis、事务、并发编程、大数据推荐思想等后端知识。</li> <li data-list="ordered"><span class="ql-ui"></span><a href="https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd#FlurdESjEohQOQxHUz9c0Hh4nAc" target="_blank">API 开放平台</a>:学习实践前后端模板开发 + 架构设计 + SDK 开发 + API 签名认证 + Dubbo RPC + Gateway 微服务网关。</li> <li data-list="ordered"><span class="ql-ui"></span><a href="https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd#R88QdQH8woDxzfx7ow3cwfmOngh" target="_blank">聚合搜索平台</a>:学习实践爬虫 + Elastic Stack + 设计模式 + 数据同步 + JMeter 压力测试。</li> <li data-list="ordered"><span class="ql-ui"></span><a href="https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd#UJnTdmWaMo7DLAxjv6jcoDdjnpg" target="_blank">智能 BI 项目</a>:学习实践异步化 + 线程池 + RabbitMQ 消息队列 + AI 应用开发 + AIGC Prompt 优化。</li> <li data-list="ordered"><span class="ql-ui"></span><a href="https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd#X6WDdmxB7oDeXFxUYrqci6wznEc" target="_blank">OJ 判题系统</a>:学习实践前后端模板开发 + 多种设计模式 + 单体项目微服务改造 + Linux 虚拟机远程开发 + Docker 代码沙箱 + Java 安全控制。</li> <li data-list="ordered"><span class="ql-ui"></span><a href="https://yuyuanweb.feishu.cn/wiki/SePYwTc9tipQiCktw7Uc7kujnCd#GoVEdwefkoY10BxcAb8csfebnZc" target="_blank">定制化代码生成项目</a>:深入业务场景,学习实践命令行开发、模板引擎、Vert.x、设计模式、对象存储、性能优化、系统设计、分布式任务调度系统等。</li> </ol> <h2>正确的学习方法</h2> <p>1)在做项目的过程中,尽量多思考、多自己解决问题、多记录笔记,而且一定要自己敲代码!</p> <p>2)每做完一个项目,都必须写一篇完整的总结文档,便于日后复习理解。</p> <p>3)当你掌握独立开发项目的能力后,建议自行给项目增加扩展点,增加简历的区分度。</p> <p>如果能做到上面几点,认真完成并理解 4 个及以上的项目,做项目的能力可达中厂水平。</p> <p><br></p> <h2>项目学习导图</h2> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/lkvznl4d.jpeg"></p> </div> </body> </html>
服务器没挂,为啥无法访问?
<html> <head></head> <body> <div class="content ql-editor"> <p>分享一个非常经典的问题:明明服务器没挂,为啥访问不了云服务器的端口?</p> <p></p> <p>这篇文章,我列举了常见的、无法访问服务器的情况和解决方案,便于大家排查问题。</p> <p></p> <h4>1、应用程序未启动</h4> <p></p> <p>这是最低级的错误,明明自己的项目都没有启动成功,就开始排查是不是服务器的问题了。</p> <p></p> <p>如果你使用的是宝塔 Linux 面板,而不是自己通过执行 jar 包命令来启动项目,那么这个坑更容易出现。因为有时候,项目刚启动的时候显示 “运行中”,但可能由于系统异常,过几秒钟就变成 “未启动了”。</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/mktqyfok.jpeg" alt=""></p> <p></p> <p>想确保项目已启动,可以使用 Linux 的 <code>ps</code> 命令查看应用进程,比如:</p> <p></p> <div class="ql-code-block-container"> <div class="ql-code-block"> ps aux | grep <你的项目名> </div> </div> <p></p> <p>如果项目已启动,将看到类似下图的输出:</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/exqevkgn.jpeg" alt=""></p> <p></p> <h4>2、端口监听错误</h4> <p></p> <p>除了正确启动项目外,要保证项目监听了正确的端口,可以通过 <code>netstat</code> 命令查看服务器的端口占用情况。</p> <p></p> <p>比如:</p> <p></p> <div class="ql-code-block-container"> <div class="ql-code-block"> netstat -ntlp | grep <你的端口号> </div> </div> <p></p> <p>如果端口正确监听,将看到类似下图的输出:</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/kfm9o09b.jpeg" alt=""></p> <p></p> <p>需要注意输出信息中的 <code>:::8104</code> 和 <code>:::*</code> ,表示程序监听在所有 IPv4 地址上的 8104 端口,并且允许所有来源的 IP 地址连接。</p> <p></p> <p>如果是其他值,可能会出现无法远程访问端口的情况,这时就需要调整应用程序的启动端口配置。</p> <p></p> <h4>3、云服务商安全组</h4> <p></p> <p>第一次使用云服务器很容易踩这个坑!你在哪家服务商购买了服务器,就要进入到对应的服务器后台安全组设置界面,确保允许指定来源的流量通过端口。</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/qxg78ape.jpeg" alt=""></p> <p></p> <p>上图中,来源设置为 0.0.0.0/0,表示允许所有用户访问,建议谨慎设置。如果是学习用,可以把来源改为自己家的 IP 地址,先只允许自己访问。</p> <p></p> <h4>4、服务器防火墙</h4> <p></p> <p>为了保护系统安全,Linux 系统内置了防火墙,除了在云服务商侧开放端口外,最好也检查一下防火墙的配置。</p> <p></p> <p>不同 Linux 发行版操作系统对应的防火墙命令可能不同,以下是 Ubuntu 的查看防火墙命令,两种方式都可以。</p> <p></p> <div class="ql-code-block-container"> <div class="ql-code-block"> # 查看防火墙状态 sudo iptables -L sudo systemctl status firewalld </div> </div> <p></p> <p>执行效果如图:</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/lgjmdpqg.jpeg" alt=""></p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/270skjf4.jpeg" alt=""></p> <p></p> <p>如果应用访问不了,可以关闭防火墙试试(不过一般不建议这么做):</p> <p></p> <div class="ql-code-block-container"> <div class="ql-code-block"> sudo systemctl stop firewalld sudo systemctl disable firewalld </div> </div> <p></p> <h4>5、应用层限制</h4> <p></p> <p>除了操作系统的防火墙之外,我们的应用程序也可能 “暗藏玄 🐓”,禁止一些 IP 的访问。</p> <p></p> <p>比如宝塔 Linux 的安全 / 防火墙面板:</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/yhm1zq0i.jpeg" alt=""></p> <p></p> <p>还可以通过 Nginx 或者业务代码对访问的 IP 进行限制,这些是常用的安全策略,别因为这些把正常用户的访问也限制了哦。</p> <p></p> <h4>6、服务器网络问题</h4> <p></p> <p>这是一种比较极端的情况,比如服务器突然断网了、或者自己瞎折腾把服务器网络设置搞错了等等。</p> <p></p> <p>可以通过 <code>ping</code> 或者 <code>traceroute</code> 命令,检测服务器的网络连通性:</p> <p></p> <div class="ql-code-block-container"> <div class="ql-code-block"> ping <你的服务器地址> traceroute <你的服务器地址> </div> </div> <p></p> <h4>7、用户自身问题</h4> <p></p> <p>最后这种情况最有意思,也是最意外、最难被排查出的问题。</p> <p></p> <p>那就是用户自己有问题!</p> <p></p> <p>我们之前就遇到过一个情况,用户说访问不了我们的网站了,结果后来反馈说是自己家的网络突然断了。。我们还纳闷呢!</p> <p></p> <p>当时我们就是这个表情:</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/sdytljox.jpeg" alt=""></p> <p></p> <p>还有一种情况,就是服务器禁用了海外的 IP,导致部分用户能访问、部分用户无法访问。</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/daj5oa94.jpeg" alt=""></p> <p></p> <p>刚刚问我服务器无法访问这个问题的同学,最后就发现是因为海外 ip 的原因,切个网络就正常了~</p> <p></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1601072287388278786/qpxupuqf.jpeg" alt=""></p> </div> </body> </html>
伙伴匹配项目 笔记
昨天刚交完用户中心项目的,今天继续交和伙伴匹配系统的作业[拳头][拳头][拳头] 项目收获: 队伍系统设计,先设计再实现 大量数据查询,使用 Redis 缓存优化重复查询请求 Swagger Kinfe4j 接口文档使用 Redis 分布式登录实现 spring-session-data-redis,整合 Spring Session 与 Redis,存储 Session 时,自动存储 Session 至 Redis 中 spring-boot-starter-data-redis,操作Redis Spring Scheduler 定时任务,corn 定时任务 Redisson 实现分布式锁 跨域再解决:CorsRegistry 和 CorsFilter 区别 https://t.zsxq.com/16cg4BTqj

