[{"data":1,"prerenderedAt":242},["ShallowReactive",2],{"projects-list":3},[4,70],{"id":5,"title":6,"approach":7,"body":8,"client":34,"cover":35,"coverImages":36,"demoUrl":39,"description":40,"draft":41,"extension":42,"featured":43,"meta":44,"navigation":43,"order":45,"outcome":46,"path":47,"problem":48,"repoUrl":39,"role":49,"screenshots":50,"seo":54,"stem":55,"tagline":56,"tags":57,"tech":61,"year":68,"__hash__":69},"projects\u002Fprojects\u002Fadmin-dashboard-system.md","Internal Admin Dashboard","Rebuilt from scratch over 4 months. Used .NET 8 Minimal API for the backend\nwith clean architecture, PostgreSQL for data, Nuxt 3 for the frontend.\nImplemented role-based access control, audit logs, and a plugin-based module\nsystem so they can add new business modules without touching core code.\n",{"type":9,"value":10,"toc":28},"minimark",[11,15,19,22,25],[12,13,14],"p",{},"可以在这里继续写项目详情、技术思考、踩坑记录等。\n这部分会作为正文（ProseContent）渲染在页面里。",[16,17,18],"h2",{"id":18},"架构决策的思考",[12,20,21],{},"比如为什么不选 Django，为什么不选 Next.js，为什么要自建认证而不用 Auth0...",[16,23,24],{"id":24},"遇到的挑战",[12,26,27],{},"项目中期客户要求加多租户支持，我们通过行级安全和中间件重构了数据访问层...",{"title":29,"searchDepth":30,"depth":30,"links":31},"",2,[32,33],{"id":18,"depth":30,"text":18},{"id":24,"depth":30,"text":24},"Confidential · B2B distribution company","\u002Fimages\u002Ftravel\u002Fhome-placeholder-1.jpg",[35,37,38],"\u002Fimages\u002Ftravel\u002Fhome-placeholder-2.jpg","\u002Fimages\u002Ftravel\u002Fhome-placeholder-3.jpg",null,"Designed and built a full-featured admin dashboard for a mid-size company, handling order management, inventory, and reporting. Replaced a legacy ASP.NET WebForms system that had become unmaintainable.",false,"md",true,{},1,"The new system ships features 10x faster, is fully mobile-responsive, and has\nreduced daily operation time by about 40%. They've since added 3 new modules\nin-house using the plugin system.\n","\u002Fprojects\u002Fadmin-dashboard-system","The client's existing admin system was built 8 years ago on ASP.NET WebForms.\nIt was slow, had no mobile support, and new features took weeks to ship.\nDaily operations were bottlenecked by the software itself.\n","Full-Stack Developer (solo)",[35,37,38,51,52,53],"\u002Fimages\u002Ftravel\u002Fhome-placeholder-4.jpg","\u002Fimages\u002Ftravel\u002Fproject-placeholder-1.jpg","\u002Fimages\u002Ftravel\u002Fproject-placeholder-2.jpg",{"title":6,"description":40},"projects\u002Fadmin-dashboard-system","A role-based admin system serving 100+ internal users.",[58,59,60],"full-stack","backend","admin",[62,63,64,65,66,67],".NET","Nuxt","TypeScript","PostgreSQL","Docker","Nginx","2025","XZl96Rg7ihyVkCI205VL87FkpI5Du69der2oJuepbw4",{"id":71,"title":72,"approach":73,"body":74,"client":214,"cover":215,"coverImages":216,"demoUrl":39,"description":217,"draft":41,"extension":42,"featured":43,"meta":218,"navigation":43,"order":30,"outcome":219,"path":220,"problem":221,"repoUrl":39,"role":222,"screenshots":223,"seo":227,"stem":228,"tagline":229,"tags":230,"tech":234,"year":240,"__hash__":241},"projects\u002Fprojects\u002Ftravel-dm-platform.md","Nationwide Travel DM Magazine B2B Platform","Designed and built an end-to-end automation pipeline that turned uploads\ninto a filesystem convention rather than manual data entry. The import\ntool walked a standardized directory tree, parsed metadata into the\ndatabase, renamed high-res files by primary key, and distributed them\nacross a file server cluster. Sharded by province for parallel ingestion,\nwith full read\u002Fwrite path separation. The frontend reader was hand-rolled\nin jQuery, supporting tiered loading and CSS-transform-based zoom and pan\nfor dense pricing tables — years before mature open-source alternatives\nexisted.\n",{"type":9,"value":75,"toc":206},[76,79,83,86,90,93,110,116,120,123,150,154,157,160,164,167,193,197,200,203],[12,77,78],{},"This is a ten-year retrospective on a project that ran from 2009 to 2019\nand was shut down as the pandemic and mobile-internet shift reshaped the\ntravel industry. The full write-up — including the engineering decisions,\nthe indexing lessons learned the hard way, and what I'd do differently\ntoday — is in the blog post linked from this page.",[16,80,82],{"id":81},"what-this-project-was","What this project was",[12,84,85],{},"A B2B platform that digitized weekly travel DM (direct-mail) magazines\nfor 25 provincial branches across China. Every week, each branch\npublished a 200–300 page magazine packed with tour pricing, itineraries,\nand departure dates. This platform took those magazines online so any\ntravel agency in the country could browse the latest pricing in real time.",[16,87,89],{"id":88},"the-real-challenge-volume-and-window","The real challenge: volume and window",[12,91,92],{},"The technical shape of the project was defined by two extremes:",[94,95,96,104],"ul",{},[97,98,99,103],"li",{},[100,101,102],"strong",{},"Volume."," 5,000–7,500 high-resolution print-quality images per week,\ncumulative storage over 1TB.",[97,105,106,109],{},[100,107,108],{},"Window."," All 25 branches finalized design on Thursday\u002FFriday and had\nto be fully digitized and live by Sunday — a 48-hour weekend ingestion\nwindow with zero tolerance for missing pages.",[12,111,112,113],{},"Manual upload was a non-starter from day one. The entire system was built\naround the principle: ",[100,114,115],{},"keep humans away from the servers.",[16,117,119],{"id":118},"the-automation-pipeline","The automation pipeline",[12,121,122],{},"Four key design decisions formed the backbone:",[124,125,126,132,138,144],"ol",{},[97,127,128,131],{},[100,129,130],{},"Directory structure as protocol."," Operations organized files into a\nstandardized hierarchy (province \u002F issue \u002F page); the import tool walked\nthe tree and wrote metadata directly to the database. Convention over\nconfiguration, before I had the vocabulary for it.",[97,133,134,137],{},[100,135,136],{},"Files renamed by database primary key."," Physical filenames on disk\nbecame plain integer IDs. This kept business semantics out of the\nstorage layer and let database indexes run cleanly on integer PKs —\na fix that came after performance had already slapped me once.",[97,139,140,143],{},[100,141,142],{},"Distributed storage with pipelined distribution."," Province-sharded\ningestion ran in parallel across a file server cluster. Each pipeline\nstage retried independently; failures didn't roll back entire batches.",[97,145,146,149],{},[100,147,148],{},"Hand-rolled jQuery reader."," Tiered image loading plus CSS-transform-\nbased zoom and pan, supporting the dense pricing tables travel agents\nneeded to read. Built years before mature open-source equivalents\nexisted.",[16,151,153],{"id":152},"why-it-ran-for-ten-years","Why it ran for ten years",[12,155,156],{},"In hindsight, the tech stack wasn't what kept it alive — the architectural\nconstraints were. Filesystem conventions absorbed the most fragile part of\nthe human workflow. Province-level sharding made peak-window load\nhorizontally scalable by default. Read\u002Fwrite path separation meant\nproduction traffic was never at the mercy of the operations-side upload\nsurge.",[12,158,159],{},"Common-sense decisions today. Each one earned the hard way back then.",[16,161,163],{"id":162},"what-id-do-differently","What I'd do differently",[12,165,166],{},"Honest reflections from a decade later:",[94,168,169,175,181,187],{},[97,170,171,174],{},[100,172,173],{},"ASPX became a liability."," Every later integration had to work around\nit. A 2014 rewrite to Web API + decoupled frontend would have made the\nnext five years much easier.",[97,176,177,180],{},[100,178,179],{},"Too much built in-house."," Especially the frontend gesture interaction\nlayer — mature alternatives existed by 2013, but \"it works\" kept us from\nrevisiting it.",[97,182,183,186],{},[100,184,185],{},"Vertical scaling on SQL Server strained hard by the later years.","\nI underestimated read\u002Fwrite separation and sharding approaches and\nmissed the best refactor window.",[97,188,189,192],{},[100,190,191],{},"Monitoring was effectively non-existent."," Issues were often reported\nby operations staff calling in. Unacceptable today.",[16,194,196],{"id":195},"closing-thought","Closing thought",[12,198,199],{},"The platform wound down in 2019. Partly the pandemic, partly something\ndeeper: mobile internet had absorbed the intermediary role this platform\nplayed. DingTalk groups, WeChat groups, and vertical SaaS made a\n\"magazine-online\" platform structurally unnecessary.",[12,201,202],{},"It wasn't defeated. It was outgrown by its era. Something served the ten\nyears it was built to serve, witnessed an industry's transformation, and\nexited with dignity — already a better ending than most engineers can\nexpect.",[12,204,205],{},"For the full retrospective with technical details, see the linked blog post.",{"title":29,"searchDepth":30,"depth":30,"links":207},[208,209,210,211,212,213],{"id":81,"depth":30,"text":82},{"id":88,"depth":30,"text":89},{"id":118,"depth":30,"text":119},{"id":152,"depth":30,"text":153},{"id":162,"depth":30,"text":163},{"id":195,"depth":30,"text":196},"Nationwide travel agency network · 25 provincial branches","https:\u002F\u002Fimages.xtop.dev\u002Fprojects\u002F2026\u002F04\u002Febook-index.jpg",[215],"Designed and built an automated import and distribution pipeline that ingested 5,000–7,500 high-resolution magazine pages every weekend within a 48-hour window. Ran in production for 10 years (2009–2019), serving travel agencies across China before the industry shifted to mobile.",{},"Ran stably for 10 years under high-load weekly publishing cycles. The 48-\nhour ingestion window consistently absorbed 5,000–7,500 high-res images\nacross 25 branches; cumulative storage exceeded 1TB. The automation\npipeline collapsed what would have required dozens of operations staff\nper branch into a standardized programmatic workflow. The platform was\nthe primary nationwide channel for B2B travel pricing until it was\nretired in 2019, as mobile internet and vertical SaaS absorbed the\nintermediary role it had served.\n","\u002Fprojects\u002Ftravel-dm-platform","Before mobile internet took hold, travel agencies across China relied on\nweekly printed DM (direct-mail) magazines to share pricing and itineraries.\nThe format had two structural problems: poor timeliness (prices often\nshifted before the magazine arrived), and information silos (no agency\ncould see nationwide pricing at once). The challenge was further compounded\nby extreme operational scale — 25 provincial branches publishing 200–300\npages each, all needing to go live within a 48-hour weekend window.\n","Full-Stack Developer (solo, end-to-end ownership)",[215,224,225,226],"https:\u002F\u002Fimages.xtop.dev\u002Fprojects\u002F2026\u002F04\u002Febook-ebook.jpg","https:\u002F\u002Fimages.xtop.dev\u002Fprojects\u002F2026\u002F04\u002Febook-search.jpg","https:\u002F\u002Fimages.xtop.dev\u002Fprojects\u002F2026\u002F04\u002Febook-details.jpg",{"title":72,"description":217},"projects\u002Ftravel-dm-platform","A decade-long B2B platform that digitized weekly travel magazines for 25 provincial branches.",[58,59,231,232,233],"automation","legacy-systems","retrospective",[235,236,237,238,239],".NET (ASPX)","SQL Server","jQuery","In-house Automation Tooling","Distributed File Storage","2009–2019","esUA1FVKY1sWS3uhjtbIC9U3fJog-slUOpsCX4bUl-c",1776954008071]