FineReport自定义登录系统技术 - 详解

news/2025/10/10 22:21:13/文章来源:https://www.cnblogs.com/yxysuanfa/p/19133983

FineReport自定义登录系统技术

项目背景与架构概述

这是一个基于FineReport 11.0的自定义登录系统实现,通过扩展FineReport的插件机制,实现了自定义登录页面与系统原生认证的无缝集成(可以实现通过系统默认登录页和自定义登录,登出时,跳转到对应的登录页)。项目采用前后端分离的架构设计,包含两个核心组件:

一、bilogin.html 深度技术分析

1.1 UI设计与用户体验

采用现代化的响应式设计,具有以下特点:

1.2 核心技术实现

登录认证流程
// 关键的登录请求实现
var loginUrl = "http://localhost:8075/webroot/decision/login?login_source=CUSTOM_PAGE";
jQuery.ajax({
url: loginUrl,
contentType: "application/json",
type: "POST",
dataType: "json",
data: JSON.stringify({
username: username,
password: password,
validity: -1,
origin: getUrlQuery("origin")
})
});

技术要点分析

  1. 参数传递策略login_source=CUSTOM_PAGE通过URL参数传递,而非JSON体,这是因为后端通过request.getParameter()获取
  2. 认证兼容性:保持与官方登录接口的完全兼容,使用相同的请求格式和参数结构
  3. 会话管理:支持origin参数,实现登录后的智能重定向
Cookie管理机制
// 认证状态保持
setCookie("fine_remember_login", data.validity, "/", day);
setCookie("fine_auth_token", data.accessToken, "/", day);

关键技术细节

  • fine_remember_login:记住登录状态标识
  • fine_auth_token:访问令牌,用于后续API调用的身份验证
  • 动态过期时间:根据validity值计算Cookie有效期
智能重定向系统
// 多种重定向方式支持
if (response.method && response.method.toUpperCase() === "GET") {
window.location.href = response.originUrl;
} else {
doActionByForm(response.originUrl, response.parameters, {
method: response.method
});
}

技术优势

  • GET请求:直接使用window.location.href进行跳转
  • POST请求:通过动态创建表单实现POST重定向,避免浏览器限制
  • 参数传递:完整保持原始请求的参数和方法

1.3 错误处理与用户体验

实现了完善的错误处理机制:

  • 超时处理:5秒超时设置,避免长时间等待
  • 网络错误:区分超时和其他网络错误,提供针对性提示
  • 业务错误:显示服务器返回的具体错误信息

二、CustomLogInOutEventProvider 架构分析

2.1 插件扩展机制

继承自AbstractLogInOutEventProvider ,这是FineReport提供的登录事件扩展点。

插件注册机制

@FunctionRecorder
public class CustomLogInOutEventProvider
extends AbstractLogInOutEventProvider

@FunctionRecorder注解确保插件被正确注册到FineReport的插件系统中。

2.2 登录源识别与状态管理

常量定义与设计模式
private static final String LOGIN_SOURCE_KEY = "LOGIN_SOURCE";
private static final String LOGIN_SOURCE_COOKIE = "FR_LOGIN_SOURCE";
private static final String CUSTOM_LOGIN_SOURCE = "CUSTOM_PAGE";
private static final String DEFAULT_LOGIN_SOURCE = "DEFAULT_PAGE";

设计优势

  • 常量集中管理:避免硬编码,提高代码可维护性
  • 命名规范:使用有意义的常量名,增强代码可读性
  • 扩展性:便于后续添加更多登录源类型
双重状态存储机制
// Session存储
session.setAttribute(LOGIN_SOURCE_KEY, "custom");
// Cookie存储
Cookie loginSourceCookie = new Cookie(LOGIN_SOURCE_COOKIE, "custom");
loginSourceCookie.setPath("/");
loginSourceCookie.setMaxAge(24 * 60 * 60);

技术优势

  • Session存储:服务器端状态,安全性高,但依赖会话
  • Cookie存储:客户端状态,持久化存储,跨会话有效
  • 双重保障:确保在各种场景下都能正确识别登录源

2.3 登录事件处理逻辑

方法实现了登录时的状态设置:

String loginFrom = result.getRequest().getParameter("login_source");
if ("CUSTOM_PAGE".equals(loginFrom)) {
session.setAttribute(LOGIN_SOURCE_KEY, "custom");
// 设置Cookie逻辑
} else {
session.removeAttribute(LOGIN_SOURCE_KEY);
// 清除Cookie逻辑
}

处理策略

  1. 参数获取:通过request.getParameter()获取登录源标识
  2. 条件判断:精确匹配"CUSTOM_PAGE"字符串
  3. 状态设置:同时设置Session和Cookie
  4. 清理机制:非自定义登录时主动清理状态

2.4 登出重定向策略

方法实现了智能的登出重定向:

// 优先从Session获取
String loginSource = (String) session.getAttribute(LOGIN_SOURCE_KEY);
// Session失效时从Cookie获取
if (loginSource == null) {
Cookie[] cookies = request.getCookies();
// 遍历Cookie查找登录源
}
// 根据登录源决定重定向目标
if ("custom".equals(loginSource)) {
return CUSTOM_LOGIN_PAGE_URL;
} else {
return DEFAULT_LOGIN_URL;
}

容错机制

  • 多级查找:Session → Cookie → 默认处理
  • 状态清理:登出时主动清理Session和Cookie
  • 日志记录:完整的操作日志,便于问题排查

三、系统协作关系与架构设计

3.1 前后端协作流程

在这里插入图片描述

3.2 状态管理架构

多层状态存储

  1. 前端状态:认证Cookie (fine_auth_token, fine_remember_login)
  2. 会话状态:Session中的登录源标识
  3. 持久状态:Cookie中的登录源备份

状态同步机制

  • 登录时:前端设置认证Cookie,后端设置登录源状态
  • 会话中:通过Session快速获取登录源
  • 跨会话:通过Cookie恢复登录源信息
  • 登出时:清理所有相关状态

3.3 安全性设计

认证安全

参数安全

四、技术要点与最佳实践

4.1 关键技术决策

  1. 参数传递方式

  2. 状态存储策略

  3. 重定向实现

4.2 性能优化要点

  1. 前端优化

    • 使用CDN加载jQuery库
    • CSS样式内联,减少HTTP请求
    • 合理的超时设置,避免长时间等待
  2. 后端优化

    • 常量定义避免重复字符串创建
    • 条件判断优化,减少不必要的操作
    • 及时清理无用的Session和Cookie

4.3 扩展性设计

  1. 多登录源支持

  2. 配置化改进


FineReport自定义登录系统深度解析:从前端到后端的完整实现

项目背景与架构概述

本项目是基于FineReport 11.0的自定义登录系统实现,通过扩展FineReport的插件机制,实现了自定义登录页面与系统默认登录的智能切换。项目包含两个核心文件:

一、前端实现:bilogin.html 深度解析

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; " charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>BI分析系统</title><script type="text/javascript" src="https://cdn.bootcss.com/jquery/1.9.1/jquery.min.js"></script><style>* {margin: 0;padding: 0;box-sizing: border-box;}body {font: 14px/1.6 "\5FAE\8F6F\96C5\9ED1", "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", Arial, sans-serif;background: #e8f0fe;min-height: 100vh;display: flex;align-items: center;justify-content: center;}.login-container {background: transparent;padding: 100px 80px;width: 600px;text-align: center;}.logo-section {display: flex;align-items: center;justify-content: center;margin-bottom: 50px;gap: 15px;}.logo img {width: 90px;height: auto;}.brand-text {font-size: 43px;font-weight: 800;color: #333;letter-spacing: 1px;}.form-group {margin-bottom: 20px;text-align: center;}.form-input {width: 100%;padding: 20px 20px;border: 1px solid #ddd;border-radius: 25px;font-size: 16px;transition: all 0.2s ease;outline: none;background: white;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);}.form-input:focus {border-color: #1976d2;box-shadow: 0 2px 12px rgba(25, 118, 210, 0.2);}.form-input::placeholder {color: #999;}.login-btn {width: 100%;padding: 15px;background: #1976d2;color: white;border: none;border-radius: 25px;font-size: 24px;font-weight: 500;cursor: pointer;transition: all 0.2s ease;margin-top: 20px;box-shadow: 0 4px 12px rgba(25, 118, 210, 0.3);}.login-btn:hover {background: #1565c0;box-shadow: 0 6px 16px rgba(25, 118, 210, 0.4);transform: translateY(-1px);}.login-btn:active {background: #0d47a1;transform: translateY(0);}.help-text {margin-top: 40px;color: #666;font-size: 16px;}</style><script type="text/javascript">function doSubmit() {var username = document.getElementById("username").value.trim();var password = document.getElementById("password").value.trim();if (username === "") {window.alert("请输入用户名");return false;}if (password === "") {window.alert("请输入密码");return false;}// 参考官方login.html的实现方式,但需要通过URL参数传递login_source// 因为CustomLogInOutEventProvider通过request.getParameter("login_source")获取参数var loginUrl = "http://localhost:8075/webroot/decision/login?login_source=CUSTOM_PAGE";jQuery.ajax({url: loginUrl,contentType: "application/json",type: "POST",dataType: "json",data: JSON.stringify({username: username,password: password,validity: -1,origin: getUrlQuery("origin") // 保持与官方login.html一致}),timeout: 5000,success: function (res) {console.log(res);// 登录成功后的处理逻辑if (res.data) {var data = res.data;// 设置登录状态和认证令牌Cookie(参考官方login.html)var day = data.validity === -2 ? (14 * 24) : -1;setCookie("fine_remember_login", data.validity, "/", day);setCookie("fine_auth_token", data.accessToken, "/", day);// 然后跳转到相应的页面var response = data.originUrlResponse;if (response) {if (response.method && response.method.toUpperCase() === "GET") {window.location.href = response.originUrl;} else {doActionByForm(response.originUrl, response.parameters, {method: response.method});}} else {// 如果没有originUrlResponse,默认跳转到决策平台window.location.href = "http://localhost:8075/webroot/decision";}} else {// 提示错误信息window.alert(res.errorMsg || "登录失败");}},error: function (xhr, status, error) {console.error("登录请求失败:", status, error);if (status === "timeout") {alert("登录超时,请重试");} else {alert("登录失败,请检查网络连接或联系管理员");}}});}// 查询url参数 - 添加与官方login.html相同的函数function getUrlQuery(name) {var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");var r = window.location.search.substr(1).match(reg);if (r !== null) return r[2];return "";}// 设置Cookie的辅助函数function setCookie(name, value, path, hours) {var expires = "";if (hours && hours >0) {var date = new Date();date.setTime(date.getTime() + (hours * 60 * 60 * 1000));expires = "; expires=" + date.toUTCString();}document.cookie = name + "=" + (value || "") + expires + "; path=" + (path || "/");}// 通过form表单跳转 - 添加与官方login.html相同的函数function doActionByForm(url, data, options) {options = options || {};var config = {method: options.method || "post",url: url,data: data || {},target: options.target || "_self"};var form = document.createElement("form");form.setAttribute("method", config.method);form.setAttribute("action", config.url);form.setAttribute("target", config.target);for (var key in config.data) {var hiddenField = document.createElement("input");hiddenField.setAttribute("type", "hidden");hiddenField.setAttribute("name", key);hiddenField.setAttribute("value", config.data[key]);form.appendChild(hiddenField);}document.body.appendChild(form);form.submit();document.body.removeChild(form);}</script></head><body><div class="login-container"><div class="logo-section"><div class="logo"><img src="data:image/svg+xml;base64,xxxxxx" alt="BI Logo"></div><div class="brand-text">BI分析系统</div></div><form id="login" name="login" method="POST" action=""><div class="form-group"><input id="username" type="text" name="username" class="form-input" placeholder="用户名" /></div><div class="form-group"><input id="password" type="password" name="password" class="form-input" placeholder="密码" /></div><button type="button" class="login-btn" onClick="doSubmit()">登 录</button></form><div class="help-text">如需试用请联系xxxx</div></div></body></html>

1.1 页面设计与用户体验

采用了现代化的响应式设计:

<div class="login-container"><div class="logo-section"><div class="logo"><img src="data:image/svg+xml;base64,..." alt="Logo"></div><div class="brand-text">BI分析系统</div></div></div>

设计亮点:

  • 使用Flexbox布局实现完美居中
  • 渐变背景色 #e8f0fe 营造专业感
  • 圆角输入框和按钮提升现代感
  • 悬停效果和阴影增强交互体验

1.2 核心登录逻辑实现

登录请求处理
function doSubmit() {
var loginUrl = "http://localhost:8075/webroot/decision/login?login_source=CUSTOM_PAGE";
jQuery.ajax({
url: loginUrl,
contentType: "application/json",
type: "POST",
dataType: "json",
data: JSON.stringify({
username: username,
password: password,
validity: -1,
origin: getUrlQuery("origin")
})
});
}

关键技术点:

  1. 参数传递策略login_source=CUSTOM_PAGE 通过URL参数传递,而非JSON体内
  2. 兼容性设计:保持与官方 的接口一致性
  3. origin参数处理:支持登录后的页面跳转逻辑
认证状态管理
// 设置登录状态和认证令牌Cookie
var day = data.validity === -2 ? (14 * 24) : -1;
setCookie("fine_remember_login", data.validity, "/", day);
setCookie("fine_auth_token", data.accessToken, "/", day);

Cookie管理机制:

  • fine_remember_login:记录登录状态持久化选项
  • fine_auth_token:存储访问令牌,用于后续API调用认证
  • 动态过期时间:根据 validity 值设置不同的Cookie生命周期
智能跳转逻辑
var response = data.originUrlResponse;
if (response) {
if (response.method && response.method.toUpperCase() === "GET") {
window.location.href = response.originUrl;
} else {
doActionByForm(response.originUrl, response.parameters, {
method: response.method
});
}
} else {
window.location.href = "http://localhost:8075/webroot/decision";
}

跳转策略分析:

  1. GET请求:直接使用 window.location.href 跳转
  2. POST/其他请求:使用 动态创建表单提交
  3. 默认跳转:无 originUrlResponse 时跳转到决策平台首页

二、后端实现:CustomLogInOutEventProvider.java 深度解析

package com.fr.plugin.demo.loginout.event;
import com.fr.decision.fun.impl.AbstractLogInOutEventProvider;
import com.fr.decision.webservice.login.LogInOutResultInfo;
import com.fr.log.FineLoggerFactory;
import com.fr.plugin.transform.FunctionRecorder;
import com.fr.web.utils.WebUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@FunctionRecorder
public class CustomLogInOutEventProvider
extends AbstractLogInOutEventProvider {
private static final String LOGIN_SOURCE_KEY = "LOGIN_SOURCE";
private static final String LOGIN_SOURCE_COOKIE = "FR_LOGIN_SOURCE";
private static final String CUSTOM_LOGIN_PAGE_URL = "http://localhost:8075/webroot/bilogin.html";
private static final String DEFAULT_LOGIN_URL = "http://localhost:8075/webroot/decision/login";
@Override
public void loginAction(LogInOutResultInfo result) {
FineLoggerFactory.getLogger().info(result.getUsername() + " login, ip: " + WebUtils.getIpAddr(result.getRequest()));
HttpSession session = result.getRequest().getSession();
HttpServletResponse response = result.getResponse();
String loginFrom = result.getRequest().getParameter("login_source");
FineLoggerFactory.getLogger().info("Login source parameter: " + loginFrom);
if ("CUSTOM_PAGE".equals(loginFrom)) {
session.setAttribute(LOGIN_SOURCE_KEY, "custom");
Cookie loginSourceCookie = new Cookie(LOGIN_SOURCE_COOKIE, "custom");
loginSourceCookie.setPath("/");
loginSourceCookie.setMaxAge(24 * 60 * 60);
if (response != null) {
response.addCookie(loginSourceCookie);
}
FineLoggerFactory.getLogger().info("Set session and cookie LOGIN_SOURCE to 'custom'");
} else {
session.removeAttribute(LOGIN_SOURCE_KEY);
Cookie loginSourceCookie = new Cookie(LOGIN_SOURCE_COOKIE, "");
loginSourceCookie.setPath("/");
loginSourceCookie.setMaxAge(0);
if (response != null) {
response.addCookie(loginSourceCookie);
}
FineLoggerFactory.getLogger().info("Removed session LOGIN_SOURCE_KEY and cleared cookie");
}
super.loginAction(result);
}
@Override
public String logoutAction(LogInOutResultInfo result) {
FineLoggerFactory.getLogger().info(result.getUsername() + " logout, ip: " + WebUtils.getIpAddr(result.getRequest()));
HttpSession session = result.getRequest().getSession();
HttpServletRequest request = result.getRequest();
String loginSource = (String) session.getAttribute(LOGIN_SOURCE_KEY);
FineLoggerFactory.getLogger().info("Logout - session LOGIN_SOURCE_KEY: " + loginSource);
if (loginSource == null) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (LOGIN_SOURCE_COOKIE.equals(cookie.getName())) {
loginSource = cookie.getValue();
FineLoggerFactory.getLogger().info("Found login source in cookie: " + loginSource);
break;
}
}
}
}
if ("custom".equals(loginSource)) {
session.removeAttribute(LOGIN_SOURCE_KEY);
HttpServletResponse response = result.getResponse();
if (response != null) {
Cookie loginSourceCookie = new Cookie(LOGIN_SOURCE_COOKIE, "");
loginSourceCookie.setPath("/");
loginSourceCookie.setMaxAge(0);
response.addCookie(loginSourceCookie);
}
FineLoggerFactory.getLogger().info("Redirecting to custom login page: " + CUSTOM_LOGIN_PAGE_URL);
return CUSTOM_LOGIN_PAGE_URL;
} else {
FineLoggerFactory.getLogger().info("Redirecting to default login page: " + DEFAULT_LOGIN_URL);
return DEFAULT_LOGIN_URL;
}
}
}

2.1 插件架构与扩展点

继承自 AbstractLogInOutEventProvider,这是FineReport提供的登录登出事件扩展点:

@FunctionRecorder
public class CustomLogInOutEventProvider
extends AbstractLogInOutEventProvider {
// 实现自定义登录登出逻辑
}

架构优势:

  • 插件化设计:通过 @FunctionRecorder 注解自动注册
  • 事件驱动:在登录/登出关键节点插入自定义逻辑
  • 无侵入性:不修改FineReport核心代码

2.2 登录源识别与状态管理

常量定义与配置
private static final String LOGIN_SOURCE_KEY = "LOGIN_SOURCE";
private static final String LOGIN_SOURCE_COOKIE = "FR_LOGIN_SOURCE";
private static final String CUSTOM_LOGIN_SOURCE = "CUSTOM_PAGE";
private static final String DEFAULT_LOGIN_SOURCE = "DEFAULT_PAGE";
登录事件处理逻辑
@Override
public void loginAction(LogInOutResultInfo result) {
String loginFrom = result.getRequest().getParameter("login_source");
if ("CUSTOM_PAGE".equals(loginFrom)) {
// 设置Session属性
session.setAttribute(LOGIN_SOURCE_KEY, "custom");
// 设置Cookie标识
Cookie loginSourceCookie = new Cookie(LOGIN_SOURCE_COOKIE, "custom");
loginSourceCookie.setPath("/");
loginSourceCookie.setMaxAge(24 * 60 * 60);
// 24小时
response.addCookie(loginSourceCookie);
}
}

状态管理策略:

  1. 双重存储:同时使用Session和Cookie存储登录源信息
  2. Session优先:Session用于服务器端快速访问
  3. Cookie备份:Cookie用于跨会话持久化和容错

2.3 智能登出重定向机制

@Override
public String logoutAction(LogInOutResultInfo result) {
String loginSource = (String) session.getAttribute(LOGIN_SOURCE_KEY);
// Session失效时从Cookie恢复
if (loginSource == null) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (LOGIN_SOURCE_COOKIE.equals(cookie.getName())) {
loginSource = cookie.getValue();
break;
}
}
}
}
if ("custom".equals(loginSource)) {
return CUSTOM_LOGIN_PAGE_URL;
} else {
return DEFAULT_LOGIN_URL;
}
}

重定向逻辑分析:

  1. 优先级机制:Session > Cookie > 默认
  2. 容错设计:Session失效时自动从Cookie恢复
  3. 清理机制:登出时清除相关状态信息

三、前后端协作机制

3.1 数据流向分析

用户访问 bilogin.html
↓
输入用户名密码,点击登录
↓
AJAX POST: /webroot/decision/login?login_source=CUSTOM_PAGE
↓
CustomLogInOutEventProvider.loginAction() 被触发
↓
设置Session和Cookie标识登录源
↓
返回登录结果和跳转信息
↓
前端处理跳转逻辑

3.2 状态同步机制

组件存储位置数据格式生命周期
前端URL参数login_source=CUSTOM_PAGE单次请求
后端SessionLOGIN_SOURCE: "custom"会话期间
后端CookieFR_LOGIN_SOURCE: "custom"24小时

3.3 错误处理与容错机制

前端容错:

error: function (xhr, status, error) {
if (status === "timeout") {
alert("登录超时,请重试");
} else {
alert("登录失败,请检查网络连接或联系管理员");
}
}

后端容错:

// Cookie为空时的处理
if (loginSource == null) {
// 从Cookie恢复状态
}

四、技术要点与最佳实践

4.1 安全性考虑

  1. 参数验证:后端严格验证 login_source 参数值
  2. Cookie安全:设置适当的路径和过期时间
  3. 日志记录:详细记录登录来源和IP地址

4.2 性能优化

  1. AJAX超时设置:5秒超时避免长时间等待
  2. Cookie生命周期:24小时过期平衡性能和安全
  3. 最小化DOM操作:动态表单创建后立即移除

4.3 兼容性设计

  1. API一致性:与官方登录接口保持完全兼容
  2. 浏览器兼容:使用jQuery确保跨浏览器支持
  3. 响应式设计:适配不同屏幕尺寸

4.4 可维护性

  1. 常量集中管理:所有配置项使用常量定义
  2. 日志完整性:关键操作都有详细日志
  3. 代码注释:核心逻辑都有清晰注释

五、扩展建议

5.1 功能增强

  • 添加验证码机制
  • 支持多种登录方式(LDAP、SSO等)
  • 实现登录失败次数限制

5.2 监控与分析

5.3 安全加固

  • 实现CSRF防护
  • 添加IP白名单机制
  • 强化密码策略

总结

本项目展示了FineReport自定义登录系统的完整实现方案,通过前后端协作实现了登录源的智能识别和登出重定向。代码设计充分考虑了安全性、性能和可维护性,为企业级BI系统的定制化需求提供了优秀的参考实现。

关键成功因素包括:

  1. 架构设计:基于FineReport插件机制的无侵入扩展
  2. 状态管理:Session+Cookie双重保障的可靠性设计
  3. 用户体验:现代化UI设计和智能跳转逻辑
  4. 容错机制:完善的错误处理和状态恢复能力

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

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

相关文章

实验报告2(简单实现图书馆管理系统)

一、实验目的:、 实现书上图书馆管理系统 (1) 主函数 (2) 修改:根据指定的ISBN,修改图书的价格 (3) 排序:将图书按照价格由低到高进行排序。 (4) 计数:统计文件中的图书数量 要求:用c语…

实验报告1(switch语句,二维数组)

一、实验目的: 熟练使用switch语句 熟练使用二维数组 二、实验仪器或设备: 操作系统:Windows11 编程环境:Dev-cpp 5.11 三、算法总体设计 (1)项目一:运输公司对用户计算运费 用到的算法的目的:计算并输出基于给…

【实现自己的 kafka!】kafka 的关键概念

kafka 的诞生 现在是在 2000 年代后期,你的名字叫做 Jay Kreps,你就职于 LinkedIn 公司。 LinkedIn 作为社交网络平台,用户规模和数据量现在快速增长,同时内部存在多种数据传递和处理需求,比如用户行为跟踪、日志…

12. 对话框

一、对话框对话框窗口是一个用来完成简单任务或者和用户进行临时交互的顶层窗口,通常用于输入信息、确认信息或者提示信息。Qt Quick 提供了一系列的标准对话框,如 FileDialog、ColorDialog、MessageDialog、FontDia…

2024ICPC区域赛香港站

define时间:#define int long long #define ind long double #define yes cout << "Yes" #define no cout << "No" #define pii pair<long long, long long> #define all(x) (…

AI产品经理要了解的算法有哪些?

中世纪拉丁语“algorismus”指的是用印度数字进行四个基本数学运算——加法,减法,乘法和除法的程序和捷径。后来,术语“算法”被人们用作表示任何逐步的逻辑过程,并成为计算逻辑的核心。 算法的历史可以分为三个阶…

一位印度小哥逆袭成为谷歌数据科学家的心路历程 - 教程

一位印度小哥逆袭成为谷歌数据科学家的心路历程 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&q…

基于selenium的网页自动搜索

第一节 通过简单的百度网页打开学习selenium库的基本功能。1 from selenium import webdriver2 from selenium.webdriver.chrome.service import Service3 from selenium.webdriver.chrome.options import Options4 fr…

MacOS Nginx

查看是否安装:brew info nginx 安装:brew install nginx 卸载:brew uninstall nginx 查看版本:nginx -v 安装目录:/opt/homebrew/Cellar/nginx/1.29.0 (27 files, 2.5MB) 根目录:Docroot is: /opt/homebrew/var/…

缓存的击穿、雪崩、穿透在你项目中的场景是什么

在我们的 OJ 平台中,为了保护数据库、提升响应速度,我设计了一套缓存防护体系: 缓存穿透: 针对恶意请求或不存在的题目 ID,我们用布隆过滤器提前过滤掉无效请求,误判率控制在 0.13% 以下,保护数据库不被大量无效…

[WC2021] 表达式求值

给定一个式子,包含 >,<,? 或者 \([0,m)\) 中的一个数字。其中每个数字代表一个数。 > 代表返回两边的最大值,< 代表返回两边的最小值,? 表示你要在上文的两个符号中选择一个符号替换它。 假设有 \(…

Set集合

无索引 Hashset主注意: LinkedHashset: 存取有顺序其余和hashset一样

JAVA - LinkedList 与 ArrayList 区别和 LinkedList 的四大接口解析

什么是 LinkedListLinkedList 就像一个火车车厢队列。每个“车厢”里装着一个数据(元素),而且每个车厢都知道:自己前面是哪节车厢(previous),自己后面是哪节车厢(next),所以它是一种 “链式结构”。 不像 Ar…

苍穹外卖第三天(Swagger、@RequestParam和@RequestBody的使用场景、@PostMapping和@RequestMapping的区别、对象属性拷贝、@Insert注解)

一、Swagger Swagger是一个用于生成、描述、文档化可视化API的工具(框架)。直接使用Swagger会比较繁琐,所以我们用到了Knife4j框架,它对Swagger进行了封装,简化了相应的操作。 1、Knife4j的使用方式: (1)导入K…

Git 多账号管理

# 新建空白文件夹 mkdir <YOUR PROJECT> # 初始化仓库 git init # 配置当前仓库账号 git config user.name "<YOUR NAME>" git config user.email "<YOUR EMAIL>" # 给当前账号…

完整教程:一文读懂费用分析:定义、分类与成本费用区别

完整教程:一文读懂费用分析:定义、分类与成本费用区别2025-10-10 21:48 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; …

Hyper Server 2019安装I226-V网卡驱动

背景:Hyper-V Server 2019 安装完提示找不到活动的网络适配器 网卡型号:Intel I226-V 以下内容以Hyper-V Server 2019和Intel I226-V网卡为例,其他系统版本(NT6以上)和同系列网卡,操作大同小异,可参考进行。 由…

P10201 永恒

rt好题。 对于一次询问 \((x_1,y_1)\) 到 \((x_2,y_2)\),显然若两点不在同一个联通块中则无解。考虑在同一个联通块中的答案。 我们对整张图进行黑白染色。则有结论:若黑色/白色格点存在不同的数,则一定有解。 证明…

CF1209H tj

zd 讲的啥玩意啊。 题意 你初始的能量为 \(0\),每秒会回复一点能量,同时你每秒可以花 \(x\in [0,2]\) 点能量行走 \(x\) 的距离。 同时地上会有 \(n\) 个传送带,传送带的基础速度为 \(s_i\),求从 \(0\) 走到 \(L\)…

AirBattery - 在Mac上实时监控所有苹果设备电量

AirBattery是一款专为Mac设计的电池监控工具,能够实时显示iPhone、iPad、Apple Watch等苹果设备的电量信息,支持Dock栏、状态栏和小组件显示,让您随时掌握所有设备的电池状态。项目简介 AirBattery 是一款功能强大的…