cppackage 0.3.1__py3-none-any.whl → 0.3.2__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.
CPpackage/db/sql_model.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import pymysql
2
2
  import time
3
- from pymysql import Error
4
3
  try:
5
4
  from .config import get_db_config
6
5
  except (ImportError, ValueError):
@@ -13,12 +12,8 @@ except (ImportError, ValueError):
13
12
 
14
13
  # ===================== 数据库连接 =====================
15
14
 
16
- def _get_connection(database=None, port=None):
17
- cfg = get_db_config()
18
-
19
- db = database if database else cfg.get('database')
20
- prt = port if port else cfg.get('port', 3306)
21
-
15
+ def _create_new_connection(cfg, db, prt):
16
+ """创建新的数据库连接"""
22
17
  return pymysql.connect(
23
18
  host=cfg.get('host'),
24
19
  user=cfg.get('user'),
@@ -30,6 +25,33 @@ def _get_connection(database=None, port=None):
30
25
  )
31
26
 
32
27
 
28
+ def _get_connection(database=None, port=None):
29
+ """
30
+ 获取数据库连接,包含健康检查机制
31
+ - 使用 ping(reconnect=True) 检测连接有效性
32
+ - 如果连接失效,自动重新创建连接
33
+ """
34
+ cfg = get_db_config()
35
+
36
+ db = database if database else cfg.get('database')
37
+ prt = port if port else cfg.get('port', 3306)
38
+
39
+ conn = _create_new_connection(cfg, db, prt)
40
+
41
+ # 连接健康检查:发送 ping 确保连接有效
42
+ try:
43
+ conn.ping(reconnect=False)
44
+ except Exception:
45
+ # 如果 ping 失败,重新创建连接
46
+ try:
47
+ conn.close()
48
+ except Exception:
49
+ pass
50
+ conn = _create_new_connection(cfg, db, prt)
51
+
52
+ return conn
53
+
54
+
33
55
  # ===================== 查询操作 =====================
34
56
 
35
57
  def sel_data(sql, params=None, port=None, database=None):
@@ -139,8 +161,7 @@ def find_duplicate_records(table_name, database, unique_index_fields, port=None)
139
161
  if not isinstance(unique_index_fields, list) or len(unique_index_fields) == 0:
140
162
  print("错误:unique_index_fields 必须是非空列表!")
141
163
  return None
142
-
143
- fields_str = ",".join([f"`{field}`" for field in unique_index_fields])
164
+
144
165
  group_by_str = ",".join([f"`{field}`" for field in unique_index_fields])
145
166
 
146
167
  # 核心SQL:查询所有重复记录
@@ -244,19 +265,46 @@ def delete_duplicate_records(table_name, database, unique_index_fields, port=Non
244
265
  return None
245
266
  # ===================== DataFrame 入库 =====================
246
267
 
247
- def update_datas(df, table_name, database, batch_size=20):
268
+ def _is_connection_error(error_msg):
269
+ """
270
+ 判断是否是数据库连接相关错误
271
+ 返回 True 表示是连接错误,可以通过重试解决
272
+ """
273
+ connection_error_codes = [
274
+ '(0, \'\')', # pymysql InterfaceError: 连接已关闭
275
+ '2003', # Can't connect to MySQL server
276
+ '2006', # MySQL server has gone away
277
+ '2013', # Lost connection to MySQL server during query
278
+ '2014', # Commands out of sync
279
+ '1045', # Access denied
280
+ '2055', # Lost connection to MySQL server at 'xxx'
281
+ ]
282
+ return any(code in error_msg for code in connection_error_codes)
248
283
 
284
+
285
+ def update_datas(df, table_name, database, batch_size=20):
286
+ """
287
+ 将 DataFrame 数据批量插入/更新到数据库
288
+ 包含完善的重试机制:
289
+ - 连接错误:最多重试3次,指数退避
290
+ - 字段缺失错误:自动同步字段后重试
291
+ - 其他错误:立即抛出
292
+ """
249
293
  if df.empty:
250
294
  print("数据为空,无需入库")
251
295
  return
296
+
252
297
  cols = list(df.columns)
253
298
  col_str = ",".join([f"`{c}`" for c in cols])
254
299
  value_tpl = "(" + ",".join(["%s"] * len(cols)) + ")"
255
300
  update_clause = ",".join(
256
301
  [f"`{c}`=VALUES(`{c}`)" for c in cols if c != "id"]
257
302
  )
303
+
258
304
  total = len(df)
259
305
  success_count = 0
306
+ max_retries = 3 # 最大重试次数
307
+
260
308
  for start in range(0, total, batch_size):
261
309
  batch_df = df.iloc[start:start + batch_size]
262
310
  values_str = ",".join([value_tpl] * len(batch_df))
@@ -267,28 +315,60 @@ def update_datas(df, table_name, database, batch_size=20):
267
315
  """
268
316
  data = [tuple(row) for row in batch_df.values]
269
317
  flat_data = [v for row in data for v in row]
270
- conn = None
271
- for attempt in range(2):
318
+
319
+ for attempt in range(max_retries):
320
+ conn = None
272
321
  try:
273
322
  conn = _get_connection(database)
323
+ # 连接健康检查
324
+ conn.ping(reconnect=False)
325
+
274
326
  cursor = conn.cursor()
275
327
  cursor.execute(sql, flat_data)
276
328
  conn.commit()
277
329
  success_count += cursor.rowcount
278
- break
330
+ break # 成功,跳出重试循环
331
+
279
332
  except Exception as e:
333
+ error_str = str(e)
280
334
  if conn:
281
- conn.rollback()
282
- print(f"批次入库失败(start={start}):", e)
283
- if '1054, "Unknown column' in str(e):
335
+ try:
336
+ conn.rollback()
337
+ except Exception:
338
+ pass
339
+
340
+ print(f"批次入库失败(start={start}, attempt={attempt+1}/{max_retries}): {e}")
341
+
342
+ # 判断是否是连接相关错误
343
+ if _is_connection_error(error_str):
344
+ if attempt < max_retries - 1:
345
+ # 指数退避:2秒、4秒、8秒
346
+ wait_time = 2 * (2 ** attempt)
347
+ print(f"连接错误,等待{wait_time}秒后重试...")
348
+ time.sleep(wait_time)
349
+ continue
350
+ else:
351
+ # 最后一次重试仍然失败,抛出异常
352
+ raise Exception(f"数据库连接错误,已重试{max_retries}次: {e}")
353
+
354
+ elif '1054, "Unknown column' in error_str:
355
+ # 字段缺失错误,同步字段后重试
284
356
  check_and_sync_columns(df, table_name, database)
285
- break
286
- elif attempt == 0:
287
- time.sleep(1)
357
+ # 字段同步成功后,继续当前批次的重试
358
+ if attempt < max_retries - 1:
359
+ continue
360
+ else:
361
+ raise Exception(f"字段同步后仍然失败: {e}")
288
362
  else:
289
- raise Exception(e)
363
+ # 其他非连接错误,直接抛出
364
+ raise
365
+
290
366
  finally:
291
367
  if conn:
292
- conn.close()
368
+ try:
369
+ conn.close()
370
+ except Exception:
371
+ pass
372
+
293
373
  print(f"成功插入/更新 {success_count} 条记录")
294
374
 
CPpackage/db/test.py CHANGED
@@ -4,10 +4,10 @@ import os
4
4
 
5
5
  if __name__ == "__main__":
6
6
  # 配置你的参数
7
- TABLE_NAME = "pinlei_spph"
7
+ TABLE_NAME = "pinlei_sp360_xsfx"
8
8
  DATABASE_NAME = "shengyicanmou"
9
9
  # 你的唯一索引字段列表(必须是列表类型!)
10
- UNIQUE_FIELDS = ["itemId", "store_id", "begindate", "value_l", "sellerId", "cateLevel2Id", "cateId", "cateLevel1Id", "mainProductId", "itemTag", "date_effect"]
10
+ UNIQUE_FIELDS = ["begindate", "value_l", "itemId", "store_id", "date_effect"]
11
11
  PORT = 3306 # 可选,默认从配置获取
12
12
 
13
13
  # 第一步:查询重复记录
@@ -1,10 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cppackage
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: 超品集团自用的Python包
5
5
  Home-page: https://github.com/example/CPpackage
6
6
  Author: team-数智组
7
7
  Author-email: m110135@163.com
8
+ License: UNKNOWN
9
+ Platform: UNKNOWN
8
10
  Classifier: Development Status :: 3 - Alpha
9
11
  Classifier: Intended Audience :: Developers
10
12
  Classifier: License :: OSI Approved :: MIT License
@@ -16,7 +18,6 @@ Classifier: Programming Language :: Python :: 3.11
16
18
  Classifier: Programming Language :: Python :: 3.12
17
19
  Requires-Python: >=3.8
18
20
  Description-Content-Type: text/markdown
19
- License-File: LICENSE
20
21
  Requires-Dist: pymysql
21
22
  Requires-Dist: pandas
22
23
  Requires-Dist: numpy
@@ -70,3 +71,5 @@ CPpackage/
70
71
  ## 许可证
71
72
 
72
73
  本项目采用MIT许可证。详情请参阅LICENSE文件。
74
+
75
+
@@ -0,0 +1,13 @@
1
+ CPpackage/__init__.py,sha256=KpVaXkIzkNDl-dfSX8Rr7z9XFlipxx1nZ_GzS4Qls4I,173
2
+ CPpackage/core/__init__.py,sha256=92mF0310uQ5ujlgd-LCMs5kZVzfGRdcl67GHIRWhHcA,504
3
+ CPpackage/db/__init__.py,sha256=WhrLqsjq97KKHF7YV-HcZf9pRtHuqKhRjXDNPCBVbjY,107
4
+ CPpackage/db/config.py,sha256=A9HFCXqlrvssHLD1Ys9HpjDckavalFj31X1Np_aDNp4,729
5
+ CPpackage/db/sql_model.py,sha256=suwQMXf_hpfYfZR07SadIa2E_y_mbEKKy-6BuZCwWE4,13002
6
+ CPpackage/db/test.py,sha256=1EfTt53H9HLIGK6U2SAF6lIDgz2LItJ9tNHpxojz9ro,939
7
+ CPpackage/utils/__init__.py,sha256=sAespT0aoLr3O0WF27DwHLYF60VZgPA1tiT0JJ8s8zI,264
8
+ cppackage-0.3.2.dist-info/LICENSE,sha256=6Pj597LnvlRtq3Vgjkfp6zgBeBh7wl8jKE-nCFg4vZI,1065
9
+ cppackage-0.3.2.dist-info/METADATA,sha256=KFc2Lr9sQj2vjZaiVRAHKd65oMROmyAUBxSbiQDv5Ug,2305
10
+ cppackage-0.3.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
11
+ cppackage-0.3.2.dist-info/entry_points.txt,sha256=6MmsHuaQY5w0GxhHAx3lQfHTaocmueEf1OGsoM-hMrQ,51
12
+ cppackage-0.3.2.dist-info/top_level.txt,sha256=FQrfWDbJistWQIrHW_Aoxoy_UWSzjOByWVcVS5ig9Tk,10
13
+ cppackage-0.3.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.46.3)
2
+ Generator: bdist_wheel (0.45.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  cppackage = CPpackage.core:main
3
+
@@ -1,13 +0,0 @@
1
- CPpackage/__init__.py,sha256=KpVaXkIzkNDl-dfSX8Rr7z9XFlipxx1nZ_GzS4Qls4I,173
2
- CPpackage/core/__init__.py,sha256=92mF0310uQ5ujlgd-LCMs5kZVzfGRdcl67GHIRWhHcA,504
3
- CPpackage/db/__init__.py,sha256=WhrLqsjq97KKHF7YV-HcZf9pRtHuqKhRjXDNPCBVbjY,107
4
- CPpackage/db/config.py,sha256=A9HFCXqlrvssHLD1Ys9HpjDckavalFj31X1Np_aDNp4,729
5
- CPpackage/db/sql_model.py,sha256=QnWEBb-ZqioZf62q-RQPgG8uJqLY9KgcyGW3zFkH4YY,10198
6
- CPpackage/db/test.py,sha256=dEeoV5FBuWEt5MFujrt3fd2KSQ690zyw6mANgeiVRCg,1015
7
- CPpackage/utils/__init__.py,sha256=sAespT0aoLr3O0WF27DwHLYF60VZgPA1tiT0JJ8s8zI,264
8
- cppackage-0.3.1.dist-info/LICENSE,sha256=6Pj597LnvlRtq3Vgjkfp6zgBeBh7wl8jKE-nCFg4vZI,1065
9
- cppackage-0.3.1.dist-info/METADATA,sha256=UZsPKX3T4zA7FbzYpb6Ppy0UvjzFjgq5zXMmZmFjqnE,2287
10
- cppackage-0.3.1.dist-info/WHEEL,sha256=hPN0AlP2dZM_3ZJZWP4WooepkmU9wzjGgCLCeFjkHLA,92
11
- cppackage-0.3.1.dist-info/entry_points.txt,sha256=SAzF2klkNyeNCqXuiNkDirN999l7v8CGCrHR0StD_oo,50
12
- cppackage-0.3.1.dist-info/top_level.txt,sha256=FQrfWDbJistWQIrHW_Aoxoy_UWSzjOByWVcVS5ig9Tk,10
13
- cppackage-0.3.1.dist-info/RECORD,,