skills/ifind-http-api/scripts/query_pension.py
wangyitong a65adcc2e5 Initial commit: merged, deduplicated, and vetted skill collection
Sources: extracted from two upstream archives (skill-repo, skills-main),
merged with the following policy:

- 15 broken symlinks (pointing to /Users/jameslee/.cc-switch/skills or
  ../../.agents/skills on a foreign machine) discarded
- 3 real name collisions with identical content (ai-pair, ifind-http-api,
  zhipu-websearch) kept as one copy
- Functional overlaps deduped keeping the strongest variant:
  - docx family: kept docx (official, full toolchain) + docx-cn
    (GB/T 9704 Chinese official-document constants),
    dropped docx_writer (no scripts, name collided with docx)
  - humanizer family: kept humanizer-zh (6 zh reference docs),
    dropped humanizer (en, redundant for CN workflow)
- Skills that only ran in a foreign environment removed:
  ablemind-ops, app-publish, hlb-design-system, openclaw-adj-skill,
  claude-driver
- alphapai excluded from this public repo because its SKILL.md hard-coded
  live credentials

Result: 25 skills, 572 files, ~7.5 MB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 14:47:12 +08:00

389 lines
12 KiB
Python

#!/usr/bin/env python3
"""
养老金产品 SQLite 查询脚本
提供命令行和函数接口查询养老金产品数据库。
用法:
# 按名称模糊搜索(全文搜索)
python query_pension.py search "平安"
python query_pension.py search "稳健增值"
# 按代码精确查找
python query_pension.py code PAA001.YLJ
# 按管理人筛选
python query_pension.py manager PA
python query_pension.py manager 平安养老
# 按产品类型筛选
python query_pension.py type A # 权益型
python query_pension.py type C # 债券型
python query_pension.py type Cb # 债券型-信用债子类
# 统计概览
python query_pension.py stats
# 导出全部数据为 CSV
python query_pension.py export pension_products.csv
# 组合筛选(管理人 + 类型)
python query_pension.py filter --manager PA --type A
# 自定义 SQL 查询
python query_pension.py sql "SELECT code, name FROM pension_products WHERE name LIKE '%增值%'"
"""
import argparse
import csv
import os
import sqlite3
import sys
from pathlib import Path
def get_db_path(custom_path: str = None) -> str:
"""获取数据库路径。"""
if custom_path:
return custom_path
default = Path(__file__).parent.parent / "data" / "pension_products.db"
if default.exists():
return str(default)
raise FileNotFoundError(
f"数据库不存在: {default}\n请先运行 build_pension_db.py 构建数据库"
)
def get_conn(db_path: str) -> sqlite3.Connection:
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
return conn
# ---------- 查询函数(可被外部 import 使用) ----------
def search(db_path: str, keyword: str, limit: int = 50) -> list[dict]:
"""全文搜索:按名称、代码、管理人等关键词搜索。"""
conn = get_conn(db_path)
# 先尝试 FTS5 全文搜索
try:
rows = conn.execute("""
SELECT p.* FROM pension_products p
JOIN pension_fts f ON p.rowid = f.rowid
WHERE pension_fts MATCH ?
ORDER BY rank
LIMIT ?
""", (keyword, limit)).fetchall()
if rows:
return [dict(r) for r in rows]
except sqlite3.OperationalError:
pass
# 回退到 LIKE 搜索
like_kw = f"%{keyword}%"
rows = conn.execute("""
SELECT * FROM pension_products
WHERE name LIKE ? OR official_name LIKE ? OR code LIKE ?
OR manager_name LIKE ? OR product_type_name LIKE ?
ORDER BY code
LIMIT ?
""", (like_kw, like_kw, like_kw, like_kw, like_kw, limit)).fetchall()
conn.close()
return [dict(r) for r in rows]
def get_by_code(db_path: str, code: str) -> dict | None:
"""按代码精确查找。"""
if not code.endswith(".YLJ"):
code += ".YLJ"
conn = get_conn(db_path)
row = conn.execute("SELECT * FROM pension_products WHERE code = ?", (code,)).fetchone()
conn.close()
return dict(row) if row else None
def get_by_manager(db_path: str, manager: str, limit: int = 200) -> list[dict]:
"""按管理人查询。支持前缀代码或中文名。"""
conn = get_conn(db_path)
rows = conn.execute("""
SELECT * FROM pension_products
WHERE manager_prefix = ? OR manager_name LIKE ?
ORDER BY product_type, code
LIMIT ?
""", (manager, f"%{manager}%", limit)).fetchall()
conn.close()
return [dict(r) for r in rows]
def get_by_type(db_path: str, ptype: str, limit: int = 500) -> list[dict]:
"""按产品类型查询。ptype: A/B/C/D 或 Ca/Cb/Cf 等子类。"""
conn = get_conn(db_path)
if len(ptype) == 1:
rows = conn.execute("""
SELECT * FROM pension_products
WHERE product_type = ?
ORDER BY manager_prefix, code
LIMIT ?
""", (ptype.upper(), limit)).fetchall()
else:
rows = conn.execute("""
SELECT * FROM pension_products
WHERE bond_subtype = ?
ORDER BY manager_prefix, code
LIMIT ?
""", (ptype, limit)).fetchall()
conn.close()
return [dict(r) for r in rows]
def filter_products(db_path: str, manager: str = None, ptype: str = None,
keyword: str = None, limit: int = 500) -> list[dict]:
"""组合筛选。"""
conn = get_conn(db_path)
conditions = []
params = []
if manager:
conditions.append("(manager_prefix = ? OR manager_name LIKE ?)")
params.extend([manager, f"%{manager}%"])
if ptype:
if len(ptype) == 1:
conditions.append("product_type = ?")
params.append(ptype.upper())
else:
conditions.append("bond_subtype = ?")
params.append(ptype)
if keyword:
conditions.append("(name LIKE ? OR official_name LIKE ?)")
params.extend([f"%{keyword}%", f"%{keyword}%"])
where = " AND ".join(conditions) if conditions else "1=1"
params.append(limit)
rows = conn.execute(f"""
SELECT * FROM pension_products
WHERE {where}
ORDER BY manager_prefix, product_type, code
LIMIT ?
""", params).fetchall()
conn.close()
return [dict(r) for r in rows]
def get_stats(db_path: str) -> dict:
"""获取数据库统计概览。"""
conn = get_conn(db_path)
total = conn.execute("SELECT COUNT(*) FROM pension_products").fetchone()[0]
type_stats = conn.execute("""
SELECT product_type, product_type_name, COUNT(*) as cnt
FROM pension_products
GROUP BY product_type
ORDER BY cnt DESC
""").fetchall()
manager_stats = conn.execute("""
SELECT manager_prefix, manager_name, COUNT(*) as cnt
FROM pension_products
GROUP BY manager_prefix
ORDER BY cnt DESC
""").fetchall()
bond_stats = conn.execute("""
SELECT bond_subtype, bond_subtype_name, COUNT(*) as cnt
FROM pension_products
WHERE bond_subtype IS NOT NULL
GROUP BY bond_subtype
ORDER BY cnt DESC
""").fetchall()
last_updated = conn.execute(
"SELECT MAX(last_updated) FROM pension_products"
).fetchone()[0]
conn.close()
return {
"total": total,
"last_updated": last_updated,
"by_type": [dict(r) for r in type_stats],
"by_manager": [dict(r) for r in manager_stats],
"by_bond_subtype": [dict(r) for r in bond_stats],
}
def export_csv(db_path: str, output_path: str):
"""导出全部数据为 CSV。"""
conn = get_conn(db_path)
rows = conn.execute("SELECT * FROM pension_products ORDER BY code").fetchall()
conn.close()
if not rows:
print("数据库为空")
return
with open(output_path, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=rows[0].keys())
writer.writeheader()
for row in rows:
writer.writerow(dict(row))
print(f"已导出 {len(rows)} 条记录到 {output_path}")
def run_sql(db_path: str, query: str, limit: int = 100) -> list[dict]:
"""执行自定义 SQL 查询。"""
conn = get_conn(db_path)
rows = conn.execute(query).fetchall()[:limit]
conn.close()
return [dict(r) for r in rows]
# ---------- 格式化输出 ----------
def print_products(products: list[dict], verbose: bool = False):
"""格式化打印产品列表。"""
if not products:
print("未找到匹配的产品")
return
print(f"\n{len(products)} 个产品:")
print(f"{'代码':<18} {'名称':<30} {'管理人':<14} {'类型':<10} {'成立日':<12}")
print("-" * 84)
for p in products:
name = (p.get("name") or p.get("official_name") or "")[:28]
mgr = (p.get("manager_name") or p.get("manager_prefix") or "")[:12]
ptype = p.get("product_type_name", "")
if p.get("bond_subtype"):
ptype = f"{ptype}({p['bond_subtype']})"
est = p.get("establishment_date") or ""
print(f"{p['code']:<18} {name:<30} {mgr:<14} {ptype:<10} {est:<12}")
if verbose and products:
print(f"\n详细信息 (第一条):")
for k, v in products[0].items():
if v is not None:
print(f" {k}: {v}")
def print_stats(stats: dict):
"""格式化打印统计信息。"""
print(f"\n养老金产品数据库概览")
print(f"{'=' * 50}")
print(f"总产品数: {stats['total']}")
print(f"最后更新: {stats['last_updated']}")
print(f"\n按产品类型:")
for t in stats["by_type"]:
print(f" {t['product_type_name']} ({t['product_type']}): {t['cnt']}")
print(f"\n按管理人:")
for m in stats["by_manager"]:
print(f" {m['manager_name']} ({m['manager_prefix']}): {m['cnt']}")
if stats["by_bond_subtype"]:
print(f"\n债券型子类分布:")
for b in stats["by_bond_subtype"]:
name = b["bond_subtype_name"] or b["bond_subtype"]
print(f" {b['bond_subtype']} ({name}): {b['cnt']}")
# ---------- CLI ----------
def main():
parser = argparse.ArgumentParser(description="养老金产品数据库查询工具")
parser.add_argument("--db", default=None, help="数据库路径")
parser.add_argument("-v", "--verbose", action="store_true", help="详细输出")
sub = parser.add_subparsers(dest="command", help="查询命令")
# search
p_search = sub.add_parser("search", help="全文搜索")
p_search.add_argument("keyword", help="搜索关键词")
p_search.add_argument("-n", "--limit", type=int, default=50)
# code
p_code = sub.add_parser("code", help="按代码查找")
p_code.add_argument("code", help="产品代码 (如 PAA001.YLJ)")
# manager
p_mgr = sub.add_parser("manager", help="按管理人筛选")
p_mgr.add_argument("manager", help="管理人前缀或名称")
# type
p_type = sub.add_parser("type", help="按产品类型筛选")
p_type.add_argument("ptype", help="类型: A/B/C/D 或子类 Ca/Cb/Cf...")
# filter
p_filter = sub.add_parser("filter", help="组合筛选")
p_filter.add_argument("--manager", "-m", help="管理人")
p_filter.add_argument("--type", "-t", dest="ptype", help="类型")
p_filter.add_argument("--keyword", "-k", help="名称关键词")
# stats
sub.add_parser("stats", help="统计概览")
# export
p_export = sub.add_parser("export", help="导出 CSV")
p_export.add_argument("output", help="输出文件路径")
# sql
p_sql = sub.add_parser("sql", help="自定义 SQL 查询")
p_sql.add_argument("query", help="SQL 语句")
args = parser.parse_args()
if not args.command:
parser.print_help()
sys.exit(0)
try:
db_path = get_db_path(args.db)
except FileNotFoundError as e:
print(e)
sys.exit(1)
if args.command == "search":
results = search(db_path, args.keyword, args.limit)
print_products(results, args.verbose)
elif args.command == "code":
result = get_by_code(db_path, args.code)
if result:
print_products([result], verbose=True)
else:
print(f"未找到产品: {args.code}")
elif args.command == "manager":
results = get_by_manager(db_path, args.manager)
print_products(results, args.verbose)
elif args.command == "type":
results = get_by_type(db_path, args.ptype)
print_products(results, args.verbose)
elif args.command == "filter":
results = filter_products(db_path, args.manager, args.ptype, args.keyword)
print_products(results, args.verbose)
elif args.command == "stats":
stats = get_stats(db_path)
print_stats(stats)
elif args.command == "export":
export_csv(db_path, args.output)
elif args.command == "sql":
results = run_sql(db_path, args.query)
if results:
# 简单表格输出
keys = list(results[0].keys())
print("\t".join(keys))
for r in results:
print("\t".join(str(r.get(k, "")) for k in keys))
print(f"\n{len(results)} 条结果")
else:
print("无结果")
if __name__ == "__main__":
main()