增加cms模块
This commit is contained in:
@@ -0,0 +1,451 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"server/models"
|
||||
"server/services"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// ArticlesController 文章管理控制器
|
||||
type ArticlesController struct {
|
||||
web.Controller
|
||||
}
|
||||
|
||||
// ListArticles 获取文章列表
|
||||
// 支持的状态值:
|
||||
// 0=草稿, 1=待审核, 2=已发布, 3=隐藏
|
||||
// 不传status参数或传空值时返回所有状态的文章
|
||||
func (c *ArticlesController) ListArticles() {
|
||||
// 获取查询参数
|
||||
page, _ := c.GetInt("page", 1)
|
||||
pageSize, _ := c.GetInt("pageSize", 10)
|
||||
keyword := c.GetString("keyword")
|
||||
cateStr := c.GetString("cate")
|
||||
statusStr := c.GetString("status")
|
||||
|
||||
// 从JWT上下文中获取租户ID和用户ID
|
||||
tenantId, _ := c.GetInt("tenantId", 0)
|
||||
userId, _ := c.GetInt("userId", 0)
|
||||
|
||||
var cate int
|
||||
var status int8 = -1 // -1表示不筛选状态,显示所有文章
|
||||
|
||||
if cateStr != "" {
|
||||
cate, _ = strconv.Atoi(cateStr)
|
||||
}
|
||||
|
||||
if statusStr != "" {
|
||||
statusInt, _ := strconv.Atoi(statusStr)
|
||||
status = int8(statusInt)
|
||||
}
|
||||
|
||||
// 调用服务层获取文章列表
|
||||
articles, total, err := services.GetArticlesList(tenantId, userId, page, pageSize, keyword, cate, status)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "获取文章列表失败: " + err.Error(),
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 格式化返回数据
|
||||
articleList := make([]map[string]interface{}, 0)
|
||||
for _, article := range articles {
|
||||
articleList = append(articleList, map[string]interface{}{
|
||||
"id": article.Id,
|
||||
"title": article.Title,
|
||||
"cate": article.Cate,
|
||||
"image": article.Image,
|
||||
"desc": article.Desc,
|
||||
"author": article.Author,
|
||||
"content": article.Content,
|
||||
"publisher": article.Publisher,
|
||||
"publishdate": article.Publishdate,
|
||||
"sort": article.Sort,
|
||||
"status": article.Status,
|
||||
"views": article.Views,
|
||||
"likes": article.Likes,
|
||||
"is_trans": article.IsTrans,
|
||||
"transurl": article.Transurl,
|
||||
"push": article.Push,
|
||||
"create_time": article.CreateTime,
|
||||
"update_time": article.UpdateTime,
|
||||
})
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "获取文章列表成功",
|
||||
"data": map[string]interface{}{
|
||||
"list": articleList,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
},
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// GetArticle 获取文章详情
|
||||
func (c *ArticlesController) GetArticle() {
|
||||
// 从URL获取文章ID
|
||||
articleId, err := c.GetInt(":id")
|
||||
if err != nil || articleId <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "无效的文章ID",
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务层获取文章详情
|
||||
article, err := services.GetArticleById(articleId)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "获取文章详情失败: " + err.Error(),
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
if article == nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "文章不存在",
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 增加浏览量
|
||||
services.IncrementArticleViews(articleId)
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "获取文章详情成功",
|
||||
"data": map[string]interface{}{
|
||||
"id": article.Id,
|
||||
"title": article.Title,
|
||||
"cate": article.Cate,
|
||||
"image": article.Image,
|
||||
"desc": article.Desc,
|
||||
"author": article.Author,
|
||||
"content": article.Content,
|
||||
"publisher": article.Publisher,
|
||||
"publishdate": article.Publishdate,
|
||||
"sort": article.Sort,
|
||||
"status": article.Status,
|
||||
"views": article.Views + 1, // 返回增加后的浏览量
|
||||
"likes": article.Likes,
|
||||
"is_trans": article.IsTrans,
|
||||
"transurl": article.Transurl,
|
||||
"push": article.Push,
|
||||
"create_time": article.CreateTime,
|
||||
"update_time": article.UpdateTime,
|
||||
},
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// CreateArticle 创建文章
|
||||
func (c *ArticlesController) CreateArticle() {
|
||||
// 定义接收文章数据的结构体
|
||||
var articleData struct {
|
||||
Title string `json:"title"`
|
||||
Cate int `json:"cate"`
|
||||
Image string `json:"image"`
|
||||
Desc string `json:"desc"`
|
||||
Author string `json:"author"`
|
||||
Content string `json:"content"`
|
||||
Publisher int `json:"publisher"`
|
||||
Publishdate string `json:"publishdate"`
|
||||
Sort int `json:"sort"`
|
||||
Status int8 `json:"status"`
|
||||
IsTrans string `json:"is_trans"`
|
||||
Transurl string `json:"transurl"`
|
||||
Push string `json:"push"`
|
||||
}
|
||||
|
||||
// 解析请求体JSON数据
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &articleData)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "请求参数格式错误: " + err.Error(),
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 校验必要参数
|
||||
if articleData.Title == "" {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "文章标题不能为空",
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
if articleData.Content == "" {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "文章内容不能为空",
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 构建文章对象
|
||||
var article models.Articles
|
||||
article.Title = articleData.Title
|
||||
article.Cate = articleData.Cate
|
||||
article.Image = articleData.Image
|
||||
article.Desc = articleData.Desc
|
||||
article.Author = articleData.Author
|
||||
article.Content = articleData.Content
|
||||
article.Publisher = articleData.Publisher
|
||||
|
||||
// 处理发布时间
|
||||
if articleData.Publishdate != "" {
|
||||
if publishTime, err := time.Parse("2006-01-02 15:04:05", articleData.Publishdate); err == nil {
|
||||
article.Publishdate = &publishTime
|
||||
} else if publishTime, err := time.Parse("2006-01-02", articleData.Publishdate); err == nil {
|
||||
article.Publishdate = &publishTime
|
||||
}
|
||||
}
|
||||
|
||||
article.Sort = articleData.Sort
|
||||
article.Status = articleData.Status
|
||||
article.IsTrans = articleData.IsTrans
|
||||
article.Transurl = articleData.Transurl
|
||||
article.Push = articleData.Push
|
||||
article.CreateTime = time.Now()
|
||||
now := time.Now()
|
||||
article.UpdateTime = &now
|
||||
|
||||
// 调用服务层创建文章
|
||||
newArticle, err := services.CreateArticle(&article)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "创建文章失败: " + err.Error(),
|
||||
"data": nil,
|
||||
}
|
||||
} else {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "创建文章成功",
|
||||
"data": map[string]interface{}{
|
||||
"id": newArticle.Id,
|
||||
},
|
||||
}
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateArticle 更新文章
|
||||
func (c *ArticlesController) UpdateArticle() {
|
||||
// 从URL获取文章ID
|
||||
articleId, err := c.GetInt(":id")
|
||||
if err != nil || articleId <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "无效的文章ID",
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 定义接收更新数据的结构体
|
||||
var articleData struct {
|
||||
Title string `json:"title"`
|
||||
Cate int `json:"cate"`
|
||||
Image string `json:"image"`
|
||||
Desc string `json:"desc"`
|
||||
Author string `json:"author"`
|
||||
Content string `json:"content"`
|
||||
Publisher int `json:"publisher"`
|
||||
Publishdate string `json:"publishdate"`
|
||||
Sort int `json:"sort"`
|
||||
Status int8 `json:"status"`
|
||||
IsTrans string `json:"is_trans"`
|
||||
Transurl string `json:"transurl"`
|
||||
Push string `json:"push"`
|
||||
}
|
||||
|
||||
// 解析请求体JSON数据
|
||||
err = json.Unmarshal(c.Ctx.Input.RequestBody, &articleData)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "请求参数格式错误: " + err.Error(),
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 校验必要参数
|
||||
if articleData.Title == "" {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "文章标题不能为空",
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
if articleData.Content == "" {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "文章内容不能为空",
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 构建文章对象
|
||||
var article models.Articles
|
||||
article.Id = articleId
|
||||
article.Title = articleData.Title
|
||||
article.Cate = articleData.Cate
|
||||
article.Image = articleData.Image
|
||||
article.Desc = articleData.Desc
|
||||
article.Author = articleData.Author
|
||||
article.Content = articleData.Content
|
||||
article.Publisher = articleData.Publisher
|
||||
|
||||
// 处理发布时间
|
||||
if articleData.Publishdate != "" {
|
||||
if publishTime, err := time.Parse("2006-01-02 15:04:05", articleData.Publishdate); err == nil {
|
||||
article.Publishdate = &publishTime
|
||||
} else if publishTime, err := time.Parse("2006-01-02", articleData.Publishdate); err == nil {
|
||||
article.Publishdate = &publishTime
|
||||
}
|
||||
}
|
||||
|
||||
article.Sort = articleData.Sort
|
||||
article.Status = articleData.Status
|
||||
article.IsTrans = articleData.IsTrans
|
||||
article.Transurl = articleData.Transurl
|
||||
article.Push = articleData.Push
|
||||
now := time.Now()
|
||||
article.UpdateTime = &now
|
||||
|
||||
// 调用服务层更新文章
|
||||
err = services.UpdateArticle(&article)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "更新文章失败: " + err.Error(),
|
||||
"data": nil,
|
||||
}
|
||||
} else {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "更新文章成功",
|
||||
"data": nil,
|
||||
}
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteArticle 删除文章
|
||||
func (c *ArticlesController) DeleteArticle() {
|
||||
// 从URL获取文章ID
|
||||
articleId, err := c.GetInt(":id")
|
||||
if err != nil || articleId <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "无效的文章ID",
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务层删除文章
|
||||
err = services.DeleteArticle(articleId)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "删除文章失败: " + err.Error(),
|
||||
"data": nil,
|
||||
}
|
||||
} else {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "删除文章成功",
|
||||
"data": nil,
|
||||
}
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateArticleStatus 更新文章状态
|
||||
func (c *ArticlesController) UpdateArticleStatus() {
|
||||
// 从URL获取文章ID
|
||||
articleId, err := c.GetInt(":id")
|
||||
if err != nil || articleId <= 0 {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "无效的文章ID",
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 定义接收状态数据的结构体
|
||||
var statusData struct {
|
||||
Status int8 `json:"status"`
|
||||
}
|
||||
|
||||
// 解析请求体JSON数据
|
||||
err = json.Unmarshal(c.Ctx.Input.RequestBody, &statusData)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "请求参数格式错误: " + err.Error(),
|
||||
"data": nil,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务层更新文章状态
|
||||
err = services.UpdateArticleStatus(articleId, statusData.Status)
|
||||
if err != nil {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 1,
|
||||
"message": "更新文章状态失败: " + err.Error(),
|
||||
"data": nil,
|
||||
}
|
||||
} else {
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 0,
|
||||
"message": "更新文章状态成功",
|
||||
"data": nil,
|
||||
}
|
||||
}
|
||||
c.ServeJSON()
|
||||
}
|
||||
@@ -93,6 +93,7 @@ func (c *AuthController) Login() {
|
||||
"avatar": user.Avatar,
|
||||
"nickname": user.Nickname,
|
||||
"tenant_id": user.TenantId,
|
||||
"uid": user.Uid,
|
||||
"role": user.Role, // 角色ID
|
||||
"type": "user", // 标识是用户登录
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ func (c *UserController) GetAllUsers() {
|
||||
for _, user := range users {
|
||||
userList = append(userList, map[string]interface{}{
|
||||
"id": user.Id,
|
||||
"uid": user.Uid,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"avatar": user.Avatar,
|
||||
@@ -79,6 +80,7 @@ func (c *UserController) GetTenantUsers() {
|
||||
for _, user := range users {
|
||||
userList = append(userList, map[string]interface{}{
|
||||
"id": user.Id,
|
||||
"uid": user.Uid,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"avatar": user.Avatar,
|
||||
@@ -203,6 +205,7 @@ func (c *UserController) GetUserInfo() {
|
||||
"message": "查询成功",
|
||||
"data": map[string]interface{}{
|
||||
"id": user.Id,
|
||||
"uid": user.Uid,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"avatar": user.Avatar,
|
||||
@@ -297,6 +300,7 @@ func (c *UserController) AddUser() {
|
||||
"message": "用户添加成功",
|
||||
"data": map[string]interface{}{
|
||||
"id": newUser.Id,
|
||||
"uid": newUser.Uid,
|
||||
"username": newUser.Username,
|
||||
"email": newUser.Email,
|
||||
"nickname": newUser.Nickname,
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
-- 迁移脚本:为用户表添加租户内自增UID字段
|
||||
-- 执行时间:需要手动执行此脚本
|
||||
|
||||
-- 1. 为yz_users表添加uid字段
|
||||
ALTER TABLE yz_users ADD COLUMN uid INT DEFAULT 0 COMMENT '租户内用户ID,自增';
|
||||
|
||||
-- 2. 为现有用户分配uid(使用存储过程或手动更新)
|
||||
-- 方法1:使用存储过程(推荐)
|
||||
DELIMITER //
|
||||
|
||||
CREATE PROCEDURE update_user_uids()
|
||||
BEGIN
|
||||
DECLARE done INT DEFAULT FALSE;
|
||||
DECLARE current_tenant_id INT;
|
||||
DECLARE current_id INT;
|
||||
DECLARE uid_counter INT DEFAULT 1;
|
||||
|
||||
-- 游标用于遍历所有租户
|
||||
DECLARE tenant_cursor CURSOR FOR
|
||||
SELECT DISTINCT tenant_id FROM yz_users WHERE delete_time IS NULL ORDER BY tenant_id;
|
||||
|
||||
-- 游标用于遍历每个租户内的用户
|
||||
DECLARE user_cursor CURSOR FOR
|
||||
SELECT id FROM yz_users
|
||||
WHERE tenant_id = current_tenant_id AND delete_time IS NULL
|
||||
ORDER BY id;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
|
||||
|
||||
OPEN tenant_cursor;
|
||||
|
||||
tenant_loop: LOOP
|
||||
FETCH tenant_cursor INTO current_tenant_id;
|
||||
IF done THEN
|
||||
LEAVE tenant_loop;
|
||||
END IF;
|
||||
|
||||
SET uid_counter = 1;
|
||||
SET done = FALSE;
|
||||
|
||||
OPEN user_cursor;
|
||||
|
||||
user_loop: LOOP
|
||||
FETCH user_cursor INTO current_id;
|
||||
IF done THEN
|
||||
LEAVE user_loop;
|
||||
END IF;
|
||||
|
||||
UPDATE yz_users SET uid = uid_counter WHERE id = current_id;
|
||||
SET uid_counter = uid_counter + 1;
|
||||
END LOOP user_loop;
|
||||
|
||||
CLOSE user_cursor;
|
||||
SET done = FALSE;
|
||||
END LOOP tenant_loop;
|
||||
|
||||
CLOSE tenant_cursor;
|
||||
END //
|
||||
|
||||
DELIMITER ;
|
||||
|
||||
-- 执行存储过程
|
||||
CALL update_user_uids();
|
||||
|
||||
-- 删除存储过程
|
||||
DROP PROCEDURE update_user_uids();
|
||||
|
||||
-- 3. 创建索引以提高查询性能
|
||||
CREATE INDEX idx_yz_users_tenant_uid ON yz_users(tenant_id, uid);
|
||||
CREATE INDEX idx_yz_users_uid ON yz_users(uid);
|
||||
|
||||
-- 4. 验证迁移结果
|
||||
-- SELECT tenant_id, uid, username FROM yz_users WHERE delete_time IS NULL ORDER BY tenant_id, uid;
|
||||
|
||||
-- 5. 添加注释说明
|
||||
-- uid字段说明:
|
||||
-- - 在每个tenant_id下从1开始递增
|
||||
-- - 不同租户的uid可以重复
|
||||
-- - 用于租户内的用户标识,避免使用全局id
|
||||
@@ -0,0 +1,32 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Articles 文章模型(对应表 yz_articles)
|
||||
type Articles struct {
|
||||
Id int `orm:"auto" json:"id"`
|
||||
Title string `orm:"column(title);size(255)" json:"title"`
|
||||
Cate int `orm:"column(cate);default(0)" json:"cate"`
|
||||
Image string `orm:"column(image);type(text);null" json:"image,omitempty"`
|
||||
Desc string `orm:"column(desc);size(500);null" json:"desc,omitempty"`
|
||||
Author string `orm:"column(author);size(255);null" json:"author,omitempty"`
|
||||
Content string `orm:"column(content);type(text)" json:"content"`
|
||||
Publisher int `orm:"column(publisher);null" json:"publisher,omitempty"`
|
||||
Publishdate *time.Time `orm:"column(publishdate);type(datetime);null" json:"publishdate,omitempty"`
|
||||
Sort int `orm:"column(sort);null" json:"sort,omitempty"`
|
||||
Status int8 `orm:"column(status);default(0)" json:"status"`
|
||||
Views int `orm:"column(views);default(0)" json:"views"`
|
||||
Likes int `orm:"column(likes);default(0)" json:"likes"`
|
||||
IsTrans string `orm:"column(is_trans);size(1);default('0')" json:"is_trans"`
|
||||
Transurl string `orm:"column(transurl);type(text);null" json:"transurl,omitempty"`
|
||||
Push string `orm:"column(push);size(255);default('0')" json:"push"`
|
||||
CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"`
|
||||
UpdateTime *time.Time `orm:"column(update_time);type(datetime);null" json:"update_time,omitempty"`
|
||||
DeleteTime *time.Time `orm:"column(delete_time);type(datetime);null" json:"delete_time,omitempty"`
|
||||
}
|
||||
|
||||
func (t *Articles) TableName() string {
|
||||
return "yz_articles"
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
// User 用户模型
|
||||
type User struct {
|
||||
Id int `orm:"auto"`
|
||||
Uid int `orm:"column(uid);default(0)" json:"uid"` // 租户内用户ID,自增
|
||||
TenantId int `orm:"column(tenant_id);default(0)" json:"tenant_id"`
|
||||
Username string
|
||||
Password string
|
||||
@@ -59,6 +60,7 @@ func Init(version string) {
|
||||
orm.RegisterModel(new(DictItem))
|
||||
orm.RegisterModel(new(OperationLog))
|
||||
orm.RegisterModel(new(AccessLog))
|
||||
orm.RegisterModel(new(Articles))
|
||||
|
||||
ormConfig, err := beego.AppConfig.String("orm")
|
||||
if err != nil {
|
||||
|
||||
@@ -391,4 +391,9 @@ func init() {
|
||||
beego.Router("/api/access-logs/user/stats", &controllers.OperationLogController{}, "get:GetUserAccessStats")
|
||||
beego.Router("/api/access-logs/clear", &controllers.OperationLogController{}, "post:ClearOldAccessLogs")
|
||||
|
||||
// 文章管理路由
|
||||
beego.Router("/api/articles", &controllers.ArticlesController{}, "get:ListArticles;post:CreateArticle")
|
||||
beego.Router("/api/articles/:id", &controllers.ArticlesController{}, "get:GetArticle;put:UpdateArticle;delete:DeleteArticle")
|
||||
beego.Router("/api/articles/:id/status", &controllers.ArticlesController{}, "patch:UpdateArticleStatus")
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"server/models"
|
||||
"time"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
)
|
||||
|
||||
// GetArticlesList 获取文章列表
|
||||
func GetArticlesList(tenant_id int, user_id int, page, pageSize int, keyword string, cate int, status int8) ([]*models.Articles, int64, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
qs := o.QueryTable(new(models.Articles))
|
||||
|
||||
// 过滤已删除的文章
|
||||
qs = qs.Filter("delete_time__isnull", true)
|
||||
|
||||
// 关键词搜索
|
||||
if keyword != "" {
|
||||
cond := orm.NewCondition()
|
||||
cond1 := cond.Or("title__icontains", keyword).Or("content__icontains", keyword).Or("desc__icontains", keyword)
|
||||
qs = qs.SetCond(cond1)
|
||||
}
|
||||
|
||||
// 分类筛选
|
||||
if cate > 0 {
|
||||
qs = qs.Filter("cate", cate)
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if status >= 0 {
|
||||
qs = qs.Filter("status", status)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
total, err := qs.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 10
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
var articles []*models.Articles
|
||||
_, err = qs.OrderBy("-create_time").Limit(pageSize, offset).All(&articles)
|
||||
|
||||
return articles, total, err
|
||||
}
|
||||
|
||||
// GetArticleById 根据ID获取文章
|
||||
func GetArticleById(id int) (*models.Articles, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
article := &models.Articles{Id: id}
|
||||
err := o.Read(article)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 检查是否已删除
|
||||
if article.DeleteTime != nil {
|
||||
return nil, nil // 返回nil表示文章不存在或已删除
|
||||
}
|
||||
|
||||
return article, nil
|
||||
}
|
||||
|
||||
// CreateArticle 创建文章
|
||||
func CreateArticle(article *models.Articles) (*models.Articles, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
id, err := o.Insert(article)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取创建后的文章
|
||||
article.Id = int(id)
|
||||
return article, nil
|
||||
}
|
||||
|
||||
// UpdateArticle 更新文章
|
||||
func UpdateArticle(article *models.Articles) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
_, err := o.Update(article, "title", "cate", "image", "desc", "author", "content", "publisher", "publishdate", "sort", "status", "is_trans", "transurl", "push", "update_time")
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteArticle 删除文章(软删除)
|
||||
func DeleteArticle(id int) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
// 获取文章
|
||||
article := &models.Articles{Id: id}
|
||||
err := o.Read(article)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 设置删除时间
|
||||
now := time.Now()
|
||||
article.DeleteTime = &now
|
||||
|
||||
_, err = o.Update(article, "delete_time")
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateArticleStatus 更新文章状态
|
||||
func UpdateArticleStatus(id int, status int8) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
article := &models.Articles{Id: id}
|
||||
err := o.Read(article)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
article.Status = status
|
||||
now := time.Now()
|
||||
article.UpdateTime = &now
|
||||
|
||||
_, err = o.Update(article, "status", "update_time")
|
||||
return err
|
||||
}
|
||||
|
||||
// IncrementArticleViews 增加文章浏览量
|
||||
func IncrementArticleViews(id int) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
article := &models.Articles{Id: id}
|
||||
err := o.Read(article)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
article.Views++
|
||||
now := time.Now()
|
||||
article.UpdateTime = &now
|
||||
|
||||
_, err = o.Update(article, "views", "update_time")
|
||||
return err
|
||||
}
|
||||
|
||||
// IncrementArticleLikes 增加文章点赞量
|
||||
func IncrementArticleLikes(id int) error {
|
||||
o := orm.NewOrm()
|
||||
|
||||
article := &models.Articles{Id: id}
|
||||
err := o.Read(article)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
article.Likes++
|
||||
now := time.Now()
|
||||
article.UpdateTime = &now
|
||||
|
||||
_, err = o.Update(article, "likes", "update_time")
|
||||
return err
|
||||
}
|
||||
|
||||
// GetPublishedArticles 获取已发布的文章列表(用于前台展示)
|
||||
func GetPublishedArticles(page, pageSize int, cate int) ([]*models.Articles, int64, error) {
|
||||
return GetArticlesList(0, 0, page, pageSize, "", cate, 2) // status=2 表示已发布,tenant_id=0, user_id=0
|
||||
}
|
||||
|
||||
// GetArticleStats 获取文章统计信息
|
||||
func GetArticleStats() (map[string]int64, error) {
|
||||
o := orm.NewOrm()
|
||||
|
||||
stats := make(map[string]int64)
|
||||
|
||||
// 总文章数
|
||||
total, err := o.QueryTable(new(models.Articles)).Filter("delete_time__isnull", true).Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["total"] = total
|
||||
|
||||
// 草稿数
|
||||
draft, err := o.QueryTable(new(models.Articles)).Filter("delete_time__isnull", true).Filter("status", 0).Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["draft"] = draft
|
||||
|
||||
// 待审核数
|
||||
pending, err := o.QueryTable(new(models.Articles)).Filter("delete_time__isnull", true).Filter("status", 1).Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["pending"] = pending
|
||||
|
||||
// 已发布数
|
||||
published, err := o.QueryTable(new(models.Articles)).Filter("delete_time__isnull", true).Filter("status", 2).Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["published"] = published
|
||||
|
||||
// 隐藏数
|
||||
hidden, err := o.QueryTable(new(models.Articles)).Filter("delete_time__isnull", true).Filter("status", 3).Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stats["hidden"] = hidden
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
+14
-5
@@ -144,20 +144,29 @@ func AddUser(username, password, email, nickname, avatar string, tenantId, role,
|
||||
return nil, fmt.Errorf("查询用户失败: %v", err)
|
||||
}
|
||||
|
||||
// 3. 生成盐值(每个用户唯一)
|
||||
// 3. 获取该租户下用户的最大Uid,用于生成新的Uid
|
||||
var maxUid int
|
||||
err = o.Raw("SELECT COALESCE(MAX(uid), 0) FROM yz_users WHERE tenant_id = ? AND delete_time IS NULL", tenantId).QueryRow(&maxUid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取最大Uid失败: %v", err)
|
||||
}
|
||||
newUid := maxUid + 1
|
||||
|
||||
// 4. 生成盐值(每个用户唯一)
|
||||
salt, err := generateUserSalt()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("生成盐值失败: %v", err)
|
||||
}
|
||||
|
||||
// 4. 加密密码(结合盐值)
|
||||
// 5. 加密密码(结合盐值)
|
||||
hashedPassword, err := hashUserPassword(password, salt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("密码加密失败: %v", err)
|
||||
}
|
||||
|
||||
// 5. 构建用户对象
|
||||
// 6. 构建用户对象
|
||||
user := &models.User{
|
||||
Uid: newUid,
|
||||
TenantId: tenantId,
|
||||
Username: username,
|
||||
Password: hashedPassword,
|
||||
@@ -171,13 +180,13 @@ func AddUser(username, password, email, nickname, avatar string, tenantId, role,
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
// 6. 插入数据库
|
||||
// 7. 插入数据库
|
||||
_, err = o.Insert(user)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("数据库插入失败: %v", err)
|
||||
}
|
||||
|
||||
// 7. 返回新创建的用户对象
|
||||
// 8. 返回新创建的用户对象
|
||||
return user, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user