Swagger Codegen Maven插件深度实践:从API规范到生产级SDK的完整解决方案
Swagger Codegen Maven插件深度实践从API规范到生产级SDK的完整解决方案【免费下载链接】swagger-codegenswagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.项目地址: https://gitcode.com/gh_mirrors/sw/swagger-codegen痛点分析为什么传统API集成如此低效在现代微服务架构中API接口的数量呈指数级增长。传统的手动编写客户端SDK方式面临诸多挑战不同团队间的接口定义不一致、版本管理混乱、文档与实际代码脱节、重复劳动导致的开发效率低下。这些问题不仅增加了开发成本更严重影响了系统的稳定性和可维护性。以典型的电商系统为例订单服务、商品服务、支付服务之间需要频繁通信。每个服务都需要为其他服务提供客户端SDK如果手动实现每个团队需要花费数天时间编写重复的HTTP请求处理代码参数校验、错误处理逻辑难以统一接口变更时需要同步更新所有相关SDK文档维护成本高昂容易与实际代码产生偏差这种模式下API集成成为开发过程中的瓶颈严重制约了团队的交付速度。核心方案Swagger Codegen Maven插件的自动化生成体系自动化代码生成的工作流原理Swagger Codegen Maven插件通过解析OpenAPI规范文件自动生成客户端SDK、服务器存根和API文档。其核心工作流程如下图所示架构解析该图展示了Swagger Codegen在PKMST项目中的完整应用架构。左侧蓝色区域为核心代码生成模块基于Mustache模板引擎生成服务端代码右侧绿色区域为功能扩展模块集成了Spring Cloud生态、安全认证、监控追踪等企业级特性。这种架构设计体现了Swagger Codegen的高度可扩展性。基础配置五分钟快速上手让我们从一个简单的Python客户端生成开始体验Swagger Codegen的高效plugin groupIdio.swagger/groupId artifactIdswagger-codegen-maven-plugin/artifactId version2.3.1/version executions execution goals goalgenerate/goal /goals configuration inputSpec${project.basedir}/api/openapi.yaml/inputSpec languagepython/language configOptions packageNameclient_sdk/packageName projectNameecommerce-api-client/projectName packageVersion1.0.0/packageVersion /configOptions /configuration /execution /executions /plugin关键参数说明inputSpec指定OpenAPI规范文件路径支持YAML和JSON格式language目标生成语言支持Java、Python、Go、TypeScript等40语言configOptions语言特定的配置选项可定制包名、项目名等实用提示建议将OpenAPI规范文件放在src/main/resources/api/目录下便于版本管理和团队协作。实践演练构建企业级Python客户端SDK场景设定电商平台API集成假设我们需要为电商平台的订单服务生成Python客户端SDK该服务包含以下核心接口创建订单POST /orders查询订单详情GET /orders/{id}取消订单DELETE /orders/{id}批量查询订单GET /orders完整配置示例plugin groupIdio.swagger/groupId artifactIdswagger-codegen-maven-plugin/artifactId version2.3.1/version executions execution idgenerate-python-client/id goals goalgenerate/goal /goals configuration inputSpec${project.basedir}/api/ecommerce-openapi.yaml/inputSpec languagepython/language output${project.build.directory}/generated-sources/python/output configOptions packageNameecommerce_client/packageName projectNameecommerce-python-sdk/projectName packageVersion${project.version}/packageVersion generateSourceCodeOnlytrue/generateSourceCodeOnly libraryasyncio/library hideGenerationTimestamptrue/hideGenerationTimestamp /configOptions generateApiTeststrue/generateApiTests generateModelTeststrue/generateModelTests modelPackageecommerce_client.models/modelPackage apiPackageecommerce_client.api/apiPackage /configuration /execution /executions /plugin生成代码结构分析执行mvn clean compile后将生成以下Python代码结构generated-sources/python/ ├── ecommerce_client/ │ ├── __init__.py │ ├── api/ │ │ ├── __init__.py │ │ ├── orders_api.py # 订单相关API接口 │ │ └── api_client.py # HTTP客户端封装 │ ├── models/ │ │ ├── __init__.py │ │ ├── order.py # 订单数据模型 │ │ └── order_item.py # 订单项数据模型 │ └── configuration.py # 客户端配置 ├── test/ │ ├── test_orders_api.py # 自动生成的测试用例 │ └── test_models.py └── requirements.txt # 依赖声明客户端使用示例from ecommerce_client import ApiClient, Configuration from ecommerce_client.api.orders_api import OrdersApi from ecommerce_client.models import Order, OrderItem # 配置客户端 config Configuration() config.host https://api.ecommerce.com/v1 config.api_key {Authorization: Bearer your-token-here} # 创建API实例 client ApiClient(configurationconfig) api_instance OrdersApi(client) # 调用API创建订单 try: order_item OrderItem(product_idprod_123, quantity2) order Order(items[order_item], customer_idcust_456) api_response api_instance.create_order(order) print(f订单创建成功ID: {api_response.id}) except Exception as e: print(f创建订单失败: {e})✅优势体现类型安全的API调用IDE自动补全自动化的参数校验和序列化统一的错误处理机制完整的文档字符串进阶技巧自定义模板与高级配置自定义模板实现业务特定需求当默认生成的代码无法满足特定业务需求时可以通过自定义模板进行深度定制。以下是创建自定义Python客户端的步骤步骤一创建模板目录结构src/main/resources/custom-templates/ └── python/ ├── api.mustache # API接口模板 ├── model.mustache # 数据模型模板 ├── api_client.mustache # HTTP客户端模板 └── configuration.mustache # 配置类模板步骤二修改插件配置configuration inputSpec${project.basedir}/api/ecommerce-openapi.yaml/inputSpec languagepython/language templateDirectory${project.basedir}/src/main/resources/custom-templates/templateDirectory configOptions packageNameecommerce_enterprise_client/packageName withLoggingtrue/withLogging withMetricstrue/withMetrics retryEnabledtrue/retryEnabled circuitBreakerEnabledtrue/circuitBreakerEnabled /configOptions /configuration步骤三自定义模板示例api_client.mustache# -*- coding: utf-8 -*- {{appName}} API客户端 版本: {{packageVersion}} 生成时间: {{generatedDate}} import logging import time from typing import Dict, Optional import httpx from .exceptions import ApiException, RetryableException logger logging.getLogger(__name__) class ApiClient: 增强版API客户端支持重试、熔断、监控 def __init__(self, configurationNone): self.configuration configuration self.client httpx.AsyncClient( timeouthttpx.Timeout(30.0), limitshttpx.Limits(max_connections100, max_keepalive_connections20) ) self.metrics {} self.circuit_breaker_state CLOSED async def call_api(self, resource_path, method, **kwargs): 增强的API调用方法支持重试和熔断 max_retries 3 retry_delay 1.0 for attempt in range(max_retries): try: if self.circuit_breaker_state OPEN: raise ApiException(熔断器已打开暂时停止服务调用) start_time time.time() response await self._make_request(resource_path, method, **kwargs) elapsed_time time.time() - start_time # 记录监控指标 self._record_metrics(resource_path, method, elapsed_time, response.status_code) if response.status_code 500: self._update_circuit_breaker(HALF_OPEN) if attempt max_retries - 1: await asyncio.sleep(retry_delay * (2 ** attempt)) continue self._update_circuit_breaker(CLOSED) return response except (httpx.TimeoutException, httpx.NetworkError) as e: logger.warning(f网络错误第{attempt 1}次重试: {e}) if attempt max_retries - 1: await asyncio.sleep(retry_delay * (2 ** attempt)) else: self._update_circuit_breaker(OPEN) raise RetryableException(f请求失败已重试{max_retries}次) from e def _record_metrics(self, endpoint, method, duration, status_code): 记录API调用指标 key f{method}_{endpoint} if key not in self.metrics: self.metrics[key] { count: 0, total_duration: 0, success_count: 0, error_count: 0 } self.metrics[key][count] 1 self.metrics[key][total_duration] duration if 200 status_code 300: self.metrics[key][success_count] 1 else: self.metrics[key][error_count] 1自定义模板的核心价值集成企业级特性重试机制、熔断器、监控指标统一错误处理和日志记录性能优化连接池管理、超时控制符合团队编码规范和安全要求多环境配置管理在实际项目中通常需要为不同环境生成不同的客户端配置profiles profile iddev/id activation activeByDefaulttrue/activeByDefault /activation properties api.hosthttps://dev-api.ecommerce.com/api.host api.versionv1/api.version /properties /profile profile idstaging/id properties api.hosthttps://staging-api.ecommerce.com/api.host api.versionv1/api.version /properties /profile profile idprod/id properties api.hosthttps://api.ecommerce.com/api.host api.versionv1/api.version /properties /profile /profiles plugin groupIdio.swagger/groupId artifactIdswagger-codegen-maven-plugin/artifactId version2.3.1/version executions execution goals goalgenerate/goal /goals configuration inputSpec${project.basedir}/api/ecommerce-openapi.yaml/inputSpec languagepython/language configOptions packageNameecommerce_client_${env}/packageName basePath${api.host}/${api.version}/basePath withEnvironmentVariablestrue/withEnvironmentVariables /configOptions /configuration /execution /executions /plugin性能优化与安全考量生成代码的性能调优1. 连接池优化配置# 在自定义模板中优化HTTP客户端配置 class OptimizedApiClient: def __init__(self): # 使用连接池减少TCP握手开销 self._client httpx.AsyncClient( limitshttpx.Limits( max_connections100, max_keepalive_connections50, keepalive_expiry30.0 ), timeouthttpx.Timeout( connect5.0, read30.0, write30.0, pool60.0 ) ) # 启用HTTP/2提升性能 async def _enable_http2(self): return httpx.AsyncClient(http2True)2. 序列化优化# 使用orjson替代标准json库提升序列化性能 import orjson class FastJsonEncoder: staticmethod def default(obj): if hasattr(obj, to_dict): return obj.to_dict() raise TypeError(fObject of type {obj.__class__.__name__} is not JSON serializable) staticmethod def dumps(obj): return orjson.dumps(obj, defaultFastJsonEncoder.default).decode()安全增强措施1. API密钥安全管理# 安全配置模板示例 class SecureConfiguration: def __init__(self): self.api_key {} self.api_key_prefix {} self._secrets_manager None def load_secrets_from_vault(self): 从安全的密钥管理服务加载API密钥 # 集成HashiCorp Vault或AWS Secrets Manager pass def rotate_api_key(self): 定期轮换API密钥 pass def validate_api_key(self, key): 验证API密钥格式和权限 if not key or len(key) 32: raise ValueError(API密钥长度不足) # 添加更多验证逻辑2. 请求签名与防重放class SecureRequestBuilder: def __init__(self, api_key, api_secret): self.api_key api_key self.api_secret api_secret def build_secure_request(self, method, path, bodyNone): 构建带签名的安全请求 timestamp str(int(time.time())) nonce str(uuid.uuid4()) # 构建签名字符串 string_to_sign f{method}\n{path}\n{timestamp}\n{nonce} if body: string_to_sign f\n{json.dumps(body, sort_keysTrue)} # 计算HMAC签名 signature hmac.new( self.api_secret.encode(), string_to_sign.encode(), hashlib.sha256 ).hexdigest() headers { X-API-Key: self.api_key, X-Timestamp: timestamp, X-Nonce: nonce, X-Signature: signature } return headers监控与可观测性集成集成Prometheus监控# 监控集成模板 from prometheus_client import Counter, Histogram, generate_latest class MonitoredApiClient: def __init__(self): # 定义监控指标 self.request_counter Counter( api_requests_total, Total API requests, [method, endpoint, status_code] ) self.request_duration Histogram( api_request_duration_seconds, API request duration, [method, endpoint], buckets(0.1, 0.5, 1.0, 2.0, 5.0, 10.0) ) self.error_counter Counter( api_errors_total, Total API errors, [method, endpoint, error_type] ) async def call_api_with_metrics(self, resource_path, method, **kwargs): 带监控的API调用 with self.request_duration.labels(methodmethod, endpointresource_path).time(): try: response await self._make_request(resource_path, method, **kwargs) self.request_counter.labels( methodmethod, endpointresource_path, status_coderesponse.status_code ).inc() return response except Exception as e: self.error_counter.labels( methodmethod, endpointresource_path, error_typetype(e).__name__ ).inc() raise分布式追踪集成# OpenTelemetry追踪集成 from opentelemetry import trace from opentelemetry.trace import Status, StatusCode class TracedApiClient: def __init__(self): self.tracer trace.get_tracer(__name__) async def call_api_with_trace(self, resource_path, method, **kwargs): 带分布式追踪的API调用 with self.tracer.start_as_current_span(fapi.{method}.{resource_path}) as span: span.set_attribute(http.method, method) span.set_attribute(http.url, resource_path) try: response await self._make_request(resource_path, method, **kwargs) span.set_attribute(http.status_code, response.status_code) span.set_status(Status(StatusCode.OK)) return response except Exception as e: span.record_exception(e) span.set_status(Status(StatusCode.ERROR, str(e))) raise最佳实践总结项目结构规范api-client-project/ ├── api/ │ └── openapi.yaml # OpenAPI规范文件 ├── src/ │ └── main/ │ ├── resources/ │ │ └── templates/ # 自定义模板目录 │ └── java/ │ └── com/ │ └── example/ │ └── generator/ # 自定义生成器代码 ├── generated-sources/ # 生成的客户端代码 ├── .swagger-codegen-ignore # 忽略文件配置 └── pom.xml # Maven配置版本管理策略API版本与客户端版本同步客户端SDK版本号应与API版本保持一致向后兼容性保证新增字段使用optional删除字段需谨慎多版本支持通过Maven Profile支持不同API版本持续集成流水线# GitHub Actions配置示例 name: Generate and Publish SDK on: push: branches: [ main ] pull_request: branches: [ main ] jobs: generate-sdk: runs-on: ubuntu-latest steps: - uses: actions/checkoutv2 - name: Set up JDK uses: actions/setup-javav2 with: java-version: 11 - name: Generate Python SDK run: mvn clean compile -Ppython-client - name: Run SDK tests run: cd generated-sources/python python -m pytest - name: Publish to PyPI if: github.ref refs/heads/main run: | cd generated-sources/python python -m pip install --upgrade twine python -m twine upload dist/*延伸阅读与进阶探索源码深度分析要进一步理解Swagger Codegen的内部机制建议阅读以下核心源码代码生成引擎modules/swagger-codegen/src/main/java/io/swagger/codegen/DefaultGenerator.java模板处理机制modules/swagger-codegen/src/main/java/io/swagger/codegen/templating/MustacheEngineAdapter.java语言特定生成器modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/目录下的各语言实现性能优化建议模板缓存启用Mustache模板缓存减少IO操作增量生成使用.swagger-codegen-ignore文件避免重复生成并行生成对于大型API规范考虑分模块并行生成安全最佳实践API密钥管理使用环境变量或密钥管理服务避免硬编码输入验证在生成代码中自动添加参数验证逻辑HTTPS强制确保所有生成的客户端默认使用HTTPS监控告警配置关键指标监控API调用成功率、响应时间、错误率容量规划基于监控数据预测资源需求自动扩缩容根据负载自动调整客户端连接池大小结语自动化API集成的未来展望Swagger Codegen Maven插件不仅仅是一个代码生成工具更是现代API驱动开发范式的核心组件。通过本文介绍的深度实践我们可以看到 效率提升从数天的手工编码缩短到几分钟的自动生成 质量保障统一的代码风格、完整的错误处理、内置的安全特性 可扩展性自定义模板和生成器支持无限的业务定制 可观测性集成的监控和追踪能力提供全面的运维支持随着云原生和微服务架构的普及API作为服务间通信的标准接口其重要性日益凸显。Swagger Codegen Maven插件通过自动化代码生成不仅解决了API集成的技术难题更推动了团队协作模式的革新。未来我们可以期待更多高级特性的集成如AI辅助的代码优化、智能的API版本管理、以及更强大的生态集成能力。但无论技术如何演进自动化、标准化、可维护性始终是API集成领域的核心追求。实践建议从今天开始将Swagger Codegen Maven插件纳入你的技术栈体验自动化API集成带来的效率革命。从简单的客户端生成开始逐步探索自定义模板、监控集成等高级特性构建属于你的高效API生态系统。【免费下载链接】swagger-codegenswagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.项目地址: https://gitcode.com/gh_mirrors/sw/swagger-codegen创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

终极Spine骨骼动画整合指南:5分钟让Godot游戏角色动起来!

终极Spine骨骼动画整合指南:5分钟让Godot游戏角色动起来!

终极Spine骨骼动画整合指南:5分钟让Godot游戏角色动起来! 【免费下载链接】spine-runtime-for-godot This project is a module for godot that allows it to load/play Spine skeleton animation. 项目地址: https://gitcode.com/gh_mirrors/sp/spine…

2026/10/14 6:04:32 阅读更多 →
Redis 缓存淘汰策略

Redis 缓存淘汰策略

Redis 缓存淘汰策略是当内存使用达到上限时,Redis 自动清理部分数据以腾出空间的核心机制,官方共定义了 ‌8 种主流策略‌,分为两大类别: 一、8种淘汰策略分类 针对所有键的策略‌ noeviction(默认)‌&…

2026/10/10 18:01:28 阅读更多 →
MinecraftForge模组开发终极指南:从入门到精通的高效开发实践

MinecraftForge模组开发终极指南:从入门到精通的高效开发实践

MinecraftForge模组开发终极指南:从入门到精通的高效开发实践 【免费下载链接】MinecraftForge Modifications to the Minecraft base files to assist in compatibility between mods. New Discord: https://discord.minecraftforge.net/ 项目地址: https://gitc…

2026/9/26 19:06:53 阅读更多 →

最新新闻

一条富碱性多肽的进阶之路:序列解析、合成纯化与核定位验证

一条富碱性多肽的进阶之路:序列解析、合成纯化与核定位验证

1. 从一条氨基酸序列反推蛋白身份:这条肽的“户口本”是这样查的拿到 PDGF A-Chain (194-211) 这个编号,加上后面那一串 GRPREGSKKRKRKRLKPT,我的第一反应不是急着做实验,而是先把它当成一个“嫌疑人”来审:它到底来自…

2026/10/14 6:04:23 阅读更多 →
Spring Boot微服务架构设计与实战指南

Spring Boot微服务架构设计与实战指南

1. 为什么Spring Boot成为微服务首选框架十年前我刚接触企业级应用开发时,EJB的复杂配置让人望而生畏。直到Spring Boot出现,用约定优于配置的理念彻底改变了Java开发方式。现在回想第一次用Spring Boot启动项目的场景:没有XML配置&#xff0…

2026/10/14 6:04:23 阅读更多 →
bl knowledge search 报 400 RagAgentGroupNotExist 怎么解决?阿里云百炼知识库检索与 agent 记忆节点使用步骤与常见报错排查

bl knowledge search 报 400 RagAgentGroupNotExist 怎么解决?阿里云百炼知识库检索与 agent 记忆节点使用步骤与常见报错排查

给 agent 补知识,阿里云百炼的 CLI(bl)里有两条路:bl knowledge 建知识库、包一层检索服务让它查;bl memory 按用户记节点。两条路的 ID 体系不一样,混用会撞上一个措辞很像服务端故障的 HTTP 400。 这篇按…

2026/10/14 6:04:23 阅读更多 →
微电网群协同调度中的目标级联法原理与Matlab实现

微电网群协同调度中的目标级联法原理与Matlab实现

1. 项目背景与核心价值微电网群协同调度是当前分布式能源领域的热点研究方向。传统集中式优化方法在面对多主体、高维度的微网群系统时,往往面临通信压力大、隐私保护难、计算复杂度高等问题。目标级联法(Analytical Target Cascading, ATC)通…

2026/10/14 6:04:23 阅读更多 →
汽车线束开发中的BOM图纸与检验记录管理

汽车线束开发中的BOM图纸与检验记录管理

汽车线束从设计到样品、再到量产,BOM材料清单、图纸和检验记录需要保持关联。连接器替换或支路调整后,如果相关文件没有同步更新,资料评审就可能遗漏要求。建立清晰的版本和关联关系,有助于工程与采购对齐信息。首先,为…

2026/10/14 6:04:23 阅读更多 →
PS5进阶玩家手册:系统设置、SSD扩容、网络调优与串流实战

PS5进阶玩家手册:系统设置、SSD扩容、网络调优与串流实战

接手的这台“AnyPS5”时,我第一反应是:这名字起得挺大。光是PS5相关的外设、存储、网络、串流、保养,随便列一列就够写好几篇长文。后来想明白了,所谓“Any PS5”,并不是什么第三方工具或改装固件,而是玩家…

2026/10/14 6:03:23 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →