730 lines
15 KiB
Go
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
|
|
}
|