在传统房产中介行业向数字化转型的关键时期,信息管理的效率直接决定了企业的市场竞争力。针对中小型中介机构普遍存在的房源信息更新滞后、客户跟进记录分散、员工业绩统计困难等核心痛点,一套整合了业务流程与数据管理的专业化系统成为刚需。本系统采用成熟的SSH集成框架,构建了一个功能完备的房产中介中心管理平台,实现了从房源录入、客户跟踪到合同管理的全流程数字化管控。
系统采用经典的三层架构设计,确保了技术实现的清晰性与可维护性。表现层由Struts2框架负责,通过配置struts.xml文件定义请求路径与后端Action的映射关系,有效处理用户交互与页面导航。业务逻辑层依托Spring框架的IoC容器进行组件管理,通过声明式事务管理确保核心业务操作(如房源状态变更、佣金结算)的原子性与数据一致性。数据持久层则采用Hibernate实现对象关系映射,将Java实体类与数据库表无缝关联,通过HQL面向对象查询语言简化复杂数据操作,显著提升了开发效率。

数据库架构设计与核心实体模型
系统共设计13张数据表,支撑房源、客户、员工、合同等核心业务模块的运转。数据库设计遵循第三范式,注重数据的完整性与关联性。以下重点分析几个核心表的结构设计亮点。
房源信息表(t_house)的设计体现了业务模型的复杂性:
CREATE TABLE t_house (
id int(11) NOT NULL AUTO_INCREMENT,
title varchar(200) NOT NULL COMMENT '房源标题',
houseType varchar(50) NOT NULL COMMENT '户型',
price decimal(10,2) NOT NULL COMMENT '价格',
area decimal(8,2) NOT NULL COMMENT '面积',
regionId int(11) NOT NULL COMMENT '区域ID',
address varchar(300) NOT NULL COMMENT '详细地址',
description text COMMENT '房源描述',
mainPhoto varchar(200) DEFAULT NULL COMMENT '主图路径',
status int(11) NOT NULL DEFAULT '0' COMMENT '状态:0待审核 1已审核 2已下架',
userId int(11) NOT NULL COMMENT '发布用户ID',
createTime datetime NOT NULL COMMENT '创建时间',
updateTime datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (id),
KEY regionId (regionId),
KEY userId (userId),
KEY status (status),
CONSTRAINT t_house_ibfk_1 FOREIGN KEY (regionId) REFERENCES t_region (id),
CONSTRAINT t_house_ibfk_2 FOREIGN KEY (userId) REFERENCES t_user (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='房源信息表';
该表设计具有多个技术亮点:采用decimal类型精确存储价格和面积数据,避免浮点数精度问题;通过status字段实现房源生命周期管理,支持工作流状态转换;建立多字段索引(regionId, userId, status)优化查询性能;通过外键约束确保数据引用完整性,防止脏数据产生。
客户跟进记录表(t_customer_follow)的设计聚焦于业务跟踪的时效性:
CREATE TABLE t_customer_follow (
id int(11) NOT NULL AUTO_INCREMENT,
customerId int(11) NOT NULL COMMENT '客户ID',
houseId int(11) DEFAULT NULL COMMENT '关联房源ID',
followType int(11) NOT NULL COMMENT '跟进类型:1电话联系 2带看 3报价',
content text NOT NULL COMMENT '跟进内容',
nextFollowTime datetime DEFAULT NULL COMMENT '下次跟进时间',
userId int(11) NOT NULL COMMENT '跟进人ID',
createTime datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (id),
KEY customerId (customerId),
KEY houseId (houseId),
KEY userId (userId),
KEY followType (followType),
CONSTRAINT t_customer_follow_ibfk_1 FOREIGN KEY (customerId) REFERENCES t_customer (id),
CONSTRAINT t_customer_follow_ibfk_2 FOREIGN KEY (houseId) REFERENCES t_house (id),
CONSTRAINT t_customer_follow_ibfk_3 FOREIGN KEY (userId) REFERENCES t_user (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='客户跟进记录表';
此表通过followType字段区分不同类型的跟进动作,为后续数据分析提供结构化基础;nextFollowTime字段支持设置下次跟进提醒,实现主动客户关系管理;多外键关联设计确保跟进记录与客户、房源、经纪人的完整关联。
核心功能模块的技术实现
1. 多条件房源搜索与分页展示
房源搜索是系统的核心功能,支持按区域、价格区间、户型等多条件组合查询。前端通过JSP页面收集用户输入的搜索条件,通过AJAX提交到后端处理。
搜索功能的Action实现:
public class HouseAction extends ActionSupport {
private HouseService houseService;
private List<House> houseList;
private int page = 1;
private int pageSize = 10;
private int totalPages;
private String regionId;
private String minPrice;
private String maxPrice;
private String houseType;
public String search() {
try {
Map<String, Object> params = new HashMap<>();
if (StringUtils.isNotBlank(regionId)) {
params.put("regionId", Integer.parseInt(regionId));
}
if (StringUtils.isNotBlank(minPrice)) {
params.put("minPrice", new BigDecimal(minPrice));
}
if (StringUtils.isNotBlank(maxPrice)) {
params.put("maxPrice", new BigDecimal(maxPrice));
}
if (StringUtils.isNotBlank(houseType)) {
params.put("houseType", houseType);
}
int totalCount = houseService.getHouseCountByParams(params);
totalPages = (int) Math.ceil((double) totalCount / pageSize);
houseList = houseService.getHousesByParams(params, page, pageSize);
return SUCCESS;
} catch (Exception e) {
addActionError("查询过程中出现错误:" + e.getMessage());
return ERROR;
}
}
// Getter和Setter方法
public HouseService getHouseService() { return houseService; }
public void setHouseService(HouseService houseService) {
this.houseService = houseService;
}
// 其他getter/setter方法...
}
Service层实现动态查询逻辑:
@Service
@Transactional
public class HouseServiceImpl implements HouseService {
@Autowired
private HouseDao houseDao;
@Override
public List<House> getHousesByParams(Map<String, Object> params, int page, int pageSize) {
StringBuilder hql = new StringBuilder("from House h where h.status = 1");
Map<String, Object> queryParams = new HashMap<>();
if (params.containsKey("regionId")) {
hql.append(" and h.region.id = :regionId");
queryParams.put("regionId", params.get("regionId"));
}
if (params.containsKey("minPrice")) {
hql.append(" and h.price >= :minPrice");
queryParams.put("minPrice", params.get("minPrice"));
}
if (params.containsKey("maxPrice")) {
hql.append(" and h.price <= :maxPrice");
queryParams.put("maxPrice", params.get("maxPrice"));
}
if (params.containsKey("houseType")) {
hql.append(" and h.houseType like :houseType");
queryParams.put("houseType", "%" + params.get("houseType") + "%");
}
hql.append(" order by h.createTime desc");
return houseDao.findByPage(hql.toString(), queryParams, page, pageSize);
}
@Override
public int getHouseCountByParams(Map<String, Object> params) {
StringBuilder hql = new StringBuilder("select count(*) from House h where h.status = 1");
Map<String, Object> queryParams = new HashMap<>();
// 构建查询条件与上述方法类似...
// 省略具体实现细节
return houseDao.getCount(hql.toString(), queryParams);
}
}

2. 房源状态工作流管理
系统实现了完整的房源生命周期管理,从创建、审核、上架到下架的状态流转。这一功能通过状态模式的设计理念实现,确保状态变更的逻辑一致性。
房源状态枚举定义:
public enum HouseStatus {
PENDING(0, "待审核"),
APPROVED(1, "已审核"),
OFF_SHELF(2, "已下架"),
SOLD(3, "已售出"),
RENTED(4, "已出租");
private int code;
private String description;
private HouseStatus(int code, String description) {
this.code = code;
this.description = description;
}
public static HouseStatus getByCode(int code) {
for (HouseStatus status : values()) {
if (status.code == code) {
return status;
}
}
return null;
}
// Getter方法...
}
房源状态变更的Service方法:
@Service
@Transactional
public class HouseWorkflowService {
@Autowired
private HouseDao houseDao;
public void changeHouseStatus(int houseId, HouseStatus newStatus, int operatorId) {
House house = houseDao.get(houseId);
if (house == null) {
throw new RuntimeException("房源不存在");
}
HouseStatus currentStatus = HouseStatus.getByCode(house.getStatus());
if (!isValidTransition(currentStatus, newStatus)) {
throw new RuntimeException("无效的状态变更:" + currentStatus + " -> " + newStatus);
}
house.setStatus(newStatus.getCode());
house.setUpdateTime(new Date());
// 记录状态变更日志
HouseStatusLog statusLog = new HouseStatusLog();
statusLog.setHouseId(houseId);
statusLog.setFromStatus(currentStatus.getCode());
statusLog.setToStatus(newStatus.getCode());
statusLog.setOperatorId(operatorId);
statusLog.setCreateTime(new Date());
houseDao.save(statusLog);
}
private boolean isValidTransition(HouseStatus from, HouseStatus to) {
// 定义允许的状态转换规则
switch (from) {
case PENDING:
return to == APPROVED || to == OFF_SHELF;
case APPROVED:
return to == OFF_SHELF || to == SOLD || to == RENTED;
case OFF_SHELF:
return to == APPROVED;
default:
return false;
}
}
}
3. 客户带看记录与跟进提醒
客户关系管理是中介业务的核心,系统通过带看记录管理和跟进提醒功能提升客户服务质量。
带看记录实体与DAO实现:
@Entity
@Table(name = "t_customer_follow")
public class CustomerFollow {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ManyToOne
@JoinColumn(name = "customerId")
private Customer customer;
@ManyToOne
@JoinColumn(name = "houseId")
private House house;
@Column(name = "followType")
private Integer followType;
@Column(name = "content")
private String content;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "nextFollowTime")
private Date nextFollowTime;
@ManyToOne
@JoinColumn(name = "userId")
private User user;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "createTime")
private Date createTime;
// Getter和Setter方法...
}
跟进提醒的定时任务实现:
@Service
public class FollowReminderService {
@Autowired
private CustomerFollowDao customerFollowDao;
@Autowired
private EmailService emailService;
@Scheduled(cron = "0 0 9 * * ?") // 每天上午9点执行
public void sendFollowReminders() {
Date now = new Date();
Date startOfDay = getStartOfDay(now);
Date endOfDay = getEndOfDay(now);
List<CustomerFollow> follows = customerFollowDao
.getFollowsByNextTimeRange(startOfDay, endOfDay);
for (CustomerFollow follow : follows) {
try {
String subject = "客户跟进提醒:" + follow.getCustomer().getName();
String content = buildReminderContent(follow);
emailService.sendEmail(follow.getUser().getEmail(), subject, content);
// 标记提醒已发送
follow.setReminderSent(true);
customerFollowDao.update(follow);
} catch (Exception e) {
logger.error("发送跟进提醒失败:" + e.getMessage(), e);
}
}
}
private String buildReminderContent(CustomerFollow follow) {
StringBuilder sb = new StringBuilder();
sb.append("尊敬的").append(follow.getUser().getRealName()).append(":\n\n");
sb.append("您有一个客户跟进任务需要处理:\n");
sb.append("客户:").append(follow.getCustomer().getName()).append("\n");
if (follow.getHouse() != null) {
sb.append("关联房源:").append(follow.getHouse().getTitle()).append("\n");
}
sb.append("跟进类型:").append(getFollowTypeName(follow.getFollowType())).append("\n");
sb.append("计划跟进时间:").append(formatDate(follow.getNextFollowTime())).append("\n");
sb.append("上次跟进内容:").append(follow.getContent()).append("\n\n");
sb.append("请及时处理!");
return sb.toString();
}
}

4. 数据统计与报表生成
系统提供多维度的数据统计分析功能,帮助管理者了解业务状况并做出决策。
房源数据统计的HQL查询实现:
@Repository
public class HouseStatDaoImpl extends BaseDaoImpl<House> implements HouseStatDao {
@Override
public List<Map<String, Object>> getHouseStatsByRegion() {
String hql = "select new map(r.name as regionName, " +
"count(h.id) as totalCount, " +
"avg(h.price) as avgPrice, " +
"min(h.price) as minPrice, " +
"max(h.price) as maxPrice) " +
"from House h join h.region r " +
"where h.status = 1 " +
"group by r.id, r.name " +
"order by totalCount desc";
return getSession().createQuery(hql).list();
}
@Override
public List<Map<String, Object>> getMonthlyTransactionStats(int year) {
String hql = "select new map(month(c.createTime) as month, " +
"count(c.id) as transactionCount, " +
"sum(c.transactionAmount) as totalAmount) " +
"from Contract c " +
"where year(c.createTime) = :year " +
"and c.status = 2 " + // 已完成的合同
"group by month(c.createTime) " +
"order by month";
return getSession().createQuery(hql)
.setParameter("year", year)
.list();
}
}

实体关系映射与业务对象模型
系统通过Hibernate注解实现了丰富的对象关系映射,支撑复杂的业务逻辑。以下展示核心实体之间的关系配置:
用户与房源的一对多关系映射:
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(unique = true, nullable = false)
private String username;
private String password;
private String realName;
private String phone;
private String email;
private Integer role; // 1管理员 2经纪人
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<House> houses = new HashSet<>();
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<CustomerFollow> customerFollows = new HashSet<>();
// 其他字段和方法...
}
区域与房源的一对多关系映射:
@Entity
@Table(name = "t_region")
public class Region {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private String code;
private Integer parentId; // 支持多级区域划分
@OneToMany(mappedBy = "region", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<House> houses = new HashSet<>();
// 其他字段和方法...
}
系统配置与集成架构
系统的SSH框架集成通过精心配置实现各层之间的松耦合协作:
Spring applicationContext.xml 核心配置片段:
<!-- 数据源配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/real_estate_db"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
<property name="maxPoolSize" value="50"/>
<property name="minPoolSize" value="5"/>
<property name="initialPoolSize" value="10"/>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<