SpringCloud+Vue民宿房屋租赁系统 附带详细运行指导视频

文章目录

一、项目演示

项目演示地址: 视频地址

二、项目介绍

项目描述:这是一个基于SpringBoot+Vue框架 开发的民宿房屋租赁系统。首先,这是一个前后端分离的项目,代码简洁规范,注释说明详细,易于理解和学习。其次,后端采用微服务架构,功能丰富,具有一个民宿房屋租赁系统该有的所有功能。

项目功能:此项目分为三个 角色:普通用户、房东和管理员。

普通用户 有登录、注册、按条件筛选房屋信息、查看房屋详情、预订房屋下单、管理个人订单、收藏房屋、评价房屋、申请成为房东、提交意见反馈、查看个人数据统计等等功能。

房东 有登录、申请成为房东、发布房屋、管理个人发布的房屋信息、管理个人订单、查看个人房屋评价信息、查看个人数据统计等等功能。

管理员有登录、查看数据统计信息、管理所有用户信息、审核房东申请信息、审核房屋信息、管理所有房屋信息、管理所有订单信息、管理所有评价信息、管理所有设施信息、管理所有意见反馈信息等等功能。

应用技术:SpringBoot3 + Spring Cloud Alibaba(Nacos + Sentinel + OpenFeign + Gateway)+ Vue3 + MySQL + MyBatis-Plus + ElementUI-Plus + Pinia + Vite

运行环境:IntelliJ IDEA + MySQL + Nacos + JDK17 + Maven + Node + Visual Studio Code

三、运行截图

四、主要代码

1.预订下单代码

java 复制代码
   /**
     * 预订下单:校验房屋存在且审核通过、日期合法、人数不超上限,生成订单快照落库(初始状态为待支付)
     *
     * @param order  订单信息(取houseId、checkIn、checkOut、guests)
     * @param userId 当前登录用户id(网关透传)
     * @return 响应结果
     */
    @Override
    public Result<String> addOrder(Order order, Integer userId) {
        // 参数基础校验
        if (order == null || order.getHouseId() == null
                || order.getCheckIn() == null || order.getCheckOut() == null
                || order.getGuests() == null) {
            return Result.error("房屋id、入住退房日期和入住人数不能为空");
        }
        // 校验入住日期不能早于今天
        if (order.getCheckIn().isBefore(LocalDate.now())) {
            return Result.error("入住日期不能早于今天");
        }
        // 校验退房日期必须晚于入住日期
        if (!order.getCheckOut().isAfter(order.getCheckIn())) {
            return Result.error("退房日期必须晚于入住日期");
        }
        // 校验入住人姓名(数据库guest_name为varchar(32))
        if (StrUtil.isBlank(order.getGuestName())) {
            return Result.error("入住人姓名不能为空");
        }
        if (order.getGuestName().trim().length() > 32) {
            return Result.error("入住人姓名长度不能超过32个字符");
        }
        // 校验联系电话格式
        if (StrUtil.isBlank(order.getPhone()) || !order.getPhone().matches("^1[3-9]\\d{9}$")) {
            return Result.error("联系电话格式不正确");
        }
        // 校验房屋是否存在且审核通过(调用房屋服务查询)
        House house = getHouseById(order.getHouseId());
        if (house == null) {
            return Result.error("房屋不存在");
        }
        // 房东不能预订自己发布的房源
        if (Objects.equals(house.getUserId(), userId)) {
            return Result.error("不能预订自己发布的房源");
        }
        if (!Objects.equals(house.getState(), HOUSE_STATE_APPROVED)) {
            return Result.error("该房屋未通过审核,暂不能预订");
        }
        // 校验入住人数不能超过房屋最大容纳人数
        if (order.getGuests() > house.getMaxNum()) {
            return Result.error("入住人数不能超过房屋最大容纳人数" + house.getMaxNum() + "人");
        }
        // 计算入住晚数与订单总金额(订单总额=房价×晚数,押金不计入订单总额)
        long nights = ChronoUnit.DAYS.between(order.getCheckIn(), order.getCheckOut());
        BigDecimal totalAmount = house.getPrice().multiply(BigDecimal.valueOf(nights));
        // 查询房屋封面图(取排序最靠前的一张,无图时使用默认图;调用图片服务查询)
        List<Picture> coverPictures = listPicturesByRefs(List.of(order.getHouseId()), PICTURE_TYPE_HOUSE);
        Picture cover = coverPictures.isEmpty() ? null : coverPictures.get(0);
        // 生成订单快照并落库
        Order save = new Order();
        save.setOrderNo(DateUtil.format(new Date(), "yyyyMMddHHmmssSSS") + RandomUtil.randomNumbers(4));
        save.setUserId(userId);
        save.setHouseId(house.getId());
        save.setHouseName(house.getName());
        save.setHouseImage(cover != null ? cover.getPhoto() : DEFAULT_HOUSE_IMAGE);
        save.setCheckIn(order.getCheckIn());
        save.setCheckOut(order.getCheckOut());
        save.setNights((int) nights);
        save.setGuests(order.getGuests());
        save.setGuestName(order.getGuestName().trim());
        save.setPhone(order.getPhone());
        save.setPrice(house.getPrice());
        save.setDeposit(house.getPledgePrice());
        save.setTotalAmount(totalAmount);
        save.setStatus(ORDER_STATUS_PENDING_PAYMENT);
        save.setRemark(order.getRemark());
        save.setCreateTime(LocalDateTime.now());
        orderMapper.insert(save);
        return Result.success("预订成功", null);
    }

2.保存房屋代码

java 复制代码
    /**
     * 保存房屋(新增或修改,同时保存关联设施和图片)
     * 权限规则:管理员(3)可管理所有房屋并可指定房东;房东(2)仅能管理自己名下的房屋
     *
     * @param house       房屋信息(含关联设施id集合、图片路径集合)
     * @param loginUserId 当前登录用户id(网关透传)
     * @param loginRoleId 当前登录用户角色(网关透传)
     * @return 响应结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<String> saveHouse(House house, Integer loginUserId, Integer loginRoleId) {
        // 参数基础校验
        if (StrUtil.isBlank(house.getName()) || StrUtil.isBlank(house.getCity())
                || StrUtil.isBlank(house.getLocation())) {
            return Result.error("房屋名称、城市和详细位置不能为空");
        }
        if (house.getPrice() == null || house.getPrice().compareTo(BigDecimal.ZERO) < 0) {
            return Result.error("请填写正确的入住价格");
        }
        if (house.getPledgePrice() != null && house.getPledgePrice().compareTo(BigDecimal.ZERO) < 0) {
            return Result.error("请填写正确的押金金额");
        }
        if (house.getHouseType() == null || house.getHouseType() < 1 || house.getHouseType() > 6) {
            return Result.error("请选择正确的房屋类型");
        }
        if (ROLE_ADMIN.equals(loginRoleId)) {
            // 管理员新增房屋时必须指定房东
            if (house.getId() == null && house.getUserId() == null) {
                return Result.error("请选择房屋所属房东");
            }
        } else if (ROLE_LANDLORD.equals(loginRoleId)) {
            if (house.getId() == null) {
                // 房东新增房屋:房屋归属于自己
                house.setUserId(loginUserId);
            }
        } else {
            return Result.error("没有保存房屋的权限");
        }
        // 补全默认值
        if (house.getMaxNum() == null || house.getMaxNum() < 1) {
            house.setMaxNum(1);
        }
        if (house.getPledgePrice() == null) {
            house.setPledgePrice(BigDecimal.ZERO);
        }

        if (house.getId() == null) {
            // 新增房屋(id由数据库自增生成,评分默认0.0由评价产生,状态默认待审核)
            house.setRate(BigDecimal.ZERO);
            house.setState(HOUSE_STATE_PENDING);
            house.setRemark("");
            houseMapper.insert(house);
            // 保存关联设施和图片
            saveHouseRelation(house);
            return Result.success("添加房屋成功,请等待管理员审核", null);
        }

        // 修改房屋
        House dbHouse = houseMapper.selectById(house.getId());
        if (dbHouse == null) {
            return Result.error("房屋不存在");
        }
        // 房东只能修改自己名下的房屋,且房屋归属保持不变
        if (!ROLE_ADMIN.equals(loginRoleId)) {
            if (!dbHouse.getUserId().equals(loginUserId)) {
                return Result.error("只能修改自己名下的房屋");
            }
            house.setUserId(dbHouse.getUserId());
        }
        // 评分由订单评价产生,不随表单修改;修改后需重新审核,清空原审核备注
        house.setRate(dbHouse.getRate());
        house.setState(HOUSE_STATE_PENDING);
        house.setRemark("");
        houseMapper.updateById(house);
        // 重新保存关联设施和图片(先删后插)
        saveHouseRelation(house);
        return Result.success("修改房屋成功,请等待管理员重新审核", null);
    }

3.用户登录代码

java 复制代码
    /**
     * 用户登录:校验手机号、密码、角色,签发JWT token
     * 管理后台需传角色(2房东/3管理员);租客端可不传角色,默认以普通用户(1)身份登录
     *
     * @param loginDTO 登录参数(手机号/密码/角色)
     * @return 响应结果,data 为 token 字符串
     */
    @Override
    public Result<String> login(LoginDTO loginDTO) {
        // 参数基础校验
        if (loginDTO == null || StrUtil.isBlank(loginDTO.getPhone())
                || StrUtil.isBlank(loginDTO.getPassword())) {
            return Result.error("请填写完整的登录信息");
        }
        // 校验登录角色合法性(未传角色时跳过,按账号实际角色登录,支持房东/管理员登录前台web)
        if (loginDTO.getRoleId() != null && !ROLE_LANDLORD.equals(loginDTO.getRoleId())
                && !ROLE_ADMIN.equals(loginDTO.getRoleId()) && !ROLE_NORMAL.equals(loginDTO.getRoleId())) {
            return Result.error("请选择正确的登录角色");
        }
        // 根据手机号查询用户
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .eq(User::getPhone, loginDTO.getPhone()));
        if (user == null || !loginDTO.getPassword().equals(user.getPassword())) {
            return Result.error("手机号或密码错误");
        }
        // 显式传入角色时校验与账号实际角色是否一致(后台管理端按角色入口登录)
        if (loginDTO.getRoleId() != null && !loginDTO.getRoleId().equals(user.getRoleId())) {
            return Result.error("当前账号无此角色登录权限");
        }
        // 签发JWT token(OAuth2 JWT,含 iss/jti/roles 标准声明,按账号实际角色签发)
        String token = jwtTokenService.createToken(String.valueOf(user.getId()), user.getRoleId());
        return Result.success("登录成功", token);
    }
相关推荐
RuoyiOffice1 小时前
SpringBoot OA推荐:面向信创选型,看看这套企业协同平台
spring boot·vue3·信创·flowable·企业协同·ruoyi office·oa选型
jianxuan2 小时前
Spring Boot + Maven 接入 Allatori:从普通打包到可选混淆发布
spring boot·python·maven
RuoyiOffice4 小时前
llama.cpp + SpringBoot3:本地部署 Qwen3.8,接入 AgentOS 企业办公智能体
spring boot·springboot3·llama.cpp·spring ai·qwen3.8·agentos
学长毕设13 小时前
基于SpringBoot的青少年编程学习平台系统-附源码
spring boot·学习·青少年编程
小大宇13 小时前
Vue知识点6-computed计算属性
前端·javascript·vue.js
程序员海军13 小时前
尤雨溪 : Vue 4 已经在规划中了!
前端·javascript·vue.js
徐小夕17 小时前
doc2docx方案,开源!
前端·vue.js·github
徐小夕17 小时前
JitOFD:一款面向信创与档案场景的 Web 端 OFD 编辑器
前端·vue.js·github
学长毕设18 小时前
基于SpringBoot的建筑材料管理系统-附源码
java·spring boot·后端