优化页面、修复BUG

This commit is contained in:
李琦
2026-06-24 16:50:04 +08:00
parent 88ba9be318
commit 3f653bc336
36 changed files with 1672 additions and 2401 deletions

View File

@@ -1,16 +0,0 @@
CREATE TABLE IF NOT EXISTS `about_profiles` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`avatar` VARCHAR(255) DEFAULT '',
`location` VARCHAR(255) DEFAULT '',
`bio` TEXT,
`email` VARCHAR(255) DEFAULT '',
`wechat` VARCHAR(255) DEFAULT '',
`tech_stack` TEXT COMMENT 'JSON string or comma separated list',
`is_primary` BOOLEAN DEFAULT FALSE,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `about_profiles` (`name`, `avatar`, `location`, `bio`, `email`, `wechat`, `tech_stack`, `is_primary`) VALUES
('年糕崽崽', 'https://api.dicebear.com/7.x/avataaars/svg?seed=NianGao&backgroundColor=b6e3f4', '中国 · 浙江杭州', '嗨!我是年糕崽崽,一名热衷于探索数字边界的前端架构师。我的职业生涯始于对像素级完美的执念。从早期的 jQuery 插件开发到如今的 WebGL 3D 场景构建,我始终认为:<strong>技术是骨架,艺术是灵魂。</strong><br><br>目前,我专注于高性能 B 端应用的体验升级以及探索生成式艺术Generative Art在网页中的应用。闲暇之余我喜欢在杭州的西湖边散步或者捣腾我的客制化机械键盘。', 'hello@niangao.dev', 'Niangao_Dev', '["Vue 3", "React", "TypeScript", "Three.js", "Golang", "Tailwind CSS", "Rust", "Wails"]', TRUE);

View File

@@ -1,15 +0,0 @@
-- 清理脚本:删除可能有问题的表
-- 在执行 nl_blog.sql 之前先执行此脚本
SET FOREIGN_KEY_CHECKS = 0;
-- 删除 operation_logs 表(如果存在)
DROP TABLE IF EXISTS `operation_logs`;
-- 删除 tags 表(如果存在)
DROP TABLE IF EXISTS `tags`;
-- 删除 post_tags 关联表(如果存在,因为它可能引用 tags
DROP TABLE IF EXISTS `post_tags`;
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -48,6 +48,7 @@ require (
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
go.uber.org/mock v0.5.0 // indirect

View File

@@ -116,6 +116,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -173,10 +175,12 @@ google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7I
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -139,57 +139,63 @@ func GetPost(c *gin.Context) {
// 构建响应,包含内容
response := repositories.BuildPostResponse(post, true)
// 记录用户访问日志 (异步执行,不阻塞响应)
go func() {
// 添加 panic recover 保护
defer func() {
if r := recover(); r != nil {
log.Printf("Panic in user access log goroutine for post %d: %v", post.ID, r)
}
}()
visitorKey := utils.GetOrSetVisitorID(c)
// 获取客户端IP
ip := c.ClientIP()
var userID uint = 0
if uid, exists := c.Get("userID"); exists {
userID = uid.(uint)
}
// 获取归属地,使用 recover 保护
var location string
func() {
alreadyVisited, err := repositories.HasVisitedThisHour(post.ID, userID, visitorKey)
if err != nil {
log.Printf("Failed to check visit dedup for post %d: %v", post.ID, err)
}
if !alreadyVisited {
if err := repositories.IncrementReadCount(post.ID); err != nil {
log.Printf("Failed to increment read count for post %d: %v", post.ID, err)
} else {
response.ReadCount++
}
// 记录用户访问日志 (异步执行,不阻塞响应)
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("Panic in GetRegion for IP %s (post %d): %v", ip, post.ID, r)
location = "Unknown"
log.Printf("Panic in user access log goroutine for post %d: %v", post.ID, r)
}
}()
location = utils.GetRegion(ip)
ip := c.ClientIP()
var location string
func() {
defer func() {
if r := recover(); r != nil {
log.Printf("Panic in GetRegion for IP %s (post %d): %v", ip, post.ID, r)
location = "Unknown"
}
}()
location = utils.GetRegion(ip)
}()
if location == "" {
location = "Unknown"
}
logEntry := &models.UserAccessLog{
UserID: userID,
UserIP: ip,
UserLocation: location,
ArticleID: post.ID,
VisitorKey: visitorKey,
}
if err := repositories.CreateUserAccessLog(logEntry); err != nil {
log.Printf("Failed to create user access log for PostID=%d, IP=%s: %v", post.ID, ip, err)
}
}()
// 确保 location 不为空,如果为空则设置为 "Unknown"
if location == "" {
location = "Unknown"
}
// 记录调试信息
log.Printf("Creating user access log: PostID=%d, IP=%s, Location=%s", post.ID, ip, location)
// 获取用户ID (如果已登录)
var userID uint = 0
if uid, exists := c.Get("userID"); exists {
userID = uid.(uint)
}
logEntry := &models.UserAccessLog{
UserID: userID,
UserIP: ip,
UserLocation: location,
ArticleID: post.ID,
}
if err := repositories.CreateUserAccessLog(logEntry); err != nil {
log.Printf("Failed to create user access log for PostID=%d, IP=%s, Location=%s: %v", post.ID, ip, location, err)
} else {
log.Printf("Successfully created user access log: PostID=%d, IP=%s, Location=%s", post.ID, ip, location)
}
}()
}
utils.Success(c, response)
}
@@ -412,7 +418,62 @@ func AdminGetPostHistoryByVersion(c *gin.Context) {
return
}
utils.Success(c, repositories.BuildPostHistoryResponse(history))
utils.Success(c, repositories.BuildPostHistoryResponse(history, true))
}
// AdminGetPostHistoryDiff 对比两个历史版本
func AdminGetPostHistoryDiff(c *gin.Context) {
postIDStr := c.Param("id")
var postID uint
if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
utils.Error(c, 400, "Invalid post ID")
return
}
fromVersion, err := strconv.ParseUint(c.Query("from"), 10, 32)
if err != nil {
utils.Error(c, 400, "Invalid from version")
return
}
toVersion, err := strconv.ParseUint(c.Query("to"), 10, 32)
if err != nil {
utils.Error(c, 400, "Invalid to version")
return
}
diff, err := repositories.GetPostHistoryDiff(postID, uint(fromVersion), uint(toVersion))
if err != nil {
utils.Error(c, 404, err.Error())
return
}
utils.Success(c, diff)
}
// AdminRestorePostHistory 恢复指定历史版本
func AdminRestorePostHistory(c *gin.Context) {
postIDStr := c.Param("id")
var postID uint
if _, err := fmt.Sscanf(postIDStr, "%d", &postID); err != nil {
utils.Error(c, 400, "Invalid post ID")
return
}
versionStr := c.Param("version")
versionUint, err := strconv.ParseUint(versionStr, 10, 32)
if err != nil {
utils.Error(c, 400, "Invalid version")
return
}
userID, _ := c.Get("userID")
post, err := repositories.RestorePostFromHistory(postID, uint(versionUint), userID.(uint))
if err != nil {
utils.ServerError(c, err)
return
}
utils.Success(c, repositories.BuildPostResponse(post, true))
}
// GetPostsByTagID 根据标签ID获取文章

View File

@@ -47,6 +47,23 @@ func AdminDeleteSearchLog(c *gin.Context) {
utils.SuccessWithMsg(c, "Search log deleted successfully", nil)
}
// GetHotSearches 获取热门搜索关键词(公开)
func GetHotSearches(c *gin.Context) {
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
days, _ := strconv.Atoi(c.DefaultQuery("days", "30"))
keywords, err := repositories.GetHotKeywords(limit, days)
if err != nil {
utils.ServerError(c, err)
return
}
if keywords == nil {
keywords = []repositories.HotKeyword{}
}
utils.Success(c, keywords)
}
// LogSearch 记录搜索(异步,不阻塞)
func LogSearch(keyword, searchType, userIP, userLocation string) {
go func() {

View File

@@ -56,6 +56,7 @@ func main() {
// 博客路由
api.GET("/posts", handlers.GetPosts)
api.GET("/posts/:id", handlers.GetPost)
api.GET("/search/hot", handlers.GetHotSearches)
api.GET("/posts/:id/recommendations", handlers.GetRecommendedPosts)
// 分类路由
@@ -168,6 +169,8 @@ func main() {
// 文章历史记录
authAdmin.GET("/posts/:id/history", middleware.PermissionMiddleware("posts", "read"), handlers.AdminGetPostHistory)
authAdmin.GET("/posts/:id/history/diff", middleware.PermissionMiddleware("posts", "read"), handlers.AdminGetPostHistoryDiff)
authAdmin.POST("/posts/:id/history/:version/restore", middleware.PermissionMiddleware("posts", "update"), handlers.AdminRestorePostHistory)
authAdmin.GET("/posts/:id/history/:version", middleware.PermissionMiddleware("posts", "read"), handlers.AdminGetPostHistoryByVersion)
// 操作日志管理

View File

@@ -2,7 +2,6 @@ package middleware
import (
"errors"
"net/http"
"os"
"strings"
"time"
@@ -10,6 +9,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"github.com/niangaodev/art-code/repositories"
"github.com/niangaodev/art-code/utils"
)
// JWT密钥
@@ -96,7 +96,7 @@ func AuthMiddleware() gin.HandlerFunc {
// 从请求头中获取令牌
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is required"})
utils.Error(c, 401, "登录已过期,请重新登录")
c.Abort()
return
}
@@ -104,7 +104,7 @@ func AuthMiddleware() gin.HandlerFunc {
// 检查令牌格式
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header format must be Bearer {token}"})
utils.Error(c, 401, "登录已过期,请重新登录")
c.Abort()
return
}
@@ -112,7 +112,7 @@ func AuthMiddleware() gin.HandlerFunc {
// 解析令牌
claims, err := ParseToken(parts[1])
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
utils.Error(c, 401, "登录已过期,请重新登录")
c.Abort()
return
}
@@ -133,7 +133,7 @@ func RoleMiddleware(roles ...string) gin.HandlerFunc {
// 从上下文中获取用户角色
role, exists := c.Get("role")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
utils.Error(c, 401, "未授权,请重新登录")
c.Abort()
return
}
@@ -148,7 +148,7 @@ func RoleMiddleware(roles ...string) gin.HandlerFunc {
}
if !allowed {
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
utils.Error(c, 403, "权限不足")
c.Abort()
return
}
@@ -166,13 +166,13 @@ func PermissionMiddleware(resource, action string) gin.HandlerFunc {
// 尝试从role name获取
roleName, exists := c.Get("role")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
utils.Error(c, 401, "未授权,请重新登录")
c.Abort()
return
}
role, err := repositories.GetRoleByName(roleName.(string))
if err != nil || role == nil {
c.JSON(http.StatusForbidden, gin.H{"error": "Role not found"})
utils.Error(c, 403, "角色不存在")
c.Abort()
return
}
@@ -184,7 +184,7 @@ func PermissionMiddleware(resource, action string) gin.HandlerFunc {
// 获取该角色的所有权限
permissions, err := repositories.GetPermissionsByRoleID(roleID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check permissions"})
utils.Error(c, 500, "权限校验失败")
c.Abort()
return
}
@@ -199,7 +199,7 @@ func PermissionMiddleware(resource, action string) gin.HandlerFunc {
}
if !allowed {
c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
utils.Error(c, 403, "权限不足")
c.Abort()
return
}

View File

@@ -1,6 +1,7 @@
package models
import (
"encoding/json"
"time"
"gorm.io/gorm"
@@ -76,6 +77,8 @@ type PostHistory struct {
Version int `json:"version" gorm:"column:version"`
Title string `json:"title" gorm:"column:title"`
CategoryID uint `json:"categoryId" gorm:"column:category_id"`
ColumnID *uint `json:"columnId,omitempty" gorm:"column:column_id"`
TagIDs string `json:"tagIds,omitempty" gorm:"column:tag_ids;type:json"`
Excerpt string `json:"excerpt" gorm:"column:excerpt"`
Content string `json:"content" gorm:"column:content;type:text"`
IsPublished int `json:"isPublished" gorm:"column:is_published"`
@@ -84,6 +87,18 @@ type PostHistory struct {
CreatedAt int64 `json:"createdAt" gorm:"column:created_at"`
}
// GetTagIDList parses stored tag_ids JSON into a slice of uint IDs.
func (ph *PostHistory) GetTagIDList() []uint {
if ph.TagIDs == "" {
return nil
}
var ids []uint
if err := json.Unmarshal([]byte(ph.TagIDs), &ids); err != nil {
return nil
}
return ids
}
// TableName 指定表名
func (PostHistory) TableName() string {
return "post_history"
@@ -108,10 +123,29 @@ type PostHistoryResponse struct {
Version int `json:"version"`
Title string `json:"title"`
CategoryID uint `json:"categoryId"`
CategoryName string `json:"categoryName"`
CategoryName string `json:"categoryName,omitempty"`
ColumnID *uint `json:"columnId,omitempty"`
TagIDs []uint `json:"tagIds,omitempty"`
Excerpt string `json:"excerpt,omitempty"`
Content string `json:"content,omitempty"`
Date string `json:"date"`
IsPublished int `json:"isPublished"`
ModifiedBy uint `json:"modifiedBy"`
ModifiedAt string `json:"modifiedAt"`
CreatedAt string `json:"createdAt"`
}
// PostHistoryFieldDiff 字段对比结果
type PostHistoryFieldDiff struct {
From string `json:"from"`
To string `json:"to"`
Changed bool `json:"changed"`
Diff string `json:"diff,omitempty"`
}
// PostHistoryDiffResponse 版本对比响应
type PostHistoryDiffResponse struct {
FromVersion int `json:"fromVersion"`
ToVersion int `json:"toVersion"`
Fields map[string]PostHistoryFieldDiff `json:"fields"`
}

View File

@@ -9,11 +9,12 @@ import (
// UserAccessLog 用户访问日志模型
type UserAccessLog struct {
ID uint `json:"id" gorm:"primaryKey;column:id"`
UserID uint `json:"user_id" gorm:"column:user_id;index"` // 用户ID未登录用户为0
UserIP string `json:"user_ip" gorm:"column:user_ip;index"` // 用户IP地址
UserLocation string `json:"user_location" gorm:"column:user_location"` // 用户归属地
ArticleID uint `json:"article_id" gorm:"column:article_id;index"` // 访问的文章ID
AccessTime int64 `json:"access_time" gorm:"column:access_time"` // 访问时间
UserID uint `json:"user_id" gorm:"column:user_id;index"` // 用户ID未登录用户为0
UserIP string `json:"user_ip" gorm:"column:user_ip;index"` // 用户IP地址
UserLocation string `json:"user_location" gorm:"column:user_location"` // 用户归属地
ArticleID uint `json:"article_id" gorm:"column:article_id;index"` // 访问的文章ID
VisitorKey string `json:"visitor_key" gorm:"column:visitor_key;size:64"` // 匿名访客标识
AccessTime int64 `json:"access_time" gorm:"column:access_time"` // 访问时间
DeletedAt int64 `json:"deleted_at" gorm:"column:deleted_at;default:0"`
}

View File

@@ -1,17 +1,17 @@
/*
Navicat Premium Dump SQL
Source Server : 开发环境-本地
Source Server : 我的mysql8
Source Server Type : MySQL
Source Server Version : 80407 (8.4.7)
Source Host : localhost:3306
Source Host : 101.43.12.11:3306
Source Schema : nl_blog
Target Server Type : MySQL
Target Server Version : 80407 (8.4.7)
File Encoding : 65001
Date: 20/01/2026 13:33:04
Date: 24/06/2026 16:07:16
*/
SET NAMES utf8mb4;
@@ -36,12 +36,7 @@ CREATE TABLE `about_profiles` (
`updated_at` bigint NOT NULL DEFAULT 0,
`deleted_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of about_profiles
-- ----------------------------
INSERT INTO `about_profiles` VALUES (3, '年糕崽崽', 'https://api.dicebear.com/7.x/avataaars/svg?seed=NianGao&backgroundColor=b6e3f4', '中国 · 浙江杭州', '嗨!我是年糕崽崽,一名热衷于探索数字边界的前端架构师。我的职业生涯始于对像素级完美的执念。从早期的 jQuery 插件开发到如今的 WebGL 3D 场景构建,我始终认为:<strong>技术是骨架,艺术是灵魂。</strong><br><br>目前,我专注于高性能 B 端应用的体验升级以及探索生成式艺术Generative Art在网页中的应用。闲暇之余我喜欢在杭州的西湖边散步或者捣腾我的客制化机械键盘。', 'liqiworker@gmail.com', 'ngzz_0218', '[\\\"Vue 3\\\",\\\"React\\\",\\\"TypeScript\\\",\\\"Three.js\\\",\\\"Golang\\\",\\\"Tailwind CSS\\\",\\\"Rust\\\",\\\"Wails\\\"]', '[{\\\"year\\\":\\\"2024 - 至今\\\",\\\"role\\\":\\\"技术负责人\\\",\\\"company\\\":\\\"某医疗平台公司\\\"},{\\\"year\\\":\\\"2020 - 2024\\\",\\\"role\\\":\\\"PHP开发工程师\\\",\\\"company\\\":\\\"某电商公司\\\"}]', 0, 0, 0, 0);
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for access_logs
@@ -55,16 +50,13 @@ CREATE TABLE `access_logs` (
`method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'HTTP方法',
`status_code` int UNSIGNED NOT NULL COMMENT 'HTTP状态码',
`response_time` int UNSIGNED NOT NULL COMMENT '响应时间(毫秒)',
`region` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'IP归属地',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_path`(`path` ASC) USING BTREE COMMENT '按访问路径查询索引',
INDEX `idx_status_code`(`status_code` ASC) USING BTREE COMMENT '按状态码查询索引'
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '访问日志表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of access_logs
-- ----------------------------
) ENGINE = InnoDB AUTO_INCREMENT = 25100 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '访问日志表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for attachment_categories
@@ -80,12 +72,7 @@ CREATE TABLE `attachment_categories` (
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_sort_order`(`sort_order` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '附件分类表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of attachment_categories
-- ----------------------------
INSERT INTO `attachment_categories` VALUES (1, '文章附件', '在博客中上传的附件', 0, 0, 1768812104, 1768812104);
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '附件分类表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for attachments
@@ -111,23 +98,7 @@ CREATE TABLE `attachments` (
INDEX `idx_storage_type`(`storage_type` ASC) USING BTREE,
INDEX `idx_file_type`(`file_type` ASC) USING BTREE,
INDEX `idx_created_at`(`created_at` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '附件表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of attachments
-- ----------------------------
INSERT INTO `attachments` VALUES (1, NULL, 'cc_upload_xTUFrRBdm7zWyjQs693a39ea93969.jpg', 'cc_upload_xTUFrRBdm7zWyjQs693a39ea93969.jpg', 'uploads\\2026\\01\\19\\cc_upload_xTUFrRBdm7zWyjQs693a39ea93969_1768807852.jpg', '/uploads/2026/01/19/cc_upload_xTUFrRBdm7zWyjQs693a39ea93969_1768807852.jpg', 40648, 'image', 'image/jpeg', 'local', NULL, 0, 1768807852, 1768807852);
INSERT INTO `attachments` VALUES (2, 1, 'cc_upload_opYFPBlXkfyOSrH269152a21424c4.jpg', 'cc_upload_opYFPBlXkfyOSrH269152a21424c4.jpg', 'uploads\\2026\\01\\19\\cc_upload_opYFPBlXkfyOSrH269152a21424c4_1768811098.jpg', '/uploads/2026/01/19/cc_upload_opYFPBlXkfyOSrH269152a21424c4_1768811098.jpg', 12911, 'image', 'image/jpeg', 'local', NULL, 0, 1768811098, 1768811098);
INSERT INTO `attachments` VALUES (3, 1, 'cc_upload_xTUFrRBdm7zWyjQs693a39ea93969.jpg', 'cc_upload_xTUFrRBdm7zWyjQs693a39ea93969.jpg', 'uploads\\2026\\01\\19\\cc_upload_xTUFrRBdm7zWyjQs693a39ea93969_1768824015.jpg', '/uploads/2026/01/19/cc_upload_xTUFrRBdm7zWyjQs693a39ea93969_1768824015.jpg', 40648, 'image', 'image/jpeg', 'local', NULL, 0, 1768824015, 1768824015);
INSERT INTO `attachments` VALUES (4, 1, 'cc_upload_0NeLXeWx8AmBi7Mx693ce05fea320.jpg', 'cc_upload_0NeLXeWx8AmBi7Mx693ce05fea320.jpg', 'uploads\\2026\\01\\19\\cc_upload_0NeLXeWx8AmBi7Mx693ce05fea320_1768824028.jpg', '/uploads/2026/01/19/cc_upload_0NeLXeWx8AmBi7Mx693ce05fea320_1768824028.jpg', 19875, 'image', 'image/jpeg', 'local', NULL, 0, 1768824029, 1768824029);
INSERT INTO `attachments` VALUES (5, 1, 'cc_upload_62sXZ0HMvINRUYDO693d6c27c7045.jpg', 'cc_upload_62sXZ0HMvINRUYDO693d6c27c7045.jpg', 'uploads\\2026\\01\\19\\cc_upload_62sXZ0HMvINRUYDO693d6c27c7045_1768824028.jpg', '/uploads/2026/01/19/cc_upload_62sXZ0HMvINRUYDO693d6c27c7045_1768824028.jpg', 305101, 'image', 'image/jpeg', 'local', NULL, 0, 1768824029, 1768824029);
INSERT INTO `attachments` VALUES (6, 1, 'cc_upload_9AqkM4FiiRhzl1On693d6c27ce6eb.jpg', 'cc_upload_9AqkM4FiiRhzl1On693d6c27ce6eb.jpg', 'uploads\\2026\\01\\19\\cc_upload_9AqkM4FiiRhzl1On693d6c27ce6eb_1768824028.jpg', '/uploads/2026/01/19/cc_upload_9AqkM4FiiRhzl1On693d6c27ce6eb_1768824028.jpg', 237431, 'image', 'image/jpeg', 'local', NULL, 0, 1768824029, 1768824029);
INSERT INTO `attachments` VALUES (7, 1, 'cc_upload_gXTnJhQ2DSbBr3Ql693d6c27c0b62.jpg', 'cc_upload_gXTnJhQ2DSbBr3Ql693d6c27c0b62.jpg', 'uploads\\2026\\01\\19\\cc_upload_gXTnJhQ2DSbBr3Ql693d6c27c0b62_1768824028.jpg', '/uploads/2026/01/19/cc_upload_gXTnJhQ2DSbBr3Ql693d6c27c0b62_1768824028.jpg', 210572, 'image', 'image/jpeg', 'local', NULL, 0, 1768824029, 1768824029);
INSERT INTO `attachments` VALUES (8, 1, 'cc_upload_etVM7GrPwl33KYzn693cc3016abdf.png', 'cc_upload_etVM7GrPwl33KYzn693cc3016abdf.png', 'uploads\\2026\\01\\19\\cc_upload_etVM7GrPwl33KYzn693cc3016abdf_1768824028.png', '/uploads/2026/01/19/cc_upload_etVM7GrPwl33KYzn693cc3016abdf_1768824028.png', 1161285, 'image', 'image/png', 'local', NULL, 0, 1768824029, 1768824029);
INSERT INTO `attachments` VALUES (9, 1, 'cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c.jpg', 'cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c.jpg', 'uploads\\2026\\01\\19\\cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c_1768825204.jpg', '/uploads/2026/01/19/cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c_1768825204.jpg', 1674408, 'image', 'image/jpeg', 'local', NULL, 0, 1768825204, 1768825204);
INSERT INTO `attachments` VALUES (10, 1, 'cc_upload_7BNbjCmbBatx3S4t6958b017537b6.jpg', 'cc_upload_7BNbjCmbBatx3S4t6958b017537b6.jpg', 'uploads\\2026\\01\\19\\cc_upload_7BNbjCmbBatx3S4t6958b017537b6_1768828219.jpg', '/uploads/2026/01/19/cc_upload_7BNbjCmbBatx3S4t6958b017537b6_1768828219.jpg', 104725, 'image', 'image/jpeg', 'local', NULL, 0, 1768828220, 1768828220);
INSERT INTO `attachments` VALUES (11, 1, 'cc_upload_P17ls1spvgI0uDk56958b02daaf14.jpg', 'cc_upload_P17ls1spvgI0uDk56958b02daaf14.jpg', 'uploads\\2026\\01\\19\\cc_upload_P17ls1spvgI0uDk56958b02daaf14_1768828219.jpg', '/uploads/2026/01/19/cc_upload_P17ls1spvgI0uDk56958b02daaf14_1768828219.jpg', 305101, 'image', 'image/jpeg', 'local', NULL, 0, 1768828220, 1768828220);
INSERT INTO `attachments` VALUES (12, 1, 'cc_upload_7BNbjCmbBatx3S4t6958b017537b6.jpg', 'cc_upload_7BNbjCmbBatx3S4t6958b017537b6.jpg', 'uploads\\2026\\01\\19\\cc_upload_7BNbjCmbBatx3S4t6958b017537b6_1768828707.jpg', '/uploads/2026/01/19/cc_upload_7BNbjCmbBatx3S4t6958b017537b6_1768828707.jpg', 104725, 'image', 'image/jpeg', 'local', NULL, 0, 1768828707, 1768828707);
) ENGINE = InnoDB AUTO_INCREMENT = 74 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '附件表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for categories
@@ -145,15 +116,7 @@ CREATE TABLE `categories` (
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_slug`(`slug` ASC) USING BTREE,
INDEX `idx_sort_order`(`sort_order` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文章分类表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of categories
-- ----------------------------
INSERT INTO `categories` VALUES (1, '工程化', '工程化', NULL, 0, 0, 0, 0);
INSERT INTO `categories` VALUES (2, '图形渲染', '图形渲染', NULL, 0, 0, 0, 0);
INSERT INTO `categories` VALUES (3, '设计思维', '设计思维', NULL, 0, 0, 0, 0);
INSERT INTO `categories` VALUES (4, 'Go语言', 'Go语言', NULL, 0, 0, 0, 0);
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文章分类表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for column_posts
@@ -166,16 +129,7 @@ CREATE TABLE `column_posts` (
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`column_id`, `post_id`) USING BTREE,
INDEX `idx_post_id`(`post_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '专栏文章关联表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of column_posts
-- ----------------------------
INSERT INTO `column_posts` VALUES (0, 2, 0, 0);
INSERT INTO `column_posts` VALUES (0, 3, 0, 0);
INSERT INTO `column_posts` VALUES (1, 4, 0, 0);
INSERT INTO `column_posts` VALUES (1, 5, 0, 0);
INSERT INTO `column_posts` VALUES (1, 6, 0, 1768809352);
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '专栏文章关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for columns
@@ -193,12 +147,7 @@ CREATE TABLE `columns` (
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_sort_order`(`sort_order` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '专栏表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of columns
-- ----------------------------
INSERT INTO `columns` VALUES (1, 'Goravel', 'Goravel入门手册', 'https://www.goravel.dev/logo.png', 1, 0, 0, 1768552230, 1768552230);
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '专栏表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for email_suffixes
@@ -214,18 +163,7 @@ CREATE TABLE `email_suffixes` (
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_suffix`(`suffix` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮箱后缀配置表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of email_suffixes
-- ----------------------------
INSERT INTO `email_suffixes` VALUES (1, '@gmail.com', 1, 1, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (2, '@163.com', 1, 2, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (3, '@qq.com', 1, 3, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (4, '@outlook.com', 1, 4, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (5, '@foxmail.com', 1, 5, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (6, '@sina.com', 1, 6, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (7, '@126.com', 1, 7, 0, 1768523866, 1768538954);
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮箱后缀配置表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for inquiries
@@ -244,11 +182,7 @@ CREATE TABLE `inquiries` (
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '合作咨询表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of inquiries
-- ----------------------------
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '合作咨询表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for operation_logs
@@ -269,235 +203,7 @@ CREATE TABLE `operation_logs` (
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_user_id`(`user_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 225 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of operation_logs
-- ----------------------------
INSERT INTO `operation_logs` VALUES (1, 1, 'lq', '::1', NULL, '/api/admin/oss-configs', 'GET', '', 200, 97, 0, 1768828795);
INSERT INTO `operation_logs` VALUES (2, 1, 'lq', '::1', NULL, '/api/admin/columns', 'GET', '', 200, 51, 0, 1768828812);
INSERT INTO `operation_logs` VALUES (3, 1, 'lq', '::1', NULL, '/api/admin/columns', 'GET', '', 200, 45, 0, 1768828890);
INSERT INTO `operation_logs` VALUES (4, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 44, 0, 1768828891);
INSERT INTO `operation_logs` VALUES (5, 1, 'lq', '::1', NULL, '/api/admin/operation-logs', 'GET', '', 200, 98, 0, 1768828901);
INSERT INTO `operation_logs` VALUES (6, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 48, 0, 1768829023);
INSERT INTO `operation_logs` VALUES (7, 1, 'lq', '::1', NULL, '/api/admin/attachments', 'GET', '', 200, 144, 0, 1768829023);
INSERT INTO `operation_logs` VALUES (8, 1, 'lq', '::1', NULL, '/api/admin/works', 'GET', '', 200, 85, 0, 1768829039);
INSERT INTO `operation_logs` VALUES (9, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 42, 0, 1768829041);
INSERT INTO `operation_logs` VALUES (10, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 46, 0, 1768829041);
INSERT INTO `operation_logs` VALUES (11, 1, 'lq', '::1', NULL, '/api/admin/attachments', 'GET', '', 200, 133, 0, 1768829045);
INSERT INTO `operation_logs` VALUES (12, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 44, 0, 1768829482);
INSERT INTO `operation_logs` VALUES (13, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 44, 0, 1768829482);
INSERT INTO `operation_logs` VALUES (14, 1, 'lq', '::1', NULL, '/api/admin/posts', 'GET', '', 200, 265, 0, 1768829814);
INSERT INTO `operation_logs` VALUES (15, 1, 'lq', '::1', NULL, '/api/admin/columns', 'GET', '', 200, 49, 0, 1768869064);
INSERT INTO `operation_logs` VALUES (16, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 44, 0, 1768869065);
INSERT INTO `operation_logs` VALUES (17, 1, 'lq', '::1', NULL, '/api/admin/attachments', 'GET', '', 200, 140, 0, 1768869068);
INSERT INTO `operation_logs` VALUES (18, 1, 'lq', '::1', NULL, '/api/admin/columns', 'GET', '', 200, 49, 0, 1768869074);
INSERT INTO `operation_logs` VALUES (19, 1, 'lq', '::1', NULL, '/api/admin/about', 'GET', '', 200, 47, 0, 1768869173);
INSERT INTO `operation_logs` VALUES (20, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 51, 0, 1768869180);
INSERT INTO `operation_logs` VALUES (21, 1, 'lq', '::1', NULL, '/api/admin/attachments', 'GET', '', 200, 139, 0, 1768869189);
INSERT INTO `operation_logs` VALUES (22, 1, 'lq', '::1', NULL, '/api/admin/partners/2', 'PUT', '{\"name\":\"Supabase\",\"logo\":\"/uploads/2026/01/19/cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c_1768825204.jpg\",\"description\":\"开源 Firebase 替代方案\"}', 200, 100, 0, 1768869197);
INSERT INTO `operation_logs` VALUES (23, 1, 'lq', '::1', NULL, '/api/admin/snippets', 'GET', '', 200, 95, 0, 1768869231);
INSERT INTO `operation_logs` VALUES (24, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 379, 0, 1768869446);
INSERT INTO `operation_logs` VALUES (25, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 376, 0, 1768869486);
INSERT INTO `operation_logs` VALUES (26, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 327, 0, 1768869688);
INSERT INTO `operation_logs` VALUES (27, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 372, 0, 1768869729);
INSERT INTO `operation_logs` VALUES (28, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 325, 0, 1768869747);
INSERT INTO `operation_logs` VALUES (29, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 375, 0, 1768869756);
INSERT INTO `operation_logs` VALUES (30, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 375, 0, 1768869758);
INSERT INTO `operation_logs` VALUES (31, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 375, 0, 1768869760);
INSERT INTO `operation_logs` VALUES (32, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 358, 0, 1768869781);
INSERT INTO `operation_logs` VALUES (33, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 376, 0, 1768869786);
INSERT INTO `operation_logs` VALUES (34, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 386, 0, 1768869793);
INSERT INTO `operation_logs` VALUES (35, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 385, 0, 1768869795);
INSERT INTO `operation_logs` VALUES (36, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 388, 0, 1768869800);
INSERT INTO `operation_logs` VALUES (37, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 341, 0, 1768870363);
INSERT INTO `operation_logs` VALUES (38, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 340, 0, 1768870495);
INSERT INTO `operation_logs` VALUES (39, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 354, 0, 1768870522);
INSERT INTO `operation_logs` VALUES (40, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 335, 0, 1768870535);
INSERT INTO `operation_logs` VALUES (41, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 334, 0, 1768870539);
INSERT INTO `operation_logs` VALUES (42, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 338, 0, 1768870541);
INSERT INTO `operation_logs` VALUES (43, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 325, 0, 1768870546);
INSERT INTO `operation_logs` VALUES (44, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 322, 0, 1768870579);
INSERT INTO `operation_logs` VALUES (45, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 321, 0, 1768870581);
INSERT INTO `operation_logs` VALUES (46, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 321, 0, 1768870587);
INSERT INTO `operation_logs` VALUES (47, 1, 'lq', '::1', NULL, '/api/admin/operation-logs', 'GET', '', 200, 95, 0, 1768870656);
INSERT INTO `operation_logs` VALUES (48, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 280, 0, 1768870656);
INSERT INTO `operation_logs` VALUES (49, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 376, 0, 1768870657);
INSERT INTO `operation_logs` VALUES (50, 1, 'lq', '::1', NULL, '/api/admin/posts', 'GET', '', 200, 275, 0, 1768870658);
INSERT INTO `operation_logs` VALUES (51, 1, 'lq', '::1', NULL, '/api/admin/categories', 'GET', '', 200, 47, 0, 1768870659);
INSERT INTO `operation_logs` VALUES (52, 1, 'lq', '::1', NULL, '/api/admin/columns', 'GET', '', 200, 48, 0, 1768870660);
INSERT INTO `operation_logs` VALUES (53, 1, 'lq', '::1', NULL, '/api/admin/works', 'GET', '', 200, 92, 0, 1768870660);
INSERT INTO `operation_logs` VALUES (54, 1, 'lq', '::1', NULL, '/api/admin/snippets', 'GET', '', 200, 92, 0, 1768870661);
INSERT INTO `operation_logs` VALUES (55, 1, 'lq', '::1', NULL, '/api/admin/tags', 'GET', '', 200, 44, 0, 1768870661);
INSERT INTO `operation_logs` VALUES (56, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768870663);
INSERT INTO `operation_logs` VALUES (57, 1, 'lq', '::1', NULL, '/api/admin/about', 'GET', '', 200, 47, 0, 1768870663);
INSERT INTO `operation_logs` VALUES (58, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768870664);
INSERT INTO `operation_logs` VALUES (59, 1, 'lq', '::1', NULL, '/api/admin/about', 'GET', '', 200, 47, 0, 1768870664);
INSERT INTO `operation_logs` VALUES (60, 1, 'lq', '::1', NULL, '/api/admin/inquiries', 'GET', '', 200, 47, 0, 1768870668);
INSERT INTO `operation_logs` VALUES (61, 1, 'lq', '::1', NULL, '/api/admin/email-suffixes', 'GET', '', 200, 49, 0, 1768870669);
INSERT INTO `operation_logs` VALUES (62, 1, 'lq', '::1', NULL, '/api/admin/users', 'GET', '', 200, 93, 0, 1768870671);
INSERT INTO `operation_logs` VALUES (63, 1, 'lq', '::1', NULL, '/api/admin/roles', 'GET', '', 200, 138, 0, 1768870672);
INSERT INTO `operation_logs` VALUES (64, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 49, 0, 1768870674);
INSERT INTO `operation_logs` VALUES (65, 1, 'lq', '::1', NULL, '/api/admin/attachments', 'GET', '', 200, 104, 0, 1768870674);
INSERT INTO `operation_logs` VALUES (66, 1, 'lq', '::1', NULL, '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768870675);
INSERT INTO `operation_logs` VALUES (67, 1, 'lq', '::1', NULL, '/api/admin/oss-configs', 'GET', '', 200, 97, 0, 1768870676);
INSERT INTO `operation_logs` VALUES (68, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 49, 0, 1768870678);
INSERT INTO `operation_logs` VALUES (69, 1, 'lq', '::1', NULL, '/api/admin/operation-logs', 'GET', '', 200, 95, 0, 1768870678);
INSERT INTO `operation_logs` VALUES (70, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 46, 0, 1768870679);
INSERT INTO `operation_logs` VALUES (71, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":4,\"keyName\":\"site_keywords\",\"value\":\"前端, 设计, 技术博客\",\"description\":\"网站关键词\"}', 400, 5, 0, 1768870685);
INSERT INTO `operation_logs` VALUES (72, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":5,\"keyName\":\"posts_per_page\",\"value\":\"12\",\"description\":\"每页显示的文章数量\"}', 400, 2, 0, 1768870685);
INSERT INTO `operation_logs` VALUES (73, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":3,\"keyName\":\"site_author\",\"value\":\"年糕崽崽\",\"description\":\"网站作者\"}', 400, 4, 0, 1768870685);
INSERT INTO `operation_logs` VALUES (74, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":2,\"keyName\":\"site_description\",\"value\":\"分享前端技术、交互设计以及数字艺术的深度思考\",\"description\":\"网站描述\"}', 400, 3, 0, 1768870685);
INSERT INTO `operation_logs` VALUES (75, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":1,\"keyName\":\"site_title\",\"value\":\"年糕博客\",\"description\":\"网站标题\"}', 400, 6, 0, 1768870685);
INSERT INTO `operation_logs` VALUES (76, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":7,\"keyName\":\"snippets_per_page\",\"value\":\"8\",\"description\":\"每页显示的代码片段数量\"}', 400, 3, 0, 1768870685);
INSERT INTO `operation_logs` VALUES (77, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":6,\"keyName\":\"works_per_page\",\"value\":\"6\",\"description\":\"每页显示的作品数量\"}', 400, 4, 0, 1768870685);
INSERT INTO `operation_logs` VALUES (78, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":5,\"keyName\":\"posts_per_page\",\"value\":\"12\",\"description\":\"每页显示的文章数量\"}', 200, 95, 0, 1768870798);
INSERT INTO `operation_logs` VALUES (79, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":3,\"keyName\":\"site_author\",\"value\":\"年糕崽崽\",\"description\":\"网站作者\"}', 200, 97, 0, 1768870798);
INSERT INTO `operation_logs` VALUES (80, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":2,\"keyName\":\"site_description\",\"value\":\"分享前端技术、交互设计以及数字艺术的深度思考\",\"description\":\"网站描述\"}', 200, 96, 0, 1768870799);
INSERT INTO `operation_logs` VALUES (81, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":4,\"keyName\":\"site_keywords\",\"value\":\"前端, 设计, 技术博客\",\"description\":\"网站关键词\"}', 200, 96, 0, 1768870799);
INSERT INTO `operation_logs` VALUES (82, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":1,\"keyName\":\"site_title\",\"value\":\"年糕博客\",\"description\":\"网站标题\"}', 200, 94, 0, 1768870799);
INSERT INTO `operation_logs` VALUES (83, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":7,\"keyName\":\"snippets_per_page\",\"value\":\"8\",\"description\":\"每页显示的代码片段数量\"}', 200, 96, 0, 1768870799);
INSERT INTO `operation_logs` VALUES (84, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":6,\"keyName\":\"works_per_page\",\"value\":\"6\",\"description\":\"每页显示的作品数量\"}', 200, 96, 0, 1768870799);
INSERT INTO `operation_logs` VALUES (85, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 46, 0, 1768870799);
INSERT INTO `operation_logs` VALUES (86, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 47, 0, 1768870807);
INSERT INTO `operation_logs` VALUES (87, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 49, 0, 1768870895);
INSERT INTO `operation_logs` VALUES (88, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 48, 0, 1768870907);
INSERT INTO `operation_logs` VALUES (89, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 45, 0, 1768870951);
INSERT INTO `operation_logs` VALUES (90, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 53, 0, 1768871123);
INSERT INTO `operation_logs` VALUES (91, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 50, 0, 1768871142);
INSERT INTO `operation_logs` VALUES (92, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 47, 0, 1768871185);
INSERT INTO `operation_logs` VALUES (93, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 50, 0, 1768871213);
INSERT INTO `operation_logs` VALUES (94, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 49, 0, 1768871273);
INSERT INTO `operation_logs` VALUES (95, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 50, 0, 1768871314);
INSERT INTO `operation_logs` VALUES (96, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 49, 0, 1768871359);
INSERT INTO `operation_logs` VALUES (97, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 47, 0, 1768871360);
INSERT INTO `operation_logs` VALUES (98, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 48, 0, 1768871381);
INSERT INTO `operation_logs` VALUES (99, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 57, 0, 1768871394);
INSERT INTO `operation_logs` VALUES (100, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 49, 0, 1768871427);
INSERT INTO `operation_logs` VALUES (101, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 44, 0, 1768871860);
INSERT INTO `operation_logs` VALUES (102, 1, 'lq', '::1', NULL, '/api/admin/operation-logs', 'GET', '', 200, 97, 0, 1768872916);
INSERT INTO `operation_logs` VALUES (103, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 334, 0, 1768872916);
INSERT INTO `operation_logs` VALUES (104, 1, 'lq', '::1', NULL, '/api/admin/operation-logs', 'GET', '', 200, 98, 0, 1768872983);
INSERT INTO `operation_logs` VALUES (105, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 338, 0, 1768872983);
INSERT INTO `operation_logs` VALUES (106, 1, 'lq', '::1', NULL, '/api/admin/posts', 'GET', '', 200, 277, 0, 1768873803);
INSERT INTO `operation_logs` VALUES (107, 1, 'lq', '::1', NULL, '/api/admin/posts', 'GET', '', 200, 290, 0, 1768877345);
INSERT INTO `operation_logs` VALUES (108, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 51, 0, 1768877347);
INSERT INTO `operation_logs` VALUES (109, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":5,\"keyName\":\"posts_per_page\",\"value\":\"12\",\"description\":\"每页显示的文章数量\"}', 200, 98, 0, 1768877365);
INSERT INTO `operation_logs` VALUES (110, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":3,\"keyName\":\"site_author\",\"value\":\"年糕崽崽1\",\"description\":\"网站作者\"}', 200, 93, 0, 1768877365);
INSERT INTO `operation_logs` VALUES (111, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":2,\"keyName\":\"site_description\",\"value\":\"分享前端技术、交互设计以及数字艺术的深度思考\",\"description\":\"网站描述\"}', 200, 96, 0, 1768877365);
INSERT INTO `operation_logs` VALUES (112, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":4,\"keyName\":\"site_keywords\",\"value\":\"前端, 设计, 技术博客\",\"description\":\"网站关键词\"}', 200, 98, 0, 1768877365);
INSERT INTO `operation_logs` VALUES (113, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":1,\"keyName\":\"site_title\",\"value\":\"年糕博客\",\"description\":\"网站标题\"}', 200, 96, 0, 1768877365);
INSERT INTO `operation_logs` VALUES (114, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":7,\"keyName\":\"snippets_per_page\",\"value\":\"8\",\"description\":\"每页显示的代码片段数量\"}', 200, 97, 0, 1768877365);
INSERT INTO `operation_logs` VALUES (115, 1, 'lq', '::1', NULL, '/api/admin/settings', 'PUT', '{\"id\":6,\"keyName\":\"works_per_page\",\"value\":\"6\",\"description\":\"每页显示的作品数量\"}', 200, 96, 0, 1768877365);
INSERT INTO `operation_logs` VALUES (116, 1, 'lq', '::1', NULL, '/api/admin/settings', 'GET', '', 200, 47, 0, 1768877365);
INSERT INTO `operation_logs` VALUES (117, 1, 'lq', '::1', NULL, '/api/admin/operation-logs', 'GET', '', 200, 101, 0, 1768877421);
INSERT INTO `operation_logs` VALUES (118, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 332, 0, 1768877421);
INSERT INTO `operation_logs` VALUES (119, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 373, 0, 1768877441);
INSERT INTO `operation_logs` VALUES (120, 1, 'lq', '::1', NULL, '/api/admin/dashboard/stats', 'GET', '', 200, 373, 0, 1768877446);
INSERT INTO `operation_logs` VALUES (121, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 332, 0, 1768877578);
INSERT INTO `operation_logs` VALUES (122, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 328, 0, 1768877614);
INSERT INTO `operation_logs` VALUES (123, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 328, 0, 1768877620);
INSERT INTO `operation_logs` VALUES (124, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 329, 0, 1768877627);
INSERT INTO `operation_logs` VALUES (125, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 330, 0, 1768877629);
INSERT INTO `operation_logs` VALUES (126, 1, 'lq', '::1', 'Internal', '/api/admin/access-logs', 'GET', '', 200, 95, 0, 1768877636);
INSERT INTO `operation_logs` VALUES (127, 1, 'lq', '::1', 'Internal', '/api/admin/operation-logs', 'GET', '', 200, 97, 0, 1768877637);
INSERT INTO `operation_logs` VALUES (128, 1, 'lq', '::1', 'Internal', '/api/admin/access-logs', 'GET', '', 200, 94, 0, 1768877641);
INSERT INTO `operation_logs` VALUES (129, 1, 'lq', '::1', 'Internal', '/api/admin/operation-logs', 'GET', '', 200, 94, 0, 1768877642);
INSERT INTO `operation_logs` VALUES (130, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 48, 0, 1768877642);
INSERT INTO `operation_logs` VALUES (131, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 330, 0, 1768877655);
INSERT INTO `operation_logs` VALUES (132, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 328, 0, 1768877658);
INSERT INTO `operation_logs` VALUES (133, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 329, 0, 1768877716);
INSERT INTO `operation_logs` VALUES (134, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 48, 0, 1768877809);
INSERT INTO `operation_logs` VALUES (135, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 52, 0, 1768878153);
INSERT INTO `operation_logs` VALUES (136, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 49, 0, 1768878166);
INSERT INTO `operation_logs` VALUES (137, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 55, 0, 1768878174);
INSERT INTO `operation_logs` VALUES (138, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 57, 0, 1768878177);
INSERT INTO `operation_logs` VALUES (139, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 52, 0, 1768878188);
INSERT INTO `operation_logs` VALUES (140, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 51, 0, 1768878472);
INSERT INTO `operation_logs` VALUES (141, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 47, 0, 1768878512);
INSERT INTO `operation_logs` VALUES (142, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 52, 0, 1768878616);
INSERT INTO `operation_logs` VALUES (143, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":5,\"keyName\":\"posts_per_page\",\"value\":\"12\",\"description\":\"每页显示的文章数量\"}', 200, 96, 0, 1768878627);
INSERT INTO `operation_logs` VALUES (144, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":3,\"keyName\":\"site_author\",\"value\":\"年糕崽崽1\",\"description\":\"网站作者\"}', 200, 98, 0, 1768878627);
INSERT INTO `operation_logs` VALUES (145, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":2,\"keyName\":\"site_description\",\"value\":\"分享前端技术、交互设计以及数字艺术的深度思考\",\"description\":\"网站描述\"}', 200, 96, 0, 1768878627);
INSERT INTO `operation_logs` VALUES (146, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":4,\"keyName\":\"site_keywords\",\"value\":\"前端, 设计, 技术博客\",\"description\":\"网站关键词\"}', 200, 98, 0, 1768878628);
INSERT INTO `operation_logs` VALUES (147, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":1,\"keyName\":\"site_title\",\"value\":\"年糕崽崽\",\"description\":\"网站标题\"}', 200, 97, 0, 1768878628);
INSERT INTO `operation_logs` VALUES (148, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":7,\"keyName\":\"snippets_per_page\",\"value\":\"8\",\"description\":\"每页显示的代码片段数量\"}', 200, 98, 0, 1768878628);
INSERT INTO `operation_logs` VALUES (149, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":6,\"keyName\":\"works_per_page\",\"value\":\"6\",\"description\":\"每页显示的作品数量\"}', 200, 97, 0, 1768878628);
INSERT INTO `operation_logs` VALUES (150, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 50, 0, 1768878628);
INSERT INTO `operation_logs` VALUES (151, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 50, 0, 1768878630);
INSERT INTO `operation_logs` VALUES (152, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 52, 0, 1768878632);
INSERT INTO `operation_logs` VALUES (153, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 51, 0, 1768878651);
INSERT INTO `operation_logs` VALUES (154, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 51, 0, 1768878667);
INSERT INTO `operation_logs` VALUES (155, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":5,\"keyName\":\"posts_per_page\",\"value\":\"12\",\"description\":\"每页显示的文章数量\"}', 200, 98, 0, 1768878783);
INSERT INTO `operation_logs` VALUES (156, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":3,\"keyName\":\"site_author\",\"value\":\"年糕崽崽1\",\"description\":\"网站作者\"}', 200, 96, 0, 1768878783);
INSERT INTO `operation_logs` VALUES (157, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":2,\"keyName\":\"site_description\",\"value\":\"分享前端技术、交互设计以及数字艺术的深度思考\",\"description\":\"网站描述\"}', 200, 97, 0, 1768878783);
INSERT INTO `operation_logs` VALUES (158, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":4,\"keyName\":\"site_keywords\",\"value\":\"前端, 设计, 技术博客\",\"description\":\"网站关键词\"}', 200, 98, 0, 1768878783);
INSERT INTO `operation_logs` VALUES (159, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":1,\"keyName\":\"site_title\",\"value\":\"年糕崽崽\",\"description\":\"网站标题\"}', 200, 96, 0, 1768878783);
INSERT INTO `operation_logs` VALUES (160, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":7,\"keyName\":\"snippets_per_page\",\"value\":\"8\",\"description\":\"每页显示的代码片段数量\"}', 200, 98, 0, 1768878783);
INSERT INTO `operation_logs` VALUES (161, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":6,\"keyName\":\"works_per_page\",\"value\":\"6\",\"description\":\"每页显示的作品数量\"}', 200, 104, 0, 1768878783);
INSERT INTO `operation_logs` VALUES (162, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 48, 0, 1768878783);
INSERT INTO `operation_logs` VALUES (163, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 45, 0, 1768878798);
INSERT INTO `operation_logs` VALUES (164, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 47, 0, 1768878805);
INSERT INTO `operation_logs` VALUES (165, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 46, 0, 1768878844);
INSERT INTO `operation_logs` VALUES (166, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 50, 0, 1768878871);
INSERT INTO `operation_logs` VALUES (167, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 50, 0, 1768878985);
INSERT INTO `operation_logs` VALUES (168, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":15,\"keyName\":\"visible_menus\",\"value\":\"[\\\"home\\\",\\\"blog\\\",\\\"columns\\\",\\\"works\\\",\\\"about\\\",\\\"services\\\"]\",\"description\":\"前台显示的菜单项JSON数组格式\"}', 200, 95, 0, 1768878990);
INSERT INTO `operation_logs` VALUES (169, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 44, 0, 1768878990);
INSERT INTO `operation_logs` VALUES (170, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 47, 0, 1768879150);
INSERT INTO `operation_logs` VALUES (171, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 46, 0, 1768879154);
INSERT INTO `operation_logs` VALUES (172, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 48, 0, 1768879158);
INSERT INTO `operation_logs` VALUES (173, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 16, 0, 1768880799);
INSERT INTO `operation_logs` VALUES (174, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 53, 0, 1768880809);
INSERT INTO `operation_logs` VALUES (175, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'PUT', '{\"id\":15,\"keyName\":\"visible_menus\",\"value\":\"[\\\"home\\\",\\\"blog\\\",\\\"columns\\\",\\\"works\\\",\\\"about\\\"]\",\"description\":\"前台显示的菜单项JSON数组格式\"}', 200, 99, 0, 1768881703);
INSERT INTO `operation_logs` VALUES (176, 1, 'lq', '::1', 'Internal', '/api/admin/settings', 'GET', '', 200, 50, 0, 1768881703);
INSERT INTO `operation_logs` VALUES (177, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 312, 0, 1768881885);
INSERT INTO `operation_logs` VALUES (178, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 289, 0, 1768881900);
INSERT INTO `operation_logs` VALUES (179, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 329, 0, 1768882129);
INSERT INTO `operation_logs` VALUES (180, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 342, 0, 1768882288);
INSERT INTO `operation_logs` VALUES (181, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 330, 0, 1768882702);
INSERT INTO `operation_logs` VALUES (182, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 406, 0, 1768882750);
INSERT INTO `operation_logs` VALUES (183, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 387, 0, 1768882851);
INSERT INTO `operation_logs` VALUES (184, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 390, 0, 1768882853);
INSERT INTO `operation_logs` VALUES (185, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 392, 0, 1768882899);
INSERT INTO `operation_logs` VALUES (186, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 390, 0, 1768882943);
INSERT INTO `operation_logs` VALUES (187, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 344, 0, 1768883044);
INSERT INTO `operation_logs` VALUES (188, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 393, 0, 1768883048);
INSERT INTO `operation_logs` VALUES (189, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 337, 0, 1768883061);
INSERT INTO `operation_logs` VALUES (190, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 403, 0, 1768883292);
INSERT INTO `operation_logs` VALUES (191, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 385, 0, 1768883350);
INSERT INTO `operation_logs` VALUES (192, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 390, 0, 1768883362);
INSERT INTO `operation_logs` VALUES (193, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 380, 0, 1768883369);
INSERT INTO `operation_logs` VALUES (194, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 386, 0, 1768883370);
INSERT INTO `operation_logs` VALUES (195, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 382, 0, 1768883398);
INSERT INTO `operation_logs` VALUES (196, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 380, 0, 1768883424);
INSERT INTO `operation_logs` VALUES (197, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 379, 0, 1768883445);
INSERT INTO `operation_logs` VALUES (198, 1, 'lq', '::1', 'Internal', '/api/admin/dashboard/stats', 'GET', '', 200, 383, 0, 1768883471);
INSERT INTO `operation_logs` VALUES (199, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 49, 0, 1768885354);
INSERT INTO `operation_logs` VALUES (200, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 101, 0, 1768885354);
INSERT INTO `operation_logs` VALUES (201, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768885377);
INSERT INTO `operation_logs` VALUES (202, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 143, 0, 1768885377);
INSERT INTO `operation_logs` VALUES (203, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 51, 0, 1768885548);
INSERT INTO `operation_logs` VALUES (204, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 101, 0, 1768885548);
INSERT INTO `operation_logs` VALUES (205, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 52, 0, 1768886215);
INSERT INTO `operation_logs` VALUES (206, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 98, 0, 1768886215);
INSERT INTO `operation_logs` VALUES (207, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 55, 0, 1768886263);
INSERT INTO `operation_logs` VALUES (208, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 99, 0, 1768886263);
INSERT INTO `operation_logs` VALUES (209, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 54, 0, 1768886265);
INSERT INTO `operation_logs` VALUES (210, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 102, 0, 1768886265);
INSERT INTO `operation_logs` VALUES (211, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 52, 0, 1768886268);
INSERT INTO `operation_logs` VALUES (212, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 99, 0, 1768886268);
INSERT INTO `operation_logs` VALUES (213, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 48, 0, 1768886279);
INSERT INTO `operation_logs` VALUES (214, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 98, 0, 1768886279);
INSERT INTO `operation_logs` VALUES (215, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 45, 0, 1768886289);
INSERT INTO `operation_logs` VALUES (216, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 133, 0, 1768886289);
INSERT INTO `operation_logs` VALUES (217, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768886433);
INSERT INTO `operation_logs` VALUES (218, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 144, 0, 1768886433);
INSERT INTO `operation_logs` VALUES (219, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 55, 0, 1768886433);
INSERT INTO `operation_logs` VALUES (220, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 152, 0, 1768886433);
INSERT INTO `operation_logs` VALUES (221, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768886790);
INSERT INTO `operation_logs` VALUES (222, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 147, 0, 1768886790);
INSERT INTO `operation_logs` VALUES (223, 1, 'lq', '::1', 'Internal', '/api/admin/attachment-categories', 'GET', '', 200, 49, 0, 1768887180);
INSERT INTO `operation_logs` VALUES (224, 1, 'lq', '::1', 'Internal', '/api/admin/attachments', 'GET', '', 200, 97, 0, 1768887180);
) ENGINE = InnoDB AUTO_INCREMENT = 487 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for oss_configs
@@ -519,15 +225,7 @@ CREATE TABLE `oss_configs` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_storage_type`(`storage_type` ASC) USING BTREE,
INDEX `idx_is_active`(`is_active` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OSS配置表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oss_configs
-- ----------------------------
INSERT INTO `oss_configs` VALUES (1, '本地存储', 'local', '', '', '', '', '', 1, 0, 1768820300, 1768820300);
INSERT INTO `oss_configs` VALUES (2, '阿里云OSS', 'aliyun', '', '', '', '', '', 0, 0, 1768820300, 1768820300);
INSERT INTO `oss_configs` VALUES (3, '腾讯云COS', 'qcloud', '', '', '', '', '', 0, 0, 1768820300, 1768820300);
INSERT INTO `oss_configs` VALUES (4, '七牛云', 'qiniu', '', '', '', '', '', 0, 0, 1768820300, 1768820300);
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OSS配置表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for partners
@@ -544,16 +242,7 @@ CREATE TABLE `partners` (
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '合作伙伴表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of partners
-- ----------------------------
INSERT INTO `partners` VALUES (1, 'Vercel', 'https://upload.wikimedia.org/wikipedia/commons/5/5e/Vercel_logo_black.svg', '前端部署平台', 'https://vercel.com', 0, 0, 1768482993, 1768538953);
INSERT INTO `partners` VALUES (2, 'Supabase', '/uploads/2026/01/19/cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c_1768825204.jpg', '开源 Firebase 替代方案', '', 0, 0, 1768482993, 1768869197);
INSERT INTO `partners` VALUES (3, 'Stripe', 'https://upload.wikimedia.org/wikipedia/commons/b/ba/Stripe_Logo%2C_revised_2016.svg', '在线支付基础设施', 'https://stripe.com', 0, 0, 1768482993, 1768538953);
INSERT INTO `partners` VALUES (4, 'Algolia', 'https://upload.wikimedia.org/wikipedia/commons/6/69/Algolia-logo.svg', '搜索即服务 API', 'https://algolia.com', 0, 0, 1768482993, 1768538953);
INSERT INTO `partners` VALUES (5, 'Prisma', 'https://seeklogo.com/images/P/prisma-logo-3805665B69-seeklogo.com.png', '下一代 ORM', 'https://prisma.io', 0, 0, 1768482993, 1768538953);
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '合作伙伴表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for permissions
@@ -569,41 +258,7 @@ CREATE TABLE `permissions` (
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `unique_resource_action`(`resource` ASC, `action` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 31 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '权限表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of permissions
-- ----------------------------
INSERT INTO `permissions` VALUES (1, 'Create User', 'users', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (2, 'Read User', 'users', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (3, 'Update User', 'users', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (4, 'Delete User', 'users', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (5, 'Create Role', 'roles', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (6, 'Read Role', 'roles', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (7, 'Update Role', 'roles', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (8, 'Delete Role', 'roles', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (9, 'Create Post', 'posts', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (10, 'Read Post', 'posts', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (11, 'Update Post', 'posts', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (12, 'Delete Post', 'posts', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (13, 'Create Work', 'works', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (14, 'Read Work', 'works', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (15, 'Update Work', 'works', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (16, 'Delete Work', 'works', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (17, 'Create Snippet', 'snippets', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (18, 'Read Snippet', 'snippets', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (19, 'Update Snippet', 'snippets', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (20, 'Delete Snippet', 'snippets', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (21, 'Create Setting', 'settings', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (22, 'Read Setting', 'settings', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (23, 'Update Setting', 'settings', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (24, 'Delete Setting', 'settings', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (25, 'Create Tag', 'tags', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (26, 'Read Tag', 'tags', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (27, 'Update Tag', 'tags', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (28, 'Delete Tag', 'tags', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (29, 'Read Operation Log', 'operation_logs', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (30, 'Read Dashboard', 'dashboard', 'read', 0, 1768452892, 1768538956);
) ENGINE = InnoDB AUTO_INCREMENT = 31 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '权限表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for post_history
@@ -615,6 +270,8 @@ CREATE TABLE `post_history` (
`version` int UNSIGNED NOT NULL DEFAULT 1 COMMENT '版本号',
`title` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文章标题',
`category_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '文章分类ID',
`column_id` int UNSIGNED NULL DEFAULT NULL COMMENT '专栏ID',
`tag_ids` json NULL COMMENT '标签ID快照',
`excerpt` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '文章摘要',
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文章内容',
`is_published` tinyint(1) NULL DEFAULT 1 COMMENT '是否已发布',
@@ -625,14 +282,7 @@ CREATE TABLE `post_history` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_post_id`(`post_id` ASC) USING BTREE,
INDEX `idx_version`(`version` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文章历史记录表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of post_history
-- ----------------------------
INSERT INTO `post_history` VALUES (1, 6, 1, 'Goravel 入门指南 (三)ORM 数据库操作', 4, '掌握 Goravel 强大的 ORM 功能从数据库配置、模型定义到执行增删改查CRUD操作。', '', 1, 1, 1768809133, 1768809133, 0);
INSERT INTO `post_history` VALUES (2, 6, 2, 'Goravel 入门指南 (三)ORM 数据库操作', 4, '掌握 Goravel 强大的 ORM 功能从数据库配置、模型定义到执行增删改查CRUD操作。', '', 1, 1, 1768809352, 1768809352, 0);
INSERT INTO `post_history` VALUES (3, 1, 1, '重构的艺术:如何优雅地处理遗留代码', 4, '在本文中,我们将探讨重构的核心原则和实用技巧,帮助你优雅地处理遗留代码,提高代码质量和可维护性。', '<h2>什么是代码重构?</h2><p>代码重构是在不改变代码外部行为的前提下,优化代码内部结构的过程...</p>\n\n\n![cc_upload_opYFPBlXkfyOSrH269152a21424c4.jpg](/uploads/2026/01/19/cc_upload_opYFPBlXkfyOSrH269152a21424c4_1768811098.jpg)\n', 1, 1, 1768811113, 1768811113, 0);
) ENGINE = InnoDB AUTO_INCREMENT = 60 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文章历史记录表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for post_tags
@@ -644,15 +294,7 @@ CREATE TABLE `post_tags` (
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`post_id`, `tag_id`) USING BTREE,
INDEX `idx_tag_id`(`tag_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文章标签关联表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of post_tags
-- ----------------------------
INSERT INTO `post_tags` VALUES (1, 6, 0);
INSERT INTO `post_tags` VALUES (2, 6, 0);
INSERT INTO `post_tags` VALUES (3, 6, 0);
INSERT INTO `post_tags` VALUES (4, 6, 0);
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文章标签关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for posts
@@ -677,17 +319,7 @@ CREATE TABLE `posts` (
INDEX `idx_is_published`(`is_published` ASC) USING BTREE COMMENT '按发布状态查询索引',
INDEX `idx_original_id`(`original_id` ASC) USING BTREE,
FULLTEXT INDEX `idx_title_content`(`title`, `content`) COMMENT '标题和内容全文索引,用于搜索'
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '博客文章表(新结构)' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of posts
-- ----------------------------
INSERT INTO `posts` VALUES (1, 'refactor', '重构的艺术:如何优雅地处理遗留代码', 4, NULL, '在本文中,我们将探讨重构的核心原则和实用技巧,帮助你优雅地处理遗留代码,提高代码质量和可维护性。', '<h2>什么是代码重构?</h2><p>代码重构是在不改变代码外部行为的前提下,优化代码内部结构的过程...</p>\n\n\n![cc_upload_opYFPBlXkfyOSrH269152a21424c4.jpg](/uploads/2026/01/19/cc_upload_opYFPBlXkfyOSrH269152a21424c4_1768811098.jpg)\n', 10, 1, 0, 1768291814, 1768811113);
INSERT INTO `posts` VALUES (2, 'shader', '着色器魔法:从零开始写一个噪声生成器', 2, NULL, '深入了解WebGL着色器学习如何从零开始实现一个高性能的噪声生成器为你的3D作品增添独特的视觉效果。', ' <p>GLSL (OpenGL Shading Language) 是一门让人生畏但也充满魅力的语言。它运行在 GPU 上,能够并行处理数百万个像素,创造出惊人的视觉效果。</p>\r\n <h2>什么是柏林噪声?</h2>\r\n <p>柏林噪声Perlin Noise是一种梯度噪声它比普通的随机数生成的噪声看起来更自然、更平滑。它常被用来模拟云彩、地形、火焰等自然现象。</p>\r\n <h2>Three.js 中的实现</h2>\r\n <p>在 Three.js 中,我们可以通过 <code>ShaderMaterial</code> 直接编写 GLSL 代码。</p>\r\n <pre><code><span class=\"code-comment\">// 简单的顶点着色器</span>\r\n<span class=\"code-keyword\">varying</span> <span class=\"code-keyword\">vec2</span> vUv;\r\n<span class=\"code-keyword\">void</span> <span class=\"code-func\">main</span>() {\r\n vUv = uv;\r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}</code></pre>\r\n <p>通过调整噪声的频率和振幅,我们可以得到各种不同的纹理效果。在我的个人网站背景中,就使用了这种技术来生成流动的极光效果。</p>\r\n ', 1, 1, 0, 1768291815, 1768538949);
INSERT INTO `posts` VALUES (3, 'ux', '用户体验设计:从认知心理学到交互实践', 3, NULL, '探索用户体验设计的核心原理,结合认知心理学知识,学习如何设计出真正符合用户需求的交互界面。', '<h2>认知心理学在UX设计中的应用</h2><p>了解用户的认知过程是设计良好用户体验的基础...</p>', 0, 1, 0, 1768291816, 1768538949);
INSERT INTO `posts` VALUES (4, 'goravel-guide-1', 'Goravel 入门指南 (一):环境搭建与 Hello World', 4, 1, '本文将带你了解 Go 语言领域的 Laravel —— Goravel 框架,并演示如何快速搭建环境及运行第一个 Web 服务。', '## 什么是 Goravel\n\nGoravel 是一个基于 Go 语言的 Web 开发框架,它的设计理念深受 PHP Laravel 框架的启发。如果你是一名从 PHP 转 Go 的开发者,或者你喜欢 Laravel 那种“开箱即用、优雅简洁”的开发体验,那么 Goravel 绝对是你的不二之选。\n\n它集成了丰富的功能模块包括但不限于\n- 强大的路由系统\n- ORM基于 GORM 封装)\n- 依赖注入容器\n- 队列与任务调度\n- 缓存与文件存储\n\n## 环境搭建\n\nGoravel 提供了一个名为 `knit` 的命令行工具(类似 Laravel 的 artisan可以帮助我们快速初始化项目。\n\n### 1. 安装 Knit CLI\n\n确保你已经安装了 Go (1.20+),然后运行以下命令:\n\n```bash\ngo install github.com/goravel/knit/cmd/knit@latest\n```\n\n### 2. 创建新项目\n\n使用 `knit new` 命令创建项目:\n\n```bash\nknit new my-goravel-app\ncd my-goravel-app\n```\n\n### 3. 安装依赖\n\n```bash\ngo mod tidy\n```\n\n## 目录结构\n\n打开项目你会发现它的目录结构非常清晰带有浓厚的 Laravel 风格:\n\n- **app/**: 核心业务代码Http 控制器、模型、服务提供者等)\n- **config/**: 配置文件(应用配置、数据库配置等)\n- **routes/**: 路由定义文件\n- **database/**: 数据库迁移与填充\n- **public/**: 静态资源文件\n\n## 运行 Hello World\n\nGoravel 的入口文件是根目录下的 `main.go`。在运行之前,我们先看一眼路由定义。打开 `routes/web.go`\n\n```go\npackage routes\n\nimport (\n \"github.com/goravel/framework/contracts/http\"\n \"github.com/goravel/framework/facades\"\n)\n\nfunc Web() {\n facades.Route().Get(\"/\", func(ctx http.Context) http.Response {\n return ctx.Response().Json(200, http.Json{\n \"Hello\": \"Goravel\",\n })\n })\n}\n```\n\n非常直观现在让我们启动服务\n\n```bash\ngo run .\n```\n\n默认情况下服务会运行在 `http://localhost:3000`。打开浏览器访问,你应该能看到 JSON 响应:\n\n```json\n{\n \"Hello\": \"Goravel\"\n}\n```\n\n至此你已经成功运行了你的第一个 Goravel 应用!在下一篇文章中,我们将深入探讨路由与控制器的使用。', 1223, 1, 0, 1768465932, 1768538949);
INSERT INTO `posts` VALUES (5, 'goravel-guide-2', 'Goravel 入门指南 (二):路由与控制器', 4, 1, '深入理解 Goravel 的 HTTP 层,学习如何定义 RESTful 路由、创建控制器以及处理 HTTP 请求与响应。', '## 路由系统\n\n在 Goravel 中,路由定义通常位于 `routes/` 目录下。`api.go` 用于定义 API 路由,`web.go` 用于定义网页路由。Goravel 使用 `facades.Route()` 来定义路由,这得益于其强大的依赖注入系统。\n\n### 基础路由\n\n```go\n// GET 请求\nfacades.Route().Get(\"/users\", func(ctx http.Context) http.Response {\n return ctx.Response().String(200, \"User List\")\n})\n\n// POST 请求\nfacades.Route().Post(\"/users\", func(ctx http.Context) http.Response {\n return ctx.Response().Success().Json(http.Json{\"id\": 1})\n})\n```\n\n### 路由参数\n\n获取 URL 中的动态参数非常简单:\n\n```go\nfacades.Route().Get(\"/users/{id}\", func(ctx http.Context) http.Response {\n id := ctx.Request().Input(\"id\")\n return ctx.Response().Success().Json(http.Json{\"user_id\": id})\n})\n```\n\n## 控制器 (Controllers)\n\n随着应用变大我们不可能把所有逻辑都写在路由闭包里。这时候就需要控制器了。\n\n### 创建控制器\n\n使用 `knit` 工具可以快速生成控制器:\n\n```bash\nknit make:controller UserController\n```\n\n这会在 `app/http/controllers` 目录下生成 `user_controller.go`。让我们修改它来添加一个 `Show` 方法:\n\n```go\npackage controllers\n\nimport (\n \"github.com/goravel/framework/contracts/http\"\n)\n\ntype UserController struct {\n // 可以在这里注入服务\n}\n\nfunc NewUserController() *UserController {\n return &UserController{}\n}\n\nfunc (r *UserController) Show(ctx http.Context) http.Response {\n id := ctx.Request().Input(\"id\")\n // 模拟数据库查询\n return ctx.Response().Success().Json(http.Json{\n \"id\": id,\n \"name\": \"Goravel User\",\n })\n}\n```\n\n### 注册控制器路由\n\n回到 `routes/api.go`,我们需要先实例化控制器,然后绑定路由:\n\n```go\nimport \"my-goravel-app/app/http/controllers\"\n\nfunc Api() {\n userController := controllers.NewUserController()\n \n // 绑定到控制器方法\n facades.Route().Get(\"/users/{id}\", userController.Show)\n}\n```\n\n## 请求与响应\n\n在控制器方法中`ctx` (http.Context) 是核心:\n\n- **获取输入**: `ctx.Request().Input(\"key\")`\n- **获取 JSON**: `ctx.Request().Bind(&user)`\n- **返回 JSON**: `ctx.Response().Json(200, data)`\n- **设置状态码**: `ctx.Response().Status(404)`\n\n通过这种方式Goravel 让 HTTP 层的处理变得异常清晰和标准化。下一章,我们将学习如何通过 ORM 操作数据库。', 925, 1, 0, 1768465933, 1768538949);
INSERT INTO `posts` VALUES (6, 'goravel-guide-3', 'Goravel 入门指南 (三)ORM 数据库操作', 4, 1, '掌握 Goravel 强大的 ORM 功能从数据库配置、模型定义到执行增删改查CRUD操作。', '## Goravel ORM 简介\r\nGoravel 的 ORM 基于著名的 GORM 库构建,但它进行了一层优雅的封装,使其调用方式更接近 Laravel 的 Eloquent ORM。这意味着你可以使用 Facade 模式轻松调用数据库,且支持自动迁移。\r\n## 配置数据库\r\n打开 `config/database.go` 或 `.env` 文件,配置你的 MySQL 连接信息:\r\n```env\r\nDB_CONNECTION=mysql\r\nDB_HOST=127.0.0.1\r\nDB_PORT=3306\r\nDB_DATABASE=goravel\r\nDB_USERNAME=root\r\nDB_PASSWORD=password\r\n```\r\n## 定义模型\r\n使用 `knit` 生成模型:\r\n```bash\r\nknit make:model Post\r\n```\r\n生成的模型文件位于 `app/models/post.go`。我们需要定义结构体字段:\r\n```go\r\npackage models\r\nimport (\r\n \\\"github.com/goravel/framework/database/orm\\\"\r\n )\r\ntype Post struct {\r\n orm.Model\r\n Title string `gorm:\\\"size:255;not null\\\"`\r\n Content string `gorm:\\\"type:text\\\"`\r\n UserID uint\r\n }\r\n ```\r\n## 数据库迁移\r\n虽然 GORM 支持 AutoMigrate但 Goravel 推荐使用迁移文件来管理数据库变更。\r\n```bash\r\nknit make:migration create_posts_table\r\n```\r\n编辑生成的迁移文件 (`database/migrations/xxxx_create_posts_table.go`),定义表结构,然后运行:\r\n```bash\r\nknit migrate\r\n```\r\n## CRUD 操作\r\n有了模型我们就可以愉快地操作数据库了。所有操作都通过 `facades.Orm()` 入口。\r\n### 创建 (Create)\r\n```go\r\npost := models.Post{\r\n Title: \\\"My First Post\\\",\r\n Content: \\\"Content goes here...\\\",\r\n }\r\n err := facades.Orm().Query().Create(&post)\r\n ```\r\n### 查询 (Read)\r\n```go\r\nvar post models.Post\r\n// 根据主键查询\r\nfacades.Orm().Query().Find(&post, 1)\r\n// 条件查询\r\nvar posts []models.Post\r\nfacades.Orm().Query().Where(\\\"title\\\", \\\"My First Post\\\").Get(&posts)\r\n```\r\n### 更新 (Update)\r\n```go\r\nvar post models.Post\r\nfacades.Orm().Query().Find(&post, 1)\r\npost.Title = \\\"Updated Title\\\"\r\nfacades.Orm().Query().Save(&post)\r\n```\r\n### 删除 (Delete)\r\n```go\r\nvar post models.Post\r\nfacades.Orm().Query().Delete(&post, 1)\r\n```\r\nGoravel 的 ORM 让 Go 语言的数据库操作不再繁琐,极大地提高了开发效率。配合之前的路由和控制器知识,你现在已经具备了开发完整 RESTful API 的能力!', 1602, 1, 0, 1768465934, 1768809351);
) ENGINE = InnoDB AUTO_INCREMENT = 36 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '博客文章表(新结构)' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for role_permissions
@@ -698,54 +330,7 @@ CREATE TABLE `role_permissions` (
`permission_id` bigint UNSIGNED NOT NULL COMMENT '权限ID',
PRIMARY KEY (`role_id`, `permission_id`) USING BTREE,
INDEX `role_permissions_ibfk_2`(`permission_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色权限关联表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of role_permissions
-- ----------------------------
INSERT INTO `role_permissions` VALUES (1, 1);
INSERT INTO `role_permissions` VALUES (1, 2);
INSERT INTO `role_permissions` VALUES (3, 2);
INSERT INTO `role_permissions` VALUES (1, 3);
INSERT INTO `role_permissions` VALUES (1, 4);
INSERT INTO `role_permissions` VALUES (1, 5);
INSERT INTO `role_permissions` VALUES (1, 6);
INSERT INTO `role_permissions` VALUES (3, 6);
INSERT INTO `role_permissions` VALUES (1, 7);
INSERT INTO `role_permissions` VALUES (1, 8);
INSERT INTO `role_permissions` VALUES (1, 9);
INSERT INTO `role_permissions` VALUES (2, 9);
INSERT INTO `role_permissions` VALUES (1, 10);
INSERT INTO `role_permissions` VALUES (2, 10);
INSERT INTO `role_permissions` VALUES (3, 10);
INSERT INTO `role_permissions` VALUES (1, 11);
INSERT INTO `role_permissions` VALUES (2, 11);
INSERT INTO `role_permissions` VALUES (1, 12);
INSERT INTO `role_permissions` VALUES (2, 12);
INSERT INTO `role_permissions` VALUES (1, 13);
INSERT INTO `role_permissions` VALUES (1, 14);
INSERT INTO `role_permissions` VALUES (3, 14);
INSERT INTO `role_permissions` VALUES (1, 15);
INSERT INTO `role_permissions` VALUES (1, 16);
INSERT INTO `role_permissions` VALUES (1, 17);
INSERT INTO `role_permissions` VALUES (1, 18);
INSERT INTO `role_permissions` VALUES (3, 18);
INSERT INTO `role_permissions` VALUES (1, 19);
INSERT INTO `role_permissions` VALUES (1, 20);
INSERT INTO `role_permissions` VALUES (1, 21);
INSERT INTO `role_permissions` VALUES (1, 22);
INSERT INTO `role_permissions` VALUES (3, 22);
INSERT INTO `role_permissions` VALUES (1, 23);
INSERT INTO `role_permissions` VALUES (1, 24);
INSERT INTO `role_permissions` VALUES (1, 25);
INSERT INTO `role_permissions` VALUES (1, 26);
INSERT INTO `role_permissions` VALUES (3, 26);
INSERT INTO `role_permissions` VALUES (1, 27);
INSERT INTO `role_permissions` VALUES (1, 28);
INSERT INTO `role_permissions` VALUES (1, 29);
INSERT INTO `role_permissions` VALUES (3, 29);
INSERT INTO `role_permissions` VALUES (1, 30);
INSERT INTO `role_permissions` VALUES (3, 30);
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色权限关联表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for roles
@@ -760,14 +345,7 @@ CREATE TABLE `roles` (
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `name`(`name` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of roles
-- ----------------------------
INSERT INTO `roles` VALUES (1, 'admin', '系统管理员', 0, 1768452892, 1768538956);
INSERT INTO `roles` VALUES (2, 'editor', '内容编辑', 0, 1768452892, 1768538956);
INSERT INTO `roles` VALUES (3, 'viewer', '普通访客', 0, 1768452892, 1768538956);
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for search_logs
@@ -784,11 +362,7 @@ CREATE TABLE `search_logs` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_search_type`(`search_type` ASC) USING BTREE,
INDEX `idx_created_at`(`created_at` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '搜索记录表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of search_logs
-- ----------------------------
) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '搜索记录表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for settings
@@ -805,19 +379,7 @@ CREATE TABLE `settings` (
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `key_name`(`key_name` ASC) USING BTREE,
INDEX `idx_key_name`(`key_name` ASC) USING BTREE COMMENT '按键名查询索引'
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '网站配置表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of settings
-- ----------------------------
INSERT INTO `settings` VALUES (1, 'site_title', '年糕崽崽', '网站标题', 0, 1768291814, 1768878783);
INSERT INTO `settings` VALUES (2, 'site_description', '分享前端技术、交互设计以及数字艺术的深度思考', '网站描述', 0, 1768291814, 1768878783);
INSERT INTO `settings` VALUES (3, 'site_author', '年糕崽崽1', '网站作者', 0, 1768291814, 1768878783);
INSERT INTO `settings` VALUES (4, 'site_keywords', '前端, 设计, 技术博客', '网站关键词', 0, 1768291814, 1768878783);
INSERT INTO `settings` VALUES (5, 'posts_per_page', '12', '每页显示的文章数量', 0, 1768291814, 1768878783);
INSERT INTO `settings` VALUES (6, 'works_per_page', '6', '每页显示的作品数量', 0, 1768291814, 1768878783);
INSERT INTO `settings` VALUES (7, 'snippets_per_page', '8', '每页显示的代码片段数量', 0, 1768291814, 1768878783);
INSERT INTO `settings` VALUES (15, 'visible_menus', '[\"home\",\"blog\",\"columns\",\"works\",\"about\"]', '前台显示的菜单项JSON数组格式', 0, 1768878978, 1768881703);
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '网站配置表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for snippets
@@ -836,12 +398,7 @@ CREATE TABLE `snippets` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_type`(`type` ASC) USING BTREE COMMENT '按代码类型查询索引',
INDEX `idx_view_count`(`view_count` ASC) USING BTREE COMMENT '按查看次数查询索引'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码片段表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of snippets
-- ----------------------------
INSERT INTO `snippets` VALUES ('1', 'React 鼠标追踪 Hook', 'import { useState, useEffect } from \'react\';\r\n\r\nexport const useMousePosition = () => {\r\n const [pos, setPos] = useState({ x: 0, y: 0 });\r\n useEffect(() => {\r\n const update = (e) => setPos({ x: e.clientX, y: e.clientY });\r\n window.addEventListener(\'mousemove\', update);\r\n return () => window.removeEventListener(\'mousemove\', update);\r\n }, []);\r\n return pos;\r\n};', 'mouse', '这是一个鼠标追踪', 3, 0, 1768350739, 1768538952);
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码片段表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for tags
@@ -858,15 +415,7 @@ CREATE TABLE `tags` (
UNIQUE INDEX `name`(`name` ASC) USING BTREE,
UNIQUE INDEX `slug`(`slug` ASC) USING BTREE,
INDEX `idx_slug`(`slug` ASC) USING BTREE COMMENT '按别名查询索引'
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '标签表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of tags
-- ----------------------------
INSERT INTO `tags` VALUES (1, 'Goravel', '', 0, 1768550858, 1768550858);
INSERT INTO `tags` VALUES (2, '入门教程', '入门教程', 0, 1768809322, 1768809322);
INSERT INTO `tags` VALUES (3, 'Golang框架', 'Golang框架', 0, 1768809341, 1768809341);
INSERT INTO `tags` VALUES (4, '前后端分离', '前后端分离', 0, 1768809349, 1768809349);
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '标签表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for testimonials
@@ -884,17 +433,7 @@ CREATE TABLE `testimonials` (
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '客户评价表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of testimonials
-- ----------------------------
INSERT INTO `testimonials` VALUES (1, 'Alex Chen', 'Product Owner @ TechFlow', '年糕不仅技术过硬,对设计细节的把控更是令人惊叹。', 'https://api.dicebear.com/7.x/avataaars/svg?seed=Alex', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (2, 'Sarah Wu', 'Design Director @ ArtSpace', '很少见到能把代码写得像诗一样的工程师,合作非常愉快!', 'https://api.dicebear.com/7.x/avataaars/svg?seed=Sarah', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (3, 'Mike Zhang', 'CTO @ FutureWave', '交付质量远超预期,特别是在性能优化方面做得非常出色。', 'https://api.dicebear.com/7.x/avataaars/svg?seed=Mike', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (4, 'Jessica Li', 'Founder @ ZenMode', '从交互动效到整体架构,都体现了极高的专业水准。', 'https://api.dicebear.com/7.x/avataaars/svg?seed=Jessica', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (5, 'David Wang', 'Tech Lead @ Innovate', '代码结构清晰,注释完善,后续维护非常轻松。', 'https://api.dicebear.com/7.x/avataaars/svg?seed=David', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (6, '', 'CTO', '服务很贴心,技术够硬', 'https://api.dicebear.com/7.x/avataaars/svg?seed=David', 5, 0, 0, 20260116130552, 20260116130552);
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '客户评价表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for user_access_logs
@@ -906,61 +445,15 @@ CREATE TABLE `user_access_logs` (
`user_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户IP地址',
`user_location` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户归属地',
`article_id` int NOT NULL COMMENT '访问的文章ID',
`visitor_key` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '匿名访客标识',
`deleted_at` bigint NOT NULL DEFAULT 0,
`access_time` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_user_id`(`user_id` ASC) USING BTREE,
INDEX `idx_article_id`(`article_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 46 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户访问记录表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of user_access_logs
-- ----------------------------
INSERT INTO `user_access_logs` VALUES (1, 0, '::1', 'Internal', 1, 0, 1768829817);
INSERT INTO `user_access_logs` VALUES (2, 0, '::1', 'Internal', 4, 0, 1768829860);
INSERT INTO `user_access_logs` VALUES (3, 0, '::1', 'Internal', 4, 0, 1768869688);
INSERT INTO `user_access_logs` VALUES (4, 0, '::1', 'Internal', 4, 0, 1768869746);
INSERT INTO `user_access_logs` VALUES (5, 0, '::1', 'Internal', 4, 0, 1768870362);
INSERT INTO `user_access_logs` VALUES (6, 0, '::1', 'Internal', 4, 0, 1768870495);
INSERT INTO `user_access_logs` VALUES (7, 0, '::1', 'Internal', 4, 0, 1768870534);
INSERT INTO `user_access_logs` VALUES (8, 0, '::1', 'Internal', 4, 0, 1768870538);
INSERT INTO `user_access_logs` VALUES (9, 0, '::1', 'Internal', 4, 0, 1768870540);
INSERT INTO `user_access_logs` VALUES (10, 0, '::1', 'Internal', 4, 0, 1768870545);
INSERT INTO `user_access_logs` VALUES (11, 0, '::1', 'Internal', 4, 0, 1768870579);
INSERT INTO `user_access_logs` VALUES (12, 0, '::1', 'Internal', 4, 0, 1768870580);
INSERT INTO `user_access_logs` VALUES (13, 0, '::1', 'Internal', 4, 0, 1768870586);
INSERT INTO `user_access_logs` VALUES (14, 0, '::1', 'Internal', 4, 0, 1768870637);
INSERT INTO `user_access_logs` VALUES (15, 0, '::1', 'Internal', 5, 0, 1768870640);
INSERT INTO `user_access_logs` VALUES (16, 1, '::1', 'Internal', 0, 0, 1768872916);
INSERT INTO `user_access_logs` VALUES (17, 0, '::1', 'Internal', 6, 0, 1768878230);
INSERT INTO `user_access_logs` VALUES (18, 0, '::1', 'Internal', 6, 0, 1768878635);
INSERT INTO `user_access_logs` VALUES (19, 0, '::1', 'Internal', 6, 0, 1768878639);
INSERT INTO `user_access_logs` VALUES (20, 0, '::1', 'Internal', 6, 0, 1768878649);
INSERT INTO `user_access_logs` VALUES (21, 0, '::1', 'Internal', 6, 0, 1768878651);
INSERT INTO `user_access_logs` VALUES (22, 0, '::1', 'Internal', 6, 0, 1768878667);
INSERT INTO `user_access_logs` VALUES (23, 0, '::1', 'Internal', 6, 0, 1768878844);
INSERT INTO `user_access_logs` VALUES (24, 0, '::1', 'Internal', 6, 0, 1768878871);
INSERT INTO `user_access_logs` VALUES (25, 0, '::1', 'Internal', 6, 0, 1768878875);
INSERT INTO `user_access_logs` VALUES (26, 0, '::1', 'Internal', 5, 0, 1768879449);
INSERT INTO `user_access_logs` VALUES (27, 0, '::1', 'Internal', 5, 0, 1768879452);
INSERT INTO `user_access_logs` VALUES (28, 0, '::1', 'Internal', 5, 0, 1768879513);
INSERT INTO `user_access_logs` VALUES (29, 0, '::1', 'Internal', 5, 0, 1768880797);
INSERT INTO `user_access_logs` VALUES (30, 0, '::1', 'Internal', 5, 0, 1768880799);
INSERT INTO `user_access_logs` VALUES (31, 0, '::1', 'Internal', 5, 0, 1768880810);
INSERT INTO `user_access_logs` VALUES (32, 0, '::1', 'Internal', 5, 0, 1768881631);
INSERT INTO `user_access_logs` VALUES (33, 0, '::1', 'Internal', 5, 0, 1768881636);
INSERT INTO `user_access_logs` VALUES (34, 0, '::1', 'Internal', 5, 0, 1768881706);
INSERT INTO `user_access_logs` VALUES (35, 0, '::1', 'Internal', 5, 0, 1768883044);
INSERT INTO `user_access_logs` VALUES (36, 0, '::1', 'Internal', 5, 0, 1768883060);
INSERT INTO `user_access_logs` VALUES (37, 0, '::1', 'Internal', 5, 0, 1768883290);
INSERT INTO `user_access_logs` VALUES (38, 0, '::1', 'Internal', 5, 0, 1768883291);
INSERT INTO `user_access_logs` VALUES (39, 0, '::1', 'Internal', 5, 0, 1768886263);
INSERT INTO `user_access_logs` VALUES (40, 0, '::1', 'Internal', 5, 0, 1768886279);
INSERT INTO `user_access_logs` VALUES (41, 0, '::1', 'Internal', 5, 0, 1768886433);
INSERT INTO `user_access_logs` VALUES (42, 0, '::1', 'Internal', 5, 0, 1768886788);
INSERT INTO `user_access_logs` VALUES (43, 0, '::1', 'Internal', 5, 0, 1768886790);
INSERT INTO `user_access_logs` VALUES (44, 0, '::1', 'Internal', 5, 0, 1768887178);
INSERT INTO `user_access_logs` VALUES (45, 0, '::1', 'Internal', 5, 0, 1768887180);
INDEX `idx_article_id`(`article_id` ASC) USING BTREE,
INDEX `idx_dedup_hour`(`article_id` ASC, `visitor_key` ASC, `access_time` ASC) USING BTREE,
INDEX `idx_dedup_user_hour`(`article_id` ASC, `user_id` ASC, `access_time` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 298 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户访问记录表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for users
@@ -984,14 +477,7 @@ CREATE TABLE `users` (
INDEX `idx_email`(`email` ASC) USING BTREE COMMENT '按邮箱查询索引',
INDEX `idx_role`(`role` ASC) USING BTREE COMMENT '按角色查询索引',
INDEX `users_ibfk_1`(`role_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES (1, 'lq', 'liqiworker@gmail.com', '$2a$10$b.KMnG261WjsvdKciKn01uRpuZs2eIiiIIXhpwIu6rgQ8AL5oxA8e', 1, 'admin', 1, 0, 1768291814, 1768538951);
INSERT INTO `users` VALUES (2, 'editor', 'editor@example.com', '$2a$14$OqZ7svgz3S68yuuXTJWz1.0CuqI2i3WvwC1jIGBy4l0mcJW9Kqvy2', 2, 'editor', 1, 0, 1768291814, 1768538951);
INSERT INTO `users` VALUES (5, 'cs', 'cs@nailaoyun.cn', '$2a$14$OqZ7svgz3S68yuuXTJWz1.0CuqI2i3WvwC1jIGBy4l0mcJW9Kqvy2', 3, 'viewer', 1, 0, 1768462115, 1768538951);
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for work_gallery
@@ -1008,40 +494,7 @@ CREATE TABLE `work_gallery` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_work_id`(`work_id` ASC) USING BTREE,
INDEX `idx_sort_order`(`sort_order` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 30 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '作品图库表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of work_gallery
-- ----------------------------
INSERT INTO `work_gallery` VALUES (1, 'nova', 'https://images.unsplash.com/photo-1642543492481-44e81e3914a7?q=80&w=2070', 1, 'Nova 交易平台首页', 1768826702, 1768291937);
INSERT INTO `work_gallery` VALUES (2, 'nova', 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070', 2, 'Nova 交易平台交易界面', 1768826702, 1768291937);
INSERT INTO `work_gallery` VALUES (3, 'archdaily', 'https://images.unsplash.com/photo-1503387762-592deb58ef4e?q=80&w=2089', 1, 'ArchDaily 网站首页', 1768828228, 1768291937);
INSERT INTO `work_gallery` VALUES (4, 'archdaily', 'https://images.unsplash.com/photo-1518005020951-ecc859466abc?q=80&w=1920', 2, 'ArchDaily 文章详情页', 1768828228, 1768291937);
INSERT INTO `work_gallery` VALUES (5, 'nova', 'https://images.unsplash.com/photo-1642543492481-44e81e3914a7?q=80&w=2070', 1, '', 1768826702, 1768825221);
INSERT INTO `work_gallery` VALUES (6, 'nova', 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070', 2, '', 1768826702, 1768825221);
INSERT INTO `work_gallery` VALUES (7, 'nova', '/uploads/2026/01/19/cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c_1768825204.jpg', 3, '', 1768826702, 1768825221);
INSERT INTO `work_gallery` VALUES (8, 'nova', 'https://images.unsplash.com/photo-1642543492481-44e81e3914a7?q=80&w=2070', 1, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (9, 'nova', 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070', 2, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (10, 'nova', '/uploads/2026/01/19/cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c_1768825204.jpg', 3, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (11, 'nova', 'https://images.unsplash.com/photo-1642543492481-44e81e3914a7?q=80&w=2070', 4, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (12, 'nova', 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070', 5, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (13, 'nova', '/uploads/2026/01/19/cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c_1768825204.jpg', 6, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (14, 'nova', '/uploads/2026/01/19/cc_upload_0NeLXeWx8AmBi7Mx693ce05fea320_1768824028.jpg', 7, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (15, 'nova', '/uploads/2026/01/19/cc_upload_62sXZ0HMvINRUYDO693d6c27c7045_1768824028.jpg', 8, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (16, 'nova', '/uploads/2026/01/19/cc_upload_9AqkM4FiiRhzl1On693d6c27ce6eb_1768824028.jpg', 9, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (17, 'nova', '/uploads/2026/01/19/cc_upload_gXTnJhQ2DSbBr3Ql693d6c27c0b62_1768824028.jpg', 10, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (18, 'nova', '/uploads/2026/01/19/cc_upload_etVM7GrPwl33KYzn693cc3016abdf_1768824028.png', 11, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (19, 'nova', '/uploads/2026/01/19/cc_upload_xTUFrRBdm7zWyjQs693a39ea93969_1768824015.jpg', 12, '', 0, 1768826702);
INSERT INTO `work_gallery` VALUES (20, 'archdaily', 'https://images.unsplash.com/photo-1503387762-592deb58ef4e?q=80&w=2089', 1, '', 0, 1768828228);
INSERT INTO `work_gallery` VALUES (21, 'archdaily', '/uploads/2026/01/19/cc_upload_7BNbjCmbBatx3S4t6958b017537b6_1768828219.jpg', 2, '', 0, 1768828228);
INSERT INTO `work_gallery` VALUES (22, 'archdaily', '/uploads/2026/01/19/cc_upload_P17ls1spvgI0uDk56958b02daaf14_1768828219.jpg', 3, '', 0, 1768828228);
INSERT INTO `work_gallery` VALUES (23, 'work_1768828710998326100', '/uploads/2026/01/19/cc_upload_HiPDFg6oL0Q5ijfo693ce1f5eb57c_1768825204.jpg', 1, '', 0, 1768828711);
INSERT INTO `work_gallery` VALUES (24, 'work_1768828710998326100', '/uploads/2026/01/19/cc_upload_P17ls1spvgI0uDk56958b02daaf14_1768828219.jpg', 2, '', 0, 1768828711);
INSERT INTO `work_gallery` VALUES (25, 'work_1768828710998326100', '/uploads/2026/01/19/cc_upload_7BNbjCmbBatx3S4t6958b017537b6_1768828219.jpg', 3, '', 0, 1768828711);
INSERT INTO `work_gallery` VALUES (26, 'work_1768828710998326100', '/uploads/2026/01/19/cc_upload_gXTnJhQ2DSbBr3Ql693d6c27c0b62_1768824028.jpg', 4, '', 0, 1768828711);
INSERT INTO `work_gallery` VALUES (27, 'work_1768828710998326100', '/uploads/2026/01/19/cc_upload_etVM7GrPwl33KYzn693cc3016abdf_1768824028.png', 5, '', 0, 1768828711);
INSERT INTO `work_gallery` VALUES (28, 'work_1768828710998326100', '/uploads/2026/01/19/cc_upload_xTUFrRBdm7zWyjQs693a39ea93969_1768824015.jpg', 6, '', 0, 1768828711);
INSERT INTO `work_gallery` VALUES (29, 'work_1768828710998326100', '/uploads/2026/01/19/cc_upload_9AqkM4FiiRhzl1On693d6c27ce6eb_1768824028.jpg', 7, '', 0, 1768828711);
) ENGINE = InnoDB AUTO_INCREMENT = 30 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '作品图库表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for work_tech_stack
@@ -1057,38 +510,7 @@ CREATE TABLE `work_tech_stack` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_work_id`(`work_id` ASC) USING BTREE,
INDEX `idx_category`(`category` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 28 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '作品技术栈表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of work_tech_stack
-- ----------------------------
INSERT INTO `work_tech_stack` VALUES (1, 'nova', '前端层', 'React 18', 1768826702, 1768291937);
INSERT INTO `work_tech_stack` VALUES (2, 'nova', '前端层', 'TypeScript', 1768826702, 1768291937);
INSERT INTO `work_tech_stack` VALUES (3, 'nova', '前端层', 'D3.js', 1768826702, 1768291937);
INSERT INTO `work_tech_stack` VALUES (4, 'nova', '后端服务', 'Golang', 1768826702, 1768291937);
INSERT INTO `work_tech_stack` VALUES (5, 'nova', '后端服务', 'gRPC', 1768826702, 1768291937);
INSERT INTO `work_tech_stack` VALUES (6, 'archdaily', '核心前端', 'Vue 3', 1768828228, 1768291937);
INSERT INTO `work_tech_stack` VALUES (7, 'archdaily', '核心前端', 'Nuxt.js', 1768828228, 1768291937);
INSERT INTO `work_tech_stack` VALUES (8, 'archdaily', '核心前端', 'GSAP', 1768828228, 1768291937);
INSERT INTO `work_tech_stack` VALUES (9, 'archdaily', 'CMS', 'Strapi', 1768828228, 1768291937);
INSERT INTO `work_tech_stack` VALUES (10, 'nova', '前端层', 'React 18', 1768826702, 1768825220);
INSERT INTO `work_tech_stack` VALUES (11, 'nova', '前端层', 'TypeScript', 1768826702, 1768825220);
INSERT INTO `work_tech_stack` VALUES (12, 'nova', '前端层', 'D3.js', 1768826702, 1768825220);
INSERT INTO `work_tech_stack` VALUES (13, 'nova', '后端服务', 'Golang', 1768826702, 1768825220);
INSERT INTO `work_tech_stack` VALUES (14, 'nova', '后端服务', 'gRPC', 1768826702, 1768825220);
INSERT INTO `work_tech_stack` VALUES (15, 'nova', '前端层', 'React 18', 0, 1768826702);
INSERT INTO `work_tech_stack` VALUES (16, 'nova', '前端层', 'TypeScript', 0, 1768826702);
INSERT INTO `work_tech_stack` VALUES (17, 'nova', '前端层', 'D3.js', 0, 1768826702);
INSERT INTO `work_tech_stack` VALUES (18, 'nova', '后端服务', 'Golang', 0, 1768826702);
INSERT INTO `work_tech_stack` VALUES (19, 'nova', '后端服务', 'gRPC', 0, 1768826702);
INSERT INTO `work_tech_stack` VALUES (20, 'archdaily', 'CMS', 'Strapi', 0, 1768828228);
INSERT INTO `work_tech_stack` VALUES (21, 'archdaily', '核心前端', 'Vue 3', 0, 1768828228);
INSERT INTO `work_tech_stack` VALUES (22, 'archdaily', '核心前端', 'Nuxt.js', 0, 1768828228);
INSERT INTO `work_tech_stack` VALUES (23, 'archdaily', '核心前端', 'GSAP', 0, 1768828228);
INSERT INTO `work_tech_stack` VALUES (24, 'work_1768828710998326100', '前端', 'Vue3', 0, 1768828711);
INSERT INTO `work_tech_stack` VALUES (25, 'work_1768828710998326100', '前端', 'Ts', 0, 1768828711);
INSERT INTO `work_tech_stack` VALUES (26, 'work_1768828710998326100', '前端', 'Tailwindcss', 0, 1768828711);
INSERT INTO `work_tech_stack` VALUES (27, 'work_1768828710998326100', '前端', 'IndexDB', 0, 1768828711);
) ENGINE = InnoDB AUTO_INCREMENT = 28 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '作品技术栈表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for works
@@ -1110,13 +532,6 @@ CREATE TABLE `works` (
INDEX `idx_category`(`category` ASC) USING BTREE COMMENT '按分类查询索引',
INDEX `idx_year`(`year` ASC) USING BTREE COMMENT '按年份查询索引',
INDEX `idx_is_featured`(`is_featured` ASC) USING BTREE COMMENT '按精选状态查询索引'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '作品表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of works
-- ----------------------------
INSERT INTO `works` VALUES ('archdaily', 'ArchDaily 网站重构', '建筑设计', '2022', 'https://images.unsplash.com/photo-1487958449943-2429e8be8625?q=80&w=2070', 'ArchDaily 是全球最受欢迎的建筑网站之一。这次重构的目标是提升移动端体验。我们使用了 Nuxt 3 进行服务端渲染SSR', '{\"demo\":\"http://localhost:3000/works\",\"github\":\"http://localhost:3000/works\",\"live\":\"http://localhost:3000/works\"}', 0, 0, 1768291814, 1768828228);
INSERT INTO `works` VALUES ('nova', 'Nova 交易平台', '金融科技', '2023', 'https://images.unsplash.com/photo-1487958449943-2429e8be8625?q=80&w=2070', 'Nova 是一个专为机构交易员设计的高频交易终端。我们面临的最大挑战是如何在处理毫秒级市场数据的同时,保持界面的流畅响应。我们采用 Web Worker 来处理繁重的数据计算,避免阻塞主线程。', '{\"demo\":\"http://localhost:3000/works/nova\",\"github\":\"http://localhost:3000/works/nova\",\"live\":\"http://localhost:3000/works/nova\"}', 0, 0, 1768291814, 1768826701);
INSERT INTO `works` VALUES ('work_1768828710998326100', 'Mac OS', '仿系统', '2025', '/uploads/2026/01/19/cc_upload_7BNbjCmbBatx3S4t6958b017537b6_1768828707.jpg', '这是一个基于Vue3的仿Mac风格系统前端项目可以而开成企业网盘这些功能', '{\"demo\":\"\",\"github\":\"\",\"live\":\"\"}', 0, 0, 1768828711, 1768828711);
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '作品表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -1,898 +0,0 @@
/*
Navicat Premium Dump SQL
Source Server : 开发环境-本地
Source Server Type : MySQL
Source Server Version : 80407 (8.4.7)
Source Host : localhost:3306
Source Schema : nl_blog
Target Server Type : MySQL
Target Server Version : 80407 (8.4.7)
File Encoding : 65001
Date: 19/01/2026 16:13:35
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for about_profiles
-- ----------------------------
DROP TABLE IF EXISTS `about_profiles`;
CREATE TABLE `about_profiles` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '',
`location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '',
`bio` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL,
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '',
`wechat` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '',
`tech_stack` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT 'JSON string or comma separated list',
`experiences` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT 'JSON string of experience list',
`is_primary` tinyint(1) NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
`deleted_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of about_profiles
-- ----------------------------
INSERT INTO `about_profiles` VALUES (3, '年糕崽崽', 'https://api.dicebear.com/7.x/avataaars/svg?seed=NianGao&backgroundColor=b6e3f4', '中国 · 浙江杭州', '嗨!我是年糕崽崽,一名热衷于探索数字边界的前端架构师。我的职业生涯始于对像素级完美的执念。从早期的 jQuery 插件开发到如今的 WebGL 3D 场景构建,我始终认为:<strong>技术是骨架,艺术是灵魂。</strong><br><br>目前,我专注于高性能 B 端应用的体验升级以及探索生成式艺术Generative Art在网页中的应用。闲暇之余我喜欢在杭州的西湖边散步或者捣腾我的客制化机械键盘。', 'liqiworker@gmail.com', 'ngzz_0218', '[\\\"Vue 3\\\",\\\"React\\\",\\\"TypeScript\\\",\\\"Three.js\\\",\\\"Golang\\\",\\\"Tailwind CSS\\\",\\\"Rust\\\",\\\"Wails\\\"]', '[{\\\"year\\\":\\\"2024 - 至今\\\",\\\"role\\\":\\\"技术负责人\\\",\\\"company\\\":\\\"某医疗平台公司\\\"},{\\\"year\\\":\\\"2020 - 2024\\\",\\\"role\\\":\\\"PHP开发工程师\\\",\\\"company\\\":\\\"某电商公司\\\"}]', 0, 0, 0, 0);
-- ----------------------------
-- Table structure for access_logs
-- ----------------------------
DROP TABLE IF EXISTS `access_logs`;
CREATE TABLE `access_logs` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '访问者IP地址',
`user_agent` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '访问者浏览器信息',
`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '访问路径',
`method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'HTTP方法',
`status_code` int UNSIGNED NOT NULL COMMENT 'HTTP状态码',
`response_time` int UNSIGNED NOT NULL COMMENT '响应时间(毫秒)',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_path`(`path` ASC) USING BTREE COMMENT '按访问路径查询索引',
INDEX `idx_status_code`(`status_code` ASC) USING BTREE COMMENT '按状态码查询索引'
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '访问日志表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of access_logs
-- ----------------------------
-- ----------------------------
-- Table structure for attachment_categories
-- ----------------------------
DROP TABLE IF EXISTS `attachment_categories`;
CREATE TABLE `attachment_categories` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类名称',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '分类描述',
`sort_order` int UNSIGNED NULL DEFAULT 0 COMMENT '排序',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_sort_order`(`sort_order` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '附件分类表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of attachment_categories
-- ----------------------------
-- ----------------------------
-- Table structure for attachments
-- ----------------------------
DROP TABLE IF EXISTS `attachments`;
CREATE TABLE `attachments` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`category_id` int UNSIGNED NULL DEFAULT NULL COMMENT '附件分类ID',
`original_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '原始文件名',
`stored_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '存储文件名',
`file_path` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件路径',
`file_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文件访问URL',
`file_size` bigint UNSIGNED NOT NULL DEFAULT 0 COMMENT '文件大小(字节)',
`file_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '文件类型: image/video/document/other',
`mime_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'MIME类型',
`storage_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'local' COMMENT '存储类型: local/qcloud/aliyun/qiniu',
`oss_config_id` int UNSIGNED NULL DEFAULT NULL COMMENT 'OSS配置ID',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_category_id`(`category_id` ASC) USING BTREE,
INDEX `idx_storage_type`(`storage_type` ASC) USING BTREE,
INDEX `idx_file_type`(`file_type` ASC) USING BTREE,
INDEX `idx_created_at`(`created_at` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '附件表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of attachments
-- ----------------------------
INSERT INTO `attachments` VALUES (1, NULL, 'cc_upload_xTUFrRBdm7zWyjQs693a39ea93969.jpg', 'cc_upload_xTUFrRBdm7zWyjQs693a39ea93969.jpg', 'uploads\\2026\\01\\19\\cc_upload_xTUFrRBdm7zWyjQs693a39ea93969_1768807852.jpg', '/uploads/2026/01/19/cc_upload_xTUFrRBdm7zWyjQs693a39ea93969_1768807852.jpg', 40648, 'image', 'image/jpeg', 'local', NULL, 0, 1768807852, 1768807852);
-- ----------------------------
-- Table structure for categories
-- ----------------------------
DROP TABLE IF EXISTS `categories`;
CREATE TABLE `categories` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '分类ID',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类名称',
`slug` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '分类别名',
`description` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '描述',
`sort_order` int UNSIGNED NULL DEFAULT 0 COMMENT '排序',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_slug`(`slug` ASC) USING BTREE,
INDEX `idx_sort_order`(`sort_order` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文章分类表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of categories
-- ----------------------------
INSERT INTO `categories` VALUES (1, '工程化', '工程化', NULL, 0, 0, 0, 0);
INSERT INTO `categories` VALUES (2, '图形渲染', '图形渲染', NULL, 0, 0, 0, 0);
INSERT INTO `categories` VALUES (3, '设计思维', '设计思维', NULL, 0, 0, 0, 0);
INSERT INTO `categories` VALUES (4, 'Go语言', 'Go语言', NULL, 0, 0, 0, 0);
-- ----------------------------
-- Table structure for column_posts
-- ----------------------------
DROP TABLE IF EXISTS `column_posts`;
CREATE TABLE `column_posts` (
`column_id` int UNSIGNED NOT NULL COMMENT '专栏ID',
`post_id` int UNSIGNED NOT NULL COMMENT '文章ID',
`sort_order` int UNSIGNED NULL DEFAULT 0 COMMENT '排序',
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`column_id`, `post_id`) USING BTREE,
INDEX `idx_post_id`(`post_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '专栏文章关联表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of column_posts
-- ----------------------------
INSERT INTO `column_posts` VALUES (0, 1, 0, 0);
INSERT INTO `column_posts` VALUES (0, 2, 0, 0);
INSERT INTO `column_posts` VALUES (0, 3, 0, 0);
INSERT INTO `column_posts` VALUES (1, 4, 0, 0);
INSERT INTO `column_posts` VALUES (1, 5, 0, 0);
INSERT INTO `column_posts` VALUES (1, 6, 0, 1768809352);
-- ----------------------------
-- Table structure for columns
-- ----------------------------
DROP TABLE IF EXISTS `columns`;
CREATE TABLE `columns` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '专栏ID',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '专栏名称',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '专栏描述',
`cover` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '专栏封面',
`is_active` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
`sort_order` int UNSIGNED NULL DEFAULT 0 COMMENT '排序',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_sort_order`(`sort_order` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '专栏表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of columns
-- ----------------------------
INSERT INTO `columns` VALUES (1, 'Goravel', 'Goravel入门手册', 'https://www.goravel.dev/logo.png', 1, 0, 0, 1768552230, 1768552230);
-- ----------------------------
-- Table structure for email_suffixes
-- ----------------------------
DROP TABLE IF EXISTS `email_suffixes`;
CREATE TABLE `email_suffixes` (
`id` int NOT NULL AUTO_INCREMENT,
`suffix` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮箱后缀',
`is_active` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否启用',
`sort_order` int NOT NULL DEFAULT 0 COMMENT '排序',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `uk_suffix`(`suffix` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮箱后缀配置表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of email_suffixes
-- ----------------------------
INSERT INTO `email_suffixes` VALUES (1, '@gmail.com', 1, 1, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (2, '@163.com', 1, 2, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (3, '@qq.com', 1, 3, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (4, '@outlook.com', 1, 4, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (5, '@foxmail.com', 1, 5, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (6, '@sina.com', 1, 6, 0, 1768523866, 1768538954);
INSERT INTO `email_suffixes` VALUES (7, '@126.com', 1, 7, 0, 1768523866, 1768538954);
-- ----------------------------
-- Table structure for inquiries
-- ----------------------------
DROP TABLE IF EXISTS `inquiries`;
CREATE TABLE `inquiries` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '姓名',
`company` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '公司/组织',
`contact_method` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系方式类型(email/wechat/phone)',
`contact_value` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '联系方式值',
`budget` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '预算范围',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '需求描述',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '状态: 0-未读, 1-已读, 2-已联系',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '合作咨询表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of inquiries
-- ----------------------------
-- ----------------------------
-- Table structure for operation_logs
-- ----------------------------
DROP TABLE IF EXISTS `operation_logs`;
CREATE TABLE `operation_logs` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`user_id` bigint UNSIGNED NOT NULL COMMENT '操作用户ID',
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作用户名',
`ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作IP地址',
`path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作路径',
`method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'HTTP方法',
`params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '请求参数',
`status` int NOT NULL COMMENT '响应状态码',
`duration` int NOT NULL COMMENT '响应时间(毫秒)',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_user_id`(`user_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 720 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of operation_logs
-- ----------------------------
INSERT INTO `operation_logs` VALUES (644, 1, 'lq', '::1', '/api/admin/operation-logs', 'GET', '', 200, 52, 0, 1768807488);
INSERT INTO `operation_logs` VALUES (645, 1, 'lq', '::1', '/api/admin/dashboard/stats', 'GET', '', 200, 330, 0, 1768807489);
INSERT INTO `operation_logs` VALUES (646, 1, 'lq', '::1', '/api/admin/posts', 'GET', '', 200, 224, 0, 1768807490);
INSERT INTO `operation_logs` VALUES (647, 1, 'lq', '::1', '/api/admin/settings', 'GET', '', 200, 45, 0, 1768807512);
INSERT INTO `operation_logs` VALUES (648, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768807513);
INSERT INTO `operation_logs` VALUES (649, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 89, 0, 1768807513);
INSERT INTO `operation_logs` VALUES (650, 1, 'lq', '::1', '/api/admin/operation-logs', 'GET', '', 200, 90, 0, 1768807515);
INSERT INTO `operation_logs` VALUES (651, 1, 'lq', '::1', '/api/admin/settings', 'GET', '', 200, 45, 0, 1768807516);
INSERT INTO `operation_logs` VALUES (652, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 45, 0, 1768807569);
INSERT INTO `operation_logs` VALUES (653, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 90, 0, 1768807569);
INSERT INTO `operation_logs` VALUES (654, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 46, 0, 1768807801);
INSERT INTO `operation_logs` VALUES (655, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 91, 0, 1768807801);
INSERT INTO `operation_logs` VALUES (656, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 46, 0, 1768807805);
INSERT INTO `operation_logs` VALUES (657, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 92, 0, 1768807805);
INSERT INTO `operation_logs` VALUES (658, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 51, 0, 1768807839);
INSERT INTO `operation_logs` VALUES (659, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 98, 0, 1768807839);
INSERT INTO `operation_logs` VALUES (660, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 97, 0, 1768807843);
INSERT INTO `operation_logs` VALUES (661, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 95, 0, 1768807844);
INSERT INTO `operation_logs` VALUES (662, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 98, 0, 1768807845);
INSERT INTO `operation_logs` VALUES (663, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 95, 0, 1768807852);
INSERT INTO `operation_logs` VALUES (664, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 44, 0, 1768807858);
INSERT INTO `operation_logs` VALUES (665, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 88, 0, 1768807858);
INSERT INTO `operation_logs` VALUES (666, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 96, 0, 1768807861);
INSERT INTO `operation_logs` VALUES (667, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 94, 0, 1768807862);
INSERT INTO `operation_logs` VALUES (668, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 96, 0, 1768807863);
INSERT INTO `operation_logs` VALUES (669, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 98, 0, 1768807863);
INSERT INTO `operation_logs` VALUES (670, 1, 'lq', '::1', '/api/admin/attachments/1', 'DELETE', '', 400, 45, 0, 1768807885);
INSERT INTO `operation_logs` VALUES (671, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768807890);
INSERT INTO `operation_logs` VALUES (672, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 50, 0, 1768807890);
INSERT INTO `operation_logs` VALUES (673, 1, 'lq', '::1', '/api/admin/attachments/1', 'DELETE', '', 400, 47, 0, 1768807893);
INSERT INTO `operation_logs` VALUES (674, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 43, 0, 1768807942);
INSERT INTO `operation_logs` VALUES (675, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 91, 0, 1768807942);
INSERT INTO `operation_logs` VALUES (676, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 49, 0, 1768807943);
INSERT INTO `operation_logs` VALUES (677, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 97, 0, 1768807943);
INSERT INTO `operation_logs` VALUES (678, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 46, 0, 1768807958);
INSERT INTO `operation_logs` VALUES (679, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 91, 0, 1768807958);
INSERT INTO `operation_logs` VALUES (680, 1, 'lq', '::1', '/api/admin/operation-logs', 'GET', '', 200, 93, 0, 1768807958);
INSERT INTO `operation_logs` VALUES (681, 1, 'lq', '::1', '/api/admin/dashboard/stats', 'GET', '', 200, 309, 0, 1768807958);
INSERT INTO `operation_logs` VALUES (682, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 45, 0, 1768807962);
INSERT INTO `operation_logs` VALUES (683, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 95, 0, 1768807962);
INSERT INTO `operation_logs` VALUES (684, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 46, 0, 1768807968);
INSERT INTO `operation_logs` VALUES (685, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 95, 0, 1768807968);
INSERT INTO `operation_logs` VALUES (686, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768807994);
INSERT INTO `operation_logs` VALUES (687, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 94, 0, 1768807994);
INSERT INTO `operation_logs` VALUES (688, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 47, 0, 1768808006);
INSERT INTO `operation_logs` VALUES (689, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 96, 0, 1768808006);
INSERT INTO `operation_logs` VALUES (690, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 46, 0, 1768808082);
INSERT INTO `operation_logs` VALUES (691, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 95, 0, 1768808082);
INSERT INTO `operation_logs` VALUES (692, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 51, 0, 1768808127);
INSERT INTO `operation_logs` VALUES (693, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 50, 0, 1768808127);
INSERT INTO `operation_logs` VALUES (694, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 44, 0, 1768808155);
INSERT INTO `operation_logs` VALUES (695, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 94, 0, 1768808155);
INSERT INTO `operation_logs` VALUES (696, 1, 'lq', '::1', '/api/admin/settings', 'GET', '', 200, 47, 0, 1768808167);
INSERT INTO `operation_logs` VALUES (697, 1, 'lq', '::1', '/api/admin/oss-configs', 'GET', '', 200, 49, 0, 1768808167);
INSERT INTO `operation_logs` VALUES (698, 1, 'lq', '::1', '/api/admin/oss-configs', 'GET', '', 200, 49, 0, 1768808492);
INSERT INTO `operation_logs` VALUES (699, 1, 'lq', '::1', '/api/admin/settings', 'GET', '', 200, 49, 0, 1768808492);
INSERT INTO `operation_logs` VALUES (700, 1, 'lq', '::1', '/api/admin/oss-configs', 'POST', '{\"name\":\"默认-本地\",\"storageType\":\"local\",\"accessKey\":\"1\",\"secretKey\":\"1\",\"bucket\":\"/uploads/\",\"region\":\"locallhost\",\"domain\":\"http://locallhost:3001\",\"isActive\":1,\"createdAt\":\"\",\"updatedAt\":\"\"}', 500, 1, 0, 1768808546);
INSERT INTO `operation_logs` VALUES (701, 1, 'lq', '::1', '/api/admin/oss-configs', 'POST', '{\"name\":\"默认-本地\",\"storageType\":\"local\",\"accessKey\":\"1\",\"secretKey\":\"1\",\"bucket\":\"/uploads/\",\"region\":\"locallhost\",\"domain\":\"http://locallhost:3001\",\"isActive\":1,\"createdAt\":\"\",\"updatedAt\":\"\"}', 500, 1, 0, 1768808550);
INSERT INTO `operation_logs` VALUES (702, 1, 'lq', '::1', '/api/admin/oss-configs', 'POST', '{\"name\":\"默认-本地\",\"storageType\":\"local\",\"accessKey\":\"123123aaa\",\"secretKey\":\"123123aaa\",\"bucket\":\"/uploads/\",\"region\":\"locallhost\",\"domain\":\"http://locallhost:3001\",\"isActive\":1,\"createdAt\":\"\",\"updatedAt\":\"\"}', 500, 1, 0, 1768808572);
INSERT INTO `operation_logs` VALUES (703, 1, 'lq', '::1', '/api/admin/columns', 'GET', '', 200, 50, 0, 1768809000);
INSERT INTO `operation_logs` VALUES (704, 1, 'lq', '::1', '/api/admin/categories', 'GET', '', 200, 48, 0, 1768809017);
INSERT INTO `operation_logs` VALUES (705, 1, 'lq', '::1', '/api/admin/posts', 'GET', '', 200, 230, 0, 1768809020);
INSERT INTO `operation_logs` VALUES (706, 1, 'lq', '::1', '/api/admin/posts/6', 'PUT', '{\"title\":\"Goravel 入门指南 (三)ORM 数据库操作\",\"categoryId\":4,\"date\":\"2026-01-15\",\"excerpt\":\"掌握 Goravel 强大的 ORM 功能从数据库配置、模型定义到执行增删改查CRUD操作。\",\"content\":\"\",\"isPublished\":1,\"tags\":[{\"id\":1}],\"columnId\":1}', 200, 509, 0, 1768809133);
INSERT INTO `operation_logs` VALUES (707, 1, 'lq', '::1', '/api/admin/posts', 'GET', '', 200, 265, 0, 1768809134);
INSERT INTO `operation_logs` VALUES (708, 1, 'lq', '::1', '/api/admin/posts', 'GET', '', 200, 266, 0, 1768809309);
INSERT INTO `operation_logs` VALUES (709, 1, 'lq', '::1', '/api/admin/tags', 'POST', '{\"name\":\"入门教程\",\"slug\":\"入门教程\"}', 200, 98, 0, 1768809322);
INSERT INTO `operation_logs` VALUES (710, 1, 'lq', '::1', '/api/admin/tags', 'POST', '{\"name\":\"Golang框架\",\"slug\":\"Golang框架\"}', 200, 89, 0, 1768809341);
INSERT INTO `operation_logs` VALUES (711, 1, 'lq', '::1', '/api/admin/tags', 'POST', '{\"name\":\"前后端分离\",\"slug\":\"前后端分离\"}', 200, 91, 0, 1768809349);
INSERT INTO `operation_logs` VALUES (712, 1, 'lq', '::1', '/api/admin/posts/6', 'PUT', '{\"title\":\"Goravel 入门指南 (三)ORM 数据库操作\",\"categoryId\":4,\"date\":\"2026-01-15\",\"excerpt\":\"掌握 Goravel 强大的 ORM 功能从数据库配置、模型定义到执行增删改查CRUD操作。\",\"content\":\"\",\"isPublished\":1,\"tags\":[{\"id\":1},{\"id\":2},{\"id\":3},{\"id\":4}],\"columnId\":1}', 200, 521, 0, 1768809352);
INSERT INTO `operation_logs` VALUES (713, 1, 'lq', '::1', '/api/admin/posts', 'GET', '', 200, 289, 0, 1768809352);
INSERT INTO `operation_logs` VALUES (714, 1, 'lq', '::1', '/api/admin/categories', 'GET', '', 200, 46, 0, 1768809361);
INSERT INTO `operation_logs` VALUES (715, 1, 'lq', '::1', '/api/admin/columns', 'GET', '', 200, 49, 0, 1768809361);
INSERT INTO `operation_logs` VALUES (716, 1, 'lq', '::1', '/api/admin/posts', 'GET', '', 200, 302, 0, 1768810118);
INSERT INTO `operation_logs` VALUES (717, 1, 'lq', '::1', '/api/admin/posts', 'GET', '', 200, 286, 0, 1768810284);
INSERT INTO `operation_logs` VALUES (718, 1, 'lq', '::1', '/api/admin/attachment-categories', 'GET', '', 200, 45, 0, 1768810405);
INSERT INTO `operation_logs` VALUES (719, 1, 'lq', '::1', '/api/admin/attachments', 'GET', '', 200, 89, 0, 1768810405);
-- ----------------------------
-- Table structure for oss_configs
-- ----------------------------
DROP TABLE IF EXISTS `oss_configs`;
CREATE TABLE `oss_configs` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '配置名称',
`storage_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '存储类型: local/qcloud/aliyun/qiniu',
`access_key` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Access Key (AES加密)',
`secret_key` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Secret Key (AES加密)',
`bucket` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '存储桶名称',
`region` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '区域',
`domain` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '访问域名',
`is_active` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否启用',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_storage_type`(`storage_type` ASC) USING BTREE,
INDEX `idx_is_active`(`is_active` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OSS配置表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oss_configs
-- ----------------------------
-- ----------------------------
-- Table structure for partners
-- ----------------------------
DROP TABLE IF EXISTS `partners`;
CREATE TABLE `partners` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '合作伙伴名称',
`logo` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '合作伙伴Logo URL',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '合作伙伴介绍',
`url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '合作伙伴官网链接',
`sort_order` int UNSIGNED NULL DEFAULT 0 COMMENT '排序权重',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '合作伙伴表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of partners
-- ----------------------------
INSERT INTO `partners` VALUES (1, 'Vercel', 'https://upload.wikimedia.org/wikipedia/commons/5/5e/Vercel_logo_black.svg', '前端部署平台', 'https://vercel.com', 0, 0, 1768482993, 1768538953);
INSERT INTO `partners` VALUES (2, 'Supabase', 'https://seeklogo.com/images/S/supabase-logo-DCC676FFE2-seeklogo.com.png', '开源 Firebase 替代方案', 'https://supabase.com', 0, 0, 1768482993, 1768538953);
INSERT INTO `partners` VALUES (3, 'Stripe', 'https://upload.wikimedia.org/wikipedia/commons/b/ba/Stripe_Logo%2C_revised_2016.svg', '在线支付基础设施', 'https://stripe.com', 0, 0, 1768482993, 1768538953);
INSERT INTO `partners` VALUES (4, 'Algolia', 'https://upload.wikimedia.org/wikipedia/commons/6/69/Algolia-logo.svg', '搜索即服务 API', 'https://algolia.com', 0, 0, 1768482993, 1768538953);
INSERT INTO `partners` VALUES (5, 'Prisma', 'https://seeklogo.com/images/P/prisma-logo-3805665B69-seeklogo.com.png', '下一代 ORM', 'https://prisma.io', 0, 0, 1768482993, 1768538953);
-- ----------------------------
-- Table structure for permissions
-- ----------------------------
DROP TABLE IF EXISTS `permissions`;
CREATE TABLE `permissions` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '权限名称',
`resource` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '资源名称',
`action` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '操作名称',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `unique_resource_action`(`resource` ASC, `action` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 31 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '权限表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of permissions
-- ----------------------------
INSERT INTO `permissions` VALUES (1, 'Create User', 'users', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (2, 'Read User', 'users', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (3, 'Update User', 'users', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (4, 'Delete User', 'users', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (5, 'Create Role', 'roles', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (6, 'Read Role', 'roles', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (7, 'Update Role', 'roles', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (8, 'Delete Role', 'roles', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (9, 'Create Post', 'posts', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (10, 'Read Post', 'posts', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (11, 'Update Post', 'posts', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (12, 'Delete Post', 'posts', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (13, 'Create Work', 'works', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (14, 'Read Work', 'works', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (15, 'Update Work', 'works', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (16, 'Delete Work', 'works', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (17, 'Create Snippet', 'snippets', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (18, 'Read Snippet', 'snippets', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (19, 'Update Snippet', 'snippets', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (20, 'Delete Snippet', 'snippets', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (21, 'Create Setting', 'settings', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (22, 'Read Setting', 'settings', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (23, 'Update Setting', 'settings', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (24, 'Delete Setting', 'settings', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (25, 'Create Tag', 'tags', 'create', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (26, 'Read Tag', 'tags', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (27, 'Update Tag', 'tags', 'update', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (28, 'Delete Tag', 'tags', 'delete', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (29, 'Read Operation Log', 'operation_logs', 'read', 0, 1768452892, 1768538956);
INSERT INTO `permissions` VALUES (30, 'Read Dashboard', 'dashboard', 'read', 0, 1768452892, 1768538956);
-- ----------------------------
-- Table structure for post_history
-- ----------------------------
DROP TABLE IF EXISTS `post_history`;
CREATE TABLE `post_history` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '历史记录ID',
`post_id` int UNSIGNED NOT NULL COMMENT '文章ID',
`version` int UNSIGNED NOT NULL DEFAULT 1 COMMENT '版本号',
`title` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文章标题',
`category_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '文章分类ID',
`excerpt` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '文章摘要',
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文章内容',
`is_published` tinyint(1) NULL DEFAULT 1 COMMENT '是否已发布',
`modified_by` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '修改人ID',
`modified_at` bigint NOT NULL DEFAULT 0 COMMENT '修改时间',
`created_at` bigint NOT NULL DEFAULT 0,
`deleted_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_post_id`(`post_id` ASC) USING BTREE,
INDEX `idx_version`(`version` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文章历史记录表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of post_history
-- ----------------------------
INSERT INTO `post_history` VALUES (1, 6, 1, 'Goravel 入门指南 (三)ORM 数据库操作', 4, '掌握 Goravel 强大的 ORM 功能从数据库配置、模型定义到执行增删改查CRUD操作。', '', 1, 1, 1768809133, 1768809133, 0);
INSERT INTO `post_history` VALUES (2, 6, 2, 'Goravel 入门指南 (三)ORM 数据库操作', 4, '掌握 Goravel 强大的 ORM 功能从数据库配置、模型定义到执行增删改查CRUD操作。', '', 1, 1, 1768809352, 1768809352, 0);
-- ----------------------------
-- Table structure for post_tags
-- ----------------------------
DROP TABLE IF EXISTS `post_tags`;
CREATE TABLE `post_tags` (
`tag_id` bigint UNSIGNED NOT NULL COMMENT '关联的标签ID',
`post_id` int UNSIGNED NOT NULL COMMENT '关联的文章ID',
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`post_id`, `tag_id`) USING BTREE,
INDEX `idx_tag_id`(`tag_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文章标签关联表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of post_tags
-- ----------------------------
INSERT INTO `post_tags` VALUES (1, 6, 0);
INSERT INTO `post_tags` VALUES (2, 6, 0);
INSERT INTO `post_tags` VALUES (3, 6, 0);
INSERT INTO `post_tags` VALUES (4, 6, 0);
-- ----------------------------
-- Table structure for posts
-- ----------------------------
DROP TABLE IF EXISTS `posts`;
CREATE TABLE `posts` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '文章唯一标识自增ID',
`original_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '原始字符串ID备份',
`title` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文章标题',
`category_id` int UNSIGNED NOT NULL DEFAULT 0 COMMENT '文章分类ID',
`column_id` int UNSIGNED NULL DEFAULT NULL COMMENT '专栏ID可选',
`excerpt` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '文章摘要',
`content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '文章内容',
`read_count` int UNSIGNED NULL DEFAULT 0 COMMENT '阅读量',
`is_published` tinyint(1) NULL DEFAULT 1 COMMENT '是否已发布0草稿1已发布',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_category_id`(`category_id` ASC) USING BTREE COMMENT '按分类查询索引',
INDEX `idx_column_id`(`column_id` ASC) USING BTREE COMMENT '按专栏查询索引',
INDEX `idx_is_published`(`is_published` ASC) USING BTREE COMMENT '按发布状态查询索引',
INDEX `idx_original_id`(`original_id` ASC) USING BTREE,
FULLTEXT INDEX `idx_title_content`(`title`, `content`) COMMENT '标题和内容全文索引,用于搜索'
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '博客文章表(新结构)' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of posts
-- ----------------------------
INSERT INTO `posts` VALUES (1, 'refactor', '重构的艺术:如何优雅地处理遗留代码', 4, NULL, '在本文中,我们将探讨重构的核心原则和实用技巧,帮助你优雅地处理遗留代码,提高代码质量和可维护性。', '<h2>什么是代码重构?</h2><p>代码重构是在不改变代码外部行为的前提下,优化代码内部结构的过程...</p>', 5, 1, 0, 1768291814, 1768538949);
INSERT INTO `posts` VALUES (2, 'shader', '着色器魔法:从零开始写一个噪声生成器', 2, NULL, '深入了解WebGL着色器学习如何从零开始实现一个高性能的噪声生成器为你的3D作品增添独特的视觉效果。', ' <p>GLSL (OpenGL Shading Language) 是一门让人生畏但也充满魅力的语言。它运行在 GPU 上,能够并行处理数百万个像素,创造出惊人的视觉效果。</p>\r\n <h2>什么是柏林噪声?</h2>\r\n <p>柏林噪声Perlin Noise是一种梯度噪声它比普通的随机数生成的噪声看起来更自然、更平滑。它常被用来模拟云彩、地形、火焰等自然现象。</p>\r\n <h2>Three.js 中的实现</h2>\r\n <p>在 Three.js 中,我们可以通过 <code>ShaderMaterial</code> 直接编写 GLSL 代码。</p>\r\n <pre><code><span class=\"code-comment\">// 简单的顶点着色器</span>\r\n<span class=\"code-keyword\">varying</span> <span class=\"code-keyword\">vec2</span> vUv;\r\n<span class=\"code-keyword\">void</span> <span class=\"code-func\">main</span>() {\r\n vUv = uv;\r\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\r\n}</code></pre>\r\n <p>通过调整噪声的频率和振幅,我们可以得到各种不同的纹理效果。在我的个人网站背景中,就使用了这种技术来生成流动的极光效果。</p>\r\n ', 1, 1, 0, 1768291815, 1768538949);
INSERT INTO `posts` VALUES (3, 'ux', '用户体验设计:从认知心理学到交互实践', 3, NULL, '探索用户体验设计的核心原理,结合认知心理学知识,学习如何设计出真正符合用户需求的交互界面。', '<h2>认知心理学在UX设计中的应用</h2><p>了解用户的认知过程是设计良好用户体验的基础...</p>', 0, 1, 0, 1768291816, 1768538949);
INSERT INTO `posts` VALUES (4, 'goravel-guide-1', 'Goravel 入门指南 (一):环境搭建与 Hello World', 4, 1, '本文将带你了解 Go 语言领域的 Laravel —— Goravel 框架,并演示如何快速搭建环境及运行第一个 Web 服务。', '## 什么是 Goravel\n\nGoravel 是一个基于 Go 语言的 Web 开发框架,它的设计理念深受 PHP Laravel 框架的启发。如果你是一名从 PHP 转 Go 的开发者,或者你喜欢 Laravel 那种“开箱即用、优雅简洁”的开发体验,那么 Goravel 绝对是你的不二之选。\n\n它集成了丰富的功能模块包括但不限于\n- 强大的路由系统\n- ORM基于 GORM 封装)\n- 依赖注入容器\n- 队列与任务调度\n- 缓存与文件存储\n\n## 环境搭建\n\nGoravel 提供了一个名为 `knit` 的命令行工具(类似 Laravel 的 artisan可以帮助我们快速初始化项目。\n\n### 1. 安装 Knit CLI\n\n确保你已经安装了 Go (1.20+),然后运行以下命令:\n\n```bash\ngo install github.com/goravel/knit/cmd/knit@latest\n```\n\n### 2. 创建新项目\n\n使用 `knit new` 命令创建项目:\n\n```bash\nknit new my-goravel-app\ncd my-goravel-app\n```\n\n### 3. 安装依赖\n\n```bash\ngo mod tidy\n```\n\n## 目录结构\n\n打开项目你会发现它的目录结构非常清晰带有浓厚的 Laravel 风格:\n\n- **app/**: 核心业务代码Http 控制器、模型、服务提供者等)\n- **config/**: 配置文件(应用配置、数据库配置等)\n- **routes/**: 路由定义文件\n- **database/**: 数据库迁移与填充\n- **public/**: 静态资源文件\n\n## 运行 Hello World\n\nGoravel 的入口文件是根目录下的 `main.go`。在运行之前,我们先看一眼路由定义。打开 `routes/web.go`\n\n```go\npackage routes\n\nimport (\n \"github.com/goravel/framework/contracts/http\"\n \"github.com/goravel/framework/facades\"\n)\n\nfunc Web() {\n facades.Route().Get(\"/\", func(ctx http.Context) http.Response {\n return ctx.Response().Json(200, http.Json{\n \"Hello\": \"Goravel\",\n })\n })\n}\n```\n\n非常直观现在让我们启动服务\n\n```bash\ngo run .\n```\n\n默认情况下服务会运行在 `http://localhost:3000`。打开浏览器访问,你应该能看到 JSON 响应:\n\n```json\n{\n \"Hello\": \"Goravel\"\n}\n```\n\n至此你已经成功运行了你的第一个 Goravel 应用!在下一篇文章中,我们将深入探讨路由与控制器的使用。', 1210, 1, 0, 1768465932, 1768538949);
INSERT INTO `posts` VALUES (5, 'goravel-guide-2', 'Goravel 入门指南 (二):路由与控制器', 4, 1, '深入理解 Goravel 的 HTTP 层,学习如何定义 RESTful 路由、创建控制器以及处理 HTTP 请求与响应。', '## 路由系统\n\n在 Goravel 中,路由定义通常位于 `routes/` 目录下。`api.go` 用于定义 API 路由,`web.go` 用于定义网页路由。Goravel 使用 `facades.Route()` 来定义路由,这得益于其强大的依赖注入系统。\n\n### 基础路由\n\n```go\n// GET 请求\nfacades.Route().Get(\"/users\", func(ctx http.Context) http.Response {\n return ctx.Response().String(200, \"User List\")\n})\n\n// POST 请求\nfacades.Route().Post(\"/users\", func(ctx http.Context) http.Response {\n return ctx.Response().Success().Json(http.Json{\"id\": 1})\n})\n```\n\n### 路由参数\n\n获取 URL 中的动态参数非常简单:\n\n```go\nfacades.Route().Get(\"/users/{id}\", func(ctx http.Context) http.Response {\n id := ctx.Request().Input(\"id\")\n return ctx.Response().Success().Json(http.Json{\"user_id\": id})\n})\n```\n\n## 控制器 (Controllers)\n\n随着应用变大我们不可能把所有逻辑都写在路由闭包里。这时候就需要控制器了。\n\n### 创建控制器\n\n使用 `knit` 工具可以快速生成控制器:\n\n```bash\nknit make:controller UserController\n```\n\n这会在 `app/http/controllers` 目录下生成 `user_controller.go`。让我们修改它来添加一个 `Show` 方法:\n\n```go\npackage controllers\n\nimport (\n \"github.com/goravel/framework/contracts/http\"\n)\n\ntype UserController struct {\n // 可以在这里注入服务\n}\n\nfunc NewUserController() *UserController {\n return &UserController{}\n}\n\nfunc (r *UserController) Show(ctx http.Context) http.Response {\n id := ctx.Request().Input(\"id\")\n // 模拟数据库查询\n return ctx.Response().Success().Json(http.Json{\n \"id\": id,\n \"name\": \"Goravel User\",\n })\n}\n```\n\n### 注册控制器路由\n\n回到 `routes/api.go`,我们需要先实例化控制器,然后绑定路由:\n\n```go\nimport \"my-goravel-app/app/http/controllers\"\n\nfunc Api() {\n userController := controllers.NewUserController()\n \n // 绑定到控制器方法\n facades.Route().Get(\"/users/{id}\", userController.Show)\n}\n```\n\n## 请求与响应\n\n在控制器方法中`ctx` (http.Context) 是核心:\n\n- **获取输入**: `ctx.Request().Input(\"key\")`\n- **获取 JSON**: `ctx.Request().Bind(&user)`\n- **返回 JSON**: `ctx.Response().Json(200, data)`\n- **设置状态码**: `ctx.Response().Status(404)`\n\n通过这种方式Goravel 让 HTTP 层的处理变得异常清晰和标准化。下一章,我们将学习如何通过 ORM 操作数据库。', 903, 1, 0, 1768465933, 1768538949);
INSERT INTO `posts` VALUES (6, 'goravel-guide-3', 'Goravel 入门指南 (三)ORM 数据库操作', 4, 1, '掌握 Goravel 强大的 ORM 功能从数据库配置、模型定义到执行增删改查CRUD操作。', '## Goravel ORM 简介\r\nGoravel 的 ORM 基于著名的 GORM 库构建,但它进行了一层优雅的封装,使其调用方式更接近 Laravel 的 Eloquent ORM。这意味着你可以使用 Facade 模式轻松调用数据库,且支持自动迁移。\r\n## 配置数据库\r\n打开 `config/database.go` 或 `.env` 文件,配置你的 MySQL 连接信息:\r\n```env\r\nDB_CONNECTION=mysql\r\nDB_HOST=127.0.0.1\r\nDB_PORT=3306\r\nDB_DATABASE=goravel\r\nDB_USERNAME=root\r\nDB_PASSWORD=password\r\n```\r\n## 定义模型\r\n使用 `knit` 生成模型:\r\n```bash\r\nknit make:model Post\r\n```\r\n生成的模型文件位于 `app/models/post.go`。我们需要定义结构体字段:\r\n```go\r\npackage models\r\nimport (\r\n \\\"github.com/goravel/framework/database/orm\\\"\r\n )\r\ntype Post struct {\r\n orm.Model\r\n Title string `gorm:\\\"size:255;not null\\\"`\r\n Content string `gorm:\\\"type:text\\\"`\r\n UserID uint\r\n }\r\n ```\r\n## 数据库迁移\r\n虽然 GORM 支持 AutoMigrate但 Goravel 推荐使用迁移文件来管理数据库变更。\r\n```bash\r\nknit make:migration create_posts_table\r\n```\r\n编辑生成的迁移文件 (`database/migrations/xxxx_create_posts_table.go`),定义表结构,然后运行:\r\n```bash\r\nknit migrate\r\n```\r\n## CRUD 操作\r\n有了模型我们就可以愉快地操作数据库了。所有操作都通过 `facades.Orm()` 入口。\r\n### 创建 (Create)\r\n```go\r\npost := models.Post{\r\n Title: \\\"My First Post\\\",\r\n Content: \\\"Content goes here...\\\",\r\n }\r\n err := facades.Orm().Query().Create(&post)\r\n ```\r\n### 查询 (Read)\r\n```go\r\nvar post models.Post\r\n// 根据主键查询\r\nfacades.Orm().Query().Find(&post, 1)\r\n// 条件查询\r\nvar posts []models.Post\r\nfacades.Orm().Query().Where(\\\"title\\\", \\\"My First Post\\\").Get(&posts)\r\n```\r\n### 更新 (Update)\r\n```go\r\nvar post models.Post\r\nfacades.Orm().Query().Find(&post, 1)\r\npost.Title = \\\"Updated Title\\\"\r\nfacades.Orm().Query().Save(&post)\r\n```\r\n### 删除 (Delete)\r\n```go\r\nvar post models.Post\r\nfacades.Orm().Query().Delete(&post, 1)\r\n```\r\nGoravel 的 ORM 让 Go 语言的数据库操作不再繁琐,极大地提高了开发效率。配合之前的路由和控制器知识,你现在已经具备了开发完整 RESTful API 的能力!', 1586, 1, 0, 1768465934, 1768809351);
-- ----------------------------
-- Table structure for role_permissions
-- ----------------------------
DROP TABLE IF EXISTS `role_permissions`;
CREATE TABLE `role_permissions` (
`role_id` bigint UNSIGNED NOT NULL COMMENT '角色ID',
`permission_id` bigint UNSIGNED NOT NULL COMMENT '权限ID',
PRIMARY KEY (`role_id`, `permission_id`) USING BTREE,
INDEX `role_permissions_ibfk_2`(`permission_id` ASC) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色权限关联表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of role_permissions
-- ----------------------------
INSERT INTO `role_permissions` VALUES (1, 1);
INSERT INTO `role_permissions` VALUES (1, 2);
INSERT INTO `role_permissions` VALUES (3, 2);
INSERT INTO `role_permissions` VALUES (1, 3);
INSERT INTO `role_permissions` VALUES (1, 4);
INSERT INTO `role_permissions` VALUES (1, 5);
INSERT INTO `role_permissions` VALUES (1, 6);
INSERT INTO `role_permissions` VALUES (3, 6);
INSERT INTO `role_permissions` VALUES (1, 7);
INSERT INTO `role_permissions` VALUES (1, 8);
INSERT INTO `role_permissions` VALUES (1, 9);
INSERT INTO `role_permissions` VALUES (2, 9);
INSERT INTO `role_permissions` VALUES (1, 10);
INSERT INTO `role_permissions` VALUES (2, 10);
INSERT INTO `role_permissions` VALUES (3, 10);
INSERT INTO `role_permissions` VALUES (1, 11);
INSERT INTO `role_permissions` VALUES (2, 11);
INSERT INTO `role_permissions` VALUES (1, 12);
INSERT INTO `role_permissions` VALUES (2, 12);
INSERT INTO `role_permissions` VALUES (1, 13);
INSERT INTO `role_permissions` VALUES (1, 14);
INSERT INTO `role_permissions` VALUES (3, 14);
INSERT INTO `role_permissions` VALUES (1, 15);
INSERT INTO `role_permissions` VALUES (1, 16);
INSERT INTO `role_permissions` VALUES (1, 17);
INSERT INTO `role_permissions` VALUES (1, 18);
INSERT INTO `role_permissions` VALUES (3, 18);
INSERT INTO `role_permissions` VALUES (1, 19);
INSERT INTO `role_permissions` VALUES (1, 20);
INSERT INTO `role_permissions` VALUES (1, 21);
INSERT INTO `role_permissions` VALUES (1, 22);
INSERT INTO `role_permissions` VALUES (3, 22);
INSERT INTO `role_permissions` VALUES (1, 23);
INSERT INTO `role_permissions` VALUES (1, 24);
INSERT INTO `role_permissions` VALUES (1, 25);
INSERT INTO `role_permissions` VALUES (1, 26);
INSERT INTO `role_permissions` VALUES (3, 26);
INSERT INTO `role_permissions` VALUES (1, 27);
INSERT INTO `role_permissions` VALUES (1, 28);
INSERT INTO `role_permissions` VALUES (1, 29);
INSERT INTO `role_permissions` VALUES (3, 29);
INSERT INTO `role_permissions` VALUES (1, 30);
INSERT INTO `role_permissions` VALUES (3, 30);
-- ----------------------------
-- Table structure for roles
-- ----------------------------
DROP TABLE IF EXISTS `roles`;
CREATE TABLE `roles` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '角色名称',
`description` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '角色描述',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `name`(`name` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of roles
-- ----------------------------
INSERT INTO `roles` VALUES (1, 'admin', '系统管理员', 0, 1768452892, 1768538956);
INSERT INTO `roles` VALUES (2, 'editor', '内容编辑', 0, 1768452892, 1768538956);
INSERT INTO `roles` VALUES (3, 'viewer', '普通访客', 0, 1768452892, 1768538956);
-- ----------------------------
-- Table structure for search_logs
-- ----------------------------
DROP TABLE IF EXISTS `search_logs`;
CREATE TABLE `search_logs` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`keyword` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '搜索关键词',
`search_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '搜索类型: category/tag/column/keyword',
`user_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户IP地址',
`user_location` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '用户归属地',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_search_type`(`search_type` ASC) USING BTREE,
INDEX `idx_created_at`(`created_at` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '搜索记录表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of search_logs
-- ----------------------------
INSERT INTO `search_logs` VALUES (1, '4', 'category', '::1', 'Internal', 0, 1768808440);
INSERT INTO `search_logs` VALUES (2, '4', 'category', '::1', 'Internal', 0, 1768808446);
INSERT INTO `search_logs` VALUES (3, '', 'keyword', '::1', 'Internal', 0, 1768808446);
INSERT INTO `search_logs` VALUES (4, '4', 'category', '::1', 'Internal', 0, 1768808447);
-- ----------------------------
-- Table structure for settings
-- ----------------------------
DROP TABLE IF EXISTS `settings`;
CREATE TABLE `settings` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`key_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '配置项键名',
`value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '配置项值',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '配置项描述',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `key_name`(`key_name` ASC) USING BTREE,
INDEX `idx_key_name`(`key_name` ASC) USING BTREE COMMENT '按键名查询索引'
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '网站配置表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of settings
-- ----------------------------
INSERT INTO `settings` VALUES (1, 'site_title', '年糕博客', '网站标题', 0, 1768291814, 1768538952);
INSERT INTO `settings` VALUES (2, 'site_description', '分享前端技术、交互设计以及数字艺术的深度思考', '网站描述', 0, 1768291814, 1768538952);
INSERT INTO `settings` VALUES (3, 'site_author', '年糕崽崽', '网站作者', 0, 1768291814, 1768538952);
INSERT INTO `settings` VALUES (4, 'site_keywords', '前端, 设计, 技术博客', '网站关键词', 0, 1768291814, 1768538952);
INSERT INTO `settings` VALUES (5, 'posts_per_page', '10', '每页显示的文章数量', 0, 1768291814, 1768538952);
INSERT INTO `settings` VALUES (6, 'works_per_page', '6', '每页显示的作品数量', 0, 1768291814, 1768538952);
INSERT INTO `settings` VALUES (7, 'snippets_per_page', '8', '每页显示的代码片段数量', 0, 1768291814, 1768538952);
-- ----------------------------
-- Table structure for snippets
-- ----------------------------
DROP TABLE IF EXISTS `snippets`;
CREATE TABLE `snippets` (
`id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '代码片段唯一标识',
`title` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '代码片段标题',
`code` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '代码内容',
`type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '代码类型javascript、css、html等',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '代码片段描述',
`view_count` int UNSIGNED NULL DEFAULT 0 COMMENT '查看次数',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_type`(`type` ASC) USING BTREE COMMENT '按代码类型查询索引',
INDEX `idx_view_count`(`view_count` ASC) USING BTREE COMMENT '按查看次数查询索引'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码片段表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of snippets
-- ----------------------------
INSERT INTO `snippets` VALUES ('1', 'React 鼠标追踪 Hook', 'import { useState, useEffect } from \'react\';\r\n\r\nexport const useMousePosition = () => {\r\n const [pos, setPos] = useState({ x: 0, y: 0 });\r\n useEffect(() => {\r\n const update = (e) => setPos({ x: e.clientX, y: e.clientY });\r\n window.addEventListener(\'mousemove\', update);\r\n return () => window.removeEventListener(\'mousemove\', update);\r\n }, []);\r\n return pos;\r\n};', 'mouse', '这是一个鼠标追踪', 2, 0, 1768350739, 1768538952);
-- ----------------------------
-- Table structure for tags
-- ----------------------------
DROP TABLE IF EXISTS `tags`;
CREATE TABLE `tags` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '标签名称',
`slug` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '标签别名用于URL',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `name`(`name` ASC) USING BTREE,
UNIQUE INDEX `slug`(`slug` ASC) USING BTREE,
INDEX `idx_slug`(`slug` ASC) USING BTREE COMMENT '按别名查询索引'
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '标签表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of tags
-- ----------------------------
INSERT INTO `tags` VALUES (1, 'Goravel', '', 0, 1768550858, 1768550858);
INSERT INTO `tags` VALUES (2, '入门教程', '入门教程', 0, 1768809322, 1768809322);
INSERT INTO `tags` VALUES (3, 'Golang框架', 'Golang框架', 0, 1768809341, 1768809341);
INSERT INTO `tags` VALUES (4, '前后端分离', '前后端分离', 0, 1768809349, 1768809349);
-- ----------------------------
-- Table structure for testimonials
-- ----------------------------
DROP TABLE IF EXISTS `testimonials`;
CREATE TABLE `testimonials` (
`id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '客户姓名',
`role` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '客户职位',
`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '评价内容',
`avatar` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '客户头像URL',
`rating` tinyint UNSIGNED NULL DEFAULT 5 COMMENT '评分(1-5)',
`sort_order` int UNSIGNED NULL DEFAULT 0 COMMENT '排序权重',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '客户评价表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of testimonials
-- ----------------------------
INSERT INTO `testimonials` VALUES (1, 'Alex Chen', 'Product Owner @ TechFlow', '年糕不仅技术过硬,对设计细节的把控更是令人惊叹。', 'https://api.dicebear.com/7.x/avataaars/svg?seed=Alex', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (2, 'Sarah Wu', 'Design Director @ ArtSpace', '很少见到能把代码写得像诗一样的工程师,合作非常愉快!', 'https://api.dicebear.com/7.x/avataaars/svg?seed=Sarah', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (3, 'Mike Zhang', 'CTO @ FutureWave', '交付质量远超预期,特别是在性能优化方面做得非常出色。', 'https://api.dicebear.com/7.x/avataaars/svg?seed=Mike', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (4, 'Jessica Li', 'Founder @ ZenMode', '从交互动效到整体架构,都体现了极高的专业水准。', 'https://api.dicebear.com/7.x/avataaars/svg?seed=Jessica', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (5, 'David Wang', 'Tech Lead @ Innovate', '代码结构清晰,注释完善,后续维护非常轻松。', 'https://api.dicebear.com/7.x/avataaars/svg?seed=David', 5, 0, 0, 1768482993, 1768538954);
INSERT INTO `testimonials` VALUES (6, '', 'CTO', '服务很贴心,技术够硬', 'https://api.dicebear.com/7.x/avataaars/svg?seed=David', 5, 0, 0, 20260116130552, 20260116130552);
-- ----------------------------
-- Table structure for user_access_logs
-- ----------------------------
DROP TABLE IF EXISTS `user_access_logs`;
CREATE TABLE `user_access_logs` (
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NULL DEFAULT 0 COMMENT '用户ID未登录用户为0',
`user_ip` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户IP地址',
`user_location` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户归属地',
`article_id` int NOT NULL COMMENT '访问的文章ID',
`deleted_at` bigint NOT NULL DEFAULT 0,
`access_time` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_user_id`(`user_id` ASC) USING BTREE,
INDEX `idx_article_id`(`article_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '用户访问记录表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of user_access_logs
-- ----------------------------
INSERT INTO `user_access_logs` VALUES (13, 0, '::1', 'Unknown', 6, 0, 20260116160835);
INSERT INTO `user_access_logs` VALUES (14, 0, '::1', 'Unknown', 6, 0, 20260116165800);
INSERT INTO `user_access_logs` VALUES (15, 0, '::1', 'Unknown', 6, 0, 20260119133107);
INSERT INTO `user_access_logs` VALUES (16, 0, '::1', 'Unknown', 6, 0, 20260119133147);
INSERT INTO `user_access_logs` VALUES (17, 0, '::1', 'Unknown', 6, 0, 20260119133216);
INSERT INTO `user_access_logs` VALUES (18, 0, '::1', 'Internal', 6, 0, 1768801962);
INSERT INTO `user_access_logs` VALUES (19, 1, '::1', 'Internal', 0, 0, 1768807318);
INSERT INTO `user_access_logs` VALUES (20, 1, '::1', 'Internal', 0, 0, 1768807357);
INSERT INTO `user_access_logs` VALUES (21, 0, '::1', 'Internal', 6, 0, 1768807466);
INSERT INTO `user_access_logs` VALUES (22, 1, '::1', 'Internal', 0, 0, 1768807488);
INSERT INTO `user_access_logs` VALUES (23, 0, '::1', 'Internal', 6, 0, 1768807765);
INSERT INTO `user_access_logs` VALUES (24, 0, '::1', 'Internal', 6, 0, 1768807777);
INSERT INTO `user_access_logs` VALUES (25, 0, '::1', 'Internal', 6, 0, 1768807795);
INSERT INTO `user_access_logs` VALUES (26, 0, '::1', 'Internal', 6, 0, 1768809365);
INSERT INTO `user_access_logs` VALUES (27, 0, '::1', 'Internal', 6, 0, 1768809378);
INSERT INTO `user_access_logs` VALUES (28, 0, '::1', 'Internal', 6, 0, 1768809388);
INSERT INTO `user_access_logs` VALUES (29, 0, '::1', 'Internal', 6, 0, 1768809694);
INSERT INTO `user_access_logs` VALUES (30, 0, '::1', 'Internal', 6, 0, 1768809805);
INSERT INTO `user_access_logs` VALUES (31, 0, '::1', 'Internal', 5, 0, 1768809830);
INSERT INTO `user_access_logs` VALUES (32, 0, '::1', 'Internal', 6, 0, 1768809866);
INSERT INTO `user_access_logs` VALUES (33, 0, '::1', 'Internal', 6, 0, 1768809889);
INSERT INTO `user_access_logs` VALUES (34, 0, '::1', 'Internal', 6, 0, 1768809926);
INSERT INTO `user_access_logs` VALUES (35, 0, '::1', 'Internal', 5, 0, 1768809977);
INSERT INTO `user_access_logs` VALUES (36, 0, '::1', 'Internal', 6, 0, 1768810062);
INSERT INTO `user_access_logs` VALUES (37, 0, '::1', 'Internal', 6, 0, 1768810115);
INSERT INTO `user_access_logs` VALUES (38, 0, '::1', 'Internal', 6, 0, 1768810120);
INSERT INTO `user_access_logs` VALUES (39, 0, '::1', 'Internal', 6, 0, 1768810276);
INSERT INTO `user_access_logs` VALUES (40, 0, '::1', 'Internal', 6, 0, 1768810279);
-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名',
`email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮箱地址',
`password_hash` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码哈希值',
`role_id` bigint UNSIGNED NULL DEFAULT NULL COMMENT '角色ID',
`role` enum('admin','editor','viewer') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT 'viewer' COMMENT '用户角色(兼容旧版)',
`is_active` tinyint(1) NULL DEFAULT 1 COMMENT '是否激活0禁用1激活',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `username`(`username` ASC) USING BTREE,
UNIQUE INDEX `email`(`email` ASC) USING BTREE,
INDEX `idx_username`(`username` ASC) USING BTREE COMMENT '按用户名查询索引',
INDEX `idx_email`(`email` ASC) USING BTREE COMMENT '按邮箱查询索引',
INDEX `idx_role`(`role` ASC) USING BTREE COMMENT '按角色查询索引',
INDEX `users_ibfk_1`(`role_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES (1, 'lq', 'liqiworker@gmail.com', '$2a$10$Bq6rv7714W3jGyXruYx4puXjflMTSkq2QM9kF54x9iZnk.0AD4B1G', 1, 'admin', 1, 0, 1768291814, 1768538951);
INSERT INTO `users` VALUES (2, 'editor', 'editor@example.com', '$2a$14$OqZ7svgz3S68yuuXTJWz1.0CuqI2i3WvwC1jIGBy4l0mcJW9Kqvy2', 2, 'editor', 1, 0, 1768291814, 1768538951);
INSERT INTO `users` VALUES (5, 'cs', 'cs@nailaoyun.cn', '$2a$14$OqZ7svgz3S68yuuXTJWz1.0CuqI2i3WvwC1jIGBy4l0mcJW9Kqvy2', 3, 'viewer', 1, 0, 1768462115, 1768538951);
-- ----------------------------
-- Table structure for work_gallery
-- ----------------------------
DROP TABLE IF EXISTS `work_gallery`;
CREATE TABLE `work_gallery` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`work_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联的作品ID',
`image_url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '图片URL',
`sort_order` int UNSIGNED NULL DEFAULT 0 COMMENT '排序顺序,数值越小越靠前',
`description` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '图片描述',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_work_id`(`work_id` ASC) USING BTREE,
INDEX `idx_sort_order`(`sort_order` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '作品图库表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of work_gallery
-- ----------------------------
INSERT INTO `work_gallery` VALUES (1, 'nova', 'https://images.unsplash.com/photo-1642543492481-44e81e3914a7?q=80&w=2070', 1, 'Nova 交易平台首页', 0, 1768291937);
INSERT INTO `work_gallery` VALUES (2, 'nova', 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?q=80&w=2070', 2, 'Nova 交易平台交易界面', 0, 1768291937);
INSERT INTO `work_gallery` VALUES (3, 'archdaily', 'https://images.unsplash.com/photo-1503387762-592deb58ef4e?q=80&w=2089', 1, 'ArchDaily 网站首页', 0, 1768291937);
INSERT INTO `work_gallery` VALUES (4, 'archdaily', 'https://images.unsplash.com/photo-1518005020951-ecc859466abc?q=80&w=1920', 2, 'ArchDaily 文章详情页', 0, 1768291937);
-- ----------------------------
-- Table structure for work_tech_stack
-- ----------------------------
DROP TABLE IF EXISTS `work_tech_stack`;
CREATE TABLE `work_tech_stack` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`work_id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '关联的作品ID',
`category` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '技术分类(如:前端、后端、数据库等)',
`item` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '具体技术项Vue 3、Golang、MySQL等',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_work_id`(`work_id` ASC) USING BTREE,
INDEX `idx_category`(`category` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '作品技术栈表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of work_tech_stack
-- ----------------------------
INSERT INTO `work_tech_stack` VALUES (1, 'nova', '前端层', 'React 18', 0, 1768291937);
INSERT INTO `work_tech_stack` VALUES (2, 'nova', '前端层', 'TypeScript', 0, 1768291937);
INSERT INTO `work_tech_stack` VALUES (3, 'nova', '前端层', 'D3.js', 0, 1768291937);
INSERT INTO `work_tech_stack` VALUES (4, 'nova', '后端服务', 'Golang', 0, 1768291937);
INSERT INTO `work_tech_stack` VALUES (5, 'nova', '后端服务', 'gRPC', 0, 1768291937);
INSERT INTO `work_tech_stack` VALUES (6, 'archdaily', '核心前端', 'Vue 3', 0, 1768291937);
INSERT INTO `work_tech_stack` VALUES (7, 'archdaily', '核心前端', 'Nuxt.js', 0, 1768291937);
INSERT INTO `work_tech_stack` VALUES (8, 'archdaily', '核心前端', 'GSAP', 0, 1768291937);
INSERT INTO `work_tech_stack` VALUES (9, 'archdaily', 'CMS', 'Strapi', 0, 1768291937);
-- ----------------------------
-- Table structure for works
-- ----------------------------
DROP TABLE IF EXISTS `works`;
CREATE TABLE `works` (
`id` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作品唯一标识',
`title` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作品标题',
`category` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作品分类',
`year` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '创作年份',
`hero_img` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作品主图URL',
`description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '作品详细描述',
`is_featured` tinyint(1) NULL DEFAULT 0 COMMENT '是否为精选作品01',
`deleted_at` bigint NOT NULL DEFAULT 0,
`created_at` bigint NOT NULL DEFAULT 0,
`updated_at` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_category`(`category` ASC) USING BTREE COMMENT '按分类查询索引',
INDEX `idx_year`(`year` ASC) USING BTREE COMMENT '按年份查询索引',
INDEX `idx_is_featured`(`is_featured` ASC) USING BTREE COMMENT '按精选状态查询索引'
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '作品表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of works
-- ----------------------------
INSERT INTO `works` VALUES ('archdaily', 'ArchDaily 网站重构', '建筑设计', '2022', 'https://images.unsplash.com/photo-1487958449943-2429e8be8625?q=80&w=2070', 'ArchDaily 是全球最受欢迎的建筑网站之一。这次重构的目标是提升移动端体验。我们使用了 Nuxt 3 进行服务端渲染SSR', 1, 0, 1768291814, 1768538952);
INSERT INTO `works` VALUES ('nova', 'Nova 交易平台', '金融科技', '2023', 'https://images.unsplash.com/photo-1487958449943-2429e8be8625?q=80&w=2070', 'Nova 是一个专为机构交易员设计的高频交易终端。我们面临的最大挑战是如何在处理毫秒级市场数据的同时,保持界面的流畅响应。我们采用 Web Worker 来处理繁重的数据计算,避免阻塞主线程。', 1, 0, 1768291814, 1768538952);
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -1,11 +1,15 @@
package repositories
import (
"encoding/json"
"fmt"
"log"
"strconv"
"time"
"github.com/niangaodev/art-code/config"
"github.com/niangaodev/art-code/models"
"github.com/sergi/go-diff/diffmatchpatch"
"gorm.io/gorm"
)
@@ -91,14 +95,17 @@ func GetPostByID(id uint) (*models.Post, error) {
return nil, err
}
// 更新阅读量
config.DB.Model(&models.Post{}).
Where("id = ?", id).
UpdateColumn("read_count", gorm.Expr("read_count + ?", 1))
// 不再在此处更新阅读量,由 handler 按小时去重后递增
return &post, nil
}
// IncrementReadCount 递增文章阅读量
func IncrementReadCount(id uint) error {
return config.DB.Model(&models.Post{}).
Where("id = ?", id).
UpdateColumn("read_count", gorm.Expr("read_count + ?", 1)).Error
}
// GetAllPosts 获取所有博客文章(包括未发布的,后台用,支持搜索)
func GetAllPosts(page, pageSize int, keyword string) ([]models.Post, int64, error) {
offset := (page - 1) * pageSize
@@ -408,11 +415,19 @@ func SavePostHistory(post *models.Post, modifiedBy uint) error {
return err
}
tagIDs := make([]uint, 0, len(post.Tags))
for _, tag := range post.Tags {
tagIDs = append(tagIDs, tag.ID)
}
tagJSON, _ := json.Marshal(tagIDs)
history := &models.PostHistory{
PostID: post.ID,
Version: maxVersion + 1,
Title: post.Title,
CategoryID: post.CategoryID,
ColumnID: post.ColumnID,
TagIDs: string(tagJSON),
Excerpt: post.Excerpt,
Content: post.Content,
IsPublished: post.IsPublished,
@@ -541,30 +556,163 @@ func GetPostHistoryByVersion(postID uint, version uint) (*models.PostHistory, er
}
// BuildPostHistoryResponse 构建历史记录响应
func BuildPostHistoryResponse(h *models.PostHistory) *models.PostHistoryResponse {
return &models.PostHistoryResponse{
func BuildPostHistoryResponse(h *models.PostHistory, includeFull bool) *models.PostHistoryResponse {
resp := &models.PostHistoryResponse{
ID: h.ID,
PostID: h.PostID,
Version: h.Version,
Title: h.Title,
CategoryID: h.CategoryID,
ColumnID: h.ColumnID,
TagIDs: h.GetTagIDList(),
Date: time.Unix(h.CreatedAt, 0).Format("2006-01-02"),
IsPublished: h.IsPublished,
ModifiedBy: h.ModifiedBy,
ModifiedAt: time.Unix(h.ModifiedAt, 0).Format("2006-01-02 15:04:05"),
CreatedAt: time.Unix(h.CreatedAt, 0).Format("2006-01-02 15:04:05"),
}
if includeFull {
resp.Excerpt = h.Excerpt
resp.Content = h.Content
}
return resp
}
// BuildPostHistoryResponses 构建历史记录列表响应
func BuildPostHistoryResponses(history []models.PostHistory) []models.PostHistoryResponse {
var responses []models.PostHistoryResponse
for _, h := range history {
responses = append(responses, *BuildPostHistoryResponse(&h))
responses = append(responses, *BuildPostHistoryResponse(&h, false))
}
return responses
}
func buildFieldDiff(from, to string, withLineDiff bool) models.PostHistoryFieldDiff {
diff := models.PostHistoryFieldDiff{
From: from,
To: to,
Changed: from != to,
}
if withLineDiff && from != to {
dmp := diffmatchpatch.New()
patches := dmp.PatchMake(from, to)
diff.Diff = dmp.PatchToText(patches)
}
return diff
}
// GetPostHistoryDiff compares two history versions.
func GetPostHistoryDiff(postID uint, fromVersion, toVersion uint) (*models.PostHistoryDiffResponse, error) {
fromHistory, err := GetPostHistoryByVersion(postID, fromVersion)
if err != nil {
return nil, err
}
if fromHistory == nil {
return nil, fmt.Errorf("from version not found")
}
toHistory, err := GetPostHistoryByVersion(postID, toVersion)
if err != nil {
return nil, err
}
if toHistory == nil {
return nil, fmt.Errorf("to version not found")
}
fromTags, _ := json.Marshal(fromHistory.GetTagIDList())
toTags, _ := json.Marshal(toHistory.GetTagIDList())
fields := map[string]models.PostHistoryFieldDiff{
"title": buildFieldDiff(fromHistory.Title, toHistory.Title, false),
"excerpt": buildFieldDiff(fromHistory.Excerpt, toHistory.Excerpt, false),
"content": buildFieldDiff(fromHistory.Content, toHistory.Content, true),
"categoryId": buildFieldDiff(strconv.FormatUint(uint64(fromHistory.CategoryID), 10), strconv.FormatUint(uint64(toHistory.CategoryID), 10), false),
"columnId": buildFieldDiff(formatOptionalUint(fromHistory.ColumnID), formatOptionalUint(toHistory.ColumnID), false),
"tagIds": buildFieldDiff(string(fromTags), string(toTags), false),
"isPublished": buildFieldDiff(strconv.Itoa(fromHistory.IsPublished), strconv.Itoa(toHistory.IsPublished), false),
}
return &models.PostHistoryDiffResponse{
FromVersion: int(fromVersion),
ToVersion: int(toVersion),
Fields: fields,
}, nil
}
func formatOptionalUint(v *uint) string {
if v == nil {
return ""
}
return strconv.FormatUint(uint64(*v), 10)
}
// RestorePostFromHistory restores a post from a history snapshot and saves a new history entry.
func RestorePostFromHistory(postID, version, modifiedBy uint) (*models.Post, error) {
history, err := GetPostHistoryByVersion(postID, version)
if err != nil {
return nil, err
}
if history == nil {
return nil, fmt.Errorf("history version not found")
}
var post models.Post
if err := config.DB.Preload("Tags").Where("id = ? AND deleted_at = ?", postID, 0).First(&post).Error; err != nil {
return nil, err
}
post.Title = history.Title
post.CategoryID = history.CategoryID
post.ColumnID = history.ColumnID
post.Excerpt = history.Excerpt
post.Content = history.Content
post.IsPublished = history.IsPublished
if err := UpdatePost(&post); err != nil {
return nil, err
}
tagIDs := history.GetTagIDList()
tags := make([]models.Tag, 0, len(tagIDs))
for _, id := range tagIDs {
tags = append(tags, models.Tag{ID: id})
}
if err := config.DB.Model(&post).Association("Tags").Replace(tags); err != nil {
return nil, err
}
post.Tags = tags
if err := SavePostHistory(&post, modifiedBy); err != nil {
return nil, err
}
reloaded, err := GetPostByIDAdmin(postID)
if err != nil {
return nil, err
}
return reloaded, nil
}
// GetPostByIDAdmin loads a post for admin use without incrementing read count.
func GetPostByIDAdmin(id uint) (*models.Post, error) {
var post models.Post
err := config.DB.Model(&models.Post{}).
Preload("Category").
Preload("Column").
Preload("Tags").
Where("id = ? AND deleted_at = ?", id, 0).
First(&post).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &post, nil
}
// GetRecommendedPostsByIP 基于IP的协同过滤推荐算法
// 查找相同IP访问过的其他文章按访问频率排序返回
func GetRecommendedPostsByIP(currentPostID uint, userIP string, limit int) ([]models.Post, error) {

View File

@@ -61,3 +61,39 @@ func DeleteSearchLog(id uint) error {
}
return nil
}
// HotKeyword 热门搜索关键词
type HotKeyword struct {
Keyword string `json:"keyword"`
Count int `json:"count"`
}
// GetHotKeywords 获取热门搜索关键词
func GetHotKeywords(limit, days int) ([]HotKeyword, error) {
if limit <= 0 {
limit = 10
}
if days <= 0 {
days = 30
}
since := time.Now().AddDate(0, 0, -days).Unix()
var results []HotKeyword
err := config.DB.Model(&models.SearchLog{}).
Select("keyword, COUNT(*) as count").
Where("deleted_at = ?", 0).
Where("search_type = ?", "keyword").
Where("created_at >= ?", since).
Where("CHAR_LENGTH(keyword) >= ?", 2).
Group("keyword").
Order("count DESC").
Limit(limit).
Scan(&results).Error
if err != nil {
log.Printf("Error querying hot keywords: %v", err)
return nil, err
}
return results, nil
}

View File

@@ -23,6 +23,30 @@ func CreateUserAccessLog(logEntry *models.UserAccessLog) error {
return nil
}
// HasVisitedThisHour checks if the visitor already has an access log for this article in the current hour.
func HasVisitedThisHour(articleID, userID uint, visitorKey string) (bool, error) {
hourStart := time.Now().Truncate(time.Hour).Unix()
hourEnd := hourStart + 3600
var count int64
query := config.DB.Model(&models.UserAccessLog{}).
Where("deleted_at = ?", 0).
Where("article_id = ?", articleID).
Where("access_time >= ? AND access_time < ?", hourStart, hourEnd)
if userID > 0 {
query = query.Where("user_id = ?", userID)
} else {
query = query.Where("visitor_key = ?", visitorKey)
}
if err := query.Count(&count).Error; err != nil {
return false, err
}
return count > 0, nil
}
// AccessStats 访问统计数据结构
type AccessStats struct {
Date string `json:"date"`

View File

@@ -31,14 +31,9 @@ func SuccessWithMsg(c *gin.Context, msg string, result interface{}) {
})
}
// Error 错误响应 (默认 Code 400)
// Error 错误响应HTTP 状态恒为 200通过 code 区分业务状态
func Error(c *gin.Context, code int, msg string) {
httpStatus := http.StatusBadRequest
if code == 500 {
httpStatus = http.StatusInternalServerError
}
c.JSON(httpStatus, Response{
c.JSON(http.StatusOK, Response{
Code: code,
Message: msg,
Result: nil,
@@ -47,7 +42,7 @@ func Error(c *gin.Context, code int, msg string) {
// ServerError 服务器错误 (Code 500)
func ServerError(c *gin.Context, err error) {
c.JSON(http.StatusInternalServerError, Response{
c.JSON(http.StatusOK, Response{
Code: 500,
Message: err.Error(),
Result: nil,

17
server/utils/visitor.go Normal file
View File

@@ -0,0 +1,17 @@
package utils
import (
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
// GetOrSetVisitorID returns an anonymous visitor identifier from cookie, creating one if missing.
func GetOrSetVisitorID(c *gin.Context) string {
if cookie, err := c.Cookie("visitor_id"); err == nil && cookie != "" {
return cookie
}
id := uuid.New().String()
c.SetCookie("visitor_id", id, 365*24*3600, "/", "", false, true)
return id
}