增加发布正式版的命令
This commit is contained in:
21
.env.example
Normal file
21
.env.example
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# ========================================
|
||||||
|
# 订单日报系统 - 环境变量配置示例
|
||||||
|
# ========================================
|
||||||
|
# 使用方法:复制本文件为 .env,然后修改相应配置
|
||||||
|
# 注意:如果使用 .env 文件,需要安装 python-dotenv
|
||||||
|
|
||||||
|
# 服务监听地址
|
||||||
|
DEPLOY_RUN_HOST=0.0.0.0
|
||||||
|
|
||||||
|
# 服务监听端口
|
||||||
|
DEPLOY_RUN_PORT=5000
|
||||||
|
|
||||||
|
# 数据库配置
|
||||||
|
DB_HOST=haoslm2.xicp.net
|
||||||
|
DB_PORT=10216
|
||||||
|
DB_USER=root
|
||||||
|
DB_PASSWORD=your_password
|
||||||
|
DB_NAME=your_database
|
||||||
|
|
||||||
|
# 日志级别(DEBUG/INFO/WARNING/ERROR)
|
||||||
|
LOG_LEVEL=INFO
|
||||||
19
.env.prod
Normal file
19
.env.prod
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# ========================================
|
||||||
|
# 订单日报系统 - 正式环境配置
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
# 服务监听地址
|
||||||
|
DEPLOY_RUN_HOST=0.0.0.0
|
||||||
|
|
||||||
|
# 服务监听端口
|
||||||
|
DEPLOY_RUN_PORT=5000
|
||||||
|
|
||||||
|
# 数据库配置(正式环境)
|
||||||
|
DB_HOST=192.168.10.250
|
||||||
|
DB_PORT=9030
|
||||||
|
DB_USER=root
|
||||||
|
DB_PASSWORD=DsideaL147258369
|
||||||
|
DB_NAME=yltcharge
|
||||||
|
|
||||||
|
# 日志级别(DEBUG/INFO/WARNING/ERROR)
|
||||||
|
LOG_LEVEL=INFO
|
||||||
32
lib/db.py
32
lib/db.py
@@ -1,24 +1,28 @@
|
|||||||
"""
|
"""
|
||||||
数据库连接配置
|
数据库连接配置
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
import pymysql
|
import pymysql
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from lib.logger import log_info, log_error, log_warning
|
from lib.logger import log_info, log_error, log_warning
|
||||||
|
|
||||||
# 数据库配置
|
def get_db_config():
|
||||||
DB_CONFIG = {
|
"""从环境变量或默认值获取数据库配置"""
|
||||||
'host': 'haoslm2.xicp.net',
|
return {
|
||||||
'port': 10216,
|
'host': os.environ.get('DB_HOST', 'haoslm2.xicp.net'),
|
||||||
'user': 'root',
|
'port': int(os.environ.get('DB_PORT', 10216)),
|
||||||
'password': 'DsideaL147258369',
|
'user': os.environ.get('DB_USER', 'root'),
|
||||||
'database': 'yltcharge',
|
'password': os.environ.get('DB_PASSWORD', 'DsideaL147258369'),
|
||||||
'charset': 'utf8mb4',
|
'database': os.environ.get('DB_NAME', 'yltcharge'),
|
||||||
'cursorclass': pymysql.cursors.DictCursor,
|
'charset': 'utf8mb4',
|
||||||
'autocommit': True,
|
'cursorclass': pymysql.cursors.DictCursor,
|
||||||
'connect_timeout': 30, # 连接超时30秒
|
'autocommit': True,
|
||||||
'read_timeout': 60, # 读取超时60秒
|
'connect_timeout': 30,
|
||||||
'write_timeout': 60 # 写入超时60秒
|
'read_timeout': 60,
|
||||||
}
|
'write_timeout': 60
|
||||||
|
}
|
||||||
|
|
||||||
|
DB_CONFIG = get_db_config()
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
|
|||||||
@@ -869,31 +869,46 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
charge_degree_col = col_idx
|
charge_degree_col = col_idx
|
||||||
break
|
break
|
||||||
|
|
||||||
# 第一列显示标题
|
# 总列数
|
||||||
|
total_cols = len(selected_fields) + 2
|
||||||
|
if has_custom_service_fee:
|
||||||
|
total_cols += 1
|
||||||
|
|
||||||
|
# 标题文字
|
||||||
title_text = f'当月累计充电量(共{total_days}天,截止{report_date})'
|
title_text = f'当月累计充电量(共{total_days}天,截止{report_date})'
|
||||||
if missing_dates and len(missing_dates) > 0:
|
if missing_dates and len(missing_dates) > 0:
|
||||||
# 最多显示5个缺失日期,超过的用"...共N天"表示
|
|
||||||
if len(missing_dates) <= 5:
|
if len(missing_dates) <= 5:
|
||||||
missing_str = '、'.join([d[5:] for d in missing_dates]) # 只显示月-日
|
missing_str = '、'.join([d[5:] for d in missing_dates])
|
||||||
else:
|
else:
|
||||||
missing_str = '、'.join([d[5:] for d in missing_dates[:5]]) + f'...共{len(missing_dates)}天'
|
missing_str = '、'.join([d[5:] for d in missing_dates[:5]]) + f'...共{len(missing_dates)}天'
|
||||||
title_text += f'\n(缺:{missing_str})'
|
title_text += f'\n(缺:{missing_str})'
|
||||||
|
|
||||||
ws.cell(row=monthly_row, column=1).value = title_text
|
# 标题跨列:从第1列到 charge_degree_col 的前一列
|
||||||
ws.cell(row=monthly_row, column=1).font = monthly_font
|
# 如果没有找到充电电量列,就跨到倒数第二列
|
||||||
ws.cell(row=monthly_row, column=1).fill = monthly_fill
|
title_end_col = charge_degree_col - 1 if charge_degree_col else total_cols - 1
|
||||||
ws.cell(row=monthly_row, column=1).alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
|
if title_end_col < 1:
|
||||||
ws.cell(row=monthly_row, column=1).border = thin_border
|
title_end_col = 1
|
||||||
|
|
||||||
# 其他列也填充背景色
|
# 合并标题单元格
|
||||||
total_cols = len(selected_fields) + 2
|
if title_end_col > 1:
|
||||||
if has_custom_service_fee:
|
ws.merge_cells(start_row=monthly_row, start_column=1,
|
||||||
total_cols += 1
|
end_row=monthly_row, end_column=title_end_col)
|
||||||
|
|
||||||
|
# 设置标题单元格
|
||||||
|
title_cell = ws.cell(row=monthly_row, column=1)
|
||||||
|
title_cell.value = title_text
|
||||||
|
title_cell.font = monthly_font
|
||||||
|
title_cell.fill = monthly_fill
|
||||||
|
title_cell.alignment = Alignment(horizontal='center', vertical='center', wrap_text=True)
|
||||||
|
title_cell.border = thin_border
|
||||||
|
|
||||||
|
# 其他列填充背景色和边框
|
||||||
for col_idx in range(2, total_cols):
|
for col_idx in range(2, total_cols):
|
||||||
cell = ws.cell(row=monthly_row, column=col_idx)
|
cell = ws.cell(row=monthly_row, column=col_idx)
|
||||||
cell.fill = monthly_fill
|
cell.fill = monthly_fill
|
||||||
cell.border = thin_border
|
cell.border = thin_border
|
||||||
|
|
||||||
|
# 数值放在 charge_degree 列
|
||||||
if charge_degree_col:
|
if charge_degree_col:
|
||||||
cell = ws.cell(row=monthly_row, column=charge_degree_col)
|
cell = ws.cell(row=monthly_row, column=charge_degree_col)
|
||||||
cell.value = total_degree
|
cell.value = total_degree
|
||||||
@@ -901,11 +916,11 @@ def generate_excel(orders, selected_fields, sum_fields, sum_results,
|
|||||||
cell.alignment = Alignment(horizontal='right', vertical='center')
|
cell.alignment = Alignment(horizontal='right', vertical='center')
|
||||||
cell.number_format = '0.000'
|
cell.number_format = '0.000'
|
||||||
|
|
||||||
# 如果有缺失日期,行高加大一点
|
# 行高
|
||||||
if missing_dates and len(missing_dates) > 0:
|
if missing_dates and len(missing_dates) > 0:
|
||||||
ws.row_dimensions[monthly_row].height = 40
|
ws.row_dimensions[monthly_row].height = 45
|
||||||
else:
|
else:
|
||||||
ws.row_dimensions[monthly_row].height = 25
|
ws.row_dimensions[monthly_row].height = 28
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_error(f"[日报生成] 写入月度总计时出错: {e}", 'report')
|
log_error(f"[日报生成] 写入月度总计时出错: {e}", 'report')
|
||||||
|
|
||||||
|
|||||||
@@ -18,5 +18,8 @@ APScheduler==3.10.4
|
|||||||
python-dateutil==2.8.2
|
python-dateutil==2.8.2
|
||||||
pytz==2023.3
|
pytz==2023.3
|
||||||
|
|
||||||
|
# 生产服务器(Windows推荐)
|
||||||
|
waitress==3.0.0
|
||||||
|
|
||||||
# 开发工具(可选)
|
# 开发工具(可选)
|
||||||
python-dotenv==1.0.0
|
python-dotenv==1.0.0
|
||||||
25
run_prod.py
Normal file
25
run_prod.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
生产环境启动脚本
|
||||||
|
使用 waitress 作为 WSGI 服务器
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from waitress import serve
|
||||||
|
from app import app
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
port = int(os.environ.get('DEPLOY_RUN_PORT', 5000))
|
||||||
|
host = os.environ.get('DEPLOY_RUN_HOST', '0.0.0.0')
|
||||||
|
|
||||||
|
print(f'订单日报系统 - 生产模式')
|
||||||
|
print(f'监听地址: {host}:{port}')
|
||||||
|
print('按 Ctrl+C 停止服务')
|
||||||
|
print('-' * 50)
|
||||||
|
|
||||||
|
serve(
|
||||||
|
app,
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
threads=8,
|
||||||
|
connection_limit=200,
|
||||||
|
channel_timeout=300,
|
||||||
|
)
|
||||||
132
启动.bat
Normal file
132
启动.bat
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
title 订单日报系统 - 生产模式
|
||||||
|
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo 订单日报系统 - 生产模式启动
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM ============================================
|
||||||
|
REM 第一步:读取 .env 配置文件(如果存在)
|
||||||
|
REM ============================================
|
||||||
|
echo [1/5] 读取配置...
|
||||||
|
|
||||||
|
if exist ".env" (
|
||||||
|
echo [信息] 找到 .env 文件,正在加载配置...
|
||||||
|
for /f "usebackq tokens=1,2 delims==" %%a in (".env") do (
|
||||||
|
if not "%%a"=="" if not "%%~a"=="#" (
|
||||||
|
set "%%a=%%b"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo [信息] 配置加载完成
|
||||||
|
) else if exist ".env.prod" (
|
||||||
|
echo [信息] 找到 .env.prod 文件,正在加载配置...
|
||||||
|
for /f "usebackq tokens=1,2 delims==" %%a in (".env.prod") do (
|
||||||
|
if not "%%a"=="" if not "%%~a"=="#" (
|
||||||
|
set "%%a=%%b"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo [信息] 配置加载完成
|
||||||
|
) else (
|
||||||
|
echo [信息] 未找到 .env 文件,使用默认配置
|
||||||
|
)
|
||||||
|
|
||||||
|
REM ============================================
|
||||||
|
REM 第二步:检查 Python 是否安装
|
||||||
|
REM ============================================
|
||||||
|
echo.
|
||||||
|
echo [2/5] 检查 Python 环境...
|
||||||
|
|
||||||
|
where python >nul 2>&1
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [错误] 未检测到 Python!
|
||||||
|
echo.
|
||||||
|
echo 请先安装 Python 3.10 或更高版本:
|
||||||
|
echo 下载地址: https://www.python.org/downloads/
|
||||||
|
echo 安装时请勾选 "Add Python to PATH"
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
for /f "delims=" %%v in ('python --version 2^>^&1') do set PY_VERSION=%%v
|
||||||
|
echo [信息] Python 已安装: %PY_VERSION%
|
||||||
|
|
||||||
|
REM ============================================
|
||||||
|
REM 第三步:创建虚拟环境(如果不存在)
|
||||||
|
REM ============================================
|
||||||
|
echo.
|
||||||
|
echo [3/5] 检查虚拟环境...
|
||||||
|
|
||||||
|
if not exist ".venv\Scripts\python.exe" (
|
||||||
|
echo [信息] 虚拟环境不存在,正在创建...
|
||||||
|
python -m venv .venv
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [错误] 虚拟环境创建失败!
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo [信息] 虚拟环境创建成功
|
||||||
|
) else (
|
||||||
|
echo [信息] 虚拟环境已存在
|
||||||
|
)
|
||||||
|
|
||||||
|
set PYTHON=.venv\Scripts\python.exe
|
||||||
|
set PIP=.venv\Scripts\pip.exe
|
||||||
|
|
||||||
|
REM ============================================
|
||||||
|
REM 第四步:安装依赖(只在缺少时安装)
|
||||||
|
REM ============================================
|
||||||
|
echo.
|
||||||
|
echo [4/5] 检查依赖...
|
||||||
|
|
||||||
|
REM 检查核心依赖是否已安装
|
||||||
|
%PYTHON% -c "import flask" 2>nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
set NEED_INSTALL=1
|
||||||
|
) else (
|
||||||
|
set NEED_INSTALL=0
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%NEED_INSTALL%"=="1" (
|
||||||
|
echo [信息] 正在安装依赖,请稍候...
|
||||||
|
%PIP% install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [警告] 清华源安装失败,尝试官方源...
|
||||||
|
%PIP% install -r requirements.txt
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [错误] 依赖安装失败!
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo [信息] 依赖安装完成
|
||||||
|
) else (
|
||||||
|
echo [信息] 依赖已就绪
|
||||||
|
)
|
||||||
|
|
||||||
|
REM ============================================
|
||||||
|
REM 第五步:启动服务
|
||||||
|
REM ============================================
|
||||||
|
echo.
|
||||||
|
echo [5/5] 启动服务...
|
||||||
|
echo.
|
||||||
|
echo [信息] 服务启动后,访问 http://localhost:5000
|
||||||
|
echo [信息] 按 Ctrl+C 停止服务
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
%PYTHON% run_prod.py
|
||||||
|
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo.
|
||||||
|
echo ========================================
|
||||||
|
echo [错误] 服务异常退出,错误代码: %errorlevel%
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
)
|
||||||
123
服务管理.bat
Normal file
123
服务管理.bat
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 >nul
|
||||||
|
title 订单日报系统 - Windows服务管理
|
||||||
|
|
||||||
|
cd /d "%~dp0"
|
||||||
|
|
||||||
|
REM ============================================
|
||||||
|
REM 使用 NSSM 将程序注册为 Windows 服务
|
||||||
|
REM 需要先下载 nssm.exe 放到当前目录
|
||||||
|
REM 下载地址: https://nssm.cc/download
|
||||||
|
REM ============================================
|
||||||
|
|
||||||
|
set SERVICE_NAME=DailyReportSystem
|
||||||
|
set SERVICE_DESC=订单日报系统服务
|
||||||
|
set NSSM_EXE=nssm.exe
|
||||||
|
|
||||||
|
if not exist "%NSSM_EXE%" (
|
||||||
|
echo [错误] 未找到 %NSSM_EXE%
|
||||||
|
echo.
|
||||||
|
echo 请先下载 NSSM:
|
||||||
|
echo 1. 访问 https://nssm.cc/download
|
||||||
|
echo 2. 下载 Win64 版本
|
||||||
|
echo 3. 解压后将 nssm.exe 放到当前目录
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
REM 检查虚拟环境
|
||||||
|
if exist ".venv\Scripts\python.exe" (
|
||||||
|
set PYTHON_PATH=%cd%\.venv\Scripts\python.exe
|
||||||
|
) else (
|
||||||
|
for /f "delims=" %%i in ('where python') do (
|
||||||
|
set PYTHON_PATH=%%i
|
||||||
|
goto :found_python
|
||||||
|
)
|
||||||
|
:found_python
|
||||||
|
)
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo 订单日报系统 - Windows服务管理
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
echo 服务名称: %SERVICE_NAME%
|
||||||
|
echo Python路径: %PYTHON_PATH%
|
||||||
|
echo 工作目录: %cd%
|
||||||
|
echo.
|
||||||
|
echo 请选择操作:
|
||||||
|
echo 1. 安装服务
|
||||||
|
echo 2. 启动服务
|
||||||
|
echo 3. 停止服务
|
||||||
|
echo 4. 重启服务
|
||||||
|
echo 5. 删除服务
|
||||||
|
echo 6. 查看服务状态
|
||||||
|
echo 0. 退出
|
||||||
|
echo.
|
||||||
|
set /p choice=请输入选项:
|
||||||
|
|
||||||
|
if "%choice%"=="1" goto install
|
||||||
|
if "%choice%"=="2" goto start
|
||||||
|
if "%choice%"=="3" goto stop
|
||||||
|
if "%choice%"=="4" goto restart
|
||||||
|
if "%choice%"=="5" goto remove
|
||||||
|
if "%choice%"=="6" goto status
|
||||||
|
if "%choice%"=="0" goto end
|
||||||
|
|
||||||
|
:install
|
||||||
|
echo.
|
||||||
|
echo [信息] 正在安装服务...
|
||||||
|
%NSSM_EXE% install %SERVICE_NAME% "%PYTHON_PATH%" "%cd%\run_prod.py"
|
||||||
|
%NSSM_EXE% set %SERVICE_NAME% AppDirectory "%cd%"
|
||||||
|
%NSSM_EXE% set %SERVICE_NAME% DisplayName "%SERVICE_DESC%"
|
||||||
|
%NSSM_EXE% set %SERVICE_NAME% Description "订单日报系统 - 自动生成充电订单日报报表"
|
||||||
|
%NSSM_EXE% set %SERVICE_NAME% Start SERVICE_AUTO_START
|
||||||
|
%NSSM_EXE% set %SERVICE_NAME% AppStdout "%cd%\logs\service_stdout.log"
|
||||||
|
%NSSM_EXE% set %SERVICE_NAME% AppStderr "%cd%\logs\service_stderr.log"
|
||||||
|
%NSSM_EXE% set %SERVICE_NAME% AppRotateFiles 1
|
||||||
|
%NSSM_EXE% set %SERVICE_NAME% AppRotateOnline 1
|
||||||
|
%NSSM_EXE% set %SERVICE_NAME% AppRotateBytes 10485760
|
||||||
|
echo [信息] 服务安装完成!
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:start
|
||||||
|
echo.
|
||||||
|
echo [信息] 正在启动服务...
|
||||||
|
net start %SERVICE_NAME%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:stop
|
||||||
|
echo.
|
||||||
|
echo [信息] 正在停止服务...
|
||||||
|
net stop %SERVICE_NAME%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:restart
|
||||||
|
echo.
|
||||||
|
echo [信息] 正在重启服务...
|
||||||
|
net stop %SERVICE_NAME%
|
||||||
|
timeout /t 3 /nobreak >nul
|
||||||
|
net start %SERVICE_NAME%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:remove
|
||||||
|
echo.
|
||||||
|
echo [警告] 即将删除服务 %SERVICE_NAME%
|
||||||
|
set /p confirm=确认删除吗?(y/n):
|
||||||
|
if /i not "%confirm%"=="y" goto end
|
||||||
|
echo [信息] 正在停止服务...
|
||||||
|
net stop %SERVICE_NAME% 2>nul
|
||||||
|
echo [信息] 正在删除服务...
|
||||||
|
%NSSM_EXE% remove %SERVICE_NAME% confirm
|
||||||
|
echo [信息] 服务已删除!
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:status
|
||||||
|
echo.
|
||||||
|
echo [信息] 服务状态:
|
||||||
|
sc query %SERVICE_NAME%
|
||||||
|
goto end
|
||||||
|
|
||||||
|
:end
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
Reference in New Issue
Block a user