制作开发文档,添加充电卡备注到日报中

This commit is contained in:
2026-07-29 10:02:32 +08:00
parent 4a1b0f71c3
commit 5d153cf505
6 changed files with 1264 additions and 2 deletions

395
docs/API文档.md Normal file
View File

@@ -0,0 +1,395 @@
# 订单日报系统 API 接口文档
## 一、全局配置接口
### 1.1 获取求和数据采集字段配置
**接口地址**: `GET /api/global-config/sum-data-fields`
**请求参数**: 无
**返回示例**:
```json
{
"success": true,
"data": ["sharp_electricity", "peak_electricity", "flat_electricity", "valley_electricity"]
}
```
**字段说明**:
| 字段Key | 显示名称 | 说明 |
|---------|----------|------|
| sharp_electricity | 尖时电量(kWh) | 尖时段充电电量 |
| peak_electricity | 峰时电量(kWh) | 峰时段充电电量 |
| flat_electricity | 平时电量(kWh) | 平时段充电电量 |
| valley_electricity | 谷时电量(kWh) | 谷时段充电电量 |
| charge_elecfee_amount | 实收电费(元) | 实际收取的电费金额 |
| charge_servicefee_amount | 实收服务费(元) | 实际收取的服务费金额 |
| charge_amount | 实收总金额(元) | 实际收取的总金额(电费+服务费) |
| charge_frequency | 充电次数 | 充电订单次数 |
| custom_service_fee | 自定义服务费(元) | 按自定义单价计算的服务费 |
| custom_total_amount | 自定义实收总金额(元) | 实收电费+自定义服务费 |
### 1.2 保存求和数据采集字段配置
**接口地址**: `POST /api/global-config/sum-data-fields`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| sum_data_fields | Array | 是 | 选中的采集字段Key列表 |
**请求示例**:
```json
{
"sum_data_fields": ["sharp_electricity", "peak_electricity", "flat_electricity", "valley_electricity"]
}
```
**返回示例**:
```json
{
"success": true,
"message": "配置保存成功"
}
```
## 二、求和数据接口
### 2.1 查询求和数据列表
**接口地址**: `GET /api/sum-data`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| page | Integer | 否 | 页码(不传则返回全部数据) |
| page_size | Integer | 否 | 每页大小默认20 |
| start_date | String | 否 | 开始日期格式YYYY-MM-DD |
| end_date | String | 否 | 结束日期格式YYYY-MM-DD |
| config_id | Integer | 否 | 配置ID |
| config_name | String | 否 | 配置名称(模糊搜索) |
| sum_field_key | String | 否 | 求和字段键名 |
| data_source | String | 否 | 数据来源auto/手动编辑) |
**返回示例**:
```json
{
"success": true,
"data": {
"list": [...],
"total": 100,
"page": 1,
"page_size": 20
}
}
```
### 2.2 从历史记录采集求和数据
**接口地址**: `POST /api/sum-data/collect`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| history_id | Integer | 否 | 历史记录ID不传则采集所有 |
| start_date | String | 否 | 开始日期 |
| end_date | String | 否 | 结束日期 |
**返回示例**:
```json
{
"success": true,
"message": "采集完成,共采集 50 条求和数据,跳过 0 条无求和数据的记录"
}
```
### 2.3 批量添加求和数据
**接口地址**: `POST /api/sum-data/batch-add`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| report_date | String | 是 | 报表日期YYYY-MM-DD |
| config_name | String | 是 | 配置名称 |
| split_type | String | 是 | 拆分方式company_id/user_id/station_id |
| split_value | String | 是 | 拆分值 |
| split_name | String | 否 | 拆分显示名称 |
| total_orders | Integer | 否 | 订单数 |
| remark | String | 否 | 备注/修改原因 |
| fields | Array | 是 | 求和字段数据列表 |
**fields数组结构**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| sum_field_key | String | 是 | 字段键名 |
| sum_field_name | String | 是 | 字段显示名称 |
| sum_value | Float | 是 | 求和值 |
### 2.4 更新求和数据
**接口地址**: `PUT /api/sum-data/<id>`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| sum_value | Float | 是 | 更新后的求和值 |
| remark | String | 否 | 修改原因/备注 |
### 2.5 删除求和数据
**接口地址**: `DELETE /api/sum-data/<id>`
**请求参数**: 无
### 2.6 批量删除求和数据
**接口地址**: `POST /api/sum-data/batch-delete`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | Array | 是 | 要删除的记录ID列表 |
### 2.7 导出求和数据
**接口地址**: `GET /api/sum-data/export`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| start_date | String | 否 | 开始日期 |
| end_date | String | 否 | 结束日期 |
| keyword | String | 否 | 配置名称关键字 |
| data_source | String | 否 | 数据来源 |
**返回示例**:
```json
{
"success": true,
"total": 100,
"file_path": "/public/reports/求和数据导出_20260728.xlsx",
"filename": "求和数据导出_20260728.xlsx"
}
```
## 三、配置管理接口
### 3.1 创建配置
**接口地址**: `POST /api/config`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| config_name | String | 是 | 配置名称 |
| split_type | String | 是 | 拆分方式 |
| split_value | String | 是 | 拆分值(逗号分隔) |
| split_name | String | 否 | 拆分显示名称 |
| selected_fields | Array | 是 | 选择的字段列表 |
| sum_fields | Array | 否 | 求和字段列表 |
| time_periods | Object | 否 | 分时段电量配置 |
| merge_telecom | Boolean | 否 | 是否合并特来电数据 |
| telecom_vehicle_no | String | 否 | 特来电车量自编号 |
| field_custom_names | Object | 否 | 字段自定义表头名称 |
| show_monthly_total | Boolean | 否 | 是否显示月度总计 |
| custom_service_fee_price | Float | 否 | 自定义服务费单价(元/kWh) |
| custom_service_fee_name | String | 否 | 自定义服务费表头名称 |
| show_custom_service_fee | Boolean | 否 | 是否显示自定义服务费列 |
| show_total_amount | Boolean | 否 | 是否显示实收总金额列 |
| total_amount_name | String | 否 | 实收总金额表头名称 |
### 3.2 查询配置列表
**接口地址**: `GET /api/config`
**请求参数**: 无
### 3.3 查询单个配置
**接口地址**: `GET /api/config/<id>`
**请求参数**: 无
### 3.4 更新配置
**接口地址**: `PUT /api/config/<id>`
**请求参数**: 同创建配置
### 3.5 删除配置
**接口地址**: `DELETE /api/config/<id>`
**请求参数**: 无
### 3.6 复制配置
**接口地址**: `POST /api/config/<id>/copy`
**请求参数**: 无
### 3.7 启用/禁用配置
**接口地址**: `POST /api/config/<id>/toggle`
**请求参数**: 无
### 3.8 配置排序
**接口地址**: `POST /api/config/<id>/move`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| direction | String | 是 | 移动方向up/down |
## 四、认证接口
### 4.1 登录
**接口地址**: `POST /api/auth/login`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| username | String | 是 | 用户名 |
| password | String | 是 | 密码 |
### 4.2 获取用户信息
**接口地址**: `GET /api/auth/info`
**请求参数**: 无
### 4.3 修改密码
**接口地址**: `POST /api/auth/change-password`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| old_password | String | 是 | 旧密码 |
| new_password | String | 是 | 新密码 |
| confirm_password | String | 是 | 确认新密码 |
## 五、实体管理接口
### 5.1 查询企业列表
**接口地址**: `GET /api/entities/company`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| page | Integer | 否 | 页码 |
| page_size | Integer | 否 | 每页大小 |
| keyword | String | 否 | 搜索关键字 |
### 5.2 查询用户列表
**接口地址**: `GET /api/entities/user`
**请求参数**: 同企业列表
### 5.3 查询场站列表
**接口地址**: `GET /api/entities/station`
**请求参数**: 同企业列表
### 5.4 按ID批量查询实体
**接口地址**: `POST /api/entities/by_ids`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| type | String | 是 | 实体类型company/user/station |
| ids | Array | 是 | 实体ID列表 |
## 六、字段列表接口
### 6.1 获取字段列表
**接口地址**: `GET /api/fields`
**请求参数**: 无
## 七、日报生成接口
### 7.1 生成日报
**接口地址**: `POST /api/report/generate`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| config_id | Integer | 是 | 配置ID |
| report_date | String | 否 | 报表日期(默认昨天) |
### 7.2 查询历史记录
**接口地址**: `GET /api/report/history`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| page | Integer | 否 | 页码 |
| page_size | Integer | 否 | 每页大小 |
| start_date | String | 否 | 开始日期 |
| end_date | String | 否 | 结束日期 |
| config_name | String | 否 | 配置名称 |
| status | Integer | 否 | 状态0失败/1成功 |
### 7.3 删除历史记录
**接口地址**: `DELETE /api/report/history/<id>`
**请求参数**: 无
### 7.4 批量删除历史记录
**接口地址**: `POST /api/report/history/batch-delete`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | Array | 是 | 记录ID列表 |
## 八、文件下载接口
### 8.1 下载报表文件
**接口地址**: `GET /api/download/report/<file_name>`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| file_name | String | 是 | 文件名 |

176
docs/变更日志.md Normal file
View File

@@ -0,0 +1,176 @@
# 订单日报系统 变更日志
## 版本记录
### v1.0.0 (2026-07-28)
**新增功能**
1. **求和数据全局配置功能**
- 在"求和数据"页面添加"全局配置"按钮
- 创建全局配置表 `t_daily_report_global_config`
- 支持配置求和数据采集和显示的字段范围
- 充电电量charge_degree始终采集和显示
- 未配置时显示所有字段(向后兼容)
2. **新增全局配置API**
- `GET /api/global-config/sum-data-fields` - 获取配置
- `POST /api/global-config/sum-data-fields` - 保存配置
**修复问题**
1. **配置加载失败**
- 修复编辑配置时 `selectedFields is not defined` 错误
-`refreshSumDataFieldsList(selectedFields, sumDataFields)` 改为 `refreshSumDataFieldsList(config.selected_fields, sumDataFields)`
2. **求和数据显示字段过滤**
- 修改渲染逻辑,按全局配置过滤显示字段
- 在加载数据前先获取全局配置
- 页面加载时初始化全局配置
**重构优化**
1. **移除配置管理页面中的求和数据采集字段配置**
-`index.html` 中移除相关UI
-`config.js` 中移除相关逻辑
-`config_api.py` 中移除 `sum_data_fields` 参数处理
2. **修改求和数据采集逻辑**
- 从全局配置表读取字段配置
- 过滤采集字段,只采集配置的字段
**文件变更**
| 文件路径 | 变更类型 | 说明 |
|----------|----------|------|
| `lib/db_init.py` | 修改 | 新增全局配置表 `t_daily_report_global_config` |
| `lib/api/global_config_api.py` | 新增 | 全局配置API |
| `lib/api/__init__.py` | 修改 | 注册全局配置蓝图 |
| `lib/api/config_api.py` | 修改 | 移除 `sum_data_fields` 参数处理 |
| `lib/sum_data_collector.py` | 修改 | 从全局配置读取字段配置 |
| `templates/index.html` | 修改 | 移除配置管理页面的求和数据采集字段UI添加全局配置按钮和模态框 |
| `public/static/js/config.js` | 修改 | 移除求和数据采集字段相关逻辑 |
| `public/static/js/sum_data.js` | 修改 | 添加全局配置功能和字段过滤逻辑 |
### v0.9.0 (2026-07-27)
**新增功能**
1. **自定义服务费功能**
- 支持配置自定义服务费单价
- 支持配置自定义服务费表头名称
- 支持在报表中显示自定义服务费字段
2. **自定义实收总金额功能**
- 支持配置是否显示实收总金额字段
- 支持配置实收总金额表头名称
- 计算逻辑:实收电费 + 自定义服务费
3. **月度总计功能**
- 支持在报表底部显示当月充电量累计总计
- 数据来源于求和采集表
**文件变更**
| 文件路径 | 变更类型 | 说明 |
|----------|----------|------|
| `lib/db_init.py` | 修改 | 新增 `show_monthly_total`, `custom_service_fee_price`, `custom_service_fee_name`, `show_custom_service_fee`, `show_total_amount`, `total_amount_name` 字段 |
| `lib/api/config_api.py` | 修改 | 添加自定义服务费和实收总金额配置处理 |
| `lib/report_generator.py` | 修改 | 添加自定义服务费和实收总金额计算逻辑 |
| `templates/index.html` | 修改 | 添加自定义服务费和实收总金额配置UI |
| `public/static/js/config.js` | 修改 | 添加自定义服务费和实收总金额配置逻辑 |
### v0.8.0 (2026-07-20)
**新增功能**
1. **特来电数据合并**
- 支持合并特来电平台数据
- 支持配置特来电车量自编号
2. **分时段电量统计**
- 支持尖峰平谷分时段电量统计
- 支持自定义时段配置
3. **求和数据管理**
- 支持从历史记录采集求和数据
- 支持手动添加和编辑求和数据
- 支持导出求和数据
**文件变更**
| 文件路径 | 变更类型 | 说明 |
|----------|----------|------|
| `lib/db_init.py` | 修改 | 创建求和数据表 `t_daily_report_sum_data` |
| `lib/sum_data_collector.py` | 新增 | 求和数据采集和管理逻辑 |
| `lib/api/sum_data_api.py` | 新增 | 求和数据API |
| `templates/index.html` | 修改 | 添加求和数据页面和相关模态框 |
| `public/static/js/sum_data.js` | 新增 | 求和数据前端逻辑 |
### v0.7.0 (2026-07-15)
**新增功能**
1. **配置管理**
- 支持创建、编辑、删除配置
- 支持配置复制
- 支持启用/禁用配置
- 支持配置排序
2. **实体管理**
- 支持企业、用户、场站列表查询
- 支持分页和搜索
3. **日报生成**
- 支持手动生成日报
- 支持定时任务自动生成
4. **历史记录**
- 支持查看生成历史
- 支持下载报表文件
- 支持批量删除
**文件变更**
| 文件路径 | 变更类型 | 说明 |
|----------|----------|------|
| `app.py` | 新增 | Flask 主应用 |
| `lib/db.py` | 新增 | 数据库连接配置 |
| `lib/db_init.py` | 新增 | 数据库初始化 |
| `lib/field_mapping.py` | 新增 | 字段映射定义 |
| `lib/report_generator.py` | 新增 | 日报生成核心逻辑 |
| `lib/api/config_api.py` | 新增 | 配置管理API |
| `lib/api/entity_api.py` | 新增 | 实体管理API |
| `lib/api/field_api.py` | 新增 | 字段列表API |
| `lib/api/report_api.py` | 新增 | 日报生成和历史记录API |
| `lib/api/download_api.py` | 新增 | 文件下载API |
| `lib/api/auth_api.py` | 新增 | 认证API |
| `templates/index.html` | 新增 | 主页面 |
| `public/static/css/style.css` | 新增 | 全局样式 |
| `public/static/js/common.js` | 新增 | 通用工具函数 |
| `public/static/js/config.js` | 新增 | 配置管理逻辑 |
| `public/static/js/entity.js` | 新增 | 实体管理逻辑 |
| `public/static/js/report.js` | 新增 | 日报生成逻辑 |
| `public/static/js/history.js` | 新增 | 历史记录逻辑 |
## 版本说明
### 版本号规则
- **主版本号**:重大功能变更或架构调整
- **次版本号**:新增功能或修复问题
- **修订号**:小修复和优化
### 向后兼容性
- v1.0.0 与 v0.9.0 完全兼容
- v0.9.0 与 v0.8.0 完全兼容
- v0.8.0 与 v0.7.0 完全兼容
### 数据库迁移
系统启动时会自动执行数据库迁移,无需手动操作:
1. 创建缺失的表
2. 添加缺失的字段
3. 初始化全局配置记录(如果不存在)

View File

@@ -0,0 +1,377 @@
# 订单日报系统 数据库变更与部署文档
## 一、数据库变更说明
### 1.1 新增表t_daily_report_global_config
**表结构**
```sql
CREATE TABLE IF NOT EXISTS t_daily_report_global_config (
id BIGINT NOT NULL DEFAULT 1 COMMENT '主键固定为1',
sum_data_fields VARCHAR(1000) NULL COMMENT '求和数据采集和显示字段列表(JSON格式)',
create_time DATETIME NULL COMMENT '创建时间',
update_time DATETIME NULL COMMENT '更新时间'
)
UNIQUE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES("replication_num" = "1")
```
**字段说明**
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | BIGINT | 主键固定为1全局配置只有一条记录 |
| sum_data_fields | VARCHAR(1000) | 求和数据采集和显示字段列表JSON格式 |
| create_time | DATETIME | 创建时间 |
| update_time | DATETIME | 更新时间 |
**数据示例**
```sql
INSERT INTO t_daily_report_global_config (id, sum_data_fields, create_time, update_time)
VALUES (1, '["sharp_electricity","peak_electricity","flat_electricity","valley_electricity"]', NOW(), NOW());
```
### 1.2 自动初始化
系统启动时会自动执行以下操作:
1. 检查表是否存在,不存在则创建
2. 检查ID=1的记录是否存在不存在则初始化一条空记录
**相关代码**[db_init.py](file:///d:/py/code/ylt_power_diy/ylt_diy/lib/db_init.py#L186-L211)
## 二、部署步骤
### 2.1 开发环境部署
**前置条件**
- Python 3.12+
- MySQL/Apache Doris 数据库
- Git
**步骤**
1. **克隆代码**
```bash
git clone <仓库地址>
cd ylt_diy
```
2. **安装依赖**
```bash
pip install -r requirements.txt
```
3. **配置环境变量**(可选)
```bash
# 设置数据库连接(默认值见下文)
export DB_HOST=haoslm2.xicp.net
export DB_PORT=10216
export DB_USER=root
export DB_PASSWORD=your_password
export DB_DATABASE=ylt_power_diy
export DEPLOY_RUN_PORT=5000
```
4. **启动开发服务器**
```bash
python app.py
```
5. **访问系统**
打开浏览器访问http://localhost:5000
### 2.2 生产环境部署
**前置条件**
- Linux 服务器(推荐 CentOS 7+
- Python 3.12+
- MySQL/Apache Doris 数据库
- Nginx可选用于反向代理
**步骤**
1. **克隆代码**
```bash
git clone <仓库地址>
cd ylt_diy
```
2. **安装依赖**
```bash
pip install -r requirements.txt
```
3. **配置环境变量**
创建 `.env` 文件:
```bash
DEPLOY_RUN_PORT=5000
DB_HOST=haoslm2.xicp.net
DB_PORT=10216
DB_USER=root
DB_PASSWORD=your_password
DB_DATABASE=ylt_power_diy
```
4. **创建服务配置**
创建 `/etc/systemd/system/ylt_daily_report.service`
```ini
[Unit]
Description=订单日报系统
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/path/to/ylt_diy
Environment="DEPLOY_RUN_PORT=5000"
Environment="DB_HOST=haoslm2.xicp.net"
Environment="DB_PORT=10216"
Environment="DB_USER=root"
Environment="DB_PASSWORD=your_password"
Environment="DB_DATABASE=ylt_power_diy"
ExecStart=/usr/bin/python3 app.py
Restart=always
[Install]
WantedBy=multi-user.target
```
5. **启动服务**
```bash
systemctl daemon-reload
systemctl start ylt_daily_report
systemctl enable ylt_daily_report
```
6. **配置 Nginx 反向代理**(可选)
创建 `/etc/nginx/conf.d/ylt_daily_report.conf`
```nginx
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /public/ {
root /path/to/ylt_diy;
expires 30d;
}
}
```
7. **重启 Nginx**
```bash
nginx -t
systemctl restart nginx
```
### 2.3 数据库配置
**默认配置**
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| DB_HOST | haoslm2.xicp.net | 数据库主机地址 |
| DB_PORT | 10216 | 数据库端口 |
| DB_USER | root | 数据库用户名 |
| DB_PASSWORD | (空) | 数据库密码 |
| DB_DATABASE | ylt_power_diy | 数据库名 |
**连接方式**
系统使用 PyMySQL 连接数据库,支持 MySQL 和 Apache DorisMySQL 协议兼容)。
**相关代码**[db.py](file:///d:/py/code/ylt_power_diy/ylt_diy/lib/db.py)
## 三、版本升级
### 3.1 升级步骤
1. **拉取最新代码**
```bash
cd /path/to/ylt_diy
git pull
```
2. **安装新依赖**(如果有)
```bash
pip install -r requirements.txt
```
3. **重启服务**
```bash
systemctl restart ylt_daily_report
```
### 3.2 数据库自动迁移
系统启动时会自动执行数据库初始化,包括:
1. 创建缺失的表
2. 添加缺失的字段
3. 初始化全局配置记录
**注意**:如果出现数据库迁移失败,请检查数据库连接和权限。
## 四、备份与恢复
### 4.1 数据库备份
```bash
# 使用 mysqldump 备份
mysqldump -h haoslm2.xicp.net -P 10216 -u root -p ylt_power_diy > backup.sql
# 使用 Doris 备份命令(如果使用 Doris
curl -X POST http://doris-be:8030/api/bootstrap -d '{"type":"backup","backup_name":"daily_report_backup","storage":"hdfs://your-hdfs/path"}'
```
### 4.2 数据库恢复
```bash
# 使用 mysql 命令恢复
mysql -h haoslm2.xicp.net -P 10216 -u root -p ylt_power_diy < backup.sql
```
### 4.3 文件备份
```bash
# 备份报表文件
tar -czvf reports_backup.tar.gz public/reports/
# 备份配置文件
tar -czvf config_backup.tar.gz lib/
```
## 五、常见部署问题
### 5.1 数据库连接失败
**问题**:系统启动时提示数据库连接失败
**解决**
- 检查数据库主机和端口是否正确
- 确认数据库服务正常运行
- 检查数据库用户名和密码
- 确认网络连通性ping 数据库主机)
### 5.2 端口被占用
**问题**:启动时提示端口 5000 被占用
**解决**
```bash
# 查看占用进程
lsof -i :5000
# 杀死占用进程
kill -9 <PID>
# 或修改端口
export DEPLOY_RUN_PORT=5001
```
### 5.3 静态文件加载失败
**问题**:页面样式和脚本无法加载
**解决**
- 检查 `public/static/` 目录是否存在
- 确认 Flask 应用的 `static_folder` 配置正确
- 清除浏览器缓存Ctrl + Shift + R
### 5.4 定时任务不执行
**问题**:每天 08:01 的定时任务没有执行
**解决**
- 确认服务正常运行(`systemctl status ylt_daily_report`
- 检查日志文件(`/app/work/logs/bypass/`
- 确认系统时间正确
- 检查配置是否启用(`is_active = 1`
### 5.5 内存不足
**问题**:服务运行一段时间后崩溃
**解决**
- 增加服务器内存
- 优化查询语句,减少内存使用
- 配置定时清理任务
## 六、日志管理
### 6.1 日志位置
- 应用日志:`/app/work/logs/bypass/`
- 系统日志:`/var/log/syslog`Linux
### 6.2 日志级别
- INFO普通信息
- WARNING警告信息
- ERROR错误信息
### 6.3 查看日志
```bash
# 查看应用日志
tail -f /app/work/logs/bypass/app.log
# 查看系统日志
journalctl -u ylt_daily_report -f
```
## 七、安全建议
### 7.1 数据库安全
- 使用强密码
- 限制数据库访问 IP
- 定期备份数据库
### 7.2 应用安全
- 使用 HTTPS
- 定期更新依赖
- 关闭调试模式(生产环境)
- 配置防火墙
### 7.3 服务器安全
- 禁用 SSH 密码登录(使用密钥)
- 定期更新系统
- 禁用不必要的服务

236
docs/用户使用说明.md Normal file
View File

@@ -0,0 +1,236 @@
# 订单日报系统 用户使用说明
## 一、系统概述
订单日报系统是一个基于 Flask 的 Web 应用,用于自动生成充电订单的日报报表。系统支持按企业/用户/场站拆分数据,自定义字段和求和功能,并支持合并特来电平台数据。
## 二、登录系统
1. 打开浏览器访问系统地址http://localhost:5000
2. 在登录页面输入用户名和密码
3. 点击"登录"按钮进入系统
## 三、配置管理
### 3.1 创建配置
1. 在"配置管理"页面点击"创建配置"按钮
2. 填写配置名称西南79路
3. 选择拆分方式:
- 按企业按企业ID拆分数据
- 按用户按用户ID拆分数据
- 按场站按场站ID拆分数据
4. 搜索并选择拆分对象(可多选)
5. 在"选择字段"区域勾选需要在报表中显示的字段
6. (可选)在"字段顺序"区域调整字段显示顺序和自定义表头名称
7. (可选)勾选"合并特来电平台数据"并输入车量自编号
8. (可选)配置分时段电量统计(尖峰平谷)
9. (可选)配置自定义服务费和实收总金额
10. 点击"保存"按钮完成创建
### 3.2 编辑配置
1. 在配置列表中找到目标配置
2. 点击"编辑"按钮
3. 修改需要调整的配置项
4. 点击"保存"按钮
### 3.3 复制配置
1. 在配置列表中找到目标配置
2. 点击"复制"按钮
3. 系统会自动创建一个"配置名_副本"的新配置
4. 可根据需要修改新配置
### 3.4 启用/禁用配置
1. 在配置列表中找到目标配置
2. 点击"启用"或"禁用"按钮
3. 禁用的配置不会参与定时任务自动生成
### 3.5 删除配置
1. 在配置列表中找到目标配置
2. 点击"删除"按钮
3. 确认删除操作
## 四、日报生成
### 4.1 手动生成日报
1. 切换到"日报生成"页面
2. 选择需要生成的配置(可多选)
3. 选择报表日期(默认昨天)
4. 点击"生成日报"按钮
5. 等待生成完成,可查看生成结果
### 4.2 定时任务
系统每天 08:01 自动执行日报生成任务,遍历所有启用的配置逐一生成日报。
## 五、历史记录
### 5.1 查看历史记录
1. 切换到"历史记录"页面
2. 可按日期范围和配置名称筛选
3. 查看每条记录的状态(成功/失败)、订单数、总电量等信息
### 5.2 下载报表
1. 在历史记录列表中找到目标记录
2. 点击"下载"按钮
3. 浏览器会自动下载报表文件
### 5.3 删除历史记录
1. 在历史记录列表中找到目标记录
2. 点击"删除"按钮,或勾选多条记录后点击"批量删除"
3. 确认删除操作
## 六、求和数据
### 6.1 查看求和数据
1. 切换到"求和数据"页面
2. 可按日期范围、配置名称、数据来源筛选
3. 表格显示各配置的每日求和数据,包括:
- 充电电量(必选字段)
- 分时电量(尖、峰、平、谷)
- 其他配置的求和字段
### 6.2 从历史采集
1. 在"求和数据"页面选择日期范围
2. 点击"从历史采集"按钮
3. 确认操作后,系统会从历史记录中采集求和数据
4. **注意**:会先清除该日期范围内旧的自动采集数据
### 6.3 手动添加
1. 点击"手动添加"按钮
2. 填写报表日期、配置名称、拆分方式、拆分值等信息
3. 输入各求和字段的值(至少输入一个)
4. (可选)填写备注/修改原因
5. 点击"保存"按钮
### 6.4 编辑求和数据
1. 在求和数据列表中找到目标记录
2. 点击"编辑"按钮
3. 修改求和字段的值
4. (可选)填写修改原因
5. 点击"保存"按钮
### 6.5 删除求和数据
1. 在求和数据列表中找到目标记录
2. 点击"删除"按钮,或勾选多条记录后点击"批量删除"
3. 确认删除操作
### 6.6 导出求和数据
1. 在"求和数据"页面选择筛选条件
2. 点击"导出"按钮
3. 系统会生成 Excel 文件并下载
### 6.7 全局配置(重要)
**功能说明**:控制求和数据采集和显示的字段范围,避免采集和显示不需要的数据。
**操作步骤**
1. 在"求和数据"页面点击"全局配置"按钮
2. 在弹出的配置窗口中,勾选需要采集和显示的字段
3. **注意**充电电量charge_degree是必选字段始终采集和显示
4. 取消勾选不需要的字段(如电费金额、服务费金额等)
5. 点击"保存"按钮
6. 页面会自动刷新,只显示勾选的字段
**可配置字段**
| 字段名称 | 说明 |
|----------|------|
| 尖时电量(kWh) | 尖时段充电电量 |
| 峰时电量(kWh) | 峰时段充电电量 |
| 平时电量(kWh) | 平时段充电电量 |
| 谷时电量(kWh) | 谷时段充电电量 |
| 实收电费(元) | 实际收取的电费金额 |
| 实收服务费(元) | 实际收取的服务费金额 |
| 实收总金额(元) | 实际收取的总金额(电费+服务费) |
| 充电次数 | 充电订单次数 |
| 自定义服务费(元) | 按自定义单价计算的服务费 |
| 自定义实收总金额(元) | 实收电费+自定义服务费 |
**配置生效范围**
- **采集时**:从历史记录采集数据时,只采集勾选的字段
- **显示时**:求和数据页面只显示勾选的字段
- **向后兼容**:如果没有配置任何字段,则采集和显示所有字段
## 七、常见问题
### 7.1 配置加载失败
**问题**:编辑配置时提示"加载配置失败: selectedFields is not defined"
**解决**:系统已修复此问题,请刷新页面重试。
### 7.2 报表生成失败
**问题**:生成日报时提示错误
**解决**
- 检查配置是否正确(拆分方式、拆分值、字段选择等)
- 确认订单数据存在
- 查看日志文件(/app/work/logs/bypass/
### 7.3 求和数据显示字段过多
**问题**:求和数据页面显示了不需要的字段
**解决**
- 点击"全局配置"按钮
- 取消勾选不需要的字段
- 点击"保存"按钮
### 7.4 充电电量显示为0或空
**问题**求和数据中充电电量为0或显示"-"
**解决**
- 检查对应日期是否有充电订单数据
- 尝试重新从历史采集数据
### 7.5 手动添加的数据不显示
**问题**:手动添加求和数据后,页面不显示
**解决**
- 确认添加操作成功(弹出"添加成功"提示)
- 刷新页面查看
- 检查筛选条件是否正确
### 7.6 配置名称搜索自动填充用户名
**问题**:配置名称搜索框自动填充了登录用户名
**解决**:搜索框已设置 `autocomplete="off"`,请清除浏览器缓存后重试。
## 八、快捷键
| 快捷键 | 功能 |
|--------|------|
| Ctrl + Enter | 搜索(在搜索框中) |
## 九、系统要求
### 浏览器要求
- Chrome 90+
- Firefox 88+
- Edge 90+
### 分辨率要求
- 推荐分辨率1920×1080 及以上
- 最小分辨率1280×720

View File

@@ -177,6 +177,9 @@ FIELD_MAPPING = {
# 车辆信息(从 t_car 表关联)
'car_bus_path': '公交线路',
'car_sn': '车辆编码',
# 充电卡信息(从 t_user_card 表关联)
'user_card_memo': '充电卡备注',
}
# 获取字段中文名称

View File

@@ -350,10 +350,10 @@ def generate_daily_report(config_id, start_time=None, end_time=None):
report_date = start_time.strftime('%Y-%m-%d')
delete_old_history(config_id, report_date, config['split_type'], config['split_value'])
# 构建查询SQL - 过滤掉虚拟字段(时段电量、自定义服务费、自定义实收总金额、车辆信息等)
# 构建查询SQL - 过滤掉虚拟字段(时段电量、自定义服务费、自定义实收总金额、车辆信息、充电卡信息等)
# 虚拟字段不是数据库表中的实际字段,需要动态计算或关联查询
# 注意total_amount 是旧版虚拟字段名,为了向后兼容也需要包含在内
VIRTUAL_FIELDS = {'sharp_electricity', 'peak_electricity', 'flat_electricity', 'valley_electricity', 'custom_service_fee', 'custom_total_amount', 'total_amount', 'car_bus_path', 'car_sn'}
VIRTUAL_FIELDS = {'sharp_electricity', 'peak_electricity', 'flat_electricity', 'valley_electricity', 'custom_service_fee', 'custom_total_amount', 'total_amount', 'car_bus_path', 'car_sn', 'user_card_memo'}
db_fields = [f for f in selected_fields if f not in VIRTUAL_FIELDS]
# 确保 charge_degree 总是被查询(用于计算总电量)
@@ -373,6 +373,11 @@ def generate_daily_report(config_id, start_time=None, end_time=None):
if need_car_info and 'charge_vin' not in db_fields:
db_fields.append('charge_vin')
# 如果选择了充电卡备注字段,需要确保查询 user_card_no用于关联 t_user_card 表)
need_card_memo = 'user_card_memo' in selected_fields
if need_card_memo and 'user_card_no' not in db_fields:
db_fields.append('user_card_no')
fields_str = ', '.join(db_fields)
# 检查是否为多选值(逗号分隔)
@@ -447,6 +452,30 @@ def generate_daily_report(config_id, start_time=None, end_time=None):
order['car_sn'] = ''
log_info(f"[日报生成] 关联查询到 {len(car_map)} 条车辆信息", 'report')
# 如果需要充电卡信息,从 t_user_card 表关联查询
if need_card_memo and orders:
# 获取所有充电卡号
card_no_list = list(set(order.get('user_card_no') for order in orders if order.get('user_card_no')))
if card_no_list:
placeholders = ', '.join(['%s'] * len(card_no_list))
card_sql = f"""
SELECT card_no, memo
FROM t_user_card
WHERE card_no IN ({placeholders})
"""
card_results = execute_query(card_sql, tuple(card_no_list))
card_map = {card['card_no']: card for card in card_results}
# 将充电卡备注添加到订单中
for order in orders:
card_no = order.get('user_card_no')
if card_no and card_no in card_map:
card_info = card_map[card_no]
order['user_card_memo'] = card_info.get('memo', '')
else:
order['user_card_memo'] = ''
log_info(f"[日报生成] 关联查询到 {len(card_map)} 条充电卡信息", 'report')
# 检查是否有补单记录finish_type = 2 表示补单结束)
has_supplement = any(order.get('finish_type') == 2 for order in orders)
if has_supplement:
@@ -472,6 +501,52 @@ def generate_daily_report(config_id, start_time=None, end_time=None):
# 检查特来电数据中是否有补单记录
if not has_supplement:
has_supplement = any(order.get('finish_type') == 2 for order in telecom_orders)
# 合并后再次处理车辆信息(针对特来电订单)
if need_car_info:
telecom_vin_list = list(set(order.get('charge_vin') for order in telecom_orders if order.get('charge_vin')))
if telecom_vin_list:
placeholders = ', '.join(['%s'] * len(telecom_vin_list))
car_sql = f"""
SELECT car_vin, car_bus_path, car_sn
FROM t_car
WHERE car_vin IN ({placeholders})
"""
car_results = execute_query(car_sql, tuple(telecom_vin_list))
car_map = {car['car_vin']: car for car in car_results}
for order in telecom_orders:
vin = order.get('charge_vin')
if vin and vin in car_map:
car_info = car_map[vin]
order['car_bus_path'] = car_info.get('car_bus_path', '')
order['car_sn'] = car_info.get('car_sn', '')
else:
order['car_bus_path'] = ''
order['car_sn'] = ''
log_info(f"[日报生成] 特来电数据关联查询到 {len(car_map)} 条车辆信息", 'report')
# 合并后再次处理充电卡信息(针对特来电订单)
if need_card_memo:
telecom_card_no_list = list(set(order.get('user_card_no') for order in telecom_orders if order.get('user_card_no')))
if telecom_card_no_list:
placeholders = ', '.join(['%s'] * len(telecom_card_no_list))
card_sql = f"""
SELECT card_no, memo
FROM t_user_card
WHERE card_no IN ({placeholders})
"""
card_results = execute_query(card_sql, tuple(telecom_card_no_list))
card_map = {card['card_no']: card for card in card_results}
for order in telecom_orders:
card_no = order.get('user_card_no')
if card_no and card_no in card_map:
card_info = card_map[card_no]
order['user_card_memo'] = card_info.get('memo', '')
else:
order['user_card_memo'] = ''
log_info(f"[日报生成] 特来电数据关联查询到 {len(card_map)} 条充电卡信息", 'report')
if not orders:
# 即使没有数据,也保存一条失败记录