feat: 完善IM后台Kafka与组织架构管理

This commit is contained in:
richjohnrambo
2026-09-03 17:11:29 +08:00
parent 5a2ed4b2a4
commit 25abba8eeb
23 changed files with 652 additions and 142 deletions
+14 -9
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"hotgo/internal/consts"
"hotgo/internal/library/imtenant"
"hotgo/internal/library/queue"
"time"
@@ -60,7 +61,7 @@ func PublishAdminCommand(ctx context.Context, in AdminCommandInput) (res *AdminC
return nil, gerror.Wrap(err, "序列化IM命令Envelope失败")
}
db := g.DB()
db := adminDB()
err = db.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
nowTime := gtime.Now()
if _, err := tx.Exec(`INSERT INTO im_admin_commands
@@ -101,7 +102,7 @@ func PublishAdminCommand(ctx context.Context, in AdminCommandInput) (res *AdminC
}
func DispatchOutboxByEvent(ctx context.Context, eventID string) error {
row, err := g.DB().GetOne(ctx, `SELECT * FROM im_admin_outbox WHERE event_id=? AND status=? LIMIT 1`,
row, err := adminDB().GetOne(ctx, `SELECT * FROM im_admin_outbox WHERE event_id=? AND status=? LIMIT 1`,
eventID, consts.ImAdminOutboxStatusPending)
if err != nil {
return gerror.Wrap(err, "读取IM命令Outbox失败")
@@ -146,7 +147,7 @@ func PublishConfigEvent(ctx context.Context, in ConfigEventInput) error {
return gerror.Wrap(err, "序列化IM配置事件Envelope失败")
}
nowTime := gtime.Now()
_, err = g.DB().Exec(ctx, `INSERT INTO im_admin_outbox
_, err = adminDB().Exec(ctx, `INSERT INTO im_admin_outbox
(event_id,command_id,topic,event_type,biz_type,biz_id,key_value,envelope,status,created_at,updated_at)
VALUES(?,?,?,?,?,?,?,CAST(? AS JSON),?,?,?)`,
eventID, "", consts.QueueImConfigEventTopic, in.EventType, "config", in.BizID, in.TenantCode,
@@ -198,7 +199,7 @@ func PublishEvent(ctx context.Context, in EventInput) (eventID string, err error
return "", gerror.Wrap(err, "序列化IM Kafka事件Envelope失败")
}
nowTime := gtime.Now()
_, err = g.DB().Exec(ctx, `INSERT INTO im_admin_outbox
_, err = adminDB().Exec(ctx, `INSERT INTO im_admin_outbox
(event_id,command_id,topic,event_type,biz_type,biz_id,key_value,envelope,status,created_at,updated_at)
VALUES(?,?,?,?,?,?,?,CAST(? AS JSON),?,?,?)`,
eventID, "", in.Topic, in.EventType, in.BizType, in.BizID, in.BizID,
@@ -216,7 +217,7 @@ func DispatchPendingOutbox(ctx context.Context, limit int) (sent int, err error)
if limit <= 0 {
limit = 100
}
rows, err := g.DB().GetAll(ctx, `SELECT * FROM im_admin_outbox
rows, err := adminDB().GetAll(ctx, `SELECT * FROM im_admin_outbox
WHERE status IN (?,?) AND (next_retry_at IS NULL OR next_retry_at <= NOW())
ORDER BY id ASC LIMIT ?`, consts.ImAdminOutboxStatusPending, consts.ImAdminOutboxStatusFailed, limit)
if err != nil {
@@ -250,14 +251,14 @@ func dispatchOutboxRecord(ctx context.Context, row gdb.Record) error {
return err
}
now := gtime.Now()
_, err := g.DB().Exec(ctx, `UPDATE im_admin_outbox
_, err := adminDB().Exec(ctx, `UPDATE im_admin_outbox
SET status=?,attempts=attempts+1,sent_at=?,last_reason='',last_error='',updated_at=?
WHERE event_id=?`, consts.ImAdminOutboxStatusSent, now, now, eventID)
if err != nil {
return gerror.Wrap(err, "更新IM命令Outbox投递状态失败")
}
if commandID != "" {
_, err = g.DB().Exec(ctx, `UPDATE im_admin_commands
_, err = adminDB().Exec(ctx, `UPDATE im_admin_commands
SET status=?,sent_at=?,updated_at=? WHERE command_id=?`,
consts.ImAdminCommandStatusRunning, now, now, commandID)
if err != nil {
@@ -269,7 +270,7 @@ func dispatchOutboxRecord(ctx context.Context, row gdb.Record) error {
func markOutboxFailed(ctx context.Context, eventID string, reason string, message string) error {
now := gtime.Now()
_, err := g.DB().Exec(ctx, `UPDATE im_admin_outbox
_, err := adminDB().Exec(ctx, `UPDATE im_admin_outbox
SET status=?,attempts=attempts+1,next_retry_at=?,last_reason=?,last_error=?,updated_at=?
WHERE event_id=?`, consts.ImAdminOutboxStatusFailed, now.Add(30*time.Second), reason, message, now, eventID)
if err != nil {
@@ -316,9 +317,13 @@ func ApplyCommandResult(ctx context.Context, result CommandResult) error {
}
sql += ` WHERE ` + whereColumn + `=?`
args = append(args, whereValue)
_, err := g.DB().Exec(ctx, sql, args...)
_, err := adminDB().Exec(ctx, sql, args...)
if err != nil {
return gerror.Wrap(err, "更新IM后台命令结果失败")
}
return nil
}
func adminDB() gdb.DB {
return imtenant.RegistryDB()
}
+5 -4
View File
@@ -2,12 +2,12 @@ package sys
import (
"context"
"hotgo/internal/library/imtenant"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
)
type sSysImKafkaAudit struct{}
@@ -25,7 +25,8 @@ func (s *sSysImKafkaAudit) List(ctx context.Context, in *sysin.ImKafkaAuditListI
return
}
where, args := buildImKafkaAuditWhere(in)
totalValue, err := g.DB().GetValue(ctx, "SELECT COUNT(*) FROM im_audit_events "+where, args...)
db := imtenant.RegistryDB()
totalValue, err := db.GetValue(ctx, "SELECT COUNT(*) FROM im_audit_events "+where, args...)
if err != nil {
err = gerror.Wrap(err, "获取IM Kafka审计事件总数失败,请稍后重试!")
return
@@ -45,7 +46,7 @@ func (s *sSysImKafkaAudit) List(ctx context.Context, in *sysin.ImKafkaAuditListI
action,resource,resource_id,result,reason,occurred_at,received_at
FROM im_audit_events ` + where + ` ORDER BY id DESC LIMIT ? OFFSET ?`
queryArgs := append(append([]any{}, args...), perPage, offset)
rows, err := g.DB().GetAll(ctx, query, queryArgs...)
rows, err := db.GetAll(ctx, query, queryArgs...)
if err != nil {
err = gerror.Wrap(err, "获取IM Kafka审计事件列表失败,请稍后重试!")
return
@@ -126,7 +127,7 @@ func (s *sSysImKafkaAudit) View(ctx context.Context, in *sysin.ImKafkaAuditViewI
args = append(args, in.EventId)
}
query += " LIMIT 1"
row, err := g.DB().GetOne(ctx, query, args...)
row, err := imtenant.RegistryDB().GetOne(ctx, query, args...)
if err != nil {
return nil, gerror.Wrap(err, "获取IM Kafka审计事件详情失败,请稍后重试!")
}
@@ -10,12 +10,12 @@ import (
"hotgo/internal/consts"
"hotgo/internal/library/contexts"
"hotgo/internal/library/imkafka"
"hotgo/internal/library/imtenant"
"hotgo/internal/model/input/sysin"
"hotgo/internal/service"
"strings"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
@@ -34,7 +34,8 @@ func (s *sSysImKafkaCommand) List(ctx context.Context, in *sysin.ImKafkaCommandL
return
}
where, args := buildImKafkaCommandWhere(in)
totalValue, err := g.DB().GetValue(ctx, "SELECT COUNT(*) FROM im_admin_commands "+where, args...)
db := imtenant.RegistryDB()
totalValue, err := db.GetValue(ctx, "SELECT COUNT(*) FROM im_admin_commands "+where, args...)
if err != nil {
err = gerror.Wrap(err, "获取IM后台命令总数失败,请稍后重试!")
return
@@ -53,7 +54,7 @@ func (s *sSysImKafkaCommand) List(ctx context.Context, in *sysin.ImKafkaCommandL
status,reason,message,sent_at,completed_at,created_by,created_at,updated_at
FROM im_admin_commands ` + where + ` ORDER BY id DESC LIMIT ? OFFSET ?`
queryArgs := append(append([]any{}, args...), perPage, offset)
rows, err := g.DB().GetAll(ctx, query, queryArgs...)
rows, err := db.GetAll(ctx, query, queryArgs...)
if err != nil {
err = gerror.Wrap(err, "获取IM后台命令列表失败,请稍后重试!")
return
@@ -80,7 +81,7 @@ func (s *sSysImKafkaCommand) View(ctx context.Context, in *sysin.ImKafkaCommandV
args = append(args, in.CommandId)
}
query += " LIMIT 1"
row, err := g.DB().GetOne(ctx, query, args...)
row, err := imtenant.RegistryDB().GetOne(ctx, query, args...)
if err != nil {
return nil, gerror.Wrap(err, "获取IM后台命令详情失败,请稍后重试!")
}
+24 -34
View File
@@ -9,6 +9,7 @@ import (
"hotgo/internal/service"
"hotgo/utility/convert"
"hotgo/utility/excel"
"net/url"
"strings"
"github.com/gogf/gf/v2/database/gdb"
@@ -34,33 +35,28 @@ func (s *sSysImOrg) DepartmentList(ctx context.Context, in *sysin.ImOrgDepartmen
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return
}
mod := s.departmentModel(ctx, db)
values := addPageQuery(url.Values{}, in.Page, in.PerPage)
if in.DepartmentId != "" {
mod = mod.Where("d.department_id", strings.TrimSpace(in.DepartmentId))
values.Set("department_id", strings.TrimSpace(in.DepartmentId))
}
if in.ParentId != "" {
mod = mod.Where("d.parent_id", strings.TrimSpace(in.ParentId))
values.Set("parent_id", strings.TrimSpace(in.ParentId))
}
if in.Name != "" {
mod = mod.WhereLike("d.name", "%"+strings.TrimSpace(in.Name)+"%")
values.Set("name", strings.TrimSpace(in.Name))
}
if in.State != "" {
mod = mod.Where("d.state", strings.TrimSpace(in.State))
values.Set("state", strings.TrimSpace(in.State))
}
mod = mod.Page(in.Page, in.PerPage).OrderAsc("d.sort_order").OrderAsc("d.id")
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
err = gerror.Wrap(err, "获取IM组织部门失败,请稍后重试!")
return
}
for _, item := range list {
item.TenantId = tenant.Id
item.TenantName = tenant.Name
}
return
return imAdminQueryList[sysin.ImOrgDepartmentModel](ctx, tenant,
"/internal/admin-query/tenants/"+url.PathEscape(tenant.Code)+"/org/departments",
values,
nil,
)
}
func (s *sSysImOrg) DepartmentExport(ctx context.Context, in *sysin.ImOrgDepartmentListInp) (err error) {
@@ -195,34 +191,28 @@ func (s *sSysImOrg) MemberList(ctx context.Context, in *sysin.ImOrgMemberListInp
if err = in.Filter(ctx); err != nil {
return
}
db, tenant, err := resolveImTenantDB(ctx, in.TenantId)
tenant, err := resolveImAdminTenant(ctx, in.TenantId)
if err != nil {
return
}
mod := s.memberModel(ctx, db)
values := addPageQuery(url.Values{}, in.Page, in.PerPage)
if in.DepartmentId != "" {
mod = mod.Where("m.department_id", strings.TrimSpace(in.DepartmentId))
values.Set("department_id", strings.TrimSpace(in.DepartmentId))
}
if in.UserId > 0 {
mod = mod.Where("m.user_id", in.UserId)
values.Set("user_id", int64ToString(in.UserId))
}
if in.EmploymentState != "" {
mod = mod.Where("m.employment_state", strings.TrimSpace(in.EmploymentState))
values.Set("employment_state", strings.TrimSpace(in.EmploymentState))
}
if q := strings.TrimSpace(in.Query); q != "" {
like := "%" + q + "%"
mod = mod.Where("(m.display_name LIKE ? OR m.mobile LIKE ? OR m.email LIKE ? OR CAST(m.user_id AS CHAR) LIKE ?)", like, like, like, like)
if in.Query != "" {
values.Set("q", strings.TrimSpace(in.Query))
}
mod = mod.Page(in.Page, in.PerPage).OrderAsc("m.sort_order").OrderAsc("m.id")
if err = mod.ScanAndCount(&list, &totalCount, false); err != nil {
err = gerror.Wrap(err, "获取IM组织成员失败,请稍后重试!")
return
}
for _, item := range list {
item.TenantId = tenant.Id
item.TenantName = tenant.Name
}
return
return imAdminQueryList[sysin.ImOrgMemberModel](ctx, tenant,
"/internal/admin-query/tenants/"+url.PathEscape(tenant.Code)+"/org/members",
values,
nil,
)
}
func (s *sSysImOrg) MemberExport(ctx context.Context, in *sysin.ImOrgMemberListInp) (err error) {
+2 -2
View File
@@ -5,9 +5,9 @@ import (
"encoding/json"
"hotgo/internal/consts"
"hotgo/internal/library/imkafka"
"hotgo/internal/library/imtenant"
"hotgo/internal/library/queue"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
@@ -32,7 +32,7 @@ func (q *qImAuditEvent) Handle(ctx context.Context, mqMsg queue.MqMsg) (err erro
if len(envelope.Payload) > 0 {
_ = json.Unmarshal(envelope.Payload, &payload)
}
_, err = g.DB().Exec(ctx, `INSERT IGNORE INTO im_audit_events
_, err = imtenant.RegistryDB().Exec(ctx, `INSERT IGNORE INTO im_audit_events
(event_id,tenant_code,event_type,biz_type,biz_id,actor_type,actor_id,action,resource,resource_id,result,reason,payload,envelope,occurred_at,received_at)
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,CAST(NULLIF(?, '') AS JSON),CAST(? AS JSON),?,?)`,
envelope.EventID, envelope.TenantCode, envelope.EventType, envelope.BizType, envelope.BizID,
+2 -2
View File
@@ -5,9 +5,9 @@ import (
"encoding/json"
"hotgo/internal/consts"
"hotgo/internal/library/imkafka"
"hotgo/internal/library/imtenant"
"hotgo/internal/library/queue"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
)
@@ -32,7 +32,7 @@ func (q *qImDeadLetter) Handle(ctx context.Context, mqMsg queue.MqMsg) (err erro
if len(envelope.Payload) > 0 {
_ = json.Unmarshal(envelope.Payload, &payload)
}
_, err = g.DB().Exec(ctx, `INSERT IGNORE INTO im_kafka_dead_letter_events
_, err = imtenant.RegistryDB().Exec(ctx, `INSERT IGNORE INTO im_kafka_dead_letter_events
(event_id,parent_event_id,tenant_code,event_type,biz_type,biz_id,source_service,target_service,dead_id,reason,message,payload,envelope,received_at)
VALUES(?,?,?,?,?,?,?,?,?,?,?,CAST(NULLIF(?, '') AS JSON),CAST(? AS JSON),?)`,
envelope.EventID, envelope.ParentEventID, envelope.TenantCode, envelope.EventType, envelope.BizType, envelope.BizID,