docs: add comprehensive project guide

This commit is contained in:
Wyndham ARR committed 2026-09-06 13:15:29 +08:00
1 parent 2f5bd13b4d
commit fb62afb7d8
2 files changed
+244 -56

No files matched your search

+242 -55
View File
@@ -1,83 +1,270 @@
# Condo Owner Desk Prototype
# CONDO Owner Desk
Static local frontend for annual owner stay privilege management. The primary navigation contains Dashboard and Owner Accounts; account data and linked Usage history are managed together inside Owner Accounts.
CONDO Owner Desk 是一套面向酒店公寓运营团队的业主年度住宿权益管理系统。它把业主账户、房型规则、入住使用记录和年度剩余权益集中在一个工作台中,并由后端统一计算房晚、扣减倍率和余额,避免浏览器直接修改权威数据。
Use the `EN / 中文 / ไทย` control at the bottom-left of the sidebar (or the matching control on the login page) to switch the complete interface in place. English is restored when the page is refreshed. Thai dates use Thai month/day names with the Gregorian year and Latin operational digits.
当前版本由原生 Web 前端、TypeScript/Fastify API 和 PostgreSQL 数据层组成,可通过 Docker 部署,也可使用 Mock API 或本地历史审计快照进行演示。
## Sign in
## 主要功能
The workspace now requires the backend-authenticated operator login:
- 受保护的运营人员登录、12 小时 HttpOnly 会话和主动退出。
- Dashboard 展示业主房间数、剩余权益、已用权益、房型分布与月度使用情况。
- 按编号、业主、房号、单元号、会员号和房型查询业主账户。
- 查询、筛选和分页浏览使用记录及业主详情。
- 新增使用记录时预览扣减结果;Night、倍率、Use 和 Balance 最终由后端计算。
- 删除使用记录时通过事务恢复账户余额。
- 英语、简体中文和泰语界面切换。
- 支持 PostgreSQL 持久化模式、完整历史快照预览模式和内置原型数据模式。
- Username: `wyndhamcondon`
- Password: `wyndhamcondon`
## 系统架构
The browser never stores the password. A successful login creates a 12-hour, HttpOnly session cookie; refresh keeps the session and Sign out invalidates it immediately. The local defaults can be overridden with backend authentication environment variables.
```mermaid
flowchart LR
Browser[浏览器<br/>HTML / CSS / JavaScript]
Nginx[Nginx<br/>静态资源与 /api 反向代理]
API[Fastify API<br/>认证、校验、业务事务]
DB[(PostgreSQL<br/>booking_test / condon)]
## Run locally
For a login-protected preview with the complete verified historical import, start the Fastify snapshot backend first:
```bash
cd backend
npm run build
npm run preview:history
Browser --> Nginx
Nginx --> API
API --> DB
```
This validates and loads the retained `legacy-016dc36d15cc40a5` import snapshot in memory: 388 owner accounts, 157 usage records, 438 privilege nights used in 2026 and 5,394 remaining. It uses the same backend login/session and API contract as PostgreSQL, but does not require or modify the remote database. Preview writes are memory-only and reset when this backend process stops.
- 前端没有打包步骤,可直接由任意静态 Web 服务器托管。
- Docker 模式下,浏览器只访问 Nginx;`/api/*` 被同源代理到后端,后端端口不对外发布。
- PostgreSQL 不包含在 `docker-compose.yml` 中,后端连接现有的 `booking_test` 数据库,并且只使用隔离的 `condon` schema。
- 浏览器不接触数据库凭据,也不直接连接 PostgreSQL。
In a second terminal, serve the frontend:
## 快速体验
```bash
python3 -m http.server 4173 --bind 127.0.0.1
```
### 前置条件
Then open <http://127.0.0.1:4173>.
- Node.js 24 或更高版本。
- Python 3(仅用于下面的本地静态文件服务;也可换成其他静态服务器)。
Authentication always requires a backend. Use the verified snapshot command above for the complete historical preview, or start the PostgreSQL backend with its runtime database secrets for persistent live data.
### 使用内置原型数据
The default URL uses the backend API and loads the imported owner accounts and usage history after sign-in. To use the local prototype dataset after authentication, open `http://127.0.0.1:4173/?mode=demo`. Imported usage history keeps its original source-row order. Records added in the interface appear above imported history in newest-entry-first order, independent of Check-in or Check-out dates.
这是全新克隆仓库后最容易复现的方式,不需要 PostgreSQL,也不会写入真实数据。
Demo-mode records and snapshot-preview writes are stored in memory. A page refresh keeps them while their serving process remains active, but restarting the relevant process restores the original dataset. PostgreSQL-backed changes remain persistent. Annual entitlement information remains on the Dashboard; there is no separate Annual Report page.
## API integration mode
Start the backend on `127.0.0.1:3000`, start the frontend on `127.0.0.1:4173`, then open the default URL:
<http://127.0.0.1:4173>
This mode:
- verifies the operator session before loading or rendering the workspace;
- includes the HttpOnly session cookie on every API request and returns to login when the session expires;
- loads health, room types, all paginated owner accounts, all paginated usage records and Dashboard aggregates from the API;
- keeps UUIDs as strings and safely renders nullable account numbers, transfer dates and annual balances;
- shows explicit loading, connected, empty and error states with a Retry action;
- never falls back to the 190 demo accounts when the API fails;
- sends only owner, confirmation, dates, used room type, optional AC2 multiplier, remark and an idempotency key when creating usage; Night, Use and Balance remain server-derived.
The API base URL and default mode are centralized in `runtime-config.js`. The API hostname follows the frontend hostname so local cookies remain same-site on both `127.0.0.1` and `localhost`. It contains no database or login credentials. Browser code never connects directly to PostgreSQL.
For tiny-fixture frontend debugging without database writes, start the two-account in-memory Mock API:
在第一个终端启动本地认证 Mock API:
```bash
node tests/mock-api-server.mjs
```
Use `node tests/mock-api-server.mjs --empty` to verify the empty-database interface. The Mock is intentionally not the historical preview; it runs only in process memory and is discarded when stopped.
在第二个终端从仓库根目录启动前端:
Run the frontend API-client and localization tests with:
```bash
python3 -m http.server 4173 --bind 127.0.0.1
```
打开 <http://127.0.0.1:4173/?mode=demo>,使用本地开发账号登录:
```text
Username: wyndhamcondon
Password: wyndhamcondon
```
`?mode=demo` 使用前端内置的 190 条原型账户;登录由 Mock API 完成。演示期间新增或删除的数据只保存在当前进程内存中,进程重启后恢复初始状态。
> 默认账号只用于本地开发和演示。任何共享或生产环境都必须通过环境变量更换密码。
## 运行模式
### 1. PostgreSQL 持久化模式
前端默认运行在 API 模式。后端需要可访问且已经应用 `condon` 迁移的 `booking_test` 数据库。
```bash
cd backend
npm ci
cp .env.example .env
```
编辑 `.env` 中的数据库连接和认证信息,然后在 zsh/bash 中加载环境变量并启动后端:
```bash
set -a
source .env
set +a
npm run dev
```
另开终端,在仓库根目录启动前端:
```bash
python3 -m http.server 4173 --bind 127.0.0.1
```
打开 <http://127.0.0.1:4173>。前端会连接当前主机的 `3000` 端口,并在认证成功后加载数据库中的业主、使用记录和 Dashboard 聚合数据。
数据库迁移、导入和只读部署验证需要额外的运维步骤,详见 [后端说明](./backend/README.md) 和 [Docker 部署说明](./DOCKER_DEPLOYMENT.md)。
### 2. 完整历史快照预览
历史预览不连接 PostgreSQL,提供与正式 API 相同的认证和业务接口。但预览所需的审计 JSON 含业务数据,位于 Git 忽略目录,**不会随仓库克隆或推送**。
只有已安全取得该批次材料时才可使用此模式。默认目录为:
```text
.planning/data_import_audit/import_batch_2026_07_31
```
也可以通过 `CONDO_IMPORT_SNAPSHOT_DIR` 指向外部安全目录。准备好材料后执行:
```bash
cd backend
npm ci
npm run build
npm run preview:history:check
npm run preview:history
```
再从仓库根目录启动 `4173` 端口的前端并打开默认 URL。快照写入只存在内存中,预览进程停止后即丢失。
当前脚本校验的历史基线为:388 个业主账户、157 条使用记录、2026 年已使用 438 个权益晚、剩余 5,394 个权益晚。
### 3. Docker 部署
复制生产环境示例文件:
```bash
cp deploy/.env.production.example .env
```
至少填写 `DB_HOST`、`DB_USER`、`DB_PASSWORD` 和 `AUTH_PASSWORD`。如果不是通过 `http://localhost:8080` 访问,还要把 `CORS_ORIGINS` 改为浏览器实际访问的完整 Origin;HTTPS 环境应设置 `AUTH_COOKIE_SECURE=true`。
```bash
docker compose build
docker compose up -d
```
默认访问地址为 <http://localhost:8080>。停止服务:
```bash
docker compose down
```
部署拓扑、迁移命令和连接输入格式见 [Docker 部署说明](./DOCKER_DEPLOYMENT.md)。
## 核心业务规则
每个业主房号独立拥有年度权益期间,当前默认年度新增为 15 晚,可包含上一年度结转。一次使用的权威计算为:
```text
Night = Check-out - Check-in
Multiplier = max(1, Used Tier - Purchased Tier + 1)
Use = Night × Multiplier
Balance After = Balance Before - Use
```
房型分级如下:
| Tier | 房型 |
| --- | --- |
| 1 | RM1、RM2、RM3、RM4、UG1、UG2 |
| 2 | SU1、SU2、SU6 |
| 3 | SU3 |
AC2 不进入自动分级;购买房型或使用房型涉及 AC2 时,运营人员必须选择 1–3 的人工倍率,后端再次校验。
使用记录创建和删除、权益流水及当前余额在 PostgreSQL 事务中同步更新。系统拒绝余额不足、日期无效、跨权益年度、房型规则不符和幂等键冲突等请求。完整表结构和规则见 [后端数据模型](./backend-data-model.md)。
## API 概览
除三个 `/auth/*` 接口和 CORS 预检外,其他接口(包括 `/docs`)都要求有效的 `condon_session` Cookie。
| Method | Path | 说明 |
| --- | --- | --- |
| POST | `/auth/login` | 登录并创建会话 |
| GET | `/auth/session` | 查询当前会话 |
| POST | `/auth/logout` | 注销当前会话 |
| GET | `/health` | 检查数据库、schema 和迁移版本 |
| GET | `/room-types` | 获取房型与倍率规则元数据 |
| GET | `/owner-accounts` | 搜索和分页查询业主账户 |
| GET | `/owner-accounts/:id` | 查询业主账户详情与年度余额 |
| GET | `/usage-records` | 查询使用记录 |
| POST | `/usage-records` | 事务性新增使用记录并扣减余额 |
| DELETE | `/usage-records/:id` | 事务性删除使用记录并恢复余额 |
| GET | `/dashboard` | 查询年度 Dashboard 聚合 |
后端运行时可通过 `/docs` 查看 OpenAPI UI。请求字段、返回模型和数据库函数详见 [后端说明](./backend/README.md)。
## 主要配置
后端本地配置模板为 [`backend/.env.example`](./backend/.env.example),Docker 配置模板为 [`deploy/.env.production.example`](./deploy/.env.production.example)。
| 变量 | 默认值/要求 | 用途 |
| --- | --- | --- |
| `API_HOST` | `127.0.0.1` | 后端监听地址 |
| `API_PORT` | `3000` | 后端监听端口 |
| `CORS_ORIGINS` | 本地 `4173` Origin | 允许携带 Cookie 的前端来源,逗号分隔 |
| `AUTH_USERNAME` | 本地默认 `wyndhamcondon` | 运营登录名 |
| `AUTH_PASSWORD` | 生产必须设置 | 运营登录密码 |
| `AUTH_SESSION_TTL_HOURS` | `12` | 会话有效期,允许 1–168 小时 |
| `AUTH_COOKIE_SECURE` | 生产默认 `true` | 是否只通过 HTTPS 发送 Cookie |
| `DB_HOST` / `DB_USER` / `DB_PASSWORD` | 必填 | PostgreSQL 连接信息 |
| `DB_NAME` | 必须为 `booking_test` | 防止误连其他数据库 |
| `DB_SSL` | `false` | 是否启用数据库 SSL |
| `CONDO_API_BASE_URL` | Docker 默认 `/api` | 前端 API 根地址 |
| `CONDO_DEFAULT_MODE` | `api` | 前端默认数据模式 |
| `CONDO_PERIOD_YEAR` | 当前 UTC 年 | Dashboard 与账户权益年度 |
应用不会自动读取 `.env` 文件;本地运行需要先把其中的值导出到当前 shell。不要提交真实环境文件或凭据。
## 目录结构
```text
.
├── index.html / styles.css / app.js # 原生前端界面与交互
├── api-client.js / runtime-config.js # API 客户端与前端运行时配置
├── owners-data.js # demo 模式原型数据
├── backend/
│ ├── src/ # Fastify API、认证、仓储和配置
│ ├── migrations/ # condon schema SQL 迁移
│ ├── scripts/ # 迁移、导入、冒烟和部署验证工具
│ └── tests/ # 后端单元与仓储测试
├── tests/ # 前端 Node 测试和 Mock API
├── deploy/ # Nginx 与 Docker 运行时配置
├── docker-compose.yml # 前后端容器编排
├── backend-data-model.md # 数据模型与业务规则规格
└── DOCKER_DEPLOYMENT.md # Docker 运维说明
```
`业主账户.md` 和 `使用记录.md` 是历史业务资料摘要,含个人与运营数据。共享、复制或导出前请遵循适用的数据保护要求。
## 开发与验证
前端测试:
```bash
node --test tests/*.test.mjs
```
## Backend foundation
后端检查:
The isolated PostgreSQL database layer and TypeScript/Fastify API are implemented under [backend](./backend/README.md).
```bash
cd backend
npm ci
npm run typecheck
npm test
npm run build
npm run db:check-migrations
```
- The existing database remains `booking_test`.
- New database objects live only in the `condon` schema.
- The current latest workbook import has been applied and verified in the isolated `condon` schema.
- The frontend uses API integration by default; `?mode=demo` is an explicit static-prototype override.
- Login, session validation and logout are owned by the backend; all health, documentation and business endpoints require an authenticated session.
- The latest verified import contains 388 owner accounts and 157 usage records.
需要真实数据库的命令包括 `db:inventory`、`db:migrate:up`、`db:verify`、`db:verify-import` 和 `db:test-integration`。其中 `db:test-integration` 只允许针对空的测试 schema 执行,不能用于已导入业务数据的环境。
## 安全与当前边界
- 生产环境必须更换默认登录凭据,并通过 Secret Manager 或运行时环境变量注入数据库密码。
- 会话令牌只通过 HttpOnly Cookie 发送,服务端只保存其 SHA-256 键;当前会话存储在后端内存中,后端重启后所有会话失效。
- 后端日志对 Cookie、认证信息和数据库密码做脱敏处理。
- 当前版本是单一运营账号模型,尚未实现多用户身份、角色权限和持久化会话。
- 当前不支持人工余额调整或跨权益年度入住。
- 数据库回滚属于显式运维操作;业务数据导入后不要执行迁移回滚。
## 相关文档
- [后端 API、数据库命令与验证](./backend/README.md)
- [后端数据模型与业务规则](./backend-data-model.md)
- [Docker 部署与数据库运维](./DOCKER_DEPLOYMENT.md)
- [前端设计系统](./design-system/condo-owner-desk/MASTER.md)
+2 -1
View File
@@ -104,6 +104,8 @@ The API listens on `127.0.0.1:3000` by default. The default CORS allowlist accep
`preview:history:check` validates the retained import source hash and the fixed 388 owner / 157 usage / 438 used / 5,394 remaining totals without opening a port. `preview:history` serves that exact snapshot through the normal Fastify authentication and API routes without connecting to PostgreSQL. It is intended for safe local preview when remote database secrets are not present; any new preview records and balance changes are memory-only and reset when the process stops.
The audited snapshot contains business data and is intentionally excluded from Git. It must exist locally at `.planning/data_import_audit/import_batch_2026_07_31`, relative to the repository root, or be supplied through `CONDO_IMPORT_SNAPSHOT_DIR`. A fresh clone cannot run either history-preview command until those files are provided through an approved secure channel.
Database operator commands:
```bash
@@ -139,7 +141,6 @@ Do not run rollback after business data has been imported. Rollback is an explic
## Deliberately deferred
- Importing a newer workbook without a new preflight batch.
- Switching the static frontend to the API.
- Multi-user identities, roles and backend-restart-persistent sessions.
- Manual balance adjustment.
- Cross-entitlement-year stays.