k3cloud-sdk 0.4.7 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +43 -1
- data/CHANGELOG.md +97 -1
- data/Gemfile +5 -1
- data/Gemfile.lock +50 -1
- data/README.md +10 -5
- data/k3cloud.gemspec +6 -2
- data/lib/k3cloud/configuration.rb +24 -5
- data/lib/k3cloud/errors/k3cloud_error.rb +9 -4
- data/lib/k3cloud/http.rb +28 -11
- data/lib/k3cloud/k3cloud_api.rb +8 -2
- data/lib/k3cloud/utils/base64_utils.rb +10 -12
- data/lib/k3cloud/utils/const_define.rb +23 -21
- data/lib/k3cloud/utils/md5_utils.rb +16 -23
- data/lib/k3cloud/version.rb +1 -1
- data/lib/k3cloud/web_api_client.rb +22 -15
- data/lib/k3cloud.rb +56 -8
- metadata +8 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5dfedb0c66eddf2de607406e2cf083b98f54350412d9cbb2699d177eb66c347a
|
|
4
|
+
data.tar.gz: b73a7c6847c615b4d2ec9fcb0dbacce4e69078ba24e204fa2cf850974a28404c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 14bba40c28f9a4909551df05847b713ffce4029928fc5b9a82b020c95d7f1f155fff0a2eccce316b54c261c3bdcc3e085e823702eb3a8bf11787e181c0f52a6e
|
|
7
|
+
data.tar.gz: 1339bf2093aad7ee854a1d342d50341f0f0bcaf376daf68e620914271d3576fbcbdfc7ee41f08d8925192a150038809298c06614646042a205a68306ed9971b1
|
data/.rubocop.yml
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
AllCops:
|
|
2
|
-
TargetRubyVersion: 2.
|
|
2
|
+
TargetRubyVersion: 2.5
|
|
3
|
+
NewCops: enable
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
Exclude:
|
|
6
|
+
- "**/*.md"
|
|
7
|
+
- "ChangeLog*"
|
|
8
|
+
- "CHANGELOG*"
|
|
9
|
+
- "vendor/**/*"
|
|
10
|
+
- "pkg/**/*"
|
|
3
11
|
|
|
4
12
|
Style/StringLiterals:
|
|
5
13
|
Enabled: true
|
|
@@ -11,3 +19,37 @@ Style/StringLiteralsInInterpolation:
|
|
|
11
19
|
|
|
12
20
|
Layout/LineLength:
|
|
13
21
|
Max: 120
|
|
22
|
+
|
|
23
|
+
# 本仓库既有签名/加密逻辑偏过程式,文档与度量类 cops 放宽以避免大改业务逻辑。
|
|
24
|
+
Style/Documentation:
|
|
25
|
+
Enabled: false
|
|
26
|
+
|
|
27
|
+
Gemspec/RequireMFA:
|
|
28
|
+
Enabled: false
|
|
29
|
+
|
|
30
|
+
Style/FormatStringToken:
|
|
31
|
+
Enabled: false
|
|
32
|
+
|
|
33
|
+
Style/FormatString:
|
|
34
|
+
EnforcedStyle: format
|
|
35
|
+
|
|
36
|
+
Naming/FileName:
|
|
37
|
+
Exclude:
|
|
38
|
+
- "lib/k3cloud-sdk.rb"
|
|
39
|
+
|
|
40
|
+
Metrics/AbcSize:
|
|
41
|
+
Max: 40
|
|
42
|
+
|
|
43
|
+
Metrics/MethodLength:
|
|
44
|
+
Max: 30
|
|
45
|
+
|
|
46
|
+
Metrics/BlockLength:
|
|
47
|
+
Exclude:
|
|
48
|
+
- "spec/**/*"
|
|
49
|
+
- "k3cloud.gemspec"
|
|
50
|
+
|
|
51
|
+
Metrics/CyclomaticComplexity:
|
|
52
|
+
Max: 12
|
|
53
|
+
|
|
54
|
+
Metrics/ParameterLists:
|
|
55
|
+
Max: 8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,101 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
1
8
|
## [Unreleased]
|
|
2
9
|
|
|
10
|
+
## [0.5.0] - 2026-07-17
|
|
11
|
+
|
|
12
|
+
### ⚠ Breaking Changes
|
|
13
|
+
|
|
14
|
+
- 最低 Ruby 版本提升至 `>= 2.5.0`。
|
|
15
|
+
- 默认启用 HTTPS 证书校验;私有云自签证书需显式设置 `Configuration#verify_ssl = false`。
|
|
16
|
+
- `Configuration` 为 `lcid`、`connect_timeout`(30s)、`request_timeout`(60s)、`verify_ssl` 提供默认值。
|
|
17
|
+
- 工具类与常量迁移至 `K3cloud` 命名空间(`K3cloud::Base64Utils`、`K3cloud::MD5Utils`、`K3cloud::ConstDefine`),不再保留无命名空间别名。
|
|
18
|
+
- `Http` 构造参数改为关键字参数。
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
|
|
22
|
+
- 构造签名头失败时不再静默返回空 header,改为抛出异常。
|
|
23
|
+
- 签名密钥为空时 `MD5Utils.hash_mac` 改为抛出 `ArgumentError`。
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- 修复 `MD5Utils.encrypt` 在 Ruby 下始终返回空字符串的 bug。
|
|
28
|
+
- 修复公有云请求 URL 出现双斜杠的问题。
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- 日志输出支持可配置格式:`Configuration#log_format` 取 `:text`(默认,常规 Logger 格式)或 `:json`(每行一个 JSON,便于采集与检索)。
|
|
33
|
+
- 支持注入自定义 logger(`K3cloud.logger = Rails.logger`)与重建默认 logger(`K3cloud.reset_logger!`)。
|
|
34
|
+
|
|
35
|
+
## [0.4.7] - 2025-02-18
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- 修复 `execute_bill_query` 查询成功时误返回空数组、丢失业务数据的问题。
|
|
40
|
+
|
|
41
|
+
## [0.4.6] - 2025-02-18
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- 修复 `execute_bill_query` 在空结果时误判为错误的问题。
|
|
46
|
+
|
|
47
|
+
## [0.4.5] - 2025-02-18
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- 自定义错误类型统一改为抛出 `K3cloud::ResponseError`。
|
|
52
|
+
- `execute_bill_query` 增加对错误返回结构的识别:当首行首列为 Hash 时记录日志并返回空数组。
|
|
53
|
+
- User-Agent 携带 SDK 版本与运行环境信息。
|
|
54
|
+
|
|
55
|
+
## [0.4.4] - 2024-07-19
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- HTTP 请求超时自动重试(最多 5 次,间隔 5 秒)。
|
|
60
|
+
|
|
61
|
+
## [0.4.3] - 2023-11-29
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
|
|
65
|
+
- 新增统一异常类 `K3cloud::ResponseError`、`K3cloud::K3cloudError`。
|
|
66
|
+
|
|
67
|
+
## [0.4.2] - 2023-11-29
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- 修复非 200/206 响应被误判为失败的问题(`response.code` 为字符串比较恒为假)。
|
|
72
|
+
|
|
73
|
+
## [0.4.1] - 2023-11-28
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
|
|
77
|
+
- HTTP 请求失败时改为抛出异常,不再静默返回 `nil`。
|
|
78
|
+
|
|
79
|
+
## [0.4.0] - 2023-11-17
|
|
80
|
+
|
|
81
|
+
### Changed
|
|
82
|
+
|
|
83
|
+
- 兼容更低版本 Ruby 语法。
|
|
84
|
+
|
|
85
|
+
## [0.3.0] - 2023-11-14
|
|
86
|
+
|
|
87
|
+
### Added
|
|
88
|
+
|
|
89
|
+
- 多账号配置支持:通过 `K3cloud.new_api(config)` 创建多账号客户端实例。
|
|
90
|
+
|
|
91
|
+
## [0.2.0] - 2023-10-09
|
|
92
|
+
|
|
93
|
+
### Fixed
|
|
94
|
+
|
|
95
|
+
- 修复入口文件大小写问题导致部分平台上 `require "k3cloud-sdk"` 失败。
|
|
96
|
+
|
|
3
97
|
## [0.1.0] - 2023-09-26
|
|
4
98
|
|
|
5
|
-
|
|
99
|
+
### Added
|
|
100
|
+
|
|
101
|
+
- 初始版本:封装金蝶云星空 OpenApi,涵盖暂存/保存/提交/审核/查看/查询/删除/下推/分配等单据操作及附件、工作流、报表等共 26 个业务方法。
|
data/Gemfile
CHANGED
|
@@ -6,5 +6,9 @@ source "https://rubygems.org"
|
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
8
|
gem "rake", "~> 13.0"
|
|
9
|
-
|
|
10
9
|
gem "rspec", "~> 3.0"
|
|
10
|
+
gem "rubocop", require: false
|
|
11
|
+
gem "webmock", "~> 3.0", group: :test
|
|
12
|
+
# Ruby 3.x: base64 became a bundled gem, declare explicitly so vendored bundle resolves it.
|
|
13
|
+
gem "base64", require: false
|
|
14
|
+
gem "json", require: false
|
data/Gemfile.lock
CHANGED
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
k3cloud-sdk (0.
|
|
4
|
+
k3cloud-sdk (0.5.0)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
+
addressable (2.9.0)
|
|
10
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
11
|
+
ast (2.4.3)
|
|
12
|
+
base64 (0.3.0)
|
|
13
|
+
bigdecimal (4.1.2)
|
|
14
|
+
crack (1.0.1)
|
|
15
|
+
bigdecimal
|
|
16
|
+
rexml
|
|
9
17
|
diff-lcs (1.5.0)
|
|
18
|
+
hashdiff (1.2.1)
|
|
19
|
+
json (2.21.1)
|
|
20
|
+
language_server-protocol (3.17.0.6)
|
|
21
|
+
lint_roller (1.1.0)
|
|
22
|
+
parallel (2.1.0)
|
|
23
|
+
parser (3.3.12.0)
|
|
24
|
+
ast (~> 2.4.1)
|
|
25
|
+
racc
|
|
26
|
+
prism (1.9.0)
|
|
27
|
+
public_suffix (7.0.5)
|
|
28
|
+
racc (1.8.1)
|
|
29
|
+
rainbow (3.1.1)
|
|
10
30
|
rake (13.0.6)
|
|
31
|
+
regexp_parser (2.12.0)
|
|
32
|
+
rexml (3.4.4)
|
|
11
33
|
rspec (3.12.0)
|
|
12
34
|
rspec-core (~> 3.12.0)
|
|
13
35
|
rspec-expectations (~> 3.12.0)
|
|
@@ -21,15 +43,42 @@ GEM
|
|
|
21
43
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
22
44
|
rspec-support (~> 3.12.0)
|
|
23
45
|
rspec-support (3.12.1)
|
|
46
|
+
rubocop (1.88.2)
|
|
47
|
+
json (~> 2.3)
|
|
48
|
+
language_server-protocol (~> 3.17.0.2)
|
|
49
|
+
lint_roller (~> 1.1.0)
|
|
50
|
+
parallel (>= 1.10)
|
|
51
|
+
parser (>= 3.3.0.2)
|
|
52
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
53
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
54
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
55
|
+
ruby-progressbar (~> 1.7)
|
|
56
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
57
|
+
rubocop-ast (1.50.0)
|
|
58
|
+
parser (>= 3.3.7.2)
|
|
59
|
+
prism (~> 1.7)
|
|
60
|
+
ruby-progressbar (1.13.0)
|
|
61
|
+
unicode-display_width (3.2.0)
|
|
62
|
+
unicode-emoji (~> 4.1)
|
|
63
|
+
unicode-emoji (4.2.0)
|
|
64
|
+
webmock (3.26.2)
|
|
65
|
+
addressable (>= 2.8.0)
|
|
66
|
+
crack (>= 0.3.2)
|
|
67
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
24
68
|
|
|
25
69
|
PLATFORMS
|
|
26
70
|
arm64-darwin-22
|
|
27
71
|
arm64-darwin-23
|
|
72
|
+
arm64-darwin-25
|
|
28
73
|
|
|
29
74
|
DEPENDENCIES
|
|
75
|
+
base64
|
|
76
|
+
json
|
|
30
77
|
k3cloud-sdk!
|
|
31
78
|
rake (~> 13.0)
|
|
32
79
|
rspec (~> 3.0)
|
|
80
|
+
rubocop
|
|
81
|
+
webmock (~> 3.0)
|
|
33
82
|
|
|
34
83
|
BUNDLED WITH
|
|
35
84
|
2.4.14
|
data/README.md
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# K3cloud-sdk
|
|
2
|
+
|
|
2
3
|
Ruby Gem for K3cloud OpenApi that uses cryptographic signature technology to avoid plaintext transmission of keys and enables automatic login.
|
|
3
4
|
|
|
4
5
|
## Installation
|
|
@@ -14,6 +15,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
|
14
15
|
## Usage
|
|
15
16
|
|
|
16
17
|
#### 1. Single Account Configuration:
|
|
18
|
+
|
|
17
19
|
You'll need to configure it in `config/initializers/k3cloud.rb`:
|
|
18
20
|
|
|
19
21
|
```ruby
|
|
@@ -27,6 +29,7 @@ You'll need to configure it in `config/initializers/k3cloud.rb`:
|
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
Then you can call the k3cloud API using the following code:
|
|
32
|
+
|
|
30
33
|
```ruby
|
|
31
34
|
data = {
|
|
32
35
|
FormId: "",
|
|
@@ -37,6 +40,7 @@ Then you can call the k3cloud API using the following code:
|
|
|
37
40
|
```
|
|
38
41
|
|
|
39
42
|
#### 2. Multi Account Configuration:
|
|
43
|
+
|
|
40
44
|
You'll need to configure it in `config/initializers/k3cloud.rb`:
|
|
41
45
|
|
|
42
46
|
```ruby
|
|
@@ -56,19 +60,20 @@ You'll need to configure it in `config/initializers/k3cloud.rb`:
|
|
|
56
60
|
c.server_url = ENV['K3CLOUD2_SERVER_URL']
|
|
57
61
|
end
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
client1 = K3cloud.new_api(config1)
|
|
64
|
+
client2 = K3cloud.new_api(config2)
|
|
61
65
|
```
|
|
62
66
|
|
|
63
67
|
Then you can call the k3cloud API using the following code:
|
|
68
|
+
|
|
64
69
|
```ruby
|
|
65
70
|
data = {
|
|
66
71
|
FormId: "",
|
|
67
72
|
FieldKeys: "",
|
|
68
73
|
FilterString: ""
|
|
69
74
|
}
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
client1.execute_bill_query(data)
|
|
76
|
+
client2.execute_bill_query(data)
|
|
72
77
|
```
|
|
73
78
|
|
|
74
79
|
## Development
|
|
@@ -79,7 +84,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
|
79
84
|
|
|
80
85
|
## Contributing
|
|
81
86
|
|
|
82
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/zevinto/k3cloud-sdk.
|
|
87
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/zevinto/k3cloud-sdk.
|
|
83
88
|
This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/zevinto/k3cloud-sdk/blob/main/CODE_OF_CONDUCT.md).
|
|
84
89
|
|
|
85
90
|
## License
|
data/k3cloud.gemspec
CHANGED
|
@@ -9,11 +9,15 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.email = ["zevinto@163.com"]
|
|
10
10
|
|
|
11
11
|
spec.summary = "Ruby Gem for K3cloud API."
|
|
12
|
-
spec.description = "Ruby Gem for K3cloud OpenApi
|
|
12
|
+
spec.description = "Ruby Gem for K3cloud OpenApi. Uses cryptographic signature technology to avoid " \
|
|
13
|
+
"plaintext transmission of keys and enables automatic login."
|
|
13
14
|
spec.homepage = "https://github.com/zevinto/k3cloud-sdk"
|
|
14
15
|
spec.license = "MIT"
|
|
15
|
-
spec.required_ruby_version = ">= 2.
|
|
16
|
+
spec.required_ruby_version = ">= 2.5.0"
|
|
16
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
20
|
+
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
|
17
21
|
|
|
18
22
|
# Specify which files should be added to the gem when it is released.
|
|
19
23
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
@@ -29,7 +29,16 @@ module K3cloud
|
|
|
29
29
|
# 组织编码,启用多组织时配置对应的组织编码才有效
|
|
30
30
|
attr_accessor :org_num
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
# 连接/请求超时(秒)。私有云自签证书时可设 verify_ssl=false 跳过校验
|
|
33
|
+
attr_accessor :connect_timeout, :request_timeout, :verify_ssl
|
|
34
|
+
|
|
35
|
+
# 日志输出格式::text(默认,常规 Logger 格式)或 :json(每行一个 JSON)
|
|
36
|
+
attr_accessor :log_format
|
|
37
|
+
|
|
38
|
+
DEFAULT_LCID = 2052
|
|
39
|
+
DEFAULT_CONNECT_TIMEOUT = 30
|
|
40
|
+
DEFAULT_REQUEST_TIMEOUT = 60
|
|
41
|
+
DEFAULT_LOG_FORMAT = :text
|
|
33
42
|
|
|
34
43
|
def initialize(options = {})
|
|
35
44
|
@acct_id = options[:acct_id]
|
|
@@ -39,14 +48,24 @@ module K3cloud
|
|
|
39
48
|
@app_secret = options[:app_secret]
|
|
40
49
|
@server_url = options[:server_url]
|
|
41
50
|
@org_num = options[:org_num]
|
|
42
|
-
@
|
|
43
|
-
@
|
|
51
|
+
@lcid = options.fetch(:lcid, DEFAULT_LCID)
|
|
52
|
+
@connect_timeout = options.fetch(:connect_timeout, DEFAULT_CONNECT_TIMEOUT)
|
|
53
|
+
@request_timeout = options.fetch(:request_timeout, DEFAULT_REQUEST_TIMEOUT)
|
|
54
|
+
@verify_ssl = options.fetch(:verify_ssl, true)
|
|
55
|
+
@log_format = options.fetch(:log_format, DEFAULT_LOG_FORMAT)
|
|
44
56
|
|
|
45
57
|
yield(self) if block_given?
|
|
46
58
|
end
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
class << self
|
|
61
|
+
def default
|
|
62
|
+
@default ||= Configuration.new
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# 测试或重新配置时重置默认配置
|
|
66
|
+
def reset_default!
|
|
67
|
+
@default = nil
|
|
68
|
+
end
|
|
50
69
|
end
|
|
51
70
|
end
|
|
52
71
|
end
|
|
@@ -14,17 +14,22 @@ module K3cloud
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def self.parse(json)
|
|
17
|
+
return nil if json.nil? || json.to_s.empty?
|
|
18
|
+
|
|
19
|
+
json = json.to_s
|
|
17
20
|
index = json.index("{")
|
|
18
|
-
json = json[index..-1] if index
|
|
21
|
+
json = json[index..-1] if index
|
|
19
22
|
|
|
20
23
|
parsed_json = JSON.parse(json)
|
|
21
24
|
kd_error = K3cloudError.new
|
|
22
25
|
kd_error.message = parsed_json["Message"]
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
inner_wrapper = parsed_json["InnerExWrapper"]
|
|
27
|
+
inner_exception = parsed_json["InnerException"]
|
|
28
|
+
kd_error.inner_ex_wrapper = inner_wrapper ? K3cloudError.parse(inner_wrapper.to_json) : nil
|
|
29
|
+
kd_error.inner_exception = inner_exception ? K3cloudError.parse(inner_exception.to_json) : nil
|
|
25
30
|
kd_error
|
|
26
31
|
rescue StandardError => e
|
|
27
|
-
K3cloud.logger.error({
|
|
32
|
+
K3cloud.logger.error({ msg: "Failed to parse exception message. #{e.message}", type: "error", level: "ERROR" })
|
|
28
33
|
nil
|
|
29
34
|
end
|
|
30
35
|
end
|
data/lib/k3cloud/http.rb
CHANGED
|
@@ -9,27 +9,22 @@ require "json"
|
|
|
9
9
|
module K3cloud
|
|
10
10
|
# HTTP Client
|
|
11
11
|
class Http
|
|
12
|
-
attr_accessor :url, :header, :body, :connect_timeout, :request_timeout, :status_code
|
|
12
|
+
attr_accessor :url, :header, :body, :connect_timeout, :request_timeout, :verify_ssl, :status_code
|
|
13
13
|
|
|
14
|
-
def initialize(url
|
|
14
|
+
def initialize(url:, header:, body:, connect_timeout: Configuration::DEFAULT_CONNECT_TIMEOUT,
|
|
15
|
+
request_timeout: Configuration::DEFAULT_REQUEST_TIMEOUT, verify_ssl: true)
|
|
15
16
|
@url = url
|
|
16
17
|
@header = header || {}
|
|
17
18
|
@body = body
|
|
18
19
|
@connect_timeout = connect_timeout
|
|
19
20
|
@request_timeout = request_timeout
|
|
21
|
+
@verify_ssl = verify_ssl
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
def post
|
|
23
25
|
uri = URI.parse(@url)
|
|
24
|
-
http =
|
|
25
|
-
# SSL/TLS configuration
|
|
26
|
-
if uri.scheme == "https"
|
|
27
|
-
http.use_ssl = true
|
|
28
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
29
|
-
end
|
|
26
|
+
http = build_http(uri)
|
|
30
27
|
|
|
31
|
-
http.open_timeout = @connect_timeout
|
|
32
|
-
http.read_timeout = @request_timeout
|
|
33
28
|
request = Net::HTTP::Post.new(uri.request_uri)
|
|
34
29
|
request.initialize_http_header(@header)
|
|
35
30
|
request["Content-Type"] = "application/json"
|
|
@@ -43,14 +38,36 @@ module K3cloud
|
|
|
43
38
|
response.body
|
|
44
39
|
rescue Errno::ETIMEDOUT, Net::OpenTimeout, Net::ReadTimeout => e
|
|
45
40
|
raise K3cloud::ResponseError, "Request timed out: #{e.message}"
|
|
41
|
+
rescue K3cloud::ResponseError
|
|
42
|
+
raise
|
|
46
43
|
rescue Net::HTTPServerError => e
|
|
47
44
|
raise K3cloud::ResponseError, "Server error: #{e.message}"
|
|
48
|
-
rescue => e
|
|
45
|
+
rescue StandardError => e
|
|
49
46
|
raise K3cloud::ResponseError, "Unexpected error: #{e.message}"
|
|
50
47
|
end
|
|
51
48
|
|
|
52
49
|
private
|
|
53
50
|
|
|
51
|
+
def build_http(uri)
|
|
52
|
+
http = connection(uri) || Net::HTTP.new(uri.host, uri.port)
|
|
53
|
+
configure_ssl!(http, uri) if uri.scheme == "https"
|
|
54
|
+
http.open_timeout = @connect_timeout
|
|
55
|
+
http.read_timeout = @request_timeout
|
|
56
|
+
http.write_timeout = @request_timeout if http.respond_to?(:write_timeout=)
|
|
57
|
+
http.ssl_timeout = @request_timeout if http.respond_to?(:ssl_timeout=) && uri.scheme == "https"
|
|
58
|
+
http
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def configure_ssl!(http, _uri)
|
|
62
|
+
http.use_ssl = true
|
|
63
|
+
http.verify_mode = @verify_ssl ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# 子类可覆盖以注入复用的连接(如 Net::HTTP::Persistent)。
|
|
67
|
+
def connection(_uri)
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
|
|
54
71
|
# Generate User-Agent string with client info
|
|
55
72
|
def generate_user_agent
|
|
56
73
|
"Kingdee/Ruby WebApi SDK(v#{K3cloud::VERSION}) (Ruby #{RUBY_VERSION}; #{RUBY_PLATFORM})"
|
data/lib/k3cloud/k3cloud_api.rb
CHANGED
|
@@ -135,9 +135,15 @@ module K3cloud
|
|
|
135
135
|
|
|
136
136
|
private
|
|
137
137
|
|
|
138
|
+
# 金蝶 ExecuteBillQuery 在出错时会把错误信息放在首行首列(一个 Hash),
|
|
139
|
+
# 正常查询则返回二维数组。这里识别并记录错误,避免把错误对象当业务数据返回。
|
|
138
140
|
def handle_query_result(rows)
|
|
139
|
-
if
|
|
140
|
-
|
|
141
|
+
return rows if rows.nil? || rows.empty?
|
|
142
|
+
|
|
143
|
+
first_row = rows[0]
|
|
144
|
+
result = first_row.nil? || first_row.empty? ? nil : first_row[0]
|
|
145
|
+
if result.is_a?(Hash)
|
|
146
|
+
K3cloud.logger.error({ msg: result, type: "error", level: "ERROR" })
|
|
141
147
|
[]
|
|
142
148
|
else
|
|
143
149
|
rows
|
|
@@ -2,18 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
require "base64"
|
|
4
4
|
|
|
5
|
-
module
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
end
|
|
5
|
+
module K3cloud
|
|
6
|
+
module Base64Utils
|
|
7
|
+
def self.encoding_to_base64(buffer)
|
|
8
|
+
b64buffer = Base64.strict_encode64(buffer.pack("C*"))
|
|
9
|
+
b64buffer.force_encoding("UTF-8")
|
|
10
|
+
end
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
raise e
|
|
12
|
+
def self.decoding_from_base64(base64)
|
|
13
|
+
buffer = base64.encode("UTF-8")
|
|
14
|
+
Base64.strict_decode64(buffer).bytes
|
|
15
|
+
end
|
|
18
16
|
end
|
|
19
17
|
end
|
|
@@ -1,43 +1,45 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module
|
|
4
|
-
|
|
3
|
+
module K3cloud
|
|
4
|
+
module ConstDefine
|
|
5
|
+
X_API_CLIENT_ID = "X-Api-ClientID"
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
X_API_AUTH_VERSION = "X-Api-Auth-Version"
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
X_API_TIMESTAMP = "x-api-timestamp"
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
X_API_NONCE = "x-api-nonce"
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
X_API_SIGN_HEADERS = "x-api-signheaders"
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
X_API_SIGNATURE = "X-Api-Signature"
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
X_KD_APP_KEY = "X-Kd-Appkey"
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
X_KD_APP_DATA = "X-Kd-Appdata"
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
X_KD_SIGNATURE = "X-Kd-Signature"
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
KD_SERVICE_SESSIONID = "kdservice-sessionid"
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
SESSION_ID = "ASP.NET_SessionId"
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
X_KD_SID = "SID"
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
COOKIE_SET = "Set-Cookie"
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
BEGIN_METHOD_HEADER = "beginmethod"
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
QUERY_METHOD_HEADER = "querymethod"
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
BEGIN_METHOD_METHOD = "BeginQueryImpl"
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
QUERY_METHOD_METHOD = "QueryAsyncResult"
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
QUERY_INVODE_STATE_PENDING = 0
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
QUERY_INVODE_STATE_RUNNING = 1
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
QUERY_INVODE_STATE_COMPLETE = 2
|
|
44
|
+
end
|
|
43
45
|
end
|
|
@@ -3,33 +3,26 @@
|
|
|
3
3
|
require "digest"
|
|
4
4
|
require "base64"
|
|
5
5
|
|
|
6
|
-
module
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
m.update(data_str.encode("UTF-8"))
|
|
10
|
-
s = m.digest
|
|
11
|
-
result = ""
|
|
6
|
+
module K3cloud
|
|
7
|
+
module MD5Utils
|
|
8
|
+
UNSECURE_KEY = "0054f397c6234378b09ca7d3e5debce7"
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
# 返回 32 位小写十六进制 MD5。原来的逐字节位运算 hack 在 Ruby 下会得到 `nil`,
|
|
11
|
+
# 且被 rescue 吞掉返回空串,故直接使用 Digest::MD5.hexdigest。
|
|
12
|
+
def self.encrypt(data_str)
|
|
13
|
+
Digest::MD5.hexdigest(data_str.encode("UTF-8"))
|
|
15
14
|
end
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
e.backtrace
|
|
20
|
-
""
|
|
21
|
-
end
|
|
16
|
+
def self.hash_mac(data, secret)
|
|
17
|
+
raise ArgumentError, "secret is required for HMAC" if secret.nil? || secret.to_s.empty?
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
rescue StandardError => e
|
|
28
|
-
e.backtrace
|
|
29
|
-
nil
|
|
30
|
-
end
|
|
19
|
+
kd_mac = OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha256"), secret, data)
|
|
20
|
+
hex_string = bytes_to_hex(kd_mac.bytes)
|
|
21
|
+
Base64.strict_encode64(hex_string)
|
|
22
|
+
end
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
def self.bytes_to_hex(bytes)
|
|
25
|
+
bytes.map { |byte| byte.to_s(16).rjust(2, "0") }.join
|
|
26
|
+
end
|
|
34
27
|
end
|
|
35
28
|
end
|
data/lib/k3cloud/version.rb
CHANGED
|
@@ -22,7 +22,8 @@ module K3cloud
|
|
|
22
22
|
error = K3cloudError.parse(result)
|
|
23
23
|
raise K3cloud::ResponseError, result if error.nil?
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
inner_msg = error.inner_ex_wrapper&.message
|
|
26
|
+
message = inner_msg ? "#{error.message} --> #{inner_msg}" : error.message
|
|
26
27
|
raise K3cloud::ResponseError, message
|
|
27
28
|
|
|
28
29
|
else
|
|
@@ -42,28 +43,37 @@ module K3cloud
|
|
|
42
43
|
|
|
43
44
|
header = build_header(url_path(url))
|
|
44
45
|
body = { parameters: parameters }
|
|
45
|
-
request = Http.new(
|
|
46
|
+
request = Http.new(
|
|
47
|
+
url: url,
|
|
48
|
+
header: header,
|
|
49
|
+
body: body,
|
|
50
|
+
connect_timeout: @config.connect_timeout,
|
|
51
|
+
request_timeout: @config.request_timeout,
|
|
52
|
+
verify_ssl: @config.verify_ssl
|
|
53
|
+
)
|
|
46
54
|
request.post
|
|
47
55
|
end
|
|
48
56
|
|
|
49
57
|
def build_url(service_name)
|
|
50
|
-
base_url = if @config.server_url.nil? || @config.server_url.empty?
|
|
51
|
-
"https://api.kingdee.com/galaxyapi/"
|
|
52
|
-
else
|
|
53
|
-
@config.server_url.to_s.strip.chomp("/")
|
|
54
|
-
end
|
|
55
58
|
"#{base_url}/#{service_name}.common.kdsvc"
|
|
56
59
|
end
|
|
57
60
|
|
|
58
|
-
def
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
def base_url
|
|
62
|
+
server = @config.server_url
|
|
63
|
+
if server.nil? || server.empty?
|
|
64
|
+
"https://api.kingdee.com/galaxyapi"
|
|
62
65
|
else
|
|
63
|
-
|
|
66
|
+
server.to_s.strip.chomp("/")
|
|
64
67
|
end
|
|
65
68
|
end
|
|
66
69
|
|
|
70
|
+
def url_path(url)
|
|
71
|
+
return url unless url.start_with?("http")
|
|
72
|
+
|
|
73
|
+
index = url.index("/", 10)
|
|
74
|
+
index ? url[index..-1] : url
|
|
75
|
+
end
|
|
76
|
+
|
|
67
77
|
# @note: https://vip.kingdee.com/knowledge/specialDetail/229961573895771136?category=229964512944566016&id=423060878259269120&productLineId=1
|
|
68
78
|
def build_header(url)
|
|
69
79
|
header = {}
|
|
@@ -94,9 +104,6 @@ module K3cloud
|
|
|
94
104
|
header[ConstDefine::X_KD_SIGNATURE] = kd_signature
|
|
95
105
|
end
|
|
96
106
|
header
|
|
97
|
-
rescue StandardError => e
|
|
98
|
-
K3cloud.logger.error({ errmsg: "Build header exception.", backtrace: "#{e.backtrace}", type: 'error', lever: 'ERROR' })
|
|
99
|
-
{}
|
|
100
107
|
end
|
|
101
108
|
|
|
102
109
|
def decode_sec(sec)
|
data/lib/k3cloud.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "k3cloud/version"
|
|
4
4
|
require "logger"
|
|
5
|
+
require "json"
|
|
5
6
|
require "k3cloud/configuration"
|
|
6
7
|
require "k3cloud/k3cloud_api"
|
|
7
8
|
require "forwardable"
|
|
@@ -19,20 +20,67 @@ module K3cloud
|
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def logger
|
|
22
|
-
@logger ||=
|
|
23
|
+
@logger ||= build_logger
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# 注入自定义 logger(例如 Rails.logger)。设为 nil 可触发下次重建。
|
|
27
|
+
attr_writer :logger
|
|
28
|
+
|
|
29
|
+
# 重建默认 logger(切换 log_format 后调用)
|
|
30
|
+
def reset_logger!
|
|
31
|
+
@logger = nil
|
|
23
32
|
end
|
|
24
33
|
|
|
25
34
|
def new_api(config = nil)
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
config ? K3cloud::K3cloudApi.new(config) : default_api
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# 默认账号客户端(带缓存;传入新 config 不会污染此处)
|
|
39
|
+
def default_api
|
|
40
|
+
@default_api ||= K3cloud::K3cloudApi.new
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# 测试/重新配置时重置默认客户端
|
|
44
|
+
def reset_default_api!
|
|
45
|
+
@default_api = nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
API_METHODS = %i[
|
|
49
|
+
draft save batch_save submit audit un_audit view execute_bill_query delete cancel_assign push execute_operation
|
|
50
|
+
allocate cancel_allocate group_save query_group_info group_delete attachment_upload attachment_download
|
|
51
|
+
query_business_info get_sys_report_data flex_save send_msg disassembly workflow_audit switch_org
|
|
52
|
+
].freeze
|
|
53
|
+
|
|
54
|
+
def_delegators :default_api, *API_METHODS
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def build_logger
|
|
59
|
+
logger = ::Logger.new($stderr)
|
|
60
|
+
logger.formatter = method(:format_log)
|
|
61
|
+
logger
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# 根据 Configuration.default.log_format 决定输出::text(默认)或 :json
|
|
65
|
+
def format_log(severity, datetime, _progname, msg)
|
|
66
|
+
if Configuration.default.log_format == :json
|
|
67
|
+
format_log_json(severity, datetime, msg)
|
|
28
68
|
else
|
|
29
|
-
|
|
69
|
+
format_log_text(severity, datetime, msg)
|
|
30
70
|
end
|
|
31
71
|
end
|
|
32
72
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
73
|
+
def format_log_json(severity, datetime, msg)
|
|
74
|
+
payload = msg.is_a?(Hash) ? msg.dup : { msg: msg.to_s }
|
|
75
|
+
payload[:level] = severity
|
|
76
|
+
payload[:time] = datetime.strftime("%Y-%m-%dT%H:%M:%S.%L")
|
|
77
|
+
"#{payload.to_json}\n"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def format_log_text(severity, datetime, msg)
|
|
81
|
+
body = msg.is_a?(Hash) ? msg.inspect : msg.to_s
|
|
82
|
+
format("%s, [%s#%d] %5s -- %s\n", severity[0..0],
|
|
83
|
+
datetime.strftime("%Y-%m-%dT%H:%M:%S.%L"), Process.pid, severity, body)
|
|
84
|
+
end
|
|
37
85
|
end
|
|
38
86
|
end
|
metadata
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: k3cloud-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- zevinto
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
|
-
description: Ruby Gem for K3cloud OpenApi
|
|
12
|
+
description: Ruby Gem for K3cloud OpenApi. Uses cryptographic signature technology
|
|
14
13
|
to avoid plaintext transmission of keys and enables automatic login.
|
|
15
14
|
email:
|
|
16
15
|
- zevinto@163.com
|
|
@@ -49,7 +48,9 @@ licenses:
|
|
|
49
48
|
- MIT
|
|
50
49
|
metadata:
|
|
51
50
|
homepage_uri: https://github.com/zevinto/k3cloud-sdk
|
|
52
|
-
|
|
51
|
+
source_code_uri: https://github.com/zevinto/k3cloud-sdk
|
|
52
|
+
changelog_uri: https://github.com/zevinto/k3cloud-sdk/blob/main/CHANGELOG.md
|
|
53
|
+
bug_tracker_uri: https://github.com/zevinto/k3cloud-sdk/issues
|
|
53
54
|
rdoc_options: []
|
|
54
55
|
require_paths:
|
|
55
56
|
- lib
|
|
@@ -57,15 +58,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
57
58
|
requirements:
|
|
58
59
|
- - ">="
|
|
59
60
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: 2.
|
|
61
|
+
version: 2.5.0
|
|
61
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
63
|
requirements:
|
|
63
64
|
- - ">="
|
|
64
65
|
- !ruby/object:Gem::Version
|
|
65
66
|
version: '0'
|
|
66
67
|
requirements: []
|
|
67
|
-
rubygems_version: 3.
|
|
68
|
-
signing_key:
|
|
68
|
+
rubygems_version: 3.6.9
|
|
69
69
|
specification_version: 4
|
|
70
70
|
summary: Ruby Gem for K3cloud API.
|
|
71
71
|
test_files: []
|