Files
gongxue-base/apps/api/src/features/scoreline/routes.ts
2026-06-21 21:54:43 +08:00

165 lines
5.7 KiB
TypeScript

import type { RequestContext } from '../../core/http.js';
import { intParam, stringParam, tenantIdFrom } from '../../core/request.js';
import { query, queryOne } from '../../core/db.js';
export async function scorelineFieldsRoute(ctx: RequestContext) {
const tenantId = tenantIdFrom(ctx);
const regionId = stringParam(ctx, 'regionId');
const items = await query(
`
select id, legacy_id as "legacyId", region_id as "regionId",
field_key as "fieldKey", field_name as "fieldName",
field_type as "fieldType", unit, is_filter as "isFilter",
is_required as "isRequired", is_visible as "isVisible",
is_trend as "isTrend", options, placeholder, description,
sort_order as "sortOrder", created_at as "createdAt",
updated_at as "updatedAt"
from public.scoreline_fields
where tenant_id = $1 and ($2::uuid is null or region_id = $2::uuid)
order by sort_order asc, field_name asc
`,
[tenantId, regionId || null],
);
return { items };
}
export async function scorelineSchoolsRoute(ctx: RequestContext) {
const tenantId = tenantIdFrom(ctx);
const regionId = stringParam(ctx, 'regionId');
const q = stringParam(ctx, 'q');
const limit = intParam(ctx, 'limit', 200, 1000);
const items = await query(
`
select id, legacy_id as "legacyId", region_id as "regionId",
name, short_name as "shortName", type, is_hot as "isHot",
sort_order as "order", created_at as "createdAt",
updated_at as "updatedAt"
from public.scoreline_schools
where tenant_id = $1
and ($2::uuid is null or region_id = $2::uuid)
and ($3::text = '' or name ilike '%' || $3 || '%' or short_name ilike '%' || $3 || '%')
order by is_hot desc, sort_order asc, name asc, type asc nulls last
limit $4
`,
[tenantId, regionId || null, q, limit],
);
return { items };
}
export async function scorelineMajorsRoute(ctx: RequestContext) {
const tenantId = tenantIdFrom(ctx);
const regionId = stringParam(ctx, 'regionId');
const schoolId = stringParam(ctx, 'schoolId');
const limit = intParam(ctx, 'limit', 500, 2000);
const items = await query(
`
select id, legacy_id as "legacyId", region_id as "regionId",
school_id as "schoolId", name, sort_order as "order",
has_restriction as "hasRestriction", restriction_desc as "restrictionDesc",
created_at as "createdAt", updated_at as "updatedAt"
from public.scoreline_majors
where tenant_id = $1
and ($2::uuid is null or region_id = $2::uuid)
and ($3::uuid is null or school_id = $3::uuid)
order by sort_order asc, name asc
limit $4
`,
[tenantId, regionId || null, schoolId || null, limit],
);
return { items };
}
export async function scorelineRecordsRoute(ctx: RequestContext) {
const tenantId = tenantIdFrom(ctx);
const regionId = stringParam(ctx, 'regionId');
const schoolId = stringParam(ctx, 'schoolId');
const majorId = stringParam(ctx, 'majorId');
const year = Number(ctx.url.searchParams.get('year') || 0);
const page = intParam(ctx, 'page', 1, 10_000);
const pageSize = intParam(ctx, 'pageSize', intParam(ctx, 'perPage', 20, 100), 100);
const offset = (page - 1) * pageSize;
const where = `
tenant_id = $1
and ($2::uuid is null or region_id = $2::uuid)
and ($3::uuid is null or school_id = $3::uuid)
and ($4::uuid is null or major_id = $4::uuid)
and ($5::integer = 0 or year = $5::integer)
`;
const params = [tenantId, regionId || null, schoolId || null, majorId || null, Number.isFinite(year) ? Math.trunc(year) : 0];
const [countRow, items] = await Promise.all([
queryOne<{ total: string }>(`select count(*)::text as total from public.scoreline_records where ${where}`, params),
query(
`
select id, legacy_id as "legacyId", region_id as "regionId",
school_id as "schoolId", major_id as "majorId", year,
school_name as "schoolName", major_name as "majorName",
field_values as "fieldValues", created_at as "createdAt",
updated_at as "updatedAt"
from public.scoreline_records
where ${where}
order by year desc, school_name asc nulls last, major_name asc nulls last
limit $6 offset $7
`,
[...params, pageSize, offset],
),
]);
return {
page,
pageSize,
total: Number(countRow?.total || 0),
items,
};
}
export async function scorelineTrendRoute(ctx: RequestContext) {
const tenantId = tenantIdFrom(ctx);
const regionId = stringParam(ctx, 'regionId');
const schoolId = stringParam(ctx, 'schoolId');
const majorId = stringParam(ctx, 'majorId');
const limit = intParam(ctx, 'limit', 20, 100);
const items = await query(
`
select id, region_id as "regionId", school_id as "schoolId",
major_id as "majorId", year, school_name as "schoolName",
major_name as "majorName", field_values as "fieldValues"
from public.scoreline_records
where tenant_id = $1
and ($2::uuid is null or region_id = $2::uuid)
and ($3::uuid is null or school_id = $3::uuid)
and ($4::uuid is null or major_id = $4::uuid)
order by year asc, major_name asc nulls last
limit $5
`,
[tenantId, regionId || null, schoolId || null, majorId || null, limit],
);
return { items };
}
export async function scorelineYearsRoute(ctx: RequestContext) {
const tenantId = tenantIdFrom(ctx);
const regionId = stringParam(ctx, 'regionId');
const items = await query<{ year: number }>(
`
select distinct year
from public.scoreline_records
where tenant_id = $1 and ($2::uuid is null or region_id = $2::uuid)
order by year desc
`,
[tenantId, regionId || null],
);
return { items: items.map(item => item.year) };
}