sycommon-python-lib 0.2.1b8__tar.gz → 0.2.1b9__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.
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/PKG-INFO +1 -1
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/pyproject.toml +1 -1
- sycommon_python_lib-0.2.1b9/src/sycommon/auth/__init__.py +46 -0
- sycommon_python_lib-0.2.1b9/src/sycommon/auth/wecom_ldap_service.py +490 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/database/token_usage_db_service.py +5 -5
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/token_usage_mysql_service.py +83 -114
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon_python_lib.egg-info/PKG-INFO +1 -1
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon_python_lib.egg-info/SOURCES.txt +1 -0
- sycommon_python_lib-0.2.1b8/src/sycommon/auth/__init__.py +0 -36
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/README.md +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/setup.cfg +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/cli.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/core/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/core/console.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/core/models.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/core/project.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/core/utils.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/README.md.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/agent/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/agent/main_agent.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/agent/nodes/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/agent/nodes/example_node.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/agent/states/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/agent/states/agent_state.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/api/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/api/query.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/api/sse/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/api/sse/agent.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/app.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/client/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/db/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/db/model/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/db/model/entity.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/db/service.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/model/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/model/parse.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/tools/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/agent/tools/mq.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/.env.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/.gitignore.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/Dockerfile.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/README.md.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/app.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/app.yaml.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/client/FileServiceClient.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/client/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/model/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/model/file_info.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/base/requirements.txt.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/README.md.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/api/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/api/echo.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/api/sse/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/api/sse/echo.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/app.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/client/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/model/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/tools/__init__.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/command/templates/web/tools/mq.py.tpl +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/__main__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/agents/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/agents/factory.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/agents/prompts.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/browser.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/capabilities/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/capabilities/console.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/capabilities/dom.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/capabilities/network.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/capabilities/page_errors.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/capabilities/performance.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/capabilities/screenshot.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/client.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/protocol.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cdp/repl.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/cli.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/cdp_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/chat_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/create_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/evaluate_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/init_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/memory_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/resume_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/rollback_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/commands/run_cmd.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/core/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/core/backend.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/core/config.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/core/display.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/core/git_integration.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/core/llm.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/core/state.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/evaluate/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/evaluate/api_evaluator.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/memory/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/memory/compressor.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/memory/full.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/memory/hot.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/memory/manager.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/memory/warm.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/mode/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/mode/create.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/mode/optimize.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/models/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/models/config_models.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/convergence.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/diagnosis.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/engine.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/environment.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/experience.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/harness_prompts.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/history.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/pre_validation.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/reward.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/strategy_bandit.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/strategy_generator.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/rl/strategy_prompts.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycli/skills/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/agent_manager.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/chat_events.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/deep_agent.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/multi_agent_team.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/sandbox/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/sandbox/file_ops.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/sandbox/http_sandbox_backend.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/sandbox/sandbox_pool.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/sandbox/sandbox_recovery.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/agent/sandbox/session.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/auth/ldap_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/Config.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/DatabaseConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/ElasticsearchConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/EmbeddingConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/LLMConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/LangfuseConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/MQConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/RedisConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/RerankerConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/SentryConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/XxlJobConfig.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/config/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/database/async_base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/database/async_database_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/database/base_db_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/database/database_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/database/elasticsearch_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/database/redis_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/health/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/health/health_check.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/health/metrics.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/health/ping.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/heartbeat_process/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/heartbeat_process/heartbeat_config.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/heartbeat_process/heartbeat_process_manager.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/heartbeat_process/heartbeat_process_worker.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/embedding.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/get_llm.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/llm_logger.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/llm_tokens.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/llm_with_token_tracking.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/native_with_fallback_runnable.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/output_fixing_runnable.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/struct_token.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/sy_langfuse.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/token_usage_es_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/llm/usage_token.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/logging/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/logging/async_sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/logging/kafka_log.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/logging/logger_levels.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/logging/logger_wrapper.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/logging/process_logger.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/logging/sql_logger.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/background_execution.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/context.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/cors.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/docs.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/exception.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/middleware.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/monitor_memory.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/mq.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/sandbox.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/timeout.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/token_tracking.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/middleware/traceid.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/base_http.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/log.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/mqlistener_config.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/mqmsg_model.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/mqsend_config.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/sandbox.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/sso_user.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/token_usage.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/token_usage_mysql.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/models/xxljob_handler_config.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/notice/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/notice/uvicorn_monitor.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/rabbitmq/process_pool_consumer.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/rabbitmq/rabbitmq_client.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/rabbitmq/rabbitmq_pool.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/rabbitmq/rabbitmq_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/rabbitmq/rabbitmq_service_client_manager.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/rabbitmq/rabbitmq_service_connection_monitor.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/rabbitmq/rabbitmq_service_consumer_manager.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/rabbitmq/rabbitmq_service_core.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/rabbitmq/rabbitmq_service_producer_manager.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/sentry/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/sentry/sy_sentry.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/services.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/sse/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/sse/event.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/sse/sse.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/example.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/example2.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/feign.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/feign_client.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/nacos_client_base.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/nacos_config_manager.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/nacos_heartbeat_manager.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/nacos_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/nacos_service_discovery.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/nacos_service_registration.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/synacos/param.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tests/deep_agent_server.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tests/test_deep_agent.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tests/test_email.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tests/test_mq.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tools/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tools/async_utils.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tools/docs.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tools/env.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tools/merge_headers.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tools/snowflake.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tools/syemail.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/tools/timing.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/xxljob/__init__.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon/xxljob/xxljob_service.py +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon_python_lib.egg-info/dependency_links.txt +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon_python_lib.egg-info/entry_points.txt +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon_python_lib.egg-info/requires.txt +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/src/sycommon_python_lib.egg-info/top_level.txt +0 -0
- {sycommon_python_lib-0.2.1b8 → sycommon_python_lib-0.2.1b9}/tests/test_sycli.py +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
认证模块
|
|
3
|
+
|
|
4
|
+
提供 LDAP 域账号登录认证、企微-LDAP 用户关联功能。
|
|
5
|
+
|
|
6
|
+
LDAP 认证示例::
|
|
7
|
+
|
|
8
|
+
from sycommon.auth import LDAPAuthService, LDAPConfig
|
|
9
|
+
|
|
10
|
+
config = LDAPConfig.from_config()
|
|
11
|
+
auth_service = LDAPAuthService(config)
|
|
12
|
+
result = await auth_service.authenticate("username", "password")
|
|
13
|
+
|
|
14
|
+
企微-LDAP 关联示例::
|
|
15
|
+
|
|
16
|
+
from sycommon.auth import WeComLDAPService
|
|
17
|
+
|
|
18
|
+
service = WeComLDAPService()
|
|
19
|
+
user = await service.get_user_info("0633")
|
|
20
|
+
if user.matched and user.ldap_user:
|
|
21
|
+
print(user.ldap_user.email)
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from sycommon.auth.ldap_service import (
|
|
25
|
+
LDAPConfig,
|
|
26
|
+
LDAPAuthService,
|
|
27
|
+
LDAPUser,
|
|
28
|
+
LDAPAuthResult,
|
|
29
|
+
)
|
|
30
|
+
from sycommon.auth.wecom_ldap_service import (
|
|
31
|
+
WeComConfig,
|
|
32
|
+
WeComLDAPService,
|
|
33
|
+
WeComLDAPUser,
|
|
34
|
+
LDAPFullUser,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"LDAPConfig",
|
|
39
|
+
"LDAPAuthService",
|
|
40
|
+
"LDAPUser",
|
|
41
|
+
"LDAPAuthResult",
|
|
42
|
+
"WeComConfig",
|
|
43
|
+
"WeComLDAPService",
|
|
44
|
+
"WeComLDAPUser",
|
|
45
|
+
"LDAPFullUser",
|
|
46
|
+
]
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
"""
|
|
2
|
+
企微-LDAP 用户关联服务
|
|
3
|
+
|
|
4
|
+
通过企微 userid 关联 LDAP 域账号,获取邮箱、部门、职位等完整信息。
|
|
5
|
+
|
|
6
|
+
关联策略:中文名优先 + 英文名兜底,匹配率约 95.8%。
|
|
7
|
+
|
|
8
|
+
使用示例::
|
|
9
|
+
|
|
10
|
+
from sycommon.auth import WeComLDAPService
|
|
11
|
+
|
|
12
|
+
service = WeComLDAPService()
|
|
13
|
+
user = await service.get_user_info("0633")
|
|
14
|
+
if user.matched and user.ldap_user:
|
|
15
|
+
print(user.ldap_user.email)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import logging
|
|
20
|
+
import time
|
|
21
|
+
from collections import defaultdict
|
|
22
|
+
from typing import Dict, List, Optional
|
|
23
|
+
|
|
24
|
+
import aiohttp
|
|
25
|
+
from pydantic import BaseModel
|
|
26
|
+
|
|
27
|
+
from sycommon.config.Config import SingletonMeta
|
|
28
|
+
from sycommon.logging.kafka_log import SYLogger
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ─── 模型定义 ───
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class WeComConfig(BaseModel):
|
|
35
|
+
"""企微通讯录同步配置"""
|
|
36
|
+
|
|
37
|
+
corpid: str
|
|
38
|
+
corpsecret: str
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_config(cls) -> "WeComConfig":
|
|
42
|
+
"""从 Config 加载企微配置
|
|
43
|
+
|
|
44
|
+
配置路径: config['llm']['WeCom']
|
|
45
|
+
"""
|
|
46
|
+
from sycommon.config.Config import Config
|
|
47
|
+
|
|
48
|
+
wecom_config = Config().config.get("llm", {}).get("WeCom", {})
|
|
49
|
+
if not wecom_config:
|
|
50
|
+
raise ValueError("企微配置不存在,请在 llm.WeCom 下配置 corpid 和 corpsecret")
|
|
51
|
+
|
|
52
|
+
corpid = wecom_config.get("corpid", "")
|
|
53
|
+
corpsecret = wecom_config.get("corpsecret", "")
|
|
54
|
+
if not corpid or not corpsecret:
|
|
55
|
+
raise ValueError("企微配置不完整,需要 corpid 和 corpsecret")
|
|
56
|
+
|
|
57
|
+
return cls(corpid=corpid, corpsecret=corpsecret)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class LDAPFullUser(BaseModel):
|
|
61
|
+
"""LDAP 域账号完整用户信息"""
|
|
62
|
+
|
|
63
|
+
username: str # sAMAccountName 域账号
|
|
64
|
+
display_name: Optional[str] = None # 中文名
|
|
65
|
+
email: Optional[str] = None # 邮箱
|
|
66
|
+
department: Optional[str] = None # 部门
|
|
67
|
+
title: Optional[str] = None # 职位
|
|
68
|
+
phone: Optional[str] = None # 电话
|
|
69
|
+
dn: Optional[str] = None # DN 路径
|
|
70
|
+
ou_chain: List[str] = [] # OU 层级路径
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class WeComLDAPUser(BaseModel):
|
|
74
|
+
"""企微-LDAP 关联用户(最终返回结果)"""
|
|
75
|
+
|
|
76
|
+
wecom_userid: str # 企微 userid
|
|
77
|
+
wecom_name: str # 企微中文名
|
|
78
|
+
wecom_english_name: Optional[str] = None # 企微英文名
|
|
79
|
+
wecom_departments: List[str] = [] # 企微部门列表
|
|
80
|
+
wecom_position: Optional[str] = None # 企微职位
|
|
81
|
+
matched: bool = False # 是否匹配到 LDAP
|
|
82
|
+
match_method: Optional[str] = None # 匹配方式: chinese_name / english_name
|
|
83
|
+
ldap_user: Optional[LDAPFullUser] = None # LDAP 完整信息
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ─── 内部缓存结构 ───
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class _LDAPIndex:
|
|
90
|
+
"""LDAP 用户索引(按 cn 和 sAMAccountName 前缀建索引)"""
|
|
91
|
+
|
|
92
|
+
def __init__(self):
|
|
93
|
+
# cn(中文名) -> LDAPFullUser
|
|
94
|
+
self.by_cn: Dict[str, LDAPFullUser] = {}
|
|
95
|
+
# sAMAccountName 前缀(小写) -> [cn]
|
|
96
|
+
self.by_sam_prefix: Dict[str, List[str]] = defaultdict(list)
|
|
97
|
+
|
|
98
|
+
def build(self, users: List[dict]):
|
|
99
|
+
"""从 LDAP 查询结果构建索引"""
|
|
100
|
+
self.by_cn.clear()
|
|
101
|
+
self.by_sam_prefix.clear()
|
|
102
|
+
|
|
103
|
+
for u in users:
|
|
104
|
+
cn = u.get("cn", "").strip()
|
|
105
|
+
sam = u.get("sAMAccountName", "").strip()
|
|
106
|
+
if not cn or not sam:
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
dn = u.get("distinguishedName", "")
|
|
110
|
+
dn_parts = dn.split(",")
|
|
111
|
+
ou_chain = [
|
|
112
|
+
p.split("=", 1)[1] for p in dn_parts if p.startswith("OU=")
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
ldap_user = LDAPFullUser(
|
|
116
|
+
username=sam,
|
|
117
|
+
display_name=cn,
|
|
118
|
+
email=u.get("mail", "") or None,
|
|
119
|
+
department=u.get("department", "") or None,
|
|
120
|
+
title=u.get("title", "") or None,
|
|
121
|
+
phone=u.get("telephoneNumber", "") or None,
|
|
122
|
+
dn=dn or None,
|
|
123
|
+
ou_chain=ou_chain,
|
|
124
|
+
)
|
|
125
|
+
self.by_cn[cn] = ldap_user
|
|
126
|
+
|
|
127
|
+
# sAMAccountName 如 "sussie.xia" -> 前缀 "sussie"
|
|
128
|
+
prefix = sam.split(".")[0].lower()
|
|
129
|
+
self.by_sam_prefix[prefix].append(cn)
|
|
130
|
+
|
|
131
|
+
def lookup(self, chinese_name: str, english_name: str = "") -> tuple:
|
|
132
|
+
"""查找匹配的 LDAP 用户
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
chinese_name: 中文名
|
|
136
|
+
english_name: 英文名(来自企微 extattr)
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
(LDAPFullUser | None, match_method | None)
|
|
140
|
+
"""
|
|
141
|
+
# 第一优先级:中文名直接匹配
|
|
142
|
+
if chinese_name in self.by_cn:
|
|
143
|
+
return self.by_cn[chinese_name], "chinese_name"
|
|
144
|
+
|
|
145
|
+
# 兜底:英文名前缀匹配
|
|
146
|
+
if english_name:
|
|
147
|
+
prefix = english_name.lower().split()[0]
|
|
148
|
+
candidates = self.by_sam_prefix.get(prefix, [])
|
|
149
|
+
if len(candidates) == 1:
|
|
150
|
+
# 唯一匹配,直接返回
|
|
151
|
+
return self.by_cn[candidates[0]], "english_name"
|
|
152
|
+
elif len(candidates) > 1:
|
|
153
|
+
# 重名,无法确定,不匹配
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
return None, None
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# ─── 服务类 ───
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class WeComLDAPService(metaclass=SingletonMeta):
|
|
163
|
+
"""企微-LDAP 用户关联服务
|
|
164
|
+
|
|
165
|
+
通过企微 userid 查询关联的 LDAP 域账号完整信息。
|
|
166
|
+
内置 access_token 缓存和 LDAP 用户索引缓存。
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
def __init__(self):
|
|
170
|
+
self._wecom_config: Optional[WeComConfig] = None
|
|
171
|
+
self._ldap_config = None # LDAPConfig
|
|
172
|
+
self._access_token: Optional[str] = None
|
|
173
|
+
self._token_expires_at: float = 0
|
|
174
|
+
self._ldap_index: Optional[_LDAPIndex] = None
|
|
175
|
+
self._ldap_index_expires_at: float = 0
|
|
176
|
+
self._ldap_index_ttl: int = 3600 # LDAP 索引缓存 1 小时
|
|
177
|
+
# 企微部门 ID -> 名称 缓存
|
|
178
|
+
self._dept_map: Dict[int, str] = {}
|
|
179
|
+
|
|
180
|
+
def _ensure_configs(self):
|
|
181
|
+
"""延迟加载配置"""
|
|
182
|
+
if self._wecom_config is None:
|
|
183
|
+
self._wecom_config = WeComConfig.from_config()
|
|
184
|
+
if self._ldap_config is None:
|
|
185
|
+
from sycommon.auth.ldap_service import LDAPConfig
|
|
186
|
+
|
|
187
|
+
self._ldap_config = LDAPConfig.from_config()
|
|
188
|
+
|
|
189
|
+
# ─── 企微 API ───
|
|
190
|
+
|
|
191
|
+
async def _get_access_token(self) -> str:
|
|
192
|
+
"""获取企微 access_token(带缓存)"""
|
|
193
|
+
if self._access_token and time.time() < self._token_expires_at:
|
|
194
|
+
return self._access_token
|
|
195
|
+
|
|
196
|
+
self._ensure_configs()
|
|
197
|
+
url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
|
|
198
|
+
params = {
|
|
199
|
+
"corpid": self._wecom_config.corpid,
|
|
200
|
+
"corpsecret": self._wecom_config.corpsecret,
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async with aiohttp.ClientSession() as session:
|
|
204
|
+
async with session.get(url, params=params) as resp:
|
|
205
|
+
data = await resp.json()
|
|
206
|
+
|
|
207
|
+
if data.get("errcode") != 0:
|
|
208
|
+
raise RuntimeError(f"获取企微 access_token 失败: {data}")
|
|
209
|
+
|
|
210
|
+
self._access_token = data["access_token"]
|
|
211
|
+
# 提前 300 秒过期,避免边界情况
|
|
212
|
+
self._token_expires_at = time.time() + data.get("expires_in", 7200) - 300
|
|
213
|
+
logging.info("[WeComLDAP] access_token 已刷新")
|
|
214
|
+
return self._access_token
|
|
215
|
+
|
|
216
|
+
async def _wecom_get(self, path: str, params: dict = None) -> dict:
|
|
217
|
+
"""封装企微 GET 请求"""
|
|
218
|
+
token = await self._get_access_token()
|
|
219
|
+
url = f"https://qyapi.weixin.qq.com{path}"
|
|
220
|
+
if params is None:
|
|
221
|
+
params = {}
|
|
222
|
+
params["access_token"] = token
|
|
223
|
+
|
|
224
|
+
async with aiohttp.ClientSession() as session:
|
|
225
|
+
async with session.get(url, params=params) as resp:
|
|
226
|
+
return await resp.json()
|
|
227
|
+
|
|
228
|
+
async def _get_wecom_user(self, userid: str) -> Optional[dict]:
|
|
229
|
+
"""获取单个企微用户详情"""
|
|
230
|
+
data = await self._wecom_get(
|
|
231
|
+
"/cgi-bin/user/get", {"userid": userid}
|
|
232
|
+
)
|
|
233
|
+
if data.get("errcode") != 0:
|
|
234
|
+
SYLogger.warning(f"[WeComLDAP] 获取企微用户 {userid} 失败: {data}")
|
|
235
|
+
return None
|
|
236
|
+
return data
|
|
237
|
+
|
|
238
|
+
async def _get_all_wecom_users(self) -> List[dict]:
|
|
239
|
+
"""获取全量企微用户"""
|
|
240
|
+
data = await self._wecom_get(
|
|
241
|
+
"/cgi-bin/user/list",
|
|
242
|
+
{"department_id": 1, "fetch_child": 1},
|
|
243
|
+
)
|
|
244
|
+
if data.get("errcode") != 0:
|
|
245
|
+
SYLogger.error(f"[WeComLDAP] 获取企微用户列表失败: {data}")
|
|
246
|
+
return []
|
|
247
|
+
return data.get("userlist", [])
|
|
248
|
+
|
|
249
|
+
async def _get_dept_map(self) -> Dict[int, str]:
|
|
250
|
+
"""获取部门 ID -> 名称映射"""
|
|
251
|
+
if self._dept_map:
|
|
252
|
+
return self._dept_map
|
|
253
|
+
|
|
254
|
+
data = await self._wecom_get("/cgi-bin/department/list")
|
|
255
|
+
if data.get("errcode") != 0:
|
|
256
|
+
return {}
|
|
257
|
+
|
|
258
|
+
self._dept_map = {d["id"]: d["name"] for d in data.get("department", [])}
|
|
259
|
+
return self._dept_map
|
|
260
|
+
|
|
261
|
+
# ─── LDAP 查询 ───
|
|
262
|
+
|
|
263
|
+
def _load_ldap_index_sync(self) -> _LDAPIndex:
|
|
264
|
+
"""同步加载 LDAP 用户索引"""
|
|
265
|
+
import ldap3
|
|
266
|
+
|
|
267
|
+
self._ensure_configs()
|
|
268
|
+
config = self._ldap_config
|
|
269
|
+
|
|
270
|
+
bind_user = f"{config.username}@{config.default_dc}"
|
|
271
|
+
raw_users = []
|
|
272
|
+
|
|
273
|
+
for url in config.urls:
|
|
274
|
+
try:
|
|
275
|
+
server = ldap3.Server(
|
|
276
|
+
url.rstrip("/"),
|
|
277
|
+
get_info=ldap3.ALL,
|
|
278
|
+
connect_timeout=5,
|
|
279
|
+
)
|
|
280
|
+
conn = ldap3.Connection(
|
|
281
|
+
server,
|
|
282
|
+
user=bind_user,
|
|
283
|
+
password=config.password,
|
|
284
|
+
auto_bind=True,
|
|
285
|
+
receive_timeout=30,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
# 搜索盛业(大陆)和盛业(香港)两个 OU
|
|
289
|
+
for ou_base in [
|
|
290
|
+
f"OU=盛业(大陆),{config.base}",
|
|
291
|
+
f"OU=盛业(香港),{config.base}",
|
|
292
|
+
]:
|
|
293
|
+
conn.search(
|
|
294
|
+
search_base=ou_base,
|
|
295
|
+
search_filter="(objectClass=user)",
|
|
296
|
+
search_scope=ldap3.SUBTREE,
|
|
297
|
+
attributes=[
|
|
298
|
+
"cn",
|
|
299
|
+
"sAMAccountName",
|
|
300
|
+
"mail",
|
|
301
|
+
"department",
|
|
302
|
+
"title",
|
|
303
|
+
"telephoneNumber",
|
|
304
|
+
"distinguishedName",
|
|
305
|
+
],
|
|
306
|
+
size_limit=0,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
for entry in conn.entries:
|
|
310
|
+
cn = str(entry.cn).strip() if entry.cn else ""
|
|
311
|
+
sam = (
|
|
312
|
+
str(entry.sAMAccountName).strip()
|
|
313
|
+
if entry.sAMAccountName
|
|
314
|
+
else ""
|
|
315
|
+
)
|
|
316
|
+
if not cn or not sam or sam.startswith("$"):
|
|
317
|
+
continue
|
|
318
|
+
|
|
319
|
+
raw_users.append(
|
|
320
|
+
{
|
|
321
|
+
"cn": cn,
|
|
322
|
+
"sAMAccountName": sam,
|
|
323
|
+
"mail": str(entry.mail).strip()
|
|
324
|
+
if entry.mail
|
|
325
|
+
else "",
|
|
326
|
+
"department": str(entry.department).strip()
|
|
327
|
+
if entry.department
|
|
328
|
+
else "",
|
|
329
|
+
"title": str(entry.title).strip()
|
|
330
|
+
if entry.title
|
|
331
|
+
else "",
|
|
332
|
+
"telephoneNumber": str(
|
|
333
|
+
entry.telephoneNumber
|
|
334
|
+
).strip()
|
|
335
|
+
if entry.telephoneNumber
|
|
336
|
+
else "",
|
|
337
|
+
"distinguishedName": str(
|
|
338
|
+
entry.distinguishedName
|
|
339
|
+
).strip()
|
|
340
|
+
if entry.distinguishedName
|
|
341
|
+
else "",
|
|
342
|
+
}
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
conn.unbind()
|
|
346
|
+
break # 第一个可用的 LDAP 服务器成功即停止
|
|
347
|
+
|
|
348
|
+
except Exception as e:
|
|
349
|
+
SYLogger.warning(f"[WeComLDAP] LDAP 连接 {url} 失败: {e}")
|
|
350
|
+
continue
|
|
351
|
+
|
|
352
|
+
index = _LDAPIndex()
|
|
353
|
+
index.build(raw_users)
|
|
354
|
+
logging.info(f"[WeComLDAP] LDAP 索引构建完成,共 {len(raw_users)} 人")
|
|
355
|
+
return index
|
|
356
|
+
|
|
357
|
+
async def _get_ldap_index(self) -> _LDAPIndex:
|
|
358
|
+
"""获取 LDAP 索引(带缓存)"""
|
|
359
|
+
if (
|
|
360
|
+
self._ldap_index
|
|
361
|
+
and time.time() < self._ldap_index_expires_at
|
|
362
|
+
):
|
|
363
|
+
return self._ldap_index
|
|
364
|
+
|
|
365
|
+
loop = asyncio.get_event_loop()
|
|
366
|
+
self._ldap_index = await loop.run_in_executor(
|
|
367
|
+
None, self._load_ldap_index_sync
|
|
368
|
+
)
|
|
369
|
+
self._ldap_index_expires_at = time.time() + self._ldap_index_ttl
|
|
370
|
+
return self._ldap_index
|
|
371
|
+
|
|
372
|
+
# ─── 辅助方法 ───
|
|
373
|
+
|
|
374
|
+
@staticmethod
|
|
375
|
+
def _parse_wecom_english_name(user_data: dict) -> str:
|
|
376
|
+
"""从企微用户数据中提取英文名"""
|
|
377
|
+
for attr in user_data.get("extattr", {}).get("attrs", []):
|
|
378
|
+
if attr.get("name") == "英文名":
|
|
379
|
+
return attr.get("value", "")
|
|
380
|
+
return ""
|
|
381
|
+
|
|
382
|
+
async def _build_wecom_ldap_user(
|
|
383
|
+
self, user_data: dict, ldap_index: _LDAPIndex
|
|
384
|
+
) -> WeComLDAPUser:
|
|
385
|
+
"""将企微用户数据与 LDAP 信息合并"""
|
|
386
|
+
dept_map = await self._get_dept_map()
|
|
387
|
+
dept_ids = user_data.get("department", [])
|
|
388
|
+
dept_names = [dept_map.get(did, str(did)) for did in dept_ids]
|
|
389
|
+
english_name = self._parse_wecom_english_name(user_data)
|
|
390
|
+
chinese_name = user_data.get("name", "")
|
|
391
|
+
|
|
392
|
+
ldap_user, match_method = ldap_index.lookup(chinese_name, english_name)
|
|
393
|
+
|
|
394
|
+
return WeComLDAPUser(
|
|
395
|
+
wecom_userid=user_data.get("userid", ""),
|
|
396
|
+
wecom_name=chinese_name,
|
|
397
|
+
wecom_english_name=english_name or None,
|
|
398
|
+
wecom_departments=dept_names,
|
|
399
|
+
wecom_position=user_data.get("position") or None,
|
|
400
|
+
matched=ldap_user is not None,
|
|
401
|
+
match_method=match_method,
|
|
402
|
+
ldap_user=ldap_user,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# ─── 公开接口 ───
|
|
406
|
+
|
|
407
|
+
async def get_user_info(self, wecom_userid: str) -> WeComLDAPUser:
|
|
408
|
+
"""通过企微 userid 获取关联的 LDAP 完整用户信息
|
|
409
|
+
|
|
410
|
+
Args:
|
|
411
|
+
wecom_userid: 企微用户ID(如 "0633", "SZ0888")
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
WeComLDAPUser: 合并后的用户信息,包含 LDAP 域账号详情
|
|
415
|
+
"""
|
|
416
|
+
# 1. 从企微获取用户信息
|
|
417
|
+
user_data = await self._get_wecom_user(wecom_userid)
|
|
418
|
+
if not user_data:
|
|
419
|
+
return WeComLDAPUser(wecom_userid=wecom_userid, wecom_name="")
|
|
420
|
+
|
|
421
|
+
# 2. 获取 LDAP 索引
|
|
422
|
+
ldap_index = await self._get_ldap_index()
|
|
423
|
+
|
|
424
|
+
# 3. 合并返回
|
|
425
|
+
return await self._build_wecom_ldap_user(user_data, ldap_index)
|
|
426
|
+
|
|
427
|
+
async def get_all_users(self) -> List[WeComLDAPUser]:
|
|
428
|
+
"""获取全量企微用户并关联 LDAP 信息
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
所有企微用户的关联结果列表
|
|
432
|
+
"""
|
|
433
|
+
# 1. 并行获取企微用户列表和 LDAP 索引
|
|
434
|
+
wecom_users, ldap_index = await asyncio.gather(
|
|
435
|
+
self._get_all_wecom_users(),
|
|
436
|
+
self._get_ldap_index(),
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
# 2. 逐个匹配
|
|
440
|
+
results = []
|
|
441
|
+
for u in wecom_users:
|
|
442
|
+
result = await self._build_wecom_ldap_user(u, ldap_index)
|
|
443
|
+
results.append(result)
|
|
444
|
+
|
|
445
|
+
matched = sum(1 for r in results if r.matched)
|
|
446
|
+
logging.info(
|
|
447
|
+
f"[WeComLDAP] 全量匹配完成: {matched}/{len(results)} 已关联"
|
|
448
|
+
)
|
|
449
|
+
return results
|
|
450
|
+
|
|
451
|
+
async def refresh_cache(self):
|
|
452
|
+
"""手动刷新缓存(access_token + LDAP 索引 + 部门映射)"""
|
|
453
|
+
self._access_token = None
|
|
454
|
+
self._token_expires_at = 0
|
|
455
|
+
self._ldap_index = None
|
|
456
|
+
self._ldap_index_expires_at = 0
|
|
457
|
+
self._dept_map = {}
|
|
458
|
+
|
|
459
|
+
await self._get_access_token()
|
|
460
|
+
await self._get_ldap_index()
|
|
461
|
+
await self._get_dept_map()
|
|
462
|
+
logging.info("[WeComLDAP] 缓存已刷新")
|
|
463
|
+
|
|
464
|
+
async def get_user_email(self, wecom_userid: str) -> Optional[str]:
|
|
465
|
+
"""快捷方法:通过企微 userid 获取邮箱
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
wecom_userid: 企微用户ID
|
|
469
|
+
|
|
470
|
+
Returns:
|
|
471
|
+
邮箱地址,未匹配返回 None
|
|
472
|
+
"""
|
|
473
|
+
user = await self.get_user_info(wecom_userid)
|
|
474
|
+
if user.matched and user.ldap_user:
|
|
475
|
+
return user.ldap_user.email
|
|
476
|
+
return None
|
|
477
|
+
|
|
478
|
+
async def get_user_domain_account(self, wecom_userid: str) -> Optional[str]:
|
|
479
|
+
"""快捷方法:通过企微 userid 获取域账号名
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
wecom_userid: 企微用户ID
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
域账号名(sAMAccountName),未匹配返回 None
|
|
486
|
+
"""
|
|
487
|
+
user = await self.get_user_info(wecom_userid)
|
|
488
|
+
if user.matched and user.ldap_user:
|
|
489
|
+
return user.ldap_user.username
|
|
490
|
+
return None
|
|
@@ -153,13 +153,13 @@ class TokenUsageDBConnector(metaclass=SingletonMeta):
|
|
|
153
153
|
safe_url = f'mysql+aiomysql://{self.db_user}:***@{self.db_host}:{self.db_port}/{self.db_name}'
|
|
154
154
|
SYLogger.info(f"TokenUsageDBService Database URL: {safe_url}")
|
|
155
155
|
|
|
156
|
-
#
|
|
156
|
+
# 创建异步引擎(Token 记录连接池较小,增加连接超时防止慢 MySQL 拖垮主服务)
|
|
157
157
|
self.engine = create_async_engine(
|
|
158
158
|
self.db_url,
|
|
159
|
-
connect_args=params,
|
|
160
|
-
pool_size=
|
|
161
|
-
max_overflow=
|
|
162
|
-
pool_timeout=
|
|
159
|
+
connect_args={**params, "connect_timeout": 3},
|
|
160
|
+
pool_size=2,
|
|
161
|
+
max_overflow=3,
|
|
162
|
+
pool_timeout=5,
|
|
163
163
|
pool_recycle=3600,
|
|
164
164
|
pool_pre_ping=True,
|
|
165
165
|
echo=False,
|