Jinping HeSenior Full-Stack Developer & Data Analyst · Auckland
Gespcsp: A Multi-Tenant Platform for Informatics Olympiad Practice and Assessment Abstract

2 Nov 2026

Gespcsp: A Multi-Tenant Platform for Informatics Olympiad Practice and Assessment Abstract

Gespcsp is a multi-tenant platform for GESP and CSP practice, online exams, and programming contests, with a Next.js site and a .NET backend that judges code and keeps each institution in its own database.

Gespcsp is the system behind 校君宝信奥 (www.xw3q.cc), a site for students preparing for GESP and CSP, the mainstream informatics olympiad pathways in China. It is not a single-purpose question bank. Students practice problems, enter timed exams, and compete in contests. Teachers and institutions manage classes, lesson hours, and learners. The operations team publishes papers, watches submissions, and runs membership and payments. The public site and the admin console share one Next.js application. The backend is a set of .NET services behind a gateway, with PostgreSQL, Redis, and RabbitMQ. Each institution can be given its own database. Programming answers are judged on a separate judge server. This article describes what the product does, which technologies it uses, and the engineering choices that shape it.
Who it is for
The product serves five kinds of accounts: platform administrators, institutions, teachers, enrolled students, and prospective students. Those roles do not share one screen. A platform administrator works in the operations console. A student lands in a personal center after login and cannot open the admin area. An administrator cannot browse the student center as if they were a learner. Public pages, such as news and problem statements that are meant to be read before login, stay available to everyone.
That split matches how an olympiad program actually runs. The platform owner publishes content and watches the whole site. A training institution cares about its own students, classes, and lesson hours. A student cares about the next problem, the next exam, and the score they just received.
What students can do
Practice is the daily loop. A student opens a problem, reads the statement, and writes C++ or Python in a code editor on the page. The submission goes to the judge. The result comes back as accepted, wrong answer, time limit, or another standard verdict. Submissions are kept, so a student can look back at earlier attempts. A wrong-answer notebook collects problems that still need work.
Exams are a different mode. A paper is assembled from the question bank, the student starts an attempt, answers the paper, and submits programming tasks for judging before handing the paper in. The platform then produces a report, including a PDF the student can keep. Contests sit beside exams: they have their own windows, problem sets, and ranking.
Progress is visible as points. Daily login, a first fully correct practice solution, a perfect contest paper, and exam scores can all add points under rules the operator configures. Students see a balance and a level name. A leaderboard ranks learners and leaves platform administrators off the list. Points are stored as a ledger, so a total can be explained by the lines that produced it.
Accounts sign in with a mobile number and a password, or with a one-time SMS code. WeChat binding and QR login are part of the same identity system. SMS sending is rate-limited: a number must wait between messages, there is a daily cap, and a code expires after a few minutes and cannot be reused. An unregistered number is not sent a code.
What the operations console covers
The console is where the site is actually operated. Website management covers news articles, friend links, and SEO fields. Students and institutions are managed as separate directories. Tags are split into problem tags, knowledge-point tags, and ability tags, so a paper and a later report can talk about the same curriculum language.
Academic tools cover paper templates, papers, and exam records. The question bank, classes, contests, the submission list, and downloadable materials live together under contest and practice management. From a class list an operator opens the roster, moves a student to another class, or adds the student to an additional class without dropping the ones they already attend. A placement page lists active students, including those who are not in any class yet, so an enrollment can start there.
Lesson hours are a small accounting system of their own. Courses, purchases, remaining balances, student attendance, and teacher sessions are separate lists. Recording a teacher session deducts hours for students who already have a balance in that course, writes an attendance row, and can restore the hours if the session is deleted. Purchases and attendance can be exported to Excel.
Orders, redemption codes, and refunds support membership. WeChat Pay and Alipay are used for real charges and refunds. A paid invitation can grant VIP automatically. Statistics pages summarize students, institutions, papers, exams, submissions, and orders. System pages cover settings, administrator accounts, judge servers, and the operation log.
Technology stack
The backend is .NET 10. In local development, .NET Aspire starts the application and its containers. In production, the same services run from Docker Compose on a single Ubuntu server. Aspire is a development orchestrator here, not the production process model.
Three business services sit behind a YARP gateway. The gateway is the only API address the browser calls. It checks the JWT and passes the tenant identifier downstream. Auth owns identity, tenants, and platform or student login, and stores them in PostgreSQL under a dedicated schema. Operations owns tenant business data. Support runs Hangfire schedules and keeps a task audit trail. Services talk about tenant provisioning through RabbitMQ and MassTransit. Redis holds short-lived data such as SMS codes, rate-limit counters, and cached tenant connection strings.
The frontend is one Next.js 16 application on React 19. The marketing site, the student center, and the admin console are routes in that app, not three deployments. Admin screens use MUI. The student-facing pages use Tailwind where a lighter layout fits. The in-browser editor is Monaco. It is served from the site itself rather than loaded from a public CDN, because a CDN that students cannot reach leaves the editor stuck on a loading message. The production build is a standalone Node server. Nginx on the host terminates HTTPS and proxies the site and the gateway.
Judging is not done inside the web process. A separate judge server compiles and runs student programs under a sandbox. The admin console can see the judge machines it is allowed to use. That boundary matters: accepting arbitrary code and executing it in the same process as the API would be reckless.
Technical characteristics
A database per tenant. Creating an institution writes a tenant record and publishes an event. Operations creates a database for that tenant, builds the schema, and caches the connection string. Later requests carry the tenant id from the token. The data context opens the matching database. One institution's exam rows are not mixed into another institution's tables. Platform identity still lives in one auth database, so a person has a single login even when business data is isolated.
One front door. Browsers do not call Auth or Operations by their internal ports. They call the gateway. CORS, JWT checks, and the tenant header are applied in one place. Production exposes only HTTP and HTTPS on Nginx. Application ports stay on the machine.
Time is stored in UTC and shown in China Standard Time. Exam clocks, purchase times, and logs do not depend on whichever timezone the server image happens to use.
Writes that must succeed together use an explicit transaction. The PostgreSQL provider is configured to retry transient failures. A retry cannot rerun a user-started transaction unless the work is wrapped in the provider's execution strategy. Class transfers, lesson purchases, and point grants follow that rule, so a retried request does not leave a half-applied balance.
The schema is created idempotently. Auth tables are ensured at startup with create-if-not-exists statements, not only by a one-shot migration. A restarted container can come up against an existing database without dropping it.
Membership, points, and lesson hours are ledgers, not a single mutable number. The screen shows a balance. The stored history can say why the balance changed, who operated it, and when.
HTTPS is operated, not pasted in from a vendor zip file. The public site uses a Let's Encrypt certificate for both www.xw3q.cc and the apex domain. A daily job renews it when it enters the renewal window and reloads Nginx. The apex name has its own DNS record. A wildcard record does not cover the apex, and a certificate request that includes a name with no address fails for the whole certificate.
What is deliberately not finished
Reports already summarize an exam and can be exported. A knowledge-graph or ability diagnosis that reasons over the knowledge-point tags is still later work. The tags are in the product so that diagnosis has something precise to attach to, but the inference itself is not claimed as done.
Closing
Gespcsp is a working operations system for an olympiad training site: practice, exams, contests, classes, lesson hours, payments, and a judged programming environment, with tenant isolation behind a single website. The stack is conventional on purpose. Next.js for the product surface, .NET services for identity and tenant data, PostgreSQL for the records, Redis and RabbitMQ for the short-lived and asynchronous parts, and a separate judge for untrusted code. The interesting part is the fit between those pieces and the way a training institution actually runs a week of classes.