Compare commits
2 Commits
5743d05bb5
...
c38f3eb467
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c38f3eb467 | ||
|
|
911e891451 |
328
README.md
328
README.md
@@ -205,17 +205,140 @@ MIT
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Phase 8: 商业化与规模化 - 进行中 🚧
|
||||||
|
|
||||||
|
基于 Phase 1-7 的完整功能,Phase 8 聚焦**商业化落地**和**规模化运营**:
|
||||||
|
|
||||||
|
### 1. 多租户 SaaS 架构 🏢
|
||||||
|
**优先级: P0** | **状态: ✅ 已完成**
|
||||||
|
- ✅ 租户隔离(数据、配置、资源完全隔离)
|
||||||
|
- ✅ 自定义域名绑定(CNAME 支持)
|
||||||
|
- ✅ 品牌白标(Logo、主题色、自定义 CSS)
|
||||||
|
- ✅ 租户级权限管理(超级管理员、管理员、成员)
|
||||||
|
|
||||||
|
### 2. 订阅与计费系统 💳
|
||||||
|
**优先级: P0** | **状态: ✅ 已完成**
|
||||||
|
- ✅ 多层级订阅计划(Free/Pro/Enterprise)
|
||||||
|
- ✅ 按量计费(转录时长、存储空间、API 调用次数)
|
||||||
|
- ✅ 支付集成(Stripe、支付宝、微信支付)
|
||||||
|
- ✅ 发票管理、退款处理、账单历史
|
||||||
|
|
||||||
|
### 3. 企业级功能 🏭
|
||||||
|
**优先级: P1** | **状态: ✅ 已完成**
|
||||||
|
- ✅ SSO/SAML 单点登录(企业微信、钉钉、飞书、Okta)
|
||||||
|
- ✅ SCIM 用户目录同步
|
||||||
|
- ✅ 审计日志导出(SOC2/ISO27001 合规)
|
||||||
|
- ✅ 数据保留策略(自动归档、数据删除)
|
||||||
|
|
||||||
|
### 4. 运营与增长工具 📈
|
||||||
|
**优先级: P1**
|
||||||
|
- 用户行为分析(Mixpanel/Amplitude 集成)
|
||||||
|
- A/B 测试框架
|
||||||
|
- 邮件营销自动化(欢迎序列、流失挽回)
|
||||||
|
- 推荐系统(邀请返利、团队升级激励)
|
||||||
|
|
||||||
|
### 5. 开发者生态 🛠️
|
||||||
|
**优先级: P2**
|
||||||
|
- SDK 发布(Python/JavaScript/Go)
|
||||||
|
- 模板市场(行业模板、预训练模型)
|
||||||
|
- 插件市场(第三方插件审核与分发)
|
||||||
|
- 开发者文档与示例代码
|
||||||
|
|
||||||
|
### 6. 全球化与本地化 🌍
|
||||||
|
**优先级: P2**
|
||||||
|
- 多语言支持(i18n,至少 10 种语言)
|
||||||
|
- 区域数据中心(北美、欧洲、亚太)
|
||||||
|
- 本地化支付(各国主流支付方式)
|
||||||
|
- 时区与日历本地化
|
||||||
|
|
||||||
|
### 7. AI 能力增强 🤖
|
||||||
|
**优先级: P1** | **状态: ✅ 已完成**
|
||||||
|
- ✅ 自定义模型训练(领域特定实体识别)
|
||||||
|
- ✅ 多模态大模型集成(GPT-4V、Claude 3)
|
||||||
|
- ✅ 智能摘要与问答(基于知识图谱的 RAG)
|
||||||
|
- ✅ 预测性分析(趋势预测、异常检测)
|
||||||
|
|
||||||
|
### 8. 运维与监控 🔧
|
||||||
|
**优先级: P2**
|
||||||
|
- 实时告警系统(PagerDuty/Opsgenie 集成)
|
||||||
|
- 容量规划与自动扩缩容
|
||||||
|
- 灾备与故障转移(多活架构)
|
||||||
|
- 成本优化(资源利用率监控)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 8 任务 7 完成内容
|
||||||
|
|
||||||
|
**全球化与本地化** ✅
|
||||||
|
|
||||||
|
- ✅ 创建 localization_manager.py - 全球化与本地化管理模块
|
||||||
|
- LocalizationManager: 全球化与本地化管理主类
|
||||||
|
- LanguageCode: 支持12种语言(英语、简体中文、繁体中文、日语、韩语、德语、法语、西班牙语、葡萄牙语、俄语、阿拉伯语、印地语)
|
||||||
|
- RegionCode/DataCenterRegion: 区域和数据中心配置(北美、欧洲、亚太、中国等)
|
||||||
|
- Translation: 翻译管理(支持命名空间、回退语言、审核流程)
|
||||||
|
- LanguageConfig: 语言配置(RTL支持、日期时间格式、数字格式、日历类型)
|
||||||
|
- DataCenter: 数据中心管理(9个数据中心,支持全球分布)
|
||||||
|
- TenantDataCenterMapping: 租户数据中心映射(主备数据中心、数据驻留策略)
|
||||||
|
- LocalizedPaymentMethod: 本地化支付方式(12种支付方式,支持国家/货币过滤)
|
||||||
|
- CountryConfig: 国家配置(语言、货币、时区、税率等)
|
||||||
|
- TimezoneConfig: 时区配置管理
|
||||||
|
- CurrencyConfig: 货币配置管理
|
||||||
|
- LocalizationSettings: 租户本地化设置
|
||||||
|
- 日期时间格式化(支持Babel本地化)
|
||||||
|
- 数字和货币格式化
|
||||||
|
- 时区转换
|
||||||
|
- 日历信息获取
|
||||||
|
- 用户偏好自动检测
|
||||||
|
- ✅ 更新 schema.sql - 添加本地化相关数据库表
|
||||||
|
- translations: 翻译表
|
||||||
|
- language_configs: 语言配置表
|
||||||
|
- data_centers: 数据中心表
|
||||||
|
- tenant_data_center_mappings: 租户数据中心映射表
|
||||||
|
- localized_payment_methods: 本地化支付方式表
|
||||||
|
- country_configs: 国家配置表
|
||||||
|
- timezone_configs: 时区配置表
|
||||||
|
- currency_configs: 货币配置表
|
||||||
|
- localization_settings: 租户本地化设置表
|
||||||
|
- 相关索引优化
|
||||||
|
- ✅ 更新 main.py - 添加本地化相关 API 端点(35个端点)
|
||||||
|
- GET /api/v1/translations/{language}/{key} - 获取翻译
|
||||||
|
- POST /api/v1/translations/{language} - 创建翻译
|
||||||
|
- PUT /api/v1/translations/{language}/{key} - 更新翻译
|
||||||
|
- DELETE /api/v1/translations/{language}/{key} - 删除翻译
|
||||||
|
- GET /api/v1/translations - 列出翻译
|
||||||
|
- GET /api/v1/languages - 列出语言
|
||||||
|
- GET /api/v1/languages/{code} - 获取语言详情
|
||||||
|
- GET /api/v1/data-centers - 列出数据中心
|
||||||
|
- GET /api/v1/data-centers/{dc_id} - 获取数据中心详情
|
||||||
|
- GET /api/v1/tenants/{tenant_id}/data-center - 获取租户数据中心
|
||||||
|
- POST /api/v1/tenants/{tenant_id}/data-center - 设置租户数据中心
|
||||||
|
- GET /api/v1/payment-methods - 列出支付方式
|
||||||
|
- GET /api/v1/payment-methods/localized - 获取本地化支付方式
|
||||||
|
- GET /api/v1/countries - 列出国家
|
||||||
|
- GET /api/v1/countries/{code} - 获取国家详情
|
||||||
|
- GET /api/v1/tenants/{tenant_id}/localization - 获取租户本地化设置
|
||||||
|
- POST /api/v1/tenants/{tenant_id}/localization - 创建租户本地化设置
|
||||||
|
- PUT /api/v1/tenants/{tenant_id}/localization - 更新租户本地化设置
|
||||||
|
- POST /api/v1/format/datetime - 格式化日期时间
|
||||||
|
- POST /api/v1/format/number - 格式化数字
|
||||||
|
- POST /api/v1/format/currency - 格式化货币
|
||||||
|
- POST /api/v1/convert/timezone - 转换时区
|
||||||
|
- GET /api/v1/detect/locale - 检测用户本地化偏好
|
||||||
|
- GET /api/v1/calendar/{calendar_type} - 获取日历信息
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Phase 8 开发进度
|
## Phase 8 开发进度
|
||||||
|
|
||||||
| 任务 | 状态 | 完成时间 |
|
| 任务 | 状态 | 完成时间 |
|
||||||
|------|------|----------|
|
|------|------|----------|
|
||||||
| 1. 多租户 SaaS 架构 | ✅ 已完成 | 2026-02-25 |
|
| 1. 多租户 SaaS 架构 | ✅ 已完成 | 2026-02-25 |
|
||||||
| 2. 订阅与计费系统 | 🚧 进行中 | - |
|
| 2. 订阅与计费系统 | ✅ 已完成 | 2026-02-25 |
|
||||||
| 3. 企业级功能 | ⏳ 待开始 | - |
|
| 3. 企业级功能 | ✅ 已完成 | 2026-02-25 |
|
||||||
| 4. AI 能力增强 | ⏳ 待开始 | - |
|
| 7. 全球化与本地化 | ✅ 已完成 | 2026-02-25 |
|
||||||
|
| 4. AI 能力增强 | ✅ 已完成 | 2026-02-26 |
|
||||||
| 5. 运营与增长工具 | ⏳ 待开始 | - |
|
| 5. 运营与增长工具 | ⏳ 待开始 | - |
|
||||||
| 6. 开发者生态 | ⏳ 待开始 | - |
|
| 6. 开发者生态 | ⏳ 待开始 | - |
|
||||||
| 7. 全球化与本地化 | ⏳ 待开始 | - |
|
|
||||||
| 8. 运维与监控 | ⏳ 待开始 | - |
|
| 8. 运维与监控 | ⏳ 待开始 | - |
|
||||||
|
|
||||||
### Phase 8 任务 1 完成内容
|
### Phase 8 任务 1 完成内容
|
||||||
@@ -249,72 +372,145 @@ MIT
|
|||||||
- GET /api/v1/tenants/{id}/limits/{type} - 资源限制检查
|
- GET /api/v1/tenants/{id}/limits/{type} - 资源限制检查
|
||||||
- GET /api/v1/resolve-tenant - 域名解析租户
|
- GET /api/v1/resolve-tenant - 域名解析租户
|
||||||
|
|
||||||
|
### Phase 8 任务 2 完成内容
|
||||||
|
|
||||||
|
**订阅与计费系统** ✅
|
||||||
|
|
||||||
|
- ✅ 创建 subscription_manager.py - 订阅与计费管理模块
|
||||||
|
- SubscriptionPlan: 订阅计划模型(Free/Pro/Enterprise)
|
||||||
|
- Subscription: 订阅记录(支持试用、周期计费)
|
||||||
|
- UsageRecord: 用量记录(转录时长、存储空间、API 调用)
|
||||||
|
- Payment: 支付记录(支持 Stripe/支付宝/微信支付)
|
||||||
|
- Invoice: 发票管理
|
||||||
|
- Refund: 退款处理
|
||||||
|
- BillingHistory: 账单历史
|
||||||
|
- ✅ 更新 schema.sql - 添加订阅相关数据库表
|
||||||
|
- subscription_plans: 订阅计划表
|
||||||
|
- subscriptions: 订阅表
|
||||||
|
- usage_records: 用量记录表
|
||||||
|
- payments: 支付记录表
|
||||||
|
- invoices: 发票表
|
||||||
|
- refunds: 退款表
|
||||||
|
- billing_history: 账单历史表
|
||||||
|
- ✅ 更新 main.py - 添加订阅相关 API 端点(26个端点)
|
||||||
|
- GET /api/v1/subscription-plans - 获取订阅计划列表
|
||||||
|
- POST/GET /api/v1/tenants/{id}/subscriptions - 订阅管理
|
||||||
|
- POST /api/v1/tenants/{id}/subscriptions/{id}/cancel - 取消订阅
|
||||||
|
- POST /api/v1/tenants/{id}/subscriptions/{id}/change-plan - 变更计划
|
||||||
|
- GET /api/v1/tenants/{id}/usage - 用量统计
|
||||||
|
- POST /api/v1/tenants/{id}/usage/record - 记录用量
|
||||||
|
- POST /api/v1/tenants/{id}/payments - 创建支付
|
||||||
|
- GET /api/v1/tenants/{id}/payments - 支付历史
|
||||||
|
- POST/GET /api/v1/tenants/{id}/invoices - 发票管理
|
||||||
|
- POST/GET /api/v1/tenants/{id}/refunds - 退款管理
|
||||||
|
- POST /api/v1/tenants/{id}/refunds/{id}/process - 处理退款
|
||||||
|
- GET /api/v1/tenants/{id}/billing-history - 账单历史
|
||||||
|
- POST /api/v1/payments/stripe/create - Stripe 支付
|
||||||
|
- POST /api/v1/payments/alipay/create - 支付宝支付
|
||||||
|
- POST /api/v1/payments/wechat/create - 微信支付
|
||||||
|
- POST /webhooks/stripe - Stripe Webhook
|
||||||
|
- POST /webhooks/alipay - 支付宝 Webhook
|
||||||
|
- POST /webhooks/wechat - 微信支付 Webhook
|
||||||
|
|
||||||
|
### Phase 8 任务 3 完成内容
|
||||||
|
|
||||||
|
**企业级功能** ✅
|
||||||
|
|
||||||
|
- ✅ 创建 enterprise_manager.py - 企业级功能管理模块
|
||||||
|
- SSOConfig: SSO/SAML 配置数据模型(支持企业微信、钉钉、飞书、Okta、Azure AD、Google、自定义 SAML)
|
||||||
|
- SCIMConfig/SCIMUser: SCIM 用户目录同步配置和用户数据模型
|
||||||
|
- AuditLogExport: 审计日志导出记录(支持 SOC2/ISO27001/GDPR/HIPAA/PCI DSS 合规)
|
||||||
|
- DataRetentionPolicy/DataRetentionJob: 数据保留策略和任务管理
|
||||||
|
- SAMLAuthRequest/SAMLAuthResponse: SAML 认证请求和响应管理
|
||||||
|
- SSO 配置管理(创建、更新、删除、列表、元数据生成)
|
||||||
|
- SCIM 用户同步(配置管理、手动同步、用户列表)
|
||||||
|
- 审计日志导出(创建导出任务、处理、下载、合规标准支持)
|
||||||
|
- 数据保留策略(创建、执行、归档/删除/匿名化、任务追踪)
|
||||||
|
- ✅ 更新 schema.sql - 添加企业级功能相关数据库表
|
||||||
|
- sso_configs: SSO 配置表(SAML/OAuth 配置、属性映射、域名限制)
|
||||||
|
- saml_auth_requests: SAML 认证请求表
|
||||||
|
- saml_auth_responses: SAML 认证响应表
|
||||||
|
- scim_configs: SCIM 配置表
|
||||||
|
- scim_users: SCIM 用户表
|
||||||
|
- audit_log_exports: 审计日志导出表
|
||||||
|
- data_retention_policies: 数据保留策略表
|
||||||
|
- data_retention_jobs: 数据保留任务表
|
||||||
|
- 相关索引优化
|
||||||
|
- ✅ 更新 main.py - 添加企业级功能相关 API 端点(25个端点)
|
||||||
|
- POST/GET /api/v1/tenants/{id}/sso-configs - SSO 配置管理
|
||||||
|
- GET/PUT/DELETE /api/v1/tenants/{id}/sso-configs/{id} - SSO 配置详情/更新/删除
|
||||||
|
- GET /api/v1/tenants/{id}/sso-configs/{id}/metadata - 获取 SAML 元数据
|
||||||
|
- POST/GET /api/v1/tenants/{id}/scim-configs - SCIM 配置管理
|
||||||
|
- PUT /api/v1/tenants/{id}/scim-configs/{id} - 更新 SCIM 配置
|
||||||
|
- POST /api/v1/tenants/{id}/scim-configs/{id}/sync - 执行 SCIM 同步
|
||||||
|
- GET /api/v1/tenants/{id}/scim-users - 列出 SCIM 用户
|
||||||
|
- POST /api/v1/tenants/{id}/audit-exports - 创建审计日志导出
|
||||||
|
- GET /api/v1/tenants/{id}/audit-exports - 列出审计日志导出
|
||||||
|
- GET /api/v1/tenants/{id}/audit-exports/{id} - 获取导出详情
|
||||||
|
- POST /api/v1/tenants/{id}/audit-exports/{id}/download - 下载导出文件
|
||||||
|
- POST /api/v1/tenants/{id}/retention-policies - 创建数据保留策略
|
||||||
|
- GET /api/v1/tenants/{id}/retention-policies - 列出保留策略
|
||||||
|
- GET /api/v1/tenants/{id}/retention-policies/{id} - 获取策略详情
|
||||||
|
- PUT /api/v1/tenants/{id}/retention-policies/{id} - 更新保留策略
|
||||||
|
- DELETE /api/v1/tenants/{id}/retention-policies/{id} - 删除保留策略
|
||||||
|
- POST /api/v1/tenants/{id}/retention-policies/{id}/execute - 执行保留策略
|
||||||
|
- GET /api/v1/tenants/{id}/retention-policies/{id}/jobs - 列出保留任务
|
||||||
|
|
||||||
|
### Phase 8 任务 4 完成内容
|
||||||
|
|
||||||
|
**AI 能力增强** ✅
|
||||||
|
|
||||||
|
- ✅ 创建 ai_manager.py - AI 能力增强管理模块
|
||||||
|
- AIManager: AI 能力管理主类
|
||||||
|
- CustomModel/ModelType/ModelStatus: 自定义模型管理(支持领域特定实体识别)
|
||||||
|
- TrainingSample: 训练样本管理
|
||||||
|
- MultimodalAnalysis/MultimodalProvider: 多模态分析(支持 GPT-4V、Claude 3、Gemini、Kimi-VL)
|
||||||
|
- KnowledgeGraphRAG: 基于知识图谱的 RAG 配置管理
|
||||||
|
- RAGQuery: RAG 查询记录
|
||||||
|
- SmartSummary: 智能摘要(extractive/abstractive/key_points/timeline)
|
||||||
|
- PredictionModel/PredictionType: 预测模型管理(趋势预测、异常检测、实体增长预测、关系演变预测)
|
||||||
|
- PredictionResult: 预测结果管理
|
||||||
|
- 自定义模型训练流程(创建、添加样本、训练、预测)
|
||||||
|
- 多模态分析流程(图片、视频、音频、混合输入)
|
||||||
|
- 知识图谱 RAG 检索与生成
|
||||||
|
- 智能摘要生成
|
||||||
|
- 预测性分析(趋势、异常、增长、演变)
|
||||||
|
- ✅ 更新 schema.sql - 添加 AI 能力增强相关数据库表
|
||||||
|
- custom_models: 自定义模型表
|
||||||
|
- training_samples: 训练样本表
|
||||||
|
- multimodal_analyses: 多模态分析表
|
||||||
|
- kg_rag_configs: 知识图谱 RAG 配置表
|
||||||
|
- rag_queries: RAG 查询记录表
|
||||||
|
- smart_summaries: 智能摘要表
|
||||||
|
- prediction_models: 预测模型表
|
||||||
|
- prediction_results: 预测结果表
|
||||||
|
- 相关索引优化
|
||||||
|
- ✅ 更新 main.py - 添加 AI 能力增强相关 API 端点(30+个端点)
|
||||||
|
- POST /api/v1/tenants/{tenant_id}/ai/custom-models - 创建自定义模型
|
||||||
|
- GET /api/v1/tenants/{tenant_id}/ai/custom-models - 列出自定义模型
|
||||||
|
- GET /api/v1/ai/custom-models/{model_id} - 获取模型详情
|
||||||
|
- POST /api/v1/ai/custom-models/{model_id}/samples - 添加训练样本
|
||||||
|
- GET /api/v1/ai/custom-models/{model_id}/samples - 获取训练样本
|
||||||
|
- POST /api/v1/ai/custom-models/{model_id}/train - 训练模型
|
||||||
|
- POST /api/v1/ai/custom-models/predict - 模型预测
|
||||||
|
- POST /api/v1/tenants/{tenant_id}/projects/{project_id}/ai/multimodal - 多模态分析
|
||||||
|
- GET /api/v1/tenants/{tenant_id}/ai/multimodal - 获取多模态分析历史
|
||||||
|
- POST /api/v1/tenants/{tenant_id}/projects/{project_id}/ai/kg-rag - 创建知识图谱 RAG
|
||||||
|
- GET /api/v1/tenants/{tenant_id}/ai/kg-rag - 列出 RAG 配置
|
||||||
|
- POST /api/v1/ai/kg-rag/query - 知识图谱 RAG 查询
|
||||||
|
- POST /api/v1/tenants/{tenant_id}/projects/{project_id}/ai/summarize - 生成智能摘要
|
||||||
|
- POST /api/v1/tenants/{tenant_id}/projects/{project_id}/ai/prediction-models - 创建预测模型
|
||||||
|
- GET /api/v1/tenants/{tenant_id}/ai/prediction-models - 列出预测模型
|
||||||
|
- GET /api/v1/ai/prediction-models/{model_id} - 获取预测模型详情
|
||||||
|
- POST /api/v1/ai/prediction-models/{model_id}/train - 训练预测模型
|
||||||
|
- POST /api/v1/ai/prediction-models/predict - 进行预测
|
||||||
|
- GET /api/v1/ai/prediction-models/{model_id}/results - 获取预测结果历史
|
||||||
|
- POST /api/v1/ai/prediction-results/feedback - 更新预测反馈
|
||||||
|
|
||||||
**预计 Phase 8 完成时间**: 6-8 周
|
**预计 Phase 8 完成时间**: 6-8 周
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 8: 商业化与规模化 - 进行中 🚧
|
|
||||||
|
|
||||||
基于 Phase 1-7 的完整功能,Phase 8 聚焦**商业化落地**和**规模化运营**:
|
|
||||||
|
|
||||||
### 1. 多租户 SaaS 架构 🏢
|
|
||||||
**优先级: P0** | **状态: ✅ 已完成**
|
|
||||||
- ✅ 租户隔离(数据、配置、资源完全隔离)
|
|
||||||
- ✅ 自定义域名绑定(CNAME 支持)
|
|
||||||
- ✅ 品牌白标(Logo、主题色、自定义 CSS)
|
|
||||||
- ✅ 租户级权限管理(超级管理员、管理员、成员)
|
|
||||||
|
|
||||||
### 2. 订阅与计费系统 💳
|
|
||||||
**优先级: P0**
|
|
||||||
- 多层级订阅计划(Free/Pro/Enterprise)
|
|
||||||
- 按量计费(转录时长、存储空间、API 调用次数)
|
|
||||||
- 支付集成(Stripe、支付宝、微信支付)
|
|
||||||
- 发票管理、退款处理、账单历史
|
|
||||||
|
|
||||||
### 3. 企业级功能 🏭
|
|
||||||
**优先级: P1**
|
|
||||||
- SSO/SAML 单点登录(企业微信、钉钉、飞书、Okta)
|
|
||||||
- SCIM 用户目录同步
|
|
||||||
- 审计日志导出(SOC2/ISO27001 合规)
|
|
||||||
- 数据保留策略(自动归档、数据删除)
|
|
||||||
|
|
||||||
### 4. 运营与增长工具 📈
|
|
||||||
**优先级: P1**
|
|
||||||
- 用户行为分析(Mixpanel/Amplitude 集成)
|
|
||||||
- A/B 测试框架
|
|
||||||
- 邮件营销自动化(欢迎序列、流失挽回)
|
|
||||||
- 推荐系统(邀请返利、团队升级激励)
|
|
||||||
|
|
||||||
### 5. 开发者生态 🛠️
|
|
||||||
**优先级: P2**
|
|
||||||
- SDK 发布(Python/JavaScript/Go)
|
|
||||||
- 模板市场(行业模板、预训练模型)
|
|
||||||
- 插件市场(第三方插件审核与分发)
|
|
||||||
- 开发者文档与示例代码
|
|
||||||
|
|
||||||
### 6. 全球化与本地化 🌍
|
|
||||||
**优先级: P2**
|
|
||||||
- 多语言支持(i18n,至少 10 种语言)
|
|
||||||
- 区域数据中心(北美、欧洲、亚太)
|
|
||||||
- 本地化支付(各国主流支付方式)
|
|
||||||
- 时区与日历本地化
|
|
||||||
|
|
||||||
### 7. AI 能力增强 🤖
|
|
||||||
**优先级: P1**
|
|
||||||
- 自定义模型训练(领域特定实体识别)
|
|
||||||
- 多模态大模型集成(GPT-4V、Claude 3)
|
|
||||||
- 智能摘要与问答(基于知识图谱的 RAG)
|
|
||||||
- 预测性分析(趋势预测、异常检测)
|
|
||||||
|
|
||||||
### 8. 运维与监控 🔧
|
|
||||||
**优先级: P2**
|
|
||||||
- 实时告警系统(PagerDuty/Opsgenie 集成)
|
|
||||||
- 容量规划与自动扩缩容
|
|
||||||
- 灾备与故障转移(多活架构)
|
|
||||||
- 成本优化(资源利用率监控)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**建议开发顺序**: 1 → 2 → 3 → 7 → 4 → 5 → 6 → 8
|
**建议开发顺序**: 1 → 2 → 3 → 7 → 4 → 5 → 6 → 8
|
||||||
|
|
||||||
**预计 Phase 8 完成时间**: 6-8 周
|
**预计 Phase 8 完成时间**: 6-8 周
|
||||||
|
|||||||
229
backend/STATUS.md
Normal file
229
backend/STATUS.md
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
# InsightFlow 开发状态
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
InsightFlow 是一个智能知识管理平台,支持从会议记录、文档中提取实体和关系,构建知识图谱。
|
||||||
|
|
||||||
|
## 当前阶段:Phase 8 - 商业化与规模化
|
||||||
|
|
||||||
|
### 已完成任务
|
||||||
|
|
||||||
|
#### Phase 8 Task 1: 多租户 SaaS 架构 (P0 - 最高优先级) ✅
|
||||||
|
|
||||||
|
**功能实现:**
|
||||||
|
|
||||||
|
1. **租户隔离**(数据、配置、资源完全隔离)✅
|
||||||
|
- 租户数据隔离方案设计 - 使用表前缀隔离
|
||||||
|
- 数据库级别的租户隔离 - 通过 `table_prefix` 字段实现
|
||||||
|
- API 层面的租户上下文管理 - `TenantContext` 类
|
||||||
|
|
||||||
|
2. **自定义域名绑定**(CNAME 支持)✅
|
||||||
|
- 租户自定义域名配置 - `tenant_domains` 表
|
||||||
|
- 域名验证机制 - DNS TXT 记录验证
|
||||||
|
- 基于域名的租户路由 - `get_tenant_by_domain()` 方法
|
||||||
|
|
||||||
|
3. **品牌白标**(Logo、主题色、自定义 CSS)✅
|
||||||
|
- 租户品牌配置存储 - `tenant_branding` 表
|
||||||
|
- 动态主题加载 - `get_branding_css()` 方法
|
||||||
|
- 自定义 CSS 支持 - `custom_css` 字段
|
||||||
|
|
||||||
|
4. **租户级权限管理**✅
|
||||||
|
- 租户管理员角色 - `TenantRole` (owner, admin, member, viewer)
|
||||||
|
- 成员邀请与管理 - `invite_member()`, `accept_invitation()`
|
||||||
|
- 角色权限配置 - `ROLE_PERMISSIONS` 映射
|
||||||
|
|
||||||
|
**技术实现:**
|
||||||
|
|
||||||
|
- ✅ `tenant_manager.py` - 租户管理核心模块
|
||||||
|
- ✅ `schema.sql` - 更新数据库表结构
|
||||||
|
- `tenants` - 租户主表
|
||||||
|
- `tenant_domains` - 租户域名绑定表
|
||||||
|
- `tenant_branding` - 租户品牌配置表
|
||||||
|
- `tenant_members` - 租户成员表
|
||||||
|
- `tenant_permissions` - 租户权限表
|
||||||
|
- `tenant_usage` - 租户资源使用统计表
|
||||||
|
- ✅ `main.py` - 添加租户相关 API 端点
|
||||||
|
- ✅ `requirements.txt` - 无需新增依赖
|
||||||
|
- ✅ `test_tenant.py` - 测试脚本
|
||||||
|
|
||||||
|
#### Phase 8 Task 2: 订阅与计费系统 (P0 - 最高优先级) ✅
|
||||||
|
|
||||||
|
**功能实现:**
|
||||||
|
|
||||||
|
1. **多层级订阅计划**(Free/Pro/Enterprise)✅
|
||||||
|
2. **按量计费**(转录时长、存储空间、API 调用次数)✅
|
||||||
|
3. **支付集成**(Stripe、支付宝、微信支付)✅
|
||||||
|
4. **发票管理、退款处理、账单历史**✅
|
||||||
|
|
||||||
|
**技术实现:**
|
||||||
|
|
||||||
|
- ✅ `subscription_manager.py` - 订阅与计费管理模块
|
||||||
|
- ✅ `schema.sql` - 添加订阅相关数据库表
|
||||||
|
- ✅ `main.py` - 添加 26 个 API 端点
|
||||||
|
|
||||||
|
#### Phase 8 Task 3: 企业级功能 (P1 - 高优先级) ✅
|
||||||
|
|
||||||
|
**功能实现:**
|
||||||
|
|
||||||
|
1. **SSO/SAML 单点登录**(企业微信、钉钉、飞书、Okta)✅
|
||||||
|
2. **SCIM 用户目录同步**✅
|
||||||
|
3. **审计日志导出**(SOC2/ISO27001 合规)✅
|
||||||
|
4. **数据保留策略**(自动归档、数据删除)✅
|
||||||
|
|
||||||
|
**技术实现:**
|
||||||
|
|
||||||
|
- ✅ `enterprise_manager.py` - 企业级功能管理模块
|
||||||
|
- ✅ `schema.sql` - 添加企业级功能相关数据库表
|
||||||
|
- ✅ `main.py` - 添加 25 个 API 端点
|
||||||
|
|
||||||
|
#### Phase 8 Task 4: AI 能力增强 (P1 - 高优先级) ✅
|
||||||
|
|
||||||
|
**功能实现:**
|
||||||
|
|
||||||
|
1. **自定义模型训练**(领域特定实体识别)✅
|
||||||
|
- CustomModel/ModelType/ModelStatus 数据模型
|
||||||
|
- TrainingSample 训练样本管理
|
||||||
|
- 模型训练流程(创建、添加样本、训练、预测)
|
||||||
|
|
||||||
|
2. **多模态大模型集成**(GPT-4V、Claude 3)✅
|
||||||
|
- MultimodalAnalysis 多模态分析
|
||||||
|
- 支持 GPT-4V、Claude 3、Gemini、Kimi-VL
|
||||||
|
- 图片、视频、音频、混合输入分析
|
||||||
|
|
||||||
|
3. **智能摘要与问答**(基于知识图谱的 RAG)✅
|
||||||
|
- KnowledgeGraphRAG 配置管理
|
||||||
|
- RAGQuery 查询记录
|
||||||
|
- SmartSummary 智能摘要(extractive/abstractive/key_points/timeline)
|
||||||
|
|
||||||
|
4. **预测性分析**(趋势预测、异常检测)✅
|
||||||
|
- PredictionModel/PredictionType 预测模型管理
|
||||||
|
- 趋势预测、异常检测、实体增长预测、关系演变预测
|
||||||
|
- PredictionResult 预测结果管理
|
||||||
|
|
||||||
|
**技术实现:**
|
||||||
|
|
||||||
|
- ✅ `ai_manager.py` - AI 能力增强管理模块(1330+ 行代码)
|
||||||
|
- AIManager: AI 能力管理主类
|
||||||
|
- 自定义模型训练流程
|
||||||
|
- 多模态分析(GPT-4V、Claude 3、Gemini、Kimi-VL)
|
||||||
|
- 知识图谱 RAG 检索与生成
|
||||||
|
- 智能摘要生成(多种类型)
|
||||||
|
- 预测性分析(趋势、异常、增长、演变)
|
||||||
|
|
||||||
|
- ✅ `schema.sql` - 添加 AI 能力增强相关数据库表
|
||||||
|
- `custom_models` - 自定义模型表
|
||||||
|
- `training_samples` - 训练样本表
|
||||||
|
- `multimodal_analyses` - 多模态分析表
|
||||||
|
- `kg_rag_configs` - 知识图谱 RAG 配置表
|
||||||
|
- `rag_queries` - RAG 查询记录表
|
||||||
|
- `smart_summaries` - 智能摘要表
|
||||||
|
- `prediction_models` - 预测模型表
|
||||||
|
- `prediction_results` - 预测结果表
|
||||||
|
|
||||||
|
- ✅ `main.py` - 添加 30+ 个 API 端点
|
||||||
|
- 自定义模型管理(创建、训练、预测)
|
||||||
|
- 多模态分析
|
||||||
|
- 知识图谱 RAG(配置、查询)
|
||||||
|
- 智能摘要
|
||||||
|
- 预测模型(创建、训练、预测、反馈)
|
||||||
|
|
||||||
|
- ✅ `test_phase8_task4.py` - 测试脚本
|
||||||
|
|
||||||
|
**API 端点:**
|
||||||
|
|
||||||
|
自定义模型管理:
|
||||||
|
- `POST /api/v1/tenants/{tenant_id}/ai/custom-models` - 创建自定义模型
|
||||||
|
- `GET /api/v1/tenants/{tenant_id}/ai/custom-models` - 列出自定义模型
|
||||||
|
- `GET /api/v1/ai/custom-models/{model_id}` - 获取模型详情
|
||||||
|
- `POST /api/v1/ai/custom-models/{model_id}/samples` - 添加训练样本
|
||||||
|
- `GET /api/v1/ai/custom-models/{model_id}/samples` - 获取训练样本
|
||||||
|
- `POST /api/v1/ai/custom-models/{model_id}/train` - 训练模型
|
||||||
|
- `POST /api/v1/ai/custom-models/predict` - 模型预测
|
||||||
|
|
||||||
|
多模态分析:
|
||||||
|
- `POST /api/v1/tenants/{tenant_id}/projects/{project_id}/ai/multimodal` - 多模态分析
|
||||||
|
- `GET /api/v1/tenants/{tenant_id}/ai/multimodal` - 获取多模态分析历史
|
||||||
|
|
||||||
|
知识图谱 RAG:
|
||||||
|
- `POST /api/v1/tenants/{tenant_id}/projects/{project_id}/ai/kg-rag` - 创建 RAG 配置
|
||||||
|
- `GET /api/v1/tenants/{tenant_id}/ai/kg-rag` - 列出 RAG 配置
|
||||||
|
- `POST /api/v1/ai/kg-rag/query` - 知识图谱 RAG 查询
|
||||||
|
|
||||||
|
智能摘要:
|
||||||
|
- `POST /api/v1/tenants/{tenant_id}/projects/{project_id}/ai/summarize` - 生成智能摘要
|
||||||
|
|
||||||
|
预测模型:
|
||||||
|
- `POST /api/v1/tenants/{tenant_id}/projects/{project_id}/ai/prediction-models` - 创建预测模型
|
||||||
|
- `GET /api/v1/tenants/{tenant_id}/ai/prediction-models` - 列出预测模型
|
||||||
|
- `GET /api/v1/ai/prediction-models/{model_id}` - 获取预测模型详情
|
||||||
|
- `POST /api/v1/ai/prediction-models/{model_id}/train` - 训练预测模型
|
||||||
|
- `POST /api/v1/ai/prediction-models/predict` - 进行预测
|
||||||
|
- `GET /api/v1/ai/prediction-models/{model_id}/results` - 获取预测结果历史
|
||||||
|
- `POST /api/v1/ai/prediction-results/feedback` - 更新预测反馈
|
||||||
|
|
||||||
|
**测试状态:** ✅ 核心功能测试通过
|
||||||
|
|
||||||
|
运行测试:
|
||||||
|
```bash
|
||||||
|
cd /root/.openclaw/workspace/projects/insightflow/backend
|
||||||
|
python3 test_phase8_task4.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 历史阶段
|
||||||
|
|
||||||
|
### Phase 7 - 插件与集成 (已完成)
|
||||||
|
- 工作流自动化
|
||||||
|
- 多模态支持(视频、图片)
|
||||||
|
- 数据安全与合规
|
||||||
|
- 协作与共享
|
||||||
|
- 报告生成器
|
||||||
|
- 高级搜索与发现
|
||||||
|
- 性能优化与扩展
|
||||||
|
|
||||||
|
### Phase 6 - API 平台 (已完成)
|
||||||
|
- API Key 管理
|
||||||
|
- Swagger 文档
|
||||||
|
- 限流控制
|
||||||
|
|
||||||
|
### Phase 5 - 属性扩展 (已完成)
|
||||||
|
- 属性模板系统
|
||||||
|
- 实体属性管理
|
||||||
|
- 属性变更历史
|
||||||
|
|
||||||
|
### Phase 4 - Agent 助手 (已完成)
|
||||||
|
- RAG 问答
|
||||||
|
- 知识推理
|
||||||
|
- 智能总结
|
||||||
|
|
||||||
|
### Phase 3 - 知识生长 (已完成)
|
||||||
|
- 实体对齐
|
||||||
|
- 多文件融合
|
||||||
|
- 术语表
|
||||||
|
|
||||||
|
### Phase 2 - 编辑功能 (已完成)
|
||||||
|
- 实体编辑
|
||||||
|
- 关系编辑
|
||||||
|
- 转录编辑
|
||||||
|
|
||||||
|
### Phase 1 - 基础功能 (已完成)
|
||||||
|
- 项目管理
|
||||||
|
- 音频转录
|
||||||
|
- 实体提取
|
||||||
|
|
||||||
|
## 待办事项
|
||||||
|
|
||||||
|
### Phase 8 后续任务
|
||||||
|
- [ ] Task 5: 运营与增长工具
|
||||||
|
- [ ] Task 6: 开发者生态
|
||||||
|
- [ ] Task 8: 运维与监控
|
||||||
|
|
||||||
|
### 技术债务
|
||||||
|
- [ ] 完善单元测试覆盖
|
||||||
|
- [ ] API 性能优化
|
||||||
|
- [ ] 文档完善
|
||||||
|
|
||||||
|
## 最近更新
|
||||||
|
|
||||||
|
- 2026-02-26: Phase 8 Task 4 完成 - AI 能力增强
|
||||||
|
- 2026-02-25: Phase 8 Task 1/2/3/7 完成 - 多租户、订阅计费、企业级功能、全球化
|
||||||
|
- 2026-02-24: Phase 7 完成 - 插件与集成
|
||||||
|
- 2026-02-23: Phase 6 完成 - API 平台
|
||||||
Binary file not shown.
BIN
backend/__pycache__/collaboration_manager.cpython-312.pyc
Normal file
BIN
backend/__pycache__/collaboration_manager.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/__pycache__/document_processor.cpython-312.pyc
Normal file
BIN
backend/__pycache__/document_processor.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/__pycache__/entity_aligner.cpython-312.pyc
Normal file
BIN
backend/__pycache__/entity_aligner.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/__pycache__/knowledge_reasoner.cpython-312.pyc
Normal file
BIN
backend/__pycache__/knowledge_reasoner.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/__pycache__/llm_client.cpython-312.pyc
Normal file
BIN
backend/__pycache__/llm_client.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
backend/__pycache__/oss_uploader.cpython-312.pyc
Normal file
BIN
backend/__pycache__/oss_uploader.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
backend/__pycache__/subscription_manager.cpython-312.pyc
Normal file
BIN
backend/__pycache__/subscription_manager.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/__pycache__/tingwu_client.cpython-312.pyc
Normal file
BIN
backend/__pycache__/tingwu_client.cpython-312.pyc
Normal file
Binary file not shown.
1359
backend/ai_manager.py
Normal file
1359
backend/ai_manager.py
Normal file
File diff suppressed because it is too large
Load Diff
1849
backend/enterprise_manager.py
Normal file
1849
backend/enterprise_manager.py
Normal file
File diff suppressed because it is too large
Load Diff
3018
backend/main.py
3018
backend/main.py
File diff suppressed because it is too large
Load Diff
@@ -1060,125 +1060,666 @@ CREATE INDEX IF NOT EXISTS idx_usage_tenant ON tenant_usage(tenant_id);
|
|||||||
CREATE INDEX IF NOT EXISTS idx_usage_date ON tenant_usage(date);
|
CREATE INDEX IF NOT EXISTS idx_usage_date ON tenant_usage(date);
|
||||||
|
|
||||||
-- ============================================
|
-- ============================================
|
||||||
-- Phase 8: Multi-Tenant SaaS Architecture
|
-- Phase 8 Task 2: 订阅与计费系统
|
||||||
-- ============================================
|
-- ============================================
|
||||||
|
|
||||||
-- 租户主表
|
-- 订阅计划表
|
||||||
CREATE TABLE IF NOT EXISTS tenants (
|
CREATE TABLE IF NOT EXISTS subscription_plans (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
slug TEXT UNIQUE NOT NULL, -- URL 友好的唯一标识
|
tier TEXT UNIQUE NOT NULL, -- free/pro/enterprise
|
||||||
description TEXT DEFAULT '',
|
description TEXT,
|
||||||
status TEXT DEFAULT 'active', -- active, suspended, trial, expired, pending
|
price_monthly REAL DEFAULT 0,
|
||||||
plan TEXT DEFAULT 'free', -- free, starter, professional, enterprise
|
price_yearly REAL DEFAULT 0,
|
||||||
max_projects INTEGER DEFAULT 5,
|
currency TEXT DEFAULT 'CNY',
|
||||||
max_members INTEGER DEFAULT 10,
|
features TEXT DEFAULT '[]', -- JSON array
|
||||||
max_storage_gb REAL DEFAULT 1.0,
|
limits TEXT DEFAULT '{}', -- JSON object
|
||||||
max_api_calls_per_day INTEGER DEFAULT 1000,
|
is_active INTEGER DEFAULT 1,
|
||||||
billing_email TEXT DEFAULT '',
|
|
||||||
subscription_start TEXT,
|
|
||||||
subscription_end TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
created_by TEXT DEFAULT '', -- 创建者用户ID
|
metadata TEXT DEFAULT '{}'
|
||||||
db_schema TEXT DEFAULT '', -- 数据库 schema 名称
|
|
||||||
table_prefix TEXT DEFAULT '' -- 表前缀
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 租户域名绑定表
|
-- 订阅表
|
||||||
CREATE TABLE IF NOT EXISTS tenant_domains (
|
CREATE TABLE IF NOT EXISTS subscriptions (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
tenant_id TEXT NOT NULL,
|
tenant_id TEXT NOT NULL,
|
||||||
domain TEXT NOT NULL, -- 自定义域名
|
plan_id TEXT NOT NULL,
|
||||||
status TEXT DEFAULT 'pending', -- pending, verified, active, failed, expired
|
status TEXT DEFAULT 'pending', -- active/cancelled/expired/past_due/trial/pending
|
||||||
verification_record TEXT DEFAULT '', -- DNS TXT 记录值
|
current_period_start TIMESTAMP,
|
||||||
verification_expires_at TEXT,
|
current_period_end TIMESTAMP,
|
||||||
ssl_enabled INTEGER DEFAULT 0,
|
cancel_at_period_end INTEGER DEFAULT 0,
|
||||||
ssl_cert_path TEXT,
|
canceled_at TIMESTAMP,
|
||||||
ssl_key_path TEXT,
|
trial_start TIMESTAMP,
|
||||||
ssl_expires_at TEXT,
|
trial_end TIMESTAMP,
|
||||||
|
payment_provider TEXT, -- stripe/alipay/wechat/bank_transfer
|
||||||
|
provider_subscription_id TEXT,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
verified_at TEXT,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
UNIQUE(tenant_id, domain),
|
metadata TEXT DEFAULT '{}',
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (plan_id) REFERENCES subscription_plans(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 用量记录表
|
||||||
|
CREATE TABLE IF NOT EXISTS usage_records (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
resource_type TEXT NOT NULL, -- transcription/storage/api_call/export
|
||||||
|
quantity REAL DEFAULT 0,
|
||||||
|
unit TEXT NOT NULL, -- minutes/mb/count/page
|
||||||
|
recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
cost REAL DEFAULT 0,
|
||||||
|
description TEXT,
|
||||||
|
metadata TEXT DEFAULT '{}',
|
||||||
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 租户品牌配置表(白标)
|
-- 支付记录表
|
||||||
CREATE TABLE IF NOT EXISTS tenant_branding (
|
CREATE TABLE IF NOT EXISTS payments (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
tenant_id TEXT UNIQUE NOT NULL,
|
tenant_id TEXT NOT NULL,
|
||||||
logo_url TEXT,
|
subscription_id TEXT,
|
||||||
logo_dark_url TEXT, -- 深色模式 Logo
|
invoice_id TEXT,
|
||||||
favicon_url TEXT,
|
amount REAL NOT NULL,
|
||||||
primary_color TEXT DEFAULT '#3B82F6',
|
currency TEXT DEFAULT 'CNY',
|
||||||
secondary_color TEXT DEFAULT '#10B981',
|
provider TEXT NOT NULL, -- stripe/alipay/wechat/bank_transfer
|
||||||
accent_color TEXT DEFAULT '#F59E0B',
|
provider_payment_id TEXT,
|
||||||
background_color TEXT DEFAULT '#FFFFFF',
|
status TEXT DEFAULT 'pending', -- pending/processing/completed/failed/refunded/partial_refunded
|
||||||
text_color TEXT DEFAULT '#1F2937',
|
payment_method TEXT,
|
||||||
dark_primary_color TEXT DEFAULT '#60A5FA',
|
payment_details TEXT DEFAULT '{}', -- JSON
|
||||||
dark_background_color TEXT DEFAULT '#111827',
|
paid_at TIMESTAMP,
|
||||||
dark_text_color TEXT DEFAULT '#F9FAFB',
|
failed_at TIMESTAMP,
|
||||||
font_family TEXT DEFAULT 'Inter, system-ui, sans-serif',
|
failure_reason TEXT,
|
||||||
heading_font_family TEXT,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
custom_css TEXT DEFAULT '',
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
custom_js TEXT DEFAULT '',
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
app_name TEXT DEFAULT 'InsightFlow',
|
FOREIGN KEY (subscription_id) REFERENCES subscriptions(id) ON DELETE SET NULL,
|
||||||
login_page_title TEXT DEFAULT '登录到 InsightFlow',
|
FOREIGN KEY (invoice_id) REFERENCES invoices(id) ON DELETE SET NULL
|
||||||
login_page_description TEXT DEFAULT '',
|
);
|
||||||
footer_text TEXT DEFAULT '© 2024 InsightFlow',
|
|
||||||
|
-- 发票表
|
||||||
|
CREATE TABLE IF NOT EXISTS invoices (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
subscription_id TEXT,
|
||||||
|
invoice_number TEXT UNIQUE NOT NULL,
|
||||||
|
status TEXT DEFAULT 'draft', -- draft/issued/paid/overdue/void/credit_note
|
||||||
|
amount_due REAL DEFAULT 0,
|
||||||
|
amount_paid REAL DEFAULT 0,
|
||||||
|
currency TEXT DEFAULT 'CNY',
|
||||||
|
period_start TIMESTAMP,
|
||||||
|
period_end TIMESTAMP,
|
||||||
|
description TEXT,
|
||||||
|
line_items TEXT DEFAULT '[]', -- JSON array
|
||||||
|
due_date TIMESTAMP,
|
||||||
|
paid_at TIMESTAMP,
|
||||||
|
voided_at TIMESTAMP,
|
||||||
|
void_reason TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (subscription_id) REFERENCES subscriptions(id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 退款表
|
||||||
|
CREATE TABLE IF NOT EXISTS refunds (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
payment_id TEXT NOT NULL,
|
||||||
|
invoice_id TEXT,
|
||||||
|
amount REAL NOT NULL,
|
||||||
|
currency TEXT DEFAULT 'CNY',
|
||||||
|
reason TEXT,
|
||||||
|
status TEXT DEFAULT 'pending', -- pending/approved/rejected/completed/failed
|
||||||
|
requested_by TEXT NOT NULL,
|
||||||
|
requested_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
approved_by TEXT,
|
||||||
|
approved_at TIMESTAMP,
|
||||||
|
completed_at TIMESTAMP,
|
||||||
|
provider_refund_id TEXT,
|
||||||
|
metadata TEXT DEFAULT '{}',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (payment_id) REFERENCES payments(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (invoice_id) REFERENCES invoices(id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 账单历史表
|
||||||
|
CREATE TABLE IF NOT EXISTS billing_history (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
type TEXT NOT NULL, -- subscription/usage/payment/refund
|
||||||
|
amount REAL NOT NULL,
|
||||||
|
currency TEXT DEFAULT 'CNY',
|
||||||
|
description TEXT,
|
||||||
|
reference_id TEXT, -- 关联的订阅/支付/退款ID
|
||||||
|
balance_after REAL DEFAULT 0,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
metadata TEXT DEFAULT '{}',
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 订阅相关索引
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_subscriptions_tenant ON subscriptions(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_subscriptions_plan ON subscriptions(plan_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_usage_records_tenant ON usage_records(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_usage_records_type ON usage_records(resource_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_usage_records_recorded ON usage_records(recorded_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_payments_tenant ON payments(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_payments_status ON payments(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_payments_provider ON payments(provider);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_invoices_tenant ON invoices(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_invoices_status ON invoices(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_invoices_number ON invoices(invoice_number);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_refunds_tenant ON refunds(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_refunds_status ON refunds(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_refunds_payment ON refunds(payment_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_billing_history_tenant ON billing_history(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_billing_history_created ON billing_history(created_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_billing_history_type ON billing_history(type);
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- Phase 8 Task 3: 企业级功能
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- SSO 配置表
|
||||||
|
CREATE TABLE IF NOT EXISTS sso_configs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
provider TEXT NOT NULL, -- wechat_work/dingtalk/feishu/okta/azure_ad/google/custom_saml
|
||||||
|
status TEXT DEFAULT 'disabled', -- disabled/pending/active/error
|
||||||
|
entity_id TEXT,
|
||||||
|
sso_url TEXT,
|
||||||
|
slo_url TEXT,
|
||||||
|
certificate TEXT, -- X.509 证书
|
||||||
|
metadata_url TEXT,
|
||||||
|
metadata_xml TEXT,
|
||||||
|
client_id TEXT,
|
||||||
|
client_secret TEXT,
|
||||||
|
authorization_url TEXT,
|
||||||
|
token_url TEXT,
|
||||||
|
userinfo_url TEXT,
|
||||||
|
scopes TEXT DEFAULT '["openid", "email", "profile"]',
|
||||||
|
attribute_mapping TEXT DEFAULT '{}', -- JSON: 属性映射
|
||||||
|
auto_provision INTEGER DEFAULT 1, -- 自动创建用户
|
||||||
|
default_role TEXT DEFAULT 'member',
|
||||||
|
domain_restriction TEXT DEFAULT '[]', -- JSON: 允许的邮箱域名
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
last_tested_at TIMESTAMP,
|
||||||
|
last_error TEXT,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- SAML 认证请求表
|
||||||
|
CREATE TABLE IF NOT EXISTS saml_auth_requests (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
sso_config_id TEXT NOT NULL,
|
||||||
|
request_id TEXT NOT NULL UNIQUE,
|
||||||
|
relay_state TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
expires_at TIMESTAMP NOT NULL,
|
||||||
|
used INTEGER DEFAULT 0,
|
||||||
|
used_at TIMESTAMP,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (sso_config_id) REFERENCES sso_configs(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- SAML 认证响应表
|
||||||
|
CREATE TABLE IF NOT EXISTS saml_auth_responses (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
request_id TEXT NOT NULL,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
user_id TEXT,
|
||||||
|
email TEXT,
|
||||||
|
name TEXT,
|
||||||
|
attributes TEXT DEFAULT '{}', -- JSON: SAML 属性
|
||||||
|
session_index TEXT,
|
||||||
|
processed INTEGER DEFAULT 0,
|
||||||
|
processed_at TIMESTAMP,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (request_id) REFERENCES saml_auth_requests(request_id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- SCIM 配置表
|
||||||
|
CREATE TABLE IF NOT EXISTS scim_configs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
provider TEXT NOT NULL,
|
||||||
|
status TEXT DEFAULT 'disabled',
|
||||||
|
scim_base_url TEXT,
|
||||||
|
scim_token TEXT,
|
||||||
|
sync_interval_minutes INTEGER DEFAULT 60,
|
||||||
|
last_sync_at TIMESTAMP,
|
||||||
|
last_sync_status TEXT,
|
||||||
|
last_sync_error TEXT,
|
||||||
|
last_sync_users_count INTEGER DEFAULT 0,
|
||||||
|
attribute_mapping TEXT DEFAULT '{}',
|
||||||
|
sync_rules TEXT DEFAULT '{}',
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 租户成员表
|
-- SCIM 用户表
|
||||||
CREATE TABLE IF NOT EXISTS tenant_members (
|
CREATE TABLE IF NOT EXISTS scim_users (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
tenant_id TEXT NOT NULL,
|
tenant_id TEXT NOT NULL,
|
||||||
user_id TEXT NOT NULL,
|
external_id TEXT NOT NULL,
|
||||||
|
user_name TEXT NOT NULL,
|
||||||
email TEXT NOT NULL,
|
email TEXT NOT NULL,
|
||||||
name TEXT DEFAULT '',
|
display_name TEXT,
|
||||||
role TEXT DEFAULT 'viewer', -- owner, admin, editor, viewer, guest
|
given_name TEXT,
|
||||||
status TEXT DEFAULT 'invited', -- active, invited, suspended, removed
|
family_name TEXT,
|
||||||
invited_by TEXT,
|
active INTEGER DEFAULT 1,
|
||||||
invited_at TEXT,
|
groups TEXT DEFAULT '[]',
|
||||||
invitation_token TEXT,
|
raw_data TEXT DEFAULT '{}',
|
||||||
invitation_expires_at TEXT,
|
synced_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
joined_at TEXT,
|
|
||||||
last_active_at TEXT,
|
|
||||||
custom_permissions TEXT DEFAULT '[]', -- JSON 数组
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
UNIQUE(tenant_id, user_id),
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
UNIQUE(tenant_id, external_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 审计日志导出表
|
||||||
|
CREATE TABLE IF NOT EXISTS audit_log_exports (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
export_format TEXT NOT NULL, -- json/csv/pdf/xlsx
|
||||||
|
start_date TIMESTAMP NOT NULL,
|
||||||
|
end_date TIMESTAMP NOT NULL,
|
||||||
|
filters TEXT DEFAULT '{}',
|
||||||
|
compliance_standard TEXT, -- soc2/iso27001/gdpr/hipaa/pci_dss
|
||||||
|
status TEXT DEFAULT 'pending', -- pending/processing/completed/failed
|
||||||
|
file_path TEXT,
|
||||||
|
file_size INTEGER,
|
||||||
|
record_count INTEGER,
|
||||||
|
checksum TEXT,
|
||||||
|
downloaded_by TEXT,
|
||||||
|
downloaded_at TIMESTAMP,
|
||||||
|
expires_at TIMESTAMP,
|
||||||
|
created_by TEXT NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
completed_at TIMESTAMP,
|
||||||
|
error_message TEXT,
|
||||||
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 租户角色表
|
-- 数据保留策略表
|
||||||
CREATE TABLE IF NOT EXISTS tenant_roles (
|
CREATE TABLE IF NOT EXISTS data_retention_policies (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
tenant_id TEXT NOT NULL,
|
tenant_id TEXT NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
description TEXT DEFAULT '',
|
description TEXT,
|
||||||
permissions TEXT DEFAULT '[]', -- JSON 数组
|
resource_type TEXT NOT NULL, -- project/transcript/entity/audit_log/user_data
|
||||||
is_system INTEGER DEFAULT 0, -- 1=系统预设, 0=自定义
|
retention_days INTEGER NOT NULL,
|
||||||
|
action TEXT NOT NULL, -- archive/delete/anonymize
|
||||||
|
conditions TEXT DEFAULT '{}',
|
||||||
|
auto_execute INTEGER DEFAULT 0,
|
||||||
|
execute_at TEXT, -- cron 表达式
|
||||||
|
notify_before_days INTEGER DEFAULT 7,
|
||||||
|
archive_location TEXT,
|
||||||
|
archive_encryption INTEGER DEFAULT 1,
|
||||||
|
is_active INTEGER DEFAULT 1,
|
||||||
|
last_executed_at TIMESTAMP,
|
||||||
|
last_execution_result TEXT,
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
-- 租户相关索引
|
-- 数据保留任务表
|
||||||
CREATE INDEX IF NOT EXISTS idx_tenants_slug ON tenants(slug);
|
CREATE TABLE IF NOT EXISTS data_retention_jobs (
|
||||||
CREATE INDEX IF NOT EXISTS idx_tenants_status ON tenants(status);
|
id TEXT PRIMARY KEY,
|
||||||
CREATE INDEX IF NOT EXISTS idx_domains_tenant ON tenant_domains(tenant_id);
|
policy_id TEXT NOT NULL,
|
||||||
CREATE INDEX IF NOT EXISTS idx_domains_domain ON tenant_domains(domain);
|
tenant_id TEXT NOT NULL,
|
||||||
CREATE INDEX IF NOT EXISTS idx_domains_status ON tenant_domains(status);
|
status TEXT DEFAULT 'pending', -- pending/running/completed/failed
|
||||||
CREATE INDEX IF NOT EXISTS idx_members_tenant ON tenant_members(tenant_id);
|
started_at TIMESTAMP,
|
||||||
CREATE INDEX IF NOT EXISTS idx_members_user ON tenant_members(user_id);
|
completed_at TIMESTAMP,
|
||||||
CREATE INDEX IF NOT EXISTS idx_members_role ON tenant_members(role);
|
affected_records INTEGER DEFAULT 0,
|
||||||
CREATE INDEX IF NOT EXISTS idx_members_status ON tenant_members(status);
|
archived_records INTEGER DEFAULT 0,
|
||||||
CREATE INDEX IF NOT EXISTS idx_members_token ON tenant_members(invitation_token);
|
deleted_records INTEGER DEFAULT 0,
|
||||||
CREATE INDEX IF NOT EXISTS idx_roles_tenant ON tenant_roles(tenant_id);
|
error_count INTEGER DEFAULT 0,
|
||||||
|
details TEXT DEFAULT '{}',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (policy_id) REFERENCES data_retention_policies(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
-- 更新项目表,添加租户关联(可选,支持租户隔离)
|
-- 企业级功能相关索引
|
||||||
ALTER TABLE projects ADD COLUMN tenant_id TEXT;
|
CREATE INDEX IF NOT EXISTS idx_sso_tenant ON sso_configs(tenant_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_projects_tenant ON projects(tenant_id);
|
CREATE INDEX IF NOT EXISTS idx_sso_provider ON sso_configs(provider);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_saml_requests_config ON saml_auth_requests(sso_config_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_saml_requests_expires ON saml_auth_requests(expires_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_saml_responses_request ON saml_auth_responses(request_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_scim_config_tenant ON scim_configs(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_scim_users_tenant ON scim_users(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_scim_users_external ON scim_users(external_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_export_tenant ON audit_log_exports(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_audit_export_status ON audit_log_exports(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_retention_tenant ON data_retention_policies(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_retention_type ON data_retention_policies(resource_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_retention_jobs_policy ON data_retention_jobs(policy_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_retention_jobs_status ON data_retention_jobs(status);
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- Phase 8 Task 7: 全球化与本地化
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- 翻译表
|
||||||
|
CREATE TABLE IF NOT EXISTS translations (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
language TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
namespace TEXT DEFAULT 'common',
|
||||||
|
context TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
is_reviewed INTEGER DEFAULT 0,
|
||||||
|
reviewed_by TEXT,
|
||||||
|
reviewed_at TIMESTAMP,
|
||||||
|
UNIQUE(key, language, namespace)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 语言配置表
|
||||||
|
CREATE TABLE IF NOT EXISTS language_configs (
|
||||||
|
code TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
name_local TEXT NOT NULL,
|
||||||
|
is_rtl INTEGER DEFAULT 0,
|
||||||
|
is_active INTEGER DEFAULT 1,
|
||||||
|
is_default INTEGER DEFAULT 0,
|
||||||
|
fallback_language TEXT,
|
||||||
|
date_format TEXT,
|
||||||
|
time_format TEXT,
|
||||||
|
datetime_format TEXT,
|
||||||
|
number_format TEXT,
|
||||||
|
currency_format TEXT,
|
||||||
|
first_day_of_week INTEGER DEFAULT 1,
|
||||||
|
calendar_type TEXT DEFAULT 'gregorian'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 数据中心表
|
||||||
|
CREATE TABLE IF NOT EXISTS data_centers (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
region_code TEXT NOT NULL UNIQUE,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
location TEXT NOT NULL,
|
||||||
|
endpoint TEXT NOT NULL,
|
||||||
|
status TEXT DEFAULT 'active',
|
||||||
|
priority INTEGER DEFAULT 1,
|
||||||
|
supported_regions TEXT DEFAULT '[]',
|
||||||
|
capabilities TEXT DEFAULT '{}',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 租户数据中心映射表
|
||||||
|
CREATE TABLE IF NOT EXISTS tenant_data_center_mappings (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL UNIQUE,
|
||||||
|
primary_dc_id TEXT NOT NULL,
|
||||||
|
secondary_dc_id TEXT,
|
||||||
|
region_code TEXT NOT NULL,
|
||||||
|
data_residency TEXT DEFAULT 'regional',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (primary_dc_id) REFERENCES data_centers(id),
|
||||||
|
FOREIGN KEY (secondary_dc_id) REFERENCES data_centers(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 本地化支付方式表
|
||||||
|
CREATE TABLE IF NOT EXISTS localized_payment_methods (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
provider TEXT NOT NULL UNIQUE,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
name_local TEXT DEFAULT '{}',
|
||||||
|
supported_countries TEXT DEFAULT '[]',
|
||||||
|
supported_currencies TEXT DEFAULT '[]',
|
||||||
|
is_active INTEGER DEFAULT 1,
|
||||||
|
config TEXT DEFAULT '{}',
|
||||||
|
icon_url TEXT,
|
||||||
|
display_order INTEGER DEFAULT 0,
|
||||||
|
min_amount REAL,
|
||||||
|
max_amount REAL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 国家配置表
|
||||||
|
CREATE TABLE IF NOT EXISTS country_configs (
|
||||||
|
code TEXT PRIMARY KEY,
|
||||||
|
code3 TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
name_local TEXT DEFAULT '{}',
|
||||||
|
region TEXT NOT NULL,
|
||||||
|
default_language TEXT NOT NULL,
|
||||||
|
supported_languages TEXT DEFAULT '[]',
|
||||||
|
default_currency TEXT NOT NULL,
|
||||||
|
supported_currencies TEXT DEFAULT '[]',
|
||||||
|
timezone TEXT NOT NULL,
|
||||||
|
calendar_type TEXT DEFAULT 'gregorian',
|
||||||
|
date_format TEXT,
|
||||||
|
time_format TEXT,
|
||||||
|
number_format TEXT,
|
||||||
|
address_format TEXT,
|
||||||
|
phone_format TEXT,
|
||||||
|
vat_rate REAL,
|
||||||
|
is_active INTEGER DEFAULT 1
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 时区配置表
|
||||||
|
CREATE TABLE IF NOT EXISTS timezone_configs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
timezone TEXT NOT NULL UNIQUE,
|
||||||
|
utc_offset TEXT NOT NULL,
|
||||||
|
dst_offset TEXT,
|
||||||
|
country_code TEXT NOT NULL,
|
||||||
|
region TEXT NOT NULL,
|
||||||
|
is_active INTEGER DEFAULT 1
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 货币配置表
|
||||||
|
CREATE TABLE IF NOT EXISTS currency_configs (
|
||||||
|
code TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
name_local TEXT DEFAULT '{}',
|
||||||
|
symbol TEXT NOT NULL,
|
||||||
|
decimal_places INTEGER DEFAULT 2,
|
||||||
|
decimal_separator TEXT DEFAULT '.',
|
||||||
|
thousands_separator TEXT DEFAULT ',',
|
||||||
|
is_active INTEGER DEFAULT 1
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 租户本地化设置表
|
||||||
|
CREATE TABLE IF NOT EXISTS localization_settings (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL UNIQUE,
|
||||||
|
default_language TEXT DEFAULT 'en',
|
||||||
|
supported_languages TEXT DEFAULT '["en"]',
|
||||||
|
default_currency TEXT DEFAULT 'USD',
|
||||||
|
supported_currencies TEXT DEFAULT '["USD"]',
|
||||||
|
default_timezone TEXT DEFAULT 'UTC',
|
||||||
|
default_date_format TEXT,
|
||||||
|
default_time_format TEXT,
|
||||||
|
default_number_format TEXT,
|
||||||
|
calendar_type TEXT DEFAULT 'gregorian',
|
||||||
|
first_day_of_week INTEGER DEFAULT 1,
|
||||||
|
region_code TEXT DEFAULT 'global',
|
||||||
|
data_residency TEXT DEFAULT 'regional',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 本地化相关索引
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_translations_key ON translations(key);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_translations_lang ON translations(language);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_translations_ns ON translations(namespace);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dc_region ON data_centers(region_code);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dc_status ON data_centers(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_tenant_dc ON tenant_data_center_mappings(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_payment_provider ON localized_payment_methods(provider);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_payment_active ON localized_payment_methods(is_active);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_country_region ON country_configs(region);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_tz_country ON timezone_configs(country_code);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_locale_settings_tenant ON localization_settings(tenant_id);
|
||||||
|
|
||||||
|
-- ============================================
|
||||||
|
-- Phase 8 Task 4: AI 能力增强
|
||||||
|
-- ============================================
|
||||||
|
|
||||||
|
-- 自定义模型表
|
||||||
|
CREATE TABLE IF NOT EXISTS custom_models (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
model_type TEXT NOT NULL, -- custom_ner, multimodal, summarization, prediction
|
||||||
|
status TEXT DEFAULT 'pending', -- pending, training, ready, failed, archived
|
||||||
|
training_data TEXT DEFAULT '{}',
|
||||||
|
hyperparameters TEXT DEFAULT '{}',
|
||||||
|
metrics TEXT DEFAULT '{}',
|
||||||
|
model_path TEXT,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
trained_at TIMESTAMP,
|
||||||
|
created_by TEXT NOT NULL,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 训练样本表
|
||||||
|
CREATE TABLE IF NOT EXISTS training_samples (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
model_id TEXT NOT NULL,
|
||||||
|
text TEXT NOT NULL,
|
||||||
|
entities TEXT DEFAULT '[]', -- JSON: [{"start": 0, "end": 5, "label": "PERSON", "text": "..."}]
|
||||||
|
metadata TEXT DEFAULT '{}',
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (model_id) REFERENCES custom_models(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 多模态分析表
|
||||||
|
CREATE TABLE IF NOT EXISTS multimodal_analyses (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
project_id TEXT NOT NULL,
|
||||||
|
provider TEXT NOT NULL, -- gpt-4-vision, claude-3, gemini-pro-vision, kimi-vl
|
||||||
|
input_type TEXT NOT NULL, -- image, video, audio, mixed
|
||||||
|
input_urls TEXT DEFAULT '[]',
|
||||||
|
prompt TEXT NOT NULL,
|
||||||
|
result TEXT DEFAULT '{}',
|
||||||
|
tokens_used INTEGER DEFAULT 0,
|
||||||
|
cost REAL DEFAULT 0,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 知识图谱 RAG 配置表
|
||||||
|
CREATE TABLE IF NOT EXISTS kg_rag_configs (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
project_id TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
kg_config TEXT DEFAULT '{}', -- 知识图谱配置
|
||||||
|
retrieval_config TEXT DEFAULT '{}', -- 检索配置
|
||||||
|
generation_config TEXT DEFAULT '{}', -- 生成配置
|
||||||
|
is_active INTEGER DEFAULT 1,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- RAG 查询记录表
|
||||||
|
CREATE TABLE IF NOT EXISTS rag_queries (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
rag_id TEXT NOT NULL,
|
||||||
|
query TEXT NOT NULL,
|
||||||
|
context TEXT DEFAULT '{}',
|
||||||
|
answer TEXT NOT NULL,
|
||||||
|
sources TEXT DEFAULT '[]',
|
||||||
|
confidence REAL DEFAULT 0,
|
||||||
|
tokens_used INTEGER DEFAULT 0,
|
||||||
|
latency_ms INTEGER DEFAULT 0,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (rag_id) REFERENCES kg_rag_configs(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 智能摘要表
|
||||||
|
CREATE TABLE IF NOT EXISTS smart_summaries (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
project_id TEXT NOT NULL,
|
||||||
|
source_type TEXT NOT NULL, -- transcript, entity, project
|
||||||
|
source_id TEXT NOT NULL,
|
||||||
|
summary_type TEXT NOT NULL, -- extractive, abstractive, key_points, timeline
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
key_points TEXT DEFAULT '[]',
|
||||||
|
entities_mentioned TEXT DEFAULT '[]',
|
||||||
|
confidence REAL DEFAULT 0,
|
||||||
|
tokens_used INTEGER DEFAULT 0,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 预测模型表
|
||||||
|
CREATE TABLE IF NOT EXISTS prediction_models (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
tenant_id TEXT NOT NULL,
|
||||||
|
project_id TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
prediction_type TEXT NOT NULL, -- trend, anomaly, entity_growth, relation_evolution
|
||||||
|
target_entity_type TEXT,
|
||||||
|
features TEXT DEFAULT '[]',
|
||||||
|
model_config TEXT DEFAULT '{}',
|
||||||
|
accuracy REAL,
|
||||||
|
last_trained_at TIMESTAMP,
|
||||||
|
prediction_count INTEGER DEFAULT 0,
|
||||||
|
is_active INTEGER DEFAULT 1,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 预测结果表
|
||||||
|
CREATE TABLE IF NOT EXISTS prediction_results (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
model_id TEXT NOT NULL,
|
||||||
|
prediction_type TEXT NOT NULL,
|
||||||
|
target_id TEXT,
|
||||||
|
prediction_data TEXT DEFAULT '{}',
|
||||||
|
confidence REAL DEFAULT 0,
|
||||||
|
explanation TEXT,
|
||||||
|
actual_value TEXT,
|
||||||
|
is_correct INTEGER,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (model_id) REFERENCES prediction_models(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AI 能力增强相关索引
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_custom_models_tenant ON custom_models(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_custom_models_type ON custom_models(model_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_custom_models_status ON custom_models(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_training_samples_model ON training_samples(model_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_multimodal_tenant ON multimodal_analyses(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_multimodal_project ON multimodal_analyses(project_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_kg_rag_tenant ON kg_rag_configs(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_kg_rag_project ON kg_rag_configs(project_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_rag_queries_rag ON rag_queries(rag_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_smart_summaries_tenant ON smart_summaries(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_smart_summaries_project ON smart_summaries(project_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_prediction_models_tenant ON prediction_models(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_prediction_models_project ON prediction_models(project_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_prediction_results_model ON prediction_results(model_id);
|
||||||
|
|||||||
1840
backend/subscription_manager.py
Normal file
1840
backend/subscription_manager.py
Normal file
File diff suppressed because it is too large
Load Diff
246
backend/test_phase8_task2.py
Normal file
246
backend/test_phase8_task2.py
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
InsightFlow Phase 8 Task 2 测试脚本 - 订阅与计费系统
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
from subscription_manager import (
|
||||||
|
get_subscription_manager, SubscriptionManager,
|
||||||
|
SubscriptionStatus, PaymentProvider, PaymentStatus, InvoiceStatus, RefundStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_subscription_manager():
|
||||||
|
"""测试订阅管理器"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("InsightFlow Phase 8 Task 2 - 订阅与计费系统测试")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# 使用临时文件数据库进行测试
|
||||||
|
db_path = tempfile.mktemp(suffix='.db')
|
||||||
|
|
||||||
|
try:
|
||||||
|
manager = SubscriptionManager(db_path=db_path)
|
||||||
|
|
||||||
|
print("\n1. 测试订阅计划管理")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
# 获取默认计划
|
||||||
|
plans = manager.list_plans()
|
||||||
|
print(f"✓ 默认计划数量: {len(plans)}")
|
||||||
|
for plan in plans:
|
||||||
|
print(f" - {plan.name} ({plan.tier}): ¥{plan.price_monthly}/月")
|
||||||
|
|
||||||
|
# 通过 tier 获取计划
|
||||||
|
free_plan = manager.get_plan_by_tier("free")
|
||||||
|
pro_plan = manager.get_plan_by_tier("pro")
|
||||||
|
enterprise_plan = manager.get_plan_by_tier("enterprise")
|
||||||
|
|
||||||
|
assert free_plan is not None, "Free 计划应该存在"
|
||||||
|
assert pro_plan is not None, "Pro 计划应该存在"
|
||||||
|
assert enterprise_plan is not None, "Enterprise 计划应该存在"
|
||||||
|
|
||||||
|
print(f"✓ Free 计划: {free_plan.name}")
|
||||||
|
print(f"✓ Pro 计划: {pro_plan.name}")
|
||||||
|
print(f"✓ Enterprise 计划: {enterprise_plan.name}")
|
||||||
|
|
||||||
|
print("\n2. 测试订阅管理")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
tenant_id = "test-tenant-001"
|
||||||
|
|
||||||
|
# 创建订阅
|
||||||
|
subscription = manager.create_subscription(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
plan_id=pro_plan.id,
|
||||||
|
payment_provider=PaymentProvider.STRIPE.value,
|
||||||
|
trial_days=14
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"✓ 创建订阅: {subscription.id}")
|
||||||
|
print(f" - 状态: {subscription.status}")
|
||||||
|
print(f" - 计划: {pro_plan.name}")
|
||||||
|
print(f" - 试用开始: {subscription.trial_start}")
|
||||||
|
print(f" - 试用结束: {subscription.trial_end}")
|
||||||
|
|
||||||
|
# 获取租户订阅
|
||||||
|
tenant_sub = manager.get_tenant_subscription(tenant_id)
|
||||||
|
assert tenant_sub is not None, "应该能获取到租户订阅"
|
||||||
|
print(f"✓ 获取租户订阅: {tenant_sub.id}")
|
||||||
|
|
||||||
|
print("\n3. 测试用量记录")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
# 记录转录用量
|
||||||
|
usage1 = manager.record_usage(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
resource_type="transcription",
|
||||||
|
quantity=120,
|
||||||
|
unit="minute",
|
||||||
|
description="会议转录"
|
||||||
|
)
|
||||||
|
print(f"✓ 记录转录用量: {usage1.quantity} {usage1.unit}, 费用: ¥{usage1.cost:.2f}")
|
||||||
|
|
||||||
|
# 记录存储用量
|
||||||
|
usage2 = manager.record_usage(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
resource_type="storage",
|
||||||
|
quantity=2.5,
|
||||||
|
unit="gb",
|
||||||
|
description="文件存储"
|
||||||
|
)
|
||||||
|
print(f"✓ 记录存储用量: {usage2.quantity} {usage2.unit}, 费用: ¥{usage2.cost:.2f}")
|
||||||
|
|
||||||
|
# 获取用量汇总
|
||||||
|
summary = manager.get_usage_summary(tenant_id)
|
||||||
|
print(f"✓ 用量汇总:")
|
||||||
|
print(f" - 总费用: ¥{summary['total_cost']:.2f}")
|
||||||
|
for resource, data in summary['breakdown'].items():
|
||||||
|
print(f" - {resource}: {data['quantity']} (¥{data['cost']:.2f})")
|
||||||
|
|
||||||
|
print("\n4. 测试支付管理")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
# 创建支付
|
||||||
|
payment = manager.create_payment(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
amount=99.0,
|
||||||
|
currency="CNY",
|
||||||
|
provider=PaymentProvider.ALIPAY.value,
|
||||||
|
payment_method="qrcode"
|
||||||
|
)
|
||||||
|
print(f"✓ 创建支付: {payment.id}")
|
||||||
|
print(f" - 金额: ¥{payment.amount}")
|
||||||
|
print(f" - 提供商: {payment.provider}")
|
||||||
|
print(f" - 状态: {payment.status}")
|
||||||
|
|
||||||
|
# 确认支付
|
||||||
|
confirmed = manager.confirm_payment(payment.id, "alipay_123456")
|
||||||
|
print(f"✓ 确认支付完成: {confirmed.status}")
|
||||||
|
|
||||||
|
# 列出支付记录
|
||||||
|
payments = manager.list_payments(tenant_id)
|
||||||
|
print(f"✓ 支付记录数量: {len(payments)}")
|
||||||
|
|
||||||
|
print("\n5. 测试发票管理")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
# 列出发票
|
||||||
|
invoices = manager.list_invoices(tenant_id)
|
||||||
|
print(f"✓ 发票数量: {len(invoices)}")
|
||||||
|
|
||||||
|
if invoices:
|
||||||
|
invoice = invoices[0]
|
||||||
|
print(f" - 发票号: {invoice.invoice_number}")
|
||||||
|
print(f" - 金额: ¥{invoice.amount_due}")
|
||||||
|
print(f" - 状态: {invoice.status}")
|
||||||
|
|
||||||
|
print("\n6. 测试退款管理")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
# 申请退款
|
||||||
|
refund = manager.request_refund(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
payment_id=payment.id,
|
||||||
|
amount=50.0,
|
||||||
|
reason="服务不满意",
|
||||||
|
requested_by="user_001"
|
||||||
|
)
|
||||||
|
print(f"✓ 申请退款: {refund.id}")
|
||||||
|
print(f" - 金额: ¥{refund.amount}")
|
||||||
|
print(f" - 原因: {refund.reason}")
|
||||||
|
print(f" - 状态: {refund.status}")
|
||||||
|
|
||||||
|
# 批准退款
|
||||||
|
approved = manager.approve_refund(refund.id, "admin_001")
|
||||||
|
print(f"✓ 批准退款: {approved.status}")
|
||||||
|
|
||||||
|
# 完成退款
|
||||||
|
completed = manager.complete_refund(refund.id, "refund_123456")
|
||||||
|
print(f"✓ 完成退款: {completed.status}")
|
||||||
|
|
||||||
|
# 列出退款记录
|
||||||
|
refunds = manager.list_refunds(tenant_id)
|
||||||
|
print(f"✓ 退款记录数量: {len(refunds)}")
|
||||||
|
|
||||||
|
print("\n7. 测试账单历史")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
history = manager.get_billing_history(tenant_id)
|
||||||
|
print(f"✓ 账单历史记录数量: {len(history)}")
|
||||||
|
for h in history:
|
||||||
|
print(f" - [{h.type}] {h.description}: ¥{h.amount}")
|
||||||
|
|
||||||
|
print("\n8. 测试支付提供商集成")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
# Stripe Checkout
|
||||||
|
stripe_session = manager.create_stripe_checkout_session(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
plan_id=enterprise_plan.id,
|
||||||
|
success_url="https://example.com/success",
|
||||||
|
cancel_url="https://example.com/cancel"
|
||||||
|
)
|
||||||
|
print(f"✓ Stripe Checkout 会话: {stripe_session['session_id']}")
|
||||||
|
|
||||||
|
# 支付宝订单
|
||||||
|
alipay_order = manager.create_alipay_order(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
plan_id=pro_plan.id
|
||||||
|
)
|
||||||
|
print(f"✓ 支付宝订单: {alipay_order['order_id']}")
|
||||||
|
|
||||||
|
# 微信支付订单
|
||||||
|
wechat_order = manager.create_wechat_order(
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
plan_id=pro_plan.id
|
||||||
|
)
|
||||||
|
print(f"✓ 微信支付订单: {wechat_order['order_id']}")
|
||||||
|
|
||||||
|
# Webhook 处理
|
||||||
|
webhook_result = manager.handle_webhook("stripe", {
|
||||||
|
"event_type": "checkout.session.completed",
|
||||||
|
"data": {"object": {"id": "cs_test"}}
|
||||||
|
})
|
||||||
|
print(f"✓ Webhook 处理: {webhook_result}")
|
||||||
|
|
||||||
|
print("\n9. 测试订阅变更")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
|
# 更改计划
|
||||||
|
changed = manager.change_plan(
|
||||||
|
subscription_id=subscription.id,
|
||||||
|
new_plan_id=enterprise_plan.id
|
||||||
|
)
|
||||||
|
print(f"✓ 更改计划: {changed.plan_id} (Enterprise)")
|
||||||
|
|
||||||
|
# 取消订阅
|
||||||
|
cancelled = manager.cancel_subscription(
|
||||||
|
subscription_id=subscription.id,
|
||||||
|
at_period_end=True
|
||||||
|
)
|
||||||
|
print(f"✓ 取消订阅: {cancelled.status}")
|
||||||
|
print(f" - 周期结束时取消: {cancelled.cancel_at_period_end}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("所有测试通过! ✓")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# 清理临时数据库
|
||||||
|
if os.path.exists(db_path):
|
||||||
|
os.remove(db_path)
|
||||||
|
print(f"\n清理临时数据库: {db_path}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
test_subscription_manager()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ 测试失败: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.exit(1)
|
||||||
383
backend/test_phase8_task4.py
Normal file
383
backend/test_phase8_task4.py
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
InsightFlow Phase 8 Task 4 测试脚本
|
||||||
|
测试 AI 能力增强功能
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Add backend directory to path
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
from ai_manager import (
|
||||||
|
get_ai_manager, CustomModel, TrainingSample, MultimodalAnalysis,
|
||||||
|
KnowledgeGraphRAG, SmartSummary, PredictionModel, PredictionResult,
|
||||||
|
ModelType, ModelStatus, MultimodalProvider, PredictionType
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_model():
|
||||||
|
"""测试自定义模型功能"""
|
||||||
|
print("\n=== 测试自定义模型 ===")
|
||||||
|
|
||||||
|
manager = get_ai_manager()
|
||||||
|
|
||||||
|
# 1. 创建自定义模型
|
||||||
|
print("1. 创建自定义模型...")
|
||||||
|
model = manager.create_custom_model(
|
||||||
|
tenant_id="tenant_001",
|
||||||
|
name="领域实体识别模型",
|
||||||
|
description="用于识别医疗领域实体的自定义模型",
|
||||||
|
model_type=ModelType.CUSTOM_NER,
|
||||||
|
training_data={
|
||||||
|
"entity_types": ["DISEASE", "SYMPTOM", "DRUG", "TREATMENT"],
|
||||||
|
"domain": "medical"
|
||||||
|
},
|
||||||
|
hyperparameters={
|
||||||
|
"epochs": 15,
|
||||||
|
"learning_rate": 0.001,
|
||||||
|
"batch_size": 32
|
||||||
|
},
|
||||||
|
created_by="user_001"
|
||||||
|
)
|
||||||
|
print(f" 创建成功: {model.id}, 状态: {model.status.value}")
|
||||||
|
|
||||||
|
# 2. 添加训练样本
|
||||||
|
print("2. 添加训练样本...")
|
||||||
|
samples = [
|
||||||
|
{
|
||||||
|
"text": "患者张三患有高血压,正在服用降压药治疗。",
|
||||||
|
"entities": [
|
||||||
|
{"start": 2, "end": 4, "label": "PERSON", "text": "张三"},
|
||||||
|
{"start": 6, "end": 9, "label": "DISEASE", "text": "高血压"},
|
||||||
|
{"start": 14, "end": 17, "label": "DRUG", "text": "降压药"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "李四因感冒发烧到医院就诊,医生开具了退烧药。",
|
||||||
|
"entities": [
|
||||||
|
{"start": 0, "end": 2, "label": "PERSON", "text": "李四"},
|
||||||
|
{"start": 3, "end": 5, "label": "SYMPTOM", "text": "感冒"},
|
||||||
|
{"start": 5, "end": 7, "label": "SYMPTOM", "text": "发烧"},
|
||||||
|
{"start": 21, "end": 24, "label": "DRUG", "text": "退烧药"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "王五接受了心脏搭桥手术,术后恢复良好。",
|
||||||
|
"entities": [
|
||||||
|
{"start": 0, "end": 2, "label": "PERSON", "text": "王五"},
|
||||||
|
{"start": 5, "end": 11, "label": "TREATMENT", "text": "心脏搭桥手术"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
for sample_data in samples:
|
||||||
|
sample = manager.add_training_sample(
|
||||||
|
model_id=model.id,
|
||||||
|
text=sample_data["text"],
|
||||||
|
entities=sample_data["entities"],
|
||||||
|
metadata={"source": "manual"}
|
||||||
|
)
|
||||||
|
print(f" 添加样本: {sample.id}")
|
||||||
|
|
||||||
|
# 3. 获取训练样本
|
||||||
|
print("3. 获取训练样本...")
|
||||||
|
all_samples = manager.get_training_samples(model.id)
|
||||||
|
print(f" 共有 {len(all_samples)} 个训练样本")
|
||||||
|
|
||||||
|
# 4. 列出自定义模型
|
||||||
|
print("4. 列出自定义模型...")
|
||||||
|
models = manager.list_custom_models(tenant_id="tenant_001")
|
||||||
|
print(f" 找到 {len(models)} 个模型")
|
||||||
|
for m in models:
|
||||||
|
print(f" - {m.name} ({m.model_type.value}): {m.status.value}")
|
||||||
|
|
||||||
|
return model.id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_train_and_predict(model_id: str):
|
||||||
|
"""测试训练和预测"""
|
||||||
|
print("\n=== 测试模型训练和预测 ===")
|
||||||
|
|
||||||
|
manager = get_ai_manager()
|
||||||
|
|
||||||
|
# 1. 训练模型
|
||||||
|
print("1. 训练模型...")
|
||||||
|
try:
|
||||||
|
trained_model = await manager.train_custom_model(model_id)
|
||||||
|
print(f" 训练完成: {trained_model.status.value}")
|
||||||
|
print(f" 指标: {trained_model.metrics}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" 训练失败: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2. 使用模型预测
|
||||||
|
print("2. 使用模型预测...")
|
||||||
|
test_text = "赵六患有糖尿病,正在使用胰岛素治疗。"
|
||||||
|
try:
|
||||||
|
entities = await manager.predict_with_custom_model(model_id, test_text)
|
||||||
|
print(f" 输入: {test_text}")
|
||||||
|
print(f" 预测实体: {entities}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" 预测失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def test_prediction_models():
|
||||||
|
"""测试预测模型"""
|
||||||
|
print("\n=== 测试预测模型 ===")
|
||||||
|
|
||||||
|
manager = get_ai_manager()
|
||||||
|
|
||||||
|
# 1. 创建趋势预测模型
|
||||||
|
print("1. 创建趋势预测模型...")
|
||||||
|
trend_model = manager.create_prediction_model(
|
||||||
|
tenant_id="tenant_001",
|
||||||
|
project_id="project_001",
|
||||||
|
name="实体数量趋势预测",
|
||||||
|
prediction_type=PredictionType.TREND,
|
||||||
|
target_entity_type="PERSON",
|
||||||
|
features=["entity_count", "time_period", "document_count"],
|
||||||
|
model_config={
|
||||||
|
"algorithm": "linear_regression",
|
||||||
|
"window_size": 7
|
||||||
|
}
|
||||||
|
)
|
||||||
|
print(f" 创建成功: {trend_model.id}")
|
||||||
|
|
||||||
|
# 2. 创建异常检测模型
|
||||||
|
print("2. 创建异常检测模型...")
|
||||||
|
anomaly_model = manager.create_prediction_model(
|
||||||
|
tenant_id="tenant_001",
|
||||||
|
project_id="project_001",
|
||||||
|
name="实体增长异常检测",
|
||||||
|
prediction_type=PredictionType.ANOMALY,
|
||||||
|
target_entity_type=None,
|
||||||
|
features=["daily_growth", "weekly_growth"],
|
||||||
|
model_config={
|
||||||
|
"threshold": 2.5,
|
||||||
|
"sensitivity": "medium"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
print(f" 创建成功: {anomaly_model.id}")
|
||||||
|
|
||||||
|
# 3. 列出预测模型
|
||||||
|
print("3. 列出预测模型...")
|
||||||
|
models = manager.list_prediction_models(tenant_id="tenant_001")
|
||||||
|
print(f" 找到 {len(models)} 个预测模型")
|
||||||
|
for m in models:
|
||||||
|
print(f" - {m.name} ({m.prediction_type.value})")
|
||||||
|
|
||||||
|
return trend_model.id, anomaly_model.id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_predictions(trend_model_id: str, anomaly_model_id: str):
|
||||||
|
"""测试预测功能"""
|
||||||
|
print("\n=== 测试预测功能 ===")
|
||||||
|
|
||||||
|
manager = get_ai_manager()
|
||||||
|
|
||||||
|
# 1. 训练趋势预测模型
|
||||||
|
print("1. 训练趋势预测模型...")
|
||||||
|
historical_data = [
|
||||||
|
{"date": "2024-01-01", "value": 10},
|
||||||
|
{"date": "2024-01-02", "value": 12},
|
||||||
|
{"date": "2024-01-03", "value": 15},
|
||||||
|
{"date": "2024-01-04", "value": 14},
|
||||||
|
{"date": "2024-01-05", "value": 18},
|
||||||
|
{"date": "2024-01-06", "value": 20},
|
||||||
|
{"date": "2024-01-07", "value": 22}
|
||||||
|
]
|
||||||
|
trained = await manager.train_prediction_model(trend_model_id, historical_data)
|
||||||
|
print(f" 训练完成,准确率: {trained.accuracy}")
|
||||||
|
|
||||||
|
# 2. 趋势预测
|
||||||
|
print("2. 趋势预测...")
|
||||||
|
trend_result = await manager.predict(
|
||||||
|
trend_model_id,
|
||||||
|
{"historical_values": [10, 12, 15, 14, 18, 20, 22]}
|
||||||
|
)
|
||||||
|
print(f" 预测结果: {trend_result.prediction_data}")
|
||||||
|
|
||||||
|
# 3. 异常检测
|
||||||
|
print("3. 异常检测...")
|
||||||
|
anomaly_result = await manager.predict(
|
||||||
|
anomaly_model_id,
|
||||||
|
{
|
||||||
|
"value": 50,
|
||||||
|
"historical_values": [10, 12, 11, 13, 12, 14, 13]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
print(f" 检测结果: {anomaly_result.prediction_data}")
|
||||||
|
|
||||||
|
|
||||||
|
def test_kg_rag():
|
||||||
|
"""测试知识图谱 RAG"""
|
||||||
|
print("\n=== 测试知识图谱 RAG ===")
|
||||||
|
|
||||||
|
manager = get_ai_manager()
|
||||||
|
|
||||||
|
# 创建 RAG 配置
|
||||||
|
print("1. 创建知识图谱 RAG 配置...")
|
||||||
|
rag = manager.create_kg_rag(
|
||||||
|
tenant_id="tenant_001",
|
||||||
|
project_id="project_001",
|
||||||
|
name="项目知识问答",
|
||||||
|
description="基于项目知识图谱的智能问答",
|
||||||
|
kg_config={
|
||||||
|
"entity_types": ["PERSON", "ORG", "PROJECT", "TECH"],
|
||||||
|
"relation_types": ["works_with", "belongs_to", "depends_on"]
|
||||||
|
},
|
||||||
|
retrieval_config={
|
||||||
|
"top_k": 5,
|
||||||
|
"similarity_threshold": 0.7,
|
||||||
|
"expand_relations": True
|
||||||
|
},
|
||||||
|
generation_config={
|
||||||
|
"temperature": 0.3,
|
||||||
|
"max_tokens": 1000,
|
||||||
|
"include_sources": True
|
||||||
|
}
|
||||||
|
)
|
||||||
|
print(f" 创建成功: {rag.id}")
|
||||||
|
|
||||||
|
# 列出 RAG 配置
|
||||||
|
print("2. 列出 RAG 配置...")
|
||||||
|
rags = manager.list_kg_rags(tenant_id="tenant_001")
|
||||||
|
print(f" 找到 {len(rags)} 个配置")
|
||||||
|
|
||||||
|
return rag.id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_kg_rag_query(rag_id: str):
|
||||||
|
"""测试 RAG 查询"""
|
||||||
|
print("\n=== 测试知识图谱 RAG 查询 ===")
|
||||||
|
|
||||||
|
manager = get_ai_manager()
|
||||||
|
|
||||||
|
# 模拟项目实体和关系
|
||||||
|
project_entities = [
|
||||||
|
{"id": "e1", "name": "张三", "type": "PERSON", "definition": "项目经理"},
|
||||||
|
{"id": "e2", "name": "李四", "type": "PERSON", "definition": "技术负责人"},
|
||||||
|
{"id": "e3", "name": "Project Alpha", "type": "PROJECT", "definition": "核心产品项目"},
|
||||||
|
{"id": "e4", "name": "Kubernetes", "type": "TECH", "definition": "容器编排平台"},
|
||||||
|
{"id": "e5", "name": "TechCorp", "type": "ORG", "definition": "科技公司"}
|
||||||
|
]
|
||||||
|
|
||||||
|
project_relations = [
|
||||||
|
{"source_entity_id": "e1", "target_entity_id": "e3", "source_name": "张三", "target_name": "Project Alpha", "relation_type": "works_with", "evidence": "张三负责 Project Alpha 的管理工作"},
|
||||||
|
{"source_entity_id": "e2", "target_entity_id": "e3", "source_name": "李四", "target_name": "Project Alpha", "relation_type": "works_with", "evidence": "李四负责 Project Alpha 的技术架构"},
|
||||||
|
{"source_entity_id": "e3", "target_entity_id": "e4", "source_name": "Project Alpha", "target_name": "Kubernetes", "relation_type": "depends_on", "evidence": "项目使用 Kubernetes 进行部署"},
|
||||||
|
{"source_entity_id": "e1", "target_entity_id": "e5", "source_name": "张三", "target_name": "TechCorp", "relation_type": "belongs_to", "evidence": "张三是 TechCorp 的员工"}
|
||||||
|
]
|
||||||
|
|
||||||
|
# 执行查询
|
||||||
|
print("1. 执行 RAG 查询...")
|
||||||
|
query_text = "Project Alpha 项目有哪些人参与?使用了什么技术?"
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = await manager.query_kg_rag(
|
||||||
|
rag_id=rag_id,
|
||||||
|
query=query_text,
|
||||||
|
project_entities=project_entities,
|
||||||
|
project_relations=project_relations
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f" 查询: {result.query}")
|
||||||
|
print(f" 回答: {result.answer[:200]}...")
|
||||||
|
print(f" 置信度: {result.confidence}")
|
||||||
|
print(f" 来源: {len(result.sources)} 个实体")
|
||||||
|
print(f" 延迟: {result.latency_ms}ms")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" 查询失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_smart_summary():
|
||||||
|
"""测试智能摘要"""
|
||||||
|
print("\n=== 测试智能摘要 ===")
|
||||||
|
|
||||||
|
manager = get_ai_manager()
|
||||||
|
|
||||||
|
# 模拟转录文本
|
||||||
|
transcript_text = """
|
||||||
|
今天的会议主要讨论了 Project Alpha 的进展情况。张三作为项目经理,
|
||||||
|
汇报了当前的项目进度,表示已经完成了 80% 的开发工作。李四提出了
|
||||||
|
一些关于 Kubernetes 部署的问题,建议我们采用新的部署策略。
|
||||||
|
会议还讨论了下一步的工作计划,包括测试、文档编写和上线准备。
|
||||||
|
大家一致认为项目进展顺利,预计可以按时交付。
|
||||||
|
"""
|
||||||
|
|
||||||
|
content_data = {
|
||||||
|
"text": transcript_text,
|
||||||
|
"entities": [
|
||||||
|
{"name": "张三", "type": "PERSON"},
|
||||||
|
{"name": "李四", "type": "PERSON"},
|
||||||
|
{"name": "Project Alpha", "type": "PROJECT"},
|
||||||
|
{"name": "Kubernetes", "type": "TECH"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 生成不同类型的摘要
|
||||||
|
summary_types = ["extractive", "abstractive", "key_points"]
|
||||||
|
|
||||||
|
for summary_type in summary_types:
|
||||||
|
print(f"1. 生成 {summary_type} 类型摘要...")
|
||||||
|
try:
|
||||||
|
summary = await manager.generate_smart_summary(
|
||||||
|
tenant_id="tenant_001",
|
||||||
|
project_id="project_001",
|
||||||
|
source_type="transcript",
|
||||||
|
source_id="transcript_001",
|
||||||
|
summary_type=summary_type,
|
||||||
|
content_data=content_data
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f" 摘要类型: {summary.summary_type}")
|
||||||
|
print(f" 内容: {summary.content[:150]}...")
|
||||||
|
print(f" 关键要点: {summary.key_points[:3]}")
|
||||||
|
print(f" 置信度: {summary.confidence}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f" 生成失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
"""主测试函数"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("InsightFlow Phase 8 Task 4 - AI 能力增强测试")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 测试自定义模型
|
||||||
|
model_id = test_custom_model()
|
||||||
|
|
||||||
|
# 测试训练和预测
|
||||||
|
await test_train_and_predict(model_id)
|
||||||
|
|
||||||
|
# 测试预测模型
|
||||||
|
trend_model_id, anomaly_model_id = test_prediction_models()
|
||||||
|
|
||||||
|
# 测试预测功能
|
||||||
|
await test_predictions(trend_model_id, anomaly_model_id)
|
||||||
|
|
||||||
|
# 测试知识图谱 RAG
|
||||||
|
rag_id = test_kg_rag()
|
||||||
|
|
||||||
|
# 测试 RAG 查询
|
||||||
|
await test_kg_rag_query(rag_id)
|
||||||
|
|
||||||
|
# 测试智能摘要
|
||||||
|
await test_smart_summary()
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("所有测试完成!")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n测试失败: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
@@ -1,507 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
InsightFlow Phase 8 - Multi-Tenant SaaS Test Script
|
|
||||||
多租户 SaaS 架构测试脚本
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
from tenant_manager import (
|
|
||||||
get_tenant_manager, TenantManager, Tenant, TenantDomain, TenantBranding,
|
|
||||||
TenantMember, TenantStatus, TenantTier, TenantRole, DomainStatus,
|
|
||||||
TenantContext
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_tenant_management():
|
|
||||||
"""测试租户管理功能"""
|
|
||||||
print("=" * 60)
|
|
||||||
print("测试租户管理功能")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# 使用测试数据库
|
|
||||||
test_db = "test_tenant.db"
|
|
||||||
if os.path.exists(test_db):
|
|
||||||
os.remove(test_db)
|
|
||||||
|
|
||||||
manager = get_tenant_manager(test_db)
|
|
||||||
|
|
||||||
# 1. 创建租户
|
|
||||||
print("\n1. 创建租户...")
|
|
||||||
try:
|
|
||||||
tenant = manager.create_tenant(
|
|
||||||
name="Test Company",
|
|
||||||
owner_id="user_001",
|
|
||||||
tier="pro",
|
|
||||||
description="A test tenant for validation",
|
|
||||||
settings={"theme": "dark"}
|
|
||||||
)
|
|
||||||
print(f" ✓ 租户创建成功: {tenant.id}")
|
|
||||||
print(f" - 名称: {tenant.name}")
|
|
||||||
print(f" - Slug: {tenant.slug}")
|
|
||||||
print(f" - 层级: {tenant.tier}")
|
|
||||||
print(f" - 状态: {tenant.status}")
|
|
||||||
print(f" - 资源限制: {tenant.resource_limits}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 租户创建失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 2. 获取租户
|
|
||||||
print("\n2. 获取租户...")
|
|
||||||
try:
|
|
||||||
fetched = manager.get_tenant(tenant.id)
|
|
||||||
assert fetched is not None
|
|
||||||
assert fetched.name == tenant.name
|
|
||||||
print(f" ✓ 通过 ID 获取租户成功")
|
|
||||||
|
|
||||||
fetched_by_slug = manager.get_tenant_by_slug(tenant.slug)
|
|
||||||
assert fetched_by_slug is not None
|
|
||||||
assert fetched_by_slug.id == tenant.id
|
|
||||||
print(f" ✓ 通过 Slug 获取租户成功")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 获取租户失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 3. 更新租户
|
|
||||||
print("\n3. 更新租户...")
|
|
||||||
try:
|
|
||||||
updated = manager.update_tenant(
|
|
||||||
tenant.id,
|
|
||||||
name="Test Company Updated",
|
|
||||||
tier="enterprise"
|
|
||||||
)
|
|
||||||
assert updated is not None
|
|
||||||
assert updated.name == "Test Company Updated"
|
|
||||||
assert updated.tier == "enterprise"
|
|
||||||
print(f" ✓ 租户更新成功")
|
|
||||||
print(f" - 新名称: {updated.name}")
|
|
||||||
print(f" - 新层级: {updated.tier}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 租户更新失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 4. 列出租户
|
|
||||||
print("\n4. 列出租户...")
|
|
||||||
try:
|
|
||||||
tenants = manager.list_tenants()
|
|
||||||
assert len(tenants) >= 1
|
|
||||||
print(f" ✓ 列出租户成功,共 {len(tenants)} 个租户")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 列出租户失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return tenant.id
|
|
||||||
|
|
||||||
|
|
||||||
def test_domain_management(tenant_id: str):
|
|
||||||
"""测试域名管理功能"""
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("测试域名管理功能")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
manager = get_tenant_manager("test_tenant.db")
|
|
||||||
|
|
||||||
# 1. 添加域名
|
|
||||||
print("\n1. 添加自定义域名...")
|
|
||||||
try:
|
|
||||||
domain = manager.add_domain(tenant_id, "app.example.com", is_primary=True)
|
|
||||||
print(f" ✓ 域名添加成功: {domain.id}")
|
|
||||||
print(f" - 域名: {domain.domain}")
|
|
||||||
print(f" - 状态: {domain.status}")
|
|
||||||
print(f" - 验证令牌: {domain.verification_token}")
|
|
||||||
print(f" - 是否主域名: {domain.is_primary}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 域名添加失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 2. 获取域名验证指导
|
|
||||||
print("\n2. 获取域名验证指导...")
|
|
||||||
try:
|
|
||||||
instructions = manager.get_domain_verification_instructions(domain.id)
|
|
||||||
assert instructions is not None
|
|
||||||
print(f" ✓ 获取验证指导成功")
|
|
||||||
print(f" - DNS 记录: {instructions['dns_record']}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 获取验证指导失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 3. 验证域名
|
|
||||||
print("\n3. 验证域名...")
|
|
||||||
try:
|
|
||||||
success = manager.verify_domain(tenant_id, domain.id)
|
|
||||||
if success:
|
|
||||||
print(f" ✓ 域名验证成功")
|
|
||||||
else:
|
|
||||||
print(f" ! 域名验证返回 False(可能是模拟验证)")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 域名验证失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 4. 获取域名列表
|
|
||||||
print("\n4. 获取域名列表...")
|
|
||||||
try:
|
|
||||||
domains = manager.list_domains(tenant_id)
|
|
||||||
assert len(domains) >= 1
|
|
||||||
print(f" ✓ 获取域名列表成功,共 {len(domains)} 个域名")
|
|
||||||
for d in domains:
|
|
||||||
print(f" - {d.domain} ({d.status})")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 获取域名列表失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 5. 通过域名获取租户
|
|
||||||
print("\n5. 通过域名解析租户...")
|
|
||||||
try:
|
|
||||||
resolved = manager.get_tenant_by_domain("app.example.com")
|
|
||||||
if resolved:
|
|
||||||
assert resolved.id == tenant_id
|
|
||||||
print(f" ✓ 域名解析租户成功")
|
|
||||||
else:
|
|
||||||
print(f" ! 域名解析租户返回 None(可能域名未激活)")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 域名解析失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def test_branding_management(tenant_id: str):
|
|
||||||
"""测试品牌配置管理功能"""
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("测试品牌配置管理功能")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
manager = get_tenant_manager("test_tenant.db")
|
|
||||||
|
|
||||||
# 1. 更新品牌配置
|
|
||||||
print("\n1. 更新品牌配置...")
|
|
||||||
try:
|
|
||||||
branding = manager.update_branding(
|
|
||||||
tenant_id,
|
|
||||||
logo_url="https://example.com/logo.png",
|
|
||||||
favicon_url="https://example.com/favicon.ico",
|
|
||||||
primary_color="#FF5733",
|
|
||||||
secondary_color="#33FF57",
|
|
||||||
custom_css="body { font-size: 14px; }",
|
|
||||||
custom_js="console.log('Custom JS loaded');"
|
|
||||||
)
|
|
||||||
assert branding is not None
|
|
||||||
print(f" ✓ 品牌配置更新成功")
|
|
||||||
print(f" - Logo: {branding.logo_url}")
|
|
||||||
print(f" - 主色调: {branding.primary_color}")
|
|
||||||
print(f" - 次色调: {branding.secondary_color}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 品牌配置更新失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 2. 获取品牌配置
|
|
||||||
print("\n2. 获取品牌配置...")
|
|
||||||
try:
|
|
||||||
fetched = manager.get_branding(tenant_id)
|
|
||||||
assert fetched is not None
|
|
||||||
assert fetched.primary_color == "#FF5733"
|
|
||||||
print(f" ✓ 获取品牌配置成功")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 获取品牌配置失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 3. 生成品牌 CSS
|
|
||||||
print("\n3. 生成品牌 CSS...")
|
|
||||||
try:
|
|
||||||
css = manager.get_branding_css(tenant_id)
|
|
||||||
assert "--tenant-primary" in css
|
|
||||||
assert "#FF5733" in css
|
|
||||||
print(f" ✓ 品牌 CSS 生成成功")
|
|
||||||
print(f" - CSS 长度: {len(css)} 字符")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 品牌 CSS 生成失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def test_member_management(tenant_id: str):
|
|
||||||
"""测试成员管理功能"""
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("测试成员管理功能")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
manager = get_tenant_manager("test_tenant.db")
|
|
||||||
|
|
||||||
# 1. 邀请成员
|
|
||||||
print("\n1. 邀请成员...")
|
|
||||||
try:
|
|
||||||
member = manager.invite_member(
|
|
||||||
tenant_id=tenant_id,
|
|
||||||
email="user@example.com",
|
|
||||||
role="admin",
|
|
||||||
invited_by="user_001"
|
|
||||||
)
|
|
||||||
print(f" ✓ 成员邀请成功: {member.id}")
|
|
||||||
print(f" - 邮箱: {member.email}")
|
|
||||||
print(f" - 角色: {member.role}")
|
|
||||||
print(f" - 状态: {member.status}")
|
|
||||||
print(f" - 权限: {member.permissions}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 成员邀请失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 2. 获取成员列表
|
|
||||||
print("\n2. 获取成员列表...")
|
|
||||||
try:
|
|
||||||
members = manager.list_members(tenant_id)
|
|
||||||
assert len(members) >= 2 # owner + invited member
|
|
||||||
print(f" ✓ 获取成员列表成功,共 {len(members)} 个成员")
|
|
||||||
for m in members:
|
|
||||||
print(f" - {m.email} ({m.role}, {m.status})")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 获取成员列表失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 3. 接受邀请
|
|
||||||
print("\n3. 接受邀请...")
|
|
||||||
try:
|
|
||||||
# 注意:accept_invitation 使用的是 member id 而不是 token
|
|
||||||
# 修正:查看源码后发现它接受的是 invitation_id(即 member id)
|
|
||||||
accepted = manager.accept_invitation(member.id, "user_002")
|
|
||||||
if accepted:
|
|
||||||
print(f" ✓ 邀请接受成功")
|
|
||||||
else:
|
|
||||||
print(f" ! 邀请接受返回 False(可能是状态不对)")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 邀请接受失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 4. 更新成员角色
|
|
||||||
print("\n4. 更新成员角色...")
|
|
||||||
try:
|
|
||||||
success = manager.update_member_role(
|
|
||||||
tenant_id=tenant_id,
|
|
||||||
member_id=member.id,
|
|
||||||
role="member"
|
|
||||||
)
|
|
||||||
if success:
|
|
||||||
print(f" ✓ 成员角色更新成功")
|
|
||||||
else:
|
|
||||||
print(f" ! 成员角色更新返回 False")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 成员角色更新失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 5. 检查权限
|
|
||||||
print("\n5. 检查用户权限...")
|
|
||||||
try:
|
|
||||||
# 检查 owner 权限
|
|
||||||
has_permission = manager.check_permission(
|
|
||||||
tenant_id=tenant_id,
|
|
||||||
user_id="user_001",
|
|
||||||
resource="project",
|
|
||||||
action="create"
|
|
||||||
)
|
|
||||||
print(f" ✓ 权限检查成功")
|
|
||||||
print(f" - Owner 是否有 project:create 权限: {has_permission}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 权限检查失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 6. 获取用户租户列表
|
|
||||||
print("\n6. 获取用户租户列表...")
|
|
||||||
try:
|
|
||||||
user_tenants = manager.get_user_tenants("user_001")
|
|
||||||
assert len(user_tenants) >= 1
|
|
||||||
print(f" ✓ 获取用户租户列表成功,共 {len(user_tenants)} 个租户")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 获取用户租户列表失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def test_usage_stats(tenant_id: str):
|
|
||||||
"""测试使用统计功能"""
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("测试使用统计功能")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
manager = get_tenant_manager("test_tenant.db")
|
|
||||||
|
|
||||||
# 1. 记录使用
|
|
||||||
print("\n1. 记录资源使用...")
|
|
||||||
try:
|
|
||||||
manager.record_usage(
|
|
||||||
tenant_id=tenant_id,
|
|
||||||
storage_bytes=1024 * 1024 * 50, # 50MB
|
|
||||||
transcription_seconds=600, # 10分钟
|
|
||||||
api_calls=100,
|
|
||||||
projects_count=5,
|
|
||||||
entities_count=50,
|
|
||||||
members_count=3
|
|
||||||
)
|
|
||||||
print(f" ✓ 资源使用记录成功")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 资源使用记录失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 2. 获取使用统计
|
|
||||||
print("\n2. 获取使用统计...")
|
|
||||||
try:
|
|
||||||
stats = manager.get_usage_stats(tenant_id)
|
|
||||||
print(f" ✓ 使用统计获取成功")
|
|
||||||
print(f" - 存储: {stats['storage_mb']:.2f} MB")
|
|
||||||
print(f" - 转录: {stats['transcription_minutes']:.2f} 分钟")
|
|
||||||
print(f" - API 调用: {stats['api_calls']}")
|
|
||||||
print(f" - 项目数: {stats['projects_count']}")
|
|
||||||
print(f" - 实体数: {stats['entities_count']}")
|
|
||||||
print(f" - 成员数: {stats['members_count']}")
|
|
||||||
print(f" - 配额: {stats['limits']}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 使用统计获取失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 3. 检查资源限制
|
|
||||||
print("\n3. 检查资源限制...")
|
|
||||||
try:
|
|
||||||
allowed, current, limit = manager.check_resource_limit(tenant_id, "storage")
|
|
||||||
print(f" ✓ 资源限制检查成功")
|
|
||||||
print(f" - 存储: {allowed}, 当前: {current}, 限制: {limit}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 资源限制检查失败: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def test_tenant_context():
|
|
||||||
"""测试租户上下文管理"""
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("测试租户上下文管理")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# 1. 设置和获取租户上下文
|
|
||||||
print("\n1. 设置和获取租户上下文...")
|
|
||||||
try:
|
|
||||||
TenantContext.set_current_tenant("tenant_123")
|
|
||||||
tenant_id = TenantContext.get_current_tenant()
|
|
||||||
assert tenant_id == "tenant_123"
|
|
||||||
print(f" ✓ 租户上下文设置成功: {tenant_id}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 租户上下文设置失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 2. 设置和获取用户上下文
|
|
||||||
print("\n2. 设置和获取用户上下文...")
|
|
||||||
try:
|
|
||||||
TenantContext.set_current_user("user_456")
|
|
||||||
user_id = TenantContext.get_current_user()
|
|
||||||
assert user_id == "user_456"
|
|
||||||
print(f" ✓ 用户上下文设置成功: {user_id}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 用户上下文设置失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 3. 清除上下文
|
|
||||||
print("\n3. 清除上下文...")
|
|
||||||
try:
|
|
||||||
TenantContext.clear()
|
|
||||||
assert TenantContext.get_current_tenant() is None
|
|
||||||
assert TenantContext.get_current_user() is None
|
|
||||||
print(f" ✓ 上下文清除成功")
|
|
||||||
except Exception as e:
|
|
||||||
print(f" ✗ 上下文清除失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def cleanup():
|
|
||||||
"""清理测试数据"""
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("清理测试数据")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
test_db = "test_tenant.db"
|
|
||||||
if os.path.exists(test_db):
|
|
||||||
os.remove(test_db)
|
|
||||||
print(f"✓ 删除测试数据库: {test_db}")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主测试函数"""
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("InsightFlow Phase 8 - Multi-Tenant SaaS 测试")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
all_passed = True
|
|
||||||
tenant_id = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 测试租户上下文
|
|
||||||
if not test_tenant_context():
|
|
||||||
all_passed = False
|
|
||||||
|
|
||||||
# 测试租户管理
|
|
||||||
tenant_id = test_tenant_management()
|
|
||||||
if not tenant_id:
|
|
||||||
all_passed = False
|
|
||||||
|
|
||||||
# 测试域名管理
|
|
||||||
if not test_domain_management(tenant_id):
|
|
||||||
all_passed = False
|
|
||||||
|
|
||||||
# 测试品牌配置
|
|
||||||
if not test_branding_management(tenant_id):
|
|
||||||
all_passed = False
|
|
||||||
|
|
||||||
# 测试成员管理
|
|
||||||
if not test_member_management(tenant_id):
|
|
||||||
all_passed = False
|
|
||||||
|
|
||||||
# 测试使用统计
|
|
||||||
if not test_usage_stats(tenant_id):
|
|
||||||
all_passed = False
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\n测试过程中发生错误: {e}")
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
all_passed = False
|
|
||||||
|
|
||||||
finally:
|
|
||||||
cleanup()
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
if all_passed:
|
|
||||||
print("✓ 所有测试通过!")
|
|
||||||
else:
|
|
||||||
print("✗ 部分测试失败")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
return 0 if all_passed else 1
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
Reference in New Issue
Block a user