# -*- coding: utf-8 -*- ''' 脚本说明:普康健康规则和策略模型 ''' #导入模块 import json import re from datetime import datetime, date from dateutil.relativedelta import relativedelta import pandas import numpy #规则模版 decisions = { '中银保规则模版一': { '出险人姓名非空': None, '出险人证件类型为居民身份证且通过校验码校验': None, '出险人证件有效': None, '主被保险人姓名非空': None, '主被保险人证件类型为居民身份证且通过校验码校验': None, '主被保险人证件有效': None, '领款人姓名非空': None, '领款人证件类型为居民身份证且通过校验码校验': None, '领款人证件有效': None, '出险人和主被保险人关系与所选一致': None, '出险人和领款人关系与所选一致': None, '开户行非空': None, '银行账户通过LUHN校验': None, '联系电话非空': None, '联系电话通过校验': None, #'证件资料数大于等于2': None, #'证件资料包含证件正面': None, #'证件资料包含证件背面': None, #'票据资料数大于等于1': None, #'未分类资料数等于0': None, '票据就诊类型均非住院治疗': None, '票据开具日期起期和开具日期止期均为同一日': None, '票据诊断疾病均非空': None, '票据备注均非疾病': None, '票据合理金额、部分自费和全部自费之和大于0': None, '票据关联责任均为同一类型': None, '理算第一行保额大于0': None }, '中银保规则模版二': { '出险人姓名非空': None, '出险人证件类型为居民身份证且通过校验码校验': None, '出险人证件有效': None, '主被保险人姓名非空': None, '主被保险人证件类型为居民身份证且通过校验码校验': None, '主被保险人证件有效': None, '领款人姓名非空': None, '领款人证件类型为居民身份证且通过校验码校验': None, '领款人证件有效': None, '出险人和主被保险人关系与所选一致': None, '出险人和领款人关系与所选一致': None, '开户行非空': None, '银行账户通过LUHN校验': None, '联系电话非空': None, '联系电话通过校验': None, #'证件资料数大于等于2': None, #'证件资料包含证件正面': None, #'证件资料包含证件背面': None, #'票据资料数大于等于1': None, #'未分类资料数等于0': None, '票据就诊类型均为药店购药': None, '票据开具日期起期和开具日期止期均为同一日': None, '票据诊断疾病均非空': None, '票据备注均非疾病': None, '票据合理金额、部分自费和全部自费之和大于0': None, '票据关联责任均为同一类型': None, '理算第一行保额大于0': None }, '永诚规则模版一': { '出险人姓名非空': None, '出险人证件类型为居民身份证且通过校验码校验': None, '非空出险人证件有效': None, '线下报案出险地址非空': None, '主被保险人姓名非空': None, '主被保险人证件类型为居民身份证且通过校验码校验': None, '非空主被保险人证件有效': None, '领款人姓名非空': None, '领款人证件类型为居民身份证且通过校验码校验': None, '赔付金额超过一万元领款人证件有效': None, '开户行非空': None, '银行账户通过LUHN校验(忽略18位)': None, '联系电话非空': None, '联系电话通过校验': None, '赔付金额超过一万元联系地址非空': None, '未分类资料数等于0': None, '无重复票据提示': None, '票据交款人均为出险人': None, '票据诊断疾病均非空': None, '票据合理金额、部分自费和全部自费之和大于0': None, '理算第一行保额大于0': None }, '瑞泰规则模版一': { '出险类型非空': None, '出险人姓名非空': None, '出险人证件类型为居民身份证且通过校验码校验': None, '主被保险人姓名非空': None, '主被保险人证件类型为居民身份证且通过校验码校验': None, '领款人姓名非空': None, '领款人证件类型为居民身份证且通过校验码校验': None, '开户行非空': None, '开户行分行非空': None, '开户行分行所在省市非空': None, '银行账户通过LUHN校验': None, '联系电话非空': None, '联系电话通过校验': None, '联系地址非空': None, '票据交款人均为出险人': None, '票据诊断疾病均非空': None, '理算第一行保额大于0': None, '票据合理金额、部分自费和全部自费之和大于0': None, '赔付金额小于3000': None } } class Cognition: def __init__(self, extractions): self.extractions = extractions #数据转化 def transform(self): data = { '票据影像件序号': [], '申请书影像件序号': [], '证件影像件序号': [], '银行卡折影像件序号': [], '未分类影像件序号': [] } #遍历解析抽取内容 for extraction in self.extractions: #遍历键值对 for key, value in list(extraction.items()): match key: case '票据影像件序号': try: content = value.split(':')[1] #仅将关联票据序号保存 if content != '' and content.isdigit(): data['票据影像件序号'].append(content) except: data['票据影像件序号'].append(value) case '申请书影像件序号': data['申请书影像件序号'].append(value) case '证件影像件类型': match value: case '身份证正面': data['证件影像件序号'].append('居民身份证正面') case '身份证反面': data['证件影像件序号'].append('居民身份证背面') case default: data['证件影像件序号'].append(value) case '银行卡折影像件序号': data['银行卡折影像件序号'].append(value) case '未分类影像件序号': data['未分类影像件序号'].append(value) case '报案保单号': #原始报案保单号为“客户报案保单:20241201000”,其中20241201000为报案保单号 try: content = value.split(':')[1] except: content = None data.update({key: content}) case '报案方式': match value: case '赔案来源:线下': content = '线下报案' case '赔案来源:线上': content = '线上报案' case default: content = None data.update({key: content}) case '理赔类型': match value: case '理赔类型:门诊': content = '门急诊就诊' case '理赔类型:其他': content = '药店购药' case default: content = None data.update({key: content}) case condition if '证件号' in key: data.update({key: value}) case condition if '证件类型' in key: match value: case '身份证': content = '居民身份证' case default: content = None data.update({key: content}) case condition if '证件有效期' in key: #在永诚审核页面证件有效期日期格式为%Y-%m-%d,在瑞泰审核页面证件有效期日期格式为%Y%m%d。先尝试按照日期格式%Y-%m-%d,再尝试按照日期格式%Y%m%d解析 try: #若证件有效期为9999-12-31则转为2099-12-31 if value == '9999-12-31': value = '2099-12-31' content = datetime.strptime(value, '%Y-%m-%d').date() except: try: #若证件有效期为99991231则转为20991231 if value == '99991231': value = '20991231' content = datetime.strptime(value, '%Y%m%d').date() except: content = None data.update({key: content}) case '出险人与主被保险人关系' | '出险人与领款人关系': match value: case '本人' | '': content = '本人' case default: content = None data.update({key: content}) case '银行账户': try: content = value.replace(' ', '') except: content = None data.update({key: content}) case '票据提示': if '发票查重:' in value: content = '票据重复提示' else: content = '无票据重复提示' data.update({key: content}) case '保单信息': content = [] #遍历保单信息 for slip in value: slip_content = {} for nested_key, nested_value in list(slip.items()): match nested_key: case '保障期': try: slip_content.update({'保障期起期': datetime.strptime(nested_value.split(' - ')[0].split(' ')[0], '%Y-%m-%d').date()}) slip_content.update({'保障期止期': datetime.strptime(nested_value.split(' - ')[1].split(' ')[0], '%Y-%m-%d').date()}) except: slip_content.update({'保障期起期': None}) slip_content.update({'保障期止期': None}) case default: slip_content.update({nested_key: nested_value}) content.append(slip_content) data.update({key: content}) case '票据信息': content = [] #遍历票据 for invoice in value: invoice_content = {} for nested_key, nested_value in list(invoice.items()): match nested_key: case '就诊类型': match nested_value: case '药房': nested_content = '药店购药' case '门/急诊': nested_content = '门急诊就诊' case '住院': nested_content = '住院治疗' case default: nested_content = None invoice_content.update({nested_key: nested_content}) case '开具日期': #在永诚审核页面开具日期日期格式为%Y-%m-%d,在瑞泰审核页面开具日期格式为%Y%m%d。先尝试按照日期格式%Y-%m-%d,再尝试按照日期格式%Y%m%d解析 try: invoice_content.update({'开具日期起期': datetime.strptime(nested_value.split(' - ')[0], '%Y-%m-%d').date()}) invoice_content.update({'开具日期止期': datetime.strptime(nested_value.split(' - ')[1], '%Y-%m-%d').date()}) except: try: invoice_content.update({'开具日期起期': datetime.strptime(nested_value.split(' - ')[0], '%Y%m%d').date()}) invoice_content.update({'开具日期止期': datetime.strptime(nested_value.split(' - ')[1], '%Y%m%d').date()}) except: invoice_content.update({'开具日期起期': None}) invoice_content.update({'开具日期止期': None}) case '票据验真': match nested_value: case '真票': nested_content = '已验真' case '换开': nested_content = '已验换开' case '红冲': nested_content = '已验红冲' case '假票': nested_content = '已验假票' case '无法验真/查无此票': nested_content = '无法查验' case '未验真': nested_content = '未查验' case default: nested_content = None invoice_content.update({nested_key: nested_content}) case condition if '金额' in nested_key or '自费' in nested_key: try: invoice_content.update({nested_key: float(nested_value)}) except: invoice_content.update({nested_key: None}) case default: invoice_content.update({nested_key: nested_value}) content.append(invoice_content) data.update({key: content}) case '理算第一行保额': try: content = float(value) except: content = None data.update({key: content}) case '审核结论': match value: case '赔付': content = 1 case condition if value in ['拒赔', '拒付']: content = 0 case default: content = None data.update({key: content}) case condition if '金额' in key or '保额' in key: try: data.update({key: float(value)}) except: data.update({key: None}) case default: data.update({key: value}) return data #理算前认知模型 def before_adjustment(self, insurance): data = self.transform() #自动审核默认为是 auto_audit = True #所选保单索引 slip_index = None #所选保单所属保险分公司 slip_insurance_branch = None #所选保单保障期起期 slip_assurance_start = None #所选保单保障期止期 slip_assurance_end = None try: #若报案方式为线上报案则将报案保单作为所选保单 if data.get('报案方式') == '线上报案' and isinstance(data.get('报案保单号'), str): #遍历保单信息查询保单号与报案保单号相同的保单 for index, slip in enumerate(data.get('保单信息')): if slip.get('保单号') == data.get('报案保单号'): #获取所选保单索引 slip_index = index + 1 #获取所选保单保险分公司 slip_insurance_branch = slip.get('保险分公司') #获取所选保单保障期起期 slip_assurance_start = slip.get('保障期起期') #获取所选保单保障期止期 slip_assurance_end = slip.get('保障期止期') #若报案方式非线上报案则先根据票据定义出险日期,再根据出险日期匹配保单 else: try: #定义出险日期 occurrence_date = datetime.strptime('0001-01-01', '%Y-%m-%d').date() #遍历票据以最大开具日期起期作为出险日期 for invoice in data.get('票据信息'): if invoice.get('开具日期起期') > occurrence_date: occurrence_date = invoice.get('开具日期起期') #遍历保单查询保单保障期起期小于等于出险日期且保单保障期止期大于等于出险日期且保险分公司名称包含保险总公司名称的第一张保单作为所选保单 for index, slip in enumerate(data.get('保单信息')): if slip.get('保障期起期') <= occurrence_date and slip.get('保障期止期') >= occurrence_date and insurance in slip.get('保险分公司'): slip_index = index + 1 slip_insurance_branch = slip.get('保险分公司') slip_assurance_start = slip.get('保障期起期') slip_assurance_end = slip.get('保障期止期') break #断定所选保单索引非空 assert slip_index is not None #若根据票据无法匹配保单则以保险分公司名称包含保险总公司名称的最后一张保单作为所选保单 except: for index, slip in range(len(data.get('保单信息')), 0, -1): if insurance in slip.get('保险分公司'): slip_index = index slip_insurance_branch = slip.get('保险分公司') slip_assurance_start = slip.get('保障期起期') slip_assurance_end = slip.get('保障期止期') break #若上述无法选择保单则人工审核 except: auto_audit = False #拒付票据默认为否 invoices_refuse = False #需要拒付的票据索引 invoices_indices = { '不在保单保障期的票据索引': [], '交款人非出险人的票据索引': [], '已验换开的票据索引': [], '已验红冲的票据索引': [], '已验假票的票据索引': [], '无法验真的票据索引': [], '未验真的票据索引': [] } #需要拒付的票据号(用于拼接赔付时结论原因) invoices_numbers = { '不在保单保障期的票据号': [], '交款人非出险人的票据号': [], '已验换开的票据号': [], '已验红冲的票据号': [], '已验假票的票据号': [], '无法验真的票据号': [], '未验真的票据号': [] } #遍历票据 for index, invoice in enumerate(data.get('票据信息')): index += 1 #若票据开具日期起期小于保单保障期或票据开具日期止期大于保单保障期止期则将该票据索引添加至不在保单保障期的票据索引 if invoice.get('开具日期起期') < slip_assurance_start or invoice.get('开具日期止期') > slip_assurance_end: invoices_indices.get('不在保单保障期的票据索引').append(index) invoices_numbers.get('不在保单保障期的票据号').append(invoice.get('票据号')) #若交款人非出险人则将该票据索引添加至交款人非出险人的票据索引 if invoice.get('交款人') != data.get('出险人姓名'): invoices_indices.get('交款人非出险人的票据索引').append(index) invoices_numbers.get('交款人非出险人的票据号').append(invoice.get('票据号')) #根据票据查验结果匹配(若票据验真结果为空默认按照票据赔付处理) match invoice.get('票据验真'): case '已验换开': invoices_indices.get('已验换开的票据索引').append(index) invoices_numbers.get('已验换开的票据号').append(invoice.get('票据号')) case '已验红冲': invoices_indices.get('已验红冲的票据索引').append(index) invoices_numbers.get('已验红冲的票据号').append(invoice.get('票据号')) case '已验假票': invoices_indices.get('已验假票的票据索引').append(index) invoices_numbers.get('已验假票的票据号').append(invoice.get('票据号')) case '无法查验': invoices_indices.get('无法验真的票据索引').append(index) invoices_numbers.get('无法验真的票据号').append(invoice.get('票据号')) case '未查验': invoices_indices.get('未验真的票据索引').append(index) invoices_numbers.get('未验真的票据号').append(invoice.get('票据号')) #根据保险分公司判断是否需修改票据不合理金额 match slip_insurance_branch: #针对永诚(目前是辽宁、黑龙江和浙江分公司) #拒付不在保单保单保障期、交款人非出险人、查验结果为已验换开/红冲/假票的票据 #人工处理查验结果为无法查验/未验真的票据 case condition if '永诚财产保险股份有限公司' in slip_insurance_branch: for key, value in list(invoices_indices.items()): if key in ['不在保单保障期的票据索引', '交款人非出险人的票据索引', '已验换开的票据索引', '已验红冲的票据索引', '已验假票的票据索引'] and value != []: invoices_refuse = True if key in ['无法验真的票据索引', '未验真的票据索引'] and value != []: invoices_indices[key] = [] auto_audit = False #针对瑞泰(目前是北京分公司) #拒付不在保单保单保障期、交款人非出险人、查验结果为已验换开/红冲/假票的票据 #人工处理查验结果为无法查验 #赔付查验结果为未验真的票据(按照余富强老师规则配置) case condition if '瑞泰人寿保险有限公司' in slip_insurance_branch: for key, value in list(invoices_indices.items()): if key in ['不在保单保障期的票据索引', '交款人非出险人的票据索引', '已验换开的票据索引', '已验红冲的票据索引', '已验假票的票据索引'] and value != []: invoices_refuse = True if key in ['无法验真的票据索引'] and value != []: invoices_indices[key] = [] auto_audit = False if key in ['未验真的票据索引'] and value != []: invoices_indices[key] = [] #默认 #拒付不在保单保单保障期、交款人非出险人的票据 case default: for key, value in list(invoices_indices.items()): if key in ['不在保单保障期的票据索引', '交款人非出险人的票据索引'] and value != []: invoices_refuse = True if key in ['已验换开的票据索引', '已验红冲的票据索引', '已验假票的票据索引', '无法验真的票据索引', '未验真的票据索引'] and value != []: invoices_indices[key] = [] #若需要拒付的票据索引为空列表则删除 for key, value in list(invoices_indices.items()): if value == []: invoices_indices.pop(key) #根据保险分公司匹配赔付时结论原因拼接方案 match slip_insurance_branch: #针对永诚和瑞泰 #赔付时结论原因仅保留不在保单保单保障期、交款人非出险人、查验结果为已验换开/红冲/假票的票据号 case condition if '永诚财产保险股份有限公司' in slip_insurance_branch or '瑞泰人寿保险有限公司' in slip_insurance_branch: for key, value in list(invoices_numbers.items()): if key not in ['不在保单保障期的票据号', '交款人非出险人的票据号', '已验换开的票据号', '已验红冲的票据号', '已验假票的票据号'] and value != []: invoices_numbers[key] = [] #默认 #赔付时结论原因仅保留不在保单保单保障期、交款人非出险人的票据号 case default: for key, value in list(invoices_numbers.items()): if key not in ['不在保单保障期的票据号', '交款人非出险人的票据号'] and value != []: invoices_numbers[key] = [] #赔付时结论原因 payment_remark = '' #遍历所该票据索引,若票据索引列表非空列表则拼接至赔付时结论原因(不包括未验真,保司要求TPA验真,如果显示未验真则我司过错) for key, value in list(invoices_numbers.items()): if invoices_numbers.get(key) != []: match key: case '不在保单保障期的票据号': payment_remark += ','.join(invoices_numbers.get(key)) + '不在保单保障期歉难给付;' case '交款人非出险人的票据号': payment_remark += ','.join(invoices_numbers.get(key)) + '非本人发票歉难给付;' case '已验换开的票据号': payment_remark += ','.join(invoices_numbers.get(key)) + '换开歉难给付;' case '已验红冲的票据号': payment_remark += ','.join(invoices_numbers.get(key)) + '红冲歉难给付;' case '已验假票的票据号': payment_remark += ','.join(invoices_numbers.get(key)) + '假票歉难给付;' case '无法验真的票据号': payment_remark += ','.join(invoices_numbers.get(key)) + '无法查验歉难给付;' case '未验真的票据号': payment_remark += ','.join(invoices_numbers.get(key)) + '未查验歉难给付;' #若复核人为空则人工审核 if data.get('复核人') != '无': auto_audit = False #若票据提示为“票据重复提示”则人工审核 if data.get('票据提示') == '票据重复提示': auto_audit = False #根据保险分公司判断是否可自动审核 match slip_insurance_branch: #针对瑞泰(目前是北京分公司) #若开户行包含平安则人工审核 case condition if '瑞泰人寿保险有限公司' in slip_insurance_branch: if '平安' in data.get('开户行'): auto_audit = False #在线报案默认为否 case_report = False #根据保险分公司判断需要在线报案 match slip_insurance_branch: case condition if '永诚财产保险股份有限公司' in slip_insurance_branch and data.get('报案方式') == '线下报案': case_report = True return {'自动审核': auto_audit, '所选保单索引': slip_index, '所选保单所属保险分公司': slip_insurance_branch, '票据拒付': invoices_refuse, '拒付票据索引': invoices_indices, '赔付结论原因': payment_remark, '在线报案': case_report, '转换数据': data} #校验:数据类型为字符且非空字符 def is_string(self, content): try: assert isinstance(content, str) and content != '' result = 1 except: result = 0 finally: return result #校验:若证件类型为居民身份证则满足校验码校验 def is_identification_card(self, content): try: assert isinstance(content, str) and content != '' #使用居民身份证校验码校验时前十七位系数 coefficients = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] #获取校验码索引 index = sum([int(x) * y for x, y in zip(content[: -1], coefficients)]) % 11 #断定校验码和最后一位相同 assert content[-1].upper() == '10X98765432'[index] result = 1 except: result = 0 finally: return result #校验:根据证件有效期止期校验证件有效 def is_valid(self, content): try: assert isinstance(content, date) data = self.transform() #统计所有票据最小开具日期作为出险日期 occurrence_date = min([invoice.get('开具日期起期') for invoice in data.get('票据信息')]) #断定证件有效期止期大于等于出险日期 assert content >= occurrence_date result = 1 except: result = 0 finally: return result #校验:根据居民身份证中有效期校验已成年 def is_adult(self, content): try: assert isinstance(content, str) and content != '' data = self.transform() #统计所有票据最小开具日期作为出险日期 occurrence_date = min([invoice.get('开具日期起期') for invoice in data.get('票据信息')]) #断定居民身份证中出生日期和出险日期的相差年份大于等于十八 assert relativedelta(occurrence_date, datetime.strptime(content[6: 14], '%Y%m%d')).years >= 18 result = 1 except: result = 0 finally: return result #校验:根据与出险人关系校验证件号相同 def verify_relationship(self, content): match content: case '主被保险人': try: data = self.transform() relationship_type = data.get('出险人与主被保险人关系') match relationship_type: case '本人': assert data.get('出险人证件号') == data.get('主被保险人证件号') case default: assert data.get('出险人证件号') != data.get('主被保险人证件号') result = 1 except: result = 0 case '领款人': try: data = self.transform() relationship_type = data.get('出险人与领款人关系') match relationship_type: case '本人' | None: assert data.get('出险人证件号') == data.get('领款人证件号') case default: assert data.get('出险人证件号') != data.get('领款人证件号') result = 1 except: result = 0 case default: result = 0 return result #校验:银行账户满足LUHN校验 def is_bank_card(self, content): #银行卡 #若银行账户位数大于等于16且小于等于19则使用LUHN校验 if len(content) >= 16 and len(content) <= 19: #将银行账户转为数字列表 digits = [int(digit) for digit in content if digit.isdigit()] digits_sum = 0 for index, digit in enumerate(reversed(digits)): #若数字索引加上一为偶数则数字乘以二 if (index + 1) % 2 == 0: digit *= 2 #若数字大于九则减去九 if digit > 9: digit -= 9 digits_sum += digit #断定数字和能够被十整除 if digits_sum % 10 == 0: result = 1 else: result = 0 #存折 else: result = 1 return result #校验:联系电话数据类型为字符,11位,每一位均为数字、第一位为1、第二位为3、4、5、6、7、8、9、后面每一位均为0、1、2、3、4、5、6、7、8、9 def is_mobile_phone(self, content): try: #断定数据类型为字符且为11位 assert isinstance(content, str) and len(content) == 11 #遍历每一位 for index in range(len(content)): #断定每一位均为数字 assert content[index].isdigit() match index: case 0: assert content[index] == '1' case 1: assert content[index] in '3456789' case default: assert content[index] in '0123456789' result = 1 except: result = 0 finally: return result #生成规则结果并决策 def after_adjustment(self, insurance): data = self.transform() #解析所选保单所属分公司 slip_insurance_branch = self.before_adjustment(insurance = insurance).get('所选保单所属保险分公司') #默认足额赔付为True payment_complement = True #根据保险分公司匹配足额赔付处理方式 match slip_insurance_branch: case condition if '瑞泰人寿保险有限公司' in slip_insurance_branch: #统计所有票据的合理金额 reasonable_amounts = sum([invoice.get('合理金额') for invoice in data.get('票据信息')]) #统计所有票据的部分自费 part_self_amounts = sum([invoice.get('部分自费') for invoice in data.get('票据信息')]) #统计所有票据的全部自费 all_self_amounts = sum([invoice.get('全部自费') for invoice in data.get('票据信息')]) #若第一行保额小于合理金额、部分自费和全部自费之和则足额赔付为False if data.get('理算第一行保额') < reasonable_amounts + part_self_amounts + all_self_amounts: payment_complement = False decision = {'赔案号': data.get('赔案号'), '审核结论': data.get('审核结论'), '自动化:审核说明': '', '足额赔付': payment_complement} #根据保险分公司匹配规则和决策模型 match slip_insurance_branch: case condition if slip_insurance_branch in ['中银保险有限公司广东分公司', '中银保险有限公司广州中心支公司', '中银保险有限公司东莞中心支公司', '中银保险有限公司惠州中心支公司', '中银保险有限公司肇庆中心支公司', '中银保险有限公司广西分公司', '中银保险有限公司天津分公司', '中银保险有限公司云南分公司', '中银保险有限公司陕西分公司', '中银保险有限公司苏州分公司', '中银保险有限公司扬州中心支公司', '中银保险有限公司无锡中心支公司', '中银保险有限公司泰州中心支公司']: decision.update({'规则结果': decisions.get('中银保规则模版一')}) case condition if slip_insurance_branch in ['中银保险有限公司江西分公司', '中银保险有限公司吉林分公司', '中银保险有限公司辽宁分公司', '中银保险有限公司营口中心支公司']: decision.update({'规则结果': decisions.get('中银保规则模版二')}) case condition if slip_insurance_branch in ['永诚财产保险股份有限公司黑龙江分公司']: decision.update({'规则结果': decisions.get('永诚规则模版一')}) case condition if slip_insurance_branch in ['瑞泰人寿保险有限公司北京分公司']: decision.update({'规则结果': decisions.get('瑞泰规则模版一')}) case default: raise Exception('保险分公司 {} 未配置规则和决策模型'.format(slip_insurance_branch)) #遍历规则键值对 for key, value in list(decision.get('规则结果').items()): match key: case '出险类型非空': result = self.is_string(content = data.get('出险类型')) decision['规则结果'].update({key: result}) #若规则结果为0则补充审核说明 if result == 0: decision.update({'自动化:审核说明': '{}出险类型为空;'.format(decision.get('自动化:审核说明'))}) case '出险人姓名非空': result = self.is_string(content = data.get('出险人姓名')) decision['规则结果'].update({key: result}) #若规则结果为0则补充审核说明 if result == 0: decision.update({'自动化:审核说明': '{}出险人姓名为空;'.format(decision.get('自动化:审核说明'))}) case '出险人证件类型为居民身份证且通过校验码校验': result = self.is_identification_card(content = data.get('出险人证件号')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}出险人居民身份证有误;'.format(decision.get('自动化:审核说明'))}) case '出险人证件有效': #若出险人证件有效期止期为空则判断领款人证件号和是否一致,若一致则取领款人证件有限期止期 if data.get('出险人证件有效期止期') is None: if data.get('出险人证件号') == data.get('领款人证件号'): result = self.is_valid(content = data.get('领款人证件有效期止期')) else: result = 0 else: result = self.is_valid(content = data.get('出险人证件有效期止期')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}出险人证件过期;'.format(decision.get('自动化:审核说明'))}) case '非空出险人证件有效': if data.get('出险人证件有效期止期') is not None: result = self.is_valid(content = data.get('出险人证件有效期止期')) else: result = 1 decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}出险人证件过期;'.format(decision.get('自动化:审核说明'))}) case '出险人已成年': result = self.is_adult(content = data.get('出险人证件号')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}出险人未成年;'.format(decision.get('自动化:审核说明'))}) case '线下报案出险地址非空': if data.get('报案方式') == '线下报案': result = self.is_string(content = data.get('出险地址')) else: result = 1 decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}线下报案出险地址为空;'.format(decision.get('自动化:审核说明'))}) case '主被保险人姓名非空': result = self.is_string(content = data.get('主被保险人姓名')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}主被保险人姓名为空;'.format(decision.get('自动化:审核说明'))}) case '主被保险人证件类型为居民身份证且通过校验码校验': result = self.is_identification_card(content = data.get('主被保险人证件号')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}主被保险人居民身份证有误;'.format(decision.get('自动化:审核说明'))}) case '主被保险人证件有效': result = self.is_valid(content = data.get('主被保险人证件有效期止期')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}主被保险人证件过期;'.format(decision.get('自动化:审核说明'))}) case '非空主被保险人证件有效': if data.get('主被保险人证件有效期止期') is not None: result = self.is_valid(content = data.get('主被保险人证件有效期止期')) else: result = 1 decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}出险人证件过期;'.format(decision.get('自动化:审核说明'))}) case '领款人姓名非空': result = self.is_string(content = data.get('领款人姓名')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}领款人姓名为空;'.format(decision.get('自动化:审核说明'))}) case '领款人证件类型为居民身份证且通过校验码校验': result = self.is_identification_card(content = data.get('领款人证件号')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}领款人居民身份证有误;'.format(decision.get('自动化:审核说明'))}) case '领款人证件有效': result = self.is_valid(content = data.get('领款人证件有效期止期')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}领款人证件过期;'.format(decision.get('自动化:审核说明'))}) case '赔付金额超过一万元领款人证件有效': if data.get('理算金额') >= 10000: result = self.is_valid(content = data.get('领款人证件有效期止期')) else: if self.is_string(content = data.get('领款人证件有效期止期')) == 1: result = self.is_valid(content = data.get('领款人证件有效期止期')) else: result = 1 decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}领款人证件过期;'.format(decision.get('自动化:审核说明'))}) case '出险人和主被保险人关系与所选一致': result = self.verify_relationship(content = '主被保险人') decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}出险人和主被保险人非同一人;'.format(decision.get('自动化:审核说明'))}) case '出险人和领款人关系与所选一致': result = self.verify_relationship(content = '领款人') decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}出险人和领款人非同一人;'.format(decision.get('自动化:审核说明'))}) case '开户行非空': result = self.is_string(content = data.get('开户行')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}开户行为空;'.format(decision.get('自动化:审核说明'))}) case '开户行分行非空': result = self.is_string(content = data.get('开户行分行')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}开户行分行为空;'.format(decision.get('自动化:审核说明'))}) case '开户行分行所在省市非空': result = self.is_string(content = data.get('开户行分行所在省市')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}开户行分行为空;'.format(decision.get('自动化:审核说明'))}) case '开户行在指定范围之内': #根据保险分公司匹配开户行范围 match slip_insurance_branch: case condition if '中银保险有限公司' in slip_insurance_branch: try: assert data.get('开户行') in banks.get('中银保险有限公司') result = 1 except: result = 0 case default: result = 0 decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}开户行不支持;'.format(decision.get('自动化:审核说明'))}) case '银行账户通过LUHN校验': result = self.is_bank_card(content = data.get('银行账户')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}银行账户有误;'.format(decision.get('自动化:审核说明'))}) case '银行账户通过LUHN校验(忽略18位)': result = self.is_bank_card(content = data.get('银行账户')) #24年11月13日就永诚黑龙江分公司跑批规则时,因赔案245005967032的银行账户为存折类故与业务商定若银行账户为18位则不校验 if len(data.get('银行账户')) == 18: result = 1 decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}银行账户有误;'.format(decision.get('自动化:审核说明'))}) case '联系电话非空': result = self.is_string(content = data.get('联系电话')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}联系电话为空;'.format(decision.get('自动化:审核说明'))}) case '联系电话通过校验': result = self.is_mobile_phone(content = data.get('联系电话')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}联系电话有误;'.format(decision.get('自动化:审核说明'))}) case '联系地址非空': result = self.is_string(content = data.get('联系地址')) decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}联系地址为空;'.format(decision.get('自动化:审核说明'))}) case '赔付金额超过一万元联系地址非空': if data.get('理算金额') >= 10000: result = self.is_string(content = data.get('联系地址')) else: result = 1 decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}赔付金额超过一万元联系地址为空;'.format(decision.get('自动化:审核说明'))}) case '证件资料数大于等于2': try: assert len(data.get('证件影像件序号')) >= 2 result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}证件资料不全;'.format(decision.get('自动化:审核说明'))}) case '证件资料包含证件正面': try: assert '居民身份证正面' in data.get('证件影像件序号') result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}无证件正面;'.format(decision.get('自动化:审核说明'))}) case '证件资料包含证件背面': try: assert '居民身份证背面' in data.get('证件影像件序号') result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}无证件背面;'.format(decision.get('自动化:审核说明'))}) case '银行卡折资料数等于1': try: #若报案方式为线上报案则检查银行卡折资料数 if data.get('报案方式') == '线上报案': assert len(data.get('银行卡折影像件序号')) == 1 result = 1 else: result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}银行卡折资料不全;'.format(decision.get('自动化:审核说明'))}) case '申请资料数等于1': try: assert len(data.get('申请书影像件序号')) == 1 result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}申请资料不全;'.format(decision.get('自动化:审核说明'))}) case '票据资料数大于等于1': try: assert len(data.get('票据影像件序号')) >= 1 result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据资料不全;'.format(decision.get('自动化:审核说明'))}) case '未分类资料数等于0': try: assert len(data.get('未分类影像件序号')) == 0 result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}有未分类影像件;'.format(decision.get('自动化:审核说明'))}) case '票据资料均已关联票据序号': try: #统计去重后影像件关联票据序号数和票据数是否相同(但是暂无法判断影像件关联票据序号是否正确) assert len(list(set(data.get('票据影像件序号')))) == len([invoice.get('票据序号') for invoice in data.get('票据信息')]) result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据资料关联票据序号错误;'.format(decision.get('自动化:审核说明'))}) case '无重复票据提示': try: assert data.get('票据提示') == '无票据重复提示' result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}有票据提示;'.format(decision.get('自动化:审核说明'))}) case '票据交款人均为出险人': try: assert all([invoice.get('交款人') == data.get('出险人姓名') for invoice in data.get('票据信息')]) result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据交款人非出险人;'.format(decision.get('自动化:审核说明'))}) case '票据就诊类型均为药店购药': try: #断定票据就诊类型均非住院治疗 assert all([invoice.get('就诊类型') == '药店购药' for invoice in data.get('票据信息')]) result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据就诊类型包括住院治疗;'.format(decision.get('自动化:审核说明'))}) case '票据就诊类型均非住院治疗': try: #断定票据就诊类型均非住院治疗 assert all([invoice.get('就诊类型') != '住院治疗' for invoice in data.get('票据信息')]) result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据就诊类型包括住院治疗;'.format(decision.get('自动化:审核说明'))}) case '票据开具日期起期和开具日期止期均为同一日': try: #断定票据开具日期起期和止期均相同 assert all([invoice.get('开具日期起期') == invoice.get('开具日期止期') for invoice in data.get('票据信息')]) result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据包含住院日期和出院日期非同一天;'.format(decision.get('自动化:审核说明'))}) case '票据诊断疾病均非空': try: assert all([self.is_string(invoice.get('诊断疾病')) == 1 for invoice in data.get('票据信息')]) result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据包含诊断疾病为空;'.format(decision.get('自动化:审核说明'))}) case '票据备注均非疾病': try: assert all(['非疾病' not in invoice.get('票据备注') for invoice in data.get('票据信息')]) result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据包含备注非疾病;'.format(decision.get('自动化:审核说明'))}) case '票据合理金额、部分自费和全部自费之和大于0': try: #统计所有票据合理金额之和 assert sum([invoice.get('合理金额') + invoice.get('部分自费') + invoice.get('全部自费') for invoice in data.get('票据信息')]) > 0 result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据合理金额、部分自费和全部自费之和等于0;'.format(decision.get('自动化:审核说明'))}) case '票据关联责任均为同一类型': try: #统计去重后票据关联责任数是否等于1 assert len(list(set([invoice.get('关联责任') for invoice in data.get('票据信息')]))) == 1 result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}票据关联多责任;'.format(decision.get('自动化:审核说明'))}) case '理算第一行保额大于0': try: assert data.get('理算第一行保额') > 0 result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}累计赔付已达到个人账户年度限额;'.format(decision.get('自动化:审核说明'))}) case '赔付金额小于3000': try: assert data.get('理算金额') < 3000 result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}理算金额大于等于3000元;'.format(decision.get('自动化:审核说明'))}) case '理算金额通过校验': try: assert data.get('理算金额') == data.get('公账赔付金额') + data.get('个账赔付金额') result = 1 except: result = 0 finally: decision['规则结果'].update({key: result}) if result == 0: decision.update({'自动化:审核说明': '{}理算金额未通过校验'.format(decision.get('自动化:审核说明'))}) #若所有规则结果为1则赔付,否则拒付 if all([value == 1 for key, value in list(decision['规则结果'].items())]): decision.update({'自动化:审核结论': 1}) else: decision.update({'自动化:审核结论': 0}) return decision