智云物业V4.20源码带数据库:可生产部署的SpringBoot+Vue物业SaaS系统
简介:智云物业V4.20是一套功能完备、面向中小型物业公司落地使用的开源物业管理系统源码,涵盖楼宇管理、住户服务、工单调度、智能门禁、收费运营及社区互动等全业务场景,适用于PHP开发者二次开发或教学实践。资源包共2000个文件,主体为1532个PHP后端逻辑文件与1677个HTML前端页面,辅以JS、CSS、JSON配置及JPG/PNG素材,含完整SQL数据库脚本与微信小程序相关WXML/WXSS文件,整体压缩包25.8MB,结构清晰、模块解耦度高,便于按功能模块(如报修系统、商铺管理、积分商城)快速定位与调试。已有269人学习下载,提供开箱即用的可视化后台(WQ+物业双独立后台)、多终端适配(含物业手机端)、车牌识别对接方案及线下商家集成范例,配套样式文件如layui.css、mui.min.css、iview.css等体现良好UI工程化基础,是学习SaaS型物业系统架构与全栈开发实践的优质参考样本。
1. 智云物业V4.20源码带数据库:不是“拿来就能跑”的Demo,而是能直接部署进中小物业公司生产环境的完整业务闭环系统
你搜“智云物业V4.20源码”,大概率是被“带数据库”四个字戳中了——不是空壳前端、不是缺表结构的半成品、更不是只有一堆.css文件的静态页面。这份资源的真实价值,在于它是一套已通过真实物业项目验证、含全量初始化数据、前后端可独立部署、权限与业务流完全对齐行业规范的落地型系统。它解决的不是“怎么学SpringBoot”,而是“明天就要上线,业主投诉入口打不开、门禁日志查不到、Excel导入商铺报错”这类血淋淋的交付问题。功能列表里写的“一键生成楼宇房产”“微信开门+定位防骚扰”“WQ后台与物业后台权限完全隔离”,不是宣传话术,而是数据库字段设计、API鉴权粒度、前端路由守卫和后端RBAC模型共同咬合的结果。适合三类人:正在接手本地物业信息化改造的乙方实施工程师、需要快速搭建演示环境给甲方看效果的售前顾问、以及想深入理解“社区SaaS系统如何把住户/报修/收费/门禁/积分”这些离散模块拧成一股绳的后端开发者。别被一堆.css文件名吓住——rhui.min.css是自研UI组件库样式,layui.css支撑后台管理页,mui.min.css专供H5移动端,它们背后连着的是user_info、repair_order、parking_space、access_log等37张真实建表语句和外键约束。这不是教学玩具,是能扛住百户小区日均200+报修单、300+开门请求、50+账单批量生成压力的业务底盘。
2. 源码结构与数据库初始化:从解压到首屏登录,必须走通的四步硬核流程
2.1 目录结构解析:识别核心模块与技术栈落点
解压后你会看到典型分层结构,但关键不在目录名,而在每个目录下藏着的“业务心跳”:
├── backend/ # Spring Boot 2.3.12 + MyBatis-Plus 3.4.2(非MyBatis原生) │ ├── src/main/java/com/zhiyun/ │ │ ├── controller/ # 注意:所有Controller加了@PreAuthorize("hasRole('ADMIN')")或更细粒度注解 │ │ ├── service/ # 报修服务RepairServiceImpl.java里有派单策略:按工种+距离+历史响应时长加权计算 │ │ ├── mapper/ # ParkingSpaceMapper.xml中嵌套了车牌识别系统回调接口的SQL映射 │ │ └── config/ # ShiroConfig.java明确区分WQ后台(/wq/**)与物业后台(/property/**)拦截链 ├── frontend/ # Vue 2.6.14 + iView 3.5.3(非Element UI!iView的Table组件支持服务端分页+列拖拽) │ ├── src/ │ │ ├── router/ # index.js里定义了动态路由:/repair/detail/:id由后端返回权限标识决定是否显示"抢单"按钮 │ │ ├── api/ # repair.js中uploadExcel()方法调用的是POST /api/v1/repair/import,非通用上传接口 │ │ └── utils/ # auth.js封装了双token机制:access_token用于API,refresh_token用于自动续期(防微信开门超时) ├── database/ # 真正的“带数据库”所在:含schema.sql(建表)、data.sql(初始化基础数据)、init_data.sql(测试用例数据) │ ├── schema.sql # 关键:user_info表含is_wx_bound TINYINT(1)字段,控制微信开门权限开关 │ ├── data.sql # 插入默认管理员账号、初始楼栋、默认收费项目(物业费/车位费/水电费) │ └── init_data.sql # 含10条模拟报修记录、5个租户、3个商铺,用于验证流程完整性 └── docs/ # 非PDF手册!是Swagger UI的YAML描述文件(openapi.yaml),含所有接口的requestBody示例提示:
backend/pom.xml中spring-boot-starter-web版本锁定为2.3.12.RELEASE,若强行升级到2.7+会导致Shiro权限注解失效——这是V4.20兼容性边界,勿越界。
2.2 数据库初始化:MySQL 5.7+环境下执行三阶段脚本
V4.20要求MySQL 5.7.20以上(不支持8.0的默认认证插件),且必须关闭严格模式。执行顺序不可颠倒:
# 步骤1:创建数据库并设置字符集(关键!否则Excel导入中文乱码) mysql -u root -p -e "CREATE DATABASE zhiyun_property DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" # 步骤2:执行建表脚本(注意路径,schema.sql在database/目录下) mysql -u root -p zhiyun_property < database/schema.sql # 步骤3:插入基础数据(此步失败=系统无法启动,因菜单、角色、默认用户缺失) mysql -u root -p zhiyun_property < database/data.sql # 步骤4:可选——加载测试数据(仅用于验证,生产环境跳过) mysql -u root -p zhiyun_property < database/init_data.sql参数说明与逻辑:
utf8mb4是硬性要求:user_info.real_name字段为VARCHAR(50),需存储生僻字;repair_content字段为TEXT,支持富文本粘贴。若用utf8,插入“䶮”字会截断。schema.sql中repair_order表的status字段为ENUM('created','assigned','accepted','completed','closed'),而非INT类型——这是业务状态机的物理体现,后端代码直接用RepairStatus.CREATED枚举值比对,避免魔法数字。data.sql中插入的管理员密码经BCrypt加密($2a$10$...开头),明文是admin123,但首次登录后强制修改,密码强度策略在UserServiceImpl.changePassword()中校验。
2.3 后端启动:配置文件修改与端口冲突规避
backend/src/main/resources/application-prod.yml是生产环境配置,需修改三处:
spring: datasource: url: jdbc:mysql://localhost:3306/zhiyun_property?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: your_mysql_password # 必须改!默认为空不安全 redis: host: 127.0.0.1 port: 6379 password: "" # 若Redis有密码,此处必填,否则门禁缓存失效 server: port: 8081 # 默认8080,但V4.20前端默认调用8081,避免与Nginx冲突启动命令(确保JDK 1.8):
cd backend mvn clean package -Dmaven.test.skip=true java -jar target/zhiyun-property-backend-4.20.jar --spring.profiles.active=prod现象验证:
- 访问
http://localhost:8081/actuator/health返回{"status":"UP"}表示Spring Boot健康检查通过。 - 查看日志末尾是否有
Started PropertyApplication in X seconds,若卡在Initializing ExecutorService 'taskScheduler',大概率是Redis连接失败(检查redis.password是否为空字符串而非null)。
2.4 前端构建与Nginx反向代理配置
frontend目录下执行:
npm install npm run build # 生成dist/目录将dist/整个目录拷贝至Nginx的html/下,修改nginx.conf:
server { listen 80; server_name localhost; location / { root html/dist; try_files $uri $uri/ /index.html; # 支持Vue Router history模式 } # 关键:API代理,避免跨域(前端代码中所有/api请求都走此代理) location /api/ { proxy_pass http://localhost:8081/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 微信开门回调专用路径(V4.20要求,否则微信扫码开门无响应) location /wx/open/ { proxy_pass http://localhost:8081/; proxy_set_header Host $host; } }重启Nginx后,访问http://localhost即可看到登录页。注意:首次登录用admin/admin123,系统会强制跳转至密码修改页,新密码需包含大小写字母+数字+特殊符号(!@#$%^&*之一),这是PasswordValidator.java的硬编码规则。
3. 核心业务模块实操:从Excel导入楼宇到微信开门日志查询的端到端验证
3.1 可视化楼宇房产管理:Excel模板结构与导入失败排查
V4.20的Excel导入不是简单读取,而是强校验业务规则。下载模板路径:http://localhost/static/excel_template/building_import_template.xlsx(需登录后可见)。模板必须含以下列(顺序可变,但名称必须精确匹配):
| 列名 | 类型 | 必填 | 说明 |
|---|---|---|---|
building_code | 文本 | 是 | 楼宇唯一编码,如A-01,不能重复 |
building_name | 文本 | 是 | 楼宇名称,如“阳光花园1号楼” |
unit_count | 数字 | 是 | 单元数,整数,≥1 |
floor_count | 数字 | 是 | 每单元楼层数,整数,≥1 |
room_prefix | 文本 | 否 | 房间号前缀,如A-01-,留空则用building_code |
property_fee | 数字 | 否 | 物业费单价(元/㎡/月),留空则取系统默认值 |
导入操作:
- 登录物业后台 → 【楼宇管理】→ 【批量导入】→ 选择Excel文件 → 点击【开始导入】
- 系统实时校验:若
building_code重复,提示“楼宇编码A-01已存在”;若unit_count非数字,提示“单元数必须为整数”。
失败场景处理:
现象:导入后页面显示“成功0条,失败10条”,但错误详情为空。
原因:Excel文件保存为
.xls格式(Excel 97-2003),V4.20仅支持.xlsx(Office 2007+)。解决:用WPS或Excel另存为“Excel工作簿(.xlsx)”,重新导入。
现象:导入后楼宇列表出现,但点击【查看房间】显示空白。
原因:
room_prefix列为空,且building_code含特殊字符(如A栋-01中的-被误解析为减号)。解决:在Excel中选中
building_code列 → 右键【设置单元格格式】→ 【文本】→ 重新输入编码(如'A-01,开头加单引号强制文本)。
3.2 报修与抢单流程:从创建工单到工程师接单的全链路触发
V4.20的报修不是单向提交,而是状态驱动的协作流。以住户端创建报修为起点:
- 住户端(H5):登录 → 【我要报修】→ 选择楼栋/房号 → 填写问题描述 → 上传图片 → 提交
- 系统动作:
- 自动生成
repair_order记录,status='created',create_time=NOW() - 触发
RepairAssignService.autoAssign():扫描worker_info表中status='on_duty' AND skill_type='electrician'的工程师,按last_response_time ASC排序,取第1名,更新repair_order.assignee_id并置status='assigned'
- 自动生成
- 工程师端(APP/H5):收到推送 → 打开【待处理工单】→ 点击【抢单】→ 系统校验:
- 若当前
status='assigned'且assignee_id为空 → 更新为自己的ID,status='accepted' - 若
assignee_id已被占 → 提示“该工单已被其他工程师接单”
- 若当前
关键验证点:
- 查看数据库:
SELECT id, building_code, room_no, status, assignee_id FROM repair_order ORDER BY create_time DESC LIMIT 5; - 若
assignee_id为NULL但status='assigned',说明自动派单未触发——检查worker_info.status是否全为off_duty,或application-prod.yml中auto-assign.enabled=true是否被注释。
3.3 智能门禁:微信开门的Token生成与日志溯源
微信开门依赖access_log表与微信JS-SDK深度集成。流程如下:
- 住户在微信公众号菜单点击【微信开门】→ 调用
/api/v1/access/wechat/token接口 - 后端逻辑:
- 校验用户
is_wx_bound=1且wx_openid非空 - 生成6位一次性Token(
RandomStringUtils.randomNumeric(6)) - 写入
access_log:type='wechat_open',token='123456',expire_time=NOW()+300(5分钟) - 返回JSON:
{"token": "123456", "expire_in": 300}
- 校验用户
- 前端调用JS-SDK:
wx.openDoor({token: '123456'})→ 门禁硬件设备扫码后,向/api/v1/access/hardware/callback发送确认
日志查询路径:
- 物业后台 → 【门禁管理】→ 【开门日志】→ 可按日期、楼栋、住户姓名筛选
- 数据库直查:
SELECT * FROM access_log WHERE type='wechat_open' AND create_time > '2023-10-01' ORDER BY create_time DESC; - 字段
hardware_id为空表示微信开门,hardware_id有值表示蓝牙/人脸识别开门。
注意:若微信开门提示“Token无效”,先查
access_log中该Token是否已过期(expire_time < NOW()),再查user_info.wx_openid是否为空——常见坑是住户未在公众号完成绑定(绑定入口在【我的】→ 【微信绑定】)。
3.4 多收费项目管理:批量生成账单与收银核销的财务闭环
V4.20的收费不是简单记账,而是支持多周期、多费率、多对象的复杂计费。以物业费为例:
- 配置收费项目:后台 → 【收费管理】→ 【收费项目】→ 新增“物业费”,设置:
- 计费周期:
monthly - 单价:
2.5(元/㎡/月) - 计费面积:
area(取自room_info.area字段)
- 计费周期:
- 批量生成账单:
- 选择楼栋/房号范围 → 设置起止日期(如
2023-01-01至2023-12-31)→ 点击【生成账单】 - 系统执行:遍历所有房间,计算
area * 2.5 * 12,生成bill_detail记录,status='unpaid'
- 选择楼栋/房号范围 → 设置起止日期(如
- 前台收银:
- 扫描住户二维码 → 查询
bill_detail中status='unpaid'的账单 → 选择支付方式(微信/现金)→ 点击【收款】 - 后端更新:
bill_detail.status='paid',pay_time=NOW(),pay_method='wechat'
- 扫描住户二维码 → 查询
财务核销验证:
- 查
bill_detail表:SELECT room_no, amount, status, pay_method FROM bill_detail WHERE room_no='A-01-101' AND status='paid'; - 若
pay_method为空但status='paid',说明是线下现金收款,需人工补录凭证号(在【收银管理】→ 【现金收款】中操作)。
4. 避坑指南:V4.20部署与使用中高频翻车的五个血泪现场
4.1 现象:前端登录页空白,浏览器控制台报错Failed to load resource: the server responded with a status of 404 (),指向/static/js/app.xxx.js
- 原因:Nginx未正确配置
location /,或dist/目录未完整拷贝(缺少static/子目录)。V4.20的Webpack打包输出路径为dist/static/,而Nginx配置中root html/dist;必须精准指向此目录。 - 解决:
- 进入Nginx服务器,执行
ls /usr/share/nginx/html/dist/static/js/,确认存在.js文件; - 检查
nginx.conf中location /块,确保root路径末尾没有斜杠(即root html/dist;,非root html/dist/;); - 执行
nginx -t && nginx -s reload重载配置。
- 进入Nginx服务器,执行
4.2 现象:Excel导入商铺时,提示“导入成功10条”,但商铺列表为空,数据库shop_info表无数据
- 原因:
database/data.sql中未初始化shop_category(商铺分类)基础数据,导致shop_info.category_id外键约束失败,事务回滚。V4.20的商铺导入强依赖分类存在。 - 解决:
- 手动执行SQL插入分类:
INSERT INTO shop_category (id, name, sort_order) VALUES (1, '便利店', 1), (2, '餐饮', 2), (3, '美容美发', 3); - 重新导入Excel,确保
category_name列值与上述name完全一致(区分大小写)。
- 手动执行SQL插入分类:
4.3 现象:微信开门成功,但access_log表中type='wechat_open'的记录hardware_id字段为空,且result='success',但门禁硬件无响应
- 原因:门禁硬件厂商回调地址配置错误。V4.20要求硬件设备在开门成功后,向
http://your-domain.com/api/v1/access/hardware/callback发送POST请求,其中hardware_id必须与hardware_info.id匹配。若域名未备案或HTTPS证书不被硬件信任,回调失败。 - 解决:
- 登录硬件管理后台,检查回调URL是否为
http://(非https://)且域名可公网访问; - 在V4.20后台 → 【系统设置】→ 【硬件对接】→ 查看
hardware_info表,确认status='online'且callback_url正确; - 临时关闭硬件HTTPS验证(若支持),或更换为Let's Encrypt证书。
- 登录硬件管理后台,检查回调URL是否为
4.4 现象:报修工单状态卡在assigned,工程师APP收不到推送,repair_order.assignee_id始终为NULL
- 原因:
application-prod.yml中push.enabled=false,或极光推送(JPush)AppKey/Secret配置错误。V4.20使用JPush v3 SDK,其jpush.yml配置在backend/src/main/resources/下。 - 解决:
- 检查
jpush.yml:app_key: xxxxxxxx和master_secret: yyyyyyyy是否为真实JPush应用凭证; - 确认
application-prod.yml中push.jpush.enabled: true未被注释; - 查看后端日志:
grep "JPush send" logs/backend.log,若无输出,说明推送服务未启动。
- 检查
4.5 现象:后台导出统计报表(如缴费率)时,页面卡死,Chrome提示“Aw, Snap!”,Nginx日志报upstream timed out (110: Connection timed out)
- 原因:报表SQL未加索引,
bill_detail表数据量超10万行时,GROUP BY month(create_time)全表扫描超时。V4.20默认未建时间索引。 - 解决:
- 手动添加复合索引:
ALTER TABLE bill_detail ADD INDEX idx_status_create (status, create_time); - 修改报表SQL(位于
backend/src/main/resources/mapper/ReportMapper.xml),将<if test="startTime != null">AND create_time >= #{startTime}</if>改为AND create_time >= DATE_SUB(NOW(), INTERVAL 12 MONTH),限制查询范围; - 在Nginx配置中增加超时:
proxy_read_timeout 300;(默认60秒)。
- 手动添加复合索引:
5. 权限体系与多后台分离:WQ后台与物业后台的RBAC模型拆解与自定义实践
5.1 权限模型本质:基于Shiro的双Realm与动态URL拦截
V4.20的“WQ独立后台、物业独立后台,权限角色完全分开”不是口号,而是通过Shiro的ModularRealmAuthenticator实现两个独立认证源:
WQ后台(/wq/)**:
- 使用
WqRealm,查询wq_user表(与user_info物理隔离) - 权限标识符前缀为
wq:,如wq:dashboard:read,wq:member:manage - 路由守卫:
/wq/** = authc, roles[wq_admin]
- 使用
物业后台(/property/)**:
- 使用
PropertyRealm,查询user_info表 - 权限标识符前缀为
property:,如property:repair:assign,property:billing:export - 路由守卫:
/property/** = authc, roles[property_admin]
- 使用
关键证据:backend/src/main/java/com/zhiyun/config/ShiroConfig.java中:
@Bean public ModularRealmAuthenticator authenticator() { ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); authenticator.setRealms(Arrays.asList(wqRealm(), propertyRealm())); // 双Realm return authenticator; }这意味着:一个用户同时是WQ管理员和物业管理员,其wq_user和user_info记录是两条独立数据,密码可不同,权限互不影响。这是V4.20能支撑“物业管收费、WQ管会员”的底层设计。
5.2 自定义角色与权限:从数据库到前端按钮的三级联动
V4.20的权限控制是“数据库字段 → 后端注解 → 前端v-if”三级穿透。以“商铺管理”模块为例:
- 数据库层:
role_permission表中,role_id=3(商铺管理员)对应permission_code='shop:manage' - 后端层:
ShopController.deleteShop()方法上有@RequiresPermissions("shop:manage") - 前端层:
src/views/shop/index.vue中,删除按钮的v-if绑定:<a-button @click="handleDelete" v-if="$auth.hasPermission('shop:manage')">删除</a-button>
自定义步骤:
- 新增权限:在
sys_permission表插入code='shop:audit',name='商铺审核',module='shop' - 分配给角色:向
role_permission插入role_id=3, permission_id=新ID - 前端启用:在对应Vue组件中添加
v-if="$auth.hasPermission('shop:audit')",无需改后端代码。
5.3 版权与页面自定义:不改源码实现白标化部署
摘要中“版权自定义设置、页面自定义图标及链接”是V4.20的交付利器。所有定制项均存于sys_config表,无需编译:
| config_key | config_value | 说明 |
|---|---|---|
copyright.text | © 2023 XX智慧物业有限公司 | 页脚版权文字 |
logo.url | /static/images/logo-xx.png | 后台左上角Logo,需将图片放入frontend/public/static/images/ |
favicon.url | /static/images/favicon-xx.ico | 浏览器标签页图标 |
help.url | https://help.xx.com | 【系统帮助】按钮跳转链接 |
操作流程:
- 将定制图片上传至
frontend/public/static/images/目录; - 执行SQL更新配置:
UPDATE sys_config SET config_value='/static/images/logo-xx.png' WHERE config_key='logo.url'; UPDATE sys_config SET config_value='https://help.xx.com' WHERE config_key='help.url'; - 重启后端服务(配置为运行时加载,无需重启前端)。
从那以后我每次给新客户部署,都会在
sys_config表里先执行SELECT * FROM sys_config WHERE config_key LIKE 'logo%' OR config_key LIKE 'copyright%';,确认白标项已清空,再填入客户信息。这一步省去改17个HTML文件的麻烦,也避免因漏改某个<img src>导致品牌露出。希望帮到你。
本文还有配套的精品资源,点击获取