package org import ( "fmt" "strings" "time" "github.com/veypi/vbase/internal/api/middleware" "github.com/veypi/vbase/internal/model" "github.com/veypi/vigo" "gorm.io/gorm" ) // ListRequest 组织列表请求 type ListRequest struct { Page int `json:"page" src:"query" default:"1" desc:"页码"` PageSize int `json:"page_size" src:"query" default:"10" desc:"每页数量"` Keyword string `json:"keyword" src:"query" desc:"搜索关键词"` } // ListResponse 组织列表响应 type ListResponse struct { Items []OrgInfo `json:"items"` Total int64 `json:"total"` Page int `json:"page"` PageSize int `json:"page_size"` TotalPages int `json:"total_pages"` } // OrgInfo 组织信息 type OrgInfo struct { ID string `json:"id"` Name string `json:"name"` Code string `json:"code"` OwnerID string `json:"owner_id"` ParentID string `json:"parent_id,omitempty"` Path string `json:"path"` Level int `json:"level"` LeaderID string `json:"leader_id,omitempty"` Description string `json:"description"` Logo string `json:"logo"` Status int `json:"status"` MaxMembers int `json:"max_members"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` // 当前用户在该组织的信息 MyRoles []string `json:"my_roles,omitempty"` MyStatus int `json:"my_status,omitempty"` } // List 获取当前用户的组织列表 func List(x *vigo.X, req *ListRequest) (*ListResponse, error) { userID := middleware.CurrentUser(x) if userID == "" { return nil, vigo.ErrNotAuthorized } if req.Page < 1 { req.Page = 1 } if req.PageSize < 1 || req.PageSize > 100 { req.PageSize = 10 } // 查询用户所属的组织ID var memberOrgs []model.OrgMember if err := model.DB.Where("user_id = ? AND status = ?", userID, model.MemberStatusActive).Find(&memberOrgs).Error; err != nil { return nil, vigo.ErrInternalServer.WithError(err) } if len(memberOrgs) == 0 { return &ListResponse{ Items: []OrgInfo{}, Total: 0, Page: req.Page, PageSize: req.PageSize, TotalPages: 0, }, nil } orgIDs := make([]string, 0, len(memberOrgs)) memberMap := make(map[string]*model.OrgMember) for _, m := range memberOrgs { orgIDs = append(orgIDs, m.OrgID) memberMap[m.OrgID] = &m } // 查询组织详情 var total int64 query := model.DB.Model(&model.Org{}).Where("id IN ?", orgIDs) if req.Keyword != "" { query = query.Where("name LIKE ? OR code LIKE ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%") } if err := query.Count(&total).Error; err != nil { return nil, vigo.ErrInternalServer.WithError(err) } var orgs []model.Org offset := (req.Page - 1) * req.PageSize if err := query.Offset(offset).Limit(req.PageSize).Order("created_at DESC").Find(&orgs).Error; err != nil { return nil, vigo.ErrInternalServer.WithError(err) } items := make([]OrgInfo, 0, len(orgs)) for _, o := range orgs { info := toOrgInfo(&o) if m, ok := memberMap[o.ID]; ok { info.MyRoles = parseRoles(m.RoleIDs) info.MyStatus = m.Status } items = append(items, info) } totalPages := int((total + int64(req.PageSize) - 1) / int64(req.PageSize)) return &ListResponse{ Items: items, Total: total, Page: req.Page, PageSize: req.PageSize, TotalPages: totalPages, }, nil } // CreateRequest 创建组织请求 type CreateRequest struct { Name string `json:"name" src:"json" desc:"组织名称"` Code string `json:"code" src:"json" desc:"组织编码"` ParentID *string `json:"parent_id" src:"json" desc:"父组织ID"` Description *string `json:"description" src:"json" desc:"描述"` Logo *string `json:"logo" src:"json" desc:"Logo"` MaxMembers *int `json:"max_members" src:"json" desc:"最大成员数"` } // Create 创建组织 func Create(x *vigo.X, req *CreateRequest) (*OrgInfo, error) { userID := middleware.CurrentUser(x) if userID == "" { return nil, vigo.ErrNotAuthorized } // 检查编码是否已存在 var count int64 model.DB.Model(&model.Org{}).Where("code = ?", req.Code).Count(&count) if count > 0 { return nil, vigo.ErrArgInvalid.WithString("organization code already exists") } // 构建组织路径 path := "/" + req.Code level := 0 if req.ParentID != nil && *req.ParentID != "" { var parent model.Org if err := model.DB.First(&parent, "id = ?", *req.ParentID).Error; err != nil { return nil, vigo.ErrArgInvalid.WithString("parent organization not found") } path = parent.Path + "/" + req.Code level = parent.Level + 1 } org := &model.Org{ Name: req.Name, Code: req.Code, OwnerID: userID, Path: path, Level: level, Status: model.OrgStatusActive, MaxMembers: 100, } if req.ParentID != nil { org.ParentID = req.ParentID } if req.Description != nil { org.Description = *req.Description } if req.Logo != nil { org.Logo = *req.Logo } if req.MaxMembers != nil { org.MaxMembers = *req.MaxMembers } err := model.DB.Transaction(func(tx *gorm.DB) error { // 创建组织 if err := tx.Create(org).Error; err != nil { return err } // 创建成员关系(所有者) member := &model.OrgMember{ OrgID: org.ID, UserID: userID, Status: model.MemberStatusActive, JoinedAt: time.Now().Format("2006-01-02 15:04:05"), } return tx.Create(member).Error }) if err != nil { return nil, vigo.ErrInternalServer.WithError(err) } info := toOrgInfo(org) return &info, nil } // GetRequest 获取组织请求 type GetRequest struct { ID string `json:"id" src:"path@org_id" desc:"组织ID"` } // Get 获取组织详情 func Get(x *vigo.X, req *GetRequest) (*OrgInfo, error) { userID := middleware.CurrentUser(x) if userID == "" { return nil, vigo.ErrNotAuthorized } var org model.Org if err := model.DB.First(&org, "id = ?", req.ID).Error; err != nil { return nil, vigo.ErrNotFound } // 检查用户是否是组织成员 var member model.OrgMember if err := model.DB.Where("org_id = ? AND user_id = ?", req.ID, userID).First(&member).Error; err != nil { return nil, vigo.ErrForbidden.WithString("you are not a member of this organization") } info := toOrgInfo(&org) info.MyRoles = parseRoles(member.RoleIDs) info.MyStatus = member.Status return &info, nil } // UpdateRequest 更新组织请求 type UpdateRequest struct { ID string `json:"id" src:"path@org_id" desc:"组织ID"` Name *string `json:"name" src:"json" desc:"组织名称"` Description *string `json:"description" src:"json" desc:"描述"` Logo *string `json:"logo" src:"json" desc:"Logo"` LeaderID *string `json:"leader_id" src:"json" desc:"负责人ID"` MaxMembers *int `json:"max_members" src:"json" desc:"最大成员数"` Status *int `json:"status" src:"json" desc:"状态"` } // Update 更新组织 func Update(x *vigo.X, req *UpdateRequest) (*OrgInfo, error) { userID := middleware.CurrentUser(x) if userID == "" { return nil, vigo.ErrNotAuthorized } var org model.Org if err := model.DB.First(&org, "id = ?", req.ID).Error; err != nil { return nil, vigo.ErrNotFound } // 检查权限(只有所有者可以修改) if org.OwnerID != userID { return nil, vigo.ErrForbidden.WithString("only organization owner can update") } updates := make(map[string]interface{}) if req.Name != nil { updates["name"] = *req.Name } if req.Description != nil { updates["description"] = *req.Description } if req.Logo != nil { updates["logo"] = *req.Logo } if req.LeaderID != nil { updates["leader_id"] = *req.LeaderID } if req.MaxMembers != nil { updates["max_members"] = *req.MaxMembers } if req.Status != nil { updates["status"] = *req.Status } if len(updates) > 0 { if err := model.DB.Model(&org).Updates(updates).Error; err != nil { return nil, vigo.ErrInternalServer.WithError(err) } } info := toOrgInfo(&org) return &info, nil } // DeleteRequest 删除组织请求 type DeleteRequest struct { ID string `json:"id" src:"path@org_id" desc:"组织ID"` } // Delete 删除组织 func Delete(x *vigo.X, req *DeleteRequest) error { userID := middleware.CurrentUser(x) if userID == "" { return vigo.ErrNotAuthorized } var org model.Org if err := model.DB.First(&org, "id = ?", req.ID).Error; err != nil { return vigo.ErrNotFound } // 检查权限(只有所有者可以删除) if org.OwnerID != userID { return vigo.ErrForbidden.WithString("only organization owner can delete") } // 检查是否有子组织 var childCount int64 model.DB.Model(&model.Org{}).Where("parent_id = ?", req.ID).Count(&childCount) if childCount > 0 { return vigo.ErrArgInvalid.WithString("cannot delete organization with sub-organizations") } // 软删除 if err := model.DB.Delete(&org).Error; err != nil { return vigo.ErrInternalServer.WithError(err) } return nil } // TreeResponse 组织树响应 type TreeResponse struct { OrgInfo Children []TreeResponse `json:"children,omitempty"` } // Tree 获取组织树 func Tree(x *vigo.X) ([]TreeResponse, error) { userID := middleware.CurrentUser(x) if userID == "" { return nil, vigo.ErrNotAuthorized } // 获取用户所属的所有组织ID var memberOrgs []model.OrgMember if err := model.DB.Where("user_id = ? AND status = ?", userID, model.MemberStatusActive).Find(&memberOrgs).Error; err != nil { return nil, vigo.ErrInternalServer.WithError(err) } if len(memberOrgs) == 0 { return []TreeResponse{}, nil } orgIDs := make([]string, 0, len(memberOrgs)) for _, m := range memberOrgs { orgIDs = append(orgIDs, m.OrgID) } // 获取所有组织 var orgs []model.Org if err := model.DB.Where("id IN ?", orgIDs).Order("path").Find(&orgs).Error; err != nil { return nil, vigo.ErrInternalServer.WithError(err) } // 构建树 return buildOrgTree(orgs, ""), nil } func buildOrgTree(orgs []model.Org, parentID string) []TreeResponse { var result []TreeResponse for _, o := range orgs { if (parentID == "" && (o.ParentID == nil || *o.ParentID == "")) || (o.ParentID != nil && *o.ParentID == parentID) { node := TreeResponse{ OrgInfo: toOrgInfo(&o), } node.Children = buildOrgTree(orgs, o.ID) result = append(result, node) } } return result } // helper functions func toOrgInfo(o *model.Org) OrgInfo { info := OrgInfo{ ID: o.ID, Name: o.Name, Code: o.Code, OwnerID: o.OwnerID, Path: o.Path, Level: o.Level, Description: o.Description, Logo: o.Logo, Status: o.Status, MaxMembers: o.MaxMembers, CreatedAt: o.CreatedAt.Format("2006-01-02 15:04:05"), UpdatedAt: o.UpdatedAt.Format("2006-01-02 15:04:05"), } if o.ParentID != nil { info.ParentID = *o.ParentID } if o.LeaderID != nil { info.LeaderID = *o.LeaderID } return info } func parseRoles(roleIDs string) []string { if roleIDs == "" { return []string{} } return strings.Split(roleIDs, ",") } func formatRoleID(roleIDs []string) string { return strings.Join(roleIDs, ",") } // ListMembersRequest 成员列表请求 type ListMembersRequest struct { OrgID string `json:"org_id" src:"path@org_id" desc:"组织ID"` Page int `json:"page" src:"query" default:"1" desc:"页码"` PageSize int `json:"page_size" src:"query" default:"10" desc:"每页数量"` Status *int `json:"status" src:"query" desc:"状态筛选"` } // MemberInfo 成员信息 type MemberInfo struct { ID string `json:"id"` OrgID string `json:"org_id"` UserID string `json:"user_id"` Username string `json:"username"` Nickname string `json:"nickname"` Avatar string `json:"avatar"` Email string `json:"email"` Roles []string `json:"roles"` Position string `json:"position"` Department string `json:"department"` JoinedAt string `json:"joined_at"` Status int `json:"status"` } // ListMembers 获取组织成员列表 func ListMembers(x *vigo.X, req *ListMembersRequest) (*ListResponse, error) { userID := middleware.CurrentUser(x) if userID == "" { return nil, vigo.ErrNotAuthorized } if req.Page < 1 { req.Page = 1 } if req.PageSize < 1 || req.PageSize > 100 { req.PageSize = 10 } // 检查用户是否是组织成员 var currentMember model.OrgMember if err := model.DB.Where("org_id = ? AND user_id = ?", req.OrgID, userID).First(¤tMember).Error; err != nil { return nil, vigo.ErrForbidden.WithString("you are not a member of this organization") } var total int64 query := model.DB.Model(&model.OrgMember{}).Where("org_id = ?", req.OrgID) if req.Status != nil { query = query.Where("status = ?", *req.Status) } if err := query.Count(&total).Error; err != nil { return nil, vigo.ErrInternalServer.WithError(err) } var members []model.OrgMember offset := (req.Page - 1) * req.PageSize if err := query.Offset(offset).Limit(req.PageSize).Find(&members).Error; err != nil { return nil, vigo.ErrInternalServer.WithError(err) } items := make([]OrgInfo, 0) // 这里需要修改返回类型 _ = items // 获取用户信息 memberInfos := make([]MemberInfo, 0, len(members)) for _, m := range members { var user model.User if err := model.DB.First(&user, "id = ?", m.UserID).Error; err != nil { continue } memberInfos = append(memberInfos, MemberInfo{ ID: m.ID, OrgID: m.OrgID, UserID: m.UserID, Username: user.Username, Nickname: user.Nickname, Avatar: user.Avatar, Email: user.Email, Roles: parseRoles(m.RoleIDs), Position: m.Position, Department: m.Department, JoinedAt: m.JoinedAt, Status: m.Status, }) } _ = memberInfos return nil, fmt.Errorf("not fully implemented") }