gob 등록

This commit is contained in:
2023-07-17 17:47:07 +09:00
parent 67cca13326
commit ba61a11659
7 changed files with 125 additions and 39 deletions

View File

@ -2,7 +2,6 @@ package core
import ( import (
"encoding/json" "encoding/json"
"io"
"net/http" "net/http"
common "repositories.action2quare.com/ayo/gocommon" common "repositories.action2quare.com/ayo/gocommon"
@ -486,14 +485,15 @@ func (sub *subTavern) UpdateGroupDocument(w http.ResponseWriter, r *http.Request
return return
} }
body, err := io.ReadAll(r.Body) var frag bson.M
if err != nil { dec := json.NewDecoder(r.Body)
if err := dec.Decode(&frag); err != nil {
logger.Error("UpdateGroupDocument failed. readBsonDoc err :", err) logger.Error("UpdateGroupDocument failed. readBsonDoc err :", err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
if err := group.UpdateGroupDocument(gid, body); err != nil { if err := group.UpdateGroupDocument(gid, frag); err != nil {
logger.Error("UpdateGroupDocument failed. group.UpdateGroupDocument returns err :", err) logger.Error("UpdateGroupDocument failed. group.UpdateGroupDocument returns err :", err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return

View File

@ -37,5 +37,5 @@ type group interface {
Leave(groupID primitive.ObjectID, memberID primitive.ObjectID) error Leave(groupID primitive.ObjectID, memberID primitive.ObjectID) error
UpdateMemberDocument(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error UpdateMemberDocument(groupID primitive.ObjectID, memberID primitive.ObjectID, doc bson.M) error
Dismiss(groupID primitive.ObjectID) error Dismiss(groupID primitive.ObjectID) error
UpdateGroupDocument(groupID primitive.ObjectID, body []byte) error UpdateGroupDocument(groupID primitive.ObjectID, doc bson.M) error
} }

View File

@ -2,7 +2,10 @@ package core
import ( import (
"context" "context"
"encoding/gob"
"encoding/json"
"errors" "errors"
"fmt"
"net/url" "net/url"
"time" "time"
@ -18,6 +21,11 @@ import (
type accountID = primitive.ObjectID type accountID = primitive.ObjectID
type ticketID = primitive.ObjectID type ticketID = primitive.ObjectID
type groupID = primitive.ObjectID type groupID = primitive.ObjectID
type Body = bson.M
func init() {
gob.Register(memberDoc{})
}
func makeTid(gid groupID, in accountID) string { func makeTid(gid groupID, in accountID) string {
var out primitive.ObjectID var out primitive.ObjectID
@ -28,32 +36,36 @@ func makeTid(gid groupID, in accountID) string {
} }
type Invitation struct { type Invitation struct {
GroupID groupID `json:"gid"` GroupID groupID `json:"_gid"`
TicketID string `json:"tid"` TicketID string `json:"_tid"`
Inviter bson.M `json:"inviter"` Inviter bson.M `json:"_inviter"` // memberDoc.Body
ExpireAtUTC int64 `json:"expire_at_utc"` ExpireAtUTC int64 `json:"_expire_at_utc"`
} }
// 플레이어한테 공유하는 멤버 정보 // 플레이어한테 공유하는 멤버 정보
type memberDoc struct { type memberDoc struct {
Body bson.M `json:"body"` Body `json:",inline"`
Invite bool `json:"invite"` Invite bool `json:"_invite"`
InviteExpire int64 `json:"invite_exp"` InviteExpire int64 `json:"_invite_exp"`
} }
type GroupDocBody = bson.M
type InvitationFail bson.M type InvitationFail bson.M
type groupDoc struct { type groupDoc struct {
Body GroupDocBody `json:"body"` Body `json:",inline"`
Members map[string]*memberDoc `json:"members"` Members map[string]*memberDoc `json:"_members"`
InCharge string `json:"incharge"` InCharge string `json:"_incharge"`
rh *RedisonHandler rh *RedisonHandler
id groupID id groupID
idhex string idhex string
} }
type groupDocWithId struct {
*groupDoc `json:",inline"`
Gid string `json:"_gid"`
}
func (gd *groupDoc) strid() string { func (gd *groupDoc) strid() string {
if len(gd.idhex) == 0 { if len(gd.idhex) == 0 {
gd.idhex = gd.id.Hex() gd.idhex = gd.id.Hex()
@ -75,11 +87,11 @@ func (gd *groupDoc) mid(tid string) accountID {
} }
func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*memberDoc, error) { func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*memberDoc, error) {
mid := inviteeDoc["_mid"].(accountID) targetmid := inviteeDoc["_mid"].(accountID)
body := inviteeDoc["body"].(bson.M) targetbody := inviteeDoc["body"].(bson.M)
// 초대 가능한 빈 자리가 있나 // 초대 가능한 빈 자리가 있나
tids, err := gd.rh.JSONObjKeys(gd.strid(), "$.members") tids, err := gd.rh.JSONObjKeys(gd.strid(), "$._members")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -87,19 +99,21 @@ func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*m
now := time.Now().UTC() now := time.Now().UTC()
createNewDoc := func() *memberDoc { createNewDoc := func() *memberDoc {
return &memberDoc{ return &memberDoc{
Body: body, Body: targetbody,
Invite: true, Invite: true,
InviteExpire: now.Add(ttl).Unix(), InviteExpire: now.Add(ttl).Unix(),
} }
} }
newtid := gd.tid(targetmid)
if len(tids) < max { if len(tids) < max {
// 빈자리를 찾았다.
newdoc := createNewDoc() newdoc := createNewDoc()
_, err := gd.rh.JSONSet(gd.strid(), "$.members."+gd.tid(mid), newdoc) _, err := gd.rh.JSONSet(gd.strid(), "$._members."+newtid, newdoc)
return newdoc, err return newdoc, err
} }
expires, err := gd.rh.JSONGetInt64(gd.strid(), "$.members..invite_exp") expires, err := gd.rh.JSONGetInt64(gd.strid(), "$._members.._invite_exp")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -108,7 +122,7 @@ func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*m
for i, expire := range expires { for i, expire := range expires {
if expire < now.Unix() { if expire < now.Unix() {
// 만료된 초대가 있네? 지우자 // 만료된 초대가 있네? 지우자
delpaths = append(delpaths, "$.members."+tids[i]) delpaths = append(delpaths, "$._members."+tids[i])
} }
} }
@ -122,18 +136,18 @@ func (gd *groupDoc) addInvite(inviteeDoc bson.M, ttl time.Duration, max int) (*m
} }
newdoc := createNewDoc() newdoc := createNewDoc()
_, err = gd.rh.JSONSet(gd.strid(), "$.members."+mid.Hex(), newdoc) _, err = gd.rh.JSONSet(gd.strid(), "$._members."+newtid, newdoc)
return newdoc, err return newdoc, err
} }
func (gd *groupDoc) addMember(mid accountID, doc bson.M) (*memberDoc, error) { func (gd *groupDoc) addMember(mid accountID, doc bson.M) (*memberDoc, error) {
memdoc := &memberDoc{ memdoc := &memberDoc{
Body: doc,
Invite: false, Invite: false,
InviteExpire: 0, InviteExpire: 0,
Body: doc,
} }
if _, err := gd.rh.JSONSet(gd.strid(), "$.members."+gd.tid(mid), memdoc, SetOptionXX); err != nil { if _, err := gd.rh.JSONSet(gd.strid(), "$._members."+gd.tid(mid), memdoc, SetOptionXX); err != nil {
return nil, err return nil, err
} }
@ -141,7 +155,7 @@ func (gd *groupDoc) addMember(mid accountID, doc bson.M) (*memberDoc, error) {
} }
func (gd *groupDoc) removeMember(mid accountID) error { func (gd *groupDoc) removeMember(mid accountID) error {
_, err := gd.rh.JSONDel(gd.strid(), "$.members."+gd.tid(mid)) _, err := gd.rh.JSONDel(gd.strid(), "$._members."+gd.tid(mid))
return err return err
} }
@ -265,6 +279,15 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i
} }
// 내가 wshandler room에 입장 // 내가 wshandler room에 입장
gm.sendEnterRoomMessage(gid, mid) gm.sendEnterRoomMessage(gid, mid)
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "@" + mid.Hex(),
Body: groupDocWithId{
groupDoc: gd,
Gid: gd.strid(),
},
Tag: []string{"GroupDocFull"},
})
} }
newdoc, err := gd.addInvite(inviteeDoc, time.Duration(gm.InviteExpire+1)*time.Second, gm.MaxMember) newdoc, err := gd.addInvite(inviteeDoc, time.Duration(gm.InviteExpire+1)*time.Second, gm.MaxMember)
@ -273,10 +296,11 @@ func (gm *groupInMemory) Invite(gid groupID, mid accountID, inviterDoc bson.M, i
} }
// 초대 중 표시 // 초대 중 표시
_, err = gm.rh.SetNX(gm.rh.ctx, targetid.Hex(), mid.Hex(), time.Duration(gm.InviteExpire)*time.Second).Result() success, err := gm.rh.SetNX(gm.rh.ctx, targetid.Hex(), mid.Hex(), time.Duration(gm.InviteExpire)*time.Second).Result()
if err != nil { if err != nil {
return "", err return "", err
} }
logger.Println("invitation key :", targetid.Hex(), success)
// invitee에게 알림 // invitee에게 알림
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
@ -300,6 +324,8 @@ func (gm *groupInMemory) CancelInvitation(gid groupID, tid ticketID) error {
var errInvitationExpired = errors.New("invitation is already expired") var errInvitationExpired = errors.New("invitation is already expired")
func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bson.M) error { func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bson.M) error {
logger.Println("accept invitation key :", mid.Hex())
cnt, err := gm.rh.Del(gm.rh.ctx, mid.Hex()).Result() cnt, err := gm.rh.Del(gm.rh.ctx, mid.Hex()).Result()
if err != nil { if err != nil {
return err return err
@ -314,9 +340,39 @@ func (gm *groupInMemory) AcceptInvitation(gid groupID, mid accountID, member bso
rh: gm.rh, rh: gm.rh,
} }
_, err = gd.addMember(mid, member) memberDoc, err := gd.addMember(mid, member)
if err == nil { if err == nil {
// 기존 멤버에게 새 멤버를 알림
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + gid.Hex(),
Body: map[string]any{
gd.tid(mid): memberDoc,
},
Tag: []string{"MemberDocFull"},
})
gm.sendEnterRoomMessage(gid, mid) gm.sendEnterRoomMessage(gid, mid)
// 새 멤버에 그룹 전체를 알림
full, err := gm.rh.JSONGet(gd.strid(), "$")
if err != nil {
return err
}
logger.Println(full)
var temp []groupDoc
err = json.Unmarshal([]byte(full.(string)), &temp)
if err != nil {
return err
}
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "@" + mid.Hex(),
Body: groupDocWithId{
groupDoc: &temp[0],
Gid: gd.strid(),
},
Tag: []string{"GroupDocFull"},
})
} }
// 실패 // 실패
@ -353,11 +409,11 @@ func (gm *groupInMemory) Leave(gid groupID, mid accountID) error {
return err return err
} }
// 나한테는 빈 FullGroupDoc을 보낸다. // 나한테는 빈 GroupDocFull을 보낸다. 그러면 지워짐
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{ gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "@" + mid.Hex(), Target: "@" + mid.Hex(),
Body: bson.M{"gid": gid}, Body: bson.M{"gid": gid},
Tag: []string{"FullGroupDoc", gid.Hex()}, Tag: []string{"GroupDocFull", gid.Hex()},
}) })
gm.sendLeaveRoomMessage(gid, mid) gm.sendLeaveRoomMessage(gid, mid)
@ -369,7 +425,8 @@ func (gm *groupInMemory) UpdateMemberDocument(gid groupID, mid accountID, doc bs
id: gid, id: gid,
rh: gm.rh, rh: gm.rh,
} }
_, err := gm.rh.JSONSet(gd.strid(), "$.members."+gd.tid(mid)+".body", doc, SetOptionXX) prefixPath := fmt.Sprintf("$._members.%s.", gd.tid(mid))
err := gm.rh.JSONMSetRel(gd.strid(), prefixPath, doc)
if err != nil { if err != nil {
return err return err
} }
@ -379,7 +436,7 @@ func (gm *groupInMemory) UpdateMemberDocument(gid groupID, mid accountID, doc bs
Body: map[string]any{ Body: map[string]any{
gd.tid(mid): doc, gd.tid(mid): doc,
}, },
Tag: []string{"GroupDocBody"}, Tag: []string{"MemberDocFragment"},
}) })
return nil return nil
@ -389,13 +446,24 @@ func (gm *groupInMemory) Dismiss(gid groupID) error {
return nil return nil
} }
func (gm *groupInMemory) UpdateGroupDocument(gid groupID, body []byte) error { func (gm *groupInMemory) UpdateGroupDocument(gid groupID, frag bson.M) error {
gd := groupDoc{ gd := groupDoc{
id: gid, id: gid,
rh: gm.rh, rh: gm.rh,
} }
_, err := gm.rh.JSONSet(gd.strid(), "$.members.body", body, SetOptionXX)
return err if err := gm.rh.JSONMSetRel(gd.strid(), "$.", frag); err != nil {
return err
}
// 업데이트 알림
gm.sendUpstreamMessage(&wshandler.UpstreamMessage{
Target: "#" + gid.Hex(),
Body: frag,
Tag: []string{"GroupDocFragment"},
})
return nil
} }
func (cfg *groupConfig) prepareInMemory(ctx context.Context, typename string, sub *subTavern) (group, error) { func (cfg *groupConfig) prepareInMemory(ctx context.Context, typename string, sub *subTavern) (group, error) {

View File

@ -58,14 +58,18 @@ func appendArgs[T any](args []any, ext ...T) []any {
return args return args
} }
func (rh *RedisonHandler) JSONMSet(key string, kv map[string]any) error { func (rh *RedisonHandler) JSONMSetRel(key string, prefixPath string, kv map[string]any) error {
if len(prefixPath) > 0 && !strings.HasSuffix(prefixPath, ".") {
prefixPath += "."
}
pl := rh.Pipeline() pl := rh.Pipeline()
for path, obj := range kv { for path, obj := range kv {
b, err := json.Marshal(obj) b, err := json.Marshal(obj)
if err != nil { if err != nil {
return err return err
} }
pl.Do(rh.ctx, "JSON.SET", key, path, b) pl.Do(rh.ctx, "JSON.SET", key, prefixPath+path, b)
} }
cmders, err := pl.Exec(rh.ctx) cmders, err := pl.Exec(rh.ctx)
@ -81,6 +85,10 @@ func (rh *RedisonHandler) JSONMSet(key string, kv map[string]any) error {
return nil return nil
} }
func (rh *RedisonHandler) JSONMSet(key string, kv map[string]any) error {
return rh.JSONMSetRel(key, "", kv)
}
func (rh *RedisonHandler) JSONSet(key, path string, obj any, opts ...SetOption) (bool, error) { func (rh *RedisonHandler) JSONSet(key, path string, obj any, opts ...SetOption) (bool, error) {
b, err := json.Marshal(obj) b, err := json.Marshal(obj)
if err != nil { if err != nil {

View File

@ -228,6 +228,14 @@ func (sub *subTavern) clientMessageReceived(sender *wshandler.Sender, messageTyp
if group := sub.groups[typename]; group != nil { if group := sub.groups[typename]; group != nil {
group.UpdateMemberDocument(gidobj, sender.Accid, doc) group.UpdateMemberDocument(gidobj, sender.Accid, doc)
} }
case "UpdateGroupDocument":
typename := args[0].(string)
gidobj, _ := primitive.ObjectIDFromHex(args[1].(string))
doc := args[2].(map[string]any)
if group := sub.groups[typename]; group != nil {
group.UpdateGroupDocument(gidobj, doc)
}
} }
} }
} }

2
go.mod
View File

@ -5,7 +5,7 @@ go 1.20
require ( require (
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5
go.mongodb.org/mongo-driver v1.11.7 go.mongodb.org/mongo-driver v1.11.7
repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732 repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e
) )
require ( require (

2
go.sum
View File

@ -110,3 +110,5 @@ repositories.action2quare.com/ayo/gocommon v0.0.0-20230716073702-8f6c87a8aeb8 h1
repositories.action2quare.com/ayo/gocommon v0.0.0-20230716073702-8f6c87a8aeb8/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= repositories.action2quare.com/ayo/gocommon v0.0.0-20230716073702-8f6c87a8aeb8/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732 h1:Aq4E8kn1mN5z4ZpRYo5VFj2KektVNrTTuk0HocYMDCk= repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732 h1:Aq4E8kn1mN5z4ZpRYo5VFj2KektVNrTTuk0HocYMDCk=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw= repositories.action2quare.com/ayo/gocommon v0.0.0-20230716093911-66aea48fb732/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e h1:/eG6tAQzEaN178Aib+/erjHrE/+IjIVLRSmP4gx6D7E=
repositories.action2quare.com/ayo/gocommon v0.0.0-20230717084540-29843802ff0e/go.mod h1:PdpZ16O1czKKxCxn+0AFNaEX/0kssYwC3G8jR0V7ybw=