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
+37
View File
@@ -0,0 +1,37 @@
// 配置加载:区分dev/prod环境
package core
import (
"flag"
"simple-memo/global"
"github.com/spf13/viper"
)
// InitConfig 读取命令行参数 -env dev/prod
func InitConfig() {
// 读取启动参数
env := flag.String("env", "dev", "运行环境:dev 或 prod")
flag.Parse()
// 1. 获取项目根目录的绝对路径(基于当前执行文件的位置)
//exePath, err := os.Executable()
//if err != nil {
// panic(fmt.Sprintf("获取执行文件路径失败: %v", err))
//}
//projectRoot := filepath.Dir(exePath) // 执行文件在根目录,所以Dir就是根目录
// 2. 用绝对路径加载配置文件
v := viper.New()
v.SetConfigName("config-" + *env) // config-dev.yaml
v.SetConfigType("yaml")
v.AddConfigPath("../config") // 配置文件目录
//viper.AddConfigPath(filepath.Join(projectRoot, "config")) // 绝对路径:根目录/config
// 读取配置
if err := v.ReadInConfig(); err != nil {
panic("配置文件读取失败:" + err.Error())
}
global.VP = v // 赋值给全局
}
+58
View File
@@ -0,0 +1,58 @@
package core
import (
"time"
"simple-memo/global"
"simple-memo/models"
"go.uber.org/zap"
)
func InitCron() {
go func() {
for {
now := time.Now()
next := getNextRunTime(10, 0)
duration := next.Sub(now)
time.Sleep(duration)
checkPerfectDay()
}
}()
}
func getNextRunTime(hour, minute int) time.Time {
now := time.Now()
today := time.Date(now.Year(), now.Month(), now.Day(), hour, minute, 0, 0, now.Location())
if now.Before(today) {
return today
}
return today.AddDate(0, 0, 1)
}
func checkPerfectDay() {
yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
var progressList []models.DailyProgress
err := global.DB.Where("record_date = ?", yesterday).Find(&progressList).Error
if err != nil {
global.Logger.Error("查询每日进度失败", zap.Error(err))
return
}
for _, progress := range progressList {
if progress.TotalTasks > 0 && progress.TaskCount >= progress.TotalTasks && !progress.PerfectDay {
err := global.DB.Model(&models.DailyProgress{}).
Where("id = ?", progress.ID).
Update("perfect_day", true).Error
if err != nil {
global.Logger.Error("更新PerfectDay失败", zap.Error(err))
}
}
}
global.Logger.Info("PerfectDay check completed", zap.String("date", yesterday))
}
+25
View File
@@ -0,0 +1,25 @@
// 日志模块:zap 高性能日志
package core
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"simple-memo/global"
)
func InitLogger() {
logPath := global.VP.GetString("log.path")
file, _ := os.Create(logPath)
// 同时输出到文件 + 控制台
writeSyncer := zapcore.NewMultiWriteSyncer(zapcore.AddSync(file), zapcore.AddSync(os.Stdout))
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.TimeKey = "time"
encoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
// 日志级别
core := zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), writeSyncer, zap.InfoLevel)
global.Logger = zap.New(core, zap.AddCaller())
}
+50
View File
@@ -0,0 +1,50 @@
// MySQL初始化 + 表前缀sm_(已修复GORM兼容报错)
package core
import (
"simple-memo/global"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
)
// InitMysql 初始化MySQL数据库连接
func InitMysql() {
// 从配置文件读取数据库信息
host := global.VP.GetString("mysql.host")
port := global.VP.GetString("mysql.port")
dbname := global.VP.GetString("mysql.dbname")
username := global.VP.GetString("mysql.username")
password := global.VP.GetString("mysql.password")
// MySQL 连接字符串
dsn := username + ":" + password + "@tcp(" + host + ":" + port + ")/" + dbname + "?charset=utf8mb4&parseTime=True&loc=Local"
// 打开数据库连接,启用表前缀sm_
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
// ========== 修复:正确设置表前缀,无报错 ==========
NamingStrategy: schema.NamingStrategy{
TablePrefix: "sm_", // 所有表统一前缀:sm_
SingularTable: true, // 禁用表名复数(User → sm_user
},
})
if err != nil {
panic("MySQL连接失败:" + err.Error())
}
// 设置数据库连接池,支撑高并发
sqlDB, err := db.DB()
if err != nil {
panic("获取数据库连接失败:" + err.Error())
}
sqlDB.SetMaxOpenConns(100) // 最大打开连接数
sqlDB.SetMaxIdleConns(20) // 最大空闲连接数
sqlDB.SetConnMaxLifetime(30 * time.Minute) // 连接最长存活时间
// 赋值给全局DB变量
global.DB = db
}
+24
View File
@@ -0,0 +1,24 @@
// Redis初始化
package core
import (
"context"
"github.com/redis/go-redis/v9"
"simple-memo/global"
)
func InitRedis() {
client := redis.NewClient(&redis.Options{
Addr: global.VP.GetString("redis.host") + ":" + global.VP.GetString("redis.port"),
Password: global.VP.GetString("redis.password"),
DB: global.VP.GetInt("redis.db"),
})
// 测试连接
_, err := client.Ping(context.Background()).Result()
if err != nil {
panic("Redis连接失败:" + err.Error())
}
global.Redis = client
}
+28
View File
@@ -0,0 +1,28 @@
// core/router.go
// Gin引擎初始化 - 只保留核心配置
package core
import (
"simple-memo/middleware"
"simple-memo/routes"
"github.com/gin-gonic/gin"
)
// InitRouter 初始化Gin引擎并注册所有路由
func InitRouter() *gin.Engine {
// 使用 gin.New() 手动控制中间件,避免 gin.Default() 自带的 Logger
r := gin.New()
// 全局中间件
r.Use(gin.Recovery()) // 恢复中间件(捕获 panic
r.Use(middleware.RequestLog()) // 请求日志中间件(记录 request-id / route / client-ip 等)
// 设置信任的代理(开发环境设置为 nil,生产环境应设置具体的代理地址)
_ = r.SetTrustedProxies(nil)
// 注册所有路由(静态资源、页面、API)
routes.RegisterRoutes(r)
return r
}