Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ pnpm-debug.log*

# Environment files
.env
progress-wall.code-workspace
6 changes: 2 additions & 4 deletions backend/database/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,8 @@ func Migrate(db *gorm.DB) error {
err := db.AutoMigrate(
// 用户和权限相关
&models.User{},
&models.UserPermission{},
&models.UserPermissionGroup{},
&models.UserPermissionGroupUser{},
&models.UserPermissionGroup{},
&models.Team{},
&models.TeamMember{},

// 项目和看板相关
&models.Project{},
Expand Down
25 changes: 2 additions & 23 deletions backend/database/seed.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ func Seed(db *gorm.DB) error {
return err
}

// 创建默认权限
if err := createDefaultPermissions(db); err != nil {
return err
}

log.Println("基础数据初始化完成")
return nil
}
Expand All @@ -43,6 +38,7 @@ func createAdminUser(db *gorm.DB) error {
return nil
}

log.Println("未找到admin用户,正在创建...")
// 加密管理员密码
adminPasswordHash, err := utils.HashPassword("admin123")
if err != nil {
Expand All @@ -56,6 +52,7 @@ func createAdminUser(db *gorm.DB) error {
Password: adminPasswordHash,
Nickname: "系统管理员",
Status: models.UserStatusEnabled,
SystemRole: models.SystemRoleAdmin,
}

if err := db.Create(&adminUser).Error; err != nil {
Expand All @@ -65,21 +62,3 @@ func createAdminUser(db *gorm.DB) error {
log.Println("默认用户创建成功: admin")
return nil
}

// createDefaultPermissions 创建默认权限组
// 该函数负责在系统初始化时创建默认的权限组,包括管理员权限组和访客权限组
// 参数: db - 数据库连接实例
// 返回: error - 如果创建失败则返回错误,否则返回nil
func createDefaultPermissions(db *gorm.DB) error {
adminPermissionGroup := models.UserPermissionGroup{
GroupName: "admin",
PermissionStrings: "*",
}
db.Create(&adminPermissionGroup)
guestPermissionGroup := models.UserPermissionGroup{
GroupName: "guest",
PermissionStrings: "users.list;users.detail",
}
db.Create(&guestPermissionGroup)
return nil
}
31 changes: 28 additions & 3 deletions backend/handlers/board/board.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type BoardHandler struct {

// NewBoardHandler 创建看板处理器
func NewBoardHandler(db *gorm.DB) *BoardHandler {
return &BoardHandler{
return &BoardHandler {
boardService: services.NewBoardService(db),
}
}
Expand Down Expand Up @@ -61,6 +61,24 @@ func (h *BoardHandler) GetBoards(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"boards": boards})
}

// GET /api/projects/:projectId/boards
func (h *BoardHandler) GetBoardsByProject(c *gin.Context) {
projectIDStr := c.Param("projectId")
projectID, err := strconv.ParseUint(projectIDStr, 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Project ID"})
return
}

boards, err := h.boardService.GetBoardsByProjectID(uint(projectID))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"boards": boards})
}

// CreateBoard 创建看板
func (h *BoardHandler) CreateBoard(c *gin.Context) {
userID := c.GetUint("user_id")
Expand All @@ -69,11 +87,18 @@ func (h *BoardHandler) CreateBoard(c *gin.Context) {
return
}

projectIDStr := c.Param("projectId")
projectID, err := strconv.ParseUint(projectIDStr, 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Project ID"})
return
}

var createBoardRequest struct {
Name string `json:"name" binding:"required"`
Description string `json:"description"`
Color string `json:"color"`
ProjectID uint `json:"project_id" binding:"required"`
// ProjectID uint `json:"project_id" binding:"required"`
}

if err := c.ShouldBindJSON(&createBoardRequest); err != nil {
Expand All @@ -85,7 +110,7 @@ func (h *BoardHandler) CreateBoard(c *gin.Context) {
Name: createBoardRequest.Name,
Description: createBoardRequest.Description,
Color: createBoardRequest.Color,
ProjectID: createBoardRequest.ProjectID,
ProjectID: uint(projectID),
OwnerID: userID,
Status: models.BoardStatusActive,
}
Expand Down
36 changes: 29 additions & 7 deletions backend/handlers/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,21 @@ func (h *ProjectHandler) GetProjects(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"projects": projects})
}

// CreateProject 创建项目
// POST /api/teams/:teamId/projects
func (h *ProjectHandler) CreateProject(c *gin.Context) {
userID := c.GetUint("user_id")
userID := c.GetUint("user_id") // Set by AuthMiddleware
if userID == 0 {
c.JSON(http.StatusUnauthorized, gin.H{"error": "无法获取用户信息"})
return
}

teamIDStr := c.Param("teamId")
teamID, err := strconv.ParseUint(teamIDStr, 10, 32) // Should already validated by RBAC Middleware
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Team ID"})
return
}

var createProjectRequest struct {
Name string `json:"name" binding:"required"`
Description string `json:"description"`
Expand All @@ -95,6 +102,7 @@ func (h *ProjectHandler) CreateProject(c *gin.Context) {
StartDate: createProjectRequest.StartDate,
EndDate: createProjectRequest.EndDate,
OwnerID: userID,
TeamID: uint(teamID),
}

if err := h.projectService.CreateProject(project); err != nil {
Expand All @@ -105,6 +113,21 @@ func (h *ProjectHandler) CreateProject(c *gin.Context) {
c.JSON(http.StatusCreated, project)
}

// Gets all projects for a specific team.
// GET /api/teams/:teamId/projects
func (h *ProjectHandler) GetTeamProjects(c *gin.Context) {
teamIDStr := c.Param("teamId")
teamID, _ := strconv.ParseUint(teamIDStr, 10, 32) // Already validated by RBAC Middleware

projects, err := h.projectService.GetTeamProjects(uint(teamID))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch projects: " + err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"projects": projects})
}

// UpdateProject 更新项目
func (h *ProjectHandler) UpdateProject(c *gin.Context) {
projectID, err := strconv.ParseUint(c.Param("projectId"), 10, 32)
Expand All @@ -114,11 +137,11 @@ func (h *ProjectHandler) UpdateProject(c *gin.Context) {
}

var updateProjectRequest struct {
Name *string `json:"name"`
Description *string `json:"description"`
Name *string `json:"name"`
Description *string `json:"description"`
Status *models.ProjectStatus `json:"status"`
StartDate *time.Time `json:"start_date"`
EndDate *time.Time `json:"end_date"`
StartDate *time.Time `json:"start_date"`
EndDate *time.Time `json:"end_date"`
}

if err := c.ShouldBindJSON(&updateProjectRequest); err != nil {
Expand Down Expand Up @@ -174,4 +197,3 @@ func (h *ProjectHandler) DeleteProject(c *gin.Context) {

c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
}

110 changes: 110 additions & 0 deletions backend/handlers/team/team.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package team

import (
"net/http"
"strconv"

"progress-wall-backend/models"
"progress-wall-backend/services"

"github.com/gin-gonic/gin"
"gorm.io/gorm"
)

type TeamHandler struct {
teamService *services.TeamService
}

func NewTeamHandler(db *gorm.DB) *TeamHandler {
return &TeamHandler{
teamService: services.NewTeamService(db),
}
}

// POST /api/teams
func (h *TeamHandler) CreateTeam(c *gin.Context) {
userID := c.GetUint("user_id") // Get from AuthMiddleware

var req struct {
Name string `json:"name" binding:"required"`
Description string `json:"description"`
}

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid parameters"})
return
}

team, err := h.teamService.CreateTeam(req.Name, req.Description, userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusCreated, team)
}

// Gets all teams a user belongs to.
// GET /api/teams
func (h *TeamHandler) GetMyTeams(c *gin.Context) {
userID := c.GetUint("user_id")

teams, err := h.teamService.GetUserTeams(userID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch teams: " + err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"teams": teams})
}

// POST /api/teams/:teamId/members
func (h *TeamHandler) AddMember(c *gin.Context) {
teamIDStr := c.Param("teamId")
teamID, err := strconv.ParseUint(teamIDStr, 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Team ID"})
return
}

// Who to add, and what role
var req struct {
UserID uint `json:"user_id" binding:"required"`
Role models.TeamRole `json:"role"`
}

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid parameters"})
return
}

// Default to Member if role is invalid
if req.Role != models.TeamRoleAdmin {
req.Role = models.TeamRoleMember
}

if err := h.teamService.AddMember(uint(teamID), req.UserID, req.Role); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"message": "Member added successfully"})
}

// GET /api/teams/:teamId/members
func (h *TeamHandler) GetTeamMembers(c *gin.Context) {
teamIDStr := c.Param("teamId")
teamID, err := strconv.ParseUint(teamIDStr, 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Team ID"})
return
}

members, err := h.teamService.GetTeamMembers(uint(teamID))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch team members: " + err.Error()})
return
}

c.JSON(http.StatusOK, gin.H{"members": members})
}
Loading