Java全栈项目-大学生租房管理系统(3)

代码3:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.HouseDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.House">
        <id column="id" property="id"/>
        <result column="landlord_id" property="landlordId"/>
        <result column="title" property="title"/>
        <result column="price" property="price"/>
        <result column="area" property="area"/>
        <result column="house_type" property="houseType"/>
        <result column="floor" property="floor"/>
        <result column="orientation" property="orientation"/>
        <result column="decoration" property="decoration"/>
        <result column="community" property="community"/>
        <result column="address" property="address"/>
        <result column="longitude" property="longitude"/>
        <result column="latitude" property="latitude"/>
        <result column="contact" property="contact"/>
        <result column="contact_phone" property="contactPhone"/>
        <result column="status" property="status"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, landlord_id, title, price, area, house_type, floor, orientation, decoration, community, address, 
        longitude, latitude, contact, contact_phone, status, create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house
        where id = #{id}
    </select>

    <select id="selectByLandlordId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house
        where landlord_id = #{landlordId}
        order by create_time desc
    </select>

    <select id="selectList" parameterType="com.rental.entity.House" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house
        <where>
            <if test="landlordId != null">
                and landlord_id = #{landlordId}
            </if>
            <if test="title != null and title != ''">
                and title like concat('%', #{title}, '%')
            </if>
            <if test="houseType != null and houseType != ''">
                and house_type = #{houseType}
            </if>
            <if test="community != null and community != ''">
                and community like concat('%', #{community}, '%')
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
        </where>
        order by create_time desc
    </select>

    <select id="search" parameterType="java.util.Map" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house
        <where>
            <if test="keyword != null and keyword != ''">
                and (
                    title like concat('%', #{keyword}, '%')
                    or community like concat('%', #{keyword}, '%')
                    or address like concat('%', #{keyword}, '%')
                )
            </if>
            <if test="minPrice != null">
                and price >= #{minPrice}
            </if>
            <if test="maxPrice != null">
                and price <= #{maxPrice}
            </if>
            <if test="minArea != null">
                and area >= #{minArea}
            </if>
            <if test="maxArea != null">
                and area <= #{maxArea}
            </if>
            <if test="houseType != null and houseType != ''">
                and house_type = #{houseType}
            </if>
            <if test="orientation != null and orientation != ''">
                and orientation = #{orientation}
            </if>
            <if test="decoration != null and decoration != ''">
                and decoration = #{decoration}
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
            <if test="status == null">
                and status = 1
            </if>
        </where>
        order by
        <choose>
            <when test="orderBy != null and orderBy == 'price_asc'">
                price asc
            </when>
            <when test="orderBy != null and orderBy == 'price_desc'">
                price desc
            </when>
            <when test="orderBy != null and orderBy == 'area_asc'">
                area asc
            </when>
            <when test="orderBy != null and orderBy == 'area_desc'">
                area desc
            </when>
            <when test="orderBy != null and orderBy == 'time_asc'">
                create_time asc
            </when>
            <otherwise>
                create_time desc
            </otherwise>
        </choose>
    </select>

    <insert id="insert" parameterType="com.rental.entity.House" useGeneratedKeys="true" keyProperty="id">
        insert into house
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="landlordId != null">landlord_id,</if>
            <if test="title != null">title,</if>
            <if test="price != null">price,</if>
            <if test="area != null">area,</if>
            <if test="houseType != null">house_type,</if>
            <if test="floor != null">floor,</if>
            <if test="orientation != null">orientation,</if>
            <if test="decoration != null">decoration,</if>
            <if test="community != null">community,</if>
            <if test="address != null">address,</if>
            <if test="longitude != null">longitude,</if>
            <if test="latitude != null">latitude,</if>
            <if test="contact != null">contact,</if>
            <if test="contactPhone != null">contact_phone,</if>
            <if test="status != null">status,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="landlordId != null">#{landlordId},</if>
            <if test="title != null">#{title},</if>
            <if test="price != null">#{price},</if>
            <if test="area != null">#{area},</if>
            <if test="houseType != null">#{houseType},</if>
            <if test="floor != null">#{floor},</if>
            <if test="orientation != null">#{orientation},</if>
            <if test="decoration != null">#{decoration},</if>
            <if test="community != null">#{community},</if>
            <if test="address != null">#{address},</if>
            <if test="longitude != null">#{longitude},</if>
            <if test="latitude != null">#{latitude},</if>
            <if test="contact != null">#{contact},</if>
            <if test="contactPhone != null">#{contactPhone},</if>
            <if test="status != null">#{status},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.House">
        update house
        <set>
            <if test="title != null">title = #{title},</if>
            <if test="price != null">price = #{price},</if>
            <if test="area != null">area = #{area},</if>
            <if test="houseType != null">house_type = #{houseType},</if>
            <if test="floor != null">floor = #{floor},</if>
            <if test="orientation != null">orientation = #{orientation},</if>
            <if test="decoration != null">decoration = #{decoration},</if>
            <if test="community != null">community = #{community},</if>
            <if test="address != null">address = #{address},</if>
            <if test="longitude != null">longitude = #{longitude},</if>
            <if test="latitude != null">latitude = #{latitude},</if>
            <if test="contact != null">contact = #{contact},</if>
            <if test="contactPhone != null">contact_phone = #{contactPhone},</if>
            <if test="status != null">status = #{status},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <update id="updateStatus">
        update house
        set status = #{status}, update_time = now()
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from house
        where id = #{id}
    </delete>

    <delete id="deleteBatchByIds" parameterType="java.lang.Long">
        delete from house
        where id in
        <foreach collection="array" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
</mapper>

backend\src\main\resources\mapper\HouseReviewMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.HouseReviewDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.HouseReview">
        <id column="id" property="id"/>
        <result column="house_id" property="houseId"/>
        <result column="user_id" property="userId"/>
        <result column="content" property="content"/>
        <result column="rating" property="rating"/>
        <result column="location_rating" property="locationRating"/>
        <result column="cleanliness_rating" property="cleanlinessRating"/>
        <result column="value_rating" property="valueRating"/>
        <result column="landlord_rating" property="landlordRating"/>
        <result column="images" property="images"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, house_id, user_id, content, rating, location_rating, cleanliness_rating, value_rating, 
        landlord_rating, images, create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        where id = #{id}
    </select>

    <select id="selectByHouseId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        where house_id = #{houseId}
        order by create_time desc
    </select>

    <select id="selectByUserId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        where user_id = #{userId}
        order by create_time desc
    </select>

    <select id="selectByHouseIdAndUserId" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        where house_id = #{houseId} and user_id = #{userId}
        limit 1
    </select>

    <select id="selectList" parameterType="com.rental.entity.HouseReview" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        <where>
            <if test="houseId != null">
                and house_id = #{houseId}
            </if>
            <if test="userId != null">
                and user_id = #{userId}
            </if>
            <if test="rating != null">
                and rating = #{rating}
            </if>
        </where>
        order by create_time desc
    </select>

    <select id="calculateAverageRating" parameterType="java.lang.Long" resultType="java.lang.Double">
        select avg(rating)
        from house_review
        where house_id = #{houseId}
    </select>

    <insert id="insert" parameterType="com.rental.entity.HouseReview" useGeneratedKeys="true" keyProperty="id">
        insert into house_review
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="houseId != null">house_id,</if>
            <if test="userId != null">user_id,</if>
            <if test="content != null">content,</if>
            <if test="rating != null">rating,</if>
            <if test="locationRating != null">location_rating,</if>
            <if test="cleanlinessRating != null">cleanliness_rating,</if>
            <if test="valueRating != null">value_rating,</if>
            <if test="landlordRating != null">landlord_rating,</if>
            <if test="images != null">images,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="houseId != null">#{houseId},</if>
            <if test="userId != null">#{userId},</if>
            <if test="content != null">#{content},</if>
            <if test="rating != null">#{rating},</if>
            <if test="locationRating != null">#{locationRating},</if>
            <if test="cleanlinessRating != null">#{cleanlinessRating},</if>
            <if test="valueRating != null">#{valueRating},</if>
            <if test="landlordRating != null">#{landlordRating},</if>
            <if test="images != null">#{images},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.HouseReview">
        update house_review
        <set>
            <if test="content != null">content = #{content},</if>
            <if test="rating != null">rating = #{rating},</if>
            <if test="locationRating != null">location_rating = #{locationRating},</if>
            <if test="cleanlinessRating != null">cleanliness_rating = #{cleanlinessRating},</if>
            <if test="valueRating != null">value_rating = #{valueRating},</if>
            <if test="landlordRating != null">landlord_rating = #{landlordRating},</if>
            <if test="images != null">images = #{images},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from house_review
        where id = #{id}
    </delete>
</mapper>

backend\src\main\resources\mapper\ReviewReplyMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.ReviewReplyDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.ReviewReply">
        <id column="id" property="id"/>
        <result column="review_id" property="reviewId"/>
        <result column="user_id" property="userId"/>
        <result column="content" property="content"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, review_id, user_id, content, create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from review_reply
        where id = #{id}
    </select>

    <select id="selectByReviewId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from review_reply
        where review_id = #{reviewId}
        order by create_time asc
    </select>

    <select id="selectByUserId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from review_reply
        where user_id = #{userId}
        order by create_time desc
    </select>

    <insert id="insert" parameterType="com.rental.entity.ReviewReply" useGeneratedKeys="true" keyProperty="id">
        insert into review_reply
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="reviewId != null">review_id,</if>
            <if test="userId != null">user_id,</if>
            <if test="content != null">content,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="reviewId != null">#{reviewId},</if>
            <if test="userId != null">#{userId},</if>
            <if test="content != null">#{content},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.ReviewReply">
        update review_reply
        <set>
            <if test="content != null">content = #{content},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from review_reply
        where id = #{id}
    </delete>
</mapper>
2.5 Service Interfaces

backend\src\main\resources\mapper\UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.UserDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="real_name" property="realName"/>
        <result column="phone" property="phone"/>
        <result column="email" property="email"/>
        <result column="avatar" property="avatar"/>
        <result column="gender" property="gender"/>
        <result column="status" property="status"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, username, password, real_name, phone, email, avatar, gender, status, create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from user
        where id = #{id}
    </select>

    <select id="selectByUsername" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from user
        where username = #{username}
    </select>

    <select id="selectByPhone" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from user
        where phone = #{phone}
    </select>

    <select id="selectList" parameterType="com.rental.entity.User" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from user
        <where>
            <if test="username != null and username != ''">
                and username like concat('%', #{username}, '%')
            </if>
            <if test="realName != null and realName != ''">
                and real_name like concat('%', #{realName}, '%')
            </if>
            <if test="phone != null and phone != ''">
                and phone like concat('%', #{phone}, '%')
            </if>
            <if test="email != null and email != ''">
                and email like concat('%', #{email}, '%')
            </if>
            <if test="gender != null">
                and gender = #{gender}
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
        </where>
        order by create_time desc
    </select>

    <insert id="insert" parameterType="com.rental.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into user
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username != null">username,</if>
            <if test="password != null">password,</if>
            <if test="realName != null">real_name,</if>
            <if test="phone != null">phone,</if>
            <if test="email != null">email,</if>
            <if test="avatar != null">avatar,</if>
            <if test="gender != null">gender,</if>
            <if test="status != null">status,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="username != null">#{username},</if>
            <if test="password != null">#{password},</if>
            <if test="realName != null">#{realName},</if>
            <if test="phone != null">#{phone},</if>
            <if test="email != null">#{email},</if>
            <if test="avatar != null">#{avatar},</if>
            <if test="gender != null">#{gender},</if>
            <if test="status != null">#{status},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.User">
        update user
        <set>
            <if test="username != null">username = #{username},</if>
            <if test="password != null">password = #{password},</if>
            <if test="realName != null">real_name = #{realName},</if>
            <if test="phone != null">phone = #{phone},</if>
            <if test="email != null">email = #{email},</if>
            <if test="avatar != null">avatar = #{avatar},</if>
            <if test="gender != null">gender = #{gender},</if>
            <if test="status != null">status = #{status},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <update id="updateStatus">
        update user
        set status = #{status}, update_time = now()
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from user
        where id = #{id}
    </delete>

    <insert id="insertUserRole">
        insert into user_role (user_id, role_id) values (#{userId}, #{roleId})
    </insert>

    <delete id="deleteUserRole" parameterType="java.lang.Long">
        delete from user_role
        where user_id = #{userId}
    </delete>
</mapper>

backend\src\main\resources\mapper\ViewingFeedbackMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.ViewingFeedbackDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.ViewingFeedback">
        <id column="id" property="id"/>
        <result column="appointment_id" property="appointmentId"/>
        <result column="user_id" property="userId"/>
        <result column="house_id" property="houseId"/>
        <result column="feedback_content" property="feedbackContent"/>
        <result column="satisfaction_level" property="satisfactionLevel"/>
        <result column="is_public" property="isPublic"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, appointment_id, user_id, house_id, feedback_content, satisfaction_level, is_public, 
        create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        where id = #{id}
    </select>

    <select id="selectByAppointmentId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        where appointment_id = #{appointmentId}
    </select>

    <select id="selectByUserId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        where user_id = #{userId}
        order by create_time desc
    </select>

    <select id="selectPublicByHouseId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        where house_id = #{houseId} and is_public = 1
        order by create_time desc
    </select>

    <select id="selectList" parameterType="com.rental.entity.ViewingFeedback" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        <where>
            <if test="appointmentId != null">
                and appointment_id = #{appointmentId}
            </if>
            <if test="userId != null">
                and user_id = #{userId}
            </if>
            <if test="houseId != null">
                and house_id = #{houseId}
            </if>
            <if test="isPublic != null">
                and is_public = #{isPublic}
            </if>
        </where>
        order by create_time desc
    </select>

    <insert id="insert" parameterType="com.rental.entity.ViewingFeedback" useGeneratedKeys="true" keyProperty="id">
        insert into viewing_feedback
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="appointmentId != null">appointment_id,</if>
            <if test="userId != null">user_id,</if>
            <if test="houseId != null">house_id,</if>
            <if test="feedbackContent != null">feedback_content,</if>
            <if test="satisfactionLevel != null">satisfaction_level,</if>
            <if test="isPublic != null">is_public,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="appointmentId != null">#{appointmentId},</if>
            <if test="userId != null">#{userId},</if>
            <if test="houseId != null">#{houseId},</if>
            <if test="feedbackContent != null">#{feedbackContent},</if>
            <if test="satisfactionLevel != null">#{satisfactionLevel},</if>
            <if test="isPublic != null">#{isPublic},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.ViewingFeedback">
        update viewing_feedback
        <set>
            <if test="feedbackContent != null">feedback_content = #{feedbackContent},</if>
            <if test="satisfactionLevel != null">satisfaction_level = #{satisfactionLevel},</if>
            <if test="isPublic != null">is_public = #{isPublic},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <update id="updateIsPublic">
        update viewing_feedback
        set is_public = #{isPublic}, update_time = now()
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from viewing_feedback
        where id = #{id}
    </delete>
</mapper>

backend\src\main\resources\sql\appointment_module.sql

-- 预约看房表
CREATE TABLE appointment (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '预约ID',
    house_id BIGINT NOT NULL COMMENT '房源ID',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    landlord_id BIGINT NOT NULL COMMENT '房东ID',
    appointment_time DATETIME NOT NULL COMMENT '预约时间',
    contact_name VARCHAR(50) NOT NULL COMMENT '联系人姓名',
    contact_phone VARCHAR(20) NOT NULL COMMENT '联系电话',
    appointment_notes VARCHAR(500) COMMENT '预约备注',
    status TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0-待确认,1-已确认,2-已取消,3-已完成',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    INDEX idx_house_id (house_id),
    INDEX idx_user_id (user_id),
    INDEX idx_landlord_id (landlord_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='预约看房表';

-- 看房反馈表
CREATE TABLE viewing_feedback (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '反馈ID',
    appointment_id BIGINT NOT NULL COMMENT '预约ID',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    house_id BIGINT NOT NULL COMMENT '房源ID',
    feedback_content TEXT NOT NULL COMMENT '反馈内容',
    satisfaction_level TINYINT NOT NULL COMMENT '满意度:1-5星',
    is_public TINYINT NOT NULL DEFAULT 0 COMMENT '是否公开:0-私密,1-公开',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    INDEX idx_appointment_id (appointment_id),
    INDEX idx_user_id (user_id),
    INDEX idx_house_id (house_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='看房反馈表';

-- 房源评价表
CREATE TABLE house_review (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '评价ID',
    house_id BIGINT NOT NULL COMMENT '房源ID',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    content TEXT NOT NULL COMMENT '评价内容',
    rating TINYINT NOT NULL COMMENT '评分:1-5星',
    location_rating TINYINT NOT NULL COMMENT '位置评分:1-5星',
    cleanliness_rating TINYINT NOT NULL COMMENT '清洁度评分:1-5星',
    value_rating TINYINT NOT NULL COMMENT '性价比评分:1-5星',
    landlord_rating TINYINT NOT NULL COMMENT '房东评分:1-5星',
    images VARCHAR(1000) COMMENT '评价图片,多个图片用逗号分隔',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    INDEX idx_house_id (house_id),
    INDEX idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房源评价表';

-- 评价回复表
CREATE TABLE review_reply (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '回复ID',
    review_id BIGINT NOT NULL COMMENT '评价ID',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    content TEXT NOT NULL COMMENT '回复内容',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    INDEX idx_review_id (review_id),
    INDEX idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评价回复表';

2.1 Entity Classes

backend\target\classes\application.properties

# 服务配置
server.port=8080
server.servlet.context-path=/

# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/rental_system?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456

# MyBatis配置
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.rental.entity
mybatis.configuration.map-underscore-to-camel-case=true

# 日志配置
logging.level.com.rental=debug
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n

# 文件上传配置
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
# 文件上传路径
file.upload.path=D:/rental-system/upload/
# 文件访问路径
file.access.path=/upload/

# JWT配置
jwt.secret=rental-system-secret-key
jwt.expiration=86400000

# 分页配置
pagehelper.helper-dialect=mysql
pagehelper.reasonable=true
pagehelper.support-methods-arguments=true
pagehelper.params=count=countSql

backend\target\classes\application.yml

server:
  port: 8080
  servlet:
    context-path: /api

spring:
  application:
    name: rental-system
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/rental_system?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 60000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 1 FROM DUAL
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      filters: stat,wall
  redis:
    host: localhost
    port: 6379
    database: 0
    timeout: 10000
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 20MB

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.rental.entity
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true

logging:
  level:
    com.rental: debug

# JWT配置
jwt:
  # JWT加密密钥
  secret: rental-system-secret
  # token有效时间(单位:秒)
  expiration: 86400

# 文件上传配置
file:
  upload-dir: ./uploads
  allowed-types: jpg,jpeg,png,gif
  max-size: 10485760

backend\target\classes\schema.sql

-- 创建数据库
CREATE DATABASE IF NOT EXISTS rental_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

USE rental_system;

-- 用户表
CREATE TABLE IF NOT EXISTS `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `username` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) NOT NULL COMMENT '密码',
  `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
  `phone` varchar(20) DEFAULT NULL COMMENT '手机号码',
  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
  `avatar` varchar(255) DEFAULT NULL COMMENT '头像',
  `gender` tinyint(1) DEFAULT 0 COMMENT '性别:0-未知,1-男,2-女',
  `status` tinyint(1) DEFAULT 1 COMMENT '状态:0-禁用,1-正常',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_username` (`username`),
  UNIQUE KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

-- 角色表
CREATE TABLE IF NOT EXISTS `role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID',
  `name` varchar(50) NOT NULL COMMENT '角色名称',
  `code` varchar(50) NOT NULL COMMENT '角色编码',
  `description` varchar(255) DEFAULT NULL COMMENT '角色描述',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_code` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';

-- 用户角色关联表
CREATE TABLE IF NOT EXISTS `user_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `role_id` bigint(20) NOT NULL COMMENT '角色ID',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_user_role` (`user_id`, `role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户角色关联表';

-- 学生认证表
CREATE TABLE IF NOT EXISTS `student_verification` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `student_id` varchar(50) NOT NULL COMMENT '学号',
  `school` varchar(100) NOT NULL COMMENT '学校',
  `college` varchar(100) DEFAULT NULL COMMENT '学院',
  `major` varchar(100) DEFAULT NULL COMMENT '专业',
  `id_card` varchar(50) NOT NULL COMMENT '身份证号',
  `id_card_front` varchar(255) NOT NULL COMMENT '身份证正面照',
  `id_card_back` varchar(255) NOT NULL COMMENT '身份证背面照',
  `student_card` varchar(255) NOT NULL COMMENT '学生证照片',
  `status` tinyint(1) DEFAULT 0 COMMENT '状态:0-待审核,1-已通过,2-已拒绝',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生认证表';

-- 房东认证表
CREATE TABLE IF NOT EXISTS `landlord_verification` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `real_name` varchar(50) NOT NULL COMMENT '真实姓名',
  `id_card` varchar(50) NOT NULL COMMENT '身份证号',
  `id_card_front` varchar(255) NOT NULL COMMENT '身份证正面照',
  `id_card_back` varchar(255) NOT NULL COMMENT '身份证背面照',
  `house_certificate` varchar(255) DEFAULT NULL COMMENT '房产证照片',
  `status` tinyint(1) DEFAULT 0 COMMENT '状态:0-待审核,1-已通过,2-已拒绝',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房东认证表';

-- 房源表
CREATE TABLE IF NOT EXISTS `house` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '房源ID',
  `landlord_id` bigint(20) NOT NULL COMMENT '房东ID',
  `title` varchar(100) NOT NULL COMMENT '标题',
  `price` decimal(10,2) NOT NULL COMMENT '租金',
  `area` decimal(10,2) NOT NULL COMMENT '面积',
  `house_type` varchar(50) NOT NULL COMMENT '户型',
  `floor` varchar(50) DEFAULT NULL COMMENT '楼层',
  `orientation` varchar(50) DEFAULT NULL COMMENT '朝向',
  `decoration` varchar(50) DEFAULT NULL COMMENT '装修',
  `community` varchar(100) NOT NULL COMMENT '小区名称',
  `address` varchar(255) NOT NULL COMMENT '详细地址',
  `longitude` decimal(10,6) DEFAULT NULL COMMENT '经度',
  `latitude` decimal(10,6) DEFAULT NULL COMMENT '纬度',
  `contact` varchar(50) DEFAULT NULL COMMENT '联系人',
  `contact_phone` varchar(20) DEFAULT NULL COMMENT '联系电话',
  `status` tinyint(1) DEFAULT 0 COMMENT '状态:0-待审核,1-已上架,2-已下架,3-已出租',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_landlord_id` (`landlord_id`),
  KEY `idx_community` (`community`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房源表';

-- 房源详情表
CREATE TABLE IF NOT EXISTS `house_detail` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `house_id` bigint(20) NOT NULL COMMENT '房源ID',
  `house_category` varchar(50) DEFAULT NULL COMMENT '房源类别:整租、合租',
  `rent_type` varchar(50) DEFAULT NULL COMMENT '出租方式:月付、季付、半年付、年付',
  `payment_type` varchar(50) DEFAULT NULL COMMENT '付款方式:押一付一、押一付三、押二付三等',
  `has_elevator` tinyint(1) DEFAULT NULL COMMENT '是否有电梯:0-无,1-有',
  `heating_type` varchar(50) DEFAULT NULL COMMENT '供暖方式',
  `water_fee` varchar(50) DEFAULT NULL COMMENT '水费',
  `electricity_fee` varchar(50) DEFAULT NULL COMMENT '电费',
  `gas_fee` varchar(50) DEFAULT NULL COMMENT '燃气费',
  `internet_fee` varchar(50) DEFAULT NULL COMMENT '网费',
  `property_fee` varchar(50) DEFAULT NULL COMMENT '物业费',
  `has_parking` tinyint(1) DEFAULT NULL COMMENT '是否有停车位:0-无,1-有',
  `check_in_time` date DEFAULT NULL COMMENT '入住时间',
  `min_rent_period` int(11) DEFAULT NULL COMMENT '最短租期(月)',
  `max_rent_period` int(11) DEFAULT NULL COMMENT '最长租期(月)',
  `facilities` text DEFAULT NULL COMMENT '配套设施',
  `transportation` text DEFAULT NULL COMMENT '交通情况',
  `surroundings` text DEFAULT NULL COMMENT '周边配套',
  `description` text DEFAULT NULL COMMENT '房源描述',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_house_id` (`house_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房源详情表';

-- 房源图片表
CREATE TABLE IF NOT EXISTS `house_image` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `house_id` bigint(20) NOT NULL COMMENT '房源ID',
  `url` varchar(255) NOT NULL COMMENT '图片URL',
  `is_cover` tinyint(1) DEFAULT 0 COMMENT '是否封面:0-否,1-是',
  `sort` int(11) DEFAULT 0 COMMENT '排序',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `idx_house_id` (`house_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房源图片表';

-- 收藏表
CREATE TABLE IF NOT EXISTS `favorite` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `house_id` bigint(20) NOT NULL COMMENT '房源ID',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_user_house` (`user_id`, `house_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='收藏表';

-- 浏览记录表
CREATE TABLE IF NOT EXISTS `browse_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `house_id` bigint(20) NOT NULL COMMENT '房源ID',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_house_id` (`house_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='浏览记录表';

-- 预约看房表
CREATE TABLE IF NOT EXISTS `appointment` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` bigint(20) NOT NULL COMMENT '用户ID',
  `house_id` bigint(20) NOT NULL COMMENT '房源ID',
  `landlord_id` bigint(20) NOT NULL COMMENT '房东ID',
  `appointment_time` datetime NOT NULL COMMENT '预约时间',
  `contact` varchar(50) NOT NULL COMMENT '联系人',
  `contact_phone` varchar(20) NOT NULL COMMENT '联系电话',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  `status` tinyint(1) DEFAULT 0 COMMENT '状态:0-待确认,1-已确认,2-已取消,3-已完成',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_user_id` (`user_id`),
  KEY `idx_house_id` (`house_id`),
  KEY `idx_landlord_id` (`landlord_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='预约看房表';

-- 合同表
CREATE TABLE IF NOT EXISTS `contract` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `house_id` bigint(20) NOT NULL COMMENT '房源ID',
  `landlord_id` bigint(20) NOT NULL COMMENT '房东ID',
  `tenant_id` bigint(20) NOT NULL COMMENT '租客ID',
  `contract_no` varchar(50) NOT NULL COMMENT '合同编号',
  `start_date` date NOT NULL COMMENT '开始日期',
  `end_date` date NOT NULL COMMENT '结束日期',
  `rent` decimal(10,2) NOT NULL COMMENT '租金',
  `deposit` decimal(10,2) NOT NULL COMMENT '押金',
  `payment_type` varchar(50) NOT NULL COMMENT '付款方式',
  `payment_cycle` int(11) NOT NULL COMMENT '付款周期(月)',
  `file_url` varchar(255) DEFAULT NULL COMMENT '合同文件URL',
  `status` tinyint(1) DEFAULT 0 COMMENT '状态:0-待签署,1-已签署,2-已终止',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_contract_no` (`contract_no`),
  KEY `idx_house_id` (`house_id`),
  KEY `idx_landlord_id` (`landlord_id`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='合同表';

-- 租金支付记录表
CREATE TABLE IF NOT EXISTS `rent_payment` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `contract_id` bigint(20) NOT NULL COMMENT '合同ID',
  `tenant_id` bigint(20) NOT NULL COMMENT '租客ID',
  `amount` decimal(10,2) NOT NULL COMMENT '金额',
  `payment_date` date NOT NULL COMMENT '支付日期',
  `payment_method` varchar(50) DEFAULT NULL COMMENT '支付方式',
  `status` tinyint(1) DEFAULT 0 COMMENT '状态:0-未支付,1-已支付',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_contract_id` (`contract_id`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租金支付记录表';

-- 初始化角色数据
INSERT INTO `role` (`name`, `code`, `description`) VALUES 
('管理员', 'admin', '系统管理员'),
('房东', 'landlord', '房源发布者'),
('学生', 'student', '租房学生');

-- 初始化管理员账号
INSERT INTO `user` (`username`, `password`, `real_name`, `phone`, `email`, `status`) VALUES 
('admin', 'jGl25bVBBBW96Qi9Te4V37Fnqchz/Eu4qB9vKrRIqRg=', '管理员', '13800138000', 'admin@example.com', 1);

-- 关联管理员角色
INSERT INTO `user_role` (`user_id`, `role_id`) VALUES (1, 1);

backend\target\classes\mapper\AppointmentMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.AppointmentDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.Appointment">
        <id column="id" property="id"/>
        <result column="house_id" property="houseId"/>
        <result column="user_id" property="userId"/>
        <result column="landlord_id" property="landlordId"/>
        <result column="appointment_time" property="appointmentTime"/>
        <result column="contact_name" property="contactName"/>
        <result column="contact_phone" property="contactPhone"/>
        <result column="appointment_notes" property="appointmentNotes"/>
        <result column="status" property="status"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, house_id, user_id, landlord_id, appointment_time, contact_name, contact_phone, 
        appointment_notes, status, create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from appointment
        where id = #{id}
    </select>

    <select id="selectByUserId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from appointment
        where user_id = #{userId}
        order by appointment_time desc
    </select>

    <select id="selectByLandlordId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from appointment
        where landlord_id = #{landlordId}
        order by appointment_time desc
    </select>

    <select id="selectByHouseId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from appointment
        where house_id = #{houseId}
        order by appointment_time desc
    </select>

    <select id="selectByDateRange" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from appointment
        where appointment_time between #{startDate} and #{endDate}
        order by appointment_time
    </select>

    <select id="selectList" parameterType="com.rental.entity.Appointment" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from appointment
        <where>
            <if test="houseId != null">
                and house_id = #{houseId}
            </if>
            <if test="userId != null">
                and user_id = #{userId}
            </if>
            <if test="landlordId != null">
                and landlord_id = #{landlordId}
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
        </where>
        order by appointment_time desc
    </select>

    <select id="checkTimeConflict" resultType="java.lang.Integer">
        select count(1)
        from appointment
        where house_id = #{houseId}
        and status in (0, 1)
        and appointment_time between date_sub(#{appointmentTime}, interval 1 hour) 
        and date_add(#{appointmentTime}, interval 1 hour)
    </select>

    <insert id="insert" parameterType="com.rental.entity.Appointment" useGeneratedKeys="true" keyProperty="id">
        insert into appointment
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="houseId != null">house_id,</if>
            <if test="userId != null">user_id,</if>
            <if test="landlordId != null">landlord_id,</if>
            <if test="appointmentTime != null">appointment_time,</if>
            <if test="contactName != null">contact_name,</if>
            <if test="contactPhone != null">contact_phone,</if>
            <if test="appointmentNotes != null">appointment_notes,</if>
            <if test="status != null">status,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="houseId != null">#{houseId},</if>
            <if test="userId != null">#{userId},</if>
            <if test="landlordId != null">#{landlordId},</if>
            <if test="appointmentTime != null">#{appointmentTime},</if>
            <if test="contactName != null">#{contactName},</if>
            <if test="contactPhone != null">#{contactPhone},</if>
            <if test="appointmentNotes != null">#{appointmentNotes},</if>
            <if test="status != null">#{status},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.Appointment">
        update appointment
        <set>
            <if test="appointmentTime != null">appointment_time = #{appointmentTime},</if>
            <if test="contactName != null">contact_name = #{contactName},</if>
            <if test="contactPhone != null">contact_phone = #{contactPhone},</if>
            <if test="appointmentNotes != null">appointment_notes = #{appointmentNotes},</if>
            <if test="status != null">status = #{status},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <update id="updateStatus">
        update appointment
        set status = #{status}, update_time = now()
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from appointment
        where id = #{id}
    </delete>
</mapper>

backend\target\classes\mapper\HouseDetailMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.rental.dao.HouseDetailDao">
    
    <resultMap id="BaseResultMap" type="com.rental.entity.HouseDetail">
        <id column="id" property="id" />
        <result column="house_id" property="houseId" />
        <result column="house_category" property="houseCategory" />
        <result column="rent_type" property="rentType" />
        <result column="payment_type" property="paymentType" />
        <result column="has_elevator" property="hasElevator" />
        <result column="heating_type" property="heatingType" />
        <result column="water_fee" property="waterFee" />
        <result column="electricity_fee" property="electricityFee" />
        <result column="gas_fee" property="gasFee" />
        <result column="internet_fee" property="internetFee" />
        <result column="property_fee" property="propertyFee" />
        <result column="has_parking" property="hasParking" />
        <result column="check_in_time" property="checkInTime" />
        <result column="min_rent_period" property="minRentPeriod" />
        <result column="max_rent_period" property="maxRentPeriod" />
        <result column="facilities" property="facilities" />
        <result column="transportation" property="transportation" />
        <result column="surroundings" property="surroundings" />
        <result column="description" property="description" />
    </resultMap>
    
    <sql id="Base_Column_List">
        id, house_id, house_category, rent_type, payment_type, has_elevator, heating_type, 
        water_fee, electricity_fee, gas_fee, internet_fee, property_fee, has_parking, 
        check_in_time, min_rent_period, max_rent_period, facilities, transportation, 
        surroundings, description
    </sql>
    
    <select id="selectById" resultMap="BaseResultMap">
        SELECT <include refid="Base_Column_List" />
        FROM house_detail
        WHERE id = #{id}
    </select>
    
    <select id="selectByHouseId" resultMap="BaseResultMap">
        SELECT <include refid="Base_Column_List" />
        FROM house_detail
        WHERE house_id = #{houseId}
    </select>
    
    <insert id="insert" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO house_detail (
            house_id, house_category, rent_type, payment_type, has_elevator, heating_type, 
            water_fee, electricity_fee, gas_fee, internet_fee, property_fee, has_parking, 
            check_in_time, min_rent_period, max_rent_period, facilities, transportation, 
            surroundings, description
        ) VALUES (
            #{houseId}, #{houseCategory}, #{rentType}, #{paymentType}, #{hasElevator}, #{heatingType}, 
            #{waterFee}, #{electricityFee}, #{gasFee}, #{internetFee}, #{propertyFee}, #{hasParking}, 
            #{checkInTime}, #{minRentPeriod}, #{maxRentPeriod}, #{facilities}, #{transportation}, 
            #{surroundings}, #{description}
        )
    </insert>
    
    <update id="update">
        UPDATE house_detail
        <set>
            <if test="houseCategory != null">house_category = #{houseCategory},</if>
            <if test="rentType != null">rent_type = #{rentType},</if>
            <if test="paymentType != null">payment_type = #{paymentType},</if>
            <if test="hasElevator != null">has_elevator = #{hasElevator},</if>
            <if test="heatingType != null">heating_type = #{heatingType},</if>
            <if test="waterFee != null">water_fee = #{waterFee},</if>
            <if test="electricityFee != null">electricity_fee = #{electricityFee},</if>
            <if test="gasFee != null">gas_fee = #{gasFee},</if>
            <if test="internetFee != null">internet_fee = #{internetFee},</if>
            <if test="propertyFee != null">property_fee = #{propertyFee},</if>
            <if test="hasParking != null">has_parking = #{hasParking},</if>
            <if test="checkInTime != null">check_in_time = #{checkInTime},</if>
            <if test="minRentPeriod != null">min_rent_period = #{minRentPeriod},</if>
            <if test="maxRentPeriod != null">max_rent_period = #{maxRentPeriod},</if>
            <if test="facilities != null">facilities = #{facilities},</if>
            <if test="transportation != null">transportation = #{transportation},</if>
            <if test="surroundings != null">surroundings = #{surroundings},</if>
            <if test="description != null">description = #{description},</if>
        </set>
        WHERE id = #{id}
    </update>
    
    <delete id="deleteById">
        DELETE FROM house_detail
        WHERE id = #{id}
    </delete>
    
    <delete id="deleteByHouseId">
        DELETE FROM house_detail
        WHERE house_id = #{houseId}
    </delete>
</mapper>

backend\target\classes\mapper\HouseMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.HouseDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.House">
        <id column="id" property="id"/>
        <result column="landlord_id" property="landlordId"/>
        <result column="title" property="title"/>
        <result column="price" property="price"/>
        <result column="area" property="area"/>
        <result column="house_type" property="houseType"/>
        <result column="floor" property="floor"/>
        <result column="orientation" property="orientation"/>
        <result column="decoration" property="decoration"/>
        <result column="community" property="community"/>
        <result column="address" property="address"/>
        <result column="longitude" property="longitude"/>
        <result column="latitude" property="latitude"/>
        <result column="contact" property="contact"/>
        <result column="contact_phone" property="contactPhone"/>
        <result column="status" property="status"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, landlord_id, title, price, area, house_type, floor, orientation, decoration, community, address, 
        longitude, latitude, contact, contact_phone, status, create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house
        where id = #{id}
    </select>

    <select id="selectByLandlordId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house
        where landlord_id = #{landlordId}
        order by create_time desc
    </select>

    <select id="selectList" parameterType="com.rental.entity.House" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house
        <where>
            <if test="landlordId != null">
                and landlord_id = #{landlordId}
            </if>
            <if test="title != null and title != ''">
                and title like concat('%', #{title}, '%')
            </if>
            <if test="houseType != null and houseType != ''">
                and house_type = #{houseType}
            </if>
            <if test="community != null and community != ''">
                and community like concat('%', #{community}, '%')
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
        </where>
        order by create_time desc
    </select>

    <select id="search" parameterType="java.util.Map" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house
        <where>
            <if test="keyword != null and keyword != ''">
                and (
                    title like concat('%', #{keyword}, '%')
                    or community like concat('%', #{keyword}, '%')
                    or address like concat('%', #{keyword}, '%')
                )
            </if>
            <if test="minPrice != null">
                and price >= #{minPrice}
            </if>
            <if test="maxPrice != null">
                and price <= #{maxPrice}
            </if>
            <if test="minArea != null">
                and area >= #{minArea}
            </if>
            <if test="maxArea != null">
                and area <= #{maxArea}
            </if>
            <if test="houseType != null and houseType != ''">
                and house_type = #{houseType}
            </if>
            <if test="orientation != null and orientation != ''">
                and orientation = #{orientation}
            </if>
            <if test="decoration != null and decoration != ''">
                and decoration = #{decoration}
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
            <if test="status == null">
                and status = 1
            </if>
        </where>
        order by
        <choose>
            <when test="orderBy != null and orderBy == 'price_asc'">
                price asc
            </when>
            <when test="orderBy != null and orderBy == 'price_desc'">
                price desc
            </when>
            <when test="orderBy != null and orderBy == 'area_asc'">
                area asc
            </when>
            <when test="orderBy != null and orderBy == 'area_desc'">
                area desc
            </when>
            <when test="orderBy != null and orderBy == 'time_asc'">
                create_time asc
            </when>
            <otherwise>
                create_time desc
            </otherwise>
        </choose>
    </select>

    <insert id="insert" parameterType="com.rental.entity.House" useGeneratedKeys="true" keyProperty="id">
        insert into house
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="landlordId != null">landlord_id,</if>
            <if test="title != null">title,</if>
            <if test="price != null">price,</if>
            <if test="area != null">area,</if>
            <if test="houseType != null">house_type,</if>
            <if test="floor != null">floor,</if>
            <if test="orientation != null">orientation,</if>
            <if test="decoration != null">decoration,</if>
            <if test="community != null">community,</if>
            <if test="address != null">address,</if>
            <if test="longitude != null">longitude,</if>
            <if test="latitude != null">latitude,</if>
            <if test="contact != null">contact,</if>
            <if test="contactPhone != null">contact_phone,</if>
            <if test="status != null">status,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="landlordId != null">#{landlordId},</if>
            <if test="title != null">#{title},</if>
            <if test="price != null">#{price},</if>
            <if test="area != null">#{area},</if>
            <if test="houseType != null">#{houseType},</if>
            <if test="floor != null">#{floor},</if>
            <if test="orientation != null">#{orientation},</if>
            <if test="decoration != null">#{decoration},</if>
            <if test="community != null">#{community},</if>
            <if test="address != null">#{address},</if>
            <if test="longitude != null">#{longitude},</if>
            <if test="latitude != null">#{latitude},</if>
            <if test="contact != null">#{contact},</if>
            <if test="contactPhone != null">#{contactPhone},</if>
            <if test="status != null">#{status},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.House">
        update house
        <set>
            <if test="title != null">title = #{title},</if>
            <if test="price != null">price = #{price},</if>
            <if test="area != null">area = #{area},</if>
            <if test="houseType != null">house_type = #{houseType},</if>
            <if test="floor != null">floor = #{floor},</if>
            <if test="orientation != null">orientation = #{orientation},</if>
            <if test="decoration != null">decoration = #{decoration},</if>
            <if test="community != null">community = #{community},</if>
            <if test="address != null">address = #{address},</if>
            <if test="longitude != null">longitude = #{longitude},</if>
            <if test="latitude != null">latitude = #{latitude},</if>
            <if test="contact != null">contact = #{contact},</if>
            <if test="contactPhone != null">contact_phone = #{contactPhone},</if>
            <if test="status != null">status = #{status},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <update id="updateStatus">
        update house
        set status = #{status}, update_time = now()
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from house
        where id = #{id}
    </delete>

    <delete id="deleteBatchByIds" parameterType="java.lang.Long">
        delete from house
        where id in
        <foreach collection="array" item="id" open="(" separator="," close=")">
            #{id}
        </foreach>
    </delete>
</mapper>

backend\target\classes\mapper\HouseReviewMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.HouseReviewDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.HouseReview">
        <id column="id" property="id"/>
        <result column="house_id" property="houseId"/>
        <result column="user_id" property="userId"/>
        <result column="content" property="content"/>
        <result column="rating" property="rating"/>
        <result column="location_rating" property="locationRating"/>
        <result column="cleanliness_rating" property="cleanlinessRating"/>
        <result column="value_rating" property="valueRating"/>
        <result column="landlord_rating" property="landlordRating"/>
        <result column="images" property="images"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, house_id, user_id, content, rating, location_rating, cleanliness_rating, value_rating, 
        landlord_rating, images, create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        where id = #{id}
    </select>

    <select id="selectByHouseId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        where house_id = #{houseId}
        order by create_time desc
    </select>

    <select id="selectByUserId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        where user_id = #{userId}
        order by create_time desc
    </select>

    <select id="selectByHouseIdAndUserId" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        where house_id = #{houseId} and user_id = #{userId}
        limit 1
    </select>

    <select id="selectList" parameterType="com.rental.entity.HouseReview" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from house_review
        <where>
            <if test="houseId != null">
                and house_id = #{houseId}
            </if>
            <if test="userId != null">
                and user_id = #{userId}
            </if>
            <if test="rating != null">
                and rating = #{rating}
            </if>
        </where>
        order by create_time desc
    </select>

    <select id="calculateAverageRating" parameterType="java.lang.Long" resultType="java.lang.Double">
        select avg(rating)
        from house_review
        where house_id = #{houseId}
    </select>

    <insert id="insert" parameterType="com.rental.entity.HouseReview" useGeneratedKeys="true" keyProperty="id">
        insert into house_review
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="houseId != null">house_id,</if>
            <if test="userId != null">user_id,</if>
            <if test="content != null">content,</if>
            <if test="rating != null">rating,</if>
            <if test="locationRating != null">location_rating,</if>
            <if test="cleanlinessRating != null">cleanliness_rating,</if>
            <if test="valueRating != null">value_rating,</if>
            <if test="landlordRating != null">landlord_rating,</if>
            <if test="images != null">images,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="houseId != null">#{houseId},</if>
            <if test="userId != null">#{userId},</if>
            <if test="content != null">#{content},</if>
            <if test="rating != null">#{rating},</if>
            <if test="locationRating != null">#{locationRating},</if>
            <if test="cleanlinessRating != null">#{cleanlinessRating},</if>
            <if test="valueRating != null">#{valueRating},</if>
            <if test="landlordRating != null">#{landlordRating},</if>
            <if test="images != null">#{images},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.HouseReview">
        update house_review
        <set>
            <if test="content != null">content = #{content},</if>
            <if test="rating != null">rating = #{rating},</if>
            <if test="locationRating != null">location_rating = #{locationRating},</if>
            <if test="cleanlinessRating != null">cleanliness_rating = #{cleanlinessRating},</if>
            <if test="valueRating != null">value_rating = #{valueRating},</if>
            <if test="landlordRating != null">landlord_rating = #{landlordRating},</if>
            <if test="images != null">images = #{images},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from house_review
        where id = #{id}
    </delete>
</mapper>

backend\target\classes\mapper\ReviewReplyMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.ReviewReplyDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.ReviewReply">
        <id column="id" property="id"/>
        <result column="review_id" property="reviewId"/>
        <result column="user_id" property="userId"/>
        <result column="content" property="content"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, review_id, user_id, content, create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from review_reply
        where id = #{id}
    </select>

    <select id="selectByReviewId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from review_reply
        where review_id = #{reviewId}
        order by create_time asc
    </select>

    <select id="selectByUserId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from review_reply
        where user_id = #{userId}
        order by create_time desc
    </select>

    <insert id="insert" parameterType="com.rental.entity.ReviewReply" useGeneratedKeys="true" keyProperty="id">
        insert into review_reply
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="reviewId != null">review_id,</if>
            <if test="userId != null">user_id,</if>
            <if test="content != null">content,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="reviewId != null">#{reviewId},</if>
            <if test="userId != null">#{userId},</if>
            <if test="content != null">#{content},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.ReviewReply">
        update review_reply
        <set>
            <if test="content != null">content = #{content},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from review_reply
        where id = #{id}
    </delete>
</mapper>
2.5 Service Interfaces

backend\target\classes\mapper\UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.UserDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="real_name" property="realName"/>
        <result column="phone" property="phone"/>
        <result column="email" property="email"/>
        <result column="avatar" property="avatar"/>
        <result column="gender" property="gender"/>
        <result column="status" property="status"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, username, password, real_name, phone, email, avatar, gender, status, create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from user
        where id = #{id}
    </select>

    <select id="selectByUsername" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from user
        where username = #{username}
    </select>

    <select id="selectByPhone" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from user
        where phone = #{phone}
    </select>

    <select id="selectList" parameterType="com.rental.entity.User" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from user
        <where>
            <if test="username != null and username != ''">
                and username like concat('%', #{username}, '%')
            </if>
            <if test="realName != null and realName != ''">
                and real_name like concat('%', #{realName}, '%')
            </if>
            <if test="phone != null and phone != ''">
                and phone like concat('%', #{phone}, '%')
            </if>
            <if test="email != null and email != ''">
                and email like concat('%', #{email}, '%')
            </if>
            <if test="gender != null">
                and gender = #{gender}
            </if>
            <if test="status != null">
                and status = #{status}
            </if>
        </where>
        order by create_time desc
    </select>

    <insert id="insert" parameterType="com.rental.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into user
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="username != null">username,</if>
            <if test="password != null">password,</if>
            <if test="realName != null">real_name,</if>
            <if test="phone != null">phone,</if>
            <if test="email != null">email,</if>
            <if test="avatar != null">avatar,</if>
            <if test="gender != null">gender,</if>
            <if test="status != null">status,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="username != null">#{username},</if>
            <if test="password != null">#{password},</if>
            <if test="realName != null">#{realName},</if>
            <if test="phone != null">#{phone},</if>
            <if test="email != null">#{email},</if>
            <if test="avatar != null">#{avatar},</if>
            <if test="gender != null">#{gender},</if>
            <if test="status != null">#{status},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.User">
        update user
        <set>
            <if test="username != null">username = #{username},</if>
            <if test="password != null">password = #{password},</if>
            <if test="realName != null">real_name = #{realName},</if>
            <if test="phone != null">phone = #{phone},</if>
            <if test="email != null">email = #{email},</if>
            <if test="avatar != null">avatar = #{avatar},</if>
            <if test="gender != null">gender = #{gender},</if>
            <if test="status != null">status = #{status},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <update id="updateStatus">
        update user
        set status = #{status}, update_time = now()
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from user
        where id = #{id}
    </delete>

    <insert id="insertUserRole">
        insert into user_role (user_id, role_id) values (#{userId}, #{roleId})
    </insert>

    <delete id="deleteUserRole" parameterType="java.lang.Long">
        delete from user_role
        where user_id = #{userId}
    </delete>
</mapper>

backend\target\classes\mapper\ViewingFeedbackMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.rental.dao.ViewingFeedbackDao">

    <resultMap id="BaseResultMap" type="com.rental.entity.ViewingFeedback">
        <id column="id" property="id"/>
        <result column="appointment_id" property="appointmentId"/>
        <result column="user_id" property="userId"/>
        <result column="house_id" property="houseId"/>
        <result column="feedback_content" property="feedbackContent"/>
        <result column="satisfaction_level" property="satisfactionLevel"/>
        <result column="is_public" property="isPublic"/>
        <result column="create_time" property="createTime"/>
        <result column="update_time" property="updateTime"/>
    </resultMap>

    <sql id="Base_Column_List">
        id, appointment_id, user_id, house_id, feedback_content, satisfaction_level, is_public, 
        create_time, update_time
    </sql>

    <select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        where id = #{id}
    </select>

    <select id="selectByAppointmentId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        where appointment_id = #{appointmentId}
    </select>

    <select id="selectByUserId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        where user_id = #{userId}
        order by create_time desc
    </select>

    <select id="selectPublicByHouseId" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        where house_id = #{houseId} and is_public = 1
        order by create_time desc
    </select>

    <select id="selectList" parameterType="com.rental.entity.ViewingFeedback" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from viewing_feedback
        <where>
            <if test="appointmentId != null">
                and appointment_id = #{appointmentId}
            </if>
            <if test="userId != null">
                and user_id = #{userId}
            </if>
            <if test="houseId != null">
                and house_id = #{houseId}
            </if>
            <if test="isPublic != null">
                and is_public = #{isPublic}
            </if>
        </where>
        order by create_time desc
    </select>

    <insert id="insert" parameterType="com.rental.entity.ViewingFeedback" useGeneratedKeys="true" keyProperty="id">
        insert into viewing_feedback
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="appointmentId != null">appointment_id,</if>
            <if test="userId != null">user_id,</if>
            <if test="houseId != null">house_id,</if>
            <if test="feedbackContent != null">feedback_content,</if>
            <if test="satisfactionLevel != null">satisfaction_level,</if>
            <if test="isPublic != null">is_public,</if>
            create_time,
            update_time,
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="appointmentId != null">#{appointmentId},</if>
            <if test="userId != null">#{userId},</if>
            <if test="houseId != null">#{houseId},</if>
            <if test="feedbackContent != null">#{feedbackContent},</if>
            <if test="satisfactionLevel != null">#{satisfactionLevel},</if>
            <if test="isPublic != null">#{isPublic},</if>
            now(),
            now(),
        </trim>
    </insert>

    <update id="update" parameterType="com.rental.entity.ViewingFeedback">
        update viewing_feedback
        <set>
            <if test="feedbackContent != null">feedback_content = #{feedbackContent},</if>
            <if test="satisfactionLevel != null">satisfaction_level = #{satisfactionLevel},</if>
            <if test="isPublic != null">is_public = #{isPublic},</if>
            update_time = now(),
        </set>
        where id = #{id}
    </update>

    <update id="updateIsPublic">
        update viewing_feedback
        set is_public = #{isPublic}, update_time = now()
        where id = #{id}
    </update>

    <delete id="deleteById" parameterType="java.lang.Long">
        delete from viewing_feedback
        where id = #{id}
    </delete>
</mapper>

backend\target\classes\sql\appointment_module.sql

-- 预约看房表
CREATE TABLE appointment (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '预约ID',
    house_id BIGINT NOT NULL COMMENT '房源ID',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    landlord_id BIGINT NOT NULL COMMENT '房东ID',
    appointment_time DATETIME NOT NULL COMMENT '预约时间',
    contact_name VARCHAR(50) NOT NULL COMMENT '联系人姓名',
    contact_phone VARCHAR(20) NOT NULL COMMENT '联系电话',
    appointment_notes VARCHAR(500) COMMENT '预约备注',
    status TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0-待确认,1-已确认,2-已取消,3-已完成',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    INDEX idx_house_id (house_id),
    INDEX idx_user_id (user_id),
    INDEX idx_landlord_id (landlord_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='预约看房表';

-- 看房反馈表
CREATE TABLE viewing_feedback (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '反馈ID',
    appointment_id BIGINT NOT NULL COMMENT '预约ID',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    house_id BIGINT NOT NULL COMMENT '房源ID',
    feedback_content TEXT NOT NULL COMMENT '反馈内容',
    satisfaction_level TINYINT NOT NULL COMMENT '满意度:1-5星',
    is_public TINYINT NOT NULL DEFAULT 0 COMMENT '是否公开:0-私密,1-公开',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    INDEX idx_appointment_id (appointment_id),
    INDEX idx_user_id (user_id),
    INDEX idx_house_id (house_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='看房反馈表';

-- 房源评价表
CREATE TABLE house_review (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '评价ID',
    house_id BIGINT NOT NULL COMMENT '房源ID',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    content TEXT NOT NULL COMMENT '评价内容',
    rating TINYINT NOT NULL COMMENT '评分:1-5星',
    location_rating TINYINT NOT NULL COMMENT '位置评分:1-5星',
    cleanliness_rating TINYINT NOT NULL COMMENT '清洁度评分:1-5星',
    value_rating TINYINT NOT NULL COMMENT '性价比评分:1-5星',
    landlord_rating TINYINT NOT NULL COMMENT '房东评分:1-5星',
    images VARCHAR(1000) COMMENT '评价图片,多个图片用逗号分隔',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    INDEX idx_house_id (house_id),
    INDEX idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房源评价表';

-- 评价回复表
CREATE TABLE review_reply (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '回复ID',
    review_id BIGINT NOT NULL COMMENT '评价ID',
    user_id BIGINT NOT NULL COMMENT '用户ID',
    content TEXT NOT NULL COMMENT '回复内容',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    INDEX idx_review_id (review_id),
    INDEX idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评价回复表';

2.1 Entity Classes

frontend\package.json

{
  "name": "rental-system-frontend",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "axios": "^0.21.1",
    "core-js": "^3.6.5",
    "element-ui": "^2.15.6",
    "vue": "^2.6.11",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2",
    "vue-template-compiler": "^2.6.11"
  }
}

frontend\src\App.vue

<template>
  <div id="app">
    <router-view />
  </div>
</template>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  height: 100%;
}
</style>

frontend\src\main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import './assets/css/global.css'
import axios from 'axios'

Vue.config.productionTip = false

// 使用ElementUI
Vue.use(ElementUI)

// 配置axios
axios.defaults.baseURL = 'http://localhost:8080/api'
// 请求拦截器,添加token
axios.interceptors.request.use(config => {
  const token = store.state.token
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})
// 响应拦截器,处理错误
axios.interceptors.response.use(
  response => {
    return response
  },
  error => {
    if (error.response) {
      if (error.response.status === 401) {
        // 未授权,跳转到登录页
        store.commit('CLEAR_USER_INFO')
        router.push('/login')
        ElementUI.Message.error('登录已过期,请重新登录')
      } else {
        // 其他错误
        ElementUI.Message.error(error.response.data.message || '服务器错误')
      }
    } else {
      ElementUI.Message.error('网络错误,请稍后再试')
    }
    return Promise.reject(error)
  }
)
Vue.prototype.$http = axios

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

frontend\src\api\appointment.js

import request from '@/utils/request';

/**
 * 预约相关API
 */
export default {
  /**
   * 创建预约
   * @param {Object} data 预约信息
   */
  createAppointment(data) {
    return request({
      url: '/appointment/create',
      method: 'post',
      data
    });
  },

  /**
   * 确认预约
   * @param {Number} id 预约ID
   */
  confirmAppointment(id) {
    return request({
      url: `/appointment/confirm/${id}`,
      method: 'post'
    });
  },

  /**
   * 取消预约
   * @param {Number} id 预约ID
   */
  cancelAppointment(id) {
    return request({
      url: `/appointment/cancel/${id}`,
      method: 'post'
    });
  },

  /**
   * 完成预约
   * @param {Number} id 预约ID
   */
  completeAppointment(id) {
    return request({
      url: `/appointment/complete/${id}`,
      method: 'post'
    });
  },

  /**
   * 获取预约详情
   * @param {Number} id 预约ID
   */
  getAppointmentDetail(id) {
    return request({
      url: `/appointment/detail/${id}`,
      method: 'get'
    });
  },

  /**
   * 获取用户的预约列表
   */
  getUserAppointments() {
    return request({
      url: '/appointment/user',
      method: 'get'
    });
  },

  /**
   * 获取房东的预约列表
   */
  getLandlordAppointments() {
    return request({
      url: '/appointment/landlord',
      method: 'get'
    });
  },

  /**
   * 获取房源的预约列表
   * @param {Number} houseId 房源ID
   */
  getHouseAppointments(houseId) {
    return request({
      url: `/appointment/house/${houseId}`,
      method: 'get'
    });
  },

  /**
   * 获取指定日期范围内的预约
   * @param {Date} startDate 开始日期
   * @param {Date} endDate 结束日期
   */
  getAppointmentsByDateRange(startDate, endDate) {
    return request({
      url: '/appointment/date-range',
      method: 'get',
      params: {
        startDate,
        endDate
      }
    });
  },

  /**
   * 获取预约统计数据
   */
  getAppointmentStatistics() {
    return request({
      url: '/appointment/statistics',
      method: 'get'
    });
  }
};

frontend\src\api\favorite.js

import request from '@/utils/request';

// 添加收藏
export function addFavorite(houseId) {
  return request({
    url: '/favorite/add',
    method: 'post',
    data: {
      houseId
    }
  });
}

// 取消收藏
export function cancelFavorite(houseId) {
  return request({
    url: '/favorite/cancel',
    method: 'delete',
    params: {
      houseId
    }
  });
}

// 获取收藏列表
export function getFavoriteList() {
  return request({
    url: '/favorite/list',
    method: 'get'
  });
}

// 检查是否已收藏
export function checkFavorite(houseId) {
  return request({
    url: '/favorite/check',
    method: 'get',
    params: {
      houseId
    }
  });
}

frontend\src\api\feedback.js

import request from '@/utils/request';

/**
 * 看房反馈相关API
 */
export default {
  /**
   * 提交看房反馈
   * @param {Object} data 反馈信息
   */
  submitFeedback(data) {
    return request({
      url: '/feedback/submit',
      method: 'post',
      data
    });
  },

  /**
   * 更新看房反馈
   * @param {Number} id 反馈ID
   * @param {Object} data 反馈信息
   */
  updateFeedback(id, data) {
    return request({
      url: `/feedback/update/${id}`,
      method: 'put',
      data
    });
  },

  /**
   * 删除看房反馈
   * @param {Number} id 反馈ID
   */
  deleteFeedback(id) {
    return request({
      url: `/feedback/delete/${id}`,
      method: 'delete'
    });
  },

  /**
   * 设置反馈是否公开
   * @param {Number} id 反馈ID
   * @param {Number} isPublic 是否公开:0-私密,1-公开
   */
  setFeedbackPublic(id, isPublic) {
    return request({
      url: `/feedback/public/${id}`,
      method: 'put',
      params: {
        isPublic
      }
    });
  },

  /**
   * 获取反馈详情
   * @param {Number} id 反馈ID
   */
  getFeedbackDetail(id) {
    return request({
      url: `/feedback/detail/${id}`,
      method: 'get'
    });
  },

  /**
   * 获取用户的反馈列表
   */
  getUserFeedbacks() {
    return request({
      url: '/feedback/user',
      method: 'get'
    });
  },

  /**
   * 获取房源的公开反馈列表
   * @param {Number} houseId 房源ID
   */
  getHousePublicFeedbacks(houseId) {
    return request({
      url: `/feedback/house/${houseId}`,
      method: 'get'
    });
  },

  /**
   * 获取预约的反馈
   * @param {Number} appointmentId 预约ID
   */
  getAppointmentFeedback(appointmentId) {
    return request({
      url: `/feedback/appointment/${appointmentId}`,
      method: 'get'
    });
  }
};

frontend\src\api\house.js

import request from '@/utils/request';

// 获取房源列表
export function getHouseList(params) {
  return request({
    url: '/house/list',
    method: 'get',
    params
  });
}

// 搜索房源
export function searchHouse(params) {
  return request({
    url: '/house/search',
    method: 'get',
    params
  });
}

// 获取房源详情
export function getHouseDetail(id) {
  return request({
    url: `/house/detail/${id}`,
    method: 'get'
  });
}

// 获取房源图片
export function getHouseImages(houseId) {
  return request({
    url: `/house/images/${houseId}`,
    method: 'get'
  });
}

// 添加房源
export function addHouse(data) {
  return request({
    url: '/house/add',
    method: 'post',
    data
  });
}

// 更新房源
export function updateHouse(data) {
  return request({
    url: '/house/update',
    method: 'put',
    data
  });
}

// 删除房源
export function deleteHouse(id) {
  return request({
    url: `/house/delete/${id}`,
    method: 'delete'
  });
}

// 上传房源图片
export function uploadHouseImage(data) {
  return request({
    url: '/house/upload',
    method: 'post',
    data,
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  });
}

// 设置封面图片
export function setCoverImage(houseId, imageId) {
  return request({
    url: '/house/setCover',
    method: 'put',
    data: {
      houseId,
      imageId
    }
  });
}

// 获取房东的房源列表
export function getLandlordHouses() {
  return request({
    url: '/house/landlord',
    method: 'get'
  });
}

frontend\src\api\review.js

import request from '@/utils/request';

/**
 * 房源评价相关API
 */
export default {
  /**
   * 提交房源评价
   * @param {Object} data 评价信息
   */
  submitReview(data) {
    return request({
      url: '/review/submit',
      method: 'post',
      data
    });
  },

  /**
   * 更新房源评价
   * @param {Number} id 评价ID
   * @param {Object} data 评价信息
   */
  updateReview(id, data) {
    return request({
      url: `/review/update/${id}`,
      method: 'put',
      data
    });
  },

  /**
   * 删除房源评价
   * @param {Number} id 评价ID
   */
  deleteReview(id) {
    return request({
      url: `/review/delete/${id}`,
      method: 'delete'
    });
  },

  /**
   * 获取评价详情
   * @param {Number} id 评价ID
   */
  getReviewDetail(id) {
    return request({
      url: `/review/detail/${id}`,
      method: 'get'
    });
  },

  /**
   * 获取房源的评价列表
   * @param {Number} houseId 房源ID
   */
  getHouseReviews(houseId) {
    return request({
      url: `/review/house/${houseId}`,
      method: 'get'
    });
  },

  /**
   * 获取用户的评价列表
   */
  getUserReviews() {
    return request({
      url: '/review/user',
      method: 'get'
    });
  },

  /**
   * 获取房源评价统计数据
   * @param {Number} houseId 房源ID
   */
  getHouseReviewStatistics(houseId) {
    return request({
      url: `/review/statistics/${houseId}`,
      method: 'get'
    });
  },

  /**
   * 检查用户是否已评价房源
   * @param {Number} houseId 房源ID
   */
  checkUserReviewed(houseId) {
    return request({
      url: `/review/check/${houseId}`,
      method: 'get'
    });
  },

  /**
   * 提交评价回复
   * @param {Object} data 回复信息
   */
  submitReply(data) {
    return request({
      url: '/reply/submit',
      method: 'post',
      data
    });
  },

  /**
   * 更新评价回复
   * @param {Number} id 回复ID
   * @param {Object} data 回复信息
   */
  updateReply(id, data) {
    return request({
      url: `/reply/update/${id}`,
      method: 'put',
      data
    });
  },

  /**
   * 删除评价回复
   * @param {Number} id 回复ID
   */
  deleteReply(id) {
    return request({
      url: `/reply/delete/${id}`,
      method: 'delete'
    });
  },

  /**
   * 获取回复详情
   * @param {Number} id 回复ID
   */
  getReplyDetail(id) {
    return request({
      url: `/reply/detail/${id}`,
      method: 'get'
    });
  },

  /**
   * 获取评价的回复列表
   * @param {Number} reviewId 评价ID
   */
  getReviewReplies(reviewId) {
    return request({
      url: `/reply/review/${reviewId}`,
      method: 'get'
    });
  }
};
3.2 Vue Components

frontend\src\api\user.js

import request from '@/utils/request';

// 用户登录
export function login(data) {
  return request({
    url: '/user/login',
    method: 'post',
    data
  });
}

// 用户注册
export function register(data) {
  return request({
    url: '/user/register',
    method: 'post',
    data
  });
}

// 获取用户信息
export function getUserInfo() {
  return request({
    url: '/user/info',
    method: 'get'
  });
}

// 更新用户信息
export function updateUserInfo(data) {
  return request({
    url: '/user/update',
    method: 'put',
    data
  });
}

// 修改密码
export function changePassword(data) {
  return request({
    url: '/user/changePassword',
    method: 'put',
    data
  });
}

// 上传头像
export function uploadAvatar(data) {
  return request({
    url: '/user/avatar',
    method: 'post',
    data,
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  });
}

frontend\src\api\verification.js

import request from '@/utils/request';

// 提交学生认证
export function submitStudentVerification(data) {
  return request({
    url: '/verification/student/submit',
    method: 'post',
    data,
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  });
}

// 提交房东认证
export function submitLandlordVerification(data) {
  return request({
    url: '/verification/landlord/submit',
    method: 'post',
    data,
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  });
}

// 获取学生认证状态
export function getStudentVerificationStatus() {
  return request({
    url: '/verification/student/status',
    method: 'get'
  });
}

// 获取房东认证状态
export function getLandlordVerificationStatus() {
  return request({
    url: '/verification/landlord/status',
    method: 'get'
  });
}

// 获取学生认证列表(管理员)
export function getStudentVerificationList(params) {
  return request({
    url: '/verification/student/list',
    method: 'get',
    params
  });
}

// 获取房东认证列表(管理员)
export function getLandlordVerificationList(params) {
  return request({
    url: '/verification/landlord/list',
    method: 'get',
    params
  });
}

// 审核学生认证(管理员)
export function reviewStudentVerification(data) {
  return request({
    url: '/verification/student/review',
    method: 'put',
    data
  });
}

// 审核房东认证(管理员)
export function reviewLandlordVerification(data) {
  return request({
    url: '/verification/landlord/review',
    method: 'put',
    data
  });
}
User Profile Component

frontend\src\assets\css\global.css

/* 全局样式 */
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}

body {
  font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
  background-color: #f5f5f5;
}

/* 清除浮动 */
.clearfix:after {
  content: "";
  display: block;
  clear: both;
}

/* 页面容器 */
.container {
  width: 1200px;
  margin: 0 auto;
}

/* 页面内容区域 */
.content {
  padding: 20px;
  background-color: #fff;
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}

/* 页面标题 */
.page-title {
  font-size: 20px;
  font-weight: bold;
  margin-bottom: 20px;
  padding-bottom: 10px;
  border-bottom: 1px solid #eee;
}

/* 表单项间距 */
.el-form-item {
  margin-bottom: 20px;
}

/* 按钮组 */
.button-group {
  margin-top: 20px;
  text-align: center;
}

/* 卡片样式 */
.card {
  background-color: #fff;
  border-radius: 4px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
  margin-bottom: 20px;
  overflow: hidden;
}

.card-header {
  padding: 15px;
  border-bottom: 1px solid #eee;
  font-weight: bold;
}

.card-body {
  padding: 15px;
}

/* 价格文本 */
.price {
  color: #ff6700;
  font-weight: bold;
}

/* 标签样式 */
.tag {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 2px;
  font-size: 12px;
  margin-right: 5px;
}

.tag-primary {
  background-color: #409EFF;
  color: #fff;
}

.tag-success {
  background-color: #67C23A;
  color: #fff;
}

.tag-warning {
  background-color: #E6A23C;
  color: #fff;
}

.tag-danger {
  background-color: #F56C6C;
  color: #fff;
}

/* 图片容器 */
.image-container {
  position: relative;
  overflow: hidden;
}

.image-container img {
  width: 100%;
  transition: transform 0.3s;
}

.image-container:hover img {
  transform: scale(1.05);
}

/* 分页容器 */
.pagination-container {
  text-align: center;
  margin-top: 20px;
}

frontend\src\router\index.js

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天天进步2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值