Files
hm-server-v2/app/main/api/internal/service/apiRegistryService.go
2025-10-07 11:48:29 +08:00

224 lines
5.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"context"
"fmt"
"regexp"
"strings"
"hm-server/app/main/model"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/rest"
)
type ApiRegistryService struct {
adminApiModel model.AdminApiModel
}
func NewApiRegistryService(adminApiModel model.AdminApiModel) *ApiRegistryService {
return &ApiRegistryService{
adminApiModel: adminApiModel,
}
}
// RegisterAllApis 自动注册所有API到数据库
func (s *ApiRegistryService) RegisterAllApis(ctx context.Context, routes []rest.Route) error {
logx.Infof("开始注册API共 %d 个路由", len(routes))
registeredCount := 0
skippedCount := 0
for _, route := range routes {
// 跳过不需要权限控制的API
if s.shouldSkipApi(route.Path) {
skippedCount++
continue
}
// 解析API信息
apiInfo := s.parseRouteToApi(route)
// 检查是否已存在
existing, err := s.adminApiModel.FindOneByApiCode(ctx, apiInfo.ApiCode)
if err != nil && !errors.Is(err, model.ErrNotFound) {
logx.Errorf("查询API失败: %v, apiCode: %s", err, apiInfo.ApiCode)
continue
}
// 如果不存在则插入
if existing == nil {
_, err = s.adminApiModel.Insert(ctx, nil, apiInfo)
if err != nil {
logx.Errorf("插入API失败: %v, apiCode: %s", err, apiInfo.ApiCode)
continue
}
registeredCount++
logx.Infof("注册API成功: %s %s", apiInfo.Method, apiInfo.Url)
} else {
// 如果存在但信息有变化,则更新
if s.shouldUpdateApi(existing, apiInfo) {
existing.ApiName = apiInfo.ApiName
existing.Method = apiInfo.Method
existing.Url = apiInfo.Url
existing.Description = apiInfo.Description
_, err = s.adminApiModel.Update(ctx, nil, existing)
if err != nil {
logx.Errorf("更新API失败: %v, apiCode: %s", err, apiInfo.ApiCode)
continue
}
logx.Infof("更新API成功: %s %s", apiInfo.Method, apiInfo.Url)
}
}
}
logx.Infof("API注册完成新增: %d, 跳过: %d", registeredCount, skippedCount)
return nil
}
// shouldSkipApi 判断是否应该跳过此API
func (s *ApiRegistryService) shouldSkipApi(path string) bool {
// 跳过公开API
skipPaths := []string{
"/api/v1/admin/auth/login", // 登录接口
"/api/v1/app/", // 前端应用接口
"/api/v1/agent/", // 代理接口
"/api/v1/user/", // 用户接口
"/api/v1/auth/", // 认证接口
"/api/v1/notification/", // 通知接口
"/api/v1/pay/", // 支付接口
"/api/v1/query/", // 查询接口
"/api/v1/product/", // 产品接口
"/api/v1/authorization/", // 授权接口
"/health", // 健康检查
}
for _, skipPath := range skipPaths {
if strings.HasPrefix(path, skipPath) {
return true
}
}
return false
}
// parseRouteToApi 将路由解析为API信息
func (s *ApiRegistryService) parseRouteToApi(route rest.Route) *model.AdminApi {
// 生成API编码
apiCode := s.generateApiCode(route.Method, route.Path)
// 生成API名称
apiName := s.generateApiName(route.Path)
// 生成描述
description := s.generateDescription(route.Method, route.Path)
return &model.AdminApi{
ApiName: apiName,
ApiCode: apiCode,
Method: route.Method,
Url: route.Path,
Status: 1, // 默认启用
Description: description,
}
}
// generateApiCode 生成API编码
func (s *ApiRegistryService) generateApiCode(method, path string) string {
// 移除路径参数,如 :id
cleanPath := regexp.MustCompile(`/:[\w]+`).ReplaceAllString(path, "")
// 转换为小写并替换特殊字符
apiCode := strings.ToLower(method) + "_" + strings.ReplaceAll(cleanPath, "/", "_")
apiCode = strings.TrimPrefix(apiCode, "_")
apiCode = strings.TrimSuffix(apiCode, "_")
return apiCode
}
// generateApiName 生成API名称
func (s *ApiRegistryService) generateApiName(path string) string {
// 从路径中提取模块和操作
parts := strings.Split(strings.Trim(path, "/"), "/")
if len(parts) < 3 {
return path
}
// 获取模块名和操作名
module := parts[len(parts)-2]
action := parts[len(parts)-1]
// 转换为中文描述
moduleMap := map[string]string{
"agent": "代理管理",
"auth": "认证管理",
"feature": "功能管理",
"menu": "菜单管理",
"notification": "通知管理",
"order": "订单管理",
"platform_user": "平台用户",
"product": "产品管理",
"promotion": "推广管理",
"query": "查询管理",
"role": "角色管理",
"user": "用户管理",
}
actionMap := map[string]string{
"list": "列表",
"create": "创建",
"update": "更新",
"delete": "删除",
"detail": "详情",
"login": "登录",
"config": "配置",
"example": "示例",
"refund": "退款",
"link": "链接",
"stats": "统计",
"cleanup": "清理",
"record": "记录",
}
moduleName := moduleMap[module]
if moduleName == "" {
moduleName = module
}
actionName := actionMap[action]
if actionName == "" {
actionName = action
}
return fmt.Sprintf("%s-%s", moduleName, actionName)
}
// generateDescription 生成API描述
func (s *ApiRegistryService) generateDescription(method, path string) string {
methodMap := map[string]string{
"GET": "查询",
"POST": "创建",
"PUT": "更新",
"DELETE": "删除",
}
methodDesc := methodMap[method]
if methodDesc == "" {
methodDesc = method
}
apiName := s.generateApiName(path)
return fmt.Sprintf("%s%s", methodDesc, apiName)
}
// shouldUpdateApi 判断是否需要更新API
func (s *ApiRegistryService) shouldUpdateApi(existing, new *model.AdminApi) bool {
return existing.ApiName != new.ApiName ||
existing.Method != new.Method ||
existing.Url != new.Url ||
existing.Description != new.Description
}