liumiaomiao 1 týždeň pred
rodič
commit
b9b96ca84f

+ 27 - 0
.gitignore

@@ -0,0 +1,27 @@
+# 忽略所有 .log 文件
+*.log
+
+# 忽略特定目录(如 node_modules/)
+node_modules/
+
+# 忽略所有 .tmp 和 .temp 文件
+*.tmp
+*.temp
+
+# 忽略 macOS 系统文件
+.DS_Store
+
+# 忽略 IDE 配置文件(如 VS Code)
+.vscode/
+.idea/
+
+# 忽略本地环境文件(如 .env)
+.env
+.env.local
+
+# 忽略编译产物(如 dist/ 或 build/)
+dist/
+build/
+
+.pyc
+__pycache__/

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 33 - 6
app.py


+ 1 - 0
docs/last_processed_id.txt

@@ -0,0 +1 @@
+67e4d04c3309c0998be8676c

+ 1 - 1
docs/last_processed_id_mysql.txt

@@ -1 +1 @@
-67e4a6eb3309c0998be727e9
+67e497533309c0998be6e662

+ 4 - 1
docs/table_head_doc/abnormal_buyer_contain.csv

@@ -36,4 +36,7 @@ X
 自然资源和规局
 村民委会
 萎员会
-经济技术开发茎
+经济技术开发茎
+项目
+分公司
+分行

+ 31 - 0
docs/table_head_doc/area_31.csv

@@ -0,0 +1,31 @@
+河北省
+山西省
+辽宁省
+吉林省
+黑龙江省
+江苏省
+浙江省
+安徽省
+福建省
+江西省
+山东省
+河南省
+湖北省
+湖南省
+广东省
+海南省
+四川省
+贵州省
+云南省
+陕西省
+甘肃省
+青海省
+内蒙古自治区
+广西壮族自治区
+西藏自治区
+宁夏回族自治区
+新疆维吾尔自治区
+北京市
+天津市
+上海市
+重庆市

+ 258 - 3
tables/fields/NoField.py

@@ -22,12 +22,17 @@ class NoFieldChecker(object):
             "budget": self.check_budget,
             "bidamount": self.check_bidamount,
             "area":self.check_region,
+            "city":self.check_city,
+            "district": self.check_district,
             "projectcode": self.check_projectcode,
             "toptype":self.check_toptype,
             "subtype":self.check_subtype,
             "publishtime":self.check_publishtime,
             "multipackage":self.check_subpackage,
-            "purchasinglist":self.check_purchasinglist
+            "purchasinglist":self.check_purchasinglist,
+            "detail":self.check_detail,
+            "href":self.check_href,
+            "est_purchase_time":self.check_est_purchase_time
         }
 
     def check_bidamount(self,obj,catch_content: CatchContentObject) -> bool:
@@ -99,7 +104,7 @@ class NoFieldChecker(object):
 
     def check_region(self,obj, catch_content: CatchContentObject) -> bool:
         """
-        区域为空检测
+        省份为空检测
         :param obj:代表一个item
         :return:返回true 代表异常
         """
@@ -107,6 +112,26 @@ class NoFieldChecker(object):
         if not area:
             return True
         return False
+    def check_city(self,obj, catch_content: CatchContentObject) -> bool:
+        """
+        城市为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        city = obj.get("city")
+        if not city:
+            return True
+        return False
+    def check_district(self,obj, catch_content: CatchContentObject) -> bool:
+        """
+        区县为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        district = obj.get("district")
+        if not district:
+            return True
+        return False
 
     def check_title(self,obj, catch_content: CatchContentObject) -> bool:
         """
@@ -181,4 +206,234 @@ class NoFieldChecker(object):
     def check_publishtime(self,obj, catch_content: CatchContentObject) -> bool:
         if not obj.get("publishtime"):
             return True
-        return  False
+        return  False
+    def check_detail(self,obj, catch_content: CatchContentObject) -> bool:
+        if not obj.get("detail"):
+            return True
+        return  False
+    def check_href(self,obj, catch_content: CatchContentObject) -> bool:
+        if not obj.get("href"):
+            return True
+        return  False
+
+    def check_est_purchase_time(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        预计采购时间为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        if obj.get("toptype") == "预告":
+            if not obj.get("est_purchase_time"):
+                return True
+            return False
+        return False
+    def check_docstarttime(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        招标文件获取开始时间为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        if obj.get("toptype") == "招标":
+            if not obj.get("docstarttime"):
+                return True
+            return False
+        return False
+
+    def check_docendtime(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        招标文件获取截止时间为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        if obj.get("toptype") == "招标":
+            if not obj.get("docendtime"):
+                return True
+            return False
+        return False
+
+    def check_bidstarttime(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        投标文件递交开始时间为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        if obj.get("toptype") == "招标":
+            if not obj.get("bidstarttime"):
+                return True
+            return False
+        return False
+    def check_bidendtime(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        投标截止日期为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        if obj.get("toptype") == "招标":
+            if not obj.get("bidendtime"):
+                return True
+            return False
+        return False
+    def check_bidopentime(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        开标日期为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        if obj.get("toptype") != "结果" and obj.get("toptype") != "预告":
+            if not obj.get("bidopentime"):
+                return True
+            return False
+        return False
+    def check_bidway(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        投标方式为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        toptype = obj.get("toptype")
+        subtype = obj.get("subtype", "")
+        if toptype == "招标" or subtype in ["合同", "验收"]:
+            if not obj.get("bidway"):
+                return True
+            return False
+        return False
+    def check_buyerperson(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        采购单位联系人为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        toptype = obj.get("toptype")
+        subtype = obj.get("subtype", "")
+        if toptype =="招标" or subtype in ["中标", "成交", "合同", "验收"]:
+            if not obj.get("buyerperson"):
+                return True
+            return False
+        return False
+    def check_buyertel(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        采购单位联系电话为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        toptype = obj.get("toptype")
+        subtype = obj.get("subtype", "")
+        if toptype =="招标" or subtype in ["中标", "成交", "合同", "验收"]:
+            if not obj.get("buyertel"):
+                return True
+            return False
+        return False
+    def check_agency(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        招标代理机构空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        toptype = obj.get("toptype")
+        subtype = obj.get("subtype", "")
+        if toptype =="招标" or subtype in ["中标", "成交", "合同", "验收"]:
+            if not obj.get("agency"):
+                return True
+            return False
+        return False
+    def check_agencyperson(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        招标代理机构联系人为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        toptype = obj.get("toptype")
+        subtype = obj.get("subtype", "")
+        if toptype =="招标" or subtype in ["中标", "成交", "合同", "验收"]:
+            if not obj.get("agencyperson"):
+                return True
+            return False
+        return False
+    def check_agencytel(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        招标代理机构联系电话为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        toptype = obj.get("toptype")
+        subtype = obj.get("subtype", "")
+        if toptype =="招标" or subtype in ["中标", "成交", "合同", "验收"]:
+            if not obj.get("agencytel"):
+                return True
+            return False
+        return False
+    def check_winnerperson(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        中标单位联系人为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        subtype = obj.get("subtype", "")
+        if subtype in ["中标", "成交", "合同", "验收"]:
+            if not obj.get("winnerperson"):
+                return True
+            return False
+        return False
+    def check_winnertel(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        中标单位联系电话为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        subtype = obj.get("subtype", "")
+        if subtype in ["中标", "成交", "合同", "验收"]:
+            if not obj.get("winnertel"):
+                return True
+            return False
+        return False
+    def check_entname(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        候选人名称为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        subtype = obj.get("subtype", "")
+        if subtype in ["中标", "成交"]:
+            if "winnerorder" not in obj:
+                return True  # 如果没有winnerorder字段,视为无entname
+
+            for item in obj["winnerorder"]:
+                if "entname" in item:
+                    return False  # 只要有一个entname存在,就返回False
+
+            return True  # 所有条目都没有entname,返回True
+        return False
+    def check_sort(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        候选人名次为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        subtype = obj.get("subtype", "")
+        if subtype in ["中标", "成交"]:
+            if "winnerorder" not in obj:
+                return True  # 如果没有winnerorder字段,视为无entname
+
+            for item in obj["winnerorder"]:
+                if "sort" in item or "sortstr" in item:
+                    return False  # 只要有一个entname存在,就返回False
+
+            return True  # 所有条目都没有entname,返回True
+        return False
+    def check_price(self, obj, catch_content: CatchContentObject) -> bool:
+        """
+        投标报价为空检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        subtype = obj.get("subtype", "")
+        if subtype in ["中标", "成交"]:
+            if "winnerorder" not in obj:
+                return True  # 如果没有winnerorder字段,视为无entname
+
+            for item in obj["winnerorder"]:
+                if "price" in item :
+                    return False  # 只要有一个entname存在,就返回False
+
+            return True  # 所有条目都没有entname,返回True
+        return False

+ 19 - 26
tables/fields/area.py

@@ -2,6 +2,7 @@ import os
 import re
 import pandas as pd
 
+
 class AreaChecker(object):
     def __init__(self):
         self.errors_tables = {
@@ -23,17 +24,11 @@ class AreaChecker(object):
                 "parent_code": "03",
                 "checkFn": self.check0301
             },
-            "0302": {
-                "name": "城市不在[3,11]个字之间",
-                "parent_name": "长度异常类型",
-                "parent_code": "03",
-                "checkFn": self.check0302
-            },
-            "0303": {
-                "name": "区县不在[2,15]个字之间",
-                "parent_name": "长度异常类型",
-                "parent_code": "03",
-                "checkFn": self.check0303
+            "0401": {
+                "name": "不在31省份中",
+                "parent_name": "内容异常类型",
+                "parent_code": "04",
+                "checkFn": self.check0401
             }
         }
 
@@ -112,27 +107,25 @@ class AreaChecker(object):
         """
         return true  代表返回异常
         """
+        if area == '':
+            return  True
         if 2 <= len(area) <= 3:
            return False
         return True
 
-    #城市不在[3,11]个字之间
-    def check0302(self, city: str) -> bool:
+    def check0401(self, area: str) -> bool:
         """
         return true  代表返回异常
         """
-        if city=='':
+        area_31 =[
+              "河北", "山西", "辽宁", "吉林", "黑龙江",
+              "江苏", "浙江", "安徽", "福建", "江西",
+              "山东", "河南", "湖北", "湖南", "广东",
+              "海南", "四川", "贵州", "云南", "陕西",
+              "甘肃", "青海", "内蒙古", "广西", "西藏",
+              "宁夏", "新疆", "北京", "天津", "上海",
+              "重庆"
+            ]
+        if area in area_31:
             return False
-        if 3 <= len(city) <= 11:
-           return False
         return True
-    #区县不在[2,15]个字之间
-    def check0303(self, district: str) -> bool:
-        """
-        return true  代表返回异常
-        """
-        if district=='':
-            return False
-        if 2 <= len(district) <= 15:
-           return False
-        return True

+ 16 - 0
tables/fields/bidamount.py

@@ -34,6 +34,12 @@ class BidAmountChecker(object):
                 "parent_code": "01",
                 "checkFn": self.check0104
             },
+            "0301": {
+                "name": "中标金额<0",
+                "parent_name": "金额错误",
+                "parent_code": "01",
+                "checkFn": self.check0301
+            }
         }
 
     @staticmethod
@@ -94,3 +100,13 @@ class BidAmountChecker(object):
         if supervisorrate==0 :
             return False
         return True
+
+    @staticmethod
+    def check0301(bidamount: float) -> bool :
+        """
+        预算<0,视为异常
+        :return: 返回true 代表异常
+        """
+        if  bidamount < 0:
+            return True
+        return False

+ 22 - 0
tables/fields/bidendtime.py

@@ -0,0 +1,22 @@
+
+#投标文件递交截止时间(投标截止日期)
+class BidendtimeChecker(object):
+    def __init__(self):
+        self.errors_tables = {
+            "0101": {
+                "name": "投标截止日期<投标文件递交开始时间",
+                "parent_name": "时间有效性异常",
+                "parent_code": "01",
+                "checkFn": self.check0101
+            }
+        }
+
+    #招标文件获取开始时间>发布时间
+    def check0101(self, toptype:str,bidendtime:int,bidstarttime:int) -> bool:
+        """
+        return true  代表返回异常
+        """
+        if toptype == '招标':
+            if bidendtime < bidstarttime:
+                return True
+        return False

+ 22 - 5
tables/fields/bidopentime.py

@@ -1,11 +1,7 @@
-"""
-    中标时间字段检查
-"""
-
 
 class BidopentimeChecker(object):
     """
-        中标时间字段检查
+        开标日期字段检查
     """
     def __init__(self):
         self.errors_tables = {
@@ -14,6 +10,12 @@ class BidopentimeChecker(object):
                 "parent_name": "数据范围类型",
                 "parent_code": "02",
                 "checkFn": self.check0201
+            },
+            "0202": {
+                "name": "开标时间 < 投标文件递交截止时间",
+                "parent_name": "数据范围类型",
+                "parent_code": "02",
+                "checkFn": self.check0202
             }
         }
 
@@ -30,5 +32,20 @@ class BidopentimeChecker(object):
             else:
                 # 两者中有一方为空不判断
                 return False
+        else:
+            return False
+    def check0202(self, subtype:str,bidopentime: int, bidendtime:int ) -> bool:
+        """
+        return true  代表返回异常
+        """
+        if subtype  in ["招标", "邀标", "询价", "竞谈","单一","竞价","变更"]:
+            if bidopentime and bidendtime:
+                if bidopentime < bidendtime :
+                    return True
+                else:
+                    return False
+            else:
+                # 两者中有一方为空不判断
+                return False
         else:
             return False

+ 22 - 0
tables/fields/bidstarttime.py

@@ -0,0 +1,22 @@
+
+#投标文件递交开始时间
+class BidstarttimeChecker(object):
+    def __init__(self):
+        self.errors_tables = {
+            "0101": {
+                "name": "投标文件递交开始时间<发布时间",
+                "parent_name": "时间有效性异常",
+                "parent_code": "01",
+                "checkFn": self.check0101
+            }
+        }
+
+    #招标文件获取开始时间>发布时间
+    def check0101(self, toptype:str,publishtime:int,bidstarttime:int) -> bool:
+        """
+        return true  代表返回异常
+        """
+        if toptype == '招标':
+            if bidstarttime < publishtime:
+                return True
+        return False

+ 16 - 0
tables/fields/budget.py

@@ -28,6 +28,12 @@ class BudgetChecker(object):
                 "parent_code": "01",
                 "checkFn": self.check0103
             },
+            "0201": {
+                "name": "预算<0",
+                "parent_name": "金额错误",
+                "parent_code": "01",
+                "checkFn": self.check0201
+            }
         }
 
     @staticmethod
@@ -77,3 +83,13 @@ class BudgetChecker(object):
              length = 0
         if length > 4 :
             return True
+
+    @staticmethod
+    def check0201(budget: float) -> bool :
+        """
+        预算<0,视为异常
+        :return: 返回true 代表异常
+        """
+        if  budget < 0:
+            return True
+        return False

+ 15 - 1
tables/fields/buyer.py

@@ -49,8 +49,13 @@ class BuyerChecker(object):
                 "parent_name": "名称错误",
                 "parent_code": "01",
                 "checkFn": self.check0104
+            },
+            "0301": {
+                "name": "采购单位名称长度<3",
+                "parent_name": "名称长度异常错误",
+                "parent_code": "03",
+                "checkFn": self.check0301
             }
-
         }
         #
         self.buyer_ac = AcAutomation()
@@ -316,3 +321,12 @@ class BuyerChecker(object):
             if province == None and city == None and district == None:
                 return True
         return False
+
+    def check0301(self, buyer: str):
+        """
+        return  True 代表异常
+        """
+        if len(buyer) < 3:
+            return True
+        return False
+

+ 22 - 0
tables/fields/city.py

@@ -0,0 +1,22 @@
+
+class CityChecker(object):
+    def __init__(self):
+        self.errors_tables = {
+            "0101": {
+                "name": "城市不在[3,11]个字之间",
+                "parent_name": "长度异常类型",
+                "parent_code": "01",
+                "checkFn": self.check0101
+            }
+        }
+
+    #城市不在[3,11]个字之间
+    def check0101(self, city: str) -> bool:
+        """
+        return true  代表返回异常
+        """
+        if city=='':
+            return True
+        if 3 <= len(city) <= 11:
+           return False
+        return True

+ 25 - 0
tables/fields/district.py

@@ -0,0 +1,25 @@
+import os
+import re
+import pandas as pd
+
+class DistrictChecker(object):
+    def __init__(self):
+        self.errors_tables = {
+            "0101": {
+                "name": "区县不在[2,15]个字之间",
+                "parent_name": "长度异常类型",
+                "parent_code": "01",
+                "checkFn": self.check0101
+            }
+        }
+
+    #区县不在[2,15]个字之间
+    def check0101(self, district: str) -> bool:
+        """
+        return true  代表返回异常
+        """
+        if district=='':
+            return True
+        if 2 <= len(district) <= 15:
+           return False
+        return True

+ 22 - 0
tables/fields/docendtime.py

@@ -0,0 +1,22 @@
+
+#招标文件获取截止时间
+class DocendtimeChecker(object):
+    def __init__(self):
+        self.errors_tables = {
+            "0101": {
+                "name": "招标文件获取截止时间<招标文件获取开始时间",
+                "parent_name": "时间有效性异常",
+                "parent_code": "01",
+                "checkFn": self.check0101
+            }
+        }
+
+    #招标文件获取开始时间>发布时间
+    def check0101(self, toptype:str,docendtime:int,docstarttime:int) -> bool:
+        """
+        return true  代表返回异常
+        """
+        if toptype == '招标':
+            if docendtime < docstarttime:
+                return True
+        return False

+ 22 - 0
tables/fields/docstarttime.py

@@ -0,0 +1,22 @@
+
+#招标文件获取开始时间
+class DocstarttimeChecker(object):
+    def __init__(self):
+        self.errors_tables = {
+            "0101": {
+                "name": "招标文件获取开始时间<发布时间",
+                "parent_name": "时间有效性异常",
+                "parent_code": "01",
+                "checkFn": self.check0101
+            }
+        }
+
+    #招标文件获取开始时间>发布时间
+    def check0101(self,toptype:str, publishtime:int,docstarttime:int) -> bool:
+        """
+        return true  代表返回异常
+        """
+        if toptype == '招标':
+            if docstarttime < publishtime:
+                return True
+        return False

+ 30 - 0
tables/fields/winner.py

@@ -38,6 +38,18 @@ class WinnerChecker(object):
                 "parent_name": "名称错误",
                 "parent_code": "01",
                 "checkFn": self.check0103
+            },
+            "0104": {
+                "name": "中标单位包含采购单位",
+                "parent_name": "名称错误",
+                "parent_code": "01",
+                "checkFn": self.check0104
+            },
+            "0301": {
+                "name": "中标单位名称长度<3",
+                "parent_name": "名称长度异常错误",
+                "parent_code": "03",
+                "checkFn": self.check0301
             }
         }
         #
@@ -287,3 +299,21 @@ class WinnerChecker(object):
                 if re.search(f"{w[0]}$", s_winner):
                     return True
         return False
+    def check0301(self,s_winner:str):
+        """
+        中标单位长度异常检测
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        if len(s_winner) < 3:
+            return True
+        return False
+    def check0104(self,s_winner:str,buyer:str):
+        """
+        中标单位包含采购单位
+        :param obj:代表一个item
+        :return:返回true 代表异常
+        """
+        if buyer in s_winner:
+            return True
+        return False

+ 33 - 0
test.py

@@ -0,0 +1,33 @@
+def has_non_empty_qa(data):
+    # 获取data字典
+    data_dict = data.get('data', {})
+
+    # 遍历所有键值对
+    for key, value in data_dict.items():
+        # 检查键以'_qa'结尾且值不为空
+        if key.endswith('_qa') and value:  # value不为None、空字典、空列表等
+            return True
+    return False
+
+
+# 测试数据
+data = {
+    'code': 200,
+    'msg': '成功',
+    'data': {
+        'title_qa': {},
+        'projectname_qa': {},
+        'winner_qa': {},
+        'projectcode_qa': {},
+        'buyer_qa': {},
+        'bidamount_qa': {},
+        'area_qa': {},
+        'com_package_qa': {},
+        'bidopentime_qa': {},
+        'publishtime_qa': {'0202': '发布时间 > 当前时间'},
+        'score': 100
+    }
+}
+
+print(has_non_empty_qa(data))  # 输出: False
+

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov