fiuai-sdk-python 0.15.0__tar.gz → 0.16.1__tar.gz
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.
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/CHANGELOG.md +12 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/PKG-INFO +1 -1
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/pyproject.toml +1 -3
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/__init__.py +1 -1
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/auth/__init__.py +13 -2
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/auth/context_mgr.py +4 -1
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/auth/header.py +45 -4
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/auth/helper.py +21 -1
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/auth/type.py +10 -1
- fiuai_sdk_python-0.16.1/src/fiuai_sdk_python/bank.py +175 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/client.py +3 -1
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/context.py +5 -1
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/http/client.py +1 -0
- fiuai_sdk_python-0.15.0/src/fiuai_sdk_python/bank.py +0 -175
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/.gitignore +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/LICENSE +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/README.md +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/_resp_helpers.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/company.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/config.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/const.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/datatype.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/doctype.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/error.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/error_code.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/error_parsers.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/examples/fastapi_integration.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/http/__init__.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/item.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/perm.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/__init__.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/cache_client.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/circuit_breaker.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/decorator.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/redis_manager.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/types.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/scheduler/__init__.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/scheduler/runner.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/profile.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/resp.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/setup.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/type.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/util.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/utils/__init__.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/utils/ids.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/utils/logger.py +0 -0
- {fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/utils/text.py +0 -0
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.16.1] - 2026-07-18
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Bank Account 已存在但未关联标准 Bank/Branch 时仍返回 `exists=True`,关联字段作为可选 enrichment。
|
|
12
|
+
- 修正 `CompanyBankAccountSearchResult.bank_id` 错误返回银行账号 ID。
|
|
13
|
+
|
|
14
|
+
## [0.16.0] - 2026-07-15
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- 新增 `X-FiuAI-Roles` JSON 数组认证头及 `AuthData.roles`, 支持严格解析、上下文传播和下游 HTTP 注入。
|
|
18
|
+
- 新增 `get_roles()` 与 `get_effective_tenant_id()` 认证 helper。
|
|
19
|
+
|
|
8
20
|
## [0.13.0] - 2026-05-13
|
|
9
21
|
|
|
10
22
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fiuai_sdk_python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.1
|
|
4
4
|
Summary: FiuAI Python SDK - 企业级AI服务集成开发工具包
|
|
5
5
|
Project-URL: Homepage, https://github.com/fiuai/fiuai-sdk-python
|
|
6
6
|
Project-URL: Documentation, https://github.com/fiuai/fiuai-sdk-python#readme
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "fiuai_sdk_python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.16.1"
|
|
4
4
|
description = "FiuAI Python SDK - 企业级AI服务集成开发工具包"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -74,5 +74,3 @@ dev-dependencies = [
|
|
|
74
74
|
"mypy>=1.0.0",
|
|
75
75
|
"pre-commit>=3.0.0",
|
|
76
76
|
]
|
|
77
|
-
|
|
78
|
-
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
from .header import
|
|
1
|
+
from .header import (
|
|
2
|
+
extract_auth_from_request,
|
|
3
|
+
parse_auth_headers,
|
|
4
|
+
parse_roles_header,
|
|
5
|
+
serialize_roles_header,
|
|
6
|
+
)
|
|
2
7
|
from .type import AuthData, AuthHeader
|
|
3
8
|
from .helper import (
|
|
4
9
|
get_auth_data,
|
|
@@ -7,6 +12,8 @@ from .helper import (
|
|
|
7
12
|
get_current_company,
|
|
8
13
|
get_company_unique_no,
|
|
9
14
|
get_impersonation,
|
|
15
|
+
get_roles,
|
|
16
|
+
get_effective_tenant_id,
|
|
10
17
|
is_impersonating,
|
|
11
18
|
)
|
|
12
19
|
from .context_mgr import (
|
|
@@ -27,6 +34,8 @@ get_auth_data_from_request = get_auth_data
|
|
|
27
34
|
__all__ = [
|
|
28
35
|
"parse_auth_headers",
|
|
29
36
|
"extract_auth_from_request",
|
|
37
|
+
"parse_roles_header",
|
|
38
|
+
"serialize_roles_header",
|
|
30
39
|
"AuthData",
|
|
31
40
|
"AuthHeader",
|
|
32
41
|
"get_auth_data",
|
|
@@ -37,6 +46,8 @@ __all__ = [
|
|
|
37
46
|
"get_current_company",
|
|
38
47
|
"get_company_unique_no",
|
|
39
48
|
"get_impersonation",
|
|
49
|
+
"get_roles",
|
|
50
|
+
"get_effective_tenant_id",
|
|
40
51
|
"is_impersonating",
|
|
41
52
|
"ContextManager",
|
|
42
53
|
"init_context",
|
|
@@ -46,4 +57,4 @@ __all__ = [
|
|
|
46
57
|
"get_world_data",
|
|
47
58
|
"set_user_profile_info",
|
|
48
59
|
"get_user_profile_info",
|
|
49
|
-
]
|
|
60
|
+
]
|
{fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/auth/context_mgr.py
RENAMED
|
@@ -20,7 +20,7 @@ from pydantic import BaseModel, Field
|
|
|
20
20
|
|
|
21
21
|
from ..context import RequestContext, get_current_headers, is_current_context_valid
|
|
22
22
|
from .type import AuthData, FIUAI_CLIENT_VALUES
|
|
23
|
-
from .header import parse_auth_headers
|
|
23
|
+
from .header import parse_auth_headers, serialize_roles_header
|
|
24
24
|
|
|
25
25
|
logger = logging.getLogger(__name__)
|
|
26
26
|
|
|
@@ -92,6 +92,7 @@ class ContextManager:
|
|
|
92
92
|
"x-fiuai-channel": "default",
|
|
93
93
|
"x-fiuai-lang": auth_data.lang or "zh",
|
|
94
94
|
"accept-language": auth_data.accept_language or "zh",
|
|
95
|
+
"x-fiuai-roles": serialize_roles_header(auth_data.roles),
|
|
95
96
|
}
|
|
96
97
|
return RequestContext.from_dict(headers)
|
|
97
98
|
|
|
@@ -182,6 +183,7 @@ def init_context(
|
|
|
182
183
|
lang=kwargs.get("lang", "zh"),
|
|
183
184
|
accept_language=kwargs.get("accept_language", "zh"),
|
|
184
185
|
model=(kwargs.get("model") or "auto").strip() or "auto",
|
|
186
|
+
roles=kwargs.get("roles") or [],
|
|
185
187
|
)
|
|
186
188
|
return ContextManager(
|
|
187
189
|
auth_data=auth_data,
|
|
@@ -275,6 +277,7 @@ def update_auth_data(
|
|
|
275
277
|
lang=kwargs.get("lang", current.lang),
|
|
276
278
|
accept_language=kwargs.get("accept_language", current.accept_language),
|
|
277
279
|
model=model,
|
|
280
|
+
roles=kwargs.get("roles", current.roles),
|
|
278
281
|
)
|
|
279
282
|
return set_auth_data(new_auth_data)
|
|
280
283
|
except Exception as e:
|
|
@@ -5,12 +5,51 @@
|
|
|
5
5
|
# Email: lmlala@aliyun.com
|
|
6
6
|
# Copyright (c) 2025 FiuAI
|
|
7
7
|
|
|
8
|
+
import json
|
|
8
9
|
from typing import Dict, Optional, Union, Literal
|
|
9
10
|
from .type import AuthData, FIUAI_CLIENT_VALUES
|
|
10
11
|
from fastapi import Request
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
def
|
|
14
|
+
def parse_roles_header(raw_value: str | None) -> list[str]:
|
|
15
|
+
"""严格解析 ``X-FiuAI-Roles`` JSON 数组并保持首次出现顺序."""
|
|
16
|
+
value = (raw_value or "").strip()
|
|
17
|
+
if not value:
|
|
18
|
+
return []
|
|
19
|
+
try:
|
|
20
|
+
decoded = json.loads(value)
|
|
21
|
+
except json.JSONDecodeError as exc:
|
|
22
|
+
raise ValueError("Invalid header x-fiuai-roles: expected JSON array") from exc
|
|
23
|
+
if not isinstance(decoded, list):
|
|
24
|
+
raise ValueError("Invalid header x-fiuai-roles: expected JSON array")
|
|
25
|
+
|
|
26
|
+
roles: list[str] = []
|
|
27
|
+
seen: set[str] = set()
|
|
28
|
+
for raw_role in decoded:
|
|
29
|
+
if not isinstance(raw_role, str):
|
|
30
|
+
raise ValueError("Invalid header x-fiuai-roles: roles must be strings")
|
|
31
|
+
role = raw_role.strip()
|
|
32
|
+
if not role:
|
|
33
|
+
raise ValueError("Invalid header x-fiuai-roles: role cannot be empty")
|
|
34
|
+
if role not in seen:
|
|
35
|
+
seen.add(role)
|
|
36
|
+
roles.append(role)
|
|
37
|
+
return roles
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def serialize_roles_header(roles: list[str] | tuple[str, ...] | None) -> str:
|
|
41
|
+
"""把角色代码规范化为紧凑、无歧义的 JSON header 值."""
|
|
42
|
+
normalized = parse_roles_header(
|
|
43
|
+
json.dumps(list(roles or []), ensure_ascii=True, separators=(",", ":"))
|
|
44
|
+
)
|
|
45
|
+
return json.dumps(normalized, ensure_ascii=True, separators=(",", ":"))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def parse_auth_headers(
|
|
49
|
+
headers: Dict[str, str],
|
|
50
|
+
*,
|
|
51
|
+
allow_empty_company: bool = False,
|
|
52
|
+
) -> Optional[AuthData]:
|
|
14
53
|
"""
|
|
15
54
|
从 HTTP headers 中解析认证信息并转换为 AuthData
|
|
16
55
|
|
|
@@ -38,6 +77,7 @@ def parse_auth_headers(headers: Dict[str, str]) -> Optional[AuthData]:
|
|
|
38
77
|
channel = "default"
|
|
39
78
|
lang = headers.get("x-fiuai-lang", "zh") or "zh"
|
|
40
79
|
accept_language = headers.get("accept-language", "zh") or "zh"
|
|
80
|
+
roles = parse_roles_header(headers.get("x-fiuai-roles"))
|
|
41
81
|
|
|
42
82
|
# 验证必需字段
|
|
43
83
|
if not user_id:
|
|
@@ -51,17 +91,17 @@ def parse_auth_headers(headers: Dict[str, str]) -> Optional[AuthData]:
|
|
|
51
91
|
current_company = [company.strip() for company in current_company_str.split(",") if company.strip()]
|
|
52
92
|
|
|
53
93
|
# 检查是否有有效的公司信息
|
|
54
|
-
if not current_company:
|
|
94
|
+
if not current_company and not allow_empty_company:
|
|
55
95
|
raise ValueError("Missing required header: x-fiuai-current-company")
|
|
56
96
|
|
|
57
97
|
# 如果没有提供 unique_no,使用第一个公司ID作为默认值
|
|
58
|
-
if not unique_no:
|
|
98
|
+
if not unique_no and current_company:
|
|
59
99
|
unique_no = current_company[0]
|
|
60
100
|
|
|
61
101
|
return AuthData(
|
|
62
102
|
user_id=user_id,
|
|
63
103
|
auth_tenant_id=auth_tenant_id,
|
|
64
|
-
current_company=current_company[0],
|
|
104
|
+
current_company=current_company[0] if current_company else "",
|
|
65
105
|
impersonation=impersonation,
|
|
66
106
|
company_unique_no=unique_no,
|
|
67
107
|
trace_id=trace_id,
|
|
@@ -70,6 +110,7 @@ def parse_auth_headers(headers: Dict[str, str]) -> Optional[AuthData]:
|
|
|
70
110
|
lang=lang,
|
|
71
111
|
accept_language=accept_language,
|
|
72
112
|
model=model,
|
|
113
|
+
roles=roles,
|
|
73
114
|
)
|
|
74
115
|
|
|
75
116
|
except Exception as e:
|
|
@@ -103,6 +103,26 @@ def get_impersonation(
|
|
|
103
103
|
return auth_data.impersonation if auth_data else None
|
|
104
104
|
|
|
105
105
|
|
|
106
|
+
def get_roles(
|
|
107
|
+
request: Optional[Union[Request, Dict[str, str]]] = None,
|
|
108
|
+
engine: Literal["fastapi", "dict"] = "fastapi",
|
|
109
|
+
) -> list[str]:
|
|
110
|
+
"""获取当前认证角色;无认证上下文时返回空列表."""
|
|
111
|
+
auth_data = get_auth_data(request, engine)
|
|
112
|
+
return list(auth_data.roles) if auth_data else []
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_effective_tenant_id(
|
|
116
|
+
request: Optional[Union[Request, Dict[str, str]]] = None,
|
|
117
|
+
engine: Literal["fastapi", "dict"] = "fastapi",
|
|
118
|
+
) -> Optional[str]:
|
|
119
|
+
"""获取权限判断使用的有效租户,impersonation 优先."""
|
|
120
|
+
auth_data = get_auth_data(request, engine)
|
|
121
|
+
if not auth_data:
|
|
122
|
+
return None
|
|
123
|
+
return auth_data.impersonation or auth_data.auth_tenant_id
|
|
124
|
+
|
|
125
|
+
|
|
106
126
|
def get_current_client(
|
|
107
127
|
request: Optional[Union[Request, Dict[str, str]]] = None,
|
|
108
128
|
engine: Literal["fastapi", "dict"] = "fastapi",
|
|
@@ -137,4 +157,4 @@ def get_current_model_mode(
|
|
|
137
157
|
获取当前模型模式。request 为 None 时从当前上下文取;无上下文或未认证时返回 None。
|
|
138
158
|
"""
|
|
139
159
|
auth_data = get_auth_data(request, engine)
|
|
140
|
-
return auth_data.model if auth_data else None
|
|
160
|
+
return auth_data.model if auth_data else None
|
|
@@ -31,6 +31,10 @@ class AuthData(BaseModel):
|
|
|
31
31
|
description="下游/本地自定义 LLM 模型标识, 请求头 x-fiuai-model, 默认 auto",
|
|
32
32
|
default="auto",
|
|
33
33
|
)
|
|
34
|
+
roles: list[str] = Field(
|
|
35
|
+
description="网关认证后注入的规范化角色代码",
|
|
36
|
+
default_factory=list,
|
|
37
|
+
)
|
|
34
38
|
|
|
35
39
|
# edoc
|
|
36
40
|
|
|
@@ -49,8 +53,13 @@ class AuthHeader(BaseModel):
|
|
|
49
53
|
default="unknown",
|
|
50
54
|
description="本地请求自定义 model, 默认 unknown",
|
|
51
55
|
)
|
|
56
|
+
x_fiuai_roles: str = Field(
|
|
57
|
+
alias="x-fiuai-roles",
|
|
58
|
+
default="[]",
|
|
59
|
+
description="规范化角色代码 JSON 数组",
|
|
60
|
+
)
|
|
52
61
|
x_fiuai_channel: str = Field(alias="x-fiuai-channel", default="default", description="渠道code,目前未用")
|
|
53
62
|
accept_language: str = Field(alias="accept-language", default="zh", description="语言")
|
|
54
63
|
|
|
55
64
|
class Config:
|
|
56
|
-
populate_by_name = True
|
|
65
|
+
populate_by_name = True
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# -- coding: utf-8 --
|
|
2
|
+
# Project: fiuaiclient
|
|
3
|
+
# Created Date: 2025 06 Fr
|
|
4
|
+
# Author: liming
|
|
5
|
+
# Email: lmlala@aliyun.com
|
|
6
|
+
# Copyright (c) 2025 FiuAI
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
from .client import FiuaiSDK
|
|
13
|
+
from .type import BankAccount
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CompanyBankAccountSearchResult(BaseModel):
|
|
19
|
+
bank_account_id: str = Field(description="银行账号id", default="")
|
|
20
|
+
bank_account_no: str = Field(description="银行账号", default="")
|
|
21
|
+
bank_account_name: str = Field(description="银行账号", default="")
|
|
22
|
+
bank_id: str = Field(description="银行id", default="")
|
|
23
|
+
bank_name: str = Field(description="银行名称", default="")
|
|
24
|
+
opening_bank_branch_id: str = Field(description="开户行id", default="")
|
|
25
|
+
opening_bank_branch_name: str = Field(description="开户行名称", default="")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
exists: bool = Field(description="是否存在", default=False)
|
|
29
|
+
|
|
30
|
+
comment: str = Field(description="备注", default="")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_bank_account(
|
|
34
|
+
client: FiuaiSDK,
|
|
35
|
+
bank_account_no: str,
|
|
36
|
+
) -> Optional[CompanyBankAccountSearchResult]:
|
|
37
|
+
"""
|
|
38
|
+
根据银行账号获取银行账号信息
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
bank_account_no: 银行账号
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
CompanyBankAccountSearchResult: 银行账号信息
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if not bank_account_no:
|
|
48
|
+
logger.error("bank_account_no is empty, return None")
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
bank_account_response = client.internal_get(
|
|
52
|
+
doctype="Bank Account",
|
|
53
|
+
name=bank_account_no,
|
|
54
|
+
fields=[
|
|
55
|
+
"name",
|
|
56
|
+
"bank_account_no",
|
|
57
|
+
"account_name",
|
|
58
|
+
"bank",
|
|
59
|
+
"opening_bank_branch",
|
|
60
|
+
],
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if not bank_account_response.is_success():
|
|
64
|
+
return CompanyBankAccountSearchResult(exists=False)
|
|
65
|
+
|
|
66
|
+
bank_account = bank_account_response.data
|
|
67
|
+
if not bank_account:
|
|
68
|
+
return CompanyBankAccountSearchResult(exists=False)
|
|
69
|
+
|
|
70
|
+
bank_id = str(bank_account.get("bank") or "")
|
|
71
|
+
bank_name = ""
|
|
72
|
+
if bank_id:
|
|
73
|
+
bank_response = client.internal_get(
|
|
74
|
+
doctype="Bank",
|
|
75
|
+
name=bank_id,
|
|
76
|
+
fields=["name", "bank_name"],
|
|
77
|
+
)
|
|
78
|
+
if bank_response.is_success() and bank_response.data:
|
|
79
|
+
bank_name = str(bank_response.data.get("bank_name") or "")
|
|
80
|
+
else:
|
|
81
|
+
logger.warning("bank enrichment failed for id %s", bank_id)
|
|
82
|
+
|
|
83
|
+
opening_value = str(bank_account.get("opening_bank_branch") or "")
|
|
84
|
+
opening_bank_branch_id = ""
|
|
85
|
+
opening_bank_branch_name = opening_value
|
|
86
|
+
if bank_id and opening_value:
|
|
87
|
+
branch_response = client.internal_get(
|
|
88
|
+
doctype="Bank Branch",
|
|
89
|
+
name=opening_value,
|
|
90
|
+
fields=["name", "bank_branch_name"],
|
|
91
|
+
)
|
|
92
|
+
if branch_response.is_success() and branch_response.data:
|
|
93
|
+
opening_bank_branch_id = str(branch_response.data.get("name") or "")
|
|
94
|
+
opening_bank_branch_name = str(
|
|
95
|
+
branch_response.data.get("bank_branch_name") or ""
|
|
96
|
+
)
|
|
97
|
+
else:
|
|
98
|
+
logger.warning("bank branch enrichment failed for id %s", opening_value)
|
|
99
|
+
|
|
100
|
+
return CompanyBankAccountSearchResult(
|
|
101
|
+
exists=True,
|
|
102
|
+
bank_account_id=str(bank_account.get("name") or ""),
|
|
103
|
+
bank_account_no=str(bank_account.get("bank_account_no") or ""),
|
|
104
|
+
bank_account_name=str(bank_account.get("account_name") or ""),
|
|
105
|
+
bank_id=bank_id,
|
|
106
|
+
bank_name=bank_name,
|
|
107
|
+
opening_bank_branch_id=opening_bank_branch_id,
|
|
108
|
+
opening_bank_branch_name=opening_bank_branch_name,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def get_bank_account_by_account_no(
|
|
113
|
+
client: FiuaiSDK,
|
|
114
|
+
account_no: str,
|
|
115
|
+
) -> BankAccount | None:
|
|
116
|
+
"""
|
|
117
|
+
根据银行账号id获取银行账号信息
|
|
118
|
+
"""
|
|
119
|
+
response = client.internal_get_list(
|
|
120
|
+
doctype="Bank Account",
|
|
121
|
+
filters=[["bank_account_no", "=", account_no]],
|
|
122
|
+
fields=[
|
|
123
|
+
"name",
|
|
124
|
+
"account_name",
|
|
125
|
+
"bank",
|
|
126
|
+
"bank_account_no",
|
|
127
|
+
"opening_bank_branch",
|
|
128
|
+
],
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if not response.is_success():
|
|
132
|
+
logger.warning("bank account is not found by account no %s", account_no)
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
rows = response.data
|
|
136
|
+
if not rows:
|
|
137
|
+
logger.warning("bank account is not found by account no %s", account_no)
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
bank_account = rows[0]
|
|
141
|
+
bank_id = str(bank_account.get("bank") or "")
|
|
142
|
+
bank_name = ""
|
|
143
|
+
if bank_id:
|
|
144
|
+
bank_response = client.internal_get(
|
|
145
|
+
doctype="Bank",
|
|
146
|
+
name=bank_id,
|
|
147
|
+
fields=["name", "bank_name"],
|
|
148
|
+
)
|
|
149
|
+
if bank_response.is_success() and bank_response.data:
|
|
150
|
+
bank_name = str(bank_response.data.get("bank_name") or "")
|
|
151
|
+
else:
|
|
152
|
+
logger.warning("bank enrichment failed for id %s", bank_id)
|
|
153
|
+
|
|
154
|
+
opening_value = str(bank_account.get("opening_bank_branch") or "")
|
|
155
|
+
opening_bank_branch_name = opening_value
|
|
156
|
+
if bank_id and opening_value:
|
|
157
|
+
branch_response = client.internal_get(
|
|
158
|
+
doctype="Bank Branch",
|
|
159
|
+
name=opening_value,
|
|
160
|
+
fields=["name", "bank_branch_name"],
|
|
161
|
+
)
|
|
162
|
+
if branch_response.is_success() and branch_response.data:
|
|
163
|
+
opening_bank_branch_name = str(
|
|
164
|
+
branch_response.data.get("bank_branch_name") or ""
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
logger.warning("bank branch enrichment failed for id %s", opening_value)
|
|
168
|
+
|
|
169
|
+
return BankAccount(
|
|
170
|
+
name=str(bank_account.get("name") or ""),
|
|
171
|
+
account_name=str(bank_account.get("account_name") or ""),
|
|
172
|
+
bank=bank_name,
|
|
173
|
+
bank_account_no=account_no,
|
|
174
|
+
bank_branch=opening_bank_branch_name,
|
|
175
|
+
)
|
|
@@ -69,6 +69,7 @@ class FiuaiSDK(object):
|
|
|
69
69
|
x_fiuai_impersonation="",
|
|
70
70
|
x_fiuai_unique_no=company_unique_no or current_company or "", # 优先使用 company_unique_no
|
|
71
71
|
x_fiuai_trace_id="",
|
|
72
|
+
x_fiuai_roles="[]",
|
|
72
73
|
)
|
|
73
74
|
|
|
74
75
|
# 临时 headers 存储,用于 set_temp_header 方法
|
|
@@ -116,6 +117,7 @@ class FiuaiSDK(object):
|
|
|
116
117
|
"x-fiuai-channel": context_headers.get("x-fiuai-channel", self.headers.x_fiuai_channel),
|
|
117
118
|
"x-fiuai-lang": context_headers.get("x-fiuai-lang", self.headers.x_fiuai_lang),
|
|
118
119
|
"accept-language": context_headers.get("accept-language", self.headers.accept_language),
|
|
120
|
+
"x-fiuai-roles": context_headers.get("x-fiuai-roles", self.headers.x_fiuai_roles),
|
|
119
121
|
})
|
|
120
122
|
else:
|
|
121
123
|
# 如果没有上下文,使用初始化参数或默认的 headers
|
|
@@ -137,6 +139,7 @@ class FiuaiSDK(object):
|
|
|
137
139
|
"x-fiuai-channel": self.headers.x_fiuai_channel,
|
|
138
140
|
"x-fiuai-lang": self.headers.x_fiuai_lang,
|
|
139
141
|
"accept-language": self.headers.accept_language,
|
|
142
|
+
"x-fiuai-roles": self.headers.x_fiuai_roles,
|
|
140
143
|
}
|
|
141
144
|
|
|
142
145
|
# 应用临时 headers(优先级高于上下文,低于 extra_headers)
|
|
@@ -588,4 +591,3 @@ def get_client(username: str = None, auth_tenant_id: str = None, current_company
|
|
|
588
591
|
host=client_config.host,
|
|
589
592
|
trust_env=client_config.trust_env,
|
|
590
593
|
)
|
|
591
|
-
|
|
@@ -105,6 +105,8 @@ class RequestContext:
|
|
|
105
105
|
processed_headers["x-fiuai-client"] = "default"
|
|
106
106
|
if not (processed_headers.get("x-fiuai-model") or "").strip():
|
|
107
107
|
processed_headers["x-fiuai-model"] = "auto"
|
|
108
|
+
if not (processed_headers.get("x-fiuai-roles") or "").strip():
|
|
109
|
+
processed_headers["x-fiuai-roles"] = "[]"
|
|
108
110
|
|
|
109
111
|
return processed_headers
|
|
110
112
|
|
|
@@ -250,6 +252,7 @@ def extract_auth_headers_from_context(
|
|
|
250
252
|
channel: str = "default",
|
|
251
253
|
lang: str = "zh",
|
|
252
254
|
accept_language: str = "zh",
|
|
255
|
+
roles: str = "[]",
|
|
253
256
|
) -> AuthHeader:
|
|
254
257
|
"""
|
|
255
258
|
从当前上下文中提取认证头信息,并合并用户提供的参数
|
|
@@ -287,6 +290,7 @@ def extract_auth_headers_from_context(
|
|
|
287
290
|
x_fiuai_channel="default",
|
|
288
291
|
x_fiuai_lang=lang,
|
|
289
292
|
accept_language=accept_language,
|
|
293
|
+
x_fiuai_roles=roles or "[]",
|
|
290
294
|
)
|
|
291
295
|
|
|
292
296
|
# 从上下文中提取信息,优先使用上下文中的值
|
|
@@ -310,5 +314,5 @@ def extract_auth_headers_from_context(
|
|
|
310
314
|
x_fiuai_channel="default",
|
|
311
315
|
x_fiuai_lang=context_lang or lang,
|
|
312
316
|
accept_language=context_accept_language or accept_language,
|
|
317
|
+
x_fiuai_roles=context_headers.get("x-fiuai-roles") or roles or "[]",
|
|
313
318
|
)
|
|
314
|
-
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
# -- coding: utf-8 --
|
|
2
|
-
# Project: fiuaiclient
|
|
3
|
-
# Created Date: 2025 06 Fr
|
|
4
|
-
# Author: liming
|
|
5
|
-
# Email: lmlala@aliyun.com
|
|
6
|
-
# Copyright (c) 2025 FiuAI
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
from pydantic import BaseModel, Field
|
|
11
|
-
import logging
|
|
12
|
-
from .type import BankAccount
|
|
13
|
-
from .client import FiuaiSDK
|
|
14
|
-
from typing import Literal, Optional
|
|
15
|
-
|
|
16
|
-
logger = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class CompanyBankAccountSearchResult(BaseModel):
|
|
20
|
-
bank_account_id: str = Field(description="银行账号id", default="")
|
|
21
|
-
bank_account_no: str = Field(description="银行账号", default="")
|
|
22
|
-
bank_account_name: str = Field(description="银行账号", default="")
|
|
23
|
-
bank_id: str = Field(description="银行id", default="")
|
|
24
|
-
bank_name: str = Field(description="银行名称", default="")
|
|
25
|
-
opening_bank_branch_id: str = Field(description="开户行id", default="")
|
|
26
|
-
opening_bank_branch_name: str = Field(description="开户行名称", default="")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
exists: bool = Field(description="是否存在", default=False)
|
|
30
|
-
|
|
31
|
-
comment: str = Field(description="备注", default="")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def get_bank_account(client: FiuaiSDK, bank_account_no: str) -> Optional[CompanyBankAccountSearchResult]:
|
|
35
|
-
"""
|
|
36
|
-
根据银行账号获取银行账号信息
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
back_account: 银行账号
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
CompanyBankAccountSearchResult: 银行账号信息
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
if not bank_account_no:
|
|
46
|
-
logger.error(f"bank_account_no is empty, return None")
|
|
47
|
-
return None
|
|
48
|
-
|
|
49
|
-
_bank_account_response = client.internal_get(
|
|
50
|
-
doctype="Bank Account",
|
|
51
|
-
name=bank_account_no,
|
|
52
|
-
fields=["name","bank_account_no", "account_name", "bank", "opening_bank_branch"]
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if not _bank_account_response.is_success():
|
|
57
|
-
return CompanyBankAccountSearchResult(exists=False)
|
|
58
|
-
|
|
59
|
-
_bank_account = _bank_account_response.data
|
|
60
|
-
if not _bank_account:
|
|
61
|
-
return CompanyBankAccountSearchResult(exists=False)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
# 获取银行信息
|
|
65
|
-
_bank_detail_response = client.internal_get(
|
|
66
|
-
doctype="Bank",
|
|
67
|
-
name=_bank_account["bank"],
|
|
68
|
-
fields=["name", "bank_name"]
|
|
69
|
-
)
|
|
70
|
-
if not _bank_detail_response.is_success():
|
|
71
|
-
return CompanyBankAccountSearchResult(exists=False)
|
|
72
|
-
|
|
73
|
-
_bank_detail = _bank_detail_response.data
|
|
74
|
-
if not _bank_detail:
|
|
75
|
-
logger.error(f"bank is not found by id {_bank_account["bank"]}")
|
|
76
|
-
return CompanyBankAccountSearchResult(exists=False)
|
|
77
|
-
|
|
78
|
-
# 获取支行信息
|
|
79
|
-
_bank_branch_response = client.internal_get(
|
|
80
|
-
doctype="Bank Branch",
|
|
81
|
-
name=_bank_account["opening_bank_branch"],
|
|
82
|
-
fields=["name", "bank_branch_name"]
|
|
83
|
-
)
|
|
84
|
-
if not _bank_branch_response.is_success():
|
|
85
|
-
return CompanyBankAccountSearchResult(exists=False)
|
|
86
|
-
|
|
87
|
-
_bank_branch = _bank_branch_response.data
|
|
88
|
-
if not _bank_branch:
|
|
89
|
-
logger.error(f"bank branch is not found by id {_bank_account["opening_bank_branch"]}")
|
|
90
|
-
return CompanyBankAccountSearchResult(exists=False)
|
|
91
|
-
|
|
92
|
-
return CompanyBankAccountSearchResult(
|
|
93
|
-
exists=True,
|
|
94
|
-
bank_account_id=_bank_account["name"],
|
|
95
|
-
bank_account_no=_bank_account["bank_account_no"],
|
|
96
|
-
bank_account_name=_bank_account["account_name"],
|
|
97
|
-
bank_id=_bank_account["name"],
|
|
98
|
-
bank_name=_bank_detail["bank_name"],
|
|
99
|
-
opening_bank_branch_id=_bank_branch["name"],
|
|
100
|
-
opening_bank_branch_name=_bank_branch["bank_branch_name"]
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def get_bank_account_by_account_no(client: FiuaiSDK, account_no: str) -> BankAccount|None:
|
|
105
|
-
"""
|
|
106
|
-
根据银行账号id获取银行账号信息
|
|
107
|
-
"""
|
|
108
|
-
resp_response = client.internal_get_list(
|
|
109
|
-
doctype="Bank Account",
|
|
110
|
-
filters=[["bank_account_no", "=", account_no]],
|
|
111
|
-
fields=[
|
|
112
|
-
"name",
|
|
113
|
-
"account_name",
|
|
114
|
-
"bank",
|
|
115
|
-
"bank_account_no",
|
|
116
|
-
"opening_bank_branch",
|
|
117
|
-
],
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
if not resp_response.is_success():
|
|
121
|
-
logger.warning(f"bank account is not found by account no {account_no}")
|
|
122
|
-
return None
|
|
123
|
-
|
|
124
|
-
resp = resp_response.data
|
|
125
|
-
if not resp:
|
|
126
|
-
logger.warning(f"bank account is not found by account no {account_no}")
|
|
127
|
-
return None
|
|
128
|
-
|
|
129
|
-
resp = resp[0]
|
|
130
|
-
|
|
131
|
-
# 获取支行信息
|
|
132
|
-
if resp["opening_bank_branch"] != "":
|
|
133
|
-
_bank_branch_response = client.internal_get_list(
|
|
134
|
-
doctype="Bank Branch",
|
|
135
|
-
filters=[["bank_branch_name", "=", resp["opening_bank_branch"]]],
|
|
136
|
-
fields=["parent_bank", "bank_branch_name"]
|
|
137
|
-
)
|
|
138
|
-
if not _bank_branch_response.is_success():
|
|
139
|
-
logger.warning(f"bank branch is not found by id {resp["opening_bank_branch"]}")
|
|
140
|
-
return None
|
|
141
|
-
|
|
142
|
-
if not _bank_branch_response.data:
|
|
143
|
-
logger.warning(f"bank branch is not found by id {resp["opening_bank_branch"]}")
|
|
144
|
-
return None
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
_parent_bank_id = _bank_branch_response.data[0]["parent_bank"]
|
|
148
|
-
if not _parent_bank_id:
|
|
149
|
-
logger.warning(f"parent bank is not found by id {resp["opening_bank_branch"]}")
|
|
150
|
-
return None
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
# 获取银行信息
|
|
154
|
-
_bank_detail_response = client.internal_get(
|
|
155
|
-
doctype="Bank",
|
|
156
|
-
name=_parent_bank_id,
|
|
157
|
-
fields=["name", "bank_name"]
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
if not _bank_detail_response.is_success():
|
|
161
|
-
logger.warning(f"bank is not found by id {_parent_bank_id}")
|
|
162
|
-
return None
|
|
163
|
-
|
|
164
|
-
_bank_detail = _bank_detail_response.data
|
|
165
|
-
if not _bank_detail:
|
|
166
|
-
logger.warning(f"bank is not found by id {_parent_bank_id}")
|
|
167
|
-
return None
|
|
168
|
-
|
|
169
|
-
return BankAccount(
|
|
170
|
-
name=resp["name"],
|
|
171
|
-
account_name=resp["account_name"],
|
|
172
|
-
bank=_bank_detail["bank_name"],
|
|
173
|
-
bank_account_no=account_no,
|
|
174
|
-
bank_branch=resp["opening_bank_branch"],
|
|
175
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/__init__.py
RENAMED
|
File without changes
|
{fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/cache_client.py
RENAMED
|
File without changes
|
|
File without changes
|
{fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/decorator.py
RENAMED
|
File without changes
|
{fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/cache/redis_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/scheduler/__init__.py
RENAMED
|
File without changes
|
{fiuai_sdk_python-0.15.0 → fiuai_sdk_python-0.16.1}/src/fiuai_sdk_python/pkg/scheduler/runner.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|