房屋销售管理系统设计与实现:数据库、接口与前端全流程解析
发布时间:2026/10/1 19:04:13 作者:尧图编辑部 阅读量:1,286

最近有朋友问我有没有适合毕业设计和练手的 Web 项目我第一个想到的就是房屋销售管理系统。原因很简单它麻雀虽小五脏俱全既有典型的增删改查又有房源、客户、跟进、预约、成交这条完整业务链还牵扯权限、图片上传、数据统计这些面试官最爱问的点。这套系统的核心不是技术难度而是业务流程怎么在数据库和页面上串成一条线。我前后搭过 Java 和 PHP 两个版本也用 Python 重写过一个简化版C# 那边也看过不少同行的实现思路发现底层逻辑大差不差。无论你用 Spring Boot、ThinkPHP、Django 还是 ASP.NET Core都能做区别只在开发效率和答辩时的表达侧重点。所以这篇基于 Web 的房屋销售管理系统的设计和实现我会从需求拆解、数据库设计、后端接口、前端页面、部署排错五个方面完整讲透Java、PHP、Python、C# 四条技术路线都能参考。这类系统最适合谁毕业设计、课程设计、转行找工作练手甚至小中介门店内部试用都没问题。你不需要会微服务、不需要会消息队列只要把 CRUD 写扎实、把状态流转想清楚、把权限和统计做好就已经超过绝大多数同类作品了。1. 整体设计与技术选型先搞清楚业务再动手写代码1.1 业务角色与核心模块拆解很多初学者拿到题目就急着建表我见过不止一个人先写用户表写完之后才发现业务角色没理清后面对不齐需求又反复改。正规做法是先画业务角色图和核心流程图。这套系统的角色一般分三类。管理员管后台负责维护用户账号、角色权限、数据字典偶尔处理一些异常订单和房源上下架审核销售顾问是核心使用者日常操作是录入房源、新增客户、写跟进记录、安排看房、登记合同管理层或者老板只看统计报表关心成交量、成交额、各个区域房源去化速度。对应的功能模块可以拆成七块房源管理房源信息的新增、编辑、上下架、带图展示、条件搜索。客户管理客户信息的录入、分配归属、客户来源统计。跟进记录每个客户的每一次电话、带看、微信沟通都按时间线追加。预约看房销售与客户约时间校验时间和房源的冲突。合同管理成交登记、合同编号生成、合同状态变化。收款管理定金、首付、尾款的到账记录。统计报表按月份、区域、销售顾问维度统计成交量、成交额和房源去化。这个模块设计带来的好处很明显每一个模块之间都有外键关联和状态联动天然覆盖了“一对多”“多对一”关系你的数据库设计有东西可以讲同时每个模块对前端来说都是一组独立页面页面数量够了展示效果也丰富。1.2 四种后端语言怎么选标题里提到了 Java、PHP、Python、C#我分别说下适用场景你自己判断。以下是我实操下来的真实感受不代表绝对标准答案。JavaSpring Boot资料最多、社区最活跃面试认可度最高。缺点是写起来重实体类、Mapper、Service、Controller 一堆文件开发速度慢一些。适合答辩的时候往“企业级开发”“项目分层规范”方向讲。PHPThinkPHP / Laravel开发效率是真的高模型层、验证器、中间件都有现成的一个控制器写完一整套接口也就一晚上。适合时间紧、想快速跑通全部功能的场景。缺点是面试时容易被追着问“高并发怎么处理”答案会比较尴尬。PythonDjango / Flask代码量少逻辑清晰尤其适合再加一个“房价趋势分析”或“客户画像”的加分功能用 pandas 做数据聚合比 Java 漂亮得多。Django 自带 Admin 后台演示管理功能时非常加分。C#ASP.NET Core如果你本身在 Windows 环境、用过 Visual Studio那也完全可以。EF Core 做数据库操作和迁移很顺手就是部署时如果只是 Windows 服务器别人跑起来会稍麻烦。我的建议很直接如果你只是想要一个稳妥的毕设项目首选 Spring Boot 或者 PHP如果你后期想往数据分析方向延伸选 Python。系统功能逻辑和数据库结构基本是通用的换语言只是换一层实现方式所以下面我讲核心设计的时候会尽量用数据库和接口思路来描述而不是绑定某一种语言。1.3 项目结构与开发环境规划从零开始搭这个项目时环境规划很重要。我常用的组合是后端Spring Boot 2.7 MyBatis-Plus MySQL 8.0或者 ThinkPHP 8 MySQL。前端Vue 3 Vite Element Plus方便做前后端分离。开发工具IDEA 或 VS Code数据库用 Navicat。接口调试Apifox 或 Postman。如果你不想搞前后端分离也可以直接用服务端渲染比如 PHP 的模板引擎或者 Java 的 Thymeleaf省去跨域问题但页面响应速度会差一些而且答辩时展示效果不如 SPA 流畅。我个人还是推荐 Vue 做前端因为现在前后端分离已是主流面试官看到“分离架构”本身就是一个加分项。项目目录上后端我习惯这样做分包controller/ # 接收请求、参数校验 service/ # 业务逻辑、事务控制 mapper/ # 数据库操作 entity/ # 数据实体 common/ # 统一返回结果、异常处理、工具类 config/ # 拦截器、跨域配置、静态资源配置这个结构本身也是答辩时介绍系统架构的素材。你在介绍系统时完全可以这样讲系统采用经典三层架构控制层负责请求分发业务层处理核心逻辑持久层访问数据库通过统一返回对象实现前后端解耦。2. 数据库设计把表建对了后面能少写一半代码2.1 核心表结构规划与字段说明数据库设计是整个系统最值得花时间的地方因为后面所有接口、页面、统计都依赖于表结构。我这个项目最终整理出了八张核心表再加上一个字典表基本覆盖全部业务。表名中文名说明sys_user用户表管理员、销售顾问等系统账号house房源表房屋基础信息、价格、面积、状态house_image房源图片表一个房源多张图片一对多关系customer客户表客户基础信息、归属销售、状态follow_record跟进记录表每次沟通的时间、内容、下一步计划visit_appointment看房预约表预约时间、房源、客户、状态contract合同表成交合同信息、成交金额、状态payment收款记录表每一笔收款的明细sys_dict数据字典表存房源朝向、装修、付款方式等枚举值用户表字段不复杂用户名、密码、手机号、角色标识、状态、创建时间。密码不要明文存用 BCrypt 或 PHP 的 password_hash 加密。客户表要有归属字段 seller_id这个字段决定销售只能看到自己名下的客户是后面权限控制的基础。房源表是整个系统的核心字段我会在下面单独讲。2.2 状态字段与价格字段的设计细节这是很多人容易踩坑的地方。首先价格字段千万不要用 double 或者 float因为浮点数有精度问题成交价和定金计算容易出现 0.1 0.2 不等于 0.3 的尴尬情况。正确做法是用 decimal(12,2)比如 5000000.00 就是五百万整这在 Java 里对应 BigDecimalPHP 里直接按字符串处理前端拿到的是字符串展示和计算都不会丢精度。状态字段建议用整数或短字符串并且要有一套统一的语义。我当时为房源设计了四状态1 在售、2 预订、3 已售、4 下架。客户状态也设计了四个1 潜在客户、2 跟进中、3 已成交、4 已流失。合同状态是1 待签约、2 已签约、3 已完成、4 已取消。为什么要专门设计状态因为业务关联性强。比如房源被预约后销售可能会把它置为“预订”状态这时其他销售在列表里就看不到了一旦合同签订房源状态要更新为“已售”后续任何编辑操作都要禁止。这些状态如果散落在代码里写成魔法值后面维护非常痛苦所以必须在代码常量类或字典表里统一管理。还有两个所有表都建议加的字段create_time 和 update_time以及 del_flag逻辑删除标识。有了 del_flag删除操作就变成 UPDATE 而不是 DELETE数据还在误删能恢复统计报表也不会因为物理删除而缺数据。这一点我在答辩和面试时被问过很多次属于高频加分点。2.3 建表脚本参考以 MySQL 为例下面给出核心表的简化建表脚本。不用完全抄重点关注类型选择和索引设计。CREATE TABLE house ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(100) NOT NULL, city VARCHAR(20), district VARCHAR(20), address VARCHAR(200), area DECIMAL(8,2) COMMENT 建筑面积, total_price DECIMAL(12,2) COMMENT 总价, unit_price DECIMAL(10,2) COMMENT 单价, room_count INT COMMENT 几室, hall_count INT COMMENT 几厅, toward VARCHAR(10) COMMENT 朝向, decoration VARCHAR(20) COMMENT 装修情况, floor_no INT, total_floor INT, house_status TINYINT DEFAULT 1 COMMENT 1在售 2预订 3已售 4下架, seller_id BIGINT COMMENT 归属销售, description TEXT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, del_flag TINYINT DEFAULT 0, INDEX idx_status_city_price (house_status, city, total_price) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;CREATE TABLE follow_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, customer_id BIGINT NOT NULL, seller_id BIGINT NOT NULL, follow_type VARCHAR(20) COMMENT 电话/微信/带看, content VARCHAR(500), next_plan VARCHAR(500), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, del_flag TINYINT DEFAULT 0, INDEX idx_customer (customer_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;关于外键很多课程设计喜欢在表上直接加 FOREIGN KEY我不推荐。原因有两点一是物理外键在更新、删除时会牵动很多表后期改数据麻烦二是线上系统和面试场景里“逻辑外键 应用层保证一致性”反而是更被认可的做法。你只需要在实体配置里标明关联关系业务代码做校验就够了。3. 后端核心接口设计与实现要点3.1 登录认证与权限控制登录认证是所有系统的第一道门。这里我不推荐在课程项目里用复杂的 Spring Security 全家桶或 Laravel 的完整权限体系而是建议掌握 JWT 这套东西又实用又容易讲清楚。JWT 的原理是后端登录成功后签发一个携带用户 ID 和角色信息的 JSON Web Token 给前端前端每次请求放在请求头 Authorization 里后端用拦截器解析校验无需在服务端保存 Session。实现流程大概是用户提交账号密码。后端比对密码成功则生成 token失效时间建议设 24 小时。前端把 token 存到 localStorage。后端写一个拦截器或中间件统一校验带过来的 token校验失败返回 401 并提示重新登录。权限控制怎么实现最简单但够用的方式是基于角色的菜单权限。admin 登录后能看到“系统管理”菜单销售顾问登录后看不到。前端根据后端返回的用户角色信息动态渲染菜单后端接口再用一个自定义注解或中间件做二次校验比如只有具备 admin 权限的角色才能调用删除用户的接口。JWT 相比传统 Session 的好处是无状态、天然适合前后端分离你用 Vue 调接口不会遇到跨域携带 Cookie 的坑。但有一点要注意JWT 一旦签发在过期之前无法主动失效所以管理员禁用某个账号时要额外加一层状态校验比如数据库里 user 表的 status 字段每次请求可以在拦截器里查一次或者用 Redis 做黑名单。3.2 房源管理接口分页搜索与图片上传房源管理是核心中的核心。列表接口必须支持分页和条件搜索至少包含城市、区域、价格区间、面积、户型、状态、关键词几个条件。接口设计成 GET /api/house/list前端传过来一堆 query 参数后端封装一个查询对象接收。分页是必考知识点。前端只需要传 page 和 size后端返回 total、records、current、size 四个字段。最好别用 LIMIT 100000,20 这种写法让前端看了无语用 MyBatis-Plus 的 Page 或者 PHP 的分页类都行。搜索条件里价格区间用 priceMin 和 priceMax 两个参数底层放到 SQL 的 WHERE 子句里做 between 区间过滤注意两个参数都不为空时才拼条件。图片上传这里我要重点说。最坑的做法是把图片转成 base64 字符串直接存进数据库因为一张 2MB 的图片转成 base64 后可能变成 3MB查列表接口一次返回几十条数据响应能慢到怀疑人生。正确定位是前端把图片文件通过 multipart/form-data 提交到 /api/file/upload 接口后端把文件保存到本地磁盘目录数据库里只存相对路径比如 /upload/20240901/xxx.jpg。这个方案还有一个好处部署上线时可以让 Nginx 直接托管 upload 静态目录图片请求完全不经过后端性能要好很多。这个我放到后面部署部分再细说。编辑删接口要关注状态已售和预订状态的房源常规编辑建议加校验删除建议做成逻辑删除只改 del_flag。3.3 跟进记录与看房预约的时序设计跟进记录这个模块很重要但实现很简单它属于一对多追加式记录。核心设计原则是客户详情页看跟进记录永远按时间倒序排列每条记录包含跟进方式、沟通内容、下次计划。销售每次新增跟进客户状态应该同步更新。潜在风险是并发写入问题不过一般毕设系统并发量不大给 customer_id 加索引就够了。要注意的是跟进记录只能追加不能覆盖否则销售写错内容就会把之前的记录弄没业务上不允许。看房预约是逻辑稍微复杂一点的模块。设计上至少要考虑三件事同一个房源同一时间段不能被重复预约。同一个客户同一时间段也不能有两条预约。预约通过后房源状态应变为预订。实现冲突校验时SQL 大致是查 visit_appointment 表里是否存在 target_date 相同、time_slot 有交集、且 state 为有效预约的数据。前端的日期选择器可以精确到小时后端也要做二次校验不要信任前端传上来的任何数据。预约的会话状态也分几种待确认、待看房、已完成、已取消、爽约。这里我强调一个实操细节预约时间字段建议拆成预约日期 visit_date 和时段 time_slot 两个字段不要把“2024-09-01 10:00”整个压进一个 datetime 里。拆开后做日历视图展示时简单很多。还要在处理预约确认时把房源的 status 改为 2如果预约取消再改回 1这些状态变更要统一封装到一个 service 方法里面避免散落。3.4 成交合同与数据统计实现合同模块是业务流程的终点。销售录入合同时后端要做三件事而且要放在一个事务里生成合同编号格式类似 C20240901001前缀 C 日期 三位流水号。更新合同表写入客户、房源、成交价、签约时间、付款方式。更新房源状态为已售更新客户状态为已成交。这三步只要有一个失败全部回滚。Java 里在 service 方法上加 Transactional 注解PHP 里用 DB::transaction 包裹Python 里用 context manager 或者 Django 的 transaction.atomic。为回答“为什么用事务”最典型的场景就是合同写入成功但房源状态更新失败结果系统里同一套房被卖了两回这在真实业务里是重大事故。收款记录是合同的一对多子表。首付、尾款分开登记每笔记录含项目金额、收款方式、到账时间。统计报表模块就可以从这个数据基础上去聚合。常用的统计查询大概有三种按月份统计成交套数和成交额GROUP BY MONTH(create_time)。按销售统计业绩GROUP BY seller_id关联用户表查出姓名。按房源状态统计去化率GROUP BY house_status然后算已售 / 总数。前端用 ECharts 做折线图和柱状图后端的统计接口把 ListMapString,Object 直接 return 出去前端取到之后渲染即可。如果你用 Python还可以直接把 pandas 的 DataFrame 转成 JSON 返回效果更直观。4. 前端页面与前后端联调的关键细节4.1 前端技术选型与页面清单前端这块我依然是推荐 Vue 3 Element Plus原因很现实组件成熟、文档全、页面好看。如果你实在不熟 Vue用 jQuery layui 也能做但整体质感和开发效率差别不小不建议。页面清单基本对应后端的七个模块从路由规划到页面组件我通常这样安排登录页账号密码表单记住密码优化项。布局页左侧菜单 顶栏 内容区普通后台管理布局。工作台看板统计展示今日预约数量、我的客户数、在售房源数。房源管理页列表、搜索条件、新增编辑弹窗、图片预览。客户管理页客户列表、客户详情抽屉、跟进记录时间线。预约看房页预约日历或列表支持状态操作。合同管理页合同列表、合同详情、收款记录表。统计报表页月份成交趋势折线图、销售业绩柱状图、房源状态饼图。页面数量大概在八到十个已经足够撑起一次完整的答辩演示。做的时候注意左侧菜单要长成动态的——根据用户角色显示或隐藏后端登录接口返回一个 roles 字段或者 permissions 数组。这比把菜单写死在前端要有说服力得多。4.2 接口请求封装与状态码约定前后端分离项目如果不约定好返回格式联调能折磨死人。我后端所有接口统一返回下面这个结构{ code: 200, msg: 操作成功, data: {} }code 为 200 表示成功400 表示参数错误401 表示未登录或 token 失效500 表示服务端异常。前端封装 axios 的时候用响应拦截器统一处理请求前把 token 挂到 Authorization 头响应时判断 code如果不是 200 就用 Element Plus 的 Message 组件弹出提示401 时清理本地 token 并跳转登录页。这个约定一旦定下来前端写页面就非常快。每个人开发的接口都长一个样子数据格式统一是 data 字段包裹不需要每个接口单独写判断逻辑。这里我加一条建议BigDecimal 类型的价格字段返回给前端时一定要转成字符串否则 JavaScript 的 Number 在传大数值时会丢失精度。Java 里可以在实体类的 price 字段上做序列化配置PHP 和 Python 天然字符串返回问题不大。4.3 联调避坑跨域、日期格式、文件上传前后端分离最常遇到的问题就是跨域。开发环境最简单的解决方案是 Vue 的 Vite 配置 proxy 代理把 /api 前缀的请求转发到后端地址比如 http://localhost:8080这样浏览器里看到的请求是同源的不涉及跨域。部署阶段让 Nginx 把前端和后端都通过同一个域名对外用路径区分即可也就没有跨域问题了。后端也要在配置里允许前端的来源地址这个我一般会写全局跨域配置类。日期格式是一个隐藏坑。MySQL 的 DATETIME 返回给 Java 时默认可能是 2024-09-01T10:00:00。如果你英文不是特别好还会踩时区坑MySQL 8 的驱动在链接 URL 里必须加 serverTimezoneAsia/Shanghai否则查询会报“The server time zone value”相关异常。前端拿到标准格式之后再用 dayjs 或 moment 格式化统一成 yyyy-MM-dd HH:mm:ss。文件上传的坑也很经典。开发时 easylocalhost 下图片能显示运行时一旦打成 jar 包部署写死的本地盘路径就可能失效。我自己养成的一个好习惯数据库永远存相对路径例如 /upload/20240901/xxx.jpg后端单独写一个静态资源映射把 upload 目录映射到根路径部署后让 Nginx 直接托管这个目录。这样无论在本地、服务器还是迁移环境只要配置一个目录路径就行。5. 常见问题与排查技巧实录5.1 打不开、启动失败类问题先说一个 Java 新手高频问题IDEA 2024 版本创建 Web 项目时初始化出来的工程没有 webapp 目录也没有部署配置。原因是新版本默认推荐 Spring Boot 内嵌容器不再生成传统 war 结构。解决办法是用 Spring Initializr 创建项目然后直接加 spring-boot-starter-web 依赖写一个 Controller 就能启动不需要手动配置 Tomcat。端口占用也很常见启动时报“Port 8080 was already in use”。Windows 下用 netstat -ano | findstr 8080 查 PID然后在任务管理器里结束进程。PHP 项目常见的问题是 PHP 版本和扩展不匹配比如 ThinkPHP 8 要求 PHP 8 以上而有些人本机还是 PHP 7页面直接白屏。建议用 PHPStudy 或小皮面板统一管理版本。数据库连接报错是另一个高频问题。除了刚才说的 serverTimezone还要注意 MySQL 8 和 MySQL 5.7 的驱动类不一样Spring Boot 2.7 里如果用 mysql:mysql-connector-java 8.x驱动类是 com.mysql.cj.jdbc.Driver。账号密码要对库要提前建好字符集 utf8mb4 不能少。调试时打开 MyBatis-Plus 的 SQL 日志能直接看到执行了什么 SQL排查效率翻倍。5.2 登录失效和数据不一致问题JWT 登录失效一般发生在两个场景token 过期或者后端修改了加密密钥导致之前发的 token 全部失效。排查时先看前端有没有把 token 成功放到请求头再看后端拦截器有没有正确解析 token。有一种情况是后端返回 401 后前端一直跳登录页这是处理逻辑写成了“任何 401 都清缓存跳转”但登录接口本身如果返回 401就会死循环要把登录接口排除拦截。数据不一致问题主要在预约和合同。比如用户提交看房预约时后端的冲突校验做得不好导致同一个房源在 10:00 同时被两组客户预约。解决思路是状态校验 事务 唯一约束三层防护。房源成交时加一条乐观锁更新UPDATE house SET house_status 3 WHERE id ? AND house_status 1如果影响行数为 0说明这个房源已经不是“在售”状态了后端直接返回“房源已被售出”并发问题就解决了。这条 SQL 比任何锁机制都直观面试时讲出来也是加分项。5.3 部署上线后的静态资源与数据库连接问题本地跑得好好的部署到服务器就 404多半是静态资源和文件上传路径的问题。我的做法是后端打包成 jar放到服务器后用 java -jar 启动Nginx 监听 80 端口前端三个路径规则/ 和 /api 前缀转发给后端地址。/upload 前缀指向服务器上实际的图片存放目录。这个 Nginx 配置片段我经常复用给一个基础版参考server { listen 80; server_name your-domain.com; location /api/ { proxy_pass http://127.0.0.1:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /upload/ { alias /data/house_system/upload/; } location / { root /data/house_system/dist; try_files $uri $uri/ /index.html; } }数据库连接方面服务器上的 MySQL 默认可能只允许 localhost 连接远程工具连不上要检查 bind-address 和账号授权。生产环境不要用 root 账号连应用数据库单独建一个业务账号只授权对应库的增删改查权限这样也更安全。6. 后续可扩展方向与个人经验补充6.1 低成本扩展地图找房、消息通知、H5 适配系统做完之后如果你还想增加亮点我建议优先做这三个方向。第一个是地图找房。给 house 表加两个字段 lng 和 lat接入高德或百度地图的 JavaScript API在页面上绘制房源标记点点击弹窗展示房源卡片。这个扩展视觉冲击力强答辩现场随手一演就很加分而且技术门槛不高。第二个是消息通知。预约看房成功之后给客户手机号发一条短信或者给销售的企业微信机器人发一条提醒。短信服务需要申请第三方企业微信机器人只要一个 webhook URL用后端 HTTP 调用即可成本几乎为零但“主动通知”这个设计点比被动等用户刷新页面要高级很多。第三个是 H5 适配。给移动端做一套轻量页面客户扫码进来就能看房源列表和预约记录。后端接口可以完全复用只是前端多一套自适应布局。如果你会一点小程序开发把 H5 套进小程序的 web-view 里也是常见路数。不过准备毕设的话H5 就够不建议这个时候去碰小程序编译环境和审核问题。6.2 开发这类项目最值得养成的习惯最后说我个人的体会。做房屋销售管理系统这类项目真正花时间的不是“页面好看”而是业务状态流转和数据一致性。预约、合同、房源状态、客户状态每一个角色操作都会引发一连串变化你只要有一次忘了把状态同步更新演示的时候就会露出破绽。我养成的几个习惯分享给你所有表都建 create_time、update_time、del_flag 三个基础字段后面做报表、恢复、迁移都靠它们。枚举值不散落统一放常量类或数据字典表代码里出现魔法字符串就顺手改掉。列表查询按时间倒序新录入的房源、客户、合同永远在最前面演示效果最合理。每次写接口先定返回结构然后写文档再写代码。文档即使简单到只有接口名和字段列表也比写完就忘强。如果你时间紧张优先把房源管理、客户管理、合同管理、预约看房这四个模块做完整统计报表能简单显示几条曲线就够。剩下这些扩展功能是你主流程稳定之后再考虑的部分。做项目不是把所有功能都堆上去就叫完整而是把核心链路走通、走到每一个分支状态都对这比什么都重要。