home公寓项目/租赁管理模块
租房项目的后台管理功能全部代码实现,后台管理程序!
共计包含五大模块: 公寓信息管理\租赁信息管理\用户信息管理\系统管理\登录管理!!!
一、看房预约管理
1.1 看房预约管理介绍
看房预约管理共有两个接口,分别是根据条件分页查询预约信息、根据ID更新预约状态,下面逐一实现!!
首先在ViewAppointmentController中注入ViewAppointmentService,如下
@Tag(name = "预约看房管理")
@RequestMapping("/admin/appointment")
@RestController
public class ViewAppointmentController {
@Autowired
private ViewAppointmentService service;
}
1.2 根据条件分页查询预约信息
-
查看请求和响应的数据结构
-
请求数据结构
-
current和size为分页相关参数,分别表示当前所处页面和每个页面的记录数。 -
AppointmentQueryVo为看房预约的查询条件,详细结构如下:@Data @Schema(description = "预约看房查询实体") public class AppointmentQueryVo { @Schema(description="预约公寓所在省份") private Long provinceId; @Schema(description="预约公寓所在城市") private Long cityId; @Schema(description="预约公寓所在区") private Long districtId; @Schema(description="预约公寓所在公寓") private Long apartmentId; @Schema(description="预约用户姓名") private String name; @Schema(description="预约用户手机号码") private String phone; }
-
-
响应数据结构
单个看房预约信息的结构可查看web-admin模块下的
com.mytest.lease.web.admin.vo.appointment.AppointmentVo,内容如下:@Data @Schema(description = "预约看房信息") public class AppointmentVo extends ViewAppointment { @Schema(description = "预约公寓信息") private ApartmentInfo apartmentInfo; }
-
-
编写Controller层逻辑
在
ViewAppointmentController中增加如下内容@Operation(summary = "分页查询预约信息") @GetMapping("page") public Result<IPage<AppointmentVo>> page(@RequestParam long current, @RequestParam long size, AppointmentQueryVo queryVo) { IPage<AppointmentVo> page = new Page<>(current, size); IPage<AppointmentVo> list = service.pageAppointmentByQuery(page, queryVo); return Result.ok(list); } -
编写Service层逻辑
-
在
ViewAppointmentService中增加如下内容IPage<AppointmentVo> pageAppointmentByQuery(IPage<AppointmentVo> page, AppointmentQueryVo queryVo); -
在
ViewAppointmentServiceImpl中增加如下内容@Override public IPage<AppointmentVo> pageAppointmentByQuery(IPage<AppointmentVo> page, AppointmentQueryVo queryVo) { return viewAppointmentMapper.pageAppointmentByQuery(page, queryVo); }
-
-
编写Mapper层逻辑
-
在
ViewAppointmentMapper中增加如下内容IPage<AppointmentVo> pageAppointmentByQuery(IPage<AppointmentVo> page, AppointmentQueryVo queryVo); -
在
ViewAppointmentMapper.xml中增加如下内容<resultMap id="AppointmentVoMap" type="com.mytest.lease.web.admin.vo.appointment.AppointmentVo" autoMapping="true"> <id property="id" column="id"/> <association property="apartmentInfo" javaType="com.mytest.lease.model.entity.ApartmentInfo" autoMapping="true"> <id property="id" column="apartment_id"/> <result property="name" column="apartment_name"/> </association> </resultMap> <select id="pageAppointmentByQuery" resultMap="AppointmentVoMap"> select va.id, va.user_id, va.name, va.phone, va.appointment_time, va.additional_info, va.appointment_status, ai.id apartment_id, ai.name apartment_name, ai.district_id, ai.district_name, ai.city_id, ai.city_name, ai.province_id, ai.province_name from view_appointment va left join apartment_info ai on va.apartment_id = ai.id and ai.is_deleted=0 <where> va.is_deleted = 0 <if test="queryVo.provinceId != null"> and ai.province_id = #{queryVo.provinceId} </if> <if test="queryVo.cityId != null"> and ai.city_id = #{queryVo.cityId} </if> <if test="queryVo.districtId != null"> and ai.district_id = #{queryVo.districtId} </if> <if test="queryVo.apartmentId != null"> and va.apartment_id = #{queryVo.apartmentId} </if> <if test="queryVo.name != null and queryVo.name != ''"> and va.name like concat('%',#{queryVo.name},'%') </if> <if test="queryVo.phone != null and queryVo.phone != ''"> and va.phone like concat('%',#{queryVo.phone},'%') </if> </where> </select>
知识点:
ViewAppointment实体类中的appointmentTime字段为Date类型,Date类型的字段在序列化成JSON字符串时,需要考虑两个点,分别是格式和时区。本项目使用JSON序列化框架为Jackson,具体配置如下-
格式
格式可按照字段单独配置,也可全局配置,下面分别介绍
-
单独配置
在指定字段增加
@JsonFormat注解,如下@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date appointmentTime; -
全局配置
在
application.yml中增加如下内容spring: jackson: date-format: yyyy-MM-dd HH:mm:ss
-
-
时区
时区同样可按照字段单独配置,也可全局配置,下面分别介绍
-
单独配置
在指定字段增加
@JsonFormat注解,如下@JsonFormat(timezone = "GMT+8") private Date appointmentTime; -
全局配置
spring: jackson: time-zone: GMT+8
-
推荐格式按照字段单独配置,时区全局配置。
-
1.3 根据ID更新预约状态
在ViewAppointmentController中增加如下内容
@Operation(summary = "根据id更新预约状态")
@PostMapping("updateStatusById")
public Result updateStatusById(@RequestParam Long id, @RequestParam AppointmentStatus status) {
LambdaUpdateWrapper<ViewAppointment> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(ViewAppointment::getId, id);
updateWrapper.set(ViewAppointment::getAppointmentStatus, status);
service.update(updateWrapper);
return Result.ok();
}
二、租约管理
租约管理共有五个接口需要实现,除此之外,还需实现一个定时任务,用于检查租约是否到期以及修改到期状态。下面逐一实现!!
首先在LeaseAgreementController中注入LeaseAgreementService,如下
@Tag(name = "租约管理")
@RestController
@RequestMapping("/admin/agreement")
public class LeaseAgreementController {
@Autowired
private LeaseAgreementService service;
}
2.1 保存或更新租约信息
在LeaseAgreementController中增加如下内容
@Operation(summary = "保存或修改租约信息")
@PostMapping("saveOrUpdate")
public Result saveOrUpdate(@RequestBody LeaseAgreement leaseAgreement) {
service.saveOrUpdate(leaseAgreement);
return Result.ok();
}
2.2 根据条件分页查询租约列表
-
查看请求和响应的数据结构
-
请求数据结构
-
current和size为分页相关参数,分别表示当前所处页面和每个页面的记录数。 -
AgreementQueryVo为公寓的查询条件,详细结构如下:@Data @Schema(description = "租约查询实体") public class AgreementQueryVo { @Schema(description = "公寓所处省份id") private Long provinceId; @Schema(description = "公寓所处城市id") private Long cityId; @Schema(description = "公寓所处区域id") private Long districtId; @Schema(description = "公寓id") private Long apartmentId; @Schema(description = "房间号") private String roomNumber; @Schema(description = "用户姓名") private String name; @Schema(description = "用户手机号码") private String phone; }
-
-
响应数据结构
单个租约信息的结构可查看
com.mytest.lease.web.admin.vo.agreement.AgreementVo,内容如下:@Data @Schema(description = "租约信息") public class AgreementVo extends LeaseAgreement { @Schema(description = "签约公寓信息") private ApartmentInfo apartmentInfo; @Schema(description = "签约房间信息") private RoomInfo roomInfo; @Schema(description = "支付方式") private PaymentType paymentType; @Schema(description = "租期") private LeaseTerm leaseTerm; }
-
-
编写Controller层逻辑
在
LeaseAgreementController中增加如下内容@Operation(summary = "根据条件分页查询租约列表") @GetMapping("page") public Result<IPage<AgreementVo>> page(@RequestParam long current, @RequestParam long size, AgreementQueryVo queryVo) { IPage<AgreementVo> page = new Page<>(current, size); IPage<AgreementVo> list = service.pageAgreementByQuery(page, queryVo); return Result.ok(list); } -
编写Service层逻辑
-
在
LeaseAgreementService中增加如下内容IPage<AgreementVo> pageAgreementByQuery(IPage<AgreementVo> page, AgreementQueryVo queryVo); -
在
LeaseAgreementServiceImpl中增加如下内容@Override public IPage<AgreementVo> pageAgreementByQuery(IPage<AgreementVo> page, AgreementQueryVo queryVo) { return leaseAgreementMapper.pageAgreementByQuery(page, queryVo); }
-
-
编写Mapper层逻辑
-
在
LeaseAgreementMapper中增加如下内容IPage<AgreementVo> pageAgreementByQuery(IPage<AgreementVo> page, AgreementQueryVo queryVo); -
在
LeaseAgreementMapper.xml中增加如下内容<resultMap id="agreementVoMap" type="com.mytest.lease.web.admin.vo.agreement.AgreementVo" autoMapping="true"> <id property="id" column="id"/> <association property="apartmentInfo" javaType="com.mytest.lease.model.entity.ApartmentInfo" autoMapping="true"> <id property="id" column="apartment_id"/> <result property="name" column="apartment_name"/> </association> <association property="roomInfo" javaType="com.mytest.lease.model.entity.RoomInfo" autoMapping="true"> <id property="id" column="room_id"/> </association> <association property="paymentType" javaType="com.mytest.lease.model.entity.PaymentType" autoMapping="true"> <id property="id" column="payment_type_id"/> <result property="name" column="payment_type_name"/> </association> <association property="leaseTerm" javaType="com.mytest.lease.model.entity.LeaseTerm" autoMapping="true"> <id property="id" column="lease_term_id"/> </association> </resultMap> <select id="pageAgreementByQuery" resultMap="agreementVoMap"> select la.id, la.phone, la.name, la.identification_number, la.lease_start_date, la.lease_end_date, la.rent, la.deposit, la.status, la.source_type, la.additional_info, ai.id apartment_id, ai.name apartment_name, ai.district_id, ai.district_name, ai.city_id, ai.city_name, ai.province_id, ai.province_name, ri.id room_id, ri.room_number, pt.id payment_type_id, pt.name payment_type_name, pt.pay_month_count, lt.id lease_term_id, lt.month_count, lt.unit from lease_agreement la left join apartment_info ai on la.apartment_id = ai.id and ai.is_deleted=0 left join room_info ri on la.room_id = ri.id and ri.is_deleted=0 left join payment_type pt on la.payment_type_id = pt.id and pt.is_deleted=0 left join lease_term lt on la.lease_term_id = lt.id and lt.is_deleted=0 <where> la.is_deleted = 0 <if test="queryVo.provinceId != null"> and ai.province_id = #{queryVo.provinceId} </if> <if test="queryVo.cityId != null"> and ai.city_id = #{queryVo.cityId} </if> <if test="queryVo.districtId != null"> and ai.district_id = #{queryVo.districtId} </if> <if test="queryVo.apartmentId != null"> and la.apartment_id = #{queryVo.apartmentId} </if> <if test="queryVo.roomNumber != null and queryVo.roomNumber != ''"> and ri.room_number like concat('%',#{queryVo.roomNumber},'%') </if> <if test="queryVo.name != null and queryVo.name != ''"> and la.name like concat('%',#{queryVo.name},'%') </if> <if test="queryVo.phone != null and queryVo.phone != ''"> and la.phone like concat('%',#{queryVo.phone},'%') </if> </where> </select>
-
2.3 根据ID查询租约信息
-
编写Controller层逻辑
在
LeaseAgreementController中增加如下内容@Operation(summary = "根据id查询租约信息") @GetMapping(name = "getById") public Result<AgreementVo> getById(@RequestParam Long id) { AgreementVo apartment = service.getAgreementById(id); return Result.ok(apartment); } -
编写Service层逻辑
-
在
LeaseAgreementService中增加如下内容AgreementVo getAgreementById(Long id); -
在
LeaseAgreementServiceImpl中增加如下内容@Override public AgreementVo getAgreementById(Long id) { //1.查询租约信息 LeaseAgreement leaseAgreement = leaseAgreementMapper.selectById(id); //2.查询公寓信息 ApartmentInfo apartmentInfo = apartmentInfoMapper.selectById(leaseAgreement.getApartmentId()); //3.查询房间信息 RoomInfo roomInfo = roomInfoMapper.selectById(leaseAgreement.getRoomId()); //4.查询支付方式 PaymentType paymentType = paymentTypeMapper.selectById(leaseAgreement.getPaymentTypeId()); //5.查询租期 LeaseTerm leaseTerm = leaseTermMapper.selectById(leaseAgreement.getLeaseTermId()); AgreementVo adminAgreementVo = new AgreementVo(); BeanUtils.copyProperties(leaseAgreement, adminAgreementVo); adminAgreementVo.setApartmentInfo(apartmentInfo); adminAgreementVo.setRoomInfo(roomInfo); adminAgreementVo.setPaymentType(paymentType); adminAgreementVo.setLeaseTerm(leaseTerm); return adminAgreementVo; }
-
2.4 根据ID删除租约信息
在LeaseAgreementController中增加如下内容
@Operation(summary = "根据id删除租约信息")
@DeleteMapping("removeById")
public Result removeById(@RequestParam Long id) {
service.removeById(id);
return Result.ok();
}
2.5 根据ID更新租约状态
后台管理系统需要多个修改租约状态的接口,例如修改租约状态为已取消、修改租约状态为已退租等等。为省去重复编码,此处将多个接口合并为一个如下,注意,在生产中应避免这样的写法。
在LeaseAgreementController中增加如下内容
@Operation(summary = "根据id更新租约状态")
@PostMapping("updateStatusById")
public Result updateStatusById(@RequestParam Long id, @RequestParam LeaseStatus status) {
LambdaUpdateWrapper<LeaseAgreement> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(LeaseAgreement::getId, id);
updateWrapper.set(LeaseAgreement::getStatus, status);
service.update(updateWrapper);
return Result.ok();
}
2.6 定时检查租约状态
本节内容是通过定时任务定时检查租约是否到期。SpringBoot内置了定时任务,具体实现如下。
-
启用Spring Boot定时任务
在SpringBoot启动类上增加
@EnableScheduling注解,如下@SpringBootApplication @EnableScheduling public class AdminWebApplication { public static void main(String[] args) { SpringApplication.run(AdminWebApplication.class, args); } } -
编写定时逻辑
在web-admin模块下创建
com.mytest.lease.web.admin.schedule.ScheduledTasks类,内容如下@Component public class ScheduledTasks { @Autowired private LeaseAgreementService leaseAgreementService; @Scheduled(cron = "0 0 0 * * *") public void checkLeaseStatus() { LambdaUpdateWrapper<LeaseAgreement> updateWrapper = new LambdaUpdateWrapper<>(); Date now = new Date(); updateWrapper.le(LeaseAgreement::getLeaseEndDate, now); updateWrapper.in(LeaseAgreement::getStatus, LeaseStatus.SIGNED, LeaseStatus.WITHDRAWING); updateWrapper.set(LeaseAgreement::getStatus, LeaseStatus.EXPIRED); leaseAgreementService.update(updateWrapper); } }知识点:
SpringBoot中的cron表达式语法如下
┌───────────── second (0-59) │ ┌───────────── minute (0 - 59) │ │ ┌───────────── hour (0 - 23) │ │ │ ┌───────────── day of the month (1 - 31) │ │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) │ │ │ │ │ ┌───────────── day of the week (1 - 7 or SUN-SAT) │ │ │ │ │ │ ┌───────────── year (空,或者1970-2099) │ │ │ │ │ │ │ * * * * * * * 除了数字之外,还可以使用特殊字符来指定特定的值: *:代表所有可能的值,例如 * 在分钟字段中代表每分钟。 ,:用于指定多个值,例如 1,5,10 在分钟字段中代表第1、第5和第10分钟。 -:用于指定一个范围,例如 1-5 在分钟字段中代表从第1分钟到第5分钟。 /:用于指定增量值,例如 */5 在分钟字段中代表每隔5分钟。 ?:在日期和星期字段中,用于指示该字段不被指定。 L:在日期字段中,用于指定最后一天,例如 L 在日期字段中代表每月最后一天。 0/5 * * * * ? 每隔5秒执行一次 【/间隔】 【?用日期和星期,一方放弃】 0 */1 * * * ? 每隔1分钟执行一次 0 0 5-15 * * ? 每天5-15每个整点执行一遍 0 0/3 * * * ? 每三分钟触发一次 0 0-5 14 * * ? 在每天下午2点到下午2:05期间的每1分钟触发 0 0/5 14 * * ? 在每天下午2点到下午2:55期间的每5分钟触发 0 0/5 14,18 * * ? 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 【,多个节点】 0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时 0 0 10,14,16 * * ? 每天上午10点,下午2点,4点 0 0 12 ? * WED 表示每个星期三中午12点 0 0 17 ? * TUES,THUR,SAT 每周二、四、六下午五点 0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发 0 15 10 ? * MON-FRI 周一至周五的上午10:15触发 0 0 23 L * ? 每月最后一天23点执行一次 0 15 10 L * ? 每月最后一日的上午10:15触发 0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发 0 15 10 * * ? 2005 2005年的每天上午10:15触发 0 15 10 ? * 6L 2002-2005 2002年至2005年的每月的最后一个星期五上午10:15触发 0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发 "30 * * * * ?" 每半分钟触发任务 "30 10 * * * ?" 每小时的10分30秒触发任务 "30 10 1 * * ?" 每天1点10分30秒触发任务 "30 10 1 20 * ?" 每月20号1点10分30秒触发任务 "30 10 1 20 10 ? *" 每年10月20号1点10分30秒触发任务 "30 10 1 20 10 ? 2011" 2011年10月20号1点10分30秒触发任务 "30 10 1 ? 10 * 2011" 2011年10月每天1点10分30秒触发任务 "30 10 1 ? 10 SUN 2011" 2011年10月每周日1点10分30秒触发任务 "15,30,45 * * * * ?" 每15秒,30秒,45秒时触发任务 "15-45 * * * * ?" 15到45秒内,每秒都触发任务 "15/5 * * * * ?" 每分钟的每15秒开始触发,每隔5秒触发一次 "15-30/5 * * * * ?" 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次 "0 0/3 * * * ?" 每小时的第0分0秒开始,每三分钟触发一次 "0 15 10 ? * MON-FRI" 星期一到星期五的10点15分0秒触发任务 "0 15 10 L * ?" 每个月最后一天的10点15分0秒触发任务 "0 15 10 LW * ?" 每个月最后一个工作日的10点15分0秒触发任务 "0 15 10 ? * 5L" 每个月最后一个星期四的10点15分0秒触发任务 "0 15 10 ? * 5#3" 每个月第三周的星期四的10点15分0秒触发任务