diff --git a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/controllers/AgreedRecordController.java b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/controllers/AgreedRecordController.java new file mode 100644 index 0000000000000000000000000000000000000000..ad812f2ad9fe8eeb0ea4b9bf75b144345da05606 --- /dev/null +++ b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/controllers/AgreedRecordController.java @@ -0,0 +1,74 @@ +package vt.CS5934.SwitchRoom.controllers; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import vt.CS5934.SwitchRoom.models.AgreedRecordModel; +import vt.CS5934.SwitchRoom.models.ResponseModel; +import vt.CS5934.SwitchRoom.services.CommentRatingService; + +@CrossOrigin( + allowCredentials = "true", + origins = {"http://localhost:8080/"} +) +@RestController +@RequestMapping("agreedRecord") +public class AgreedRecordController { + private final Logger logger = LoggerFactory.getLogger(AgreedRecordController.class); + + @Autowired + CommentRatingService commentRatingService; + + @GetMapping("/offer") + public ResponseModel getAgreedOfferList(@CookieValue(value = "userId") Long userId){ + ResponseModel responseModel = new ResponseModel(); + try{ + responseModel.setMessage("Success"); + responseModel.setStatus(HttpStatus.OK); + responseModel.setData(commentRatingService.getRecordsByOfferId(userId)); + return responseModel; + }catch (Exception e){ + logger.error("Error in getWishlistList: "+e); + responseModel.setMessage("getAgreedOfferList Failed, Reason: " + e); + responseModel.setStatus(HttpStatus.NOT_FOUND); + responseModel.setData(null); + return responseModel; + } + } + + @GetMapping("/wishlist") + public ResponseModel getAgreedWishlistList(@CookieValue(value = "userId") Long userId){ + ResponseModel responseModel = new ResponseModel(); + try{ + responseModel.setMessage("Success"); + responseModel.setStatus(HttpStatus.OK); + responseModel.setData(commentRatingService.getRecordsByWishlistUserId(userId)); + return responseModel; + }catch (Exception e){ + logger.error("Error in getWishlistList: "+e); + responseModel.setMessage("getAgreedOfferList Failed, Reason: " + e); + responseModel.setStatus(HttpStatus.NOT_FOUND); + responseModel.setData(null); + return responseModel; + } + } + + @PostMapping("/updateCommentItem") + public ResponseModel updateRecordCommentAndRating(@RequestBody AgreedRecordModel agreedRecordModel){ + ResponseModel responseModel = new ResponseModel(); + try{ + responseModel.setMessage("Success"); + responseModel.setStatus(HttpStatus.OK); + responseModel.setData(commentRatingService.saveNewCommentAndRating(agreedRecordModel)); + return responseModel; + }catch (Exception e){ + logger.error("Error in getWishlistList: "+e); + responseModel.setMessage("updateRecordCommentAndRating Failed, Reason: " + e); + responseModel.setStatus(HttpStatus.NOT_FOUND); + responseModel.setData(null); + return responseModel; + } + } +} diff --git a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/jobs/OfferWishlistMatchingJob.java b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/jobs/OfferWishlistMatchingJob.java index 8abe78e63600f7a9c90f754961b4cfa261a021eb..0f0817230b9ac07bdee1aa4bd50a88dce7604a31 100644 --- a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/jobs/OfferWishlistMatchingJob.java +++ b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/jobs/OfferWishlistMatchingJob.java @@ -26,6 +26,7 @@ import static vt.CS5934.SwitchRoom.utility.UsefulTools.DATE_FORMAT; @Component @EnableAsync +@Async public class OfferWishlistMatchingJob { private final Logger logger = LoggerFactory.getLogger(OfferWishlistMatchingJob.class); @@ -40,12 +41,11 @@ public class OfferWishlistMatchingJob { WishlistItemRepository wishlistItemRepository; @Autowired WishlistWaitingMatchRepository wishlistWaitingMatchRepository; - @Autowired MatchedWishlistRecordRepository matchedWishlistRecordRepository; + @Autowired + AgreedRecordRepository agreedRecordRepository; - - @Async @Scheduled(fixedDelayString = "${offer.wishlist.job.gap.seconds:60}000", initialDelayString = "${offer.wishlist.job.init.delay.seconds:60}000") public void jobStarter(){ @@ -88,7 +88,23 @@ public class OfferWishlistMatchingJob { } - + @Scheduled(fixedDelayString = "${offer.wishlist.clear.old.job.gap.seconds}000", + initialDelayString = "${offer.wishlist.job.init.delay.seconds:60}000") + public void clearOldJobStarter(){ + logger.info("clearOldJobStarter() Started..."); + try{ + List<MatchedWishlistRecordModel> oldRecords = matchedWishlistRecordRepository + .findAllByEndTimeBefore(new Date()); + List<Long>agreedRecordIds = oldRecords.stream().map(MatchedWishlistRecordModel::getAgreedRecordId) + .collect(Collectors.toList()); + List<AgreedRecordModel> agreedRecordModels = agreedRecordRepository.findAllByIdIn(agreedRecordIds); + agreedRecordModels.forEach(item->item.setOnGoing(false)); + agreedRecordRepository.saveAll(agreedRecordModels); + matchedWishlistRecordRepository.deleteAll(oldRecords); + }catch (Exception e){ + logger.error("Error occur when clear old matched records"); + } + } /** @@ -267,16 +283,24 @@ public class OfferWishlistMatchingJob { private void useWishlistItemAndOfferListBuildMatchedRecordDB(WishlistWaitingMatchModel wishlistItem, List<OfferWaitingMatchModel> offerList) { List<MatchedWishlistRecordModel> matchedRecordList = offerList.parallelStream() - .filter(offer-> isOverlapping(wishlistItem.getStartTime(),wishlistItem.getEndTime(), - offer.getStartTime(),offer.getEndTime())) + .filter(offer-> isWithin(wishlistItem.getStartTime(),wishlistItem.getEndTime(), + offer.getStartTime(),offer.getEndTime()) && checkAgreedOfferTime(offer, wishlistItem)) .map(offer -> new MatchedWishlistRecordModel(wishlistItem.getWishlistItemId(), offer.getOfferId(), LookupTables.MATCHING_RECORD_USER_RESULT.Waiting, - LookupTables.MATCHING_RECORD_USER_RESULT.Waiting)) + LookupTables.MATCHING_RECORD_USER_RESULT.Waiting, + wishlistItem.getStartTime(), wishlistItem.getEndTime())) .toList(); - matchedWishlistRecordRepository.saveAll(matchedRecordList); } + private boolean checkAgreedOfferTime(OfferWaitingMatchModel offer, WishlistWaitingMatchModel wishlistItem) { + AgreedRecordModel agreedRecordList = agreedRecordRepository + .findOneByOfferIdAndStartTimeGreaterThanEqualAndStartTimeLessThanEqualOrOfferIdAndEndTimeGreaterThanEqualAndEndTimeLessThanEqual( + offer.getOfferId(),wishlistItem.getStartTime(), wishlistItem.getEndTime(), + offer.getOfferId(),wishlistItem.getStartTime(), wishlistItem.getEndTime()); + return agreedRecordList == null; + } + private ConcurrentMap<Long, List<OfferWaitingMatchModel>> loadOfferListIntoMap( List<OfferWaitingMatchModel> offerRecords){ return offerRecords.parallelStream() @@ -293,6 +317,10 @@ public class OfferWishlistMatchingJob { return start1.before(end2) && start2.before(end1); } + private boolean isWithin(Date start1, Date end1, Date start2, Date end2){ + return start2.before(start1) && end1.before(end2); + } + } /* Change logs: diff --git a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/models/AgreedRecordModel.java b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/models/AgreedRecordModel.java new file mode 100644 index 0000000000000000000000000000000000000000..6c5fa43579a45dbe5888986bcd561a3b29ea5210 --- /dev/null +++ b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/models/AgreedRecordModel.java @@ -0,0 +1,161 @@ +package vt.CS5934.SwitchRoom.models; + + +import lombok.NoArgsConstructor; +import javax.persistence.*; +import java.util.Date; +import java.util.concurrent.BlockingDeque; + +@Entity +@Table(name = "success_record_table") +@NoArgsConstructor +public class AgreedRecordModel { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + private Long stateCityCode; + private String state; + private String city; + private Long wishlistId; + private Long wishlistUserId; + private Long offerId; + private Date startTime; + private Date endTime; + private Integer toOfferStar = 5; + private Integer toVisitorStar = 5; + @Column(length = 500) + private String toOfferComment; + @Column(length = 500) + private String toVisitorComment; + private boolean onGoing = true; + + public AgreedRecordModel(Long stateCityCode, String state, String city, Long wishlistId, Long wishlistUserId, + Long offerId, Date startTime, + Date endTime, Integer toOfferStar, Integer toVisitorStar, String toOfferComment, + String toVisitorComment) { + this.stateCityCode = stateCityCode; + this.state = state; + this.city = city; + this.wishlistId = wishlistId; + this.wishlistUserId = wishlistUserId; + this.offerId = offerId; + this.startTime = startTime; + this.endTime = endTime; + this.toOfferStar = toOfferStar; + this.toVisitorStar = toVisitorStar; + this.toOfferComment = toOfferComment; + this.toVisitorComment = toVisitorComment; + } + + public Long getStateCityCode() { + return stateCityCode; + } + + public void setStateCityCode(Long stateCityCode) { + this.stateCityCode = stateCityCode; + } + + public Long getWishlistId() { + return wishlistId; + } + + public void setWishlistId(Long wishlistId) { + this.wishlistId = wishlistId; + } + + public Long getWishlistUserId() { + return wishlistUserId; + } + + public void setWishlistUserId(Long wishlistUserId) { + this.wishlistUserId = wishlistUserId; + } + + public Long getOfferId() { + return offerId; + } + + public void setOfferId(Long offerId) { + this.offerId = offerId; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Integer getToOfferStar() { + return toOfferStar; + } + + public void setToOfferStar(Integer toOfferStar) { + this.toOfferStar = toOfferStar; + } + + public Integer getToVisitorStar() { + return toVisitorStar; + } + + public void setToVisitorStar(Integer toVisitorStar) { + this.toVisitorStar = toVisitorStar; + } + + public String getToOfferComment() { + return toOfferComment; + } + + public void setToOfferComment(String toOfferComment) { + this.toOfferComment = toOfferComment; + } + + public String getToVisitorComment() { + return toVisitorComment; + } + + public void setToVisitorComment(String toVisitorComment) { + this.toVisitorComment = toVisitorComment; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public boolean isOnGoing() { + return onGoing; + } + + public void setOnGoing(boolean onGoing) { + this.onGoing = onGoing; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } +} diff --git a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/models/MatchedWishlistRecordModel.java b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/models/MatchedWishlistRecordModel.java index 8d47292fea38e33f44c5df68d74a35d1548134a5..90ccfd772c7911a140e438793e43f370d3244c30 100644 --- a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/models/MatchedWishlistRecordModel.java +++ b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/models/MatchedWishlistRecordModel.java @@ -19,8 +19,11 @@ public class MatchedWishlistRecordModel { @Id private Long wishlistItemId; private LookupTables.MATCHING_RECORD_USER_RESULT offerResult; - private LookupTables.MATCHING_RECORD_USER_RESULT wishlistResult; + private Date startTime; + private Date endTime; + private Long agreedRecordId; + @UpdateTimestamp @Temporal(TemporalType.TIMESTAMP) @Column(name = "modify_date") @@ -35,6 +38,18 @@ public class MatchedWishlistRecordModel { this.wishlistResult = wishlistResult; } + public MatchedWishlistRecordModel(Long offerId, Long wishlistItemId, + LookupTables.MATCHING_RECORD_USER_RESULT offerResult, + LookupTables.MATCHING_RECORD_USER_RESULT wishlistResult, + Date startTime, Date endTime) { + this.offerId = offerId; + this.wishlistItemId = wishlistItemId; + this.offerResult = offerResult; + this.wishlistResult = wishlistResult; + this.startTime = startTime; + this.endTime = endTime; + } + public Long getWishlistItemId() { return wishlistItemId; } @@ -67,6 +82,30 @@ public class MatchedWishlistRecordModel { this.wishlistResult = wishlistResult; } + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Long getAgreedRecordId() { + return agreedRecordId; + } + + public void setAgreedRecordId(Long agreedRecordId) { + this.agreedRecordId = agreedRecordId; + } + public Date getModifyDate() { return modifyDate; } diff --git a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/repositories/AgreedRecordRepository.java b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/repositories/AgreedRecordRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..38c4c39b7a31034b48dcc5dfede11c5bb221798b --- /dev/null +++ b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/repositories/AgreedRecordRepository.java @@ -0,0 +1,17 @@ +package vt.CS5934.SwitchRoom.repositories; + +import org.springframework.data.jpa.repository.JpaRepository; +import vt.CS5934.SwitchRoom.models.AgreedRecordModel; + +import java.util.Date; +import java.util.List; + +public interface AgreedRecordRepository extends JpaRepository<AgreedRecordModel, Long> { + + List<AgreedRecordModel> findAllByWishlistUserId(Long wishlistUserId); + List<AgreedRecordModel> findAllByOfferId(Long offerId); + AgreedRecordModel findByOfferIdAndWishlistId(Long offerId, Long wishlistId); + AgreedRecordModel findOneByOfferIdAndStartTimeGreaterThanEqualAndStartTimeLessThanEqualOrOfferIdAndEndTimeGreaterThanEqualAndEndTimeLessThanEqual( + Long offerId1,Date startTime1, Date endTime1, Long offerId2,Date startTime2, Date endTime2); + List<AgreedRecordModel> findAllByIdIn(List<Long> ids); +} diff --git a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/repositories/MatchedWishlistRecordRepository.java b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/repositories/MatchedWishlistRecordRepository.java index e9ef530a764129d691d31f707e55f231040cd650..06acd97c0c4c638787d26a50a40eb103f902a108 100644 --- a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/repositories/MatchedWishlistRecordRepository.java +++ b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/repositories/MatchedWishlistRecordRepository.java @@ -4,6 +4,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import vt.CS5934.SwitchRoom.models.MatchedRecordIdModel; import vt.CS5934.SwitchRoom.models.MatchedWishlistRecordModel; +import java.util.Date; import java.util.List; public interface MatchedWishlistRecordRepository extends JpaRepository<MatchedWishlistRecordModel, MatchedRecordIdModel> { @@ -27,4 +28,7 @@ public interface MatchedWishlistRecordRepository extends JpaRepository<MatchedWi void deleteAllByOfferId(Long offerId); void deleteAllByWishlistItemId(Long wishlistItemId); Long countByWishlistItemId(Long wishlistItemId); + MatchedWishlistRecordModel findByOfferIdAndWishlistItemId(Long offerId, Long wishlistId); + List<MatchedWishlistRecordModel> findAllByOfferIdAndAgreedRecordIdIsNotNull(Long offerId); + List<MatchedWishlistRecordModel> findAllByEndTimeBefore(Date todayDate); } diff --git a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/services/CommentRatingService.java b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/services/CommentRatingService.java new file mode 100644 index 0000000000000000000000000000000000000000..e39e6a53435d77549a61fefaf709d5f5c4148dfb --- /dev/null +++ b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/services/CommentRatingService.java @@ -0,0 +1,41 @@ +package vt.CS5934.SwitchRoom.services; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import vt.CS5934.SwitchRoom.models.AgreedRecordModel; +import vt.CS5934.SwitchRoom.repositories.AgreedRecordRepository; + +import java.util.List; +import java.util.Optional; + +@Service +public class CommentRatingService { + + private final Logger logger = LoggerFactory.getLogger(CommentRatingService.class); + @Autowired + private AgreedRecordRepository agreedRecordRepository; + + public List<AgreedRecordModel> getRecordsByOfferId(Long OfferId){ + return agreedRecordRepository.findAllByOfferId(OfferId); + } + + public List<AgreedRecordModel> getRecordsByWishlistUserId(Long wishlistUserId){ + return agreedRecordRepository.findAllByWishlistUserId(wishlistUserId); + } + + public void setRecordDone(Long offerId, Long wishlistId){ + AgreedRecordModel record = agreedRecordRepository.findByOfferIdAndWishlistId(offerId, wishlistId); + if(record!=null){ + record.setOnGoing(false); + agreedRecordRepository.save(record); + }else{ + logger.error("Can not find AgreedRecordModel with OfferId: {}, and wishlistId: {}", offerId, wishlistId); + } + } + + public AgreedRecordModel saveNewCommentAndRating(AgreedRecordModel agreedRecordModel){ + return agreedRecordRepository.save(agreedRecordModel); + } +} diff --git a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/services/MatchedWishlistRecordService.java b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/services/MatchedWishlistRecordService.java index 8840f63b35e7063bca9e7759f319825795d5bc08..89d6a43e60d66cfd54c5dc9a2f085ac8333ac764 100644 --- a/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/services/MatchedWishlistRecordService.java +++ b/BackendFolder/SwitchRoom/src/main/java/vt/CS5934/SwitchRoom/services/MatchedWishlistRecordService.java @@ -4,10 +4,13 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import vt.CS5934.SwitchRoom.models.MatchedWishlistRecordModel; -import vt.CS5934.SwitchRoom.repositories.MatchedWishlistRecordRepository; +import vt.CS5934.SwitchRoom.models.*; +import vt.CS5934.SwitchRoom.repositories.*; +import vt.CS5934.SwitchRoom.utility.LookupTables; +import java.util.Date; import java.util.List; +import java.util.Objects; @Service public class MatchedWishlistRecordService { @@ -23,9 +26,96 @@ public class MatchedWishlistRecordService { */ @Autowired MatchedWishlistRecordRepository matchedWishlistRecordRepository; + @Autowired + AgreedRecordRepository agreedRecordRepository; + @Autowired + UserOfferRepository userOfferRepository; + @Autowired + UserOfferWishlistLookUpRepository userOfferWishlistLookUpRepository; + @Autowired + WishlistWaitingMatchRepository wishlistWaitingMatchRepository; + @Autowired + WishlistItemRepository wishlistItemRepository; public List<MatchedWishlistRecordModel> getOfferListWithIDFromDB(long id){ logger.info("Reached getOfferListIDFromDB()"); return matchedWishlistRecordRepository.findAllByOfferId(id); } + + public void hostAcceptedOrUpdate(Long offerId, Long wishlistId, Date startTime, Date endTime){ + MatchedWishlistRecordModel record = matchedWishlistRecordRepository + .findByOfferIdAndWishlistItemId(offerId, wishlistId); + if(record != null){ + record.setStartTime(startTime); + record.setEndTime(endTime); + record.setOfferResult(LookupTables.MATCHING_RECORD_USER_RESULT.Accepted); + matchedWishlistRecordRepository.save(record); + }else{ + logger.error("Can not find the matched record with offerId: {}, and wishlistItemId:{}", + offerId, wishlistId); + } + } + + public void hostReject(Long offerId, Long wishlistId){ + MatchedWishlistRecordModel record = matchedWishlistRecordRepository + .findByOfferIdAndWishlistItemId(offerId, wishlistId); + if(record != null){ + record.setOfferResult(LookupTables.MATCHING_RECORD_USER_RESULT.Declined); + if(record.getAgreedRecordId() != null){ + agreedRecordRepository.deleteById(record.getAgreedRecordId()); + wishlistItemRepository.save(wishlistItemRepository.findByWishlistItemId(wishlistId)); + record.setAgreedRecordId(null); + } + matchedWishlistRecordRepository.save(record); + }else{ + logger.error("Can not find the matched record with offerId: {}, and wishlistItemId:{}", + offerId, wishlistId); + } + } + + public void visitorReject(Long offerId, Long wishlistId){ + MatchedWishlistRecordModel record = matchedWishlistRecordRepository + .findByOfferIdAndWishlistItemId(offerId, wishlistId); + if(record != null){ + record.setWishlistResult(LookupTables.MATCHING_RECORD_USER_RESULT.Declined); + if(record.getAgreedRecordId() != null){ + agreedRecordRepository.deleteById(record.getAgreedRecordId()); + wishlistItemRepository.save(wishlistItemRepository.findByWishlistItemId(wishlistId)); + record.setAgreedRecordId(null); + } + matchedWishlistRecordRepository.save(record); + }else{ + logger.error("Can not find the matched record with offerId: {}, and wishlistItemId:{}", + offerId, wishlistId); + } + } + + public void visitorAccepted(Long offerId, Long wishlistId){ + MatchedWishlistRecordModel record = matchedWishlistRecordRepository + .findByOfferIdAndWishlistItemId(offerId, wishlistId); + if(record != null){ + record.setWishlistResult(LookupTables.MATCHING_RECORD_USER_RESULT.Accepted); + UserOfferModel offerModel = userOfferRepository.findByUserId(offerId); + UserOfferWishlistLookUpModel offerWishLookup = userOfferWishlistLookUpRepository + .findByWishlistItemId(wishlistId); + AgreedRecordModel agreedRecord = new AgreedRecordModel( + offerModel.getStateCityCode(), offerModel.getState(), offerModel.getSpaceLocateCity(), + wishlistId, offerWishLookup.getUserId(), offerId, + record.getStartTime(), record.getEndTime(), null, null, + null, null); + agreedRecord = agreedRecordRepository.save(agreedRecord); + record.setAgreedRecordId(agreedRecord.getId()); + matchedWishlistRecordRepository.save(record); + wishlistWaitingMatchRepository.deleteAllByWishlistItemId(wishlistId); + List<MatchedWishlistRecordModel> matchedWishlistRecords = matchedWishlistRecordRepository + .findAllByWishlistItemId(wishlistId) + .stream() + .filter(item-> !Objects.equals(item.getOfferId(), offerId)) + .toList(); + matchedWishlistRecordRepository.deleteAll(matchedWishlistRecords); + }else{ + logger.error("Can not find the matched record with offerId: {}, and wishlistItemId:{}", + offerId, wishlistId); + } + } } diff --git a/BackendFolder/SwitchRoom/src/main/resources/application.properties b/BackendFolder/SwitchRoom/src/main/resources/application.properties index 66d7d7b9e0142b321aa2212ce2cdd08469b99bad..5e0d167738cb85edbed5b60df1200cbd9197779e 100644 --- a/BackendFolder/SwitchRoom/src/main/resources/application.properties +++ b/BackendFolder/SwitchRoom/src/main/resources/application.properties @@ -16,6 +16,7 @@ spring.task.scheduling.pool.size = 1 #SpringBoot Offer Wishlist matching job fields: offer.wishlist.job.gap.seconds = 60 +offer.wishlist.clear.old.job.gap.seconds = 86400 offer.wishlist.job.init.delay.seconds = 10 # spring mail diff --git a/FrontendFolder/switch-room/src/components/AgreedMatchPage.vue b/FrontendFolder/switch-room/src/components/AgreedMatchPage.vue new file mode 100644 index 0000000000000000000000000000000000000000..3f7f17e957e93ce98f5ef5db8d65b09a5019e6e9 --- /dev/null +++ b/FrontendFolder/switch-room/src/components/AgreedMatchPage.vue @@ -0,0 +1,271 @@ +<template> + <div class="common-layout"> + <el-container> + <el-header> + <el-tabs type="border-card" class="demo-tabs" @tab-change="handleTabClick"> + <el-tab-pane label="Offer Agreements" @tab-click="clickOnOfferTab"></el-tab-pane> + <el-tab-pane label="Wishlist Agreements" @tab-click="clickOnWishlistTab"></el-tab-pane> + </el-tabs> + </el-header> + <el-container class="box-body"> + <el-aside width="300px"> + <el-space direction="vertical" width="100%"> + <el-card + v-for="(item, index) in sideList.value" + shadow="hover" + style="width: 280px" + @click="selectItem(index)" + > + <span>{{ item.city }}, {{ item.state }}</span> + <div class="bottom"> + <time class="time" + >"{{ formatDate(item.startTime) }} to {{ formatDate(item.endTime) }}"</time + > + </div> + </el-card> + </el-space> + </el-aside> + <el-main> + <!-- This is the main body part --> + <el-empty v-if="sideList.value.length===0" /> + <el-card shadow="never" class="main-card" v-if="sideList.value.length>0"> + <span>{{displayPage}} Matched Record Detail</span> + <el-form + ref="ruleFormRef" + :model="agreedRecord" + :rules="rules" + label-width="120px" + :label-position="'left'" + class="demo-ruleForm" + :size="formSize" + status-icon + > + <el-form-item></el-form-item> + + <el-form-item label="City Name:" prop="city"> + <el-input v-model="agreedRecord.city" :readonly="true" /> + </el-form-item> + + <el-form-item label="State:" prop="state"> + <el-select + v-model="agreedRecord.state" + placeholder="Please select state" + :disabled="true" + > + <el-option v-for="(state_item, index) in USA_STATE_INITAL_LIST" + :label="state_item" :value="state_item" /> + </el-select> + </el-form-item> + + <el-form-item label="Trip Start Time"> + <el-form-item prop="startTime"> + <el-date-picker + v-model="agreedRecord.startTime" + type="date" + label="Pick a date" + placeholder="Pick a date" + style="width: 100%" + :readonly="true" + /> + </el-form-item> + </el-form-item> + + <el-form-item label="Trip End Time"> + <el-form-item prop="endTime"> + <el-date-picker + v-model="agreedRecord.endTime" + type="date" + label="Pick a date" + placeholder="Pick a date" + style="width: 100%" + :readonly="true" + /> + </el-form-item> + </el-form-item> + + <!-- Rating and comment when the agreement is due --> + <el-form-item label="Rate Your Visitor:" prop="toVisitorStar" + v-if="!agreedRecord.onGoing && displayPage==='Offer'" > + <el-rate + v-model="agreedRecord.toVisitorStar" + :texts="['oops', 'disappointed', 'normal', 'good', 'great']" + show-text + :disabled="buttonState === 'edit'" + /> + </el-form-item> + + <el-form-item label="Leave Comment:" prop="toVisitorComment" + v-if="!agreedRecord.onGoing && displayPage==='Offer'"> + <el-input + v-model="agreedRecord.toVisitorComment" + type="textarea" + :readonly="buttonState === 'edit'" + /> + </el-form-item> + + <el-form-item label="Rate Your Host:" prop="toOfferStar" + v-if="!agreedRecord.onGoing && displayPage==='Wishlist'"> + <el-rate + v-model="agreedRecord.toOfferStar" + :texts="['oops', 'disappointed', 'normal', 'good', 'great']" + show-text + :disabled="buttonState === 'edit'" + /> + </el-form-item> + + <el-form-item label="Leave Comment:" prop="toOfferComment" + v-if="!agreedRecord.onGoing && displayPage==='Wishlist'" > + <el-input + v-model="agreedRecord.toOfferComment" + type="textarea" + :readonly="buttonState === 'edit'" + /> + </el-form-item> + + + <el-button type="primary" v-if="buttonState === 'edit'" @click="onEdit()" + >Edit Comment</el-button> + <el-button type="success" v-if="buttonState === 'update'" @click="updateForm(ruleFormRef)" + >Save</el-button> + </el-form> + </el-card> + </el-main> + </el-container> + </el-container> + </div> +</template> + + +<script setup lang="ts"> +import { Calendar } from '@element-plus/icons-vue' +import { reactive, ref, onMounted } from "vue"; +import {AgreedRecordModel} from "@/models/AgreedRecordModel"; +import * as AgreedRecordService from "@/services/AgreedMatchService"; +import type { FormInstance, FormRules } from "element-plus"; +import { USA_STATE_INITAL_LIST } from "@/services/Constans"; + +const ruleFormRef = ref<FormInstance>(); +let displayPage = ref(""); +const sideList = reactive({value:[]}); +const agreedRecord = ref(new AgreedRecordModel()); +const buttonState = ref("edit"); + +onMounted(() => { + handleTabClick(0); +}) + +const selectItem = (listIdx:number) => { + agreedRecord.value = sideList.value[listIdx]; + console.log("You selected index: " + listIdx+" , and data is: ", agreedRecord.value) +} + +const handleTabClick = (tabIdx: number) => { + if(tabIdx == 0){ + clickOnOfferTab(); + }else if(tabIdx == 1){ + clickOnWishlistTab(); + } +} + +const clickOnOfferTab = async() =>{ + // Load agreed offers from backend + displayPage.value = "Offer"; + await AgreedRecordService.getAgreedOfferRecord().then((response)=>{ + console.log("Receiving offer records from backend: ", response); + if(response["data"]!== null && response["data"].length > 0){ + sideList.value = response["data"]; + selectItem(0); + }else{ + agreedRecord.value = new AgreedRecordModel(); + sideList.value = []; + } + }) +} + +const clickOnWishlistTab = async() =>{ + // Load agreed Wishlist from backend + displayPage.value = "Wishlist"; + + await AgreedRecordService.getAgreedWishlistRecord().then((response)=>{ + console.log("Receiving wishlist records from backend: ", response); + if(response["data"]!== null && response["data"].length > 0){ + sideList.value = response["data"]; + selectItem(0); + }else{ + sideList.value = []; + agreedRecord.value = new AgreedRecordModel(); + } + }) +} + +const formatDate = (dateString: string) => { + const date = new Date(dateString); + return new Intl.DateTimeFormat('en-US').format(date); +} + +const rules = reactive<FormRules>({ + toVisitorComment: [ + { + required: false, + message: "Please share your experience of the visitor.", + trigger: "blur", + }, + { min: 0, max: 500, message: "Length should be 0 to 500", trigger: "blur" }, + ], + toOfferComment: [ + { + required: false, + message: "Please share your experience of this visit.", + trigger: "blur", + }, + { min: 0, max: 500, message: "Length should be 0 to 500", trigger: "blur" }, + ], + +}); + +const onEdit = () =>{ + buttonState.value = "update"; +} + +const updateForm = async (formEl: FormInstance | undefined) =>{ + if (!formEl) return; + await formEl.validate((valid, fields) => { + if (valid) { + AgreedRecordService.updateRatingInformation( agreedRecord.value).then((response)=>{ + console.log("Receiving updateRatingInformation item data", response); + if(response["data"] === null){ + // TODO: create failed, handle it later + }else{ + buttonState.value = "edit"; + } + console.log("submit!"); + }) + } else { + console.log("error submit!", fields); + } + console.log(agreedRecord.value); + }); +} + +</script> + +<style> +.demo-tabs > .el-tabs__content { +} +.el-tabs--border-card>.el-tabs__content{ + padding: 0; +} +.demo-tabs .custom-tabs-label .el-icon { + vertical-align: middle; +} +.demo-tabs .custom-tabs-label span { + vertical-align: middle; + margin-left: 4px; +} +.box-body{ + padding: 20px 0; +} +.el-header { + padding: 20px 0; +} +</style> \ No newline at end of file diff --git a/FrontendFolder/switch-room/src/components/AppHeader.vue b/FrontendFolder/switch-room/src/components/AppHeader.vue index 97d1133ca1a01e4efd4d460b541d07a39da080c2..f53c160bdf92142032fb3f5899ab31bf4a78b635 100644 --- a/FrontendFolder/switch-room/src/components/AppHeader.vue +++ b/FrontendFolder/switch-room/src/components/AppHeader.vue @@ -18,14 +18,17 @@ <el-menu-item index="3" v-on:click="redirect('/wishlist-page')" >Wish List</el-menu-item > - <el-menu-item index="4" v-on:click="redirect('/flight')" + <el-menu-item index="4" v-on:click="redirect('/agreed-match-page')" + >Agreed Records</el-menu-item + > + <el-menu-item index="5" v-on:click="redirect('/flight')" >Flight Ticket</el-menu-item > - <el-menu-item index="5" v-on:click="redirect('/profile')" + <el-menu-item index="6" v-on:click="redirect('/profile')" >Profile</el-menu-item > <notification class="position"></notification> - <el-menu-item index="6" class="dock-right" @click="hanldeLogOut()" + <el-menu-item index="7" class="dock-right" @click="hanldeLogOut()" >Log Out</el-menu-item > </el-menu> diff --git a/FrontendFolder/switch-room/src/models/AgreedRecordModel.ts b/FrontendFolder/switch-room/src/models/AgreedRecordModel.ts new file mode 100644 index 0000000000000000000000000000000000000000..e79d0ad060b54aa12b8e88934dc0127a81d08ab9 --- /dev/null +++ b/FrontendFolder/switch-room/src/models/AgreedRecordModel.ts @@ -0,0 +1,47 @@ +export class AgreedRecordModel { + public readonly id: number; + public stateCityCode: number; + public state: string; + public city:string; + public wishlistId: number; + public wishlistUserId: number; + public offerId: number; + public startTime: Date|any; + public endTime: Date|any; + public toOfferStar: number; + public toVisitorStar: number; + public toOfferComment: string; + public toVisitorComment: string; + public onGoing: boolean|any; + constructor( + id = -1, + stateCityCode = -1, + state = "", + city = "", + wishlistId = -1, + wishlistUserId = -1, + offerId = -1, + startTime = null, + endTime = null, + toOfferStar = 5, + toVisitorStar = 5, + toOfferComment = "", + toVisitorComment = "", + onGoing = null, + ){ + this.id = id; + this.stateCityCode = stateCityCode; + this.state = state; + this.city = city; + this.wishlistId = wishlistId; + this.wishlistUserId = wishlistUserId; + this.offerId = offerId; + this.startTime = startTime; + this.endTime = endTime; + this.toOfferStar = toOfferStar; + this.toVisitorStar = toVisitorStar; + this.toOfferComment = toOfferComment; + this.toVisitorComment = toVisitorComment; + this.onGoing = onGoing; + } +} \ No newline at end of file diff --git a/FrontendFolder/switch-room/src/router/index.ts b/FrontendFolder/switch-room/src/router/index.ts index 06c70aaac8a32123ee0d4898ddb2236113712094..ef211696d7c1d8a028fba9dedd9fa9cbe99ba071 100644 --- a/FrontendFolder/switch-room/src/router/index.ts +++ b/FrontendFolder/switch-room/src/router/index.ts @@ -70,6 +70,16 @@ const routes: Array<RouteRecordRaw> = [ component: () => import(/* webpackChunkName: "about" */ "../views/WishlistView.vue"), }, + { + path: "/agreed-match-page", + name: "AgreedMatchPage", + meta: { + requiresAuth: true, + hideHeader: false, + }, + component: () => + import(/* webpackChunkName: "about" */ "../views/AgreedMatchView.vue"), + }, { path: "/flight", name: "flight", diff --git a/FrontendFolder/switch-room/src/services/AgreedMatchService.ts b/FrontendFolder/switch-room/src/services/AgreedMatchService.ts new file mode 100644 index 0000000000000000000000000000000000000000..c605533f95299a9d9e40673fa0ac72c1b7ae9f7e --- /dev/null +++ b/FrontendFolder/switch-room/src/services/AgreedMatchService.ts @@ -0,0 +1,16 @@ +import * as serverHttpService from "./ServerHttpService"; +import {AgreedRecordModel} from "@/models/AgreedRecordModel"; + +const baseUrl = "agreedRecord"; + +export function getAgreedOfferRecord() { + const urlPath = "/offer"; + return serverHttpService.Get(baseUrl + urlPath); +} +export function getAgreedWishlistRecord() { + const urlPath = "/wishlist"; + return serverHttpService.Get(baseUrl + urlPath); +} +export function updateRatingInformation(agreedRecord: AgreedRecordModel) { + return serverHttpService.Post(baseUrl + "/updateCommentItem", agreedRecord); +} \ No newline at end of file diff --git a/FrontendFolder/switch-room/src/views/AgreedMatchView.vue b/FrontendFolder/switch-room/src/views/AgreedMatchView.vue new file mode 100644 index 0000000000000000000000000000000000000000..0843c775b69eb4cfe375d17c6393aa3f789f18c6 --- /dev/null +++ b/FrontendFolder/switch-room/src/views/AgreedMatchView.vue @@ -0,0 +1,7 @@ +<template> + <agreed-match-page></agreed-match-page> +</template> + +<script setup lang="ts"> +import AgreedMatchPage from "@/components/AgreedMatchPage.vue"; +</script> \ No newline at end of file