|
|
|
@@ -92,6 +92,56 @@ func finalizeTenantMessage(id t.TenantID, msg t.Message, recalledBy sql.NullInt6
|
|
|
|
|
return msg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func normalizeGroupMessage(id t.TenantID, msg t.Message, recalledBy sql.NullInt64) t.Message {
|
|
|
|
|
msg.TenantID = id
|
|
|
|
|
msg.From = common.EncodeUidString(msg.From).String()
|
|
|
|
|
if recalledBy.Valid {
|
|
|
|
|
msg.RecalledBy = common.EncodeUidString(strconv.FormatInt(recalledBy.Int64, 10)).String()
|
|
|
|
|
}
|
|
|
|
|
msg.Content = common.FromJSON(msg.Content)
|
|
|
|
|
return msg
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func messageHeadType(head t.KVMap) string {
|
|
|
|
|
if head == nil {
|
|
|
|
|
return "text"
|
|
|
|
|
}
|
|
|
|
|
for _, key := range []string{"x-im-type", "type"} {
|
|
|
|
|
if value, ok := head[key].(string); ok {
|
|
|
|
|
value = strings.TrimSpace(value)
|
|
|
|
|
if value != "" {
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return "text"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func messageHeadJSON(head t.KVMap, key string) []byte {
|
|
|
|
|
if head == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if value, ok := head[key]; ok {
|
|
|
|
|
return common.ToJSON(value)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func messageHeadBool(head t.KVMap, key string) bool {
|
|
|
|
|
if head == nil {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
switch value := head[key].(type) {
|
|
|
|
|
case bool:
|
|
|
|
|
return value
|
|
|
|
|
case string:
|
|
|
|
|
parsed, _ := strconv.ParseBool(value)
|
|
|
|
|
return parsed
|
|
|
|
|
default:
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func stampTenantFile(id t.TenantID, file *t.FileDef) *t.FileDef {
|
|
|
|
|
if file != nil {
|
|
|
|
|
file.TenantID = id
|
|
|
|
@@ -1105,6 +1155,63 @@ func tenantMessageDeleteList(tx *sqlx.Tx, tenantID t.TenantID, topic string, toD
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func groupMessageDeleteList(tx *sqlx.Tx, topic string, toDel *t.DelMessage) error {
|
|
|
|
|
var err error
|
|
|
|
|
if toDel == nil {
|
|
|
|
|
if _, err = tx.Exec("DELETE FROM im_dellog WHERE topic=?", topic); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
_, err = tx.Exec("DELETE FROM im_group_messages WHERE topic=?", topic)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
delRanges := toDel.SeqIdRanges
|
|
|
|
|
if toDel.DeletedFor == "" {
|
|
|
|
|
where := "m.topic=?"
|
|
|
|
|
args := []any{topic}
|
|
|
|
|
if len(delRanges) > 0 {
|
|
|
|
|
rSQL, rArgs := common.RangesToSql(delRanges)
|
|
|
|
|
where += " AND m.seqid " + rSQL
|
|
|
|
|
args = append(args, rArgs...)
|
|
|
|
|
}
|
|
|
|
|
where += " AND m.deleted_at IS NULL"
|
|
|
|
|
if newerThan := toDel.GetNewerThan(); newerThan != nil {
|
|
|
|
|
where += " AND m.created_at>?"
|
|
|
|
|
args = append(args, newerThan)
|
|
|
|
|
}
|
|
|
|
|
var seqIDs []int
|
|
|
|
|
if err = tx.Select(&seqIDs, "SELECT seqid FROM im_group_messages AS m WHERE "+where, args...); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if len(seqIDs) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
sort.Ints(seqIDs)
|
|
|
|
|
delRanges = t.SliceToRanges(seqIDs)
|
|
|
|
|
rSQL, rArgs := common.RangesToSql(delRanges)
|
|
|
|
|
where = "m.topic=? AND m.seqid " + rSQL
|
|
|
|
|
args = append([]any{topic}, rArgs...)
|
|
|
|
|
if _, err = tx.Exec("UPDATE im_group_messages AS m SET m.updated_at=?,m.deleted_at=?,m.status='deleted',m.from_user=0,m.head=NULL,m.content=NULL,m.content_size=0 WHERE "+
|
|
|
|
|
where, append([]any{t.TimeNow(), t.TimeNow()}, args...)...); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
stmt, err := tx.Prepare("INSERT INTO im_dellog(topic,deletedfor,delid,low,hi) VALUES(?,?,?,?,?)")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer stmt.Close()
|
|
|
|
|
forUser := common.DecodeUidString(toDel.DeletedFor)
|
|
|
|
|
for _, rng := range delRanges {
|
|
|
|
|
if rng.Hi == 0 {
|
|
|
|
|
rng.Hi = rng.Low + 1
|
|
|
|
|
}
|
|
|
|
|
if _, err = stmt.Exec(topic, forUser, toDel.DelId, rng.Low, rng.Hi); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantTopicDelete(id t.TenantID, topic string, isChan, hard bool) error {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil || topic == "" {
|
|
|
|
@@ -1138,7 +1245,12 @@ func (a *adapter) TenantTopicDelete(id t.TenantID, topic string, isChan, hard bo
|
|
|
|
|
if _, err = tx.Exec(tx.Rebind(q), args...); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if err = tenantMessageDeleteList(tx, id, topic, nil); err != nil {
|
|
|
|
|
if t.GetTopicCat(topic) == t.TopicCatGrp {
|
|
|
|
|
err = groupMessageDeleteList(tx, topic, nil)
|
|
|
|
|
} else {
|
|
|
|
|
err = tenantMessageDeleteList(tx, id, topic, nil)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if _, err = tx.Exec("DELETE FROM im_topictags WHERE tenant_id=? AND topic=?", id, topic); err != nil {
|
|
|
|
@@ -1652,6 +1764,42 @@ func (a *adapter) TenantMessageSave(id t.TenantID, msg *t.Message) error {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupMessageSave(id t.TenantID, msg *t.Message) error {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil || msg == nil || msg.TenantID != id || msg.Topic == "" || msg.SeqId <= 0 {
|
|
|
|
|
return t.ErrMalformed
|
|
|
|
|
}
|
|
|
|
|
if tenantAdp != a {
|
|
|
|
|
return tenantAdp.TenantGroupMessageSave(id, msg)
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := a.getContext()
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
groupID, err := a.groupIDByTopic(ctx, msg.Topic)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
clientMsgID := sql.NullString{String: msg.ClientMsgID, Valid: msg.ClientMsgID != ""}
|
|
|
|
|
contentJSON := common.ToJSON(msg.Content)
|
|
|
|
|
res, err := a.db.ExecContext(ctx,
|
|
|
|
|
`INSERT INTO im_group_messages
|
|
|
|
|
(topic,group_id,seqid,from_user,client_msg_id,msg_type,status,content,head,mentioned_user_ids,mention_all,
|
|
|
|
|
file_count,content_size,version,created_at,updated_at)
|
|
|
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`,
|
|
|
|
|
msg.Topic, groupID, msg.SeqId, decodeUID(t.ParseUid(msg.From)), clientMsgID, messageHeadType(msg.Head), "normal",
|
|
|
|
|
contentJSON, msg.Head, messageHeadJSON(msg.Head, "mentioned_user_ids"), messageHeadBool(msg.Head, "mention_all"),
|
|
|
|
|
0, len(contentJSON), messageVersion(msg.Version), msg.CreatedAt, msg.UpdatedAt)
|
|
|
|
|
if err == nil {
|
|
|
|
|
lastID, _ := res.LastInsertId()
|
|
|
|
|
msg.SetUid(t.Uid(lastID))
|
|
|
|
|
}
|
|
|
|
|
if isDupe(err) {
|
|
|
|
|
return t.ErrDuplicate
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageGetByClientID(id t.TenantID, from t.Uid, clientMsgID string) (*t.Message, error) {
|
|
|
|
|
if !a.tenantScopedDB {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
@@ -1687,7 +1835,40 @@ func (a *adapter) TenantMessageGetByClientID(id t.TenantID, from t.Uid, clientMs
|
|
|
|
|
return &msg, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageGetOne(id t.TenantID, topic string, seq int) (*t.Message, error) {
|
|
|
|
|
func (a *adapter) TenantGroupMessageGetByClientID(id t.TenantID, topic string, from t.Uid, clientMsgID string) (*t.Message, error) {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if tenantAdp != a {
|
|
|
|
|
return tenantAdp.TenantGroupMessageGetByClientID(id, topic, from, clientMsgID)
|
|
|
|
|
}
|
|
|
|
|
if topic == "" || from.IsZero() || clientMsgID == "" {
|
|
|
|
|
return nil, t.ErrMalformed
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := a.getContext()
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
row := struct {
|
|
|
|
|
t.Message
|
|
|
|
|
RecalledBy sql.NullInt64 `db:"recalled_by"`
|
|
|
|
|
}{}
|
|
|
|
|
err = a.db.QueryRowxContext(ctx,
|
|
|
|
|
"SELECT id,created_at AS createdat,updated_at AS updatedat,deleted_at AS deletedat,0 AS delid,seqid,topic,from_user AS `from`,client_msg_id,version,recalled_at,recalled_by,head,content "+
|
|
|
|
|
"FROM im_group_messages WHERE topic=? AND from_user=? AND client_msg_id=?",
|
|
|
|
|
topic, decodeUID(from), clientMsgID).StructScan(&row)
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
msg := normalizeGroupMessage(id, row.Message, row.RecalledBy)
|
|
|
|
|
return &msg, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageGetOne(id t.TenantID, topic string, seq int64) (*t.Message, error) {
|
|
|
|
|
if !a.tenantScopedDB {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil {
|
|
|
|
@@ -1711,7 +1892,29 @@ func (a *adapter) TenantMessageGetOne(id t.TenantID, topic string, seq int) (*t.
|
|
|
|
|
return msg, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageRecall(id t.TenantID, topic string, seq int, actor t.Uid, expectedVersion int, recalledAt time.Time) (*t.Message, error) {
|
|
|
|
|
func (a *adapter) TenantGroupMessageGetOne(id t.TenantID, topic string, seq int64) (*t.Message, error) {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if tenantAdp != a {
|
|
|
|
|
return tenantAdp.TenantGroupMessageGetOne(id, topic, seq)
|
|
|
|
|
}
|
|
|
|
|
if topic == "" || seq <= 0 {
|
|
|
|
|
return nil, t.ErrMalformed
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := a.getContext()
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
msg, err := tenantGroupMessageGetOne(a.db, ctx, id, topic, seq, "")
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
return nil, t.ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return msg, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageRecall(id t.TenantID, topic string, seq int64, actor t.Uid, expectedVersion int, recalledAt time.Time) (*t.Message, error) {
|
|
|
|
|
if !a.tenantScopedDB {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil {
|
|
|
|
@@ -1763,7 +1966,57 @@ func (a *adapter) TenantMessageRecall(id t.TenantID, topic string, seq int, acto
|
|
|
|
|
return a.TenantMessageGetOne(id, topic, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageEdit(id t.TenantID, topic string, seq int, actor t.Uid, expectedVersion int, head t.KVMap, content any, reason string, editedAt time.Time) (*t.Message, error) {
|
|
|
|
|
func (a *adapter) TenantGroupMessageRecall(id t.TenantID, topic string, seq int64, actor t.Uid, expectedVersion int, recalledAt time.Time) (*t.Message, error) {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if tenantAdp != a {
|
|
|
|
|
return tenantAdp.TenantGroupMessageRecall(id, topic, seq, actor, expectedVersion, recalledAt)
|
|
|
|
|
}
|
|
|
|
|
if topic == "" || seq <= 0 || actor.IsZero() || expectedVersion <= 0 {
|
|
|
|
|
return nil, t.ErrMalformed
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := a.getContextForTx()
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
tx, err := a.db.BeginTxx(ctx, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
if err != nil {
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
msg, err := tenantGroupMessageGetOne(tx, ctx, id, topic, seq, " FOR UPDATE")
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
return nil, t.ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if msg.Version != expectedVersion || msg.RecalledAt != nil || msg.DeletedAt != nil {
|
|
|
|
|
return nil, t.ErrDuplicate
|
|
|
|
|
}
|
|
|
|
|
_, err = tx.ExecContext(ctx,
|
|
|
|
|
"UPDATE im_group_messages SET updated_at=?,version=version+1,status='recalled',recalled_at=?,recalled_by=?,head=NULL,content=NULL WHERE topic=? AND seqid=? AND version=?",
|
|
|
|
|
recalledAt, recalledAt, decodeUID(actor), topic, seq, expectedVersion)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err = insertBusinessEventTx(tx, t.StableEventID("message", topic, seq, expectedVersion+1, "recalled"),
|
|
|
|
|
"topic", topic, actor, "message.recalled", map[string]any{"topic": topic, "seq": seq, "version": expectedVersion + 1}); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err = tx.Commit(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return a.TenantGroupMessageGetOne(id, topic, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageEdit(id t.TenantID, topic string, seq int64, actor t.Uid, expectedVersion int, head t.KVMap, content any, reason string, editedAt time.Time) (*t.Message, error) {
|
|
|
|
|
if !a.tenantScopedDB {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil {
|
|
|
|
@@ -1820,6 +2073,62 @@ func (a *adapter) TenantMessageEdit(id t.TenantID, topic string, seq int, actor
|
|
|
|
|
return a.TenantMessageGetOne(id, topic, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupMessageEdit(id t.TenantID, topic string, seq int64, actor t.Uid, expectedVersion int, head t.KVMap, content any, reason string, editedAt time.Time) (*t.Message, error) {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if tenantAdp != a {
|
|
|
|
|
return tenantAdp.TenantGroupMessageEdit(id, topic, seq, actor, expectedVersion, head, content, reason, editedAt)
|
|
|
|
|
}
|
|
|
|
|
if topic == "" || seq <= 0 || actor.IsZero() || expectedVersion <= 0 {
|
|
|
|
|
return nil, t.ErrMalformed
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := a.getContextForTx()
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
tx, err := a.db.BeginTxx(ctx, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
if err != nil {
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
msg, err := tenantGroupMessageGetOne(tx, ctx, id, topic, seq, " FOR UPDATE")
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
return nil, t.ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if msg.Version != expectedVersion || msg.RecalledAt != nil || msg.DeletedAt != nil {
|
|
|
|
|
return nil, t.ErrDuplicate
|
|
|
|
|
}
|
|
|
|
|
if _, err = tx.ExecContext(ctx,
|
|
|
|
|
"INSERT INTO im_message_revisions(createdat,updatedat,topic,seqid,version,actor,head,content,reason) VALUES(?,?,?,?,?,?,?,?,?)",
|
|
|
|
|
editedAt, editedAt, topic, seq, msg.Version, decodeUID(actor), msg.Head, common.ToJSON(msg.Content), strings.TrimSpace(reason)); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
contentJSON := common.ToJSON(content)
|
|
|
|
|
_, err = tx.ExecContext(ctx,
|
|
|
|
|
"UPDATE im_group_messages SET updated_at=?,version=version+1,msg_type=?,head=?,content=?,content_size=? WHERE topic=? AND seqid=? AND version=?",
|
|
|
|
|
editedAt, messageHeadType(head), head, contentJSON, len(contentJSON), topic, seq, expectedVersion)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err = insertBusinessEventTx(tx, t.StableEventID("message", topic, seq, expectedVersion+1, "edited"),
|
|
|
|
|
"topic", topic, actor, "message.edited", map[string]any{"topic": topic, "seq": seq, "version": expectedVersion + 1}); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if err = tx.Commit(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return a.TenantGroupMessageGetOne(id, topic, seq)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageGetAll(id t.TenantID, topic string, uid t.Uid, opts *t.QueryOpt) ([]t.Message, error) {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil || topic == "" {
|
|
|
|
@@ -1888,6 +2197,69 @@ func (a *adapter) TenantMessageGetAll(id t.TenantID, topic string, uid t.Uid, op
|
|
|
|
|
return msgs, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupMessageGetAll(id t.TenantID, topic string, uid t.Uid, opts *t.QueryOpt) ([]t.Message, error) {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil || topic == "" {
|
|
|
|
|
return nil, t.ErrMalformed
|
|
|
|
|
}
|
|
|
|
|
if tenantAdp != a {
|
|
|
|
|
return tenantAdp.TenantGroupMessageGetAll(id, topic, uid, opts)
|
|
|
|
|
}
|
|
|
|
|
limit := a.maxMessageResults
|
|
|
|
|
args := []any{store.DecodeUid(uid), topic}
|
|
|
|
|
seq := ""
|
|
|
|
|
if opts != nil {
|
|
|
|
|
seq = "AND m.seqid "
|
|
|
|
|
if len(opts.IdRanges) > 0 {
|
|
|
|
|
constr, addArgs := common.RangesToSql(opts.IdRanges)
|
|
|
|
|
seq += constr
|
|
|
|
|
args = append(args, addArgs...)
|
|
|
|
|
} else {
|
|
|
|
|
seq += "BETWEEN ? AND ?"
|
|
|
|
|
if opts.Since > 0 {
|
|
|
|
|
args = append(args, opts.Since)
|
|
|
|
|
} else {
|
|
|
|
|
args = append(args, 0)
|
|
|
|
|
}
|
|
|
|
|
if opts.Before > 1 {
|
|
|
|
|
args = append(args, opts.Before-1)
|
|
|
|
|
} else {
|
|
|
|
|
args = append(args, 1<<31-1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if opts.Limit > 0 && opts.Limit < limit {
|
|
|
|
|
limit = opts.Limit
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
args = append(args, limit)
|
|
|
|
|
ctx, cancel := a.getContext()
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
rows, err := a.db.QueryxContext(ctx,
|
|
|
|
|
"SELECT m.id,m.created_at AS createdat,m.updated_at AS updatedat,m.deleted_at AS deletedat,0 AS delid,m.seqid,m.topic,m.from_user AS `from`,m.client_msg_id,m.version,m.recalled_at,m.recalled_by,m.head,m.content "+
|
|
|
|
|
"FROM im_group_messages AS m LEFT JOIN im_dellog AS d "+
|
|
|
|
|
"ON d.topic=m.topic AND m.seqid BETWEEN d.low AND d.hi-1 AND d.deletedfor=? "+
|
|
|
|
|
"WHERE m.status!='deleted' AND m.topic=? "+seq+" AND d.deletedfor IS NULL ORDER BY m.seqid DESC LIMIT ?",
|
|
|
|
|
args...)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer rows.Close()
|
|
|
|
|
msgs := make([]t.Message, 0, limit)
|
|
|
|
|
for rows.Next() {
|
|
|
|
|
row := struct {
|
|
|
|
|
t.Message
|
|
|
|
|
RecalledBy sql.NullInt64 `db:"recalled_by"`
|
|
|
|
|
}{}
|
|
|
|
|
if err = rows.StructScan(&row); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
msgs = append(msgs, normalizeGroupMessage(id, row.Message, row.RecalledBy))
|
|
|
|
|
}
|
|
|
|
|
return msgs, rows.Err()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageSearch(id t.TenantID, topic string, uid t.Uid, queryText, messageType, cursor string, limit int) ([]t.Message, string, error) {
|
|
|
|
|
if !a.tenantScopedDB {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
@@ -1951,7 +2323,7 @@ func (a *adapter) TenantMessageSearch(id t.TenantID, topic string, uid t.Uid, qu
|
|
|
|
|
msg := finalizeTenantMessage(id, row.Message, row.RecalledBy)
|
|
|
|
|
msg.Head = kvMapFromJSON(msg.Head)
|
|
|
|
|
if len(msgs) == limit {
|
|
|
|
|
nextCursor = strconv.Itoa(msg.SeqId)
|
|
|
|
|
nextCursor = strconv.FormatInt(msg.SeqId, 10)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
msgs = append(msgs, msg)
|
|
|
|
@@ -2001,7 +2373,7 @@ func (a *adapter) TenantCallCreate(id t.TenantID, record *t.CallRecord) error {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantCallAccept(id t.TenantID, callID string, seq int, acceptedAt time.Time) error {
|
|
|
|
|
func (a *adapter) TenantCallAccept(id t.TenantID, callID string, seq int64, acceptedAt time.Time) error {
|
|
|
|
|
if !a.tenantScopedDB {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil {
|
|
|
|
@@ -2026,7 +2398,7 @@ func (a *adapter) TenantCallAccept(id t.TenantID, callID string, seq int, accept
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantCallFinish(id t.TenantID, callID string, seq int, status string, durationMS int64, finishedAt time.Time) error {
|
|
|
|
|
func (a *adapter) TenantCallFinish(id t.TenantID, callID string, seq int64, status string, durationMS int64, finishedAt time.Time) error {
|
|
|
|
|
if !a.tenantScopedDB {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil {
|
|
|
|
@@ -2102,7 +2474,7 @@ func (a *adapter) TenantCallList(id t.TenantID, uid t.Uid, topic, cursor string,
|
|
|
|
|
ID int64
|
|
|
|
|
CallID string `db:"call_id"`
|
|
|
|
|
Topic string
|
|
|
|
|
SeqID int `db:"seq_id"`
|
|
|
|
|
SeqID int64 `db:"seq_id"`
|
|
|
|
|
CallerID int64 `db:"caller_id"`
|
|
|
|
|
CalleeID int64 `db:"callee_id"`
|
|
|
|
|
CallType string `db:"call_type"`
|
|
|
|
@@ -4509,7 +4881,7 @@ func (a *adapter) abuseReportByIdempotency(ctx context.Context, uid t.Uid, idemp
|
|
|
|
|
TargetType string `db:"target_type"`
|
|
|
|
|
TargetUserID int64 `db:"target_user_id"`
|
|
|
|
|
TargetTopic string `db:"target_topic"`
|
|
|
|
|
TargetSeq int `db:"target_seq"`
|
|
|
|
|
TargetSeq int64 `db:"target_seq"`
|
|
|
|
|
ReasonCode string `db:"reason_code"`
|
|
|
|
|
Description string `db:"description"`
|
|
|
|
|
EvidenceFileIDs []byte `db:"evidence_file_ids"`
|
|
|
|
@@ -4699,7 +5071,7 @@ func isAllowedWorkbenchRoute(route string) bool {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func tenantMessageGetOne(q sqlx.QueryerContext, ctx context.Context, tenantID t.TenantID, topic string, seq int, suffix string) (*t.Message, error) {
|
|
|
|
|
func tenantMessageGetOne(q sqlx.QueryerContext, ctx context.Context, tenantID t.TenantID, topic string, seq int64, suffix string) (*t.Message, error) {
|
|
|
|
|
row := struct {
|
|
|
|
|
t.Message
|
|
|
|
|
RecalledBy sql.NullInt64 `db:"recalled_by"`
|
|
|
|
@@ -4721,6 +5093,31 @@ func tenantMessageGetOne(q sqlx.QueryerContext, ctx context.Context, tenantID t.
|
|
|
|
|
return &msg, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) groupIDByTopic(ctx context.Context, topic string) (int64, error) {
|
|
|
|
|
var groupID int64
|
|
|
|
|
err := a.db.QueryRowContext(ctx, "SELECT id FROM im_group_settings WHERE topic=? LIMIT 1", topic).Scan(&groupID)
|
|
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
|
return 0, t.ErrNotFound
|
|
|
|
|
}
|
|
|
|
|
return groupID, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func tenantGroupMessageGetOne(q sqlx.QueryerContext, ctx context.Context, tenantID t.TenantID, topic string, seq int64, suffix string) (*t.Message, error) {
|
|
|
|
|
row := struct {
|
|
|
|
|
t.Message
|
|
|
|
|
RecalledBy sql.NullInt64 `db:"recalled_by"`
|
|
|
|
|
}{}
|
|
|
|
|
err := sqlx.GetContext(ctx, q, &row,
|
|
|
|
|
"SELECT id,created_at AS createdat,updated_at AS updatedat,deleted_at AS deletedat,0 AS delid,seqid,topic,from_user AS `from`,client_msg_id,version,recalled_at,recalled_by,head,content "+
|
|
|
|
|
"FROM im_group_messages WHERE topic=? AND seqid=?"+suffix,
|
|
|
|
|
topic, seq)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
msg := normalizeGroupMessage(tenantID, row.Message, row.RecalledBy)
|
|
|
|
|
return &msg, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func messageVersion(version int) int {
|
|
|
|
|
if version <= 0 {
|
|
|
|
|
return 1
|
|
|
|
@@ -5174,6 +5571,33 @@ func (a *adapter) TenantMessageDeleteList(id t.TenantID, topic string, del *t.De
|
|
|
|
|
return tx.Commit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantGroupMessageDeleteList(id t.TenantID, topic string, del *t.DelMessage) error {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil || topic == "" || (del != nil && del.TenantID != id) {
|
|
|
|
|
return t.ErrMalformed
|
|
|
|
|
}
|
|
|
|
|
if tenantAdp != a {
|
|
|
|
|
return tenantAdp.TenantGroupMessageDeleteList(id, topic, del)
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := a.getContextForTx()
|
|
|
|
|
if cancel != nil {
|
|
|
|
|
defer cancel()
|
|
|
|
|
}
|
|
|
|
|
tx, err := a.db.BeginTxx(ctx, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
defer func() {
|
|
|
|
|
if err != nil {
|
|
|
|
|
tx.Rollback()
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
if err = groupMessageDeleteList(tx, topic, del); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return tx.Commit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *adapter) TenantMessageGetDeleted(id t.TenantID, topic string, uid t.Uid, opts *t.QueryOpt) ([]t.DelMessage, error) {
|
|
|
|
|
tenantAdp, err := a.routedTenantAdapter(id)
|
|
|
|
|
if err != nil || topic == "" {
|
|
|
|
|