微服务项目->在线oj系统(Java版 - 4)
相信自己,终会成功
目录
B端用户管理
C端用户代码
发送验证码:
验证验证码
退出登录
登录用户信息功能
用户详情与用户编辑
用户竞赛接口
用户报名竞赛
用户竞赛报名接口查询
用户信息列表
ThreadLocalUtil
Hutool工具库
常用功能介绍
B端用户管理
进行列表显示与用户状态修改(拉黑操作)
用户列表显示主要运用了分页管理给前端传输数据(UserVO)从而在页面上显示
拉黑操作,根据前端传送过来的数据,在数据库中根据用户id搜索此用户,失败返回异常
成功执行user.setStatus(userDTO.getStatus());
作用是把 userDTO
里的状态值赋给 user
对象
userCacheManager.updateStatus(user.getUserId(), userDTO.getStatus());
则是让缓存中的用户状态与新状态保持一致,执行 updateStatus 函数,先刷新用户缓存,
@Overridepublic List<UserVO> list(UserQueryDTO userQueryDTO) {PageHelper.startPage(userQueryDTO.getPageNum(),userQueryDTO.getPageSize());return userMapper.selectUserList(userQueryDTO);}@Overridepublic int updateStatus(UserDTO userDTO) {User user = userMapper.selectById(userDTO.getUserId());if(user==null){throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);}//这里是从前端拿到的值,假设前端把用户拉黑,返回的就是0,设置也就是0user.setStatus(userDTO.getStatus());userCacheManager.updateStatus(user.getUserId(),userDTO.getStatus());return userMapper.updateById(user);}
public void updateStatus(Long userId,Integer status) {//刷新用户缓存String userKey = getUserKey(userId);User user = redisService.getCacheObject(userKey, User.class);if(user==null){return;}user.setStatus(status);redisService.setCacheObject(userKey, user);//设置用户缓存有效期为10分钟redisService.expire(userKey, CacheConstants.USER_EXP, TimeUnit.MINUTES);}
getCacheObiect中从Redis缓存中获取对象并转换为指定类型
从redis缓存中获取指定key的对象,并将其转换为指定类型的示例
/*** 获得缓存的基本对象。** @param key 缓存键值* @return 缓存键值对应的数据*/public <T> T getCacheObject(final String key, Class<T> clazz) {// 获取 Redis 的 Value 操作对象ValueOperations<String, T> operation = redisTemplate.opsForValue();// 从缓存中获取对象T t = operation.get(key); // 如果缓存值是 String 类型且目标类型也是 String,则直接返回if (t instanceof String) {return t;}// 否则尝试将对象转换为 JSON 字符串并解析为目标类型return JSON.parseObject(String.valueOf(t), clazz);}
C端用户代码
发送验证码:
从前端拿到数据,获取到用户手机号码,
进入validatePhone(phone);用于判断phone是否为空(StrUtil.isBlank(phone),isStrUtil.isBlank是Hutool工具类下的方法Hutool工具类下方有介绍),为空执行异常,不为空判断手机号格式是否正确,如果正确,则正常进行频率检查
checkDailyRequestLimit()检查每日请求限制
生成Redis存储的key(格式示例:c:t:13800138000),从redis获取已发送次数(String类型),转化发送次数为Long类型(若为空则默认为0),给sendTimes赋值,与每日限制次数(sendLimit)进行对比,结果为true则抛出异常
@Overridepublic boolean sendCode(UserDTO userDTO) {// 1. 参数校验String phone = userDTO.getPhone();validatePhone(phone);// 2. 频率控制检查checkRequestFrequency(phone);// 3. 每日限额检查checkDailyRequestLimit(phone);// 4. 生成并存储验证码String code = generateVerificationCode(phone);// 5. 发送验证码sendVerificationCode(phone, code);System.out.println("code: "+code);return true;}
private void validatePhone(String phone) {if (StrUtil.isBlank(phone)) {throw new ServiceException(ResultCode.FAILED_USER_PHONE_EMPTY);}// 中国手机号正则(11位,1开头)String regex = "^1[3-9]\\d{9}$";if (!Pattern.matches(regex, phone)) {throw new ServiceException(ResultCode.FAILED_USER_PHONE_INVALID);}}private void checkRequestFrequency(String phone) {// getExpire()获取当前 Key 的剩余生存时间String phoneCodeKey = getPhoneCodeKey(phone);Long expire = redisService.getExpire(phoneCodeKey, TimeUnit.SECONDS);// 如果上一次验证码的发送时间距离现在 不足1分钟(即 总有效期 - 剩余时间 < 60秒),则拒绝新请求。if (expire != null && (emailCodeExpiration * 60 - expire) < 60) {throw new ServiceException(ResultCode.FAILED_FREQUENT);}}private void checkDailyRequestLimit(String phone) {// 每天的验证码获取次数有一个限制 50 次 , 第二天 计数清0 重新开始计数// 计数 怎么存,存在哪里 ?// 操作次数数据频繁,不需要存储, 记录的次数 有效时间(当天有效) redis String key: c:t:// 1.获取已经请求的次数 和 50 进行比较String codeTimeKey = getCodeTimeKey(phone);String sendTimesStr = redisService.getCacheObject(codeTimeKey, String.class);Long sendTimes = sendTimesStr != null ? Long.parseLong(sendTimesStr) : 0L;// 如果大于限制,抛出异常// 如果不大于限制,正常执行后续逻辑,并且将获取计数+1if (sendTimes >= sendLimit) {throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);}}
验证验证码
对比验证码是否正确,如果正确,根据输入的phone去数据库中查询
如果user为null则在数据库中添加一个新用户,添加手机号,用户状态
最后生成专属令牌,用于后续验证
@Overridepublic String codeLogin(String phone, String code) {//判断验证码是否正确CheckCode(phone,code);User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));if(user==null){ //新用户//注册逻辑 1.先完成验证码的比对->成功,往系统中增加用户user=new User();user.setPhone(phone);user.setStatus(UserStatus.Normal.getValue());userMapper.insert(user);}return tokenService.createToken(user.getUserId(),secret, UserIdentity.ORDINARY.getValue(),user.getNickName(),user.getHeadImage());private void CheckCode(String phone, String code) {String phoneCodeKey = getPhoneCodeKey(phone);String cacheCode = redisService.getCacheObject(phoneCodeKey,String.class);if(StrUtil.isEmpty(cacheCode)){throw new ServiceException(ResultCode.FAILED_INVALID_CODE);}if(!cacheCode.equals(code)){throw new ServiceException(ResultCode.FAILED_CODE_MISMATCH);}redisService.deleteObject(phoneCodeKey);}
退出登录
最开始检测token是否为空这步是一个保障,防止没有生成令牌
public static final String PREFIX = "Bearer ";为令牌前缀,如果存在令牌前缀则把前缀替换成空
最后删除令牌
@Overridepublic boolean logout(String token) {//检查token是否为空if (StrUtil.isEmpty(token)) {throw new ServiceException(ResultCode.TOKEN_IS_DEFAULT);}//再处理前缀if (token.startsWith(HttpConstants.PREFIX)) {token = token.replaceFirst(HttpConstants.PREFIX,StrUtil.EMPTY);}return tokenService.deleteLoginUser(token,secret);}
登录用户信息功能
LoginUserVO,返回给前端的数据(包括头像和昵称)
根据token获取登录用户信息返回,包含用户基本信息的VO对象
用户头像字段需要拼接下载地址前缀,使用StrUtil.isNotEmpty确保头像字段非空
@Overridepublic R<LoginUserVO> info(String token) {// if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
// token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
// }// 先检查token是否为空if (StrUtil.isEmpty(token)) {throw new ServiceException(ResultCode.TOKEN_IS_DEFAULT);}
// 再处理前缀if (token.startsWith(HttpConstants.PREFIX)) {token = token.substring(HttpConstants.PREFIX.length());}LoginUser loginUser = tokenService.getLoginUser(token, secret);if(loginUser==null){return R.fail();}LoginUserVO loginUserVO=new LoginUserVO();loginUserVO.setNickName(loginUser.getNickName());if(StrUtil.isNotEmpty(loginUser.getHeadImage())){loginUserVO.setHeadImage(download+loginUser.getHeadImage());}return R.ok(loginUserVO);}
用户详情与用户编辑
用户详情
从线程池中获取userId的值
定义一个userVO对象,从数据库中根据userId查询内容赋值给userVO
如果用户存在,设置头像,存入userVO,最后返回给前端
用户编辑
先把用户获取出来,根据前端输入的内容去跟新数据库中的值
最后更新缓存
extracted(),刷新用户缓存和登录状态信息
UserCacheManager .refreshUser: 用户缓存刷新接口
TokenService.refreshLoginUser: Token信息刷新接口
@Overridepublic UserVO detail() {Long userId= ThreadLocalUtil.get(Constants.USER_ID,Long.class);if(userId==null){throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);}UserVO userVO;userVO=userCacheManager.getUserById(userId);if(userVO==null){throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);}if(StrUtil.isNotEmpty(userVO.getHeadImage())){userVO.setHeadImage(download+userVO.getHeadImage());}return userVO;}@Overridepublic int edit(UserUpdateDTO userUpdateDTO) {User user = getUser();user.setNickName(userUpdateDTO.getNickName());user.setSex(userUpdateDTO.getSex());user.setSchoolName(userUpdateDTO.getSchoolName());user.setMajorName(userUpdateDTO.getMajorName());user.setPhone(userUpdateDTO.getPhone());user.setEmail(userUpdateDTO.getEmail());user.setWechat(userUpdateDTO.getWechat());user.setIntroduce(userUpdateDTO.getIntroduce());//更新用户缓存extracted(user);return userMapper.updateById(user);}private void extracted(User user) {userCacheManager.refreshUser(user);tokenService.refreshLoginUser(user.getNickName(), user.getHeadImage(),ThreadLocalUtil.get(Constants.USER_KEY, String.class));}
用户竞赛接口
用户报名竞赛
获取当前用户信息,筛选用户是否符合条件(1.用户处于登录状态 2.不能报名不存在的竞赛 3.不能重复报名 4.已经开赛的禁止报名)
条件符合之后,往用户竞赛表中添加数据(底层使用list结构存储数据(头插法))
@Overridepublic int enter(String token, Long examId) {// 获取当前用户的信息 statusLong userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
// UserVO user = userCacheManager.getUserById(userId);
// if (user.getStatus()==0){
// throw new ServiceException(ResultCode.FAILED_USER_BANNED);
// }//使用spring aop//报名是否符合条件 (1.用户处于登录状态 2.不能报名不存在的竞赛 3.不能重复报名 4.已经开赛的禁止报名)Exam exam = examMapper.selectById(examId);if(exam==null){throw new ServiceException(ResultCode.EXAM_RESULT_NOT_EXIST);}if(exam.getStartTime().isBefore(LocalDateTime.now())){throw new ServiceException(ResultCode.EXAM_IS_START);}
// Long userId= tokenService.getUserId(token,secret);
// Long userId = userId;UserExam userExam = userExamMapper.selectOne(new LambdaQueryWrapper<UserExam>().eq(UserExam::getExamId, examId).eq(UserExam::getUserId, userId));if(userExam!=null){throw new ServiceException(ResultCode.USER_EXAM_HAS_ENTER);}examCacheManager.addUserExamCache(userId, examId);userExam=new UserExam();userExam.setExamId(examId);userExam.setUserId(userId);return userExamMapper.insert(userExam);}
用户竞赛报名接口查询
type :0:代表未完赛 1:代表历史竞赛
先查询用户信息,把type的值设置为2,
userExamMapper.selectUserExamList(userId)根据userId获取指定类型的考试列表长度
判断type和userId是否为空
不为空生成redis唯一标识符,返回一个json形式的数据
@Overridepublic TableDataInfo list(ExamQueryDTO examQueryDTO) {Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);examQueryDTO.setType(ExamListType.USER_EXAM_LIST.getValue());Long total = examCacheManager.getListSize(ExamListType.USER_EXAM_LIST.getValue(),userId);List<ExamVO> examVOList;if(total == null || total <= 0){//缓存不存在时的处理://从数据库中查询我的竞赛列表PageHelper.startPage(examQueryDTO.getPageNum(),examQueryDTO.getPageSize());examVOList = userExamMapper.selectUserExamList(userId);//将数据库中的数据同步给缓存examCacheManager.refreshCache(examQueryDTO.getType(),userId);//从数据库中查询total=new PageInfo<>(examVOList).getTotal();}else{//缓存存在时的处理:examVOList = examCacheManager.getExamVOList(examQueryDTO,userId);//从redis中查询,在查询时,出现异常情况,可能会重新刷新数据,这次查询用户数据的更新
// 0:代表未完赛 1:表示开始竞赛total = examCacheManager.getListSize(examQueryDTO.getType(),userId);}
// 空结果处理:if (CollectionUtil.isEmpty(examVOList)){return TableDataInfo.empty();}//获取符合查询条件的数据总数//从数据库中查询数据,不是从redis中查询数据return TableDataInfo.success(examVOList,total);}
public Long getListSize(Integer examListType, Long userId) {// 1. 参数校验(示例,根据实际需求调整)if (examListType == null || userId == null) {throw new IllegalArgumentException("参数不能为null");}// 2. 生成业务唯一的Redis键String examListKey = getExamListKey(examListType, userId);// 3. 获取Redis列表长度(返回Long类型,可能为0)return redisService.getListSize(examListKey);
}
//未查出任何数据时调用public static TableDataInfo empty() {TableDataInfo rspData = new TableDataInfo();rspData.setCode(ResultCode.SUCCESS.getCode());rspData.setRows(new ArrayList<>());rspData.setMsg(ResultCode.SUCCESS.getMsg());rspData.setTotal(0);return rspData;}//查出数据时调用public static TableDataInfo success(List<?> list,long total) {TableDataInfo rspData = new TableDataInfo();rspData.setCode(ResultCode.SUCCESS.getCode());rspData.setRows(list);rspData.setMsg(ResultCode.SUCCESS.getMsg());rspData.setTotal(total);return rspData;}
用户信息列表
获取用户消息列表(带分页,优先从缓存读取)
从ThreadLocal获取当前用户ID 检查缓存中是否存在消息列表
缓存不存在,为空时查询数据库并刷新缓存
返回分页格式的统一响应
public TableDataInfo list(PageQueryDTO dto) {// 1. 获取当前用户ID(从线程上下文)Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);if (userId == null) {throw new ServiceException("用户未登录");}// 2. 检查缓存是否存在有效数据Long total = messageCacheManager.getListSize(userId);List<MessageTextVO> messageTextVOList;// 3. 缓存不存在时走数据库查询if (total == null || total <= 0) {// 3.1 启用分页查询PageHelper.startPage(dto.getPageNum(), dto.getPageSize());messageTextVOList = messageTextMapper.selectUserMsgList(userId);// 3.2 刷新缓存messageCacheManager.refreshCache(userId);total = new PageInfo<>(messageTextVOList).getTotal();} // 4. 缓存存在时直接读取else {messageTextVOList = messageCacheManager.getMsgTextVOList(dto, userId);}// 5. 处理空结果if (CollectionUtil.isEmpty(messageTextVOList)) {return TableDataInfo.empty();}// 6. 返回统一分页响应return TableDataInfo.success(messageTextVOList, total);
}
ThreadLocalUtil
ThreadLocalUtil是一个工具类,封装了Java的ThreadLocal操作,ThreadLocal工具类(基于TransmittableThreadLocal实现)
封装线程隔离的变量存储功能,解决原生ThreadLocal在线程池场景下的数据污染问题
使用TransmittableThreadLocal替代原生ThreadLocal,支持线程池环境
每个线程独立维护ConcurrentHashMap存储数据,线程安全
自动处理null值存储(转为空字符串)
必须显式调用remove()避免内存泄漏详情看注释
//ThreadLocalUtil是一个工具类,封装了Java的ThreadLocal操作,
//用于实现线程隔离的变量存储(每个线程独立存取数据,互不干扰)。
public class ThreadLocalUtil {
// 使用 TransmittableThreadLocal(而非原生 ThreadLocal),
// 解决原生 ThreadLocal 在线程池中线程复用导致的数据错乱问题。private static final TransmittableThreadLocal<Map<String, Object>>THREAD_LOCAL = new TransmittableThreadLocal<>();/*** 存储键值对到当前线程上下文* @param key 键(非空)* @param value 值(自动转换null为空字符串)*/public static void set(String key, Object value) {Map<String, Object> map = getLocalMap();map.put(key, value == null ? StrUtil.EMPTY : value);}/*** 从当前线程上下文获取值(带类型转换)* @param key 键* @param clazz 目标类型Class对象* @return 值(不存在时返回null)* @param <T> 返回值泛型*/public static <T> T get(String key, Class<T> clazz) {Map<String, Object> map = getLocalMap();return (T) map.getOrDefault(key, null);}/*** 获取当前线程的存储Map(不存在时自动初始化)* * 注意:使用ConcurrentHashMap保证线程安全</p>* * @return 当前线程关联的Map(永不为null)*/// 每个线程独立维护一个 Map<String, Object>,
// 通过键值对(Key-Value)存储数据,线程间数据互不干扰。public static Map<String, Object> getLocalMap() {Map<String, Object> map = THREAD_LOCAL.get();if (map == null) {map = new ConcurrentHashMap<String, Object>();THREAD_LOCAL.set(map);}return map;}/*** 清除当前线程的存储Map(* * 使用场景:* 线程池场景必须调用,避免内存泄漏* 请求处理结束时建议调用*///清除当前线程的 Map,防止内存泄漏(尤其在线程池场景中必须调用)public static void remove() {THREAD_LOCAL.remove();}
}
Hutool工具库
官方网站:
Hutool🍬一个功能丰富且易用的Java工具库,涵盖了字符串、数字、集合、编码、日期、文件、IO、加密、数据库JDBC、JSON、HTTP客户端等功能。
导入依赖
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-core</artifactId><version>5.8.16</version> <!-- 以最新版本为准 -->
</dependency>
常用功能介绍
1.1 字符串工具(StrUtil)
import cn.hutool.core.util.StrUtil;// 判空
boolean isBlank = StrUtil.isBlank(" "); // true(空或空白字符)
boolean isEmpty = StrUtil.isEmpty(""); // true(仅空字符串)// 格式化字符串
String formatted = StrUtil.format("Hello, {}!", "Hutool"); // "Hello, Hutool!"// 字符串截取、填充、反转
String sub = StrUtil.sub("HelloWorld", 2, 5); // "llo"
String padded = StrUtil.padPre("123", 5, '0'); // "00123"
String reversed = StrUtil.reverse("ABC"); // "CBA"
1.2 数字工具(NumberUtil)
import cn.hutool.core.util.NumberUtil;// 数学计算
int sum = NumberUtil.add(1, 2, 3); // 6
double div = NumberUtil.div(10, 3, 2); // 3.33(保留2位小数)// 数字判断
boolean isNumber = NumberUtil.isNumber("123.45"); // true
boolean isInteger = NumberUtil.isInteger("100"); // true
1.3 日期时间工具(DateUtil)
import cn.hutool.core.date.DateUtil;// 日期解析与格式化
Date date = DateUtil.parse("2023-10-01"); // String → Date
String dateStr = DateUtil.format(date, "yyyy/MM/dd"); // "2023/10/01"// 日期计算
Date tomorrow = DateUtil.offsetDay(new Date(), 1); // 明天
long betweenDays = DateUtil.between(date, new Date(), DateUnit.DAY); // 相差天数// 获取时间部分
int year = DateUtil.year(date); // 2023
int month = DateUtil.month(date) + 1; // 10(月份从0开始)
1.4 集合工具(CollUtil)
import cn.hutool.core.collection.CollUtil;List<String> list = CollUtil.newArrayList("A", "B", "C");// 判空
boolean isEmpty = CollUtil.isEmpty(list); // false// 集合操作
List<String> reversedList = CollUtil.reverse(list); // ["C", "B", "A"]
String joined = CollUtil.join(list, ","); // "A,B,C"
2.1 文件工具(FileUtil)
import cn.hutool.core.io.FileUtil;// 读取文件内容
String content = FileUtil.readUtf8String("test.txt");// 写入文件
FileUtil.writeUtf8String("Hello, Hutool!", "output.txt");// 文件操作
boolean exists = FileUtil.exist("test.txt"); // 检查文件是否存在
FileUtil.copy("src.txt", "dest.txt", true); // 复制文件(覆盖)
FileUtil.del("temp_dir"); // 删除目录
2.2 流工具(IoUtil)
import cn.hutool.core.io.IoUtil;// 流拷贝(自动关闭流)
try (InputStream in = new FileInputStream("src.txt");OutputStream out = new FileOutputStream("dest.txt")) {IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE);
}
3.1 摘要加密(DigestUtil)
import cn.hutool.crypto.digest.DigestUtil;// MD5
String md5 = DigestUtil.md5Hex("123456"); // "e10adc3949ba59abbe56e057f20f883e"// SHA-256
String sha256 = DigestUtil.sha256Hex("123456");// 加盐加密(BCrypt)
String hashed = BCrypt.hashpw("password");
boolean isMatch = BCrypt.checkpw("password", hashed); // 校验
3.2 对称加密(AES、DES)
import cn.hutool.crypto.SecureUtil;// AES 加密
String key = "1234567890abcdef"; // 16/24/32位密钥
String encrypted = SecureUtil.aes(key.getBytes()).encryptHex("Hello");
String decrypted = SecureUtil.aes(key.getBytes()).decryptStr(encrypted);
模块 | 主要功能 | 常用类 |
---|---|---|
hutool-core | 字符串、日期、集合、数字 | StrUtil , DateUtil , CollUtil |
hutool-http | HTTP 请求 | HttpUtil |
hutool-crypto | 加密解密(MD5、AES、BCrypt) | DigestUtil , SecureUtil |
hutool-json | JSON/XML 处理 | JSONUtil , XmlUtil |
hutool-extra | 文件、IO、邮件等 | FileUtil , MailUtil |
相关文章:
微服务项目->在线oj系统(Java版 - 4)
相信自己,终会成功 目录 B端用户管理 C端用户代码 发送验证码: 验证验证码 退出登录 登录用户信息功能 用户详情与用户编辑 用户竞赛接口 用户报名竞赛 用户竞赛报名接口查询 用户信息列表 ThreadLocalUtil Hutool工具库 常用功能介绍 B端用户管理 进行列表显示与…...
DDoS与CC攻击:谁才是服务器的终极威胁?
在网络安全领域,DDoS(分布式拒绝服务)与CC(Challenge Collapsar)攻击是两种最常见的拒绝服务攻击方式。它们的目标都是通过消耗服务器资源,导致服务不可用,但攻击方式、威胁程度和防御策略存在显…...
旧物回收小程序,一键解决旧物处理难题
在快节奏的现代生活中,我们常常会面临旧物处理的困扰。扔掉觉得可惜,留着又占空间,而且处理起来还十分麻烦。别担心,我们的旧物回收小程序来啦,只需一键,就能轻松解决你的旧物处理难题! 这款小…...
uniapp小程序获取手机设备安全距离
utils.js let systemInfo null;export const getSystemInfo () > {if (!systemInfo) {systemInfo uni.getSystemInfoSync();// 补充安全区域默认值systemInfo.safeAreaInsets systemInfo.safeAreaInsets || {top: 0,bottom: 0,left: 0,right: 0};// 确保statusBarHei…...
小程序弹出层/抽屉封装 (抖音小程序)
最近忙于开发抖音小程序,最想吐槽的就是,既没有适配的UI框架,百度上还找不到关于抖音小程序的案列,我真的很裂开啊,于是我通过大模型封装了一套代码 效果如下 介绍 可以看到 这个弹出层是支持关闭和标题显示的…...
map与set封装
封装map和set一般分为6步: 1.封装map与set 2.普通迭代器 3.const 迭代器 4.insert返回值处理 5.map operator【】 6.key不能修改的问题 一.红黑树的改造 map与set的底层是通过红黑树来封装的,但是map与set的结点储存的值不一样,set只需要存…...
【C语言基础语法入门】通过简单实例快速掌握C语言核心概念
文章目录 1. Hello World:第一个C程序2. 变量与数据类型3. 运算符4. 控制结构4.1 if-else 条件判断4.2 for 循环4.3 while 循环 5. 函数6. 数组7. 指针8. 结构体总结 📣按照国际惯例,首先声明:本文只是我自己学习的理解࿰…...
Manus AI 突破多语言手写识别技术壁垒:创新架构、算法与应用解析
在人工智能领域,手写识别技术作为连接人类自然书写与数字世界的桥梁,一直备受关注。然而,多语言手写识别面临诸多技术挑战,如语言多样性、书写风格差异、数据稀缺性等。Manus AI 作为该领域的领军者,通过一系列创新技术…...
数字图像处理——图像压缩
背景 图像压缩是一种减少图像文件大小的技术,旨在在保持视觉质量的同时降低存储和传输成本。随着数字图像的广泛应用,图像压缩在多个领域如互联网、移动通信、医学影像和卫星图像处理中变得至关重要。 技术总览 当下图像压缩JPEG几乎一统天下ÿ…...
SGLang和vllm比有什么优势?
环境: SGLang vllm 问题描述: SGLang和vllm比有什么优势? 解决方案: SGLang和vLLM都是在大语言模型(LLM)推理和部署领域的开源项目或框架,它们各自有不同的设计目标和优势。下面我综合目前…...
BeanFactory和FactoryBean的区别
目录 1、Spring-core 2、控制反转(IoC) 2.1、定义 2.2、实现方式 1、BeanFactory 2、ApplicationContext 3、FactoryBean BeanFactory是容器,管理所有Bean(包括FactoryBean),FactoryBean是被管理的Bean,只是它有…...
仓颉开发语言入门教程:搭建开发环境
仓颉开发语言作为华为为鸿蒙系统自研的开发语言,虽然才发布不久,但是它承担着极其重要的历史使命。作为鸿蒙开发者,掌握仓颉开发语言将成为不可或缺的技能,今天我们从零开始,为大家分享仓颉语言的开发教程,…...
火花生态【算力通】公测,助力全球闲置算力训练AI模型
近日,在数字化浪潮迅猛推进的大背景下,人工智能模型训练对算力的需求呈井喷式增长,而全球范围内大量算力资源却处于闲置状态,如何高效整合这些闲置算力,成为推动行业发展的关键命题。在此关键时刻,火花生态旗下的核心产品【算力通】(ComputePower)于 2025 年 5 月 10 日正式开启…...
OpenMV IDE 的图像接收缓冲区原理
OpenMV IDE 的图像接收缓冲区原理与 嵌入式图像处理系统 的数据流控制密切相关。以下是其核心工作原理的分步解析: 一、图像缓冲区架构 OpenMV 的整个图像处理流程基于 双缓冲(Double Buffering)机制,主要分为以下层级࿱…...
如何在LVGL之外的线程更新UI内容
前言 作为一个刚开始学习LVGL和嵌入式开发的新手,学会绘制一个界面之后,遇到了一个问题:在LVGL线程之外的线程,更新UI内容时,会导致程序崩溃。 1、问题分析 首先,需要了解LVGL的基本工作原理。LVGL&#…...
实景VR展厅制作流程与众趣科技实景VR展厅应用
实景VR展厅制作是一种利用虚拟现实技术将现实世界中的展览空间数字化并在线上重现的技术。 这种技术通过三维重建和扫描等手段,将线下展馆的场景、展品和信息以三维形式搬到云端数字空间,从而实现更加直观、立体的展示效果。在制作过程中,首…...
Regmap子系统之六轴传感器驱动-编写icm20607.c驱动
(一)在驱动中要操作很多芯片相关的寄存器,所以需要先新建一个icm20607.h的头文件,用来定义相关寄存器值。 #ifndef ICM20607_H #define ICM20607_H /*************************************************************** 文件名 : i…...
计算机网络-HTTP与HTTPS
文章目录 计算机网络网络模型网络OSITCP/IP 应用层常用协议HTTP报文HTTP状态码HTTP请求类型HTTP握手过程HTTP连接HTTP断点续传HTTPSHTTPS握手过程 计算机网络 网络模型 为了解决多种设备能够通过网络相互通信,解决网络互联兼容性问题。 网络模型是计算机网络中用于…...
Text2SQL在Spark NLP中的实现与应用:将自然语言问题转换为SQL查询的技术解析
概述 SQL 仍然是当前行业中最受欢迎的技能之一 免责声明:Spark NLP 中的 Text2SQL 注释器在 v3.x(2021 年 3 月)中已被弃用,不再使用。如果您想测试该模块,请使用 Spark NLP for Healthcare 的早期版本。 自新千年伊…...
Ubuntu20.04下使用dpkg方式安装WPS后,将WPS改为中文界面方法
Ubuntu20.04下使用dpkg方式安装WPS后,将WPS改为中文界面方法 说明方法 说明 Ubuntu20.04下使用dpkg方式安装WPS后,打开WPS后,发现界面是英文的,如有需要可以按照下面的方法将其改为中文界面。 方法 cd /opt/kingsoft/wps-offic…...
OpenCV CUDA 模块中的矩阵算术运算-----在频域(复数频谱)中执行逐元素乘法并缩放的函数mulAndScaleSpectrums()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 mulAndScaleSpectrums()是OpenCV CUDA模块中用于在频域(复数频谱)中执行逐元素乘法并缩放 的函数。 这个函数主要用于在…...
批量剪辑 + 矩阵分发 + 数字人分身源码搭建全技术解析,支持OEM
在互联网内容生态蓬勃发展的当下,企业与创作者对内容生产与传播效率的要求日益增长。批量剪辑、矩阵分发和数字人分身技术的融合,成为提升内容创作与运营效能的关键方案。从源码层面实现三者的搭建与整合,需要深入理解各功能技术原理…...
Spring Boot 与 RabbitMQ 的深度集成实践(三)
高级特性实现 消息持久化 在实际的生产环境中,消息的可靠性是至关重要的。消息持久化是确保 RabbitMQ 在发生故障或重启后,消息不会丢失的关键机制。它涉及到消息、队列和交换机的持久化配置。 首先,配置队列持久化。在创建队列时…...
部署java项目
1.编写shell脚本部署服务 restart.sh #!/bin/bash # # start the user program # echo "-------------------- start jk service --------------------" LOG_DIR"/home/joy/usr/app/ers-log" LOG_FILE"$LOG_DIR/log_$(date "%Y%m%d").txt&…...
中国城市间交通驾车距离矩阵(2024)
中国城市间交通驾车距离矩阵(2024) 1852 数据简介 中国城市中心的交通驾车距离,该数据为通过审图号GS(2024)0650的中国城市地图得其城市中心距离,再通过高德地图api计算得出其交通驾车最短距离矩阵,单位为KM,方便大家研究使用。…...
物联网数据湖架构
物联网海量数据湖分析架构(推荐实践) ┌──────────────┐ │ IoT设备端 │ └──────┬───────┘│(MQTT/HTTP)▼ ┌──────────────┐ │ EMQX等 │ 可选(也可…...
Python将Excel单元格某一范围生成—截图(进阶版—带样式+批量+多级表头)
目录 专栏导读1、库的介绍2、库的安装3、核心代码4、通用版——带样式5、进阶版(可筛选+自动截图)多级表头版总结专栏导读 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手 🏳️🌈 博客主页:请点击——> 一晌小贪欢的博客主页求关注 👍 该…...
使用Python将 Excel 中的图表、形状和其他元素导出为图片
目录 为什么将 Excel 中的图表、形状和其他元素导出为图片? 工具与设置 Python 将 Excel 图表导出为图片 将图表导出为图片 将图表工作表导出为图片 Python 将 Excel 中的形状和其他元素导出为图片 微软 Excel 是一个功能强大的数据分析和可视化工具ÿ…...
从编程助手到AI工程师:Trae插件Builder模式实战Excel合并工具开发
Trae插件下载链接:https://www.trae.com.cn/plugin 引言:AI编程工具的新纪元 在软件开发领域,AI辅助编程正在经历一场革命性的变革。Trae插件(原MarsCode编程助手)最新推出的Builder模式,标志着AI编程工具…...
AI大模型从0到1记录学习numpy pandas day25
第 3 章 Pandas 3.1 什么是Pandas Pandas 是一个开源的数据分析和数据处理库,它是基于 Python 编程语言的。 Pandas 提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据(类似于Excel表格)…...
【云实验】Excel文件转存到RDS数据库
实验名称:Excel文件转存到RDS数据库 说明:把Excel的数据通过数据管理服务DMS(Data Management Service)导入到RDS MySQL数据库中。 流程:创建一个RDS for MySQL的实例,再创建数据库和账号,通过D…...
用Python实现数据库数据自动化导出PDF报告:从MySQL到个性化文档的全流程实践
本文将介绍如何使用Python构建一个自动化工具,实现从MySQL数据库提取员工数据,并为每位员工生成包含定制化表格的PDF报告。通过该方案,可显著提升数据导出效率,避免手动操作误差,同时支持灵活的格式定制。 需求&#…...
深入理解 ZAB:ZooKeeper 原子广播协议的工作原理
目录 ZAB 协议:ZooKeeper 如何做到高可用和强一致?🔒ZAB 协议的核心目标 🎯ZAB 协议的关键概念 💡ZAB 协议的运行阶段 🎬阶段一:Leader 选举 (Leader Election) 🗳️阶段二ÿ…...
Javascript本地存储的方式有哪些?区别及应用场景?(含Deep Seek讲解)
JavaScript本地存储方式的区别与适用场景 1. Cookie 特点: Cookie是一种较早的本地存储技术,主要通过HTTP协议在客户端和服务器之间传递数据。它的大小通常被限制为4KB以内,并且每次HTTP请求都会携带Cookie信息。缺点: 数据量有限制(最多4K…...
二元Logistic回归
二元Logistic回归 在机器学习领域,二元Logistic回归是一种非常经典的分类模型,广泛用于解决具有两类标签的分类问题。Logistic回归通过逻辑函数(Sigmoid函数)将预测结果映射到概率值,并进行分类。 一、Logistic回归 …...
Android framework 问题记录
一、休眠唤醒,很快熄屏 1.1 问题描述 机器休眠唤醒后,没有按照约定的熄屏timeout 进行熄屏,很快就熄屏(约2s~3s左右) 1.2 原因分析: 抓取相关log,打印休眠背光 相关调用栈 //具体打印调用栈…...
企业网站架构部署与优化 --web技术与nginx网站环境部署
一、Web 基础 本节将介绍Web 基础知识,包括域名的概念、DNS 原理、静态网页和动态网页的 相关知识。 1、域名和DNS 1.1、域名的概念 网络是基于TCP/IP 协议进行通信和连接的,每一台主机都有一个唯一的标识(固定的IP 地址),用以区别在网络上成千上万个用户和计算机。…...
Scala与Spark:原理、实践与技术全景详解
Scala与Spark:原理、实践与技术全景详解 一、引言 在大数据与分布式计算领域,Apache Spark 已成为事实标准的计算引擎,而 Scala 作为其主要开发语言,也逐渐成为数据工程师和后端开发者的必备技能。本文将系统梳理 Scala 语言基础…...
【聚类】层次聚类
层次聚类 文章目录 层次聚类1. 算法介绍2. 公式及原理3. 伪代码 1. 算法介绍 背景与目标 层次聚类(Hierarchical Clustering)是一类无需事先指定簇数的聚类方法,通过构造一棵“树状图”(dendrogram)来呈现数据的多层次…...
Windows环境安装LibreOffice实现word转pdf
前言:最近在工作中遇到了一个需求要实现word转pdf,本来我在上一个公司使用aspose.words工具使用的得心应手,都已经把功能点实现了,两句代码轻轻松松,但是被告知不能用商业版的东西,公司要求只能用开源的&am…...
【FAQ】HarmonyOS SDK 闭源开放能力 —Vision Kit (3)
1.问题描述: 通过CardRecognition识别身份证拍照拿到的照片地址,使用该方法获取不到图片文件,请问如何解决? 解决方案: //卡证识别实现页,文件名为CardDemoPage,需被引入至入口页 import { …...
【聚类】K-means++
K-means 文章目录 K-means1. 算法介绍2. 公式及原理3. 伪代码 1. 算法介绍 背景与目标 k-means 是 David Arthur 和 Sergei Vassilvitskii 于2007年提出的改进 k-means 初始化方法,其核心目标是: 在保证聚类质量的前提下,通过更合理地选择初始…...
Java实现PDF加水印功能:技术解析与实践指南
Java实现PDF加水印功能:技术解析与实践指南 在当今数字化办公环境中,PDF文件因其跨平台兼容性和格式稳定性而被广泛应用。然而,为了保护文档的版权、标记文档状态(如“草稿”“机密”等)或增加文档的可追溯性…...
【C#】用 DevExpress 创建带“下拉子表”的参数表格视图
展示如何用 DevExpress 创建带“下拉子表”的参数表格视图。主表为 参数行 ParamRow,子表为 子项 ChildParam。 一、创建模型类 public class ParamRow {public string Pn { get; set; }public string DisplayName { get; set; }public string Value { get; set; }…...
Go语言八股文之Mysql优化
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…...
学习记录:DAY29
项目开发日志:技术实践与成长之路 前言 回顾这几天的状态,热情总是比我想象中更快被消耗完。比起茫然徘徊的小丑,我更希望自己是对着风车冲锋的疯子。 今天继续深入项目的实际业务。 状态好点的时候,再看自己EMO时写的东西&…...
LLaMA-Factory:了解webUI参数
Finetuning method参数 full(全量微调) 更新模型全部参数,完全适配新任务 效果最好,但资源消耗最大适用于计算资源充足的场景存在过拟合的风险,需要大量数据支持 freeze(冻结微调) 固定底层参…...
【实战】GPT-SoVITS+内网穿透:3分钟搭建可公网访问的语音克隆系统
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
HTML向四周扩散背景
<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>扩散背景效果</title><style>body {…...
React 个人笔记 Hooks编程
作用 配合函数式编程,保证在不产生类的时候完成一个整体的组件 常用组件 useStateuseContextuseReduceruseEffectuseMemouseCallback 前三个值为自变量 后三者为因变量 前三者相当于其他编程函数的变量声明,而后三者相当于对变量进行了(if now ! pr…...