55
票据理赔自动化/1.py
|
|
@ -1,55 +0,0 @@
|
|||
import csv
|
||||
|
||||
import chardet
|
||||
|
||||
|
||||
def convert_csv_to_utf8(input_csv: str, output_csv: str, encoding: str = None):
|
||||
"""
|
||||
将非UTF-8编码的CSV文件转换为UTF-8编码
|
||||
:param input_csv: 输入CSV文件路径
|
||||
:param output_csv: 输出UTF-8编码的CSV文件路径
|
||||
:param encoding: 手动指定输入文件编码(如"gbk"),若为None则自动检测
|
||||
"""
|
||||
# 步骤1:检测输入文件的原始编码(若未手动指定)
|
||||
if not encoding:
|
||||
print(f"🔍 正在检测 {input_csv} 的编码...")
|
||||
with open(input_csv, "rb") as f:
|
||||
raw_data = f.read(10240) # 读取前10KB数据用于检测(足够识别编码)
|
||||
result = chardet.detect(raw_data)
|
||||
encoding = result["encoding"]
|
||||
confidence = result["confidence"]
|
||||
print(f"✅ 检测到编码:{encoding}(置信度:{confidence:.2f})")
|
||||
# 处理chardet检测结果为空的情况(兜底用gbk,适配中文常见编码)
|
||||
if not encoding:
|
||||
encoding = "gbk"
|
||||
print(f"⚠️ 编码检测失败,兜底使用 {encoding}")
|
||||
|
||||
# 步骤2:按原始编码读取CSV并转换为UTF-8保存
|
||||
try:
|
||||
# 读取原始CSV(处理编码错误:replace表示用<E7A4BA>替换无法解码的字符,避免程序崩溃)
|
||||
with open(input_csv, "r", encoding=encoding, errors="replace") as infile:
|
||||
# 兼容CSV的不同分隔符(默认逗号,若为制表符可改delimiter='\t')
|
||||
reader = csv.reader(infile)
|
||||
rows = list(reader) # 读取所有行
|
||||
|
||||
# 保存为UTF-8编码的CSV(newline=''避免空行,encoding='utf-8-sig'带BOM,适配Excel打开)
|
||||
with open(output_csv, "w", encoding="utf-8-sig", newline="") as outfile:
|
||||
writer = csv.writer(outfile)
|
||||
writer.writerows(rows)
|
||||
|
||||
print(f"✅ 转换完成!UTF-8编码文件已保存至:{output_csv}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 转换失败:{str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
# ========== 示例调用 ==========
|
||||
if __name__ == "__main__":
|
||||
# 输入/输出文件路径(替换为你的实际路径)
|
||||
input_path = "转换后_UTF8.csv"
|
||||
output_path = "转换后_UTF8.csv"
|
||||
|
||||
# 自动检测编码并转换
|
||||
convert_csv_to_utf8(input_path, output_path)
|
||||
|
|
@ -118,10 +118,10 @@ if __name__ == "__main__":
|
|||
# 初始化药品表
|
||||
self._execute(
|
||||
sql="""
|
||||
CREATE TABLE IF NOT EXISTS drugs
|
||||
CREATE TABLE IF NOT EXISTS medicines
|
||||
(
|
||||
--药品
|
||||
drug TEXT PRIMARY KEY
|
||||
--药品/医疗服务
|
||||
medicine TEXT PRIMARY KEY
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
|
@ -218,35 +218,36 @@ if __name__ == "__main__":
|
|||
raise RuntimeError("查询并获取多条个单和被保险人记录发生异常")
|
||||
|
||||
# noinspection PyShadowingNames
|
||||
def query_drug(
|
||||
def query_medicine(
|
||||
self,
|
||||
content: str,
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
根据明细项具体内容查询药品
|
||||
根据明细项名称中具体内容查询药品/医疗服务
|
||||
:param content: 明细项具体内容
|
||||
:return: 药品
|
||||
:return: 药品/医疗服务
|
||||
"""
|
||||
# TODO: 暂仅支持查询药品,后续完善查询医疗服务
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
with self:
|
||||
# noinspection SqlResolve
|
||||
result = self._query_all(
|
||||
sql="""
|
||||
SELECT drug
|
||||
FROM drugs
|
||||
WHERE ? LIKE '%' || drug || '%'
|
||||
SELECT medicine
|
||||
FROM medicines
|
||||
WHERE ? LIKE '%' || medicine || '%'
|
||||
""",
|
||||
parameters=(content,),
|
||||
)
|
||||
if result:
|
||||
return max(result, key=lambda x: len(x["drug"]))[
|
||||
"drug"
|
||||
] # 仅返回最大长度的药品
|
||||
return max(result, key=lambda x: len(x["medicine"]))[
|
||||
"medicine"
|
||||
] # 返回药品最大长度的药品
|
||||
raise
|
||||
# TODO: 若根据明细项具体内容查询药品发生异常则流转至主数据人工处理
|
||||
# TODO: 若根据明细项名称中具体内容查询药品/医疗服务发生异常则流转至主数据人工处理
|
||||
except Exception:
|
||||
raise RuntimeError("根据明细项具体内容查询药品发生异常")
|
||||
raise RuntimeError("根据明细项名称中具体内容查询药品/医疗服务发生异常")
|
||||
|
||||
# 实例化主数据
|
||||
master_data = MasterData()
|
||||
|
|
@ -859,6 +860,23 @@ if __name__ == "__main__":
|
|||
else None
|
||||
)
|
||||
|
||||
def parse_name(name: str) -> Tuple[str, Optional[str]]:
|
||||
"""
|
||||
根据明细项名称解析明细项类别和具体内容,并根据明细项名称中具体内容查询药品/医疗服务
|
||||
:param name: 明细项名称
|
||||
return 明细项类别和药品/医疗服务
|
||||
"""
|
||||
if match := re.match(
|
||||
r"^\*(?P<category>.*?)\*(?P<specific>.*)$",
|
||||
name,
|
||||
):
|
||||
return match.group("category"), master_data.query_medicine(
|
||||
match.group("specific")
|
||||
)
|
||||
# 一般增值税发票明细项格式形如*{category}*{specific},其中category为明细项类别,例如中成药;specific为明细项具体内容,例如[同仁堂]金贵肾气水蜜丸 300丸/瓶,需要据此查询药品。其它格式则将明细项内容作为明细项类别,药品为空值
|
||||
else:
|
||||
return name, None
|
||||
|
||||
# 初始化票据数据
|
||||
receipt = {"影像件编号": image["影像件编号"]}
|
||||
# 请求深圳快瞳票据查验接口(兼容增值税发票、医疗门诊/住院收费票据)
|
||||
|
|
@ -1291,24 +1309,22 @@ if __name__ == "__main__":
|
|||
lambda dataframe: dataframe["金额"] != 0
|
||||
] # 仅保留金额非0的明细项
|
||||
.reset_index()
|
||||
.to_dict("records")
|
||||
.pipe(
|
||||
lambda dataframe: dataframe.join(
|
||||
dataframe["名称"]
|
||||
.apply(
|
||||
parse_name
|
||||
) # 根据明细项名称解析明细项类别和具体内容,并根据明细项名称中具体内容查询药品/医疗服务
|
||||
.apply(
|
||||
pandas.Series
|
||||
) # 就明细项类别和药品/医疗服务元组展开为两列
|
||||
.rename(columns={0: "类别", 1: "药品/医疗服务"})
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
for item in items:
|
||||
# 匹配并解析明细项大类和具体内容
|
||||
if match := re.match(
|
||||
r"^\*(?P<category>.*?)\*(?P<content>.*)$",
|
||||
item["名称"],
|
||||
):
|
||||
category = match.group("category") # 明细项大类
|
||||
# 根据明细项具体内容查询药品
|
||||
drug = master_data.query_drug(match.group("content"))
|
||||
# TODO: 若匹配明细项大类和具体内容发生异常则流转至人工处理
|
||||
else:
|
||||
raise RuntimeError("匹配明细项大类和具体内容发生异常")
|
||||
|
||||
print(dossier["被保险人层"])
|
||||
exit()
|
||||
print(items)
|
||||
print()
|
||||
|
||||
case ("增值税发票", "私立医院"):
|
||||
receipt["购药及就医类型"] = "门诊就医"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,135 @@
|
|||
{
|
||||
"contentType": "application/vnd.gorules.decision",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "d0fbd74f-6514-4c42-87e5-fdf849b8ab6f",
|
||||
"name": "request",
|
||||
"type": "inputNode",
|
||||
"content": {
|
||||
"schema": ""
|
||||
},
|
||||
"position": {
|
||||
"x": 110,
|
||||
"y": 292.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "d717ec6d-d082-4f23-8fe6-3fa52b228e65",
|
||||
"name": "decisionTable",
|
||||
"type": "decisionTableNode",
|
||||
"content": {
|
||||
"rules": [
|
||||
{
|
||||
"_id": "16c78c24-882b-4428-b429-e66ed67e0e85",
|
||||
"_description": "中银保险有限公司苏州分公司就中成药不扣除",
|
||||
"1fdeb8d9-62c5-4f07-b5d1-69dbaf6020fe": "amount",
|
||||
"2522d0a7-437b-421b-90e7-165e2365ebba": "\"中银保险有限公司苏州分公司\"",
|
||||
"4ed5e43b-f519-46cb-8c92-80097788d21a": "\"中成药\"",
|
||||
"8a5837ec-3df9-4445-a515-34762a244a68": "",
|
||||
"cd1dc117-39f5-4dc2-96bb-f67236199851": ""
|
||||
},
|
||||
{
|
||||
"_id": "33343bbe-ee06-44f2-8247-84106cdaad8a",
|
||||
"_description": "中银保险有限公司苏州分公司就化学药品制剂不扣除",
|
||||
"1fdeb8d9-62c5-4f07-b5d1-69dbaf6020fe": "amount",
|
||||
"2522d0a7-437b-421b-90e7-165e2365ebba": "\"中银保险有限公司苏州分公司\"",
|
||||
"4ed5e43b-f519-46cb-8c92-80097788d21a": "\"化学药品制剂\"",
|
||||
"8a5837ec-3df9-4445-a515-34762a244a68": "",
|
||||
"cd1dc117-39f5-4dc2-96bb-f67236199851": ""
|
||||
},
|
||||
{
|
||||
"_id": "87dd3e93-5b9b-4aa3-a7f8-899f0745260a",
|
||||
"_description": "中银保险有限公司苏州分公司就医疗仪器器械不扣除",
|
||||
"1fdeb8d9-62c5-4f07-b5d1-69dbaf6020fe": "amount",
|
||||
"2522d0a7-437b-421b-90e7-165e2365ebba": "\"中银保险有限公司苏州分公司\"",
|
||||
"4ed5e43b-f519-46cb-8c92-80097788d21a": "\"医疗仪器器械\"",
|
||||
"8a5837ec-3df9-4445-a515-34762a244a68": "",
|
||||
"cd1dc117-39f5-4dc2-96bb-f67236199851": ""
|
||||
},
|
||||
{
|
||||
"_id": "5a39b06b-deb9-411b-9aa3-e110588f3f7a",
|
||||
"_description": "中银保险有限公司苏州分公司就其它明细项类别扣除",
|
||||
"1fdeb8d9-62c5-4f07-b5d1-69dbaf6020fe": "\"0.00\"",
|
||||
"2522d0a7-437b-421b-90e7-165e2365ebba": "\"中银保险有限公司苏州分公司\"",
|
||||
"4ed5e43b-f519-46cb-8c92-80097788d21a": "",
|
||||
"8a5837ec-3df9-4445-a515-34762a244a68": "",
|
||||
"cd1dc117-39f5-4dc2-96bb-f67236199851": ""
|
||||
},
|
||||
{
|
||||
"_id": "cd1b883f-2dc8-459a-8eba-bbae94b3d9b2",
|
||||
"_description": "其它保险分公司就所有明细项类别扣除",
|
||||
"1fdeb8d9-62c5-4f07-b5d1-69dbaf6020fe": "\"0.00\"",
|
||||
"2522d0a7-437b-421b-90e7-165e2365ebba": "",
|
||||
"4ed5e43b-f519-46cb-8c92-80097788d21a": "",
|
||||
"8a5837ec-3df9-4445-a515-34762a244a68": "",
|
||||
"cd1dc117-39f5-4dc2-96bb-f67236199851": ""
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "2522d0a7-437b-421b-90e7-165e2365ebba",
|
||||
"name": "保险分公司",
|
||||
"field": "insurer_company"
|
||||
},
|
||||
{
|
||||
"id": "4ed5e43b-f519-46cb-8c92-80097788d21a",
|
||||
"name": "明细项类别",
|
||||
"field": "category"
|
||||
},
|
||||
{
|
||||
"id": "cd1dc117-39f5-4dc2-96bb-f67236199851",
|
||||
"name": "药品/医疗服务",
|
||||
"field": "medicine"
|
||||
},
|
||||
{
|
||||
"id": "8a5837ec-3df9-4445-a515-34762a244a68",
|
||||
"name": "金额",
|
||||
"field": "amount"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"id": "1fdeb8d9-62c5-4f07-b5d1-69dbaf6020fe",
|
||||
"name": "合理金额",
|
||||
"field": "reasonable_amount"
|
||||
}
|
||||
],
|
||||
"hitPolicy": "first",
|
||||
"inputField": null,
|
||||
"outputPath": null,
|
||||
"passThrough": true,
|
||||
"executionMode": "single"
|
||||
},
|
||||
"position": {
|
||||
"x": 430,
|
||||
"y": 292.5
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "b7e720d7-9ef7-4470-be68-a38a1a711b45",
|
||||
"name": "response",
|
||||
"type": "outputNode",
|
||||
"content": {
|
||||
"schema": ""
|
||||
},
|
||||
"position": {
|
||||
"x": 750,
|
||||
"y": 292.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "0b72f551-884f-41c9-b9a7-2eb711e93181",
|
||||
"type": "edge",
|
||||
"sourceId": "d0fbd74f-6514-4c42-87e5-fdf849b8ab6f",
|
||||
"targetId": "d717ec6d-d082-4f23-8fe6-3fa52b228e65"
|
||||
},
|
||||
{
|
||||
"id": "9dc2590c-8061-4974-b06e-40e7c49a1d97",
|
||||
"type": "edge",
|
||||
"sourceId": "d717ec6d-d082-4f23-8fe6-3fa52b228e65",
|
||||
"targetId": "b7e720d7-9ef7-4470-be68-a38a1a711b45"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 219 KiB |
|
Before Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 241 KiB |
|
Before Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 262 KiB |
|
Before Width: | Height: | Size: 251 KiB |
|
Before Width: | Height: | Size: 262 KiB |
|
Before Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 274 KiB |
|
Before Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 276 KiB |
|
Before Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 188 KiB |
|
Before Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 251 KiB |
|
Before Width: | Height: | Size: 243 KiB |
|
Before Width: | Height: | Size: 236 KiB |
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 250 KiB |
|
Before Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 248 KiB |
|
Before Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 244 KiB |
|
Before Width: | Height: | Size: 248 KiB |
|
Before Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 304 KiB |
|
Before Width: | Height: | Size: 298 KiB |