Files
workspace/code/allapp/allapp-go/api/handler/space/space.go
T
2026-03-04 17:30:05 +08:00

730 lines
15 KiB
Go

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
}