docs(website): 添加简记memo产品原型和UI设计规范文档
- 新增「简记memo」一体化小程序产品原型设计文档 - 新增简记memo完整版UI视觉设计规范和界面细节 - 添加IDEA项目配置文件.gitignore - 创建404页面HTML文件,包含响应式布局和错误提示 - 添加关于页面HTML文件,展示品牌介绍和团队信息 - 实现AES加解密工具函数,支持请求体加密 - 添加用户协议页面基础框架
This commit is contained in:
+411
@@ -0,0 +1,411 @@
|
||||
// 心情接口
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"simple-memo/global"
|
||||
"simple-memo/models"
|
||||
"simple-memo/utils"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// -------------------------- 1. 定义 Handler 接口 --------------------------
|
||||
// MoodHandler 心情模块接口定义
|
||||
type MoodHandler interface {
|
||||
GetMoodByDate(c *gin.Context)
|
||||
SaveMood(c *gin.Context)
|
||||
GetMoodList(c *gin.Context)
|
||||
DeleteMood(c *gin.Context)
|
||||
MoodStat(c *gin.Context) // 心情统计
|
||||
}
|
||||
|
||||
// -------------------------- 2. 实现结构体 --------------------------
|
||||
// moodHandler 接口实现结构体
|
||||
type moodHandler struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewMoodHandler 创建心情处理器
|
||||
func NewMoodHandler() MoodHandler {
|
||||
return &moodHandler{
|
||||
db: global.DB,
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------- 3. 请求结构体 --------------------------
|
||||
// GetMoodByDateReq 获取某天心情请求
|
||||
type GetMoodByDateReq struct {
|
||||
Date string `json:"date" binding:"required"`
|
||||
}
|
||||
|
||||
// SaveMoodReq 保存心情请求
|
||||
type SaveMoodReq struct {
|
||||
ID uint `json:"id"` // 编辑时传入
|
||||
Date string `json:"date" binding:"required"`
|
||||
Emoji string `json:"emoji" binding:"required"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// GetMoodListReq 获取心情列表请求
|
||||
type GetMoodListReq struct {
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
}
|
||||
|
||||
// DeleteMoodReq 删除心情请求
|
||||
type DeleteMoodReq struct {
|
||||
ID uint `json:"id" binding:"required"`
|
||||
}
|
||||
|
||||
// MoodStatReq 心情统计请求
|
||||
type MoodStatReq struct {
|
||||
Month string `json:"month" binding:"required"` // YYYY-MM
|
||||
}
|
||||
|
||||
// -------------------------- 4. 接口实现 --------------------------
|
||||
|
||||
// MoodCalendarItem 日历心情项(优化后的返回结构)
|
||||
type MoodCalendarItem struct {
|
||||
ID uint `json:"id"`
|
||||
Date string `json:"date"`
|
||||
Emoji string `json:"emoji"`
|
||||
Content string `json:"content"`
|
||||
AIStatus int `json:"ai_status"` // 0无 1生成中 2成功 3失败
|
||||
AIText string `json:"ai_text"`
|
||||
AIImageURL string `json:"ai_image_url"`
|
||||
AIGenerateCount int `json:"ai_generate_count"`
|
||||
}
|
||||
|
||||
// GetMoodByDate 获取某天的心情(包含AI生成内容)
|
||||
func (h *moodHandler) GetMoodByDate(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var req GetMoodByDateReq
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 使用 GORM Joins 联表查询
|
||||
var result MoodCalendarItem
|
||||
h.db.Model(&models.Mood{}).
|
||||
Select(`
|
||||
sm_mood.ID,
|
||||
sm_mood.date,
|
||||
sm_mood.emoji,
|
||||
sm_mood.content,
|
||||
COALESCE(sm_mood_ai_generations.status, 0) as ai_status,
|
||||
COALESCE(sm_mood_ai_generations.ai_text, '') as ai_text,
|
||||
COALESCE(sm_mood_ai_generations.ai_image_url, '') as ai_image_url,
|
||||
COALESCE(sm_mood_ai_generations.generate_count, 0) as ai_generate_count
|
||||
`).
|
||||
Joins("LEFT JOIN sm_mood_ai_generations ON sm_mood.id = sm_mood_ai_generations.mood_id").
|
||||
Where("sm_mood.user_id = ? AND sm_mood.date = ?", userID, req.Date).
|
||||
Scan(&result)
|
||||
|
||||
// 检查是否找到记录
|
||||
if result.ID == 0 {
|
||||
utils.Ok(c, nil)
|
||||
return
|
||||
}
|
||||
|
||||
utils.Ok(c, result)
|
||||
}
|
||||
|
||||
// SaveMood 保存心情
|
||||
func (h *moodHandler) SaveMood(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var req SaveMoodReq
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 敏感词过滤
|
||||
filteredContent := utils.FilterSensitive(req.Content)
|
||||
|
||||
var mood models.Mood
|
||||
isNewRecord := false
|
||||
|
||||
// 如果传入了 id,先尝试按 id 查找
|
||||
if req.ID > 0 {
|
||||
err := h.db.Where("id = ? AND user_id = ?", req.ID, userID).First(&mood).Error
|
||||
if err == nil {
|
||||
// 编辑模式
|
||||
mood.Emoji = req.Emoji
|
||||
mood.Content = filteredContent
|
||||
if err := h.db.Save(&mood).Error; err != nil {
|
||||
utils.Fail(c, "保存失败")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// id 找不到,按日期查找
|
||||
err = h.db.Where("user_id = ? AND date = ?", userID, req.Date).First(&mood).Error
|
||||
if err == nil {
|
||||
mood.Emoji = req.Emoji
|
||||
mood.Content = filteredContent
|
||||
if err := h.db.Save(&mood).Error; err != nil {
|
||||
utils.Fail(c, "保存失败")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// 新增模式
|
||||
mood = models.Mood{
|
||||
UserID: userID,
|
||||
Date: req.Date,
|
||||
Emoji: req.Emoji,
|
||||
Content: filteredContent,
|
||||
}
|
||||
if err := h.db.Create(&mood).Error; err != nil {
|
||||
utils.Fail(c, "保存失败")
|
||||
return
|
||||
}
|
||||
isNewRecord = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 没有传入 id,按日期查找
|
||||
err := h.db.Where("user_id = ? AND date = ?", userID, req.Date).First(&mood).Error
|
||||
if err == nil {
|
||||
// 更新已有记录
|
||||
mood.Emoji = req.Emoji
|
||||
mood.Content = filteredContent
|
||||
if err := h.db.Save(&mood).Error; err != nil {
|
||||
utils.Fail(c, "保存失败")
|
||||
return
|
||||
}
|
||||
isNewRecord = true
|
||||
} else {
|
||||
// 新增记录
|
||||
mood = models.Mood{
|
||||
UserID: userID,
|
||||
Date: req.Date,
|
||||
Emoji: req.Emoji,
|
||||
Content: filteredContent,
|
||||
}
|
||||
if err := h.db.Create(&mood).Error; err != nil {
|
||||
utils.Fail(c, "保存失败")
|
||||
return
|
||||
}
|
||||
isNewRecord = true
|
||||
}
|
||||
}
|
||||
|
||||
var growthResult any
|
||||
debugMode := os.Getenv("DEBUG_MODE")
|
||||
if isNewRecord {
|
||||
// ==================== 调试模式 ====================
|
||||
if debugMode == "true" {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
levelUp := true // rand.Intn(10) < 2 // 20%概率升级
|
||||
hasAchievement := true // rand.Intn(10) < 3 // 30%概率获得成就
|
||||
growthResult = MockGrowthRewards(levelUp, hasAchievement)
|
||||
} else {
|
||||
// 生产模式:真实调用
|
||||
today := time.Now().Format("2006-01-02")
|
||||
if req.Date == today {
|
||||
ctx := context.Background()
|
||||
key := fmt.Sprintf("mood_count:%d:%s", userID, today)
|
||||
exists, _ := global.Redis.Exists(ctx, key).Result()
|
||||
if exists == 0 {
|
||||
growthHandler := NewGrowthHandler()
|
||||
growthResult = growthHandler.AddExpInternal(userID, "mood", fmt.Sprintf("%d", mood.ID), nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
// ==================================================
|
||||
}
|
||||
|
||||
// 异步触发AI生成(仅新记录触发,编辑不触发)
|
||||
if isNewRecord {
|
||||
GenerateMoodAIContent(mood.ID, userID, mood.Emoji, mood.Content, mood.Date)
|
||||
}
|
||||
|
||||
// 查询 AI 生成记录并填充
|
||||
var aiGen models.MoodAIGeneration
|
||||
h.db.Where("mood_id = ?", mood.ID).First(&aiGen)
|
||||
mood.AIGen = aiGen
|
||||
|
||||
utils.Ok(c, gin.H{
|
||||
"success": true,
|
||||
"growth": growthResult,
|
||||
"mood": mood,
|
||||
"debugMode": debugMode,
|
||||
"isDebug": debugMode == "true",
|
||||
"isNewRecord": isNewRecord,
|
||||
})
|
||||
}
|
||||
|
||||
// GetMoodList 获取心情列表(包含AI生成内容)
|
||||
func (h *moodHandler) GetMoodList(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var req GetMoodListReq
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 构建基础查询
|
||||
query := h.db.Model(&models.Mood{}).
|
||||
Select(`
|
||||
sm_mood.ID,
|
||||
sm_mood.date,
|
||||
sm_mood.emoji,
|
||||
sm_mood.content,
|
||||
COALESCE(sm_mood_ai_generations.status, 0) as ai_status,
|
||||
COALESCE(sm_mood_ai_generations.ai_text, '') as ai_text,
|
||||
COALESCE(sm_mood_ai_generations.ai_image_url, '') as ai_image_url,
|
||||
COALESCE(sm_mood_ai_generations.generate_count, 0) as ai_generate_count
|
||||
`).
|
||||
Joins("LEFT JOIN sm_mood_ai_generations ON sm_mood.id = sm_mood_ai_generations.mood_id").
|
||||
Where("sm_mood.user_id = ?", userID)
|
||||
|
||||
// 日期筛选
|
||||
if req.StartDate != "" && req.EndDate != "" {
|
||||
query = query.Where("sm_mood.date BETWEEN ? AND ?", req.StartDate, req.EndDate)
|
||||
} else if req.StartDate != "" {
|
||||
query = query.Where("sm_mood.date >= ?", req.StartDate)
|
||||
} else if req.EndDate != "" {
|
||||
query = query.Where("sm_mood.date <= ?", req.EndDate)
|
||||
}
|
||||
|
||||
var results []MoodCalendarItem
|
||||
if err := query.Order("sm_mood.date DESC").Scan(&results).Error; err != nil {
|
||||
utils.Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
|
||||
utils.Ok(c, results)
|
||||
}
|
||||
|
||||
// DeleteMood 删除心情
|
||||
func (h *moodHandler) DeleteMood(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var req DeleteMoodReq
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 查询心情信息(用于扣除积分)
|
||||
var mood models.Mood
|
||||
err := h.db.Where("id = ? AND user_id = ?", req.ID, userID).First(&mood).Error
|
||||
if err != nil {
|
||||
utils.Fail(c, "心情记录不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 删除心情
|
||||
err = h.db.Where("id = ? AND user_id = ?", req.ID, userID).Delete(&models.Mood{}).Error
|
||||
if err != nil {
|
||||
utils.Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 扣除今天添加时获得的积分
|
||||
growthHandler := NewGrowthHandler()
|
||||
today := time.Now().Format("2006-01-02")
|
||||
deducted := growthHandler.DeductExpInternal(userID, "mood", fmt.Sprintf("%d", req.ID), today)
|
||||
|
||||
utils.Ok(c, gin.H{
|
||||
"deducted_exp": deducted,
|
||||
})
|
||||
}
|
||||
|
||||
// MoodStat 心情统计
|
||||
func (h *moodHandler) MoodStat(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var req MoodStatReq
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 计算月份范围
|
||||
startDate := req.Month + "-01"
|
||||
year, month := 0, 0
|
||||
fmt.Sscanf(req.Month, "%d-%d", &year, &month)
|
||||
_, lastDay := utils.GetMonthLastDay(year, month)
|
||||
endDate := fmt.Sprintf("%s-%02d", req.Month, lastDay)
|
||||
|
||||
// 查询该月的心情记录
|
||||
var moods []models.Mood
|
||||
h.db.Where("user_id = ? AND date BETWEEN ? AND ?", userID, startDate, endDate).
|
||||
Order("date ASC").
|
||||
Find(&moods)
|
||||
|
||||
// 心情分布
|
||||
moodDistribution := make(map[string]int)
|
||||
for _, m := range moods {
|
||||
moodDistribution[m.Emoji]++
|
||||
}
|
||||
|
||||
// 计算总天数和已记录天数
|
||||
totalDays := lastDay
|
||||
recordedDays := len(moods)
|
||||
|
||||
// 周趋势(按周分组)
|
||||
weeklyTrend := []gin.H{}
|
||||
weekMap := make(map[string][]models.Mood)
|
||||
|
||||
for _, m := range moods {
|
||||
// 解析日期,计算是第几周
|
||||
t, _ := time.Parse("2006-01-02", m.Date)
|
||||
_, week := t.ISOWeek()
|
||||
weekKey := fmt.Sprintf("W%d", week)
|
||||
weekMap[weekKey] = append(weekMap[weekKey], m)
|
||||
}
|
||||
|
||||
// 计算每周的平均分数
|
||||
// 心情emoji映射到分数(简单映射)
|
||||
emojiScore := map[string]float64{
|
||||
"😊": 5.0, "😄": 5.0, "😎": 4.5,
|
||||
"😌": 4.0, "🙂": 3.5,
|
||||
"😐": 3.0,
|
||||
"😔": 2.5, "😢": 2.0,
|
||||
"😤": 2.0, "😰": 1.5,
|
||||
"😴": 3.0, "🤔": 3.0,
|
||||
}
|
||||
|
||||
for weekKey, weekMoods := range weekMap {
|
||||
var totalScore float64
|
||||
for _, m := range weekMoods {
|
||||
if score, ok := emojiScore[m.Emoji]; ok {
|
||||
totalScore += score
|
||||
} else {
|
||||
totalScore += 3.0 // 默认中等分数
|
||||
}
|
||||
}
|
||||
avgScore := 0.0
|
||||
if len(weekMoods) > 0 {
|
||||
avgScore = totalScore / float64(len(weekMoods))
|
||||
}
|
||||
weeklyTrend = append(weeklyTrend, gin.H{
|
||||
"week": weekKey,
|
||||
"avg_score": avgScore,
|
||||
})
|
||||
}
|
||||
|
||||
// 高频词汇(这里简化处理,返回固定示例)
|
||||
topPhrases := []string{"开心", "平静", "工作顺利", "充实", "放松"}
|
||||
|
||||
utils.Ok(c, gin.H{
|
||||
"month": req.Month,
|
||||
"total_days": totalDays,
|
||||
"recorded_days": recordedDays,
|
||||
"mood_distribution": moodDistribution,
|
||||
"weekly_trend": weeklyTrend,
|
||||
"top_phrases": topPhrases,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user