[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6D80E6MNVHCXFD2H85BN15D":3,"skill-same:01M6D80E6MNVHCXFD2H85BN15D":58},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":29,"linkOnly":30,"markdownContent":31,"markdownBody":32,"frontMatter":33,"refName":35,"commitSha":36,"skillDirectory":37,"forksCount":38,"repositoryHtmlUrl":39,"repositoryHomepageUrl":6,"ownerHtmlUrl":40,"ownerBlogUrl":41,"ownerDescription":42,"updatedAt":43,"install":44,"resources":57},"01M6D80E6MNVHCXFD2H85BN15D","springboot-patterns",null,"Spring Boot架构模式、REST API设计、分层服务、数据访问、缓存、异步处理和日志记录。用于Java Spring Boot后端工作。","oabdelmaksoud\u002FSuperClaw","oabdelmaksoud","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F50699149?v=4","01M6D5CVVN778ZCDP1WHS816SP","Omar Abdelmaksoud","01M6D5CW1RHNSKCJR85GJACBFT",2,"MIT","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FSuperClaw\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-CN\u002Fskills\u002Fspringboot-patterns\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",[24,25,26,27,28],"springboot patterns","patterns","design","REST","spring","2026-11-10 10:12:30",false,"---\nname: springboot-patterns\ndescription: Spring Boot架构模式、REST API设计、分层服务、数据访问、缓存、异步处理和日志记录。用于Java Spring Boot后端工作。\norigin: ECC\n---\n\n# Spring Boot 开发模式\n\n用于可扩展、生产级服务的 Spring Boot 架构和 API 模式。\n\n## 何时激活\n\n* 使用 Spring MVC 或 WebFlux 构建 REST API\n* 构建控制器 → 服务 → 仓库层结构\n* 配置 Spring Data JPA、缓存或异步处理\n* 添加验证、异常处理或分页\n* 为开发\u002F预发布\u002F生产环境设置配置文件\n* 使用 Spring Events 或 Kafka 实现事件驱动模式\n\n## REST API 结构\n\n```java\n@RestController\n@RequestMapping(\"\u002Fapi\u002Fmarkets\")\n@Validated\nclass MarketController {\n  private final MarketService marketService;\n\n  MarketController(MarketService marketService) {\n    this.marketService = marketService;\n  }\n\n  @GetMapping\n  ResponseEntity\u003CPage\u003CMarketResponse>> list(\n      @RequestParam(defaultValue = \"0\") int page,\n      @RequestParam(defaultValue = \"20\") int size) {\n    Page\u003CMarket> markets = marketService.list(PageRequest.of(page, size));\n    return ResponseEntity.ok(markets.map(MarketResponse::from));\n  }\n\n  @PostMapping\n  ResponseEntity\u003CMarketResponse> create(@Valid @RequestBody CreateMarketRequest request) {\n    Market market = marketService.create(request);\n    return ResponseEntity.status(HttpStatus.CREATED).body(MarketResponse.from(market));\n  }\n}\n```\n\n## 仓库模式 (Spring Data JPA)\n\n```java\npublic interface MarketRepository extends JpaRepository\u003CMarketEntity, Long> {\n  @Query(\"select m from MarketEntity m where m.status = :status order by m.volume desc\")\n  List\u003CMarketEntity> findActive(@Param(\"status\") MarketStatus status, Pageable pageable);\n}\n```\n\n## 带事务的服务层\n\n```java\n@Service\npublic class MarketService {\n  private final MarketRepository repo;\n\n  public MarketService(MarketRepository repo) {\n    this.repo = repo;\n  }\n\n  @Transactional\n  public Market create(CreateMarketRequest request) {\n    MarketEntity entity = MarketEntity.from(request);\n    MarketEntity saved = repo.save(entity);\n    return Market.from(saved);\n  }\n}\n```\n\n## DTO 和验证\n\n```java\npublic record CreateMarketRequest(\n    @NotBlank @Size(max = 200) String name,\n    @NotBlank @Size(max = 2000) String description,\n    @NotNull @FutureOrPresent Instant endDate,\n    @NotEmpty List\u003C@NotBlank String> categories) {}\n\npublic record MarketResponse(Long id, String name, MarketStatus status) {\n  static MarketResponse from(Market market) {\n    return new MarketResponse(market.id(), market.name(), market.status());\n  }\n}\n```\n\n## 异常处理\n\n```java\n@ControllerAdvice\nclass GlobalExceptionHandler {\n  @ExceptionHandler(MethodArgumentNotValidException.class)\n  ResponseEntity\u003CApiError> handleValidation(MethodArgumentNotValidException ex) {\n    String message = ex.getBindingResult().getFieldErrors().stream()\n        .map(e -> e.getField() + \": \" + e.getDefaultMessage())\n        .collect(Collectors.joining(\", \"));\n    return ResponseEntity.badRequest().body(ApiError.validation(message));\n  }\n\n  @ExceptionHandler(AccessDeniedException.class)\n  ResponseEntity\u003CApiError> handleAccessDenied() {\n    return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ApiError.of(\"Forbidden\"));\n  }\n\n  @ExceptionHandler(Exception.class)\n  ResponseEntity\u003CApiError> handleGeneric(Exception ex) {\n    \u002F\u002F Log unexpected errors with stack traces\n    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)\n        .body(ApiError.of(\"Internal server error\"));\n  }\n}\n```\n\n## 缓存\n\n需要在配置类上使用 `@EnableCaching`。\n\n```java\n@Service\npublic class MarketCacheService {\n  private final MarketRepository repo;\n\n  public MarketCacheService(MarketRepository repo) {\n    this.repo = repo;\n  }\n\n  @Cacheable(value = \"market\", key = \"#id\")\n  public Market getById(Long id) {\n    return repo.findById(id)\n        .map(Market::from)\n        .orElseThrow(() -> new EntityNotFoundException(\"Market not found\"));\n  }\n\n  @CacheEvict(value = \"market\", key = \"#id\")\n  public void evict(Long id) {}\n}\n```\n\n## 异步处理\n\n需要在配置类上使用 `@EnableAsync`。\n\n```java\n@Service\npublic class NotificationService {\n  @Async\n  public CompletableFuture\u003CVoid> sendAsync(Notification notification) {\n    \u002F\u002F send email\u002FSMS\n    return CompletableFuture.completedFuture(null);\n  }\n}\n```\n\n## 日志记录 (SLF4J)\n\n```java\n@Service\npublic class ReportService {\n  private static final Logger log = LoggerFactory.getLogger(ReportService.class);\n\n  public Report generate(Long marketId) {\n    log.info(\"generate_report marketId={}\", marketId);\n    try {\n      \u002F\u002F logic\n    } catch (Exception ex) {\n      log.error(\"generate_report_failed marketId={}\", marketId, ex);\n      throw ex;\n    }\n    return new Report();\n  }\n}\n```\n\n## 中间件 \u002F 过滤器\n\n```java\n@Component\npublic class RequestLoggingFilter extends OncePerRequestFilter {\n  private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);\n\n  @Override\n  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n      FilterChain filterChain) throws ServletException, IOException {\n    long start = System.currentTimeMillis();\n    try {\n      filterChain.doFilter(request, response);\n    } finally {\n      long duration = System.currentTimeMillis() - start;\n      log.info(\"req method={} uri={} status={} durationMs={}\",\n          request.getMethod(), request.getRequestURI(), response.getStatus(), duration);\n    }\n  }\n}\n```\n\n## 分页和排序\n\n```java\nPageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by(\"createdAt\").descending());\nPage\u003CMarket> results = marketService.list(page);\n```\n\n## 容错的外部调用\n\n```java\npublic \u003CT> T withRetry(Supplier\u003CT> supplier, int maxRetries) {\n  int attempts = 0;\n  while (true) {\n    try {\n      return supplier.get();\n    } catch (Exception ex) {\n      attempts++;\n      if (attempts >= maxRetries) {\n        throw ex;\n      }\n      try {\n        Thread.sleep((long) Math.pow(2, attempts) * 100L);\n      } catch (InterruptedException ie) {\n        Thread.currentThread().interrupt();\n        throw ex;\n      }\n    }\n  }\n}\n```\n\n## 速率限制 (过滤器 + Bucket4j)\n\n**安全须知**：默认情况下 `X-Forwarded-For` 头是不可信的，因为客户端可以伪造它。\n仅在以下情况下使用转发头：\n\n1. 您的应用程序位于可信的反向代理（nginx、AWS ALB 等）之后\n2. 您已将 `ForwardedHeaderFilter` 注册为 bean\n3. 您已在应用属性中配置了 `server.forward-headers-strategy=NATIVE` 或 `FRAMEWORK`\n4. 您的代理配置为覆盖（而非追加）`X-Forwarded-For` 头\n\n当 `ForwardedHeaderFilter` 被正确配置时，`request.getRemoteAddr()` 将自动从转发的头中返回正确的客户端 IP。\n没有此配置时，请直接使用 `request.getRemoteAddr()`——它返回的是直接连接的 IP，这是唯一可信的值。\n\n```java\n@Component\npublic class RateLimitFilter extends OncePerRequestFilter {\n  private final Map\u003CString, Bucket> buckets = new ConcurrentHashMap\u003C>();\n\n  \u002F*\n   * SECURITY: This filter uses request.getRemoteAddr() to identify clients for rate limiting.\n   *\n   * If your application is behind a reverse proxy (nginx, AWS ALB, etc.), you MUST configure\n   * Spring to handle forwarded headers properly for accurate client IP detection:\n   *\n   * 1. Set server.forward-headers-strategy=NATIVE (for cloud platforms) or FRAMEWORK in\n   *    application.properties\u002Fyaml\n   * 2. If using FRAMEWORK strategy, register ForwardedHeaderFilter:\n   *\n   *    @Bean\n   *    ForwardedHeaderFilter forwardedHeaderFilter() {\n   *        return new ForwardedHeaderFilter();\n   *    }\n   *\n   * 3. Ensure your proxy overwrites (not appends) the X-Forwarded-For header to prevent spoofing\n   * 4. Configure server.tomcat.remoteip.trusted-proxies or equivalent for your container\n   *\n   * Without this configuration, request.getRemoteAddr() returns the proxy IP, not the client IP.\n   * Do NOT read X-Forwarded-For directly—it is trivially spoofable without trusted proxy handling.\n   *\u002F\n  @Override\n  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n      FilterChain filterChain) throws ServletException, IOException {\n    \u002F\u002F Use getRemoteAddr() which returns the correct client IP when ForwardedHeaderFilter\n    \u002F\u002F is configured, or the direct connection IP otherwise. Never trust X-Forwarded-For\n    \u002F\u002F headers directly without proper proxy configuration.\n    String clientIp = request.getRemoteAddr();\n\n    Bucket bucket = buckets.computeIfAbsent(clientIp,\n        k -> Bucket.builder()\n            .addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1))))\n            .build());\n\n    if (bucket.tryConsume(1)) {\n      filterChain.doFilter(request, response);\n    } else {\n      response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());\n    }\n  }\n}\n```\n\n## 后台作业\n\n使用 Spring 的 `@Scheduled` 或与队列（如 Kafka、SQS、RabbitMQ）集成。保持处理程序是幂等的和可观察的。\n\n## 可观测性\n\n* 通过 Logback 编码器进行结构化日志记录 (JSON)\n* 指标：Micrometer + Prometheus\u002FOTel\n* 追踪：带有 OpenTelemetry 或 Brave 后端的 Micrometer Tracing\n\n## 生产环境默认设置\n\n* 优先使用构造函数注入，避免字段注入\n* 启用 `spring.mvc.problemdetails.enabled=true` 以获得 RFC 7807 错误 (Spring Boot 3+)\n* 根据工作负载配置 HikariCP 连接池大小，设置超时\n* 对查询使用 `@Transactional(readOnly = true)`\n* 在适当的地方通过 `@NonNull` 和 `Optional` 强制执行空值安全\n\n**记住**：保持控制器精简、服务专注、仓库简单，并集中处理错误。为可维护性和可测试性进行优化。\n","# Spring Boot 开发模式\n\n用于可扩展、生产级服务的 Spring Boot 架构和 API 模式。\n\n## 何时激活\n\n* 使用 Spring MVC 或 WebFlux 构建 REST API\n* 构建控制器 → 服务 → 仓库层结构\n* 配置 Spring Data JPA、缓存或异步处理\n* 添加验证、异常处理或分页\n* 为开发\u002F预发布\u002F生产环境设置配置文件\n* 使用 Spring Events 或 Kafka 实现事件驱动模式\n\n## REST API 结构\n\n```java\n@RestController\n@RequestMapping(\"\u002Fapi\u002Fmarkets\")\n@Validated\nclass MarketController {\n  private final MarketService marketService;\n\n  MarketController(MarketService marketService) {\n    this.marketService = marketService;\n  }\n\n  @GetMapping\n  ResponseEntity\u003CPage\u003CMarketResponse>> list(\n      @RequestParam(defaultValue = \"0\") int page,\n      @RequestParam(defaultValue = \"20\") int size) {\n    Page\u003CMarket> markets = marketService.list(PageRequest.of(page, size));\n    return ResponseEntity.ok(markets.map(MarketResponse::from));\n  }\n\n  @PostMapping\n  ResponseEntity\u003CMarketResponse> create(@Valid @RequestBody CreateMarketRequest request) {\n    Market market = marketService.create(request);\n    return ResponseEntity.status(HttpStatus.CREATED).body(MarketResponse.from(market));\n  }\n}\n```\n\n## 仓库模式 (Spring Data JPA)\n\n```java\npublic interface MarketRepository extends JpaRepository\u003CMarketEntity, Long> {\n  @Query(\"select m from MarketEntity m where m.status = :status order by m.volume desc\")\n  List\u003CMarketEntity> findActive(@Param(\"status\") MarketStatus status, Pageable pageable);\n}\n```\n\n## 带事务的服务层\n\n```java\n@Service\npublic class MarketService {\n  private final MarketRepository repo;\n\n  public MarketService(MarketRepository repo) {\n    this.repo = repo;\n  }\n\n  @Transactional\n  public Market create(CreateMarketRequest request) {\n    MarketEntity entity = MarketEntity.from(request);\n    MarketEntity saved = repo.save(entity);\n    return Market.from(saved);\n  }\n}\n```\n\n## DTO 和验证\n\n```java\npublic record CreateMarketRequest(\n    @NotBlank @Size(max = 200) String name,\n    @NotBlank @Size(max = 2000) String description,\n    @NotNull @FutureOrPresent Instant endDate,\n    @NotEmpty List\u003C@NotBlank String> categories) {}\n\npublic record MarketResponse(Long id, String name, MarketStatus status) {\n  static MarketResponse from(Market market) {\n    return new MarketResponse(market.id(), market.name(), market.status());\n  }\n}\n```\n\n## 异常处理\n\n```java\n@ControllerAdvice\nclass GlobalExceptionHandler {\n  @ExceptionHandler(MethodArgumentNotValidException.class)\n  ResponseEntity\u003CApiError> handleValidation(MethodArgumentNotValidException ex) {\n    String message = ex.getBindingResult().getFieldErrors().stream()\n        .map(e -> e.getField() + \": \" + e.getDefaultMessage())\n        .collect(Collectors.joining(\", \"));\n    return ResponseEntity.badRequest().body(ApiError.validation(message));\n  }\n\n  @ExceptionHandler(AccessDeniedException.class)\n  ResponseEntity\u003CApiError> handleAccessDenied() {\n    return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ApiError.of(\"Forbidden\"));\n  }\n\n  @ExceptionHandler(Exception.class)\n  ResponseEntity\u003CApiError> handleGeneric(Exception ex) {\n    \u002F\u002F Log unexpected errors with stack traces\n    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)\n        .body(ApiError.of(\"Internal server error\"));\n  }\n}\n```\n\n## 缓存\n\n需要在配置类上使用 `@EnableCaching`。\n\n```java\n@Service\npublic class MarketCacheService {\n  private final MarketRepository repo;\n\n  public MarketCacheService(MarketRepository repo) {\n    this.repo = repo;\n  }\n\n  @Cacheable(value = \"market\", key = \"#id\")\n  public Market getById(Long id) {\n    return repo.findById(id)\n        .map(Market::from)\n        .orElseThrow(() -> new EntityNotFoundException(\"Market not found\"));\n  }\n\n  @CacheEvict(value = \"market\", key = \"#id\")\n  public void evict(Long id) {}\n}\n```\n\n## 异步处理\n\n需要在配置类上使用 `@EnableAsync`。\n\n```java\n@Service\npublic class NotificationService {\n  @Async\n  public CompletableFuture\u003CVoid> sendAsync(Notification notification) {\n    \u002F\u002F send email\u002FSMS\n    return CompletableFuture.completedFuture(null);\n  }\n}\n```\n\n## 日志记录 (SLF4J)\n\n```java\n@Service\npublic class ReportService {\n  private static final Logger log = LoggerFactory.getLogger(ReportService.class);\n\n  public Report generate(Long marketId) {\n    log.info(\"generate_report marketId={}\", marketId);\n    try {\n      \u002F\u002F logic\n    } catch (Exception ex) {\n      log.error(\"generate_report_failed marketId={}\", marketId, ex);\n      throw ex;\n    }\n    return new Report();\n  }\n}\n```\n\n## 中间件 \u002F 过滤器\n\n```java\n@Component\npublic class RequestLoggingFilter extends OncePerRequestFilter {\n  private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);\n\n  @Override\n  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n      FilterChain filterChain) throws ServletException, IOException {\n    long start = System.currentTimeMillis();\n    try {\n      filterChain.doFilter(request, response);\n    } finally {\n      long duration = System.currentTimeMillis() - start;\n      log.info(\"req method={} uri={} status={} durationMs={}\",\n          request.getMethod(), request.getRequestURI(), response.getStatus(), duration);\n    }\n  }\n}\n```\n\n## 分页和排序\n\n```java\nPageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by(\"createdAt\").descending());\nPage\u003CMarket> results = marketService.list(page);\n```\n\n## 容错的外部调用\n\n```java\npublic \u003CT> T withRetry(Supplier\u003CT> supplier, int maxRetries) {\n  int attempts = 0;\n  while (true) {\n    try {\n      return supplier.get();\n    } catch (Exception ex) {\n      attempts++;\n      if (attempts >= maxRetries) {\n        throw ex;\n      }\n      try {\n        Thread.sleep((long) Math.pow(2, attempts) * 100L);\n      } catch (InterruptedException ie) {\n        Thread.currentThread().interrupt();\n        throw ex;\n      }\n    }\n  }\n}\n```\n\n## 速率限制 (过滤器 + Bucket4j)\n\n**安全须知**：默认情况下 `X-Forwarded-For` 头是不可信的，因为客户端可以伪造它。\n仅在以下情况下使用转发头：\n\n1. 您的应用程序位于可信的反向代理（nginx、AWS ALB 等）之后\n2. 您已将 `ForwardedHeaderFilter` 注册为 bean\n3. 您已在应用属性中配置了 `server.forward-headers-strategy=NATIVE` 或 `FRAMEWORK`\n4. 您的代理配置为覆盖（而非追加）`X-Forwarded-For` 头\n\n当 `ForwardedHeaderFilter` 被正确配置时，`request.getRemoteAddr()` 将自动从转发的头中返回正确的客户端 IP。\n没有此配置时，请直接使用 `request.getRemoteAddr()`——它返回的是直接连接的 IP，这是唯一可信的值。\n\n```java\n@Component\npublic class RateLimitFilter extends OncePerRequestFilter {\n  private final Map\u003CString, Bucket> buckets = new ConcurrentHashMap\u003C>();\n\n  \u002F*\n   * SECURITY: This filter uses request.getRemoteAddr() to identify clients for rate limiting.\n   *\n   * If your application is behind a reverse proxy (nginx, AWS ALB, etc.), you MUST configure\n   * Spring to handle forwarded headers properly for accurate client IP detection:\n   *\n   * 1. Set server.forward-headers-strategy=NATIVE (for cloud platforms) or FRAMEWORK in\n   *    application.properties\u002Fyaml\n   * 2. If using FRAMEWORK strategy, register ForwardedHeaderFilter:\n   *\n   *    @Bean\n   *    ForwardedHeaderFilter forwardedHeaderFilter() {\n   *        return new ForwardedHeaderFilter();\n   *    }\n   *\n   * 3. Ensure your proxy overwrites (not appends) the X-Forwarded-For header to prevent spoofing\n   * 4. Configure server.tomcat.remoteip.trusted-proxies or equivalent for your container\n   *\n   * Without this configuration, request.getRemoteAddr() returns the proxy IP, not the client IP.\n   * Do NOT read X-Forwarded-For directly—it is trivially spoofable without trusted proxy handling.\n   *\u002F\n  @Override\n  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,\n      FilterChain filterChain) throws ServletException, IOException {\n    \u002F\u002F Use getRemoteAddr() which returns the correct client IP when ForwardedHeaderFilter\n    \u002F\u002F is configured, or the direct connection IP otherwise. Never trust X-Forwarded-For\n    \u002F\u002F headers directly without proper proxy configuration.\n    String clientIp = request.getRemoteAddr();\n\n    Bucket bucket = buckets.computeIfAbsent(clientIp,\n        k -> Bucket.builder()\n            .addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1))))\n            .build());\n\n    if (bucket.tryConsume(1)) {\n      filterChain.doFilter(request, response);\n    } else {\n      response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());\n    }\n  }\n}\n```\n\n## 后台作业\n\n使用 Spring 的 `@Scheduled` 或与队列（如 Kafka、SQS、RabbitMQ）集成。保持处理程序是幂等的和可观察的。\n\n## 可观测性\n\n* 通过 Logback 编码器进行结构化日志记录 (JSON)\n* 指标：Micrometer + Prometheus\u002FOTel\n* 追踪：带有 OpenTelemetry 或 Brave 后端的 Micrometer Tracing\n\n## 生产环境默认设置\n\n* 优先使用构造函数注入，避免字段注入\n* 启用 `spring.mvc.problemdetails.enabled=true` 以获得 RFC 7807 错误 (Spring Boot 3+)\n* 根据工作负载配置 HikariCP 连接池大小，设置超时\n* 对查询使用 `@Transactional(readOnly = true)`\n* 在适当的地方通过 `@NonNull` 和 `Optional` 强制执行空值安全\n\n**记住**：保持控制器精简、服务专注、仓库简单，并集中处理错误。为可维护性和可测试性进行优化。\n",{"name":5,"origin":34,"description":7},"ECC","main","7b610356e36623652299fa2acb477ff821b1d920","upstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-CN\u002Fskills\u002Fspringboot-patterns",0,"https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FSuperClaw","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fo-abdelmaksoud\u002F","Full-Stack Engineer | AI Systems Architect | Building autonomous multi-agent systems","2026-03-08 21:12:53",{"source":8,"skillName":5,"commands":45,"prompt":55,"zipAvailable":56},[46,49,52],{"tool":47,"command":48},"npx","npx skills add oabdelmaksoud\u002FSuperClaw --skill springboot-patterns",{"tool":50,"command":51},"bunx","bunx skills add oabdelmaksoud\u002FSuperClaw --skill springboot-patterns",{"tool":53,"command":54},"pnpm","pnpm dlx skills add oabdelmaksoud\u002FSuperClaw --skill springboot-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FSuperClaw\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fdocs\u002Fzh-CN\u002Fskills\u002Fspringboot-patterns\u002FSKILL.md",true,[],[59,78,92,123,148,167],{"skillNo":60,"name":61,"nameZh":6,"description":62,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":63,"categories":64,"keywords":68,"publishedAt":29},"01M6D88QCPTBZVCRED0NZ1Y8K8","visa-doc-translate","Translate visa application documents (images) to English and create a bilingual PDF with original and translation","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FSuperClaw\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fvisa-doc-translate\u002FSKILL.md",[65],{"categoryNo":66,"categoryName":67,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000002","文档处理",[69,70,71,72,73,74,75,76,77],"translate","doc","visa doc translate","translation","application","create","images","PDF","documents",{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":84,"publishedAt":29},"01M6D88NBM95T3TFM50CYYAP65","verification-loop","A comprehensive verification system for Claude Code sessions.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FSuperClaw\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fverification-loop\u002FSKILL.md",[],[85,86,87,88,89,90,91],"verification","loop","verification loop","sessions","code","claude code sessions","system",{"skillNo":93,"name":94,"nameZh":6,"description":95,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":109,"publishedAt":29},"01M6D88JFZ2P996GVRHJNSZSTM","tdd-workflow","Use this skill when writing new features, fixing bugs, or refactoring code. Enforces test-driven development with 80%+ coverage including unit, integration, and E2E tests.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FSuperClaw\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Ftdd-workflow\u002FSKILL.md",[98,101,104],{"categoryNo":99,"categoryName":100,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":102,"categoryName":103,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":105,"categoryName":106,"taxonomyType":107,"taxonomyTypeDesc":108},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[110,111,112,113,114,115,89,116,117,118,119,120,121,122],"tdd","workflow","tdd workflow","e2e tests","e2e","integration","development","use","test","refactoring","coverage","tests","writing",{"skillNo":124,"name":125,"nameZh":6,"description":126,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":127,"categories":128,"keywords":129,"publishedAt":29},"01M6D88FTHRRSVJMXF2G7MQAK3","swiftui-patterns","SwiftUI architecture patterns, state management with @Observable, view composition, navigation, performance optimization, and modern iOS\u002FmacOS UI best practices.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FSuperClaw\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fswiftui-patterns\u002FSKILL.md",[],[25,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147],"swiftui","swiftui patterns","architecture","macos","performance","state management","best practices","state","ui","Optimization","ios","architecture patterns","Composition","performance optimization","view","management","swiftui architecture","navigation",{"skillNo":149,"name":150,"nameZh":6,"description":151,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":155,"publishedAt":29},"01M6D88D527QTQT9DKHTRFN6EA","swift-protocol-di-testing","Protocol-based dependency injection for testable Swift code — mock file system, network, and external APIs using focused protocols and Swift Testing.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FSuperClaw\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fswift-protocol-di-testing\u002FSKILL.md",[154],{"categoryNo":99,"categoryName":100,"taxonomyType":21,"taxonomyTypeDesc":22},[156,157,158,159,89,160,161,162,163,91,164,165,166],"swift protocol di testing","protocol","swift","testing","protocols","file","network","injection","dependency","swift testing","dependency injection",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":173,"publishedAt":29},"01M6D88AJW8H2QNWSW5ZY82R43","swift-concurrency-6-2","Swift 6.2 Approachable Concurrency — single-threaded by default, @concurrent for explicit background offloading, isolated conformances for main actor types.","https:\u002F\u002Fgithub.com\u002Foabdelmaksoud\u002FSuperClaw\u002Fblob\u002Fmain\u002Fupstream-sync\u002Fwork\u002Fecc\u002Fskills\u002Fswift-concurrency-6-2\u002FSKILL.md",[],[174,175,158,176,177,35],"concurrency","swift concurrency","swift concurrency 6 2","default"]