feat(smoke): support reason tags on smoke logs
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
||||
"wx_service/internal/middleware"
|
||||
"wx_service/internal/model"
|
||||
quitcheckinservice "wx_service/internal/quitcheckin/service"
|
||||
smokemodel "wx_service/internal/smoke/model"
|
||||
smokeservice "wx_service/internal/smoke/service"
|
||||
)
|
||||
|
||||
@@ -58,10 +59,11 @@ type createSmokeLogRequest struct {
|
||||
// 只记录“日期”即可;如果不传,后端会按当天处理
|
||||
SmokeTime string `json:"smoke_time"`
|
||||
// 真实抽烟时间(精确到时分秒,可补录)
|
||||
SmokeAt string `json:"smoke_at"`
|
||||
Remark string `json:"remark"`
|
||||
Level *int64 `json:"level"`
|
||||
Num *int `json:"num"`
|
||||
SmokeAt string `json:"smoke_at"`
|
||||
Remark string `json:"remark"`
|
||||
ReasonTags smokemodel.StringSlice `json:"reason_tags"`
|
||||
Level *int64 `json:"level"`
|
||||
Num *int `json:"num"`
|
||||
}
|
||||
|
||||
func (h *SmokeHandler) Create(c *gin.Context) {
|
||||
@@ -120,11 +122,12 @@ func (h *SmokeHandler) Create(c *gin.Context) {
|
||||
}
|
||||
|
||||
record, err := h.smokeLogService.Create(c.Request.Context(), int(user.ID), smokeservice.CreateSmokeLogRequest{
|
||||
SmokeTime: smokeTime,
|
||||
SmokeAt: smokeAt,
|
||||
Remark: req.Remark,
|
||||
Level: level,
|
||||
Num: num,
|
||||
SmokeTime: smokeTime,
|
||||
SmokeAt: smokeAt,
|
||||
Remark: req.Remark,
|
||||
ReasonTags: req.ReasonTags,
|
||||
Level: level,
|
||||
Num: num,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "创建记录失败,请稍后重试"))
|
||||
@@ -135,9 +138,10 @@ func (h *SmokeHandler) Create(c *gin.Context) {
|
||||
}
|
||||
|
||||
type resistedSmokeLogRequest struct {
|
||||
SmokeTime string `json:"smoke_time"`
|
||||
SmokeAt string `json:"smoke_at"`
|
||||
Remark string `json:"remark"`
|
||||
SmokeTime string `json:"smoke_time"`
|
||||
SmokeAt string `json:"smoke_at"`
|
||||
Remark string `json:"remark"`
|
||||
ReasonTags smokemodel.StringSlice `json:"reason_tags"`
|
||||
}
|
||||
|
||||
// Resist 表示“想抽但忍住了”:在 fa_smoke_log 中写入 level=0,num=0。
|
||||
@@ -171,11 +175,12 @@ func (h *SmokeHandler) Resist(c *gin.Context) {
|
||||
}
|
||||
|
||||
record, err := h.smokeLogService.Create(c.Request.Context(), int(user.ID), smokeservice.CreateSmokeLogRequest{
|
||||
SmokeTime: smokeTime,
|
||||
SmokeAt: smokeAt,
|
||||
Remark: req.Remark,
|
||||
Level: 0,
|
||||
Num: 0,
|
||||
SmokeTime: smokeTime,
|
||||
SmokeAt: smokeAt,
|
||||
Remark: req.Remark,
|
||||
ReasonTags: req.ReasonTags,
|
||||
Level: 0,
|
||||
Num: 0,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "创建记录失败,请稍后重试"))
|
||||
@@ -331,11 +336,12 @@ func (h *SmokeHandler) LatestLogs(c *gin.Context) {
|
||||
}
|
||||
|
||||
type updateSmokeLogRequest struct {
|
||||
SmokeTime *string `json:"smoke_time"`
|
||||
SmokeAt *string `json:"smoke_at"`
|
||||
Remark *string `json:"remark"`
|
||||
Level *int64 `json:"level"`
|
||||
Num *int `json:"num"`
|
||||
SmokeTime *string `json:"smoke_time"`
|
||||
SmokeAt *string `json:"smoke_at"`
|
||||
Remark *string `json:"remark"`
|
||||
ReasonTags *smokemodel.StringSlice `json:"reason_tags"`
|
||||
Level *int64 `json:"level"`
|
||||
Num *int `json:"num"`
|
||||
}
|
||||
|
||||
func (h *SmokeHandler) Update(c *gin.Context) {
|
||||
@@ -402,6 +408,7 @@ func (h *SmokeHandler) Update(c *gin.Context) {
|
||||
SmokeAtProvided: smokeAtProvided,
|
||||
SmokeAt: smokeAt,
|
||||
Remark: req.Remark,
|
||||
ReasonTags: req.ReasonTags,
|
||||
Level: req.Level,
|
||||
Num: req.Num,
|
||||
})
|
||||
|
||||
@@ -16,7 +16,8 @@ type SmokeLog struct {
|
||||
// smoke_at:真实抽烟时间(可补录,精确到时分秒)
|
||||
SmokeAt *time.Time `gorm:"column:smoke_at;type:datetime;comment:真实抽烟时间(精确到秒)" json:"smoke_at,omitempty"`
|
||||
|
||||
Remark string `gorm:"column:remark;type:text;comment:原因/备注" json:"remark,omitempty"`
|
||||
Remark string `gorm:"column:remark;type:text;comment:原因/备注" json:"remark,omitempty"`
|
||||
ReasonTags StringSlice `gorm:"column:reason_tags;type:json;comment:结构化原因标签(JSON数组)" json:"reason_tags,omitempty"`
|
||||
|
||||
// createtime/updatetime/deletetime:秒级 Unix 时间戳(与 gorm 默认字段不同)
|
||||
CreateTime *int64 `gorm:"column:createtime;comment:创建时间(秒)" json:"createtime,omitempty"`
|
||||
|
||||
@@ -21,16 +21,17 @@ type SmokeLogService struct {
|
||||
|
||||
// smokeLogCreateRow 用于写入 fa_smoke_log,避免 SmokeLog 的 default 标签覆盖 0 值。
|
||||
type smokeLogCreateRow struct {
|
||||
ID int `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
UID int `gorm:"column:uid"`
|
||||
SmokeTime *time.Time `gorm:"column:smoke_time"`
|
||||
SmokeAt *time.Time `gorm:"column:smoke_at"`
|
||||
Remark string `gorm:"column:remark"`
|
||||
CreateTime *int64 `gorm:"column:createtime"`
|
||||
UpdateTime *int64 `gorm:"column:updatetime"`
|
||||
DeleteTime *int64 `gorm:"column:deletetime"`
|
||||
Level *int64 `gorm:"column:level"`
|
||||
Num *int `gorm:"column:num"`
|
||||
ID int `gorm:"column:id;primaryKey;autoIncrement"`
|
||||
UID int `gorm:"column:uid"`
|
||||
SmokeTime *time.Time `gorm:"column:smoke_time"`
|
||||
SmokeAt *time.Time `gorm:"column:smoke_at"`
|
||||
Remark string `gorm:"column:remark"`
|
||||
ReasonTags smokemodel.StringSlice `gorm:"column:reason_tags"`
|
||||
CreateTime *int64 `gorm:"column:createtime"`
|
||||
UpdateTime *int64 `gorm:"column:updatetime"`
|
||||
DeleteTime *int64 `gorm:"column:deletetime"`
|
||||
Level *int64 `gorm:"column:level"`
|
||||
Num *int `gorm:"column:num"`
|
||||
}
|
||||
|
||||
func (smokeLogCreateRow) TableName() string {
|
||||
@@ -42,11 +43,12 @@ func NewSmokeLogService(db *gorm.DB) *SmokeLogService {
|
||||
}
|
||||
|
||||
type CreateSmokeLogRequest struct {
|
||||
SmokeTime *time.Time
|
||||
SmokeAt *time.Time
|
||||
Remark string
|
||||
Level int64
|
||||
Num int
|
||||
SmokeTime *time.Time
|
||||
SmokeAt *time.Time
|
||||
Remark string
|
||||
ReasonTags smokemodel.StringSlice
|
||||
Level int64
|
||||
Num int
|
||||
}
|
||||
|
||||
func (s *SmokeLogService) Create(ctx context.Context, uid int, req CreateSmokeLogRequest) (*smokemodel.SmokeLog, error) {
|
||||
@@ -82,6 +84,7 @@ func (s *SmokeLogService) Create(ctx context.Context, uid int, req CreateSmokeLo
|
||||
SmokeTime: smokeTime,
|
||||
SmokeAt: smokeAt,
|
||||
Remark: req.Remark,
|
||||
ReasonTags: req.ReasonTags,
|
||||
CreateTime: &createTime,
|
||||
UpdateTime: &updateTime,
|
||||
Level: &level,
|
||||
@@ -98,6 +101,7 @@ func (s *SmokeLogService) Create(ctx context.Context, uid int, req CreateSmokeLo
|
||||
SmokeTime: insert.SmokeTime,
|
||||
SmokeAt: insert.SmokeAt,
|
||||
Remark: insert.Remark,
|
||||
ReasonTags: insert.ReasonTags,
|
||||
CreateTime: insert.CreateTime,
|
||||
UpdateTime: insert.UpdateTime,
|
||||
DeleteTime: insert.DeleteTime,
|
||||
@@ -393,7 +397,7 @@ func (s *SmokeLogService) ListLatest(ctx context.Context, uid int, limit int) ([
|
||||
var items []smokemodel.SmokeLog
|
||||
if err := s.db.WithContext(ctx).
|
||||
Model(&smokemodel.SmokeLog{}).
|
||||
Select("id, uid, smoke_time, smoke_at, remark, level, num, createtime, updatetime, deletetime").
|
||||
Select("id, uid, smoke_time, smoke_at, remark, reason_tags, level, num, createtime, updatetime, deletetime").
|
||||
Where("uid = ? AND (deletetime IS NULL OR deletetime = 0)", uid).
|
||||
Order("COALESCE(smoke_at, FROM_UNIXTIME(createtime), smoke_time) DESC").
|
||||
Order("id DESC").
|
||||
@@ -431,6 +435,7 @@ type UpdateSmokeLogRequest struct {
|
||||
SmokeAtProvided bool
|
||||
SmokeAt *time.Time
|
||||
Remark *string
|
||||
ReasonTags *smokemodel.StringSlice
|
||||
Level *int64
|
||||
Num *int
|
||||
}
|
||||
@@ -455,6 +460,9 @@ func (s *SmokeLogService) Update(ctx context.Context, uid int, id int, req Updat
|
||||
if req.Remark != nil {
|
||||
updates["remark"] = *req.Remark
|
||||
}
|
||||
if req.ReasonTags != nil {
|
||||
updates["reason_tags"] = *req.ReasonTags
|
||||
}
|
||||
if req.Level != nil {
|
||||
if *req.Level < 0 {
|
||||
updates["level"] = int64(1)
|
||||
|
||||
@@ -29,6 +29,7 @@ CREATE TABLE fa_smoke_log (
|
||||
smoke_time DATE NULL,
|
||||
smoke_at DATETIME NULL,
|
||||
remark TEXT,
|
||||
reason_tags TEXT,
|
||||
createtime INTEGER,
|
||||
updatetime INTEGER,
|
||||
deletetime INTEGER,
|
||||
@@ -68,3 +69,60 @@ func TestSmokeLogServiceCreateKeepsZeroForResisted(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmokeLogServiceCreatePersistsReasonTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db := setupSmokeLogServiceTestDB(t)
|
||||
svc := NewSmokeLogService(db)
|
||||
|
||||
smokeAt := time.Date(2026, 3, 4, 8, 30, 0, 0, time.Local)
|
||||
_, err := svc.Create(context.Background(), 1002, CreateSmokeLogRequest{
|
||||
SmokeAt: &smokeAt,
|
||||
Remark: "压力大;会后补了一根",
|
||||
ReasonTags: smokemodel.StringSlice{"stress", "social"},
|
||||
Level: 3,
|
||||
Num: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create log with reason tags: %v", err)
|
||||
}
|
||||
|
||||
var got smokemodel.SmokeLog
|
||||
if err := db.Where("uid = ?", 1002).Order("id DESC").First(&got).Error; err != nil {
|
||||
t.Fatalf("load created record: %v", err)
|
||||
}
|
||||
|
||||
if len(got.ReasonTags) != 2 || got.ReasonTags[0] != "stress" || got.ReasonTags[1] != "social" {
|
||||
t.Fatalf("created reason_tags=%v, want=[stress social]", got.ReasonTags)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmokeLogServiceUpdatePersistsReasonTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db := setupSmokeLogServiceTestDB(t)
|
||||
svc := NewSmokeLogService(db)
|
||||
|
||||
smokeAt := time.Date(2026, 3, 4, 9, 0, 0, 0, time.Local)
|
||||
record, err := svc.Create(context.Background(), 1003, CreateSmokeLogRequest{
|
||||
SmokeAt: &smokeAt,
|
||||
Remark: "old",
|
||||
Level: 2,
|
||||
Num: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("create seed log: %v", err)
|
||||
}
|
||||
|
||||
reasonTags := smokemodel.StringSlice{"after_meal", "other"}
|
||||
updated, err := svc.Update(context.Background(), 1003, record.ID, UpdateSmokeLogRequest{
|
||||
ReasonTags: &reasonTags,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("update reason_tags: %v", err)
|
||||
}
|
||||
|
||||
if len(updated.ReasonTags) != 2 || updated.ReasonTags[0] != "after_meal" || updated.ReasonTags[1] != "other" {
|
||||
t.Fatalf("updated reason_tags=%v, want=[after_meal other]", updated.ReasonTags)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user