diff --git a/docs/docs.go b/docs/docs.go index 5197694..a17866f 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1316,46 +1316,6 @@ const docTemplate = `{ } } }, - "/app/messages/read": { - "post": { - "description": "标记指定消息为已读状态", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "用户端" - ], - "summary": "标记消息为已读", - "parameters": [ - { - "description": "请求参数", - "name": "RequestBody", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/message.markMessageReadRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/message.markMessageReadResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/code.Failure" - } - } - } - } - }, "/app/send_message": { "post": { "description": "用户发送消息", @@ -2364,36 +2324,6 @@ const docTemplate = `{ } } }, - "message.markMessageReadRequest": { - "type": "object", - "required": [ - "app_id", - "message_id", - "user_id" - ], - "properties": { - "app_id": { - "description": "小程序ID", - "type": "string" - }, - "message_id": { - "description": "消息ID", - "type": "integer" - }, - "user_id": { - "description": "用户ID", - "type": "string" - } - } - }, - "message.markMessageReadResponse": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - } - }, "message.userSendMessageRequest": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index e20e8bd..44d3451 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1308,46 +1308,6 @@ } } }, - "/app/messages/read": { - "post": { - "description": "标记指定消息为已读状态", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "用户端" - ], - "summary": "标记消息为已读", - "parameters": [ - { - "description": "请求参数", - "name": "RequestBody", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/message.markMessageReadRequest" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/message.markMessageReadResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/code.Failure" - } - } - } - } - }, "/app/send_message": { "post": { "description": "用户发送消息", @@ -2356,36 +2316,6 @@ } } }, - "message.markMessageReadRequest": { - "type": "object", - "required": [ - "app_id", - "message_id", - "user_id" - ], - "properties": { - "app_id": { - "description": "小程序ID", - "type": "string" - }, - "message_id": { - "description": "消息ID", - "type": "integer" - }, - "user_id": { - "description": "用户ID", - "type": "string" - } - } - }, - "message.markMessageReadResponse": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - } - }, "message.userSendMessageRequest": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 28a683e..8b77003 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -656,27 +656,6 @@ definitions: description: 发送人昵称 type: string type: object - message.markMessageReadRequest: - properties: - app_id: - description: 小程序ID - type: string - message_id: - description: 消息ID - type: integer - user_id: - description: 用户ID - type: string - required: - - app_id - - message_id - - user_id - type: object - message.markMessageReadResponse: - properties: - message: - type: string - type: object message.userSendMessageRequest: properties: app_id: @@ -1617,32 +1596,6 @@ paths: summary: 获取消息日志 tags: - 用户端 - /app/messages/read: - post: - consumes: - - application/json - description: 标记指定消息为已读状态 - parameters: - - description: 请求参数 - in: body - name: RequestBody - required: true - schema: - $ref: '#/definitions/message.markMessageReadRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/message.markMessageReadResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/code.Failure' - summary: 标记消息为已读 - tags: - - 用户端 /app/send_message: post: consumes: diff --git a/internal/api/app/app_latest_messages.go b/internal/api/app/app_latest_messages.go index f1073cf..8a9e881 100644 --- a/internal/api/app/app_latest_messages.go +++ b/internal/api/app/app_latest_messages.go @@ -3,13 +3,11 @@ package app import ( "fmt" "net/http" - "time" "mini-chat/internal/code" "mini-chat/internal/pkg/core" "mini-chat/internal/pkg/timeutil" "mini-chat/internal/pkg/validation" - "mini-chat/internal/repository/mysql/model" ) type latestMessageByAppIdRequest struct { @@ -110,38 +108,13 @@ func (h *handler) LatestMessageByAppId() core.HandlerFunc { } // 自动标记该appid下的所有消息为已读(管理端访问时) - // 这里我们为每个消息的接收者创建或更新已读状态 - for _, message := range resultData { - // 检查是否已存在已读状态记录 - existingStatus, _ := h.readDB.AppMessageReadStatus.WithContext(ctx.RequestContext()). - Where(h.readDB.AppMessageReadStatus.AppID.Eq(req.AppID)). - Where(h.readDB.AppMessageReadStatus.MessageID.Eq(message.ID)). - Where(h.readDB.AppMessageReadStatus.UserID.Eq(message.ReceiverID)). - First() - - if existingStatus == nil { - // 如果不存在,创建新的已读状态记录 - now := time.Now() - _ = h.writeDB.AppMessageReadStatus.WithContext(ctx.RequestContext()).Create(&model.AppMessageReadStatus{ - AppID: req.AppID, - MessageID: message.ID, - UserID: message.ReceiverID, - IsRead: 1, - ReadTime: &now, - CreatedAt: now, - UpdatedAt: now, - }) - } else if existingStatus.IsRead == 0 { - // 如果存在但未读,更新为已读 - now := time.Now() - _, _ = h.writeDB.AppMessageReadStatus.WithContext(ctx.RequestContext()). - Where(h.writeDB.AppMessageReadStatus.ID.Eq(existingStatus.ID)). - Updates(map[string]interface{}{ - "is_read": 1, - "read_time": &now, - "updated_at": now, - }) - } + _, err = h.writeDB.AppMessageLog.WithContext(ctx.RequestContext()). + Where(h.writeDB.AppMessageLog.AppID.Eq(req.AppID)). + Where(h.writeDB.AppMessageLog.IsRead.Eq(0)). + Update(h.writeDB.AppMessageLog.IsRead, 1) + if err != nil { + // 记录错误但不影响查询结果 + // TODO: 可以添加日志记录 } res.Page = req.Page @@ -150,24 +123,11 @@ func (h *handler) LatestMessageByAppId() core.HandlerFunc { res.List = make([]latestMessageData, len(resultData)) for k, v := range resultData { - // 查询该消息的已读状态 - readStatus, _ := h.readDB.AppMessageReadStatus.WithContext(ctx.RequestContext()). - Where(h.readDB.AppMessageReadStatus.AppID.Eq(req.AppID)). - Where(h.readDB.AppMessageReadStatus.MessageID.Eq(v.ID)). - Where(h.readDB.AppMessageReadStatus.UserID.Eq(v.ReceiverID)). - First() - - // 判断是否已读 - isRead := int32(0) - if readStatus != nil && readStatus.IsRead == 1 { - isRead = 1 - } - // 计算该用户在该应用下的未读消息总数 - unreadCount, _ := h.readDB.AppMessageReadStatus.WithContext(ctx.RequestContext()). - Where(h.readDB.AppMessageReadStatus.AppID.Eq(req.AppID)). - Where(h.readDB.AppMessageReadStatus.UserID.Eq(v.ReceiverID)). - Where(h.readDB.AppMessageReadStatus.IsRead.Eq(0)). + unreadCount, _ := h.readDB.AppMessageLog.WithContext(ctx.RequestContext()). + Where(h.readDB.AppMessageLog.AppID.Eq(req.AppID)). + Where(h.readDB.AppMessageLog.ReceiverID.Eq(v.ReceiverID)). + Where(h.readDB.AppMessageLog.IsRead.Eq(0)). Count() res.List[k] = latestMessageData{ @@ -179,7 +139,7 @@ func (h *handler) LatestMessageByAppId() core.HandlerFunc { ReceiverID: v.ReceiverID, Content: v.Content, MsgType: v.MsgType, - IsRead: isRead, + IsRead: v.IsRead, // 直接使用消息表中的 is_read 字段 UnreadCount: unreadCount, } } diff --git a/internal/api/message/message_mark_read.go b/internal/api/message/message_mark_read.go deleted file mode 100644 index 61e3cbb..0000000 --- a/internal/api/message/message_mark_read.go +++ /dev/null @@ -1,115 +0,0 @@ -package message - -import ( - "fmt" - "net/http" - "time" - - "mini-chat/internal/code" - "mini-chat/internal/pkg/core" - "mini-chat/internal/pkg/validation" - "mini-chat/internal/repository/mysql/model" -) - -type markMessageReadRequest struct { - AppID string `json:"app_id" binding:"required"` // 小程序ID - UserID string `json:"user_id" binding:"required"` // 用户ID - MessageID int32 `json:"message_id" binding:"required"` // 消息ID -} - -type markMessageReadResponse struct { - Message string `json:"message"` -} - -// MarkMessageRead 标记消息为已读 -// @Summary 标记消息为已读 -// @Description 标记指定消息为已读状态 -// @Tags 用户端 -// @Accept json -// @Produce json -// @Param RequestBody body markMessageReadRequest true "请求参数" -// @Success 200 {object} markMessageReadResponse -// @Failure 400 {object} code.Failure -// @Router /app/messages/read [post] -func (h *handler) MarkMessageRead() core.HandlerFunc { - return func(ctx core.Context) { - req := new(markMessageReadRequest) - res := new(markMessageReadResponse) - if err := ctx.ShouldBindJSON(req); err != nil { - ctx.AbortWithError(core.Error( - http.StatusBadRequest, - code.ParamBindError, - validation.Error(err)), - ) - return - } - - // 检查消息是否存在 - message, err := h.readDB.AppMessageLog.WithContext(ctx.RequestContext()). - Where(h.readDB.AppMessageLog.ID.Eq(req.MessageID)). - Where(h.readDB.AppMessageLog.AppID.Eq(req.AppID)). - First() - if err != nil { - ctx.AbortWithError(core.Error( - http.StatusBadRequest, - code.ListMessageError, - fmt.Sprintf("消息不存在:%s", err.Error())), - ) - return - } - - // 检查用户是否有权限标记此消息为已读(只有接收者可以标记) - if message.ReceiverID != req.UserID { - ctx.AbortWithError(core.Error( - http.StatusBadRequest, - code.ListMessageError, - "只有消息接收者可以标记消息为已读"), - ) - return - } - - // 检查是否已经标记为已读 - existingReadStatus, _ := h.readDB.AppMessageReadStatus.WithContext(ctx.RequestContext()). - Where(h.readDB.AppMessageReadStatus.AppID.Eq(req.AppID)). - Where(h.readDB.AppMessageReadStatus.MessageID.Eq(req.MessageID)). - Where(h.readDB.AppMessageReadStatus.UserID.Eq(req.UserID)). - First() - - now := time.Now() - - if existingReadStatus != nil { - // 如果已存在记录,更新为已读状态 - _, err = h.writeDB.AppMessageReadStatus.WithContext(ctx.RequestContext()). - Where(h.writeDB.AppMessageReadStatus.ID.Eq(existingReadStatus.ID)). - Updates(map[string]interface{}{ - "is_read": 1, - "read_time": &now, - "updated_at": now, - }) - } else { - // 如果不存在记录,创建新的已读状态记录 - newReadStatus := &model.AppMessageReadStatus{ - AppID: req.AppID, - MessageID: req.MessageID, - UserID: req.UserID, - IsRead: 1, - ReadTime: &now, - CreatedAt: now, - UpdatedAt: now, - } - err = h.writeDB.AppMessageReadStatus.WithContext(ctx.RequestContext()).Create(newReadStatus) - } - - if err != nil { - ctx.AbortWithError(core.Error( - http.StatusBadRequest, - code.ListMessageError, - fmt.Sprintf("标记消息已读失败:%s", err.Error())), - ) - return - } - - res.Message = "消息已标记为已读" - ctx.Payload(res) - } -} \ No newline at end of file diff --git a/internal/repository/mysql/dao/app_message_log.gen.go b/internal/repository/mysql/dao/app_message_log.gen.go index 4543d91..e98d9a2 100644 --- a/internal/repository/mysql/dao/app_message_log.gen.go +++ b/internal/repository/mysql/dao/app_message_log.gen.go @@ -35,6 +35,7 @@ func newAppMessageLog(db *gorm.DB, opts ...gen.DOOption) appMessageLog { _appMessageLog.ReceiverID = field.NewString(tableName, "receiver_id") _appMessageLog.MsgType = field.NewInt32(tableName, "msg_type") _appMessageLog.Content = field.NewString(tableName, "content") + _appMessageLog.IsRead = field.NewInt32(tableName, "is_read") _appMessageLog.CreatedAt = field.NewTime(tableName, "created_at") _appMessageLog.fillFieldMap() @@ -55,6 +56,7 @@ type appMessageLog struct { ReceiverID field.String // 接收人ID MsgType field.Int32 // 消息类型(1:文本 2:图片) Content field.String // 消息内容 + IsRead field.Int32 // 是否已读(0:未读 1:已读) CreatedAt field.Time // 创建时间 fieldMap map[string]field.Expr @@ -97,7 +99,7 @@ func (a *appMessageLog) GetFieldByName(fieldName string) (field.OrderExpr, bool) } func (a *appMessageLog) fillFieldMap() { - a.fieldMap = make(map[string]field.Expr, 9) + a.fieldMap = make(map[string]field.Expr, 10) a.fieldMap["id"] = a.ID a.fieldMap["app_id"] = a.AppID a.fieldMap["sender_id"] = a.SenderID @@ -106,6 +108,7 @@ func (a *appMessageLog) fillFieldMap() { a.fieldMap["receiver_id"] = a.ReceiverID a.fieldMap["msg_type"] = a.MsgType a.fieldMap["content"] = a.Content + a.fieldMap["is_read"] = a.IsRead a.fieldMap["created_at"] = a.CreatedAt } diff --git a/internal/repository/mysql/dao/app_message_read_status.gen.go b/internal/repository/mysql/dao/app_message_read_status.gen.go deleted file mode 100644 index 8655ec1..0000000 --- a/internal/repository/mysql/dao/app_message_read_status.gen.go +++ /dev/null @@ -1,398 +0,0 @@ -// Code generated by gorm.io/gen. DO NOT EDIT. -// Code generated by gorm.io/gen. DO NOT EDIT. -// Code generated by gorm.io/gen. DO NOT EDIT. - -package dao - -import ( - "context" - - "gorm.io/gorm" - "gorm.io/gorm/clause" - "gorm.io/gorm/schema" - - "gorm.io/gen" - "gorm.io/gen/field" - - "gorm.io/plugin/dbresolver" - - "mini-chat/internal/repository/mysql/model" -) - -func newAppMessageReadStatus(db *gorm.DB, opts ...gen.DOOption) appMessageReadStatus { - _appMessageReadStatus := appMessageReadStatus{} - - _appMessageReadStatus.appMessageReadStatusDo.UseDB(db, opts...) - _appMessageReadStatus.appMessageReadStatusDo.UseModel(&model.AppMessageReadStatus{}) - - tableName := _appMessageReadStatus.appMessageReadStatusDo.TableName() - _appMessageReadStatus.ALL = field.NewAsterisk(tableName) - _appMessageReadStatus.ID = field.NewInt32(tableName, "id") - _appMessageReadStatus.AppID = field.NewString(tableName, "app_id") - _appMessageReadStatus.MessageID = field.NewInt32(tableName, "message_id") - _appMessageReadStatus.UserID = field.NewString(tableName, "user_id") - _appMessageReadStatus.IsRead = field.NewInt32(tableName, "is_read") - _appMessageReadStatus.ReadTime = field.NewTime(tableName, "read_time") - _appMessageReadStatus.CreatedAt = field.NewTime(tableName, "created_at") - _appMessageReadStatus.UpdatedAt = field.NewTime(tableName, "updated_at") - - _appMessageReadStatus.fillFieldMap() - - return _appMessageReadStatus -} - -// appMessageReadStatus 消息已读状态表 -type appMessageReadStatus struct { - appMessageReadStatusDo - - ALL field.Asterisk - ID field.Int32 // 主键ID - AppID field.String // 小程序ID - MessageID field.Int32 // 消息ID - UserID field.String // 用户ID - IsRead field.Int32 // 是否已读(0:未读 1:已读) - ReadTime field.Time // 已读时间 - CreatedAt field.Time // 创建时间 - UpdatedAt field.Time // 更新时间 - - fieldMap map[string]field.Expr -} - -func (a appMessageReadStatus) Table(newTableName string) *appMessageReadStatus { - a.appMessageReadStatusDo.UseTable(newTableName) - return a.updateTableName(newTableName) -} - -func (a appMessageReadStatus) As(alias string) *appMessageReadStatus { - a.appMessageReadStatusDo.DO = *(a.appMessageReadStatusDo.As(alias).(*gen.DO)) - return a.updateTableName(alias) -} - -func (a appMessageReadStatus) updateTableName(table string) *appMessageReadStatus { - a.ALL = field.NewAsterisk(table) - a.ID = field.NewInt32(table, "id") - a.AppID = field.NewString(table, "app_id") - a.MessageID = field.NewInt32(table, "message_id") - a.UserID = field.NewString(table, "user_id") - a.IsRead = field.NewInt32(table, "is_read") - a.ReadTime = field.NewTime(table, "read_time") - a.CreatedAt = field.NewTime(table, "created_at") - a.UpdatedAt = field.NewTime(table, "updated_at") - - a.fillFieldMap() - - return &a -} - -func (a *appMessageReadStatus) fillFieldMap() { - a.fieldMap = make(map[string]field.Expr, 8) - a.fieldMap["id"] = a.ID - a.fieldMap["app_id"] = a.AppID - a.fieldMap["message_id"] = a.MessageID - a.fieldMap["user_id"] = a.UserID - a.fieldMap["is_read"] = a.IsRead - a.fieldMap["read_time"] = a.ReadTime - a.fieldMap["created_at"] = a.CreatedAt - a.fieldMap["updated_at"] = a.UpdatedAt -} - -func (a appMessageReadStatus) clone(db *gorm.DB) appMessageReadStatus { - a.appMessageReadStatusDo.ReplaceConnPool(db.Statement.ConnPool) - return a -} - -func (a appMessageReadStatus) replaceDB(db *gorm.DB) appMessageReadStatus { - a.appMessageReadStatusDo.ReplaceDB(db) - return a -} - -type appMessageReadStatusDo struct{ gen.DO } - -type IAppMessageReadStatusDo interface { - gen.SubQuery - Debug() IAppMessageReadStatusDo - WithContext(ctx context.Context) IAppMessageReadStatusDo - WithResult(fc func(tx gen.Dao)) gen.ResultInfo - ReplaceDB(db *gorm.DB) - ReadDB() IAppMessageReadStatusDo - WriteDB() IAppMessageReadStatusDo - As(alias string) gen.Dao - Session(config *gorm.Session) IAppMessageReadStatusDo - Columns(cols ...field.Expr) gen.Columns - Clauses(conds ...clause.Expression) IAppMessageReadStatusDo - Not(conds ...gen.Condition) IAppMessageReadStatusDo - Or(conds ...gen.Condition) IAppMessageReadStatusDo - Select(conds ...field.Expr) IAppMessageReadStatusDo - Where(conds ...gen.Condition) IAppMessageReadStatusDo - Order(conds ...field.Expr) IAppMessageReadStatusDo - Distinct(cols ...field.Expr) IAppMessageReadStatusDo - Omit(cols ...field.Expr) IAppMessageReadStatusDo - Join(table schema.Tabler, on ...field.Expr) IAppMessageReadStatusDo - LeftJoin(table schema.Tabler, on ...field.Expr) IAppMessageReadStatusDo - RightJoin(table schema.Tabler, on ...field.Expr) IAppMessageReadStatusDo - Group(cols ...field.Expr) IAppMessageReadStatusDo - Having(conds ...gen.Condition) IAppMessageReadStatusDo - Limit(limit int) IAppMessageReadStatusDo - Offset(offset int) IAppMessageReadStatusDo - Count() (count int64, err error) - Scopes(funcs ...func(gen.Dao) gen.Dao) IAppMessageReadStatusDo - Unscoped() IAppMessageReadStatusDo - Create(values ...*model.AppMessageReadStatus) error - CreateInBatches(values []*model.AppMessageReadStatus, batchSize int) error - Save(values ...*model.AppMessageReadStatus) error - First() (*model.AppMessageReadStatus, error) - Take() (*model.AppMessageReadStatus, error) - Last() (*model.AppMessageReadStatus, error) - Find() ([]*model.AppMessageReadStatus, error) - FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.AppMessageReadStatus, err error) - FindInBatches(result *[]*model.AppMessageReadStatus, batchSize int, fc func(tx gen.Dao, batch int) error) error - Pluck(column field.Expr, dest interface{}) error - Delete(...*model.AppMessageReadStatus) (info gen.ResultInfo, err error) - Update(column field.Expr, value interface{}) (info gen.ResultInfo, err error) - UpdateSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) - Updates(value interface{}) (info gen.ResultInfo, err error) - UpdateColumn(column field.Expr, value interface{}) (info gen.ResultInfo, err error) - UpdateColumnSimple(columns ...field.AssignExpr) (info gen.ResultInfo, err error) - UpdateColumns(value interface{}) (info gen.ResultInfo, err error) - UpdateFrom(q gen.SubQuery) gen.Dao - Attrs(attrs ...field.AssignExpr) IAppMessageReadStatusDo - Assign(attrs ...field.AssignExpr) IAppMessageReadStatusDo - Joins(fields ...field.RelationField) IAppMessageReadStatusDo - Preload(fields ...field.RelationField) IAppMessageReadStatusDo - FirstOrInit() (*model.AppMessageReadStatus, error) - FirstOrCreate() (*model.AppMessageReadStatus, error) - FindByPage(offset int, limit int) (result []*model.AppMessageReadStatus, count int64, err error) - ScanByPage(result interface{}, offset int, limit int) (count int64, err error) - Scan(result interface{}) (err error) - Returning(value interface{}, columns ...string) IAppMessageReadStatusDo - UnderlyingDB() *gorm.DB - schema.Tabler -} - -func (a appMessageReadStatusDo) Debug() IAppMessageReadStatusDo { - return a.withDO(a.DO.Debug()) -} - -func (a appMessageReadStatusDo) WithContext(ctx context.Context) IAppMessageReadStatusDo { - return a.withDO(a.DO.WithContext(ctx)) -} - -func (a appMessageReadStatusDo) ReadDB() IAppMessageReadStatusDo { - return a.withDO(a.DO.Clauses(dbresolver.Read)) -} - -func (a appMessageReadStatusDo) WriteDB() IAppMessageReadStatusDo { - return a.withDO(a.DO.Clauses(dbresolver.Write)) -} - -func (a appMessageReadStatusDo) Session(config *gorm.Session) IAppMessageReadStatusDo { - return a.withDO(a.DO.Session(config)) -} - -func (a appMessageReadStatusDo) Clauses(conds ...clause.Expression) IAppMessageReadStatusDo { - return a.withDO(a.DO.Clauses(conds...)) -} - -func (a appMessageReadStatusDo) Returning(value interface{}, columns ...string) IAppMessageReadStatusDo { - return a.withDO(a.DO.Returning(value, columns...)) -} - -func (a appMessageReadStatusDo) Not(conds ...gen.Condition) IAppMessageReadStatusDo { - return a.withDO(a.DO.Not(conds...)) -} - -func (a appMessageReadStatusDo) Or(conds ...gen.Condition) IAppMessageReadStatusDo { - return a.withDO(a.DO.Or(conds...)) -} - -func (a appMessageReadStatusDo) Select(conds ...field.Expr) IAppMessageReadStatusDo { - return a.withDO(a.DO.Select(conds...)) -} - -func (a appMessageReadStatusDo) Where(conds ...gen.Condition) IAppMessageReadStatusDo { - return a.withDO(a.DO.Where(conds...)) -} - -func (a appMessageReadStatusDo) Order(conds ...field.Expr) IAppMessageReadStatusDo { - return a.withDO(a.DO.Order(conds...)) -} - -func (a appMessageReadStatusDo) Distinct(cols ...field.Expr) IAppMessageReadStatusDo { - return a.withDO(a.DO.Distinct(cols...)) -} - -func (a appMessageReadStatusDo) Omit(cols ...field.Expr) IAppMessageReadStatusDo { - return a.withDO(a.DO.Omit(cols...)) -} - -func (a appMessageReadStatusDo) Join(table schema.Tabler, on ...field.Expr) IAppMessageReadStatusDo { - return a.withDO(a.DO.Join(table, on...)) -} - -func (a appMessageReadStatusDo) LeftJoin(table schema.Tabler, on ...field.Expr) IAppMessageReadStatusDo { - return a.withDO(a.DO.LeftJoin(table, on...)) -} - -func (a appMessageReadStatusDo) RightJoin(table schema.Tabler, on ...field.Expr) IAppMessageReadStatusDo { - return a.withDO(a.DO.RightJoin(table, on...)) -} - -func (a appMessageReadStatusDo) Group(cols ...field.Expr) IAppMessageReadStatusDo { - return a.withDO(a.DO.Group(cols...)) -} - -func (a appMessageReadStatusDo) Having(conds ...gen.Condition) IAppMessageReadStatusDo { - return a.withDO(a.DO.Having(conds...)) -} - -func (a appMessageReadStatusDo) Limit(limit int) IAppMessageReadStatusDo { - return a.withDO(a.DO.Limit(limit)) -} - -func (a appMessageReadStatusDo) Offset(offset int) IAppMessageReadStatusDo { - return a.withDO(a.DO.Offset(offset)) -} - -func (a appMessageReadStatusDo) Scopes(funcs ...func(gen.Dao) gen.Dao) IAppMessageReadStatusDo { - return a.withDO(a.DO.Scopes(funcs...)) -} - -func (a appMessageReadStatusDo) Unscoped() IAppMessageReadStatusDo { - return a.withDO(a.DO.Unscoped()) -} - -func (a appMessageReadStatusDo) Create(values ...*model.AppMessageReadStatus) error { - if len(values) == 0 { - return nil - } - return a.DO.Create(values) -} - -func (a appMessageReadStatusDo) CreateInBatches(values []*model.AppMessageReadStatus, batchSize int) error { - return a.DO.CreateInBatches(values, batchSize) -} - -func (a appMessageReadStatusDo) Save(values ...*model.AppMessageReadStatus) error { - if len(values) == 0 { - return nil - } - return a.DO.Save(values) -} - -func (a appMessageReadStatusDo) First() (*model.AppMessageReadStatus, error) { - if result, err := a.DO.First(); err != nil { - return nil, err - } else { - return result.(*model.AppMessageReadStatus), nil - } -} - -func (a appMessageReadStatusDo) Take() (*model.AppMessageReadStatus, error) { - if result, err := a.DO.Take(); err != nil { - return nil, err - } else { - return result.(*model.AppMessageReadStatus), nil - } -} - -func (a appMessageReadStatusDo) Last() (*model.AppMessageReadStatus, error) { - if result, err := a.DO.Last(); err != nil { - return nil, err - } else { - return result.(*model.AppMessageReadStatus), nil - } -} - -func (a appMessageReadStatusDo) Find() ([]*model.AppMessageReadStatus, error) { - result, err := a.DO.Find() - return result.([]*model.AppMessageReadStatus), err -} - -func (a appMessageReadStatusDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.AppMessageReadStatus, err error) { - buf := make([]*model.AppMessageReadStatus, 0, batchSize) - err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { - defer func() { results = append(results, buf...) }() - return fc(tx, batch) - }) - return results, err -} - -func (a appMessageReadStatusDo) FindInBatches(result *[]*model.AppMessageReadStatus, batchSize int, fc func(tx gen.Dao, batch int) error) error { - return a.DO.FindInBatches(result, batchSize, fc) -} - -func (a appMessageReadStatusDo) Attrs(attrs ...field.AssignExpr) IAppMessageReadStatusDo { - return a.withDO(a.DO.Attrs(attrs...)) -} - -func (a appMessageReadStatusDo) Assign(attrs ...field.AssignExpr) IAppMessageReadStatusDo { - return a.withDO(a.DO.Assign(attrs...)) -} - -func (a appMessageReadStatusDo) Joins(fields ...field.RelationField) IAppMessageReadStatusDo { - for _, _f := range fields { - a = *a.withDO(a.DO.Joins(_f)) - } - return &a -} - -func (a appMessageReadStatusDo) Preload(fields ...field.RelationField) IAppMessageReadStatusDo { - for _, _f := range fields { - a = *a.withDO(a.DO.Preload(_f)) - } - return &a -} - -func (a appMessageReadStatusDo) FirstOrInit() (*model.AppMessageReadStatus, error) { - if result, err := a.DO.FirstOrInit(); err != nil { - return nil, err - } else { - return result.(*model.AppMessageReadStatus), nil - } -} - -func (a appMessageReadStatusDo) FirstOrCreate() (*model.AppMessageReadStatus, error) { - if result, err := a.DO.FirstOrCreate(); err != nil { - return nil, err - } else { - return result.(*model.AppMessageReadStatus), nil - } -} - -func (a appMessageReadStatusDo) FindByPage(offset int, limit int) (result []*model.AppMessageReadStatus, count int64, err error) { - result, err = a.Offset(offset).Limit(limit).Find() - if err != nil { - return - } - - if size := len(result); 0 < limit && 0 < size && size < limit { - count = int64(size + offset) - return - } - - count, err = a.Offset(-1).Limit(-1).Count() - return -} - -func (a appMessageReadStatusDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { - count, err = a.Count() - if err != nil { - return - } - - err = a.Offset(offset).Limit(limit).Scan(result) - return -} - -func (a appMessageReadStatusDo) Scan(result interface{}) (err error) { - return a.DO.Scan(result) -} - -func (a appMessageReadStatusDo) Delete(models ...*model.AppMessageReadStatus) (result gen.ResultInfo, err error) { - return a.DO.Delete(models) -} - -func (a *appMessageReadStatusDo) withDO(do gen.Dao) *appMessageReadStatusDo { - a.DO = *do.(*gen.DO) - return a -} \ No newline at end of file diff --git a/internal/repository/mysql/dao/gen.go b/internal/repository/mysql/dao/gen.go index 841b333..371c10f 100644 --- a/internal/repository/mysql/dao/gen.go +++ b/internal/repository/mysql/dao/gen.go @@ -21,7 +21,6 @@ var ( AppKeyword *appKeyword AppKeywordReply *appKeywordReply AppMessageLog *appMessageLog - AppMessageReadStatus *appMessageReadStatus AppUser *appUser LogOperation *logOperation LogRequest *logRequest @@ -34,7 +33,6 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) { AppKeyword = &Q.AppKeyword AppKeywordReply = &Q.AppKeywordReply AppMessageLog = &Q.AppMessageLog - AppMessageReadStatus = &Q.AppMessageReadStatus AppUser = &Q.AppUser LogOperation = &Q.LogOperation LogRequest = &Q.LogRequest @@ -43,47 +41,44 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) { func Use(db *gorm.DB, opts ...gen.DOOption) *Query { return &Query{ - db: db, - Admin: newAdmin(db, opts...), - AppKeyword: newAppKeyword(db, opts...), - AppKeywordReply: newAppKeywordReply(db, opts...), - AppMessageLog: newAppMessageLog(db, opts...), - AppMessageReadStatus: newAppMessageReadStatus(db, opts...), - AppUser: newAppUser(db, opts...), - LogOperation: newLogOperation(db, opts...), - LogRequest: newLogRequest(db, opts...), - MiniProgram: newMiniProgram(db, opts...), + db: db, + Admin: newAdmin(db, opts...), + AppKeyword: newAppKeyword(db, opts...), + AppKeywordReply: newAppKeywordReply(db, opts...), + AppMessageLog: newAppMessageLog(db, opts...), + AppUser: newAppUser(db, opts...), + LogOperation: newLogOperation(db, opts...), + LogRequest: newLogRequest(db, opts...), + MiniProgram: newMiniProgram(db, opts...), } } type Query struct { db *gorm.DB - Admin admin - AppKeyword appKeyword - AppKeywordReply appKeywordReply - AppMessageLog appMessageLog - AppMessageReadStatus appMessageReadStatus - AppUser appUser - LogOperation logOperation - LogRequest logRequest - MiniProgram miniProgram + Admin admin + AppKeyword appKeyword + AppKeywordReply appKeywordReply + AppMessageLog appMessageLog + AppUser appUser + LogOperation logOperation + LogRequest logRequest + MiniProgram miniProgram } func (q *Query) Available() bool { return q.db != nil } func (q *Query) clone(db *gorm.DB) *Query { return &Query{ - db: db, - Admin: q.Admin.clone(db), - AppKeyword: q.AppKeyword.clone(db), - AppKeywordReply: q.AppKeywordReply.clone(db), - AppMessageLog: q.AppMessageLog.clone(db), - AppMessageReadStatus: q.AppMessageReadStatus.clone(db), - AppUser: q.AppUser.clone(db), - LogOperation: q.LogOperation.clone(db), - LogRequest: q.LogRequest.clone(db), - MiniProgram: q.MiniProgram.clone(db), + db: db, + Admin: q.Admin.clone(db), + AppKeyword: q.AppKeyword.clone(db), + AppKeywordReply: q.AppKeywordReply.clone(db), + AppMessageLog: q.AppMessageLog.clone(db), + AppUser: q.AppUser.clone(db), + LogOperation: q.LogOperation.clone(db), + LogRequest: q.LogRequest.clone(db), + MiniProgram: q.MiniProgram.clone(db), } } @@ -97,52 +92,49 @@ func (q *Query) WriteDB() *Query { func (q *Query) ReplaceDB(db *gorm.DB) *Query { return &Query{ - db: db, - Admin: q.Admin.replaceDB(db), - AppKeyword: q.AppKeyword.replaceDB(db), - AppKeywordReply: q.AppKeywordReply.replaceDB(db), - AppMessageLog: q.AppMessageLog.replaceDB(db), - AppMessageReadStatus: q.AppMessageReadStatus.replaceDB(db), - AppUser: q.AppUser.replaceDB(db), - LogOperation: q.LogOperation.replaceDB(db), - LogRequest: q.LogRequest.replaceDB(db), - MiniProgram: q.MiniProgram.replaceDB(db), + db: db, + Admin: q.Admin.replaceDB(db), + AppKeyword: q.AppKeyword.replaceDB(db), + AppKeywordReply: q.AppKeywordReply.replaceDB(db), + AppMessageLog: q.AppMessageLog.replaceDB(db), + AppUser: q.AppUser.replaceDB(db), + LogOperation: q.LogOperation.replaceDB(db), + LogRequest: q.LogRequest.replaceDB(db), + MiniProgram: q.MiniProgram.replaceDB(db), } } type queryCtx struct { - Admin *adminDo - AppKeyword *appKeywordDo - AppKeywordReply *appKeywordReplyDo - AppMessageLog *appMessageLogDo - AppMessageReadStatus *appMessageReadStatusDo - AppUser *appUserDo - LogOperation *logOperationDo - LogRequest *logRequestDo - MiniProgram *miniProgramDo + Admin *adminDo + AppKeyword *appKeywordDo + AppKeywordReply *appKeywordReplyDo + AppMessageLog *appMessageLogDo + AppUser *appUserDo + LogOperation *logOperationDo + LogRequest *logRequestDo + MiniProgram *miniProgramDo } func (q *Query) WithContext(ctx context.Context) *queryCtx { return &queryCtx{ - Admin: q.Admin.WithContext(ctx), - AppKeyword: q.AppKeyword.WithContext(ctx), - AppKeywordReply: q.AppKeywordReply.WithContext(ctx), - AppMessageLog: q.AppMessageLog.WithContext(ctx), - AppMessageReadStatus: q.AppMessageReadStatus.WithContext(ctx).(*appMessageReadStatusDo), - AppUser: q.AppUser.WithContext(ctx), - LogOperation: q.LogOperation.WithContext(ctx), - LogRequest: q.LogRequest.WithContext(ctx), - MiniProgram: q.MiniProgram.WithContext(ctx), + Admin: q.Admin.WithContext(ctx), + AppKeyword: q.AppKeyword.WithContext(ctx), + AppKeywordReply: q.AppKeywordReply.WithContext(ctx), + AppMessageLog: q.AppMessageLog.WithContext(ctx), + AppUser: q.AppUser.WithContext(ctx), + LogOperation: q.LogOperation.WithContext(ctx), + LogRequest: q.LogRequest.WithContext(ctx), + MiniProgram: q.MiniProgram.WithContext(ctx), } } func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error { - return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...) + return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.ReplaceDB(tx)) }, opts...) } func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx { tx := q.db.Begin(opts...) - return &QueryTx{Query: q.clone(tx), Error: tx.Error} + return &QueryTx{Query: q.ReplaceDB(tx), Error: tx.Error} } type QueryTx struct { diff --git a/internal/repository/mysql/model/app_message_log.gen.go b/internal/repository/mysql/model/app_message_log.gen.go index 1775a84..f0b9fee 100644 --- a/internal/repository/mysql/model/app_message_log.gen.go +++ b/internal/repository/mysql/model/app_message_log.gen.go @@ -20,6 +20,7 @@ type AppMessageLog struct { ReceiverID string `gorm:"column:receiver_id;not null;comment:接收人ID" json:"receiver_id"` // 接收人ID MsgType int32 `gorm:"column:msg_type;not null;comment:消息类型(1:文本 2:图片)" json:"msg_type"` // 消息类型(1:文本 2:图片) Content string `gorm:"column:content;not null;comment:消息内容" json:"content"` // 消息内容 + IsRead int32 `gorm:"column:is_read;not null;default:0;comment:是否已读(0:未读 1:已读)" json:"is_read"` // 是否已读(0:未读 1:已读) CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 } diff --git a/internal/repository/mysql/model/app_message_read_status.gen.go b/internal/repository/mysql/model/app_message_read_status.gen.go deleted file mode 100644 index ff6cd21..0000000 --- a/internal/repository/mysql/model/app_message_read_status.gen.go +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated by gorm.io/gen. DO NOT EDIT. -// Code generated by gorm.io/gen. DO NOT EDIT. -// Code generated by gorm.io/gen. DO NOT EDIT. - -package model - -import ( - "time" -) - -const TableNameAppMessageReadStatus = "app_message_read_status" - -// AppMessageReadStatus 消息已读状态表 -type AppMessageReadStatus struct { - ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:主键ID" json:"id"` // 主键ID - AppID string `gorm:"column:app_id;not null;comment:小程序ID" json:"app_id"` // 小程序ID - MessageID int32 `gorm:"column:message_id;not null;comment:消息ID" json:"message_id"` // 消息ID - UserID string `gorm:"column:user_id;not null;comment:用户ID" json:"user_id"` // 用户ID - IsRead int32 `gorm:"column:is_read;not null;default:0;comment:是否已读(0:未读 1:已读)" json:"is_read"` // 是否已读(0:未读 1:已读) - ReadTime *time.Time `gorm:"column:read_time;comment:已读时间" json:"read_time"` // 已读时间 - CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 - UpdatedAt time.Time `gorm:"column:updated_at;not null;default:CURRENT_TIMESTAMP;comment:更新时间" json:"updated_at"` // 更新时间 -} - -// TableName AppMessageReadStatus's table name -func (*AppMessageReadStatus) TableName() string { - return TableNameAppMessageReadStatus -} \ No newline at end of file diff --git a/internal/router/router.go b/internal/router/router.go index a6f3d1c..dd41e84 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -63,10 +63,9 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo, cron cron.Server) (co appNonAuthApiRouter := mux.Group("/app") { - appNonAuthApiRouter.POST("/user/create", appHandler.CreateAppUser()) // 新增小程序用户 - appNonAuthApiRouter.GET("/messages", messageHandler.AppMessagePageList()) // 消息列表 - appNonAuthApiRouter.POST("/messages/read", messageHandler.MarkMessageRead()) // 标记消息为已读 - appNonAuthApiRouter.POST("/send_message", messageHandler.UserSendMessage()) // 发送消息 + appNonAuthApiRouter.POST("/user/create", appHandler.CreateAppUser()) // 新增小程序用户 + appNonAuthApiRouter.GET("/messages", messageHandler.AppMessagePageList()) // 消息列表 + appNonAuthApiRouter.POST("/send_message", messageHandler.UserSendMessage()) // 发送消息 } // 微信 API 路由组 diff --git a/logs/mini-chat-access.log b/logs/mini-chat-access.log new file mode 100644 index 0000000..9c25d46 --- /dev/null +++ b/logs/mini-chat-access.log @@ -0,0 +1 @@ +{"level":"fatal","time":"2025-10-18 19:32:41","caller":"logger/logger.go:333","msg":"http server startup err","domain":"mini-chat[fat]","error":"listen tcp :9991: bind: address already in use"} diff --git a/mini-chat b/mini-chat new file mode 100755 index 0000000..79b5370 Binary files /dev/null and b/mini-chat differ