16844d4a42
- Added AI configuration options to .env.example and config.go for OpenAI integration. - Implemented Redis caching for session management in main.go and auth middleware. - Updated smoke logging service to support real smoking time (`smoke_at`) and AI advice retrieval. - Enhanced API routes to include endpoints for AI advice and unlock functionality for non-members. - Improved database schema with new tables for AI advice and unlock records. - Expanded documentation to cover new AI features and Redis caching implementation.
239 lines
5.6 KiB
Go
239 lines
5.6 KiB
Go
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
|
|
}
|