Expose subscription_status on user profile responses instead of active_subscription.

Users see ACTIVE, PENDING, or Unsubscribed via new batch and single SQL helpers; Swagger refreshed.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Yared Yemane 2026-05-18 00:28:19 -07:00
parent 1e62510321
commit 49bcc22d0d
11 changed files with 179 additions and 281 deletions

View File

@ -61,28 +61,38 @@ FROM user_subscriptions us
JOIN subscription_plans sp ON sp.id = us.plan_id
WHERE us.id = $1;
-- name: ListActiveSubscriptionsByUserIDs :many
-- One ACTIVE, non-expired row per user (latest expires_at wins), same rules as GetActiveSubscriptionByUserID.
SELECT DISTINCT ON (us.user_id)
us.user_id,
us.id,
us.plan_id,
us.starts_at,
us.expires_at,
us.status,
us.auto_renew,
us.payment_method,
sp.name AS plan_name,
sp.duration_value,
sp.duration_unit,
sp.price,
sp.currency
FROM user_subscriptions us
JOIN subscription_plans sp ON sp.id = us.plan_id
WHERE us.user_id = ANY($1::bigint[])
AND us.status = 'ACTIVE'
AND us.expires_at > CURRENT_TIMESTAMP
ORDER BY us.user_id, us.expires_at DESC;
-- Display status for admin user lists: ACTIVE (non-expired), else latest PENDING, else Unsubscribed.
-- name: ListSubscriptionDisplayStatusesByUserIDs :many
WITH input AS (
SELECT unnest($1::bigint[])::bigint AS user_id
)
SELECT
input.user_id,
COALESCE(
(SELECT us.status::text FROM user_subscriptions us
WHERE us.user_id = input.user_id
AND us.status = 'ACTIVE' AND us.expires_at > CURRENT_TIMESTAMP
ORDER BY us.expires_at DESC LIMIT 1),
(SELECT us.status::text FROM user_subscriptions us
WHERE us.user_id = input.user_id
AND us.status = 'PENDING'
ORDER BY us.created_at DESC LIMIT 1),
'Unsubscribed'
)::text AS subscription_status
FROM input;
-- name: GetSubscriptionDisplayStatusByUserID :one
SELECT COALESCE(
(SELECT us.status::text FROM user_subscriptions us
WHERE us.user_id = $1
AND us.status = 'ACTIVE' AND us.expires_at > CURRENT_TIMESTAMP
ORDER BY us.expires_at DESC LIMIT 1),
(SELECT us.status::text FROM user_subscriptions us
WHERE us.user_id = $1
AND us.status = 'PENDING'
ORDER BY us.created_at DESC LIMIT 1),
'Unsubscribed'
)::text AS subscription_status;
-- name: GetActiveSubscriptionByUserID :one
SELECT

View File

@ -8436,7 +8436,7 @@ const docTemplate = `{
},
"/api/v1/users": {
"get": {
"description": "Get users with optional filters. Each user includes active_subscription: an object when they have a current ACTIVE, non-expired plan, otherwise null.",
"description": "Get users with optional filters. Each user includes subscription_status: ACTIVE, PENDING, or Unsubscribed.",
"consumes": [
"application/json"
],
@ -11294,9 +11294,6 @@ const docTemplate = `{
"domain.UserProfileResponse": {
"type": "object",
"properties": {
"active_subscription": {
"$ref": "#/definitions/domain.UserSubscriptionSummary"
},
"age_group": {
"type": "string"
},
@ -11384,6 +11381,9 @@ const docTemplate = `{
"status": {
"$ref": "#/definitions/domain.UserStatus"
},
"subscription_status": {
"type": "string"
},
"updated_at": {
"type": "string"
}
@ -11404,47 +11404,6 @@ const docTemplate = `{
"UserStatusDeactivated"
]
},
"domain.UserSubscriptionSummary": {
"type": "object",
"properties": {
"auto_renew": {
"type": "boolean"
},
"currency": {
"type": "string"
},
"duration_unit": {
"type": "string"
},
"duration_value": {
"type": "integer"
},
"expires_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"payment_method": {
"type": "string"
},
"plan_id": {
"type": "integer"
},
"plan_name": {
"type": "string"
},
"price": {
"type": "number"
},
"starts_at": {
"type": "string"
},
"status": {
"type": "string"
}
}
},
"domain.UserSummary": {
"type": "object",
"properties": {

View File

@ -8428,7 +8428,7 @@
},
"/api/v1/users": {
"get": {
"description": "Get users with optional filters. Each user includes active_subscription: an object when they have a current ACTIVE, non-expired plan, otherwise null.",
"description": "Get users with optional filters. Each user includes subscription_status: ACTIVE, PENDING, or Unsubscribed.",
"consumes": [
"application/json"
],
@ -11286,9 +11286,6 @@
"domain.UserProfileResponse": {
"type": "object",
"properties": {
"active_subscription": {
"$ref": "#/definitions/domain.UserSubscriptionSummary"
},
"age_group": {
"type": "string"
},
@ -11376,6 +11373,9 @@
"status": {
"$ref": "#/definitions/domain.UserStatus"
},
"subscription_status": {
"type": "string"
},
"updated_at": {
"type": "string"
}
@ -11396,47 +11396,6 @@
"UserStatusDeactivated"
]
},
"domain.UserSubscriptionSummary": {
"type": "object",
"properties": {
"auto_renew": {
"type": "boolean"
},
"currency": {
"type": "string"
},
"duration_unit": {
"type": "string"
},
"duration_value": {
"type": "integer"
},
"expires_at": {
"type": "string"
},
"id": {
"type": "integer"
},
"payment_method": {
"type": "string"
},
"plan_id": {
"type": "integer"
},
"plan_name": {
"type": "string"
},
"price": {
"type": "number"
},
"starts_at": {
"type": "string"
},
"status": {
"type": "string"
}
}
},
"domain.UserSummary": {
"type": "object",
"properties": {

View File

@ -1145,8 +1145,6 @@ definitions:
type: object
domain.UserProfileResponse:
properties:
active_subscription:
$ref: '#/definitions/domain.UserSubscriptionSummary'
age_group:
type: string
birth_day:
@ -1206,6 +1204,8 @@ definitions:
$ref: '#/definitions/domain.Role'
status:
$ref: '#/definitions/domain.UserStatus'
subscription_status:
type: string
updated_at:
type: string
type: object
@ -1221,33 +1221,6 @@ definitions:
- UserStatusActive
- UserStatusSuspended
- UserStatusDeactivated
domain.UserSubscriptionSummary:
properties:
auto_renew:
type: boolean
currency:
type: string
duration_unit:
type: string
duration_value:
type: integer
expires_at:
type: string
id:
type: integer
payment_method:
type: string
plan_id:
type: integer
plan_name:
type: string
price:
type: number
starts_at:
type: string
status:
type: string
type: object
domain.UserSummary:
properties:
active_users:
@ -8017,8 +7990,8 @@ paths:
get:
consumes:
- application/json
description: 'Get users with optional filters. Each user includes active_subscription:
an object when they have a current ACTIVE, non-expired plan, otherwise null.'
description: 'Get users with optional filters. Each user includes subscription_status:
ACTIVE, PENDING, or Unsubscribed.'
parameters:
- description: Role filter
in: query

View File

@ -365,6 +365,27 @@ func (q *Queries) GetExpiringSubscriptions(ctx context.Context) ([]GetExpiringSu
return items, nil
}
const GetSubscriptionDisplayStatusByUserID = `-- name: GetSubscriptionDisplayStatusByUserID :one
SELECT COALESCE(
(SELECT us.status::text FROM user_subscriptions us
WHERE us.user_id = $1
AND us.status = 'ACTIVE' AND us.expires_at > CURRENT_TIMESTAMP
ORDER BY us.expires_at DESC LIMIT 1),
(SELECT us.status::text FROM user_subscriptions us
WHERE us.user_id = $1
AND us.status = 'PENDING'
ORDER BY us.created_at DESC LIMIT 1),
'Unsubscribed'
)::text AS subscription_status
`
func (q *Queries) GetSubscriptionDisplayStatusByUserID(ctx context.Context, userID int64) (string, error) {
row := q.db.QueryRow(ctx, GetSubscriptionDisplayStatusByUserID, userID)
var subscription_status string
err := row.Scan(&subscription_status)
return subscription_status, err
}
const GetSubscriptionPlanByID = `-- name: GetSubscriptionPlanByID :one
SELECT id, name, description, duration_value, duration_unit, price, currency, is_active, created_at, updated_at FROM subscription_plans WHERE id = $1
`
@ -578,70 +599,42 @@ func (q *Queries) ListActiveSubscriptionPlans(ctx context.Context) ([]Subscripti
return items, nil
}
const ListActiveSubscriptionsByUserIDs = `-- name: ListActiveSubscriptionsByUserIDs :many
SELECT DISTINCT ON (us.user_id)
us.user_id,
us.id,
us.plan_id,
us.starts_at,
us.expires_at,
us.status,
us.auto_renew,
us.payment_method,
sp.name AS plan_name,
sp.duration_value,
sp.duration_unit,
sp.price,
sp.currency
FROM user_subscriptions us
JOIN subscription_plans sp ON sp.id = us.plan_id
WHERE us.user_id = ANY($1::bigint[])
AND us.status = 'ACTIVE'
AND us.expires_at > CURRENT_TIMESTAMP
ORDER BY us.user_id, us.expires_at DESC
const ListSubscriptionDisplayStatusesByUserIDs = `-- name: ListSubscriptionDisplayStatusesByUserIDs :many
WITH input AS (
SELECT unnest($1::bigint[])::bigint AS user_id
)
SELECT
input.user_id,
COALESCE(
(SELECT us.status::text FROM user_subscriptions us
WHERE us.user_id = input.user_id
AND us.status = 'ACTIVE' AND us.expires_at > CURRENT_TIMESTAMP
ORDER BY us.expires_at DESC LIMIT 1),
(SELECT us.status::text FROM user_subscriptions us
WHERE us.user_id = input.user_id
AND us.status = 'PENDING'
ORDER BY us.created_at DESC LIMIT 1),
'Unsubscribed'
)::text AS subscription_status
FROM input
`
type ListActiveSubscriptionsByUserIDsRow struct {
type ListSubscriptionDisplayStatusesByUserIDsRow struct {
UserID int64 `json:"user_id"`
ID int64 `json:"id"`
PlanID int64 `json:"plan_id"`
StartsAt pgtype.Timestamptz `json:"starts_at"`
ExpiresAt pgtype.Timestamptz `json:"expires_at"`
Status string `json:"status"`
AutoRenew bool `json:"auto_renew"`
PaymentMethod pgtype.Text `json:"payment_method"`
PlanName string `json:"plan_name"`
DurationValue int32 `json:"duration_value"`
DurationUnit string `json:"duration_unit"`
Price pgtype.Numeric `json:"price"`
Currency string `json:"currency"`
SubscriptionStatus string `json:"subscription_status"`
}
// One ACTIVE, non-expired row per user (latest expires_at wins), same rules as GetActiveSubscriptionByUserID.
func (q *Queries) ListActiveSubscriptionsByUserIDs(ctx context.Context, dollar_1 []int64) ([]ListActiveSubscriptionsByUserIDsRow, error) {
rows, err := q.db.Query(ctx, ListActiveSubscriptionsByUserIDs, dollar_1)
// Display status for admin user lists: ACTIVE (non-expired), else latest PENDING, else Unsubscribed.
func (q *Queries) ListSubscriptionDisplayStatusesByUserIDs(ctx context.Context, dollar_1 []int64) ([]ListSubscriptionDisplayStatusesByUserIDsRow, error) {
rows, err := q.db.Query(ctx, ListSubscriptionDisplayStatusesByUserIDs, dollar_1)
if err != nil {
return nil, err
}
defer rows.Close()
var items []ListActiveSubscriptionsByUserIDsRow
var items []ListSubscriptionDisplayStatusesByUserIDsRow
for rows.Next() {
var i ListActiveSubscriptionsByUserIDsRow
if err := rows.Scan(
&i.UserID,
&i.ID,
&i.PlanID,
&i.StartsAt,
&i.ExpiresAt,
&i.Status,
&i.AutoRenew,
&i.PaymentMethod,
&i.PlanName,
&i.DurationValue,
&i.DurationUnit,
&i.Price,
&i.Currency,
); err != nil {
var i ListSubscriptionDisplayStatusesByUserIDsRow
if err := rows.Scan(&i.UserID, &i.SubscriptionStatus); err != nil {
return nil, err
}
items = append(items, i)

View File

@ -56,54 +56,6 @@ type UserSubscription struct {
Currency *string
}
// UserSubscriptionSummary is the active subscription attached to admin user list responses (GET /users).
type UserSubscriptionSummary struct {
ID int64 `json:"id"`
PlanID int64 `json:"plan_id"`
PlanName string `json:"plan_name"`
Status string `json:"status"`
StartsAt time.Time `json:"starts_at"`
ExpiresAt time.Time `json:"expires_at"`
AutoRenew bool `json:"auto_renew"`
PaymentMethod *string `json:"payment_method,omitempty"`
DurationValue int32 `json:"duration_value"`
DurationUnit string `json:"duration_unit"`
Price float64 `json:"price"`
Currency string `json:"currency"`
}
// Summary returns a copy safe for JSON embedding; nil if receiver is nil.
func (us *UserSubscription) Summary() *UserSubscriptionSummary {
if us == nil {
return nil
}
s := &UserSubscriptionSummary{
ID: us.ID,
PlanID: us.PlanID,
Status: us.Status,
StartsAt: us.StartsAt,
ExpiresAt: us.ExpiresAt,
AutoRenew: us.AutoRenew,
PaymentMethod: us.PaymentMethod,
}
if us.PlanName != nil {
s.PlanName = *us.PlanName
}
if us.DurationValue != nil {
s.DurationValue = *us.DurationValue
}
if us.DurationUnit != nil {
s.DurationUnit = *us.DurationUnit
}
if us.Price != nil {
s.Price = *us.Price
}
if us.Currency != nil {
s.Currency = *us.Currency
}
return s
}
type CreateSubscriptionPlanInput struct {
Name string
Description *string

View File

@ -121,7 +121,7 @@ type UserProfileResponse struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
ActiveSubscription *UserSubscriptionSummary `json:"active_subscription"`
SubscriptionStatus string `json:"subscription_status"`
}
type UserFilter struct {

View File

@ -18,7 +18,8 @@ type SubscriptionStore interface {
CreateUserSubscription(ctx context.Context, input domain.CreateUserSubscriptionInput) (*domain.UserSubscription, error)
GetUserSubscriptionByID(ctx context.Context, id int64) (*domain.UserSubscription, error)
GetActiveSubscriptionByUserID(ctx context.Context, userID int64) (*domain.UserSubscription, error)
ListActiveSubscriptionsByUserIDs(ctx context.Context, userIDs []int64) (map[int64]*domain.UserSubscription, error)
ListSubscriptionDisplayStatusesByUserIDs(ctx context.Context, userIDs []int64) (map[int64]string, error)
GetSubscriptionDisplayStatusByUserID(ctx context.Context, userID int64) (string, error)
GetUserSubscriptionHistory(ctx context.Context, userID int64, limit, offset int32) ([]domain.UserSubscription, error)
HasActiveSubscription(ctx context.Context, userID int64) (bool, error)
CancelUserSubscription(ctx context.Context, id int64) error

View File

@ -157,39 +157,25 @@ func (s *Store) GetActiveSubscriptionByUserID(ctx context.Context, userID int64)
}, nil
}
func (s *Store) ListActiveSubscriptionsByUserIDs(ctx context.Context, userIDs []int64) (map[int64]*domain.UserSubscription, error) {
func (s *Store) ListSubscriptionDisplayStatusesByUserIDs(ctx context.Context, userIDs []int64) (map[int64]string, error) {
if len(userIDs) == 0 {
return map[int64]*domain.UserSubscription{}, nil
return map[int64]string{}, nil
}
rows, err := s.queries.ListActiveSubscriptionsByUserIDs(ctx, userIDs)
rows, err := s.queries.ListSubscriptionDisplayStatusesByUserIDs(ctx, userIDs)
if err != nil {
return nil, err
}
out := make(map[int64]*domain.UserSubscription, len(rows))
out := make(map[int64]string, len(rows))
for _, r := range rows {
dv := r.DurationValue
du := r.DurationUnit
pn := r.PlanName
cur := r.Currency
out[r.UserID] = &domain.UserSubscription{
ID: r.ID,
UserID: r.UserID,
PlanID: r.PlanID,
StartsAt: r.StartsAt.Time,
ExpiresAt: r.ExpiresAt.Time,
Status: r.Status,
AutoRenew: r.AutoRenew,
PaymentMethod: fromPgText(r.PaymentMethod),
PlanName: &pn,
DurationValue: &dv,
DurationUnit: &du,
Price: float64Ptr(fromPgNumeric(r.Price)),
Currency: &cur,
}
out[r.UserID] = r.SubscriptionStatus
}
return out, nil
}
func (s *Store) GetSubscriptionDisplayStatusByUserID(ctx context.Context, userID int64) (string, error) {
return s.queries.GetSubscriptionDisplayStatusByUserID(ctx, userID)
}
func (s *Store) GetUserSubscriptionHistory(ctx context.Context, userID int64, limit, offset int32) ([]domain.UserSubscription, error) {
subs, err := s.queries.GetUserSubscriptionHistory(ctx, dbgen.GetUserSubscriptionHistoryParams{
UserID: userID,

View File

@ -103,13 +103,19 @@ func (s *Service) GetSubscriptionByID(ctx context.Context, id int64) (*domain.Us
return sub, nil
}
// GetActiveSubscription returns the ACTIVE, non-expired subscription for the user.
func (s *Service) GetActiveSubscription(ctx context.Context, userID int64) (*domain.UserSubscription, error) {
return s.store.GetActiveSubscriptionByUserID(ctx, userID)
}
// ListActiveSubscriptionsForUserIDs returns the current ACTIVE, non-expired subscription per user (latest expiry).
func (s *Service) ListActiveSubscriptionsForUserIDs(ctx context.Context, userIDs []int64) (map[int64]*domain.UserSubscription, error) {
return s.store.ListActiveSubscriptionsByUserIDs(ctx, userIDs)
// ListSubscriptionDisplayStatusesForUserIDs returns ACTIVE, PENDING, or Unsubscribed per user_id (admin list).
func (s *Service) ListSubscriptionDisplayStatusesForUserIDs(ctx context.Context, userIDs []int64) (map[int64]string, error) {
return s.store.ListSubscriptionDisplayStatusesByUserIDs(ctx, userIDs)
}
// GetSubscriptionDisplayStatusForUserID returns ACTIVE, PENDING, or Unsubscribed for one user.
func (s *Service) GetSubscriptionDisplayStatusForUserID(ctx context.Context, userID int64) (string, error) {
return s.store.GetSubscriptionDisplayStatusByUserID(ctx, userID)
}
func (s *Service) GetSubscriptionHistory(ctx context.Context, userID int64, limit, offset int32) ([]domain.UserSubscription, error) {

View File

@ -423,7 +423,7 @@ func (h *Handler) CheckUserPending(c *fiber.Ctx) error {
// GetAllUsers godoc
// @Summary Get all users
// @Description Get users with optional filters. Each user includes active_subscription: an object when they have a current ACTIVE, non-expired plan, otherwise null.
// @Description Get users with optional filters. Each user includes subscription_status: ACTIVE, PENDING, or Unsubscribed.
// @Tags user
// @Accept json
// @Produce json
@ -503,9 +503,9 @@ func (h *Handler) GetAllUsers(c *fiber.Ctx) error {
for i, u := range users {
userIDs[i] = u.ID
}
activeSubs, err := h.subscriptionsSvc.ListActiveSubscriptionsForUserIDs(c.Context(), userIDs)
subStatuses, err := h.subscriptionsSvc.ListSubscriptionDisplayStatusesForUserIDs(c.Context(), userIDs)
if err != nil {
h.mongoLoggerSvc.Error("failed to batch-load active subscriptions for user list",
h.mongoLoggerSvc.Error("failed to batch-load subscription display status for user list",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()))
@ -551,9 +551,11 @@ func (h *Handler) GetAllUsers(c *fiber.Ctx) error {
if !u.BirthDay.IsZero() {
bd = u.BirthDay.Format("2006-01-02")
}
var activeSub *domain.UserSubscriptionSummary
if sub, ok := activeSubs[u.ID]; ok {
activeSub = sub.Summary()
var subStatus string
if s, ok := subStatuses[u.ID]; ok {
subStatus = s
} else {
subStatus = "Unsubscribed"
}
mapped = append(mapped, domain.UserProfileResponse{
@ -585,7 +587,7 @@ func (h *Handler) GetAllUsers(c *fiber.Ctx) error {
PreferredLanguage: u.PreferredLanguage,
CreatedAt: u.CreatedAt,
UpdatedAt: u.UpdatedAt,
ActiveSubscription: activeSub,
SubscriptionStatus: subStatus,
})
}
@ -1405,6 +1407,17 @@ func (h *Handler) GetUserProfile(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user profile:"+err.Error())
}
subscriptionStatus, err := h.subscriptionsSvc.GetSubscriptionDisplayStatusForUserID(c.Context(), user.ID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get subscription display status for profile",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve subscription status:"+err.Error())
}
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
if err != nil {
if err != authentication.ErrRefreshTokenNotFound {
@ -1448,6 +1461,7 @@ func (h *Handler) GetUserProfile(c *fiber.Ctx) error {
PreferredLanguage: user.PreferredLanguage,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
SubscriptionStatus: subscriptionStatus,
}
return response.WriteJSON(c, fiber.StatusOK, "User profile retrieved successfully", res, nil)
}
@ -1502,6 +1516,17 @@ func (h *Handler) AdminProfile(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user profile:"+err.Error())
}
subscriptionStatus, err := h.subscriptionsSvc.GetSubscriptionDisplayStatusForUserID(c.Context(), user.ID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get subscription display status for admin profile",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve subscription status:"+err.Error())
}
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
if err != nil {
if err != authentication.ErrRefreshTokenNotFound {
@ -1537,6 +1562,7 @@ func (h *Handler) AdminProfile(c *fiber.Ctx) error {
PreferredLanguage: user.PreferredLanguage,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
SubscriptionStatus: subscriptionStatus,
}
// Ensure birthday is included and formatted
if !user.BirthDay.IsZero() {
@ -1621,6 +1647,21 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get users: "+err.Error())
}
userIDs := make([]int64, len(users))
for i, u := range users {
userIDs[i] = u.ID
}
subStatuses, err := h.subscriptionsSvc.ListSubscriptionDisplayStatusesForUserIDs(c.Context(), userIDs)
if err != nil {
h.mongoLoggerSvc.Error("SearchUserByNameOrPhone - failed to load subscription status",
zap.Any("request", req),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get subscription info: "+err.Error())
}
res := make([]domain.UserProfileResponse, 0, len(users))
for _, user := range users {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
@ -1637,6 +1678,11 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
lastLogin = &user.CreatedAt
}
subStatus := "Unsubscribed"
if s, ok := subStatuses[user.ID]; ok {
subStatus = s
}
// var orgID *int64
// if user.OrganizationID.Valid {
// orgID = &user.OrganizationID.Value
@ -1669,6 +1715,7 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
PreferredLanguage: user.PreferredLanguage,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
SubscriptionStatus: subStatus,
})
}
@ -1711,6 +1758,17 @@ func (h *Handler) GetUserByID(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get user: "+err.Error())
}
subscriptionStatus, err := h.subscriptionsSvc.GetSubscriptionDisplayStatusForUserID(c.Context(), user.ID)
if err != nil {
h.mongoLoggerSvc.Error("Failed to get subscription display status for user by id",
zap.Int64("userID", userID),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve subscription status: "+err.Error())
}
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
if err != nil && err != authentication.ErrRefreshTokenNotFound {
h.mongoLoggerSvc.Error("Failed to get user last login",
@ -1765,6 +1823,7 @@ func (h *Handler) GetUserByID(c *fiber.Ctx) error {
PreferredLanguage: user.PreferredLanguage,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
SubscriptionStatus: subscriptionStatus,
}
return response.WriteJSON(c, fiber.StatusOK, "User retrieved successfully", res, nil)