第 5 阶段:纠纷、通知与后台-1

This commit is contained in:
yml
2026-05-22 16:34:32 +08:00
parent 9ebdfab078
commit 99f9df7bcd
32 changed files with 1711 additions and 8 deletions
@@ -0,0 +1,3 @@
# Notification Module
站内信列表、已读状态和订单流程消息。
@@ -0,0 +1,15 @@
package notification
import "time"
type NotificationDTO struct {
ID uint64 `json:"id"`
UserID uint64 `json:"user_id"`
Type string `json:"type"`
Title string `json:"title"`
Content string `json:"content"`
BizType string `json:"biz_type"`
BizID *uint64 `json:"biz_id"`
ReadAt *time.Time `json:"read_at"`
CreatedAt time.Time `json:"created_at"`
}
@@ -0,0 +1,69 @@
package notification
import (
"errors"
"strconv"
"hfb_sys/backend/internal/middleware"
"hfb_sys/backend/pkg/response"
"github.com/gin-gonic/gin"
)
type Handler struct {
service *Service
}
func NewHandler(service *Service) *Handler {
return &Handler{service: service}
}
func (h *Handler) List(c *gin.Context) {
userID, ok := currentUserID(c)
if !ok {
response.Unauthorized(c, "缺少用户上下文")
return
}
items, err := h.service.List(userID)
if err != nil {
writeNotificationError(c, err)
return
}
response.OK(c, gin.H{"items": items})
}
func (h *Handler) MarkRead(c *gin.Context) {
userID, ok := currentUserID(c)
if !ok {
response.Unauthorized(c, "缺少用户上下文")
return
}
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || id == 0 {
response.BadRequest(c, "ID 不正确")
return
}
if err := h.service.MarkRead(userID, id); err != nil {
writeNotificationError(c, err)
return
}
response.OK(c, gin.H{"read": true})
}
func currentUserID(c *gin.Context) (uint64, bool) {
value, ok := c.Get(middleware.ContextUserID)
if !ok {
return 0, false
}
userID, ok := value.(uint64)
return userID, ok
}
func writeNotificationError(c *gin.Context, err error) {
switch {
case errors.Is(err, ErrDependencyUnavailable):
response.ServiceUnavailable(c, "数据库未连接")
default:
response.ServiceUnavailable(c, "通知服务暂时不可用")
}
}
@@ -0,0 +1,82 @@
package notification
import (
"time"
"hfb_sys/backend/internal/model"
"gorm.io/gorm"
)
type Repository struct {
db *gorm.DB
}
type Entry struct {
UserID uint64
Type string
Title string
Content string
BizType string
BizID *uint64
}
func NewRepository(db *gorm.DB) *Repository {
return &Repository{db: db}
}
func (r *Repository) List(userID uint64) ([]NotificationDTO, error) {
var rows []model.Notification
if err := r.db.Where("user_id = ?", userID).Order("id DESC").Limit(100).Find(&rows).Error; err != nil {
return nil, err
}
items := make([]NotificationDTO, 0, len(rows))
for _, row := range rows {
items = append(items, toDTO(row))
}
return items, nil
}
func (r *Repository) MarkRead(userID uint64, id uint64) error {
now := time.Now()
return r.db.Model(&model.Notification{}).
Where("id = ? AND user_id = ?", id, userID).
Update("read_at", now).Error
}
func Append(tx *gorm.DB, entries ...Entry) error {
for _, entry := range entries {
if entry.UserID == 0 || entry.Title == "" {
continue
}
row := model.Notification{
UserID: entry.UserID,
Type: entry.Type,
Title: entry.Title,
Content: entry.Content,
BizType: entry.BizType,
BizID: entry.BizID,
}
if row.Type == "" {
row.Type = "system"
}
if err := tx.Create(&row).Error; err != nil {
return err
}
}
return nil
}
func toDTO(row model.Notification) NotificationDTO {
return NotificationDTO{
ID: row.ID,
UserID: row.UserID,
Type: row.Type,
Title: row.Title,
Content: row.Content,
BizType: row.BizType,
BizID: row.BizID,
ReadAt: row.ReadAt,
CreatedAt: row.CreatedAt,
}
}
@@ -0,0 +1,27 @@
package notification
import "errors"
var ErrDependencyUnavailable = errors.New("dependency unavailable")
type Service struct {
repo *Repository
}
func NewService(repo *Repository) *Service {
return &Service{repo: repo}
}
func (s *Service) List(userID uint64) ([]NotificationDTO, error) {
if s.repo == nil {
return nil, ErrDependencyUnavailable
}
return s.repo.List(userID)
}
func (s *Service) MarkRead(userID uint64, id uint64) error {
if s.repo == nil {
return ErrDependencyUnavailable
}
return s.repo.MarkRead(userID, id)
}