uc-integration 0.1.0__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.
Files changed (23) hide show
  1. uc_integration-0.1.0/PKG-INFO +112 -0
  2. uc_integration-0.1.0/README.md +92 -0
  3. uc_integration-0.1.0/pyproject.toml +33 -0
  4. uc_integration-0.1.0/setup.cfg +4 -0
  5. uc_integration-0.1.0/setup.py +18 -0
  6. uc_integration-0.1.0/uc_integration/__init__.py +64 -0
  7. uc_integration-0.1.0/uc_integration/backend/__init__.py +42 -0
  8. uc_integration-0.1.0/uc_integration/backend/controller/SsoAuthController.py +47 -0
  9. uc_integration-0.1.0/uc_integration/backend/controller/UcDataSyncController.py +62 -0
  10. uc_integration-0.1.0/uc_integration/backend/controller/__init__.py +49 -0
  11. uc_integration-0.1.0/uc_integration/backend/deps.py +27 -0
  12. uc_integration-0.1.0/uc_integration/backend/schema/SsoAuthSchema.py +32 -0
  13. uc_integration-0.1.0/uc_integration/backend/schema/__init__.py +0 -0
  14. uc_integration-0.1.0/uc_integration/backend/service/SsoAuthService.py +301 -0
  15. uc_integration-0.1.0/uc_integration/backend/service/UcDataSyncService.py +260 -0
  16. uc_integration-0.1.0/uc_integration/backend/service/UcStoreScopeService.py +245 -0
  17. uc_integration-0.1.0/uc_integration/backend/service/UserCenterClient.py +92 -0
  18. uc_integration-0.1.0/uc_integration/backend/service/__init__.py +0 -0
  19. uc_integration-0.1.0/uc_integration.egg-info/PKG-INFO +112 -0
  20. uc_integration-0.1.0/uc_integration.egg-info/SOURCES.txt +21 -0
  21. uc_integration-0.1.0/uc_integration.egg-info/dependency_links.txt +1 -0
  22. uc_integration-0.1.0/uc_integration.egg-info/requires.txt +10 -0
  23. uc_integration-0.1.0/uc_integration.egg-info/top_level.txt +1 -0
@@ -0,0 +1,112 @@
1
+ Metadata-Version: 2.4
2
+ Name: uc-integration
3
+ Version: 0.1.0
4
+ Summary: GSS 用户中心 SSO / 主数据同步 Fred 模块(子系统可 pip 安装)
5
+ Author: GSS
6
+ License: Proprietary
7
+ Project-URL: Homepage, https://github.com/renzhong/gss
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: flask>=2.0
11
+ Requires-Dist: flask-smorest>=0.40
12
+ Requires-Dist: flask-jwt-extended>=4.0
13
+ Requires-Dist: flask-babelplus>=2.0
14
+ Requires-Dist: marshmallow>=3.0
15
+ Requires-Dist: sqlalchemy>=1.4
16
+ Requires-Dist: pip==26.1.2
17
+ Provides-Extra: fred
18
+ Requires-Dist: fred-admin; extra == "fred"
19
+ Dynamic: requires-python
20
+
21
+ # GSS UC Integration — 用户中心 SSO / 主数据同步(Fred 子系统共享包)
22
+
23
+ 将各子系统里同构的 `modules/uc_integration` 抽成可安装包,避免复制粘贴分叉。
24
+
25
+ ## 能力
26
+
27
+ | 接口 | 说明 |
28
+ |------|------|
29
+ | `GET /{subsystem}/uc_integration/auth/sso/config` | 登录模式 |
30
+ | `POST /{subsystem}/uc_integration/auth/sso/exchange` | UC code/accessToken → 本地 JWT |
31
+ | `POST /{subsystem}/uc_integration/auth/sso/refresh` | UC refreshToken 续期 |
32
+ | `POST /{subsystem}/uc_integration/internal/uc-sync` | UC 主数据推送(仅本机) |
33
+ | `GET /{subsystem}/uc_integration/internal/uc-sync` | 同步健康检查 |
34
+
35
+ 本地 JWT 的 `sub` 含:
36
+
37
+ - 本地身份:`id` / `username` / `name` / `store_id` / `store_ids` / `role`
38
+ - **`uc`:UC validate/exchange 原样透传**(不做字段白名单)
39
+
40
+ ## 安装
41
+
42
+ ```bash
43
+ # 开发:可编辑安装
44
+ pip install -e /path/to/uc-integration
45
+
46
+ # 或从 git
47
+ pip install "gss-uc-integration @ git+ssh://git@github.com/org/uc-integration.git"
48
+ ```
49
+
50
+ Fred 只从项目 `modules/` 扫描蓝图,安装后必须链接:
51
+
52
+ ```bash
53
+ # 在子系统根目录(含 modules/)
54
+ python /path/to/uc-integration/scripts/link_to_project.py .
55
+ # 或覆盖已有 symlink
56
+ python /path/to/uc-integration/scripts/link_to_project.py . --force
57
+ ```
58
+
59
+ 等价手动操作:
60
+
61
+ ```bash
62
+ ln -sfn "$(python -c 'import uc_integration,pathlib; print(pathlib.Path(uc_integration.__file__).parent)')" \
63
+ modules/uc_integration
64
+ ```
65
+
66
+ ## 宿主配置
67
+
68
+ `common/config/Config.py`(或等价配置):
69
+
70
+ ```python
71
+ LOAD_CUSTOM_MODULES = [..., "uc_integration"]
72
+
73
+ SSO_LOGIN_MODE = 'uc' # 或 'local'
74
+ UC_SUBSYSTEM_CODE = 'ai_check' # 路由前缀:/{code}/uc_integration
75
+ USER_CENTER_BASE_URL = 'http://127.0.0.1:5001'
76
+ USER_CENTER_INTERNAL_SECRET = '...' # 与 UC OPEN_API_INTERNAL_SECRET 一致
77
+
78
+ # 可选
79
+ UC_INCLUDE_STORE_SCOPE = True # JWT 写入 store_ids / role(默认 True)
80
+ UC_STORE_FALLBACK_FIRST = True # 无匹配门店时回退第一个(默认 True)
81
+ ```
82
+
83
+ ## 宿主前置条件
84
+
85
+ - 本地库有 `uc_user` / `uc_store` 等镜像表(可由 sync DDL 自动建)
86
+ - ORM:`model.model` 中存在 `UcUser`(以及门店 scope 需要的 `UcDept` 等)
87
+ - 依赖:`fred_admin`、Flask、SQLAlchemy(由子系统环境提供)
88
+
89
+ ## 目录结构(Fred 兼容)
90
+
91
+ ```
92
+ uc_integration/
93
+ __init__.py # 导出蓝图
94
+ backend/
95
+ __init__.py # Blueprint + url_prefix
96
+ deps.py # 惰性解析 model.model
97
+ controller/
98
+ service/
99
+ schema/
100
+ ```
101
+
102
+ ## 从旧模块迁移
103
+
104
+ 1. `pip install -e ../uc-integration`
105
+ 2. 备份并删除子系统 `modules/uc_integration`
106
+ 3. `python ../uc-integration/scripts/link_to_project.py .`
107
+ 4. 确认 `UC_SUBSYSTEM_CODE` 与门户子系统编码一致
108
+ 5. 验证 `GET /{code}/uc_integration/auth/sso/config`
109
+
110
+ ## 版本
111
+
112
+ 当前 `0.1.0`,以 ai_check(UC 原样透传)+ todo(门店 scope / 动态前缀)合并为基准。
@@ -0,0 +1,92 @@
1
+ # GSS UC Integration — 用户中心 SSO / 主数据同步(Fred 子系统共享包)
2
+
3
+ 将各子系统里同构的 `modules/uc_integration` 抽成可安装包,避免复制粘贴分叉。
4
+
5
+ ## 能力
6
+
7
+ | 接口 | 说明 |
8
+ |------|------|
9
+ | `GET /{subsystem}/uc_integration/auth/sso/config` | 登录模式 |
10
+ | `POST /{subsystem}/uc_integration/auth/sso/exchange` | UC code/accessToken → 本地 JWT |
11
+ | `POST /{subsystem}/uc_integration/auth/sso/refresh` | UC refreshToken 续期 |
12
+ | `POST /{subsystem}/uc_integration/internal/uc-sync` | UC 主数据推送(仅本机) |
13
+ | `GET /{subsystem}/uc_integration/internal/uc-sync` | 同步健康检查 |
14
+
15
+ 本地 JWT 的 `sub` 含:
16
+
17
+ - 本地身份:`id` / `username` / `name` / `store_id` / `store_ids` / `role`
18
+ - **`uc`:UC validate/exchange 原样透传**(不做字段白名单)
19
+
20
+ ## 安装
21
+
22
+ ```bash
23
+ # 开发:可编辑安装
24
+ pip install -e /path/to/uc-integration
25
+
26
+ # 或从 git
27
+ pip install "gss-uc-integration @ git+ssh://git@github.com/org/uc-integration.git"
28
+ ```
29
+
30
+ Fred 只从项目 `modules/` 扫描蓝图,安装后必须链接:
31
+
32
+ ```bash
33
+ # 在子系统根目录(含 modules/)
34
+ python /path/to/uc-integration/scripts/link_to_project.py .
35
+ # 或覆盖已有 symlink
36
+ python /path/to/uc-integration/scripts/link_to_project.py . --force
37
+ ```
38
+
39
+ 等价手动操作:
40
+
41
+ ```bash
42
+ ln -sfn "$(python -c 'import uc_integration,pathlib; print(pathlib.Path(uc_integration.__file__).parent)')" \
43
+ modules/uc_integration
44
+ ```
45
+
46
+ ## 宿主配置
47
+
48
+ `common/config/Config.py`(或等价配置):
49
+
50
+ ```python
51
+ LOAD_CUSTOM_MODULES = [..., "uc_integration"]
52
+
53
+ SSO_LOGIN_MODE = 'uc' # 或 'local'
54
+ UC_SUBSYSTEM_CODE = 'ai_check' # 路由前缀:/{code}/uc_integration
55
+ USER_CENTER_BASE_URL = 'http://127.0.0.1:5001'
56
+ USER_CENTER_INTERNAL_SECRET = '...' # 与 UC OPEN_API_INTERNAL_SECRET 一致
57
+
58
+ # 可选
59
+ UC_INCLUDE_STORE_SCOPE = True # JWT 写入 store_ids / role(默认 True)
60
+ UC_STORE_FALLBACK_FIRST = True # 无匹配门店时回退第一个(默认 True)
61
+ ```
62
+
63
+ ## 宿主前置条件
64
+
65
+ - 本地库有 `uc_user` / `uc_store` 等镜像表(可由 sync DDL 自动建)
66
+ - ORM:`model.model` 中存在 `UcUser`(以及门店 scope 需要的 `UcDept` 等)
67
+ - 依赖:`fred_admin`、Flask、SQLAlchemy(由子系统环境提供)
68
+
69
+ ## 目录结构(Fred 兼容)
70
+
71
+ ```
72
+ uc_integration/
73
+ __init__.py # 导出蓝图
74
+ backend/
75
+ __init__.py # Blueprint + url_prefix
76
+ deps.py # 惰性解析 model.model
77
+ controller/
78
+ service/
79
+ schema/
80
+ ```
81
+
82
+ ## 从旧模块迁移
83
+
84
+ 1. `pip install -e ../uc-integration`
85
+ 2. 备份并删除子系统 `modules/uc_integration`
86
+ 3. `python ../uc-integration/scripts/link_to_project.py .`
87
+ 4. 确认 `UC_SUBSYSTEM_CODE` 与门户子系统编码一致
88
+ 5. 验证 `GET /{code}/uc_integration/auth/sso/config`
89
+
90
+ ## 版本
91
+
92
+ 当前 `0.1.0`,以 ai_check(UC 原样透传)+ todo(门店 scope / 动态前缀)合并为基准。
@@ -0,0 +1,33 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "uc-integration"
7
+ version = "0.1.0"
8
+ description = "GSS 用户中心 SSO / 主数据同步 Fred 模块(子系统可 pip 安装)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "Proprietary" }
12
+ authors = [{ name = "GSS" }]
13
+ dependencies = [
14
+ "flask>=2.0",
15
+ "flask-smorest>=0.40",
16
+ "flask-jwt-extended>=4.0",
17
+ "flask-babelplus>=2.0",
18
+ "marshmallow>=3.0",
19
+ "sqlalchemy>=1.4",
20
+ "pip==26.1.2",
21
+ ]
22
+
23
+ [project.optional-dependencies]
24
+ fred = ["fred-admin"]
25
+
26
+ [project.urls]
27
+ Homepage = "https://github.com/renzhong/gss"
28
+
29
+ [tool.setuptools.packages.find]
30
+ include = ["uc_integration*"]
31
+
32
+ [tool.setuptools.package-data]
33
+ uc_integration = ["py.typed"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,18 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name='uc-integration',
5
+ version='0.1.0',
6
+ description='GSS 用户中心 SSO / 主数据同步 Fred 模块',
7
+ packages=find_packages(include=['uc_integration', 'uc_integration.*']),
8
+ python_requires='>=3.10',
9
+ install_requires=[
10
+ 'flask>=2.0',
11
+ 'flask-smorest>=0.40',
12
+ 'flask-jwt-extended>=4.0',
13
+ 'flask-babelplus>=2.0',
14
+ 'marshmallow>=3.0',
15
+ 'sqlalchemy>=1.4',
16
+ ],
17
+ include_package_data=True,
18
+ )
@@ -0,0 +1,64 @@
1
+ # uc_integration — 用户中心 SSO / 主数据同步(Fred 子系统可安装模块)
2
+ """安装后可直接 import;在 Fred 下通过 modules/ symlink 自动注册蓝图。"""
3
+ from __future__ import annotations
4
+
5
+ import importlib.util
6
+ import sys
7
+ import types
8
+ from pathlib import Path
9
+
10
+ __version__ = '0.1.0'
11
+
12
+
13
+ def _ensure_fred_style_aliases() -> None:
14
+ """将 backend/{service,schema,...} 注册为 uc_integration.*,并预加载 .py 文件。
15
+
16
+ 对齐 Fred `_register_backend_submodules`:仅建空包会导致
17
+ `from uc_integration.schema.X import Y` 在部分加载顺序下失败。
18
+ """
19
+ backend = Path(__file__).resolve().parent / 'backend'
20
+ if not backend.is_dir():
21
+ return
22
+ for subdir in backend.iterdir():
23
+ if not subdir.is_dir() or subdir.name.startswith('_') or subdir.name == 'controller':
24
+ continue
25
+ pkg_name = f'uc_integration.{subdir.name}'
26
+ if pkg_name not in sys.modules:
27
+ mod = types.ModuleType(pkg_name)
28
+ mod.__path__ = [str(subdir)]
29
+ mod.__file__ = str(subdir / '__init__.py')
30
+ mod.__package__ = pkg_name
31
+ sys.modules[pkg_name] = mod
32
+ setattr(sys.modules[__name__], subdir.name, mod)
33
+ else:
34
+ mod = sys.modules[pkg_name]
35
+ if not getattr(mod, '__path__', None):
36
+ mod.__path__ = [str(subdir)]
37
+
38
+ init_file = subdir / '__init__.py'
39
+ if init_file.exists() and not getattr(mod, '__file__', None):
40
+ mod.__file__ = str(init_file)
41
+
42
+ for py_file in subdir.glob('*.py'):
43
+ if py_file.name.startswith('__'):
44
+ continue
45
+ file_mod_name = f'{pkg_name}.{py_file.stem}'
46
+ if file_mod_name in sys.modules:
47
+ continue
48
+ try:
49
+ spec = importlib.util.spec_from_file_location(file_mod_name, py_file)
50
+ if not spec or not spec.loader:
51
+ continue
52
+ file_mod = importlib.util.module_from_spec(spec)
53
+ sys.modules[file_mod_name] = file_mod
54
+ spec.loader.exec_module(file_mod)
55
+ setattr(mod, py_file.stem, file_mod)
56
+ except Exception:
57
+ sys.modules.pop(file_mod_name, None)
58
+
59
+
60
+ _ensure_fred_style_aliases()
61
+
62
+ from .backend import uc_integration, get_admin_role, get_subsystem_code # noqa: E402
63
+
64
+ __all__ = ['uc_integration', 'get_admin_role', 'get_subsystem_code', '__version__']
@@ -0,0 +1,42 @@
1
+ # -*- coding: utf-8
2
+ """蓝图入口:url_prefix 由 UC_SUBSYSTEM_CODE 动态推导。"""
3
+ from flask_smorest import Blueprint
4
+
5
+
6
+ def get_subsystem_code() -> str:
7
+ """子系统编码:优先 Config.UC_SUBSYSTEM_CODE,否则 DEFAULT_MODULE。"""
8
+ try:
9
+ from flask import current_app, has_app_context
10
+ if has_app_context():
11
+ code = (current_app.config.get('UC_SUBSYSTEM_CODE') or '').strip()
12
+ if code:
13
+ return code
14
+ return (current_app.config.get('DEFAULT_MODULE') or 'app').strip()
15
+ except Exception:
16
+ pass
17
+ try:
18
+ from common.config.Config import Config
19
+ code = (getattr(Config, 'UC_SUBSYSTEM_CODE', None) or '').strip()
20
+ if code:
21
+ return code
22
+ return (getattr(Config, 'DEFAULT_MODULE', None) or 'app').strip()
23
+ except Exception:
24
+ return 'app'
25
+
26
+
27
+ def get_admin_role() -> str:
28
+ """从 DEFAULT_MODULE 推导管理员角色:{subsystem}_admin。"""
29
+ from flask import current_app
30
+ subsystem = (current_app.config.get('DEFAULT_MODULE') or get_subsystem_code()).strip()
31
+ return f'{subsystem}_admin'
32
+
33
+
34
+ uc_integration = Blueprint(
35
+ 'uc_integration',
36
+ __name__,
37
+ url_prefix=f'/{get_subsystem_code()}/uc_integration',
38
+ )
39
+
40
+ # SSO:/{UC_SUBSYSTEM_CODE}/uc_integration/auth/sso/*
41
+ # 同步:/{UC_SUBSYSTEM_CODE}/uc_integration/internal/uc-sync
42
+ # controller 由 Fred 扫描 modules/uc_integration/backend/controller/
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8
2
+ """SSO 换票接口:UC code / accessToken → 本地 JWT。"""
3
+ from flask import abort, current_app
4
+ from flask.views import MethodView
5
+ from flask_babelplus import gettext
6
+
7
+ from uc_integration import uc_integration
8
+ from uc_integration.schema.SsoAuthSchema import (
9
+ SsoExchangeBodySchema,
10
+ SsoRefreshBodySchema,
11
+ SsoExchangeResponseSchema,
12
+ )
13
+ from uc_integration.service.SsoAuthService import SsoAuthService
14
+
15
+
16
+ @uc_integration.route('/auth/sso/config')
17
+ class SsoConfigController(MethodView):
18
+ def get(self):
19
+ """子系统前端读取登录方式(无需登录)。"""
20
+ mode = (current_app.config.get('SSO_LOGIN_MODE') or 'local').strip().lower()
21
+ if mode not in ('uc', 'local'):
22
+ mode = 'local'
23
+ base_url = (current_app.config.get('USER_CENTER_BASE_URL') or '').strip().rstrip('/')
24
+ if not base_url:
25
+ abort(500, description=gettext('用户中心地址未配置(USER_CENTER_BASE_URL)'))
26
+ return {
27
+ 'loginMode': mode,
28
+ 'userCenterBaseUrl': base_url,
29
+ }
30
+
31
+
32
+ @uc_integration.route('/auth/sso/exchange')
33
+ class SsoExchangeController(MethodView):
34
+ @uc_integration.arguments(SsoExchangeBodySchema)
35
+ @uc_integration.response(200, SsoExchangeResponseSchema)
36
+ def post(self, args):
37
+ """用 UC 授权码或 accessToken 兑换本子系统 JWT(服务端调 UC)。"""
38
+ return SsoAuthService().sso_exchange(args)
39
+
40
+
41
+ @uc_integration.route('/auth/sso/refresh')
42
+ class SsoRefreshController(MethodView):
43
+ @uc_integration.arguments(SsoRefreshBodySchema)
44
+ @uc_integration.response(200, SsoExchangeResponseSchema)
45
+ def post(self, args):
46
+ """UC refreshToken 续期并重新签发本地 JWT。"""
47
+ return SsoAuthService().sso_refresh(args)
@@ -0,0 +1,62 @@
1
+ # coding: utf-8
2
+ """
3
+ UC 主数据同步接口
4
+
5
+ 接收 user-center 推送的用户、组织架构、门店等主数据,保存到本地数据库。
6
+ - POST /{subsystem}/uc_integration/internal/uc-sync 接收推送
7
+ - GET /{subsystem}/uc_integration/internal/uc-sync 健康检查
8
+
9
+ 安全策略:
10
+ - 仅允许本机(127.0.0.1 / ::1)访问
11
+ - 校验请求头 X-UC-Internal-Secret 与本地 USER_CENTER_INTERNAL_SECRET 一致
12
+ """
13
+ from flask import abort, current_app, request
14
+ from flask.views import MethodView
15
+ from flask_babelplus import gettext
16
+ from functools import wraps
17
+
18
+ from uc_integration import uc_integration
19
+ from uc_integration.service.UcDataSyncService import UcDataSyncService
20
+
21
+ _LOCALHOST_ADDRS = frozenset({'127.0.0.1', '::1'})
22
+
23
+
24
+ def _verify_local_internal_access() -> None:
25
+ """校验请求来自本机且携带正确的内部密钥。"""
26
+ remote = (request.remote_addr or '').strip()
27
+ if remote not in _LOCALHOST_ADDRS:
28
+ abort(403, description=gettext('仅允许本机访问'))
29
+
30
+ expected = (current_app.config.get('USER_CENTER_INTERNAL_SECRET') or '').strip()
31
+ if not expected:
32
+ abort(501, description=gettext('内部同步密钥未配置(USER_CENTER_INTERNAL_SECRET)'))
33
+
34
+ provided = (request.headers.get('X-UC-Internal-Secret') or '').strip()
35
+ if provided != expected:
36
+ abort(403, description=gettext('无权访问同步接口'))
37
+
38
+
39
+ def uc_sync_local_only(f):
40
+ """装饰器:限制仅本机 + 正确密钥可访问。"""
41
+ @wraps(f)
42
+ def wrapper(*args, **kwargs):
43
+ _verify_local_internal_access()
44
+ return f(*args, **kwargs)
45
+ return wrapper
46
+
47
+
48
+ @uc_integration.route('/internal/uc-sync')
49
+ class UcDataSyncController(MethodView):
50
+
51
+ @uc_sync_local_only
52
+ @uc_integration.response(200)
53
+ def post(self):
54
+ """接收 UC 推送的用户、组织、门店主数据(仅本机)。"""
55
+ payload = request.get_json(silent=True) or {}
56
+ return UcDataSyncService().sync(payload)
57
+
58
+ @uc_sync_local_only
59
+ @uc_integration.response(200)
60
+ def get(self):
61
+ """同步接口健康检查(仅本机),返回各镜像表有效记录数。"""
62
+ return UcDataSyncService.health_check()
@@ -0,0 +1,49 @@
1
+ # coding: utf-8
2
+ """业务 API 认证装饰器:仅认本地 JWT。"""
3
+ from functools import wraps
4
+ import json
5
+
6
+ from flask import abort, g, request
7
+ from flask_babelplus import gettext
8
+ from flask_jwt_extended import decode_token
9
+
10
+
11
+ def _extract_bearer_token() -> str:
12
+ auth = request.headers.get('Authorization') or ''
13
+ if auth.lower().startswith('bearer '):
14
+ return auth[7:].strip()
15
+ return ''
16
+
17
+
18
+ def _try_admin_jwt(token: str) -> bool:
19
+ from uc_integration.backend import get_admin_role
20
+ try:
21
+ decoded = decode_token(token)
22
+ except Exception:
23
+ return False
24
+ roles = decoded.get('role')
25
+ admin_role = get_admin_role()
26
+ if isinstance(roles, list):
27
+ if 'uc_integration' not in roles and admin_role not in roles:
28
+ return False
29
+ elif roles not in ('uc_integration', admin_role):
30
+ return False
31
+ identity = json.loads(decoded['sub'])
32
+ g.uc_user_id = identity.get('username') or str(identity.get('id'))
33
+ g.uc_identity = identity
34
+ return True
35
+
36
+
37
+ def public_api(f):
38
+ """业务 API 仅认本地 JWT(uc_integration 或子系统管理员角色)。"""
39
+
40
+ @wraps(f)
41
+ def wrapper(*args, **kwargs):
42
+ token = _extract_bearer_token()
43
+ if not token:
44
+ abort(401, description=gettext('请先登录'))
45
+ if not _try_admin_jwt(token):
46
+ abort(401, description=gettext('请先登录'))
47
+ return f(*args, **kwargs)
48
+
49
+ return wrapper
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8
2
+ """宿主依赖惰性解析:避免包安装时强绑 model.model。"""
3
+ from __future__ import annotations
4
+
5
+
6
+ def get_db():
7
+ """返回宿主 Flask-SQLAlchemy db 实例。"""
8
+ from model.model import db
9
+ return db
10
+
11
+
12
+ def get_uc_user_model():
13
+ """返回宿主 UcUser ORM 类。"""
14
+ from model.model import UcUser
15
+ return UcUser
16
+
17
+
18
+ def get_uc_models():
19
+ """返回门店 scope 所需的 UC 镜像表模型。"""
20
+ from model.model import UcDept, UcDeptUserRelation, UcStore, UcUser, db
21
+ return {
22
+ 'UcDept': UcDept,
23
+ 'UcDeptUserRelation': UcDeptUserRelation,
24
+ 'UcStore': UcStore,
25
+ 'UcUser': UcUser,
26
+ 'db': db,
27
+ }
@@ -0,0 +1,32 @@
1
+ # -*- coding: utf-8
2
+ """SSO 换票接口 Schema 定义。"""
3
+ from marshmallow import Schema, fields
4
+
5
+
6
+ class SsoExchangeBodySchema(Schema):
7
+ code = fields.Str(metadata={'description': 'UC 跨域回跳一次性授权码'})
8
+ accessToken = fields.Str(metadata={'description': 'UC accessToken(同域兜底)'})
9
+ refreshToken = fields.Str(metadata={'description': 'UC refreshToken(可选)'})
10
+
11
+
12
+ class SsoRefreshBodySchema(Schema):
13
+ refreshToken = fields.Str(required=True, metadata={'description': 'UC refreshToken'})
14
+
15
+
16
+ class SsoExchangeResponseSchema(Schema):
17
+ id = fields.Int()
18
+ username = fields.Str()
19
+ name = fields.Str()
20
+ role = fields.Str(metadata={'description': '业务角色 manager/employee'})
21
+ access_token = fields.Str()
22
+ refresh_token = fields.Str()
23
+ uc_access_token = fields.Str()
24
+ uc_refresh_token = fields.Str()
25
+ store_id = fields.Int(metadata={'description': '当前门店 ID'})
26
+ storeId = fields.Int(metadata={'description': '当前门店 ID(驼峰)'})
27
+ store_ids = fields.List(fields.Int(), metadata={'description': '可访问门店 ID 列表'})
28
+ storeIds = fields.List(fields.Int(), metadata={'description': '可访问门店 ID 列表(驼峰)'})
29
+ store_name = fields.Str(metadata={'description': '门店名称(来自 uc_store)'})
30
+ uc = fields.Dict(
31
+ metadata={'description': 'UC 换票/校验原样返回(完整透传,不做字段裁剪)'},
32
+ )