caf9b7ed85
解决改代码 30-60min 才能看到结果的痛点。本地拉起完整 stack, 改代码 → 1-2min 看到结果,无需服务器。 ## 交付物 ### Docker stack (docker-compose.dev.yml) - postgres:16-alpine 端口 5432 - redis:7-alpine 端口 6379 - backend 端口 8000,代码 volume mount + uvicorn --reload ### Dev 镜像 (backend/Dockerfile.dev) - 单阶段(无需 gcc / libpq-dev) - apt 源换阿里云(公司内网) - 装 pytest pytest-asyncio httpx watchfiles - CMD: uvicorn --reload ### 配置 (.env.dev, 强制 add 因 .env.* 在 .gitignore) 内容是 dev 占位符,无任何真实密钥: - DEV_MODE=true (启用 Mock OAuth) - WECOM_* 全部 dev_xxx 占位 - 集成系统 API 全 dev_ 占位(调用会失败但不影响主流程) ### Mock OAuth (backend/app/api/dev_auth.py) - GET /api/dev/login?userid=xxx&name=xxx&role=xxx 走完全真实的 TokenService.create_token(不绕过业务逻辑) - GET /api/dev/users 列出 6 个预设 dev 用户 - GET /api/dev/health dev 模式状态自检 - 6 预设用户覆盖所有角色(user/agent/supervisor/security/admin/多角色) - 每个端点 _dev_mode_enabled() 二次校验,生产环境访问 403 ### 集成改动 - backend/app/main.py: 加 _is_dev_mode() + DEV_MODE=true 时条件挂载 dev_auth 路由 + 启动时大声警告 - backend/app/config.py: Settings 加 dev_mode / dev_default_userid / dev_default_name / dev_default_dept 字段 ### PowerShell 脚本 - scripts/dev-start.ps1: 5 步验证(检查 Docker / .env / compose / 健康 / dev health),首次 2-5min build,后续秒起 - scripts/dev-stop.ps1: 停止,支持 -v 清数据卷 - scripts/dev-test.ps1: 一键跑 pytest(可选 -Frontend 跑 vitest) ## 阶段 - ✅ Phase 0 基础(本 commit) - ⏳ Phase 1 pytest(任务 #90) - 500 bug 回归测试已就绪 - ⏳ Phase 2 vitest - ⏳ Phase 3 playwright E2E ## 安全保证 - DEV_MODE 三个地方都校验(环境变量/settings/端点内) - 生产环境 /api/dev/* 端点根本不存在(未挂载) - .env.dev 是 dev 占位符,无敏感,可入 git
191 lines
9.0 KiB
Python
191 lines
9.0 KiB
Python
# =============================================================================
|
||
# 企微IT智能服务台 — 配置管理模块
|
||
# =============================================================================
|
||
# 说明:使用 pydantic-settings 从环境变量读取所有配置项
|
||
# 优先级:环境变量 > .env 文件 > 默认值
|
||
# 所有配置项集中管理,避免散落在代码各处
|
||
# =============================================================================
|
||
|
||
from typing import List
|
||
|
||
import redis.asyncio as aioredis
|
||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
"""应用配置类。
|
||
|
||
使用 pydantic-settings 自动从环境变量读取配置值。
|
||
支持 .env 文件自动加载(开发环境便利)。
|
||
|
||
Attributes:
|
||
wecom_corp_id: 企业微信企业ID
|
||
wecom_agent_id: 企业微信应用AgentId
|
||
wecom_secret: 企业微信应用Secret
|
||
wecom_token: 企业微信回调Token
|
||
wecom_encoding_aes_key: 企业微信回调EncodingAESKey(43位)
|
||
database_url: PostgreSQL 数据库连接地址
|
||
redis_url: Redis 连接地址
|
||
backend_host: 后端监听地址
|
||
backend_port: 后端监听端口
|
||
cors_origins: CORS 允许的源地址(逗号分隔)
|
||
"""
|
||
|
||
# ----------------------------------------------------------------------
|
||
# 企微自建应用配置
|
||
# ----------------------------------------------------------------------
|
||
# 企业ID(在企微管理后台 > 我的企业 > 企业信息 中查看)
|
||
wecom_corp_id: str = "ww1234567890abcdef"
|
||
# 应用AgentId(在企微管理后台 > 应用管理 > 自建应用 中查看)
|
||
wecom_agent_id: str = "1000002"
|
||
# 应用Secret(在企微管理后台 > 应用管理 > 自建应用 中查看)
|
||
wecom_secret: str = "your-agent-secret"
|
||
# 回调Token(在企微管理后台 > 应用管理 > 接收消息 中设置)
|
||
wecom_token: str = "your-callback-token"
|
||
# 回调EncodingAESKey(43位字符串,用于消息加解密)
|
||
wecom_encoding_aes_key: str = "your-aes-key-43-characters-long-encoding-key"
|
||
|
||
# ----------------------------------------------------------------------
|
||
# 数据库配置
|
||
# ----------------------------------------------------------------------
|
||
# PostgreSQL 连接地址
|
||
# Docker 环境使用容器名 postgres,本地开发使用 localhost
|
||
database_url: str = "postgresql://wecom:wecom_secret@localhost:5432/wecom_it_desk"
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Redis 配置
|
||
# ----------------------------------------------------------------------
|
||
# Redis 连接地址
|
||
# Docker 环境使用容器名 redis,本地开发使用 localhost
|
||
redis_url: str = "redis://localhost:6379/0"
|
||
|
||
# ----------------------------------------------------------------------
|
||
# 服务配置
|
||
# ----------------------------------------------------------------------
|
||
# 后端监听地址(0.0.0.0 表示监听所有网卡)
|
||
backend_host: str = "0.0.0.0"
|
||
# 后端监听端口
|
||
backend_port: int = 8000
|
||
# CORS 允许的源地址(逗号分隔的字符串)
|
||
cors_origins: str = "http://localhost:5173,http://localhost:5174,http://localhost:5175"
|
||
|
||
# ----------------------------------------------------------------------
|
||
# AI 服务配置(Dify)
|
||
# ----------------------------------------------------------------------
|
||
# Dify API 端点(兼容 OpenAI Chat Completions 格式)
|
||
# 必须通过环境变量 DIFY_API_URL 配置,不设置默认值(防止凭据泄露)
|
||
dify_api_url: str = ""
|
||
# Dify API Key(格式:base_url|app_id|app_name)
|
||
# 必须通过环境变量 DIFY_API_KEY 配置,不设置默认值(防止凭据泄露)
|
||
dify_api_key: str = ""
|
||
# Dify API 请求超时(秒),在网络慢时可调大
|
||
dify_timeout: int = 30
|
||
|
||
# ----------------------------------------------------------------------
|
||
# AI Wingman 服务配置(Dify Agent 2 — 坐席端辅助)
|
||
# ----------------------------------------------------------------------
|
||
# 坐席端 Wingman 专用 Dify API 端点(与员工端 Agent 分开)
|
||
# 留空则禁用 Wingman 功能(不影响主流程)
|
||
dify_wingman_api_url: str = ""
|
||
# 坐席端 Wingman Dify API Key(需要新建 Agent 后填入,留空则禁用)
|
||
# 格式:base_url|app_id|app_name(与 dify_api_key 相同格式)
|
||
dify_wingman_api_key: str = ""
|
||
# Wingman API 请求超时(秒)
|
||
dify_wingman_timeout: int = 30
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Mock 登录配置(测试阶段使用,跳过企微 OAuth2)
|
||
# ----------------------------------------------------------------------
|
||
# 是否启用 Mock 登录(默认 false,生产环境必须关闭)
|
||
mock_login_enabled: bool = False
|
||
|
||
# ----------------------------------------------------------------------
|
||
# 开发模式配置(本地 docker-compose.dev.yml 用)
|
||
# ----------------------------------------------------------------------
|
||
# 是否启用开发模式(本地开发环境,启用后挂载 /api/dev/* Mock OAuth 路由)
|
||
# ⚠️ 生产环境必须为 false / 不设置
|
||
# 启用的副作用:
|
||
# 1. 后端启动时挂载 /api/dev/login /users /health 三个 Mock 端点
|
||
# 2. /api/dev/login 跳过企微 OAuth 直接生成 token
|
||
# 3. 启动日志会大声警告 "🧪 DEV_MODE enabled"
|
||
dev_mode: bool = False
|
||
# 开发模式默认 userid(本地前端兜底用,实际由前端 /api/dev/login 传入)
|
||
dev_default_userid: str = "dev-user-001"
|
||
# 开发模式默认姓名
|
||
dev_default_name: str = "开发测试用户"
|
||
# 开发模式默认部门
|
||
dev_default_dept: str = "信息技术部"
|
||
|
||
# ----------------------------------------------------------------------
|
||
# 审批模板配置(企微审批应用)
|
||
# ----------------------------------------------------------------------
|
||
# 资源申请审批模板ID(在企微审批应用设置中获取)
|
||
approval_template_resource: str = ""
|
||
# 设备申请审批模板ID(在企微审批应用设置中获取)
|
||
approval_template_device: str = ""
|
||
|
||
# ----------------------------------------------------------------------
|
||
# v0.5.4 应急页身份检测配置
|
||
# ----------------------------------------------------------------------
|
||
# IT支持-咨询坐席 通讯录标签 ID(在企微管理后台 > 通讯录管理 > 标签管理 中查看)
|
||
# 配置后,应急页会通过此标签判断当前用户是否为坐席
|
||
# 留空则降级到下面的硬编码名单
|
||
wecom_agent_tag_id: str = ""
|
||
# 硬编码坐席 userid 列表(逗号分隔),作为标签检测的降级方案
|
||
# 例:"zhangsan,lisi,wangwu"(生产环境建议用标签方案)
|
||
wecom_agent_userids: str = ""
|
||
|
||
# ----------------------------------------------------------------------
|
||
# v0.6.0 内容审核报警配置(占位,后续完善)
|
||
# ----------------------------------------------------------------------
|
||
# 合规通知企微群机器人 webhook
|
||
content_audit_webhook: str = ""
|
||
# 主管接收报警的 userid(多个用逗号分隔)
|
||
content_audit_supervisor_userids: str = ""
|
||
|
||
# ----------------------------------------------------------------------
|
||
# Pydantic-settings 配置
|
||
# ----------------------------------------------------------------------
|
||
model_config = SettingsConfigDict(
|
||
# 自动从 .env 文件加载环境变量
|
||
env_file=".env",
|
||
# .env 文件编码
|
||
env_file_encoding="utf-8",
|
||
# 环境变量名大小写不敏感
|
||
case_sensitive=False,
|
||
# 额外字段不允许(防止拼写错误的配置被忽略)
|
||
extra="ignore",
|
||
)
|
||
|
||
@property
|
||
def cors_origins_list(self) -> List[str]:
|
||
"""将 CORS 源地址字符串解析为列表。
|
||
|
||
将逗号分隔的字符串(如 "http://a,http://b")
|
||
转换为列表(如 ["http://a", "http://b"]),
|
||
方便 FastAPI 的 CORSMiddleware 使用。
|
||
|
||
Returns:
|
||
List[str]: CORS 允许的源地址列表
|
||
"""
|
||
# 去除每项的前后空格,过滤空字符串
|
||
return [origin.strip() for origin in self.cors_origins.split(",") if origin.strip()]
|
||
|
||
def create_redis_client(self) -> aioredis.Redis:
|
||
"""创建 Redis 异步客户端实例。
|
||
|
||
自动附加 protocol=2 参数,强制使用 RESP2 协议。
|
||
原因:Windows 版 Redis 3.x 不支持 RESP3 协议(HELLO 命令),
|
||
而 redis-py 8.0+ 默认使用 RESP3,会导致连接失败。
|
||
全项目统一使用此方法创建 Redis 客户端,避免协议不匹配。
|
||
|
||
Returns:
|
||
aioredis.Redis: 配置好的 Redis 异步客户端
|
||
"""
|
||
return aioredis.from_url(self.redis_url, protocol=2)
|
||
|
||
|
||
# 创建全局配置实例
|
||
# 整个应用通过 from app.config import settings 使用同一个实例
|
||
settings = Settings()
|