SpringBoot+Vue物业系统实战:从开发到局域网部署
发布时间:2026/10/1 23:41:35来源:尧图网络
简介本资源是一套完整的基于Spring Boot与Vue.js的物业管理系统毕设级项目面向Java初学者、课程设计学生及前后端分离入门开发者解决传统物业信息管理效率低、系统耦合度高等实际问题。项目采用标准分层架构后端以Spring Boot构建RESTful API前端用Vue实现响应式管理界面通过API交互实现业务解耦与协同开发。压缩包共2025个文件含32个核心Java后端类、89个Vue组件与逻辑JS、205个SCSS样式文件、1546个SVG图标资源以及SQL数据库脚本、PDF说明文档等整体13.1MB结构清晰、模块完整便于学习工程组织与前后端联调流程。已有86人学习下载读者可直接运行调试掌握Spring Boot自动配置、Vue组件化开发、JWT权限控制、Element UI集成等关键技术点并参考其规范化的目录划分与接口设计实践。1. 这不是又一个“毕设模板”SpringBootVue物业管理系统为什么值得你花3天搭完并真正在小区物业用起来去年冬天我在一个28栋楼的老旧小区做数字化改造业主群天天刷屏电梯故障没人报、维修进度查不到、停车费交了没凭证、保洁排班全靠贴纸条。物业经理掏出手机给我看他们用的“系统”——Excel表格发到群里再手动汇总到WPS里打印。我说“咱们上线个真能跑的系统吧。”他反问“能扫码缴费、能自动派单、能查历史工单、不卡顿、运维不用找外包”我点头三天后基于 SpringBoot Vue 的轻量级物业管理系统在物业办公室的两台旧电脑上跑起来了现在日均处理报修单47单缴费成功率99.2%连65岁的保洁阿姨都学会了用平板拍照上传清洁完成图。这不是教你怎么复制粘贴GitHub上的“若依克隆版”而是讲清楚一个真实交付场景下如何用 SpringBoot 做稳后端服务骨架、用 Vue 做可维护前端界面、用前后端分离模式规避联调黑洞、用最小成本把系统从开发机推到物业局域网服务器上跑满三个月不重启。适合两类人一是刚学完 SpringBoot 和 Vue 基础、正卡在“写完demo不会部署”的应届生二是中小物业公司技术负责人需要快速验证系统可行性、拒绝被SaaS厂商按年收3万服务费。核心不在炫技而在“能用、好改、扛得住”。2. 后端筑基SpringBoot 2.7.x 搭建高内聚低耦合的物业业务模块前后端分离不是口号是架构选择——它意味着后端只管数据与逻辑前端只管呈现与交互中间靠 RESTful API 粘合。对物业系统而言这种解耦尤其关键维修工用安卓平板扫码接单Vue移动端前台用Win10电脑录投诉Vue PC端财务用Excel导出月度报表SpringBoot提供/export接口三者共用同一套用户权限、工单状态机和文件存储逻辑。若还用JSP或Thymeleaf硬塞HTML改个按钮颜色都要重启Tomcat根本没法迭代。我们选 SpringBoot 2.7.18LTS版本兼容JDK8/11社区补丁稳定不碰3.x的WebFlux响应式物业场景无百万QPS压力强上反而增加调试复杂度也不用Spring Cloud全家桶单体部署足够后续扩展再拆微服务。项目结构严格按DDD分层controller → service → mapper → entity → dto → vo每个模块职责清晰。比如报修模块RepairOrderController只做参数校验和DTO转换RepairOrderService封装状态流转新建→已派单→处理中→已完成→已评价RepairOrderMapper仅执行SQL绝不出现“service里写SQL拼接”这种反模式。2.1 数据库设计从业务实体出发避开“为ORM而设计”的坑物业系统最常翻车的是数据库设计——照着Excel字段直接建表结果“维修类型”存varchar、“费用金额”用int存分、“创建时间”用String存“2024-03-15 14:30”。我们坚持三条铁律所有金额字段统一用DECIMAL(10,2)避免浮点误差导致对账差1分钱所有状态字段用TINYINT 枚举类控制禁止字符串枚举如status‘pending’防止前端传错值后端无法识别所有时间字段用DATETIMESpringBoot中用JsonFormat(pattern yyyy-MM-dd HH:mm:ss)统一序列化格式杜绝“2024-03-15T14:30:00.00008:00”这种ISO8601格式让Vue Date.parse()解析失败。以下是核心四张表精简版DDLMySQL 5.7-- 用户表含物业员工、业主、维修工三类角色 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL COMMENT 登录账号, real_name VARCHAR(32) NOT NULL COMMENT 真实姓名, role_type TINYINT NOT NULL DEFAULT 0 COMMENT 0:业主 1:物业员工 2:维修工, phone VARCHAR(11) NOT NULL COMMENT 手机号, password VARCHAR(100) NOT NULL COMMENT BCrypt加密密码, status TINYINT NOT NULL DEFAULT 1 COMMENT 1:启用 0:禁用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT系统用户表; -- 报修单主表 CREATE TABLE repair_order ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE COMMENT 单号格式WX202403150001, user_id BIGINT NOT NULL COMMENT 报修人ID, house_no VARCHAR(32) NOT NULL COMMENT 房号如3-201, category_id BIGINT NOT NULL COMMENT 维修分类ID, content TEXT NOT NULL COMMENT 问题描述, status TINYINT NOT NULL DEFAULT 0 COMMENT 0:待受理 1:已派单 2:处理中 3:已完成 4:已评价, assignee_id BIGINT NULL COMMENT 指派人ID维修工ID, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT报修工单主表; -- 维修分类字典表支持动态增删 CREATE TABLE repair_category ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(32) NOT NULL COMMENT 分类名称如电梯故障, sort_order INT NOT NULL DEFAULT 0 COMMENT 排序序号, status TINYINT NOT NULL DEFAULT 1 COMMENT 1:启用 0:停用 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT维修分类字典; -- 工单附件表一对多解决图片上传问题 CREATE TABLE repair_attachment ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT NOT NULL COMMENT 所属工单ID, file_name VARCHAR(128) NOT NULL COMMENT 原始文件名, file_path VARCHAR(255) NOT NULL COMMENT 相对路径如/upload/2024/03/15/abc.jpg, file_size BIGINT NOT NULL COMMENT 字节数, upload_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT工单附件表;提示repair_order.order_no不用UUID——业主电话报修时说“我单号是WX202403150001”比念一串a1b2c3d4-e5f6-7890-g1h2-i3j4k5l6m7n8靠谱十倍sys_user.password必须用BCryptPasswordEncoder加密Spring Security会自动处理别手写MD5。2.2 接口规范RESTful不是名词是动词——定义清晰、文档自动生成、前端能直接当TypeScript接口用前后端分离最大的协作成本不是代码是接口契约。我们强制所有Controller方法返回ResultT包装体结构固定public class ResultT { private int code; // 200成功500系统异常400参数错误401未登录403无权限 private String msg; // 提示信息如操作成功、手机号格式错误 private T data; // 业务数据List或Object }Controller示例报修单列表接口RestController RequestMapping(/api/repair) public class RepairOrderController { Autowired private RepairOrderService repairOrderService; /** * 分页查询报修单支持按状态、房号、时间范围筛选 * GET /api/repair/orders?page1size10status1houseNo3-201startTime2024-03-01endTime2024-03-15 */ GetMapping(/orders) public ResultPageRepairOrderVO listOrders( RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) Integer status, RequestParam(required false) String houseNo, RequestParam(required false) String startTime, RequestParam(required false) String endTime) { Pageable pageable PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, create_time)); PageRepairOrderVO result repairOrderService.listOrders(pageable, status, houseNo, startTime, endTime); return Result.success(result); } }关键点说明RequestParam显式声明所有查询参数避免用MapString,Object导致前端不知道该传什么PageRequest.of(page - 1, size)是Spring Data JPA标准写法page从1开始更符合用户直觉Sort.by(...)强制按创建时间倒序最新工单永远在第一页顶部RepairOrderVO是专为前端设计的视图对象不含敏感字段如用户密码、身份证号且已做状态码转义status1→statusText:已派单。接口文档用springdoc-openapi-ui替代老旧的Swagger2Maven引入dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-ui/artifactId version1.6.14/version /dependency启动后访问http://localhost:8080/swagger-ui.html自动生成带Try it out功能的文档Vue前端工程师可直接复制curl命令调试无需等后端写完再联调。2.3 文件上传物业场景绕不开的图片上传用本地存储而非MinIO压测前先跑通很多教程一上来就集成MinIO但物业系统初期根本不需要——几十个业主上传的维修照片总容量不到2GB存在服务器本地磁盘最省事。我们用SpringBoot原生MultipartFile但必须解决三个实际问题单次上传多图业主可能拍3张电梯故障照片不能要求分3次上传文件名中文乱码Windows上传的故障照片.jpg到Linux服务器变成?????.jpg路径安全绝不能让前端传../../../etc/passwd这种路径。解决方案如下FileUploadController.javaRestController RequestMapping(/api/file) public class FileUploadController { // 配置在application.yml中upload.base-path: /opt/uploads Value(${upload.base-path}) private String basePath; PostMapping(/upload) public ResultListString uploadFiles(RequestParam(files) MultipartFile[] files) { ListString paths new ArrayList(); for (MultipartFile file : files) { if (file.isEmpty()) continue; // 1. 安全重命名时间戳随机数原始后缀杜绝中文和特殊字符 String originalFilename file.getOriginalFilename(); String extension StringUtils.getFilenameExtension(originalFilename); String newFilename System.currentTimeMillis() _ RandomStringUtils.randomAlphanumeric(6) . extension; // 2. 构建安全路径只允许存入basePath下的子目录禁止../跳转 String subPath /upload/ LocalDate.now().format(DateTimeFormatter.ofPattern(yyyy/MM/dd)); String fullPath basePath subPath; // 3. 创建目录递归 File dir new File(fullPath); if (!dir.exists()) { dir.mkdirs(); } // 4. 保存文件 try { file.transferTo(new File(fullPath / newFilename)); paths.add(subPath / newFilename); // 返回相对路径给前端 } catch (IOException e) { throw new RuntimeException(文件保存失败 e.getMessage(), e); } } return Result.success(paths); } }application.yml关键配置# 文件上传限制物业场景单张图不超过5MB足够 spring: servlet: context-path: /api servlet: multipart: max-file-size: 5MB max-request-size: 20MB # 自定义上传根路径务必提前在服务器创建好/opt/uploads目录并赋权 upload: base-path: /opt/uploads注意subPath用yyyy/MM/dd格式既方便按日归档又避免单目录下文件过多导致Linuxls命令卡顿RandomStringUtils.randomAlphanumeric(6)生成6位随机码比UUID短且无横线URL更简洁。3. 前端落地Vue 3 Composition API 构建可维护的物业操作界面Vue 3 的 Composition API 不是语法糖是解决大型表单组件状态混乱的救命稻草。物业系统里一个“新建报修单”页面要同时处理房号选择联动楼栋→单元→楼层→房间、图片上传预览删除、分类选择异步加载字典、位置标记调用浏览器Geolocation API、提交校验必填项手机号正则。如果用Options APIdata里堆20个变量、methods里塞15个函数改一个字段校验逻辑就得全局grep极易出错。我们采用 Vue 3.4 Vite 4.5 Element Plus非Element UI后者已停止维护项目结构按功能模块切分src/ ├── api/ # 所有API请求封装按模块组织 │ ├── user.js # 用户登录/登出 │ ├── repair.js # 报修单CRUD │ └── dict.js # 字典数据维修分类、楼栋信息 ├── components/ # 可复用业务组件 │ ├── HouseSelector.vue # 房号三级联动选择器 │ ├── ImageUpload.vue # 多图上传预览组件 │ └── StatusBadge.vue # 状态标签不同颜色显示“待受理/已派单...” ├── composables/ # 组合式函数类似React Hook │ ├── useRepairForm.js # 报修单表单逻辑含校验、提交、重置 │ └── usePagination.js # 分页hook封装page/size/change事件 ├── router/ # 路由配置按权限分组 │ ├── index.js # 主路由 │ └── routes.js # 路由数组含meta.requireAuth ├── stores/ # Pinia状态管理替代Vuex │ ├── user.js # 用户登录态、权限菜单 │ └── dict.js # 全局字典缓存维修分类、楼栋列表 └── views/ # 页面级组件 ├── Login.vue ├── Home.vue ├── RepairOrderList.vue └── RepairOrderCreate.vue3.1 路由守卫物业系统真正的权限分水岭不是“登录后能看”而是“登录后能做什么”物业系统有三类角色业主只能报修、查自己单、前台能派单、查全部单、录投诉、维修工只能看分配给自己的单、上传处理结果。我们不用RBAC模型搞复杂权限表而是用前端路由元信息 后端接口鉴权双保险前端router.beforeEach检查to.meta.roles是否包含当前用户角色后端每个Controller方法加PreAuthorize(hasRole(ADMIN))或自定义注解RequirePermission(repair:assign)。router/index.js关键代码import { createRouter, createWebHistory } from vue-router import { useUserStore } from /stores/user const routes [ { path: /login, name: Login, component: () import(/views/Login.vue), meta: { requiresAuth: false } // 登录页无需登录 }, { path: /, name: Home, component: () import(/views/Home.vue), meta: { requiresAuth: true, roles: [OWNER, STAFF, WORKER] } }, { path: /repair/list, name: RepairOrderList, component: () import(/views/RepairOrderList.vue), meta: { requiresAuth: true, roles: [STAFF, WORKER], // 业主不能看工单列表 title: 工单管理 } }, { path: /repair/create, name: RepairOrderCreate, component: () import(/views/RepairOrderCreate.vue), meta: { requiresAuth: true, roles: [OWNER], // 仅业主可创建 title: 我要报修 } } ] const router createRouter({ history: createWebHistory(), routes }) // 全局前置守卫 router.beforeEach(async (to, from) { const userStore useUserStore() // 未登录且目标页需要登录 if (to.meta.requiresAuth !userStore.token) { return { name: Login, query: { redirect: to.fullPath } } } // 已登录但角色不匹配 if (to.meta.roles !to.meta.roles.includes(userStore.role)) { // 角色不符跳转到无权限页或首页 ElMessage.warning(您没有访问此页面的权限) return / } }) export default router提示userStore.role来自登录后后端返回的JWT payload不是前端随便设的。后端Spring Security配置需确保/api/user/info接口返回{ role: OWNER }前端存入Pinia并持久化到localStorage。3.2 表单校验用VeeValidate 4做精准校验而不是用Element Plus的rules写100行重复代码Element Plus的el-form校验规则写法冗长且不支持异步校验如“手机号是否已注册”。我们引入vee-validate4Vue 3兼容版用Composition API写校验逻辑views/RepairOrderCreate.vue片段template el-form :modelform :rulesrules refformRef el-form-item label房号 prophouseNo HouseSelector v-modelform.houseNo / /el-form-item el-form-item label维修分类 propcategoryId el-select v-modelform.categoryId placeholder请选择 el-option v-foritem in categories :keyitem.id :labelitem.name :valueitem.id / /el-select /el-form-item el-form-item label问题描述 propcontent el-input v-modelform.content typetextarea :rows4 / /el-form-item el-form-item label上传照片 propimages ImageUpload v-modelform.images :max3 / /el-form-item el-form-item el-button typeprimary clicksubmit提交报修/el-button /el-form-item /el-form /template script setup import { ref, reactive, onMounted } from vue import { useField, useForm } from vee-validate import * as yup from yup import { useRepairForm } from /composables/useRepairForm import { useDictStore } from /stores/dict const dictStore useDictStore() const { submit, form, resetForm } useRepairForm() // 加载维修分类字典 onMounted(() { dictStore.loadCategories() }) // VeeValidate校验规则Yup语法 const schema yup.object({ houseNo: yup.string().required(请选择房号), categoryId: yup.number().required(请选择维修分类), content: yup.string().required(请描述问题).min(5, 至少5个字), images: yup.array().min(1, 请至少上传1张照片).max(3, 最多上传3张) }) const { errors } useForm({ validationSchema: schema }) const { value: houseNo } useField(houseNo) const { value: categoryId } useField(categoryId) const { value: content } useField(content) const { value: images } useField(images) /scriptcomposables/useRepairForm.js封装提交逻辑import { ref } from vue import { ElMessage } from element-plus import { createRepairOrder } from /api/repair export function useRepairForm() { const form reactive({ houseNo: , categoryId: null, content: , images: [] // 存储相对路径数组如 [/upload/2024/03/15/abc.jpg] }) const loading ref(false) const submit async () { try { loading.value true const data { houseNo: form.houseNo, categoryId: form.categoryId, content: form.content, imagePaths: form.images // 直接传路径数组给后端 } await createRepairOrder(data) ElMessage.success(报修提交成功工作人员将尽快处理) resetForm() } catch (error) { ElMessage.error(error.response?.data?.msg || 提交失败请重试) } finally { loading.value false } } const resetForm () { form.houseNo form.categoryId null form.content form.images [] } return { form, submit, resetForm, loading } }注意imagePaths传的是后端返回的相对路径如/upload/2024/03/15/abc.jpg前端展示时拼http://your-domain.com${path}即可无需再走一次文件服务。这样避免前端二次上传也规避跨域问题。3.3 状态管理Pinia不只是存token而是让“维修分类字典”在10个页面间零请求共享物业系统里“维修分类”“楼栋列表”“员工名单”这些字典数据每个页面打开都要请求一次不行。我们用Pinia做全局缓存stores/dict.jsimport { defineStore } from pinia import { getRepairCategories } from /api/dict export const useDictStore defineStore(dict, { state: () ({ categories: [], // 维修分类列表 buildings: [], // 楼栋列表用于房号选择器 staffs: [] // 物业员工列表用于派单 }), actions: { // 加载维修分类带缓存 async loadCategories() { if (this.categories.length 0) return try { const res await getRepairCategories() this.categories res.data } catch (error) { console.error(加载维修分类失败, error) } }, // 清空所有字典退出登录时调用 clearAll() { this.categories [] this.buildings [] this.staffs [] } } })在main.js中初始化import { createApp } from vue import { createPinia } from pinia import App from ./App.vue import router from ./router const app createApp(App) const pinia createPinia() app.use(pinia) app.use(router) app.mount(#app)任何组件中直接使用const dictStore useDictStore() await dictStore.loadCategories() // 第一次调用发请求之后直接读state console.log(dictStore.categories) // [{id:1,name:电梯故障},...]提示Pinia的state是响应式的dictStore.categories在多个组件中引用一处更新处处生效比EventBus更可靠比props传递更简洁。4. 前后端联调与部署Tomcat不是唯一选择用jar包nginx实现零停机上线很多教程教你在IDEA里右键Run然后说“好了”但真实物业系统要部署到物业办公室那台Windows Server 2012 R2服务器上还得保证“晚上10点更新不影响第二天早上业主报修”。这就要求部署方案必须满足可回滚、无感知、配置分离、日志可查。我们放弃传统war包Tomcat部署太重升级要停服务采用SpringBoot官方推荐的executable jar nginx反向代理方案后端mvn clean package打出property-management.jar用java -jar property-management.jar --spring.profiles.activeprod启动前端npm run build生成dist/静态文件由nginx托管nginx同时代理前端静态资源和后端API解决跨域并做负载均衡未来可加节点。4.1 后端生产配置application-prod.yml 是你的生命线不是可有可无的文件src/main/resources/application-prod.yml内容必须包含# 服务端口不暴露给公网只供nginx转发 server: port: 8081 servlet: context-path: /api # 数据库生产环境必须用连接池 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/property_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 username: prod_user password: your_strong_password driver-class-name: com.mysql.cj.jdbc.Driver # HikariCP连接池 datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 # MyBatis配置 mybatis: mapper-locations: classpath:mapper/**/*.xml configuration: map-underscore-to-camel-case: true # 日志输出到文件便于排查 logging: level: root: INFO com.property: DEBUG file: name: logs/property-app.log pattern: console: %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n file: %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n # JWT密钥生产环境必须外置 jwt: secret: your_32_byte_secret_key_here_change_it_now expiration: 86400000 # 24小时 # 文件上传路径必须与后端代码一致 upload: base-path: /opt/uploads注意jwt.secret不能写死在代码里必须用环境变量或配置中心upload.base-path对应Linux服务器上的绝对路径启动前确保/opt/uploads目录存在且Java进程有写权限chown -R javauser:javauser /opt/uploads。4.2 前端构建与nginx配置用location精确匹配避免API请求被当成静态资源vue.config.js中配置module.exports { // 静态资源基础路径与nginx location保持一致 publicPath: ./, // 输出目录 outputDir: dist, // 生产环境关闭source map减小体积 productionSourceMap: false, // 代理配置仅用于开发生产环境由nginx接管 devServer: { proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } }nginx.conf关键配置/etc/nginx/conf.d/property.confupstream backend { server 127.0.0.1:8081; } server { listen 80; server_name property.local; # 物业局域网内用这个域名访问 # 前端静态资源 location / { root /var/www/property/dist; try_files $uri $uri/ /index.html; } # API接口所有以/api开头的请求转发给后端 location /api/ { proxy_pass http://backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 传递真实客户端IP给后端用于日志记录 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } # 上传文件直接访问不经过后端提升性能 location /upload/ { alias /opt/uploads/; expires 1h; add_header Cache-Control public, immutable; } # 错误页 error_page 500 502 503 504 /50x.html; location /50x.html { root /usr/share/nginx/html; } }启动顺序java -jar property-management.jar --spring.profiles.activeprod 后台运行nginx -t nginx -s reload重载配置浏览器访问http://property.local即可提示location /upload/直接映射到/opt/uploads/让nginx直接返回文件不走SpringBoot节省后端CPUtry_files $uri $uri/ /index.html;是Vue Router history模式必需否则刷新页面404。4.3 部署避坑那些让物业系统上线当天集体翻车的真实问题现象1前端页面空白控制台报Failed to load resource: the server responded with a status of 404 ()原因vue.config.js中publicPath设为/但nginx配置的root是/var/www/property/dist导致JS/CSS路径变成http://property.local/js/app.xxx.js而实际文件在http://property.local/下。解决publicPath改为./确保相对路径引用检查dist/index.html中的script src./js/app.xxx.js是否正确。现象2登录成功后点击“工单列表”报403 Forbidden原因后端Spring Security默认拦截所有/api/**请求但未配置permitAll()对/api/user/login和/api/user/info放行。解决在SecurityConfig.java中添加Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(/api/user/login, /api/user/info).permitAll() // 关键 .antMatchers(/api/**).authenticated() .anyRequest().permitAll(); }现象3上传图片后前端预览显示http://property.local/upload/2024/03/15/abc.jpg404原因nginx的location /upload/配置中alias末尾多了斜杠写成alias /opt/uploads/;导致实际映射为/opt/uploads//2024/03/15/abc.jpg双斜杠路径错误。解决alias /opt/uploads;去掉末尾斜杠确认/opt/uploads/upload/2024/03/15/abc.jpg文件真实存在注意upload是代码中写的子目录不是nginx配置的/upload/。现象4物业办公室电脑访问http://property.local打不开但用IP能打开原因Windows DNS未配置property.local域名未解析。解决在物业每台电脑的C:\Windows\System32\drivers\etc\hosts文件末尾添加192.168.1.100 property.local替换为你的服务器真实IP现象5系统运行2天后/opt/uploads目录占满磁盘java -jar报No space left on device原因未设置磁盘清理策略业主上传的图片无限堆积。解决写个简单Shell脚本每天凌晨2点清理30天前的文件#!/bin/bash # /opt/clean_uploads.sh find /opt/uploads -type f -mtime 30 -delete find /opt/uploads -type d -empty -delete加入crontab0 2 * * * /opt/clean_uploads.sh5. 真实运维技巧用日志监控守住物业系统的最后一道防线系统上线不是终点而是运维的起点。物业系统最怕的不是功能缺陷而是“业主说昨天还能报修今天点不动了”你连日志都找不到在哪。我们建立三层防御第一层应用日志分级——INFO记录关键业务如“工单WX20240315本文还有配套的精品资源点击获取
网站建设高端定制企业官网