database-smartools 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- database-smartools/__init__.py +18 -0
- database-smartools/api/__init__.py +18 -0
- database-smartools/api/etl.py +160 -0
- database-smartools/database/__init__.py +18 -0
- database-smartools/database/dameng_extend.py +65 -0
- database-smartools/database/db_handler.py +676 -0
- database-smartools/database/mysql_extend.py +396 -0
- database-smartools/database/oceanbase_extend.py +55 -0
- database-smartools/database/oracle_extend.py +27 -0
- database-smartools/database/postgre_extend.py +396 -0
- database-smartools/main.py +86 -0
- database-smartools/module/__init__.py +18 -0
- database-smartools/module/function.py +115 -0
- database-smartools/utils/__init__.py +18 -0
- database-smartools/utils/config.py +56 -0
- database-smartools/utils/debug.py +72 -0
- database-smartools/utils/file.py +42 -0
- database-smartools/utils/http.py +74 -0
- database-smartools/utils/logger.py +237 -0
- database-smartools/utils/output.py +31 -0
- database-smartools/utils/texter.py +186 -0
- database-smartools/utils/timer.py +52 -0
- database-tools/__init__.py +18 -0
- database-tools/api/__init__.py +18 -0
- database-tools/api/etl.py +160 -0
- database-tools/database/__init__.py +18 -0
- database-tools/database/dameng_extend.py +65 -0
- database-tools/database/db_handler.py +676 -0
- database-tools/database/mysql_extend.py +396 -0
- database-tools/database/oceanbase_extend.py +55 -0
- database-tools/database/oracle_extend.py +27 -0
- database-tools/database/postgre_extend.py +396 -0
- database-tools/main.py +86 -0
- database-tools/module/__init__.py +18 -0
- database-tools/module/function.py +115 -0
- database-tools/utils/__init__.py +18 -0
- database-tools/utils/config.py +56 -0
- database-tools/utils/debug.py +72 -0
- database-tools/utils/file.py +42 -0
- database-tools/utils/http.py +74 -0
- database-tools/utils/logger.py +237 -0
- database-tools/utils/output.py +31 -0
- database-tools/utils/texter.py +186 -0
- database-tools/utils/timer.py +52 -0
- database_smartools-1.0.0.dist-info/METADATA +84 -0
- database_smartools-1.0.0.dist-info/RECORD +50 -0
- database_smartools-1.0.0.dist-info/WHEEL +5 -0
- database_smartools-1.0.0.dist-info/licenses/LICENSE +13 -0
- database_smartools-1.0.0.dist-info/top_level.txt +1 -0
- model/__init__.py +12 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
@项目名称 : yhfin-data-agent
|
4
|
+
@文件名称 : debug.py
|
5
|
+
@创建人 : zhongbinjie
|
6
|
+
@创建时间 : 2025/9/9 15:24
|
7
|
+
@文件说明 :
|
8
|
+
@企业名称 : 深圳市赢和信息技术有限公司
|
9
|
+
@Copyright:2025-2035, 深圳市赢和信息技术有限公司. All rights Reserved.
|
10
|
+
"""
|
11
|
+
from utils import logger, config, file, http
|
12
|
+
import pandas as pd
|
13
|
+
from utils.timer import get_time, get_timediff
|
14
|
+
import inspect # 添加inspect模块导入
|
15
|
+
import os
|
16
|
+
|
17
|
+
def get_caller_file_path(level=1):
|
18
|
+
"""
|
19
|
+
获取调用当前函数的文件路径
|
20
|
+
:param level: 调用栈层级,默认为1(直接调用者)
|
21
|
+
:return: 调用文件的绝对路径
|
22
|
+
"""
|
23
|
+
try:
|
24
|
+
# 获取调用栈信息
|
25
|
+
stack = inspect.stack()
|
26
|
+
if len(stack) > level + 1:
|
27
|
+
# 获取调用者的帧信息
|
28
|
+
caller_frame = stack[level + 1]
|
29
|
+
# 获取调用者文件路径并转换为绝对路径
|
30
|
+
return file.get_file_abspath(caller_frame.filename)
|
31
|
+
return None
|
32
|
+
except (IndexError, AttributeError):
|
33
|
+
return None
|
34
|
+
|
35
|
+
def debug_script(obj, params, env="dev"):
|
36
|
+
func_file = get_caller_file_path()
|
37
|
+
workspace = file.get_file_dir(file.get_file_abspath(func_file))
|
38
|
+
cf = config.Config(os.path.join(workspace, "conf.ini"), "UTF-8", env)
|
39
|
+
config.add_conf("env", env)
|
40
|
+
config.add_conf("root", workspace)
|
41
|
+
print(config.MAP)
|
42
|
+
logger.Logger()
|
43
|
+
pd.set_option('display.max_colwidth', None)
|
44
|
+
pd.set_option('display.max_columns', None)
|
45
|
+
pd.set_option('display.expand_frame_repr', False)
|
46
|
+
pd.set_option('display.max_rows', None)
|
47
|
+
|
48
|
+
# params = {"db_key": "GB_YFDW_DW", "p_jgdm": "70380000", "p_file_date": "20241231","ind_type": "D","p_data_guid": "test_cqb"}
|
49
|
+
logger.info("执行接口开始")
|
50
|
+
params = {"db_key": "GB_YFDW_DW", "p_jgdm": "70380000", "p_file_date": "20240101",
|
51
|
+
"ind_type": "D", "p_data_guid": "test_cqb", "p_fund_type": "08", "p_fund_category": "01",
|
52
|
+
"p_item_table": "", "p_fdate": "20240101",
|
53
|
+
}
|
54
|
+
|
55
|
+
start_time = get_time()
|
56
|
+
|
57
|
+
map = obj(params)
|
58
|
+
# print(map)
|
59
|
+
end_time = get_time()
|
60
|
+
logger.info(f"用时:{get_timediff(end_time, start_time)}")
|
61
|
+
|
62
|
+
# 打印性能分析结果
|
63
|
+
# profiler.print_stats()
|
64
|
+
|
65
|
+
logger.info("执行接口结束")
|
66
|
+
|
67
|
+
def test():
|
68
|
+
print()
|
69
|
+
|
70
|
+
|
71
|
+
if __name__ == '__main__':
|
72
|
+
None
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
@项目名称 : python-main
|
4
|
+
@文件名称 : file.py
|
5
|
+
@创建人 : zhongbinjie
|
6
|
+
@创建时间 : 2025/6/7 19:06
|
7
|
+
@文件说明 :
|
8
|
+
@企业名称 : 深圳市赢和信息技术有限公司
|
9
|
+
@Copyright:2025-2035, 深圳市赢和信息技术有限公司. All rights Reserved.
|
10
|
+
"""
|
11
|
+
|
12
|
+
import os
|
13
|
+
import inspect
|
14
|
+
|
15
|
+
def get_file_abspath(file):
|
16
|
+
# 获取当前脚本的绝对路径
|
17
|
+
file_path = os.path.abspath(file)
|
18
|
+
return file_path
|
19
|
+
|
20
|
+
def get_file_dir(file_path):
|
21
|
+
# 获取脚本所在的目录
|
22
|
+
file_dir = os.path.dirname(file_path)
|
23
|
+
return file_dir
|
24
|
+
|
25
|
+
def search_script(path, filename):
|
26
|
+
for root, dirs, files in os.walk(path):
|
27
|
+
if filename in files:
|
28
|
+
return root, filename
|
29
|
+
return None, None
|
30
|
+
def is_file(obj):
|
31
|
+
# 获取脚本所在的目录
|
32
|
+
return obj.is_file()
|
33
|
+
|
34
|
+
def is_dir(obj):
|
35
|
+
# 获取脚本所在的目录
|
36
|
+
return obj.is_dir()
|
37
|
+
|
38
|
+
def test():
|
39
|
+
print(search_script('D:\workspace\python-main\\functions', 'py_test2.py'))
|
40
|
+
|
41
|
+
if __name__ == '__main__':
|
42
|
+
None
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
@项目名称 : python-main
|
4
|
+
@文件名称 : http.py
|
5
|
+
@创建人 : zhongbinjie
|
6
|
+
@创建时间 : 2025/6/7 19:06
|
7
|
+
@文件说明 :
|
8
|
+
@企业名称 : 深圳市赢和信息技术有限公司
|
9
|
+
@Copyright:2025-2035, 深圳市赢和信息技术有限公司. All rights Reserved.
|
10
|
+
"""
|
11
|
+
|
12
|
+
# 200:请求成功的状态码
|
13
|
+
# 404:页面找不到
|
14
|
+
# 422:请求体中数据有问题(格式不正确,名字匹配不对)
|
15
|
+
# 405:请求的方式不匹配(如路径是get形式,但是函数上面写的是其他的请求类型)
|
16
|
+
# 500:后台服务器程序出错
|
17
|
+
import socket
|
18
|
+
import json
|
19
|
+
|
20
|
+
class ResponseUtil:
|
21
|
+
@staticmethod
|
22
|
+
def success(data=None, message="执行成功", code=200, **kwargs):
|
23
|
+
"""成功响应"""
|
24
|
+
response = {
|
25
|
+
"code": code,
|
26
|
+
"message": message,
|
27
|
+
"data": data or {},
|
28
|
+
**kwargs # 支持扩展字段(如 timestamp、pagination)
|
29
|
+
}
|
30
|
+
if 'json' in kwargs:
|
31
|
+
response = json.dumps(response, ensure_ascii=False)
|
32
|
+
return response
|
33
|
+
|
34
|
+
@staticmethod
|
35
|
+
def error(code=500, message="失败", data=None, errors=None, **kwargs):
|
36
|
+
"""错误响应"""
|
37
|
+
response = {
|
38
|
+
"code": code,
|
39
|
+
"message": message,
|
40
|
+
"data": data or {},
|
41
|
+
"errors": errors or {},
|
42
|
+
**kwargs # 支持扩展字段
|
43
|
+
}
|
44
|
+
if 'json' in kwargs:
|
45
|
+
response = json.dumps(response, ensure_ascii=False)
|
46
|
+
return response
|
47
|
+
|
48
|
+
@staticmethod
|
49
|
+
def empty(message="无数据", code=400):
|
50
|
+
"""空数据响应"""
|
51
|
+
return ResponseUtil.success(data={}, message=message, code=code)
|
52
|
+
|
53
|
+
def get_local_ip():
|
54
|
+
# 创建一个UDP socket
|
55
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
56
|
+
try:
|
57
|
+
# 连接到一个公共的外部IP地址(不会实际发送数据)
|
58
|
+
s.connect(('8.8.8.8', 80))
|
59
|
+
# 获取连接的本地地址
|
60
|
+
ip = s.getsockname()[0]
|
61
|
+
except Exception as e:
|
62
|
+
print(f"Error occurred: {e}")
|
63
|
+
ip = '127.0.0.1'
|
64
|
+
finally:
|
65
|
+
s.close()
|
66
|
+
|
67
|
+
return ip
|
68
|
+
|
69
|
+
def test():
|
70
|
+
print()
|
71
|
+
|
72
|
+
|
73
|
+
if __name__ == '__main__':
|
74
|
+
None
|
@@ -0,0 +1,237 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
@项目名称 : python-main
|
4
|
+
@文件名称 : logger.py
|
5
|
+
@创建人 : zhongbinjie
|
6
|
+
@创建时间 : 2025/6/7 19:06
|
7
|
+
@文件说明 :
|
8
|
+
@企业名称 : 深圳市赢和信息技术有限公司
|
9
|
+
@Copyright:2025-2035, 深圳市赢和信息技术有限公司. All rights Reserved.
|
10
|
+
"""
|
11
|
+
|
12
|
+
import json
|
13
|
+
import logging
|
14
|
+
import logging.handlers
|
15
|
+
import sys
|
16
|
+
import os
|
17
|
+
from utils import config, timer, texter
|
18
|
+
import time
|
19
|
+
from datetime import datetime, timedelta
|
20
|
+
"""
|
21
|
+
日志等级:
|
22
|
+
FATAL:致命错误
|
23
|
+
CRITICAL:特别糟糕的事情,如内存耗尽、磁盘空间为空,一般很少使用
|
24
|
+
ERROR:发生错误时,如IO操作失败或者连接问题
|
25
|
+
WARNING:发生很重要的事件,但是并不是错误时,如用户登录密码错误
|
26
|
+
INFO:处理请求或者状态变化等日常事务
|
27
|
+
DEBUG:调试过程中使用DEBUG等级,如算法中每个循环的中间状态
|
28
|
+
|
29
|
+
日志参数:
|
30
|
+
%(levelno)s:打印日志级别的数值
|
31
|
+
%(levelname)s:打印日志级别的名称
|
32
|
+
%(pathname)s:打印当前执行程序的路径,其实就是sys.argv[0]
|
33
|
+
%(filename)s:打印当前执行程序名
|
34
|
+
%(funcName)s:打印日志的当前函数
|
35
|
+
%(lineno)d:打印日志的当前行号
|
36
|
+
%(asctime)s:打印日志的时间
|
37
|
+
%(thread)d:打印线程ID
|
38
|
+
%(threadName)s:打印线程名称
|
39
|
+
%(process)d:打印进程ID
|
40
|
+
%(message)s:打印日志信息
|
41
|
+
"""
|
42
|
+
# 单例装饰器
|
43
|
+
def singleton(cls):
|
44
|
+
instances = {}
|
45
|
+
def get_instance(*args, **kwargs):
|
46
|
+
if cls not in instances:
|
47
|
+
instances[cls] = cls(*args, **kwargs)
|
48
|
+
return instances[cls]
|
49
|
+
return get_instance
|
50
|
+
|
51
|
+
LOGGER = None
|
52
|
+
CONSOLE_HANDLER = None
|
53
|
+
FILE_HANDLER = None
|
54
|
+
LOG_NAME = ""
|
55
|
+
|
56
|
+
class ModuleFilter(logging.Filter):
|
57
|
+
def filter(self, record):
|
58
|
+
# 仅记录非 INFO 和 WARNING 级别
|
59
|
+
return record.levelno not in (logging.INFO, logging.WARNING)
|
60
|
+
|
61
|
+
# 使用单例装饰器
|
62
|
+
@singleton
|
63
|
+
class Logger:
|
64
|
+
def __init__(self):
|
65
|
+
print("初始化日志信息")
|
66
|
+
self._init_logger()
|
67
|
+
|
68
|
+
def _init_logger(self, name=__name__):
|
69
|
+
log_params = {
|
70
|
+
"root_path": config.MAP['root'],
|
71
|
+
"log_path": config.MAP['log_path'] if 'log_path' in config.MAP else 'logs',
|
72
|
+
"log_filename": f"{config.MAP['log_filename']}_{timer.get_curdatetime('%Y%m%d')}.log" if 'log_filename' in config.MAP else f"log_{timer.get_curdatetime('%Y%m%d')}.log",
|
73
|
+
"log_level": config.MAP['log_level'] if 'log_level' in config.MAP else 'INFO',
|
74
|
+
"log_mode": config.MAP['log_mode'] if 'log_mode' in config.MAP else 'a'
|
75
|
+
}
|
76
|
+
global LOGGER # logger对象
|
77
|
+
global CONSOLE_HANDLER # 控制台处理器
|
78
|
+
global FILE_HANDLER # 文件处理器
|
79
|
+
if not LOGGER:
|
80
|
+
LOGGER = logging.getLogger(name)
|
81
|
+
|
82
|
+
if LOGGER.hasHandlers():
|
83
|
+
for handler in LOGGER.handlers[:]:
|
84
|
+
LOGGER.removeHandler(handler)
|
85
|
+
|
86
|
+
self._set_log_level(log_params)
|
87
|
+
|
88
|
+
root_path = log_params['root_path']
|
89
|
+
log_path = log_params['log_path']
|
90
|
+
log_abspath = os.path.join(root_path, log_path)
|
91
|
+
# 检查日志目录是否存在
|
92
|
+
if not os.path.exists(log_abspath):
|
93
|
+
os.mkdir(log_abspath)
|
94
|
+
log_filename = log_params['log_filename']
|
95
|
+
global LOG_NAME # log文件
|
96
|
+
LOG_NAME = os.path.join(log_abspath, log_filename)
|
97
|
+
# 创建处理器 handlers
|
98
|
+
# 日志文件大小超过 50MB 时进行轮转,最多保留 10 个备份文件
|
99
|
+
log_max_size = config.MAP['log_max_size'] if 'log_max_size' in config.MAP else '50MB'
|
100
|
+
FILE_HANDLER = logging.handlers.RotatingFileHandler(
|
101
|
+
LOG_NAME,
|
102
|
+
encoding='UTF-8',
|
103
|
+
maxBytes=texter.convert_to_bytes(log_max_size),
|
104
|
+
backupCount=1000 # 一个极大值,近似无上限
|
105
|
+
)
|
106
|
+
FILE_HANDLER.setLevel(logging.DEBUG)
|
107
|
+
CONSOLE_HANDLER = logging.StreamHandler(stream=sys.stdout) # 输出到控制台
|
108
|
+
CONSOLE_HANDLER.setLevel(logging.DEBUG)
|
109
|
+
# 创建格式器
|
110
|
+
file_format = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s', '%Y%m%d-%H:%M:%S')
|
111
|
+
console_format = logging.Formatter(
|
112
|
+
'[%(levelname)s]' + ' ' * (6 - len(str(CONSOLE_HANDLER.level))) + '%(message)s')
|
113
|
+
# 将格式器添加到处理器中
|
114
|
+
FILE_HANDLER.setFormatter(file_format)
|
115
|
+
CONSOLE_HANDLER.setFormatter(console_format)
|
116
|
+
# 添加filter
|
117
|
+
CONSOLE_HANDLER.addFilter(ModuleFilter())
|
118
|
+
LOGGER.addHandler(CONSOLE_HANDLER)
|
119
|
+
# 将处理器添加到logger
|
120
|
+
LOGGER.addHandler(FILE_HANDLER)
|
121
|
+
# 删除七天前的日志文件
|
122
|
+
self._delete_old_logs(log_abspath)
|
123
|
+
|
124
|
+
def _delete_old_logs(self, log_dir):
|
125
|
+
save_days = int(config.MAP['log_save_days'])
|
126
|
+
save_days_ago = datetime.now() - timedelta(days=save_days)
|
127
|
+
for root, dirs, files in os.walk(log_dir):
|
128
|
+
for file in files:
|
129
|
+
file_path = os.path.join(root, file)
|
130
|
+
try:
|
131
|
+
file_mtime = datetime.fromtimestamp(os.path.getmtime(file_path))
|
132
|
+
if file_mtime < save_days_ago:
|
133
|
+
os.remove(file_path)
|
134
|
+
print(f"删除旧日志文件: {file_path}")
|
135
|
+
except Exception as e:
|
136
|
+
print(f"删除文件 {file_path} 时出错: {e}")
|
137
|
+
|
138
|
+
def _set_log_level(self, log_params):
|
139
|
+
LOGGER.setLevel(logging.INFO)
|
140
|
+
if log_params['log_level'] == 'FATAL':
|
141
|
+
LOGGER.setLevel(logging.FATAL)
|
142
|
+
elif log_params['log_level'] == 'CRITICAL':
|
143
|
+
LOGGER.setLevel(logging.CRITICAL)
|
144
|
+
elif log_params['log_level'] == 'ERROR':
|
145
|
+
LOGGER.setLevel(logging.ERROR)
|
146
|
+
elif log_params['log_level'] == 'WARNING':
|
147
|
+
LOGGER.setLevel(logging.WARNING)
|
148
|
+
elif log_params['log_level'] == 'INFO':
|
149
|
+
LOGGER.setLevel(logging.INFO)
|
150
|
+
elif log_params['log_level'] == 'DEBUG':
|
151
|
+
LOGGER.setLevel(logging.DEBUG)
|
152
|
+
|
153
|
+
def _check_and_rotate_log(self, refresh=False):
|
154
|
+
current_date = timer.get_curdatetime('%Y%m%d')
|
155
|
+
expected_log_filename = f"{config.MAP['log_filename']}_{current_date}.log" if 'log_filename' in config.MAP else f"log_{current_date}.log"
|
156
|
+
global FILE_HANDLER
|
157
|
+
global LOG_NAME
|
158
|
+
if expected_log_filename not in LOG_NAME or refresh:
|
159
|
+
if refresh: print("重置日志信息")
|
160
|
+
# 移除旧的文件处理器
|
161
|
+
LOGGER.removeHandler(FILE_HANDLER)
|
162
|
+
FILE_HANDLER.close()
|
163
|
+
|
164
|
+
log_params = {
|
165
|
+
"root_path": config.MAP['root'],
|
166
|
+
"log_path": config.MAP['log_path'] if 'log_path' in config.MAP else 'logs',
|
167
|
+
"log_filename": expected_log_filename,
|
168
|
+
"log_level": config.MAP['log_level'] if 'log_level' in config.MAP else 'INFO',
|
169
|
+
"log_mode": config.MAP['log_mode'] if 'log_mode' in config.MAP else 'a'
|
170
|
+
}
|
171
|
+
|
172
|
+
root_path = log_params['root_path']
|
173
|
+
log_path = log_params['log_path']
|
174
|
+
log_abspath = os.path.join(root_path, log_path)
|
175
|
+
if not os.path.exists(log_abspath):
|
176
|
+
os.mkdir(log_abspath)
|
177
|
+
LOG_NAME = os.path.join(log_abspath, expected_log_filename)
|
178
|
+
|
179
|
+
# 创建新的文件处理器
|
180
|
+
log_max_size = config.MAP['log_max_size'] if 'log_max_size' in config.MAP else '50MB'
|
181
|
+
FILE_HANDLER = logging.handlers.RotatingFileHandler(
|
182
|
+
LOG_NAME,
|
183
|
+
encoding='UTF-8',
|
184
|
+
maxBytes=texter.convert_to_bytes(log_max_size),
|
185
|
+
backupCount=1000 # 一个极大值,近似无上限
|
186
|
+
)
|
187
|
+
FILE_HANDLER.setLevel(logging.DEBUG)
|
188
|
+
file_format = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s', '%Y%m%d-%H:%M:%S')
|
189
|
+
FILE_HANDLER.setFormatter(file_format)
|
190
|
+
LOGGER.addHandler(FILE_HANDLER)
|
191
|
+
return log_abspath
|
192
|
+
return None
|
193
|
+
|
194
|
+
def _log_wrapper(func):
|
195
|
+
def wrapper(message, *args, **kwargs):
|
196
|
+
log_abspath = Logger()._check_and_rotate_log()
|
197
|
+
if log_abspath:
|
198
|
+
Logger()._delete_old_logs(log_abspath)
|
199
|
+
return func(message, *args, **kwargs)
|
200
|
+
return wrapper
|
201
|
+
|
202
|
+
@_log_wrapper
|
203
|
+
def info(message):
|
204
|
+
LOGGER.info(message)
|
205
|
+
|
206
|
+
@_log_wrapper
|
207
|
+
def debug(message):
|
208
|
+
LOGGER.debug(message)
|
209
|
+
|
210
|
+
@_log_wrapper
|
211
|
+
def warning(message):
|
212
|
+
LOGGER.warning(message)
|
213
|
+
|
214
|
+
@_log_wrapper
|
215
|
+
def error(message):
|
216
|
+
LOGGER.error(message)
|
217
|
+
|
218
|
+
def system(message, id):
|
219
|
+
Logger()._check_and_rotate_log()
|
220
|
+
file_handler = [handler for handler in LOGGER.handlers if isinstance(handler, logging.FileHandler)][0]
|
221
|
+
LOGGER.removeHandler(file_handler)
|
222
|
+
log = {
|
223
|
+
"timestamp": timer.get_curdatetime('%Y%m%d-%H:%M:%S'),
|
224
|
+
"level": "system",
|
225
|
+
"id": id,
|
226
|
+
"message": message.replace("\n", "\\n")
|
227
|
+
}
|
228
|
+
|
229
|
+
file_format = logging.Formatter('')
|
230
|
+
FILE_HANDLER.setFormatter(file_format)
|
231
|
+
LOGGER.addHandler(FILE_HANDLER)
|
232
|
+
LOGGER.debug(json.dumps(log, ensure_ascii=False))
|
233
|
+
|
234
|
+
|
235
|
+
if __name__ == '__main__':
|
236
|
+
None
|
237
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
@项目名称 : python-main
|
4
|
+
@文件名称 : output.py
|
5
|
+
@创建人 : zhongbinjie
|
6
|
+
@创建时间 : 2025/6/7 19:06
|
7
|
+
@文件说明 :
|
8
|
+
@企业名称 : 深圳市赢和信息技术有限公司
|
9
|
+
@Copyright:2025-2035, 深圳市赢和信息技术有限公司. All rights Reserved.
|
10
|
+
"""
|
11
|
+
|
12
|
+
import json
|
13
|
+
|
14
|
+
class OutputUtil:
|
15
|
+
@staticmethod
|
16
|
+
def map(data=None, message="执行成功", result=True, **kwargs):
|
17
|
+
response = {
|
18
|
+
"result": result,
|
19
|
+
"message": message,
|
20
|
+
"data": data or {},
|
21
|
+
**kwargs # 支持扩展字段(如 timestamp、pagination)
|
22
|
+
}
|
23
|
+
if 'json' in kwargs:
|
24
|
+
response = json.dumps(response, ensure_ascii=False)
|
25
|
+
return response
|
26
|
+
|
27
|
+
def test():
|
28
|
+
print()
|
29
|
+
|
30
|
+
if __name__ == '__main__':
|
31
|
+
None
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
@项目名称 : python-main
|
4
|
+
@文件名称 : texter.py
|
5
|
+
@创建人 : zhongbinjie
|
6
|
+
@创建时间 : 2025/6/7 19:06
|
7
|
+
@文件说明 :
|
8
|
+
@企业名称 : 深圳市赢和信息技术有限公司
|
9
|
+
@Copyright:2025-2035, 深圳市赢和信息技术有限公司. All rights Reserved.
|
10
|
+
"""
|
11
|
+
|
12
|
+
from datetime import datetime
|
13
|
+
import re
|
14
|
+
import uuid
|
15
|
+
|
16
|
+
def ljust(text, len):
|
17
|
+
return text.ljust(len)
|
18
|
+
|
19
|
+
def get_uuid(is_hex):
|
20
|
+
# 生成一个随机的 UUID
|
21
|
+
unique_id = uuid.uuid4()
|
22
|
+
# 使用 hex 属性获取不带 '-' 的 UUID
|
23
|
+
if is_hex:
|
24
|
+
unique_id = unique_id.hex
|
25
|
+
|
26
|
+
return unique_id
|
27
|
+
|
28
|
+
def parse_jdbc_url(jdbc_url):
|
29
|
+
# 定义正则表达式匹配不同数据库的JDBC URL
|
30
|
+
patterns = {
|
31
|
+
"mysql": r"jdbc:mysql://(?:\[([0-9a-fA-F:]+)\]|([^:/]+))(?::(\d+))?/([^?]+)",
|
32
|
+
"postgresql": r"jdbc:postgresql://(?:\[([0-9a-fA-F:]+)\]|([^:/]+))(?::(\d+))?/([^?]+)",
|
33
|
+
"oracle": r"jdbc:oracle:thin:@(?:\[([0-9a-fA-F:]+)\]|([^:/]+))(?::(\d+))?/(\w+)",
|
34
|
+
"sqlserver": r"jdbc:sqlserver://(?:\[([0-9a-fA-F:]+)\]|([^:/;]+))(?::(\d+))?;database[Name=]*([^;]+)",
|
35
|
+
"dameng": r"jdbc:dm://(?:\[([0-9a-fA-F:]+)\]|([^:/;]+))(?::(\d+))?/([^?]+)",
|
36
|
+
"oceanbase": r"jdbc:oceanbase://(?:\[([0-9a-fA-F:]+)\]|([^:/;]+))(?::(\d+))?/([^?]+)"
|
37
|
+
}
|
38
|
+
|
39
|
+
# 尝试匹配不同数据库的URL
|
40
|
+
for db_type, pattern in patterns.items():
|
41
|
+
match = re.match(pattern, jdbc_url)
|
42
|
+
if match:
|
43
|
+
# 提取IPv6(带方括号)或IPv4/主机名
|
44
|
+
host = match.group(1) or match.group(2)
|
45
|
+
port = match.group(3)
|
46
|
+
database = match.group(4)
|
47
|
+
return {
|
48
|
+
"type": db_type,
|
49
|
+
"host": host,
|
50
|
+
"port": port,
|
51
|
+
"database": database
|
52
|
+
}
|
53
|
+
|
54
|
+
return None # 未匹配到已知格式
|
55
|
+
|
56
|
+
def text2date(text):
|
57
|
+
# 定义常见的日期格式列表
|
58
|
+
date_formats = [
|
59
|
+
"%Y%m%d", "%Y-%m-%d", "%Y/%m/%d", "%m-%d-%Y", "%m/%d/%Y",
|
60
|
+
"%d-%m-%Y", "%d/%m/%Y", "%b %d, %Y",
|
61
|
+
"%B %d, %Y", "%b %d %Y", "%B %d %Y", "%Y年%m月%d日"
|
62
|
+
]
|
63
|
+
for fmt in date_formats:
|
64
|
+
try:
|
65
|
+
# 尝试使用当前格式解析日期字符串
|
66
|
+
dt = datetime.strptime(text, fmt)
|
67
|
+
return dt.date()
|
68
|
+
except ValueError:
|
69
|
+
continue
|
70
|
+
return None # 所有格式都尝试失败,返回 None
|
71
|
+
|
72
|
+
# 添加MB/GB/KB转字节的转换函数
|
73
|
+
def convert_to_bytes(size_str):
|
74
|
+
"""将带单位的大小字符串(如"50MB")转换为字节数
|
75
|
+
|
76
|
+
Args:
|
77
|
+
size_str (str): 带单位的大小字符串,支持 B, KB, MB, GB (不区分大小写)
|
78
|
+
|
79
|
+
Returns:
|
80
|
+
int: 转换后的字节数
|
81
|
+
|
82
|
+
Raises:
|
83
|
+
ValueError: 当输入格式无效或单位不支持时触发
|
84
|
+
"""
|
85
|
+
# 正则表达式匹配数值和单位(支持整数/小数和可选空格)
|
86
|
+
match = re.match(r'^(\d+\.?\d*)\s*([A-Za-z]+)$', size_str.strip())
|
87
|
+
if not match:
|
88
|
+
raise ValueError(f"无效的大小格式: {size_str},请使用类似'50MB'的格式")
|
89
|
+
|
90
|
+
size = float(match.group(1))
|
91
|
+
unit = match.group(2).upper()
|
92
|
+
|
93
|
+
# 定义单位到字节的转换因子(1024进制)
|
94
|
+
unit_factors = {
|
95
|
+
'B': 1, # 字节
|
96
|
+
'KB': 1024, # 千字节
|
97
|
+
'MB': 1024 ** 2, # 兆字节
|
98
|
+
'GB': 1024 ** 3 # 吉字节
|
99
|
+
}
|
100
|
+
|
101
|
+
if unit not in unit_factors:
|
102
|
+
supported_units = ', '.join(unit_factors.keys())
|
103
|
+
raise ValueError(f"不支持的单位: {unit},支持的单位有: {supported_units}")
|
104
|
+
|
105
|
+
# 计算并返回字节数(转换为整数)
|
106
|
+
return int(size * unit_factors[unit])
|
107
|
+
|
108
|
+
|
109
|
+
def extract_table_names(sql_query):
|
110
|
+
"""
|
111
|
+
从 SQL 查询语句中提取表名和别名。
|
112
|
+
返回格式: [{'table': '表名', 'alias': '别名'}, ...]
|
113
|
+
"""
|
114
|
+
if not sql_query: return []
|
115
|
+
sql_query = sql_query.replace('\n', ' ')
|
116
|
+
# 增强正则表达式以匹配表名和可选别名
|
117
|
+
table_pattern = re.compile(
|
118
|
+
r'\b(?:FROM|JOIN)\s+([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)?)\s*(?:AS\s+)?([a-zA-Z_][a-zA-Z0-9_]*)?\b',
|
119
|
+
re.IGNORECASE
|
120
|
+
)
|
121
|
+
matches = table_pattern.findall(sql_query)
|
122
|
+
# 转换为字典列表,处理无别名情况
|
123
|
+
result = []
|
124
|
+
for table, alias in matches:
|
125
|
+
result.append({
|
126
|
+
'table': table,
|
127
|
+
'alias': alias if alias else None
|
128
|
+
})
|
129
|
+
return result
|
130
|
+
|
131
|
+
def extract_columns(sql_query):
|
132
|
+
# 匹配SELECT和FROM之间的内容(忽略大小写,支持多行)
|
133
|
+
pattern = r'SELECT\s+(.*?)\s+FROM'
|
134
|
+
flags = re.IGNORECASE | re.DOTALL
|
135
|
+
|
136
|
+
# 提取内容
|
137
|
+
match = re.search(pattern, sql_query, flags)
|
138
|
+
select_content = None
|
139
|
+
if match:
|
140
|
+
select_content = match.group(1).strip()
|
141
|
+
|
142
|
+
if not select_content:
|
143
|
+
return []
|
144
|
+
columns = [col.strip() for col in select_content.split(',')]
|
145
|
+
|
146
|
+
# 解析每个列的源字段和别名
|
147
|
+
parsed_columns = []
|
148
|
+
for col in columns:
|
149
|
+
# 支持带AS关键字和不带AS关键字的别名格式
|
150
|
+
match = re.match(r'^(.+?)\s+(?:AS\s+)?([^\s]+)$', col, re.IGNORECASE)
|
151
|
+
if match:
|
152
|
+
source = match.group(1).strip()
|
153
|
+
alias = match.group(2).strip()
|
154
|
+
parsed_columns.append({"source": source, "alias": alias})
|
155
|
+
else:
|
156
|
+
parsed_columns.append({"source": col, "alias": None})
|
157
|
+
|
158
|
+
return parsed_columns
|
159
|
+
|
160
|
+
def convert_to_oracle_datetime(input_str, type):
|
161
|
+
"""将'YYYY-MM-DD HH:MM:SS'格式字符串转换为Oracle兼容的'DD-Mon-YYYY HH:MM:SS'格式"""
|
162
|
+
try:
|
163
|
+
# 解析输入日期时间字符串
|
164
|
+
dt = datetime.strptime(input_str, '%Y-%m-%d %H:%M:%S')
|
165
|
+
# 格式化为目标格式并将月份缩写转为大写
|
166
|
+
if type == 'DATE':
|
167
|
+
dt = dt.strftime('%d-%b-%y').upper()
|
168
|
+
elif type == 'TIMESTAMP':
|
169
|
+
dt = dt.strftime('%d-%b-%y %H:%M:%S').upper()
|
170
|
+
return dt
|
171
|
+
except ValueError as e:
|
172
|
+
raise ValueError(f"日期格式转换失败: {str(e)}")
|
173
|
+
|
174
|
+
|
175
|
+
if __name__ == '__main__':
|
176
|
+
# url = "jdbc:oracle:thin:@192.168.74.26:1521/orcl"
|
177
|
+
# url2 = "jdbc:mysql://192.168.74.26:3306/db"
|
178
|
+
# url3 = "jdbc:dm://192.168.75.23:5236/UTMGR"
|
179
|
+
# print(parse_jdbc_url(url3))
|
180
|
+
# print(convert_to_kb("50MB"))
|
181
|
+
# url3 = "jdbc:oceanbase://192.168.74.33:2883/fdc?autocommit=true"
|
182
|
+
# print(parse_jdbc_url(url3))
|
183
|
+
# 示例SQL
|
184
|
+
# sql = "SELECT * FROM user_tab_columns WHERE table_name = 'T_CHECK_LOG_TEST'"
|
185
|
+
# print(extract_columns(sql))
|
186
|
+
None
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
@项目名称 : python-main
|
4
|
+
@文件名称 : timer.py
|
5
|
+
@创建人 : zhongbinjie
|
6
|
+
@创建时间 : 2025/6/7 19:06
|
7
|
+
@文件说明 :
|
8
|
+
@企业名称 : 深圳市赢和信息技术有限公司
|
9
|
+
@Copyright:2025-2035, 深圳市赢和信息技术有限公司. All rights Reserved.
|
10
|
+
"""
|
11
|
+
|
12
|
+
|
13
|
+
import time
|
14
|
+
from datetime import datetime
|
15
|
+
from utils import logger
|
16
|
+
|
17
|
+
# 时间装饰器,计算函数运行时长
|
18
|
+
def timing_decorator(func):
|
19
|
+
def wrapper(*args, **kwargs):
|
20
|
+
start_time = time.time()
|
21
|
+
result = func(*args, **kwargs)
|
22
|
+
end_time = time.time()
|
23
|
+
execution_time = end_time - start_time
|
24
|
+
logger.debug(f"文件 {func.__module__} 函数 {func.__name__} 执行时间:{execution_time:.6f} 秒")
|
25
|
+
return result
|
26
|
+
return wrapper
|
27
|
+
|
28
|
+
def get_curdate(format = None):
|
29
|
+
t = datetime.now().strftime('%Y-%m-%d')
|
30
|
+
if format:
|
31
|
+
try:
|
32
|
+
t = t.strftime(format)
|
33
|
+
except Exception as e:
|
34
|
+
|
35
|
+
return None
|
36
|
+
return t
|
37
|
+
|
38
|
+
def get_curdatetime(format = None):
|
39
|
+
t = datetime.now()
|
40
|
+
if format:
|
41
|
+
try:
|
42
|
+
t = t.strftime(format)
|
43
|
+
except Exception as e:
|
44
|
+
|
45
|
+
return None
|
46
|
+
return t
|
47
|
+
|
48
|
+
def get_time():
|
49
|
+
return time.time()
|
50
|
+
|
51
|
+
def get_timediff(time1, time2):
|
52
|
+
return time1 - time2
|