diff --git a/utils/client.py b/utils/client.py index 4f01b06..5968eb0 100644 --- a/utils/client.py +++ b/utils/client.py @@ -103,7 +103,9 @@ class CacheClient: try: # 创建缓存数据库连接 self.connection = sqlite3.connect( - database=(Path(__file__).parent.resolve() / database), # 当前目录下创建缓存数据库 + database=( + Path(__file__).parent.resolve() / database + ), # 当前目录下创建缓存数据库 check_same_thread=False, timeout=30, # 缓存数据库锁超时时间(单位:秒),默认为30秒,避免并发锁死 ) @@ -310,6 +312,7 @@ class Arguments(BaseModel): :param stream: 是否启用流式传输 :param guid: 全局唯一标识 """ + # 统一资源定位符 url: HttpUrl = Field(default=...) # 查询参数 @@ -456,17 +459,13 @@ class HTTPClient: # 重构表单数据 if arguments.get("data") is not None: arguments["data"] = { - k: v - for k, v in arguments["data"].items() - if v is not None + k: v for k, v in arguments["data"].items() if v is not None } # 重构JSON数据 if arguments.get("json_data") is not None: arguments["json_data"] = { - k: v - for k, v in arguments["json_data"].items() - if v is not None + k: v for k, v in arguments["json_data"].items() if v is not None } # 重构文件数据 diff --git a/票据理赔自动化/main.py b/票据理赔自动化/main.py index ccd9216..5b28404 100644 --- a/票据理赔自动化/main.py +++ b/票据理赔自动化/main.py @@ -26,7 +26,6 @@ from zen import ZenDecision, ZenEngine from utils.client import Authenticator, HTTPClient - # from utils.ocr import fuzzy_match @@ -478,7 +477,12 @@ if __name__ == "__main__": # noinspection PyShadowingNames def image_recognize( - image_index, image_guid, image_format, image_base64, insurance_branch, image_type + image_index, + image_guid, + image_format, + image_base64, + insurance_branch, + image_type, ) -> None: """ 影像件识别并整合至赔案档案 @@ -624,648 +628,661 @@ if __name__ == "__main__": url=(url := "https://ai.inspirvision.cn/s/api/ocr/invoiceCheckAll"), headers={"X-RequestId-Header": image_guid}, data={ - "token": authenticator.get_token(servicer="szkt"), # 获取深圳快瞳访问令牌 + "token": authenticator.get_token( + servicer="szkt" + ), # 获取深圳快瞳访问令牌 "imgBase64": f"data:image/{image_format.lstrip(".")};base64,{image_base64}", }, - guid=md5((url + image_guid).encode("utf-8")) - .hexdigest() - .upper(), + guid=md5((url + image_guid).encode("utf-8")).hexdigest().upper(), ) - # 若查验为真票或红票则直接整合至赔案档案 + # 若查验成功则直接整合至赔案档案 if response.get("status") == 200 and response.get("code") == 10000: # noinspection PyTypeChecker match response["data"]["productCode"]: # 增值税发票,目前深圳快瞳支持全电和全电纸质增值税发票查验 case "003082": # noinspection PyTypeChecker - receipt.update({ - "查验状态": ( - "真票" - if response["data"]["details"]["invoiceTypeNo"] == "0" - else "红票" - ), - "票据号码": response["data"]["details"]["number"], - "票据代码": ( - response["data"]["details"]["code"] - if response["data"]["details"]["code"] - else None - ), # 全电发票无发票代码,深圳快瞳票据查验接口中票据代码由空字符转为None - "开票日期": datetime.strptime( - response["data"]["details"]["date"], "%Y年%m月%d日" - ).strftime( - "%Y-%m-%d" - ), # 深圳快瞳票据查验接口中开票日期由字符串(%Y年%m月%d日)转为日期 - "校验码": response["data"]["details"]["check_code"], - "开票金额": Decimal(response["data"]["details"]["total"]).quantize( + receipt.update( + { + "查验状态": ( + "真票" + if response["data"]["details"]["invoiceTypeNo"] + == "0" + else "红票" + ), + "票据号码": response["data"]["details"]["number"], + "票据代码": ( + response["data"]["details"]["code"] + if response["data"]["details"]["code"] + else None + ), # 全电发票无发票代码,深圳快瞳票据查验接口中票据代码由空字符转为None + "开票日期": datetime.strptime( + response["data"]["details"]["date"], "%Y年%m月%d日" + ).strftime( + "%Y-%m-%d" + ), # 深圳快瞳票据查验接口中开票日期由字符串(%Y年%m月%d日)转为日期 + "校验码": response["data"]["details"]["check_code"], + "开票金额": Decimal( + response["data"]["details"]["total"] + ).quantize( Decimal("0.00"), rounding=ROUND_HALF_UP, ), # 深圳快瞳票据查验接口中开票金额由字符串转为Decimal,保留两位小数 - "姓名": response["data"]["details"]["buyer"], - "购药及就医机构": response["data"]["details"]["seller"], - "备注": ( - response["data"]["details"]["remark"] - if response["data"]["details"]["remark"] - else None - ), # 深圳快瞳票据查验接口中备注由空字符转为None - "明细层": [ - { - "名称": item["name"], - "规格": ( - item["specification"] - if item["specification"] - else None - ), # 深圳快瞳票据查验接口中明细规则由空字符转为None - "单位": ( - item["unit"] - if item["unit"] - else None - ), # 深圳快瞳票据查验接口中明细单位由空字符转为None - "数量": (Decimal(item["quantity"]).quantize( + "姓名": response["data"]["details"]["buyer"], + "备注": ( + response["data"]["details"]["remark"] + if response["data"]["details"]["remark"] + else None + ), # 深圳快瞳票据查验接口中备注由空字符转为None + "明细层": [ + { + "名称": item["name"], + "规格": ( + item["specification"] + if item["specification"] + else None + ), # 深圳快瞳票据查验接口中明细规则由空字符转为None + "单位": ( + item["unit"] if item["unit"] else None + ), # 深圳快瞳票据查验接口中明细单位由空字符转为None + "数量": ( + Decimal(item["quantity"]).quantize( Decimal("0.00"), rounding=ROUND_HALF_UP, ) - if item["quantity"] - else None - ), # 深圳快瞳票据查验接口中明细单位由空字符转为None,若非空字符由字符串转为Decimal,保留两位小数 - "金额": ( - Decimal(item["total"]) - + Decimal(item["tax"]) + if item["quantity"] + else None + ), # 深圳快瞳票据查验接口中明细单位由空字符转为None,若非空字符由字符串转为Decimal,保留两位小数 + "金额": ( + Decimal(item["total"]) + + Decimal(item["tax"]) ).quantize( Decimal("0.00"), rounding=ROUND_HALF_UP, ), # 深圳快瞳票据查验接口中明细金额税额由字符串转为Decimal,保留两位小数,求和 - } - for item in response["data"]["details"].get("items", []) - ], - }) + } + for item in response["data"]["details"].get( + "items", [] + ) + ], + "购药及就医机构": ( + institution := response["data"]["details"]["seller"] + ), + } + ) # 门诊/住院收费票据 case "003081": # noinspection PyTypeChecker - receipt.update({ - "查验状态": ( - "真票" - if response["data"]["flushedRed"] == "true" - else "红票" - ), - "票据号码": response["data"]["billNumber"], - "票据代码": ( - response["data"]["billCode"] - if response["data"]["billCode"] - else None - ), # 部分地区医疗收费票据无发票代码,深圳快瞳票据查验接口中票据代码由空字符转为None - "开票日期": parse(response["data"][ - "invoiceDate" - ]).strftime( - "%Y-%m-%d" - ), # 深圳快瞳票据查验接口中开票日期由字符串(%Y-%m-%d)转为日期 - "校验码": response["data"]["checkCode"], - "票据金额": Decimal(response["data"]["amount"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, + receipt.update( + { + "查验状态": ( + "真票" + if response["data"]["flushedRed"] == "true" + else "红票" ), - "姓名": response["data"]["payer"], - "购药及就医机构": response["data"]["receivablesInstitution"], - - - - "医保支付": format( - Decimal( - response["data"].get("medicarePay", "0.00") + "票据号码": response["data"]["billNumber"], + "票据代码": ( + response["data"]["billCode"] + if response["data"]["billCode"] + else None + ), # 部分地区医疗收费票据无发票代码,深圳快瞳票据查验接口中票据代码由空字符转为None + "开票日期": parse( + response["data"]["invoiceDate"] + ).strftime( + "%Y-%m-%d" + ), # 深圳快瞳票据查验接口中开票日期由字符串(%Y-%m-%d)转为日期 + "校验码": response["data"]["checkCode"], + "票据金额": Decimal( + response["data"]["amount"] ).quantize( Decimal("0.00"), rounding=ROUND_HALF_UP, ), - ".2f", - ), - "其它支付": format( - Decimal( - response["data"].get("otherPayment", "0.00") - ).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ), - "个人自付": format( - Decimal( - response["data"].get("personalPay", "0.00") - ).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ), - "自付一": format( - Decimal( - response["data"].get("self_pay_one", "0.00") - ).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ), # 深圳快瞳票据查验中就部分地区无自付一 - "自付二": format( - Decimal( - response["data"].get("classificationPays", "0.00") - ).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ), # 深圳快瞳票据查验中就部分地区无自付二 - "个人自费": format( - Decimal( - response["data"].get("personalExpense", "0.00") - ).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ), - "住院日期": ( - parse(date.split("-")[0]).strftime("%Y-%m-%d") - if (date := response["data"].get("hospitalizationDate")) - else None - ), # 深圳快瞳票据查验中就收费票据住院日期格式为%Y%m%d-%Y%m%d,即住院日期-出院日期 - "出院日期": ( - parse(date.split("-")[1]).strftime("%Y-%m-%d") - if date - else None - ), - "医疗机构类型": response["data"]["institutionsType"], - "项目": [ - { - "名称": item["itemName"], - "规格": item[ - "medical_level" - ], # 甲类无自付、乙类有自付、丙类全自付 - "单位": item["unit"], - "数量": format( - Decimal(item["number"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ), - "金额": format( - Decimal(item["totalAmount"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ), - } - for item in response["data"]["feedetails"] - ], - }) - - - # 若请求深圳快瞳票据查验接口或解析发生异常,则根据影像件类型请求深圳快瞳增值税发票/收费票据识别接口 - - # 影像件类型 - image_type = kwargs.get("image_type", globals()["image_type"]) - if image_type is None: - return None - - match image_type: - case "增值税发票": - try: - # 请求深圳快瞳增值税发票识别接口 - response = globals()["http_client"].post( - url=( - url := "https://ai.inspirvision.cn/s/api/ocr/vatInvoice" - ), - headers={"X-RequestId-Header": image_guid}, - data={ - "token": globals()["authenticator"].get_token( - servicer="szkt" - ), - "imgBase64": f"data:image/{image_format};base64,{image_base64}", - }, - guid=hashlib.md5((url + image_guid).encode("utf-8")) - .hexdigest() - .upper(), - ) - # 若深圳快瞳增值税发票识别响应非成功则返回None - if not ( - response.get("status") == 200 - and response.get("code") == 0 - ): - return None - - extraction = { - "票据类型": ( - invoice_type := ( - data := { - item["desc"]: item["value"] - for item in response["data"] - } - ).get("发票类型") - ), - "票据号码": (number := data.get("发票号码")), - "票据代码": data.get("发票代码"), - "开票日期": ( - datetime.strptime(date, "%Y年%m月%d日").strftime( - "%Y-%m-%d" - ) - if re.match( - r"\d{4}年\d{1,2}月\d{1,2}日", - (date := data.get("开票日期")), - ) - else date - ), - "校验码": ( - check_code - if (check_code := data.get("校验码")) - else number - ), # 若校验码为空则默认为票据号码 - "收款方": data.get("销售方名称"), - "付款方": data.get("购买方名称"), - "票据金额": format( + "姓名": response["data"]["payer"], + "购药及就医机构": response["data"][ + "receivablesInstitution" + ], + "医保支付": format( Decimal( - data.get("小写金额").replace("¥", "") - if invoice_type == "电子发票(普通发票)" - else data.get("合计金额(小写)") + response["data"].get("medicarePay", "0.00") ).quantize( Decimal("0.00"), rounding=ROUND_HALF_UP, ), ".2f", ), - "备注": ( - remark if (remark := data.get("备注")) else None - ), - "项目": ( - [ - { - "名称": name, - "规格": ( - specification if specification else None - ), - "单位": unit if unit else None, - "数量": ( - format( - Decimal(quantity).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ) - if quantity - else None - ), - "金额": format( - ( - Decimal(amount) + Decimal(tax) - ).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", # 价税合计,保留两位小数 - ), - } - for name, specification, unit, quantity, amount, tax in zip( - [ - component["value"] - for component in response["data"] - if re.match( - r"^项目名称(\d+)?$", - component["desc"], - ) - ], - [ - component["value"] - for component in response["data"] - if re.match( - r"^规格型号(\d+)?$", - component["desc"], - ) - ], - [ - component["value"] - for component in response["data"] - if re.match( - r"^单位(\d+)?$", - component["desc"], - ) - ], - [ - component["value"] - for component in response["data"] - if re.match( - r"^数量(\d+)?$", - component["desc"], - ) - ], - [ - component["value"] - for component in response["data"] - if re.match( - r"^金额(\d+)?$", - component["desc"], - ) - ], - [ - component["value"] - for component in response["data"] - if re.match( - r"^税额(\d+)?$", - component["desc"], - ) - ], - ) - ] - if invoice_type == "电子发票(普通发票)" - else [ - { - "名称": name, - "数量": format( - Decimal(quantity).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - "0.2f", - ), - "金额": format( - Decimal(amount).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ), - } - for name, quantity, amount in zip( - [ - component["value"] - for component in response["data"] - if re.match( - r"^项目名称明细(\d+)?$", - component["desc"], - ) - ], - [ - component["value"] - for component in response["data"] - if re.match( - r"^项目数量明细(\d+)?$", - component["desc"], - ) - ], - [ - component["value"] - for component in response["data"] - if re.match( - r"^项目金额明细(\d+)?$", - component["desc"], - ) - ], - ) - ] - ), - "查验状态": "无法查验", - } - - # 若查验为假票或无法查验 - else: - if response.get("status") == 400 and (response.get("code") == 10100 or response.get("code") == 10001): - receipt["查验结果"] = "假票" - else: - receipt["查验结果"] = "无法查验" - - - - return extraction - except: - return None - - case _: - try: - # 请求深圳快瞳收费票据识别接口 - response = globals()["http_client"].post( - url=( - url := "https://ai.inspirvision.cn/s/api/ocr/medical" - ), - headers={"X-RequestId-Header": image_guid}, - data={ - "token": globals()["authenticator"].get_token( - servicer="szkt" - ), - "imgBase64": f"data:image/{image_format};base64,{image_base64}", - }, - guid=hashlib.md5((url + image_guid).encode("utf-8")) - .hexdigest() - .upper(), - ) - # 若深圳快瞳收费票据识别响应非成功则返回NONE - if not ( - response.get("status") == 200 - and response.get("code") == 0 - ): - return None - - extraction = { - "票据类型": ( - "门诊收费票据" - if response["data"]["insured"]["receipt_outpatient"] - else "住院收费票据" - ), - "票据号码": ( - receipt := ( - response["data"]["insured"][ - "receipt_outpatient" - ] - or response["data"]["insured"][ - "receipt_hospitalization" - ] - )["receipts"][0] - )["receipt_no"][ - "value" - ], # 默认提取门诊/住院收费票据的第一张票据 - "票据代码": receipt["global_detail"]["invoice_code"][ - "value" - ], - "开票日期": receipt["global_detail"]["invoice_date"][ - "value" - ], # 深圳快瞳收费票据识别中就开票日期格式为%Y-%m-%d - "校验码": fuzzy_match( - target="校验码", - components=receipt["global_detail"][ - "region_specific" - ], - specify_key="name", - return_key="word.value", - ), - "收款方": receipt["hospital_name"]["value"], - "付款方": receipt["name"]["value"], - "票据金额": format( - Decimal(receipt["total_amount"]["value"]).quantize( + "其它支付": format( + Decimal( + response["data"].get("otherPayment", "0.00") + ).quantize( Decimal("0.00"), rounding=ROUND_HALF_UP, ), ".2f", ), - "医保支付": ( - format( - Decimal(field["value"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ) - if isinstance( - (field := receipt.get("medicare_pay")), dict - ) - else None - ), - "其它支付": format( - ( - Decimal(value).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ) - if ( - value := fuzzy_match( - target="其它支付", - components=receipt.get( - "global_detail", {} - ).get("pay_list", []), - specify_key="name", - return_key="word.value", - ) - ) - else None + "个人自付": format( + Decimal( + response["data"].get("personalPay", "0.00") + ).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, ), ".2f", ), - "个人自付": ( - format( - Decimal(field["value"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ) - if isinstance( - (field := receipt.get("self_pay")), dict - ) - else None - ), - "自付一": ( - format( - Decimal(field["value"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ) - if isinstance( - field := (receipt.get("self_pay_one")), dict - ) - else None - ), - "自付二": ( - format( - Decimal(field["value"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ) - if isinstance( - field := (receipt.get("self_pay_two")), dict - ) - else None - ), - "个人自费": ( - format( - Decimal(field["value"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ) - if isinstance( - field := (receipt.get("self_cost")), dict - ) - else None + "自付一": format( + Decimal( + response["data"].get("self_pay_one", "0.00") + ).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ), # 深圳快瞳票据查验中就部分地区无自付一 + "自付二": format( + Decimal( + response["data"].get( + "classificationPays", "0.00" + ) + ).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ), # 深圳快瞳票据查验中就部分地区无自付二 + "个人自费": format( + Decimal( + response["data"].get("personalExpense", "0.00") + ).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", ), "住院日期": ( - datetime.strptime( - field["value"], "%Y%m%d" - ).strftime("%Y-%m-%d") - if isinstance( - field := (receipt.get("starttime")), dict + parse(date.split("-")[0]).strftime("%Y-%m-%d") + if ( + date := response["data"].get( + "hospitalizationDate" + ) ) else None - ), + ), # 深圳快瞳票据查验中就收费票据住院日期格式为%Y%m%d-%Y%m%d,即住院日期-出院日期 "出院日期": ( - datetime.strptime( - field["value"], "%Y%m%d" - ).strftime("%Y-%m-%d") - if isinstance( - field := (receipt.get("endtime")), dict - ) + parse(date.split("-")[1]).strftime("%Y-%m-%d") + if date else None ), - "医疗机构类型": receipt["others"][ - "medical_institution_type" - ]["value"], + "医疗机构类型": response["data"]["institutionsType"], "项目": [ { - "名称": ( - field["value"] - if isinstance( - (field := item["item_name"]), dict - ) - else None + "名称": item["itemName"], + "规格": item[ + "medical_level" + ], # 甲类无自付、乙类有自付、丙类全自付 + "单位": item["unit"], + "数量": format( + Decimal(item["number"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", ), - "规格": ( - field["value"] - if isinstance( - (field := item["specifications"]), dict - ) - else None - ), - "单位": ( - field["value"] - if isinstance((field := item["unit"]), dict) - else None - ), - "数量": ( - format( - Decimal(field["value"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ) - if isinstance( - (field := item["number"]), dict - ) - else None - ), - "金额": ( - format( - Decimal(field["value"]).quantize( - Decimal("0.00"), - rounding=ROUND_HALF_UP, - ), - ".2f", - ) - if isinstance( - (field := item["total_amount"]), dict - ) - else None + "金额": format( + Decimal(item["totalAmount"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", ), } - for item in receipt["feeitems"] + for item in response["data"]["feedetails"] ], - "查验状态": "无法查验", } - return extraction - except: + ) + # 若查验为假票或无法查验 + else: + if response.get("status") == 400 and ( + response.get("code") == 10100 or response.get("code") == 10001 + ): + receipt["查验结果"] = "假票" + else: + receipt["查验结果"] = "无法查验" + + try: + + match image_type: + case "增值税发票": + try: + # 请求深圳快瞳增值税发票识别接口 + response = globals()["http_client"].post( + url=( + url := "https://ai.inspirvision.cn/s/api/ocr/vatInvoice" + ), + headers={"X-RequestId-Header": image_guid}, + data={ + "token": globals()[ + "authenticator" + ].get_token(servicer="szkt"), + "imgBase64": f"data:image/{image_format};base64,{image_base64}", + }, + guid=hashlib.md5( + (url + image_guid).encode("utf-8") + ) + .hexdigest() + .upper(), + ) + # 若深圳快瞳增值税发票识别响应非成功则返回None + if not ( + response.get("status") == 200 + and response.get("code") == 0 + ): + return None + + extraction = { + "票据类型": ( + invoice_type := ( + data := { + item["desc"]: item["value"] + for item in response["data"] + } + ).get("发票类型") + ), + "票据号码": (number := data.get("发票号码")), + "票据代码": data.get("发票代码"), + "开票日期": ( + datetime.strptime( + date, "%Y年%m月%d日" + ).strftime("%Y-%m-%d") + if re.match( + r"\d{4}年\d{1,2}月\d{1,2}日", + (date := data.get("开票日期")), + ) + else date + ), + "校验码": ( + check_code + if (check_code := data.get("校验码")) + else number + ), # 若校验码为空则默认为票据号码 + "收款方": data.get("销售方名称"), + "付款方": data.get("购买方名称"), + "票据金额": format( + Decimal( + data.get("小写金额").replace("¥", "") + if invoice_type == "电子发票(普通发票)" + else data.get("合计金额(小写)") + ).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ), + "备注": ( + remark + if (remark := data.get("备注")) + else None + ), + "项目": ( + [ + { + "名称": name, + "规格": ( + specification + if specification + else None + ), + "单位": unit if unit else None, + "数量": ( + format( + Decimal(quantity).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ) + if quantity + else None + ), + "金额": format( + ( + Decimal(amount) + + Decimal(tax) + ).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", # 价税合计,保留两位小数 + ), + } + for name, specification, unit, quantity, amount, tax in zip( + [ + component["value"] + for component in response[ + "data" + ] + if re.match( + r"^项目名称(\d+)?$", + component["desc"], + ) + ], + [ + component["value"] + for component in response[ + "data" + ] + if re.match( + r"^规格型号(\d+)?$", + component["desc"], + ) + ], + [ + component["value"] + for component in response[ + "data" + ] + if re.match( + r"^单位(\d+)?$", + component["desc"], + ) + ], + [ + component["value"] + for component in response[ + "data" + ] + if re.match( + r"^数量(\d+)?$", + component["desc"], + ) + ], + [ + component["value"] + for component in response[ + "data" + ] + if re.match( + r"^金额(\d+)?$", + component["desc"], + ) + ], + [ + component["value"] + for component in response[ + "data" + ] + if re.match( + r"^税额(\d+)?$", + component["desc"], + ) + ], + ) + ] + if invoice_type == "电子发票(普通发票)" + else [ + { + "名称": name, + "数量": format( + Decimal(quantity).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + "0.2f", + ), + "金额": format( + Decimal(amount).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ), + } + for name, quantity, amount in zip( + [ + component["value"] + for component in response[ + "data" + ] + if re.match( + r"^项目名称明细(\d+)?$", + component["desc"], + ) + ], + [ + component["value"] + for component in response[ + "data" + ] + if re.match( + r"^项目数量明细(\d+)?$", + component["desc"], + ) + ], + [ + component["value"] + for component in response[ + "data" + ] + if re.match( + r"^项目金额明细(\d+)?$", + component["desc"], + ) + ], + ) + ] + ), + "查验状态": "无法查验", + } + except: + pass + # 请求深圳快瞳收费票据识别接口 + response = globals()["http_client"].post( + url=(url := "https://ai.inspirvision.cn/s/api/ocr/medical"), + headers={"X-RequestId-Header": image_guid}, + data={ + "token": globals()["authenticator"].get_token( + servicer="szkt" + ), + "imgBase64": f"data:image/{image_format};base64,{image_base64}", + }, + guid=hashlib.md5((url + image_guid).encode("utf-8")) + .hexdigest() + .upper(), + ) + # 若深圳快瞳收费票据识别响应非成功则返回NONE + if not ( + response.get("status") == 200 and response.get("code") == 0 + ): return None + extraction = { + "票据类型": ( + "门诊收费票据" + if response["data"]["insured"]["receipt_outpatient"] + else "住院收费票据" + ), + "票据号码": ( + receipt := ( + response["data"]["insured"]["receipt_outpatient"] + or response["data"]["insured"][ + "receipt_hospitalization" + ] + )["receipts"][0] + )["receipt_no"][ + "value" + ], # 默认提取门诊/住院收费票据的第一张票据 + "票据代码": receipt["global_detail"]["invoice_code"][ + "value" + ], + "开票日期": receipt["global_detail"]["invoice_date"][ + "value" + ], # 深圳快瞳收费票据识别中就开票日期格式为%Y-%m-%d + "校验码": fuzzy_match( + target="校验码", + components=receipt["global_detail"]["region_specific"], + specify_key="name", + return_key="word.value", + ), + "收款方": receipt["hospital_name"]["value"], + "付款方": receipt["name"]["value"], + "票据金额": format( + Decimal(receipt["total_amount"]["value"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ), + "医保支付": ( + format( + Decimal(field["value"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ) + if isinstance( + (field := receipt.get("medicare_pay")), dict + ) + else None + ), + "其它支付": format( + ( + Decimal(value).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ) + if ( + value := fuzzy_match( + target="其它支付", + components=receipt.get( + "global_detail", {} + ).get("pay_list", []), + specify_key="name", + return_key="word.value", + ) + ) + else None + ), + ".2f", + ), + "个人自付": ( + format( + Decimal(field["value"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ) + if isinstance((field := receipt.get("self_pay")), dict) + else None + ), + "自付一": ( + format( + Decimal(field["value"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ) + if isinstance( + field := (receipt.get("self_pay_one")), dict + ) + else None + ), + "自付二": ( + format( + Decimal(field["value"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ) + if isinstance( + field := (receipt.get("self_pay_two")), dict + ) + else None + ), + "个人自费": ( + format( + Decimal(field["value"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ) + if isinstance(field := (receipt.get("self_cost")), dict) + else None + ), + "住院日期": ( + datetime.strptime(field["value"], "%Y%m%d").strftime( + "%Y-%m-%d" + ) + if isinstance(field := (receipt.get("starttime")), dict) + else None + ), + "出院日期": ( + datetime.strptime(field["value"], "%Y%m%d").strftime( + "%Y-%m-%d" + ) + if isinstance(field := (receipt.get("endtime")), dict) + else None + ), + "医疗机构类型": receipt["others"][ + "medical_institution_type" + ]["value"], + "项目": [ + { + "名称": ( + field["value"] + if isinstance( + (field := item["item_name"]), dict + ) + else None + ), + "规格": ( + field["value"] + if isinstance( + (field := item["specifications"]), dict + ) + else None + ), + "单位": ( + field["value"] + if isinstance((field := item["unit"]), dict) + else None + ), + "数量": ( + format( + Decimal(field["value"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ) + if isinstance((field := item["number"]), dict) + else None + ), + "金额": ( + format( + Decimal(field["value"]).quantize( + Decimal("0.00"), + rounding=ROUND_HALF_UP, + ), + ".2f", + ) + if isinstance( + (field := item["total_amount"]), dict + ) + else None + ), + } + for item in receipt["feeitems"] + ], + "查验状态": "无法查验", + } + return extraction + except: + return None + # 影像件识别使能检查,若影像件不识别则跳过 if not recognition_enable.evaluate( { @@ -1355,7 +1372,12 @@ if __name__ == "__main__": # 影像件识别并整合至赔案档案 image_recognize( - image_index, image_guid, image_format, image_base64, insurance_branch, image_type + image_index, + image_guid, + image_format, + image_base64, + insurance_branch, + image_type, ) """