--- url: https://www.aiforge.cn/overview/project-overview.md --- # 项目概述与产品定位 ## 1. 项目背景 Ai Forge 是一套企业级项目套件,旨在为开发者提供快速构建高质量应用的基础框架。套件包含: * **Ai Forge API**:后端 API 服务,基于 Laravel 13.x 构建,采用领域驱动设计(DDD)架构,内置完整的管理员/用户双端认证、RBAC 权限管理、后台管理系统与 AI 助手能力 * **Ai Forge Admin**:管理后台前端(Vue 3 + TypeScript + Element Plus SPA) * **Ai Forge 文档站**:产品与技术文档(即本站) 其他信息: * **官方站点**: * **演示后台**: * **本地开发域名**:`http://laravel-skel-forge.test`(Laravel Herd 自动提供) ## 2. 项目目标 * 提供开箱即用的企业级 API 开发模板 * 集成成熟的认证、授权和权限管理方案 * 支持多用户系统(管理员+前台用户,双 Guard 隔离) * 内置常用的系统管理功能(用户、角色、菜单、配置、地区、附件、验证码、通知、审计) * 集成 AI Agent 能力,为后台提供智能助手 * 内置完整的监控、队列、定时任务、调度日志体系 * 提供完整的开发、测试和部署方案 ## 3. 目标用户 | 用户类型 | 说明 | |---------|------| | 后端开发人员 | 快速搭建 API 服务 | | 技术团队 | 统一技术栈和开发规范 | | 初创企业 | 快速构建 MVP 产品 | | 教育机构 | 学习 Laravel 最佳实践 | ## 4. 产品定位 ### 4.1 产品类型 企业级 API 开发框架模板(可直接作为 APP 项目的后端起点) ### 4.2 核心价值 | 核心价值 | 说明 | |---------|------| | **快速启动** | 一键安装和配置,快速开始开发 | | **安全可靠** | 内置完整的认证和权限体系 | | **架构清晰** | 采用 DDD 分层架构,代码组织规范 | | **功能完整** | 包含常用的系统管理、监控、AI 能力 | | **易于扩展** | 模块化设计,易于添加新功能 | | **实时通信** | Laravel Reverb + Echo 提供 WebSocket 能力 | ### 4.3 市场定位 | 维度 | 定位 | |------|------| | 技术层次 | 中高级后端开发框架 | | 应用场景 | APP 后端服务、企业管理系统、电商后台、SaaS 服务 | | 目标规模 | 中小型到大型企业级应用 | ### 4.4 作为 APP 后端起点 前台 API v1 已覆盖 APP 业务的基础能力,拿到手后只需实现具体业务逻辑,无需从地基搭起: | 能力域 | 已具备的接口 | |--------|-------------| | 账户 | 注册登录(手机/邮箱/微信/Apple)、Token 刷新、账户注销 | | 用户中心 | 资料、头像(自动转 WebP)、联系方式、密码、VIP、积分/金币流水、社交账号、登录历史、实名认证 | | 资产 | 钱包概览、流水、充值下单与订单查询、提现申请 | | 增长与留存 | 任务中心(进度查询、领奖)、邀请关系与统计、访问记录(PV/UV、最近浏览) | | 内容与互动 | 多态点赞/收藏/关注/评论、Banner、公告、协议 | | 服务与合规 | 通知、收货地址、反馈提交、举报提交 | | AI 接入 | MCP Server(`/mcp/v1`),C 端能力直接被 AI 客户端调用 | *** > 文档来源:PRD 第 1、2 章 --- --- url: https://www.aiforge.cn/features/user-system.md --- # 用户系统 ## 1. 管理员用户系统(Guard: admin) | 功能 | 说明 | |------|------| | 管理员登录 | 支持用户名/邮箱/手机号登录,可选微信/Apple 授权登录 | | Token 管理 | 刷新 Token、查看已签发 Token、销毁指定 Token | | 管理员信息管理 | 查看和修改个人资料、上传头像 | | 密码管理 | 修改密码、重置密码 | | 状态管理 | 启用/禁用管理员账号 | | 角色分配 | 为管理员分配角色(多对多) | | 登录历史 | 记录管理员登录历史(IP、设备、浏览器) | | 权限控制 | 基于 RBAC 的权限管理(spatie/laravel-permission v8) | ## 2. 前台用户系统(Guard: web) | 功能 | 说明 | |------|------| | 用户注册 | 支持手机号/邮箱注册 | | 用户认证 | 基于 Sanctum 的 Token 认证 | | 用户信息管理 | 查看和修改个人资料(profile/extra 两张扩展表) | | 头像管理 | 上传头像(multipart/form-data),服务端自动转换 WebP(质量 60),以用户 ID 命名并按分片目录存储,重复上传覆盖旧图;上传后派发 `UserProfileUpdated` 事件驱动完善资料任务 | | 密码重置 | 通过邮箱/手机验证码重置密码 | | 联系方式重置 | 通过验证码重置手机号/邮箱 | | 用户状态管理 | 启用、禁用、冻结用户 | | VIP会员系统 | 支持VIP会员延期功能 | | 积分/金币系统 | 用户积分和金币流水管理(调整余额、查询流水) | | 钱包系统 | 真实货币余额管理(余额查询、流水明细、充值、提现、冻结/解冻),记录层多态设计支持未来扩展至其他主体 | | 实名认证 | 支持个人认证(姓名+身份证)和企业认证(营业执照+法人信息),多态认证记录,管理员审核通过/驳回,驳回原因字典化管理,认证有效期 3 年 | | 第三方绑定 | 查询用户社交账号绑定记录(前台 `GET /api/v1/user/socials` + 后台 `GET /admin/users/{id}/socials`,openid/unionid 脱敏展示) | | 登录历史 | 记录用户登录历史 | | 用户分群 | 手动分群(人工打标/解标)与规则分群(基于规则 JSON 自动匹配),支持笛卡尔批量打标/解标、规则增量 diff 同步、异步任务跟踪与站内信通知,每日凌晨 2 点自动同步启用的规则分群 | | 访问记录 | 多态访问埋点(记录一次访问 + 我的访问记录列表),用于 PV/UV 与"最近浏览"等业务场景;append-only 模型,无 `updated_at`、无 unique 约束 | 详见 [实名认证技术方案](/technical-solutions/identity-verification)、[访问记录技术方案](/technical-solutions/visit-tracking)。 *** > 文档来源:PRD 第 3.1 节 --- --- url: https://www.aiforge.cn/features/auth-and-permission.md --- # 认证与授权 ## 1. 认证系统 | 功能 | 说明 | |------|------| | Sanctum Token认证 | 双 Guard Token 认证(admin/web),默认有效期 6 个月 | | 多账号登录 | 支持用户名/邮箱/手机号三种登录方式 | | 第三方登录 | 微信公众号授权登录、Apple 登录(管理员端预留) | | 验证码服务 | 短信验证码(EasySMS)和邮件验证码 | | Token 管理 | 刷新 Token、查看/撤销已签发的个人访问令牌 | | 登录防护 | 登录频率限制、IP 限制 | ## 2. 权限管理 | 功能 | 说明 | |------|------| | 角色管理 | 创建、编辑、删除角色 | | 权限管理 | 管理系统权限列表 | | 角色权限分配 | 为角色分配权限 | | 管理员角色分配 | 为管理员分配角色 | | 权限检查 | 路由级(permission:xxx 中间件)和方法级权限控制 | | 菜单权限 | 基于权限的菜单显示控制,前端自动过滤无权限路由 | ### 权限模型 * 使用 `spatie/laravel-permission v8` 实现 RBAC * 支持角色与权限多对多关系 * 支持模型级多态关联(管理员、用户等多主体共用权限体系) * 权限检查支持中间件和 Gate 两种方式 ### AI 助手权限 AI 助手(`/admin/chat`)复用同一套权限体系,分三层控制: 1. **入口**:`ChatController` 挂载 `permission:chat.use` 中间件 2. **工具过滤**:`Assistant::tools()` 按当前管理员的权限点动态过滤可用工具(工具实现 `RequiresPermission` 契约声明权限点) 3. **执行兜底**:工具 `handle()` 内 `authorizeAdmin()` 二次校验,防止模型被诱导发起越权调用 权限点与后台菜单共用(如 `users.index`/`users.edit`/`settings.edit`),AI 助手入口使用独立的 `chat.use` 权限点。 *** > 文档来源:PRD 第 3.2 节 --- --- url: https://www.aiforge.cn/features/admin-system.md --- # 后台管理系统 后台管理系统提供完整的系统管理和运营管理能力,所有接口前缀为 `/admin`,需管理员登录态。 ## 1. 系统设置 | 功能 | 说明 | |------|------| | 配置管理 | 管理系统配置项(支持 string/int/float/bool/json 数据类型),支持分组查看和批量更新 | | 管理员管理 | 管理员账号 CRUD(创建/编辑弹窗支持头像上传:图片经通用上传接口 `/admin/uploader/image` 中转后将 URL 随 `avatar` 字段提交保存)、状态切换、密码重置、角色分配、登录历史查询 | | 菜单管理 | 管理后台菜单/按钮结构(树结构,支持目录/菜单/按钮三种类型) | | 字典管理 | 管理数据字典类型和字典项(支持标签样式 default/primary/success/warning/danger/info),字典项按排序权重排列,删除字典时级联删除字典项 | | 地区管理 | 管理省市区地区数据(树结构,含经纬度、区号、邮编) | | 应用管理 | 管理客户端应用(名称、唯一标识 slug、描述、图标、排序、启用状态),支持查看各平台最新发布版本 | | 应用版本管理 | 管理应用版本(平台:安卓/iOS/鸿蒙;版本号、构建号、下载地址、更新说明、最低支持版本、强制更新标记、文件大小/哈希),支持草稿/发布/撤回/废弃四种状态,发布时自动将同应用同平台的旧发布版本置为废弃 | | 应用缓存清理 | 支持清理全部缓存或仅清理系统设置缓存(`types=all` 或 `types=settings`) | | 操作审计日志 | 通过中间件自动记录后台操作(路径、方法、参数、状态码、IP、UA、耗时),只读查看 | | 调度任务日志 | 自动记录定时任务执行情况(成功/失败/跳过、退出码、耗时、异常),支持按保留天数自动清理 | | 定时任务管理 | 数据库驱动的动态定时任务 CRUD:支持 Artisan 命令 / 自定义 Shell 两种类型、命令参数与选项配置、Cron 表达式、环境限定、禁止重叠 / 单服务器 / 维护模式运行等高级选项;启用/禁用切换、手动执行(同步返回输出)、可用命令列表接口;动态任务在 AppServiceProvider 中注册到 Laravel 调度器,执行日志复用 ScheduleLogListener 自动记录 | ## 2. 运营管理 | 功能 | 说明 | |------|------| | 评论管理 | 评论列表(支持按审核状态/源类型/关键词/用户过滤)、评论详情(含 IP、上级评论摘要)、回复链查看(分页展示指定评论下的直接回复,支持链内下钻)、审核(通过/驳回/设为审核中,可填审核备注)、删除 | | 反馈管理 | 用户反馈列表与统计、反馈详情(含附件图片、客户端信息)、处理(调整状态/优先级、填写处理备注)、删除 | | 举报管理 | 举报列表(按状态/分类/对象类型/时间/关键词过滤)、详情(含被举报对象快照:评论内容/作者或用户资料/注册时间)、处理(resolved=已处理/rejected=已驳回 + 联动处置动作)、统计、列表导出 Excel。联动处置动作支持:`hide_comment`(将评论状态置为已驳回)、`delete_comment`(递归删除评论及其回复)、`freeze_user`(**永久冻结**,仅设置 `status=FROZEN`,用户表不保存冻结截止时间,需管理员通过「用户管理 → 状态切换」人工解冻)。被举报对象覆盖评论和用户两类共举报,使用多态关联(morphMap)实现任意被举报对象类型可扩展。举报分类通过字典 `report_category` 管理(9 类:色情低俗/暴力血腥/政治敏感/广告营销/辱骂人身攻击/虚假信息/侵权抄袭/违法违规/其他) | | 敏感词管理 | 敏感词 CRUD(含分类与级别),按分类与级别触发不同处理(替换/复审/拦截)。敏感词状态可切换,列表可导出,支持批量导入与刷新字典缓存 | | 短信验证码记录 | 查看短信验证码发送记录(只读) | | 邮件验证码记录 | 查看邮件验证码发送记录(只读) | | 上传服务 | 文件/图片/视频/APK 上传接口,生成直传 Token(兼容 S3 协议云存储) | | 通知管理 | 站内通知列表、未读数、标记已读、批量已读、清理已读 | | 钱包管理 | 钱包流水列表(全局,可按持有者/类型/时间筛选)、流水详情、后台调账(充正/扣减)、钱包统计(总余额/总充值/总提现) | | 提现审核 | 提现申请列表与统计、提现详情(含脱敏收款账户)、审核通过/驳回(驳回自动解冻)、标记已打款、导出提现记录 | | 充值订单 | 充值订单列表(按支付状态/渠道/时间筛选)、订单详情、超时订单自动关闭 | | 协议管理 | 管理用户协议、隐私政策、充值协议等协议内容(支持富文本 HTML),协议类别通过字典管理(`agreement_category`),支持版本号、草稿/发布/归档状态流转,发布时自动写入生效时间,前台通过 `GET /api/v1/agreements/{category}` 按类别获取最新已发布版本 | ## 3. 公共选项接口 | 功能 | 说明 | |------|------| | 源类型选项 | 基于 `config/morph_maps.php` 的 morph map 输出多态源类型选项(label/value),供前端筛选下拉使用。此类与业务模块无关的通用选项统一挂在 `CommonController` 下(路由前缀 `/admin/common`) | ## 4. 附件管理(上传文件管理器) | 功能 | 说明 | |------|------| | 附件列表 | 分页查询,支持类型/磁盘/扩展名/上传者/关键词/时间范围筛选 | | 附件详情 | 查看单个附件的完整元数据(含物理文件存在性检查) | | 附件删除 | 单个删除、批量删除,同步删除 Storage 物理文件 | | 附件预览 | 图片/视频返回可访问 URL(公有盘直链,私有盘返回 null) | | 附件下载 | 服务端流式下载,保留原始文件名 | | 临时 URL | 为私有磁盘生成有时效的签名访问地址 | | 附件重命名 | 修改显示名称,不改动物理路径 | | 附件移动 | 在同一磁盘内移动物理路径(Storage 层 move) | | 磁盘列表 | 查询可用的存储磁盘列表 | | 直传登记 | 客户端直传云存储后回调登记元数据 | 详细技术方案见:[附件管理技术方案](../technical-solutions/attachment-management.md) ## 5. 监控面板 | 功能 | 说明 | |------|------| | 服务器性能 | 读取 Laravel Pulse 数据(CPU、内存、请求量) | | 队列监控 | 队列负载、慢任务统计 | | Reverb 监控 | WebSocket 连接数、消息吞吐 | | 缓存监控 | 缓存命中率统计 | | 异常监控 | 应用异常聚合统计 | | 慢查询/慢请求/慢任务 | 数据库慢查询、慢 HTTP 请求、慢队列任务 | | 外部请求监控 | 慢外部 HTTP 请求 | ## 6. 调试面板(Telescope,仅 local 环境) | 功能 | 说明 | |------|------| | 标签管理 | 查询/监控/取消监控标签 | | 记录开关 | 开启/暂停 Telescope 记录 | | 条目查询 | 分页查询 Telescope 条目、查看详情、标记解决、删除条目 | ## 7. 队列管理(Horizon) | 功能 | 说明 | |------|------| | 队列状态 | 队列统计、负载、Master/Supervisor 进程状态 | | 标签监控 | 监控/取消监控指定标签 | | 任务指标 | Jobs/Queues 吞吐指标和详情 | | 批处理 | 批处理列表、详情、重试失败批次 | | 任务列表 | 待处理/已完成/失败/静默任务列表和详情,重试失败任务 | ## 8. 数据概览 | 功能 | 说明 | |------|------| | 仪表盘统计 | 今日/总数统计(用户数、管理员数等核心指标),展示于「仪表盘 - 工作台」 | | 财务维度 | 充值/提现汇总(充值总额、提现总额、净收入、今日充值/提现、订单笔数)与资金流向趋势(近 N 天充值/提现金额双线图),展示于独立页面「仪表盘 - 财务分析」 | *** > 文档来源:PRD 第 3.3 节 --- --- url: https://www.aiforge.cn/features/ai-assistant.md --- # AI 助手 基于 `laravel/ai` 集成的后台 AI 助手能力,为管理员提供智能对话和工具调用能力。 ## 功能说明 | 功能 | 说明 | |------|------| | SSE 流式对话 | 以 Server-Sent Events 实时推送 AI 响应,支持思考过程(reasoning)和文本输出 | | 工具调用 | AI 可调用内置工具查询数据或执行操作(审计日志查询、验证码查询、系统设置读写、仪表盘统计、地区查询、系统信息、用户管理与站内通知、钱包流水/充值/提现查询与提现审核、财务趋势环比/充值转化漏斗/提现异常扫描、用户增长与留存分析、流失预警名单、意见反馈查询与处理、实名认证查询与审核、公告/Banner/敏感词/评论/协议管理、用户分群同步、任务系统查询、队列失败任务查询与重试、定时任务查询、App 版本查询、字典查询、管理员/角色查询、举报处理、邀请记录查询、附件查询、开放平台账户管理与令牌轮换、知识库语义检索(RAG)、网页抓取等) | | 知识库(RAG) | 知识库文档经「段落分块 + Embedding 向量化」异步索引后,AI 可通过 `SearchKnowledgeBase` 工具做语义检索,回答平台业务规则、产品说明、操作指南等知识类问题;知识库管理见「AI 知识库」接口章节。检索引擎可配置(`KNOWLEDGE_ENGINE`):默认 `database` 库内暴力余弦(零外部依赖);`meilisearch` 将分块向量同步至 Meilisearch(userProvided embedder),检索下沉引擎执行,支撑大规模分块,切换后执行 `php artisan knowledge:sync-index` 迁移存量数据 | | 工具审批 | 敏感操作(如调整用户余额、VIP 延期、重置密码等)需管理员二次审批后继续执行 | | 对话历史 | 按会话(Conversation)保存对话记录,支持查看历史会话、删除会话 | | 权限控制 | 接口入口由 `chat.use` 权限点保护;工具按当前管理员角色权限动态过滤,越权调用在执行层兜底拒绝 | ## 权限控制 AI 助手与后台 RBAC(`spatie/laravel-permission`)深度集成,采用三层防线防止越权: | 防线 | 位置 | 说明 | |------|------|------| | 入口控制 | `ChatController` | `permission:chat.use` 中间件,无权限的管理员无法使用任何 AI 助手接口(返回 403) | | 工具过滤 | `Assistant::tools()` | 按当前对话管理员的权限点(`canAny`)动态过滤工具列表,无权限的工具不会暴露给模型;无对话参与者时按 deny-by-default 全部过滤 | | 执行兜底 | 各工具 `handle()` | 工具装配时注入当前管理员,执行前 `authorizeAdmin()` 二次校验,即使模型被诱导发起越权调用也会被拒绝并返回提示文案 | 实现要点: * 工具实现 `App\Ai\Contracts\RequiresPermission` 契约声明所需权限点(支持字符串或数组,数组任一满足即可) * 授权能力由 `App\Ai\Tools\Concerns\AuthorizesAdmin` trait 提供(`forAdmin()` 注入 + `authorizeAdmin()` 校验) * `chat.use` 权限点随 `ai.chat` 菜单同步注册(菜单 saved 事件自动写入 permissions 表,超级管理员角色自动获得) ## 内置 AI 工具 | 工具分类 | 工具名称 | 说明 | 所需权限 | |---------|---------|------|---------| | Apps | ListAppVersions | 查询 App 版本列表 | `app-versions.index` | | Audit | ListAuditLogs | 查询后台操作日志 | `audit-logs.index` | | Audit | ListLoginHistories | 查询登录历史 | `login-histories.index` | | Audit | ListVerificationCodes | 查询验证码记录 | `phone-codes.index` 或 `mail-codes.index`(任一) | | Content | HandleReport | 处理用户举报(需审批) | `reports.edit` | | Content | ListComments | 查询用户评论 | `comments.index` | | Content | ListReports | 查询用户举报 | `reports.index` | | Content | ModerateComment | 审核评论(需审批) | `comments.edit` | | Feedbacks | ListFeedbacks | 查询用户意见反馈 | `feedbacks.index` | | Feedbacks | ReplyFeedback | 处理用户反馈(需审批) | `feedbacks.edit` | | Knowledge | SearchKnowledgeBase | 知识库语义检索(RAG) | `knowledge-documents.index` | | Open | ListOpenAccounts | 查询开放平台账户 | `open_accounts.index` | | Open | RotateOpenAccountToken | 轮换开放平台账户令牌(需审批) | `open_accounts.edit` | | Open | SetOpenAccountStatus | 启用/冻结开放平台账户(需审批) | `open_accounts.edit` | | Operation | ListAgreements | 查询用户协议列表 | `agreements.index` | | Operation | ListAnnouncements | 查询公告列表 | `announcements.index` | | Operation | ListBanners | 查询 Banner 列表 | `banners.index` | | Operation | PublishAnnouncement | 发布/归档公告(需审批) | `announcements.edit` | | Operation | SetBannerStatus | 启用/禁用 Banner(需审批) | `banners.edit` | | Operation | UpdateAgreement | 更新用户协议(需审批) | `agreements.edit` | | Queue | ListFailedJobs | 查询队列失败任务 | `horizon.index` | | Queue | RetryFailedJob | 重试队列失败任务(需审批) | `horizon.index` | | Segments | CreateUserSegment | 创建用户分群(需审批) | `segments.create` | | Segments | ListUserSegments | 查询用户分群列表 | `segments.index` | | Segments | SyncUserSegment | 同步规则分群成员(需审批) | `segments.edit` | | Settings | GetSetting | 获取系统设置 | `settings.index` | | Settings | SetSetting | 设置系统配置值(需审批) | `settings.edit` | | Stats | GetDashboardStats | 获取仪表盘统计数据 | `chat.use` | | Stats | GetUserGrowthStats | 用户增长/活跃/留存趋势分析 | `users.index` | | System | AddSensitiveWord | 新增敏感词(需审批) | `sensitive-words.create` | | System | GetAreas | 查询地区数据 | `areas.index` | | System | ListAdmins | 查询后台管理员列表 | `admins.index` | | System | ListAttachments | 查询附件列表 | `attachments.index` | | System | ListDicts | 查询字典类型与字典数据 | `dicts.index` | | System | ListRoles | 查询后台角色及权限构成 | `roles.index` | | System | ListScheduleLogs | 查询定时任务执行日志 | `schedules.index` | | System | ListSchedules | 查询定时任务定义 | `schedules.index` | | System | ListSensitiveWords | 查询敏感词库 | `sensitive-words.index` | | System | SystemInfo | 获取系统信息 | `chat.use` | | Tasks | ListTaskLogs | 查询任务奖励发放日志 | `task-logs.index` | | Tasks | ListTasks | 查询任务定义列表 | `tasks.index` | | Users | GetChurnRiskUsers | 流失风险用户名单(沉默时长 + 历史活跃度规则) | `users.index` | | Users | GetUser | 获取用户详情 | `users.index` | | Users | ListIdentityVerifications | 查询实名认证记录 | `identity-verifications.index` | | Users | ListInviteRecords | 查询用户邀请关系记录 | `invite-records.index` | | Users | ListUsers | 查询用户列表 | `users.index` | | Users | AdjustUserBalance | 调整用户积分/金币余额(需审批) | `users.edit` | | Users | ExtendUserVip | 延长用户VIP(需审批) | `users.edit` | | Users | GetUserSocials | 获取用户第三方绑定 | `users.index` | | Users | ResetUserContact | 重置用户联系方式(需审批) | `users.edit` | | Users | ResetUserPassword | 重置用户密码(需审批) | `users.edit` | | Users | ReviewIdentityVerification | 审核实名认证(需审批) | `identity-verifications.edit` | | Users | SendUserNotification | 向用户发送站内通知(需审批) | `users.edit` | | Users | SetUserStatus | 设置用户状态(需审批) | `users.edit` | | Wallet | DetectWithdrawAnomalies | 提现异常扫描(大额聚集/短时密集/新注册即提/共享账户/共享 IP) | `wallet.withdraws` | | Wallet | GetFinanceTrends | 财务趋势按日聚合与环比 | `wallet.index` 或 `wallet.orders` 或 `wallet.withdraws`(任一) | | Wallet | GetRechargeFunnel | 充值转化漏斗与分渠道成功率 | `wallet.orders` | | Wallet | ListRecharges | 查询充值订单 | `wallet.orders` | | Wallet | ListWalletTrades | 查询钱包流水 | `wallet.index` | | Wallet | ListWithdraws | 查询提现申请 | `wallet.withdraws` | | Wallet | ReviewWithdraw | 审核提现申请(需审批) | `wallet.withdraws` | | Web | WebFetch | 网页抓取 | `chat.use` | ## 模型提供方(Providers) AI 能力统一由 `laravel/ai` 驱动,提供方在 `config/ai.php` 配置: | Provider | 驱动 | 说明 | |----------|------|------| | `volc`(默认) | `volc` | 火山方舟,驱动由 `larva/laravel-ai-volc` 包注册,在 OpenAI 兼容协议(chat/completions)基础上扩展了 TTS 语音合成与 Seedance 视频生成能力;覆盖文本对话、Embedding 向量、语音、图像、视频五类模型 | | `aliyun-image` | `openai` | 阿里百炼图像生成(万相 / qwen-image) | `volc` provider 的接入点与模型默认值均可通过环境变量覆盖: | 环境变量 | 默认值 | 说明 | |---------|--------|------| | `VOLC_ARK_URL` / `VOLC_ARK_KEY` | `https://ark.cn-beijing.volces.com/api/v3` | 方舟接入点地址与密钥 | | `VOLC_TEXT_MODEL` | `doubao-seed-2-1-lite-260915` | 文本对话模型 | | `VOLC_EMBEDDINGS_MODEL` | `doubao-embedding-vision-251215` | 向量模型(知识库 Embedding) | | `VOLC_EMBEDDINGS_DIMENSIONS` | `2048` | 向量维度(支持传 `dimensions` 降为 1024) | | `VOLC_AUDIO_MODEL` | `doubao-seed-tts-2.0` | TTS 语音合成模型 | | `VOLC_AUDIO_VOICE` / `VOLC_AUDIO_MALE_VOICE` | 见 `config/ai.php` | 默认女 / 男音色(SDK 占位音色 `default-female` / `default-male` 分别映射到这两项) | | `VOLC_AUDIO_FORMAT` / `VOLC_AUDIO_SPEED` | `mp3` / `1.0` | 音频输出格式与语速 | | `VOLC_IMAGE_MODEL` | `doubao-seedream-5-0-pro-260628` | 图像生成模型(Seedream) | | `VOLC_IMAGE_WATERMARK` | `false` | 生成图右下角是否添加 AI 生成水印 | | `VOLC_VIDEO_MODEL` | `doubao-seedance-2-5-260628` | 视频生成模型(Seedance,异步任务:创建任务 → 轮询结果) | ## 内置代理(Agents) | 代理 | 用途 | 运行配置 | |------|------|---------| | `Assistant` | 后台 AI 助手(SSE 流式对话 + 工具调用) | `MaxSteps(12)`、`Temperature(0.3)`、`Timeout(300)` | | `Finance` | 财务分析对话(财务数据查询、趋势环比、提现风控扫描与解读) | `MaxSteps(12)`、`Temperature(0.1)`、`Timeout(300)` | | `Copywriter` | 运营文案生成(公告 / Banner,JSON 结构输出) | `Temperature(0.7)`、`Timeout(300)` | | `ContentReviewer` | 内容审核辅助(评论复核 / 举报初判 / 反馈草拟) | `Temperature(0.2)`、`Timeout(300)` | | `WechatAssistant` | 公众号智能客服(基于公开知识库问答) | `MaxSteps(4)`、`Temperature(0.4)`、`Timeout(300)` | > 所有代理统一通过 `#[Timeout(300)]` 设置 300 秒超时,避免多步工具调用或长文本生成被默认超时中断;Banner AI 生图等一次性生成调用同样显式设置 300 秒超时。 ## SSE 事件类型 聊天接口通过 `text/event-stream` 逐行推送以下事件类型: | 事件类型 | 说明 | |---------|------| | `text` | 文本增量输出 | | `reasoning` | 思考过程展示 | | `tool_call` | 工具调用信息 | | `tool_approval_required` | 需要人工审批的敏感操作 | | `error` | 错误信息 | | `done` | 流结束标记 | ## 聊天请求流转 ``` POST /admin/chat → ChatController → AI Agent (laravel/ai) → Tool 调用 → SSE Stream 响应 ↓ (敏感操作) 审批等待 → POST /admin/chat/approve → 继续执行 ``` *** > 文档来源:PRD 第 3.4 节 --- --- url: https://www.aiforge.cn/features/core-services.md --- # 核心服务 ## 1. 验证码服务 | 功能 | 说明 | |------|------| | 短信验证码发送 | 支持多种场景(登录、注册、重置密码等),EasySMS 多网关支持 | | 短信验证码验证 | 验证验证码的正确性,过期/错误次数限制 | | 邮件验证码发送 | 通过邮件发送验证码 | | 邮件验证码验证 | 验证邮件验证码 | | 验证码记录管理 | 记录和查询验证码发送历史 | ### 验证码表 * `phone_codes`:短信验证码(phone、scene、code、state、ip、send_at) * `mail_codes`:邮件验证码(email、scene、code、state、ip、send_at) ## 2. 文件上传服务 | 功能 | 说明 | |------|------| | 文件上传 | 通用文件上传接口 | | 图片上传 | 图片专用上传接口 | | 视频上传 | 视频专用上传接口 | | APK 上传 | Android 安装包专用上传接口(返回附件 id 和文件元信息) | | 上传Token生成 | 生成临时直传 Token(兼容 S3 协议) | | 文件安全 | 上传文件类型和大小限制,自动写入附件台账 | 详细技术方案见:[附件管理技术方案](../technical-solutions/attachment-management.md) ## 3. 系统监控 | 功能 | 说明 | |------|------| | 应用性能监控 | Laravel Pulse 提供性能监控(/pulse) | | 调试辅助 | Laravel Telescope 提供调试功能(/telescope,仅 local) | | 队列管理 | Laravel Horizon 提供队列管理(/horizon) | | 实时日志 | Laravel Pail 提供实时日志查看 | | WebSocket | Laravel Reverb 提供实时通信服务 | | 高性能服务 | Laravel Octane 支持 Swoole/RoadRunner | *** > 文档来源:PRD 第 3.5 节 --- --- url: https://www.aiforge.cn/architecture/tech-stack.md --- # 技术栈 | 技术 | 版本 | 用途 | |------|------|------| | PHP | ^8.4 | 编程语言 | | Laravel | ^13.x | Web 框架 | | Laravel Sanctum | ^4.x | API 认证 | | Spatie Laravel Permission | ^8.x | 权限管理 | | Laravel Horizon | ^5.x | 队列管理 | | Laravel Pulse | ^1.x | 性能监控 | | Laravel Telescope | ^5.x | 调试工具 | | Laravel Reverb | ^1.x | WebSocket 服务 | | Laravel Octane | ^2.x | 高性能应用服务 | | Laravel Scout | ^11.x | 全文搜索 | | Laravel Echo | ^2.x | 前端 WebSocket 客户端 | | Laravel AI | ^0.10.x | AI Agent 能力 | | larva/laravel-ai-volc | ^1.x | 火山方舟 AI 驱动(文本/向量/TTS 语音/Seedream 图像/Seedance 视频) | | Redis | - | 缓存和队列驱动 | | Predis | ^2.x | Redis 客户端 | | EasySMS | ^3.x | 短信发送 | | Intervention Image | ^4.x | 图像处理 | | Tailwind CSS | v4 | 前端样式 | | Vite | ^8.x | 前端构建 | | PHPUnit | ^12.x | 单元/功能测试 | | Laravel Pint | ^1.x | 代码格式化 | *** > 文档来源:PRD 第 4.1 节 --- --- url: https://www.aiforge.cn/architecture/project-structure.md --- # 项目结构 ``` laravel-skel-forge/ ├── app/ │ ├── Ai/ # AI 能力 │ │ ├── Agents/ # AI Agent(Assistant) │ │ └── Tools/ # AI 工具(Audit/Settings/Stats/System/Users/Web 等) │ ├── Console/ │ │ └── Commands/ # Artisan 命令 │ ├── Enums/ # 枚举类(含 Traits/HasLabel) │ │ ├── AppPlatform.php # 应用平台枚举(android/ios/harmony) │ │ ├── DictLabelType.php # 字典标签样式枚举 │ │ ├── DictStatus.php # 字典状态枚举 │ │ └── ReleaseStatus.php # 版本发布状态枚举(draft/published/withdrawn/obsolete) │ ├── Events/ # 事件类(Admin/、User/、System/ 等子目录) │ ├── Exceptions/ # 自定义异常 │ ├── Http/ │ │ ├── Controllers/ │ │ │ ├── Admin/ # 后台控制器(Auth、Admin、User、Role、Menu、Setting、Area、Attachment、Uploader、PhoneCode、MailCode、Notification、Chat、Dashboard、Monitor、Debug、Queue、AuditLog、ScheduleLog、AppRelease、AppVersion、Dict、DictItem、Main) │ │ │ └── Api/V1/ # API v1 控制器(Auth、Register、Common、User、App) │ │ ├── Middleware/ # 中间件(RecordAuditLog) │ │ ├── Requests/ # 表单请求验证(Admin/、Api/V1/) │ │ └── Resources/ # API 资源(Admin/、Api/V1/) │ ├── Jobs/ # 队列任务(User/DeleteAccessTokenJob、StatUserJob) │ ├── Listeners/ # 事件监听器(Admin/、System/、User/) │ ├── Mail/ # 邮件类(MailVerifyCode) │ ├── Mcp/ # MCP Server 与工具 │ │ ├── Servers/ # Server 定义(ApiServer) │ │ └── Tools/ # 按业务域分目录(User/Wallet/Common 等) │ ├── Models/ │ │ ├── Admin/ # 管理员模型(Admin、AdminMenu) │ │ ├── App/ # 应用版本模型(AppRelease、AppVersion) │ │ ├── Coin/ # 金币模型(CoinTrade) │ │ ├── Point/ # 积分模型(PointRecord、PointTrade) │ │ ├── Wallet/ # 钱包模型(WalletTrade、Recharge、WithdrawOrder,均为多态 owner) │ │ ├── System/ # 系统模型(Area、Attachment、AuditLog、Dict、DictItem、Setting、Role、Permission、Notification、MailCode、PhoneCode、ScheduleLog 等) │ │ ├── User/ # 用户扩展模型(UserExtra、UserProfile、UserStat) │ │ ├── Traits/ # 模型 Trait(DateTimeFormatter、HasSettingsProperty、HasUser、HasWalletBalance) │ │ ├── Model.php # 模型基类 │ │ └── User.php # 前台用户模型 │ ├── Observers/ # 模型观察者(UserObserver) │ ├── Policies/ # 授权策略(UserPolicy) │ ├── Providers/ # 服务提供者(App、DynamicConfig、Horizon、Telescope) │ ├── Rules/ # 自定义验证规则(PhoneRule、MailCaptchaRule 等) │ ├── Services/ # 业务服务(AttachmentService、SmsCaptchaService、WechatService 等) │ ├── Sms/ # 短信消息类(VerifyCodeMessage) │ └── Support/ # 辅助类(CoinHelper、PointHelper、UserHelper、WalletHelper) ├── bootstrap/ │ └── helpers.php # 全局辅助函数 ├── config/ # 配置文件 ├── database/ │ ├── factories/ # 模型工厂 │ ├── migrations/ # 数据库迁移 │ └── seeders/ # 数据填充 ├── lang/ # 语言文件(zh_CN、en) ├── public/ ├── resources/ ├── routes/ # 路由文件 │ ├── web.php # Web 路由 │ ├── admin.php # 后台路由(前缀 /admin,通过 bootstrap/app.php 注册) │ ├── api_v1.php # API v1 路由 │ ├── ai.php # MCP 路由(前缀 /mcp/v1) │ ├── channels.php # 广播频道 │ └── console.php # 定时任务 ├── stubs/ # 自定义 Artisan stub(agent、tool) ├── storage/ └── tests/ # 测试文件 ``` *** > 文档来源:PRD 第 4.2 节 --- --- url: https://www.aiforge.cn/architecture/architecture-design.md --- # 架构设计 ## 1. DDD 分层 项目采用领域驱动设计思想,按业务领域组织代码: | 层次 | 目录 | 职责 | |------|------|------| | **Models Layer** | `app/Models/` | 数据模型和业务逻辑,按领域分子目录(Admin/System/User/Coin/Point/Wallet) | | **Services Layer** | `app/Services/` | 业务服务封装,处理跨模型事务逻辑(如附件台账+Storage一致性) | | **Controllers Layer** | `app/Http/Controllers/` | HTTP 请求编排,调用 Service 和 Resource | | **Resources Layer** | `app/Http/Resources/` | API 响应格式化 | | **Requests Layer** | `app/Http/Requests/` | 请求参数校验 | ## 2. 请求流转 ``` HTTP Request → Route → Middleware (Auth/Permission/Audit) → FormRequest Validation → Controller → Service (业务逻辑) → Model (Eloquent) → Database → Resource (响应格式化) → JSON Response ``` ### AI 聊天特殊流转 ``` POST /admin/chat → ChatController → AI Agent (laravel/ai) → Tool 调用 → SSE Stream 响应 ↓ (敏感操作) 审批等待 → POST /admin/chat/approve → 继续执行 ``` ## 3. 目录按领域划分 模型层按业务领域分子目录,实现高内聚低耦合: | 目录 | 包含模型 | |------|---------| | `Admin/` | Admin、AdminMenu | | `App/` | AppRelease、AppVersion | | `Coin/` | CoinTrade | | `Content/` | Collection、Comment、Like | | `Operation/` | Announcement、Banner | | `Point/` | PointRecord、PointTrade | | `System/` | Area、Attachment、AuditLog、Dict、DictItem、Feedback、LoginHistory、MailCode、Permission、PersonalAccessToken、PhoneCode、Role、ScheduleLog、Setting、Social | | `User/` | Address、IdentityVerification、UserExtra、UserProfile、UserStat | | `UserSegment/` | Membership、Segment、SegmentTask | | `Wallet/` | WalletTrade、Recharge、WithdrawOrder | *** > 文档来源:PRD 第 4.3 节 --- --- url: https://www.aiforge.cn/database/core-tables.md --- # 核心数据表 ## 1. 管理员表 (admin_users) | 字段名 | 类型 | 说明 | |--------|------|------| | user_id | BIGINT | 主键(统一 user_id 命名) | | user_name | VARCHAR | 用户名 | | email | VARCHAR | 邮箱地址 | | phone | VARCHAR | 手机号 | | name | VARCHAR | 昵称/姓名 | | avatar | VARCHAR | 头像 URL | | status | TINYINT | 状态(0禁用,1启用) | | password | VARCHAR | 密码哈希 | | login_count | INT | 登录次数 | | last_login_ip | VARCHAR | 最后登录 IP | | last_login_at | TIMESTAMP | 最后登录时间 | | last_active_at | TIMESTAMP | 最后活动时间 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | | deleted_at | TIMESTAMP | 删除时间(软删除) | ## 2. 后台菜单表 (admin_menus) | 字段名 | 类型 | 说明 | |--------|------|------| | menu_id | BIGINT | 主键 | | parent_id | BIGINT | 父级菜单 ID | | path | VARCHAR | 路由路径 | | name | VARCHAR | 路由名称 | | component | VARCHAR | 组件路径 | | redirect | VARCHAR | 重定向路径 | | title | VARCHAR | 菜单标题 | | icon | VARCHAR | 菜单图标 | | type | TINYINT | 类型(0目录,1菜单,2按钮) | | permission | VARCHAR | 权限标识 | | is_enable | BOOLEAN | 是否启用 | | is_hide | BOOLEAN | 是否隐藏菜单 | | is_hide_tab | BOOLEAN | 是否隐藏标签 | | is_iframe | BOOLEAN | 是否 iframe 嵌入 | | keep_alive | BOOLEAN | 是否缓存 | | is_full_page | BOOLEAN | 是否全屏 | | fixed_tab | BOOLEAN | 是否固定标签 | | show_badge | BOOLEAN | 是否显示徽章 | | show_text_badge | VARCHAR | 徽章文本 | | active_path | VARCHAR | 激活路径 | | link | VARCHAR | 外部链接 | | sort | INT | 排序 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | ## 3. 用户表 (users) | 字段名 | 类型 | 说明 | |--------|------|------| | user_id | BIGINT | 主键 | | user_name | VARCHAR | 用户名 | | email | VARCHAR | 邮箱地址 | | mobile | VARCHAR | 手机号 | | avatar | VARCHAR | 头像 | | status | TINYINT | 状态 | | available_points | INT | 可用积分 | | available_coins | INT | 可用金币 | | balance | DECIMAL(16,2) | 钱包可用余额(元) | | frozen_balance | DECIMAL(16,2) | 钱包冻结余额(元) | | vip_expires_at | TIMESTAMP | VIP 过期时间 | | password | VARCHAR | 密码哈希 | | login_count | INT | 登录次数 | | last_login_ip | VARCHAR | 最后登录 IP | | last_login_at | TIMESTAMP | 最后登录时间 | | last_active_at | TIMESTAMP | 最后活动时间 | | email_verified_at | TIMESTAMP | 邮箱验证时间 | | mobile_verified_at | TIMESTAMP | 手机验证时间 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | | deleted_at | TIMESTAMP | 删除时间(软删除) | ## 4. 用户扩展表 | 表名 | 说明 | |------|------| | user_extras | 用户额外信息(性别、生日、签名等) | | user_profiles | 用户画像/偏好 | ## 5. 角色和权限表(spatie/laravel-permission) | 表名 | 说明 | |------|------| | roles | 角色表 | | permissions | 权限表 | | model_has_roles | 模型角色关联表(多态) | | model_has_permissions | 模型权限关联表(多态) | | role_has_permissions | 角色权限关联表 | ## 6. 系统配置表 (settings) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | name | VARCHAR | 配置名称 | | key | VARCHAR | 配置键名(唯一) | | value | TEXT | 配置值 | | cast_type | VARCHAR | 数据类型(string/int/float/bool/json) | | input_type | VARCHAR | 输入类型 | | param | TEXT | 参数配置(JSON) | | remark | VARCHAR | 备注 | | sort | INT | 排序 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | ## 7. 地区表 (areas) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | parent_id | BIGINT | 父级地区 ID | | name | VARCHAR | 地区名称 | | area_code | INT | 地区代码 | | city_code | VARCHAR | 城市代码 | | lat | DECIMAL | 纬度 | | lng | DECIMAL | 经度 | | level | TINYINT | 层级 | | sort | INT | 排序 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | ## 8. 验证码表 | 表名 | 说明 | 关键字段 | |------|------|----------| | phone_codes | 短信验证码 | phone、scene、code、state、ip、send_at | | mail_codes | 邮件验证码 | email、scene、code、state、ip、send_at | ## 9. 登录历史表 (login_histories) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | user_type | VARCHAR | 用户类型(多态:App\Models\User / App\Models\Admin\Admin) | | user_id | BIGINT | 用户 ID | | ip | VARCHAR | 登录 IP | | port | INT | 端口 | | platform | VARCHAR | 平台 | | device | VARCHAR | 设备 | | browser | VARCHAR | 浏览器 | | user_agent | TEXT | User-Agent | | address | VARCHAR | 地址 | | login_at | TIMESTAMP | 登录时间 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | ## 10. 附件表 (attachments) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键(从 10000000 起) | | uploader_type | VARCHAR | 上传者类型(多态) | | uploader_id | BIGINT | 上传者 ID | | disk | VARCHAR | 存储磁盘(local/public/s3 等) | | path | VARCHAR | 物理存储路径 | | name | VARCHAR | 显示名称 | | original_name | VARCHAR | 原始文件名 | | extension | VARCHAR | 文件扩展名 | | mime_type | VARCHAR | MIME 类型 | | type | VARCHAR | 附件类型(image/video/audio/document/other) | | size | BIGINT | 文件大小(字节) | | hash | CHAR(32) | 文件 MD5 哈希(用于去重校验) | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | | deleted_at | TIMESTAMP | 删除时间(软删除) | **索引**:`(uploader_type, uploader_id)` 复合索引、`type` 索引、`hash` 索引、`(disk, path)` 唯一索引。 ## 11. 审计日志表 (audit_logs) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | admin_id | BIGINT | 操作管理员 ID | | method | VARCHAR | HTTP 方法 | | path | VARCHAR | 请求路径 | | query | TEXT | Query 参数(JSON) | | body | TEXT | 请求体(JSON,脱敏) | | status_code | INT | 响应状态码 | | ip | VARCHAR | 请求 IP | | user_agent | VARCHAR | User-Agent | | duration_ms | INT | 请求耗时(毫秒) | | created_at | TIMESTAMP | 创建时间 | ## 12. 调度任务日志表 (schedule_logs) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | task_name | VARCHAR | 任务名称 | | task_type | VARCHAR | 任务类型(command/callback/exec) | | expression | VARCHAR | Cron 表达式 | | status | VARCHAR | 执行状态(running/success/failed/skipped) | | exit_code | INT | 退出码 | | duration_ms | INT | 执行耗时(毫秒) | | exception | TEXT | 异常信息摘要 | | started_at | TIMESTAMP | 开始时间 | | finished_at | TIMESTAMP | 结束时间 | | created_at | TIMESTAMP | 创建时间 | 通过 `Schedule::task` 的事件监听自动记录,支持按 `schedule.log_prunable_days` 设置自动清理。 ## 13. AI 对话表 | 表名 | 说明 | |------|------| | agent_conversations | AI 会话列表(管理员 ID、标题、模型、创建/更新时间) | | agent_messages | AI 消息记录(会话 ID、角色 user/assistant/tool、内容、工具调用信息等) | ## 14. 其他核心表 | 表名 | 说明 | |------|------| | personal_access_tokens | Sanctum 个人访问令牌 | | notifications | 站内通知(多态) | | socials | 第三方账号绑定 | | password_reset_tokens | 密码重置令牌 | *** > 文档来源:PRD 第 5.1 节 --- --- url: https://www.aiforge.cn/database/business-tables.md --- # 业务数据表 ## 1. 数据字典表 ### 1.1 字典类型表 (dicts) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | name | VARCHAR | 字典名称 | | code | VARCHAR | 字典编码(唯一) | | status | TINYINT | 状态(0 禁用,1 启用) | | sort | INT | 排序权重(越小越靠前) | | remark | VARCHAR | 备注 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | ### 1.2 字典数据表 (dict_items) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | dict_id | BIGINT | 所属字典 ID | | label | VARCHAR | 字典项标签 | | value | VARCHAR | 字典项值 | | label_type | VARCHAR | 标签样式(default/primary/success/warning/danger/info,对应 Element Plus Tag type) | | status | TINYINT | 状态(0 禁用,1 启用) | | sort | INT | 排序权重 | | remark | VARCHAR | 备注 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | > 删除字典时通过模型 `booted` 事件级联删除其所有字典项。 ## 2. 应用发布表 (app_releases) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | name | VARCHAR | 应用名称 | | slug | VARCHAR | 应用唯一标识(客户端传参用) | | description | VARCHAR | 应用描述 | | icon | VARCHAR | 应用图标 URL | | sort | INT | 排序权重 | | status | BOOLEAN | 启用状态 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | | deleted_at | TIMESTAMP | 删除时间(软删除) | ## 3. 应用版本表 (app_versions) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | app_release_id | BIGINT | 所属应用 ID | | platform | VARCHAR | 平台(android/ios/harmony,枚举 AppPlatform) | | version | VARCHAR | 语义化版本号(如 1.2.0) | | build | INT | 构建号(整数递增,用于版本比较) | | download_url | VARCHAR | 安装包下载地址 | | release_notes | TEXT | 更新说明 | | min_supported_version | VARCHAR | 最低支持版本号 | | min_supported_build | INT | 最低支持构建号(低于此构建号需强制更新) | | force_update | BOOLEAN | 是否强制更新 | | file_size | BIGINT | 安装包大小(字节) | | file_hash | VARCHAR | 文件哈希(校验用) | | status | VARCHAR | 发布状态(draft/published/withdrawn/obsolete,枚举 ReleaseStatus) | | published_at | TIMESTAMP | 发布时间 | | created_by | BIGINT | 创建人管理员 ID | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | | deleted_at | TIMESTAMP | 删除时间(软删除) | > 发布版本时会自动将同应用同平台下其他已发布版本置为 `obsolete`;已发布状态的版本不可删除,需先撤回或废弃。 **版本状态流转**:`draft` → `published`(publish)→ `withdrawn`(withdraw)/ `obsolete`(obsolete 或被新版本自动替换)。 ## 4. 内容互动表 ### 4.1 点赞表 (likes) | 字段 | 类型 | 说明 | |------|------|------| | id | bigint | 主键 | | user_id | bigint | 点赞用户 | | source_type / source_id | string / bigint | 多态目标(morphs) | | extra | json | 扩展信息 | | created_at / updated_at | timestamp | 时间戳 | * 唯一约束 `uniq_user_like`:`user_id + source_type + source_id`,同一用户对同一资源只能点赞一次 * 模型事件:`created` 时对目标 `like_count` 自增,`deleted` 时自减 ### 4.2 收藏表 (collections) | 字段 | 类型 | 说明 | |------|------|------| | id | bigint | 主键 | | user_id | bigint | 收藏用户 | | source_type / source_id | string / bigint | 多态目标(morphs) | | extra | json | 扩展信息 | | created_at / updated_at | timestamp | 时间戳 | * 唯一约束 `uniq_user_collection`:`user_id + source_type + source_id` * 模型事件:`created` 时对目标 `collection_count` 自增,`deleted` 时自减 ### 4.3 评论表 (comments) | 字段 | 类型 | 说明 | |------|------|------| | id | bigint | 主键 | | user_id | bigint | 评论用户 | | source_type / source_id | string / bigint | 多态目标(自关联可实现楼层回复) | | is_top | boolean | 是否置顶 | | status | string | 审核状态 | | like_count / comment_count | int | 点赞数 / 回复数 | | content | string(1000) | 评论内容 | | mentioned_users | json | 艾特用户 ID 列表 | | ip_address | ip | 评论 IP | | created_at | timestamp | 评论时间(无 `updated_at`) | ### 4.4 互动能力 Trait 被互动的业务模型通过 trait 接入,目标表需自备对应计数列: | Trait | 提供方法 | 依赖计数列 | |-------|---------|-----------| | `HasLikeable` | `likes()`、`isLikedBy($user)` | `like_count` | | `HasCollectionable` | `collections()`、`isCollectedBy($user)` | `collection_count` | | `HasCommentable` | `comments()` | `comment_count` | * `source_type` 仅接受 `config/morph_maps.php` 中注册的多态别名 * 计数列由模型事件自动维护,接入前须确认目标表已有对应字段 * **评论楼层回复约定**:当 `source_type = 'comment'` 时表示本条为对另一条评论的回复,`source_id` 即父评论 ID;顶级评论的 `source_type` 指向业务实体(如 `user`、`post`)。后台判定回复关系与查询回复链均基于此约定,无需额外 `parent_id` 字段 ### 4.5 访问记录表 (visits) | 字段 | 类型 | 说明 | |------|------|------| | id | bigint | 主键 | | user_id | bigint | 访问用户 | | source_type / source_id | string(255) / bigint | 多态目标(无 morphs,自行维护) | | ip | varbinary(16) | 访问 IP(与 `login_histories` 一致,支持 IPv4/IPv6) | | created_at | timestamp | 访问时间(无 `updated_at`,append-only) | * 索引:`idx_visit_source`(`source_type`,`source_id`)、`idx_visit_user_source`(`user_id`,`source_type`,`source_id`) * **不做唯一约束**:同一用户对同一资源可重复记录访问(PV 语义) * 仅 Api 模块提供记录与查询接口,Admin 后台暂不实现 * 模型 `Visit` 关闭 `updated_at`,仅 `created_at` 由数据库 `CURRENT_TIMESTAMP` 填充 ## 5. 钱包相关表(真实货币) > 设计原则:**余额热数据落在持有者主表字段上(查询零 Join),记录层全部多态**。 > 未来其他模型(如 `admin_users`、未来的 `merchants`)需要支持钱包时,只需在其表追加 `balance` / `frozen_balance` 两个字段并 `use HasWalletBalance`,记录层无需任何改动。 ### 5.1 钱包流水表 (wallet_trades) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键(从 10000000 起) | | owner_type | VARCHAR(64) | 持有者类型(多态,取 `config/morph_maps.php` 别名,如 `user`) | | owner_id | BIGINT | 持有者 ID | | amount | DECIMAL(16,2) | 本次变动金额(正增负减) | | before_balance | DECIMAL(16,2) | 变动前可用余额快照 | | after_balance | DECIMAL(16,2) | 变动后可用余额快照 | | before_frozen | DECIMAL(16,2) | 变动前冻结余额快照 | | after_frozen | DECIMAL(16,2) | 变动后冻结余额快照 | | type | VARCHAR(32) | 交易类型(枚举 `WalletTradeType`) | | source_type | VARCHAR(64) | 业务来源类型(多态,如订单、提现单) | | source_id | BIGINT | 业务来源 ID | | status | VARCHAR(20) | 流水状态(pending/success/failed) | | description | VARCHAR(255) | 交易描述 | | created_at | TIMESTAMP | 交易时间 | **索引**:`(owner_type, owner_id, created_at)` 复合索引、`(source_type, source_id)` 复合索引、`type` 索引。 > 冻结/解冻操作只变动冻结余额而不动可用余额,因此必须同时保留 `before_frozen` / `after_frozen` 快照,否则无法完整审计对账。 ### 5.2 充值订单表 (recharges) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | owner_type / owner_id | VARCHAR(64) / BIGINT | 持有者(多态) | | order_no | VARCHAR(64) | 订单号(唯一,前缀 `R` + 日期 + 随机串) | | amount | DECIMAL(16,2) | 充值金额 | | bonus_amount | DECIMAL(16,2) | 赠送金额(按 `wallet.recharge_bonus_rate` 计算) | | pay_channel | VARCHAR(32) | 支付渠道(manual/wechat/alipay/apple,当前仅 manual 可用) | | pay_status | VARCHAR(20) | 支付状态(pending/success/failed/expired/closed) | | transaction_id | VARCHAR(128) | 渠道流水号(唯一,防重复入账;手动确认为空) | | confirmed_by | BIGINT | 手动确认到账的管理员 ID | | paid_at | TIMESTAMP | 支付完成时间 | | expired_at | TIMESTAMP | 订单过期时间(默认创建后 30 分钟) | | client_ip | VARCHAR(45) | 下单 IP | | remark | VARCHAR(255) | 备注 | | callback_payload | JSON | 渠道原始回调报文(渠道接入后使用) | | created_at / updated_at | TIMESTAMP | 时间戳 | **索引**:`order_no` 唯一索引、`transaction_id` 唯一索引、`(owner_type, owner_id, created_at)` 复合索引、`pay_status` 索引、`expired_at` 索引。 ### 5.3 提现申请表 (withdraws) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键(从 10000000 起) | | owner_type / owner_id | VARCHAR(64) / BIGINT | 持有者(多态,别名 `user`) | | order_no | VARCHAR(64) | 提现单号(唯一,前缀 `W` + YmdHis + 6 位随机) | | amount | DECIMAL(16,2) | 申请提现金额 | | fee | DECIMAL(16,2) | 手续费(按 `wallet.withdraw_fee_rate` 百分比计算,默认 0) | | actual_amount | DECIMAL(16,2) | 实际到账金额(amount - fee) | | account_type | VARCHAR(32) | 收款账户类型(枚举 `WithdrawAccountType`:wechat/alipay/bank) | | account_info | JSON | 收款账户信息(账号、姓名等,响应脱敏展示) | | status | VARCHAR(20) | 状态(枚举 `WithdrawStatus`,默认 pending) | | reviewed_by | BIGINT | 审核管理员 ID(nullable) | | reviewed_at | TIMESTAMP | 审核时间(nullable) | | reject_reason | VARCHAR(255) | 驳回原因(nullable) | | transfer_at | TIMESTAMP | 打款完成时间(nullable) | | fail_reason | VARCHAR(255) | 打款失败原因(nullable) | | remark | VARCHAR(255) | 管理员备注(nullable) | | client_ip | VARCHAR(45) | 下单 IP(nullable) | | created_at / updated_at | TIMESTAMP | 时间戳 | **索引**:`order_no` 唯一索引、`idx_withdraw_owner_at (owner_type, owner_id, created_at)` 复合索引、`idx_withdraw_status (status)`。 **状态流转**:`pending` →(审核通过)`approved` →(打款成功)`success`;`pending` →(审核驳回)`rejected`(自动解冻);`approved` →(打款失败)`failed`(自动解冻)。`rejected`/`success`/`failed` 为终态。 ### 5.4 钱包相关枚举 **WalletTradeType(钱包交易类型)**: | 值 | 说明 | |----|------| | recharge | 充值 | | withdraw | 提现 | | freeze | 冻结 | | unfreeze | 解冻 | | payment | 消费 | | refund | 退款 | | admin_recharge | 后台充正 | | admin_deduct | 后台扣减 | **WithdrawStatus(提现状态)**: | 值 | 说明 | |----|------| | pending | 待审核 | | approved | 审核通过,待打款 | | rejected | 已驳回(自动解冻退回) | | success | 打款成功(结算冻结,资金流出) | | failed | 打款失败(自动解冻退回) | **WithdrawAccountType(收款账户类型)**: | 值 | 说明 | |----|------| | wechat | 微信 | | alipay | 支付宝 | | bank | 银行卡 | **与现有积分/金币体系的关系**:钱包是独立的**真实货币**账本,与现有 `available_points`(积分)、`available_coins`(金币)两套虚拟资产体系**并行且互不影响**,本期不做迁移或合并。 ## 6. 虚拟资产流水表 | 表名 | 说明 | |------|------| | coin_trades | 金币流水记录 | | point_records | 积分记录 | | point_trades | 积分流水记录 | ## 7. 实名认证表 (identity_verifications) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | verifiable_type | VARCHAR(100) | 申请人类型(多态) | | verifiable_id | BIGINT | 申请人 ID | | type | VARCHAR(16) | 认证类型(personal 个人 / enterprise 企业) | | status | VARCHAR(16) | 审核状态(pending/review/approved/rejected,默认 pending) | | real_name | VARCHAR(64) | 真实姓名(个人认证) | | id_card_no | VARCHAR(32) | 身份证号(密文存储,响应隐藏) | | id_card_front | VARCHAR | 身份证人像面图片路径 | | id_card_back | VARCHAR | 身份证国徽面图片路径 | | handheld | VARCHAR | 手持身份证图片路径 | | company_name | VARCHAR(128) | 企业名称(企业认证) | | credit_code | VARCHAR(32) | 统一社会信用代码(18 位) | | legal_person_name | VARCHAR(64) | 法人姓名(企业认证) | | legal_person_id_card_no | VARCHAR(32) | 法人身份证号(密文存储,响应隐藏) | | business_license | VARCHAR | 营业执照图片路径 | | opening_permit | VARCHAR | 开户许可证图片路径 | | bank_account | VARCHAR(64) | 对公账户(响应隐藏) | | bank_name | VARCHAR(64) | 开户行 | | admin_id | BIGINT | 审核管理员 ID | | reject_reason | VARCHAR(32) | 驳回原因(字典 `identity_reject_reason` 的 value) | | reject_remark | TEXT | 驳回备注 | | submitted_at | TIMESTAMP | 提交时间 | | reviewed_at | TIMESTAMP | 审核时间 | | expires_at | TIMESTAMP | 认证有效期(通过后自动 +3 年) | | created_at / updated_at | TIMESTAMP | 时间戳 | **索引**:`(verifiable_type, verifiable_id)` 复合索引、`type` 索引、`status` 索引、`admin_id` 索引。 > 敏感字段(`id_card_no`、`legal_person_id_card_no`、`bank_account`)通过模型 `#[Hidden]` 在所有响应中自动隐藏。驳回原因通过预置字典 `identity_reject_reason` 管理,迁移时自动 seed。 **users 表关联字段**: | 字段名 | 类型 | 说明 | |--------|------|------| | identity_type | VARCHAR(16) | 实名认证类型(personal/enterprise),null 表示未认证 | | identity_status | VARCHAR(16) | 实名认证状态(默认 unsubmitted,索引) | > 用户表直接存储认证状态热数据,查询零 Join。`identity_verified` 为逻辑属性,由 `identity_status === approved` 派生。 详见 [实名认证技术方案](/technical-solutions/identity-verification)。 ## 8. 用户分群表 ### 8.1 分群定义表 (user_segments) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键(从 10000000 起) | | code | VARCHAR(64) | 分群编码(规则分群必填且唯一,手动可空) | | name | VARCHAR(64) | 分群名称 | | label_type | VARCHAR(16) | 标签样式(default/primary/success/warning/danger/info) | | type | TINYINT | 分群类型(1=手动,2=规则) | | group | VARCHAR(32) | 分组(用户画像/运营分群/风控 等) | | description | VARCHAR(500) | 分群说明 | | sort | INT | 排序权重(越小越靠前) | | status | BOOLEAN | 启用状态(0=禁用,1=启用) | | rule | JSON | 规则定义(仅 type=2 有效,结构含 version/logic/conditions) | | last_synced_at | TIMESTAMP | 最近一次规则刷新时间 | | last_synced_user_count | INT | 最近一次规则匹配用户数 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | **索引**:`code` 唯一索引、`type` 索引、`group` 索引、`status` 索引。 ### 8.2 分群成员表 (user_segment_memberships) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | segment_id | BIGINT | 分群 ID(外键,级联删除) | | user_id | BIGINT | 用户 ID(外键,级联删除) | | source | TINYINT | 打标来源(1=手动,2=规则) | | created_at | TIMESTAMP | 打标时间 | **索引**:`uniq_segment_user` 唯一约束(segment_id + user_id)、`idx_user_segment`(user_id + segment_id)、`idx_segment_source_time`(segment_id + source + created_at)。 > 用户软删时通过 `UserObserver` 自动清理分群成员记录。规则同步增量 diff 仅操作 `source=2` 的成员,不影响手动打标的成员。 ### 8.3 分群异步任务表 (segment_tasks) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | admin_id | BIGINT | 提交任务的管理员 ID | | type | VARCHAR(32) | 任务类型(attach 打标 / detach 解标) | | payload | JSON | 任务载荷(segment_ids + user_ids) | | total_count | INT | 计划处理总数 | | processed_count | INT | 已处理数 | | added_count | INT | 实际新增/解绑数 | | status | VARCHAR(16) | 状态(pending/running/completed/failed) | | error_message | VARCHAR(1000) | 失败原因 | | started_at | TIMESTAMP | 开始执行时间 | | finished_at | TIMESTAMP | 完成时间 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | **索引**:`admin_id` 索引、`type` 索引、`status` + `created_at` 复合索引。 > 当批量打标/解标操作超过 `BATCH_LIMIT=1000` 时自动创建任务记录并派发 `BulkSegmentTaskJob`,任务完成后发送站内信通知管理员。 ## 9. 举报表 (reports) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | user_id | BIGINT | 举报人用户 ID | | reportable_id | BIGINT | 被举报对象 ID | | reportable_type | VARCHAR(255) | 被举报对象多态类型(morph alias:`comment` / `user`,由 `Relation::enforceMorphMap()` 全局别名统一管理) | | category | VARCHAR(32) | 举报分类(取自字典 `report_category`) | | reason | VARCHAR(500) | 举报描述(可空) | | evidence | JSON | 证据图片 URL 数组(可空) | | status | VARCHAR(16) | 处理状态(pending/processing/resolved/rejected) | | handler_id | BIGINT | 处理人管理员 ID(可空) | | process_remark | VARCHAR(500) | 处理备注(可空) | | process_actions | JSON | 处置动作记录(可空,格式见 §10) | | processed_at | TIMESTAMP | 处理时间(可空) | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间(列存在,但模型声明 `UPDATED_AT = null` 不自动维护,值恒为 NULL) | **索引**: * 主键 * `reportable_type + reportable_id` 复合索引(多态查询) * `status + category + created_at` 复合索引(筛选) * `handler_id + processed_at` 复合索引(按处理人查询) * `user_id + status` 复合索引(用户自己的举报列表) **字典**:`report_category`(举报分类),固定 9 项:色情低俗、暴力血腥、政治敏感、广告营销、辱骂人身攻击、虚假信息、侵权抄袭、违法违规、其他。 **唯一性约束**:业务层校验同一 `(user_id, reportable_type, reportable_id)` 在 `pending`/`processing` 状态下不可重复提交,数据库层不强制。 ## 10. process_actions JSON 协议 `reports.process_actions` 为 JSON 数组,每项结构: ```json [ { "type": "hide_comment", "reason": null }, { "type": "delete_comment", "reason": null }, { "type": "freeze_user", "reason": null } ] ``` | 字段 | 类型 | 说明 | |------|------|------| | type | string | 处置动作类型:`hide_comment` / `delete_comment` / `freeze_user` | | reason | string | null | 动作说明(可空) | ## 11. 系统基础表 | 表名 | 说明 | |------|------| | sessions | 会话存储 | | cache / cache_locks | 缓存/缓存锁 | | jobs / job_batches / failed_jobs | 队列任务/批次/失败任务 | | pulse_values / pulse_entries / pulse_aggregates | Laravel Pulse 监控数据 | ## 12. AI 知识库表 ### 12.1 知识库文档表 (knowledge_documents) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | title | VARCHAR(200) | 文档标题 | | content | LONGTEXT | 文档原始内容 | | status | VARCHAR(20) | 索引状态(pending/processing/ready/failed,枚举 KnowledgeDocumentStatus),带索引 | | is_enabled | BOOLEAN | 是否启用(停用的文档不参与检索) | | chunks_count | UNSIGNED INT | 分块数量 | | tokens | UNSIGNED INT | 向量化消耗的 Tokens | | error | VARCHAR(500) | 最近一次索引失败原因(可空) | | created_by | BIGINT | 创建管理员 ID(可空,带索引) | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | > 文档保存后自动派发 `ProcessKnowledgeDocumentJob` 异步索引任务(段落分块 + Embedding 向量化);内容变更自动重建,亦可手动 reindex。索引状态流转:`pending` → `processing` → `ready`/`failed`。 ### 12.2 知识库文档分块表 (knowledge_chunks) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | document_id | BIGINT | 所属文档 ID(外键级联删除,带索引) | | content | TEXT | 分块文本内容 | | embedding | JSON | 向量(JSON 数组,维度由配置的 Embedding 模型决定,默认 2048) | | tokens | UNSIGNED INT | 该分块向量化消耗的 Tokens | | sort | UNSIGNED SMALLINT | 分块在文档内的顺序 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | > 语义检索时将查询文本向量化,与分块向量做余弦相似度计算,按得分降序返回 Top-N;仅召回「已完成索引且启用」文档的分块。 *** > 文档来源:PRD 第 5.1 节 --- --- url: https://www.aiforge.cn/api/response-format.md --- # 响应格式与 HTTP 状态码规范 ## 1. 响应格式 项目使用 Laravel 原生响应 + Eloquent API Resources,**不使用统一 code/msg/data 包装**。调用 `JsonResource::withoutWrapping()` 取消默认 data 包裹,通过 HTTP 状态码表达结果。 ### 1.1 单条资源(GET 详情、POST 创建) ```json { "id": 1, "name": "...", "...": "其他字段" } ``` ### 1.2 分页列表(GET 列表) ```json { "data": [ { "...": "资源对象" } ], "links": { "first": "...", "last": "...", "prev": null, "next": "..." }, "meta": { "current_page": 1, "per_page": 15, "total": 100 } } ``` ### 1.3 操作成功(PUT/PATCH/DELETE) `204 No Content` 或 `{ "message": "操作成功" }` ### 1.4 认证成功(POST login) ```json { "access_token": "1|laravel_sanctum_token_xxx", "user": { "user_id": 1, "user_name": "admin", "...": "..." } } ``` ### 1.5 错误响应 ```json // 401 未认证 { "message": "Unauthenticated." } // 403 无权限 { "message": "This action is unauthorized." } // 422 验证失败 { "message": "The given data was invalid.", "errors": { "field": ["错误信息"] } } ``` ## 2. HTTP 状态码规范 | 状态码 | 含义 | |--------|------| | 200 | 成功(查询/更新/同步二进制下载) | | 201 | 创建成功 | | 202 | 已接受(异步入队,如大批量导出任务,响应体携带任务入队提示消息) | | 204 | 删除/操作成功,无返回内容 | | 400 | 请求参数错误 | | 401 | 未认证或 Token 过期 | | 403 | 无权限访问 | | 404 | 资源不存在 | | 422 | 数据验证失败 | | 429 | 请求频率超限 | | 500 | 服务器错误 | *** > 文档来源:PRD 第 6.1、6.2 节 --- --- url: https://www.aiforge.cn/api/admin-api.md --- # 后台管理接口(/admin) 所有后台接口前缀为 `/admin`,需 `Authorization: Bearer {token}` 请求头。 > 在线接口文档(Apifox,可在线调试): ## 1. 认证接口(/admin/auth) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | POST | `/admin/auth/login` | 管理员登录(账号+密码) | 否 | | POST | `/admin/auth/wx-login` | 微信公众号授权登录 | 否 | | POST | `/admin/auth/apple-login` | Apple 授权登录 | 否 | | POST | `/admin/auth/refresh-token` | 刷新 Token | 是 | | GET | `/admin/auth/tokens` | 查询已签发的所有 Token | 是 | | DELETE | `/admin/auth/tokens/{tokenId}` | 销毁指定 Token | 是 | | POST | `/admin/auth/logout` | 退出登录 | 是 | | GET | `/admin/auth/info` | 获取当前管理员信息(含角色/按钮权限) | 是 | ## 2. 管理员管理(/admin/admins) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/admins` | 管理员列表(分页) | admins.index | | POST | `/admin/admins` | 创建管理员(支持传入头像 `avatar`) | admins.create | | GET | `/admin/admins/{id}` | 管理员详情 | admins.index | | PUT | `/admin/admins/{id}` | 更新管理员(支持传入头像 `avatar`) | admins.edit | | DELETE | `/admin/admins/{id}` | 删除管理员 | admins.delete | | GET | `/admin/admins/profile` | 当前管理员资料 | - | | PUT | `/admin/admins/profile` | 更新当前管理员资料 | - | | POST | `/admin/admins/avatar` | 更新头像 | - | | PUT | `/admin/admins/change-password` | 修改当前管理员密码 | - | | GET | `/admin/admins/{id}/roles` | 获取管理员角色 | admins.edit | | PUT | `/admin/admins/{id}/roles` | 分配管理员角色 | admins.edit | | PUT | `/admin/admins/{id}/toggle-status` | 切换管理员状态 | admins.edit | | PUT | `/admin/admins/{id}/reset-password` | 重置管理员密码 | admins.edit | | GET | `/admin/admins/{id}/login-histories` | 管理员登录历史 | admins.index | ## 3. 前台用户管理(/admin/users) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/users` | 用户列表(分页) | users.index | | GET | `/admin/users/{id}` | 用户详情 | users.index | | PUT | `/admin/users/{id}` | 更新用户信息 | users.edit | | DELETE | `/admin/users/{id}` | 删除用户 | users.delete | | PUT | `/admin/users/{id}/toggle-status` | 切换用户状态 | users.edit | | PUT | `/admin/users/{id}/reset-password` | 重置用户密码 | users.edit | | PUT | `/admin/users/{id}/reset-contact` | 重置用户手机号/邮箱 | users.edit | | PUT | `/admin/users/{id}/adjust-balance` | 调整用户积分/金币余额 | users.edit | | PUT | `/admin/users/{id}/extend-vip` | 延长 VIP 有效期 | users.edit | | GET | `/admin/users/{id}/login-histories` | 用户登录历史 | users.index | | GET | `/admin/users/{id}/socials` | 用户第三方绑定 | users.index | | GET | `/admin/users/{id}/point-trades` | 用户积分流水 | users.index | | GET | `/admin/users/{id}/coin-trades` | 用户金币流水 | users.index | | GET | `/admin/users/export` | 导出用户列表(xlsx) | users.export | | GET | `/admin/users/{id}/point-trades/export` | 导出用户积分流水(xlsx) | users.export | | GET | `/admin/users/{id}/coin-trades/export` | 导出用户金币流水(xlsx) | users.export | | GET | `/admin/users/{user}/wallet` | 指定用户钱包概览 | users.index | | GET | `/admin/users/{user}/wallet-trades` | 指定用户钱包流水(分页) | users.index | | PUT | `/admin/users/{user}/adjust-wallet` | 后台调整用户余额 | wallet.adjust | ## 4. 角色管理(/admin/roles) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/roles` | 角色列表 | roles.index | | POST | `/admin/roles` | 创建角色 | roles.create | | GET | `/admin/roles/{id}` | 角色详情 | roles.index | | PUT | `/admin/roles/{id}` | 更新角色 | roles.edit | | DELETE | `/admin/roles/{id}` | 删除角色 | roles.delete | | GET | `/admin/roles/permissions` | 获取全部权限列表 | roles.index | | GET | `/admin/roles/{id}/permissions` | 获取角色权限 | roles.index | | PUT | `/admin/roles/{id}/permissions` | 分配角色权限 | roles.edit | ## 5. 菜单管理(/admin/menus) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/menus` | 获取菜单树 | menus.index | | POST | `/admin/menus` | 创建菜单 | menus.create | | GET | `/admin/menus/{id}` | 菜单详情 | menus.index | | PUT | `/admin/menus/{id}` | 更新菜单 | menus.edit | | DELETE | `/admin/menus/{id}` | 删除菜单 | menus.delete | | GET | `/admin/routes` | 获取前端路由配置(按当前管理员权限过滤) | - | ## 6. 缓存管理(/admin/cache) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | POST | `/admin/cache/clear` | 清理应用缓存(types=all/settings,支持数组多选) | 是 | ## 7. 配置管理(/admin/settings) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/settings` | 配置列表(分页) | settings.index | | POST | `/admin/settings` | 创建配置 | settings.create | | GET | `/admin/settings/{id}` | 配置详情 | settings.index | | PUT | `/admin/settings/{id}` | 更新配置 | settings.edit | | DELETE | `/admin/settings/{id}` | 删除配置 | settings.delete | | GET | `/admin/settings/groups` | 获取配置分组列表 | settings.index | | GET | `/admin/settings/input-types` | 获取支持的输入类型 | settings.index | | PUT | `/admin/settings/batch` | 批量更新配置值 | settings.edit | ## 8. 字典管理(/admin/dicts) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/dicts` | 字典类型列表(分页,支持关键字/状态筛选) | dicts.index | | POST | `/admin/dicts` | 创建字典类型 | dicts.create | | GET | `/admin/dicts/{id}` | 字典详情(含字典项) | dicts.index | | PUT | `/admin/dicts/{id}` | 更新字典 | dicts.edit | | DELETE | `/admin/dicts/{id}` | 删除字典(级联删除字典项) | dicts.delete | ## 9. 字典数据管理(/admin/dict-items) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/dict-items` | 字典项列表(分页,支持按 dict_id 筛选) | dict-items.index | | POST | `/admin/dict-items` | 创建字典项 | dict-items.create | | GET | `/admin/dict-items/{id}` | 字典项详情 | dict-items.index | | PUT | `/admin/dict-items/{id}` | 更新字典项 | dict-items.edit | | DELETE | `/admin/dict-items/{id}` | 删除字典项 | dict-items.delete | ## 10. 地区管理(/admin/areas) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/areas` | 获取地区树 | areas.index | | POST | `/admin/areas` | 创建地区 | areas.create | | GET | `/admin/areas/{id}` | 地区详情 | areas.index | | PUT | `/admin/areas/{id}` | 更新地区 | areas.edit | | DELETE | `/admin/areas/{id}` | 删除地区 | areas.delete | ## 11. 通知管理(/admin/notifications) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | GET | `/admin/notifications` | 通知列表(分页) | 是 | | GET | `/admin/notifications/unread` | 未读通知列表/未读数 | 是 | | PUT | `/admin/notifications/mark-read` | 标记单条已读 | 是 | | PUT | `/admin/notifications/mark-all-read` | 全部标记已读 | 是 | | DELETE | `/admin/notifications/clear-read` | 清理已读通知 | 是 | ## 12. 评论管理(/admin/comments) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/comments` | 评论列表(分页,支持 `status`/`source_type`/`keyword`/`user_id` 过滤) | comments.index | | GET | `/admin/comments/{comment}` | 评论详情(含 `ip_address`、`is_reply`、`parent_comment` 上级评论摘要) | comments.index | | GET | `/admin/comments/{comment}/replies` | 回复列表(分页,返回该评论下的直接回复) | comments.index | | POST | `/admin/comments/{comment}/review` | 审核评论(`status` 取 `approved`/`rejected`/`review`,可选 `remark`) | comments.edit | | POST | `/admin/comments/{comment}/ai-review` | AI 敏感词命中智能复核(先本地词库检测命中词,再由 AI 语境复核减少误杀;返回 `suggestion`(approve/reject)/`reason`/`hits`,仅辅助建议不改状态) | comments.edit | | DELETE | `/admin/comments/{comment}` | 删除评论 | comments.delete | | GET | `/admin/comments/export` | 导出评论列表(xlsx) | comments.export | > 评论父子关系通过多态自关联实现:回复的 `source_type='comment'`、`source_id` 指向父评论 ID。列表接口不返回 `ip_address`,仅详情接口返回。 ## 13. 实名认证(/admin/identity-verifications) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/identity-verifications` | 认证记录列表(分页,支持 `keyword`/`user_id`/`type`/`status`/`start_date`/`end_date` 过滤) | identity-verifications.index | | GET | `/admin/identity-verifications/stats` | 认证统计(总量/待审/今日新增/已通过/已驳回) | identity-verifications.index | | GET | `/admin/identity-verifications/{verification}` | 认证记录详情(含证件附件 URL) | identity-verifications.index | | POST | `/admin/identity-verifications/{verification}/approve` | 审核通过(同步 `users.identity_verified`/`identity_type`) | identity-verifications.edit | | POST | `/admin/identity-verifications/{verification}/reject` | 审核驳回(必填 `reason`、可选 `remark`) | identity-verifications.edit | > 同一用户同时仅允许一条 `pending`/`review` 记录;已认证用户禁止再次提交,驳回后可重新提交。 ## 14. 反馈管理(/admin/feedbacks) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/feedbacks` | 反馈列表(分页,支持 `keyword`/`type`/`status`/`priority` 过滤) | feedbacks.index | | GET | `/admin/feedbacks/stats` | 反馈统计(总量、待推进、今日新增、已闭环) | feedbacks.index | | GET | `/admin/feedbacks/{feedback}` | 反馈详情(含附件图片、客户端信息) | feedbacks.index | | PUT | `/admin/feedbacks/{feedback}` | 处理反馈(调整 `status`/`priority`、填写 `admin_remark`) | feedbacks.edit | | POST | `/admin/feedbacks/{feedback}/draft-reply` | AI 草拟处理回复(按反馈类型/标题/内容生成 `draft` 草稿,不修改反馈) | feedbacks.edit | | DELETE | `/admin/feedbacks/{feedback}` | 删除反馈 | feedbacks.delete | | GET | `/admin/feedbacks/export` | 导出反馈列表(xlsx) | feedbacks.export | ## 14b. 举报管理(/admin/reports) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/reports` | 举报列表(分页,支持 `keyword`/`status`/`category`/`reportable_type`/`handler_id`/`start_date`/`end_date` 过滤) | reports.index | | GET | `/admin/reports/stats` | 举报统计(总量、待处理、今日新增、已结案) | reports.index | | GET | `/admin/reports/{report}` | 举报详情(含 `reason`、`evidence`、`reportable_snapshot` 被举报对象快照、`process_actions`/`process_remark` 处理记录) | reports.index | | PUT | `/admin/reports/{report}` | 处理举报(`status` ∈ `resolved`/`rejected`,可选 `remark`,可选 `actions` 联动处置数组:含 `type` ∈ `hide_comment`/`delete_comment`/`freeze_user` 与可选 `reason`;冻结为永久,不携带时长字段,需管理员人工解冻) | reports.edit | | POST | `/admin/reports/{report}/ai-triage` | AI 举报智能初判(结合举报分类/描述/被举报内容返回 `suggestion`(uphold/reject)/`reason`,仅辅助建议不改状态;已结案返回 422) | reports.edit | | GET | `/admin/reports/export` | 导出举报列表(xlsx,同步/异步双通道,阈值 1000 行) | reports.export | > * 被举报对象使用多态 `reportable_type` 存储 morph alias(`comment` / `user`,由 `Relation::enforceMorphMap()` 全局别名统一管理;列表筛选参数 `reportable_type` 同样按 alias 取值),详情接口返回按 `reportable_snapshot.type` 区分的快照(`comment`/`user`),对象已被删除时返回 `null`。 > * 联动处置仅在 `status=resolved` 时执行;`status=rejected` 不下发动作。 > * 冻结用户为永久冻结(仅设置 `status=FROZEN`,用户表未存储冻结截止时间),需管理员通过「用户管理 → 状态切换」人工解冻。 > * 同一管理员连续处理的中间状态由 `pending → processing` 自动流转,避免多人重复操作;已结案(resolved/rejected)的举报不可再次处理。 > * 状态机:`pending`(待处理) → `processing`(处理中,自动) → `resolved`(已处理)/ `rejected`(已驳回,管理员手动) ## 15. 公共选项(/admin/common) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | GET | `/admin/common/source-types` | 多态源类型选项列表(基于 `config/morph_maps.php`,返回 `[{label, value}]`) | 是 | > 与业务模块无关的通用选项、枚举、字典类接口统一由 `CommonController` 承载,仅需登录态,不绑定业务权限。 ## 16. 验证码管理 | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/phone-codes` | 短信验证码列表 | phone-codes.index | | GET | `/admin/phone-codes/{id}` | 短信验证码详情 | phone-codes.index | | GET | `/admin/mail-codes` | 邮件验证码列表 | mail-codes.index | | GET | `/admin/mail-codes/{id}` | 邮件验证码详情 | mail-codes.index | ## 17. 上传管理(/admin/uploader) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | POST | `/admin/uploader/file` | 上传通用文件 | 是 | | POST | `/admin/uploader/image` | 上传图片 | 是 | | POST | `/admin/uploader/video` | 上传视频 | 是 | | POST | `/admin/uploader/apk` | 上传 APK 安装包 | 是 | | POST | `/admin/uploader/token` | 获取直传 Token(S3 兼容) | 是 | 上传成功后返回字段包含附件 `id`,前端可直接用于附件管理。 ## 18. 附件管理(/admin/attachments) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/attachments/disks` | 获取可用存储磁盘列表 | attachments.index | | GET | `/admin/attachments` | 附件列表(分页+筛选) | attachments.index | | POST | `/admin/attachments/register` | 直传完成后登记元数据 | attachments.create | | GET | `/admin/attachments/{id}` | 附件详情 | attachments.index | | PUT | `/admin/attachments/{id}/rename` | 重命名显示名 | attachments.edit | | PUT | `/admin/attachments/{id}/move` | 移动物理路径 | attachments.edit | | DELETE | `/admin/attachments/{id}` | 删除单个附件 | attachments.delete | | DELETE | `/admin/attachments` | 批量删除附件(ids 数组) | attachments.delete | | GET | `/admin/attachments/{id}/download` | 流式下载 | attachments.index | | GET | `/admin/attachments/{id}/temporary-url` | 临时签名 URL | attachments.index | 列表筛选参数:`per_page`、`type`、`disk`、`keyword`、`extension`、`uploader_id`、`start_date`、`end_date`。 附件资源字段: | 字段 | 类型 | 说明 | |------|------|------| | id | int | 附件 ID | | name | string | 显示名称 | | original_name | string | 原始文件名 | | disk | string | 存储磁盘 | | path | string | 存储路径 | | url | string|null | 可访问 URL(公有盘直链,私有盘为 null) | | type | object | 附件类型(value + label) | | extension | string | 扩展名 | | mime_type | string | MIME 类型 | | size | int | 字节数 | | size_text | string | 人类可读大小 | | exists | bool | 物理文件是否存在(仅详情接口返回) | | uploader | object|null | 上传者摘要(id + name) | | created_at | string | 创建时间 | ## 19. 应用管理(/admin/apps) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/apps` | 应用列表(分页,支持关键字/状态/日期筛选,预加载各平台最新版本) | apps.index | | POST | `/admin/apps` | 创建应用 | apps.create | | GET | `/admin/apps/{id}` | 应用详情 | apps.index | | PUT | `/admin/apps/{id}` | 更新应用 | apps.edit | | DELETE | `/admin/apps/{id}` | 删除应用(软删除) | apps.delete | 应用资源字段包含 `latest_versions`(各平台最多 3 条已发布版本,按 build 倒序)。 ## 20. 应用版本管理(/admin/apps/{app}/versions) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/apps/{app}/versions` | 版本列表(分页,支持平台/状态/版本号/强制更新/日期筛选) | app-versions.index | | POST | `/admin/apps/{app}/versions` | 创建版本 | app-versions.create | | GET | `/admin/apps/{app}/versions/{version}` | 版本详情 | app-versions.index | | PUT | `/admin/apps/{app}/versions/{version}` | 更新版本 | app-versions.edit | | DELETE | `/admin/apps/{app}/versions/{version}` | 删除版本(已发布状态不可删除) | app-versions.delete | | POST | `/admin/apps/{app}/versions/{version}/publish` | 发布版本(自动将同平台旧已发布版本置为废弃) | app-versions.edit | | POST | `/admin/apps/{app}/versions/{version}/withdraw` | 撤回版本(仅已发布状态可撤回) | app-versions.edit | | POST | `/admin/apps/{app}/versions/{version}/obsolete` | 废弃版本 | app-versions.edit | ## 21. 审计日志(/admin/audit-logs) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/audit-logs` | 审计日志列表(分页,支持时间范围/管理员/方法筛选) | audit-logs.index | | GET | `/admin/audit-logs/{id}` | 审计日志详情(含请求体、响应) | audit-logs.index | | GET | `/admin/audit-logs/export` | 导出审计日志(xlsx) | audit-logs.export | ## 22. 调度任务日志(/admin/schedule-logs) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/schedule-logs` | 调度日志列表(分页,支持任务名/状态/时间范围筛选) | schedule-logs.index | | GET | `/admin/schedule-logs/{id}` | 调度日志详情 | schedule-logs.index | ## 23. 钱包管理(/admin/wallet) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/wallet/stats` | 钱包统计(总余额、总冻结、累计充值、累计提现、待审提现数) | wallet.index | | GET | `/admin/wallet/trades` | 全局钱包流水列表(分页,支持 `owner_type`/`owner_id`/`type`/`status`/关键词/时间范围筛选) | wallet.index | | GET | `/admin/wallet/trades/{trade}` | 钱包流水详情(含持有者摘要、业务来源摘要、前后余额快照) | wallet.index | | GET | `/admin/wallet/trades/export` | 导出钱包流水(xlsx) | wallet.export | ## 24. 提现审核(/admin/wallet/withdraws) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/wallet/withdraws/stats` | 提现统计(总量/待审/今日新增/已通过/已驳回/打款成功/打款失败) | wallet.withdraws | | GET | `/admin/wallet/withdraws` | 提现申请列表(分页,支持 `owner_type`/`owner_id`/`status`/`account_type`/`order_no`/`start_date`/`end_date` 筛选) | wallet.withdraws | | GET | `/admin/wallet/withdraws/{withdraw}` | 提现申请详情(含 `owner` 关联、收款账户脱敏展示) | wallet.withdraws | | POST | `/admin/wallet/withdraws/{withdraw}/approve` | 审核通过(可选 `remark`,状态转 `approved`,保持冻结待打款) | wallet.withdraws | | POST | `/admin/wallet/withdraws/{withdraw}/reject` | 审核驳回(必填 `reason`、可选 `remark`,自动解冻并通知用户) | wallet.withdraws | | POST | `/admin/wallet/withdraws/{withdraw}/transfer` | 标记已打款(可选 `remark`,状态转 `success`,扣减冻结余额并通知用户) | wallet.withdraws | | POST | `/admin/wallet/withdraws/{withdraw}/fail` | 标记打款失败(必填 `reason`、可选 `remark`,自动解冻并通知用户) | wallet.withdraws | | GET | `/admin/wallet/withdraws/export` | 导出提现申请(xlsx,阈值 2000 条同步导出) | wallet.export | > 提现状态流转:`pending` →(approve)`approved` →(transfer)`success`;`pending` →(reject)`rejected`(自动解冻退回);`approved` →(fail)`failed`(自动解冻退回)。已进入终态(`success`/`rejected`/`failed`)的记录不可重复操作,接口幂等返回。响应使用 `Admin\WithdrawResource`,收款账号通过 `maskedAccountInfo()` 脱敏(前 2 + `****` + 后 4)。 ## 25. 充值订单(/admin/wallet/recharges) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/wallet/recharges` | 充值订单列表(分页,支持 `owner_type`/`owner_id`/`pay_status`/`pay_channel`/`order_no`/时间范围筛选) | wallet.orders | | GET | `/admin/wallet/recharges/stats` | 充值统计(累计/今日金额、赠送总额、订单数、成功率) | wallet.orders | | GET | `/admin/wallet/recharges/{recharge}` | 充值订单详情(含渠道原始回调报文) | wallet.orders | | POST | `/admin/wallet/recharges/{recharge}/confirm` | 手动确认到账(幂等,入账并发站内信通知) | wallet.orders | | POST | `/admin/wallet/recharges/{recharge}/close` | 关闭订单(仅待支付可关闭) | wallet.orders | | GET | `/admin/wallet/recharges/export` | 导出充值订单(xlsx) | wallet.export | > 支付渠道对接已挂起,当前到账依赖管理员手动确认。确认接口具备三重幂等保障(行锁 + 状态判定 + `transaction_id` 唯一索引),重复调用不会重复入账。详见 `docs/technical-solutions/wallet.md` 第 12.1 节。 ## 26. 数据概览(/admin/dashboard) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | GET | `/admin/dashboard/stats` | 仪表盘统计数据 | 是 | ## 27. 监控接口(/admin/monitor,读取 Pulse 数据) | 方法 | 路径 | 说明 | |------|------|------| | GET | `/admin/monitor/servers` | 服务器性能指标 | | GET | `/admin/monitor/queues` | 队列负载 | | GET | `/admin/monitor/reverb/connections` | Reverb 连接数 | | GET | `/admin/monitor/reverb/messages` | Reverb 消息吞吐 | | GET | `/admin/monitor/cache` | 缓存命中率 | | GET | `/admin/monitor/exceptions` | 异常统计 | | GET | `/admin/monitor/slow-queries` | 慢查询 | | GET | `/admin/monitor/slow-requests` | 慢请求 | | GET | `/admin/monitor/slow-jobs` | 慢任务 | | GET | `/admin/monitor/slow-outgoing-requests` | 慢外部请求 | | GET | `/admin/monitor/usage` | 使用情况 | ## 28. 调试接口(/admin/debug,仅 local 环境,读取 Telescope) | 方法 | 路径 | 说明 | |------|------|------| | GET | `/admin/debug/tags` | 获取标签列表 | | POST | `/admin/debug/tags` | 监控指定标签 | | DELETE | `/admin/debug/tags` | 取消监控标签 | | POST | `/admin/debug/toggle-recording` | 开启/暂停记录 | | GET | `/admin/debug/entries` | 条目列表(分页) | | GET | `/admin/debug/entries/{id}` | 条目详情 | | PUT | `/admin/debug/entries/{id}/resolve` | 标记已解决 | | DELETE | `/admin/debug/entries` | 删除条目 | ## 29. 队列管理(/admin/queue,代理 Horizon 数据) | 方法 | 路径 | 说明 | |------|------|------| | GET | `/admin/queue/stats` | 队列总体统计 | | GET | `/admin/queue/workload` | 队列工作负载 | | GET | `/admin/queue/masters` | Master 进程状态 | | GET | `/admin/queue/monitoring/tags` | 监控标签列表 | | GET | `/admin/queue/monitoring/jobs` | 监控任务列表 | | POST | `/admin/queue/monitoring/tags` | 添加监控标签 | | DELETE | `/admin/queue/monitoring/tags/{tag}` | 移除监控标签 | | GET | `/admin/queue/metrics/jobs` | 任务吞吐指标 | | GET | `/admin/queue/metrics/jobs/{id}` | 任务指标详情 | | GET | `/admin/queue/metrics/queues` | 队列指标 | | GET | `/admin/queue/metrics/queues/{id}` | 队列指标详情 | | GET | `/admin/queue/batches` | 批处理列表 | | GET | `/admin/queue/batches/{id}` | 批处理详情 | | POST | `/admin/queue/batches/{id}/retry` | 重试失败批次 | | GET | `/admin/queue/jobs/pending` | 待处理任务 | | GET | `/admin/queue/jobs/completed` | 已完成任务 | | GET | `/admin/queue/jobs/silenced` | 静默任务 | | GET | `/admin/queue/jobs/failed` | 失败任务 | | GET | `/admin/queue/jobs/failed/{id}` | 失败任务详情 | | POST | `/admin/queue/jobs/failed/{id}/retry` | 重试失败任务 | | GET | `/admin/queue/jobs/{id}` | 任务详情 | ## 30. AI 聊天(/admin/chat,SSE 流式) 全部接口需 `chat.use` 权限点,缺失时返回 403。 | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | GET | `/admin/chat/conversations` | 会话列表 | 是(`chat.use`) | | GET | `/admin/chat/conversations/{id}` | 会话详情(含消息历史) | 是(`chat.use`) | | POST | `/admin/chat` | 发送消息(SSE 流式响应) | 是(`chat.use`) | | POST | `/admin/chat/approve` | 审批工具调用并继续执行 | 是(`chat.use`) | | DELETE | `/admin/chat/conversations/{id}` | 删除会话 | 是(`chat.use`) | > 工具可用性随当前管理员角色权限动态过滤(详见「AI 助手」功能文档的权限控制章节),无权限的工具不会暴露给模型,越权调用在执行层兜底拒绝。 SSE 流式响应通过 `text/event-stream` 逐行推送以下事件类型: * `text`:文本增量 * `reasoning`:思考过程 * `tool_call`:工具调用信息 * `tool_approval_required`:需要人工审批的敏感操作 * `error`:错误 * `done`:流结束 ## 31. 用户分群管理(/admin/user-segments) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/user-segments` | 分群列表(分页,支持 `keyword`/`type`/`group`/`status` 筛选) | segments.index | | POST | `/admin/user-segments` | 新建分群(规则分群必填 `code` + `rule`) | segments.create | | GET | `/admin/user-segments/options` | 启用手动分群下拉选项(公告群发复用) | segments.index | | GET | `/admin/user-segments/{id}` | 分群详情(含 `users_count`) | segments.index | | PUT | `/admin/user-segments/{id}` | 更新分群(规则分群禁止改 `code`/`rule`/`type`) | segments.edit | | DELETE | `/admin/user-segments/{id}` | 删除分群(级联清理 memberships) | segments.delete | | PUT | `/admin/user-segments/{id}/toggle-status` | 启用/停用切换 | segments.edit | | POST | `/admin/user-segments/{id}/sync` | 手动触发规则同步(增量 diff) | segments.edit | | GET | `/admin/user-segments/{id}/preview` | 规则预览(不写库,返回 `total` + `sample`) | segments.index | | GET | `/admin/user-segments/{id}/users` | 分群下用户列表(分页,支持 `keyword` 筛选) | segments.index | | POST | `/admin/user-segments/{id}/attach` | 单分群批量绑定用户(>1000 异步入队返回 202) | segments.edit | | POST | `/admin/user-segments/{id}/detach` | 单分群批量解绑用户(>1000 异步入队返回 202) | segments.assign | | POST | `/admin/user-segments/batch-attach` | 笛卡尔批量打标(任一维度 >1000 异步入队) | segments.edit | | POST | `/admin/user-segments/batch-detach` | 笛卡尔批量解标(任一维度 >1000 异步入队) | segments.assign | > 分群类型:`type=1` 手动分群(人工打标/解标),`type=2` 规则分群(基于规则 JSON 自动匹配,定时同步 + 手动同步)。规则分群的 `code` 必填且唯一,更新时不可修改 `code`/`rule`/`type`。批量操作超过 `BATCH_LIMIT=1000` 时自动入队 `BulkSegmentTaskJob`,返回 202 + `task_id`,任务完成后发送站内信通知。 **分群资源字段**: | 字段 | 类型 | 说明 | |------|------|------| | id | int | 分群 ID(从 10000000 起) | | code | string|null | 分群编码(规则分群必填且唯一) | | name | string | 分群名称 | | label_type | string | 标签样式(default/primary/success/warning/danger/info) | | type | int | 分群类型(1=手动,2=规则) | | type_label | string | 分群类型标签 | | group | string|null | 分组(用户画像/运营分群/风控 等) | | description | string|null | 分群说明 | | sort | int | 排序权重(越小越靠前) | | status | bool | 启用状态 | | rule | object|null | 规则定义(仅 type=2 有效) | | last_synced_at | string|null | 最近一次规则刷新时间 | | last_synced_user_count | int|null | 最近一次规则匹配用户数 | | users_count | int|null | 分群用户数(仅详情接口返回) | | created_at | string | 创建时间 | | updated_at | string | 更新时间 | **规则 JSON 结构**: ```json { "version": 1, "logic": "and", "conditions": [ { "field": "status", "operator": "=", "value": 1 }, { "field": "last_active_at", "operator": ">", "value": "now-7d" } ] } ``` 规则字段(25 个): * 主表字段:`status`、`identity_status`、`identity_type`、`vip_expires_at`、`last_login_at`、`last_active_at`、`created_at`、`available_points`、`available_coins`、`login_count`、`balance`(可用余额)、`frozen_balance`(冻结余额)、`email`、`phone`、`username`(后三者仅支持 `is_null`/`is_not_null` 判定绑定状态) * `profile` 关联字段:`gender`、`birthday`、`province_id`(省份 ID)、`city_id`(城市 ID) * `extra` 关联字段:`email_verified_at`(邮箱验证时间,仅支持空值判定) * 关系存在性字段:`social_provider`(社交平台绑定,`=` 已绑定 / `!=` 未绑定,取值 `wechat_mp`/`wechat_app`/`wechat_mini_program`/`apple`/`douyin`/`kuaishou`/`xiaohongshu`) * 关系计数字段:`recharge_count`(充值次数,仅统计已到账)、`withdraw_count`(提现次数)、`invite_count`(邀请人数),仅支持比较操作符 * 关系求和字段:`recharge_total`(累计充值金额,仅统计已到账,支持比较与 `between`;无充值记录的用户不会被 `= 0` 类条件命中) > `gender`(性别:0=保密/1=男/2=女)、`birthday`(生日)、`province_id`、`city_id` 字段位于 `user_profiles` 表,通过 `profile` 关联查询;`email_verified_at` 位于 `user_extras` 表,通过 `extra` 关联查询。 操作符(11 种):`=`、`!=`、`>`、`>=`、`<`、`<=`、`between`、`in`、`not_in`、`is_null`、`is_not_null`。 动态时间值:`now`、`today`、`now-7d`、`now-3h`、`today-1d` 等。 ## 32. 分群异步任务(/admin/segment-tasks) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/segment-tasks` | 当前管理员的异步任务列表(分页,支持 `status`/`type` 筛选) | segments.assign | | GET | `/admin/segment-tasks/{id}` | 异步任务详情(含进度百分比) | segments.assign | > 任务由批量打标/解标超限时自动创建,仅可查看本人提交的任务。任务状态流转:`pending` → `running` → `completed`/`failed`。 **任务资源字段**: | 字段 | 类型 | 说明 | |------|------|------| | id | int | 任务 ID | | admin_id | int | 提交任务的管理员 ID | | type | string | 任务类型(attach 打标 / detach 解标) | | type_label | string | 任务类型标签 | | status | string | 状态(pending/running/completed/failed) | | status_label | string | 状态标签 | | total_count | int | 计划处理总数 | | processed_count | int | 已处理数 | | added_count | int | 实际新增/解绑数 | | progress_percent | int | 进度百分比(0-100) | | error_message | string|null | 失败原因 | | started_at | string|null | 开始执行时间 | | finished_at | string|null | 完成时间 | | created_at | string | 创建时间 | ## 33. AI 知识库(/admin/knowledge-documents) RAG 知识库管理。文档保存后自动派发异步索引任务(段落分块 + Embedding 向量化,经队列执行),仅「已完成索引且启用」的文档参与语义检索;AI 助手内置 `SearchKnowledgeBase` 工具按 `knowledge-documents.index` 权限检索知识库。 | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/knowledge-documents` | 文档列表(分页,支持 `keyword` 标题搜索、`status` 索引状态、`is_enabled` 筛选) | knowledge-documents.index | | POST | `/admin/knowledge-documents` | 新增文档(自动派发索引任务,状态置为 pending) | knowledge-documents.create | | GET | `/admin/knowledge-documents/{id}` | 文档详情(含原始内容 content) | knowledge-documents.index | | PUT | `/admin/knowledge-documents/{id}` | 更新文档(content 变更时自动重建索引) | knowledge-documents.edit | | PUT | `/admin/knowledge-documents/{id}/toggle-status` | 切换启用状态(停用不参与检索) | knowledge-documents.edit | | POST | `/admin/knowledge-documents/{id}/reindex` | 重建索引(重置为 pending 并重新分块向量化) | knowledge-documents.edit | | DELETE | `/admin/knowledge-documents/{id}` | 删除文档(分块级联删除) | knowledge-documents.delete | | GET | `/admin/knowledge-documents/{id}/chunks` | 文档分块列表(分页) | knowledge-documents.index | | POST | `/admin/knowledge-documents/search` | 语义检索测试(`query` 必填、`limit` 默认 5 最大 10,按余弦相似度降序返回 Top-N 分块及来源文档、得分) | knowledge-documents.index | > 索引状态流转:`pending` → `processing` → `ready`/`failed`;失败原因记录在文档 `error` 字段,可通过 reindex 重建。向量模型由 `config/ai.php` 的 `volc` provider 提供(默认 `doubao-embedding-vision-251215`,可用 `VOLC_EMBEDDINGS_MODEL` / `VOLC_EMBEDDINGS_DIMENSIONS` 覆盖)。 **文档资源字段**: | 字段 | 类型 | 说明 | |------|------|------| | id | int | 文档 ID | | title | string | 文档标题 | | content | string | 原始内容(仅详情接口返回) | | status | string | 索引状态(pending/processing/ready/failed) | | status_label | string | 索引状态文案 | | status_type | string | 索引状态标签样式 | | is_enabled | bool | 是否启用 | | chunks_count | int | 分块数量 | | tokens | int | 向量化消耗的 Tokens | | error | string|null | 最近一次索引失败原因 | | created_by | int|null | 创建管理员 ID | | created_at | string|null | 创建时间 | | updated_at | string|null | 更新时间 | ## 34. AI 辅助内容生产(/admin/announcements、/admin/banners) 复用 `laravel/ai` 一次性生成能力为运营场景提供 AI 辅助(`Copywriter` 文案 Agent + Seedream 绘图)。所有接口仅生成内容不落库,由管理员检查后通过原有 CRUD 接口提交。 | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | POST | `/admin/announcements/ai-copy` | AI 生成公告文案(入参 `brief` 需求简述;返回 `title`/`summary`/`content`(HTML)) | announcements.create | | POST | `/admin/banners/ai-copy` | AI 生成 Banner 文案(入参 `brief`;返回 `title` 与 `image_prompt` 绘图提示词) | banners.create | | POST | `/admin/banners/ai-image` | AI 生成 Banner 配图(入参 `prompt` 绘图提示词;Seedream 3:2 横版绘图并落盘,返回 `path`/`url`;生图调用已设置 300 秒超时) | banners.create | > 配套的审核辅助接口分布见各业务章节:评论 AI 复核(12 章)、反馈 AI 草拟(14 章)、举报 AI 初判(14b 章)。 ## 协议管理(/admin/agreements) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/agreements` | 协议列表(分页,支持 `keyword` 标题搜索、`category` 类别、`status` 状态筛选) | agreements.index | | POST | `/admin/agreements` | 创建协议 | agreements.create | | GET | `/admin/agreements/{id}` | 协议详情 | agreements.index | | PUT | `/admin/agreements/{id}` | 更新协议 | agreements.edit | | PUT | `/admin/agreements/{id}/toggle-status` | 切换状态(草稿→已发布→已归档) | agreements.edit | | DELETE | `/admin/agreements/{id}` | 删除协议(软删除) | agreements.delete | > 协议类别(`category`)使用字典管理,字典编码为 `agreement_category`,字典项的 `value` 即为协议类别编码。前台公开接口 `GET /api/v1/agreements/{category}` 按类别返回最新已发布版本。状态流转:`draft` →(toggle)`published`(自动写入 `effective_at`)→(toggle)`archived` →(toggle)`published`。 *** > 文档来源:PRD 第 6.3 节 --- --- url: https://www.aiforge.cn/api/api-v1.md --- # 前台 API v1 接口(/api/v1) > 在线接口文档(Apifox,可在线调试): ## 1. 用户认证与账户 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/v1/auth/login` | 用户登录(密码/手机验证码/微信/Apple) | | POST | `/api/v1/auth/register` | 用户注册(手机/邮箱) | | POST | `/api/v1/auth/logout` | 退出登录 | | GET | `/api/v1/auth/info` | 当前用户信息 | ## 2. 公共接口 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/common/*` | 公共接口(短信/邮件验证码、地区等) | | POST | `/api/v1/common/sms-captcha` | 发送短信验证码 | | POST | `/api/v1/common/mail-captcha` | 发送邮件验证码 | ## 3. 应用版本 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/v1/apps/check-update` | App 版本检查更新(传 slug/platform/build,返回是否有更新、是否强制、最新版本信息) | ## 4. 用户个人中心 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/user/*` | 用户个人中心(资料、密码、通知、积分、金币、登录历史等) | | GET | `/api/v1/user` | 获取基本资料 | | POST | `/api/v1/user/profile` | 修改个人资料 | | POST | `/api/v1/user/avatar` | 修改头像(multipart/form-data,字段 `avatar`,自动转 WebP) | | POST | `/api/v1/user/username` | 修改用户名 | | POST | `/api/v1/user/password` | 修改密码 | | POST | `/api/v1/user/phone` | 修改手机号 | | POST | `/api/v1/user/email` | 修改邮箱 | | POST | `/api/v1/user/verification-mail` | 发送验证邮件 | | DELETE | `/api/v1/user` | 注销并删除自己的账户 | | GET | `/api/v1/user/notifications` | 通知列表 | | GET | `/api/v1/user/login-histories` | 登录历史 | | GET | `/api/v1/user/socials` | 当前用户绑定的第三方账号列表(分页,可按 `provider` 过滤;openid/unionid 脱敏) | | GET | `/api/v1/user/identity` | 当前用户最新一条实名认证记录 | | GET | `/api/v1/user/identity/status` | 简要实名认证状态 | | POST | `/api/v1/user/identity` | 提交/重新提交实名认证(personal 个人 / enterprise 企业) | | GET | `/api/v1/user/addresses` | 收货地址列表 | | POST | `/api/v1/user/addresses` | 新增收货地址 | | PUT | `/api/v1/user/addresses/{id}` | 更新收货地址 | | DELETE | `/api/v1/user/addresses/{id}` | 删除收货地址 | ### 4.1 修改头像(POST `/api/v1/user/avatar`) * 需 Sanctum 登录认证,请求格式为 `multipart/form-data` | 表单字段 | 类型 | 必填 | 说明 | |----------|------|------|------| | `avatar` | file | 是 | 头像图片,支持 jpg/jpeg/png/gif | 服务端处理:图片经 Intervention 统一转换为 WebP(质量 60),以用户 ID 命名(`{id}.webp`),按 ID 补零 9 位分两级目录存储(`avatar/000/0000/{id}.webp`),重复上传直接覆盖旧头像;上传结果写入附件台账,更新资料后派发 `UserProfileUpdated` 事件(驱动完善资料任务进度)。 成功响应 `200`: ```json { "message": "头像修改完成", "path": "avatar/000/0000/1.webp", "avatar": "avatar/000/0000/1.webp" } ``` 错误响应:未登录返回 `401`;未传文件或文件类型不合法返回 `422`(`errors.avatar` 含具体校验信息)。 ## 5. 内容互动接口 ### 5.1 点赞(/api/v1/likes) | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/likes` | 我的点赞列表(分页,可按 `source_type` 过滤) | | POST | `/api/v1/likes` | 添加点赞 | | DELETE | `/api/v1/likes/{like}` | 取消点赞 | ### 5.2 收藏(/api/v1/collections) | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/collections` | 我的收藏列表(分页,可按 `source_type` 过滤) | | POST | `/api/v1/collections` | 添加收藏 | | DELETE | `/api/v1/collections/{collection}` | 取消收藏 | ### 5.3 关注(/api/v1/follows) | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/follows` | 我的关注列表(分页,可按 `source_type` 过滤) | | POST | `/api/v1/follows` | 添加关注 | | DELETE | `/api/v1/follows/{follow}` | 取消关注 | ### 5.4 评论(/api/v1/comments) | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/comments` | 评论列表(分页,可按 `source_type` + `source_id` 过滤;公开接口) | | POST | `/api/v1/comments` | 发布评论(支持 `parent_id` 回复、`mentioned_users` 艾特) | | DELETE | `/api/v1/comments/{comment}` | 删除评论(仅本人) | ### 5.5 访问记录(/api/v1/visits) | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/v1/visits` | 记录一次访问(`user_id` / `ip` 由后端注入,前端仅传 `source_type` + `source_id`) | | GET | `/api/v1/user/visits` | 我的访问记录(分页,可按 `source_type` 过滤) | * 两条接口均需 Sanctum 认证;写入接口由 `RecordVisitRequest::prepareForValidation()` 合并 `user_id` 与 `ip`,控制器不直接拼装 * 资源校验复用现有 `source_types()` 白名单(同 Like/Collection),`source_type` 命中多态别名之外的值返回 `422` * **不做唯一约束**:同一用户对同一资源可重复访问,符合 PV 语义 ### 5.6 内容互动约定 * 点赞、收藏、关注、访问记录全部接口需 Sanctum 认证;评论仅 `POST`/`DELETE` 需认证,列表公开 * 写入参数:`source_type`(必填,须为 `config/morph_maps.php` 已注册别名)、`source_id`(必填)、`extra`(可选,JSON,仅点赞/收藏/关注使用) * 点赞/收藏/关注对同一资源重复提交返回 `422`,由 `Rule::unique` 拦截 * 越权删除他人记录返回 `403`,由 `LikePolicy` / `CollectionPolicy` / `FollowPolicy` / `CommentPolicy` 校验 * 响应中的 `user_id` 通过模型 `#[Hidden]` 隐藏 * 列表接口使用 Laravel 标准分页,支持 `page` / `per_page`(1-100,默认 15) ## 6. 公告 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/announcements` | 公告列表 | | GET | `/api/v1/announcements/{id}` | 公告详情 | ## 7. 协议 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/agreements/{category}` | 按类别获取最新已发布协议(公开,带 HTTP 缓存) | > 前端传入协议类别编码(如 `user_agreement`、`privacy_policy`),返回该类别最新已发布版本的完整内容(含 title、content、version、effective_at)。 ## 8. Banner | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/banners` | 获取公开 Banner 列表(自动过滤禁用/未开始/已过期) | ## 9. 用户反馈(/api/v1/user/feedbacks) > 全部接口需 Sanctum 登录认证,反馈自动绑定当前登录用户(`user_id` 由服务端注入,无需客户端传)。 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/v1/user/feedbacks` | 提交用户反馈(成功返回 201,响应体含反馈单号 `code`) | | GET | `/api/v1/user/feedbacks/my` | 我的反馈列表(分页,按提交时间倒序) | ## 9b. 用户举报(/api/v1/user/reports) > 全部接口需 Sanctum 登录认证,举报自动绑定当前登录用户。被举报对象使用 `morph_alias` (`comment` / `user`) 接收,服务端按 morphMap 解析为全限定类名并校验目标存在。 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/v1/user/reports` | 提交举报。Body:`target_type` ∈ `comment`/`user`、`target_id`、`category`(取自字典 `report_category`,9 类)、可选 `reason`(≤500 字)、可选 `evidence`(≤9 个 URL)。成功返回 201。同一人对同一目标在 `pending`/`processing` 状态下不可重复提交(返回 422)。`reason` 内容会经过敏感词过滤。目标对象不存在返回 422 | | GET | `/api/v1/user/reports/my` | 我的举报列表(分页,按提交时间倒序) | ## 10. 钱包接口(/api/v1/user/wallet) | 方法 | 路径 | 说明 | 状态 | |------|------|------|------| | GET | `/api/v1/user/wallet` | 我的钱包概览(可用余额、冻结余额、累计收入、累计支出) | ✅ 已交付 | | GET | `/api/v1/user/wallet/trades` | 我的钱包流水(分页,可按 `type`/时间范围过滤) | ✅ 已交付 | | POST | `/api/v1/user/wallet/recharges` | 创建充值订单(返回 `order_no`,成功返回 201) | ✅ 已交付 | | GET | `/api/v1/user/wallet/recharges` | 我的充值订单列表(分页,可按 `pay_status` 过滤) | ✅ 已交付 | | GET | `/api/v1/user/wallet/recharges/{recharge}` | 充值订单详情(用于轮询到账状态) | ✅ 已交付 | | POST | `/api/v1/user/wallet/withdraws` | 提交提现申请(`amount`、`account_type`∈wechat/alipay/bank、`account_info:{account,real_name}`,成功返回 201,冻结对应金额) | ✅ 已交付 | | GET | `/api/v1/user/wallet/withdraws` | 我的提现列表(分页,可按 `status` 过滤,收款账号脱敏) | ✅ 已交付 | | GET | `/api/v1/user/wallet/withdraws/{withdraw}` | 提现详情(Policy 校验归属,收款账号脱敏) | ✅ 已交付 | ### 钱包接口约定 * 全部接口需 Sanctum 认证,内部统一取 `auth()->user()` 作为 owner,前台无需感知多态 * 充值下单走 `wallet.recharge_throttle` 限流,提现提交走 `wallet.withdraw_throttle`(默认 5 次/分钟),防止刷单 * 越权访问他人订单返回 `403`,由 `RechargePolicy` / `WithdrawPolicy` 校验 * 提现提交需校验:开关(`wallet.enable_withdraw`)、金额区间(`wallet.min_withdraw_amount`/`wallet.max_withdraw_amount`)、当日次数(`wallet.daily_withdraw_limit`)、不存在待审核单;余额不足返回 `422` * 金额字段统一以元为单位、`decimal(16,2)` 精度返回字符串,避免前端浮点精度丢失 * 前台提现响应使用 `Api\V1\WithdrawResource`,脱敏展示,不返回 `owner`、`client_ip`、`reviewed_by`、`remark` 等内部字段 * 支付渠道对接已挂起,`pay_channel` 当前仅接受 `manual`,到账由管理员在后台确认 ## 11. 字典公开接口 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/dict-items` | 公开字典项查询(按 code 获取字典数据) | ## 12. 支付回调(/callback) | 方法 | 路径 | 说明 | |------|------|------| | POST | `/callback/pay/wechat` | 微信支付异步回调(验签后置订单成功并入账) | | POST | `/callback/pay/alipay` | 支付宝异步回调(验签后置订单成功并入账) | 回调接口豁免 CSRF 与认证中间件,必须完成渠道验签;处理逻辑幂等(同一 `order_no` 重复回调只入账一次)。 ## 13. MCP Server(/mcp/v1) 将上述 C 端接口能力封装为 MCP(Model Context Protocol)工具,供 AI 客户端直接调用。采用进程内封装:工具直接调用业务层/模型,不经过 HTTP 转发。 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/mcp/v1` | MCP JSON-RPC 2.0 端点(Sanctum Token 认证) | ### 接入方式 * 请求头:`Authorization: Bearer `,与 `/api/v1` 共用同一套用户令牌 * Content-Type:`application/json`,请求体为 JSON-RPC 2.0(`tools/list`、`tools/call` 等) * 未认证返回 `401`;工具入参校验失败在 JSON-RPC 响应中返回校验错误信息 ### 客户端配置示例 ```json { "mcpServers": { "skel-api": { "type": "http", "url": "https://laravel-skel-forge.test/mcp/v1", "headers": { "Authorization": "Bearer " } } } } ``` ### 已封装工具(70 个:35 只读 + 35 写操作) | 域 | 工具名 | 说明 | 入参 | |--------|------|------|------| | 用户 | `user.profile.get` | 基本资料(账号、昵称、头像、积分、金币、会员状态) | 无 | | 用户 | `user.login_histories.list` | 登录历史 | 分页 | | 用户 | `user.points.list` | 积分流水 | 分页 | | 用户 | `user.coins.list` | 金币流水 | 分页 | | 用户 | `user.socials.list` | 第三方账号绑定(openid/unionid 脱敏) | 分页 + `provider` | | 用户 | `user.visits.list` | 我的访问记录 | 分页 + `source_type` | | 通知 | `user.notifications.list` | 通知列表 | 分页 + `type` | | 通知 | `user.notifications.unread` | 未读通知列表 | 分页 + `type` | | 地址 | `user.address.list` | 收货地址列表 | 分页 | | 地址 | `user.address.get` | 地址详情(越权拦截) | `id` | | 反馈 | `user.feedback.list` | 我的反馈列表 | 分页 | | 举报 | `user.report.list` | 我的举报列表 | 分页 | | 实名 | `user.identity.get` | 最新一条实名认证记录 | 无 | | 实名 | `user.identity.status` | 实名认证简要状态 | 无 | | 钱包 | `wallet.overview.get` | 钱包概览(余额、冻结、累计收支) | 无 | | 钱包 | `wallet.trades.list` | 钱包流水 | 分页 + `type` / `start_date` / `end_date` | | 钱包 | `wallet.recharge.list` | 充值订单列表 | 分页 + `pay_status` | | 钱包 | `wallet.recharge.get` | 充值订单详情(越权拦截) | `id` | | 钱包 | `wallet.withdraw.list` | 提现申请列表 | 分页 + `status` | | 钱包 | `wallet.withdraw.get` | 提现详情(越权拦截) | `id` | | 钱包 | `wallet.withdraw_account.list` | 收款账户列表(默认排最前) | 分页 + `account_type` | | 内容互动 | `content.comment.list` | 已审核通过的公开评论列表 | 分页 + `source_type` / `source_id` | | 内容互动 | `content.collection.list` | 我的收藏列表 | 分页 + `source_type` | | 内容互动 | `content.like.list` | 我的点赞列表 | 分页 + `source_type` | | 内容互动 | `content.follow.list` | 我的关注列表 | 分页 + `source_type` | | 任务 | `task.list` | 任务中心(按分组返回,含当前用户进度) | 无 | | 邀请 | `invite.record.list` | 我的邀请记录 | 分页 | | 邀请 | `invite.stats.get` | 邀请统计(人数、是否有邀请人) | 无 | | 公开 | `common.banner.list` | 指定广告位生效中的 Banner | `position` | | 公开 | `common.area.list` | 地区列表(不传 `area_id` 返回省级) | `area_id`? | | 公开 | `common.dict_item.list` | 公开字典项(白名单内编码) | `code` | | 公开 | `common.announcement.list` | 已发布公告列表 | 分页 | | 公开 | `common.announcement.get` | 公告详情 | `id` | | 公开 | `common.agreement.get` | 最新已发布协议 | `category` | | 会话 | `auth.token.list` | 已签发的访问令牌列表 | 分页 | ### 写操作工具(18 个) | 域 | 工具名 | 说明 | 入参 | |--------|------|------|------| | 内容互动 | `content.comment.create` | 发布评论(敏感词过滤,命中 review 级别进入待审核) | `source_type` / `source_id` / `content` / `parent_id`? / `mentioned_users`? | | 内容互动 | `content.comment.delete` | 删除自己的评论 | `id` | | 内容互动 | `content.like.create` | 点赞(同一用户同一对象防重复) | `source_type` / `source_id` / `extra`? | | 内容互动 | `content.like.delete` | 取消点赞 | `source_type` / `source_id` | | 内容互动 | `content.collection.create` | 收藏(防重复) | `source_type` / `source_id` / `extra`? | | 内容互动 | `content.collection.delete` | 取消收藏 | `source_type` / `source_id` | | 内容互动 | `content.follow.create` | 关注(防重复) | `source_type` / `source_id` / `extra`? | | 内容互动 | `content.follow.delete` | 取消关注 | `source_type` / `source_id` | | 用户资料 | `user.profile.update` | 修改个人资料(昵称、生日、性别、地区、网站、介绍、签名) | 各字段可选 | | 用户资料 | `user.username.update` | 修改用户名(全局唯一) | `username` | | 用户资料 | `user.email.update` | 修改邮箱(需邮件验证码) | `email` / `verify_code` | | 用户资料 | `user.phone.update` | 修改手机号(需短信验证码) | `phone` / `verify_code` | | 用户资料 | `user.avatar.update` | 修改头像(base64 编码图片,自动转 WebP) | `avatar_base64` / `extension` | | 用户资料 | `user.password.update` | 修改密码(需旧密码验证) | `old_password` / `password` | | 地址 | `user.address.create` | 添加收货地址 | `name` / `phone` / `province` / `city` / `district` / `address` / `country`? / `is_default`? / `zipcode`? | | 地址 | `user.address.update` | 修改收货地址 | `id` + 可选字段 | | 地址 | `user.address.delete` | 删除收货地址(软删除) | `id` | | 地址 | `user.address.set_default` | 设为默认地址 | `id` | | 反馈 | `user.feedback.create` | 提交用户反馈(敏感词过滤) | `title` / `content` / `type` / `images`? / `contact_name`? / `contact_info`? | | 举报 | `user.report.create` | 提交举报(同一对象未结案前不可重复) | `target_type` / `target_id` / `category` / `reason`? / `evidence`? | | 钱包 | `wallet.recharge.create` | 创建充值订单(destructive) | `amount` / `pay_channel` | | 钱包 | `wallet.withdraw.create` | 提交提现申请(destructive,冻结余额) | `amount` / `withdraw_account_id` | | 钱包 | `wallet.withdraw_account.create` | 添加收款账户(首个自动默认) | `account_type` / `real_name` / `account_no` / `bank_name`? / `bank_branch`? / `is_default`? | | 钱包 | `wallet.withdraw_account.update` | 修改收款账户 | `id` + 可选字段 | | 钱包 | `wallet.withdraw_account.delete` | 删除收款账户(destructive) | `id` | | 钱包 | `wallet.withdraw_account.set_default` | 设为默认收款账户 | `id` | | 任务 | `task.claim` | 领取任务奖励 | `task_id` / `period_key`? | | 通知 | `notification.mark_all_read` | 全部标记已读 | 无 | | 通知 | `notification.mark_read` | 标记指定通知已读 | `id` | | 通知 | `notification.clear_read` | 清空已读通知(不可恢复) | 无 | | 访问记录 | `visit.record` | 记录一次访问 | `source_type` / `source_id` | | 会话 | `auth.token.delete` | 撤销指定访问令牌 | `id` | | 会话 | `auth.logout` | 退出登录(当前令牌立即失效) | 无 | | 账户 | `user.delete` | 注销并删除账户(destructive,软删除) | 无 | **写操作统一约定**: * 涉及内容发布的工具(评论、资料介绍/签名、反馈、举报)自动经过敏感词过滤 * 点赞/收藏/关注工具内置防重复校验,重复调用返回错误 * 删除/详情工具仅能操作当前登录用户自己的数据,越权返回错误 * `user.email.update` / `user.phone.update` 需先通过 `/api/v1/common/mail-captcha` 或 `/api/v1/common/sms-captcha` 获取验证码 * 资金类(`wallet.recharge.create`、`wallet.withdraw.create`)与账户注销(`user.delete`)标记 `destructiveHint=true`,AI 客户端应在执行前向用户二次确认 * `wallet.withdraw.create` 会冻结对应金额,需确保用户余额充足;同一用户仅允许一笔待审核提现 **统一约定**: * 「分页」入参为 `page`(默认 1)与 `per_page`(默认 15,1-100);响应统一为 `{items: [...], pagination: {total, per_page, current_page, last_page, has_more}}` * 详情类工具在记录不存在或不属于当前用户时返回错误(`isError=true`),不会越权泄漏他人数据 * 全部工具均带 `readOnlyHint` 与 `idempotentHint`,可由 AI 客户端安全缓存结果 * 工具定义位于 `app/Mcp/Tools/`,Server 定义位于 `app/Mcp/Servers/ApiServer.php` * 调试:`php artisan mcp:inspector mcp/v1` *** > 文档来源:PRD 第 6.4 节 --- --- url: https://www.aiforge.cn/technical-solutions/attachment-management.md --- # 附件管理技术方案 ## 1. 设计原则 * **元数据入库、字节流交给 Storage**:`attachments` 表只保存 `disk + path` 等元数据,所有物理读写通过 `Storage::disk($disk)` 完成,不出现任何 `storage_path()` 硬编码,保证 local/public/s3 三种驱动行为一致。 * **复用现有上传链路**:不改动 `UploaderController` 的对外契约,上传落盘成功后由 `AttachmentService` 追加台账写入,返回体增加 `id` 字段。 * **分层职责清晰**:Controller 只做 HTTP 编排,物理文件操作与台账一致性收敛到 `AttachmentService`。 * **失败可回滚**:删除、移动等涉及"数据库 + 物理文件"两段操作以数据库事务包裹,物理操作失败即回滚台账。 ## 2. 代码结构 ``` app/ ├── Enums/ │ └── AttachmentType.php # 附件类型枚举(image/video/audio/document/other,实现 HasLabel) ├── Models/System/ │ └── Attachment.php # 附件模型(软删除 + morphTo uploader) ├── Services/ │ └── AttachmentService.php # 台账 + Storage 一致性编排 ├── Http/ │ ├── Controllers/Admin/ │ │ └── AttachmentController.php # 后台附件管理控制器 │ ├── Requests/Admin/Attachment/ │ │ ├── AttachmentIndexRequest.php # 列表筛选参数校验 │ │ ├── AttachmentRenameRequest.php # 重命名校验 │ │ ├── AttachmentMoveRequest.php # 移动路径校验 │ │ ├── AttachmentDestroyRequest.php# 批量删除 ids 校验 │ │ └── AttachmentRegisterRequest.php # 直传登记校验 │ └── Resources/Admin/ │ └── AttachmentResource.php # 附件资源 database/ ├── migrations/ │ └── 0001_01_01_000016_create_attachments_table.php └── factories/System/ └── AttachmentFactory.php tests/Feature/Admin/ └── AttachmentControllerTest.php ``` ## 3. 数据表设计要点 * 主键从 `10000000` 起(与其他业务表保持一致风格),带表注释与字段注释。 * 索引:`(uploader_type, uploader_id)` 复合索引、`type` 索引、`hash` 索引、`(disk, path)` 唯一索引(防止同一物理文件重复登记)。 * 使用软删除 `deleted_at`:接口默认执行"硬删除物理文件 + 软删除台账",保留审计线索。 * `size` 使用 `unsignedBigInteger`,`hash` 使用 `char(32)`(md5)并允许为空(直传场景可能拿不到内容哈希)。 ## 4. AttachmentService 关键方法 | 方法 | 职责 | Storage 调用 | |------|------|-------------| | `record(array $meta): Attachment` | 上传成功后写台账 | 无 | | `url(Attachment $a): ?string` | 公有盘返回直链 | `Storage::disk()->url()` | | `temporaryUrl(Attachment $a, int $minutes): string` | 私有盘签名地址;本地盘回退直链 | `Storage::disk()->temporaryUrl()` | | `download(Attachment $a): StreamedResponse` | 流式下载,保留原始名 | `Storage::disk()->download()` | | `move(Attachment $a, string $newPath): Attachment` | 同盘移动,事务内先改库再移文件 | `Storage::disk()->move()` | | `delete(Attachment $a): bool` | 删物理文件后软删台账 | `Storage::disk()->delete()` | | `deleteMany(array $ids): int` | 按磁盘分组批量删除 | `Storage::disk()->delete(array)` | | `exists(Attachment $a): bool` | 校验物理文件是否仍存在 | `Storage::disk()->exists()` | ### 关键实现要点 * `temporaryUrl()` 需捕获驱动不支持的异常(local 驱动在未启用 `serve` 时不支持),降级为 `url()` 或 422 提示。 * `move()` 只允许同磁盘内移动,目标路径必须落在 `uploads/` 前缀下,且先做 `exists()` 冲突检查,避免覆盖既有文件。 * `deleteMany()` 按 `disk` 分组后一次性传数组给 `Storage::disk()->delete()`,减少远端请求次数。 * 物理文件已丢失(`exists() === false`)时删除仍视为成功,只清理台账,避免脏数据无法删除。 ## 5. 上传链路改造 在 `UploadRequest::handleUpload()` 落盘成功后调用 `AttachmentService::record()`,`uploader` 取 `$this->user()`(morphTo 兼容 Admin 与 User 两种守卫)。返回结构在原有字段上追加 `id`。 直传场景(`uploader/token`)文件不经过服务端,改为前端上传成功后调用 `POST /admin/attachments/register` 补登记,服务端用 `Storage::disk()->exists()` 与 `size()` 校验真实存在后再入库,防止伪造记录。 ## 6. 权限与菜单 * 新增权限:`attachments.index`、`attachments.create`、`attachments.edit`、`attachments.delete`,在控制器构造器中以 `permission:` 中间件绑定。 * 新增菜单:`system.attachment`,挂在"系统管理"目录下,含"删除附件""重命名附件""移动附件"按钮权限。 * 通过迁移追加权限与菜单,不修改已执行过的历史迁移。 ## 7. 配置项 沿用现有 `upload.*` 配置(`upload.storage`、`upload.allow_extension` 等),新增: | 配置键 | 默认值 | 说明 | |--------|--------|------| | upload.temporary_url_minutes | 10 | 临时签名 URL 有效期(分钟) | | upload.prune_orphan_days | 0 | 孤儿附件自动清理天数,0 表示不清理 | *** > 文档来源:PRD 第 7 章 --- --- url: https://www.aiforge.cn/technical-solutions/identity-verification.md --- # 实名认证技术方案 ## 1. 设计原则 * **多态认证主体**:认证记录通过 `verifiable_type` + `verifiable_id` 多态关联,当前接入 `User`,未来可扩展至其他主体(如商户、合作伙伴),认证记录层无需改动。 * **状态同步到主体主表**:用户表 (`users`) 直接存储 `identity_type` 和 `identity_status` 两个字段,查询认证状态零 Join;认证记录表 (`identity_verifications`) 仅保留完整审核轨迹。 * **个人与企业双类型**:支持 `personal`(个人)和 `enterprise`(企业)两种认证类型,共用同一张记录表,通过 `type` 字段区分必填字段。 * **驳回原因字典化**:驳回原因通过 `identity_reject_reason` 字典管理,管理员从预设原因中选择,支持灵活扩展。 * **事件驱动通知**:审核通过/驳回分别派发 `IdentityVerified` / `IdentityRejected` 事件,支持实时广播(Private Channel)和异步队列处理。 * **认证有效期**:审核通过时自动设置 3 年有效期(`expires_at`),到期后需重新认证。 ## 2. 代码结构 ``` app/ ├── Enums/ │ ├── IdentityStatus.php # 实名认证状态枚举(unsubmitted/pending/approved/rejected) │ ├── IdentityType.php # 认证类型枚举(personal/enterprise) │ └── ReviewStatus.php # 通用审核状态枚举(含 review,评论模块使用) ├── Models/User/ │ └── IdentityVerification.php # 认证记录模型(多态 verifiable + BelongsTo admin) ├── Services/ │ └── IdentityVerificationService.php # 认证服务(提交/通过/驳回编排) ├── Events/User/ │ ├── IdentityVerified.php # 认证通过事件(ShouldBroadcast + ShouldQueue) │ └── IdentityRejected.php # 认证驳回事件(ShouldBroadcast + ShouldQueue) ├── Http/Controllers/ │ ├── Api/V1/User/ │ │ └── IdentityController.php # 用户端认证接口(show/status/submit) │ └── Admin/ │ └── IdentityVerificationController.php # 管理端认证审核接口(index/stats/show/approve/reject) ├── Http/Requests/ │ ├── Api/V1/User/Identity/ │ │ └── SubmitIdentityRequest.php # 用户提交认证请求校验 │ └── Admin/Identity/ │ ├── IdentityIndexRequest.php # 管理端列表筛选请求 │ └── RejectIdentityRequest.php # 管理端驳回请求校验 ├── Http/Resources/ │ ├── Api/V1/ │ │ └── IdentityVerificationResource.php # 用户端认证资源(脱敏展示) │ └── Admin/ │ ├── IdentityVerificationResource.php # 管理端认证资源(含完整信息 + 驳回原因标签) │ └── IdentityVerificationStatResource.php # 管理端认证统计资源 └── Policies/ └── IdentityVerificationPolicy.php # 认证记录策略 ``` ## 3. 数据模型 ### 3.1 users 表(主体表) 用户表直接存储认证状态热数据,查询零 Join: | 字段名 | 类型 | 说明 | |--------|------|------| | identity_type | VARCHAR(16) | 实名认证类型:`personal` / `enterprise`,null 表示未认证 | | identity_status | VARCHAR(16) | 实名认证状态(枚举 `ReviewStatus`,默认 `unsubmitted`) | ### 3.2 identity_verifications 表(认证记录表) | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | verifiable_type | VARCHAR(100) | 申请人类型(多态,如 `App\Models\User`) | | verifiable_id | BIGINT | 申请人 ID | | type | VARCHAR(16) | 认证类型(`personal` / `enterprise`) | | status | VARCHAR(16) | 审核状态(`pending` / `approved` / `rejected`) | | real_name | VARCHAR(64) | 真实姓名(个人认证) | | id_card_no | VARCHAR(32) | 身份证号(密文存储,响应隐藏) | | id_card_front | VARCHAR | 身份证人像面图片路径 | | id_card_back | VARCHAR | 身份证国徽面图片路径 | | handheld | VARCHAR | 手持身份证图片路径 | | company_name | VARCHAR(128) | 企业名称(企业认证) | | credit_code | VARCHAR(32) | 统一社会信用代码(18 位) | | legal_person_name | VARCHAR(64) | 法人姓名(企业认证) | | legal_person_id_card_no | VARCHAR(32) | 法人身份证号(密文存储,响应隐藏) | | business_license | VARCHAR | 营业执照图片路径 | | opening_permit | VARCHAR | 开户许可证图片路径 | | bank_account | VARCHAR(64) | 对公账户(响应隐藏) | | bank_name | VARCHAR(64) | 开户行 | | admin_id | BIGINT | 审核管理员 ID | | reject_reason | VARCHAR(32) | 驳回原因(字典 `identity_reject_reason` 的 value) | | reject_remark | TEXT | 驳回备注 | | submitted_at | TIMESTAMP | 提交时间 | | reviewed_at | TIMESTAMP | 审核时间 | | expires_at | TIMESTAMP | 认证有效期(通过后自动设置 +3 年) | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间 | **索引**:`(verifiable_type, verifiable_id)` 复合索引、`type` 索引、`status` 索引、`admin_id` 索引。 > 敏感字段(`id_card_no`、`legal_person_id_card_no`、`bank_account`)通过模型 `#[Hidden]` 属性在所有响应中自动隐藏。 ### 3.3 相关枚举 **IdentityType(认证类型)**: | 值 | 说明 | |----|------| | personal | 个人认证 | | enterprise | 企业认证 | **IdentityStatus(实名认证状态)**: > 实名认证模块使用独立的 `IdentityStatus` 枚举,不复用 `ReviewStatus`(后者包含 `review` 状态,供评论模块使用)。 | 值 | 说明 | 标签样式 | |----|------|----------| | unsubmitted | 未提交 | info | | pending | 待审核 | warning | | approved | 已通过 | success | | rejected | 已驳回 | danger | ### 3.4 驳回原因字典(identity_reject_reason) 迁移文件预置以下字典项,管理员也可通过后台字典管理动态扩展: | 值 | 标签 | 标签样式 | |----|------|----------| | id_blurry | 证件模糊不清 | warning | | mismatch | 证件信息不符 | danger | | expired | 证件已过期 | info | | invalid | 证件无效 | danger | | incomplete | 证件资料不完整 | warning | | other | 其他原因 | info | ## 4. 业务流程 ### 4.1 用户提交认证 1. 用户通过 `POST /api/v1/user/identity` 提交认证信息,`type` 指定个人或企业。 2. 服务层在事务内加行锁检查:同一用户同时仅允许一条 `pending` 记录,否则返回 422。 3. 创建认证记录(状态 `pending`),同步用户表 `identity_status` 为 `pending`。 4. 返回认证记录资源。 **个人认证必填字段**:`real_name`、`id_card_no`、`id_card_front` **企业认证必填字段**:`company_name`、`credit_code`、`legal_person_name`、`legal_person_id_card_no`、`business_license` > 身份证号通过 `IdCardRule` 校验合法性;统一社会信用代码通过正则校验 18 位格式。 ### 4.2 管理员审核通过 1. 管理员通过 `POST /admin/identity-verifications/{verification}/approve` 审核通过。 2. 服务层在事务内: * 更新记录状态为 `approved`,记录 `admin_id`、`reviewed_at`、`expires_at`(+3 年),清空驳回字段。 * 同步用户表 `identity_type` 和 `identity_status` 为 `approved`。 * 派发 `IdentityVerified` 事件。 3. 前置校验:已通过或已驳回的记录不可重复审核,返回 422。 ### 4.3 管理员审核驳回 1. 管理员通过 `POST /admin/identity-verifications/{verification}/reject` 审核驳回。 2. 请求参数:`reason`(必填,须为字典 `identity_reject_reason` 中已启用的 value)、`remark`(可选,最多 500 字)。 3. 服务层在事务内: * 更新记录状态为 `rejected`,记录 `admin_id`、`reviewed_at`、`reject_reason`、`reject_remark`。 * 同步用户表 `identity_status` 为 `rejected`。 * 派发 `IdentityRejected` 事件。 4. 用户可在驳回后重新提交认证。 ### 4.4 状态流转图 ``` 提交认证 │ ▼ ┌─────────┐ │ pending │ └────┬────┘ │ 管理员审核 ├──────────────┐ ▼ ▼ ┌──────────┐ ┌──────────┐ │ approved │ │ rejected │(可重新提交) └──────────┘ └──────────┘ (终态,设置有效期) ``` ## 5. 事件与广播 ### 5.1 IdentityVerified(认证通过) * 实现:`ShouldBroadcast` + `ShouldQueue` + `ShouldHandleEventsAfterCommit` * 广播频道:`PrivateChannel("User.{id}")` * 用途:触发站内信通知、推送等异步处理 ### 5.2 IdentityRejected(认证驳回) * 实现:`ShouldBroadcast` + `ShouldQueue` + `ShouldHandleEventsAfterCommit` * 广播频道:`PrivateChannel("User.{id}")` * 携带数据:`reason`(驳回原因)、`remark`(驳回备注) * 用途:触发站内信通知用户驳回原因 > 两个事件均在数据库事务提交后派发,确保数据一致性。广播频道通过 `routes/channels.php` 中的 `Broadcast::channel('User.{id}', ...)` 授权。 ## 6. API 接口 ### 6.1 用户端接口(/api/v1/user/identity) | 方法 | 路径 | 说明 | 认证 | |------|------|------|------| | GET | `/api/v1/user/identity` | 当前用户最新一条认证记录 | Sanctum | | GET | `/api/v1/user/identity/status` | 简要认证状态 | Sanctum | | POST | `/api/v1/user/identity` | 提交/重新提交实名认证 | Sanctum | **GET `/api/v1/user/identity/status` 响应**: ```json { "is_verified": false, "identity_type": null, "identity_type_label": null, "latest_status": "rejected", "latest_status_label": "已驳回", "latest_reject_reason": "id_blurry", "latest_reject_remark": "身份证人像面照片模糊,请重新拍摄上传" } ``` **POST `/api/v1/user/identity` 请求参数**: | 字段 | 类型 | 必填条件 | 说明 | |------|------|----------|------| | type | string | 必填 | 认证类型:`personal` / `enterprise` | | real_name | string | type=personal | 真实姓名(最长 64 字) | | id_card_no | string | type=personal | 身份证号(校验合法性) | | id_card_front | string | type=personal | 身份证人像面图片路径 | | id_card_back | string | 可选 | 身份证国徽面图片路径 | | handheld | string | 可选 | 手持身份证图片路径 | | company_name | string | type=enterprise | 企业名称(最长 128 字) | | credit_code | string | type=enterprise | 统一社会信用代码(18 位) | | legal_person_name | string | type=enterprise | 法人姓名 | | legal_person_id_card_no | string | type=enterprise | 法人身份证号 | | business_license | string | type=enterprise | 营业执照图片路径 | | opening_permit | string | 可选 | 开户许可证图片路径 | | bank_account | string | 可选 | 对公账户 | | bank_name | string | 可选 | 开户行 | > 用户端响应通过 `IdentityVerificationResource` 返回,个人认证不返回企业字段,企业认证不返回个人字段,敏感字段(身份证号、对公账户)始终隐藏。 ### 6.2 管理端接口(/admin/identity-verifications) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/identity-verifications` | 认证记录列表(分页) | identity-verifications.index | | GET | `/admin/identity-verifications/stats` | 认证统计 | identity-verifications.index | | GET | `/admin/identity-verifications/{verification}` | 认证记录详情 | identity-verifications.index | | POST | `/admin/identity-verifications/{verification}/approve` | 审核通过 | identity-verifications.edit | | POST | `/admin/identity-verifications/{verification}/reject` | 审核驳回 | identity-verifications.edit | **列表筛选参数**: | 参数 | 说明 | |------|------| | verifiable_id | 申请人 ID | | keyword | 关键词(匹配真实姓名/企业名称/信用代码/法人姓名/用户名/手机号) | | type | 认证类型 | | status | 审核状态 | | start_date | 提交起始日期 | | end_date | 提交截止日期 | **统计接口响应**: ```json { "total": 1280, "pending": 15, "today": 8, "approved": 1200, "rejected": 65 } ``` > 管理端响应通过 `Admin\IdentityVerificationResource` 返回,包含完整认证信息、审核管理员名称、驳回原因标签(从字典缓存读取,TTL 2 小时)。驳回原因标签映射通过 `getRejectReasonMap()` 方法带缓存读取,字典变更后可通过 `flushRejectReasonMapCache()` 清除缓存。 ## 7. 安全与隐私 * **敏感字段隐藏**:`id_card_no`、`legal_person_id_card_no`、`bank_account` 通过模型 `#[Hidden]` 在所有响应中自动隐藏。 * **并发控制**:用户提交认证时使用 `lockForUpdate()` 防止并发提交多条待审记录。 * **事务一致性**:提交、通过、驳回三个操作均在 `DB::transaction` 内执行,确保认证记录与用户表状态同步。 * **权限控制**:管理端审核接口通过 `permission` 中间件控制(`identity-verifications.index` / `identity-verifications.edit`)。 * **驳回原因校验**:驳回请求的 `reason` 字段在运行时从字典表动态读取可用值,字典禁用后旧值不可再选。 * **认证有效期**:审核通过时自动设置 `expires_at`(+3 年),到期后可通过定时任务或业务逻辑引导用户重新认证。 *** > 文档来源:PRD 实名认证模块 --- --- url: https://www.aiforge.cn/technical-solutions/invitation.md --- # 邀请系统技术方案 ## 1. 设计目标与定位 邀请系统用于记录用户之间的邀请关系,是后续邀请奖励、分销结算、拉新统计等功能的基础数据层。本系统以**轻量、通用、可扩展**为设计原则,仅记录邀请关系本身,奖励发放等业务逻辑通过事件 + 监听器的方式解耦。 ### 1.1 设计原则 | 原则 | 说明 | |------|------| | **关系唯一** | 同一被邀请人只能绑定一个邀请人,绑定后不可变更 | | **多态设计** | 邀请人与被邀请人均采用多态关联,支持未来扩展到 Admin 邀请、第三方账号邀请等场景 | | **仅记事实** | 模型只记录邀请关系,不承载奖励、状态等业务字段,保持数据层纯粹 | | **事件驱动** | 邀请关系建立后触发事件,奖励/统计等逻辑由监听器处理,便于扩展 | *** ## 2. 数据模型 ### 2.1 数据表:invite_codes 邀请码表,每条记录代表一个主体(User、Admin 等)持有的唯一邀请码。邀请码通过多态关联归属主体,从原 `user_extras.invite_code` 字段抽取独立建模。 | 字段 | 类型 | 说明 | |------|------|------| | `id` | BIGINT UNSIGNED PK | 主键 | | `code` | VARCHAR(26) | 邀请码(大写 ULID,全局唯一) | | `owner_id` | BIGINT UNSIGNED | 所属主体 ID | | `owner_type` | VARCHAR(64) | 所属主体类型(多态类型别名,如 `user`、`admin`) | | `created_at` | TIMESTAMP NULL | 创建时间 | | `updated_at` | TIMESTAMP NULL | 更新时间 | #### 索引 | 索引名 | 字段 | 类型 | 说明 | |--------|------|------|------| | `uniq_code` | `code` | 唯一索引 | 邀请码全局唯一 | | `uniq_owner` | `owner_id, owner_type` | 唯一索引 | 一个主体仅持有一个邀请码 | *** ### 2.2 模型:InviteCode **命名空间**:`App\Models\Invite\InviteCode` **表名**:`invite_codes` #### 关联 ```php /** * 所属主体(多态) */ public function owner(): MorphTo ``` #### Model 结构 * 使用 `#[Table]`、`#[Fillable]` 等属性注解(对齐项目现有风格) * `code`、`owner_id`、`owner_type` 为 fillable * `creating` 钩子在未显式指定 `code` 时自动生成大写 ULID;`generateUniqueCode()` 应用层查重 + DB 唯一索引双重兜底 * 用户创建时由 `UserObserver` 自动为其创建邀请码;用户硬删除时一并清理 *** ### 2.3 数据表:invite_records 邀请记录表,每条记录代表一个邀请关系。 | 字段 | 类型 | 说明 | |------|------|------| | `id` | BIGINT UNSIGNED PK | 主键 | | `inviter_id` | BIGINT UNSIGNED | 邀请人 ID | | `inviter_type` | VARCHAR(64) | 邀请人类型(多态类型别名,如 `user`、`admin`) | | `invitee_id` | BIGINT UNSIGNED | 被邀请人 ID | | `invitee_type` | VARCHAR(64) | 被邀请人类型(多态类型别名) | | `description` | VARCHAR(255) | 描述/备注,如「通过邀请码注册」 | | `created_at` | TIMESTAMP NULL | 创建时间 | #### 索引 | 索引名 | 字段 | 类型 | 说明 | |--------|------|------|------| | `idx_inviter` | `inviter_id, inviter_type, created_at` | 普通索引 | 查询某邀请人的邀请列表 | | `uniq_invitee` | `invitee_id, invitee_type` | 唯一索引 | 同一被邀请人只能有一条邀请记录 | *** ### 2.4 模型:InviteRecord **命名空间**:`App\Models\Invite\InviteRecord` **表名**:`invite_records` #### 关联 ```php /** * 邀请人(多态) */ public function inviter(): MorphTo /** * 被邀请人(多态) */ public function invitee(): MorphTo ``` #### Model 结构 * 使用 `#[Table]`、`#[Fillable]` 等属性注解(对齐项目现有风格) * `inviter_id`、`inviter_type`、`invitee_id`、`invitee_type`、`description` 为 fillable * 无 `updated_at`(模型中设置 `const UPDATED_AT = null`) *** ### 2.5 多态映射 在 `config/morph_maps.php` 中注册的类型均可作为邀请方/被邀请方,邀请码模型自身也注册了多态别名: | 类型别名 | 模型类 | 典型场景 | |----------|--------|---------| | `user` | `App\Models\User` | 用户邀请用户(主流场景) | | `admin` | `App\Models\Admin\Admin` | 后台管理员手动添加邀请关系 | | `invite_code` | `App\Models\Invite\InviteCode` | 邀请码多态归属 | | `invite_record` | `App\Models\Invite\InviteRecord` | 邀请记录多态关联 | *** ## 3. 核心流程 ### 3.1 邀请关系建立(注册时) ``` 用户注册携带 invite_code ↓ 校验邀请码是否存在(invite_codes.code) ↓ 通过邀请码反查所属主体 owner(多态,即邀请人) ↓ 创建 InviteRecord 记录(inviter=邀请人, invitee=新用户) ↓ 触发 InviteRelationshipCreated 事件 ↓ 监听器处理奖励发放等后续逻辑 ``` **幂等保障**:依靠 `uniq_invitee` 唯一索引,同一用户重复触发也只会有一条邀请记录。 *** ## 4. 事件系统 ### 4.1 事件列表 | 事件类 | 触发场景 | 携带数据 | |--------|---------|---------| | `Events\Invite\InviteRelationshipCreated` | 邀请关系建立成功 | `inviteRecord`(邀请记录模型实例) | ### 4.2 与注册流程的协作 ``` RegisterController 收到 invite_code ↓ 触发 InviteRegistered 事件(同步,立即返回) ↓ InviteRegisteredListener(队列异步) ↓ 调用 InviteService::createRelationshipByCode() ↓ 创建 InviteRecord → 触发 InviteRelationshipCreated 事件 ``` **设计意图**: * `InviteRegistered` 是**业务入口事件**,表示"有人用邀请码注册了",由注册流程触发 * `InviteRegisteredListener` 实现 `ShouldQueue`,走队列异步执行,不阻塞注册响应 * `InviteRelationshipCreated` 是**数据落地事件**,表示"邀请关系已写入数据库",供奖励、任务等下游监听 *** ## 5. API 设计 ### 5.1 前台用户端 API 需 `auth:sanctum` 认证,响应为 Laravel 标准分页结构(`data` / `links` / `meta`)。 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/api/v1/invite/records` | 我的邀请记录列表(分页,支持 `per_page`) | | GET | `/api/v1/invite/stats` | 邀请统计(`invitee_count`、`has_inviter`) | > 邀请码不单独提供接口,已包含在 `UserResource` 的 `invite_code` 字段中。 **邀请记录列表响应字段**: | 字段 | 说明 | |------|------| | `id` | 记录 ID | | `invitee.id` / `.name` / `.username` / `.avatar` | 被邀请人信息 | | `description` | 描述/备注 | | `created_at` | 邀请时间 | ### 5.2 后台管理 API 需 `auth:admin` 认证 + 对应权限。 | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | GET | `/admin/invite-records` | `invite-records.index` | 邀请记录分页列表 | | GET | `/admin/invite-records/{id}` | `invite-records.show` | 邀请记录详情 | **列表筛选参数**(由 `InviteRecordIndexRequest` 校验,`InviteRecord::scopeFilter` 执行): | 参数 | 类型 | 说明 | |------|------|------| | `inviter_type` | string | 邀请人类型(`user` / `admin`) | | `inviter_id` | int | 邀请人 ID | | `invitee_type` | string | 被邀请人类型(`user` / `admin`) | | `invitee_id` | int | 被邀请人 ID | | `description` | string | 描述模糊匹配 | | `created_start` | date | 邀请时间起始 | | `created_end` | date | 邀请时间截止(须 ≥ `created_start`) | | `per_page` | int | 每页条数(1-100) | **响应字段**:`id`、`inviter_type`、`inviter_id`、`inviter_name`、`invitee_type`、`invitee_id`、`invitee_name`、`description`、`created_at` ### 5.3 后台菜单与权限 菜单挂在「用户管理」目录下(`user.invite-record`,sort 4),权限由迁移文件自动生成: | 权限 key | 说明 | |----------|------| | `invite-records.index` | 邀请记录列表(菜单权限) | | `invite-records.show` | 查看详情(按钮权限) | *** ## 6. 与现有系统的集成点 ### 6.1 User 模型 通过 trait(如 `HasInvitation`)为 User、Admin 等模型追加: ```php // 我的邀请码(多态归属,morph 名 owner,一个主体一个码) $this->inviteCode(): MorphOne // 我邀请的人 $this->invitees(): MorphMany // 通过 InviteRecord 的 inviter 反向关联 // 我的邀请人 $this->inviterRelation(): MorphOne // 通过 InviteRecord 的 invitee 反向关联 // 快捷方法 $this->hasInviter(): bool $this->getInviteCode(): string ``` 用户创建时 `UserObserver` 自动创建 InviteCode,用户硬删除时一并清理;UserExtra 不再承载邀请码。 ### 6.2 注册流程 在 `RegisterController` 中,收到 `invite_code` 后: 1. 表单校验:`Rule::exists(InviteCode::class, 'code')` 校验邀请码有效 2. 新增逻辑:`InviteService` 通过邀请码反查 owner(邀请人),创建 `InviteRecord`,触发 `InviteRelationshipCreated` 事件 3. 邀请码无效、自邀或已存在邀请关系时静默跳过,不影响注册主流程 ### 6.3 奖励系统(暂缓) 邀请奖励(金币/积分/VIP)后续可通过监听 `InviteRelationshipCreated` 事件处理,不写入邀请记录表本身。奖励类型枚举 `CoinType::TYPE_INVITE_REGISTER` 和 `PointType::TYPE_INVITE_REGISTER` 已预置,待需要时接入。 *** ## 7. 实施阶段 | 阶段 | 内容 | 交付物 | 状态 | |------|------|--------|------| | **Phase 1 数据模型** | `invite_codes` / `invite_records` 表迁移 + `InviteCode`、`InviteRecord` 模型 + 多态映射 + 工厂 + 单元测试 | 邀请码与邀请关系数据层 | ✅ 已完成 | | **Phase 2 关系建立** | 用户创建自动发放邀请码 + 注册流程集成(创建邀请关系)+ `InviteRelationshipCreated` 事件 + 功能测试 | 邀请码自动生成、邀请关系可正常建立 | ✅ 已完成 | | **Phase 3 查询接口** | 前台「我的邀请列表/统计」+ 后台邀请记录列表与详情 + 筛选作用域 + 权限菜单 | 前后端查询能力完整 | ✅ 已完成 | | **Phase 4 文档与发布** | Apifox 全量发布 + README 更新 | 生产可用 | ✅ 已完成 | > **暂缓项**(后续按需启动): > > * \~~邀请奖励发放~~(金币/积分/VIP 奖励、双向奖励) > * \~~与用户任务系统联动~~(触发 `invite_user` 任务进度) > * 测试覆盖率量化(需环境安装 Xdebug 或 PCOV 后执行 `php artisan test --coverage --min=70`) *** ## 7.1 Apifox 发布记录 项目:**Laravel Skel**(`8720052`),分支 `main`。 ### 数据模型(Schema) 严格遵循模块隔离,Admin 与 Api 各自独立定义,禁止跨模块 `$ref`: | Schema ID | 名称 | 模块 | 目录 | |-----------|------|------|------| | `312589523` | `InviteRecord` | Api (`8396001`) | 用户相关 (`20995717`) | | `312590135` | `InviteRecordAdminResource` | Admin (`8396002`) | 用户相关 (`20885420`) | ### 接口 | 接口 ID | 名称 | 方法 | 路径 | 模块 | 目录 | |---------|------|------|------|------|------| | `513246901` | 获取我的邀请记录 | GET | `/api/v1/invite/records` | Api | V1/邀请接口 (`95504233`) | | `513246906` | 获取邀请统计 | GET | `/api/v1/invite/stats` | Api | V1/邀请接口 (`95504233`) | | `513246910` | 获取邀请记录列表 | GET | `/admin/invite-records` | Admin | 用户管理/邀请记录 (`95504235`) | | `513246915` | 获取邀请记录详情 | GET | `/admin/invite-records/{inviteRecord}` | Admin | 用户管理/邀请记录 (`95504235`) | ### 分页规范 两个列表接口均按项目规范启用全局参数 `page` / `per_page`,并通过 `x-apifox-refs` 引用对应模块的 `EloquentPaginate` 后覆写 `data` 字段: | 模块 | EloquentPaginate ID | |------|---------------------| | Api | `307396242` | | Admin | `304922372` | ### 标签 * 前台:`邀请`、`分页接口`(仅列表) * 后台:`邀请记录`、`分页接口`(仅列表) *** ## 8. 代码结构 ``` app/ ├── Models/ │ └── Invite/ │ ├── InviteCode.php # 邀请码模型(多态归属 owner) │ └── InviteRecord.php # 邀请记录模型(含 scopeFilter) ├── Events/ │ ├── Invite/ │ │ └── InviteRelationshipCreated.php # 数据落地事件 │ └── User/ │ └── InviteRegistered.php # 业务入口事件 ├── Listeners/ │ └── User/ │ └── InviteRegisteredListener.php # 邀请注册监听器(队列异步) ├── Services/ │ └── InviteService.php # 邀请关系建立编排 ├── Support/ │ └── Traits/ │ └── HasInvitation.php # 邀请关联 trait ├── Observers/ │ └── UserObserver.php # 用户创建时自动发放邀请码 ├── Http/ │ ├── Controllers/ │ │ ├── Api/V1/InviteController.php # 前台:records / stats │ │ └── Admin/InviteRecordController.php # 后台:index / show │ ├── Requests/ │ │ └── Admin/Invite/ │ │ └── InviteRecordIndexRequest.php # 后台列表筛选校验 │ └── Resources/ │ ├── Api/V1/InviteRecordResource.php # 前台资源 │ └── Admin/InviteRecordResource.php # 后台资源 config/ └── morph_maps.php # invite_code / invite_record 别名 routes/ ├── api_v1.php # /api/v1/invite/* └── admin.php # /admin/invite-records/* database/ ├── migrations/ │ ├── 0001_01_01_000018_create_invite_codes_table.php │ ├── 0001_01_01_000019_create_invite_records_table.php │ └── 2026_10_31_111934_seed_initial_admin_data.php # 菜单与权限 └── factories/ └── Invite/ ├── InviteCodeFactory.php └── InviteRecordFactory.php tests/ ├── Unit/ │ ├── Models/Invite/ │ │ ├── InviteCodeTest.php │ │ └── InviteRecordTest.php │ ├── Models/User/HasInvitationTest.php │ ├── Listeners/InviteRegisteredListenerTest.php │ ├── Observers/UserObserverTest.php │ └── Services/InviteServiceTest.php └── Feature/ ├── Api/V1/InviteControllerTest.php └── Admin/InviteRecordControllerTest.php ``` --- --- url: https://www.aiforge.cn/technical-solutions/visit-tracking.md --- # 访问记录技术方案 ## 1. 设计目标与定位 访问记录用于记录用户对任意多态资源(Post / Goods / Video / Comment 等)的浏览行为,是后续 PV/UV 分析、"最近浏览"、热度排序等业务场景的基础数据层。本系统以**轻量、append-only、可扩展**为设计原则,仅记录"看过"这一事实,不做撤销、计数字段维护或唯一约束。 ### 1.1 设计原则 | 原则 | 说明 | |------|------| | **Append-only** | 仅插入新记录,不提供撤销/删除接口;表无 `updated_at` | | **多态设计** | `source_type` + `source_id` 复用项目现有 `morph_maps` 别名,与 Like/Collection/Comment 保持一致 | | **不做唯一约束** | 同一用户对同一资源可多次访问,符合 PV(页面浏览量)语义 | | **不维护计数** | 区别于 Like/Collection,Visit 不维护目标资源的访问次数(避免与多态资源耦合,PV 由后续聚合查询按需计算) | | **同步写入** | 当前采用同步 `insert`;若后续出现性能瓶颈可平滑迁移到队列异步落库 | ### 1.2 与现有互动表的关系 | 维度 | Like / Collection | Visit | |------|-------------------|-------| | 多态资源 | ✅ morphs | ✅ `source_type` + `source_id` | | 唯一约束 | ✅ `uniq_user_*` | ❌(允许重复) | | 计数字段 | `like_count` / `collection_count` | ❌(不维护) | | `updated_at` | ✅ | ❌ | | `user_id` 隐藏 | ✅ `#[Hidden]` | ❌(保留,便于自查询) | | 删除/撤销 | ✅ DELETE 接口 | ❌(append-only) | | IP | ❌ | ✅ `varbinary(16)` | *** ## 2. 数据模型 ### 2.1 数据表:visits 访问记录表,每条记录代表一次用户对资源的访问行为。 | 字段 | 类型 | 说明 | |------|------|------| | `id` | BIGINT UNSIGNED PK AUTO_INCREMENT | 主键 | | `user_id` | BIGINT UNSIGNED NOT NULL | 访问用户 ID(前台用户) | | `source_type` | VARCHAR(255) NOT NULL | 多态类型别名(须在 `config/morph_maps.php` 中注册) | | `source_id` | BIGINT UNSIGNED NOT NULL | 资源 ID | | `ip` | VARBINARY(16) NULL | 访问 IP(兼容 IPv4/IPv6,与 `login_histories` 一致) | | `created_at` | TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP | 访问时间 | #### 索引 | 索引名 | 字段 | 类型 | 说明 | |--------|------|------|------| | `idx_visit_source` | `source_type, source_id` | 普通索引 | 按资源反查访问者(资源热度 / PV 计算) | | `idx_visit_user_source` | `user_id, source_type, source_id` | 普通索引 | 按用户查访问过的资源("最近浏览"等场景) | > 设计要点: > > * `source_type` 长度 255 与项目 morph_maps 别名体系保持一致 > * `ip` 在迁移文件中通过 `ALTER TABLE ... MODIFY COLUMN ip VARBINARY(16)` 收紧为定长二进制存储,节省空间并兼容 IPv6 > * 不创建外键约束,避免与多态资源耦合;多态完整性由业务层(`source_types()` 白名单)兜底 *** ### 2.2 模型:Visit **命名空间**:`App\Models\Visit` **表名**:`visits` #### 关键属性 ```php #[Table('visits')] #[Fillable(['user_id', 'source_type', 'source_id', 'ip'])] class Visit extends Model { use HasFactory, HasUser; public const UPDATED_AT = null; } ``` #### 关联 通过 `HasUser` trait 复用: ```php /** @return BelongsTo */ public function user(): BelongsTo /** 查询作用域:限定到指定用户的访问记录 */ public function scopeForUser(Builder $query, User|int|string $user): Builder ``` > **注意**:Visit 模型**未定义** `source(): MorphTo` 关联,因为: > > 1. 访问记录的目标资源访问频次极低(仅写入与列表),按需通过 `morphMap` 反查即可 > 2. 避免每次记录访问都额外 join 多态目标资源 > 3. 资源后续被删除时不影响访问历史 #### 字段类型映射(`casts()`) | 字段 | 类型 | |------|------| | `id` | integer | | `user_id` | integer | | `source_type` | string | | `source_id` | integer | | `ip` | string | | `created_at` | datetime | *** ## 3. 核心流程 ### 3.1 记录一次访问(写入流程) ``` 客户端打开资源详情页 ↓ POST /api/v1/visits 携带 { source_type, source_id } ↓ VisitController::record(RecordVisitRequest) ↓ RecordVisitRequest::prepareForValidation() - 注入 user_id(来自 Sanctum 当前用户) - 注入 ip(来自 Request::ip(),即 X-Forwarded-For 解析后的客户端 IP) ↓ rules() 校验 source_type 是否在 source_types() 白名单 ↓ Visit::create($request->validated()) ↓ 返回 201 + VisitResource ``` **设计要点**: * `user_id` 与 `ip` **禁止**由客户端传入,由 `prepareForValidation()` 在服务端注入,符合「控制器不写赋值逻辑」的项目规范 * `user_id` 通过 Sanctum 中间件保证已登录;IP 通过 Laravel `Request::ip()` 自动处理反向代理头 ### 3.2 我的访问记录列表(读取流程) ``` GET /api/v1/user/visits?source_type=comment&page=1&per_page=15 ↓ VisitController::myVisits(MyVisitsIndexRequest) ↓ Visit::forUser($request->user()->id) ->when(source_type, where source_type = ?) ->orderByDesc('id') ->paginate(per_page($request)) ↓ VisitResource::collection() ``` **索引使用**: * 无 `source_type` 过滤:使用 `PRIMARY KEY` 倒序扫描(id 即聚集索引) * 有 `source_type` 过滤:依靠 `idx_visit_user_source(user_id, source_type, source_id)` 前缀匹配 * 反向场景(按资源查访问者):使用 `idx_visit_source(source_type, source_id)` *** ## 4. API 设计 ### 4.1 前台用户端 API 两条接口均需 `auth:sanctum` 认证。 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/api/v1/visits` | 记录一次访问(`user_id` / `ip` 由后端注入) | | GET | `/api/v1/user/visits` | 我的访问记录列表(分页,可按 `source_type` 过滤) | #### 4.1.1 POST /api/v1/visits **请求体**: ```json { "source_type": "comment", "source_id": 1001 } ``` | 字段 | 必填 | 校验规则 | |------|------|----------| | `source_type` | ✅ | 命中 `source_types()` 白名单(`morph_maps.php` 已注册别名) | | `source_id` | ✅ | integer ≥ 1 | **响应 201**: ```json { "id": 62825, "source_type": "comment", "source_id": 1001, "ip": "192.168.1.1", "created_at": "2026-09-30 10:00:00" } ``` > 客户端不需要关心 `user_id` 与 `ip`;由服务端在 `prepareForValidation()` 中注入,避免越权写入他人访问记录。 **错误响应**: | 状态码 | 场景 | |--------|------| | 401 | 未登录或 Token 无效 | | 422 | `source_type` 不在白名单、`source_id` 缺失或非法 | #### 4.1.2 GET /api/v1/user/visits **查询参数**: | 参数 | 必填 | 说明 | |------|------|------| | `source_type` | ❌ | 可选过滤,须命中白名单 | | `page` | ❌ | 全局分页参数,默认 1 | | `per_page` | ❌ | 全局分页参数,默认 15,范围 1-100 | **响应 200**:Laravel 标准分页结构,`data` 字段为 `VisitResource[]`。 ```json { "data": [ { "id": 62825, "source_type": "comment", "source_id": 1001, "ip": "192.168.1.1", "created_at": "2026-09-30 10:00:00" } ], "links": {...}, "meta": { "current_page": 1, "last_page": 1, "per_page": 15, "total": 1 } } ``` ### 4.2 后台管理 API > 本期**不实现**管理后台接口(`/admin/visits`),仅保留扩展点: > > * 模型 `Visit` 已就绪,遵守 Admin/Api 模块隔离规范,未来需要时可独立创建 `VisitAdminResource` 与 `Admin/VisitController` > * 如需统计 PV/UV,建议使用聚合查询而非全量拉取,避免大表扫描 *** ## 5. 与现有系统的集成点 ### 5.1 多态资源白名单 复用项目 `config/morph_maps.php` 注册的多态别名,无需额外配置: ```php // config/morph_maps.php return [ 'user' => App\Models\User::class, 'admin' => App\Models\Admin\Admin::class, 'comment' => App\Models\Content\Comment::class, // ... ]; ``` 校验来源(`bootstrap/helpers.php`): ```php function source_types(): array { return array_keys(Relation::morphMap()); } ``` 任何未在 `morph_maps.php` 中注册的 `source_type` 均返回 422,避免脏数据污染统计。 ### 5.2 与 IP 存储规范的对齐 与 `login_histories` 表保持一致:`ip` 字段在 MySQL 下迁移为 `VARBINARY(16)`,支持 IPv4/IPv6 双栈。读取时按需调用 `INET_NTOA(ip)` 反解(不在 Resource 中暴露原始二进制,由 Eloquent cast 为 string 自动处理)。 ### 5.3 与控制器 / Request 职责划分规范 严格遵守 `.trae/rules/controller_request_rules.md`: | 维度 | 实现位置 | |------|----------| | `user_id` 注入 | `RecordVisitRequest::prepareForValidation()` | | `ip` 注入 | `RecordVisitRequest::prepareForValidation()` | | `source_type` 白名单校验 | `RecordVisitRequest::rules()` | | 业务调用 | `VisitController::record()` 仅 `Visit::create($request->validated())` | | 列表查询条件组装 | `VisitController::myVisits()` 通过 `MyVisitsIndexRequest` 校验后用 query builder 拼装 | 控制器中**不存在**任何 `$request->input(...)` / `$request->merge(...)` / `$request->ip()` 等赋值调用。 *** ## 6. 性能与扩展性 ### 6.1 写入路径 * 单条 `INSERT INTO visits (...)` 在 InnoDB 下写入延迟 < 1ms(4 亿级数据量测试基线) * 写入频次取决于客户端埋点策略(建议:进入详情页 + 离开时各一次,或仅进入时一次) ### 6.2 后续优化点(按需启动) | 场景 | 方案 | |------|------| | 写入 QPS 高峰 | 迁移到队列异步落库(事件 `VisitRecorded` + Listener `ShouldQueue`) | | 数据量爆炸(>1 亿行) | 按 `created_at` 月份分区(PARTITION BY RANGE) | | PV 实时聚合 | 引入 Redis 计数器 + 定时任务回写统计表 | | 用户画像/最近浏览 | Redis Sorted Set 按 `user_id + source_type` 维度去重并按时间排序 | ### 6.3 索引覆盖检查 | 查询场景 | 命中索引 | |----------|----------| | 我的访问(无 source_type) | `PRIMARY KEY (id)` 倒序 | | 我的访问(带 source_type) | `idx_visit_user_source` | | 某资源的访问列表 | `idx_visit_source` | | 某资源在某用户的访问 | `idx_visit_user_source` 前缀 | *** ## 7. 实施阶段 | 阶段 | 内容 | 交付物 | 状态 | |------|------|--------|------| | **Phase 1 数据模型** | `visits` 表迁移(含 `VARBINARY(16)` 改列)+ `Visit` 模型 + `VisitFactory` + `forSource()` 状态 | 数据层就绪 | ✅ 已完成 | | **Phase 2 接口实现** | `RecordVisitRequest` / `MyVisitsIndexRequest` + `VisitResource` + `VisitController` + 路由注册 + Feature 测试 9 用例 / 31 断言全通过 | Api 接口可用 | ✅ 已完成 | | **Phase 3 Apifox 发布** | Api 模块下独立 `VisitResource` Schema + 2 个接口(POST 记录、GET 列表)+ 分页规范 + 标签 `访问记录`/`分页接口` | 文档同步 | ✅ 已完成 | | **Phase 4 README 与 doc** | 项目 README 接口表格 + `business-tables.md` 表结构 + `api-v1.md` 接口文档 + 本技术方案 | 项目文档完备 | ✅ 已完成 | > **暂缓项**: > > * \~~后台管理接口~~(PV/UV 统计由后续 BI 需求驱动,避免过度设计) > * \~~PV 实时聚合与 Redis 缓存~~(按需启动) *** ## 8. Apifox 发布记录 项目:**Laravel Skel**(`8720052`),分支 `main`。 ### 数据模型(Schema) 严格遵循模块隔离,Api 模块下独立定义: | Schema ID | 名称 | 模块 | 目录 | |-----------|------|------|------| | `317970962` | `VisitResource` | Api (`8396001`) | V1(公共模块) | > 仅创建 Api 模块下的 Schema;如后续需 Admin 接口,必须独立创建 `VisitAdminResource`,禁止跨模块 `$ref`。 ### 接口 | 接口 ID | 名称 | 方法 | 路径 | 模块 | 目录 | |---------|------|------|------|------|------| | `521022106` | 记录一次访问 | POST | `/api/v1/visits` | Api | V1/互动接口/访问记录 (`96882392`) | | `521022351` | 我的访问记录 | GET | `/api/v1/user/visits` | Api | V1/互动接口/访问记录 (`96882392`) | ### 分页规范 列表接口按项目规范启用全局参数 `page` / `per_page`,并通过 `x-apifox-refs` 引用 Api 模块的 `EloquentPaginate` (`307396242`) 后覆写 `data` 字段为 `VisitResource` (`317970962`)。 ### 标签 * 写入接口:`访问记录` * 列表接口:`访问记录` + `分页接口` *** ## 9. 代码结构 ``` app/ ├── Models/ │ └── Visit.php # 访问记录模型(UPDATED_AT=null) ├── Http/ │ ├── Controllers/Api/V1/ │ │ └── VisitController.php # 控制器(无赋值逻辑) │ ├── Requests/Api/V1/Visit/ │ │ ├── RecordVisitRequest.php # 写入:prepareForValidation 注入 user_id/ip │ │ └── MyVisitsIndexRequest.php # 列表:source_type 白名单校验 │ └── Resources/Api/V1/ │ └── VisitResource.php # Api 模块独立 Resource routes/ └── api_v1.php # /api/v1/visits + /api/v1/user/visits database/ ├── migrations/ │ └── 2026_09_30_000001_create_visits_table.php # VARBINARY(16) ip + 两个索引 └── factories/ └── VisitFactory.php # 含 forSource() 状态 tests/ └── Feature/Api/V1/ └── VisitControllerTest.php # 9 用例 / 31 断言 ``` --- --- url: https://www.aiforge.cn/technical-solutions/user-tasks.md --- # 用户任务系统技术方案 ## 1. 设计目标与定位 用户任务系统是一套**通用、可扩展、事件驱动**的任务奖励引擎,用于驱动用户行为、提升活跃度和留存。业务方可通过配置任务模板(任务组 + 任务),并在关键业务节点触发对应事件,即可自动完成任务进度判定与奖励发放。 ### 1.1 设计原则 | 原则 | 说明 | |------|------| | **配置驱动** | 任务类型、条件、奖励均在数据库中配置,无需改代码即可上线新任务 | | **事件驱动** | 业务侧只负责发事件,任务系统监听事件并判定进度,解耦业务与任务 | | **阶梯奖励** | 支持累计型阶梯任务(如"累计观看30秒/60秒/120秒"),一次行为可同时推进多阶 | | **奖励可扩展** | 奖励类型通过 `reward_type` 区分,支持金币、积分、VIP 时长等,新增奖励类型无需改表结构 | | **幂等安全** | 同一任务同一周期内只奖励一次,并发场景下依靠唯一索引 + 事务保证 | | **通用骨架** | 作为骨架项目能力,提供基础任务类型(签到、邀请、完善资料等),业务项目按需扩展 | ### 1.2 与参考项目的差异 参考 `drama-forge-base` 的任务系统是面向短剧业务定制的(看剧、看广告等),本系统作为通用骨架项目,采用以下策略: * 保留**任务组 + 任务 + 完成日志**的三层核心结构 * 保留**事件驱动 + 监听器判定**的架构模式 * 移除短剧业务特有的任务类型(看剧、视频广告、激励视频) * 新增通用业务常用任务类型(签到、完善资料、邀请好友、每日登录、绑定手机/邮箱等) * 奖励体系对齐本项目现有**金币 / 积分 / VIP** 三种虚拟资产,不再引入新资产类型 * 奖励发放统一通过 `CoinHelper` / `PointHelper` / VIP 能力,禁止直接操作余额字段 *** ## 2. 核心概念与数据模型 ### 2.1 核心实体 ``` TaskGroup(任务组) ──1:N── Task(任务) ──1:N── TaskProgress(用户任务进度) └──1:N── TaskLog(完成/领取日志) ``` ### 2.2 数据表设计 #### 2.2.1 任务组表 (task_groups) 按业务场景组织任务的分组容器,如"新手任务""每日任务""成就任务"。 | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT PK | 主键 | | name | VARCHAR(64) | 任务组名称,如"每日任务" | | code | VARCHAR(64) UNIQUE | 任务组唯一标识,如 `daily` / `newbie` / `achievement` | | description | VARCHAR(255) | 描述 | | reset_type | VARCHAR(20) | 重置周期:`none`(不重置) / `daily`(每日) / `weekly`(每周) / `monthly`(每月) | | visibility | TINYINT | 前台是否可见:0 不可见 1 可见 | | status | TINYINT | 状态:0 禁用 1 启用 | | sort | INT | 排序 | | task_count | INT | 任务数(冗余) | | created_at / updated_at | TIMESTAMP | 时间戳 | | deleted_at | TIMESTAMP | 软删除 | **索引**:`(status, sort)`、`code` 唯一索引。 #### 2.2.2 任务表 (tasks) 单个任务的定义,包含类型、条件、奖励。 | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT PK | 主键 | | group_id | BIGINT | 所属任务组 ID | | name | VARCHAR(64) | 任务名称,如"每日签到" | | code | VARCHAR(64) | 任务唯一标识(同组内唯一),如 `daily_sign_in` | | type | VARCHAR(32) | 任务类型,见 `TaskType` 枚举 | | description | VARCHAR(255) | 任务描述 | | condition | JSON | 任务条件,见下方"条件结构" | | target_count | INT DEFAULT 1 | 目标次数/数量(累计型任务用,1 表示一次性任务) | | reward_type | VARCHAR(32) | 奖励类型:`coin` / `point` / `vip_days` / `none` | | reward_amount | INT DEFAULT 0 | 奖励数量(金币数/积分数/VIP天数) | | claim_mode | VARCHAR(20) | 领取方式:`auto`(达成自动发放) / `manual`(用户手动领取) | | icon | VARCHAR(255) | 任务图标 URL(可选) | | status | TINYINT | 状态:0 禁用 1 启用 | | sort | INT | 排序 | | log_count | INT | 完成次数(冗余统计) | | created_at / updated_at | TIMESTAMP | 时间戳 | | deleted_at | TIMESTAMP | 软删除 | **索引**:`(group_id, status, sort)`、`(type, status)`、`code` 唯一索引。 **条件结构(condition JSON)**: 不同任务类型使用不同字段,通过 `AsTaskCondition` Cast 提供默认值: ```json { "serial_days": 0, // 连续天数(签到类) "target_value": 0, // 目标值(数值累计类,如邀请人数) "completeness_threshold": 100 // 资料完整度阈值,百分比(完善资料类) } ``` #### 2.2.3 用户任务进度表 (task_progress) 记录每个用户在每个任务上的当前进度,用于累计型任务的中间状态。一次性任务也写入一条记录表示已完成。 | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT PK | 主键 | | user_id | BIGINT | 用户 ID | | task_id | BIGINT | 任务 ID | | group_id | BIGINT | 任务组 ID(冗余,便于按组查询) | | current_value | INT DEFAULT 0 | 当前进度值 | | is_completed | TINYINT DEFAULT 0 | 是否已完成(达成目标) | | claimed_at | TIMESTAMP NULL | 领取时间(manual 模式用) | | period_key | VARCHAR(32) | 周期标识,如 `2026-09-09`(日) / `2026-W37`(周) / `2026-09`(月) / `all`(永久) | | completed_at | TIMESTAMP NULL | 完成时间 | | created_at / updated_at | TIMESTAMP | 时间戳 | **唯一索引**:`uniq_user_task_period (user_id, task_id, period_key)` — 同一用户同一任务同一周期只能有一条进度记录。 **索引**:`(user_id, group_id, period_key)`、`(task_id, is_completed)`。 #### 2.2.4 任务完成日志表 (task_logs) 记录用户完成任务并领取奖励的流水,用于审计和去重。 | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT PK | 主键 | | user_id | BIGINT | 用户 ID | | task_id | BIGINT | 任务 ID | | group_id | BIGINT | 任务组 ID(冗余) | | reward_type | VARCHAR(32) | 奖励类型(冗余快照) | | reward_amount | INT | 奖励数量(冗余快照) | | reward_trade_id | BIGINT NULL | 奖励关联的交易记录 ID(coin_trade_id / point_trade_id 等) | | period_key | VARCHAR(32) | 周期标识 | | claimed_by | VARCHAR(20) | 领取触发方:`system`(自动) / `user`(手动) | | created_at | TIMESTAMP | 完成时间 | **唯一索引**:`uniq_user_task_period (user_id, task_id, period_key)` — 保证同一周期同一任务只发放一次奖励。 **索引**:`(user_id, created_at)`、`(task_id, created_at)`、`(group_id, created_at)`。 *** ## 3. 任务类型体系 ### 3.1 TaskType 枚举 | 枚举值 | 值 | 说明 | 重置周期建议 | |--------|-----|------|-------------| | `SIGN_IN` | `sign_in` | 签到任务 | daily | | `COMPLETE_PROFILE` | `complete_profile` | 完善资料 | none(一次性) | | `FOLLOW_OFFICIAL_ACCOUNT` | `follow_official_account` | 关注公众号 | none(一次性) | | `INVITE_USER` | `invite_user` | 邀请好友注册 | none(累计) | | `DAILY_LOGIN` | `daily_login` | 每日登录 | daily | | `CUSTOM_ACTION` | `custom_action` | 自定义行为事件 | 按业务而定 | > `CUSTOM_ACTION` 为兜底类型,业务方可通过事件名 + `target_count` 快速接入新任务,无需新增枚举值。 ### 3.2 各类型任务判定逻辑 #### 3.2.1 签到任务(sign_in) * **触发事件**:`UserSignedIn`(携带 `user_id`、`serial_days` 连续签到天数) * **实现方式**:**多任务 + serial_days 条件匹配**(方案A)— 每个连续签到天数对应一条独立任务,由运营在后台配置。 * **判定逻辑**: 1. 监听 `UserSignedIn` 事件,获取当前用户的连续签到天数 `serial_days` 2. 查询所有 `type = sign_in` 且 `status = 1` 的任务 3. 遍历任务,按 `condition.serial_days == serial_days` 精确匹配 4. 匹配成功则发放对应奖励(`claim_mode = auto` 自动发放) * **period_key 策略**: * **每日签到基础奖励**(`serial_days = 0` 表示每日签到通用奖励):`period_key = Y-m-d`,每日可领一次 * **连续签到阶梯奖励**(`serial_days > 0`):`period_key = sign_in_day_{serial_days}`,同一连续天数一生只领一次(断签重来也不重复发放,避免刷奖励) * **奖励模式**:auto(自动发放) * **后台配置示例**: | 任务名称 | condition.serial_days | reward_type | reward_amount | |---------|----------------------|-------------|---------------| | 每日签到 | 0 | coin | 10 | | 连续签到第2天 | 2 | coin | 15 | | 连续签到第3天 | 3 | coin | 20 | | 连续签到第7天 | 7 | coin | 100 | | 连续签到第30天 | 30 | coin | 500 | * **注意**:签到任务不走通用的 `current_value >= target_count` 判定流程,由 `SignInTaskListener` 特殊处理。`condition.serial_days = 0` 的任务表示"每日签到"基础奖励,每天都能领;`serial_days > 0` 表示连续签到第 N 天的阶梯奖励,一生只领一次。 #### 3.2.2 完善资料任务(complete_profile) * **触发事件**:`UserProfileUpdated`(携带 `user_id`,由 `UserController@modifyProfile` 和头像上传接口在用户更新资料后触发) * **判定逻辑**:资料完整度基于 `UserProfile` 模型字段计算,共 6 个维度,每填一项算 1 分: | 序号 | 维度 | 所在表 | 字段 | 判定为"已填"的条件 | |------|------|--------|------|-------------------| | 1 | 昵称 | users | `name` | `name is not null`(注意:null 时 getter 会 fallback 为 username,此处必须判断数据库原始值非 null) | | 2 | 头像 | users | `avatar` | `avatar is not null` | | 3 | 性别 | user_profiles | `gender` | `gender != 0`(0=保密即未选择) | | 4 | 生日 | user_profiles | `birthday` | `birthday is not null` | | 5 | 签名 | user_profiles | `bio` | `bio is not null and bio != ''` | | 6 | 地区 | user_profiles | `province_id` / `city_id` / `district_id` | 三个字段均 `is not null`(省/市/区三级联动,都选才算填了地区) | 完整度 = 已填项数 / 6(满分 100%)。当完整度达到任务 `condition.completeness_threshold`(默认 100%,即全部填完)时标记完成。阈值通过任务的 `condition` JSON 配置,方便运营灵活调整。 * **condition 字段**:`{ "completeness_threshold": 100 }`,单位为百分比 * **period_key 策略**:`all`(一次性,完成后永久有效,不重置) * **奖励模式**:auto * **重置周期**:none * **注意**:资料完整度计算逻辑封装在 `TaskService::calculateProfileCompleteness($user)` 方法中,监听器调用此方法获得完整度百分比,与任务阈值对比判定是否达成。 #### 3.2.3 关注公众号任务(follow_official_account) * **触发事件**:`UserFollowedOfficialAccount`(公众号关注/取关回调时触发,携带 `user_id`、`openid`、`is_followed`) * **判定逻辑**: 1. 公众号关注回调(`is_followed = true`)时,通过 `openid` 匹配本地用户(`socials` 表中 `provider = wechat_official` 的记录) 2. 匹配成功则对用户的 `follow_official_account` 类型任务标记完成 3. 取关回调(`is_followed = false`)不做回滚(已发放的奖励不回收,但可在前台标记任务为未完成) * **奖励模式**:auto * **重置周期**:none * **注意**:依赖项目已有 `WechatService` 和公众号回调处理,关注事件由微信服务器推送,需在 `WechatController` 中处理 `event` 消息并触发事件。用户与公众号的关联通过 `socials` 表的 `provider = wechat_official` 记录建立。 #### 3.2.4 邀请好友任务(invite_user) * **触发事件**:`App\Events\Invite\InviteRelationshipCreated`(已有事件,邀请关系建立时触发,携带 `InviteRecord` 模型) * **判定逻辑**: 1. 从事件中获取 `InviteRecord`,通过 `$inviteRecord->inviter` 获取邀请人 2. 查询邀请人的 `invite_user` 类型任务 3. 对匹配任务的进度 `current_value + 1` 4. 当 `current_value >= target_count` 时标记完成 * **奖励模式**:auto 或 manual * **重置周期**:none * **注意**:该事件由 `InviteService::createRelationshipByCode()` 在邀请关系写入 `invite_records` 表后触发,任务系统监听此事件即可接入奖励。已有 `CoinType::TYPE_INVITE_REGISTER` 和 `PointType::TYPE_INVITE_REGISTER` 枚举可用。 #### 3.2.5 每日登录任务(daily_login) * **触发事件**:`UserLoggedIn` * **判定逻辑**:当日有登录记录即完成,每日一次 * **奖励模式**:auto * **重置周期**:daily #### 3.2.6 自定义行为任务(custom_action) * **触发事件**:`TaskActionTriggered`(携带 `action` 和 `value`) * **判定逻辑**: 1. 匹配 `condition.action == event.action` 的任务 2. 进度值累加 `value` 3. 达到 `target_count` 即完成 * **奖励模式**:manual 或 auto * **重置周期**:依业务而定 *** ## 4. 奖励体系 ### 4.1 奖励类型 | 类型 | 值 | 发放方式 | 说明 | |------|-----|---------|------| | 金币 | `coin` | `CoinHelper::incr()` | 对应 `users.available_coins` | | 积分 | `point` | `PointHelper::incr()` | 对应 `users.available_points` | | VIP 天数 | `vip_days` | `UserHelper::extendVip()` | 对应 `users.vip_expires_at` | | 无奖励 | `none` | — | 纯成就/解锁性质 | ### 4.2 发放流程 ``` 事件触发 → 监听器 → 更新 task_progress → 判定是否达成 ↓ 达成且 claim_mode=auto 创建 task_log(事务内) ↓ 调用对应奖励 Helper 的 incr 方法 ↓ 回填 reward_trade_id 到 task_log ``` ### 4.3 并发与幂等保障 1. **唯一索引防重**:`task_progress` 和 `task_logs` 均有 `(user_id, task_id, period_key)` 唯一索引,并发下重复插入会抛唯一键冲突,捕获后直接返回成功。 2. **事务包裹**:进度更新 + 奖励发放 + 日志写入在同一个 DB 事务中。 3. **先写日志后发奖励**:在事务内先 insert task_log(获取行锁),再发奖励,确保奖励只发一次。 *** ## 5. 事件系统设计 ### 5.1 事件列表 | 事件类 | 触发场景 | 携带数据 | |--------|---------|---------| | `Events\User\UserSignedIn` | 用户签到 | `user_id`、`serial_days` | | `Events\User\UserLoggedIn` | 用户登录 | `user_id`、`ip` | | `Events\User\UserProfileUpdated` | 用户更新资料 | `user_id`、`completeness` | | `Events\User\UserFollowedOfficialAccount` | 公众号关注回调 | `user_id`、`openid`、`is_followed` | | `Events\Invite\InviteRelationshipCreated` | 邀请关系建立(已有事件) | `inviteRecord`(含 `inviter`、`invitee` 关联) | | `Events\Task\TaskActionTriggered` | 通用任务行为触发 | `user_id`、`action`、`value` | ### 5.2 监听器列表 | 监听器 | 监听事件 | 职责 | |--------|---------|------| | `Listeners\Task\SignInTaskListener` | `UserSignedIn` | 处理签到类任务进度 | | `Listeners\Task\LoginTaskListener` | `UserLoggedIn` | 处理每日登录任务 | | `Listeners\Task\ProfileTaskListener` | `UserProfileUpdated` | 处理完善资料任务 | | `Listeners\Task\FollowOfficialAccountTaskListener` | `UserFollowedOfficialAccount` | 处理关注公众号任务 | | `Listeners\Task\InviteTaskListener` | `InviteRelationshipCreated` | 处理邀请任务 | | `Listeners\Task\CustomActionTaskListener` | `TaskActionTriggered` | 处理自定义行为任务 | 每个监听器内部按以下通用流程处理: ```php // 伪代码 public function handle($event): void { // 1. 找出该类型下所有启用的任务 $tasks = Task::where('type', $this->taskType)->where('status', 1)->get(); foreach ($tasks as $task) { // 2. 计算 period_key $periodKey = $task->group->getPeriodKey(); // 3. upsert 进度(靠唯一索引防并发) $progress = TaskProgress::updateOrCreate( ['user_id' => $event->userId, 'task_id' => $task->id, 'period_key' => $periodKey], [...增量更新 current_value...] ); // 4. 判定是否达成 if ($progress->isCompleted() && !$progress->is_claimed) { // 5. 发放奖励(事务内) $task->grantReward($event->userId, $periodKey); } } } ``` #### 5.2.1 特殊监听器:SignInTaskListener 签到任务不走通用的 `current_value` 累加判定流程,因为连续签到天数由签到系统维护,任务系统只负责"匹配 + 发放"。 ```php // SignInTaskListener 伪代码 public function handle(UserSignedIn $event): void { $serialDays = $event->serial_days; // 1. 查询所有启用的签到任务 $tasks = Task::where('type', TaskType::SIGN_IN) ->where('status', 1) ->with('group') ->get(); foreach ($tasks as $task) { $conditionSerialDays = (int) ($task->condition['serial_days'] ?? 0); // 2. serial_days = 0 表示每日签到基础奖励,每天都匹配 if ($conditionSerialDays === 0) { $periodKey = date('Y-m-d'); $this->tryGrantReward($event->user_id, $task, $periodKey); continue; } // 3. serial_days > 0 表示连续签到阶梯奖励,精确匹配 if ($conditionSerialDays === $serialDays) { $periodKey = "sign_in_day_{$conditionSerialDays}"; $this->tryGrantReward($event->user_id, $task, $periodKey); } } } ``` > 签到任务同样写入 `task_progress` 和 `task_logs`,保持数据一致性,方便前台列表统一展示。 *** ## 6. API 设计 ### 6.1 前台用户端 API(/api/v1) | 方法 | 路径 | 说明 | 权限 | 状态 | |------|------|------|------|------| | GET | `/api/v1/tasks` | 任务中心列表(按分组返回,含用户进度) | 已登录用户 | ✅ 已完成 | | POST | `/api/v1/tasks/{task_id}/claim` | 手动领取任务奖励 | 已登录用户 | ✅ 已完成 | | POST | `/api/v1/tasks/sign-in` | 签到(同时触发签到事件) | 已登录用户 | ⏳ 待实现 | ### 6.2 后台管理 API(/admin) | 方法 | 路径 | 说明 | 权限 | 状态 | |------|------|------|------|------| | GET | `/admin/task-groups` | 任务组分页列表 | task-groups.index | ✅ 已完成 | | GET | `/admin/task-groups/options` | 任务分组选项(不分页,下拉选择用) | task-groups.index | ✅ 已完成 | | POST | `/admin/task-groups` | 创建任务分组 | task-groups.create | ✅ 已完成 | | GET | `/admin/task-groups/{id}` | 任务分组详情(含分组下任务) | task-groups.index | ✅ 已完成 | | PUT | `/admin/task-groups/{id}` | 更新任务分组 | task-groups.edit | ✅ 已完成 | | PUT | `/admin/task-groups/{id}/toggle-status` | 切换任务分组启用状态 | task-groups.edit | ✅ 已完成 | | DELETE | `/admin/task-groups/{id}` | 删除任务分组(有任务时不允许删除) | task-groups.delete | ✅ 已完成 | | GET | `/admin/tasks` | 任务分页列表(支持 group_id/type/status/keyword 筛选) | tasks.index | ✅ 已完成 | | POST | `/admin/tasks` | 创建任务 | tasks.create | ✅ 已完成 | | GET | `/admin/tasks/{id}` | 任务详情 | tasks.index | ✅ 已完成 | | PUT | `/admin/tasks/{id}` | 更新任务 | tasks.edit | ✅ 已完成 | | PUT | `/admin/tasks/{id}/toggle-status` | 切换任务启用状态 | tasks.edit | ✅ 已完成 | | DELETE | `/admin/tasks/{id}` | 删除任务 | tasks.delete | ✅ 已完成 | | GET | `/admin/task-logs` | 任务完成日志列表 | task-logs.index | ⏳ 待实现 | | POST | `/admin/tasks/{id}/reward` | 补发/调整奖励(高权限) | tasks.reward | ⏳ 待实现 | *** ## 7. 核心代码结构 ``` app/ ├── Enums/ │ ├── PointType.php # 新增 TYPE_TASK 枚举值 │ └── Task/ │ ├── TaskType.php # 任务类型枚举(sign_in/complete_profile/...) │ ├── TaskGroupType.php # 任务组类型(NEWBIE/DAILY/WEEKLY/SIGN_IN/CUSTOM) │ ├── TaskResetType.php # 重置周期枚举(NONE/DAILY/WEEKLY/MONTHLY) │ ├── TaskRewardType.php # 奖励类型枚举(coin/point/vip_days) │ ├── TaskClaimMode.php # 领取方式枚举(AUTO/MANUAL) │ └── TaskProgressStatus.php # 进度状态枚举 ├── Models/ │ └── Task/ │ ├── TaskGroup.php # 任务组模型(type/reset_type 为 int Enum) │ ├── Task.php # 任务模型(含 activeTasks 作用域) │ ├── TaskProgress.php # 用户任务进度(status 为 Enum Cast) │ └── TaskLog.php # 完成日志 ├── Casts/ │ └── AsTaskCondition.php # 任务条件 JSON Cast,默认值 {serial_days:0,target_value:0,completeness_threshold:100} ├── Events/ │ └── User/ │ └── UserProfileUpdated.php # ✅ 用户资料更新事件(modifyProfile 后触发) ├── Listeners/ │ └── Task/ │ └── ProfileTaskListener.php # ✅ 完善资料监听器(ShouldQueue) │ # SignInTaskListener / LoginTaskListener / FollowOfficialAccountTaskListener │ # InviteTaskListener / CustomActionTaskListener ⏳ 待实现 ├── Services/ │ └── TaskService.php # 任务进度判定 + 奖励发放编排 ├── Support/ │ ├── UserHelper.php # 新增 extendVip($user, $days) 方法 │ └── Traits/ │ └── HasTaskProgress.php # 用户模型的任务关联 trait ├── Http/ │ ├── Controllers/ │ │ ├── Api/V1/ │ │ │ └── TaskController.php # ✅ 前台任务接口(index / claim) │ │ └── Admin/ │ │ ├── TaskGroupController.php # ✅ 任务分组 CRUD + options + toggle-status │ │ └── TaskController.php # ✅ 任务 CRUD + toggle-status │ │ # TaskLogController.php ⏳ 待实现 │ ├── Requests/ │ │ └── Admin/ │ │ ├── TaskGroup/ │ │ │ ├── TaskGroupIndexRequest.php │ │ │ └── TaskGroupSaveRequest.php │ │ └── Task/ │ │ ├── TaskIndexRequest.php │ │ └── TaskSaveRequest.php │ └── Resources/ │ ├── Admin/ │ │ ├── TaskGroupResource.php │ │ └── TaskResource.php │ └── Api/V1/ │ ├── TaskGroupResource.php │ └── TaskResource.php config/ └── morph_maps.php # 注册 'task' => Task::class 多态别名 database/ └── migrations/ ├── 2026_09_10_150858_create_task_tables.php # 4 张表(task_groups/tasks/task_progress/task_logs) ├── 2026_09_10_154135_seed_initial_task_data.php # 初始任务组和任务种子(迁移方式,不用 Seeder) └── 2026_10_31_111934_seed_initial_admin_data.php # 追加任务分组/任务管理菜单与按钮权限 routes/ ├── api_v1.php # 新增 /tasks 路由组 └── admin.php # 新增 task-groups 和 tasks 资源路由 tests/ ├── Feature/ │ ├── Api/V1/TaskControllerTest.php # ✅ 前台任务接口测试(5 个用例) │ └── Admin/ # ⏳ 待补充后台 CRUD 测试 └── Unit/Services/ └── TaskServiceTest.php # ✅ TaskService 单元测试(12 个用例) ``` > 注:实际表结构相比设计文档有所调整——`task_groups` 使用 int Enum(`type`/`reset_type`),不使用 `visibility`/`deleted_at`/`task_count`;`tasks` 不使用 `target_count`/`icon`/`log_count`/`deleted_at`,条件通过统一的 `condition` JSON 存储;`task_progress` 使用 `status` Enum(IN_PROGRESS/COMPLETED/CLAIMED)替代 `is_completed`+`claimed_at` 组合判断。 ### 7.1 TaskService 核心方法 | 方法 | 说明 | |------|------| | `getUserTaskGroups($user)` | 获取用户视角的任务组列表(含进度) | | `getUserProgress($user, $taskId)` | 获取用户某任务的进度 | | `incrementProgress($userId, $task, $value, $periodKey)` | 增加任务进度,返回是否新达成 | | `grantReward($userId, $task, $periodKey, $claimedBy)` | 发放奖励并写日志,幂等 | | `claimReward($userId, $taskId)` | 手动领取奖励(manual 模式用) | ### 7.2 User 模型集成 通过 `HasTaskProgress` trait 给 `User` 模型追加: ```php // 关联 $this->taskProgresses(): HasMany $this->taskLogs(): HasMany // 快捷方法 $this->hasCompletedTask($taskCode): bool $this->getTaskProgress($taskCode): ?TaskProgress ``` *** ## 8. 初始种子数据 初始数据通过 `2026_09_10_154135_seed_initial_task_data.php` 迁移写入(使用 `updateOrCreate`,幂等执行): ### 8.1 新手任务组(newbie,不重置,type=0, reset_type=0) | 任务 | 类型 | condition | 奖励 | 奖励数量 | claim_mode | |------|------|-----------|------|---------|------------| | 完善个人资料 | complete_profile | {completeness_threshold:100} | coin | 50 | auto | | 关注公众号 | follow_official_account | {target_value:1} | coin | 30 | auto | ### 8.2 签到任务组(sign_in,type=3, reset_type=1)⏳ 待补充 | 任务 | type | condition.serial_days | reward_type | reward_amount | period_key 策略 | |------|------|----------------------|-------------|---------------|----------------| | 每日签到 | sign_in | 0 | coin | 10 | `Y-m-d`,每日重置 | | 连续签到第2天~第30天阶梯 | sign_in | 2/3/7/30 | coin | 15/20/100/500 | `sign_in_day_N`,永久唯一 | ### 8.3 每日任务组(daily,type=1, reset_type=1)⏳ 待补充 | 任务 | 类型 | 奖励 | 奖励数量 | |------|------|------|---------| | 每日登录 | daily_login | point | 5 | *** ## 9. 权限与菜单 ### 9.1 新增权限 | 权限标识 | 说明 | 状态 | |---------|------|------| | task-groups.index | 查看任务组列表 | ✅ 已实现 | | task-groups.create | 创建任务组 | ✅ 已实现 | | task-groups.edit | 编辑任务组 | ✅ 已实现 | | task-groups.delete | 删除任务组 | ✅ 已实现 | | tasks.index | 查看任务列表 | ✅ 已实现 | | tasks.create | 创建任务 | ✅ 已实现 | | tasks.edit | 编辑任务 | ✅ 已实现 | | tasks.delete | 删除任务 | ✅ 已实现 | | tasks.reward | 补发/调整任务奖励(高权限) | ⏳ 待实现 | | task-logs.index | 查看任务完成日志 | ⏳ 待实现 | ### 9.2 新增菜单 已追加到 `database/migrations/2026_10_31_111934_seed_initial_admin_data.php`,挂在「运营管理」目录下,原运营菜单 sort 值已顺延: | 菜单 key | path | component | title | icon | permission | 按钮权限 | |----------|------|-----------|-------|------|------------|---------| | operation.task-group | task-group | /operation/task-group | 任务分组 | ri:folder-3-line | task-groups.index | create/edit/delete | | operation.task | task | /operation/task | 任务管理 | ri:task-line | tasks.index | create/edit/delete | | operation.task-log | task-log | /operation/task-log | 任务记录 | — | task-logs.index | ⏳ 待添加 | *** ## 10. 与现有系统的集成点 ### 10.1 金币 / 积分 / VIP * 金币奖励:调用 `CoinHelper::incr($user, $amount, $taskLog, 'task', '完成任务奖励')`,签到类用 `CoinType::TYPE_SIGN_IN`,邀请类用 `CoinType::TYPE_INVITE_REGISTER` * 积分奖励:调用 `PointHelper::incr(...)`,同上区分类型 * VIP 奖励:调用 `UserHelper::extendVip($user, $days)` * 所有奖励操作均通过已有 Helper,不直接修改 `users` 表字段 ### 10.2 事件系统 * 复用现有 `UserLoggedIn`、`UserProfileUpdated` 等事件 * 复用已有 `InviteRelationshipCreated` 事件接入邀请任务奖励 * 新增 `UserSignedIn`、`TaskActionTriggered` 等事件 * 项目使用 Laravel 11+ 自动事件发现机制,监听器放在 `app/Listeners/` 下自动注册,无需手动绑定 ### 10.3 多态映射 在 `config/morph_maps.php` 中追加: ```php 'task_group' => \App\Models\Task\TaskGroup::class, 'task' => \App\Models\Task\Task::class, 'task_log' => \App\Models\Task\TaskLog::class, ``` *** ## 11. 实施阶段 | 阶段 | 内容 | 交付物 | 状态 | |------|------|--------|------| | **Phase 1 数据模型** | 4 张表迁移 + 4 个模型 + 6 个枚举 + Cast;`HasTaskProgress` trait;`UserHelper::extendVip`;`PointType::TYPE_TASK`;morph map 注册;初始任务种子迁移 | 可查询、可配置的任务数据层 | ✅ 已完成 | | **Phase 2 任务引擎(基础)** | `TaskService` 核心逻辑(周期计算、进度更新、完整度判定、签到处理、领取奖励、发奖事务、列表查询);`UserProfileUpdated` 事件 + `ProfileTaskListener` 队列监听器;`UserController::modifyProfile` 触发事件;单元测试覆盖(12 个用例) | 完善资料类型任务奖励引擎可用 | ✅ 部分完成 | | **Phase 3 前台 API** | `GET /api/v1/tasks`(任务中心列表,按分组返回含进度);`POST /api/v1/tasks/{taskId}/claim`(手动领奖);`TaskResource` + `TaskGroupResource`;功能测试 5 个用例;Apifox Api 模块发布 | 前台用户端基础接口可用 | ✅ 已完成 | | **Phase 4 后台管理(分组+任务)** | `TaskGroupController`(7 个接口:列表/options/创建/详情/更新/状态切换/删除);`TaskController`(6 个接口:列表/创建/详情/更新/状态切换/删除);Form Request(Index+Save);Admin Resource;菜单权限迁移;Apifox Admin 模块发布 | 运营可配置任务分组和任务 | ✅ 已完成 | | **Phase 5 前端落地(分组+任务)** | Ai Forge Admin 中 `src/views/operation/task-group/`(列表+搜索+编辑弹窗)和 `src/views/operation/task/`(列表+搜索+编辑弹窗,条件字段按任务类型动态显示);API 函数 + TS 类型;`pnpm build` 通过 | 分组和任务管理 UI 可用 | ✅ 已完成 | | **Phase 2+ 剩余任务类型引擎** | 剩余 5 个事件(`UserSignedIn`/`UserLoggedIn`/`UserFollowedOfficialAccount`/`InviteRelationshipCreated`/`TaskActionTriggered`)+ 5 个监听器(SignIn/Login/Follow/Invite/CustomAction);签到特殊 period_key 策略;邀请任务累计;自定义行为通用触发;补齐单元测试 | 全部任务类型奖励引擎可用 | ⏳ 待实现 | | **Phase 3+ 前台补充接口** | `POST /api/v1/tasks/sign-in` 签到专用接口(触发签到事件并返回签到结果);签到相关的连续签到天数返回 | 前台签到功能可用 | ⏳ 待实现 | | **Phase 4+ 后台日志与补偿** | `TaskLogController`(日志分页列表/详情);补发奖励接口(`POST /admin/tasks/{id}/reward`);后台 CRUD 功能测试 | 任务审计与运营补偿能力 | ⏳ 待实现 | | **Phase 5+ 前端日志页面** | Ai Forge Admin 中 `src/views/operation/task-log/` 任务记录页面 | 后台任务记录 UI | ⏳ 待实现 | | **Phase 6 文档与收尾** | 补齐测试覆盖率至 70%+;前端开发环境联调验证;README 更新;最终 Apifox 质量检查 | 生产可用 | ⏳ 待实现 | ### 当前可用功能(截至本次实现) * 后台可创建/编辑/启用/禁用任务分组(新手/每日/每周/签到/自定义 5 种类型) * 后台可创建/编辑/启用/禁用任务(6 种类型 + 3 种奖励 + 自动/手动领取模式 + 生效时间范围) * 前台用户调用 `GET /api/v1/tasks` 可查看按分组组织的任务列表及个人进度 * 用户更新个人资料后自动触发完善资料任务判定,达到阈值(默认100%即6项全填)自动发放金币奖励 * 前台用户对 manual 领取模式的任务可调用 `POST /api/v1/tasks/{taskId}/claim` 领取奖励 * 所有写操作幂等安全(唯一索引 + 事务 + UniqueConstraintViolationException 捕获) *** ## 12. 风险与注意事项 | 风险 | 说明 | 应对策略 | |------|------|---------| | 重复发放奖励 | 并发下同一用户同一任务可能被多个请求同时触发 | 唯一索引 + 事务 + 捕获唯一键冲突返回成功 | | 事件丢失 | 事件监听器抛异常可能导致任务进度未更新 | 关键事件启用 ShouldQueue + 失败重试 + failed_jobs 监控 | | 重置周期边界 | 跨日/跨周时旧周期数据残留 | 所有查询均带 `period_key` 过滤,后台可定期归档历史数据 | | 奖励发放失败 | 金币/积分 Helper 抛异常导致部分成功 | 全部操作在同一事务内,任何一步失败整体回滚 | | 配置错误导致异常奖励 | 运营配置 reward_amount 过大 | 后台创建任务时增加最大奖励数量校验 | --- --- url: https://www.aiforge.cn/technical-solutions/wallet.md --- # 钱包技术方案 ## 1. 设计原则 * **余额热数据落在持有者主表,记录层全部多态**:余额是最高频读取的字段,直接作为 `users` 表列存储可做到查询零 Join;而流水、充值单、提现单三张记录表统一使用 `owner_type` + `owner_id` 多态关联,业务逻辑不与 `User` 耦合。 * **可扩展但不过度设计**:未来其他主体(如 `admin_users` 平台账户、未来的 `merchants` 商户)需要钱包时,只需在其表追加 `balance` / `frozen_balance` 两个字段并 `use HasWalletBalance`,记录层与 `WalletHelper` 无需改动。本期不引入独立 `wallets` 表和多币种,避免为假想需求付出复杂度。 * **增量更新而非流水汇总**:与现有 `CoinHelper`(每次 `SUM` 全表重算)不同,钱包余额采用事务内行锁 + 增量更新(`balance + amount`),流水表保存前后余额快照用于对账。真实货币场景对一致性与性能要求都更高,`SUM` 全表在数据量增长后不可接受。 * **冻结与可用余额分离**:`balance` 为可用余额,`frozen_balance` 为冻结余额,两者互不重叠。下单冻结即 `balance -= n; frozen_balance += n`,成交扣款即 `frozen_balance -= n`,取消解冻即反向操作。 * **金额精度**:统一 `decimal(16,2)`,以元为单位;PHP 侧用字符串/`decimal` cast 传递,禁止使用 `float` 参与金额运算。 * **与虚拟资产完全隔离**:钱包是真实货币账本,与 `available_points`(积分)、`available_coins`(金币)并行独立,不做迁移合并。 ## 2. 代码结构 ``` app/ ├── Enums/ │ ├── WalletTradeType.php # 钱包交易类型枚举(实现 HasLabel) │ ├── WalletTradeStatus.php # 流水状态枚举(pending/success/failed) │ ├── WithdrawStatus.php # 提现状态枚举(实现 HasLabel) │ ├── PayChannel.php # 支付渠道枚举(wechat/alipay/apple) │ └── WithdrawAccountType.php # 收款账户类型枚举(wechat/alipay/bank) ├── Exceptions/ │ ├── InsufficientBalanceException.php # 余额不足 │ └── InsufficientFrozenBalanceException.php # 冻结余额不足 ├── Models/ │ ├── Traits/ │ │ └── HasWalletBalance.php # 钱包能力 trait(多态关系 + 余额判定) │ └── Wallet/ │ ├── WalletTrade.php # 钱包流水(morphTo owner + morphTo source) │ ├── Recharge.php # 充值订单(morphTo owner) │ └── Withdraw.php # 提现申请(morphTo owner) ├── Support/ │ └── WalletHelper.php # 余额引擎(incr/decr/freeze/unfreeze) ├── Services/ │ ├── RechargeService.php # 充值下单与回调入账编排 │ └── WithdrawService.php # 提现申请与审核流转编排 ├── Events/Wallet/ │ ├── BalanceChanged.php # 余额变动事件 │ ├── RechargeSucceeded.php # 充值成功事件 │ ├── WithdrawApproved.php # 提现审核通过事件 │ ├── WithdrawRejected.php # 提现驳回事件 │ ├── WithdrawTransferred.php # 提现打款成功事件 │ └── WithdrawFailed.php # 提现打款失败事件 ├── Notifications/Wallet/ │ ├── RechargeSucceededNotification.php │ ├── WithdrawApprovedNotification.php │ ├── WithdrawRejectedNotification.php │ ├── WithdrawTransferredNotification.php │ └── WithdrawFailedNotification.php ├── Jobs/Wallet/ │ └── CloseExpiredRechargesJob.php # 关闭超时未支付订单 ├── Policies/ │ ├── RechargePolicy.php │ └── WithdrawPolicy.php ├── Exports/Admin/ │ ├── WalletTradeExport.php │ ├── WithdrawExport.php │ └── RechargeExport.php ├── Http/ │ ├── Controllers/ │ │ ├── Admin/ │ │ │ ├── WalletController.php # 流水列表/详情/统计/调账 │ │ │ ├── WithdrawController.php # 提现审核 │ │ │ └── RechargeController.php # 充值订单查看 │ │ ├── Api/V1/Wallet/ │ │ │ ├── WalletController.php # 前台钱包概览/流水/充值 │ │ │ └── WithdrawController.php # 前台提现提交/列表/详情 │ │ └── PayCallbackController.php # 支付渠道异步回调 │ ├── Requests/ │ │ ├── Admin/Wallet/ │ │ │ ├── WalletTradeIndexRequest.php │ │ │ ├── WalletAdjustRequest.php │ │ │ ├── WithdrawIndexRequest.php │ │ │ ├── WithdrawRejectRequest.php │ │ │ ├── WithdrawFailRequest.php │ │ │ └── WithdrawRemarkRequest.php │ │ └── Api/V1/Wallet/ │ │ ├── CreateRechargeRequest.php │ │ └── CreateWithdrawRequest.php │ └── Resources/ │ ├── Admin/ │ │ ├── WalletTradeResource.php │ │ ├── WalletStatResource.php │ │ ├── WithdrawResource.php │ │ └── RechargeResource.php │ └── Api/V1/ │ ├── WalletResource.php │ ├── WalletTradeResource.php │ ├── WithdrawResource.php │ └── RechargeResource.php database/ ├── migrations/ │ ├── xxxx_add_balance_to_users_table.php # users 表加 balance/frozen_balance │ ├── xxxx_create_wallet_trades_table.php │ ├── xxxx_create_recharges_table.php │ └── xxxx_create_withdraws_table.php └── factories/Wallet/ ├── WalletTradeFactory.php ├── RechargeFactory.php └── WithdrawFactory.php tests/Feature/ ├── Admin/ │ ├── WalletControllerTest.php │ ├── WithdrawAdminControllerTest.php │ └── RechargeAdminControllerTest.php ├── Api/V1/ │ ├── WalletControllerTest.php │ ├── RechargeControllerTest.php │ └── WithdrawControllerTest.php └── Wallet/ ├── WalletHelperTest.php ├── RechargeServiceTest.php └── WithdrawServiceTest.php ``` ## 3. HasWalletBalance Trait 任何主表含 `balance` / `frozen_balance` 两列的模型 `use HasWalletBalance` 即获得完整钱包能力: | 方法 | 职责 | |------|------| | `walletTrades(): MorphMany` | 钱包流水(作为 owner) | | `recharges(): MorphMany` | 充值订单(作为 owner) | | `withdraws(): MorphMany` | 提现申请(作为 owner) | | `hasBalance(float $amount): bool` | 判定可用余额是否充足 | | `totalBalance(): float` | 总资产(可用 + 冻结) | 本期仅 `User` 接入。 ## 4. WalletHelper 关键方法 | 方法 | 职责 | 余额变动 | |------|------|---------| | `incr($owner, $amount, $source, $type, $desc)` | 增加可用余额(充值到账、退款、后台充正) | `balance += n` | | `decr($owner, $amount, $source, $type, $desc)` | 扣减可用余额(直接消费、后台扣减) | `balance -= n` | | `freeze($owner, $amount, $source, $desc)` | 冻结(下单预授权、提现申请) | `balance -= n; frozen_balance += n` | | `unfreeze($owner, $amount, $source, $desc)` | 解冻(订单取消、提现驳回) | `frozen_balance -= n; balance += n` | | `settleFrozen($owner, $amount, $source, $type, $desc)` | 结算冻结(订单成交、提现打款成功) | `frozen_balance -= n` | | `getBalance($owner): float` | 读取可用余额 | — | | `getFrozenBalance($owner): float` | 读取冻结余额 | — | ### 并发安全实现要点 * 所有写方法包裹在 `DB::transaction()` 中,进入事务后先对持有者主表行执行 `lockForUpdate()` 取最新余额 * 校验余额充足性使用锁内读到的值,不使用传入模型的过期属性 * 余额更新使用增量表达式(`increment` / `decrement`)而非先读后写的赋值,避免丢失更新 * 流水记录写入 `before_balance` / `after_balance` / `before_frozen` / `after_frozen` 四个快照字段,供财务对账与差异排查 * 余额不足抛 `InsufficientBalanceException`,控制器层捕获后 `abort(422, ...)`,与现有 `InsufficientCoinsException` 处理方式一致 ## 5. 充值链路 > **当前状态**:支付渠道对接已挂起(见第 12 章「挂起待办」)。Phase 2 只实现订单生命周期与管理员手动确认到账,下方渠道相关步骤为渠道接入后的目标形态。 **Phase 2 实现的链路(管理员手动确认)** ``` POST /api/v1/user/wallet/recharges → CreateRechargeRequest 校验开关与金额区间 → RechargeService::create() → 计算赠送金额(wallet.recharge_bonus_rate) → 生成 order_no(R + YmdHis + 随机串) → 创建 Recharge(pay_status=pending, expired_at=now+30min) → 返回订单信息(暂不返回渠道支付参数) POST /admin/wallet/recharges/{recharge}/confirm → RechargeService::confirmManually() → 事务内按 order_no 加锁查单 → 幂等判定:已 success 直接返回 → 置 pay_status=success、paid_at、confirmed_by → WalletHelper::incr() 入账(amount + bonus_amount) → 派发 RechargeSucceeded 事件 → 队列发站内信 ``` **渠道接入后的目标链路(挂起)** ``` POST /api/v1/user/wallet/recharges → ... 同上 ... → 调用支付渠道预下单,返回客户端支付参数 POST /callback/pay/{channel} → PayCallbackController 完成渠道验签 → RechargeService::handleCallback() → 与 confirmManually() 复用同一入账内核(保证两条路径行为一致) ``` 超时未支付订单由 `CloseExpiredRechargesJob` 定时置为 `expired`,注册到 `routes/console.php`。 ## 6. 提现链路 ``` POST /api/v1/user/wallet/withdraws → CreateWithdrawRequest 校验开关/金额区间/当日次数/待审核单 → WithdrawService::submit() → 事务内按 owner 行锁查最新余额 → 计算手续费(wallet.withdraw_fee_rate)与实际到账金额 → WalletHelper::freeze() 冻结申请金额(source = Withdraw 本身) → 创建 Withdraw(status=pending) → 返回 201(无 data 包裹) POST /admin/wallet/withdraws/{id}/approve → WithdrawService::approve() → 校验 status=pending(非终态) → 置 status=approved、reviewed_by、reviewed_at → 派发 WithdrawApproved 事件 → 队列发站内信 POST /admin/wallet/withdraws/{id}/reject → WithdrawService::reject() → 校验 status=pending,reason 必填 → 置 status=rejected、reject_reason → WalletHelper::unfreeze() 解冻退回可用余额 → 派发 WithdrawRejected 事件 → 队列发站内信(含原因) POST /admin/wallet/withdraws/{id}/transfer → WithdrawService::markTransferred() → 校验 status=approved → 置 status=success、transfer_at → WalletHelper::settleFrozen() 扣减冻结余额(资金真正流出,WalletTradeType::WITHDRAW) → 派发 WithdrawTransferred 事件 → 队列发站内信 POST /admin/wallet/withdraws/{id}/fail → WithdrawService::markFailed() → 校验 status=approved,reason 必填 → 置 status=failed、fail_reason → WalletHelper::unfreeze() 解冻退回可用余额 → 派发 WithdrawFailed 事件 → 队列发站内信(含原因) ``` 打款失败置 `failed` 并执行 `unfreeze()` 退回余额;所有状态变更均幂等(重复调用已终态记录直接返回)。 ## 7. 权限与菜单 ### 7.1 新增权限 | 权限标识 | 说明 | |---------|------| | wallet.index | 查看钱包流水与统计 | | wallet.withdraws | 提现审核管理(列表/详情/审核/打款) | | wallet.orders | 充值订单查看 | | wallet.adjust | 后台调整余额(高风险操作) | | wallet.export | 导出钱包流水/提现记录/充值订单 | ### 7.2 新增菜单 (挂在「运营管理」目录下,通过编辑初始数据迁移追加) | 菜单 key | path | component | title | permission | |----------|------|-----------|-------|------------| | operation.wallet | wallet | /operation/wallet | 钱包流水 | wallet.index | | operation.withdraw | withdraw | /operation/withdraw | 提现审核 | wallet.withdraws | | operation.recharge | recharge | /operation/recharge | 充值订单 | wallet.orders | 按钮权限:钱包流水页挂「调账」(`wallet.adjust`)、「导出」(`wallet.export`);提现审核页挂「审核通过」「审核驳回」「标记打款」「标记失败」(均为 `wallet.withdraws`)、「导出提现申请」(`wallet.export`)。 `wallet.adjust` 属高风险操作,后续接入敏感操作二次验证时需纳入覆盖范围。 ## 8. 配置项 新增 `wallet.*` 分组系统设置(写入 settings 表种子): | 配置键 | 默认值 | 类型 | 说明 | |--------|--------|------|------| | wallet.enable_recharge | true | bool | 是否开放充值 | | wallet.enable_withdraw | true | bool | 是否开放提现 | | wallet.min_recharge_amount | 1 | int | 单笔最低充值金额(元) | | wallet.max_recharge_amount | 50000 | int | 单笔最高充值金额(元) | | wallet.recharge_bonus_rate | 0 | float | 充值赠送比例(%) | | wallet.recharge_expire_minutes | 30 | int | 充值订单超时时长(分钟) | | wallet.min_withdraw_amount | 100 | int | 单笔最低提现金额(元) | | wallet.max_withdraw_amount | 50000 | int | 单笔最高提现金额(元) | | wallet.withdraw_fee_rate | 0 | float | 提现手续费率(%) | | wallet.daily_withdraw_limit | 3 | int | 每日提现申请次数上限 | | wallet.withdraw_throttle | 5,1 | string | 提现接口限流(次数,分钟) | ## 9. 多态别名 `config/morph_maps.php` 新增: ```php 'wallet_trade' => \App\Models\Wallet\WalletTrade::class, 'recharge' => \App\Models\Wallet\Recharge::class, 'withdraw' => \App\Models\Wallet\Withdraw::class, ``` `owner_type` 直接复用已注册的 `user` / `admin` 别名,无需额外注册。 ## 10. 前端落地清单(Ai Forge Admin) | 文件 | 说明 | |------|------| | `src/api/wallet-manage.ts` | 钱包流水、提现审核、充值订单全部 API 函数 | | `src/types/api/api.d.ts` | 新增 `Api.WalletManage` 命名空间类型定义 | | `src/views/operation/wallet/index.vue` | 钱包流水列表(统计卡片 + 表格) | | `src/views/operation/wallet/modules/wallet-search.vue` | 流水搜索栏 | | `src/views/operation/wallet/modules/wallet-trade-detail-dialog.vue` | 流水详情弹窗(含前后余额快照) | | `src/views/operation/wallet/modules/wallet-adjust-dialog.vue` | 后台调账弹窗 | | `src/views/operation/withdraw/index.vue` | 提现审核列表 | | `src/views/operation/withdraw/modules/withdraw-search.vue` | 提现搜索栏 | | `src/views/operation/withdraw/modules/withdraw-stats.vue` | 提现统计卡片 | | `src/views/operation/withdraw/modules/withdraw-detail-drawer.vue` | 提现详情抽屉 | | `src/views/operation/withdraw/modules/withdraw-reject-dialog.vue` | 驳回弹窗 | | `src/views/operation/recharge/index.vue` | 充值订单列表 | | `src/views/operation/recharge/modules/recharge-search.vue` | 充值订单搜索栏 | | `src/views/operation/recharge/modules/recharge-detail-dialog.vue` | 充值订单详情弹窗 | | `src/views/user/list/modules/user-detail-dialog.vue` | 现有文件追加「钱包」Tab(余额 + 流水 + 导出) | 页面路由由后端菜单动态注册,前端无需手写路由配置。 ## 11. 实施阶段 | 阶段 | 内容 | 交付物 | 状态 | |------|------|--------|------| | **Phase 1 基础账本** | `users` 表加字段;`wallet_trades` 表;`WalletTradeType` / `WalletTradeStatus` 枚举;`HasWalletBalance` trait;`WalletHelper` 四个核心方法 + 结算方法;后台流水列表/详情/统计/调账;前台钱包概览与流水接口;PHPUnit 覆盖并发扣减与余额不足场景 | 可查询、可调账的完整账本 | ✅ **已完成**(35 用例 / 137 断言全绿) | | **Phase 2 充值订单** | `recharges` 表 + 模型 + Policy;`PayChannel` / `RechargeStatus` 枚举;`RechargeService`(下单 + 管理员手动确认到账 + 幂等 + 超时关单);超时关单 Job + 定时任务注册;后台充值订单管理(列表/详情/统计/确认/导出);前台充值下单与订单查询接口;`RechargeSucceededNotification` | 订单生命周期完整,管理员可手动确认到账 | 🔄 进行中 | | **Phase 3 提现审核** | `withdraws` 表 + 模型 + Policy;`WithdrawStatus` / `WithdrawAccountType` 枚举;`WithdrawService` 完整状态流转(submit/approve/reject/markTransferred/markFailed);冻结/解冻/结算联动(流水 source_type=withdraw);后台提现审核页面(列表/详情/统计/审核/打款/失败/导出);前台提现提交(限流 `wallet.withdraw_throttle`)+ 列表 + 详情接口;4 个事件(ShouldBroadcast + ShouldQueue)与 4 个站内信通知;导出类(阈值 2000);收款账号响应脱敏 | 提现申请到打款全流程闭环 | ✅ **已完成** 2026-09-09(42 用例全绿) | | **Phase 4 集成与验收** | 订单/优惠券等业务侧接入冻结与结算;钱包指标并入业务数据看板;Apifox 按 Admin/Api 模块规范全量发布;补齐测试至项目 70% 覆盖率要求;README 与文档同步 | 生产可用 | 未开始 | ### 前置依赖 * Phase 4 的冻结链路需支付与订单模块先落地,否则冻结无实际业务场景 * `wallet.adjust` 高风险操作建议与敏感操作二次验证协同推进 ## 12. 挂起待办 以下能力已从当前排期中挂起,待条件满足后启动。挂起不影响 Phase 2/3 交付,因为入账内核与订单状态机均已按可插拔方式设计。 ### 12.1 支付渠道对接(挂起) | 项目 | 说明 | |------|------| | 挂起原因 | 本地无商户号与支付证书,真实支付链路无法验证;强行实现会导致核心的幂等与入账逻辑缺乏可靠测试覆盖 | | 恢复条件 | 提供微信支付/支付宝商户号、API 密钥与证书,并配置到 `config/services.php` | | 待补内容 | ① `app/Payments/Contracts/PaymentGateway.php` 网关契约(`prepay` / `verifyCallback` / `queryOrder` 三个方法);② `PaymentManager` 渠道路由;③ `WechatPayGateway`、`AlipayGateway` 实现类;④ `PayCallbackController` 统一回调入口(`POST /callback/pay/{channel}`,豁免认证与 CSRF);⑤ `RechargeService::handleCallback()` 验签入账;⑥ `queryAndSync()` 主动查单补偿 | | 设计约束 | 渠道接入时**不得修改** `RechargeService` 的入账内核与 `Recharge` 状态机,只允许新增网关实现类并在 `PaymentManager` 注册;回调入账必须复用 Phase 2 已验证的幂等逻辑 | | 数据准备 | `recharges` 表在 Phase 2 已预留 `pay_channel`、`transaction_id`(唯一索引防重复入账)、`callback_payload` 三个字段,渠道接入无需再改表结构 | ### 12.2 充值套餐配置(挂起) | 项目 | 说明 | |------|------| | 挂起原因 | 属运营增强功能,不阻塞资金链路 | | 待补内容 | `recharge_plans` 表(面额、赠送金额、排序、启用状态)+ 后台 CRUD + 前台套餐列表接口 | | 现状替代 | Phase 2 仅支持自定义金额,按 `wallet.min_recharge_amount` / `wallet.max_recharge_amount` 校验区间 | *** > 文档来源:PRD 第 8 章 --- --- url: https://www.aiforge.cn/technical-solutions/report-management.md --- # 举报管理技术方案 ## 1. 设计原则 * **多态被举报对象**:举报记录通过 `reportable_type` + `reportable_id` 多态关联被举报对象,当前接入 `Comment` 与 `User`,未来可扩展任意业务实体(动态、作品等),举报记录层无需改动。 * **可被举报对象 trait 化**:`App\Models\Traits\HasReportable` 提供 `reports()` 反向关联 + `pendingReportsCount()` 辅助方法。需要被举报的模型仅需 `use HasReportable`。 * **字典化管理举报分类**:通过 `report_category` 字典统一管理 9 项分类,可由运营在后台字典管理界面动态调整而无需发版。 * **手动联动处置**:管理员处理举报时手动勾选要下发的动作(`hide_comment` / `delete_comment` / `freeze_user`),由 `ReportService::process()` 在同一事务中执行;失败回滚保证一致性。 * **冻结为永久冻结**:仅设置 `UserStatus::FROZEN`,管理员通过「用户管理 → 状态切换」人工解冻;动作协议不含时长字段。 * **状态机防并发**:首次处理时由 `pending` 自动流转为 `processing`,避免多人重复操作;已结案(resolved/rejected)的举报不可再次处理。 * **防刷(业务层)**:同一 `(user_id, reportable_type, reportable_id)` 在 `pending`/`processing` 状态下不可重复提交,由 `ReportService::submit()` 校验。 ## 2. 代码结构 ``` app/ ├── Enums/ │ └── ReportStatus.php # 举报处理状态枚举(pending/processing/resolved/rejected) ├── Models/Content/ │ └── Report.php # 举报模型(多态 MorphTo + belongsTo User/Admin) ├── Models/Traits/ │ └── HasReportable.php # 可被举报对象 trait(morphMany + pendingReportsCount) ├── Models/User.php # 引入 HasReportable(reports() morphMany = 该用户被举报的记录;submit() 手动 new Report 不依赖关系) ├── Services/ │ └── ReportService.php # 举报业务服务(submit/process/listQuery/stats) ├── Http/Controllers/ │ ├── Api/V1/ │ │ └── ReportController.php # 用户端举报接口(store/index) │ └── Admin/ │ └── ReportController.php # 管理端举报接口(index/show/update/stats/export) ├── Http/Requests/ │ ├── Api/V1/Report/ │ │ └── StoreReportRequest.php # 用户端提交举报请求校验(含 morph alias + exists 校验) │ └── Admin/Report/ │ └── ReportProcessRequest.php # 管理端处理举报请求校验(含 actions 内联 JSON Schema) ├── Http/Resources/ │ ├── Api/V1/ │ │ └── ReportResource.php # 用户端举报资源(自动 morph alias 反查) │ └── Admin/ │ ├── ReportResource.php # 管理端举报资源(含 reportable_snapshot 多态快照) │ └── ReportStatResource.php # 管理端举报统计资源 ├── Exports/Admin/ │ └── ReportListExport.php # 列表导出实现(AbstractExport,同步/异步双通道) ├── Policies/ │ └── ReportPolicy.php # 举报策略 └── database/migrations/ └── 2026_09_30_000002_create_reports_table.php # 主表迁移(同时注入举报分类字典) ``` ## 3. 数据模型 ### 3.1 reports 主表 | 字段名 | 类型 | 说明 | |--------|------|------| | id | BIGINT | 主键 | | user_id | BIGINT | 举报人用户 ID | | reportable_id | BIGINT | 被举报对象 ID | | reportable_type | VARCHAR(255) | 被举报对象多态类型(morph alias:`comment` / `user`,由 `Relation::enforceMorphMap()` 全局别名统一管理,不存储全限定类名) | | category | VARCHAR(32) | 举报分类(取自字典 `report_category`) | | reason | VARCHAR(500) | 举报描述(可空) | | evidence | JSON | 证据图片 URL 数组(可空,最多 9 张) | | status | VARCHAR(16) | 处理状态(pending/processing/resolved/rejected) | | handler_id | BIGINT | 处理人管理员 ID(可空) | | process_remark | VARCHAR(500) | 处理备注(可空) | | process_actions | JSON | 处置动作记录(可空,格式见下) | | processed_at | TIMESTAMP | 处理时间(可空) | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 更新时间(列存在,但模型声明 `UPDATED_AT = null` 不自动维护,值恒为 NULL) | 索引: * `reportable_type + reportable_id` 复合索引(多态查询) * `status + category + created_at` 复合索引(筛选) * `handler_id + processed_at` 复合索引(按处理人查询) * `user_id + status` 复合索引(用户自己的举报列表) ### 3.2 process_actions JSON 协议 ```json [ { "type": "hide_comment", "reason": null }, { "type": "freeze_user", "reason": null } ] ``` | 字段 | 类型 | 说明 | |------|------|------| | type | string | 动作类型:`hide_comment` / `delete_comment` / `freeze_user` | | reason | string | null | 动作说明(可空) | ### 3.3 字典 report_category 固定 9 项(迁移自动注入,可由后台字典管理界面调整): | value | label | label_type | |-------|-------|------------| | pornography | 色情低俗 | danger | | violence | 暴力血腥 | warning | | political | 政治敏感 | danger | | ad | 广告营销 | info | | insult | 辱骂人身攻击 | warning | | fake | 虚假信息 | warning | | infringement | 侵权抄袭 | info | | illegal | 违法违规 | danger | | other | 其他 | default | ## 4. 状态机 ``` [新举报] → pending ──(管理员首次处理)──→ processing ──(status=resolved)──→ resolved │ └────────(status=rejected)───────→ rejected ``` * `pending → processing` 在 `ReportController::update()` 入口自动推进(避免多人重复操作) * `status` 仅允许写入 `resolved` / `rejected`(controller 白名单 + `ReportStatus::tryFrom` 双重校验) * 状态机在 `ReportStatus` 枚举上提供 `isClosed()` / `isResolved()` / `isRejected()` 辅助判断 * 已结案(resolved / rejected)的举报再次处理返回 422 ## 5. 联动处置动作 ### 5.1 hide_comment 将被举报评论的 `status` 改为 `ReviewStatus::REJECTED`,触发「评论模块」的「已驳回」展示逻辑。 ### 5.2 delete_comment 删除被举报评论,`CommentObserver::deleted` 事件递归删除该评论的所有回复。 ### 5.3 freeze_user 将被举报对象的作者(被举报用户 / 被举报评论的作者)`status` 改为 `UserStatus::FROZEN`(永久冻结)。 * 不设置 `frozen_until`(用户表未存储冻结截止时间) * 解冻路径:`PUT /admin/users/{user}/toggle-status`(管理员人工切换) ## 6. 接口约定 ### 6.1 用户端 (`/api/v1/user/reports`) * `POST /reports`:提交举报(`target_type` ∈ `comment`/`user`,`category` 字典项,`reason` ≤500 字,`evidence` ≤9 个 URL),需 Sanctum 登录;返回 201 + 完整 ReportResource(`target_type` 回显 morph alias、`target_id` 回显对象 ID) * `GET /reports/my`:当前登录用户的举报列表(按提交时间倒序),分页 提交时 `target_type` 仅接受 morph alias(`comment` / `user`),避免暴露内部类名。`StoreReportRequest` 在 `prepareForValidation()` 阶段通过 `Relation::getMorphedModel()` 解析 alias → 全限定类名,并在 `rules()` 中追加 `exists:,id` 校验目标存在。 ### 6.2 管理端 (`/admin/reports`) | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | GET | `/admin/reports` | reports.index | 列表(分页,支持 7 维度筛选) | | GET | `/admin/reports/stats` | reports.index | 统计(total/pending/today/closed) | | GET | `/admin/reports/{report}` | reports.index | 详情(含 reportable_snapshot 多态快照) | | PUT | `/admin/reports/{report}` | reports.edit | 处理(status=resolved/rejected + 联动处置 actions) | | GET | `/admin/reports/export` | reports.export | 导出(同步/异步双通道,阈值 1000 行) | 详情接口的 `reportable_snapshot` 字段按对象类型返回不同结构: ```json // comment 快照 { "type": "comment", "id": 100, "user_id": 200, "username": "违规作者", "content": "违规内容...", "status": "rejected", "status_label": "已驳回", "created_at": "2026-09-30 14:00:00" } // user 快照 { "type": "user", "id": 200, "username": "spammer", "name": "违规作者", "avatar": "https://...", "status": 0, "status_label": "已冻结", "registered_at": "2026-01-01 00:00:00" } ``` 当被举报对象已被删除时,`reportable_snapshot` 返回 `null`,详情接口其余字段仍正常返回。 ## 7. 防并发与幂等 * **同人多态目标去重**:`ReportService::submit()` 在事务前查询同 `(user_id, reportable_type, reportable_id)` 是否存在 `pending`/`processing` 记录,存在则 abort(422) * **管理员并发处理**:`pending → processing` 在处理入口自动推进(见 §4),标记"处理中"减少重复介入;`processing` 状态仍允许其他管理员提交最终结论,处置动作仅在 `status=resolved` 时执行(`rejected` 不下发动作);已结案(resolved/rejected)记录在入口即被 `isClosed()` 拦截返回 422 * **状态机白名单**:controller 拒绝任何非 `resolved`/`rejected` 的状态写入 * **联动处置事务包裹**:`ReportService::process()` 在 `DB::transaction()` 中执行,写入与处置动作同进退 ## 8. 测试覆盖 | 测试文件 | 覆盖场景 | |---------|---------| | `tests/Feature/Api/V1/ReportControllerTest.php` | 用户端:游客 401、登录提交成功、举报评论/用户、目标不存在 422、非法 target_type/category 422、重复举报 422、已驳回可重新举报、我的列表隔离、evidence 边界 | | `tests/Feature/Admin/ReportControllerIndexTest.php` | 管理端:列表分页与字段、详情快照(comment/user/已删除)、按 status/category/reportable_type/keyword/日期 筛选、统计四字段 | | `tests/Feature/Admin/ReportControllerProcessTest.php` | 管理端:状态流转、handler_id/时间记录、已结案 422、联动处置 hide/delete/freeze、rejected 不执行动作、多动作并发、非法 action/status 422、process_actions 持久化 | --- --- url: https://www.aiforge.cn/operations/non-functional-requirements.md --- # 非功能性需求 ## 1. 性能要求 | 指标 | 要求 | |------|------| | API 响应时间 | < 200ms(95% 请求) | | 并发用户数 | 1000+(Octane 模式可进一步提升) | | 数据库查询优化 | 预加载防止 N+1 查询 | | 缓存策略 | 热点数据使用 Redis 缓存 | | 队列异步处理 | 耗时操作(统计、AI 工具等)走队列 | ## 2. 安全要求 | 安全项 | 实现方式 | |--------|---------| | 密码加密 | 使用 Bcrypt 加密(rounds = 12) | | API 认证 | 使用 Sanctum Token 认证 | | 权限验证 | 敏感接口使用权限验证(permission/role 中间件) | | SQL 注入防护 | 使用 Eloquent ORM / 查询构建器 | | XSS 防护 | API Resource 输出转义 | | CSRF 防护 | Web 接口启用,`/admin/*` 和 `/api/*` 豁免 | | 请求频率限制 | 登录、验证码等接口设置 throttle | | 操作审计 | 后台写操作自动记录审计日志 | | CSP 内容安全策略 | 配置内容安全策略响应头 | | Telescope 保护 | 生产环境默认禁用 | ## 3. 可扩展性 * **模块化设计**:功能独立封装 * **微服务友好**:API 化设计,易于拆分 * **支持水平扩展**:无状态设计(Token 认证) * **队列系统**:异步任务处理 * **AI Tool 可插拔**:新工具只需实现 Tool 契约即可接入 ## 4. 可维护性 | 规范项 | 要求 | |--------|------| | 代码规范 | 遵循 PSR-12 标准,Laravel Pint 自动格式化 | | 类型安全 | PHP 8.4 严格类型(`declare(strict_types=1)`),所有方法参数和返回值类型声明 | | 注释完整 | 关键逻辑使用 PHPDoc,复杂处加内联注释 | | 测试覆盖 | 单元测试 + 功能测试,PHPUnit 12 属性语法,覆盖率 >= 70% | | 枚举统一 | 使用 PHP 8 Enum + HasLabel trait | ## 5. 可靠性 * **错误处理**:统一的异常处理机制 * **日志记录**:完整的日志记录(Monolog 多通道) * **数据备份**:支持数据库备份 * **故障恢复**:队列失败重试、调度任务自动记录失败状态 *** > 文档来源:PRD 第 9 章 --- --- url: https://www.aiforge.cn/operations/development-guide.md --- # 开发流程指南 ## 1. 环境要求 * PHP >= 8.4 * PHP 扩展:`pdo`、`mbstring`、`openssl`、`redis`、`bcmath`、`ctype`、`json`、`fileinfo`、`tokenizer`、`xml` * Composer >= 2.x * MySQL 8.0+ / PostgreSQL / SQLite * Redis 6.0+ * Node.js >= 20.x(前端资源构建) * 推荐使用 [Laravel Herd](https://herd.laravel.com/) 作为本地开发环境 ## 2. 快速开始 ```bash # 一键安装(推荐) composer run setup # 启动开发环境 composer run dev ``` 手动步骤: ```bash composer install cp .env.example .env php artisan key:generate # 配置 .env 数据库连接后 php artisan migrate php artisan storage:link npm install npm run build ``` 本地访问地址:`http://laravel-skel-forge.test`(Herd 自动提供)。 ## 3. 开发命令 | 命令 | 说明 | |------|------| | `composer run setup` | 一键安装(依赖、密钥、迁移、存储链接、前端构建) | | `composer run dev` | 启动开发环境(server/queue/pail/vite) | | `composer run test` | 运行全部测试 | | `composer run check-style` | 检查代码风格(Pint) | | `composer run fix-style` | 修复代码风格(Pint) | | `php artisan route:list` | 查看路由列表 | | `php artisan route:list --path=admin` | 查看后台路由 | | `php artisan horizon` | 启动队列服务(生产用 Supervisor 守护) | | `php artisan pail` | 查看实时日志 | | `php artisan schedule:work` | 本地运行调度器 | ## 4. 代码规范 * 遵循 PSR-12 编码标准 * 使用 PHP 8.4 特性:构造器属性提升、命名参数、枚举、match 表达式 * 所有 PHP 文件强制 `declare(strict_types=1)` 和文件头 License 注释 * 所有方法添加参数类型声明和返回类型声明 * 使用 PHPUnit 12 新属性语法(`#[Test]`、`#[CoversClass]`、`#[Group]`、`#[TestDox]`) * 修改 PHP 文件后必须运行 `vendor/bin/pint --dirty --format agent` * 使用 `php artisan make:` 命令创建文件,传递 `--no-interaction` 参数 * AI Agent 使用 `php artisan make:agent` / `php artisan make:tool`(自定义 stub 在 stubs/) ## 5. 全局辅助函数(bootstrap/helpers.php) | 函数 | 说明 | |------|------| | `cpu_count(): int` | 获取 CPU 核心数 | | `per_page(Request $request, int $limit = 15): int` | 获取分页大小(限制 1~100) | | `mobile_replace(?string $value): string` | 手机号脱敏 | | `settings(string $key, $default): mixed` | 快捷读取系统设置 | | `validation_exception($field, $message): void` | 快速抛出表单验证异常 | *** > 文档来源:PRD 第 10 章 --- --- url: https://www.aiforge.cn/operations/deployment.md --- # 部署方案 ## 1. 生产环境配置 ```bash APP_ENV=production APP_DEBUG=false APP_URL=https://api.your-domain.com DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=your_database DB_USERNAME=your_username DB_PASSWORD=your_password CACHE_STORE=redis QUEUE_CONNECTION=redis SESSION_DRIVER=redis REDIS_CLIENT=phpredis BROADCAST_CONNECTION=reverb FILESYSTEM_DISK=s3 ``` ### 优化命令 ```bash php artisan config:cache php artisan route:cache php artisan view:cache php artisan event:cache php artisan storage:link ``` ## 2. 部署方式 | 方式 | 说明 | |------|------| | Octane + Swoole/RoadRunner | 高性能常驻内存模式 | | Nginx + PHP-FPM | 传统部署方式 | 可通过 [Laravel Cloud](https://cloud.laravel.com/) 快速部署。 ## 3. 服务配置 | 服务 | 建议配置 | |------|----------| | Horizon | Supervisor 守护,`php artisan horizon` | | Reverb | 独立进程或 Supervisor 守护,`php artisan reverb:start` | | Scheduler | Cron 每分钟执行 `php artisan schedule:run` | | Pulse | 访问控制保护(Gate 鉴权) | | Telescope | 生产环境禁用或仅限管理员访问 | | Meilisearch | 可选,知识库向量检索引擎(`KNOWLEDGE_ENGINE=meilisearch` 时启用;配置 `MEILISEARCH_HOST/KEY`,切换后执行一次 `php artisan knowledge:sync-index` 迁移存量分块) | ## 4. 备份策略 * 数据库每日备份 * 重要配置备份 * 上传文件备份(如使用 S3 可开启版本控制) * 备份保留 30 天 *** > 文档来源:PRD 第 11 章 --- --- url: https://www.aiforge.cn/operations/monitoring.md --- # 监控与维护 ## 1. 监控工具 | 工具 | 用途 | 访问路径 | |------|------|----------| | Laravel Pulse | 应用性能监控 | `/pulse` | | Laravel Horizon | 队列监控 | `/horizon` | | Laravel Telescope | 调试辅助 | `/telescope`(local 环境) | | Laravel Reverb | WebSocket 统计 | `/api/laravel-reverb` 或后台 Monitor | | Laravel Pail | 实时日志 | 命令行 `php artisan pail` | ### 后台监控面板功能 | 监控项 | 说明 | |--------|------| | 服务器性能 | 读取 Laravel Pulse 数据(CPU、内存、请求量) | | 队列监控 | 队列负载、慢任务统计 | | Reverb 监控 | WebSocket 连接数、消息吞吐 | | 缓存监控 | 缓存命中率统计 | | 异常监控 | 应用异常聚合统计 | | 慢查询/慢请求/慢任务 | 数据库慢查询、慢 HTTP 请求、慢队列任务 | | 外部请求监控 | 慢外部 HTTP 请求 | ## 2. 日常维护 | 维护项 | 命令/方式 | 频率 | |--------|----------|------| | 清理过期密码重置令牌 | `php artisan auth:clear-resets` | 每日(通过调度任务) | | 清理 Telescope 记录 | `php artisan telescope:prune --hours=48` | 每日(通过调度任务) | | 清理审计日志和调度日志 | `model:prune` | 每日(通过调度任务,按配置保留天数) | | 数据库优化 | 索引维护、慢查询优化 | 定期 | | 依赖安全更新检查 | `composer audit` | 定期 | | Horizon 快照记录 | `php artisan horizon:snapshot` | 每 5 分钟(调度任务) | *** > 文档来源:PRD 第 12 章 --- --- url: https://www.aiforge.cn/operations/scheduled-tasks.md --- # 定时任务 在 `routes/console.php` 中定义定时任务,执行情况自动记录到 `schedule_logs` 表,可在后台"监控管理-调度日志"查看。 ## 任务清单 | 任务 | 频率 | 说明 | |------|------|------| | `auth:clear-resets` | 每日 01:00 | 清理过期密码重置令牌 | | `model:prune` | 每日 01:05 | 清理已标记删除的模型(含调度日志过期清理) | | `stats:user` | 每日 01:10 | 统计昨日用户数据 | | `telescope:prune --hours=48 --keep-exceptions` | 每日 01:15 | 清理 Telescope 记录 | | `horizon:snapshot` | 每 5 分钟 | 记录应用状态快照 | | `CloseExpiredRechargesJob` | 每 5 分钟 | 关闭超时未支付的充值订单(钱包 Phase 2 交付) | | `segments:sync-rules` | 每日 02:00 | 同步所有启用的规则分群(增量 diff,仅影响规则打标的成员,不误删手动打标) | ## 调度日志 调度任务执行情况自动记录到 `schedule_logs` 表: | 字段 | 说明 | |------|------| | task_name | 任务名称 | | task_type | 任务类型(command/callback/exec) | | expression | Cron 表达式 | | status | 执行状态(running/success/failed/skipped) | | exit_code | 退出码 | | duration_ms | 执行耗时(毫秒) | | exception | 异常信息摘要 | | started_at | 开始时间 | | finished_at | 结束时间 | 支持按 `schedule.log_prunable_days` 配置自动清理过期日志。 *** > 文档来源:PRD 第 13 章 --- --- url: https://www.aiforge.cn/roadmap/roadmap.md --- # Roadmap 路线图 > 优先级定义: > **P0** = 上线前必备(缺失将阻塞生产发布);**P1** = 强烈建议(影响运营闭环); > **P2** = 典型业务扩展(按业务形态选做);**P3** = 体验与性能优化。 ## 1. P0 上线前必备 > 状态:P0 共 6 项,已全部完成。 | 需求 | 状态 | 说明 | |------|------|------| | Banner / 轮播图管理 | ✅ 已完成 | 后端 7 个管理端点 + 1 个前台公开端点,前端完整页面,Apifox 全量发布,PHPUnit 38 个用例验证 | | 管理员登录日志 | ✅ 已完成 | 管理员与前台用户复用多态表 `login_histories`,支持多视角查询和全局跨主体检索 | | 列表数据导出 | ✅ 已完成 | 同步/异步双通道,9 个导出端点(用户/用户金币流水/用户积分流水/钱包流水/充值订单/提现申请/审计日志/评论/反馈),前端 `useExport` Hook 统一处理,中文文件名 RFC 5987 解析 | | 系统公告管理 | ✅ 已完成 | 公告 CRUD + 三态流转(草稿/已发布/归档)+ 前台公开列表/详情接口 + 后台完整管理页面(富文本编辑器 + 封面图上传) | | 敏感词过滤 | ✅ 已完成 | 本地 DFA 算法(Trie 字典树 + 贪婪最长匹配 + 干扰字符跳过),三级命中处理(REPLACE 自动替换/REVIEW 人工审核/BLOCK 直接拦截),缓存至 Cache 层;评论/反馈/用户资料已接入 SensitiveTextRule;后台敏感词管理 CRUD + 批量导入 + 手动刷新字典页面;预留 `ContentModerator` 接口支持云审核扩展;18 个 PHPUnit 用例全通过,Apifox 已发布 | | 安全基线加固 | ✅ 已完成 | 登录限流、短信/邮件验证码限流、验证码路由层 IP 防刷限流、CORS 白名单收敛、CSRF、审计日志、代理信任、连续失败账户锁定机制均已就绪;安全响应头(X-Frame-Options/HSTS/CSP 等)由前端 nginx 反向代理层提供 | ## 2. P1 强烈建议 > 状态:P1 共 7 项,已完成 4 项、部分完成 1 项、未开始 2 项。 | 需求 | 状态 | 说明 | |------|------|------| | 用户标签与分群 | ✅ 已完成 | 手动/规则两种分群,11 种操作符 + 7 字段规则评估器,异步化批量打标,85 个 PHPUnit 用例全绿 | | 业务通知联动 | ✅ 已完成 | 数据库通知 + 微信模板/订阅消息(3 个渠道)+ Reverb 实时事件广播,9 种业务通知覆盖充值/提现/分群/导出等场景,两端(管理员/用户)通知列表与已读管理完备 | | 业务数据看板 | 🔶 部分完成 | 基础版已上线:用户总数/新增/活跃卡片、增长趋势、登录趋势、财务卡片(充值/提现/净收入)与资金流向趋势、最近注册/登录记录,UserStat 日统计表 + 定时命令预聚合;待补内容维度、审核时效等运营指标 | | 业务定时任务管理 | ✅ 已完成 | 7+ 静态定时任务(密码重置清理、模型清理、用户统计、分群同步、Telescope清理、Horizon快照、充值超时关单)+ ScheduleLog 调度日志(记录/查看/自动清理)+ 数据库驱动的动态定时任务管理(CRUD / 启停 / 手动执行 / 可用命令列表 / Cron 表达式 / 环境限定 / 高级选项)+ AppServiceProvider 动态注册到 Laravel 调度器 | | 内容 / 帖子管理 | ⏳ 未开始 | 评论/点赞/收藏的多态基础设施(HasCommentable/HasLikeable/HasCollectionable)已就绪,帖子/文章主体模型与 CRUD 未创建 | | 举报管理 | ✅ 已完成 | 评论/用户多态举报(morphMap 可扩展),9 类分类字典管理,后台列表 7 维度筛选 + 详情快照 + 统计 + 导出,联动处置(hide_comment/delete_comment/freeze_user 永久冻结),用户端提交/我的举报接口,防刷去重 + 状态机防并发,Apifox 已发布 | | 敏感操作二次验证 | ⏳ 未开始 | 高风险操作防误操作与防越权(删除用户、余额调整、权限变更、提现审核等) | ## 3. P2 业务扩展(按业务形态选做) > 状态:P2 共 9 项,已完成 1 项、部分完成 2 项、未开始 6 项。按"支付 > 营销 > 客服 > 前台 > 其他"排序。 | # | 需求 | 状态 | 说明 | 依赖 | |---|------|------|------|------| | 1 | 用户钱包 | ✅ 已完成 | 真实货币余额账本已完成:余额/冻结余额、多态流水(WalletTrade)、充值下单/手动确认/超时关单、提现申请/审核/打款/驳回,前台用户端 + 后台管理端全套接口,定时关单 Job 已上线。完整技术方案见[钱包技术方案](../technical-solutions/wallet.md) | — | | 2 | 支付与订单 | ⏳ 未开始 | 支付渠道对接(微信/支付宝/苹果内购)、回调处理、退款、对账 | 用户钱包(已完成)、业务定时任务(订单超时关闭,部分完成) | | 3 | 优惠券 / 活动管理 | ⏳ 未开始 | 优惠券模板、领取记录、核销、活动页配置 | 业务通知(核销成功通知,已完成) | | 4 | 签到与任务体系 | 🔶 部分完成 | 任务系统骨架已上线:任务组/任务/进度/日志 4 表数据层(6 种任务类型 + 3 种奖励 + 自动/手动领取),前台任务中心 API(列表/手动领取),后台任务分组、任务管理、任务日志全套管理端点,前端 task-group/task/task-log 三个页面,完善资料任务引擎(事件驱动自动发奖)已生效;待补签到/每日登录/关注公众号/邀请好友/自定义行为 5 类任务引擎、签到接口、补发奖励接口。完整技术方案见[用户任务系统技术方案](../technical-solutions/user-tasks.md) | 业务通知(任务完成通知,已完成) | | 5 | 邀请与分销 | 🔶 部分完成 | 邀请系统已全量落地:邀请码独立建模(invite_codes,多态归属,ULID 自动生成)、邀请关系(invite_records,注册时事件队列异步建立)、前台查询 API(records/stats)、后台邀请记录管理(列表/详情 + 多维度筛选)、Apifox 全量发布;待补邀请奖励发放、邀请关系树、分销结算、分群拉新统计联动。完整技术方案见[邀请系统技术方案](../technical-solutions/invitation.md) | 用户钱包(佣金入账,已完成) | | 6 | 工单 / 客服系统 | ⏳ 未开始 | 工单流转、分配、SLA 计时、多轮会话 | 无强依赖 | | 7 | 前台用户端 | ⏳ 未开始 | H5 / 小程序 / App 用户端(当前仅有 API 接口层) | 国际化(如果海外) | | 8 | 国际化落地 | ⏳ 未开始 | 海外市场扩张,前端 vue-i18n 已安装但未落地语言包 | — | | 9 | 地区数据校验 | ⏳ 未开始 | 核对地区表是否符合最新国家标准,与收货地址接口打通联动 | 仅在选做"商品/物流"时启动 | ## 4. P3 体验与性能优化 > 状态:P3 共 7 项,部分完成。 | # | 需求 | 状态 | 说明 | |---|------|------|------| | 1 | 批量操作 | 🔶 部分完成 | 附件批量删除、分群批量关联/移除、配置批量更新、Banner 批量排序已实现;待补用户/评论/审核/公告/反馈/充值/提现等模块的批量审核、批量删除、批量处理 | | 2 | 暗黑模式完整度 | ⏳ 未开始 | 抽屉、详情弹窗、图表等场景暗色适配 | | 3 | 前端错误边界 | ⏳ 未开始 | 组件级错误捕获与降级渲染 | | 4 | 大列表性能 | ⏳ 未开始 | 审计日志、Telescope 条目等虚拟滚动 / 图片懒加载 | | 5 | 单元测试覆盖 | 🔄 进行中 | 持续补齐至 70%+ 覆盖率(当前 1580 tests / 4013 assertions 全部通过,敏感词 DFA/SensitiveTextRule 18 个用例均已覆盖) | | 6 | API 文档同步 | 🔄 进行中 | 新增接口同步发布至 Apifox,保持文档与代码一致 | | 7 | 性能基准测试 | ⏳ 未开始 | 建立关键接口基准测试与压测报告 | ## 5. 建议实施顺序 > 每个阶段开始前必须确认上一阶段所有 ✅ 项已上线稳定。 ### 第一阶段(P0 必达 · 上线前) 1. \~~Banner / 轮播图管理~~ ✅ 2026-08-27 2. \~~管理员登录日志~~ ✅ 2026-08-27 3. \~~列表数据导出~~ ✅ 2026-08-27 4. \~~系统公告管理~~ ✅ 2026-09-14 5. \~~敏感词过滤~~ ✅ 2026-09-14 6. \~~安全基线加固~~ ✅ 2026-10-08(登录/验证码限流、IP 防刷、CORS 收敛、账户锁定等;安全响应头由 nginx 代理提供) ### 第二阶段(P1 业务闭环) 7. \~~用户标签与分群~~ ✅ 2026-08-28 8. \~~业务通知联动~~ ✅ 2026-09-10 9. 业务数据看板(基础版已上线,财务维度已补,待补内容维度/审核时效) 10. \~~业务定时任务管理~~ ✅ 2026-10-08(调度日志 + 动态定时任务管理 CRUD / 启停 / 手动执行) 11. 内容 / 帖子管理 12. \~~举报管理~~ ✅ 2026-09-30 13. 敏感操作二次验证 ### 第三阶段(P2 业务扩展 · 按需选做) 14. \~~用户钱包~~ ✅ 2026-09-10(钱包/充值/提现全链路已上线) 15. 邀请与分销(邀请系统已上线 ✅ 2026-09-10:邀请码/邀请关系/前后台查询/Apifox 发布,待补奖励结算与分销) 16. 支付与订单 17. 优惠券 / 活动管理 18. 签到与任务体系(任务系统骨架已上线 ✅ 2026-09-11:任务中心/后台配置/任务日志,待补签到等 5 类任务引擎) 19. 工单 / 客服系统 20. 前台用户端 21. 国际化落地 22. 地区数据校验 ### 第四阶段(P3 体验与性能 · 长期迭代) 23. 批量操作、暗黑模式完整度、前端错误边界、大列表性能、单元测试覆盖、API 文档同步、性能基准测试(每月迭代 1-2 项) ## 6. P 级别优先级总览 | P 级别 | 总项 | 已完成 | 进行中/部分完成 | 未开始 | 下一行动 | |--------|------|--------|----------------|--------|----------| | **P0** | 6 | 6 ✅ | 0 | 0 | ✅ 已全部完成,进入 P1 阶段 | | **P1** | 7 | 4 ✅ | 1 🔶(数据看板) | 2(内容管理 / 二次验证) | 补全数据看板内容维度 → 内容/帖子管理 | | **P2** | 9 | 1 ✅ | 2 🔶(邀请系统 / 任务系统骨架) | 6 | P0/P1 收口后按需启动支付渠道对接 | | **P3** | 7 | 0 | 3(批量操作/测试覆盖/API文档) | 4 | 持续推进,与每次新功能同步发布 | | **合计** | 29 | 11 | 6 | 12 | — | ## 7. 技术演进方向(长期) * \[ ] 更多认证方式(OAuth2 Server、Socialite 多平台社交登录) * \[ ] GraphQL 支持(与现有 RESTful 并行) * \[ ] AI 工具扩展(更多业务场景的 Function Calling) * \[ ] 微服务架构支持(服务拆分与服务间通信规范) * \[ ] 多云部署支持 * \[ ] 可视化代码生成工具(CRUD 模块一键生成前后端代码) *** > 文档来源:PRD 第 14 章 > 最后更新:2026-10-08(定时任务管理已完成) --- --- url: https://www.aiforge.cn/roadmap/appendix.md --- # 附录 ## 1. 默认账号 * 超级管理员:用户名 `admin`,密码 `12345678`(由迁移 `2026_07_31_111934_seed_initial_admin_data.php` 创建) ## 2. 相关文档 | 文档 | 链接/说明 | |------|----------| | Laravel 官方文档 | https://laravel.com/docs | | 官方站点 | https://www.aiforge.cn | | 演示后台 | https://admin-demo.aiforge.cn | | 后台接口文档(Apifox) | https://s.apifox.cn/28f8b0b7-abda-470f-8013-4e1b924c47f0 | | 前台接口文档(Apifox) | https://s.apifox.cn/beffd1da-1021-434c-8f13-8cbf50a3f406 | | 项目首页 | [首页](/) | | 前端项目 | Ai Forge Admin(Vue 3 SPA 管理后台) | ## 3. 文档索引 | 分类 | 文档 | |------|------| | 项目概述 | [项目概述与产品定位](../overview/project-overview.md) | | 功能需求 | [用户系统](../features/user-system.md)、[认证与授权](../features/auth-and-permission.md)、[后台管理系统](../features/admin-system.md)、[AI 助手](../features/ai-assistant.md)、[核心服务](../features/core-services.md) | | 技术架构 | [技术栈](../architecture/tech-stack.md)、[项目结构](../architecture/project-structure.md)、[架构设计](../architecture/architecture-design.md) | | 数据模型 | [核心数据表](../database/core-tables.md)、[业务数据表](../database/business-tables.md) | | API 接口 | [响应格式与状态码](../api/response-format.md)、[后台管理接口](../api/admin-api.md)、[前台 API v1](../api/api-v1.md) | | 技术方案 | [附件管理](../technical-solutions/attachment-management.md)、[实名认证](../technical-solutions/identity-verification.md)、[邀请](../technical-solutions/invitation.md)、[访问记录](../technical-solutions/visit-tracking.md)、[用户任务](../technical-solutions/user-tasks.md)、[钱包](../technical-solutions/wallet.md)、[举报管理](../technical-solutions/report-management.md) | | 运维开发 | [非功能性需求](../operations/non-functional-requirements.md)、[开发流程指南](../operations/development-guide.md)、[部署方案](../operations/deployment.md)、[监控与维护](../operations/monitoring.md)、[定时任务](../operations/scheduled-tasks.md) | | 路线图 | [Roadmap](roadmap.md)、[附录](appendix.md) | ## 4. 联系方式 * 作者:Tongle Xu *** **文档版本**: v2.9 **最后更新**: 2026-09-30 **维护人员**: Tongle Xu --- --- url: https://www.aiforge.cn/other/octane-sysctl.md --- # Octane 内核参数调整 Laravel Octane 通过 Swoole / RoadRunner 让应用常驻内存,以少量长生命周期的 Worker 进程承接大量并发连接。默认的 Linux 内核参数面向通用场景,在高并发下容易成为瓶颈:文件描述符耗尽、端口不能及时回收、连接队列溢出等。本文参考 [Swoole 官方维基的内核参数调整](https://wiki.swoole.com/zh-cn/#/other/sysctl),结合 Octane 的运行特点给出适配建议。 ::: tip 适用范围 * 标注「仅 Swoole」的参数只在 Octane 使用 Swoole 驱动时生效,RoadRunner / FrankenPHP 驱动可忽略。 * 内核参数与 Octane 自身的 `workers`、`max_requests` 等配置是两层优化,应用层配置请参考[部署方案](/operations/deployment)。 ::: ## 1. 文件描述符(ulimit) Octane 的每个 Worker 会同时持有大量连接(客户端连接、MySQL、Redis、Reverb WebSocket 等),`ulimit -n` 建议调整为 `100000` 以上。 ### 临时修改 ```bash ulimit -n 100000 ``` ### 永久修改 编辑 `/etc/security/limits.conf`,加入: ```ini * soft nofile 262140 * hard nofile 262140 root soft nofile 262140 root hard nofile 262140 ``` 修改后需重新登录或重启系统生效。 ::: warning 进程守护方式决定了生效路径 Shell 中执行的 `ulimit` **不会影响**由 systemd / Supervisor 启动的 Octane 进程,必须在守护配置中单独设置: ```ini # systemd unit 文件(推荐) [Service] LimitNOFILE=262140 ``` ```ini ; Supervisor 的 [program:octane] 段 minfds=262140 ``` ::: ## 2. 内核参数修改方式 Linux 下修改内核参数有三种方式: * 修改 `/etc/sysctl.conf`(或新增 `/etc/sysctl.d/99-octane.conf`),保存后执行 `sysctl -p`(或 `sysctl --system`)加载,**重启后自动生效,推荐** * 使用 `sysctl` 命令临时修改:`sysctl -w net.core.somaxconn=65535` * 直接写 `/proc/sys/` 目录:`echo 65535 > /proc/sys/net/core/somaxconn` 后两种方式重启后失效,仅适合临时验证。 ## 3. 连接队列 ### net.core.somaxconn Socket 监听队列(backlog)的最大长度,系统默认通常只有 `128` ~ `4096`。高并发下新连接来不及被 Worker 接收时会在队列中等待,队列溢出会导致连接被拒绝或超时。 ```ini net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 81920 ``` 同时需要保证上层配置不超过该值: * Octane(Swoole 驱动)的 `swoole.options.backlog` 不要超过 `somaxconn` * 前置 Nginx 的 `listen ... backlog=` 指令同理 ### net.unix.max_dgram_qlen(仅 Swoole) Swoole 使用 Unix Socket Dgram 做进程间通信(如 Task 进程投递),系统默认为 `10`,请求量大时建议调大: ```ini net.unix.max_dgram_qlen = 100 ``` 也可以增加 Worker / Task Worker 数量,分摊单个进程的请求量。 ## 4. Socket 缓冲区 调大 Socket 缓冲区,提升大响应(如文件下载、AI 流式输出)场景下的吞吐: ```ini net.ipv4.tcp_mem = 379008 505344 758016 net.ipv4.tcp_wmem = 4096 16384 4194304 net.ipv4.tcp_rmem = 4096 87380 4194304 net.core.wmem_default = 8388608 net.core.rmem_default = 8388608 net.core.wmem_max = 16777216 net.core.rmem_max = 16777216 ``` ## 5. TIME_WAIT 与端口回收 ### net.ipv4.tcp_tw_reuse 开启后允许快速复用处于 TIME_WAIT 状态的端口。对 Octane 有两个直接收益: * 执行 `php artisan octane:reload` / 重启服务时,监听端口能及时重新绑定,避免「Address already in use」 * Octane 作为客户端高频访问 MySQL / Redis / 外部 HTTP 服务时,缓解本地端口耗尽 ```ini net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30 net.ipv4.ip_local_port_range = 20000 65000 net.ipv4.tcp_max_tw_buckets = 200000 ``` ### net.ipv4.tcp_tw_recycle(不要开启) Swoole 旧文档建议短连接服务开启此参数,但需注意: * 该参数在 **Linux 4.12 中已被移除**,新内核上设置会直接报错 * 开启后会造成 NAT 后的用户连接不稳定 **Octane 部署请保持关闭(0),用 `tcp_tw_reuse` 替代。** ## 6. 其他推荐配置 ```ini # SYN 洪水防护 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_synack_retries = 3 net.ipv4.tcp_syn_retries = 3 # TCP 保活(配合 Reverb 等长连接场景) net.ipv4.tcp_keepalive_time = 300 ``` ## 7. 消息队列(仅 Swoole IPC 场景) 仅当 Swoole 使用消息队列做进程间通信时才需要调整: ```ini kernel.msgmnb = 4203520 # 单个消息队列的最大字节数 kernel.msgmni = 64 # 允许创建的消息队列数量 kernel.msgmax = 8192 # 单条消息的最大长度 ``` ## 8. 开启 CoreDump Swoole Worker 发生段错误等底层异常时,CoreDump 是定位问题的重要手段: ```ini kernel.core_pattern = /data/core_files/core-%e-%p-%t ``` 通过 `ulimit -c` 查看 core 文件大小限制,若为 `0`,需在 `/etc/security/limits.conf` 中放开: ```ini * soft core unlimited * hard core unlimited ``` 注意提前创建 `/data/core_files` 目录并保证磁盘空间充足。 ## 9. 完整示例 将以下内容保存为 `/etc/sysctl.d/99-octane.conf`,执行 `sysctl --system` 一次性加载: ```ini # 连接队列 net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 81920 net.unix.max_dgram_qlen = 100 # Socket 缓冲区 net.ipv4.tcp_mem = 379008 505344 758016 net.ipv4.tcp_wmem = 4096 16384 4194304 net.ipv4.tcp_rmem = 4096 87380 4194304 net.core.wmem_default = 8388608 net.core.rmem_default = 8388608 net.core.wmem_max = 16777216 net.core.rmem_max = 16777216 # TIME_WAIT 与端口回收 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30 net.ipv4.ip_local_port_range = 20000 65000 net.ipv4.tcp_max_tw_buckets = 200000 # 其他 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_synack_retries = 3 net.ipv4.tcp_syn_retries = 3 net.ipv4.tcp_keepalive_time = 300 ``` ## 10. 验证是否生效 ```bash # 单项检查 cat /proc/sys/net/core/somaxconn cat /proc/sys/net/unix/max_dgram_qlen # 或 sysctl net.core.somaxconn # 查看当前进程的 nofile 限制(PID 为 Octane Master 进程) cat /proc//limits | grep 'open files' ``` 修改生效后重启 Octane: ```bash php artisan octane:reload ``` --- --- url: https://www.aiforge.cn/commercial-license.md --- # Ai Forge 官方商业化定价 & 授权协议 ## 定价说明 Ai Forge 是基于 Laravel 13.x + DDD 架构的**企业级 AI 全业务开发套件**,集成权限体系、钱包账务、用户运营、RAG 知识库、多模态 AI 生成、实时通信、全链路运维监控全套能力。 所有授权**一次性付费、永久商用**,区分个人学习、单项目商用、企业无限授权、白标贴牌四大套餐,价格透明、权益清晰、无隐形消费。所有付费用户均可享受版本迭代更新、安全漏洞修复、基础技术支持服务。 ## 四大授权套餐(核心在售) ### 一、开源学习版|免费・永久 **适用人群**:个人开发者、技术学习者、在校学生,仅用于技术研究、本地学习测试 **核心权益** 1. 开放基础源码,完整体验 Ai Forge 架构与全部功能模块 2. 可本地部署、二次学习调试,用于技术提升与毕业设计 3. 免费获取官方基础文档、公开教程 **严格限制** 1. **禁止任何商业用途**:不得用于商业项目开发、线上部署、对外交付、盈利变现 2. 禁止二次分发、售卖、改写后开源传播 3. 无官方技术售后、无版本升级保障 ### 二、个人商用版|¥999・特惠永久授权 **适用人群**:独立开发者、自由程序员、个人接单开发者 **定价**:¥999,一次性付费,永久有效 **核心权益** 1. 完整无删减源码,可商用二次开发 2. **仅限个人单项目商用**,支持 1 个线上商业项目部署运营 3. 终身免费基础版本更新、安全补丁修复 4. 官方专属文档、基础部署答疑、BUG 工单支持 5. 解锁全部内置模块:AI 助手、RAG 知识库、多模态生成、钱包账务、用户运营、内容审核、WebSocket 实时通信 **使用限制** 1. 仅限个人独立使用,不可转让、共享给团队多人使用 2. 禁止多项目复用、禁止团队商用、禁止贴牌售卖 3. 不包含白标去版权授权 ### 三、企业项目版|¥3999・主流推荐・团队商用 **适用人群**:中小型技术团队、外包工作室、小微企业、项目制开发团队 **定价**:¥3999,一次性付费,永久有效 **核心权益(全网最具性价比)** 1. 全套完整版源码、无功能阉割、无调用限制 2. **团队多人协作使用**,支持企业内部多人开发 3. 可无限开发、交付**自有商业项目**(企业自用业务系统不限量) 4. 终身免费版本迭代、架构优化、接口适配更新 5. 优先技术工单、专属售后答疑、部署排障指导 6. 适配火山方舟全量多模态能力、完整账务审计、风控体系 **使用限制** 1. 仅限**本企业自用项目**开发部署 2. **禁止对外贴牌、倒卖源码、二次分发售卖** 3. 不可作为标准化产品对外授权给第三方公司使用 ### 四、旗舰白标无限版|¥6999・高端企业・渠道专属(爆款) **适用人群**:软件服务商、外包公司、AI 创业团队、需要对外交付客户的企业 **定价**:¥6999,一次性付费,终身永久授权 **核心权益(最高权限、无任何限制)** 1. **全网最高权限,全功能无锁源码** 2. **无限项目商用、无限域名部署、无限客户交付** 3. **完整白标去版权**:可删除 Ai Forge 全部品牌标识、版权信息、后台 LOGO 4. 可二次封装为自有产品,对外售卖、交付终端客户 5. 享受 VIP 专属技术支持、一对一部署调试、架构咨询 6. 优先适配最新 Laravel 版本、最新大模型接口 7. 免费获取后续所有官方插件、功能模块更新 **无限制特权** 1. 允许商业二次分发、贴牌、项目交付 2. 支持团队规模化商用、渠道代理交付 3. 终身尊享全站权益,无后续隐性收费 ## 增值付费服务(按需选购) 以下服务不包含在任何授权套餐内,按项目需求单独报价。 ### 1. 年度高级运维服务 * 包含:季度安全巡检、服务器适配优化、队列性能调优、数据库优化 * 包含:专属客服 7×12 小时响应、紧急 BUG 优先修复 * 适合:线上正式运营、对稳定性要求高的商业项目 ### 2. 私有化部署实施服务 * 官方技术团队全程协助:服务器环境配置、源码部署、域名绑定、SSL 配置、数据初始化、系统上线 * 全程落地交付,到手即用,零部署门槛 ### 3. 定制开发服务 支持按需定制:支付对接、第三方 API 集成、企微 / 公众号机器人、信创适配、前端定制、专属业务模块开发 ## 官方通用授权协议核心条款(必读) ### 1. 授权通用规则 1. Ai Forge 所有源码、架构、功能模块均受国家著作权法保护,版权归属官方所有。 2. 所有付费授权均为**永久授权**,一次购买、终身使用,无需年度续费。 3. 授权为**单主体授权**,仅限购买方自身使用,授权主体不可转移、不可转借、不可共享。 ### 2. 合规使用规范 1. 严禁破解、反编译、倒卖、分销、网盘传播 Ai Forge 源码,一经发现永久拉黑、收回所有授权,追究法律责任。 2. 严禁利用本套件开发违法、违规、灰黑产项目,所有终端业务合规责任由使用者自行承担,与本项目无关。 3. 禁止去除非白标版本的官方版权标识、品牌信息,违规视为侵权。 ### 3. 售后与更新保障 1. 所有商用授权用户,终身享受官方通用版本更新、安全漏洞修复、接口兼容适配。 2. 免费技术支持仅针对**原生套件 BUG、部署环境基础问题**,不包含用户二次开发改错、自定义功能排障。 3. 老旧版本兼容问题、个性化定制需求,属于增值付费服务范畴。 ### 4. 免责声明 1. 本套件为**技术开发底座工具**,仅提供标准化技术能力,不承担使用者的项目运营、资金交易、内容审核、合规经营等连带责任。 2. 因用户服务器环境、二次开发、第三方接口变更导致的故障,不在官方免费保修范围内。 3. 火山方舟、第三方模型接口的资费、稳定性、服务变更,由对应厂商负责,本项目不承担相关责任。 ### 5. 退款政策 1. 开源免费版无退款权益。 2. 付费授权一经激活、交付源码,**无质量 BUG 问题不支持退款**,请务必确认需求后购买。 3. 若套件存在原生重大 BUG 且官方无法修复,支持全额退款。 ## 套餐权益总览表 | 权益项目 | 开源学习版 | 个人商用版 | 企业项目版 | 旗舰白标无限版 | | --- | --- | --- | --- | --- | | 价格 | 免费 | ¥999 永久 | ¥3999 永久 | ¥6999 永久 | | 完整源码 | ✅ 学习可用 | ✅ 全部解锁 | ✅ 全部解锁 | ✅ 全部解锁 | | 永久商用权限 | ❌ | ✅ 单项目 | ✅ 企业自用不限 | ✅ 无限项目交付 | | 团队多人使用 | ❌ | ❌ | ✅ | ✅ | | 版本终身更新 | ❌ | ✅ | ✅ | ✅ | | 官方技术支持 | ❌ 社区答疑 | ✅ 基础工单 | ✅ 优先售后 | ✅ VIP 一对一 | | 白标去版权 | ❌ | ❌ | ❌ | ✅ 全权去除 | | 二次贴牌售卖 | ❌ | ❌ | ❌ | ✅ 完全允许 | | 客户项目交付 | ❌ | ❌ | ❌ | ✅ 无限交付 | ## 购买方式 请通过以下方式联系购买,付款后交付对应授权源码: * 邮箱:Tongle Xu * 官方站点: ## 结语 Ai Forge 专注为开发者、企业团队提供**开箱即用的 Laravel DDD 企业级 AI 业务底座**,一站式解决权限、账务、运营、AI 多模态、RAG 知识库、实时通信、运维监控全场景开发难题,大幅降低 AI 商业项目开发成本、缩短落地周期。 正规授权、合规商用、长期迭代、全程保障,助力每一位开发者快速落地商业化 AI 项目。 *** **文档版本**: v2.0 **最后更新**: 2026-10-24 **维护人员**: Tongle Xu