Browse Source

refactor(internal/service): 重构 AoDun 服务中的 HTTP 请求逻辑

- 新增共享的 http.Client 实例,提高性能和复用性
- 使用 context 传递到 HTTP 请求中,支持请求取消- 修复 token 设置逻辑,仅在 token 不为空时设置 Authorization头
- 封装认证请求流程,简化重复的 token 获取和请求发送过程
- 优化代码结构,提高可读性和可维护性
fusu 1 month ago
parent
commit
164b5954fa
2 changed files with 77 additions and 78 deletions
  1. 76 78
      internal/service/aodun.go
  2. 1 0
      internal/service/webforwarding.go

+ 76 - 78
internal/service/aodun.go

@@ -23,68 +23,75 @@ type AoDunService interface {
 	GetWhiteStaticList(ctx context.Context,ip string) (int,error)
 }
 func NewAoDunService(
-    service *Service,
+	service *Service,
 	conf *viper.Viper,
-
 ) AoDunService {
+	// 1. 创建一个可复用的 Transport,并配置好 TLS 和其他参数
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 忽略 SSL 验证
+		MaxIdleConns:    100,                               // 最大空闲连接数
+		IdleConnTimeout: 90 * time.Second,                // 空闲连接超时时间
+	}
+
+	// 2. 基于该 Transport 创建一个可复用的 http.Client
+	client := &http.Client{
+		Transport: tr,
+		Timeout:   15 * time.Second, // 设置所有请求的默认超时时间
+	}
+
 	return &aoDunService{
 		Service:        service,
-		Url:                       conf.GetString("aodun.Url"),
-		clientID:          conf.GetString("aodun.clientID"),
-		username:          conf.GetString("aodun.username"),
-		password:          conf.GetString("aodun.password"),
-		IPusername:        conf.GetString("aodunIp.username"),
-		IPpassword:        conf.GetString("aodunIp.password"),
-		domainUserName:    conf.GetString("domainWhite.username"),
-		domainPassword:    conf.GetString("domainWhite.password"),
-
+		Url:            conf.GetString("aodun.Url"),
+		clientID:       conf.GetString("aodun.clientID"),
+		username:       conf.GetString("aodun.username"),
+		password:       conf.GetString("aodun.password"),
+		IPusername:     conf.GetString("aodunIp.username"),
+		IPpassword:     conf.GetString("aodunIp.password"),
+		domainUserName: conf.GetString("domainWhite.username"),
+		domainPassword: conf.GetString("domainWhite.password"),
+		httpClient:     client, // 存储共享的 client
 	}
 }
 
 type aoDunService struct {
 	*Service
-	Url string
-	clientID string
-	username string
-	password string
-	IPusername string
-	IPpassword string
+	Url            string
+	clientID       string
+	username       string
+	password       string
+	IPusername     string
+	IPpassword     string
 	domainUserName string
 	domainPassword string
+	httpClient     *http.Client // <--- 新增 http client 字段
 }
 
 
-func (s *aoDunService) sendFormData(ctx context.Context,apiUrl string,tokenType string,token string,formData map[string]interface{})  ([]byte,error) {
+func (s *aoDunService) sendFormData(ctx context.Context, apiUrl string, tokenType string, token string, formData map[string]interface{}) ([]byte, error) {
 	URL := s.Url + apiUrl
 	jsonData, err := json.Marshal(formData)
 	if err != nil {
 		return nil, fmt.Errorf("序列化请求数据失败: %w", err)
 	}
-	req, err := http.NewRequest("POST", URL, bytes.NewBuffer(jsonData))
+
+	// 使用带有 context 的请求,以便上游可以控制请求的取消
+	req, err := http.NewRequestWithContext(ctx, "POST", URL, bytes.NewBuffer(jsonData))
 	if err != nil {
 		return nil, fmt.Errorf("创建 HTTP 请求失败: %w", err)
 	}
-	// 设置请求头 Content-Type 为 "application/json"
-	req.Header.Set("Content-Type", "application/json")
-	if tokenType == "" {
-		req.Header.Set("Authorization", tokenType + " " + token)
-	}
 
-
-	tr := &http.Transport{
-		TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // <--- 关键修改:忽略 SSL 验证
+	// 设置请求头
+	req.Header.Set("Content-Type", "application/json")
+	// 修正逻辑:当 token 不为空时才设置 Authorization
+	if token != "" {
+		req.Header.Set("Authorization", tokenType+" "+token)
 	}
 
-	// 5. 使用 HTTP 客户端发送请求
-	client := &http.Client{
-		Transport: tr,
-		Timeout: 15 * time.Second, // 设置一个合理的超时时间,例如15秒
-	}
-	resp, err := client.Do(req)
+	// 使用结构体中共享的 httpClient 实例发送请求
+	resp, err := s.httpClient.Do(req)
 	if err != nil {
 		return nil, fmt.Errorf("发送 HTTP 请求失败: %w", err)
 	}
-	// defer 确保在函数返回前关闭响应体,防止资源泄露
 	defer resp.Body.Close()
 
 	// 6. 读取响应体内容
@@ -96,7 +103,7 @@ func (s *aoDunService) sendFormData(ctx context.Context,apiUrl string,tokenType
 }
 
 
-func (s *aoDunService) sendDomainFormData(ctx context.Context,domain string,ip string,apiType string)  ([]byte,error) {
+func (s *aoDunService) sendDomainFormData(ctx context.Context, domain string, ip string, apiType string) ([]byte, error) {
 	var URL string
 	if apiType == "add" {
 		URL = "http://zapi.zzybgp.com/api/user/do_main"
@@ -110,29 +117,20 @@ func (s *aoDunService) sendDomainFormData(ctx context.Context,domain string,ip s
 	formData.Add("do_main_list[ip]", ip)
 	encodedData := formData.Encode()
 
-	req, err := http.NewRequest("POST", URL, bytes.NewBuffer([]byte(encodedData)))
+	// 使用带有 context 的请求
+	req, err := http.NewRequestWithContext(ctx, "POST", URL, bytes.NewBuffer([]byte(encodedData)))
 	if err != nil {
 		return nil, fmt.Errorf("创建 HTTP 请求失败: %w", err)
 	}
 
-	// 设置请求头 Content-Type 为 "application/x-www-form-urlencoded"
+	// 设置请求头
 	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 
-
-	tr := &http.Transport{
-		TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // <--- 关键修改:忽略 SSL 验证
-	}
-
-	// 5. 使用 HTTP 客户端发送请求
-	client := &http.Client{
-		Transport: tr,
-		Timeout: 15 * time.Second, // 设置一个合理的超时时间,例如15秒
-	}
-	resp, err := client.Do(req)
+	// 使用共享的 httpClient 实例发送请求
+	resp, err := s.httpClient.Do(req)
 	if err != nil {
 		return nil, fmt.Errorf("发送 HTTP 请求失败: %w", err)
 	}
-	// defer 确保在函数返回前关闭响应体,防止资源泄露
 	defer resp.Body.Close()
 
 	// 6. 读取响应体内容
@@ -144,7 +142,19 @@ func (s *aoDunService) sendDomainFormData(ctx context.Context,domain string,ip s
 }
 
 
-func (s *aoDunService) GetToken(ctx context.Context)  (string,string,error) {
+// sendAuthenticatedRequest 封装了需要认证的API请求的通用流程:获取token -> 发送请求。
+func (s *aoDunService) sendAuthenticatedRequest(ctx context.Context, apiPath string, formData map[string]interface{}) ([]byte, error) {
+	tokenType, token, err := s.GetToken(ctx)
+	if err != nil {
+		// 如果获取token失败,直接返回错误
+		return nil, err
+	}
+
+	// 使用获取到的token发送请求
+	return s.sendFormData(ctx, apiPath, tokenType, token, formData)
+}
+
+func (s *aoDunService) GetToken(ctx context.Context) (string, string, error) {
 
 	formData := map[string]interface{}{
 		"ClientID":  s.clientID,
@@ -178,21 +188,17 @@ func (s *aoDunService) GetToken(ctx context.Context)  (string,string,error) {
 	return responsePayload.TokenType,responsePayload.AccessToken, nil
 }
 
-func (s *aoDunService) AddWhiteStaticList(ctx context.Context,req []v1.IpInfo) error {
-	tokenType,token, err := s.GetToken(ctx)
-	if err != nil {
-		return err
-	}
-
+func (s *aoDunService) AddWhiteStaticList(ctx context.Context, req []v1.IpInfo) error {
 	formData := map[string]interface{}{
-		"action" : "add",
-		"bwflag" : "white",
+		"action":         "add",
+		"bwflag":         "white",
 		"insert_bw_list": req,
 	}
 
-	resBody, err := s.sendFormData(ctx,"/v1.0/firewall/static_bw_list",tokenType,token,formData)
+	// 使用封装好的方法发送认证请求
+	resBody, err := s.sendAuthenticatedRequest(ctx, "/v1.0/firewall/static_bw_list", formData)
 	if err != nil {
-		return  err
+		return err
 	}
 	// 7. 将响应体 JSON 数据反序列化到 ResponsePayload 结构体
 	var res v1.IpResponse
@@ -212,20 +218,16 @@ func (s *aoDunService) AddWhiteStaticList(ctx context.Context,req []v1.IpInfo) e
 	return nil
 }
 
-func (s *aoDunService) GetWhiteStaticList(ctx context.Context,ip string) (int,error) {
-	tokenType,token, err := s.GetToken(ctx)
-	if err != nil {
-		return 0, err
-	}
-
+func (s *aoDunService) GetWhiteStaticList(ctx context.Context, ip string) (int, error) {
 	formData := map[string]interface{}{
-		"action" : "get",
-		"bwflag" : "white",
-		"page" : 1,
-		"ids": ip,
+		"action": "get",
+		"bwflag": "white",
+		"page":   1,
+		"ids":    ip,
 	}
 
-	resBody, err := s.sendFormData(ctx,"/v1.0/firewall/static_bw_list",tokenType,token,formData)
+	// 使用封装好的方法发送认证请求
+	resBody, err := s.sendAuthenticatedRequest(ctx, "/v1.0/firewall/static_bw_list", formData)
 	if err != nil {
 		return 0, err
 	}
@@ -256,19 +258,15 @@ func (s *aoDunService) GetWhiteStaticList(ctx context.Context,ip string) (int,er
 }
 
 func (s *aoDunService) DelWhiteStaticList(ctx context.Context, id string) error {
-	tokenType, token, err := s.GetToken(ctx)
-	if err != nil {
-		return err
-	}
-
 	formData := map[string]interface{}{
 		"action": "del",
 		"bwflag": "white",
 		"flag":   0,
-		"ids":   id,
+		"ids":    id,
 	}
 
-	resBody, err := s.sendFormData(ctx, "/v1.0/firewall/static_bw_list", tokenType, token, formData)
+	// 使用封装好的方法发送认证请求
+	resBody, err := s.sendAuthenticatedRequest(ctx, "/v1.0/firewall/static_bw_list", formData)
 	if err != nil {
 		return err
 	}

+ 1 - 0
internal/service/webforwarding.go

@@ -383,6 +383,7 @@ func (s *webForwardingService) EditWebForwarding(ctx context.Context, req *v1.We
 		return err
 	}
 
+	// 将域名添加到白名单
 	webData, err := s.webForwardingRepository.GetWebForwarding(ctx, int64(req.WebForwardingData.Id))
 	if err != nil {
 		return err