添加首页文章页面

This commit is contained in:
2025-10-08 20:00:30 +08:00
parent 3d7240565e
commit 698f27372d
30 changed files with 2149 additions and 667 deletions

View File

@@ -4,23 +4,57 @@
package article
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/JACKYMYPERSON/hldrCenter/config"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/logic/article"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/model"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/types"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/rest/httpx"
)
func DeleteArticleHandler(cfg *config.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.DeleteArticleReq
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
// 1. 从URL路径提取id核心步骤
pathParts := strings.Split(r.URL.Path, "/")
// 完整路径是 "/api/articles/12"split后应为 ["", "api", "articles", "12"]
if len(pathParts) < 4 {
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("无效的 URL 路径"))
return
}
idStr := pathParts[3]
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil || id <= 0 { // 增加id有效性校验
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("无效的文章ID%s", idStr))
return
}
req.Id = id // 只赋值一次确保Id正确
l := article.NewDeleteArticleLogic(r.Context(), cfg)
// 验证打印id确认此时是正确的如12
fmt.Println("Handler中获取的文章ID", req.Id)
mysqlCfg := cfg.MySQL
dsn := fmt.Sprintf(
"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=true&loc=Local",
mysqlCfg.Username,
mysqlCfg.Password,
mysqlCfg.Host,
mysqlCfg.Port,
mysqlCfg.Database,
mysqlCfg.Charset,
)
fmt.Println("接收到articlePost请求")
conn := sqlx.NewSqlConn("mysql", dsn)
articleModel := model.NewArticleModel(conn)
l := article.NewDeleteArticleLogic(r.Context(), cfg, articleModel)
resp, err := l.DeleteArticle(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)

View File

@@ -33,7 +33,7 @@ func ListArticleHandler(cfg *config.Config) http.HandlerFunc {
mysqlCfg.Database,
mysqlCfg.Charset,
)
fmt.Println("接收到articlePost请求")
fmt.Println("接收到获取文章列表请求:", req)
conn := sqlx.NewSqlConn("mysql", dsn)
articleModel := model.NewArticleModel(conn)

View File

@@ -4,23 +4,57 @@
package article
import (
"fmt"
"net/http"
"strconv"
"strings"
"github.com/JACKYMYPERSON/hldrCenter/config"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/logic/article"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/model"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/types"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/rest/httpx"
)
func UpdateArticleHandler(cfg *config.Config) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.UpdateArticleReq
pathParts := strings.Split(r.URL.Path, "/")
// 确保路径格式正确(至少包含 4 个部分:["", "api", "articles", "12"]
if len(pathParts) < 4 {
httpx.ErrorCtx(r.Context(), w, fmt.Errorf("无效的 URL 路径"))
return
}
idStr := pathParts[3] // 取路径中最后一个部分(如 "12"
id, err := strconv.ParseInt(idStr, 10, 64)
fmt.Println("访问路径:", id)
req.Id = id
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := article.NewUpdateArticleLogic(r.Context(), cfg)
// 新增打印绑定后的req.Id确认是否为12
fmt.Println("Handler中获取的文章ID", req.Id)
mysqlCfg := cfg.MySQL
dsn := fmt.Sprintf(
"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=true&loc=Local",
mysqlCfg.Username,
mysqlCfg.Password,
mysqlCfg.Host,
mysqlCfg.Port,
mysqlCfg.Database,
mysqlCfg.Charset,
)
conn := sqlx.NewSqlConn("mysql", dsn)
articleModel := model.NewArticleModel(conn)
l := article.NewUpdateArticleLogic(r.Context(), cfg, articleModel)
resp, err := l.UpdateArticle(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)

View File

@@ -5,8 +5,10 @@ package article
import (
"context"
"errors"
"github.com/JACKYMYPERSON/hldrCenter/config"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/model"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/types"
"github.com/zeromicro/go-zero/core/logx"
@@ -14,20 +16,35 @@ import (
type DeleteArticleLogic struct {
logx.Logger
ctx context.Context
cfg *config.Config
ctx context.Context
cfg *config.Config
model model.ArticleModel
}
func NewDeleteArticleLogic(ctx context.Context, cfg *config.Config) *DeleteArticleLogic {
func NewDeleteArticleLogic(ctx context.Context, cfg *config.Config, model model.ArticleModel) *DeleteArticleLogic {
return &DeleteArticleLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
cfg: cfg,
model: model,
}
}
func (l *DeleteArticleLogic) DeleteArticle(req *types.DeleteArticleReq) (resp *types.DeleteArticleResp, err error) {
// todo: add your logic here and delete this line
// 1. 校验参数确保文章ID有效大于0
if req.Id <= 0 {
return nil, errors.New("无效的文章ID")
}
return
// 2. 调用Model层执行逻辑删除更新is_delete=1
err = l.model.Delete(l.ctx, req.Id)
if err != nil {
// 可根据实际错误类型返回更具体的信息
return nil, errors.New("删除文章失败:" + err.Error())
}
// 3. 返回成功响应
return &types.DeleteArticleResp{
Success: true,
}, nil
}

View File

@@ -93,11 +93,6 @@ func (l *ListArticleLogic) ListArticle(req *types.ListArticleReq) (resp *types.L
Page: page, // 当前页码(处理后的有效值)
Size: size, // 每页条数(处理后的有效值)
}
for i, item := range resp.List {
fmt.Printf("[DEBUG] 响应第 %d 条文章的 Content%q\n", i+1, item.Content)
}
l.Infof("文章列表查询成功topic=%s, page=%d, size=%d, 总条数=%d, 总页数=%d",
topic, page, size, total, totalPage)
return resp, nil
}

View File

@@ -5,29 +5,52 @@ package article
import (
"context"
"fmt"
"github.com/JACKYMYPERSON/hldrCenter/config"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/model"
"github.com/JACKYMYPERSON/hldrCenter/internal/article/internal/types"
"github.com/go-playground/validator/v10"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateArticleLogic struct {
logx.Logger
ctx context.Context
cfg *config.Config
ctx context.Context
cfg *config.Config
model model.ArticleModel
}
func NewUpdateArticleLogic(ctx context.Context, cfg *config.Config) *UpdateArticleLogic {
func NewUpdateArticleLogic(ctx context.Context, cfg *config.Config, model model.ArticleModel) *UpdateArticleLogic {
return &UpdateArticleLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
cfg: cfg,
model: model,
}
}
func (l *UpdateArticleLogic) UpdateArticle(req *types.UpdateArticleReq) (resp *types.UpdateArticleResp, err error) {
// todo: add your logic here and delete this line
validate := validator.New()
if err := validate.Struct(req); err != nil {
// 新增:打印校验失败原因
fmt.Println("参数校验失败:", err)
return nil, err
}
tmp := &model.ArticleUpdate{
Id: req.Id,
Title: req.Title,
Excerpt: req.Excerpt,
Cover: req.Cover,
Content: req.Content,
}
return
fmt.Println("更改的文章信息:", tmp)
err = l.model.Update(l.ctx, tmp)
if err != nil {
return nil, fmt.Errorf("更新文章失败: %v", err)
}
return &types.UpdateArticleResp{Success: true}, nil
}

View File

@@ -34,16 +34,28 @@ var (
"`is_delete`", // 9. 对应 is_delete
},
",",
)
)
articleChangeFields = func() string {
// 获取ArticleUpdate的字段列表假设RawFieldNames返回带`的字段名,如`title`
fields := stringx.Remove(builder.RawFieldNames(&ArticleUpdate{}), "`id`")
// 拼接成 "`title`=?, `content`=?, ..." 格式
withPlaceholders := make([]string, len(fields))
for i, field := range fields {
withPlaceholders[i] = field + "=?"
}
// 用逗号连接所有字段
return strings.Join(withPlaceholders, ", ")
}()
)
type (
articleModel interface {
Insert(ctx context.Context, data *Article) (sql.Result, error)
FindOne(ctx context.Context, id int64) (*Article, error)
Update(ctx context.Context, data *Article) error
Update(ctx context.Context, data *ArticleUpdate) error
Delete(ctx context.Context, id int64) error
List(ctx context.Context, page, size int, topic string) ([]*Article, int64, error)
}
DefaultArticleModel struct {
@@ -75,6 +87,15 @@ type (
IsDelete int64 `db:"is_delete"` // 与第9个字段对应
}
ArticleUpdate struct {
Id int64 `db:"id"`
Title string `db:"title"`
Content string `db:"content"`
Cover string `db:"cover"`
Excerpt string `db:"excerpt"`
}
)
func newArticleModel(conn sqlx.SqlConn) *DefaultArticleModel {
@@ -85,7 +106,9 @@ func newArticleModel(conn sqlx.SqlConn) *DefaultArticleModel {
}
func (m *DefaultArticleModel) Delete(ctx context.Context, id int64) error {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
// 逻辑删除更新is_delete字段为11表示已删除
query := fmt.Sprintf("update %s set `is_delete` = 1 where `id` = ?", m.table)
// 执行更新操作参数为文章IDwhere条件
_, err := m.conn.ExecCtx(ctx, query, id)
return err
}
@@ -110,10 +133,20 @@ func (m *DefaultArticleModel) Insert(ctx context.Context, data *Article) (sql.Re
return ret, err
}
func (m *DefaultArticleModel) Update(ctx context.Context, data *Article) error {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, articleRowsWithPlaceHolder)
fmt.Println("Insert SQL 字段列表:", articleRowsExpectAutoSet)
_, err := m.conn.ExecCtx(ctx, query, data.Title, data.Content, data.Cover, data.IsDelete, data.Topic, data.Excerpt, data.Id)
func (m *DefaultArticleModel) Update(ctx context.Context, data *ArticleUpdate) error {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, articleChangeFields)
fmt.Println("Update SQL:", query) // 打印SQL便于调试
// 参数顺序与articleChangeFields中的字段顺序一致最后跟where条件的id
_, err := m.conn.ExecCtx(
ctx,
query,
data.Title, // 对应`title`=?
data.Content, // 对应`content`=?
data.Cover, // 对应`cover`=?
data.Excerpt, // 对应`excerpt`=?
data.Id, // 对应where `id`=?
)
return err
}
@@ -172,9 +205,6 @@ func (m *DefaultArticleModel) List(ctx context.Context, page, size int, topic st
articleList := make([]*Article, 0, len(tempList))
for _, item := range tempList {
fmt.Printf("[DEBUG] 扫描到的文章ID%dContent值%q\n", item.Id, item.Content)
// 打印 SQL 实际查询的字段,确认 content 存在
fmt.Printf("[DEBUG] SQL 查询字段:%s\n", articleListFields)
articleList = append(articleList, &Article{
Id: item.Id, // 主键
Title: item.Title, // 标题
@@ -187,9 +217,6 @@ func (m *DefaultArticleModel) List(ctx context.Context, page, size int, topic st
IsDelete: item.IsDelete, // 软删除状态
})
}
for i, item := range articleList {
fmt.Printf("[DEBUG] 响应第 %d 条文章的 Content%q\n", i+1, item.Content)
}
// 5. 返回结果Article 切片+总条数+无错误)

View File

@@ -36,7 +36,7 @@ type DeleteArticleResp struct {
}
type DetailArticleReq struct {
Id int64 `path:"id"` // 从路径参数取 ID如 /api/articles/:id
Id int64 `uri:"id" binding:"required,min=1"` // 从路径参数取 ID如 /api/articles/:id
}
type DetailArticleResp struct {
@@ -59,11 +59,10 @@ type ListArticleResp struct {
}
type UpdateArticleReq struct {
Id int64 `path:"id"` // 要更新的文章 ID
Id int64 `uri:"id" binding:"required,min=1"` // 要更新的文章 ID
Title string `json:"title,optional"`
Content string `json:"content,optional"`
Cover string `json:"cover,optional"`
Topic string `json:"topic,optional"`
Excerpt string `json:"excerpt,optional"`
}