当前位置: 首页 > news >正文

部署一个自己的Spring Ai 服务(deepseek/通义千问)

Spring Boot 无缝接入 DeepSeek 和通义千问请求日志记录及其ip黑白名单
SpringBoot版本 3.2.0 JDK 版本为17 redis 3.2.0 mybatis 3.0.3

依赖引入

关键依赖

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

完整依赖

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.cqie</groupId><artifactId>spring-ai</artifactId><version>0.0.1-SNAPSHOT</version><name>spring-ai</name><description>spring-ai</description><properties><java.version>17</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>3.2.0</spring-boot.version><spring-ai.version>0.8.1</spring-ai.version></properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><!-- Spring Boot Web Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot Test Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- JUnit (for testing) --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>io.swagger</groupId><artifactId>swagger-annotations</artifactId><version>1.5.21</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.40</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.28</version></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.17</version></dependency><!-- 添加官方Spring AI OpenAI依赖 --><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.3</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><!-- Maven Compiler Plugin --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>${java.version}</source><target>${java.version}</target><encoding>${project.build.sourceEncoding}</encoding></configuration></plugin><!-- Spring Boot Maven Plugin --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring-boot.version}</version><configuration><mainClass>com.cqie.SpringAiApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build><repositories><!-- Spring Milestones Repository --><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository><!-- Spring Snapshots Repository --><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>false</enabled></releases></repository></repositories>
</project>

建表(日志+黑名单)

CREATE TABLE `request_log` (`id` varchar(100) NOT NULL COMMENT '主键',`date` datetime DEFAULT NULL COMMENT '请求时间',`request_url` varchar(255) DEFAULT NULL COMMENT '请求路径',`user_agent` varchar(255) DEFAULT NULL COMMENT 'userAgent',`status` int(11) DEFAULT NULL COMMENT '状态码',`ip_address` varchar(255) DEFAULT NULL COMMENT 'ip地址',`method` varchar(100) DEFAULT NULL COMMENT '方法',`error_message` varchar(255) DEFAULT NULL COMMENT '错误原因',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `black_ips` (`id` varchar(100) NOT NULL COMMENT '主键id',`black_ip` varchar(255) DEFAULT NULL COMMENT 'ip地址',`status` tinyint(1) DEFAULT NULL COMMENT '转态',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

配置文件


# DeepSeek 配置,完全兼容openai配置
# spring:
#   ai:
#     openai:
#       base-url: https://api.deepseek.com  # DeepSeek的OpenAI式端点
#       api-key: sk-xxxxxxxxx
#       chat.options:
#         model: deepseek-chat  # 指定DeepSeek的模型名称# 通义千问配置
spring:ai:openai:base-url: https://dashscope.aliyuncs.com/compatible-mode  # 通义千问api-key: sk-xxxxxxxxxxxchat.options:model: qwen-plus

配置文件示例

server:port: 8080
spring:application:name: spring-aiai:openai:base-url: https://dashscope.aliyuncs.com/compatible-modeapi-key: sk-***chat.options:model: qwen-plusdatasource:url: jdbc:mysql://ip:3306/springai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8username: 用户名password: 密码driver-class-name: com.mysql.cj.jdbc.Driver# Redis配置data:redis:host: ipport: 6379password: 密码database: 3lettuce:pool:max-active: 8max-wait: -1msmax-idle: 8min-idle: 0timeout: 10000ms
# 日志配置
logging:level:org.springframework.ai: DEBUG# mybatis配置
mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.cqie.entityconfiguration:map-underscore-to-camel-case: truecall-setters-on-nulls: truejdbc-type-for-null: 'null'

全局异常捕获

统一返回

package com.cqie.common;import lombok.AllArgsConstructor;
import lombok.Getter;@Getter
@AllArgsConstructor
public class Result {private int code;private String message;private Object data;public Result() {this.code = 0;this.message = "success";this.data = null;}public static Result success(Object data, String message) {Result result = new Result();result.code = 200;result.message = message;result.data = data;return result;}public static Result success(Object data) {Result result = new Result();result.code = 200;result.message = "success";result.data = data;return result;}public static Result error(String errorMsg) {Result result = new Result();result.code = 500;result.message = errorMsg;result.data = null;return result;}}

定义异常

package com.cqie.common;/*** 服务异常*/
public class ServerException extends RuntimeException {public ServerException(String message) {super(message);}public ServerException(String message, Throwable cause) {super(message, cause);}
}

全局捕获

package com.cqie.config;import com.cqie.common.CommonException;
import com.cqie.common.Result;
import com.cqie.common.ServerException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;/*** 全局异常处理器*/
@RestControllerAdvice
@Slf4j
public class GlobeExceptionHandler {// 处理全局异常@ExceptionHandler(CommonException.class)public Result CommonException(Exception e) {return Result.error(e.getMessage());}@ExceptionHandler(ServerException.class)public Result ServerException(Exception e) {return Result.error(e.getMessage());}}

基于interceptor的日志拦截器

package com.cqie.common;import com.cqie.dao.BlackIpsDao;
import com.cqie.dao.RequestLogDao;
import com.cqie.entity.BlackIps;
import com.cqie.entity.RequestLog;
import com.cqie.utils.RedisUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;/*** 请求检查,请求日志记录,黑名单处理*/
@Component
@ConditionalOnBean(RequestLogDao.class)
public class LoggingInterceptor implements HandlerInterceptor {private final RequestLogDao requestLogDao;private final BlackIpsDao blackIpsDao;private final RedisUtils redisUtils;private final String BLACK_IPS_KEY = "black_ips:";public LoggingInterceptor(RequestLogDao requestLogDao, BlackIpsDao blackIpsDao, RedisUtils redisUtils) {this.requestLogDao = requestLogDao;this.blackIpsDao = blackIpsDao;this.redisUtils = redisUtils;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String ipAddress = getClientIpAddress(request);int status = response.getStatus();String errorMessage = null;// 对黑名单的ip进行处理List<String> blackIps = (List<String>) redisUtils.get(BLACK_IPS_KEY + "spring-ai");if (blackIps == null) {List<String> ipBlackList = blackIpsDao.queryByStatus(0).stream().map(BlackIps::getBlackIp).collect(Collectors.toList());// 一天的过期时间redisUtils.set(BLACK_IPS_KEY + "spring-ai", ipBlackList, 24);}if (blackIps != null && blackIps.contains(ipAddress)) {status = 500;errorMessage = "请求ip已被加入黑名单";saveRequestLog(request, ipAddress, status, errorMessage);throw new ServerException(errorMessage);}// 判断2s请求最多请求一次,对请求频率做限制boolean exists = redisUtils.exists(ipAddress);if (exists) {status = 500;errorMessage = "ai服务请求太频繁";saveRequestLog(request, ipAddress, status, errorMessage);throw new ServerException(errorMessage);}// 记录调用日志saveRequestLog(request, ipAddress, status, null);// 对请求记录分析 限制2s请求最多请求一次redisUtils.set(ipAddress, "1", 5);return HandlerInterceptor.super.preHandle(request, response, handler);}private void saveRequestLog(HttpServletRequest request, String ipAddress, int status, String errorMessage) {LocalDateTime now = LocalDateTime.now();String method = request.getMethod();String url = request.getRequestURI();String userAgent = request.getHeader("User-Agent");RequestLog requestLog = new RequestLog();requestLog.setDate(now);requestLog.setRequestUrl(url);requestLog.setStatus(status);requestLog.setUserAgent(userAgent);requestLog.setIpAddress(ipAddress);requestLog.setMethod(method);requestLog.setErrorMessage(errorMessage);requestLogDao.insert(requestLog);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}private String getClientIpAddress(HttpServletRequest request) {String ipAddress = request.getHeader("X-Forwarded-For");if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("Proxy-Client-IP");}if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("WL-Proxy-Client-IP");}if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddr();}return ipAddress;}
}

注册拦截器

package com.cqie.config;import com.cqie.common.LoggingInterceptor;
import com.cqie.dao.BlackIpsDao;
import com.cqie.dao.RequestLogDao;
import com.cqie.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {private final RequestLogDao requestLogDao;private final BlackIpsDao blackIpsDao;private final RedisUtils redisUtils;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//日志拦截器registry.addInterceptor(new LoggingInterceptor(requestLogDao, blackIpsDao,redisUtils)).addPathPatterns("/**").order(0);}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {// 静态资源访问路径和存放路径配置registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/", "classpath:/public/");// 新增Camunda webjar资源映射registry.addResourceHandler("/webjars/camunda/**").addResourceLocations("classpath:/META-INF/resources/webjars/camunda-webapp-ui/");// swagger访问配置registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/", "classpath:/META-INF/resources/webjars/");}}

实现日志和日志表操作

dao server impl 这里只需要dao层就行

redis工具类简单封装

package com.cqie.utils;import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Component
public class RedisUtils {@Resourceprivate RedisTemplate<String, Object> redisTemplate;/*** 设置缓存** @param key   键* @param value 值*/public void set(String key, Object value) {redisTemplate.opsForValue().set(key, value);}/*** 设置缓存并设置过期时间** @param key     键* @param value   值* @param timeout 过期时间(秒)*/public void set(String key, Object value, long timeout) {redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.HOURS);}/*** 获取缓存** @param key 键* @return 值*/public Object get(String key) {return redisTemplate.opsForValue().get(key);}/*** 删除缓存** @param key 键*/public void delete(String key) {redisTemplate.delete(key);}/*** 判断key是否存在** @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {return Boolean.TRUE.equals(redisTemplate.hasKey(key));}/*** 设置过期时间** @param key     键* @param timeout 过期时间(秒)*/public void expire(String key, long timeout) {redisTemplate.expire(key, timeout, TimeUnit.SECONDS);}/*** 获取过期时间** @param key 键* @return 过期时间(秒)*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}public boolean exists(String key) {return redisTemplate.hasKey(key);}
} 

接口实现

package com.cqie.controller;import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.StreamingChatClient;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;import java.util.ArrayList;
import java.util.List;/*** 基于DeepSeek/通义千问的聊天控制器** @author qingyuqiao*/
@RestController
@RequestMapping("/api")
public class ChatController {/*** 上下文*/private final List<Message> contextHistoryList = new ArrayList<>();private final ChatClient chatClient;private final StreamingChatClient streamingChatClient;/*** ai 初始化信息** @param chatClient* @param streamingChatClient*/public ChatController(ChatClient chatClient, StreamingChatClient streamingChatClient) {this.chatClient = chatClient;this.streamingChatClient = streamingChatClient;// 对用户输入进行增强contextHistoryList.add(new SystemMessage("你是一个专业的it技术顾问。"));}/*** 普通对话** @param message 问题* @return 回答结果*/@GetMapping("/chat")public ChatResponse chat(@RequestParam String message) {contextHistoryList.add(new UserMessage(message));Prompt prompt = new Prompt(contextHistoryList);ChatResponse chatResp = chatClient.call(prompt);if (chatResp.getResult() != null) {contextHistoryList.add(chatResp.getResult().getOutput());}return chatResp;}/*** 流式返回** @param message 问题* @return 流式结果*/@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public Flux<String> streamChat(@RequestParam String message) {contextHistoryList.add(new UserMessage(message));Prompt prompt = new Prompt(contextHistoryList);return streamingChatClient.stream(prompt).map(chatResponse -> {if (chatResponse.getResult() != null) {return chatResponse.getResult().getOutput().getContent();}return "";});}
}

成功请求

配合黑名单

接口限制

后续可无缝接入deepseek,只需要修改配置文件的模型和密匙!!!

相关文章:

部署一个自己的Spring Ai 服务(deepseek/通义千问)

Spring Boot 无缝接入 DeepSeek 和通义千问请求日志记录及其ip黑白名单 SpringBoot版本 3.2.0 JDK 版本为17 redis 3.2.0 mybatis 3.0.3 依赖引入 关键依赖 <dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-sp…...

第一讲 | 算法复杂度

算法复杂度 一、数据结构前言1、数据结构&#xff08;DS&#xff09;2、算法&#xff08;Algorithm&#xff09; 二、算法效率1、复杂度的概念 三、时间复杂度&#xff08;1&#xff09;、案例&#xff08;2&#xff09;、大O的渐进表示法&#xff08;3&#xff09;、时间复杂度…...

【运维】还原 Docker 启动命令的利器:runlike 与 docker-autocompose

&#x1f50d; 还原 Docker 启动命令的利器&#xff1a;runlike 与 docker-autocompose 实用教程 在日常使用 Docker 时&#xff0c;我们常常通过 docker run 启动容器&#xff0c;但有时候过了一段时间就忘记了当初使用的具体参数&#xff08;端口、挂载、环境变量等&#xf…...

IP属地是实时位置还是自己设置

刷微博、抖音时&#xff0c;评论区总能看到“IP属地”&#xff1f;这个突然冒出来的小标签&#xff0c;让不少网友摸不着头脑&#xff1a;‌IP属地是实时位置&#xff0c;还是可以自己设置&#xff1f;‌别急&#xff0c;今天咱们就来聊聊这个话题&#xff01; 1、什么是IP属地…...

Android WIFI体系

先说说WifiLock、MulticastLock 、IWificond WifiLock 允许应用强制保持 WiFi 活跃&#xff0c;即便设备处于休眠状态。如WIFI_MODE_FULL_HIGH_PERF&#xff1a;保持高性能 WiFi 活跃状态&#xff0c;适用于高带宽需求&#xff0c;如视频通话、流媒体。经测试有的场景能减少10…...

什么是静态住宅ip,跨境电商为什么要用静态住宅ip

在数字时代&#xff0c;IP地址不仅是设备联网的“ID”&#xff0c;更是跨境电商运营中的关键工具。尤其对于需要长期稳定、安全操作的场景&#xff0c;静态住宅IP逐渐成为行业首选。 一、什么是静态住宅IP&#xff1f; 静态住宅IP&#xff08;Static Residential IP&#xff0…...

常见位运算总结

目录 常见位运算总结 191:位1的个数 338&#xff1a;比特位计数 461:汉明距离 136&#xff1a;只出现一次的数字 260&#xff1a;只出现一次的数字III 常见位运算总结 191:位1的个数 链接&#xff1a;191. 位1的个数 - 力扣&#xff08;LeetCode&#xff09; class Sol…...

[密码学实战]SDF之对称运算类函数(四)

[密码学实战]SDF之对称运算类函数(四) 一、标准解读:GM/T 0018-2023核心要求 1.1 SDF接口定位 安全边界:硬件密码设备与应用系统间的标准交互层 功能范畴: #mermaid-svg-1jptduZFNFiRZ2lS {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16…...

【C++编程入门】:基本语法

上一篇提到了C关键字和缺省参数以及命名空间域&#xff0c;这篇继续分享C入门语法&#xff0c;把基本语法掌握扎实后面学习更才能更轻松一些。 目录 引用 引用的特性 常引用 内联函数 auto关键字 引用 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&am…...

区块链最佳框架:Truffle vs Hardhat vs Brownie

区块链技术的快速发展使得智能合约开发成为主流&#xff0c;而选择合适的开发框架是提升效率的关键。目前&#xff0c;Truffle、Hardhat和Brownie是三大主流框架&#xff0c;它们各有特点&#xff0c;适用于不同的开发场景和开发者偏好。本文将从功能、生态系统、适用人群等角度…...

Apache Flink的架构设计与运行流程说明

在大数据领域&#xff0c;实时计算的重要性随着业务需求的爆发式增长愈发凸显。从电商的实时销量监控到金融的高频交易风控&#xff0c;从物联网设备的实时告警到社交平台的热点追踪&#xff0c;企业对“秒级甚至毫秒级”数据处理能力的需求已成为刚需。在众多实时计算框架中&a…...

AI+零售:智能推荐、无人店与供应链管理的未来

AI零售&#xff1a;智能推荐、无人店与供应链管理的未来 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 AI零售&#xff1a;智能推荐、无人店与供应链管理的未来摘要引言一、智能推荐系统&#xff1a;从流量收割到用…...

华为云IoT平台与MicroPython实战:从MQTT协议到物联网设备开发

目录 前言 1. 华为云 1.1. 创建实例 1.2. 创建产品 1.3. 编辑服务模型 1.4. 注册设备 1.4.1. 复制设备连接参数 1.5. 连接参考代码 2. micropython版-物联网 2.1. 环境搭建 2.2. 实现步骤 2.3. 示例代码 结语 前言 物联网&#xff08;IoT&#xff09;技术的快速发…...

【Linux】Linux内核模块开发

Linux内核模块开发 零、关于 1、概述 最近在学习Linux相关的东西&#xff0c;学习了U-Boot的编译&#xff0c;Linux的编译&#xff0c;能够在开发板上运行自己编译的U-Boot和Linux了&#xff0c;那么接下来就是在自己编译的Linux上做应用级或者系统级的开发了。本文以字符设…...

linux 下查看指定进程的内存CPU占用情况(用于程序崩溃类的排查)

在程序开发过程中&#xff0c;如果程序较为庞大&#xff0c;逻辑较为复杂时&#xff0c;容易出现运行时崩溃的问题。导致的原因有很多&#xff0c;我这里只对较为通用的内容占用情况作记录&#xff0c;如程序中对文件描述符打开未关闭&#xff08;导致fd积攒过多超过了系统的标…...

ASP.NET MVC​ 入门指南五

26. 响应式设计与移动开发 26.1 响应式视图设计 为了使 MVC 应用程序在不同设备上都能提供良好的用户体验&#xff0c;需要采用响应式设计。可以使用 CSS 框架如 Bootstrap 来实现响应式布局。 引入 Bootstrap&#xff1a;在项目中引入 Bootstrap 的 CSS 和 JavaScript 文件。…...

字节跳动社招面经 —— BSP驱动工程师(4)

接前一篇文章&#xff1a;字节跳动社招面经 —— BSP驱动工程师&#xff08;3&#xff09; 本文内容参考&#xff1a; 嵌入式硬件平台修改启动地址-CSDN博客 特此致谢&#xff01; 上一回开始针对于“嵌入式充电站”发的一篇文章字节跳动社招面经——BSP驱动工程师中的面试题…...

Spring MVC中自定义日期类型格式转换器

在Spring MVC中&#xff0c;自定义日期类型格式转换器可以通过实现Converter接口或使用DateTimeFormat注解。以下是两种方法的详细说明&#xff1a; 方法一&#xff1a;全局自定义转换器&#xff08;推荐&#xff09; 1. 创建日期转换器类 实现 org.springframework.core.con…...

【3D 地图】无人机测绘制作 3D 地图流程 ( 无人机采集数据 | 地图原始数据处理原理 | 数据处理软件 | 无人机测绘完整解决方案 )

文章目录 一、无人机采集数据1、多角度影像数据2、定位与姿态数据 二、无人机采集数据处理原理1、空三解算2、密集点云生成与三维重建3、地形与正射影像生成4、三维模型优化与瓦片化 三、无人机影像处理软件介绍 一、无人机采集数据 无人机原始数据采集 : 多角度影像数据 : 多…...

arduino Nano介绍

【仅供学习&#xff0c;具体参数参考官网或销售商】 Arduino Nano 是一款基于 ATmega328P 微控制器&#xff08;或 ATmega168 旧版&#xff09;的紧凑型开发板&#xff0c;专为嵌入式项目和原型设计而设计。 以下是Arduino Nano V3.0 328P详细介绍&#xff1a; 主要特性 微…...

解决 Flutter 在 iOS 真机上构建失败的问题

在开发 Flutter 应用时&#xff0c;有时会在尝试将应用部署到 iOS 真机时遇到构建失败的问题。错误信息通常类似于以下内容&#xff1a; Could not build the precompiled application for the device. Uncategorized (Xcode): Timed out waiting for all destinations matchi…...

【办公类-89-03】20250429AI写的研讨记录,清除格式,统一格式,名字替换。部分加粗,添加页眉

背景需求: 检查自即,需要AI一下院内的五次科研培训记录。 本次用了豆包 豆包写的不错,也是“水字数”的高手 把每次培训内容贴到WORD里 把AI资料贴到WORD里,发现问题: 1、字体、段落什么都是不统一的,需要统一改成宋体小四,1.5倍行距 2、十个研讨人也要改成真人。就找…...

react-native 安卓APK打包流程

一、使用keytool命令生成一个签名密钥 $ keytool -genkeypair -v -storetype PKCS12 -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000 在 Windows 上keytool命令放在 JDK 的 bin 目录中&#xff08;比如C:\Program Files\…...

Android Studio中OpenCV应用详解:图像处理、颜色对比与OCR识别

文章目录 一、OpenCV在Android中的集成与配置1.1 OpenCV简介1.2 在Android Studio中集成OpenCV1.2.1 通过Gradle依赖集成1.2.2 通过模块方式集成1.2.3 初始化OpenCV 1.3 OpenCV基础类介绍 二、指定区域图像抓取与对比2.1 图像抓取基础2.2 指定区域图像抓取实现2.2.1 从Bitmap中…...

企业办公协同平台安全一体化生态入住技术架构与接口标准分析报告

全球组织数字化与智能化背景下 企业办公协同平台安全一体化生态入住技术架构与接口标准分析报告 一、背景与市场需求 市场规模与增量 根据Statista数据&#xff0c;全球协同办公平台市场规模预计从2023年的$480亿增长至2027年的$900亿&#xff0c;年复合增长率&#xff08;CAG…...

从零搭建体育比分网站:技术选型与API调用实战(附完整源码)

一、前言&#xff1a;为什么选择体育比分项目&#xff1f; 体育数据网站是练手全栈开发的绝佳项目&#xff0c;涉及&#xff1a; ✅ 前端&#xff08;实时数据渲染、可视化图表&#xff09; ✅ 后端&#xff08;API对接、数据缓存、高并发优化&#xff09; ✅ 数据库&#xff…...

非凸科技受邀出席AI SPARK活动,共探生成式AI驱动金融新生态

4月19日&#xff0c;由AI SPARK社区主办的“生成式AI创新与应用构建”主题沙龙在北京举行。活动聚焦生成式AI的技术突破与产业融合&#xff0c;围绕大模型优化、多模态应用、存内计算等前沿议题展开深度探讨。非凸科技受邀出席并发表主题演讲&#xff0c;深入解析金融垂直大模型…...

深入蜂窝物联网 第五章 EC-GSM-IoT 及其他技术:混合组网与前瞻

1. 前言与应用场景 在一些地区,GSM 网络仍然大面积覆盖且运营成本低廉,运营商可通过 EC-GSM-IoT(Extended Coverage GSM for IoT)在现有GSM基站上升级,实现物联网互联。同时,为了满足不同场景的需求,常常需要与 NB-IoT、LTE-M、5G RedCap 等技术混合组网,形成多层次、…...

2025年深圳软件开发公司推荐

随着移动互联网的深度发展&#xff0c;软件开发已成为企业实现数字化转型的重要途径。作为中国科技创新中心的深圳&#xff0c;汇聚了众多技术实力雄厚的软件开发企业。本文将为您精选推荐6家在深圳表现突出的软件开发服务商&#xff0c;帮助企业找到合适的数字化转型合作伙伴。…...

仿腾讯会议——注册登录UI

1、加载素材 2、新添加资源类 3、加载图片 4、添加左侧图片 在左侧添加一个标签 选择图片 选择图片 勾选保证图片不变形 5、修改组件名称 6、设置密码输入框 5、切换 6、编辑提示框 7、定义提交和清空的槽函数 8、设置页面标题和最先显示页面 9、清空登录信息函数实现 10、清空…...

香港科技大学广州|可持续能源与环境学域博士招生宣讲会—四川大学专场

香港科技大学广州&#xff5c;可持续能源与环境学域博士招生宣讲会—四川大学专场 时间&#xff1a;2025年5月8日&#xff08;星期四&#xff09;16:30开始 地点&#xff1a;四川大学基础教学楼A座504 宣讲嘉宾&#xff1a;肖殿勋 助理教授 一经录取&#xff0c;享全额奖学金…...

设计模式(工厂模式)

工厂设计模式&#xff1a;打造你的代码生产线 引言 想象一下&#xff0c;你正站在一家现代化的玩具工厂门前。工厂内部&#xff0c;各种机器有条不紊地运转&#xff0c;原材料在传送带上流动&#xff0c;最终变成精美的玩具。你不需要了解每个玩具的具体制作工艺&#xff0c;…...

本地大模型编程实战(29)查询图数据库NEO4J(2)

上一篇文章 用大语言模型LLM查询图数据库NEO4J(1) 介绍了使用GraphQACypherChain查询NEO4J。用它实现简单快捷&#xff0c;但是不容易定制&#xff0c;在生产环境中可能会面临挑战。 本文将基于langgraph 框架&#xff0c;用LLM(大语言模型)查询图数据库NEO4J。它可以定义清晰复…...

Python爬虫(10)Python数据存储实战:基于pymongo的MongoDB开发深度指南

目录 一、为什么需要文档型数据库&#xff1f;1.1 数据存储的范式变革1.2 pymongo的核心优势 二、pymongo核心操作全解析2.1 环境准备2.2 数据库连接与CRUD操作2.3 聚合管道实战2.4 分批次插入百万级数据&#xff08;进阶&#xff09;2.5 分批次插入百万级数据&#xff08;进阶…...

从遍历序列构造二叉树:前序+中序与中序+后序的递归解法详解

文章目录 1. 问题背景2. 核心思路3. 从前序与中序遍历序列构造二叉树3.1 递归分治思路3.2 代码实现与注释 4. 从中序与后序遍历序列构造二叉树4.1 递归分治思路4.2 代码实现与注释 5. 复杂度分析6. 总结 1. 问题背景 二叉树的遍历方式包括前序&#xff08;根-左-右&#xff09…...

数据挖掘专栏介绍:用 Python + 大语言模型 (LLM) 重塑电商数据价值

写在前面 —— 不止于挖掘,更要智能涌现:用 Python + 大语言模型 (LLM) 重塑电商数据价值 或许你已经跟随我们之前的 “零基础上手Python数据分析” 专栏,掌握了 Pandas 的数据操纵、Matplotlib/Seaborn 的可视化呈现,甚至对传统的数据挖掘技术如聚类、分类、回归有了初步…...

CSS Transition入门指南

CSS Transition 完全指南 目录 Transition 的作用核心属性代码示例使用场景性能优化常见问题思维导图 1. Transition 的作用 CSS Transition 用于在 属性值变化时 创建平滑的过渡效果。例如&#xff1a; 鼠标悬停时按钮放大元素颜色渐变切换位置移动的缓动效果 2. 核心属性…...

Nginx 核心功能

目录 一、基于授权的访问控制 &#xff08;1&#xff09;使用htpasswd 生成用户认证文件 &#xff08;2&#xff09;修改密码文件权限为400&#xff0c;将所有者改为nginx &#xff0c;设置 Nginx 的运行用户能够读取 &#xff08;3&#xff09;修改主配置文件 nginx.conf&…...

排序版研究方向

姓 名研究方向电子邮箱办公电话办公地点曹培根代数cao2024ustc.edu.cn 新楼412陈洪佳代数hjchenustc.edu.cn0551-636076931529陈小伍代数xwchenmail.ustc.edu.cn0551-636062351321梁永祺代数yqliangustc.edu.cn0551-636006171613欧阳毅代数yiouyangustc.edu.cn0551-63600337…...

AI国学智慧语录视频,条条视频10W+播放量

家人们&#xff01;图书类带货玩法真的非常多&#xff0c;之前也分享过蛮多&#xff0c;例如情感语录、育儿教育、爆款图书金句类、AI历史人物解说类等等。 本期继续来分享一个对于普通人来说&#xff0c;上手相当简单&#xff0c;容易起号&#xff0c;可作为长线深耕的AI带货…...

RN 获取视频封面,获取视频第一帧

严格的说&#xff0c;没有解决这个问题&#xff0c;实际上是绕过了这个问题&#xff0c;严格的说获取的也不是第一帧。 RN的video 视频应用&#xff0c;大多是这样的 1、安装 yarn add react-native-video 2、导入 import Video from react-native-video; 3、使用 …...

缓存分片哈希 vs 一致性哈希:优缺点、区别对比及适用场景(图示版)

&#x1f4dc; 引言 在分布式缓存系统中&#xff0c;数据分布策略是设计的关键之一。缓存分片哈希和一致性哈希是两种常见的数据分布算法&#xff0c;它们各有优缺点和适用场景。本文将通过图示对比表格&#xff0c;深入解析这两种算法的核心原理、优缺点及适用场景。 &#x…...

iOS - 音频: Core Audio - 播放

环境 iOS 18 Xcode 16.3 swift-driver version: 1.120.5 Apple Swift version 6.1 (swiftlang-6.1.0.110.21 clang-1700.0.13.3) Target: x86_64-apple-macosx15.0 Core Audio 的架构 声音数据的描述 sample: 一个声道采样的值,采样率定义了每秒从连续信号中提取并组成离散信号…...

Nerfstudio 环境配置与自有数据集(图片和视频)测试全方位全流程实战【2025最新版!!】

一、引言 神经辐射场(Neural Radiance Fields&#xff0c;简称NeRF)是近年来计算机视觉和图形学领域的一项革命性技术&#xff0c;它能够从2D图像中学习复杂的3D场景表示。然而&#xff0c;NeRF技术的实现和应用门槛较高&#xff0c;需要较为专业的计算机视觉和深度学习知识。…...

【Java学习】动态代理有哪些形式?

Java动态代理的两种主要形式 动态代理在Java中有两种主要的实现方式&#xff0c;它们各有特点和使用场景&#xff1a; 1. JDK动态代理 (基于接口) 特点&#xff1a; Java标准库自带的功能&#xff08;java.lang.reflect.Proxy&#xff09;只能代理接口&#xff0c;不能代理…...

Android Studio 中实现方法和参数显示一行

Android Studio 中实现方法和参数显示一行&#xff0c;可通过以下步骤配置&#xff1a; 一、基础格式化设置 ‌快捷键格式化‌ 选中代码后使用 Ctrl Alt L&#xff08;Windows/Linux&#xff09;或 Cmd Option L&#xff08;Mac&#xff09;进行快速格式化27。 ‌菜单操作…...

SQLyog中DELIMITER执行存储过程时出现的前置缩进问题

在SQLyog中执行存储过程时出现的前置缩进问题&#xff0c;实际上反映了SQLyog对SQL语句解析的一个特殊行为。以下是详细解释和解决方案&#xff1a; 问题根源 SQLyog的语句分隔逻辑&#xff1a; SQLyog默认会根据分号(;)和换行自动分隔SQL语句 当代码有缩进时&#xff0c;SQLy…...

基于Spring Boot 3.0、ShardingSphere、PostgreSQL或达梦数据库的分库分表

要实现基于Spring Boot 3.0、ShardingSphere、PostgreSQL或达梦数据库的分库分表&#xff0c;首先需要对ShardingSphere进行一些基本配置。你提到的溯源码、批次号等数据需要考虑到跨年数据的存储&#xff0c;因此要设计一个能够动态扩展的分表策略 添加ShardingSphere依赖 在…...

vscode chrome调试怎么在所有浏览器都好使

chrome调试时只能在打开的浏览器里进行调试&#xff0c;其它打开的chrome浏览器就不能调试了&#xff0c;怎么解决。 右键点击 Chrome 的快捷方式图标&#xff0c;选择属性 在目标一栏&#xff0c;最后加上--remote-debugging-port9222 注意要用空格隔开 lanch.json 文件配置 …...

20250429在Ubuntu 20.04.6下安装VMware Workstation16

20250429在Ubuntu 20.04.6下安装VMware Workstation16 2025/4/29 20:16 缘起&#xff1a;1、在ubuntu14.04下git clone异常该如何处理呢&#xff1f; 2、请问 现在 编译NanoPi NEO的FriendlyCore系统使用ubuntu哪一个版本比较好&#xff1f; ubuntu14.04 编译异常/下载不了&am…...