docs: 初始化 Docs 仓库

This commit is contained in:
2026-01-29 01:58:15 +00:00
commit 88ad71041b
37 changed files with 25416 additions and 0 deletions

15
deploy/dbhub/dbhub.toml Normal file
View File

@@ -0,0 +1,15 @@
[[sources]]
id = "takeout_app"
dsn = "postgres://app_user:AppUser112233@120.53.222.17:5432/takeout_app_db"
[[sources]]
id = "takeout_identity"
dsn = "postgres://identity_user:IdentityUser112233@120.53.222.17:5432/takeout_identity_db"
[[sources]]
id = "takeout_dictionary"
dsn = "postgres://dictionary_user:DictionaryUser112233@120.53.222.17:5432/takeout_dictionary_db"
[[sources]]
id = "takeout_hangfire"
dsn = "postgres://hangfire_user:HangFire112233@120.53.222.17:5432/takeout_hangfire_db"

47
deploy/postgres/README.md Normal file
View File

@@ -0,0 +1,47 @@
# PostgreSQL 部署脚本
本目录提供在测试/预发布环境快速拉起 PostgreSQL 的脚本,复用线上同名数据库与账号,方便迁移/恢复。
## 目录结构
- `create_databases.sql`:创建四个业务库与对应角色(可多次执行,存在则跳过)。
- `bootstrap.ps1`PowerShell 包装脚本,调用 `psql` 执行 SQL。
## 前置条件
1. 已安装 PostgreSQL 12+,并能以管理员身份访问(默认使用 `postgres`)。
2. 本地已配置 `psql` 可执行命令。
## 使用方法
```powershell
cd deploy/postgres
.\bootstrap.ps1 `
-Host 120.53.222.17 `
-Port 5432 `
-AdminUser postgres `
-AdminPassword "超级管理员密码"
```
脚本会:
1. 创建/更新以下角色与库:
- `app_user` / `takeout_app_db`
- `identity_user` / `takeout_identity_db`
- `dictionary_user` / `takeout_dictionary_db`
- `hangfire_user` / `takeout_hangfire_db`
2. 为库设置 COMMENT授予 Schema `public` 的 CRUD 权限。
3. 输出执行日志,失败时终止。
## 自定义
- 如需修改密码或新增库,编辑 `create_databases.sql` 后重新运行脚本。
- 若在本地拉起测试库,可把 `Host` 指向 `localhost`,其余参数保持一致。
## 常见问题
| 问题 | 处理方式 |
| --- | --- |
| `psql : command not found` | 确认 PostgreSQL bin 目录已加入 PATH。 |
| `permission denied to create database` | 改用具有 `CREATEDB` 权限的管理员执行脚本。 |
| 需要删除库 | 先 `DROP DATABASE xxx`,再运行脚本重新创建。 |

View File

@@ -0,0 +1,37 @@
param(
[string]$Host = "120.53.222.17",
[int]$Port = 5432,
[string]$AdminUser = "postgres",
[string]$AdminPassword = ""
)
if (-not (Get-Command psql -ErrorAction SilentlyContinue)) {
throw "psql command not found. Add PostgreSQL bin directory to PATH."
}
if ([string]::IsNullOrWhiteSpace($AdminPassword)) {
Write-Warning "AdminPassword not provided. You will be prompted by psql."
}
$sqlPath = Join-Path $PSScriptRoot "create_databases.sql"
if (-not (Test-Path $sqlPath)) {
throw "Cannot find create_databases.sql under $PSScriptRoot."
}
$env:PGPASSWORD = $AdminPassword
$arguments = @(
"-h", $Host,
"-p", $Port,
"-U", $AdminUser,
"-f", $sqlPath
)
Write-Host "Executing create_databases.sql on $Host:$Port as $AdminUser ..."
& psql @arguments
if ($LASTEXITCODE -ne 0) {
throw "psql returned non-zero exit code ($LASTEXITCODE)."
}
Write-Host "PostgreSQL databases and roles ensured successfully."

View File

@@ -0,0 +1,102 @@
-- Reusable provisioning script for Takeout SaaS PostgreSQL databases.
-- Execute with a superuser (e.g. postgres). Safe to re-run.
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'app_user') THEN
CREATE ROLE app_user LOGIN PASSWORD 'AppUser112233';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'identity_user') THEN
CREATE ROLE identity_user LOGIN PASSWORD 'IdentityUser112233';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'dictionary_user') THEN
CREATE ROLE dictionary_user LOGIN PASSWORD 'DictionaryUser112233';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'hangfire_user') THEN
CREATE ROLE hangfire_user LOGIN PASSWORD 'HangFire112233';
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'logs_user') THEN
CREATE ROLE logs_user LOGIN PASSWORD 'Logs112233';
END IF;
END $$;
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_app_db') THEN
CREATE DATABASE takeout_app_db OWNER app_user ENCODING 'UTF8';
END IF;
END $$;
COMMENT ON DATABASE takeout_app_db IS 'Takeout SaaS 业务域数据库';
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_identity_db') THEN
CREATE DATABASE takeout_identity_db OWNER identity_user ENCODING 'UTF8';
END IF;
END $$;
COMMENT ON DATABASE takeout_identity_db IS 'Takeout SaaS 身份域数据库';
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_dictionary_db') THEN
CREATE DATABASE takeout_dictionary_db OWNER dictionary_user ENCODING 'UTF8';
END IF;
END $$;
COMMENT ON DATABASE takeout_dictionary_db IS 'Takeout SaaS 字典域数据库';
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_hangfire_db') THEN
CREATE DATABASE takeout_hangfire_db OWNER hangfire_user ENCODING 'UTF8';
END IF;
END $$;
COMMENT ON DATABASE takeout_hangfire_db IS 'Takeout SaaS 调度/Hangfire 数据库';
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'takeout_logs_db') THEN
CREATE DATABASE takeout_logs_db OWNER logs_user ENCODING 'UTF8';
END IF;
END $$;
COMMENT ON DATABASE takeout_logs_db IS 'Takeout SaaS 审计/日志数据库';
-- Ensure privileges and default schema permissions
\connect takeout_app_db
GRANT CONNECT, TEMP ON DATABASE takeout_app_db TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO app_user;
\connect takeout_identity_db
GRANT CONNECT, TEMP ON DATABASE takeout_identity_db TO identity_user;
GRANT USAGE ON SCHEMA public TO identity_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO identity_user;
GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO identity_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO identity_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO identity_user;
\connect takeout_dictionary_db
GRANT CONNECT, TEMP ON DATABASE takeout_dictionary_db TO dictionary_user;
GRANT USAGE ON SCHEMA public TO dictionary_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO dictionary_user;
GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO dictionary_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO dictionary_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO dictionary_user;
\connect takeout_hangfire_db
GRANT CONNECT, TEMP ON DATABASE takeout_hangfire_db TO hangfire_user;
GRANT USAGE ON SCHEMA public TO hangfire_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO hangfire_user;
GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO hangfire_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO hangfire_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO hangfire_user;
\connect takeout_logs_db
GRANT CONNECT, TEMP ON DATABASE takeout_logs_db TO logs_user;
GRANT USAGE ON SCHEMA public TO logs_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO logs_user;
GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO logs_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO logs_user;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO logs_user;

View File

@@ -0,0 +1,89 @@
-- 日志库迁移脚本(请在 psql 中按步骤执行)
-- 1. 在日志库创建表结构takeout_logs_db
\connect takeout_logs_db
CREATE TABLE IF NOT EXISTS tenant_audit_logs (
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
"TenantId" bigint NOT NULL,
"Action" integer NOT NULL,
"Title" character varying(128) NOT NULL,
"Description" character varying(1024),
"OperatorId" bigint,
"OperatorName" character varying(64),
"PreviousStatus" integer,
"CurrentStatus" integer,
"CreatedAt" timestamp with time zone NOT NULL,
"UpdatedAt" timestamp with time zone,
"DeletedAt" timestamp with time zone,
"CreatedBy" bigint,
"UpdatedBy" bigint,
"DeletedBy" bigint
);
CREATE INDEX IF NOT EXISTS "IX_tenant_audit_logs_TenantId" ON tenant_audit_logs ("TenantId");
CREATE TABLE IF NOT EXISTS merchant_audit_logs (
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
"MerchantId" bigint NOT NULL,
"Action" integer NOT NULL,
"Title" character varying(128) NOT NULL,
"Description" character varying(1024),
"OperatorId" bigint,
"OperatorName" character varying(64),
"CreatedAt" timestamp with time zone NOT NULL,
"UpdatedAt" timestamp with time zone,
"DeletedAt" timestamp with time zone,
"CreatedBy" bigint,
"UpdatedBy" bigint,
"DeletedBy" bigint,
"TenantId" bigint NOT NULL
);
CREATE INDEX IF NOT EXISTS "IX_merchant_audit_logs_TenantId_MerchantId" ON merchant_audit_logs ("TenantId", "MerchantId");
CREATE TABLE IF NOT EXISTS operation_logs (
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
"OperationType" character varying(64) NOT NULL,
"TargetType" character varying(64) NOT NULL,
"TargetIds" text,
"OperatorId" character varying(64),
"OperatorName" character varying(128),
"Parameters" text,
"Result" text,
"Success" boolean NOT NULL,
"CreatedAt" timestamp with time zone NOT NULL,
"UpdatedAt" timestamp with time zone,
"DeletedAt" timestamp with time zone,
"CreatedBy" bigint,
"UpdatedBy" bigint,
"DeletedBy" bigint
);
CREATE INDEX IF NOT EXISTS "IX_operation_logs_CreatedAt" ON operation_logs ("CreatedAt");
CREATE INDEX IF NOT EXISTS "IX_operation_logs_OperationType_CreatedAt" ON operation_logs ("OperationType", "CreatedAt");
CREATE TABLE IF NOT EXISTS member_growth_logs (
"Id" bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
"MemberId" bigint NOT NULL,
"ChangeValue" integer NOT NULL,
"CurrentValue" integer NOT NULL,
"Notes" character varying(256),
"OccurredAt" timestamp with time zone NOT NULL,
"CreatedAt" timestamp with time zone NOT NULL,
"UpdatedAt" timestamp with time zone,
"DeletedAt" timestamp with time zone,
"CreatedBy" bigint,
"UpdatedBy" bigint,
"DeletedBy" bigint,
"TenantId" bigint NOT NULL
);
CREATE INDEX IF NOT EXISTS "IX_member_growth_logs_TenantId_MemberId_OccurredAt" ON member_growth_logs ("TenantId", "MemberId", "OccurredAt");
-- 2. 迁移数据(建议使用 pg_dump/pg_restore 或应用侧批量拷贝)
-- 示例pg_dump -t tenant_audit_logs -t merchant_audit_logs -t operation_logs -t member_growth_logs takeout_app_db > logs_dump.sql
-- psql -d takeout_logs_db -f logs_dump.sql
-- 3. 在业务库删除旧日志表takeout_app_db
\connect takeout_app_db
DROP TABLE IF EXISTS tenant_audit_logs;
DROP TABLE IF EXISTS merchant_audit_logs;
DROP TABLE IF EXISTS operation_logs;
DROP TABLE IF EXISTS member_growth_logs;

View File

@@ -0,0 +1,34 @@
groups:
- name: takeoutsaas-app
interval: 30s
rules:
- alert: HighErrorRate
expr: |
sum(rate(http_server_request_duration_seconds_count{http_response_status_code=~"5.."}[5m]))
/ sum(rate(http_server_request_duration_seconds_count[5m])) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "API 5xx 错误率过高"
description: "过去 5 分钟 5xx 占比超过 5%,请检查依赖或发布"
- alert: HighP95Latency
expr: |
histogram_quantile(0.95, sum(rate(http_server_request_duration_seconds_bucket[5m])) by (le, service_name))
> 1
for: 5m
labels:
severity: warning
annotations:
summary: "API P95 延迟过高"
description: "过去 5 分钟 P95 超过 1s请排查热点接口或依赖"
- alert: InstanceDown
expr: up{job=~"admin-api|mini-api|user-api"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "实例不可达"
description: "Prometheus 抓取失败,实例处于 down 状态"

View File

@@ -0,0 +1,28 @@
global:
scrape_interval: 15s
evaluation_interval: 30s
rule_files:
- alert.rules.yml
scrape_configs:
- job_name: admin-api
metrics_path: /metrics
static_configs:
- targets: ["admin-api:8080"]
labels:
service: admin-api
- job_name: mini-api
metrics_path: /metrics
static_configs:
- targets: ["mini-api:8080"]
labels:
service: mini-api
- job_name: user-api
metrics_path: /metrics
static_configs:
- targets: ["user-api:8080"]
labels:
service: user-api

34
deploy/redis/README.md Normal file
View File

@@ -0,0 +1,34 @@
# Redis 部署脚本
本目录提供可复用的 Redis 配置,既可在本地通过 Docker Compose 启动,也可将 `redis.conf` 拷贝到现有服务器,确保与线上一致。
## 1. 部署步骤 (裸机)\n\n1. 将 \\
edis.conf\\ 拷贝到服务器(例如 /etc/redis/redis.conf。\n2. 根据需要修改数据目录(\\dir\\)和绑定地址。\n3. 使用系统服务或 \\
edis-server redis.conf\\ 启动。\n4. 确认开放端口 6379保证通过 \\
edis-cli -h <host> -a <pwd> ping\\ 可访问。\n\n## 2. 配置说明\n\n- \\
equirepass\\ 已设置为 MsuMshk112233。\n- 启用 appendonlyAOF并每秒 fsync。\n- \\maxmemory-policy\\ 为 allkeys-lru适合缓存场景。\n- \\protected-mode no\\ 允许远程连接,需结合安全组或防火墙限制来源 IP。\n\n## 3. 常用命令使用 `redis.conf`
1.`redis.conf` 拷贝到服务器 `/etc/redis/redis.conf`(或自定义目录)。
2. 修改 `dir` 指向实际数据目录。
3. 使用系统服务或 `redis-server redis.conf` 启动。
关键配置已包含:
- `requirepass`(密码)
- `protected-mode no`(允许远程连接)
- `appendonly yes` + `appendfsync everysec`
- `maxmemory-policy allkeys-lru`
## 3. 常用命令
在应用或 CLI 中使用:
```bash
redis-cli -h 49.232.6.45 -p 6379 -a MsuMshk112233 ping
```
`appsettings.*.json` 的格式:`"Redis": "49.232.6.45:6379,password=MsuMshk112233,abortConnect=false"`
## 4. 备份
- RDB 文件:`dump.rdb`

25
deploy/redis/redis.conf Normal file
View File

@@ -0,0 +1,25 @@
bind 0.0.0.0
port 6379
protected-mode no
requirepass MsuMshk112233
timeout 0
tcp-keepalive 300
daemonize no
loglevel notice
databases 16
save 900 1
save 300 10
save 60 10000
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
dir /data
maxmemory-policy allkeys-lru