forked from wangziqi/gongxue-base
feat: manage tenant badges
This commit is contained in:
@@ -72,6 +72,7 @@ function daysBetween(dateValue: unknown, today: Date) {
|
||||
}
|
||||
|
||||
const FEEDBACK_TYPES = ['question_error', 'content_error', 'video_error', 'asset_error', 'system_bug', 'suggestion', 'other'];
|
||||
const BADGE_CATEGORIES = ['learning', 'practice', 'vocabulary', 'mock_exam', 'activity', 'feedback', 'sales', 'system', 'custom'];
|
||||
|
||||
export async function profileMeRoute(ctx: RequestContext) {
|
||||
const tenantId = await tenantIdFrom(ctx);
|
||||
@@ -430,6 +431,63 @@ export async function scoreEventsRoute(ctx: RequestContext) {
|
||||
return { items };
|
||||
}
|
||||
|
||||
export async function profileBadgesRoute(ctx: RequestContext) {
|
||||
const tenantId = await tenantIdFrom(ctx);
|
||||
const userId = await userIdFrom(ctx);
|
||||
const limit = intParam(ctx, 'limit', 100, 300);
|
||||
const includeLocked = ctx.url.searchParams.get('includeLocked') === 'true';
|
||||
const category = stringParam(ctx, 'category');
|
||||
const params: unknown[] = [tenantId, userId];
|
||||
const filters = ['b.tenant_id = $1', 'b.is_active = true'];
|
||||
if (category) {
|
||||
if (!BADGE_CATEGORIES.includes(category)) {
|
||||
throw new HttpError(400, `Invalid badge category: ${category}`, 'INVALID_BADGE_CATEGORY');
|
||||
}
|
||||
params.push(category);
|
||||
filters.push(`b.category = $${params.length}`);
|
||||
}
|
||||
if (!includeLocked) {
|
||||
filters.push('ub.id is not null');
|
||||
}
|
||||
params.push(limit);
|
||||
|
||||
const items = await query<{ unlocked: boolean } & Record<string, unknown>>(
|
||||
`
|
||||
select b.id as "badgeId", b.legacy_id as "legacyId", b.name,
|
||||
b.description, b.category, b.icon_url as "iconUrl", b.level,
|
||||
b.unlock_type as "unlockType", b.condition_field as "conditionField",
|
||||
b.condition_operator as "conditionOperator", b.condition_value as "conditionValue",
|
||||
b.condition_extra as "conditionExtra", b.metadata,
|
||||
b.sort_order as "order",
|
||||
ub.id as "grantId", ub.note, ub.metadata as "grantMetadata",
|
||||
ub.granted_at as "grantedAt",
|
||||
(ub.id is not null) as unlocked
|
||||
from public.badges b
|
||||
left join public.user_badges ub
|
||||
on ub.tenant_id = b.tenant_id
|
||||
and ub.badge_id = b.id
|
||||
and ub.user_id = $2
|
||||
where ${filters.join(' and ')}
|
||||
order by (ub.id is not null) desc,
|
||||
coalesce(ub.granted_at, ub.created_at) desc nulls last,
|
||||
b.sort_order asc,
|
||||
b.level asc nulls last,
|
||||
b.created_at desc
|
||||
limit $${params.length}
|
||||
`,
|
||||
params,
|
||||
);
|
||||
|
||||
return {
|
||||
items,
|
||||
summary: {
|
||||
total: items.length,
|
||||
unlocked: items.filter(item => item.unlocked).length,
|
||||
includeLocked,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function feedbacksRoute(ctx: RequestContext) {
|
||||
const tenantId = await tenantIdFrom(ctx);
|
||||
const userId = await userIdFrom(ctx);
|
||||
|
||||
Reference in New Issue
Block a user