尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用Python从零实现婚嫁礼金统筹助手:数据建模与Excel报表实战

用Python从零实现婚嫁礼金统筹助手:数据建模与Excel报表实战 “彩礼谈崩”这个场景最近在不少社交平台都引发了讨论。很多人在评论区感慨原本已经到了谈婚论嫁的阶段却因为礼金数额临时变动导致双方关系瞬间破裂。抛开具体对错不谈这类事件背后其实暴露了一个很现实的问题——婚姻筹备涉及大量资金、流程、人员沟通如果只靠口头约定和记忆很容易在某个环节出现信息不一致进而升级成矛盾。作为一名开发者我当时的第一个念头是这类问题能不能用技术手段去做“预期管理”比如做一个婚嫁礼金统筹与流程管理的小工具把预算、项目、阶段、变动记录都结构化让每一笔钱、每一次调整都有据可查。这样虽然不能解决情感问题但至少能把“信息不对称”这个矛盾源头降到最低。本文就围绕这个思路从零实现一个基于 Python 的“婚嫁礼金统筹助手”。它会涉及数据建模、Excel 报表生成、预算差异分析、命令行交互等知识点。即使你只是 Python 初学者也可以跟着一步步做出来。1. 背景为什么婚姻筹备需要一套结构化方案1.1 婚姻筹备中的典型痛点在讨论彩礼、嫁妆、婚宴等话题时我们经常看到几类矛盾口头约定没有留痕事后双方记忆不一致。预算没有统一汇总这边超支了那边还不知情。临时加价或调整项目时没有走“变更确认”流程导致一方突然接受不了。涉及双方家庭多个角色信息传递链路过长容易失真。这些问题的本质是“沟通信息”和“资金数据”都缺少一个统一的记录、校验、提醒机制。1.2 技术工具能解决什么技术无法解决感情问题但可以解决数据问题。一个结构化的礼金统筹工具至少能提供三个能力记录能力每一项礼金的名称、金额、支付方、接收方、状态、备注。计算能力自动汇总总预算、已支付金额、待支付金额、项目占比。风控能力当某项金额被修改或总预算超过设定阈值时及时提示。这就是一个典型的数据管理需求用 Python 来做非常合适。1.3 本文项目目标本文要实现的工具叫marriage_budget_assistant功能包括支持新增礼金项目如彩礼、嫁妆、婚宴定金、首饰、酒水等。支持修改金额时自动记录变更历史。生成 Excel 格式的预算明细报表。生成预算差异分析报告。所有数据存储到 SQLite 数据库方便后续扩展。最终效果是你可以在命令行输入命令把整个筹备过程中的资金变动都管理起来。2. 环境准备与版本说明2.1 运行环境本文示例以以下环境为参考实际操作时请根据你的本地环境调整操作系统Windows 10 / macOS / Linux 均可。Python3.9 及以上建议 3.10。包管理器pip。2.2 需要安装的依赖我们只需要三个基础库pandas用于数据处理和 Excel 生成。openpyxlpandas 写入 Excel 时需要。tabulate用于在命令行以表格形式展示数据。安装命令pip install pandas openpyxl tabulate如果你在国内网络环境下载较慢可以使用国内镜像源pip install pandas openpyxl tabulate -i https://pypi.tuna.tsinghua.edu.cn/simple2.3 项目目录结构我们采用一个小型项目的标准结构marriage_budget_assistant/ ├── main.py # 程序入口命令行交互 ├── database.py # SQLite 数据库操作 ├── service.py # 业务逻辑层 ├── report.py # Excel 报表生成 ├── models.py # 数据类定义 ├── requirements.txt # 依赖清单 └── data/ └── marriage.db # SQLite 数据库文件运行时自动生成如果某个文件还不存在手动创建即可代码会在运行时自动生成data目录。3. 核心设计与数据模型在动手写代码之前先理清数据模型和业务边界。3.1 礼金项目的数据结构一条礼金记录应该包含以下字段字段名类型说明idINTEGER主键自增item_nameTEXT项目名称如“彩礼”categoryTEXT分类如“礼金”“婚宴”“首饰”amountREAL金额单位元payerTEXT支付方如“男方家庭”receiverTEXT接收方如“女方家庭”statusTEXT状态pending待支付 /paid已支付remarkTEXT备注created_atTEXT创建时间updated_atTEXT最后更新时间3.2 变更历史的必要性为什么需要变更历史因为“临时加价10万”这类冲突本质上是金额变更没有经过双方确认和记录。如果在系统中每一次修改都留下历史记录谁在什么时间把金额从 A 改成了 B。修改前后的金额是多少。修改原因是什么。这样至少能在沟通时拿出客观数据而不是各说各话。变更历史表结构字段名类型说明idINTEGER主键item_idINTEGER对应的礼金项目 idold_amountREAL修改前金额new_amountREAL修改后金额change_timeTEXT修改时间reasonTEXT修改原因3.3 预算阈值与预警逻辑为了避免总预算失控我们设置一个简单的预警逻辑当所有待支付项目金额合计超过某个阈值时输出警告。当单个项目修改幅度超过原金额的 20% 时标记为“大幅变更”。这两条规则都要以配置的形式写在代码里方便后续修改。4. 完整实战编写婚嫁礼金统筹助手接下来进入编码环节。我会按照数据库层、业务层、报表层、入口层的顺序来写。4.1 创建 requirements.txt先创建依赖清单文件。pandas1.5.0 openpyxl3.0.10 tabulate0.9.04.2 数据库操作层 database.py这个文件负责 SQLite 的连接、建表和基础 CRUD 操作。# 文件路径marriage_budget_assistant/database.py import sqlite3 from pathlib import Path BASE_DIR Path(__file__).resolve().parent DATA_DIR BASE_DIR / data DB_PATH DATA_DIR / marriage.db def get_connection(): 获取数据库连接并开启外键约束 DATA_DIR.mkdir(exist_okTrue) conn sqlite3.connect(DB_PATH) conn.row_factory sqlite3.Row conn.execute(PRAGMA foreign_keys ON) return conn def init_db(): 初始化数据库表结构 conn get_connection() cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS budget_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, item_name TEXT NOT NULL, category TEXT NOT NULL, amount REAL NOT NULL, payer TEXT NOT NULL, receiver TEXT NOT NULL, status TEXT NOT NULL DEFAULT pending, remark TEXT DEFAULT , created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ) ) cursor.execute( CREATE TABLE IF NOT EXISTS amount_changes ( id INTEGER PRIMARY KEY AUTOINCREMENT, item_id INTEGER NOT NULL, old_amount REAL NOT NULL, new_amount REAL NOT NULL, change_time TEXT DEFAULT CURRENT_TIMESTAMP, reason TEXT DEFAULT , FOREIGN KEY (item_id) REFERENCES budget_items(id) ) ) conn.commit() conn.close()这里有几个设计细节PRAGMA foreign_keys ON确保删除项目时能级联处理历史记录。created_at和updated_at使用 SQLite 默认时间戳。budget_items和amount_changes通过item_id关联形成一对多关系。4.3 业务逻辑层 service.py业务逻辑层负责处理“新增项目”“修改金额”“生成统计”“校验阈值”等核心操作。# 文件路径marriage_budget_assistant/service.py from database import get_connection, init_db # 预警阈值配置 TOTAL_BUDGET_LIMIT 300000 # 总预算上限单位元 CHANGE_RATIO_LIMIT 0.20 # 单次变更比例上限 def add_item(item_name, category, amount, payer, receiver, statuspending, remark): 新增一条礼金项目 conn get_connection() cursor conn.cursor() cursor.execute( INSERT INTO budget_items (item_name, category, amount, payer, receiver, status, remark) VALUES (?, ?, ?, ?, ?, ?, ?) , (item_name, category, amount, payer, receiver, status, remark)) conn.commit() item_id cursor.lastrowid conn.close() return item_id def update_amount(item_id, new_amount, reason): 修改项目金额并记录变更历史 conn get_connection() cursor conn.cursor() # 查出旧金额 row cursor.execute( SELECT amount FROM budget_items WHERE id ?, (item_id,) ).fetchone() if row is None: conn.close() raise ValueError(f项目 id{item_id} 不存在) old_amount row[amount] # 更新金额 cursor.execute( UPDATE budget_items SET amount ?, updated_at CURRENT_TIMESTAMP WHERE id ? , (new_amount, item_id)) # 插入变更历史 cursor.execute( INSERT INTO amount_changes (item_id, old_amount, new_amount, reason) VALUES (?, ?, ?, ?) , (item_id, old_amount, new_amount, reason)) conn.commit() conn.close() # 计算变更比例 change_ratio abs(new_amount - old_amount) / old_amount if old_amount else 1.0 if change_ratio CHANGE_RATIO_LIMIT: print(f[提醒] 项目 id{item_id} 变更比例达到 {change_ratio:.1%}建议双方再次确认。) else: print(f[信息] 项目 id{item_id} 金额已更新{old_amount} - {new_amount}) def get_all_items(): 获取所有礼金项目 conn get_connection() cursor conn.cursor() rows cursor.execute(SELECT * FROM budget_items ORDER BY id).fetchall() conn.close() return [dict(row) for row in rows] def get_items_by_status(statuspending): 按状态筛选项目 conn get_connection() cursor conn.cursor() rows cursor.execute( SELECT * FROM budget_items WHERE status ? ORDER BY id, (status,) ).fetchall() conn.close() return [dict(row) for row in rows] def get_summary(): 生成预算汇总数据 conn get_connection() cursor conn.cursor() rows cursor.execute( SELECT category, COUNT(*) as item_count, SUM(amount) as total_amount FROM budget_items GROUP BY category ).fetchall() conn.close() return [dict(row) for row in rows] def check_budget_limit(): 检查总预算是否超过阈值 conn get_connection() cursor conn.cursor() row cursor.execute(SELECT SUM(amount) as total FROM budget_items).fetchone() conn.close() total row[total] if row and row[total] else 0 if total TOTAL_BUDGET_LIMIT: print(f[警告] 当前预算合计 {total:.2f} 元已超过设定阈值 {TOTAL_BUDGET_LIMIT:.2f} 元) else: print(f[信息] 当前预算合计 {total:.2f} 元未超过阈值 {TOTAL_BUDGET_LIMIT:.2f} 元。) return total这个文件是核心业务逻辑。需要注意修改金额时先查旧值再更新新值同时写入历史保证可以追溯。变更比例超过 20% 就会提醒比例值在配置常量中定义。所有函数都返回结构化数据方便上层展示和报表生成。4.4 报表生成 report.py报表模块负责把数据库中的数据导出为 Excel 文件并且生成一张预算差异分析表。# 文件路径marriage_budget_assistant/report.py from pathlib import Path import pandas as pd from service import get_all_items, get_items_by_status, get_summary def export_excel(output_pathmarriage_budget_report.xlsx): 导出完整预算报表到 Excel items get_all_items() # 转换成 DataFrame df pd.DataFrame(items) # 如果数据为空创建空列避免报错 if df.empty: df pd.DataFrame(columns[ id, item_name, category, amount, payer, receiver, status, remark, created_at, updated_at ]) output_file Path(output_path) output_file.parent.mkdir(parentsTrue, exist_okTrue) with pd.ExcelWriter(output_file, engineopenpyxl) as writer: # Sheet1预算明细 df.to_excel(writer, sheet_name预算明细, indexFalse) # Sheet2按分类汇总 summary get_summary() df_summary pd.DataFrame(summary) if summary else pd.DataFrame( columns[category, item_count, total_amount] ) df_summary.to_excel(writer, sheet_name分类汇总, indexFalse) # Sheet3待支付清单 pending get_items_by_status(pending) df_pending pd.DataFrame(pending) if pending else pd.DataFrame( columns[id, item_name, category, amount, payer, receiver, status, remark] ) df_pending.to_excel(writer, sheet_name待支付清单, indexFalse) print(f[成功] 报表已生成{output_file.resolve()}) def print_console_report(): 在控制台输出简易报告 items get_all_items() if not items: print(当前没有任何礼金项目。) return df pd.DataFrame(items) print( 礼金项目明细 ) print(df.to_string(indexFalse)) summary get_summary() if summary: df_summary pd.DataFrame(summary) print(\n 分类汇总 ) print(df_summary.to_string(indexFalse))这里用pd.ExcelWriter同时写入多个 Sheet比逐行写 Excel 更方便。openpyxl是默认引擎。4.5 程序入口 main.py入口文件负责解析用户命令调用 service 和 report 模块。# 文件路径marriage_budget_assistant/main.py import argparse from database import init_db from service import add_item, update_amount, get_all_items, check_budget_limit from report import export_excel, print_console_report def main(): parser argparse.ArgumentParser( description婚嫁礼金统筹助手 ) subparsers parser.add_subparsers(destcommand) # 新增项目命令 add_parser subparsers.add_parser(add, help新增礼金项目) add_parser.add_argument(--name, requiredTrue, help项目名称) add_parser.add_argument(--category, requiredTrue, help项目分类) add_parser.add_argument(--amount, requiredTrue, typefloat, help金额) add_parser.add_argument(--payer, requiredTrue, help支付方) add_parser.add_argument(--receiver, requiredTrue, help接收方) add_parser.add_argument(--status, choices[pending, paid], defaultpending) add_parser.add_argument(--remark, default) # 修改金额命令 update_parser subparsers.add_parser(update, help修改项目金额) update_parser.add_argument(--id, requiredTrue, typeint, help项目 id) update_parser.add_argument(--amount, requiredTrue, typefloat, help新金额) update_parser.add_argument(--reason, default, help修改原因) # 查看列表命令 subparsers.add_parser(list, help查看所有项目) # 统计命令 subparsers.add_parser(summary, help查看预算汇总) # 导出 Excel 命令 export_parser subparsers.add_parser(export, help导出 Excel 报表) export_parser.add_argument(--output, defaultmarriage_budget_report.xlsx) args parser.parse_args() init_db() if args.command add: item_id add_item( item_nameargs.name, categoryargs.category, amountargs.amount, payerargs.payer, receiverargs.receiver, statusargs.status, remarkargs.remark, ) print(f[成功] 已新增项目 id{item_id}) elif args.command update: update_amount(args.id, args.amount, args.reason) elif args.command list: items get_all_items() if items: for item in items: print(item) else: print(列表为空) elif args.command summary: check_budget_limit() print_console_report() elif args.command export: export_excel(args.output) else: parser.print_help() if __name__ __main__: main()4.6 运行与验证先把项目代码保存到本地然后在命令行执行以下命令。第一步新增项目python main.py add --name 彩礼 --category 礼金 --amount 100000 --payer 男方家庭 --receiver 女方家庭 --status pending --remark 双方初步商定金额预期输出[成功] 已新增项目 id1第二步新增更多项目python main.py add --name 婚宴定金 --category 婚宴 --amount 30000 --payer 男方家庭 --receiver 酒店 --status pending python main.py add --name 首饰 --category 首饰 --amount 50000 --payer 男方家庭 --receiver 珠宝店 --status paid --remark 已购买第三步修改金额模拟临时加价场景python main.py update --id 1 --amount 110000 --reason 对方家庭临时提出增加预期输出[提醒] 项目 id1 变更比例达到 10.0%建议双方再次确认。这里 10% 没有超过 20%所以只提示变更不触发大幅变更警告。如果改成python main.py update --id 1 --amount 130000 --reason 对方家庭临时提出增加输出就会变成[提醒] 项目 id1 变更比例达到 30.0%建议双方再次确认。第四步查看汇总python main.py summary预期输出类似[信息] 当前预算合计 210000.00 元未超过阈值 300000.00 元。 礼金项目明细 id item_name category amount payer receiver status remark created_at updated_at 1 彩礼 礼金 130000.0 男方家庭 女方家庭 pending 双方初步商定金额 2025-01-01 10:00:00 2025-01-01 10:05:00 2 婚宴定金 婚宴 30000.0 男方家庭 酒店 pending 2025-01-01 10:02:00 2025-01-01 10:02:00 3 首饰 首饰 50000.0 男方家庭 珠宝店 paid 已购买 2025-01-01 10:03:00 2025-01-01 10:03:00 分类汇总 category item_count total_amount 礼金 1 130000.0 婚宴 1 30000.0 首饰 1 50000.0第五步导出 Excelpython main.py export --output data/report.xlsx会在data目录下生成report.xlsx里面包含三个 Sheet。5. 常见问题与排查思路5.1 运行main.py时提示ModuleNotFoundError: No module named pandas问题现象常见原因解决思路导入 pandas 失败依赖未安装执行pip install -r requirements.txt安装后仍然报错使用了多个 Python 环境检查python --version确认是否在虚拟环境中下载速度慢网络原因使用国内镜像源安装5.2 中文写入 Excel 后乱码pandas写入 Excel 一般不会乱码但如果你在控制台打印中文出现乱码在 Windows 下可以尝试chcp 65001或者在 Python 脚本开头加import sys sys.stdout.reconfigure(encodingutf-8)5.3 修改不存在的项目 idupdate_amount函数会抛出ValueError提示项目不存在。在入口层目前没有捕获异常会导致程序报错退出。你可以把入口层改成 try-excepttry: update_amount(args.id, args.amount, args.reason) except ValueError as e: print(f[错误] {e})5.4 金额精度问题由于 SQLite 中存的是REAL浮点数进行大量累加时可能出现精度误差。如果对精度要求高建议把金额字段改为整数“分”来存储显示时再除以 100。示例改造思路# 存储时 amount_fen int(round(amount * 100)) # 展示时 amount_yuan amount_fen / 1005.5 删除项目时希望同步删除历史记录可以在数据库初始化时把外键约束和级联删除打开。PRAGMA foreign_keys ON;并且建外键时指定ON DELETE CASCADEFOREIGN KEY (item_id) REFERENCES budget_items(id) ON DELETE CASCADE6. 最佳实践与工程建议6.1 金额数据统一使用“分”存储浮点数在金融场景下是不可靠的。建议所有金额以整数“分”为单位存储涉及展示时再转换避免累加误差。6.2 变更必须留痕在真实场景中不管是婚姻筹备还是日常报销金额变更必须留下审计记录。这套工具里amount_changes表就是审计日志。工程上的做法是不允许直接 UPDATE 金额字段。必须通过“新增变更单”的方式修改。每个变更单包含原值、新值、原因、操作人、时间。6.3 配置与代码分离阈值、预算上限这类参数不要硬编码在业务代码中可以抽到配置文件或环境变量中。例如创建config.pyimport os TOTAL_BUDGET_LIMIT int(os.getenv(TOTAL_BUDGET_LIMIT, 300000)) CHANGE_RATIO_LIMIT float(os.getenv(CHANGE_RATIO_LIMIT, 0.20))这样运维同学不需要改代码只要设置环境变量就能调整规则。6.4 数据库备份SQLite 是文件型数据库备份方法很简单。在每次生成报表前可以把数据库文件复制一份。import shutil from datetime import datetime backup_file fdata/marriage_{datetime.now():%Y%m%d_%H%M%S}.db shutil.copy(data/marriage.db, backup_file)6.5 输出规范报表中不要只输出金额要把“支付方、接收方、用途、备注”全部展示避免因为信息缺失造成误会。6.6 权限与隐私这类工具涉及个人家庭财务信息要注意数据库文件不要提交到公共 Git 仓库。Excel 报表如果分享给他人注意脱敏。建议在本地运行不开放公网端口。7. 延伸与总结7.1 本文实现了什么通过一个简单的 Python 项目我们完成了数据库建模预算项目表 金额变更历史表。命令行交互新增、修改、查询、导出全部可用。报表生成Excel 多 Sheet 输出。风险提示预算超限、金额大幅变更自动提醒。这个项目虽然不大但把“记录—计算—提醒—导出”的完整闭环走通了。7.2 可以继续扩展的方向增加 Web 界面让家庭成员通过浏览器填写数据。部署到局域网让多方共同维护但要注意访问权限。接入短信或邮件提醒在金额变更时通知相关人员。增加 PDF 导出用于打印留档。增加“双方确认”功能金额变更需要双方在线确认后才生效。7.3 一点思考回到最初那个“彩礼谈崩”的场景这类冲突背后通常不是某一方的“恶”而是沟通机制缺失。如果我们能通过数据管理工具让每一笔资金、每一次变动都清晰透明也许很多矛盾在爆发之前就可以被拦截。当然工具只是辅助真正重要的是双方愿意用规则而不是情绪来处理问题。这也是我在写这个项目时最大的体会技术改变不了人心但可以让人心平气和地看到事实。如果你也正在用代码解决生活中的实际问题欢迎把你的项目思路写在评论区我们一起讨论。
返回列表