Commit 5f7631d4 authored by 徐 誉甜's avatar 徐 誉甜
Browse files

Initial commit of frontend and backend project

parents
# Go 忽略项
*.exe # Windows 可执行文件
*.dll # Windows 动态链接库
*.so # Linux 共享库
*.dylib # macOS 动态库
*.test # Go 测试可执行文件
*.prof # Go Profiling 文件
# Go Module 依赖
/vendor/
# 本地开发工具或IDE文件 (根据你使用的工具添加)
.vscode/
.idea/
*.sublime-project
*.sublime-workspace
# 数据库文件 - 极其重要!不要提交数据库文件
comments.db
*.db
document.addEventListener('DOMContentLoaded', () => {
// 修复变量名和对应的HTML ID
const userNameInput = document.getElementById('username');
const commentContentInput = document.getElementById('comment-text');
const submitBtn = document.getElementById('submit-comment');
const prevPageBtn = document.getElementById('prev-page');
const nextPageBtn = document.getElementById('next-page');
const currentPageSpan = document.getElementById('current-page');
const pageSizeSelect = document.getElementById('pageSizeSelect');
const commentsList = document.getElementById('comments-list');
const API_HOST = 'http://localhost:8080';
let currentPage = 1;
let pageSize = parseInt(pageSizeSelect.value, 10);
let totalComments = 0;
async function fetchComments() {
try {
const response = await fetch(`${API_HOST}/comment/get?page=${currentPage}&size=${pageSize}`);
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const result = await response.json();
if (result.code === 0) {
renderComments(result.data.comments);
totalComments = result.data.total;
updatePaginationControls();
} else {
commentsList.innerHTML = `<p>加载评论失败: ${escapeHTML(result.msg)}</p>`; // 显示错误信息
totalComments = 0;
updatePaginationControls();
alert(`无法加载评论: ${result.msg}`);
}
} catch (error) {
console.error('加载评论错误: ', error);
commentsList.innerHTML = '<p>加载评论出错,请检查服务器是否运行。</p>';
totalComments = 0;
updatePaginationControls();
alert('无法加载评论,请检查后端服务器是否正常运行?');
}
}
function renderComments(comments = []) {
commentsList.innerHTML = '';
if (!comments || comments.length === 0) {
commentsList.innerHTML = '<p>暂无评论。</p>';
return;
}
comments.forEach(comment => {
const commentItem = document.createElement('div');
commentItem.classList.add('comment-item');
commentItem.dataset.id = comment.id;
commentItem.innerHTML = `
<span class="user-name">${escapeHTML(comment.name)}</span>
<p class="comment-text">${escapeHTML(comment.content)}</p>
<button class="delete-btn">删除</button>
`;
commentsList.appendChild(commentItem);
});
}
async function addComment() {
const userName = userNameInput.value.trim();
const content = commentContentInput.value.trim();
if (!userName || !content) {
alert('用户名和评论内容不能为空');
return;
}
try {
const response = await fetch(`${API_HOST}/comment/add`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: userName, content: content }),
});
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`);
}
const result = await response.json();
if (result.code === 0) {
currentPage = 1;
fetchComments();
userNameInput.value = '';
commentContentInput.value = '';
} else {
alert(`添加评论失败: ${result.msg}`);
}
} catch (error) {
console.error('添加评论错误', error);
alert('添加评论出错');
}
}
async function handleDelete(event) {
if (event.target.classList.contains('delete-btn')) {
const commentItem = event.target.closest('.comment-item');
if (commentItem) {
const commentId = commentItem.dataset.id;
if (confirm(`确定要删除ID为 ${commentId} 的评论吗?`)) {
try {
const response = await fetch(`${API_HOST}/comment/delete?id=${commentId}`, {
method: 'POST',
});
if (!response.ok) {
// 对于非2xx状态码,尝试读取后端返回的错误信息
const errorData = await response.json().catch(() => ({ msg: `HTTP status ${response.status}` }));
throw new Error(`删除失败: ${errorData.msg || `HTTP status ${response.status}`}`);
}
const result = await response.json();
if (result.code === 0) {
fetchComments();
} else {
alert(`删除失败: ${result.msg}`);
}
} catch (error) {
console.error('删除评论错误', error);
alert(`删除评论出错: ${error.message}`);
}
}
}
}
}
function updatePaginationControls() {
currentPageSpan.textContent = `Page: ${currentPage}`;
const maxPage = (pageSize === -1 || totalComments === 0) ? 1 : Math.ceil(totalComments / pageSize);
prevPageBtn.disabled = currentPage <= 1;
nextPageBtn.disabled = currentPage >= maxPage;
if (pageSize === -1) {
prevPageBtn.disabled = true;
nextPageBtn.disabled = true;
}
}
function escapeHTML(str) {
const div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
if(submitBtn) submitBtn.addEventListener('click', addComment);
if(commentsList) commentsList.addEventListener('click', handleDelete);
if(prevPageBtn) {
prevPageBtn.addEventListener('click', () => {
if (currentPage > 1) {
currentPage--;
fetchComments();
}
});
}
if(nextPageBtn) {
nextPageBtn.addEventListener('click', () => {
const maxPage = (pageSize === -1 || totalComments === 0) ? 1 : Math.ceil(totalComments / pageSize);
if (currentPage < maxPage || pageSize === -1) {
if (pageSize === -1 && currentPage === 1) {
// 如果是显示全部模式且在第一页,点击下一页不应该有反应
return;
}
if (pageSize !== -1 && currentPage >= maxPage) {
// 如果不是显示全部模式且已经是最后一页,不应该有反应
return;
}
currentPage++;
fetchComments();
}
});
}
if(pageSizeSelect) {
pageSizeSelect.addEventListener('change', (event) => {
pageSize = parseInt(event.target.value, 10);
currentPage = 1;
fetchComments();
});
}
fetchComments();
});
\ No newline at end of file
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>简易评论区</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="container">
<div class="comment-input-area">
<div class="form-group">
<label for="userName">用户名</label>
<input type="text" id="username" placeholder="用户名" />
</div>
<div class="form-group">
<label for="commentContent">评论内容</label>
<textarea id="comment-text" placeholder="说点什么吧..." rows="4"></textarea>
</div>
<button id="submit-comment">提交</button>
</div>
<div id="comments-list" class="comments-list">
</div>
<div class="pagination">
<button id="prev-page" disabled>上一页</button>
<span id="current-page">Page: 1</span>
<button id="next-page">下一页</button>
<select id="pageSizeSelect">
<option value="5">5 per page</option>
<option value="10" selected>10 per page</option>
<option value="20">20 per page</option>
<option value="-1">Show All</option>
</select>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
\ No newline at end of file
body {
font-family: sans-serif;
margin: 0;
padding: 20px;
background-color: #f4f6f8;
color: #333;
display: flex;
justify-content: center;
}
.container {
background-color: #fff;
padding: 20px;
margin-bottom: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 500px;
}
.comment-input-area {
background-color: #e0e5ea;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.comment-input-area div {
margin-bottom: 10px;
}
.comment-input-area label {
display: block;
margin-bottom: 5px;
font-weight: bold;
font-size: 0.9em;
}
.comment-input-area input[type="text"],
.comment-input-area textarea {
width: calc(100% - 12px);
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.comment-input-area textarea {
min-height: 80px;
resize: vertical;
}
.comment-input-area button {
background-color: #343a40;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
float: right;
}
.comment-input-area button:hover {
background-color: #23272b;
}
.comment-input-area::after {
content: "";
clear: both;
display: table;
}
.pagination {
margin-top: 20px;
text-align: center;
}
.pagination button,
.pagination select {
padding: 8px 12px;
margin: 0 5px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
}
.pagination button:disabled {
cursor: not-allowed;
opacity: 0.5;
}
.pagination span {
margin: 0 10px;
}
.comments-list .comment-item {
border-left: 3px solid #000;
padding-left: 15px;
margin-bottom: 20px;
position: relative;
}
\ No newline at end of file
module xlab.zju.edu.cn/git/melonmusk/first-project-comment
go 1.24.3
require github.com/mattn/go-sqlite3 v1.14.28 // indirect
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
package main
import (
"database/sql"
"encoding/json"
"log"
"net/http"
"strconv"
"sync"
"time"
_ "github.com/mattn/go-sqlite3"
)
type UnifiedResponse struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
type Comment struct {
ID int `json:"id"`
Name string `json:"name"`
Content string `json:"content"`
CreatedAt time.Time `json:"created_at"`
}
type AddCommentRequest struct {
Name string `json:"name"`
Content string `json:"content"`
}
type GetCommentsData struct {
Total int `json:"total"`
Comments []Comment `json:"comments"`
}
var (
comments = make([]Comment, 0)
mutex = &sync.RWMutex{}
nextCommentID = 1
)
var db *sql.DB
func initDB(dataSourceName string) (*sql.DB, error) {
var err error
// 打开数据库连接 (如果文件不存在会创建)
// dataSourceName 通常是数据库文件的路径
db, err = sql.Open("sqlite3", dataSourceName)
if err != nil {
log.Printf("Error opening database: %v", err)
return nil, err
}
// 检查连接是否有效
if err = db.Ping(); err != nil {
db.Close() // 确保关闭连接
log.Printf("Error connecting to database: %v", err)
return nil, err
}
// 创建 comments 表 (如果不存在)
createTableSQL := `
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`
_, err = db.Exec(createTableSQL)
if err != nil {
db.Close() // 确保关闭连接
log.Printf("Error creating comments table: %v", err)
return nil, err
}
log.Printf("Database initialized successfully.")
return db, nil
}
func sendJSONResponse(w http.ResponseWriter, statusCode int, data UnifiedResponse) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
w.WriteHeader(statusCode)
if err := json.NewEncoder(w).Encode(data); err != nil {
log.Printf("JSON error: %v", err)
}
}
/*
func getCommentsHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "OPTIONS OK"})
return
}
if r.Method != http.MethodGet {
sendJSONResponse(w, http.StatusMethodNotAllowed, UnifiedResponse{Code: 1, Msg: "invalid method"})
return
}
pageStr := r.URL.Query().Get("page")
sizeStr := r.URL.Query().Get("size")
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
size, err := strconv.Atoi(sizeStr)
if err != nil || size == 0 {
size = 10
}
mutex.RLock()
defer mutex.RUnlock()
var commentsToReturn []Comment
total := len(comments)
if size == -1 {
commentsToReturn = make([]Comment, total)
copy(commentsToReturn, comments)
} else if total > 0 {
start := (page - 1) * size
end := start + size
if start < 0 {
start = 0
}
if start >= total {
commentsToReturn = []Comment{}
} else {
if end > total {
end = total
}
commentsToReturn = make([]Comment, end-start)
copy(commentsToReturn, comments[start:end])
}
} else {
commentsToReturn = []Comment{}
}
responseData := GetCommentsData{
Total: total,
Comments: commentsToReturn,
}
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "success", Data: responseData})
}
*/
func getCommentsHandler(database *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "OPTIONS OK"})
return
}
if r.Method != http.MethodGet {
sendJSONResponse(w, http.StatusMethodNotAllowed, UnifiedResponse{Code: 1, Msg: "invalid method"})
return
}
pageStr := r.URL.Query().Get("page")
sizeStr := r.URL.Query().Get("size")
page, err := strconv.Atoi(pageStr)
if err != nil || page < 1 {
page = 1
}
size, err := strconv.Atoi(sizeStr)
if err != nil || size == 0 {
size = 10 // 默认每页10条
}
var total int
// 查询总评论数
row := database.QueryRow("SELECT COUNT(*) FROM comments;")
err = row.Scan(&total)
if err != nil {
log.Printf("Error getting total comment count: %v", err)
sendJSONResponse(w, http.StatusInternalServerError, UnifiedResponse{Code: 1, Msg: "failed to get total count"})
return
}
var commentsToReturn []Comment
var selectSQL string
var args []interface{}
if size == -1 { // 显示全部
selectSQL = "SELECT id, name, content, created_at FROM comments ORDER BY created_at ASC;"
// args 保持为空
} else { // 分页
offset := (page - 1) * size
// 检查 offset 是否越界,虽然数据库会处理,但这里可以提前打印警告或调整
if offset < 0 {
offset = 0
}
// 使用 ? 作为占位符,LIMIT 和 OFFSET 控制分页
selectSQL = "SELECT id, name, content, created_at FROM comments ORDER BY created_at ASC LIMIT ? OFFSET ?;"
args = []interface{}{size, offset}
}
// 执行查询
rows, err := database.Query(selectSQL, args...)
if err != nil {
log.Printf("Error querying comments: %v", err)
sendJSONResponse(w, http.StatusInternalServerError, UnifiedResponse{Code: 1, Msg: "failed to retrieve comments"})
return
}
defer rows.Close() // 确保关闭 rows
// 遍历结果集
for rows.Next() {
var c Comment
// 使用 Scan 将当前行的数据读入 Comment 结构体字段
err := rows.Scan(&c.ID, &c.Name, &c.Content, &c.CreatedAt)
if err != nil {
log.Printf("Error scanning comment row: %v", err)
// 扫描错误通常表示数据有问题或查询/结构体不匹配
continue // 跳过这一条,尝试下一条
}
commentsToReturn = append(commentsToReturn, c)
}
// 检查在遍历过程中是否发生了错误
if err = rows.Err(); err != nil {
log.Printf("Error during comments iteration: %v", err)
sendJSONResponse(w, http.StatusInternalServerError, UnifiedResponse{Code: 1, Msg: "error reading comments result"})
return
}
responseData := GetCommentsData{
Total: total,
Comments: commentsToReturn,
}
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "success", Data: responseData})
}
}
/*
func addCommentHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "OPTIONS OK"})
return
}
if r.Method != http.MethodPost {
sendJSONResponse(w, http.StatusMethodNotAllowed, UnifiedResponse{Code: 1, Msg: "disapproval method"})
return
}
var reqBody AddCommentRequest
err := json.NewDecoder(r.Body).Decode(&reqBody)
if err != nil {
sendJSONResponse(w, http.StatusBadRequest, UnifiedResponse{Code: 1, Msg: "invalid request: " + err.Error()})
return
}
defer r.Body.Close()
if reqBody.Name == "" || reqBody.Content == "" {
sendJSONResponse(w, http.StatusBadRequest, UnifiedResponse{Code: 1, Msg: "name and content can't be empty"})
return
}
mutex.Lock()
defer mutex.Unlock()
newComment := Comment{
ID: nextCommentID,
Name: reqBody.Name,
Content: reqBody.Content,
}
comments = append(comments, newComment)
nextCommentID++
log.Printf("add comment: ID=%d, Name=%s", newComment.ID, newComment.Name)
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "success", Data: newComment})
}
*/
func addCommentHandler(database *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "OPTIONS OK"})
return
}
if r.Method != http.MethodPost {
sendJSONResponse(w, http.StatusMethodNotAllowed, UnifiedResponse{Code: 1, Msg: "disapproval method"})
return
}
var reqBody AddCommentRequest
// 限制读取大小,防止滥用
// r.Body = http.MaxBytesReader(w, r.Body, 1048576) // 例如限制1MB
err := json.NewDecoder(r.Body).Decode(&reqBody)
if err != nil {
// 检查是否是读取大小限制错误
// if _, ok := err.(*http.MaxBytesError); ok {
// sendJSONResponse(w, http.StatusBadRequest, UnifiedResponse{Code: 1, Msg: "Request body too large"})
// return
// }
log.Printf("Error decoding request body: %v", err)
sendJSONResponse(w, http.StatusBadRequest, UnifiedResponse{Code: 1, Msg: "invalid request format"})
return
}
defer r.Body.Close() // 确保关闭请求体
if reqBody.Name == "" || reqBody.Content == "" {
sendJSONResponse(w, http.StatusBadRequest, UnifiedResponse{Code: 1, Msg: "name and content can't be empty"})
return
}
// 插入数据到数据库
insertSQL := "INSERT INTO comments (name, content) VALUES (?, ?);"
// Exec 用于执行 INSERT, UPDATE, DELETE 等不返回行的语句
result, err := database.Exec(insertSQL, reqBody.Name, reqBody.Content)
if err != nil {
log.Printf("Error inserting comment: %v", err)
sendJSONResponse(w, http.StatusInternalServerError, UnifiedResponse{Code: 1, Msg: "failed to save comment"})
return
}
// 获取新插入记录的ID
id, err := result.LastInsertId()
if err != nil {
log.Printf("Error getting last insert ID: %v", err)
// 即使获取ID失败,评论可能已经成功插入
// 可以选择返回成功,或者根据业务决定是否算失败
sendJSONResponse(w, http.StatusInternalServerError, UnifiedResponse{Code: 1, Msg: "failed to get new comment ID"})
return
}
log.Printf("add comment: ID=%d, Name=%s", id, reqBody.Name)
// 返回新评论的ID (可选,取决于前端是否需要立即知道)
// 可以通过查询刚刚插入的数据获取完整的 Comment 结构体,包括 created_at
// 或者只返回ID
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "success", Data: map[string]int{"id": int(id)}})
}
}
/*
func deleteCommentHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "options ok"})
return
}
if r.Method != http.MethodPost {
sendJSONResponse(w, http.StatusMethodNotAllowed, UnifiedResponse{Code: 1, Msg: "invalid method"})
return
}
idStr := r.URL.Query().Get("id")
commentID, err := strconv.Atoi(idStr)
if err != nil {
sendJSONResponse(w, http.StatusBadRequest, UnifiedResponse{Code: 1, Msg: "invalid comment ID"})
return
}
mutex.Lock()
defer mutex.Unlock()
found := false
newCommentsList := make([]Comment, 0, len(comments))
for _, c := range comments {
if c.ID == commentID {
found = true
} else {
newCommentsList = append(newCommentsList, c)
}
}
if !found {
sendJSONResponse(w, http.StatusNotFound, UnifiedResponse{Code: 1, Msg: "评论未找到"})
return
}
comments = newCommentsList
log.Printf("删除评论 ID: %d", commentID)
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "success", Data: nil}) // [cite: 40]
}
*/
func deleteCommentHandler(database *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodOptions {
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "options ok"})
return
}
// 前端使用POST方法删除,所以这里保持POST
if r.Method != http.MethodPost {
sendJSONResponse(w, http.StatusMethodNotAllowed, UnifiedResponse{Code: 1, Msg: "invalid method"})
return
}
idStr := r.URL.Query().Get("id")
commentID, err := strconv.Atoi(idStr)
if err != nil {
sendJSONResponse(w, http.StatusBadRequest, UnifiedResponse{Code: 1, Msg: "invalid comment ID format"})
return
}
// 执行删除操作
deleteSQL := "DELETE FROM comments WHERE id = ?;"
result, err := database.Exec(deleteSQL, commentID)
if err != nil {
log.Printf("Error deleting comment ID %d: %v", commentID, err)
sendJSONResponse(w, http.StatusInternalServerError, UnifiedResponse{Code: 1, Msg: "failed to delete comment"})
return
}
// 检查影响的行数,判断是否删除了记录
rowsAffected, err := result.RowsAffected()
if err != nil {
log.Printf("Error getting rows affected for delete ID %d: %v", commentID, err)
// 即使获取行数失败,删除操作可能已经成功,或者本来就没找到,这里可以返回成功或根据业务决定
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "delete operation attempted, status unknown"})
return
}
if rowsAffected == 0 {
// 没有找到匹配ID的评论
log.Printf("Delete comment ID %d not found", commentID)
sendJSONResponse(w, http.StatusNotFound, UnifiedResponse{Code: 1, Msg: "评论未找到"})
return
}
log.Printf("删除评论 ID: %d", commentID)
sendJSONResponse(w, http.StatusOK, UnifiedResponse{Code: 0, Msg: "success", Data: nil})
}
}
/*
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
lrw := &loggingResponseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(lrw, r)
log.Printf(
"request: %s %s, answer: %d, time_comsume: %s",
r.Method,
r.RequestURI,
lrw.statusCode,
time.Since(start),
)
})
}
*/
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
lrw := &loggingResponseWriter{ResponseWriter: w, statusCode: http.StatusOK}
next.ServeHTTP(lrw, r)
log.Printf(
"request: %s %s, answer: %d, time_comsume: %s",
r.Method,
r.RequestURI,
lrw.statusCode,
time.Since(start),
)
})
}
type loggingResponseWriter struct {
http.ResponseWriter
statusCode int
}
func (lrw *loggingResponseWriter) WriteHeader(code int) {
lrw.statusCode = code
lrw.ResponseWriter.WriteHeader(code)
}
func timeoutMiddleware(next http.Handler, duration time.Duration) http.Handler {
return http.TimeoutHandler(next, duration, "time exceed")
}
/*
func loadInitialData() {
mutex.Lock()
defer mutex.Unlock()
if len(comments) == 0 {
comments = append(comments, Comment{ID: nextCommentID, Name: "thy", Content: "这是第一条评论qaq"})
nextCommentID++
comments = append(comments, Comment{ID: nextCommentID, Name: "thy", Content: "这是第一条评论qwq"})
nextCommentID++
comments = append(comments, Comment{ID: nextCommentID, Name: "thy", Content: "这是第一条评论awa"})
nextCommentID++
log.Println("initial comments loading successfully")
}
}
*/
func loadInitialData(database *sql.DB) {
// 检查数据库是否已经有评论
var count int
row := database.QueryRow("SELECT COUNT(*) FROM comments;")
err := row.Scan(&count)
if err != nil {
log.Printf("Error checking initial data count: %v", err)
return
}
if count == 0 {
log.Println("No comments found, inserting initial data.")
// 使用事务插入多条数据
tx, err := database.Begin()
if err != nil {
log.Printf("Error starting transaction for initial data: %v", err)
return
}
defer tx.Rollback() // 如果后续操作失败,回滚事务
insertSQL := "INSERT INTO comments (name, content) VALUES (?, ?);"
stmt, err := tx.Prepare(insertSQL)
if err != nil {
log.Printf("Error preparing statement for initial data: %v", err)
return
}
defer stmt.Close()
initialComments := []struct{ Name, Content string }{
{"thyA", "这是第一条初始化评论!"},
{"thyB", "这是第二条初始化评论!"},
{"thyC", "这是第三条初始化评论!"},
}
for _, c := range initialComments {
_, err := stmt.Exec(c.Name, c.Content)
if err != nil {
log.Printf("Error inserting initial comment (%s): %v", c.Name, err)
return // 插入失败,回滚整个事务
}
}
err = tx.Commit() // 提交事务
if err != nil {
log.Printf("Error committing transaction for initial data: %v", err)
return
}
log.Println("Initial comments loaded successfully into database.")
} else {
log.Printf("Database already contains %d comments, skipping initial data load.", count)
}
}
func main() {
var err error
db, err = initDB("comments.db")
if err != nil {
log.Fatalf("Failed to initialize database: %v", err)
}
defer db.Close()
loadInitialData(db)
mux := http.NewServeMux()
/*
mux.HandleFunc("/comment/get", getCommentsHandler)
mux.HandleFunc("/comment/add", addCommentHandler)
mux.HandleFunc("/comment/delete", deleteCommentHandler)
*/
mux.HandleFunc("/comment/get", getCommentsHandler(db))
mux.HandleFunc("/comment/add", addCommentHandler(db))
mux.HandleFunc("/comment/delete", deleteCommentHandler(db))
loggedMux := loggingMiddleware(mux)
port := ":8080"
log.Printf("server is current working on %s\n", port)
if err := http.ListenAndServe(port, loggedMux); err != nil {
log.Fatalf("can't start server: %s\n", err.Error())
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment