package space import ( "allapp/api/router/middleware" "allapp/api/types" "allapp/db" "allapp/db/repo" "allapp/utils" "allapp/utils/errorx" "allapp/utils/pgtypex" "allapp/utils/redisx" "allapp/utils/requestx" "allapp/utils/responsex" "allapp/utils/uniqueid" "context" "github.com/gofiber/fiber/v3" ) // CreateSpace 创建空间 func CreateSpace(c fiber.Ctx) error { dto := new(types.CreateSpaceDTO) vo := new(types.CreateSpaceVO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userID, err2 := middleware.GetUserID(c) if err2 != nil { return err2 } var inviteCode string for { // 生成邀请码 code := utils.GenerateCode(6) // 检查数据库是否存在 _, err := db.Queries.GetSpaceByInviteCode(c.Context(), code) if err != nil { if errorx.IsNotFound(err) { inviteCode = code break } else { return err } } } spaceId := uniqueid.NextId() err := db.WithTx(c.Context(), func(q *repo.Queries) error { // 查询是否存在空间 countSpaces, err3 := q.CountUserSpaces(c.Context(), userID) if err3 != nil { return err3 } isDefault := 0 if countSpaces == 0 { isDefault = 1 } // 创建空间 params := repo.InsertSpaceParams{ ID: spaceId, Name: dto.Name, InviteCode: inviteCode, OwnerID: userID, } err := q.InsertSpace(c.Context(), params) if err != nil { return err } // 将创建用户默认添加到该空间内 spaceMemberParams := repo.InsertSpaceMemberParams{ ID: uniqueid.NextId(), SpaceID: spaceId, UserID: userID, Role: 0, IsDefault: int16(isDefault), MemberName: dto.MemberName, SortNumber: dto.SortNumber, } err = q.InsertSpaceMember(c.Context(), spaceMemberParams) if err != nil { return err } // 初始化记账 err = initFinanceCategory(c.Context(), q, spaceId, userID) if err != nil { return err } return nil }) if err != nil { return err } vo.SpaceId = spaceId vo.InviteCode = inviteCode return responsex.OK(c, vo) } // ListUserSpaces 查询该用户的空间 func ListUserSpaces(c fiber.Ctx) error { vo := new(types.ListSpacesVO) userID, err := middleware.GetUserID(c) if err != nil { return err } rows, err := db.Queries.ListSpacesByUserId(c.Context(), userID) if err != nil { return err } userSpaces := make([]types.UserSpace, 0, len(rows)) for _, row := range rows { userSpaces = append(userSpaces, types.UserSpace{ SpaceId: row.SpaceID, OwnerId: row.OwnerID, Name: row.Name, Role: row.Role, MemberCount: row.MemberCount, IsDefault: row.IsDefault, MemberName: row.MemberName, SortNumber: row.SortNumber, }) } vo.Spaces = userSpaces return responsex.OK(c, vo) } // DissolveSpace 解散空间 func DissolveSpace(c fiber.Ctx) error { dto := new(types.DissolveSpaceDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userId, err := middleware.GetUserID(c) if err != nil { return err } spaceId := dto.SpaceId space, err := db.Queries.GetSpaceById(c.Context(), spaceId) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "空间不存在或已解散") } return err } if userId != space.OwnerID { return responsex.Error(c, "您不是该空间的拥有者,无法解散") } if err := db.Queries.DissolveSpace(c.Context(), spaceId); err != nil { return err } err = redisx.SpaceDelete(spaceId) if err != nil { return err } return responsex.OK(c, "空间已成功解散") } // ExitSpace 退出空间 func ExitSpace(c fiber.Ctx) error { dto := new(types.ExitSpaceDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userId, err := middleware.GetUserID(c) if err != nil { return err } spaceId := dto.SpaceId _, err = db.Queries.GetSpaceById(c.Context(), spaceId) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "空间不存在或已解散") } return err } if err := db.Queries.UpdateSpaceMemberExit(c.Context(), repo.UpdateSpaceMemberExitParams{ SpaceID: spaceId, UserID: userId, }); err != nil { return err } err = redisx.SpaceRemoveMember(spaceId, userId) if err != nil { return err } return responsex.OK(c, "空间已成功退出") } // UpdateSpaceName 更新空间名称 func UpdateSpaceName(c fiber.Ctx) error { dto := new(types.UpdateSpaceNameDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userId, err := middleware.GetUserID(c) if err != nil { return err } spaceId := dto.SpaceId space, err := db.Queries.GetSpaceById(c.Context(), spaceId) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "空间不存在或已解散") } return err } if userId != space.OwnerID { return responsex.Error(c, "您不是该空间的拥有者,无法修改群名称") } err = db.Queries.UpdateSpaceName(c.Context(), repo.UpdateSpaceNameParams{ Name: dto.SpaceName, ID: spaceId, }) if err != nil { return err } return responsex.OK(c, "更新成功") } // GetSpaceSettings 获取空间配置信息 func GetSpaceSettings(c fiber.Ctx) error { dto := new(types.GetSpaceSettingDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userId, err := middleware.GetUserID(c) if err != nil { return err } spaceId := dto.SpaceId row, err := db.Queries.GetSpaceByUserIdAndSpaceId(c.Context(), repo.GetSpaceByUserIdAndSpaceIdParams{ UserID: userId, ID: spaceId, }) if err != nil { if errorx.IsNotFound(err) { return responsex.SpaceError(c, "不在该空间或该空间已解散") } return err } vo := new(types.UserSpace) vo.SpaceId = row.SpaceID vo.Name = row.Name vo.IsDefault = row.IsDefault vo.MemberName = row.MemberName vo.Role = row.Role vo.MemberCount = row.MemberCount vo.OwnerId = row.OwnerID vo.SortNumber = row.SortNumber vo.InviteCode = row.InviteCode return responsex.OK(c, vo) } func ListSpaceMembers(c fiber.Ctx) error { dto := new(types.ListSpaceMembersDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } members, err := db.Queries.ListSpaceMembers(c.Context(), dto.SpaceId) if err != nil { return err } spaceMembers := make([]types.SpaceMember, 0, len(members)) for _, member := range members { spaceMembers = append(spaceMembers, types.SpaceMember{ SpaceId: member.SpaceID, UserId: member.UserID, Role: member.Role, MemberName: member.MemberName, MemberStatus: member.MemberStatus, MemberUpdateTime: pgtypex.TimestamptzToMillis(member.MemberUpdateTime), JoinTime: pgtypex.TimestamptzToMillis(member.JoinTime), Avatar: pgtypex.TextToString(member.Avatar), UserStatus: pgtypex.Int2ToInt16(member.UserStatus), LastLoginTime: pgtypex.TimestamptzToMillis(member.LastLoginTime), }) } vo := new(types.ListSpaceMembersVO) vo.Members = spaceMembers return responsex.OK(c, vo) } // KickoutSpaceMember 踢出空间 func KickoutSpaceMember(c fiber.Ctx) error { dto := new(types.KickoutSpaceMemberDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userId, err := middleware.GetUserID(c) if err != nil { return err } spaceId := dto.SpaceId space, err := db.Queries.GetSpaceById(c.Context(), spaceId) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "空间不存在或已解散") } return err } if userId != space.OwnerID { return responsex.Error(c, "您不是该空间的拥有者") } _, err = db.Queries.GetSpaceByUserIdAndSpaceId(c.Context(), repo.GetSpaceByUserIdAndSpaceIdParams{ ID: spaceId, UserID: dto.MemberId, }) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "该用户已不再该空间内") } return err } if err := db.Queries.UpdateSpaceMemberExit(c.Context(), repo.UpdateSpaceMemberExitParams{ SpaceID: spaceId, UserID: dto.MemberId, }); err != nil { return err } err = redisx.SpaceRemoveMember(spaceId, dto.MemberId) if err != nil { return err } return responsex.OK(c, "已踢出该空间") } // TransferSpace 转让空间 func TransferSpace(c fiber.Ctx) error { dto := new(types.TransferSpaceDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userId, err := middleware.GetUserID(c) if err != nil { return err } spaceId := dto.SpaceId space, err := db.Queries.GetSpaceById(c.Context(), spaceId) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "空间不存在或已解散") } return err } if userId != space.OwnerID { return responsex.Error(c, "您不是该空间的拥有者") } _, err = db.Queries.GetSpaceByUserIdAndSpaceId(c.Context(), repo.GetSpaceByUserIdAndSpaceIdParams{ ID: spaceId, UserID: dto.MemberId, }) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "用户已不再该空间内") } return err } // 转让 err = db.WithTx(c.Context(), func(q *repo.Queries) error { err2 := q.UpdateSpaceOwnerId(c.Context(), repo.UpdateSpaceOwnerIdParams{ OwnerID: dto.MemberId, ID: spaceId, }) if err2 != nil { return err2 } err2 = q.UpdateSpaceMemberRole(c.Context(), repo.UpdateSpaceMemberRoleParams{ SpaceID: spaceId, Role: 0, UserID: dto.MemberId, }) if err2 != nil { return err2 } err2 = q.UpdateSpaceMemberRole(c.Context(), repo.UpdateSpaceMemberRoleParams{ SpaceID: spaceId, Role: 2, UserID: userId, }) if err2 != nil { return err2 } return nil }) if err != nil { return err } err = redisx.SpaceAddOrUpdateMember(spaceId, dto.MemberId, 0) if err != nil { return err } err = redisx.SpaceAddOrUpdateMember(spaceId, userId, 2) if err != nil { return err } return responsex.OK(c, nil) } func UpdateSpaceInviteCode(c fiber.Ctx) error { dto := new(types.UpdateSpaceInviteCodeDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userId, err := middleware.GetUserID(c) if err != nil { return err } spaceId := dto.SpaceId space, err := db.Queries.GetSpaceById(c.Context(), spaceId) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "空间不存在或已解散") } return err } if userId != space.OwnerID { return responsex.Error(c, "您不是该空间的拥有者") } var inviteCode string for { // 生成邀请码 code := utils.GenerateCode(6) // 检查数据库是否存在 _, err := db.Queries.GetSpaceByInviteCode(c.Context(), code) if err != nil { if errorx.IsNotFound(err) { inviteCode = code break } else { return err } } } err = db.Queries.UpdateSpaceInviteCode(c.Context(), repo.UpdateSpaceInviteCodeParams{ ID: spaceId, InviteCode: inviteCode, }) if err != nil { return err } vo := new(types.UpdateSpaceInviteCodeVO) vo.InviteCode = inviteCode return responsex.OK(c, vo) } // JoinSpace 加入空间 func JoinSpace(c fiber.Ctx) error { dto := new(types.JoinSpaceDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userId, err := middleware.GetUserID(c) if err != nil { return err } space, err := db.Queries.GetSpaceByInviteCode(c.Context(), dto.InviteCode) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "邀请码无效") } return err } if space.Status == 2 { return responsex.Error(c, "邀请码无效") } spaceMember, err := db.Queries.GetSpaceMember(c.Context(), repo.GetSpaceMemberParams{ SpaceID: space.ID, UserID: userId, }) isNew := false if err != nil { if errorx.IsNotFound(err) { isNew = true } else { return err } } if isNew { err = db.Queries.InsertSpaceMember(c.Context(), repo.InsertSpaceMemberParams{ ID: uniqueid.NextId(), SpaceID: space.ID, UserID: userId, Role: 2, IsDefault: int16(0), MemberName: dto.MemberName, SortNumber: 0, }) } else { if spaceMember.Status != 2 { return responsex.Error(c, "已在该空间内,不用重新加入") } err = db.Queries.UpdateSpaceMemberJoin(c.Context(), repo.UpdateSpaceMemberJoinParams{ SpaceID: space.ID, UserID: userId, }) } if err != nil { return err } return responsex.OK(c, nil) } // EnterSpace 进入空间 func EnterSpace(c fiber.Ctx) error { dto := new(types.EnterSpaceDTO) if err := requestx.BindAndValidate(c, dto); err != nil { return err } userId, err := middleware.GetUserID(c) if err != nil { return err } space, err := db.Queries.GetSpaceById(c.Context(), dto.SpaceId) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "不在该空间内") } return err } if space.Status == 2 { return responsex.Error(c, "不在该空间内") } spaceMember, err := db.Queries.GetSpaceMember(c.Context(), repo.GetSpaceMemberParams{ SpaceID: dto.SpaceId, UserID: userId, }) if err != nil { if errorx.IsNotFound(err) { return responsex.Error(c, "不在该空间内") } return err } if spaceMember.Status == 2 { return responsex.Error(c, "不在该空间内") } err = redisx.SpaceAddOrUpdateMember(dto.SpaceId, userId, spaceMember.Role) if err != nil { return err } return responsex.OK(c, nil) } // EnterDefaultSpace 默认进入空间,登录用 func EnterDefaultSpace(c fiber.Ctx) error { userId, err := middleware.GetUserID(c) if err != nil { return err } vo := new(types.EnterSpaceDefaultVO) spaceMember, err := db.Queries.GetSpaceMemberByDefault(c.Context(), userId) if err != nil { if errorx.IsNotFound(err) { return responsex.OK(c, vo) } return err } err = redisx.SpaceAddOrUpdateMember(spaceMember.SpaceID, userId, spaceMember.Role) if err != nil { return err } vo.SpaceId = spaceMember.SpaceID vo.Role = spaceMember.Role return responsex.OK(c, vo) } // 初始化记账分类 func initFinanceCategory(ctx context.Context, q *repo.Queries, spaceId int64, userId int64) error { categoryDefaults, err := q.ListFinanceCategoryDefault(ctx) if err != nil { return err } if len(categoryDefaults) == 0 { return nil } categorySpaces := make([]repo.BatchInsertFinanceCategorySpaceParams, 0, len(categoryDefaults)) for _, sysCat := range categoryDefaults { categorySpaces = append(categorySpaces, repo.BatchInsertFinanceCategorySpaceParams{ ID: uniqueid.NextId(), SpaceID: spaceId, Name: sysCat.Name, Icon: sysCat.Icon, Type: sysCat.Type, SortNumber: sysCat.SortNumber, CreateBy: userId, UpdateBy: userId, }) } if _, err := q.BatchInsertFinanceCategorySpace(ctx, categorySpaces); err != nil { return err } return nil }