项目基本完成

This commit is contained in:
yovinchen 2025-06-30 10:36:41 +08:00
commit 672760bb25
12 changed files with 1725 additions and 0 deletions

8
.idea/TableSynthesis.iml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="TableSynthesis" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

6
.idea/misc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="TableSynthesis" />
</component>
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/TableSynthesis.iml" filepath="$PROJECT_DIR$/.idea/TableSynthesis.iml" />
</modules>
</component>
</project>

146
README.md Normal file
View File

@ -0,0 +1,146 @@
# 座位分配系统
智能座位分配工具支持1-10人连坐需求能够处理不连续座位的复杂情况。
## 🎯 功能特点
- ✅ **智能分配**: 支持1-10人的各种连坐需求
- ✅ **数据校验**: 完整的文件格式和逻辑校验
- ✅ **不连续座位**: 自动处理座位号间隙
- ✅ **详细日志**: 完整的操作记录和结果验证
- ✅ **跨平台**: 支持Windows、macOS、Linux
## 📋 使用方法
### 准备Excel文件
1. **人员信息.xlsx** - 包含以下列:
- 姓名、证件类型、证件号、手机号、备注
- 备注数字表示连坐人数备注4表示当前行+后3行共4人连坐
2. **座位信息.xlsx** - 包含以下列:
- 区域、楼层、排号、座位号
### 运行程序
#### Python环境运行
```bash
# 安装依赖
pip install pandas openpyxl
# 运行程序
python seat_allocation_system.py
```
#### 构建可执行文件
```bash
# 在当前平台构建
python simple_build.py
# 跨平台构建macOS构建Windows版本
python cross_platform_build.py
```
### 查看结果
程序运行后会生成:
- `座位信息_最终分配.xlsx` - 最终分配结果
- `最终座位分配日志.xlsx` - 详细分配记录
- `seat_allocation_log.txt` - 完整运行日志
## 🔧 构建选项
### 本地构建
```bash
# 简单构建(推荐)
python simple_build.py
```
### 跨平台构建
```bash
# 在macOS上构建Windows版本
python cross_platform_build.py
```
### 自动化构建
使用GitHub Actions自动构建多平台版本
1. 将 `build_workflow.yml` 放入 `.github/workflows/` 目录
2. 推送到GitHub仓库
3. 自动构建Windows x86/x64和macOS版本
## 📁 文件说明
### 核心文件
- `seat_allocation_system.py` - 主程序
- `simple_build.py` - 简化构建脚本
- `cross_platform_build.py` - 跨平台构建脚本
- `requirements.txt` - Python依赖
### 配置文件
- `build_workflow.yml` - GitHub Actions工作流
- `人员信息.xlsx` - 示例人员数据
- `座位信息.xlsx` - 示例座位数据
## 🎯 支持的平台
### 直接运行
- Windows 7/8/10/11 (x86/x64/ARM64)
- macOS 10.14+ (Intel/Apple Silicon)
- Linux (x86_64/ARM64)
### 构建目标
- **Windows x86** - 32位兼容版本
- **Windows x64** - 64位主流版本
- **macOS** - Intel和Apple Silicon
- **Linux** - 主流发行版
## 📊 数据格式要求
### 人员信息格式
```
姓名 | 证件类型 | 证件号 | 手机号 | 备注
张三 | 身份证 | 123456789012345678 | 13800138000 | 2
李四 | 身份证 | 123456789012345679 | 13800138001 |
王五 | 身份证 | 12345678901234567X | 13800138002 | 3
赵六 | 身份证 | 123456789012345680 | 13800138003 |
钱七 | 身份证 | 123456789012345681 | 13800138004 |
```
### 座位信息格式
```
区域 | 楼层 | 排号 | 座位号
A区通道 | 一层 | 1排 | 1号
A区通道 | 一层 | 1排 | 2号
A区通道 | 一层 | 1排 | 3号
```
## ⚠️ 注意事项
1. **备注逻辑**: 备注数字表示连坐人数,只有组长填写备注,成员留空
2. **证件号格式**: 支持包含X的身份证号自动处理为字符串格式
3. **文字清理**: 自动清除姓名等字段的多余空格
4. **座位连续性**: 支持不连续座位号,算法会自动寻找合适的连续段
## 🔍 故障排除
### 常见问题
1. **文件格式错误**: 确保Excel文件包含必需的列
2. **编码问题**: 确保Excel文件使用UTF-8编码
3. **权限问题**: 确保有读写Excel文件的权限
4. **内存不足**: 处理大量数据时可能需要更多内存
### 技术支持
如遇问题,请查看生成的日志文件:
- `seat_allocation_log.txt` - 详细的运行日志
- 控制台输出 - 实时处理信息
## 📈 版本历史
- **v1.0** - 初始版本,支持基本座位分配
- **v1.1** - 添加数据校验和错误处理
- **v1.2** - 支持不连续座位处理
- **v1.3** - 优化数据类型处理,支持跨平台构建
## 📄 许可证
本项目采用MIT许可证详见LICENSE文件。

89
build_workflow.yml Normal file
View File

@ -0,0 +1,89 @@
# 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 }}

326
cross_platform_build.py Normal file
View File

@ -0,0 +1,326 @@
#!/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()

898
seat_allocation_system.py Normal file
View File

@ -0,0 +1,898 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
完整的座位分配系统
包含文件校验智能分配算法和日志输出功能
支持1-10人连坐需求能够处理不连续座位
"""
import pandas as pd
import numpy as np
from pathlib import Path
import datetime
import sys
class Logger:
"""日志记录器"""
def __init__(self, log_file='seat_allocation_log.txt'):
self.log_file = log_file
self.logs = []
def log(self, message, print_to_console=True):
"""记录日志"""
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] {message}"
self.logs.append(log_entry)
if print_to_console:
print(message)
def save_logs(self):
"""保存日志到文件"""
try:
with open(self.log_file, 'w', encoding='utf-8') as f:
f.write('\n'.join(self.logs))
self.log(f"日志已保存到: {self.log_file}")
return True
except Exception as e:
print(f"保存日志失败: {e}")
return False
class SeatAllocationSystem:
"""座位分配系统"""
def __init__(self):
self.logger = Logger()
self.personnel_df = None
self.seat_df = None
self.seating_groups = []
self.row_analysis = {}
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} 文件不存在")
return False
if not Path(seat_file).exists():
self.logger.log(f"❌ 错误: {seat_file} 文件不存在")
return False
try:
# 读取人员信息,指定数据类型
self.personnel_df = pd.read_excel(personnel_file, dtype={
'姓名': 'str',
'证件类型': 'str',
'证件号': 'str', # 证件号作为字符串读取避免X被转换
'手机号': 'str'
})
# 读取座位信息,指定数据类型
self.seat_df = pd.read_excel(seat_file, dtype={
'区域': 'str',
'楼层': 'str',
'排号': 'str',
'座位号': 'str',
'姓名': 'str',
'证件类型': 'str',
'证件号': 'str',
'手机号': 'str',
'签发地/国籍': 'str'
})
# 清理文字信息中的空格
self.clean_text_data()
self.logger.log(f"✅ 文件加载成功")
self.logger.log(f" 人员信息: {self.personnel_df.shape[0]}× {self.personnel_df.shape[1]}")
self.logger.log(f" 座位信息: {self.seat_df.shape[0]}× {self.seat_df.shape[1]}")
return True
except Exception as e:
self.logger.log(f"❌ 文件加载失败: {e}")
return False
def clean_text_data(self):
"""清理文字数据中的空格"""
self.logger.log("清理文字数据中的空格...")
# 清理人员信息中的空格
text_columns_personnel = ['姓名', '证件类型', '证件号']
for col in text_columns_personnel:
if col in self.personnel_df.columns:
self.personnel_df[col] = self.personnel_df[col].astype(str).str.strip()
# 清理座位信息中的空格
text_columns_seat = ['区域', '楼层', '排号', '座位号', '姓名', '证件类型', '证件号', '签发地/国籍']
for col in text_columns_seat:
if col in self.seat_df.columns:
# 只对非空值进行清理
mask = self.seat_df[col].notna()
self.seat_df.loc[mask, col] = self.seat_df.loc[mask, col].astype(str).str.strip()
self.logger.log("✅ 文字数据清理完成")
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 ['姓名', '证件类型', '证件号', '手机号']:
if col in self.personnel_df.columns:
null_count = self.personnel_df[col].isnull().sum()
if null_count > 0:
self.logger.log(f"⚠️ {col} 列有 {null_count} 个空值")
validation_results.append(False)
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)
else:
self.logger.log("✅ 姓名无重复")
validation_results.append(True)
return all(validation_results)
def validate_seating_groups(self):
"""校验连坐组的完整性和正确性"""
self.logger.log("\n=== 连坐组校验 ===")
validation_results = []
issues = []
i = 0
group_num = 1
while i < len(self.personnel_df):
person = self.personnel_df.iloc[i]
remark = person['备注']
if pd.isna(remark):
# 无备注,单独坐
self.logger.log(f"✅ 第 {group_num} 组: {person['姓名']} (单独)")
i += 1
else:
# 有备注,检查连坐组
try:
group_size = int(remark)
if group_size < 1 or group_size > 10:
issue = f"❌ 第 {group_num} 组: {person['姓名']} 的备注 {group_size} 超出范围 (1-10)"
self.logger.log(issue)
issues.append(issue)
validation_results.append(False)
i += 1
continue
# 检查后续人员是否足够
if i + group_size > len(self.personnel_df):
issue = f"❌ 第 {group_num} 组: {person['姓名']} 需要 {group_size} 人连坐,但后续人员不足"
self.logger.log(issue)
issues.append(issue)
validation_results.append(False)
i += 1
continue
# 检查后续人员的备注是否为空
group_members = []
valid_group = True
for j in range(group_size):
member = self.personnel_df.iloc[i + j]
group_members.append(member['姓名'])
if j == 0:
# 第一个人应该有备注
if pd.isna(member['备注']) or int(member['备注']) != group_size:
issue = f"❌ 第 {group_num} 组: 组长 {member['姓名']} 的备注应该是 {group_size}"
self.logger.log(issue)
issues.append(issue)
valid_group = False
else:
# 后续人员应该没有备注
if not pd.isna(member['备注']):
issue = f"❌ 第 {group_num} 组: {member['姓名']} 应该没有备注(当前备注: {member['备注']}"
self.logger.log(issue)
issues.append(issue)
valid_group = False
if valid_group:
self.logger.log(f"✅ 第 {group_num} 组: {', '.join(group_members)} ({group_size}人连坐)")
validation_results.append(True)
else:
validation_results.append(False)
i += group_size
except ValueError:
issue = f"❌ 第 {group_num} 组: {person['姓名']} 的备注 '{remark}' 不是有效数字"
self.logger.log(issue)
issues.append(issue)
validation_results.append(False)
i += 1
group_num += 1
return all(validation_results), issues
def validate_seat_structure(self):
"""校验座位信息结构和连续性"""
self.logger.log("\n=== 座位信息结构校验 ===")
required_columns = ['区域', '楼层', '排号', '座位号']
validation_results = []
# 检查必需列
missing_columns = []
for col in required_columns:
if col not in self.seat_df.columns:
missing_columns.append(col)
if missing_columns:
self.logger.log(f"❌ 缺少必需列: {missing_columns}")
return False, {}
self.logger.log("✅ 所有必需列都存在")
# 检查数据完整性
for col in required_columns:
null_count = self.seat_df[col].isnull().sum()
if null_count > 0:
self.logger.log(f"{col} 列有 {null_count} 个空值")
validation_results.append(False)
else:
self.logger.log(f"{col} 列数据完整")
validation_results.append(True)
# 分析座位结构
self.logger.log("\n座位结构分析:")
seat_groups = {}
for _, row in self.seat_df.iterrows():
key = (row['区域'], row['楼层'], row['排号'])
if key not in seat_groups:
seat_groups[key] = []
seat_groups[key].append(row['座位号'])
# 检查每排的座位结构和可用连续段
self.logger.log("\n座位结构分析:")
for (area, floor, row_num), seats in seat_groups.items():
# 提取座位号数字
seat_numbers = []
for seat in seats:
try:
seat_numbers.append(int(str(seat).replace('', '')))
except:
self.logger.log(f"{area}-{floor}-{row_num}: 座位号格式异常 '{seat}'")
validation_results.append(False)
continue
seat_numbers.sort()
# 分析连续段
consecutive_segments = []
if seat_numbers:
current_segment = [seat_numbers[0]]
for i in range(1, len(seat_numbers)):
if seat_numbers[i] - seat_numbers[i-1] == 1:
# 连续
current_segment.append(seat_numbers[i])
else:
# 不连续,保存当前段,开始新段
consecutive_segments.append(current_segment)
current_segment = [seat_numbers[i]]
# 添加最后一段
consecutive_segments.append(current_segment)
# 显示分析结果
if len(consecutive_segments) == 1 and len(consecutive_segments[0]) == len(seat_numbers):
self.logger.log(f"{area}-{floor}-{row_num}: {len(seats)} 个座位完全连续 ({min(seat_numbers)}-{max(seat_numbers)})")
else:
segments_info = []
max_segment_size = 0
for segment in consecutive_segments:
if len(segment) == 1:
segments_info.append(f"{segment[0]}")
else:
segments_info.append(f"{segment[0]}-{segment[-1]}")
max_segment_size = max(max_segment_size, len(segment))
self.logger.log(f"📊 {area}-{floor}-{row_num}: {len(seats)} 个座位,{len(consecutive_segments)} 个连续段: {', '.join(segments_info)}")
self.logger.log(f" 最大连续段: {max_segment_size} 个座位")
validation_results.append(True) # 座位不连续不算错误,只是需要特殊处理
return all(validation_results), seat_groups
def validate_capacity_feasibility(self, seat_groups):
"""校验座位容量和分配可行性"""
self.logger.log("\n=== 容量和可行性校验 ===")
# 统计人员总数
total_people = len(self.personnel_df)
total_seats = sum(len(seats) for seats in seat_groups.values())
self.logger.log(f"总人数: {total_people}")
self.logger.log(f"总座位数: {total_seats}")
if total_people > total_seats:
self.logger.log(f"❌ 座位不足: 需要 {total_people} 个座位,只有 {total_seats}")
return False
else:
self.logger.log(f"✅ 座位充足: 剩余 {total_seats - total_people} 个座位")
# 分析连坐组需求
self.logger.log("\n连坐组需求分析:")
group_sizes = []
i = 0
while i < len(self.personnel_df):
person = self.personnel_df.iloc[i]
remark = person['备注']
if pd.isna(remark):
group_sizes.append(1)
i += 1
else:
try:
group_size = int(remark)
group_sizes.append(group_size)
i += group_size
except:
group_sizes.append(1)
i += 1
max_group_size = max(group_sizes)
self.logger.log(f"最大连坐组: {max_group_size}")
# 检查是否有足够的连续座位来容纳最大连坐组
self.logger.log(f"\n连续座位可行性分析:")
max_consecutive_available = 0
feasible_rows = []
for (area, floor, row_num), seats in seat_groups.items():
# 分析每排的连续座位段
seat_numbers = []
for seat in seats:
try:
seat_numbers.append(int(str(seat).replace('', '')))
except:
continue
seat_numbers.sort()
# 找出最大连续段
max_consecutive_in_row = 0
if seat_numbers:
current_consecutive = 1
for i in range(1, len(seat_numbers)):
if seat_numbers[i] - seat_numbers[i-1] == 1:
current_consecutive += 1
else:
max_consecutive_in_row = max(max_consecutive_in_row, current_consecutive)
current_consecutive = 1
max_consecutive_in_row = max(max_consecutive_in_row, current_consecutive)
if max_consecutive_in_row >= max_group_size:
feasible_rows.append((area, floor, row_num, max_consecutive_in_row))
max_consecutive_available = max(max_consecutive_available, max_consecutive_in_row)
self.logger.log(f" {area}-{floor}-{row_num}: 最大连续 {max_consecutive_in_row} 个座位")
self.logger.log(f"\n全场最大连续座位: {max_consecutive_available}")
if max_group_size > max_consecutive_available:
self.logger.log(f"❌ 无法容纳最大连坐组: 需要 {max_group_size} 个连续座位,最大连续段只有 {max_consecutive_available}")
return False
else:
self.logger.log(f"✅ 可以容纳最大连坐组")
self.logger.log(f"可容纳最大连坐组的排数: {len(feasible_rows)}")
# 统计各种大小的连坐组
size_counts = {}
for size in group_sizes:
size_counts[size] = size_counts.get(size, 0) + 1
self.logger.log("\n连坐组分布:")
for size in sorted(size_counts.keys()):
count = size_counts[size]
if size == 1:
self.logger.log(f" 单人组: {count}")
else:
self.logger.log(f" {size}人连坐组: {count}")
return True
def analyze_seating_requirements(self):
"""分析人员连坐需求"""
self.logger.log("\n=== 人员连坐需求分析 ===")
self.seating_groups = []
i = 0
while i < len(self.personnel_df):
person = self.personnel_df.iloc[i]
remark = person['备注']
if pd.isna(remark):
# 无备注,单独坐
self.seating_groups.append({
'type': 'single',
'size': 1,
'members': [person],
'leader': person['姓名']
})
i += 1
else:
# 有备注,按备注数量连坐
group_size = int(remark)
group_members = []
# 收集连坐组成员
for j in range(group_size):
if i + j < len(self.personnel_df):
group_members.append(self.personnel_df.iloc[i + j])
self.seating_groups.append({
'type': 'group' if group_size > 1 else 'single',
'size': len(group_members),
'members': group_members,
'leader': person['姓名']
})
i += 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"总共识别出 {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_seat_structure_advanced(self):
"""高级座位结构分析,识别连续段"""
self.logger.log("\n=== 高级座位结构分析 ===")
seat_groups = {}
for _, row in self.seat_df.iterrows():
key = (row['区域'], row['楼层'], row['排号'])
if key not in seat_groups:
seat_groups[key] = []
seat_groups[key].append({
'index': row.name,
'座位号': row['座位号'],
'row_data': row
})
# 分析每排的连续段
self.row_analysis = {}
for key, seats in seat_groups.items():
area, floor, row_num = key
# 按座位号排序
def get_seat_number(seat_info):
try:
return int(seat_info['座位号'].replace('', ''))
except:
return 0
seats.sort(key=get_seat_number)
seat_numbers = [get_seat_number(seat) for seat in seats]
# 找出所有连续段
consecutive_segments = []
if seat_numbers:
current_segment_start = 0
for i in range(1, len(seat_numbers)):
if seat_numbers[i] - seat_numbers[i-1] != 1:
# 发现间隙,结束当前段
consecutive_segments.append({
'start_idx': current_segment_start,
'end_idx': i - 1,
'size': i - current_segment_start,
'seat_numbers': seat_numbers[current_segment_start:i],
'seats': seats[current_segment_start:i]
})
current_segment_start = i
# 添加最后一段
consecutive_segments.append({
'start_idx': current_segment_start,
'end_idx': len(seat_numbers) - 1,
'size': len(seat_numbers) - current_segment_start,
'seat_numbers': seat_numbers[current_segment_start:],
'seats': seats[current_segment_start:]
})
self.row_analysis[key] = {
'total_seats': len(seats),
'all_seats': seats,
'consecutive_segments': consecutive_segments,
'max_consecutive': max(seg['size'] for seg in consecutive_segments) if consecutive_segments else 0
}
# 显示分析结果
if len(consecutive_segments) == 1:
self.logger.log(f"{area}-{floor}-{row_num}: {len(seats)} 个座位完全连续")
else:
segments_info = []
for seg in consecutive_segments:
if seg['size'] == 1:
segments_info.append(f"{seg['seat_numbers'][0]}")
else:
segments_info.append(f"{seg['seat_numbers'][0]}-{seg['seat_numbers'][-1]}")
self.logger.log(f"📊 {area}-{floor}-{row_num}: {len(seats)} 个座位,{len(consecutive_segments)} 段: {', '.join(segments_info)}")
self.logger.log(f" 最大连续段: {self.row_analysis[key]['max_consecutive']} 个座位")
return True
def smart_seat_assignment(self):
"""智能座位分配算法"""
self.logger.log("\n=== 开始智能座位分配 ===")
# 按组大小排序,大组优先分配
sorted_groups = sorted(self.seating_groups, key=lambda x: x['size'], reverse=True)
# 创建座位分配结果
seat_df_copy = self.seat_df.copy()
# 记录已使用的座位
used_seats = set()
assignment_log = []
unassigned_groups = []
self.logger.log(f"需要分配 {len(sorted_groups)} 个组")
for group_idx, seating_group in enumerate(sorted_groups):
group_size = seating_group['size']
group_type = seating_group['type']
leader = seating_group['leader']
self.logger.log(f"\n处理第 {group_idx + 1} 组: {leader} ({group_type}, {group_size} 人)")
if group_size == 1:
# 单人组,找任意可用座位
assigned = False
for (area, floor, row_num), analysis in self.row_analysis.items():
for seat_info in analysis['all_seats']:
if seat_info['index'] not in used_seats:
# 分配座位
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()
assignment_log.append({
'组号': group_idx + 1,
'组类型': group_type,
'组大小': group_size,
'组长': leader,
'姓名': person_info['姓名'],
'区域': area,
'楼层': floor,
'排号': row_num,
'座位号': seat_info['座位号']
})
used_seats.add(seat_index)
self.logger.log(f" 分配到 {area}-{floor}-{row_num}: {person_info['姓名']} -> {seat_info['座位号']}")
assigned = True
break
if assigned:
break
else:
# 多人组,需要连续座位
# 更新可用座位分析(排除已使用的座位)
current_row_analysis = {}
for key, analysis in self.row_analysis.items():
available_seats = [seat for seat in analysis['all_seats'] if seat['index'] not in used_seats]
if available_seats:
# 重新分析连续段
available_seats.sort(key=lambda x: int(x['座位号'].replace('', '')))
seat_numbers = [int(seat['座位号'].replace('', '')) for seat in available_seats]
# 找出连续段
consecutive_segments = []
if seat_numbers:
current_segment_start = 0
for i in range(1, len(seat_numbers)):
if seat_numbers[i] - seat_numbers[i-1] != 1:
consecutive_segments.append({
'start_idx': current_segment_start,
'end_idx': i - 1,
'size': i - current_segment_start,
'seats': available_seats[current_segment_start:i]
})
current_segment_start = i
consecutive_segments.append({
'start_idx': current_segment_start,
'end_idx': len(seat_numbers) - 1,
'size': len(seat_numbers) - current_segment_start,
'seats': available_seats[current_segment_start:]
})
current_row_analysis[key] = {
'consecutive_segments': consecutive_segments
}
# 寻找合适的连续座位
assigned = False
for (area, floor, row_num), analysis in current_row_analysis.items():
for segment in analysis['consecutive_segments']:
if segment['size'] >= group_size:
# 找到合适的连续座位
seats_to_use = segment['seats'][:group_size]
seat_numbers = [int(seat['座位号'].replace('', '')) for seat in seats_to_use]
self.logger.log(f" 分配到 {area}-{floor}-{row_num} (连续座位: {min(seat_numbers)}-{max(seat_numbers)})")
# 分配座位
for i, seat_info in enumerate(seats_to_use):
person_info = seating_group['members'][i]
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()
assignment_log.append({
'组号': group_idx + 1,
'组类型': group_type,
'组大小': group_size,
'组长': leader,
'姓名': person_info['姓名'],
'区域': area,
'楼层': floor,
'排号': row_num,
'座位号': seat_info['座位号']
})
used_seats.add(seat_index)
self.logger.log(f" {person_info['姓名']} -> {seat_info['座位号']}")
assigned = True
break
if assigned:
break
if not assigned:
self.logger.log(f" ❌ 无法为第 {group_idx + 1} 组分配座位")
unassigned_groups.append(seating_group)
# 显示未分配的组
if unassigned_groups:
self.logger.log(f"\n⚠️ 有 {len(unassigned_groups)} 个组未能分配座位")
for group in unassigned_groups:
if group['type'] == 'single':
self.logger.log(f" 未分配: {group['leader']}")
else:
member_names = [member['姓名'] for member in group['members']]
self.logger.log(f" 未分配: {', '.join(member_names)} (连坐 {group['size']} 人)")
return seat_df_copy, assignment_log
def save_results(self, seat_df_result, assignment_log):
"""保存分配结果"""
try:
# 保存更新后的座位信息
output_file = '座位信息_最终分配.xlsx'
seat_df_result.to_excel(output_file, index=False)
self.logger.log(f"\n座位分配结果已保存到: {output_file}")
# 保存分配日志
if assignment_log:
log_df = pd.DataFrame(assignment_log)
log_file = '最终座位分配日志.xlsx'
log_df.to_excel(log_file, index=False)
self.logger.log(f"分配日志已保存到: {log_file}")
# 显示分配统计
self.logger.log(f"\n=== 分配统计 ===")
self.logger.log(f"总共分配了 {len(assignment_log)} 个座位")
# 按组大小统计
size_stats = log_df.groupby('组大小').agg({
'姓名': 'count',
'组号': 'nunique'
}).rename(columns={'姓名': '总人数', '组号': '组数'})
self.logger.log("\n按组大小统计:")
for size, row in size_stats.iterrows():
if size == 1:
self.logger.log(f" 单人组: {row['组数']} 个组, {row['总人数']}")
else:
self.logger.log(f" {size}人连坐组: {row['组数']} 个组, {row['总人数']}")
# 验证连续性
self.logger.log("\n=== 连续性验证 ===")
consecutive_check = []
for group_num in sorted(log_df['组号'].unique()):
group_data = log_df[log_df['组号'] == group_num]
group_size = group_data.iloc[0]['组大小']
group_leader = group_data.iloc[0]['组长']
if group_size > 1: # 多人组
# 检查是否在同一排
areas = group_data['区域'].unique()
floors = group_data['楼层'].unique()
rows = group_data['排号'].unique()
if len(areas) == 1 and len(floors) == 1 and len(rows) == 1:
# 在同一排,检查座位号是否连续
seats = group_data['座位号'].tolist()
seat_numbers = []
for seat in seats:
try:
seat_numbers.append(int(seat.replace('', '')))
except:
seat_numbers.append(0)
seat_numbers.sort()
is_consecutive = all(seat_numbers[i] + 1 == seat_numbers[i+1] for i in range(len(seat_numbers)-1))
if is_consecutive:
consecutive_check.append(True)
self.logger.log(f"✅ 组 {group_num} ({group_leader}): {group_size}人连坐,座位连续 {seat_numbers}")
else:
consecutive_check.append(False)
self.logger.log(f"❌ 组 {group_num} ({group_leader}): {group_size}人连坐,座位不连续 {seat_numbers}")
else:
consecutive_check.append(False)
self.logger.log(f"❌ 组 {group_num} ({group_leader}): 不在同一排")
success_rate = sum(consecutive_check) / len(consecutive_check) * 100 if consecutive_check else 100
self.logger.log(f"\n连续性检查结果: {sum(consecutive_check)}/{len(consecutive_check)} 个多人组座位连续 ({success_rate:.1f}%)")
return True
except Exception as e:
self.logger.log(f"保存结果时出错: {e}")
return False
def run_validation(self):
"""运行完整的文件校验"""
self.logger.log("=" * 60)
self.logger.log("座位分配系统 - 文件校验")
self.logger.log("=" * 60)
# 加载数据
if not self.load_data():
return False
# 人员信息结构校验
personnel_structure_valid = self.validate_personnel_structure()
# 连坐组校验
seating_groups_valid, group_issues = self.validate_seating_groups()
# 座位信息校验
seat_structure_valid, seat_groups = self.validate_seat_structure()
# 容量可行性校验
capacity_valid = self.validate_capacity_feasibility(seat_groups)
# 总结
self.logger.log("\n" + "=" * 60)
self.logger.log("校验结果总结")
self.logger.log("=" * 60)
all_valid = all([
personnel_structure_valid,
seating_groups_valid,
seat_structure_valid,
capacity_valid
])
self.logger.log(f"人员信息结构: {'✅ 通过' if personnel_structure_valid else '❌ 失败'}")
self.logger.log(f"连坐组完整性: {'✅ 通过' if seating_groups_valid else '❌ 失败'}")
self.logger.log(f"座位信息结构: {'✅ 通过' if seat_structure_valid else '❌ 失败'}")
self.logger.log(f"容量可行性: {'✅ 通过' if capacity_valid else '❌ 失败'}")
self.logger.log(f"\n总体校验结果: {'✅ 全部通过' if all_valid else '❌ 存在问题'}")
if group_issues:
self.logger.log(f"\n发现的问题:")
for issue in group_issues:
self.logger.log(f" {issue}")
if all_valid:
self.logger.log("\n🎉 文件校验通过,可以进行座位分配!")
else:
self.logger.log("\n⚠️ 请修复上述问题后再进行座位分配。")
return all_valid
def run_allocation(self):
"""运行完整的座位分配"""
self.logger.log("\n" + "=" * 60)
self.logger.log("开始座位分配")
self.logger.log("=" * 60)
# 分析人员连坐需求
if not self.analyze_seating_requirements():
return False
# 高级座位结构分析
if not self.analyze_seat_structure_advanced():
return False
# 执行智能座位分配
seat_df_result, assignment_log = self.smart_seat_assignment()
# 保存结果
if self.save_results(seat_df_result, assignment_log):
self.logger.log("\n🎉 座位分配完成!")
return True
else:
self.logger.log("\n❌ 座位分配失败!")
return False
def main():
"""主函数"""
system = SeatAllocationSystem()
try:
# 运行校验
if system.run_validation():
# 校验通过,运行分配
system.run_allocation()
# 保存日志
system.logger.save_logs()
except Exception as e:
system.logger.log(f"系统运行出错: {e}")
system.logger.save_logs()
if __name__ == "__main__":
main()

232
simple_build.py Normal file
View File

@ -0,0 +1,232 @@
#!/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()

BIN
人员信息.xlsx Normal file

Binary file not shown.

BIN
座位信息.xlsx Normal file

Binary file not shown.