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

news2024/11/8 19:40:48

一、需求

公司想要在页面中加入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:[
            ],
            // 逐字输出 START
            timer: 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,默认是1
        msg.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.coloradmin.cn/o/1819129.html

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

相关文章

从0到1搭建MCU芯片上操作系统环境。开发都需要哪些环节和准备

MCU芯片环境搭建与操作系统上载步骤 1. 硬件准备 选择合适的MCU芯片&#xff0c;例如STM32、GD32等。 准备开发板&#xff0c;用于硬件连接和实验。 准备必要的外围设备&#xff0c;如电源适配器、USB转串口模块等。 2. 软件环境搭建 安装编程语言环境&#xff0c;如C/C编译…

2024年中漫谈

不知不觉&#xff0c;2024年已来到了6月&#xff0c;博主不禁感叹时光易逝&#xff0c;岁月的车轮滚滚向前&#xff0c;永不止步&#xff0c;此刻无关贫穷与富裕&#xff0c;伟大与平凡。 于是乎&#xff0c;宇宙&#xff08;时空&#xff09;看似毫无终点&#xff0c;一望无垠…

for循环结构

循环&#xff1a; 循环是一个重复执行一个代码的结构。只要满足循环的条件&#xff0c;会一直执行这个代码。 循环条件&#xff1a;在一定范围之内&#xff0c;按照指定的次数来执行循环。 循环体&#xff1a;在指定的次数内&#xff0c;执行的命令序列。只要条件满足&#…

【深度优先搜索 广度优先搜索】297. 二叉树的序列化与反序列化

本文涉及知识点 深度优先搜索 广度优先搜索 深度优先搜索汇总 图论知识汇总 LeetCode297. 二叉树的序列化与反序列化 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传…

GUI初步开始(matlab)

GUI初步开始&#xff08;matlab&#xff09; &#xff08;自用笔记&#xff09; 打工人艰辛速成&#xff0c;花几个小时从零到能用&#xff0c;记录下details and problems&#xff1a; 甲方要求&#xff1a;GUI界面&#xff0c;读下位机&#xff0c;找到解码后格式中所需要的…

GGML简单介绍

GGML是一个用于机器学习的张量库&#xff0c;可以在商用硬件上实现大型模型和高性能。它被llama.cpp和whisper.cpp使用 C语言编写 16位浮点支撑 整数量化支持(如4位、5位、8位) 自动分化 内置优化算法(如ADAM, L-BFGS) 针对苹果芯片进行优化 在x86架构上利用AVX / AVX2的内在特…

人工智能:项目管理的新视角与未来影响

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;已经从科幻小说中的概念变为现实生活中的强大工具。作为一名工作多年的项目管理人员&#xff0c;我深感AI在项目管理领域中的潜力和影响。在这篇文章中&#xff0c;我将从项目管理人员的角度&#xff0c;探讨…

晶圆代工市占洗牌,中芯跃居第三名 | 百能云芯

市场研究机构集邦咨询&#xff08;TrendForce&#xff09;最新发布的调查显示&#xff0c;今年第1季前五大晶圆代工厂第1季排名出现明显变动&#xff0c;除了台积电&#xff08;TSMC&#xff09;继续蝉联第一名&#xff0c;中芯国际&#xff08;SMIC&#xff09;受惠消费性库存…

视频媒介VS文字媒介

看到一篇蛮有思考意义的文章就摘录下来了&#xff0c;也引起了反思 目录 一、视频的定义 二、”视频媒介“与”文字媒介”作对比 1.形象 VS 抽象 2.被动 VS 主动 三、视频的缺点-【更少】的思考 1.看视频为啥会导致【更少的思考】 2.内容的【浅薄化】 3.内容的【娱乐化…

一文讲清:bom管理系统是什么?在生产管理中有什么作用?

在制造业中&#xff0c;物料清单&#xff08;Bill of Materials&#xff0c;简称BOM&#xff09;扮演着至关重要的角色。物料清单&#xff08;BOM&#xff09;是制造或维修产品所需的材料、组件和零件的结构化综合列表&#xff0c;以及所需材料的数量、名称、描述和成本。简而言…

【代码随想录】【算法训练营】【第36天】[452]用最少数量的箭引爆气球 [435]无重叠区间 [763]划分字母区间

前言 思路及算法思维&#xff0c;指路 代码随想录。 题目来自 LeetCode。 day 36&#xff0c;周三&#xff0c;最难坚持的一天~ 题目详情 [452] 用最少数量的箭引爆气球 题目描述 452 用最少数量的箭引爆气球 解题思路 前提&#xff1a;区间可能重叠 思路&#xff1a;…

对接钉钉Stream模式考勤打卡相关事件的指南

钉钉之前的accessToken是公司级别的&#xff0c;现在的accessToken是基于应用的&#xff0c;接口的权限也是基于应用的。所以第一步是在钉钉开放平台&#xff08;https://open-dev.dingtalk.com/&#xff09;创建一个应用。 创建好应用之后&#xff0c;因为我们后续还需要调用钉…

分布式事务seata之AT与TCC模型

1. seata分布式事务简介 seata是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。 Seata提供了AT、TCC、SAGA和XA事务模型&#xff0c;为用户打造一站式的分布式解决方案。 简单来说&#xff0c;Seata就是针对主流事务解决方案的封装…

Unity:Text-TextMeshPro 不显示中文

共计四步&#xff1a; 一、去C盘复制一份字体&#xff1a; C:\Windows\Fonts二、粘贴到你的项目里&#xff08;任意文件位置&#xff09;&#xff0c;得到“MSYH”&#xff1a; 三、右键字体文件&#xff0c;依次点击create–>TextMeshPro–>FontAsset&#xff1a; …

visdom使用时所遇的问题及解决方法

最近在用visdom进行可视化的过程中&#xff0c;虽然可有效的避免主机拒绝访问&#xff08;该问题的解决方法&#xff0c;请参考深度学习可视化工具visdom使用-CSDN博客&#xff09;即在终端输入python -m visom.server 1.训练过程中visdom出现ValueError: too many file descr…

科技项目验收测试必须进行吗?软件测试公司推荐

科技项目验收测试是指在科技项目开发周期中&#xff0c;对项目完成后进行的一种测试和评估工作。它的目的是验证项目是否达到预期的要求&#xff0c;并确保项目交付给客户前达到预期的质量标准。 一、科技项目验收测试的必要性   科技项目验收测试是项目管理中不可或缺的一个…

5.5 Python 迭代器与生成器

文章目录 1. 三元表达式1.1 格式1.2 示例1.3 嵌套 2. 生成式2.1 列表生成式2.2 字典生成式2.3 集合生成式2.4 元组生成式 3. 可迭代对象4. 迭代器4.1 迭代器的优缺点4.2 迭代器的惰性机制4.3 生成迭代器4.4 文本IO包装器4.5 字符串迭代器4.6 列表迭代器4.7 字典键迭代器4.8 元组…

怎么改图片尺寸更方便?在线图片改大小的使用方法

图片怎么快速改尺寸呢&#xff1f;在网上传图或者做其他用途时&#xff0c;经常会对图片的尺寸有要求&#xff0c;当拍摄或者制作的图片太大或者太小时&#xff0c;都会导致图片的无法正常使用&#xff0c;那么就需要按照规定将图片改大小之后才能正常使用。 在遇到图片修改大…

ui自动化中,隐式等待和显示等待什么时候使用

隐式等待 在页面刷新加载时&#xff0c;页面元素还没有出来&#xff0c;这个时候如果去找元素就会找不到报错 或者点了一个菜单&#xff0c;页面加载时 用笨办法&#xff0c;就是用sleep等待固定的时间&#xff0c;这种浪费的时间比较多&#xff0c;就可以用隐式等待&#xf…

酷暑骄阳,热情似火丨deepin校园联盟走进湖北大学,共话开源新篇章

内容来源&#xff1a;deepin&#xff08;深度&#xff09;社区 炎炎夏日&#xff0c;骄阳似火&#xff0c;6月11日&#xff0c;deepin校园联盟湖北大学站交流活动如期举行。在湖北大学计算机与信息工程学院&#xff0c;deepin(深度)社区研发工程师王溢学为热情似火的学子们开展…