| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- package configure
-
- import (
- "bytes"
- "context"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "strings"
- "time"
-
- "git.x2erp.com/qdy/go-base/model/response"
- )
-
- // Client 配置中心客户端
- type Client struct {
- config ClientConfig
- httpClient *http.Client
- }
-
- // ResponseWrapper 包装API响应
- type ResponseWrapper[T any] struct {
- response.QueryResult[T]
- }
-
- // NewClient 创建新的配置中心客户端
- func NewClient() (*Client, error) {
- config := DefaultConfig()
- return NewClientWithConfig(config)
- }
-
- // NewClientWithConfig 使用自定义配置创建客户端
- func NewClientWithConfig(config ClientConfig) (*Client, error) {
- // 验证配置
- if err := config.Validate(); err != nil {
- return nil, err
- }
-
- // 创建HTTP客户端
- httpClient := &http.Client{
- Timeout: config.HTTPTimeout,
- }
-
- return &Client{
- config: config,
- httpClient: httpClient,
- }, nil
- }
-
- // NewBasicAuthClient 创建使用Basic认证的客户端
- func NewBasicAuthClient(baseURL, username, password string) (*Client, error) {
- config := ClientConfig{
- BaseURL: baseURL,
- AuthType: AuthTypeBasic,
- Username: username,
- Password: password,
- HTTPTimeout: 30 * time.Second,
- }
-
- return NewClientWithConfig(config)
- }
-
- // NewTokenAuthClient 创建使用Token认证的客户端
- func NewTokenAuthClient(baseURL, token string) (*Client, error) {
- config := ClientConfig{
- BaseURL: baseURL,
- AuthType: AuthTypeToken,
- Token: token,
- HTTPTimeout: 30 * time.Second,
- }
-
- return NewClientWithConfig(config)
- }
-
- // ListTables 查询数据库表字典列表
- func (c *Client) ListTables(ctx context.Context, query *DicTableQueryRequest) (*DicTableList, error) {
- endpoint := "/api/dic-table/list"
-
- var result ResponseWrapper[[]DicTableDB]
- if err := c.doRequest(ctx, http.MethodPost, endpoint, query, &result); err != nil {
- return nil, err
- }
-
- if !result.Success {
- return nil, NewClientError("list_tables", fmt.Sprintf("API error: %s", result.Error), nil)
- }
-
- return &DicTableList{
- TotalCount: result.TotalCount,
- LastPage: result.LastPage,
- Data: result.Data,
- }, nil
- }
-
- // GetTable 查询数据库表字典详情
- func (c *Client) GetTable(ctx context.Context, tableID string) (*DicTableDetail, error) {
- endpoint := fmt.Sprintf("/api/dic-table/detail/%s", tableID)
-
- var result ResponseWrapper[DicTableDetail]
- if err := c.doRequest(ctx, http.MethodPost, endpoint, nil, &result); err != nil {
- return nil, err
- }
-
- if !result.Success {
- // 检查是否是"未找到"错误
- if result.Error == "not found" ||
- result.Error == "数据库表字典不存在" ||
- result.Error == "查询数据库表字典主表失败: sql: no rows in result set" ||
- result.Error == "查询数据库表字典主表失败: context canceled" {
- return nil, ErrNotFound
- }
- // 检查是否是context取消错误
- if result.Error == "查询数据库表字典主表失败: context canceled" ||
- strings.Contains(result.Error, "context canceled") ||
- strings.Contains(result.Error, "context deadline exceeded") {
- return nil, WrapError("get_table", "context canceled", nil)
- }
- return nil, NewClientError("get_table", fmt.Sprintf("API error: %s", result.Error), nil)
- }
-
- return &result.Data, nil
- }
-
- // SaveTable 创建或更新数据库表字典
- func (c *Client) SaveTable(ctx context.Context, req *DicTableRequest) (*DicTableDetail, error) {
- endpoint := "/api/dic-table/save"
-
- var result ResponseWrapper[DicTableDetail]
- if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result); err != nil {
- return nil, err
- }
-
- if !result.Success {
- return nil, NewClientError("save_table", fmt.Sprintf("API error: %s", result.Error), nil)
- }
-
- return &result.Data, nil
- }
-
- // DeleteTable 删除数据库表字典
- func (c *Client) DeleteTable(ctx context.Context, tableID string) error {
- endpoint := fmt.Sprintf("/api/dic-table/delete/%s", tableID)
-
- var result ResponseWrapper[int64]
- if err := c.doRequest(ctx, http.MethodPost, endpoint, nil, &result); err != nil {
- return err
- }
-
- if !result.Success {
- return NewClientError("delete_table", fmt.Sprintf("API error: %s", result.Error), nil)
- }
-
- return nil
- }
-
- // doRequest 执行HTTP请求
- func (c *Client) doRequest(ctx context.Context, method, endpoint string, body interface{}, result interface{}) error {
- // 构建URL
- url := c.config.BaseURL + endpoint
-
- // 准备请求体
- var requestBody io.Reader
- if body != nil {
- jsonData, err := json.Marshal(body)
- if err != nil {
- return WrapError("do_request", "failed to marshal request body", err)
- }
- requestBody = bytes.NewBuffer(jsonData)
- }
-
- // 创建请求
- req, err := http.NewRequestWithContext(ctx, method, url, requestBody)
- if err != nil {
- return WrapError("do_request", "failed to create request", err)
- }
-
- // 设置认证头
- if err := c.setAuthHeader(req); err != nil {
- return err
- }
-
- // 设置内容类型
- // 对于POST、PUT、PATCH方法,即使body为nil也设置Content-Type
- if body != nil || method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch {
- req.Header.Set("Content-Type", "application/json")
- }
-
- // 执行请求
- resp, err := c.httpClient.Do(req)
- if err != nil {
- if ctx.Err() == context.DeadlineExceeded {
- return ErrRequestTimeout
- }
- return WrapError("do_request", "HTTP request failed", err)
- }
- defer resp.Body.Close()
-
- // 读取响应体
- respBody, err := io.ReadAll(resp.Body)
- if err != nil {
- return WrapError("do_request", "failed to read response body", err)
- }
-
- // 检查HTTP状态码
- if resp.StatusCode != http.StatusOK {
- switch resp.StatusCode {
- case http.StatusUnauthorized:
- return ErrUnauthorized
- case http.StatusForbidden:
- return ErrForbidden
- case http.StatusNotFound:
- return ErrNotFound
- case http.StatusBadRequest:
- return ErrValidationFailed
- default:
- return NewClientError("do_request",
- fmt.Sprintf("HTTP error %d: %s", resp.StatusCode, string(respBody)), nil)
- }
- }
-
- // 解析响应
- if err := json.Unmarshal(respBody, result); err != nil {
- return WrapError("do_request", "failed to unmarshal response", err)
- }
-
- return nil
- }
-
- // setAuthHeader 设置认证头
- func (c *Client) setAuthHeader(req *http.Request) error {
- switch c.config.AuthType {
- case AuthTypeBasic:
- req.SetBasicAuth(c.config.Username, c.config.Password)
- case AuthTypeToken:
- req.Header.Set("Authorization", "Bearer "+c.config.Token)
- default:
- return ErrConfigInvalidAuthType
- }
- return nil
- }
-
- // GetConfig 获取客户端配置
- func (c *Client) GetConfig() ClientConfig {
- return c.config
- }
|