|
@@ -3,7 +3,7 @@ from datetime import datetime
|
|
|
|
|
|
import utils
|
|
|
from models.process_data import ProcessData
|
|
|
-from models.process_result_data import ProcessResultData
|
|
|
+from models.process_result_data import ProcessResultData, InstrumentData
|
|
|
from stores.data_store_interface import IDataStore
|
|
|
|
|
|
|
|
@@ -58,12 +58,12 @@ class DataSend:
|
|
|
utils.get_logger().info("发送中标报告邮件成功")
|
|
|
|
|
|
def _send_item(self, item: ProcessData) -> None:
|
|
|
- utils.get_logger().info(f"开始发送邮件,地区为:{item.area} ,URL为 {item.url}")
|
|
|
- email = self._get_email_by_area(item.area)
|
|
|
+ utils.get_logger().info(f"开始发送邮件,地区为:{item.city} ,URL为 {item.url}")
|
|
|
+ email = self._get_email_by_area(item.city)
|
|
|
if not email:
|
|
|
- utils.get_logger().error(f"{item.area} 下没有找到email")
|
|
|
- if item.area not in self._error_arr:
|
|
|
- self._error_arr.append(item.area)
|
|
|
+ utils.get_logger().error(f"{item.city} 下没有找到email")
|
|
|
+ if item.city not in self._error_arr:
|
|
|
+ self._error_arr.append(item.city)
|
|
|
return
|
|
|
title_prev = utils.get_config_value("email.title_prev", "【招标信息】")
|
|
|
body = self._build_email_html(item)
|
|
@@ -77,13 +77,9 @@ class DataSend:
|
|
|
self, area: str, count: int = 0, virtual_area: str = None
|
|
|
) -> str:
|
|
|
email = None
|
|
|
- area_str = area
|
|
|
- # if "省" in area:
|
|
|
- # area_str_arr = area.split("省")
|
|
|
- # area_str = area_str_arr[1] if len(area_str) > 1 else area_str_arr[0]
|
|
|
- # if "市" in area:
|
|
|
- # area_str_arr = area.split("市")
|
|
|
- # area_str = area_str_arr[0]
|
|
|
+ area_str = (
|
|
|
+ area.replace("省", "").replace("市", "").replace("区", "").replace("县", "")
|
|
|
+ )
|
|
|
for area_item in self._email_area_arr:
|
|
|
if area_str in area_item.area:
|
|
|
email = area_item.email
|
|
@@ -159,11 +155,13 @@ class DataSend:
|
|
|
<body>
|
|
|
<div class="container">
|
|
|
<h1>{item.title}</h1>
|
|
|
- <p><strong>发布日期:</strong> {item.release_date}</p>
|
|
|
- <p><strong>招标编号:</strong> {item.no}</p>
|
|
|
- <p><strong>开标时间:</strong> {item.date}</p>
|
|
|
- <p><strong>开标地点:</strong> {item.address}</p>
|
|
|
- <p><strong>标书摘要:</strong> {item.summary}</p>
|
|
|
+ <p><strong>招标编号:</strong> {item.no if item.no else ""}</p>
|
|
|
+ <p><strong>项目区域:</strong> {item.provice if item.provice else ""}{item.city if item.city else ""}</p>
|
|
|
+ <p><strong>相关设备:</strong> {item.devices if item.devices else ""}</p>
|
|
|
+ <p><strong>开标时间:</strong> {item.date if item.date else ""}</p>
|
|
|
+ <p><strong>开标地点:</strong> {item.address if item.address else ""}</p>
|
|
|
+ <p><strong>发布日期:</strong> {item.release_date if item.release_date else ""}</p>
|
|
|
+ <p><strong>标书摘要:</strong> {item.summary if item.summary else ""}</p>
|
|
|
<div class="button-container">
|
|
|
<a href="{item.url}" class="button">查看详情</a>
|
|
|
</div>
|
|
@@ -195,7 +193,7 @@ class DataSend:
|
|
|
color: #333;
|
|
|
}}
|
|
|
.container {{
|
|
|
- max-width: 1000px;
|
|
|
+ max-width: 1200px;
|
|
|
margin: 0 auto;
|
|
|
background-color: #fff;
|
|
|
padding: 20px;
|
|
@@ -211,7 +209,7 @@ class DataSend:
|
|
|
width: 100%;
|
|
|
}}
|
|
|
.table {{
|
|
|
- width: 1000px;
|
|
|
+ width: 1200px;
|
|
|
background-color: #ffffff;
|
|
|
border: 1px solid #dddddd;
|
|
|
border-radius: 8px;
|
|
@@ -227,9 +225,10 @@ class DataSend:
|
|
|
text-align: center;
|
|
|
font-size:12px;
|
|
|
}}
|
|
|
- .table th:not(:first-child), .table td:not(:first-child) {{
|
|
|
+ .table th:not(:first-child), .table td:not(:first-child) ,.table tr.instrument-row th,.table tr.instrument-row td{{
|
|
|
border-left: 1px solid #dddddd;
|
|
|
}}
|
|
|
+
|
|
|
.table th {{
|
|
|
padding: 10px;
|
|
|
background-color: #f8f9fa;
|
|
@@ -255,8 +254,7 @@ class DataSend:
|
|
|
"""
|
|
|
return html
|
|
|
|
|
|
- @staticmethod
|
|
|
- def _build_report_email_body(items: list[ProcessResultData]) -> str:
|
|
|
+ def _build_report_email_body(self, items: list[ProcessResultData]) -> str:
|
|
|
if not items:
|
|
|
return ""
|
|
|
|
|
@@ -264,40 +262,102 @@ class DataSend:
|
|
|
<div class="table-container">
|
|
|
<table class="table">
|
|
|
<tr>
|
|
|
- <th style="width:250px">项目名称</th>
|
|
|
- <th>设备</th>
|
|
|
- <th style="width:150px">公告日期</th>
|
|
|
- <th style="width:120px">价格</th>
|
|
|
+ <th style="width:200px" rowspan="2">项目名称</th>
|
|
|
+ <th style="width:100px" rowspan="2">地区</th>
|
|
|
+ <th colspan="6" style="width:800px">中标设备</th>
|
|
|
+ <th style="width:100px" rowspan="2">公告日期</th>
|
|
|
+ </tr>
|
|
|
+ <tr class='instrument-row'>
|
|
|
+ <th style="180px">仪器名称</th>
|
|
|
+ <th style="200px">仪器厂商</th>
|
|
|
+ <th style="130px">仪器型号</th>
|
|
|
+ <th style="80px">数量</th>
|
|
|
+ <th style="140px">单价(元)</th>
|
|
|
+ <th>中标单位</th>
|
|
|
</tr>
|
|
|
"""
|
|
|
for item in items:
|
|
|
- body += f"""
|
|
|
- <tr>
|
|
|
- <td><a title="点击查看详情" href="{item.url}">{item.title}</a></td>
|
|
|
- <td>{item.devices}</td>
|
|
|
- <td>{item.date}</td>
|
|
|
- <td>{item.price}</td>
|
|
|
- </tr>
|
|
|
- """
|
|
|
+ body += self._gen_report_body_item(item)
|
|
|
body += "</table></div>"
|
|
|
return body
|
|
|
|
|
|
- @staticmethod
|
|
|
- def _gen_report_exlecl(title, items: list[ProcessResultData]) -> str:
|
|
|
- if not items:
|
|
|
+ def _gen_report_body_item(self, item: ProcessResultData):
|
|
|
+ if not item:
|
|
|
return ""
|
|
|
- # 将 list 数据转换为 DataFrame
|
|
|
- data = {
|
|
|
- "项目编号": [item.no for item in items],
|
|
|
- "项目名称": [item.title for item in items],
|
|
|
- "公告日期": [item.date for item in items],
|
|
|
- "相关设备": [item.devices for item in items],
|
|
|
- "价格": [item.price for item in items],
|
|
|
- "中标人": [item.bidder for item in items],
|
|
|
- "公告摘要": [item.summary for item in items],
|
|
|
- "URL": [item.url for item in items],
|
|
|
+ row_count = len(item.instruments) if item.instruments else 1
|
|
|
+ html = f"""
|
|
|
+ <tr>
|
|
|
+ <td rowspan="{row_count}"><a title="点击查看详情" href="{item.url}">{item.title}</a></td>
|
|
|
+ <td rowspan="{row_count}">{item.provice if item.provice else ''}{item.city if item.city else ''}</td>
|
|
|
+ {self._gen_report_body_item_instrument(item.instruments[0] if item.instruments else None)}
|
|
|
+ <td rowspan="{row_count}">{item.date if item.date else ''}</td>
|
|
|
+ </tr>
|
|
|
+ """
|
|
|
+ if row_count > 1:
|
|
|
+ for instrument in item.instruments[1:]:
|
|
|
+ html += f"""
|
|
|
+ <tr class="instrument-row">
|
|
|
+ {self._gen_report_body_item_instrument(instrument)}
|
|
|
+ </tr>
|
|
|
+ """
|
|
|
+ return html
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def _gen_report_body_item_instrument(instrument):
|
|
|
+ if not instrument:
|
|
|
+ return '<td colspan="6">无设备信息</td>'
|
|
|
+ return f"""
|
|
|
+ <td>{instrument.name if instrument.name else ''}</td>
|
|
|
+ <td>{instrument.manufacturer if instrument.manufacturer else ''}</td>
|
|
|
+ <td>{instrument.model if instrument.model else ''}</td>
|
|
|
+ <td>{instrument.quantity if instrument.quantity else ''}</td>
|
|
|
+ <td>{instrument.unit_price if instrument.unit_price else ''}</td>
|
|
|
+ <td>{instrument.company if instrument.company else ''}</td>
|
|
|
+ """
|
|
|
+
|
|
|
+ def _gen_report_exlecl(self, title, items: list[ProcessResultData]) -> str:
|
|
|
+ all_data = []
|
|
|
+
|
|
|
+ for item in items:
|
|
|
+ if item.instruments:
|
|
|
+ # 获取第一台仪器的数据
|
|
|
+ first_instrument = item.instruments[0]
|
|
|
+ all_data.append(self._gen_report_row_data(item, first_instrument))
|
|
|
+
|
|
|
+ # 处理剩余的仪器
|
|
|
+ for instrument in item.instruments[1:]:
|
|
|
+ all_data.append(self._gen_report_row_data(None, instrument))
|
|
|
+ else:
|
|
|
+ # 如果没有仪器,只添加 ProcessResultData 的字段
|
|
|
+ all_data.append(self._gen_report_row_data(item, None))
|
|
|
+
|
|
|
+ return utils.save_reort_excel(all_data, title)
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def _gen_report_row_data(
|
|
|
+ data: ProcessResultData | None, instrument: InstrumentData | None
|
|
|
+ ):
|
|
|
+ return {
|
|
|
+ "项目编号": data.no if data and data.no else "",
|
|
|
+ "项目名称": data.title if data and data.title else "",
|
|
|
+ "公告日期": data.date if data and data.date else "",
|
|
|
+ "招标省份": data.provice if data and data.provice else "",
|
|
|
+ "招标城市": data.city if data and data.city else "",
|
|
|
+ "中标单位名称": instrument.company if instrument.company else "",
|
|
|
+ "仪器名称": instrument.name if instrument and instrument.name else "",
|
|
|
+ "仪器厂商": (
|
|
|
+ instrument.manufacturer
|
|
|
+ if instrument and instrument.manufacturer
|
|
|
+ else ""
|
|
|
+ ),
|
|
|
+ "仪器型号": instrument.model if instrument and instrument.model else "",
|
|
|
+ "数量": instrument.quantity if instrument and instrument.quantity else "",
|
|
|
+ "单价": (
|
|
|
+ instrument.unit_price if instrument and instrument.unit_price else ""
|
|
|
+ ),
|
|
|
+ "公告摘要": data.summary if data and data.summary else "",
|
|
|
+ "URL": data.url if data and data.url else "",
|
|
|
}
|
|
|
- return utils.save_reort_excel(data, title)
|
|
|
|
|
|
def _send_email_no_found(self) -> None:
|
|
|
email = utils.get_config_value("email.error_email")
|