Skip to main content

06 / WORK / CASE STUDY

NKUST Equipment Borrowing System

Barcode lending, inventory, compensation and a meal-fund ledger for a university department — solo-built and deployed since 2025.

TYPE
FULL STACK
SCOPE
Deployed · solo build
STACK
DJANGO · DRF · MYSQL · REACT · PLAYWRIGHT
LINKS
LIVE

01 / PROBLEM

The department's equipment lending lived inside a legacy platform module with messy data, and the shared meal fund was a hand-maintained spreadsheet. This system took over both: formalizing lending (overdue, compensation, stocktakes, kits) and turning the fund into real ledger accounting with balances, settlement periods and cash verification.

02 / CONSTRAINTS

  • Had to absorb the legacy database — old borrowing records were riddled with empty fields.
  • Front-desk lending is high-frequency and run by non-technical staff — each transaction must finish in seconds.
  • One VPS, no containers or Redis: caching is in-process LocMemCache, with rate limits on a DB cache to share across workers.

03 / ARCHITECTURE

A Django 5 + DRF modular monolith split into 13 domain apps (borrowing, finance, audit, inventory, kits…) — 27 models, 23 resource routes plus 86 custom actions — with a React 19 + Ant Design frontend of 40 pages. JWT lives in HttpOnly cookies with refresh rotation and blacklisting; RBAC is modeled as Roles and Capabilities.

Auditing is its own subsystem: middleware records every operation (sensitive-action flags, latency, response status), plus field-level model change logs. The finance subsystem runs the meal fund: transactions maintain account balances on write, with settlement periods and cash verification — and the whole ledger can be rebuilt from the original spreadsheet in one import.

04 / RESPONSIBILITIES

Independent full-stack development (single author across the entire git history, 2025/09–2026/07): data models, APIs, permissions, the React frontend, test strategy and deployment (nginx + gunicorn under systemd).

05 / CHALLENGES → SOLUTIONS

CHALLENGE

Legacy borrowing records had to move into the new schema, but the data was too dirty to import directly.

SOLUTION

Built a migration toolkit (one mapper per entity, a standalone verifier, dry-run and reports), keeping the legacy DB behind its own connection. One real run took 180 seconds and moved 9,138 borrow tickets, 3,336 students and 370 pieces of equipment — while 2,271 legacy rows failed on empty fields. Those errors live in the migration report as tracked data-quality debt, not swallowed.

CHALLENGE

The front desk needs speed — manual entry was too slow, and desktop and mobile scan with entirely different hardware.

SOLUTION

Made the whole flow barcode-driven: the backend generates Code128 labels; the frontend auto-switches by device — scanner-gun input on desktop, camera (@zxing) on mobile — with batch scanning and short-window dedup. One scan pulls up the equipment and its ticket.

CHALLENGE

Years of old and new records were tangled in the meal-fund spreadsheet — formalizing it couldn't lose a single row.

SOLUTION

Wrote a two-pass import command: pass one creates accounts and categories, pass two handles opening balances, settlement periods and every transaction — all inside a DB transaction, with dry-run and re-runs. Balances maintain themselves on write, and month-end cash verification closes the loop.

06 / SYSTEM FACTS

13

domain apps

86

custom API actions

1,112

backend test functions

36

Playwright E2E specs

07 / LESSONS

Migration guards belong in the mapper: the 2,271 failures were nearly all one class of empty-field problem — clean first, import second, and that error report never exists.

Silencing errors into 'everything is fine' is worse than breaking. The duplicate pages and fake-data displays a late UX audit surfaced are debt from the fast-stacking phase — now numbered, listed, and being paid down.

08 / SCREENS

All screens below show demo (mock) data — no real user data.

Equipment borrowing system login page
Login (self-built JWT auth)
Borrow/return center: tabs for lending, returns and quick lookup, daily stats and barcode scanning
Borrow/return center (barcode flow)
Equipment management: category tree, equipment list, batch import and batch barcode printing
Equipment management (batch barcodes)

NEXT

Large-scale Django Microservices Commerce Platform