[尚庭公寓]15-个人中心
浏览历史
浏览历史指的是浏览房间详情的历史,关于浏览历史,有两项工作需要完成,一是提供一个查询浏览历史列表的接口,二是在浏览完房间详情后,增加保存浏览历史的逻辑,下面分别实现。
1. 分页查询浏览历史列表
package com.atguigu.lease.web.app.controller.history;@RestController
@Tag(name = "浏览历史管理")
@RequestMapping("/app/history")
public class BrowsingHistoryController {@Autowiredprivate BrowsingHistoryService browsingHistoryService;@Operation(summary = "获取浏览历史")@GetMapping("pageItem")private Result<IPage<HistoryItemVo>> page(@RequestParam long current, @RequestParam long size) {IPage<HistoryItemVo> page = new Page<>(current, size);Long userId = LoginUserHolder.getLoginUser().getUserId();IPage<HistoryItemVo> res = browsingHistoryService.pageHistoryItemByUserId(page, userId)return Result.ok(res);}
}
package com.atguigu.lease.web.app.service.impl;/*** @author liubo* @description 针对表【browsing_history(浏览历史)】的数据库操作Service实现* @createDate 2023-07-26 11:12:39*/
@Service
public class BrowsingHistoryServiceImpl extends ServiceImpl<BrowsingHistoryMapper, BrowsingHistory>implements BrowsingHistoryService {@AutowiredBrowsingHistoryMapper browsingHistoryMapper;@Overridepublic IPage<HistoryItemVo> pageHistoryItemByUserId(IPage<HistoryItemVo> page, Long userId) {return browsingHistoryMapper.pageHistoryItemByUserId(page, userId);}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.lease.web.app.mapper.BrowsingHistoryMapper"><resultMap id="HistoryItemVoMap" type="com.atguigu.lease.web.app.vo.history.HistoryItemVo" autoMapping="true"><id property="id" column="id"/><result property="roomId" column="room_id"/><collection property="roomGraphVoList" ofType="com.atguigu.lease.web.app.vo.graph.GraphVo"select="selectGraphListById" column="room_id"/></resultMap><select id="pageHistoryItemByUserId" resultMap="HistoryItemVoMap">select bh.id,bh.room_id,bh.user_id,bh.browse_time,ri.room_number,ri.rent,ai.name as apartment_name,ai.province_name,ai.city_name,ai.district_namefrom browsing_history bhleft join room_info ri on bh.room_id = ri.id and ri.is_deleted = 0left join apartment_info ai on ri.apartment_id = ai.id and ai.is_deleted = 0where bh.user_id = #{userId}and bh.is_deleted = 0order by bh.browse_time desc</select><select id="selectGraphListById" resultType="com.atguigu.lease.web.app.vo.graph.GraphVo">select name,urlfrom graph_infowhere item_type = 2and item_id = #{roomId}and is_deleted = 0</select>
</mapper>
2. 保存浏览历史
保存浏览历史的动作应该在浏览房间详情时触发,所以在`RoomInfoServiceImpl`中的`getDetailById`方法的最后增加如下内容
package com.atguigu.lease.web.app.service.impl;/*** @author liubo* @description 针对表【room_info(房间信息表)】的数据库操作Service实现* @createDate 2023-07-26 11:12:39*/
@Service
@Slf4j
public class RoomInfoServiceImpl extends ServiceImpl<RoomInfoMapper, RoomInfo>implements RoomInfoService {@Autowiredprivate BrowsingHistoryService browsingHistoryService;@Overridepublic RoomDetailVo getDetailById(Long id) {... ...// 保存浏览记录browsingHistoryService.saveHistory(LoginUserHolder.getLoginUser().getUserId(), id);return roomDetailVo;}}
在`BrowsingHistoryService`中增加如下内容
package com.atguigu.lease.web.app.service;/**
* @author liubo
* @description 针对表【browsing_history(浏览历史)】的数据库操作Service
* @createDate 2023-07-26 11:12:39
*/
public interface BrowsingHistoryService extends IService<BrowsingHistory> {IPage<HistoryItemVo> pageHistoryItemByUserId(IPage<HistoryItemVo> page, Long userId);void saveHistory(Long userId, Long roomId);
}
package com.atguigu.lease.web.app.service.impl;/*** @author liubo* @description 针对表【browsing_history(浏览历史)】的数据库操作Service实现* @createDate 2023-07-26 11:12:39*/
@Service
public class BrowsingHistoryServiceImpl extends ServiceImpl<BrowsingHistoryMapper, BrowsingHistory>implements BrowsingHistoryService {@AutowiredBrowsingHistoryMapper browsingHistoryMapper;@Overridepublic void saveHistory(Long userId, Long roomId) {LambdaQueryWrapper<BrowsingHistory> wrapper = new LambdaQueryWrapper<>();wrapper.eq(BrowsingHistory::getUserId, userId);wrapper.eq(BrowsingHistory::getRoomId, roomId);BrowsingHistory browsingHistory = browsingHistoryMapper.selectOne(wrapper);if (browsingHistory == null) {BrowsingHistory history = new BrowsingHistory();history.setUserId(userId);history.setRoomId(roomId);history.setBrowseTime(new Date());browsingHistoryMapper.insert(history);} else {browsingHistory.setBrowseTime(new Date());browsingHistoryMapper.updateById(browsingHistory);}}
}
知识点
保存浏览历史的动作不应影响前端获取房间详情信息,故此处采取异步操作。Spring Boot提供了`@Async`注解来完成异步操作,具体使用方式为:
- 启用Spring Boot异步操作支持
在 Spring Boot 主应用程序类上添加 `@EnableAsync` 注解,如下
@SpringBootApplication@EnableAsyncpublic class AppWebApplication {public static void main(String[] args) {SpringApplication.run(AppWebApplication.class);}}
- 在要进行异步处理的方法上添加 `@Async` 注解,如下
@Override@Asyncpublic void saveHistory(Long userId, Long roomId) {... ...}
预约看房
保存或更新看房预约
package com.atguigu.lease.web.app.controller.appointment;@Tag(name = "看房预约信息")
@RestController
@RequestMapping("/app/appointment")
public class ViewAppointmentController {@AutowiredViewAppointmentService viewAppointmentService;@Operation(summary = "保存或更新看房预约")@PostMapping("/saveOrUpdate")public Result saveOrUpdate(@RequestBody ViewAppointment viewAppointment) {viewAppointment.setUserId(LoginUserHolder.getLoginUser().getUserId());viewAppointmentService.saveOrUpdate(viewAppointment);return Result.ok();}}
查询个人预约看房列表
package com.atguigu.lease.web.app.controller.appointment;@Tag(name = "看房预约信息")
@RestController
@RequestMapping("/app/appointment")
public class ViewAppointmentController {@AutowiredViewAppointmentService viewAppointmentService;@Operation(summary = "查询个人预约看房列表")@GetMapping("listItem")public Result<List<AppointmentItemVo>> listItem() {List<AppointmentItemVo> list = viewAppointmentService.listItemByUserId(LoginUserHolder.getLoginUser().getUserId());return Result.ok(list);}}
package com.atguigu.lease.web.app.service.impl;/*** @author liubo* @description 针对表【view_appointment(预约看房信息表)】的数据库操作Service实现* @createDate 2023-07-26 11:12:39*/
@Service
public class ViewAppointmentServiceImpl extends ServiceImpl<ViewAppointmentMapper, ViewAppointment>implements ViewAppointmentService {@Autowiredprivate ViewAppointmentMapper viewAppointmentMapper;@Overridepublic List<AppointmentItemVo> listItemByUserId(Long userId) {return viewAppointmentMapper.listItemByUserId(userId);}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.lease.web.app.mapper.ViewAppointmentMapper"><resultMap id="AppointmentItemVoMap" type="com.atguigu.lease.web.app.vo.appointment.AppointmentItemVo"autoMapping="true"><collection property="graphVoList" ofType="com.atguigu.lease.web.app.vo.graph.GraphVo" autoMapping="true"></collection></resultMap><select id="listItemByUserId" resultMap="AppointmentItemVoMap">select va.id,va.apartment_id,va.appointment_time,va.appointment_status,ai.name as apartment_name,gi.name,gi.urlfrom view_appointment valeft join apartment_info ai on ai.id = va.apartment_id and ai.is_deleted = 0left join graph_info gi on gi.item_id = ai.id and gi.item_type = 1 and gi.is_deleted = 0where va.user_id = #{userId}and va.is_deleted = 0order by va.appointment_time desc</select>
</mapper>
根据ID查询预约详情信息
package com.atguigu.lease.web.app.controller.appointment;@Tag(name = "看房预约信息")
@RestController
@RequestMapping("/app/appointment")
public class ViewAppointmentController {@AutowiredViewAppointmentService viewAppointmentService;@GetMapping("getDetailById")@Operation(summary = "根据ID查询预约详情信息")public Result<AppointmentDetailVo> getDetailById(Long id) {AppointmentDetailVo appointmentDetailVo = viewAppointmentService.getDetailById(id);return Result.ok(appointmentDetailVo);}}
package com.atguigu.lease.web.app.service.impl;/*** @author liubo* @description 针对表【view_appointment(预约看房信息表)】的数据库操作Service实现* @createDate 2023-07-26 11:12:39*/
@Service
public class ViewAppointmentServiceImpl extends ServiceImpl<ViewAppointmentMapper, ViewAppointment>implements ViewAppointmentService {@Autowiredprivate ViewAppointmentMapper viewAppointmentMapper;@Autowiredprivate ApartmentInfoServiceImpl apartmentInfoService;@Overridepublic AppointmentDetailVo getDetailById(Long id) {// 预约看房基础信息ViewAppointment viewAppointment = viewAppointmentMapper.selectById(id);// 房间详情ApartmentItemVo apartmentItemVo = apartmentInfoService.selectApartmentItemVoById(viewAppointment.getApartmentId());AppointmentDetailVo appointmentDetailVo = new AppointmentDetailVo();BeanUtils.copyProperties(viewAppointment, appointmentDetailVo);appointmentDetailVo.setApartmentItemVo(apartmentItemVo);return appointmentDetailVo;}
}
租约管理
获取个人租约基本信息列表
查看响应的数据结构
查看**web-appp模块**下的`com.atguigu.lease.web.app.vo.agreement.AgreementItemVo`,内容如下
@Data@Schema(description = "租约基本信息")public class AgreementItemVo {@Schema(description = "租约id")private Long id;@Schema(description = "房间图片列表")private List<GraphVo> roomGraphVoList;@Schema(description = "公寓名称")private String apartmentName;@Schema(description = "房间号")private String roomNumber;@Schema(description = "租约状态")private LeaseStatus leaseStatus;@Schema(description = "租约开始日期")@JsonFormat(pattern = "yyyy-MM-dd")private Date leaseStartDate;@Schema(description = "租约结束日期")@JsonFormat(pattern = "yyyy-MM-dd")private Date leaseEndDate;@Schema(description = "租约来源")private LeaseSourceType sourceType;@Schema(description = "租金")private BigDecimal rent;}
编写Controller层逻辑
在`LeaseAgreementController`中增加如下内容
package com.atguigu.lease.web.app.controller.agreement;@RestController
@RequestMapping("/app/agreement")
@Tag(name = "租约信息")
public class LeaseAgreementController {@Autowiredprivate LeaseAgreementService leaseAgreementService;@Operation(summary = "获取个人租约基本信息列表")@GetMapping("listItem")public Result<List<AgreementItemVo>> listItem() {List<AgreementItemVo> result = leaseAgreementService.listItemByPhone(LoginUserHolder.getLoginUser().getUsername());return Result.ok(result);}}
package com.atguigu.lease.web.app.service.impl;/*** @author liubo* @description 针对表【lease_agreement(租约信息表)】的数据库操作Service实现* @createDate 2023-07-26 11:12:39*/
@Service
public class LeaseAgreementServiceImpl extends ServiceImpl<LeaseAgreementMapper, LeaseAgreement>implements LeaseAgreementService {@Autowiredprivate LeaseAgreementMapper leaseAgreementMapper;@Overridepublic List<AgreementItemVo> listItemByPhone(String phone) {return leaseAgreementMapper.listItemByPhone(phone);}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.lease.web.app.mapper.LeaseAgreementMapper"><resultMap id="AgreementItemVoMap" type="com.atguigu.lease.web.app.vo.agreement.AgreementItemVo"><collection property="roomGraphVoList" ofType="com.atguigu.lease.web.app.vo.graph.GraphVo" autoMapping="true"></collection></resultMap><select id="listItemByPhone" resultMap="AgreementItemVoMap">select la.id,la.status,la.lease_start_date,la.lease_end_date,la.source_type,la.rent,ri.room_number,ri.id as room_id,ai.name as apartment_name,gi.name,gi.urlfrom lease_agreement laleft join room_info ri on la.room_id = ri.id and ri.is_deleted = 0left join apartment_info ai on room_id = ai.id and ai.is_deleted = 0left join graph_info gi on room_id = gi.item_id ans gi.item_type = 2 and gi.is_deleted = 0where la.phone = #{phone}and la.is_deleted = 0</select>
</mapper>
根据ID获取租约详细信息
查看响应的数据结构
查看**web-app模块**下的`com.atguigu.lease.web.app.vo.agreement.AgreementDetailVo`,内容如下
@Data@Schema(description = "租约详细信息")public class AgreementDetailVo extends LeaseAgreement {@Schema(description = "租约id")private Long id;@Schema(description = "公寓名称")private String apartmentName;@Schema(description = "公寓图片列表")private List<GraphVo> apartmentGraphVoList;@Schema(description = "房间号")private String roomNumber;@Schema(description = "房间图片列表")private List<GraphVo> roomGraphVoList;@Schema(description = "支付方式")private String paymentTypeName;@Schema(description = "租期月数")private Integer leaseTermMonthCount;@Schema(description = "租期单位")private String leaseTermUnit;}
编写Controller层逻辑
package com.atguigu.lease.web.app.controller.agreement;@RestController
@RequestMapping("/app/agreement")
@Tag(name = "租约信息")
public class LeaseAgreementController {@Autowiredprivate LeaseAgreementService leaseAgreementService;@Operation(summary = "根据id获取租约详细信息")@GetMapping("getDetailById")public Result<AgreementDetailVo> getDetailById(@RequestParam Long id) {AgreementDetailVo agreementDetailVo = leaseAgreementService.getDetailById(id);return Result.ok(agreementDetailVo);}}
package com.atguigu.lease.web.app.service.impl;/*** @author liubo* @description 针对表【lease_agreement(租约信息表)】的数据库操作Service实现* @createDate 2023-07-26 11:12:39*/
@Service
public class LeaseAgreementServiceImpl extends ServiceImpl<LeaseAgreementMapper, LeaseAgreement> implements LeaseAgreementService {@Autowiredprivate LeaseAgreementMapper leaseAgreementMapper;@Autowiredprivate ApartmentInfoMapper apartmentInfoMapper;@Autowiredprivate RoomInfoMapper roomInfoMapper;@Autowiredprivate GraphInfoMapper graphInfoMapper;@Autowiredprivate PaymentTypeMapper paymentTypeMapper;@Autowiredprivate LeaseTermMapper leaseTermMapper;@Overridepublic AgreementDetailVo getDetailById(Long id) {LeaseAgreement leaseAgreement = leaseAgreementMapper.selectById(id);if (leaseAgreement == null) {return null;}// 查询公寓信息ApartmentInfo apartmentInfo = apartmentInfoMapper.selectById(leaseAgreement.getApartmentId());// 查询房间信息RoomInfo roomInfo = roomInfoMapper.selectById(leaseAgreement.getRoomId());// 查询图片信息List<GraphVo> roomGraphVoList = graphInfoMapper.selectListByItemTypeAndId(ItemType.ROOM, leaseAgreement.getRoomId());List<GraphVo> apartmentGraphVoList = graphInfoMapper.selectListByItemTypeAndId(ItemType.APARTMENT, leaseAgreement.getApartmentId());// 支付方式PaymentType paymentType = paymentTypeMapper.selectById(leaseAgreement.getPaymentTypeId());// 租期LeaseTerm leaseTerm = leaseTermMapper.selectById(leaseAgreement.getLeaseTermId());AgreementDetailVo agreementDetailVo = new AgreementDetailVo();BeanUtils.copyProperties(leaseAgreement, agreementDetailVo);agreementDetailVo.setApartmentGraphVoList(apartmentGraphVoList);agreementDetailVo.setRoomGraphVoList(roomGraphVoList);agreementDetailVo.setApartmentName(apartmentInfo.getName());agreementDetailVo.setRoomNumber(roomInfo.getRoomNumber());agreementDetailVo.setPaymentTypeName(paymentType.getName());agreementDetailVo.setLeaseTermMonthCount(leaseTerm.getMonthCount());return agreementDetailVo;}
}
根据ID更新租约状态
package com.atguigu.lease.web.app.controller.agreement;@RestController
@RequestMapping("/app/agreement")
@Tag(name = "租约信息")
public class LeaseAgreementController {@Autowiredprivate LeaseAgreementService leaseAgreementService;@Operation(summary = "根据id更新租约状态", description = "用于确认租约和提前退租")@PostMapping("updateStatusById")public Result updateStatusById(@RequestParam Long id, @RequestParam LeaseStatus leaseStatus) {LambdaUpdateWrapper<LeaseAgreement> warp = new LambdaUpdateWrapper<LeaseAgreement>().eq(LeaseAgreement::getId, id).set(LeaseAgreement::getStatus, leaseStatus);leaseAgreementService.update(warp);return Result.ok();}}
保存或更新租约
package com.atguigu.lease.web.app.controller.agreement;@RestController
@RequestMapping("/app/agreement")
@Tag(name = "租约信息")
public class LeaseAgreementController {@Autowiredprivate LeaseAgreementService leaseAgreementService;@Operation(summary = "保存或更新租约", description = "用于续约")@PostMapping("saveOrUpdate")public Result saveOrUpdate(@RequestBody LeaseAgreement leaseAgreement) {leaseAgreementService.saveOrUpdate(leaseAgreement);return Result.ok();}}
根据房间ID获取可选支付方式
package com.atguigu.lease.web.app.controller.payment;@Tag(name = "支付方式接口")
@RestController
@RequestMapping("/app/payment")
public class PaymentTypeController {@Autowiredprivate PaymentTypeService paymentTypeService;@Operation(summary = "根据房间id获取可选支付方式列表")@GetMapping("listByRoomId")public Result<List<PaymentType>> list(@RequestParam Long id) {List<PaymentType> list = paymentTypeService.listByRoomId(id);return Result.ok(list);}}
package com.atguigu.lease.web.app.service.impl;/**
* @author liubo
* @description 针对表【payment_type(支付方式表)】的数据库操作Service实现
* @createDate 2023-07-26 11:12:39
*/
@Service
public class PaymentTypeServiceImpl extends ServiceImpl<PaymentTypeMapper, PaymentType>implements PaymentTypeService{@Autowiredprivate PaymentTypeMapper paymentTypeMapper;@Overridepublic List<PaymentType> listByRoomId(Long id) {List<PaymentType> paymentTypes = paymentTypeMapper.selectListByRoomId(id);return paymentTypes;}
}
根据房间ID获取可选租期
package com.atguigu.lease.web.app.controller.leasaterm;@RestController
@RequestMapping("/app/term/")
@Tag(name = "租期信息")
public class LeaseTermController {@Autowiredprivate LeaseTermService leaseTermService;@GetMapping("listByRoomId")@Operation(summary = "根据房间id获取可选获取租期列表")public Result<List<LeaseTerm>> list(@RequestParam Long id) {List<LeaseTerm> list = leaseTermService.listByRoomId(id);return Result.ok(list);}
}
package com.atguigu.lease.web.app.service.impl;/*** @author liubo* @description 针对表【lease_term(租期)】的数据库操作Service实现* @createDate 2023-07-26 11:12:39*/
@Service
public class LeaseTermServiceImpl extends ServiceImpl<LeaseTermMapper, LeaseTerm>implements LeaseTermService {@Autowiredprivate LeaseTermMapper leaseTermMapper;@Overridepublic List<LeaseTerm> listByRoomId(Long id) {return leaseTermMapper.selectListByRoomId(id);}
}
前后端联调
启动后端项目
启动后端项目,供前端调用接口。
启动前端项目
导入前端项目
将移动端的前端项目(**rentHouseH5**)导入`vscode`或者`WebStorm`,打开终端,在项目根目录执行以下命令,安装所需依赖
npm install
配置后端接口地址
修改项目根目录下的`.env.development`文件中的`VITE_APP_BASE_URL`变量的值为后端接口的地址,此处改为`http://localhost:8081`即可,如下
VITE_APP_BASE_URL='http://localhost:8081'
注意:上述主机名和端口号需要根据实际情况进行修改。
启动前端项目
上述配置完成之后,便可执行以下命令启动前端项目了
npm run dev
访问前端项目
在浏览器中访问前端项目,并逐个测试每个页面的相关功能。