增加按照手机号排序

增加自动选择文以及识别文件
修改座位信息表位置
This commit is contained in:
yovinchen 2025-07-02 16:26:35 +08:00
parent f52df5e42d
commit 02341150a9
14 changed files with 394 additions and 1428 deletions

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位)

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()

View File

@ -1,127 +0,0 @@
[2025-07-01 15:41:09] ============================================================
[2025-07-01 15:41:09] 座位分配系统 - 文件校验
[2025-07-01 15:41:09] ============================================================
[2025-07-01 15:41:09] === 开始加载数据 ===
[2025-07-01 15:41:09] 正在读取人员信息文件: 人员信息.xlsx
[2025-07-01 15:41:09] 正在读取座位信息文件: 座位信息.xlsx
[2025-07-01 15:41:09] 开始过滤座位数据...
[2025-07-01 15:41:09] 原始数据行数: 17
[2025-07-01 15:41:09] 过滤后数据行数: 6
[2025-07-01 15:41:09] 移除无效行数: 11
[2025-07-01 15:41:09] ✅ 已过滤掉 11 行无效数据(空行、示例数据等)
[2025-07-01 15:41:09] 有效座位区域: 西北门入口场地A5区
[2025-07-01 15:41:09] 有效座位楼层: 场地
[2025-07-01 15:41:09] 清理文字数据中的空格...
[2025-07-01 15:41:09] ✅ 文字数据清理完成
[2025-07-01 15:41:09] ✅ 文件加载成功
[2025-07-01 15:41:09] 人员信息: 6 行 × 6 列
[2025-07-01 15:41:09] 座位信息: 6 行 × 12 列
[2025-07-01 15:41:09]
=== 人员信息结构校验 ===
[2025-07-01 15:41:09] ✅ 所有必需列都存在
[2025-07-01 15:41:09]
数据完整性检查:
[2025-07-01 15:41:09] ✅ 姓名 列数据完整
[2025-07-01 15:41:09] ✅ 证件类型 列数据完整
[2025-07-01 15:41:09] ✅ 证件号 列数据完整
[2025-07-01 15:41:09] ✅ 手机号 列数据完整
[2025-07-01 15:41:09] ✅ 姓名无重复
[2025-07-01 15:41:09]
=== 连坐组校验 ===
[2025-07-01 15:41:09] ✅ 第 1 组: 叶一帆 (单独)
[2025-07-01 15:41:09] ✅ 第 2 组: 刘泽 (单独)
[2025-07-01 15:41:09] ✅ 第 3 组: 黄锡恩 (单独)
[2025-07-01 15:41:09] ✅ 第 4 组: 胡中, 丁逸夫 (2人连坐)
[2025-07-01 15:41:09] ✅ 第 5 组: 沈佳琰 (单独)
[2025-07-01 15:41:09]
=== 座位信息结构校验 ===
[2025-07-01 15:41:09] ✅ 所有必需列都存在
[2025-07-01 15:41:09] ✅ 区域 列数据完整
[2025-07-01 15:41:09] ✅ 楼层 列数据完整
[2025-07-01 15:41:09] ✅ 排号 列数据完整
[2025-07-01 15:41:09] ✅ 座位号 列数据完整
[2025-07-01 15:41:09]
座位结构分析:
[2025-07-01 15:41:09]
座位结构分析:
[2025-07-01 15:41:09] ✅ 西北门入口场地A5区-场地-19排: 6 个座位完全连续 (1-6)
[2025-07-01 15:41:09]
=== 容量和可行性校验 ===
[2025-07-01 15:41:09] 总人数: 6
[2025-07-01 15:41:09] 总座位数: 6
[2025-07-01 15:41:09] ✅ 座位充足: 剩余 0 个座位
[2025-07-01 15:41:09]
连坐组需求分析:
[2025-07-01 15:41:09] 最大连坐组: 2 人
[2025-07-01 15:41:09]
连续座位可行性分析:
[2025-07-01 15:41:09] 西北门入口场地A5区-场地-19排: 最大连续 6 个座位
[2025-07-01 15:41:09]
全场最大连续座位: 6 个
[2025-07-01 15:41:09] ✅ 可以容纳最大连坐组
[2025-07-01 15:41:09] 可容纳最大连坐组的排数: 1 个
[2025-07-01 15:41:09]
连坐组分布:
[2025-07-01 15:41:09] 单人组: 4 个
[2025-07-01 15:41:09] 2人连坐组: 1 个
[2025-07-01 15:41:09]
============================================================
[2025-07-01 15:41:09] 校验结果总结
[2025-07-01 15:41:09] ============================================================
[2025-07-01 15:41:09] 人员信息结构: ✅ 通过
[2025-07-01 15:41:09] 连坐组完整性: ✅ 通过
[2025-07-01 15:41:09] 座位信息结构: ✅ 通过
[2025-07-01 15:41:09] 容量可行性: ✅ 通过
[2025-07-01 15:41:09]
总体校验结果: ✅ 全部通过
[2025-07-01 15:41:09]
🎉 文件校验通过,可以进行座位分配!
[2025-07-01 15:41:19]
============================================================
[2025-07-01 15:41:19] 开始座位分配
[2025-07-01 15:41:19] ============================================================
[2025-07-01 15:41:19]
=== 人员连坐需求分析 ===
[2025-07-01 15:41:19] 总共识别出 5 个座位组:
[2025-07-01 15:41:19] 单人组: 4 个
[2025-07-01 15:41:19] 2人连坐组: 1 个
[2025-07-01 15:41:19]
=== 高级座位结构分析 ===
[2025-07-01 15:41:19] ✅ 西北门入口场地A5区-场地-19排: 6 个座位完全连续
[2025-07-01 15:41:19]
=== 开始智能座位分配 ===
[2025-07-01 15:41:19] 需要分配 5 个组
[2025-07-01 15:41:19]
处理第 1 组: 胡中 (group, 2 人)
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排 (连续座位: 1-2)
[2025-07-01 15:41:19] 胡中 -> 1号
[2025-07-01 15:41:19] 丁逸夫 -> 2号
[2025-07-01 15:41:19]
处理第 2 组: 叶一帆 (single, 1 人)
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排: 叶一帆 -> 3号
[2025-07-01 15:41:19]
处理第 3 组: 刘泽 (single, 1 人)
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排: 刘泽 -> 4号
[2025-07-01 15:41:19]
处理第 4 组: 黄锡恩 (single, 1 人)
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排: 黄锡恩 -> 5号
[2025-07-01 15:41:19]
处理第 5 组: 沈佳琰 (single, 1 人)
[2025-07-01 15:41:19] 分配到 西北门入口场地A5区-场地-19排: 沈佳琰 -> 6号
[2025-07-01 15:41:19]
座位分配结果已保存到: log/座位信息_最终分配.xlsx
[2025-07-01 15:41:19] 分配日志已保存到: log/最终座位分配日志.xlsx
[2025-07-01 15:41:19]
=== 分配统计 ===
[2025-07-01 15:41:19] 总共分配了 6 个座位
[2025-07-01 15:41:19]
按组大小统计:
[2025-07-01 15:41:19] 单人组: 4 个组, 4 人
[2025-07-01 15:41:19] 2人连坐组: 1 个组, 2 人
[2025-07-01 15:41:19]
=== 连续性验证 ===
[2025-07-01 15:41:19] ✅ 组 1 (胡中): 2人连坐座位连续 [1, 2]
[2025-07-01 15:41:19]
连续性检查结果: 1/1 个多人组座位连续 (100.0%)
[2025-07-01 15:41:19]
🎉 座位分配完成!

Binary file not shown.

Binary file not shown.

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,85 +0,0 @@
[2025-07-01 15:33:27] ============================================================
[2025-07-01 15:33:27] 座位分配系统 - 文件校验
[2025-07-01 15:33:27] ============================================================
[2025-07-01 15:33:27] === 开始加载数据 ===
[2025-07-01 15:33:27] 正在读取人员信息文件: 人员信息.xlsx
[2025-07-01 15:33:27] 正在读取座位信息文件: 座位信息.xlsx
[2025-07-01 15:33:27] 清理文字数据中的空格...
[2025-07-01 15:33:27] ✅ 文字数据清理完成
[2025-07-01 15:33:27] ✅ 文件加载成功
[2025-07-01 15:33:27] 人员信息: 6 行 × 6 列
[2025-07-01 15:33:27] 座位信息: 17 行 × 12 列
[2025-07-01 15:33:27]
=== 人员信息结构校验 ===
[2025-07-01 15:33:27] ✅ 所有必需列都存在
[2025-07-01 15:33:27]
数据完整性检查:
[2025-07-01 15:33:27] ✅ 姓名 列数据完整
[2025-07-01 15:33:27] ✅ 证件类型 列数据完整
[2025-07-01 15:33:27] ✅ 证件号 列数据完整
[2025-07-01 15:33:27] ✅ 手机号 列数据完整
[2025-07-01 15:33:27] ✅ 姓名无重复
[2025-07-01 15:33:27]
=== 连坐组校验 ===
[2025-07-01 15:33:27] ✅ 第 1 组: 叶一帆 (单独)
[2025-07-01 15:33:27] ✅ 第 2 组: 刘泽 (单独)
[2025-07-01 15:33:27] ✅ 第 3 组: 黄锡恩 (单独)
[2025-07-01 15:33:27] ✅ 第 4 组: 胡中, 丁逸夫 (2人连坐)
[2025-07-01 15:33:27] ✅ 第 5 组: 沈佳琰 (单独)
[2025-07-01 15:33:27]
=== 座位信息结构校验 ===
[2025-07-01 15:33:27] ✅ 所有必需列都存在
[2025-07-01 15:33:27] ❌ 区域 列有 11 个空值
[2025-07-01 15:33:27] ❌ 楼层 列有 11 个空值
[2025-07-01 15:33:27] ❌ 排号 列有 11 个空值
[2025-07-01 15:33:27] ❌ 座位号 列有 11 个空值
[2025-07-01 15:33:27]
座位结构分析:
[2025-07-01 15:33:27]
座位结构分析:
[2025-07-01 15:33:27] ✅ 西北门入口场地A5区-场地-19排: 6 个座位完全连续 (1-6)
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] ❌ nan-nan-nan: 座位号格式异常 'nan'
[2025-07-01 15:33:27] 📊 nan-nan-nan: 11 个座位0 个连续段:
[2025-07-01 15:33:27] 最大连续段: 0 个座位
[2025-07-01 15:33:27]
=== 容量和可行性校验 ===
[2025-07-01 15:33:27] 总人数: 6
[2025-07-01 15:33:27] 总座位数: 17
[2025-07-01 15:33:27] ✅ 座位充足: 剩余 11 个座位
[2025-07-01 15:33:27]
连坐组需求分析:
[2025-07-01 15:33:27] 最大连坐组: 2 人
[2025-07-01 15:33:27]
连续座位可行性分析:
[2025-07-01 15:33:27] 西北门入口场地A5区-场地-19排: 最大连续 6 个座位
[2025-07-01 15:33:27] nan-nan-nan: 最大连续 0 个座位
[2025-07-01 15:33:27]
全场最大连续座位: 6 个
[2025-07-01 15:33:27] ✅ 可以容纳最大连坐组
[2025-07-01 15:33:27] 可容纳最大连坐组的排数: 1 个
[2025-07-01 15:33:27]
连坐组分布:
[2025-07-01 15:33:27] 单人组: 4 个
[2025-07-01 15:33:27] 2人连坐组: 1 个
[2025-07-01 15:33:27]
============================================================
[2025-07-01 15:33:27] 校验结果总结
[2025-07-01 15:33:27] ============================================================
[2025-07-01 15:33:27] 人员信息结构: ✅ 通过
[2025-07-01 15:33:27] 连坐组完整性: ✅ 通过
[2025-07-01 15:33:27] 座位信息结构: ❌ 失败
[2025-07-01 15:33:27] 容量可行性: ✅ 通过
[2025-07-01 15:33:27]
总体校验结果: ❌ 存在问题
[2025-07-01 15:33:27]
⚠️ 请修复上述问题后再进行座位分配。

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():
"""检查并安装必要的依赖包"""
@ -192,16 +194,8 @@ def check_data_files():
return True, str(personnel_file), str(seat_file)
# 只有在依赖检查通过后才导入这些包
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:
# 检查依赖
if not check_dependencies():
print("❌ 依赖检查失败")
input("按Enter键退出...")
sys.exit(1)
@ -253,11 +247,212 @@ class SeatAllocationSystem:
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("=== 开始加载数据 ===")
@ -411,15 +606,35 @@ class SeatAllocationSystem:
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()}")
# 检查重复人员(姓名+身份证号组合)
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("✅ 姓名无重复")
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(False)
return all(validation_results)
def validate_seating_groups(self):
@ -693,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
@ -710,7 +988,8 @@ class SeatAllocationSystem:
'type': 'single',
'size': 1,
'members': [person],
'leader': person['姓名']
'leader': person['姓名'],
'grouping_method': 'remark'
})
i += 1
else:
@ -727,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
@ -738,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:
@ -861,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,
@ -945,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,
@ -990,6 +1272,15 @@ class SeatAllocationSystem:
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:
@ -1067,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
@ -1120,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
# 高级座位结构分析
@ -1139,6 +1437,29 @@ 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)

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()

View File

@ -1,116 +0,0 @@
@echo off
chcp 65001 >nul
title 座位分配系统 v2.0
:: 设置颜色
color 0F
echo.
echo ==========================================
echo 座位分配系统 v2.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 [成功] 程序文件检查通过
:: 检查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 [错误] 未找到Excel数据文件
echo.
echo 请确保当前目录下有Excel数据文件:
echo 1. 人员信息文件 (5-6列): 姓名、证件类型、证件号、手机号、备注等
echo 2. 座位信息文件 (10+列): 区域、楼层、排号、座位号等
echo.
echo 提示: 程序会自动识别文件类型,无需固定文件名
pause
exit /b 1
)
if %count% gtr 2 (
echo.
echo [警告] 发现超过2个Excel文件
echo 为避免识别混淆请确保目录下只有2个数据文件
echo 程序会自动排除输出文件和示例文件
echo.
echo 当前Excel文件:
for %%f in (*.xlsx) do echo %%f
echo.
echo 请移除多余文件后重试
pause
exit /b 1
)
echo [成功] 找到 %count% 个Excel文件程序将自动识别文件类型
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