构建多链资产追踪器:Node.js与React实现链上资产聚合与估值

news2026/5/18 11:11:11
1. 项目概述一个链上资产追踪器的诞生最近在整理自己的数字资产时发现了一个挺普遍但有点烦人的问题当你在不同的区块链网络比如以太坊、BSC、Polygon上持有多种代币Token和NFT时很难有一个统一的视角去实时掌握它们的总价值、交易动态和分布情况。要么得一个个打开不同的区块浏览器要么依赖的中心化交易所或钱包App提供的资产总览功能又不够精细特别是对于DeFi协议里的质押资产、流动性池份额追踪起来更是麻烦。于是我就动手写了一个叫TokenTracker的工具。它的核心目标很简单聚合你在多个区块链地址上的所有链上资产提供一个清晰、实时、可定制的资产仪表盘。你可以把它理解为一个自托管的、功能更强的“区块浏览器资产页面”聚合器。它不管理你的私钥只通过公开的区块链API读取数据所以安全性上你完全不用担心。这个项目特别适合那些积极参与多链DeFi、频繁进行链上交易或者单纯想更清晰管理自己加密资产的朋友。2. 核心需求与设计思路拆解在动手编码之前我花了些时间梳理了核心需求和对应的技术选型。一个资产追踪器听起来简单但拆开来看涉及到的环节不少。2.1 核心需求解析多链支持这是基石。不能只支持一条链。初期我瞄准了以太坊主网、BNB Smart Chain (BSC) 和 Polygon因为它们生态丰富、用户量大。设计上必须考虑良好的扩展性未来能方便地添加Avalanche、Arbitrum等新链。资产类型全覆盖不仅要能识别和查询标准的ERC-20代币余额还要能处理ERC-721和ERC-1155标准的NFT。对于NFT除了数量最好还能拉取元数据如图片、名称、属性并展示。实时定价与估值知道有多少个代币没用关键是要知道它值多少钱。这就需要接入可靠的价格预言机将代币余额乘以实时单价计算出法币如USD总价值。交易历史查询资产变动需要追溯。需要能查询指定地址的历史交易记录、代币转账记录并按时间、类型等进行筛选。友好的用户界面需要一个清晰直观的Web界面来展示所有信息。仪表盘应该包含总资产概览、各链资产分布、代币列表含价格、涨跌幅、NFT画廊以及交易历史列表。数据缓存与性能频繁地直接调用区块链RPC节点或第三方API可能会导致速率限制且速度慢。需要引入缓存层来存储不常变动的数据如代币信息、NFT元数据并优化频繁查询如余额、价格的更新策略。2.2 技术栈选型与考量基于以上需求我选择了以下技术栈并说说为什么这么选后端框架Node.js Express。JavaScript生态在区块链领域有强大的工具库支持如ethers.js、web3.js且开发效率高适合快速构建API服务。Express轻量且灵活。区块链交互库ethers.js v6。相比web3.jsethers.js的API设计更现代、清晰文档也更好。v6版本在Tree Shaking和模块化方面做得更出色有助于减小构建体积。数据获取与缓存区块链数据使用Infura以太坊、BSC Public RPC和Polygon Public RPC作为主要数据源。对于生产环境建议使用付费套餐以获得更高的速率限制和可靠性。价格数据使用CoinGecko API。它免费层额度充足支持的代币和链非常全面是很多项目的首选。缓存使用Redis。将代币信息、NFT元数据、以及短时间内的余额和价格结果缓存起来设置合理的TTL能极大减轻对上游服务的压力提升响应速度。前端框架React Vite TypeScript。React组件化非常适合构建复杂的仪表盘界面。Vite作为构建工具开发体验极佳。TypeScript能显著提升代码的可维护性和开发体验尤其是在处理复杂的区块链数据时。UI库Ant Design (antd)。它提供了丰富、高质量的企业级React组件能快速搭建出专业美观的界面让我更专注于业务逻辑而非样式细节。数据库可选PostgreSQL。主要用于存储用户自定义的观察地址列表、偏好设置等。如果只是个人使用这部分可以简化甚至用配置文件代替。但考虑到未来可能的多用户支持提前设计是值得的。注意技术选型没有绝对的对错关键是匹配项目阶段和团队熟悉度。例如如果你更熟悉Python用web3.py和FastAPI来构建后端也完全可行。3. 核心模块实现细节整个项目我分成了几个核心模块来逐步实现。3.1 多链RPC客户端管理这是与区块链对话的起点。我们不能把不同链的RPC URL硬编码在业务逻辑里。我创建了一个BlockchainService类它根据链IDchainId来管理和提供对应的ethers.js Provider实例。// services/blockchainService.js import { ethers } from ethers; const CHAIN_CONFIGS { 1: { // 以太坊主网 name: Ethereum, rpcUrl: process.env.ETH_RPC_URL || https://mainnet.infura.io/v3/YOUR_KEY, explorer: https://etherscan.io, nativeCurrency: { symbol: ETH, decimals: 18 } }, 56: { // BSC主网 name: BNB Smart Chain, rpcUrl: process.env.BSC_RPC_URL || https://bsc-dataseed.binance.org/, explorer: https://bscscan.com, nativeCurrency: { symbol: BNB, decimals: 18 } }, 137: { // Polygon主网 name: Polygon, rpcUrl: process.env.POLYGON_RPC_URL || https://polygon-rpc.com, explorer: https://polygonscan.com, nativeCurrency: { symbol: MATIC, decimals: 18 } } }; class BlockchainService { constructor() { this.providers {}; this.initProviders(); } initProviders() { for (const [chainId, config] of Object.entries(CHAIN_CONFIGS)) { // 使用JsonRpcProvider连接 this.providers[chainId] new ethers.JsonRpcProvider(config.rpcUrl); // 可以在这里绑定一些链的元信息 this.providers[chainId]._chainConfig config; } } getProvider(chainId) { const provider this.providers[chainId]; if (!provider) { throw new Error(Unsupported chainId: ${chainId}); } return provider; } getChainConfig(chainId) { return CHAIN_CONFIGS[chainId]; } // 一个便捷方法获取所有支持的链 getAllSupportedChains() { return Object.values(CHAIN_CONFIGS); } } export default new BlockchainService(); // 导出单例这样在业务代码中我只需要import blockchainService然后通过blockchainService.getProvider(56)就能拿到BSC的Provider非常清晰。3.2 资产余额获取代币与NFT这是最核心的功能之一。对于一个给定的地址我们需要获取其原生币如ETH、BNB余额和所有代币余额。1. 原生币余额这个最简单直接用Provider的getBalance方法。async getNativeBalance(address, chainId) { const provider this.getProvider(chainId); const balanceWei await provider.getBalance(address); // 转换为可读的单位如ETH const balance ethers.formatEther(balanceWei); return { balance, symbol: this.getChainConfig(chainId).nativeCurrency.symbol }; }2. ERC-20代币余额这里有个问题我们怎么知道一个地址持有哪些代币区块链上没有直接的“查询地址所有代币”的接口。通常有两种策略预定义代币列表维护一个各链上主流、常见代币的合约地址列表然后批量查询余额。这种方式快但会遗漏用户持有的长尾代币。扫描交易记录通过区块浏览器API如Etherscan获取地址所有的代币转账事件从中提取出出现过的代币合约地址集合。这种方式全面但API有调用限制且速度慢。我采用了混合策略首先查询一个主流代币列表的余额确保覆盖主流资产同时提供一个“扫描”功能当用户手动触发时去调用区块浏览器的API来补充可能遗漏的代币。查询单个代币余额需要用到代币合约的ABI。// 简化的ERC-20 ABI只需要balanceOf和decimals函数 const ERC20_ABI [ function balanceOf(address owner) view returns (uint256), function decimals() view returns (uint8), function symbol() view returns (string) ]; async getTokenBalances(address, chainId, tokenAddresses) { const provider this.getProvider(chainId); const balancePromises tokenAddresses.map(async (tokenAddr) { try { const contract new ethers.Contract(tokenAddr, ERC20_ABI, provider); // 并行查询余额、小数位和符号 const [balanceWei, decimals, symbol] await Promise.all([ contract.balanceOf(address), contract.decimals(), contract.symbol() ]); const balance ethers.formatUnits(balanceWei, decimals); return { contractAddress: tokenAddr, symbol, balance, decimals, balanceWei: balanceWei.toString() }; } catch (error) { console.error(Error fetching token ${tokenAddr} on chain ${chainId}:, error); // 对于出错的代币返回null后续过滤掉 return null; } }); const results await Promise.all(balancePromises); // 过滤掉查询失败合约可能不是ERC20或已废弃和余额为0的项 return results.filter(item item ! null item.balanceWei ! 0); }3. NFT余额获取流程类似但更复杂。需要区分ERC-721和ERC-1155。对于ERC-721一个合约地址下可能有多个tokenId需要查询balanceOf和tokenOfOwnerByIndex来枚举。对于ERC-1155则需要查询balanceOf并可能涉及批量查询。同样获取NFT的元数据图片、名称需要调用tokenURI然后去解析可能是IPFS链接、HTTP链接或Base64编码的数据。这部分代码量较大核心是处理好异步枚举和元数据获取的缓存因为tokenURI调用和HTTP获取可能很慢。我通常会为NFT的元数据设置一个较长的Redis缓存时间比如24小时。实操心得在批量查询代币或NFT余额时一定要做好错误处理try...catch和并行优化Promise.all。有些代币合约可能不标准比如decimals()函数缺失或者RPC节点暂时无响应。不能让一个失败的请求导致整个批量查询挂掉。此外将余额为0的资产过滤掉能让前端展示更清晰。3.3 价格获取与资产估值获取到余额后下一步就是估值。这里我选择了CoinGecko API。1. 代币价格映射CoinGecko API需要每个代币的id如ethereum来查询价格。但我们的资产列表里只有合约地址和符号。因此我们需要一个从“链合约地址”到CoinGecko代币ID的映射表。CoinGecko提供了/api/v3/coins/list?include_platformtrue接口可以获取所有代币及其在各链上的合约地址信息。我写了一个脚本定期比如每天运行将这个列表拉取下来并存储到Redis或数据库中构建一个快速的查找字典。2. 批量查询价格CoinGecko的/api/v3/simple/price接口支持通过代币ID批量查询价格。我们需要将资产列表中的代币转换成对应的ID然后一次性查询。// services/priceService.js import axios from axios; import redisClient from ../cache/redisClient.js; // 假设的Redis客户端 class PriceService { constructor() { this.coingeckoBaseUrl https://api.coingecko.com/api/v3; } async getTokenPrices(tokenGeckoIds) { const cacheKey prices:${tokenGeckoIds.sort().join(,)}; // 尝试从缓存读取缓存5分钟 const cached await redisClient.get(cacheKey); if (cached) { return JSON.parse(cached); } try { const response await axios.get(${this.coingeckoBaseUrl}/simple/price, { params: { ids: tokenGeckoIds.join(,), vs_currencies: usd, include_market_cap: false, include_24hr_vol: false, include_24hr_change: true, // 包含24小时涨跌幅 include_last_updated_at: true } }); const prices response.data; // 存入缓存TTL设为300秒5分钟 await redisClient.setex(cacheKey, 300, JSON.stringify(prices)); return prices; } catch (error) { console.error(Error fetching prices from CoinGecko:, error); // 如果API失败可以尝试返回缓存中可能存在的旧数据或者抛出错误 throw new Error(Price service unavailable); } } // 根据合约地址查找CoinGecko ID需要预加载的映射表 async findGeckoId(chainId, contractAddress) { const mapKey coingecko_map:${chainId}:${contractAddress.toLowerCase()}; return await redisClient.get(mapKey); } }3. 计算总价值有了余额和单价计算就简单了总价值 余额 * 单价。需要特别注意小数位的处理避免JavaScript浮点数精度问题。我通常会使用BigNumber库ethers.js自带来进行精确的数学运算。import { ethers } from ethers; function calculateValue(balance, price, decimals) { // balance是格式化后的字符串如“10.5” // price是数字如2500.50 // 先将balance字符串根据其decimals转换回最小单位wei的BigNumber // 但更简单的是直接用decimal.js或直接计算 // 这里简单处理注意精度损失风险 const numericBalance parseFloat(balance); if (isNaN(numericBalance) || !price) return 0; return numericBalance * price; }对于生产环境建议使用decimal.js或ethers.formatUnits配合BigNumber进行更精确的计算。4. 后端API设计与数据聚合后端的主要职责是提供一个清晰的API接收前端传来的地址列表然后并行查询各链资产聚合数据后返回。4.1 核心聚合API端点我设计了一个主要的POST /api/portfolio端点。请求体:{ addresses: [ {chainId: 1, address: 0x...}, {chainId: 56, address: 0x...} ] }后端处理流程:参数校验检查地址格式和链ID是否支持。并行查询对每个(chainId, address)对并行执行调用BlockchainService获取原生币余额。调用预定义的代币列表和可能的扫描逻辑获取ERC-20代币列表及余额。调用NFT查询逻辑获取NFT列表及元数据。将所有代币包括原生币的标识符合约地址或coingecko_id收集起来。批量获取价格将上一步收集的所有代币标识符去重后调用PriceService批量获取实时价格。数据聚合与计算将价格映射回每个代币资产。计算每个代币的美元价值。按链、按资产类型分类汇总。计算总资产价值。返回响应将结构化的数据返回给前端。// 响应结构示意 { totalValueUSD: 125430.50, chains: [ { chainId: 1, chainName: Ethereum, valueUSD: 85430.20, nativeAsset: { symbol: ETH, balance: 3.2, valueUSD: 9600.00, ... }, tokens: [ ... ], // ERC20代币列表 nfts: [ ... ] // NFT列表 }, { chainId: 56, chainName: BSC, valueUSD: 40000.30, nativeAsset: { ... }, tokens: [ ... ], nfts: [] } ], updatedAt: 2023-10-27T10:30:00Z }4.2 性能优化策略这个聚合查询涉及大量外部API调用如果不加优化响应时间会很长。Redis缓存层层递进代币元数据符号、小数位缓存时间较长24小时因为很少变化。NFT元数据JSON、图片链接缓存24小时。价格数据缓存5分钟。对于资产总览5分钟内的价格波动通常可以接受。余额数据谨慎缓存。因为余额变化频繁。可以考虑为余额设置一个非常短的缓存比如15-30秒或者不缓存但对于不活跃地址可以适当缓存几分钟。聚合结果可以为特定的地址列表组合生成一个缓存键设置短时间缓存如30秒适合用户频繁刷新。并行与异步大量使用Promise.all来并行执行独立的网络请求如同时查询多个链、查询多个代币的余额。超时与重试为每个外部API调用RPC、CoinGecko设置合理的超时时间并实现简单的重试逻辑特别是对于偶发的RPC节点无响应。分页与懒加载对于NFT数量特别多的地址在API响应中可以先只返回总数和前几个前端通过额外的请求来分页加载详情。交易历史记录也必须分页查询。5. 前端仪表盘构建前端的目标是将后端聚合的数据清晰、直观地呈现出来。5.1 技术实现要点状态管理使用React Context或Zustand这样的轻量级状态库来管理全局状态如当前查询的地址列表、资产数据、加载状态等。数据获取使用axios或fetch调用后端API结合React Query或SWR库来管理服务端状态、缓存、轮询和错误处理。这能极大简化数据同步的逻辑。UI布局顶部概览用大的数字卡片展示总资产价值、24小时变化、各链价值分布可以用饼图或环形图使用recharts或victory库。资产列表用表格展示所有代币列包括资产图标从CoinGecko或缓存获取、名称/符号、持仓量、单价、持仓价值、24小时涨跌幅、操作如跳转到区块浏览器。NFT画廊用网格Masonry Grid布局展示NFT每张卡片显示图片、名称、编号。图片加载需要处理错误情况显示占位图。交易历史可折叠或独立页签表格展示交易哈希、时间、类型发送/接收、对方地址、金额、状态。5.2 关键交互与体验优化地址管理提供输入框让用户添加新的链上地址并本地存储localStorage或同步到后端数据库如果登录了。支持给地址打标签如“我的主钱包”、“DeFi专用”。自动刷新提供手动刷新按钮也可以设置自动刷新间隔如每60秒。自动刷新时只重新获取余额和价格不重新获取元数据等静态信息。资产筛选与排序用户可以根据链、资产类型代币/NFT、价值大小等进行筛选和排序。错误边界与加载状态每个数据区域都要有良好的加载中Skeleton Screen和错误提示状态。网络请求失败后提供重试按钮。响应式设计确保在手机和桌面端都有良好的浏览体验。6. 部署与运维考量开发完成后要让它能稳定运行。环境变量所有敏感信息Infura项目ID、CoinGecko API Key、Redis连接字符串、数据库密码必须通过环境变量配置绝不能硬编码在代码中。进程管理使用pm2或Docker来管理Node.js进程确保服务崩溃后能自动重启。日志记录使用winston或pino等日志库记录详细的请求日志、错误日志和性能日志方便排查问题。监控与告警监控服务器的CPU、内存、磁盘。监控API的响应时间和错误率。可以设置简单的健康检查端点/health。安全使用helmet中间件设置安全的HTTP头。对用户输入的地址进行严格格式校验防止注入攻击。如果开放给多用户使用需要考虑API速率限制、身份认证和授权。容器化可选但推荐使用Docker将应用、Redis、数据库等容器化用docker-compose编排能极大简化部署和环境一致性问题。7. 遇到的典型问题与解决方案在开发过程中踩了不少坑这里记录几个典型的问题一RPC节点速率限制与稳定性现象批量查询代币余额时偶尔会出现超时或“rate limit”错误导致整个查询失败或部分数据缺失。解决使用付费节点对于主网Infura、Alchemy的付费套餐是必须的它们提供更高的调用频率和稳定性。实现请求队列与限流自己实现一个简单的队列控制对同一个RPC节点的并发请求数并在请求失败时加入指数退避重试。多节点故障转移配置多个RPC节点URL当主节点失败时自动切换到备用节点。更激进的缓存对于不活跃的观察地址其持有的代币列表变化不频繁可以将“地址持有哪些代币合约”这个列表也缓存起来比如1小时减少不必要的balanceOf调用。问题二CoinGecko API的调用频率限制现象免费版每分钟30次调用如果用户观察地址多、资产种类多很容易超限。解决批量查询是王道一定要用/simple/price接口的批量查询功能将几十个代币ID一次查询。价格缓存这是最关键的措施。将价格数据缓存至少5分钟。大多数用户刷新仪表盘5分钟内的价格变化是可以接受的。考虑备用数据源可以集成多个价格预言机如1inch的Token Price API、Chainlink的喂价对于主流币作为CoinGecko的备份或补充。问题三NFT元数据获取慢且格式不一现象tokenURI返回的可能是IPFS网关链接、HTTP链接或直接内嵌的Base64数据。IPFS网关可能很慢有些HTTP链接甚至已经失效。元数据JSON格式也并非完全标准。解决异步获取与缓存在前端展示时不要阻塞主要资产数据的渲染。可以先显示NFT的占位符和tokenId在后台异步加载图片。服务端将获取到的元数据尤其是图片URL持久化缓存。IPFS网关优选如果检测到是IPFS链接ipfs://可以将其转换为多个公共网关如Cloudflare的、官方的进行尝试选择最快的。超时与降级设置合理的获取超时如3秒如果超时或失败则显示一个通用的NFT图标并记录错误日志。问题四前端渲染大量NFT图片导致卡顿现象一个地址可能有成千上万个NFT一次性渲染所有图片会导致浏览器内存和CPU占用极高页面卡死。解决虚拟滚动使用react-window或react-virtualized库只渲染可视区域内的NFT项。分页加载后端API对NFT列表进行分页前端每次只加载一页比如50个。图片懒加载使用loading”lazy”属性或Intersection Observer API让图片只在进入视口时才加载。8. 项目总结与扩展思考构建TokenTracker的过程是一个典型的全栈项目实践涉及了区块链交互、第三方API集成、前后端分离、性能优化等多个方面。它从一个具体的需求痛点出发通过合理的架构设计和一步步的编码实现最终形成了一个能解决实际问题的工具。这个项目还有很大的扩展空间支持更多链按照现有的模式添加新的链如Arbitrum, Optimism, Avalanche主要是配置RPC和区块浏览器信息。DeFi头寸集成更进阶的功能是解析用户在Uniswap、Aave、Compound等DeFi协议中的头寸例如流动性池份额、抵押借贷的头寸等。这需要与协议的智能合约进行更复杂的交互解析特定的事件日志。价格预警可以为持有的特定代币设置价格预警当价格达到某个阈值时发送通知邮件、Telegram等。多账户与分享支持用户管理多个地址组合并生成一个只读的资产概览链接分享给他人。本地化部署提供Docker一键部署脚本让用户可以在自己的服务器或甚至本地机器上运行彻底掌控数据隐私。我个人最大的体会是在区块链开发中错误处理、缓存策略和性能优化的重要性丝毫不亚于业务逻辑本身。网络的不确定性、外部服务的限流、数据的海量这些都是在设计之初就必须充分考虑的问题。从0到1做出一个能稳定运行的工具带来的成就感远超单纯调用一个现成的API。如果你也对链上数据感兴趣不妨从这样一个项目开始动手过程中遇到的每一个问题都会让你对Web3的技术栈有更深的理解。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…