新增消息功能
1. 发布功能发送广播消息,保存public_message 2. 公告消息查询功能
This commit is contained in:
parent
d93aa00fbe
commit
1dde8a11af
@ -114,6 +114,16 @@ public enum GroupMessageTypeEnum {
|
||||
/**
|
||||
* 发送礼物
|
||||
*/
|
||||
SEND_GIFT
|
||||
SEND_GIFT,
|
||||
|
||||
/**
|
||||
* 系统公告
|
||||
*/
|
||||
SYS_ANNOUNCEMENT,
|
||||
|
||||
/**
|
||||
* 系统活动
|
||||
*/
|
||||
SYS_ACTIVITY,
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,76 @@
|
||||
package com.red.circle.other.inner.model.dto.message;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 公共消息DTO.
|
||||
*
|
||||
* @author your_name
|
||||
* @date 2025-10-14
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class PublicMessageDTO implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* MongoDB主键.
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 业务消息ID.
|
||||
*/
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 消息类型:notification-通知,activity-活动.
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 消息标题.
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 消息内容.
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息图片URL.
|
||||
*/
|
||||
private String imageUrl;
|
||||
|
||||
/**
|
||||
* 扩展字段,存储额外信息.
|
||||
*/
|
||||
private Map<String, Object> extraData;
|
||||
|
||||
/**
|
||||
* 创建时间.
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long createdAt;
|
||||
|
||||
/**
|
||||
* 过期时间.
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long expiresAt;
|
||||
|
||||
/**
|
||||
* 消息状态:active-激活,deleted-删除.
|
||||
*/
|
||||
private String status;
|
||||
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
package com.red.circle.other.adapter.app.message;
|
||||
|
||||
import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.framework.web.controller.BaseController;
|
||||
import com.red.circle.other.infra.convertor.message.PublicMessageConvertor;
|
||||
import com.red.circle.other.inner.model.dto.message.PublicMessageDTO;
|
||||
import com.red.circle.other.adapter.app.message.dto.PublicMessagePageQry;
|
||||
import com.red.circle.other.infra.database.mongo.entity.message.PublicMessage;
|
||||
import com.red.circle.other.infra.database.mongo.service.message.PublicMessageService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 公共消息控制器.
|
||||
* </p>
|
||||
*
|
||||
* @author your_name
|
||||
* @since 2025-10-14
|
||||
* @eo.api-type http
|
||||
* @eo.groupName 消息管理
|
||||
* @eo.path /public-message
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping(value = "/public-message", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class PublicMessageController extends BaseController {
|
||||
|
||||
private final PublicMessageService publicMessageService;
|
||||
private final PublicMessageConvertor publicMessageConvertor;
|
||||
|
||||
/**
|
||||
* 分页查询公共消息列表.
|
||||
*
|
||||
* @param qry 查询条件
|
||||
* @return 分页结果
|
||||
* @eo.name 分页查询公共消息列表
|
||||
* @eo.url /page
|
||||
* @eo.method get
|
||||
* @eo.request-type formdata
|
||||
*/
|
||||
@GetMapping("/page")
|
||||
public PageResult<PublicMessageDTO> pagePublicMessages(@Validated PublicMessagePageQry qry) {
|
||||
log.info("分页查询公共消息, qry={}", qry);
|
||||
|
||||
// 调用服务层分页查询
|
||||
Page<PublicMessage> page = publicMessageService.pagePublicMessages(
|
||||
qry.getType(),
|
||||
qry.getPageNo(),
|
||||
qry.getPageSize()
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
List<PublicMessageDTO> dtoList = page.getContent().stream()
|
||||
.map(publicMessageConvertor::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<PublicMessageDTO> pageResult = new PageResult<>();
|
||||
pageResult.setSize(page.getSize());
|
||||
|
||||
pageResult.setCurrent(qry.getPageNo());
|
||||
pageResult.setRecords(dtoList);
|
||||
pageResult.setTotal(page.getTotalElements());
|
||||
|
||||
// 构建分页结果
|
||||
return pageResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询最新的公共消息列表.
|
||||
*
|
||||
* @param qry 查询条件
|
||||
* @return 消息列表
|
||||
* @eo.name 查询最新的公共消息列表
|
||||
* @eo.url /latest
|
||||
* @eo.method get
|
||||
* @eo.request-type formdata
|
||||
*/
|
||||
// @GetMapping("/latest")
|
||||
public List<PublicMessageDTO> listLatestMessages(@Validated PublicMessagePageQry qry) {
|
||||
log.info("查询最新公共消息, qry={}", qry);
|
||||
|
||||
// 如果没有传limit,默认查询10条
|
||||
Integer limit = qry.getPageSize() != null ? qry.getPageSize() : 10;
|
||||
|
||||
// 调用服务层查询
|
||||
List<PublicMessage> messages = publicMessageService.listLatestMessages(
|
||||
qry.getType(),
|
||||
limit
|
||||
);
|
||||
|
||||
// 转换为DTO
|
||||
return messages.stream()
|
||||
.map(publicMessageConvertor::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
package com.red.circle.other.adapter.app.message.dto;
|
||||
|
||||
import com.red.circle.common.business.dto.PageQueryCmd;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* 公共消息分页查询参数.
|
||||
*
|
||||
* @author your_name
|
||||
* @date 2025-10-14
|
||||
*/
|
||||
@Data
|
||||
public class PublicMessagePageQry {
|
||||
|
||||
/**
|
||||
* 消息类型:notification-通知,activity-活动(null-查询全部).
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 页码(从1开始).
|
||||
*/
|
||||
@NotNull(message = "页码不能为空")
|
||||
@Min(value = 1, message = "页码最小为1")
|
||||
private Integer pageNo;
|
||||
|
||||
/**
|
||||
* 每页大小.
|
||||
*/
|
||||
@NotNull(message = "每页大小不能为空")
|
||||
@Min(value = 1, message = "每页大小最小为1")
|
||||
@Max(value = 100, message = "每页大小最大为100")
|
||||
private Integer pageSize;
|
||||
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package com.red.circle.other.infra.convertor.message;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.message.PublicMessage;
|
||||
import com.red.circle.other.inner.model.dto.message.PublicMessageDTO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
|
||||
/**
|
||||
* 公共消息转换器.
|
||||
*
|
||||
* @author your_name
|
||||
* @date 2025-10-14
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
public interface PublicMessageConvertor {
|
||||
|
||||
/**
|
||||
* Entity转DTO.
|
||||
*
|
||||
* @param entity 实体对象
|
||||
* @return DTO对象
|
||||
*/
|
||||
@Mappings({
|
||||
@Mapping(target = "createdAt", expression = "java(entity.getCreatedAt() != null ? entity.getCreatedAt().getTime() : null)"),
|
||||
@Mapping(target = "expiresAt", expression = "java(entity.getExpiresAt() != null ? entity.getExpiresAt().getTime() : null)")
|
||||
})
|
||||
PublicMessageDTO toDTO(PublicMessage entity);
|
||||
|
||||
/**
|
||||
* DTO转Entity.
|
||||
*
|
||||
* @param dto DTO对象
|
||||
* @return 实体对象
|
||||
*/
|
||||
@Mappings({
|
||||
@Mapping(target = "createdAt", expression = "java(dto.getCreatedAt() != null ? new java.sql.Timestamp(dto.getCreatedAt()) : null)"),
|
||||
@Mapping(target = "expiresAt", expression = "java(dto.getExpiresAt() != null ? new java.sql.Timestamp(dto.getExpiresAt()) : null)")
|
||||
})
|
||||
PublicMessage toEntity(PublicMessageDTO dto);
|
||||
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package com.red.circle.other.infra.database.mongo.entity.message;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
|
||||
/**
|
||||
* 公共消息实体类.
|
||||
*
|
||||
* @author tf
|
||||
* @date 2025-10-14
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Document("public_messages")
|
||||
public class PublicMessage implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* MongoDB主键.
|
||||
*/
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 业务消息ID.
|
||||
*/
|
||||
@Field("message_id")
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 消息类型:notification-通知,activity-活动.
|
||||
*/
|
||||
@Field("type")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 消息标题.
|
||||
*/
|
||||
@Field("title")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 消息内容.
|
||||
*/
|
||||
@Field("content")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息图片URL.
|
||||
*/
|
||||
@Field("image_url")
|
||||
private String imageUrl;
|
||||
|
||||
/**
|
||||
* 扩展字段,存储额外信息.
|
||||
*/
|
||||
@Field("extra_data")
|
||||
private Map<String, Object> extraData;
|
||||
|
||||
/**
|
||||
* 创建时间.
|
||||
*/
|
||||
@Field("created_at")
|
||||
private Timestamp createdAt;
|
||||
|
||||
/**
|
||||
* 过期时间.
|
||||
*/
|
||||
@Field("expires_at")
|
||||
private Timestamp expiresAt;
|
||||
|
||||
/**
|
||||
* 消息状态:active-激活,deleted-删除.
|
||||
*/
|
||||
@Field("status")
|
||||
private String status;
|
||||
|
||||
}
|
||||
@ -0,0 +1,134 @@
|
||||
package com.red.circle.other.infra.database.mongo.entity.message;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
|
||||
/**
|
||||
* 用户消息实体类.
|
||||
*
|
||||
* @author tf
|
||||
* @date 2025-10-14
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Document("user_messages")
|
||||
public class UserMessage implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* MongoDB主键.
|
||||
*/
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 业务消息ID.
|
||||
*/
|
||||
@Field("message_id")
|
||||
private String messageId;
|
||||
|
||||
/**
|
||||
* 消息类型.
|
||||
*/
|
||||
@Field("type")
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 发送者类型:system-系统,user-用户.
|
||||
*/
|
||||
@Field("from_type")
|
||||
private String fromType;
|
||||
|
||||
/**
|
||||
* 发送者用户ID(系统消息为null).
|
||||
*/
|
||||
@Field("from_user_id")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long fromUserId;
|
||||
|
||||
/**
|
||||
* 发送者用户名.
|
||||
*/
|
||||
@Field("from_user_name")
|
||||
private String fromUserName;
|
||||
|
||||
/**
|
||||
* 发送者头像URL.
|
||||
*/
|
||||
@Field("from_user_avatar")
|
||||
private String fromUserAvatar;
|
||||
|
||||
/**
|
||||
* 接收者用户ID.
|
||||
*/
|
||||
@Field("to_user_id")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long toUserId;
|
||||
|
||||
/**
|
||||
* 消息标题.
|
||||
*/
|
||||
@Field("title")
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 消息内容.
|
||||
*/
|
||||
@Field("content")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 消息动作类型.
|
||||
*/
|
||||
@Field("action_type")
|
||||
private String actionType;
|
||||
|
||||
/**
|
||||
* 动作相关数据.
|
||||
*/
|
||||
@Field("action_data")
|
||||
private Map<String, Object> actionData;
|
||||
|
||||
/**
|
||||
* 是否已读.
|
||||
*/
|
||||
@Field("is_read")
|
||||
private Boolean isRead;
|
||||
|
||||
/**
|
||||
* 读取时间.
|
||||
*/
|
||||
@Field("read_at")
|
||||
private Date readAt;
|
||||
|
||||
/**
|
||||
* 用户是否删除.
|
||||
*/
|
||||
@Field("is_deleted")
|
||||
private Boolean isDeleted;
|
||||
|
||||
/**
|
||||
* 创建时间.
|
||||
*/
|
||||
@Field("created_at")
|
||||
private Timestamp createdAt;
|
||||
|
||||
/**
|
||||
* 过期时间.
|
||||
*/
|
||||
@Field("expires_at")
|
||||
private Timestamp expiresAt;
|
||||
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package com.red.circle.other.infra.database.mongo.service.message;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.message.PublicMessage;
|
||||
import java.util.List;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/**
|
||||
* 公共消息服务接口.
|
||||
*
|
||||
* @author tf
|
||||
* @date 2025-10-14
|
||||
*/
|
||||
public interface PublicMessageService {
|
||||
|
||||
/**
|
||||
* 分页查询公共消息列表.
|
||||
*
|
||||
* @param type 消息类型(notification/activity,null-查询全部)
|
||||
* @param pageNo 页码(从1开始)
|
||||
* @param pageSize 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
Page<PublicMessage> pagePublicMessages(String type, Integer pageNo, Integer pageSize);
|
||||
|
||||
/**
|
||||
* 查询最新的N条消息.
|
||||
*
|
||||
* @param type 消息类型(notification/activity,null-查询全部)
|
||||
* @param limit 查询数量限制
|
||||
* @return 消息列表
|
||||
*/
|
||||
List<PublicMessage> listLatestMessages(String type, Integer limit);
|
||||
|
||||
/**
|
||||
* 根据消息ID查询.
|
||||
*
|
||||
* @param messageId 消息ID
|
||||
* @return 消息实体
|
||||
*/
|
||||
PublicMessage getByMessageId(String messageId);
|
||||
|
||||
/**
|
||||
* 创建公共消息.
|
||||
*
|
||||
* @param message 消息实体
|
||||
* @return 创建后的消息实体
|
||||
*/
|
||||
PublicMessage createPublicMessage(PublicMessage message);
|
||||
|
||||
/**
|
||||
* 批量创建公共消息.
|
||||
*
|
||||
* @param messages 消息列表
|
||||
* @return 创建后的消息列表
|
||||
*/
|
||||
List<PublicMessage> batchCreatePublicMessages(List<PublicMessage> messages);
|
||||
|
||||
/**
|
||||
* 更新消息状态.
|
||||
*
|
||||
* @param messageId 消息ID
|
||||
* @param status 状态(active/deleted)
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean updateStatus(String messageId, String status);
|
||||
|
||||
/**
|
||||
* 删除消息(逻辑删除).
|
||||
*
|
||||
* @param messageId 消息ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean deleteMessage(String messageId);
|
||||
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
package com.red.circle.other.infra.database.mongo.service.message;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.message.UserMessage;
|
||||
import java.util.List;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
/**
|
||||
* 用户消息服务接口.
|
||||
*
|
||||
* @author tf
|
||||
* @date 2025-10-14
|
||||
*/
|
||||
public interface UserMessageService {
|
||||
|
||||
/**
|
||||
* 分页查询用户消息列表.
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param isRead 是否已读(null-全部,true-已读,false-未读)
|
||||
* @param pageNo 页码(从1开始)
|
||||
* @param pageSize 每页大小
|
||||
* @return 分页结果
|
||||
*/
|
||||
Page<UserMessage> pageUserMessages(Long userId, Boolean isRead, Integer pageNo, Integer pageSize);
|
||||
|
||||
/**
|
||||
* 查询未读消息数量.
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 未读消息数量
|
||||
*/
|
||||
Long countUnreadMessages(Long userId);
|
||||
|
||||
/**
|
||||
* 根据消息ID查询.
|
||||
*
|
||||
* @param messageId 消息ID
|
||||
* @return 消息实体
|
||||
*/
|
||||
UserMessage getByMessageId(String messageId);
|
||||
|
||||
/**
|
||||
* 根据动作类型查询消息列表.
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param actionType 动作类型
|
||||
* @param limit 查询数量限制
|
||||
* @return 消息列表
|
||||
*/
|
||||
List<UserMessage> listByActionType(Long userId, String actionType, Integer limit);
|
||||
|
||||
/**
|
||||
* 创建用户消息.
|
||||
*
|
||||
* @param message 消息实体
|
||||
* @return 创建后的消息实体
|
||||
*/
|
||||
UserMessage createUserMessage(UserMessage message);
|
||||
|
||||
/**
|
||||
* 批量创建用户消息.
|
||||
*
|
||||
* @param messages 消息列表
|
||||
* @return 创建后的消息列表
|
||||
*/
|
||||
List<UserMessage> batchCreateUserMessages(List<UserMessage> messages);
|
||||
|
||||
/**
|
||||
* 标记消息已读.
|
||||
*
|
||||
* @param messageId 消息ID
|
||||
* @param userId 用户ID(校验消息归属)
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean markAsRead(String messageId, Long userId);
|
||||
|
||||
/**
|
||||
* 批量标记消息已读.
|
||||
*
|
||||
* @param messageIds 消息ID列表
|
||||
* @param userId 用户ID(校验消息归属)
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean batchMarkAsRead(List<String> messageIds, Long userId);
|
||||
|
||||
/**
|
||||
* 标记所有消息已读.
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean markAllAsRead(Long userId);
|
||||
|
||||
/**
|
||||
* 删除消息(逻辑删除).
|
||||
*
|
||||
* @param messageId 消息ID
|
||||
* @param userId 用户ID(校验消息归属)
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean deleteMessage(String messageId, Long userId);
|
||||
|
||||
/**
|
||||
* 批量删除消息.
|
||||
*
|
||||
* @param messageIds 消息ID列表
|
||||
* @param userId 用户ID(校验消息归属)
|
||||
* @return 是否成功
|
||||
*/
|
||||
Boolean batchDeleteMessages(List<String> messageIds, Long userId);
|
||||
|
||||
}
|
||||
@ -0,0 +1,172 @@
|
||||
package com.red.circle.other.infra.database.mongo.service.message.impl;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.message.PublicMessage;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.service.message.PublicMessageService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 公共消息服务实现类.
|
||||
*
|
||||
* @author tf
|
||||
* @date 2025-10-14
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class PublicMessageServiceImpl implements PublicMessageService {
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
@Override
|
||||
public Page<PublicMessage> pagePublicMessages(String type, Integer pageNo, Integer pageSize) {
|
||||
log.info("分页查询公共消息, type={}, pageNo={}, pageSize={}", type, pageNo, pageSize);
|
||||
|
||||
// 构建查询条件
|
||||
Criteria criteria = Criteria.where("status").is("active");
|
||||
|
||||
if (StringUtils.hasText(type)) {
|
||||
criteria.and("type").is(type);
|
||||
}
|
||||
|
||||
Query query = new Query(criteria);
|
||||
|
||||
// 分页和排序
|
||||
Pageable pageable = PageRequest.of(pageNo - 1, pageSize,
|
||||
Sort.by(Sort.Direction.DESC, "created_at"));
|
||||
query.with(pageable);
|
||||
|
||||
// 查询数据
|
||||
List<PublicMessage> messages = mongoTemplate.find(query, PublicMessage.class);
|
||||
|
||||
// 查询总数
|
||||
long total = mongoTemplate.count(Query.of(query).limit(-1).skip(-1), PublicMessage.class);
|
||||
|
||||
return PageableExecutionUtils.getPage(messages, pageable, () -> total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PublicMessage> listLatestMessages(String type, Integer limit) {
|
||||
log.info("查询最新公共消息, type={}, limit={}", type, limit);
|
||||
|
||||
// 构建查询条件
|
||||
Criteria criteria = Criteria.where("status").is("active");
|
||||
|
||||
if (StringUtils.hasText(type)) {
|
||||
criteria.and("type").is(type);
|
||||
}
|
||||
|
||||
Query query = new Query(criteria)
|
||||
.with(Sort.by(Sort.Direction.DESC, "created_at"))
|
||||
.limit(limit);
|
||||
|
||||
return mongoTemplate.find(query, PublicMessage.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicMessage getByMessageId(String messageId) {
|
||||
log.info("根据消息ID查询, messageId={}", messageId);
|
||||
|
||||
if (!StringUtils.hasText(messageId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Query query = new Query(Criteria.where("message_id").is(messageId));
|
||||
return mongoTemplate.findOne(query, PublicMessage.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublicMessage createPublicMessage(PublicMessage message) {
|
||||
log.info("创建公共消息, type={}, title={}", message.getType(), message.getTitle());
|
||||
|
||||
// 生成消息ID
|
||||
if (!StringUtils.hasText(message.getMessageId())) {
|
||||
message.setMessageId(generateMessageId());
|
||||
}
|
||||
|
||||
// 设置默认状态
|
||||
if (!StringUtils.hasText(message.getStatus())) {
|
||||
message.setStatus("active");
|
||||
}
|
||||
|
||||
// 设置创建时间
|
||||
Timestamp now = new Timestamp(System.currentTimeMillis());
|
||||
message.setCreatedAt(now);
|
||||
|
||||
// 保存到数据库
|
||||
return mongoTemplate.insert(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PublicMessage> batchCreatePublicMessages(List<PublicMessage> messages) {
|
||||
log.info("批量创建公共消息, count={}", messages.size());
|
||||
|
||||
if (CollectionUtils.isEmpty(messages)) {
|
||||
return messages;
|
||||
}
|
||||
|
||||
Timestamp now = new Timestamp(System.currentTimeMillis());
|
||||
|
||||
// 批量设置默认值
|
||||
messages.forEach(message -> {
|
||||
if (!StringUtils.hasText(message.getMessageId())) {
|
||||
message.setMessageId(generateMessageId());
|
||||
}
|
||||
if (!StringUtils.hasText(message.getStatus())) {
|
||||
message.setStatus("active");
|
||||
}
|
||||
message.setCreatedAt(now);
|
||||
});
|
||||
|
||||
// 批量插入
|
||||
return (List<PublicMessage>) mongoTemplate.insertAll(messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean updateStatus(String messageId, String status) {
|
||||
log.info("更新消息状态, messageId={}, status={}", messageId, status);
|
||||
|
||||
Query query = new Query(Criteria.where("message_id").is(messageId));
|
||||
Update update = new Update().set("status", status);
|
||||
|
||||
return mongoTemplate.updateFirst(query, update, PublicMessage.class)
|
||||
.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteMessage(String messageId) {
|
||||
log.info("删除公共消息, messageId={}", messageId);
|
||||
|
||||
Query query = new Query(Criteria.where("message_id").is(messageId));
|
||||
Update update = new Update().set("status", "deleted");
|
||||
|
||||
return mongoTemplate.updateFirst(query, update, PublicMessage.class)
|
||||
.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成消息ID.
|
||||
*
|
||||
* @return 消息ID
|
||||
*/
|
||||
private String generateMessageId() {
|
||||
return "msg_" + UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,251 @@
|
||||
package com.red.circle.other.infra.database.mongo.service.message.impl;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.entity.message.UserMessage;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.red.circle.other.infra.database.mongo.service.message.UserMessageService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.Update;
|
||||
import org.springframework.data.support.PageableExecutionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 用户消息服务实现类.
|
||||
*
|
||||
* @author tf
|
||||
* @date 2025-10-14
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class UserMessageServiceImpl implements UserMessageService {
|
||||
|
||||
private final MongoTemplate mongoTemplate;
|
||||
|
||||
@Override
|
||||
public Page<UserMessage> pageUserMessages(Long userId, Boolean isRead, Integer pageNo,
|
||||
Integer pageSize) {
|
||||
log.info("分页查询用户消息, userId={}, isRead={}, pageNo={}, pageSize={}",
|
||||
userId, isRead, pageNo, pageSize);
|
||||
|
||||
// 构建查询条件
|
||||
Criteria criteria = Criteria.where("to_user_id").is(userId)
|
||||
.and("is_deleted").is(false);
|
||||
|
||||
if (isRead != null) {
|
||||
criteria.and("is_read").is(isRead);
|
||||
}
|
||||
|
||||
Query query = new Query(criteria);
|
||||
|
||||
// 分页和排序
|
||||
Pageable pageable = PageRequest.of(pageNo - 1, pageSize,
|
||||
Sort.by(Sort.Direction.DESC, "created_at"));
|
||||
query.with(pageable);
|
||||
|
||||
// 查询数据
|
||||
List<UserMessage> messages = mongoTemplate.find(query, UserMessage.class);
|
||||
|
||||
// 查询总数
|
||||
long total = mongoTemplate.count(Query.of(query).limit(-1).skip(-1), UserMessage.class);
|
||||
|
||||
return PageableExecutionUtils.getPage(messages, pageable, () -> total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countUnreadMessages(Long userId) {
|
||||
log.info("查询未读消息数量, userId={}", userId);
|
||||
|
||||
Criteria criteria = Criteria.where("to_user_id").is(userId)
|
||||
.and("is_read").is(false)
|
||||
.and("is_deleted").is(false);
|
||||
|
||||
Query query = new Query(criteria);
|
||||
|
||||
return mongoTemplate.count(query, UserMessage.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserMessage getByMessageId(String messageId) {
|
||||
log.info("根据消息ID查询, messageId={}", messageId);
|
||||
|
||||
if (!StringUtils.hasText(messageId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Query query = new Query(Criteria.where("message_id").is(messageId));
|
||||
return mongoTemplate.findOne(query, UserMessage.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserMessage> listByActionType(Long userId, String actionType, Integer limit) {
|
||||
log.info("根据动作类型查询消息列表, userId={}, actionType={}, limit={}",
|
||||
userId, actionType, limit);
|
||||
|
||||
Criteria criteria = Criteria.where("to_user_id").is(userId)
|
||||
.and("action_type").is(actionType)
|
||||
.and("is_deleted").is(false);
|
||||
|
||||
Query query = new Query(criteria)
|
||||
.with(Sort.by(Sort.Direction.DESC, "created_at"))
|
||||
.limit(limit);
|
||||
|
||||
return mongoTemplate.find(query, UserMessage.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserMessage createUserMessage(UserMessage message) {
|
||||
log.info("创建用户消息, toUserId={}, fromType={}",
|
||||
message.getToUserId(), message.getFromType());
|
||||
|
||||
// 生成消息ID
|
||||
if (!StringUtils.hasText(message.getMessageId())) {
|
||||
message.setMessageId(generateMessageId());
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if (message.getIsRead() == null) {
|
||||
message.setIsRead(false);
|
||||
}
|
||||
if (message.getIsDeleted() == null) {
|
||||
message.setIsDeleted(false);
|
||||
}
|
||||
|
||||
// 设置创建时间
|
||||
Timestamp now = new Timestamp(System.currentTimeMillis());
|
||||
message.setCreatedAt(now);
|
||||
|
||||
// 保存到数据库
|
||||
return mongoTemplate.insert(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserMessage> batchCreateUserMessages(List<UserMessage> messages) {
|
||||
log.info("批量创建用户消息, count={}", messages.size());
|
||||
|
||||
if (CollectionUtils.isEmpty(messages)) {
|
||||
return messages;
|
||||
}
|
||||
|
||||
Timestamp now = new Timestamp(System.currentTimeMillis());
|
||||
|
||||
// 批量设置默认值
|
||||
messages.forEach(message -> {
|
||||
if (!StringUtils.hasText(message.getMessageId())) {
|
||||
message.setMessageId(generateMessageId());
|
||||
}
|
||||
if (message.getIsRead() == null) {
|
||||
message.setIsRead(false);
|
||||
}
|
||||
if (message.getIsDeleted() == null) {
|
||||
message.setIsDeleted(false);
|
||||
}
|
||||
message.setCreatedAt(now);
|
||||
});
|
||||
|
||||
// 批量插入
|
||||
return (List<UserMessage>) mongoTemplate.insertAll(messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean markAsRead(String messageId, Long userId) {
|
||||
log.info("标记消息已读, messageId={}, userId={}", messageId, userId);
|
||||
|
||||
Criteria criteria = Criteria.where("message_id").is(messageId)
|
||||
.and("to_user_id").is(userId);
|
||||
|
||||
Update update = new Update()
|
||||
.set("is_read", true)
|
||||
.set("read_at", new Timestamp(System.currentTimeMillis()));
|
||||
|
||||
return mongoTemplate.updateFirst(new Query(criteria), update, UserMessage.class)
|
||||
.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean batchMarkAsRead(List<String> messageIds, Long userId) {
|
||||
log.info("批量标记消息已读, userId={}, count={}", userId, messageIds.size());
|
||||
|
||||
if (CollectionUtils.isEmpty(messageIds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Criteria criteria = Criteria.where("message_id").in(messageIds)
|
||||
.and("to_user_id").is(userId);
|
||||
|
||||
Update update = new Update()
|
||||
.set("is_read", true)
|
||||
.set("read_at", new Timestamp(System.currentTimeMillis()));
|
||||
|
||||
return mongoTemplate.updateMulti(new Query(criteria), update, UserMessage.class)
|
||||
.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean markAllAsRead(Long userId) {
|
||||
log.info("标记所有消息已读, userId={}", userId);
|
||||
|
||||
Criteria criteria = Criteria.where("to_user_id").is(userId)
|
||||
.and("is_read").is(false)
|
||||
.and("is_deleted").is(false);
|
||||
|
||||
Update update = new Update()
|
||||
.set("is_read", true)
|
||||
.set("read_at", new Timestamp(System.currentTimeMillis()));
|
||||
|
||||
return mongoTemplate.updateMulti(new Query(criteria), update, UserMessage.class)
|
||||
.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean deleteMessage(String messageId, Long userId) {
|
||||
log.info("删除消息, messageId={}, userId={}", messageId, userId);
|
||||
|
||||
Criteria criteria = Criteria.where("message_id").is(messageId)
|
||||
.and("to_user_id").is(userId);
|
||||
|
||||
Update update = new Update().set("is_deleted", true);
|
||||
|
||||
return mongoTemplate.updateFirst(new Query(criteria), update, UserMessage.class)
|
||||
.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean batchDeleteMessages(List<String> messageIds, Long userId) {
|
||||
log.info("批量删除消息, userId={}, count={}", userId, messageIds.size());
|
||||
|
||||
if (CollectionUtils.isEmpty(messageIds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Criteria criteria = Criteria.where("message_id").in(messageIds)
|
||||
.and("to_user_id").is(userId);
|
||||
|
||||
Update update = new Update().set("is_deleted", true);
|
||||
|
||||
return mongoTemplate.updateMulti(new Query(criteria), update, UserMessage.class)
|
||||
.getModifiedCount() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成消息ID.
|
||||
*
|
||||
* @return 消息ID
|
||||
*/
|
||||
private String generateMessageId() {
|
||||
return "msg_" + UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,11 +1,21 @@
|
||||
package com.red.circle.other.app.inner.service.sys.impl;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.red.circle.common.business.core.enums.SysOriginPlatformEnum;
|
||||
import com.red.circle.external.inner.endpoint.message.ImGroupClient;
|
||||
import com.red.circle.external.inner.endpoint.message.NewsletterClient;
|
||||
import com.red.circle.external.inner.endpoint.message.OfficialNoticeClient;
|
||||
import com.red.circle.external.inner.endpoint.push.GoogleFirebasePushClient;
|
||||
import com.red.circle.external.inner.model.cmd.message.BroadcastGroupMsgBodyCmd;
|
||||
import com.red.circle.external.inner.model.cmd.message.NewsletterResultCmd;
|
||||
import com.red.circle.external.inner.model.cmd.message.google.GooglePushCmd;
|
||||
import com.red.circle.external.inner.model.cmd.message.google.PushMsgBodyCmd;
|
||||
import com.red.circle.external.inner.model.cmd.message.notice.official.NoticeExtTemplateTypeCmd;
|
||||
import com.red.circle.external.inner.model.enums.PushNoticeType;
|
||||
import com.red.circle.external.inner.model.enums.message.GroupMessageTypeEnum;
|
||||
import com.red.circle.external.inner.model.enums.message.NewsletterEvent;
|
||||
import com.red.circle.external.inner.model.enums.message.OfficialNoticeTypeEnum;
|
||||
import com.red.circle.framework.core.asserts.ResponseAssert;
|
||||
import com.red.circle.framework.core.response.CommonErrorCode;
|
||||
import com.red.circle.framework.core.response.ResponseErrorCode;
|
||||
@ -13,15 +23,24 @@ import com.red.circle.framework.dto.PageResult;
|
||||
import com.red.circle.framework.mybatis.constant.PageConstant;
|
||||
import com.red.circle.other.app.inner.convertor.sys.SysNoticeInnerConvertor;
|
||||
import com.red.circle.other.app.inner.service.sys.NoticeMessageClientService;
|
||||
import com.red.circle.other.app.service.user.user.UserProfileService;
|
||||
import com.red.circle.other.infra.database.mongo.entity.message.PublicMessage;
|
||||
import com.red.circle.other.infra.database.mongo.service.message.PublicMessageService;
|
||||
import com.red.circle.other.infra.database.rds.entity.sys.NoticeMessage;
|
||||
import com.red.circle.other.infra.database.rds.service.sys.NoticeMessageService;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysNoticeMessageCmd;
|
||||
import com.red.circle.other.inner.model.cmd.sys.SysNoticeMessageQryCmd;
|
||||
import com.red.circle.other.inner.model.dto.sys.SysNoticeMessageDTO;
|
||||
import com.red.circle.tool.core.collection.MapBuilder;
|
||||
import com.red.circle.tool.core.date.LocalDateUtils;
|
||||
import com.red.circle.tool.core.date.ZonedDateTimeAsiaRiyadhUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 系统公告通知消息 服务实现类.
|
||||
@ -36,7 +55,10 @@ public class NoticeMessageClientServiceImpl implements NoticeMessageClientServic
|
||||
|
||||
private final NoticeMessageService noticeMessageService;
|
||||
private final SysNoticeInnerConvertor sysNoticeInnerConvertor;
|
||||
private final GoogleFirebasePushClient googleFirebasePushClient;
|
||||
private final ImGroupClient imGroupClient;
|
||||
private final OfficialNoticeClient officialNoticeClient;
|
||||
private final NewsletterClient newsletterClient;
|
||||
private final PublicMessageService publicMessageService;
|
||||
|
||||
@Override
|
||||
public PageResult<SysNoticeMessageDTO> getNoticeMessage(SysNoticeMessageQryCmd query) {
|
||||
@ -94,9 +116,54 @@ public class NoticeMessageClientServiceImpl implements NoticeMessageClientServic
|
||||
@Override
|
||||
public void announcement(Long id) {
|
||||
ResponseAssert.notNull(ResponseErrorCode.REQUEST_PARAMETER_ERROR, id);
|
||||
|
||||
// 查询公告详情
|
||||
NoticeMessage noticeMessage = noticeMessageService.getById(id);
|
||||
if (noticeMessage == null) {
|
||||
throw new RuntimeException("Announcement does not exist.");
|
||||
}
|
||||
|
||||
// 更新公告状态
|
||||
noticeMessageService.update().set(NoticeMessage::getAnnouncement, Boolean.TRUE)
|
||||
.set(NoticeMessage::getAnnouncementTime, LocalDateUtils.nowTimestamp())
|
||||
.eq(NoticeMessage::getId, id)
|
||||
.execute();
|
||||
|
||||
// 发送IM群组广播
|
||||
imGroupClient.sendMessageBroadcast(
|
||||
BroadcastGroupMsgBodyCmd.builder()
|
||||
.toPlatform(SysOriginPlatformEnum.LIKEI)
|
||||
.type(GroupMessageTypeEnum.SYS_ANNOUNCEMENT)
|
||||
.data("issued an announcement")
|
||||
.build());
|
||||
|
||||
// 创建公共消息到MongoDB
|
||||
PublicMessage publicMessage = new PublicMessage();
|
||||
publicMessage.setId(IdWorker.getId());
|
||||
publicMessage.setType("notification");
|
||||
publicMessage.setTitle(noticeMessage.getTitle());
|
||||
publicMessage.setContent(noticeMessage.getDescription());
|
||||
publicMessage.setImageUrl(noticeMessage.getCover());
|
||||
publicMessage.setExtraData(MapBuilder.<String, Object>builder()
|
||||
.put("notice_id", noticeMessage.getId())
|
||||
.put("link", noticeMessage.getLink())
|
||||
.put("sys_origin", noticeMessage.getSysOrigin())
|
||||
.build());
|
||||
publicMessage.setStatus("active");
|
||||
|
||||
// 设置过期时间(30天后过期)
|
||||
|
||||
ZonedDateTime zonedDateTime = ZonedDateTimeAsiaRiyadhUtils.now().plusDays(30);
|
||||
Timestamp timestamp = new Timestamp(zonedDateTime.toInstant().toEpochMilli());
|
||||
publicMessage.setExpiresAt(timestamp);
|
||||
|
||||
publicMessageService.createPublicMessage(publicMessage);
|
||||
|
||||
/*newsletterClient.send(new NewsletterResultCmd()
|
||||
.setUserIds(List.of(1954021558348390402L))
|
||||
.setEvent(NewsletterEvent.DYNAMIC_MESSAGE_QUANTITY)
|
||||
.setData("list.size()")
|
||||
);*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user