vue+java实现简易AI问答组件(基于百度文心大模型)

一、需求

公司想要在页面中加入AI智能对话功能,故查找免费gpt接口,最终决定百度千帆大模型(进入官网、官方文档中心);

二、主要功能列举

  • AI智能对话;
  • 记录上下文回答环境;
  • 折叠/展开窗口;
  • 可提前中止回答;
  • 回答内容逐字展示并语音播报;

三、效果图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、技术选型

1、前端环境

  • node(14.21.3)
  • VueCli 2
  • element-ui(^2.15.14)
  • axios
  • node-sass(^4.14.1)
  • sass-loader(^7.3.1)
  • js-md5(^0.8.3)

2、后端环境

  • JDK8
  • springboot

五、声明

  • 本文章以及源码纯粹自己写着玩,等于是个demo,有许多需要完善和优化的地方,仅供大家参考,有错误的地方欢迎大家批评指正~~
  • 由于作者其实是java,所以前端代码中如果看到神奇的地方,希望大家包涵,哈哈哈哈

四、百度千帆大模型应用创建

1、访问官网,注册账号并登录;

2、选择“应用接入”-“创建应用”

在这里插入图片描述
进去填一个应用名及描述即可,服务默认全勾选上;

3、保存后返回应用列表,获取api key和secret key

在这里插入图片描述

PS:

百度提供的大模型服务有好多种,我此处是白嫖的其中一个免费的,如下图:
其中ERNIE开头的是百度自己的,文心一言用的就是这种,其他有些是三方大模型;
在这里插入图片描述

具体不同服务之间有什么区别可以看官方介绍,个人觉得免费的几个主要在于轻量等级、响应速度、回答内容复杂程度、可保存的上下文大小等几个方面;

五、部分后台代码

1、官网下载java sdk或者引入百度千帆pom

官放文档地址:https://cloud.baidu.com/doc/WENXINWORKSHOP/s/7ltgucw50
java SDK地址:https://github.com/baidubce/bce-qianfan-sdk/tree/main/java
maven仓库地址:https://mvnrepository.com/artifact/com.baidubce/qianfan

POM:

<!-- https://mvnrepository.com/artifact/com.baidubce/qianfan -->
<dependency><groupId>com.baidubce</groupId><artifactId>qianfan</artifactId><version>0.0.4</version>
</dependency>

2、创建springboot项目,并导入sdk或引入千帆pom

此处是把sdk导入到工程中;

3、项目代码结构如下

在这里插入图片描述

pom.xml :

<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.baidu</groupId><artifactId>aichat</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>aichat</name><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.9</version></parent><dependencies><!-- SpringBoot的依赖配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.2.13.RELEASE</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.10.1</version></dependency><dependency><groupId>org.apache.httpcomponents.client5</groupId><artifactId>httpclient5</artifactId><version>5.3.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.1.1.RELEASE</version><configuration><fork>true</fork> <!-- 如果没有该配置,devtools不会生效 --></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>3.1.0</version><configuration><failOnMissingWebXml>false</failOnMissingWebXml><warName>${project.artifactId}</warName></configuration></plugin></plugins><finalName>${project.artifactId}</finalName></build></project>

AiChatController.java:

package com.baidubce.controller;import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import com.baidubce.qianfan.Qianfan;
import com.baidubce.qianfan.core.builder.ChatBuilder;
import com.baidubce.qianfan.model.chat.ChatResponse;
import com.baidubce.qianfan.model.chat.Message;
import com.baidubce.utils.JsonUtils;
import com.baidubce.utils.SecUtils;@RestController
public class AiChatController {private static final String accessKey = "你创建的应用的API Key";private static final String secretKey = "你创建的应用的Secret Key";private static Qianfan qianfan = new Qianfan("OAuth", accessKey, secretKey);/*** 	参数:* 		messages: 对话记录,role:user是用户,assistant是AI,如:[{"role":"user","content":"1"},{"role":"assistant","content":"“1”是一个数字。"},{"role":"user","content":"你是"},{"role":"assistant","content":""}]* 		timestamp:请求毫秒值,1717742825695* 		signature: 签名,4bc9c3b8dbe4de5bc924b6fa0506c606* @author x轩* @version 2024年6月7日 下午2:46:32*/@PostMapping("/sendMsg")public String sendMsg(@RequestBody Map<String, Object> params) {// 验签,我自己加的,防止恶意调用,作用不大,提高门槛而已if(!checkSign(params)) {return "签名不正确!";}String result = null;try {result = chat(String.valueOf(params.get("messages")));} catch (Exception e) {e.printStackTrace();return "接口繁忙,请稍后再试!";}return result;}/*** 	参数:* 		messages: 业务参数* 		timestamp:请求毫秒值* 		signature: 签名* *	加签规则:*		要求1:timestamp和当前系统时间不能超过5秒钟*		要求2:MYCHAT|timestamp|messages拼接后MD53次加密** @author x轩* @version 2024年6月6日 下午4:13:03*/private boolean checkSign(Map<String, Object> params) {String timestamp = String.valueOf(params.get("timestamp"));String messages = String.valueOf(params.get("messages"));String signature = String.valueOf(params.get("signature"));if(StringUtils.isAnyBlank(timestamp, messages, signature)) {return false;}// 1.判断时间if((System.currentTimeMillis()- Long.valueOf(timestamp))>5000) {// 过期return false;}// 2.验签String p = "MYCHAT|"+timestamp+"|"+messages;String md5of3 = SecUtils.encoderByMd5With32Bit(SecUtils.encoderByMd5With32Bit(SecUtils.encoderByMd5With32Bit(p)));if(!signature.equalsIgnoreCase(md5of3)) {return false;}return true;}public static void main(String[] args) {
//        chat("对于调休你怎么看");chatStream("介绍一下自己");}private static String chat(String messages) {ChatBuilder bulder = qianfan.chatCompletion()
//        		.model("ERNIE-Speed-128K")
//        		.model("ERNIE-Speed-8K").model("ERNIE-Tiny-8K");List<Message> messageList = JsonUtils.readValues(messages, Message.class);// 过滤一下,去除空内容对象messageList = messageList.stream().filter(m->{return StringUtils.isNotBlank(m.getContent());}).collect(Collectors.toList());for(Message m : messageList) {bulder.addMessage(m);}ChatResponse response = bulder.execute();return response.getResult();}private static void chatStream(String message) {Iterator<ChatResponse> stream = qianfan.chatCompletion()
//		.model("ERNIE-Speed-128K")
//		.model("ERNIE-Speed-8K").model("ERNIE-Tiny-8K").addMessage("user", message).executeStream();while(stream.hasNext()) {System.out.println(stream.next().getResult());}}
}

六、 Vue部分代码

1、vue.config.js

const port = process.env.port || process.env.npm_config_port || 80 // 端口module.exports = {lintOnSave: false,publicPath: "/aichat-front",assetsDir: 'static',// 如果你不需要生产环境的 source map,可以将其设置为 false 以加速生产环境构建。productionSourceMap: false,devServer: {host: '0.0.0.0',port: port,open: true,proxy: {['/aichat']: {target: `http://127.0.0.1:8080/aichat`,changeOrigin: true,pathRewrite: {['^/aichat']: ''}},},},
}

2、App.vue

<template><div id="app"><qian-fan-chat/></div>
</template><script>
import QianFanChat from './components/QianFanChat'export default {name: 'App',components: {QianFanChat}
}
</script><style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}
</style>

3、@/components/QianFanChat组件

<template><div class="chat-div"><div v-show="showChatBox" class="chat-main"><div id="messagediv" class="messagediv"><div class="item"><img class="avatar" :src="aiAvatar" ><div class="answerDiv"><p>我是AI智能小助手,有问题请咨询我吧!</p></div></div><div v-for="(item, index) in messageList" class="item"><img v-if="item.role=='assistant'" class="avatar" :src="aiAvatar" ><img v-if="item.role=='user'" class="avatar" :src="userAvatar" ><div class="answerDiv"><p v-if="!item.loading" v-html="item.content"></p><p v-else>思考中 <i class="el-icon-loading"></i></p><a v-if="index==messageList.length-1 &&item.role=='assistant' && (loading || speaking)" href="#" @click="stopAnswer">停止回答</a></div></div></div><div class="sendDiv"><el-input @keyup.enter.native="getAnswer" v-model="question" placeholder="输入中医药相关内容搜一搜"></el-input><el-button @click="getAnswer"><i class="el-icon-s-promotion"></i></el-button></div></div><div v-show="showChatBox" class="close-btn" @click="showChatBox=false"><i class="el-icon-close"></i></div><div v-show="!showChatBox" class="small-window" @click="showChatBox=true">AI问答</div></div>
</template>
<script>
const msg = new SpeechSynthesisUtterance();import { sendMsg } from '@/api/aichat';
import { sign } from '@/utils/securityUtil'
import aiAvatar from '@/assets/images/ai-avatar.jpg';
import userAvatar from '@/assets/images/user-avatar.jpg';export default {name: 'QianFanChat',data(){return {showChatBox: false,loading: false,speaking: false,aiAvatar,userAvatar,question:'',messageList:[],// 逐字输出 STARTtimer: null,length: 0,index: 0,// 逐字输出 END}},mounted(){},methods: {getAnswer(){if(this.loading){this.$message({type: 'warning', message:'正在回答,请耐心等待!'});return;}if(!this.question.trim()){this.$message({type: 'warning', message:'请输入内容!'});return;}this.loading = true;let tmpQustion = this.question;this.question = '';this.messageList.push({ role:'user', content: tmpQustion, loading: false });this.messageList.push({ role:'assistant', content: '' , loading: true});this.$nextTick(()=>{this.scroll();})let params = {messages: JSON.stringify(this.messageList)};params.timestamp = new Date().getTime();params.signature = sign(params);sendMsg(params).then(res=>{// 判断loading是否被中断(停止回答可中断)if(!this.loading){// 点击了“停止回答”return;}// 接口请求完毕,替换最后一条内容this.messageList[this.messageList.length-1].loading = false;this.index = 0;this.length = res.length;this.handleSpeak(res);// 一个字一个字给我蹦this.timer = setInterval(()=>{if(this.index<=this.length-1){let word = res.charAt(this.index);if(word=='\n'){word = '<br>'}this.messageList[this.messageList.length-1].content += word;this.index++;}else{// 结束this.loading = false;clearInterval(this.timer);}this.scroll();}, 30);})},scroll(){messagediv.scrollTo({top: messagediv.scrollHeight,})},stopAnswer(){this.handleStop();clearInterval(this.timer);this.length = 0;this.index = 0;this.loading = false;// 判断最后一条内容是不是空,是则给上默认输出let lastMsg = this.messageList[this.messageList.length-1];if(!lastMsg.content){lastMsg.loading = false;lastMsg.content = '请继续向我提问吧!';}},// 语音播报的函数handleSpeak(text) {this.handleStop();this.speaking = true;// 处理多音字msg.text = text;     // 朗读内容msg.lang = "zh-CN";  // 使用的语言:中文 msg.volume = 0.5;      // 声音音量:1  设置将在其中发言的音量。区间范围是0到1,默认是1msg.rate = 1.6;        // 语速:1  设置说话的速度。默认值是1,范围是0.1到10,表示语速的倍数,例如2表示正常语速的两倍msg.pitch = 1.5;       // 音高:2  设置说话的音调(音高)。范围从0(最小)到2(最大)。默认值为1// msg.voiceURI = 'Google 普通话(中国大陆)';msg.onstart = (e)=>{};msg.onend = (e)=>{this.speaking = false;};msg.onboundary = (e) => {}speechSynthesis.speak(msg);    // 播放},// 语音停止handleStop(e) {this.speaking = false;msg.text = e;msg.lang = "zh-CN";speechSynthesis.cancel(msg);},}
}
</script>
<style lang="scss" scoped>::v-deep {.el-input {width: 270px;input {background-color: rgba(0,0,0,.5);border: none;color: white;}}.el-button {background-color: rgba(0,0,0,.5);border: none;margin-left: 10px;padding: 0 20px;i {font-size: 20px;color: white;}&:focus {background-color: rgba(0,0,0,.5);}&:hover {background-color: white;i {color: black;}}}}.chat-div {position: absolute;top: 0;left: 0;display: flex;z-index: 99;cursor: pointer;.close-btn {margin-top: 18px;color: white;background-color: rgba(0, 0, 0, .6);height: 30px;width: 30px;text-align: center;line-height: 30px;border-radius: 50%;}.small-window {color: white;margin-top: 18px;padding: 10px;background-color: rgba(16, 168, 129, .8);width: 26px;font-size: 20px;border-top-right-radius: 10px;border-bottom-right-radius: 10px;}}.chat-main {border-radius: 4px;margin: 10px 0;padding: .1rem .1rem;width: 374px;.messagediv {overflow: auto;max-height: 60vh;.item {display: flex;margin-bottom: .1rem;.avatar {width: 40px;height: 40px;}.answerDiv {p {color: white;background-color: rgba(0, 0, 0, .6);padding: 10px;margin: 0 10px;font-size: 16px;border-radius: 6px;text-align: left;}a {cursor: pointer;font-size: 15px;color: red;text-decoration: underline;margin-left: 10px;font-weight: bold;}}}&::-webkit-scrollbar {width: 0;}}.sendDiv {display: flex;justify-content: end;margin: 10px 10px 0 0;}}
</style>

4、@/utils/request.js

import axios from 'axios'
import { Notification, MessageBox, Message } from 'element-ui'axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({// axios中请求配置有baseURL选项,表示请求URL公共部分baseURL: process.env.VUE_APP_BASE_API,// 超时timeout: 50000
})
// request拦截器
service.interceptors.request.use(config => {// get请求映射params参数if (config.method === 'get' && config.params) {let url = config.url + '?';for (const propName of Object.keys(config.params)) {const value = config.params[propName];var part = encodeURIComponent(propName) + "=";if (value !== null && typeof (value) !== "undefined") {if (typeof value === 'object') {for (const key of Object.keys(value)) {if (value[key] !== null && typeof (value[key]) !== 'undefined') {let params = propName + '[' + key + ']';let subPart = encodeURIComponent(params) + '=';url += subPart + encodeURIComponent(value[key]) + '&';}}} else {url += part + encodeURIComponent(value) + "&";}}}url = url.slice(0, -1);config.params = {};config.url = url;}return config
}, error => {console.log(error)Promise.reject(error)
})// 响应拦截器
service.interceptors.response.use(res => {return res.data;},error => {console.log('err' + error)let { message } = error;if (message == "Network Error") {message = "后端接口连接异常";}else if (message.includes("timeout")) {message = "系统接口请求超时";}else if (message.includes("Request failed with status code")) {message = "系统接口" + message.substr(message.length - 3) + "异常";}Message({message: message,type: 'error',duration: 5 * 1000})return Promise.reject(error)}
)export default service

5、@/utils/securityUtil.js

PS: 由于这个项目不需要登录,我又怕接口泄露导致别人恶意调用,所以给接口加了个签名,具体策略大家可以自定义(讲真的,没什么用,前台加签别人打开调试模式照样可以看到加签策略。。。为了应对这个情况,我把前台加签JS给做了个混淆,算是增加一下门槛吧;还可以禁止用户点击F12和右键事件【具体代码见此篇文章】)

// 加签方法,方法接收两个参数: timestamp和messages(消息JSON字符串),返回签名;已混淆,以下代码具体签名策略如下:固定字符串"MYCHAT"、时间戳、消息体用|拼接后进行3次MD5加密:如MYCHAT|1718181053994|[{"role":"user","content":"1"},{"role":"assistant","content":"“1”是一个数字。"}]
const _0x4e66=['MYCHAT','timestamp'];const _0x3524=function(_0x4e6602,_0x35247b){_0x4e6602=_0x4e6602-0x0;let _0x2ee47d=_0x4e66[_0x4e6602];return _0x2ee47d;};import _0x50d0de from'js-md5';export function sign(_0x5c789a){let _0x1bf721='|'+_0x5c789a[_0x3524('0x1')];let _0x202f97='|'+_0x5c789a['messages'];let _0x74f806=_0x3524('0x0')+_0x1bf721+_0x202f97;_0x74f806=_0x50d0de(_0x74f806);_0x74f806=_0x50d0de(_0x74f806);_0x74f806=_0x50d0de(_0x74f806);return _0x74f806;}

6、@/api/aichat.js

import request from '@/utils/request'export function sendMsg(data) {return request({url: '/sendMsg',method: 'post',data: data})}

有问题或者需要完整项目源码的话私聊吧,关机下班底薪到手~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/26621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用Qt 中的QPainter类画时钟

在Qt中&#xff0c;我们可以通过QPainter类来绘制各种图形&#xff0c;包括时钟指针。以下是一个简单的例子&#xff0c;展示了如何在Qt中绘制一个简单的时钟指针。 #include <QPainter> #include <QWidget> #include <QTimer> #include <QTime>class…

管道(channel)select

select功能&#xff1a;解决多个管道的选择问题&#xff0c;也可以叫多路复用&#xff0c;可以从多个管道中随机公平的选择一个来执行 case后面必须进行的是io操作&#xff0c;不能是等值&#xff0c;随机去选择一个io操作 default防止select被阻塞&#xff0c;加入default 只…

使用MATLAB对地铁站、公交站等求解最短路径

使用MATLAB对城市的地铁站、公交站等站点&#xff0c;根据站点的经纬度坐标和彼此之间的权重&#xff0c;求解其最短路径、途径站点和路程 已知的数据如图&#xff0c;是西安市地铁站点的数据&#xff0c;保存在一个Excel里 如图&#xff0c;每列的内容都在上面&#xff0c;不…

滚动条样式

/*滚动条*/ //滚动条没有滑块的轨道部分. ::-webkit-scrollbar-track-piece {background: #fff;border-radius: 0px;opacity: 1;border: 1px solid #D9D9D9; } //整个滚动条 ::-webkit-scrollbar {width: 8px;height: 8px;background-color: transparent; } //滚动条上的滚动滑…

经常加班却不给加班费,我又不想离开这个单位,该怎么办?

经常加班却不给加班费&#xff0c;我又不想离开这个单位&#xff0c;该怎么办&#xff1f; --#李秘书讲写作#教你如何在维护权益与保持工作稳定间找到平衡&#xff1f; 在现代社会中&#xff0c;加班已经成为许多职场人士的常态。你也许也不例外&#xff0c;每天都在忙碌的工作…

Python教程:机器学习 - 百分位数(4)

什么是百分位数&#xff1f; 统计学中使用百分位数&#xff08;Percentiles&#xff09;为您提供一个数字&#xff0c;该数字描述了给定百分比值小于的值。 例如&#xff1a;假设我们有一个数组&#xff0c;包含住在一条街上的人的年龄。 ages [5,31,43,48,50,41,7,11,15,3…

深入理解服务器路由功能:配置与应用详解

在现代网络结构中&#xff0c;服务器不仅仅是提供数据和服务的中心节点&#xff0c;它们还经常承担着路由的角色&#xff0c;负责在不同网络之间转发数据包。这一过程依赖于服务器的路由功能&#xff0c;通过精确的路由表配置和管理&#xff0c;确保数据能够高效、准确地到达目…

SSL协议在实际生活中有哪些应用实例?

SSL协议的实际应用案例 SSL&#xff08;Secure Sockets Layer&#xff09;协议是一种网络通信协议&#xff0c;用于在客户端和服务器之间建立加密链接&#xff0c;以确保数据在传输过程中的安全性。尽管SSL协议已被TLS&#xff08;Transport Layer Security&#xff09;协议所取…

AMS(ActivityManagerService)源码解析,Activity是如何被打开的

AMS解析&#xff0c;Activity是如何被打开的 前言总结1. PMS保存App缩略信息2. startActivity发送请求3. AMS从PMS获取目标Activity的相关信息3.1 AMS部分3.2 PMS部分 4. AMS操作Activity返回栈&#xff0c;将目标Activity调用resume5. AMS将目标页面信息发给ActivityThread6. …

STM32学习笔记(二)--GPIO通用输入输出口详解

&#xff08;1&#xff09;配置步骤1.使用RCC开启GPIO时钟2.使用GPIO_Init函数初始化GPIO3.使用输入或输出的函数等来控制GPIO 其中涉及外设有RCC、GPIO &#xff08;2&#xff09;代码示例 案例1&#xff08;设置PA0为推挽输出&#xff09; RCC_APB2PeriphClockCmd(RCC_APB2…

使用Spring Cloud设计电商系统架构

在当今互联网高速发展的时代&#xff0c;电子商务系统成为了商家与用户互动的主要方式之一。为了能够更好地应对高并发、可扩展性、灵活性等需求&#xff0c;微服务架构逐渐成为设计电商系统的首选方案。Spring Cloud作为一个成熟的微服务框架&#xff0c;为开发人员提供了一整…

后端开发面试题4(附答案)

前言 在下首语言是golang,所以会用他作为示例。 原文参见 @arialdomartini的: Back-End Developer Interview Questions 语言相关问题 1. 告诉我你的首选语言的三个最坏的缺陷。 Golang虽然在很多方面表现出色,但它并非完美无缺,根据已有的讨论和批评,以下是Go语言被广泛…

深入浅出:面向对象软件设计原则(OOD)

目录 前言 1.单一责任原则&#xff08;SRP&#xff09; 2.开发封闭原则&#xff08;OCP&#xff09; 3.里氏替换原则&#xff08;LSP&#xff09; 4.依赖倒置原则&#xff08;DIP&#xff09; 5.接口分离原则&#xff08;ISP) 6.共同封闭原则&#xff08;CCP&#xff09…

Android替换默认的按键音

替换audio_assets.xml文件 此文件在AudioService.java被调用 <!--文件位置 /frameworks/base/core/res/res/xml/--> <audio_assets version"1.0"><group name"touch_sounds"><asset id"FX_KEY_CLICK" file"Effect…

用idea将java文件打成jar包

一、用idea将java文件打成jar包 1、在idea上选择file—Project Structure 2、Artifacts —点–JAR—From modules with dependencies 3、选择要打包的java文件 4、Build — Build Artifacts 5、找到刚才添加的Artifacts直接Build 6、生成jar包文件

【总结】项目中用过的设计模式

1.单例模式&#xff08;饿汉式 、懒汉式&#xff09; 应用场景&#xff1a;当频繁使用同一个对象&#xff0c;如配置信息、数据库连接池、线程池等时&#xff0c;使用单例模式可以避免重复创建对象&#xff0c;节省资源 线程池 2.工厂模式 应用场景&#xff1a;当需要创建的…

关于 UEditorPlus 和 TinyMCE 编辑器上线 HKCMS 插件市场的通知

亲爱的各位用户&#xff1a; 我们非常高兴地向大家宣布一个重要的消息&#xff01;UEditorPlus 和 TinyMCE 编辑器现已成功上线 HKCMS 插件市场&#xff01; UEditorPlus 以其强大的功能和便捷的操作&#xff0c;为用户提供了卓越的编辑体验。而 TinyMCE 编辑器也一直以其稳定…

C语言 | Leetcode C语言题解之第140题单词拆分II

题目&#xff1a; 题解&#xff1a; struct Trie {int ch[26];bool flag; } trie[10001];int size;void insert(char* s, int sSize) {int add 0;for (int i 0; i < sSize; i) {int x s[i] - a;if (trie[add].ch[x] 0) {trie[add].ch[x] size;memset(trie[size].ch, 0…

C语言 | Leetcode C语言题解之第139题单词拆分

题目&#xff1a; 题解&#xff1a; unsigned long long Hash(char* s, int l, int r) {unsigned long long value 0;for (int i l; i < r; i) {value value * 2333ull;value s[i] - a 1;}return value; } bool query(unsigned long long* rec, int len_rec, unsigned…

【代码随想录】【算法训练营】【第30天 1】 [322]重新安排行程 [51]N皇后

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 30&#xff0c;周四&#xff0c;好难&#xff0c;会不了一点~ 题目详情 [322] 重新安排行程 题目描述 322 重新安排行程 解题思路 前提&#xff1a;…… 思路&#xff1a;回溯。 重点&…