feat: implement first batch im enhancements

This commit is contained in:
richjohnrambo
2026-08-21 11:42:45 +08:00
parent 14abe58b88
commit c89c466387
16 changed files with 2629 additions and 130 deletions
+199
View File
@@ -1045,6 +1045,114 @@ func (messagesMapper) Edit(tenantID types.TenantID, topic string, seq int, actor
return tenantAdp.TenantMessageEdit(tenantID, topic, seq, actor, expectedVersion, head, content, reason, editedAt)
}
type ConversationsPersistenceInterface interface {
EnsureP2P(tenantID types.TenantID, uid, peer types.Uid) (string, error)
GetSettings(tenantID types.TenantID, uid types.Uid, topic string) (*types.ConversationSettings, error)
UpdateSettings(tenantID types.TenantID, uid types.Uid, topic string, patch types.ConversationSettingsPatch) (*types.ConversationSettings, error)
GetDraft(tenantID types.TenantID, uid types.Uid, topic string) (*types.TopicDraft, error)
PutDraft(tenantID types.TenantID, uid types.Uid, topic, content string, entities []byte, version int) (*types.TopicDraft, error)
}
type conversationsMapper struct{}
var Conversations ConversationsPersistenceInterface
func (conversationsMapper) EnsureP2P(tenantID types.TenantID, uid, peer types.Uid) (string, error) {
if uid.IsZero() || peer.IsZero() || uid == peer {
return "", types.ErrMalformed
}
canSend, _, err := Relations.CanSendP2P(tenantID, uid, peer)
if err != nil {
return "", err
}
if !canSend {
return "", types.ErrPermissionDenied
}
topic := uid.P2PName(peer)
if topic == "" {
return "", types.ErrMalformed
}
now := types.TimeNow()
initiator := &types.Subscription{
ObjHeader: types.ObjHeader{CreatedAt: now, UpdatedAt: now},
TenantID: tenantID,
User: uid.String(),
Topic: topic,
ModeWant: types.ModeCP2P,
ModeGiven: types.ModeCP2P,
Private: map[string]any{"peer": peer.UserId(), "created_by": "http"},
}
invited := &types.Subscription{
ObjHeader: types.ObjHeader{CreatedAt: now, UpdatedAt: now},
TenantID: tenantID,
User: peer.String(),
Topic: topic,
ModeWant: types.ModeCP2P,
ModeGiven: types.ModeCP2P,
Private: map[string]any{"peer": uid.UserId(), "created_by": "http"},
}
if err = Topics.CreateP2P(tenantID, initiator, invited); err != nil {
return "", err
}
return topic, nil
}
func (conversationsMapper) GetSettings(tenantID types.TenantID, uid types.Uid, topic string) (*types.ConversationSettings, error) {
if uid.IsZero() || strings.TrimSpace(topic) == "" {
return nil, types.ErrMalformed
}
if _, err := Subs.Get(tenantID, strings.TrimSpace(topic), uid, false); err != nil {
return nil, err
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantConversationSettingsGet(tenantID, uid, strings.TrimSpace(topic))
}
func (conversationsMapper) UpdateSettings(tenantID types.TenantID, uid types.Uid, topic string, patch types.ConversationSettingsPatch) (*types.ConversationSettings, error) {
if uid.IsZero() || strings.TrimSpace(topic) == "" || patch.Version <= 0 {
return nil, types.ErrMalformed
}
if _, err := Subs.Get(tenantID, strings.TrimSpace(topic), uid, false); err != nil {
return nil, err
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantConversationSettingsUpdate(tenantID, uid, strings.TrimSpace(topic), patch)
}
func (conversationsMapper) GetDraft(tenantID types.TenantID, uid types.Uid, topic string) (*types.TopicDraft, error) {
if uid.IsZero() || strings.TrimSpace(topic) == "" {
return nil, types.ErrMalformed
}
if _, err := Subs.Get(tenantID, strings.TrimSpace(topic), uid, false); err != nil {
return nil, err
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantTopicDraftGet(tenantID, uid, strings.TrimSpace(topic))
}
func (conversationsMapper) PutDraft(tenantID types.TenantID, uid types.Uid, topic, content string, entities []byte, version int) (*types.TopicDraft, error) {
if uid.IsZero() || strings.TrimSpace(topic) == "" || version < 0 {
return nil, types.ErrMalformed
}
if _, err := Subs.Get(tenantID, strings.TrimSpace(topic), uid, false); err != nil {
return nil, err
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantTopicDraftPut(tenantID, uid, strings.TrimSpace(topic), content, entities, version)
}
type CallsPersistenceInterface interface {
Create(tenantID types.TenantID, record *types.CallRecord) error
Accept(tenantID types.TenantID, callID string, seq int, acceptedAt time.Time) error
@@ -1120,6 +1228,7 @@ type GroupBusinessPersistenceInterface interface {
ListMembers(types.TenantID, string, types.GroupMemberListOptions) ([]types.GroupMember, string, error)
UpdateMember(types.TenantID, string, types.Uid, types.GroupMemberPatch) (*types.GroupMember, error)
CreateInvitation(types.TenantID, *types.GroupInvitation) (*types.GroupInvitation, error)
AcceptInvitation(types.TenantID, string, types.Uid, any, time.Time) (*types.GroupInvitation, error)
CreateJoinRequest(types.TenantID, *types.GroupJoinRequest) (*types.GroupJoinRequest, error)
ListJoinRequests(types.TenantID, string, string, string, int) ([]types.GroupJoinRequest, string, error)
ResolveJoinRequest(types.TenantID, string, string, types.Uid, string, string, int, any, time.Time) (*types.GroupJoinRequest, error)
@@ -1129,6 +1238,9 @@ type GroupBusinessPersistenceInterface interface {
ListMutes(types.TenantID, string, string, int) ([]types.GroupMute, string, error)
UpsertMute(types.TenantID, *types.GroupMute) (*types.GroupMute, error)
DeleteMute(types.TenantID, string, types.Uid, types.Uid, time.Time) error
ListFiles(types.TenantID, string, string, string, string, int) ([]types.GroupFile, string, error)
CreateFile(types.TenantID, *types.GroupFile) (*types.GroupFile, error)
DeleteFile(types.TenantID, string, string, types.Uid, bool, time.Time) (*types.GroupFile, error)
InviteMember(types.TenantID, string, types.Uid, types.Uid, string, string, any, time.Time) (bool, error)
Leave(types.TenantID, string, types.Uid, time.Time) error
UpdateRole(types.TenantID, string, types.Uid, types.Uid, string, types.AccessMode, time.Time) error
@@ -1248,6 +1360,17 @@ func (groupBusinessMapper) CreateInvitation(tenantID types.TenantID, invitation
return tenantAdp.TenantGroupInvitationCreate(tenantID, invitation)
}
func (groupBusinessMapper) AcceptInvitation(tenantID types.TenantID, invitationID string, uid types.Uid, memberPrivate any, now time.Time) (*types.GroupInvitation, error) {
if strings.TrimSpace(invitationID) == "" || uid.IsZero() || now.IsZero() {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantGroupInvitationAccept(tenantID, strings.TrimSpace(invitationID), uid, memberPrivate, now)
}
func (groupBusinessMapper) CreateJoinRequest(tenantID types.TenantID, req *types.GroupJoinRequest) (*types.GroupJoinRequest, error) {
if req == nil || req.Topic == "" || req.ApplicantUserID.IsZero() || req.RequestID == "" {
return nil, types.ErrMalformed
@@ -1353,6 +1476,42 @@ func (groupBusinessMapper) DeleteMute(tenantID types.TenantID, topic string, uid
return tenantAdp.TenantGroupMuteDelete(tenantID, strings.TrimSpace(topic), uid, actor, now)
}
func (groupBusinessMapper) ListFiles(tenantID types.TenantID, topic, fileType, query, cursor string, limit int) ([]types.GroupFile, string, error) {
if strings.TrimSpace(topic) == "" {
return nil, "", types.ErrMalformed
}
if limit <= 0 || limit > 100 {
limit = 50
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, "", err
}
return tenantAdp.TenantGroupFileList(tenantID, strings.TrimSpace(topic), strings.TrimSpace(fileType), strings.TrimSpace(query), strings.TrimSpace(cursor), limit)
}
func (groupBusinessMapper) CreateFile(tenantID types.TenantID, file *types.GroupFile) (*types.GroupFile, error) {
if file == nil || strings.TrimSpace(file.Topic) == "" || strings.TrimSpace(file.FileID) == "" || file.UploaderID.IsZero() {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantGroupFileCreate(tenantID, file)
}
func (groupBusinessMapper) DeleteFile(tenantID types.TenantID, topic, groupFileID string, actor types.Uid, canManage bool, now time.Time) (*types.GroupFile, error) {
if strings.TrimSpace(topic) == "" || strings.TrimSpace(groupFileID) == "" || actor.IsZero() || now.IsZero() {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantGroupFileDelete(tenantID, strings.TrimSpace(topic), strings.TrimSpace(groupFileID), actor, canManage, now)
}
func (groupBusinessMapper) InviteMember(tenantID types.TenantID, topic string, inviter, target types.Uid,
message, idempotencyKey string, private any, now time.Time) (bool, error) {
if strings.TrimSpace(topic) == "" || inviter.IsZero() || target.IsZero() || now.IsZero() {
@@ -1456,6 +1615,7 @@ func (workbenchMapper) Get(tenantID types.TenantID, uid types.Uid) ([]types.Work
type OrganizationPersistenceInterface interface {
ListDepartments(tenantID types.TenantID, uid types.Uid, parentID, cursor string, limit int) ([]types.OrganizationDepartment, string, error)
ListMembers(tenantID types.TenantID, uid types.Uid, departmentID, cursor string, limit int) ([]types.OrganizationMember, string, error)
ListAllMembers(tenantID types.TenantID, uid types.Uid, departmentID, queryText, cursor string, limit int) ([]types.OrganizationMember, string, error)
Search(tenantID types.TenantID, uid types.Uid, queryText, resultTypes, cursor string, limit int) ([]types.OrganizationSearchItem, string, error)
}
@@ -1491,6 +1651,20 @@ func (organizationMapper) ListMembers(tenantID types.TenantID, uid types.Uid, de
return tenantAdp.TenantOrganizationMembers(tenantID, uid, strings.TrimSpace(departmentID), strings.TrimSpace(cursor), limit)
}
func (organizationMapper) ListAllMembers(tenantID types.TenantID, uid types.Uid, departmentID, queryText, cursor string, limit int) ([]types.OrganizationMember, string, error) {
if uid.IsZero() {
return nil, "", types.ErrMalformed
}
if limit <= 0 || limit > 100 {
limit = 50
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, "", err
}
return tenantAdp.TenantOrganizationMembersAll(tenantID, uid, strings.TrimSpace(departmentID), strings.TrimSpace(queryText), strings.TrimSpace(cursor), limit)
}
func (organizationMapper) Search(tenantID types.TenantID, uid types.Uid, queryText, resultTypes, cursor string, limit int) ([]types.OrganizationSearchItem, string, error) {
if uid.IsZero() || strings.TrimSpace(queryText) == "" {
return nil, "", types.ErrMalformed
@@ -1714,6 +1888,7 @@ type MonthlyReportsPersistenceInterface interface {
Get(tenantID types.TenantID, uid types.Uid, reportID string) (*types.MonthlyReport, error)
SaveDraft(tenantID types.TenantID, uid types.Uid, reportID, workSummary, nextPlan string, attachments []string, version int) (*types.MonthlyReport, error)
Submit(tenantID types.TenantID, uid types.Uid, reportID string, version int) (*types.MonthlyReport, error)
Reopen(tenantID types.TenantID, uid types.Uid, reportID string, version int) (*types.MonthlyReport, error)
Stats(tenantID types.TenantID, uid types.Uid, month, departmentID string) (*types.MonthlyReportStats, error)
}
@@ -1779,6 +1954,17 @@ func (monthlyReportsMapper) Submit(tenantID types.TenantID, uid types.Uid, repor
return tenantAdp.TenantMonthlyReportSubmit(tenantID, uid, strings.TrimSpace(reportID), version)
}
func (monthlyReportsMapper) Reopen(tenantID types.TenantID, uid types.Uid, reportID string, version int) (*types.MonthlyReport, error) {
if uid.IsZero() || strings.TrimSpace(reportID) == "" || version <= 0 {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantMonthlyReportReopen(tenantID, uid, strings.TrimSpace(reportID), version)
}
func (monthlyReportsMapper) Stats(tenantID types.TenantID, uid types.Uid, month, departmentID string) (*types.MonthlyReportStats, error) {
if uid.IsZero() || strings.TrimSpace(month) == "" {
return nil, types.ErrMalformed
@@ -3118,6 +3304,7 @@ type UserSecurityPersistenceInterface interface {
BumpAuthVersion(tenantID types.TenantID, uid types.Uid, updatedAt time.Time) (int, error)
GetRealName(tenantID types.TenantID, uid types.Uid) (*types.RealNameVerification, error)
CreateRealNameSubmission(tenantID types.TenantID, rv *types.RealNameVerification) error
CreateRealNameAppeal(tenantID types.TenantID, appeal *types.RealNameAppeal) (*types.RealNameAppeal, error)
CreateActionToken(tenantID types.TenantID, token *types.ActionToken) error
ConsumeActionToken(tenantID types.TenantID, uid types.Uid, token, action, target string, now time.Time) (*types.ActionToken, error)
GetIndependentPassword(tenantID types.TenantID, uid types.Uid, purpose string) (*types.IndependentPassword, error)
@@ -3171,6 +3358,17 @@ func (userSecurityMapper) CreateRealNameSubmission(tenantID types.TenantID, rv *
return tenantAdp.TenantRealNameCreate(tenantID, rv)
}
func (userSecurityMapper) CreateRealNameAppeal(tenantID types.TenantID, appeal *types.RealNameAppeal) (*types.RealNameAppeal, error) {
if appeal == nil || appeal.TenantID != tenantID || appeal.UserID.IsZero() || strings.TrimSpace(appeal.AppealID) == "" {
return nil, types.ErrMalformed
}
tenantAdp, err := tenantBusinessAdapter(tenantID)
if err != nil {
return nil, err
}
return tenantAdp.TenantRealNameAppealCreate(tenantID, appeal)
}
func (userSecurityMapper) CreateActionToken(tenantID types.TenantID, token *types.ActionToken) error {
if token == nil || token.TenantID != tenantID {
return types.ErrMalformed
@@ -3481,6 +3679,7 @@ func init() {
Topics = topicsMapper{}
Subs = subsMapper{}
Messages = messagesMapper{}
Conversations = conversationsMapper{}
Calls = callsMapper{}
GroupBusiness = groupBusinessMapper{}
Workbench = workbenchMapper{}