ARTICLE DETAIL

资讯详情

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

基于Django和大数据的图书推荐系统设计与协同过滤实现

基于Django和大数据的图书推荐系统设计与协同过滤实现 又到一年毕设季图书推荐系统这个题目几乎年年都有但真正能拿出来讲的东西却不多。很多同学一搜“图书推荐系统”就是一堆老掉牙的JSPServlet项目界面停留在十年前推荐逻辑就是一个简单的“按热度排序”。今年我带学生做了一套基于django大数据技术的图书推荐系统从数据处理、推荐算法到可视化大屏全部走了一遍前后花了两周时间。这篇文章把整个项目的设计与实现细节完整拆开从技术选型到协同过滤算法的落地再到远程调试和部署交付的实操路线都给你整理清楚。如果你是计算机相关专业、正在准备毕业设计或者想快速搭一套能演示的推荐系统这篇内容可以直接照抄作业。1. 项目全貌与设计思路1.1 选题背景与真实需求图书推荐系统在大数据毕设里属于“经典中的经典”它的好处是业务模型清晰用户、图书、评分/行为这三张表就是整套系统的主干延展性强既可以做基础CRUD也可以往上叠加协同过滤、用户画像、数据可视化等复杂功能。更重要的是推荐系统的评估指标比较直观准确率、召回率、覆盖率都能算论文的“实验结果”章节不会空着。但很多学生做完之后要么过于简单就是一个图书列表加一个收藏功能要么过于复杂代码堆了五六个Github项目却连数据链路都跑不通。这套系统在设计时我先定了一条主线围绕“数据-算法-可视化”三层展开用户行为数据从采集、清洗到入库推荐引擎基于协同过滤计算并产出TopN候选集最后通过ECharts大屏把推荐效果和平台数据展示出来。这套结构既符合答辩老师对“大数据技术应用”的期待也保证了代码量和工作量饱满。1.2 功能模块拆解系统分为前台和后台两大块具体功能如下前台用户端注册登录、图书浏览、图书详情、评分操作、收藏图书、个性化推荐猜你喜欢、热门榜单。后台管理端图书管理、用户管理、评分数据管理、推荐算法参数配置、系统数据概览。数据展示大屏用户行为概览、图书热度排行、推荐命中转化、基于ECharts的实时可视化图表。推荐模块是核心实际实现时我做了两种推荐策略一种是基于物品的协同过滤ItemCF按用户历史评分找相似图书另一种是基于用户的协同过滤UserCF按相似用户的偏好做推荐。默认启用ItemCF因为图书场景下用户行为稀疏物品相似度比用户相似度稳定得多。1.3 项目的技术亮点说几个答辩时可以主动讲出来的亮点数据量上做了模拟仿真生成了近万条用户行为记录能够体现“大数据”特征。推荐算法使用“离线计算在线读取”模式提前算出相似度矩阵和推荐结果接口响应速度控制在毫秒级。数据可视化不是简单画图而是把推荐系统的核心指标曝光、点击、命中率直接映射成看板体现了数据闭环思想。提供远程调试能力和两种一键启动脚本Windows和Linux都能跑部署环境不挑。2. 技术栈选型与系统架构2.1 为什么选django图书推荐系统可选的后端框架很多Spring Boot、Flask、FastAPI各有拥趸。但在毕设这个场景下django的优势非常突出一是自带Admin后台图书、用户、评分管理直接复用admin模块省去大量后台页面的开发时间二是ORM非常成熟三个核心模型User、Book、Rating用Python类定义好迁移命令一执行表就建好了三是模板引擎配合Bootstrap、ECharts做页面非常顺手不需要前后端分离也能实现完整的展示效果。不过要注意django默认的MTV模式里视图View和模板Template耦合比较高项目大了会显得杂乱。我的做法是全项目保持“视图只做数据组装模板只做渲染”的约束所有业务逻辑都放进service层这样后期改推荐算法时不动视图代码直接在service层替换实现类就行。2.2 大数据技术在项目中怎么体现这里要提前说清楚毕设里的大数据不是让你搭Hadoop集群而是用大数据处理思维解决具体问题。这套系统我用了三个大数据相关组件Pandas用于离线数据处理和特征工程。原始的行为日志是CSV文件用Pandas进行数据清洗、去重、用户ID对齐输出标准化的评分表。NumPy用于协同过滤的矩阵运算。用户-图书评分矩阵的构建、相似度的批量计算都依赖NumPy的向量化操作速度快且代码简洁。ECharts数据可视化。从MySQL聚合出的统计结果通过JSON传递到前端ECharts渲染体现数据可视化环节。这套方案的好处是不依赖重型大数据组件本地就能跑环境搭建成本低但该有的数据处理链路、算法实现和可视化展示一个不少答辩时老师问“大数据体现在哪里”你可以有理有据地回答。2.3 整体架构与数据流系统架构按照“数据采集 → 数据清洗 → 数据存储 → 算法计算 → 结果展示”五层设计数据采集层通过爬虫和模拟脚本生成图书信息和用户行为数据行为数据包括评分、收藏、浏览。数据清洗层用Pandas对原始数据进行空值填充、重复值处理、异常值剔除输出标准化的CSV或直接入库。数据存储层MySQL保存用户、图书、评分、推荐结果四类核心表Redis可选用于在线推荐结果缓存。算法计算层基于协同过滤实现推荐引擎定时离线计算推荐列表存入结果表。展示层django模板ECharts渲染推荐结果和大屏图表。这套架构的可扩展性非常好后续如果有时间可以把清洗层替换成Spark把存储层替换成HBase就变成标准的“大数据平台”架构了。但那是加分项不是必需项。2.4 项目目录结构规划我习惯在动手写代码之前先把目录结构定好避免后期代码到处乱丢。这套系统的目录结构如下book_recommend/ ├── manage.py ├── requirements.txt ├── config/ # 项目配置 │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── apps/ │ ├── users/ # 用户模块 │ ├── books/ # 图书模块 │ ├── ratings/ # 评分行为模块 │ ├── recommend/ # 推荐引擎模块 │ └── dashboard/ # 数据可视化模块 ├── data/ # 原始数据与清洗脚本 │ ├── raw/ │ ├── clean/ │ └── generate_data.py ├── static/ # 静态资源 ├── templates/ # 模板文件 └── scripts/ ├── run_server.sh └── run_windows.bat模块化拆分后每个功能区块对应一个django app互不干扰。如果你是想拿这套项目做毕设强烈建议保留这种结构在你论文的“系统设计”章节里可以原样画一张架构图出来。3. 数据准备与清洗入库3.1 图书数据的获取策略图书数据是推荐系统的基础质量直接决定推荐效果。我整理数据时用了两种方式一类是通过公开的数据集比如从Kaggle的Book-Crossing或国内的公开图书语料中提取获取结构性较好的图书信息另一类是编写Python脚本爬取公开图书网站的标题、作者、出版社、分类、封面图和简介。爬虫部分要注意robots协议和请求频率我采用的是对单页进行限速抓取再配合数据脱敏和字段规整保证拿到的数据可以合法使用。当然如果你不想处理爬虫的麻烦也可以直接用随机生成的模拟数据——但推荐效果会显得假答辩容易露馅。我的建议是真实数据打底模拟数据补量。3.2 清洗流程与关键操作原始数据不能直接入库必须先清洗。下面这段代码是数据清洗阶段的核心流程基于Pandas实现import pandas as pd # 读取原始评分数据 df pd.read_csv(data/raw/user_behavior.csv) # 1. 去重同一用户对同一本书的多次评分保留最新一条 df df.sort_values(timestamp).drop_duplicates( subset[user_id, book_id], keeplast ) # 2. 空值处理评分缺失的按用户平均分填充 df[rating] df[rating].fillna( df.groupby(user_id)[rating].transform(mean) ) # 3. 异常值过滤评分范围限制在1-5之间 df df[(df[rating] 1) (df[rating] 5)] # 4. 用户活跃度过滤删除行为数少于5条的用户 user_count df[user_id].value_counts() active_users user_count[user_count 5].index df df[df[user_id].isin(active_users)] # 5. 输出清洗后的数据 df.to_csv(data/clean/ratings_clean.csv, indexFalse)第4步很关键。协同过滤算法依赖于“用户之间要有共同评分过的物品”如果大量用户只有一两条评分记录相似度矩阵会变得极度稀疏最终推荐结果基本靠随机。在我生成的数据集中原本有两万条行为记录清洗后剩下一万六千多条用户覆盖率和行为丰富度明显提升。3.3 数据入库与模型设计清洗完成后数据通过django的ORM写入MySQL。相关的数据模型如下from django.db import models from django.contrib.auth.models import AbstractUser class User(AbstractUser): avatar models.URLField(blankTrue, verbose_name头像) birthday models.DateField(nullTrue, blankTrue, verbose_name生日) class Meta: verbose_name 用户 class Book(models.Model): title models.CharField(max_length200, verbose_name书名) author models.CharField(max_length100, verbose_name作者) publisher models.CharField(max_length200, blankTrue, verbose_name出版社) category models.CharField(max_length50, verbose_name分类) pub_date models.DateField(nullTrue, blankTrue, verbose_name出版日期) rating_avg models.FloatField(default0, verbose_name平均评分) rating_count models.IntegerField(default0, verbose_name评分人数) cover_url models.URLField(blankTrue, verbose_name封面地址) intro models.TextField(blankTrue, verbose_name简介) class Meta: verbose_name 图书 class Rating(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name用户) book models.ForeignKey(Book, on_deletemodels.CASCADE, verbose_name图书) score models.IntegerField(verbose_name评分, choices[(i, i) for i in range(1, 6)]) create_time models.DateTimeField(auto_now_addTrue, verbose_name评分时间) class Meta: unique_together (user, book) verbose_name 评分记录User模型继承自AbstractUser直接复用django自带的认证体系省去了自己实现登录注册的麻烦。Book模型里的rating_avg和rating_count是两个冗余字段用于排行榜展示和推荐算法预处理避免每次都要countavg去扫描评分表。这种用空间换时间的做法在真实推荐系统里也很常见。4. 推荐算法落地细节4.1 为什么用协同过滤而不是深度学习很多同学一上来就想上深度学习模型但说实话在毕设的体量下协同过滤是性价比最高的选择。一是解释性强相似的用户买了什么、相似的图书被谁喜欢这些逻辑通俗易懂答辩时老师能跟上你的思路二是计算可控一万用户、一万本书的规模协同过滤用普通的服务器几分钟就能算出结果三是评估容易可以离线算准确率、召回率也可以在线算人工评估的结果。当然如果你学有余力可以在协同过滤的结果之上叠加一个简单的热门惩罚或多样性重排这样整个推荐流程就有了“召回-精排-重排”的雏形显得更专业。4.2 基于物品的协同过滤实现这一部分是项目的算法核心。实现思路拆成四步第一步构建“用户-图书”评分矩阵行是用户列是图书值是评分。 第二步计算图书之间的相似度。我用的是余弦相似度因为评分数据是数值型余弦相似度能很好地体现两个向量方向的差异。 第三步根据目标用户已评分的图书找到每本书最相似的K本书加权汇总成候选推荐列表。 第四步过滤掉用户已读过的图书按得分排序取TopN输出。下面给出核心的相似度计算和推荐代码import numpy as np import pandas as pd from sklearn.metrics.pairwise import cosine_similarity # 读取清洗好的评分数据 ratings pd.read_csv(data/clean/ratings_clean.csv) # 构建用户-图书评分矩阵 user_item_matrix ratings.pivot_table( indexuser_id, columnsbook_id, valuesrating ).fillna(0) # 计算图书之间的余弦相似度矩阵 # 这里转置之后行表示图书列表示用户 item_similarity cosine_similarity(user_item_matrix.T) item_sim_df pd.DataFrame( item_similarity, indexuser_item_matrix.columns, columnsuser_item_matrix.columns ) def recommend_for_user(user_id, top_k10, sim_threshold0.4): # 获取该用户的评分记录 user_ratings ratings[ratings[user_id] user_id] if user_ratings.empty: return [] # 记录用户已经评过分的书最终推荐时排除 rated_books set(user_ratings[book_id]) # 候选集所有与用户评过分的书相似度超过阈值的书 scores {} for book_id, score in user_ratings[[book_id, score]].values: sims item_sim_df[book_id] for candidate, sim in sims.items(): if candidate in rated_books: continue if sim sim_threshold: continue scores[candidate] scores.get(candidate, 0) sim * score # 按推荐得分排序取TopN top_books sorted(scores.items(), keylambda x: x[1], reverseTrue)[:top_k] return [book_id for book_id, _ in top_books]这里提几个细节相似度阈值不能设得太高我试过0.6会导致大量图书没有候选推荐设得太低噪声大推荐结果不精准。0.3到0.4之间效果比较好。加权公式用的是相似度 × 用户评分也就是用户越喜欢的书它的相似书权重越高。这个权重计算看起来很朴素但在图书场景下效果完全可以打。4.3 离线计算与定时更新为避免每来一个请求都跑一遍全量矩阵运算我给推荐模块设计了“离线计算结果入库”的模式。后台管理页面提供“重新计算推荐”按钮点击后执行上述算法把每个用户的TopN推荐列表写入一张推荐结果表。系统同时支持计划任务设定每天凌晨2点自动重算一次。# 伪代码示意更新全部用户的推荐结果 from apps.recommend.utils import recommend_for_user def update_all_recommendations(): user_ids User.objects.values_list(id, flatTrue) for uid in user_ids: rec_books recommend_for_user(uid, top_k20) # 清空旧结果写入新结果 RecommendResult.objects.filter(user_iduid).delete() RecommendResult.objects.bulk_create([ RecommendResult(user_iduid, book_idb, scorei) for i, b in enumerate(rec_books) ])批量插入用bulk_create不要逐条create否则一万个用户能把数据库事务拖垮。我在第一次实现时偷懒用循环逐条插入结果跑了十几分钟还没算完。改用bulk_create后整体时长压缩到几十秒内。4.4 推荐接口的对外输出推荐结果表写好后前端通过一个统一接口读取def recommend_view(request): user request.user if not user.is_authenticated: # 未登录用户返回热门排行 books Book.objects.order_by(-rating_avg)[:10] else: recs RecommendResult.objects.filter(useruser).order_by(score) books [rec.book for rec in recs[:10]] return render(request, recommend.html, {books: books})未登录用户能看到热门榜单兜底登录用户看到个性化列表这是产品逻辑也是答辩时可以补充说明的细节。5. 可视化大屏与前端展示5.1 大屏数据的聚合逻辑推荐系统光有推荐列表还不够一定要有一屏能“震住场子”的数据可视化大屏。这个模块在毕设里特别加分因为老师第一眼看到的就是你的成果展示页。大屏上的每个图表背后都对应一条数据聚合SQL或Pandas统计逻辑。用户注册趋势按天统计用户注册数用折线图展示。图书分类分布按category字段分组统计图书数量用饼图展示。评分分布统计1-5分的评分数量用柱状图展示。热门图书排行榜按rating_count排序用横向条形图展示Top10。推荐命中率统计用户在推荐位上的点击/加收藏行为换算成命中率。其中“推荐命中率”是这套系统的特色指标它通过埋点记录用户在推荐位上的操作行为回传到行为表中。这个指标能直接说明推荐算法的效果好坏也是答辩时最能“秀”的数据点。5.2 ECharts与django的集成方式ECharts不复杂核心就是把数据从后端传出来再塞进图表的option。后端视图代码如下from django.http import JsonResponse from django.db.models import Count from apps.books.models import Book def category_stats(request): data ( Book.objects .values(category) .annotate(countCount(id)) .order_by(-count) ) result { categories: [item[category] for item in data], counts: [item[count] for item in data] } return JsonResponse(result)前端页面中我通过Ajax请求这个接口拿到数据后动态设置ECharts的option$.getJSON(/dashboard/category_stats/, function(res) { var chart echarts.init(document.getElementById(categoryChart)); chart.setOption({ tooltip: { trigger: item }, series: [{ type: pie, radius: 65%, label: { show: true, formatter: {b}: {c} }, data: res.categories.map(function(cat, i) { return { name: cat, value: res.counts[i] }; }) }] }); });模板页面里只需要放一个div作为图表容器并记得在页面底部加载ECharts的CDN资源和上面的JS代码。这套组合拳打下来数据展示的效果立刻上一个档次。5.3 页面与视觉风格的把握大屏页面我建议采用深色背景亮色图表科技感强答辩演示时在投影仪上也看得清楚。布局上顶部是系统标题和核心指标卡片总用户数、总图书数、总评分量、平均分中间主体放推荐命中率和趋势图左右两侧分别是图书分类占比和热门榜单。整体用Flex弹性布局或Grid网格布局无需复杂框架Bootstrap 5的栅格系统就足够。如果想把视觉效果再拔高一点还可以加一个简单的“滚动数字”动画每次刷新页面时用户数、图书数从0滚动到目标值。这个细节成本极低但对演示效果有奇效。6. 用户与权限体系设计6.1 django自带的认证体系复用用户模块没有必要从零写登录注册django的AbstractUser封装好了密码哈希、会话管理、权限校验。前端登录、注册、退出分别对应三行代码。注册页面我用一个自定义表单接收用户名、密码、邮箱等字段重写UserCreationForm可以快速实现定制。from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User class RegisterForm(UserCreationForm): email forms.EmailField(requiredTrue) class Meta: model User fields [username, email, password1, password2] def save(self, commitTrue): user super().save(commitFalse) user.email self.cleaned_data[email] if commit: user.save() return user密码的哈希存储和校验全部交给django内部处理这里有一个非常重要的安全点不要自己去实现密码的明文比对也不要将密码明文入库。答辩时如果被问到密码安全直接回答“django默认使用PBKDF2算法对密码进行单向哈希我们复用了这套机制”就是标准答案。6.2 权限与后台管理的配置后台数据管理直接使用django自带的Admin模块。我保留了三大核心数据表的管理入口并为不同的表配置了不同的权限普通用户只能浏览图书和进行评分运营角色可以管理图书超级管理员拥有全部权限。这个权限模型通过django的PermissionMixin实现配置很灵活。Admin的显示列表也很重要下面这段代码可以让管理后台更清晰from django.contrib import admin from apps.books.models import Book admin.register(Book) class BookAdmin(admin.ModelAdmin): list_display (id, title, author, publisher, category, rating_avg, rating_count) search_fields (title, author, publisher) list_filter (category,) ordering (-rating_count,)有这一层后台图书管理和数据管理就不再需要单独写一堆页面工作量直接减半。这也是我在文章一开头推荐django做毕设项目的核心原因。7. 远程调试、部署与验收交付7.1 远程调试模式的实现毕设项目有一个实际情况大部分同学主要在自己的电脑上开发但代码最终要部署到另一台服务器上给老师演示。为了不在现场翻车我留了一个“远程调试”的接口本质上就是一套完整的日志追踪和错误反馈机制。首先是日志分级settings.py里配置了INFO、WARNING、ERROR三档日志运行日志按天切割控制台和文件双输出LOGGING { version: 1, disable_existing_loggers: False, handlers: { console: {class: logging.StreamHandler}, file: { level: INFO, class: logging.handlers.TimedRotatingFileHandler, filename: logs/project.log, when: midnight, backupCount: 7 } }, root: {handlers: [console, file], level: INFO} }其次是调试辅助接口我专门写了一个/debug/health/接口返回当前数据库连接状态、缓存状态、推荐结果记录数和最近一次推荐计算的时间戳。远程排查问题时先看这个接口的返回值就能快速定位是不是数据链路断了。最后是异常兜底页面django的500错误页里直接展示异常堆栈仅在DEBUG模式下开启或返回一个JSON格式的错误码方便我在远程查看具体是哪一个模块出的问题。7.2 部署环境与启动脚本项目使用pipenv管理依赖requirements.txt里列出了所有必要的包关键是锁定版本号避免环境不一致导致的灵异问题。下面是我这份项目用的依赖清单精简版Django4.2.5 mysqlclient2.2.0 pandas2.0.3 numpy1.25.2 scikit-learn1.3.0 requests2.31.0 python-dotenv1.0.0部署时我写了两个启动脚本一个给Mac/Linux一个给Windows# run_server.sh #!/bin/bash pip install -r requirements.txt python manage.py migrate python manage.py collectstatic --noinput python manage.py runserver 0.0.0.0:8000:: run_windows.bat echo off pip install -r requirements.txt python manage.py migrate python manage.py collectstatic --noinput python manage.py runserver 0.0.0.0:8000 pause这里的collectstatic命令是给生产环境收集静态文件用的。如果你只在本机测试runserver就够了。7.3 交付清单与验收标准一套完整交付的项目不只是代码。我自己的交付清单是这样的源码压缩包包含完整项目目录和数据库初始化SQL文件。环境依赖清单requirements.txt和安装说明文档。数据库初始化脚本一步执行即可建库建表并写入演示数据。项目讲解PPT重点讲清系统架构、推荐算法和实验对比。部署与运行视频录一段从解压到运行成功的全过程。论文大纲和章节建议包含摘要、系统设计、数据库设计与实验分析的标准结构。验收标准方面我要求系统必须满足“三分钟启动、五分钟演示、十分钟讲清”的原则。也就是说在任何一台干净的机器上从拉取代码到系统跑起来不超过三分钟演示流程不能超过五分钟否则现场节奏会散讲原理时十分钟内能把推荐算法和数据流程说清楚。8. 常见问题与排查技巧8.1 数据库连接报错最常见的问题就是mysqlclient安装失败在Windows上尤为突出很多同学卡在这一步。解决办法是使用预编译的whl包安装或者使用PyMySQL作为替代方案# settings.py import pymysql pymysql.install_as_MySQLdb()如果是连接报错优先检查数据库是否启动、账号密码是否正确、库是否已创建。这一步可以用Navicat或命令行先手动连一下千万不要直接去改代码。8.2 推荐结果为空如果你刚导入数据就跑推荐很可能算法算完了但推荐结果表是空的。原因基本就两个一是相似度阈值太高导致没有候选集二是目标用户的行为数据太少。排查时先打印用户行为数量再检查相似度矩阵的非零值比例。我建议在管理后台加一个“推荐统计”的小卡片直接展示每个用户的推荐数量分布方便一眼看出问题。8.3 ECharts图表不显示ECharts图表不显示的原因通常不是数据的问题而是容器高度没设置。ECharts容器必须有明确的宽度和高度初始的div如果高度为0图表就渲染不出来。CSS里给图表容器设置height: 400px即可。另外如果异步加载数据要确保在AJAX回调中再init图表不要把init放在页面加载前。8.4 远程环境运行异常本地正常、服务器上跑不起来大概率是环境差异。我用一个检查脚本一次性自动检测Python版本、pip包版本、MySQL连接和静态文件目录权限把异常信息直接打印到控制台。这样远程管理时不用逐条敲命令一条命令就能诊断全部环境依赖问题。你可以在交付时把这个检查脚本一起放进去远程排查效率翻倍。8.5 关于定制与二次开发很多同学拿到项目后会想加功能我最常被问到的两个方向是“怎么加评论功能”和“怎么加图书推荐的分页”。评论功能本质就是多一张Comment表关联User和Book列表页展示时复用已有的模板分页逻辑即可。分页推荐列表则相对更好实现用django自带的Paginator对推荐结果做分页就可以了注意分页时不要重新计算推荐结果直接从结果表读取。最后强调一点任何定制改动前先备份数据库和代码否则改坏了没有后悔药。9. 写在最后的几点实操心得这套图书推荐系统从开发到完整交付我前后整理了两周最大的体会是推荐算法本身并不难真正的体力活全在数据处理和项目整理上。如果你拿到源码后想自己跑通建议按照“先启动后端、再导入数据、再触发离线推荐、最后看大屏”的顺序来做。不要一上来就去改算法参数先把数据链路跑通了再动推荐逻辑否则出了问题都不知道是哪里出的错。在实际操作中我还发现了一个小技巧做毕业设计千万不要只准备一条演示路径要预设至少两个“分支故事”。比如如果现场网络不好导致ECharts资源加载慢怎么快速降级成静态图片展示如果推荐结果不理想如何用“热门推荐”兜底如果老师问“你这个系统能支撑多少用户”你怎么从数据库索引、缓存层和异步任务三个角度回答。提前想好这些问题答辩的时候你的状态会完全不同。关于定制和扩展我建议你先跑通这套系统再根据自己的论文题目改数据来源和推荐策略。换图书数据是改动量最小的换评分规则也不难但如果你想换成视频推荐或新闻推荐核心的协同过滤逻辑并不需要动只需要把模型里的Book换成对应的内容实体即可。掌握了这套代码的骨架思路迁移到任何“内容推荐”场景都只是改改模型字段的问题。
返回列表