docs(website): 添加简记memo产品原型和UI设计规范文档

- 新增「简记memo」一体化小程序产品原型设计文档
- 新增简记memo完整版UI视觉设计规范和界面细节
- 添加IDEA项目配置文件.gitignore
- 创建404页面HTML文件,包含响应式布局和错误提示
- 添加关于页面HTML文件,展示品牌介绍和团队信息
- 实现AES加解密工具函数,支持请求体加密
- 添加用户协议页面基础框架
This commit is contained in:
sunct
2026-07-31 14:12:32 +08:00
commit 3c3bf53ae4
115 changed files with 21304 additions and 0 deletions
+436
View File
@@ -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,
})
}