Compare commits

...

7 Commits

Author SHA1 Message Date
1e9925a9f2 修改打包交脚本 2025-07-02 20:08:23 +08:00
8a103224b5 修改打包交脚本 2025-07-02 19:59:41 +08:00
22b17a1d5d 修改打包交脚本 2025-07-02 19:43:52 +08:00
c0568cc1ec 修改打包交脚本 2025-07-02 19:37:10 +08:00
02341150a9 增加按照手机号排序
增加自动选择文以及识别文件
修改座位信息表位置
2025-07-02 16:26:35 +08:00
f52df5e42d Merge remote-tracking branch 'origin/master' 2025-07-01 15:45:55 +08:00
fcacab52c2 增加文件类型识别以及错误数据校验 2025-07-01 15:42:52 +08:00
26 changed files with 2174 additions and 1773 deletions

BIN
1.xlsx Normal file

Binary file not shown.

BIN
2.xlsx Normal file

Binary file not shown.

108
BUILD_FIXES.md Normal file
View File

@@ -0,0 +1,108 @@
# Windows构建问题修复指南
## 🔧 已修复的问题
### 1. Hidden import 'numpy.core._methods' not found
**解决方案**: 在spec文件中添加了完整的numpy隐藏导入
```python
'numpy.core._methods',
'numpy.core._dtype_ctypes',
'numpy.core._internal',
```
### 2. Hidden import "jinja2" not found
**解决方案**: 添加了jinja2相关导入
```python
'jinja2',
'jinja2.ext',
'markupsafe',
```
### 3. Library not found: msvcp140-1a0962f2a91a74c6d7136a768987a591.dll
**解决方案**:
- 禁用UPX压缩 (`upx=False`)
- 添加pandas完整依赖收集
- 使用`--collect-all=pandas`选项
## 🚀 使用修复后的构建脚本
### 方法1: 简化构建(推荐)
```bash
# Windows批处理
build_simple.bat
# 或直接运行Python脚本
python simple_windows_build.py
```
### 方法2: 完整构建
```bash
# 先测试环境
python test_build.py
# 运行完整构建
python windows_build.py
```
## ⚠️ 重要修复
### PyInstaller选项冲突问题
**问题**: `option(s) not allowed` 错误
**原因**: 使用spec文件时不能同时使用某些命令行选项
**解决**: 将所有配置写入spec文件命令行只保留基本选项
```python
# ❌ 错误用法
cmd = ['pyinstaller', '--collect-all=pandas', 'file.spec']
# ✅ 正确用法
cmd = ['pyinstaller', '--clean', '--noconfirm', 'file.spec']
```
## 📋 构建选项说明
### PyInstaller命令行选项
- `--clean`: 清理缓存
- `--noconfirm`: 不询问确认
- `--log-level=INFO`: 详细日志
- `--collect-all=pandas`: 收集pandas所有模块
- `--collect-all=numpy`: 收集numpy所有模块
- `--collect-all=openpyxl`: 收集openpyxl所有模块
### 关键隐藏导入
```python
'numpy.core._methods',
'pandas._libs.window.aggregations',
'ctypes.util',
'pkg_resources.py2_warn',
```
## ⚠️ 常见问题
### 1. 构建时间长
- 正常现象首次构建可能需要5-10分钟
- 后续构建会使用缓存,速度更快
### 2. 文件大小大
- 包含完整Python环境和所有依赖
- 可以通过排除不必要的模块来减小大小
### 3. 警告信息
- 大部分WARNING可以忽略
- 只要没有ERROR构建通常能成功
## 🎯 构建成功标志
```
✅ 构建成功! 耗时: XX.X秒
✅ 生成文件: dist/座位分配系统.exe
✅ 文件大小: XX.X MB
```
## 📦 分发包内容
构建成功后会生成:
- `座位分配系统_Windows_分发包/`
- `座位分配系统.exe` - 主程序
- `README.txt` - 使用说明
- `示例文件/` - 示例Excel文件如果存在

View File

@@ -5,22 +5,35 @@
## 🎯 功能特点
-**智能分配**: 支持1-10人的各种连坐需求
-**数据校验**: 完整的文件格式和逻辑校验
-**智能识别**: 自动识别Excel文件类型支持默认文件名和智能识别两种模式
-**双重分组**: 支持备注分组和手机号分组两种模式
-**数据校验**: 完整的文件格式和逻辑校验,支持重名+身份证号校验
-**不连续座位**: 自动处理座位号间隙
-**详细日志**: 完整的操作记录和结果验证
-**跨平台**: 支持Windows、macOS、Linux
-**智能覆盖**: 人员信息覆盖座位表对应列分配备注单独存储在第13列
## 📋 使用方法
### 准备Excel文件
1. **人员信息.xlsx** - 包含以下列:
1. **人员信息文件** - 包含以下列:
- 姓名、证件类型、证件号、手机号、备注
- 备注数字表示连坐人数备注4表示当前行+后3行共4人连坐
- **备注模式**: 备注数字表示连坐人数备注4表示当前行+后3行共4人连坐
- **手机号模式**: 相同手机号的人员自动识别为连坐组
2. **座位信息.xlsx** - 包含以下列:
2. **座位信息文件** - 包含以下列:
- 区域、楼层、排号、座位号
### 文件识别方式
程序启动时会提供两种文件识别方式:
1. **默认文件名** - 读取固定文件名 `人员信息.xlsx``座位信息.xlsx`
2. **智能识别** - 自动分析目录中的Excel文件并识别类型
- 根据列名特征自动判断文件类型
- 支持任意文件名(如 `1.xlsx``2.xlsx`
- 提供手动选择功能作为备选
### 运行程序
#### Python环境运行
@@ -41,12 +54,28 @@ python simple_build.py
python cross_platform_build.py
```
### 选择文件识别模式
程序运行时首先选择文件识别模式:
1. **默认文件名** - 读取 `人员信息.xlsx``座位信息.xlsx`
2. **智能识别** - 自动识别目录中的Excel文件
### 选择分组方式
然后选择连坐分组方式:
1. **备注分组** - 根据备注数字识别连坐组(默认)
2. **手机号分组** - 根据相同手机号识别连坐组
### 查看结果
程序运行后会生成:
- `座位信息_最终分配.xlsx` - 最终分配结果
- `最终座位分配日志.xlsx` - 详细分配记录
- `seat_allocation_log.txt` - 完整运行日志
- `log/座位信息_最终分配.xlsx` - 最终分配结果
- 第1-5列座位基本信息保持不变
- 第6-10列人员信息覆盖原座位表数据
- 第11-12列原座位表其他列保持不变
- 第13列分配备注新增列
- `log/最终座位分配日志.xlsx` - 详细分配记录
- `log/seat_allocation_log.txt` - 完整运行日志
## 🔧 构建选项
@@ -117,9 +146,12 @@ A区通道 | 一层 | 1排 | 3号
## ⚠️ 注意事项
1. **备注逻辑**: 备注数字表示连坐人数,只有组长填写备注,成员留空
2. **证件号格式**: 支持包含X的身份证号自动处理为字符串格式
3. **文字清理**: 自动清除姓名等字段的多余空格
4. **座位连续性**: 支持不连续座位号,算法会自动寻找合适的连续段
2. **手机号逻辑**: 相同手机号的人员会被识别为连坐组
3. **重名处理**: 支持重名人员,通过姓名+身份证号组合进行唯一性校验
4. **证件号格式**: 支持包含X的身份证号自动处理为字符串格式
5. **文字清理**: 自动清除姓名等字段的多余空格
6. **座位连续性**: 支持不连续座位号,算法会自动寻找合适的连续段
7. **智能覆盖**: 人员信息覆盖座位表对应列,分配备注单独存储
## 🔍 故障排除

View File

@@ -1,284 +0,0 @@
# 座位分配系统 - Windows部署说明
## 目录
- [概述](#概述)
- [系统要求](#系统要求)
- [开发者构建指南](#开发者构建指南)
- [用户使用指南](#用户使用指南)
- [故障排除](#故障排除)
- [技术支持](#技术支持)
## 概述
本文档说明如何在Windows系统上构建和部署座位分配系统。该系统支持1-10人连坐需求能够处理不连续座位分配。
**主要功能:**
- 智能座位分配算法
- 支持连坐组合1-10人
- 完整的文件校验
- 详细的分配日志
- 用户友好的命令行界面
## 系统要求
### 开发环境要求
- **操作系统**: Windows 7/10/11 (64位)
- **Python版本**: Python 3.8 或更高版本
- **内存**: 至少 2GB RAM
- **磁盘空间**: 至少 500MB 可用空间
- **网络**: 需要网络连接以下载依赖包
### 运行环境要求(最终用户)
- **操作系统**: Windows 7/10/11 (64位)
- **内存**: 至少 1GB RAM
- **磁盘空间**: 至少 100MB 可用空间
- **Excel**: Microsoft Excel 2010 或更高版本(查看结果文件)
## 开发者构建指南
### 1. 环境准备
#### 1.1 安装Python
从 [Python官网](https://www.python.org/downloads/) 下载并安装Python 3.8+
确保安装时勾选:
- ✅ Add Python to PATH
- ✅ Install pip
#### 1.2 验证安装
```cmd
python --version
pip --version
```
### 2. 下载源代码
```cmd
git clone <repository-url>
cd TableSynthesis
```
### 3. 自动构建(推荐)
#### 3.1 运行构建脚本
```cmd
python windows_build.py
```
构建脚本将自动完成:
- ✅ 检查系统环境
- ✅ 安装必要依赖
- ✅ 构建独立exe文件
- ✅ 创建分发包
#### 3.2 构建输出
构建成功后,会生成:
```
座位分配系统_Windows_分发包/
├── 座位分配系统.exe # 主程序
├── 运行座位分配系统.bat # 启动脚本
├── 使用说明.txt # 使用说明
├── 人员信息_示例.xlsx # 示例文件
└── 座位信息_示例.xlsx # 示例文件
```
### 4. 手动构建(可选)
如果自动构建失败,可以手动构建:
#### 4.1 安装依赖
```cmd
pip install pandas>=1.3.0 openpyxl>=3.0.0 numpy>=1.20.0 pyinstaller>=4.0
```
#### 4.2 使用PyInstaller构建
```cmd
pyinstaller --clean --noconfirm seat_allocation.spec
```
#### 4.3 手动创建分发包
```cmd
mkdir 座位分配系统_Windows_分发包
copy dist\座位分配系统.exe 座位分配系统_Windows_分发包\
copy 人员信息.xlsx 座位分配系统_Windows_分发包\人员信息_示例.xlsx
copy 座位信息.xlsx 座位分配系统_Windows_分发包\座位信息_示例.xlsx
copy 运行座位分配系统.bat 座位分配系统_Windows_分发包\
```
## 用户使用指南
### 1. 获取分发包
从开发者处获取 `座位分配系统_Windows_分发包` 文件夹,或下载已构建的发布版本。
### 2. 准备数据文件
#### 2.1 复制示例文件
```
人员信息_示例.xlsx → 人员信息.xlsx
座位信息_示例.xlsx → 座位信息.xlsx
```
#### 2.2 填写人员信息.xlsx
**必需列:**
- **姓名**: 人员姓名
- **证件类型**: 身份证、护照等
- **证件号**: 证件号码
- **手机号**: 联系电话
- **备注**: 连坐人数(留空表示单独坐)
**连坐规则:**
- 单人坐位:备注列留空
- 连坐组合:第一人填写总人数,其他人留空
**示例:**
| 姓名 | 证件类型 | 证件号 | 手机号 | 备注 |
|------|----------|--------|--------|------|
| 张三 | 身份证 | 123456789012345678 | 13800138000 | 3 |
| 李四 | 身份证 | 123456789012345679 | 13800138001 | |
| 王五 | 身份证 | 123456789012345680 | 13800138002 | |
| 赵六 | 身份证 | 123456789012345681 | 13800138003 | |
上述示例表示张三、李四、王五需要3人连坐赵六单独坐。
#### 2.3 填写座位信息.xlsx
**必需列:**
- **区域**: 座位区域(如"A区"、"B区"
- **楼层**: 楼层信息(如"1层"、"2层"
- **排号**: 排号(如"第1排"、"第2排"
- **座位号**: 具体座位号(如"1号"、"2号"
**示例:**
| 区域 | 楼层 | 排号 | 座位号 |
|------|------|------|--------|
| A区 | 1层 | 第1排 | 1号 |
| A区 | 1层 | 第1排 | 2号 |
| A区 | 1层 | 第1排 | 3号 |
### 3. 运行程序
#### 3.1 推荐方式使用bat脚本
双击 `运行座位分配系统.bat`
脚本会自动:
- ✅ 检查环境和文件
- ✅ 提供友好的错误提示
- ✅ 运行主程序
- ✅ 显示运行结果
#### 3.2 直接运行
双击 `座位分配系统.exe`
### 4. 查看结果
程序运行成功后,会生成以下文件:
- **座位信息_最终分配.xlsx**: 最终座位分配结果
- **最终座位分配日志.xlsx**: 详细分配记录
- **seat_allocation_log.txt**: 运行日志
使用Excel打开xlsx文件查看分配结果。
## 故障排除
### 常见问题
#### Q1: 程序无法启动
**可能原因:**
- 系统不兼容需要Windows 7+
- 缺少Visual C++ Redistributable
**解决方案:**
1. 确认系统版本Windows 7/10/11 64位
2. 下载安装 [Microsoft Visual C++ Redistributable](https://aka.ms/vs/17/release/vc_redist.x64.exe)
#### Q2: 提示缺少依赖包
**可能原因:**
- 网络连接问题
- 权限不足
**解决方案:**
1. 确保网络连接正常
2. 以管理员身份运行
3. 手动安装:`pip install pandas openpyxl numpy`
#### Q3: 文件权限错误
**可能原因:**
- Excel文件被占用
- 程序目录只读
**解决方案:**
1. 关闭所有Excel窗口
2. 确保程序目录有写入权限
3. 避免在系统盘根目录运行
#### Q4: 数据文件格式错误
**可能原因:**
- 列名不匹配
- 数据类型错误
- 编码问题
**解决方案:**
1. 对照示例文件检查列名
2. 确保数据格式正确
3. 使用UTF-8编码保存
#### Q5: 无法找到连续座位
**可能原因:**
- 座位信息不连续
- 连坐组过大
**解决方案:**
1. 检查座位信息的连续性
2. 适当调整连坐组大小
3. 增加座位数量
### 日志分析
查看 `seat_allocation_log.txt` 文件可获取详细错误信息:
- **文件加载错误**: 检查文件路径和格式
- **数据校验错误**: 检查数据完整性
- **分配算法错误**: 检查座位和人员匹配
### 性能优化
#### 大数据处理
- **人员数量**: 建议不超过1000人
- **座位数量**: 建议不超过2000个
- **连坐组大小**: 建议不超过8人
#### 内存使用
- 小型场景(<100人: 约50MB内存
- 中型场景100-500人: 约100MB内存
- 大型场景500-1000人: 约200MB内存
## 技术支持
### 获取帮助
1. **查看日志文件**: `seat_allocation_log.txt`
2. **检查使用说明**: `使用说明.txt`
3. **参考示例文件**: 确保数据格式正确
### 报告问题
报告问题时请提供
- 系统信息操作系统版本
- 错误截图或错误信息
- 数据文件脱敏后
- seat_allocation_log.txt 内容
### 更新与维护
- 定期检查更新版本
- 备份重要数据文件
- 保持系统和依赖包更新
---
**版本**: v1.0
**最后更新**: 2024年12月
**兼容性**: Windows 7/10/11 (64位)

31
build_simple.bat Normal file
View File

@@ -0,0 +1,31 @@
@echo off
chcp 65001 >nul
echo ============================================================
echo 简化Windows构建脚本
echo ============================================================
echo.
echo 检查Python环境...
python --version
if errorlevel 1 (
echo ❌ Python未安装或未添加到PATH
pause
exit /b 1
)
echo.
echo 检查依赖包...
python -c "import pandas, numpy, openpyxl, PyInstaller; print('✅ 所有依赖已安装')"
if errorlevel 1 (
echo ❌ 缺少必要的依赖包
echo 正在安装依赖...
pip install pandas numpy openpyxl pyinstaller
)
echo.
echo 开始构建...
python simple_windows_build.py
echo.
echo 构建完成!
pause

138
build_windows.bat Normal file
View File

@@ -0,0 +1,138 @@
@echo off
chcp 65001 >nul
echo ================================
echo 座位分配系统 Windows 打包脚本
echo ================================
echo.
:: 检查Python是否安装
python --version >nul 2>&1
if %errorlevel% neq 0 (
echo [错误] Python未安装或不在PATH中
echo 请先安装Python 3.7+
pause
exit /b 1
)
echo [信息] 检查Python版本...
python --version
:: 检查pip是否可用
pip --version >nul 2>&1
if %errorlevel% neq 0 (
echo [错误] pip不可用
pause
exit /b 1
)
:: 安装PyInstaller
echo.
echo [步骤1] 安装PyInstaller...
pip install pyinstaller
:: 检查PyInstaller是否安装成功
pyinstaller --version >nul 2>&1
if %errorlevel% neq 0 (
echo [错误] PyInstaller安装失败
pause
exit /b 1
)
:: 创建build目录
echo.
echo [步骤2] 创建构建目录...
if not exist "build" mkdir build
if not exist "dist" mkdir dist
:: 安装依赖包
echo.
echo [步骤3] 安装依赖包...
pip install pandas numpy openpyxl
:: 打包主程序
echo.
echo [步骤4] 开始打包座位分配系统...
pyinstaller --onefile ^
--console ^
--name "座位分配系统" ^
--icon=icon.ico ^
--add-data "README.md;." ^
--distpath "dist" ^
--workpath "build" ^
--specpath "build" ^
--clean ^
seat_allocation_system.py
:: 检查打包是否成功
if exist "dist\座位分配系统.exe" (
echo.
echo [成功] 打包完成!
echo 可执行文件位置: dist\座位分配系统.exe
:: 创建发布目录
echo.
echo [步骤5] 创建发布包...
if not exist "release" mkdir release
:: 复制可执行文件
copy "dist\座位分配系统.exe" "release\"
:: 创建示例文件夹
if not exist "release\示例文件" mkdir "release\示例文件"
:: 复制示例文件(如果存在)
if exist "人员信息_示例.xlsx" copy "人员信息_示例.xlsx" "release\示例文件\"
if exist "座位信息_示例.xlsx" copy "座位信息_示例.xlsx" "release\示例文件\"
:: 创建使用说明
echo.
echo [步骤6] 创建使用说明...
(
echo 座位分配系统 v2.0
echo ==================
echo.
echo 使用方法:
echo 1. 将人员信息和座位信息的Excel文件放在程序同一目录下
echo 2. 双击运行"座位分配系统.exe"
echo 3. 按照提示操作
echo.
echo 文件要求:
echo - 人员信息文件:包含姓名、证件类型、证件号、手机号、备注等列
echo - 座位信息文件:包含区域、楼层、排号、座位号等列
echo.
echo 输出文件:
echo - log/座位信息_最终分配.xlsx (分配结果)
echo - log/最终座位分配日志.xlsx (详细日志)
echo - log/seat_allocation_log.txt (运行日志)
echo.
echo 注意事项:
echo - 程序支持智能文件识别
echo - 支持1-10人连坐需求
echo - 支持备注分组和手机号分组两种模式
echo.
echo 如有问题请查看日志文件
) > "release\使用说明.txt"
echo.
echo ================================
echo 打包完成!
echo ================================
echo.
echo 发布文件位置: release\
echo 主程序: release\座位分配系统.exe
echo 使用说明: release\使用说明.txt
echo 示例文件: release\示例文件\
echo.
echo 您可以将整个release文件夹分发给用户
) else (
echo.
echo [错误] 打包失败!
echo 请检查错误信息并重试
pause
exit /b 1
)
echo.
echo 按任意键继续...
pause >nul

226
build_windows.ps1 Normal file
View File

@@ -0,0 +1,226 @@
# 座位分配系统 Windows 打包脚本 (PowerShell版本)
# 编码UTF-8
Write-Host "================================" -ForegroundColor Cyan
Write-Host " 座位分配系统 Windows 打包脚本" -ForegroundColor Cyan
Write-Host "================================" -ForegroundColor Cyan
Write-Host ""
# 设置错误处理
$ErrorActionPreference = "Stop"
try {
# 检查Python是否安装
Write-Host "[信息] 检查Python环境..." -ForegroundColor Yellow
$pythonVersion = python --version 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Python未安装或不在PATH中请先安装Python 3.7+"
}
Write-Host "Python版本: $pythonVersion" -ForegroundColor Green
# 检查pip
$pipVersion = pip --version 2>&1
if ($LASTEXITCODE -ne 0) {
throw "pip不可用"
}
Write-Host "pip版本: $pipVersion" -ForegroundColor Green
# 升级pip
Write-Host ""
Write-Host "[步骤1] 升级pip..." -ForegroundColor Yellow
python -m pip install --upgrade pip
# 安装PyInstaller
Write-Host ""
Write-Host "[步骤2] 安装PyInstaller..." -ForegroundColor Yellow
pip install pyinstaller
# 安装依赖包
Write-Host ""
Write-Host "[步骤3] 安装依赖包..." -ForegroundColor Yellow
pip install pandas numpy openpyxl
# 创建必要的目录
Write-Host ""
Write-Host "[步骤4] 创建构建目录..." -ForegroundColor Yellow
$dirs = @("build", "dist", "release", "release\示例文件")
foreach ($dir in $dirs) {
if (!(Test-Path $dir)) {
New-Item -ItemType Directory -Path $dir -Force | Out-Null
Write-Host "创建目录: $dir" -ForegroundColor Gray
}
}
# 检查主文件是否存在
if (!(Test-Path "seat_allocation_system.py")) {
throw "找不到主程序文件 seat_allocation_system.py"
}
# 打包主程序
Write-Host ""
Write-Host "[步骤5] 开始打包座位分配系统..." -ForegroundColor Yellow
Write-Host "这可能需要几分钟时间,请耐心等待..." -ForegroundColor Gray
$pyinstallerArgs = @(
"--onefile",
"--console",
"--name", "座位分配系统",
"--distpath", "dist",
"--workpath", "build",
"--specpath", "build",
"--clean",
"seat_allocation_system.py"
)
# 如果存在图标文件,添加图标参数
if (Test-Path "icon.ico") {
$pyinstallerArgs += "--icon", "icon.ico"
}
# 如果存在README文件添加数据文件
if (Test-Path "README.md") {
$pyinstallerArgs += "--add-data", "README.md;."
}
& pyinstaller @pyinstallerArgs
if ($LASTEXITCODE -ne 0) {
throw "PyInstaller打包失败"
}
# 检查打包结果
$exePath = "dist\座位分配系统.exe"
if (!(Test-Path $exePath)) {
throw "打包失败:找不到生成的可执行文件"
}
# 复制文件到发布目录
Write-Host ""
Write-Host "[步骤6] 创建发布包..." -ForegroundColor Yellow
# 复制主程序
Copy-Item $exePath "release\" -Force
Write-Host "复制主程序到发布目录" -ForegroundColor Gray
# 复制示例文件
$exampleFiles = @("人员信息_示例.xlsx", "座位信息_示例.xlsx", "*.xlsx")
foreach ($pattern in $exampleFiles) {
$files = Get-ChildItem -Path . -Name $pattern -ErrorAction SilentlyContinue
foreach ($file in $files) {
if ($file -notlike "*_最终分配*" -and $file -notlike "*日志*") {
Copy-Item $file "release\示例文件\" -Force -ErrorAction SilentlyContinue
Write-Host "复制示例文件: $file" -ForegroundColor Gray
}
}
}
# 创建使用说明
Write-Host ""
Write-Host "[步骤7] 创建使用说明..." -ForegroundColor Yellow
$readme = @"
v2.0
==================
使
1. Excel
2. ".exe"
3.
-
-
- Excel
- 1-10
-
-
-
-
log
- log/_.xlsx
- log/.xlsx
- log/seat_allocation_log.txt
1.
2.
3.
4.
5.
- Excel
- ""
-
-
log
- v2.0 ()
- Windows 7/8/10/11
- Python
"@
$readme | Out-File -FilePath "release\使用说明.txt" -Encoding UTF8
# 创建批处理启动文件(可选)
$batchContent = @"
@echo off
chcp 65001 >nul
echo ...
echo.
.exe
echo.
echo
pause
"@
$batchContent | Out-File -FilePath "release\启动座位分配系统.bat" -Encoding UTF8
# 显示完成信息
Write-Host ""
Write-Host "================================" -ForegroundColor Green
Write-Host " 打包完成!" -ForegroundColor Green
Write-Host "================================" -ForegroundColor Green
Write-Host ""
Write-Host "发布文件位置:" -ForegroundColor Cyan
Write-Host "📁 release\" -ForegroundColor Yellow
Write-Host " ├── 座位分配系统.exe (主程序)" -ForegroundColor White
Write-Host " ├── 启动座位分配系统.bat (启动脚本)" -ForegroundColor White
Write-Host " ├── 使用说明.txt (使用说明)" -ForegroundColor White
Write-Host " └── 示例文件\ (示例文件夹)" -ForegroundColor White
Write-Host ""
Write-Host "文件大小信息:" -ForegroundColor Cyan
$fileSize = (Get-Item $exePath).Length / 1MB
Write-Host "主程序大小: $([math]::Round($fileSize, 2)) MB" -ForegroundColor Yellow
Write-Host ""
Write-Host "部署说明:" -ForegroundColor Cyan
Write-Host "1. 将整个 release 文件夹分发给用户" -ForegroundColor White
Write-Host "2. 用户直接运行 座位分配系统.exe 或 启动座位分配系统.bat" -ForegroundColor White
Write-Host "3. 无需安装Python环境" -ForegroundColor White
Write-Host ""
Write-Host "✅ 打包成功完成!" -ForegroundColor Green
} catch {
Write-Host ""
Write-Host "❌ 打包过程中出现错误:" -ForegroundColor Red
Write-Host $_.Exception.Message -ForegroundColor Red
Write-Host ""
Write-Host "常见解决方案:" -ForegroundColor Yellow
Write-Host "1. 确保已安装Python 3.7+" -ForegroundColor White
Write-Host "2. 确保网络连接正常(需要下载依赖包)" -ForegroundColor White
Write-Host "3. 确保有足够的磁盘空间" -ForegroundColor White
Write-Host "4. 尝试以管理员权限运行此脚本" -ForegroundColor White
exit 1
}
Write-Host ""
Write-Host "按任意键继续..." -ForegroundColor Gray
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")

View File

@@ -1,89 +0,0 @@
# GitHub Actions工作流 - 多平台构建
# 将此文件放在 .github/workflows/ 目录下
name: Build Multi-Platform Executables
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
build-windows:
runs-on: windows-latest
strategy:
matrix:
arch: [x64, x86]
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
architecture: ${{ matrix.arch }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pandas openpyxl pyinstaller
- name: Build executable
run: |
pyinstaller --onefile --console --name "座位分配系统_${{ matrix.arch }}" seat_allocation_system.py
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: windows-${{ matrix.arch }}
path: dist/座位分配系统_${{ matrix.arch }}.exe
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pandas openpyxl pyinstaller
- name: Build executable
run: |
pyinstaller --onefile --console --name "座位分配系统_macos" seat_allocation_system.py
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: macos
path: dist/座位分配系统_macos
create-release:
needs: [build-windows, build-macos]
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Download all artifacts
uses: actions/download-artifact@v3
- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ github.run_number }}
name: Release v${{ github.run_number }}
files: |
windows-x64/座位分配系统_x64.exe
windows-x86/座位分配系统_x86.exe
macos/座位分配系统_macos
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,326 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
跨平台构建脚本
在macOS上构建Windows x86/x64版本的exe文件
使用Wine和PyInstaller实现跨平台编译
"""
import os
import sys
import subprocess
import platform
import shutil
from pathlib import Path
def check_dependencies():
"""检查必要的依赖"""
print("检查构建依赖...")
# 检查是否在macOS上
if platform.system() != 'Darwin':
print(f"当前系统: {platform.system()}")
print("此脚本专为macOS设计用于构建Windows版本")
return False
# 检查Python包
required_packages = ['pandas', 'openpyxl', 'pyinstaller']
missing_packages = []
for package in required_packages:
try:
__import__(package)
print(f"{package} 已安装")
except ImportError:
missing_packages.append(package)
print(f"{package} 未安装")
if missing_packages:
print(f"\n安装缺失的包...")
for package in missing_packages:
try:
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
print(f"{package} 安装成功")
except subprocess.CalledProcessError:
print(f"{package} 安装失败")
return False
return True
def install_wine():
"""安装Wine如果需要"""
print("\n检查Wine环境...")
# 检查是否已安装Wine
try:
result = subprocess.run(['wine', '--version'], capture_output=True, text=True)
if result.returncode == 0:
print(f"✅ Wine已安装: {result.stdout.strip()}")
return True
except FileNotFoundError:
pass
print("Wine未安装开始安装...")
print("请按照以下步骤手动安装Wine:")
print("1. 安装Homebrew (如果未安装): /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"")
print("2. 安装Wine: brew install --cask wine-stable")
print("3. 配置Wine: winecfg")
response = input("是否已完成Wine安装? (y/N): ")
return response.lower() == 'y'
def setup_wine_python():
"""在Wine中设置Python环境"""
print("\n设置Wine Python环境...")
# 检查Wine中是否已安装Python
try:
result = subprocess.run(['wine', 'python', '--version'], capture_output=True, text=True)
if result.returncode == 0:
print(f"✅ Wine Python已安装: {result.stdout.strip()}")
return True
except:
pass
print("需要在Wine中安装Python...")
print("请按照以下步骤:")
print("1. 下载Windows版Python: https://www.python.org/downloads/windows/")
print("2. 使用Wine安装: wine python-3.10.x-amd64.exe")
print("3. 安装pip包: wine python -m pip install pandas openpyxl pyinstaller")
response = input("是否已完成Wine Python安装? (y/N): ")
return response.lower() == 'y'
def create_build_spec(target_arch='x64'):
"""创建构建规格文件"""
exe_name = f"座位分配系统_{target_arch}"
spec_content = f'''# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['seat_allocation_system.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[
'pandas',
'openpyxl',
'numpy',
'xlsxwriter',
'xlrd',
'datetime',
'pathlib'
],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[
'matplotlib',
'scipy',
'IPython',
'jupyter',
'notebook',
'tkinter'
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='{exe_name}',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=None,
)
'''
spec_file = f'{exe_name}.spec'
with open(spec_file, 'w', encoding='utf-8') as f:
f.write(spec_content)
return spec_file, exe_name
def build_with_docker():
"""使用Docker构建Windows版本"""
print("\n使用Docker构建Windows版本...")
# 创建Dockerfile
dockerfile_content = '''FROM python:3.10-windowsservercore
# 设置工作目录
WORKDIR /app
# 复制文件
COPY requirements.txt .
COPY seat_allocation_system.py .
# 安装依赖
RUN pip install -r requirements.txt
# 构建exe
RUN pyinstaller --onefile --console --name "座位分配系统_x64" seat_allocation_system.py
# 输出目录
VOLUME ["/app/dist"]
'''
with open('Dockerfile.windows', 'w') as f:
f.write(dockerfile_content)
print("Docker方法需要Docker Desktop和Windows容器支持")
print("这是一个高级选项,建议使用其他方法")
return False
def build_native_macos():
"""在macOS上直接构建生成macOS版本"""
print("\n在macOS上构建本地版本...")
# 清理之前的构建
if os.path.exists('dist'):
shutil.rmtree('dist')
if os.path.exists('build'):
shutil.rmtree('build')
# 构建命令
cmd = [
sys.executable, '-m', 'PyInstaller',
'--onefile',
'--console',
'--clean',
'--name', '座位分配系统_macos',
'seat_allocation_system.py'
]
try:
print(f"执行命令: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print("✅ macOS版本构建成功!")
# 检查生成的文件
app_path = Path('dist') / '座位分配系统_macos'
if app_path.exists():
file_size = app_path.stat().st_size / (1024 * 1024) # MB
print(f"生成文件: {app_path}")
print(f"文件大小: {file_size:.1f} MB")
return True, app_path
else:
print("❌ 构建失败!")
print("错误输出:")
print(result.stderr)
return False, None
except Exception as e:
print(f"❌ 构建过程中出现错误: {e}")
return False, None
def create_cross_compile_script():
"""创建跨编译脚本"""
script_content = '''#!/bin/bash
# Windows交叉编译脚本
echo "座位分配系统 - Windows交叉编译"
echo "================================"
# 检查是否安装了mingw-w64
if ! command -v x86_64-w64-mingw32-gcc &> /dev/null; then
echo "安装mingw-w64..."
brew install mingw-w64
fi
# 设置交叉编译环境
export CC=x86_64-w64-mingw32-gcc
export CXX=x86_64-w64-mingw32-g++
# 构建Windows版本
echo "开始构建Windows x64版本..."
python -m PyInstaller \\
--onefile \\
--console \\
--clean \\
--name "座位分配系统_x64" \\
--target-arch x86_64 \\
seat_allocation_system.py
echo "构建完成!"
'''
with open('cross_compile.sh', 'w') as f:
f.write(script_content)
# 设置执行权限
os.chmod('cross_compile.sh', 0o755)
print("✅ 已创建交叉编译脚本: cross_compile.sh")
def main():
"""主函数"""
print("座位分配系统 - 跨平台构建工具")
print("=" * 50)
print("在macOS上构建Windows版本")
# 检查依赖
if not check_dependencies():
return
print("\n选择构建方法:")
print("1. 构建macOS版本推荐")
print("2. 使用Wine构建Windows版本复杂")
print("3. 创建交叉编译脚本(实验性)")
print("4. 生成Docker构建文件高级")
choice = input("\n请选择 (1-4): ").strip()
if choice == '1':
print("\n构建macOS版本...")
success, app_path = build_native_macos()
if success:
print(f"\n🎉 构建完成!")
print(f"生成文件: {app_path}")
print("\n注意: 这是macOS版本不能在Windows上运行")
print("如需Windows版本请在Windows系统上运行构建脚本")
elif choice == '2':
print("\n使用Wine构建Windows版本...")
if install_wine() and setup_wine_python():
print("Wine环境已准备就绪")
print("请手动在Wine中运行: wine python windows_build.py")
else:
print("Wine环境设置失败")
elif choice == '3':
print("\n创建交叉编译脚本...")
create_cross_compile_script()
print("请运行: ./cross_compile.sh")
elif choice == '4':
print("\n生成Docker构建文件...")
build_with_docker()
else:
print("无效选择")
if __name__ == "__main__":
main()

42
fix_dll_build.bat Normal file
View File

@@ -0,0 +1,42 @@
@echo off
chcp 65001 >nul
echo ============================================================
echo pandas DLL修复构建脚本
echo 专门解决: ImportError: DLL load failed while importing aggregations
echo ============================================================
echo.
echo 检查Python环境...
python --version
if errorlevel 1 (
echo ❌ Python未安装或未添加到PATH
pause
exit /b 1
)
echo.
echo 检查pandas安装...
python -c "import pandas; print(f'✅ pandas版本: {pandas.__version__}')"
if errorlevel 1 (
echo ❌ pandas未安装
echo 正在安装pandas...
pip install pandas
)
echo.
echo 检查PyInstaller...
python -c "import PyInstaller; print('✅ PyInstaller已安装')"
if errorlevel 1 (
echo ❌ PyInstaller未安装
echo 正在安装PyInstaller...
pip install pyinstaller
)
echo.
echo 开始DLL修复构建...
python fix_dll_build.py
echo.
echo 构建完成!
echo 如果成功,可执行文件位于: dist\座位分配系统_修复版.exe
pause

302
fix_dll_build.py Normal file
View File

@@ -0,0 +1,302 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
修复DLL加载失败的专用构建脚本
专门解决pandas._libs.window.aggregations DLL问题
"""
import sys
import subprocess
import platform
import site
from pathlib import Path
def get_pandas_libs_path():
"""获取pandas库文件路径"""
try:
import pandas
pandas_path = Path(pandas.__file__).parent
libs_path = pandas_path / '_libs'
return libs_path
except ImportError:
return None
def create_dll_fix_spec():
"""创建修复DLL问题的spec文件"""
# 获取pandas库路径
pandas_libs = get_pandas_libs_path()
if not pandas_libs or not pandas_libs.exists():
print("❌ 无法找到pandas库路径")
return None
print(f"✅ 找到pandas库路径: {pandas_libs}")
# 查找所有.pyd文件Windows DLL
pyd_files = list(pandas_libs.glob('**/*.pyd'))
print(f"✅ 找到 {len(pyd_files)} 个pandas DLL文件")
# 构建binaries列表
binaries_list = []
for pyd_file in pyd_files:
rel_path = pyd_file.relative_to(pandas_libs.parent)
binaries_list.append(f"(r'{pyd_file}', r'{rel_path.parent}')")
binaries_str = ',\n '.join(binaries_list)
spec_content = f'''# -*- mode: python ; coding: utf-8 -*-
# 专门修复pandas DLL加载问题的配置
import sys
from pathlib import Path
block_cipher = None
a = Analysis(
['seat_allocation_system.py'],
pathex=[],
binaries=[
# 手动包含所有pandas DLL文件
{binaries_str}
],
datas=[],
hiddenimports=[
# 核心模块
'pandas',
'numpy',
'openpyxl',
# pandas核心库必须
'pandas._libs',
'pandas._libs.lib',
'pandas._libs.hashtable',
'pandas._libs.tslib',
'pandas._libs.algos',
'pandas._libs.join',
'pandas._libs.index',
'pandas._libs.internals',
'pandas._libs.writers',
'pandas._libs.parsers',
'pandas._libs.testing',
'pandas._libs.sparse',
'pandas._libs.reduction',
'pandas._libs.ops',
'pandas._libs.missing',
'pandas._libs.groupby',
'pandas._libs.reshape',
# pandas时间序列库
'pandas._libs.tslibs',
'pandas._libs.tslibs.base',
'pandas._libs.tslibs.ccalendar',
'pandas._libs.tslibs.conversion',
'pandas._libs.tslibs.dtypes',
'pandas._libs.tslibs.fields',
'pandas._libs.tslibs.nattype',
'pandas._libs.tslibs.np_datetime',
'pandas._libs.tslibs.offsets',
'pandas._libs.tslibs.parsing',
'pandas._libs.tslibs.period',
'pandas._libs.tslibs.strptime',
'pandas._libs.tslibs.timedeltas',
'pandas._libs.tslibs.timestamps',
'pandas._libs.tslibs.timezones',
'pandas._libs.tslibs.tzconversion',
'pandas._libs.tslibs.vectorized',
# pandas窗口函数库关键
'pandas._libs.window',
'pandas._libs.window.aggregations',
'pandas._libs.window.indexers',
# numpy核心
'numpy.core._methods',
'numpy.core._dtype_ctypes',
'numpy.core.multiarray',
'numpy.core.umath',
# 其他必要模块
'ctypes.util',
'pkg_resources.py2_warn',
'encodings.utf_8',
'encodings.gbk',
# openpyxl
'openpyxl.workbook',
'openpyxl.worksheet',
'openpyxl.styles',
],
hookspath=[],
hooksconfig={{}},
runtime_hooks=[],
excludes=[
'matplotlib',
'scipy',
'IPython',
'jupyter',
'tkinter',
'PyQt5',
'PyQt6',
'PIL',
'cv2',
'sklearn',
'tensorflow',
'torch',
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='座位分配系统_修复版',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False, # 禁用UPX压缩避免DLL问题
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
'''
spec_file = Path('dll_fix.spec')
with open(spec_file, 'w', encoding='utf-8') as f:
f.write(spec_content)
print(f"✅ 创建DLL修复spec文件: {spec_file}")
return spec_file
def build_with_dll_fix():
"""使用DLL修复配置构建"""
print("=" * 60)
print("DLL修复构建")
print("=" * 60)
# 检查环境
if platform.system() != 'Windows':
print("❌ 此脚本仅适用于Windows")
return False
print(f"✅ 系统: {platform.system()} {platform.release()}")
# 检查主文件
main_file = Path('seat_allocation_system.py')
if not main_file.exists():
print("❌ 未找到主程序文件")
return False
# 创建修复spec文件
spec_file = create_dll_fix_spec()
if not spec_file:
return False
# 清理旧文件
dist_dir = Path('dist')
if dist_dir.exists():
import shutil
shutil.rmtree(dist_dir)
print("✅ 清理dist目录")
# 构建命令
cmd = [
sys.executable, '-m', 'PyInstaller',
'--clean',
'--noconfirm',
'--log-level=WARN', # 减少输出
str(spec_file)
]
print(f"\n执行命令: {' '.join(cmd)}")
print("开始DLL修复构建...")
try:
# 执行构建
result = subprocess.run(
cmd,
capture_output=True,
text=True,
encoding='utf-8',
errors='ignore'
)
# 显示结果
if result.returncode == 0:
print("✅ DLL修复构建成功!")
# 检查生成的文件
exe_path = Path('dist/座位分配系统_修复版.exe')
if exe_path.exists():
size_mb = exe_path.stat().st_size / (1024 * 1024)
print(f"✅ 生成文件: {exe_path}")
print(f"✅ 文件大小: {size_mb:.1f} MB")
# 测试运行
print("\n🧪 测试运行...")
test_cmd = [str(exe_path), '--help']
try:
test_result = subprocess.run(
test_cmd,
capture_output=True,
text=True,
timeout=10
)
if test_result.returncode == 0:
print("✅ 程序可以正常启动")
else:
print("⚠️ 程序启动有问题,但文件已生成")
except subprocess.TimeoutExpired:
print("⚠️ 测试超时,但文件已生成")
except Exception as e:
print(f"⚠️ 测试失败: {e}")
return True
else:
print("❌ 未找到生成的exe文件")
return False
else:
print("❌ DLL修复构建失败!")
print("错误输出:")
print(result.stderr)
return False
except Exception as e:
print(f"❌ 构建出错: {e}")
return False
def main():
"""主函数"""
print("🔧 pandas DLL加载失败修复工具")
print("专门解决: ImportError: DLL load failed while importing aggregations")
print()
success = build_with_dll_fix()
if success:
print("\n🎉 DLL修复构建完成!")
print("可执行文件: dist/座位分配系统_修复版.exe")
print("\n💡 如果仍有问题,请尝试:")
print("1. 在目标机器上安装 Visual C++ Redistributable")
print("2. 确保目标机器有相同的Windows版本")
else:
print("\n❌ DLL修复构建失败!")
print("请检查pandas安装是否完整")
input("\n按Enter键退出...")
if __name__ == '__main__':
main()

View File

@@ -1,128 +0,0 @@
# -*- mode: python ; coding: utf-8 -*-
# 完全独立的PyInstaller配置
# 包含Python解释器和所有依赖无需目标机器安装Python
import sys
from pathlib import Path
block_cipher = None
a = Analysis(
['seat_allocation_system.py'],
pathex=[str(Path.cwd())],
binaries=[],
datas=[
# 包含必要的数据文件(如果有的话)
],
hiddenimports=[
# 核心依赖
'pandas',
'openpyxl',
'numpy',
'xlsxwriter',
'xlrd',
'datetime',
'pathlib',
'subprocess',
'platform',
'sys',
'os',
# openpyxl相关
'openpyxl.workbook',
'openpyxl.worksheet',
'openpyxl.styles',
'openpyxl.utils',
'openpyxl.writer.excel',
'openpyxl.reader.excel',
'openpyxl.cell',
'openpyxl.formatting',
'openpyxl.formatting.rule',
# pandas相关
'pandas.io.excel',
'pandas.io.common',
'pandas.io.parsers',
'pandas.io.formats',
'pandas.io.formats.excel',
'pandas._libs.tslibs.timedeltas',
'pandas._libs.tslibs.np_datetime',
'pandas._libs.tslibs.nattype',
'pandas._libs.skiplist',
# numpy相关
'numpy.core.multiarray',
'numpy.core.umath',
'numpy.core._methods',
'numpy.lib.format',
'numpy.random._pickle',
# 编码相关
'encodings',
'encodings.utf_8',
'encodings.gbk',
'encodings.cp1252',
# 其他必要模块
'pkg_resources.py2_warn',
'_ctypes',
'ctypes.util'
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
# 排除不必要的大型库
'matplotlib',
'scipy',
'IPython',
'jupyter',
'notebook',
'tkinter',
'PyQt5',
'PyQt6',
'PySide2',
'PySide6',
'test',
'tests',
'unittest',
'doctest',
'pdb',
'pydoc',
'setuptools',
'pip',
'wheel',
'distutils'
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
# 过滤和优化
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='座位分配系统',
debug=False,
bootloader_ignore_signals=False,
strip=False, # 保留符号信息以便调试
upx=False, # 不使用UPX压缩以提高兼容性
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
# 确保包含Python运行时
exclude_binaries=False,
)

View File

@@ -1,465 +0,0 @@
[2025-06-30 20:58:50] ============================================================
[2025-06-30 20:58:50] 座位分配系统 - 文件校验
[2025-06-30 20:58:50] ============================================================
[2025-06-30 20:58:50] === 开始加载数据 ===
[2025-06-30 20:58:50] 清理文字数据中的空格...
[2025-06-30 20:58:50] ✅ 文字数据清理完成
[2025-06-30 20:58:50] ✅ 文件加载成功
[2025-06-30 20:58:50] 人员信息: 100 行 × 6 列
[2025-06-30 20:58:50] 座位信息: 100 行 × 12 列
[2025-06-30 20:58:50]
=== 人员信息结构校验 ===
[2025-06-30 20:58:50] ✅ 所有必需列都存在
[2025-06-30 20:58:50]
数据完整性检查:
[2025-06-30 20:58:50] ✅ 姓名 列数据完整
[2025-06-30 20:58:50] ✅ 证件类型 列数据完整
[2025-06-30 20:58:50] ✅ 证件号 列数据完整
[2025-06-30 20:58:50] ✅ 手机号 列数据完整
[2025-06-30 20:58:50] ✅ 姓名无重复
[2025-06-30 20:58:50]
=== 连坐组校验 ===
[2025-06-30 20:58:50] ✅ 第 1 组: 安春利, 杨茗珺 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 2 组: 侯俊帆, 杨璇羽 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 3 组: 刘金龙, 杨宇航 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 4 组: 张城, 张倩 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 5 组: 陈新闻, 刘佳欢 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 6 组: 王鹏舒 (1人连坐)
[2025-06-30 20:58:50] ✅ 第 7 组: 孙昊, 晋秋跃 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 8 组: 刘晓雨, 李宇峥, 李霞 (3人连坐)
[2025-06-30 20:58:50] ✅ 第 9 组: 常洁, 陈媛, 王晨婧, 胡艳彬 (4人连坐)
[2025-06-30 20:58:50] ✅ 第 10 组: 高尚谦, 胡梦幻 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 11 组: 郝罡, 林杰 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 12 组: 王璐瑶, 刘昌昊 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 13 组: 吴旭 (1人连坐)
[2025-06-30 20:58:50] ✅ 第 14 组: 刘捷 (1人连坐)
[2025-06-30 20:58:50] ✅ 第 15 组: 尹泽, 尹依伊, 夏明慧, 贾娜 (4人连坐)
[2025-06-30 20:58:50] ✅ 第 16 组: 张艺馨 (单独)
[2025-06-30 20:58:50] ✅ 第 17 组: 陈宇, 房晨 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 18 组: 赵瑞, 郭闯远 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 19 组: 张然殊 (1人连坐)
[2025-06-30 20:58:50] ✅ 第 20 组: 王莲 (1人连坐)
[2025-06-30 20:58:50] ✅ 第 21 组: 王建华, 赵雅楠, 王格 (3人连坐)
[2025-06-30 20:58:50] ✅ 第 22 组: 尚校, 尚冕 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 23 组: 徐金瑞, 赵春颖 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 24 组: 赵占博, 孙羽佳, 季海洋, 刘柳 (4人连坐)
[2025-06-30 20:58:50] ✅ 第 25 组: 解子辰 (1人连坐)
[2025-06-30 20:58:50] ✅ 第 26 组: 杨文, 马新雨 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 27 组: 白建新, 李海生, 李苓 (3人连坐)
[2025-06-30 20:58:50] ✅ 第 28 组: 周勇, 常佳琦 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 29 组: 马志鑫, 肖金玲, 马学利 (3人连坐)
[2025-06-30 20:58:50] ✅ 第 30 组: 张瑞 (1人连坐)
[2025-06-30 20:58:50] ✅ 第 31 组: 李慧, 白杨 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 32 组: 赵欣悦, 金子昂 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 33 组: 马梓涵, 宋晓娟 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 34 组: 杨文彦, 彭玉荣, 袁会池, 杨文芳, 张艳芳 (5人连坐)
[2025-06-30 20:58:50] ✅ 第 35 组: 张春红, 蒋梦娇 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 36 组: 肖垚, 韩文娟 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 37 组: 朱仁龙, 肖俊斌, 孙铭泽 (3人连坐)
[2025-06-30 20:58:50] ✅ 第 38 组: 黄鹤, 张刚, 张宸宁 (3人连坐)
[2025-06-30 20:58:50] ✅ 第 39 组: 粘旻环, 李怡台 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 40 组: 文志, 刘薇 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 41 组: 李薏 (单独)
[2025-06-30 20:58:50] ✅ 第 42 组: 于浩, 王雅暄 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 43 组: 高跃, 李敏 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 44 组: 张松 (1人连坐)
[2025-06-30 20:58:50] ✅ 第 45 组: 吴晓辉, 王子宁 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 46 组: 丁勇, 焦雯 (2人连坐)
[2025-06-30 20:58:50] ✅ 第 47 组: 游伊萌 (1人连坐)
[2025-06-30 20:58:50] ✅ 第 48 组: 田悦, 叶贵天 (2人连坐)
[2025-06-30 20:58:50]
=== 座位信息结构校验 ===
[2025-06-30 20:58:50] ✅ 所有必需列都存在
[2025-06-30 20:58:50] ✅ 区域 列数据完整
[2025-06-30 20:58:50] ✅ 楼层 列数据完整
[2025-06-30 20:58:50] ✅ 排号 列数据完整
[2025-06-30 20:58:50] ✅ 座位号 列数据完整
[2025-06-30 20:58:50]
座位结构分析:
[2025-06-30 20:58:50]
座位结构分析:
[2025-06-30 20:58:50] ✅ 区7895通道-五层-8排: 4 个座位完全连续 (1-4)
[2025-06-30 20:58:50] ✅ 区7895通道-五层-9排: 10 个座位完全连续 (1-10)
[2025-06-30 20:58:50] ✅ 区7895通道-五层-10排: 5 个座位完全连续 (1-5)
[2025-06-30 20:58:50] ✅ A区136通道-五层-10排: 5 个座位完全连续 (6-10)
[2025-06-30 20:58:50] 📊 A区136通道-五层-11排: 11 个座位2 个连续段: 1-2, 5-13
[2025-06-30 20:58:50] 最大连续段: 9 个座位
[2025-06-30 20:58:50] ✅ A区136通道-五层-12排: 1 个座位完全连续 (1-1)
[2025-06-30 20:58:50] ✅ A区136通道-三层-12排: 10 个座位完全连续 (2-11)
[2025-06-30 20:58:50] ✅ 区552通道-三层-12排: 2 个座位完全连续 (12-13)
[2025-06-30 20:58:50] ✅ 区552通道-三层-13排: 13 个座位完全连续 (1-13)
[2025-06-30 20:58:50] ✅ 区552通道-三层-14排: 9 个座位完全连续 (1-9)
[2025-06-30 20:58:50] ✅ 区552通道-五层-14排: 4 个座位完全连续 (10-13)
[2025-06-30 20:58:50] 📊 区552通道-五层-15排: 11 个座位2 个连续段: 1-7, 10-13
[2025-06-30 20:58:50] 最大连续段: 7 个座位
[2025-06-30 20:58:50] ✅ 区552通道-五层-16排: 13 个座位完全连续 (1-13)
[2025-06-30 20:58:50] ✅ 区552通道-五层-17排: 2 个座位完全连续 (1-2)
[2025-06-30 20:58:50]
=== 容量和可行性校验 ===
[2025-06-30 20:58:50] 总人数: 100
[2025-06-30 20:58:50] 总座位数: 100
[2025-06-30 20:58:50] ✅ 座位充足: 剩余 0 个座位
[2025-06-30 20:58:50]
连坐组需求分析:
[2025-06-30 20:58:50] 最大连坐组: 5 人
[2025-06-30 20:58:50]
连续座位可行性分析:
[2025-06-30 20:58:50] 区7895通道-五层-8排: 最大连续 4 个座位
[2025-06-30 20:58:50] 区7895通道-五层-9排: 最大连续 10 个座位
[2025-06-30 20:58:50] 区7895通道-五层-10排: 最大连续 5 个座位
[2025-06-30 20:58:50] A区136通道-五层-10排: 最大连续 5 个座位
[2025-06-30 20:58:50] A区136通道-五层-11排: 最大连续 9 个座位
[2025-06-30 20:58:50] A区136通道-五层-12排: 最大连续 1 个座位
[2025-06-30 20:58:50] A区136通道-三层-12排: 最大连续 10 个座位
[2025-06-30 20:58:50] 区552通道-三层-12排: 最大连续 2 个座位
[2025-06-30 20:58:50] 区552通道-三层-13排: 最大连续 13 个座位
[2025-06-30 20:58:50] 区552通道-三层-14排: 最大连续 9 个座位
[2025-06-30 20:58:50] 区552通道-五层-14排: 最大连续 4 个座位
[2025-06-30 20:58:50] 区552通道-五层-15排: 最大连续 7 个座位
[2025-06-30 20:58:50] 区552通道-五层-16排: 最大连续 13 个座位
[2025-06-30 20:58:50] 区552通道-五层-17排: 最大连续 2 个座位
[2025-06-30 20:58:50]
全场最大连续座位: 13 个
[2025-06-30 20:58:50] ✅ 可以容纳最大连坐组
[2025-06-30 20:58:50] 可容纳最大连坐组的排数: 9 个
[2025-06-30 20:58:50]
连坐组分布:
[2025-06-30 20:58:50] 单人组: 11 个
[2025-06-30 20:58:50] 2人连坐组: 27 个
[2025-06-30 20:58:50] 3人连坐组: 6 个
[2025-06-30 20:58:50] 4人连坐组: 3 个
[2025-06-30 20:58:50] 5人连坐组: 1 个
[2025-06-30 20:58:50]
============================================================
[2025-06-30 20:58:50] 校验结果总结
[2025-06-30 20:58:50] ============================================================
[2025-06-30 20:58:50] 人员信息结构: ✅ 通过
[2025-06-30 20:58:50] 连坐组完整性: ✅ 通过
[2025-06-30 20:58:50] 座位信息结构: ✅ 通过
[2025-06-30 20:58:50] 容量可行性: ✅ 通过
[2025-06-30 20:58:50]
总体校验结果: ✅ 全部通过
[2025-06-30 20:58:50]
🎉 文件校验通过,可以进行座位分配!
[2025-06-30 20:58:53]
============================================================
[2025-06-30 20:58:53] 开始座位分配
[2025-06-30 20:58:53] ============================================================
[2025-06-30 20:58:53]
=== 人员连坐需求分析 ===
[2025-06-30 20:58:53] 总共识别出 48 个座位组:
[2025-06-30 20:58:53] 单人组: 11 个
[2025-06-30 20:58:53] 2人连坐组: 27 个
[2025-06-30 20:58:53] 3人连坐组: 6 个
[2025-06-30 20:58:53] 4人连坐组: 3 个
[2025-06-30 20:58:53] 5人连坐组: 1 个
[2025-06-30 20:58:53]
=== 高级座位结构分析 ===
[2025-06-30 20:58:53] ✅ 区7895通道-五层-8排: 4 个座位完全连续
[2025-06-30 20:58:53] ✅ 区7895通道-五层-9排: 10 个座位完全连续
[2025-06-30 20:58:53] ✅ 区7895通道-五层-10排: 5 个座位完全连续
[2025-06-30 20:58:53] ✅ A区136通道-五层-10排: 5 个座位完全连续
[2025-06-30 20:58:53] 📊 A区136通道-五层-11排: 11 个座位2 段: 1-2, 5-13
[2025-06-30 20:58:53] 最大连续段: 9 个座位
[2025-06-30 20:58:53] ✅ A区136通道-五层-12排: 1 个座位完全连续
[2025-06-30 20:58:53] ✅ A区136通道-三层-12排: 10 个座位完全连续
[2025-06-30 20:58:53] ✅ 区552通道-三层-12排: 2 个座位完全连续
[2025-06-30 20:58:53] ✅ 区552通道-三层-13排: 13 个座位完全连续
[2025-06-30 20:58:53] ✅ 区552通道-三层-14排: 9 个座位完全连续
[2025-06-30 20:58:53] ✅ 区552通道-五层-14排: 4 个座位完全连续
[2025-06-30 20:58:53] 📊 区552通道-五层-15排: 11 个座位2 段: 1-7, 10-13
[2025-06-30 20:58:53] 最大连续段: 7 个座位
[2025-06-30 20:58:53] ✅ 区552通道-五层-16排: 13 个座位完全连续
[2025-06-30 20:58:53] ✅ 区552通道-五层-17排: 2 个座位完全连续
[2025-06-30 20:58:53]
=== 开始智能座位分配 ===
[2025-06-30 20:58:53] 需要分配 48 个组
[2025-06-30 20:58:53]
处理第 1 组: 杨文彦 (group, 5 人)
[2025-06-30 20:58:53] 分配到 区7895通道-五层-9排 (连续座位: 1-5)
[2025-06-30 20:58:53] 杨文彦 -> 1号
[2025-06-30 20:58:53] 彭玉荣 -> 2号
[2025-06-30 20:58:53] 袁会池 -> 3号
[2025-06-30 20:58:53] 杨文芳 -> 4号
[2025-06-30 20:58:53] 张艳芳 -> 5号
[2025-06-30 20:58:53]
处理第 2 组: 常洁 (group, 4 人)
[2025-06-30 20:58:53] 分配到 区7895通道-五层-8排 (连续座位: 1-4)
[2025-06-30 20:58:53] 常洁 -> 1号
[2025-06-30 20:58:53] 陈媛 -> 2号
[2025-06-30 20:58:53] 王晨婧 -> 3号
[2025-06-30 20:58:53] 胡艳彬 -> 4号
[2025-06-30 20:58:53]
处理第 3 组: 尹泽 (group, 4 人)
[2025-06-30 20:58:53] 分配到 区7895通道-五层-9排 (连续座位: 6-9)
[2025-06-30 20:58:53] 尹泽 -> 6号
[2025-06-30 20:58:53] 尹依伊 -> 7号
[2025-06-30 20:58:53] 夏明慧 -> 8号
[2025-06-30 20:58:53] 贾娜 -> 9号
[2025-06-30 20:58:53]
处理第 4 组: 赵占博 (group, 4 人)
[2025-06-30 20:58:53] 分配到 区7895通道-五层-10排 (连续座位: 1-4)
[2025-06-30 20:58:53] 赵占博 -> 1号
[2025-06-30 20:58:53] 孙羽佳 -> 2号
[2025-06-30 20:58:53] 季海洋 -> 3号
[2025-06-30 20:58:53] 刘柳 -> 4号
[2025-06-30 20:58:53]
处理第 5 组: 刘晓雨 (group, 3 人)
[2025-06-30 20:58:53] 分配到 A区136通道-五层-10排 (连续座位: 6-8)
[2025-06-30 20:58:53] 刘晓雨 -> 6号
[2025-06-30 20:58:53] 李宇峥 -> 7号
[2025-06-30 20:58:53] 李霞 -> 8号
[2025-06-30 20:58:53]
处理第 6 组: 王建华 (group, 3 人)
[2025-06-30 20:58:53] 分配到 A区136通道-五层-11排 (连续座位: 5-7)
[2025-06-30 20:58:53] 王建华 -> 5号
[2025-06-30 20:58:53] 赵雅楠 -> 6号
[2025-06-30 20:58:53] 王格 -> 7号
[2025-06-30 20:58:53]
处理第 7 组: 白建新 (group, 3 人)
[2025-06-30 20:58:53] 分配到 A区136通道-五层-11排 (连续座位: 8-10)
[2025-06-30 20:58:53] 白建新 -> 8号
[2025-06-30 20:58:53] 李海生 -> 9号
[2025-06-30 20:58:53] 李苓 -> 10号
[2025-06-30 20:58:53]
处理第 8 组: 马志鑫 (group, 3 人)
[2025-06-30 20:58:53] 分配到 A区136通道-五层-11排 (连续座位: 11-13)
[2025-06-30 20:58:53] 马志鑫 -> 11号
[2025-06-30 20:58:53] 肖金玲 -> 12号
[2025-06-30 20:58:53] 马学利 -> 13号
[2025-06-30 20:58:53]
处理第 9 组: 朱仁龙 (group, 3 人)
[2025-06-30 20:58:53] 分配到 A区136通道-三层-12排 (连续座位: 2-4)
[2025-06-30 20:58:53] 朱仁龙 -> 2号
[2025-06-30 20:58:53] 肖俊斌 -> 3号
[2025-06-30 20:58:53] 孙铭泽 -> 4号
[2025-06-30 20:58:53]
处理第 10 组: 黄鹤 (group, 3 人)
[2025-06-30 20:58:53] 分配到 A区136通道-三层-12排 (连续座位: 5-7)
[2025-06-30 20:58:53] 黄鹤 -> 5号
[2025-06-30 20:58:53] 张刚 -> 6号
[2025-06-30 20:58:53] 张宸宁 -> 7号
[2025-06-30 20:58:53]
处理第 11 组: 安春利 (group, 2 人)
[2025-06-30 20:58:53] 分配到 A区136通道-五层-10排 (连续座位: 9-10)
[2025-06-30 20:58:53] 安春利 -> 9号
[2025-06-30 20:58:53] 杨茗珺 -> 10号
[2025-06-30 20:58:53]
处理第 12 组: 侯俊帆 (group, 2 人)
[2025-06-30 20:58:53] 分配到 A区136通道-五层-11排 (连续座位: 1-2)
[2025-06-30 20:58:53] 侯俊帆 -> 1号
[2025-06-30 20:58:53] 杨璇羽 -> 2号
[2025-06-30 20:58:53]
处理第 13 组: 刘金龙 (group, 2 人)
[2025-06-30 20:58:53] 分配到 A区136通道-三层-12排 (连续座位: 8-9)
[2025-06-30 20:58:53] 刘金龙 -> 8号
[2025-06-30 20:58:53] 杨宇航 -> 9号
[2025-06-30 20:58:53]
处理第 14 组: 张城 (group, 2 人)
[2025-06-30 20:58:53] 分配到 A区136通道-三层-12排 (连续座位: 10-11)
[2025-06-30 20:58:53] 张城 -> 10号
[2025-06-30 20:58:53] 张倩 -> 11号
[2025-06-30 20:58:53]
处理第 15 组: 陈新闻 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-12排 (连续座位: 12-13)
[2025-06-30 20:58:53] 陈新闻 -> 12号
[2025-06-30 20:58:53] 刘佳欢 -> 13号
[2025-06-30 20:58:53]
处理第 16 组: 孙昊 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-13排 (连续座位: 1-2)
[2025-06-30 20:58:53] 孙昊 -> 1号
[2025-06-30 20:58:53] 晋秋跃 -> 2号
[2025-06-30 20:58:53]
处理第 17 组: 高尚谦 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-13排 (连续座位: 3-4)
[2025-06-30 20:58:53] 高尚谦 -> 3号
[2025-06-30 20:58:53] 胡梦幻 -> 4号
[2025-06-30 20:58:53]
处理第 18 组: 郝罡 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-13排 (连续座位: 5-6)
[2025-06-30 20:58:53] 郝罡 -> 5号
[2025-06-30 20:58:53] 林杰 -> 6号
[2025-06-30 20:58:53]
处理第 19 组: 王璐瑶 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-13排 (连续座位: 7-8)
[2025-06-30 20:58:53] 王璐瑶 -> 7号
[2025-06-30 20:58:53] 刘昌昊 -> 8号
[2025-06-30 20:58:53]
处理第 20 组: 陈宇 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-13排 (连续座位: 9-10)
[2025-06-30 20:58:53] 陈宇 -> 9号
[2025-06-30 20:58:53] 房晨 -> 10号
[2025-06-30 20:58:53]
处理第 21 组: 赵瑞 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-13排 (连续座位: 11-12)
[2025-06-30 20:58:53] 赵瑞 -> 11号
[2025-06-30 20:58:53] 郭闯远 -> 12号
[2025-06-30 20:58:53]
处理第 22 组: 尚校 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-14排 (连续座位: 1-2)
[2025-06-30 20:58:53] 尚校 -> 1号
[2025-06-30 20:58:53] 尚冕 -> 2号
[2025-06-30 20:58:53]
处理第 23 组: 徐金瑞 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-14排 (连续座位: 3-4)
[2025-06-30 20:58:53] 徐金瑞 -> 3号
[2025-06-30 20:58:53] 赵春颖 -> 4号
[2025-06-30 20:58:53]
处理第 24 组: 杨文 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-14排 (连续座位: 5-6)
[2025-06-30 20:58:53] 杨文 -> 5号
[2025-06-30 20:58:53] 马新雨 -> 6号
[2025-06-30 20:58:53]
处理第 25 组: 周勇 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-14排 (连续座位: 7-8)
[2025-06-30 20:58:53] 周勇 -> 7号
[2025-06-30 20:58:53] 常佳琦 -> 8号
[2025-06-30 20:58:53]
处理第 26 组: 李慧 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-14排 (连续座位: 10-11)
[2025-06-30 20:58:53] 李慧 -> 10号
[2025-06-30 20:58:53] 白杨 -> 11号
[2025-06-30 20:58:53]
处理第 27 组: 赵欣悦 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-14排 (连续座位: 12-13)
[2025-06-30 20:58:53] 赵欣悦 -> 12号
[2025-06-30 20:58:53] 金子昂 -> 13号
[2025-06-30 20:58:53]
处理第 28 组: 马梓涵 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-15排 (连续座位: 1-2)
[2025-06-30 20:58:53] 马梓涵 -> 1号
[2025-06-30 20:58:53] 宋晓娟 -> 2号
[2025-06-30 20:58:53]
处理第 29 组: 张春红 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-15排 (连续座位: 3-4)
[2025-06-30 20:58:53] 张春红 -> 3号
[2025-06-30 20:58:53] 蒋梦娇 -> 4号
[2025-06-30 20:58:53]
处理第 30 组: 肖垚 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-15排 (连续座位: 5-6)
[2025-06-30 20:58:53] 肖垚 -> 5号
[2025-06-30 20:58:53] 韩文娟 -> 6号
[2025-06-30 20:58:53]
处理第 31 组: 粘旻环 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-15排 (连续座位: 10-11)
[2025-06-30 20:58:53] 粘旻环 -> 10号
[2025-06-30 20:58:53] 李怡台 -> 11号
[2025-06-30 20:58:53]
处理第 32 组: 文志 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-15排 (连续座位: 12-13)
[2025-06-30 20:58:53] 文志 -> 12号
[2025-06-30 20:58:53] 刘薇 -> 13号
[2025-06-30 20:58:53]
处理第 33 组: 于浩 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-16排 (连续座位: 1-2)
[2025-06-30 20:58:53] 于浩 -> 1号
[2025-06-30 20:58:53] 王雅暄 -> 2号
[2025-06-30 20:58:53]
处理第 34 组: 高跃 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-16排 (连续座位: 3-4)
[2025-06-30 20:58:53] 高跃 -> 3号
[2025-06-30 20:58:53] 李敏 -> 4号
[2025-06-30 20:58:53]
处理第 35 组: 吴晓辉 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-16排 (连续座位: 5-6)
[2025-06-30 20:58:53] 吴晓辉 -> 5号
[2025-06-30 20:58:53] 王子宁 -> 6号
[2025-06-30 20:58:53]
处理第 36 组: 丁勇 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-16排 (连续座位: 7-8)
[2025-06-30 20:58:53] 丁勇 -> 7号
[2025-06-30 20:58:53] 焦雯 -> 8号
[2025-06-30 20:58:53]
处理第 37 组: 田悦 (group, 2 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-16排 (连续座位: 9-10)
[2025-06-30 20:58:53] 田悦 -> 9号
[2025-06-30 20:58:53] 叶贵天 -> 10号
[2025-06-30 20:58:53]
处理第 38 组: 王鹏舒 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区7895通道-五层-9排: 王鹏舒 -> 10号
[2025-06-30 20:58:53]
处理第 39 组: 吴旭 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区7895通道-五层-10排: 吴旭 -> 5号
[2025-06-30 20:58:53]
处理第 40 组: 刘捷 (single, 1 人)
[2025-06-30 20:58:53] 分配到 A区136通道-五层-12排: 刘捷 -> 1号
[2025-06-30 20:58:53]
处理第 41 组: 张艺馨 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-13排: 张艺馨 -> 13号
[2025-06-30 20:58:53]
处理第 42 组: 张然殊 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区552通道-三层-14排: 张然殊 -> 9号
[2025-06-30 20:58:53]
处理第 43 组: 王莲 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-15排: 王莲 -> 7号
[2025-06-30 20:58:53]
处理第 44 组: 解子辰 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-16排: 解子辰 -> 11号
[2025-06-30 20:58:53]
处理第 45 组: 张瑞 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-16排: 张瑞 -> 12号
[2025-06-30 20:58:53]
处理第 46 组: 李薏 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-16排: 李薏 -> 13号
[2025-06-30 20:58:53]
处理第 47 组: 张松 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-17排: 张松 -> 1号
[2025-06-30 20:58:53]
处理第 48 组: 游伊萌 (single, 1 人)
[2025-06-30 20:58:53] 分配到 区552通道-五层-17排: 游伊萌 -> 2号
[2025-06-30 20:58:53]
座位分配结果已保存到: 座位信息_最终分配.xlsx
[2025-06-30 20:58:53] 分配日志已保存到: 最终座位分配日志.xlsx
[2025-06-30 20:58:53]
=== 分配统计 ===
[2025-06-30 20:58:53] 总共分配了 100 个座位
[2025-06-30 20:58:53]
按组大小统计:
[2025-06-30 20:58:53] 单人组: 11 个组, 11 人
[2025-06-30 20:58:53] 2人连坐组: 27 个组, 54 人
[2025-06-30 20:58:53] 3人连坐组: 6 个组, 18 人
[2025-06-30 20:58:53] 4人连坐组: 3 个组, 12 人
[2025-06-30 20:58:53] 5人连坐组: 1 个组, 5 人
[2025-06-30 20:58:53]
=== 连续性验证 ===
[2025-06-30 20:58:53] ✅ 组 1 (杨文彦): 5人连坐座位连续 [1, 2, 3, 4, 5]
[2025-06-30 20:58:53] ✅ 组 2 (常洁): 4人连坐座位连续 [1, 2, 3, 4]
[2025-06-30 20:58:53] ✅ 组 3 (尹泽): 4人连坐座位连续 [6, 7, 8, 9]
[2025-06-30 20:58:53] ✅ 组 4 (赵占博): 4人连坐座位连续 [1, 2, 3, 4]
[2025-06-30 20:58:53] ✅ 组 5 (刘晓雨): 3人连坐座位连续 [6, 7, 8]
[2025-06-30 20:58:53] ✅ 组 6 (王建华): 3人连坐座位连续 [5, 6, 7]
[2025-06-30 20:58:53] ✅ 组 7 (白建新): 3人连坐座位连续 [8, 9, 10]
[2025-06-30 20:58:53] ✅ 组 8 (马志鑫): 3人连坐座位连续 [11, 12, 13]
[2025-06-30 20:58:53] ✅ 组 9 (朱仁龙): 3人连坐座位连续 [2, 3, 4]
[2025-06-30 20:58:53] ✅ 组 10 (黄鹤): 3人连坐座位连续 [5, 6, 7]
[2025-06-30 20:58:53] ✅ 组 11 (安春利): 2人连坐座位连续 [9, 10]
[2025-06-30 20:58:53] ✅ 组 12 (侯俊帆): 2人连坐座位连续 [1, 2]
[2025-06-30 20:58:53] ✅ 组 13 (刘金龙): 2人连坐座位连续 [8, 9]
[2025-06-30 20:58:53] ✅ 组 14 (张城): 2人连坐座位连续 [10, 11]
[2025-06-30 20:58:53] ✅ 组 15 (陈新闻): 2人连坐座位连续 [12, 13]
[2025-06-30 20:58:53] ✅ 组 16 (孙昊): 2人连坐座位连续 [1, 2]
[2025-06-30 20:58:53] ✅ 组 17 (高尚谦): 2人连坐座位连续 [3, 4]
[2025-06-30 20:58:53] ✅ 组 18 (郝罡): 2人连坐座位连续 [5, 6]
[2025-06-30 20:58:53] ✅ 组 19 (王璐瑶): 2人连坐座位连续 [7, 8]
[2025-06-30 20:58:53] ✅ 组 20 (陈宇): 2人连坐座位连续 [9, 10]
[2025-06-30 20:58:53] ✅ 组 21 (赵瑞): 2人连坐座位连续 [11, 12]
[2025-06-30 20:58:53] ✅ 组 22 (尚校): 2人连坐座位连续 [1, 2]
[2025-06-30 20:58:53] ✅ 组 23 (徐金瑞): 2人连坐座位连续 [3, 4]
[2025-06-30 20:58:53] ✅ 组 24 (杨文): 2人连坐座位连续 [5, 6]
[2025-06-30 20:58:53] ✅ 组 25 (周勇): 2人连坐座位连续 [7, 8]
[2025-06-30 20:58:53] ✅ 组 26 (李慧): 2人连坐座位连续 [10, 11]
[2025-06-30 20:58:53] ✅ 组 27 (赵欣悦): 2人连坐座位连续 [12, 13]
[2025-06-30 20:58:53] ✅ 组 28 (马梓涵): 2人连坐座位连续 [1, 2]
[2025-06-30 20:58:53] ✅ 组 29 (张春红): 2人连坐座位连续 [3, 4]
[2025-06-30 20:58:53] ✅ 组 30 (肖垚): 2人连坐座位连续 [5, 6]
[2025-06-30 20:58:53] ✅ 组 31 (粘旻环): 2人连坐座位连续 [10, 11]
[2025-06-30 20:58:53] ✅ 组 32 (文志): 2人连坐座位连续 [12, 13]
[2025-06-30 20:58:53] ✅ 组 33 (于浩): 2人连坐座位连续 [1, 2]
[2025-06-30 20:58:53] ✅ 组 34 (高跃): 2人连坐座位连续 [3, 4]
[2025-06-30 20:58:53] ✅ 组 35 (吴晓辉): 2人连坐座位连续 [5, 6]
[2025-06-30 20:58:53] ✅ 组 36 (丁勇): 2人连坐座位连续 [7, 8]
[2025-06-30 20:58:53] ✅ 组 37 (田悦): 2人连坐座位连续 [9, 10]
[2025-06-30 20:58:53]
连续性检查结果: 37/37 个多人组座位连续 (100.0%)
[2025-06-30 20:58:53]
🎉 座位分配完成!

View File

@@ -10,6 +10,8 @@ import sys
import os
from pathlib import Path
import datetime
import pandas as pd
import numpy as np
def check_dependencies():
"""检查并安装必要的依赖包"""
@@ -55,39 +57,145 @@ def check_dependencies():
return True
def check_data_files():
"""检查必要的数据文件"""
required_files = ['人员信息.xlsx', '座位信息.xlsx']
missing_files = []
def auto_detect_files():
"""自动检测和识别Excel文件"""
print("\n=== 自动文件识别系统 ===")
print("\n检查数据文件...")
for file_name in required_files:
if Path(file_name).exists():
print(f"{file_name} 存在")
# 扫描当前目录下的所有xlsx文件
current_dir = Path('.')
xlsx_files = list(current_dir.glob('*.xlsx'))
# 过滤掉输出文件和示例文件
exclude_patterns = ['座位信息_最终分配', '最终座位分配日志', '_示例', '_temp', '_backup']
xlsx_files = [f for f in xlsx_files if not any(pattern in f.name for pattern in exclude_patterns)]
print(f"发现 {len(xlsx_files)} 个Excel文件:")
for i, file_path in enumerate(xlsx_files, 1):
file_size = file_path.stat().st_size / 1024 # KB
print(f" {i}. {file_path.name} ({file_size:.1f} KB)")
if len(xlsx_files) == 0:
print("\n❌ 未找到Excel文件")
print("请确保当前目录下有Excel数据文件")
return None, None
if len(xlsx_files) > 2:
print(f"\n⚠️ 发现 {len(xlsx_files)} 个Excel文件超过2个")
print("为避免混淆请确保目录下只有2个数据文件")
print("(程序会自动忽略输出文件和示例文件)")
print("\n请手动移除多余文件,或选择要使用的文件:")
for i, file_path in enumerate(xlsx_files, 1):
print(f" {i}. {file_path.name}")
return None, None
# 分析文件结构
print(f"\n正在分析文件结构...")
file_info = []
for file_path in xlsx_files:
try:
# 读取文件获取基本信息
df = pd.read_excel(file_path, nrows=5) # 只读前5行进行分析
info = {
'path': file_path,
'name': file_path.name,
'columns': len(df.columns),
'rows': len(df) + 1, # 加上标题行
'column_names': list(df.columns),
'sample_data': df.head(2) # 获取前2行样本数据
}
file_info.append(info)
print(f"\n📄 {file_path.name}:")
print(f" 列数: {info['columns']}")
print(f" 行数: {info['rows']}+ (仅检查前5行)")
print(f" 列名: {', '.join(str(col) for col in info['column_names'][:6])}{'...' if len(info['column_names']) > 6 else ''}")
except Exception as e:
print(f"\n❌ 读取文件 {file_path.name} 失败: {e}")
return None, None
# 根据列数自动识别文件类型
personnel_file = None
seat_file = None
print(f"\n=== 文件类型识别 ===")
for info in file_info:
if info['columns'] == 5 or info['columns'] == 6: # 人员信息通常是5-6列
# 进一步检查列名是否符合人员信息格式
col_names = [str(col).lower() for col in info['column_names']]
personnel_keywords = ['姓名', 'name', '证件', 'id', '手机', 'phone', '备注', 'remark']
if any(keyword in ' '.join(col_names) for keyword in personnel_keywords):
personnel_file = info
print(f"{info['name']} -> 识别为人员信息文件 ({info['columns']}列)")
else:
print(f"⚠️ {info['name']}{info['columns']}列,但列名不符合人员信息格式")
elif info['columns'] >= 10: # 座位信息通常是10-12列
# 进一步检查列名是否符合座位信息格式
col_names = [str(col).lower() for col in info['column_names']]
seat_keywords = ['区域', 'area', '楼层', 'floor', '排号', 'row', '座位', 'seat']
if any(keyword in ' '.join(col_names) for keyword in seat_keywords):
seat_file = info
print(f"{info['name']} -> 识别为座位信息文件 ({info['columns']}列)")
else:
print(f"⚠️ {info['name']}{info['columns']}列,但列名不符合座位信息格式")
else:
missing_files.append(file_name)
print(f"{file_name} 不存在")
print(f"{info['name']}{info['columns']}列,无法自动识别类型")
if missing_files:
print(f"\n❌ 缺少必要文件: {', '.join(missing_files)}")
print("\n请确保以下文件存在于程序同一目录下:")
print("1. 人员信息.xlsx - 包含姓名、证件类型、证件号、手机号、备注等列")
print("2. 座位信息.xlsx - 包含区域、楼层、排号、座位号等列")
print("\n提示: 您可以参考示例文件来准备数据")
return False
# 验证识别结果
if personnel_file is None:
print(f"\n❌ 未找到人员信息文件")
print("人员信息文件应包含: 姓名、证件类型、证件号、手机号、备注等列")
return None, None
return True
if seat_file is None:
print(f"\n❌ 未找到座位信息文件")
print("座位信息文件应包含: 区域、楼层、排号、座位号等列")
return None, None
print(f"\n🎉 文件识别成功!")
print(f"人员信息: {personnel_file['name']}")
print(f"座位信息: {seat_file['name']}")
# 显示文件内容预览
print(f"\n=== 文件内容预览 ===")
print(f"\n📋 人员信息文件预览 ({personnel_file['name']}):")
print(personnel_file['sample_data'].to_string(index=False, max_cols=10))
print(f"\n🪑 座位信息文件预览 ({seat_file['name']}):")
print(seat_file['sample_data'].to_string(index=False, max_cols=10))
return personnel_file['path'], seat_file['path']
# 只有在依赖检查通过后才导入这些包
if check_dependencies():
try:
import pandas as pd
import numpy as np
except ImportError as e:
print(f"❌ 导入依赖包失败: {e}")
input("按Enter键退出...")
sys.exit(1)
else:
def check_data_files():
"""检查并自动识别数据文件"""
print("\n=== 数据文件检查 ===")
# 首先尝试自动识别
personnel_file, seat_file = auto_detect_files()
if personnel_file is None or seat_file is None:
print(f"\n❌ 自动识别失败")
print("\n请检查以下要求:")
print("1. 确保目录下有且仅有2个Excel文件")
print("2. 人员信息文件应有5-6列姓名、证件类型、证件号、手机号、备注等")
print("3. 座位信息文件应有10+列(区域、楼层、排号、座位号等)")
print("4. 列名应包含相关关键词")
print("\n提示: 您可以参考示例文件来准备数据")
return False, None, None
return True, str(personnel_file), str(seat_file)
# 检查依赖
if not check_dependencies():
print("❌ 依赖检查失败")
input("按Enter键退出...")
sys.exit(1)
@@ -95,7 +203,12 @@ else:
class Logger:
"""日志记录器"""
def __init__(self, log_file='seat_allocation_log.txt'):
self.log_file = log_file
# 创建log文件夹
self.log_dir = Path('log')
self.log_dir.mkdir(exist_ok=True)
# 设置日志文件路径
self.log_file = self.log_dir / log_file
self.logs = []
def log(self, message, print_to_console=True):
@@ -117,6 +230,10 @@ class Logger:
except Exception as e:
print(f"保存日志失败: {e}")
return False
def get_log_path(self, filename):
"""获取log文件夹中的文件路径"""
return self.log_dir / filename
class SeatAllocationSystem:
"""座位分配系统"""
@@ -127,34 +244,245 @@ class SeatAllocationSystem:
self.seat_df = None
self.seating_groups = []
self.row_analysis = {}
self.personnel_file = None
self.seat_file = None
def choose_file_mode(self):
"""选择文件识别模式"""
self.logger.log("\n=== 选择文件识别模式 ===")
self.logger.log("1. 默认文件名 - 读取 '人员信息.xlsx''座位信息.xlsx'")
self.logger.log("2. 智能识别 - 自动识别目录中的Excel文件")
while True:
try:
choice = input("\n请选择文件识别模式 (1/2直接回车选择默认): ").strip()
if choice == '' or choice == '1':
self.logger.log("✅ 选择默认文件名模式")
return 'default'
elif choice == '2':
self.logger.log("✅ 选择智能识别模式")
return 'smart'
else:
print("请输入 1 或 2")
except KeyboardInterrupt:
self.logger.log("\n用户取消操作")
return 'default'
def smart_identify_files(self):
"""智能识别Excel文件"""
self.logger.log("\n=== 智能识别Excel文件 ===")
# 获取当前目录下的所有Excel文件
excel_files = []
for ext in ['*.xlsx', '*.xls']:
excel_files.extend(Path('.').glob(ext))
if len(excel_files) < 2:
self.logger.log(f"❌ 当前目录只找到 {len(excel_files)} 个Excel文件需要至少2个")
return None, None
self.logger.log(f"📁 找到 {len(excel_files)} 个Excel文件:")
# 分析每个文件的内容
file_analysis = []
for file_path in excel_files:
try:
# 先读取完整文件获取真实行数
df_full = pd.read_excel(file_path)
total_rows = len(df_full)
# 再读取前5行进行列分析
df_sample = pd.read_excel(file_path, nrows=5)
analysis = {
'path': file_path,
'name': file_path.name,
'rows': total_rows, # 使用真实行数
'cols': len(df_sample.columns),
'columns': list(df_sample.columns),
'score': 0,
'type': 'unknown'
}
# 判断文件类型 - 改进的识别算法
columns_str = ' '.join(str(col).lower() for col in df_sample.columns)
columns_list = [str(col).lower() for col in df_sample.columns]
# 座位信息文件的强特征(优先判断)
seat_strong_keywords = ['座位id', '区域', '楼层', '排号', '座位号']
seat_strong_score = sum(1 for keyword in seat_strong_keywords if keyword in columns_str)
# 人员信息文件的强特征
personnel_strong_keywords = ['备注'] # 备注列是人员信息文件的强特征
personnel_strong_score = sum(1 for keyword in personnel_strong_keywords if keyword in columns_str)
# 如果有座位强特征,优先识别为座位文件
if seat_strong_score >= 3: # 至少包含3个座位强特征
analysis['type'] = 'seat'
analysis['score'] = seat_strong_score * 10 # 给予高权重
# 如果有人员强特征且列数较少,识别为人员文件
elif personnel_strong_score > 0 and len(df_sample.columns) <= 8:
analysis['type'] = 'personnel'
analysis['score'] = personnel_strong_score * 10
else:
# 使用原有的弱特征判断
personnel_keywords = ['姓名', '证件', '手机', 'name', 'id', 'phone']
personnel_score = sum(1 for keyword in personnel_keywords if keyword in columns_str)
seat_keywords = ['区域', '楼层', '排号', '座位', 'area', 'floor', 'row', 'seat']
seat_score = sum(1 for keyword in seat_keywords if keyword in columns_str)
# 结合列数进行判断
if len(df_sample.columns) <= 8 and personnel_score > 0:
analysis['type'] = 'personnel'
analysis['score'] = personnel_score
elif len(df_sample.columns) > 8 and seat_score > 0:
analysis['type'] = 'seat'
analysis['score'] = seat_score
elif personnel_score > seat_score:
analysis['type'] = 'personnel'
analysis['score'] = personnel_score
elif seat_score > personnel_score:
analysis['type'] = 'seat'
analysis['score'] = seat_score
file_analysis.append(analysis)
self.logger.log(f" 📄 {file_path.name}: {total_rows}× {len(df_sample.columns)}")
self.logger.log(f" 列名: {', '.join(str(col) for col in df_sample.columns[:5])}{'...' if len(df_sample.columns) > 5 else ''}")
self.logger.log(f" 推测类型: {analysis['type']} (得分: {analysis['score']})")
except Exception as e:
self.logger.log(f"{file_path.name}: 读取失败 - {e}")
# 自动匹配最佳文件
personnel_files = [f for f in file_analysis if f['type'] == 'personnel']
seat_files = [f for f in file_analysis if f['type'] == 'seat']
personnel_file = None
seat_file = None
if personnel_files:
personnel_file = max(personnel_files, key=lambda x: x['score'])['path']
self.logger.log(f"\n🎯 自动识别人员信息文件: {personnel_file.name}")
if seat_files:
seat_file = max(seat_files, key=lambda x: x['score'])['path']
self.logger.log(f"🎯 自动识别座位信息文件: {seat_file.name}")
# 如果自动识别不完整,提供手动选择
if not personnel_file or not seat_file:
self.logger.log("\n⚠️ 自动识别不完整,请手动选择:")
personnel_file, seat_file = self.manual_select_files(file_analysis)
return personnel_file, seat_file
def manual_select_files(self, file_analysis):
"""手动选择文件"""
self.logger.log("\n=== 手动选择文件 ===")
print("\n可用的Excel文件:")
for i, analysis in enumerate(file_analysis):
print(f"{i+1}. {analysis['name']} ({analysis['rows']}× {analysis['cols']}列)")
print(f" 列名: {', '.join(str(col) for col in analysis['columns'][:3])}...")
personnel_file = None
seat_file = None
# 选择人员信息文件
while not personnel_file:
try:
choice = input(f"\n请选择人员信息文件 (1-{len(file_analysis)}): ").strip()
idx = int(choice) - 1
if 0 <= idx < len(file_analysis):
personnel_file = file_analysis[idx]['path']
self.logger.log(f"✅ 选择人员信息文件: {personnel_file.name}")
else:
print("请输入有效的数字")
except (ValueError, KeyboardInterrupt):
print("请输入有效的数字")
# 选择座位信息文件
while not seat_file:
try:
choice = input(f"\n请选择座位信息文件 (1-{len(file_analysis)}): ").strip()
idx = int(choice) - 1
if 0 <= idx < len(file_analysis):
if file_analysis[idx]['path'] == personnel_file:
print("不能选择相同的文件,请重新选择")
continue
seat_file = file_analysis[idx]['path']
self.logger.log(f"✅ 选择座位信息文件: {seat_file.name}")
else:
print("请输入有效的数字")
except (ValueError, KeyboardInterrupt):
print("请输入有效的数字")
return personnel_file, seat_file
def set_data_files(self, personnel_file, seat_file):
"""设置数据文件路径"""
self.personnel_file = personnel_file
self.seat_file = seat_file
def identify_and_set_files(self):
"""识别并设置数据文件"""
file_mode = self.choose_file_mode()
if file_mode == 'default':
# 默认文件名模式
personnel_file = Path('人员信息.xlsx')
seat_file = Path('座位信息.xlsx')
if not personnel_file.exists():
self.logger.log(f"❌ 错误: {personnel_file} 文件不存在")
return False
if not seat_file.exists():
self.logger.log(f"❌ 错误: {seat_file} 文件不存在")
return False
else:
# 智能识别模式
personnel_file, seat_file = self.smart_identify_files()
if not personnel_file or not seat_file:
self.logger.log("❌ 文件识别失败")
return False
self.set_data_files(personnel_file, seat_file)
return True
def load_data(self):
"""加载人员信息和座位信息数据"""
self.logger.log("=== 开始加载数据 ===")
# 检查文件是否存在
personnel_file = '人员信息.xlsx'
seat_file = '座位信息.xlsx'
if not Path(personnel_file).exists():
self.logger.log(f"❌ 错误: {personnel_file} 文件不存在")
# 使用自动识别的文件路径
if not self.personnel_file or not self.seat_file:
self.logger.log("❌ 错误: 数据文件路径未设置")
return False
if not Path(seat_file).exists():
self.logger.log(f"❌ 错误: {seat_file} 文件不存在")
if not Path(self.personnel_file).exists():
self.logger.log(f"❌ 错误: {self.personnel_file} 文件不存在")
return False
if not Path(self.seat_file).exists():
self.logger.log(f"❌ 错误: {self.seat_file} 文件不存在")
return False
try:
self.logger.log(f"正在读取人员信息文件: {self.personnel_file}")
# 读取人员信息,指定数据类型
self.personnel_df = pd.read_excel(personnel_file, dtype={
self.personnel_df = pd.read_excel(self.personnel_file, dtype={
'姓名': 'str',
'证件类型': 'str',
'证件号': 'str', # 证件号作为字符串读取避免X被转换
'手机号': 'str'
})
self.logger.log(f"正在读取座位信息文件: {self.seat_file}")
# 读取座位信息,指定数据类型
self.seat_df = pd.read_excel(seat_file, dtype={
seat_df_raw = pd.read_excel(self.seat_file, dtype={
'区域': 'str',
'楼层': 'str',
'排号': 'str',
@@ -165,6 +493,9 @@ class SeatAllocationSystem:
'手机号': 'str',
'签发地/国籍': 'str'
})
# 过滤和清洗座位数据
self.seat_df = self.filter_seat_data(seat_df_raw)
# 清理文字信息中的空格
self.clean_text_data()
@@ -197,26 +528,72 @@ class SeatAllocationSystem:
self.logger.log("✅ 文字数据清理完成")
def filter_seat_data(self, seat_df_raw):
"""过滤和清洗座位数据,移除空行和无效数据"""
self.logger.log("开始过滤座位数据...")
original_count = len(seat_df_raw)
self.logger.log(f"原始数据行数: {original_count}")
# 关键列,这些列都不能为空
key_columns = ['区域', '楼层', '排号', '座位号']
# 创建过滤条件:关键列都不能为空且不能为'nan'字符串
filter_condition = True
for col in key_columns:
if col in seat_df_raw.columns:
# 过滤空值、NaN、'nan'字符串
col_condition = (
seat_df_raw[col].notna() &
(seat_df_raw[col].astype(str).str.strip() != '') &
(seat_df_raw[col].astype(str).str.lower() != 'nan')
)
filter_condition = filter_condition & col_condition
# 应用过滤条件
seat_df_filtered = seat_df_raw[filter_condition].copy()
# 重置索引
seat_df_filtered.reset_index(drop=True, inplace=True)
filtered_count = len(seat_df_filtered)
removed_count = original_count - filtered_count
self.logger.log(f"过滤后数据行数: {filtered_count}")
self.logger.log(f"移除无效行数: {removed_count}")
if removed_count > 0:
self.logger.log(f"✅ 已过滤掉 {removed_count} 行无效数据(空行、示例数据等)")
# 显示过滤后的数据概览
if filtered_count > 0:
areas = seat_df_filtered['区域'].unique()
floors = seat_df_filtered['楼层'].unique()
self.logger.log(f"有效座位区域: {', '.join(areas)}")
self.logger.log(f"有效座位楼层: {', '.join(floors)}")
return seat_df_filtered
def validate_personnel_structure(self):
"""校验人员信息文件结构"""
self.logger.log("\n=== 人员信息结构校验 ===")
required_columns = ['姓名', '证件类型', '证件号', '手机号', '备注']
validation_results = []
# 检查必需列
missing_columns = []
for col in required_columns:
if col not in self.personnel_df.columns:
missing_columns.append(col)
if missing_columns:
self.logger.log(f"❌ 缺少必需列: {missing_columns}")
validation_results.append(False)
else:
self.logger.log("✅ 所有必需列都存在")
validation_results.append(True)
# 检查数据完整性
self.logger.log("\n数据完整性检查:")
for col in ['姓名', '证件类型', '证件号', '手机号']:
@@ -228,16 +605,36 @@ class SeatAllocationSystem:
else:
self.logger.log(f"{col} 列数据完整")
validation_results.append(True)
# 检查重复姓名
duplicate_names = self.personnel_df[self.personnel_df['姓名'].duplicated()]
if not duplicate_names.empty:
self.logger.log(f"⚠️ 发现重复姓名: {duplicate_names['姓名'].tolist()}")
validation_results.append(False)
# 检查重复人员(姓名+身份证号组合)
self.logger.log("\n重复人员检查:")
if '姓名' in self.personnel_df.columns and '证件号' in self.personnel_df.columns:
# 创建姓名+证件号的组合标识
self.personnel_df['人员标识'] = self.personnel_df['姓名'].astype(str) + '_' + self.personnel_df['证件号'].astype(str)
duplicate_persons = self.personnel_df[self.personnel_df['人员标识'].duplicated()]
if not duplicate_persons.empty:
self.logger.log(f"⚠️ 发现重复人员:")
for _, person in duplicate_persons.iterrows():
self.logger.log(f" {person['姓名']} (证件号: {person['证件号']})")
validation_results.append(False)
else:
self.logger.log("✅ 无重复人员(姓名+证件号组合检查)")
validation_results.append(True)
# 单独检查重名情况(仅提示,不影响校验结果)
duplicate_names = self.personnel_df[self.personnel_df['姓名'].duplicated(keep=False)]
if not duplicate_names.empty:
self.logger.log(f"📝 发现重名人员(但证件号不同,允许通过):")
name_groups = duplicate_names.groupby('姓名')
for name, group in name_groups:
self.logger.log(f" 姓名: {name}")
for _, person in group.iterrows():
self.logger.log(f" 证件号: {person['证件号']}")
else:
self.logger.log("✅ 姓名无重复")
validation_results.append(True)
self.logger.log("❌ 缺少姓名或证件号列,无法进行重复检查")
validation_results.append(False)
return all(validation_results)
def validate_seating_groups(self):
@@ -511,10 +908,73 @@ class SeatAllocationSystem:
return True
def analyze_seating_requirements(self):
def analyze_seating_requirements(self, use_phone_grouping=False):
"""分析人员连坐需求"""
self.logger.log("\n=== 人员连坐需求分析 ===")
if use_phone_grouping:
self.logger.log("🔍 使用手机号分组模式")
return self.analyze_seating_by_phone()
else:
self.logger.log("🔍 使用备注分组模式")
return self.analyze_seating_by_remark()
def analyze_seating_by_phone(self):
"""基于手机号分析连坐需求"""
self.logger.log("根据相同手机号识别连坐组...")
# 按手机号分组
phone_groups = self.personnel_df.groupby('手机号')
self.seating_groups = []
for phone, group in phone_groups:
group_members = [row for _, row in group.iterrows()]
group_size = len(group_members)
if group_size == 1:
# 单人组
self.seating_groups.append({
'type': 'single',
'size': 1,
'members': group_members,
'leader': group_members[0]['姓名'],
'grouping_method': 'phone'
})
self.logger.log(f"📱 单人: {group_members[0]['姓名']} (手机号: {phone})")
else:
# 连坐组
leader_name = group_members[0]['姓名']
member_names = [member['姓名'] for member in group_members]
self.seating_groups.append({
'type': 'group',
'size': group_size,
'members': group_members,
'leader': leader_name,
'grouping_method': 'phone'
})
self.logger.log(f"📱 连坐组: {', '.join(member_names)} (手机号: {phone}, {group_size}人)")
# 统计
size_stats = {}
for group in self.seating_groups:
size = group['size']
size_stats[size] = size_stats.get(size, 0) + 1
self.logger.log(f"\n基于手机号识别出 {len(self.seating_groups)} 个座位组:")
for size in sorted(size_stats.keys()):
count = size_stats[size]
if size == 1:
self.logger.log(f" 单人组: {count}")
else:
self.logger.log(f" {size}人连坐组: {count}")
return True
def analyze_seating_by_remark(self):
"""基于备注分析连坐需求"""
self.logger.log("根据备注数字识别连坐组...")
self.seating_groups = []
i = 0
@@ -528,7 +988,8 @@ class SeatAllocationSystem:
'type': 'single',
'size': 1,
'members': [person],
'leader': person['姓名']
'leader': person['姓名'],
'grouping_method': 'remark'
})
i += 1
else:
@@ -545,7 +1006,8 @@ class SeatAllocationSystem:
'type': 'group' if group_size > 1 else 'single',
'size': len(group_members),
'members': group_members,
'leader': person['姓名']
'leader': person['姓名'],
'grouping_method': 'remark'
})
i += group_size
@@ -556,7 +1018,7 @@ class SeatAllocationSystem:
size = group['size']
size_stats[size] = size_stats.get(size, 0) + 1
self.logger.log(f"总共识别出 {len(self.seating_groups)} 个座位组:")
self.logger.log(f"\n基于备注识别出 {len(self.seating_groups)} 个座位组:")
for size in sorted(size_stats.keys()):
count = size_stats[size]
if size == 1:
@@ -679,13 +1141,14 @@ class SeatAllocationSystem:
person_info = seating_group['members'][0]
seat_index = seat_info['index']
# 更新座位信息,确保数据类型正确
# 覆盖座位表中的人员信息列
seat_df_copy.loc[seat_index, '姓名'] = str(person_info['姓名']).strip()
seat_df_copy.loc[seat_index, '证件类型'] = str(person_info['证件类型']).strip()
seat_df_copy.loc[seat_index, '证件号'] = str(person_info['证件号']).strip()
seat_df_copy.loc[seat_index, '手机国家号'] = person_info.get('Unnamed: 3', 86)
seat_df_copy.loc[seat_index, '手机号'] = str(person_info['手机号']).strip()
seat_df_copy.loc[seat_index, '签发地/国籍'] = str(person_info.get('备注', '')).strip()
# 只有分配备注放在第13列
seat_df_copy.loc[seat_index, '分配备注'] = str(person_info.get('备注', '')).strip()
assignment_log.append({
'组号': group_idx + 1,
@@ -763,7 +1226,8 @@ class SeatAllocationSystem:
seat_df_copy.loc[seat_index, '证件号'] = str(person_info['证件号']).strip()
seat_df_copy.loc[seat_index, '手机国家号'] = person_info.get('Unnamed: 3', 86)
seat_df_copy.loc[seat_index, '手机号'] = str(person_info['手机号']).strip()
seat_df_copy.loc[seat_index, '签发地/国籍'] = str(person_info.get('备注', '')).strip()
# 只有分配备注放在第13列
seat_df_copy.loc[seat_index, '分配备注'] = str(person_info.get('备注', '')).strip()
assignment_log.append({
'组号': group_idx + 1,
@@ -805,14 +1269,23 @@ class SeatAllocationSystem:
"""保存分配结果"""
try:
# 保存更新后的座位信息
output_file = '座位信息_最终分配.xlsx'
output_file = self.logger.get_log_path('座位信息_最终分配.xlsx')
seat_df_result.to_excel(output_file, index=False)
self.logger.log(f"\n座位分配结果已保存到: {output_file}")
self.logger.log(f"📋 人员信息已覆盖到座位表对应列:")
self.logger.log(f" 第6列: 姓名")
self.logger.log(f" 第7列: 证件类型")
self.logger.log(f" 第8列: 证件号")
self.logger.log(f" 第9列: 手机国家号")
self.logger.log(f" 第10列: 手机号")
self.logger.log(f"📝 分配备注信息:")
self.logger.log(f" 第13列: 分配备注(新增列,不覆盖原数据)")
self.logger.log(f"💡 座位基本信息第1-5列保持不变")
# 保存分配日志
if assignment_log:
log_df = pd.DataFrame(assignment_log)
log_file = '最终座位分配日志.xlsx'
log_file = self.logger.get_log_path('最终座位分配日志.xlsx')
log_df.to_excel(log_file, index=False)
self.logger.log(f"分配日志已保存到: {log_file}")
@@ -885,6 +1358,10 @@ class SeatAllocationSystem:
self.logger.log("座位分配系统 - 文件校验")
self.logger.log("=" * 60)
# 识别并设置数据文件
if not self.identify_and_set_files():
return False
# 加载数据
if not self.load_data():
return False
@@ -938,8 +1415,11 @@ class SeatAllocationSystem:
self.logger.log("开始座位分配")
self.logger.log("=" * 60)
# 选择分组方式
grouping_method = self.choose_grouping_method()
# 分析人员连坐需求
if not self.analyze_seating_requirements():
if not self.analyze_seating_requirements(use_phone_grouping=(grouping_method == 'phone')):
return False
# 高级座位结构分析
@@ -957,20 +1437,47 @@ class SeatAllocationSystem:
self.logger.log("\n❌ 座位分配失败!")
return False
def choose_grouping_method(self):
"""选择分组方式"""
self.logger.log("\n=== 选择连坐分组方式 ===")
self.logger.log("1. 备注分组 - 根据备注数字识别连坐组(默认)")
self.logger.log("2. 手机号分组 - 根据相同手机号识别连坐组")
while True:
try:
choice = input("\n请选择分组方式 (1/2直接回车选择默认): ").strip()
if choice == '' or choice == '1':
self.logger.log("✅ 选择备注分组模式")
return 'remark'
elif choice == '2':
self.logger.log("✅ 选择手机号分组模式")
return 'phone'
else:
print("请输入 1 或 2")
except KeyboardInterrupt:
self.logger.log("\n用户取消操作")
return 'remark'
def main():
"""主函数"""
print("=" * 60)
print("座位分配系统 v1.0")
print("座位分配系统 v2.0 (智能识别版)")
print("=" * 60)
try:
# 检查数据文件
if not check_data_files():
# 自动检查和识别数据文件
success, personnel_file, seat_file = check_data_files()
if not success:
input("\n按Enter键退出...")
return
print("\n开始运行座位分配系统...")
system = SeatAllocationSystem()
# 设置识别出的文件路径
system.set_data_files(personnel_file, seat_file)
# 运行校验
if system.run_validation():
@@ -980,10 +1487,10 @@ def main():
# 运行分配
if system.run_allocation():
print("\n🎉 座位分配完成!")
print("请查看以下输出文件:")
print("- 座位信息_最终分配.xlsx (分配结果)")
print("- 最终座位分配日志.xlsx (详细日志)")
print("- seat_allocation_log.txt (运行日志)")
print("请查看log文件夹中的输出文件:")
print("- log/座位信息_最终分配.xlsx (分配结果)")
print("- log/最终座位分配日志.xlsx (详细日志)")
print("- log/seat_allocation_log.txt (运行日志)")
else:
print("\n❌ 座位分配失败!")
else:

View File

@@ -1,232 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简化构建脚本
在当前平台构建可执行文件
"""
import os
import sys
import subprocess
import platform
import shutil
from pathlib import Path
def get_platform_info():
"""获取平台信息"""
system = platform.system()
machine = platform.machine().lower()
if system == 'Windows':
if machine in ['amd64', 'x86_64']:
return 'windows_x64'
elif machine in ['i386', 'i686', 'x86']:
return 'windows_x86'
else:
return f'windows_{machine}'
elif system == 'Darwin':
if machine in ['arm64', 'aarch64']:
return 'macos_arm64'
else:
return 'macos_x64'
elif system == 'Linux':
return f'linux_{machine}'
else:
return f'{system.lower()}_{machine}'
def install_dependencies():
"""安装依赖"""
print("安装依赖包...")
packages = ['pandas', 'openpyxl', 'pyinstaller']
for package in packages:
try:
__import__(package)
print(f"{package} 已安装")
except ImportError:
print(f"📦 安装 {package}...")
try:
subprocess.check_call([sys.executable, '-m', 'pip', 'install', package])
print(f"{package} 安装成功")
except subprocess.CalledProcessError:
print(f"{package} 安装失败")
return False
return True
def build_executable():
"""构建可执行文件"""
print("\n开始构建可执行文件...")
# 获取平台信息
platform_name = get_platform_info()
exe_name = f"座位分配系统_{platform_name}"
print(f"目标平台: {platform_name}")
print(f"输出文件: {exe_name}")
# 清理之前的构建
if os.path.exists('dist'):
shutil.rmtree('dist')
if os.path.exists('build'):
shutil.rmtree('build')
# 构建命令
cmd = [
sys.executable, '-m', 'PyInstaller',
'--onefile',
'--console',
'--clean',
'--name', exe_name,
'seat_allocation_system.py'
]
try:
print(f"执行命令: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print("✅ 构建成功!")
# 检查生成的文件
if platform.system() == 'Windows':
exe_path = Path('dist') / f'{exe_name}.exe'
else:
exe_path = Path('dist') / exe_name
if exe_path.exists():
file_size = exe_path.stat().st_size / (1024 * 1024) # MB
print(f"生成文件: {exe_path}")
print(f"文件大小: {file_size:.1f} MB")
return True, exe_path
else:
print("❌ 未找到生成的文件")
return False, None
else:
print("❌ 构建失败!")
print("错误输出:")
print(result.stderr)
return False, None
except Exception as e:
print(f"❌ 构建过程中出现错误: {e}")
return False, None
def create_distribution():
"""创建分发包"""
print("\n创建分发包...")
platform_name = get_platform_info()
package_name = f"座位分配系统_{platform_name}_分发包"
package_dir = Path(package_name)
# 创建分发目录
if package_dir.exists():
shutil.rmtree(package_dir)
package_dir.mkdir()
# 复制可执行文件
dist_dir = Path('dist')
if dist_dir.exists():
for file in dist_dir.iterdir():
if file.is_file():
shutil.copy2(file, package_dir)
print(f"复制文件: {file.name}")
# 复制示例文件
if Path('人员信息.xlsx').exists():
shutil.copy2('人员信息.xlsx', package_dir / '人员信息_示例.xlsx')
print("复制示例文件: 人员信息_示例.xlsx")
if Path('座位信息.xlsx').exists():
shutil.copy2('座位信息.xlsx', package_dir / '座位信息_示例.xlsx')
print("复制示例文件: 座位信息_示例.xlsx")
# 创建使用说明
readme_content = f"""座位分配系统 使用说明
平台: {platform_name}
构建时间: {platform.platform()}
使用方法:
1. 准备Excel文件
- 人员信息.xlsx: 包含姓名、证件信息、备注等
- 座位信息.xlsx: 包含区域、楼层、排号、座位号等
2. 运行程序
- 将可执行文件放在Excel文件同一目录
- 双击运行可执行文件
- 等待处理完成
3. 查看结果
- 座位信息_最终分配.xlsx: 分配结果
- 最终座位分配日志.xlsx: 详细记录
- seat_allocation_log.txt: 运行日志
功能特点:
- 支持1-10人连坐需求
- 自动处理不连续座位
- 完整的数据校验
- 详细的分配日志
注意事项:
- 确保Excel文件格式正确
- 备注数字表示连坐人数
- 运行时会在同目录生成结果文件
"""
with open(package_dir / '使用说明.txt', 'w', encoding='utf-8') as f:
f.write(readme_content)
print(f"✅ 分发包已创建: {package_dir}")
return package_dir
def main():
"""主函数"""
print("座位分配系统 - 简化构建工具")
print("=" * 50)
# 显示系统信息
print(f"系统: {platform.system()} {platform.release()}")
print(f"架构: {platform.machine()}")
print(f"Python: {sys.version}")
# 检查主程序文件
if not os.path.exists('seat_allocation_system.py'):
print("❌ 未找到 seat_allocation_system.py 文件")
return
# 安装依赖
if not install_dependencies():
print("❌ 依赖安装失败")
return
# 构建可执行文件
success, exe_path = build_executable()
if not success:
print("❌ 构建失败")
return
# 创建分发包
package_dir = create_distribution()
print("\n🎉 构建完成!")
print(f"✅ 可执行文件: {exe_path}")
print(f"✅ 分发包: {package_dir}")
# 平台特定说明
if platform.system() == 'Windows':
print("\n📝 Windows使用说明:")
print("- 可以直接在Windows系统上运行")
print("- 确保安装了Visual C++ Redistributable")
elif platform.system() == 'Darwin':
print("\n📝 macOS使用说明:")
print("- 只能在macOS系统上运行")
print("- 如需Windows版本请在Windows系统上构建")
print("- 或使用GitHub Actions自动构建")
else:
print(f"\n📝 {platform.system()}使用说明:")
print("- 只能在相同系统上运行")
print("- 如需其他平台版本,请在对应系统上构建")
if __name__ == "__main__":
main()

196
simple_windows_build.py Normal file
View File

@@ -0,0 +1,196 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简化的Windows构建脚本
专门解决PyInstaller依赖问题
"""
import sys
import subprocess
import platform
from pathlib import Path
def create_simple_spec():
"""创建简化的spec文件"""
spec_content = '''# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['seat_allocation_system.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[
# 核心依赖
'pandas',
'openpyxl',
'numpy',
'datetime',
'pathlib',
'sys',
'os',
# 修复关键错误
'numpy.core._methods',
'pandas._libs.window.aggregations',
'ctypes.util',
'pkg_resources.py2_warn',
# pandas核心
'pandas._libs',
'pandas._libs.tslibs',
'pandas._libs.tslibs.base',
'pandas.io.excel',
'pandas.io.common',
# numpy核心
'numpy.core',
'numpy.core.multiarray',
'numpy.core.umath',
# openpyxl
'openpyxl.workbook',
'openpyxl.worksheet',
'openpyxl.styles',
# 编码
'encodings.utf_8',
'encodings.gbk',
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[
'matplotlib',
'scipy',
'IPython',
'jupyter',
'tkinter',
'PyQt5',
'PyQt6',
'PIL',
'cv2',
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='座位分配系统',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
'''
spec_file = Path('simple_build.spec')
with open(spec_file, 'w', encoding='utf-8') as f:
f.write(spec_content)
print(f"✅ 创建spec文件: {spec_file}")
return spec_file
def build_exe():
"""构建可执行文件"""
print("=" * 60)
print("简化Windows构建")
print("=" * 60)
# 检查环境
if platform.system() != 'Windows':
print("❌ 此脚本仅适用于Windows")
return False
print(f"✅ 系统: {platform.system()} {platform.release()}")
print(f"✅ Python: {sys.version}")
# 检查主文件
main_file = Path('seat_allocation_system.py')
if not main_file.exists():
print("❌ 未找到主程序文件")
return False
print(f"✅ 主程序: {main_file}")
# 创建spec文件
spec_file = create_simple_spec()
# 构建命令
cmd = [
sys.executable, '-m', 'PyInstaller',
'--clean',
'--noconfirm',
str(spec_file)
]
print(f"\n执行命令: {' '.join(cmd)}")
print("开始构建...")
try:
# 执行构建
result = subprocess.run(
cmd,
capture_output=True,
text=True,
encoding='utf-8',
errors='ignore'
)
# 显示结果
if result.returncode == 0:
print("✅ 构建成功!")
# 检查生成的文件
exe_path = Path('dist/座位分配系统.exe')
if exe_path.exists():
size_mb = exe_path.stat().st_size / (1024 * 1024)
print(f"✅ 生成文件: {exe_path}")
print(f"✅ 文件大小: {size_mb:.1f} MB")
return True
else:
print("❌ 未找到生成的exe文件")
return False
else:
print("❌ 构建失败!")
print("错误输出:")
print(result.stderr)
return False
except Exception as e:
print(f"❌ 构建出错: {e}")
return False
def main():
"""主函数"""
success = build_exe()
if success:
print("\n🎉 构建完成!")
print("可执行文件位置: dist/座位分配系统.exe")
else:
print("\n❌ 构建失败!")
input("\n按Enter键退出...")
if __name__ == '__main__':
main()

101
test_build.py Normal file
View File

@@ -0,0 +1,101 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试构建脚本 - 验证依赖和配置
"""
import sys
import subprocess
import platform
def test_dependencies():
"""测试关键依赖"""
print("=" * 50)
print("测试关键依赖")
print("=" * 50)
dependencies = [
'pandas',
'numpy',
'openpyxl',
'pyinstaller'
]
for dep in dependencies:
try:
__import__(dep)
print(f"{dep} - 已安装")
except ImportError:
print(f"{dep} - 未安装")
return False
return True
def test_hidden_imports():
"""测试隐藏导入"""
print("\n" + "=" * 50)
print("测试隐藏导入")
print("=" * 50)
hidden_imports = [
'numpy.core._methods',
'pandas._libs.window.aggregations',
'ctypes.util'
]
for imp in hidden_imports:
try:
parts = imp.split('.')
module = __import__(parts[0])
for part in parts[1:]:
module = getattr(module, part)
print(f"{imp} - 可访问")
except (ImportError, AttributeError) as e:
print(f"⚠️ {imp} - 无法访问: {e}")
return True
def test_pyinstaller():
"""测试PyInstaller"""
print("\n" + "=" * 50)
print("测试PyInstaller")
print("=" * 50)
try:
result = subprocess.run([
sys.executable, '-m', 'PyInstaller', '--version'
], capture_output=True, text=True)
if result.returncode == 0:
version = result.stdout.strip()
print(f"✅ PyInstaller版本: {version}")
return True
else:
print(f"❌ PyInstaller测试失败: {result.stderr}")
return False
except Exception as e:
print(f"❌ PyInstaller测试出错: {e}")
return False
def main():
"""主函数"""
print(f"Python版本: {sys.version}")
print(f"操作系统: {platform.system()} {platform.release()}")
print(f"架构: {platform.machine()}")
success = True
success &= test_dependencies()
success &= test_hidden_imports()
success &= test_pyinstaller()
print("\n" + "=" * 50)
if success:
print("✅ 所有测试通过,可以进行构建")
else:
print("❌ 存在问题,请先解决依赖问题")
print("=" * 50)
return success
if __name__ == '__main__':
main()

169
test_pandas_dll.py Normal file
View File

@@ -0,0 +1,169 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
测试pandas DLL模块加载
专门检查可能导致PyInstaller问题的模块
"""
import sys
import traceback
from pathlib import Path
def test_pandas_core():
"""测试pandas核心模块"""
print("=" * 50)
print("测试pandas核心模块")
print("=" * 50)
modules_to_test = [
'pandas',
'pandas._libs',
'pandas._libs.lib',
'pandas._libs.hashtable',
'pandas._libs.algos',
'pandas._libs.index',
'pandas._libs.writers',
'pandas._libs.parsers',
'pandas._libs.tslibs',
'pandas._libs.tslibs.base',
'pandas._libs.window',
'pandas._libs.window.aggregations', # 关键模块!
'pandas._libs.window.indexers',
]
success_count = 0
for module in modules_to_test:
try:
__import__(module)
print(f"{module}")
success_count += 1
except ImportError as e:
print(f"{module}: {e}")
except Exception as e:
print(f"⚠️ {module}: {e}")
print(f"\n成功加载: {success_count}/{len(modules_to_test)} 个模块")
return success_count == len(modules_to_test)
def test_pandas_functionality():
"""测试pandas基本功能"""
print("\n" + "=" * 50)
print("测试pandas基本功能")
print("=" * 50)
try:
import pandas as pd
import numpy as np
# 创建测试数据
df = pd.DataFrame({
'A': [1, 2, 3, 4, 5],
'B': [10, 20, 30, 40, 50]
})
print("✅ DataFrame创建成功")
# 测试窗口函数(这是出错的关键)
result = df['A'].rolling(window=2).sum()
print("✅ rolling窗口函数正常")
# 测试聚合函数
agg_result = df.groupby('A').agg({'B': 'sum'})
print("✅ groupby聚合函数正常")
# 测试Excel读写
test_file = Path('test_pandas.xlsx')
df.to_excel(test_file, index=False)
df_read = pd.read_excel(test_file)
test_file.unlink() # 删除测试文件
print("✅ Excel读写功能正常")
return True
except Exception as e:
print(f"❌ pandas功能测试失败: {e}")
traceback.print_exc()
return False
def check_pandas_installation():
"""检查pandas安装信息"""
print("\n" + "=" * 50)
print("pandas安装信息")
print("=" * 50)
try:
import pandas as pd
print(f"✅ pandas版本: {pd.__version__}")
print(f"✅ pandas路径: {pd.__file__}")
# 检查_libs目录
pandas_path = Path(pd.__file__).parent
libs_path = pandas_path / '_libs'
if libs_path.exists():
print(f"✅ _libs目录存在: {libs_path}")
# 统计.pyd文件
pyd_files = list(libs_path.glob('**/*.pyd'))
print(f"✅ 找到 {len(pyd_files)} 个.pyd文件")
# 检查关键文件
key_files = [
'window/aggregations.cp310-win_amd64.pyd',
'window/indexers.cp310-win_amd64.pyd',
'hashtable.cp310-win_amd64.pyd',
'lib.cp310-win_amd64.pyd'
]
for key_file in key_files:
file_path = libs_path / key_file
if file_path.exists():
print(f"✅ 关键文件存在: {key_file}")
else:
print(f"❌ 关键文件缺失: {key_file}")
else:
print(f"❌ _libs目录不存在: {libs_path}")
return False
return True
except Exception as e:
print(f"❌ 检查pandas安装失败: {e}")
return False
def main():
"""主函数"""
print("🔍 pandas DLL模块测试工具")
print("用于诊断PyInstaller构建问题")
print()
# 基本信息
print(f"Python版本: {sys.version}")
print(f"平台: {sys.platform}")
# 运行测试
install_ok = check_pandas_installation()
core_ok = test_pandas_core()
func_ok = test_pandas_functionality()
print("\n" + "=" * 50)
print("测试结果总结")
print("=" * 50)
if install_ok and core_ok and func_ok:
print("✅ 所有测试通过pandas安装正常")
print("💡 如果PyInstaller仍有问题请使用 fix_dll_build.py")
else:
print("❌ 存在问题,建议:")
if not install_ok:
print(" 1. 重新安装pandas: pip uninstall pandas && pip install pandas")
if not core_ok:
print(" 2. 检查pandas C扩展是否正确编译")
if not func_ok:
print(" 3. 检查依赖库numpy等是否正常")
print("=" * 50)
input("\n按Enter键退出...")
if __name__ == '__main__':
main()

View File

@@ -53,38 +53,64 @@ class WindowsBuilder:
print("\n" + "=" * 60)
print("安装构建依赖")
print("=" * 60)
required_packages = [
'pandas>=1.3.0',
'openpyxl>=3.0.0',
'numpy>=1.20.0',
'pyinstaller>=4.0'
'pyinstaller>=5.0',
'pywin32>=227' # Windows特定依赖
]
for package in required_packages:
print(f"\n检查 {package}...")
package_name = package.split('>=')[0]
try:
__import__(package_name)
print(f"{package_name} 已安装")
except ImportError:
print(f"📦 安装 {package}...")
# 特殊处理pywin32
if package_name == 'pywin32':
try:
cmd = [sys.executable, '-m', 'pip', 'install', package, '--user']
result = subprocess.run(cmd, capture_output=True, text=True,
encoding='utf-8', errors='ignore')
if result.returncode == 0:
print(f"{package} 安装成功")
else:
print(f"{package} 安装失败")
print(f"错误信息: {result.stderr}")
return False
except Exception as e:
print(f"❌ 安装过程出错: {e}")
import win32api
print(f"{package_name} 已安装")
continue
except ImportError:
pass
else:
try:
__import__(package_name)
print(f" {package_name} 已安装")
continue
except ImportError:
pass
print(f"📦 安装 {package}...")
try:
cmd = [sys.executable, '-m', 'pip', 'install', package, '--upgrade']
result = subprocess.run(cmd, capture_output=True, text=True,
encoding='utf-8', errors='ignore')
if result.returncode == 0:
print(f"{package} 安装成功")
else:
print(f"{package} 安装失败")
print(f"错误信息: {result.stderr}")
# 对于pywin32尝试替代安装方法
if package_name == 'pywin32':
print("尝试使用conda安装pywin32...")
try:
cmd_conda = ['conda', 'install', '-y', 'pywin32']
result_conda = subprocess.run(cmd_conda, capture_output=True, text=True)
if result_conda.returncode == 0:
print("✅ pywin32 通过conda安装成功")
continue
except:
pass
print("⚠️ pywin32安装失败但可能不影响构建")
continue
return False
except Exception as e:
print(f"❌ 安装过程出错: {e}")
return False
return True
def clean_build_dirs(self):
@@ -128,7 +154,7 @@ a = Analysis(
hiddenimports=[
# 核心依赖
'pandas',
'openpyxl',
'openpyxl',
'numpy',
'xlsxwriter',
'xlrd',
@@ -138,7 +164,7 @@ a = Analysis(
'platform',
'sys',
'os',
# openpyxl相关
'openpyxl.workbook',
'openpyxl.worksheet',
@@ -148,30 +174,79 @@ a = Analysis(
'openpyxl.reader.excel',
'openpyxl.cell',
'openpyxl.formatting',
# pandas相关
# pandas相关 - 完整导入
'pandas.io.excel',
'pandas.io.common',
'pandas.io.parsers',
'pandas.io.formats.excel',
'pandas._libs',
'pandas._libs.tslibs',
'pandas._libs.tslibs.base',
'pandas._libs.tslibs.timedeltas',
'pandas._libs.tslibs.np_datetime',
'pandas._libs.tslibs.nattype',
# numpy相关
'pandas._libs.window',
'pandas._libs.window.aggregations',
'pandas._libs.hashtable',
'pandas._libs.algos',
'pandas._libs.index',
'pandas._libs.lib',
'pandas._libs.missing',
'pandas._libs.parsers',
'pandas._libs.reduction',
'pandas._libs.reshape',
'pandas._libs.sparse',
'pandas._libs.testing',
'pandas._libs.writers',
# numpy相关 - 完整导入
'numpy.core',
'numpy.core.multiarray',
'numpy.core.umath',
'numpy.core._methods',
'numpy.core._dtype_ctypes',
'numpy.core._internal',
'numpy.core.numeric',
'numpy.core.numerictypes',
'numpy.core.function_base',
'numpy.core.machar',
'numpy.core.getlimits',
'numpy.core.shape_base',
'numpy.lib.format',
'numpy.lib.mixins',
'numpy.lib.scimath',
'numpy.lib.stride_tricks',
'numpy.random',
'numpy.random._pickle',
'numpy.random.mtrand',
'numpy.random._common',
'numpy.random._generator',
# 编码相关
'encodings',
'encodings.utf_8',
'encodings.gbk',
'encodings.cp1252',
'encodings.latin1',
# 其他必要模块
'_ctypes',
'ctypes.util'
'ctypes.util',
'pkg_resources',
'pkg_resources.py2_warn',
'pkg_resources.markers',
# Jinja2相关可选某些依赖可能需要
# 'jinja2',
# 'jinja2.ext',
# 'markupsafe',
# Windows特定模块仅在Windows上可用
# 'win32api',
# 'win32con',
# 'pywintypes',
# 'pythoncom'
],
hookspath=[],
hooksconfig={},
@@ -179,7 +254,11 @@ a = Analysis(
excludes=[
# 排除不必要的大型库
'matplotlib',
'matplotlib.pyplot',
'scipy',
'sklearn',
'tensorflow',
'torch',
'IPython',
'jupyter',
'notebook',
@@ -188,12 +267,37 @@ a = Analysis(
'PyQt6',
'PySide2',
'PySide6',
'PIL',
'cv2',
'seaborn',
'plotly',
'bokeh',
# 测试和开发工具
'test',
'tests',
'unittest',
'pytest',
'setuptools',
'pip',
'wheel'
'wheel',
'distutils',
# 文档和示例
'sphinx',
'docutils',
'examples',
'sample',
# 其他不需要的模块
'curses',
'readline',
'sqlite3',
'xml.etree.ElementTree',
'html',
'http',
'urllib3',
'requests'
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
@@ -240,6 +344,7 @@ exe = EXE(
print("开始构建可执行文件")
print("=" * 60)
# 使用spec文件时只能使用基本选项
cmd = [
sys.executable, '-m', 'PyInstaller',
'--clean',
@@ -349,32 +454,48 @@ chcp 65001 >nul
title 座位分配系统
echo ==========================================
echo 座位分配系统 v1.0
echo 座位分配系统 v2.0
echo ==========================================
echo.
:: 检查数据文件
if not exist "人员信息.xlsx" (
echo [错误] 缺少文件: 人员信息.xlsx
:: 检查Excel文件
echo 正在扫描Excel文件...
:: 计算xlsx文件数量
set count=0
for %%f in (*.xlsx) do (
:: 排除输出和示例文件
echo "%%f" | findstr /v /i "最终分配\|分配日志\|示例\|temp\|backup" >nul
if not errorlevel 1 (
set /a count+=1
echo 发现文件: %%f
)
)
if %count% equ 0 (
echo.
echo 请将 人员信息_示例.xlsx 重命名为 人员信息.xlsx
echo 并按照格式填入您的数据
echo [错误] 未找到Excel数据文件
echo.
echo 请确保当前目录下有Excel数据文件:
echo 1. 人员信息文件 (5-6列): 姓名、证件类型、证件号、手机号、备注等
echo 2. 座位信息文件 (10+列): 区域、楼层、排号、座位号等
echo.
echo 提示: 程序会自动识别文件类型,无需固定文件名
pause
exit /b 1
)
if not exist "座位信息.xlsx" (
echo [错误] 缺少文件: 座位信息.xlsx
if %count% gtr 2 (
echo.
echo 请将 座位信息_示例.xlsx 重命名为 座位信息.xlsx
echo 并按照格式填入您的数据
echo [警告] 发现超过2个Excel文件
echo 为避免识别混淆请确保目录下只有2个数据文件
echo.
echo 请移除多余文件后重试
pause
exit /b 1
)
echo [成功] 数据文件检查通过
echo [成功] 找到 %count% 个Excel文件程序将自动识别文件类型
echo.
echo 正在启动座位分配系统...
echo.

Binary file not shown.

Binary file not shown.

Binary file not shown.

79
快速打包.bat Normal file
View File

@@ -0,0 +1,79 @@
@echo off
chcp 65001 >nul
title 座位分配系统 - 快速打包
echo.
echo ╔══════════════════════════════════════╗
echo ║ 座位分配系统 快速打包 ║
echo ╚══════════════════════════════════════╝
echo.
:: 检查Python
python --version >nul 2>&1
if %errorlevel% neq 0 (
echo ❌ Python未安装请先安装Python
goto :error
)
:: 安装打包工具
echo 📦 安装打包工具...
pip install pyinstaller pandas numpy openpyxl --quiet
:: 清理旧文件
echo 🧹 清理旧文件...
if exist "dist" rmdir /s /q "dist"
if exist "build" rmdir /s /q "build"
if exist "release" rmdir /s /q "release"
:: 开始打包
echo 🔨 开始打包...
pyinstaller --onefile --console --name "座位分配系统" --clean seat_allocation_system.py
:: 检查结果
if not exist "dist\座位分配系统.exe" (
echo ❌ 打包失败
goto :error
)
:: 创建发布目录
echo 📁 创建发布目录...
mkdir "release"
copy "dist\座位分配系统.exe" "release\"
:: 创建说明文件
echo 📝 创建说明文件...
(
echo 座位分配系统 v2.0
echo ==================
echo.
echo 使用方法:
echo 1. 将Excel数据文件放在本程序同一目录
echo 2. 双击运行"座位分配系统.exe"
echo 3. 按提示操作即可
echo.
echo 输出文件将保存在log文件夹中
echo.
echo 如有问题请查看log文件夹中的日志
) > "release\使用说明.txt"
:: 完成
echo.
echo ✅ 打包完成!
echo.
echo 📍 发布文件位置: release\座位分配系统.exe
echo 📍 使用说明: release\使用说明.txt
echo.
echo 可以将release文件夹分发给用户使用
goto :end
:error
echo.
echo ❌ 打包失败,请检查错误信息
echo.
pause
exit /b 1
:end
echo.
echo 按任意键退出...
pause >nul

Binary file not shown.

View File

@@ -1,127 +0,0 @@
@echo off
chcp 65001 >nul
title 座位分配系统 v1.0
:: 设置颜色
color 0F
echo.
echo ==========================================
echo 座位分配系统 v1.0
echo ==========================================
echo.
echo 正在检查运行环境...
echo.
:: 检查可执行文件是否存在
if not exist "座位分配系统.exe" (
echo [错误] 未找到 座位分配系统.exe 文件
echo.
echo 请确保以下文件在同一目录下:
echo - 座位分配系统.exe
echo - 人员信息.xlsx
echo - 座位信息.xlsx
echo.
pause
exit /b 1
)
echo [成功] 程序文件检查通过
:: 检查数据文件
set "missing_files="
if not exist "人员信息.xlsx" (
echo [错误] 缺少文件: 人员信息.xlsx
set "missing_files=1"
) else (
echo [成功] 人员信息.xlsx 存在
)
if not exist "座位信息.xlsx" (
echo [错误] 缺少文件: 座位信息.xlsx
set "missing_files=1"
) else (
echo [成功] 座位信息.xlsx 存在
)
:: 如果有缺失文件,给出指导
if defined missing_files (
echo.
echo ==========================================
echo 数据文件缺失提示
echo ==========================================
echo.
echo 请按照以下步骤准备数据文件:
echo.
echo 1. 如果您有示例文件,请重命名:
echo - 人员信息_示例.xlsx → 人员信息.xlsx
echo - 座位信息_示例.xlsx → 座位信息.xlsx
echo.
echo 2. 数据文件格式要求:
echo.
echo 人员信息.xlsx 必需列:
echo - 姓名: 人员姓名
echo - 证件类型: 身份证/护照等
echo - 证件号: 证件号码
echo - 手机号: 联系电话
echo - 备注: 连坐人数(留空=单独坐)
echo.
echo 座位信息.xlsx 必需列:
echo - 区域: 座位区域
echo - 楼层: 楼层信息
echo - 排号: 排号
echo - 座位号: 具体座位号
echo.
echo 3. 连坐规则:
echo - 单人: 备注列留空
echo - 多人: 第一人填写总人数,其他人留空
echo - 示例: 张三(备注:3)、李四(空)、王五(空)
echo.
pause
exit /b 1
)
echo.
echo ==========================================
echo 开始运行程序
echo ==========================================
echo.
echo 所有检查通过,正在启动座位分配系统...
echo 请等待程序运行完成...
echo.
:: 运行程序并捕获错误
"座位分配系统.exe"
set "exit_code=%ERRORLEVEL%"
echo.
echo ==========================================
echo 运行结果
echo ==========================================
echo.
if %exit_code% equ 0 (
echo [成功] 程序运行成功!
echo.
echo 输出文件说明:
echo - 座位信息_最终分配.xlsx: 最终座位分配结果
echo - 最终座位分配日志.xlsx: 详细分配记录
echo - seat_allocation_log.txt: 运行日志文件
echo.
echo 您可以用Excel打开xlsx文件查看结果
) else (
echo [错误] 程序运行出现错误 (错误代码: %exit_code%)
echo.
echo 可能的原因:
echo 1. 数据文件格式不正确
echo 2. 文件权限不足
echo 3. 磁盘空间不足
echo 4. 缺少必要的依赖
echo.
echo 请查看 seat_allocation_log.txt 获取详细错误信息
)
echo.
echo 按任意键退出...
pause >nul