SpringBoot+Vue3民宿租赁系统:从数据库设计到部署上线的完整实战
做民宿租赁系统这件事我一开始是想省事的。去年有位做城市民宿的朋友找我说市面上能找到的开源项目要么太重要么前后端还在一起改一个页面要拖着整个模板引擎跑。我只想要一套“能管房态、能下单、能结算”的系统。于是我干脆用 Java SpringBoot 写后端Vue3 写前端MyBatis 做数据访问层MySQL 做存储前后端彻底分离重新整理了一套可以直接上线的民宿租赁系统源码。这段时间陆续有人问这套系统的技术细节所以写成这篇内容讲清楚它从数据库设计到部署上线的完整逻辑也把最容易踩的几个坑一次说透。如果你正打算做毕业设计、学前后端分离实战或者想快速搭一个民宿管理平台这篇内容应该能帮你省下不少时间。1. 从一套可复用的民宿租赁源码说起项目定位与选型时的取舍1.1 民宿租赁系统的业务边界民宿和连锁酒店最大的区别在哪里酒店按标准间、大床房售卖每天一个牌价客人到店办入住。民宿租的是整套房子或独立房间同一个房源在不同日期的价格完全可能不一样周五和周一价差两倍都是常态。再加上农家乐、短租公寓往往只有几间房老板要自己看房态、算价、收押金。所以民宿系统的业务重心压在三个地方房源与房态管理房东维护房源、房间、日历价格、可订库存订单流转客人浏览、选日期、创建订单、支付、入住、退房、取消经营数据订单统计、房源收入、评价情况房东后台要看基础报表。想清楚业务边界后我没有把系统设计成酒店 PMS 的样子。酒店 PMS 有房务中心、夜审、房价计划一堆概念民宿业务根本用不上。最后功能收敛成三个模块用户端负责搜索房源和下单房东端负责维护房态和订单管理端只做基础的用户和房源审核。一个系统能长期用下去不是功能多而是每个功能都刚好够用。1.2 为什么最终锁定 SpringBoot Vue3 MyBatis MySQL这套技术栈是反复比较后才定的不是因为它“生态最强”。SpringBoot 解决的是 Java 后端启动和集成的成本。配置少、能直接跑一个可执行 Jar 包就能把整个后端带起来对民宿这种需要快速部署的场景非常重要。权限用 JWT定时任务用 Scheduled接口安全用过滤器全都在一个进程里处理完。Vue3 和 Element Plus 的组合对中后台页面很友好。Vue3 的 Composition API 大大提升了逻辑复用能力。房源搜索页、订单列表页、统计面板都有大量状态需要维护用 reactive 管理查询表单用 ref 管理列表结果比 Vue2 的 data 写法清晰很多。Element Plus 组件齐全日历、表格、表单校验都能直接拿来用。MyBatis 我坚持用 XML 方式写 SQL。民宿业务有不少报表类查询比如“统计某个房源某个月的入住晚数”这种 SQL 用 MyBatis 的 select 标签维护最直观。MyBatis-Plus 虽然能减少 CRUD 代码但业务越复杂它那层抽象反而越会成为阻碍。直接写 SQLSQL 优化和排查问题的时候都更快。MySQL 则是因为民宿项目的数据量在中型平台几百个房源、几万订单这个范围内完全够用。选 MySQL 还有一个实际原因部署资料最多出问题很容易搜到解决方案。比起学一个新的数据库体系把精力留在业务本身更值。1.3 版本选择是最不起眼但最关键的决定看到很多人搜“springboot版本太高”我太理解这个情况了。SpringBoot 3.x 默认要求 JDK17包名也从 javax.servlet 换成了 jakarta.servlet很多老教程的代码直接编译报错。这套源码最终选用 SpringBoot 2.7.18 JDK8理由很实际绝大多数人的服务器和本机还是 JDK8跑 Maven 打包不用额外折腾环境2.7 版本支持 javax 命名空间和网上绝大多数资料兼容。Vue3 这边用 Vite Node 18Element Plus 按官方文档安装即可。如果你是新项目、团队没有 JDK8 包袱直接上 SpringBoot 3.2 JDK17 也可以只是源码不是同一套。技术选型不追求最新追求的是能稳定跑起来、代码能被人看懂。2. 数据库先行民宿表的字段设计与索引背后的考量2.1 七张核心表比想象中要少很多初学者一上来就设计二十多张表全是主数据。实际上民宿系统 90% 的查询都集中在少量表上。最终我保留了七张核心表表名用途核心字段sys_user用户/房东/管理员id, username, password, phone, role_type, create_timehouse房源主表id, owner_id, title, city, address, cover_url, statusroom房间表id, house_id, room_name, bed_type, area, max_guestroom_price_date日期价格库存表id, room_id, price_date, price, stockbooking_order订单主表id, order_no, user_id, room_id, check_in_date, check_out_date, total_amount, statusorder_status_log订单状态日志表id, order_id, from_status, to_status, remark, create_timereview评价表id, order_id, user_id, room_id, rating, content, create_time设计细节上sys_user 的 role_type 用 tinyint0 普通用户1 房东2 管理员。密码存 BCrypt 加密串绝不能存明文。house 和 room 分开拆是因为有些民宿是整栋出租有些是一栋楼里分多个房间。为了扩展哪怕整栋出租时只有一个房间也保留两张表的结构。room 表不带 daily_price因为房价放在日期表里。booking_order 没有用 order 做表名order 在 SQL 里是关键词所以命名加了前缀。订单状态变更日志表非常重要任何状态变化都插入一条记录客服查纠纷时能确切知道订单是怎么从“待支付”变成“已取消”的。review 表通过 order_id 唯一约束保证一个订单只能评价一次。2.2 为什么价格和库存要单独放到日期表这是整套民宿数据库设计里最重要的一点。民宿和酒店的最大不同就是同一个房源在不同日期的价格不同。如果只在 room 表里放一个 daily_price房东改周末价格就得改整张表前端要问“周五到周日这间房多少钱”也只能写临时逻辑。把价格和库存抽到 room_price_date 表之后每个日期的价格变成一行数据房间价格就是一组“日期 价格 库存”的列表。前端日历组件展示可订日期和价格直接查这张表。计算一笔订单金额SQL 里按日期范围求和即可SELECT COALESCE(SUM(price), 0) AS total FROM room_price_date WHERE room_id #{roomId} AND price_date BETWEEN #{checkInDate} AND DATE_SUB(#{checkOutDate}, INTERVAL 1 DAY)这里注意为什么用 DATE_SUB 而不是直接包含退房日。退房日期是最后一天的 12 点房费只算从入住当夜到退房前一晚所以扣款区间是[checkInDate, checkOutDate)。这个细节如果不注意就会多算一晚的钱上线后所有订单金额都差一宿。2.3 索引和约束少建外键多建普通索引物理外键这个项目里一条都没建。原因很现实MySQL 物理外键写入时要检查父表行锁范围会扩大并发一高就容易死锁而且以后想把订单表拆分出去物理外键根本没法迁移。替代方案是逻辑外键 普通索引。哪些索引最值得建room_price_date(room_id, price_date) 唯一索引保证同一个房间同一天只有一行价格记录这是防重复数据的兜底。booking_order(order_no) 唯一索引订单号唯一支付回调时用订单号做幂等。booking_order(user_id) 普通索引用户查自己订单的高频路径。booking_order(status, create_time) 联合索引定时任务扫描待支付超时订单非常快。booking_order(check_in_date) 普通索引统计某天入住订单用得上。另外price_date 字段统一用 DATE 类型不要用 DATETIME。日期语义就是日用 DATE 能省空间也不会出现 00:00:00 这种歧义。订单里 create_time 才用 DATETIME这是两个不同的时间语义。3. 后端接口与MyBatis数据访问层的硬核细节3.1 接口设计保持简单但要有统一风格后端不搞炫技所有接口走 RESTful统一前缀 /api。登录接口返回 JWT token其余需要登录的接口从请求头 Authorization 里读取 token。方法路径功能POST/api/auth/login登录返回 tokenGET/api/house/search分页搜索房源GET/api/house/{id}房源详情GET/api/room/{roomId}/price查询价格日历POST/api/order创建订单POST/api/order/{orderNo}/pay模拟支付POST/api/order/{orderNo}/checkin入住POST/api/order/{orderNo}/checkout退房POST/api/review提交评价分页接口统一用 pageNum、pageSize 参数。返回结构统一是 Result 里面 code0 表示成功非 0 表示业务错误。这样前端 axios 拦截器只看 code 即可不用判断 HTTP 状态码。全局异常处理再补充一个细节业务异常主动 throw BizException比如“价格已变化请刷新后重试”。TechnicalException 留给真正的系统错误。全局异常处理器只对业务异常做包装其余异常打印日志后返回“服务器开小差了”不要把 SQL 报错信息直接抛给前端。3.2 MyBatis XML 动态 SQL 是查询灵活性的关键核心文件是 HouseMapper.xml 和 OrderMapper.xml。房源搜索的典型场景关键字、城市、入住日期、价格区间同时过滤。纯注解 SQL 拼接起来很痛苦XML 的 where 和 if 则清晰很多select idsearchHouses resultTypecom.homestay.domain.vo.HouseVO SELECT h.id, h.title, h.city, h.address, h.cover_url, IFNULL(MIN(rpd.price), 0) AS min_price FROM house h LEFT JOIN room r ON r.house_id h.id LEFT JOIN room_price_date rpd ON rpd.room_id r.id where h.status 1 if testkeyword ! null and keyword ! AND (h.title LIKE CONCAT(%, #{keyword}, %) OR h.city LIKE CONCAT(%, #{keyword}, %)) /if if testcity ! null and city ! AND h.city #{city} /if if teststartDate ! null and endDate ! null AND EXISTS ( SELECT 1 FROM room_price_date rpd2 WHERE rpd2.room_id r.id AND rpd2.price_date BETWEEN #{startDate} AND DATE_SUB(#{endDate}, INTERVAL 1 DAY) AND rpd2.stock 0 ) /if /where GROUP BY h.id, h.title, h.city, h.address, h.cover_url /select这里有个容易错的地方LEFT JOIN GROUP BY。如果 LEFT JOIN 出多条价格记录聚集函数要注意。民宿按“房源”维度搜索所以 GROUP BY h.idmin_price 取最低价作为起价。动态更新订单状态用 set 标签update idupdateOrderStatus UPDATE booking_order set if testpayTime ! nullpay_time #{payTime},/if if teststatus ! nullstatus #{status},/if /set WHERE order_no #{orderNo} /update这样可以避免 NULL 覆盖原有值。实际开发中很多数据不一致问题就出在“全字段更新”上。3.3 MyBatis缓存的坑民宿订单模块千万别开二级缓存MyBatis 一级缓存是 SqlSession 级别。在 Spring 管理的 Mapper 里一个事务内两次查询默认复用 SqlSession所以一级缓存实际上跟着事务走。大多时候没问题但如果你在一个大事务里先查订单再等另一个逻辑更新了订单第二次查同一个订单可能拿到旧数据。订单相关查询要么放在独立事务里要么干脆在 SQL 上使用乐观锁版本号。二级缓存更危险它是 namespace 级的。如果 HouseMapper 开了二级缓存而 RoomMapper 更新了房间信息HouseMapper 的缓存不会感知就会出现“房间已改名房源列表还显示旧名”的脏数据。民宿的数据变化频率比字典表高得多所以我在 mybatis-config.xml 里直接关闭二级缓存settings setting namecacheEnabled valuefalse/ setting namelocalCacheScope valueSTATEMENT/ /settingslocalCacheScope 设为 STATEMENT 是更严格的做法每个 SQL 执行完就清一级缓存。虽然会损失少量性能但可以避免大量事务内数据不一致的排查痛苦。对民宿这种中小并发系统完全值得。3.4 分页好不好用只有写了才知道分页用的是 PageHelper但它有几个坑。第一startPage 之后必须紧跟一条 SQL否则分页不生效。第二startPage 使用 ThreadLocal线程池复用时如果不 clearPage下一个请求会串数据。第三COUNT 查询会帮你在原 SQL 外面包一层如果 SQL 里有 GROUP BYCOUNT 直接报错。所以在代码里封装了一个 PaginationUtils用 try-finally 包裹public T PageResultT pageQuery(PageParam param, SupplierListT query) { PageHelper.startPage(param.getPageNum(), param.getPageSize()); ListT list; try { list query.get(); } finally { PageHelper.clearPage(); } PageInfoT info new PageInfo(list); return new PageResult(info.getTotal(), info.getList()); }这样调用方永远不会忘记 clearPage。如果不想依赖 PageHelper数据量小的时候直接用 limit #{offset}, #{size} 也可以。4. Vue3前端交互设计与前后端联调的关键处理4.1 前端工程结构前端工程也比较务实Vite Vue3 Element Plus Pinia Axios。目录结构大致如下src/ api/ # 后端接口封装 assets/ # 静态资源 components/ # 通用组件如房源卡片、价格日历 layout/ # 用户端/房东端布局 router/ # 路由与守卫 stores/ # Pinia 状态 views/ # 页面组件路由分三套/user、/host、/admin利用路由守卫拦截角色。这里有个小技巧路由 meta 里存 role 数组守卫里判断用户角色和页面允许角色是否有交集比到处 if else 要整洁。4.2 reactive 和 ref 用在哪怎么用不糊涂很多人学 Vue3 最先问 reactive 和 ref 的区别。我的经验是对象类型的表单用 reactive比如搜索条件、新增房源表单基本类型和会被整体替换的数组用 ref比如列表数据、分页信息。搜索页的典型写法const searchForm reactive({ keyword: , city: , priceMin: null, priceMax: null, startDate: , endDate: }) const houseList ref([]) const loading ref(false) async function loadHouses() { loading.value true try { const res await searchHouses({ ...searchForm, pageNum: 1, pageSize: 10 }) houseList.value res.data.list } finally { loading.value false } }注意用展开运算符把 reactive 对象转成普通对象传给 axios否则在传参过程中可能带出 Proxy 的响应式副作用虽然大部分情况下没问题但排查起来很费劲。日期选择器用 Element Plus 的 el-date-pickerv-model 绑定一个长度为 2 的数组。提交时拆成 startDate、endDate 传给后端。后端的 LocalDate 接收使用 JsonFormat(pattern yyyy-MM-dd)返回时同理会自动转成字符串前端就不用再做时间解析。4.3 axios 封装请求带 token 和统一业务码const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(homestay_token) if (token) config.headers.Authorization Bearer ${token} return config }) service.interceptors.response.use( res { if (res.data.code ! 0) { if (res.data.code 401) { router.push(/login) } ElMessage.error(res.data.message) return Promise.reject(new Error(res.data.message)) } return res.data }, err { ElMessage.error(err.response?.data?.message || 网络异常) return Promise.reject(err) } )关键点是拦截器统一处理业务码页面里写const res await searchHouses(...)时拿到 res.data.list 直接能用不会有 data.data.data 的嵌套。4.4 联调时最常见的三个错第一跨域配置。开发环境用 Vite proxy不要开后端 CORS// vite.config.js server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }如果同时打开后端 CORS 和前端 proxyOPTIONS 预检请求会冲突可能看到 Access-Control-Allow-Origin 重复报错。第二时间类型。后端 LocalDateTime 如果没有配 jackson 序列化前端会收到类似[2025, 6, 12, 21, 30, 0]这种数组完全没用。必须在 application.yml 里配spring: jackson: time-zone: GMT8 date-format: yyyy-MM-dd HH:mm:ss然后用 JsonFormat 对 LocalDate 单独注明格式。第三路由刷新 404。开发模式没感觉部署到 Nginx 后刷新 /house/detail/123 会 404原因是前端是 history 路由服务器没配 try_files。这个在第六节部署部分会详细写。5. 订单并发与房态一致性防超卖的防御性设计5.1 没处理并发之前库存确实会变成 -1民宿房态和电商库存不一样一个房间在某个日期就是 1 间卖掉了就没有了。我用两个浏览器分别下单同一个房间同一天结果两个订单都创建成功room_price_date 的 stock 变成了 -1。为什么会这样最初下单逻辑是先 select 查 stock等于 1 就插入订单再 update stock0。两个请求同时 select都看到 1都认为可以卖。解决思路可以抽象成三句话用一条 SQL 扣减不要先查再减扣减时加条件 stock 0如果扣减影响行数为 0说明已经没了事务回滚。5.2 行锁 条件更新下单事务的正确姿势在订单创建这种短事务里最终用了 SELECT ... FOR UPDATE 做悲观锁。先把订单涉及的所有日期价格记录锁住其他人进不来再计算价格、插入订单、扣减库存提交事务时释放锁。核心代码Transactional(rollbackFor Exception.class) public String createOrder(CreateOrderDTO dto) { // 1. 锁定该房间在入住日期区间内的价格记录 ListRoomPriceDate priceRecords roomPriceDateMapper.selectForUpdate( dto.getRoomId(), dto.getCheckInDate(), dto.getCheckOutDate()); if (priceRecords.size() ! nights(dto.getCheckInDate(), dto.getCheckOutDate())) { throw new BizException(所选日期有未开放房价请刷新); } // 2. 检查每一条记录都有库存 long totalAmount 0L; for (RoomPriceDate record : priceRecords) { if (record.getStock() 0) { throw new BizException(record.getPriceDate() 已无房); } totalAmount record.getPrice(); } // 3. 生成订单号日期随机数用户ID后四位 String orderNo generateOrderNo(dto.getUserId()); // 4. 插入订单 bookingOrderMapper.insert(orderNo, dto.getUserId(), dto.getRoomId(), ...); // 5. 扣减每一天库存 roomPriceDateMapper.decreaseStock(dto.getRoomId(), dto.getCheckInDate(), dto.getCheckOutDate()); // 6. 记录状态日志 orderStatusLogMapper.insert(...); return orderNo; }对应的 XMLselect idselectForUpdate resultTypecom.homestay.domain.RoomPriceDate SELECT id, room_id, price_date, price, stock FROM room_price_date WHERE room_id #{roomId} AND price_date gt; #{startDate} AND price_date lt; #{endDate} FOR UPDATE /select update iddecreaseStock UPDATE room_price_date SET stock stock - 1 WHERE room_id #{roomId} AND price_date gt; #{startDate} AND price_date lt; #{endDate} AND stock 0 /update几个关键点为什么不用 BETWEEN 直接写退房日因为退房日不占用房间不能锁。查询区间用 [checkIn, checkOut)。为什么先 FOR UPDATE 再 decreaseStock为了把多条价格记录一次性锁住避免两个人各锁一天造成交叉死锁。FOR UPDATE 时SQL 会按主键索引顺序加锁短事务下死锁概率很低。decreaseStock 的条件里必须带上 stock 0即使前面已经锁住这也是双重保障防止逻辑漏判。事务里如果插入订单失败前面锁会在回滚时释放库存不扣。5.3 乐观锁可以吗可以但场景不一样乐观锁版本号适合读多写少、并发冲突不太多的场景。民宿平台做到节假日抢房两个用户同时抢最后一间房乐观锁会有一方 update 影响行数为 0只能重试体验很差。悲观锁虽然会短暂阻塞另一个请求但对于“抢一个日期库存”这种业务阻塞一下反而是最自然的等待。源码里我保留了两种方案默认开启悲观锁配置项order.use-pessimistic-lock: true可以切换成乐观锁。想学习并发控制的朋友可以把配置改成 false跑一遍并发测试会看到冲突率明显上升。理解两种锁的差异比背面试题有用得多。5.4 状态机与超时自动取消订单状态放在 status 字段里0 待支付1 已支付2 已入住3 已退房4 已取消5 已退款允许的状态流转待支付 - 已支付支付成功回调待支付 - 已取消用户手动取消/超时已支付 - 已入住到店办理已支付 - 已退款用户申请取消且通过已入住 - 已退房超时取消用 Spring 的 Scheduled 定时任务执行UPDATE booking_order SET status 4 WHERE status 0 AND create_time DATE_SUB(NOW(), INTERVAL 15 MINUTE)生产环境再给这个 SQL 加一个 limit 限制一次改多少避免批量锁太多行。恢复库存靠另一个任务把取消订单对应的日期段 stock 加回来。这里要注意幂等每条订单都有唯一订单号通过 order_no 判断是否已经处理过。6. 部署环境搭建与上线排障MySQL安装、打包、Nginx转发6.1 本地先把 MySQL 跑起来很多人卡在 MySQL 安装这一步。MySQL 8.0 安装包在 Windows 下最好选 Custom把 Server 和 Command Line Client 都装上。安装时字符集选 utf8mb4不要选默认的 latin1否则中文会变问号。安装完成后检查服务mysql -uroot -p常见错误连接时提示 Public Key Retrieval is not allowed需要在 JDBC 连接串加 allowPublicKeyRetrievaltrue。MySQL8 默认 caching_sha2_password 插件会引发这个错。报 Access denied for user rootlocalhost多半是密码授权问题用 root 登录后执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;端口被占用可以netstat -ano | findstr :3306查。初始化数据库脚本mysql -uroot -p -e CREATE DATABASE homestay DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; mysql -uroot -p homestay db/homestay.sql6.2 后端打包与环境变量后端使用 Maven 打包mvn clean package -DskipTests生产环境配置文件 application-prod.yml 里不写死数据库密码用环境变量占位spring: datasource: url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: ${DB_USER} password: ${DB_PASSWORD}启动export DB_HOST127.0.0.1 export DB_USERhomestay export DB_PASSWORDxxx nohup java -Xms256m -Xmx512m -jar homestay.jar --spring.profiles.activeprod --server.port8080 logs/app.log 21 为什么堆内存只给 256m 到 512m民宿后端没有大对象缓存512m 完全够。给太多了反而浪费服务器资源。后面如果加 Redis再调大。6.3 前端打包与 Nginx 代理前端打包npm run builddist 目录上传到服务器 /home/www/homestay/。Nginx 配置server { listen 80; server_name homestay.example.com; root /home/www/homestay/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } }这里有两个关键细节proxy_pass http://127.0.0.1:8080; 末尾没有斜杠意思是把 /api/xxx 原样转发给后端。如果写成 http://127.0.0.1:8080/;会在转发时去掉 /api 前缀后端路由就找不到了。实际部署时最容易因为这个 404。try_files $uri $uri/ /index.html; 是为了解决 Vue3 history 路由刷新 404。所有未匹配到物理文件的路径都回退到 index.html由前端路由接管。6.4 上线后我总结的三个易错点第一时间统一问题。MySQL 连接串、JVM 默认时区、前端 dayjs 解析三者必须都是 Asia/Shanghai。尤其是 MySQL 连接串没写 serverTimezone默认用 JVM 时区会导致时间偏移八小时。第二日志滚动。SpringBoot 默认日志文件会一直涨不加配置几个月就能占满磁盘。在 logback-spring.xml 里配按天滚动保留三十天即可。第三备份。民宿系统最重要的数据就一张订单表用 crontab 每天凌晨跑一次 mysqldump0 2 * * * mysqldump -uhomestay -pxxx homestay /backup/homestay_$(date \%Y\%m\%d).sql备份和恢复本身不复杂但很多人到数据丢了才想起来。最后再说一个我实际运维中的体会。这套源码最值得阅读的顺序不是从前端页面开始而是先看数据库设计再看 OrderMapper.xml 和 createOrder 方法。我见过很多朋友先把页面调得五颜六色结果下单并发逻辑没想清楚一测试库存就负数。民宿租赁系统的核心价值是“房态不会卖重、订单状态可追溯”UI 永远排在后面。把这两条主线跑通后面换任何前端框架、加任何营销功能都不会乱。

相关新闻

t3code:类型生成、Three.js与Token统计的命令行工具

t3code:类型生成、Three.js与Token统计的命令行工具

写 t3code 这个工具,纯粹是被三个重复劳动逼出来的。日常开发里我同时维护前端项目和几个三维展示页面,还要时不时代管一些文本预处理脚本,时间长了就发现三件事特别烦:手写 TypeScript 接口定义、反复调 Three.js 的场景初始化模…

2026/10/9 12:31:55 阅读更多 →
JSP+MVC+MySQL实战:从零构建图书购物网站

JSP+MVC+MySQL实战:从零构建图书购物网站

简介:这是一套基于JSP与MVC设计模式、以MySQL为数据库的网上图书购物系统源码,面向Java Web初学者、进阶学习者以及需要完成毕设、课程设计或大作业的学生,帮助其理解分层架构与购物流程的实现思路。压缩包共76个文件,约47.8MB&am…

2026/10/9 12:31:54 阅读更多 →
Python中的类对象示例详解

Python中的类对象示例详解

前言 "类对象"这个说法经常被误用。有人用它泛指"类的实例",有人用它指"类本身"。本文取后者:类本身也是一个对象。这一句不是修辞——你可以把类赋值给变量、放进字典、当参数传、当返回值返回,就像操作任何普…

2026/10/9 12:31:54 阅读更多 →

最新新闻

双色球历史开奖数据导入MySQL与Excel分析实战:从建表到数据校验

双色球历史开奖数据导入MySQL与Excel分析实战:从建表到数据校验

简介:双色球自2003年2月23日首期开售至2025年4月15日全部3287期开奖记录,已按时间顺序完整整理为一份轻量数据包,面向需要批量获取历史号码的趋势分析、频次统计或预测建模用户,能有效省去手工收集与清洗校验的繁琐环节。压缩包共…

2026/10/9 16:49:11 阅读更多 →
达梦DM8 DCP笔试解析:SQL语义、备份恢复与避坑冲刺

达梦DM8 DCP笔试解析:SQL语义、备份恢复与避坑冲刺

简介:这是一份达梦 DM8 数据库认证笔试题解析文档,专门面向备考达梦认证考试以及需要日常维护达梦数据库的 IT 从业者、数据库管理员。内容围绕认证考试常见的单选、多选与判断题型展开,覆盖 SQL 语言基础、数据库对象创建与管理、事务处理、…

2026/10/9 16:49:11 阅读更多 →
C# WinForm 部署 YOLOv11 ONNX 模型:从导出、推理到后处理避坑指南

C# WinForm 部署 YOLOv11 ONNX 模型:从导出、推理到后处理避坑指南

简介:面向需要在Windows桌面端集成深度学习目标检测能力的C#开发者,这是一份基于WinForm部署YOLOv11的完整工程示例,借助ONNX Runtime与OpenCvSharp完成模型加载、图像预处理、推理预测、目标框绘制与结果展示,开箱即可运行演示&a…

2026/10/9 16:49:11 阅读更多 →
汽车美容店管理系统数据库设计:从ER模型到存储过程的完整实践

汽车美容店管理系统数据库设计:从ER模型到存储过程的完整实践

简介:这是中北大学软件学院数据库课程设计任务书,面向软件工程等专业学生,可用于完成“某汽车美容店管理系统数据库设计”课程作业,也可作为数据库课程设计全流程的参考模板。文档明确了设计目的、内容和要求,涵盖美容…

2026/10/9 16:49:11 阅读更多 →
WSL2+Codex+Superpowers踩坑实录:本地AI编程环境搭建避坑指南

WSL2+Codex+Superpowers踩坑实录:本地AI编程环境搭建避坑指南

1. 这不是又一篇“安装教程”,而是一份真实踩出来的WSLCodexSuperpowers组合拳操作日志我第一次在某高校实验室的旧笔记本上装WSL2跑Codex时,以为只是点几下鼠标的事。结果从凌晨两点折腾到早上六点,重装了四次Ubuntu子系统,两次删…

2026/10/9 16:49:11 阅读更多 →
Oracle EBS财务模块实操手册:环境验证与事务链路解析

Oracle EBS财务模块实操手册:环境验证与事务链路解析

简介:本资源是Oracle EBS财务全模块的官方级用户操作手册中文版,面向ERP实施顾问、财务系统运维人员及Oracle财务模块初学者,聚焦总账、凭证管理、预算控制、外币处理等核心财务流程的实操指导。文档结构完整,涵盖系统配置、快捷键…

2026/10/9 16:48:10 阅读更多 →

日新闻

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API实战:LocalDate、Date与ZonedDateTime的转换与避坑指南

Java时间API这个话题,隔三差五就会在群里被翻出来讨论一次。上周还有个同事线上处理一个订单超时问题,排查到最后发现是ZonedDateTime序列化后时区丢了,用户在下单当天晚上看到的时间整整差了8个小时。这类问题几乎每个做Java开发的人都遇到过…

2026/10/9 0:00:49 阅读更多 →
EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

EasyTier实践:从NAT穿透到子网代理的异地组网部署与排错

前几个月我手头有好几台机器需要互相访问:办公室台式机、家里 NAS、还有一台云主机。如果只是偶尔传个文件倒还好,问题是工作场景经常要在几处环境之间来回切换,每次都先登录跳板机再层层代理,实在折腾。我先后试过端口映射、自建…

2026/10/9 0:00:49 阅读更多 →
AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent工程实战:从七要素到七个决策点的系统设计指南

AI Agent 这个词在过去一年里被反复提及,但真正动手搭过一套能跑起来的 Agent 系统的人都知道,从"知道它是什么"到"让它稳定干活"之间隔着一整套工程决策。我前后参与过几个 Agent 项目的落地,从最初用现成框架拼装&…

2026/10/9 0:01:50 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:32 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:40 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 10:11:06 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 21:13:17 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 15:26:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 6:17:20 阅读更多 →