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.
Files changed (50) hide show
  1. database-smartools/__init__.py +18 -0
  2. database-smartools/api/__init__.py +18 -0
  3. database-smartools/api/etl.py +160 -0
  4. database-smartools/database/__init__.py +18 -0
  5. database-smartools/database/dameng_extend.py +65 -0
  6. database-smartools/database/db_handler.py +676 -0
  7. database-smartools/database/mysql_extend.py +396 -0
  8. database-smartools/database/oceanbase_extend.py +55 -0
  9. database-smartools/database/oracle_extend.py +27 -0
  10. database-smartools/database/postgre_extend.py +396 -0
  11. database-smartools/main.py +86 -0
  12. database-smartools/module/__init__.py +18 -0
  13. database-smartools/module/function.py +115 -0
  14. database-smartools/utils/__init__.py +18 -0
  15. database-smartools/utils/config.py +56 -0
  16. database-smartools/utils/debug.py +72 -0
  17. database-smartools/utils/file.py +42 -0
  18. database-smartools/utils/http.py +74 -0
  19. database-smartools/utils/logger.py +237 -0
  20. database-smartools/utils/output.py +31 -0
  21. database-smartools/utils/texter.py +186 -0
  22. database-smartools/utils/timer.py +52 -0
  23. database-tools/__init__.py +18 -0
  24. database-tools/api/__init__.py +18 -0
  25. database-tools/api/etl.py +160 -0
  26. database-tools/database/__init__.py +18 -0
  27. database-tools/database/dameng_extend.py +65 -0
  28. database-tools/database/db_handler.py +676 -0
  29. database-tools/database/mysql_extend.py +396 -0
  30. database-tools/database/oceanbase_extend.py +55 -0
  31. database-tools/database/oracle_extend.py +27 -0
  32. database-tools/database/postgre_extend.py +396 -0
  33. database-tools/main.py +86 -0
  34. database-tools/module/__init__.py +18 -0
  35. database-tools/module/function.py +115 -0
  36. database-tools/utils/__init__.py +18 -0
  37. database-tools/utils/config.py +56 -0
  38. database-tools/utils/debug.py +72 -0
  39. database-tools/utils/file.py +42 -0
  40. database-tools/utils/http.py +74 -0
  41. database-tools/utils/logger.py +237 -0
  42. database-tools/utils/output.py +31 -0
  43. database-tools/utils/texter.py +186 -0
  44. database-tools/utils/timer.py +52 -0
  45. database_smartools-1.0.0.dist-info/METADATA +84 -0
  46. database_smartools-1.0.0.dist-info/RECORD +50 -0
  47. database_smartools-1.0.0.dist-info/WHEEL +5 -0
  48. database_smartools-1.0.0.dist-info/licenses/LICENSE +13 -0
  49. database_smartools-1.0.0.dist-info/top_level.txt +1 -0
  50. 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