ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

房屋租赁报修与应收应付系统:Django+小程序全栈实践

房屋租赁报修与应收应付系统:Django+小程序全栈实践 这个题目我在不同场合见过好几回多数是毕业设计或课程项目也有一部分是小型公寓物业管理系统。乍一看像是两个系统拼在一起——维修报修和应收应付怎么看都不像同一个模块但真正在物业场景里跑过的人会明白报修单最后一定会变成一笔钱这笔钱要么记在租客头上要么记在维修师傅和配件供应商头上绕不开账务。我自己的做法是用 Python 后端加微信小程序前端把这套业务完整串了起来后端在用 Django 和 Flask 之间反复横跳过最后选了 Django 搭主服务、部分轻量接口用 Flask 单独挂。下面把从需求拆解到数据库设计、从接口实现到小程序端踩坑的完整过程写清楚给正在做同类型项目的朋友一个可以直接参照的思路。1. 先理清业务逻辑报修和应收应付为什么必须放进同一个系统这类系统最忌讳一上来就建表写接口先把业务关系弄清楚后面能少返工一大半。我当时花了一个周末把物业报修、租赁合同、财务流水三块业务画在一张纸上发现它们的逻辑链条比想象中清晰。1.1 房屋租赁场景下的三个闭环第一闭环是房源与租约管理。房源从待租、已租、维修中到退租清理每个状态都会影响系统里的数据流向。比如房源处于维修中就不能继续生成新合同退租后要自动触发押金应退或应扣流程。第二闭环是报修工单的生命周期。租客发起报修管理员派单维修师傅接单完工后填写维修内容和费用最后租客或管理员确认。这个闭环的核心不只是把活干完而是过程中产生的材料费、人工费、紧急出勤费必须明确归属方。第三闭环是应收应付。应收侧包括房租、押金、租客承担维修费、违约金应付侧包括维修师傅劳务费、配件采购款、公共区域维护费。这三个闭环在维修工单产生费用这个节点交汇交汇点就是整个系统的核心复杂度所在。1.2 报修费用如何流转到财务模块举个例子。租客报修热水器维修师傅检查后说是加热管烧了换件加人工一共 180 元。这笔费用先进入工单的 cost 字段然后管理员审核确认这笔钱由租客承担于是系统自动生成一笔应收账单同时需要支付维修师傅 80 元人工费生成一笔应付账单配件商那边还有 100 元采购款生成另一笔应付。如果不把维修和财务打通这三笔账很容易散落在 Excel 和纸质单据里月底对账全靠运气。1.3 小程序端在三类用户眼中的职责差异同一个微信小程序按角色差异显示完全不同内容租客看到的是我要报修、我的账单、我的合同管理员看到的是工单派单、费用审核、应收统计维修师傅看到的是待接单、我的维修记录、已结算劳务费。后端接口因此需要做好角色权限控制避免数据越权这一点我在后面接口设计部分会专门展开。2. 技术选型复盘Django 和 Flask 的取舍以及小程序端怎么定这个项目的技术选型没有绝对标准答案但有几个关键问题想清楚后会容易很多业务模型多不多后台管理需求重不重团队对 Python 的熟悉程度如何部署环境是否可控我两个方案都实际搭过最后的分工是主业务用 Django独立的小服务用 Flask 挂载。2.1 Django 适合做核心业务Flask 适合做轻量侧服务做一个包含房源、合同、工单、应收、应付、流水、用户多套模型的系统Django 自带的 ORM、Admin 后台、迁移工具和认证体系能省下大量重复劳动。租客管理、账单审核这些页面如果全部手写光 CRUD 就要多写两三千行代码而 Django Admin 配合 django-unfold 这类新式后台主题可以直接生成一套可用的管理界面非常适合内部人员使用。Flask 的优势是轻量、灵活适合挂一些独立的小功能比如维修师傅劳务费计算器、数据导入导出服务这些功能单独跑在 Django 进程里也可以但用 Flask 拆出来会让主服务更清爽。需要注意的是 Flask 没有内置 ORM 和 Admin如果整个系统都用 Flask 写模型一多就得自己搭一套模型层和管理后台工作量会成倍增加。对比维度DjangoFlask内置 ORM有成熟稳定无需自己集成 SQLAlchemy管理后台自带 Admin可扩展需自己开发或集成第三方数据迁移自带 migrations常用 Flask-Migrate认证授权内置认证可扩展需自己集成 JWT 或 Session适合场景模型多、后台重的业务系统轻量 API、微服务、原型验证学习曲线前期偏陡后期省力上手快架构自控如果你是一个人独立开发时间又比较紧我建议主业务直接选 Django。如果只是写 API 给小程序用后台管理页面不做或另找现成方案Flask 也可以但表结构设计和权限控制要自己多花心思。2.2 小程序端为什么用原生而不是 uni-app开发微信小程序有三种常见路线原生微信小程序、uni-app、Taro。这个项目只面向微信用户没有跨端需求我选了原生。原生小程序的页面生命周期、组件行为和微信支付、订阅消息的接入都是第一手支持遇到问题搜索到的解决方案也最直接。uni-app 的跨端优势确实存在但它的转译层会引入额外的包体积和编译问题特别是预览和真机调试时偶尔出现样式不一致。对于业务系统这种以表单、列表、详情页为主的小程序原生写法足够还能少踩一层框架的坑。另外建议不要一开始就上云开发。这个项目的数据必须和自建后端 MySQL 打通用云开发等于维护两套数据源后续对账和统计会非常难受。正确做法是小程序通过 request 请求自己的后端域名后端统一处理逻辑和权限。2.3 部署环境准备从 Python 安装到 Web 服务服务器建议 Linux 系统我用的是一台 2 核 4G 的云主机。Python 版本选了 3.10这个版本兼容性好Django、uWSGI、gunicorn 都稳定支持。安装时注意用系统自带的包管理器装好 build-essential 和相关依赖不然编译某些 Python 库时会报错。后端跑起来需要两层服务外层用 Nginx 处理 HTTPS、静态文件和反向代理内层用 Gunicorn 跑 Django。Gunicorn 启动命令大致是pip install gunicorn gunicorn config.wsgi:application -w 4 -b 127.0.0.1:8000Django 的静态文件和 media 上传目录通过 Nginx 直接服务DEBUGFalse之后必须配置好这些否则页面会丢样式、上传图片无法访问。域名方面需要配置 HTTPS 证书微信小程序正式环境要求所有请求域名必须备案且支持 HTTPS这个后面专项说。3. 核心表结构设计把房源、合同、工单、账款理成一张网表结构是整个系统最不能马虎的部分字段少了补起来麻烦字段设计错了改起来更麻烦。我按业务模块拆解后最终定下十五张核心表这里重点讲四组最关键的设计思路。3.1 房源、租客、合同一对多关系如何处理房源表保存最基础的物理信息和价格信息包括小区名称、楼栋房号、面积、朝向、月租金、押金、当前状态。租客表保存微信 openid、姓名、手机号openid 是和小程序登录绑定的关键字段不能为空。合同表是房源和租客之间的桥梁包含合同编号、起止日期、租金周期、月租金、押金、违约金规则、当前状态。这里有一个容易踩坑的地方合同状态和房源状态必须联动。比如合同生效时房源状态要同步改成已租合同到期或退租后房源要变回待租如果只改一边后面生成账单或刷列表时数据就是错的。3.2 报修工单状态机让工单从创建到结算每一步都可追踪报修工单不能只做一个简单的状态字段我设计了一个状态机每一步变化都记录操作人和时间状态含义可执行操作pending租客已提交待管理员受理管理员派单或驳回assigned已派单给维修师傅师傅接单或拒单processing维修中师傅提交完工信息finished已完工待审核管理员审核费用confirmed费用已审核系统生成应收应付账单cancelled已取消记录取消原因费用信息在 finished 阶段由维修师傅填写包括材料费、人工费、总金额并上传收费明细照片。管理员确认后系统以确认金额为准生成应收应付账单这一步是整个流程数据一致性的关键节点金额只允许在审核时修改一次后续修改必须走冲销流程。3.3 应收应付与流水核销关系必须用专门表承载应收账单和应付账单是两个独立模型字段结构类似账单编号、关联合同或工单、项目名称、金额、到期日、状态未付、部分支付、已收/已付、已核销、已坏账。应收账单的关联对象可以是合同也可以是工单应付账单的关联对象通常是供应商或维修师傅。这里的核心难点是核销。现实中租客可能一次性付清两期房租加一笔维修费总共 2000 元但系统里对应的是三笔应收账单。如果用付款记录直接改账单金额完全说不清楚钱是怎么分配的。我加了一张账单核销关联表每一笔收款记录都可以拆成多条核销明细每条明细对应一笔账单、一个核销金额核销汇总金额等于收款金额才允许关闭流水。这部分的数据库事务很重要。核销操作必须用 Django 的事务包裹先查询账单剩余金额再写入核销明细最后更新账单状态任何一步失败整体回滚否则会出现超收或账单状态错乱。3.4 容易被忽略的公共字段每个业务表我都统一加了这几个字段created_at、updated_at、created_by、updated_by、is_deleted。金额字段统一用DecimalField(max_digits10, decimal_places2)绝对不用 Float 类型存钱否则计算时会出现浮点精度问题对账对不上非常头疼。状态字段用短字符串配合常量枚举不直接用 magic number可读性会好很多。4. 后端接口与业务逻辑实现要点接口层是后端和小程序的交汇面这块有几个高频易错点我逐一说明。4.1 微信登录与自建 Token如何让后端认识小程序里的用户小程序端调用wx.login拿到临时 code传给后端登录接口后端用 code 向微信的jscode2session接口换取 openid 和 session_key然后签发自己的 token。我这里用的是 JWT通过一段有效期为 7 天的签名 token 来维持登录状态。# views.py 登录接口关键逻辑Django 示例 import jwt import requests from django.conf import settings from .models import Tenant def wx_login(request): code request.data.get(code) resp requests.get( https://api.weixin.qq.com/sns/jscode2session, params{ appid: settings.WX_APPID, secret: settings.WX_APPSECRET, js_code: code, grant_type: authorization_code, }, timeout5, ).json() openid resp[openid] tenant, _ Tenant.objects.get_or_create(openidopenid) token jwt.encode( {openid: openid, exp: timezone.now() timedelta(days7)}, settings.JWT_SECRET, algorithmHS256, ) return JsonResponse({token: token, tenant_id: tenant.id})实际项目中还加了一个简单角色判断租客、管理员、维修师傅各自有单独的用户模型或用户的 role 字段。管理员和维修师傅是后台创建的账号不受登录接口控制他们在小程序端的令牌是通过另一套账号密码登录接口签发的。4.2 报修工单的派单逻辑自动分配还是人工选择我这里做了两种模式默认是人工派单管理员在工单详情页选择空闲的维修师傅另一种是按维修类型自动匹配比如漏水工单自动分给水工师傅。自动分配用了一段简单的过滤逻辑repair RepairOrder.objects.select_for_update().get(pkrepair_id) workers Worker.objects.filter( is_activeTrue, skills__containsrepair.type, ).exclude( current_order__isnullFalse, # 正在忙的师傅不参与自动分配 ).order_by(finished_count)派单后要给师傅发送微信订阅消息这里需要注意订阅消息的模板审核和用户授权逻辑一次授权只能推一条消息需要在合适的时机引导用户订阅。4.3 应收应付核销的核心代码事务包裹、原子更新核销模块是最容易出 bug 的地方我把关键逻辑放在 Django 事务里并用select_for_update锁行处理并发。以租客支付一笔多账单合计款项为例from django.db import transaction transaction.atomic def settle_bills(tenant_id, payment_amount, bills): remain payment_amount with transaction.atomic(): for bill in bills: bill ReceivableBill.objects.select_for_update().get(pkbill.pk) unpaid bill.amount - bill.paid_amount pay min(remain, unpaid) bill.paid_amount pay bill.status paid if bill.paid_amount bill.amount else partial bill.save() PaymentRecord.objects.create( billbill, amountpay, directionin, ) remain - pay if remain 0: # 剩余金额生成预收款 AdvancePayment.objects.create(tenanttenant, amountremain)加锁之后,同一时间内即使两个请求同时点击支付系统也只会把金额计入一个核实流程不会出现重复扣减或超收。4.4 Django ORM 使用中的几个注意点ORM 用得不好接口性能会肉眼可见地变差。查询工单列表时一定要用select_related或prefetch_related否则会产生 N1 查询小项目数据量少还不明显数据过万后接口会慢得离谱。删除对象时如果不是业务上明确要永久删除建议用软删除给模型加is_deleted字段查询时默认加过滤条件。这样即便误删数据也还能恢复做审计时也有依据。Django 原生delete()是硬删除覆盖重写delete方法即可实现软删除。批量更新时用QuerySet.update()而不是循环save()尤其是需要给很多工单同时置状态时一条 update 语句就能完成效率差几十倍。5. 微信小程序端实现页面结构、请求封装与常见交互细节小程序端的重点是把后端接口的能力转化为租客和管理员都能顺畅操作界面。我按照租客高频使用的几个场景来设计页面首页、报修、账单、我的四栏 Tab加上管理员专用的后台入口。5.1 请求封装统一处理 token、错误和加载态小程序没有一个全局的 axios需要自己封装一个request方法。我把基础配置、token 注入、401 处理、统一 loading、超时重试都写在utils/request.js里页面只管调用const request (url, method GET, data {}) { const token wx.getStorageSync(token); return new Promise((resolve, reject) { wx.request({ url: ${BASE_URL}${url}, method, data, header: { Authorization: Bearer ${token} }, timeout: 10000, success: (res) { if (res.data.code 401) { wx.removeStorageSync(token); wx.navigateTo({ url: /pages/login/login }); return; } if (res.data.code ! 0) { wx.showToast({ title: res.data.msg, icon: none }); reject(res.data); return; } resolve(res.data); }, fail: (err) reject(err), }); }); };这个封装有几个细节值得留意请求失败时统一提示401 时清除本地 token 并跳转登录页为后面的订阅消息和支付回调预留统一的错误处理入口。每次请求最好都带一个loading状态控制参数避免用户重复点击提交。5.2 列表加载更多与顶部导航栏高度适配报修记录和账单记录都涉及分页加载小程序里用onReachBottom触发下一页加载维护一个 page 参数。接口返回结构统一为{ list, hasMore }前端根据hasMore判断是否显示加载中提示。顶部导航栏高度是新手容易踩坑的地方。如果页面需要自定义导航栏比如把搜索框或标题放在胶囊按钮同排要动态获取状态栏高度和胶囊位置const menuButton wx.getMenuButtonBoundingClientRect(); const statusBarHeight wx.getSystemInfoSync().statusBarHeight; const navBarHeight (menuButton.top - statusBarHeight) * 2 menuButton.height;这段代码返回的navBarHeight就是自定义导航栏高度适配刘海屏和正常屏都不会错。5.3 表单组件细节单选框、图片上传、textarea 防误触报修表单里的维修类型用单选框直接用小程序的radio组件即可注意值要跟后端字段对应。图片上传用wx.chooseMedia获取临时文件路径后再逐个调用wx.uploadFile传到后端这里要控制最大数量通常限 3 张并在上传进度里给用户反馈。提交按钮要加防重复提交逻辑点击后立即禁用按钮并在文案前加提交中...防止用户手抖连点两次造成重复工单。这属于最常见的用户交互细节直接影响业务数据质量。5.4 监听用户离开小程序的隐藏逻辑热词里有一条微信小程序如何监听用户离开小程序这个在小程序生命周期里是onHide和onUnload。我在报修页的onHide里会把未提交的草稿内容存进本地缓存用户再次进入时从缓存恢复避免填了一半的内容丢失。同页面还有onShow刷新的问题。比如用户在账单页支付成功后返回账单页的onShow要重新拉取账单状态否则用户会看到旧的未支付状态造成误解。6. 开发与部署过程中的踩坑清单这类项目真正耗时间的不是写代码而是各种环境的坑和联调问题。我把高频问题列成一份对照表并附上解决方案方便直接排查。问题现象根因解决方法本地跑 Django 报MySQLdb不存在的错误缺数据库驱动适配安装pymysql在项目__init__.py里执行pymysql.install_as_MySQLdb()部署后图片上传失败Nginx 上传文件大小限制Nginx 配置client_max_body_size 10m;DEBUGFalse后管理后台样式全丢静态文件未收集python manage.py collectstatic并配置 STATIC_ROOT小程序请求正式域名报非法域名未配置合法请求域名在小程序后台的「开发管理-服务器域名」里添加 HTTPS 域名微信支付回调重复处理回调请求可能重复推送用支付单号做唯一约束重复时直接忽略列表加载更多出现重复数据分页排序不稳定排序字段加id兜底避免同更新时间的数据跳页6.1 本地环境Python 版本和依赖管理要提前锁死最好一开始就在requirements.txt里锁版本。Django 我推荐 4.2 LTS长期维护稳定如果你用 Django 5 系列部分第三方库可能还没适配。Python 版本别追最新3.10 或 3.11 是当前兼容性最优的选择。本地开发时务必用虚拟环境我在实际项目里见过直接把依赖装进系统环境导致版本冲突的惨案。6.2 部署环境中两个隐蔽的小问题第一个是DEBUGFalse之后Django 不再处理静态文件和上传文件所有静态资源必须通过 Nginx 或者对象存储。第二个是 CORS 问题小程序端本身不受跨域限制但如果要额外做一个浏览器端管理页面就必须在后端配置跨域头我用了django-cors-headers处理只放开自己管理后台的域名。6.3 微信审核与正式环境之间的几个流程细节小程序开发工具里可以勾选不校验合法域名这能在开发阶段方便联调但上线前必须关闭。正式环境里域名必须备案且配置 HTTPSJava 证书我用的是云厂商的免费证书有效期一年需要留意到期前提醒。小程序提审前要准备好相应的服务类目。房屋租赁、维修服务类目下可能涉及营业执照和资质要求这些要在项目排期初期就确认好别等代码写完了才发现类目不对。年度审核相关的流程也记得提前安排避免线上小程序突然被停用。6.4 微信支付接入的实操心得支付这块是这个项目里比较绕的一环。核心流程是小程序请求后端创建支付单后端调用微信统一下单接口拿到prepay_id再签名生成支付参数返回给小程序小程序调用wx.requestPayment完成支付后端接收支付回调。回调处理必须做幂等校验。微信的支付回调可能会重复推送后端要按支付单号去重保证同一笔回调只处理一次。另外支付金额单位是分接口传参时一定要amount * 100转成整数否则偶尔会出现金额不对齐的线上问题。7. 一些模块上的优化思路做完核心功能后我基于实际使用体验又做了一些模块级迭代这些优化对提升系统使用感和降低运营成本有明显帮助。7.1 应收应付的逾期提醒与自动计提到期未付的应收账单会在租客小程序里以醒目方式展示并且每天定时扫描一遍逾期账单通过微信订阅消息推送提醒。定时任务用 Django 的django-crontab实现在服务器上配置一个分钟级的 cron 任务扫描due_date today and status ! paid的账单生成待跟进列表。应付侧则是反向操作管理员在后台能查看未来 7 天内的应付到期项方便提前备款避免因付款不及时影响维修师傅和供应商的合作关系。7.2 维修成本统计与异常工单预警通过把维修工单关联到房源和应付账单后台可以统计每个房源的月度维修成本发现维修频繁的问题房源便于管理者决定是否长期维修还是重新装修。这个统计接口用 Django ORM 聚合查询即可RepairOrder.objects.filter( created_at__yearyear, created_at__monthmonth, ).values(house_id).annotate( total_costSum(cost), countCount(id), ).order_by(-total_cost)7.3 报修进度的订阅消息推送给租客的进度推送我用的是微信订阅消息。工单每个状态变更时后端调用subscribeMessage.send推送一条进度通知。这里要注意订阅消息的一次性授权限制用户订阅一次只能收到一次推送所以只在最关键的状态节点已派单、已完工推送不要每个细节都推否则用户订阅很快耗尽反而收不到重要消息。最后说几句给自己的记录这个项目做完后我最深刻的体会是一开始总觉得报修和财务是两个系统硬捏在一起很别扭但真正把表结构和状态机梳理清楚之后发现它们本来就是一条业务链的首尾两端。如果重新做一遍我会在表设计阶段直接用状态机框架来表达工单流转权限部分也会尽早从单纯的字段判断升级成完整 RBAC省得后期加角色时才来重构。如果正在做同类型项目我的建议是先花两天把业务流转画清楚再动手建表和接口。表的关联、状态的可迁移路径、金额的核销关系这三件事比任何技术选型都重要。技术上的坑都可以查得到业务逻辑的坑才是最容易让整个项目推倒重来的地方。这套基于 Django 加微信小程序的房屋租赁维修与应收应付方案至少在我维护的项目里已经稳定跑了大半年希望这篇记录能帮你少走一段弯路。
返回列表