feat: 添加飞书用户模块和订阅功能支持

- 新增feishu_users模块用于处理飞书用户身份验证和权限管理
- 新增subscriptions模块用于处理订阅相关功能
- 新增personalization模块用于个性化服务
- 在alembic迁移配置中注册新的模型模块
- 在API路由器中添加feishu_users和subscriptions路由
- 实现事件调度服务的改进,包括错误处理和状态更新优化
- 添加飞书命令处理的权限检查机制
- 实现飞书应用票据事件处理
- 改进审计日志记录功能
```
This commit is contained in:
2026-07-27 08:02:17 +08:00
parent db751f03b4
commit d7db84571d
148 changed files with 17110 additions and 765 deletions

View File

@@ -0,0 +1,86 @@
from enum import StrEnum
from typing import Iterable
class FeishuUserRole(StrEnum):
USER = "user"
ADMIN = "admin"
class FeishuUserStatus(StrEnum):
ACTIVE = "active"
DISABLED = "disabled"
class FeishuCapability(StrEnum):
PERSONAL_AI = "personal_ai"
PERSONAL_DATA = "personal_data"
PRIVATE_SUBSCRIPTION = "private_subscription"
PERSONAL_MARKET = "personal_market"
COMPANY_REPORTS = "company_reports"
COMPANY_RULES = "company_rules"
USER_ADMINISTRATION = "user_administration"
GROUP_SUBSCRIPTION = "group_subscription"
class FeishuUserAuditAction(StrEnum):
REGISTER = "feishu.user.register"
AUTHENTICATE = "feishu.user.authenticate"
PERMISSION_DENIED = "feishu.permission.denied"
LIST = "feishu.user.list"
READ = "feishu.user.read"
UPDATE = "feishu.user.update"
UPDATE_DENIED = "feishu.user.update_denied"
FEISHU_USER_CODE_PREFIX = "FSU"
FEISHU_USER_TARGET_TYPE = "feishu-user"
DEFAULT_FEISHU_USER_TIMEZONE = "Asia/Shanghai"
LAST_ACTIVE_ADMIN_ERROR = "The last active Feishu administrator cannot be changed"
FEISHU_USER_NOT_FOUND = "Feishu user not found"
INVALID_FEISHU_IDENTITY = "tenant_key and open_id are required"
INVALID_FEISHU_TIMEZONE = "Invalid IANA timezone"
INVALID_QUIET_HOURS = "quiet_hours_start and quiet_hours_end must both be set or cleared"
INVALID_ADMIN_IDENTITY = (
"FEISHU_ADMIN_IDENTITIES entries must use tenant_key:open_id format"
)
_USER_CAPABILITIES = frozenset(
{
FeishuCapability.PERSONAL_AI,
FeishuCapability.PERSONAL_DATA,
FeishuCapability.PRIVATE_SUBSCRIPTION,
FeishuCapability.PERSONAL_MARKET,
}
)
_ADMIN_CAPABILITIES = frozenset(FeishuCapability)
def capabilities_for_role(role: str | FeishuUserRole) -> frozenset[FeishuCapability]:
"""Return the fixed capability set for a Feishu user role."""
if FeishuUserRole(role) == FeishuUserRole.ADMIN:
return _ADMIN_CAPABILITIES
return _USER_CAPABILITIES
def parse_admin_identities(
value: str | Iterable[str] | None,
) -> frozenset[tuple[str, str]]:
"""Parse exact tenant/open-id pairs used only for initial administrator creation."""
if value is None:
return frozenset()
entries = value.split(",") if isinstance(value, str) else value
identities: set[tuple[str, str]] = set()
for entry in entries:
text = str(entry).strip()
if not text:
continue
tenant_key, separator, open_id = text.partition(":")
tenant_key = tenant_key.strip()
open_id = open_id.strip()
if not separator or not tenant_key or not open_id:
raise ValueError(INVALID_ADMIN_IDENTITY)
identities.add((tenant_key, open_id))
return frozenset(identities)