auto: sync OpenClaw config 2026-08-08 23:17
This commit is contained in:
@@ -47,6 +47,31 @@
|
||||
|
||||
## 2026年数据进展
|
||||
|
||||
- **2026-07 王芳上半年支付宝账单**(1/01–6/30,452笔)
|
||||
- 净支出229笔¥20,909.23 | 收入8笔¥1,029.34 | 退货/免计223笔
|
||||
- 分类:餐饮/食品¥5,084.51(24.3%) | 教育培训¥4,500(21.5%,新星幼教中心单笔) | 购物¥3,242.69(15.5%) | 交通¥2,788.15(13.3%,12306火车票¥1,374) | 家居¥2,562.49(12.3%,床垫¥1,780+工学椅¥537) | 医疗¥1,247.54(6%) | 其他小额
|
||||
- 单笔最大:新星幼教中心培训费¥4,500(3/2)| 梦百合床垫¥1,780(2/26)
|
||||
- 注:剔除交易关闭/退款订单;源报告支出¥20,615.94与实算¥20,909.23有小差异约¥293
|
||||
- 大量含邮储银行储蓄卡(8719)支付
|
||||
- 历史:王芳2025年已存有支付宝等数据(¥116K级别)
|
||||
|
||||
- **2026-07 王芳邮储卡(8719)上半年流水**(2/05–6/30,458笔)
|
||||
- 收入103笔¥59,936.55 | 支出355笔¥59,760.32(卡内多为资金通道)
|
||||
- **去重决策**:支付宝渠道130笔¥22,658(与支付宝账单重复)、微信渠道18笔¥15,079(另计)、内部自转/家庭内部转¥9,351均不计入;仅导入「外部消费」195笔¥11,315.77 + 还款另行标注
|
||||
- 外部消费分类:其他(银联快捷直付)¥7,823 | 餐饮/食品¥2,334 | 购物¥795 | 运动¥284 | 交通¥55 | 母婴¥25
|
||||
- 外部消费大额:赵风刚¥2,400(6/13)| 王中富¥325 | 银联快捷多笔
|
||||
- 还款记录(未计为支出):支付宝信贷/花呗还款¥12,749 + 美团月付¥921 = ¥13,671
|
||||
- 家庭内部转出:给杨轩¥2,186(1,246+640+300)
|
||||
- 王芳2026H1支付宝+邮储卡去重合计:424笔¥32,225
|
||||
|
||||
- **2026-07 王芳微信账单(w💃卷儿)**(1/02–6/30,426笔)
|
||||
- 支出305笔¥45,385.93 | 收入104笔¥30,474.79 | 中性中转17笔¥16,827
|
||||
- **去重**:剔除88笔与支付宝/邮储卡重复(87笔邮储卡支付+1笔零钱通,¥5,485.5,均已在库);导入202笔¥35,825
|
||||
- 支出分类:转账/红包¥11,025 | 其他¥10,918 | 旅游¥10,453(信天游旅行社曹***尾款6,860+押金2,000+新增1,280) | 餐饮/食品¥8,074 | 医疗¥6,808(口腔门诊5,500) | 教育培训¥6,050(股老师暑假班1,100+延时班450) | 购物¥5,017 | 交通¥3,623(汽油/滴滴) | 家居¥2,562 | 保险¥2,159(众安车险)
|
||||
- 微信收入89笔¥26,399(转账红包/收款为主,含J-大姑转等)
|
||||
- **注意**:清除了一笔早前会话导入的重复微信数据(75笔);保留了10笔独有历史记录(¥1,226)
|
||||
- 王芳2026H1全渠道去重总支出:636笔¥69,276.17 | 收入≈¥27,429
|
||||
|
||||
- **2026-07 汪礼平七月微信账单**(7/01–7/31,77笔)
|
||||
- 支出74笔¥3,130.44 | 收入3笔¥610.00
|
||||
- 支出分类:餐饮/食品¥1,709.79 | 转账/红包¥900(杨小平400+老公200+红包给佳艳300)| 购物¥475.85(拼多多/抖音)| 其他¥40(三城寺香火5×10)| 交通¥4.8(铁塔能源)
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import pymysql, json, collections
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
cur.execute("""SELECT id, amount, recorded_date, note FROM variable_expenses
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%' AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'""")
|
||||
rows = cur.fetchall()
|
||||
old = [] # 历史格式 (前导空格格式 [微信] 类型 |)
|
||||
new = [] # 本次导入格式 [微信] 支出|
|
||||
for vid, a, d, n in rows:
|
||||
if n.startswith('[微信] 支出'):
|
||||
new.append((d.strftime('%Y-%m-%d'), round(float(a), 2), vid, n))
|
||||
else:
|
||||
old.append((d.strftime('%Y-%m-%d'), round(float(a), 2), vid, n))
|
||||
print(f"历史微信记录: {len(old)}笔 ¥{sum(x[1] for x in old):.2f}")
|
||||
print(f"本次导入: {len(new)}笔 ¥{sum(x[1] for x in new):.2f}")
|
||||
|
||||
new_set = {(t, a) for t, a, _, _ in new}
|
||||
old_set = {(t, a) for t, a, _, _ in old}
|
||||
overlap = new_set & old_set
|
||||
only_new = new_set - old_set
|
||||
only_old = old_set - new_set
|
||||
print(f"\n重叠(同日同金额,新旧都有): {len(overlap)}笔 ¥{sum(x[1] for x in new):.2f}")
|
||||
print(f"仅本次导入: {len(only_new)}笔")
|
||||
print(f"仅历史: {len(only_old)}笔")
|
||||
conn.close()
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import pymysql, json
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
# 微信记录按年月
|
||||
cur.execute("""SELECT date_format(recorded_date,'%%Y-%%m'), COUNT(*), SUM(amount) FROM variable_expenses
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%' AND recorded_date BETWEEN '2026-01-01' AND '2026-12-31'
|
||||
GROUP BY 1 ORDER BY 1""")
|
||||
print("王芳2026 [微信]支出 按月:")
|
||||
for y, n, a in cur.fetchall():
|
||||
print(f" {y}: {n}笔 ¥{float(a):.2f}")
|
||||
print()
|
||||
# 有多少是今天之前就存在的(即不是本次导入的)— 通过 note 前缀区分
|
||||
cur.execute("""SELECT COUNT(*), SUM(amount) FROM variable_expenses
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%' AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'""")
|
||||
print("2026H1 [微信]支出总计(含历史):", cur.fetchone())
|
||||
conn.close()
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import pymysql, json
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
cur.execute("""SELECT note FROM variable_expenses
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%' AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'""")
|
||||
notes = [r[0] for r in cur.fetchall()]
|
||||
# 分析 note 格式种类
|
||||
import collections
|
||||
fmt = collections.Counter()
|
||||
for n in notes:
|
||||
if n.startswith('[微信] 支出'):
|
||||
fmt['导入格式-支出'] += 1
|
||||
elif '微信' in n and '转账' in n:
|
||||
fmt['含微信+转账'] += 1
|
||||
else:
|
||||
fmt['其他:' + n[:30]] += 1
|
||||
print("note格式分布:")
|
||||
for k, v in fmt.most_common(10):
|
||||
print(f" {v}笔 {k}")
|
||||
# 看几条历史微信的note样例
|
||||
print("\n样例(非本次导入格式):")
|
||||
shown = 0
|
||||
for n in notes:
|
||||
if not n.startswith('[微信] 支出'):
|
||||
print(" ", n[:60])
|
||||
shown += 1
|
||||
if shown >= 5: break
|
||||
conn.close()
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""检查微信账单与已导入的支付宝/邮储卡支出的重叠"""
|
||||
import json, pymysql, collections
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
cur.execute("""SELECT amount, recorded_date FROM variable_expenses
|
||||
WHERE member_id=3 AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
AND (note LIKE '%[支付宝]%' OR note LIKE '%[邮储卡]%')""")
|
||||
imported = set()
|
||||
for a, d in cur.fetchall():
|
||||
imported.add((d.strftime('%Y-%m-%d'), round(float(a), 2)))
|
||||
print(f"已导入支付宝+邮储卡支出: {len(imported)}笔")
|
||||
|
||||
d = json.load(open('/tmp/wx_juaner.json'))
|
||||
def fp(s): return float(s)
|
||||
wx_pay = [(r[0][:10], round(fp(r[5]), 2), r[1].strip(), r[2], r[6].strip())
|
||||
for r in d['records'] if r[4].strip() == '支出']
|
||||
overlap = 0; dupe_amt = 0.0; over_pay = collections.Counter()
|
||||
for t, amt, typ, peer, pay in wx_pay:
|
||||
if (t, amt) in imported:
|
||||
overlap += 1; dupe_amt += amt; over_pay[pay] += 1
|
||||
print(f"微信支出中与已导入(支付宝/邮储卡)同日同金额重叠: {overlap}笔 ¥{dupe_amt:.2f}")
|
||||
print(" 重叠按微信支付方式:", dict(over_pay))
|
||||
for pay in ['邮储银行储蓄卡(8719)', '零钱通', '零钱', '徽商银行储蓄卡(1491)']:
|
||||
sub = [x for x in wx_pay if x[4] == pay]
|
||||
ov = sum(1 for x in sub if (x[0], x[1]) in imported)
|
||||
print(f" {pay}: {len(sub)}笔, 其中与已导入重叠 {ov}笔")
|
||||
conn.close()
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""删除历史[微信]记录中与本次导入重复的(避免双计)"""
|
||||
import pymysql, json
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
cur.execute("""SELECT id, amount, recorded_date, note FROM variable_expenses
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%' AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'""")
|
||||
rows = cur.fetchall()
|
||||
old_ids = [] # 历史记录 id
|
||||
new_keys = set() # 本次导入 (date, amount)
|
||||
for vid, a, d, n in rows:
|
||||
key = (d.strftime('%Y-%m-%d'), round(float(a), 2))
|
||||
if n.startswith('[微信] 支出'):
|
||||
new_keys.add(key)
|
||||
else:
|
||||
old_ids.append((vid, key))
|
||||
# 删除历史中与本次导入重复的
|
||||
to_delete = [vid for vid, key in old_ids if key in new_keys]
|
||||
print(f"待删除重复历史记录: {len(to_delete)}笔")
|
||||
for vid in to_delete:
|
||||
cur.execute("DELETE FROM variable_expenses WHERE id=%s", (vid,))
|
||||
conn.commit()
|
||||
# 删除后剩余历史记录
|
||||
cur.execute("""SELECT COUNT(*), SUM(amount) FROM variable_expenses
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%' AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30' AND note NOT LIKE '[微信] 支出%'""")
|
||||
print("剩余历史微信记录:", cur.fetchone())
|
||||
conn.close()
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""王芳2026H1 最终去重总账"""
|
||||
import pymysql, json
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
|
||||
tags = "(note LIKE '%%[支付宝]%%' OR note LIKE '%%[邮储卡]%%' OR note LIKE '%%[微信]%%')"
|
||||
print("=== 支出分类结构(全渠道去重后) ===")
|
||||
cur.execute(f"""SELECT category, SUM(amount), COUNT(*) FROM variable_expenses
|
||||
WHERE member_id=3 AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30' AND {tags}
|
||||
GROUP BY category ORDER BY SUM(amount) DESC""")
|
||||
rows = cur.fetchall()
|
||||
tot = sum(float(r[1]) for r in rows); nrec = sum(r[2] for r in rows)
|
||||
print(f"总支出: {nrec}笔 ¥{tot:,.2f}\n")
|
||||
for c, a, n in rows:
|
||||
print(f" {c:10s} ¥{float(a):>10,.2f} {100*float(a)/tot:5.1f}% ({n}笔)")
|
||||
|
||||
print("\n=== 收入 ===")
|
||||
cur.execute(f"""SELECT '支付宝' src, COUNT(*), SUM(amount) FROM income_records WHERE member_id=3 AND note LIKE '%%[支付宝]%%' AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
UNION ALL SELECT '微信', COUNT(*), SUM(amount) FROM income_records WHERE member_id=3 AND note LIKE '%%[微信]%%' AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'""")
|
||||
for s, n, a in cur.fetchall():
|
||||
print(f" {s}: {n}笔 ¥{float(a):,.2f}")
|
||||
conn.close()
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""修正微信导入中明显归类错误的项"""
|
||||
import pymysql, json
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
|
||||
# 殷老师学费 -> 教育培训
|
||||
cur.execute("""UPDATE variable_expenses SET category='教育培训'
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%' AND note LIKE '%殷老师%'""")
|
||||
print("殷老师学费->教育培训:", cur.rowcount, "笔")
|
||||
|
||||
# 众安车险 -> 保险
|
||||
cur.execute("""UPDATE variable_expenses SET category='保险'
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%' AND note LIKE '%众安%'""")
|
||||
print("众安车险->保险:", cur.rowcount, "笔")
|
||||
|
||||
# 中国石油/汽油 -> 交通
|
||||
cur.execute("""UPDATE variable_expenses SET category='交通'
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%'
|
||||
AND (note LIKE '%中国石油%' OR note LIKE '%中国石化%' OR note LIKE '%加油站%' OR note LIKE '%昆仑%') AND category='其他'""")
|
||||
print("汽油->交通:", cur.rowcount, "笔")
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""重新导入王芳2026H1支付宝:只算真实支出(交易成功/支付成功),排除交易关闭(退款)"""
|
||||
import json, pymysql
|
||||
from datetime import datetime
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
MEMBER_ID = 3
|
||||
data = json.load(open('/tmp/wangfang_records.json'))
|
||||
|
||||
ali2cat = {
|
||||
'餐饮美食': '餐饮/食品', '日用百货': '购物', '服饰装扮': '购物',
|
||||
'交通出行': '交通', '医疗健康': '医疗', '家居家装': '家居',
|
||||
'美容美发': '美容美发', '保险': '保险', '爱车养车': '交通',
|
||||
'文化休闲': '娱乐', '生活服务': '生活服务', '数码电器': '数码',
|
||||
'商业服务': '商业服务', '母婴亲子': '母婴', '转账红包': '转账/红包',
|
||||
'酒店旅游': '旅游', '充值缴费': '生活缴费', '运动户外': '运动',
|
||||
'教育培训': '教育培训', '其他': '其他',
|
||||
}
|
||||
EXCLUDE_STATUS = ('交易关闭',) # 退款/取消订单
|
||||
|
||||
inc=exp=skip=0; inc_sum=exp_sum=0.0; cat_tot={}
|
||||
closed_list=[]
|
||||
for rec in data['records']:
|
||||
ts=rec['交易时间'].strip(); dt=datetime.strptime(ts,'%Y-%m-%d %H:%M:%S')
|
||||
direction=rec['收/支'].strip(); amount=float(rec['金额'])
|
||||
ali_cat=rec['交易分类'].strip(); status=rec['交易状态'].strip()
|
||||
peer=rec['交易对方']; goods=rec['商品说明']; pay=rec['收/付款方式']
|
||||
note=f"[支付宝] {ali_cat}|{peer}|{goods or '/'}|{pay}"
|
||||
|
||||
if direction=='支出':
|
||||
if status in EXCLUDE_STATUS:
|
||||
skip+=1; continue
|
||||
cat=ali2cat.get(ali_cat,'其他')
|
||||
cur.execute("INSERT INTO variable_expenses (member_id,category,amount,recorded_date,note) VALUES (%s,%s,%s,%s,%s)",
|
||||
(MEMBER_ID,cat,amount,dt.date(),note))
|
||||
exp+=1; exp_sum+=amount; cat_tot[cat]=cat_tot.get(cat,0)+amount
|
||||
elif direction=='收入':
|
||||
cur.execute("INSERT INTO income_records (member_id,source,amount,frequency,recorded_date,note) VALUES (%s,%s,%s,%s,%s,%s)",
|
||||
(MEMBER_ID,peer,amount,'一次性',dt.date(),note))
|
||||
inc+=1; inc_sum+=amount
|
||||
else:
|
||||
skip+=1
|
||||
|
||||
conn.commit()
|
||||
print(f"支出 {exp}笔 ¥{exp_sum:.2f} (排除交易关闭 {skip}笔)")
|
||||
print(f"收入 {inc}笔 ¥{inc_sum:.2f}")
|
||||
print("支出分类:", {k:round(v,2) for k,v in sorted(cat_tot.items(),key=lambda x:-x[1])})
|
||||
conn.close()
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""导入王芳邮储卡外部真实消费(排除支付宝/微信渠道重复、内部自转、家庭内部)"""
|
||||
import csv, re, json, pymysql
|
||||
from datetime import datetime
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
MEMBER = 3
|
||||
|
||||
rows = []
|
||||
with open('/tmp/psbc_records.csv', encoding='utf-8') as f:
|
||||
for row in csv.reader(f):
|
||||
if not row or len(row) < 3:
|
||||
continue
|
||||
try:
|
||||
float(row[1])
|
||||
except:
|
||||
continue
|
||||
rows.append(row)
|
||||
|
||||
PROP_CARDS = {'621775831', '621778830', '622203130', '567156987', '208860212'}
|
||||
alipay_pat = re.compile('2088\\d{11}')
|
||||
|
||||
def is_external_expense(r):
|
||||
amt = float(r[1]); s = r[3]
|
||||
if amt >= 0: return False
|
||||
if '退货' in s: return False
|
||||
if alipay_pat.search(s): return False
|
||||
if '财付通' in s or '微信' in s: return False
|
||||
for c in PROP_CARDS:
|
||||
if c in s: return False
|
||||
if '杨轩' in s or '汪礼平' in s or '汪和平' in s: return False
|
||||
if '还款' in s: return False
|
||||
return True
|
||||
|
||||
def cat_of(s):
|
||||
if any(k in s for k in ['超市','生鲜','便利店','水果','餐饮','烧烤','龙虾','包子','蜜雪','瑞幸','金拱门','餐厅','食堂','食品','桃酥','米糕','药材','调料']):
|
||||
return '餐饮/食品'
|
||||
if any(k in s for k in ['铁路','12306','滴滴','停车','地铁','交通','火车','顺风','轨道']):
|
||||
return '交通'
|
||||
if any(k in s for k in ['药','医院','诊所','健康']):
|
||||
return '医疗'
|
||||
if any(k in s for k in ['母婴','奶粉','尿不湿']):
|
||||
return '母婴'
|
||||
if any(k in s for k in ['童装','服装','女装','百货','鞋','服饰']):
|
||||
return '购物'
|
||||
if any(k in s for k in ['美发','美容']):
|
||||
return '美容美发'
|
||||
if any(k in s for k in ['缴费','水电','话费','燃气']):
|
||||
return '生活缴费'
|
||||
if any(k in s for k in ['游泳','健身','运动']):
|
||||
return '运动'
|
||||
if any(k in s for k in ['快团团','拼多多']):
|
||||
return '购物'
|
||||
if any(k in s for k in ['美团','饿了么','拉扎斯','三快','外卖']):
|
||||
return '餐饮/食品'
|
||||
return '其他'
|
||||
|
||||
ext_count = 0; ext_sum = 0.0; cat_tot = {}
|
||||
for r in rows:
|
||||
if is_external_expense(r):
|
||||
amt = abs(float(r[1])); s = r[3]
|
||||
dt = datetime.strptime(r[0], '%Y-%m-%d %H:%M:%S')
|
||||
c = cat_of(s)
|
||||
note = f"[邮储卡] 外部消费|{s.strip()}"
|
||||
cur.execute("INSERT INTO variable_expenses (member_id,category,amount,recorded_date,note) VALUES (%s,%s,%s,%s,%s)",
|
||||
(MEMBER, c, amt, dt.date(), note))
|
||||
ext_count += 1; ext_sum += amt; cat_tot[c] = cat_tot.get(c, 0) + amt
|
||||
|
||||
conn.commit()
|
||||
print(f"外部消费导入: {ext_count}笔 ¥{ext_sum:.2f}")
|
||||
for k, v in sorted(cat_tot.items(), key=lambda x: -x[1]):
|
||||
print(f" {k:10s} ¥{v:.2f}")
|
||||
conn.close()
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""导入王芳2026H1微信账单,剔除与支付宝/邮储卡重复的支出"""
|
||||
import json, pymysql
|
||||
from datetime import datetime
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
MEMBER = 3
|
||||
|
||||
# 已导入的支付宝+邮储卡支出集合 (同日+同金额)
|
||||
cur.execute("""SELECT amount, recorded_date FROM variable_expenses
|
||||
WHERE member_id=3 AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
AND (note LIKE '%[支付宝]%' OR note LIKE '%[邮储卡]%')""")
|
||||
imported = {(d.strftime('%Y-%m-%d'), round(float(a), 2)) for a, d in cur.fetchall()}
|
||||
print(f"已导入支付宝+邮储卡: {len(imported)}笔")
|
||||
|
||||
d = json.load(open('/tmp/wx_juaner.json'))
|
||||
def fp(s): return float(s)
|
||||
|
||||
REFUND_STATES = ('已全额退款', '对方已退还', '已退款', '已退款¥')
|
||||
def is_refunded(st):
|
||||
return any(x in st for x in REFUND_STATES)
|
||||
|
||||
def cat_of(peer, goods):
|
||||
s = peer + '|' + (goods or '')
|
||||
if any(k in s for k in ['超市','生鲜','便利店','水果','餐饮','烧烤','龙虾','包子','蜜雪','瑞幸','金拱门','餐厅','食堂','食品','桃酥','米糕','奶茶','蛋糕','饭店','卷' ,'鱼店','粮油','菜']):
|
||||
return '餐饮/食品'
|
||||
if any(k in s for k in ['滴滴','12306','铁路','交通','停车','地铁','火车','中石化','加油','顺风','轨道','导航','共享单车']):
|
||||
return '交通'
|
||||
if any(k in s for k in ['药','医院','诊所','口腔','门诊','健康','体检']):
|
||||
return '医疗'
|
||||
if any(k in s for k in ['母婴','奶粉','尿不湿','童装','早教','幼教','园']):
|
||||
return '母婴'
|
||||
if any(k in s for k in ['服装','女装','百货','鞋','淘宝','拼多多','快团团','旗舰店','饰品','电商','服饰']):
|
||||
return '购物'
|
||||
if any(k in s for k in ['美发','美容','护肤','理容']):
|
||||
return '美容美发'
|
||||
if any(k in s for k in ['缴费','水电','话费','燃气','物业']):
|
||||
return '生活缴费'
|
||||
if any(k in s for k in ['游泳','健身','运动','乐刻']):
|
||||
return '运动'
|
||||
if any(k in s for k in ['酒店','旅游','门票','住宿']):
|
||||
return '旅游'
|
||||
if any(k in s for k in ['美团','饿了么','外卖','三快','拉扎斯']):
|
||||
return '餐饮/食品'
|
||||
if any(k in s for k in ['转账','红包']):
|
||||
return '转账/红包'
|
||||
return '其他'
|
||||
|
||||
exp_c = 0; exp_s = 0.0; dup = 0; dup_amt = 0.0; cat_tot = {}
|
||||
for r in d['records']:
|
||||
direction = r[4].strip()
|
||||
if direction != '支出':
|
||||
continue
|
||||
if is_refunded(r[7].strip()):
|
||||
continue
|
||||
amt = fp(r[5]); dt = datetime.strptime(r[0][:19], '%Y-%m-%d %H:%M:%S')
|
||||
key = (r[0][:10], round(amt, 2))
|
||||
if key in imported: # 跨平台重复,不重复计数
|
||||
dup += 1; dup_amt += amt
|
||||
continue
|
||||
peer = r[2]; goods = r[3]
|
||||
c = cat_of(peer, goods)
|
||||
note = f"[微信] 支出|{r[1].strip()}|{peer}|{goods or '/'}|{r[6].strip()}"
|
||||
cur.execute("INSERT INTO variable_expenses (member_id,category,amount,recorded_date,note) VALUES (%s,%s,%s,%s,%s)",
|
||||
(MEMBER, c, amt, dt.date(), note))
|
||||
exp_c += 1; exp_s += amt; cat_tot[c] = cat_tot.get(c, 0) + amt
|
||||
|
||||
inc_c = 0; inc_s = 0.0
|
||||
for r in d['records']:
|
||||
direction = r[4].strip()
|
||||
if direction != '收入':
|
||||
continue
|
||||
if is_refunded(r[7].strip()):
|
||||
continue
|
||||
amt = fp(r[5]); dt = datetime.strptime(r[0][:19], '%Y-%m-%d %H:%M:%S')
|
||||
source = r[2] if r[2].strip() else r[1].strip()
|
||||
note = f"[微信] 收入|{r[1].strip()}|{r[2]}"
|
||||
cur.execute("INSERT INTO income_records (member_id,source,amount,frequency,recorded_date,note) VALUES (%s,%s,%s,%s,%s,%s)",
|
||||
(MEMBER, source, amt, '一次性', dt.date(), note))
|
||||
inc_c += 1; inc_s += amt
|
||||
|
||||
conn.commit()
|
||||
print(f"微信支出导入: {exp_c}笔 ¥{exp_s:.2f}")
|
||||
print(f"微信支出去重排除: {dup}笔 ¥{dup_amt:.2f}")
|
||||
print(f"微信收入导入: {inc_c}笔 ¥{inc_s:.2f}")
|
||||
print("支出分类:", {k: round(v, 2) for k, v in sorted(cat_tot.items(), key=lambda x: -x[1])})
|
||||
conn.close()
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import json, collections
|
||||
d = json.load(open('/tmp/wx_juaner.json'))
|
||||
recs = d['records']
|
||||
def fp(s): return float(s)
|
||||
|
||||
# 中性记录(收/支='/')
|
||||
print("=== 中性交易 17笔 ===")
|
||||
nz = [r for r in recs if r[4].strip() == '/']
|
||||
for r in nz:
|
||||
print(f" {r[0][:16]} {r[1].strip():6s} {r[2]:16s} {r[3][:25]:25s} ¥{fp(r[5])}")
|
||||
print()
|
||||
# 支出中当前状态非支付成功(退款/关闭)
|
||||
st = collections.Counter(r[7].strip() for r in recs if r[4].strip() == '支出')
|
||||
print("=== 支出-当前状态 分布 ===", dict(st))
|
||||
st_inc = collections.Counter(r[7].strip() for r in recs if r[4].strip() == '收入')
|
||||
print("=== 收入-当前状态 分布 ===", dict(st_inc))
|
||||
# 收入明细类型
|
||||
inc_types = collections.Counter(r[1].strip() for r in recs if r[4].strip() == '收入')
|
||||
print("=== 收入-交易类型 ===", dict(inc_types))
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import pymysql, json
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
cur.execute("""SELECT category, amount, recorded_date, note FROM variable_expenses
|
||||
WHERE member_id=3 AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
AND note LIKE '%[微信]%' ORDER BY amount DESC LIMIT 30""")
|
||||
print("=== 微信导入大额TOP30 ===")
|
||||
for c, a, dt, n in cur.fetchall():
|
||||
parts = n.split('|')
|
||||
peer = parts[2] if len(parts) > 2 else ''
|
||||
goods = parts[3] if len(parts) > 3 else ''
|
||||
print(f" {c:8s} ¥{float(a):>9.2f} {dt} {peer[:20]:20s} {goods[:30]}")
|
||||
conn.close()
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import pymysql, json
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
cur.execute("""SELECT amount, recorded_date, note FROM variable_expenses
|
||||
WHERE member_id=3 AND note LIKE '%[微信]%' AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
AND note NOT LIKE '[微信] 支出%' ORDER BY amount DESC""")
|
||||
print("=== 剩余历史[微信]记录(非本次导入) ===")
|
||||
tot = 0
|
||||
for a, d, n in cur.fetchall():
|
||||
print(f" ¥{float(a):>9.2f} {d} {n[:50]}")
|
||||
tot += float(a)
|
||||
print(f"小计: {tot:.2f}")
|
||||
conn.close()
|
||||
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""王芳2026H1 全渠道(支付宝+邮储卡+微信)去重后总览"""
|
||||
import pymysql, json
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
|
||||
print("=== 按渠道支出(2026 H1) ===")
|
||||
chan = {'[支付宝]': '支付宝', '[邮储卡]': '邮储卡外部', '[微信]': '微信(去重后)'}
|
||||
for tag, name in chan.items():
|
||||
cur.execute(f"""SELECT COUNT(*), SUM(amount) FROM variable_expenses
|
||||
WHERE member_id=3 AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
AND note LIKE '%{tag}%'""")
|
||||
n, a = cur.fetchone()
|
||||
print(f" {name:14s} {n}笔 ¥{float(a):>10.2f}")
|
||||
|
||||
print("\n=== 汇总(全渠道去重) ===")
|
||||
cur.execute("""SELECT COUNT(*), SUM(amount) FROM variable_expenses
|
||||
WHERE member_id=3 AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
AND (note LIKE '%[支付宝]%' OR note LIKE '%[邮储卡]%' OR note LIKE '%[微信]%')""")
|
||||
n, a = cur.fetchone()
|
||||
print(f" 支出合计: {n}笔 ¥{float(a):.2f}")
|
||||
|
||||
print("\n=== 支出分类结构 ===")
|
||||
cur.execute("""SELECT category, SUM(amount), COUNT(*) FROM variable_expenses
|
||||
WHERE member_id=3 AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
AND (note LIKE '%[支付宝]%' OR note LIKE '%[邮储卡]%' OR note LIKE '%[微信]%')
|
||||
GROUP BY category ORDER BY SUM(amount) DESC""")
|
||||
tot = 0
|
||||
rows = cur.fetchall()
|
||||
tot = sum(float(r[1]) for r in rows)
|
||||
for c, a, n in rows:
|
||||
print(f" {c:10s} ¥{float(a):>10.2f} {100*float(a)/tot:5.1f}% ({n}笔)")
|
||||
print(f" 总计: ¥{tot:.2f}")
|
||||
|
||||
print("\n=== 收入 ===")
|
||||
cur.execute("""SELECT COUNT(*), SUM(amount) FROM income_records
|
||||
WHERE member_id=3 AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
AND note LIKE '%[微信]%'""")
|
||||
n, a = cur.fetchone()
|
||||
print(f" 微信收入: {n}笔 ¥{float(a):.2f}")
|
||||
conn.close()
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import json, pymysql, collections
|
||||
cfg = json.load(open('db_config.json'))
|
||||
conn = pymysql.connect(host=cfg['host'], port=cfg['port'], user=cfg['user'],
|
||||
password=cfg.get('password'), database=cfg['database'], charset='utf8mb4')
|
||||
cur = conn.cursor()
|
||||
cur.execute("""SELECT amount, recorded_date FROM variable_expenses
|
||||
WHERE member_id=3 AND recorded_date BETWEEN '2026-01-01' AND '2026-06-30'
|
||||
AND (note LIKE '%[支付宝]%' OR note LIKE '%[邮储卡]%')""")
|
||||
imp = {(d.strftime('%Y-%m-%d'), round(float(a), 2)) for a, d in cur.fetchall()}
|
||||
print(f"支付宝+邮储卡已计入: {len(imp)}笔")
|
||||
|
||||
d = json.load(open('/tmp/wx_juaner.json'))
|
||||
def fp(s): return float(s)
|
||||
REFUND = ('已全额退款', '对方已退还', '已退款')
|
||||
excluded = []
|
||||
for r in d['records']:
|
||||
if r[4].strip() != '支出': continue
|
||||
if any(x in r[7] for x in REFUND): continue
|
||||
key = (r[0][:10], round(fp(r[5]), 2))
|
||||
if key in imp:
|
||||
excluded.append((key, r[2], r[6].strip()))
|
||||
print(f"微信中被去重排除的: {len(excluded)}笔")
|
||||
by_pay = collections.Counter(x[2] for x in excluded)
|
||||
print(" 按微信支付方式:", dict(by_pay))
|
||||
amt = sum(x[0][1] for x in excluded)
|
||||
print(f" 被排除总额: ¥{amt:.2f}")
|
||||
# 每个排除项在库中是否有邮储卡/支付宝对应
|
||||
cnt_ext = 0; cnt_ali = 0; missing = []
|
||||
for key, peer, pay in excluded:
|
||||
cur.execute("""SELECT note FROM variable_expenses WHERE member_id=3 AND recorded_date=%s AND amount=%s
|
||||
AND (note LIKE '%%[邮储卡]%%' OR note LIKE '%%[支付宝]%%')""", (key[0], key[1]))
|
||||
tags = set()
|
||||
for (n,) in cur.fetchall():
|
||||
if '[邮储卡]' in n: tags.add('PSBC')
|
||||
if '[支付宝]' in n: tags.add('ALI')
|
||||
if 'PSBC' in tags: cnt_ext += 1
|
||||
if 'ALI' in tags: cnt_ali += 1
|
||||
if not tags:
|
||||
missing.append((key, peer))
|
||||
print(f" 排除项对应邮储卡记录: {cnt_ext}, 对应支付宝记录: {cnt_ali}")
|
||||
print(f" 排除项但库中无对应(=可能漏计): {len(missing)}笔")
|
||||
for m in missing[:10]:
|
||||
print(" 缺:", m)
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user