Nenhuma descrição
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

client.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. package configure
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "io"
  8. "net/http"
  9. "strings"
  10. "time"
  11. "git.x2erp.com/qdy/go-base/model/response"
  12. )
  13. // Client 配置中心客户端
  14. type Client struct {
  15. config ClientConfig
  16. httpClient *http.Client
  17. }
  18. // ResponseWrapper 包装API响应
  19. type ResponseWrapper[T any] struct {
  20. response.QueryResult[T]
  21. }
  22. // NewClient 创建新的配置中心客户端
  23. func NewClient() (*Client, error) {
  24. config := DefaultConfig()
  25. return NewClientWithConfig(config)
  26. }
  27. // NewClientWithConfig 使用自定义配置创建客户端
  28. func NewClientWithConfig(config ClientConfig) (*Client, error) {
  29. // 验证配置
  30. if err := config.Validate(); err != nil {
  31. return nil, err
  32. }
  33. // 创建HTTP客户端
  34. httpClient := &http.Client{
  35. Timeout: config.HTTPTimeout,
  36. }
  37. return &Client{
  38. config: config,
  39. httpClient: httpClient,
  40. }, nil
  41. }
  42. // NewBasicAuthClient 创建使用Basic认证的客户端
  43. func NewBasicAuthClient(baseURL, username, password string) (*Client, error) {
  44. config := ClientConfig{
  45. BaseURL: baseURL,
  46. AuthType: AuthTypeBasic,
  47. Username: username,
  48. Password: password,
  49. HTTPTimeout: 30 * time.Second,
  50. }
  51. return NewClientWithConfig(config)
  52. }
  53. // NewTokenAuthClient 创建使用Token认证的客户端
  54. func NewTokenAuthClient(baseURL, token string) (*Client, error) {
  55. config := ClientConfig{
  56. BaseURL: baseURL,
  57. AuthType: AuthTypeToken,
  58. Token: token,
  59. HTTPTimeout: 30 * time.Second,
  60. }
  61. return NewClientWithConfig(config)
  62. }
  63. // ListTables 查询数据库表字典列表
  64. func (c *Client) ListTables(ctx context.Context, query *DicTableQueryRequest) (*DicTableList, error) {
  65. endpoint := "/api/dic-table/list"
  66. var result ResponseWrapper[[]DicTableDB]
  67. if err := c.doRequest(ctx, http.MethodPost, endpoint, query, &result, false); err != nil {
  68. return nil, err
  69. }
  70. if !result.Success {
  71. return nil, NewClientError("list_tables", fmt.Sprintf("API error: %s", result.Error), nil)
  72. }
  73. return &DicTableList{
  74. TotalCount: result.TotalCount,
  75. LastPage: result.LastPage,
  76. Data: result.Data,
  77. }, nil
  78. }
  79. // GetTable 查询数据库表字典详情
  80. func (c *Client) GetTable(ctx context.Context, tableID string) (*DicTableDetail, error) {
  81. endpoint := fmt.Sprintf("/api/dic-table/detail/%s", tableID)
  82. var result ResponseWrapper[DicTableDetail]
  83. if err := c.doRequest(ctx, http.MethodPost, endpoint, nil, &result, false); err != nil {
  84. return nil, err
  85. }
  86. if !result.Success {
  87. // 检查是否是"未找到"错误
  88. if result.Error == "not found" ||
  89. result.Error == "数据库表字典不存在" ||
  90. result.Error == "查询数据库表字典主表失败: sql: no rows in result set" ||
  91. result.Error == "查询数据库表字典主表失败: context canceled" {
  92. return nil, ErrNotFound
  93. }
  94. // 检查是否是context取消错误
  95. if result.Error == "查询数据库表字典主表失败: context canceled" ||
  96. strings.Contains(result.Error, "context canceled") ||
  97. strings.Contains(result.Error, "context deadline exceeded") {
  98. return nil, WrapError("get_table", "context canceled", nil)
  99. }
  100. return nil, NewClientError("get_table", fmt.Sprintf("API error: %s", result.Error), nil)
  101. }
  102. return &result.Data, nil
  103. }
  104. // SaveTable 创建或更新数据库表字典
  105. func (c *Client) SaveTable(ctx context.Context, req *DicTableRequest) (*DicTableDetail, error) {
  106. endpoint := "/api/dic-table/save"
  107. var result ResponseWrapper[DicTableDetail]
  108. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result, false); err != nil {
  109. return nil, err
  110. }
  111. if !result.Success {
  112. return nil, NewClientError("save_table", fmt.Sprintf("API error: %s", result.Error), nil)
  113. }
  114. return &result.Data, nil
  115. }
  116. // DeleteTable 删除数据库表字典
  117. func (c *Client) DeleteTable(ctx context.Context, tableID string) error {
  118. endpoint := fmt.Sprintf("/api/dic-table/delete/%s", tableID)
  119. var result ResponseWrapper[int64]
  120. if err := c.doRequest(ctx, http.MethodPost, endpoint, nil, &result, false); err != nil {
  121. return err
  122. }
  123. if !result.Success {
  124. return NewClientError("delete_table", fmt.Sprintf("API error: %s", result.Error), nil)
  125. }
  126. return nil
  127. }
  128. // BatchSaveTables 批量保存数据库表字典
  129. func (c *Client) BatchSaveTables(ctx context.Context, req *BatchSaveDicTablesRequest) error {
  130. endpoint := "/api/dic-table/batch-save"
  131. var result ResponseWrapper[bool]
  132. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result, false); err != nil {
  133. return err
  134. }
  135. if !result.Success {
  136. return NewClientError("batch_save_tables", fmt.Sprintf("API error: %s", result.Error), nil)
  137. }
  138. return nil
  139. }
  140. // BatchSyncTables 批量同步表字典
  141. func (c *Client) BatchSyncTables(ctx context.Context, req *BatchTableSyncRequest) (*BatchTableSyncResponse, error) {
  142. endpoint := "/api/dic-table/batch-sync"
  143. var result ResponseWrapper[BatchTableSyncResponse]
  144. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result, false); err != nil {
  145. return nil, err
  146. }
  147. if !result.Success {
  148. return nil, NewClientError("batch_sync_tables", fmt.Sprintf("API error: %s", result.Error), nil)
  149. }
  150. return &result.Data, nil
  151. }
  152. // BatchSyncAliases 批量同步别名
  153. func (c *Client) BatchSyncAliases(ctx context.Context, req *BatchAliasSyncRequest) (*BatchAliasSyncResponse, error) {
  154. endpoint := "/api/alias/batch-sync"
  155. var result ResponseWrapper[BatchAliasSyncResponse]
  156. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result, false); err != nil {
  157. return nil, err
  158. }
  159. if !result.Success {
  160. return nil, NewClientError("batch_sync_aliases", fmt.Sprintf("API error: %s", result.Error), nil)
  161. }
  162. return &result.Data, nil
  163. }
  164. // SaveTableAlias 保存表别名字典
  165. func (c *Client) SaveTableAlias(ctx context.Context, req *TableAliasRequest) (*TableAliasResponse, error) {
  166. endpoint := "/api/dic-table-alias/save"
  167. var result ResponseWrapper[TableAliasResponse]
  168. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result, false); err != nil {
  169. return nil, err
  170. }
  171. if !result.Success {
  172. return nil, NewClientError("save_table_alias", fmt.Sprintf("API error: %s", result.Error), nil)
  173. }
  174. return &result.Data, nil
  175. }
  176. // SaveTableFieldAlias 保存表字段别名字典
  177. func (c *Client) SaveTableFieldAlias(ctx context.Context, req *TableFieldAliasRequest) (*TableFieldAliasResponse, error) {
  178. endpoint := "/api/dic-table-field-alias/save"
  179. var result ResponseWrapper[TableFieldAliasResponse]
  180. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result, false); err != nil {
  181. return nil, err
  182. }
  183. if !result.Success {
  184. return nil, NewClientError("save_table_field_alias", fmt.Sprintf("API error: %s", result.Error), nil)
  185. }
  186. return &result.Data, nil
  187. }
  188. // ListTableAliases 查询表别名字典列表
  189. func (c *Client) ListTableAliases(ctx context.Context, query *TableAliasQueryRequest) (*TableAliasList, error) {
  190. endpoint := "/api/dic-table-alias/list"
  191. var result ResponseWrapper[[]TableAliasResponse]
  192. if err := c.doRequest(ctx, http.MethodPost, endpoint, query, &result, false); err != nil {
  193. return nil, err
  194. }
  195. if !result.Success {
  196. return nil, NewClientError("list_table_aliases", fmt.Sprintf("API error: %s", result.Error), nil)
  197. }
  198. return &TableAliasList{
  199. TotalCount: result.TotalCount,
  200. LastPage: result.LastPage,
  201. Data: result.Data,
  202. }, nil
  203. }
  204. // ListTableFieldAliases 查询表字段别名字典列表
  205. func (c *Client) ListTableFieldAliases(ctx context.Context, query *TableFieldAliasQueryRequest) (*TableFieldAliasList, error) {
  206. endpoint := "/api/dic-table-field-alias/list"
  207. var result ResponseWrapper[[]TableFieldAliasResponse]
  208. if err := c.doRequest(ctx, http.MethodPost, endpoint, query, &result, false); err != nil {
  209. return nil, err
  210. }
  211. if !result.Success {
  212. return nil, NewClientError("list_table_field_aliases", fmt.Sprintf("API error: %s", result.Error), nil)
  213. }
  214. return &TableFieldAliasList{
  215. TotalCount: result.TotalCount,
  216. LastPage: result.LastPage,
  217. Data: result.Data,
  218. }, nil
  219. }
  220. // doRequest 执行HTTP请求
  221. func (c *Client) doRequest(ctx context.Context, method, endpoint string, body interface{}, result interface{}, skipAuth bool) error {
  222. // 构建URL
  223. url := c.config.BaseURL + endpoint
  224. // 准备请求体
  225. var requestBody io.Reader
  226. if body != nil {
  227. jsonData, err := json.Marshal(body)
  228. if err != nil {
  229. return WrapError("do_request", "failed to marshal request body", err)
  230. }
  231. requestBody = bytes.NewBuffer(jsonData)
  232. }
  233. // 创建请求
  234. req, err := http.NewRequestWithContext(ctx, method, url, requestBody)
  235. if err != nil {
  236. return WrapError("do_request", "failed to create request", err)
  237. }
  238. // 设置认证头(可选)
  239. if !skipAuth {
  240. if err := c.setAuthHeader(req); err != nil {
  241. return err
  242. }
  243. }
  244. // 设置内容类型
  245. // 对于POST、PUT、PATCH方法,即使body为nil也设置Content-Type
  246. if body != nil || method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch {
  247. req.Header.Set("Content-Type", "application/json")
  248. }
  249. // 执行请求
  250. resp, err := c.httpClient.Do(req)
  251. if err != nil {
  252. if ctx.Err() == context.DeadlineExceeded {
  253. return ErrRequestTimeout
  254. }
  255. return WrapError("do_request", "HTTP request failed", err)
  256. }
  257. defer resp.Body.Close()
  258. // 读取响应体
  259. respBody, err := io.ReadAll(resp.Body)
  260. if err != nil {
  261. return WrapError("do_request", "failed to read response body", err)
  262. }
  263. // 检查HTTP状态码
  264. if resp.StatusCode != http.StatusOK {
  265. switch resp.StatusCode {
  266. case http.StatusUnauthorized:
  267. return ErrUnauthorized
  268. case http.StatusForbidden:
  269. return ErrForbidden
  270. case http.StatusNotFound:
  271. return ErrNotFound
  272. case http.StatusBadRequest:
  273. return ErrValidationFailed
  274. default:
  275. return NewClientError("do_request",
  276. fmt.Sprintf("HTTP error %d: %s", resp.StatusCode, string(respBody)), nil)
  277. }
  278. }
  279. // 解析响应
  280. if err := json.Unmarshal(respBody, result); err != nil {
  281. return WrapError("do_request", "failed to unmarshal response", err)
  282. }
  283. return nil
  284. }
  285. // setAuthHeader 设置认证头
  286. func (c *Client) setAuthHeader(req *http.Request) error {
  287. switch c.config.AuthType {
  288. case AuthTypeBasic:
  289. req.SetBasicAuth(c.config.Username, c.config.Password)
  290. case AuthTypeToken:
  291. req.Header.Set("Authorization", "Bearer "+c.config.Token)
  292. case AuthTypeNone:
  293. // 无认证,不设置任何认证头
  294. default:
  295. return ErrConfigInvalidAuthType
  296. }
  297. return nil
  298. }
  299. // LoginUser 用户登录(无需认证)
  300. func (c *Client) LoginUser(ctx context.Context, req *UserLoginRequest) (string, error) {
  301. endpoint := "/api/login/user"
  302. var result ResponseWrapper[string]
  303. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result, true); err != nil {
  304. return "", err
  305. }
  306. if !result.Success {
  307. return "", NewClientError("login_user", fmt.Sprintf("API error: %s", result.Error), nil)
  308. }
  309. return result.Data, nil
  310. }
  311. // GetConfig 获取客户端配置
  312. func (c *Client) GetConfig() ClientConfig {
  313. return c.config
  314. }