no message
This commit is contained in:
17
HRS/.classpath
Normal file
17
HRS/.classpath
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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>
|
||||
31
HRS/.project
Normal file
31
HRS/.project
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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>
|
||||
12
HRS/.settings/.jsdtscope
Normal file
12
HRS/.settings/.jsdtscope
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
7
HRS/.settings/org.eclipse.jdt.core.prefs
Normal file
7
HRS/.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
8
HRS/.settings/org.eclipse.wst.common.component
Normal file
8
HRS/.settings/org.eclipse.wst.common.component
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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>
|
||||
10
HRS/.settings/org.eclipse.wst.common.project.facet.core.xml
Normal file
10
HRS/.settings/org.eclipse.wst.common.project.facet.core.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?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>
|
||||
@@ -0,0 +1 @@
|
||||
org.eclipse.wst.jsdt.launching.baseBrowserLibrary
|
||||
1
HRS/.settings/org.eclipse.wst.jsdt.ui.superType.name
Normal file
1
HRS/.settings/org.eclipse.wst.jsdt.ui.superType.name
Normal file
@@ -0,0 +1 @@
|
||||
Window
|
||||
17
HRS/HRS/.classpath
Normal file
17
HRS/HRS/.classpath
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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>
|
||||
31
HRS/HRS/.project
Normal file
31
HRS/HRS/.project
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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>
|
||||
12
HRS/HRS/.settings/.jsdtscope
Normal file
12
HRS/HRS/.settings/.jsdtscope
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
7
HRS/HRS/.settings/org.eclipse.jdt.core.prefs
Normal file
7
HRS/HRS/.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
8
HRS/HRS/.settings/org.eclipse.wst.common.component
Normal file
8
HRS/HRS/.settings/org.eclipse.wst.common.component
Normal file
@@ -0,0 +1,8 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?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>
|
||||
@@ -0,0 +1 @@
|
||||
org.eclipse.wst.jsdt.launching.baseBrowserLibrary
|
||||
1
HRS/HRS/.settings/org.eclipse.wst.jsdt.ui.superType.name
Normal file
1
HRS/HRS/.settings/org.eclipse.wst.jsdt.ui.superType.name
Normal file
@@ -0,0 +1 @@
|
||||
Window
|
||||
3
HRS/HRS/WebContent/META-INF/MANIFEST.MF
Normal file
3
HRS/HRS/WebContent/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
||||
BIN
HRS/HRS/WebContent/WEB-INF/lib/mysql-connector-java-5.1.49.jar
Normal file
BIN
HRS/HRS/WebContent/WEB-INF/lib/mysql-connector-java-5.1.49.jar
Normal file
Binary file not shown.
29
HRS/HRS/WebContent/WEB-INF/web.xml
Normal file
29
HRS/HRS/WebContent/WEB-INF/web.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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>
|
||||
20
HRS/HRS/WebContent/jsp/admin/index.jsp
Normal file
20
HRS/HRS/WebContent/jsp/admin/index.jsp
Normal file
@@ -0,0 +1,20 @@
|
||||
<%@ 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>
|
||||
20
HRS/HRS/WebContent/jsp/landlord/index.jsp
Normal file
20
HRS/HRS/WebContent/jsp/landlord/index.jsp
Normal file
@@ -0,0 +1,20 @@
|
||||
<%@ 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>
|
||||
324
HRS/HRS/WebContent/jsp/landlord/reservation_list.jsp
Normal file
324
HRS/HRS/WebContent/jsp/landlord/reservation_list.jsp
Normal file
@@ -0,0 +1,324 @@
|
||||
<%@ 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> >
|
||||
<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>
|
||||
418
HRS/HRS/WebContent/jsp/user/collection_list.jsp
Normal file
418
HRS/HRS/WebContent/jsp/user/collection_list.jsp
Normal file
@@ -0,0 +1,418 @@
|
||||
<%@ 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> >
|
||||
<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">去浏览房源 >></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>
|
||||
597
HRS/HRS/WebContent/jsp/user/house_detail.jsp
Normal file
597
HRS/HRS/WebContent/jsp/user/house_detail.jsp
Normal file
@@ -0,0 +1,597 @@
|
||||
<%@ 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> >
|
||||
<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>
|
||||
412
HRS/HRS/WebContent/jsp/user/house_list.jsp
Normal file
412
HRS/HRS/WebContent/jsp/user/house_list.jsp
Normal file
@@ -0,0 +1,412 @@
|
||||
<%@ 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>
|
||||
5
HRS/HRS/WebContent/jsp/user/index.jsp
Normal file
5
HRS/HRS/WebContent/jsp/user/index.jsp
Normal file
@@ -0,0 +1,5 @@
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%
|
||||
// 重定向到房源列表页
|
||||
response.sendRedirect(request.getContextPath() + "/user/house/list");
|
||||
%>
|
||||
151
HRS/HRS/WebContent/jsp/user/login.jsp
Normal file
151
HRS/HRS/WebContent/jsp/user/login.jsp
Normal file
@@ -0,0 +1,151 @@
|
||||
<%@ 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>
|
||||
218
HRS/HRS/WebContent/jsp/user/register.jsp
Normal file
218
HRS/HRS/WebContent/jsp/user/register.jsp
Normal file
@@ -0,0 +1,218 @@
|
||||
<%@ 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>
|
||||
313
HRS/HRS/WebContent/jsp/user/reservation_add.jsp
Normal file
313
HRS/HRS/WebContent/jsp/user/reservation_add.jsp
Normal file
@@ -0,0 +1,313 @@
|
||||
<%@ 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> >
|
||||
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">房源详情</a> >
|
||||
<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>
|
||||
347
HRS/HRS/WebContent/jsp/user/reservation_list.jsp
Normal file
347
HRS/HRS/WebContent/jsp/user/reservation_list.jsp
Normal file
@@ -0,0 +1,347 @@
|
||||
<%@ 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> >
|
||||
<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">去浏览房源 >></a></p>
|
||||
</div>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>© 2026 租房系统 | 让租房更简单</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/filter/EncodingFilter.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/filter/EncodingFilter.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/filter/LoginFilter.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/filter/LoginFilter.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/model/dao/CollectionDAO.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/model/dao/CollectionDAO.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/model/dao/HouseDAO.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/model/dao/HouseDAO.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/model/dao/ReservationDAO.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/model/dao/ReservationDAO.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/model/dao/UserDAO.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/model/dao/UserDAO.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/model/entity/Collection.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/model/entity/Collection.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/model/entity/House.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/model/entity/House.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/model/entity/Reservation.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/model/entity/Reservation.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/model/entity/User.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/model/entity/User.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/util/DBUtil.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/util/DBUtil.class
Normal file
Binary file not shown.
BIN
HRS/HRS/build/classes/com/hrs/util/MD5Util.class
Normal file
BIN
HRS/HRS/build/classes/com/hrs/util/MD5Util.class
Normal file
Binary file not shown.
170
HRS/HRS/sql/hrs_init.sql
Normal file
170
HRS/HRS/sql/hrs_init.sql
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
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;
|
||||
@@ -0,0 +1,60 @@
|
||||
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=操作失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
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=取消收藏失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
63
HRS/HRS/src/com/hrs/controller/user/HouseListController.java
Normal file
63
HRS/HRS/src/com/hrs/controller/user/HouseListController.java
Normal file
@@ -0,0 +1,63 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
106
HRS/HRS/src/com/hrs/controller/user/LoginController.java
Normal file
106
HRS/HRS/src/com/hrs/controller/user/LoginController.java
Normal file
@@ -0,0 +1,106 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
38
HRS/HRS/src/com/hrs/controller/user/LogoutController.java
Normal file
38
HRS/HRS/src/com/hrs/controller/user/LogoutController.java
Normal file
@@ -0,0 +1,38 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
120
HRS/HRS/src/com/hrs/controller/user/RegisterController.java
Normal file
120
HRS/HRS/src/com/hrs/controller/user/RegisterController.java
Normal file
@@ -0,0 +1,120 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
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=取消预约失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
37
HRS/HRS/src/com/hrs/filter/EncodingFilter.java
Normal file
37
HRS/HRS/src/com/hrs/filter/EncodingFilter.java
Normal file
@@ -0,0 +1,37 @@
|
||||
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() {
|
||||
}
|
||||
}
|
||||
93
HRS/HRS/src/com/hrs/filter/LoginFilter.java
Normal file
93
HRS/HRS/src/com/hrs/filter/LoginFilter.java
Normal file
@@ -0,0 +1,93 @@
|
||||
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("登录过滤器销毁");
|
||||
}
|
||||
}
|
||||
204
HRS/HRS/src/com/hrs/model/dao/CollectionDAO.java
Normal file
204
HRS/HRS/src/com/hrs/model/dao/CollectionDAO.java
Normal file
@@ -0,0 +1,204 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
397
HRS/HRS/src/com/hrs/model/dao/HouseDAO.java
Normal file
397
HRS/HRS/src/com/hrs/model/dao/HouseDAO.java
Normal file
@@ -0,0 +1,397 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
286
HRS/HRS/src/com/hrs/model/dao/ReservationDAO.java
Normal file
286
HRS/HRS/src/com/hrs/model/dao/ReservationDAO.java
Normal file
@@ -0,0 +1,286 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
399
HRS/HRS/src/com/hrs/model/dao/UserDAO.java
Normal file
399
HRS/HRS/src/com/hrs/model/dao/UserDAO.java
Normal file
@@ -0,0 +1,399 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
85
HRS/HRS/src/com/hrs/model/entity/Collection.java
Normal file
85
HRS/HRS/src/com/hrs/model/entity/Collection.java
Normal file
@@ -0,0 +1,85 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
226
HRS/HRS/src/com/hrs/model/entity/House.java
Normal file
226
HRS/HRS/src/com/hrs/model/entity/House.java
Normal file
@@ -0,0 +1,226 @@
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
174
HRS/HRS/src/com/hrs/model/entity/Reservation.java
Normal file
174
HRS/HRS/src/com/hrs/model/entity/Reservation.java
Normal file
@@ -0,0 +1,174 @@
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
186
HRS/HRS/src/com/hrs/model/entity/User.java
Normal file
186
HRS/HRS/src/com/hrs/model/entity/User.java
Normal file
@@ -0,0 +1,186 @@
|
||||
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 + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
91
HRS/HRS/src/com/hrs/util/DBUtil.java
Normal file
91
HRS/HRS/src/com/hrs/util/DBUtil.java
Normal file
@@ -0,0 +1,91 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
80
HRS/HRS/src/com/hrs/util/MD5Util.java
Normal file
80
HRS/HRS/src/com/hrs/util/MD5Util.java
Normal file
@@ -0,0 +1,80 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
3
HRS/WebContent/META-INF/MANIFEST.MF
Normal file
3
HRS/WebContent/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
||||
BIN
HRS/WebContent/WEB-INF/lib/mysql-connector-java-5.1.49.jar
Normal file
BIN
HRS/WebContent/WEB-INF/lib/mysql-connector-java-5.1.49.jar
Normal file
Binary file not shown.
29
HRS/WebContent/WEB-INF/web.xml
Normal file
29
HRS/WebContent/WEB-INF/web.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?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>
|
||||
20
HRS/WebContent/jsp/admin/index.jsp
Normal file
20
HRS/WebContent/jsp/admin/index.jsp
Normal file
@@ -0,0 +1,20 @@
|
||||
<%@ 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>
|
||||
20
HRS/WebContent/jsp/landlord/index.jsp
Normal file
20
HRS/WebContent/jsp/landlord/index.jsp
Normal file
@@ -0,0 +1,20 @@
|
||||
<%@ 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>
|
||||
324
HRS/WebContent/jsp/landlord/reservation_list.jsp
Normal file
324
HRS/WebContent/jsp/landlord/reservation_list.jsp
Normal file
@@ -0,0 +1,324 @@
|
||||
<%@ 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> >
|
||||
<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>
|
||||
418
HRS/WebContent/jsp/user/collection_list.jsp
Normal file
418
HRS/WebContent/jsp/user/collection_list.jsp
Normal file
@@ -0,0 +1,418 @@
|
||||
<%@ 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> >
|
||||
<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">去浏览房源 >></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>
|
||||
597
HRS/WebContent/jsp/user/house_detail.jsp
Normal file
597
HRS/WebContent/jsp/user/house_detail.jsp
Normal file
@@ -0,0 +1,597 @@
|
||||
<%@ 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> >
|
||||
<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>
|
||||
412
HRS/WebContent/jsp/user/house_list.jsp
Normal file
412
HRS/WebContent/jsp/user/house_list.jsp
Normal file
@@ -0,0 +1,412 @@
|
||||
<%@ 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>
|
||||
5
HRS/WebContent/jsp/user/index.jsp
Normal file
5
HRS/WebContent/jsp/user/index.jsp
Normal file
@@ -0,0 +1,5 @@
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%
|
||||
// 重定向到房源列表页
|
||||
response.sendRedirect(request.getContextPath() + "/user/house/list");
|
||||
%>
|
||||
151
HRS/WebContent/jsp/user/login.jsp
Normal file
151
HRS/WebContent/jsp/user/login.jsp
Normal file
@@ -0,0 +1,151 @@
|
||||
<%@ 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>
|
||||
218
HRS/WebContent/jsp/user/register.jsp
Normal file
218
HRS/WebContent/jsp/user/register.jsp
Normal file
@@ -0,0 +1,218 @@
|
||||
<%@ 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>
|
||||
313
HRS/WebContent/jsp/user/reservation_add.jsp
Normal file
313
HRS/WebContent/jsp/user/reservation_add.jsp
Normal file
@@ -0,0 +1,313 @@
|
||||
<%@ 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> >
|
||||
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">房源详情</a> >
|
||||
<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>
|
||||
347
HRS/WebContent/jsp/user/reservation_list.jsp
Normal file
347
HRS/WebContent/jsp/user/reservation_list.jsp
Normal file
@@ -0,0 +1,347 @@
|
||||
<%@ 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> >
|
||||
<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">去浏览房源 >></a></p>
|
||||
</div>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>© 2026 租房系统 | 让租房更简单</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user