1. 项目概述:Java房屋租赁收租系统vue3
最近在帮朋友改造他们公司的房屋租赁管理系统,用Java+vue3技术栈重构了一套收租系统。这个系统主要解决房产中介公司对多房源租金管理的痛点:手工记账易出错、催缴效率低、数据统计困难。我们团队用Spring Boot处理业务逻辑,Vue3构建动态前端界面,实现了租约管理、账单生成、在线支付和财务统计的全流程数字化。
传统Excel管理方式存在三个致命缺陷:一是租金变动时需手动修改所有关联表格,二是无法实时跟踪付款状态,三是缺乏可视化数据分析。新系统上线后,财务人员处理租金业务的效率提升了60%,逾期率下降了45%。特别在vue3的composition API加持下,复杂账单页面的渲染性能比原先jQuery方案快3倍以上。
2. 技术架构设计
2.1 后端技术选型
采用Spring Boot 2.7 + MyBatis Plus组合,考虑因素有三:
- MyBatis Plus的Lambda查询构建器能优雅处理多条件租金查询
- Spring Schedule实现每日凌晨自动生成账单
- 支付宝/微信支付SDK集成更成熟
数据库设计核心表包括:
sql复制CREATE TABLE `lease_contract` (
`id` BIGINT PRIMARY KEY,
`room_id` BIGINT NOT NULL COMMENT '房源ID',
`tenant_id` BIGINT NOT NULL COMMENT '租客ID',
`start_date` DATE NOT NULL COMMENT '起租日',
`end_date` DATE NOT NULL COMMENT '到期日',
`monthly_rent` DECIMAL(10,2) NOT NULL COMMENT '月租金',
`payment_day` TINYINT NOT NULL DEFAULT 1 COMMENT '每月付款日'
);
2.2 前端技术方案
Vue3组合式API解决了传统Options API的代码碎片化问题。例如账单列表页:
javascript复制// 使用setup语法糖
const { pending, data: bills } = useAsyncData(
'bills',
() => $fetch('/api/bills', {
params: {
month: currentMonth.value,
paymentStatus: filterStatus.value
}
})
);
Element Plus的Table组件配合虚拟滚动,轻松应对万级数据渲染:
html复制<el-table
:data="bills"
height="600"
row-key="id"
v-loading="pending">
<el-table-column prop="roomNumber" label="房号" />
<el-table-column prop="tenantName" label="租客" />
<el-table-column prop="amount" label="金额" />
</el-table>
3. 核心功能实现细节
3.1 智能账单生成
采用策略模式处理不同租金计算规则:
java复制public interface RentCalculator {
BigDecimal calculate(LeaseContract contract, LocalDate billDate);
}
// 普通月付实现
@Component
public class MonthlyCalculator implements RentCalculator {
@Override
public BigDecimal calculate(LeaseContract contract, LocalDate billDate) {
return contract.getMonthlyRent();
}
}
// 账单生成服务
@Service
@RequiredArgsConstructor
public class BillGenerateService {
private final Map<String, RentCalculator> calculators;
public void generateMonthlyBills() {
List<LeaseContract> contracts = leaseContractMapper.selectActiveContracts();
contracts.forEach(contract -> {
BigDecimal amount = calculators.get(contract.getPaymentType())
.calculate(contract, LocalDate.now());
// 生成账单记录...
});
}
}
3.2 支付状态同步
支付宝回调处理需注意:
- 验证签名防止伪造请求
- 处理幂等性问题(相同支付号只处理一次)
- 异步更新账单状态
java复制@PostMapping("/alipay/callback")
public String alipayCallback(@RequestBody String body) {
// 1. 验证签名
if (!AlipaySignature.rsaCheckV1(params, alipayPublicKey, "UTF-8")) {
throw new IllegalStateException("签名验证失败");
}
// 2. 检查是否已处理
String tradeNo = params.get("out_trade_no");
if (paymentLogService.existsByTradeNo(tradeNo)) {
return "success";
}
// 3. 异步更新
eventPublisher.publishEvent(new PaymentSuccessEvent(tradeNo));
return "success";
}
4. 性能优化实践
4.1 账单导出优化
当导出年度数据时(约10万条记录):
- 使用MyBatis流式查询避免OOM
- 通过Apache POI的SXSSFWorkbook实现分页写入
- 前端采用WebSocket推送进度
java复制public void exportBills(Long landlordId, HttpServletResponse response) {
try (ScrollResult<Bill> scroll = billMapper.scrollByLandlord(landlordId)) {
SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 保持100行在内存中
Sheet sheet = workbook.createSheet("账单");
while (scroll.hasNext()) {
Bill bill = scroll.next();
// 写入行数据...
}
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
workbook.write(response.getOutputStream());
}
}
4.2 前端渲染加速
针对大型房源列表:
- 使用vue-virtual-scroller实现虚拟滚动
- 通过v-memo缓存静态节点
- 账单日历采用Canvas渲染
html复制<RecycleScroller
class="scroller"
:items="rooms"
:item-size="72"
key-field="id">
<template #default="{ item }">
<div v-memo="[item.id]">
<RoomCard :room="item" />
</div>
</template>
</RecycleScroller>
5. 踩坑实录与解决方案
5.1 租金浮动计算陷阱
初期直接按30天折算日租金导致误差:
java复制// 错误做法(2月会多算)
BigDecimal dailyRent = monthlyRent.divide(BigDecimal.valueOf(30), 2);
// 正确做法(按实际月份天数)
LocalDate date = LocalDate.of(2023, 2, 1);
int days = date.lengthOfMonth();
BigDecimal dailyRent = monthlyRent.divide(BigDecimal.valueOf(days), 6, RoundingMode.HALF_UP);
5.2 时区导致的账单问题
服务器UTC时间导致本地日期错误:
- 统一使用Asia/Shanghai时区
- 前端传参带时区信息
- MySQL连接设置serverTimezone=Asia/Shanghai
yaml复制# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/rent_db?serverTimezone=Asia/Shanghai
5.3 微信支付证书加载
生产环境证书加载失败:
java复制// 正确加载方式(Spring资源路径)
@Value("classpath:/cert/apiclient_cert.p12")
private Resource certResource;
public WXPay initWxPay() throws Exception {
WXPayConfig config = new WXPayConfig() {
public InputStream getCertStream() {
return certResource.getInputStream();
}
// 其他配置...
};
return new WXPay(config);
}
6. 安全防护措施
6.1 防越权查询
在Controller层添加租户过滤:
java复制@GetMapping("/bills")
public PageResult<BillVO> getBills(BillQuery query, @CurrentUser User user) {
// 自动注入当前用户ID
query.setLandlordId(user.getLandlordId());
return billService.queryBills(query);
}
6.2 敏感数据脱敏
使用Jackson注解实现:
java复制public class TenantDTO {
private Long id;
private String name;
@JsonSerialize(using = MobileSerializer.class)
private String mobile;
}
// 自定义手机号序列化
public class MobileSerializer extends StdSerializer<String> {
protected MobileSerializer() {
super(String.class);
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) {
gen.writeString(value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
}
}
这套系统经过三个月的迭代开发,目前稳定管理着超过2000套出租房源。最大的体会是:在租金计算这种涉及金钱的业务上,必须用BigDecimal处理金额(千万别用double),并且所有计算都要有单元测试覆盖边界情况。另外vue3的
