mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-13 20:11:29 +00:00
Compare commits
57 Commits
e601eb6db5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1fc02894b | ||
|
|
bf7b5eac72 | ||
|
|
d602b805bd | ||
|
|
9cf18904bb | ||
|
|
2f39fa0f53 | ||
|
|
d2005cfa48 | ||
|
|
4e38f853f3 | ||
|
|
3cfb185dde | ||
|
|
ef99c540bb | ||
|
|
3071bfd0f9 | ||
|
|
7bb938c145 | ||
|
|
75b21d3633 | ||
|
|
7ed9d8def4 | ||
|
|
63ed7ddb02 | ||
|
|
11696a016d | ||
|
|
1a10104751 | ||
|
|
f95cb17933 | ||
|
|
0687b49542 | ||
|
|
27ad00ac3a | ||
|
|
c84d6247b0 | ||
|
|
f582f38570 | ||
|
|
13800dc389 | ||
|
|
619d9b1e84 | ||
|
|
556cc93f14 | ||
|
|
a50375616e | ||
|
|
e33447d023 | ||
|
|
7be277b3e6 | ||
|
|
5fa385e90b | ||
|
|
0a78966737 | ||
|
|
0eb7f00867 | ||
|
|
00ce7f2d98 | ||
|
|
003c066361 | ||
|
|
a5e7c59fd4 | ||
|
|
accac603cf | ||
|
|
7c96c730e6 | ||
|
|
a2dbdb30ff | ||
|
|
772e4af9bf | ||
|
|
a8bd4b47a0 | ||
|
|
a59ddf6070 | ||
|
|
797ecbb054 | ||
|
|
b6b78afea9 | ||
|
|
02240f3fd0 | ||
|
|
1600ab384e | ||
|
|
418805a1ef | ||
|
|
4c4b52bca7 | ||
|
|
7245259bc4 | ||
|
|
6690c8204c | ||
|
|
d8fc597f85 | ||
|
|
5a716da5a6 | ||
|
|
a916f14efc | ||
|
|
523628ade6 | ||
|
|
2259a2f717 | ||
|
|
8df37274da | ||
|
|
393057ab24 | ||
|
|
ee8c882b6f | ||
|
|
69ec2a33a4 | ||
|
|
1cd8ae1cd9 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -44,6 +44,7 @@ nbdist/
|
|||||||
*.log.gz
|
*.log.gz
|
||||||
*.xml.versionsBackup
|
*.xml.versionsBackup
|
||||||
*.swp
|
*.swp
|
||||||
|
data/
|
||||||
|
|
||||||
!*/build/*.java
|
!*/build/*.java
|
||||||
!*/build/*.html
|
!*/build/*.html
|
||||||
|
|||||||
165
README.md
165
README.md
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
### 企业级AI助手平台
|
### 企业级AI助手平台
|
||||||
|
|
||||||
*开箱即用的全栈AI平台,支持多智能体协同、Supervisor模式编排、多种决策模型,提供先进的RAG技术和可视化流程编排能力*
|
*开箱即用的全栈AI平台,支持多智能体协同、Supervisor模式编排、多种决策模式、RAG技术和流程编排能力*
|
||||||
|
|
||||||
**[English](README_EN.md)** | **[📖 使用文档](https://doc.pandarobot.chat)** |
|
**[English](README_EN.md)** | **[📖 使用文档](https://doc.pandarobot.chat)** |
|
||||||
**[🚀 在线体验](https://web.pandarobot.chat)** | **[🐛 问题反馈](https://github.com/ageerle/ruoyi-ai/issues)** | **[💡 功能建议](https://github.com/ageerle/ruoyi-ai/issues)**
|
**[🚀 在线体验](https://web.pandarobot.chat)** | **[🐛 问题反馈](https://github.com/ageerle/ruoyi-ai/issues)** | **[💡 功能建议](https://github.com/ageerle/ruoyi-ai/issues)**
|
||||||
@@ -27,18 +27,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## ✨ 核心亮点
|
## ✨ 核心亮点
|
||||||
|
|
||||||
| 模块 | 现有能力 | 扩展方向 |
|
| 模块 | 现有能力
|
||||||
|:---:|---|---|
|
|:----------:|---
|
||||||
| **模型管理** | 多模型接入(OpenAI/DeepSeek/通义/智谱)、多模态理解、Coze/DIFY/FastGPT平台集成 | 自动模式、容错机制 |
|
| **模型管理** | 多模型接入(OpenAI/DeepSeek/通义/智谱)、多模态理解、Coze/DIFY/FastGPT平台集成
|
||||||
| **知识库** | 本地RAG + 向量库(Milvus/Weaviate) + 知识图谱 + 文档解析 +重排序 | 音频视频解析、知识出处 |
|
| **知识管理** | 本地RAG + 向量库(Milvus/Weaviate/Qdrant) + 文档解析
|
||||||
| **工具管理** | Mcp协议集成、Skills能力 + 可扩展工具生态 | 工具插件市场、toolAgent自动加载工具 |
|
| **工具管理** | Mcp协议集成、Skills能力 + 可扩展工具生态
|
||||||
| **流程编排** | 可视化工作流设计器、节点拖拽编排、SSE流式执行,目前已经支持模型调用,邮件发送,人工审核等节点 | 更多节点类型 |
|
| **流程编排** | 可视化工作流设计器、节点拖拽编排、SSE流式执行,目前已经支持模型调用,邮件发送,人工审核等节点
|
||||||
| **多智能体** | 基于Langchain4j的Agent框架、Supervisor模式编排,支持多种决策模型 | 智能体可配置 |
|
| **多智能体** | 基于Langchain4j的Agent框架、Supervisor模式编排,支持多种决策模型
|
||||||
| **AI编程** | 智能代码分析、项目脚手架生成、Copilot助手 | 代码生成优化 |
|
|
||||||
|
|
||||||
## 🚀 快速体验
|
## 🚀 快速体验
|
||||||
|
|
||||||
@@ -65,12 +62,127 @@
|
|||||||
## 🛠️ 技术架构
|
## 🛠️ 技术架构
|
||||||
|
|
||||||
### 核心框架
|
### 核心框架
|
||||||
- **后端架构**:Spring Boot 4.0 + Spring ai 2.0 + Langchain4j
|
- **后端架构**:Spring Boot 3.5.8 + Langchain4j
|
||||||
- **数据存储**:MySQL 8.0 + Redis + 向量数据库(Milvus/Weaviate)
|
- **数据存储**:MySQL 8.0 + Redis + 向量数据库(Milvus/Weaviate/Qdrant)
|
||||||
- **前端技术**:Vue 3 + Vben Admin + element-plus-x
|
- **前端技术**:Vue 3 + Vben Admin + element-plus-x
|
||||||
- **安全认证**:Sa-Token + JWT 双重保障
|
- **安全认证**:Sa-Token + JWT 双重保障
|
||||||
|
|
||||||
|
|
||||||
|
- **文档处理**:PDF、Word、Excel 解析,图像智能分析
|
||||||
|
- **实时通信**:WebSocket 实时通信,SSE 流式响应
|
||||||
|
- **系统监控**:完善的日志体系、性能监控、服务健康检查
|
||||||
|
|
||||||
|
## 🐳 Docker 部署
|
||||||
|
|
||||||
|
本项目提供两种 Docker 部署方式:
|
||||||
|
|
||||||
|
### 方式一:一键启动所有服务(推荐)
|
||||||
|
|
||||||
|
使用 `docker-compose-all.yaml` 可以一键启动所有服务(包括后端、管理端、用户端及依赖服务):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 克隆仓库
|
||||||
|
git clone https://github.com/ageerle/ruoyi-ai.git
|
||||||
|
cd ruoyi-ai
|
||||||
|
|
||||||
|
# 启动所有服务(从镜像仓库拉取预构建镜像)
|
||||||
|
docker-compose -f docker-compose-all.yaml up -d
|
||||||
|
|
||||||
|
# 查看服务状态
|
||||||
|
docker-compose -f docker-compose-all.yaml ps
|
||||||
|
|
||||||
|
# 访问服务
|
||||||
|
# 管理端: http://localhost:25666 (admin / admin123)
|
||||||
|
# 用户端: http://localhost:25137
|
||||||
|
# 后端API: http://localhost:26039
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:分步部署(源码编译)
|
||||||
|
|
||||||
|
如果您需要从源码构建后端服务,请按照以下步骤操作:
|
||||||
|
|
||||||
|
#### 第一步:部署后端服务
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 进入后端项目目录
|
||||||
|
cd ruoyi-ai
|
||||||
|
|
||||||
|
# 启动后端服务(源码编译构建)
|
||||||
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
# 等待后端服务启动完成
|
||||||
|
docker-compose logs -f backend
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第二步:部署管理端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 进入管理端项目目录
|
||||||
|
cd ruoyi-admin
|
||||||
|
|
||||||
|
# 构建并启动管理端
|
||||||
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
# 访问管理端
|
||||||
|
# 地址: http://localhost:5666
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第三步:部署用户端(可选)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 进入用户端项目目录
|
||||||
|
cd ruoyi-web
|
||||||
|
|
||||||
|
# 构建并启动用户端
|
||||||
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
# 访问用户端
|
||||||
|
# 地址: http://localhost:5137
|
||||||
|
```
|
||||||
|
|
||||||
|
### 服务端口说明
|
||||||
|
|
||||||
|
| 服务 | 一键启动端口 | 分步部署端口 | 说明 |
|
||||||
|
|------|-------------|-------------|------|
|
||||||
|
| 管理端 | 25666 | 5666 | 管理后台访问地址 |
|
||||||
|
| 用户端 | 25137 | 5137 | 用户前端访问地址 |
|
||||||
|
| 后端服务 | 26039 | 6039 | 后端 API 服务 |
|
||||||
|
| MySQL | 23306 | 23306 | 数据库服务 |
|
||||||
|
| Redis | 26379 | 6379 | 缓存服务 |
|
||||||
|
| Weaviate | 28080 | 28080 | 向量数据库 |
|
||||||
|
| MinIO API | 29000 | 9000 | 对象存储 API |
|
||||||
|
| MinIO Console | 29090 | 9090 | 对象存储控制台 |
|
||||||
|
|
||||||
|
### 镜像仓库
|
||||||
|
|
||||||
|
所有镜像托管在阿里云容器镜像服务:
|
||||||
|
|
||||||
|
```
|
||||||
|
crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai
|
||||||
|
```
|
||||||
|
|
||||||
|
可用镜像:
|
||||||
|
- `mysql:v3` - MySQL 数据库(包含初始化 SQL)
|
||||||
|
- `redis:6.2` - Redis 缓存
|
||||||
|
- `weaviate:1.30.0` - 向量数据库
|
||||||
|
- `minio:latest` - 对象存储
|
||||||
|
- `ruoyi-ai-backend:latest` - 后端服务
|
||||||
|
- `ruoyi-ai-admin:latest` - 管理端前端
|
||||||
|
- `ruoyi-ai-web:latest` - 用户端前端
|
||||||
|
|
||||||
|
### 常用命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 停止所有服务
|
||||||
|
docker-compose -f docker-compose-all.yaml down
|
||||||
|
|
||||||
|
# 查看服务日志
|
||||||
|
docker-compose -f docker-compose-all.yaml logs -f [服务名]
|
||||||
|
|
||||||
|
# 重启某个服务
|
||||||
|
docker-compose -f docker-compose-all.yaml restart [服务名]
|
||||||
|
```
|
||||||
|
|
||||||
## 📚 使用文档
|
## 📚 使用文档
|
||||||
|
|
||||||
想要深入了解安装部署、功能配置和二次开发?
|
想要深入了解安装部署、功能配置和二次开发?
|
||||||
@@ -110,9 +222,6 @@
|
|||||||
算力和模型 API 服务
|
算力和模型 API 服务
|
||||||
- [优云智算](https://www.compshare.cn/?ytag=GPU_YY-gh_ruoyi) - 万卡RTX40系GPU+海内外主流模型API服务,秒级响应,按量计费,新客免费用。
|
- [优云智算](https://www.compshare.cn/?ytag=GPU_YY-gh_ruoyi) - 万卡RTX40系GPU+海内外主流模型API服务,秒级响应,按量计费,新客免费用。
|
||||||
|
|
||||||
## 优秀开源项目及社区推荐
|
|
||||||
- [imaiwork](https://gitee.com/tsinghua-open/imaiwork) - AI手机开源版,AI获客手机项目,基于无障碍模式,RPA,比豆包AI手机更强大。
|
|
||||||
|
|
||||||
## 💬 社区交流
|
## 💬 社区交流
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
@@ -136,30 +245,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
## 📺 视频教程
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td align="center">
|
|
||||||
<img src="docs/image/dy.png" alt="微信二维码" width="200" height="200"><br>
|
|
||||||
<strong>打开抖音扫一扫</strong><br>
|
|
||||||
<em>获取免费视频教程</em>
|
|
||||||
</td>
|
|
||||||
<td align="center">
|
|
||||||
<img src="docs/image/bibi.png" alt="QQ群二维码" width="200" height="200"><br>
|
|
||||||
<strong>打开B站扫一扫</strong><br>
|
|
||||||
<em>获取免费视频教程</em>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
**[⭐ 点个Star支持一下](https://github.com/ageerle/ruoyi-ai)** • **[ Fork 开始贡献](https://github.com/ageerle/ruoyi-ai/fork)** • **[📚 English](README_EN.md)** • **[📖 查看完整文档](https://doc.pandarobot.chat)**
|
**[⭐ 点个Star支持一下](https://github.com/ageerle/ruoyi-ai)** • **[ Fork 开始贡献](https://github.com/ageerle/ruoyi-ai/fork)** • **[📚 English](README_EN.md)** • **[📖 查看完整文档](https://doc.pandarobot.chat)**
|
||||||
|
|||||||
141
README_EN.md
141
README_EN.md
@@ -32,14 +32,13 @@
|
|||||||
|
|
||||||
## ✨ Core Features
|
## ✨ Core Features
|
||||||
|
|
||||||
| Module | Current Capabilities | Extension Direction |
|
| Module | Current Capabilities |
|
||||||
|:---:|---|---|
|
|:---:|---|
|
||||||
| **Model Management** | Multi-model integration (OpenAI/DeepSeek/Tongyi/Zhipu), multi-modal understanding, Coze/DIFY/FastGPT platform integration | Auto mode, fault tolerance |
|
| **Model Management** | Multi-model integration (OpenAI/DeepSeek/Tongyi/Zhipu), multi-modal understanding, Coze/DIFY/FastGPT platform integration |
|
||||||
| **Knowledge Base** | Local RAG + Vector DB (Milvus/Weaviate) + Knowledge Graph + Document parsing + Reranking | Audio/video parsing, knowledge source |
|
| **Knowledge Base** | Local RAG + Vector DB (Milvus/Weaviate/Qdrant) + Document parsing |
|
||||||
| **Tool Management** | MCP protocol integration, Skills capability + Extensible tool ecosystem | Tool plugin marketplace, toolAgent auto-loading |
|
| **Tool Management** | MCP protocol integration, Skills capability + Extensible tool ecosystem |
|
||||||
| **Workflow Orchestration** | Visual workflow designer, drag-and-drop node orchestration, SSE streaming execution, currently supports model (with RAG) calls, email sending, manual review nodes | More node types |
|
| **Workflow Orchestration** | Visual workflow designer, drag-and-drop node orchestration, SSE streaming execution, currently supports model calls, email sending, manual review nodes |
|
||||||
| **Multi-Agent** | Agent framework based on Langchain4j, Supervisor mode orchestration, supports multiple decision models | Configurable agents |
|
| **Multi-Agent** | Agent framework based on Langchain4j, Supervisor mode orchestration, supports multiple decision models |
|
||||||
| **AI Coding** | Intelligent code analysis, project scaffolding generation, Copilot assistant | Code generation optimization |
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 Quick Start
|
||||||
|
|
||||||
@@ -59,19 +58,134 @@
|
|||||||
| 🛠️ Admin Panel | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
|
| 🛠️ Admin Panel | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
|
||||||
|
|
||||||
### Partner Projects
|
### Partner Projects
|
||||||
| Project Name | GitHub Repository | Gitee Repository
|
| Project Name | GitHub Repository | Gitee Repository |
|
||||||
|----------------|-------------------------------------------------------|------------------------------------------------------|
|
|----------------|-------------------------------------------------------|------------------------------------------------------|
|
||||||
| element-plus-x | [element-plus-x](https://github.com/element-plus-x/Element-Plus-X) | [element-plus-x](https://gitee.com/he-jiayue/element-plus-x) |
|
| element-plus-x | [element-plus-x](https://github.com/element-plus-x/Element-Plus-X) | [element-plus-x](https://gitee.com/he-jiayue/element-plus-x) |
|
||||||
|
|
||||||
## 🛠️ Technical Architecture
|
## 🛠️ Technical Architecture
|
||||||
|
|
||||||
### Core Framework
|
### Core Framework
|
||||||
- **Backend**: Spring Boot 4.0 + Spring AI 2.0 + Langchain4j
|
- **Backend**: Spring Boot 3.5.8 + Langchain4j
|
||||||
- **Data Storage**: MySQL 8.0 + Redis + Vector Databases (Milvus/Weaviate)
|
- **Data Storage**: MySQL 8.0 + Redis + Vector Databases (Milvus/Weaviate/Qdrant)
|
||||||
- **Frontend**: Vue 3 + Vben Admin + element-plus-x
|
- **Frontend**: Vue 3 + Vben Admin + element-plus-x
|
||||||
- **Security**: Sa-Token + JWT dual-layer security
|
- **Security**: Sa-Token + JWT dual-layer security
|
||||||
|
|
||||||
|
|
||||||
|
- **Document Processing**: PDF, Word, Excel parsing, intelligent image analysis
|
||||||
|
- **Real-time Communication**: WebSocket real-time communication, SSE streaming response
|
||||||
|
- **System Monitoring**: Comprehensive logging system, performance monitoring, service health checks
|
||||||
|
|
||||||
|
## 🐳 Docker Deployment
|
||||||
|
|
||||||
|
This project provides two Docker deployment methods:
|
||||||
|
|
||||||
|
### Method 1: One-click Start All Services (Recommended)
|
||||||
|
|
||||||
|
Use `docker-compose-all.yaml` to start all services at once (including backend, admin panel, user frontend, and dependencies):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone https://github.com/ageerle/ruoyi-ai.git
|
||||||
|
cd ruoyi-ai
|
||||||
|
|
||||||
|
# Start all services (pull pre-built images from registry)
|
||||||
|
docker-compose -f docker-compose-all.yaml up -d
|
||||||
|
|
||||||
|
# Check service status
|
||||||
|
docker-compose -f docker-compose-all.yaml ps
|
||||||
|
|
||||||
|
# Access services
|
||||||
|
# Admin Panel: http://localhost:25666 (admin / admin123)
|
||||||
|
# User Frontend: http://localhost:25137
|
||||||
|
# Backend API: http://localhost:26039
|
||||||
|
```
|
||||||
|
|
||||||
|
### Method 2: Step-by-step Deployment (Source Build)
|
||||||
|
|
||||||
|
If you need to build backend services from source, follow these steps:
|
||||||
|
|
||||||
|
#### Step 1: Deploy Backend Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enter backend project directory
|
||||||
|
cd ruoyi-ai
|
||||||
|
|
||||||
|
# Start backend service (build from source)
|
||||||
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
# Wait for backend service to start
|
||||||
|
docker-compose logs -f backend
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 2: Deploy Admin Panel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enter admin panel project directory
|
||||||
|
cd ruoyi-admin
|
||||||
|
|
||||||
|
# Build and start admin panel
|
||||||
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
# Access admin panel
|
||||||
|
# URL: http://localhost:5666
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Step 3: Deploy User Frontend (Optional)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enter user frontend project directory
|
||||||
|
cd ruoyi-web
|
||||||
|
|
||||||
|
# Build and start user frontend
|
||||||
|
docker-compose up -d --build
|
||||||
|
|
||||||
|
# Access user frontend
|
||||||
|
# URL: http://localhost:5137
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service Ports
|
||||||
|
|
||||||
|
| Service | One-click Port | Step-by-step Port | Description |
|
||||||
|
|------|-------------|-------------|------|
|
||||||
|
| Admin Panel | 25666 | 5666 | Admin backend access |
|
||||||
|
| User Frontend | 25137 | 5137 | User frontend access |
|
||||||
|
| Backend Service | 26039 | 6039 | Backend API service |
|
||||||
|
| MySQL | 23306 | 23306 | Database service |
|
||||||
|
| Redis | 26379 | 6379 | Cache service |
|
||||||
|
| Weaviate | 28080 | 28080 | Vector database |
|
||||||
|
| MinIO API | 29000 | 9000 | Object storage API |
|
||||||
|
| MinIO Console | 29090 | 9090 | Object storage console |
|
||||||
|
|
||||||
|
### Image Registry
|
||||||
|
|
||||||
|
All images are hosted on Alibaba Cloud Container Registry:
|
||||||
|
|
||||||
|
```
|
||||||
|
crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai
|
||||||
|
```
|
||||||
|
|
||||||
|
Available images:
|
||||||
|
- `mysql:v3` - MySQL database (includes initialization SQL)
|
||||||
|
- `redis:6.2` - Redis cache
|
||||||
|
- `weaviate:1.30.0` - Vector database
|
||||||
|
- `minio:latest` - Object storage
|
||||||
|
- `ruoyi-ai-backend:latest` - Backend service
|
||||||
|
- `ruoyi-ai-admin:latest` - Admin frontend
|
||||||
|
- `ruoyi-ai-web:latest` - User frontend
|
||||||
|
|
||||||
|
### Common Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop all services
|
||||||
|
docker-compose -f docker-compose-all.yaml down
|
||||||
|
|
||||||
|
# View service logs
|
||||||
|
docker-compose -f docker-compose-all.yaml logs -f [service-name]
|
||||||
|
|
||||||
|
# Restart a service
|
||||||
|
docker-compose -f docker-compose-all.yaml restart [service-name]
|
||||||
|
```
|
||||||
|
|
||||||
## 📚 Documentation
|
## 📚 Documentation
|
||||||
|
|
||||||
Want to learn more about installation, deployment, configuration, and secondary development?
|
Want to learn more about installation, deployment, configuration, and secondary development?
|
||||||
@@ -109,14 +223,13 @@ Thanks to the following excellent open-source projects for their support:
|
|||||||
- [PPIO Cloud](https://ppinfra.com/user/register?invited_by=P8QTUY&utm_source=github_ruoyi-ai) - Provides cost-effective GPU computing and model API services
|
- [PPIO Cloud](https://ppinfra.com/user/register?invited_by=P8QTUY&utm_source=github_ruoyi-ai) - Provides cost-effective GPU computing and model API services
|
||||||
- [Youyun Intelligent Computing](https://www.compshare.cn/?ytag=GPU_YY-gh_ruoyi) - Thousands of RTX40 series GPUs + mainstream models API services, second-level response, pay-per-use, free for new customers.
|
- [Youyun Intelligent Computing](https://www.compshare.cn/?ytag=GPU_YY-gh_ruoyi) - Thousands of RTX40 series GPUs + mainstream models API services, second-level response, pay-per-use, free for new customers.
|
||||||
|
|
||||||
## Outstanding Open-Source Projects and Community Recommendations
|
|
||||||
- [imaiwork](https://gitee.com/tsinghua-open/imaiwork) - Open-source AI phone, AI customer acquisition phone project, based on accessibility mode and RPA, more powerful than Doubao AI phone.
|
|
||||||
|
|
||||||
## 💬 Community Chat
|
## 💬 Community Chat
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
**[📱 Join Telegram Group](https://t.me/+LqooQAc5HxRmYmE1)**
|
**[📱 Join Telegram Group](
|
||||||
|
https://t.me/+LqooQAc5HxRmYmE1)**
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
28
docs/docker/ minio/ docker-compose.yml
Normal file
28
docs/docker/ minio/ docker-compose.yml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
minio:
|
||||||
|
image: minio/minio
|
||||||
|
container_name: minio
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
- "9090:9090"
|
||||||
|
environment:
|
||||||
|
- MINIO_ACCESS_KEY=ruoyi
|
||||||
|
- MINIO_SECRET_KEY=ruoyi123
|
||||||
|
volumes:
|
||||||
|
- minio_data:/data
|
||||||
|
- minio_config:/root/.minio
|
||||||
|
command: server /data --console-address ":9090"
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- minio-net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
minio-net:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
minio_data:
|
||||||
|
minio_config:
|
||||||
65
docs/docker/ neo4j/docker-compose.yml
Normal file
65
docs/docker/ neo4j/docker-compose.yml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
neo4j:
|
||||||
|
image: neo4j:5.15.0
|
||||||
|
container_name: ruoyi-neo4j
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
# HTTP端口
|
||||||
|
- "7474:7474"
|
||||||
|
# HTTPS端口
|
||||||
|
- "7473:7473"
|
||||||
|
# Bolt端口
|
||||||
|
- "7687:7687"
|
||||||
|
environment:
|
||||||
|
# 初始密码设置(首次启动后需要修改)
|
||||||
|
- NEO4J_AUTH=neo4j/your_password
|
||||||
|
# 接受许可协议
|
||||||
|
- NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
|
||||||
|
# 内存配置(根据服务器配置调整)
|
||||||
|
- NEO4J_dbms_memory_heap_initial__size=512m
|
||||||
|
- NEO4J_dbms_memory_heap_max__size=2g
|
||||||
|
- NEO4J_dbms_memory_pagecache_size=1g
|
||||||
|
# 事务日志配置
|
||||||
|
- NEO4J_dbms_tx__log_rotation_retention__policy=3 days
|
||||||
|
# 允许从任何主机连接
|
||||||
|
- NEO4J_dbms_default__listen__address=0.0.0.0
|
||||||
|
# 启用APOC插件
|
||||||
|
- NEO4J_dbms_security_procedures_unrestricted=apoc.*
|
||||||
|
- NEO4J_dbms_security_procedures_allowlist=apoc.*
|
||||||
|
# 日志级别
|
||||||
|
- NEO4J_dbms_logs_debug_level=INFO
|
||||||
|
volumes:
|
||||||
|
# 数据持久化
|
||||||
|
- neo4j_data:/data
|
||||||
|
# 日志持久化
|
||||||
|
- neo4j_logs:/logs
|
||||||
|
# 导入目录
|
||||||
|
- neo4j_import:/var/lib/neo4j/import
|
||||||
|
# 插件目录
|
||||||
|
- neo4j_plugins:/plugins
|
||||||
|
networks:
|
||||||
|
- ruoyi-network
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1" ]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
start_period: 40s
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
neo4j_data:
|
||||||
|
name: ruoyi-neo4j-data
|
||||||
|
neo4j_logs:
|
||||||
|
name: ruoyi-neo4j-logs
|
||||||
|
neo4j_import:
|
||||||
|
name: ruoyi-neo4j-import
|
||||||
|
neo4j_plugins:
|
||||||
|
name: ruoyi-neo4j-plugins
|
||||||
|
|
||||||
|
networks:
|
||||||
|
ruoyi-network:
|
||||||
|
name: ruoyi-network
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
75
docs/docker/milvus/docker-compose.yml
Normal file
75
docs/docker/milvus/docker-compose.yml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
version: '3.5'
|
||||||
|
|
||||||
|
services:
|
||||||
|
etcd:
|
||||||
|
container_name: milvus-etcd
|
||||||
|
image: quay.io/coreos/etcd:v3.5.18
|
||||||
|
environment:
|
||||||
|
- ETCD_AUTO_COMPACTION_MODE=revision
|
||||||
|
- ETCD_AUTO_COMPACTION_RETENTION=1000
|
||||||
|
- ETCD_QUOTA_BACKEND_BYTES=4294967296
|
||||||
|
- ETCD_SNAPSHOT_COUNT=50000
|
||||||
|
volumes:
|
||||||
|
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
|
||||||
|
command: etcd -advertise-client-urls=http://etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "etcdctl", "endpoint", "health"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
minio:
|
||||||
|
container_name: milvus-minio
|
||||||
|
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
|
||||||
|
environment:
|
||||||
|
MINIO_ACCESS_KEY: minioadmin
|
||||||
|
MINIO_SECRET_KEY: minioadmin
|
||||||
|
ports:
|
||||||
|
- "9001:9001"
|
||||||
|
- "9000:9000"
|
||||||
|
volumes:
|
||||||
|
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data
|
||||||
|
command: minio server /minio_data --console-address ":9001"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
standalone:
|
||||||
|
container_name: milvus-standalone
|
||||||
|
image: milvusdb/milvus:v2.5.7
|
||||||
|
command: ["milvus", "run", "standalone"]
|
||||||
|
security_opt:
|
||||||
|
- seccomp:unconfined
|
||||||
|
environment:
|
||||||
|
ETCD_ENDPOINTS: etcd:2379
|
||||||
|
MINIO_ADDRESS: minio:9000
|
||||||
|
volumes:
|
||||||
|
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"]
|
||||||
|
interval: 30s
|
||||||
|
start_period: 90s
|
||||||
|
timeout: 20s
|
||||||
|
retries: 3
|
||||||
|
ports:
|
||||||
|
- "19530:19530"
|
||||||
|
- "9091:9091"
|
||||||
|
depends_on:
|
||||||
|
- "etcd"
|
||||||
|
- "minio"
|
||||||
|
|
||||||
|
attu:
|
||||||
|
container_name: attu
|
||||||
|
image: zilliz/attu:v2.5.7
|
||||||
|
environment:
|
||||||
|
MILVUS_URL: milvus-standalone:19530
|
||||||
|
ports:
|
||||||
|
- "19500:3000" # 外部端口19500可以自定义
|
||||||
|
depends_on:
|
||||||
|
- "standalone"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
name: milvus
|
||||||
12
docs/docker/qdrant/docker-compose.yml
Normal file
12
docs/docker/qdrant/docker-compose.yml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
services:
|
||||||
|
qdrant:
|
||||||
|
image: qdrant/qdrant:latest
|
||||||
|
ports:
|
||||||
|
- 6333:6333
|
||||||
|
- 6334:6334
|
||||||
|
volumes:
|
||||||
|
- qdrant_data:/qdrant/storage
|
||||||
|
volumes:
|
||||||
|
qdrant_data:
|
||||||
|
...
|
||||||
36
docs/docker/ruoyi-ai/Dockerfile.backend
Normal file
36
docs/docker/ruoyi-ai/Dockerfile.backend
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# RuoYi-AI 后端 Dockerfile
|
||||||
|
# 基于 Maven + OpenJDK 17
|
||||||
|
|
||||||
|
FROM maven:3.9-eclipse-temurin-17 AS builder
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 复制 pom.xml 和源码
|
||||||
|
COPY pom.xml .
|
||||||
|
COPY ruoyi-admin ./ruoyi-admin
|
||||||
|
COPY ruoyi-common ./ruoyi-common
|
||||||
|
COPY ruoyi-modules ./ruoyi-modules
|
||||||
|
COPY ruoyi-extend ./ruoyi-extend
|
||||||
|
|
||||||
|
|
||||||
|
# 构建项目 (使用 prod profile)
|
||||||
|
RUN mvn clean package -Pprod -DskipTests
|
||||||
|
|
||||||
|
# 最终运行镜像
|
||||||
|
FROM eclipse-temurin:17-jre-alpine
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 从构建阶段复制 jar 包
|
||||||
|
COPY --from=builder /build/ruoyi-admin/target/ruoyi-admin.jar ./ruoyi-admin.jar
|
||||||
|
|
||||||
|
# 创建日志目录
|
||||||
|
RUN mkdir -p /ruoyi/server/logs
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 6039
|
||||||
|
|
||||||
|
# 启动命令
|
||||||
|
ENTRYPOINT ["java", "-jar", "ruoyi-admin.jar", "--spring.profiles.active=prod"]
|
||||||
21
docs/docker/ruoyi-ai/Dockerfile.mysql
Normal file
21
docs/docker/ruoyi-ai/Dockerfile.mysql
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# 基于官方MySQL 8.0镜像构建自定义镜像
|
||||||
|
# 构建命令: docker build -t registry.cn-hangzhou.aliyuncs.com/ruoyi-ai/mysql:v3 -f Dockerfile.mysql .
|
||||||
|
FROM mysql:8.0.33
|
||||||
|
|
||||||
|
# 设置时区
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
|
||||||
|
# 复制初始化脚本和SQL文件到镜像中
|
||||||
|
COPY docs/script/docker/mysql/init/init-db.sh /docker-entrypoint-initdb.d/init-db.sh
|
||||||
|
COPY docs/script/sql/ruoyi-ai-v3_mysql8.sql /docker-entrypoint-initdb.d/ruoyi-ai-v3_mysql8.sql
|
||||||
|
|
||||||
|
# 设置脚本可执行权限
|
||||||
|
RUN chmod +x /docker-entrypoint-initdb.d/init-db.sh
|
||||||
|
|
||||||
|
# MySQL启动参数
|
||||||
|
CMD ["--default-authentication-plugin=mysql_native_password", \
|
||||||
|
"--character-set-server=utf8mb4", \
|
||||||
|
"--collation-server=utf8mb4_general_ci", \
|
||||||
|
"--explicit_defaults_for_timestamp=true", \
|
||||||
|
"--lower_case_table_names=1", \
|
||||||
|
"--skip-ssl"]
|
||||||
180
docs/docker/ruoyi-ai/docker-compose-all.yaml
Normal file
180
docs/docker/ruoyi-ai/docker-compose-all.yaml
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
# RuoYi-AI 一键启动全部服务
|
||||||
|
# 使用方式: docker-compose up -d
|
||||||
|
#
|
||||||
|
# 包含服务:
|
||||||
|
# - MySQL 8.0 (数据库,包含初始化SQL)
|
||||||
|
# - Redis 6.2 (缓存)
|
||||||
|
# - Weaviate (向量数据库)
|
||||||
|
# - MinIO (对象存储)
|
||||||
|
# - RuoYi-Backend (后端服务)
|
||||||
|
# - RuoYi-Admin (管理端前端)
|
||||||
|
# - RuoYi-Web (用户端前端)
|
||||||
|
#
|
||||||
|
# 镜像仓库地址: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ==================== MySQL 数据库 ====================
|
||||||
|
mysql:
|
||||||
|
# 阿里云镜像地址(包含初始化SQL)
|
||||||
|
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/mysql:v3
|
||||||
|
container_name: ruoyi-ai-mysql
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "23306:3306"
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: root
|
||||||
|
MYSQL_DATABASE: ruoyi-ai-agent
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
volumes:
|
||||||
|
- mysql-data:/var/lib/mysql
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 10
|
||||||
|
start_period: 60s
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# ==================== Redis 缓存 ====================
|
||||||
|
redis:
|
||||||
|
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/redis:6.2
|
||||||
|
container_name: ruoyi-ai-redis
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "26379:6379"
|
||||||
|
volumes:
|
||||||
|
- redis-data:/data
|
||||||
|
command: redis-server --appendonly yes
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# ==================== Weaviate 向量数据库 ====================
|
||||||
|
weaviate:
|
||||||
|
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/weaviate:1.30.0
|
||||||
|
container_name: ruoyi-ai-weaviate
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "28080:8080"
|
||||||
|
environment:
|
||||||
|
QUERY_DEFAULTS_LIMIT: 25
|
||||||
|
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: true
|
||||||
|
PERSISTENCE_DATA_PATH: /var/lib/weaviate
|
||||||
|
DEFAULT_VECTORIZER_MODULE: none
|
||||||
|
ENABLE_MODULES: text2vec-cohere,text2vec-huggingface,text2vec-palm,text2vec-openai,generative-openai,generative-cohere,generative-palm,ref2vec-centroid,reranker-cohere,qna-openai
|
||||||
|
CLUSTER_HOSTNAME: node1
|
||||||
|
volumes:
|
||||||
|
- weaviate-data:/var/lib/weaviate
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# ==================== MinIO 对象存储 ====================
|
||||||
|
minio:
|
||||||
|
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/minio:latest
|
||||||
|
container_name: ruoyi-ai-minio
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "29000:9000"
|
||||||
|
- "29090:9090"
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ruoyi
|
||||||
|
MINIO_ROOT_PASSWORD: ruoyi123
|
||||||
|
volumes:
|
||||||
|
- minio-data:/data
|
||||||
|
command: server /data --console-address ":9090"
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# ==================== RuoYi-AI 后端服务 ====================
|
||||||
|
backend:
|
||||||
|
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/ruoyi-ai-backend:latest
|
||||||
|
container_name: ruoyi-ai-backend
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "26039:6039"
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
# MySQL 配置
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_PRIMARY: master
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_DRIVERCLASSNAME: com.mysql.cj.jdbc.Driver
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_URL: jdbc:mysql://mysql:3306/ruoyi-ai-agent?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_USERNAME: root
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_PASSWORD: root
|
||||||
|
# Redis 配置
|
||||||
|
SPRING_DATA_REDIS_HOST: redis
|
||||||
|
SPRING_DATA_REDIS_PORT: 6379
|
||||||
|
SPRING_DATA_REDIS_DATABASE: 0
|
||||||
|
# 日志配置
|
||||||
|
LOGGING_LEVEL_ORG_RUOYI: info
|
||||||
|
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: warn
|
||||||
|
SYS_UPLOAD_PATH: /ruoyi/upload
|
||||||
|
volumes:
|
||||||
|
- logs-data:/ruoyi/server/logs
|
||||||
|
- upload-data:/ruoyi/upload
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_started
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# ==================== RuoYi-AI 管理端前端 ====================
|
||||||
|
admin-frontend:
|
||||||
|
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/ruoyi-ai-admin:latest
|
||||||
|
container_name: ruoyi-ai-admin
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "25666:5666"
|
||||||
|
environment:
|
||||||
|
# 后端 API 地址 - 运行时动态配置(无需重新构建镜像)
|
||||||
|
# nginx upstream 配置不需要 http:// 前缀,直接使用 host:port
|
||||||
|
UPSTREAM_HOST: backend:6039
|
||||||
|
# 资源限制 - 防止 CPU 和内存耗尽
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: '2'
|
||||||
|
memory: 3G
|
||||||
|
reservations:
|
||||||
|
cpus: '1'
|
||||||
|
memory: 1G
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# ==================== RuoYi-AI 用户端前端 ====================
|
||||||
|
web-frontend:
|
||||||
|
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/ruoyi-ai-web:latest
|
||||||
|
container_name: ruoyi-ai-web
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "25137:5137"
|
||||||
|
environment:
|
||||||
|
UPSTREAM_URL: http://backend:6039
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# ==================== 网络配置 ====================
|
||||||
|
networks:
|
||||||
|
ruoyi-net:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
# ==================== 数据卷配置 ====================
|
||||||
|
volumes:
|
||||||
|
mysql-data:
|
||||||
|
redis-data:
|
||||||
|
weaviate-data:
|
||||||
|
minio-data:
|
||||||
|
logs-data:
|
||||||
|
upload-data:
|
||||||
144
docs/docker/ruoyi-ai/docker-compose.yaml
Normal file
144
docs/docker/ruoyi-ai/docker-compose.yaml
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
# RuoYi-AI 一键启动后端服务
|
||||||
|
# 使用方式: docker-compose up -d --build
|
||||||
|
#
|
||||||
|
# 包含服务:
|
||||||
|
# - MySQL 8.0 (数据库)
|
||||||
|
# - Redis 6.2 (缓存)
|
||||||
|
# - Weaviate (向量数据库)
|
||||||
|
# - MinIO (对象存储)
|
||||||
|
# - RuoYi-Backend (后端服务,源码编译)
|
||||||
|
|
||||||
|
services:
|
||||||
|
# MySQL 数据库
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0.33
|
||||||
|
container_name: ruoyi-ai-mysql
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "23306:3306"
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: root
|
||||||
|
MYSQL_DATABASE: ruoyi-ai-agent
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
volumes:
|
||||||
|
- ./docs/script/docker/mysql/init/init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro
|
||||||
|
- ./docs/script/sql/ruoyi-ai-v3_mysql8.sql:/docker-entrypoint-initdb.d/ruoyi-ai-v3_mysql8.sql:ro
|
||||||
|
- mysql-data:/var/lib/mysql
|
||||||
|
command:
|
||||||
|
--default-authentication-plugin=mysql_native_password
|
||||||
|
--character-set-server=utf8mb4
|
||||||
|
--collation-server=utf8mb4_general_ci
|
||||||
|
--explicit_defaults_for_timestamp=true
|
||||||
|
--lower_case_table_names=1
|
||||||
|
--skip-ssl
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 10
|
||||||
|
start_period: 60s
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# Redis 缓存
|
||||||
|
redis:
|
||||||
|
image: redis:6.2
|
||||||
|
container_name: ruoyi-ai-redis
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "6379:6379"
|
||||||
|
volumes:
|
||||||
|
- redis-data:/data
|
||||||
|
command: redis-server --appendonly yes
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# Weaviate 向量数据库
|
||||||
|
weaviate:
|
||||||
|
image: semitechnologies/weaviate:1.30.0
|
||||||
|
container_name: ruoyi-ai-weaviate
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "28080:8080"
|
||||||
|
environment:
|
||||||
|
QUERY_DEFAULTS_LIMIT: 25
|
||||||
|
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: true
|
||||||
|
PERSISTENCE_DATA_PATH: /var/lib/weaviate
|
||||||
|
DEFAULT_VECTORIZER_MODULE: none
|
||||||
|
ENABLE_MODULES: text2vec-cohere,text2vec-huggingface,text2vec-palm,text2vec-openai,generative-openai,generative-cohere,generative-palm,ref2vec-centroid,reranker-cohere,qna-openai
|
||||||
|
CLUSTER_HOSTNAME: node1
|
||||||
|
volumes:
|
||||||
|
- weaviate-data:/var/lib/weaviate
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# MinIO 对象存储
|
||||||
|
minio:
|
||||||
|
image: minio/minio
|
||||||
|
container_name: ruoyi-ai-minio
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "9000:9000"
|
||||||
|
- "9090:9090"
|
||||||
|
environment:
|
||||||
|
MINIO_ROOT_USER: ruoyi
|
||||||
|
MINIO_ROOT_PASSWORD: ruoyi123
|
||||||
|
volumes:
|
||||||
|
- minio-data:/data
|
||||||
|
command: server /data --console-address ":9090"
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
# RuoYi-AI 后端服务 (源码编译)
|
||||||
|
backend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile.backend
|
||||||
|
container_name: ruoyi-ai-backend
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "26039:6039"
|
||||||
|
environment:
|
||||||
|
TZ: Asia/Shanghai
|
||||||
|
# MySQL 配置
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_PRIMARY: master
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_DRIVERCLASSNAME: com.mysql.cj.jdbc.Driver
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_URL: jdbc:mysql://mysql:3306/ruoyi-ai-agent?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_USERNAME: root
|
||||||
|
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_PASSWORD: root
|
||||||
|
# Redis 配置
|
||||||
|
SPRING_DATA_REDIS_HOST: redis
|
||||||
|
SPRING_DATA_REDIS_PORT: 6379
|
||||||
|
SPRING_DATA_REDIS_DATABASE: 0
|
||||||
|
# 日志配置
|
||||||
|
LOGGING_LEVEL_ORG_RUOYI: info
|
||||||
|
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: warn
|
||||||
|
SYS_UPLOAD_PATH: /ruoyi/upload # 新增:对应 sys.upload.path
|
||||||
|
volumes:
|
||||||
|
- logs-data:/ruoyi/server/logs
|
||||||
|
- upload-data:/ruoyi/upload
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_started
|
||||||
|
networks:
|
||||||
|
- ruoyi-net
|
||||||
|
|
||||||
|
networks:
|
||||||
|
ruoyi-net:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
# 数据卷 支持手动指定 空为默认值
|
||||||
|
volumes:
|
||||||
|
mysql-data:
|
||||||
|
redis-data:
|
||||||
|
weaviate-data:
|
||||||
|
minio-data:
|
||||||
|
logs-data:
|
||||||
|
upload-data:
|
||||||
25
docs/docker/weaviate/docker-compose.yml
Normal file
25
docs/docker/weaviate/docker-compose.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
services:
|
||||||
|
weaviate:
|
||||||
|
command:
|
||||||
|
- --host
|
||||||
|
- 0.0.0.0
|
||||||
|
- --port
|
||||||
|
- '6038'
|
||||||
|
- --scheme
|
||||||
|
- http
|
||||||
|
image: semitechnologies/weaviate:1.19.7
|
||||||
|
ports:
|
||||||
|
- 6038:6038
|
||||||
|
- 50051:50051
|
||||||
|
volumes:
|
||||||
|
- weaviate_data:/var/lib/weaviate
|
||||||
|
environment:
|
||||||
|
QUERY_DEFAULTS_LIMIT: 25
|
||||||
|
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
|
||||||
|
PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
|
||||||
|
DEFAULT_VECTORIZER_MODULE: 'none'
|
||||||
|
CLUSTER_HOSTNAME: 'node1'
|
||||||
|
volumes:
|
||||||
|
weaviate_data:
|
||||||
|
...
|
||||||
10
docs/script/docker/mysql/init/init-db.sh
Normal file
10
docs/script/docker/mysql/init/init-db.sh
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 数据库初始化脚本
|
||||||
|
# 使用 --force 参数确保即使出错也继续执行
|
||||||
|
|
||||||
|
echo "开始初始化数据库..."
|
||||||
|
|
||||||
|
# 使用 --force 参数忽略错误继续执行
|
||||||
|
mysql -uroot -proot ruoyi-ai-agent --force < /docker-entrypoint-initdb.d/ruoyi-ai-v3_mysql8.sql
|
||||||
|
|
||||||
|
echo "数据库初始化完成"
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
worker_processes 1;
|
|
||||||
|
|
||||||
error_log /var/log/nginx/error.log warn;
|
|
||||||
pid /var/run/nginx.pid;
|
|
||||||
|
|
||||||
events {
|
|
||||||
# 可以根据业务并发量适当调高
|
|
||||||
worker_connections 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
http {
|
|
||||||
include mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
# 高效传输文件
|
|
||||||
sendfile on;
|
|
||||||
# 长连接超时时间
|
|
||||||
keepalive_timeout 65;
|
|
||||||
# 单连接最大请求数,提高长连接复用率
|
|
||||||
keepalive_requests 100000;
|
|
||||||
# 限制body大小
|
|
||||||
client_max_body_size 100m;
|
|
||||||
client_header_buffer_size 32k;
|
|
||||||
client_body_buffer_size 512k;
|
|
||||||
# 开启静态资源压缩
|
|
||||||
gzip_static on;
|
|
||||||
# 连接数限制 (防御类配置) 10m 一般够用了,能存储上万 IP 的计数
|
|
||||||
limit_conn_zone $binary_remote_addr zone=perip:10m;
|
|
||||||
limit_conn_zone $server_name zone=perserver:10m;
|
|
||||||
# 隐藏 nginx 版本号,防止暴露版本信息
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
||||||
'$status $body_bytes_sent "$http_referer" '
|
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
|
||||||
|
|
||||||
access_log /var/log/nginx/access.log main;
|
|
||||||
|
|
||||||
upstream server {
|
|
||||||
ip_hash;
|
|
||||||
server 127.0.0.1:8080;
|
|
||||||
server 127.0.0.1:8081;
|
|
||||||
}
|
|
||||||
|
|
||||||
upstream monitor-admin {
|
|
||||||
server 127.0.0.1:9090;
|
|
||||||
}
|
|
||||||
|
|
||||||
upstream snailjob-server {
|
|
||||||
server 127.0.0.1:8800;
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name localhost;
|
|
||||||
|
|
||||||
# https配置参考 start
|
|
||||||
#listen 443 ssl;
|
|
||||||
|
|
||||||
# 证书直接存放 /docker/nginx/cert/ 目录下即可 更改证书名称即可 无需更改证书路径
|
|
||||||
#ssl on;
|
|
||||||
#ssl_certificate /etc/nginx/cert/xxx.local.crt; # /etc/nginx/cert/ 为docker映射路径 不允许更改
|
|
||||||
#ssl_certificate_key /etc/nginx/cert/xxx.local.key; # /etc/nginx/cert/ 为docker映射路径 不允许更改
|
|
||||||
#ssl_session_timeout 5m;
|
|
||||||
#ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
|
|
||||||
#ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1 TLSv1;
|
|
||||||
#ssl_prefer_server_ciphers on;
|
|
||||||
# https配置参考 end
|
|
||||||
|
|
||||||
# 演示环境配置 拦截除 GET POST 之外的所有请求
|
|
||||||
# if ($request_method !~* GET|POST) {
|
|
||||||
# rewrite ^/(.*)$ /403;
|
|
||||||
# }
|
|
||||||
|
|
||||||
# location = /403 {
|
|
||||||
# default_type application/json;
|
|
||||||
# return 200 '{"msg":"演示模式,不允许操作","code":500}';
|
|
||||||
# }
|
|
||||||
|
|
||||||
# 限制外网访问内网 actuator 相关路径
|
|
||||||
location ~ ^(/[^/]*)?/actuator.*(/.*)?$ {
|
|
||||||
return 403;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
root /usr/share/nginx/html; # docker映射路径 不允许更改
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
index index.html index.htm;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /prod-api/ {
|
|
||||||
# 设置客户端请求头中的 Host 信息(保持原始 Host)
|
|
||||||
proxy_set_header Host $http_host;
|
|
||||||
# 获取客户端真实 IP
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
# 自定义头 REMOTE-HOST,记录客户端 IP
|
|
||||||
proxy_set_header REMOTE-HOST $remote_addr;
|
|
||||||
# 获取完整的客户端 IP 链(经过多级代理时)
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
# 设置后端响应超时时间(这里是 24 小时,适合长连接/SSE)
|
|
||||||
proxy_read_timeout 86400s;
|
|
||||||
# SSE (Server-Sent Events) 与 WebSocket 支持参数
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
# 禁用代理缓冲,数据直接传给客户端
|
|
||||||
proxy_buffering off;
|
|
||||||
# 禁用代理缓存
|
|
||||||
proxy_cache off;
|
|
||||||
# 按 IP 限制连接数(防 CC 攻击) 小型站:10~20 就够 中型站:50~100
|
|
||||||
limit_conn perip 20;
|
|
||||||
|
|
||||||
# 按 Server 限制总并发连接数 根据服务器的最大并发处理能力来定 太小会限制合法用户访问,太大会占满服务器资源
|
|
||||||
limit_conn perserver 500;
|
|
||||||
proxy_pass http://server/;
|
|
||||||
}
|
|
||||||
|
|
||||||
# https 会拦截内链所有的 http 请求 造成功能无法使用
|
|
||||||
# 解决方案1 将 admin 服务 也配置成 https
|
|
||||||
# 解决方案2 将菜单配置为外链访问 走独立页面 http 访问
|
|
||||||
location /admin/ {
|
|
||||||
# 设置客户端请求头中的 Host 信息(保持原始 Host)
|
|
||||||
proxy_set_header Host $http_host;
|
|
||||||
# 获取客户端真实 IP
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
# 自定义头 REMOTE-HOST,记录客户端 IP
|
|
||||||
proxy_set_header REMOTE-HOST $remote_addr;
|
|
||||||
# 获取完整的客户端 IP 链(经过多级代理时)
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
# 禁用代理缓冲,数据直接传给客户端
|
|
||||||
proxy_buffering off;
|
|
||||||
# 禁用代理缓存
|
|
||||||
proxy_cache off;
|
|
||||||
proxy_pass http://monitor-admin/admin/;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /snail-job/ {
|
|
||||||
# 设置客户端请求头中的 Host 信息(保持原始 Host)
|
|
||||||
proxy_set_header Host $http_host;
|
|
||||||
# 获取客户端真实 IP
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
# 自定义头 REMOTE-HOST,记录客户端 IP
|
|
||||||
proxy_set_header REMOTE-HOST $remote_addr;
|
|
||||||
# 获取完整的客户端 IP 链(经过多级代理时)
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
# SSE (Server-Sent Events) 与 WebSocket 支持参数
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header Connection "upgrade";
|
|
||||||
# 禁用代理缓冲,直接传输给客户端
|
|
||||||
proxy_buffering off;
|
|
||||||
# 禁用代理缓存
|
|
||||||
proxy_cache off;
|
|
||||||
proxy_pass http://snailjob-server/snail-job/;
|
|
||||||
}
|
|
||||||
|
|
||||||
error_page 500 502 503 504 /50x.html;
|
|
||||||
location = /50x.html {
|
|
||||||
root html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# redis 密码
|
|
||||||
requirepass ruoyi123
|
|
||||||
|
|
||||||
# key 监听器配置
|
|
||||||
# notify-keyspace-events Ex
|
|
||||||
|
|
||||||
# 配置持久化文件存储路径
|
|
||||||
dir /redis/data
|
|
||||||
# 配置rdb
|
|
||||||
# 15分钟内有至少1个key被更改则进行快照
|
|
||||||
save 900 1
|
|
||||||
# 5分钟内有至少10个key被更改则进行快照
|
|
||||||
save 300 10
|
|
||||||
# 1分钟内有至少10000个key被更改则进行快照
|
|
||||||
save 60 10000
|
|
||||||
# 开启压缩
|
|
||||||
rdbcompression yes
|
|
||||||
# rdb文件名 用默认的即可
|
|
||||||
dbfilename dump.rdb
|
|
||||||
|
|
||||||
# 开启aof
|
|
||||||
appendonly yes
|
|
||||||
# 文件名
|
|
||||||
appendfilename "appendonly.aof"
|
|
||||||
# 持久化策略,no:不同步,everysec:每秒一次,always:总是同步,速度比较慢
|
|
||||||
# appendfsync always
|
|
||||||
appendfsync everysec
|
|
||||||
# appendfsync no
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
数据目录 请执行 `chmod 777 /docker/redis/data` 赋予读写权限 否则将无法写入数据
|
|
||||||
File diff suppressed because it is too large
Load Diff
41
pom.xml
41
pom.xml
@@ -54,11 +54,13 @@
|
|||||||
<!-- Jackson XML -->
|
<!-- Jackson XML -->
|
||||||
<jackson-dataformat-xml.version>2.18.2</jackson-dataformat-xml.version>
|
<jackson-dataformat-xml.version>2.18.2</jackson-dataformat-xml.version>
|
||||||
<!-- AI 相关依赖 -->
|
<!-- AI 相关依赖 -->
|
||||||
<langchain4j.version>1.11.0</langchain4j.version>
|
<langchain4j.version>1.13.0</langchain4j.version>
|
||||||
<langchain4j.community.version>1.11.0-beta19</langchain4j.community.version>
|
<langchain4j.community.version>1.13.0-beta23</langchain4j.community.version>
|
||||||
<langgraph4j.version>1.5.3</langgraph4j.version>
|
<langgraph4j.version>1.5.3</langgraph4j.version>
|
||||||
<weaviate.version>1.19.6</weaviate.version>
|
<weaviate.version>1.19.6</weaviate.version>
|
||||||
<dify.version>1.0.7</dify.version>
|
<dify.version>1.0.7</dify.version>
|
||||||
|
<!-- gRPC 版本 - 解决 Milvus SDK 依赖冲突 -->
|
||||||
|
<grpc.version>1.62.2</grpc.version>
|
||||||
<!-- Apache Commons Compress - 用于POI处理ZIP格式 -->
|
<!-- Apache Commons Compress - 用于POI处理ZIP格式 -->
|
||||||
<commons-compress.version>1.27.1</commons-compress.version>
|
<commons-compress.version>1.27.1</commons-compress.version>
|
||||||
|
|
||||||
@@ -129,6 +131,15 @@
|
|||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- gRPC BOM - 解决 Milvus SDK 依赖冲突,强制统一版本 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.grpc</groupId>
|
||||||
|
<artifactId>grpc-bom</artifactId>
|
||||||
|
<version>${grpc.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- hutool 的依赖配置-->
|
<!-- hutool 的依赖配置-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cn.hutool</groupId>
|
<groupId>cn.hutool</groupId>
|
||||||
@@ -352,24 +363,12 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-job</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.ruoyi</groupId>
|
<groupId>org.ruoyi</groupId>
|
||||||
<artifactId>ruoyi-generator</artifactId>
|
<artifactId>ruoyi-generator</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-demo</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.ruoyi</groupId>
|
<groupId>org.ruoyi</groupId>
|
||||||
<artifactId>ruoyi-chat</artifactId>
|
<artifactId>ruoyi-chat</artifactId>
|
||||||
@@ -383,13 +382,6 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 微信模块 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-wechat</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- AI流程编排模块 -->
|
<!-- AI流程编排模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.ruoyi</groupId>
|
<groupId>org.ruoyi</groupId>
|
||||||
@@ -397,13 +389,6 @@
|
|||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 企业微信SDK -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.binarywang</groupId>
|
|
||||||
<artifactId>weixin-java-cp</artifactId>
|
|
||||||
<version>${weixin-java-cp.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Jackson XML -->
|
<!-- Jackson XML -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
<groupId>com.fasterxml.jackson.dataformat</groupId>
|
||||||
|
|||||||
@@ -70,23 +70,12 @@
|
|||||||
<artifactId>ruoyi-system</artifactId>
|
<artifactId>ruoyi-system</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-job</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- 代码生成-->
|
<!-- 代码生成-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.ruoyi</groupId>
|
<groupId>org.ruoyi</groupId>
|
||||||
<artifactId>ruoyi-generator</artifactId>
|
<artifactId>ruoyi-generator</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- demo模块 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-demo</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.ruoyi</groupId>
|
<groupId>org.ruoyi</groupId>
|
||||||
<artifactId>ruoyi-chat</artifactId>
|
<artifactId>ruoyi-chat</artifactId>
|
||||||
@@ -98,12 +87,6 @@
|
|||||||
<artifactId>ruoyi-workflow</artifactId>
|
<artifactId>ruoyi-workflow</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- 微信模块 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-wechat</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- AI流程编排模块 -->
|
<!-- AI流程编排模块 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.ruoyi</groupId>
|
<groupId>org.ruoyi</groupId>
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动程序
|
* 启动程序
|
||||||
*
|
*
|
||||||
@@ -13,10 +16,66 @@ import org.springframework.boot.context.metrics.buffering.BufferingApplicationSt
|
|||||||
public class RuoYiAIApplication {
|
public class RuoYiAIApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
killPortProcess(6039);
|
||||||
SpringApplication application = new SpringApplication(RuoYiAIApplication.class);
|
SpringApplication application = new SpringApplication(RuoYiAIApplication.class);
|
||||||
application.setApplicationStartup(new BufferingApplicationStartup(2048));
|
application.setApplicationStartup(new BufferingApplicationStartup(2048));
|
||||||
application.run(args);
|
application.run(args);
|
||||||
System.out.println("(♥◠‿◠)ノ゙ RuoYi-AI启动成功 ლ(´ڡ`ლ)゙");
|
System.out.println("(♥◠‿◠)ノ゙ RuoYi-AI启动成功 ლ(´ڡ`ლ)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查并终止占用指定端口的进程
|
||||||
|
*
|
||||||
|
* @param port 端口号
|
||||||
|
*/
|
||||||
|
private static void killPortProcess(int port) {
|
||||||
|
try {
|
||||||
|
if (!isPortInUse(port)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
System.out.println("端口 " + port + " 已被占用,正在查找并终止进程...");
|
||||||
|
|
||||||
|
ProcessBuilder pb = new ProcessBuilder("netstat", "-ano");
|
||||||
|
Process process = pb.start();
|
||||||
|
java.io.BufferedReader reader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(process.getInputStream()));
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.contains(":" + port + " ") && line.contains("LISTENING")) {
|
||||||
|
String[] parts = line.trim().split("\\s+");
|
||||||
|
String pid = parts[parts.length - 1];
|
||||||
|
System.out.println("找到占用端口 " + port + " 的进程 PID: " + pid + ",正在终止...");
|
||||||
|
|
||||||
|
ProcessBuilder killPb = new ProcessBuilder("taskkill", "/F", "/PID", pid);
|
||||||
|
Process killProcess = killPb.start();
|
||||||
|
int exitCode = killProcess.waitFor();
|
||||||
|
if (exitCode == 0) {
|
||||||
|
System.out.println("进程 " + pid + " 已成功终止");
|
||||||
|
} else {
|
||||||
|
System.out.println("终止进程 " + pid + " 失败,exitCode: " + exitCode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待一小段时间确保端口释放
|
||||||
|
Thread.sleep(500);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println("检查/终止端口进程时发生异常: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查端口是否被占用
|
||||||
|
*/
|
||||||
|
private static boolean isPortInUse(int port) {
|
||||||
|
try (ServerSocket socket = new ServerSocket()) {
|
||||||
|
socket.bind(new InetSocketAddress(port));
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,10 +80,16 @@ public class AuthController {
|
|||||||
// 授权类型和客户端id
|
// 授权类型和客户端id
|
||||||
String clientId = loginBody.getClientId();
|
String clientId = loginBody.getClientId();
|
||||||
String grantType = loginBody.getGrantType();
|
String grantType = loginBody.getGrantType();
|
||||||
|
log.info("登录请求 - clientId: {}, grantType: {}", clientId, grantType);
|
||||||
SysClientVo client = clientService.queryByClientId(clientId);
|
SysClientVo client = clientService.queryByClientId(clientId);
|
||||||
|
log.info("查询客户端结果 - client: {}, grantType: {}", client, client != null ? client.getGrantType() : "null");
|
||||||
// 查询不到 client 或 client 内不包含 grantType
|
// 查询不到 client 或 client 内不包含 grantType
|
||||||
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
|
if (ObjectUtil.isNull(client)) {
|
||||||
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
|
log.info("客户端id: {} 不存在!", clientId);
|
||||||
|
return R.fail(MessageUtils.message("auth.grant.type.error"));
|
||||||
|
}
|
||||||
|
if (!StringUtils.contains(client.getGrantType(), grantType)) {
|
||||||
|
log.info("客户端id: {} 认证类型:{} 不匹配! 数据库grantType: {}", clientId, grantType, client.getGrantType());
|
||||||
return R.fail(MessageUtils.message("auth.grant.type.error"));
|
return R.fail(MessageUtils.message("auth.grant.type.error"));
|
||||||
} else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
|
} else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
|
||||||
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
|
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
|
||||||
|
|||||||
@@ -58,15 +58,15 @@ spring:
|
|||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
driverClassName: com.mysql.cj.jdbc.Driver
|
||||||
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||||
url: jdbc:mysql://127.0.0.1:3306/ruoyi_ai_agent?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
url: jdbc:mysql://127.0.0.1:3306/ruoyi-ai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||||
username: root
|
username: root
|
||||||
password: root
|
password: root
|
||||||
agent:
|
# agent:
|
||||||
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
# url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||||
# url: jdbc:mysql://localhost:3306/agent_db
|
# # url: jdbc:mysql://localhost:3306/agent_db
|
||||||
username: root
|
# username: root
|
||||||
password: root
|
# password: root
|
||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
# driverClassName: com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
hikari:
|
hikari:
|
||||||
# 最大连接池数量
|
# 最大连接池数量
|
||||||
@@ -268,4 +268,4 @@ justauth:
|
|||||||
client-secret: 1f7d08**********5b7**********29e
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
redirect-uri: ${justauth.address}/social-callback?source=gitea
|
redirect-uri: ${justauth.address}/social-callback?source=gitea
|
||||||
|
|
||||||
AGENT_ALLOWED_TABLES: "abtest_rule,abtest_project,agent_ban_log,agent_ban_logs,agent_install_sub_task,agent_install_sum_task,agent_install_task"
|
AGENT_ALLOWED_TABLES: ""
|
||||||
|
|||||||
272
ruoyi-admin/src/main/resources/application-prod.yml
Normal file
272
ruoyi-admin/src/main/resources/application-prod.yml
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
|
||||||
|
--- # 监控中心配置
|
||||||
|
spring.boot.admin.client:
|
||||||
|
# 增加客户端开关
|
||||||
|
enabled: false
|
||||||
|
url: http://localhost:9090/admin
|
||||||
|
instance:
|
||||||
|
service-host-type: IP
|
||||||
|
metadata:
|
||||||
|
username: ${spring.boot.admin.client.username}
|
||||||
|
userpassword: ${spring.boot.admin.client.password}
|
||||||
|
username: @monitor.username@
|
||||||
|
password: @monitor.password@
|
||||||
|
|
||||||
|
--- # mcp配置信息
|
||||||
|
mcp:
|
||||||
|
sse:
|
||||||
|
enabled: false
|
||||||
|
url: http://localhost:8085/sse
|
||||||
|
|
||||||
|
--- # 上传文件地址
|
||||||
|
sys:
|
||||||
|
upload:
|
||||||
|
path: D:\\DownLoad
|
||||||
|
|
||||||
|
--- # snail-job 配置
|
||||||
|
snail-job:
|
||||||
|
enabled: false
|
||||||
|
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
||||||
|
group: "ruoyi_group"
|
||||||
|
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
|
||||||
|
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
||||||
|
server:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 17888
|
||||||
|
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
|
||||||
|
namespace: ${spring.profiles.active}
|
||||||
|
# 随主应用端口漂移
|
||||||
|
port: 2${server.port}
|
||||||
|
# 客户端ip指定
|
||||||
|
host:
|
||||||
|
|
||||||
|
--- # 数据源配置
|
||||||
|
spring:
|
||||||
|
datasource:
|
||||||
|
type: com.zaxxer.hikari.HikariDataSource
|
||||||
|
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
||||||
|
dynamic:
|
||||||
|
# 性能分析插件(有性能损耗 不建议生产环境使用)
|
||||||
|
p6spy: true
|
||||||
|
# 设置默认的数据源或者数据源组,默认值即为 master
|
||||||
|
primary: master
|
||||||
|
# 严格模式 匹配不到数据源则报错
|
||||||
|
strict: true
|
||||||
|
datasource:
|
||||||
|
# 主库数据源
|
||||||
|
master:
|
||||||
|
type: ${spring.datasource.type}
|
||||||
|
driverClassName: com.mysql.cj.jdbc.Driver
|
||||||
|
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||||
|
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/ruoyi-ai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||||
|
username: root
|
||||||
|
password: root
|
||||||
|
# agent:
|
||||||
|
# url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||||
|
# # url: jdbc:mysql://localhost:3306/agent_db
|
||||||
|
# username: root
|
||||||
|
# password: root
|
||||||
|
# driverClassName: com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
|
hikari:
|
||||||
|
# 最大连接池数量
|
||||||
|
maxPoolSize: 20
|
||||||
|
# 最小空闲线程数量
|
||||||
|
minIdle: 10
|
||||||
|
# 配置获取连接等待超时的时间
|
||||||
|
connectionTimeout: 30000
|
||||||
|
# 校验超时时间
|
||||||
|
validationTimeout: 5000
|
||||||
|
# 空闲连接存活最大时间,默认10分钟
|
||||||
|
idleTimeout: 600000
|
||||||
|
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
||||||
|
maxLifetime: 1800000
|
||||||
|
# 多久检查一次连接的活性
|
||||||
|
keepaliveTime: 30000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- # 上传文件地址
|
||||||
|
sys:
|
||||||
|
upload:
|
||||||
|
path: D:\\DownLoad
|
||||||
|
|
||||||
|
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||||
|
spring.data:
|
||||||
|
redis:
|
||||||
|
# 地址
|
||||||
|
host: localhost
|
||||||
|
# 端口,默认为6379
|
||||||
|
port: 6379
|
||||||
|
# 数据库索引
|
||||||
|
database: 0
|
||||||
|
# redis 密码必须配置
|
||||||
|
# password: 123456
|
||||||
|
# 连接超时时间
|
||||||
|
timeout: 10s
|
||||||
|
# 是否开启ssl
|
||||||
|
ssl.enabled: false
|
||||||
|
|
||||||
|
# redisson 配置
|
||||||
|
redisson:
|
||||||
|
# redis key前缀
|
||||||
|
keyPrefix:
|
||||||
|
# 线程池数量
|
||||||
|
threads: 4
|
||||||
|
# Netty线程池数量
|
||||||
|
nettyThreads: 8
|
||||||
|
# 单节点配置
|
||||||
|
singleServerConfig:
|
||||||
|
# 客户端名称 不能用中文
|
||||||
|
clientName: ruoyi-ai
|
||||||
|
# 最小空闲连接数
|
||||||
|
connectionMinimumIdleSize: 8
|
||||||
|
# 连接池大小
|
||||||
|
connectionPoolSize: 32
|
||||||
|
# 连接空闲超时,单位:毫秒
|
||||||
|
idleConnectionTimeout: 10000
|
||||||
|
# 命令等待超时,单位:毫秒
|
||||||
|
timeout: 3000
|
||||||
|
# 发布和订阅连接池大小
|
||||||
|
subscriptionConnectionPoolSize: 50
|
||||||
|
|
||||||
|
--- # mail 邮件发送
|
||||||
|
mail:
|
||||||
|
enabled: false
|
||||||
|
host: smtp.163.com
|
||||||
|
port: 465
|
||||||
|
# 是否需要用户名密码验证
|
||||||
|
auth: true
|
||||||
|
# 发送方,遵循RFC-822标准
|
||||||
|
from: xxx@163.com
|
||||||
|
# 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
|
||||||
|
user: xxx@163.com
|
||||||
|
# 密码(注意,某些邮箱需要为SMTP服务单独设置密码,详情查看相关帮助)
|
||||||
|
pass: xxxxxxxxxx
|
||||||
|
# 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
|
||||||
|
starttlsEnable: true
|
||||||
|
# 使用SSL安全连接
|
||||||
|
sslEnable: true
|
||||||
|
# SMTP超时时长,单位毫秒,缺省值不超时
|
||||||
|
timeout: 0
|
||||||
|
# Socket连接超时值,单位毫秒,缺省值不超时
|
||||||
|
connectionTimeout: 0
|
||||||
|
|
||||||
|
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
|
||||||
|
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
|
||||||
|
sms:
|
||||||
|
# 配置源类型用于标定配置来源(interface,yaml)
|
||||||
|
config-type: yaml
|
||||||
|
# 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
|
||||||
|
restricted: true
|
||||||
|
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
|
||||||
|
minute-max: 1
|
||||||
|
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
|
||||||
|
account-max: 30
|
||||||
|
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
|
||||||
|
blends:
|
||||||
|
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
|
||||||
|
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
|
||||||
|
config1:
|
||||||
|
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
|
||||||
|
supplier: alibaba
|
||||||
|
# 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
|
||||||
|
access-key-id: 您的accessKey
|
||||||
|
# 称为accessSecret有些称之为apiSecret
|
||||||
|
access-key-secret: 您的accessKeySecret
|
||||||
|
signature: 您的短信签名
|
||||||
|
sdk-app-id: 您的sdkAppId
|
||||||
|
config2:
|
||||||
|
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
|
||||||
|
supplier: tencent
|
||||||
|
access-key-id: 您的accessKey
|
||||||
|
access-key-secret: 您的accessKeySecret
|
||||||
|
signature: 您的短信签名
|
||||||
|
sdk-app-id: 您的sdkAppId
|
||||||
|
|
||||||
|
|
||||||
|
--- # 三方授权
|
||||||
|
justauth:
|
||||||
|
# 前端外网访问地址
|
||||||
|
address: http://localhost:80
|
||||||
|
type:
|
||||||
|
maxkey:
|
||||||
|
# maxkey 服务器地址
|
||||||
|
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
|
||||||
|
server-url: http://sso.maxkey.top
|
||||||
|
client-id: 876892492581044224
|
||||||
|
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=maxkey
|
||||||
|
topiam:
|
||||||
|
# topiam 服务器地址
|
||||||
|
server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
|
||||||
|
client-id: 449c4*********937************759
|
||||||
|
client-secret: ac7***********1e0************28d
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=topiam
|
||||||
|
scopes: [openid, email, phone, profile]
|
||||||
|
qq:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=qq
|
||||||
|
union-id: false
|
||||||
|
weibo:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=weibo
|
||||||
|
gitee:
|
||||||
|
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
|
||||||
|
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=gitee
|
||||||
|
dingtalk:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=dingtalk
|
||||||
|
baidu:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=baidu
|
||||||
|
csdn:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=csdn
|
||||||
|
coding:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=coding
|
||||||
|
coding-group-name: xx
|
||||||
|
oschina:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=oschina
|
||||||
|
alipay_wallet:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
|
||||||
|
alipay-public-key: MIIB**************DAQAB
|
||||||
|
wechat_open:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
|
||||||
|
wechat_mp:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
|
||||||
|
wechat_enterprise:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
|
||||||
|
agent-id: 1000002
|
||||||
|
gitlab:
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=gitlab
|
||||||
|
gitea:
|
||||||
|
# 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
|
||||||
|
# gitea 服务器地址
|
||||||
|
server-url: https://demo.gitea.com
|
||||||
|
client-id: 10**********6
|
||||||
|
client-secret: 1f7d08**********5b7**********29e
|
||||||
|
redirect-uri: ${justauth.address}/social-callback?source=gitea
|
||||||
|
|
||||||
|
AGENT_ALLOWED_TABLES: "abtest_rule,abtest_project,agent_ban_log,agent_ban_logs,agent_install_sub_task,agent_install_sum_task,agent_install_task"
|
||||||
@@ -125,9 +125,6 @@ security:
|
|||||||
- /*/api-docs/**
|
- /*/api-docs/**
|
||||||
- /warm-flow-ui/config
|
- /warm-flow-ui/config
|
||||||
- /workflow/run
|
- /workflow/run
|
||||||
- /user/qrcode
|
|
||||||
- /user/login/qrcode
|
|
||||||
- /weixin/check
|
|
||||||
# 多租户配置
|
# 多租户配置
|
||||||
tenant:
|
tenant:
|
||||||
# 是否开启
|
# 是否开启
|
||||||
@@ -206,6 +203,7 @@ springdoc:
|
|||||||
name: ageerle
|
name: ageerle
|
||||||
email: ageerle@163.com
|
email: ageerle@163.com
|
||||||
url: https://gitee.com/ageerle/ruoyi-ai
|
url: https://gitee.com/ageerle/ruoyi-ai
|
||||||
|
|
||||||
#这里定义了两个分组,可定义多个,也可以不定义
|
#这里定义了两个分组,可定义多个,也可以不定义
|
||||||
group-configs:
|
group-configs:
|
||||||
- group: 1.演示模块
|
- group: 1.演示模块
|
||||||
@@ -247,12 +245,6 @@ management:
|
|||||||
show-details: ALWAYS
|
show-details: ALWAYS
|
||||||
logfile:
|
logfile:
|
||||||
external-file: ./logs/sys-console.log
|
external-file: ./logs/sys-console.log
|
||||||
health:
|
|
||||||
# ⚠️ 禁用 Neo4j 健康检查
|
|
||||||
# Spring Boot Actuator 会自动为 Neo4j 创建健康检查器
|
|
||||||
# 这会导致应用在启动时尝试连接到 Neo4j
|
|
||||||
neo4j:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
--- # 默认/推荐使用sse推送
|
--- # 默认/推荐使用sse推送
|
||||||
sse:
|
sse:
|
||||||
@@ -268,6 +260,23 @@ websocket:
|
|||||||
# 设置访问源地址
|
# 设置访问源地址
|
||||||
allowedOrigins: '*'
|
allowedOrigins: '*'
|
||||||
|
|
||||||
|
--- # 演示模式配置
|
||||||
|
demo:
|
||||||
|
# 是否开启演示模式(开启后所有写操作将被拦截)
|
||||||
|
enabled: false
|
||||||
|
# 提示消息
|
||||||
|
message: "演示模式,不允许进行写操作"
|
||||||
|
# 排除的路径(这些路径不受演示模式限制)
|
||||||
|
excludes:
|
||||||
|
- /login
|
||||||
|
- /logout
|
||||||
|
- /register
|
||||||
|
- /captcha/**
|
||||||
|
- /auth/**
|
||||||
|
- /chat/send
|
||||||
|
- /system/session/**
|
||||||
|
- /system/message/**
|
||||||
|
|
||||||
--- # warm-flow工作流配置
|
--- # warm-flow工作流配置
|
||||||
warm-flow:
|
warm-flow:
|
||||||
# 是否开启工作流,默认true
|
# 是否开启工作流,默认true
|
||||||
@@ -283,9 +292,9 @@ warm-flow:
|
|||||||
|
|
||||||
# 向量库配置
|
# 向量库配置
|
||||||
vector-store:
|
vector-store:
|
||||||
# 向量存储类型 可选(weaviate/milvus)
|
# 向量存储类型 可选(weaviate/milvus/qdrant)
|
||||||
# 如需修改向量库类型,请修改此配置值!
|
# 如需修改向量库类型,请修改此配置值!
|
||||||
type: weaviate
|
type: milvus
|
||||||
# Weaviate配置
|
# Weaviate配置
|
||||||
weaviate:
|
weaviate:
|
||||||
protocol: http
|
protocol: http
|
||||||
@@ -295,81 +304,10 @@ vector-store:
|
|||||||
milvus:
|
milvus:
|
||||||
url: http://localhost:19530
|
url: http://localhost:19530
|
||||||
collectionname: LocalKnowledge
|
collectionname: LocalKnowledge
|
||||||
|
# Qdrant配置
|
||||||
chat:
|
qdrant:
|
||||||
memory:
|
host: localhost
|
||||||
enabled: true
|
port: 6334
|
||||||
maxMessages: 20
|
collectionname: LocalKnowledge
|
||||||
persistenceEnabled: true
|
api-key:
|
||||||
|
use-tls: false
|
||||||
# 企业微信应用
|
|
||||||
wechat:
|
|
||||||
cp:
|
|
||||||
corpId:
|
|
||||||
appConfigs:
|
|
||||||
- agentId:
|
|
||||||
secret: ''
|
|
||||||
token: ''
|
|
||||||
aesKey: ''
|
|
||||||
|
|
||||||
--- # Neo4j 知识图谱配置
|
|
||||||
neo4j:
|
|
||||||
uri: bolt://117.72.192.162:7687
|
|
||||||
username: neo4j
|
|
||||||
password: MySecurePass123!
|
|
||||||
database: neo4j
|
|
||||||
max-connection-pool-size: 50
|
|
||||||
connection-timeout-seconds: 30
|
|
||||||
|
|
||||||
# 知识图谱配置
|
|
||||||
knowledge:
|
|
||||||
graph:
|
|
||||||
# 是否启用知识图谱功能
|
|
||||||
enabled: false
|
|
||||||
# 图数据库类型: neo4j 或 apache-age
|
|
||||||
database-type: neo4j
|
|
||||||
# 是否自动创建索引
|
|
||||||
auto-create-index: true
|
|
||||||
# 批量处理大小
|
|
||||||
batch-size: 1000
|
|
||||||
# 最大重试次数
|
|
||||||
max-retry-count: 3
|
|
||||||
|
|
||||||
# 实体抽取配置
|
|
||||||
extraction:
|
|
||||||
# 置信度阈值(低于此值的实体将被过滤)
|
|
||||||
confidence-threshold: 0.7
|
|
||||||
# 最大实体数量(每个文档)
|
|
||||||
max-entities-per-doc: 100
|
|
||||||
# 最大关系数量(每个文档)
|
|
||||||
max-relations-per-doc: 200
|
|
||||||
# 文本分片大小(用于长文档)
|
|
||||||
chunk-size: 2000
|
|
||||||
# 分片重叠大小
|
|
||||||
chunk-overlap: 200
|
|
||||||
|
|
||||||
# 查询配置
|
|
||||||
query:
|
|
||||||
# 默认查询限制数量
|
|
||||||
default-limit: 100
|
|
||||||
# 最大查询限制数量
|
|
||||||
max-limit: 1000
|
|
||||||
# 路径查询最大深度
|
|
||||||
max-path-depth: 5
|
|
||||||
# 查询超时时间(秒)
|
|
||||||
timeout-seconds: 30
|
|
||||||
# 是否启用查询缓存
|
|
||||||
cache-enabled: true
|
|
||||||
# 缓存过期时间(分钟)
|
|
||||||
cache-expire-minutes: 60
|
|
||||||
|
|
||||||
--- # MCP 模块配置
|
|
||||||
app:
|
|
||||||
mcp:
|
|
||||||
client:
|
|
||||||
# 请求超时时间(秒)
|
|
||||||
request-timeout: 30
|
|
||||||
# 连接超时时间(秒)
|
|
||||||
connection-timeout: 10
|
|
||||||
# 最大重试次数
|
|
||||||
max-retries: 3
|
|
||||||
|
|||||||
590
ruoyi-admin/src/main/resources/skills/docx/SKILL.md
Normal file
590
ruoyi-admin/src/main/resources/skills/docx/SKILL.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
"""Accept all tracked changes in a DOCX file using LibreOffice.
|
||||||
|
|
||||||
|
Requires LibreOffice (soffice) to be installed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from office.soffice import get_soffice_env
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
LIBREOFFICE_PROFILE = "/tmp/libreoffice_docx_profile"
|
||||||
|
MACRO_DIR = f"{LIBREOFFICE_PROFILE}/user/basic/Standard"
|
||||||
|
|
||||||
|
ACCEPT_CHANGES_MACRO = """<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
|
||||||
|
<script:module xmlns:script="http://openoffice.org/2000/script" script:name="Module1" script:language="StarBasic">
|
||||||
|
Sub AcceptAllTrackedChanges()
|
||||||
|
Dim document As Object
|
||||||
|
Dim dispatcher As Object
|
||||||
|
|
||||||
|
document = ThisComponent.CurrentController.Frame
|
||||||
|
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
|
||||||
|
|
||||||
|
dispatcher.executeDispatch(document, ".uno:AcceptAllTrackedChanges", "", 0, Array())
|
||||||
|
ThisComponent.store()
|
||||||
|
ThisComponent.close(True)
|
||||||
|
End Sub
|
||||||
|
</script:module>"""
|
||||||
|
|
||||||
|
|
||||||
|
def accept_changes(
|
||||||
|
input_file: str,
|
||||||
|
output_file: str,
|
||||||
|
) -> tuple[None, str]:
|
||||||
|
input_path = Path(input_file)
|
||||||
|
output_path = Path(output_file)
|
||||||
|
|
||||||
|
if not input_path.exists():
|
||||||
|
return None, f"Error: Input file not found: {input_file}"
|
||||||
|
|
||||||
|
if not input_path.suffix.lower() == ".docx":
|
||||||
|
return None, f"Error: Input file is not a DOCX file: {input_file}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(input_path, output_path)
|
||||||
|
except Exception as e:
|
||||||
|
return None, f"Error: Failed to copy input file to output location: {e}"
|
||||||
|
|
||||||
|
if not _setup_libreoffice_macro():
|
||||||
|
return None, "Error: Failed to setup LibreOffice macro"
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
"soffice",
|
||||||
|
"--headless",
|
||||||
|
f"-env:UserInstallation=file://{LIBREOFFICE_PROFILE}",
|
||||||
|
"--norestore",
|
||||||
|
"vnd.sun.star.script:Standard.Module1.AcceptAllTrackedChanges?language=Basic&location=application",
|
||||||
|
str(output_path.absolute()),
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=30,
|
||||||
|
check=False,
|
||||||
|
env=get_soffice_env(),
|
||||||
|
)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return (
|
||||||
|
None,
|
||||||
|
f"Successfully accepted all tracked changes: {input_file} -> {output_file}",
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
return None, f"Error: LibreOffice failed: {result.stderr}"
|
||||||
|
|
||||||
|
return (
|
||||||
|
None,
|
||||||
|
f"Successfully accepted all tracked changes: {input_file} -> {output_file}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_libreoffice_macro() -> bool:
|
||||||
|
macro_dir = Path(MACRO_DIR)
|
||||||
|
macro_file = macro_dir / "Module1.xba"
|
||||||
|
|
||||||
|
if macro_file.exists() and "AcceptAllTrackedChanges" in macro_file.read_text():
|
||||||
|
return True
|
||||||
|
|
||||||
|
if not macro_dir.exists():
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"soffice",
|
||||||
|
"--headless",
|
||||||
|
f"-env:UserInstallation=file://{LIBREOFFICE_PROFILE}",
|
||||||
|
"--terminate_after_init",
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
timeout=10,
|
||||||
|
check=False,
|
||||||
|
env=get_soffice_env(),
|
||||||
|
)
|
||||||
|
macro_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
macro_file.write_text(ACCEPT_CHANGES_MACRO)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to setup LibreOffice macro: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Accept all tracked changes in a DOCX file"
|
||||||
|
)
|
||||||
|
parser.add_argument("input_file", help="Input DOCX file with tracked changes")
|
||||||
|
parser.add_argument(
|
||||||
|
"output_file", help="Output DOCX file (clean, no tracked changes)"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
_, message = accept_changes(args.input_file, args.output_file)
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
if "Error" in message:
|
||||||
|
raise SystemExit(1)
|
||||||
318
ruoyi-admin/src/main/resources/skills/docx/scripts/comment.py
Normal file
318
ruoyi-admin/src/main/resources/skills/docx/scripts/comment.py
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
"""Add comments to DOCX documents.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python comment.py unpacked/ 0 "Comment text"
|
||||||
|
python comment.py unpacked/ 1 "Reply text" --parent 0
|
||||||
|
|
||||||
|
Text should be pre-escaped XML (e.g., & for &, ’ for smart quotes).
|
||||||
|
|
||||||
|
After running, add markers to document.xml:
|
||||||
|
<w:commentRangeStart w:id="0"/>
|
||||||
|
... commented content ...
|
||||||
|
<w:commentRangeEnd w:id="0"/>
|
||||||
|
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:commentReference w:id="0"/></w:r>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import random
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import defusedxml.minidom
|
||||||
|
|
||||||
|
TEMPLATE_DIR = Path(__file__).parent / "templates"
|
||||||
|
NS = {
|
||||||
|
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
||||||
|
"w14": "http://schemas.microsoft.com/office/word/2010/wordml",
|
||||||
|
"w15": "http://schemas.microsoft.com/office/word/2012/wordml",
|
||||||
|
"w16cid": "http://schemas.microsoft.com/office/word/2016/wordml/cid",
|
||||||
|
"w16cex": "http://schemas.microsoft.com/office/word/2018/wordml/cex",
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMENT_XML = """\
|
||||||
|
<w:comment w:id="{id}" w:author="{author}" w:date="{date}" w:initials="{initials}">
|
||||||
|
<w:p w14:paraId="{para_id}" w14:textId="77777777">
|
||||||
|
<w:r>
|
||||||
|
<w:rPr><w:rStyle w:val="CommentReference"/></w:rPr>
|
||||||
|
<w:annotationRef/>
|
||||||
|
</w:r>
|
||||||
|
<w:r>
|
||||||
|
<w:rPr>
|
||||||
|
<w:color w:val="000000"/>
|
||||||
|
<w:sz w:val="20"/>
|
||||||
|
<w:szCs w:val="20"/>
|
||||||
|
</w:rPr>
|
||||||
|
<w:t>{text}</w:t>
|
||||||
|
</w:r>
|
||||||
|
</w:p>
|
||||||
|
</w:comment>"""
|
||||||
|
|
||||||
|
COMMENT_MARKER_TEMPLATE = """
|
||||||
|
Add to document.xml (markers must be direct children of w:p, never inside w:r):
|
||||||
|
<w:commentRangeStart w:id="{cid}"/>
|
||||||
|
<w:r>...</w:r>
|
||||||
|
<w:commentRangeEnd w:id="{cid}"/>
|
||||||
|
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:commentReference w:id="{cid}"/></w:r>"""
|
||||||
|
|
||||||
|
REPLY_MARKER_TEMPLATE = """
|
||||||
|
Nest markers inside parent {pid}'s markers (markers must be direct children of w:p, never inside w:r):
|
||||||
|
<w:commentRangeStart w:id="{pid}"/><w:commentRangeStart w:id="{cid}"/>
|
||||||
|
<w:r>...</w:r>
|
||||||
|
<w:commentRangeEnd w:id="{cid}"/><w:commentRangeEnd w:id="{pid}"/>
|
||||||
|
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:commentReference w:id="{pid}"/></w:r>
|
||||||
|
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:commentReference w:id="{cid}"/></w:r>"""
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_hex_id() -> str:
|
||||||
|
return f"{random.randint(0, 0x7FFFFFFE):08X}"
|
||||||
|
|
||||||
|
|
||||||
|
SMART_QUOTE_ENTITIES = {
|
||||||
|
"\u201c": "“",
|
||||||
|
"\u201d": "”",
|
||||||
|
"\u2018": "‘",
|
||||||
|
"\u2019": "’",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _encode_smart_quotes(text: str) -> str:
|
||||||
|
for char, entity in SMART_QUOTE_ENTITIES.items():
|
||||||
|
text = text.replace(char, entity)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def _append_xml(xml_path: Path, root_tag: str, content: str) -> None:
|
||||||
|
dom = defusedxml.minidom.parseString(xml_path.read_text(encoding="utf-8"))
|
||||||
|
root = dom.getElementsByTagName(root_tag)[0]
|
||||||
|
ns_attrs = " ".join(f'xmlns:{k}="{v}"' for k, v in NS.items())
|
||||||
|
wrapper_dom = defusedxml.minidom.parseString(f"<root {ns_attrs}>{content}</root>")
|
||||||
|
for child in wrapper_dom.documentElement.childNodes:
|
||||||
|
if child.nodeType == child.ELEMENT_NODE:
|
||||||
|
root.appendChild(dom.importNode(child, True))
|
||||||
|
output = _encode_smart_quotes(dom.toxml(encoding="UTF-8").decode("utf-8"))
|
||||||
|
xml_path.write_text(output, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def _find_para_id(comments_path: Path, comment_id: int) -> str | None:
|
||||||
|
dom = defusedxml.minidom.parseString(comments_path.read_text(encoding="utf-8"))
|
||||||
|
for c in dom.getElementsByTagName("w:comment"):
|
||||||
|
if c.getAttribute("w:id") == str(comment_id):
|
||||||
|
for p in c.getElementsByTagName("w:p"):
|
||||||
|
if pid := p.getAttribute("w14:paraId"):
|
||||||
|
return pid
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_next_rid(rels_path: Path) -> int:
|
||||||
|
dom = defusedxml.minidom.parseString(rels_path.read_text(encoding="utf-8"))
|
||||||
|
max_rid = 0
|
||||||
|
for rel in dom.getElementsByTagName("Relationship"):
|
||||||
|
rid = rel.getAttribute("Id")
|
||||||
|
if rid and rid.startswith("rId"):
|
||||||
|
try:
|
||||||
|
max_rid = max(max_rid, int(rid[3:]))
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
return max_rid + 1
|
||||||
|
|
||||||
|
|
||||||
|
def _has_relationship(rels_path: Path, target: str) -> bool:
|
||||||
|
dom = defusedxml.minidom.parseString(rels_path.read_text(encoding="utf-8"))
|
||||||
|
for rel in dom.getElementsByTagName("Relationship"):
|
||||||
|
if rel.getAttribute("Target") == target:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _has_content_type(ct_path: Path, part_name: str) -> bool:
|
||||||
|
dom = defusedxml.minidom.parseString(ct_path.read_text(encoding="utf-8"))
|
||||||
|
for override in dom.getElementsByTagName("Override"):
|
||||||
|
if override.getAttribute("PartName") == part_name:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_comment_relationships(unpacked_dir: Path) -> None:
|
||||||
|
rels_path = unpacked_dir / "word" / "_rels" / "document.xml.rels"
|
||||||
|
if not rels_path.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
if _has_relationship(rels_path, "comments.xml"):
|
||||||
|
return
|
||||||
|
|
||||||
|
dom = defusedxml.minidom.parseString(rels_path.read_text(encoding="utf-8"))
|
||||||
|
root = dom.documentElement
|
||||||
|
next_rid = _get_next_rid(rels_path)
|
||||||
|
|
||||||
|
rels = [
|
||||||
|
(
|
||||||
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
|
||||||
|
"comments.xml",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended",
|
||||||
|
"commentsExtended.xml",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"http://schemas.microsoft.com/office/2016/09/relationships/commentsIds",
|
||||||
|
"commentsIds.xml",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"http://schemas.microsoft.com/office/2018/08/relationships/commentsExtensible",
|
||||||
|
"commentsExtensible.xml",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
for rel_type, target in rels:
|
||||||
|
rel = dom.createElement("Relationship")
|
||||||
|
rel.setAttribute("Id", f"rId{next_rid}")
|
||||||
|
rel.setAttribute("Type", rel_type)
|
||||||
|
rel.setAttribute("Target", target)
|
||||||
|
root.appendChild(rel)
|
||||||
|
next_rid += 1
|
||||||
|
|
||||||
|
rels_path.write_bytes(dom.toxml(encoding="UTF-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_comment_content_types(unpacked_dir: Path) -> None:
|
||||||
|
ct_path = unpacked_dir / "[Content_Types].xml"
|
||||||
|
if not ct_path.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
if _has_content_type(ct_path, "/word/comments.xml"):
|
||||||
|
return
|
||||||
|
|
||||||
|
dom = defusedxml.minidom.parseString(ct_path.read_text(encoding="utf-8"))
|
||||||
|
root = dom.documentElement
|
||||||
|
|
||||||
|
overrides = [
|
||||||
|
(
|
||||||
|
"/word/comments.xml",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/word/commentsExtended.xml",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/word/commentsIds.xml",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsIds+xml",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"/word/commentsExtensible.xml",
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtensible+xml",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
for part_name, content_type in overrides:
|
||||||
|
override = dom.createElement("Override")
|
||||||
|
override.setAttribute("PartName", part_name)
|
||||||
|
override.setAttribute("ContentType", content_type)
|
||||||
|
root.appendChild(override)
|
||||||
|
|
||||||
|
ct_path.write_bytes(dom.toxml(encoding="UTF-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def add_comment(
|
||||||
|
unpacked_dir: str,
|
||||||
|
comment_id: int,
|
||||||
|
text: str,
|
||||||
|
author: str = "Claude",
|
||||||
|
initials: str = "C",
|
||||||
|
parent_id: int | None = None,
|
||||||
|
) -> tuple[str, str]:
|
||||||
|
word = Path(unpacked_dir) / "word"
|
||||||
|
if not word.exists():
|
||||||
|
return "", f"Error: {word} not found"
|
||||||
|
|
||||||
|
para_id, durable_id = _generate_hex_id(), _generate_hex_id()
|
||||||
|
ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
|
||||||
|
comments = word / "comments.xml"
|
||||||
|
first_comment = not comments.exists()
|
||||||
|
if first_comment:
|
||||||
|
shutil.copy(TEMPLATE_DIR / "comments.xml", comments)
|
||||||
|
_ensure_comment_relationships(Path(unpacked_dir))
|
||||||
|
_ensure_comment_content_types(Path(unpacked_dir))
|
||||||
|
_append_xml(
|
||||||
|
comments,
|
||||||
|
"w:comments",
|
||||||
|
COMMENT_XML.format(
|
||||||
|
id=comment_id,
|
||||||
|
author=author,
|
||||||
|
date=ts,
|
||||||
|
initials=initials,
|
||||||
|
para_id=para_id,
|
||||||
|
text=text,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
ext = word / "commentsExtended.xml"
|
||||||
|
if not ext.exists():
|
||||||
|
shutil.copy(TEMPLATE_DIR / "commentsExtended.xml", ext)
|
||||||
|
if parent_id is not None:
|
||||||
|
parent_para = _find_para_id(comments, parent_id)
|
||||||
|
if not parent_para:
|
||||||
|
return "", f"Error: Parent comment {parent_id} not found"
|
||||||
|
_append_xml(
|
||||||
|
ext,
|
||||||
|
"w15:commentsEx",
|
||||||
|
f'<w15:commentEx w15:paraId="{para_id}" w15:paraIdParent="{parent_para}" w15:done="0"/>',
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_append_xml(
|
||||||
|
ext,
|
||||||
|
"w15:commentsEx",
|
||||||
|
f'<w15:commentEx w15:paraId="{para_id}" w15:done="0"/>',
|
||||||
|
)
|
||||||
|
|
||||||
|
ids = word / "commentsIds.xml"
|
||||||
|
if not ids.exists():
|
||||||
|
shutil.copy(TEMPLATE_DIR / "commentsIds.xml", ids)
|
||||||
|
_append_xml(
|
||||||
|
ids,
|
||||||
|
"w16cid:commentsIds",
|
||||||
|
f'<w16cid:commentId w16cid:paraId="{para_id}" w16cid:durableId="{durable_id}"/>',
|
||||||
|
)
|
||||||
|
|
||||||
|
extensible = word / "commentsExtensible.xml"
|
||||||
|
if not extensible.exists():
|
||||||
|
shutil.copy(TEMPLATE_DIR / "commentsExtensible.xml", extensible)
|
||||||
|
_append_xml(
|
||||||
|
extensible,
|
||||||
|
"w16cex:commentsExtensible",
|
||||||
|
f'<w16cex:commentExtensible w16cex:durableId="{durable_id}" w16cex:dateUtc="{ts}"/>',
|
||||||
|
)
|
||||||
|
|
||||||
|
action = "reply" if parent_id is not None else "comment"
|
||||||
|
return para_id, f"Added {action} {comment_id} (para_id={para_id})"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
p = argparse.ArgumentParser(description="Add comments to DOCX documents")
|
||||||
|
p.add_argument("unpacked_dir", help="Unpacked DOCX directory")
|
||||||
|
p.add_argument("comment_id", type=int, help="Comment ID (must be unique)")
|
||||||
|
p.add_argument("text", help="Comment text")
|
||||||
|
p.add_argument("--author", default="Claude", help="Author name")
|
||||||
|
p.add_argument("--initials", default="C", help="Author initials")
|
||||||
|
p.add_argument("--parent", type=int, help="Parent comment ID (for replies)")
|
||||||
|
args = p.parse_args()
|
||||||
|
|
||||||
|
para_id, msg = add_comment(
|
||||||
|
args.unpacked_dir,
|
||||||
|
args.comment_id,
|
||||||
|
args.text,
|
||||||
|
args.author,
|
||||||
|
args.initials,
|
||||||
|
args.parent,
|
||||||
|
)
|
||||||
|
print(msg)
|
||||||
|
if "Error" in msg:
|
||||||
|
sys.exit(1)
|
||||||
|
cid = args.comment_id
|
||||||
|
if args.parent is not None:
|
||||||
|
print(REPLY_MARKER_TEMPLATE.format(pid=args.parent, cid=cid))
|
||||||
|
else:
|
||||||
|
print(COMMENT_MARKER_TEMPLATE.format(cid=cid))
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
"""Merge adjacent runs with identical formatting in DOCX.
|
||||||
|
|
||||||
|
Merges adjacent <w:r> elements that have identical <w:rPr> properties.
|
||||||
|
Works on runs in paragraphs and inside tracked changes (<w:ins>, <w:del>).
|
||||||
|
|
||||||
|
Also:
|
||||||
|
- Removes rsid attributes from runs (revision metadata that doesn't affect rendering)
|
||||||
|
- Removes proofErr elements (spell/grammar markers that block merging)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import defusedxml.minidom
|
||||||
|
|
||||||
|
|
||||||
|
def merge_runs(input_dir: str) -> tuple[int, str]:
|
||||||
|
doc_xml = Path(input_dir) / "word" / "document.xml"
|
||||||
|
|
||||||
|
if not doc_xml.exists():
|
||||||
|
return 0, f"Error: {doc_xml} not found"
|
||||||
|
|
||||||
|
try:
|
||||||
|
dom = defusedxml.minidom.parseString(doc_xml.read_text(encoding="utf-8"))
|
||||||
|
root = dom.documentElement
|
||||||
|
|
||||||
|
_remove_elements(root, "proofErr")
|
||||||
|
_strip_run_rsid_attrs(root)
|
||||||
|
|
||||||
|
containers = {run.parentNode for run in _find_elements(root, "r")}
|
||||||
|
|
||||||
|
merge_count = 0
|
||||||
|
for container in containers:
|
||||||
|
merge_count += _merge_runs_in(container)
|
||||||
|
|
||||||
|
doc_xml.write_bytes(dom.toxml(encoding="UTF-8"))
|
||||||
|
return merge_count, f"Merged {merge_count} runs"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return 0, f"Error: {e}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _find_elements(root, tag: str) -> list:
|
||||||
|
results = []
|
||||||
|
|
||||||
|
def traverse(node):
|
||||||
|
if node.nodeType == node.ELEMENT_NODE:
|
||||||
|
name = node.localName or node.tagName
|
||||||
|
if name == tag or name.endswith(f":{tag}"):
|
||||||
|
results.append(node)
|
||||||
|
for child in node.childNodes:
|
||||||
|
traverse(child)
|
||||||
|
|
||||||
|
traverse(root)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def _get_child(parent, tag: str):
|
||||||
|
for child in parent.childNodes:
|
||||||
|
if child.nodeType == child.ELEMENT_NODE:
|
||||||
|
name = child.localName or child.tagName
|
||||||
|
if name == tag or name.endswith(f":{tag}"):
|
||||||
|
return child
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_children(parent, tag: str) -> list:
|
||||||
|
results = []
|
||||||
|
for child in parent.childNodes:
|
||||||
|
if child.nodeType == child.ELEMENT_NODE:
|
||||||
|
name = child.localName or child.tagName
|
||||||
|
if name == tag or name.endswith(f":{tag}"):
|
||||||
|
results.append(child)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def _is_adjacent(elem1, elem2) -> bool:
|
||||||
|
node = elem1.nextSibling
|
||||||
|
while node:
|
||||||
|
if node == elem2:
|
||||||
|
return True
|
||||||
|
if node.nodeType == node.ELEMENT_NODE:
|
||||||
|
return False
|
||||||
|
if node.nodeType == node.TEXT_NODE and node.data.strip():
|
||||||
|
return False
|
||||||
|
node = node.nextSibling
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_elements(root, tag: str):
|
||||||
|
for elem in _find_elements(root, tag):
|
||||||
|
if elem.parentNode:
|
||||||
|
elem.parentNode.removeChild(elem)
|
||||||
|
|
||||||
|
|
||||||
|
def _strip_run_rsid_attrs(root):
|
||||||
|
for run in _find_elements(root, "r"):
|
||||||
|
for attr in list(run.attributes.values()):
|
||||||
|
if "rsid" in attr.name.lower():
|
||||||
|
run.removeAttribute(attr.name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_runs_in(container) -> int:
|
||||||
|
merge_count = 0
|
||||||
|
run = _first_child_run(container)
|
||||||
|
|
||||||
|
while run:
|
||||||
|
while True:
|
||||||
|
next_elem = _next_element_sibling(run)
|
||||||
|
if next_elem and _is_run(next_elem) and _can_merge(run, next_elem):
|
||||||
|
_merge_run_content(run, next_elem)
|
||||||
|
container.removeChild(next_elem)
|
||||||
|
merge_count += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
_consolidate_text(run)
|
||||||
|
run = _next_sibling_run(run)
|
||||||
|
|
||||||
|
return merge_count
|
||||||
|
|
||||||
|
|
||||||
|
def _first_child_run(container):
|
||||||
|
for child in container.childNodes:
|
||||||
|
if child.nodeType == child.ELEMENT_NODE and _is_run(child):
|
||||||
|
return child
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _next_element_sibling(node):
|
||||||
|
sibling = node.nextSibling
|
||||||
|
while sibling:
|
||||||
|
if sibling.nodeType == sibling.ELEMENT_NODE:
|
||||||
|
return sibling
|
||||||
|
sibling = sibling.nextSibling
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _next_sibling_run(node):
|
||||||
|
sibling = node.nextSibling
|
||||||
|
while sibling:
|
||||||
|
if sibling.nodeType == sibling.ELEMENT_NODE:
|
||||||
|
if _is_run(sibling):
|
||||||
|
return sibling
|
||||||
|
sibling = sibling.nextSibling
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _is_run(node) -> bool:
|
||||||
|
name = node.localName or node.tagName
|
||||||
|
return name == "r" or name.endswith(":r")
|
||||||
|
|
||||||
|
|
||||||
|
def _can_merge(run1, run2) -> bool:
|
||||||
|
rpr1 = _get_child(run1, "rPr")
|
||||||
|
rpr2 = _get_child(run2, "rPr")
|
||||||
|
|
||||||
|
if (rpr1 is None) != (rpr2 is None):
|
||||||
|
return False
|
||||||
|
if rpr1 is None:
|
||||||
|
return True
|
||||||
|
return rpr1.toxml() == rpr2.toxml()
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_run_content(target, source):
|
||||||
|
for child in list(source.childNodes):
|
||||||
|
if child.nodeType == child.ELEMENT_NODE:
|
||||||
|
name = child.localName or child.tagName
|
||||||
|
if name != "rPr" and not name.endswith(":rPr"):
|
||||||
|
target.appendChild(child)
|
||||||
|
|
||||||
|
|
||||||
|
def _consolidate_text(run):
|
||||||
|
t_elements = _get_children(run, "t")
|
||||||
|
|
||||||
|
for i in range(len(t_elements) - 1, 0, -1):
|
||||||
|
curr, prev = t_elements[i], t_elements[i - 1]
|
||||||
|
|
||||||
|
if _is_adjacent(prev, curr):
|
||||||
|
prev_text = prev.firstChild.data if prev.firstChild else ""
|
||||||
|
curr_text = curr.firstChild.data if curr.firstChild else ""
|
||||||
|
merged = prev_text + curr_text
|
||||||
|
|
||||||
|
if prev.firstChild:
|
||||||
|
prev.firstChild.data = merged
|
||||||
|
else:
|
||||||
|
prev.appendChild(run.ownerDocument.createTextNode(merged))
|
||||||
|
|
||||||
|
if merged.startswith(" ") or merged.endswith(" "):
|
||||||
|
prev.setAttribute("xml:space", "preserve")
|
||||||
|
elif prev.hasAttribute("xml:space"):
|
||||||
|
prev.removeAttribute("xml:space")
|
||||||
|
|
||||||
|
run.removeChild(curr)
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
"""Simplify tracked changes by merging adjacent w:ins or w:del elements.
|
||||||
|
|
||||||
|
Merges adjacent <w:ins> elements from the same author into a single element.
|
||||||
|
Same for <w:del> elements. This makes heavily-redlined documents easier to
|
||||||
|
work with by reducing the number of tracked change wrappers.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Only merges w:ins with w:ins, w:del with w:del (same element type)
|
||||||
|
- Only merges if same author (ignores timestamp differences)
|
||||||
|
- Only merges if truly adjacent (only whitespace between them)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import defusedxml.minidom
|
||||||
|
|
||||||
|
WORD_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
||||||
|
|
||||||
|
|
||||||
|
def simplify_redlines(input_dir: str) -> tuple[int, str]:
|
||||||
|
doc_xml = Path(input_dir) / "word" / "document.xml"
|
||||||
|
|
||||||
|
if not doc_xml.exists():
|
||||||
|
return 0, f"Error: {doc_xml} not found"
|
||||||
|
|
||||||
|
try:
|
||||||
|
dom = defusedxml.minidom.parseString(doc_xml.read_text(encoding="utf-8"))
|
||||||
|
root = dom.documentElement
|
||||||
|
|
||||||
|
merge_count = 0
|
||||||
|
|
||||||
|
containers = _find_elements(root, "p") + _find_elements(root, "tc")
|
||||||
|
|
||||||
|
for container in containers:
|
||||||
|
merge_count += _merge_tracked_changes_in(container, "ins")
|
||||||
|
merge_count += _merge_tracked_changes_in(container, "del")
|
||||||
|
|
||||||
|
doc_xml.write_bytes(dom.toxml(encoding="UTF-8"))
|
||||||
|
return merge_count, f"Simplified {merge_count} tracked changes"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return 0, f"Error: {e}"
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_tracked_changes_in(container, tag: str) -> int:
|
||||||
|
merge_count = 0
|
||||||
|
|
||||||
|
tracked = [
|
||||||
|
child
|
||||||
|
for child in container.childNodes
|
||||||
|
if child.nodeType == child.ELEMENT_NODE and _is_element(child, tag)
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(tracked) < 2:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < len(tracked) - 1:
|
||||||
|
curr = tracked[i]
|
||||||
|
next_elem = tracked[i + 1]
|
||||||
|
|
||||||
|
if _can_merge_tracked(curr, next_elem):
|
||||||
|
_merge_tracked_content(curr, next_elem)
|
||||||
|
container.removeChild(next_elem)
|
||||||
|
tracked.pop(i + 1)
|
||||||
|
merge_count += 1
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return merge_count
|
||||||
|
|
||||||
|
|
||||||
|
def _is_element(node, tag: str) -> bool:
|
||||||
|
name = node.localName or node.tagName
|
||||||
|
return name == tag or name.endswith(f":{tag}")
|
||||||
|
|
||||||
|
|
||||||
|
def _get_author(elem) -> str:
|
||||||
|
author = elem.getAttribute("w:author")
|
||||||
|
if not author:
|
||||||
|
for attr in elem.attributes.values():
|
||||||
|
if attr.localName == "author" or attr.name.endswith(":author"):
|
||||||
|
return attr.value
|
||||||
|
return author
|
||||||
|
|
||||||
|
|
||||||
|
def _can_merge_tracked(elem1, elem2) -> bool:
|
||||||
|
if _get_author(elem1) != _get_author(elem2):
|
||||||
|
return False
|
||||||
|
|
||||||
|
node = elem1.nextSibling
|
||||||
|
while node and node != elem2:
|
||||||
|
if node.nodeType == node.ELEMENT_NODE:
|
||||||
|
return False
|
||||||
|
if node.nodeType == node.TEXT_NODE and node.data.strip():
|
||||||
|
return False
|
||||||
|
node = node.nextSibling
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_tracked_content(target, source):
|
||||||
|
while source.firstChild:
|
||||||
|
child = source.firstChild
|
||||||
|
source.removeChild(child)
|
||||||
|
target.appendChild(child)
|
||||||
|
|
||||||
|
|
||||||
|
def _find_elements(root, tag: str) -> list:
|
||||||
|
results = []
|
||||||
|
|
||||||
|
def traverse(node):
|
||||||
|
if node.nodeType == node.ELEMENT_NODE:
|
||||||
|
name = node.localName or node.tagName
|
||||||
|
if name == tag or name.endswith(f":{tag}"):
|
||||||
|
results.append(node)
|
||||||
|
for child in node.childNodes:
|
||||||
|
traverse(child)
|
||||||
|
|
||||||
|
traverse(root)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def get_tracked_change_authors(doc_xml_path: Path) -> dict[str, int]:
|
||||||
|
if not doc_xml_path.exists():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
tree = ET.parse(doc_xml_path)
|
||||||
|
root = tree.getroot()
|
||||||
|
except ET.ParseError:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
namespaces = {"w": WORD_NS}
|
||||||
|
author_attr = f"{{{WORD_NS}}}author"
|
||||||
|
|
||||||
|
authors: dict[str, int] = {}
|
||||||
|
for tag in ["ins", "del"]:
|
||||||
|
for elem in root.findall(f".//w:{tag}", namespaces):
|
||||||
|
author = elem.get(author_attr)
|
||||||
|
if author:
|
||||||
|
authors[author] = authors.get(author, 0) + 1
|
||||||
|
|
||||||
|
return authors
|
||||||
|
|
||||||
|
|
||||||
|
def _get_authors_from_docx(docx_path: Path) -> dict[str, int]:
|
||||||
|
try:
|
||||||
|
with zipfile.ZipFile(docx_path, "r") as zf:
|
||||||
|
if "word/document.xml" not in zf.namelist():
|
||||||
|
return {}
|
||||||
|
with zf.open("word/document.xml") as f:
|
||||||
|
tree = ET.parse(f)
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
namespaces = {"w": WORD_NS}
|
||||||
|
author_attr = f"{{{WORD_NS}}}author"
|
||||||
|
|
||||||
|
authors: dict[str, int] = {}
|
||||||
|
for tag in ["ins", "del"]:
|
||||||
|
for elem in root.findall(f".//w:{tag}", namespaces):
|
||||||
|
author = elem.get(author_attr)
|
||||||
|
if author:
|
||||||
|
authors[author] = authors.get(author, 0) + 1
|
||||||
|
return authors
|
||||||
|
except (zipfile.BadZipFile, ET.ParseError):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def infer_author(modified_dir: Path, original_docx: Path, default: str = "Claude") -> str:
|
||||||
|
modified_xml = modified_dir / "word" / "document.xml"
|
||||||
|
modified_authors = get_tracked_change_authors(modified_xml)
|
||||||
|
|
||||||
|
if not modified_authors:
|
||||||
|
return default
|
||||||
|
|
||||||
|
original_authors = _get_authors_from_docx(original_docx)
|
||||||
|
|
||||||
|
new_changes: dict[str, int] = {}
|
||||||
|
for author, count in modified_authors.items():
|
||||||
|
original_count = original_authors.get(author, 0)
|
||||||
|
diff = count - original_count
|
||||||
|
if diff > 0:
|
||||||
|
new_changes[author] = diff
|
||||||
|
|
||||||
|
if not new_changes:
|
||||||
|
return default
|
||||||
|
|
||||||
|
if len(new_changes) == 1:
|
||||||
|
return next(iter(new_changes))
|
||||||
|
|
||||||
|
raise ValueError(
|
||||||
|
f"Multiple authors added new changes: {new_changes}. "
|
||||||
|
"Cannot infer which author to validate."
|
||||||
|
)
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
"""Pack a directory into a DOCX, PPTX, or XLSX file.
|
||||||
|
|
||||||
|
Validates with auto-repair, condenses XML formatting, and creates the Office file.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python pack.py <input_directory> <output_file> [--original <file>] [--validate true|false]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
python pack.py unpacked/ output.docx --original input.docx
|
||||||
|
python pack.py unpacked/ output.pptx --validate false
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import defusedxml.minidom
|
||||||
|
|
||||||
|
from validators import DOCXSchemaValidator, PPTXSchemaValidator, RedliningValidator
|
||||||
|
|
||||||
|
def pack(
|
||||||
|
input_directory: str,
|
||||||
|
output_file: str,
|
||||||
|
original_file: str | None = None,
|
||||||
|
validate: bool = True,
|
||||||
|
infer_author_func=None,
|
||||||
|
) -> tuple[None, str]:
|
||||||
|
input_dir = Path(input_directory)
|
||||||
|
output_path = Path(output_file)
|
||||||
|
suffix = output_path.suffix.lower()
|
||||||
|
|
||||||
|
if not input_dir.is_dir():
|
||||||
|
return None, f"Error: {input_dir} is not a directory"
|
||||||
|
|
||||||
|
if suffix not in {".docx", ".pptx", ".xlsx"}:
|
||||||
|
return None, f"Error: {output_file} must be a .docx, .pptx, or .xlsx file"
|
||||||
|
|
||||||
|
if validate and original_file:
|
||||||
|
original_path = Path(original_file)
|
||||||
|
if original_path.exists():
|
||||||
|
success, output = _run_validation(
|
||||||
|
input_dir, original_path, suffix, infer_author_func
|
||||||
|
)
|
||||||
|
if output:
|
||||||
|
print(output)
|
||||||
|
if not success:
|
||||||
|
return None, f"Error: Validation failed for {input_dir}"
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
||||||
|
temp_content_dir = Path(temp_dir) / "content"
|
||||||
|
shutil.copytree(input_dir, temp_content_dir)
|
||||||
|
|
||||||
|
for pattern in ["*.xml", "*.rels"]:
|
||||||
|
for xml_file in temp_content_dir.rglob(pattern):
|
||||||
|
_condense_xml(xml_file)
|
||||||
|
|
||||||
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with zipfile.ZipFile(output_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||||
|
for f in temp_content_dir.rglob("*"):
|
||||||
|
if f.is_file():
|
||||||
|
zf.write(f, f.relative_to(temp_content_dir))
|
||||||
|
|
||||||
|
return None, f"Successfully packed {input_dir} to {output_file}"
|
||||||
|
|
||||||
|
|
||||||
|
def _run_validation(
|
||||||
|
unpacked_dir: Path,
|
||||||
|
original_file: Path,
|
||||||
|
suffix: str,
|
||||||
|
infer_author_func=None,
|
||||||
|
) -> tuple[bool, str | None]:
|
||||||
|
output_lines = []
|
||||||
|
validators = []
|
||||||
|
|
||||||
|
if suffix == ".docx":
|
||||||
|
author = "Claude"
|
||||||
|
if infer_author_func:
|
||||||
|
try:
|
||||||
|
author = infer_author_func(unpacked_dir, original_file)
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Warning: {e} Using default author 'Claude'.", file=sys.stderr)
|
||||||
|
|
||||||
|
validators = [
|
||||||
|
DOCXSchemaValidator(unpacked_dir, original_file),
|
||||||
|
RedliningValidator(unpacked_dir, original_file, author=author),
|
||||||
|
]
|
||||||
|
elif suffix == ".pptx":
|
||||||
|
validators = [PPTXSchemaValidator(unpacked_dir, original_file)]
|
||||||
|
|
||||||
|
if not validators:
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
total_repairs = sum(v.repair() for v in validators)
|
||||||
|
if total_repairs:
|
||||||
|
output_lines.append(f"Auto-repaired {total_repairs} issue(s)")
|
||||||
|
|
||||||
|
success = all(v.validate() for v in validators)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
output_lines.append("All validations PASSED!")
|
||||||
|
|
||||||
|
return success, "\n".join(output_lines) if output_lines else None
|
||||||
|
|
||||||
|
|
||||||
|
def _condense_xml(xml_file: Path) -> None:
|
||||||
|
try:
|
||||||
|
with open(xml_file, encoding="utf-8") as f:
|
||||||
|
dom = defusedxml.minidom.parse(f)
|
||||||
|
|
||||||
|
for element in dom.getElementsByTagName("*"):
|
||||||
|
if element.tagName.endswith(":t"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
for child in list(element.childNodes):
|
||||||
|
if (
|
||||||
|
child.nodeType == child.TEXT_NODE
|
||||||
|
and child.nodeValue
|
||||||
|
and child.nodeValue.strip() == ""
|
||||||
|
) or child.nodeType == child.COMMENT_NODE:
|
||||||
|
element.removeChild(child)
|
||||||
|
|
||||||
|
xml_file.write_bytes(dom.toxml(encoding="UTF-8"))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"ERROR: Failed to parse {xml_file.name}: {e}", file=sys.stderr)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Pack a directory into a DOCX, PPTX, or XLSX file"
|
||||||
|
)
|
||||||
|
parser.add_argument("input_directory", help="Unpacked Office document directory")
|
||||||
|
parser.add_argument("output_file", help="Output Office file (.docx/.pptx/.xlsx)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--original",
|
||||||
|
help="Original file for validation comparison",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--validate",
|
||||||
|
type=lambda x: x.lower() == "true",
|
||||||
|
default=True,
|
||||||
|
metavar="true|false",
|
||||||
|
help="Run validation with auto-repair (default: true)",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
_, message = pack(
|
||||||
|
args.input_directory,
|
||||||
|
args.output_file,
|
||||||
|
original_file=args.original,
|
||||||
|
validate=args.validate,
|
||||||
|
)
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
if "Error" in message:
|
||||||
|
sys.exit(1)
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,146 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/drawingml/2006/chartDrawing"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/drawingml/2006/chartDrawing"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
schemaLocation="dml-main.xsd"/>
|
||||||
|
<xsd:complexType name="CT_ShapeNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvSpPr" type="a:CT_NonVisualDrawingShapeProps" minOccurs="1" maxOccurs="1"
|
||||||
|
/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Shape">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvSpPr" type="CT_ShapeNonVisual" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="style" type="a:CT_ShapeStyle" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element name="txBody" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="macro" type="xsd:string" use="optional"/>
|
||||||
|
<xsd:attribute name="textlink" type="xsd:string" use="optional"/>
|
||||||
|
<xsd:attribute name="fLocksText" type="xsd:boolean" use="optional" default="true"/>
|
||||||
|
<xsd:attribute name="fPublished" type="xsd:boolean" use="optional" default="false"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_ConnectorNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvCxnSpPr" type="a:CT_NonVisualConnectorProperties" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Connector">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvCxnSpPr" type="CT_ConnectorNonVisual" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="style" type="a:CT_ShapeStyle" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="macro" type="xsd:string" use="optional"/>
|
||||||
|
<xsd:attribute name="fPublished" type="xsd:boolean" use="optional" default="false"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_PictureNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvPicPr" type="a:CT_NonVisualPictureProperties" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Picture">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvPicPr" type="CT_PictureNonVisual" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="blipFill" type="a:CT_BlipFillProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="style" type="a:CT_ShapeStyle" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="macro" type="xsd:string" use="optional" default=""/>
|
||||||
|
<xsd:attribute name="fPublished" type="xsd:boolean" use="optional" default="false"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_GraphicFrameNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvGraphicFramePr" type="a:CT_NonVisualGraphicFrameProperties"
|
||||||
|
minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_GraphicFrame">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvGraphicFramePr" type="CT_GraphicFrameNonVisual" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
<xsd:element name="xfrm" type="a:CT_Transform2D" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element ref="a:graphic" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="macro" type="xsd:string" use="optional"/>
|
||||||
|
<xsd:attribute name="fPublished" type="xsd:boolean" use="optional" default="false"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_GroupShapeNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvGrpSpPr" type="a:CT_NonVisualGroupDrawingShapeProps" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_GroupShape">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvGrpSpPr" type="CT_GroupShapeNonVisual" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="grpSpPr" type="a:CT_GroupShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xsd:element name="sp" type="CT_Shape"/>
|
||||||
|
<xsd:element name="grpSp" type="CT_GroupShape"/>
|
||||||
|
<xsd:element name="graphicFrame" type="CT_GraphicFrame"/>
|
||||||
|
<xsd:element name="cxnSp" type="CT_Connector"/>
|
||||||
|
<xsd:element name="pic" type="CT_Picture"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:group name="EG_ObjectChoices">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:choice minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="sp" type="CT_Shape"/>
|
||||||
|
<xsd:element name="grpSp" type="CT_GroupShape"/>
|
||||||
|
<xsd:element name="graphicFrame" type="CT_GraphicFrame"/>
|
||||||
|
<xsd:element name="cxnSp" type="CT_Connector"/>
|
||||||
|
<xsd:element name="pic" type="CT_Picture"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:group>
|
||||||
|
<xsd:simpleType name="ST_MarkerCoordinate">
|
||||||
|
<xsd:restriction base="xsd:double">
|
||||||
|
<xsd:minInclusive value="0.0"/>
|
||||||
|
<xsd:maxInclusive value="1.0"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:complexType name="CT_Marker">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="x" type="ST_MarkerCoordinate" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="y" type="ST_MarkerCoordinate" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_RelSizeAnchor">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="from" type="CT_Marker"/>
|
||||||
|
<xsd:element name="to" type="CT_Marker"/>
|
||||||
|
<xsd:group ref="EG_ObjectChoices"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_AbsSizeAnchor">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="from" type="CT_Marker"/>
|
||||||
|
<xsd:element name="ext" type="a:CT_PositiveSize2D"/>
|
||||||
|
<xsd:group ref="EG_ObjectChoices"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:group name="EG_Anchor">
|
||||||
|
<xsd:choice>
|
||||||
|
<xsd:element name="relSizeAnchor" type="CT_RelSizeAnchor"/>
|
||||||
|
<xsd:element name="absSizeAnchor" type="CT_AbsSizeAnchor"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:group>
|
||||||
|
<xsd:complexType name="CT_Drawing">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:group ref="EG_Anchor" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:schema>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas"
|
||||||
|
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||||
|
elementFormDefault="qualified"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas">
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
schemaLocation="dml-main.xsd"/>
|
||||||
|
<xsd:element name="lockedCanvas" type="a:CT_GvmlGroupShape"/>
|
||||||
|
</xsd:schema>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/drawingml/2006/picture"
|
||||||
|
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" elementFormDefault="qualified"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/drawingml/2006/picture">
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
schemaLocation="dml-main.xsd"/>
|
||||||
|
<xsd:complexType name="CT_PictureNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvPicPr" type="a:CT_NonVisualPictureProperties" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Picture">
|
||||||
|
<xsd:sequence minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="nvPicPr" type="CT_PictureNonVisual" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="blipFill" type="a:CT_BlipFillProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:element name="pic" type="CT_Picture"/>
|
||||||
|
</xsd:schema>
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
|
||||||
|
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
schemaLocation="dml-main.xsd"/>
|
||||||
|
<xsd:import schemaLocation="shared-relationshipReference.xsd"
|
||||||
|
namespace="http://schemas.openxmlformats.org/officeDocument/2006/relationships"/>
|
||||||
|
<xsd:element name="from" type="CT_Marker"/>
|
||||||
|
<xsd:element name="to" type="CT_Marker"/>
|
||||||
|
<xsd:complexType name="CT_AnchorClientData">
|
||||||
|
<xsd:attribute name="fLocksWithSheet" type="xsd:boolean" use="optional" default="true"/>
|
||||||
|
<xsd:attribute name="fPrintsWithSheet" type="xsd:boolean" use="optional" default="true"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_ShapeNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvSpPr" type="a:CT_NonVisualDrawingShapeProps" minOccurs="1" maxOccurs="1"
|
||||||
|
/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Shape">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvSpPr" type="CT_ShapeNonVisual" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="style" type="a:CT_ShapeStyle" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element name="txBody" type="a:CT_TextBody" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="macro" type="xsd:string" use="optional"/>
|
||||||
|
<xsd:attribute name="textlink" type="xsd:string" use="optional"/>
|
||||||
|
<xsd:attribute name="fLocksText" type="xsd:boolean" use="optional" default="true"/>
|
||||||
|
<xsd:attribute name="fPublished" type="xsd:boolean" use="optional" default="false"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_ConnectorNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvCxnSpPr" type="a:CT_NonVisualConnectorProperties" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Connector">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvCxnSpPr" type="CT_ConnectorNonVisual" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="style" type="a:CT_ShapeStyle" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="macro" type="xsd:string" use="optional"/>
|
||||||
|
<xsd:attribute name="fPublished" type="xsd:boolean" use="optional" default="false"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_PictureNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvPicPr" type="a:CT_NonVisualPictureProperties" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Picture">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvPicPr" type="CT_PictureNonVisual" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="blipFill" type="a:CT_BlipFillProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="style" type="a:CT_ShapeStyle" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="macro" type="xsd:string" use="optional" default=""/>
|
||||||
|
<xsd:attribute name="fPublished" type="xsd:boolean" use="optional" default="false"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_GraphicalObjectFrameNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvGraphicFramePr" type="a:CT_NonVisualGraphicFrameProperties"
|
||||||
|
minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_GraphicalObjectFrame">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvGraphicFramePr" type="CT_GraphicalObjectFrameNonVisual" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
<xsd:element name="xfrm" type="a:CT_Transform2D" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element ref="a:graphic" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="macro" type="xsd:string" use="optional"/>
|
||||||
|
<xsd:attribute name="fPublished" type="xsd:boolean" use="optional" default="false"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_GroupShapeNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvGrpSpPr" type="a:CT_NonVisualGroupDrawingShapeProps" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_GroupShape">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvGrpSpPr" type="CT_GroupShapeNonVisual" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="grpSpPr" type="a:CT_GroupShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xsd:element name="sp" type="CT_Shape"/>
|
||||||
|
<xsd:element name="grpSp" type="CT_GroupShape"/>
|
||||||
|
<xsd:element name="graphicFrame" type="CT_GraphicalObjectFrame"/>
|
||||||
|
<xsd:element name="cxnSp" type="CT_Connector"/>
|
||||||
|
<xsd:element name="pic" type="CT_Picture"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:group name="EG_ObjectChoices">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:choice minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="sp" type="CT_Shape"/>
|
||||||
|
<xsd:element name="grpSp" type="CT_GroupShape"/>
|
||||||
|
<xsd:element name="graphicFrame" type="CT_GraphicalObjectFrame"/>
|
||||||
|
<xsd:element name="cxnSp" type="CT_Connector"/>
|
||||||
|
<xsd:element name="pic" type="CT_Picture"/>
|
||||||
|
<xsd:element name="contentPart" type="CT_Rel"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:group>
|
||||||
|
<xsd:complexType name="CT_Rel">
|
||||||
|
<xsd:attribute ref="r:id" use="required"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:simpleType name="ST_ColID">
|
||||||
|
<xsd:restriction base="xsd:int">
|
||||||
|
<xsd:minInclusive value="0"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_RowID">
|
||||||
|
<xsd:restriction base="xsd:int">
|
||||||
|
<xsd:minInclusive value="0"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:complexType name="CT_Marker">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="col" type="ST_ColID"/>
|
||||||
|
<xsd:element name="colOff" type="a:ST_Coordinate"/>
|
||||||
|
<xsd:element name="row" type="ST_RowID"/>
|
||||||
|
<xsd:element name="rowOff" type="a:ST_Coordinate"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:simpleType name="ST_EditAs">
|
||||||
|
<xsd:restriction base="xsd:token">
|
||||||
|
<xsd:enumeration value="twoCell"/>
|
||||||
|
<xsd:enumeration value="oneCell"/>
|
||||||
|
<xsd:enumeration value="absolute"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:complexType name="CT_TwoCellAnchor">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="from" type="CT_Marker"/>
|
||||||
|
<xsd:element name="to" type="CT_Marker"/>
|
||||||
|
<xsd:group ref="EG_ObjectChoices"/>
|
||||||
|
<xsd:element name="clientData" type="CT_AnchorClientData" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="editAs" type="ST_EditAs" use="optional" default="twoCell"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_OneCellAnchor">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="from" type="CT_Marker"/>
|
||||||
|
<xsd:element name="ext" type="a:CT_PositiveSize2D"/>
|
||||||
|
<xsd:group ref="EG_ObjectChoices"/>
|
||||||
|
<xsd:element name="clientData" type="CT_AnchorClientData" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_AbsoluteAnchor">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="pos" type="a:CT_Point2D"/>
|
||||||
|
<xsd:element name="ext" type="a:CT_PositiveSize2D"/>
|
||||||
|
<xsd:group ref="EG_ObjectChoices"/>
|
||||||
|
<xsd:element name="clientData" type="CT_AnchorClientData" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:group name="EG_Anchor">
|
||||||
|
<xsd:choice>
|
||||||
|
<xsd:element name="twoCellAnchor" type="CT_TwoCellAnchor"/>
|
||||||
|
<xsd:element name="oneCellAnchor" type="CT_OneCellAnchor"/>
|
||||||
|
<xsd:element name="absoluteAnchor" type="CT_AbsoluteAnchor"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:group>
|
||||||
|
<xsd:complexType name="CT_Drawing">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:group ref="EG_Anchor" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:element name="wsDr" type="CT_Drawing"/>
|
||||||
|
</xsd:schema>
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
||||||
|
xmlns:dpct="http://schemas.openxmlformats.org/drawingml/2006/picture"
|
||||||
|
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/drawingml/2006/main"
|
||||||
|
schemaLocation="dml-main.xsd"/>
|
||||||
|
<xsd:import schemaLocation="wml.xsd"
|
||||||
|
namespace="http://schemas.openxmlformats.org/wordprocessingml/2006/main"/>
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/drawingml/2006/picture"
|
||||||
|
schemaLocation="dml-picture.xsd"/>
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
||||||
|
schemaLocation="shared-relationshipReference.xsd"/>
|
||||||
|
<xsd:complexType name="CT_EffectExtent">
|
||||||
|
<xsd:attribute name="l" type="a:ST_Coordinate" use="required"/>
|
||||||
|
<xsd:attribute name="t" type="a:ST_Coordinate" use="required"/>
|
||||||
|
<xsd:attribute name="r" type="a:ST_Coordinate" use="required"/>
|
||||||
|
<xsd:attribute name="b" type="a:ST_Coordinate" use="required"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:simpleType name="ST_WrapDistance">
|
||||||
|
<xsd:restriction base="xsd:unsignedInt"/>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:complexType name="CT_Inline">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="extent" type="a:CT_PositiveSize2D"/>
|
||||||
|
<xsd:element name="effectExtent" type="CT_EffectExtent" minOccurs="0"/>
|
||||||
|
<xsd:element name="docPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvGraphicFramePr" type="a:CT_NonVisualGraphicFrameProperties"
|
||||||
|
minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element ref="a:graphic" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="distT" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distB" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distL" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distR" type="ST_WrapDistance" use="optional"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:simpleType name="ST_WrapText">
|
||||||
|
<xsd:restriction base="xsd:token">
|
||||||
|
<xsd:enumeration value="bothSides"/>
|
||||||
|
<xsd:enumeration value="left"/>
|
||||||
|
<xsd:enumeration value="right"/>
|
||||||
|
<xsd:enumeration value="largest"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:complexType name="CT_WrapPath">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="start" type="a:CT_Point2D" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="lineTo" type="a:CT_Point2D" minOccurs="2" maxOccurs="unbounded"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="edited" type="xsd:boolean" use="optional"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_WrapNone"/>
|
||||||
|
<xsd:complexType name="CT_WrapSquare">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="effectExtent" type="CT_EffectExtent" minOccurs="0"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="wrapText" type="ST_WrapText" use="required"/>
|
||||||
|
<xsd:attribute name="distT" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distB" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distL" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distR" type="ST_WrapDistance" use="optional"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_WrapTight">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="wrapPolygon" type="CT_WrapPath" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="wrapText" type="ST_WrapText" use="required"/>
|
||||||
|
<xsd:attribute name="distL" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distR" type="ST_WrapDistance" use="optional"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_WrapThrough">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="wrapPolygon" type="CT_WrapPath" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="wrapText" type="ST_WrapText" use="required"/>
|
||||||
|
<xsd:attribute name="distL" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distR" type="ST_WrapDistance" use="optional"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_WrapTopBottom">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="effectExtent" type="CT_EffectExtent" minOccurs="0"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="distT" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distB" type="ST_WrapDistance" use="optional"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:group name="EG_WrapType">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:choice minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="wrapNone" type="CT_WrapNone" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="wrapSquare" type="CT_WrapSquare" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="wrapTight" type="CT_WrapTight" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="wrapThrough" type="CT_WrapThrough" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="wrapTopAndBottom" type="CT_WrapTopBottom" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:group>
|
||||||
|
<xsd:simpleType name="ST_PositionOffset">
|
||||||
|
<xsd:restriction base="xsd:int"/>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_AlignH">
|
||||||
|
<xsd:restriction base="xsd:token">
|
||||||
|
<xsd:enumeration value="left"/>
|
||||||
|
<xsd:enumeration value="right"/>
|
||||||
|
<xsd:enumeration value="center"/>
|
||||||
|
<xsd:enumeration value="inside"/>
|
||||||
|
<xsd:enumeration value="outside"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_RelFromH">
|
||||||
|
<xsd:restriction base="xsd:token">
|
||||||
|
<xsd:enumeration value="margin"/>
|
||||||
|
<xsd:enumeration value="page"/>
|
||||||
|
<xsd:enumeration value="column"/>
|
||||||
|
<xsd:enumeration value="character"/>
|
||||||
|
<xsd:enumeration value="leftMargin"/>
|
||||||
|
<xsd:enumeration value="rightMargin"/>
|
||||||
|
<xsd:enumeration value="insideMargin"/>
|
||||||
|
<xsd:enumeration value="outsideMargin"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:complexType name="CT_PosH">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:choice minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="align" type="ST_AlignH" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="posOffset" type="ST_PositionOffset" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="relativeFrom" type="ST_RelFromH" use="required"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:simpleType name="ST_AlignV">
|
||||||
|
<xsd:restriction base="xsd:token">
|
||||||
|
<xsd:enumeration value="top"/>
|
||||||
|
<xsd:enumeration value="bottom"/>
|
||||||
|
<xsd:enumeration value="center"/>
|
||||||
|
<xsd:enumeration value="inside"/>
|
||||||
|
<xsd:enumeration value="outside"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_RelFromV">
|
||||||
|
<xsd:restriction base="xsd:token">
|
||||||
|
<xsd:enumeration value="margin"/>
|
||||||
|
<xsd:enumeration value="page"/>
|
||||||
|
<xsd:enumeration value="paragraph"/>
|
||||||
|
<xsd:enumeration value="line"/>
|
||||||
|
<xsd:enumeration value="topMargin"/>
|
||||||
|
<xsd:enumeration value="bottomMargin"/>
|
||||||
|
<xsd:enumeration value="insideMargin"/>
|
||||||
|
<xsd:enumeration value="outsideMargin"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:complexType name="CT_PosV">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:choice minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="align" type="ST_AlignV" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="posOffset" type="ST_PositionOffset" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="relativeFrom" type="ST_RelFromV" use="required"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Anchor">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="simplePos" type="a:CT_Point2D"/>
|
||||||
|
<xsd:element name="positionH" type="CT_PosH"/>
|
||||||
|
<xsd:element name="positionV" type="CT_PosV"/>
|
||||||
|
<xsd:element name="extent" type="a:CT_PositiveSize2D"/>
|
||||||
|
<xsd:element name="effectExtent" type="CT_EffectExtent" minOccurs="0"/>
|
||||||
|
<xsd:group ref="EG_WrapType"/>
|
||||||
|
<xsd:element name="docPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvGraphicFramePr" type="a:CT_NonVisualGraphicFrameProperties"
|
||||||
|
minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element ref="a:graphic" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="distT" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distB" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distL" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="distR" type="ST_WrapDistance" use="optional"/>
|
||||||
|
<xsd:attribute name="simplePos" type="xsd:boolean"/>
|
||||||
|
<xsd:attribute name="relativeHeight" type="xsd:unsignedInt" use="required"/>
|
||||||
|
<xsd:attribute name="behindDoc" type="xsd:boolean" use="required"/>
|
||||||
|
<xsd:attribute name="locked" type="xsd:boolean" use="required"/>
|
||||||
|
<xsd:attribute name="layoutInCell" type="xsd:boolean" use="required"/>
|
||||||
|
<xsd:attribute name="hidden" type="xsd:boolean" use="optional"/>
|
||||||
|
<xsd:attribute name="allowOverlap" type="xsd:boolean" use="required"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_TxbxContent">
|
||||||
|
<xsd:group ref="w:EG_BlockLevelElts" minOccurs="1" maxOccurs="unbounded"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_TextboxInfo">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="txbxContent" type="CT_TxbxContent" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="extLst" type="a:CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="id" type="xsd:unsignedShort" use="optional" default="0"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_LinkedTextboxInformation">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="extLst" type="a:CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="id" type="xsd:unsignedShort" use="required"/>
|
||||||
|
<xsd:attribute name="seq" type="xsd:unsignedShort" use="required"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_WordprocessingShape">
|
||||||
|
<xsd:sequence minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:choice minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="cNvSpPr" type="a:CT_NonVisualDrawingShapeProps" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvCnPr" type="a:CT_NonVisualConnectorProperties" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
</xsd:choice>
|
||||||
|
<xsd:element name="spPr" type="a:CT_ShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="style" type="a:CT_ShapeStyle" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element name="extLst" type="a:CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:choice minOccurs="0" maxOccurs="1">
|
||||||
|
<xsd:element name="txbx" type="CT_TextboxInfo" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="linkedTxbx" type="CT_LinkedTextboxInformation" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
</xsd:choice>
|
||||||
|
<xsd:element name="bodyPr" type="a:CT_TextBodyProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="normalEastAsianFlow" type="xsd:boolean" use="optional" default="false"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_GraphicFrame">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvFrPr" type="a:CT_NonVisualGraphicFrameProperties" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
<xsd:element name="xfrm" type="a:CT_Transform2D" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element ref="a:graphic" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="extLst" type="a:CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_WordprocessingContentPartNonVisual">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvContentPartPr" type="a:CT_NonVisualContentPartProperties" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_WordprocessingContentPart">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="nvContentPartPr" type="CT_WordprocessingContentPartNonVisual" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element name="xfrm" type="a:CT_Transform2D" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element name="extLst" type="a:CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="bwMode" type="a:ST_BlackWhiteMode" use="optional"/>
|
||||||
|
<xsd:attribute ref="r:id" use="required"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_WordprocessingGroup">
|
||||||
|
<xsd:sequence minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="cNvPr" type="a:CT_NonVisualDrawingProps" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element name="cNvGrpSpPr" type="a:CT_NonVisualGroupDrawingShapeProps" minOccurs="1"
|
||||||
|
maxOccurs="1"/>
|
||||||
|
<xsd:element name="grpSpPr" type="a:CT_GroupShapeProperties" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xsd:element ref="wsp"/>
|
||||||
|
<xsd:element name="grpSp" type="CT_WordprocessingGroup"/>
|
||||||
|
<xsd:element name="graphicFrame" type="CT_GraphicFrame"/>
|
||||||
|
<xsd:element ref="dpct:pic"/>
|
||||||
|
<xsd:element name="contentPart" type="CT_WordprocessingContentPart"/>
|
||||||
|
</xsd:choice>
|
||||||
|
<xsd:element name="extLst" type="a:CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_WordprocessingCanvas">
|
||||||
|
<xsd:sequence minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element name="bg" type="a:CT_BackgroundFormatting" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:element name="whole" type="a:CT_WholeE2oFormatting" minOccurs="0" maxOccurs="1"/>
|
||||||
|
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xsd:element ref="wsp"/>
|
||||||
|
<xsd:element ref="dpct:pic"/>
|
||||||
|
<xsd:element name="contentPart" type="CT_WordprocessingContentPart"/>
|
||||||
|
<xsd:element ref="wgp"/>
|
||||||
|
<xsd:element name="graphicFrame" type="CT_GraphicFrame"/>
|
||||||
|
</xsd:choice>
|
||||||
|
<xsd:element name="extLst" type="a:CT_OfficeArtExtensionList" minOccurs="0" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:element name="wpc" type="CT_WordprocessingCanvas"/>
|
||||||
|
<xsd:element name="wgp" type="CT_WordprocessingGroup"/>
|
||||||
|
<xsd:element name="wsp" type="CT_WordprocessingShape"/>
|
||||||
|
<xsd:element name="inline" type="CT_Inline"/>
|
||||||
|
<xsd:element name="anchor" type="CT_Anchor"/>
|
||||||
|
</xsd:schema>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/officeDocument/2006/characteristics"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/officeDocument/2006/characteristics"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
<xsd:complexType name="CT_AdditionalCharacteristics">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="characteristic" type="CT_Characteristic" minOccurs="0"
|
||||||
|
maxOccurs="unbounded"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Characteristic">
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||||
|
<xsd:attribute name="relation" type="ST_Relation" use="required"/>
|
||||||
|
<xsd:attribute name="val" type="xsd:string" use="required"/>
|
||||||
|
<xsd:attribute name="vocabulary" type="xsd:anyURI" use="optional"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:simpleType name="ST_Relation">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="ge"/>
|
||||||
|
<xsd:enumeration value="le"/>
|
||||||
|
<xsd:enumeration value="gt"/>
|
||||||
|
<xsd:enumeration value="lt"/>
|
||||||
|
<xsd:enumeration value="eq"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:element name="additionalCharacteristics" type="CT_AdditionalCharacteristics"/>
|
||||||
|
</xsd:schema>
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/officeDocument/2006/bibliography"
|
||||||
|
xmlns:s="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/officeDocument/2006/bibliography"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
|
||||||
|
schemaLocation="shared-commonSimpleTypes.xsd"/>
|
||||||
|
<xsd:simpleType name="ST_SourceType">
|
||||||
|
<xsd:restriction base="s:ST_String">
|
||||||
|
<xsd:enumeration value="ArticleInAPeriodical"/>
|
||||||
|
<xsd:enumeration value="Book"/>
|
||||||
|
<xsd:enumeration value="BookSection"/>
|
||||||
|
<xsd:enumeration value="JournalArticle"/>
|
||||||
|
<xsd:enumeration value="ConferenceProceedings"/>
|
||||||
|
<xsd:enumeration value="Report"/>
|
||||||
|
<xsd:enumeration value="SoundRecording"/>
|
||||||
|
<xsd:enumeration value="Performance"/>
|
||||||
|
<xsd:enumeration value="Art"/>
|
||||||
|
<xsd:enumeration value="DocumentFromInternetSite"/>
|
||||||
|
<xsd:enumeration value="InternetSite"/>
|
||||||
|
<xsd:enumeration value="Film"/>
|
||||||
|
<xsd:enumeration value="Interview"/>
|
||||||
|
<xsd:enumeration value="Patent"/>
|
||||||
|
<xsd:enumeration value="ElectronicSource"/>
|
||||||
|
<xsd:enumeration value="Case"/>
|
||||||
|
<xsd:enumeration value="Misc"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:complexType name="CT_NameListType">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="Person" type="CT_PersonType" minOccurs="1" maxOccurs="unbounded"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_PersonType">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="Last" type="s:ST_String" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
<xsd:element name="First" type="s:ST_String" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
<xsd:element name="Middle" type="s:ST_String" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_NameType">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="NameList" type="CT_NameListType" minOccurs="1" maxOccurs="1"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_NameOrCorporateType">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:choice minOccurs="0" maxOccurs="1">
|
||||||
|
<xsd:element name="NameList" type="CT_NameListType" minOccurs="1" maxOccurs="1"/>
|
||||||
|
<xsd:element name="Corporate" minOccurs="1" maxOccurs="1" type="s:ST_String"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_AuthorType">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xsd:element name="Artist" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Author" type="CT_NameOrCorporateType"/>
|
||||||
|
<xsd:element name="BookAuthor" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Compiler" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Composer" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Conductor" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Counsel" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Director" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Editor" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Interviewee" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Interviewer" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Inventor" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Performer" type="CT_NameOrCorporateType"/>
|
||||||
|
<xsd:element name="ProducerName" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Translator" type="CT_NameType"/>
|
||||||
|
<xsd:element name="Writer" type="CT_NameType"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_SourceType">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xsd:element name="AbbreviatedCaseNumber" type="s:ST_String"/>
|
||||||
|
<xsd:element name="AlbumTitle" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Author" type="CT_AuthorType"/>
|
||||||
|
<xsd:element name="BookTitle" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Broadcaster" type="s:ST_String"/>
|
||||||
|
<xsd:element name="BroadcastTitle" type="s:ST_String"/>
|
||||||
|
<xsd:element name="CaseNumber" type="s:ST_String"/>
|
||||||
|
<xsd:element name="ChapterNumber" type="s:ST_String"/>
|
||||||
|
<xsd:element name="City" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Comments" type="s:ST_String"/>
|
||||||
|
<xsd:element name="ConferenceName" type="s:ST_String"/>
|
||||||
|
<xsd:element name="CountryRegion" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Court" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Day" type="s:ST_String"/>
|
||||||
|
<xsd:element name="DayAccessed" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Department" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Distributor" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Edition" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Guid" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Institution" type="s:ST_String"/>
|
||||||
|
<xsd:element name="InternetSiteTitle" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Issue" type="s:ST_String"/>
|
||||||
|
<xsd:element name="JournalName" type="s:ST_String"/>
|
||||||
|
<xsd:element name="LCID" type="s:ST_Lang"/>
|
||||||
|
<xsd:element name="Medium" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Month" type="s:ST_String"/>
|
||||||
|
<xsd:element name="MonthAccessed" type="s:ST_String"/>
|
||||||
|
<xsd:element name="NumberVolumes" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Pages" type="s:ST_String"/>
|
||||||
|
<xsd:element name="PatentNumber" type="s:ST_String"/>
|
||||||
|
<xsd:element name="PeriodicalTitle" type="s:ST_String"/>
|
||||||
|
<xsd:element name="ProductionCompany" type="s:ST_String"/>
|
||||||
|
<xsd:element name="PublicationTitle" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Publisher" type="s:ST_String"/>
|
||||||
|
<xsd:element name="RecordingNumber" type="s:ST_String"/>
|
||||||
|
<xsd:element name="RefOrder" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Reporter" type="s:ST_String"/>
|
||||||
|
<xsd:element name="SourceType" type="ST_SourceType"/>
|
||||||
|
<xsd:element name="ShortTitle" type="s:ST_String"/>
|
||||||
|
<xsd:element name="StandardNumber" type="s:ST_String"/>
|
||||||
|
<xsd:element name="StateProvince" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Station" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Tag" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Theater" type="s:ST_String"/>
|
||||||
|
<xsd:element name="ThesisType" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Title" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Type" type="s:ST_String"/>
|
||||||
|
<xsd:element name="URL" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Version" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Volume" type="s:ST_String"/>
|
||||||
|
<xsd:element name="Year" type="s:ST_String"/>
|
||||||
|
<xsd:element name="YearAccessed" type="s:ST_String"/>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:element name="Sources" type="CT_Sources"/>
|
||||||
|
<xsd:complexType name="CT_Sources">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="Source" type="CT_SourceType" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="SelectedStyle" type="s:ST_String"/>
|
||||||
|
<xsd:attribute name="StyleName" type="s:ST_String"/>
|
||||||
|
<xsd:attribute name="URI" type="s:ST_String"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:schema>
|
||||||
@@ -0,0 +1,174 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
|
||||||
|
elementFormDefault="qualified">
|
||||||
|
<xsd:simpleType name="ST_Lang">
|
||||||
|
<xsd:restriction base="xsd:string"/>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_HexColorRGB">
|
||||||
|
<xsd:restriction base="xsd:hexBinary">
|
||||||
|
<xsd:length value="3" fixed="true"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_Panose">
|
||||||
|
<xsd:restriction base="xsd:hexBinary">
|
||||||
|
<xsd:length value="10"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_CalendarType">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="gregorian"/>
|
||||||
|
<xsd:enumeration value="gregorianUs"/>
|
||||||
|
<xsd:enumeration value="gregorianMeFrench"/>
|
||||||
|
<xsd:enumeration value="gregorianArabic"/>
|
||||||
|
<xsd:enumeration value="hijri"/>
|
||||||
|
<xsd:enumeration value="hebrew"/>
|
||||||
|
<xsd:enumeration value="taiwan"/>
|
||||||
|
<xsd:enumeration value="japan"/>
|
||||||
|
<xsd:enumeration value="thai"/>
|
||||||
|
<xsd:enumeration value="korea"/>
|
||||||
|
<xsd:enumeration value="saka"/>
|
||||||
|
<xsd:enumeration value="gregorianXlitEnglish"/>
|
||||||
|
<xsd:enumeration value="gregorianXlitFrench"/>
|
||||||
|
<xsd:enumeration value="none"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_AlgClass">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="hash"/>
|
||||||
|
<xsd:enumeration value="custom"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_CryptProv">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="rsaAES"/>
|
||||||
|
<xsd:enumeration value="rsaFull"/>
|
||||||
|
<xsd:enumeration value="custom"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_AlgType">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="typeAny"/>
|
||||||
|
<xsd:enumeration value="custom"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_ColorType">
|
||||||
|
<xsd:restriction base="xsd:string"/>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_Guid">
|
||||||
|
<xsd:restriction base="xsd:token">
|
||||||
|
<xsd:pattern value="\{[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\}"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_OnOff">
|
||||||
|
<xsd:union memberTypes="xsd:boolean ST_OnOff1"/>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_OnOff1">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="on"/>
|
||||||
|
<xsd:enumeration value="off"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_String">
|
||||||
|
<xsd:restriction base="xsd:string"/>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_XmlName">
|
||||||
|
<xsd:restriction base="xsd:NCName">
|
||||||
|
<xsd:minLength value="1"/>
|
||||||
|
<xsd:maxLength value="255"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_TrueFalse">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="t"/>
|
||||||
|
<xsd:enumeration value="f"/>
|
||||||
|
<xsd:enumeration value="true"/>
|
||||||
|
<xsd:enumeration value="false"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_TrueFalseBlank">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="t"/>
|
||||||
|
<xsd:enumeration value="f"/>
|
||||||
|
<xsd:enumeration value="true"/>
|
||||||
|
<xsd:enumeration value="false"/>
|
||||||
|
<xsd:enumeration value=""/>
|
||||||
|
<xsd:enumeration value="True"/>
|
||||||
|
<xsd:enumeration value="False"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_UnsignedDecimalNumber">
|
||||||
|
<xsd:restriction base="xsd:decimal">
|
||||||
|
<xsd:minInclusive value="0"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_TwipsMeasure">
|
||||||
|
<xsd:union memberTypes="ST_UnsignedDecimalNumber ST_PositiveUniversalMeasure"/>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_VerticalAlignRun">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="baseline"/>
|
||||||
|
<xsd:enumeration value="superscript"/>
|
||||||
|
<xsd:enumeration value="subscript"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_Xstring">
|
||||||
|
<xsd:restriction base="xsd:string"/>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_XAlign">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="left"/>
|
||||||
|
<xsd:enumeration value="center"/>
|
||||||
|
<xsd:enumeration value="right"/>
|
||||||
|
<xsd:enumeration value="inside"/>
|
||||||
|
<xsd:enumeration value="outside"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_YAlign">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="inline"/>
|
||||||
|
<xsd:enumeration value="top"/>
|
||||||
|
<xsd:enumeration value="center"/>
|
||||||
|
<xsd:enumeration value="bottom"/>
|
||||||
|
<xsd:enumeration value="inside"/>
|
||||||
|
<xsd:enumeration value="outside"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_ConformanceClass">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="strict"/>
|
||||||
|
<xsd:enumeration value="transitional"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_UniversalMeasure">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:pattern value="-?[0-9]+(\.[0-9]+)?(mm|cm|in|pt|pc|pi)"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_PositiveUniversalMeasure">
|
||||||
|
<xsd:restriction base="ST_UniversalMeasure">
|
||||||
|
<xsd:pattern value="[0-9]+(\.[0-9]+)?(mm|cm|in|pt|pc|pi)"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_Percentage">
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:pattern value="-?[0-9]+(\.[0-9]+)?%"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_FixedPercentage">
|
||||||
|
<xsd:restriction base="ST_Percentage">
|
||||||
|
<xsd:pattern value="-?((100)|([0-9][0-9]?))(\.[0-9][0-9]?)?%"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_PositivePercentage">
|
||||||
|
<xsd:restriction base="ST_Percentage">
|
||||||
|
<xsd:pattern value="[0-9]+(\.[0-9]+)?%"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
<xsd:simpleType name="ST_PositiveFixedPercentage">
|
||||||
|
<xsd:restriction base="ST_Percentage">
|
||||||
|
<xsd:pattern value="((100)|([0-9][0-9]?))(\.[0-9][0-9]?)?%"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
</xsd:schema>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/officeDocument/2006/customXml"
|
||||||
|
xmlns:s="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/officeDocument/2006/customXml"
|
||||||
|
elementFormDefault="qualified" attributeFormDefault="qualified" blockDefault="#all">
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
|
||||||
|
schemaLocation="shared-commonSimpleTypes.xsd"/>
|
||||||
|
<xsd:complexType name="CT_DatastoreSchemaRef">
|
||||||
|
<xsd:attribute name="uri" type="xsd:string" use="required"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_DatastoreSchemaRefs">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="schemaRef" type="CT_DatastoreSchemaRef" minOccurs="0" maxOccurs="unbounded"
|
||||||
|
/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_DatastoreItem">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="schemaRefs" type="CT_DatastoreSchemaRefs" minOccurs="0"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="itemID" type="s:ST_Guid" use="required"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:element name="datastoreItem" type="CT_DatastoreItem"/>
|
||||||
|
</xsd:schema>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/schemaLibrary/2006/main"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/schemaLibrary/2006/main"
|
||||||
|
attributeFormDefault="qualified" elementFormDefault="qualified">
|
||||||
|
<xsd:complexType name="CT_Schema">
|
||||||
|
<xsd:attribute name="uri" type="xsd:string" default=""/>
|
||||||
|
<xsd:attribute name="manifestLocation" type="xsd:string"/>
|
||||||
|
<xsd:attribute name="schemaLocation" type="xsd:string"/>
|
||||||
|
<xsd:attribute name="schemaLanguage" type="xsd:token"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_SchemaLibrary">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="schema" type="CT_Schema" minOccurs="0" maxOccurs="unbounded"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:element name="schemaLibrary" type="CT_SchemaLibrary"/>
|
||||||
|
</xsd:schema>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"
|
||||||
|
xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
|
||||||
|
xmlns:s="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"
|
||||||
|
blockDefault="#all" elementFormDefault="qualified">
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
|
||||||
|
schemaLocation="shared-documentPropertiesVariantTypes.xsd"/>
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/officeDocument/2006/sharedTypes"
|
||||||
|
schemaLocation="shared-commonSimpleTypes.xsd"/>
|
||||||
|
<xsd:element name="Properties" type="CT_Properties"/>
|
||||||
|
<xsd:complexType name="CT_Properties">
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="property" minOccurs="0" maxOccurs="unbounded" type="CT_Property"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_Property">
|
||||||
|
<xsd:choice minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element ref="vt:vector"/>
|
||||||
|
<xsd:element ref="vt:array"/>
|
||||||
|
<xsd:element ref="vt:blob"/>
|
||||||
|
<xsd:element ref="vt:oblob"/>
|
||||||
|
<xsd:element ref="vt:empty"/>
|
||||||
|
<xsd:element ref="vt:null"/>
|
||||||
|
<xsd:element ref="vt:i1"/>
|
||||||
|
<xsd:element ref="vt:i2"/>
|
||||||
|
<xsd:element ref="vt:i4"/>
|
||||||
|
<xsd:element ref="vt:i8"/>
|
||||||
|
<xsd:element ref="vt:int"/>
|
||||||
|
<xsd:element ref="vt:ui1"/>
|
||||||
|
<xsd:element ref="vt:ui2"/>
|
||||||
|
<xsd:element ref="vt:ui4"/>
|
||||||
|
<xsd:element ref="vt:ui8"/>
|
||||||
|
<xsd:element ref="vt:uint"/>
|
||||||
|
<xsd:element ref="vt:r4"/>
|
||||||
|
<xsd:element ref="vt:r8"/>
|
||||||
|
<xsd:element ref="vt:decimal"/>
|
||||||
|
<xsd:element ref="vt:lpstr"/>
|
||||||
|
<xsd:element ref="vt:lpwstr"/>
|
||||||
|
<xsd:element ref="vt:bstr"/>
|
||||||
|
<xsd:element ref="vt:date"/>
|
||||||
|
<xsd:element ref="vt:filetime"/>
|
||||||
|
<xsd:element ref="vt:bool"/>
|
||||||
|
<xsd:element ref="vt:cy"/>
|
||||||
|
<xsd:element ref="vt:error"/>
|
||||||
|
<xsd:element ref="vt:stream"/>
|
||||||
|
<xsd:element ref="vt:ostream"/>
|
||||||
|
<xsd:element ref="vt:storage"/>
|
||||||
|
<xsd:element ref="vt:ostorage"/>
|
||||||
|
<xsd:element ref="vt:vstream"/>
|
||||||
|
<xsd:element ref="vt:clsid"/>
|
||||||
|
</xsd:choice>
|
||||||
|
<xsd:attribute name="fmtid" use="required" type="s:ST_Guid"/>
|
||||||
|
<xsd:attribute name="pid" use="required" type="xsd:int"/>
|
||||||
|
<xsd:attribute name="name" use="optional" type="xsd:string"/>
|
||||||
|
<xsd:attribute name="linkTarget" use="optional" type="xsd:string"/>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:schema>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"
|
||||||
|
xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
|
||||||
|
targetNamespace="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"
|
||||||
|
elementFormDefault="qualified" blockDefault="#all">
|
||||||
|
<xsd:import namespace="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
|
||||||
|
schemaLocation="shared-documentPropertiesVariantTypes.xsd"/>
|
||||||
|
<xsd:element name="Properties" type="CT_Properties"/>
|
||||||
|
<xsd:complexType name="CT_Properties">
|
||||||
|
<xsd:all>
|
||||||
|
<xsd:element name="Template" minOccurs="0" maxOccurs="1" type="xsd:string"/>
|
||||||
|
<xsd:element name="Manager" minOccurs="0" maxOccurs="1" type="xsd:string"/>
|
||||||
|
<xsd:element name="Company" minOccurs="0" maxOccurs="1" type="xsd:string"/>
|
||||||
|
<xsd:element name="Pages" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="Words" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="Characters" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="PresentationFormat" minOccurs="0" maxOccurs="1" type="xsd:string"/>
|
||||||
|
<xsd:element name="Lines" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="Paragraphs" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="Slides" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="Notes" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="TotalTime" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="HiddenSlides" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="MMClips" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="ScaleCrop" minOccurs="0" maxOccurs="1" type="xsd:boolean"/>
|
||||||
|
<xsd:element name="HeadingPairs" minOccurs="0" maxOccurs="1" type="CT_VectorVariant"/>
|
||||||
|
<xsd:element name="TitlesOfParts" minOccurs="0" maxOccurs="1" type="CT_VectorLpstr"/>
|
||||||
|
<xsd:element name="LinksUpToDate" minOccurs="0" maxOccurs="1" type="xsd:boolean"/>
|
||||||
|
<xsd:element name="CharactersWithSpaces" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
<xsd:element name="SharedDoc" minOccurs="0" maxOccurs="1" type="xsd:boolean"/>
|
||||||
|
<xsd:element name="HyperlinkBase" minOccurs="0" maxOccurs="1" type="xsd:string"/>
|
||||||
|
<xsd:element name="HLinks" minOccurs="0" maxOccurs="1" type="CT_VectorVariant"/>
|
||||||
|
<xsd:element name="HyperlinksChanged" minOccurs="0" maxOccurs="1" type="xsd:boolean"/>
|
||||||
|
<xsd:element name="DigSig" minOccurs="0" maxOccurs="1" type="CT_DigSigBlob"/>
|
||||||
|
<xsd:element name="Application" minOccurs="0" maxOccurs="1" type="xsd:string"/>
|
||||||
|
<xsd:element name="AppVersion" minOccurs="0" maxOccurs="1" type="xsd:string"/>
|
||||||
|
<xsd:element name="DocSecurity" minOccurs="0" maxOccurs="1" type="xsd:int"/>
|
||||||
|
</xsd:all>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_VectorVariant">
|
||||||
|
<xsd:sequence minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element ref="vt:vector"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_VectorLpstr">
|
||||||
|
<xsd:sequence minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element ref="vt:vector"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
<xsd:complexType name="CT_DigSigBlob">
|
||||||
|
<xsd:sequence minOccurs="1" maxOccurs="1">
|
||||||
|
<xsd:element ref="vt:blob"/>
|
||||||
|
</xsd:sequence>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:schema>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user