API开放平台
快来分享你的内容吧~
2025-10-24·@官方运营 学习、求职、生活问题欢迎交流- 2025-08-25·Java后端兄弟们,接口管理平台,可以集成kafka的接口吗,下游用户直接调用接口去生产消费数据? 是不是需要较大的cpu、内存等 部署资源,毕竟kafka给大数据用的Cool:可以的吧,可以参考阿里开源的一个框架430分享
- 2025-05-29·全栈开发
- 2025-03-26·Java后端项目求助 | 已解决 项目:API开放平台 | 本地部署前端项目 报错信息:npmERR!code 1,command C:\Windows\system32\cmd.exe /d /s /c husky install 请问有人知道解决吗aklry:在你前端的目录用打开cmd窗口git init一下320分享
- 2025-03-25·Java后端查看全文项目求助 | 已解决 1. 伙伴匹配项目 最近做完了伙伴匹配项目后端部分,但是出现了问题 通过swagger测试可以得到后端传来的数据,但是前端运行后,通过浏览器调试展示队伍功能,显示无法加载项响应数据 ...MapleJVM:1.伙伴匹配可以提供一下报错信息,浏览器控制台 or 后端控制台;2.API 开放平台不需要学习过 Spring Cloud 的知识,可以直接跟着视频学习项目,视频中对于 Gateway 网关、Dubbo 等概念都是手把手带的,可以边学边做,做完项目之后可以再系统学习相关的知识。2100分享
- 2025-02-24·@编程小助手 微信: leikooo_
API开放平台
首先感谢:https://www.codefather.cn/note/1805877794735435778 项目地址:https://openapihub.top/ 已完成: 接口调用、接口统计、接口令牌桶限流、api签名认证、网关鉴权、全局日志链路跟踪等 未完成: 仅支持支付宝支付(沙箱),开发者自己接入sdk 声明:项目未完善,别上压力,我服务器2核2G,部署3个jar包,顶不住,发出来仅供互相学习,还未开源,需要源码私我     
IntelliHub API 下发指南
# IntelliHub API 下发指南 本文档详细说明如何在 IntelliHub 平台创建和发布一个开放API,以 **auth服务的验证码接口** 为例。 项目地址:https://github.com/DevYangJC/intelli_hub --- ## 一、概念说明 ### 什么是"下发API"? 将内部服务的接口通过网关暴露给外部调用方,实现: - 统一入口:所有API通过网关访问 - 安全控制:认证、限流、IP白名单 - 路径转换:对外路径与内部路径解耦 ```mermaid sequenceDiagram participant External as 外部调用方 participant Gateway as 网关 participant Service as 内部服务 External->>Gateway: GET /open/auth/captcha Gateway->>Service: GET /iam/v1/auth/captcha Service-->>Gateway: 验证码图片 Gateway-->>External: 验证码图片 ``` --- ## 二、创建API字段详解 ### 📋 1. 基本信息 | 字段 | 必填 | 说明 | 验证码示例 | |------|------|------|-----------| | **API名称** | ✅ | 接口的中文名称,用于展示和搜索 | `获取验证码` | | **API编码** | ✅ | 唯一标识符,创建后不可修改。只能包含小写字母、数字和连字符,以字母开头 | `get-captcha` | | **API分组** | ✅ | 将API归类到某个分组,便于管理 | 选择"认证服务"或新建 | | **版本号** | ✅ | API版本,用于版本管理 | `v1` | | **API描述** | ❌ | 详细描述API功能,会展示在API文档中 | `获取图形验证码,用于登录时人机验证` | | **标签** | ❌ | 多个标签,用于筛选和分类 | `认证相关`、`公开接口` | --- ### 🔧 2. 后端服务类型 选择API后端的实现方式: | 类型 | 说明 | 适用场景 | |------|------|----------| | **HTTP/HTTPS** | 转发到HTTP服务 | 最常用,调用Spring Cloud微服务 | | **Dubbo** | 调用Dubbo RPC服务 | 后端是Dubbo服务时使用 | | **内部API** | 关联平台已有的API | API编排、组合接口 | | **Mock** | 返回模拟数据 | 开发调试、前端联调 | #### 验证码接口选择:`HTTP/HTTPS` --- ### 🌐 3. HTTP后端配置(选择HTTP时显示) | 字段 | 必填 | 说明 | 验证码示例 | |------|------|------|-----------| | **协议** | ✅ | HTTP或HTTPS | `HTTP` | | **后端地址** | ✅ | 后端服务地址。可以是:<br>- Nacos服务名:`http://intelli-auth-iam-service`<br>- IP地址:`http://192.168.1.100:8081`<br>- 域名:`https://api.example.com` | `http://intelli-auth-iam-service` | | **后端路径** | ✅ | 后端服务的实际接口路径 | `/iam/v1/auth/captcha` | | **请求方式** | ✅ | 调用后端使用的HTTP方法(默认自动同步请求配置) | `GET` | --- ### 📡 4. 请求配置 定义**对外暴露**的接口规范: | 字段 | 必填 | 说明 | 验证码示例 | |------|------|------|-----------| | **请求方式** | ✅ | 外部调用方使用的HTTP方法 | `GET` | | **请求路径** | ✅ | 对外暴露的API路径,建议以`/open/`开头 | `/open/auth/captcha` | | **Content-Type** | ✅ | 请求体格式 | `application/json` | > **路径设计建议**: > > - 公开接口:`/open/xxx` > - 需要认证:`/api/xxx` > - 支持路径参数:`/open/users/{userId}` --- ### 📝 5. 请求参数(可选) 定义接口的入参,用于生成API文档: | 参数位置 | 说明 | 示例 | |----------|------|------| | **Query参数** | URL查询参数 `?key=value` | `?type=image` | | **Path参数** | 路径参数 `/users/{id}` | `id` | | **Header参数** | 请求头参数 | `X-Token` | | **Body参数** | 请求体参数(POST/PUT时) | `{"username": "xxx"}` | **验证码接口**:无需参数,跳过此部分 --- ### 📄 6. 响应配置(可选) 定义接口的返回格式,用于生成API文档: | 字段 | 说明 | 验证码示例 | |------|------|-----------| | **成功响应示例** | 请求成功时的返回JSON | 见下方 | | **错误响应示例** | 请求失败时的返回JSON | 见下方 | ```json // 成功响应示例 { "code": 200, "message": "success", "data": { "captchaId": "abc123", "captchaImage": "data:image/png;base64,..." } } // 错误响应示例 { "code": 500, "message": "验证码生成失败", "data": null } ``` --- ### ⚙️ 7. 高级配置(可选) | 字段 | 默认值 | 说明 | 验证码示例 | |------|--------|------|-----------| | **超时时间** | 5000ms | 后端调用超时时间,超时返回504 | `5000` | | **重试次数** | 0 | 失败后重试次数,0表示不重试 | `0` | --- ### 🔒 8. 安全配置 #### 认证方式 | 选项 | 说明 | 适用场景 | |------|------|----------| | **无需认证** | 任何人都可以调用 | 公开接口:验证码、公告 | | **签名认证** | 需要AppKey+签名 | 开放给第三方的接口 | | **JWT Token** | 需要登录Token | 用户登录后的接口 | **验证码接口**:选择 `无需认证`(因为是登录前调用) #### 限流策略 | 字段 | 说明 | 验证码示例 | |------|------|-----------| | **开启限流** | 是否启用限流 | ✅ 开启 | | **限流值** | 单位时间内允许的请求数 | `100` | | **限流单位** | 秒/分钟/小时 | `秒` | > **建议**:验证码接口开启限流,防止恶意刷取 #### IP白名单 | 字段 | 说明 | 验证码示例 | |------|------|-----------| | **开启白名单** | 是否限制调用IP | ❌ 不开启 | | **IP列表** | 允许访问的IP,支持CIDR | - | --- ## 三、完整填写示例:验证码接口 ### 表单填写 ```yaml # 基本信息 API名称: 获取验证码 API编码: get-captcha API分组: 认证服务 版本号: v1 API描述: 获取图形验证码,用于登录时人机验证 标签: [认证相关, 公开接口] # 后端服务类型 服务类型: HTTP/HTTPS # HTTP后端配置 协议: HTTP 后端地址: http://intelli-auth-iam-service 后端路径: /iam/v1/auth/captcha 请求方式: GET (自动同步) # 请求配置 请求方式: GET 请求路径: /open/auth/captcha Content-Type: application/json # 请求参数 (无) # 响应配置 (可选填写) # 高级配置 超时时间: 5000毫秒 重试次数: 0 # 安全配置 认证方式: ☑ 无需认证 限流策略: ☑ 开启, 100次/秒 IP白名单: ☐ 不开启 ``` ### 创建后测试 ```bash # 直接调用(无需认证) curl http://localhost:8080/open/auth/captcha # 预期返回 { "code": 200, "message": "success", "data": { "captchaId": "xxx", "captchaImage": "data:image/png;base64,..." } } ``` --- ## 四、API生命周期 ``` 创建 → 草稿 → 发布 → 已发布 → 下线 → 已下线 ↑ │ └────────────────────┘ 可重新发布 ``` | 状态 | 说明 | 可执行操作 | |------|------|-----------| | **草稿** | 刚创建,未发布 | 编辑、删除、发布 | | **已发布** | 正常对外服务 | 编辑、下线 | | **已下线** | 暂停服务 | 编辑、发布、删除 | | **已废弃** | 即将删除 | 删除 | --- ## 五、常见问题 ### Q1: 后端地址填IP还是服务名? **推荐使用Nacos服务名**: - ✅ `http://intelli-auth-iam-service` - 自动负载均衡 - ❌ `http://192.168.1.100:8081` - 单点,不推荐 ### Q2: 请求路径和后端路径有什么区别? | 路径 | 作用 | 示例 | |------|------|------| | 请求路径 | 对外暴露,调用方使用 | `/open/auth/captcha` | | 后端路径 | 内部服务实际路径 | `/iam/v1/auth/captcha` | ### Q3: 认证方式怎么选? | 场景 | 认证方式 | |------|----------| | 登录前的接口(验证码、注册) | 无需认证 | | 开放给第三方系统调用 | 签名认证(AppKey) | | 用户登录后的接口 | JWT Token | ### Q4: 限流怎么设置合理? | 接口类型 | 建议QPS | |----------|---------| | 验证码 | 50-100/秒 | | 登录 | 10-20/秒 | | 普通查询 | 100-500/秒 | | 数据导出 | 1-5/秒 | --- ## 六、Dubbo接口配置示例:获取用户信息 以 **AuthDubboService.getUserById** 接口为例,演示如何将Dubbo接口暴露为HTTP API。 ### Dubbo接口信息 ```java // 接口定义位置:intelli-common/src/main/java/com/intellihub/common/dubbo/AuthDubboService.java package com.intellihub.common.dubbo; public interface AuthDubboService { /** * 根据用户ID获取用户信息 */ UserInfoDTO getUserById(String userId); } // 实现类注解 @DubboService(version = "1.0.0", group = "intellihub") public class AuthDubboServiceImpl implements AuthDubboService { ... } ``` ### 表单填写 ```yaml # 基本信息 API名称: 获取用户信息 API编码: get-user-by-id API分组: 用户服务 版本号: v1 API描述: 根据用户ID获取用户详细信息(Dubbo接口) 标签: [用户管理, Dubbo接口] # 后端服务类型 服务类型: Dubbo # ← 选择Dubbo # Dubbo后端配置 接口全路径: com.intellihub.common.dubbo.AuthDubboService 方法名: getUserById 版本: 1.0.0 分组: intellihub 参数类型: java.lang.String # 方法参数类型 # 请求配置 请求方式: GET 请求路径: /open/user/{userId} Content-Type: application/json # 请求参数 参数名称: userId 参数位置: Path 参数类型: String 是否必填: 是 参数说明: 用户ID # 安全配置 认证方式: ☑ 签名认证 (AppKey) # 用户信息需要认证 限流策略: ☑ 开启, 50次/秒 ``` ### Dubbo配置字段说明 | 字段 | 必填 | 说明 | 示例 | |------|------|------|------| | **接口全路径** | ✅ | Dubbo接口的完整包名+类名 | `com.intellihub.common.dubbo.AuthDubboService` | | **方法名** | ✅ | 要调用的方法名称 | `getUserById` | | **版本** | ❌ | Dubbo服务版本,对应`@DubboService(version=...)` | `1.0.0` | | **分组** | ❌ | Dubbo服务分组,对应`@DubboService(group=...)` | `intellihub` | | **参数类型** | ✅ | 方法参数的Java类型,多个用逗号分隔 | `java.lang.String` | ### 调用测试 ```bash # 1. 生成签名(需要AppKey和AppSecret) TIMESTAMP=$(date +%s%3N) NONCE=$(uuidgen | tr -d '-') APP_KEY="your-app-key" APP_SECRET="your-app-secret" METHOD="GET" PATH="/open/user/123456" # 2. 计算签名 STRING_TO_SIGN="${METHOD}\n${PATH}\n${TIMESTAMP}\n${NONCE}" SIGNATURE=$(echo -n "$STRING_TO_SIGN" | openssl dgst -sha256 -hmac "$APP_SECRET" -binary | base64) # 3. 发起请求 curl -X GET "http://localhost:8080/open/user/123456" \ -H "X-App-Key: ${APP_KEY}" \ -H "X-Timestamp: ${TIMESTAMP}" \ -H "X-Nonce: ${NONCE}" \ -H "X-Signature: ${SIGNATURE}" # 预期返回 { "code": 200, "message": "success", "data": { "userId": "123456", "username": "admin", "nickname": "管理员", "email": "admin@example.com", "roles": ["admin"], "permissions": ["user:read", "user:write"] } } ``` ### 多参数Dubbo方法示例 如果Dubbo方法有多个参数: ```java // 接口方法 UserInfoDTO getUserByCondition(String tenantId, String username, Integer status); ``` 配置时参数类型用逗号分隔: ```yaml 参数类型: java.lang.String,java.lang.String,java.lang.Integer ``` 请求参数对应配置: | 参数名 | 位置 | 类型 | 说明 | |--------|------|------|------| | tenantId | Query | String | 租户ID | | username | Query | String | 用户名 | | status | Query | Integer | 状态 | 调用示例: ```bash curl "http://localhost:8080/open/user/query?tenantId=1&username=admin&status=1" ``` --- ## 七、现有Dubbo接口清单 | 服务 | 接口 | 方法 | 说明 | |------|------|------|------| | **AuthDubboService** | `com.intellihub.common.dubbo.AuthDubboService` | `validateToken(String)` | 验证Token | | | | `getUserById(String)` | 根据ID获取用户 | | | | `getUserByUsername(String)` | 根据用户名获取用户 | | **TenantDubboService** | `com.intellihub.common.dubbo.TenantDubboService` | - | 租户服务 | | **ApiPlatformDubboService** | `com.intellihub.common.dubbo.ApiPlatformDubboService` | - | API平台服务 | | **AppCenterDubboService** | `com.intellihub.common.dubbo.AppCenterDubboService` | - | 应用中心服务 | > **注意**:Dubbo版本统一为 `1.0.0`,分组统一为 `intellihub` --- ## 八、下一步 1. **创建应用**:控制台 → 应用管理 → 创建应用 2. **获取密钥**:应用详情 → 密钥管理 → 获取AppKey/AppSecret 3. **订阅API**:应用详情 → API授权 → 勾选需要的API 4. **调用测试**:使用AppKey+签名调用(如果是签名认证)
🚀 从0到1打造企业级API开放平台:IntelliHub项目
## 💡 为什么要做这个项目? ### 想法来源 在我刚刚作为java程序员进入社会开始打拼的时候,第一家公司我会经常用到开放平台,来将开发接口提供出去,时过境迁,五年时间已过,AI的浪潮已经将新人和老人的平衡打破,独立开发一个大型应用,也将不可能转化为可能,看到了鱼皮的开放平台,大概明白了技术要点,**自己动手,打造一个企业级的API开放平台**。回顾自己学习的内容,也打造一个自己的开源项目。 ## 页面前瞻  ### 项目地址 https://github.com/DevYangJC/intelli_hub 目前项目还不完善还有很多bug,后续慢慢更新,希望小伙伴们一起加入进来,多提一些pr,一起完成一个大型项目的开发 ### 项目定位 IntelliHub定位为**企业级智能API开放平台**,核心解决三个问题: 1. **统一入口** - 所有API通过一个网关对外开放 2. **统一安全** - 标准化的认证鉴权,防重放攻击 3. **统一治理** - 完整的监控、统计、告警能力 ## 🎯 需求分析:我想要什么? ### 核心需求梳理 我先画了一张思维导图(后来演变成了需求文档),明确了几个核心模块:  ### 非功能需求 技术选型时,我特别考虑了几个点: - **高性能** - 网关使用WebFlux响应式编程 - **可扩展** - 微服务架构,支持水平扩展 - **可靠性** - Kafka削峰填谷,Redis高速缓存 - **易用性** - Vue3现代化前端,Element Plus组件库 ## 🏗️ 架构设计:怎么搭建? ### 整体架构 我采用了**微服务+事件驱动**的架构: ``` 客户端 ↓ 网关(Spring Cloud Gateway) ↓ 业务服务集群 ↓ 数据存储(MySQL + Redis + Kafka + elasticSearch) ``` ### 微服务架构图 ``` intellihub-parent // 整个平台的 Maven 父工程,统一管理子模块版本、依赖和插件配置 ├── inner-intergration // 内部通用组件与 Spring Boot Starter 集合,供各业务服务复用 │ ├── aop-spring-boot-starter // 面向切面编程自动配置:如日志埋点、性能监控、异常处理等 │ ├── common-dubbo-api // Dubbo 接口定义模块(API 层),包含服务间调用的 DTO 和接口契约 │ ├── common-helper // 通用工具类集合:日期、字符串、加密、ID 生成、校验等辅助方法 │ ├── common-interceptor-spring-boot-starter // Web 拦截器自动配置:如租户上下文解析、请求预处理等 │ ├── common-security-spring-boot-starter // 安全基础能力封装:JWT 解析、权限上下文、租户隔离等 │ ├── kafka-spring-boot-starter // Kafka 自动配置封装:生产者/消费者模板、序列化、错误重试等 │ ├── mybatis-helper-spring-boot-starter // MyBatis 增强封装:分页插件、逻辑删除、字段自动填充等 │ ├── pom.xml // inner-intergration 模块的 Maven 配置文件 │ ├── redis-cache-spring-boot-starter // Redis 缓存自动配置:缓存注解支持(@Cacheable)、连接池、序列化等 │ └── swagger-spring-boot-starter // Swagger/OpenAPI 自动集成:统一 API 文档入口、安全开关、UI 配置 ├── intelli-aigc-service // AIGC(人工智能生成内容)业务服务:提供文本生成、智能问答等 AI 能力 ├── intelli-api-platform-service // API 平台核心服务:管理 API 元数据、路由配置、文档、测试等 ├── intelli-app-center-service // 应用中心服务:管理第三方应用注册、AppKey/Secret、订阅关系等 ├── intelli-auth-iam-service // 身份认证与权限管理服务(IAM):用户、角色、权限、SSO、多租户体系 ├── intelli-event-service // 事件中心服务:处理系统内异步事件(如审计、通知、状态变更) ├── intelli-gateway-service // API 网关服务:路由转发、鉴权、限流、日志采集、协议转换等 ├── intelli-governance-service // 治理服务:服务注册发现、配置中心、熔断降级、指标监控等(若未用 Nacos/Sentinel 可能自研) ├── intelli-log-audit-service // 日志与审计服务:集中存储访问日志、操作日志,支持查询、告警、合规导出 ├── intelli-sdk // 开放平台 SDK:提供 Java/Python 等语言的客户端库,简化开发者接入 ├── intelli-search-service // 搜索服务:基于 Elasticsearch 实现全局搜索(API、应用、文档等) └── pom.xml // 根项目的 Maven 配置,定义所有子模块及公共依赖管理 ``` ### 关键设计决策 **1. 双流量认证模型** 这是我花了最多时间思考的设计。最终决定: - **管理后台**(`/api/**`)→ JWT认证 - 用户登录后获取Token - 网关本地验签,性能更好 - 注入用户ID、租户ID到请求头 - **开放API**(`/open/**`)→ AppKey+签名认证 - 类似AWS的签名机制 - HMAC-SHA256签名算法 - Timestamp + Nonce防重放攻击 **为什么不用一套认证?** 因为场景不同: - 管理后台是人使用,需要登录态 - 开放API是系统调用,无状态更合适 **2. 动态路由设计** 这是最有技术含量的部分! 传统网关:配置写死,要改就得重启 我的方案:**配置存数据库,网关动态加载** ```java // 网关根据path匹配路由配置 ApiRouteDTO route = routeService.matchRoute(path, method); // 根据backendType决定调用方式 if ("http".equals(route.getBackendType())) { // HTTP转发,支持服务名负载均衡 webClient.forward(route.getBackendHost()); } else if ("dubbo".equals(route.getBackendType())) { // Dubbo泛化调用,无需依赖业务JAR包 dubboService.invoke(route); } ``` - API配置变更无需重启网关 - 支持HTTP、Dubbo、Mock多种后端 - 通过Redis Pub/Sub实时刷新配置 调用日志怎么采集?很多人会想到直接写数据库。 **我的方案:Kafka异步 + Redis实时统计** ``` 请求 → 网关 → 同步返回响应 ↓(异步) 写Kafka消息 + 写Redis统计 ↓ 治理服务消费 → 落库 + 聚合分析 ``` 为什么这样设计? - **性能考虑** - 网关是入口,不能阻塞 - **可靠性考虑** - Kafka保证消息不丢 - **实时性考虑** - Redis支持秒级告警 ## 💻 技术实现:怎么落地? ### 技术选型 | 技术 | 选型 | 理由 | | -------- | -------------------- | ------------------------ | | 网关 | Spring Cloud Gateway | 响应式,性能好 | | 服务发现 | Nacos | 配置中心+注册中心二合一 | | RPC | Dubbo | 泛化调用支持动态路由 | | 消息队列 | Kafka | 高吞吐,日志采集首选 | | 缓存 | Redis | 高性能,支持多种数据结构 | | 数据库 | MySQL | 稳定可靠 | | 聚合搜索 | ElasticSearch | 快速搜索 | | 前端 | Vue3 + TS | 现代化,类型安全 | ### 开发过程的坑 **坑1:WebFlux的Body只能读一次** 问题:网关需要记录请求Body,但WebFlux读一次就没了 解决:写了一个CacheBodyFilter,在最开始缓存Body **坑2:多租户数据隔离** 问题:每个SQL都要加tenantId条件,容易遗漏 解决:MyBatis Plus的租户插件 + ThreadLocal传递租户上下文 **坑3:告警风暴** 问题:一个API出问题,瞬间产生上千条告警 解决:引入冷却期机制,同一告警5分钟内只通知一次 ## ✨ 核心功能展示 ### 1. API生命周期管理 API管理列表(发布,下线,复制,历史,废弃,删除)  #### 创建API  #### API详情(文档,调试)  ### 2. 实时监控统计  ### 3. API市场  ### 4. 开发指南  ## 📊 项目成果 ### 代码量统计 - **后端代码**:约15000行 - **前端代码**:约8000行 - **配置文件**:约2000行 - **文档**:6篇技术文档,共约8000字 ### 技术亮点 1. ✅ **双流量认证模型** - JWT + AppKey签名 2. ✅ **动态路由** - 支持HTTP/Dubbo/Mock 3. ✅ **泛化调用** - 网关无需依赖业务JAR 4. ✅ **实时告警** - Redis + 定时任务 5. ✅ **多租户架构** - 全链路数据隔离 6. ✅ **事件驱动** - Kafka异步解耦 7. ✅ **响应式编程** - WebFlux高性能网关 ### 学到的经验 **架构设计** - 先搞清楚核心问题,不要为了技术而技术 - 多画图,架构图、流程图、时序图都很重要 - 先做MVP,不要一开始就追求完美 **技术选型** - 选择熟悉的技术栈,不要盲目追新 - 考虑团队规模,微服务不是唯一选择 - 性能和可维护性要平衡 **开发节奏** - 按模块拆分,先做核心链路 - 频繁提交代码,小步快跑 - 及时写文档,否则后面会忘 ## 🎬 后续规划 ### 短期计划(1-2个月) - [ ] 完善告警通知渠道(邮件、钉钉、企业微信) - [ ] 接入AIGC能力,智能分析调用异常 - [ ] 添加API测试工具(类似Postman) - [ ] 支持GraphQL协议 ### 长期规划(3-6个月) - [ ] 插件化架构改造 - [ ] 支持灰度发布和流量染色 - [ ] 接入链路追踪(SkyWalking) - [ ] API市场和计费系统
🚀 编程导航成品项目(上百个真实项目)
编程导航已累计发布数十套项目教程:[课程地址](https://www.codefather.cn/course)。 为方便大家集中学习参考,也帮助已完成项目的同学获得更多曝光与真实用户,我们整理汇总了过往提交的优质项目帖。 🔍 如果你的项目已提交,欢迎确认是否被收录; 🚀 如果你正在开发中,不妨参考已有项目的实现思路与笔记总结,相信对你有帮助。 > 收录说明:若你的项目文章尚未被收录,欢迎评论区留言:项目文章链接 + 线上地址(如有),管理员会定期更新到汇总帖里,助力每一个认真分享的项目被更多人看见~ ## AI 零代码应用生成平台 企业级项目教程:https://codefather.cn/course/1948291549923344386 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### CodeLoom(已上线) 👉 线上地址:http://1.15.243.228:9010/CodeLoom/ 📚 原帖链接:https://codefather.cn/post/1960256724123316226   #### AI Code Mother(已上线) 👉 线上地址:https://joinoai.cloud/user/register?inviteCode=INVNJZKYSJH 📚 原帖链接:https://codefather.cn/post/1974436661408411649   #### 代码灵构 📚 原帖链接:https://codefather.cn/note/1957033275842670593   #### XiangAI 📚 原帖链接:https://codefather.cn/essay/1957335221259333633   #### AI Code easen 📚 原帖链接:https://codefather.cn/post/1954389275447824386   ## AI 超级智能体 企业级项目教程:https://codefather.cn/course/1915010091721236482 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### Crispix Agent 📚 原帖链接:https://codefather.cn/post/1962528203403792386  #### ApocalyxAI(智能助手平台) 📚 原帖链接:https://codefather.cn/post/1937413626668843010   #### 神农医慧库 📚 原帖链接:https://codefather.cn/post/1936010788000563201  #### AI 智能应用中心 📚 原帖链接:https://codefather.cn/post/1928019148442537985  #### CodeManus AI 助手 📚 原帖链接:https://codefather.cn/post/1966800552892235778   ## 智能协同云图库项目项目 企业级项目教程:https://codefather.cn/course/1864210260732116994 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### 悦木图库(已上线) 👉 线上地址:https://lumenglover.com/ 📚 原帖链接:https://codefather.cn/essay/1890067861869486081   #### 暴龙图库(已上线) 👉 线上地址:https://picture.baolong.icu/ 📚 原帖链接:https://codefather.cn/post/1905997569888280577   #### Crispix AI 图库(已上线) 👉 线上地址:https://crispix.fun 📚 原帖链接:https://codefather.cn/post/1915861894909386754   #### 云图智联(已上线) 👉 线上地址:https://imagecore.netlify.app/ 📚 原帖链接:https://codefather.cn/post/1970098502037934081   #### 荒梦图库(已上线) 👉 线上地址:https://pichm.cloud/ 📚 原帖链接:https://codefather.cn/post/1960673049771610113   #### Picazza 图库(已上线) 👉 线上地址:https://picazza.com/ 📚 原帖链接:https://www.codefather.cn/post/1945659153240215554   #### 悦突突(已上线) 👉 线上地址:https://coder-live.cn/ 📚 原帖链接:https://codefather.cn/post/1950740016980426754   #### 图潮(已上线) 👉 线上地址:https://picwave.top/ 📚 原帖链接:https://codefather.cn/essay/1911024578559188993   #### 云相册(已上线) 👉 线上地址:https://pic.lin03.cn/ 📚 原帖链接:https://codefather.cn/post/1929477619980566530   #### HY 映像(已上线) 👉 线上地址:https://zhydoc.com/picture/ 📚 原帖链接:https://codefather.cn/post/1967843032026775553  #### 智图云链(已上线) 👉 线上地址:http://117.72.69.235/  #### han_picture 📚 原帖链接:https://codefather.cn/post/1974327430218887170   ## 面试刷题平台 企业级项目教程:https://codefather.cn/course/1826803928691945473 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### 面试秘境(已上线) 👉 线上地址:http://mianshimijing.icu/ 📚 原帖链接:https://codefather.cn/post/1869660024067571713   ## AI 答题应用平台 企业级项目教程:https://codefather.cn/course/1790274408835506178 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### 腾蛇问渊(已上线) 👉 线上地址:https://aianswerproj.vercel.app/ 📚 原帖链接:https://codefather.cn/essay/1809498338799878146  #### 智慧答 📚 原帖链接:https://codefather.cn/post/1826490068167761921   ## OJ 在线判题系统 企业级项目教程:https://codefather.cn/course/1790980707917017089 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### 雪 OJ(已上线) 👉 线上地址:http://snowyee.cn/snowyee-oj/#/ 📚 原帖链接:https://codefather.cn/note/1810751001442844674   #### 流火 OJ 📚 原帖链接:https://codefather.cn/post/1827575600860672001   #### SSPU-OJ 📚 原帖链接:https://codefather.cn/post/1964988539086487554   #### 新 OJ 📚 原帖链接:https://codefather.cn/note/1806522941518364673   #### HHOJ 📚 原帖链接:https://codefather.cn/post/1895681485945774082   #### AICoder OJ 📚 原帖链接:https://codefather.cn/note/1811051057198858241  📚 原帖链接:https://codefather.cn/note/1829734814391640065   #### CsCS OJ 📚 原帖链接:https://codefather.cn/post/1836003849697837057  #### 疯 OJ 📚 原帖链接:https://codefather.cn/post/1913879429893369857   ## 代码生成器共享平台 企业级项目教程:https://codefather.cn/course/1790980795074654209 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### 飞云代码生成 📚 原帖链接:https://codefather.cn/note/1816913082742140930  ## 智能 BI 项目 企业级项目教程:https://codefather.cn/course/1790980531403927553 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### 灵犀 BI(已上线) 👉 线上地址:http://bi.hejiajun.icu/ 📚 原帖链接:https://codefather.cn/post/1876163793297530881  #### Kim 智能 BI 📚 原帖链接:https://codefather.cn/post/1908147328172138498   #### Liy 智能 BI 分析系统 👉 线上地址:https://8.141.7.82/login 📚 原帖链接:https://codefather.cn/essay/1853271746140782593   #### Admin 智能 BI 分析系统 📚 原帖链接:https://codefather.cn/post/1812563734513770498   #### Flance 智能 BI 系统 📚 原帖链接:https://codefather.cn/note/1813731731964133378   #### AI 洞察 📚 原帖链接:https://codefather.cn/post/1816913159401435138    #### hx BI 📚 原帖链接:https://codefather.cn/post/1812563043074367490   #### Solar BI 📚 原帖链接:https://codefather.cn/note/1816551710832824322   #### 哥布林智能 BI 分析系统 📚 原帖链接:https://codefather.cn/note/1811050100016742402   #### PS 智能 BI 分析系统 📚 原帖链接:https://codefather.cn/note/1810278024374505474   #### ToolVerse:一体化智能管理与创新服务平台 📚 原帖链接:https://codefather.cn/post/1810389203533025282   #### Nero 智能 BI 分析系统 📚 原帖链接:https://codefather.cn/note/1811010648561090562   #### GBC 智能 BI 分析系统 📚 原帖链接:https://codefather.cn/note/1808941076960215041   ### 聚合搜索平台 企业级项目教程:https://codefather.cn/course/1790979621621641217 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### 空空-聚合搜索平台 📚 原帖链接:https://codefather.cn/note/1924150587763404802   #### 崽崽聚合搜索平台 📚 原帖链接:https://codefather.cn/note/1817637092455018497   #### 青年聚合搜索平台 📚 原帖链接:https://codefather.cn/post/1825764402195537921  ### API 开放平台 企业级项目教程:https://codefather.cn/course/1790979723916521474 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### Qi-API 接口开放平台(已上线) 👉 线上地址:https://api.qimuu.icu/user/login 📚 原帖链接:https://codefather.cn/note/1805877794735435778   #### 羊腿 API 开放平台(已上线) 👉 线上地址:http://api.panyuwen.top/user/login 📚 原帖链接:https://codefather.cn/note/1855478010485653505    #### Cxm-API 接口开放平台 📚 原帖链接:https://codefather.cn/essay/1905452207706185730    #### LeAPI 接口开放平台 📚 原帖链接:https://codefather.cn/note/1816551310788497409   #### X API 开放调用平台 📚 原帖链接:https://codefather.cn/note/1816913304989921281   #### Adagio 接口开放平台 📚 原帖链接:https://codefather.cn/note/1816551835571425281   #### 聚合 API 开放平台 📚 原帖链接:https://codefather.cn/note/1811049203207438337   #### EasyAPI 开放平台 📚 原帖链接:https://codefather.cn/note/1832713500474793986   #### 洛言接口开放平台 📚 原帖链接:https://codefather.cn/post/1828449609845428226  #### k 接口开放平台 📚 原帖链接:https://codefather.cn/note/1808772929765376001  #### 为-接口开放平台 📚 原帖链接:https://codefather.cn/note/1811050695448526849   #### API 接口服务平台 📚 原帖链接:https://codefather.cn/post/1839933551062880258  #### 银竹接口开放平台 📚 原帖链接:https://codefather.cn/post/1810389203533025282   #### Insight平台 📚 原帖链接:https://codefather.cn/post/1812563734513770498  ### 伙伴匹配系统 企业级项目教程:https://codefather.cn/course/1790950013153095682 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### homie 伙伴匹配系统(已上线) 👉 线上地址:http://hm.hejiajun.icu/ 📚 原帖链接:https://codefather.cn/note/1823592419365535746   #### 花语-伙伴匹配系统(已上线) 👉 线上地址:http://huayu.hanades.online  #### 速配 SUPER - 伙伴匹配系统(已上线) 👉 线上地址:https://ochiamalu.fun/ 📚 原帖链接:https://codefather.cn/essay/1909073423851560961   #### yes - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/post/1811047157863481345   #### 等闲 - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/note/1812741780545921026   #### 鱼泡 - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/note/1806571986623836161   #### Louis - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/note/1806569725122228226  #### 知秋 - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/note/1816551544797106177   #### 小虎 - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/note/1814012455103463426  #### 龙 - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/note/1818361301050142721   #### w - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/note/1838446471885594625   #### 云交友 - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/post/1825763690480869378   #### 大学生 - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/note/1820898093159849986   #### Yz - 伙伴匹配系统 📚 原帖链接:https://codefather.cn/essay/1870039737155010561   ### 用户中心项目 企业级项目教程:https://codefather.cn/course/1790943469757837313 > 视频教程 + 项目文档 + 源码 + 简历写法 + 面试题解 + 专属交流群 #### 可乐用户中心(已上线) 👉 线上地址:http://user.xykcola.site/user/login 📚 原帖链接:https://codefather.cn/note/1811475602152685570   #### (NextJS 版)用户中心管理 📚 原帖链接:https://codefather.cn/post/1912880102888280065  #### chen 用户中心 📚 原帖链接:https://codefather.cn/note/1811050198691938306   #### Find Me 用户中心 📚 原帖链接:https://codefather.cn/note/1811342501757911041   #### 小汉用户中心 📚 原帖链接:https://codefather.cn/note/1810181026137473026   #### 用户中心 📚 原帖链接:https://codefather.cn/post/1925206864866131970   #### 吃鱼用户中心 📚 原帖链接:https://codefather.cn/post/1896897093685288961  📚 原帖链接:https://codefather.cn/note/1811049194567172097  #### 崽崽用户中心 📚 原帖链接:https://codefather.cn/note/1817637102550708225   #### Jiaxiong 用户中心 📚 原帖链接:https://codefather.cn/note/1815828758460895233  
兄弟们,接口管理平台,可以集成kafka的接口吗,下游用户直接调用接口去生产消费数据? 是不是需要较大的cpu、内存等 部署资源,毕竟kafka给大数据用的
API开放平台完结
# 第一期 #### 前端初始化 1、Ant Design Pro使用的@3.1.0 react 、ts ```bash npm install -g @ant-design/pro-cli@3.1.0 ``` 2、项目瘦身 删除国际化 ```json "i18n-remove": "pro i18n-remove --locale=zh-CN --write", ``` 删除scr下的locales文件 删除components下的SelectLang ```ts export { AvatarDropdown, AvatarName, Footer, Question, SelectLang }; ``` 删除e2e(多段测试) #### 后端初始化 todo 自己写一个完整的v2.7的模板,用户的增删改查 ```sql -- 创建库 create database if not exists larly_api; -- 切换库 use larly_api; -- 用户表 create table if not exists user ( id bigint auto_increment comment 'id' primary key, userName varchar(256) null comment '用户昵称', userAccount varchar(256) not null comment '账号', userAvatar varchar(1024) null comment '用户头像', gender tinyint null comment '性别', userRole varchar(256) default 'user' not null comment '用户角色:user / admin', userPassword varchar(512) not null comment '密码', createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', isDelete tinyint default 0 not null comment '是否删除', constraint uni_userAccount unique (userAccount) ) comment '用户'; ```  #### 数据库表设计 id name 接口名称 descrpition 描述 url 接口地址 type 接口类型 requestHeader 请求头 responseHeader 响应头 status 状态 (0 - 关闭 ,1- 开启) conut 使用次数 isDelete createTime updateTime SQL网址:https://sqlfather.yupi.icu/ ```sql -- 接口信息表 create table if not exists larly_api.`interface_Info` ( `id` bigint not null auto_increment comment '主键' primary key, `name` varchar(256) not null comment '接口名称', `descrpition` varchar(256) null comment '描述', `requestHeader` text not null comment '请求头', `url` varchar(256) not null comment '接口地址', `method` varchar(256) not null comment '接口类型', `responeHeader` varchar(256) not null comment '响应头', `status` int default 0 not null comment '接口状态(0-关闭,1-开启)', `count` int default 0 not null comment '用户名', `createTime` datetime default CURRENT_TIMESTAMP not null comment '创建时间', `updateTime` datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', `isDeleted` tinyint default 0 not null comment '是否删除(0-未删, 1-已删)' ) comment '接口信息表'; ``` #### OPEN AI生成前端api接口的插件 使用apifox导出选项,已openAPI 导出json格式,在前端config中使用 ```ts //================ pro 插件配置 ================= presets: ['umi-presets-pro'], /** * @name openAPI 插件的配置 * @description 基于 openapi 的规范生成serve 和mock,能减少很多样板代码 * @doc https://pro.ant.design/zh-cn/docs/openapi/ */ openAPI: [ { requestLibPath: "import { request } from '@umijs/max'", schemaPath: 'http://127.0.0.1:4523/export/openapi/3?version=3.0', projectName: 'LarlyAPI_backend', }, ], mfsu: { strategy: 'normal', }, esbuildMinifyIIFE: true, requestRecord: {}, }); ``` 找到命令 ```json "openapi": "max openapi" ``` #### 前端登录 设置全局状态 每次页面加载获取 #### 表格渲染 ###### todo 优化第一个项目---用户中心 # 第二期 #### 前端优化 表格页面的增删改查 了解ProTable Promodal模块 了解Ant design Pro的全局状态管理 ```tsx import { PlusOutlined } from '@ant-design/icons'; import type { ActionType, ProColumns, ProDescriptionsItemProps } from '@ant-design/pro-components'; import { FooterToolbar, PageContainer, ProDescriptions, ProTable, } from '@ant-design/pro-components'; import '@umijs/max'; import { Button, Drawer, message } from 'antd'; import React, { useRef, useState } from 'react'; import type {SortOrder} from "antd/lib/table/interface"; import { getInterfaceInfoListPage, postInterfaceInfoAdd, postInterfaceInfoOpenApiDelete, postInterfaceInfoUpdate } from "@/services/larlyAPI_frontend/interfaceInfoController"; import CreateInterface from "@/pages/TableList/components/CreateInterface"; import UpdateInterface from "@/pages/TableList/components/UpdateInterface"; const TableList: React.FC = () => { /** * @en-US Pop-up window of new window * @zh-CN 新建窗口的弹窗 * */ const [createModalOpen, handleModalOpen] = useState<boolean>(false); /** * @en-US The pop-up window of the distribution update window * @zh-CN 分布更新窗口的弹窗 * */ const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false); const [showDetail, setShowDetail] = useState<boolean>(false); const actionRef = useRef<ActionType>(); const [currentRow, setCurrentRow] = useState<API.InterfaceInfoVo>(); const [selectedRowsState, setSelectedRows] = useState<API.InterfaceInfoVo[]>([]); /** * @en-US Add node * @zh-CN 添加节点 * @param fields */ const handleAdd = async (fields: API.InterfaceInfoVo) => { const hide = message.loading('正在添加'); try { await postInterfaceInfoAdd({ ...fields, }); handleModalOpen(false) message.success('Added successfully'); actionRef.current?.reload(); return true; } catch (error) { hide(); message.error('添加失败'); return false; } }; /** * @en-US Update node * @zh-CN 更新节点 * * @param fields */ const handleUpdate = async (fields: API.InterfaceInfoVo) => { const hide = message.loading('修改中。。。。'); console.log(currentRow) try { await postInterfaceInfoUpdate({ id: currentRow?.id, ...fields, }); hide(); handleUpdateModalOpen(false) message.success('Configuration is successful'); actionRef.current?.reload(); return true; } catch (error) { hide(); message.error('修改失败'); return false; } }; /** * Delete node * @zh-CN 删除节点 * * @param selectedRows */ const handleRemove = async (selectedRows: API.InterfaceInfoVo) => { const hide = message.loading('正在删除'); console.log(selectedRows) if (!selectedRows) return true; try { await postInterfaceInfoOpenApiDelete({ id: selectedRows.id }) hide(); message.success('删除成功'); actionRef.current?.reload(); return true; } catch (error) { hide(); message.error('删除失败'); return false; } }; const columns: ProColumns<API.InterfaceInfoVo>[] = [ { title: 'id', dataIndex: 'id', valueType: 'text', hideInForm: true, }, { title: '接口名称', dataIndex: 'name', valueType: 'text', }, { title: '描述', dataIndex: 'descrpition', valueType: 'textarea', }, { title: '请求头', dataIndex: 'requestHeader', valueType: 'textarea', }, { title: '接口地址', dataIndex: 'url', valueType: 'text', }, { title: '接口类型', dataIndex: 'method', valueType: 'text', }, { title: '响应头', dataIndex: 'responeHeader', valueType: 'textarea', }, { title: '使用次数', dataIndex: 'callNo', sorter: true, hideInForm: true, renderText: (val: string) => `${val}${'万'}`, }, { title: '接口状态', dataIndex: 'status', hideInForm: true, valueEnum: { 0: { text: '关闭', status: 'Default', }, 1: { text: '运行中', status: 'Processing', }, }, }, { title: '创建时间', sorter: true, dataIndex: 'createTime', valueType: 'dateTime', hideInForm: true, }, { title: '操作', dataIndex: 'option', hideInForm: true, valueType: 'option', render: (_, record) => [ <a key="config" onClick={() => { handleUpdateModalOpen(true); setCurrentRow(record); }} > 编辑 </a>, <a onClick={() => handleRemove(record)} key="subscribeAlert"> 删除 </a>, ], }, ]; return ( <PageContainer> <ProTable<API.RuleListItem, API.PageParams> headerTitle={'查询表格'} actionRef={actionRef} rowKey="id" search={{ labelWidth: 120, }} toolBarRender={() => [ <Button type="primary" key="primary" onClick={() => { handleModalOpen(true); }} > <PlusOutlined /> 新建 </Button>, ]} tableAlertOptionRender={false} request={ async (params: API.InterfaceInfoQueryRequest & { pageSize?: number; current?: number; keyword?: string; }, sort: Record<string, SortOrder>, filter: Record<string, (string | number)[] | null>) => { // 获取排序字段和顺序 const sortField = Object.keys(sort)?.[0]; const sortOrder = sort?.[sortField]; const res = await getInterfaceInfoListPage({ pageSize: params.pageSize, pageNum: params.current, name: params.keyword, status: params.status, method: params.method, url: params.url, descrpition: params.descrpition, sortField: sortField, sortOrder: sortOrder as 'asc' | 'desc' | undefined, },{}) const formattedList = res?.data?.list || [] return{ data: formattedList, success: true, total: res?.data?.total || 0, } } } columns={columns} rowSelection={{ onChange: (_, selectedRows) => { setSelectedRows(selectedRows); }, }} /> <Drawer width={600} open={showDetail} onClose={() => { setCurrentRow(undefined); setShowDetail(false); }} closable={false} > {currentRow?.name && ( <ProDescriptions<API.RuleListItem> column={2} title={currentRow?.name} request={async () => ({ data: currentRow || {}, })} params={{ id: currentRow?.name, }} columns={columns as ProDescriptionsItemProps<API.RuleListItem>[]} /> )} </Drawer> <UpdateInterface values={currentRow} columns={columns} onCancel={() => handleUpdateModalOpen(false)} onSubmit={handleUpdate} open={updateModalOpen}/> <CreateInterface columns={columns} onCancel={() => handleModalOpen(false) } onSubmit={handleAdd} open={createModalOpen}/> </PageContainer> ); }; export default TableList; ``` 编辑页面 ```tsx import { ProTable } from '@ant-design/pro-components'; import '@umijs/max'; import { Modal } from 'antd'; import React, {useEffect, useRef} from 'react'; import {ProFormInstance} from "@ant-design/pro-form/lib"; export type props = { values: API.InterfaceInfoVo | undefined columns: API.InterfaceInfoVo[] onCancel: () => void; onSubmit: (values:API.InterfaceInfoVo) => void; open: boolean; }; const UpdateInterface: React.FC<props> = (props) => { const { columns,open,onCancel,onSubmit,values} = props; const formRef = useRef<ProFormInstance>(); useEffect(() => { formRef?.current?.setFieldsValue(values); },[values]) return ( <Modal open={open} onCancel={() => onCancel()}> <ProTable type="form" formRef={formRef} form={ { initialValues: values } } columns={columns} onSubmit={async (values) => { onSubmit(values); }}/> </Modal> ) } export default UpdateInterface; ``` 新建页面 ```tsx import { ProTable } from '@ant-design/pro-components'; import '@umijs/max'; import { Modal } from 'antd'; import React, {useEffect} from 'react'; export type props = { columns: API.InterfaceInfoVo[] onCancel: () => void; onSubmit: (values:API.InterfaceInfoVo) => void; open: boolean; }; const CreateInterface: React.FC<props> = (props) => { const { columns,open,onCancel,onSubmit} = props; return ( <Modal open={open} onCancel={() => onCancel()}> <ProTable type="form" columns={columns} onSubmit={async (values) => { onSubmit(values); }}/> </Modal> ) } export default CreateInterface; ``` #### 后端模拟接口(三个) 1、GET 接口 2、POST (URL传参) 3、POST(对象传参) #### 调用接口(用程序) 几种HTTP调用方式: 1、HttpClient 2、RestTemplete 3、Hutool https://www.hutool.cn/docs/#/ 这里使用RestTemplte ```java package com.larly.project.larlyinterface.client; import com.larly.project.larlyinterface.model.User; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import java.util.HashMap; import java.util.Map; /** * 调用第三方接口的客户端 */ public class LarlyAPIClient { private static final String BASEURL = "http://localhost:8081/api"; // GET请求 public void getName(String name){ RestTemplate restTemplate = new RestTemplate(); // 使用 UriComponentsBuilder 构建带查询参数的 URL String url = UriComponentsBuilder.fromHttpUrl(BASEURL + "/name/") .queryParam("name", name) .toUriString(); ResponseEntity<String> result = restTemplate.getForEntity(url,String.class); System.out.println(result.getBody()); } // POST请求 public void postName(@RequestParam String name){ RestTemplate restTemplate = new RestTemplate(); // 使用 UriComponentsBuilder 构建带查询参数的 URL String url = UriComponentsBuilder.fromHttpUrl(BASEURL + "/name/") .queryParam("name", name) .toUriString(); // String url = BASEURL + "/name/"; // Map<String, String> params = new HashMap<>(); // params.put("name", name); ResponseEntity<String> result = restTemplate.postForEntity(url, null, String.class); System.out.println(result.getBody()); } // Post请求 public void postNameJSON(@RequestBody User user){ RestTemplate restTemplate = new RestTemplate(); String url = BASEURL + "/name/user"; ResponseEntity<String> response = restTemplate.postForEntity(url, user, String.class); // System.out.println("POST对象传参" + user.getName()); System.out.println(response.getBody()); } } ``` #### API签名认证(重要,重要) 本质: 1、签发签名 2、使用签名 ##### 实现(放在请求头中获取) 参数一:accessKey: 调用的识别 参数二:secretKey:密钥(该参数不能传递) (区别与用户名,密码:ak,sk无状态,用完一次就重置) ```java package com.larly.project.larlyinterface.client; import com.larly.project.larlyinterface.model.User; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import java.util.HashMap; import java.util.Map; /** * 调用第三方接口的客户端 */ public class LarlyAPIClient { /** * accessKey 和 secretKey */ private String accessKey; private String secretKey; public LarlyAPIClient(String accessKey, String secretKey) { this.accessKey = accessKey; this.secretKey = secretKey; } private static final String BASEURL = "http://localhost:8081/api"; // GET请求 public void getName(String name){ RestTemplate restTemplate = new RestTemplate(); // 使用 UriComponentsBuilder 构建带查询参数的 URL String url = UriComponentsBuilder.fromHttpUrl(BASEURL + "/name/") .queryParam("name", name) .toUriString(); ResponseEntity<String> result = restTemplate.getForEntity(url,String.class); System.out.println(result.getBody()); } // POST请求 public void postName(@RequestParam String name){ RestTemplate restTemplate = new RestTemplate(); // 使用 UriComponentsBuilder 构建带查询参数的 URL String url = UriComponentsBuilder.fromHttpUrl(BASEURL + "/name/") .queryParam("name", name) .toUriString(); // String url = BASEURL + "/name/"; // Map<String, String> params = new HashMap<>(); // params.put("name", name); ResponseEntity<String> result = restTemplate.postForEntity(url, null, String.class); System.out.println(result.getBody()); } // public MultiValueMap<String,String> getHeaderMap(String accessKey, String secretKey){ // MultiValueMap<String,String> headerMap = new LinkedMultiValueMap<>(); // headerMap.add("accessKey", accessKey); // headerMap.add("secretKey", secretKey); // return headerMap; // } // Post请求 public void postNameJSON(@RequestBody User user){ RestTemplate restTemplate = new RestTemplate(); String url = BASEURL + "/name/user"; HttpHeaders httpHeaders = new HttpHeaders(); // httpHeaders.addAll(getHeaderMap(accessKey, secretKey)); httpHeaders.add("accessKey", accessKey); httpHeaders.add("secretKey", secretKey); HttpEntity<User> request = new HttpEntity<>(user, httpHeaders); ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class); // // 发送 POST 请求,user 会被自动序列化为 JSON // ResponseEntity<String> response = restTemplate.postForEntity(url, user, String.class); // System.out.println("POST对象传参" + user.getName()); System.out.println(response.getBody()); } } ``` ```java @PostMapping("/user") public String postName(@RequestBody User user, HttpServletRequest request){ String accessKey = request.getHeader("accessKey"); String secretKey = request.getHeader("secretKey"); if(!"larly".equals(accessKey)){ throw new Error("accessKey错误"); } if(!"123456".equals(secretKey)){ throw new Error("secretKey错误"); } return "POST对象传参" + user.getName(); } ``` ##### 不可以(密码不能明文传递,有可能被拦截) 加密方式:对称加密(根据密钥可以加密解密),非对称加密(公钥加密,私钥解密),单向签名(不可解密MD5) 参数五:用户参数 参数四:sign 用户参数+密钥 => 签名算法=> 不可解迷 后端怎么知道? 服务器用一模一样的加密方式加密对比 ##### 为了防止用户重复调用 参数五:加一个随机数:后端使用后就清除,只能用一次 参数六:时间戳:可以设置过期时间timestamp 服务端 ```java public String postName(@RequestBody User user, HttpServletRequest request){ String accessKey = request.getHeader("accessKey"); String timestamp = request.getHeader("timestamp"); String sign = request.getHeader("sign"); String nonce = request.getHeader("nonce"); String data = request.getHeader("data"); if(!"larly".equals(accessKey)){ throw new Error("accessKey错误"); } // todo 时间戳验证, 有效时间多少分 // todo 随机数验证, 防重放(数据库查) // secretKey实际上从数据库中取出来 String serveSign = SignUtils.getSign(data, "1234567"); if(!serveSign.equals(sign)){ throw new Error("sign错误"); } return "POST对象传参" + user.getName(); } ``` 客户端 (其实这里我感觉还是不安全,这个客户端还是保存secretKey,只不过不是在传递过程中明文发送) ```java public void postNameJSON(@RequestBody User user){ RestTemplate restTemplate = new RestTemplate(); String url = BASEURL + "/name/user"; HttpHeaders httpHeaders = new HttpHeaders(); // httpHeaders.addAll(getHeaderMap(accessKey, secretKey)); httpHeaders.add("accessKey", accessKey); // httpHeaders.add("secretKey", secretKey); // 添加用户数据 httpHeaders.add("data", user.toString()); // 添加随机数(防重放) httpHeaders.add("nonce", String.valueOf((int) (Math.random() * 1000000000))); // 加时间戳 httpHeaders.add("timestamp", String.valueOf(System.currentTimeMillis())); // 加sign httpHeaders.add("sign", SignUtils.getSign(user.toString(), secretKey)); HttpEntity<User> request = new HttpEntity<>(user, httpHeaders); ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class); // // 发送 POST 请求,user 会被自动序列化为 JSON // ResponseEntity<String> response = restTemplate.postForEntity(url, user, String.class); // System.out.println("POST对象传参" + user.getName()); System.out.println(response.getBody()); } ``` MD5加密sign ```java public static String getSign(String data , String secretKey) { // md5加密 Mac mac = null; try { mac = Mac.getInstance("HmacSHA256"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256"); try { mac.init(keySpec); } catch (InvalidKeyException e) { throw new RuntimeException(e); } byte[] result = mac.doFinal(data.getBytes()); return Base64.getEncoder().encodeToString(result); } ``` #### 开发一个简单的SDK 开发者只用传递接口,不需要加密sign 1、新建项目 larly-client-sdk 删除maven自带的build打包工具,防止生成jar包 spring-boot-configuration-processor:帮助生成代码提示 新建META-INFO配置spring.factories文件 ``` org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.larly.larlyclientsdk.LarlyAPIClientConfig(你的sdk包名) ``` maven install(会下载到你本地的maven仓库)  再别的项目中引入你的依赖,就可以使用  ```java package com.larly.project.larlyinterface; import com.larly.larlyclientsdk.client.LarlyClient; import com.larly.larlyclientsdk.model.User; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; @SpringBootTest class LarlyInterfaceApplicationTests { @Resource private LarlyClient larlyClient; @Test void contextLoads() { String larly = larlyClient.getNameByGet("larly"); System.out.println(larly); String larly1 = larlyClient.getNameByPost("larly"); System.out.println(larly1); User user = new User(); user.setName("larly"); String larly2 = larlyClient.getUserNameByPost(user); System.out.println(larly2); } } ``` # 第三期 #### 开发接口发布/下线(管理员) 发布: 1、判断接口是否存在 2、接口是否可以使用 3、修改状态 下线: 1、判断接口是否存在 2、修改状态 #### 前端浏览接口、查看接口文档(接口信息) 在管理员列表展示(发布、下线) 路由传参 #### 申请签名(注册) 用户在注册成功。自动分配accessKey、secretKey post # 第四期 #### 开发接口次数的统计 需求:用户每次调用接口成功,次数+1 1、接口用户关系表 ```sql -- 用户调用接口关系表 create table if not exists yuapi.`user_interface_info` ( `id` bigint not null auto_increment comment '主键' primary key, `userId` bigint not null comment '调用用户 id', `interfaceInfoId` bigint not null comment '接口 id', `totalNum` int default 0 not null comment '总调用次数', `leftNum` int default 0 not null comment '剩余调用次数', `status` int default 0 not null comment '0-正常,1-禁用', `createTime` datetime default CURRENT_TIMESTAMP not null comment '创建时间', `updateTime` datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', `isDelete` tinyint default 0 not null comment '是否删除(0-未删, 1-已删)' ) comment '用户调用接口关系'; -- 修改接口信息表 create table interface_info ( id bigint auto_increment comment '主键' primary key, name varchar(256) not null comment '接口名称', descrpition varchar(256) null comment '描述', requestHeader text not null comment '请求头', url varchar(256) not null comment '接口地址', method varchar(256) not null comment '接口类型', responeHeader varchar(256) not null comment '响应头', status int default 0 not null comment '接口状态(0-关闭,1-开启)', count int default 0 not null comment '用户名', createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', isDeleted tinyint default 0 not null comment '是否删除(0-未删, 1-已删)', userId bigint not null comment '创建人', totalNum int default 0 not null comment '总次数', leftNum int default 0 not null comment '剩余次数' ) comment '接口信息表'; ``` 2、增删改查(给管理员使用) 每个接口成功都要+1,使用API网关可以抽离出来(AOP编程/API网关) AOP缺点:只能在一个项目中使用 #### 优化整个系统的构架(API网关) 什么是网关?理解成火车的中转站,**统一**去检票 1、路由 2、鉴权 3、跨域 4、缓存 5、流量染色 6、访问控制 7、统一的业务处理 8、发布控制 9、脱敏 10、负载均衡 11、接口保护 a、限制请求 c、降级(熔断) d、限流 12、统一日志 13、统一文档 ###### 路由 转发作用,用户访问接口A,B网关会记录,并且会转发到对用的接口 /a=> 接口A /b=> 接口B ###### 负载均衡 路由基础上 /a=>集群A/服务A ###### 统一鉴权 统一的鉴权 ###### 统一处理跨域 网关统一处理跨域 ###### 统一业务处理 把一些统一的业务逻辑放到网关。 ###### 访问控制(黑白名单) 限制DDOS IP ###### 发布控制 发布灰度,比如一个新接口测试分布流量20%,旧接口使用80% ###### 流量染色 给请求(流量)添加一些标识一般在请求头,来区分一些东西,或者过滤,比如为了防止绕过网关,流量在经过网关会添加请求头 ###### 统一的日志 统一的请求,响应输入 ###### 统一的文档 把不同接口的文档,通义在一起 #### 网关的分类 1、业务网关(多个项目,多个微服务基础上):处理一些业务逻辑,作用是将请求转发到不同的业务/项目/接口 2、全局网关:负载均衡,作用是请求日志。。。 #### 实现 1、Nginx(全局网关)、Kong网关(API网关),编程成本高 2、Spring Cloud Gateway(取代了zuul)性能高、可以使用java #### Spring Cloud Gateway 用法 中文网:https://springdoc.cn/spring-cloud-gateway/#google_vignette router(路由): 根据什么条件,转发请求 predicate(断言):一组规则,用来确定如何转发路由(请求地址) filter(过滤器):对请求进行处理,比如添加请求头,鉴权  Gateway Client: 发送请求的客户端 GatewayHabdler Mapping:根据断言,转发请求 Gateway Web Handler:处理请求,过滤器 ###### 两种方式 1、配置式 ```yml spring: cloud: gateway: routes: - id: after_route uri: https://example.org predicates: - Cookie=mycookie,mycookievalue ``` 2、编程式 #### 建议开启日志 ```yml logging: level: org: springframework: cloud: gateway: trace ``` #### 断言 1、After:在xx时间之后执行 2、Before:在xx时间之前执行 3、Between:在xx时间之间执行 4、请求类型 5、请求头(包含Cookie,鉴权) 6、查询参数 7、客户端地址(黑白名单) 8、**权重**(版本控制) #### 过滤器 1、添加请求头 2、添加请求参数 3、添加响应头 4、降级 5、限流 6、重试 ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId> </dependency> ``` # 第五期 #### 把API网关运用到项目 1、实现统一的接口的鉴权、计费、调用次数 #### 项目使用的特性 1、路由(转发到接口) 2、鉴权(acessKey、 secretKey) 3、流量染色 (记录是否是从网管来的。添加请求头标识) 4、访问控制(黑白名单) 5、统一的业务处理 (每次接口调用后。次数加1) 6、接口保护 a、限制请求 c、降级(熔断) d、限流 7、统一日志(记录每次的请求和响应日志) #### 业务逻辑 1、用户发送请求到 API 网关 2、请求日志 3、用户鉴权(ak,sk是否合法) 4、判断接口是否存在 5、添加唯一标识 6、请求转发到接口 7、相应日志 8、调用次数加1 #### 实现 ##### 1、请求转发 ##### http://localhost:8081/api/name/?name=xhd 接口地址 API网关跳转: http://localhost:8090/api/** => http://localhost:8081/api/name/?name=xhd ```yaml #设置路由 spring: cloud: gateway: routes: - id: api_route # 测试接口 uri: http://localhost:8081 predicates: - Path=/api/** ``` uri: http://localhost:8081跳转时会带上path路径 ##### 2、编写业务逻辑 使用全局过滤器(编程式), 全局拦截处理拦截(类似AOP) ```java package com.larly.apigateway; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import com.larly.larlyclientsdk.utils.SignUtils; import java.util.UUID; @Component @Slf4j /** * 全局过滤器 */ public class CustomGlobalFilter implements GlobalFilter, Ordered { // 1、用户发送请求到 API 网关 @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 2、请求日志 ServerHttpRequest request = exchange.getRequest(); log.info("请求唯一id:{},请求方法:{},请求路径:{}",request.getId(),request.getMethod(),request.getPath()); log.info("请求参数:{}",request.getQueryParams().values()); // 3、用户鉴权(ak,sk是否合法) String accessKey = request.getHeaders().getFirst("accessKey"); String timestamp = request.getHeaders().getFirst("timestamp"); String sign = request.getHeaders().getFirst("sign"); String nonce = request.getHeaders().getFirst("nonce"); String data = request.getHeaders().getFirst("data"); if(!"larly".equals(accessKey)){ throw new Error("accessKey错误"); } // todo 时间戳验证, 有效时间多少分 // todo 随机数验证, 防重放(数据库查) // secretKey实际上从数据库中取出来 String serveSign = SignUtils.getSign(data, "1234567"); if(!serveSign.equals(sign)){ throw new Error("sign错误"); } // 4、判断接口是否存在 // todo 调用api-backend 接口 获取结果 // 5、添加唯一标识 String traceId = UUID.randomUUID().toString(); ServerHttpRequest newRequest = exchange.getRequest().mutate() .header("X-Trace-ID", traceId) .build(); // 7、相应日志 ServerHttpResponse response = exchange.getResponse(); log.info("响应结果:{}",response.getStatusCode()); // todo 8、调用次数加1 // 6、请求转发到接口 return chain.filter(exchange.mutate().request(newRequest).build()); } @Override public int getOrder() { return -1; } } ``` 注意:这是一个异步的操作,直到filter过滤器return才会执行接口 解决方法:利用response装饰者,增强原有的response 处理一下response结果 ```java /** * 处理响应 * * @param exchange * @param chain * @return */ public Mono<Void> handleResponse(ServerWebExchange exchange, GatewayFilterChain chain) { try { // 获取原始的响应对象 ServerHttpResponse originalResponse = exchange.getResponse(); // 获取数据缓冲工厂 DataBufferFactory bufferFactory = originalResponse.bufferFactory(); // 获取响应的状态码 HttpStatus statusCode = originalResponse.getStatusCode(); // 判断状态码是否为200 OK(按道理来说,现在没有调用,是拿不到响应码的,对这个保持怀疑 沉思.jpg) if(statusCode == HttpStatus.OK) { // 创建一个装饰后的响应对象(开始穿装备,增强能力) ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { // 重写writeWith方法,用于处理响应体的数据 // 这段方法就是只要当我们的模拟接口调用完成之后,等它返回结果, // 就会调用writeWith方法,我们就能根据响应结果做一些自己的处理 @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { log.info("body instanceof Flux: {}", (body instanceof Flux)); // 判断响应体是否是Flux类型 if (body instanceof Flux) { Flux<? extends DataBuffer> fluxBody = Flux.from(body); // 返回一个处理后的响应体 // (这里就理解为它在拼接字符串,它把缓冲区的数据取出来,一点一点拼接好) return super.writeWith(fluxBody.map(dataBuffer -> { // 读取响应体的内容并转换为字节数组 byte[] content = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(content); DataBufferUtils.release(dataBuffer);//释放掉内存 // 构建日志 StringBuilder sb2 = new StringBuilder(200); sb2.append("<--- {} {} \n"); List<Object> rspArgs = new ArrayList<>(); rspArgs.add(originalResponse.getStatusCode()); //rspArgs.add(requestUrl); String data = new String(content, StandardCharsets.UTF_8);//data sb2.append(data); log.info(sb2.toString(), rspArgs.toArray());//log.info("<-- {} {}\n", originalResponse.getStatusCode(), data); // 将处理后的内容重新包装成DataBuffer并返回 return bufferFactory.wrap(content); })); } else { log.error("<--- {} 响应code异常", getStatusCode()); } return super.writeWith(body); } }; // 对于200 OK的请求,将装饰后的响应对象传递给下一个过滤器链,并继续处理(设置repsonse对象为装饰过的) return chain.filter(exchange.mutate().response(decoratedResponse).build()); } // 对于非200 OK的请求,直接返回,进行降级处理 return chain.filter(exchange); }catch (Exception e){ // 处理异常情况,记录错误日志 log.error("gateway log exception.\n" + e); return chain.filter(exchange); } } ``` # 第六期 #### 计划 1、补充完成网关的业务逻辑(操作数据库,复用之前的方法) 2、完善系统、开发一个监控统计功网管 #### 业务逻辑 问题:网管项目比较纯净,没有操作数据库的包,并且调用之前写过的方法 理想:直接请求其他方法 ##### 怎么调用其他项目 1、复制代码 2、HTTP请求(提供接口) 3、RPC 4、把公共的代码打个jar包,其他项目引用(客户端SDK) #### HTTP请求怎么调用 1、提供方开发一个接口 2、调用方使用HTTP Client发送请求 #### RPC 作用:像调用本地方法一样调用远程方法 假设你在项目A编写了一个方法想在其他项目中使用,可以使用RPC #### 和直接调用HTTP区别 1、对开发者透明,开发者不用写接口地址,请求方法 2、底层是向远程服务器发送请求,不一定使用HTTP,或者使用一些协议TCP #### PRC实现步骤 提供者将地址和方法注册到注册中心,消费者在使用时向注册中心要地址和方法,调用方直接调用提供者的方法,不经过注册中心  #### Feign Feign本质上也是HTTP客户端,他只是精简了HTTP请求的步骤 #### Dubbo框架(RPC实现) https://cn.dubbo.apache.org/zh-cn/overview/mannual/java-sdk/quick-start/starter/ ##### 两种使用方式: 1、Spring Boot代码(注解+编程式):写java接口,服务提供者实现这个接口,消费者调用 2、IDL(接口调用语言):创建一个公共的接口定义文件,服务提供者和消费者读取这个文件。优点式跨语言,所有框架都能识别 ##### 应用 1、backend作为服务提供者 2、三个方法:a、从数据库中获取用户ak,sk b、判断接口是否存在 c、调用接口成功后,次数+1 3、gateway作为消费者 ##### Zookeeper和Nacos Zookeeper作为一个注册中心,Dubbo的作用就是将消费者、提供者、注册中心三者通过其框架整合在一起 Nacos也可以作为注册中心(推荐) #### Nacos https://nacos.io/docs/next/quickstart/quick-start/#0-%E7%89%88%E6%9C%AC%E9%80%89%E6%8B%A9 需要java8+ bin目录下 ```bash startup.cmd -m standalone ``` 添加依赖 ```xml <!-- Dubbo --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>3.3.0</version> </dependency> <!-- Nacos --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-nacos-spring-boot-starter</artifactId> <version>3.3.0</version> </dependency> ``` 添加配置 ```yml # 以下配置指定了应用的名称、使用的协议(Dubbo)、注册中心的类型(Nacos)和地址 dubbo: application: # 设置应用的名称 name: dubbo-springboot-demo-provider # 指定使用 Dubbo 协议,且端口设置为 -1,表示随机分配可用端口 protocol: name: dubbo port: -1 registry: # 配置注册中心为 Nacos,使用的地址是 nacos://localhost:8848 id: nacos-registry address: nacos://localhost:8848 ``` ##### 提供者(backend)  ##### 消费者(gateway)  ##### 注意:消费者的包的地址要和注册中心的提供者包的地址一样,不然消费者会找不到提供者对应的包 # 第七期 1、完成网管业务逻辑 2、上线 #### 封装总的业务逻辑 ##### larly-common:使用install安装,并在backend和gateway引入依赖 1、获取用户的accessKey(accessKey,用户对象) 2、判断接口是否存在(接口名称,接口请求类型,接口对象) 3、接口调用完成次数加一(接口id,用户id)
2025年了,还有人在做api开放平台项目吗😇 借鉴了很多大佬的经验,欢迎把玩我上线的网站:https://www.ymcapi.xyz 前端仓库地址:https://github.com/Sm1leYc/Cxm-Api-fronted 文档仓库地址:https://github.com/Sm1leYc/Cxm-Api-Doc 后端仓库地址:https://github.com/Sm1leYc/Cxm-Api SDK仓库地址:https://github.com/Sm1leYc/cxm-sdk
项目求助 | 已解决 项目:API开放平台 | 本地部署前端项目 报错信息:npmERR!code 1,command C:\Windows\system32\cmd.exe /d /s /c husky install 请问有人知道解决吗
项目求助 | 已解决 1. 伙伴匹配项目 最近做完了伙伴匹配项目后端部分,但是出现了问题 通过swagger测试可以得到后端传来的数据,但是前端运行后,通过浏览器调试展示队伍功能,显示无法加载项响应数据 2. API开放平台项目 看了下介绍,API开放平台涉及到Spring Cloud,但是这部分没学过,有没有好入门的视频推荐,可以边学边做API开放平台项目
API 前端本地运行&部署教程
# 环境准备 + [API 源码](https://www.codefather.cn/course/1790979723916521474/section/1790982803160608769?type=) + node >= **18** 推荐使用 [NVM 安装](https://www.codefather.cn/post/1823595688926167041) + yarn [官方链接](https://classic.yarnpkg.com/lang/en/docs/install/#windows-stable) [百度网盘](https://pan.baidu.com/s/1TexTr-PHE5sejjq6795oVg) 密码:958c + WebStorm # 本地运行 ## 安装依赖 1、使用 WebStorm 打开(VSCode 也是可以的) 用 WebStorm 打开前端源码 <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/VHZZNgQ8h3WVXsKZ.webp" alt="image-20250223170905331" width="100%" /> > 会弹出一个是否相信,选择相信即可 <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/hzi8BuWB3vflVUlU.webp" alt="image-20250223171001758" width="100%" /> 2、找到 `Terminal` 选项 --- 如果没有看到看这里有没有 <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/7Cx1tIUNzkpOjaH2.webp" alt="image-20241114101254432" width="471px" /> 还是没有的话找到 `Settings` -> `plugins` -> `Terminal` <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/ku7YWr2Tu1Ho7w7z.webp" alt="image-20241114101344618" width="334px" /> <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/ld3HyrQiLnZuQOVX.webp" alt="image-20241114101433358" width="100%" /> --- 3、执行 ``` npm install --force ``` <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/rH8NT5ezcLcFpi8F.webp" alt="image-20250223171201584" width="100%" /> 最后执行效果: <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/MLVHDi3qOXfcONUw.webp" alt="image-20250223172333989" width="100%" /> ## 运行前端 找到根目录下的 `pakage.json` <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/15AFBtGqUoEWQidj.webp" alt="image-20250223172510968" width="100%" /> <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/k2ISiuBFc92H79t2.webp" alt="image-20250223172530825" width="688px" /> 启动成功 > 正常使用需要启动后端,后端的启动可以参考 https://www.codefather.cn/post/1891368782725439489 <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/4eoQrzzcWq4itkNX.webp" alt="image-20250223174249420" width="100%" /> <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/tIGZqgmKnzeg4Hz6.webp" alt="image-20250223175329945" width="100%" /> **报错** ``` error - MFSU dist file: E:\TEMPLATE\API-TEMPT\yuapi-frontend-master\node_modules\.cache\mfsu\mf-va_remoteEntry.js not found error - MFSU latest build error: AssertionError [ERR_ASSERTION]: dependence not found: echarts-for-react ``` 解决:安装 echarts-for-react,安装成功之后重启前端 <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/qYARHGeKqqpXM7mD.webp" alt="image-20250223173900574" width="100%" /> ``` npm i echarts-for-react@3.0.2 ``` 成功安装结果 <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/n4AZ8EYM45B8isG0.webp" alt="image-20250223173929391" width="100%" /> --- pakage.json ``` "scripts": { "analyze": "cross-env ANALYZE=1 max build", "build": "max build", "deploy": "npm run build && npm run gh-pages", "dev": "npm run start:dev", "gh-pages": "gh-pages -d dist", "i18n-remove": "pro i18n-remove --locale=zh-CN --write", "postinstall": "max setup", "lint": "npm run lint:js && npm run lint:prettier && npm run tsc", "lint-staged": "lint-staged", "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ", "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src ", "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", "lint:prettier": "prettier -c --write \"src/**/*\" --end-of-line auto", "openapi": "max openapi", "prepare": "husky install", "prettier": "prettier -c --write \"src/**/*\"", "serve": "umi-serve", "start": "cross-env UMI_ENV=dev max dev", "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev", "start:no-mock": "cross-env MOCK=none UMI_ENV=dev max dev", "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev", "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev", "tsc": "tsc --noEmit" }, ``` 1. `dev` 开发化境启动,会**热更新**(如果有修改会立刻体现) 2. `buid` 构建模式,会生成 dist 文件方便前端部署 3. `lint` 代码检查 4. `prettier` 代码格式化 ## 修改请求路径 修改 `baseURL` 就是前端请求后端的路径 `src/requestConfig.ts` <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/Fj08ZFePgdqCBFWt.webp" alt="image-20250223172836344" width="100%" /> 本地就有可以写 localhost 当我们部署到服务器上 baseURL 可以写 192.122.11.11:端口 (自己实际的 IP)或者是自己的域名 + http://localhost:8101 + http://192.122.11.11:8101 + http://leikooo.com:8101 域名相当于平替 IP + http://leikooo.com 没有端口?别慌 `http` 默认请求`80` 端口只需要在 `Nginx` 进行`反向代理`即可 + https://leikooo.com 和上面的区别?这个是 `https` 安全的连接,默认端口也变成 `443` ## 打包 <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/bwTxf1IHGjDIaLv1.webp" alt="image-20250223174627397" width="100%" /> <img src="https://pic.code-nav.cn/post_picture/1608460212774109186/fZXTc4R50YktLM4E.webp" alt="image-20250223174710752" width="100%" /> build 成功之后,可以在发现 dist 目录  # 部署 之后可以参考[『 前端部署教程(通用) 』](https://www.codefather.cn/post/1836710450264096769)

