ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Django银行信贷管理系统实战:从数据模型到业务闭环的毕业设计落地

Django银行信贷管理系统实战:从数据模型到业务闭环的毕业设计落地 简介这是一套面向高校计算机相关专业学生与Java/Python初学者的毕业设计级项目源码主题为基于Django框架与MySQL数据库的银行信贷管理系统适合用作课程设计、毕业设计参考或Web开发练手案例。压缩包共2687个文件整体约5.66MB其中以JavaScript脚本、HTML页面与CSS样式文件为主构成前端交互与界面布局另有44个Python源文件承载Django后端业务逻辑并附带SQL建表脚本、SQLite数据库文件及图片、字体等静态资源结构完整、层次清晰。项目已通过本地编译验证按文档配置环境后即可运行难度适中内容经助教老师审定能够满足学习与使用需求。目前已有236人学习下载读者可从中获取完整的信贷业务模块实现思路、前后端分离的目录组织方式以及数据库表结构设计参考便于快速理解Django项目开发流程并在此基础上二次开发。1. 银行信贷管理系统用 Django 落地从毕业设计到能跑起来的业务闭环很多同学拿到「Python基于Djangomysql银行信贷管理系统设计毕业源码案例设计」这个题目时第一反应是去搜一套现成源码改改页面、换个配色就交差。但真正跑过一遍的人都知道信贷管理系统的难点从来不在页面上而在「一笔贷款从申请到放款再到还款」这条业务链怎么用数据表串起来。银行信贷管理系统本质上是一套围绕客户、授信额度、贷款合同、还款计划、逾期记录五个核心对象做增删改查和状态流转的信息系统Django 负责把业务规则写进模型层和视图层MySQL 负责把状态持久化。它适合正在做毕业设计、需要一套能演示完整业务流程的 Python 开发者也适合想用 Django 练手真实业务建模的入门者。这篇文章不讲空泛的架构图只讲怎么把这条链路在本地跑通、参数怎么设、哪些地方最容易翻车。2. 先把数据模型立住五张核心表怎么设计才不返工信贷系统的业务复杂度几乎全部压在数据模型上。模型设计错了后面写视图和模板时就会不断打补丁最后变成一堆 if-else 堆出来的玄学代码。我一般会先把业务对象之间的关系画清楚再动手写 models.py。2.1 客户、授信、贷款、还款计划、逾期记录的关系一个客户可以有多次授信申请一次授信通过后产生一个授信额度额度下可以有多笔贷款合同每笔合同对应一组按月拆分的还款计划还款计划逾期后生成逾期记录。这五张表的关系是客户表一对多授信表授信表一对多贷款合同表贷款合同表一对多还款计划表还款计划表一对多逾期记录表。# models.py 核心表结构 from django.db import models class Customer(models.Model): name models.CharField(max_length50, verbose_name客户姓名) id_card models.CharField(max_length18, uniqueTrue, verbose_name身份证号) phone models.CharField(max_length11, verbose_name手机号) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table t_customer class CreditApply(models.Model): STATUS_CHOICES [(0, 待审核), (1, 已通过), (2, 已拒绝)] customer models.ForeignKey(Customer, on_deletemodels.CASCADE) amount models.DecimalField(max_digits12, decimal_places2, verbose_name申请额度) status models.IntegerField(choicesSTATUS_CHOICES, default0) apply_time models.DateTimeField(auto_now_addTrue) class Meta: db_table t_credit_apply class LoanContract(models.Model): credit models.ForeignKey(CreditApply, on_deletemodels.PROTECT) principal models.DecimalField(max_digits12, decimal_places2, verbose_name贷款本金) rate models.DecimalField(max_digits6, decimal_places4, verbose_name年利率) periods models.IntegerField(default12, verbose_name期数) loan_date models.DateField(verbose_name放款日期) status models.IntegerField(default1, verbose_name合同状态) class Meta: db_table t_loan_contract class RepayPlan(models.Model): contract models.ForeignKey(LoanContract, on_deletemodels.CASCADE) period_no models.IntegerField(verbose_name期数序号) due_date models.DateField(verbose_name应还日期) principal_part models.DecimalField(max_digits12, decimal_places2) interest_part models.DecimalField(max_digits12, decimal_places2) paid models.BooleanField(defaultFalse) class Meta: db_table t_repay_plan class OverdueRecord(models.Model): plan models.ForeignKey(RepayPlan, on_deletemodels.CASCADE) overdue_days models.IntegerField(default0) penalty models.DecimalField(max_digits10, decimal_places2, default0) created_at models.DateTimeField(auto_now_addTrue) class Meta: db_table t_overdue_record这段代码里几个参数值得单独说。DecimalField的max_digits和decimal_places必须根据业务量级设定贷款本金用 12 位总长、2 位小数足够覆盖千万级利率用 6 位总长、4 位小数是为了保留像 0.0485 这种精确到万分位的年化利率。on_delete的选择很关键客户删了授信跟着删用CASCADE但授信删了合同不能跟着删必须用PROTECT否则历史合同会凭空消失这在信贷场景里是致命的。db_table显式指定表名避免 Django 默认的app_model命名在后期做数据迁移时对不上。2.2 用 Django 创建 app 并生成迁移模型写完后标准流程是创建 app、注册、生成迁移文件、执行迁移。这几步看起来简单但顺序错了或者 app 没注册makemigrations会直接告诉你「No changes detected」新手经常卡在这里。# 创建 app python manage.py startapp loan # 在 settings.py 的 INSTALLED_APPS 里加入 loan # 然后执行迁移 python manage.py makemigrations loan python manage.py migratestartapp之后必须手动把loan加到INSTALLED_APPS这一步漏了是最常见的「迁移不生效」原因。makemigrations后面跟 app 名可以只针对这个 app 生成迁移避免把其他 app 的改动混进来。执行migrate之前建议先确认 MySQL 数据库已经建好并且settings.py里的DATABASES配置指向正确的库。2.3 MySQL 连接配置与字符集设置Django 连 MySQL 需要在settings.py里配好引擎、库名、账号密码和字符集。字符集不设utf8mb4客户姓名里的生僻字或者备注里的特殊符号就会报错。# settings.py 数据库配置 DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: bank_credit, USER: root, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { charset: utf8mb4, init_command: SET sql_modeSTRICT_TRANS_TABLES, }, } }charset用utf8mb4而不是utf8因为 MySQL 的utf8实际只支持三字节存不下完整的四字节字符。init_command里开STRICT_TRANS_TABLES是为了让字段超长、类型不符时直接报错而不是静默截断信贷数据被截断的后果不用我多说。另外记得在项目__init__.py里加pymysql.install_as_MySQLdb()否则 Django 找不到 MySQL 驱动。3. 业务逻辑写进视图授信审批和还款计划生成怎么实现模型立住之后真正体现业务价值的是视图层。信贷系统里有两个逻辑最重的地方授信审批的状态流转以及放款后还款计划的自动生成。这两块写不好演示的时候就会露馅。3.1 授信审批的状态流转与权限控制授信申请提交后是「待审核」风控人员审核后改成「已通过」或「已拒绝」。这个流转不能只靠前端按钮控制后端必须校验当前状态防止重复审批或者越权操作。# views.py 授信审批 from django.contrib.auth.decorators import login_required from django.http import JsonResponse from .models import CreditApply login_required def approve_credit(request, apply_id): if not request.user.has_perm(loan.change_creditapply): return JsonResponse({code: 403, msg: 无审批权限}) apply CreditApply.objects.get(idapply_id) if apply.status ! 0: return JsonResponse({code: 400, msg: 该申请已处理不可重复审批}) action request.POST.get(action) if action pass: apply.status 1 elif action reject: apply.status 2 else: return JsonResponse({code: 400, msg: 非法操作}) apply.save() return JsonResponse({code: 200, msg: 审批完成})这里用has_perm做权限校验配合 Django 自带的权限系统比在视图里硬编码角色判断更干净。状态判断放在最前面只要status ! 0就直接拒绝这是防止重复审批的关键。action参数只接受pass和reject两个值其他一律拒绝避免前端传参被篡改。3.2 等额本息还款计划的生成算法放款之后要按合同金额、利率、期数生成每期的还款计划。等额本息是最常见的还款方式公式是每期还款额 本金 × 月利率 × (1月利率)^期数 / ((1月利率)^期数 - 1)。# services.py 还款计划生成 from decimal import Decimal, ROUND_HALF_UP from datetime import date from dateutil.relativedelta import relativedelta from .models import LoanContract, RepayPlan def generate_repay_plan(contract: LoanContract): monthly_rate contract.rate / Decimal(12) periods contract.periods principal contract.principal factor (1 monthly_rate) ** periods monthly_pay principal * monthly_rate * factor / (factor - 1) monthly_pay monthly_pay.quantize(Decimal(0.01), roundingROUND_HALF_UP) remain principal plans [] for i in range(1, periods 1): interest (remain * monthly_rate).quantize(Decimal(0.01), roundingROUND_HALF_UP) principal_part monthly_pay - interest if i periods: principal_part remain monthly_pay principal_part interest remain - principal_part plans.append(RepayPlan( contractcontract, period_noi, due_datecontract.loan_date relativedelta(monthsi), principal_partprincipal_part, interest_partinterest, )) RepayPlan.objects.bulk_create(plans)Decimal全程参与计算是硬性要求用 float 算利息最后一定对不上账。quantize统一保留两位小数并指定ROUND_HALF_UP避免银行场景里出现四舍五入争议。最后一期做特殊处理把剩余本金全部还完否则因为逐期舍入误差最后一期会差几分钱。bulk_create一次性写入所有期数比循环save快一个数量级12 期可能看不出来60 期就很明显了。3.3 用 ORM 做逾期扫描与罚息计算每天需要扫描所有未还且已过应还日期的还款计划计算逾期天数和罚息。这个逻辑适合写成管理命令配合定时任务执行。# management/commands/scan_overdue.py from django.core.management.base import BaseCommand from django.utils import timezone from decimal import Decimal from loan.models import RepayPlan, OverdueRecord class Command(BaseCommand): help 扫描逾期还款计划并生成逾期记录 def handle(self, *args, **options): today timezone.localdate() overdue_plans RepayPlan.objects.filter(paidFalse, due_date__lttoday) count 0 for plan in overdue_plans: days (today - plan.due_date).days penalty (plan.principal_part plan.interest_part) * Decimal(0.0005) * days OverdueRecord.objects.update_or_create( planplan, defaults{overdue_days: days, penalty: penalty.quantize(Decimal(0.01))}, ) count 1 self.stdout.write(f处理逾期计划 {count} 条)update_or_create保证同一条计划重复扫描时不会产生多条逾期记录只更新天数和罚息。罚息系数0.0005是日万分之五这个值要按实际业务规则调整写成常量方便后期改。timezone.localdate()而不是date.today()是为了和 Django 的时区配置保持一致避免跨时区部署时日期差一天。4. 避坑与排查这套系统最容易翻车的五个地方信贷系统的坑大多集中在数据库连接、时区、金额精度和权限这几块。下面五条是我自己踩过或者帮别人排查过的真实问题按「现象 → 原因 → 解决」写清楚。4.1 迁移时报错 Cant connect to local MySQL server现象是执行python manage.py migrate时抛出django.db.utils.OperationalError: (2002, Cant connect to local MySQL server through socket /tmp/mysql.sock)。原因是 Django 默认走 socket 连接而配置里HOST写的是127.0.0.1时应该走 TCP但 MySQL 客户端库在某些环境下仍然尝试 socket。解决办法是在DATABASES里显式写HOST: 127.0.0.1和PORT: 3306不要留空如果 MySQL 服务没启动先确认服务状态再重试。4.2 金额字段出现浮点误差现象是还款计划里本金加利息和合同总额差几分钱。原因是模型里用了FloatField或者 Python 的 float 参与计算。解决办法是把所有金额字段改成DecimalField计算时用Decimal并显式quantize最后一期做余额兜底。这个坑在演示时特别容易被老师追问提前改掉省心。4.3 审批接口被重复调用导致状态错乱现象是同一个授信申请被审批两次第二次把「已通过」改成了「已拒绝」。原因是视图里没有做状态前置校验只依赖前端按钮禁用。解决办法是在视图入口先查当前状态非「待审核」直接返回错误码前端按钮禁用只是体验优化不能当安全边界。4.4 时区不一致导致逾期天数算错现象是明明当天到期的计划被算成逾期一天。原因是settings.py里USE_TZ和TIME_ZONE配置与数据库存储不一致date.today()和timezone.localdate()返回的日期不同。解决办法是统一用timezone.localdate()取当前日期USE_TZ TrueTIME_ZONE Asia/Shanghai并且数据库连接不额外设时区。4.5 权限判断漏掉导致越权访问现象是普通用户能直接访问审批 URL 并成功提交。原因是视图只加了login_required没做权限校验。解决办法是配合 Django 权限系统用has_perm判断或者在admin里给用户组分配权限视图里统一校验。毕业设计演示时如果被问到权限这一条能加分。5. 进阶技巧用 Django 信号和数据库连接池把系统做扎实基础功能跑通之后有两个进阶点能让这套银行信贷管理系统从「能演示」变成「像那么回事」。第一个是用 Django 信号在合同创建后自动触发生成还款计划第二个是给 MySQL 配连接池避免并发演示时连接数打满。5.1 用 post_save 信号自动生成还款计划手动在视图里调generate_repay_plan容易漏调用信号绑定在LoanContract保存之后自动执行业务上更稳。# signals.py from django.db.models.signals import post_save from django.dispatch import receiver from .models import LoanContract from .services import generate_repay_plan receiver(post_save, senderLoanContract) def on_contract_created(sender, instance, created, **kwargs): if created: generate_repay_plan(instance)created判断保证只在新建合同时生成计划更新合同不会重复生成。信号要在apps.py的ready()里导入否则不会注册。这个写法把「生成计划」和「创建合同」解耦视图层只管创建合同职责更清晰。5.2 给 MySQL 加连接池Django 默认每个请求新建连接演示时并发一高就容易报Too many connections。用django-db-connection-pool或者直接在DATABASES里配CONN_MAX_AGE都能缓解。DATABASES[default][CONN_MAX_AGE] 600 DATABASES[default][OPTIONS][pool] { minsize: 2, maxsize: 10, pool_recycle: 3600, }CONN_MAX_AGE设 600 秒表示连接复用十分钟pool_recycle设 3600 秒让连接每小时回收一次避免 MySQL 的wait_timeout把空闲连接掐掉后 Django 还在用。minsize和maxsize按演示机器配置调一般 2 到 10 够用。5.3 用 Django unfold 美化后台毕业设计答辩时后台界面是加分项。django-unfold可以在不写前端的情况下把 admin 换成现代风格配置也简单。# settings.py INSTALLED_APPS [ unfold, unfold.contrib.filters, django.contrib.admin, # ... 其他 app ] UNFOLD { SITE_TITLE: 银行信贷管理系统, SITE_HEADER: 信贷业务后台, SHOW_HISTORY: True, }装好之后admin.py里正常注册模型即可unfold会自动接管样式。注意unfold要放在django.contrib.admin前面顺序错了样式不生效。这个技巧不改变业务逻辑纯粹提升观感答辩前花十分钟配一下很值。我自己做这类系统最大的教训是别一上来就写页面先把模型和状态流转想清楚后面写视图和模板就是顺水推舟。模型返工的代价远大于页面返工。希望帮到你。本文还有配套的精品资源点击获取
返回列表