19 KiB
Architecture
Analysis Date: 2026-04-21
Pattern Overview
Overall: MVC (Model-View-Controller) with module-based multi-application architecture, built on ThinkPHP 5.x framework and FastAdmin 1.6.1 admin framework.
Key Characteristics:
- Multi-module structure:
admin(backend management),index(frontend user portal),api(RESTful API),common(shared code) - Trait-based CRUD inheritance: Backend controllers inherit
index/add/edit/del/multi/recyclebin/destroy/restorefromapp\admin\library\traits\Backend - RBAC (Role-Based Access Control): Admin permissions via admin → auth_group → auth_group_access → auth_rule chain; user permissions via user → user_group → user_rule chain
- Addon/plugin architecture: Extensible system via
addons/directory with lifecycle hooks - Dual Auth systems:
app\admin\library\Auth(session-based admin auth) andapp\common\library\Auth(token-based user auth)
Modules
The application is organized into 4 ThinkPHP modules under application/:
Admin Module (application/admin/)
- Purpose: Backend management panel for administrators
- Contains: Admin CRUD controllers, RBAC management, system config, user management, lottery data management (History, Num), command execution interface
- Entry:
public/index.php→ moduleadmin - Controllers: 18 controllers across 4 subdirectories
Index Module (application/index/)
- Purpose: Frontend user-facing website
- Contains: Public homepage, user login/register/profile, ajax endpoints, lottery data scraping
- Controllers:
Index.php(lottery scraping + homepage),User.php(member center),Ajax.php(frontend async operations)
API Module (application/api/)
- Purpose: RESTful API endpoints for mobile/third-party clients
- Contains: User auth, registration, profile, token management, SMS/EMS verification
- Controllers:
Index.php,User.php,Common.php,Demo.php,Ems.php,Sms.php,Token.php,Validate.php
Common Module (application/common/)
- Purpose: Shared code across all modules
- Contains: Base controllers, shared models, libraries (Auth, Upload, Email, Token drivers), exceptions
Controller Inheritance Chain
Backend (Admin Controllers)
think\Controller (ThinkPHP base)
└── app\common\controller\Backend (base backend controller)
└── app\admin\library\traits\Backend (trait: CRUD methods)
└── app\admin\controller\* (all admin controllers)
app\common\controller\Backend (application/common/controller/Backend.php):
- Properties:
$noNeedLogin,$noNeedRight,$layout,$auth,$model,$searchFields,$relationSearch,$dataLimit,$dataLimitField,$dataLimitFieldAutoFill,$modelValidate,$modelSceneValidate,$multiFields,$selectpageFields,$excludeFields,$importHeadType _initialize(): IP check → Auth instance → login verification → permission check → breadcrumb setup → layout → language loading → view config assignmentbuildparams(): Constructs WHERE conditions from GET params (search, filter, op, sort, order, offset, limit) with support for LIKE, IN, BETWEEN, RANGE, FIND_IN_SET, NULL operatorsselectpage(): Universal select/dropdown search with tree supportloadlang(): Loads language file for current controllerassignconfig(): Merges config into viewgetDataLimitAdminIds(): Returns admin IDs for data scoping based on$dataLimitsetting
app\admin\library\traits\Backend (application/admin/library/traits/Backend.php):
index(): List with pagination, JSON response for AJAXadd(): Create with validation, transaction supportedit(): Update with validation, data limit checkdel(): Soft delete (batch supported)recyclebin(): View soft-deleted recordsdestroy(): Permanent delete from recycle binrestore(): Restore from recycle binmulti(): Batch update specified fieldsimport(): Excel/CSV import with PhpSpreadsheet
Frontend (Index Controllers)
think\Controller (ThinkPHP base)
└── app\common\controller\Frontend (base frontend controller)
└── app\index\controller\* (all frontend controllers)
app\common\controller\Frontend (application/common/controller/Frontend.php):
_initialize(): Input filtering → IP check → layout → Auth init → token-based login check → user assignment → site config → language loading- Auth uses
app\common\library\Auth(token-based) loadlang(): Loads language file for current controllerassignconfig(): Merges config into view
API Controllers
(no ThinkPHP Controller base)
└── app\common\controller\Api (base API controller, not extending think\Controller)
└── app\api\controller\* (all API controllers)
app\common\controller\Api (application/common/controller/Api.php):
- Does NOT extend
think\Controller— manual constructor pattern - Properties:
$noNeedLogin,$noNeedRight,$auth,$responseType,$failException,$batchValidate _initialize(): CORS check → IP check → input filtering → token auth → permission check → language loadingsuccess()/error(): Standard API response format{code, msg, time, data}result(): Unified response with HTTP status code mappingvalidate(): Data validation with fail-exception modebeforeAction(): Before-action hook support
RBAC Auth System
Admin RBAC
fa_admin (管理员)
└── fa_auth_group_access (管理员-角色关联, N:N)
└── fa_auth_group (角色组)
└── rules (权限规则ID列表, 逗号分隔)
└── fa_auth_rule (权限规则)
Key classes:
app\admin\library\Authextendsfast\Auth— admin authentication and authorizationapp\admin\model\Admin— admin user modelapp\admin\model\AuthGroup— role group modelapp\admin\model\AuthGroupAccess— admin-role pivot modelapp\admin\model\AuthRule— permission rule modelapp\admin\model\AdminLog— admin operation log model
Auth flow:
- Login:
Admin::login()→ verifies password (MD5 double-hash with salt:md5(md5(password) . salt)) → sets Session + token + keeplogin cookie - Permission check:
Auth::check($path)→ reads user's group rules → checks if controller/action path is in allowed rules - Super admin:
Auth::isSuperAdmin()→ returns true if rule list contains* - Data scoping:
Backend::$dataLimitsupportsauth(group-scoped) andpersonal(user-scoped) data filtering viagetDataLimitAdminIds() - Session management:
safecodevalidation ensures re-login on credential changes; supports single-session mode (fastadmin.login_unique) and IP check mode (fastadmin.loginip_check) - Auto-login:
Auth::autologin()checkskeeplogincookie with time-limited key validation
Key Auth methods:
login($username, $password, $keeptime)— admin loginlogout()— clear session and tokencheck($name, $uid, $relation, $mode)— permission checkmatch($arr)— check if current action matches whitelistisLogin()— session + safecode validationisSuperAdmin()— check for*in rulesgetSidebar($params, $fixedPage)— generate left/top navigation menugetBreadCrumb($path)— breadcrumb navigationgetChildrenAdminIds($withself)— scoped admin IDsgetChildrenGroupIds($withself)— scoped group IDs
User RBAC (Frontend)
fa_user (会员)
└── group_id → fa_user_group (会员组)
└── rules → fa_user_rule (会员权限规则)
Key classes:
app\common\library\Auth— user authentication (token-based, singleton pattern)app\common\model\User— user model with money/score log hooksapp\common\model\UserGroup— user group modelapp\common\model\UserRule— user rule model- Token storage:
app\common\library\Tokenwith MySQL or Redis drivers
Auth flow:
- Token init:
Auth::init($token)→ Token::get() → load user → set_loginedflag - Login:
Auth::login($account, $password)→ finds user by email/mobile/username → verifies password →direct($user_id) - Token management: UUID token stored in Token model (or Redis) with configurable TTL (
keeptime = 2592000= 30 days) - Password encryption:
md5(md5(password) . salt)— same algorithm as admin
Key Auth methods:
instance($options)— singleton getterinit($token)— initialize from tokenlogin($account, $password)— user loginregister($username, $password, $email, $mobile, $extend)— user registrationlogout()— delete tokenchangepwd($newpassword, $oldpassword, $ignoreoldpassword)— change passworddirect($user_id)— direct login (bypass password check)check($path, $module)— permission checkdelete($user_id)— delete user and clear tokens
MVC Patterns
Model Pattern
- All models extend
think\Model - Timestamps:
autoWriteTimestamp = 'int'with custom field names (createtime,updatetime) - Attribute mutators:
getXxxAttr()andsetXxxAttr()for data transformation - Model events:
self::init()withself::beforeWrite,self::afterInserthooks - Relations:
belongsTo,hasManyusing ThinkPHP ORM - Appended attributes:
$append = ['field_text']for computed/display fields - Enum lists:
getStatusList(),getGenderList()for select/radio options - Hidden fields:
$hidden = ['password', 'salt']for sensitive data
View Pattern
- Template engine: ThinkPHP template with
{}syntax - Layout:
application/admin/view/layout/default.htmlincludescommon/meta,common/script,common/header,common/menu - Admin views:
build_heading()generates toolbar/buttons,{:__()}for i18n - View config injection via
$this->view->assign()and$this->assignconfig() - Layout template set via
$this->layoutproperty in controllers - Frontend layout:
application/index/view/layout/default.html
Validation Pattern
- Validators extend
think\Validate - Rules defined in
$rulearray (require, unique, regex, email, length, etc.) - Scenes:
$scene = ['add' => [...], 'edit' => [...]]for context-specific validation - Constructor customization for i18n field labels
- Model-level validation:
Backend::$modelValidateand$modelSceneValidatetoggle automatic validation on add/edit
Addon Architecture
Directory: addons/
Plugin lifecycle:
- Install:
Service::install($name)→ downloads, extracts, runs SQL - Uninstall:
Service::uninstall($name)→ removes files, optionally drops tables - Enable/Disable:
Service::enable($name)/Service::disable($name) - Upgrade:
Service::upgrade($name)→ downloads new version, runs migration - Configure:
get_addon_config($name)/set_addon_fullconfig($name, $config)
Addon structure:
addons/{name}/
├── info.ini # Plugin metadata
├── {Name}.php # Main class extends \think\addons\Addon
├── config.php # Plugin configuration schema
├── config.html # Config view (optional, custom config UI)
├── controller/ # Plugin controllers
├── model/ # Plugin models
├── view/ # Plugin views
└── install.sql # Installation SQL
Admin addon controller (application/admin/controller/Addon.php):
- Manages plugin list, install/uninstall, enable/disable, config, upgrade
- Restricted to super admin for destructive operations (install, uninstall, local, upgrade, authorization, testdata)
- Communicates with FastAdmin marketplace API (
fastadmin.api_url) - Methods:
index(),config($name),install(),uninstall(),state(),local(),upgrade(),testdata(),downloaded(),isbuy(),authorization(),get_table_list()
Admin commands for addons:
application/admin/command/Crud.php— one-click CRUD generation from tableapplication/admin/command/Menu.php— menu generation from controllersapplication/admin/command/Min.php— JS/CSS minificationapplication/admin/command/Api.php— API documentation generationapplication/admin/command/Install.php— installation wizardapplication/admin/command/Addon.php— addon-related stubs and operations
Hook system: ThinkPHP Hook integration
admin_login_after,admin_logout_after,admin_nologin,admin_nopermissionuser_login_successed,user_register_successed,user_logout_successed,user_delete_successedupload_config_init,config_init,wipecache_afterupload_delete,admin_sidebar_beginadmin_login_init
Request Flow
Admin Request
public/index.php
→ define APP_PATH → check install.lock → require thinkphp/start.php
→ ThinkPHP dispatch (module/controller/action)
→ app\admin\controller\{Controller}
→ parent::_initialize() (app\common\controller\Backend)
→ check_ip_allowed()
→ Auth::instance() → login check → permission check
→ loadlang() → view config assignment
→ action method (index/add/edit/del from trait or custom)
→ model operations
→ $this->view->fetch() or json()
Frontend Request
public/index.php
→ ThinkPHP dispatch
→ app\index\controller\{Controller}
→ parent::_initialize() (app\common\controller\Frontend)
→ input filter → Auth init (token-based)
→ loadlang() → view config
→ action method → view->fetch()
API Request
public/index.php
→ ThinkPHP dispatch
→ app\api\controller\{Controller}
→ __construct() → _initialize() (app\common\controller\Api)
→ CORS → IP check → token init → permission check
→ action method → $this->success() / $this->error()
→ result() → JSON response {code, msg, time, data}
Data Flow: Lottery Feature (History, Num)
History Model
- Table:
fa_history(application/admin/model/History.php) - Fields:
id,expect(期号),openTime(开奖时间),num1~num7(7个开奖号码) - No timestamps:
autoWriteTimestamp = false,createTime = false,updateTime = false,deleteTime = false - No validation rules defined in
application/admin/validate/History.php
Num Model
- Table:
fa_num(application/admin/model/Num.php) - Fields:
num,color(波色映射 — wave color mapping for lottery numbers) - No timestamps:
autoWriteTimestamp = false
Data Scraping Flow (application/index/controller/Index.php::get_history())
- User visits
index/index/get_history(no auth required:$noNeedLogin = '*') - Controller creates
\GuzzleHttp\Clientinstance - GET request to
https://history.macaumarksix.com/history/macaujc2/y/2026(Macau lottery history API) - Parses JSON response, extracts
dataarray - For each item: splits
openCodeby comma intonum1~num7 - Checks if
expectalready exists infa_historyviaDb::name('history')->where('expect', $item['expect'])->find() - If new:
Db::name('history')->insert($insert_data); if exists:Db::name('history')->where('expect', $item['expect'])->update($insert_data) - Returns success/failure JSON via
$this->success()/$this->error()
Admin History Management (application/admin/controller/History.php)
- Extends
Backend, uses standard CRUD from trait - Model:
app\admin\model\History - View:
application/admin/view/history/index.html— list only, add/edit buttons hidden in template - No custom methods — relies entirely on inherited CRUD
Admin Num Query (application/admin/controller/Num.php)
- Extends
Backend - Model:
app\admin\model\Num - Custom method:
getColorMap()— returns num→color mapping for frontend display as JSON
Key Abstractions
Auth (Dual System)
app\admin\library\Auth— session-based admin auth extendingfast\Auth(base RBAC class fromfastnamespace)app\common\library\Auth— token-based user auth (singleton), no ThinkPHP Controller dependency
Tree
fast\Tree— hierarchical data structure for menus, categories, rules- Methods:
init(),getTreeList(),getTreeArray(),getChildrenIds(),getParentsIds(),getTreeMenu(),getChildren()
Token Drivers
app\common\library\Token— token storage abstractionapp\common\library\token\driver\Mysql— MySQL-backed token storageapp\common\library\token\driver\Redis— Redis-backed token storage
Upload
app\common\library\Upload— file upload with chunked upload support- Integrates with
app\common\model\Attachmentfor metadata tracking - Exception:
app\common\exception\UploadException
Date Utility
fast\Date— date manipulation utility (e.g.,Date::unixtime('day', -6))
Random Utility
fast\Random— random string/UUID generation (used for salt, tokens)
Entry Points
public/index.php — Main entry point for all modules
- Defines
APP_PATH - Checks
application/admin/command/Install/install.lockfor installation status - Redirects to
install.phpif not installed - Loads
thinkphp/start.phpfor framework bootstrap
think — CLI entry point for ThinkPHP commands
- Used for CRUD generation, menu generation, API docs, minification, addon management
Error Handling
Strategy: Exception-based with user-friendly error pages
Patterns:
$this->error($msg)/$this->success($msg)— controller-level response helperstry/catchwithDb::startTrans()/Db::commit()/Db::rollback()for transaction safetyValidateException— model validation failuresPDOException— database errorsUploadException— upload failuresAddonException— plugin operation failures with structured error dataHttpResponseException— API response termination
Cross-Cutting Concerns
Logging: AdminLog::record() auto-logs admin operations (title, content, URL, IP, user agent); filtered by ignoreRegex to skip selectpage/index actions; AdminLog::setTitle() for custom titles; behavior hook via app\admin\behavior\AdminLog
Validation: ThinkPHP Validate with rule-based validation, scene support, custom messages; model-level and controller-level validation modes
Authentication: Dual auth system — session-based for admin, token-based for users/API; IP allowlist check via check_ip_allowed(); CSRF tokens for forms
Internationalization: Language files per module under lang/zh-cn/ and lang/en/; __() function for translation; controller auto-loads matching lang file in _initialize(); admin loads zh-cn as default
IP Filtering: check_ip_allowed() called in all base controllers — reads fastadmin.ip_blacklist / fastadmin.ip_whitelist config
Caching: File-based cache (ThinkPHP default); menu cache via cache("__menu__"); template caching enabled; addon list cache via Cache::get("onlineaddons")
Architecture analysis: 2026-04-21