ddddtools 0.1.5__tar.gz

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 (30) hide show
  1. ddddtools-0.1.5/PKG-INFO +965 -0
  2. ddddtools-0.1.5/README.md +943 -0
  3. ddddtools-0.1.5/pyproject.toml +36 -0
  4. ddddtools-0.1.5/setup.cfg +4 -0
  5. ddddtools-0.1.5/src/ddddtools/__init__.py +22 -0
  6. ddddtools-0.1.5/src/ddddtools/decorator/__init__.py +5 -0
  7. ddddtools-0.1.5/src/ddddtools/decorator/log_call.py +69 -0
  8. ddddtools-0.1.5/src/ddddtools/decorator/timer.py +17 -0
  9. ddddtools-0.1.5/src/ddddtools/encryption/__init__.py +12 -0
  10. ddddtools-0.1.5/src/ddddtools/encryption/aes.py +78 -0
  11. ddddtools-0.1.5/src/ddddtools/encryption/rsa.py +184 -0
  12. ddddtools-0.1.5/src/ddddtools/ftp/__init__.py +14 -0
  13. ddddtools-0.1.5/src/ddddtools/ftp/connection.py +67 -0
  14. ddddtools-0.1.5/src/ddddtools/ftp/delete.py +76 -0
  15. ddddtools-0.1.5/src/ddddtools/ftp/download.py +75 -0
  16. ddddtools-0.1.5/src/ddddtools/ftp/list.py +100 -0
  17. ddddtools-0.1.5/src/ddddtools/ftp/upload.py +86 -0
  18. ddddtools-0.1.5/src/ddddtools/logging/__init__.py +110 -0
  19. ddddtools-0.1.5/src/ddddtools/mail/__init__.py +167 -0
  20. ddddtools-0.1.5/src/ddddtools/mail/template/__init__.py +451 -0
  21. ddddtools-0.1.5/src/ddddtools/mongodb/__init__.py +50 -0
  22. ddddtools-0.1.5/src/ddddtools/mongodb/collection.py +104 -0
  23. ddddtools-0.1.5/src/ddddtools/redis/__init__.py +161 -0
  24. ddddtools-0.1.5/src/ddddtools/redis/decorator.py +116 -0
  25. ddddtools-0.1.5/src/ddddtools/wechat/__init__.py +298 -0
  26. ddddtools-0.1.5/src/ddddtools.egg-info/PKG-INFO +965 -0
  27. ddddtools-0.1.5/src/ddddtools.egg-info/SOURCES.txt +28 -0
  28. ddddtools-0.1.5/src/ddddtools.egg-info/dependency_links.txt +1 -0
  29. ddddtools-0.1.5/src/ddddtools.egg-info/requires.txt +2 -0
  30. ddddtools-0.1.5/src/ddddtools.egg-info/top_level.txt +1 -0
@@ -0,0 +1,965 @@
1
+ Metadata-Version: 2.4
2
+ Name: ddddtools
3
+ Version: 0.1.5
4
+ Summary: 常用工具函数集合
5
+ Author-email: Owner <owner@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/owner/dtools
8
+ Keywords: utils,tools
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.8
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: pycryptodome>=3.19.0
21
+ Requires-Dist: pymongo>=4.6.0
22
+
23
+ # ddddtools
24
+
25
+ 常用工具函数集合。支持 Python 3.8+。
26
+
27
+ ---
28
+
29
+ ## 目录
30
+
31
+ - [安装](#安装)
32
+ - [快速开始](#快速开始)
33
+ - [日志模块 (logging)](#日志模块-logging)
34
+ - [装饰器 (decorator)](#装饰器-decorator)
35
+ - [FTP操作 (ftp)](#ftp操作-ftp)
36
+ - [邮件发送 (mail)](#邮件发送-mail)
37
+ - [HTML模板 (mail.template)](#html模板-mailtemplate)
38
+ - [缓存管理 (redis)](#缓存管理-redis)
39
+ - [文件结构](#文件结构)
40
+ - [常见问题](#常见问题)
41
+
42
+ ---
43
+
44
+ ## 安装
45
+
46
+ ### PyPI 安装(稳定版)
47
+
48
+ ```bash
49
+ pip install ddddtools
50
+ ```
51
+
52
+ ### 本地安装(开发版)
53
+
54
+ ```bash
55
+ cd /path/to/ddddtools
56
+ pip install -e .
57
+ ```
58
+
59
+ ### 从源码安装
60
+
61
+ ```bash
62
+ git clone https://github.com/yourname/ddddtools.git
63
+ cd ddddtools
64
+ pip install -e .
65
+ ```
66
+
67
+ ---
68
+
69
+ ## 快速开始
70
+
71
+ ```python
72
+ # 导入所有功能
73
+ from ddddtools import logger, timer, log_call, ftp, mail
74
+
75
+ # 1. 日志自动启动
76
+ logger.info("ddddtools 已就绪")
77
+
78
+ # 2. 使用装饰器
79
+ @timer
80
+ @log_call()
81
+ def fetch_data():
82
+ return {"data": [1, 2, 3]}
83
+
84
+ result = fetch_data()
85
+
86
+ # 3. FTP操作
87
+ with ftp.connect("ftp.example.com", username="user", password="pass") as conn:
88
+ files = conn.list_files("/")
89
+ conn.download_file("/pub/data.csv", "./data.csv")
90
+
91
+ # 4. 发送邮件
92
+ mail.send_simple(
93
+ to_addrs=["admin@example.com"],
94
+ subject="系统通知",
95
+ content="任务已完成",
96
+ username="your@qq.com",
97
+ password="your授权码"
98
+ )
99
+ ```
100
+
101
+ ---
102
+
103
+ ## 日志模块 (logging)
104
+
105
+ 自动创建logs目录,按日期存储,自动清理7天前日志。
106
+
107
+ ### 默认日志器
108
+
109
+ ```python
110
+ from ddddtools import logger
111
+
112
+ logger.debug("调试信息")
113
+ logger.info("普通信息")
114
+ logger.warning("警告")
115
+ logger.error("错误")
116
+ logger.critical("严重错误")
117
+ ```
118
+
119
+ ### 自定义日志器
120
+
121
+ ```python
122
+ from ddddtools import get_logger
123
+
124
+ # 自定义名称、目录、保留天数
125
+ app_logger = get_logger(
126
+ name="MyApp", # 日志器名称
127
+ log_dir="/var/log/myapp", # 日志目录
128
+ days=30 # 保留30天
129
+ )
130
+
131
+ app_logger.info("自定义日志器")
132
+ ```
133
+
134
+ ### 日志格式
135
+
136
+ ```
137
+ 2026-02-07 14:03 | INFO | ddddtools | ddddtools 已就绪
138
+ 2026-02-07 14:03 | INFO | MyApp | 自定义日志器
139
+ ```
140
+
141
+ - 自动在运行目录创建 `logs` 文件夹
142
+ - 按日期切割:`2026-02-07.log`
143
+ - 同时输出到控制台和文件
144
+ - 默认保留7天
145
+
146
+ ---
147
+
148
+ ## 装饰器 (decorator)
149
+
150
+ ### timer - 耗时统计
151
+
152
+ 统计函数执行时间(毫秒)。
153
+
154
+ ```python
155
+ from ddddtools import timer
156
+
157
+ @timer
158
+ def slow_function():
159
+ time.sleep(2)
160
+ return "完成"
161
+
162
+ result = slow_function()
163
+ # 输出: [slow_function] 耗时: 2002.35ms
164
+ ```
165
+
166
+ ### log_call - 调用日志
167
+
168
+ 记录函数调用详情。
169
+
170
+ ```python
171
+ from ddddtools import log_call
172
+
173
+ # 基本用法(使用函数名作为日志名)
174
+ @log_call()
175
+ def add(a, b):
176
+ return a + b
177
+
178
+ # 自定义日志名
179
+ @log_call(name="计算器")
180
+ def multiply(x, y):
181
+ return x * y
182
+
183
+ # 自定义参数截断长度
184
+ @log_call(arg_limit=500, return_limit=800)
185
+ def process(data):
186
+ return {"result": data}
187
+
188
+ # 带关键字参数
189
+ @log_call()
190
+ def create_user(name, age, *, email=None):
191
+ return {"name": name, "age": age, "email": email}
192
+
193
+ # 调用示例
194
+ add(1, 2)
195
+ # 输出:
196
+ # [add] 传入: (a=1, b=2)
197
+ # [add] 返回: 3 | 耗时: 0.05ms
198
+
199
+ create_user("张三", 25, email="zhangsan@example.com")
200
+ # 输出:
201
+ # [create_user] 传入: (name='张三', age=25, email='zhangsan@example.com')
202
+ # [create_user] 返回: {'name': '张三', 'age': 25, 'email': 'zhangsan@example.com'} | 耗时: 0.12ms
203
+ ```
204
+
205
+ #### log_call 参数
206
+
207
+ | 参数 | 类型 | 默认值 | 说明 |
208
+ |------|------|--------|------|
209
+ | `name` | str | 函数名 | 日志名称 |
210
+ | `arg_limit` | int | 1000 | 参数字符串截断长度 |
211
+ | `return_limit` | int | 1000 | 返回值字符串截断长度 |
212
+
213
+ ---
214
+
215
+ ## FTP操作 (ftp)
216
+
217
+ ### 连接管理
218
+
219
+ ```python
220
+ from ddddtools import ftp
221
+
222
+ # 基本连接
223
+ with ftp.connect("ftp.example.com") as conn:
224
+ pass
225
+
226
+ # 带认证
227
+ with ftp.connect(
228
+ host="ftp.example.com",
229
+ port=21,
230
+ username="your_user",
231
+ password="your_password",
232
+ timeout=30
233
+ ) as conn:
234
+ pass
235
+
236
+ # 匿名连接
237
+ with ftp.connect("ftp.example.com", username="anonymous", password="") as conn:
238
+ pass
239
+ ```
240
+
241
+ ### 获取文件列表
242
+
243
+ ```python
244
+ from ddddtools import ftp
245
+
246
+ with ftp.connect("ftp.example.com", username="user") as conn:
247
+ # 获取当前目录列表
248
+ files = conn.list_files("/")
249
+ for f in files:
250
+ print(f"{'[DIR]' if f.is_dir else 'FILE']} {f.name} ({f.size} bytes)")
251
+
252
+ # 递归获取所有文件
253
+ all_files = conn.list_all("/", recursive=True)
254
+
255
+ # FTPFile 属性
256
+ # - name: 文件名/完整路径
257
+ # - size: 文件大小(字节)
258
+ # - is_dir: 是否为文件夹
259
+ # - modify_time: 修改时间
260
+ ```
261
+
262
+ ### 下载文件
263
+
264
+ ```python
265
+ from ddddtools import ftp
266
+
267
+ with ftp.connect("ftp.example.com", username="user") as conn:
268
+ # 下载单个文件
269
+ conn.download_file(
270
+ remote_path="/pub/data/report.csv",
271
+ local_path="./downloads/report.csv",
272
+ overwrite=True # 是否覆盖
273
+ )
274
+
275
+ # 下载整个文件夹
276
+ conn.download_folder(
277
+ remote_folder="/pub/backups",
278
+ local_folder="./backups",
279
+ overwrite=True
280
+ )
281
+ ```
282
+
283
+ ### 上传文件
284
+
285
+ ```python
286
+ from ddddtools import ftp
287
+
288
+ with ftp.connect("ftp.example.com", username="user") as conn:
289
+ # 上传单个文件
290
+ conn.upload_file(
291
+ local_path="./data.csv",
292
+ remote_path="/pub/uploads/data.csv",
293
+ overwrite=True
294
+ )
295
+
296
+ # 上传整个文件夹
297
+ conn.upload_folder(
298
+ local_folder="./project",
299
+ remote_path="/pub/projects/project",
300
+ overwrite=True
301
+ )
302
+ ```
303
+
304
+ ### 删除操作
305
+
306
+ ```python
307
+ from ddddtools import ftp
308
+
309
+ with ftp.connect("ftp.example.com", username="user") as conn:
310
+ # 删除文件
311
+ conn.delete_file("/pub/old_file.txt")
312
+
313
+ # 删除空文件夹
314
+ conn.delete_folder("/pub/empty_folder")
315
+
316
+ # 递归删除(文件夹及其内容)
317
+ conn.delete_recursive("/pub/to_delete")
318
+ ```
319
+
320
+ ### FTP 完整示例
321
+
322
+ ```python
323
+ from ddddtools import ftp, logger
324
+
325
+ def backup_website():
326
+ """备份网站文件"""
327
+ logger.info("开始备份")
328
+
329
+ with ftp.connect("ftp.yoursite.com", username="admin", password="pass") as conn:
330
+ # 获取文件列表
331
+ files = conn.list_all("/public_html", recursive=True)
332
+ logger.info(f"共找到 {len(files)} 个文件")
333
+
334
+ # 下载
335
+ conn.download_folder(
336
+ remote_folder="/public_html",
337
+ local_folder="./backup"
338
+ )
339
+
340
+ logger.info("备份完成")
341
+
342
+ backup_website()
343
+ ```
344
+
345
+ ---
346
+
347
+ ## 邮件发送 (mail)
348
+
349
+ ### 快速发送
350
+
351
+ ```python
352
+ from ddddtools import mail
353
+
354
+ # 发送普通文本邮件
355
+ mail.send_simple(
356
+ to_addrs=["admin@example.com", "support@example.com"],
357
+ subject="系统通知",
358
+ content="您的订单已发货,请注意查收。",
359
+ username="123456@qq.com",
360
+ password="your授权码"
361
+ )
362
+
363
+ # 发送HTML邮件
364
+ mail.send_html(
365
+ to_addrs=["user@example.com"],
366
+ subject="欢迎注册",
367
+ html_content="<h1>欢迎使用 ddddtools</h1><p>感谢您的注册</p>",
368
+ username="123456@qq.com",
369
+ password="your授权码"
370
+ )
371
+
372
+ # 发送带附件的邮件
373
+ mail.send_with_attachment(
374
+ to_addrs=["boss@example.com"],
375
+ subject="月度报告",
376
+ content="请查收附件中的月度报告。",
377
+ attachments=[
378
+ "/path/to/report.pdf",
379
+ "/path/to/summary.xlsx"
380
+ ],
381
+ username="123456@qq.com",
382
+ password="your授权码"
383
+ )
384
+ ```
385
+
386
+ ### 使用 MailClient
387
+
388
+ ```python
389
+ from ddddtools import mail
390
+
391
+ # 创建客户端(重复发送时更高效)
392
+ client = mail.create_client(
393
+ username="123456@qq.com",
394
+ password="your授权码",
395
+ smtp_host="smtp.qq.com",
396
+ smtp_port=465
397
+ )
398
+
399
+ # 多次发送
400
+ client.send(
401
+ to_addrs=["user1@example.com"],
402
+ subject="通知1",
403
+ content="内容1"
404
+ )
405
+
406
+ client.send(
407
+ to_addrs=["user2@example.com"],
408
+ subject="通知2",
409
+ content="内容2",
410
+ is_html=True,
411
+ attachments=["file.pdf"]
412
+ )
413
+ ```
414
+
415
+ ### 参数说明
416
+
417
+ | 参数 | 说明 |
418
+ |------|------|
419
+ | `to_addrs` | 收件人列表,支持多个 |
420
+ | `subject` | 邮件主题 |
421
+ | `content` | 邮件内容(普通或HTML) |
422
+ | `is_html` | 是否为HTML格式,默认False |
423
+ | `attachments` | 附件路径列表 |
424
+ | `username` | 发件人邮箱 |
425
+ | `password` | SMTP授权码(不是密码) |
426
+ | `smtp_host` | SMTP服务器,默认 smtp.qq.com |
427
+ | `smtp_port` | SMTP端口,默认 465 |
428
+
429
+ ### QQ邮箱授权码获取
430
+
431
+ 1. 登录 QQ 邮箱
432
+ 2. 进入「设置」→「账户」
433
+ 3. 找到「POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务」
434
+ 4. 开启「POP3/SMTP服务」
435
+ 5. 点击「生成授权码」
436
+
437
+ ---
438
+
439
+ ## HTML模板 (mail.template)
440
+
441
+ ### 8种内置模板
442
+
443
+ | 模板名 | 用途 | 变量 |
444
+ |--------|------|------|
445
+ | `simple` | 基础模板 | `content`, `now` |
446
+ | `notification` | 通知/公告 | `title`, `content`, `button_url`, `button_text`, `highlights` |
447
+ | `verification` | 验证码 | `title`, `description`, `code` |
448
+ | `welcome` | 欢迎邮件 | `username`, `site_name`, `message`, `feature1`, `feature2`, `feature3` |
449
+ | `data_table` | 表格数据 | `title`, `description`, `table_html`, `summary` |
450
+ | `order` | 订单通知 | `username`, `order_no`, `order_time`, `status`, `tracking_no`, `total_amount`, `order_details` |
451
+ | `password_reset` | 密码重置 | `username`, `reset_url`, `expire_time` |
452
+ | `report` | 周报/月报 | `report_title`, `period`, `metrics_html`, `highlights_html`, `notes` |
453
+
454
+ ### 基础用法
455
+
456
+ ```python
457
+ from ddddtools.mail.template import render
458
+
459
+ # 渲染模板
460
+ html = render("simple", content="这是一封测试邮件")
461
+ html = render("notification", title="重要通知", content="您的账户已更新")
462
+ ```
463
+
464
+ ### 验证码模板
465
+
466
+ ```python
467
+ from ddddtools.mail.template import render, mail
468
+
469
+ html = render(
470
+ "verification",
471
+ title="验证码",
472
+ description="您的验证码如下,请于5分钟内完成验证:",
473
+ code="852741"
474
+ )
475
+
476
+ mail.send_html(
477
+ to_addrs=["user@example.com"],
478
+ subject="验证码",
479
+ html_content=html,
480
+ username="123456@qq.com",
481
+ password="授权码"
482
+ )
483
+ ```
484
+
485
+ ### 订单通知模板
486
+
487
+ ```python
488
+ from ddddtools.mail.template import render, mail
489
+
490
+ html = render(
491
+ "order",
492
+ username="张三",
493
+ order_no="DD20260207001",
494
+ order_time="2026-02-07 14:30:00",
495
+ status="已发货",
496
+ tracking_no="SF1234567890",
497
+ total_amount="299.00"
498
+ )
499
+
500
+ mail.send_html(
501
+ to_addrs=["user@example.com"],
502
+ subject="订单已发货",
503
+ html_content=html,
504
+ username="123456@qq.com",
505
+ password="授权码"
506
+ )
507
+ ```
508
+
509
+ ### 数据表格模板
510
+
511
+ ```python
512
+ from ddddtools.mail.template import render, mail, make_table_html
513
+
514
+ # 生成表格HTML
515
+ table = make_table_html(
516
+ headers=["姓名", "数学", "语文", "英语"],
517
+ rows=[
518
+ ["张三", 95, 88, 92],
519
+ ["李四", 85, 90, 87],
520
+ ["王五", 92, 85, 94]
521
+ ]
522
+ )
523
+
524
+ html = render(
525
+ "data_table",
526
+ title="期末考试成绩",
527
+ description="本次考试共3人参加",
528
+ table_html=table,
529
+ summary="平均分: 90.2"
530
+ )
531
+ ```
532
+
533
+ ### 指标卡片模板
534
+
535
+ ```python
536
+ from ddddtools.mail.template import render, mail, make_metrics_html
537
+
538
+ # 生成指标HTML
539
+ metrics = make_metrics_html({
540
+ "总访问量": {"value": "12,345", "change": "+15%", "type": "up"},
541
+ "新增用户": {"value": "528", "change": "+8%", "type": "up"},
542
+ "转化率": {"value": "3.2%", "change": "-0.5%", "type": "down"},
543
+ "流失率": {"value": "5.1%", "change": "0%", "type": "neutral"}
544
+ })
545
+
546
+ html = render(
547
+ "report",
548
+ report_title="周报",
549
+ period="2026-02-01 ~ 2026-02-07",
550
+ metrics_html=metrics
551
+ )
552
+ ```
553
+
554
+ ### 辅助函数
555
+
556
+ ```python
557
+ from ddddtools.mail.template import make_table_html, make_metrics_html, make_highlights_html
558
+
559
+ # 表格
560
+ make_table_html(
561
+ headers=["列1", "列2", "列3"],
562
+ rows=[["a", "b", "c"], ["d", "e", "f"]]
563
+ )
564
+
565
+ # 指标(up=绿色,down=红色,neutral=黑色)
566
+ make_metrics_html({
567
+ "指标名": {"value": "数值", "change": "+10%", "type": "up"}
568
+ })
569
+
570
+ # 亮点列表
571
+ make_highlights_html(["亮点1", "亮点2", "亮点3"])
572
+ ```
573
+
574
+ ### 注册自定义模板
575
+
576
+ ```python
577
+ from ddddtools.mail.template import register
578
+
579
+ # 注册模板
580
+ register("newsletter", """
581
+ <!DOCTYPE html>
582
+ <html>
583
+ <body>
584
+ <h1>{{title}}</h1>
585
+ <div>{{content}}</div>
586
+ <footer>unsubscribe at {{unsubscribe_url}}</footer>
587
+ </body>
588
+ </html>
589
+ """)
590
+
591
+ # 使用
592
+ html = render("newsletter", title="新闻简报", content="...", unsubscribe_url="...")
593
+ ```
594
+
595
+ ---
596
+
597
+ ## 缓存管理 (redis)
598
+
599
+ ### 连接管理
600
+
601
+ ```python
602
+ from ddddtools import connect, RedisClient
603
+
604
+ # 方式一:使用 connect 函数
605
+ redis = connect(host="localhost", port=6379, db=0, password="your_password")
606
+
607
+ # 方式二:使用连接字符串
608
+ redis = RedisClient(host="redis.example.com", port=6379, password="pass")
609
+
610
+ # 测试连接
611
+ redis.ping() # True/False
612
+
613
+ # 使用原生 Redis 客户端
614
+ redis.client.set("key", "value")
615
+ ```
616
+
617
+ ### 基础操作
618
+
619
+ ```python
620
+ from ddddtools import redis
621
+
622
+ r = redis.connect(host="localhost", password="your_password")
623
+
624
+ # 字符串操作
625
+ r.set("name", "张三", ex=3600) # 1小时过期
626
+ value = r.get("name")
627
+
628
+ # 检查存在
629
+ r.exists("name") # 返回数量
630
+
631
+ # 设置过期
632
+ r.expire("name", 1800) # 30秒
633
+ print(r.ttl("name")) # 查看剩余时间
634
+
635
+ # 删除
636
+ r.delete("name")
637
+ ```
638
+
639
+ ### Hash 操作
640
+
641
+ ```python
642
+ r = redis.connect()
643
+
644
+ # 设置 Hash
645
+ r.hset("user:1", "name", "张三")
646
+ r.hset("user:1", "age", "25")
647
+
648
+ # 获取字段
649
+ r.hget("user:1", "name") # "张三"
650
+
651
+ # 获取全部
652
+ r.hgetall("user:1") # {'name': '张三', 'age': '25'}
653
+
654
+ # 删除字段
655
+ r.hdel("user:1", "age")
656
+ ```
657
+
658
+ ### List 操作
659
+
660
+ ```python
661
+ r = redis.connect()
662
+
663
+ # 插入
664
+ r.lpush("queue", "task1", "task2")
665
+ r.rpush("queue", "task3")
666
+
667
+ # 获取
668
+ r.lrange("queue", 0, -1) # ['task2', 'task1', 'task3']
669
+ r.lpop("queue") # 'task2'
670
+ r.llen("queue") # 长度
671
+ ```
672
+
673
+ ### 缓存装饰器
674
+
675
+ ```python
676
+ from ddddtools import cache
677
+
678
+ # 简单缓存
679
+ @cache(key="user:{user_id}", expire=600)
680
+ def get_user(user_id: int):
681
+ # 首次调用:执行函数并缓存结果
682
+ return db.query_user(user_id)
683
+ # 后续调用:直接从 Redis 返回缓存
684
+
685
+ # 动态键
686
+ @cache(key="{id}", expire=300)
687
+ def get_data(id: int):
688
+ return api.fetch(id)
689
+
690
+ # 清除缓存
691
+ from ddddtools import clear_cache, clear_prefix
692
+
693
+ clear_cache(key="user:123") # 清除单个
694
+ clear_prefix(prefix="cache") # 清除前缀(谨慎)
695
+ ```
696
+
697
+ ### 缓存装饰器参数
698
+
699
+ | 参数 | 类型 | 默认值 | 说明 |
700
+ |------|------|--------|------|
701
+ | `key` | str | 空 | 缓存键,支持 `{func_name}`, `{args[i]}`, `{kwargs[key]}` |
702
+ | `expire` | int | 300 | 过期时间(秒) |
703
+ | `prefix` | str | "cache" | 缓存前缀 |
704
+
705
+ ---
706
+
707
+ ## 加密模块 (encryption)
708
+
709
+ ### AES加密
710
+
711
+ ```python
712
+ from ddddtools import AESEncrypt, encrypt_aes, decrypt_aes
713
+
714
+ # 方式一:使用类
715
+ aes = AESEncrypt(key="16位密钥字符串")
716
+ encrypted = aes.encrypt("Hello World")
717
+ decrypted = aes.decrypt(encrypted)
718
+
719
+ # 方式二:快速函数
720
+ encrypted = encrypt_aes("敏感数据", "密钥")
721
+ decrypted = decrypt_aes(encrypted, "密钥")
722
+ ```
723
+
724
+ ### RSA加密
725
+
726
+ ```python
727
+ from ddddtools import RSAEncrypt, generate_rsa_keys, encrypt_rsa, decrypt_rsa
728
+
729
+ # 生成密钥对
730
+ private_key, public_key = generate_rsa_keys(key_size=2048)
731
+
732
+ # 加密/解密
733
+ rsa = RSAEncrypt(private_key=private_key, public_key=public_key)
734
+ encrypted = rsa.encrypt("秘密消息")
735
+ decrypted = rsa.decrypt(encrypted)
736
+
737
+ # 快速函数
738
+ encrypted = encrypt_rsa("消息", public_key)
739
+ decrypted = decrypt_rsa(encrypted, private_key)
740
+ ```
741
+
742
+ ### RSA签名/验签
743
+
744
+ ```python
745
+ from ddddtools import sign_data, verify_signature
746
+
747
+ # 签名
748
+ private_key = "-----BEGIN RSA PRIVATE KEY-----..."
749
+ signature = sign_data("要签名的数据", private_key, hash_method="sha256")
750
+
751
+ # 验签
752
+ public_key = "-----BEGIN PUBLIC KEY-----..."
753
+ is_valid = verify_signature("要验证的数据", signature, public_key)
754
+ # 返回 True/False
755
+ ```
756
+
757
+ ### 参数说明
758
+
759
+ | 功能 | 参数 | 说明 |
760
+ |------|------|------|
761
+ | `generate_rsa_keys` | key_size | 密钥长度(1024/2048/4096)|
762
+ | `AESEncrypt` | key | 16/24/32字节(对应AES-128/192/256)|
763
+ | `sign_data` | hash_method | 哈希算法(md5/sha1/sha256)|
764
+
765
+ ---
766
+
767
+ ## MongoDB数据库 (mongodb)
768
+
769
+ ### 连接管理
770
+
771
+ ```python
772
+ from ddddtools import MongoDB, mongo_connect
773
+
774
+ # 方式一:使用 connect 函数
775
+ db = mongo_connect("mongodb://localhost:27017", db_name="myapp")
776
+
777
+ # 方式二:使用类
778
+ mongo = MongoDB("mongodb://localhost:27017")
779
+ mongo.set_db("myapp")
780
+
781
+ # 测试连接
782
+ print(mongo.ping()) # True/False
783
+
784
+ mongo.close()
785
+ ```
786
+
787
+ ### 集合操作
788
+
789
+ ```python
790
+ from ddddtools import mongo_connect, MongoCollection
791
+
792
+ db = mongo_connect(db_name="testdb")
793
+ users = db.get_collection("users") # 返回 MongoCollection 对象
794
+
795
+ # 插入
796
+ id = users.insert_one({"name": "张三", "age": 25})
797
+ ids = users.insert_many([
798
+ {"name": "李四", "age": 30},
799
+ {"name": "王五", "age": 28}
800
+ ])
801
+
802
+ # 查询
803
+ user = users.find_one({"name": "张三"})
804
+ user = users.find_by_id("507f1f77bcf86cd799439011") # 根据ID查询
805
+
806
+ all_users = users.find_all() # 查询所有
807
+ all_users = users.find_all({"age": {"$gt": 25}}) # 条件查询
808
+ all_users = users.find_all(sort=[("age", -1)], limit=10) # 排序和限制
809
+
810
+ count = users.count({"name": "张三"}) # 统计数量
811
+ exists = users.exists({"name": "李四"}) # 判断是否存在
812
+
813
+ # 更新
814
+ users.update_one({"name": "张三"}, {"age": 26}) # 更新单个
815
+ users.update_by_id("507f1f77bcf86cd799439011", {"age": 27}) # 根据ID更新
816
+ users.increment({"name": "李四"}, "age", 1) # 字段自增
817
+
818
+ # 删除
819
+ users.delete_one({"name": "王五"}) # 删除单个
820
+ users.delete_by_id("507f1f77bcf86cd799439011") # 根据ID删除
821
+ users.delete_many({"status": "inactive"}) # 删除多个
822
+
823
+ # 聚合
824
+ pipeline = [
825
+ {"$match": {"age": {"$gte": 25}}},
826
+ {"$group": {"_id": None, "avg_age": {"$avg": "$age"}, "count": {"$sum": 1}}}
827
+ ]
828
+ result = users.aggregate(pipeline)
829
+
830
+ # 索引
831
+ users.create_index([("name", 1)]) # 普通索引
832
+ users.create_unique_index("email") # 唯一索引
833
+ ```
834
+
835
+ ### MongoCollection 方法
836
+
837
+ | 方法 | 说明 |
838
+ |------|------|
839
+ | `insert_one(doc)` | 插入单个文档,返回ID |
840
+ | `insert_many(docs)` | 插入多个文档,返回ID列表 |
841
+ | `find_one(query)` | 查询单个文档 |
842
+ | `find_by_id(id)` | 根据ID查询 |
843
+ | `find_all(query, sort, limit)` | 查询多个文档 |
844
+ | `count(query)` | 统计数量 |
845
+ | `exists(query)` | 判断是否存在 |
846
+ | `update_one(query, update)` | 更新单个 |
847
+ | `update_by_id(id, update)` | 根据ID更新 |
848
+ | `update_many(query, update)` | 更新多个 |
849
+ | `increment(query, field, amount)` | 字段自增 |
850
+ | `delete_one(query)` | 删除单个 |
851
+ | `delete_by_id(id)` | 根据ID删除 |
852
+ | `delete_many(query)` | 删除多个 |
853
+ | `aggregate(pipeline)` | 聚合查询 |
854
+ | `create_index(keys)` | 创建索引 |
855
+ | `create_unique_index(field)` | 创建唯一索引 |
856
+
857
+ ---
858
+
859
+ ## 文件结构
860
+
861
+ ```
862
+ ddddtools/
863
+ ├── pyproject.toml # Python项目配置
864
+ ├── README.md # 本文档
865
+ ├── .gitignore # Git忽略配置
866
+ └── src/ddddtools/
867
+ ├── __init__.py # 统一导出所有功能
868
+ ├── logging/ # 日志模块
869
+ │ └── __init__.py # 自动日志、get_logger
870
+ ├── decorator/ # 装饰器
871
+ │ ├── __init__.py # timer, log_call
872
+ │ ├── timer.py # 耗时统计
873
+ │ └── log_call.py # 调用日志
874
+ ├── ftp/ # FTP操作
875
+ │ ├── __init__.py # 导出所有FTP函数
876
+ │ ├── connection.py # 连接管理
877
+ │ ├── list.py # 文件列表
878
+ │ ├── download.py # 下载
879
+ │ ├── upload.py # 上传
880
+ │ └── delete.py # 删除
881
+ ├── mail/ # 邮件发送
882
+ │ ├── __init__.py # send_simple, send_html, send_with_attachment
883
+ │ └── template/ # HTML模板
884
+ │ └── __init__.py # 8种模板 + 辅助函数
885
+ ├── redis/ # 缓存管理
886
+ │ ├── __init__.py # RedisClient, connect, cache, clear_cache
887
+ │ └── decorator.py # 缓存装饰器
888
+ ├── encryption/ # 加密模块
889
+ │ ├── __init__.py # RSAEncrypt, AESEncrypt
890
+ │ ├── rsa.py # RSA加解密、签名验签
891
+ │ └── aes.py # AES加解密
892
+ ├── mongodb/ # MongoDB数据库
893
+ │ ├── __init__.py # MongoDB, connect, MongoCollection
894
+ │ └── collection.py # 集合操作
895
+ ├── file/ # (预留) 文件操作
896
+ ├── string/ # (预留) 字符串处理
897
+ ├── system/ # (预留) 系统工具
898
+ └── datetime/ # (预留) 日期时间
899
+ ```
900
+
901
+ ---
902
+
903
+ ## 常见问题
904
+
905
+ ### Q: 日志文件在哪里?
906
+
907
+ 默认在运行目录下创建 `logs` 文件夹。可通过 `get_logger(log_dir="/path")` 自定义。
908
+
909
+ ### Q: 如何更改日志保留天数?
910
+
911
+ ```python
912
+ logger = get_logger(days=30) # 保留30天
913
+ ```
914
+
915
+ ### Q: log_call 如何自定义日志名?
916
+
917
+ ```python
918
+ @log_call(name="MyFunction")
919
+ def my_func(): ...
920
+ ```
921
+
922
+ ### Q: FTP 连接失败?
923
+
924
+ - 检查用户名密码是否正确
925
+ - 检查服务器地址和端口
926
+ - 确保防火墙开放 FTP 端口(21)
927
+ - 尝试使用主动模式:`ftp.connect(...)._ftp.set_pasv(False)`
928
+
929
+ ### Q: QQ 邮箱发送失败?
930
+
931
+ - 确保已开启 POP3/SMTP 服务
932
+ - 使用授权码而非登录密码
933
+ - 确认 SMTP 地址和端口(smtp.qq.com:465)
934
+
935
+ ### Q: 如何调试 log_call?
936
+
937
+ 设置环境变量或查看控制台输出,它会显示参数和返回值。
938
+
939
+ ### Q: 支持异步吗?
940
+
941
+ 当前版本为同步实现。如需异步支持,可使用 `asyncio.to_thread` 包装。
942
+
943
+ ### Q: Redis 连接失败?
944
+
945
+ - 检查 Redis 服务是否启动
946
+ - 确认 host、port、password 是否正确
947
+ - 检查防火墙是否开放 6379 端口
948
+
949
+ ### Q: 缓存装饰器如何动态生成键?
950
+
951
+ ```python
952
+ @cache(key="{user_id}:{page}", expire=300)
953
+ def get_user_posts(user_id: int, page: int):
954
+ return db.query_posts(user_id, page)
955
+ ```
956
+
957
+ ### Q: 如何查看当前缓存?
958
+
959
+ 直接使用 `redis.client.keys("cache:*")` 或 `scan_iter` 遍历。
960
+
961
+ ---
962
+
963
+ ## License
964
+
965
+ MIT License