no message

This commit is contained in:
2026-04-14 12:16:53 +08:00
parent 8a667533c1
commit fc8f9b61cc
147 changed files with 0 additions and 12710 deletions

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jre-1.8">
<attributes>
<attribute name="owner.project.facets" value="java"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v9.0">
<attributes>
<attribute name="owner.project.facets" value="jst.web"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/>
<classpathentry kind="output" path="build/classes"/>
</classpath>

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>HRS</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="**/bower_components/*|**/node_modules/*|**/*.min.js" kind="src" path="WebContent"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
<attributes>
<attribute name="hide" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
<classpathentry kind="output" path=""/>
</classpath>

View File

@@ -1,7 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="HRS">
<wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
<property name="context-root" value="HRS"/>
<property name="java-output-path" value="/HRS/build/classes"/>
</wb-module>
</project-modules>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<runtime name="Apache Tomcat v9.0"/>
<fixed facet="wst.jsdt.web"/>
<fixed facet="java"/>
<fixed facet="jst.web"/>
<installed facet="java" version="1.8"/>
<installed facet="jst.web" version="4.0"/>
<installed facet="wst.jsdt.web" version="1.0"/>
</faceted-project>

View File

@@ -1 +0,0 @@
org.eclipse.wst.jsdt.launching.baseBrowserLibrary

View File

@@ -1 +0,0 @@
Window

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jre-1.8">
<attributes>
<attribute name="owner.project.facets" value="java"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v9.0">
<attributes>
<attribute name="owner.project.facets" value="jst.web"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
<classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/>
<classpathentry kind="output" path="build/classes"/>
</classpath>

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>HRS</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.common.project.facet.core.builder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>

View File

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="**/bower_components/*|**/node_modules/*|**/*.min.js" kind="src" path="WebContent"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
<attributes>
<attribute name="hide" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
<classpathentry kind="output" path=""/>
</classpath>

View File

@@ -1,7 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="HRS">
<wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
<property name="context-root" value="HRS"/>
<property name="java-output-path" value="/HRS/build/classes"/>
</wb-module>
</project-modules>

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<runtime name="Apache Tomcat v9.0"/>
<fixed facet="wst.jsdt.web"/>
<fixed facet="java"/>
<fixed facet="jst.web"/>
<installed facet="java" version="1.8"/>
<installed facet="jst.web" version="4.0"/>
<installed facet="wst.jsdt.web" version="1.0"/>
</faceted-project>

View File

@@ -1 +0,0 @@
org.eclipse.wst.jsdt.launching.baseBrowserLibrary

View File

@@ -1,3 +0,0 @@
Manifest-Version: 1.0
Class-Path:

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>HRS</display-name>
<!-- 欢迎页面 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.hrs.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

View File

@@ -1,20 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.User" %>
<%
User loginUser = (User)session.getAttribute("loginUser");
if(loginUser == null || !"2".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>管理员首页</title>
</head>
<body>
<h1>欢迎管理员:<%= loginUser.getRealName() %></h1>
<a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
</body>
</html>

View File

@@ -1,20 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.User" %>
<%
User loginUser = (User)session.getAttribute("loginUser");
if(loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>房东首页</title>
</head>
<body>
<h1>欢迎房东:<%= loginUser.getRealName() %></h1>
<a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
</body>
</html>

View File

@@ -1,324 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.Reservation, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Reservation> reservationList = (List<Reservation>) request.getAttribute("reservationList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>预约管理 - 房东后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #ff9800;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #fb8c00;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.reservation-table {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.status-confirmed {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-cancelled {
background-color: #ffebee;
color: #f44336;
}
.btn {
padding: 5px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
margin: 0 3px;
}
.btn-success {
background-color: #4CAF50;
color: white;
}
.btn-success:hover {
background-color: #45a049;
}
.btn-danger {
background-color: #f44336;
color: white;
}
.btn-danger:hover {
background-color: #d32f2f;
}
.btn-disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 房东后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/landlord/house/list">房源管理</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/list" class="active">预约管理</a>
<a href="${pageContext.request.contextPath}/landlord/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/landlord/house/list">首页</a> &gt;
<span>预约管理</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📋 共有 <strong><%= totalCount %></strong> 条预约记录
</div>
<div class="reservation-table">
<%
if (reservationList != null && !reservationList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>租客信息</th>
<th>房源信息</th>
<th>预约时间</th>
<th>状态</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
for (Reservation reservation : reservationList) {
House house = reservation.getHouse();
User tenant = reservation.getTenant();
%>
<tr>
<td>
<%= tenant.getRealName() != null ? tenant.getRealName() : tenant.getUserName() %><br>
<span style="font-size:12px;color:#999;"><%= tenant.getPhone() %></span>
</td>
<td>
<strong><%= house.getTitle() %></strong><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %></span>
</td>
<td><%= sdf.format(reservation.getReserveTime()) %></td>
<td>
<span class="status-badge status-<%= reservation.getStatus().equals("0") ? "pending" : "confirmed" %>">
<%= reservation.getStatusText() %>
</span>
</td>
<td style="max-width:150px;">
<%= reservation.getRemark() != null ? reservation.getRemark() : "-" %>
</td>
<td>
<% if ("0".equals(reservation.getStatus())) { %>
<a href="${pageContext.request.contextPath}/landlord/reservation/handle?id=<%= reservation.getId() %>&action=confirm"
class="btn btn-success">确认</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/handle?id=<%= reservation.getId() %>&action=reject"
class="btn btn-danger">拒绝</a>
<% } else { %>
<span class="btn-disabled">已处理</span>
<% } %>
</td>
</tr>
<%
}
%>
</tbody>
</table>
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">📅</div>
<p>暂无预约记录</p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -1,418 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, com.hrs.model.entity.Collection, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Collection> collectionList = (List<Collection>) request.getAttribute("collectionList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>我的收藏 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.collection-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.collection-card {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s;
position: relative;
}
.collection-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.house-image {
height: 200px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
}
.house-image .no-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 14px;
}
.house-info {
padding: 15px;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.house-title a {
color: #333;
text-decoration: none;
}
.house-title a:hover {
color: #4CAF50;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 8px;
}
.house-price {
color: #f44336;
font-size: 20px;
font-weight: bold;
margin: 10px 0;
}
.house-price span {
font-size: 14px;
font-weight: normal;
}
.collect-time {
font-size: 12px;
color: #999;
margin-top: 10px;
}
.delete-btn {
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(0,0,0,0.6);
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 18px;
transition: background-color 0.3s;
z-index: 10;
}
.delete-btn:hover {
background-color: #f44336;
}
.empty-state {
text-align: center;
padding: 80px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 20px;
font-size: 16px;
}
.empty-state a {
color: #4CAF50;
text-decoration: none;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.toast {
position: fixed;
top: 80px;
right: 20px;
padding: 12px 24px;
border-radius: 4px;
color: white;
z-index: 1000;
animation: slideIn 0.3s ease;
}
.toast-success {
background-color: #4CAF50;
}
.toast-error {
background-color: #f44336;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list" class="active">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span>我的收藏</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📚 共收藏 <strong id="totalCount"><%= totalCount %></strong> 套房源
</div>
<div class="collection-list" id="collectionList">
<%
if (collectionList != null && !collectionList.isEmpty()) {
for (Collection collection : collectionList) {
House house = collection.getHouse();
if (house != null) {
%>
<div class="collection-card" data-id="<%= collection.getId() %>">
<div class="house-image" style="background-image: url('<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
<button class="delete-btn" onclick="deleteCollection(<%= collection.getId() %>)" title="取消收藏">×</button>
<div class="house-info">
<div class="house-title">
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">
<%= house.getTitle() %>
</a>
</div>
<div class="house-detail">
📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %>
</div>
<div class="house-price">
¥<%= house.getRentPrice() %><span>/月</span>
</div>
<div class="collect-time">
收藏时间:<%= collection.getCollectTime() %>
</div>
</div>
</div>
<%
}
}
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">❤️</div>
<p>还没有收藏任何房源</p>
<p><a href="${pageContext.request.contextPath}/user/house/list">去浏览房源 &gt;&gt;</a></p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// AJAX删除收藏
function deleteCollection(id) {
if (confirm('确定要取消收藏这套房源吗?')) {
// 发送AJAX请求
var xhr = new XMLHttpRequest();
xhr.open('GET', '${pageContext.request.contextPath}/user/collection/delete?id=' + id + '&ajax=true&from=list', true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = JSON.parse(xhr.responseText);
if (result.success) {
// 移除DOM元素
var card = document.querySelector('.collection-card[data-id="' + id + '"]');
if (card) {
card.remove();
}
// 更新计数
var totalSpan = document.getElementById('totalCount');
var currentCount = parseInt(totalSpan.innerText);
totalSpan.innerText = currentCount - 1;
// 显示成功提示
showToast('取消收藏成功', 'success');
// 检查是否还有收藏
var remainingCards = document.querySelectorAll('.collection-card');
if (remainingCards.length == 0) {
location.reload(); // 刷新页面显示空状态
}
} else {
showToast(result.msg || '取消收藏失败', 'error');
}
}
};
xhr.send();
}
}
// 显示提示消息
function showToast(msg, type) {
var toast = document.createElement('div');
toast.className = 'toast toast-' + type;
toast.innerHTML = msg;
document.body.appendChild(toast);
setTimeout(function() {
toast.remove();
}, 2000);
}
</script>
</body>
</html>

View File

@@ -1,597 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
House house = (House) request.getAttribute("house");
if (house == null) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><%= house.getTitle() %> - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
/* 头部导航 */
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
/* 面包屑导航 */
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.breadcrumb a:hover {
text-decoration: underline;
}
/* 主要内容区 */
.main-content {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
/* 图片区域 */
.house-images {
padding: 20px;
border-bottom: 1px solid #eee;
}
.main-image {
width: 100%;
height: 400px;
background-color: #f0f0f0;
background-size: cover;
background-position: center;
border-radius: 8px;
margin-bottom: 15px;
}
.thumb-images {
display: flex;
gap: 10px;
overflow-x: auto;
}
.thumb {
width: 80px;
height: 60px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
border-radius: 4px;
cursor: pointer;
border: 2px solid transparent;
}
.thumb:hover {
border-color: #4CAF50;
}
.thumb.active {
border-color: #4CAF50;
}
.no-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 24px;
}
/* 房源信息 */
.house-info {
padding: 20px;
}
.house-title {
font-size: 28px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
}
.price-box {
background-color: #fff3e0;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.price {
font-size: 32px;
color: #f44336;
font-weight: bold;
}
.price span {
font-size: 16px;
font-weight: normal;
}
.info-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 20px;
padding: 15px 0;
border-bottom: 1px solid #eee;
}
.info-item {
text-align: center;
}
.info-label {
font-size: 12px;
color: #999;
margin-bottom: 5px;
}
.info-value {
font-size: 18px;
font-weight: bold;
color: #333;
}
.section {
margin-bottom: 25px;
}
.section-title {
font-size: 18px;
font-weight: bold;
padding-bottom: 10px;
margin-bottom: 15px;
border-bottom: 2px solid #4CAF50;
}
.facility-list {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.facility-item {
background-color: #f5f5f5;
padding: 5px 15px;
border-radius: 20px;
font-size: 14px;
color: #666;
}
.description {
line-height: 1.8;
color: #666;
background-color: #f9f9f9;
padding: 15px;
border-radius: 8px;
}
/* 房东信息 */
.landlord-card {
background-color: #f9f9f9;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.landlord-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.landlord-info {
display: flex;
align-items: center;
gap: 20px;
}
.landlord-avatar {
width: 60px;
height: 60px;
background-color: #4CAF50;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
}
.landlord-details p {
margin: 5px 0;
color: #666;
}
/* 操作按钮 */
.action-buttons {
display: flex;
gap: 15px;
margin-top: 20px;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
text-decoration: none;
display: inline-block;
text-align: center;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-secondary {
background-color: #ff9800;
color: white;
}
.btn-secondary:hover {
background-color: #fb8c00;
}
.btn-outline {
background-color: transparent;
border: 1px solid #4CAF50;
color: #4CAF50;
}
.btn-outline:hover {
background-color: #4CAF50;
color: white;
}
/* 页脚 */
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
/* 消息提示 */
.message {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
border-radius: 4px;
color: white;
z-index: 1000;
animation: slideIn 0.3s ease;
}
.message-success {
background-color: #4CAF50;
}
.message-error {
background-color: #f44336;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<!-- 头部导航 -->
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<!-- 面包屑导航 -->
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span><%= house.getTitle() %></span>
</div>
<div class="main-content">
<!-- 图片区域 -->
<div class="house-images">
<div class="main-image" id="mainImage" style="background-image: url('<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
<div class="thumb-images" id="thumbImages">
<%
String[] images = null;
if (house.getImgUrl() != null && !house.getImgUrl().isEmpty()) {
images = house.getImgUrl().split(",");
for (int i = 0; i < images.length; i++) {
%>
<div class="thumb" style="background-image: url('<%= images[i] %>');" onclick="changeImage('<%= images[i] %>', this)"></div>
<%
}
}
%>
</div>
</div>
<!-- 房源信息 -->
<div class="house-info">
<div class="house-title"><%= house.getTitle() %></div>
<div class="price-box">
<span class="price">¥<%= house.getRentPrice() %><span>/月</span></span>
</div>
<div class="info-grid">
<div class="info-item">
<div class="info-label">户型</div>
<div class="info-value"><%= house.getHouseType() != null ? house.getHouseType() : "待定" %></div>
</div>
<div class="info-item">
<div class="info-label">面积</div>
<div class="info-value"><%= house.getArea() != null ? house.getArea() : "待定" %></div>
</div>
<div class="info-item">
<div class="info-label">租赁类型</div>
<div class="info-value"><%= house.getRentTypeText() %></div>
</div>
<div class="info-item">
<div class="info-label">房源编号</div>
<div class="info-value"><%= house.getHouseNo() %></div>
</div>
</div>
<div class="section">
<div class="section-title">📍 房源地址</div>
<p style="color: #666;"><%= house.getAddress() != null ? house.getAddress() : "暂无地址信息" %></p>
</div>
<div class="section">
<div class="section-title">🛋️ 配套设施</div>
<div class="facility-list">
<%
String facility = house.getFacility();
if (facility != null && !facility.isEmpty()) {
String[] facilities = facility.split(",");
for (String f : facilities) {
%>
<span class="facility-item"><%= f.trim() %></span>
<%
}
} else {
%>
<span class="facility-item">暂无设施信息</span>
<%
}
%>
</div>
</div>
<div class="section">
<div class="section-title">📝 房源描述</div>
<div class="description">
<%= house.getDescription() != null ? house.getDescription() : "暂无描述信息" %>
</div>
</div>
</div>
</div>
<!-- 房东信息 -->
<div class="landlord-card">
<div class="landlord-title">👤 房东信息</div>
<div class="landlord-info">
<div class="landlord-avatar">👨</div>
<div class="landlord-details">
<p><strong><%= house.getLandlordName() != null ? house.getLandlordName() : "房东" %></strong></p>
<p>📞 <%= house.getLandlordPhone() != null ? house.getLandlordPhone() : "暂无联系方式" %></p>
<p>⭐ 认证房东 · 已发布房源</p>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<button class="btn btn-primary" onclick="makeReservation()">📅 预约看房</button>
<%
// 检查是否已收藏
com.hrs.model.dao.CollectionDAO collectionDAO = new com.hrs.model.dao.CollectionDAO();
boolean isCollected = collectionDAO.isCollected(loginUser.getId(), house.getId());
if (isCollected) {
%>
<button class="btn btn-outline" onclick="cancelCollection(<%= house.getId() %>)">❤️ 已收藏</button>
<%
} else {
%>
<button class="btn btn-secondary" onclick="addToCollection()">❤️ 收藏房源</button>
<%
}
%>
<a href="${pageContext.request.contextPath}/user/house/list" class="btn btn-outline">🔙 返回列表</a>
</div>
</div>
<!-- 页脚 -->
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// 切换主图
function changeImage(url, element) {
document.getElementById('mainImage').style.backgroundImage = 'url(' + url + ')';
// 移除所有active类
var thumbs = document.querySelectorAll('.thumb');
thumbs.forEach(function(thumb) {
thumb.classList.remove('active');
});
// 添加active类到当前
element.classList.add('active');
}
// 预约看房
function makeReservation() {
var houseId = <%= house.getId() %>;
// 跳转到预约页面
window.location.href = '${pageContext.request.contextPath}/user/reservation/add?houseId=' + houseId;
}
// 收藏房源暂时用alert后面实现真正的收藏功能
function addToCollection() {
var houseId = <%= house.getId() %>;
// 发送AJAX请求收藏
var xhr = new XMLHttpRequest();
xhr.open('POST', '${pageContext.request.contextPath}/user/collection/add', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = xhr.responseText;
if (result == 'success') {
showMessage('收藏成功!', 'success');
// 刷新页面更新按钮状态
setTimeout(function() {
location.reload();
}, 1000);
} else if (result == 'exists') {
showMessage('您已经收藏过这套房源了', 'error');
} else {
showMessage('收藏失败,请稍后重试', 'error');
}
}
};
xhr.send('houseId=' + houseId);
}
// 取消收藏
function cancelCollection(houseId) {
if (confirm('确定要取消收藏这套房源吗?')) {
window.location.href = '${pageContext.request.contextPath}/user/collection/deleteByHouse?houseId=' + houseId;
}
}
// 显示提示消息
function showMessage(msg, type) {
var messageDiv = document.createElement('div');
messageDiv.className = 'message message-' + type;
messageDiv.innerHTML = msg;
document.body.appendChild(messageDiv);
setTimeout(function() {
messageDiv.remove();
}, 3000);
}
// 如果有缩略图,默认第一个高亮
var firstThumb = document.querySelector('.thumb');
if (firstThumb) {
firstThumb.classList.add('active');
}
</script>
</body>
</html>

View File

@@ -1,412 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<House> houseList = (List<House>) request.getAttribute("houseList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>房源列表 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
/* 头部导航 */
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
/* 搜索栏 */
.search-bar {
background-color: white;
padding: 20px 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.search-form {
display: flex;
gap: 15px;
align-items: flex-end;
flex-wrap: wrap;
}
.search-group {
display: flex;
flex-direction: column;
}
.search-group label {
font-size: 12px;
color: #666;
margin-bottom: 5px;
}
.search-group input, .search-group select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 150px;
}
.search-btn {
background-color: #4CAF50;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
cursor: pointer;
}
.search-btn:hover {
background-color: #45a049;
}
.reset-btn {
background-color: #999;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
/* 房源列表 */
.house-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.house-card {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.house-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.house-image {
height: 200px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
position: relative;
}
.house-image .no-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 14px;
}
.house-info {
padding: 15px;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
.house-title a {
color: #333;
text-decoration: none;
}
.house-title a:hover {
color: #4CAF50;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 8px;
}
.house-price {
color: #f44336;
font-size: 22px;
font-weight: bold;
margin: 10px 0;
}
.house-price span {
font-size: 14px;
font-weight: normal;
}
.house-tags {
margin: 10px 0;
}
.tag {
display: inline-block;
background-color: #f0f0f0;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
margin-right: 5px;
color: #666;
}
.house-footer {
border-top: 1px solid #eee;
padding-top: 10px;
margin-top: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.landlord-info {
font-size: 12px;
color: #999;
}
.btn {
padding: 5px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-outline {
background-color: transparent;
border: 1px solid #4CAF50;
color: #4CAF50;
}
.btn-outline:hover {
background-color: #4CAF50;
color: white;
}
/* 统计信息 */
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 10px;
}
/* 页脚 */
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<!-- 头部导航 -->
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="#">我的预约</a>
<a href="#">我的订单</a>
</div>
</div>
</div>
<!-- 搜索栏 -->
<div class="search-bar">
<div class="container">
<form class="search-form" action="${pageContext.request.contextPath}/user/house/list" method="get">
<div class="search-group">
<label>区域</label>
<select name="area">
<option value="">全部区域</option>
<option value="朝阳区" <%= "朝阳区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>朝阳区</option>
<option value="海淀区" <%= "海淀区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>海淀区</option>
<option value="东城区" <%= "东城区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>东城区</option>
<option value="西城区" <%= "西城区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>西城区</option>
<option value="丰台区" <%= "丰台区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>丰台区</option>
</select>
</div>
<div class="search-group">
<label>最低租金(元)</label>
<input type="number" name="minPrice" placeholder="不限" value="<%= request.getAttribute("minPrice") != null ? request.getAttribute("minPrice") : "" %>">
</div>
<div class="search-group">
<label>最高租金(元)</label>
<input type="number" name="maxPrice" placeholder="不限" value="<%= request.getAttribute("maxPrice") != null ? request.getAttribute("maxPrice") : "" %>">
</div>
<button type="submit" class="search-btn">搜索</button>
<a href="${pageContext.request.contextPath}/user/house/list" class="reset-btn">重置</a>
</form>
</div>
</div>
<div class="container">
<!-- 统计信息 -->
<div class="stats">
📊 共找到 <strong><%= totalCount %></strong> 套房源
</div>
<!-- 房源列表 -->
<div class="house-list">
<%
if (houseList != null && !houseList.isEmpty()) {
for (House house : houseList) {
%>
<div class="house-card">
<div class="house-image" style="background-image: url('<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
<div class="house-info">
<div class="house-title">
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">
<%= house.getTitle() %>
</a>
</div>
<div class="house-detail">
📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %>
</div>
<div class="house-price">
¥<%= house.getRentPrice() %><span>/月</span>
</div>
<div class="house-tags">
<span class="tag">#<%= house.getHouseType() %></span>
<span class="tag">#<%= house.getArea() %></span>
</div>
<div class="house-footer">
<div class="landlord-info">
👤 <%= house.getLandlordName() != null ? house.getLandlordName() : "房东" %>
</div>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="btn btn-primary">
查看详情
</a>
</div>
</div>
</div>
<%
}
} else {
%>
<div class="empty-state">
<div style="font-size: 48px;">🏠</div>
<p>暂无房源,请稍后再来</p>
</div>
<%
}
%>
</div>
</div>
<!-- 页脚 -->
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -1,5 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
// 重定向到房源列表页
response.sendRedirect(request.getContextPath() + "/user/house/list");
%>

View File

@@ -1,151 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房系统登录</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.login-container {
background-color: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
width: 360px;
}
h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #666;
font-weight: bold;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
font-size: 14px;
}
.role-group {
display: flex;
justify-content: space-around;
margin: 10px 0;
}
.role-group label {
display: inline;
font-weight: normal;
}
button {
width: 100%;
padding: 12px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #45a049;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.register-link {
text-align: center;
margin-top: 20px;
color: #666;
}
.register-link a {
color: #4CAF50;
text-decoration: none;
}
.register-link a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="login-container">
<h2>租房系统登录</h2>
<%-- 显示错误信息 --%>
<%
String error = (String)request.getAttribute("error");
if(error != null) {
%>
<div class="error"><%= error %></div>
<%
}
%>
<%-- 显示成功信息(如注册成功) --%>
<%
String message = (String)request.getAttribute("message");
if(message != null) {
%>
<div class="message" style="background-color:#e8f5e9; color:#2e7d32; padding:10px; border-radius:4px; margin-bottom:20px; text-align:center;">
<%= message %>
</div>
<%
}
%>
<form action="${pageContext.request.contextPath}/user/login" method="post">
<div class="form-group">
<label>用户名:</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" name="password" required>
</div>
<div class="form-group">
<label>登录角色:</label>
<div class="role-group">
<label>
<input type="radio" name="roleType" value="0" checked> 租客
</label>
<label>
<input type="radio" name="roleType" value="1"> 房东
</label>
<label>
<input type="radio" name="roleType" value="2"> 管理员
</label>
</div>
</div>
<button type="submit">登录</button>
</form>
<div class="register-link">
还没有账号? <a href="${pageContext.request.contextPath}/user/register">立即注册</a>
</div>
</div>
</body>
</html>

View File

@@ -1,218 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房系统注册</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
}
.register-container {
background-color: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
width: 450px;
}
h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #666;
font-weight: bold;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
font-size: 14px;
}
.role-group {
display: flex;
gap: 20px;
margin: 10px 0;
}
.role-group label {
display: inline;
font-weight: normal;
}
button {
width: 100%;
padding: 12px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #45a049;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.login-link {
text-align: center;
margin-top: 20px;
color: #666;
}
.login-link a {
color: #4CAF50;
text-decoration: none;
}
.login-link a:hover {
text-decoration: underline;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.required {
color: #f44336;
}
</style>
</head>
<body>
<div class="register-container">
<h2>租房系统注册</h2>
<%-- 显示错误信息 --%>
<%
String error = (String)request.getAttribute("error");
if(error != null) {
%>
<div class="error"><%= error %></div>
<%
}
%>
<%-- 显示成功信息 --%>
<%
String message = (String)request.getAttribute("message");
if(message != null) {
%>
<div class="message"><%= message %></div>
<%
}
%>
<form action="${pageContext.request.contextPath}/user/register" method="post">
<div class="form-group">
<label>用户名 <span class="required">*</span></label>
<input type="text" name="username" placeholder="请输入用户名" required>
</div>
<div class="form-group">
<label>密码 <span class="required">*</span></label>
<input type="password" name="password" placeholder="请输入密码" required>
<div class="hint">密码长度6-20位建议使用字母+数字组合</div>
</div>
<div class="form-group">
<label>确认密码 <span class="required">*</span></label>
<input type="password" name="confirmPassword" placeholder="请再次输入密码" required>
</div>
<div class="form-group">
<label>真实姓名</label>
<input type="text" name="realName" placeholder="请输入真实姓名">
</div>
<div class="form-group">
<label>手机号 <span class="required">*</span></label>
<input type="text" name="phone" placeholder="请输入11位手机号" required>
</div>
<div class="form-group">
<label>身份证号</label>
<input type="text" name="idCard" placeholder="请输入身份证号(可选)">
<div class="hint">房东认证需要提供身份证号</div>
</div>
<div class="form-group">
<label>注册角色 <span class="required">*</span></label>
<div class="role-group">
<label>
<input type="radio" name="roleType" value="0" checked> 租客
</label>
<label>
<input type="radio" name="roleType" value="1"> 房东
</label>
</div>
<div class="hint">房东需要管理员审核认证后才能发布房源</div>
</div>
<button type="submit">立即注册</button>
</form>
<div class="login-link">
已有账号? <a href="${pageContext.request.contextPath}/user/login">返回登录</a>
</div>
</div>
<script>
// 简单的前端验证
document.querySelector('form').addEventListener('submit', function(e) {
var password = document.querySelector('input[name="password"]').value;
var confirm = document.querySelector('input[name="confirmPassword"]').value;
var phone = document.querySelector('input[name="phone"]').value;
// 验证密码长度
if(password.length < 6 || password.length > 20) {
alert('密码长度应为6-20位');
e.preventDefault();
return false;
}
// 验证两次密码
if(password !== confirm) {
alert('两次输入的密码不一致');
e.preventDefault();
return false;
}
// 验证手机号
var phoneReg = /^1[3-9]\d{9}$/;
if(!phoneReg.test(phone)) {
alert('请输入正确的11位手机号');
e.preventDefault();
return false;
}
});
</script>
</body>
</html>

View File

@@ -1,313 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
House house = (House) request.getAttribute("house");
if (house == null) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
String error = (String) request.getAttribute("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>预约看房 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 800px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.main-card {
background-color: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.house-summary {
background-color: #f9f9f9;
padding: 15px;
border-radius: 8px;
margin-bottom: 25px;
display: flex;
gap: 15px;
}
.house-img {
width: 100px;
height: 100px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
border-radius: 8px;
}
.house-info {
flex: 1;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 5px;
}
.house-price {
color: #f44336;
font-size: 18px;
font-weight: bold;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #333;
}
input[type="date"],
input[type="time"],
textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
textarea {
resize: vertical;
min-height: 80px;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-secondary {
background-color: #999;
color: white;
margin-left: 10px;
}
.btn-secondary:hover {
background-color: #777;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">房源详情</a> &gt;
<span>预约看房</span>
</div>
<div class="main-card">
<h2 style="margin-bottom: 20px;">📅 预约看房</h2>
<% if (error != null) { %>
<div class="error"><%= error %></div>
<% } %>
<!-- 房源信息摘要 -->
<div class="house-summary">
<div class="house-img" style="background-image: url('<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;">🏠</div>
<% } %>
</div>
<div class="house-info">
<div class="house-title"><%= house.getTitle() %></div>
<div class="house-detail">📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %></div>
<div class="house-price">¥<%= house.getRentPrice() %>/月</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/user/reservation/add" method="post">
<input type="hidden" name="houseId" value="<%= house.getId() %>">
<div class="form-group">
<label>选择看房日期 *</label>
<input type="date" name="reserveDate" required min="<%= new java.text.SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date()) %>">
<div class="hint">请选择您方便的看房日期</div>
</div>
<div class="form-group">
<label>选择看房时间 *</label>
<input type="time" name="reserveTime" required>
<div class="hint">建议选择 9:00-18:00 之间的时间</div>
</div>
<div class="form-group">
<label>备注信息(可选)</label>
<textarea name="remark" placeholder="请输入您的特殊需求或问题..."></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交预约</button>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="btn btn-secondary">返回</a>
</div>
</form>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// 设置日期最小值
var dateInput = document.querySelector('input[type="date"]');
var today = new Date();
var yyyy = today.getFullYear();
var mm = String(today.getMonth() + 1).padStart(2, '0');
var dd = String(today.getDate()).padStart(2, '0');
dateInput.min = yyyy + '-' + mm + '-' + dd;
// 设置时间默认值
var timeInput = document.querySelector('input[type="time"]');
timeInput.value = "10:00";
</script>
</body>
</html>

View File

@@ -1,347 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.Reservation, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Reservation> reservationList = (List<Reservation>) request.getAttribute("reservationList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>我的预约 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.reservation-table {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.status-confirmed {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-cancelled {
background-color: #ffebee;
color: #f44336;
}
.status-completed {
background-color: #e3f2fd;
color: #2196F3;
}
.house-link {
color: #4CAF50;
text-decoration: none;
}
.house-link:hover {
text-decoration: underline;
}
.btn {
padding: 4px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
}
.btn-danger {
background-color: #f44336;
color: white;
}
.btn-danger:hover {
background-color: #d32f2f;
}
.btn-disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 20px;
}
.empty-state a {
color: #4CAF50;
text-decoration: none;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list" class="active">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span>我的预约</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📋 共有 <strong><%= totalCount %></strong> 条预约记录
</div>
<div class="reservation-table">
<%
if (reservationList != null && !reservationList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>房源信息</th>
<th>预约时间</th>
<th>状态</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
for (Reservation reservation : reservationList) {
House house = reservation.getHouse();
%>
<tr>
<td>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="house-link">
<strong><%= house.getTitle() %></strong>
</a><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %> | ¥<%= house.getRentPrice() %>/月</span>
</td>
<td><%= sdf.format(reservation.getReserveTime()) %></td>
<td>
<span class="status-badge status-<%= reservation.getStatus().equals("0") ? "pending" : reservation.getStatus().equals("1") ? "confirmed" : reservation.getStatus().equals("2") ? "cancelled" : "completed" %>">
<%= reservation.getStatusText() %>
</span>
</td>
<td style="max-width:200px; overflow:hidden; text-overflow:ellipsis;">
<%= reservation.getRemark() != null ? reservation.getRemark() : "-" %>
</td>
<td>
<% if ("0".equals(reservation.getStatus())) { %>
<a href="${pageContext.request.contextPath}/user/reservation/cancel?id=<%= reservation.getId() %>"
class="btn btn-danger"
onclick="return confirm('确定要取消这个预约吗?')">取消</a>
<% } else { %>
<span class="btn btn-disabled">已处理</span>
<% } %>
</td>
</tr>
<%
}
%>
</tbody>
</table>
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">📅</div>
<p>还没有任何预约记录</p>
<p><a href="${pageContext.request.contextPath}/user/house/list">去浏览房源 &gt;&gt;</a></p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -1,170 +0,0 @@
/*
Navicat Premium Dump SQL
Source Server : 本机
Source Server Type : MySQL
Source Server Version : 90500 (9.5.0)
Source Host : localhost:3306
Source Schema : hrs
Target Server Type : MySQL
Target Server Version : 90500 (9.5.0)
File Encoding : 65001
Date: 10/03/2026 22:26:27
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for hrs_collection
-- ----------------------------
DROP TABLE IF EXISTS `hrs_collection`;
CREATE TABLE `hrs_collection` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` bigint NOT NULL COMMENT '租客ID关联hrs_user.id',
`house_id` bigint NOT NULL COMMENT '房源ID关联hrs_house.id',
`collect_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '收藏时间',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_tenant_house`(`tenant_id` ASC, `house_id` ASC) USING BTREE COMMENT '避免重复收藏',
INDEX `idx_collect_tenant_id`(`tenant_id` ASC) USING BTREE COMMENT '租客ID索引',
INDEX `idx_collect_house_id`(`house_id` ASC) USING BTREE COMMENT '房源ID索引',
CONSTRAINT `fk_collect_house` FOREIGN KEY (`house_id`) REFERENCES `hrs_house` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `fk_collect_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `hrs_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '房源收藏表外键fk_collect_tenant关联租客、fk_collect_house关联房源' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of hrs_collection
-- ----------------------------
-- ----------------------------
-- Table structure for hrs_house
-- ----------------------------
DROP TABLE IF EXISTS `hrs_house`;
CREATE TABLE `hrs_house` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`landlord_id` bigint NOT NULL COMMENT '房东ID关联hrs_user.id',
`house_no` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '房源编号',
`title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '房源标题',
`area` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '房源区域(如朝阳区)',
`address` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '详细地址',
`house_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '户型(如一室一厅)',
`rent_price` decimal(10, 2) NOT NULL COMMENT '月租金(元)',
`rent_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '0' COMMENT '租赁类型0-整租1-合租',
`facility` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '配套设施(如空调、洗衣机)',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '房源描述',
`img_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '房源图片路径(多图逗号分隔)',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '房源状态0-待审核1-已上架2-已下架3-违规',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_house_no`(`house_no` ASC) USING BTREE COMMENT '房源编号唯一',
INDEX `idx_landlord_id`(`landlord_id` ASC) USING BTREE COMMENT '房东ID索引',
INDEX `idx_area`(`area` ASC) USING BTREE COMMENT '区域索引',
INDEX `idx_house_status`(`status` ASC) USING BTREE COMMENT '房源状态索引',
CONSTRAINT `fk_house_landlord` FOREIGN KEY (`landlord_id`) REFERENCES `hrs_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '房源信息表外键fk_house_landlord关联房东用户表hrs_user.id' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of hrs_house
-- ----------------------------
INSERT INTO `hrs_house` VALUES (1, 2, 'H20260309001', '朝阳小区一室一厅', '朝阳区', '朝阳小区1号楼1单元101', '一室一厅', 2500.00, '0', NULL, NULL, NULL, '1', NULL, '2026-03-10 15:32:31', NULL, '2026-03-10 15:32:31');
-- ----------------------------
-- Table structure for hrs_order
-- ----------------------------
DROP TABLE IF EXISTS `hrs_order`;
CREATE TABLE `hrs_order` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` bigint NOT NULL COMMENT '租客ID关联hrs_user.id',
`house_id` bigint NOT NULL COMMENT '房源ID关联hrs_house.id',
`order_no` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '订单编号',
`rent_start_time` datetime NOT NULL COMMENT '租赁开始时间',
`rent_end_time` datetime NOT NULL COMMENT '租赁结束时间',
`total_rent` decimal(10, 2) NOT NULL COMMENT '总租金(元)',
`pay_status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '支付状态0-待支付1-已支付2-已退款',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '订单状态0-待签约1-已签约2-已结束3-已取消',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_order_no`(`order_no` ASC) USING BTREE COMMENT '订单编号唯一',
INDEX `idx_order_tenant_id`(`tenant_id` ASC) USING BTREE COMMENT '租客ID索引',
INDEX `idx_order_house_id`(`house_id` ASC) USING BTREE COMMENT '房源ID索引',
INDEX `idx_pay_status`(`pay_status` ASC) USING BTREE COMMENT '支付状态索引',
INDEX `idx_order_status`(`status` ASC) USING BTREE COMMENT '订单状态索引',
CONSTRAINT `fk_order_house` FOREIGN KEY (`house_id`) REFERENCES `hrs_house` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `fk_order_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `hrs_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '租房订单表外键fk_order_tenant关联租客、fk_order_house关联房源' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of hrs_order
-- ----------------------------
-- ----------------------------
-- Table structure for hrs_reservation
-- ----------------------------
DROP TABLE IF EXISTS `hrs_reservation`;
CREATE TABLE `hrs_reservation` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`tenant_id` bigint NOT NULL COMMENT '租客ID关联hrs_user.id',
`house_id` bigint NOT NULL COMMENT '房源ID关联hrs_house.id',
`landlord_id` bigint NOT NULL COMMENT '房东ID关联hrs_user.id',
`reserve_time` datetime NOT NULL COMMENT '预约看房时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '租客备注',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '预约状态0-待确认1-已确认2-已取消3-已完成',
`handle_time` datetime NULL DEFAULT NULL COMMENT '房东处理时间',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_tenant_id`(`tenant_id` ASC) USING BTREE COMMENT '租客ID索引',
INDEX `idx_house_id`(`house_id` ASC) USING BTREE COMMENT '房源ID索引',
INDEX `idx_reserve_landlord_id`(`landlord_id` ASC) USING BTREE COMMENT '房东ID索引',
INDEX `idx_reserve_status`(`status` ASC) USING BTREE COMMENT '预约状态索引',
CONSTRAINT `fk_reserve_house` FOREIGN KEY (`house_id`) REFERENCES `hrs_house` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `fk_reserve_landlord` FOREIGN KEY (`landlord_id`) REFERENCES `hrs_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `fk_reserve_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `hrs_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '预约看房记录表外键fk_reserve_tenant关联租客、fk_reserve_house关联房源、fk_reserve_landlord关联房东' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of hrs_reservation
-- ----------------------------
-- ----------------------------
-- Table structure for hrs_user
-- ----------------------------
DROP TABLE IF EXISTS `hrs_user`;
CREATE TABLE `hrs_user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名(登录账号)',
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码(加密存储)',
`real_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '真实姓名',
`phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '手机号',
`id_card` varchar(18) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '身份证号',
`role_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色类型0-租客1-房东2-管理员',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0' COMMENT '账号状态0-未认证1-已认证2-禁用',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_user_name`(`user_name` ASC) USING BTREE COMMENT '用户名唯一',
UNIQUE INDEX `uk_phone`(`phone` ASC) USING BTREE COMMENT '手机号唯一',
INDEX `idx_role_type`(`role_type` ASC) USING BTREE COMMENT '角色类型索引'
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '基础用户信息表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of hrs_user
-- ----------------------------
INSERT INTO `hrs_user` VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', '系统管理员', '13800138000', NULL, '2', '1', NULL, '2026-03-10 15:32:31', NULL, '2026-03-10 15:32:31');
INSERT INTO `hrs_user` VALUES (2, 'landlord01', 'e10adc3949ba59abbe56e057f20f883e', '张三', '13800138001', NULL, '1', '1', NULL, '2026-03-10 15:32:31', NULL, '2026-03-10 15:32:31');
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -1,60 +0,0 @@
package com.hrs.controller.landlord;
import com.hrs.model.dao.ReservationDAO;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 房东处理预约控制器
*/
@WebServlet("/landlord/reservation/handle")
public class ReservationHandleController extends HttpServlet {
private ReservationDAO reservationDAO = new ReservationDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取登录用户
HttpSession session = request.getSession(false);
if (session == null) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
// 获取参数
String idStr = request.getParameter("id");
String action = request.getParameter("action"); // confirm 或 reject
if (idStr == null || action == null) {
response.sendRedirect(request.getContextPath() + "/landlord/reservation/list");
return;
}
Long id = Long.parseLong(idStr);
String status = "confirm".equals(action) ? "1" : "2";
boolean success = reservationDAO.updateStatus(id, status);
if (success) {
response.sendRedirect(request.getContextPath() + "/landlord/reservation/list?msg=" +
("confirm".equals(action) ? "已确认预约" : "已拒绝预约"));
} else {
response.sendRedirect(request.getContextPath() + "/landlord/reservation/list?error=操作失败");
}
}
}

View File

@@ -1,50 +0,0 @@
package com.hrs.controller.landlord;
import com.hrs.model.dao.ReservationDAO;
import com.hrs.model.entity.Reservation;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* 房东预约管理控制器
*/
@WebServlet("/landlord/reservation/list")
public class ReservationManageController extends HttpServlet {
private ReservationDAO reservationDAO = new ReservationDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取登录用户
HttpSession session = request.getSession(false);
if (session == null) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
// 查询收到的预约
List<Reservation> reservationList = reservationDAO.findByLandlordId(loginUser.getId());
request.setAttribute("reservationList", reservationList);
request.setAttribute("totalCount", reservationList.size());
request.setAttribute("loginUser", loginUser);
request.getRequestDispatcher("/jsp/landlord/reservation_list.jsp").forward(request, response);
}
}

View File

@@ -1,79 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.CollectionDAO;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 添加收藏控制器
* AJAX请求返回文本结果
*/
@WebServlet("/user/collection/add")
public class CollectionAddController extends HttpServlet {
private CollectionDAO collectionDAO = new CollectionDAO();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/plain;charset=UTF-8");
PrintWriter out = response.getWriter();
// 1. 检查登录状态
HttpSession session = request.getSession(false);
if (session == null) {
out.print("not_login");
return;
}
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
out.print("not_login");
return;
}
// 2. 获取房源ID
String houseIdStr = request.getParameter("houseId");
if (houseIdStr == null || houseIdStr.trim().isEmpty()) {
out.print("error");
return;
}
Long houseId = null;
try {
houseId = Long.parseLong(houseIdStr);
} catch (NumberFormatException e) {
out.print("error");
return;
}
// 3. 检查是否已收藏
boolean exists = collectionDAO.isCollected(loginUser.getId(), houseId);
if (exists) {
out.print("exists");
return;
}
// 4. 添加收藏
com.hrs.model.entity.Collection collection = new com.hrs.model.entity.Collection();
collection.setTenantId(loginUser.getId());
collection.setHouseId(houseId);
boolean success = collectionDAO.add(collection);
if (success) {
out.print("success");
} else {
out.print("error");
}
}
}

View File

@@ -1,111 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.CollectionDAO;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 取消收藏控制器
* 支持两种删除方式:
* 1. AJAX删除收藏列表页- 返回JSON
* 2. 页面跳转删除(详情页)- 重定向
*/
@WebServlet("/user/collection/delete")
public class CollectionDeleteController extends HttpServlet {
private CollectionDAO collectionDAO = new CollectionDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取来源参数
String from = request.getParameter("from");
String idStr = request.getParameter("id");
String houseIdStr = request.getParameter("houseId");
// 判断是否是AJAX请求
String ajax = request.getParameter("ajax");
// 1. 获取登录用户
HttpSession session = request.getSession(false);
if (session == null) {
if ("true".equals(ajax)) {
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("{\"success\":false,\"msg\":\"未登录\"}");
return;
} else {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
}
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
if ("true".equals(ajax)) {
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.print("{\"success\":false,\"msg\":\"未登录\"}");
return;
} else {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
}
boolean success = false;
// 2. 根据ID删除从收藏列表删除
if (idStr != null && !idStr.trim().isEmpty()) {
Long id = Long.parseLong(idStr);
success = collectionDAO.deleteById(id);
}
// 3. 根据房源ID删除从详情页删除
else if (houseIdStr != null && !houseIdStr.trim().isEmpty()) {
Long houseId = Long.parseLong(houseIdStr);
success = collectionDAO.delete(loginUser.getId(), houseId);
}
// 4. 处理返回结果
if ("true".equals(ajax)) {
// AJAX请求返回JSON
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
if (success) {
out.print("{\"success\":true,\"msg\":\"取消收藏成功\"}");
} else {
out.print("{\"success\":false,\"msg\":\"取消收藏失败\"}");
}
} else {
// 普通请求:页面跳转
if (success) {
// 根据来源参数决定跳转位置
if ("detail".equals(from) && houseIdStr != null) {
// 从详情页删除,跳回详情页
response.sendRedirect(request.getContextPath() + "/user/house/detail?id=" + houseIdStr + "&msg=取消收藏成功");
} else if ("list".equals(from)) {
// 从收藏列表删除,跳回收藏列表
response.sendRedirect(request.getContextPath() + "/user/collection/list?msg=取消收藏成功");
} else {
// 默认跳回收藏列表
response.sendRedirect(request.getContextPath() + "/user/collection/list?msg=取消收藏成功");
}
} else {
if ("detail".equals(from) && houseIdStr != null) {
response.sendRedirect(request.getContextPath() + "/user/house/detail?id=" + houseIdStr + "&error=取消收藏失败");
} else {
response.sendRedirect(request.getContextPath() + "/user/collection/list?error=取消收藏失败");
}
}
}
}
}

View File

@@ -1,52 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.CollectionDAO;
import com.hrs.model.entity.Collection;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* 我的收藏列表控制器
*/
@WebServlet("/user/collection/list")
public class CollectionListController extends HttpServlet {
private CollectionDAO collectionDAO = new CollectionDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 获取登录用户
HttpSession session = request.getSession(false);
if (session == null) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
// 2. 查询收藏列表
List<Collection> collectionList = collectionDAO.findByTenantId(loginUser.getId());
// 3. 存入request
request.setAttribute("collectionList", collectionList);
request.setAttribute("totalCount", collectionList.size());
request.setAttribute("loginUser", loginUser);
// 4. 转发到JSP
request.getRequestDispatcher("/jsp/user/collection_list.jsp").forward(request, response);
}
}

View File

@@ -1,62 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.HouseDAO;
import com.hrs.model.entity.House;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 房源详情控制器
* 租客端查看房源详情
* URL映射/user/house/detail
*/
@WebServlet("/user/house/detail")
public class HouseDetailController extends HttpServlet {
private HouseDAO houseDAO = new HouseDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 获取房源ID
String idStr = request.getParameter("id");
if (idStr == null || idStr.trim().isEmpty()) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
Long houseId = null;
try {
houseId = Long.parseLong(idStr);
} catch (NumberFormatException e) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
// 2. 查询房源详情
House house = houseDAO.findById(houseId);
if (house == null) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
// 3. 获取当前登录用户
HttpSession session = request.getSession(false);
User loginUser = (User) session.getAttribute("loginUser");
// 4. 将数据存入request
request.setAttribute("house", house);
request.setAttribute("loginUser", loginUser);
// 5. 转发到详情页面
request.getRequestDispatcher("/jsp/user/house_detail.jsp").forward(request, response);
}
}

View File

@@ -1,63 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.HouseDAO;
import com.hrs.model.entity.House;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* 房源列表控制器
* 租客端查看房源列表
* URL映射/user/house/list
*/
@WebServlet("/user/house/list")
public class HouseListController extends HttpServlet {
private HouseDAO houseDAO = new HouseDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取请求参数
String area = request.getParameter("area");
String minPriceStr = request.getParameter("minPrice");
String maxPriceStr = request.getParameter("maxPrice");
List<House> houseList;
// 根据条件查询
if (area != null && !area.trim().isEmpty()) {
houseList = houseDAO.findHousesByArea(area);
request.setAttribute("searchArea", area);
} else if (minPriceStr != null && maxPriceStr != null && !minPriceStr.isEmpty() && !maxPriceStr.isEmpty()) {
int minPrice = Integer.parseInt(minPriceStr);
int maxPrice = Integer.parseInt(maxPriceStr);
houseList = houseDAO.findHousesByPrice(minPrice, maxPrice);
request.setAttribute("minPrice", minPrice);
request.setAttribute("maxPrice", maxPrice);
} else {
houseList = houseDAO.findPublishedHouses();
}
// 获取当前登录用户(用于判断是否已收藏)
HttpSession session = request.getSession(false);
User loginUser = (User) session.getAttribute("loginUser");
// 将数据存入request
request.setAttribute("houseList", houseList);
request.setAttribute("totalCount", houseList.size());
request.setAttribute("loginUser", loginUser);
// 转发到JSP页面
request.getRequestDispatcher("/jsp/user/house_list.jsp").forward(request, response);
}
}

View File

@@ -1,106 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.UserDAO;
import com.hrs.model.entity.User;
import com.hrs.util.MD5Util;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 登录控制器
* 处理用户登录请求
* URL映射/user/login
*/
@WebServlet("/user/login")
public class LoginController extends HttpServlet {
private UserDAO userDAO = new UserDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// GET请求显示登录页面
// 如果有消息(如注册成功),传递给页面
String message = request.getParameter("message");
if (message != null) {
request.setAttribute("message", message);
}
request.getRequestDispatcher("/jsp/user/login.jsp").forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// POST请求处理登录表单提交
request.setCharacterEncoding("UTF-8");
// 1. 获取表单参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String roleType = request.getParameter("roleType"); // 前端传0-租客 1-房东 2-管理员
// 2. 基础验证
if (username == null || username.trim().isEmpty() ||
password == null || password.trim().isEmpty()) {
request.setAttribute("error", "用户名和密码不能为空");
request.getRequestDispatcher("/jsp/user/login.jsp").forward(request, response);
return;
}
// 3. 密码MD5加密因为数据库里存的是MD5
String encryptedPassword = MD5Util.md5(password);
// 4. 调用DAO查询用户
User user = userDAO.findByUsernameAndPassword(username, encryptedPassword);
// 5. 验证结果
if (user == null) {
// 登录失败
request.setAttribute("error", "用户名或密码错误");
request.getRequestDispatcher("/jsp/user/login.jsp").forward(request, response);
return;
}
// 6. 验证角色是否匹配
if (!user.getRoleType().equals(roleType)) {
request.setAttribute("error", "角色类型不匹配");
request.getRequestDispatcher("/jsp/user/login.jsp").forward(request, response);
return;
}
// 7. 验证账号状态
if ("2".equals(user.getStatus())) {
request.setAttribute("error", "账号已被禁用,请联系管理员");
request.getRequestDispatcher("/jsp/user/login.jsp").forward(request, response);
return;
}
// 8. 登录成功保存用户信息到Session
HttpSession session = request.getSession();
session.setAttribute("loginUser", user);
session.setAttribute("userRole", user.getRoleType());
session.setMaxInactiveInterval(30 * 60); // 30分钟超时
// 9. 根据角色跳转到不同首页
String redirectUrl = "/HRS/";
switch (user.getRoleType()) {
case "0": // 租客
redirectUrl = "/HRS/jsp/user/index.jsp";
break;
case "1": // 房东
redirectUrl = "/HRS/jsp/landlord/index.jsp";
break;
case "2": // 管理员
redirectUrl = "/HRS/jsp/admin/index.jsp";
break;
}
response.sendRedirect(redirectUrl);
}
}

View File

@@ -1,38 +0,0 @@
package com.hrs.controller.user;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 退出登录控制器
*/
@WebServlet("/user/logout")
public class LogoutController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1. 获取Session
HttpSession session = request.getSession(false);
// 2. 如果Session存在销毁它
if (session != null) {
session.invalidate();
}
// 3. 跳转到登录页面
response.sendRedirect(request.getContextPath() + "/user/login");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

View File

@@ -1,120 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.UserDAO;
import com.hrs.model.entity.User;
import com.hrs.util.MD5Util;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 注册控制器
* 处理用户注册请求
* URL映射/user/register
*/
@WebServlet("/user/register")
public class RegisterController extends HttpServlet {
private UserDAO userDAO = new UserDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// GET请求显示注册页面
request.getRequestDispatcher("/jsp/user/register.jsp").forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// POST请求处理注册表单提交
request.setCharacterEncoding("UTF-8");
// 1. 获取表单参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String confirmPassword = request.getParameter("confirmPassword");
String realName = request.getParameter("realName");
String phone = request.getParameter("phone");
String idCard = request.getParameter("idCard");
String roleType = request.getParameter("roleType");
// 2. 基础验证
if (username == null || username.trim().isEmpty()) {
request.setAttribute("error", "用户名不能为空");
request.getRequestDispatcher("/jsp/user/register.jsp").forward(request, response);
return;
}
if (password == null || password.trim().isEmpty()) {
request.setAttribute("error", "密码不能为空");
request.getRequestDispatcher("/jsp/user/register.jsp").forward(request, response);
return;
}
// 3. 验证两次密码是否一致
if (!password.equals(confirmPassword)) {
request.setAttribute("error", "两次输入的密码不一致");
request.getRequestDispatcher("/jsp/user/register.jsp").forward(request, response);
return;
}
// 4. 验证手机号格式(简单验证)
if (phone == null || phone.trim().isEmpty() || !phone.matches("^1[3-9]\\d{9}$")) {
request.setAttribute("error", "手机号格式不正确");
request.getRequestDispatcher("/jsp/user/register.jsp").forward(request, response);
return;
}
// 5. 检查用户名是否已存在
User existUser = userDAO.findByUsername(username);
if (existUser != null) {
request.setAttribute("error", "用户名已存在,请换一个");
request.getRequestDispatcher("/jsp/user/register.jsp").forward(request, response);
return;
}
// 6. 检查手机号是否已注册
User existPhone = userDAO.findByPhone(phone);
if (existPhone != null) {
request.setAttribute("error", "手机号已注册");
request.getRequestDispatcher("/jsp/user/register.jsp").forward(request, response);
return;
}
// 7. 创建用户对象
User user = new User();
user.setUserName(username);
user.setPassword(MD5Util.md5(password)); // 密码MD5加密
user.setRealName(realName);
user.setPhone(phone);
user.setIdCard(idCard);
user.setRoleType(roleType); // 0-租客 1-房东
user.setStatus("0"); // 默认未认证状态
// 8. 保存到数据库
boolean success = userDAO.add(user);
if (success) {
// 注册成功,跳转到登录页面
request.setAttribute("message", "注册成功,请登录");
request.getRequestDispatcher("/jsp/user/login.jsp").forward(request, response);
} else {
// 注册失败
request.setAttribute("error", "注册失败,请稍后重试");
request.getRequestDispatcher("/jsp/user/register.jsp").forward(request, response);
}
if (success) {
// 注册成功,跳转到登录页面,并带上成功消息
response.sendRedirect(request.getContextPath() + "/user/login?message=注册成功,请登录");
} else {
// 注册失败
request.setAttribute("error", "注册失败,请稍后重试");
request.getRequestDispatcher("/jsp/user/register.jsp").forward(request, response);
}
}
}

View File

@@ -1,135 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.HouseDAO;
import com.hrs.model.dao.ReservationDAO;
import com.hrs.model.entity.House;
import com.hrs.model.entity.Reservation;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 添加预约控制器
*/
@WebServlet("/user/reservation/add")
public class ReservationAddController extends HttpServlet {
private ReservationDAO reservationDAO = new ReservationDAO();
private HouseDAO houseDAO = new HouseDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取房源ID
String houseIdStr = request.getParameter("houseId");
if (houseIdStr == null || houseIdStr.trim().isEmpty()) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
Long houseId = Long.parseLong(houseIdStr);
House house = houseDAO.findById(houseId);
if (house == null) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
// 获取登录用户
HttpSession session = request.getSession(false);
User loginUser = (User) session.getAttribute("loginUser");
request.setAttribute("house", house);
request.setAttribute("loginUser", loginUser);
request.getRequestDispatcher("/jsp/user/reservation_add.jsp").forward(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
// 获取登录用户
HttpSession session = request.getSession(false);
if (session == null) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
// 获取表单参数
String houseIdStr = request.getParameter("houseId");
String reserveDate = request.getParameter("reserveDate");
String reserveTime = request.getParameter("reserveTime");
String remark = request.getParameter("remark");
// 验证
if (houseIdStr == null || reserveDate == null || reserveTime == null) {
request.setAttribute("error", "请填写完整信息");
doGet(request, response);
return;
}
Long houseId = Long.parseLong(houseIdStr);
House house = houseDAO.findById(houseId);
if (house == null) {
request.setAttribute("error", "房源不存在");
doGet(request, response);
return;
}
// 组合预约时间
String datetimeStr = reserveDate + " " + reserveTime + ":00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date reserveDateTime;
try {
reserveDateTime = sdf.parse(datetimeStr);
} catch (ParseException e) {
request.setAttribute("error", "时间格式错误");
doGet(request, response);
return;
}
// 检查预约时间是否在今天之后
if (reserveDateTime.before(new Date())) {
request.setAttribute("error", "预约时间不能早于当前时间");
doGet(request, response);
return;
}
// 创建预约对象
Reservation reservation = new Reservation();
reservation.setTenantId(loginUser.getId());
reservation.setHouseId(houseId);
reservation.setLandlordId(house.getLandlordId());
reservation.setReserveTime(reserveDateTime);
reservation.setRemark(remark);
// 保存到数据库
boolean success = reservationDAO.add(reservation);
if (success) {
response.sendRedirect(request.getContextPath() + "/user/reservation/list?msg=预约成功,请等待房东确认");
} else {
request.setAttribute("error", "预约失败,请稍后重试");
doGet(request, response);
}
}
}

View File

@@ -1,57 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.ReservationDAO;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 取消预约控制器(租客端)
*/
@WebServlet("/user/reservation/cancel")
public class ReservationCancelController extends HttpServlet {
private ReservationDAO reservationDAO = new ReservationDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取登录用户
HttpSession session = request.getSession(false);
if (session == null) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
// 获取预约ID
String idStr = request.getParameter("id");
if (idStr == null || idStr.trim().isEmpty()) {
response.sendRedirect(request.getContextPath() + "/user/reservation/list");
return;
}
Long id = Long.parseLong(idStr);
// 取消预约
boolean success = reservationDAO.cancel(id, loginUser.getId());
if (success) {
response.sendRedirect(request.getContextPath() + "/user/reservation/list?msg=取消预约成功");
} else {
response.sendRedirect(request.getContextPath() + "/user/reservation/list?error=取消预约失败");
}
}
}

View File

@@ -1,50 +0,0 @@
package com.hrs.controller.user;
import com.hrs.model.dao.ReservationDAO;
import com.hrs.model.entity.Reservation;
import com.hrs.model.entity.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* 我的预约列表控制器(租客端)
*/
@WebServlet("/user/reservation/list")
public class ReservationListController extends HttpServlet {
private ReservationDAO reservationDAO = new ReservationDAO();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获取登录用户
HttpSession session = request.getSession(false);
if (session == null) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
// 查询预约列表
List<Reservation> reservationList = reservationDAO.findByTenantId(loginUser.getId());
request.setAttribute("reservationList", reservationList);
request.setAttribute("totalCount", reservationList.size());
request.setAttribute("loginUser", loginUser);
request.getRequestDispatcher("/jsp/user/reservation_list.jsp").forward(request, response);
}
}

View File

@@ -1,37 +0,0 @@
package com.hrs.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* 字符编码过滤器
*/
@WebFilter("/*")
public class EncodingFilter implements Filter {
private String encoding = "UTF-8";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String param = filterConfig.getInitParameter("encoding");
if (param != null && !param.isEmpty()) {
encoding = param;
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 设置请求编码
request.setCharacterEncoding(encoding);
// 设置响应编码
response.setCharacterEncoding(encoding);
// 继续执行
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}

View File

@@ -1,93 +0,0 @@
package com.hrs.filter;
import com.hrs.model.entity.User;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* 登录过滤器
* 拦截未登录的请求
*/
@WebFilter("/*") // 拦截所有请求
public class LoginFilter implements Filter {
// 不需要登录就能访问的路径
private static final String[] PUBLIC_PATHS = {
"/user/login", // 登录页面
"/user/register", // 注册页面 ✅ 新增
"/css/", // 静态资源
"/js/",
"/images/",
"/jsp/common/", // 公共页面
"/index.jsp" // 首页
};
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("登录过滤器初始化");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
// 1. 获取请求路径
String path = req.getRequestURI().substring(req.getContextPath().length());
System.out.println("过滤器拦截路径:" + path);
// 2. 判断是否是公开路径
if (isPublicPath(path)) {
chain.doFilter(request, response);
return;
}
// 3. 检查Session中是否有登录用户
HttpSession session = req.getSession(false);
User loginUser = null;
if (session != null) {
loginUser = (User) session.getAttribute("loginUser");
}
// 4. 如果没登录,跳转到登录页
if (loginUser == null) {
System.out.println("未登录,拦截请求:" + path);
resp.sendRedirect(req.getContextPath() + "/user/login");
return;
}
// 5. 已登录,继续请求
chain.doFilter(request, response);
}
/**
* 判断是否是公开路径
*/
private boolean isPublicPath(String path) {
// 如果是根路径,放行(通常重定向到首页)
if (path == null || path.equals("/") || path.isEmpty()) {
return true;
}
// 检查是否在公开路径列表中
for (String publicPath : PUBLIC_PATHS) {
if (path.startsWith(publicPath)) {
return true;
}
}
return false;
}
@Override
public void destroy() {
System.out.println("登录过滤器销毁");
}
}

View File

@@ -1,204 +0,0 @@
package com.hrs.model.dao;
import com.hrs.model.entity.Collection;
import com.hrs.model.entity.House;
import com.hrs.util.DBUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 收藏数据访问类
*/
public class CollectionDAO {
private HouseDAO houseDAO = new HouseDAO();
/**
* 添加收藏
*/
public boolean add(Collection collection) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "INSERT INTO hrs_collection (tenant_id, house_id, collect_time, create_time) " +
"VALUES (?, ?, NOW(), NOW())";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, collection.getTenantId());
pstmt.setLong(2, collection.getHouseId());
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 检查是否已收藏
*/
public boolean isCollected(Long tenantId, Long houseId) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
boolean exists = false;
try {
conn = DBUtil.getConnection();
String sql = "SELECT COUNT(*) FROM hrs_collection WHERE tenant_id = ? AND house_id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, tenantId);
pstmt.setLong(2, houseId);
rs = pstmt.executeQuery();
if (rs.next()) {
exists = rs.getInt(1) > 0;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return exists;
}
/**
* 取消收藏
*/
public boolean delete(Long tenantId, Long houseId) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "DELETE FROM hrs_collection WHERE tenant_id = ? AND house_id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, tenantId);
pstmt.setLong(2, houseId);
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 根据收藏ID删除
*/
public boolean deleteById(Long id) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "DELETE FROM hrs_collection WHERE id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 查询租客的所有收藏
*/
public List<Collection> findByTenantId(Long tenantId) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<Collection> collectionList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_collection WHERE tenant_id = ? ORDER BY collect_time DESC";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, tenantId);
rs = pstmt.executeQuery();
while (rs.next()) {
Collection collection = resultSetToCollection(rs);
// 查询关联的房源详情
House house = houseDAO.findById(collection.getHouseId());
collection.setHouse(house);
collectionList.add(collection);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return collectionList;
}
/**
* 查询租客的收藏数量
*/
public int countByTenantId(Long tenantId) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "SELECT COUNT(*) FROM hrs_collection WHERE tenant_id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, tenantId);
rs = pstmt.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return count;
}
/**
* 将ResultSet转换为Collection对象
*/
private Collection resultSetToCollection(ResultSet rs) throws SQLException {
Collection collection = new Collection();
collection.setId(rs.getLong("id"));
collection.setTenantId(rs.getLong("tenant_id"));
collection.setHouseId(rs.getLong("house_id"));
collection.setCollectTime(rs.getTimestamp("collect_time"));
collection.setCreateBy(rs.getString("create_by"));
collection.setCreateTime(rs.getTimestamp("create_time"));
return collection;
}
}

View File

@@ -1,397 +0,0 @@
package com.hrs.model.dao;
import com.hrs.model.entity.House;
import com.hrs.util.DBUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 房源数据访问类
* 负责房源表的增删改查操作
*/
public class HouseDAO {
/**
* 查询已上架的房源(供租客查看)
*/
public List<House> findPublishedHouses() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<House> houseList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT h.*, u.real_name as landlord_name, u.phone as landlord_phone " +
"FROM hrs_house h " +
"LEFT JOIN hrs_user u ON h.landlord_id = u.id " +
"WHERE h.status = '1' " +
"ORDER BY h.create_time DESC";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
houseList.add(resultSetToHouse(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return houseList;
}
/**
* 根据区域筛选房源
*/
public List<House> findHousesByArea(String area) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<House> houseList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT h.*, u.real_name as landlord_name, u.phone as landlord_phone " +
"FROM hrs_house h " +
"LEFT JOIN hrs_user u ON h.landlord_id = u.id " +
"WHERE h.status = '1' AND h.area LIKE ? " +
"ORDER BY h.create_time DESC";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "%" + area + "%");
rs = pstmt.executeQuery();
while (rs.next()) {
houseList.add(resultSetToHouse(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return houseList;
}
/**
* 根据价格区间筛选房源
*/
public List<House> findHousesByPrice(int minPrice, int maxPrice) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<House> houseList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT h.*, u.real_name as landlord_name, u.phone as landlord_phone " +
"FROM hrs_house h " +
"LEFT JOIN hrs_user u ON h.landlord_id = u.id " +
"WHERE h.status = '1' AND h.rent_price BETWEEN ? AND ? " +
"ORDER BY h.rent_price ASC";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, minPrice);
pstmt.setInt(2, maxPrice);
rs = pstmt.executeQuery();
while (rs.next()) {
houseList.add(resultSetToHouse(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return houseList;
}
/**
* 根据房源ID查询详情
*/
public House findById(Long id) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
House house = null;
try {
conn = DBUtil.getConnection();
String sql = "SELECT h.*, u.real_name as landlord_name, u.phone as landlord_phone " +
"FROM hrs_house h " +
"LEFT JOIN hrs_user u ON h.landlord_id = u.id " +
"WHERE h.id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
rs = pstmt.executeQuery();
if (rs.next()) {
house = resultSetToHouse(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return house;
}
/**
* 查询房东自己的房源
*/
public List<House> findByLandlordId(Long landlordId) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<House> houseList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_house WHERE landlord_id = ? ORDER BY create_time DESC";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, landlordId);
rs = pstmt.executeQuery();
while (rs.next()) {
houseList.add(resultSetToHouse(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return houseList;
}
/**
* 查询所有房源(管理员用)
*/
public List<House> findAll() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<House> houseList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT h.*, u.real_name as landlord_name " +
"FROM hrs_house h " +
"LEFT JOIN hrs_user u ON h.landlord_id = u.id " +
"ORDER BY h.create_time DESC";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
houseList.add(resultSetToHouse(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return houseList;
}
/**
* 添加房源
*/
public boolean add(House house) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "INSERT INTO hrs_house (landlord_id, house_no, title, area, address, " +
"house_type, rent_price, rent_type, facility, description, img_url, " +
"status, create_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, house.getLandlordId());
pstmt.setString(2, house.getHouseNo());
pstmt.setString(3, house.getTitle());
pstmt.setString(4, house.getArea());
pstmt.setString(5, house.getAddress());
pstmt.setString(6, house.getHouseType());
pstmt.setBigDecimal(7, house.getRentPrice());
pstmt.setString(8, house.getRentType());
pstmt.setString(9, house.getFacility());
pstmt.setString(10, house.getDescription());
pstmt.setString(11, house.getImgUrl());
pstmt.setString(12, house.getStatus() != null ? house.getStatus() : "0");
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 更新房源
*/
public boolean update(House house) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "UPDATE hrs_house SET title=?, area=?, address=?, house_type=?, " +
"rent_price=?, rent_type=?, facility=?, description=?, img_url=?, " +
"update_time=NOW() WHERE id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, house.getTitle());
pstmt.setString(2, house.getArea());
pstmt.setString(3, house.getAddress());
pstmt.setString(4, house.getHouseType());
pstmt.setBigDecimal(5, house.getRentPrice());
pstmt.setString(6, house.getRentType());
pstmt.setString(7, house.getFacility());
pstmt.setString(8, house.getDescription());
pstmt.setString(9, house.getImgUrl());
pstmt.setLong(10, house.getId());
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 更新房源状态(审核/上架/下架)
*/
public boolean updateStatus(Long houseId, String status) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "UPDATE hrs_house SET status=?, update_time=NOW() WHERE id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, status);
pstmt.setLong(2, houseId);
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 删除房源
*/
public boolean delete(Long id) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "DELETE FROM hrs_house WHERE id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 统计已上架房源数量
*/
public int countPublished() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "SELECT COUNT(*) FROM hrs_house WHERE status = '1'";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return count;
}
/**
* 将ResultSet转换为House对象
*/
private House resultSetToHouse(ResultSet rs) throws SQLException {
House house = new House();
house.setId(rs.getLong("id"));
house.setLandlordId(rs.getLong("landlord_id"));
house.setHouseNo(rs.getString("house_no"));
house.setTitle(rs.getString("title"));
house.setArea(rs.getString("area"));
house.setAddress(rs.getString("address"));
house.setHouseType(rs.getString("house_type"));
house.setRentPrice(rs.getBigDecimal("rent_price"));
house.setRentType(rs.getString("rent_type"));
house.setFacility(rs.getString("facility"));
house.setDescription(rs.getString("description"));
house.setImgUrl(rs.getString("img_url"));
house.setStatus(rs.getString("status"));
house.setCreateBy(rs.getString("create_by"));
house.setCreateTime(rs.getTimestamp("create_time"));
house.setUpdateBy(rs.getString("update_by"));
house.setUpdateTime(rs.getTimestamp("update_time"));
// 关联字段
try {
house.setLandlordName(rs.getString("landlord_name"));
house.setLandlordPhone(rs.getString("landlord_phone"));
} catch (SQLException e) {
// 可能没有这些字段
}
return house;
}
}

View File

@@ -1,286 +0,0 @@
package com.hrs.model.dao;
import com.hrs.model.entity.Reservation;
import com.hrs.model.entity.House;
import com.hrs.model.entity.User;
import com.hrs.util.DBUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 预约看房数据访问类
*/
public class ReservationDAO {
private HouseDAO houseDAO = new HouseDAO();
private UserDAO userDAO = new UserDAO();
/**
* 添加预约
*/
public boolean add(Reservation reservation) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "INSERT INTO hrs_reservation (tenant_id, house_id, landlord_id, reserve_time, remark, status, create_time) " +
"VALUES (?, ?, ?, ?, ?, '0', NOW())";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, reservation.getTenantId());
pstmt.setLong(2, reservation.getHouseId());
pstmt.setLong(3, reservation.getLandlordId());
pstmt.setTimestamp(4, new Timestamp(reservation.getReserveTime().getTime()));
pstmt.setString(5, reservation.getRemark());
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 查询租客的所有预约
*/
public List<Reservation> findByTenantId(Long tenantId) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<Reservation> reservationList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_reservation WHERE tenant_id = ? ORDER BY reserve_time DESC";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, tenantId);
rs = pstmt.executeQuery();
while (rs.next()) {
Reservation reservation = resultSetToReservation(rs);
// 关联房源详情
House house = houseDAO.findById(reservation.getHouseId());
reservation.setHouse(house);
reservationList.add(reservation);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return reservationList;
}
/**
* 查询房东收到的预约
*/
public List<Reservation> findByLandlordId(Long landlordId) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<Reservation> reservationList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_reservation WHERE landlord_id = ? ORDER BY reserve_time DESC";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, landlordId);
rs = pstmt.executeQuery();
while (rs.next()) {
Reservation reservation = resultSetToReservation(rs);
// 关联房源详情
House house = houseDAO.findById(reservation.getHouseId());
reservation.setHouse(house);
// 关联租客信息
User tenant = userDAO.findById(reservation.getTenantId());
reservation.setTenant(tenant);
reservationList.add(reservation);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return reservationList;
}
/**
* 根据ID查询预约
*/
public Reservation findById(Long id) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
Reservation reservation = null;
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_reservation WHERE id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
rs = pstmt.executeQuery();
if (rs.next()) {
reservation = resultSetToReservation(rs);
House house = houseDAO.findById(reservation.getHouseId());
reservation.setHouse(house);
User tenant = userDAO.findById(reservation.getTenantId());
reservation.setTenant(tenant);
User landlord = userDAO.findById(reservation.getLandlordId());
reservation.setLandlord(landlord);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return reservation;
}
/**
* 更新预约状态(房东确认/取消)
*/
public boolean updateStatus(Long id, String status) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "UPDATE hrs_reservation SET status=?, handle_time=NOW(), update_time=NOW() WHERE id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, status);
pstmt.setLong(2, id);
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 取消预约(租客取消)
*/
public boolean cancel(Long id, Long tenantId) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "UPDATE hrs_reservation SET status='2', update_time=NOW() WHERE id=? AND tenant_id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
pstmt.setLong(2, tenantId);
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 统计租客预约数量
*/
public int countByTenantId(Long tenantId) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "SELECT COUNT(*) FROM hrs_reservation WHERE tenant_id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, tenantId);
rs = pstmt.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return count;
}
/**
* 统计房东收到的预约数量
*/
public int countByLandlordId(Long landlordId) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "SELECT COUNT(*) FROM hrs_reservation WHERE landlord_id = ? AND status = '0'";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, landlordId);
rs = pstmt.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return count;
}
/**
* 将ResultSet转换为Reservation对象
*/
private Reservation resultSetToReservation(ResultSet rs) throws SQLException {
Reservation reservation = new Reservation();
reservation.setId(rs.getLong("id"));
reservation.setTenantId(rs.getLong("tenant_id"));
reservation.setHouseId(rs.getLong("house_id"));
reservation.setLandlordId(rs.getLong("landlord_id"));
reservation.setReserveTime(rs.getTimestamp("reserve_time"));
reservation.setRemark(rs.getString("remark"));
reservation.setStatus(rs.getString("status"));
reservation.setHandleTime(rs.getTimestamp("handle_time"));
reservation.setCreateBy(rs.getString("create_by"));
reservation.setCreateTime(rs.getTimestamp("create_time"));
reservation.setUpdateBy(rs.getString("update_by"));
reservation.setUpdateTime(rs.getTimestamp("update_time"));
return reservation;
}
}

View File

@@ -1,399 +0,0 @@
package com.hrs.model.dao;
import com.hrs.model.entity.User;
import com.hrs.util.DBUtil;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* 用户数据访问类
* 负责用户表的增删改查操作
*/
public class UserDAO {
/**
* 根据用户名和密码查询用户(登录用)
*/
public User findByUsernameAndPassword(String username, String password) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
User user = null;
try {
// 1. 获取连接
conn = DBUtil.getConnection();
// 2. 编写SQL注意实际项目密码应该是加密后比较这里简化
String sql = "SELECT * FROM hrs_user WHERE user_name = ? AND password = ?";
// 3. 创建预编译语句
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
pstmt.setString(2, password); // 实际应该是MD5加密后比较
// 4. 执行查询
rs = pstmt.executeQuery();
// 5. 处理结果集
if (rs.next()) {
user = resultSetToUser(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6. 释放资源
DBUtil.close(rs, pstmt, conn);
}
return user;
}
/**
* 根据用户名查询用户(检查用户名是否已存在)
*/
public User findByUsername(String username) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
User user = null;
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_user WHERE user_name = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, username);
rs = pstmt.executeQuery();
if (rs.next()) {
user = resultSetToUser(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return user;
}
/**
* 根据手机号查询用户(检查手机号是否已注册)
*/
public User findByPhone(String phone) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
User user = null;
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_user WHERE phone = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, phone);
rs = pstmt.executeQuery();
if (rs.next()) {
user = resultSetToUser(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return user;
}
/**
* 根据ID查询用户
*/
public User findById(Long id) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
User user = null;
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_user WHERE id = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
rs = pstmt.executeQuery();
if (rs.next()) {
user = resultSetToUser(rs);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return user;
}
/**
* 查询所有租客
*/
public List<User> findAllTenants() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<User> userList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_user WHERE role_type = '0' ORDER BY create_time DESC";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
userList.add(resultSetToUser(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return userList;
}
/**
* 查询所有房东
*/
public List<User> findAllLandlords() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List<User> userList = new ArrayList<>();
try {
conn = DBUtil.getConnection();
String sql = "SELECT * FROM hrs_user WHERE role_type = '1' ORDER BY create_time DESC";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
userList.add(resultSetToUser(rs));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return userList;
}
/**
* 添加用户(注册用)
*/
public boolean add(User user) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "INSERT INTO hrs_user (user_name, password, real_name, phone, id_card, role_type, status, create_time) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, NOW())";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, user.getUserName());
pstmt.setString(2, user.getPassword());
pstmt.setString(3, user.getRealName());
pstmt.setString(4, user.getPhone());
pstmt.setString(5, user.getIdCard());
pstmt.setString(6, user.getRoleType());
pstmt.setString(7, user.getStatus() != null ? user.getStatus() : "0"); // 默认未认证
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 更新用户信息
*/
public boolean update(User user) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "UPDATE hrs_user SET real_name=?, phone=?, id_card=?, status=?, update_time=NOW() WHERE id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, user.getRealName());
pstmt.setString(2, user.getPhone());
pstmt.setString(3, user.getIdCard());
pstmt.setString(4, user.getStatus());
pstmt.setLong(5, user.getId());
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 更新用户状态(启用/禁用)
*/
public boolean updateStatus(Long userId, String status) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "UPDATE hrs_user SET status=?, update_time=NOW() WHERE id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, status);
pstmt.setLong(2, userId);
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 删除用户(物理删除,一般不建议)
*/
public boolean delete(Long id) {
Connection conn = null;
PreparedStatement pstmt = null;
boolean success = false;
try {
conn = DBUtil.getConnection();
String sql = "DELETE FROM hrs_user WHERE id=?";
pstmt = conn.prepareStatement(sql);
pstmt.setLong(1, id);
int rows = pstmt.executeUpdate();
success = rows > 0;
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(pstmt, conn);
}
return success;
}
/**
* 统计用户总数
*/
public int count() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
int count = 0;
try {
conn = DBUtil.getConnection();
String sql = "SELECT COUNT(*) FROM hrs_user";
pstmt = conn.prepareStatement(sql);
rs = pstmt.executeQuery();
if (rs.next()) {
count = rs.getInt(1);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(rs, pstmt, conn);
}
return count;
}
/**
* 将ResultSet转换为User对象
*/
private User resultSetToUser(ResultSet rs) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setUserName(rs.getString("user_name"));
user.setPassword(rs.getString("password"));
user.setRealName(rs.getString("real_name"));
user.setPhone(rs.getString("phone"));
user.setIdCard(rs.getString("id_card"));
user.setRoleType(rs.getString("role_type"));
user.setStatus(rs.getString("status"));
user.setCreateBy(rs.getString("create_by"));
user.setCreateTime(rs.getTimestamp("create_time"));
user.setUpdateBy(rs.getString("update_by"));
user.setUpdateTime(rs.getTimestamp("update_time"));
return user;
}
/**
* 测试方法
*/
public static void main(String[] args) {
UserDAO userDAO = new UserDAO();
// 测试1查询所有租客
System.out.println("=== 查询所有租客 ===");
List<User> tenants = userDAO.findAllTenants();
for (User user : tenants) {
System.out.println(user);
}
// 测试2管理员登录测试admin/e10adc3949ba59abbe56e057f20f883e = 123456的MD5
System.out.println("\n=== 管理员登录测试 ===");
User admin = userDAO.findByUsernameAndPassword("admin", "e10adc3949ba59abbe56e057f20f883e");
if (admin != null) {
System.out.println("登录成功:" + admin);
System.out.println("是否是管理员:" + admin.isAdmin());
} else {
System.out.println("登录失败");
}
// 测试3房东登录测试
System.out.println("\n=== 房东登录测试 ===");
User landlord = userDAO.findByUsernameAndPassword("landlord01", "e10adc3949ba59abbe56e057f20f883e");
if (landlord != null) {
System.out.println("登录成功:" + landlord);
System.out.println("是否是房东:" + landlord.isLandlord());
} else {
System.out.println("登录失败");
}
// 测试4统计用户总数
System.out.println("\n=== 用户总数 ===");
int count = userDAO.count();
System.out.println("当前用户总数:" + count);
}
}

View File

@@ -1,85 +0,0 @@
package com.hrs.model.entity;
import java.util.Date;
/**
* 收藏实体类
* 对应数据库表hrs_collection
*/
public class Collection {
private Long id; // 主键ID
private Long tenantId; // 租客ID
private Long houseId; // 房源ID
private Date collectTime; // 收藏时间
private String createBy; // 创建人
private Date createTime; // 创建时间
// 关联字段(不存数据库,用于显示)
private House house; // 房源详情
public Collection() {
}
public Collection(Long tenantId, Long houseId) {
this.tenantId = tenantId;
this.houseId = houseId;
}
// Getter 和 Setter
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getTenantId() {
return tenantId;
}
public void setTenantId(Long tenantId) {
this.tenantId = tenantId;
}
public Long getHouseId() {
return houseId;
}
public void setHouseId(Long houseId) {
this.houseId = houseId;
}
public Date getCollectTime() {
return collectTime;
}
public void setCollectTime(Date collectTime) {
this.collectTime = collectTime;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public House getHouse() {
return house;
}
public void setHouse(House house) {
this.house = house;
}
}

View File

@@ -1,226 +0,0 @@
package com.hrs.model.entity;
import java.math.BigDecimal;
import java.util.Date;
/**
* 房源实体类
* 对应数据库表hrs_house
*
* 房源状态0-待审核1-已上架2-已下架3-违规
* 租赁类型0-整租1-合租
*/
public class House {
private Long id; // 主键ID
private Long landlordId; // 房东ID
private String houseNo; // 房源编号
private String title; // 房源标题
private String area; // 房源区域
private String address; // 详细地址
private String houseType; // 户型
private BigDecimal rentPrice; // 月租金
private String rentType; // 租赁类型0-整租1-合租
private String facility; // 配套设施
private String description; // 房源描述
private String imgUrl; // 房源图片路径
private String status; // 房源状态0-待审核1-已上架2-已下架3-违规
private String createBy; // 创建人
private Date createTime; // 创建时间
private String updateBy; // 更新人
private Date updateTime; // 更新时间
// 关联字段(不存数据库,用于显示房东信息)
private String landlordName; // 房东姓名
private String landlordPhone; // 房东电话
public House() {
}
// Getter 和 Setter
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getLandlordId() {
return landlordId;
}
public void setLandlordId(Long landlordId) {
this.landlordId = landlordId;
}
public String getHouseNo() {
return houseNo;
}
public void setHouseNo(String houseNo) {
this.houseNo = houseNo;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getHouseType() {
return houseType;
}
public void setHouseType(String houseType) {
this.houseType = houseType;
}
public BigDecimal getRentPrice() {
return rentPrice;
}
public void setRentPrice(BigDecimal rentPrice) {
this.rentPrice = rentPrice;
}
public String getRentType() {
return rentType;
}
public void setRentType(String rentType) {
this.rentType = rentType;
}
public String getRentTypeText() {
return "0".equals(rentType) ? "整租" : "合租";
}
public String getFacility() {
return facility;
}
public void setFacility(String facility) {
this.facility = facility;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getImgUrl() {
return imgUrl;
}
public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatusText() {
switch (status) {
case "0": return "待审核";
case "1": return "已上架";
case "2": return "已下架";
case "3": return "违规";
default: return "未知";
}
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getLandlordName() {
return landlordName;
}
public void setLandlordName(String landlordName) {
this.landlordName = landlordName;
}
public String getLandlordPhone() {
return landlordPhone;
}
public void setLandlordPhone(String landlordPhone) {
this.landlordPhone = landlordPhone;
}
// 辅助方法:获取第一张图片(用于列表展示)
public String getFirstImage() {
if (imgUrl != null && !imgUrl.isEmpty()) {
String[] images = imgUrl.split(",");
return images[0];
}
return null;
}
@Override
public String toString() {
return "House{" +
"id=" + id +
", title='" + title + '\'' +
", area='" + area + '\'' +
", rentPrice=" + rentPrice +
", status='" + status + '\'' +
'}';
}
}

View File

@@ -1,174 +0,0 @@
package com.hrs.model.entity;
import java.util.Date;
/**
* 预约看房实体类
* 对应数据库表hrs_reservation
*
* 预约状态0-待确认1-已确认2-已取消3-已完成
*/
public class Reservation {
private Long id; // 主键ID
private Long tenantId; // 租客ID
private Long houseId; // 房源ID
private Long landlordId; // 房东ID
private Date reserveTime; // 预约看房时间
private String remark; // 租客备注
private String status; // 预约状态0-待确认1-已确认2-已取消3-已完成
private Date handleTime; // 房东处理时间
private String createBy; // 创建人
private Date createTime; // 创建时间
private String updateBy; // 更新人
private Date updateTime; // 更新时间
// 关联字段(不存数据库)
private House house; // 房源详情
private User tenant; // 租客信息
private User landlord; // 房东信息
public Reservation() {
}
// Getter 和 Setter
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getTenantId() {
return tenantId;
}
public void setTenantId(Long tenantId) {
this.tenantId = tenantId;
}
public Long getHouseId() {
return houseId;
}
public void setHouseId(Long houseId) {
this.houseId = houseId;
}
public Long getLandlordId() {
return landlordId;
}
public void setLandlordId(Long landlordId) {
this.landlordId = landlordId;
}
public Date getReserveTime() {
return reserveTime;
}
public void setReserveTime(Date reserveTime) {
this.reserveTime = reserveTime;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatusText() {
switch (status) {
case "0": return "待确认";
case "1": return "已确认";
case "2": return "已取消";
case "3": return "已完成";
default: return "未知";
}
}
public Date getHandleTime() {
return handleTime;
}
public void setHandleTime(Date handleTime) {
this.handleTime = handleTime;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public House getHouse() {
return house;
}
public void setHouse(House house) {
this.house = house;
}
public User getTenant() {
return tenant;
}
public void setTenant(User tenant) {
this.tenant = tenant;
}
public User getLandlord() {
return landlord;
}
public void setLandlord(User landlord) {
this.landlord = landlord;
}
@Override
public String toString() {
return "Reservation{" +
"id=" + id +
", houseId=" + houseId +
", reserveTime=" + reserveTime +
", status='" + status + '\'' +
'}';
}
}

View File

@@ -1,186 +0,0 @@
package com.hrs.model.entity;
import java.util.Date;
/**
* 用户实体类
* 对应数据库表hrs_user
*
* 角色类型0-租客 1-房东 2-管理员
* 账号状态0-未认证 1-已认证 2-禁用
*/
public class User {
// 数据库字段对应
private Long id; // 主键ID
private String userName; // 用户名
private String password; // 密码
private String realName; // 真实姓名
private String phone; // 手机号
private String idCard; // 身份证号
private String roleType; // 角色类型0-租客 1-房东 2-管理员
private String status; // 账号状态0-未认证 1-已认证 2-禁用
private String createBy; // 创建人
private Date createTime; // 创建时间
private String updateBy; // 更新人
private Date updateTime; // 更新时间
// 无参构造方法
public User() {
}
// 全参构造方法
public User(Long id, String userName, String password, String realName,
String phone, String idCard, String roleType, String status,
String createBy, Date createTime, String updateBy, Date updateTime) {
this.id = id;
this.userName = userName;
this.password = password;
this.realName = realName;
this.phone = phone;
this.idCard = idCard;
this.roleType = roleType;
this.status = status;
this.createBy = createBy;
this.createTime = createTime;
this.updateBy = updateBy;
this.updateTime = updateTime;
}
// Getter 和 Setter 方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public String getRoleType() {
return roleType;
}
public void setRoleType(String roleType) {
this.roleType = roleType;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getCreateBy() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy = createBy;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getUpdateBy() {
return updateBy;
}
public void setUpdateBy(String updateBy) {
this.updateBy = updateBy;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
/**
* 辅助方法:判断是否为租客
*/
public boolean isTenant() {
return "0".equals(this.roleType);
}
/**
* 辅助方法:判断是否为房东
*/
public boolean isLandlord() {
return "1".equals(this.roleType);
}
/**
* 辅助方法:判断是否为管理员
*/
public boolean isAdmin() {
return "2".equals(this.roleType);
}
/**
* 辅助方法:判断账号是否已认证
*/
public boolean isVerified() {
return "1".equals(this.status);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", userName='" + userName + '\'' +
", realName='" + realName + '\'' +
", phone='" + phone + '\'' +
", roleType='" + roleType + '\'' +
", status='" + status + '\'' +
'}';
}
}

View File

@@ -1,91 +0,0 @@
package com.hrs.util;
import java.sql.*;
/**
* 数据库连接工具类 - 土办法版
* 只要jar包在WEB-INF/lib里就能用
*/
public class DBUtil {
// 数据库配置(改成你的)
private static final String URL = "jdbc:mysql://localhost:3306/hrs?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
private static final String USERNAME = "root"; // 你的MySQL用户名
private static final String PASSWORD = "root"; // 你的MySQL密码
// 驱动类名
private static final String DRIVER = "com.mysql.jdbc.Driver";
/**
* 静态代码块:加载驱动
*/
static {
try {
Class.forName(DRIVER);
System.out.println("✅ MySQL驱动加载成功");
} catch (ClassNotFoundException e) {
System.out.println("❌ MySQL驱动加载失败");
System.out.println("请检查WEB-INF/lib 下是否有 mysql-connector-java-5.1.49.jar");
e.printStackTrace();
}
}
/**
* 获取数据库连接
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USERNAME, PASSWORD);
}
/**
* 关闭连接(查询用)
*/
public static void close(ResultSet rs, PreparedStatement pstmt, Connection conn) {
if (rs != null) {
try { rs.close(); } catch (SQLException e) { e.printStackTrace(); }
}
close(pstmt, conn);
}
/**
* 关闭连接(增删改用)
*/
public static void close(PreparedStatement pstmt, Connection conn) {
if (pstmt != null) {
try { pstmt.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if (conn != null) {
try { conn.close(); } catch (SQLException e) { e.printStackTrace(); }
}
}
/**
* 测试方法
*/
public static void main(String[] args) {
Connection conn = null;
try {
// 1. 获取连接
conn = DBUtil.getConnection();
System.out.println("✅ 数据库连接成功!");
System.out.println("连接对象:" + conn);
// 2. 简单测试查询
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM hrs_user");
if (rs.next()) {
int count = rs.getInt(1);
System.out.println("✅ 用户表中共有 " + count + " 条记录");
}
rs.close();
stmt.close();
} catch (SQLException e) {
System.out.println("❌ 数据库连接失败!");
System.out.println("错误信息:" + e.getMessage());
e.printStackTrace();
} finally {
close(null, null, conn);
}
}
}

View File

@@ -1,80 +0,0 @@
package com.hrs.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* MD5加密工具类
* 用于密码加密
*/
public class MD5Util {
/**
* 将字符串进行MD5加密
* @param str 要加密的字符串
* @return MD5加密后的32位小写字符串
*/
public static String md5(String str) {
if (str == null || str.isEmpty()) {
return null;
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] bytes = md.digest(str.getBytes());
// 将字节数组转换为十六进制字符串
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
// 取字节的高四位
int high = (b >> 4) & 0x0f;
// 取字节的低四位
int low = b & 0x0f;
sb.append(hexChar(high));
sb.append(hexChar(low));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
/**
* 将0-15的数字转换为十六进制字符
*/
private static char hexChar(int value) {
if (value >= 0 && value <= 9) {
return (char) ('0' + value);
} else {
return (char) ('a' + (value - 10));
}
}
/**
* 验证密码是否匹配
* @param inputPassword 用户输入的明文密码
* @param storedPassword 数据库中存储的MD5密码
* @return 是否匹配
*/
public static boolean verify(String inputPassword, String storedPassword) {
String encrypted = md5(inputPassword);
return encrypted != null && encrypted.equals(storedPassword);
}
/**
* 测试方法
*/
public static void main(String[] args) {
String password = "123456";
String encrypted = md5(password);
System.out.println("明文密码:" + password);
System.out.println("MD5加密" + encrypted);
System.out.println("长度:" + encrypted.length());
// 验证
System.out.println("验证结果:" + verify("123456", encrypted));
}
}

View File

@@ -1,3 +0,0 @@
Manifest-Version: 1.0
Class-Path:

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>HRS</display-name>
<!-- 欢迎页面 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.hrs.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

View File

@@ -1,20 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.User" %>
<%
User loginUser = (User)session.getAttribute("loginUser");
if(loginUser == null || !"2".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>管理员首页</title>
</head>
<body>
<h1>欢迎管理员:<%= loginUser.getRealName() %></h1>
<a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
</body>
</html>

View File

@@ -1,20 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.User" %>
<%
User loginUser = (User)session.getAttribute("loginUser");
if(loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>房东首页</title>
</head>
<body>
<h1>欢迎房东:<%= loginUser.getRealName() %></h1>
<a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
</body>
</html>

View File

@@ -1,324 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.Reservation, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Reservation> reservationList = (List<Reservation>) request.getAttribute("reservationList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>预约管理 - 房东后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #ff9800;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #fb8c00;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.reservation-table {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.status-confirmed {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-cancelled {
background-color: #ffebee;
color: #f44336;
}
.btn {
padding: 5px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
margin: 0 3px;
}
.btn-success {
background-color: #4CAF50;
color: white;
}
.btn-success:hover {
background-color: #45a049;
}
.btn-danger {
background-color: #f44336;
color: white;
}
.btn-danger:hover {
background-color: #d32f2f;
}
.btn-disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 房东后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/landlord/house/list">房源管理</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/list" class="active">预约管理</a>
<a href="${pageContext.request.contextPath}/landlord/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/landlord/house/list">首页</a> &gt;
<span>预约管理</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📋 共有 <strong><%= totalCount %></strong> 条预约记录
</div>
<div class="reservation-table">
<%
if (reservationList != null && !reservationList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>租客信息</th>
<th>房源信息</th>
<th>预约时间</th>
<th>状态</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
for (Reservation reservation : reservationList) {
House house = reservation.getHouse();
User tenant = reservation.getTenant();
%>
<tr>
<td>
<%= tenant.getRealName() != null ? tenant.getRealName() : tenant.getUserName() %><br>
<span style="font-size:12px;color:#999;"><%= tenant.getPhone() %></span>
</td>
<td>
<strong><%= house.getTitle() %></strong><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %></span>
</td>
<td><%= sdf.format(reservation.getReserveTime()) %></td>
<td>
<span class="status-badge status-<%= reservation.getStatus().equals("0") ? "pending" : "confirmed" %>">
<%= reservation.getStatusText() %>
</span>
</td>
<td style="max-width:150px;">
<%= reservation.getRemark() != null ? reservation.getRemark() : "-" %>
</td>
<td>
<% if ("0".equals(reservation.getStatus())) { %>
<a href="${pageContext.request.contextPath}/landlord/reservation/handle?id=<%= reservation.getId() %>&action=confirm"
class="btn btn-success">确认</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/handle?id=<%= reservation.getId() %>&action=reject"
class="btn btn-danger">拒绝</a>
<% } else { %>
<span class="btn-disabled">已处理</span>
<% } %>
</td>
</tr>
<%
}
%>
</tbody>
</table>
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">📅</div>
<p>暂无预约记录</p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -1,418 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, com.hrs.model.entity.Collection, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Collection> collectionList = (List<Collection>) request.getAttribute("collectionList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>我的收藏 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.collection-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.collection-card {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s;
position: relative;
}
.collection-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.house-image {
height: 200px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
}
.house-image .no-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 14px;
}
.house-info {
padding: 15px;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.house-title a {
color: #333;
text-decoration: none;
}
.house-title a:hover {
color: #4CAF50;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 8px;
}
.house-price {
color: #f44336;
font-size: 20px;
font-weight: bold;
margin: 10px 0;
}
.house-price span {
font-size: 14px;
font-weight: normal;
}
.collect-time {
font-size: 12px;
color: #999;
margin-top: 10px;
}
.delete-btn {
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(0,0,0,0.6);
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 18px;
transition: background-color 0.3s;
z-index: 10;
}
.delete-btn:hover {
background-color: #f44336;
}
.empty-state {
text-align: center;
padding: 80px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 20px;
font-size: 16px;
}
.empty-state a {
color: #4CAF50;
text-decoration: none;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.toast {
position: fixed;
top: 80px;
right: 20px;
padding: 12px 24px;
border-radius: 4px;
color: white;
z-index: 1000;
animation: slideIn 0.3s ease;
}
.toast-success {
background-color: #4CAF50;
}
.toast-error {
background-color: #f44336;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list" class="active">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span>我的收藏</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📚 共收藏 <strong id="totalCount"><%= totalCount %></strong> 套房源
</div>
<div class="collection-list" id="collectionList">
<%
if (collectionList != null && !collectionList.isEmpty()) {
for (Collection collection : collectionList) {
House house = collection.getHouse();
if (house != null) {
%>
<div class="collection-card" data-id="<%= collection.getId() %>">
<div class="house-image" style="background-image: url('<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
<button class="delete-btn" onclick="deleteCollection(<%= collection.getId() %>)" title="取消收藏">×</button>
<div class="house-info">
<div class="house-title">
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">
<%= house.getTitle() %>
</a>
</div>
<div class="house-detail">
📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %>
</div>
<div class="house-price">
¥<%= house.getRentPrice() %><span>/月</span>
</div>
<div class="collect-time">
收藏时间:<%= collection.getCollectTime() %>
</div>
</div>
</div>
<%
}
}
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">❤️</div>
<p>还没有收藏任何房源</p>
<p><a href="${pageContext.request.contextPath}/user/house/list">去浏览房源 &gt;&gt;</a></p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// AJAX删除收藏
function deleteCollection(id) {
if (confirm('确定要取消收藏这套房源吗?')) {
// 发送AJAX请求
var xhr = new XMLHttpRequest();
xhr.open('GET', '${pageContext.request.contextPath}/user/collection/delete?id=' + id + '&ajax=true&from=list', true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = JSON.parse(xhr.responseText);
if (result.success) {
// 移除DOM元素
var card = document.querySelector('.collection-card[data-id="' + id + '"]');
if (card) {
card.remove();
}
// 更新计数
var totalSpan = document.getElementById('totalCount');
var currentCount = parseInt(totalSpan.innerText);
totalSpan.innerText = currentCount - 1;
// 显示成功提示
showToast('取消收藏成功', 'success');
// 检查是否还有收藏
var remainingCards = document.querySelectorAll('.collection-card');
if (remainingCards.length == 0) {
location.reload(); // 刷新页面显示空状态
}
} else {
showToast(result.msg || '取消收藏失败', 'error');
}
}
};
xhr.send();
}
}
// 显示提示消息
function showToast(msg, type) {
var toast = document.createElement('div');
toast.className = 'toast toast-' + type;
toast.innerHTML = msg;
document.body.appendChild(toast);
setTimeout(function() {
toast.remove();
}, 2000);
}
</script>
</body>
</html>

View File

@@ -1,597 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
House house = (House) request.getAttribute("house");
if (house == null) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><%= house.getTitle() %> - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
/* 头部导航 */
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
/* 面包屑导航 */
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.breadcrumb a:hover {
text-decoration: underline;
}
/* 主要内容区 */
.main-content {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
/* 图片区域 */
.house-images {
padding: 20px;
border-bottom: 1px solid #eee;
}
.main-image {
width: 100%;
height: 400px;
background-color: #f0f0f0;
background-size: cover;
background-position: center;
border-radius: 8px;
margin-bottom: 15px;
}
.thumb-images {
display: flex;
gap: 10px;
overflow-x: auto;
}
.thumb {
width: 80px;
height: 60px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
border-radius: 4px;
cursor: pointer;
border: 2px solid transparent;
}
.thumb:hover {
border-color: #4CAF50;
}
.thumb.active {
border-color: #4CAF50;
}
.no-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 24px;
}
/* 房源信息 */
.house-info {
padding: 20px;
}
.house-title {
font-size: 28px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
}
.price-box {
background-color: #fff3e0;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.price {
font-size: 32px;
color: #f44336;
font-weight: bold;
}
.price span {
font-size: 16px;
font-weight: normal;
}
.info-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 20px;
padding: 15px 0;
border-bottom: 1px solid #eee;
}
.info-item {
text-align: center;
}
.info-label {
font-size: 12px;
color: #999;
margin-bottom: 5px;
}
.info-value {
font-size: 18px;
font-weight: bold;
color: #333;
}
.section {
margin-bottom: 25px;
}
.section-title {
font-size: 18px;
font-weight: bold;
padding-bottom: 10px;
margin-bottom: 15px;
border-bottom: 2px solid #4CAF50;
}
.facility-list {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.facility-item {
background-color: #f5f5f5;
padding: 5px 15px;
border-radius: 20px;
font-size: 14px;
color: #666;
}
.description {
line-height: 1.8;
color: #666;
background-color: #f9f9f9;
padding: 15px;
border-radius: 8px;
}
/* 房东信息 */
.landlord-card {
background-color: #f9f9f9;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.landlord-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.landlord-info {
display: flex;
align-items: center;
gap: 20px;
}
.landlord-avatar {
width: 60px;
height: 60px;
background-color: #4CAF50;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
}
.landlord-details p {
margin: 5px 0;
color: #666;
}
/* 操作按钮 */
.action-buttons {
display: flex;
gap: 15px;
margin-top: 20px;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
text-decoration: none;
display: inline-block;
text-align: center;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-secondary {
background-color: #ff9800;
color: white;
}
.btn-secondary:hover {
background-color: #fb8c00;
}
.btn-outline {
background-color: transparent;
border: 1px solid #4CAF50;
color: #4CAF50;
}
.btn-outline:hover {
background-color: #4CAF50;
color: white;
}
/* 页脚 */
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
/* 消息提示 */
.message {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
border-radius: 4px;
color: white;
z-index: 1000;
animation: slideIn 0.3s ease;
}
.message-success {
background-color: #4CAF50;
}
.message-error {
background-color: #f44336;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<!-- 头部导航 -->
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<!-- 面包屑导航 -->
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span><%= house.getTitle() %></span>
</div>
<div class="main-content">
<!-- 图片区域 -->
<div class="house-images">
<div class="main-image" id="mainImage" style="background-image: url('<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
<div class="thumb-images" id="thumbImages">
<%
String[] images = null;
if (house.getImgUrl() != null && !house.getImgUrl().isEmpty()) {
images = house.getImgUrl().split(",");
for (int i = 0; i < images.length; i++) {
%>
<div class="thumb" style="background-image: url('<%= images[i] %>');" onclick="changeImage('<%= images[i] %>', this)"></div>
<%
}
}
%>
</div>
</div>
<!-- 房源信息 -->
<div class="house-info">
<div class="house-title"><%= house.getTitle() %></div>
<div class="price-box">
<span class="price">¥<%= house.getRentPrice() %><span>/月</span></span>
</div>
<div class="info-grid">
<div class="info-item">
<div class="info-label">户型</div>
<div class="info-value"><%= house.getHouseType() != null ? house.getHouseType() : "待定" %></div>
</div>
<div class="info-item">
<div class="info-label">面积</div>
<div class="info-value"><%= house.getArea() != null ? house.getArea() : "待定" %></div>
</div>
<div class="info-item">
<div class="info-label">租赁类型</div>
<div class="info-value"><%= house.getRentTypeText() %></div>
</div>
<div class="info-item">
<div class="info-label">房源编号</div>
<div class="info-value"><%= house.getHouseNo() %></div>
</div>
</div>
<div class="section">
<div class="section-title">📍 房源地址</div>
<p style="color: #666;"><%= house.getAddress() != null ? house.getAddress() : "暂无地址信息" %></p>
</div>
<div class="section">
<div class="section-title">🛋️ 配套设施</div>
<div class="facility-list">
<%
String facility = house.getFacility();
if (facility != null && !facility.isEmpty()) {
String[] facilities = facility.split(",");
for (String f : facilities) {
%>
<span class="facility-item"><%= f.trim() %></span>
<%
}
} else {
%>
<span class="facility-item">暂无设施信息</span>
<%
}
%>
</div>
</div>
<div class="section">
<div class="section-title">📝 房源描述</div>
<div class="description">
<%= house.getDescription() != null ? house.getDescription() : "暂无描述信息" %>
</div>
</div>
</div>
</div>
<!-- 房东信息 -->
<div class="landlord-card">
<div class="landlord-title">👤 房东信息</div>
<div class="landlord-info">
<div class="landlord-avatar">👨</div>
<div class="landlord-details">
<p><strong><%= house.getLandlordName() != null ? house.getLandlordName() : "房东" %></strong></p>
<p>📞 <%= house.getLandlordPhone() != null ? house.getLandlordPhone() : "暂无联系方式" %></p>
<p>⭐ 认证房东 · 已发布房源</p>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<button class="btn btn-primary" onclick="makeReservation()">📅 预约看房</button>
<%
// 检查是否已收藏
com.hrs.model.dao.CollectionDAO collectionDAO = new com.hrs.model.dao.CollectionDAO();
boolean isCollected = collectionDAO.isCollected(loginUser.getId(), house.getId());
if (isCollected) {
%>
<button class="btn btn-outline" onclick="cancelCollection(<%= house.getId() %>)">❤️ 已收藏</button>
<%
} else {
%>
<button class="btn btn-secondary" onclick="addToCollection()">❤️ 收藏房源</button>
<%
}
%>
<a href="${pageContext.request.contextPath}/user/house/list" class="btn btn-outline">🔙 返回列表</a>
</div>
</div>
<!-- 页脚 -->
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// 切换主图
function changeImage(url, element) {
document.getElementById('mainImage').style.backgroundImage = 'url(' + url + ')';
// 移除所有active类
var thumbs = document.querySelectorAll('.thumb');
thumbs.forEach(function(thumb) {
thumb.classList.remove('active');
});
// 添加active类到当前
element.classList.add('active');
}
// 预约看房
function makeReservation() {
var houseId = <%= house.getId() %>;
// 跳转到预约页面
window.location.href = '${pageContext.request.contextPath}/user/reservation/add?houseId=' + houseId;
}
// 收藏房源暂时用alert后面实现真正的收藏功能
function addToCollection() {
var houseId = <%= house.getId() %>;
// 发送AJAX请求收藏
var xhr = new XMLHttpRequest();
xhr.open('POST', '${pageContext.request.contextPath}/user/collection/add', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = xhr.responseText;
if (result == 'success') {
showMessage('收藏成功!', 'success');
// 刷新页面更新按钮状态
setTimeout(function() {
location.reload();
}, 1000);
} else if (result == 'exists') {
showMessage('您已经收藏过这套房源了', 'error');
} else {
showMessage('收藏失败,请稍后重试', 'error');
}
}
};
xhr.send('houseId=' + houseId);
}
// 取消收藏
function cancelCollection(houseId) {
if (confirm('确定要取消收藏这套房源吗?')) {
window.location.href = '${pageContext.request.contextPath}/user/collection/deleteByHouse?houseId=' + houseId;
}
}
// 显示提示消息
function showMessage(msg, type) {
var messageDiv = document.createElement('div');
messageDiv.className = 'message message-' + type;
messageDiv.innerHTML = msg;
document.body.appendChild(messageDiv);
setTimeout(function() {
messageDiv.remove();
}, 3000);
}
// 如果有缩略图,默认第一个高亮
var firstThumb = document.querySelector('.thumb');
if (firstThumb) {
firstThumb.classList.add('active');
}
</script>
</body>
</html>

View File

@@ -1,412 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<House> houseList = (List<House>) request.getAttribute("houseList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>房源列表 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
/* 头部导航 */
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
/* 搜索栏 */
.search-bar {
background-color: white;
padding: 20px 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.search-form {
display: flex;
gap: 15px;
align-items: flex-end;
flex-wrap: wrap;
}
.search-group {
display: flex;
flex-direction: column;
}
.search-group label {
font-size: 12px;
color: #666;
margin-bottom: 5px;
}
.search-group input, .search-group select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 150px;
}
.search-btn {
background-color: #4CAF50;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
cursor: pointer;
}
.search-btn:hover {
background-color: #45a049;
}
.reset-btn {
background-color: #999;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
/* 房源列表 */
.house-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.house-card {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.house-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.house-image {
height: 200px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
position: relative;
}
.house-image .no-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 14px;
}
.house-info {
padding: 15px;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
.house-title a {
color: #333;
text-decoration: none;
}
.house-title a:hover {
color: #4CAF50;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 8px;
}
.house-price {
color: #f44336;
font-size: 22px;
font-weight: bold;
margin: 10px 0;
}
.house-price span {
font-size: 14px;
font-weight: normal;
}
.house-tags {
margin: 10px 0;
}
.tag {
display: inline-block;
background-color: #f0f0f0;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
margin-right: 5px;
color: #666;
}
.house-footer {
border-top: 1px solid #eee;
padding-top: 10px;
margin-top: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.landlord-info {
font-size: 12px;
color: #999;
}
.btn {
padding: 5px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-outline {
background-color: transparent;
border: 1px solid #4CAF50;
color: #4CAF50;
}
.btn-outline:hover {
background-color: #4CAF50;
color: white;
}
/* 统计信息 */
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 10px;
}
/* 页脚 */
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<!-- 头部导航 -->
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="#">我的预约</a>
<a href="#">我的订单</a>
</div>
</div>
</div>
<!-- 搜索栏 -->
<div class="search-bar">
<div class="container">
<form class="search-form" action="${pageContext.request.contextPath}/user/house/list" method="get">
<div class="search-group">
<label>区域</label>
<select name="area">
<option value="">全部区域</option>
<option value="朝阳区" <%= "朝阳区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>朝阳区</option>
<option value="海淀区" <%= "海淀区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>海淀区</option>
<option value="东城区" <%= "东城区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>东城区</option>
<option value="西城区" <%= "西城区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>西城区</option>
<option value="丰台区" <%= "丰台区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>丰台区</option>
</select>
</div>
<div class="search-group">
<label>最低租金(元)</label>
<input type="number" name="minPrice" placeholder="不限" value="<%= request.getAttribute("minPrice") != null ? request.getAttribute("minPrice") : "" %>">
</div>
<div class="search-group">
<label>最高租金(元)</label>
<input type="number" name="maxPrice" placeholder="不限" value="<%= request.getAttribute("maxPrice") != null ? request.getAttribute("maxPrice") : "" %>">
</div>
<button type="submit" class="search-btn">搜索</button>
<a href="${pageContext.request.contextPath}/user/house/list" class="reset-btn">重置</a>
</form>
</div>
</div>
<div class="container">
<!-- 统计信息 -->
<div class="stats">
📊 共找到 <strong><%= totalCount %></strong> 套房源
</div>
<!-- 房源列表 -->
<div class="house-list">
<%
if (houseList != null && !houseList.isEmpty()) {
for (House house : houseList) {
%>
<div class="house-card">
<div class="house-image" style="background-image: url('<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
<div class="house-info">
<div class="house-title">
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">
<%= house.getTitle() %>
</a>
</div>
<div class="house-detail">
📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %>
</div>
<div class="house-price">
¥<%= house.getRentPrice() %><span>/月</span>
</div>
<div class="house-tags">
<span class="tag">#<%= house.getHouseType() %></span>
<span class="tag">#<%= house.getArea() %></span>
</div>
<div class="house-footer">
<div class="landlord-info">
👤 <%= house.getLandlordName() != null ? house.getLandlordName() : "房东" %>
</div>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="btn btn-primary">
查看详情
</a>
</div>
</div>
</div>
<%
}
} else {
%>
<div class="empty-state">
<div style="font-size: 48px;">🏠</div>
<p>暂无房源,请稍后再来</p>
</div>
<%
}
%>
</div>
</div>
<!-- 页脚 -->
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -1,5 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
// 重定向到房源列表页
response.sendRedirect(request.getContextPath() + "/user/house/list");
%>

View File

@@ -1,151 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房系统登录</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.login-container {
background-color: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
width: 360px;
}
h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #666;
font-weight: bold;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
font-size: 14px;
}
.role-group {
display: flex;
justify-content: space-around;
margin: 10px 0;
}
.role-group label {
display: inline;
font-weight: normal;
}
button {
width: 100%;
padding: 12px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #45a049;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.register-link {
text-align: center;
margin-top: 20px;
color: #666;
}
.register-link a {
color: #4CAF50;
text-decoration: none;
}
.register-link a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="login-container">
<h2>租房系统登录</h2>
<%-- 显示错误信息 --%>
<%
String error = (String)request.getAttribute("error");
if(error != null) {
%>
<div class="error"><%= error %></div>
<%
}
%>
<%-- 显示成功信息(如注册成功) --%>
<%
String message = (String)request.getAttribute("message");
if(message != null) {
%>
<div class="message" style="background-color:#e8f5e9; color:#2e7d32; padding:10px; border-radius:4px; margin-bottom:20px; text-align:center;">
<%= message %>
</div>
<%
}
%>
<form action="${pageContext.request.contextPath}/user/login" method="post">
<div class="form-group">
<label>用户名:</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" name="password" required>
</div>
<div class="form-group">
<label>登录角色:</label>
<div class="role-group">
<label>
<input type="radio" name="roleType" value="0" checked> 租客
</label>
<label>
<input type="radio" name="roleType" value="1"> 房东
</label>
<label>
<input type="radio" name="roleType" value="2"> 管理员
</label>
</div>
</div>
<button type="submit">登录</button>
</form>
<div class="register-link">
还没有账号? <a href="${pageContext.request.contextPath}/user/register">立即注册</a>
</div>
</div>
</body>
</html>

View File

@@ -1,218 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房系统注册</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
}
.register-container {
background-color: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
width: 450px;
}
h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #666;
font-weight: bold;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
font-size: 14px;
}
.role-group {
display: flex;
gap: 20px;
margin: 10px 0;
}
.role-group label {
display: inline;
font-weight: normal;
}
button {
width: 100%;
padding: 12px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #45a049;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.login-link {
text-align: center;
margin-top: 20px;
color: #666;
}
.login-link a {
color: #4CAF50;
text-decoration: none;
}
.login-link a:hover {
text-decoration: underline;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.required {
color: #f44336;
}
</style>
</head>
<body>
<div class="register-container">
<h2>租房系统注册</h2>
<%-- 显示错误信息 --%>
<%
String error = (String)request.getAttribute("error");
if(error != null) {
%>
<div class="error"><%= error %></div>
<%
}
%>
<%-- 显示成功信息 --%>
<%
String message = (String)request.getAttribute("message");
if(message != null) {
%>
<div class="message"><%= message %></div>
<%
}
%>
<form action="${pageContext.request.contextPath}/user/register" method="post">
<div class="form-group">
<label>用户名 <span class="required">*</span></label>
<input type="text" name="username" placeholder="请输入用户名" required>
</div>
<div class="form-group">
<label>密码 <span class="required">*</span></label>
<input type="password" name="password" placeholder="请输入密码" required>
<div class="hint">密码长度6-20位建议使用字母+数字组合</div>
</div>
<div class="form-group">
<label>确认密码 <span class="required">*</span></label>
<input type="password" name="confirmPassword" placeholder="请再次输入密码" required>
</div>
<div class="form-group">
<label>真实姓名</label>
<input type="text" name="realName" placeholder="请输入真实姓名">
</div>
<div class="form-group">
<label>手机号 <span class="required">*</span></label>
<input type="text" name="phone" placeholder="请输入11位手机号" required>
</div>
<div class="form-group">
<label>身份证号</label>
<input type="text" name="idCard" placeholder="请输入身份证号(可选)">
<div class="hint">房东认证需要提供身份证号</div>
</div>
<div class="form-group">
<label>注册角色 <span class="required">*</span></label>
<div class="role-group">
<label>
<input type="radio" name="roleType" value="0" checked> 租客
</label>
<label>
<input type="radio" name="roleType" value="1"> 房东
</label>
</div>
<div class="hint">房东需要管理员审核认证后才能发布房源</div>
</div>
<button type="submit">立即注册</button>
</form>
<div class="login-link">
已有账号? <a href="${pageContext.request.contextPath}/user/login">返回登录</a>
</div>
</div>
<script>
// 简单的前端验证
document.querySelector('form').addEventListener('submit', function(e) {
var password = document.querySelector('input[name="password"]').value;
var confirm = document.querySelector('input[name="confirmPassword"]').value;
var phone = document.querySelector('input[name="phone"]').value;
// 验证密码长度
if(password.length < 6 || password.length > 20) {
alert('密码长度应为6-20位');
e.preventDefault();
return false;
}
// 验证两次密码
if(password !== confirm) {
alert('两次输入的密码不一致');
e.preventDefault();
return false;
}
// 验证手机号
var phoneReg = /^1[3-9]\d{9}$/;
if(!phoneReg.test(phone)) {
alert('请输入正确的11位手机号');
e.preventDefault();
return false;
}
});
</script>
</body>
</html>

View File

@@ -1,313 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
House house = (House) request.getAttribute("house");
if (house == null) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
String error = (String) request.getAttribute("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>预约看房 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 800px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.main-card {
background-color: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.house-summary {
background-color: #f9f9f9;
padding: 15px;
border-radius: 8px;
margin-bottom: 25px;
display: flex;
gap: 15px;
}
.house-img {
width: 100px;
height: 100px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
border-radius: 8px;
}
.house-info {
flex: 1;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 5px;
}
.house-price {
color: #f44336;
font-size: 18px;
font-weight: bold;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #333;
}
input[type="date"],
input[type="time"],
textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
textarea {
resize: vertical;
min-height: 80px;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-secondary {
background-color: #999;
color: white;
margin-left: 10px;
}
.btn-secondary:hover {
background-color: #777;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">房源详情</a> &gt;
<span>预约看房</span>
</div>
<div class="main-card">
<h2 style="margin-bottom: 20px;">📅 预约看房</h2>
<% if (error != null) { %>
<div class="error"><%= error %></div>
<% } %>
<!-- 房源信息摘要 -->
<div class="house-summary">
<div class="house-img" style="background-image: url('<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;">🏠</div>
<% } %>
</div>
<div class="house-info">
<div class="house-title"><%= house.getTitle() %></div>
<div class="house-detail">📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %></div>
<div class="house-price">¥<%= house.getRentPrice() %>/月</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/user/reservation/add" method="post">
<input type="hidden" name="houseId" value="<%= house.getId() %>">
<div class="form-group">
<label>选择看房日期 *</label>
<input type="date" name="reserveDate" required min="<%= new java.text.SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date()) %>">
<div class="hint">请选择您方便的看房日期</div>
</div>
<div class="form-group">
<label>选择看房时间 *</label>
<input type="time" name="reserveTime" required>
<div class="hint">建议选择 9:00-18:00 之间的时间</div>
</div>
<div class="form-group">
<label>备注信息(可选)</label>
<textarea name="remark" placeholder="请输入您的特殊需求或问题..."></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交预约</button>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="btn btn-secondary">返回</a>
</div>
</form>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// 设置日期最小值
var dateInput = document.querySelector('input[type="date"]');
var today = new Date();
var yyyy = today.getFullYear();
var mm = String(today.getMonth() + 1).padStart(2, '0');
var dd = String(today.getDate()).padStart(2, '0');
dateInput.min = yyyy + '-' + mm + '-' + dd;
// 设置时间默认值
var timeInput = document.querySelector('input[type="time"]');
timeInput.value = "10:00";
</script>
</body>
</html>

View File

@@ -1,347 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.Reservation, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Reservation> reservationList = (List<Reservation>) request.getAttribute("reservationList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>我的预约 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.reservation-table {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.status-confirmed {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-cancelled {
background-color: #ffebee;
color: #f44336;
}
.status-completed {
background-color: #e3f2fd;
color: #2196F3;
}
.house-link {
color: #4CAF50;
text-decoration: none;
}
.house-link:hover {
text-decoration: underline;
}
.btn {
padding: 4px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
}
.btn-danger {
background-color: #f44336;
color: white;
}
.btn-danger:hover {
background-color: #d32f2f;
}
.btn-disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 20px;
}
.empty-state a {
color: #4CAF50;
text-decoration: none;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list" class="active">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span>我的预约</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📋 共有 <strong><%= totalCount %></strong> 条预约记录
</div>
<div class="reservation-table">
<%
if (reservationList != null && !reservationList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>房源信息</th>
<th>预约时间</th>
<th>状态</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
for (Reservation reservation : reservationList) {
House house = reservation.getHouse();
%>
<tr>
<td>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="house-link">
<strong><%= house.getTitle() %></strong>
</a><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %> | ¥<%= house.getRentPrice() %>/月</span>
</td>
<td><%= sdf.format(reservation.getReserveTime()) %></td>
<td>
<span class="status-badge status-<%= reservation.getStatus().equals("0") ? "pending" : reservation.getStatus().equals("1") ? "confirmed" : reservation.getStatus().equals("2") ? "cancelled" : "completed" %>">
<%= reservation.getStatusText() %>
</span>
</td>
<td style="max-width:200px; overflow:hidden; text-overflow:ellipsis;">
<%= reservation.getRemark() != null ? reservation.getRemark() : "-" %>
</td>
<td>
<% if ("0".equals(reservation.getStatus())) { %>
<a href="${pageContext.request.contextPath}/user/reservation/cancel?id=<%= reservation.getId() %>"
class="btn btn-danger"
onclick="return confirm('确定要取消这个预约吗?')">取消</a>
<% } else { %>
<span class="btn btn-disabled">已处理</span>
<% } %>
</td>
</tr>
<%
}
%>
</tbody>
</table>
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">📅</div>
<p>还没有任何预约记录</p>
<p><a href="${pageContext.request.contextPath}/user/house/list">去浏览房源 &gt;&gt;</a></p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More