Commit 11b2601e authored by qunfeng qiu's avatar qunfeng qiu
Browse files

Initial commit

parents
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import traceback
from ..app_response import AppResponse
from ..base_const import ConstBaseApp
from ..common_app_config import CommonAppConfig
from ...ludq_utils.utils_webservice.common_base_webservice_v2 import CommonBaseWebServiceV2
class ChangeLoggingLevel(CommonBaseWebServiceV2):
r"""
变更服务的日志级别
"""
need_login: bool = False
new_log_level: str = None
def my_do_service(self):
self.init_and_check_param()
if self.is_debug:
self.new_log_level = "debug"
conf_filepath = ConstBaseApp.COMMON_APP_CONFIG_DEFAULT_CONF_FILE_PATH
try:
app_config_dict = CommonAppConfig().load_file_as_dict(conf_filepath)
except BaseException as e:
CommonAppConfig().common_logger.warning(f"读取配置文件{conf_filepath}时发生异常:{e}")
CommonAppConfig().common_logger.warning(traceback.format_exc())
app_config_dict = dict()
app_config_dict["log_level"] = self.new_log_level
r"""
配置文件示例:
log_level: debug
"""
self.app_utils.yaml_dump_list_to_file(
source_list=[app_config_dict],
file_path=conf_filepath,
)
# 返回结果
response: AppResponse = self.gen_ok_resp()
return response
def init_and_check_param(self):
r"""
初始化&检查传入的参数
:return:
"""
key_str = "level"
key_value = self.get_attr(f"parsed_input_params_{key_str}")
if not key_value:
self.raise_missing_param_err(
message=f"未提供{key_str}参数"
)
new_log_level_int = CommonAppConfig.log_level_dict.get(key_value.lower())
if new_log_level_int is None:
self.raise_missing_param_err(f"不合法的level:{key_value}")
self.new_log_level = key_value
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import datetime
import flask
from flask import make_response, Response
from .common_base_webservice_v2 import CommonBaseWebServiceV2
from ..app_exception import AppRuntimeException, AppException
from ..app_response import AppResponse
from ..utils_k8s.k8s_api_object_utils import OperationResultException
from ..utils_v2 import UtilityV2
from ...globalconst import DateTimeConst
class CommonBaseWebServiceForStreamV2(CommonBaseWebServiceV2):
"""
基础微服务-基类(文件流类型的微服务基础),依赖一些其他应用基础类
"""
def action_when_not_login(self):
return make_response("未登录", 400)
def handle_ret_obj(self, ret_obj):
r"""
处理 ret_obj
"""
# 如果返回 AppResponse,必然是CODE不为OK的Response
if isinstance(ret_obj, AppResponse):
resp = make_response(ret_obj.message, 400)
elif isinstance(ret_obj, Response):
resp = ret_obj
else:
raise AppRuntimeException(
message="服务内部错误:处理结果类型错误",
detail=f"ret_obj类型={type(ret_obj)}, ret_obj={ret_obj}"
)
return resp
def resp_for_safe_exeception(self, safe_exception: AppException):
r"""
处理 AppException 时, 根据 AppException 返回的response
"""
return make_response(safe_exception.message, 400)
def resp_for_common_exeception(self, ee):
r"""
处理 e 时, 根据 AppException 返回的response
"""
if isinstance(ee, AppRuntimeException):
err_msg = ee.message
elif isinstance(ee, OperationResultException):
err_msg = ee.failed_reason
else:
err_msg = f"{ee.__class__.__name__}:{ee}"
return make_response(f"服务发生异常:{err_msg}", 500)
def do_audit_end(
self,
logined_user,
start_time: datetime.datetime, end_time: datetime.datetime,
request_id: str,
ret_response: flask.Response,
e=None, exception_tracback=None,
):
r"""
请求结束的时候的审计, Business业务自行实现
"""
if hasattr(logined_user, "user_name"):
user_name = logined_user.user_name
else:
user_name = str(logined_user)
if hasattr(logined_user, "user_id"):
user_id = logined_user.user_id
else:
user_id = str(logined_user)
consume_ms = (end_time - start_time).total_seconds() * 1000
headers = list()
for tmp_header_key in ret_response.headers.keys():
headers.append({
"header_key": tmp_header_key,
"header_value": ret_response.headers.getlist(tmp_header_key)
})
self.logger.info(UtilityV2.join_str_with_none(
f"{user_name}({user_id}) 服务调用:", self.__class__.__name__,
f" ,request_id={request_id}",
f" ,结束处理时间:", UtilityV2.format_date(start_time, DateTimeConst.COMMON_DATETIME_FORMAT),
f" ,耗时 {consume_ms} 豪秒",
f" ,返回内容长度:{ret_response.content_length}",
f" ,status_code:{ret_response.status_code}, Header:{headers}",
))
if e is not None:
self.logger.error(UtilityV2.join_str_with_none(
f"{user_name}({user_id}) 服务调用:", self.__class__.__name__,
f" ,request_id={request_id}",
f" ,异常结束,异常信息:{e.__class__.__name__}:{e}",
))
self.logger.error(exception_tracback)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import datetime
import logging
import threading
import traceback
from typing import Union, Optional, Any, Tuple
import flask
from flask import Request, make_response, Response, request, g
from ..utils_http.http_utils import HttpUtils
from ...globalconst import DateTimeConst
from ...ludq_utils.app_exception import AppException, AppRuntimeException
from ...ludq_utils.app_response import AppResponse
from ...ludq_utils.base_const import ConstResponseCode, ConstBaseApp
from ...ludq_utils.common_app_config import CommonAppConfig
from ...ludq_utils.logined_user import LoginedUserInfo
from ...ludq_utils.utils_http.http_cookie import HttpCookie
from ...ludq_utils.utils_k8s.k8s_api_object_utils import OperationResultException
from ...ludq_utils.utils_tg_sso.sso_utils import SsoUtils
from ...ludq_utils.utils_v2 import UtilityV2
class CommonBaseWebServiceV2:
"""
基础微服务-基类,
继承类需要按照实际需要覆盖基类的 app_config() , app_utils() 方法,
还有 refresh_app_config() 等方法,
尤其是 refresh_app_config() 方法,如果需要动态更新应用logLevel和自己的应用配置,需要覆盖该方法
"""
need_login: bool = True
r"""
应用是否需要登录,默认为True,继承类自行覆盖
"""
# 应用使用的logger
logger: logging.Logger = None
is_debug: bool = False
# 应用使用的utils实例,减少utils创建次数
_app_utils = None
r"""
应用使用的utils实例,减少utils创建次数,默认为 UtilityV2 类型,
如果应用有自己的继承自 UtilityV2 的util类,需要自行覆盖该属性,示例:
@property
def app_utils(self)->AppUtils:
if self._app_utils is None:
self._app_utils=AppUtils(logger=self.logger)
return self._app_utils
"""
_http_utils: HttpUtils = None
r"""
应用使用的http_utils实例,减少utils创建次数,默认为 HttpUtils 类型,
如果应用有自己的继承自 HttpUtils 的http_util类,需要自行覆盖该属性,示例:
@property
def http_utils(self)->HttpUtils:
if self._http_utils is None:
self._http_utils=HttpUtils(incoming_request=self.request, logger=self.logger)
return self._http_utils
"""
request: Request = None
r"""
应用处理的request
"""
request_id: str = None
r"""
request_id 通过 x-request-id header获取或者用自动生成的uuid表示
"""
request_headers: dict = None
r"""
request_headers 从request中解析出来的字典
"""
transparent_headers: dict = None
transparent_headers_without_cookie: dict = None
request_json: dict = None
r"""
API接收的json数据
"""
request_values: dict = None
r"""
query参数和form-data或x-www-form-urlencoded传入的参数综合,即request.args和request.form的综合,key相同时query参数优先,先出现的key优先
"""
params_json: dict = None
r"""
API接收的json数据,根据request的内容决定,其获取规则为:
(1) post body 如果有值,且为json,则使用
(2) 如果 args 中提供了 params 且有值.且为json,则使用
(3) 使用 request_values
"""
login_user: Optional[Any] = None
r"""
访问服务的用户身份,任意类型,应用自行获取
"""
is_login: bool = False
r"""
根据login_user是否存在,分析出的是否登录属性,应用自行获取
"""
auth_failure_reason: str = None
r"""
如果判定为未登录,表示未登录原因
"""
resp_cookie: HttpCookie = None
r"""
用于设置 response Cookie , API 需要设置 Cookie时,设置该属性
"""
def __init__(self, a_request=None):
r"""
初始化,并获取登陆用户信息以及是否登陆状态
:param a_request:
"""
# 刷新应用config
self.refresh_app_config()
# 初始化应用处理的request
a_request = a_request or request
self.request = a_request
# 解析 request_headers
request_headers: dict = dict()
for tmp_header_key, tmp_header_value in self.request.headers.items(lower=True):
request_headers[tmp_header_key] = tmp_header_value
self.request_headers = request_headers
# 设置透传的header
transparent_headers: dict = dict()
transparent_headers_without_cookie: dict = dict()
exclude_headers = {
"host", "connection", "cache-control",
"accept", "accept-encoding", "accept-language",
"content-type", "referer", "content-length", "x-real-ip", "x-forwarded-for",
"origin", "user-agent", "expect",
}
for tmp_header_key, tmp_header_value in self.request_headers.items():
tmp_header_key: str = tmp_header_key
if tmp_header_key in exclude_headers:
continue
if tmp_header_key.startswith("x-forwarded-"):
continue
if tmp_header_key.startswith("x-envoy-"):
continue
transparent_headers[tmp_header_key] = tmp_header_value
if tmp_header_key not in {"cookie", }:
transparent_headers_without_cookie[tmp_header_key] = tmp_header_value
self.transparent_headers = transparent_headers
self.transparent_headers_without_cookie = transparent_headers_without_cookie
# 生成 request_id
self.request_id = a_request.headers.get("x-request-id", None)
if not self.request_id:
self.request_id = UtilityV2.create_uuid()
# 初始化应用使用的logger
self.logger = UtilityV2.gen_logger(
logger_name=threading.currentThread().getName() + "-" + self.request_id,
log_level=CommonAppConfig().log_level,
)
# 根据应用的config判断当前是否是debug模式
self.is_debug = CommonAppConfig().is_debug
# 初始化 self.request_values
self.request_values = self.request.values.to_dict()
# 初始化 self.request_json
# 因为 self.request.json 在请求中的 Content - Type为application / json时才不为空,
# 而且此时body必须有内容,如果没有内容,调用 self.request.json 会报异常, 所以封装一个不会抛出异常的方法
self.request_json = self.gen_request_json_content()
# 初始化 params_json
# 优先使用 request_json
params_json = self.request_json
# 其次使用 params 参数
if not params_json:
params_in_query = self.request.args.get('params')
if params_in_query:
params_json = UtilityV2.jsonstr2dict(params_in_query)
# 最后使用 request_values
if not params_json:
params_json = self.request_values
self.params_json = params_json or dict()
# 自动初始化参数
for k, v in self.params_json.items():
setattr(self, f"parsed_input_params_{k}", v)
# 初始化应用的访问者身份
self.login_user, self.is_login, self.auth_failure_reason = self.get_login_user_info()
def get_attr(self, attr_name):
return getattr(self, attr_name, None)
def do_service(self):
r"""
通用的服务执行模版,记录了服务访问时长以及捕获 SafeException 时返回 code, message json
请覆盖 my_do_service() 实现你自己的逻辑
:return:
"""
start_time = datetime.datetime.now()
try:
# 开始审计
self.do_audit_start(
logined_user=self.login_user,
start_time=start_time,
request_id=self.request_id, input_request=self.request,
)
# 获取服务返回结果
ret_obj = self.gen_ret_obj()
# 返回响应信息
resp = self.handle_ret_obj(ret_obj)
if self.resp_cookie and isinstance(resp, Response):
# 设置cookie
self.resp_cookie.do_set_cookie(resp)
if isinstance(resp, Response):
resp.headers["Connection"] = "close"
e = None
exception_tracback = None
except AppException as safe_exception:
resp = self.resp_for_safe_exeception(safe_exception)
e = None
exception_tracback = None
except BaseException as ee:
resp = self.resp_for_common_exeception(ee)
e = ee
exception_tracback = traceback.format_exc()
# 计算服务时长,
end_time = datetime.datetime.now()
# 记录访问结束日志
self.do_audit_end(
logined_user=self.login_user,
start_time=start_time, end_time=end_time,
request_id=self.request_id,
ret_response=resp,
e=e, exception_tracback=exception_tracback,
)
return resp
def resp_for_safe_exeception(self, safe_exception: AppException):
r"""
处理 AppException 时, 根据 AppException 返回的response
"""
ret_obj = UtilityV2.dict2jsonstr(safe_exception.gen_err())
# 返回响应信息(错误信息)
resp = make_response(ret_obj, int(safe_exception.status_code))
resp.headers["Content-Type"] = "application/json; charset=utf-8"
resp.headers["Connection"] = "close"
return resp
def resp_for_common_exeception(self, ee):
r"""
处理 e 时, 根据 AppException 返回的response
"""
if isinstance(ee, AppRuntimeException):
err_msg = ee.message
code = ee.code or ConstResponseCode.CODE_SYS_ERROR
status_code = 200
elif isinstance(ee, OperationResultException):
err_msg = ee.failed_reason
code = ee.code or ConstResponseCode.CODE_SYS_ERROR
status_code = 200
else:
err_msg = f"{ee.__class__.__name__}:{ee}"
code = ConstResponseCode.CODE_SYS_ERROR
status_code = 200
app_response = AppResponse(
code=code,
message=err_msg,
status_code=status_code
)
resp = make_response(str(app_response), int(app_response.status_code))
resp.headers["Connection"] = "close"
return resp
def gen_ret_obj(self):
r"""
获取服务返回对象,
统一业务逻辑,减少业务代码
"""
# 统一业务逻辑,减少业务代码
if self.need_login and not self.is_login:
ret_obj = self.action_when_not_login()
else:
# 执行业务逻辑
ret_obj = self.my_do_service()
return ret_obj
def handle_ret_obj(self, ret_obj):
r"""
处理 ret_obj
"""
if isinstance(ret_obj, AppResponse):
resp = make_response(str(ret_obj), int(ret_obj.status_code))
resp.headers["Content-Type"] = "application/json; charset=utf-8"
elif isinstance(ret_obj, str):
resp = make_response(ret_obj)
elif isinstance(ret_obj, dict):
resp = make_response(UtilityV2.dict2jsonstr(ret_obj))
resp.headers["Content-Type"] = "application/json; charset=utf-8"
elif isinstance(ret_obj, list):
resp = make_response(UtilityV2.list2jsonstr(ret_obj))
resp.headers["Content-Type"] = "application/json; charset=utf-8"
else:
resp = ret_obj
return resp
def gen_ok_resp(self, data=None):
r"""
快速生成OK的返回信息
:param data:
:return:
"""
return AppResponse(code=ConstResponseCode.CODE_OK, data=data)
def gen_sys_error_resp(self, data=None):
r"""
快速生成系统错误的返回信息
:param data:
:return:
"""
return AppResponse(code=ConstResponseCode.CODE_SYS_ERROR, data=data)
def gen_not_login_err(self, message=None) -> AppResponse:
r"""
快速生成未登录错误
:return:
"""
return AppResponse(code=ConstResponseCode.CODE_AUTH_FAILURE, message=message)
def raise_not_login_err(self):
r"""
快速抛出未登录异常
"""
raise AppException(code=ConstResponseCode.CODE_AUTH_FAILURE)
def gen_no_auth_err(self, message: str = None) -> AppResponse:
r"""
快速生成缺少权限错误
:param message:
:return:
"""
return AppResponse(code=ConstResponseCode.CODE_UNAUTHORIZE_OPERATION, message=message)
def raise_no_auth_err(self, message: str = None):
r"""
快速生成缺少权限错误
:param message:
:return:
"""
raise AppException(code=ConstResponseCode.CODE_UNAUTHORIZE_OPERATION, message=message)
def gen_missing_param_err(self, message: str = None):
r"""
:param message:
:return:
"""
return AppResponse(code=ConstResponseCode.CODE_MISSING_PARAMETER, message=message)
def gen_status_err(self, message: str = None):
r"""
:param message:
:return:
"""
return AppResponse(code=ConstResponseCode.CODE_STATUS_ERROR, message=message)
def raise_missing_param_err(self, message: str = None):
r"""
:param message:
:return:
"""
raise AppException(code=ConstResponseCode.CODE_MISSING_PARAMETER, message=message)
def raise_status_err(self, message: str = None):
r"""
:param message:
:return:
"""
raise AppException(code=ConstResponseCode.CODE_STATUS_ERROR, message=message)
def raise_uims_when_not_login(self):
r"""
生成带有uims返回地址的异常
:return:
"""
uims_api_for_frontpage = ""
response: AppResponse = AppResponse()
response.code = ConstResponseCode.CODE_AUTH_FAILURE
response.message = "请先登录uims并通过uims跳转到天宫后重试"
response.data = uims_api_for_frontpage
app_exception = AppException()
app_exception.my_response = response
raise app_exception
def gen_request_json_content(self):
r"""
生成 request_json的内容
封装一个不会抛出异常的方法
:return:
"""
try:
return UtilityV2.jsonstr2dict(str(self.request.data, encoding="utf-8")) or dict()
except BaseException as e:
# 打印一下错误信息
self.logger.error("获取self.request.json时发生异常,使用空的dict,异常信息如下:")
self.logger.exception(e, exc_info=True)
# 异常时返回空的dict
return dict()
def gen_user_info(self, login_user: LoginedUserInfo = None, output_type: str = "str") -> Union[str, dict]:
r"""
生成 {"user_id":"","user_name":""}格式的字典或字符串
:param login_user: 用户, 默认为 self.login_user
:param output_type: str or dict, 输出格式,默认为 str
"""
login_user = login_user or self.login_user
return self.app_utils.gen_user_info(login_user=login_user, output_type=output_type)
@property
def app_utils(self) -> UtilityV2:
r"""
app_utils 属性
:return:
"""
if self._app_utils is None:
self._app_utils = UtilityV2(logger=self.logger)
return self._app_utils
@app_utils.setter
def app_utils(self, app_utils):
r"""
app_utils 属性
:param app_utils:
:return:
"""
self._app_utils = app_utils
@property
def http_utils(self) -> HttpUtils:
r"""
http_utils 属性
:return:
"""
if self._http_utils is None:
self._http_utils = HttpUtils(incoming_request=self.request, logger=self.logger)
return self._http_utils
@http_utils.setter
def http_utils(self, http_utils):
r"""
http_utils 属性
:param http_utils:
:return:
"""
self._http_utils = http_utils
# ================ 应用自行继承方法 ============================
def refresh_app_config(self):
r"""
刷新 CommonAppConfig() 配置 和 应用配置,
默认为每次都读取 ConstBaseApp.COMMON_APP_CONFIG_DEFAULT_CONF_FILE_PATH 文件刷新 CommonAppConfig() ,
如果继承类需要每次都重新抓取应用配置,则需要覆盖该方法 示例:
def refresh_app_config(self):
hot_reload_from_env = os.environ.get('HOT_RELOAD_CONFIG')
hot_reload = hot_reload_from_env == "true"
if hot_reload:
# 使用应用提供的配置文件刷新 common_app_config
CommonAppConfig().refresh_app_config(conf_filepath=MyAppConfig.get_app_conf_filepath())
# 使用应用提供的配置文件刷新 app_config
self.app_config.refresh_app_config(conf_filepath=MyAppConfig.get_app_conf_filepath())
或者
def refresh_app_config(self):
# 仍然使用 ConstBaseApp.COMMON_APP_CONFIG_DEFAULT_CONF_FILE_PATH 刷新 CommonAppConfig()
super().refresh_app_config()
hot_reload_from_env = os.environ.get('HOT_RELOAD_CONFIG')
hot_reload = hot_reload_from_env == "true"
if hot_reload:
# 使用应用提供的配置文件刷新 app_config
self.app_config.refresh_app_config(conf_filepath=MyAppConfig.get_app_conf_filepath())
"""
# 使用 ConstBaseApp.COMMON_APP_CONFIG_DEFAULT_CONF_FILE_PATH 刷新 CommonAppConfig()
CommonAppConfig().refresh_app_config(ConstBaseApp.COMMON_APP_CONFIG_DEFAULT_CONF_FILE_PATH)
def get_login_user_info(self) -> Tuple[Any, bool, str]:
r"""
获取用户信息, Business业务自行实现
"""
if hasattr(g, "user_info") and isinstance(g.user_info, dict):
login_user: LoginedUserInfo = SsoUtils(
sso_api="dontcare", logger=self.logger
).gen_user_info_from_sso_api_response(
action="从用户信息字典中获取用户信息",
user_info_dict=g.user_info,
access_token=request.cookies.get("accessToken"),
)
auth_failure_reason = ""
else:
login_user: Optional[LoginedUserInfo] = None
auth_failure_reason = getattr(g, "auth_failure_reason", "")
is_login = login_user is not None and login_user.is_login()
return login_user, is_login, auth_failure_reason
def action_when_not_login(self):
r"""
do_service时如果未登录的行为,默认为需要登录则返回未登录异常
:return:
"""
return self.gen_not_login_err(message=self.auth_failure_reason)
def my_do_service(self):
r"""
通用的 do_service(),调用该函数,请覆盖该函数实现你自己的逻辑
:return:
"""
raise AppRuntimeException(
message=f"TODO:my_do_service is To Implement",
detail=f"TODO:my_do_service is To Implement"
)
def do_audit_start(
self,
logined_user,
start_time: datetime.datetime,
request_id: str, input_request: flask.Request,
):
r"""
请求开始的时候的审计, Business业务自行实现
"""
if hasattr(logined_user, "user_name"):
user_name = logined_user.user_name
else:
user_name = str(logined_user)
if hasattr(logined_user, "user_id"):
user_id = logined_user.user_id
else:
user_id = str(logined_user)
self.logger.info(UtilityV2.join_str_with_none(
f"{user_name}({user_id}) 服务调用:", self.__class__.__name__,
f" ,request_id={request_id}",
f" ,开始处理时间:", UtilityV2.format_date(start_time, DateTimeConst.COMMON_DATETIME_FORMAT),
f" ,values=", UtilityV2.dict2jsonstr(input_request.values.to_dict()),
f" ,json=", UtilityV2.dict2jsonstr(self.params_json),
f" ,访问路径:{input_request.method} {input_request.path}",
))
def do_audit_end(
self,
logined_user,
start_time: datetime.datetime, end_time: datetime.datetime,
request_id: str,
ret_response: flask.Response,
e=None, exception_tracback=None,
):
r"""
请求结束的时候的审计, Business业务自行实现
"""
if hasattr(logined_user, "user_name"):
user_name = logined_user.user_name
else:
user_name = str(logined_user)
if hasattr(logined_user, "user_id"):
user_id = logined_user.user_id
else:
user_id = str(logined_user)
consume_ms = (end_time - start_time).total_seconds() * 1000
ret_str = ret_response.data.decode(ret_response.charset)
headers = list()
for tmp_header_key in ret_response.headers.keys():
headers.append({
"header_key": tmp_header_key,
"header_value": ret_response.headers.getlist(tmp_header_key)
})
self.logger.info(UtilityV2.join_str_with_none(
f"{user_name}({user_id}) 服务调用:", self.__class__.__name__,
f" ,request_id={request_id}",
f" ,结束处理时间:", UtilityV2.format_date(start_time, DateTimeConst.COMMON_DATETIME_FORMAT),
f" ,耗时 {consume_ms} 豪秒",
f" ,返回内容:{ret_str}",
f" ,status_code:{ret_response.status_code}, Header:{headers}",
))
if e is not None:
self.logger.error(UtilityV2.join_str_with_none(
f"{user_name}({user_id}) 服务调用:", self.__class__.__name__,
f" ,request_id={request_id}",
f" ,异常结束,异常信息:{e.__class__.__name__}:{e}",
))
self.logger.error(exception_tracback)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import logging
from .common_base_webservice_v2 import CommonBaseWebServiceV2
from ...ludq_utils.app_response import AppResponse
from ...ludq_utils.common_app_config import CommonAppConfig
class ShowLoggingLevel(CommonBaseWebServiceV2):
r"""
查询服务的日志级别
"""
need_login: bool = False
def my_do_service(self):
# 返回结果
response: AppResponse = self.gen_ok_resp()
response.data = {
"log_level": logging.getLevelName(CommonAppConfig().log_level),
"common_logger_level": logging.getLevelName(CommonAppConfig().common_logger.level)
}
return response
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
from typing import List, Union
from .globalconst import EnvNameConst, GlobalRetKeyConst
from .globalutility import Utility
from .my_baseexception import create_base_exception
from .my_servicehandle import ServiceHandle
from .my_stringutils import MyStringUtils
class AnyKeyValueUtils:
r'''
任意的KV存储的便捷类 <br/>
支持数据库加锁操作
'''
DEFAULT_SERVICE_NAME = 'csm-mysqlpool'
DEFAULT_READ_API_PATH = '/mysqlpool/standardsql/serviceauth/auth/r'
DEFAULT_WRITE_API_PATH = '/mysqlpool/trans/serviceauth/auth/w'
DEFAULT_KEY_TYPE = 'auto-generated'
DEFAULT_TABLENAME = 'any_key_info'
DEFAULT_COLUMNNAME_KEYID = "key_id"
DEFAULT_COLUMNNAME_KEYTYPE = "key_type"
DEFAULT_COLUMNNAME_KEYINFO = "key_info"
DEFAULT_COLUMNNAME_REMARK = "remark"
@classmethod
def query_any_key(cls, key_id: Union[List[str], str], key_type: str = DEFAULT_KEY_TYPE,
table_name: str = DEFAULT_TABLENAME, col_keyid: str = DEFAULT_COLUMNNAME_KEYID,
col_keytype: str = DEFAULT_COLUMNNAME_KEYTYPE, col_keyinfo=DEFAULT_COLUMNNAME_KEYINFO,
service_name: str = DEFAULT_SERVICE_NAME, api_path=DEFAULT_READ_API_PATH) -> dict:
r'''
查询二维码库中的 keyId和keyType对应的info ,
Args:
key_id:
key_type:
Returns:
'''
if key_id is None or not key_id:
raise create_base_exception("参数错误:未提供 keyinfo", submitted_webarg=Utility.join_str_with_none(cls.__name__,
' queryAnyKey时 未提供 keyinfo'))
# 生成数据库查询语句
if isinstance(key_id, str):
key_id = (key_id,)
key_id_list = key_id
filtered_idlist = [Utility.do_filter_mysql_param(tmp_key) for tmp_key in key_id_list]
filtered_keytype = Utility.do_filter_mysql_param(key_type)
filtered_tablename = Utility.do_filter_mysql_field(table_name)
filtered_col_keyid = Utility.do_filter_mysql_field(col_keyid)
filtered_col_keytype = Utility.do_filter_mysql_field(col_keytype)
filtered_col_keyinfo = Utility.do_filter_mysql_field(col_keyinfo)
filtered_col_list = [filtered_col_keyid, filtered_col_keyinfo]
query_str = Utility.join_str_with_none('select ', Utility.list_join_to_str(filtered_col_list), ' from ',
filtered_tablename, ' where ', filtered_col_keyid, ' in (',
Utility.list_join_to_str(filtered_idlist), ') and ',
filtered_col_keytype, '=', filtered_keytype)
# 生成服务url
service_name = ServiceHandle.get_info_with_fromenv_first(service_name, EnvNameConst.ENV_MYSQLPOOL_SERVICE)
service_name = service_name.lstrip('/')
if not api_path.startswith('/'):
api_path = Utility.join_str('/', api_path)
service_url = Utility.join_str(service_name, api_path)
# 访问查询服务获取数据
ret_dict = ServiceHandle.querydb(service_url=service_url, query_str=query_str)
# 判断返回结果
if not ServiceHandle.check_is_success(ret_dict):
raise create_base_exception("查询key信息失败", submitted_webarg=Utility.join_str_with_none('查询key信息失败:',
ret_dict.get(
GlobalRetKeyConst.RET_VAL)))
# 如果没有查到数据,直接返回空
if int(ret_dict.get(GlobalRetKeyConst.ROWCOUNT)) == 0:
return {tmp_key: MyStringUtils.EMPTY for tmp_key in key_id_list}
# 遍历查到的信息
data_rows = ServiceHandle.safe_get_datarows(ret_dict)
result = {tmp_dict.get(col_keyid): tmp_dict.get(col_keyinfo) for tmp_dict in data_rows}
# 不存在的key也补上
map(lambda tmp_key: result.setdefault(tmp_key, MyStringUtils.EMPTY), key_id_list)
return result
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import logging
from .globalconst import GlobalConst
from .my_stringutils import MyStringUtils
from .my_utils import MyUtils
class MyBaseException(Exception):
"""
自定义的异常类, import 请使用 global
"""
ret_val: str = MyStringUtils.EMPTY
ret_code: str = '0'
submitted_webarg: str = None
def __init__(self, err_msg: str):
r"""
初始化异常
Args:
err_msg: 错误信息,不能为None或空字符串
"""
# 判断 err_msg
Exception.__init__(self, err_msg)
self.ret_val = err_msg
def __str__(self):
r"""
字符串表示,使用 ret_val , 不会返回None,至少时 EMPTY字符串
Returns:
"""
return MyStringUtils.to_str(self.ret_val)
def __repr__(self):
r"""
字符串表示,使用 __str__ , 不会返回None,至少时 EMPTY字符串
Returns:
"""
return self.__str__()
def gen_err(self) -> dict:
r"""
生成带有 RetCode,RetVal,SubmittedWebArg的标准dict
Returns:
"""
ret_dict = MyBaseException.format_to_standard_dict(self.ret_val, self.ret_code)
if self.submitted_webarg is not None:
ret_dict[GlobalConst.RETKEY_SUBMITTEDWEBARG] = MyStringUtils.to_str(
MyUtils.do_filter_xss(self.submitted_webarg))
return ret_dict
@classmethod
def format_to_standard_dict(cls, ret_val: str,
ret_code: str = GlobalConst.RETCODE_COMMON_ERROR, submitted_msg: str = None) -> dict:
r"""
将ret_val和ret_code格式化成 ryy项目组使用的标准json 字符串,
即 { "RetCode": ret_code , "RetVal": ret_val }
Args:
ret_val:string 返回结果 , 自动对其进行 filterXss操作
ret_code:string 返回码
submitted_msg:
Returns: json 返回json数据
"""
ret_dict = dict()
ret_dict[GlobalConst.RETKEY_RET_CODE] = MyStringUtils.to_str(MyUtils.do_filter_xss(ret_code))
ret_dict[GlobalConst.RETKEY_RET_VAL] = MyStringUtils.to_str(MyUtils.do_filter_xss(ret_val))
if submitted_msg is not None:
ret_dict[GlobalConst.RETKEY_SUBMITTEDWEBARG] = MyStringUtils.to_str(MyUtils.do_filter_xss(submitted_msg))
return ret_dict
@classmethod
def gen_ok_dict(cls) -> dict:
r"""
生成标准的 返回给前端的jsonObject,ok
Args:
Returns: json 返回json数据
"""
ret_dict = dict()
ret_dict[GlobalConst.RETKEY_RET_CODE] = GlobalConst.RETCODE_SUCESS_CODE
ret_dict[GlobalConst.RETKEY_RET_VAL] = 'success'
return ret_dict
@classmethod
def format_exception_to_standard_dict(cls, any_exception: Exception) -> dict:
r"""
将异常格式化为标准的带有 RetCode和RetVal的字典
Args:
any_exception:
Returns:
"""
if any_exception is None:
raise ValueError("要格式化的异常对象为None")
logging.exception(msg="exception")
if MyUtils.has_attr(any_exception, 'gen_err', must_be_method=True):
# 尝试使用 gen_err方法返回
return any_exception.gen_err()
else:
if isinstance(any_exception, (NameError, ValueError,)):
return MyBaseException.format_to_standard_dict(str(any_exception))
else:
return MyBaseException.format_to_standard_dict(
'{}:{}'.format(type(any_exception), any_exception)
)
def create_base_exception(
err_msg,
ret_code='0',
submitted_webarg=None
) -> MyBaseException:
r"""
初始化一个 MyBaseException
Args:
err_msg:
ret_code:
submitted_webarg:
Returns:
:rtype: MyBaseException
"""
ret_excep = MyBaseException(err_msg)
ret_excep.ret_code = ret_code
ret_excep.submitted_webarg = submitted_webarg
return ret_excep
def create_base_exception_with_dict(
err_dict: dict,
ret_code: str = '0',
submitted_webarg: str = None
) -> MyBaseException:
r"""
初始化一个 MyBaseException,
以 err_dict 中的RetVal为errMsg,
以 err_dict 中的 SubmittedWebArg + submitted_webarg 为新的 submitted_webarg
Args:
err_dict:
ret_code:
submitted_webarg:
Returns:
:rtype: MyBaseException
"""
# 确保 err_dict 是dict
if not isinstance(err_dict, dict):
raise ValueError("err_dict不是字典")
# 获取 err_dict 中的 retVal
err_msg = err_dict.get(GlobalConst.RETKEY_RET_VAL)
# 获取 err_dict 中的 SubmittedWebArg
ori_submitted_webarg = err_dict.get(GlobalConst.RETKEY_SUBMITTEDWEBARG)
ret_excep = MyBaseException(err_msg)
ret_excep.ret_code = ret_code
if MyStringUtils.is_empty(ori_submitted_webarg):
ret_excep.submitted_webarg = submitted_webarg
else:
if MyStringUtils.is_empty(submitted_webarg):
ret_excep.submitted_webarg = ori_submitted_webarg
else:
ret_excep.submitted_webarg = '{};;;{}'.format(ori_submitted_webarg,
submitted_webarg)
return ret_excep
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import logging
from datetime import datetime
from typing import Union, List
from flask import request, Response
from .globalconst import GlobalConst, UserInfoKeyConst
from .globalutility import Utility
from .my_anykeyvalue_utils import AnyKeyValueUtils
from .my_baseexception import MyBaseException
from .my_servicehandle import ServiceHandle
from .my_stringutils import MyStringUtils
from .my_utils import MyUtils
class BaseHttpServlet:
r'''
基础的HttpServlet类,集成常用变量和常用方法,加速开发
'''
_my_serviceurl: str = None
_app_logger: logging.Logger = Utility.get_common_logger()
def do_service(self) -> Response:
r'''
接收请求的总的处理函数
Returns:
'''
request_method: str = request.method
if request_method.upper() == 'GET':
return self.do_get()
elif request_method.upper() == 'POST':
return self.do_post()
elif request_method.upper() == 'PUT':
return self.do_put()
elif request_method.upper() == 'DELETE':
return self.do_delete()
else:
return self.common_do_service_with_unsupportmethod()
def do_get(self) -> Response:
r'''
处理 GET 请求
Returns:
'''
# 默认 GET 和 POST 使用同一个处理方式
return self.do_post()
def do_post(self) -> Response:
r'''
处理 POST 请求
Returns:
'''
return self.common_do_service()
def do_put(self) -> Response:
r'''
处理 PUT 请求
Returns:
'''
# 默认不支持 PUT 方法
return self.common_do_service_with_unsupportmethod()
def do_delete(self) -> Response:
r'''
处理 DELETE 请求
Returns:
'''
# 默认不支持 DELETE 方法
return self.common_do_service_with_unsupportmethod()
def common_do_service_with_unsupportmethod(self) -> Response:
r'''
通用的返回不支持的方法的处理方式
Returns:
'''
return self.do_make_response(self.gen_err_dict('不支持的方法:{}'.format(request.method)))
def common_do_service(self) -> Response:
r'''
通用的处理请求的方法
Returns:
'''
map_webarg: dict = self.get_mapwebarg()
# 记录开始时间
start_datetime = datetime.now()
try:
self.loginfo('{}({}) 访问调用:{}, 参数:{}'.format('TODO', 'TODO', request.path, map_webarg))
response = self.my_do_get()
# 记录访问日志
end_datetime = datetime.now()
consume_seconds = (end_datetime - start_datetime).microseconds / 1000.0
self.loginfo('{}({}) 访问调用:{} 正常结束,耗时 {} 秒'.format('TODO', 'TODO', request.path, consume_seconds))
return response
except Exception as exception:
# 生成错误信息
ret_dict = MyBaseException.format_exception_to_standard_dict(exception)
ret_str = MyUtils.dict2jsonstr(ret_dict)
response = self.do_make_response(ret_str)
end_datetime = datetime.now()
consume_seconds = (end_datetime - start_datetime).microseconds / 1000.0
self.logexception(
'{}({}) 访问调用:{} 异常结束,耗时 {} 秒,异常信息:{}'.format('TODO', 'TODO', request.path, consume_seconds, ret_str))
return response
def my_do_get(self) -> Response:
r'''
common_do_service 中调用此函数
Returns:
'''
return self.do_make_response(self.gen_err_dict('未实现'))
@property
def app_logger(self):
r'''
获取应用的logger的便捷函数
Returns:
'''
return self._app_logger
@property
def my_serviceurl(self):
r'''
获取本服务的serviceurl,用于鉴权
Returns:
'''
return self._my_serviceurl
def logdebug(self, msg, *args, **kwargs) -> None:
r'''
日志记录便捷函数
Returns:
'''
self.app_logger.debug(msg, *args, **kwargs)
def loginfo(self, msg, *args, **kwargs) -> None:
r'''
日志记录便捷函数
Returns:
'''
self.app_logger.info(msg, *args, **kwargs)
def logwarning(self, msg, *args, **kwargs) -> None:
r'''
日志记录便捷函数
Returns:
'''
self.app_logger.warning(msg, *args, **kwargs)
def logerror(self, msg, *args, **kwargs) -> None:
r'''
日志记录便捷函数
Returns:
'''
self.app_logger.error(msg, *args, **kwargs)
def logexception(self, msg, *args, exc_info=True, **kwargs) -> None:
r'''
日志记录便捷函数
Returns:
'''
self.app_logger.exception(msg, *args, exc_info=exc_info, **kwargs)
def get_mapwebarg(self) -> dict:
r'''
获取request中的mapWebArg,用于在处理request的过程中更改请求参数,
获取原始的请求参数请使用 request.values.to_dict
Returns:
'''
return ServiceHandle.get_mapwebarg()
def gen_ok_dict(self) -> dict:
r'''
生成标准的 返回给前端的jsonObject,ok
Returns:
'''
return MyBaseException.gen_ok_dict()
def gen_err_dict(self, ret_val: str,
ret_code: str = GlobalConst.RETCODE_COMMON_ERROR, submitted_msg: str = None) -> dict:
r'''
生成标准的 返回给前端的jsonObject,err
Returns:
'''
return MyBaseException.format_to_standard_dict(ret_val=ret_val, ret_code=ret_code, submitted_msg=submitted_msg)
def gen_err_dict_for_exception(self, exception: Exception) -> dict:
r'''
Args:
exception:
Returns:
'''
return MyBaseException.format_exception_to_standard_dict(exception)
def get_othermsg_from_exception(self, exception: Exception) -> str:
r'''
如果e是MyBaseException,则获取e.getSubmittedWebArg(),否则返回StringUtils.EMPTY
Args:
exception:
Returns:
'''
return ServiceHandle.get_othermsg_from_exception(exception=exception)
def do_make_response(self, retinfo: Union[str, dict]) -> Response:
r'''
将 retinfo转成字符串作为返回结果, 尤其是 retinfo为字典时会将其转换为 json字符串
Args:
retinfo:
Returns:
'''
return ServiceHandle.do_make_response(retinfo=retinfo)
def check_is_success(self, ret_dict: dict, expect_retcode: str = GlobalConst.RETCODE_SUCESS_CODE) -> bool:
r'''
快速判断标准格式的json的retcode是否为1
Args:
ret_dict:
Returns:
'''
# 当 ret_dict 不为 None 且其 RETCODE为期望值时返回True
return ServiceHandle.check_is_success(ret_dict=ret_dict, expect_retcode=expect_retcode)
def safe_get_first_dict_from_list(self, a_list: list) -> dict:
r'''
尝试从 list 中获取第一个 dict 对象,获取失败则返回 None
Args:
a_list:
Returns:
'''
return ServiceHandle.safe_get_first_dict_from_list(a_list)
def safe_get_datarows(self, a_dict: dict, key_name: str = GlobalConst.RETKEY_DATAROWS) -> list:
r'''
尝试从 dict 获取指定 DataRows 表示的 list ,出错则返回一个空的 list, 这里的safe是指 key 不存在时不出错,但是key对应的对象或字符串不是 list 时仍然会出错
Args:
a_dict:
Returns:
'''
return ServiceHandle.safe_get_list_from_dict(a_dict=a_dict, key_name=key_name)
def querycount_db(self, service_url: str, query_str: str, dict_header: dict = None) -> int:
r'''
查询数据库,执行count数据库查询,直接得到返回的整数
Args:
service_url:
query_str:
Returns:
'''
return ServiceHandle.querycount_db(service_url=service_url, query_str=query_str, dict_header=dict_header)
def querydb(self, service_url: str, query_str: str, dict_header: dict = None) -> dict:
r'''
查询数据库
Args:
service_url:
query_str:
Returns:
'''
return ServiceHandle.querydb(service_url=service_url, query_str=query_str, dict_header=dict_header)
def operate_db(self, service_url: str, operate_sql_list: list, dict_header: dict = None) -> dict:
r'''
操作数据库
Args:
service_url:
operate_sql_list: 要执行的sql语句列表
Returns:
'''
return ServiceHandle.operate_db(service_url=service_url, operate_sql_list=operate_sql_list,
dict_header=dict_header)
def call_proc(self, service_url: str, proc_name: str, arg_dict: dict, dict_header: dict = None) -> dict:
r'''
执行存储过程
Args:
service_url:
proc_name:
Returns:
'''
return ServiceHandle.call_proc(service_url=service_url, proc_name=proc_name, arg_dict=arg_dict,
dict_header=dict_header)
def get_userinfo(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO) -> dict:
r'''
从全局缓存中获取用户信息
Args:
global_obj:
Returns:
'''
return ServiceHandle.get_userinfo(global_obj=global_obj, key_userinfo=key_userinfo)
def get_key_from_userinfo(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key: str = None, default: str = MyStringUtils.EMPTY) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 key 对应的内容 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key:
default:
Returns:
'''
return ServiceHandle.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo,
key=key, default=default)
def get_usersystem(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_usersystem: str = UserInfoKeyConst.USER_SYSTEM) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 usersystem ,
不会返回None,如果key不存在或dict不存在,返回 default
Args:
global_obj:
key_usersystem:
Returns:
'''
return ServiceHandle.get_usersystem(global_obj=global_obj, key_userinfo=key_userinfo,
key_usersystem=key_usersystem)
def get_userid(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_userid: str = UserInfoKeyConst.USER_ID) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 userid ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
Returns:
'''
return ServiceHandle.get_userid(global_obj=global_obj, key_userinfo=key_userinfo, key_userid=key_userid)
def is_login(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_userid: str = UserInfoKeyConst.USER_ID, key_usersystem: str = UserInfoKeyConst.USER_SYSTEM,
default_usersystem: str = None,
expect_usersystem: str = None) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 userid 和 usersystem,
判断用户的 usersystem 是否和预期的 usersystem 相同,并且有 登陆的userid
Args:
global_obj:
key_userinfo:
key_userid:
key_usersystem:
default_usersystem: 默认为 default
expect_usersystem: 默认为 default
Returns:
'''
return ServiceHandle.is_login(global_obj=global_obj, key_userinfo=key_userinfo,
key_userid=key_userid, key_usersystem=key_usersystem,
default_usersystem=default_usersystem,
expect_usersystem=expect_usersystem)
def get_usertenantid(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_tenantid: str = UserInfoKeyConst.TENANT_ID) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 userid ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_tenantid:
Returns:
'''
return ServiceHandle.get_usertenantid(global_obj=global_obj, key_userinfo=key_userinfo,
key_tenantid=key_tenantid)
def get_userdeptid(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_deptid: str = UserInfoKeyConst.DEPT_ID) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_tenantid:
Returns:
'''
return ServiceHandle.get_userdeptid(global_obj=global_obj, key_userinfo=key_userinfo,
key_deptid=key_deptid)
def get_userdept_shortname(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_deptshortname: str = UserInfoKeyConst.DEPT_NAME_SHORT) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_deptshortname:
Returns:
'''
return ServiceHandle.get_userdept_shortname(global_obj=global_obj, key_userinfo=key_userinfo,
key_deptshortname=key_deptshortname)
def get_userdept_fullname(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_deptfullname: str = UserInfoKeyConst.DEPT_NAME_FULL) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_deptfullname:
Returns:
'''
return ServiceHandle.get_userdept_fullname(global_obj=global_obj, key_userinfo=key_userinfo,
key_deptfullname=key_deptfullname)
def get_user_ouid(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_ouid: str = UserInfoKeyConst.OU_ID) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_ouid:
Returns:
'''
return ServiceHandle.get_user_ouid(global_obj=global_obj, key_userinfo=key_userinfo,
key_ouid=key_ouid)
def get_user_ouname(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_ouname: str = UserInfoKeyConst.OU_NAME) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_ouname:
Returns:
'''
return ServiceHandle.get_user_ouname(global_obj=global_obj, key_userinfo=key_userinfo,
key_ouname=key_ouname)
def get_user_email(self, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_email: str = UserInfoKeyConst.EMAIL) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_email:
Returns:
'''
return ServiceHandle.get_user_email(global_obj=global_obj, key_userinfo=key_userinfo,
key_email=key_email)
def put_userinfo_to_dict(self, arg_dict: dict, self_serviceurl: str = None, global_obj=None,
key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_userid: str = UserInfoKeyConst.USER_ID, key_username: str = UserInfoKeyConst.USER_NAME,
key_tenantid: str = UserInfoKeyConst.TENANT_ID,
key_deptid: str = UserInfoKeyConst.DEPT_ID,
key_deptshortname: str = UserInfoKeyConst.DEPT_NAME_SHORT,
key_deptfullname: str = UserInfoKeyConst.DEPT_NAME_FULL,
key_ouid: str = UserInfoKeyConst.OU_ID, key_ouname: str = UserInfoKeyConst.OU_NAME,
key_email: str = UserInfoKeyConst.EMAIL) -> dict:
r'''
将 g.userinfo 中的用户信息加入到 map 中, 必须保证 g.userinfo 有值
map一般就是mapWebArg, 加入的信息为
arg_myuserid arg_myusername arg_mytenantid arg_mydeptid arg_mydeptname arg_myouid
arg_req_userid arg_req_tenantid
然后就是将要要鉴权的服务url添加进去, arg_req_serviceurl
是否覆盖前端传递的内容,由环境变量 DontOverrideUserInfoFromFront 决定
Args:
arg_dict:
self_serviceurl:
Returns:
'''
return ServiceHandle.put_userinfo_to_dict(arg_dict=arg_dict, self_serviceurl=self_serviceurl,
global_obj=global_obj, key_userinfo=key_userinfo,
key_userid=key_userid, key_username=key_username,
key_tenantid=key_tenantid, key_deptid=key_deptid,
key_deptshortname=key_deptshortname,
key_deptfullname=key_deptfullname, key_ouid=key_ouid,
key_ouname=key_ouname, key_email=key_email)
def get_info_with_fromenv_first(self, info_obj: Union[dict, str], info_key: str):
r'''
如果 环境变量 LOCAL_RUN = 1 , 则优先获取 info_key 的环境变量对应的 内容 ,
否则获取 info_obj 中该key对应的 内容 或 info_obj 如果是字符串,则直接获取该字符串
Args:
info_obj: 要获取的内容或字典
info_key: 要获取的内容或字典中该内容对应的key,也是环境变量名称
Returns:
'''
return ServiceHandle.get_info_with_fromenv_first(info_obj=info_obj, info_key=info_key)
def query_any_key(self, key_id: Union[List[str], str], key_type: str = AnyKeyValueUtils.DEFAULT_KEY_TYPE,
table_name: str = AnyKeyValueUtils.DEFAULT_TABLENAME,
col_keyid: str = AnyKeyValueUtils.DEFAULT_COLUMNNAME_KEYID,
col_keytype: str = AnyKeyValueUtils.DEFAULT_COLUMNNAME_KEYTYPE,
col_keyinfo=AnyKeyValueUtils.DEFAULT_COLUMNNAME_KEYINFO,
service_name: str = AnyKeyValueUtils.DEFAULT_SERVICE_NAME,
api_path=AnyKeyValueUtils.DEFAULT_READ_API_PATH) -> dict:
r'''
查询二维码库中的 keyId和keyType对应的info ,
Args:
key_id:
key_type:
Returns:
'''
return AnyKeyValueUtils.query_any_key(key_id=key_id, key_type=key_type, table_name=table_name,
col_keyid=col_keyid, col_keytype=col_keytype, col_keyinfo=col_keyinfo,
service_name=service_name, api_path=api_path)
'''
@Description: 常量类
@Author: guohb65
@Date: 2019-01-21 19:47:00
@LastEditTime: 2019-07-17 17:08:52
@LastEditors: Please set LastEditors
@Email: guohb65@chinaunicom.cn
'''
class ConstGen(object):
"""
# 普通常量类
"""
AUTHORIZATION_STR = "authorization"
BEARER_STR = "Bearer "
DATA_STR = 'data'
RETCODE_STR = "RetCode"
RETVAL_STR = "RetVal"
DATAROWS_STR = "DataRows"
ROWCOUNT_STR = "RowCount"
PAGECOUNT_STR = "PageCount"
UNIQUE_STR = 'unique'
OTHERS_STR = 'others'
CONDITION_STR = "condition"
EXACT_STR = "exact"
FUZZY_STR = "fuzzy"
SORT_STR = "sort"
SORT_DICT = {"0": "DESC", "1": "ASC"}
PAGE_STR = "page"
PAGE_NUM_STR = "page_num"
PAGE_SIZE_STR = "page_size"
TRANSSQL_STR = 'transsql'
SQL_STR = 'sql'
TRANSJSONARRAY_STR = 'transjsonarray'
TABLE_NAME_STR = 'table_name'
OPT_INSERT = "insert"
OPT_UPDATE = "update"
OPT_DELETE = "delete"
RETCODE = "RetCode"
RETVAL = "RetVal"
# 服务根路径
MYSQLPOOL_ROOT_URL = 'http://csm-mysqlpool:8080/mysqlpool/'
# MYSQLPOOL_ROOT_URL = 'http://10.245.47.30:12080/mysqlpool/'
WEBUTILITY_ROOT_URL = 'http://csm-webutility:8080/webutility/'
# WEBUTILITY_ROOT_URL = 'http://10.245.47.30:31380/microservice/servicemesh/webutility/'
SVC_JSONPATH_TO_JSON = 'getjsonpath/get'
BACKSLASH = '/'
HTTPS_STR = "https://"
# mysqlpool底层服务路径
STANDERSQL = 'standardsql'
STANDARDPROC = 'standardproc'
TRANS = 'trans'
# serviceMesh库访问路径
SERVICE_MESH_W_URL = '/w/servicemesh/service'
SERVICE_MESH_R_URL = '/r/servicemesh/service'
# sql写url
TRANSSQL_URL = MYSQLPOOL_ROOT_URL + TRANS + SERVICE_MESH_W_URL
# sql读url
STANDERSQL_URL = MYSQLPOOL_ROOT_URL + STANDERSQL + SERVICE_MESH_R_URL
# 存储过程读url
STANDARDPROC_R_URL = MYSQLPOOL_ROOT_URL + \
STANDARDPROC + SERVICE_MESH_R_URL
# 存储过程写url
STANDARDPROCL_W_URL = MYSQLPOOL_ROOT_URL + \
STANDARDPROC + SERVICE_MESH_W_URL
# jsonpath转json的服务
JSONPATH_TO_JSON_URL = WEBUTILITY_ROOT_URL + SVC_JSONPATH_TO_JSON
# json 数据KEY值
KUBE_INFO = "kube_info"
CA_INFO = "ca_info"
KUBE_CA_CRT = "kube_ca_crt"
KUBE_CLIENT_CRT = "kube_client_crt"
KUBE_CLIENT_KEY = "kube_client_key"
KUBECACRTPATH = "/root/ssl/ca.crt"
KUBECLIENTCRTPATH = "/root/ssl/istio.pem"
KUBECLIENTKEYPATH = "/root/ssl/istio-key.pem"
DEFAULTCONFIGPATH = "/root/.kube/config"
BEGIN_CERTIFICATE = "-----BEGIN CERTIFICATE-----"
END_CERTIFICATE = "-----END CERTIFICATE-----"
BEGIN_RSA_PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----"
END_RSA_PRIVATE_KEY = "-----END RSA PRIVATE KEY-----"
class ConstK8sObj(object):
"""
# K8s对象常量数据
"""
# K8S_API_BASE_URL = "http://10.245.47.31:6443"
K8S_NAMESPACES_LIST_URL = "/api/v1/namespaces"
K8S_NAMESPACES_ITEM_KEY = "items"
K8S_NAMESPACES_METADATA_KEY = "metadata"
K8S_NAMESPACES_NAME_KEY = "name"
class ConstTableName(object):
"""
# K8s对象数据表
"""
TB_INFRA_PLATFORM = "infra_platform"
TB_INFRA_PLATFORM_CLUSTER = "infra_platform_cluster"
class ConstColumn(object):
"""
# 业务数据表字段名称
"""
COL_PLATFORM_IP = "platform_ip"
COL_PLATFORM_PORT = "platform_port"
COL_PLATFORM_URI = "platform_uri"
COL_PLATFORM_UID = "platform_uid"
COL_CLUSTER_NAME = "cluster_name"
COL_KUBE_TOKEN = "kube_token"
'''
@Description: 数据处理工具类
@Author: guohb65
@Email: guohb65@chinaunicom.cn
@Date: 2019-01-21 20:12:02
@LastEditTime: 2019-07-04 09:37:54
@LastEditors: Please set LastEditors
'''
import json
import re
import uuid
from cucc_common_pkg import httptrans
from .const import ConstGen, ConstTableName, ConstColumn
class DatahandleUtility(object):
def package_sql(self, sql_key, sql_str):
"""
封装传输的sql数据格式
:param sqlKey:
:param sqlStr:
:return:
"""
trans_data = {}
transsql_data = {}
if sql_key == ConstGen.TRANSSQL_STR:
transsql_data = self.trans_sql(sql_str)
trans_data[ConstGen.TRANSJSONARRAY_STR] = json.dumps(
[transsql_data])
if sql_key == ConstGen.SQL_STR:
trans_data[ConstGen.SQL_STR] = sql_str
return trans_data
def package_trans_list(self, trans_sql_list):
"""
打包多个transsql
:param sql_list:
:return:
"""
trans_data = {}
transsql_data_list = []
for sql in trans_sql_list:
transsql_data = self.trans_sql(sql)
transsql_data_list.append(transsql_data)
trans_data[ConstGen.TRANSJSONARRAY_STR] = json.dumps(
transsql_data_list)
return trans_data
def trans_sql(self, sql_str):
"""
简单封装insert语句数据格式
:param sqlKey:
:param sqlStr:
:return:
"""
transsql_data = {}
transsql_data[ConstGen.TRANSSQL_STR] = sql_str
return transsql_data
def gen_insert_sql(self, dict_data, table_name, uid=None):
"""
参数字典转insertsql语句
:param dict_data:
:param table_name:
:return:
"""
if uid is None:
uid = uuid.uuid1().__str__().replace('-', '')
insert_sql = "insert into " + table_name + "(uid," \
+ list(dict_data.keys()).__str__()[1:-1].replace('\'', '') \
+ ")values('" + uid + "'," \
+ list(dict_data.values()).__str__()[1:-1] + ")"
return insert_sql
# return self.package_sql(ConstGen.TRANSSQL_STR, insert_sql)
def request_param2dict(self, request):
"""
把请求参数封装到一个字典中
:param request:
:return:
"""
arg_params = {}
print(request.values)
for k in request.values:
arg_params[k] = request.values.get(k)
print(arg_params)
return arg_params
def request_trans_service(self, url, params):
"""
封装请求传输
:param url:
:param params:
:return:
"""
trans = httptrans.HttpTrans(None)
try:
ret = trans.post_encodedata(url, params, None)
except BaseException as err:
print('【' + url + '】请求异常:' + err)
ret = {"RetCode": "0", "RetVal": err}
return json.dumps(ret, ensure_ascii=False)
else:
return ret
def data_is_exist(self, data, table_name, uid_data=None):
"""
判断数据表中是否存在该数据
:param data:
:param table_name:
:return:
"""
select_sql = "select count(*) as counts from " + table_name + " where "
for k in data:
select_sql += k + "='" + data[k] + "' and "
if uid_data is not None:
for key in uid_data:
select_sql += key + "!='" + uid_data[key] + "' and "
select_sql += "1=1 "
print(select_sql)
trans_data = self.package_sql(ConstGen.SQL_STR, select_sql)
print(trans_data)
ret = self.request_trans_service(ConstGen.STANDERSQL_URL, trans_data)
print(ret)
ret_dict = json.loads(ret)
print(ret_dict["DataRows"])
counts = int(ret_dict["DataRows"][0]["counts"])
print(counts)
if counts <= 0:
return False
else:
return True
def gen_update_sql(self, update_data, update_condition, table_name):
"""
数据更新的sql语句生成
:param updateData:
:param updateCondition:
:param table_name:
:return:
"""
if update_condition:
unique_data = update_data[ConstGen.UNIQUE_STR]
others_data = update_data[ConstGen.OTHERS_STR]
data_array = dict(unique_data, **others_data)
update_sql = "update " + table_name + " set "
for k in data_array:
update_sql += k + "='" + data_array[k] + "',"
update_sql = update_sql[:-1]
update_sql += " where "
for k in update_condition:
update_sql += k + "='" + update_condition[k] + "' and "
update_sql += "1=1 "
return update_sql
else:
return None
def gen_delete_sql(self, delete_condition, table_name):
"""
数据更新的sql语句生成
:param deleteCondition:
:param table_name:
:return:
"""
if delete_condition:
delete_sql = "delete from " + table_name + " where "
for k in delete_condition:
delete_sql += k + "='" + delete_condition[k] + "' and "
delete_sql += "1=1 "
return delete_sql
else:
return None
def gen_select_all_sql(self, selet_condition, table_name):
"""
数据查询的sql语句生成
:param seletCondition:
:param table_name:
:return:
"""
if ConstGen.CONDITION_STR in selet_condition.keys():
condition = json.loads(selet_condition.get(ConstGen.CONDITION_STR))
select_all_sql = "select * from " + table_name + " where "
if ConstGen.EXACT_STR in condition:
exact_condition = condition.get(ConstGen.EXACT_STR)
for k in exact_condition:
select_all_sql += k + "='" + exact_condition[k] + "' and "
if ConstGen.FUZZY_STR in condition:
fuzzy_condition = condition.get(ConstGen.FUZZY_STR)
for j in fuzzy_condition:
select_all_sql += j + " like '%" + fuzzy_condition[j] + "%' and "
select_all_sql += "1=1 "
if ConstGen.SORT_STR in condition:
sort_condition = condition.get(ConstGen.SORT_STR)
select_all_sql += " order by "
for i in sort_condition:
select_all_sql += i + " " + \
ConstGen.SORT_DICT[sort_condition[i]] + ", "
select_all_sql += " 1 "
print(select_all_sql)
return self.package_sql(ConstGen.SQL_STR, select_all_sql)
else:
return None
def exec_query_sql(self, sql_string):
"""
执行sql查询语句
:param seletCondition:
:param table_name:
:return:
"""
print(sql_string)
trans_data = self.package_sql(
ConstGen.SQL_STR, sql_string)
print(trans_data)
return self.request_trans_service(
ConstGen.STANDERSQL_URL, trans_data)
def exec_alter_sql(self, sql_string):
"""
执行sql写类型语句
:param seletCondition:
:param table_name:
:return:
"""
print(sql_string)
transsql_data = {}
trans_data = {}
transsql_data[ConstGen.TRANSSQL_STR] = sql_string
trans_data[ConstGen.TRANSJSONARRAY_STR] = json.dumps([transsql_data])
return self.request_trans_service(
ConstGen.TRANSSQL_URL, trans_data)
def query_k8s_info(self, k8s_platform_uid):
"""
查询kubernetes信息
:param seletCondition:
:param table_name:
:return:
"""
query_sql = "select platform_ip,platform_port,platform_uri from " + \
ConstTableName.TB_INFRA_PLATFORM + " where uid='" + k8s_platform_uid + "'"
return self.exec_query_sql(query_sql)
def query_cluster_info(self, cluster_uid):
"""
查询集群信息
:param seletCondition:
:param table_name:
:return:
"""
query_sql = "SELECT platform_uid,cluster_name, kube_token, kube_ca_crt, kube_client_crt, kube_client_key from " + \
ConstTableName.TB_INFRA_PLATFORM_CLUSTER + " where uid='" + cluster_uid + "'"
ret_val = json.loads(self.exec_query_sql(query_sql))
if ret_val.get(ConstGen.RETCODE) != '0' and ret_val.get(ConstGen.ROWCOUNT_STR) != '0':
cluster_info = ret_val.get(ConstGen.DATAROWS_STR)[0]
return cluster_info
else:
return None
def get_cluster_name(self, cluster_uid):
"""
获取集群的名称
:param seletCondition:
:param table_name:
:return:
"""
cluster_info = self.query_cluster_info(cluster_uid)
if cluster_info is not None:
return cluster_info.get(ConstColumn.COL_CLUSTER_NAME)
def get_cluster_token(self, cluster_uid):
"""
获取集群的token
:param seletCondition:
:param table_name:
:return:
"""
cluster_info = self.query_cluster_info(cluster_uid)
if cluster_info is not None:
return cluster_info.get(ConstColumn.COL_KUBE_TOKEN)
def get_platform_uid(self, cluster_uid):
"""
获取集群的平台UID
:param seletCondition:
:param table_name:
:return:
"""
cluster_info = self.query_cluster_info(cluster_uid)
if cluster_info is not None:
return cluster_info.get(ConstColumn.COL_PLATFORM_UID)
def get_k8s_server_ip(self, k8s_platform_uid):
"""
获取k8s的IP地址
:param seletCondition:
:param table_name:
:return:
"""
k8s_info = json.loads(self.query_k8s_info(
k8s_platform_uid)).get(ConstGen.DATAROWS_STR)[0]
return k8s_info.get(ConstColumn.COL_PLATFORM_IP)
def get_k8s_server_port(self, k8s_platform_uid):
"""
获取k8s的端口
:param seletCondition:
:param table_name:
:return:
"""
k8s_info = json.loads(self.query_k8s_info(
k8s_platform_uid)).get(ConstGen.DATAROWS_STR)[0]
return k8s_info.get(ConstColumn.COL_PLATFORM_PORT)
def get_k8s_server_uri(self, k8s_platform_uid):
"""
获取k8s的Uri
:param seletCondition:
:param table_name:
:return:
"""
k8s_info = json.loads(self.query_k8s_info(
k8s_platform_uid)).get(ConstGen.DATAROWS_STR)[0]
return k8s_info.get(ConstColumn.COL_PLATFORM_URI)
def get_k8s_api_server_addr(self, k8s_platform_uid):
"""
获取k8s的Api地址
:param seletCondition:
:param table_name:
:return:
"""
ret_val = json.loads(self.query_k8s_info(k8s_platform_uid))
if ret_val.get(ConstGen.ROWCOUNT_STR) != '0':
k8s_info = ret_val.get(ConstGen.DATAROWS_STR)[0]
k8s_ip = k8s_info.get(ConstColumn.COL_PLATFORM_IP)
if re.match(r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
k8s_ip):
if k8s_ip is not None:
api_server_addr = ConstGen.HTTPS_STR + k8s_ip
k8s_port = k8s_info.get(ConstColumn.COL_PLATFORM_PORT)
if k8s_port is not None:
k8s_port = ":" + k8s_port
api_server_addr = api_server_addr + k8s_port
k8s_uri = k8s_info.get(ConstColumn.COL_PLATFORM_URI)
if k8s_uri is not None and k8s_uri != "":
k8s_uri = ConstGen.BACKSLASH + k8s_uri
api_server_addr = api_server_addr + k8s_uri
return api_server_addr
else:
return None
else:
return None
else:
return None
def get_json_from_jsonpath(self, jsonpath_list):
"""
根据jsonpath生成json数据格式的封装服务
:param seletCondition:
:param table_name:
:return:
"""
trans_data = {}
trans_data[ConstGen.TRANSJSONARRAY_STR] = jsonpath_list
ret_data = self.request_trans_service(
ConstGen.JSONPATH_TO_JSON_URL, trans_data)
json_data = json.loads(ret_data)
if json_data[ConstGen.RETCODE] != '0':
if ConstGen.RETVAL in json_data:
data = json_data[ConstGen.RETVAL]
return data
else:
return json_data
else:
return json_data
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
from typing import Union
from flask import request, Response, make_response, g
from .globalconst import GlobalReqKeyConst, GlobalConst, UserInfoKeyConst, EnvNameConst
from .globalutility import Utility
from .httptrans import HttpTrans
from .my_baseexception import create_base_exception_with_dict, create_base_exception
from .my_stringutils import MyStringUtils
from .my_utils import MyUtils
class ServiceHandle:
r'''
服务访问便捷类
'''
_KEY_REQ_DICT_IN_REQUEST: str = 'req_dict'
@classmethod
def get_info_with_fromenv_first(cls, info_obj: Union[dict, str], info_key: str):
r'''
如果 环境变量 LOCAL_RUN = 1 , 则优先获取 info_key 的环境变量对应的 内容 ,
否则获取 info_obj 中该key对应的 内容 或 info_obj 如果是字符串,则直接获取该字符串
Args:
info_obj: 要获取的内容或字典
info_key: 要获取的内容或字典中该内容对应的key,也是环境变量名称
Returns:
'''
# 如果提供了 info_key 且 LOCAL_RUN==1,
# 则优先获取 info_key 的环境变量对应的 内容 ,
if not MyStringUtils.is_empty(info_key) \
and \
MyStringUtils.equals(Utility.get_env(EnvNameConst.ENV_LOCAL_RUN), '1'):
str_in_env = Utility.get_env(info_key)
if str_in_env:
return str_in_env
if isinstance(info_obj, str):
return info_obj
return info_obj.get(info_key)
@classmethod
def get_mapwebarg(cls) -> dict:
r'''
获取request中的mapWebArg,用于在处理request的过程中更改请求参数,
获取原始的请求参数请使用 request.values.to_dict
Returns:
'''
if not hasattr(request, cls._KEY_REQ_DICT_IN_REQUEST):
a_dict = request.values.to_dict()
setattr(request, cls._KEY_REQ_DICT_IN_REQUEST, a_dict)
return a_dict
else:
return getattr(request, cls._KEY_REQ_DICT_IN_REQUEST, None)
@classmethod
def check_is_success(cls, ret_dict: dict, expect_retcode: str = GlobalConst.RETCODE_SUCESS_CODE) -> bool:
r'''
快速判断标准格式的json的retcode是否为1
Args:
ret_dict:
Returns:
'''
# 当 ret_dict 不为 None 且其 RETCODE为期望值时返回True
return ret_dict is not None and MyStringUtils.equals(expect_retcode, MyStringUtils.to_str(
ret_dict.get(GlobalConst.RETKEY_RET_CODE)))
@classmethod
def get_error_info(cls, ret_dict: dict, expect_retval_key: str = GlobalConst.RETKEY_RET_VAL) -> bool:
r"""
获取错误信息,
Args:
ret_dict: 返回值
expect_retval_key: 期望获取的返回错误的key
Returns:
"""
# 当 ret_dict 不为 None 且其 RETCODE为期望值时返回True
return ret_dict is not None and expect_retval_key in ret_dict.keys() and ret_dict[expect_retval_key]
@classmethod
def querydb(cls, service_url: str, query_str: str, dict_header: dict = None) -> dict:
r'''
查询数据库,封装的mysqlpool sql=select xxx 接口
Args:
service_url:
query_str:select * from tbname
dict_header:
Returns:
'''
# 生成 trans-data
trans_data = {
GlobalReqKeyConst.SQL: MyStringUtils.to_str(query_str)
}
# 访问 Url
try:
headers = request.headers
except:
headers = None
http_trans = HttpTrans(headers)
return Utility.jsonstr2dict(
http_trans.post_encodedata(service_url, trans_data=trans_data, dict_header=dict_header))
@classmethod
def querycount_db(cls, service_url: str, query_str: str, dict_header: dict = None) -> int:
r'''
查询数据库,执行count数据库查询,直接得到返回的整数
Args:
service_url:
query_str:
Returns:
'''
ret_dict = cls.querydb(service_url, query_str, dict_header=dict_header)
if not cls.check_is_success(ret_dict):
raise create_base_exception_with_dict(ret_dict)
ret_dict = cls.safe_get_first_dict_from_list(cls.safe_get_datarows(ret_dict))
if ret_dict is None:
raise create_base_exception('数据库查询语句返回结果集为空')
return int(list(ret_dict.values())[0])
@classmethod
def operate_db(cls, service_url: str, operate_sql_list: list, dict_header: dict = None) -> dict:
r'''
操作数据库,mysqlpool trans,规范参考word
Args:
service_url:
operate_sql_list: 要执行的sql语句列表
Returns:
'''
# 生成 trans-data
operate_sql_list_wrapper = [{GlobalReqKeyConst.TRANSSQL: tmp_sql_str} for tmp_sql_str in operate_sql_list]
trans_data = {
GlobalReqKeyConst.TRANSJSONARRAY: operate_sql_list_wrapper
}
# 访问 Url
try:
headers = request.headers
except:
headers = None
http_trans = HttpTrans(headers)
return Utility.jsonstr2dict(
http_trans.post_encodedata(service_url, trans_data=trans_data, dict_header=dict_header))
@classmethod
def call_proc(cls, service_url: str, proc_name: str, arg_dict: dict, dict_header: dict = None) -> dict:
r'''
执行存储过程 mysqlpool proc web 接口
Args:
service_url:
proc_name: 要执行的sql语句列表
Returns:
'''
# 生成 trans-data
if not arg_dict:
arg_dict = {}
arg_dict[GlobalReqKeyConst.PROCEDURENAME] = proc_name
# 访问 Url
try:
headers = request.headers
except:
headers = None
http_trans = HttpTrans(headers)
return Utility.jsonstr2dict(
http_trans.post_encodedata(service_url, trans_data=arg_dict, dict_header=dict_header))
@classmethod
def safe_get_first_dict_from_list(cls, a_list: list) -> dict:
r'''
尝试从 list 中获取第一个 dict 对象,获取失败则返回 None
Args:
a_list:
Returns:
'''
try:
return a_list[0]
except:
return None
@classmethod
def safe_get_list_from_dict(cls, a_dict: dict, key_name: str) -> list:
r'''
尝试从 dict 获取指定key表示的 list ,出错则返回一个空的 list, 这里的safe是指 key 不存在时不出错,但是key对应的对象或字符串不是 list 时仍然会出错
Args:
a_dict:
key:
Returns:
'''
if a_dict is None or key_name is None:
return []
tmp_obj = a_dict.get(key_name, None)
if tmp_obj is None:
return []
else:
if isinstance(tmp_obj, list):
return tmp_obj
else:
try:
return list(tmp_obj)
except:
raise ValueError('获取{0}失败,{1}中的{0}不是list,且不能转成list'.format(key_name, a_dict))
@classmethod
def safe_get_datarows(cls, a_dict: dict, key_name: str = GlobalConst.RETKEY_DATAROWS) -> list:
r'''
尝试从 dict 获取指定 DataRows 表示的 list ,出错则返回一个空的 list, 这里的safe是指 key 不存在时不出错,但是key对应的对象或字符串不是 list 时仍然会出错
Args:
a_dict:
Returns:
'''
return cls.safe_get_list_from_dict(a_dict=a_dict, key_name=key_name)
@classmethod
def do_make_response(cls, retinfo: Union[str, dict]) -> Response:
r'''
将 retinfo转成字符串作为返回结果, 尤其是 retinfo为字典时会将其转换为 json字符串
Args:
retinfo:
Returns:
'''
data_str = None
if retinfo is None:
data_str = MyStringUtils.EMPTY
elif isinstance(retinfo, str):
data_str = retinfo
elif isinstance(retinfo, dict):
data_str = MyUtils.dict2jsonstr(retinfo)
else:
try:
# 尝试转成dict
data_str = MyUtils.dict2jsonstr(dict(retinfo))
except:
data_str = MyStringUtils.to_str(retinfo)
response: Response = make_response(data_str, 200)
response.content_type = GlobalConst.DEFAULTCONFIG_CONTENTTYPE
response.content_encoding = GlobalConst.DEFAULTCONFIG_CHARACTERENCODING
return response
@classmethod
def get_othermsg_from_exception(cls, exception: Exception) -> str:
r'''
如果e是MyBaseException,则获取e.getSubmittedWebArg(),否则返回StringUtils.EMPTY
Args:
exception:
Returns:
'''
if MyUtils.has_attr(exception, 'submitted_webarg'):
return MyStringUtils.to_str(getattr(exception, 'submitted_webarg', None))
else:
return MyStringUtils.EMPTY
@classmethod
def put_userinfo_to_dict(cls, arg_dict: dict, self_serviceurl: str = None, global_obj=None,
key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_userid: str = UserInfoKeyConst.USER_ID, key_username: str = UserInfoKeyConst.USER_NAME,
key_tenantid: str = UserInfoKeyConst.TENANT_ID,
key_deptid: str = UserInfoKeyConst.DEPT_ID,
key_deptshortname: str = UserInfoKeyConst.DEPT_NAME_SHORT,
key_deptfullname: str = UserInfoKeyConst.DEPT_NAME_FULL,
key_ouid: str = UserInfoKeyConst.OU_ID, key_ouname: str = UserInfoKeyConst.OU_NAME,
key_email: str = UserInfoKeyConst.EMAIL) -> dict:
r'''
将 g.userinfo 中的用户信息加入到 map 中, 必须保证 g.userinfo 有值
map一般就是mapWebArg, 加入的信息为
arg_myuserid arg_myusername arg_mytenantid arg_mydeptid arg_mydeptname arg_myouid
arg_req_userid arg_req_tenantid
然后就是将要要鉴权的服务url添加进去, arg_req_serviceurl
是否覆盖前端传递的内容,由环境变量 DontOverrideUserInfoFromFront 决定
Args:
arg_dict:
self_serviceurl:
Returns:
'''
# 增加一个环境变量用于判断是否不覆盖前端传递的用户信息
# 默认为 不 不覆盖,即 覆盖
default_for_dont_override_userinfo = '0'
dont_override_userinfo = MyUtils.get_env(
EnvNameConst.ENV_DONT_OVERRITE_USERINFO_FROM_FRANT) or default_for_dont_override_userinfo
if not MyStringUtils.equals(dont_override_userinfo, '1'):
# 如果不是 不覆盖,则覆盖
userid = cls.get_userid(global_obj=global_obj, key_userinfo=key_userinfo,
key_userid=key_userid)
tenantid = cls.get_usertenantid(global_obj=global_obj, key_userinfo=key_userinfo,
key_tenantid=key_tenantid)
arg_dict['arg_myuserid'] = userid
arg_dict['arg_myusername'] = cls.get_username(global_obj=global_obj, key_userinfo=key_userinfo,
key_username=key_username)
arg_dict['arg_mydeptid'] = cls.get_userdeptid(global_obj=global_obj, key_userinfo=key_userinfo,
key_deptid=key_deptid)
arg_dict['arg_mydeptname'] = cls.get_userdept_shortname(global_obj=global_obj, key_userinfo=key_userinfo,
key_deptshortname=key_deptshortname)
arg_dict['arg_mydeptname_full'] = cls.get_userdept_fullname(global_obj=global_obj,
key_userinfo=key_userinfo,
key_deptfullname=key_deptfullname)
arg_dict['arg_myouid'] = cls.get_user_ouid(global_obj=global_obj, key_userinfo=key_userinfo,
key_ouid=key_ouid)
arg_dict['arg_myouname'] = cls.get_user_ouname(global_obj=global_obj, key_userinfo=key_userinfo,
key_ouname=key_ouname)
arg_dict['arg_mytenantid'] = tenantid
arg_dict['arg_myemail'] = cls.get_user_email(global_obj=global_obj, key_userinfo=key_userinfo,
key_email=key_email)
arg_dict['arg_req_serviceurl'] = self_serviceurl
arg_dict['arg_req_userid'] = userid
arg_dict['arg_req_tenantid'] = tenantid
else:
# 如果 是 不覆盖,则不覆盖
userid = cls.get_userid(global_obj=global_obj, key_userinfo=key_userinfo,
key_userid=key_userid)
tenantid = cls.get_usertenantid(global_obj=global_obj, key_userinfo=key_userinfo,
key_tenantid=key_tenantid)
arg_dict.setdefault('arg_myuserid', userid)
arg_dict.setdefault('arg_myusername', cls.get_username(global_obj=global_obj, key_userinfo=key_userinfo,
key_username=key_username))
arg_dict.setdefault('arg_mydeptid', cls.get_userdeptid(global_obj=global_obj, key_userinfo=key_userinfo,
key_deptid=key_deptid))
arg_dict.setdefault('arg_mydeptname',
cls.get_userdept_shortname(global_obj=global_obj, key_userinfo=key_userinfo,
key_deptshortname=key_deptshortname))
arg_dict.setdefault('arg_mydeptname_full', cls.get_userdept_fullname(global_obj=global_obj,
key_userinfo=key_userinfo,
key_deptfullname=key_deptfullname))
arg_dict.setdefault('arg_myouid', cls.get_user_ouid(global_obj=global_obj, key_userinfo=key_userinfo,
key_ouid=key_ouid))
arg_dict.setdefault('arg_myouname', cls.get_user_ouname(global_obj=global_obj, key_userinfo=key_userinfo,
key_ouname=key_ouname))
arg_dict.setdefault('arg_mytenantid', tenantid)
arg_dict.setdefault('arg_myemail', cls.get_user_email(global_obj=global_obj, key_userinfo=key_userinfo,
key_email=key_email))
arg_dict.setdefault('arg_req_serviceurl', self_serviceurl)
arg_dict.setdefault('arg_req_userid', userid)
arg_dict.setdefault('arg_req_tenantid', tenantid)
return arg_dict
@classmethod
def get_userinfo(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO) -> dict:
r'''
从全局缓存中获取用户信息
Args:
global_obj:
key_userinfo:
Returns:
'''
gg_used = global_obj or g
g_userinfo: dict = getattr(gg_used, key_userinfo, None)
if g_userinfo is None or not g_userinfo:
return None
else:
return dict(g_userinfo)
@classmethod
def get_key_from_userinfo(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key: str = None, default: str = MyStringUtils.EMPTY) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 key 对应的内容 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key:
default:
Returns:
'''
userinfo_dict = cls.get_userinfo(global_obj=global_obj, key_userinfo=key_userinfo)
if userinfo_dict:
return userinfo_dict.get(MyStringUtils.to_str(key)) or default
else:
return default
@classmethod
def get_usersystem(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_usersystem: str = UserInfoKeyConst.USER_SYSTEM, default_usersystem: str = None) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 usersystem ,
不会返回None,如果key不存在或dict不存在,默认返回 default
Args:
global_obj:
key_userinfo:
key_usersystem:
Returns:
'''
default_usersystem = default_usersystem or 'default'
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_usersystem,
default=default_usersystem)
@classmethod
def get_userid(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_userid: str = UserInfoKeyConst.USER_ID) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 userid ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_userid:
Returns:
'''
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_userid,
default=MyStringUtils.EMPTY)
@classmethod
def is_login(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_userid: str = UserInfoKeyConst.USER_ID, key_usersystem: str = UserInfoKeyConst.USER_SYSTEM,
default_usersystem: str = None,
expect_usersystem: str = None) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 userid 和 usersystem,
判断用户的 usersystem 是否和预期的 usersystem 相同,并且有 登陆的userid
Args:
global_obj:
key_userinfo:
key_userid:
key_usersystem:
default_usersystem: 默认为 default
expect_usersystem: 默认为 default
Returns:
'''
default_usersystem = default_usersystem or 'default'
expect_usersystem = expect_usersystem or 'default'
userid = cls.get_userid(global_obj=global_obj, key_userinfo=key_userinfo, key_userid=key_userid)
usersystem = cls.get_usersystem(global_obj=global_obj, key_userinfo=key_userinfo, key_usersystem=key_usersystem,
default_usersystem=default_usersystem)
return (not MyStringUtils.is_empty(userid)) and MyStringUtils.equals(usersystem, expect_usersystem)
@classmethod
def get_username(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_username: str = UserInfoKeyConst.USER_NAME) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 userid ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_username:
Returns:
'''
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_username,
default=MyStringUtils.EMPTY)
@classmethod
def get_usertenantid(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_tenantid: str = UserInfoKeyConst.TENANT_ID) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 tenantid ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_tenantid:
Returns:
'''
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_tenantid,
default=MyStringUtils.EMPTY)
@classmethod
def get_userdeptid(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_deptid: str = UserInfoKeyConst.DEPT_ID) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_tenantid:
Returns:
'''
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_deptid,
default=MyStringUtils.EMPTY)
@classmethod
def get_userdept_shortname(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_deptshortname: str = UserInfoKeyConst.DEPT_NAME_SHORT) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_deptshortname:
Returns:
'''
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_deptshortname,
default=MyStringUtils.EMPTY)
@classmethod
def get_userdept_fullname(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_deptfullname: str = UserInfoKeyConst.DEPT_NAME_FULL) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_deptfullname:
Returns:
'''
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_deptfullname,
default=MyStringUtils.EMPTY)
@classmethod
def get_user_ouid(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_ouid: str = UserInfoKeyConst.OU_ID) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_ouid:
Returns:
'''
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_ouid,
default=MyStringUtils.EMPTY)
@classmethod
def get_user_ouname(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_ouname: str = UserInfoKeyConst.OU_NAME) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_ouname:
Returns:
'''
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_ouname,
default=MyStringUtils.EMPTY)
@classmethod
def get_user_email(cls, global_obj=None, key_userinfo: str = GlobalConst.G_KEY_USERINFO,
key_email: str = UserInfoKeyConst.EMAIL) -> str:
r'''
从全局缓存中获取用户信息 , 然后获取 ,
不会返回None,只会返回 EMPTY
Args:
global_obj:
key_userinfo:
key_email:
Returns:
'''
return cls.get_key_from_userinfo(global_obj=global_obj, key_userinfo=key_userinfo, key=key_email,
default=MyStringUtils.EMPTY)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
from .my_utils import MyUtils
class MyStringUtils:
"""
字符串处理便捷函数
"""
EMPTY = MyUtils.EMPTY
@classmethod
def to_str(cls, a_obj, trim: bool = False) -> str:
r"""
将对象转换为字符串, 当对象为None或对其调用str()函数返回为None时,返回 EMPTY
Args:
a_obj:
trim:
Returns:
"""
return MyUtils.to_str(a_obj, trim=trim)
@classmethod
def is_empty(cls, a_str: str, trim: bool = False) -> bool:
r"""
检查字符串是否是空字符串, None和''都是true,其他为false
Args:
a_str:
trim:
Returns:
"""
return MyUtils.is_empty(a_str, trim=trim)
@classmethod
def equals(cls, str1: str, str2: str) -> bool:
r"""
检查两个字符串是否相同,
None 和 None 相同
Args:
str1:
str2:
Returns:
"""
return MyUtils.equals(str1, str2)
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import json
from .my_stringutils import MyStringUtils
class StructureUtils:
r'''
结构变换便捷类
'''
DEFAULT_KEYNAME_ID = "id"
DEFAULT_KEYNAME_PARENTID = "parentid"
DEFAULT_KEYNAME_ISTOPNODE = "isTopNode"
DEFAULT_KEYNAME_CHILDRENMAP = "childrenMap"
DEFAULT_KEYNAME_CHILDREN = "children"
DEFAULT_KEYNAME_SUBLIST = "sublist"
DEFAULT_KEYNAME_NODELEVEL = "nodelevel"
DEFAULT_KEYNAME_DESCENTDANTS = "descentdants"
DEFAULT_KEYNAME_ANCESTORS = "ancestors"
DEFAULT_KEYNAME_LEFTID = "leftId"
DEFAULT_KEYNAME_RIGHTID = "rightId"
@classmethod
def transfer_dict_to_list(cls, a_dict: dict) -> list:
r'''
将 map 转换回 list<map>
例如
{
"1":{ "id":1 },
"2":{ "id":2 },
"3":{ "id":3 }
}
经过转换后为
[
{"id":1 },
{"id":2 },
{"id":3 }
]
Args:
a_dict:
Returns:
'''
if not a_dict:
return []
if not isinstance(a_dict, dict):
raise ValueError("a_dict不是字典")
return [x for x in a_dict.values()]
@classmethod
def transfer_list_to_dict(cls, a_list: list, id_key: str = DEFAULT_KEYNAME_ID,
parentid_key: str = DEFAULT_KEYNAME_PARENTID,
topnode_parent_value: str = MyStringUtils.EMPTY,
append_childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP) -> dict:
r'''
将 list<map> 转换为 以 id 为key,map为内容的map,<br/>
list中的map中的内容为 id,parentid的结构,<br/>
或只有id的结构(此时parentid_key参数传值为空字符串) <br/>
生成后在原有的map中增加 children key, 其对应一个map,包含各child id对应的map <br/>
parentid有值且不是 topNodeParentValue 时, 不会出现 topNodeParentValue 对应的map, 即这样的node也算作是顶级节点 <br/>
生成后在原有的map中增加 isTopNode key, 表示该节点是否是顶级节点
例如:
[
{"id":1,"parentid":None},
{"id":2,"parentid":222},
{"id":3,"parentid":1},
{"id":4,"parentid":2}
]
经过转换后为
{
"1":{
"id":1,
"parentid":None,
"isTopNode":1,
"childrenMap":{
"3":{"id":3,"parentid":1,"isTopNode":0}
}
},
"2":{
"id":2,
"parentid":222,
"isTopNode":1,
"childrenMap":{
"4":{"id":4,"parentid":2,"isTopNode":0}
}
},
"3":{"id":3,"parentid":1,"isTopNode":0},
"4":{"id":4,"parentid":2,"isTopNode":0}
}
Args:
a_list:
id_key:
parentid_key:
topnode_parent_value:
append_childrenmap_keyname:
Returns:
'''
if not a_list:
return {}
if not isinstance(a_list, (list, tuple)):
raise ValueError("a_list不是list或tuple")
result = {}
id_key = cls._get_id_keyname(id_key)
parentid_key = cls._get_parentid_keyname(parentid_key)
topnode_parent_value = cls._get_topnode_parent_value(topnode_parent_value)
append_childrenmap_keyname = cls._get_childrenmap_keyname(append_childrenmap_keyname)
istopnode_keyname = cls._get_istopnode_keyname(None)
all_id_list = cls._gen_all_id_values(a_list)
# 遍历 a_list
for tmp_dict in a_list:
tmp_dict: dict
# 取出 id 的值
id_value = tmp_dict.get(id_key)
if id_value is None:
raise ValueError('a_list中的字典中的{}的值不能为空'.format(id_key))
id_value = MyStringUtils.to_str(id_value)
# 将自己加入到结果map中
# 取出 id对应的map
self_map_in_result: dict = result.get(id_value)
if self_map_in_result is not None:
# 如果map中有自身, 说明之前有的map已经将其设置为自己的children了, 将children键取出, 放入tmpMap中
tmp_dict[append_childrenmap_keyname] = self_map_in_result.get(append_childrenmap_keyname)
# 将id对应的map设置为自身
result[id_value] = tmp_dict
if not MyStringUtils.is_empty(parentid_key):
# 如果parentIdKeyName为空字符串,表示不需要处理parentId
# 判断条件不为空, 表示需要处理 parentId
# 取出parentid, 默认为空字符串, null等同于空字符串
parentid_val = MyStringUtils.to_str(tmp_dict.get(parentid_key))
# 将自己加入到parentId对应的map中的childrenMap中
if not (MyStringUtils.equals(topnode_parent_value, parentid_val) or MyStringUtils.is_empty(
parentid_val) or not parentid_val in all_id_list):
# 如果 parentId是空字符串或parentId的值是预设的顶级节点的parentId的值, 或者list中不包含 其父节点parentId,
# 说明该节点是顶级节点
# 否则 说明该节点不是顶级节点, 需要设置自己是谁的子节点
# 设置自身是否是顶级节点的属性
tmp_dict[istopnode_keyname] = 0
parent_dict_in_result: dict = result.get(parentid_val)
if parent_dict_in_result is None:
parent_dict_in_result = {}
result[parentid_val] = parent_dict_in_result
# 从 parent_dict_in_result 中取出childrenMap
childrenmap_in_parent: dict = parent_dict_in_result.get(append_childrenmap_keyname)
if childrenmap_in_parent is None:
childrenmap_in_parent = {}
parent_dict_in_result[append_childrenmap_keyname] = childrenmap_in_parent
childrenmap_in_parent[id_value] = tmp_dict
else:
tmp_dict[istopnode_keyname] = 1
return result
@classmethod
def transfer_list_to_dict_simple(cls, a_list: list, id_key: str = DEFAULT_KEYNAME_ID) -> dict:
r'''
将 list<map> 转换为 以 id 为key,map为内容的map,<br/>
例如:
[
{"id":1, },
{"id":2 },
{"id":3 }
]
经过转换后为
{
"1":{ "id":1 },
"2":{ "id":2 },
"3":{ "id":3 }
}
Args:
a_list:
id_key:
Returns:
'''
return cls.transfer_list_to_dict(a_list=a_list, id_key=id_key, parentid_key=MyStringUtils.EMPTY)
@classmethod
def gen_topnode_list(cls, a_dict: dict, istopnode_keyname: str = DEFAULT_KEYNAME_ISTOPNODE) -> list:
r'''
将 以 id 为key,map为内容的map,
map中的内容中包括 isTopNode key表示该map是否是顶级节点,以此提取所有的顶级节点的 id值的列表
例如
{
"1":{
"id":1,
"parentid":None,
"isTopNode":1,
"childrenMap":{
"3":{"id":3,"parentid":1,"isTopNode":0}
}
},
"2":{
"id":2,
"parentid":222,
"isTopNode":1,
"childrenMap":{
"4":{"id":4,"parentid":2,"isTopNode":0}
}
},
"3":{"id":3,"parentid":1,"isTopNode":0},
"4":{"id":4,"parentid":2,"isTopNode":0}
}
提取值为:
["1","2"]
Args:
a_dict:
istopnode_keyname:
Returns:
'''
if not a_dict:
return []
if not isinstance(a_dict, dict):
raise ValueError('a_dict 不是 dict')
result = []
for tmp_k, tmp_v in a_dict.items():
tmp_id = MyStringUtils.to_str(tmp_k)
tmp_dict: dict = tmp_v
if cls._is_topnode(tmp_dict, istopnode_keyname):
result.append(tmp_id)
return result
@classmethod
def gen_dict_only_contains_top_node(cls, a_dict: dict, istopnode_keyname: str = DEFAULT_KEYNAME_ISTOPNODE) -> dict:
r'''
将 以 id 为key,map为内容的map,<br/>
map中的内容中包括 isTopNode key表示该map是否是顶级节点,以此构建一个新的map,<br/>
例如
{
"1":{
"id":1,
"parentid":None,
"isTopNode":1,
"childrenMap":{
"3":{"id":3,"parentid":1,"isTopNode":0}
}
},
"2":{
"id":2,
"parentid":222,
"isTopNode":1,
"childrenMap":{
"4":{"id":4,"parentid":2,"isTopNode":0}
}
},
"3":{"id":3,"parentid":1,"isTopNode":0},
"4":{"id":4,"parentid":2,"isTopNode":0}
}
经过转换后为
{
"1":{
"id":1,
"parentid":None,
"isTopNode":1,
"childrenMap":{
"3":{"id":3,"parentid":1,"isTopNode":0}
}
},
"2":{
"id":2,
"parentid":222,
"isTopNode":1,
"childrenMap":{
"4":{"id":4,"parentid":2,"isTopNode":0}
}
}
}
Args:
a_dict:
istopnode_keyname:
Returns:
'''
if not a_dict:
return {}
if not isinstance(a_dict, dict):
raise ValueError('a_dict 不是 dict')
all_topnode_list = cls.gen_topnode_list(a_dict, istopnode_keyname=istopnode_keyname)
return {tmp_node: a_dict.get(tmp_node) for tmp_node in all_topnode_list}
@classmethod
def transfer_list_to_dict_only_contains_top_node(cls, a_list: list, id_key: str = DEFAULT_KEYNAME_ID,
parentid_key: str = DEFAULT_KEYNAME_PARENTID,
topnode_parent_value: str = MyStringUtils.EMPTY,
append_childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP) -> dict:
r'''
将 list 转换为 以 id 为key,map为内容的map,
list中的map中的内容为 id,parentid的结构,
或只有id的结构(此时parentIdKeyNameParam参数传值为空字符串)
生成后在原有的map中增加 children key, 其对应一个map,包含各child id对应的map
parentid有值且不是 topNodeParentValue 时, 不会出现 topNodeParentValue 对应的map, 即这样的node也算作是顶级节点,
此函数返回的map中包含顶级节点,非顶级节点只出现在 children key中
例如
[
{"id":1,"parentid":None},
{"id":2,"parentid":222},
{"id":3,"parentid":1},
{"id":4,"parentid":2}
]
经过转换后为
{
"1":{
"id":1,
"parentid":None,
"isTopNode":1,
"childrenMap":{
"3":{"id":3,"parentid":1,"isTopNode":0}
}
},
"2":{
"id":2,
"parentid":222,
"isTopNode":1,
"childrenMap":{
"4":{"id":4,"parentid":2,"isTopNode":0}
}
}
}
Args:
a_list:
id_key:
parentid_key:
topnode_parent_value:
append_childrenmap_keyname:
Returns:
'''
return cls.gen_dict_only_contains_top_node(
cls.transfer_list_to_dict(a_list, id_key=id_key, parentid_key=parentid_key,
topnode_parent_value=topnode_parent_value,
append_childrenmap_keyname=append_childrenmap_keyname))
@classmethod
def grouplist_bykey(cls, a_list: list, groupby_keyname: str, append_sublist_keyname: str = None) -> list:
r'''
将 list 聚合
例如
[
{"userid":1,"deptid":"1"},
{"userid":2,"deptid":"1"},
{"userid":3,"deptid":"2"},
{"userid":4,"deptid":"2"},
]
经过聚合后为(可能包含冗余的key)
[
{
"userid":1,"deptid":"1",
"sublist":[
{"userid":1,"deptid":"1"},
{"userid":2,"deptid":"1"}
]
},
{
"userid":3,"deptid":"2",
"sublist":[
{"userid":3,"deptid":"2"},
{"userid":4,"deptid":"2"}
]
}
]
Args:
a_list:
groupby_keyname:
append_sublist_keyname:
Returns:
'''
if not a_list:
return []
if not isinstance(a_list, (list, tuple)):
raise ValueError("a_list不是list或tuple")
groupby_keyname = MyStringUtils.to_str(groupby_keyname)
if MyStringUtils.is_empty(groupby_keyname):
raise ValueError("groupByKeyName不能为空")
append_sublist_keyname = cls._get_sublist_keyname(append_sublist_keyname)
result_dict = {}
for tmp_dict in a_list:
tmp_dict: dict
# 取出groupByKeyName对应的值
tmp_value_for_groupby_key = tmp_dict.get(groupby_keyname)
if tmp_value_for_groupby_key is None:
raise ValueError('{}对应的值不能为空'.format(groupby_keyname))
tmp_value_for_groupby_key = MyStringUtils.to_str(tmp_value_for_groupby_key)
# 将自己加入到结果map中
# 取出 tmpValue对应的map
self_map_in_result = result_dict.get(tmp_value_for_groupby_key)
if self_map_in_result is None:
# 如果resultMap 还没有 tmpValue对应的key,说明还没有将自己加入到resultMap中
self_map_in_result = tmp_dict.copy()
result_dict[tmp_value_for_groupby_key] = self_map_in_result
# 取出 selfMapInResult中的sublist对应的list
tmp_sublist = self_map_in_result.get(append_sublist_keyname)
if tmp_sublist is None:
tmp_sublist = []
self_map_in_result[append_sublist_keyname] = tmp_sublist
# 将自身加入到 sublist中去
tmp_sublist.append(tmp_dict)
return cls.transfer_dict_to_list(result_dict)
@classmethod
def add_childrenlist_to_dict(cls, hierarchy_dict: dict, append_childrenlist_keyname: str = DEFAULT_KEYNAME_CHILDREN,
childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP) -> None:
r'''
在具有层次结构的map 中增加children
例如
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1}
}
}
经过转换后为
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1}
}.
"children":[
{"id":3,"parentid":1 }
]
}
Args:
hierarchy_dict:
append_childrenlist_keyname:
childrenmap_keyname:
Returns:
'''
if not hierarchy_dict:
return
childrenmap_keyname = cls._get_childrenmap_keyname(childrenmap_keyname)
append_childrenlist_keyname = cls._get_childrenlist_keyname(append_childrenlist_keyname)
# 从map中取出childrenMap
children_dict: dict = hierarchy_dict.get(childrenmap_keyname)
if not children_dict:
return
else:
# 否则遍历所有的children递归调用
children_list = []
for tmp_v in children_dict.values():
tmp_hierarchy_dict = tmp_v
children_list.append(tmp_hierarchy_dict)
cls.add_childrenlist_to_dict(tmp_hierarchy_dict,
append_childrenlist_keyname=append_childrenlist_keyname,
childrenmap_keyname=childrenmap_keyname)
hierarchy_dict[append_childrenlist_keyname] = children_list
return
@classmethod
def add_nodelevel_to_dict(cls, hierarchy_dict: dict, append_nodelevel_keyname: str = DEFAULT_KEYNAME_NODELEVEL,
childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP, initial_level: int = 0) -> None:
r'''
在具有层次结构的map 中增加nodelevel
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1}
}
}
经过转换后为
{
"id":1,
"parentid":None,
"nodelevel":0,
"childrenMap":{
"3":{"id":3,"parentid":1,"nodelevel":1}
}
}
Args:
hierarchy_dict:
append_nodelevel_keyname:
childrenmap_keyname:
initial_level:
Returns:
'''
if not hierarchy_dict:
return
childrenmap_keyname = cls._get_childrenmap_keyname(childrenmap_keyname)
append_nodelevel_keyname = cls._get_nodelevel_keyname(append_nodelevel_keyname)
# 在第一级map中增加初始level
hierarchy_dict[append_nodelevel_keyname] = initial_level
# 从map中取出childrenMap
children_dict: dict = hierarchy_dict.get(childrenmap_keyname)
if not children_dict:
return
else:
# 否则遍历所有的children递归调用
for tmp_v in children_dict.values():
tmp_hierarchy_dict = tmp_v
cls.add_nodelevel_to_dict(tmp_hierarchy_dict, append_nodelevel_keyname=append_nodelevel_keyname,
childrenmap_keyname=childrenmap_keyname, initial_level=initial_level + 1)
return
@classmethod
def add_descentdants_to_dict(cls, hierarchy_dict: dict,
append_descentdants_keyname: str = DEFAULT_KEYNAME_DESCENTDANTS,
childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP,
id_key: str = DEFAULT_KEYNAME_ID) -> None:
r'''
在具有层次结构的map 中增加 descentdants list
例如
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1}
}
}
经过转换后为
{
"id":1,
"parentid":None,
"descentdants":["3"],
"childrenMap":{
"3":{"id":3,"parentid":1}
}
}
Args:
hierarchy_dict:
append_descentdants_keyname:
childrenmap_keyname:
id_key:
Returns:
'''
return cls._add_descentdants_to_dict_imp(hierarchy_dict=hierarchy_dict,
append_descentdants_keyname=append_descentdants_keyname,
childrenmap_keyname=childrenmap_keyname, id_key=id_key,
to_append_descendants=None)
@classmethod
def _add_descentdants_to_dict_imp(cls, hierarchy_dict: dict,
append_descentdants_keyname: str = DEFAULT_KEYNAME_DESCENTDANTS,
childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP,
id_key: str = DEFAULT_KEYNAME_ID, to_append_descendants: list = None) -> None:
r'''
在具有层次结构的map 中增加 descentdants list
例如
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1}
}
}
经过转换后为
{
"id":1,
"parentid":None,
"descentdants":["3"],
"childrenMap":{
"3":{"id":3,"parentid":1}
}
}
Args:
hierarchy_dict:
append_descentdants_keyname:
childrenmap_keyname:
id_key:
to_append_descendants:
Returns:
'''
if not hierarchy_dict:
return
childrenmap_keyname = cls._get_childrenmap_keyname(childrenmap_keyname)
append_descentdants_keyname = cls._get_descentdants_keyname(append_descentdants_keyname)
id_key = cls._get_id_keyname(id_key)
# 将自己的id追加到 toAppendList 中
id_val = hierarchy_dict.get(id_key)
if id_val is None:
raise ValueError('{}的值不能为空'.format(id_key))
id_val = MyStringUtils.to_str(id_val)
if to_append_descendants:
for tmp_list in to_append_descendants:
tmp_list: list
tmp_list.append(id_val)
# 从map中取出childrenMap
children_dict: dict = hierarchy_dict.get(childrenmap_keyname)
if not children_dict:
return
else:
# 否则遍历所有的children递归调用
descentdant_list = []
hierarchy_dict[append_descentdants_keyname] = descentdant_list
to_append_descendants_new = None
if to_append_descendants:
to_append_descendants_new = to_append_descendants.copy()
else:
to_append_descendants_new = []
to_append_descendants_new.append(descentdant_list)
for tmp_v in children_dict.values():
tmp_hierarchy_dict = tmp_v
cls._add_descentdants_to_dict_imp(hierarchy_dict=tmp_hierarchy_dict,
append_descentdants_keyname=append_descentdants_keyname,
childrenmap_keyname=childrenmap_keyname, id_key=id_key,
to_append_descendants=to_append_descendants_new)
return
@classmethod
def add_ancestors_to_dict(cls, hierarchy_dict: dict,
append_ancestors_keyname: str = DEFAULT_KEYNAME_ANCESTORS,
childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP,
id_key: str = DEFAULT_KEYNAME_ID) -> None:
r'''
在具有层次结构的map 中增加 descentdants list
例如
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1}
}
}
经过转换后为
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1 , "ancestors":["1"]}
}
}
Args:
hierarchy_dict:
append_ancestors_keyname:
childrenmap_keyname:
id_key:
Returns:
'''
return cls._add_ancestors_to_dict_imp(hierarchy_dict=hierarchy_dict,
append_ancestors_keyname=append_ancestors_keyname,
childrenmap_keyname=childrenmap_keyname, id_key=id_key,
to_append_ancestors=None)
@classmethod
def _add_ancestors_to_dict_imp(cls, hierarchy_dict: dict,
append_ancestors_keyname: str = DEFAULT_KEYNAME_ANCESTORS,
childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP,
id_key: str = DEFAULT_KEYNAME_ID, to_append_ancestors: list = None) -> None:
r'''
在具有层次结构的map 中增加 descentdants list
例如
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1}
}
}
经过转换后为
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1 , "ancestors":["1"]}
}
}
Args:
hierarchy_dict:
append_ancestors_keyname:
childrenmap_keyname:
id_key:
to_append_ancestors:
Returns:
'''
if not hierarchy_dict:
return
childrenmap_keyname = cls._get_childrenmap_keyname(childrenmap_keyname)
append_ancestors_keyname = cls._get_ancestors_keyname(append_ancestors_keyname)
id_key = cls._get_id_keyname(id_key)
# 设置自身的先祖id
if to_append_ancestors:
hierarchy_dict[append_ancestors_keyname] = to_append_ancestors
# 从map中取出childrenMap
children_dict: dict = hierarchy_dict.get(childrenmap_keyname)
if not children_dict:
return
else:
# 否则遍历所有的children递归调用
id_val = hierarchy_dict.get(id_key)
if id_val is None:
raise ValueError('{}的值不能为空'.format(id_key))
id_val = MyStringUtils.to_str(id_val)
# 生成自己的要让后代追加的先祖id列表
to_append_ancestors_new = None
if to_append_ancestors:
to_append_ancestors_new = to_append_ancestors.copy()
else:
to_append_ancestors_new = []
to_append_ancestors_new.append(id_val)
for tmp_v in children_dict.values():
tmp_hierarchy_dict = tmp_v
cls._add_ancestors_to_dict_imp(hierarchy_dict=tmp_hierarchy_dict,
append_ancestors_keyname=append_ancestors_keyname,
childrenmap_keyname=childrenmap_keyname, id_key=id_key,
to_append_ancestors=to_append_ancestors_new)
return
@classmethod
def add_lrnode_to_dict(cls, hierarchy_dict: dict, append_leftid_keyname: str = DEFAULT_KEYNAME_LEFTID,
append_rightid_keyname: str = DEFAULT_KEYNAME_RIGHTID,
childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP,
initial_leftid_value: int = 0) -> int:
r'''
在具有层次结构的map 中增加 leftId,rightId
返回自身的右节点的值,用于递归
例如
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1}
}
}
经过转换后为
{
"id":1,
"parentid":None,
"leftId":0,
"rightId":3,
"childrenMap":{
"3":{"id":3,"parentid":1,"leftId":1,"rightId":2 }
}
}
Args:
hierarchy_dict:
append_leftid_keyname:
append_rightid_keyname:
childrenmap_keyname:
initial_leftid_value:
Returns:
'''
if initial_leftid_value is None:
initial_leftid_value = 0
if not hierarchy_dict:
return initial_leftid_value - 1
childrenmap_keyname = cls._get_childrenmap_keyname(childrenmap_keyname)
append_leftid_keyname = cls._get_leftid_keyname(append_leftid_keyname)
append_rightid_keyname = cls._get_rightid_keyname(append_rightid_keyname)
# 设置自身的leftId的值
hierarchy_dict[append_leftid_keyname] = initial_leftid_value
# 预设返回值为左节点的值 + 1
ret_val = initial_leftid_value + 1
children_dict: dict = hierarchy_dict.get(childrenmap_keyname)
if children_dict:
# 如果子节点不为空,则遍历所有的children递归调用
for tmp_v in children_dict.values():
tmp_hierarchy_dict = tmp_v
# 每个子节点的初始左节点就是预设的返回值
# 子节点返回的右节点的值+1就是新的预设的返回值
ret_val = cls.add_lrnode_to_dict(tmp_hierarchy_dict, append_leftid_keyname=append_leftid_keyname,
append_rightid_keyname=append_rightid_keyname,
childrenmap_keyname=childrenmap_keyname,
initial_leftid_value=ret_val) + 1
# 将最后的返回值设置为右节点的值
hierarchy_dict[append_rightid_keyname] = ret_val
return ret_val
@classmethod
def is_leaf_node(cls, a_dict: dict, childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP) -> bool:
r'''
判断 tmpMap是否是叶子节点, 根据 a_dict.get(childrenmap_keyname) 是否为空判断
Args:
a_dict:
childrenmap_keyname:
Returns:
'''
if not a_dict:
return True
childrenmap_keyname = cls._get_childrenmap_keyname(childrenmap_keyname)
child_dict = a_dict.get(childrenmap_keyname)
return child_dict is None or not child_dict
@classmethod
def remove_all_childrenmap(cls, a_list: list, childrenmap_keyname: str = DEFAULT_KEYNAME_CHILDRENMAP,
childrenlist_keyname: str = DEFAULT_KEYNAME_CHILDREN) -> None:
r'''
清理工作,移除所有的childrenMap
例如
[
{
"id":1,
"parentid":None,
"childrenMap":{
"3":{"id":3,"parentid":1}
},
"children":[
{"id":3,"parentid":1 }
]
}
]
经过转换后为
[ {
"id":1,
"parentid":None,
"children":[
{"id":3,"parentid":1 }
]
}
]
Args:
a_list:
childrenmap_keyname:
childrenlist_keyname:
Returns:
'''
if not a_list:
return
# 遍历list
for tmp_dict in a_list:
tmp_dict: dict
# 移除map中的 childrenMap
tmp_dict.pop(cls._get_childrenmap_keyname(childrenmap_keyname), None)
# 递归移除 list的children
recursive_list = tmp_dict.get(cls._get_childrenlist_keyname(childrenlist_keyname))
cls.remove_all_childrenmap(a_list=recursive_list, childrenmap_keyname=childrenmap_keyname,
childrenlist_keyname=childrenlist_keyname)
return
@classmethod
def _is_topnode(cls, a_dict: dict, istopnode_keyname: str = DEFAULT_KEYNAME_ISTOPNODE) -> bool:
r'''
判断 tmpMap是否是顶级节点
Args:
a_dict:
istopnode_keyname:
Returns:
'''
istopnode_keyname = cls._get_istopnode_keyname(istopnode_keyname)
is_top_val = a_dict.get(istopnode_keyname)
if is_top_val is None:
raise ValueError("{}不存在,无法判断节点是否是顶级节点".format(istopnode_keyname))
return MyStringUtils.equals(is_top_val, "1")
@classmethod
def _get_str_value_with_default(cls, a_str: str, default: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 defaultValue
Args:
a_str:
default:
Returns:
'''
if MyStringUtils.is_empty(a_str):
return default
else:
return MyStringUtils.to_str(a_str)
@classmethod
def _get_id_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 id
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_ID)
@classmethod
def _get_parentid_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 parentid
Args:
a_str:
Returns:
'''
if a_str is None:
return cls.DEFAULT_KEYNAME_PARENTID
else:
return MyStringUtils.to_str(a_str)
@classmethod
def _get_istopnode_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 isTopNode
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_ISTOPNODE)
@classmethod
def _get_topnode_parent_value(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 StringUtils.EMPTY
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, MyStringUtils.EMPTY)
@classmethod
def _get_childrenmap_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 childrenMap
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_CHILDRENMAP)
@classmethod
def _get_childrenlist_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 children
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_CHILDREN)
@classmethod
def _get_sublist_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 sublist
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_SUBLIST)
@classmethod
def _get_nodelevel_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 nodelevel
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_NODELEVEL)
@classmethod
def _get_descentdants_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 descentdants
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_DESCENTDANTS)
@classmethod
def _get_ancestors_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 ancestors
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_ANCESTORS)
@classmethod
def _get_leftid_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 leftId
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_LEFTID)
@classmethod
def _get_rightid_keyname(cls, a_str: str):
r'''
如果aString不为None或EMPTY,返回aString,否则返回默认值 rightId
Args:
a_str:
Returns:
'''
return cls._get_str_value_with_default(a_str, cls.DEFAULT_KEYNAME_RIGHTID)
@classmethod
def _gen_all_id_values(cls, a_list: list, id_key: str = None) -> list:
r'''
提取所有的id值列表<br/>
a_list 的内容应该是下面的这种结构, 每个列表项是一个dict, dict中包含id这个key,且该key对应的value必须不能为空
[
{"id":1,"parentid":None},
{"id":2,"parentid":222},
{"id":3,"parentid":1},
{"id":4,"parentid":2}
]
Args:
a_list:
id_key:
Returns:
'''
if not a_list:
return []
if not isinstance(a_list, (list, tuple)):
raise ValueError('a_list不是list或tuple')
id_key = cls._get_id_keyname(id_key)
result = []
for tmp_dict in a_list:
tmp_dict: dict
tmp_val = tmp_dict.get(id_key)
if tmp_val is None:
raise ValueError('a_list中的字典中的{}的值不能为空'.format(id_key))
result.append(MyStringUtils.to_str(tmp_val))
return result
@classmethod
def do_replace_jsonobj_str(cls, a_dict: dict, keylist: list) -> dict:
r'''
替换 a_dict 中的 keylist 对应的jsonObj字符串转换为 dict 对象<br>
返回自身
Args:
a_dict:
keylist:
Returns:
'''
if not a_dict:
return a_dict
if isinstance(keylist, str):
keylist = (keylist,)
for tmp_key in keylist:
# 尝试获取 tmp_ley 对应的值
tmp_val = a_dict.get(tmp_key)
if tmp_val is None or isinstance(tmp_val, (list, tuple, dict)):
continue
# 将其转成字符串
tmp_val = MyStringUtils.to_str(tmp_val)
# 尝试转成 dict 等对象
try:
tmp_val = json.loads(tmp_val)
a_dict[tmp_key] = tmp_val
except:
raise ValueError('{}对应的值不是json有效的json字符串'.format(tmp_key))
return a_dict
@classmethod
def do_replace_jsonobj_str_for_list(cls, a_list: list, keylist: list) -> list:
r'''
替换 a_list 中的每个列表项中的 keylist 对应的jsonObj字符串转换为 dict 对象<br>
返回自身
Args:
a_list:
keylist:
Returns:
'''
if not a_list:
return list
for tmp_dict in a_list:
cls.do_replace_jsonobj_str(tmp_dict, keylist=keylist)
return a_list
@classmethod
def do_deduplication(cls, a_list: list, key_for_check_duplicate: str) -> list:
r'''
去重复,返回一个新的jsonarray,jsonarray包含的是jsonObject
Args:
a_dict:
key_for_check_duplicate:
Returns:
'''
if not a_list:
return []
result = []
existed_ids = []
for tmp_dict in a_list:
tmp_dict: dict
id_value = MyStringUtils.to_str(tmp_dict.get(key_for_check_duplicate))
if (not MyStringUtils.is_empty(id_value)) and id_value not in existed_ids:
result.append(tmp_dict)
existed_ids.append(id_value)
return result
@classmethod
def do_preserve_key_for_dict(cls, a_dict: dict, keylist: list) -> dict:
r'''
去掉jsonobject中多余的key,只保留指定的key
返回自身
Args:
a_dict:
keylist:
Returns:
'''
if not a_dict:
return a_dict
if isinstance(keylist, str):
keylist = (keylist,)
filter_keys = list(filter(lambda x: x in keylist, a_dict.keys()))
if not filter_keys:
raise ValueError("a_dict中不存在要保留的key")
to_removed_keys = list(filter(lambda x: x not in filter_keys, a_dict.keys()))
for tmp_key in to_removed_keys:
a_dict.pop(tmp_key, None)
return a_dict
@classmethod
def do_preserve_key_for_list(cls, a_list: list, keylist: list) -> list:
r'''
替换 a_list 中的每个列表项中的 多余的key,只保留指定的key<br>
返回自身
Args:
a_list:
keylist:
Returns:
'''
if not a_list:
return list
for tmp_dict in a_list:
cls.do_preserve_key_for_dict(tmp_dict, keylist=keylist)
return a_list
@classmethod
def do_rename_key_for_dict(cls, a_dict: dict, to_rename_kv: dict) -> dict:
r'''
重命名jsonobj中的key <br>
返回自身
Args:
a_dict:
keylist:
Returns:
'''
if not a_dict:
return a_dict
if not to_rename_kv:
return a_dict
for tmp_k, tmp_v in to_rename_kv.items():
if tmp_k in a_dict.keys():
tmp_v_in_dict_for_k = a_dict.pop(tmp_k)
a_dict[tmp_v] = tmp_v_in_dict_for_k
return a_dict
@classmethod
def do_rename_key_for_list(cls, a_list: list, to_rename_kv: dict) -> list:
r'''
a_list 中的每个列表项中的 重命名jsonobj中的key<br>
返回自身
Args:
a_list:
to_rename_kv:
Returns:
'''
if not a_list:
return list
for tmp_dict in a_list:
cls.do_rename_key_for_dict(tmp_dict, to_rename_kv=to_rename_kv)
return a_list
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import json
import os
from typing import Union, Optional
class MyUtils:
"""
基础全局工具类,为了防止依赖循环,此模块不依赖于任何模块
"""
EMPTY = ''
@classmethod
def do_filter_xss(cls, to_filter_str: str) -> str:
r"""
防xss处理,将 < 替换成#《#,将 > 替换成 #》#
Args:
to_filter_str: string
Returns:
"""
if cls.is_empty(to_filter_str):
return to_filter_str
to_filter_str = cls.to_str(to_filter_str)
to_filter_str = to_filter_str.replace('<', '#《#').replace('>', '#》#')
return to_filter_str
@classmethod
def has_attr(cls, a_obj, attr_name: str, must_be_method: bool = False) -> bool:
r"""
判断对象是否包含指定的属性,以及该属性是否必须是方法
Args:
a_obj:
attr_name:
must_be_method:
Returns:
"""
if not hasattr(a_obj, attr_name):
return False
if not must_be_method:
return True
attr_obj = getattr(a_obj, attr_name, None)
return hasattr(attr_obj, '__call__')
@classmethod
def format_dict_to_str_dict(cls, to_format_dict: dict, ignore_none: bool = True,
ignore_empty: bool = False) -> dict:
r"""
将 to_format_dict 中的 key 和 value 都转换成 str ,
尤其是 value 如果是 list,tuple,dict,将被转换成 json 格式的字符串,
如果 value 为 None,则默认移除key , 不移除时将其转换为 null 字符串
如果 value 为 EMPTY , 则默认不移除
Args:
to_format_dict:
ignore_none:
ignore_empty:
Returns:
"""
if not (to_format_dict is None or isinstance(to_format_dict, dict)):
raise ValueError('to_format_dict 不是字典')
ret_dict = {}
if not to_format_dict:
return ret_dict
for tmp_k, tmp_v in to_format_dict.items():
if tmp_v is None:
if not ignore_none:
tmp_v = 'null'
ret_dict[tmp_k] = tmp_v
continue
if isinstance(tmp_v, (dict, list, tuple)):
tmp_v = json.dumps(tmp_v, ensure_ascii=False)
else:
tmp_v = str(tmp_v)
if tmp_v == '':
if not ignore_empty:
ret_dict[tmp_k] = tmp_v
continue
ret_dict[tmp_k] = tmp_v
return ret_dict
@classmethod
def dict2jsonstr(cls, a_dict: dict, value_when_obj_is_none: str = 'null') -> str:
r"""
将dict转成jsonstr
Args:
a_dict: 待转成json字符串的 dict
value_when_obj_is_none: 当 a_dict 为 None 时返回的字符串,默认为null
Returns:
"""
if a_dict is None:
return value_when_obj_is_none
elif isinstance(a_dict, dict):
return json.dumps(a_dict, ensure_ascii=False, default=lambda o: o.__dict__)
else:
raise ValueError('参数类型不是字典')
@classmethod
def list2jsonstr(cls, a_list: Union[list, tuple], value_when_obj_is_none: str = 'null') -> str:
r"""
将list or tuple 转成jsonstr
Args:
a_list: 待转成json字符串的 list or tuple
value_when_obj_is_none: 当 a_list 为 None 时返回的字符串,默认为null
Returns:
"""
if a_list is None:
return value_when_obj_is_none
elif isinstance(a_list, (list, tuple)):
return json.dumps(a_list, ensure_ascii=False, default=lambda o: o.__dict__)
else:
raise ValueError('参数类型不是list或元组')
@classmethod
def jsonstr2json(cls, a_jsonstr: str, b_raise_error_when_illegal: bool = False) -> json:
r"""
将 jsonstr 转成 dict
Args:
a_jsonstr: json格式的字符串
b_raise_error_when_illegal: 当字符串不合法时抛出异常还是返回None
Returns:
"""
try:
json_ret = json.loads(a_jsonstr)
if isinstance(json_ret, dict) or isinstance(json_ret, list):
return json_ret
else:
raise ValueError('json_str不是有效的json字符串')
except Exception as exception:
encountered_e = exception
if encountered_e:
if b_raise_error_when_illegal:
raise encountered_e
else:
return None
@classmethod
def jsonstr2dict(cls, a_jsonstr: str, b_raise_error_when_illegal: bool = False) -> Optional[dict]:
r"""
将 jsonstr 转成 dict
Args:
a_jsonstr: json格式的字符串
b_raise_error_when_illegal: 当字符串不合法时抛出异常还是返回None
Returns:
"""
try:
if isinstance(a_jsonstr, dict):
return a_jsonstr
a_dict = json.loads(a_jsonstr)
if isinstance(a_dict, dict):
return a_dict
else:
raise ValueError('json_str不是有效的json字符串')
except Exception as exception:
encountered_e = exception
if encountered_e:
if b_raise_error_when_illegal:
raise encountered_e
else:
return None
@classmethod
def jsonstr2list(cls, a_jsonarrstr: str, b_raise_error_when_illegal: bool = False) -> Optional[list]:
r"""
将 jsonstr 转成 list
Args:
a_jsonarrstr: jsonarr格式的字符串
b_raise_error_when_illegal: 当字符串不合法时抛出异常还是返回None
Returns:
"""
try:
if isinstance(a_jsonarrstr, list):
return a_jsonarrstr
if isinstance(a_jsonarrstr, tuple):
return list(a_jsonarrstr)
a_list = json.loads(a_jsonarrstr)
if isinstance(a_list, list):
return a_list
else:
raise ValueError('json_str不是有效的jsonarray字符串')
except Exception as exception:
encountered_e = exception
if encountered_e:
if b_raise_error_when_illegal:
raise encountered_e
else:
return None
@classmethod
def get_env(cls, env_name: str) -> str:
r"""
获取系统环境变量的内容
Args:
env_name:
Returns:
"""
env_dict = os.environ
if env_dict and str(env_name) in env_dict:
return env_dict[env_name]
else:
return ''
@classmethod
def to_str(cls, a_obj, trim: bool = False) -> str:
r"""
将对象转换为字符串, 当对象为None或对其调用str()函数返回为None时,返回 EMPTY
Args:
a_obj:
trim:
Returns:
"""
if a_obj is None:
return cls.EMPTY
else:
if not isinstance(a_obj, str):
a_obj = str(a_obj)
if a_obj is None:
return cls.EMPTY
if trim:
a_obj = a_obj.strip()
return a_obj
@classmethod
def is_empty(cls, a_str: str, trim: bool = False) -> bool:
r"""
检查字符串是否是空字符串, None和''都是true,其他为false
Args:
a_str:
trim:
Returns:
"""
if a_str is None:
return True
else:
if not isinstance(a_str, str):
a_str = str(a_str)
if a_str is None:
return True
if trim:
a_str = a_str.strip()
return a_str == ''
@classmethod
def equals(cls, str1: str, str2: str, none_eqaul_empty: bool = False) -> bool:
r"""
检查两个字符串是否相同,
None 和 None 相同
None 和 空字符串 不相同
Args:
str1:
str2:
none_eqaul_empty:
Returns:
"""
if str1 is None and str2 is None:
return True
if str1 is None and none_eqaul_empty:
str1 = cls.EMPTY
if str2 is None and none_eqaul_empty:
str2 = cls.EMPTY
if (str1 is None and str2 is not None) or (str2 is None and str1 is not None):
return False
str1 = cls.to_str(str1)
str2 = cls.to_str(str2)
return str1 == str2
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
from typing import Union, List, Tuple, Optional
from .globalutility import Utility
from .my_baseexception import create_base_exception
from .my_stringutils import MyStringUtils
class MysqlUtils:
"""
mysql语句生成工具类
"""
@classmethod
def gen_insertsql(cls, table_name: str, a_dict: dict) -> str:
r"""
生成 数据库插入语句,
map 中的值必须是字符串或None
当 map 中的值为 None 时,填充null值,
当 map 为 None 或empty时返回空字符串
Args:
table_name:
a_dict:
Returns:
"""
# 判断参数
if a_dict is None or not a_dict:
return MyStringUtils.EMPTY
if not isinstance(a_dict, dict):
raise ValueError("a_dict 不是字典")
table_name = MyStringUtils.to_str(table_name, trim=True)
if MyStringUtils.is_empty(table_name):
raise ValueError("table_name 不能为空")
table_name = Utility.do_filter_mysql_field(table_name, append_fix='`')
columns = []
values = []
for tmp_k, tmp_v in a_dict.items():
tmp_k = Utility.do_filter_mysql_field(tmp_k, append_fix='`')
tmp_v = Utility.do_filter_mysql_param(tmp_v, append_fix="'")
columns.append(tmp_k)
values.append(tmp_v)
return Utility.join_str_with_none("insert ", table_name, "(",
Utility.list_join_to_str(columns, separator_str=",", ignore_none=False),
") select ",
Utility.list_join_to_str(values, separator_str=",", ignore_none=False))
@classmethod
def gen_insertsql_with_on_duplcate_key_update(cls, table_name: str, a_dict: dict) -> str:
r"""
生成 数据库插入语句,当值为null时,填充null值, 使用 ON DUPLICATE KEY UPDATE
map 中的值必须是字符串或None
当 map 中的值为 None 时,填充null值,
当 map 为 None 或empty时返回空字符串
Args:
table_name:
a_dict:
Returns:
"""
# 判断参数
if a_dict is None or not a_dict:
return MyStringUtils.EMPTY
if not isinstance(a_dict, dict):
raise ValueError("a_dict 不是字典")
table_name = MyStringUtils.to_str(table_name, trim=True)
if MyStringUtils.is_empty(table_name):
raise ValueError("table_name 不能为空")
table_name = Utility.do_filter_mysql_field(table_name, append_fix='`')
columns = []
values = []
on_duplicate_key_update_list = []
for tmp_k, tmp_v in a_dict.items():
tmp_k = Utility.do_filter_mysql_field(tmp_k, append_fix='`')
tmp_v = Utility.do_filter_mysql_param(tmp_v, append_fix="'")
columns.append(tmp_k)
values.append(tmp_v)
tmp_on_duplcate_key_update_content = Utility.join_str(
tmp_k, "=values(", tmp_v, ")",
separator_str='', wrapper_str=None,
ignore_none=False
)
on_duplicate_key_update_list.append(tmp_on_duplcate_key_update_content)
return Utility.join_str_with_none(
"insert ", table_name, "(",
Utility.list_join_to_str(columns, separator_str=",", ignore_none=False),
") select ",
Utility.list_join_to_str(values, separator_str=",", ignore_none=False),
" ON DUPLICATE KEY UPDATE ",
Utility.list_join_to_str(
on_duplicate_key_update_list,
separator_str=",", ignore_none=False
)
)
@classmethod
def gen_insertsql_multi(cls, table_name: str, a_list: Union[List[dict], Tuple[dict]]) -> Optional[List[str]]:
r"""
批量生成 数据库插入语句,
a_list 中必须是字典,
a_list 为 None时返回 None
map 中的值必须是字符串或None
当 map 中的值为 None 时,填充null值,
当 map 为 None 或empty时返回空字符串
Args:
table_name:
a_list:
Returns:
"""
# 判断参数
if a_list is None:
return None
if not isinstance(a_list, (list, tuple)):
raise ValueError("a_list 不是列表")
if not a_list:
return []
result_list = []
for tmp_dict in a_list:
tmp_sql = cls.gen_insertsql(table_name, tmp_dict)
result_list.append(tmp_sql)
return result_list
@classmethod
def gen_part_sql_for_update(cls, update_dict: dict, ignore_none: bool = True) -> str:
r"""
生成update语句中 set k1=v1,k2=v2 这部分语句(不包括set)
update中的值为null时,根据 ignore_none 的值决定时忽略该key,还是设置为 set key=null
ignore_none 默认为True
Args:
update_dict:
ignore_none:
Returns:
"""
# 检查参数
if update_dict is None or not update_dict:
return MyStringUtils.EMPTY
if not isinstance(update_dict, dict):
raise ValueError("update_dict 不是字典")
col_values = []
for tmp_k, tmp_v in update_dict.items():
tmp_k = Utility.do_filter_mysql_field(tmp_k, append_fix='`')
tmp_v = Utility.do_filter_mysql_param(tmp_v, append_fix="'")
if tmp_v is None:
if ignore_none:
continue
else:
tmp_v = 'null'
col_values.append(Utility.join_str(tmp_k, '=', tmp_v))
if not col_values:
raise create_base_exception(
Utility.join_str_with_none("参数错误:",
"update_dict所有的value都是None"),
submitted_webarg=Utility.join_str_with_none(cls.__name__,
" gen_part_sql_for_update时 ",
"update_dict所有的value都是None")
)
return Utility.list_join_to_str(col_values, separator_str=',')
@classmethod
def gen_part_sql_for_where(cls, where_dict: dict, where_dict_can_be_none_or_empty: bool = False,
table_alias_name: str = None) -> str:
r"""
生成update语句中 where k1=v1 and k2=v2 这部分语句(不包括where)
where_dict 中的值为null时,判断语句为 key is null
当 where_dict 为 None 或 空字典时, 根据 where_dict_can_be_none_or_empty 决定是抛出异常还是返回 1=1
where_dict_can_be_none_or_empty 默认为False
Args:
where_dict:
where_dict_can_be_none_or_empty:
table_alias_name: 表的别名
Returns:
"""
# 检查参数
if not where_dict:
if not where_dict_can_be_none_or_empty:
raise create_base_exception(
Utility.join_str_with_none("参数错误:", "where_dict为空"),
submitted_webarg=Utility.join_str_with_none(
cls.__name__, " gen_part_sql_for_where时 ", "where_dict为空"
)
)
else:
return '1=1'
if not isinstance(where_dict, dict):
raise ValueError("where_dict 不是字典")
where_args = []
table_alias_name = Utility.do_filter_mysql_field(
table_alias_name, append_fix='`') if table_alias_name else table_alias_name
for tmp_k, tmp_v in where_dict.items():
tmp_k = Utility.do_filter_mysql_field(tmp_k, append_fix='`')
if table_alias_name:
tmp_k = Utility.join_str(table_alias_name, ".", tmp_k)
tmp_v = Utility.do_filter_mysql_param(tmp_v, append_fix="'")
if tmp_v is None:
where_args.append(Utility.join_str(tmp_k, ' is null'))
else:
where_args.append(Utility.join_str(tmp_k, '=', tmp_v))
return Utility.list_join_to_str(where_args, separator_str=' and ')
@classmethod
def gen_updatesql(cls, table_name: str, update_dict: dict, where_dict: dict, ignore_none: bool = True,
where_dict_can_be_none_or_empty: bool = False) -> str:
r"""
生成update语句,
update_dict 中的值为null时,根据 ignore_none 的值决定时忽略该key,还是设置为 set key=null
ignore_none 默认为True
如果 update_dict 为 None 或 空字典时,返回 EMPTY
where_dict 中的值为null时,判断语句为 key is null
当 where_dict 为 None 或 空字典时, 根据 where_dict_can_be_none_or_empty 决定是抛出异常还是使用 1=1
where_dict_can_be_none_or_empty 默认为False
Args:
table_name:
update_dict:
where_dict:
ignore_none:
where_dict_can_be_none_or_empty:
Returns:
"""
# 检查参数
if update_dict is None or not update_dict:
return MyStringUtils.EMPTY
if (where_dict is None or not where_dict) and not where_dict_can_be_none_or_empty:
raise create_base_exception(
Utility.join_str_with_none("参数错误:",
"where_dict为空"),
submitted_webarg=Utility.join_str_with_none(cls.__name__,
" gen_updatesql时 ",
"where_dict为空")
)
table_name = MyStringUtils.to_str(table_name, trim=True)
if MyStringUtils.is_empty(table_name):
raise ValueError("table_name 不能为空")
table_name = Utility.do_filter_mysql_field(table_name, append_fix='`')
return Utility.join_str("update ", table_name, " set ",
cls.gen_part_sql_for_update(update_dict, ignore_none), " where ",
cls.gen_part_sql_for_where(where_dict,
where_dict_can_be_none_or_empty))
@classmethod
def gen_delsql(cls, table_name: str, where_dict: dict, where_dict_can_be_none_or_empty: bool = False) -> str:
r"""
生成delete语句,
where_dict 中的值为null时,判断语句为 key is null
当 where_dict 为 None 或 空字典时, 根据 where_dict_can_be_none_or_empty 决定是抛出异常还是使用 1=1
where_dict_can_be_none_or_empty 默认为False
Args:
table_name:
where_dict:
where_dict_can_be_none_or_empty:
Returns:
"""
# 检查参数
if (where_dict is None or not where_dict) and not where_dict_can_be_none_or_empty:
raise create_base_exception(
Utility.join_str_with_none("参数错误:",
"where_dict为空"),
submitted_webarg=Utility.join_str_with_none(cls.__name__,
" gen_delsql时 ",
"where_dict为空")
)
table_name = MyStringUtils.to_str(table_name, trim=True)
if MyStringUtils.is_empty(table_name):
raise ValueError("table_name 不能为空")
table_name = Utility.do_filter_mysql_field(table_name, append_fix='`')
return Utility.join_str("delete from ", table_name, " where ",
cls.gen_part_sql_for_where(where_dict,
where_dict_can_be_none_or_empty))
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import random
import re
import redis
from redis.sentinel import Sentinel
from .globalconst import RedisConnConst, RedisSentinelConnConst, RedisConst
from .globalerror import RedisError, CommonError
class RedisHandle:
def __init__(self):
r"""
初始化变量
"""
# 初始化记录连接池的字段,key 为拼接的连接字符串, val为pool
self.dict_conn_pool = {}
pass
def __del__(self):
r"""
删除所有连接和池子
"""
if self.dict_conn_pool is not None:
for k, v in self.dict_conn_pool.items():
del v
pass
def check_parm(self, class_parm: any = None, dict_parm: dict = None) -> bool:
r"""
通过类检测字典是否包含所有的key值
:param class_parm: 类名字
:param dict_parm: 字典
:return:
"""
if class_parm is not None and dict is not None:
for k, v in class_parm.__dict__.items():
if isinstance(v, str) is True and re.match('^__(.*)__$', k) is None:
if v not in dict_parm:
raise ValueError(RedisError.ERROR_LACK_CONN_PARM + k)
else:
raise ValueError(CommonError.ERROR_LACK_PARM)
return True
def get_master(self, dict_sentinelinfo: dict = None) -> list:
r"""
:param dict_sentinelinfo: 连接字典参数
:return:
"""
list_ret_val = ()
try:
sentinel = Sentinel((dict_sentinelinfo[RedisSentinelConnConst.CONN_HOSTS]), socket_timeout=0.1)
list_ret_val = sentinel.discover_master(dict_sentinelinfo[RedisSentinelConnConst.CONN_CLUSTERNAME])
except redis.sentinel.MasterNotFoundError:
raise ValueError(
RedisError.ERROR_SENTI_MASTER_NOT_FOUND + dict_sentinelinfo[RedisSentinelConnConst.CONN_HOSTS])
return list_ret_val
def get_slave(self, dict_sentinelinfo: dict = None) -> list:
r"""
:param dict_sentinelinfo:
:return:
"""
list_ret_val = []
try:
sentinel = Sentinel((dict_sentinelinfo[RedisSentinelConnConst.CONN_HOSTS]), socket_timeout=0.1)
list_ret_val = sentinel.discover_slaves(dict_sentinelinfo[RedisSentinelConnConst.CONN_CLUSTERNAME])
except redis.sentinel.MasterNotFoundError:
raise ValueError(
RedisError.ERROR_SENTI_MASTER_NOT_FOUND + dict_sentinelinfo[RedisSentinelConnConst.CONN_HOSTS])
return list_ret_val
def conn_redis(self, dict_redisinfo: dict = None, b_is_pool: bool = True) -> tuple:
"""
用户登录处理
Args:
dict_redisinfo: dict redis配置信息
b_is_pool: 是否启用连接池
Returns: dict
"""
try:
if dict_redisinfo is not None:
# 检查key是否都存在
for k, v in RedisConnConst.__dict__.items():
if isinstance(v, str) is True and re.match('^__(.*)__$', k) is None:
if v not in dict_redisinfo:
raise ValueError(RedisError.ERROR_LACK_CONN_PARM + v)
else:
raise ValueError(CommonError.ERROR_CONN_PARM)
pool = None
if b_is_pool is True:
pool = redis.ConnectionPool(host=dict_redisinfo[RedisConnConst.CONN_IP],
port=dict_redisinfo[RedisConnConst.CONN_PORT],
db=dict_redisinfo[RedisConnConst.CONN_DB],
password=dict_redisinfo[RedisConnConst.CONN_AUTH])
r = redis.Redis(connection_pool=pool)
else:
r = redis.Redis(host=dict_redisinfo[RedisConnConst.CONN_IP],
port=dict_redisinfo[RedisConnConst.CONN_PORT],
db=dict_redisinfo[RedisConnConst.CONN_DB],
password=dict_redisinfo[RedisConnConst.CONN_AUTH])
except redis.exceptions.AuthenticationError:
raise ValueError(RedisError.ERROR_AUTH + dict_redisinfo[RedisConnConst.CONN_AUTH])
except redis.exceptions.ConnectionError:
raise ValueError(RedisError.ERROR_CONN + dict_redisinfo[RedisConnConst.CONN_IP] + ":" + dict_redisinfo[
RedisConnConst.CONN_PORT])
return r, pool
def get_conn_redis_from_pool(self, redis_pool: any = None) -> any:
r"""
从redis连接池获取连接
:param redis_pool:
:return:
"""
try:
if redis_pool is None:
# 检查参数是否为null
raise ValueError(RedisError.ERROR_CONN_POOL_NONE)
r = redis.Redis(connection_pool=redis_pool)
except redis.exceptions.AuthenticationError:
raise ValueError(RedisError.ERROR_AUTH)
except redis.exceptions.ConnectionError:
raise ValueError(RedisError.ERROR_CONN)
return r
def init_redis_handle(self, dict_conn_info: dict = None, b_is_sentinel: bool = False, b_is_w: bool = False,
b_is_pool: bool = True) -> tuple:
r"""
初始化redis
Args:
dict_conn_info: 连接信息
b_is_sentinel: 是否是哨兵,默认否,不是哨兵直接是连接信息
b_is_w: 是否是写库
b_is_pool: 是否使用连接池
Returns: 返回元组, r,pool redis连接和redis连接池
"""
r = None
try:
list_host: list = []
# 处理连接池字符串
if b_is_sentinel is True:
str_conn = "_".join(
[conn[0] + "_" + conn[1] for conn in dict_conn_info[RedisSentinelConnConst.CONN_HOSTS]])
else:
str_conn = "_".join(
[dict_conn_info[RedisConnConst.CONN_IP], dict_conn_info[RedisConnConst.CONN_PORT]])
# 确定redis是读还是写
if b_is_w is True:
str_conn = str_conn + "_w"
else:
str_conn = str_conn + "_r"
# 获取redis连接
if str_conn in self.dict_conn_pool:
pool = self.dict_conn_pool[str_conn]
r = self.get_conn_redis_from_pool(pool)
else:
if b_is_sentinel is True:
dict_sentinel = {k: dict_conn_info[k] for k in
(RedisSentinelConnConst.CONN_HOSTS, RedisSentinelConnConst.CONN_CLUSTERNAME)}
if b_is_w is True:
list_host = self.get_master(dict_sentinel)
else:
list_t = self.get_slave(dict_sentinel)
list_host = list_t[random.randint(0, len(list_t) - 1)]
else:
list_host.append(dict_conn_info[RedisConnConst.CONN_IP])
list_host.append(dict_conn_info[RedisConnConst.CONN_PORT])
dict_redis = {
RedisConnConst.CONN_IP: list_host[0]
, RedisConnConst.CONN_PORT: list_host[1]
, RedisConnConst.CONN_AUTH: dict_conn_info[RedisConnConst.CONN_AUTH]
, RedisConnConst.CONN_DB: dict_conn_info[RedisConnConst.CONN_DB]
}
for k, v in RedisConnConst.__dict__.items():
# 检查是否是类自定义的属性
if isinstance(v, str) is True and re.match(r'^__(.*)__$', k) is None:
if v not in dict_redis:
raise ValueError(RedisError.ERROR_LACK_CONN_PARM + v)
r, pool = self.conn_redis(dict_redis, b_is_pool)
self.dict_conn_pool[str_conn] = pool
except redis.exceptions.AuthenticationError:
self.dict_conn_pool.pop(str_conn)
raise ValueError(RedisError.ERROR_AUTH + dict_redis[RedisConnConst.CONN_AUTH])
except redis.exceptions.ConnectionError:
self.dict_conn_pool.pop(str_conn)
raise ValueError(RedisError.ERROR_CONN + dict_redis[RedisConnConst.CONN_IP] + ":" + dict_redis[
RedisConnConst.CONN_PORT])
except Exception as E:
print(E)
raise ValueError(str(E))
return r
def get_vals(self, dict_kv: dict = None, dict_redisinfo: dict = None, b_is_sentinel: bool = False) -> dict:
r"""
获取一组带映射的redis的kv
:param dict_kv: 需要映射的key val值,即需要key,返回值的key为val
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: redis连接信息
:return:
"""
ret_dict = {}
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=False)
# 执行redis查询
for k, v in dict_kv.items():
# 处理映射路径
if r.exists(k):
ret_dict[v] = r.get(k).decode('utf-8')
else:
continue
return ret_dict
def set_vals(self, dict_kv: dict = None, dict_redisinfo: dict = None, b_is_sentinel: bool = False) -> dict:
r"""
获取一组带映射的redis的kv
:param dict_kv: 需要设置的k。v值
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: redis连接信息
:return:
"""
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=True)
if RedisConst.EXPIRE_TIME in dict_redisinfo:
ex = dict_redisinfo[RedisConst.EXPIRE_TIME]
else:
ex = None
# 执行redis查询
for k, v in dict_kv.items():
# 处理映射路径
if isinstance(v, str) is False:
raise ValueError(RedisError.ERROR_INSERT_VALUE_TYPE)
r.set(name=k, value=v, ex=ex)
return
def set_key_expire(self, dict_kv: dict = None, dict_redisinfo: dict = None, b_is_sentinel: bool = False) -> bool:
r"""
设置一组key时长
:param dict_kv: 需要设置的k。v值
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: redis连接信息
:return:
"""
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=True)
if RedisConst.EXPIRE_TIME in dict_redisinfo:
ex = dict_redisinfo[RedisConst.EXPIRE_TIME]
else:
ex = None
# 执行redis查询
for k, v in dict_kv.items():
# 处理映射路径
r.expire(name=k, time=ex)
return True
def del_key(self, dict_kv: dict = None, dict_redisinfo: dict = None, b_is_sentinel: bool = False) -> bool:
r"""
设置一组key时长为0,即删除key
:param dict_kv: 需要设置的k。v值
:param dict_redisinfo: redis连接信息
:return:
"""
dict_redisinfo_tmp = dict_redisinfo.copy()
dict_redisinfo_tmp[RedisConst.EXPIRE_TIME] = 0
self.set_key_expire(dict_kv=dict_kv, dict_redisinfo=dict_redisinfo_tmp, b_is_sentinel=b_is_sentinel)
return True
def set_key_incr(self, dict_kv: dict = None, dict_redisinfo: dict = None, b_is_sentinel: bool = False) -> dict:
r"""
设置一组key自增
:param dict_kv: 需要设置的k。v值
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: redis连接信息
:return:
"""
ret_dict = {}
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=True)
# 执行redis查询
for k, v in dict_kv.items():
# 处理映射路径
ret_dict[k] = r.incr(name=k)
return ret_dict
def set_hash_vals(self, hash_key, attrs_dict=None, dict_redisinfo=None, b_is_sentinel=False):
"""
为散列里面的一个或多个键设置值
:param hash_key: 散列键
:param attrs_dict: 键值对字典
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: 是否是哨兵,默认否,不是哨兵直接是连接信息
:return: False / True 写入成功
"""
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=True)
# hash_key不能为空
if hash_key is None or len(hash_key.strip()) == 0 \
or not attrs_dict:
return False
# 批量添加属性
ret = r.hmset(hash_key, attrs_dict)
return ret
def get_hash_vals(self, hash_key, keys_list=None, dict_redisinfo=None, b_is_sentinel=False):
"""
从散列里面获取一个或多个键的值
:param hash_key: 散列键
:param keys_list: 键值对列表
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: 是否是哨兵,默认否,不是哨兵直接是连接信息
:return: None / {'': ''}
:return: None / {'k1': 'v1' ,'k2': 'v2'}
"""
ret = {}
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=False)
# 散列表里存在hash_key
if hash_key is None or r.hlen(hash_key) == 0:
return None
# 获取散列的键值结果
if keys_list:
for key in keys_list:
ret[key] = r.hmget(hash_key, key)[0] if r.hmget(hash_key, key) else None
else:
ret = r.hgetall(hash_key)
return ret
def delete_hash_vals(self, hash_key, key=None, dict_redisinfo=None, b_is_sentinel=False):
"""
删除散列或者散列里面的一个或多个键值对
:param hash_key: 散列键
:param key: 键
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: 是否是哨兵,默认否,不是哨兵直接是连接信息
:return: False / True 删除成功
"""
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=True)
# 散列表里存在hash_key
if hash_key is None or r.hlen(hash_key) == 0:
return False
# 删除
if key is not None:
return True if r.hdel(hash_key, key) > 0 else False
# 删除整个hash
dict_kv = {hash_key: ''}
ret = self.del_key(dict_kv, dict_redisinfo=dict_redisinfo)
return ret
def set_set_vals(self, key, val=None, dict_redisinfo=None, b_is_sentinel=False):
"""
将给定元素添加到集合
:param key: key
:param val: value
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: 是否是哨兵,默认否,不是哨兵直接是连接信息
:return: None / 1 写入成功
"""
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=True)
# 散列表里存在hash_key
if key is None or val is None:
return None
# 添加进set
ret = r.sadd(key, val)
return ret
def get_set_vals(self, key, dict_redisinfo=None, b_is_sentinel=False):
"""
返回集合包含的元素
:param key: 键
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: 是否是哨兵,默认否,不是哨兵直接是连接信息
:return:None / [a, b]
"""
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=False)
# key不为空,并判断key是否有指
if key is None or r.scard(key) == 0:
return None
# 返回集合包含的所有元素
ret = list(r.smembers(key))
return ret
def publisher(self, channel, message=None, dict_redisinfo=None, b_is_sentinel=False):
"""
向给定频道发送消息
:param channel: 频道
:param message: 消息
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: 是否是哨兵,默认否,不是哨兵直接是连接信息
:return: 0 / 1
"""
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=True)
# channel和message不能为空
if channel is None or message is None:
return 0
# 发布消息
ret = r.publish(channel, message)
return ret
def subscribe_channel(self, channel, dict_redisinfo=None, b_is_sentinel=False):
"""
订阅给定的一个或多个频道
:param channel: 频道
:param dict_redisinfo: redis连接信息
:param b_is_sentinel: 是否是哨兵,默认否,不是哨兵直接是连接信息
:return: None / pubsub对象
"""
# 获取redis连接
r = self.init_redis_handle(dict_conn_info=dict_redisinfo, b_is_sentinel=b_is_sentinel, b_is_w=False)
# channel为空,返回None
if channel is None:
return None
# 订阅消息
ps = r.pubsub()
ps.subscribe(channel)
return ps
'''
@Description: 简单数据操作类
@Author: guohb65
@Email: guohb65@chinaunicom.cn
@Date: 2019-01-24 18:39:08
@LastEditTime: 2019-07-10 17:16:54
@LastEditors: Please set LastEditors
'''
import json
from .my_datahandle.const import ConstGen
from .my_datahandle.datahandle_utility import DatahandleUtility
class SimpleSqlOpt(object):
def __init__(self):
pass
def simple_add(self, table_name, request=None, data_params=None, uid=None):
"""
单条数据新增
:param request:
:return:
"""
datahandle_utility = DatahandleUtility()
if data_params is None:
data_params = json.loads(datahandle_utility.request_param2dict(
request).get(ConstGen.DATA_STR))
unique_data = data_params[ConstGen.UNIQUE_STR]
others_data = data_params[ConstGen.OTHERS_STR]
if datahandle_utility.data_is_exist(unique_data, table_name):
ret = {ConstGen.RETCODE: "2", ConstGen.RETVAL: "名称重复"}
print(ret)
return json.dumps(ret, ensure_ascii=False)
else:
insert_data = dict(unique_data, **others_data)
insert_sql = datahandle_utility.gen_insert_sql(
dict_data=insert_data, table_name=table_name, uid=uid)
trans_data = datahandle_utility.package_sql(
ConstGen.TRANSSQL_STR, insert_sql)
return datahandle_utility.request_trans_service(
ConstGen.TRANSSQL_URL, trans_data)
def batch_add(self, table_name, request=None, data_params=None):
"""
批量新增
:param request:
:return:
"""
datahandle_utility = DatahandleUtility()
if data_params is None:
data_params = json.loads(datahandle_utility.request_param2dict(
request).get(ConstGen.DATA_STR))
trans_sql_list = []
for json_obj in data_params:
if ConstGen.UNIQUE_STR in json_obj:
unique_data = data_params[ConstGen.UNIQUE_STR]
if ConstGen.OTHERS_STR in json_obj:
others_data = data_params[ConstGen.OTHERS_STR]
if datahandle_utility.data_is_exist(unique_data, table_name):
ret = {ConstGen.RETCODE: "2", ConstGen.RETVAL: "名称重复"}
print(ret)
return json.dumps(ret, ensure_ascii=False)
else:
insert_data = dict(unique_data, **others_data)
insert_sql = datahandle_utility.gen_insert_sql(
insert_data, table_name)
trans_sql = datahandle_utility.trans_sql(insert_sql)
trans_sql_list.append(trans_sql)
trans_data = datahandle_utility.package_trans_list(trans_sql_list)
return datahandle_utility.request_trans_service(
ConstGen.TRANSSQL_URL, trans_data)
def simple_alter(self, table_name, request=None, data_params=None):
"""
修改/删除
:param request:
:return:
"""
datahandle_utility = DatahandleUtility()
if data_params is None:
data_params = datahandle_utility.request_param2dict(request)
if ConstGen.OPT_UPDATE in data_params.keys():
update_all_data = json.loads(data_params.get(ConstGen.OPT_UPDATE))
update_condition = update_all_data[ConstGen.CONDITION_STR]
update_data = update_all_data[ConstGen.DATA_STR]
unique_data = update_data[ConstGen.UNIQUE_STR]
if datahandle_utility.data_is_exist(unique_data, table_name, update_condition):
ret = {ConstGen.RETCODE: "2", ConstGen.RETVAL: "名称重复"}
print(ret)
return json.dumps(ret, ensure_ascii=False)
else:
update_sql = datahandle_utility.gen_update_sql(
update_data, update_condition, table_name)
trans_data = datahandle_utility.package_sql(
ConstGen.TRANSSQL_STR, update_sql)
if trans_data is None:
ret = {ConstGen.RETCODE: "2", ConstGen.RETVAL: "更新参数传输异常"}
return json.dumps(ret, ensure_ascii=False)
else:
return datahandle_utility.request_trans_service(
ConstGen.TRANSSQL_URL, trans_data)
if ConstGen.OPT_DELETE in data_params.keys():
delete_condition = json.loads(data_params.get(ConstGen.OPT_DELETE))
delete_sql = datahandle_utility.gen_delete_sql(
delete_condition, table_name)
trans_data = datahandle_utility.package_sql(
ConstGen.TRANSSQL_STR, delete_sql)
if trans_data is None:
ret = {ConstGen.RETCODE: "2", ConstGen.RETVAL: "删除数据参数传输异常"}
return json.dumps(ret, ensure_ascii=False)
else:
return datahandle_utility.request_trans_service(
ConstGen.TRANSSQL_URL, trans_data)
def simple_query(self, table_name, request=None, data_params=None):
"""
信息查询
:param request:
:return:
"""
datahandle_utility = DatahandleUtility()
if data_params is None:
data_params = datahandle_utility.request_param2dict(request)
print(data_params)
print(ConstGen.STANDERSQL_URL)
trans_data = datahandle_utility.gen_select_all_sql(
data_params, table_name)
print(trans_data)
if trans_data is None:
ret = {ConstGen.RETCODE: "2", ConstGen.RETVAL: "查询条件不存在"}
return json.dumps(ret, ensure_ascii=False)
else:
ret_data = datahandle_utility.request_trans_service(
ConstGen.STANDERSQL_URL, trans_data)
if ConstGen.CONDITION_STR in data_params.keys():
condition = json.loads(
data_params.get(ConstGen.CONDITION_STR))
page_num = None
page_size = None
if ConstGen.PAGE_STR in condition:
page_info = condition.get(ConstGen.PAGE_STR)
if ConstGen.PAGE_NUM_STR in page_info:
page_num = page_info[ConstGen.PAGE_NUM_STR]
if ConstGen.PAGE_SIZE_STR in page_info:
page_size = page_info[ConstGen.PAGE_SIZE_STR]
if page_num is not None and page_size is not None:
ret_data = json.loads(ret_data)
start_index = (int(page_num) - 1) * int(page_size)
if int(ret_data[ConstGen.ROWCOUNT_STR]) < start_index:
ret_data.pop(ConstGen.DATAROWS_STR)
ret_data[ConstGen.PAGECOUNT_STR] = "0"
else:
if ConstGen.DATAROWS_STR in ret_data:
all_datarows = ret_data[ConstGen.DATAROWS_STR]
print(all_datarows)
datarows = all_datarows[start_index: start_index +
int(page_size)]
ret_data[ConstGen.DATAROWS_STR] = datarows
ret_data[ConstGen.PAGECOUNT_STR] = len(datarows)
else:
ret_data[ConstGen.PAGECOUNT_STR] = '0'
return json.dumps(ret_data)
return ret_data
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import logging
from .globalconst import SkyGlobalConst
from .my_stringutils import MyStringUtils
from .my_utils import MyUtils
class MyBaseException(Exception):
"""
自定义的异常类, import 请使用 global
"""
ret_val: str = MyStringUtils.EMPTY
ret_code: str = 'ERROR'
submitted_webarg: str = None
def __init__(self, err_msg: str):
r'''
初始化异常
Args:
err_msg: 错误信息,不能为None或空字符串
ret_code: 错误码
submitted_webarg:
'''
# 判断 err_msg
Exception.__init__(self, err_msg)
self.ret_val = err_msg
def __str__(self):
r'''
字符串表示,使用 ret_val , 不会返回None,至少时 EMPTY字符串
Returns:
'''
return MyStringUtils.to_str(self.ret_val)
def __repr__(self):
r'''
字符串表示,使用 __str__ , 不会返回None,至少时 EMPTY字符串
Returns:
'''
return self.__str__()
def gen_err(self) -> dict:
r'''
生成带有 RetCode,RetVal,SubmittedWebArg的标准dict
Returns:
'''
ret_dict = MyBaseException.format_to_standard_dict(self.ret_val, self.ret_code)
if self.submitted_webarg is not None:
ret_dict[SkyGlobalConst.RETKEY_SUBMITTEDWEBARG] = MyStringUtils.to_str(
MyUtils.do_filter_xss(self.submitted_webarg))
return ret_dict
@classmethod
def format_to_standard_dict(cls, ret_val: str,
ret_code: str = SkyGlobalConst.RETCODE_COMMON_ERROR, submitted_msg: str = None) -> dict:
r"""
将ret_val和ret_code格式化成 ryy项目组使用的标准json 字符串,
即 { "RetCode": ret_code , "RetVal": ret_val }
Args:
ret_val:string 返回结果 , 自动对其进行 filterXss操作
ret_code:string 返回码
Returns: json 返回json数据
"""
ret_dict = {}
ret_dict[SkyGlobalConst.RETKEY_RET_CODE] = MyStringUtils.to_str(MyUtils.do_filter_xss(ret_code))
ret_dict[SkyGlobalConst.RETKEY_RET_VAL] = MyStringUtils.to_str(MyUtils.do_filter_xss(ret_val))
if submitted_msg is not None:
ret_dict[SkyGlobalConst.RETKEY_SUBMITTEDWEBARG] = MyStringUtils.to_str(MyUtils.do_filter_xss(submitted_msg))
return ret_dict
@classmethod
def gen_ok_dict(cls) -> dict:
r"""
生成标准的 返回给前端的jsonObject,ok
Args:
Returns: json 返回json数据
"""
ret_dict = {}
ret_dict[SkyGlobalConst.RETKEY_RET_CODE] = SkyGlobalConst.RETCODE_SUCESS_CODE
ret_dict[SkyGlobalConst.RETKEY_RET_VAL] = 'success'
return ret_dict
@classmethod
def format_exception_to_standard_dict(cls, any_exception: Exception) -> dict:
r'''
将异常格式化为标准的带有 RetCode和RetVal的字典
Args:
any_exception:
Returns:
'''
if any_exception is None:
raise ValueError("要格式化的异常对象为None")
logging.exception(msg="exception")
if MyUtils.has_attr(any_exception, 'gen_err', must_be_method=True):
# 尝试使用 gen_err方法返回
return any_exception.gen_err()
else:
if isinstance(any_exception, (NameError, ValueError,)):
return MyBaseException.format_to_standard_dict(str(any_exception))
else:
return MyBaseException.format_to_standard_dict(
'{}:{}'.format(type(any_exception), any_exception)
)
def create_base_exception(
err_msg,
ret_code='ERROR',
submitted_webarg=None
) -> MyBaseException:
r'''
初始化一个 MyBaseException
Args:
err_msg:
ret_code:
submitted_webarg:
Returns:
:rtype: MyBaseException
'''
ret_excep = MyBaseException(err_msg)
ret_excep.ret_code = ret_code
ret_excep.submitted_webarg = submitted_webarg
return ret_excep
def create_base_exception_with_dict(
err_dict: dict,
ret_code: str = 'ERROR',
submitted_webarg: str = None
) -> MyBaseException:
r'''
初始化一个 MyBaseException,
以 err_dict 中的RetVal为errMsg,
以 err_dict 中的 SubmittedWebArg + submitted_webarg 为新的 submitted_webarg
Args:
err_dict:
ret_code:
submitted_webarg:
Returns:
:rtype: MyBaseException
'''
# 确保 err_dict 是dict
if not isinstance(err_dict, dict):
raise ValueError("err_dict不是字典")
# 获取 err_dict 中的 retVal
err_msg = err_dict.get(SkyGlobalConst.RETKEY_RET_VAL)
# 获取 err_dict 中的 SubmittedWebArg
ori_submitted_webarg = err_dict.get(SkyGlobalConst.RETKEY_SUBMITTEDWEBARG)
ret_excep = MyBaseException(err_msg)
ret_excep.ret_code = ret_code
if MyStringUtils.is_empty(ori_submitted_webarg):
ret_excep.submitted_webarg = submitted_webarg
else:
if MyStringUtils.is_empty(submitted_webarg):
ret_excep.submitted_webarg = ori_submitted_webarg
else:
ret_excep.submitted_webarg = '{};;;{}'.format(ori_submitted_webarg,
submitted_webarg)
return ret_excep
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@Version: 1.0
@Python Version:3.6.6
@Author: ludq1
@Email: ludq1@chinaunicom.cn
@date: 2023/04/07 11:40:00
@Description:
"""
import json
import re
import requests
from .globalconst import HttpConst
from .my_stringutils import MyStringUtils
from .my_utils import MyUtils
from .sky_baseexception import MyBaseException, create_base_exception
class HttpTrans:
"""
http传输处理
"""
# 定义要透传的header_key
_header_key = [
'Cookie'
, 'x-request-id'
, 'x-b3-traceid'
, 'x-b3-spanid'
, 'x-b3-parentspanid'
, 'x-b3-sampled'
, 'x-b3-flags'
, 'x-ot-span-context'
]
def __init__(self, http_header: dict = None):
r"""
Args:
http_header: dict http的请求头
"""
if http_header is not None:
try:
# 尝试将 http_header 转成 dict 并赋值
self._header = dict(http_header)
except:
raise ValueError('http_header 不能转换为字典')
else:
self._header = None
def generate_header(self, content_type: str = None,
dict_header: dict = None) -> dict:
"""
生成http头信息
Args:
content_type: string 传输类型
dict_header: dict 额外头信息
Returns: dict
"""
# 检查参数
if not (dict_header is None or isinstance(dict_header, dict)):
raise ValueError('dict_header 不是字典')
ret_header = {}
# 加入需要透传的 self._header
if self._header:
# 转换为标准的字符串kv字典
formatted_dict = MyUtils.format_dict_to_str_dict(self._header)
# 过滤出指定的key
filtered_keys = filter(lambda tmp_k: tmp_k in self._header_key, formatted_dict.keys())
# 生成过滤后的字典
filtered_dict = {tmp_k: formatted_dict.get(tmp_k) for tmp_k in filtered_keys}
# 更新 ret_dict
ret_header.update(filtered_dict)
# 设置参数默认值-- str_content_type
if not MyStringUtils.is_empty(content_type):
str_content_type = str(content_type)
else:
str_content_type = HttpConst.CONTENT_TYPE_URLENCODED
# 更新 ret_dict
ret_header.update({
HttpConst.HEADER_KEY_CONTENT_TYPE: str_content_type
})
# 加入额外的HEADER KEY
if dict_header:
formatted_dict = MyUtils.format_dict_to_str_dict(dict_header)
# 更新 ret_dict
ret_header.update(formatted_dict)
# 移除 Request Method 这个key,因为该key存在会造成requests请求失效
ret_header.pop(HttpConst.HEADER_KEY_REQUEST_METHOD, None)
return ret_header
def get_data(self, urlstr: str, trans_data: dict = None, dict_header: dict = None) -> requests.Response:
"""
post 传输数据
Args:
urlstr:
trans_data: 要附加到 urlstr 上的 key-value 字典 , kv 会自动编码
dict_header: 传输时使用的header
Returns:
"""
# 检查参数
if not (trans_data is None or isinstance(trans_data, dict)):
raise ValueError('trans_data 不是字典')
if trans_data:
trans_data = MyUtils.format_dict_to_str_dict(trans_data)
content_type = HttpConst.CONTENT_TYPE_URLENCODED
if dict_header is not None and HttpConst.HEADER_KEY_CONTENT_TYPE in dict_header.keys():
content_type = dict_header.get(HttpConst.HEADER_KEY_CONTENT_TYPE)
headers = self.generate_header(
content_type=content_type,
dict_header=dict_header
)
cookies = None
if HttpConst.HEADER_KEY_COOKIE in headers:
cookies = headers.get(HttpConst.HEADER_KEY_COOKIE)
regexp = (r'^https://')
match = re.search(regexp, urlstr.strip(), re.U)
if (match is not None):
return requests.get(url=urlstr, headers=headers, params=trans_data, cookies=cookies, verify=False)
else:
return requests.get(url=urlstr, headers=headers, params=trans_data, cookies=cookies)
def post_encodeddata_standard(self, urlstr: str, trans_data: dict = None,
dict_header: dict = None) -> requests.Response:
"""
post 传输数据,返回 Response对象
Args:
urlstr: string httpurl
trans_data: 传输的内容 ,字典 , kv会自动编码
dict_header: 传输时使用的header
Returns:
"""
if not (trans_data is None or isinstance(trans_data, dict)):
raise ValueError('trans_data 不是字典')
trans_data_tmp = None
content_type = HttpConst.CONTENT_TYPE_URLENCODED
if dict_header is not None and HttpConst.HEADER_KEY_CONTENT_TYPE in dict_header.keys():
content_type = dict_header.get(HttpConst.HEADER_KEY_CONTENT_TYPE)
if HttpConst.CONTENT_TYPE_JSON == dict_header.get(HttpConst.HEADER_KEY_CONTENT_TYPE):
trans_data_tmp = json.dumps(trans_data)
if trans_data_tmp is None:
trans_data_tmp = MyUtils.format_dict_to_str_dict(trans_data)
headers = self.generate_header(
content_type=content_type,
dict_header=dict_header
)
regexp = (r'^https://')
match = re.search(regexp, urlstr.strip(), re.U)
if (match is not None):
return requests.post(url=urlstr, headers=headers, data=trans_data_tmp, verify=False)
else:
return requests.post(url=urlstr, headers=headers, data=trans_data_tmp)
def post_encodedata(self, urlstr: str,
trans_data: dict = None,
dict_header: dict = None,
**dict_arg) -> str:
"""
post 传输数据,返回rensponse内容字符串,传输异常时返回 标准错误的json字符串,所以该方法适用于标准的传输
Args:
urlstr: string httpurl
trans_data: 传输的内容 , 字典,kv会自动编码
dict_header: 传输时使用的header
**dict_arg: 额外参数
Returns: 标准格式的 json字符串
"""
# 以后额外扩展
# if dict_arg:
# pass
try:
m_response = self.post_encodeddata_standard(
urlstr=urlstr,
trans_data=trans_data,
dict_header=dict_header
)
if not m_response.ok:
raise create_base_exception(
'访问url出错,访问地址:{0},返回码{1}'.format(urlstr, m_response.status_code)
)
else:
str_ret = m_response.text
# 检查返回结果是不是有效的 jsonstr
dict_ret = MyUtils.jsonstr2dict(str_ret)
if dict_ret is None:
raise create_base_exception(
'返回的内容不是有效的json字符串:{}'.format(str_ret)
)
return str_ret
except requests.exceptions.RequestException as exception:
return MyUtils.dict2jsonstr(MyBaseException.format_exception_to_standard_dict(exception))
except MyBaseException as exception:
return MyUtils.dict2jsonstr(MyBaseException.format_exception_to_standard_dict(exception))
except Exception as exception:
return MyUtils.dict2jsonstr(MyBaseException.format_exception_to_standard_dict(exception))
def patch_encodeddata_standard(self, urlstr: str, trans_data: dict = None,
dict_header: dict = None) -> requests.Response:
"""
patch 传输数据,返回 Response对象
Args:
urlstr: string httpurl
trans_data: 传输的内容 ,字典 , kv会自动编码
dict_header: 传输时使用的header
Returns:
"""
if not (trans_data is None or isinstance(trans_data, dict)):
raise ValueError('trans_data 不是字典')
trans_data_tmp = None
content_type = HttpConst.CONTENT_TYPE_PATH_JSON
if dict_header is not None and HttpConst.HEADER_KEY_CONTENT_TYPE in dict_header.keys():
content_type = dict_header.get(HttpConst.HEADER_KEY_CONTENT_TYPE)
if HttpConst.CONTENT_TYPE_PATH_JSON == dict_header.get(HttpConst.HEADER_KEY_CONTENT_TYPE):
trans_data_tmp = json.dumps(trans_data)
if trans_data_tmp is None:
trans_data_tmp = MyUtils.format_dict_to_str_dict(trans_data)
headers = self.generate_header(
content_type=content_type,
dict_header=dict_header
)
regexp = (r'^https://')
match = re.search(regexp, urlstr.strip(), re.U)
if (match is not None):
return requests.patch(url=urlstr, headers=headers, data=trans_data_tmp, verify=False)
else:
return requests.patch(url=urlstr, headers=headers, data=trans_data_tmp)
def delete_encodeddata_standard(self, urlstr: str, trans_data: dict = None,
dict_header: dict = None) -> requests.Response:
"""
patch 传输数据,返回 Response对象
Args:
urlstr: string httpurl
trans_data: 传输的内容 ,字典 , kv会自动编码
dict_header: 传输时使用的header
Returns:
"""
if not (trans_data is None or isinstance(trans_data, dict)):
raise ValueError('trans_data 不是字典')
trans_data_tmp = None
content_type = HttpConst.CONTENT_TYPE_JSON
if dict_header is not None and HttpConst.HEADER_KEY_CONTENT_TYPE in dict_header.keys():
content_type = dict_header.get(HttpConst.HEADER_KEY_CONTENT_TYPE)
if HttpConst.CONTENT_TYPE_JSON == dict_header.get(HttpConst.HEADER_KEY_CONTENT_TYPE):
trans_data_tmp = json.dumps(trans_data)
# 如果不是json,以字符串进行传递
if trans_data_tmp is None:
trans_data_tmp = MyUtils.format_dict_to_str_dict(trans_data)
headers = self.generate_header(
content_type=content_type,
dict_header=dict_header
)
regexp = (r'^https://')
match = re.search(regexp, urlstr.strip(), re.U)
if (match is not None):
return requests.delete(url=urlstr, headers=headers, verify=False)
else:
return requests.delete(url=urlstr, headers=headers)
'''
@Descripttion:
@Author: guohb65
@Email: guohb65@chinaunicom.cn
@Date: 2019-12-18 13:18:35
@LastEditors: guohb65
@LastEditTime: 2019-12-18 13:20:58
'''
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment