package sys import ( "context" "fmt" "hotgo/internal/library/imadmin" "hotgo/internal/model/input/sysin" "hotgo/internal/service" "io" "strconv" "time" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gtime" ) type sSysImInvite struct{} func NewSysImInvite() *sSysImInvite { return &sSysImInvite{} } func init() { service.RegisterSysImInvite(NewSysImInvite()) } type imInviteListRequest struct { Page int `json:"page,omitempty"` PerPage int `json:"per_page,omitempty"` InviteCode string `json:"invite_code,omitempty"` Status string `json:"status,omitempty"` BatchID int64 `json:"batch_id,omitempty"` CreatedBy int64 `json:"created_by,omitempty"` CreatedAtStart string `json:"created_at_start,omitempty"` CreatedAtEnd string `json:"created_at_end,omitempty"` ExpiresAtStart string `json:"expires_at_start,omitempty"` ExpiresAtEnd string `json:"expires_at_end,omitempty"` } type imInviteCreateRequest struct { ExpiresAt string `json:"expires_at"` Remark string `json:"remark,omitempty"` IdempotencyKey string `json:"idempotency_key,omitempty"` } type imInviteBatchCreateRequest struct { Quantity int `json:"quantity"` ExpiresAt string `json:"expires_at"` Remark string `json:"remark,omitempty"` IdempotencyKey string `json:"idempotency_key,omitempty"` } type imInviteCodeResponse struct { ID int64 `json:"id"` BatchID *int64 `json:"batch_id"` Code string `json:"code"` Status string `json:"status"` ExpiresAt *time.Time `json:"expires_at"` UsedBy *int64 `json:"used_by"` UsedAt *time.Time `json:"used_at"` RequestID string `json:"request_id"` CreatedBy int64 `json:"created_by"` Remark string `json:"remark"` CreatedAt *time.Time `json:"created_at"` } type imInviteBatchResponse struct { BatchID int64 `json:"batch_id"` Quantity int `json:"quantity"` ActiveCount int `json:"active_count"` UsedCount int `json:"used_count"` RevokedCount int `json:"revoked_count"` ExpiredCount int `json:"expired_count"` ExpiresAt *time.Time `json:"expires_at"` CreatedAt *time.Time `json:"created_at"` } func (s *sSysImInvite) List(ctx context.Context, in *sysin.ImInviteListInp) (list []*sysin.ImInviteCodeModel, totalCount int, err error) { if err = in.Filter(ctx); err != nil { return } scope, err := resolveImAdminScope(ctx, in.TenantId) if err != nil { return } req, err := buildInviteListRequest(in) if err != nil { return } var page imadmin.Page[imInviteCodeResponse] if err = imadmin.DefaultClient(ctx).ListCodes(ctx, scope, req, &page); err != nil { _ = writeImAdminAudit(ctx, scope.TenantId, "invite_list", "invite_code", 0, "failed", g.Map{"hasInviteCode": in.InviteCode != ""}) return } _ = writeImAdminAudit(ctx, scope.TenantId, "invite_list", "invite_code", 0, "success", g.Map{"hasInviteCode": in.InviteCode != ""}) list = make([]*sysin.ImInviteCodeModel, 0, len(page.Items)) for i := range page.Items { list = append(list, convertInviteCode(page.Items[i])) } totalCount = page.Total return } func (s *sSysImInvite) Create(ctx context.Context, in *sysin.ImInviteCreateInp) (res *sysin.ImInviteCodeModel, err error) { if err = in.Filter(ctx); err != nil { return } scope, err := resolveImAdminScope(ctx, in.TenantId) if err != nil { return } req := imInviteCreateRequest{ExpiresAt: formatRFC3339(in.ExpiresAt), Remark: in.Remark, IdempotencyKey: in.IdempotencyKey} var upstream imInviteCodeResponse err = imadmin.DefaultClient(ctx).CreateCode(ctx, scope, req, &upstream) res = convertInviteCode(upstream) result := "success" if err != nil { result = "failed" } targetId, _ := strconv.ParseInt(res.Id, 10, 64) _ = writeImAdminAudit(ctx, scope.TenantId, "invite_create", "invite_code", targetId, result, g.Map{"expiresAt": in.ExpiresAt, "remark": in.Remark}) return } func (s *sSysImInvite) BatchCreate(ctx context.Context, in *sysin.ImInviteBatchCreateInp) (res *sysin.ImInviteBatchModel, err error) { if err = in.Filter(ctx); err != nil { return } scope, err := resolveImAdminScope(ctx, in.TenantId) if err != nil { return } req := imInviteBatchCreateRequest{Quantity: in.Quantity, ExpiresAt: formatRFC3339(in.ExpiresAt), Remark: in.Remark, IdempotencyKey: in.IdempotencyKey} var upstream imInviteBatchResponse err = imadmin.DefaultClient(ctx).CreateBatch(ctx, scope, req, &upstream) res = convertInviteBatch(upstream) result := "success" if err != nil { result = "failed" } targetId, _ := strconv.ParseInt(res.BatchId, 10, 64) _ = writeImAdminAudit(ctx, scope.TenantId, "invite_batch_create", "invite_batch", targetId, result, g.Map{"quantity": in.Quantity, "expiresAt": in.ExpiresAt, "remark": in.Remark}) return } func (s *sSysImInvite) BatchView(ctx context.Context, in *sysin.ImInviteBatchViewInp) (res *sysin.ImInviteBatchDetailModel, err error) { scope, err := resolveImAdminScope(ctx, in.TenantId) if err != nil { return } batchId, err := sysin.ParseOptionalInt64(in.BatchId) if err != nil { return } var upstream imInviteBatchResponse err = imadmin.DefaultClient(ctx).GetBatch(ctx, scope, batchId, &upstream) res = &sysin.ImInviteBatchDetailModel{ImInviteBatchModel: *convertInviteBatch(upstream)} return } func (s *sSysImInvite) Revoke(ctx context.Context, in *sysin.ImInviteRevokeInp) (err error) { scope, err := resolveImAdminScope(ctx, in.TenantId) if err != nil { return } id, err := sysin.ParseOptionalInt64(in.Id) if err != nil { return } err = imadmin.DefaultClient(ctx).RevokeCode(ctx, scope, id) result := "success" if err != nil { result = "failed" } _ = writeImAdminAudit(ctx, scope.TenantId, "invite_revoke", "invite_code", id, result, nil) return } func (s *sSysImInvite) BatchRevoke(ctx context.Context, in *sysin.ImInviteBatchRevokeInp) (res *sysin.ImInviteBatchModel, err error) { scope, err := resolveImAdminScope(ctx, in.TenantId) if err != nil { return } batchId, err := sysin.ParseOptionalInt64(in.BatchId) if err != nil { return } var upstream struct { Batch *imInviteBatchResponse `json:"batch"` RevokedCount int `json:"revoked_count"` } err = imadmin.DefaultClient(ctx).RevokeBatch(ctx, scope, batchId, &upstream) if upstream.Batch != nil { res = convertInviteBatch(*upstream.Batch) } else { res = &sysin.ImInviteBatchModel{BatchId: strconv.FormatInt(batchId, 10), RevokedCount: upstream.RevokedCount} } result := "success" if err != nil { result = "failed" } _ = writeImAdminAudit(ctx, scope.TenantId, "invite_batch_revoke", "invite_batch", batchId, result, nil) return } func (s *sSysImInvite) Export(ctx context.Context, in *sysin.ImInviteListInp) (err error) { if err = in.Filter(ctx); err != nil { return } scope, err := resolveImAdminScope(ctx, in.TenantId) if err != nil { return } req, err := buildInviteListRequest(in) if err != nil { return } resp, err := imadmin.DefaultClient(ctx).ExportCodes(ctx, scope, req) if err != nil { _ = writeImAdminAudit(ctx, scope.TenantId, "invite_export", "invite_code", 0, "failed", g.Map{"hasInviteCode": in.InviteCode != ""}) return } defer resp.Body.Close() request := ghttp.RequestFromCtx(ctx) if request == nil { return gerror.New("请求上下文不存在") } filename := resp.Header.Get("Content-Disposition") if filename == "" { filename = `attachment; filename="registration-invite-codes.csv"` } request.Response.Header().Set("Content-Type", resp.Header.Get("Content-Type")) request.Response.Header().Set("Content-Disposition", filename) if _, err = io.Copy(request.Response.Writer, resp.Body); err != nil { return gerror.Wrap(err, "转发导出文件失败") } _ = writeImAdminAudit(ctx, scope.TenantId, "invite_export", "invite_code", 0, "success", g.Map{"hasInviteCode": in.InviteCode != ""}) fmt.Fprint(request.Response.Writer, "") return } func buildInviteListRequest(in *sysin.ImInviteListInp) (imInviteListRequest, error) { batchId, err := sysin.ParseOptionalInt64(in.BatchId) if err != nil { return imInviteListRequest{}, err } createdBy, err := sysin.ParseOptionalInt64(in.CreatedBy) if err != nil { return imInviteListRequest{}, err } return imInviteListRequest{ Page: in.Page, PerPage: in.PerPage, InviteCode: in.InviteCode, Status: in.Status, BatchID: batchId, CreatedBy: createdBy, CreatedAtStart: formatRFC3339(in.CreatedAtStart), CreatedAtEnd: formatRFC3339(in.CreatedAtEnd), ExpiresAtStart: formatRFC3339(in.ExpiresAtStart), ExpiresAtEnd: formatRFC3339(in.ExpiresAtEnd), }, nil } func formatRFC3339(t *gtime.Time) string { if t == nil { return "" } return t.Time.UTC().Format(time.RFC3339) } func convertTime(t *time.Time) *gtime.Time { if t == nil { return nil } return gtime.New(t) } func formatOptionalInt64(value *int64) string { if value == nil || *value == 0 { return "" } return strconv.FormatInt(*value, 10) } func convertInviteCode(item imInviteCodeResponse) *sysin.ImInviteCodeModel { return &sysin.ImInviteCodeModel{ Id: strconv.FormatInt(item.ID, 10), BatchId: formatOptionalInt64(item.BatchID), Code: item.Code, Status: item.Status, ExpiresAt: convertTime(item.ExpiresAt), UsedBy: formatOptionalInt64(item.UsedBy), UsedAt: convertTime(item.UsedAt), RequestId: item.RequestID, CreatedBy: strconv.FormatInt(item.CreatedBy, 10), Remark: item.Remark, CreatedAt: convertTime(item.CreatedAt), } } func convertInviteBatch(item imInviteBatchResponse) *sysin.ImInviteBatchModel { return &sysin.ImInviteBatchModel{ BatchId: strconv.FormatInt(item.BatchID, 10), Quantity: item.Quantity, ActiveCount: item.ActiveCount, UsedCount: item.UsedCount, RevokedCount: item.RevokedCount, ExpiredCount: item.ExpiredCount, ExpiresAt: convertTime(item.ExpiresAt), CreatedAt: convertTime(item.CreatedAt), } }