2 Commits 28e42242d5 ... 28e43c4f8c

Author SHA1 Message Date
  YueYunyun 28e43c4f8c update 不更新虚拟区域到真实邮箱的区域上,方便以后区域调整的维护 2 weeks ago
  YueYunyun e02dae5818 Add 增加提取招标信息的预算金额功能 2 weeks ago

+ 2 - 1
SourceCode/TenderCrawler/README.md

@@ -115,8 +115,9 @@ email:
   smtp_password: 'password' # SMTP密码
   from_email: 'from@example.com' # 发件人地址
   error_email: 'error@example.com' # 错误通知邮箱
+  default_email: 'default@example.com' # 默认收件人地址,所有业务邮件都发送到该地址
 
-# 数据库配置
+# 数据库配置  可以在docker-compose中的环境变量中配置
 mysql:
   host: 'localhost' # 数据库主机
   port: 3306 # 数据库端口

+ 2 - 0
SourceCode/TenderCrawler/app/config.yml

@@ -49,6 +49,7 @@ ai:
     city: string; // 招标单位城市
     date: string; // 项目开标的时间
     address: string; // 项目开标的地点
+    budget: string; // 项目预算金额,单位换成元,没有留空
     release_date: string; // 招标信息的发布时间
     summary: string; // 100字左右的招标条件,联系方式等内容摘要
     devices: string; // 只涉及到光谱仪相关的设备,其他无关设备不需要,多个设备以逗号分割 ,例如 红外光谱仪,拉曼光谱仪等
@@ -86,6 +87,7 @@ email:
   smtp_password: FWRwBZKHTLHjHT5F
   from_email: yueyunyun88@163.com
   error_email:
+  default_email: 349977741@qq.com
 job:
   event_id: 1 # 改变这个值,整点会检测重新加载任务
   sleep_interval: 10

+ 33 - 20
SourceCode/TenderCrawler/app/jobs/data_process.py

@@ -19,6 +19,7 @@ class DataProcess:
                 city: string; // 招标单位城市
                 date: string; // 项目开标的时间
                 address: string; // 项目开标的地点
+                budget: string; // 项目预算金额,单位换成元,没有留空
                 release_date: string; // 招标信息的发布时间
                 summary: string; // 100字左右的招标条件,联系方式等内容摘要
                 devices: string; // 只涉及到光谱仪相关的设备,其他无关设备不需要,多个设备以逗号分割 ,例如 红外光谱仪,拉曼光谱仪等
@@ -50,11 +51,14 @@ class DataProcess:
     def __init__(self, store: IDataStore):
         self._store = store
         self._ai_system_prompt = utils.get_config_value(
-            "ai.system_prompt", self.DEFAULT_AI_SYSTEM_PROMPT)
+            "ai.system_prompt", self.DEFAULT_AI_SYSTEM_PROMPT
+        )
         self._ai_prompt_template_1 = utils.get_config_value(
-            "ai.prompt_template_1", self.DEFAULT_AI_PROMPT_TEMPLATE_1)
+            "ai.prompt_template_1", self.DEFAULT_AI_PROMPT_TEMPLATE_1
+        )
         self._ai_prompt_template_2 = utils.get_config_value(
-            "ai.prompt_template_2", self.DEFAULT_AI_PROMPT_TEMPLATE_2)
+            "ai.prompt_template_2", self.DEFAULT_AI_PROMPT_TEMPLATE_2
+        )
 
     @property
     def store(self) -> IDataStore:
@@ -81,20 +85,27 @@ class DataProcess:
             if item.status == 1:
                 utils.get_logger().info(f"ALREADY1 URL:{url}")
                 return
-            data = (self.store.query_one_process_by_url(url) if item.data_type
-                    == 0 else self.store.query_one_process_result_by_url(url))
+            data = (
+                self.store.query_one_process_by_url(url)
+                if item.data_type == 0
+                else self.store.query_one_process_result_by_url(url)
+            )
             if data:
-                utils.get_logger().info(
-                    f"ALREADY2 [{item.data_type}] URL==> {url}")
+                utils.get_logger().info(f"ALREADY2 [{item.data_type}] URL==> {url}")
                 return
-            data = (self._ai_process_1(item)
-                    if item.data_type == 0 else self._ai_process_2(item))
+            data = (
+                self._ai_process_1(item)
+                if item.data_type == 0
+                else self._ai_process_2(item)
+            )
             if data:
                 old = None
                 if data.no:
-                    old = (self.store.query_one_process_result_by_no(data.no)
-                           if item.data_type == 0 else
-                           self.store.query_one_process_by_no(data.no))
+                    old = (
+                        self.store.query_one_process_result_by_no(data.no)
+                        if item.data_type == 0
+                        else self.store.query_one_process_by_no(data.no)
+                    )
                 if not old:
                     data.url = url
                     data.keyword = item.keyword
@@ -110,14 +121,15 @@ class DataProcess:
                         else:
                             old.other_urls = url
                         if item.data_type == 0:
-                            self.store.set_process_other_urls(
-                                data.url, old.other_urls)
+                            self.store.set_process_other_urls(data.url, old.other_urls)
                         else:
                             self.store.set_process_result_other_urls(
-                                data.url, old.other_urls)
+                                data.url, old.other_urls
+                            )
                     self.store.set_collect_process(old.url)
                     utils.get_logger().info(
-                        f"ALREADY 编号: {data.no} URL:{old.other_urls}")
+                        f"ALREADY 编号: {data.no} URL:{old.other_urls}"
+                    )
 
             utils.get_logger().info("END   ==>" + url)
         except Exception as e:
@@ -126,8 +138,8 @@ class DataProcess:
     def _ai_process_1(self, item: CollectData) -> ProcessData | None:
         try:
             data = utils.call_openai(
-                self._ai_system_prompt,
-                f"{item.content} {self._ai_prompt_template_1}")
+                self._ai_system_prompt, f"{item.content} {self._ai_prompt_template_1}"
+            )
             # area_str = data.get("area")
             #
             # if "省" in area_str:
@@ -145,6 +157,7 @@ class DataProcess:
                 city=data.get("city"),
                 address=data.get("address"),
                 devices=data.get("devices"),
+                budget=data.get("budget"),
                 summary=data.get("summary"),
                 release_date=data.get("release_date"),
                 prompt_tokens=data.get("prompt_tokens"),
@@ -158,8 +171,8 @@ class DataProcess:
     def _ai_process_2(self, item: CollectData) -> ProcessResultData | None:
         try:
             data = utils.call_openai(
-                self._ai_system_prompt,
-                f"{item.content} {self._ai_prompt_template_2}")
+                self._ai_system_prompt, f"{item.content} {self._ai_prompt_template_2}"
+            )
             result = ProcessResultData(
                 no=data.get("no"),
                 title=data.get("title"),

+ 17 - 18
SourceCode/TenderCrawler/app/jobs/data_send.py

@@ -17,6 +17,7 @@ class DataSend:
         return self._store
 
     def __init__(self, store: IDataStore):
+        self._default_email = utils.get_config_value("email.default_email", "")
         self._store = store
         self._email_area_arr = self.store.query_all_emails()
         self._email_area_virtual_arr = self.store.query_all_virtual_emails()
@@ -45,6 +46,8 @@ class DataSend:
             f"开始发送中标报告邮件,开始日期:{start_date.strftime("%Y-%m-%d")},结束日期:{end_date.strftime("%Y-%m-%d")}"
         )
         email = self.store.query_master_email()
+        if self._default_email and self._default_email not in email:
+            email = email + "," + self._default_email if email else self._default_email
         if not email:
             utils.get_logger().error("没有找到master email")
             return
@@ -61,21 +64,23 @@ class DataSend:
         utils.get_logger().info(f"开始发送邮件,地区为:{item.city} ,URL为 {item.url}")
         email = self._get_email_by_area(item.city)
         if not email:
+            email = ""
             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)
-        flag = utils.send_email(
-            email, f"{title_prev} {item.title}", body, True, item.attach_path
-        )
+        if self._default_email and self._default_email not in email:
+            email = email + "," + self._default_email if email else self._default_email
+        flag = False
+        if item.title:
+            title_prev = utils.get_config_value("email.title_prev", "【招标信息】")
+            body = self._build_email_html(item)
+            flag = utils.send_email(
+                email, f"{title_prev} {item.title}", body, True, item.attach_path
+            )
         if flag:
             self.store.set_send(item.no)
 
-    def _get_email_by_area(
-        self, area: str, count: int = 0, virtual_area: str = None
-    ) -> str:
+    def _get_email_by_area(self, area: str, count: int = 0) -> str:
         email = None
         area_str = (
             area.replace("省", "").replace("市", "").replace("区", "").replace("县", "")
@@ -83,18 +88,11 @@ class DataSend:
         for area_item in self._email_area_arr:
             if area_str in area_item.area:
                 email = area_item.email
-                if virtual_area:
-                    new_area = f"{area_item.area},{virtual_area}"
-                    self.store.update_area_email_area_by_name(area_item.name, new_area)
-                    self._email_area_arr = self.store.query_all_emails()
                 break
-        if not email and count < 3:
+        if not email and count <= 3:
             area_name = self._get_email_by_area_virtual(area_str)
             if area_name:
-                virtual_area = (
-                    f"{area_str},{virtual_area}" if virtual_area else area_str
-                )
-                email = self._get_email_by_area(area_name, count + 1, virtual_area)
+                email = self._get_email_by_area(area_name, count + 1)
         return email
 
     def _get_email_by_area_virtual(self, area: str) -> str:
@@ -157,6 +155,7 @@ class DataSend:
                 <h1>{item.title}</h1>
                 <p><strong>招标编号:</strong> {item.no if item.no else ""}</p>
                 <p><strong>项目区域:</strong> {item.province if item.province else ""}{item.city if item.city else ""}</p>
+                {f"<p><strong>项目预算:</strong> {item.budget + "元"}</p>" if item.budget else ""}
                 <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>

+ 44 - 33
SourceCode/TenderCrawler/app/models/process_data.py

@@ -16,6 +16,7 @@ class ProcessData:
         province=None,
         city=None,
         address=None,
+        budget=None,
         summary=None,
         release_date=None,
         devices=None,
@@ -33,13 +34,14 @@ class ProcessData:
         self.title = title
         self.url = url
         self.date = date
-        self.province = province.replace("省", "").replace(
-            "市", "") if province else ""
-        self.city = (city.replace("市", "").replace("区", "").replace("县", "")
-                     if city else "")
+        self.province = province.replace("省", "").replace("市", "") if province else ""
+        self.city = (
+            city.replace("市", "").replace("区", "").replace("县", "") if city else ""
+        )
         if self.province == self.city:
             self.province = ""
         self.keyword = keyword
+        self.budget = budget
         self.address = address
         self.summary = summary
         self.release_date = release_date
@@ -59,11 +61,12 @@ class ProcessData:
             f"ProcessData(no={self.no}, title={self.title}, date={self.date}, "
             f"province={self.province},city={self.city}, address={self.address}, summary={self.summary}, "
             f"status={self.status}, create_time={self.create_time}, "
-            f"send_time={self.send_time}, remark={self.remark})")
+            f"send_time={self.send_time}, remark={self.remark})"
+        )
 
     _insert_query = """
-              INSERT IGNORE INTO t_data (no, title, url, keyword, date, province, city, address, summary, release_date, devices, attach_path, status, create_time, prompt_tokens, completion_tokens, total_tokens)
-              VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
+              INSERT IGNORE INTO t_data (no, title, url, keyword, date, province, city, address, budget, summary, release_date, devices, attach_path, status, create_time, prompt_tokens, completion_tokens, total_tokens)
+              VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s);
           """
 
     # _update_query = """
@@ -82,6 +85,7 @@ class ProcessData:
             process_data.province,
             process_data.city,
             process_data.address,
+            process_data.budget,
             process_data.summary,
             process_data.release_date,
             process_data.devices,
@@ -102,29 +106,34 @@ class ProcessData:
 
     def insert_batch(self, process_data_list):
         if not all(
-                isinstance(process_data, self.__class__)
-                for process_data in process_data_list):
+            isinstance(process_data, self.__class__)
+            for process_data in process_data_list
+        ):
             raise TypeError("process_data_list 中的所有元素必须是 ProcessData 的实例")
 
-        insert_params = [(
-            process_data.no,
-            process_data.title,
-            process_data.url,
-            process_data.keyword,
-            process_data.date,
-            process_data.province,
-            process_data.city,
-            process_data.address,
-            process_data.summary,
-            process_data.release_date,
-            process_data.devices,
-            process_data.attach_path,
-            0,
-            datetime.now(),
-            process_data.prompt_tokens,
-            process_data.completion_tokens,
-            process_data.total_tokens,
-        ) for process_data in process_data_list]
+        insert_params = [
+            (
+                process_data.no,
+                process_data.title,
+                process_data.url,
+                process_data.keyword,
+                process_data.date,
+                process_data.province,
+                process_data.city,
+                process_data.address,
+                process_data.budget,
+                process_data.summary,
+                process_data.release_date,
+                process_data.devices,
+                process_data.attach_path,
+                0,
+                datetime.now(),
+                process_data.prompt_tokens,
+                process_data.completion_tokens,
+                process_data.total_tokens,
+            )
+            for process_data in process_data_list
+        ]
 
         # update_params = [(process_data.url, )
         #                  for process_data in process_data_list]
@@ -143,7 +152,7 @@ class ProcessData:
 
     def fetch_one_process_by_url(self, url: str):
         with MySQLHelper() as db_helper:
-            result = db_helper.fetch_one(self._one_url_query, (url, ))
+            result = db_helper.fetch_one(self._one_url_query, (url,))
             if not result:
                 return None
             data = ProcessData(
@@ -161,7 +170,7 @@ class ProcessData:
     def fetch_one_process_by_no(self, no: str):
         with MySQLHelper() as db_helper:
 
-            result = db_helper.fetch_one(self._one_no_query, (no, ))
+            result = db_helper.fetch_one(self._one_no_query, (no,))
             if not result:
                 return None
             data = ProcessData(
@@ -172,7 +181,7 @@ class ProcessData:
             )
             return data
 
-    _not_send_query = "SELECT no, title, url, keyword, devices,date, city, address, summary, attach_path, release_date FROM t_data WHERE status = 0"
+    _not_send_query = "SELECT no, title, url, keyword, devices,date, city, address, budget, summary, attach_path, release_date FROM t_data WHERE status = 0"
 
     def fetch_not_send(self):
         with MySQLHelper() as db_helper:
@@ -204,8 +213,10 @@ class ProcessData:
         :return: 删除的行数
         """
         with MySQLHelper() as db_helper:
-            params = (date, )
+            params = (date,)
             db_helper.execute_non_query(self._delete_before_date_query, params)
             affected_rows = db_helper.connection.affected_rows()
-            utils.get_logger().info(f"删除 {date} 之前共 {affected_rows} 条 招标处理记录。")
+            utils.get_logger().info(
+                f"删除 {date} 之前共 {affected_rows} 条 招标处理记录。"
+            )
             return affected_rows

+ 1 - 1
SourceCode/TenderCrawler/docker-compose.yml

@@ -56,7 +56,7 @@ services:
       #      - APP_AI__KEY=
       #      - APP_AI__URL=http://192.168.0.109:7580/api/chat
       #      - APP_AI__MODEL=qwen2.5:7b
-      - APP_LOGGER__LEVEL=INFO
+      - APP_LOGGER__LEVEL=DEBUG
       - APP_JOB__COLLECT=20:00,12:00
       - APP_JOB__PROCESS=23:00,4:00,13:00
       - APP_JOB__SEND_EMAIL=08:20,14:00

+ 15 - 10
SourceCode/TenderCrawler/init.sql

@@ -38,11 +38,11 @@ CREATE TABLE `t_area_email`  (
   PRIMARY KEY (`name`) USING BTREE
 ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
 
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('master', 'master', 'chancelot@foxmail.com,349977741@qq.com', 0,1, NULL);
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('all', '全国', 'chancelot@foxmail.com,349977741@qq.com', 0,1, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('master', 'master', '349977741@qq.com', 0,1, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('all', '全国', '349977741@qq.com', 0,1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('张志琼', '黑龙江,吉林,辽宁', 'zhiqiong.zhang@bruker.com', 0,0, NULL);
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('王双', '河北,济南,山东德州', 'shuang.wang@bruker.com', 0,0, NULL);
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('范国春', '山东', 'guochun.fan@bruker.com', 0,0, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('王双', '河北,山东_02', 'shuang.wang@bruker.com', 0,0, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('范国春', '山东_01', 'guochun.fan@bruker.com', 0,0, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('尚祖俭', '天津', 'zujian.shang@bruker.com', 0,0, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('赵跃', '北京', 'yue.zhao@bruker.com', 0,0, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('张景灿', '陕西,新疆,宁夏,青海', 'jingcan.zhang@bruker.com', 0,0, NULL);
@@ -54,10 +54,10 @@ INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`,
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('邬歆', '安徽,香港,澳门', 'xin.wu@bruker.com', 0,0, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('冯新宝', '湖北,湖南', 'xinbao.feng@bruker.com', 0,0, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('耿朝曦', '江西,贵州', 'zhaoxi.geng@bruker.com', 0,0, NULL);
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('李华斌', '广西,深圳', 'huabin.li@bruker.com', 0,0, NULL);
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('吕万明', '海南,广州,中山', 'wanming.lv@bruker.com', 0,0, NULL);
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('许建光', '西藏,云南,广东', 'jianguang.xu@bruker.com', 0,0, NULL);
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('全国', '北京,天津,上海,重庆,河北,山西,黑龙江,吉林,辽宁,江苏,浙江,安徽,福建,江西,山东,河南,湖北,湖南,广东,海南,四川,贵州,云南,陕西,甘肃,青海,台湾,内蒙古,广西,西藏,宁夏,新疆,香港,澳门', '', 1, 1, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('许建光', '西藏,云南,广东_01', 'jianguang.xu@bruker.com', 0,0, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('李华斌', '广西,广东_02', 'huabin.li@bruker.com', 0,0, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('吕万明', '海南,广东_03', 'wanming.lv@bruker.com', 0,0, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('全国', '北京,天津,上海,重庆,河北,山西,黑龙江,吉林,辽宁,江苏,浙江,安徽,福建,江西,山东_01,山东_02,河南,湖北,湖南,广东_01,广东_02,广东_03,海南,四川,贵州,云南,陕西,甘肃,青海,台湾,内蒙古,广西,西藏,宁夏,新疆,香港,澳门', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('北京', '东城区,西城区,朝阳区,丰台区,石景山区,海淀区,门头沟区,房山区,通州区,顺义区,大兴区,昌平区,平谷区,怀柔区,密云区,延庆区', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('上海', '黄浦区,徐汇区,长宁区,静安区,普陀区,虹口区,杨浦区,宝山区,闵行区,嘉定区,浦东新区,金山区,松江区,青浦区,奉贤区,崇明区', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('天津', '和平区,河东区,河西区,南开区,河北区,红桥区,东丽区,西青区,北辰区,武清区,宝坻区,滨海新区,宁河区,静海区,蓟州区', '', 1, 1, NULL);
@@ -74,7 +74,8 @@ INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`,
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('河北', '石家庄,唐山,秦皇岛,邯郸,邢台,保定,张家口,承德,沧州,廊坊,衡水', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('河南', '郑州,开封,洛阳,平顶山,安阳,鹤壁,新乡,焦作,濮阳,许昌,漯河,三门峡,南阳,商丘,周口,驻马店,济源示范区', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('甘肃', '兰州,嘉峪关,金昌,白银,天水,武威,张掖,平凉,酒泉,庆阳,定西,陇南,临夏回族自治州,甘南藏族自治州', '', 1, 1, NULL);
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('山东', '青岛,淄博,枣庄,东营,烟台,潍坊,济宁,泰安,威海,日照,莱州,临沂,聊城,滨州,菏泽', '', 1, 1, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('山东_01', '青岛,淄博,枣庄,东营,烟台,潍坊,济宁,泰安,威海,日照,莱州,临沂,聊城,滨州,菏泽', '', 1, 1, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('山东_02', '济南,德州', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('内蒙古', '呼和浩特,包头,乌海,赤峰,通辽,鄂尔多斯,呼伦贝尔,巴彦淖尔,乌兰察布,兴安盟,锡林郭勒盟,阿拉善盟', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('浙江', '杭州,宁波,温州,嘉兴,湖州,绍兴,金华,衢州,舟山,台州,丽水', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('福建', '福州,厦门,莆田,三明,泉州,漳州,南平,龙岩,宁德', '', 1, 1, NULL);
@@ -85,12 +86,15 @@ INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`,
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('贵州', '贵阳,六盘水,遵义,安顺,毕节,铜仁,黔东南苗族侗族自治州,黔南布依族苗族自治州,黔西南布依族苗族自治州', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('广西', '南宁,柳州,桂林,梧州,北海,防城港,钦州,贵港,玉林,百色,贺州,河池,来宾,崇左', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('海南', '海口,三亚,三沙,儋州,琼海,文昌,万宁,东方,澄迈,定安,屯昌,临高,白沙黎族自治县,昌江黎族自治县,乐东黎族自治县,陵水黎族自治县,保亭黎族苗族自治县,琼中黎族苗族自治县', '', 1, 1, NULL);
-INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('广东', '珠海,汕头,佛山,韶关,湛江,肇庆,江门,茂名,惠州,梅州,汕尾,河源,阳江,清远,东莞,潮州,揭阳,云浮', '', 1, 1, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('广东_01', '珠海,汕头,佛山,韶关,湛江,肇庆,江门,茂名,惠州,梅州,汕尾,河源,阳江,清远,东莞,潮州,揭阳,云浮', '', 1, 1, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('广东_02', '深圳', '', 1, 1, NULL);
+INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('广东_03', '广州,中山', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('西藏', '拉萨,日喀则,昌都,林芝,山南,那曲,阿里地区', '', 1, 1, NULL);
 INSERT INTO `t_area_email` (`name`, `area`, `email`, `is_virtual`, `is_active`, `remark`) VALUES ('云南', '昆明,曲靖,玉溪,保山,昭通,丽江,普洱,临沧,红河哈尼族彝族自治州,文山壮族苗族自治州,西双版纳傣族自治州,大理白族自治州,德宏傣族景颇族自治州,怒江傈僳族自治州,迪庆藏族自治州', '', 1, 1, NULL);
 
 
 
+
 -- ----------------------------
 -- Table structure for t_collect_data
 -- ----------------------------
@@ -136,6 +140,7 @@ CREATE TABLE `t_data`  (
   `province` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '招标单位省份',
   `city` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '招标单位城市',
   `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '详细地点',
+  `budget` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '预算金额(单位:元)',
   `summary` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL COMMENT '招标摘要',
   `release_date` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '发布时间',
   `devices` varchar(1000) NULL DEFAULT NULL COMMENT '相关设备',