forked from wangziqi/gongxue-base
feat: scaffold supabase multi-tenant backend
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
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) };
|
||||
}
|
||||
Reference in New Issue
Block a user