docs(website): 添加简记memo产品原型和UI设计规范文档
- 新增「简记memo」一体化小程序产品原型设计文档 - 新增简记memo完整版UI视觉设计规范和界面细节 - 添加IDEA项目配置文件.gitignore - 创建404页面HTML文件,包含响应式布局和错误提示 - 添加关于页面HTML文件,展示品牌介绍和团队信息 - 实现AES加解密工具函数,支持请求体加密 - 添加用户协议页面基础框架
This commit is contained in:
+436
@@ -0,0 +1,436 @@
|
||||
// 待办接口
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"simple-memo/global"
|
||||
"simple-memo/models"
|
||||
"simple-memo/utils"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// -------------------------- 1. 定义 Handler 接口 --------------------------
|
||||
// TodoHandler 待办模块接口定义
|
||||
type TodoHandler interface {
|
||||
AddTodo(c *gin.Context) // 添加待办
|
||||
TodoList(c *gin.Context) // 获取待办列表
|
||||
DeleteTodo(c *gin.Context) // 删除待办
|
||||
DoneTodo(c *gin.Context) // 完成/取消完成待办
|
||||
EditTodo(c *gin.Context) // 编辑待办
|
||||
TodoStat(c *gin.Context) // 待办统计
|
||||
}
|
||||
|
||||
// -------------------------- 2. 实现结构体 --------------------------
|
||||
// todoHandler 接口实现结构体
|
||||
type todoHandler struct {
|
||||
db *gorm.DB // 数据库依赖
|
||||
}
|
||||
|
||||
// NewTodoHandler 创建待办处理器
|
||||
func NewTodoHandler() TodoHandler {
|
||||
return &todoHandler{
|
||||
db: global.DB,
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------- 3. 请求结构体 --------------------------
|
||||
// TodoListReq 列表请求参数
|
||||
type TodoListReq struct {
|
||||
Search string `json:"search"`
|
||||
Status string `json:"status"`
|
||||
Date string `json:"date"`
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
}
|
||||
|
||||
// DeleteTodoReq 删除待办请求
|
||||
type DeleteTodoReq struct {
|
||||
ID uint `json:"id"`
|
||||
}
|
||||
|
||||
// DoneTodoReq 完成待办请求
|
||||
type DoneTodoReq struct {
|
||||
ID uint `json:"id"`
|
||||
Status uint `json:"status"` // 0-取消完成 1-完成
|
||||
}
|
||||
|
||||
// EditTodoReq 编辑待办请求(与model对齐)
|
||||
type EditTodoReq struct {
|
||||
ID uint `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Category string `json:"category"`
|
||||
Priority int `json:"priority"`
|
||||
Remark string `json:"remark"`
|
||||
Date string `json:"date"`
|
||||
StartTime string `json:"start_time"`
|
||||
EndTime string `json:"end_time"`
|
||||
}
|
||||
|
||||
// TodoStatReq 待办统计请求
|
||||
type TodoStatReq struct {
|
||||
StartDate string `json:"start_date" binding:"required"` // 开始日期
|
||||
EndDate string `json:"end_date" binding:"required"` // 结束日期
|
||||
}
|
||||
|
||||
// -------------------------- 4. 接口实现 --------------------------
|
||||
|
||||
// GetTodoListForCalendar 获取日历月份的待办列表(内部方法)
|
||||
func (h *todoHandler) GetTodoListForCalendar(c *gin.Context, year, month int) []interface{} {
|
||||
userID := c.GetUint("userID")
|
||||
|
||||
// 计算月份范围
|
||||
startDate := fmt.Sprintf("%d-%02d-01", year, month)
|
||||
// 计算月末
|
||||
_, lastDay := utils.GetMonthLastDay(year, month)
|
||||
endDate := fmt.Sprintf("%d-%02d-%02d", year, month, lastDay)
|
||||
|
||||
var list []models.Todo
|
||||
h.db.Model(&models.Todo{}).
|
||||
Where("user_id = ? AND date BETWEEN ? AND ?", userID, startDate, endDate).
|
||||
Order("date ASC, id ASC").
|
||||
Find(&list)
|
||||
|
||||
// 转换结果
|
||||
result := make([]interface{}, len(list))
|
||||
for i, v := range list {
|
||||
if v.Done == 0 && v.Date < time.Now().Format("2006-01-02") {
|
||||
v.IsOverdue = 1
|
||||
} else {
|
||||
v.IsOverdue = 0
|
||||
}
|
||||
result[i] = v
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// AddTodo 添加待办
|
||||
func (h *todoHandler) AddTodo(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var todo models.Todo
|
||||
|
||||
if err := c.ShouldBindJSON(&todo); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 敏感词过滤
|
||||
todo.Title = utils.FilterSensitive(todo.Title)
|
||||
todo.Remark = utils.FilterSensitive(todo.Remark)
|
||||
|
||||
// 检查每日限制(最多100个待办/天)
|
||||
today := time.Now().Format("2006-01-02")
|
||||
var todayCount int64
|
||||
if err := h.db.Model(&models.Todo{}).Where("user_id = ? AND date = ? AND deleted_at IS NULL", userID, today).Count(&todayCount).Error; err != nil {
|
||||
utils.Fail(c, "系统错误")
|
||||
return
|
||||
}
|
||||
if todayCount >= 100 {
|
||||
utils.Fail(c, "今日待办数量已达上限(100个)")
|
||||
return
|
||||
}
|
||||
|
||||
todo.UserID = userID
|
||||
if err := h.db.Create(&todo).Error; err != nil {
|
||||
utils.Fail(c, "添加失败")
|
||||
return
|
||||
}
|
||||
|
||||
utils.Ok(c, nil)
|
||||
}
|
||||
|
||||
// TodoList 获取待办列表
|
||||
func (h *todoHandler) TodoList(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
|
||||
var req TodoListReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 构建查询
|
||||
db := h.db.Model(&models.Todo{}).Where("user_id = ?", userID)
|
||||
|
||||
// 搜索
|
||||
if req.Search != "" {
|
||||
db = db.Where("title LIKE ? OR remark LIKE ?", "%"+req.Search+"%", "%"+req.Search+"%")
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
switch req.Status {
|
||||
case "undone":
|
||||
db = db.Where("done = 0")
|
||||
case "done":
|
||||
db = db.Where("done = 1")
|
||||
}
|
||||
|
||||
// 日期
|
||||
now := time.Now()
|
||||
today := now.Format("2006-01-02")
|
||||
|
||||
startDate := req.StartDate
|
||||
endDate := req.EndDate
|
||||
if startDate != "" && endDate != "" {
|
||||
db = db.Where("date BETWEEN ? AND ?", startDate, endDate)
|
||||
} else if startDate != "" {
|
||||
db = db.Where("date >= ?", startDate)
|
||||
} else if endDate != "" {
|
||||
db = db.Where("date <= ?", endDate)
|
||||
} else if req.Date != "" {
|
||||
db = db.Where("date = ?", req.Date)
|
||||
} else {
|
||||
db = db.Where("date = ?", today)
|
||||
}
|
||||
|
||||
// 排序
|
||||
db = db.Order("date ASC, id ASC")
|
||||
|
||||
// 查询
|
||||
var list []models.Todo
|
||||
if err := db.Find(&list).Error; err != nil {
|
||||
utils.Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 逾期状态
|
||||
for i := range list {
|
||||
if list[i].Done == 0 && list[i].Date < today {
|
||||
list[i].IsOverdue = 1
|
||||
} else {
|
||||
list[i].IsOverdue = 0
|
||||
}
|
||||
}
|
||||
|
||||
utils.Ok(c, list)
|
||||
}
|
||||
|
||||
// DeleteTodo 删除待办
|
||||
func (h *todoHandler) DeleteTodo(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var req DeleteTodoReq
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 查询待办信息(用于扣除积分)
|
||||
var todo models.Todo
|
||||
err := h.db.Where("id = ? AND user_id = ?", req.ID, userID).First(&todo).Error
|
||||
if err != nil {
|
||||
utils.Fail(c, "待办不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 删除待办
|
||||
err = h.db.Where("id = ? AND user_id = ?", req.ID, userID).Delete(&models.Todo{}).Error
|
||||
if err != nil {
|
||||
utils.Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 扣除今天添加时获得的积分
|
||||
growthHandler := NewGrowthHandler()
|
||||
today := time.Now().Format("2006-01-02")
|
||||
deducted := growthHandler.DeductExpInternal(userID, "task", fmt.Sprintf("%d", req.ID), today)
|
||||
|
||||
utils.Ok(c, gin.H{
|
||||
"deducted_exp": deducted,
|
||||
})
|
||||
}
|
||||
|
||||
// DoneTodo 完成/取消完成待办
|
||||
func (h *todoHandler) DoneTodo(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var req DoneTodoReq
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
if req.Status != 0 && req.Status != 1 {
|
||||
utils.Fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 先查询当前状态
|
||||
var todo models.Todo
|
||||
err := h.db.Where("id = ? AND user_id = ?", req.ID, userID).First(&todo).Error
|
||||
if err != nil {
|
||||
utils.Fail(c, "待办不存在")
|
||||
return
|
||||
}
|
||||
|
||||
oldDone := todo.Done
|
||||
|
||||
// 更新状态
|
||||
err = h.db.Model(&models.Todo{}).
|
||||
Where("id = ? AND user_id = ?", req.ID, userID).
|
||||
Update("done", req.Status).Error
|
||||
if err != nil {
|
||||
utils.Fail(c, "操作失败")
|
||||
return
|
||||
}
|
||||
|
||||
var result any
|
||||
debugMode := os.Getenv("DEBUG_MODE")
|
||||
// 只有从未完成变成完成(0→1)的时候才加积分,并且这个待办之前没有加过积分
|
||||
if oldDone == 0 && req.Status == 1 {
|
||||
// 查询是否已经给这个待办加过积分
|
||||
var count int64
|
||||
h.db.Model(&models.ExpChangeLog{}).
|
||||
Where("user_id = ? AND source = ? AND source_id = ?", userID, "task", fmt.Sprintf("%d", req.ID)).
|
||||
Count(&count)
|
||||
|
||||
// 只有之前没加过积分才加
|
||||
if count == 0 {
|
||||
// ==================== 调试模式 ====================
|
||||
if debugMode == "true" {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
levelUp := rand.Intn(10) < 3 // 30%概率升级
|
||||
hasAchievement := rand.Intn(10) < 4 // 40%概率获得成就
|
||||
result = MockGrowthRewards(levelUp, hasAchievement)
|
||||
} else {
|
||||
// 生产模式:真实调用
|
||||
growthHandler := NewGrowthHandler()
|
||||
result = growthHandler.AddExpInternal(userID, "task", fmt.Sprintf("%d", req.ID), nil)
|
||||
}
|
||||
// ==================================================
|
||||
}
|
||||
}
|
||||
|
||||
utils.Ok(c, gin.H{
|
||||
"success": true,
|
||||
"growth": result,
|
||||
"debugMode": debugMode,
|
||||
"isDebug": debugMode == "true",
|
||||
})
|
||||
}
|
||||
|
||||
// EditTodo 编辑待办
|
||||
func (h *todoHandler) EditTodo(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var req EditTodoReq
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 敏感词过滤
|
||||
filteredTitle := utils.FilterSensitive(req.Title)
|
||||
filteredRemark := utils.FilterSensitive(req.Remark)
|
||||
|
||||
data := map[string]any{
|
||||
"title": filteredTitle,
|
||||
"remark": filteredRemark,
|
||||
"category": req.Category,
|
||||
"priority": req.Priority,
|
||||
"date": req.Date,
|
||||
"start_time": req.StartTime,
|
||||
"end_time": req.EndTime,
|
||||
}
|
||||
|
||||
err := h.db.Model(&models.Todo{}).
|
||||
Where("id = ? AND user_id = ?", req.ID, userID).
|
||||
Updates(data).Error
|
||||
if err != nil {
|
||||
utils.Fail(c, "编辑失败")
|
||||
return
|
||||
}
|
||||
|
||||
utils.Ok(c, nil)
|
||||
}
|
||||
|
||||
// TodoStat 待办统计
|
||||
func (h *todoHandler) TodoStat(c *gin.Context) {
|
||||
userID := c.GetUint("userID")
|
||||
var req TodoStatReq
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
utils.Fail(c, "参数解析失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 总体统计
|
||||
var total, done, undone int64
|
||||
h.db.Model(&models.Todo{}).
|
||||
Where("user_id = ? AND date BETWEEN ? AND ?", userID, req.StartDate, req.EndDate).
|
||||
Count(&total)
|
||||
h.db.Model(&models.Todo{}).
|
||||
Where("user_id = ? AND date BETWEEN ? AND ? AND done = 1", userID, req.StartDate, req.EndDate).
|
||||
Count(&done)
|
||||
undone = total - done
|
||||
|
||||
// 完成率
|
||||
completionRate := 0.0
|
||||
if total > 0 {
|
||||
completionRate = float64(done) / float64(total) * 100
|
||||
}
|
||||
|
||||
// 按分类统计
|
||||
type CategoryStat struct {
|
||||
Total int64 `json:"total"`
|
||||
Done int64 `json:"done"`
|
||||
}
|
||||
byCategory := make(map[string]CategoryStat)
|
||||
|
||||
var categoryResults []struct {
|
||||
Category string
|
||||
Total int64
|
||||
Done int64
|
||||
}
|
||||
h.db.Model(&models.Todo{}).
|
||||
Select("category, COUNT(*) as total, SUM(CASE WHEN done = 1 THEN 1 ELSE 0 END) as done").
|
||||
Where("user_id = ? AND date BETWEEN ? AND ?", userID, req.StartDate, req.EndDate).
|
||||
Group("category").
|
||||
Scan(&categoryResults)
|
||||
|
||||
for _, r := range categoryResults {
|
||||
byCategory[r.Category] = CategoryStat{
|
||||
Total: r.Total,
|
||||
Done: r.Done,
|
||||
}
|
||||
}
|
||||
|
||||
// 按优先级统计
|
||||
type PriorityStat struct {
|
||||
Total int64 `json:"total"`
|
||||
Done int64 `json:"done"`
|
||||
}
|
||||
byPriority := make(map[string]PriorityStat)
|
||||
|
||||
var priorityResults []struct {
|
||||
Priority int
|
||||
Total int64
|
||||
Done int64
|
||||
}
|
||||
h.db.Model(&models.Todo{}).
|
||||
Select("priority, COUNT(*) as total, SUM(CASE WHEN done = 1 THEN 1 ELSE 0 END) as done").
|
||||
Where("user_id = ? AND date BETWEEN ? AND ?", userID, req.StartDate, req.EndDate).
|
||||
Group("priority").
|
||||
Scan(&priorityResults)
|
||||
|
||||
for _, r := range priorityResults {
|
||||
key := fmt.Sprintf("%d", r.Priority)
|
||||
byPriority[key] = PriorityStat{
|
||||
Total: r.Total,
|
||||
Done: r.Done,
|
||||
}
|
||||
}
|
||||
|
||||
utils.Ok(c, gin.H{
|
||||
"total": total,
|
||||
"done": done,
|
||||
"undone": undone,
|
||||
"completion_rate": completionRate,
|
||||
"by_category": byCategory,
|
||||
"by_priority": byPriority,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user