package user import ( "context" "encoding/json" logmodel "github.com/perfect-panel/server/internal/model/log" ordermodel "github.com/perfect-panel/server/internal/model/order" "github.com/perfect-panel/server/internal/model/user" "github.com/perfect-panel/server/internal/svc" "github.com/perfect-panel/server/internal/types" "github.com/perfect-panel/server/pkg/constant" "github.com/perfect-panel/server/pkg/hash" "github.com/perfect-panel/server/pkg/logger" "github.com/perfect-panel/server/pkg/xerr" "github.com/pkg/errors" ) const ( inviteRecordRoleInviter = "inviter" inviteRecordRoleInvitee = "invitee" ) type GetInviteRecordsLogic struct { logger.Logger ctx context.Context svcCtx *svc.ServiceContext } type inviteRecordLog struct { Id int64 `gorm:"column:id"` ObjectId int64 `gorm:"column:object_id"` Content string `gorm:"column:content"` CreatedAt int64 `gorm:"column:created_at"` } type inviteGiftContent struct { OrderNo string `json:"order_no"` Amount int64 `json:"amount"` } type parsedInviteRecordLog struct { log inviteRecordLog content inviteGiftContent } type inviteOrderUser struct { OrderNo string `gorm:"column:order_no"` UserId int64 `gorm:"column:user_id"` } // Get invite gift records func NewGetInviteRecordsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetInviteRecordsLogic { return &GetInviteRecordsLogic{ Logger: logger.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetInviteRecordsLogic) GetInviteRecords(req *types.GetInviteRecordsRequest) (resp *types.GetInviteRecordsResponse, err error) { u, ok := l.ctx.Value(constant.CtxKeyUser).(*user.User) if !ok { l.Errorw("[GetInviteRecords] user not found in context") return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidAccess), "Invalid Access") } normalizeInviteRecordsPagination(req) query := l.svcCtx.DB.WithContext(l.ctx). Table("system_logs"). Where("type = ? AND object_id = ?", logmodel.TypeGift.Uint8(), u.Id). Where("JSON_VALID(content) = 1"). Where("JSON_UNQUOTE(JSON_EXTRACT(content, '$.remark')) = ?", "邀请赠送") if req.StartTime > 0 { query = query.Where("created_at >= FROM_UNIXTIME(?)", req.StartTime) } if req.EndTime > 0 { query = query.Where("created_at <= FROM_UNIXTIME(?)", req.EndTime) } var total int64 if err = query.Count(&total).Error; err != nil { l.Errorw("[GetInviteRecords] count logs failed", logger.Field("error", err.Error()), logger.Field("user_id", u.Id)) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "count logs failed: %v", err.Error()) } var logs []inviteRecordLog if err = query. Select("id, object_id, content, CAST(UNIX_TIMESTAMP(created_at) * 1000 AS SIGNED) AS created_at"). Order("created_at DESC, id DESC"). Limit(req.Size). Offset((req.Page - 1) * req.Size). Scan(&logs).Error; err != nil { l.Errorw("[GetInviteRecords] query logs failed", logger.Field("error", err.Error()), logger.Field("user_id", u.Id)) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query logs failed: %v", err.Error()) } parsedLogs, orderNos := l.parseInviteRecordContents(logs) if len(logs) == 0 || len(parsedLogs) == 0 { return &types.GetInviteRecordsResponse{Total: total, List: []types.InviteRecord{}}, nil } orders, err := l.queryInviteRecordOrders(orderNos) if err != nil { l.Errorw("[GetInviteRecords] query orders failed", logger.Field("error", err.Error()), logger.Field("user_id", u.Id)) return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query orders failed: %v", err.Error()) } list := make([]types.InviteRecord, 0, len(parsedLogs)) for _, parsed := range parsedLogs { content := parsed.content logItem := parsed.log record := types.InviteRecord{ Role: inviteRecordRoleInviter, GiftDays: content.Amount, OrderNo: content.OrderNo, CreatedAt: logItem.CreatedAt, } if orderInfo, ok := orders[content.OrderNo]; ok { peerId := orderInfo.UserId if orderInfo.UserId == u.Id { record.Role = inviteRecordRoleInvitee peerId = u.RefererId } if peerId > 0 { record.PeerHash = hash.InvitePeerHash(peerId) } } list = append(list, record) } return &types.GetInviteRecordsResponse{ Total: total, List: list, }, nil } func normalizeInviteRecordsPagination(req *types.GetInviteRecordsRequest) { if req.Page < 1 { req.Page = 1 } if req.Size < 1 { req.Size = 10 } if req.Size > 100 { req.Size = 100 } } func (l *GetInviteRecordsLogic) parseInviteRecordContents(logs []inviteRecordLog) ([]parsedInviteRecordLog, []string) { parsedLogs := make([]parsedInviteRecordLog, 0, len(logs)) orderNos := make([]string, 0, len(logs)) for _, logItem := range logs { var content inviteGiftContent if err := json.Unmarshal([]byte(logItem.Content), &content); err != nil { l.Infow("[GetInviteRecords] parse content failed", logger.Field("error", err.Error()), logger.Field("log_id", logItem.Id)) continue } parsedLogs = append(parsedLogs, parsedInviteRecordLog{log: logItem, content: content}) if content.OrderNo != "" { orderNos = append(orderNos, content.OrderNo) } } return parsedLogs, orderNos } func (l *GetInviteRecordsLogic) queryInviteRecordOrders(orderNos []string) (map[string]inviteOrderUser, error) { orders := make(map[string]inviteOrderUser, len(orderNos)) if len(orderNos) == 0 { return orders, nil } var orderData []inviteOrderUser err := l.svcCtx.DB.WithContext(l.ctx). Model(&ordermodel.Order{}). Select("order_no, user_id"). Where("order_no IN ?", orderNos). Scan(&orderData).Error if err != nil { return nil, err } for _, item := range orderData { orders[item.OrderNo] = item } return orders, nil }