diff --git a/rc-common-business/common-business-core/src/main/java/com/red/circle/common/business/core/enums/DataApprovalTypeEnum.java b/rc-common-business/common-business-core/src/main/java/com/red/circle/common/business/core/enums/DataApprovalTypeEnum.java index 623556d2..630b9cc1 100644 --- a/rc-common-business/common-business-core/src/main/java/com/red/circle/common/business/core/enums/DataApprovalTypeEnum.java +++ b/rc-common-business/common-business-core/src/main/java/com/red/circle/common/business/core/enums/DataApprovalTypeEnum.java @@ -87,6 +87,16 @@ public enum DataApprovalTypeEnum { * 团队昵称. */ TEAM_NICKNAME("团队昵称", Type.TEXT), + + /** + * 背景照片. + */ + BACKGROUND_PHOTO("背景照片", Type.IMAGE), + + /** + * 个人形象照片. + */ + PERSONAL_PHOTO("个人形象照片", Type.IMAGE), ; private final String desc; diff --git a/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/approval/CensorProfileEvent.java b/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/approval/CensorProfileEvent.java index 22d0469b..793644f4 100644 --- a/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/approval/CensorProfileEvent.java +++ b/rc-common-business/common-business-mq/business-mq-model/src/main/java/com/red/circle/mq/business/model/event/approval/CensorProfileEvent.java @@ -77,6 +77,14 @@ public class CensorProfileEvent implements Serializable { return Objects.equals(approvalType, DataApprovalTypeEnum.ROOM_AVATAR); } + public boolean checkBackgroundPhoto() { + return Objects.equals(approvalType, DataApprovalTypeEnum.BACKGROUND_PHOTO); + } + + public boolean checkPersonalPhoto() { + return Objects.equals(approvalType, DataApprovalTypeEnum.PERSONAL_PHOTO); + } + public CensorProfileEvent addContent(CensorContent content) { if (Objects.isNull(this.contents)) { this.contents = Lists.newArrayList(); @@ -104,7 +112,9 @@ public class CensorProfileEvent implements Serializable { public boolean isImageType() { return checkUserAvatar() || checkDynamicContent() - || checkRoomAvatar(); + || checkRoomAvatar() + || checkBackgroundPhoto() + || checkPersonalPhoto(); } } diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/user/PhotoItem.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/user/PhotoItem.java new file mode 100644 index 00000000..aedaedef --- /dev/null +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/user/PhotoItem.java @@ -0,0 +1,40 @@ +package com.red.circle.other.inner.model.dto.user; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.sql.Timestamp;import java.time.LocalDateTime; + +/** + * 图片项(含审核状态) + */ +@Data +@Accessors(chain = true) +public class PhotoItem implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 图片URL + */ + private String url; + + /** + * 审核状态:0-待审核 1-审核通过 2-审核拒绝 + */ + private Integer status; + + /** + * 拒绝原因 + */ + private String rejectReason; + + /** + * 上传时间 + */ + private Timestamp createTime; + +} diff --git a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/user/UserProfileDTO.java b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/user/UserProfileDTO.java index c554f079..075b3021 100644 --- a/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/user/UserProfileDTO.java +++ b/rc-service/rc-inner-api/other-inner/other-inner-model/src/main/java/com/red/circle/other/inner/model/dto/user/UserProfileDTO.java @@ -16,6 +16,7 @@ import java.io.Serial; import java.io.Serializable; import java.sql.Timestamp; import java.text.SimpleDateFormat; +import java.time.LocalDateTime; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -110,6 +111,17 @@ public class UserProfileDTO implements Serializable { */ private String hobby; + /** + * 背景照片列表(最多9张) + */ + private List backgroundPhotos; + + /** + * 个人形象照片列表(最多9张) + */ + private List personalPhotos; + + private String inRoomId; private String roomIcon; diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/UpdateUserProfileCmdExe.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/UpdateUserProfileCmdExe.java index 6397acce..1cbcea45 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/UpdateUserProfileCmdExe.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/command/user/UpdateUserProfileCmdExe.java @@ -5,7 +5,6 @@ import com.red.circle.common.business.core.ImageSizeConst; import com.red.circle.common.business.core.SensitiveWordFilter; import com.red.circle.common.business.core.constant.KeywordConstant; import com.red.circle.common.business.core.enums.DataApprovalTypeEnum; -import com.red.circle.common.business.core.enums.SysOriginPlatformEnum; import com.red.circle.component.redis.service.RedisService; import com.red.circle.external.inner.endpoint.oss.OssServiceClient; import com.red.circle.framework.core.asserts.ResponseAssert; @@ -24,8 +23,11 @@ import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService import com.red.circle.other.inner.asserts.DynamicErrorCode; import com.red.circle.tool.core.collection.CollectionUtils; import com.red.circle.tool.core.text.StringUtils; +import com.red.circle.other.domain.enums.PhotoAuditStatus; +import com.red.circle.other.inner.model.dto.user.PhotoItem; import com.red.circle.other.inner.asserts.user.UserErrorCode; import com.red.circle.other.inner.model.dto.user.UserProfileDTO; + import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -79,6 +81,16 @@ public class UpdateUserProfileCmdExe { ResponseAssert.notNull(CommonErrorCode.UPDATE_FAILURE, updateUserProfile); updateUserProfile.setId(userProfile.getId()); + // 处理背景照片 + if (CollectionUtils.isNotEmpty(cmd.getBackgroundPhotos())) { + updateUserProfile.setBackgroundPhotos(userProfileAppConvertor.toPhotoItems(cmd.getBackgroundPhotos())); + } + + // 处理个人形象照片 + if (CollectionUtils.isNotEmpty(cmd.getPersonalPhotos())) { + updateUserProfile.setPersonalPhotos(userProfileAppConvertor.toPhotoItems(cmd.getPersonalPhotos())); + } + updateUserProfile.setUserNickname( StringUtils.isNotBlank(updateUserProfile.getUserNickname()) ? KeywordConstant.filter(updateUserProfile.getUserNickname()) @@ -152,6 +164,36 @@ public class UpdateUserProfileCmdExe { ) ); } + // 背景照片审核 + if (CollectionUtils.isNotEmpty(updateUserProfile.getBackgroundPhotos())) { + for (PhotoItem photo : updateUserProfile.getBackgroundPhotos()) { + if (StringUtils.isNotBlank(photo.getUrl()) && PhotoAuditStatus.PENDING.getCode().equals(photo.getStatus())) { + approvalParam.add( + new ProfileApprovalContent() + .setBeUserId(updateUserProfile.getId()) + .setSysOrigin(updateUserProfile.getOriginSys()) + .setApprovalType(DataApprovalTypeEnum.BACKGROUND_PHOTO) + .addContent(updateUserProfile.getId(), photo.getUrl()) + ); + } + } + } + + // 个人形象照片审核 + if (CollectionUtils.isNotEmpty(updateUserProfile.getPersonalPhotos())) { + for (PhotoItem photo : updateUserProfile.getPersonalPhotos()) { + if (StringUtils.isNotBlank(photo.getUrl()) && PhotoAuditStatus.PENDING.getCode().equals(photo.getStatus())) { + approvalParam.add( + new ProfileApprovalContent() + .setBeUserId(updateUserProfile.getId()) + .setSysOrigin(updateUserProfile.getOriginSys()) + .setApprovalType(DataApprovalTypeEnum.PERSONAL_PHOTO) + .addContent(updateUserProfile.getId(), photo.getUrl()) + ); + } + } + } + if (CollectionUtils.isNotEmpty(approvalParam)) { profileApprovalGateway.submitApprovalBatch(approvalParam); } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/user/UserProfileAppConvertor.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/user/UserProfileAppConvertor.java index 4ab39e9b..da387d2f 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/user/UserProfileAppConvertor.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/convertor/user/UserProfileAppConvertor.java @@ -10,10 +10,14 @@ import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.inner.model.dto.material.BadgeConfigDetailsDTO; import com.red.circle.other.inner.model.cmd.user.account.CreateAccountCmd; import com.red.circle.other.inner.model.dto.user.UserProfileDTO; +import com.red.circle.other.domain.enums.PhotoAuditStatus; +import com.red.circle.other.inner.model.dto.user.PhotoItem; import com.red.circle.other.inner.model.dto.user.props.UserUseBadgeDTO; import java.sql.Timestamp; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.mapstruct.Mapper; /** @@ -60,4 +64,20 @@ public interface UserProfileAppConvertor { List toListBadgeConfigDetailsDTO(List userUseBadges); + /** + * 将图片URL列表转为PhotoItem列表 + */ + default List toPhotoItems(List urls) { + if (urls == null || urls.isEmpty()) { + return null; + } + return urls.stream() + .filter(url -> url != null && !url.trim().isEmpty()) + .map(url -> new PhotoItem() + .setUrl(url) + .setStatus(PhotoAuditStatus.PENDING.getCode()) + .setCreateTime(Timestamp.valueOf(LocalDateTime.now()))) + .collect(Collectors.toList()); + } + } diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/CensorProfileListener.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/CensorProfileListener.java index 8f38b5d0..20688690 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/CensorProfileListener.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/listener/CensorProfileListener.java @@ -1,5 +1,6 @@ package com.red.circle.other.app.listener; +import com.alibaba.fastjson.JSON; import com.red.circle.common.business.core.enums.ApprovalStatusEnum; import com.red.circle.common.business.core.enums.DataApprovalTypeEnum; import com.red.circle.component.mq.MessageEventProcess; @@ -13,20 +14,24 @@ import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient; import com.red.circle.external.inner.endpoint.oss.OssServiceClient; import com.red.circle.external.inner.model.dto.CensorImageResponseDTO; import com.red.circle.external.inner.model.dto.CensorSuggestion; -import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum; import com.red.circle.mq.business.model.event.approval.CensorContent; import com.red.circle.mq.business.model.event.approval.CensorProfileEvent; import com.red.circle.mq.rocket.business.streams.CensorProfileSink; import com.red.circle.other.app.common.room.DynamicCommon; +import com.red.circle.other.domain.enums.PhotoAuditStatus; import com.red.circle.other.domain.gateway.user.UserProfileGateway; import com.red.circle.other.domain.model.user.UserProfile; +import com.red.circle.other.infra.convertor.user.UserProfileInfraConvertor; import com.red.circle.other.infra.database.cache.service.other.EnumConfigCacheService; import com.red.circle.other.infra.database.mongo.service.live.RoomProfileManagerService; +import com.red.circle.other.infra.database.mongo.service.user.profile.UserRunProfileService; import com.red.circle.other.infra.database.rds.entity.approval.ApprovalUserViolationHistory; import com.red.circle.other.infra.database.rds.entity.dynamic.DynamicPicture; +import com.red.circle.other.infra.database.rds.entity.user.user.BaseInfo; import com.red.circle.other.infra.database.rds.service.approval.ApprovalUserSettingDataService; import com.red.circle.other.infra.database.rds.service.approval.ApprovalUserViolationHistoryService; import com.red.circle.other.infra.database.rds.service.dynamic.DynamicPictureService; +import com.red.circle.other.infra.database.rds.service.user.user.BaseInfoService; import com.red.circle.other.inner.enums.config.EnumConfigKey; import com.red.circle.other.inner.enums.other.PhotoCensorStatusEnum; import com.red.circle.tool.core.collection.CollectionUtils; @@ -60,6 +65,9 @@ public class CensorProfileListener implements MessageListener { private final RoomProfileManagerService roomProfileManagerService; private final ApprovalUserSettingDataService approvalUserSettingDataService; private final ApprovalUserViolationHistoryService approvalUserViolationHistoryService; + private final UserRunProfileService userRunProfileService; + private final BaseInfoService baseInfoService; + private final UserProfileInfraConvertor userProfileInfraConvertor; @Override public Action consume(ConsumerMessage message) { @@ -101,6 +109,16 @@ public class CensorProfileListener implements MessageListener { if (eventBody.checkDynamicContent()) { dynamicImageCensor(eventBody); + return; + } + + if (eventBody.checkBackgroundPhoto()) { + batchPhotoCensor(eventBody, DataApprovalTypeEnum.BACKGROUND_PHOTO); + return; + } + + if (eventBody.checkPersonalPhoto()) { + batchPhotoCensor(eventBody, DataApprovalTypeEnum.PERSONAL_PHOTO); } } @@ -248,6 +266,125 @@ public class CensorProfileListener implements MessageListener { } } + private void batchPhotoCensor(CensorProfileEvent param, DataApprovalTypeEnum approvalType) { + if (CollectionUtils.isEmpty(param.getContents())) { + return; + } + + UserProfile userProfile = userProfileGateway.getByUserId(param.getUserId()); + if (userProfile == null) { + log.warn("用户不存在: {}", param.getUserId()); + return; + } + + for (CensorContent censorContent : param.getContents()) { + try { + CensorImageResponseDTO response = censorImageClient.auditing( + censorContent.compression300Content()).getBody(); + + String labelNames = response.formatLabelString(); + + if (response.checkBlock()) { + // 审核不通过,记录违规 + photoViolationHandle(param.getUserId(), censorContent.getContent(), + labelNames, approvalType); + // 删除违规图片 + ossServiceClient.remove(censorContent.getContent()); + // 更新MongoDB中图片状态 + updatePhotoStatus(param.getUserId(), censorContent.getContent(), + PhotoAuditStatus.REJECTED.getCode(), labelNames, approvalType); + } else if (response.checkPass()) { + // 审核通过 + approvalUserSettingDataService.updateApprovalStatusMachineLabel( + param.getUserId(), ApprovalStatusEnum.PASS, approvalType, labelNames); + // 更新MongoDB中图片状态 + updatePhotoStatus(param.getUserId(), censorContent.getContent(), + PhotoAuditStatus.APPROVED.getCode(), null, approvalType); + } else { + // 待人工审核 + approvalUserSettingDataService.updateApprovalStatusMachineLabel( + param.getUserId(), ApprovalStatusEnum.PENDING, approvalType, labelNames); + } + } catch (Exception ex) { + log.error("图片审核异常: userId={}, url={}", param.getUserId(), + censorContent.getContent(), ex); + // 调用失败进入人工审核 + approvalUserSettingDataService.updateApprovalStatusMachineLabel( + param.getUserId(), ApprovalStatusEnum.PENDING, approvalType, "鉴定服务调用异常"); + } + } + } + + private void updatePhotoStatus(Long userId, String photoUrl, Integer status, + String rejectReason, DataApprovalTypeEnum approvalType) { + // 更新 MongoDB + if (DataApprovalTypeEnum.BACKGROUND_PHOTO.equals(approvalType)) { + userRunProfileService.updateBackgroundPhotoStatus(userId, photoUrl, status, rejectReason); + } else if (DataApprovalTypeEnum.PERSONAL_PHOTO.equals(approvalType)) { + userRunProfileService.updatePersonalPhotoStatus(userId, photoUrl, status, rejectReason); + } + + // 同步更新 MySQL + try { + UserProfile userProfile = userProfileGateway.getByUserId(userId); + if (userProfile == null) { + return; + } + + BaseInfo baseInfo = new BaseInfo(); + baseInfo.setId(userId); + + if (DataApprovalTypeEnum.BACKGROUND_PHOTO.equals(approvalType) + && CollectionUtils.isNotEmpty(userProfile.getBackgroundPhotos())) { + // 更新列表中图片状态 + userProfile.getBackgroundPhotos().stream() + .filter(photo -> photo.getUrl().equals(photoUrl)) + .findFirst() + .ifPresent(photo -> { + photo.setStatus(status); + photo.setRejectReason(rejectReason); + }); + baseInfo.setBackgroundPhoto(JSON.toJSONString(userProfile.getBackgroundPhotos())); + baseInfoService.updateSelectiveById(baseInfo); + } else if (DataApprovalTypeEnum.PERSONAL_PHOTO.equals(approvalType) + && CollectionUtils.isNotEmpty(userProfile.getPersonalPhotos())) { + // 更新列表中图片状态 + userProfile.getPersonalPhotos().stream() + .filter(photo -> photo.getUrl().equals(photoUrl)) + .findFirst() + .ifPresent(photo -> { + photo.setStatus(status); + photo.setRejectReason(rejectReason); + }); + baseInfo.setPersonalPhoto(JSON.toJSONString(userProfile.getPersonalPhotos())); + baseInfoService.updateSelectiveById(baseInfo); + } + } catch (Exception e) { + log.error("更新MySQL图片状态失败: userId={}, photoUrl={}", userId, photoUrl, e); + } + } + + private void photoViolationHandle(Long userId, String content, String labelNames, + DataApprovalTypeEnum approvalType) { + // 刷新审批标签 + approvalUserSettingDataService.updateApprovalStatusMachineLabel( + userId, ApprovalStatusEnum.NOT_PASS, approvalType, labelNames); + + // 记录审批历史 + ApprovalUserViolationHistory approvalHistory = new ApprovalUserViolationHistory() + .setUserId(userId) + .setViolationType(approvalType.name()) + .setLabelNames(labelNames) + .setApprovalResult(ApprovalStatusEnum.NOT_PASS.name()) + .setContentId(userId) + .setContent(content) + .setDescription("机器审核"); + approvalHistory.setCreateUser(0L); + approvalHistory.setUpdateUser(0L); + approvalHistory.setCreateTime(TimestampUtils.now()); + approvalUserViolationHistoryService.save(approvalHistory); + } + private void userAvatarApprovalNotPassHandle(CensorProfileEvent param, String content, String avatarLabelNames) { // 更新审批状态 diff --git a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/user/user/UserProfileModifyCmd.java b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/user/user/UserProfileModifyCmd.java index 15183b01..ceddf41d 100644 --- a/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/user/user/UserProfileModifyCmd.java +++ b/rc-service/rc-service-other/other-client/src/main/java/com/red/circle/other/app/dto/cmd/user/user/UserProfileModifyCmd.java @@ -6,6 +6,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; +import java.util.List; + /** * @author pengliang on 2019/10/30 19:52 */ @@ -55,6 +57,16 @@ public class UserProfileModifyCmd extends AppExtCommand { */ private Integer userSex; + /** + * 背景照片列表(最多9张) + */ + private List backgroundPhotos; + + /** + * 个人形象照片列表(最多9张) + */ + private List personalPhotos; + /** * 国家id. */ diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/enums/PhotoAuditStatus.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/enums/PhotoAuditStatus.java new file mode 100644 index 00000000..04c71c20 --- /dev/null +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/enums/PhotoAuditStatus.java @@ -0,0 +1,32 @@ +package com.red.circle.other.domain.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 图片审核状态枚举 + */ +@Getter +@AllArgsConstructor +public enum PhotoAuditStatus { + + PENDING(0, "待审核"), + APPROVED(1, "审核通过"), + REJECTED(2, "审核拒绝"); + + private final Integer code; + private final String desc; + + public static PhotoAuditStatus of(Integer code) { + if (code == null) { + return null; + } + for (PhotoAuditStatus status : values()) { + if (status.code.equals(code)) { + return status; + } + } + return null; + } + +} diff --git a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/user/UserProfile.java b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/user/UserProfile.java index 619375f3..b2e86338 100644 --- a/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/user/UserProfile.java +++ b/rc-service/rc-service-other/other-domain/src/main/java/com/red/circle/other/domain/model/user/UserProfile.java @@ -120,6 +120,16 @@ public class UserProfile implements Serializable { */ private Integer bornDay; + /** + * 背景照片列表(最多9张) + */ + private List backgroundPhotos; + + /** + * 个人形象照片列表(最多9张) + */ + private List personalPhotos; + /** * 0.未删除 1.已删除. */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/user/UserProfileInfraConvertor.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/user/UserProfileInfraConvertor.java index 4c899cf5..2ad2c1f5 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/user/UserProfileInfraConvertor.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/convertor/user/UserProfileInfraConvertor.java @@ -1,9 +1,13 @@ package com.red.circle.other.infra.convertor.user; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import com.red.circle.common.business.dto.cmd.AppExtCommand; import com.red.circle.framework.core.convertor.ConvertorModel; import com.red.circle.mq.business.model.event.user.LoginLoggerEvent; import com.red.circle.other.domain.model.user.OwnSpecialId; +import com.red.circle.other.inner.model.dto.user.PhotoItem; import com.red.circle.other.domain.model.user.UserConsumptionLevel; import com.red.circle.other.domain.model.user.UserProfile; import com.red.circle.other.infra.database.cache.entity.user.ConsumptionLevelCache; @@ -20,7 +24,9 @@ import com.red.circle.other.inner.model.dto.material.UsePropsDTO; import com.red.circle.other.inner.model.dto.user.UserProfileDTO; import com.red.circle.other.inner.model.dto.user.props.UserUsePropsDTO; import java.sql.Timestamp; +import java.util.Collections; import java.util.List; + import org.mapstruct.Mapper; /** @@ -29,6 +35,8 @@ import org.mapstruct.Mapper; @Mapper(componentModel = ConvertorModel.SPRING) public interface UserProfileInfraConvertor { + ObjectMapper MAPPER = new ObjectMapper(); + UserProfileDTO toUserProfileDTO(BaseInfo baseInfo); List toListUsePropsDTO(List useProps); @@ -82,4 +90,32 @@ public interface UserProfileInfraConvertor { .setLanguage(cmd.getReqLanguage()) .setReqTime(new Timestamp(cmd.getReqTime().getTime())); } + + /** + * 将PhotoItem列表转为JSON字符串 + */ + default String photoItemsToJson(List photoItems) { + if (photoItems == null || photoItems.isEmpty()) { + return null; + } + try { + return MAPPER.writeValueAsString(photoItems); + } catch (JsonProcessingException e) { + return null; + } + } + + /** + * 将JSON字符串转为PhotoItem列表 + */ + default List jsonToPhotoItems(String json) { + if (json == null || json.trim().isEmpty()) { + return null; + } + try { + return MAPPER.readValue(json, new TypeReference>() {}); + } catch (JsonProcessingException e) { + return Collections.emptyList(); + } + } } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/user/profile/UserRunProfile.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/user/profile/UserRunProfile.java index 8504fd23..f13cab99 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/user/profile/UserRunProfile.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/entity/user/profile/UserRunProfile.java @@ -2,6 +2,7 @@ package com.red.circle.other.infra.database.mongo.entity.user.profile; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.red.circle.other.inner.model.dto.user.PhotoItem; import com.red.circle.other.inner.model.dto.material.UseBadgeDTO; import com.red.circle.other.inner.model.dto.material.UsePropsDTO; import com.red.circle.tool.core.collection.CollectionUtils; @@ -99,6 +100,17 @@ public class UserRunProfile implements Serializable { */ private Integer bornDay; + /** + * 背景照片列表(最多9张) + */ + private List backgroundPhotos; + + /** + * 个人形象照片列表(最多9张) + */ + private List personalPhotos; + + /** * 账号状态. */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/profile/UserRunProfileService.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/profile/UserRunProfileService.java index 22d01cdb..dc67dde7 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/profile/UserRunProfileService.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/profile/UserRunProfileService.java @@ -111,4 +111,14 @@ public interface UserRunProfileService { */ void remove(Collection ids); + /** + * 更新背景照片审核状态 + */ + void updateBackgroundPhotoStatus(Long userId, String photoUrl, Integer status, String rejectReason); + + /** + * 更新个人形象照片审核状态 + */ + void updatePersonalPhotoStatus(Long userId, String photoUrl, Integer status, String rejectReason); + } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/profile/impl/UserRunProfileServiceImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/profile/impl/UserRunProfileServiceImpl.java index 266ac977..b3630d54 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/profile/impl/UserRunProfileServiceImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/mongo/service/user/profile/impl/UserRunProfileServiceImpl.java @@ -227,6 +227,14 @@ public class UserRunProfileServiceImpl implements UserRunProfileService { update.set("hobby", userRunProfile.getHobby()); } + if (CollectionUtils.isNotEmpty(userRunProfile.getBackgroundPhotos())) { + update.set("backgroundPhotos", userRunProfile.getBackgroundPhotos()); + } + + if (CollectionUtils.isNotEmpty(userRunProfile.getPersonalPhotos())) { + update.set("personalPhotos", userRunProfile.getPersonalPhotos()); + } + if (Objects.nonNull(userRunProfile.getOwnSpecialId())) { update.set("ownSpecialId", userRunProfile.getOwnSpecialId()); } @@ -436,4 +444,52 @@ public class UserRunProfileServiceImpl implements UserRunProfileService { private boolean checkAvailable(Long expireTime) { return Objects.isNull(expireTime) || expireTime > TimestampUtils.now().getTime(); } + + @Override + public void updateBackgroundPhotoStatus(Long userId, String photoUrl, Integer status, String rejectReason) { + UserRunProfile profile = getById(userId); + if (profile == null || CollectionUtils.isEmpty(profile.getBackgroundPhotos())) { + return; + } + + profile.getBackgroundPhotos().stream() + .filter(photo -> photo.getUrl().equals(photoUrl)) + .findFirst() + .ifPresent(photo -> { + photo.setStatus(status); + photo.setRejectReason(rejectReason); + }); + + mongoTemplate.updateFirst( + Query.query(Criteria.where("id").is(userId)), + new Update() + .set("backgroundPhotos", profile.getBackgroundPhotos()) + .set("updateTime", TimestampUtils.now()), + UserRunProfile.class + ); + } + + @Override + public void updatePersonalPhotoStatus(Long userId, String photoUrl, Integer status, String rejectReason) { + UserRunProfile profile = getById(userId); + if (profile == null || CollectionUtils.isEmpty(profile.getPersonalPhotos())) { + return; + } + + profile.getPersonalPhotos().stream() + .filter(photo -> photo.getUrl().equals(photoUrl)) + .findFirst() + .ifPresent(photo -> { + photo.setStatus(status); + photo.setRejectReason(rejectReason); + }); + + mongoTemplate.updateFirst( + Query.query(Criteria.where("id").is(userId)), + new Update() + .set("personalPhotos", profile.getPersonalPhotos()) + .set("updateTime", TimestampUtils.now()), + UserRunProfile.class + ); + } } diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/user/user/BaseInfo.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/user/user/BaseInfo.java index 432f0a45..2c13001e 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/user/user/BaseInfo.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/database/rds/entity/user/user/BaseInfo.java @@ -107,6 +107,18 @@ public class BaseInfo extends TimestampBaseEntity { @TableField("born_day") private Integer bornDay; + /** + * 背景照片(JSON) + */ + @TableField("background_photo") + private String backgroundPhoto; + + /** + * 个人形象照片(JSON) + */ + @TableField("personal_photo") + private String personalPhoto; + /** * 账号状态. */ diff --git a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/UserProfileGatewayImpl.java b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/UserProfileGatewayImpl.java index 5e602e91..e746ad77 100644 --- a/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/UserProfileGatewayImpl.java +++ b/rc-service/rc-service-other/other-infrastructure/src/main/java/com/red/circle/other/infra/gateway/user/UserProfileGatewayImpl.java @@ -1,5 +1,6 @@ package com.red.circle.other.infra.gateway.user; +import com.alibaba.fastjson.JSON; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -170,7 +171,17 @@ public class UserProfileGatewayImpl implements UserProfileGateway { if (Objects.isNull(baseInfo)) { return null; } - return userProfileInfraConvertor.toUserProfile(baseInfo); + UserProfile userProfile = userProfileInfraConvertor.toUserProfile(baseInfo); + + // 从 MySQL 的 JSON 字段转换图片列表 + if (StringUtils.isNotBlank(baseInfo.getBackgroundPhoto())) { + userProfile.setBackgroundPhotos(userProfileInfraConvertor.jsonToPhotoItems(baseInfo.getBackgroundPhoto())); + } + if (StringUtils.isNotBlank(baseInfo.getPersonalPhoto())) { + userProfile.setPersonalPhotos(userProfileInfraConvertor.jsonToPhotoItems(baseInfo.getPersonalPhoto())); + } + + return userProfile; } private OwnSpecialId toOwnSpecialId(UserSpecialId userSpecialId) { @@ -263,6 +274,15 @@ public class UserProfileGatewayImpl implements UserProfileGateway { if (StringUtils.isNotBlank(userProfile.getCheckStatus())){ baseInfo.setAccountStatus(userProfile.getCheckStatus()); } + + // 转换图片列表为JSON存储到MySQL + if (CollectionUtils.isNotEmpty(userProfile.getBackgroundPhotos())) { + baseInfo.setBackgroundPhoto(JSON.toJSONString(userProfile.getBackgroundPhotos())); + } + if (CollectionUtils.isNotEmpty(userProfile.getPersonalPhotos())) { + baseInfo.setPersonalPhoto(JSON.toJSONString(userProfile.getPersonalPhotos())); + } + baseInfoService.updateSelectiveById(baseInfo); userRunProfileService.updateSelectiveById( userProfileInfraConvertor.toUserRunProfile(userProfile));