381 lines
19 KiB
Markdown
381 lines
19 KiB
Markdown
# 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/restore` from `app\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) and `app\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` → module `admin`
|
|
- 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 assignment
|
|
- `buildparams()`: Constructs WHERE conditions from GET params (search, filter, op, sort, order, offset, limit) with support for LIKE, IN, BETWEEN, RANGE, FIND_IN_SET, NULL operators
|
|
- `selectpage()`: Universal select/dropdown search with tree support
|
|
- `loadlang()`: Loads language file for current controller
|
|
- `assignconfig()`: Merges config into view
|
|
- `getDataLimitAdminIds()`: Returns admin IDs for data scoping based on `$dataLimit` setting
|
|
|
|
**`app\admin\library\traits\Backend`** (`application/admin/library/traits/Backend.php`):
|
|
- `index()`: List with pagination, JSON response for AJAX
|
|
- `add()`: Create with validation, transaction support
|
|
- `edit()`: Update with validation, data limit check
|
|
- `del()`: Soft delete (batch supported)
|
|
- `recyclebin()`: View soft-deleted records
|
|
- `destroy()`: Permanent delete from recycle bin
|
|
- `restore()`: Restore from recycle bin
|
|
- `multi()`: Batch update specified fields
|
|
- `import()`: 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 controller
|
|
- `assignconfig()`: 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 loading
|
|
- `success()` / `error()`: Standard API response format `{code, msg, time, data}`
|
|
- `result()`: Unified response with HTTP status code mapping
|
|
- `validate()`: Data validation with fail-exception mode
|
|
- `beforeAction()`: 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\Auth` extends `fast\Auth` — admin authentication and authorization
|
|
- `app\admin\model\Admin` — admin user model
|
|
- `app\admin\model\AuthGroup` — role group model
|
|
- `app\admin\model\AuthGroupAccess` — admin-role pivot model
|
|
- `app\admin\model\AuthRule` — permission rule model
|
|
- `app\admin\model\AdminLog` — admin operation log model
|
|
|
|
**Auth flow:**
|
|
1. Login: `Admin::login()` → verifies password (MD5 double-hash with salt: `md5(md5(password) . salt)`) → sets Session + token + keeplogin cookie
|
|
2. Permission check: `Auth::check($path)` → reads user's group rules → checks if controller/action path is in allowed rules
|
|
3. Super admin: `Auth::isSuperAdmin()` → returns true if rule list contains `*`
|
|
4. Data scoping: `Backend::$dataLimit` supports `auth` (group-scoped) and `personal` (user-scoped) data filtering via `getDataLimitAdminIds()`
|
|
5. Session management: `safecode` validation ensures re-login on credential changes; supports single-session mode (`fastadmin.login_unique`) and IP check mode (`fastadmin.loginip_check`)
|
|
6. Auto-login: `Auth::autologin()` checks `keeplogin` cookie with time-limited key validation
|
|
|
|
**Key Auth methods:**
|
|
- `login($username, $password, $keeptime)` — admin login
|
|
- `logout()` — clear session and token
|
|
- `check($name, $uid, $relation, $mode)` — permission check
|
|
- `match($arr)` — check if current action matches whitelist
|
|
- `isLogin()` — session + safecode validation
|
|
- `isSuperAdmin()` — check for `*` in rules
|
|
- `getSidebar($params, $fixedPage)` — generate left/top navigation menu
|
|
- `getBreadCrumb($path)` — breadcrumb navigation
|
|
- `getChildrenAdminIds($withself)` — scoped admin IDs
|
|
- `getChildrenGroupIds($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 hooks
|
|
- `app\common\model\UserGroup` — user group model
|
|
- `app\common\model\UserRule` — user rule model
|
|
- Token storage: `app\common\library\Token` with MySQL or Redis drivers
|
|
|
|
**Auth flow:**
|
|
1. Token init: `Auth::init($token)` → Token::get() → load user → set `_logined` flag
|
|
2. Login: `Auth::login($account, $password)` → finds user by email/mobile/username → verifies password → `direct($user_id)`
|
|
3. Token management: UUID token stored in Token model (or Redis) with configurable TTL (`keeptime = 2592000` = 30 days)
|
|
4. Password encryption: `md5(md5(password) . salt)` — same algorithm as admin
|
|
|
|
**Key Auth methods:**
|
|
- `instance($options)` — singleton getter
|
|
- `init($token)` — initialize from token
|
|
- `login($account, $password)` — user login
|
|
- `register($username, $password, $email, $mobile, $extend)` — user registration
|
|
- `logout()` — delete token
|
|
- `changepwd($newpassword, $oldpassword, $ignoreoldpassword)` — change password
|
|
- `direct($user_id)` — direct login (bypass password check)
|
|
- `check($path, $module)` — permission check
|
|
- `delete($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()` and `setXxxAttr()` for data transformation
|
|
- Model events: `self::init()` with `self::beforeWrite`, `self::afterInsert` hooks
|
|
- Relations: `belongsTo`, `hasMany` using 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.html` includes `common/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->layout` property in controllers
|
|
- Frontend layout: `application/index/view/layout/default.html`
|
|
|
|
### Validation Pattern
|
|
- Validators extend `think\Validate`
|
|
- Rules defined in `$rule` array (require, unique, regex, email, length, etc.)
|
|
- Scenes: `$scene = ['add' => [...], 'edit' => [...]]` for context-specific validation
|
|
- Constructor customization for i18n field labels
|
|
- Model-level validation: `Backend::$modelValidate` and `$modelSceneValidate` toggle 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 table
|
|
- `application/admin/command/Menu.php` — menu generation from controllers
|
|
- `application/admin/command/Min.php` — JS/CSS minification
|
|
- `application/admin/command/Api.php` — API documentation generation
|
|
- `application/admin/command/Install.php` — installation wizard
|
|
- `application/admin/command/Addon.php` — addon-related stubs and operations
|
|
|
|
**Hook system:** ThinkPHP Hook integration
|
|
- `admin_login_after`, `admin_logout_after`, `admin_nologin`, `admin_nopermission`
|
|
- `user_login_successed`, `user_register_successed`, `user_logout_successed`, `user_delete_successed`
|
|
- `upload_config_init`, `config_init`, `wipecache_after`
|
|
- `upload_delete`, `admin_sidebar_begin`
|
|
- `admin_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()`)
|
|
1. User visits `index/index/get_history` (no auth required: `$noNeedLogin = '*'`)
|
|
2. Controller creates `\GuzzleHttp\Client` instance
|
|
3. GET request to `https://history.macaumarksix.com/history/macaujc2/y/2026` (Macau lottery history API)
|
|
4. Parses JSON response, extracts `data` array
|
|
5. For each item: splits `openCode` by comma into `num1`~`num7`
|
|
6. Checks if `expect` already exists in `fa_history` via `Db::name('history')->where('expect', $item['expect'])->find()`
|
|
7. If new: `Db::name('history')->insert($insert_data)`; if exists: `Db::name('history')->where('expect', $item['expect'])->update($insert_data)`
|
|
8. 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 extending `fast\Auth` (base RBAC class from `fast` namespace)
|
|
- `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 abstraction
|
|
- `app\common\library\token\driver\Mysql` — MySQL-backed token storage
|
|
- `app\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\Attachment` for 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.lock` for installation status
|
|
- Redirects to `install.php` if not installed
|
|
- Loads `thinkphp/start.php` for 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 helpers
|
|
- `try/catch` with `Db::startTrans()` / `Db::commit()` / `Db::rollback()` for transaction safety
|
|
- `ValidateException` — model validation failures
|
|
- `PDOException` — database errors
|
|
- `UploadException` — upload failures
|
|
- `AddonException` — plugin operation failures with structured error data
|
|
- `HttpResponseException` — 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*
|