日常更新

from NUC
This commit is contained in:
liubiren 2025-12-29 20:26:39 +08:00
parent d232b65335
commit c9a025d2f6
7 changed files with 1709 additions and 1706 deletions

View File

@ -617,7 +617,7 @@ class GenerateDraft:
# ======================== 调用示例(使用抽象后的方法) ======================== # ======================== 调用示例(使用抽象后的方法) ========================
def execute_workflow(): def direct():
"""生成剪映草稿""" """生成剪映草稿"""
# 实例化 # 实例化
draft = GenerateDraft( draft = GenerateDraft(

View File

@ -1,194 +0,0 @@
def general_text_recognize(image) -> str:
"""
通用文本识别
:param image: 影像件
:return: 识别文本
"""
# 请求深圳快瞳通用文本识别接口
response = http_client.post(
url=(url := "https://ai.inspirvision.cn/s/api/ocr/general"),
headers={
"X-RequestId-Header": image["影像件唯一标识"]
}, # 以影像件唯一标识作为请求唯一标识,用于双方联查
data={
"token": authenticator.get_token(servicer="szkt"), # 获取深圳快瞳访问令牌
"imgBase64": f"data:image/{image["影像件格式"].lstrip(".")};base64,{image["影像件BASE64编码"]}",
},
guid=md5((url + image["影像件唯一标识"]).encode("utf-8")).hexdigest().upper(),
)
# TODO: 若响应非成功则流转至人工处理
if not (response.get("status") == 200 and response.get("code") == 0):
raise RuntimeError("请求深圳快瞳通用文本识别接口发生异常")
blocks = []
for block in response["data"]:
# noinspection PyTypeChecker
blocks.append(
[
int(block["itemPolygon"]["x"]), # 文本块左上角的X坐标
int(block["itemPolygon"]["y"]), # 文本块左上角的Y坐标
int(block["itemPolygon"]["height"]), # 文本块左上角的高度
block["value"], # 文本块的文本内容
]
)
# 使用俄罗斯方块方法整理文本块先按照文本块的Y坐标升序从上到下
blocks.sort(key=lambda x: x[1])
lines = []
for idx, block in enumerate(blocks[1:]):
if idx == 0:
line = [blocks[0]]
continue
# 若当前文本块的Y坐标和当前文本行的平均Y坐标差值小于阈值则归为同一文本行否则另起一文本行分行
if (
block[1] - numpy.array([e[1] for e in line]).mean()
< numpy.array([e[2] for e in line]).mean()
):
line.append(block)
else:
lines.append(line)
line = [block]
lines.append(line)
blocks = []
for line in lines:
blocks.extend(
[re.sub(r"\s", "", x[3]) for x in sorted(line, key=lambda x: x[0])]
) # 按照文本块的X坐标升序从左到右并去除文本块的文本内容中所有空字符
return "\n".join(blocks)
class JiojioTokenizer:
"""中文分词器"""
def __init__(self):
# 初始化jiojio分词器
# noinspection PyBroadException
try:
jiojio.init()
except:
raise RuntimeError("初始化jiojio分词器发生异常")
# noinspection PyShadowingNames
@staticmethod
def callback(text: str, flags: int, cursor) -> None:
"""
分词回调函数
:param text: 待分词文本
:param flags: FTS5分词场景标记位
:param cursor: FTS5分词回传游标
return
"""
if not text or not isinstance(text, str):
return
tokens = []
begin_idx = 0 # 当前分词开始索引
for word in jiojio.cut(text):
if word.strip() == "":
begin_idx += len(word)
continue
tokens.append(
(word, begin_idx, end_idx := begin_idx + len(word))
) # SQLite FTS5要求回传分词语音文本开始和结束索引
begin_idx = end_idx
for token, begin_idx, end_idx in tokens:
cursor.send((token, begin_idx, end_idx))
# 实例化jiojio分词器
self.threads.jiojio_tokenizer = self.JiojioTokenizer()
# 创建分词器方法
def create_tokenizer_module(tokenizer):
class JiojioTokenizerModule:
"""创建jiojio分词器方法"""
# noinspection PyShadowingNames
@staticmethod
def tokenize(text: str, flags: int, cursor) -> None:
tokenizer.callback(text, flags, cursor)
return JiojioTokenizerModule()
self.threads.connection.create_module(
"jiojio_fts5_module",
create_tokenizer_module(self.threads.jiojio_tokenizer),
)
self.threads.connection.execute(
"""
CREATE VIRTUAL TABLE IF NOT EXISTS jiojio_tokenizer USING fts5tokenizer(jiojio_fts5_module)
"""
)
{
"code": 0,
"status": 200,
"message": "success",
"serialNo": "3a08935648632621760512",
"data": [
{"desc": "金额", "value": "175.22"},
{
"desc": "项目名称",
"value": "*化学药品制剂*[海露]玻璃酸钠滴眼液0.1%*10ml支/盒",
},
{"desc": "数量", "value": "2"},
{"desc": "规格型号", "value": ""},
{"desc": "税额", "value": "22.78"},
{"desc": "税率", "value": "13%"},
{"desc": "单位", "value": ""},
{"desc": "单价", "value": "87.61"},
{"desc": "金额1", "value": "-69.42"},
{
"desc": "项目名称1",
"value": "*化学药品制剂*[海露]玻璃酸钠滴眼液0.1%*10ml/支/盒",
},
{"desc": "数量1", "value": ""},
{"desc": "规格型号1", "value": ""},
{"desc": "税额1", "value": "-9.02"},
{"desc": "税率1", "value": "13%"},
{"desc": "单位1", "value": ""},
{"desc": "单价1", "value": ""},
{"desc": "发票名称", "value": "电子发票(普通发票)"},
{"desc": "全电票标签", "value": ""},
{"desc": "发票号码", "value": "25447200000045325946"},
{"desc": "开票日期", "value": "2025年01月20日"},
{"desc": "购买方名称", "value": "唐敏华"},
{"desc": "购买方识别号", "value": ""},
{"desc": "销售方名称", "value": "广州美团大药房有限公司"},
{"desc": "销售方识别号", "value": "91440100MAC1CAJH27"},
{"desc": "合计金额", "value": "¥105.80"},
{"desc": "合计税额", "value": "¥13.76"},
{"desc": "金额小计", "value": ""},
{"desc": "税额小计", "value": ""},
{"desc": "价税合计(大写)", "value": "壹佰壹拾玖圆伍角陆分"},
{"desc": "小写金额", "value": "¥119.56"},
{"desc": "备注", "value": ""},
{"desc": "开票人", "value": "张景景"},
{"desc": "发票类型", "value": "电子发票(普通发票)"},
{"desc": "监制章存在性判断", "value": "True"},
{"desc": "总页数", "value": ""},
{"desc": "当前页数", "value": ""},
],
}
"""
with open(f"dossiers/{case_number}.html", "w", encoding="utf-8") as file:
file.write(
template.render(
{
"dossier": dossier,
}
)
)
"""

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
from pathlib import Path
from masterdata import MasterData
from utils.rule_engine import RuleEngine
# 初始化赔案档案保险公司将提供投保公司、保险分公司和报案时间等TPA作业系统签收后生成赔案号
dossier = {
"report_layer": {}, # 报案层
"images_layer": [], # 影像件层
"insured_person_layer": {}, # 出险人层
"insured_persons_layer": [], # 被保险人层
"receipts_layer": [], # 票据层
"adjustment_layer": {}, # 理算层
}
# 实例化主数据
master_data = MasterData()
# 实例化规则引擎
rule_engine = RuleEngine(rules_path=Path("rules"))

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,321 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from decimal import Decimal, ROUND_HALF_UP
from typing import Any, Dict, List, Optional
from utils.client import SQLiteClient
class MasterData(SQLiteClient):
"""主数据"""
def __init__(self):
"""
初始化主数据
"""
# 初始化SQLite客户端
super().__init__(database="database.db")
try:
with self:
# 初始化团单表
self._execute(
sql="""
CREATE TABLE IF NOT EXISTS group_policies
(
--团单唯一标识
guid TEXT PRIMARY KEY,
--团单号
group_policy TEXT NOT NULL,
--保险分公司名称
insurer_company TEXT NOT NULL,
--保险起期
commencement_date TEXT NOT NULL,
--保险止期
termination_date TEXT NOT NULL
)
"""
)
# 初始化个单表
self._execute(
sql="""
CREATE TABLE IF NOT EXISTS person_policies
(
--个单唯一标识
guid TEXT PRIMARY KEY,
--个单号
person_policy TEXT NOT NULL,
--保险起期
commencement_date TEXT NOT NULL,
--保险止期
termination_date TEXT NOT NULL,
--团单唯一标识用于联查团案
group_policy_guid TEXT NOT NULL
)
"""
)
# 初始化被保险人表,保司推送赔案时,一般无团单号,需先根据保险分公司名称、被保险人姓名、证件类型和证件号码查询被保人,再在票据理算时根据事故起期确定个单和相应责任
self._execute(
sql="""
CREATE TABLE IF NOT EXISTS insured_persons
(
--被保险人唯一标识
guid TEXT PRIMARY KEY,
--被保险人姓名
insured_person TEXT NOT NULL,
--证件类型
identity_type TEXT NOT NULL,
--证件号码
identity_number TEXT NOT NULL,
--与主被保险人关系包括本人父母配偶和子女等
relationship TEXT NOT NULL,
--个单唯一标识用于联查个单
person_policy_guid TEXT NOT NULL
)
"""
)
# 初始化责任表
self._execute(
sql="""
CREATE TABLE IF NOT EXISTS liabilities
(
--责任唯一标识
guid TEXT PRIMARY KEY,
--责任名称
liability TEXT NOT NULL,
--出险事故
accident TEXT NOT NULL,
--个人自费理算比例
personal_self_ratio TEXT NOT NULL,
--个人自付理算比例
non_medical_ratio TEXT NOT NULL,
--合理理算比例
reasonable_ratio TEXT NOT NULL,
--理算保单唯一标识
adjust_policy_guid TEXT NOT NULL,
--个单唯一标识
person_policy_guid TEXT NOT NULL
)
"""
)
# 初始化保额变动表
self._execute(
sql="""
CREATE TABLE IF NOT EXISTS coverage_changes
(
--保额变动唯一标识
guid TEXT PRIMARY KEY,
--变动类型包括承保和理算等
change_type TEXT NOT NULL,
--变动前金额
before_change_amount TEXT NOT NULL,
--变动金额
change_amount TEXT NOT NULL,
--变动后金额
after_change_amount TEXT NOT NULL,
--变动时间
change_time TEXT NOT NULL,
--变动保单唯一标识
change_policy_guid TEXT NOT NULL
)
"""
)
# 初始化购药及就医机构表
self._execute(
sql="""
CREATE TABLE IF NOT EXISTS institutions
(
--购药及就医机构
institution TEXT PRIMARY KEY,
--购药及就医机构类型
institution_type TEXT NOT NULL,
--所在省
province TEXT NOT NULL,
--所在市
city TEXT NOT NULL
)
"""
)
# 初始化药品表
self._execute(
sql="""
CREATE TABLE IF NOT EXISTS medicines
(
--药品/医疗服务
medicine TEXT PRIMARY KEY
)
"""
)
except Exception as exception:
raise RuntimeError(f"初始化数据库发生异常:{str(exception)}")
# noinspection PyShadowingNames
def query_liabilities(
self,
insurer_company: str,
insured_person: str,
identity_type: str,
identity_number: str,
report_date: str,
) -> Optional[List[Dict[str, Any]]]:
"""
根据保险分公司名称被保险人姓名证件类型证件号码和出险时间查询责任列表
:param insurer_company: 保险分公司名称
:param insured_person: 被保险人姓名
:param identity_type: 证件类型
:param identity_number: 证件号码
:param report_date: 报案时间
:return: 责任列表
"""
# noinspection PyBroadException
try:
with self:
# noinspection SqlResolve
result = self._query_all(
sql="""
SELECT group_policies.group_policy,
group_policies.insurer_company,
person_policies.person_policy,
person_policy_coverage_changes.after_change_amount AS remaining_amount,
master_insured_persons.insured_person AS master_insured_person,
insured_persons.insured_person,
insured_persons.identity_type,
insured_persons.identity_number,
insured_persons.relationship,
MAX(group_policies.commencement_date,
person_policies.commencement_date) AS commencement_date,
MIN(group_policies.termination_date,
person_policies.termination_date) AS termination_date,
liabilities.liability,
liabilities.accident,
liabilities.personal_self_ratio,
liabilities.non_medical_ratio,
liabilities.reasonable_ratio,
liabilities.adjust_policy_guid
FROM insured_persons
INNER JOIN insured_persons master_insured_persons
ON person_policies.guid = master_insured_persons.person_policy_guid
AND master_insured_persons.relationship = "本人"
INNER JOIN person_policies
ON insured_persons.person_policy_guid = person_policies.guid
INNER JOIN group_policies
ON person_policies.group_policy_guid = group_policies.guid
INNER JOIN liabilities
ON person_policies.guid = liabilities.person_policy_guid
INNER JOIN coverage_changes person_policy_coverage_changes
ON person_policies.guid =
person_policy_coverage_changes.change_policy_guid
AND
person_policy_coverage_changes.change_time = (SELECT MAX(change_time)
FROM coverage_changes
WHERE change_policy_guid = person_policies.guid)
INNER JOIN coverage_changes
ON liabilities.adjust_policy_guid = coverage_changes.change_policy_guid
AND coverage_changes.change_time = (SELECT MAX(change_time)
FROM coverage_changes
WHERE liabilities.adjust_policy_guid = change_policy_guid)
WHERE group_policies.insurer_company = ?
AND insured_persons.insured_person = ?
AND insured_persons.identity_type = ?
AND insured_persons.identity_number = ?
AND ? BETWEEN group_policies.commencement_date AND group_policies.termination_date
AND ? BETWEEN person_policies.commencement_date AND person_policies.termination_date
AND CAST(coverage_changes.after_change_amount AS REAL) > 0
""",
parameters=(
insurer_company,
insured_person,
identity_type,
identity_number,
report_date,
report_date,
),
)
if result:
return [
{
k: (
datetime.strptime(v, "%Y-%m-%d")
if k in ["commencement_date", "termination_date"]
else (
Decimal(v).quantize(
Decimal("0.00"),
rounding=ROUND_HALF_UP,
)
if k
in [
"remaining_amount",
"personal_self_ratio",
"non_medical_ratio",
"reasonable_ratio",
]
else v
)
) # 就保险起期、止期则转为日期时间datetime对象个人自费比例、个人自付比例和合理比例转为小数decimal对象
for k, v in e.items()
}
for e in result
] # 将保险起期和保险止期转为日期datetime对象
raise RuntimeError("查无数据")
# TODO: 若根据保险分公司名称、被保险人姓名、证件类型、证件号码和出险时间查询被保险人发生异常则流转至主数据人工处理
except Exception as exception:
raise RuntimeError(f"{str(exception)}")
# noinspection PyShadowingNames
def query_institution_type(self, institution: str) -> Optional[str]:
"""
根据购药及就医机构查询购药及就医机构类型
:param institution: 购药及就医机构
:return: 购药及就医机构类型
"""
# noinspection PyBroadException
try:
with self:
# noinspection SqlResolve
result = self._query_one(
sql="""
SELECT institution_type
FROM institutions
WHERE institution = ?
""",
parameters=(institution,),
)
if result:
return result["institution_type"]
raise
# TODO: 若根据购药及就医机构查询购药及就医机构类型发生异常则流转至主数据人工处理
except Exception:
raise
# noinspection PyShadowingNames
def query_medicine(
self,
content: str,
) -> Optional[str]:
"""
根据明细项中具体内容查询药品/医疗服务
:param content: 明细项具体内容
:return: 药品/医疗服务
"""
# TODO: 暂仅支持查询药品、通过药品/医疗服务包含明细项中具体内容查询
# noinspection PyBroadException
try:
with self:
# noinspection SqlResolve
result = self._query_all(
sql="""
SELECT medicine
FROM medicines
WHERE ? LIKE '%' || medicine || '%'
""",
parameters=(content,),
)
if result:
return max(result, key=lambda x: len(x["medicine"]))[
"medicine"
] # 返回最大长度的药品/医疗服务
raise
# TODO: 若根据明细项中具体内容查询药品/医疗服务发生异常则流转至主数据人工处理
except Exception:
raise