From abfe04f2b9b3a4024b3424c3924414bc8abd2f66 Mon Sep 17 00:00:00 2001 From: liubiren Date: Wed, 24 Dec 2025 12:52:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A5=E5=B8=B8=E6=9B=B4=E6=96=B0=20from=20N?= =?UTF-8?q?UC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 票据理赔自动化/database.db | Bin 85553152 -> 85553152 bytes 票据理赔自动化/main.py | 95 ++++++++++++++++++------------------- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/票据理赔自动化/database.db b/票据理赔自动化/database.db index 633208159972c752b315853092ce6b6d9d1a3013..df642b9e7dceeeab9a71b54d29d7685bf6047cd3 100644 GIT binary patch delta 6676 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 delta 6665 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 H4zK?O!nGBy diff --git a/票据理赔自动化/main.py b/票据理赔自动化/main.py index 7a36390..a4edb8e 100644 --- a/票据理赔自动化/main.py +++ b/票据理赔自动化/main.py @@ -73,33 +73,31 @@ if __name__ == "__main__": # 初始化在保被保险人表(TPA作业系统包括团单、个单和被保险人表,此处直接整合为宽表) self._execute( sql=""" - CREATE TABLE IF NOT EXISTS insured_person_policies + CREATE TABLE IF NOT EXISTS insured_persons ( - --被保险人 - insured_person TEXT NOT NULL, - --被保险人的证件类型 - identity_type TEXT NOT NULL, - --被保险人的证件号码 - identity_number TEXT NOT NULL, - --与主被保险人关系,包括本人和附属(配偶、父母和子女) - relationship TEXT NOT NULL, - --个单号 - person_policy TEXT NOT NULL, - --主被保险人 - master_insured_person TEXT NOT NULL, - --保险起期(取个单和团单起期最大值) - commencement_date REAL NOT NULL, - --保险止期(取个单和团单止期最小值) - termination_date REAL NOT NULL, --团单号 group_policy TEXT NOT NULL, - --投保公司 - insurance_company TEXT NOT NULL, + --个单号 + person_policy TEXT NOT NULL, --保险分公司 insurer_company TEXT NOT NULL, - --联合主键(投保公司+保险分公司+被保险人+被保险人的证件类型+被保险人的证件号码) - PRIMARY KEY (insurance_company, insurer_company, insured_person, identity_type, - identity_number) + --主被保险人 + master_insured_person TEXT NOT NULL, + --被保险人 + insured_person TEXT NOT NULL, + --证件类型 + identity_type TEXT NOT NULL, + --证件号码 + identity_number TEXT NOT NULL, + --与主被保险人关系,包括本人和附属(附属包括配偶、父母和子女等) + relationship TEXT NOT NULL, + --保险起期(取个单和团单起期最大值) + commencement_date TEXT NOT NULL, + --保险止期(取个单和团单止期最小值) + termination_date TEXT NOT NULL, + --联合主键(被保险人+证件类型+证件号码+保险分公司) + PRIMARY KEY (insured_person, identity_type, + identity_number, insurer_company) ) """ ) @@ -160,22 +158,20 @@ if __name__ == "__main__": raise RuntimeError("查询并获取单条购药及就医机构类型发生异常") # noinspection PyShadowingNames - def query_insured_person_records( + def query_insured_persons( self, - insurance_company: str, insurer_company: str, insured_person: str, identity_type: str, identity_number: str, ) -> Optional[List[Dict[str, Any]]]: """ - 查询并获取多条被保险人记录(例如,若夫妻同在投保公司则互为附加被保险人,一方被保险人记录包括本人和配偶两条) - :param insurance_company: 投保公司 + 根据保险分公司、被保险人、证件类型和证件号码查询被保险人(备注,若夫妻同在投保公司则互为附加被保险人,一方被保险人记录包括本人和配偶两条) :param insurer_company: 保险分公司 :param insured_person: 被保险人 - :param identity_type: 被保险人的证件类型 - :param identity_number: 被保险人的证件号码 - :return: 被保险人记录 + :param identity_type: 证件类型 + :param identity_number: 证件号码 + :return: 被保险人列表,包括被被保险人、个单号、主被保险人、与主被保险人关系、保险起期和保险止期 """ # noinspection PyBroadException try: @@ -183,21 +179,20 @@ if __name__ == "__main__": # noinspection SqlResolve result = self._query_all( sql=""" - SELECT insured_person AS "被保险人", - relationship AS "与主被保险人关系", + SELECT group_policy AS "团单号", person_policy AS "个单号", master_insured_person AS "主被保险人", + insured_person AS "被保险人", + relationship AS "与主被保险人关系", commencement_date AS "保险起期", termination_date AS "保险止期" - FROM insured_person_policies - WHERE insurance_company = ? - AND insurer_company = ? + FROM insured_persons + WHERE insurer_company = ? AND insured_person = ? AND identity_type = ? AND identity_number = ? """, parameters=( - insurance_company, insurer_company, insured_person, identity_type, @@ -208,7 +203,7 @@ if __name__ == "__main__": return [ { k: ( - datetime.fromtimestamp(v) + datetime.strptime(v, "%Y-%m-%d") # 保险 if k in ["保险起期", "保险止期"] else v ) @@ -217,9 +212,11 @@ if __name__ == "__main__": for e in result ] # 将保险起期和保险止期由时间戳转为datetime对象 raise - # TODO: 若查询并获取多条个单和被保险人记录发生异常则流转至主数据人工处理 + # TODO: 若根据保险分公司、被保险人、证件类型和证件号码查询被保险人发生异常则流转至主数据人工处理 except Exception: - raise RuntimeError("查询并获取多条个单和被保险人记录发生异常") + raise RuntimeError( + "根据保险分公司、被保险人、证件类型和证件号码查询被保险人发生异常" + ) # noinspection PyShadowingNames def query_medicine( @@ -462,24 +459,21 @@ if __name__ == "__main__": # noinspection PyShadowingNames def image_recognize( image, - insurance_company, insurer_company, ) -> None: """ 影像件识别并整合至赔案档案 :param image: 影像件 - :param insurance_company: 投保公司 :param insurer_company: 保险分公司 :return: 空 """ # TODO: 后续添加居民身份证(国徽面)和居民身份证(头像面)合并 # noinspection PyShadowingNames - def identity_card_recognize(image, insurance_company, insurer_company) -> None: + def identity_card_recognize(image, insurer_company) -> None: """ 居民身份证识别并整合至赔案档案 :param image: 影像件 - :param insurance_company: 投保公司 :param insurer_company: 保险分公司 :return: 空 """ @@ -574,8 +568,7 @@ if __name__ == "__main__": ) # 查询并获取多条被保险人记录 - dossier["被保险人层"] = master_data.query_insured_person_records( - insurance_company, + dossier["被保险人层"] = master_data.query_insured_persons( insurer_company, insured_person, # 出险人和被保险人为同一人,视角不同:出险人为理赔,被保险人为承保/保全 identity_type, @@ -1388,8 +1381,6 @@ if __name__ == "__main__": match (image["影像件类型"], receipt["购药及就医机构类型"]): # 就增值税发票且药店扣除不合理费用、增值税发票且私立医院解析个人自费、个人自付、医保支付、不合理金额和合理金额 case ("增值税发票", "药店"): - receipt["购药及就医类型"] = "药店购药" - items = ( pandas.DataFrame(receipt["明细项"]) .groupby("名称") # 就相同明细项名称合并数量和金额 @@ -1432,6 +1423,14 @@ if __name__ == "__main__": receipt.update( { + "起期": receipt["开票日期"], + "止期": receipt["开票日期"], + "姓名": ( + dossier["出险人层"]["姓名"] + if dossier["出险人层"]["姓名"] in receipt["姓名"] + else receipt["姓名"] + ), + "购药及就医类型": "药店购药", "个人自费": Decimal("0.00"), "个人自付": Decimal("0.00"), "医保支付": Decimal("0.00"), @@ -1448,6 +1447,7 @@ if __name__ == "__main__": "明细项": items.to_dict("records"), } ) + # TODO: 后续完善就购药及就医类型为门诊就诊(私立医院)处理 case ("增值税发票", "私立医院"): receipt["购药及就医类型"] = "门诊就医" @@ -1522,7 +1522,7 @@ if __name__ == "__main__": "居民身份证(国徽、头像面)" | "居民身份证(国徽面)" | "居民身份证(头像面)" ): # 居民身份证识别并整合至赔案档案 - identity_card_recognize(image, insurance_company, insurer_company) + identity_card_recognize(image, insurer_company) # TODO: 后续添加居民户口簿识别和整合方法 case "中国港澳台地区及境外护照": raise RuntimeError("暂不支持中国港澳台地区及境外护照") @@ -1618,7 +1618,6 @@ if __name__ == "__main__": # 影像件识别并整合至赔案档案 image_recognize( image, - insurance_company, insurer_company, )