Add skill for gameplay entry type workflows
This commit is contained in:
@@ -96,8 +96,10 @@ export interface ApiErrorEnvelope {
|
||||
| 当前管理员 | `GET /admin/api/me` | 管理员 Bearer |
|
||||
| 服务与数据库概览 | `GET /admin/api/overview` | 管理员 Bearer |
|
||||
| 受控 HTTP 调试 | `POST /admin/api/debug/http` | 管理员 Bearer |
|
||||
| 读取兑换码列表 | `GET /admin/api/profile/redeem-codes` | 管理员 Bearer |
|
||||
| 创建/更新兑换码 | `POST /admin/api/profile/redeem-codes` | 管理员 Bearer |
|
||||
| 停用兑换码 | `POST /admin/api/profile/redeem-codes/disable` | 管理员 Bearer |
|
||||
| 读取后台邀请码列表 | `GET /admin/api/profile/invite-codes` | 管理员 Bearer |
|
||||
| 创建/更新注册邀请码 | `POST /admin/api/profile/invite-codes` | 管理员 Bearer |
|
||||
|
||||
### 4.3 前端类型命名
|
||||
@@ -206,6 +208,10 @@ export interface ProfileRedeemCodeAdminResponse {
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface ProfileRedeemCodeAdminListResponse {
|
||||
entries: ProfileRedeemCodeAdminResponse[];
|
||||
}
|
||||
|
||||
export interface ProfileInviteCodeAdminResponse {
|
||||
userId: string;
|
||||
inviteCode: string;
|
||||
@@ -213,6 +219,10 @@ export interface ProfileInviteCodeAdminResponse {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface ProfileInviteCodeAdminListResponse {
|
||||
entries: ProfileInviteCodeAdminResponse[];
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 登录 contract
|
||||
@@ -284,6 +294,31 @@ export interface ProfileInviteCodeAdminResponse {
|
||||
|
||||
### 4.7 兑换码管理 contract
|
||||
|
||||
列表请求:
|
||||
|
||||
`GET /admin/api/profile/redeem-codes`
|
||||
|
||||
成功返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"code": "WELCOME2026",
|
||||
"mode": "public",
|
||||
"rewardPoints": 100,
|
||||
"maxUses": 1,
|
||||
"globalUsedCount": 0,
|
||||
"enabled": true,
|
||||
"allowedUserIds": [],
|
||||
"createdBy": "admin:root",
|
||||
"createdAt": "2026-04-30T00:00:00Z",
|
||||
"updatedAt": "2026-04-30T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
创建/更新请求:
|
||||
|
||||
```json
|
||||
@@ -300,8 +335,6 @@ export interface ProfileInviteCodeAdminResponse {
|
||||
|
||||
停用请求:
|
||||
|
||||
兑换码管理页的最近一次接口返回记录由 `AdminApp` 维护为管理端会话态,并传入 `AdminRedeemCodePage` 渲染。页面页签通过 hash 切换时子页面会卸载,不能把最近记录只放在兑换码页面内部 `useState` 中,否则切换到其他页签再返回会展示“暂无记录”。该会话态只用于保留当前操作结果,不作为兑换码历史列表;退出登录或重新登录时清空。
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "WELCOME2026"
|
||||
@@ -325,10 +358,36 @@ export interface ProfileInviteCodeAdminResponse {
|
||||
}
|
||||
```
|
||||
|
||||
兑换码管理页进入时必须通过 `GET /admin/api/profile/redeem-codes` 加载数据库已有记录。最近一次接口返回记录仍由 `AdminApp` 维护为管理端会话态,用于展示当前操作结果;历史列表不得依赖该会话态,刷新页面后必须从后端列表接口恢复。列表项点击后回填表单,继续通过同一个 `POST /admin/api/profile/redeem-codes` 修改原记录。
|
||||
|
||||
前端只做基础输入约束,最终标准化、私有码用户解析、次数和奖励合法性以 `server-rs` 为准。
|
||||
|
||||
### 4.8 邀请码管理 contract
|
||||
|
||||
列表请求:
|
||||
|
||||
`GET /admin/api/profile/invite-codes`
|
||||
|
||||
成功返回:
|
||||
|
||||
```json
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"userId": "admin:root:SPRING2026",
|
||||
"inviteCode": "SPRING2026",
|
||||
"metadata": {
|
||||
"batch": "spring"
|
||||
},
|
||||
"createdAt": "2026-04-30T00:00:00Z",
|
||||
"updatedAt": "2026-04-30T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
后台邀请码列表只返回后台运营预置码。后端按 `profile_invite_code.user_id` 的 `admin:` 前缀过滤,普通用户在邀请中心生成的个人邀请码不得展示在后台列表中。
|
||||
|
||||
创建/更新请求:
|
||||
|
||||
```json
|
||||
@@ -372,19 +431,22 @@ export interface ProfileInviteCodeAdminResponse {
|
||||
3. 总览页加载失败时展示后端错误,不吞掉 `fetchErrors`。
|
||||
4. API 调试页的 headers 使用键值行编辑,提交前转为 `[{ name, value }]`。
|
||||
5. 兑换码页的 `mode=private` 时展示允许用户输入区;其他模式提交空数组。
|
||||
6. 邀请码页只提交 `inviteCode` 与 JSON 对象 metadata,不在前端复制后端邀请码规则。
|
||||
7. 所有按钮的 loading 状态必须锁定重复提交。
|
||||
8. 移动端优先:表单单列,导航紧凑,结果面板可横向/纵向滚动。
|
||||
6. 兑换码页和邀请码页进入时加载数据库列表,保存后合并返回记录,点击列表项回填表单进入编辑态。
|
||||
7. 邀请码页只提交 `inviteCode` 与 JSON 对象 metadata,不在前端复制后端邀请码规则。
|
||||
8. 所有按钮的 loading 状态必须锁定重复提交。
|
||||
9. 移动端优先:表单单列,导航紧凑,结果面板可横向/纵向滚动。
|
||||
|
||||
## 7. 部署与联调
|
||||
|
||||
### 7.1 本地联调
|
||||
|
||||
1. 启动后端:`npm run api-server`。
|
||||
2. 启动后台前端:在 `apps/admin-web` 执行 `npm run dev`。
|
||||
3. 后台 dev server 通过 Vite proxy 转发 `/admin/api` 到 `ADMIN_API_TARGET`;未配置时默认 `http://127.0.0.1:3100`。
|
||||
4. 若使用非 3100 端口,在仓库根目录 `.env.local` 设置 `ADMIN_API_TARGET=http://127.0.0.1:<api-server-port>`,并重启后台前端 dev server。
|
||||
5. `GENARRATIVE_API_PORT` 控制 Rust `api-server` 监听端口;`ADMIN_API_TARGET` 只控制后台前端 dev proxy 目标,二者需要指向同一个端口。
|
||||
1. 完整本地栈直接在仓库根目录执行 `npm run dev`。
|
||||
2. `npm run dev` 默认启动 SpacetimeDB standalone、Rust `api-server`、主站 Vite 和后台 Vite。
|
||||
3. 主站默认地址为 `http://127.0.0.1:3000`,后台可从主站 `http://127.0.0.1:3000/admin/` 进入,也可直连 `http://127.0.0.1:3102`。
|
||||
4. 主站 Vite 会把 `/admin/` 转发到后台 dev server,贴近生产同域 `/admin/` 入口。
|
||||
5. 后台 dev server 通过 Vite proxy 转发 `/admin/api` 到当前 Rust API 地址;`--api-port` 改动时脚本会同步注入 `ADMIN_API_TARGET`。
|
||||
6. 如需单独启动后台前端,可继续执行根脚本 `npm run admin-web:dev`,或在 `apps/admin-web` 执行 `npm run dev`;单独启动时未配置 `ADMIN_API_TARGET` 会默认代理到 `http://127.0.0.1:3100`。
|
||||
7. 后台 dev 端口可用 `npm run dev -- --admin-web-port <port>` 覆盖。
|
||||
|
||||
### 7.2 构建部署
|
||||
|
||||
@@ -430,8 +492,8 @@ export interface ProfileInviteCodeAdminResponse {
|
||||
- token 恢复、过期清理、退出登录。
|
||||
- 总览页正常数据、部分表统计失败、整体请求失败。
|
||||
- API 调试成功访问 `/healthz`,绝对 URL 被后端拒绝。
|
||||
- 兑换码 public/unique/private 表单提交和停用。
|
||||
- 邀请码表单提交、metadata JSON 对象校验和结果展示。
|
||||
- 兑换码数据库列表加载、列表点击回填、public/unique/private 表单提交和停用。
|
||||
- 邀请码数据库列表加载、普通用户邀请码不展示、列表点击回填、metadata JSON 对象校验和结果展示。
|
||||
2. 根工程:
|
||||
- `npm run check:encoding`。
|
||||
- 后续接入根 workspace 后,补充后台工程 build/typecheck 脚本。
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# 个人任务与埋点系统技术方案
|
||||
|
||||
更新时间:`2026-05-03`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
本轮新增一套可配置的个人任务系统,并补齐任务依赖的埋点统计能力。首个任务为“每日登录”,奖励 `10` 光点,入口放在“我的”页签;后台可修改任务配置。
|
||||
|
||||
## 2. 核心边界
|
||||
|
||||
- 埋点原始事实写入 `tracking_event`,这是实际存在的 SpacetimeDB 表。
|
||||
- 聚合投影写入 `tracking_daily_stat`,这也是后端维护的真实表,不是 view。
|
||||
- 任务配置写入 `profile_task_config`,默认配置包含 `daily_login`,后台修改后不得被默认初始化覆盖。
|
||||
- 任务进度写入 `profile_task_progress`,用于任务中心快速读取状态。
|
||||
- 领奖记录写入 `profile_task_reward_claim`,与钱包流水 `profile_wallet_ledger` 同事务写入。
|
||||
- “星光”奖励复用现有“光点”钱包,不新增第二种货币。
|
||||
|
||||
## 3. 埋点分层
|
||||
|
||||
| 层级 | scope_kind | scope_id 口径 |
|
||||
| --- | --- | --- |
|
||||
| 整站 | `site` | 固定为 `site` 或站点分区 key |
|
||||
| 作品 | `work` | 作品 profile_id / work_id |
|
||||
| 模块 | `module` | 模块 key,例如 `profile`、`puzzle` |
|
||||
| 用户 | `user` | 用户 id |
|
||||
|
||||
每条埋点可同时记录 `user_id`、`owner_user_id`、`profile_id`、`module_key` 与 `metadata_json`。任务首版只依赖用户层 `daily_login`,表结构先保留四层统计能力。
|
||||
|
||||
## 4. 日期桶
|
||||
|
||||
任务统计使用北京时间自然日:`day_key = floor((occurred_at_micros + 8h) / 1d)`。
|
||||
|
||||
这样存储仍是 UTC 时间戳,日切规则固定为业务口径,不依赖服务器本地时区。`tracking_event.occurred_at` 保存精确发生时间,`tracking_daily_stat.day_key` 只承担聚合桶职责。
|
||||
|
||||
## 5. 首版任务
|
||||
|
||||
| 字段 | 默认值 |
|
||||
| --- | --- |
|
||||
| task_id | `daily_login` |
|
||||
| title | `每日登录` |
|
||||
| event_key | `daily_login` |
|
||||
| cycle | `daily` |
|
||||
| threshold | `1` |
|
||||
| reward_points | `10` |
|
||||
| enabled | `true` |
|
||||
|
||||
用户打开任务中心时,后端会幂等记录当日 `daily_login` 埋点并刷新任务进度。用户点击领取时,后端校验当日进度、领奖记录和配置状态,然后同事务写入领奖记录与钱包流水。
|
||||
|
||||
后台任务配置页的 `Event Key` 使用可搜索下拉控件,选项来自前端后台的埋点定义注册表。当前注册表默认包含 `daily_login`,展示中文名称和备注;后续新增任务依赖的埋点时,应先补充注册表,再开放运营配置。
|
||||
|
||||
## 6. 接口
|
||||
|
||||
### 用户侧
|
||||
|
||||
- `GET /api/profile/tasks`:读取任务中心,同时记录当日登录埋点。
|
||||
- `POST /api/profile/tasks/{task_id}/claim`:领取任务奖励。
|
||||
|
||||
### 后台侧
|
||||
|
||||
- `GET /admin/api/profile/tasks`:读取任务配置列表。
|
||||
- `POST /admin/api/profile/tasks`:新增或更新任务配置。
|
||||
- `POST /admin/api/profile/tasks/disable`:停用任务配置。
|
||||
|
||||
后台任务配置页进入时从 `profile_task_config` 对应的列表接口读取已有配置,点击列表项回填表单后仍通过同一个 upsert 接口修改原配置。最近一次保存结果可以保留为会话态提示,但不得作为任务配置列表的唯一来源。
|
||||
|
||||
## 7. 查询文档边界
|
||||
|
||||
- `docs/tracking/` 只存放具体埋点与埋点聚合查询,例如 `tracking_event`、`tracking_daily_stat` 的站点/作品/模块/用户查询。
|
||||
- `docs/operations/` 存放运营核查查询,例如任务进度、领奖记录、钱包流水对账。
|
||||
|
||||
不要把任务进度、领奖记录或钱包对账查询塞进 `docs/tracking/`,它们不是埋点系统本身。
|
||||
|
||||
## 8. 验收
|
||||
|
||||
1. `profile_task_config` 默认存在 `daily_login`,后台可修改奖励、阈值、标题和启用状态。
|
||||
2. “我的”页可以打开每日任务面板,登录后任务可领取 `10` 光点。
|
||||
3. 重复打开任务中心不会重复增加领取资格,重复领奖不会重复发放。
|
||||
4. 表目录、迁移白名单、Rust/TypeScript 契约和前端入口同步更新。
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
## 文档列表
|
||||
|
||||
- [PROFILE_TASK_AND_TRACKING_SYSTEM_2026-05-03.md](./PROFILE_TASK_AND_TRACKING_SYSTEM_2026-05-03.md):冻结个人任务与埋点系统首版方案,明确 `tracking_event`、`tracking_daily_stat`、`profile_task_config`、任务进度、领奖记录和光点钱包流水的边界。
|
||||
- [PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md](./PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md):冻结单机生产部署目标,从旧一体化启动脚本切到 Nginx、systemd 托管 SpacetimeDB 与 Rust `api-server`,并记录生产 Jenkins 流水线拆分计划和首批部署骨架。
|
||||
- [PUZZLE_RUNTIME_FRONTEND_LOGIC_REHOME_2026-05-02.md](./PUZZLE_RUNTIME_FRONTEND_LOGIC_REHOME_2026-05-02.md):记录拼图正式平台入口移动、交换、合并、拆分和通关裁决收回前端即时运行态,排行榜、下一关和游玩记录继续由后端持久化处理。
|
||||
- [RPG_FOUNDATION_DRAFT_ROLE_DOSSIER_TIMEOUT_FALLBACK_2026-05-02.md](./RPG_FOUNDATION_DRAFT_ROLE_DOSSIER_TIMEOUT_FALLBACK_2026-05-02.md):记录 `agent-foundation-*-dossier-batch-*` 无搜索 Responses 请求超时后的本地养成档案兜底,避免底稿主链被尾部角色润色阶段阻断。
|
||||
|
||||
@@ -24,7 +24,7 @@ spacetime sql <db> "SELECT * FROM custom_world_gallery_entry"
|
||||
| --- | --- |
|
||||
| 运维迁移 | `database_migration_operator`, `database_migration_import_chunk` |
|
||||
| 认证 | `auth_store_snapshot`, `user_account`, `auth_identity`, `refresh_session` |
|
||||
| 运行时档案 | `runtime_setting`, `runtime_snapshot`, `user_browse_history`, `profile_dashboard_state`, `profile_wallet_ledger`, `profile_redeem_code`, `profile_redeem_code_usage`, `profile_invite_code`, `profile_referral_relation`, `profile_played_world`, `profile_membership`, `profile_recharge_order`, `profile_save_archive` |
|
||||
| 运行时档案 | `runtime_setting`, `runtime_snapshot`, `user_browse_history`, `profile_dashboard_state`, `profile_wallet_ledger`, `tracking_event`, `tracking_daily_stat`, `profile_task_config`, `profile_task_progress`, `profile_task_reward_claim`, `profile_redeem_code`, `profile_redeem_code_usage`, `profile_invite_code`, `profile_referral_relation`, `profile_played_world`, `profile_membership`, `profile_recharge_order`, `profile_save_archive` |
|
||||
| RPG 运行时 | `story_session`, `story_event`, `npc_state`, `inventory_slot`, `battle_state`, `treasure_record`, `quest_record`, `quest_log`, `player_progression`, `chapter_progression` |
|
||||
| 世界创作 | `custom_world_profile`, `custom_world_session`, `custom_world_agent_session`, `custom_world_agent_message`, `custom_world_agent_operation`, `custom_world_draft_card`, `custom_world_gallery_entry` |
|
||||
| 拼图 | `puzzle_agent_session`, `puzzle_agent_message`, `puzzle_work_profile`, `puzzle_event`, `puzzle_runtime_run`, `puzzle_leaderboard_entry` |
|
||||
@@ -158,14 +158,72 @@ SELECT * FROM profile_wallet_ledger WHERE user_id = '<user_id>';
|
||||
SELECT * FROM profile_wallet_ledger WHERE user_id = '<user_id>' ORDER BY created_at DESC;
|
||||
```
|
||||
|
||||
### `tracking_event`
|
||||
|
||||
- 作用:埋点原始事件表,保存整站、作品、模块和用户层的原始事实。
|
||||
- 结构:`event_id PK: String`, `event_key: String`, `scope_kind: RuntimeTrackingScopeKind`, `scope_id: String`, `day_key: i64`, `user_id: Option<String>`, `owner_user_id: Option<String>`, `profile_id: Option<String>`, `module_key: Option<String>`, `metadata_json: String`, `occurred_at: Timestamp`。
|
||||
- 索引:`event_key`, `(scope_kind, scope_id)`, `(user_id, occurred_at)`。
|
||||
|
||||
```sql
|
||||
SELECT * FROM tracking_event WHERE event_id = '<event_id>';
|
||||
SELECT * FROM tracking_event WHERE event_key = '<event_key>' ORDER BY occurred_at DESC;
|
||||
SELECT * FROM tracking_event WHERE scope_kind = 'User' AND scope_id = '<user_id>' ORDER BY occurred_at DESC;
|
||||
```
|
||||
|
||||
### `tracking_daily_stat`
|
||||
|
||||
- 作用:埋点按北京时间自然日聚合后的真实表,供任务统计和快速查询使用。
|
||||
- 结构:`stat_id PK: String`, `event_key: String`, `scope_kind: RuntimeTrackingScopeKind`, `scope_id: String`, `day_key: i64`, `count: u32`, `first_occurred_at: Timestamp`, `last_occurred_at: Timestamp`, `updated_at: Timestamp`。
|
||||
- 索引:`(event_key, day_key)`, `(scope_kind, scope_id, day_key)`。
|
||||
|
||||
```sql
|
||||
SELECT * FROM tracking_daily_stat WHERE stat_id = '<stat_id>';
|
||||
SELECT * FROM tracking_daily_stat WHERE scope_kind = 'User' AND scope_id = '<user_id>' ORDER BY day_key DESC;
|
||||
```
|
||||
|
||||
### `profile_task_config`
|
||||
|
||||
- 作用:个人任务配置表,后台可修改每日登录等任务的奖励、阈值、启用状态和排序。
|
||||
- 结构:`task_id PK: String`, `title: String`, `description: String`, `event_key: String`, `cycle: RuntimeProfileTaskCycle`, `scope_kind: RuntimeTrackingScopeKind`, `threshold: u32`, `reward_points: u64`, `enabled: bool`, `sort_order: i32`, `created_by: String`, `created_at: Timestamp`, `updated_by: String`, `updated_at: Timestamp`。
|
||||
- 索引:主键 `task_id`。
|
||||
|
||||
```sql
|
||||
SELECT * FROM profile_task_config WHERE task_id = 'daily_login';
|
||||
SELECT * FROM profile_task_config ORDER BY updated_at DESC;
|
||||
```
|
||||
|
||||
### `profile_task_progress`
|
||||
|
||||
- 作用:个人任务进度表,保存用户在某个自然日的任务进度和状态快照。
|
||||
- 结构:`progress_id PK: String`, `user_id: String`, `task_id: String`, `day_key: i64`, `progress_count: u32`, `threshold: u32`, `status: RuntimeProfileTaskStatus`, `updated_at: Timestamp`。
|
||||
- 索引:`user_id`, `(user_id, task_id)`。
|
||||
|
||||
```sql
|
||||
SELECT * FROM profile_task_progress WHERE user_id = '<user_id>' ORDER BY updated_at DESC;
|
||||
SELECT * FROM profile_task_progress WHERE user_id = '<user_id>' AND task_id = 'daily_login' ORDER BY day_key DESC;
|
||||
```
|
||||
|
||||
### `profile_task_reward_claim`
|
||||
|
||||
- 作用:个人任务领奖记录表,记录用户、任务、自然日、奖励和对应钱包流水。
|
||||
- 结构:`claim_id PK: String`, `user_id: String`, `task_id: String`, `day_key: i64`, `reward_points: u64`, `wallet_ledger_id: String`, `claimed_at: Timestamp`。
|
||||
- 索引:`user_id`, `(user_id, task_id)`。
|
||||
|
||||
```sql
|
||||
SELECT * FROM profile_task_reward_claim WHERE user_id = '<user_id>' ORDER BY claimed_at DESC;
|
||||
SELECT * FROM profile_task_reward_claim WHERE claim_id = '<user_id>:daily_login:<day_key>';
|
||||
```
|
||||
|
||||
### `profile_redeem_code`
|
||||
|
||||
- 作用:运营发放的光点兑换码,支持公共码、唯一码和私有码。
|
||||
- 结构:`code PK: String`, `mode: RuntimeProfileRedeemCodeMode`, `reward_points: u64`, `max_uses: u32`, `global_used_count: u32`, `enabled: bool`, `allowed_user_ids: Vec<String>`, `created_by: String`, `created_at: Timestamp`, `updated_at: Timestamp`。
|
||||
- 索引:主键 `code`。
|
||||
- 后台读取:`GET /admin/api/profile/redeem-codes` 从该表返回已有兑换码,后台列表点击后通过 upsert 修改同一条记录。
|
||||
|
||||
```sql
|
||||
SELECT * FROM profile_redeem_code WHERE code = '<CODE>';
|
||||
SELECT * FROM profile_redeem_code ORDER BY updated_at DESC;
|
||||
```
|
||||
|
||||
### `profile_redeem_code_usage`
|
||||
@@ -181,13 +239,15 @@ SELECT * FROM profile_redeem_code_usage WHERE user_id = '<user_id>';
|
||||
|
||||
### `profile_invite_code`
|
||||
|
||||
- 作用:用户邀请中心的邀请码主表,保存用户当前稳定邀请码。
|
||||
- 结构:`user_id PK: String`, `invite_code: String`, `created_at: Timestamp`, `updated_at: Timestamp`。
|
||||
- 作用:用户邀请中心的邀请码主表,也承载后台运营预置邀请码。
|
||||
- 结构:`user_id PK: String`, `invite_code: String`, `metadata_json: String`, `created_at: Timestamp`, `updated_at: Timestamp`。
|
||||
- 索引:主键 `user_id`,唯一索引 `invite_code`。
|
||||
- 后台读取:`GET /admin/api/profile/invite-codes` 只返回 `user_id` 以 `admin:` 开头的后台预置码;普通用户自己的邀请码不得进入后台运营列表。
|
||||
|
||||
```sql
|
||||
SELECT * FROM profile_invite_code WHERE user_id = '<user_id>';
|
||||
SELECT * FROM profile_invite_code WHERE invite_code = '<invite_code>';
|
||||
SELECT * FROM profile_invite_code WHERE user_id LIKE 'admin:%' ORDER BY updated_at DESC;
|
||||
```
|
||||
|
||||
### `profile_referral_relation`
|
||||
|
||||
Reference in New Issue
Block a user