初始化项目
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
OA 系统对接模块。
|
||||
"""
|
||||
|
||||
class PushException(Exception):
|
||||
"""
|
||||
推送异常,用于发给OA系统。
|
||||
"""
|
||||
def __init__(self, message, flow_token=None, return_code=None):
|
||||
super().__init__(message)
|
||||
self.flow_token = flow_token
|
||||
self.return_code = return_code
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
OA 对接 API 基础功能。
|
||||
"""
|
||||
import asyncio
|
||||
import datetime
|
||||
import json
|
||||
|
||||
import apps
|
||||
import dock
|
||||
from paste.core import config
|
||||
from paste.core.logging import echo_log
|
||||
|
||||
|
||||
ApiUrl = config.get_config(f'dock.oa.env.{apps.get_active_env()}.api_url')
|
||||
"""
|
||||
对接 API 根目录。
|
||||
"""
|
||||
|
||||
|
||||
TokenPlatform = config.get_config(f'dock.oa.env.{apps.get_active_env()}.token_platform')
|
||||
"""
|
||||
OA Token 平台。
|
||||
"""
|
||||
|
||||
|
||||
async def new_api_request(api_url: str, request_body: dict, method: str = 'POST',
|
||||
timeout: float = dock.DEFAULT_TIMEOUT, use_form: bool = False, headers: dict = None):
|
||||
"""
|
||||
构造一个 API 请求对象
|
||||
|
||||
:param api_url: API 地址,以斜杠开头的 URI 地址,非完整 URL
|
||||
:param request_body: 请求体,即所有请求参数
|
||||
:param method: 请求提交方式
|
||||
:param timeout: 超时时长
|
||||
:param use_form: 是否使用表单(Form)方式提交
|
||||
:param headers: 头数据,最高优先级
|
||||
:return: HTTPRequest 对象
|
||||
"""
|
||||
# Token
|
||||
from dock.oa import oa_security
|
||||
token = await oa_security.get_token(TokenPlatform)
|
||||
|
||||
# 构建扩展头
|
||||
user_agent, browser_ver, os_name = dock.get_random_user_agent()
|
||||
extra_headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Token': token,
|
||||
'User-Agent': user_agent,
|
||||
}
|
||||
if headers is not None:
|
||||
extra_headers = {**extra_headers, **headers}
|
||||
|
||||
try:
|
||||
echo_log(json.dumps(request_body))
|
||||
except Exception:
|
||||
echo_log(str(request_body))
|
||||
|
||||
# 构造请求对象
|
||||
request = dock.new_http_request(
|
||||
url=f"{ApiUrl}{api_url}",
|
||||
body=request_body,
|
||||
method=method,
|
||||
timeout=timeout,
|
||||
use_form=use_form,
|
||||
extra_headers=extra_headers,
|
||||
)
|
||||
return request
|
||||
|
||||
|
||||
# 使用 asyncio.Lock 保证线(协)程安全
|
||||
_lock = asyncio.Lock()
|
||||
# _cache 作为内存缓存,结构 {date: counter}
|
||||
_cache = {}
|
||||
|
||||
async def generate_serial_number():
|
||||
"""
|
||||
取得当日流水号。
|
||||
:return: 流水号字符串
|
||||
"""
|
||||
today = datetime.datetime.now().strftime("%Y%m%d")
|
||||
async with _lock:
|
||||
if today not in _cache:
|
||||
_cache[today] = 1
|
||||
else:
|
||||
_cache[today] += 1
|
||||
counter = _cache[today]
|
||||
return f"{today}{counter:05d}"
|
||||
@@ -0,0 +1,469 @@
|
||||
"""
|
||||
创建 OA 接口请求对象。
|
||||
对应文档接口:7、推送附件信息
|
||||
"""
|
||||
import io
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
from dock.oa import oa_api
|
||||
from paste.core.logging import echo_log
|
||||
from paste.util import uimg
|
||||
|
||||
|
||||
async def get_push_order_request(dcm_tasks: list):
|
||||
"""
|
||||
取得推送待办工单列表的请求对象。
|
||||
对应文档接口:2、推送待办工单列表。
|
||||
接口文档说明:接收数字城管待办列表数据,并保存到 OA 系统。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
dcm_tasks: 待办工单列表,工单对象须包含以下键值:
|
||||
|
||||
- gdId (str): 待办工单ID,雪花ID。
|
||||
- attachmentList (list[dict]): 附件列表
|
||||
- taskNum (str): 任务号
|
||||
- otherTaskNum (str): 第三方任务号
|
||||
- bundleDeadlineTimeStr (str): 捆绑截止时间
|
||||
- rollbackDeadlineStr (str): 拒绝超时截止时间
|
||||
- eventSrcName (str): 问题来源
|
||||
- recTypeName (str): 案件类型
|
||||
- eventTypeName (str): 问题类型
|
||||
- mainTypeName (str): 大类名称
|
||||
- subTypeName (str): 小类名称
|
||||
- urgencyLevel (str): 紧急程度
|
||||
- eventDesc (str): 问题描述
|
||||
- address (str): 地址描述
|
||||
- disposalTimeLimit (str): 处置时限
|
||||
- districtName (str): 所属区域
|
||||
- newInstCondName (str): 立案条件
|
||||
- closingConditions (str): 结案条件
|
||||
- reporterName (str): 举报人
|
||||
- reporterContact (str): 举报电话
|
||||
- replyIntime (str): 是否两小时回复
|
||||
- firstDepartName (str): 一级专业部门
|
||||
- secondDepartName (str): 二级专业部门
|
||||
- bundleWarningTimeStr (str): 捆绑警告时间
|
||||
- actArdStateName (str): 阶段授权状态
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f"/externalWorkOrder/digitalCM/pushWaitingSignatureOrder"
|
||||
request_no = await oa_api.generate_serial_number()
|
||||
request_body = {
|
||||
"requestNo": request_no,
|
||||
"toDoList": dcm_tasks,
|
||||
}
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, request_body)
|
||||
|
||||
|
||||
async def get_upload_request(file: Union[str, io.IOBase], file_name: str, first_save: bool = True):
|
||||
"""
|
||||
取得文件上传接口的请求对象。
|
||||
对应文档接口:3、文件上传接口。
|
||||
接口文档说明:上传指定的文件,返回文件在服务器上的id。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
file: 文件路径、URL 或文件对象,支持三种输入类型:
|
||||
|
||||
- str (本地文件路径): 如 "/tmp/file.pdf",先从本地文件读取,再写入请求对象
|
||||
- str (远程 URL): 如 "https://example.com/file.pdf",先同步从远程地址下载,然后再写入请求对象
|
||||
- io.IOBase: 如 open(..., 'rb') 或 io.BytesIO
|
||||
|
||||
file_name: 文件名
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = "/attachment?applicationCategory=66"
|
||||
if first_save:
|
||||
api_url += '&firstSave=true'
|
||||
|
||||
# 如果是远程 URL,先下载内容
|
||||
if isinstance(file, str) and (file.startswith("http://") or file.startswith("https://")):
|
||||
echo_log(f"正在从 URL 下载附件: {file}")
|
||||
response, content_type = uimg.fetch_image(file) # 获取二进制内容
|
||||
file_content = b''.join(response.iter_content(1024))
|
||||
file_obj = io.BytesIO(file_content)
|
||||
|
||||
# 如果是本地文件路径,打开为二进制流
|
||||
elif isinstance(file, str) and os.path.exists(file):
|
||||
echo_log(f"正在从本地路径读取附件: {file}")
|
||||
with open(file, 'rb') as f:
|
||||
file_obj = io.BytesIO(f.read())
|
||||
|
||||
# 如果是文件对象(如 BytesIO, BufferedReader),直接使用
|
||||
elif hasattr(file, 'read') and callable(file.read):
|
||||
echo_log("正在使用传入的文件对象")
|
||||
file_obj = io.BytesIO(file.read())
|
||||
|
||||
else:
|
||||
raise TypeError(f"不支持的文件类型:{type(file)},应提供:文件路径,URL或文件对象.")
|
||||
|
||||
request_body = {
|
||||
file_name: file_obj,
|
||||
}
|
||||
|
||||
# 这里启用了 multipart/form-data 上传
|
||||
return await oa_api.new_api_request(
|
||||
api_url=api_url,
|
||||
request_body=request_body,
|
||||
method='POST',
|
||||
use_form=True,
|
||||
)
|
||||
|
||||
|
||||
async def get_download_request(media_id: str):
|
||||
"""
|
||||
取得文件下载接口的请求对象。
|
||||
对应文档接口:4、文件下载接口。
|
||||
接口文档说明:上传指定的文件,返回文件在服务器上的id。
|
||||
接口请求方式:POST
|
||||
接口返回格式:application/octet-stream;charset=UTF-8
|
||||
|
||||
Args:
|
||||
media_id: 文件 Media ID。
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f"/attachment/file/{media_id}"
|
||||
return await oa_api.new_api_request(api_url=api_url, request_body={}, method='GET')
|
||||
|
||||
|
||||
async def get_push_order_detail_request(**kwargs):
|
||||
"""
|
||||
取得推送工单详情的请求对象。
|
||||
对应文档接口:5、推送工单详情。
|
||||
接口文档说明:接收数字城管工单详情。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
**kwargs: 请求参数,须包含以下键值:
|
||||
|
||||
- gdId (str): 待办工单ID,雪花ID。
|
||||
- partCode (str): 部件编码。
|
||||
- funcLimitChar (str): 小类时限。
|
||||
- reporterName (str): 举报人。
|
||||
- mediaUploadTotalNum (str): 上传附件数。
|
||||
- returnVisitFlag (str): 是否回访。
|
||||
- undertakeUserName (str): 承办人员。
|
||||
- violationTaskNoDd (str): 市容违规任务号。
|
||||
- telReply (str): 回访电话。
|
||||
- funcForbidReporterInfoFlag (str): 是否公开。
|
||||
- dealPersonOrg (str): 承办部门。
|
||||
- contactNumberDd (str): 联系电话。
|
||||
- reportNumberDd (str): 举报电话。
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f"/externalWorkOrder/digitalCM/pushOrderDetail"
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, kwargs)
|
||||
|
||||
|
||||
async def get_push_process_info_request(**kwargs):
|
||||
"""
|
||||
取得推送办理经过的请求对象。
|
||||
对应文档接口:6、推送办理经过。
|
||||
接口文档说明:接收数字城管工单办理经过,保存到子表。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
**kwargs: 请求参数,须包含以下键值:
|
||||
|
||||
- gdId (str): 待办工单ID,雪花ID。
|
||||
- checkContent (str): 核查内容,拼接第一条办理经过得到
|
||||
- handlingProcessList (list[dict]): 办理经过列表,每个元素须包含以下字段:
|
||||
|
||||
- id (str): 唯一标识符,雪花ID。
|
||||
- actionTime (str): 操作时间,格式为 yyyy-MM-dd HH:mm:ss。
|
||||
- actDefName (str): 实际操作名称。
|
||||
- humanName (str): 经办人姓名。
|
||||
- unitName (str): 办理部门名称。
|
||||
- actionName (str): 操作类型名称。
|
||||
- nextActDefName (str): 下一环节名称。
|
||||
- detail (str): 操作意见或备注内容。
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f"/externalWorkOrder/digitalCM/pushProcessLog"
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, kwargs)
|
||||
|
||||
|
||||
async def get_push_attachment_request(**kwargs):
|
||||
"""
|
||||
取得推送附件信息的请求对象。
|
||||
对应文档接口:7、推送附件信息。
|
||||
接口文档说明:接收数字城管工单附件信息列表。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
**kwargs: 请求参数,须包含以下键值:
|
||||
|
||||
- gdId (str): 待办工单ID,雪花ID。
|
||||
- attachmentList (list[dict]): 附件列表,每个元素须包含以下字段:
|
||||
|
||||
- id (str): 唯一标识符,雪花ID。
|
||||
- mediaId (str): 媒体资源的唯一标识符,上传文件后,从 OA 平台取得,对应响应为:fileUrl。
|
||||
- mediaUsage (str): 使用场景,例如:上报、回退。
|
||||
- actDefName (str): 流程节点名称,例如:各区平台、一级专业部门、二级专业部门。
|
||||
- uploadCreateTime (str): 附件上传时间,可选。
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f"/externalWorkOrder/digitalCM/pushAttachmentInfo"
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, kwargs)
|
||||
|
||||
|
||||
async def get_push_more_info_request(**kwargs):
|
||||
"""
|
||||
取得推送更多信息的请求对象。
|
||||
对应文档接口:8、推送更多信息。
|
||||
接口文档说明:接收数字城管工单更多信息列表。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
**kwargs: 请求参数,须包含以下键值:
|
||||
|
||||
- gdId (str): 待办工单ID,雪花ID。
|
||||
- moreInfoList (list[dict]): 更多信息列表,每个元素须包含以下字段:
|
||||
|
||||
- id (str): 唯一标识符,雪花ID。
|
||||
- content (str): 内容。
|
||||
- time (str): 时间。
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f"/externalWorkOrder/digitalCM/pushMoreInfo"
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, kwargs)
|
||||
|
||||
|
||||
async def get_push_extend_info_request(**kwargs):
|
||||
"""
|
||||
取得推送扩展信息的请求对象。
|
||||
对应文档接口:9、推送扩展信息。
|
||||
接口文档说明:接收数字城管工单扩展信息列表。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
**kwargs: 请求参数,须包含以下键值:
|
||||
|
||||
- gdId (str): 待办工单ID,雪花ID。
|
||||
- extendList (list[dict]): 扩展信息列表,每个元素须包含以下字段:
|
||||
|
||||
- id (str): 唯一标识符,雪花ID。
|
||||
- fieldName: 属性。
|
||||
- fieldValue: 值。
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = '/externalWorkOrder/digitalCM/pushExtendInfo'
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, kwargs)
|
||||
|
||||
|
||||
async def get_result_notify_request(flow_token: str, message: str, return_code: int):
|
||||
"""
|
||||
取得上报单条工单的操作结果的请求对象。
|
||||
对应文档接口:10、上报单条工单的操作结果。
|
||||
接口文档说明:皓凯平台调用接口推送接口处理结果。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
flow_token (str): 工作流令牌
|
||||
message (str): 接口调用返回说明
|
||||
return_code (int): 操作类型,相关值说明如下:
|
||||
|
||||
- 1: 成功。
|
||||
- 2: 回退,超过3次,超过3次失败人为干预(3)。
|
||||
- 3: 人为干预。
|
||||
- 4: 失败。
|
||||
- 5: 停止。
|
||||
- 6: 取消。
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f'/flow/notification/{flow_token}'
|
||||
# 请求体参数
|
||||
request_body = {
|
||||
"message": message,
|
||||
"returnCode": return_code
|
||||
}
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, request_body)
|
||||
|
||||
|
||||
async def get_sign_task_request(task_id: Union[str, int]):
|
||||
"""
|
||||
取得签收工单的请求对象。
|
||||
对应文档接口:11、签收。
|
||||
接口文档说明:皓凯平台调用接口实现签收工单。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
task_id: 待办工单ID,雪花ID
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f'/externalWorkOrder/digitalCM/gdSign?gdId={task_id}'
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, {})
|
||||
|
||||
|
||||
async def get_update_process_delay_request(task_id: str, bundle_deadline_time_str: str, rollback_deadline_str: str):
|
||||
"""
|
||||
取得更新流程延期信息的请求对象。
|
||||
对应文档接口:12、更新流程延期信息。
|
||||
接口文档说明:更新流程延期信息。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
task_id (str): 待办工单ID,雪花ID。
|
||||
bundle_deadline_time_str (str): 捆绑截止时间,格式:yyyy-MM-dd HH:mm:ss
|
||||
rollback_deadline_str (str): 拒绝超时截止时间,格式:yyyy-MM-dd HH:mm:ss
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
api_url = '/externalWorkOrder/digitalCM/updateProcessDelayInfo'
|
||||
request_body = {
|
||||
'gdId': str(task_id),
|
||||
'bundleDeadlineTimeStr': bundle_deadline_time_str,
|
||||
'rollbackDeadlineStr': rollback_deadline_str
|
||||
}
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, request_body)
|
||||
|
||||
|
||||
async def get_push_govs_order_master_request(govs_tasks: list):
|
||||
"""
|
||||
获取推送12345待签收工单到OA的请求
|
||||
对应文档接口:2、推送待签收工单列表
|
||||
接口文档说明:皓凯平台调用接口推送待签收工单列表给OA
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
govs_tasks: 待签收工单列表
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
|
||||
# 接口地址
|
||||
api_url = '/externalWorkOrder/pushWaitingSignatureOrder'
|
||||
# 构造 API 请求
|
||||
request_no = await oa_api.generate_serial_number()
|
||||
request_body = {
|
||||
"requestNo": request_no,
|
||||
"toBeSignedList": govs_tasks,
|
||||
}
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, request_body)
|
||||
|
||||
|
||||
async def get_push_govs_order_detail_request(**kwargs):
|
||||
"""
|
||||
取得推送12345工单详情的请求对象。
|
||||
对应文档接口:5、推送工单详情。
|
||||
接口文档说明:接收12345工单详情。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
|
||||
# 接口地址
|
||||
api_url = '/externalWorkOrder/pushOrderDetail'
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, kwargs)
|
||||
|
||||
|
||||
async def get_push_govs_process_request(**kwargs):
|
||||
"""
|
||||
取得推送办理经过的请求对象。
|
||||
对应文档接口:6、推送工单处理流程列表。
|
||||
接口文档说明:接收12345工单办理经过,保存到子表。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f"/externalWorkOrder/pushProcessLog"
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, kwargs)
|
||||
|
||||
|
||||
async def get_push_gov_process_request(**kwargs):
|
||||
"""
|
||||
取得推送省12345办理经过的请求对象。
|
||||
对应文档接口:6、推送工单处理流程列表。
|
||||
接口文档说明:接收省12345工单办理经过,保存到子表。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f"/externalWorkOrder/pushProcessLog"
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, kwargs)
|
||||
|
||||
|
||||
async def get_sign_govs_task_request(task_id: Union[str, int]):
|
||||
"""
|
||||
取得签收工单的请求对象。
|
||||
对应文档接口:11、签收。
|
||||
接口文档说明:皓凯平台调用接口实现签收工单。
|
||||
接口请求方式:POST
|
||||
接口返回格式:JSON
|
||||
|
||||
Args:
|
||||
task_id: 待办工单ID,雪花ID
|
||||
|
||||
Returns:
|
||||
HTTPRequest: 构造好的 HTTP 请求对象,用于后续异步调用。
|
||||
"""
|
||||
# 接口地址
|
||||
api_url = f'/externalWorkOrder/gdSign?gdId={task_id}'
|
||||
# 构造 API 请求
|
||||
return await oa_api.new_api_request(api_url, {})
|
||||
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
上报 D3I 与 DCM 接口对接结果。
|
||||
|
||||
对应文档接口:10、上报单条工单的操作结果
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from tornado.httpclient import HTTPResponse, HTTPRequest
|
||||
|
||||
import dock
|
||||
from dock.oa import oa_api_request
|
||||
from paste.core.logging import echo_log
|
||||
from paste.util import udict
|
||||
from paste.web import requests
|
||||
|
||||
|
||||
async def after_result_notify_request(response: HTTPResponse, retry_queue: asyncio.Queue[HTTPRequest]):
|
||||
"""
|
||||
上报工单操作结果响应后的处理程序。
|
||||
|
||||
:param response: 响应对象
|
||||
:param retry_queue: 重试队列
|
||||
"""
|
||||
body = response.body.decode()
|
||||
echo_log(body)
|
||||
body_data = json.loads(body)
|
||||
code = udict.get_by_path(body_data, 'code')
|
||||
message = udict.get_by_path(body_data, 'msg')
|
||||
if code == 200:
|
||||
echo_log(f"上报工单操作结果成功.")
|
||||
else:
|
||||
echo_log(f"上报工单操作结果失败:{message}")
|
||||
|
||||
if retry_queue:
|
||||
echo_log(f"上报工单操作结果重试队列中有:{retry_queue.qsize()} 个请求在等待.")
|
||||
|
||||
|
||||
async def push_result_notify(flow_token: str, message: str, return_code: int):
|
||||
"""
|
||||
操作工单完成后,推送处理结果
|
||||
|
||||
:param flow_token:OA调用本项目接口时提供
|
||||
:param message:接口调用返回说明
|
||||
:param return_code:操作类型
|
||||
"""
|
||||
echo_log(f"正在准备推送工单操作结果...")
|
||||
request = await oa_api_request.get_result_notify_request(flow_token, message, return_code)
|
||||
push_queue = asyncio.Queue()
|
||||
await push_queue.put(request)
|
||||
await requests.async_concurrency(
|
||||
push_queue, con_count=dock.CONCURRENCY_COUNT, retry=dock.MAX_RETRY_COUNT,
|
||||
after_request=after_result_notify_request
|
||||
)
|
||||
echo_log(f"推送工单操作结果完成...")
|
||||
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
安全模块。
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
from tornado.httpclient import HTTPResponse, HTTPRequest
|
||||
|
||||
import apps
|
||||
import dock
|
||||
from dock.oa import oa_api
|
||||
from models.token import TokenModel
|
||||
from paste.core import config
|
||||
from paste.core.logging import echo_log
|
||||
from paste.util import udict
|
||||
from paste.web import requests
|
||||
|
||||
|
||||
async def login():
|
||||
"""
|
||||
登录 DCM 系统并获取认证 Token 和响应数据。
|
||||
|
||||
Args:
|
||||
无参数。
|
||||
|
||||
Returns:
|
||||
tuple: 包含两个元素的元组:
|
||||
- str: 请求头中的 Token 字符串(如 "JSESSIONID=abc; ...")
|
||||
- dict: DCM 接口返回的完整 JSON 响应数据
|
||||
|
||||
Raises:
|
||||
AssertionError: 登录失败(`token` 为 '-1')
|
||||
ValueError: 响应体非合法 JSON
|
||||
HTTPError: 网络请求失败(由 `async_request` 抛出)
|
||||
"""
|
||||
_username = config.get_config(f"dock.oa.env.{apps.get_active_env()}.username")
|
||||
_password = config.get_config(f"dock.oa.env.{apps.get_active_env()}.password")
|
||||
_login_name = config.get_config(f"dock.oa.env.{apps.get_active_env()}.login_name")
|
||||
login_url = f"{oa_api.ApiUrl}/token/{_username}/{_password}?loginName={_login_name}"
|
||||
|
||||
# 构建扩展头
|
||||
user_agent, browser_ver, os_name = dock.get_random_user_agent()
|
||||
extra_headers = {
|
||||
'User-Agent': user_agent,
|
||||
}
|
||||
|
||||
# 构造请求
|
||||
request_body = {}
|
||||
|
||||
# 构造请求对象
|
||||
request = dock.new_http_request(
|
||||
url=login_url,
|
||||
body=request_body,
|
||||
method='GET',
|
||||
timeout=dock.DEFAULT_TIMEOUT,
|
||||
use_form=True,
|
||||
extra_headers=extra_headers,
|
||||
)
|
||||
|
||||
queue = asyncio.Queue()
|
||||
await queue.put(request)
|
||||
await requests.async_concurrency(
|
||||
queue, con_count=1, retry=dock.MAX_RETRY_COUNT,
|
||||
after_request=after_login
|
||||
)
|
||||
|
||||
|
||||
async def after_login(response: HTTPResponse, retry_queue: asyncio.Queue[HTTPRequest]):
|
||||
response_body = response.body.decode()
|
||||
response_data = json.loads(response_body)
|
||||
token = udict.get_by_path(response_data, 'id', '')
|
||||
if token and token != '-1':
|
||||
await TokenModel.refresh(platform=f'{oa_api.TokenPlatform}', token=token)
|
||||
echo_log(f"成功刷新 OA 登录令牌.")
|
||||
else:
|
||||
echo_log(f"OA 登录失败,无法刷新令牌,响应:{response_body}")
|
||||
if retry_queue:
|
||||
echo_log(f"登录重试队列中有:{retry_queue.qsize()} 个请求在等待.")
|
||||
return response_data, token
|
||||
|
||||
|
||||
async def get_token(platform: str = 'OA'):
|
||||
"""
|
||||
取得可用 Cookies。
|
||||
|
||||
:param platform: 要查询的平台,默认是:OA
|
||||
:return: Cookies 字符串
|
||||
"""
|
||||
_token = await TokenModel.find_by_platform(platform)
|
||||
return _token.token
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from paste.core import aio_pool
|
||||
_runner = aio_pool.get_aio_runner()
|
||||
_runner(login())
|
||||
Reference in New Issue
Block a user