diff --git a/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/enums/message/OfficialNoticeTypeEnum.java b/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/enums/message/OfficialNoticeTypeEnum.java index 811e0e3f..95c6d0e7 100644 --- a/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/enums/message/OfficialNoticeTypeEnum.java +++ b/rc-service/rc-inner-api/external-inner/external-inner-model/src/main/java/com/red/circle/external/inner/model/enums/message/OfficialNoticeTypeEnum.java @@ -252,6 +252,11 @@ public enum OfficialNoticeTypeEnum { */ FAMILY_APPLY_APPROVAL, + /** + * CP告白信封 + */ + CP_LOVE_LETTER, + ; diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/user/UserRelationErrorCode.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/user/UserRelationErrorCode.java index daf3a0f7..53d4e8e2 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/user/UserRelationErrorCode.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/asserts/user/UserRelationErrorCode.java @@ -67,6 +67,11 @@ public enum UserRelationErrorCode implements IResponseErrorCode { */ UNAVAILABLE_NOT_REGION(4312, "Unavailable to operate not same region"), + /** + * CP关系不存在. + */ + CP_RELATIONSHIP_NOT_EXISTS(4313, "CP relationship does not exist"), + ; diff --git a/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/enums/GoldOrigin.java b/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/enums/GoldOrigin.java index 77d6cbfb..5962bc6e 100644 --- a/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/enums/GoldOrigin.java +++ b/rc-service/rc-inner-api/wallet-inner/wallet-inner-model/src/main/java/com/red/circle/wallet/inner/model/enums/GoldOrigin.java @@ -734,9 +734,14 @@ public enum GoldOrigin { FAMILY_BOX_REWARD("Family box reward"), /** - * 房间红包 + * 用户红包 */ USER_RED_PACKET("User red packet"), + + /** + * CP告白信封 + */ + CP_LOVE_LETTER("CP love letter"), ; private final String desc; diff --git a/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/user/relation/CpRelationshipRestController.java b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/user/relation/CpRelationshipRestController.java index c32f59b2..d9114b6b 100644 --- a/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/user/relation/CpRelationshipRestController.java +++ b/rc-service/rc-service-other/other-adapter/src/main/java/com/red/circle/other/adapter/app/user/relation/CpRelationshipRestController.java @@ -2,14 +2,15 @@ package com.red.circle.other.adapter.app.user.relation; import com.red.circle.common.business.dto.cmd.AppExtCommand; import com.red.circle.common.business.dto.cmd.UserIdCmd; -import com.red.circle.common.business.dto.cmd.UserIdCommonCmd; import com.red.circle.framework.web.controller.BaseController; -import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinUserProfileCO; +import com.red.circle.other.app.command.user.LoveLetterQueryCmdExe; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO; -import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileCO; +import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO; +import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd; +import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd; import com.red.circle.other.app.service.user.relation.UserCpRelationService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -39,6 +40,7 @@ import java.util.List; public class CpRelationshipRestController extends BaseController { private final UserCpRelationService userCpRelationService; + private final LoveLetterQueryCmdExe loveLetterQueryCmdExe; /** * 获取我的-cp对象. @@ -105,4 +107,43 @@ public class CpRelationshipRestController extends BaseController { return userCpRelationService.getCpApplyId(cmd); } + /** + * 发送CP告白信封. + * + * @eo.name 发送CP告白信封 + * @eo.url /send-love-letter + * @eo.method post + * @eo.request-type json + */ + @PostMapping("/send-love-letter") + public void sendLoveLetter(@RequestBody @Validated SendLoveLetterCmd cmd) { + userCpRelationService.sendLoveLetter(cmd); + } + + /** + * 查询告白墙. + * + * @eo.name 查询告白墙 + * @eo.url /love-letter-wall + * @eo.method get + * @eo.request-type formdata + */ + @GetMapping("/love-letter-wall") + public List getLoveLetterWall() { + return loveLetterQueryCmdExe.queryWall(); + } + + /** + * 查询我收到的最新告白信封. + * + * @eo.name 查询我收到的最新告白信封 + * @eo.url /latest-love-letter + * @eo.method get + * @eo.request-type formdata + */ + @GetMapping("/latest-love-letter") + public LoveLetterDetailCO getLatestLoveLetter(AppExtCommand cmd) { + return loveLetterQueryCmdExe.queryLatestReceived(cmd); + } + } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/LoveLetterQueryCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/LoveLetterQueryCmdExe.java new file mode 100644 index 00000000..fbab7307 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/LoveLetterQueryCmdExe.java @@ -0,0 +1,99 @@ +package com.red.circle.other.app.command.user; + +import com.red.circle.common.business.dto.cmd.AppExtCommand; +import com.red.circle.other.domain.gateway.user.CpLoveLetterGateway; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.domain.model.cp.CpLoveLetterDomain; +import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO; +import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO; +import com.red.circle.other.domain.model.user.UserProfile; +import com.red.circle.other.infra.convertor.user.CpLoveLetterConvertor; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 查询CP告白信封 + */ +@Component +@RequiredArgsConstructor +public class LoveLetterQueryCmdExe { + + private final CpLoveLetterGateway cpLoveLetterGateway; + private final UserProfileGateway userProfileGateway; + private final CpLoveLetterConvertor cpLoveLetterConvertor; + + /** + * 查询告白墙(最新50条) + */ + public List queryWall() { + List letters = cpLoveLetterGateway.listLatestLetters(50); + if (letters.isEmpty()) { + return new ArrayList<>(); + } + + // 批量查询发送者信息 + Set senderIds = letters.stream() + .map(CpLoveLetterDomain::getSenderId) + .collect(Collectors.toSet()); + + // 批量查询发送者信息 + Set receiverIds = letters.stream() + .map(CpLoveLetterDomain::getReceiverId) + .collect(Collectors.toSet()); + senderIds.addAll(receiverIds); + + Map userProfileMap = userProfileGateway.listByUserIds(senderIds) + .stream() + .collect(Collectors.toMap(UserProfile::getId, Function.identity())); + + // 组装返回数据 + return letters.stream() + .map(letter -> { + LoveLetterWallCO co = cpLoveLetterConvertor.toWallCO(letter); + UserProfile profile = userProfileMap.get(letter.getSenderId()); + if (Objects.nonNull(profile)) { + co.setSenderNickname(profile.getUserNickname()); + co.setSenderAvatar(profile.getUserAvatar()); + } + UserProfile receiver = userProfileMap.get(letter.getReceiverId()); + if (Objects.nonNull(receiver)) { + co.setReceiverNickname(receiver.getUserNickname()); + co.setReceiverAvatar(receiver.getUserAvatar()); + } + return co; + }) + .collect(Collectors.toList()); + } + + /** + * 查询我收到的最新信封 + */ + public LoveLetterDetailCO queryLatestReceived(AppExtCommand cmd) { + Long userId = cmd.requiredReqUserId(); + + CpLoveLetterDomain letter = cpLoveLetterGateway.getLatestReceivedLetter(userId); + if (Objects.isNull(letter)) { + return null; + } + + // 标记为已读 + if (letter.isUnread()) { + cpLoveLetterGateway.markAsRead(letter.getId()); + } + + // 查询发送者信息 + UserProfile senderProfile = userProfileGateway.getByUserId(letter.getSenderId()); + + LoveLetterDetailCO co = cpLoveLetterConvertor.toDetailCO(letter); + if (Objects.nonNull(senderProfile)) { + co.setSenderNickname(senderProfile.getUserNickname()); + co.setSenderAvatar(senderProfile.getUserAvatar()); + } + + return co; + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/SendLoveLetterCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/SendLoveLetterCmdExe.java new file mode 100644 index 00000000..19167dd7 --- /dev/null +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/SendLoveLetterCmdExe.java @@ -0,0 +1,108 @@ +package com.red.circle.other.app.command.user; + +import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient; +import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd; +import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum; +import com.red.circle.framework.core.asserts.ResponseAssert; +import com.red.circle.other.app.convertor.user.UserProfileAppConvertor; +import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd; +import com.red.circle.other.app.util.OfficialNoticeUtils; +import com.red.circle.other.domain.gateway.user.CpLoveLetterGateway; +import com.red.circle.other.domain.gateway.user.UserProfileGateway; +import com.red.circle.other.domain.model.cp.CpLoveLetterDomain; +import com.red.circle.other.infra.database.rds.service.user.user.CpRelationshipService; +import com.red.circle.other.inner.asserts.user.UserRelationErrorCode; +import com.red.circle.other.inner.model.dto.user.UserProfileDTO; +import com.red.circle.tool.core.date.TimestampUtils; +import com.red.circle.tool.core.tuple.PennyAmount; +import com.red.circle.wallet.inner.endpoint.wallet.WalletGoldClient; +import com.red.circle.wallet.inner.error.WalletErrorCode; +import com.red.circle.wallet.inner.model.cmd.GoldReceiptCmd; +import com.red.circle.wallet.inner.model.enums.GoldOrigin; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.Map; + +/** + * 发送CP告白信封 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class SendLoveLetterCmdExe { + + private final CpLoveLetterGateway cpLoveLetterGateway; + private final CpRelationshipService cpRelationshipService; + private final WalletGoldClient walletGoldClient; + private final OfficialNoticeClient officialNoticeClient; + private final UserProfileGateway userProfileGateway; + private final UserProfileAppConvertor userProfileAppConvertor; + + private static final Long LOVE_LETTER_COST = 5000L; + + @Transactional(rollbackFor = Exception.class) + public void execute(SendLoveLetterCmd cmd) { + Long senderId = cmd.requiredReqUserId(); + Long receiverId = cmd.getReceiverId(); + + // 校验双方是否已建立CP关系 + boolean cpExists = cpRelationshipService.existsCp(senderId, receiverId); + ResponseAssert.isTrue(UserRelationErrorCode.CP_RELATIONSHIP_NOT_EXISTS, cpExists); + + // 扣除金币 + deductGold(senderId, cmd); + + // 保存信封 + CpLoveLetterDomain letter = new CpLoveLetterDomain(); + letter.setSenderId(senderId); + letter.setReceiverId(receiverId); + letter.setContent(cmd.getContent()); + letter.setStatus(0); + letter.setCostGold(LOVE_LETTER_COST); + letter.setCreateTime(TimestampUtils.now()); + letter.setUpdateTime(TimestampUtils.now()); + cpLoveLetterGateway.save(letter); + + // 发送System消息通知 + sendNotification(senderId, receiverId, cmd.getContent(), letter.getId()); + } + + private void deductGold(Long userId, SendLoveLetterCmd cmd) { + GoldReceiptCmd build = GoldReceiptCmd.builder() + .appExpenditure() + .userId(userId) + .amount(PennyAmount.ofDollar(LOVE_LETTER_COST)) + .eventId(String.valueOf(userId)) + .sysOrigin(cmd.getReqSysOrigin().getOrigin()) + .origin(GoldOrigin.CP_LOVE_LETTER) + .build(); + + try { + walletGoldClient.changeBalance(build); + } catch (Exception e) { + log.error("扣除金币失败 userId={} cost={}", userId, LOVE_LETTER_COST, e); + ResponseAssert.isTrue(WalletErrorCode.INSUFFICIENT_BALANCE, false); + } + } + + private void sendNotification(Long senderId, Long receiverId, String content, Long letterId) { + UserProfileDTO senderProfile = userProfileAppConvertor.toUserProfileDTO( + userProfileGateway.getByUserId(senderId) + ); + + Map templateParam = OfficialNoticeUtils.buildUserProfile(senderProfile); + templateParam.put("content", content); + officialNoticeClient.send(NoticeExtTemplateTypeCmd.builder() + .toAccount(receiverId) + .noticeType(OfficialNoticeTypeEnum.CP_LOVE_LETTER) + .templateParam(templateParam) + .expand(letterId) + .build() + ); + } +} diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/user/relation/UserCpRelationServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/user/relation/UserCpRelationServiceImpl.java index 6fc9b0de..5e280092 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/user/relation/UserCpRelationServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/user/relation/UserCpRelationServiceImpl.java @@ -4,8 +4,10 @@ import com.red.circle.common.business.dto.cmd.AppExtCommand; import com.red.circle.common.business.dto.cmd.UserIdCmd; import com.red.circle.common.business.dto.cmd.UserIdCommonCmd; import com.red.circle.other.app.command.user.DismissCpApplyCmdExe; +import com.red.circle.other.app.command.user.LoveLetterQueryCmdExe; import com.red.circle.other.app.command.user.ProcessCpApplyCmd; import com.red.circle.other.app.command.user.SendCpApplyCmdExe; +import com.red.circle.other.app.command.user.SendLoveLetterCmdExe; import com.red.circle.other.app.command.user.query.CpApplyIdQryExe; import com.red.circle.other.app.command.user.query.UserCpPairUserProfileQryExe; import com.red.circle.other.app.command.user.query.UserCpRelationQryExe; @@ -13,9 +15,12 @@ import com.red.circle.other.app.command.user.query.UserCpRelationQueryV4Exe; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpCabinUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpPairUserProfileCO; import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileCO; +import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO; +import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd; +import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd; import com.red.circle.other.app.util.DistributedLockUtil; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -39,6 +44,8 @@ public class UserCpRelationServiceImpl implements UserCpRelationService { private final UserCpPairUserProfileQryExe userCpPairUserProfileQryExe; private final UserCpRelationQueryV4Exe userCpRelationQueryV4Exe; private final DistributedLockUtil distributedLockUtil; + private final SendLoveLetterCmdExe sendLoveLetterCmdExe; + private final LoveLetterQueryCmdExe loveLetterQueryCmdExe; @Override public CpUserProfileCO getCpUser(UserIdCmd cmd) { @@ -81,5 +88,13 @@ public class UserCpRelationServiceImpl implements UserCpRelationService { return userCpRelationQueryV4Exe.execute(cmd); } + @Override + public void sendLoveLetter(SendLoveLetterCmd cmd) { + String key = "SEND_LOVE_LETTER:" + cmd.getReqUserId(); + distributedLockUtil.executeWithLock(key, () -> { + sendLoveLetterCmdExe.execute(cmd); + }); + + } } diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/user/relation/cp/loveletter/SendLoveLetterCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/user/relation/cp/loveletter/SendLoveLetterCmd.java new file mode 100644 index 00000000..f441e16f --- /dev/null +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/user/relation/cp/loveletter/SendLoveLetterCmd.java @@ -0,0 +1,26 @@ +package com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.red.circle.common.business.dto.cmd.AppExtCommand; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 发送CP告白信封 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SendLoveLetterCmd extends AppExtCommand { + + @NotNull(message = "接收者用户ID不能为空") + @JsonSerialize(using = ToStringSerializer.class) + private Long receiverId; + + @NotBlank(message = "告白内容不能为空") + @Size(max = 80, message = "告白内容不能超过80字符") + private String content; +} diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/user/relation/UserCpRelationService.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/user/relation/UserCpRelationService.java index 6ced1d5f..a1d8219f 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/user/relation/UserCpRelationService.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/service/user/relation/UserCpRelationService.java @@ -9,6 +9,7 @@ import com.red.circle.other.app.dto.clientobject.user.relation.cp.CpUserProfileC import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.CpApplyDismissCmd; import com.red.circle.other.app.dto.cmd.user.relation.cp.ProcessApplyCmd; +import com.red.circle.other.app.dto.cmd.user.relation.cp.loveletter.SendLoveLetterCmd; import java.util.List; @@ -33,5 +34,6 @@ public interface UserCpRelationService { CpCabinUserProfileCO getCpUserV4(UserIdCommonCmd cmd); + void sendLoveLetter(SendLoveLetterCmd cmd); } diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/user/CpLoveLetterGateway.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/user/CpLoveLetterGateway.java new file mode 100644 index 00000000..674bb161 --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/gateway/user/CpLoveLetterGateway.java @@ -0,0 +1,31 @@ +package com.red.circle.other.domain.gateway.user; + +import com.red.circle.other.domain.model.cp.CpLoveLetterDomain; + +import java.util.List; + +/** + * CP告白信封数据网关 + */ +public interface CpLoveLetterGateway { + + /** + * 保存信封 + */ + void save(CpLoveLetterDomain letter); + + /** + * 查询告白墙最新N条 + */ + List listLatestLetters(Integer limit); + + /** + * 查询用户收到的最新一条信封 + */ + CpLoveLetterDomain getLatestReceivedLetter(Long receiverId); + + /** + * 更新信封状态为已读 + */ + void markAsRead(Long letterId); +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/cp/CpLoveLetterDomain.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/cp/CpLoveLetterDomain.java new file mode 100644 index 00000000..50098c5f --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/cp/CpLoveLetterDomain.java @@ -0,0 +1,43 @@ +package com.red.circle.other.domain.model.cp; + +import lombok.Data; + +import java.sql.Timestamp; +import java.time.LocalDateTime; + +/** + * CP告白信封领域模型 + */ +@Data +public class CpLoveLetterDomain { + + private Long id; + + private Long senderId; + + private Long receiverId; + + private String content; + + private Integer status; + + private Long costGold; + + private Timestamp createTime; + + private Timestamp updateTime; + + /** + * 是否未读 + */ + public boolean isUnread() { + return status == 0; + } + + /** + * 标记为已读 + */ + public void markAsRead() { + this.status = 1; + } +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/cp/loveletter/LoveLetterDetailCO.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/cp/loveletter/LoveLetterDetailCO.java new file mode 100644 index 00000000..1ae0ee4e --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/cp/loveletter/LoveLetterDetailCO.java @@ -0,0 +1,31 @@ +package com.red.circle.other.domain.model.cp.loveletter; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.sql.Timestamp; +import java.time.LocalDateTime; + +/** + * 告白信封详情 + */ +@Data +public class LoveLetterDetailCO { + + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + @JsonSerialize(using = ToStringSerializer.class) + private Long senderId; + + private String senderNickname; + + private String senderAvatar; + + private String content; + + private Integer status; + + private Timestamp createTime; +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/cp/loveletter/LoveLetterWallCO.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/cp/loveletter/LoveLetterWallCO.java new file mode 100644 index 00000000..2045fcba --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/cp/loveletter/LoveLetterWallCO.java @@ -0,0 +1,33 @@ +package com.red.circle.other.domain.model.cp.loveletter; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import lombok.Data; + +import java.sql.Timestamp; +import java.time.LocalDateTime; + +/** + * 告白墙信封 + */ +@Data +public class LoveLetterWallCO { + + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + @JsonSerialize(using = ToStringSerializer.class) + private Long senderId; + + private String senderNickname; + + private String senderAvatar; + + private String receiverNickname; + + private String receiverAvatar; + + private String content; + + private Timestamp createTime; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/user/CpLoveLetterConvertor.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/user/CpLoveLetterConvertor.java new file mode 100644 index 00000000..718b8a54 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/user/CpLoveLetterConvertor.java @@ -0,0 +1,17 @@ +package com.red.circle.other.infra.convertor.user; + +import com.red.circle.other.domain.model.cp.CpLoveLetterDomain; +import com.red.circle.other.domain.model.cp.loveletter.LoveLetterDetailCO; +import com.red.circle.other.domain.model.cp.loveletter.LoveLetterWallCO; +import org.mapstruct.Mapper; + +/** + * CP告白信封转换器 + */ +@Mapper(componentModel = "spring") +public interface CpLoveLetterConvertor { + + LoveLetterWallCO toWallCO(CpLoveLetterDomain letter); + + LoveLetterDetailCO toDetailCO(CpLoveLetterDomain letter); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/user/user/CpLoveLetterDAO.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/user/user/CpLoveLetterDAO.java new file mode 100644 index 00000000..b77a7858 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/dao/user/user/CpLoveLetterDAO.java @@ -0,0 +1,23 @@ +package com.red.circle.other.infra.database.rds.dao.user.user; + +import com.red.circle.framework.mybatis.dao.BaseDAO; +import com.red.circle.other.infra.database.rds.entity.user.user.CpLoveLetter; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * CP告白信封 Mapper + */ +public interface CpLoveLetterDAO extends BaseDAO { + + /** + * 查询告白墙最新N条信封 + */ + List listLatestLetters(@Param("limit") Integer limit); + + /** + * 查询用户收到的最新一条信封 + */ + CpLoveLetter getLatestReceivedLetter(@Param("receiverId") Long receiverId); +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/user/user/CpLoveLetter.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/user/user/CpLoveLetter.java new file mode 100644 index 00000000..d747c7d5 --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/user/user/CpLoveLetter.java @@ -0,0 +1,37 @@ +package com.red.circle.other.infra.database.rds.entity.user.user; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * CP告白信封 + */ +@Data +@TableName("cp_love_letter") +public class CpLoveLetter { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long senderId; + + private Long receiverId; + + private String content; + + private Integer status; + + private Long costGold; + + @TableLogic + private Integer isDeleted; + + private LocalDateTime createTime; + + private LocalDateTime updateTime; +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/CpLoveLetterGatewayImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/CpLoveLetterGatewayImpl.java new file mode 100644 index 00000000..3bec7a3e --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/CpLoveLetterGatewayImpl.java @@ -0,0 +1,84 @@ +package com.red.circle.other.infra.gateway.user; + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.red.circle.other.domain.gateway.user.CpLoveLetterGateway; +import com.red.circle.other.domain.model.cp.CpLoveLetterDomain; +import com.red.circle.other.infra.database.rds.dao.user.user.CpLoveLetterDAO; +import com.red.circle.other.infra.database.rds.entity.user.user.CpLoveLetter; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.sql.Timestamp; +import java.util.List; +import java.util.stream.Collectors; + +/** + * CP告白信封数据网关实现 + */ +@Component +@RequiredArgsConstructor +public class CpLoveLetterGatewayImpl implements CpLoveLetterGateway { + + private final CpLoveLetterDAO cpLoveLetterDAO; + + @Override + public void save(CpLoveLetterDomain letter) { + CpLoveLetter entity = toEntity(letter); + cpLoveLetterDAO.insert(entity); + letter.setId(entity.getId()); + } + + @Override + public List listLatestLetters(Integer limit) { + List entities = cpLoveLetterDAO.listLatestLetters(limit); + return entities.stream() + .map(this::toDomain) + .collect(Collectors.toList()); + } + + @Override + public CpLoveLetterDomain getLatestReceivedLetter(Long receiverId) { + CpLoveLetter entity = cpLoveLetterDAO.getLatestReceivedLetter(receiverId); + return entity == null ? null : toDomain(entity); + } + + @Override + public void markAsRead(Long letterId) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(CpLoveLetter::getId, letterId) + .set(CpLoveLetter::getStatus, 1); + cpLoveLetterDAO.update(null, wrapper); + } + + /** + * 领域模型转Entity + */ + private CpLoveLetter toEntity(CpLoveLetterDomain domain) { + CpLoveLetter entity = new CpLoveLetter(); + entity.setId(domain.getId()); + entity.setSenderId(domain.getSenderId()); + entity.setReceiverId(domain.getReceiverId()); + entity.setContent(domain.getContent()); + entity.setStatus(domain.getStatus()); + entity.setCostGold(domain.getCostGold()); + entity.setCreateTime(domain.getCreateTime().toLocalDateTime()); + entity.setUpdateTime(domain.getUpdateTime().toLocalDateTime()); + return entity; + } + + /** + * Entity转领域模型 + */ + private CpLoveLetterDomain toDomain(CpLoveLetter entity) { + CpLoveLetterDomain domain = new CpLoveLetterDomain(); + domain.setId(entity.getId()); + domain.setSenderId(entity.getSenderId()); + domain.setReceiverId(entity.getReceiverId()); + domain.setContent(entity.getContent()); + domain.setStatus(entity.getStatus()); + domain.setCostGold(entity.getCostGold()); + domain.setCreateTime(Timestamp.valueOf(entity.getCreateTime())); + domain.setUpdateTime(Timestamp.valueOf(entity.getUpdateTime())); + return domain; + } +} diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/user/user/CpLoveLetterDAO.xml b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/user/user/CpLoveLetterDAO.xml new file mode 100644 index 00000000..58383cef --- /dev/null +++ b/rc-service/rc-service-other/other-infrastructure/src/main/resources/dao/user/user/CpLoveLetterDAO.xml @@ -0,0 +1,20 @@ + + + + + + + +