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 copy
import json
import uuid
from functools import wraps
from flask import current_app, request
from .cfginfohandle import CfgInfoHandle
from .globalconst import CookiesConstKey, FlaskConfigIDConst, RedisConnConst, RedisDbNumConst, GlobalConst, \
GlobalRetKeyConst, RedisInfoKeysConst
from .globalerror import CommonError
from .my_baseexception import MyBaseException
from .my_servicehandle import ServiceHandle
from .redishanlde import RedisHandle
class LoginHandle:
# 过滤规则
FILTER_RULE_NAME = "filter_name"
# 过滤具体值
FILTER_RULE_VALUE = "filter_val"
# 精确匹配
FILTER_EXACT = "filter_exact"
# 正则匹配
FILTER_REG = "filter_regular"
def __init__(self, flask_app=None, list_filter_url: list = None):
r"""
登录处理初始化
Args:
flask_app: flask app
list_filter_url: url过滤,不需要被登录检查的 list[dict]
"""
# 初始化,处理是否接入flask
self.list_filter_url = None
if list_filter_url is not None:
self.list_filter_url = list_filter_url
self.app = flask_app
if flask_app is not None:
self.init_app(flask_app)
def init_app(self, app):
r"""
初始化flask变量,走所有服务之前去验证登录
Args:
app:
Returns:
"""
# app.before_request(self.checklogin)
pass
def login(self, login_name: str = None, login_pass: str = None, login_code: str = None, tenant_id: str = None,
proc_name: str = "auth_userlogin_proc", login_srv_url: str = None, redis_info: dict = None) -> tuple:
R"""
Args:
login_name: 登录账号
login_pass: 登录密码
login_code: 登录验证码
tenant_id: 租户ID
proc_name: proc名称
login_srv_url: 登录url
redis_info: redis信息
Returns:
"""
# 登录验证
service_url = login_srv_url
trans_data = {
"arg_tenantid": tenant_id
, "arg_username": login_name
, "arg_userpass": login_pass
}
sh = ServiceHandle()
ret_json: dict = sh.call_proc(service_url=service_url, proc_name=proc_name, arg_dict=trans_data)
# 返回失败
if sh.check_is_success(ret_json) is False or GlobalRetKeyConst.DATAROWS not in ret_json or len(
ret_json[GlobalRetKeyConst.DATAROWS]) == 0:
return ret_json, None
user_info: dict = ret_json[GlobalRetKeyConst.DATAROWS][0]
jsessionid = str(uuid.uuid1()).replace('-', '')
dict_kv = {
jsessionid: json.dumps(user_info, ensure_ascii=False)
}
# 校验成功写入缓存
rh: RedisHandle = current_app.config[FlaskConfigIDConst.REDIS_CLASS_ID]
rh.set_vals(dict_redisinfo=redis_info, dict_kv=dict_kv)
return ret_json, jsessionid
def check_login(func):
r"""
装饰器检测是否登录
Returns:
"""
@wraps(func)
def wrapper(*args, **kwargs):
if_login = False
# 检查cookies是否为null 或者 cookies中的sessionid的值是否在redis中存在
if request.cookies.keys() is not None and CookiesConstKey.LOGIN_SESSTION_KEY in request.cookies.keys():
if_login = True
rh: RedisHandle = current_app.config[FlaskConfigIDConst.REDIS_CLASS_ID]
cfg_handle: CfgInfoHandle = current_app.config[FlaskConfigIDConst.INIT_CONFIG_ID]
dict_conn: dict = copy.copy(cfg_handle.get_redis_info()[RedisInfoKeysConst.REDIS_SLAVE_KEY])
dict_conn[RedisConnConst.CONN_DB] = RedisDbNumConst.LOGIN_INFO
csm_id = request.cookies[CookiesConstKey.LOGIN_SESSTION_KEY]
dict_key = {
csm_id: csm_id
}
ret_dict = rh.get_vals(dict_key, dict_conn)
if csm_id not in ret_dict:
if_login = False
if if_login is True:
return func(*args, **kwargs)
else:
return json.dumps(MyBaseException.format_to_standard_dict(ret_code=GlobalConst.RETCODE_COMMON_ERROR,
ret_val=CommonError.ERROR_USER_UNLOGIN
), ensure_ascii=False).encode('utf-8')
return wrapper
# venv 安装
建议使用 venv 虚拟环境 使用pycharm自动生成venv
或者在项目文件夹下执行下面的语句后再在pycharm中设置python解释器为虚拟环境中的解释器
```shell
# 创建虚拟环境
python3 -m venv venv
# 激活虚拟环境
source venv/Scripts/activate
# 或者在windows 下直接使用 venv\Scripts\activate 激活
```
配置好虚拟环境后,再进行依赖包安装
# 依赖包安装
```shell
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 simplejson==3.17.0
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 Flask==1.1.2
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 pymysql==0.10.1
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 redis==3.5.3
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 pytz==2021.1
# aes加密使用的库
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 pycryptodome==3.9.9
# yaml文件
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 pyyaml==5.3.1
# rsa解密
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 rsa==4.6
# 数据库连接池
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 DBUtils==2.0
# COS操作
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 boto==2.49.0
# rarfile操作
pip3 install -i http://10.126.154.1:18083/repository/swrdcucc-group/simple --trusted-host 10.126.154.1 rarfile==4.0
# 卸载模块
pip3 uninstall cucc_common_pkg
```
# 发布
```
pip3 install --upgrade setuptools wheel twine
# 如有必要, 先将原先编译生成的目录删掉, 例如 build,dist目录
# setup.py文件同目录命令行下运行
python setup.py sdist bdist_wheel
# 检查打包的文件是否正常
python setup.py install # 安装
# 按照使用方式导入测试,没问题后继续
# 上传
twine upload dist/* --repository-url http://10.126.154.1:18083/repository/swrdcucc-hosted/ -u {wenhx8} -p {dwp}
```
# 项目调试
## 配置环境变量
```
PYTHONUNBUFFERED=1
ENV_APP_CONFIG={"db_config":{"host":"sky-mysql","port":3306,"user":"","passwd":"","database":"","charset":"utf8mb4"},"master_redis_config":{"host":"csm-redis-master.default.svc","port":6379,"password":"","db":"5"},"slave_redis_config":{"host":"csm-redis-slave.default.svc","port":6379,"password":"","db":"5"},"is_debug":false,"is_send_admin_notice":false,"is_send_user_notice":false,"send_email_config":{"mail_host":"hq.smtp.chinaunicom.cn","mail_from_account":"hqs-ioa-cusri@chinaunicom.cn","mail_from_account_dwp":""},"send_sms_config":{"api_server":"https://sms.tg.unicom.local/sms/message/send","sys_name":"","sys_token":""},"admin_list":["ludq1@chinaunicom.cn"],"runtime_env":"DEV","aes_mysql_key":"","rsa_private_key":"","sso_api":"https://sso.dev.tg.unicom.local/sso/v1","iam_api":"https://iam.dev.tg.unicom.local/iam/v2","configcenter_api":"https://configcenter.dev.tg.unicom.local/configcenter/v1","product_vpc_api":"https://vpc.console.dev.tg.unicom.local","product_clb_api":"https://vpc.console.dev.tg.unicom.local","product_cke_api":"https://cke.console.dev.tg.unicom.local","product_csm_api":"https://csm.console.dev.tg.unicom.local","product_ccr_api":"https://ccr.console.dev.tg.unicom.local","product_rds_api":"https://rds.console.dev.tg.unicom.local","product_redis_api":"https://redis.console.tg.unicom.local","product_kafka_api":"https://kafka.console.dev.tg.unicom.local","tianti_api":"https://tianti.dev.tg.unicom.local","ide_apiserver":"","ide_apiserver_secret":"disable","ide_image":""}
ENV_APP_CONFIG_INIT_STYLE=file
ENV_APP_CONFIG_FILE_PATH=C:\Users\supershll\Desktop\app_config.json
```
#!/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:
"""
#!/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 base64
from typing import Optional
from Crypto.Cipher import AES
class AesUtilsForMysql:
r"""
Mysql AES加密 utils类,不依赖任何其他应用基础类
"""
def __init__(self, key: str = None):
r"""
初始化
:param key
"""
self.key: str = key
if self.key:
self.real_aes_key: Optional[bytes] = self.gen_aes_key_by_mysql_key(self.key)
else:
self.real_aes_key: Optional[bytes] = None
def pkcs7padding_to_encrypt_content(self, to_encrypt_content: str) -> str:
r"""
明文使用PKCS7填充
最终调用AES加密方法时,传入的是一个byte数组,要求是16的整数倍,因此需要对明文进行处理
:param to_encrypt_content
:return
"""
bytes_length = len(bytes(to_encrypt_content, encoding='utf-8'))
# tips:utf-8编码时,英文占1个byte,而中文占3个byte
padding_times = 16 - bytes_length % 16
# tips:chr(padding)看与其它语言的约定,有的会使用'\0'
padding_text = chr(padding_times) * padding_times
return to_encrypt_content + padding_text
def pkcs7unpadding_decrypted_content(self, decrypted_content: str) -> str:
r"""
处理使用PKCS7填充过的数据
:param decrypted_content
:return
"""
length = len(decrypted_content)
unpadding = ord(decrypted_content[length - 1])
return decrypted_content[0:length - unpadding]
def gen_aes_key_by_mysql_key(self, key: str = None) -> bytes:
r"""
生成mysql函数中使用的key实际对应的aes_key
:param key
"""
real_key = key if key else self.key
final_key = bytearray(16)
for i, c in enumerate(real_key):
final_key[i % 16] ^= ord(real_key[i])
return bytes(final_key)
def encrypt(self, to_encrypt_content: str, key: str = None) -> str:
r"""
mysql AES加密
模式 AES.MODE_ECB
填充pkcs7
加密后使用 base64 编码
:param to_encrypt_content
:param key
:return
"""
real_aes_key = self.gen_aes_key_by_mysql_key(key) if key else self.real_aes_key
cipher = AES.new(real_aes_key, AES.MODE_ECB)
# 将内容编码
to_encrypt_content = self.pkcs7padding_to_encrypt_content(to_encrypt_content)
byte_content = to_encrypt_content.encode('utf-8')
byte_encrypted = cipher.encrypt(byte_content)
# base64 编码
byte_base64 = base64.b64encode(byte_encrypted)
return str(byte_base64, 'utf-8')
def decrypt(self, to_decrypt_content: str, key: str = None):
r"""
mysql AES解密
模式 AES.MODE_ECB
去填充pkcs7
解密前使用 base64 解码
:param to_decrypt_content
:param key
:return
"""
real_aes_key = self.gen_aes_key_by_mysql_key(key) if key else self.real_aes_key
cipher = AES.new(real_aes_key, AES.MODE_ECB)
# 先将内容解码
byte_base64 = to_decrypt_content.encode('utf-8')
byte_encrypted = base64.b64decode(byte_base64)
# 解码
result = cipher.decrypt(byte_encrypted)
result = str(result, 'utf-8')
# 去除填充内容
result = self.pkcs7unpadding_decrypted_content(result)
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:
"""
from .app_response import AppResponse
from .base_const import ConstResponseCode
from ..globalutility import Utility
class AppException(Exception):
"""
自定义的异常类, 表示可控的异常,不依赖任何其他应用基础类
"""
code: str = None
message: str = None
_status_code: int = None
def __init__(self, code: str = None, message: str = None, status_code: int = 200):
r"""
初始化异常
:param code
:param message
:param status_code
"""
self.code: str = code or ConstResponseCode.CODE_SYS_ERROR
self.message: str = message or ConstResponseCode.gen_msg_for_code(self.code)
self.status_code = status_code
self.my_response = AppResponse(code=self.code, message=self.message, status_code=status_code)
def __str__(self):
r"""
字符串表示, 使用json字符串表示
Returns:
"""
return str(self.my_response)
def __repr__(self):
r"""
字符串表示,使用 __str__ , 不会返回None,至少时 EMPTY字符串
Returns:
"""
return repr(self.my_response)
def gen_err(self) -> dict:
r"""
生成带有 code,message的标准dict
Returns:
"""
return self.my_response.gen_dict()
@property
def status_code(self) -> int:
r"""
status_code 属性
:return:
"""
if self._status_code is None or self._status_code < 1:
self._status_code = 200
return self._status_code
@status_code.setter
def status_code(self, status_code: int):
r"""
status_code 属性
:param status_code:
:return:
"""
self._status_code = status_code
class AppRuntimeException(Exception):
"""
自定义的异常类, 区分其他的异常
还用于返回给前端基础的异常信息,和在后端打印的详细异常信息
"""
message: str = None
detail: str = None
code: str = None
def __init__(self, message: str, detail: str = None, code: str = None):
r"""
初始化
:param message
:param detail
"""
self.message: str = message
self.detail: str = detail
self.code: str = code
def __str__(self):
r"""
字符串表示
Returns:
"""
return Utility.dict2jsonstr({"message": self.message, "detail": self.detail, "code": self.code})
def __repr__(self):
r"""
字符串表示
Returns:
"""
return self.__str__()
#!/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 .base_const import ConstResponseCode
from ..globalutility import Utility
class AppResponse:
r"""
标准返回数据的封装,包含 code,msg,data等key的标准dict的json表示,不依赖任何其他应用基础类
"""
code: str = None
message: str = None
data = None
other_data: dict = None
_status_code: int = None
def __init__(
self,
code: str = None, message: str = None, data=None, other_data: dict = None,
status_code: int = 200,
):
r"""
初始化,默认为OK的返回
:param code: 默认为 SysError
:param message: 默认为 系统错误
:param data:
:param other_data:
:param status_code:
"""
self.code: str = code or ConstResponseCode.CODE_OK
self.message: str = message or ConstResponseCode.gen_msg_for_code(self.code)
self.other_data = other_data if other_data is not None else dict()
self.data = data
self.status_code = status_code
@classmethod
def from_dict(
cls,
source_dict: dict,
key_for_msg: str = "message",
key_for_data: str = "data",
key_for_code: str = "code",
key_for_status_code: str = "status_code",
):
r"""
根据 source_dict(包含 code 和 nessage key的dict) 生成 AppResponse
:param source_dict:
:param key_for_code:
:param key_for_data:
:param key_for_msg:
:param key_for_status_code:
:return:
"""
if not source_dict:
raise RuntimeError("AppResponse解析dict对象出错:dict对象为空")
result: AppResponse = AppResponse()
key_for_code = key_for_code or "code"
tmp_code = source_dict.get(key_for_code)
if not tmp_code:
raise RuntimeError(Utility.join_str("AppResponse解析dict对象出错:dict对象的", key_for_code, "不合规"))
result.code = str(tmp_code)
key_for_msg = key_for_msg or "message"
tmp_msg = source_dict.get(key_for_msg) or ""
result.message = tmp_msg
key_for_data = key_for_data or "data"
tmp_data = source_dict.get(key_for_data)
result.data = tmp_data
key_for_status_code = key_for_status_code or "status_code"
tmp_data = source_dict.get(key_for_status_code)
result.status_code = tmp_data
for tmp_key, tmp_value in source_dict.items():
if tmp_key not in {key_for_code, key_for_msg, key_for_data, key_for_status_code}:
continue
result.other_data[tmp_key] = tmp_value
return result
def __str__(self):
r"""
字符串表示, 使用json字符串表示
Returns:
"""
return Utility.dict2jsonstr(self.gen_dict())
def __repr__(self):
r"""
字符串表示,使用 __str__ , 不会返回None,至少时 EMPTY字符串
Returns:
"""
return self.__str__()
def gen_dict(self) -> dict:
r"""
生成带有 code,message的标准dict
Returns:
"""
ret_dict = {"code": self.code, "message": self.message, "status_code": self.status_code}
if self.other_data:
for key, value in self.other_data.items():
ret_dict[key] = value
if self.data is not None:
ret_dict['data'] = self.data
return ret_dict
@property
def status_code(self) -> int:
r"""
status_code 属性
:return:
"""
if self._status_code is None or self._status_code < 1:
self._status_code = 200
return self._status_code
@status_code.setter
def status_code(self, status_code: int):
r"""
status_code 属性
:param status_code:
:return:
"""
self._status_code = status_code
def is_ok(self) -> bool:
r"""
验证code是否等于OK
"""
return self.code == ConstResponseCode.CODE_OK
#!/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:
"""
class ConstBaseApp(object):
COMMON_APP_CONFIG_DEFAULT_CONF_FILE_PATH = "/tmp/common_app_config.conf"
class ConstResponseCode(object):
CODE_OK: str = 'OK'
CODE_SYS_ERROR: str = 'SysError'
CODE_MISSING_PARAMETER: str = 'MissingParameter'
CODE_STATUS_ERROR: str = 'StatusError'
CODE_INVALID_ACTION: str = 'InvalidAction'
CODE_AUTH_FAILURE: str = 'AuthFailure'
CODE_UNAUTHORIZE_OPERATION: str = 'UnauthorizedOperation'
CODE_K8S_ERROR: str = 'K8sError'
CODE_MAP: dict = {
CODE_OK: "成功",
CODE_SYS_ERROR: "系统错误",
CODE_MISSING_PARAMETER: "参数错误",
CODE_STATUS_ERROR: "实例状态不允许当前的操作",
CODE_INVALID_ACTION: "请求的URI地址不存在",
CODE_AUTH_FAILURE: "登陆已超期",
CODE_UNAUTHORIZE_OPERATION: "API访问未授权",
CODE_K8S_ERROR: "K8S调用错误",
}
@classmethod
def gen_msg_for_code(cls, code: str) -> str:
return cls.CODE_MAP.get(code, code)
#!/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 typing import Optional
from pymysql.connections import Connection
from pymysql.cursors import DictCursor
from .app_exception import AppRuntimeException
from .common_app_config import CommonAppConfig
from ..globalutility import Utility
from ..my_stringutils import MyStringUtils
class QueryResultWithFoundRows:
result_list: Optional[list] = None
found_rows: Optional[int] = None
def __init__(self, **kwargs):
self.result_list = kwargs.get("result_list")
self.found_rows = kwargs.get("found_rows")
def __str__(self):
return Utility.dict2jsonstr(self.__dict__)
class DbBehaviourAfterExec:
r"""
数据库语句执行完成后的连接操作行为,不依赖任何其他应用基础类
默认 commit_after_exec 和 close_conn_after_exec 为 False
close_conn_when_exception 和 rollback_when_exception 为 True ,commit_when_exception 为 False
rollback_when_exception 的优先级比 commit_when_exception 高,
当 close_conn_when_exception 为True时,如果 rollback_when_exception 和 commit_when_exception 都为False,
则执行 rollback 操作
"""
commit_after_exec: bool = True
close_conn_after_exec: bool = True
close_conn_when_exception: bool = True
rollback_when_exception: bool = True
commit_when_exception: bool = False
def __init__(self, commit_after_exec: bool = True, close_conn_after_exec: bool = True,
close_conn_when_exception: bool = True, rollback_when_exception: bool = True,
commit_when_exception: bool = False):
r"""
初始化数据库行为
:param commit_after_exec:
:param close_conn_after_exec:
:param close_conn_when_exception:
:param rollback_when_exception:
:param commit_when_exception:
"""
self.commit_after_exec = commit_after_exec
self.close_conn_after_exec = close_conn_after_exec
self.close_conn_when_exception = close_conn_when_exception
self.rollback_when_exception = rollback_when_exception
self.commit_when_exception = commit_when_exception
@classmethod
def gen_no_commit_close_behaviour(cls):
r"""
生成一个不commit,不close,异常时rollback、close的数据库行为
:return:
"""
return DbBehaviourAfterExec(
commit_after_exec=False,
close_conn_after_exec=False,
close_conn_when_exception=True,
rollback_when_exception=True,
commit_when_exception=False)
class BaseDbService:
r"""
基础 dbService类,定义一些基础函数,不依赖任何其他应用基础类
"""
def __init__(self, logger: logging.Logger = None):
self.logger = logger or CommonAppConfig().common_logger
def join_str(self, *to_join_list: str, separator_str: str = '', wrapper_str: str = None,
ignore_none: bool = False) -> str:
r"""
连接 list, 当值为 None 时当做 null字符串 添加,但是不追加 wrapper_str
:param to_join_list
:param separator_str 默认使用空字符串
:param wrapper_str
:param ignore_none 默认为True
:return
"""
return Utility.list_join_to_str(to_join_list, separator_str=separator_str, wrapper_str=wrapper_str,
ignore_none=ignore_none)
def do_filter_mysql_param_with_single_quotes(self, to_filter_param: str, append_fix: str = "'") -> str:
r"""
mysql防注入使用,用于过滤(替换)拼接字符串中的参数,当拼接`key`='value'时,对value中的特殊字符进行替换
:param to_filter_param
:param append_fix 默认为单引号
:return
"""
return Utility.do_filter_mysql_param(to_filter_param=to_filter_param, append_fix=append_fix)
def do_filter_mysql_param_with_single_quotes_for_fuzzy_search(self, to_filter_param: str,
append_fix: str = "'") -> str:
r"""
mysql防注入使用,用于过滤(替换)拼接字符串中的参数,当拼接`key`='value'时,对value中的特殊字符进行替换
:param to_filter_param
:param append_fix 默认为单引号
:return
"""
return Utility.do_filter_mysql_param_with_single_quotes_for_fuzzy_search(to_filter_param=to_filter_param,
append_fix=append_fix)
def do_filter_mysql_param_list_with_single_quotes(
self,
to_filter_param_list: list,
append_fix: str = "'"
) -> Optional[list]:
r"""
mysql防注入使用,用于过滤(替换)拼接字符串中的参数,当拼接`key`='value'时,对value中的特殊字符进行替换
Utility.do_filter_mysql_param_list_with_single_quotes函数有bug,所以自己实现一下
:param to_filter_param_list
:param append_fix 默认为单引号
:return
"""
if to_filter_param_list is None:
return None
return [self.do_filter_mysql_param_with_single_quotes(tmp_key, append_fix=append_fix) for tmp_key in
to_filter_param_list]
def gen_default_db_behaviour_after_exec(self):
r"""
生成默认的 db_behaviour_after_exec
默认行为为 正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:return:
"""
return DbBehaviourAfterExec(
commit_after_exec=True, close_conn_after_exec=True,
close_conn_when_exception=True,
rollback_when_exception=True,
commit_when_exception=False)
def commit(self, conn: Connection, close_conn_after_exec: bool = True,
db_behaviour_after_exec: DbBehaviourAfterExec = None) -> bool:
r"""
执行commit操作
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param close_conn_after_exec
:param db_behaviour_after_exec
:return
"""
if conn is None:
return True
conn.commit()
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
if close_conn_after_exec or (db_behaviour_after_exec and db_behaviour_after_exec.close_conn_after_exec):
conn.close()
return True
def rollback(self, conn: Connection, close_conn_after_exec: bool = True,
db_behaviour_after_exec: DbBehaviourAfterExec = None) -> bool:
r"""
执行rollback操作
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param close_conn_after_exec
:param db_behaviour_after_exec
:return
"""
if conn is None:
return True
conn.rollback()
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
if close_conn_after_exec or (db_behaviour_after_exec and db_behaviour_after_exec.close_conn_after_exec):
conn.close()
return True
def close_conn(self, conn: Connection):
r"""
关闭连接
:param conn
"""
if conn is None:
return
conn.close()
def do_after_exec(self, conn: Connection, result, encountered_exception,
db_behaviour_after_exec: DbBehaviourAfterExec):
r"""
数据库执行完毕后的行为封装
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param result
:param encountered_exception
:param db_behaviour_after_exec
:return:
"""
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
if not encountered_exception:
return self.do_after_exec_without_exception(conn, result, db_behaviour_after_exec)
else:
return self.do_after_exec_with_exception(conn, encountered_exception, db_behaviour_after_exec)
def do_after_exec_without_exception(self, conn: Connection, result, db_behaviour_after_exec: DbBehaviourAfterExec):
r"""
数据库执行完毕后的行为封装
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param result
:param db_behaviour_after_exec
:return:
"""
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
close_conn_after_exec = True if db_behaviour_after_exec.close_conn_after_exec else False
commit_after_exec = True if db_behaviour_after_exec and db_behaviour_after_exec.commit_after_exec else False
if commit_after_exec and conn is not None:
conn.commit()
if close_conn_after_exec and conn is not None:
conn.close()
return result
def do_after_exec_with_exception(self, conn: Connection, encountered_exception,
db_behaviour_after_exec: DbBehaviourAfterExec):
r"""
数据库执行完毕后的行为封装
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param encountered_exception
:param db_behaviour_after_exec
:return:
"""
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
close_conn_when_exception = True if db_behaviour_after_exec.close_conn_when_exception else False
rollback_when_exception = True if db_behaviour_after_exec.rollback_when_exception else False
commit_when_exception = True if db_behaviour_after_exec.commit_when_exception else False
if close_conn_when_exception and not rollback_when_exception and not commit_when_exception:
# 当异常时关闭连接 但是 rollback_when_exception 和 commit_when_exception 都为False,则默认使用 rollback
rollback_when_exception = True
if rollback_when_exception and conn is not None:
conn.rollback()
elif commit_when_exception and conn is not None:
conn.commit()
if close_conn_when_exception and conn is not None:
conn.close()
raise encountered_exception
def querydb(
self, conn: Connection, query_str: str, factory_method=None,
db_behaviour_after_exec: DbBehaviourAfterExec = None
) -> list:
r"""
查询数据库
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param query_str
:param factory_method
:param db_behaviour_after_exec
:return
"""
if conn is None:
raise AppRuntimeException(message="未提供conn", detail="执行数据库操作时,传入的conn为None")
encountered_exception: Optional[BaseException] = None
result: Optional[list] = None
try:
self.logger.debug(f"执行sql语句:{query_str}")
with conn.cursor(cursor=DictCursor) as cursor:
try:
cursor.execute(query_str)
result = cursor.fetchall() or list()
finally:
cursor.close()
self.logger.debug(f"执行sql语句:{query_str} 返回结果: {result}")
except BaseException as e:
encountered_exception = e
self.logger.debug(f"执行sql语句:{query_str} 发生异常: {e}")
if not encountered_exception and factory_method is not None and result:
try:
result = [factory_method(tmpDict) for tmpDict in result]
except BaseException as e:
encountered_exception = e
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
return self.do_after_exec(conn, result, encountered_exception, db_behaviour_after_exec)
def querycount_db(self, conn: Connection, query_str: str,
db_behaviour_after_exec: DbBehaviourAfterExec = None) -> int:
r"""
查询数据库,执行count数据库查询,直接得到返回的整数
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param query_str
:param db_behaviour_after_exec
:return
"""
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
ret_list = self.querydb(conn, query_str, db_behaviour_after_exec=db_behaviour_after_exec)
if not ret_list:
raise ValueError('数据库查询语句返回结果集为空')
return int(list(ret_list[0].values())[0])
def querydb_with_single_column_and_line(self, conn: Connection, query_str: str,
db_behaviour_after_exec: DbBehaviourAfterExec = None) -> Optional[str]:
r"""
查询数据库,执行返回单列单行的数据,并获取字符串,查询的数据不存在时,返回None
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param query_str
:param db_behaviour_after_exec
:return
"""
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
ret_list = self.querydb(conn, query_str, db_behaviour_after_exec=db_behaviour_after_exec)
if not ret_list:
return None
return MyStringUtils.to_str(list(ret_list[0].values())[0])
def querydb_with_sql_calc_found_rows(
self, conn: Connection, query_str: str, factory_method=None,
db_behaviour_after_exec: DbBehaviourAfterExec = None
) -> QueryResultWithFoundRows:
r"""
查询数据库,为带有 SQL_CALC_FOUND_ROWS 的sql语句返回 FOUND_ROWS()
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param query_str
:param factory_method
:param db_behaviour_after_exec
:return
"""
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
tmp_db_behaviour_after_exec = DbBehaviourAfterExec.gen_no_commit_close_behaviour()
tmp_db_behaviour_after_exec.close_conn_when_exception = db_behaviour_after_exec.close_conn_when_exception
tmp_db_behaviour_after_exec.commit_when_exception = db_behaviour_after_exec.commit_when_exception
tmp_db_behaviour_after_exec.rollback_when_exception = db_behaviour_after_exec.rollback_when_exception
ret_list = self.querydb(conn, query_str, factory_method=factory_method,
db_behaviour_after_exec=tmp_db_behaviour_after_exec)
found_rows = self.querydb_with_single_column_and_line(
conn=conn,
query_str="select FOUND_ROWS() as found_rows",
db_behaviour_after_exec=db_behaviour_after_exec
)
found_rows = int(found_rows)
return QueryResultWithFoundRows(result_list=ret_list, found_rows=found_rows)
def operate_db(self, conn: Connection, *operate_sql_list: str, sql_list: list = None,
db_behaviour_after_exec: DbBehaviourAfterExec = None) -> int:
r"""
操作数据库,返回受影响的行数, 可以有两种提供操作语句的方式,或者直接执行多条语句,或者使用 sql_list 提供操作语句列表
当 db_behaviour_after_exec为None时,使用默认的 db_behaviour_after_exec,即正常操作后commit、关闭数据库, 异常时rollback、关闭数据库
:param conn
:param operate_sql_list
:param sql_list
:param db_behaviour_after_exec
:return
"""
if conn is None:
raise AppRuntimeException(message="未提供conn", detail="执行数据库操作时,传入的conn为None")
encountered_exception: Optional[BaseException] = None
result: int = 0
try:
# 先生成列表
real_sql_list = []
if operate_sql_list:
real_sql_list += operate_sql_list
elif sql_list:
real_sql_list += sql_list
else:
raise ValueError('未提供sql语句')
with conn.cursor(cursor=DictCursor) as cursor:
try:
for tmp_sql_str in real_sql_list:
self.logger.debug(f"执行sql语句:{tmp_sql_str}")
result += cursor.execute(tmp_sql_str)
self.logger.debug(f"执行sql语句:{tmp_sql_str} 返回结果: {result}")
finally:
cursor.close()
except BaseException as e:
encountered_exception = e
if db_behaviour_after_exec is None:
db_behaviour_after_exec = self.gen_default_db_behaviour_after_exec()
return self.do_after_exec(conn, result, encountered_exception, db_behaviour_after_exec)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#!/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 flask import make_response, Response
from .app_exception import AppRuntimeException, AppException
from .app_response import AppResponse
from .common_base_webservice import CommonBaseWebService
from .utils_k8s.k8s_api_object_utils import OperationResultException
class CommonBaseWebServiceForStream(CommonBaseWebService):
"""
基础微服务-基类(文件流类型的微服务基础),依赖一些其他应用基础类
"""
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, e):
r"""
处理 e 时, 根据 AppException 返回的response
"""
err_msg = f"{e.__class__.__name__}:{e}"
if isinstance(e, AppRuntimeException):
self.logger.error(e)
err_msg = e.message
if isinstance(e, OperationResultException):
self.logger.error(e)
err_msg = e.failed_reason
return make_response(f"服务发生异常:{err_msg}", 500)
#!/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 .app_exception import AppRuntimeException
from ..globalutility import Utility
class LoginedUserInfo:
r"""
登陆用户的信息POJO类,不依赖任何其他应用基础类
"""
def __init__(self):
self.account_id = None
self.account_name = None
self.user_id = None
self.user_name = None
self.mobile = None
self.email = None
self.is_root = None
self.session_uuid = None
r"""
目前获取不到session_uuid
"""
self.access_token = None
r"""
继续调用其他API时使用的access_token
"""
def is_login(self):
return not (self.user_id is None or self.user_id == '')
def __str__(self):
return Utility.dict2jsonstr(self.__dict__)
@classmethod
def from_dict(cls, user_info_dict, access_token: str = None):
r"""
根据sso_api返回的用户信息dict转换成LoginedUserInfo
:param user_info_dict
:param access_token:
"""
return cls().fill_attr_by_dict(user_info_dict, access_token)
def fill_attr_by_dict(self, user_info_dict: dict, access_token: str = None):
r"""
根据sso_api返回的用户信息dict填充自身属性,返回自身
:param user_info_dict:
:param access_token:
:return:
"""
tmp_value = user_info_dict.get('accountID')
if not tmp_value:
raise AppRuntimeException(
message=f"通过字典转换成LoginedUserInfo对象失败",
detail=f"字典内容不包括accountID,user_info_dict={Utility.dict2jsonstr(user_info_dict)}"
)
self.account_id = tmp_value
tmp_value = user_info_dict.get('userID')
if not tmp_value:
raise AppRuntimeException(
message=f"通过字典转换成LoginedUserInfo对象失败",
detail=f"字典内容不包括userID,user_info_dict={Utility.dict2jsonstr(user_info_dict)}"
)
self.user_id = tmp_value
tmp_value = user_info_dict.get('userName')
if not tmp_value:
raise AppRuntimeException(
message=f"通过字典转换成LoginedUserInfo对象失败",
detail=f"字典内容不包括userName,user_info_dict={Utility.dict2jsonstr(user_info_dict)}"
)
self.user_name = tmp_value
self.account_name = user_info_dict.get('accountName')
self.mobile = user_info_dict.get('mobile')
self.email = user_info_dict.get('email')
self.is_root = user_info_dict.get('isRoot')
self.access_token = access_token
return self
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#!/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:
"""
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