This commit is contained in:
27942
2026-02-06 02:08:20 +08:00
parent 13883519a4
commit 456b7ca03b

View File

@@ -134,22 +134,12 @@ def search_related_records(client_info, party_info, exclude_project_id=None):
if not client_info and not party_info:
return result
# 解析委托人信息和相对方信息
client_parsed = parse_person_info(client_info) if client_info else {'name': '', 'id_number': '', 'original': ''}
party_parsed = parse_person_info(party_info) if party_info else {'name': '', 'id_number': '', 'original': ''}
# 解析委托人和相对方信息(支持 JSON 数组,提取所有人员)
client_names, client_id_numbers, client_originals = _extract_all_person_info(client_info)
party_names, party_id_numbers, party_originals = _extract_all_person_info(party_info)
# 提取姓名/名称(用于匹配)
client_name = client_parsed['name']
client_id_number = client_parsed['id_number']
client_original = client_parsed['original']
party_name = party_parsed['name']
party_id_number = party_parsed['id_number']
party_original = party_parsed['original']
# 检索预立案表 - 冲突规则:当前委托人只与历史「委托人」匹配,当前相对方只与历史「相对方」匹配,任一匹配即冲突
# 预立案表字段client_username/party_usernameJSON 数组)
if (client_info or client_name) or (party_info or party_name):
# 检索预立案表 - 委托人只匹配历史委托人,相对方只匹配历史相对方,任一匹配即冲突
if client_names or party_names:
matched_prefilings = []
prefiling_all = PreFiling.objects.filter(is_deleted=False)
for pf in prefiling_all[:100]:
@@ -157,36 +147,36 @@ def search_related_records(client_info, party_info, exclude_project_id=None):
break
pf_client_names = []
try:
client_data = json.loads(pf.client_username) if isinstance(pf.client_username, str) else pf.client_username
if isinstance(client_data, list):
for item in client_data:
cd = json.loads(pf.client_username) if isinstance(pf.client_username, str) else pf.client_username
if isinstance(cd, list):
for item in cd:
if isinstance(item, dict) and 'name' in item:
pf_client_names.append(item['name'].strip())
elif isinstance(client_data, dict) and 'name' in client_data:
pf_client_names.append(client_data['name'].strip())
elif isinstance(cd, dict) and 'name' in cd:
pf_client_names.append(cd['name'].strip())
except (json.JSONDecodeError, TypeError, AttributeError):
pf_client_names = [pf.client_username.strip()] if pf.client_username else []
pf_party_names = []
try:
party_data = json.loads(pf.party_username) if isinstance(pf.party_username, str) else pf.party_username
if isinstance(party_data, list):
for item in party_data:
pd = json.loads(pf.party_username) if isinstance(pf.party_username, str) else pf.party_username
if isinstance(pd, list):
for item in pd:
if isinstance(item, dict) and 'name' in item:
pf_party_names.append(item['name'].strip())
elif isinstance(party_data, dict) and 'name' in party_data:
pf_party_names.append(party_data['name'].strip())
elif isinstance(pd, dict) and 'name' in pd:
pf_party_names.append(pd['name'].strip())
except (json.JSONDecodeError, TypeError, AttributeError):
pf_party_names = [pf.party_username.strip()] if pf.party_username else []
client_match = False
if (client_info or client_name) and pf_client_names:
for pf_client in pf_client_names:
if (client_name and pf_client in client_name) or (client_original and pf_client in client_original):
if client_names and pf_client_names:
for pf_c in pf_client_names:
if any(pf_c in cn for cn in client_names) or any(pf_c in co for co in client_originals):
client_match = True
break
party_match = False
if (party_info or party_name) and pf_party_names:
for pf_party in pf_party_names:
if (party_name and pf_party in party_name) or (party_original and pf_party in party_original):
if party_names and pf_party_names:
for pf_p in pf_party_names:
if any(pf_p in pn for pn in party_names) or any(pf_p in po for po in party_originals):
party_match = True
break
if client_match or party_match:
@@ -199,54 +189,43 @@ def search_related_records(client_info, party_info, exclude_project_id=None):
})
result['prefiling_conflicts'] = matched_prefilings
# 检索立项表 - 冲突规则:当前委托人只匹配历史 client_info当前相对方只匹配历史 party_info,任一匹配即冲突
# 检索立项表 - 委托人只匹配历史 client_info相对方只匹配历史 party_info
project_records = ProjectRegistration.objects.filter(is_deleted=False)
if exclude_project_id:
project_records = project_records.exclude(id=exclude_project_id)
project_q = Q()
if client_info or client_name:
if client_name:
project_q |= Q(client_info__icontains=client_name)
if client_original:
project_q |= Q(client_info__icontains=client_original[:50])
if client_id_number:
project_q |= Q(client_info__icontains=client_id_number)
if party_info or party_name:
if party_name:
project_q |= Q(party_info__icontains=party_name)
if party_original:
project_q |= Q(party_info__icontains=party_original[:50])
if party_id_number:
project_q |= Q(party_info__icontains=party_id_number)
for cn in client_names:
project_q |= Q(client_info__icontains=cn)
for co in client_originals:
project_q |= Q(client_info__icontains=co[:50])
for cid in client_id_numbers:
project_q |= Q(client_info__icontains=cid)
for pn in party_names:
project_q |= Q(party_info__icontains=pn)
for po in party_originals:
project_q |= Q(party_info__icontains=po[:50])
for pid in party_id_numbers:
project_q |= Q(party_info__icontains=pid)
if project_q:
project_records = project_records.filter(project_q)
project_list = list(project_records[:10].values('id', 'ContractNo', 'times', 'type', 'client_info', 'party_info'))
result['project_conflicts'] = project_list
# 检索投标表 - 投标表只有BiddingUnit字段需要检查是否包含委托人或相对方信息
# BiddingUnit可能同时包含委托人和相对方或者只包含其中一方
# 检索投标表 - 招标单位是否包含委托人或相对方信息
bid_records = Bid.objects.filter(is_deleted=False)
bid_q = Q()
# 如果有提取出的名称,优先使用名称匹配
if client_name:
bid_q |= Q(BiddingUnit__icontains=client_name)
if party_name:
bid_q |= Q(BiddingUnit__icontains=party_name)
# 如果有完整信息,也尝试匹配
if client_original:
bid_q |= Q(BiddingUnit__icontains=client_original[:50])
if party_original:
bid_q |= Q(BiddingUnit__icontains=party_original[:50])
# 如果有身份证号,也尝试匹配
if client_id_number:
bid_q |= Q(BiddingUnit__icontains=client_id_number)
if party_id_number:
bid_q |= Q(BiddingUnit__icontains=party_id_number)
# 如果构建了查询条件,执行查询
for cn in client_names:
bid_q |= Q(BiddingUnit__icontains=cn)
for pn in party_names:
bid_q |= Q(BiddingUnit__icontains=pn)
for co in client_originals:
bid_q |= Q(BiddingUnit__icontains=co[:50])
for po in party_originals:
bid_q |= Q(BiddingUnit__icontains=po[:50])
for cid in client_id_numbers:
bid_q |= Q(BiddingUnit__icontains=cid)
for pid in party_id_numbers:
bid_q |= Q(BiddingUnit__icontains=pid)
if bid_q:
bid_records = bid_records.filter(bid_q)
bid_list = list(bid_records[:10].values('id', 'ProjectName', 'times', 'BiddingUnit'))
@@ -382,12 +361,10 @@ class registration(APIView):
submit=user
)
# 利益冲突检索:比对预立案、立项、投标三张表
client_info_str = _prefiling_person_to_info(prefiling.client_username)
party_info_str = _prefiling_person_to_info(prefiling.party_username)
# 利益冲突检索:比对预立案、立项、投标三张表(直接传 JSONconflict_search 内部解析所有人员)
conflict_result = conflict_search(
client_info=client_info_str,
party_info=party_info_str,
client_info=prefiling.client_username,
party_info=prefiling.party_username,
undertaker=Undertaker,
exclude_prefiling_id=prefiling.id
)
@@ -543,12 +520,10 @@ class EditRegistration(APIView):
pre.Undertaker = Undertaker
pre.save(update_fields=['Undertaker'])
# 利益冲突检索:比对预立案、立项、投标三张表
client_info_str = _prefiling_person_to_info(pre.client_username)
party_info_str = _prefiling_person_to_info(pre.party_username)
# 利益冲突检索:比对预立案、立项、投标三张表(直接传 JSONconflict_search 内部解析所有人员)
conflict_result = conflict_search(
client_info=client_info_str,
party_info=party_info_str,
client_info=pre.client_username,
party_info=pre.party_username,
undertaker=pre.Undertaker,
exclude_prefiling_id=pre.id
)
@@ -4225,23 +4200,62 @@ def _prefiling_person_to_info(person_json):
return None
def _normalize_client_party_for_search(val):
def _extract_all_person_info(info_val):
"""
将 conflict-search 接口的 client_info/party_info 统一为检索用字符串
支持:JSON 字符串(如 '[{"name":"张三","idNumber":"111"}]'、dict/list、或普通字符串。
client_info/party_info 中提取所有人员的姓名和身份证号
支持:
- JSON 字符串数组:'[{"name":"张三","idNumber":"111"},{"name":"王五","idNumber":"333"}]'
- JSON 字符串对象:'{"name":"张三","idNumber":"111"}'
- list / dict已解析的 JSON
- 普通字符串:"张三111""张三身份证号111""张三"
返回: (names_list, id_numbers_list, originals_list)
"""
if val is None:
return None
if isinstance(val, str):
val = val.strip()
if not val:
return None
if val.startswith('[') or val.startswith('{'):
return _prefiling_person_to_info(val)
return val
if isinstance(val, (list, dict)):
return _prefiling_person_to_info(val)
return None
names = []
id_numbers = []
originals = []
if not info_val:
return names, id_numbers, originals
# 尝试 JSON 解析
data = None
if isinstance(info_val, str):
info_val = info_val.strip()
if not info_val:
return names, id_numbers, originals
if info_val.startswith('[') or info_val.startswith('{'):
try:
data = json.loads(info_val)
except (json.JSONDecodeError, TypeError):
pass
elif isinstance(info_val, (list, dict)):
data = info_val
if data is not None:
if isinstance(data, dict):
data = [data]
if isinstance(data, list):
for item in data:
if not isinstance(item, dict):
continue
name = (item.get('name') or item.get('name_original') or '').strip()
id_num = (item.get('idNumber') or item.get('id_number') or '').strip()
if name:
names.append(name)
if id_num:
id_numbers.append(id_num)
orig = f"{name}{id_num}" if name and id_num else (name or id_num or '')
if orig:
originals.append(orig)
if names or id_numbers:
return names, id_numbers, originals
# 回退到 parse_person_info 处理普通字符串
if isinstance(info_val, str):
parsed = parse_person_info(info_val)
if parsed['name']:
names.append(parsed['name'])
if parsed['id_number']:
id_numbers.append(parsed['id_number'])
if parsed['original']:
originals.append(parsed['original'])
return names, id_numbers, originals
def parse_person_info(person_str):
@@ -4337,18 +4351,9 @@ def conflict_search(client_info=None, party_info=None, undertaker=None, bidding_
if not any([client_info, party_info, undertaker, bidding_unit]):
return result
# 解析委托人信息和相对方信息
client_parsed = parse_person_info(client_info) if client_info else {'name': '', 'id_number': '', 'original': ''}
party_parsed = parse_person_info(party_info) if party_info else {'name': '', 'id_number': '', 'original': ''}
# 提取姓名/名称(用于匹配)
client_name = client_parsed['name']
client_id_number = client_parsed['id_number']
client_original = client_parsed['original']
party_name = party_parsed['name']
party_id_number = party_parsed['id_number']
party_original = party_parsed['original']
# 解析委托人和相对方信息(支持 JSON 数组,提取所有人员的姓名和身份证号)
client_names, client_id_numbers, client_originals = _extract_all_person_info(client_info)
party_names, party_id_numbers, party_originals = _extract_all_person_info(party_info)
# 1. 检索预立案表
prefiling_q = Q(is_deleted=False)
@@ -4359,99 +4364,102 @@ def conflict_search(client_info=None, party_info=None, undertaker=None, bidding_
prefiling_all = PreFiling.objects.filter(prefiling_q)
for pf in prefiling_all:
if len(matched_prefilings) >= 50: # 限制返回数量
if len(matched_prefilings) >= 50:
break
is_match = False
match_reason = []
# 检查委托人/相对方匹配
if client_info or client_name:
pf_client_names = []
try:
client_data = json.loads(pf.client_username) if isinstance(pf.client_username, str) else pf.client_username
if isinstance(client_data, list):
for item in client_data:
if isinstance(item, dict) and 'name' in item:
pf_client_names.append(item['name'].strip())
elif isinstance(client_data, dict) and 'name' in client_data:
pf_client_names.append(client_data['name'].strip())
except (json.JSONDecodeError, TypeError, AttributeError):
pf_client_names = [pf.client_username.strip()] if pf.client_username else []
pf_party_names = []
try:
party_data = json.loads(pf.party_username) if isinstance(pf.party_username, str) else pf.party_username
if isinstance(party_data, list):
for item in party_data:
if isinstance(item, dict) and 'name' in item:
pf_party_names.append(item['name'].strip())
elif isinstance(party_data, dict) and 'name' in party_data:
pf_party_names.append(party_data['name'].strip())
except (json.JSONDecodeError, TypeError, AttributeError):
pf_party_names = [pf.party_username.strip()] if pf.party_username else []
# 冲突规则:当前委托人只与历史「委托人」匹配,当前相对方只与历史「相对方」匹配
# 检查委托人匹配(仅匹配历史记录的委托人)
if client_info or client_name:
for pf_client in pf_client_names:
if client_name and pf_client in client_name:
# 解析历史记录的委托人/相对方姓名及身份证号
pf_client_names = []
pf_client_ids = []
try:
cd = json.loads(pf.client_username) if isinstance(pf.client_username, str) else pf.client_username
if isinstance(cd, list):
for item in cd:
if isinstance(item, dict):
n = (item.get('name') or '').strip()
if n:
pf_client_names.append(n)
pid = (item.get('idNumber') or item.get('id_number') or '').strip()
if pid:
pf_client_ids.append(pid)
elif isinstance(cd, dict):
n = (cd.get('name') or '').strip()
if n:
pf_client_names.append(n)
pid = (cd.get('idNumber') or cd.get('id_number') or '').strip()
if pid:
pf_client_ids.append(pid)
except (json.JSONDecodeError, TypeError, AttributeError):
if pf.client_username:
pf_client_names = [pf.client_username.strip()]
pf_party_names = []
pf_party_ids = []
try:
pd = json.loads(pf.party_username) if isinstance(pf.party_username, str) else pf.party_username
if isinstance(pd, list):
for item in pd:
if isinstance(item, dict):
n = (item.get('name') or '').strip()
if n:
pf_party_names.append(n)
pid = (item.get('idNumber') or item.get('id_number') or '').strip()
if pid:
pf_party_ids.append(pid)
elif isinstance(pd, dict):
n = (pd.get('name') or '').strip()
if n:
pf_party_names.append(n)
pid = (pd.get('idNumber') or pd.get('id_number') or '').strip()
if pid:
pf_party_ids.append(pid)
except (json.JSONDecodeError, TypeError, AttributeError):
if pf.party_username:
pf_party_names = [pf.party_username.strip()]
# 冲突规则:当前委托人只与历史「委托人」匹配,当前相对方只与历史「相对方」匹配
# 委托人姓名匹配
if client_names and pf_client_names:
for pf_c in pf_client_names:
if any(pf_c in cn for cn in client_names):
is_match = True
match_reason.append(f"委托人姓名匹配:{pf_c}")
break
if not is_match and client_originals:
for pf_c in pf_client_names:
if any(pf_c in co for co in client_originals):
is_match = True
match_reason.append(f"委托人姓名匹配:{pf_client}")
match_reason.append(f"委托人信息匹配:{pf_c}")
break
if client_original and pf_client in client_original:
# 委托人身份证号匹配
if client_id_numbers and pf_client_ids:
for pf_cid in pf_client_ids:
if any(cid in pf_cid or pf_cid in cid for cid in client_id_numbers):
is_match = True
match_reason.append("委托人身份证号匹配")
break
# 相对方姓名匹配
if party_names and pf_party_names:
for pf_p in pf_party_names:
if any(pf_p in pn for pn in party_names):
is_match = True
match_reason.append(f"相对方姓名匹配:{pf_p}")
break
if not any("相对方" in r for r in match_reason) and party_originals:
for pf_p in pf_party_names:
if any(pf_p in po for po in party_originals):
is_match = True
match_reason.append(f"委托人信息匹配:{pf_client}")
match_reason.append(f"相对方信息匹配:{pf_p}")
break
# 检查相对方匹配(仅匹配历史记录的相对方)
if party_info or party_name:
for pf_party in pf_party_names:
if party_name and pf_party in party_name:
is_match = True
match_reason.append(f"相对方姓名匹配:{pf_party}")
break
if party_original and pf_party in party_original:
is_match = True
match_reason.append(f"相对方信息匹配:{pf_party}")
break
# 检查身份证号匹配:委托人身份证只查历史委托人,相对方身份证只查历史相对方
if client_id_number:
try:
client_data = json.loads(pf.client_username) if isinstance(pf.client_username, str) else pf.client_username
if isinstance(client_data, list):
for item in client_data:
if isinstance(item, dict):
pf_id = item.get('idNumber', '') or item.get('id_number', '')
if pf_id and client_id_number in str(pf_id):
is_match = True
match_reason.append(f"委托人身份证号匹配")
break
elif isinstance(client_data, dict):
pf_id = client_data.get('idNumber', '') or client_data.get('id_number', '')
if pf_id and client_id_number in str(pf_id):
is_match = True
match_reason.append(f"委托人身份证号匹配")
except (json.JSONDecodeError, TypeError, AttributeError):
pass
if party_id_number:
try:
party_data = json.loads(pf.party_username) if isinstance(pf.party_username, str) else pf.party_username
if isinstance(party_data, list):
for item in party_data:
if isinstance(item, dict):
pf_id = item.get('idNumber', '') or item.get('id_number', '')
if pf_id and party_id_number in str(pf_id):
is_match = True
match_reason.append(f"相对方身份证号匹配")
break
elif isinstance(party_data, dict):
pf_id = party_data.get('idNumber', '') or party_data.get('id_number', '')
if pf_id and party_id_number in str(pf_id):
is_match = True
match_reason.append(f"相对方身份证号匹配")
except (json.JSONDecodeError, TypeError, AttributeError):
pass
# 相对方身份证号匹配
if party_id_numbers and pf_party_ids:
for pf_pid in pf_party_ids:
if any(pid in pf_pid or pf_pid in pid for pid in party_id_numbers):
is_match = True
match_reason.append("相对方身份证号匹配")
break
# 检查承办人员匹配
if undertaker and pf.Undertaker:
@@ -4480,32 +4488,31 @@ def conflict_search(client_info=None, party_info=None, undertaker=None, bidding_
project_records = ProjectRegistration.objects.filter(project_q)
project_list = []
for pro in project_records[:50]: # 限制查询数量
for pro in project_records[:50]:
is_match = False
match_reason = []
# 冲突规则:当前委托人只与历史「委托人」匹配,当前相对方只与历史「相对方」匹配
if (client_info or client_name) and pro.client_info:
if client_name and client_name in pro.client_info:
if (client_names or client_id_numbers) and pro.client_info:
if client_names and any(cn in pro.client_info for cn in client_names):
is_match = True
match_reason.append("委托人姓名匹配")
elif client_original and client_original in pro.client_info:
elif client_originals and any(co in pro.client_info for co in client_originals):
is_match = True
match_reason.append("委托人信息匹配")
elif client_id_number and client_id_number in pro.client_info:
elif client_id_numbers and any(cid in pro.client_info for cid in client_id_numbers):
is_match = True
match_reason.append("委托人身份证号匹配")
if party_info or party_name:
if pro.party_info:
if party_name and party_name in pro.party_info:
is_match = True
match_reason.append("相对方姓名匹配")
elif party_original and party_original in pro.party_info:
is_match = True
match_reason.append("相对方信息匹配")
elif party_id_number and party_id_number in pro.party_info:
is_match = True
match_reason.append("相对方身份证号匹配")
if (party_names or party_id_numbers) and pro.party_info:
if party_names and any(pn in pro.party_info for pn in party_names):
is_match = True
match_reason.append("相对方姓名匹配")
elif party_originals and any(po in pro.party_info for po in party_originals):
is_match = True
match_reason.append("相对方信息匹配")
elif party_id_numbers and any(pid in pro.party_info for pid in party_id_numbers):
is_match = True
match_reason.append("相对方身份证号匹配")
# 检查负责人匹配
if undertaker and pro.responsiblefor:
@@ -4516,7 +4523,6 @@ def conflict_search(client_info=None, party_info=None, undertaker=None, bidding_
main_lawyer = responsiblefor_data.get('main_lawyer', '')
assistant_lawyer = responsiblefor_data.get('assistant_lawyer', '')
case_manager_lawyer = responsiblefor_data.get('case_manager_lawyer', '')
if undertaker in str(responsible_person) or undertaker in str(main_lawyer) or \
undertaker in str(assistant_lawyer) or undertaker in str(case_manager_lawyer):
is_match = True
@@ -4549,7 +4555,7 @@ def conflict_search(client_info=None, party_info=None, undertaker=None, bidding_
bid_records = Bid.objects.filter(bid_q)
bid_list = []
for bid in bid_records[:50]: # 限制查询数量
for bid in bid_records[:50]:
is_match = False
match_reason = []
@@ -4559,26 +4565,25 @@ def conflict_search(client_info=None, party_info=None, undertaker=None, bidding_
is_match = True
match_reason.append("招标单位匹配")
# 检查委托人/相对方是否在招标单位中(支持姓名和身份证号匹配)
if (client_info or client_name) and bid.BiddingUnit:
if client_name and client_name in bid.BiddingUnit:
# 检查委托人/相对方是否在招标单位中
if (client_names or client_id_numbers) and bid.BiddingUnit:
if client_names and any(cn in bid.BiddingUnit for cn in client_names):
is_match = True
match_reason.append("委托人姓名在招标单位中")
elif client_original and client_original in bid.BiddingUnit:
elif client_originals and any(co in bid.BiddingUnit for co in client_originals):
is_match = True
match_reason.append("委托人信息在招标单位中")
elif client_id_number and client_id_number in bid.BiddingUnit:
elif client_id_numbers and any(cid in bid.BiddingUnit for cid in client_id_numbers):
is_match = True
match_reason.append("委托人身份证号在招标单位中")
if (party_info or party_name) and bid.BiddingUnit:
if party_name and party_name in bid.BiddingUnit:
if (party_names or party_id_numbers) and bid.BiddingUnit:
if party_names and any(pn in bid.BiddingUnit for pn in party_names):
is_match = True
match_reason.append("相对方姓名在招标单位中")
elif party_original and party_original in bid.BiddingUnit:
elif party_originals and any(po in bid.BiddingUnit for po in party_originals):
is_match = True
match_reason.append("相对方信息在招标单位中")
elif party_id_number and party_id_number in bid.BiddingUnit:
elif party_id_numbers and any(pid in bid.BiddingUnit for pid in party_id_numbers):
is_match = True
match_reason.append("相对方身份证号在招标单位中")
@@ -4617,10 +4622,14 @@ class ConflictSearch(APIView):
- exclude_project_id: 要排除的立项ID可选
- exclude_bid_id: 要排除的投标ID可选
"""
raw_client = request.data.get('client_info')
raw_party = request.data.get('party_info')
client_info = _normalize_client_party_for_search(raw_client)
party_info = _normalize_client_party_for_search(raw_party)
# client_info / party_info 支持 JSON 字符串(如 '[{"name":"张三","idNumber":"111"}]')或普通字符串
client_info = request.data.get('client_info') or None
party_info = request.data.get('party_info') or None
# 如果是字符串,做 strip如果是 list/dict 保持原样conflict_search 内部会用 _extract_all_person_info 解析)
if isinstance(client_info, str):
client_info = client_info.strip() or None
if isinstance(party_info, str):
party_info = party_info.strip() or None
undertaker = request.data.get('undertaker', '').strip() if request.data.get('undertaker') else None
bidding_unit = request.data.get('bidding_unit', '').strip() if request.data.get('bidding_unit') else None
exclude_prefiling_id = request.data.get('exclude_prefiling_id')