package service import ( "context" "errors" "fmt" "time" "gorm.io/gorm" smokemodel "wx_service/internal/smoke/model" ) var ( ErrSmokeLogNotFound = errors.New("smoke log not found") ) type SmokeLogService struct { db *gorm.DB } func NewSmokeLogService(db *gorm.DB) *SmokeLogService { return &SmokeLogService{db: db} } type CreateSmokeLogRequest struct { SmokeTime *time.Time SmokeAt *time.Time Remark string Level int64 Num int } func (s *SmokeLogService) Create(ctx context.Context, uid int, req CreateSmokeLogRequest) (*smokemodel.SmokeLog, error) { now := time.Now().Unix() createTime := now updateTime := now level := req.Level if level <= 0 { level = 1 } num := req.Num if num <= 0 { num = 1 } smokeAt := req.SmokeAt // smokeTime 用于“按天筛选”;如果传了 smokeAt,建议用其日期部分回填 smokeTime,避免出现不一致。 smokeTime := req.SmokeTime if smokeAt != nil { day := time.Date(smokeAt.Year(), smokeAt.Month(), smokeAt.Day(), 0, 0, 0, 0, smokeAt.Location()) smokeTime = &day } if smokeTime == nil { today := time.Now() startOfDay := time.Date(today.Year(), today.Month(), today.Day(), 0, 0, 0, 0, today.Location()) smokeTime = &startOfDay } record := smokemodel.SmokeLog{ UID: uid, SmokeTime: smokeTime, SmokeAt: smokeAt, Remark: req.Remark, CreateTime: &createTime, UpdateTime: &updateTime, Level: level, Num: num, } if err := s.db.WithContext(ctx).Create(&record).Error; err != nil { return nil, fmt.Errorf("create smoke log: %w", err) } return &record, nil } func (s *SmokeLogService) GetByID(ctx context.Context, uid int, id int) (*smokemodel.SmokeLog, error) { var record smokemodel.SmokeLog err := s.db.WithContext(ctx). Where("id = ? AND uid = ? AND (deletetime IS NULL OR deletetime = 0)", id, uid). First(&record).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, ErrSmokeLogNotFound } return nil, fmt.Errorf("load smoke log: %w", err) } return &record, nil } type ListSmokeLogsRequest struct { Page int PageSize int Start *time.Time End *time.Time } type ListSmokeLogsResult struct { Items []smokemodel.SmokeLog Total int64 Page int PageSize int } func (s *SmokeLogService) List(ctx context.Context, uid int, req ListSmokeLogsRequest) (ListSmokeLogsResult, error) { page := req.Page if page <= 0 { page = 1 } pageSize := req.PageSize if pageSize <= 0 { pageSize = 20 } if pageSize > 200 { pageSize = 200 } tx := s.db.WithContext(ctx).Model(&smokemodel.SmokeLog{}). Where("uid = ? AND (deletetime IS NULL OR deletetime = 0)", uid) if req.Start != nil { tx = tx.Where("smoke_time >= ?", req.Start.Format("2006-01-02")) } if req.End != nil { tx = tx.Where("smoke_time <= ?", req.End.Format("2006-01-02")) } var total int64 if err := tx.Count(&total).Error; err != nil { return ListSmokeLogsResult{}, fmt.Errorf("count smoke logs: %w", err) } var items []smokemodel.SmokeLog offset := (page - 1) * pageSize if err := tx. Order("smoke_time DESC"). Order("id DESC"). Limit(pageSize). Offset(offset). Find(&items).Error; err != nil { return ListSmokeLogsResult{}, fmt.Errorf("list smoke logs: %w", err) } return ListSmokeLogsResult{ Items: items, Total: total, Page: page, PageSize: pageSize, }, nil } type UpdateSmokeLogRequest struct { // SmokeTimeProvided 用于区分: // - false:前端没传 smoke_time(不修改) // - true:前端传了 smoke_time(可以设置为具体日期,也可以清空为 NULL) SmokeTimeProvided bool SmokeTime *time.Time // SmokeAtProvided 用于区分: // - false:前端没传 smoke_at(不修改) // - true:前端传了 smoke_at(可以设置为具体时间,也可以清空为 NULL) SmokeAtProvided bool SmokeAt *time.Time Remark *string Level *int64 Num *int } func (s *SmokeLogService) Update(ctx context.Context, uid int, id int, req UpdateSmokeLogRequest) (*smokemodel.SmokeLog, error) { record, err := s.GetByID(ctx, uid, id) if err != nil { return nil, err } updates := map[string]interface{}{} if req.SmokeTimeProvided { updates["smoke_time"] = req.SmokeTime } if req.SmokeAtProvided { updates["smoke_at"] = req.SmokeAt if req.SmokeAt != nil { day := time.Date(req.SmokeAt.Year(), req.SmokeAt.Month(), req.SmokeAt.Day(), 0, 0, 0, 0, req.SmokeAt.Location()) updates["smoke_time"] = &day } } if req.Remark != nil { updates["remark"] = *req.Remark } if req.Level != nil { if *req.Level <= 0 { updates["level"] = int64(1) } else { updates["level"] = *req.Level } } if req.Num != nil { if *req.Num <= 0 { updates["num"] = 1 } else { updates["num"] = *req.Num } } now := time.Now().Unix() updates["updatetime"] = now if len(updates) == 1 { return record, nil } if err := s.db.WithContext(ctx). Model(&smokemodel.SmokeLog{}). Where("id = ? AND uid = ?", id, uid). Updates(updates).Error; err != nil { return nil, fmt.Errorf("update smoke log: %w", err) } return s.GetByID(ctx, uid, id) } func (s *SmokeLogService) Delete(ctx context.Context, uid int, id int) error { now := time.Now().Unix() result := s.db.WithContext(ctx). Model(&smokemodel.SmokeLog{}). Where("id = ? AND uid = ? AND (deletetime IS NULL OR deletetime = 0)", id, uid). Updates(map[string]interface{}{ "deletetime": now, "updatetime": now, }) if result.Error != nil { return fmt.Errorf("delete smoke log: %w", result.Error) } if result.RowsAffected == 0 { return ErrSmokeLogNotFound } return nil }