From ae932ab4871678b82da5ef8aa9faa4290a15e483 Mon Sep 17 00:00:00 2001 From: liubiren Date: Sat, 6 Dec 2025 23:37:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A5=E5=B8=B8=E6=9B=B4=E6=96=B0=20?= =?UTF-8?q?=E6=98=8E=E5=A4=A9=E5=85=A5=E8=81=8C=E6=96=B0=E5=85=AC=E5=8F=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SQLite_old.db => utils/SQLite.db | Bin 274432 -> 372736 bytes utils/certifications.json | 2 +- utils/client.py | 311 ++++-------- 票据理赔自动化/SQLite.db | Bin 274432 -> 0 bytes 票据理赔自动化/main.py | 451 +++++++++--------- 5 files changed, 301 insertions(+), 463 deletions(-) rename 票据理赔自动化/SQLite_old.db => utils/SQLite.db (51%) diff --git a/票据理赔自动化/SQLite_old.db b/utils/SQLite.db similarity index 51% rename from 票据理赔自动化/SQLite_old.db rename to utils/SQLite.db index 006b8eaf608b6a81a0348070c78d3523359f7036..d2ef0d028430d0198db2a754c4667e78ed222048 100644 GIT binary patch delta 1344 zcmb`{Urbw790%~8dv9+`fu37o>k4+gtAj$zpykg7NR>jXPGQ5XZp^7rM^hUG8(<40 zOXyup@J}>#_o0a~8JGXOmOxDa+Bw zc(l~T8mZajbZmA}emlO{R_ZJjVk4!za^HM%IzBfan@-8cBB2pWYo93EM?LB$tv8=E zi*}!bEUFE(VTm8ccr+p(J`#?M%gLFk_`<~Fxuxpw&dBAR$}NuvT92DVyT?J!mL?>p zD&~}Pxt(Pz6Lu7(bCp$nqWUxSF6~taZq4B!%Ig)kRxa#(R5%(M8i~m6a;N)$y)dzS zV;BA7Ic2q0`#WgjD!*Uw=lw)TFX5H@*lUNUzz7FG1QVE{3M^0!HNbu*R*-;#4W0l8 z?14I{husi_0kFffa0s4(K?p(L(=31&TA>9Rp#gS5J9K~xWY`C6qg+l2mQPi~_H3iy zAR11qHIiGqAm0$QzrxonQFM)a5RNw?9A)!H-3TwTD$mIX;k^jMPK1MO)gHG0A+~Ne zJ4HV`VkbMdpIwB9t>jj>U8i!&-73}Z5wxK9N8?(N_^%EMk}k;HHYGky$l$SGEu{$YWxD4~IH(N!T*H-vcULsUFa1b?nUJ(lSet$K=>xLxy{JEE zCbTN2+%W0450QX5KSpla$@BWH1nD&!BS?H98*3GI>0gW7Ig(9Z;%fEJuk#7(I)8}Z)rXNg^3ku?(yz^AiANaLM+>}8bzzHt;36+!lTtp7JmV4My_|icK$^17 zS>C4iXSkmzIfmono^{+J3J#Kc)t2b*;(l>dxg9jAn_gL$M#z2MIELA;WH*1{>va1H z`4jas1VvAN@>_N}o4G@p_4WdH$Mqfer9e*UUkTiaYU2sac{OYMjBm(F@9`mhqDU^g J*n53Q{{vFRO{)L^ delta 39409 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 Usi1o@{l$Pp{Co9)v73*>fW tuple[bool, float]: - with threading.Lock(): # 本次填充令牌时间戳 refill_timestamp = time.monotonic() - # 重新计算令牌桶中令牌数 self.tokens = min( self.max_tokens, self.tokens + self.refill_rate * (refill_timestamp - self.refill_timestamp), ) - self.refill_timestamp = refill_timestamp # 若令牌桶当前令牌数大于等于1则减少令牌 @@ -323,7 +310,6 @@ class Arguments(BaseModel): :param stream: 是否启用流式传输 :param guid: 全局唯一标识 """ - # 统一资源定位符 url: HttpUrl = Field(default=...) # 查询参数 @@ -365,43 +351,38 @@ class Arguments(BaseModel): return self -# HTTP客户端 class HTTPClient: + """请求客户端""" def __init__( self, - timeout: int = 60, default_headers: Optional[Dict[str, str]] = None, total: int = 3, backoff_factor: float = 0.5, + timeout: int = 60, cache_enabled: bool = False, cache_ttl: int = 360, ): """ - :param timeout: 超时时间,单位为秒 :param default_headers: 默认请求头 :param total: 最大重试次数 :param backoff_factor: 重试间隔退避因子 - :param cache_enabled: 是否使用缓存 + :param timeout: 超时时间,单位为秒 + :param cache_enabled: 使用缓存 :param cache_ttl: 缓存生存时间,单位为天 """ - - # 超时时间 - self.timeout = timeout - # 创建HTTP会话并挂载适配器 + # 创建请求会话并挂载适配器 self.session = self._create_session( default_headers=default_headers, total=total, backoff_factor=backoff_factor ) - - # 是否使用缓存 + # 初始化超时时间 + self.timeout = timeout + # 初始化使用缓存 self.cache_enabled = cache_enabled - # 缓存生存时间 - self.cache_ttl = cache_ttl - # 若使用缓存,则初始化缓存数据库 - if self.cache_enabled: - self._initialize_cache_database() + # 初始化缓存生存时间,单位为秒 + self.cache_ttl = cache_ttl * 24 * 60 * 60 - # 创建HTTP会话并挂载适配器 + # 创建请求会话并挂载适配器 @staticmethod def _create_session( total: int, @@ -409,11 +390,11 @@ class HTTPClient: default_headers: Optional[Dict[str, str]] = None, ) -> Session: """ - :param default_headers 默认请求头 - :param total 最大重试次数 - :param backoff_factor 重试间隔退避因子 + :param default_headers: 默认请求头 + :param total: 最大重试次数 + :param backoff_factor: 重试间隔退避因子 + :return Session: 会话对象 """ - # 创建会话对象 session = Session() @@ -429,7 +410,7 @@ class HTTPClient: 502, 503, 504, - ], # 408:请求超时,502:网关错误,503:服务不可用,504:网关超时 + ], # 408为请求超时,502为网关错误,503为服务不可用,504为网关超时 total=total, respect_retry_after_header=True, backoff_factor=backoff_factor, @@ -444,94 +425,21 @@ class HTTPClient: return session - def _initialize_cache_database(self): - """初始化缓存数据库""" - - # 创建缓存数据库连接(使用SQLite) - self.cache_connection = sqlite3.connect( - database="SQLite.db", check_same_thread=False - ) - - self.cache_connection.execute( - """CREATE TABLE IF NOT EXISTS caches (guid TEXT PRIMARY KEY, response TEXT, timestamp REAL)""" - ) - # 创建时间戳索引 - self.cache_connection.execute( - """CREATE INDEX IF NOT EXISTS index_timestamp ON caches(timestamp)""" - ) - # 删除过期缓存 - self.cache_connection.execute( - "DELETE FROM caches WHERE timestamp < ?", - (time.time() - self.cache_ttl * 86400,), # 缓存生存时间单位转为秒 - ) - # 提交事物 - self.cache_connection.commit() - - # 在缓存数据库查询响应 - def _query_response(self, guid: str) -> Optional[Dict]: - - with threading.Lock(): - cursor = None - try: - # 创建游标 - cursor = self.cache_connection.cursor() - # 根据请求唯一标识查询响应 - cursor.execute( - "SELECT response FROM caches WHERE guid = ? AND timestamp >= ?", - (guid, time.time() - self.cache_ttl * 86400), - ) - if result := cursor.fetchone(): - return json.loads(result[0]) - return None - # 若发生异常则返回NONE - except: - self.cache_connection.rollback() - return None - finally: - if cursor: - cursor.close() - - # 将响应保存至缓存数据库 - def _save_response(self, guid: str, response: Dict): - - with threading.Lock(): - cursor = None - try: - # 创建游标 - cursor = self.cache_connection.cursor() - # 新增或覆盖响应 - cursor.execute( - "INSERT OR REPLACE INTO caches (guid, response, timestamp) VALUES (?, ?, ?)", - (guid, json.dumps(response, ensure_ascii=False), time.time()), - ) - # 提交事物 - self.cache_connection.commit() - # 若发生异常则返回NONE - except: - self.cache_connection.rollback() - finally: - if cursor: - cursor.close() - # GET请求 def get(self, **kwargs) -> Union[Dict, str]: - return self._request(method="GET", arguments=Arguments(**kwargs)) # POST请求 def post(self, **kwargs) -> Union[Dict, str]: - return self._request(method="POST", arguments=Arguments(**kwargs)) - # 文件下载 + # DOWNLOAD请求 def download( self, stream=False, chunk_size=1024, **kwargs ) -> Union[Dict, str, Generator[bytes, None, None]]: - response = self._request( method="GET", arguments=Arguments(**{"stream": stream, **kwargs}) ) - # 若禁用流式传输,则返回响应 if not stream: return response @@ -539,100 +447,91 @@ class HTTPClient: return self._process_stream_response(response=response, chunk_size=chunk_size) def _request(self, method: Literal["GET", "POST"], arguments: Arguments) -> Any: - """发送请求""" - + """请求""" # 请求参数模型 arguments = arguments.model_dump(exclude_none=True, by_alias=True) - - # URL转为字符串 + # URL对象转为字符串 arguments["url"] = str(arguments["url"]) # 重构表单数据 if arguments.get("data") is not None: arguments["data"] = { - key: value - for key, value in arguments["data"].items() - if value is not None + k: v + for k, v in arguments["data"].items() + if v is not None } - # 重构JSON格式数据 + # 重构JSON数据 if arguments.get("json_data") is not None: arguments["json_data"] = { - key: value - for key, value in arguments["json_data"].items() - if value is not None + k: v + for k, v in arguments["json_data"].items() + if v is not None } # 重构文件数据 if arguments.get("files") is not None: - files_valid = {} - # 遍历文件数据键值对 - for key, value in arguments["files"].items(): - if isinstance(value, (tuple, list)): - match len(value): - # 若文件数据包括文件名称和文件内容 + files = {} + for k, v in arguments["files"].items(): + if isinstance(v, (tuple, list)): + match len(v): + # 文件数据包括文件名称和文件内容 case 2: - files_valid[key] = (value[0], value[1], None, None) - # 若文件数据包含文件名称、文件内容和内容类型 + files[k] = (v[0], v[1], None, None) + # 文件数据包含文件名称、文件内容和内容类型 case 3: - files_valid[key] = (value[0], value[1], value[2], None) - # 若文件数据包含文件名称、文件内容、内容类型和请求头 + files[k] = (v[0], v[1], v[2], None) + # 文件数据包含文件名称、文件内容、内容类型和请求头 case 4: - files_valid[key] = (value[0], value[1], value[2], value[3]) - arguments.update({"files": files_valid}) + files[k] = (v[0], v[1], v[2], v[3]) + arguments.update({"files": files}) # 全局唯一标识 guid = arguments.pop("guid", None) - - # 若使用缓存且本次请求参数包含全局唯一标识,则优先返回缓存数据库中响应 + # 若使用缓存且全局唯一标识非空则查询缓存 if self.cache_enabled and guid is not None: - # 在缓存数据库查询响应 - response = self._query_response(guid=guid) - # 若缓存响应非空则返回 - if response is not None: - return response + with CacheClient(cache_ttl=self.cache_ttl) as cache_client: + cache = cache_client.query(guid) + # 若缓存非空则返回 + if cache is not None: + return cache try: # 发送请求 response = self.session.request( method=method, timeout=self.timeout, **arguments ) - # 若返回错误状态码则抛出异常 response.raise_for_status() # 处理响应 response = self._process_response(response=response) - # 若请求全局唯一标识非NONE则响应保存至缓存数据库 - # noinspection PyUnboundLocalVariable - if guid is not None: - # noinspection PyUnboundLocalVariable - self._save_response(guid=guid, response=response) + # 若使用缓存且全局唯一标识非空则更新缓存 + if self.cache_enabled and guid is not None: + with CacheClient(cache_ttl=self.cache_ttl) as cache_client: + cache_client.update(guid, response) return response except Exception as exception: - # 尝试根据响应解析响应状态码和错误信息,否则进行构造 + # 尝试根据响应解析错误状态码和错误信息,否则进行构造 + # noinspection PyBroadException try: - # JSON反序列化 - # noinspection PyUnboundLocalVariable + # 响应反序列化 response_decoded = response.json() - # 响应状态码 + # 错误状态码 status = response_decoded["status"] # 错误信息 message = response_decoded["message"] except: status = getattr(getattr(exception, "response", None), "status", None) - url = arguments["url"] - message = str(exception).split("\n")[0] - # 重新构建错误信息 - message = f"{method} {url} failed: {message}" + # 重构错误信息 + message = f"{method} {arguments["url"]} failed: {str(exception).split("\n")[0]}" raise RequestException(status=status, message=message) # 处理响应 @staticmethod def _process_response(response: Response) -> Any: - # 响应内容 content = response.content # 若响应内容为空则返回NONE @@ -643,12 +542,11 @@ class HTTPClient: content_type = ( response.headers.get("Content-Type", "").split(";")[0].strip().lower() ) - # 根据内容类型匹配解析返回内容方法 # noinspection PyUnreachableCode match content_type: case "application/json" | "text/json": - # JSON反序列化 + # 响应反序列化 return response.json() case "application/xml" | "text/xml": # 解析为XML(ELEMENT对象) @@ -667,7 +565,6 @@ class HTTPClient: def _process_stream_response( response: Response, chunk_size: int ) -> Generator[bytes, None, None]: # 生成器不接受发SEND发送至、结束时返回NONE - # 检查数据分块 if not isinstance(chunk_size, int) and isinstance(chunk_size, bool): raise ValueError("chunk_size must type=int") @@ -689,14 +586,12 @@ class Authenticator: self, ): """认证器(用于获取访问令牌)""" - # 初始化 self._initialize() def _initialize(self): - """初始化访问凭证""" - - # 创建访问凭证地址对象 + """初始化""" + # 初始化访问凭证地址对象 self.certifications_path = ( Path(__file__).parent.resolve() / "certifications.json" ) @@ -709,40 +604,34 @@ class Authenticator: ensure_ascii=False, ) - # 初始化HTTP客户端 + # 初始化请求客户端 self.http_client = HTTPClient() def _szkt_get_certification(self) -> tuple[str, float]: """获取深圳快瞳访问凭证""" - - # 请求深圳快瞳访问凭证获取接口 response = self.http_client.get( url="https://ai.inspirvision.cn/s/api/getAccessToken?accessKey=APPID_6Gf78H59D3O2Q81u&accessSecret=947b8829d4d5d55890b304d322ac2d0d" ) - # 若响应非成功则抛出异常 + # 若非响应成功则抛出异常 if not (response["status"] == 200 and response["code"] == 0): raise RuntimeError("获取深圳快瞳访问凭证发生异常") - # 返回令牌、失效时间戳 + # 返回访问令牌、失效时间戳 # noinspection PyTypeChecker return ( response["data"]["access_token"], time.time() + response["data"]["expires_in"], ) - def _hlyj_get_certification(self) -> tuple[str, float]: + def _hlyj_get_certification(self) -> Tuple[str, float]: """获取合力亿捷访问凭证""" - # 企业访问标识 access_key_id = "25938f1c190448829dbdb5d344231e42" - # 签名秘钥 secret_access_key = "44dc0299aff84d68ae27712f8784f173" - # 时间戳(秒级) timestamp = int(time.time()) - # 签名,企业访问标识、签名秘钥和时间戳拼接后计算的十六进制的HMAC-SHA256 signature = hmac.new( secret_access_key.encode("utf-8"), @@ -750,26 +639,23 @@ class Authenticator: hashlib.sha256, ).hexdigest() - # 请求合力亿捷访问凭证获取接口 response = self.http_client.get( url=f"https://kms.7x24cc.com/api/v1/corp/auth/token?access_key_id={access_key_id}×tamp={timestamp}&signature={signature}" ) - # 若响应非成功则抛出异常 + # 若非响应成功则抛出异常 if not response["success"]: raise RuntimeError("获取合力亿捷访问凭证发生异常") - # 返回令牌、失效时间戳 + # 返回访问令牌、失效时间戳 # noinspection PyTypeChecker return ( response["data"], - time.time() + 3600, # 访问令牌有效期为1小时 + time.time() + 1 * 60 * 60, # 访问令牌有效期为1小时 ) def _feishu_get_certification(self) -> tuple[str, float]: """获取飞书访问凭证""" - - # 请求飞书访问凭证获取接口 response = self.http_client.post( url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", data={ @@ -778,11 +664,11 @@ class Authenticator: }, ) - # 若响应非成功则抛出异常 + # 若非响应成功则抛出异常 if not response["code"] == 0: raise RuntimeError("获取飞书访问凭证发生异常") - # 返回令牌、失效时间戳 + # 返回访问令牌、失效时间戳 # noinspection PyTypeChecker return ( response["tenant_access_token"], @@ -790,28 +676,28 @@ class Authenticator: ) def get_token(self, servicer: str) -> str | None: - """获取访问令牌""" """ - :param servicer: 服务商,数据类型为字符串 + 获取访问令牌 + :param servicer: 服务商,暂仅支持深圳快瞳、合力亿捷和飞书 + :return token: 访问令牌 """ - with threading.Lock(): - # 初始化令牌和失效时间戳 + # 初始化访问令牌和失效时间戳 token, expired_timestamp = None, 0 try: with open(self.certifications_path, "r", encoding="utf-8") as file: - # 读取所有服务商访问凭证 + # 本地打开并读取所有服务商的访问凭证 certifications = json.load(file) - # 获取服务商访问凭证 + # 获取指定服务商的访问凭证 certification = certifications.get(servicer, None) - # 若服务商访问凭证非NONE则解析令牌和失效时间戳 + # 若指定服务商的访问凭证非空则解析访问令牌和失效时间戳 if certification is not None: - # 解析服务商访问令牌 + # 访问令牌 token = certification["token"] - # 解析服务商访问令牌失效时间戳 + # 失效时间戳 expired_timestamp = certification["expired_timestamp"] - # 若JSON反序列化时发生异常则重置访问凭证 + # 若反序列化发生异常则重置访问凭证储存文件 except json.decoder.JSONDecodeError: with open(self.certifications_path, "w", encoding="utf-8") as file: json.dump( @@ -823,21 +709,17 @@ class Authenticator: except Exception: raise RuntimeError("获取访问令牌发生异常") - # 若当前时间戳大于失效时间戳,则请求服务商获取访问凭证接口 if time.time() > expired_timestamp: # noinspection PyUnreachableCode match servicer: - # 获取深圳快瞳访问凭证 case "szkt": token, expired_timestamp = self._szkt_get_certification() - case "feishu": - token, expired_timestamp = self._feishu_get_certification() - # 获取合力亿捷访问凭证 case "hlyj": token, expired_timestamp = self._hlyj_get_certification() + case "feishu": + token, expired_timestamp = self._feishu_get_certification() case _: - raise RuntimeError(f"未设置服务商:({servicer})") - + raise RuntimeError(f"未设置服务商:{servicer}获取访问凭证方法") # 更新服务商访问凭证 certifications[servicer] = { "token": token, @@ -855,22 +737,15 @@ class Authenticator: return token -""" -封装飞书客户端,实现获取验证码、操作多维表格等 -""" - - -class FeishuClinet: +class FeishuClient: + """飞书客户端""" def __init__(self): - self.authenticator = Authenticator() - self.http_client = HTTPClient() def _headers(self): """请求头""" - # 装配飞书访问凭证 return { "Authorization": f"Bearer {self.authenticator.get_token(servicer='feishu')}", @@ -878,67 +753,51 @@ class FeishuClinet: @staticmethod def get_verification_code(): - try: - # 执行时间戳 execute_timestamp = time.time() - # 超时时间戳 timeout_timestamp = execute_timestamp + 65 - # 建立加密IMAP连接 server = IMAP4_SSL("imap.feishu.cn", 993) - # 登录 server.login("mars@liubiren.cloud", "a2SfPUgbKDmrjPV2") while True: - # 若当前时间戳大于超时时间戳则返回NONE if time.time() <= timeout_timestamp: - # 等待10秒 time.sleep(10) - # 选择文件夹(邮箱验证码) server.select("&kK57sZqMi8F4AQ-") - + # noinspection PyBroadException try: - # 获取最后一封邮件索引,server.search()返回数据类型为元组,第一个元素为查询状态,第二个元素为查询结果(邮件索引字节串的列表);然后,从列表获取字节串并分割取最后一个,作为最后一封邮件索引 index = server.search(None, "ALL")[1][0].split()[-1] - # 获取最后一封邮件内容并解析,server.fetch()返回数据类型为元组,第一个元素为查询状态,第二个元素为查询结果(邮件内容字节串的列表);然后,从列表获取字节串并解析正文 # noinspection PyUnresolvedReferences contents = BytesParser(policy=default).parsebytes( server.fetch(index, "(RFC822)")[1][0][1] ) - # 遍历邮件内容,若正文内容类型为纯文本或HTML则解析发送时间和验证码 for content in contents.walk(): - if ( content.get_content_type() == "text/plain" or content.get_content_type() == "text/html" ): - # 邮件发送时间戳 # noinspection PyUnresolvedReferences send_timestamp = parsedate_to_datetime( content["Date"] ).timestamp() - # 若邮件发送时间戳大于执行时间戳则解析验证码并返回 if ( execute_timestamp > send_timestamp >= execute_timestamp - 35 ): - # 登出 server.logout() - # 解析验证码 return re.search( r"【普康健康】您的验证码是:(\d+)", @@ -947,18 +806,14 @@ class FeishuClinet: # 若文件夹无邮件则继续 except: - pass # 若超时则登出 else: - server.logout() - return None except Exception: - raise RuntimeError("获取邮箱验证码发生其它异常") # 查询多维表格记录,单次最多查询500条记录 @@ -970,9 +825,7 @@ class FeishuClinet: field_names: Optional[list[str]] = None, filter_conditions: Optional[dict] = None, ) -> pandas.DataFrame: - # 先查询多维表格记录,在根据字段解析记录 - # 装配多维表格查询记录地址 url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{bitable}/tables/{table_id}/records/search?page_size=20" diff --git a/票据理赔自动化/SQLite.db b/票据理赔自动化/SQLite.db index 5b061248f5d10e006529e0e7ef4fde317b41e8bf..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 274432 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

B@zBNdH1wJ*QwKW#mTpAQ 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

P*xBTB?RXmT?{zDrPDm} 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 diff --git a/票据理赔自动化/main.py b/票据理赔自动化/main.py index 7f8e3a3..5e6ae00 100644 --- a/票据理赔自动化/main.py +++ b/票据理赔自动化/main.py @@ -27,175 +27,6 @@ from utils.client import Authenticator, HTTPClient, CacheClient # from utils.ocr import fuzzy_match -# ------------------------- -# 封装方法 -# ------------------------- - - -# noinspection PyShadowingNames -def image_read( - image_path: Path, -) -> Tuple[numpy.ndarray | None, str | None, bytes | None]: - """ - 本地打开并读取影像件 - :param image_path: 影像件路径对象 - :return: 影像件数组、影像件格式和影像件字节流 - """ - # noinspection PyBroadException - try: - # 影像件打开并读取(默认转为单通道灰度图) - image_ndarray = cv2.imread(image_path.as_posix(), cv2.IMREAD_GRAYSCALE) - if image_ndarray is None: - raise RuntimeError("影像件打开并读取发生异常") - - # 影像件格式 - image_format = image_path.suffix.lower() - - # 按照影像件格式将影像件数组编码 - success, image_ndarray_encoded = cv2.imencode(image_format, image_ndarray) - if not success or image_ndarray_encoded is None: - raise RuntimeError("编码为图像字节数组发生异常") - # 将编码后图像数组转为字节流 - image_bytes = image_ndarray_encoded.tobytes() - - return image_ndarray, image_format, image_bytes - except Exception: - return None, None, None - - -# noinspection PyShadowingNames -def images_compress( - image_ndarray, image_format, image_bytes, image_size_specified=2 -) -> Optional[str]: - """ - 影像件压缩 - :param image_ndarray: 影像件数组 - :param image_format: 影像件格式 - :param image_bytes: 影像件字节流 - :param image_size_specified: 指定影像件大小,单位为兆字节(MB) - :return: 压缩后影像件BASE64编码 - """ - # 将指定影像件大小单位由兆字节转为字节 - image_size_specified = image_size_specified * 1024 * 1024 - - # 影像件BASE64编码 - image_base64 = b64encode(image_bytes).decode("utf-8") - if len(image_base64) <= image_size_specified: - return image_base64 - - # 通过调整影像件质量和尺寸达到压缩影像件目的 - # 外循环压缩:通过调整影像件质量实现压缩影像件大小 - for quality in range(90, 50, -10): - image_ndarray_copy = image_ndarray.copy() - # 内循环压缩:通过调整影像件尺寸实现压缩影像件大小 - for i in range(25): - # 按照影像件格式和影像件质量将影像件数组编码 - success, image_ndarray_encoded = cv2.imencode( - image_format, - image_ndarray_copy, - params=( - [cv2.IMWRITE_PNG_COMPRESSION, 10 - quality // 10] - if image_format == "png" - else [cv2.IMWRITE_JPEG_QUALITY, quality] - ), - ) - if not success or image_ndarray_encoded is None: - break - - # 影像件BASE64编码 - image_base64 = b64encode(image_ndarray_encoded.tobytes()).decode("utf-8") - if len(image_base64) <= image_size_specified: - return image_base64 - - # 调整影像件尺寸 - image_ndarray_copy = cv2.resize( - image_ndarray_copy, - ( - int(image_ndarray_copy.shape[0] * 0.9), - int(image_ndarray_copy.shape[1] * 0.9), - ), - interpolation=cv2.INTER_AREA, - ) - # 若调整后影像件尺寸中长或宽小于350像素则停止调整影像件尺寸 - if min(image_ndarray_copy.shape[:2]) < 350: - break - - return None - - -def images_classify( - image, image_ndarray, image_format, image_bytes -) -> tuple[str | None, str | None]: - """ - 影像件分类并旋正 - :param image: 影像件数据 - :param image_ndarray: 影像件数组 - :param image_format: 影像件格式 - :param image_bytes: 影像件字节流 - :return: 压缩后影像件BASE64编码 - """ - # 影像件唯一标识 - image_uuid = image["影像件唯一标识"] - with CacheClient() as client: - # 根据作业环节和影像件唯一标识生成缓存唯一标识 - cache_guid = md5(("初审" + image_uuid).encode("utf-8")).hexdigest().upper() - cache = client.query(cache_guid) - - if cache is not None: - # 影像件类型 - image_type = cache["image_type"] - # 影像件方向 - image_o - - # 请求深圳快瞳影像件分类接口 - response = globals()["http_client"].post( - url=(url := "https://ai.inspirvision.cn/s/api/ocr/genalClassify"), - # 用于和深圳快瞳联查定位 - headers={"X-RequestId-Header": image_guid}, - data={ - "token": globals()["authenticator"].get_token( - servicer="szkt" - ), # 使用全局变量 - "imgBase64": f"data:image/{image_format};base64,{image_base64}", # 深圳快瞳要求修饰影像件BASE64编码的DATAURI - }, - 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, None - - # 根据票据类型和票据二级分类映射影像件类型 - match (response["data"]["flag"], response["data"]["type"]): - case (7, "idcard-front-back"): - image_type = "居民身份证(正背面)" - case (7, "idcard-front"): - image_type = "居民身份证(正面)" # 包含国徽一面 - case (7, "idcard-back"): - image_type = "居民身份证(背面)" # 包含头像一面 - case (8, _): - image_type = "银行卡" - case (4, _): - image_type = "增值税发票" - case (5, _): - image_type = "门诊收费票据" - case (3, _): - image_type = "住院收费票据" - case (18, _): - image_type = "理赔申请书" - case _: - return None, None - - # 影像件方向 - image_orientation = { - "0": "0度", - "90": "顺时针90度", - "180": "180度", - "270": "逆时针90度", - }.get(response["data"]["angle"], "0度") - - return image_type, image_orientation - def idcard_extraction(**kwargs) -> dict | None: """居民身份证数据提取""" @@ -1140,29 +971,25 @@ def disease_diagnosis(**kwargs) -> str | None: # ------------------------- -# 主逻辑部分 +# 主逻辑 # ------------------------- - - if __name__ == "__main__": - # 初始化HTTP客户端 - http_client = HTTPClient(timeout=300, cache_enabled=True) - - # 初始化认证器 + # 实例认证器 authenticator = Authenticator() + # 实例请求客户端 + http_client = HTTPClient(timeout=300, cache_enabled=True) # 使用缓存 + + # 初始化影像件识别规则引擎 + recognize_decision = decision(Path("rules/影像件是否需要数据提取.json")) # 初始化工作目录地址对象 directory_path = Path("directory") # 若不存在则创建 directory_path.mkdir(parents=True, exist_ok=True) - # 初始化影像件识别规则引擎 - recognize_decision = decision(Path("rules/影像件是否需要数据提取.json")) - # 初始化JINJA2环境 environment = Environment(loader=FileSystemLoader(".")) - # 添加DATE过滤器 environment.filters["date"] = lambda date: ( date.strftime("%Y-%m-%d") if date else "长期" @@ -1170,21 +997,212 @@ if __name__ == "__main__": # 加载赔案档案模版 template = environment.get_template("template.html") - # 遍历工作目录中赔案目录,根据赔案创建赔案档案(模拟自动化域就待自动化任务创建理赔档案) + + # ------------------------- + # 自定义方法 + # ------------------------- + # noinspection PyShadowingNames + def image_read( + image_path: Path, + ) -> Optional[numpy.ndarray | None]: + """ + 本地打开并读取影像件 + :param image_path: 影像件路径对象 + :return: 影像件数组 + """ + # noinspection PyBroadException + try: + # 影像件打开并读取(默认转为单通道灰度图) + image_ndarray = cv2.imread(image_path.as_posix(), cv2.IMREAD_GRAYSCALE) + if image_ndarray is None: + raise RuntimeError("影像件打开并读取发生异常") + + return image_ndarray + except Exception: + # 若本地打开并读取影像件发生异常则抛出异常(实际作业需从影像件服务器下载并读取影像件,因签收时会转存,故必可下载) + raise RuntimeError("影像件打开并读取发生异常") + + + # noinspection PyShadowingNames + def image_serialize(image_format: str, image_ndarray: numpy.ndarray) -> str: + """ + 影像件序列化 + :param image_format: 影像件格式 + :param image_ndarray: 影像件数组 + :return: 影像件唯一标识 + """ + # 按照影像件格式就影像件数组编码 + success, image_ndarray_encoded = cv2.imencode(image_format, image_ndarray) + if not success or image_ndarray_encoded is None: + raise RuntimeError("编码为图像字节数组发生异常") + + # 将编码后图像数组转为字节流 + image_bytes = image_ndarray_encoded.tobytes() + # 生成影像件唯一标识 + image_guid = md5(image_bytes).hexdigest().upper() + return image_guid + + + # noinspection PyShadowingNames + def images_classify( + image_guid: str, image_format: str, image_ndarray: numpy.ndarray + ) -> Optional[Tuple[str, str, str]]: + """ + 影像件分类并旋正 + :param image_guid: 影像件唯一标识 + :param image_format: 影像件格式 + :param image_ndarray: 影像件数据 + :return: 压缩后影像件BASE64编码,影像件类型和影像件方向 + """ + + # noinspection PyShadowingNames + def images_compress( + image_format, image_ndarray, image_size_specified=2 + ) -> Optional[str]: + """ + 影像件压缩 + :param image_ndarray: 影像件数组 + :param image_format: 影像件格式 + :param image_size_specified: 指定影像件大小,单位为兆字节(MB) + :return: 压缩后影像件BASE64编码 + """ + # 将指定影像件大小单位由兆字节转为字节 + image_size_specified = image_size_specified * 1024 * 1024 + + # 通过调整影像件质量和尺寸达到压缩影像件目的 + # 外循环压缩:通过调整影像件质量实现压缩影像件大小 + for quality in range(100, 50, -10): + image_ndarray_copy = image_ndarray.copy() + # 内循环压缩:通过调整影像件尺寸实现压缩影像件大小 + for i in range(10): + # 按照影像件格式和影像件质量将影像件数组编码 + success, image_ndarray_encoded = cv2.imencode( + image_format, + image_ndarray_copy, + params=( + [cv2.IMWRITE_PNG_COMPRESSION, 10 - quality // 10] + if image_format == "png" + else [cv2.IMWRITE_JPEG_QUALITY, quality] + ), + ) + # 若编码发生异常则停止循环 + if not success or image_ndarray_encoded is None: + break + + # 影像件BASE64编码 + image_base64 = b64encode(image_ndarray_encoded.tobytes()).decode("utf-8") + if len(image_base64) <= image_size_specified: + return image_base64 + + # 调整影像件尺寸 + image_ndarray_copy = cv2.resize( + image_ndarray_copy, + ( + int(image_ndarray_copy.shape[0] * 0.95), + int(image_ndarray_copy.shape[1] * 0.95), + ), + interpolation=cv2.INTER_AREA, + ) + # 若调整后影像件尺寸中长或宽小于350像素则停止调整影像件尺寸 + if min(image_ndarray_copy.shape[:2]) < 350: + break + + return None + + # 影像件压缩 + image_base64 = images_compress(image_format, image_ndarray, image_size_specified=2) # 深圳快瞳要求为2兆字节 + # TODO: 若影像件压缩发生异常则流转至人工处理 + if image_base64 is None: + raise RuntimeError("影像件压缩发生异常") + + # 请求深圳快瞳影像件分类接口 + response = http_client.post( + url=(url := "https://ai.inspirvision.cn/s/api/ocr/genalClassify"), + headers={"X-RequestId-Header": image_guid}, # 以影像件唯一标识作为请求唯一标识,用于双方联查 + data={ + "token": authenticator.get_token( + servicer="szkt" + ), # 获取深圳快瞳访问令牌 + "imgBase64": f"data:image/{image_format.lstrip(".")};base64,{image_base64}", # 影像件BASE64编码嵌入数据统一资源标识符 + }, + guid=md5((url + image_guid).encode("utf-8")).hexdigest().upper(), + ) + # 若响应非成功则抛出异常 + # TODO: 若响应非成功则流转至人工处理 + if not (response.get("status") == 200 and response.get("code") == 0): + raise RuntimeError("请求深圳快瞳影像件分类接口发生异常") + + # 解析影像件类型 + # TODO: 后续完成居民户口簿、中国港澳台地区及境外护照和医疗费用清单 + # noinspection PyTypeChecker + match (response["data"]["flag"], response["data"]["type"]): + case (14, _): + image_type = "居民户口簿" + case (7, "idcard-front-back"): + image_type = "居民身份证(国徽、头像面)" + case (7, "idcard-front"): + image_type = "居民身份证(国徽面)" + case (7, "idcard-back"): + image_type = "居民身份证(头像面)" + case (11, _): + image_type = "中国港澳台地区及境外护照" + case (8, _): + image_type = "银行卡" + case (4, _): + image_type = "增值税发票" + case (1, _): + image_type = "医疗费用清单" + case (5, _): + image_type = "门诊收费票据" + case (3, _): + image_type = "住院收费票据" + case (18, _): + image_type = "理赔申请书" + case _: + image_type = "其它" + + # 解析影像件方向 + # noinspection PyTypeChecker + image_orientation = { + "0": "0度", + "90": "顺时针90度", + "180": "180度", + "270": "逆时针90度", + }.get(response["data"]["angle"], "0度") + # 若影像件方向非0度则旋正 + if image_orientation != "0度": + image_ndarray = cv2.rotate( + image_ndarray, + { + "顺时针90度": cv2.ROTATE_90_COUNTERCLOCKWISE, # 逆时针旋转90度 + "180度": cv2.ROTATE_180, # 旋转180度 + "逆时针90度": cv2.ROTATE_90_CLOCKWISE, # 顺时针旋转90度 + }[image_orientation], + ) + # 旋正后影像件再次压缩 + image_base64 = images_compress(image_format, image_ndarray, image_size_specified=2) + # TODO: 若旋正后影像件再次压缩发生异常则流转至人工处理 + if image_base64 is None: + raise RuntimeError("旋正后影像件再次压缩发生异常") + + return image_base64, image_type, image_orientation + + + # 遍历工作目录中赔案目录并创建赔案档案(模拟自动化域就待自动化任务创建理赔档案) for case_path in [ - case_path for case_path in directory_path.iterdir() if case_path.is_dir() + x for x in directory_path.iterdir() if x.is_dir() ]: # 初始化赔案档案(实际报案层包括保险分公司名称、报案渠道、批次号、报案号和报案时间等) # 报案渠道包括:保司定义,例如中银项目包括总行和各地分行驻场报案和普康宝自助报案等 dossier = { "报案层": { - "保险分公司": "中银保险有限公司广东分公司", # 设定:保险分公司 + "保险分公司": "中银保险有限公司广东分公司", # 设定:保险分公司为中银保险有限公司广东分公司 "赔案号": (case_number := case_path.stem), # 设定:赔案目录名称为赔案号 }, "影像件层": [], } - # 遍历赔案目录中影像件地址 + # 遍历赔案目录中影像件路径对象 for image_index, image_path in enumerate( sorted( [ @@ -1201,64 +1219,31 @@ if __name__ == "__main__": "原始影像件": { "影像件地址": image_path.as_posix(), # 将影像件路径对象转为字符串 "影像件名称": (image_name := image_path.stem), + "影像件格式": (image_format := image_path.suffix.lower()), }, } - # 本地打开并读取影像件(实际作业为从作业系统的影像件服务器下载并读取影像件,因赔案签收时会转存影像件至影像件服务器,若下载并读取影像件发生异常则技术支持排查) - image_ndarray, image_format, image_bytes = image_read(image_path) - # 若本地打开并读取影像件发生异常则跳过该影像件 - if image_format is None or image_bytes is None: - raise RuntimeError("本地打开并读取影像件发生异常") + # 本地打开并读取影像件 + image_ndarray = image_read(image_path) - image["原始影像件"]["影像件格式"] = image_format - # 生成影像件唯一标识 + # 影像件序列化 # noinspection PyTypeChecker - image["影像件唯一标识"] = md5(image_bytes).hexdigest().upper() + image["影像件唯一标识"] = (image_guid := image_serialize(image_format, image_ndarray)) - # 影像件分类 + # 影像件分类并旋正(较初审自动化,无使能检查) + image_base64, image_type, image_orientation = images_classify(image_guid, image_format, image_ndarray) + image["影像件类型"] = image_type + image["影像件方向"] = image_orientation + # 将影像件数据添加至影像件层 + dossier["影像件层"].append(image) - print(image) - exit() + # 影像件识别 + + + print(dossier) + exit() """ - # 影像件压缩(输出BASE64编码) - image_guid, image_base64 = images_compression() - # 若发生异常则跳过该影像件 - if image_guid is None or image_base64 is None: - dossier["影像件层"][-1]["已分类"] = "否,压缩异常" - continue - - # 通过请求深圳快瞳影像件分类接口获取影像件类型和方向 - image_type, image_orientation = images_classification() - # 若发生异常则跳过该影像件 - if image_type is None or image_orientation is None: - dossier["影像件层"][-1]["已分类"] = "否,影像件分类异常" - continue - # - dossier["影像件层"].append( - { - "影像件序号": (image_index := f"{image_index:02d}"), - "影像件名称": (image_name := image_path.name), - } - ) - # 若影像件方向非0度,则影像件旋正并在此压缩 - if image_orientation != "0度": - # 影像件旋正 - image = cv2.rotate( - image, - { - "顺时针90度": cv2.ROTATE_90_COUNTERCLOCKWISE, # 逆时针旋转90度 - "180度": cv2.ROTATE_180, # 旋转180度 - "逆时针90度": cv2.ROTATE_90_CLOCKWISE, # 顺时针旋转90度 - }[image_orientation], - ) - # 影像件再次压缩 - image_guid, image_base64 = images_compression() - if image_guid is None or image_base64 is None: - dossier["影像件层"][-1]["已分类"] = "否,压缩异常" - continue - - dossier["影像件层"][-1].update({"已分类": "是", "影像件类型": image_type}) # 根据保险总公司和影像件类型评估影像件是否需要数据提取,若无需数据提取则跳过该影像件(例如,中银保险有限公司理赔申请书包含户名、开户银行和银行账号,无需识别银行卡) if not recognize_decision.evaluate(