Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
package messagemod
import (
"91porn-server/common/constant"
"91porn-server/models/commod"
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// 查询列表
type QueryCond struct {
commod.Page
MsgType int `form:"msgType" json:"msgType" binding:"required"` //1.点赞消息 2.评论消息
}
// 查询会话消息列表
type QueryMsgCond struct {
SessionId string `form:"sessionId" json:"sessionId" binding:"required"`
commod.Page
}
// AddMsgReqInfo 新增私信实体
type AddMsgReqInfo struct {
TakeUid uint64 `form:"takeUid" json:"takeUid" binding:"required"` // 接受用户uid
Content string `form:"content" json:"content"` // 消息内容
ImgUrl []string `form:"imgUrl" json:"imgUrl"` // 图片链接
}
// ChargePrivateLetterReq 私信扣费请求
type ChargePrivateLetterReq struct {
TakeUid uint64 `form:"takeUid" json:"takeUid" binding:"required"` // 接受用户uid
}
// ChargePrivateLetterResp 私信扣费响应
type ChargePrivateLetterResp struct {
Price int64 `json:"price"` // 本次应扣金币数
DeductAmount int64 `json:"deductAmount"` // 本次扣除金币余额
DeductIncome int64 `json:"deductIncome"` // 本次扣除收益余额
Amount int64 `json:"amount"` // 扣费后金币余额
Income int64 `json:"income"` // 扣费后收益余额
Balance int64 `json:"balance"` // 扣费后总余额
}
// 已读实体
type ReadMsgReqInfo struct {
MsgIds []string `form:"msgIds" json:"msgIds" binding:"required"` //阅读消息id
}
// MessageApp 消息app返回实体
type MessageApp struct {
ID primitive.ObjectID `json:"id" bson:"_id"` // id
SendUid uint64 `json:"sendUid" bson:"sendUid"` // 发送者uid
SendName string `json:"sendName" bson:"sendName"` // 发送者姓名
SendAvatar string `json:"sendAvatar" bson:"sendAvatar"` // 发送者头像
TakeUid uint64 `json:"takeUid" bson:"takeUid"` // 接收者uid
TakeName string `json:"takeName" bson:"takeName"` // 接收者姓名
TakeAvatar string `json:"takeAvatar" bson:"takeAvatar"` // 接收者头像
PeerImUserID int64 `json:"peerImUserId" bson:"peerImUserId"` // 对端 IM 平台用户 ID
Content string `json:"content" bson:"content"` // 消息内容
ContentType int `json:"contentType" bson:"contentType"` // 消息内容类型
ImgUrl []string `json:"imgUrl" bson:"imgUrl"` // 消息图片内容链接
IsRead bool `json:"isRead" bson:"isRead"` // 消息已读or未读
SessionId string `json:"sessionId" bson:"sessionId"` // 会话id,私聊信息才有,便于分页查询
LinkUrl string `json:"linkUrl" bson:"linkUrl"` // 附带链接地址
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` // 创建时间
ObjId primitive.ObjectID `json:"objId" bson:"objId"` // 动态消息使用,eg:被转发、点赞、评论帖子id
ObjName string `json:"objName" bson:"objName"` // 动态消息使用,eg:被转发、点赞、评论帖子名称
}
// MsgDynamicsApp 消息动态app返回实体
type MsgDynamicsApp struct {
ID primitive.ObjectID `json:"id" bson:"_id"` // id
SendUid uint64 `json:"sendUid" bson:"sendUid"` // 发送者uid
SendName string `json:"sendName" bson:"sendName"` // 发送者姓名
SendAvatar string `json:"sendAvatar" bson:"sendAvatar"` // 发送者头像
SendGender string `json:"sendGender" bson:"sendGender"` // 发送者性别
SuperUser int `json:"SuperUser" bson:"SuperUser"` // 发送者是否大V
Awards []int `json:"awards" bson:"awards"` // 发送者奖章
MsgType MsgType `json:"msgType" bson:"msgType"` // 消息类型
Content string `json:"content" bson:"content"` // 消息内容
IsRead bool `json:"isRead" bson:"isRead"` // 消息已读or未读
ObjId primitive.ObjectID `json:"objId" bson:"objId"` // 动态消息使用,eg:被转发、点赞、评论帖子id
ObjName string `json:"objName" bson:"objName"` // 动态消息使用,eg:被转发、点赞、评论帖子名称
ObjCover string `json:"objCover" bson:"objCover"` // 动态消息使用,eg:被转发、点赞、评论帖子封面图
ObjType constant.NewsType `json:"objType" bson:"objType"` // 动态消息使用,eg:被转发、点赞、评论帖子类型
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` // 创建时间
VipExpireDate time.Time `json:"vipExpireDate,omitempty" bson:"vipExpireDate"`
VipLevel int `json:"vipLevel"`
UserGroup []UserInfo `json:"userGroup" bson:"userGroup"` // 相同动态消息的用户集合,保留前十
UserCount int `json:"userCount" bson:"userCount"` // 相同动态消息的用户数
LikeCount int `json:"likeCount"` // 点赞数
ImgUrl string `json:"imgUrl"` // 帖子封面
}
// NoReadMsgDynamicsCountApp 未读消息数
type NoReadMsgDynamicsCountApp struct {
DtCount int `json:"dtCount"` // 动态未读数
MsgCount int `json:"msgCount"` // 消息未读数
}
+262
View File
@@ -0,0 +1,262 @@
package messagemod
import (
"91porn-server/common/db"
"91porn-server/common/log"
"91porn-server/models"
"encoding/json"
"fmt"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var mdb *db.MongoDB
const table = models.ChatMessage
const NoRedDynamicNumRedisKey = "user-no-red-dynamic-num"
// Coll 获取表名
func coll(t *db.MongoTool) *db.MongoTool {
if t == nil {
return mdb.Coll(table)
}
return t.Coll(table)
}
// InitIndex 初始化索引
func initIndex() {
many := []mongo.IndexModel{
{
Keys: bson.D{{"sendUid", 1}},
},
{
Keys: bson.D{{"takeUid", 1}},
},
{
Keys: bson.D{{"msgType", 1}},
},
{
Keys: bson.D{{"sessionId", 1}},
},
{
Keys: bson.D{{"createdAt", -1}},
},
{
Keys: bson.D{{"msgType", 1}, {"sessionId", 1}, {"takeUid", 1}, {"isRead", 1}},
},
{
Keys: bson.D{{"sessionId", 1}, {"takeUid", 1}, {"isRead", 1}},
},
}
_, err := coll(nil).CreateIndex(many)
if err != nil {
panic(fmt.Sprintf("%s model set index err ==>[%+v]", table, err))
}
return
}
// InsertOne 新增
func InsertOne(mt *db.MongoTool, t *Message) (data primitive.ObjectID, err error) {
result, err := coll(mt).InsertOne(&t)
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "InsertOne", table, "InsertOne", err))
return
}
byteID, err := json.Marshal(result.InsertedID)
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "InsertOne", table, "Marshal", err))
return
}
err = data.UnmarshalJSON(byteID)
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "InsertOne", table, "UnmarshalJSON", err))
return
}
return
}
// 查询总条数
func Count(filter primitive.M) (int64, error) {
if count, err := coll(nil).Count(filter); err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "Count", models.Activity, "Count", err),
log.Any("filter", filter),
)
return 0, err
} else {
return count, nil
}
}
// DeleteMessages 删除
func DeleteMessages(ids []primitive.ObjectID) (int64, error) {
if ids == nil {
ids = []primitive.ObjectID{}
}
cond := bson.M{"_id": bson.M{"$in": ids}}
result, err := coll(nil).DeleteMany(cond)
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DeleteMessages", table, "DeleteMany", err), log.Any("ids", ids))
return 0, err
}
return result.DeletedCount, nil
}
// DeleteMsgBySessionId 根据会话消息删除会话聊天消息
func DeleteMsgBySessionId(sessionId string) (int64, error) {
cond := bson.M{"sessionId": sessionId, "msgType": PrivateLetterMsg}
result, err := coll(nil).DeleteMany(cond)
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "DeleteMsgBySessionId", table, "DeleteMany", err), log.Any("sessionId", sessionId))
return 0, err
}
return result.DeletedCount, nil
}
// FindOneByFilter 根据条件查询单个信息
func FindOneByFilter(cond bson.M) (data Message, err error) {
opts := options.FindOne().SetSort(bson.D{{"createdAt", -1}})
err = coll(nil).FindOne(&data, cond, opts)
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "FindOneByFilter", table, "FindOne", err),
log.Any("cond", cond),
)
return
}
return
}
// Query 查询列表,app使用
func Query(cond bson.M, pageSize, pageNumber int) (data []MessageApp, hasNext bool, err error) {
data = make([]MessageApp, 0)
skip := pageSize * (pageNumber - 1)
sort := bson.D{{"createdAt", -1}}
opts := options.Find().SetSkip(int64(skip)).SetLimit(int64(pageSize) + 1).SetSort(sort)
err = coll(nil).Find(&data, cond, opts)
if err != nil {
log.Error("Query", log.Any("cond", cond), log.Any("skip", skip), log.Any("limit", pageSize), log.E(err))
return
}
if len(data) > pageSize {
hasNext = true
data = data[:pageSize]
}
return
}
// 查询动态列表,app使用
func QueryDynamics(cond bson.M, pageSize, pageNumber int) (data []MsgDynamicsApp, hasNext bool, err error) {
data = make([]MsgDynamicsApp, 0)
skip := pageSize * (pageNumber - 1)
sort := bson.D{{"createdAt", -1}}
opts := options.Find().SetSkip(int64(skip)).SetLimit(int64(pageSize) + 1).SetSort(sort)
err = coll(nil).Find(&data, cond, opts)
if err != nil {
log.Error("QueryDynamics", log.Any("cond", cond), log.Any("skip", skip), log.Any("limit", pageSize), log.E(err))
return
}
if len(data) > pageSize {
hasNext = true
data = data[:pageSize]
}
return
}
// FindByFilter 条件查询次数,app使用
func FindByFilter(cond bson.M) (data []*MsgDynamicsApp, err error) {
err = coll(nil).Find(&data, cond)
if err != nil {
log.Error("FindByFilter", log.Any("cond", cond), log.E(err))
return
}
return
}
// UpdIsRead 更新消息已读状态
func UpdIsRead(t *db.MongoTool, uid uint64, ids []primitive.ObjectID) (err error) {
filter := bson.M{"_id": bson.M{"$in": ids}, "isRead": false, "takeUid": uid}
set := bson.M{"$set": bson.M{"isRead": true, "updatedAt": time.Now()}}
_, err = coll(t).UpdateMany(filter, set)
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "UpdIsRead", table, "UpdateMany", err),
log.Any("ids", ids),
)
return err
}
return
}
// UpdIsReadBySessionIdAndUid 根据sessionId更新消息已读状态
func UpdIsReadBySessionIdAndUid(t *db.MongoTool, uid uint64, sessionId string) (err error) {
msgTypes := [...]string{string(PrivateLetterMsg), string(OfficialPrivateLetterMsg)}
filter := bson.M{"msgType": bson.M{"$in": msgTypes}, "sessionId": sessionId, "takeUid": uid, "isRead": false}
set := bson.M{"$set": bson.M{"isRead": true}}
_, err = coll(t).UpdateMany(filter, set)
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "UpdIsReadBySessionIdAndUid", table, "UpdateMany", err),
log.Any("uid", uid), log.Any("sessionId", sessionId))
return err
}
return
}
// UpdDynamicsIsReadByUid 根据uid更新动态消息已读状态
func UpdDynamicsIsReadByUid(t *db.MongoTool, uid uint64) (err error) {
filter := bson.M{"msgType": bson.M{"$in": GetDynamics()}, "takeUid": uid, "isRead": false}
set := bson.M{"$set": bson.M{"isRead": true}}
_, err = coll(t).UpdateMany(filter, set)
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "UpdDynamicsIsReadByUid", table, "UpdateMany", err), log.Any("uid", uid))
return err
}
return
}
// QueryAllDocument 分页查询文档
func QueryAllDocument(filter primitive.M, opts ...*options.FindOptions) ([]*Message, error) {
var out []*Message
if err := coll(nil).Find(&out, filter, opts...); err != nil {
log.Error(fmt.Sprintf("[METHOD-QueryAllDocument]==> Model %s Find fail error:%+v:", table, err),
log.Any("filter", filter),
)
return nil, err
}
return out, nil
}
// CountDocument 查询文档条目数
func CountDocument(filter primitive.M) (int64, error) {
count, err := coll(nil).Count(filter)
if err != nil {
log.Error(fmt.Sprintf("[METHOD-CountDocument]==> Model %s Count fail error:%+v:", table, err),
log.Any("filter", filter),
)
return 0, err
}
return count, nil
}
// RemoveDocument 删除用户私聊信息
func RemoveDocument(filter primitive.M) error {
_, err := coll(nil).DeleteOne(filter)
if err != nil {
log.Error(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "RemoveDocument", table, "DeleteOne", err),
log.Any("filter", filter),
)
}
return err
}
// UpdOneByFilter 更新
func UpdOneByFilter(t *db.MongoTool, cond bson.M, set bson.M) (err error) {
_, err = coll(t).UpdateOne(cond, set)
if err != nil {
log.Warn(fmt.Sprintf("[METHOD-%s]==> Model %s %s fail error:%+v:", "UpdOneByFilter", table, "UpdateOne", err), log.Any("cond", cond), log.Any("set", cond))
return err
}
return
}
+95
View File
@@ -0,0 +1,95 @@
package messagemod
import (
"91porn-server/common/constant"
"91porn-server/common/db"
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Init 初始化标签model索引
func Init() {
mdb = db.Init(table)
initIndex()
}
// 类型
type MsgType string
type ContentType int
const (
PrivateLetterMsg MsgType = "private_letter" // 私信消息
OfficialPrivateLetterMsg MsgType = "official_private_letter" // 官方私信消息
LikeMsg MsgType = "like_msg" // 点赞消息,属于动态
FollowMsg MsgType = "follow_msg" // 关注消息,属于动态
ForwardMsg MsgType = "forward_msg" // 转发消息,属于动态
CommentMsg MsgType = "comment_msg" // 评论消息,属于动态
LikeCommentMsg MsgType = "like_comment_msg" // 点赞评论消息,属于动态
ReplyCommentMsg MsgType = "reply_comment_msg" // 回复评论消息,属于动态
DesireMsg MsgType = "desire_msg" // 心愿工单消息消息,属于动态
)
const (
_ ContentType = iota
PromotionNotice // 1 推广通知
WithdrawalNotice // 2 提现通知
AuditNotice // 3 审核通知
BigVNotice // 4 大V通知
)
func GetDynamics() []MsgType {
var dynamics = []MsgType{LikeMsg, FollowMsg, ForwardMsg, CommentMsg, DesireMsg, OfficialPrivateLetterMsg, LikeCommentMsg, ReplyCommentMsg}
return dynamics
}
func GetDynamicsList() []MsgType {
var dynamics = []MsgType{LikeMsg, FollowMsg, ForwardMsg, CommentMsg, DesireMsg, LikeCommentMsg, ReplyCommentMsg}
return dynamics
}
//动态类型数组
// Message 消息
type Message struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"` // id
SendUid uint64 `json:"sendUid" bson:"sendUid"` // 发送者uid
TakeUid uint64 `json:"takeUid" bson:"takeUid"` // 接收者uid
MsgType MsgType `json:"msgType" bson:"msgType"` // 消息类型
ContentType int `json:"contentType" bson:"contentType"` // 消息内容类型
Content string `json:"content" bson:"content"` // 消息内容
ImgUrl []string `json:"imgUrl" bson:"imgUrl"` // 消息图片内容链接
SessionId string `json:"sessionId" bson:"sessionId"` // 会话id,私聊信息才有,便于分页查询
IsRead bool `json:"isRead" bson:"isRead"` // 消息已读or未读
ObjId primitive.ObjectID `json:"objId" bson:"objId"` // 动态消息使用,eg:被转发、点赞、评论帖子id
ObjName string `json:"objName" bson:"objName"` // 动态消息使用,eg:被转发、点赞、评论帖子名称
ObjCover string `json:"objCover" bson:"objCover"` // 动态消息使用,eg:被转发、点赞、评论帖子封面图
ObjType constant.NewsType `json:"objType" bson:"objType"` // 动态消息使用,eg:被转发、点赞、评论帖子类型
UserGroup []UserInfo `json:"userGroup" bson:"userGroup"` // 相同动态消息的用户集合,保留前十
UserCount int `json:"userCount" bson:"userCount"` // 相同动态消息的用户数
CreatedAt time.Time `json:"createdAt" bson:"createdAt"` // 创建时间
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"` // 更新时间
}
type UserInfo struct {
Uid uint64 `json:"uid" bson:"uid"` // 用户id
Name string `json:"name" bson:"name"` // 用户名
Avatar string `json:"avatar" bson:"avatar"` // 用户头像
}
// PublicAddMsg 添加消息公共实体
type PublicAddMsg struct {
SendUid uint64 `json:"sendUid" bson:"sendUid"` // 发送者uid
TakeUid uint64 `json:"takeUid" bson:"takeUid"` // 接收者uid
SessionId string `json:"sessionId" bson:"sessionId"` // 会话id,私聊信息才有,便于分页查询
Content string `json:"content" bson:"content"` // 消息内容
ImgUrl []string `json:"imgUrl" bson:"imgUrl"` // 消息图片内容链接
ContentType int `json:"contentType" bson:"contentType"` // 消息内容类型
MsgType MsgType `json:"msgType" bson:"msgType"` // 消息类型
ObjId primitive.ObjectID `json:"objId" bson:"objId"` // 动态消息使用,eg:被转发、点赞、评论帖子id
ObjName string `json:"objName" bson:"objName"` // 动态消息使用,eg:被转发、点赞、评论帖子名称
ObjCover string `json:"objCover" bson:"objCover"` // 动态消息使用,eg:被转发、点赞、评论帖子封面图
ObjType constant.NewsType `json:"objType" bson:"objType"` // 动态消息使用,eg:被转发、点赞、评论帖子类型
UserGroup []UserInfo `json:"userGroup" bson:"userGroup"` // 相同动态消息的用户集合,保留前十
UserCount int `json:"userCount" bson:"userCount"` // 相同动态消息的用户数
}
+63
View File
@@ -0,0 +1,63 @@
package messagemod
import (
"91porn-server/models/commod"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
type WebListRequest struct {
SendUid *uint64 `json:"sendUid" bson:"sendUid"` // 发送者uid
TakeUid *uint64 `json:"takeUid" bson:"takeUid"` // 接收者uid
IsRead *bool `json:"isRead" bson:"isRead"` // 消息已读or未读
commod.Page
}
func (receiver *WebListRequest) Filter() primitive.M {
filter := bson.M{}
if receiver.SendUid != nil {
filter["sendUid"] = receiver.SendUid
}
if receiver.TakeUid != nil {
filter["takeUid"] = receiver.TakeUid
}
if receiver.IsRead != nil {
filter["isRead"] = receiver.IsRead
}
return filter
}
func (receiver *WebListRequest) Options() *options.FindOptions {
return options.Find().SetSkip(int64(receiver.Skip())).SetLimit(int64(receiver.Limit() + 1)).SetSort(bson.M{"updatedAt": -1})
}
// WebDelReq 用户私聊删除
type WebDelReq struct {
ID *string `form:"id" json:"id"`
}
func (receiver *WebDelReq) Filter() primitive.M {
filter := bson.M{}
if receiver.ID != nil {
id, _ := primitive.ObjectIDFromHex(*receiver.ID)
filter["_id"] = id
}
return filter
}
// WebSendReq 发送私信
type WebSendReq struct {
Sender uint64 `form:"sender" json:"sender" binding:"required"`
Receiver uint64 `form:"receiver" json:"receiver" binding:"required"`
Content string `form:"content" json:"content" binding:"required"`
}
type SendMsg struct {
Sender uint64 `form:"sender" json:"sender" binding:"required"`
Receiver uint64 `form:"receiver" json:"receiver" binding:"required"`
Content string `form:"content" json:"content" binding:"required"`
MsgType MsgType
ObjId primitive.ObjectID `json:"objId" bson:"objId"` // 动态消息使用,eg:被转发、点赞、评论帖子id
ObjName string `json:"objName" bson:"objName"` // 动态消息使用,eg:被转发、点赞、评论帖子名称
}