No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

client.go 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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); 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); 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); 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); 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. // ListTableAliases 查询表别名字典列表
  129. func (c *Client) ListTableAliases(ctx context.Context, query *TableAliasQueryRequest) (*TableAliasList, error) {
  130. endpoint := "/api/dic-table-alias/list"
  131. var result ResponseWrapper[[]DicTableAliasDB]
  132. if err := c.doRequest(ctx, http.MethodPost, endpoint, query, &result); err != nil {
  133. return nil, err
  134. }
  135. if !result.Success {
  136. return nil, NewClientError("list_table_aliases", fmt.Sprintf("API error: %s", result.Error), nil)
  137. }
  138. return &TableAliasList{
  139. TotalCount: result.TotalCount,
  140. LastPage: result.LastPage,
  141. Data: result.Data,
  142. }, nil
  143. }
  144. // GetTableAlias 查询表别名字典详情
  145. func (c *Client) GetTableAlias(ctx context.Context, id string) (*TableAliasDetail, error) {
  146. endpoint := fmt.Sprintf("/api/dic-table-alias/detail/%s", id)
  147. var result ResponseWrapper[TableAliasDetail]
  148. if err := c.doRequest(ctx, http.MethodPost, endpoint, nil, &result); err != nil {
  149. return nil, err
  150. }
  151. if !result.Success {
  152. if result.Error == "not found" || strings.Contains(result.Error, "不存在") {
  153. return nil, ErrNotFound
  154. }
  155. return nil, NewClientError("get_table_alias", fmt.Sprintf("API error: %s", result.Error), nil)
  156. }
  157. return &result.Data, nil
  158. }
  159. // SaveTableAlias 创建或更新表别名字典
  160. func (c *Client) SaveTableAlias(ctx context.Context, req *TableAliasRequest) (*TableAliasDetail, error) {
  161. endpoint := "/api/dic-table-alias/save"
  162. var result ResponseWrapper[TableAliasDetail]
  163. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result); err != nil {
  164. return nil, err
  165. }
  166. if !result.Success {
  167. return nil, NewClientError("save_table_alias", fmt.Sprintf("API error: %s", result.Error), nil)
  168. }
  169. return &result.Data, nil
  170. }
  171. // BatchSaveTableAliases 批量保存表别名字典
  172. func (c *Client) BatchSaveTableAliases(ctx context.Context, req *BatchTableAliasRequest) ([]TableAliasDetail, error) {
  173. endpoint := "/api/dic-table-alias/batch-save"
  174. var result ResponseWrapper[[]TableAliasDetail]
  175. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result); err != nil {
  176. return nil, err
  177. }
  178. if !result.Success {
  179. return nil, NewClientError("batch_save_table_aliases", fmt.Sprintf("API error: %s", result.Error), nil)
  180. }
  181. return result.Data, nil
  182. }
  183. // DeleteTableAlias 删除表别名字典
  184. func (c *Client) DeleteTableAlias(ctx context.Context, id string) error {
  185. endpoint := fmt.Sprintf("/api/dic-table-alias/delete/%s", id)
  186. var result ResponseWrapper[int64]
  187. if err := c.doRequest(ctx, http.MethodPost, endpoint, nil, &result); err != nil {
  188. return err
  189. }
  190. if !result.Success {
  191. return NewClientError("delete_table_alias", fmt.Sprintf("API error: %s", result.Error), nil)
  192. }
  193. return nil
  194. }
  195. // ListTableFieldAliases 查询字段别名字典列表
  196. func (c *Client) ListTableFieldAliases(ctx context.Context, query *TableFieldAliasQueryRequest) (*TableFieldAliasList, error) {
  197. endpoint := "/api/dic-table-field-alias/list"
  198. var result ResponseWrapper[[]DicTableFieldAliasDB]
  199. if err := c.doRequest(ctx, http.MethodPost, endpoint, query, &result); err != nil {
  200. return nil, err
  201. }
  202. if !result.Success {
  203. return nil, NewClientError("list_table_field_aliases", fmt.Sprintf("API error: %s", result.Error), nil)
  204. }
  205. return &TableFieldAliasList{
  206. TotalCount: result.TotalCount,
  207. LastPage: result.LastPage,
  208. Data: result.Data,
  209. }, nil
  210. }
  211. // GetTableFieldAlias 查询字段别名字典详情
  212. func (c *Client) GetTableFieldAlias(ctx context.Context, id string) (*TableFieldAliasDetail, error) {
  213. endpoint := fmt.Sprintf("/api/dic-table-field-alias/detail/%s", id)
  214. var result ResponseWrapper[TableFieldAliasDetail]
  215. if err := c.doRequest(ctx, http.MethodPost, endpoint, nil, &result); err != nil {
  216. return nil, err
  217. }
  218. if !result.Success {
  219. if result.Error == "not found" || strings.Contains(result.Error, "不存在") {
  220. return nil, ErrNotFound
  221. }
  222. return nil, NewClientError("get_table_field_alias", fmt.Sprintf("API error: %s", result.Error), nil)
  223. }
  224. return &result.Data, nil
  225. }
  226. // SaveTableFieldAlias 创建或更新字段别名字典
  227. func (c *Client) SaveTableFieldAlias(ctx context.Context, req *TableFieldAliasRequest) (*TableFieldAliasDetail, error) {
  228. endpoint := "/api/dic-table-field-alias/save"
  229. var result ResponseWrapper[TableFieldAliasDetail]
  230. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result); err != nil {
  231. return nil, err
  232. }
  233. if !result.Success {
  234. return nil, NewClientError("save_table_field_alias", fmt.Sprintf("API error: %s", result.Error), nil)
  235. }
  236. return &result.Data, nil
  237. }
  238. // BatchSaveTableFieldAliases 批量保存字段别名字典
  239. func (c *Client) BatchSaveTableFieldAliases(ctx context.Context, req *BatchTableFieldAliasRequest) ([]TableFieldAliasDetail, error) {
  240. endpoint := "/api/dic-table-field-alias/batch-save"
  241. var result ResponseWrapper[[]TableFieldAliasDetail]
  242. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result); err != nil {
  243. return nil, err
  244. }
  245. if !result.Success {
  246. return nil, NewClientError("batch_save_table_field_aliases", fmt.Sprintf("API error: %s", result.Error), nil)
  247. }
  248. return result.Data, nil
  249. }
  250. // DeleteTableFieldAlias 删除字段别名字典
  251. func (c *Client) DeleteTableFieldAlias(ctx context.Context, id string) error {
  252. endpoint := fmt.Sprintf("/api/dic-table-field-alias/delete/%s", id)
  253. var result ResponseWrapper[int64]
  254. if err := c.doRequest(ctx, http.MethodPost, endpoint, nil, &result); err != nil {
  255. return err
  256. }
  257. if !result.Success {
  258. return NewClientError("delete_table_field_alias", fmt.Sprintf("API error: %s", result.Error), nil)
  259. }
  260. return nil
  261. }
  262. // ListTableAliasFlow 查询表别名字典流水列表
  263. func (c *Client) ListTableAliasFlow(ctx context.Context, query *TableAliasFlowQueryRequest) (*TableAliasFlowList, error) {
  264. endpoint := "/api/dic-table-alias-flow/list"
  265. var result ResponseWrapper[[]DicTableAliasFlowDB]
  266. if err := c.doRequest(ctx, http.MethodPost, endpoint, query, &result); err != nil {
  267. return nil, err
  268. }
  269. if !result.Success {
  270. return nil, NewClientError("list_table_alias_flow", fmt.Sprintf("API error: %s", result.Error), nil)
  271. }
  272. return &TableAliasFlowList{
  273. TotalCount: result.TotalCount,
  274. LastPage: result.LastPage,
  275. Data: result.Data,
  276. }, nil
  277. }
  278. // BatchSaveTableAliasFlow 批量保存表别名字典流水
  279. func (c *Client) BatchSaveTableAliasFlow(ctx context.Context, req *BatchTableAliasRequest, tenantID string) ([]DicTableAliasFlowDB, error) {
  280. endpoint := fmt.Sprintf("/api/dic-table-alias-flow/batch-save?tenantID=%s", tenantID)
  281. var result ResponseWrapper[[]DicTableAliasFlowDB]
  282. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result); err != nil {
  283. return nil, err
  284. }
  285. if !result.Success {
  286. return nil, NewClientError("batch_save_table_alias_flow", fmt.Sprintf("API error: %s", result.Error), nil)
  287. }
  288. return result.Data, nil
  289. }
  290. // BatchApprovalTableAliasFlow 批量审批表别名字典流水
  291. func (c *Client) BatchApprovalTableAliasFlow(ctx context.Context, req *BatchApprovalFlowRequest) error {
  292. endpoint := "/api/dic-table-alias-flow/batch-approval"
  293. var result ResponseWrapper[int64]
  294. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result); err != nil {
  295. return err
  296. }
  297. if !result.Success {
  298. return NewClientError("batch_approval_table_alias_flow", fmt.Sprintf("API error: %s", result.Error), nil)
  299. }
  300. return nil
  301. }
  302. // ListTableFieldAliasFlow 查询字段别名字典流水列表
  303. func (c *Client) ListTableFieldAliasFlow(ctx context.Context, query *TableFieldAliasFlowQueryRequest) (*TableFieldAliasFlowList, error) {
  304. endpoint := "/api/dic-table-field-alias-flow/list"
  305. var result ResponseWrapper[[]DicTableFieldAliasFlowDB]
  306. if err := c.doRequest(ctx, http.MethodPost, endpoint, query, &result); err != nil {
  307. return nil, err
  308. }
  309. if !result.Success {
  310. return nil, NewClientError("list_table_field_alias_flow", fmt.Sprintf("API error: %s", result.Error), nil)
  311. }
  312. return &TableFieldAliasFlowList{
  313. TotalCount: result.TotalCount,
  314. LastPage: result.LastPage,
  315. Data: result.Data,
  316. }, nil
  317. }
  318. // BatchSaveTableFieldAliasFlow 批量保存字段别名字典流水
  319. func (c *Client) BatchSaveTableFieldAliasFlow(ctx context.Context, req *BatchTableFieldAliasRequest, tenantID string) ([]DicTableFieldAliasFlowDB, error) {
  320. endpoint := fmt.Sprintf("/api/dic-table-field-alias-flow/batch-save?tenantID=%s", tenantID)
  321. var result ResponseWrapper[[]DicTableFieldAliasFlowDB]
  322. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result); err != nil {
  323. return nil, err
  324. }
  325. if !result.Success {
  326. return nil, NewClientError("batch_save_table_field_alias_flow", fmt.Sprintf("API error: %s", result.Error), nil)
  327. }
  328. return result.Data, nil
  329. }
  330. // BatchApprovalTableFieldAliasFlow 批量审批字段别名字典流水
  331. func (c *Client) BatchApprovalTableFieldAliasFlow(ctx context.Context, req *BatchApprovalFlowRequest) error {
  332. endpoint := "/api/dic-table-field-alias-flow/batch-approval"
  333. var result ResponseWrapper[int64]
  334. if err := c.doRequest(ctx, http.MethodPost, endpoint, req, &result); err != nil {
  335. return err
  336. }
  337. if !result.Success {
  338. return NewClientError("batch_approval_table_field_alias_flow", fmt.Sprintf("API error: %s", result.Error), nil)
  339. }
  340. return nil
  341. }
  342. // doRequest 执行HTTP请求
  343. func (c *Client) doRequest(ctx context.Context, method, endpoint string, body interface{}, result interface{}) error {
  344. // 构建URL
  345. url := c.config.BaseURL + endpoint
  346. // 准备请求体
  347. var requestBody io.Reader
  348. if body != nil {
  349. jsonData, err := json.Marshal(body)
  350. if err != nil {
  351. return WrapError("do_request", "failed to marshal request body", err)
  352. }
  353. requestBody = bytes.NewBuffer(jsonData)
  354. }
  355. // 创建请求
  356. req, err := http.NewRequestWithContext(ctx, method, url, requestBody)
  357. if err != nil {
  358. return WrapError("do_request", "failed to create request", err)
  359. }
  360. // 设置认证头
  361. if err := c.setAuthHeader(req); err != nil {
  362. return err
  363. }
  364. // 设置内容类型
  365. // 对于POST、PUT、PATCH方法,即使body为nil也设置Content-Type
  366. if body != nil || method == http.MethodPost || method == http.MethodPut || method == http.MethodPatch {
  367. req.Header.Set("Content-Type", "application/json")
  368. }
  369. // 执行请求
  370. resp, err := c.httpClient.Do(req)
  371. if err != nil {
  372. if ctx.Err() == context.DeadlineExceeded {
  373. return ErrRequestTimeout
  374. }
  375. return WrapError("do_request", "HTTP request failed", err)
  376. }
  377. defer resp.Body.Close()
  378. // 读取响应体
  379. respBody, err := io.ReadAll(resp.Body)
  380. if err != nil {
  381. return WrapError("do_request", "failed to read response body", err)
  382. }
  383. // 检查HTTP状态码
  384. if resp.StatusCode != http.StatusOK {
  385. switch resp.StatusCode {
  386. case http.StatusUnauthorized:
  387. return ErrUnauthorized
  388. case http.StatusForbidden:
  389. return ErrForbidden
  390. case http.StatusNotFound:
  391. return ErrNotFound
  392. case http.StatusBadRequest:
  393. return ErrValidationFailed
  394. default:
  395. return NewClientError("do_request",
  396. fmt.Sprintf("HTTP error %d: %s", resp.StatusCode, string(respBody)), nil)
  397. }
  398. }
  399. // 解析响应
  400. if err := json.Unmarshal(respBody, result); err != nil {
  401. return WrapError("do_request", "failed to unmarshal response", err)
  402. }
  403. return nil
  404. }
  405. // setAuthHeader 设置认证头
  406. func (c *Client) setAuthHeader(req *http.Request) error {
  407. switch c.config.AuthType {
  408. case AuthTypeBasic:
  409. req.SetBasicAuth(c.config.Username, c.config.Password)
  410. case AuthTypeToken:
  411. req.Header.Set("Authorization", "Bearer "+c.config.Token)
  412. default:
  413. return ErrConfigInvalidAuthType
  414. }
  415. return nil
  416. }
  417. // GetConfig 获取客户端配置
  418. func (c *Client) GetConfig() ClientConfig {
  419. return c.config
  420. }