diff --git a/docs/admin_reference_prd/assets/prototype_01_dashboard.png b/docs/admin_reference_prd/assets/prototype_01_dashboard.png new file mode 100644 index 0000000..a5df46e Binary files /dev/null and b/docs/admin_reference_prd/assets/prototype_01_dashboard.png differ diff --git a/docs/admin_reference_prd/assets/prototype_02_member_list.png b/docs/admin_reference_prd/assets/prototype_02_member_list.png new file mode 100644 index 0000000..fed706d Binary files /dev/null and b/docs/admin_reference_prd/assets/prototype_02_member_list.png differ diff --git a/docs/admin_reference_prd/assets/prototype_03_member_modal.png b/docs/admin_reference_prd/assets/prototype_03_member_modal.png new file mode 100644 index 0000000..656612c Binary files /dev/null and b/docs/admin_reference_prd/assets/prototype_03_member_modal.png differ diff --git a/docs/admin_reference_prd/assets/prototype_04_withdrawal.png b/docs/admin_reference_prd/assets/prototype_04_withdrawal.png new file mode 100644 index 0000000..28e3d81 Binary files /dev/null and b/docs/admin_reference_prd/assets/prototype_04_withdrawal.png differ diff --git a/docs/admin_reference_prd/assets/prototype_05_rbac.png b/docs/admin_reference_prd/assets/prototype_05_rbac.png new file mode 100644 index 0000000..0a91600 Binary files /dev/null and b/docs/admin_reference_prd/assets/prototype_05_rbac.png differ diff --git a/docs/admin_reference_prd/assets/prototype_06_flow.png b/docs/admin_reference_prd/assets/prototype_06_flow.png new file mode 100644 index 0000000..29dd1b7 Binary files /dev/null and b/docs/admin_reference_prd/assets/prototype_06_flow.png differ diff --git a/docs/admin_reference_prd/rendered/page-1.png b/docs/admin_reference_prd/rendered/page-1.png new file mode 100644 index 0000000..8fff1f7 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-1.png differ diff --git a/docs/admin_reference_prd/rendered/page-10.png b/docs/admin_reference_prd/rendered/page-10.png new file mode 100644 index 0000000..6d44e31 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-10.png differ diff --git a/docs/admin_reference_prd/rendered/page-11.png b/docs/admin_reference_prd/rendered/page-11.png new file mode 100644 index 0000000..17bb54f Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-11.png differ diff --git a/docs/admin_reference_prd/rendered/page-12.png b/docs/admin_reference_prd/rendered/page-12.png new file mode 100644 index 0000000..51e356a Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-12.png differ diff --git a/docs/admin_reference_prd/rendered/page-13.png b/docs/admin_reference_prd/rendered/page-13.png new file mode 100644 index 0000000..73f06ff Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-13.png differ diff --git a/docs/admin_reference_prd/rendered/page-14.png b/docs/admin_reference_prd/rendered/page-14.png new file mode 100644 index 0000000..89e2e09 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-14.png differ diff --git a/docs/admin_reference_prd/rendered/page-15.png b/docs/admin_reference_prd/rendered/page-15.png new file mode 100644 index 0000000..263fbf4 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-15.png differ diff --git a/docs/admin_reference_prd/rendered/page-16.png b/docs/admin_reference_prd/rendered/page-16.png new file mode 100644 index 0000000..627fb65 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-16.png differ diff --git a/docs/admin_reference_prd/rendered/page-17.png b/docs/admin_reference_prd/rendered/page-17.png new file mode 100644 index 0000000..7e84ddc Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-17.png differ diff --git a/docs/admin_reference_prd/rendered/page-18.png b/docs/admin_reference_prd/rendered/page-18.png new file mode 100644 index 0000000..2a7ac71 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-18.png differ diff --git a/docs/admin_reference_prd/rendered/page-19.png b/docs/admin_reference_prd/rendered/page-19.png new file mode 100644 index 0000000..1cd3b6a Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-19.png differ diff --git a/docs/admin_reference_prd/rendered/page-2.png b/docs/admin_reference_prd/rendered/page-2.png new file mode 100644 index 0000000..93a405c Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-2.png differ diff --git a/docs/admin_reference_prd/rendered/page-20.png b/docs/admin_reference_prd/rendered/page-20.png new file mode 100644 index 0000000..9d8f526 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-20.png differ diff --git a/docs/admin_reference_prd/rendered/page-21.png b/docs/admin_reference_prd/rendered/page-21.png new file mode 100644 index 0000000..f1ae6be Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-21.png differ diff --git a/docs/admin_reference_prd/rendered/page-22.png b/docs/admin_reference_prd/rendered/page-22.png new file mode 100644 index 0000000..13c1332 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-22.png differ diff --git a/docs/admin_reference_prd/rendered/page-23.png b/docs/admin_reference_prd/rendered/page-23.png new file mode 100644 index 0000000..de44734 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-23.png differ diff --git a/docs/admin_reference_prd/rendered/page-24.png b/docs/admin_reference_prd/rendered/page-24.png new file mode 100644 index 0000000..c163fea Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-24.png differ diff --git a/docs/admin_reference_prd/rendered/page-25.png b/docs/admin_reference_prd/rendered/page-25.png new file mode 100644 index 0000000..d125a39 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-25.png differ diff --git a/docs/admin_reference_prd/rendered/page-26.png b/docs/admin_reference_prd/rendered/page-26.png new file mode 100644 index 0000000..ec488a8 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-26.png differ diff --git a/docs/admin_reference_prd/rendered/page-27.png b/docs/admin_reference_prd/rendered/page-27.png new file mode 100644 index 0000000..634309a Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-27.png differ diff --git a/docs/admin_reference_prd/rendered/page-28.png b/docs/admin_reference_prd/rendered/page-28.png new file mode 100644 index 0000000..bbba07e Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-28.png differ diff --git a/docs/admin_reference_prd/rendered/page-29.png b/docs/admin_reference_prd/rendered/page-29.png new file mode 100644 index 0000000..b93cff7 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-29.png differ diff --git a/docs/admin_reference_prd/rendered/page-3.png b/docs/admin_reference_prd/rendered/page-3.png new file mode 100644 index 0000000..fc5c3b7 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-3.png differ diff --git a/docs/admin_reference_prd/rendered/page-30.png b/docs/admin_reference_prd/rendered/page-30.png new file mode 100644 index 0000000..177ac48 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-30.png differ diff --git a/docs/admin_reference_prd/rendered/page-31.png b/docs/admin_reference_prd/rendered/page-31.png new file mode 100644 index 0000000..beff56a Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-31.png differ diff --git a/docs/admin_reference_prd/rendered/page-4.png b/docs/admin_reference_prd/rendered/page-4.png new file mode 100644 index 0000000..cf3374e Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-4.png differ diff --git a/docs/admin_reference_prd/rendered/page-5.png b/docs/admin_reference_prd/rendered/page-5.png new file mode 100644 index 0000000..690f328 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-5.png differ diff --git a/docs/admin_reference_prd/rendered/page-6.png b/docs/admin_reference_prd/rendered/page-6.png new file mode 100644 index 0000000..54e719c Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-6.png differ diff --git a/docs/admin_reference_prd/rendered/page-7.png b/docs/admin_reference_prd/rendered/page-7.png new file mode 100644 index 0000000..901d708 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-7.png differ diff --git a/docs/admin_reference_prd/rendered/page-8.png b/docs/admin_reference_prd/rendered/page-8.png new file mode 100644 index 0000000..f621f40 Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-8.png differ diff --git a/docs/admin_reference_prd/rendered/page-9.png b/docs/admin_reference_prd/rendered/page-9.png new file mode 100644 index 0000000..0041ced Binary files /dev/null and b/docs/admin_reference_prd/rendered/page-9.png differ diff --git a/docs/admin_reference_prd/后台功能与数据库表映射.md b/docs/admin_reference_prd/后台功能与数据库表映射.md new file mode 100644 index 0000000..d5aeb83 --- /dev/null +++ b/docs/admin_reference_prd/后台功能与数据库表映射.md @@ -0,0 +1,130 @@ +# 后台功能与数据库表映射 + +> 更新时间:2026-10-09 +> 范围:当前 HotGo 后台、已实现业务模块及数据库 `olons_admin` 现有表 +> 说明:一个页面可能关联多张表;部分公共表会被多个业务模块共同使用。 + +## 1. 当前业务菜单 + +| 一级菜单 | 功能 | 核心表 | 关联表 | 说明 | +| --- | --- | --- | --- | --- | +| 主页 | 首页数据看板 | `hg_dashboard_daily_stat` | `hg_app_member`、`hg_trade_order`、`hg_recharge_order`、`hg_withdrawal_order`、`hg_task_submission` | 展示会员、订单、充值、提现、任务等汇总数据;部分指标实时统计,日指标可落汇总表。 | +| 主页 | 网站设置 | `hg_site_config_version` | — | 站点名称、Logo、隐私网址、交易限制、佣金、时间段、功能开关、自动派单等整份保存在 `config_json`;当前页面直接保存并生效。 | +| 主页 | 文本管理 | `hg_content` | `hg_content_version` | `hg_content` 保存内容主档,`hg_content_version` 保存正文版本。 | +| 主页 | Banner 设置 | `hg_banner` | `hg_sys_attachment` | 管理 Banner 图片、位置、跳转方式、展示时间和排序。 | +| 主页 | 手工商城 | `hg_product` | `hg_sys_attachment` | 与商品管理共用商品表,以 `product_type=2` 区分手工商城商品;当前只维护名称、价格和展示图片。 | +| 会员管理 | 会员列表 | `hg_app_member` | `hg_app_member_profile`、`hg_app_member_address`、`hg_app_member_payment_account`、`hg_app_member_status_log` | 会员主档、实名/扩展资料、地址、银行卡或收款账户、状态变更记录。 | +| 会员管理 | 会员列表—余额与账变 | `hg_wallet_account` | `hg_wallet_ledger`、`hg_wallet_adjustment_request` | 钱包余额保存在账户表,所有变动写入不可变流水;人工账变入口保留在会员列表操作栏。 | +| 会员管理 | 会员等级 | `hg_app_member_level` | `hg_app_member_level_version`、`hg_app_member` | 等级规则及等级版本;会员主档关联当前等级。 | +| 会员管理 | 任务大厅 | `hg_task_hall` | `hg_task_hall_level`、`hg_app_member_level` | 配置大厅入口及允许参与的会员等级。 | +| 交易管理 | 订单列表 | `hg_trade_order` | `hg_app_member`、`hg_product`、`hg_task`、`hg_wallet_ledger` | 交易订单主档及会员、商品、任务、资金流水关联。 | +| 交易管理 | 充值管理 | `hg_recharge_order` | `hg_payment_channel`、`hg_wallet_account`、`hg_wallet_ledger`、`hg_finance_review` | 充值申请、渠道信息、审核结果和入账流水。 | +| 交易管理 | 提现管理 | `hg_withdrawal_order` | `hg_wallet_account`、`hg_wallet_ledger`、`hg_finance_review`、`hg_app_member_payment_account` | 提现申请、余额冻结/扣减/退回、审核以及收款账户信息。 | +| 交易管理 | 客服管理 | `hg_customer_service` | — | 客服名称、联系方式、工作时间、在线状态和排序。 | +| 商品管理 | 商品列表 | `hg_product` | `hg_product_category`、`hg_sys_attachment` | 正式商品主档,以 `product_type=1` 区分;分类保存在商品分类表。 | +| 任务管理 | 任务列表 | `hg_task` | `hg_task_level`、`hg_product`、`hg_app_member_level` | 任务主体、关联商品、适用会员等级及佣金规则。 | +| 任务管理 | 会员任务审核 | `hg_task_submission` | `hg_task`、`hg_app_member`、`hg_app_member_level`、`hg_wallet_account`、`hg_wallet_ledger` | 审核会员提交的任务;通过后结算佣金并生成钱包流水。 | + +## 2. 已隐藏菜单但保留的业务能力 + +| 原菜单 | 保留表 | 当前入口/处理方式 | +| --- | --- | --- | +| 资金调账 | `hg_wallet_adjustment_request`、`hg_wallet_account`、`hg_wallet_ledger` | 独立菜单已隐藏;相关权限和接口保留,由会员列表中的“账变”操作进入。 | +| 会员通知 | `hg_app_member_notice` | 独立菜单已隐藏;通知/弹幕相关能力和接口保留,可从会员列表操作或后续 C 端消息中心复用。 | +| 审批中心 | `hg_approval_request`、`hg_approval_action` | 独立治理菜单已移除;表和服务仍可供高风险业务复用。 | +| 业务审计 | `hg_biz_audit_log` | 独立菜单已移除;重要操作仍可写入审计记录。 | + +## 3. HotGo 原生后台功能 + +### 3.1 组织与权限 + +| 功能 | 核心表 | 说明 | +| --- | --- | --- | +| 后台用户 | `hg_admin_member` | 后台管理员账号。 | +| 部门管理 | `hg_admin_dept` | 组织部门树。 | +| 岗位管理 | `hg_admin_post`、`hg_admin_member_post` | 岗位定义及管理员岗位关系。 | +| 角色权限 | `hg_admin_role`、`hg_admin_member_role` | 角色树、管理员角色关系。子角色可选菜单不得超过直接父角色范围。 | +| 菜单权限 | `hg_admin_menu`、`hg_admin_role_menu` | 菜单、页面、按钮权限及角色菜单关系。 | +| 接口鉴权 | `hg_admin_role_casbin` | Casbin 接口权限规则。 | +| 第三方登录 | `hg_admin_oauth` | 后台账号第三方授权关系。 | + +### 3.2 系统设置与运维 + +| 功能 | 核心表 | 说明 | +| --- | --- | --- | +| 配置管理 | `hg_sys_config` | HotGo 系统配置。网站业务配置不写入此表,而写入 `hg_site_config_version`。 | +| 字典管理 | `hg_sys_dict_type`、`hg_sys_dict_data` | 动态字典类型和选项数据;代码枚举通过 HotGo 字典注册机制提供。 | +| 定时任务 | `hg_sys_cron_group`、`hg_sys_cron` | 定时任务分组、执行策略及状态。 | +| 黑名单 | `hg_sys_blacklist` | IP 等访问黑名单。 | +| 通知公告 | `hg_admin_notice`、`hg_admin_notice_read` | 面向后台管理员的公告及阅读状态,与会员通知不是同一张表。 | +| 附件管理 | `hg_sys_attachment` | 上传文件和图片元数据。 | +| 地区编码 | `hg_sys_provinces` | 省市区层级数据。 | +| 操作日志 | `hg_sys_log` | 后台接口操作日志。 | +| 登录日志 | `hg_sys_login_log` | 后台登录记录。 | +| 短信/邮件日志 | `hg_sys_sms_log`、`hg_sys_ems_log` | 短信和邮件发送记录。 | +| 服务日志 | `hg_sys_serve_log` | 服务运行日志。 | +| 服务授权/监控 | `hg_sys_serve_license` | 服务连接与授权状态;在线用户主要依赖会话/缓存,不单独落业务表。 | + +### 3.3 HotGo 资金与支付基础能力 + +| 功能 | 核心表 | 说明 | +| --- | --- | --- | +| 后台资金账户 | `hg_admin_cash`、`hg_admin_credits_log` | HotGo 原生后台账户资金及积分/余额变动。与 C 端会员钱包表分离。 | +| HotGo 订单 | `hg_admin_order` | HotGo 原生支付订单,与业务交易订单 `hg_trade_order` 分离。 | +| 支付与退款日志 | `hg_pay_log`、`hg_pay_refund` | 支付请求、回调及退款记录。 | + +### 3.4 插件与开发工具 + +| 功能 | 核心表 | 说明 | +| --- | --- | --- | +| 插件管理 | `hg_sys_addons_install`、`hg_sys_addons_config` | 插件安装状态及插件配置。 | +| 代码生成 | `hg_sys_gen_codes` | 代码生成配置。 | +| 示例插件 | `hg_addon_hgexample_table`、`hg_addon_hgexample_tenant_order` | HotGo 示例数据,不属于正式业务。 | +| 生成示例 | `hg_sys_gen_curd_demo`、`hg_sys_gen_tree_demo`、`hg_test_category` | CRUD/树表/分类演示,不属于正式业务。 | + +## 4. 公共支撑表 + +| 表 | 用途 | 典型使用模块 | +| --- | --- | --- | +| `hg_approval_request` | 审批请求主表 | 会员状态、资金调账、充值、提现等高风险操作。 | +| `hg_approval_action` | 审批动作流水 | 记录提交、通过、拒绝等动作。 | +| `hg_biz_audit_log` | 业务审计日志 | 资金、任务审核、状态变化等重要操作。 | +| `hg_sensitive_access_log` | 敏感数据查看记录 | 会员手机号、实名、银行卡等完整信息查看。 | +| `hg_idempotency_record` | 幂等控制 | 支付、审核执行、钱包入账等不可重复操作。 | +| `hg_outbox_event` | 事务消息/事件出站箱 | 业务提交后异步通知或跨模块事件。 | +| `hg_dashboard_daily_stat` | 每日统计快照 | 首页看板。 | + +## 5. 核心数据关系 + +```text +会员 hg_app_member +├── 扩展资料 hg_app_member_profile +├── 地址 hg_app_member_address +├── 收款账户 hg_app_member_payment_account +├── 等级 hg_app_member_level +├── 钱包 hg_wallet_account +│ ├── 账变流水 hg_wallet_ledger +│ └── 人工调账 hg_wallet_adjustment_request +├── 交易订单 hg_trade_order +├── 充值订单 hg_recharge_order +├── 提现订单 hg_withdrawal_order +└── 任务提交 hg_task_submission + +商品 hg_product +├── 分类 hg_product_category +├── 交易订单 hg_trade_order +└── 任务 hg_task + ├── 等级投放 hg_task_level + └── 会员提交 hg_task_submission +``` + +## 6. 数据存储约定 + +- 业务金额统一以“分”存入 `bigint` 字段,页面负责格式化为元。 +- 会员敏感信息采用密文、检索哈希和脱敏展示值分离保存。 +- 钱包余额以 `hg_wallet_account` 为当前值,`hg_wallet_ledger` 为不可变账变依据。 +- 网站设置字段集中保存在 `hg_site_config_version.config_json`,因此新增配置项通常无需增加数据库列。 +- 商品管理和手工商城共用 `hg_product`,通过 `product_type` 区分。 +- 后台管理员资金表与 C 端会员钱包表完全分离,禁止混用。 +- 带 `tenant_id` 的业务表通过 HotGo 多租户过滤器隔离数据。 +- 带 `deleted_at` 的表使用软删除,不直接物理删除历史业务记录。 diff --git a/docs/admin_reference_prd/后台管理系统开发总结.md b/docs/admin_reference_prd/后台管理系统开发总结.md new file mode 100644 index 0000000..e9ab66c --- /dev/null +++ b/docs/admin_reference_prd/后台管理系统开发总结.md @@ -0,0 +1,453 @@ +# 后台管理系统开发总结 + +> 文档版本:V1.0 +> 编制日期:2026-10-08 +> 需求基线:[后台管理系统需求文档](./后台管理系统需求文档.md) +> 概要设计:[后台管理系统概要设计](./后台管理系统概要设计.md) +> 项目目录:`/Users/youshunlai/go_workspace/olons_admin` + +## 1. 文档说明 + +本文档汇总当前已经完成的后台管理系统开发内容,用于后续联调、测试、验收和继续开发。 + +文档中的状态分为: + +- **已开发**:已完成数据库、后端接口、前端页面和菜单权限的主体实现。 +- **复用现有系统**:HotGo 已提供对应能力,本项目不重复开发。 +- **后续补充**:已有基础模型,但仍需按专项设计补齐业务闭环。 + +## 2. 总体完成情况 + +| 章节 | 模块 | 当前状态 | 说明 | +| --- | --- | --- | --- | +| 第 4 章 | 首页数据看板 | 已开发 | 已接入真实业务统计与待办入口 | +| 第 5 章 | 门户、内容与 Banner | 已开发 | 已完成配置版本、文本、Banner、手工商城 | +| 第 6 章 | 会员管理 | 已开发 | 已完成会员、隐私、团队、钱包调账和通知 | +| 第 7 章 | 会员等级与任务大厅 | 已开发 | 已完成等级版本发布和任务大厅管理 | +| 第 8 章 | 交易管理 | 已开发 | 已完成订单、充值、提现和客服管理 | +| 第 9 章 | 商品与任务管理 | 已开发 | 已完成商品、任务和会员任务审核 | +| 第 10 章 | 管理员、角色与权限 | 复用现有系统 | HotGo 原有组织、管理员、角色和菜单权限满足主体需求 | +| 第 11 章 | 数据字典与个人设置 | 复用现有系统 | HotGo 原有字典、个人资料和安全设置满足主体需求 | +| 第 12 章 | 状态机与数据模型 | 后续补充 | 核心模型已有,冲正、归档和严格状态转换仍需补强 | + +## 3. 技术实现概况 + +### 3.1 后端 + +- Go + GoFrame v2。 +- 遵循 `api → controller → service → logic → dao` 分层。 +- 业务模块使用主模块实现,没有引入额外 Addon。 +- 表字段通过 `dao.Xxx.Columns()` 引用。 +- 多租户数据通过现有 `handler.Model` 和 `hook.SaveTenant` 处理。 +- 金额统一按“分”存储,比例统一按“万分比”存储。 +- 业务字典优先复用 HotGo 字典系统,代码枚举统一在 `internal/consts` 注册。 + +### 3.2 前端 + +- Vue 3 + Vite + TypeScript。 +- UI 统一使用 Naive UI 和项目已有的 `BasicTable`、`BasicForm`、`BasicModal`。 +- HTTP 请求统一通过 `@/utils/http/axios` 的 `http.request`。 +- 菜单由后端数据驱动,页面操作使用接口路径进行权限判断。 +- 状态标签和筛选项从后端字典加载,不在页面硬编码业务枚举。 + +### 3.3 基础治理 + +在业务模块开发前已建设以下共用能力: + +- 审批请求与审批动作。 +- 业务审计日志和哈希链。 +- 敏感信息查看记录。 +- 幂等记录。 +- Outbox 事件表。 +- 看板每日汇总表。 +- 高风险操作的申请、审批、执行分离。 + +对应迁移: + +- `20261008_001_governance.sql` +- `20261008_002_governance_menu.sql` + +## 4. 第 4 章:首页数据看板 + +### 4.1 已实现功能 + +- 今日新增会员、订单、充值、提现等核心指标。 +- 业务金额、订单和会员数据汇总。 +- 待审批和异常业务入口。 +- 按管理员权限控制敏感金额展示。 +- 原 HotGo 控制台菜单复用并扩展业务统计接口。 + +### 4.2 主要接口 + +- `GET /console/stat` +- `GET /dashboard/summary` + +### 4.3 主要代码 + +- 后端:`server/internal/logic/sys/dashboard.go` +- 前端:`web/src/views/dashboard/console/` +- 前端 API:`web/src/api/dashboard/console.ts` +- 菜单迁移:`20261008_003_dashboard_menu.sql` + +### 4.4 测试 + +- 已增加 `server/internal/logic/sys/dashboard_test.go`。 + +## 5. 第 5 章:门户、内容与 Banner 管理 + +### 5.1 网站设置 + +- 站点配置按完整快照保存。 +- 支持草稿保存、提交审批、发布和历史版本查看。 +- 支持根据历史版本生成回滚草稿。 +- 使用摘要和乐观锁避免并发覆盖。 + +主要接口: + +- `/siteConfig/current` +- `/siteConfig/history` +- `/siteConfig/save` +- `/siteConfig/submit` +- `/siteConfig/publish` +- `/siteConfig/rollback` + +### 5.2 文本管理 + +- 文本内容列表、详情、新增和编辑。 +- 内容正文按版本保存。 +- 支持发布、下线和软删除。 +- 保存当前发布版本 ID,历史版本不被覆盖。 + +主要接口: + +- `/portalContent/list` +- `/portalContent/view` +- `/portalContent/edit` +- `/portalContent/publish` +- `/portalContent/offline` +- `/portalContent/delete` + +### 5.3 Banner 管理 + +- Banner 列表、详情、新增、编辑和删除。 +- 支持投放位置、图片、替代文本、跳转方式、生效时间、排序和发布状态。 +- 支持发布和下线状态切换。 + +### 5.4 手工商城 + +- 手工商城商品列表、详情、新增、编辑、删除和上下架。 +- 支持商品类型、支付方式、金额、封面、详情、推荐和展示时间。 + +### 5.5 数据表与代码 + +新增数据表: + +- `hg_site_config_version` +- `hg_content` +- `hg_content_version` +- `hg_banner` +- `hg_product` + +主要代码: + +- 后端:`server/internal/logic/sys/portal.go` +- API:`server/api/admin/portal/portal.go` +- 前端:`web/src/views/portal/` +- 前端 API:`web/src/api/portal/index.ts` +- 迁移:`20261008_004_portal_content.sql` +- 测试:`server/internal/logic/sys/portal_test.go` + +## 6. 第 6 章:会员管理 + +### 6.1 会员主档 + +- 会员列表、详情、新增、编辑和注销。 +- 用户名、等级、上级、风控、信用分、自动派单和测试账号管理。 +- 手机号、姓名、证件号按密文、检索哈希和脱敏值分离保存。 +- 注销采用状态和软删除标记,不删除历史审计主体。 + +### 6.2 收款账户和地址 + +- 多类型收款账户管理。 +- 银行卡、开户名和手机号敏感字段加密及脱敏。 +- 会员地址列表、新增、编辑和删除。 + +### 6.3 敏感信息查看 + +- 完整敏感值通过独立授权接口查看。 +- 必须填写查看目的。 +- 记录操作人、目标、字段集、授权号和查看结果。 + +### 6.4 团队关系 + +- 支持查询会员下级团队。 +- 使用 `pid`、`level` 和 `tree` 保存层级关系。 +- 支持控制查询层级和分页。 + +### 6.5 会员状态 + +- 支持发起会员状态变更审批。 +- 审批通过后由执行接口完成状态变更。 +- 保存状态变更原因、审批单和操作记录。 + +### 6.6 钱包调账 + +- 钱包账户区分可用余额和冻结余额。 +- 调账必须先申请审批,再执行入账。 +- 每次资金变化写入不可变台账。 +- 支持幂等键、前后余额和冲正关联字段。 + +### 6.7 会员通知 + +- 支持全体或指定会员通知。 +- 区分真实通知与运营模拟。 +- 支持有效期和撤回。 + +### 6.8 数据表与代码 + +新增数据表: + +- `hg_app_member` +- `hg_app_member_profile` +- `hg_app_member_address` +- `hg_app_member_payment_account` +- `hg_app_member_status_log` +- `hg_wallet_account` +- `hg_wallet_ledger` +- `hg_wallet_adjustment_request` +- `hg_app_member_notice` + +主要代码: + +- 后端:`server/internal/logic/sys/app_member.go` +- API:`server/api/admin/appmember/appmember.go` +- 前端:`web/src/views/app-member/list/`、`wallet/`、`notice/` +- 前端 API:`web/src/api/appMember/index.ts` +- 迁移:`20261008_005_member_management.sql` + +## 7. 第 7 章:会员等级与任务大厅 + +### 7.1 会员等级 + +- 会员等级列表和详情。 +- 等级规则保存为不可变版本。 +- 支持草稿、提交审批、发布、历史版本和回滚草稿。 +- 使用乐观锁防止并发覆盖。 +- 支持等级启停和删除。 + +主要接口: + +- `/memberLevel/list` +- `/memberLevel/view` +- `/memberLevel/save` +- `/memberLevel/submit` +- `/memberLevel/publish` +- `/memberLevel/rollback` +- `/memberLevel/history` +- `/memberLevel/status` +- `/memberLevel/delete` + +### 7.2 任务大厅 + +- 任务大厅列表、详情、新增和编辑。 +- 支持封面、描述、开放时间、排序和发布状态。 +- 支持配置允许进入的会员等级。 +- 支持发布、下线和删除。 + +### 7.3 数据表与代码 + +新增数据表: + +- `hg_app_member_level` +- `hg_app_member_level_version` +- `hg_task_hall` +- `hg_task_hall_level` + +主要代码: + +- 后端:`member_level.go`、`task_hall.go` +- API:`server/api/admin/memberlevel/`、`taskhall/` +- 前端:`web/src/views/app-member/level/`、`task-hall/` +- 前端 API:`web/src/api/memberLevel/`、`taskHall/` +- 迁移:`20261008_006_member_level_task_hall.sql` + +## 8. 第 8 章:交易管理 + +### 8.1 交易订单 + +- 订单列表和详情。 +- 支持商品、任务和手工商城订单类型。 +- 保存商品名称、金额、会员等级和规则快照。 +- 人工取消匹配、重新匹配和解冻必须经过审批。 +- 执行时重新检查订单和审批状态。 + +### 8.2 充值管理 + +- 充值列表、金额汇总和详情。 +- 支持线上支付和人工充值。 +- 人工审核采用申请与执行分离。 +- 审核通过后在事务内更新钱包并写入台账。 +- 审核结果保存审批单、财务复核快照和审核原因。 + +### 8.3 提现管理 + +- 提现列表和脱敏详情。 +- 支持初审、复核、付款中、成功、失败和驳回状态。 +- 提现审核和执行通过事务、行锁、审批和台账处理。 +- 付款结果单独登记,保存外部付款单号。 + +### 8.4 客服管理 + +- 客服列表、详情、新增、编辑、启停和删除。 +- 支持客服类型、显示名称、联系方式、工作时间和排序。 + +### 8.5 数据表与代码 + +新增数据表: + +- `hg_trade_order` +- `hg_payment_channel` +- `hg_recharge_order` +- `hg_withdrawal_order` +- `hg_finance_review` +- `hg_customer_service` + +主要代码: + +- 后端:`trade_order.go`、`recharge_order.go`、`withdrawal_order.go`、`customer_service.go` +- API:`server/api/admin/tradeorder/`、`rechargeorder/`、`withdrawalorder/`、`customerservice/` +- 前端:`web/src/views/trade/` +- 前端 API:`web/src/api/trade/index.ts` +- 迁移:`20261008_007_trade_management.sql` + +## 9. 第 9 章:商品与任务管理 + +### 9.1 商品管理 + +- 商品列表、详情、新增和编辑。 +- 支持分类、类型、支付方式、店铺、价格、封面、详情和展示时间。 +- 支持上下架、推荐切换和删除。 + +### 9.2 任务管理 + +- 任务列表、详情、新增和编辑。 +- 支持任务大厅、会员等级、封面、详情、固定佣金和比例佣金。 +- 支持每日数量、单会员限制和任务有效期。 +- 支持发布、下线和删除。 + +### 9.3 会员任务审核 + +- 按会员、任务和审核状态查询提交记录。 +- 展示任务、会员、等级、佣金、成功率、证据和审核结果。 +- 待审核记录支持通过和驳回。 +- 驳回保存标准原因、补充说明和是否允许重新提交。 +- 审核通过后在事务内结算佣金并写入钱包台账。 + +### 9.4 数据表与代码 + +新增数据表: + +- `hg_product_category` +- `hg_task` +- `hg_task_level` +- `hg_task_submission` + +主要代码: + +- 后端:`product.go`、`task.go`、`task_submission.go` +- API:`server/api/admin/product/`、`task/`、`tasksubmission/` +- 前端:`web/src/views/product-task/` +- 前端 API:`web/src/api/productTask/index.ts` +- 迁移:`20261008_008_product_task_management.sql` + +菜单“商品与任务”的根布局标识已统一为 HotGo 识别的 `LAYOUT`,进入子页面时左侧菜单和顶部布局可以正常保留。 + +## 10. 复用 HotGo 的现有功能 + +### 10.1 管理员、角色与权限 + +第 10 章不重复开发,复用: + +- 组织管理中的管理员维护。 +- 角色管理和菜单权限树。 +- 接口路径权限控制。 +- 部门、岗位和数据权限。 +- 登录、禁用、密码重置等现有能力。 + +### 10.2 数据字典与个人设置 + +第 11 章不重复开发,复用: + +- “系统设置 → 字典管理”的字典类型和字典数据 CRUD。 +- 字典标签、键值、值类型、标签样式、排序和状态。 +- “个人中心 → 个人设置”的基本资料维护。 +- 手机号和邮箱验证码换绑。 +- 当前密码校验和登录密码修改。 + +密码强度、全部会话撤销、敏感配置版本管理等属于后续安全增强,不纳入当前已开发范围。 + +## 11. 数据库迁移清单 + +| 顺序 | 文件 | 用途 | +| ---: | --- | --- | +| 1 | `20261008_001_governance.sql` | 审批、审计、幂等、Outbox、看板汇总 | +| 2 | `20261008_002_governance_menu.sql` | 治理模块菜单和权限 | +| 3 | `20261008_003_dashboard_menu.sql` | 首页看板菜单和权限 | +| 4 | `20261008_004_portal_content.sql` | 门户、内容、Banner、手工商城 | +| 5 | `20261008_005_member_management.sql` | 会员、敏感资料、钱包和通知 | +| 6 | `20261008_006_member_level_task_hall.sql` | 会员等级和任务大厅 | +| 7 | `20261008_007_trade_management.sql` | 订单、充值、提现和客服 | +| 8 | `20261008_008_product_task_management.sql` | 商品、任务和会员任务审核 | + +以上迁移均采用可重复执行的建表或条件插入方式,菜单、字典和角色权限随对应业务迁移配置。 + +## 12. 当前运行与交付状态 + +- 本地后端运行地址:`http://127.0.0.1:8000`。 +- 本地前端运行地址:`http://127.0.0.1:8001`。 +- 动态菜单已加载首页、治理、门户、会员、交易、商品与任务模块。 +- 第 4~9 章页面已经可以从后台菜单进入查看。 +- 第 10、11 章继续使用 HotGo 原有页面。 + +### 12.1 本地演示数据 + +已为本次新增的 35 张业务表写入关联演示数据,覆盖审批、发布、会员、风控、订单、充值、提现、任务审核、调账和客服等全部已定义状态。演示业务编号统一使用 `DEMO` 前缀。 + +可重复执行的数据脚本: + +```text +server/storage/data/seeds/20261008_business_demo_data.sql +``` + +本地重新导入命令: + +```bash +mysql -h127.0.0.1 -P3306 -uroot -proot olons_admin \ + < server/storage/data/seeds/20261008_business_demo_data.sql +``` + +正式上线前仍需执行完整的接口回归、权限角色矩阵检查、并发资金测试和生产配置核对。 + +--- + +## 补充项:第 12 章状态机与数据模型补强 + +第 12 章不是新的一级菜单,而是对现有会员、资金、任务和内容模块的业务约束补强。当前核心数据表、审批、台账和审计基础已经具备,但以下内容尚未列入“已完成”: + +1. 会员状态合法转换和注销不可逆。 +2. 充值成功后的审批冲正。 +3. 已结算任务佣金的审批冲正。 +4. 文本内容归档和历史版本生成回滚草稿。 +5. 状态转换、重复执行、并发执行和余额不足测试。 + +专项设计已单独整理,后续开发以该文档为实施依据: + +[第12章状态机与数据模型补强设计](./第12章状态机与数据模型补强设计.md) + +建议后续新增迁移: + +```text +server/storage/data/migrations/20261008_009_state_machine_hardening.sql +``` + +MFA 真实认证流程不与本次状态机补强混合实施,后续作为独立安全增强项处理。 diff --git a/docs/admin_reference_prd/后台管理系统概要设计.md b/docs/admin_reference_prd/后台管理系统概要设计.md new file mode 100644 index 0000000..35b1924 --- /dev/null +++ b/docs/admin_reference_prd/后台管理系统概要设计.md @@ -0,0 +1,1244 @@ +# 后台管理系统概要设计 + +> 文档版本:V1.0 +> 编制日期:2026-10-08 +> 依据:[后台管理系统需求文档](./后台管理系统需求文档.md) +> 技术基线:Go 1.24 + GoFrame v2.10 + Vue 3 + TypeScript + Naive UI + MySQL 8.0 + Redis + +## 目录 + +- [1. 文档目标](#1-文档目标) +- [2. 设计原则](#2-设计原则) +- [3. 技术架构](#3-技术架构) +- [4. 系统模块设计](#4-系统模块设计) +- [5. 核心业务流程](#5-核心业务流程) +- [6. 数据库设计](#6-数据库设计) +- [7. 字典与状态设计](#7-字典与状态设计) +- [8. 接口设计约定](#8-接口设计约定) +- [9. 缓存、队列与定时任务](#9-缓存队列与定时任务) +- [10. 安全设计](#10-安全设计) +- [11. 可观测性与运维](#11-可观测性与运维) +- [12. 性能与容量设计](#12-性能与容量设计) +- [13. 开发与数据库迁移顺序](#13-开发与数据库迁移顺序) +- [14. 测试策略](#14-测试策略) +- [15. 分阶段交付](#15-分阶段交付) +- [16. 需进一步确认的设计决策](#16-需进一步确认的设计决策) + +--- + +## 1. 文档目标 + +本文档用于将需求基线转换为可实施的系统概要设计,作为后续详细设计、数据库建模、接口定义、代码开发、测试与上线评审的共同输入。 + +本版设计重点解决: + +- C 端与后台在配置、状态、金额和业务单号上的一致性。 +- 会员隐私数据、银行卡与钱包账户的加密、脱敏、临时授权和审计。 +- 充值、提现、账变、奖励金等资金操作的幂等、台账、冻结、复核与冲正。 +- 站点配置、文本、Banner、会员等级等高影响配置的草稿、审批、发布、版本和回滚。 +- 管理员 RBAC、数据权限、敏感操作复核与不可篡改审计。 + +### 1.1 范围边界 + +本设计覆盖需求文档中的 21 个后台页面和对应 C 端业务闭环。不包含支付机构的具体 SDK 选型、实名认证供应商选型、法律合规结论和生产密钥管理实施细节。这些内容需在接入真实资金前单独评审。 + +## 2. 设计原则 + +1. **单一事实源**:字典、状态、金额口径和配置版本由后端统一提供,前端不硬编码枚举。 +2. **台账驱动资金**:余额不允许普通 CRUD 直接修改,所有变动必须由不可变台账产生。 +3. **敏感数据分离**:会员主表不保存可直接展示的完整敏感值;密文、检索哈希和脱敏值分字段存储。 +4. **最小权限**:菜单权限、接口权限、数据权限和敏感值查看权限分层判定。 +5. **高风险双人复核**:资金、权限、禁用会员和发布配置采用“申请人 ≠ 复核人”。 +6. **服务端状态机**:客户端只发送意图,不直接指定结果状态。 +7. **可追溯与可冲正**:已生效的资金与审批记录不原地覆盖,通过反向记录冲正。 +8. **兼容 HotGo 规范**:遵循 `api → controller → service → logic → dao` 分层,字典由 `internal/consts` 注册,路由由 `genrouter` 加载。 + +## 3. 技术架构 + +```mermaid +flowchart LR + A[后台 Web
Vue 3 + Naive UI] -->|HTTPS /admin| B[GoFrame HTTP API] + C[C 端] -->|HTTPS /api| B + B --> D[认证、RBAC
Casbin + Middleware] + B --> E[业务 Service / Logic] + E --> F[(MySQL 8.0)] + E --> G[(Redis / Cache)] + E --> H[对象存储
附件/凭证/图片] + E --> I[队列 / Outbox] + I --> J[通知、统计、对账消费者] + E --> K[支付/客服/实名等外部服务] + B --> L[日志、Trace、指标、告警] +``` + +### 3.1 部署单元 + +| 单元 | 职责 | 状态 | +| --- | --- | --- | +| `web` | 后台 SPA,路由由后端菜单驱动 | 无状态 | +| `server` | 管理 API、C 端 API、业务状态机、审批和台账 | 无状态,可水平扩展 | +| MySQL | 核心业务数据、台账、审批和审计 | 主从/高可用 | +| Redis | 会话、限流、短期缓存、分布式锁 | 不作资金最终事实源 | +| 对象存储 | Banner、商品、任务、凭证和收款码 | 通过 `hg_sys_attachment` 管理元数据 | +| 队列 | 通知、统计、回调重试和对账 | 必须与 Outbox 表组合 | + +### 3.2 代码模块划分 + +本项目属于系统核心业务,采用主模块实现,不作为临时 Addon。 + +| 业务域 | 后端模块建议 | 前端视图建议 | +| --- | --- | --- | +| 看板 | `dashboard` | `views/dashboard/business` | +| 站点与内容 | `siteConfig`, `content`, `banner`, `customerService` | `views/content/*` | +| 会员 | `appMember`, `memberLevel`, `memberPaymentAccount` | `views/member/*` | +| 商品 | `productCategory`, `product` | `views/product/*` | +| 任务 | `taskHall`, `task`, `taskSubmission` | `views/task/*` | +| 交易 | `tradeOrder`, `recharge`, `withdrawal` | `views/trade/*` | +| 资产 | `wallet`, `walletLedger`, `walletAdjustment` | `views/finance/*` | +| 治理 | `approval`, `sensitiveAccess`, `bizAudit` | `views/governance/*` | + +每个模块依次在以下位置实现: + +```text +server/api/admin// +server/internal/controller/admin/sys/.go +server/internal/service/sys.go +server/internal/logic/sys/.go +server/internal/model/input/sysin/.go +server/internal/router/genrouter/.go +web/src/api//index.ts +web/src/views// +``` + +## 4. 系统模块设计 + +### 4.1 认证与权限 + +复用现有 `hg_admin_member`、`hg_admin_role`、`hg_admin_menu`、`hg_admin_member_role`、`hg_admin_role_menu` 和 Casbin 能力。 + +权限判定顺序: + +1. 校验管理员会话、账号状态和 MFA 状态。 +2. 按路由权限标识判定功能权限。 +3. 通过 `handler.FilterAuth` 或 `FilterAuthWithField` 施加数据范围。 +4. 对完整手机号、银行卡、收款码等额外校验敏感值查看权限和二次认证。 +5. 资金、权限和配置发布类操作进入审批流,申请人不得审批自己的申请。 + +### 4.2 站点配置与内容发布 + +- 站点配置按版本存储为 JSON,同时记录 Schema 版本、摘要和生效时间。 +- 业务代码不从零散 key-value 直接组装核心规则,而是读取唯一的“已发布版本”。 +- 文本内容使用主表 + 版本表;发布仅更新主表的 `published_version_id`。 +- Banner 和客服渠道为结构化数据,支持有效期、排序、启停和作用域。 +- 发布后删除缓存,通过 Outbox 发送“配置已更新”事件。 + +### 4.3 会员与隐私数据 + +- C 端会员使用 `hg_app_member`,不与后台管理员 `hg_admin_member` 混用。 +- 登录检索使用 `mobile_hash`,列表展示使用 `mobile_mask`,完整手机号仅存在 `mobile_cipher`。 +- 哈希使用带服务端 Pepper 的 HMAC-SHA-256,避免穷举原始手机号。 +- 收款账户、地址和身份信息分表存储,列表查询不联表返回完整密文。 +- 敏感值查看接口使用一次性授权令牌,返回短时数据,不缓存且写入专用审计表。 + +### 4.4 商品、任务和交易订单 + +- 商品、任务和会员等级均可停用;被历史订单引用后不得物理删除。 +- 交易订单保存商品、任务、等级和佣金规则快照,后续修改基础配置不影响在途订单。 +- 订单状态由 Logic 层状态机转换,更新时携带 `version` 作为乐观锁。 +- 任务提交通过独立审核表流转,任务通过后由结算服务写入佣金台账。 + +### 4.5 钱包与资金安全 + +资金字段统一使用 `bigint` 存储最小货币单位(CNY 为分),字段名以 `_amount` 或 `_balance` 结尾,不使用 `float64` 参与资金计算。每个资金动作在单个数据库事务内完成: + +1. 锁定业务单和钱包行。 +2. 校验业务单状态、幂等键、可用/冻结余额和审批结果。 +3. 写入不可变台账,台账同时记录变更前后值。 +4. 更新钱包汇总值和版本号。 +5. 更新业务单状态并写入 Outbox 事件。 +6. 事务提交后异步通知 C 端、更新看板和执行对账。 + +### 4.6 统一审批 + +`hg_approval_request` 和 `hg_approval_action` 提供通用审批能力,覆盖: + +- 提现审核与大额复核。 +- 人工充值、账变、奖励金和冲正。 +- 会员禁用、解禁和异常解冻。 +- 角色权限变更。 +- 站点配置、会员等级和重要内容发布。 + +审批请求保存目标类型、目标 ID、操作类型、变更摘要和完整 payload 快照。业务表只引用审批单号,审批通过后由对应业务 Logic 执行最终变更。 + +## 5. 核心业务流程 + +### 5.1 充值入账 + +```mermaid +sequenceDiagram + participant U as C 端会员 + participant API as API + participant P as 支付渠道 + participant R as 充值订单 + participant W as 钱包/台账 + U->>API: 创建充值订单 + API->>R: 写入 pending_payment + API->>P: 创建支付 + P-->>API: 异步回调 + API->>R: 验签、金额、商户、幂等校验 + API->>W: 事务内写台账并增加余额 + API->>R: 置为 success + API-->>U: 通知到账 +``` + +人工凭证充值不经过自动回调,进入初审和复核。两条路径共用同一个入账方法和幂等约束。 + +### 5.2 提现与资金冻结 + +```mermaid +stateDiagram-v2 + [*] --> pending_review: 提交申请并冻结金额 + pending_review --> reviewing: 初审通过 + pending_review --> rejected: 初审驳回 + reviewing --> processing: 复核通过 + reviewing --> rejected: 复核驳回 + processing --> success: 通道付款成功 + processing --> failed: 付款失败 + pending_review --> cancelled: 会员撤销 + rejected --> [*]: 解冻 + cancelled --> [*]: 解冻 + failed --> pending_review: 审核后重试 + success --> [*]: 扣减冻结金额 +``` + +创建提现订单时立即将 `available_balance` 转入 `frozen_balance`;驳回或撤销时解冻;付款成功时扣减冻结余额。每一步均产生台账。 + +### 5.3 任务审核与佣金 + +1. 会员领取任务时生成交易订单,固化商品、任务、等级和佣金快照。 +2. 会员提交任务证据,产生 `task_submission`。 +3. 后台审核通过时更新提交记录,调用结算服务写入佣金台账。 +4. 同一提交记录仅允许产生一笔有效佣金台账。 +5. 已结算记录如需撤销,必须新建冲正审批并写入反向台账。 + +### 5.4 配置发布 + +1. 编辑者基于已发布版本创建草稿。 +2. 前端完成基础校验,后端 `Filter()` 完成 Schema 和跨字段校验。 +3. 提交审批后草稿锁定,禁止再编辑。 +4. 审批人查看前后差异并通过或驳回。 +5. 即时或定时发布,原版本置为历史版本。 +6. 清理配置缓存,通知 C 端与后台重新拉取版本。 + +## 6. 数据库设计 + +### 6.1 数据库基线 + +- 生产基线为 MySQL 8.0,存储引擎 InnoDB,字符集 `utf8mb4`,排序规则统一为 `utf8mb4_0900_ai_ci`。 +- 时间字段使用 `datetime(3)` 保存 UTC,展示时根据用户时区转换。 +- 主键使用 `bigint unsigned`;全局业务单号使用时间有序 ID 或雪花 ID,字符串形式最长 32 位。 +- 金额使用 `bigint` 最小货币单位;比例使用 `int` 万分比,`10000 = 100%`。 +- 新增业务表使用 `hg_` 前缀;不直接修改 CLI 生成的 `entity`、`do` 和 `dao/internal`。 +- 为适配 HotGo 代码生成和多库兼容,业务关系使用逻辑外键 + 索引,默认不创建物理外键。 +- 可编辑业务表使用软删除;台账、审批动作、回调、幂等、Outbox 和审计表禁止删除。 + +### 6.2 通用字段 + +所有新表至少包含 `id`、`tenant_id` 和 `created_at`。可编辑业务表默认使用以下完整模板;台账、审批动作、回调、幂等、Outbox 和审计等不可变表仅保留上述三个基础字段与各自明确定义的业务字段,不设置 `updated_at`、`deleted_at`、`updated_by` 和 `deleted_by`。 + +| 字段 | 类型 | 约束 | 说明 | +| --- | --- | --- | --- | +| `id` | bigint unsigned | PK, auto_increment | 主键 | +| `tenant_id` | bigint unsigned | NOT NULL DEFAULT 0, INDEX | 租户;单租户部署为 0 | +| `status` | tinyint | NOT NULL DEFAULT 1, INDEX | 仅需通用启停时复用 `sys_normal_disable`:1正常、2停用;已有完整业务状态字段的表可省略此字段 | +| `sort` | int | NOT NULL DEFAULT 0 | 排序值,越小越靠前 | +| `version` | int unsigned | NOT NULL DEFAULT 0 | 乐观锁 | +| `remark` | varchar(500) | NULL | 内部备注 | +| `created_by` | bigint unsigned | NOT NULL DEFAULT 0 | 后台创建人;C 端创建时为 0 | +| `updated_by` | bigint unsigned | NOT NULL DEFAULT 0 | 最后后台更新人 | +| `deleted_by` | bigint unsigned | NOT NULL DEFAULT 0 | 软删除人 | +| `created_at` | datetime(3) | NOT NULL | 创建时间 | +| `updated_at` | datetime(3) | NOT NULL | 更新时间 | +| `deleted_at` | datetime(3) | NULL, INDEX | 软删除时间 | + +### 6.3 表关系概览 + +一期共规划 39 张新业务表,按域划分如下: + +| 业务域 | 表数 | 主要表 | +| --- | ---: | --- | +| 配置与内容 | 5 | 配置版本、内容与版本、Banner、客服渠道 | +| 会员 | 8 | 等级与版本、会员、隐私资料、地址、收款账户、会话、状态记录 | +| 商品与任务 | 7 | 分类、商品、任务大厅、任务、等级关系、任务提交 | +| 交易与资金 | 11 | 交易订单、钱包、台账、支付渠道、充值、提现、账变、审核、回调、对账 | +| 治理与可靠性 | 7 | 审批、敏感查看、审计、幂等、Outbox、看板汇总 | +| 通知 | 1 | C 端通知 | + +```mermaid +erDiagram + APP_MEMBER_LEVEL ||--o{ APP_MEMBER : owns + APP_MEMBER ||--|| APP_MEMBER_PROFILE : has + APP_MEMBER ||--o{ APP_MEMBER_ADDRESS : has + APP_MEMBER ||--o{ APP_MEMBER_PAYMENT_ACCOUNT : binds + APP_MEMBER ||--o{ APP_MEMBER_SESSION : logs_in + APP_MEMBER ||--|| WALLET_ACCOUNT : owns + WALLET_ACCOUNT ||--o{ WALLET_LEDGER : produces + PRODUCT_CATEGORY ||--o{ PRODUCT : contains + TASK_HALL ||--o{ TASK_HALL_LEVEL : permits + APP_MEMBER_LEVEL ||--o{ TASK_HALL_LEVEL : allowed + TASK_HALL ||--o{ TASK : groups + TASK ||--o{ TASK_LEVEL : permits + APP_MEMBER_LEVEL ||--o{ TASK_LEVEL : allowed + TASK ||--o{ TRADE_ORDER : creates + APP_MEMBER ||--o{ TRADE_ORDER : places + TRADE_ORDER ||--o| TASK_SUBMISSION : submits + APP_MEMBER ||--o{ RECHARGE_ORDER : recharges + APP_MEMBER ||--o{ WITHDRAWAL_ORDER : withdraws + APP_MEMBER_PAYMENT_ACCOUNT ||--o{ WITHDRAWAL_ORDER : receives + APPROVAL_REQUEST ||--o{ APPROVAL_ACTION : contains + APPROVAL_REQUEST ||--o{ FINANCE_REVIEW : drives + CONTENT ||--o{ CONTENT_VERSION : versions + SITE_CONFIG_VERSION ||--o| APPROVAL_REQUEST : publishes + PAYMENT_CHANNEL ||--o{ RECHARGE_ORDER : handles + PAYMENT_CHANNEL ||--o{ WITHDRAWAL_ORDER : handles +``` + +### 6.4 现有系统表复用 + +| 表 | 用途 | 设计处理 | +| --- | --- | --- | +| `hg_admin_member` | 后台管理员 | 直接复用,不存 C 端会员 | +| `hg_admin_role` / `hg_admin_menu` | 角色、菜单、接口权限 | 直接复用,增加业务菜单数据 | +| `hg_admin_member_role` / `hg_admin_role_menu` | RBAC 关系 | 直接复用 | +| `hg_sys_attachment` | 文件与对象存储元数据 | 图片、凭证、收款码统一引用附件 ID | +| `hg_sys_dict_type` / `hg_sys_dict_data` | 可维护字典 | 用于非核心动态选项 | +| `hg_sys_config` | HotGo 系统自身配置 | 仅保留框架配置,不承载需审批的业务配置 | +| `hg_sys_log` / `hg_sys_login_log` | 请求与登录日志 | 保留,与业务审计表互补 | + +字典功能不新建表、不新建 API、不另做前端 Store。统一复用 `hg_sys_dict_type`、`hg_sys_dict_data`、`server/internal/library/dict`、`/dictData/option`、`/dictData/options` 和 `web/src/store/modules/dict.ts`。详细复用规则见第 7 章。 + +### 6.5 配置与内容域 + +#### 6.5.1 `hg_site_config_version` 站点配置版本 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `config_no` | varchar(32) | NOT NULL, UNIQUE(`tenant_id`,`config_no`) | 配置版本号 | +| `config_group` | varchar(64) | NOT NULL, INDEX | `site_basic`/`trade_rule`/`time_window` 等 | +| `schema_version` | varchar(32) | NOT NULL | 校验 Schema 版本 | +| `config_json` | json | NOT NULL | 完整配置快照 | +| `config_digest` | char(64) | NOT NULL | SHA-256,用于差异与一致性校验 | +| `publish_status` | tinyint | NOT NULL, INDEX | 1草稿 2待审 3已发布 4已回滚 5已失效 | +| `effective_at` | datetime(3) | NULL, INDEX | 计划生效时间 | +| `published_at` | datetime(3) | NULL | 实际发布时间 | +| `approval_request_id` | bigint unsigned | NOT NULL DEFAULT 0, INDEX | 关联审批单 | +| `base_version_id` | bigint unsigned | NOT NULL DEFAULT 0 | 基础版本 | + +索引:`INDEX idx_config_current (tenant_id, config_group, publish_status, effective_at)`。同一租户和配置组在某一时点只允许一个有效已发布版本,由发布事务保证。 + +#### 6.5.2 `hg_content` 内容主表 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `content_key` | varchar(64) | UNIQUE(`tenant_id`,`content_key`) | 稳定内容标识,删除后不复用 | +| `content_type` | varchar(32) | NOT NULL, INDEX | notice/about/faq/rule/activity/popup | +| `title` | varchar(200) | NOT NULL | 后台标题 | +| `published_version_id` | bigint unsigned | NOT NULL DEFAULT 0 | 当前发布版本 | +| `publish_status` | tinyint | NOT NULL, INDEX | 内容状态 | +| `published_at` | datetime(3) | NULL | 发布时间 | + +#### 6.5.3 `hg_content_version` 内容版本 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `content_id` | bigint unsigned | NOT NULL, INDEX | 内容主表 ID | +| `version_no` | int unsigned | UNIQUE(`content_id`,`version_no`) | 递增版本 | +| `title` | varchar(200) | NOT NULL | 展示标题 | +| `summary` | varchar(500) | NULL | 摘要 | +| `body_html` | longtext | NOT NULL | 服务端清洗后 HTML | +| `body_text` | longtext | NULL | 搜索与降级文本 | +| `asset_ids` | json | NULL | 正文引用附件 ID | +| `publish_status` | tinyint | NOT NULL, INDEX | 1草稿 2待审 3已发布 4已下线 5已归档 | +| `effective_at` | datetime(3) | NULL | 定时发布 | +| `approval_request_id` | bigint unsigned | NOT NULL DEFAULT 0 | 审批单 | + +#### 6.5.4 `hg_banner` Banner + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `name` | varchar(100) | NOT NULL | 后台名称 | +| `position` | varchar(32) | NOT NULL, INDEX | home_top/home_popup 等 | +| `image_attachment_id` | bigint unsigned | NOT NULL | 图片附件 | +| `alt_text` | varchar(200) | NOT NULL | 替代文本 | +| `target_type` | tinyint | NOT NULL | 1无跳转 2内部路由 3外部 URL | +| `target_value` | varchar(500) | NULL | 跳转值 | +| `start_at` / `end_at` | datetime(3) | NULL, INDEX | 有效期 | +| `publish_status` | tinyint | NOT NULL, INDEX | 草稿/已发布/已下线 | + +#### 6.5.5 `hg_customer_service_channel` 客服渠道 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `name` | varchar(100) | NOT NULL | 渠道名称 | +| `channel_type` | tinyint | NOT NULL | 1内置工单 2外部链接 3客服电话 4即时通讯 | +| `entry_url_cipher` | varchar(1024) | NULL | 加密入口地址 | +| `fallback_text` | varchar(500) | NULL | 降级联系方式 | +| `work_time_json` | json | NOT NULL | 工作时段及时区 | +| `allowed_origins` | json | NULL | iframe 来源白名单 | + +### 6.6 会员域 + +#### 6.6.1 `hg_app_member_level` 会员等级 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `level_code` | varchar(32) | UNIQUE(`tenant_id`,`level_code`) | 稳定等级编码 | +| `name` | varchar(64) | NOT NULL | 等级名称 | +| `current_version_id` | bigint unsigned | NOT NULL DEFAULT 0 | 当前规则版本 | +| `member_count` | bigint unsigned | NOT NULL DEFAULT 0 | 可重建的统计值 | + +#### 6.6.2 `hg_app_member_level_version` 等级规则版本 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `level_id` | bigint unsigned | NOT NULL, INDEX | 等级 ID | +| `version_no` | int unsigned | UNIQUE(`level_id`,`version_no`) | 版本号 | +| `upgrade_amount` | bigint | NOT NULL DEFAULT 0 | 升级金额,分 | +| `gift_amount` | bigint | NOT NULL DEFAULT 0 | 赠送金额,分 | +| `daily_order_limit` | int unsigned | NOT NULL DEFAULT 0 | 每日接单次数 | +| `commission_rate` | int unsigned | NOT NULL DEFAULT 0 | 佣金万分比 | +| `daily_withdraw_limit` | int unsigned | NOT NULL DEFAULT 0 | 每日提现次数 | +| `rule_json` | json | NULL | 扩展规则快照 | +| `publish_status` | tinyint | NOT NULL, INDEX | 发布状态 | +| `effective_at` | datetime(3) | NULL | 生效时间 | + +#### 6.6.3 `hg_app_member` C 端会员 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `member_no` | varchar(32) | UNIQUE(`tenant_id`,`member_no`) | 对外会员号 | +| `username` | varchar(64) | UNIQUE(`tenant_id`,`username`) | 登录名,注销后不复用 | +| `mobile_cipher` | varchar(512) | NOT NULL | 手机号密文 | +| `mobile_hash` | char(64) | UNIQUE(`tenant_id`,`mobile_hash`) | 带 Pepper 检索哈希,注销后按合规策略做不可逆匿名化 | +| `mobile_mask` | varchar(32) | NOT NULL | 脱敏手机号 | +| `password_hash` | varchar(255) | NOT NULL | Argon2id/bcrypt 结果 | +| `trade_password_hash` | varchar(255) | NULL | 交易密码,不可回显 | +| `level_id` | bigint unsigned | NOT NULL, INDEX | 会员等级 | +| `parent_id` | bigint unsigned | NOT NULL DEFAULT 0, INDEX | 直接上级 | +| `level` | int unsigned | NOT NULL DEFAULT 1 | 关系树级别 | +| `tree` | varchar(1024) | NOT NULL DEFAULT '' | 关系树路径 | +| `invite_code` | varchar(32) | UNIQUE(`tenant_id`,`invite_code`) | 邀请码 | +| `member_status` | tinyint | NOT NULL DEFAULT 1, INDEX | 会员生命周期:正常/受限/禁用/注销 | +| `risk_status` | tinyint | NOT NULL DEFAULT 1, INDEX | 1正常 2观察 3限制 4冻结 | +| `auto_dispatch` | tinyint | NOT NULL DEFAULT 2 | 复用 `sys_switch`:1开启、2关闭 | +| `credit_score` | int | NOT NULL DEFAULT 100 | 信用分 | +| `last_login_at` | datetime(3) | NULL | 最后登录时间 | +| `last_login_ip_cipher` | varchar(512) | NULL | 加密 IP | +| `last_login_region` | varchar(128) | NULL | IP 地区粗粒度结果 | + +重要索引:`idx_member_parent (tenant_id,parent_id,status)`、`idx_member_level (tenant_id,level_id,status)`、`idx_member_created (tenant_id,created_at,id)`。 + +#### 6.6.4 `hg_app_member_profile` 会员隐私资料 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `member_id` | bigint unsigned | UNIQUE | 会员 ID | +| `real_name_cipher` | varchar(512) | NULL | 姓名密文 | +| `real_name_mask` | varchar(64) | NULL | 脱敏姓名 | +| `id_no_cipher` | varchar(1024) | NULL | 证件号密文 | +| `id_no_hash` | char(64) | NULL, INDEX | 证件检索哈希 | +| `id_no_mask` | varchar(64) | NULL | 脱敏证件号 | +| `avatar_attachment_id` | bigint unsigned | NOT NULL DEFAULT 0 | 头像 | +| `kyc_status` | tinyint | NOT NULL DEFAULT 0, INDEX | 0未认证 1审核中 2已认证 3驳回 | +| `kyc_verified_at` | datetime(3) | NULL | 认证时间 | + +#### 6.6.5 `hg_app_member_address` 会员地址 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 会员 ID | +| `receiver_name_cipher` / `receiver_name_mask` | varchar(512)/varchar(64) | NOT NULL | 收件人密文/脱敏值 | +| `receiver_mobile_cipher` / `receiver_mobile_mask` | varchar(512)/varchar(32) | NOT NULL | 电话密文/脱敏值 | +| `province_id` / `city_id` / `district_id` | bigint unsigned | NOT NULL DEFAULT 0 | 省市区 | +| `address_cipher` | varchar(1024) | NOT NULL | 详细地址密文 | +| `is_default` | tinyint | NOT NULL DEFAULT 0 | 默认地址 | + +#### 6.6.6 `hg_app_member_payment_account` 收款账户 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 会员 ID | +| `account_type` | tinyint | NOT NULL, INDEX | 1银行卡 2数字钱包 3云闪付 4支付宝 5微信 | +| `bank_code` | varchar(32) | NULL | 银行编码 | +| `bank_name` | varchar(100) | NULL | 银行名称快照 | +| `holder_name_cipher` / `holder_name_mask` | varchar(512)/varchar(64) | NOT NULL | 户名 | +| `account_no_cipher` | varchar(1024) | NOT NULL | 账号密文 | +| `account_no_hash` | char(64) | NOT NULL, INDEX | 检索与去重哈希 | +| `account_no_mask` | varchar(64) | NOT NULL | 脱敏账号 | +| `qr_attachment_id` | bigint unsigned | NOT NULL DEFAULT 0 | 收款码附件 | +| `verify_status` | tinyint | NOT NULL DEFAULT 0 | 校验状态 | +| `is_default` | tinyint | NOT NULL DEFAULT 0 | 默认收款账户 | + +唯一约束:`UNIQUE (tenant_id,member_id,account_type,account_no_hash)`。已解绑账户可重新启用,不重复插入。MySQL 中 `NULL` 不能可靠表示“仅未删除数据唯一”,因此本设计不将可空 `deleted_at` 加入唯一索引。 + +#### 6.6.7 `hg_app_member_session` 会员会话 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 会员 ID | +| `session_no` | varchar(64) | UNIQUE | 会话号 | +| `refresh_token_hash` | char(64) | NOT NULL | 刷新令牌哈希 | +| `device_id_hash` | char(64) | NULL, INDEX | 设备标识哈希 | +| `ip_cipher` | varchar(512) | NULL | IP 密文 | +| `user_agent` | varchar(512) | NULL | 客户端信息 | +| `expires_at` | datetime(3) | NOT NULL, INDEX | 过期时间 | +| `revoked_at` | datetime(3) | NULL | 撤销时间 | + +#### 6.6.8 `hg_app_member_status_log` 会员状态记录 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 会员 ID | +| `from_status` / `to_status` | tinyint | NOT NULL | 状态变更 | +| `reason_code` | varchar(64) | NOT NULL | 标准原因 | +| `reason_text` | varchar(500) | NULL | 补充说明 | +| `effective_at` / `expire_at` | datetime(3) | NULL | 生效和到期时间 | +| `approval_request_id` | bigint unsigned | NOT NULL DEFAULT 0 | 审批单 | +| `operator_id` | bigint unsigned | NOT NULL | 操作人 | + +该表为追加表,不软删除。 + +### 6.7 商品与任务域 + +#### 6.7.1 `hg_product_category` 商品分类 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `pid` | bigint unsigned | NOT NULL DEFAULT 0, INDEX | 父级 ID | +| `level` | int unsigned | NOT NULL DEFAULT 1 | 树等级 | +| `tree` | varchar(512) | NOT NULL DEFAULT '' | 关系树 | +| `category_code` | varchar(32) | UNIQUE(`tenant_id`,`category_code`) | 分类编码 | +| `name` | varchar(100) | NOT NULL | 分类名称 | + +#### 6.7.2 `hg_product` 商品 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `product_no` | varchar(32) | UNIQUE(`tenant_id`,`product_no`) | 商品编号 | +| `category_id` | bigint unsigned | NOT NULL, INDEX | 分类 | +| `product_type` | tinyint | NOT NULL DEFAULT 1, INDEX | 1标准商品 2手工商城商品 | +| `payment_type` | varchar(32) | NOT NULL DEFAULT 'balance', INDEX | 优先复用并按需扩展现有 `payType` 枚举字典 | +| `name` | varchar(200) | NOT NULL, INDEX | 商品名称 | +| `shop_name` | varchar(100) | NOT NULL | 店铺名称快照 | +| `price_amount` | bigint | NOT NULL | 单价,分 | +| `currency` | char(3) | NOT NULL DEFAULT 'CNY' | 币种 | +| `cover_attachment_id` | bigint unsigned | NOT NULL | 封面 | +| `detail_html` | longtext | NULL | 清洗后富文本 | +| `is_recommended` | tinyint | NOT NULL DEFAULT 2, INDEX | 复用 `sys_switch`:1推荐、2不推荐 | +| `publish_status` | tinyint | NOT NULL, INDEX | 草稿/上架/下架 | +| `start_at` / `end_at` | datetime(3) | NULL | 展示时间 | + +#### 6.7.3 `hg_task_hall` 任务大厅 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `hall_code` | varchar(32) | UNIQUE(`tenant_id`,`hall_code`) | 大厅编码 | +| `name` | varchar(100) | NOT NULL | 名称 | +| `icon_attachment_id` | bigint unsigned | NOT NULL | 图标 | +| `summary` | varchar(500) | NULL | 简介 | +| `commission_rate` | int unsigned | NOT NULL DEFAULT 0 | 万分比 | +| `publish_status` | tinyint | NOT NULL, INDEX | 发布状态 | + +#### 6.7.4 `hg_task_hall_level` 任务大厅可用等级 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `id` | bigint unsigned | PK, auto_increment | 主键 | +| `tenant_id` | bigint unsigned | NOT NULL DEFAULT 0 | 租户 | +| `hall_id` | bigint unsigned | NOT NULL, INDEX | 任务大厅 | +| `level_id` | bigint unsigned | NOT NULL, INDEX | 会员等级 | +| `created_at` | datetime(3) | NOT NULL | 创建时间 | + +唯一约束:`UNIQUE (tenant_id,hall_id,level_id)`。 + +#### 6.7.5 `hg_task` 任务 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `task_no` | varchar(32) | UNIQUE(`tenant_id`,`task_no`) | 任务编号 | +| `hall_id` | bigint unsigned | NOT NULL, INDEX | 任务大厅 | +| `title` | varchar(200) | NOT NULL | 标题 | +| `description` | varchar(1000) | NULL | 描述 | +| `detail_html` | longtext | NULL | 详情 | +| `cover_attachment_id` | bigint unsigned | NOT NULL | 封面 | +| `commission_amount` | bigint | NOT NULL DEFAULT 0 | 固定佣金,分 | +| `commission_rate` | int unsigned | NOT NULL DEFAULT 0 | 比例佣金,万分比 | +| `daily_limit` | int unsigned | NOT NULL DEFAULT 0 | 每日总量,0 不限 | +| `per_member_limit` | int unsigned | NOT NULL DEFAULT 1 | 单会员限制 | +| `start_at` / `end_at` | datetime(3) | NULL, INDEX | 任务周期 | +| `publish_status` | tinyint | NOT NULL, INDEX | 发布状态 | + +#### 6.7.6 `hg_task_level` 任务可用等级 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `id` | bigint unsigned | PK, auto_increment | 主键 | +| `tenant_id` | bigint unsigned | NOT NULL DEFAULT 0 | 租户 | +| `task_id` | bigint unsigned | NOT NULL, INDEX | 任务 | +| `level_id` | bigint unsigned | NOT NULL, INDEX | 会员等级 | +| `created_at` | datetime(3) | NOT NULL | 创建时间 | + +唯一约束:`UNIQUE (tenant_id,task_id,level_id)`。 + +#### 6.7.7 `hg_task_submission` 会员任务提交 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `submission_no` | varchar(32) | UNIQUE | 提交单号 | +| `trade_order_id` | bigint unsigned | UNIQUE | 交易订单 | +| `task_id` | bigint unsigned | NOT NULL, INDEX | 任务 | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 会员 | +| `evidence_text` | varchar(2000) | NULL | 提交说明 | +| `evidence_attachment_ids` | json | NULL | 证据附件 ID | +| `submission_status` | tinyint | NOT NULL, INDEX | 1待审 2通过 3驳回 4已结算 5已冲正 | +| `commission_amount` | bigint | NOT NULL DEFAULT 0 | 审核时的佣金快照 | +| `reviewed_by` | bigint unsigned | NOT NULL DEFAULT 0 | 审核人 | +| `reviewed_at` | datetime(3) | NULL | 审核时间 | +| `review_reason` | varchar(500) | NULL | 审核意见 | +| `settled_at` | datetime(3) | NULL | 结算时间 | + +### 6.8 交易与资金域 + +#### 6.8.1 `hg_trade_order` 交易订单 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `order_no` | varchar(32) | UNIQUE | 订单号 | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 会员 | +| `task_id` / `product_id` | bigint unsigned | NOT NULL DEFAULT 0, INDEX | 任务/商品 | +| `order_type` | tinyint | NOT NULL, INDEX | 1商品 2任务 3手工商城 | +| `quantity` | int unsigned | NOT NULL DEFAULT 1 | 数量 | +| `unit_price_amount` | bigint | NOT NULL | 单价,分 | +| `total_amount` | bigint | NOT NULL | 订单金额,分 | +| `commission_amount` | bigint | NOT NULL DEFAULT 0 | 佣金,分 | +| `currency` | char(3) | NOT NULL DEFAULT 'CNY' | 币种 | +| `member_level_id` | bigint unsigned | NOT NULL | 下单时等级 | +| `rule_snapshot` | json | NOT NULL | 商品、等级、佣金规则快照 | +| `order_status` | tinyint | NOT NULL, INDEX | 匹配中/待处理/处理中/完成/超时/冻结/取消/失败 | +| `freeze_status` | tinyint | NOT NULL DEFAULT 0 | 冻结状态 | +| `matched_at` / `completed_at` / `unfrozen_at` | datetime(3) | NULL | 业务节点 | + +索引:`idx_order_member_time (tenant_id,member_id,created_at,id)`、`idx_order_status_time (tenant_id,order_status,created_at,id)`。 + +#### 6.8.2 `hg_wallet_account` 钱包账户 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `member_id` | bigint unsigned | UNIQUE(`tenant_id`,`member_id`,`currency`) | 会员 | +| `currency` | char(3) | NOT NULL DEFAULT 'CNY' | 币种 | +| `available_balance` | bigint | NOT NULL DEFAULT 0 | 可用余额,分 | +| `frozen_balance` | bigint | NOT NULL DEFAULT 0 | 冻结余额,分 | +| `total_income` | bigint | NOT NULL DEFAULT 0 | 累计收入,可重建 | +| `total_expense` | bigint | NOT NULL DEFAULT 0 | 累计支出,可重建 | +| `account_status` | tinyint | NOT NULL DEFAULT 1 | 1正常 2只收不付 3冻结 4关闭 | +| `version` | int unsigned | NOT NULL DEFAULT 0 | 乐观锁 | + +检查约束:`available_balance >= 0`、`frozen_balance >= 0`。 + +#### 6.8.3 `hg_wallet_ledger` 钱包台账 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `ledger_no` | varchar(32) | UNIQUE | 台账号 | +| `account_id` | bigint unsigned | NOT NULL, INDEX | 钱包账户 | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 会员 | +| `biz_type` | varchar(32) | NOT NULL, INDEX | recharge/withdraw/freeze/unfreeze/commission/adjust/reversal | +| `biz_no` | varchar(32) | NOT NULL | 业务单号 | +| `entry_type` | tinyint | NOT NULL | 1可用余额 2冻结余额 | +| `direction` | tinyint | NOT NULL | 1收入 2支出 | +| `amount` | bigint | NOT NULL | 变动绝对值,分 | +| `before_balance` / `after_balance` | bigint | NOT NULL | 变动前后值 | +| `currency` | char(3) | NOT NULL | 币种 | +| `reversal_of` | bigint unsigned | NOT NULL DEFAULT 0, INDEX | 被冲正台账 ID | +| `idempotency_key` | varchar(128) | NOT NULL | 幂等键 | +| `occurred_at` | datetime(3) | NOT NULL, INDEX | 业务发生时间 | +| `created_at` | datetime(3) | NOT NULL | 入库时间 | + +唯一约束:`UNIQUE (account_id,idempotency_key)`。表中不设置 `updated_at`、`deleted_at`,禁止 UPDATE 和 DELETE。 + +#### 6.8.4 `hg_payment_channel` 支付渠道 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `channel_code` | varchar(32) | UNIQUE(`tenant_id`,`channel_code`) | 渠道编码 | +| `name` | varchar(100) | NOT NULL | 渠道名称 | +| `channel_type` | tinyint | NOT NULL, INDEX | 1微信 2支付宝 3银行卡 4人工 | +| `direction` | tinyint | NOT NULL | 1充值 2提现 3双向 | +| `config_cipher` | longtext | NULL | 渠道密钥加密密文,不返回前端 | +| `min_amount` / `max_amount` | bigint | NOT NULL DEFAULT 0 | 金额限制 | +| `fee_type` | tinyint | NOT NULL DEFAULT 1 | 1固定 2比例 3混合 | +| `fee_fixed_amount` | bigint | NOT NULL DEFAULT 0 | 固定手续费 | +| `fee_rate` | int unsigned | NOT NULL DEFAULT 0 | 费率万分比 | + +#### 6.8.5 `hg_recharge_order` 充值订单 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `recharge_no` | varchar(32) | UNIQUE | 充值单号 | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 会员 | +| `channel_id` | bigint unsigned | NOT NULL, INDEX | 支付渠道 | +| `request_amount` / `paid_amount` | bigint | NOT NULL | 申请/实付金额 | +| `currency` | char(3) | NOT NULL DEFAULT 'CNY' | 币种 | +| `external_no` | varchar(128) | NULL, INDEX | 外部支付单号 | +| `voucher_attachment_ids` | json | NULL | 人工凭证 | +| `recharge_status` | tinyint | NOT NULL, INDEX | 待支付/待审/成功/驳回/过期/取消 | +| `paid_at` / `reviewed_at` / `credited_at` | datetime(3) | NULL | 关键时间 | +| `approval_request_id` | bigint unsigned | NOT NULL DEFAULT 0 | 人工审核单 | +| `ledger_id` | bigint unsigned | NOT NULL DEFAULT 0 | 入账台账 | + +索引:`idx_recharge_member_time`、`idx_recharge_status_time`;外部单号在非空时必须唯一,由 Logic 与幂等表联合保证。 + +#### 6.8.6 `hg_withdrawal_order` 提现订单 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `withdrawal_no` | varchar(32) | UNIQUE | 提现单号 | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 会员 | +| `payment_account_id` | bigint unsigned | NOT NULL | 收款账户 | +| `channel_id` | bigint unsigned | NOT NULL DEFAULT 0 | 付款渠道 | +| `request_amount` | bigint | NOT NULL | 申请金额 | +| `fee_amount` | bigint | NOT NULL DEFAULT 0 | 手续费 | +| `actual_amount` | bigint | NOT NULL | 实际到账 | +| `currency` | char(3) | NOT NULL DEFAULT 'CNY' | 币种 | +| `account_snapshot_cipher` | longtext | NOT NULL | 收款账户加密快照 | +| `account_mask` | varchar(128) | NOT NULL | 脱敏快照 | +| `risk_result` | json | NULL | 风控命中摘要 | +| `withdrawal_status` | tinyint | NOT NULL, INDEX | 待审/复核中/处理中/成功/失败/驳回/取消 | +| `approval_request_id` | bigint unsigned | NOT NULL DEFAULT 0 | 审批单 | +| `external_no` | varchar(128) | NULL, INDEX | 外部付款单号 | +| `freeze_ledger_id` / `final_ledger_id` | bigint unsigned | NOT NULL DEFAULT 0 | 冻结/完成台账 | +| `reviewed_at` / `processed_at` / `completed_at` | datetime(3) | NULL | 关键时间 | + +#### 6.8.7 `hg_wallet_adjustment_request` 账变申请 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `request_no` | varchar(32) | UNIQUE | 申请单号 | +| `member_id` / `account_id` | bigint unsigned | NOT NULL, INDEX | 会员与钱包 | +| `adjust_type` | tinyint | NOT NULL | 1奖励 2补款 3扣款 4冲正 5其他 | +| `direction` | tinyint | NOT NULL | 收入/支出 | +| `amount` | bigint | NOT NULL | 金额,分 | +| `reason_code` / `reason_text` | varchar(64)/varchar(500) | NOT NULL | 原因 | +| `evidence_attachment_ids` | json | NULL | 凭证 | +| `request_status` | tinyint | NOT NULL, INDEX | 草稿/待审/通过/驳回/已执行/失败 | +| `approval_request_id` | bigint unsigned | NOT NULL DEFAULT 0 | 审批单 | +| `ledger_id` | bigint unsigned | NOT NULL DEFAULT 0 | 执行后台账 | + +#### 6.8.8 `hg_finance_review` 财务审核记录 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `approval_request_id` | bigint unsigned | NOT NULL, INDEX | 审批单 | +| `biz_type` / `biz_id` | varchar(32)/bigint unsigned | NOT NULL, INDEX | 充值/提现/账变等 | +| `review_stage` | tinyint | NOT NULL | 1初审 2复核 3放款 | +| `reviewer_id` | bigint unsigned | NOT NULL, INDEX | 审核人 | +| `decision` | tinyint | NOT NULL | 1通过 2驳回 3退回补充 | +| `reason_code` / `reason_text` | varchar(64)/varchar(500) | NOT NULL | 意见 | +| `snapshot_json` | json | NOT NULL | 审核时数据快照 | +| `created_at` | datetime(3) | NOT NULL | 审核时间 | + +唯一约束:`UNIQUE (approval_request_id,review_stage,reviewer_id)`。申请人与审核人不同的约束由 Logic 保证。 + +#### 6.8.9 `hg_payment_callback` 支付回调记录 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `channel_id` | bigint unsigned | NOT NULL, INDEX | 渠道 | +| `callback_no` | varchar(128) | UNIQUE | 渠道回调唯一号 | +| `biz_type` / `biz_no` | varchar(32)/varchar(32) | NOT NULL, INDEX | 业务定位 | +| `headers_digest` | char(64) | NOT NULL | Header 摘要 | +| `payload_cipher` | longtext | NOT NULL | 回调加密原文 | +| `verify_status` | tinyint | NOT NULL | 验签结果 | +| `process_status` | tinyint | NOT NULL, INDEX | 待处理/成功/失败/已忽略 | +| `error_message` | varchar(1000) | NULL | 处理失败摘要 | +| `received_at` / `processed_at` | datetime(3) | NOT NULL/NULL | 回调时间 | + +该表不软删除,原文禁止直接记入通用日志。 + +#### 6.8.10 `hg_reconciliation_batch` 对账批次 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `batch_no` | varchar(32) | UNIQUE | 对账批次号 | +| `channel_id` | bigint unsigned | NOT NULL DEFAULT 0, INDEX | 渠道,0 表示内部台账对账 | +| `reconcile_date` | date | NOT NULL, INDEX | 对账日期 | +| `source_type` | tinyint | NOT NULL | 1渠道账单 2业务单/台账 3钱包/台账 | +| `file_attachment_id` | bigint unsigned | NOT NULL DEFAULT 0 | 原始账单文件 | +| `total_count` / `matched_count` / `difference_count` | bigint unsigned | NOT NULL DEFAULT 0 | 数量汇总 | +| `total_amount` / `difference_amount` | bigint | NOT NULL DEFAULT 0 | 金额汇总 | +| `batch_status` | tinyint | NOT NULL, INDEX | 1待执行 2执行中 3完成 4有差异 5失败 | +| `started_at` / `finished_at` | datetime(3) | NULL | 执行时间 | + +唯一约束:`UNIQUE (tenant_id,channel_id,reconcile_date,source_type)`。 + +#### 6.8.11 `hg_reconciliation_item` 对账差异项 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `batch_id` | bigint unsigned | NOT NULL, INDEX | 对账批次 | +| `biz_type` / `biz_no` | varchar(32)/varchar(32) | NOT NULL, INDEX | 内部业务单 | +| `external_no` | varchar(128) | NULL, INDEX | 渠道单号 | +| `difference_type` | tinyint | NOT NULL, INDEX | 1内部多 2渠道多 3金额差 4状态差 5时间差 | +| `internal_amount` / `external_amount` | bigint | NOT NULL DEFAULT 0 | 内部/外部金额 | +| `internal_status` / `external_status` | varchar(32) | NULL | 状态快照 | +| `item_status` | tinyint | NOT NULL, INDEX | 1待处理 2已确认 3已修复 4已忽略 | +| `handler_id` | bigint unsigned | NOT NULL DEFAULT 0 | 处理人 | +| `handle_reason` | varchar(500) | NULL | 处理说明 | +| `handled_at` | datetime(3) | NULL | 处理时间 | + +差异修复如涉及资金,必须发起账变或冲正审批,禁止在对账页面直接修改台账。 + +### 6.9 审批、审计与可靠性域 + +#### 6.9.1 `hg_approval_request` 审批请求 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `approval_no` | varchar(32) | UNIQUE | 审批单号 | +| `approval_type` | varchar(32) | NOT NULL, INDEX | withdrawal/config/permission/member_status/adjustment | +| `target_type` / `target_id` | varchar(32)/bigint unsigned | NOT NULL, INDEX | 目标定位 | +| `action` | varchar(64) | NOT NULL | 拟执行动作 | +| `title` | varchar(200) | NOT NULL | 审批摘要 | +| `payload_json` | json | NOT NULL | 待执行数据快照 | +| `diff_json` | json | NULL | 前后差异 | +| `risk_level` | tinyint | NOT NULL DEFAULT 1 | 1低 2中 3高 4极高 | +| `required_approvals` | int unsigned | NOT NULL DEFAULT 1 | 需要的通过数 | +| `approval_status` | tinyint | NOT NULL, INDEX | 草稿/待审/审批中/通过/驳回/取消/已执行/失败 | +| `applicant_id` | bigint unsigned | NOT NULL, INDEX | 申请人 | +| `submitted_at` / `decided_at` / `executed_at` | datetime(3) | NULL | 关键时间 | +| `expire_at` | datetime(3) | NULL | 超时时间 | + +#### 6.9.2 `hg_approval_action` 审批动作 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `approval_request_id` | bigint unsigned | NOT NULL, INDEX | 审批请求 | +| `step_no` | int unsigned | NOT NULL | 节点顺序 | +| `actor_id` | bigint unsigned | NOT NULL, INDEX | 操作人 | +| `action` | tinyint | NOT NULL | 1提交 2通过 3驳回 4退回 5取消 6执行 | +| `reason_code` / `comment` | varchar(64)/varchar(1000) | NULL | 原因和意见 | +| `snapshot_json` | json | NOT NULL | 当时快照 | +| `actor_ip_cipher` | varchar(512) | NULL | 操作 IP 密文 | +| `mfa_verified` | tinyint | NOT NULL DEFAULT 0 | 是否二次认证 | +| `created_at` | datetime(3) | NOT NULL | 动作时间 | + +追加表,不允许 UPDATE/DELETE。 + +#### 6.9.3 `hg_sensitive_access_log` 敏感数据查看日志 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `access_no` | varchar(32) | UNIQUE | 查看流水号 | +| `actor_id` | bigint unsigned | NOT NULL, INDEX | 管理员 | +| `target_type` / `target_id` | varchar(32)/bigint unsigned | NOT NULL, INDEX | 被查看对象 | +| `field_set` | json | NOT NULL | 查看的字段集,不保存字段值 | +| `purpose_code` / `purpose_text` | varchar(64)/varchar(500) | NOT NULL | 用途 | +| `authorization_no` | varchar(64) | NULL | 授权号 | +| `mfa_verified` | tinyint | NOT NULL | 二次认证 | +| `result` | tinyint | NOT NULL | 允许/拒绝/失败 | +| `trace_id` | varchar(64) | NOT NULL, INDEX | 链路 ID | +| `created_at` | datetime(3) | NOT NULL, INDEX | 查看时间 | + +#### 6.9.4 `hg_biz_audit_log` 业务审计日志 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `audit_no` | varchar(32) | UNIQUE | 审计号 | +| `actor_type` / `actor_id` | tinyint/bigint unsigned | NOT NULL, INDEX | 管理员/会员/系统 | +| `module` / `action` | varchar(64) | NOT NULL, INDEX | 模块与动作 | +| `target_type` / `target_id` | varchar(64)/bigint unsigned | NOT NULL, INDEX | 目标 | +| `before_digest` / `after_digest` | char(64) | NULL | 变更前后摘要 | +| `change_json` | json | NULL | 已脱敏差异 | +| `reason` | varchar(500) | NULL | 操作原因 | +| `approval_no` | varchar(32) | NULL, INDEX | 审批单号 | +| `trace_id` | varchar(64) | NOT NULL, INDEX | 链路 ID | +| `prev_hash` / `row_hash` | char(64) | NULL/NOT NULL | 防篡改哈希链 | +| `created_at` | datetime(3) | NOT NULL, INDEX | 时间 | + +不在 `change_json` 中存储密码、令牌、完整手机、卡号、证件号和渠道密钥。 + +#### 6.9.5 `hg_idempotency_record` 幂等记录 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `scope` | varchar(64) | NOT NULL | 业务作用域 | +| `idempotency_key` | varchar(128) | NOT NULL | 客户端/系统幂等键 | +| `request_digest` | char(64) | NOT NULL | 请求摘要 | +| `resource_type` / `resource_id` | varchar(64)/bigint unsigned | NULL | 产生的资源 | +| `process_status` | tinyint | NOT NULL | 1处理中 2成功 3失败 | +| `response_code` | int | NULL | 结果码 | +| `response_digest` | char(64) | NULL | 响应摘要 | +| `expire_at` | datetime(3) | NOT NULL, INDEX | 保留到期时间 | +| `created_at` / `updated_at` | datetime(3) | NOT NULL | 时间 | + +唯一约束:`UNIQUE (tenant_id,scope,idempotency_key)`。 + +#### 6.9.6 `hg_outbox_event` 事件出站箱 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `event_no` | varchar(32) | UNIQUE | 事件号 | +| `aggregate_type` / `aggregate_id` | varchar(64)/bigint unsigned | NOT NULL, INDEX | 聚合根 | +| `event_type` | varchar(128) | NOT NULL, INDEX | `wallet.credited` 等 | +| `payload_json` | json | NOT NULL | 已脱敏事件数据 | +| `publish_status` | tinyint | NOT NULL DEFAULT 1, INDEX | 1待发送 2已发送 3失败 4死信 | +| `retry_count` | int unsigned | NOT NULL DEFAULT 0 | 重试次数 | +| `next_retry_at` | datetime(3) | NULL, INDEX | 下次重试 | +| `last_error` | varchar(1000) | NULL | 最后错误 | +| `created_at` / `published_at` | datetime(3) | NOT NULL/NULL | 时间 | + +#### 6.9.7 `hg_dashboard_daily_stat` 看板日汇总 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `stat_date` | date | NOT NULL | 统计日期 | +| `dimension_type` / `dimension_value` | varchar(32)/varchar(64) | NOT NULL | 全局/渠道/等级等 | +| `metric_code` | varchar(64) | NOT NULL | 指标编码 | +| `metric_count` | bigint | NOT NULL DEFAULT 0 | 数量 | +| `metric_amount` | bigint | NOT NULL DEFAULT 0 | 金额,分 | +| `calculated_at` | datetime(3) | NOT NULL | 计算时间 | + +唯一约束:`UNIQUE (tenant_id,stat_date,dimension_type,dimension_value,metric_code)`。看板可展示汇总值,但财务对账必须以业务单和台账为准。 + +### 6.10 通知表 + +#### `hg_app_notification` C 端通知 + +| 字段 | 类型 | 约束/索引 | 说明 | +| --- | --- | --- | --- | +| `notification_no` | varchar(32) | UNIQUE | 通知号 | +| `member_id` | bigint unsigned | NOT NULL, INDEX | 接收会员 | +| `notification_type` | varchar(32) | NOT NULL, INDEX | system/order/finance/task/security | +| `title` | varchar(200) | NOT NULL | 标题 | +| `content` | varchar(2000) | NOT NULL | 纯文本内容 | +| `biz_type` / `biz_id` | varchar(32)/bigint unsigned | NULL, INDEX | 关联业务 | +| `read_at` | datetime(3) | NULL | 已读时间 | +| `expire_at` | datetime(3) | NULL | 过期时间 | +| `created_at` | datetime(3) | NOT NULL, INDEX | 创建时间 | + +需求中的“弹幕/模拟会员”不作为真实会员行为实现。如确需运营轮播消息,使用显式标注为“平台消息”的通知类型,不关联伪造会员 ID,不进入真实统计。 + +### 6.11 核心索引和分区策略 + +| 场景 | 策略 | +| --- | --- | +| 会员查询 | 手机精确查询走 `mobile_hash`;用户名走唯一索引;列表按 `(tenant_id,created_at,id)` 游标分页 | +| 订单查询 | 业务单号唯一索引;高频列表索引必须以 `tenant_id` 开头,紧跟状态/会员和时间 | +| 台账 | `(account_id,occurred_at,id)` 和 `(biz_type,biz_no)`;按月度作冷热分层,不盲目对在线主表分区 | +| 审计 | `(target_type,target_id,created_at)`、`(actor_id,created_at)`、`trace_id`;超过在线保留期后归档到独立存储 | +| Outbox | `(publish_status,next_retry_at,id)` 供消费者抢占;已发送数据定期归档 | +| 看板 | 当日数据可实时聚合,历史数据读日汇总表 | + +## 7. 字典与状态设计 + +本系统不建设新的字典体系,完整复用 HotGo 已有的三类字典能力。三类字典通过同一组接口和前端 Store 向页面提供选项。 + +### 7.1 HotGo 现有字典机制 + +| 类型 | 现有实现 | 适用场景 | 是否可在后台修改 | +| --- | --- | --- | --- | +| 数据库字典 | `hg_sys_dict_type` + `hg_sys_dict_data` | 驳回原因、内容分类等运营可维护选项 | 是 | +| 枚举字典 | `internal/consts` + `dict.RegisterEnums()` | 状态机、资金方向、账户类型等稳定业务枚举 | 否,随代码版本发布 | +| 方法字典 | `dict.RegisterFunc()` | 会员等级、商品分类、任务大厅、支付渠道等数据库动态选项 | 数据由对应业务模块维护 | + +后端选项查询继续使用现有 `/dictData/option/{type}` 和 `/dictData/options`。`SysDictData.Select()` 已按“内置枚举/方法字典优先,数据库字典兜底”加载,新业务不重复实现查询逻辑。 + +### 7.2 直接复用的现有字典 + +| 现有字典 key | 复用场景 | 处理 | +| --- | --- | --- | +| `sys_normal_disable` | 所有可维护业务表的 `status` | 直接使用 1正常、2停用,不再定义启停枚举 | +| `sys_switch` | 自动派单、是否推荐、默认开关 | 直接使用 1开启、2关闭 | +| `sys_user_sex` | 会员或管理员性别 | 如业务确需则直接使用 | +| `payType` | 支付方式选项 | 优先复用现有微信/支付宝/QQ 值;为原 `PayTypeOptions` 补充 `balance`、`bank_transfer`、`manual` 等本项目确需值,不另建同义字典 | +| `payStatus` | 简单支付结果展示 | 仅在“待支付/已支付”语义完全一致时复用 | +| `creditType` / `creditGroup` | 旧资金日志兼容、余额/积分分类 | 数据迁移时复用映射;新钱包台账扩展原枚举而不建第二套同义字典 | + +`orderStatus` 是 HotGo 原商品订单状态(待付款、已发货、退款等),与本项目的任务匹配订单状态语义不同,不强行复用其数值。保留原字典供框架旧模块使用,新增独立的 `tradeOrderStatus` 枚举字典,避免同一值在两种业务中含义不同。 + +### 7.3 新增枚举字典(依然使用 HotGo 注册机制) + +以下仅新增 `internal/consts` 常量和 `dict.RegisterEnums()` 注册,不新增字典表或接口: + +| 字典 key | Go 选项变量 | 核心值 | +| --- | --- | --- | +| `appMemberStatus` | `AppMemberStatusOptions` | 1正常 2受限 3禁用 4注销 | +| `appMemberRiskStatus` | `AppMemberRiskStatusOptions` | 1正常 2观察 3限制 4冻结 | +| `bizPublishStatus` | `BizPublishStatusOptions` | 1草稿 2待审 3已发布 4已下线 5已归档 | +| `tradeOrderStatus` | `TradeOrderStatusOptions` | 1匹配中 2待处理 3处理中 4完成 5超时 6冻结 7取消 8失败 | +| `rechargeStatus` | `RechargeStatusOptions` | 1待支付 2待审 3成功 4驳回 5过期 6取消 | +| `withdrawalStatus` | `WithdrawalStatusOptions` | 1待审 2复核中 3处理中 4成功 5失败 6驳回 7取消 | +| `taskSubmissionStatus` | `TaskSubmissionStatusOptions` | 1待审 2通过 3驳回 4已结算 5已冲正 | +| `approvalStatus` | `ApprovalStatusOptions` | 1草稿 2待审 3审批中 4通过 5驳回 6取消 7已执行 8执行失败 | +| `ledgerBizType` | `LedgerBizTypeOptions` | recharge/withdraw/freeze/unfreeze/commission/reward/adjust/reversal | +| `paymentAccountType` | `PaymentAccountTypeOptions` | 1银行卡 2数字钱包 3云闪付 4支付宝 5微信 | + +资金、审批和状态机值必须是代码枚举,因为 Logic 会依赖其稳定语义。它们在 HotGo 字典管理页面中仍会以“内置字典 → 枚举字典”展示,但不允许运营人员修改值。 + +### 7.4 复用数据库字典的运营选项 + +以下选项在现有“系统设置 → 字典管理”中新增类型和数据,不新建业务字典表: + +| 数据库字典 key | 用途 | 修改影响 | +| --- | --- | --- | +| `member_disable_reason` | 会员禁用/限制标准原因 | 只影响后续新申请,历史记录保留 label 快照 | +| `recharge_reject_reason` | 充值驳回原因 | 同上 | +| `withdrawal_reject_reason` | 提现驳回原因 | 同上 | +| `task_reject_reason` | 任务审核驳回原因 | 同上 | +| `wallet_adjust_reason` | 账变/奖励金原因 | 同上,且操作仍需审批 | +| `content_type` | 公告、简介、FAQ、规则、活动等内容分类 | 新类型可由运营增加,但 C 端特殊展示类型需代码支持 | + +这些字典的 `value_type`、`list_class`、`sort`、`status` 继续由 `hg_sys_dict_data` 承载。删除前必须检查是否已被业务使用;已使用选项应停用而不是删除。 + +### 7.5 方法字典 + +下列选择器来自业务表,使用 `dict.RegisterFunc()` 注册,不将业务数据复制到 `hg_sys_dict_data`: + +| 方法字典 key | 数据源 | 缓存策略 | +| --- | --- | --- | +| `memberLevelOption` | `hg_app_member_level` | 可缓存,等级更新后 `dict.ClearFuncCache()` | +| `productCategoryOption` | `hg_product_category` | 可缓存,分类更新后清理 | +| `taskHallOption` | `hg_task_hall` | 可缓存,大厅更新后清理 | +| `paymentChannelOption` | `hg_payment_channel` | 可缓存,渠道更新后清理 | + +### 7.6 后端与前端用法 + +后端在 `Filter(ctx)` 或 Logic 中使用现有方法校验字典值: + +```go +if !dict.HasOptionKey(consts.WithdrawalStatusOptions, in.WithdrawalStatus) { + return gerror.New("提现状态不合法") +} +``` + +前端在每个模块 `model.ts` 中继续使用现有 Pinia 字典 Store: + +```ts +const dict = useDictStore(); + +export function loadOptions() { + dict.loadOptions([ + 'sys_normal_disable', + 'sys_switch', + 'withdrawalStatus', + 'withdrawal_reject_reason', + 'paymentChannelOption', + ]); +} + +// 表格显示 +dict.getLabel('withdrawalStatus', row.withdrawalStatus); + +// 下拉选项 +computed(() => dict.getOptionUnRef('withdrawal_reject_reason')); +``` + +不在 Vue 模板、`model.ts` 或 API 文件中重复定义 label/value 数组。 + +### 7.7 字典与业务配置的边界 + +- **字典**:为字段提供有限选项、标签、颜色和排序,复用 HotGo 字典系统。 +- **业务配置**:交易时段、最低余额、佣金比例、额度和功能开关等会直接影响 C 端和资金,使用 `hg_site_config_version` 的审批、发布和回滚机制,不塞入字典表。 +- **业务主数据**:会员等级、商品分类、支付渠道等有独立属性和关系,使用业务表,只通过方法字典提供下拉选项。 + +## 8. 接口设计约定 + +### 8.1 路由与方法 + +- 后台路由使用 HotGo 现有 `/admin` 前缀,不再叠加 `/admin-api/v1`。 +- 列表与详情使用 GET;新增、编辑、状态变更和审批使用 POST。 +- 标准 CRUD 按 `/module/list`、`/view`、`/edit`、`/delete`、`/status`、`/export` 组织。 +- 高风险动作不复用通用 `edit`,使用显式语义接口,如 `/withdrawal/review`、`/walletAdjustment/submit`、`/approval/approve`。 + +### 8.2 写请求通用要求 + +- 资金、审核、发布类接口必须携带 `idempotencyKey`。 +- 更新可并发编辑的数据必须携带 `version`,冲突返回明确错误。 +- 审核、驳回、禁用、删除、导出和敏感查看必须携带原因。 +- 资金与权限写接口返回审计号或审批单号。 +- Controller 仅做参数透传和结果组装;所有业务校验在 `sysin.Filter()` 或 Logic 中完成。 + +### 8.3 错误码分类 + +| 分类 | 示例 | 前端处理 | +| --- | --- | --- | +| 参数错误 | 时间区间反向、金额超限 | 定位到字段 | +| 状态冲突 | 订单已处理、版本过期 | 刷新当前数据 | +| 权限拒绝 | 无敏感值查看权限 | 不展示目标数据 | +| 审批约束 | 申请人尝试审批自己 | 提示职责分离 | +| 余额不足 | 可用余额小于提现金额 | 显示当前可用余额 | +| 外部服务错误 | 支付通道超时 | 显示可重试状态与 `traceId` | + +## 9. 缓存、队列与定时任务 + +### 9.1 缓存 + +| Key | TTL | 用途 | +| --- | --- | --- | +| `site:config:{tenant}:{group}` | 5 分钟 | 当前已发布站点配置 | +| `content:published:{tenant}:{key}` | 10 分钟 | 已发布内容 | +| `member:level:{tenant}:{id}` | 10 分钟 | 当前等级规则 | +| `dashboard:summary:{tenant}:{date}` | 1 分钟 | 看板汇总 | +| `sensitive:grant:{token}` | 1–5 分钟 | 一次性敏感查看授权 | +| `lock:finance:{bizNo}` | 小于 30 秒 | 防止跨实例重复处理 | + +统一通过 `cache.Instance()` 使用,不在业务代码中直接调用 Redis。更新成功后主动删除缓存。 + +### 9.2 队列主题 + +| Topic 常量 | 事件 | 消费者 | +| --- | --- | --- | +| `QueueBizNotification` | 订单、充值、提现、任务状态变更 | C 端站内信/短信/推送 | +| `QueueDashboardStat` | 会员、订单、台账事件 | 看板汇总 | +| `QueuePaymentCallback` | 通道回调待处理 | 支付处理器 | +| `QueueReconciliation` | 入账、付款和冲正 | 对账服务 | +| `QueueAuditArchive` | 审计日志归档 | 审计归档服务 | + +### 9.3 定时任务 + +| 任务名 | 频率 | 职责 | +| --- | --- | --- | +| `publishScheduledContent` | 每分钟 | 发布到期内容、Banner 和配置 | +| `expireTradeOrders` | 每分钟 | 处理超时订单 | +| `retryOutboxEvents` | 每 10 秒 | 发送 Outbox 并执行指数退避 | +| `reconcileFinanceDaily` | 每日 | 业务单、台账、钱包和渠道对账 | +| `aggregateDashboardDaily` | 每小时/每日 | 重建看板汇总 | +| `expireSessionsAndGrants` | 每 10 分钟 | 清理会话与临时授权 | + +## 10. 安全设计 + +### 10.1 数据保护 + +- 密文字段使用 AEAD(AES-256-GCM 或等价算法),每条记录使用唯一 nonce。 +- 业务库只保存密文;主密钥位于 KMS/密钥管理系统,不存在代码、数据库和普通配置文件。 +- 密钥定期轮换,密文保存密钥版本,支持惰性重加密。 +- 通用请求日志对密码、令牌、卡号、证件号、收款码 URL 和渠道密钥执行强制红线过滤。 + +### 10.2 Web 安全 + +- 富文本在服务端执行标签、属性和 URL 协议白名单清洗。 +- 设置 CSP、`X-Content-Type-Options`、`Referrer-Policy` 和合理的 iframe `sandbox`。 +- 文件上传校验 MIME、扩展名、文件头、尺寸和大小,异步病毒扫描,不使用用户原始文件名作为存储键。 +- 后台使用 MFA;资金、完整敏感值和权限修改触发再认证。 + +### 10.3 资金安全不变式 + +1. 钱包可用余额和冻结余额不得为负数。 +2. 每笔台账的 `after_balance = before_balance ± amount`。 +3. 同一账户同一幂等键只能有一条台账。 +4. 充值成功必须且只能对应一笔入账台账。 +5. 提现成功必须存在冻结和扣减冻结两类台账。 +6. 资金纠错只能冲正,不得修改或删除历史台账。 +7. 申请人、初审人和复核人按风险级别实施职责分离。 + +## 11. 可观测性与运维 + +### 11.1 指标 + +- API 请求数、成功率、P50/P95/P99 延迟和错误码分布。 +- 待审充值/提现数、审批时长、通道成功率、回调验签失败数。 +- 钱包与台账对账差异数、重复幂等请求数、事务冲突数。 +- Outbox 积压、重试数、死信数和消费延迟。 +- 敏感数据查看次数、拒绝次数、非工作时段查看和大量导出。 + +### 11.2 告警 + +| 级别 | 场景 | +| --- | --- | +| P0 | 钱包与台账不平、重复入账、未经审批的资金变更、密钥泄漏 | +| P1 | 支付通道大面积失败、Outbox 严重积压、异常批量敏感查看 | +| P2 | 单个外部服务降级、定时发布延迟、看板汇总延迟 | + +### 11.3 备份与恢复 + +- MySQL 全量备份 + binlog 时点恢复,目标 RPO ≤ 5 分钟,RTO ≤ 30 分钟。 +- 对象存储开启版本和生命周期,资金凭证在留存期内不得物理删除。 +- 每季度执行恢复演练,演练必须验证业务单、钱包和台账的一致性。 + +## 12. 性能与容量设计 + +| 指标 | 一期目标 | +| --- | --- | +| 会员 | 10 万 | +| 交易订单 | 1,000 万级历史数据 | +| 钱包台账 | 3,000 万级历史数据 | +| 后台并发用户 | 200 | +| 后台列表 | P95 < 1s | +| 普通写操作 | P95 < 1.5s | +| 资金事务 | P95 < 2s,不包外部通道等待 | + +列表默认使用索引覆盖的时间倒序与游标分页。需跳页的后台页面可保留普通分页,但必须限制最大页深;大量数据导出使用异步任务,不在 HTTP 请求中生成。 + +## 13. 开发与数据库迁移顺序 + +1. **基础治理**:字典常量、审批、审计、幂等、Outbox、敏感字段加密工具。 +2. **会员域**:会员等级、会员、隐私资料、收款账户、状态记录和会话。 +3. **内容配置域**:站点配置版本、内容版本、Banner、客服渠道。 +4. **商品任务域**:分类、商品、任务大厅、任务、交易订单和任务提交。 +5. **资金域沙箱**:钱包、台账、充值、提现、账变、财务审核和回调。 +6. **外部通道**:支付、实名、短信和客服等外部服务。 +7. **历史迁移**:脱敏盘点、数据清洗、密文转换、台账校验和切换。 + +每批建表 SQL 合入前,使用 GoFrame CLI 重新生成 `entity`、`do`、`dao/internal` 和 `dao`;禁止手动修改生成文件。 + +## 14. 测试策略 + +### 14.1 单元测试 + +- 金额计算、手续费计算、佣金计算和边界值。 +- 所有状态机的允许与禁止转换。 +- 脱敏、加密、哈希检索和密钥版本轮换。 +- 站点配置跨字段校验,特别是时间反向和比例超限。 +- 审批人与申请人分离、审批数量与权限校验。 + +### 14.2 集成测试 + +- 重复充值回调只入账一次。 +- 并发提现不得使钱包余额为负。 +- 提现驳回、撤销和付款失败的冻结与解冻一致。 +- 任务重复审核不得重复发放佣金。 +- 已发布配置回滚后,C 端和后台读取同一版本。 +- 敏感查看令牌单次使用、过期失效并产生审计记录。 + +### 14.3 数据不变式测试 + +在 CI 和生产对账中定期执行: + +```text +钱包可用余额 = 对应可用余额台账累计值 +钱包冻结余额 = 对应冻结台账累计值 +充值成功单数 = 充值入账唯一业务单数 +提现成功单数 = 提现最终扣款唯一业务单数 +已结算任务数 = 任务佣金有效台账业务单数 +``` + +## 15. 分阶段交付 + +| 阶段 | 后端 | 前端 | 数据库 | 退出条件 | +| --- | --- | --- | --- | --- | +| S1 基础 | RBAC、审批、审计、幂等、Outbox | 统一列表/弹窗/审批组件 | 治理域表 | 安全评审通过 | +| S2 配置会员 | 配置、内容、会员、脱敏 | 对应页面 | 配置与会员域表 | C 端版本联调通过 | +| S3 商品任务 | 商品、任务、订单、审核 | 对应页面 | 商品任务域表 | 沙箱任务闭环通过 | +| S4 资金沙箱 | 钱包、台账、充值、提现、对账 | 财务页面 | 资金域表 | 并发、幂等、对账测试通过 | +| S5 生产通道 | 外部支付与风控 | 运营看板与降级 | 回调与统计 | 小流量、限额、人工对账通过 | + +## 16. 需进一步确认的设计决策 + +1. 生产是否需要真实资金、支持币种以及法定小数位。 +2. 单租户还是多租户部署;本设计预留 `tenant_id`,单租户使用 0。 +3. 提现初审、复核、放款的金额阈值和岗位矩阵。 +4. 会员实名认证所需字段、外部供应商与数据留存期。 +5. 会员关系树与层级返佣是否保留;若保留,需先完成合规评审和返佣规则详细设计。 +6. 客服使用自建工单还是第三方服务;第三方可接收的会员数据范围。 +7. 原系统历史数据的字段映射、缺失值处理、重复账号合并和台账校验方案。 + +上述决策不阻塞 S1–S3 的基础开发,但在真实充值、提现和历史资金数据迁移前必须完成确认。 diff --git a/docs/admin_reference_prd/后台管理系统需求文档.docx b/docs/admin_reference_prd/后台管理系统需求文档.docx new file mode 100644 index 0000000..c39c47c Binary files /dev/null and b/docs/admin_reference_prd/后台管理系统需求文档.docx differ diff --git a/docs/admin_reference_prd/后台管理系统需求文档.md b/docs/admin_reference_prd/后台管理系统需求文档.md new file mode 100644 index 0000000..3681174 --- /dev/null +++ b/docs/admin_reference_prd/后台管理系统需求文档.md @@ -0,0 +1,508 @@ +# 后台管理系统需求文档 + +参考管理端功能拆解、C 端映射与安全重构方案 + +目标:基于参考管理系统的实际页面、按钮、链接与弹窗,形成可直接用于产品设计、前后端开发、测试和运维的后台需求基线;保留业务闭环,不复制品牌资产和真实用户数据。 + +主要读者:产品负责人、交互设计师、前端开发、后端开发、测试、运营、客服、财务、风控、安全与审计。 + +观察日期:2026-10-07 参考环境:Web 管理后台 文档版本:V1.0 + +> **结论:** 后台覆盖门户配置、会员、交易、商品、任务、RBAC 与个人设置七大域。复刻时应优先修复敏感数据裸露、资金操作单人审批、配置无校验、接口错误和审计不足。 + +## 目录 + +- [1 文档范围与走查方法](#1-文档范围与走查方法) +- [2 产品定位与后台角色](#2-产品定位与后台角色) +- [3 C 端与后台功能映射](#3-c-端与后台功能映射) +- [4 首页数据看板](#4-首页数据看板) +- [5 门户、内容与 Banner 管理](#5-门户内容与-banner-管理) +- [6 会员管理](#6-会员管理) +- [7 会员等级与任务大厅](#7-会员等级与任务大厅) +- [8 交易管理](#8-交易管理) +- [9 商品与任务管理](#9-商品与任务管理) +- [10 管理员、角色与权限](#10-管理员角色与权限) +- [11 数据字典与个人设置](#11-数据字典与个人设置) +- [12 状态机与数据模型](#12-状态机与数据模型) +- [13 推荐接口清单](#13-推荐接口清单) +- [14 安全、隐私与审计](#14-安全隐私与审计) +- [15 非功能需求](#15-非功能需求) +- [16 端到端验收场景](#16-端到端验收场景) +- [17 研发里程碑](#17-研发里程碑) +- [18 原型图说明](#18-原型图说明) +- [19 待确认事项](#19-待确认事项) + +--- + +## 1 文档范围与走查方法 + +本需求对后台 21 个可见页面入口进行了逐项走查,并打开新增、编辑、查看、分配、审核等代表性弹窗。为了保护现网数据,没有执行最终确定、上传、删除、禁用、设为假人、审核通过或驳回、资金调整、奖励金发放、权限保存和改密。 + +### 1.1 已覆盖入口 + +| 一级模块 | 页面 | 主要控件 | +| --- | --- | --- | +| 主页 | 首页、网站设置、文本管理、Banner 设置、手工商城 | 指标卡、保存、上传、编辑、删除、发布 | +| 会员管理 | 会员列表、会员等级、任务大厅 | 搜索、新增、编辑、团队、账变、奖励金、等级配置 | +| 交易管理 | 订单、充值、提现、客服 | 筛选、自动刷新、匹配、凭证、审核、客服嵌入 | +| 商城管理 | 商品列表 | 搜索、新增、编辑、推荐、删除、富文本 | +| 任务管理 | 任务列表、会员任务审核 | 任务新增、编辑、通过、驳回 | +| 管理员 | 后台管理员、角色、权限、数据字典 | 账号、角色、权限树、配置项 | +| 设置 | 基本资料、修改密码 | 资料修改、密码修改 | + +### 1.2 操作边界 + +- 只验证页面、按钮和弹窗是否可达,未提交会改变会员、资金、权限或配置的数据。 +- 删除、禁用、设为假人、审核、账变、奖励金、改密等高风险入口仅记录需求,不执行最终动作。 +- 原型图基于实测界面脱敏重绘,手机号、姓名、IP、银行卡、订单号均为掩码或虚构值。 +- 接口名称以推荐实现命名;现网真实接口需在开发前通过代码或服务端文档二次核验。 + +### 1.3 证据等级 + +| 等级 | 定义 | 使用方式 | +| --- | --- | --- | +| A 实测 | 页面、按钮、表格或弹窗在走查中直接可见 | 可作为复刻基线 | +| B 推导 | 由相邻页面、C 端闭环或字段关系推导 | 开发前需确认 | +| C 建议 | 为安全、合规、可用性提出的重构要求 | 作为新系统强制规范 | + +## 2 产品定位与后台角色 + +该后台用于运营一个移动端会员任务与账户平台。C 端会员进行注册、余额储入、领取订单、任务提交、佣金结算、提现和客服咨询;后台完成内容配置、会员治理、商品与任务配置、交易审核、财务对账和权限审计。 + +| 角色 | 职责 | 允许范围 | 禁止范围 | +| --- | --- | --- | --- | +| 超级管理员 | 系统与权限治理 | 账号、角色、权限、系统参数 | 不得绕过双人复核直接放款 | +| 运营 | 内容、Banner、商品、任务、等级 | 草稿、预览、发布申请 | 不得查看完整银行卡或改余额 | +| 客服 | 会员与订单查询、工单 | 脱敏资料、只读订单、备注 | 不得资金审核或导出全量用户 | +| 财务 | 充值、提现、对账 | 凭证、审核、支付状态 | 不得修改会员身份与权限 | +| 风控 | 异常行为与冻结复核 | 设备、IP 风险、冻结申请 | 不得单人完成冻结与解冻 | +| 审计 | 只读复核与导出 | 日志、审批链、差异报表 | 不得修改业务数据 | + +### 2.1 信息架构 + +采用“一级业务域 + 二级页面 + 多标签工作区”的桌面后台结构。一级菜单固定在左侧,二级页面以标签页打开;标签支持关闭、刷新和回到首页。任何页面刷新后,应保持当前路由但不保留未提交敏感表单。 + +### 2.2 全局交互规范 + +- 列表页统一:查询区、主操作区、表格、固定操作列、分页;查询条件可重置并写入 URL。 +- 弹窗统一:标题、关闭、表单、确定、取消;确定前做前后端双重校验,提交后禁止重复点击。 +- 危险操作使用红色按钮并要求对象、影响、理由和二次确认;资金与权限操作额外要求复核人。 +- 敏感值默认脱敏;查看完整值需单独权限、再次确认、限定时长,并记录操作者、目标、时间和用途。 +- 加载超过 2 秒展示骨架与取消,接口失败展示错误码、重试和上报入口,不长期停留空白页。 +- 操作成功后提供结果提示与审计编号;失败不得只显示“error”。 + +## 3 C 端与后台功能映射 + +后台不是独立工具,其配置和操作必须能解释 C 端每个入口、状态和金额。以下矩阵为联调的单一事实源。 + +| C 端能力 | 后台来源 | 后台操作 | 一致性要求 | +| --- | --- | --- | --- | +| 首页横幅与快捷入口 | Banner、网站设置 | 排序、有效期、跳转、开关 | 发布后按版本生效 | +| 平台简介/规则/公告 | 文本管理 | 富文本编辑、预览、发布 | CSP 与 HTML 白名单 | +| 手工商城 | 手工商城 | 商品增删改、图片、价格、时间 | C 端仅显示已上架 | +| 会员等级 | 会员等级 | 门槛、赠送、次数、佣金、提现次数 | 计算与展示同源 | +| 领取中心 | 网站设置、商品、订单 | 营业时间、最低余额、匹配规则 | 服务端状态机判定 | +| 订单记录 | 订单列表 | 查询、冻结、匹配、异常处理 | 业务单号和台账可追溯 | +| 储入 | 充值管理 | 凭证、审核、入账、驳回 | 幂等入账与自动对账 | +| 取出 | 提现管理、银行卡信息 | 收款资料、审核、通道、结果 | 金额、手续费、到账同源 | +| 任务大厅与任务 | 任务大厅、任务列表、会员任务审核 | 配置、分发、审核 | 等级、佣金、状态一致 | +| 个人资料 | 会员列表 | 资料、团队、状态、风控 | 敏感字段默认脱敏 | +| 通知与弹幕 | 文本、会员弹幕 | 发布、定向、撤回、查看记录 | 真实性标识和审计 | +| 客服 | 客服管理 | 客服入口、工作时间、会话映射 | 失败有备用通道 | + +![C 端与后台业务闭环原型](assets/prototype_06_flow.png) + +图 1 C 端与后台业务闭环(脱敏重绘原型) + +## 4 首页数据看板 + +首页展示用户、商品、订单、充值、提现、佣金、利息宝转入、下级返佣与账户余额等汇总卡,并展示微信、支付宝和银行卡渠道统计。新系统应把口径、时间区间、刷新时间和数据延迟显式化。 + +| 指标 | 定义 | 筛选维度 | 权限 | +| --- | --- | --- | --- | +| 用户总量/新增 | 去重有效会员数 | 今日、昨日、自定义日期 | 运营只读 | +| 充值用户 | 至少一笔成功充值的会员数 | 时间、渠道、等级 | 财务/运营 | +| 订单量与金额 | 按订单状态和完成时间统计 | 状态、商品、等级 | 运营/财务 | +| 充值/提现 | 成功与待处理金额分开 | 渠道、状态、时间 | 财务 | +| 佣金/返佣 | 任务佣金与层级返佣分开 | 类型、等级、时间 | 财务/审计 | +| 账户余额 | 钱包可用与冻结余额总和 | 币种、账户状态 | 财务/审计 | + +- 卡片可点击进入对应明细页,并继承时间筛选;无权限用户只能看到隐藏金额或不可点击状态。 +- 页面顶部展示“数据截至时间”和刷新按钮;自动刷新不得造成重复请求风暴。 + +![后台首页看板脱敏原型](assets/prototype_01_dashboard.png) + +图 2 后台首页看板(按实测界面脱敏重绘) + +## 5 门户、内容与 Banner 管理 + +### 5.1 网站设置 + +| 配置组 | 字段 | 要求 | +| --- | --- | --- | +| 品牌 | Logo、站点名称、隐私政策 URL | Logo 可预览;URL 仅允许 HTTPS 白名单 | +| 交易 | 交易余额、等待时间、每日次数、奖励次数 | 数值范围校验;修改按版本发布 | +| 佣金 | 0–5 级普通佣金 | Decimal;总和上限;法务开关 | +| 时间 | 提现、充值、交易、签到时间 | 支持多区间;禁止结束早于开始 | +| 门槛 | 最低充值、自动派单时间与金额 | 最小值≤最大值;币种与单位清晰 | +| 开关 | 免费领取、手工商城、首页任务、签到、商品模式、验证码 | 每项写明 C 端影响和降级文案 | + +实测问题:交易等待时间输入框提示语与字段无关;签到时段出现“18:00–17:00”反向区间。新系统保存前必须做语义校验,并支持草稿、预览、发布、回滚。 + +### 5.2 文本管理 + +内容类型包括平台公告、平台简介、订单规则、代理合作、FAQ、指南、利息宝规则、首页滚动、订单提示、活动、首页弹窗和公司资质。编辑器需支持标题、正文、图片、链接、预览、定时发布、版本和回滚。 + +- 富文本使用标签/属性白名单;禁止 script、事件属性、javascript: URL 和未知 iframe。 +- 现网编辑弹窗标题错误显示“编辑商品”,新系统标题必须与内容类型一致。 + +### 5.3 Banner 设置 + +支持多张首页 Banner 的上传、删除、排序、上下线、有效期、跳转类型与替代文本。上传前预览并校验尺寸、比例、MIME、文件大小和恶意内容。删除需要二次确认,已发布素材优先下线而非物理删除。 + +### 5.4 手工商城 + +列表字段为图片、名称、价格、时间与操作;新增/编辑字段为产品名称、价格、图片与展示时间。现网新增弹窗曾长时间停留 loading,需增加 8 秒超时、重试和错误上报。 + +## 6 会员管理 + +### 6.1 会员列表 + +查询条件:用户名、手机号、注册时间、状态、自动派单。列表字段:ID、账号、绑卡姓名、用户名、账户余额、冻结金额、上级用户、邀请码、注册时间、最后登录 IP、IP 属地、自动抢单、状态、当日交易次数。 + +| 操作 | 页面或弹窗 | 关键校验 | 审计要求 | +| --- | --- | --- | --- | +| 添加/编辑 | 会员表单 | 手机号唯一;余额不可由普通编辑修改;密码强度 | 记录前后值,密码永不回显 | +| 银行卡信息 | 开户人、银行、电话、卡号、数字钱包、云闪付、收款码 | 必要性与格式;图片扫描 | 完整值查看留痕 | +| 地址信息 | 姓名、电话、地区、详细地址 | 最小化收集;字段脱敏 | 查看和修改留痕 | +| 查看团队 | 当前用户、层级、人数、余额、佣金、充值、提现 | 仅展示授权层级 | 导出单独授权 | +| 账变 | 余额调整或流水 | 金额、类型、理由、业务单号 | 双人复核;不可直接改余额 | +| 弹幕 | 会员定向或模拟内容 | 标注来源、有效期、撤回 | 禁止伪造真实用户行为 | +| 奖励金 | 奖励金额与原因 | 非负、额度、幂等 | 财务复核与凭证 | +| 设为假人 | 账号标记 | 不得混入真实统计或 C 端荣誉榜 | 仅测试环境默认启用 | +| 禁用 | 账号状态 | 理由、范围、时长、申诉 | 双人复核;通知用户 | +| 删除 | 软删除/注销 | 检查资金与订单;不可物理删台账 | 高风险二次确认 | + +> **P0 风险:** 现网页面默认显示完整手机号、姓名与登录 IP。新系统必须默认脱敏,按角色和目的临时授权查看;列表、导出、日志、埋点和截图统一执行同一脱敏规则。 + +![会员列表脱敏原型](assets/prototype_02_member_list.png) + +图 3 会员列表脱敏原型 + +![会员编辑弹窗原型](assets/prototype_03_member_modal.png) + +图 4 添加会员弹窗原型 + +## 7 会员等级与任务大厅 + +### 7.1 会员等级 + +列表字段为等级名称、升级价格、赠送金额、接单次数、佣金比例和提现次数;支持编辑与受控删除。等级配置直接影响 C 端充值、领取和提现,必须使用版本化发布,不得修改后立即影响进行中的订单。 + +| 规则 | 校验 | 生效策略 | +| --- | --- | --- | +| 升级价格/赠送金额 | ≥0;货币精度;赠送比例上限 | 新会员或下个结算周期 | +| 接单次数 | 整数;按日重置时区明确 | 发布后次日生效 | +| 佣金比例 | 0–1 Decimal;展示百分比 | 锁定到任务订单快照 | +| 提现次数 | 整数;与提现时间窗一致 | 发布后次日生效 | +| 删除等级 | 无有效会员与在途订单方可删除 | 优先停用并保留历史 | + +### 7.2 任务大厅 + +页面字段为名称、图标、比例、简介、创建时间;新增弹窗还包含会员等级。实测列表接口直接显示“数据接口请求异常:error”,但新增弹窗可打开。新系统需要标准错误码、可重试状态、traceId 与空数据区分。 + +- 任务大厅条目必须绑定可用会员等级、比例单位、图标和 C 端跳转。 +- 停用条目不影响已领取任务;删除仅允许无历史引用的数据,建议统一软删除。 + +## 8 交易管理 + +### 8.1 订单列表 + +查询条件:订单号、手机号、用户名、日期;支持开启/关闭自动刷新。列表含绑卡姓名、用户名、自动派单、余额、手机号、当日交易次数、商品、数量、金额、佣金、下单时间、解冻时间、消息、订单状态、冻结状态和操作。 + +| 状态 | 后台操作 | 约束 | +| --- | --- | --- | +| 匹配中 | 查看进度、取消匹配 | 同一会员仅一笔匹配;需幂等 | +| 待处理 | 查看详情、发送提醒 | 不可手动完成;服务端倒计时 | +| 冻结 | 查看风险、发起解冻复核 | 冻结理由、时长、申诉路径 | +| 已完成 | 查看台账 | 金额、佣金、解冻三方一致 | +| 异常/失败 | 重新匹配、人工处理申请 | 人工改状态必须双人审批 | + +### 8.2 充值管理 + +顶部展示全部、用户已支付、用户未支付、手动审核、手动驳回等汇总金额。列表字段:订单号、真实姓名、手机号、余额、交易金额、打款凭证、添加时间、支付方式、类型、处理时间、备注和操作。 + +- 审核通过前校验凭证、订单金额、收款主体、重复订单和账户状态;入账使用幂等键。 +- 驳回必须选择标准原因并可补充说明;通知 C 端且允许补充材料。 + +### 8.3 提现管理 + +查询条件:订单号、用户名、日期。列表含用户余额、提现金额、手续费、实际到账、银行名称、开户名称、银行卡号、云闪付、数字钱包、支付宝/微信收款码、发起与处理时间、操作。 + +> **P0 风险:** 现网直接显示完整银行卡号与多个钱包账号。新系统列表只显示掩码,完整值不进入普通 DOM;付款处理时通过一次性授权查看,超时自动隐藏。 + +![提现审核脱敏原型](assets/prototype_04_withdrawal.png) + +图 5 提现审核脱敏原型 + +### 8.4 客服管理 + +实测页面在加载后保持空白。新系统至少提供客服账号、名称、工作时间、在线状态、跳转 URL、排序、启停和会话映射;第三方 iframe 失败时显示备用联系方式和错误编号。 + +## 9 商品与任务管理 + +### 9.1 商品列表 + +查询条件:商品名称、支付类型。列表:ID、图片、名称、商品价格、店铺名称、创建时间、是否推荐、操作。新增/编辑字段:商品名称、分类、店铺名称、商品单价、展示图片、商品详情富文本。 + +- 推荐/不推荐为明确状态切换,提交前展示影响范围;列表筛选和 C 端排序一致。 +- 价格采用 Decimal;图片与富文本执行内容安全校验;商品被订单引用后只允许停用。 + +### 9.2 任务列表 + +按会员类型筛选;字段为任务编号、封面、标题、描述、等级、佣金、创建时间。新增弹窗含标题、描述、封面、等级、佣金和详情。实测新增任务弹窗标题误用“添加大厅任务”,需修正。 + +### 9.3 会员任务审核 + +按手机号、用户名查询;列表含记录 ID、用户名、手机、任务标题、等级、佣金、成功率、状态、备注、创建时间和操作。待审核记录支持通过与驳回。 + +| 动作 | 前置条件 | 提交内容 | 结果 | +| --- | --- | --- | --- | +| 通过 | 任务存在、会员有效、未重复审核 | 审核意见、证据摘要、幂等键 | 发放佣金或进入结算队列 | +| 驳回 | 记录为待审核 | 标准原因、补充说明、是否允许重提 | 通知会员并保留证据 | +| 撤回审核 | 仅异常且未结算 | 变更单与复核人 | 冲正而非修改历史记录 | + +## 10 管理员、角色与权限 + +### 10.1 后台管理员 + +查询条件为登录名、手机、邮箱、角色;列表含姓名、登录名、手机号、邮箱、角色、更新时间和创建时间。新增字段为姓名、用户名、密码、手机、邮箱和多选角色。 + +- 密码强度至少 12 位,禁止默认密码,首次登录强制改密;密码永不回显。 +- 禁用优先于删除;删除账号时保留历史审计主体,不得影响日志可追溯性。 + +### 10.2 角色管理 + +角色列表支持新增、编辑、删除和分配权限。分配弹窗为可展开/折叠的权限树。保存权限属于高风险操作,必须展示新增与移除差异,并由另一名管理员复核。 + +### 10.3 权限管理 + +权限树字段为 ID、名称、权限标识、父级 ID 和操作;支持添加顶级权限、添加子级、编辑、删除、展开/折叠。权限标识唯一且不可随意改名;被角色引用时禁止删除。 + +![RBAC 角色权限原型](assets/prototype_05_rbac.png) + +图 6 建议 RBAC 权限矩阵 + +## 11 数据字典与个人设置 + +### 11.1 数据字典 + +列表字段为 ID、名称、值、创建时间;支持新增、编辑、删除。现网同时存在英文 key、中文名称和缺失名称的条目,并与网站设置重复维护。建议将字典拆为“系统常量”和“业务配置”,以结构化 Schema 管理。 + +| 字段 | 要求 | +| --- | --- | +| key | 全局唯一;小写或大写规范统一;发布后不可改名 | +| value | 按 Schema 校验类型、范围、枚举、时间段和单位 | +| description | 必填,说明业务含义、C 端影响和默认值 | +| scope | 环境、租户、渠道、会员等级等作用域 | +| version | 草稿、已发布、已回滚;记录生效时间 | +| secret | 敏感配置不得明文显示,使用密钥管理系统 | + +### 11.2 基本资料 + +字段为用户名(不可修改)、名称、手机、邮箱,提交按钮为“确认修改”。修改手机或邮箱需验证当前身份和新联系方式;成功后记录审计并通知原联系方式。 + +### 11.3 修改密码 + +字段为当前密码、新密码、确认新密码,现网提示 6–16 个字符。新系统建议至少 12 位并支持密码管理器;最终提交必须由用户本人完成,成功后撤销其他会话并发送安全通知。 + +## 12 状态机与数据模型 + +### 12.1 核心状态机 + +| 实体 | 状态 | 允许转换 | +| --- | --- | --- | +| 会员 | 正常、受限、禁用、注销 | 正常→受限/禁用;复核后恢复;注销不可逆 | +| 订单 | 匹配中、待处理、处理中、完成、超时、冻结、取消、失败 | 仅服务端状态机转换 | +| 充值 | 待支付、待审核、成功、驳回、过期、取消 | 成功后只允许冲正 | +| 提现 | 待审核、复核中、处理中、成功、失败、驳回、取消 | 付款前可取消;成功后只允许冲正 | +| 任务审核 | 待审核、通过、驳回、已结算、已冲正 | 结算后不可回到待审核 | +| 内容 | 草稿、待审、已发布、已下线、已归档 | 发布需审批;可回滚版本 | +| 配置 | 草稿、待发布、已发布、已回滚 | 发布后生成不可变版本 | + +### 12.2 核心实体 + +| 实体 | 关键字段 | 约束 | +| --- | --- | --- | +| AdminUser | id, loginName, roles, status, mfa, lastLogin | 管理员身份与会话 | +| Role/Permission | roleId, permissionKey, scope | 最小权限与数据范围 | +| Member | id, mobileEnc, nameEnc, levelId, status | 敏感字段加密、索引分离 | +| Wallet | available, frozen, currency, version | 只允许台账事务修改 | +| LedgerEntry | bizNo, type, amount, before, after, reversalOf | 不可变、幂等、可冲正 | +| Order | orderNo, memberId, productSnapshot, amount, commission, status | 保存规则快照 | +| Deposit/Withdrawal | bizNo, amount, fee, channel, status, reviewer | 审批链与支付回执 | +| Content/Banner | type, body, version, publishAt, operator | 预览与版本化发布 | +| ConfigVersion | key, value, schema, effectiveAt | 语义校验与回滚 | +| AuditLog | actor, action, target, before, after, reason, traceId | 防篡改与长期留存 | + +## 13 推荐接口清单 + +接口为新系统建议设计,统一前缀 /admin-api/v1。响应包含 data、code、message、traceId;所有写接口必须携带 Idempotency-Key,资金与权限写操作同时携带审批单号。 + +| 方法 | 路径 | 用途 | 关键参数 | +| --- | --- | --- | --- | +| GET | /dashboard/summary | 首页指标 | timeRange, dimensions | +| GET/PUT | /site-settings | 读取/保存站点配置草稿 | version, settings | +| POST | /site-settings/publish | 发布配置 | draftVersion, reason | +| GET/POST | /contents | 内容列表/新建 | type, status, body | +| POST | /contents/{id}/publish | 内容发布 | publishAt, approver | +| GET/POST | /banners | Banner 查询/新建 | imageAssetId, sort, target | +| GET/POST | /members | 会员查询/新建 | filters, profile | +| GET/PATCH | /members/{id} | 会员详情/编辑 | editableProfile, version | +| POST | /members/{id}/adjustment-requests | 账变申请 | type, amount, reason, evidence | +| POST | /members/{id}/status-requests | 禁用/解禁申请 | targetStatus, reason, duration | +| GET/POST | /member-levels | 等级查询/草稿 | rules, effectiveAt | +| GET/POST | /products | 商品查询/新建 | category, price, assets, detail | +| GET/POST | /tasks | 任务查询/新建 | levelIds, commission, detail | +| GET | /orders | 订单列表 | orderNo, member, status, timeRange | +| POST | /orders/{id}/exception-requests | 异常处理申请 | action, reason | +| GET | /deposits | 充值列表 | status, channel, timeRange | +| POST | /deposits/{id}/review | 充值审核 | decision, reason, version | +| GET | /withdrawals | 提现列表 | status, member, timeRange | +| POST | /withdrawals/{id}/review | 提现审核 | decision, reason, version | +| GET | /withdrawals/{id}/sensitive-view | 一次性查看收款信息 | purpose, challengeToken | +| GET/POST | /task-submissions | 任务审核列表/决定 | decision, reason | +| GET/POST | /admin-users | 管理员查询/新建 | profile, roleIds | +| GET/POST | /roles | 角色查询/新建 | name, scope | +| POST | /roles/{id}/permission-change-requests | 权限变更申请 | add, remove, reason | +| GET | /audit-logs | 审计查询 | actor, action, target, timeRange | + +## 14 安全、隐私与审计 + +| 风险 | 现网表现 | 新系统要求 | 优先级 | +| --- | --- | --- | --- | +| 个人信息裸露 | 完整手机号、姓名、IP | 默认脱敏;临时授权;全链路审计 | P0 | +| 金融信息裸露 | 完整银行卡与钱包账号 | 应用层加密;非 DOM 展示;一次性查看 | P0 | +| 资金单人操作 | 通过/驳回、账变、奖励入口直接可达 | 职责分离、双人复核、额度和幂等 | P0 | +| 伪造行为 | 设为假人、弹幕入口 | 测试数据隔离;禁止混入真实榜单与统计 | P0 | +| 富文本 XSS | 商品与内容支持富文本 | 白名单、CSP、上传扫描、预览隔离 | P0 | +| 配置误发布 | 时间反向、字段提示错误 | Schema 校验、预览、审批、回滚 | P0 | +| 接口不可用 | 任务大厅 error、客服空白 | 错误码、重试、熔断、监控和降级 | P1 | +| 权限漂移 | 多角色与权限树直接保存 | 差异预览、审批、定期复核 | P0 | + +### 14.1 审计日志 + +- 记录登录、查看敏感信息、导出、查询大范围数据、资金审核、账变、奖励、冻结、配置发布、权限变更和改密。 +- 字段至少包含操作者、角色、MFA、目标、动作、前值、后值、理由、审批人、IP、设备、traceId、时间和结果。 +- 日志写入独立存储并防篡改;业务管理员无删除权限;按监管与公司政策设定留存期。 +- 导出文件水印、加密、限时下载;下载与转发均可追踪。 + +### 14.2 登录与会话 + +- 管理员必须启用 MFA;高风险动作触发再次认证。 +- 闲置 15–30 分钟自动锁定;会话撤销、设备列表和异地登录提醒。 +- 连续失败触发限流与告警;不得依赖前端验证码作为唯一防护。 +- 服务端密码使用 Argon2id 或 bcrypt;任何日志、监控和前端状态都不得保存密码。 + +## 15 非功能需求 + +| 类别 | 目标 | 验收指标 | +| --- | --- | --- | +| 性能 | 常用列表与详情快速可用 | 列表 P95<1s;写操作 P95<1.5s;首屏<2.5s | +| 可用性 | 关键交易全年可用 | 资金审核 99.95%;其余 99.9% | +| 容量 | 支持 10 万会员与百万级订单 | 游标分页、索引与冷热分层 | +| 并发 | 自动刷新与多运营并发安全 | 乐观锁、版本号、冲突提示 | +| 可观测 | 定位每次请求与资金变更 | traceId、指标、日志、告警、审计关联 | +| 兼容 | 主流桌面浏览器 | Chrome/Edge 最近两个稳定版;1366×768 可用 | +| 可访问 | 键盘与读屏可操作 | 焦点、标签、颜色对比、44px 点击区 | +| 灾备 | 配置与交易可恢复 | RPO≤5 分钟,RTO≤30 分钟;季度演练 | + +### 15.1 列表与弹窗通用验收 + +- 查询条件、分页、排序、刷新后可恢复;空数据、加载中、失败、无权限均有独立状态。 +- 横向宽表固定关键列和操作列;敏感字段不因横向滚动或 tooltip 意外暴露。 +- 弹窗关闭与取消不产生写操作;表单脏数据关闭前提示;成功后只刷新受影响区域。 +- 重复点击、网络重试和浏览器回退不会重复创建、重复审核或重复入账。 +- 所有时间显示时区,金额显示币种和精度,百分比与小数含义统一。 + +## 16 端到端验收场景 + +| 编号 | 场景与验收路径 | +| --- | --- | +| A01 内容发布 | 运营新建公告→预览→提交审核→审批→定时发布→C 端显示相同版本→回滚 | +| A02 会员查询 | 客服按掩码手机号查询→查看脱敏资料→申请查看完整值→审计留痕→超时隐藏 | +| A03 任务闭环 | 配置任务→C 端领取→生成订单→提交→后台审核→佣金入账→台账核对 | +| A04 充值审核 | C 端提交→后台待审核→重复回调→单次入账→通知→对账一致 | +| A05 提现复核 | C 端申请→风控命中→财务初审→复核→通道付款→状态与到账金额一致 | +| A06 禁用会员 | 客服发起→填写理由和时长→风控复核→会员受限→通知与申诉→解禁 | +| A07 账变 | 运营申请奖励→财务复核→台账入账→重复提交被幂等拦截→审计可追溯 | +| A08 权限变更 | 管理员申请新增提现审核权限→展示差异→复核→即时生效→历史会话重新鉴权 | +| A09 配置错误 | 输入反向时间/非法比例→前后端阻止→错误定位到字段→不生成草稿版本 | +| A10 服务降级 | 任务大厅 API 失败/客服 iframe 失败→清晰错误→重试/备用入口→告警产生 | + +### 16.1 发布门槛 + +- P0 风险全部关闭;资金、权限、隐私与上传通过专项安全测试。 +- C 端和后台使用同一配置版本、状态枚举、金额口径和错误码。 +- 关键写操作完成幂等、并发、回滚、重试、超时与断网测试。 +- 审计日志可以从一次用户投诉还原完整操作者、审批链和资金流水。 +- 备份恢复、支付对账、权限复核和事故响应完成演练。 + +## 17 研发里程碑 + +| 阶段 | 范围 | 退出条件 | +| --- | --- | --- | +| M0 设计基线 | 信息架构、组件、状态机、RBAC、字段字典 | 产品/设计/技术/安全评审通过 | +| M1 基础后台 | 登录、管理员、角色、权限、审计、内容、Banner | MFA、审计、发布流可用 | +| M2 会员与配置 | 会员、等级、团队、配置中心 | 脱敏、临时授权、版本发布完成 | +| M3 商品与任务 | 商品、任务、任务大厅、审核 | C 端联调与佣金快照通过 | +| M4 交易与资金 | 订单、充值、提现、对账、复核 | 支付沙箱、幂等、双人复核通过 | +| M5 运营与稳定性 | 看板、客服、监控、灾备、性能 | SLO、告警、恢复演练通过 | + +### 17.1 建议开发顺序 + +1. 先建设统一认证、RBAC、审计、脱敏和配置发布,再开发具体业务页。 +1. 会员、商品、任务可先使用模拟资金;充值和提现必须最后接真实通道。 +1. 资金域上线前实行沙箱、小流量、额度限制和人工对账。 +1. 上线后第一周每日权限复核、账务核对和错误率复盘。 + +## 18 原型图说明 + +文档中的界面图按实测后台的布局、菜单、表格、弹窗和业务字段脱敏重绘,可直接用于讨论信息层级和交互。它们不是视觉稿:品牌、颜色、图标和真实数据应在 UI 设计阶段重新建立。 + +### 18.1 首页看板(脱敏重绘) + +![后台首页看板脱敏原型](assets/prototype_01_dashboard.png) + +### 18.2 会员列表(脱敏重绘) + +![会员列表脱敏原型](assets/prototype_02_member_list.png) + +### 18.3 会员弹窗(脱敏重绘) + +![会员编辑弹窗原型](assets/prototype_03_member_modal.png) + +### 18.4 提现审核(脱敏重绘) + +![提现审核脱敏原型](assets/prototype_04_withdrawal.png) + +### 18.5 角色权限(脱敏重绘) + +![RBAC 角色权限原型](assets/prototype_05_rbac.png) + +### 18.6 业务闭环(脱敏重绘) + +![C 端与后台业务闭环原型](assets/prototype_06_flow.png) + +## 19 待确认事项 + +- 目标系统是否处理真实资金,适用司法辖区、支付牌照与实名要求是什么。 +- “设为假人、弹幕、奖励金、层级返佣”是否保留;若保留,业务合法性和真实性如何证明。 +- 充值、提现、账变和会员禁用的初审/复核角色与金额阈值。 +- C 端现有接口能否修改,还是需要后台兼容旧字段和状态枚举。 +- 客服使用自建工单还是第三方服务,允许向第三方传输哪些数据。 +- 运营内容是否需要多语言、定时发布、区域差异与灰度发布。 +- 历史用户、订单、银行卡和台账的迁移范围、清洗规则和保留年限。 +- 生产环境数据导出、敏感值查看和紧急权限的审批人。 + +在上述事项确认前,可以完成认证、RBAC、审计、内容、会员脱敏查询、商品任务配置和沙箱交易;不得接入真实收款、放款或自动资金调整。 diff --git a/docs/admin_reference_prd/第12章状态机与数据模型补强设计.md b/docs/admin_reference_prd/第12章状态机与数据模型补强设计.md new file mode 100644 index 0000000..0b0901e --- /dev/null +++ b/docs/admin_reference_prd/第12章状态机与数据模型补强设计.md @@ -0,0 +1,484 @@ +# 第 12 章状态机与数据模型补强设计 + +> 文档版本:V1.0 +> 编制日期:2026-10-08 +> 依据:[后台管理系统需求文档](./后台管理系统需求文档.md)第 12 章 +> 关联设计:[后台管理系统概要设计](./后台管理系统概要设计.md) +> 适用项目:`olons_admin` + +## 1. 文档目标 + +本文档用于补齐需求文档第 12 章中尚未闭环的状态机、资金冲正、内容归档与历史版本回滚能力,作为数据库迁移、后端接口、前端操作、权限配置和测试验收的实施依据。 + +本次设计遵循以下原则: + +1. 复用现有 HotGo 分层、字典、审批、审计、权限和多租户能力。 +2. 不修改已经投入使用的状态编号,只追加新状态或转换规则。 +3. 客户端只提交业务意图,最终状态由服务端状态机决定。 +4. 已生效资金不得修改或删除历史台账,只能生成反向流水冲正。 +5. 历史内容和配置不得原地覆盖,回滚必须生成新的草稿版本。 +6. 所有高风险执行都必须重新检查业务状态、审批状态和当前数据版本。 + +## 2. 实施范围 + +### 2.1 本期范围 + +- 会员状态合法转换和注销不可逆。 +- 充值入账后的审批冲正。 +- 任务佣金结算后的审批冲正。 +- 文本内容归档和历史版本生成回滚草稿。 +- 状态转换、重复执行、并发执行和资金一致性测试。 +- 新增操作对应的菜单权限、业务审计和幂等保护。 + +### 2.2 不在本期范围 + +- 管理员 MFA 的真实认证流程。 +- 支付渠道退款接口和外部清算实现。 +- 新建独立的状态机管理页面。 +- 修改 HotGo 原有管理员、角色、字典和个人设置模块。 +- 直接修改 GoFrame CLI 生成的 `entity`、`do`、`dao/internal` 文件。 + +MFA 数据字段继续保留,但真实验证码、再认证凭证和会话升级机制后续单独设计开发。 + +## 3. 现状评估 + +| 领域 | 已有能力 | 本次缺口 | +| --- | --- | --- | +| 会员 | 正常、禁用、注销;风险状态;双人审批;状态日志 | 未限制全部非法转换,注销后仍可能被旧审批恢复 | +| 钱包 | 可用余额、冻结余额、乐观锁、不可变台账、幂等键、`reversal_of` | 充值和任务佣金尚未提供完整冲正入口 | +| 充值 | 待支付、待审核、成功、驳回、过期、取消;审核入账 | 成功后无冲正状态、审批和执行流程 | +| 任务审核 | 待审核、通过、驳回、已结算、已冲正;佣金台账字段 | 已冲正仅有字典和字段,缺少业务实现 | +| 内容 | 草稿、待审、已发布、已下线、已归档;内容版本 | 缺少归档和历史版本生成回滚草稿接口 | +| 配置 | 草稿、审批、发布、版本、回滚草稿 | 已满足本章要求,本期不重做 | +| 审计 | Trace ID、前后摘要、哈希链、审批单号 | 新增流程需要补充结构化差异和冲正关联信息 | + +## 4. 状态机设计 + +### 4.1 通用规则 + +每个状态变更必须依次执行: + +1. 查询目标记录并加行锁。 +2. 校验记录存在且属于当前租户。 +3. 校验当前状态与申请时的 `fromStatus` 一致。 +4. 校验 `fromStatus → toStatus` 在允许转换表中。 +5. 校验审批已通过、尚未执行,且申请人与执行人符合职责分离要求。 +6. 在同一事务内完成业务状态、台账、审批执行时间和审计记录更新。 +7. 事务提交后再触发缓存清理、通知或 Outbox 事件。 + +状态转换判断统一放在后端,不接受前端直接指定任意结果状态。 + +### 4.2 会员状态 + +现有会员状态编号保持不变: + +| 常量 | 值 | 含义 | +| --- | ---: | --- | +| `AppMemberStatusNormal` | 1 | 正常 | +| `AppMemberStatusDisabled` | 2 | 禁用 | +| `AppMemberStatusCancelled` | 3 | 已注销 | + +允许转换: + +| 当前状态 | 目标状态 | 是否允许 | 说明 | +| --- | --- | --- | --- | +| 正常 | 禁用 | 是 | 必须审批 | +| 正常 | 注销 | 是 | 必须审批,执行后不可恢复 | +| 禁用 | 正常 | 是 | 必须审批 | +| 禁用 | 注销 | 是 | 必须审批 | +| 注销 | 任意状态 | 否 | 注销不可逆 | +| 任意状态 | 相同状态 | 否 | 防止无意义重复申请 | + +需求中的“受限”继续由现有 `risk_status` 表达,不新增或重排会员主状态: + +- `AppMemberRiskNormal`:正常。 +- `AppMemberRiskWatch`:观察或限制部分能力。 +- `AppMemberRiskBlocked`:拦截高风险能力。 + +发起状态审批时保存 `fromStatus`;执行时必须重新比对。如果会员状态已变化,则旧审批失效,不得覆盖最新状态。 + +### 4.3 充值状态 + +保留现有 1~6 状态,追加: + +| 常量 | 值 | 含义 | +| --- | ---: | --- | +| `RechargeReversed` | 7 | 已冲正 | + +核心转换: + +| 当前状态 | 目标状态 | 触发方式 | +| --- | --- | --- | +| 用户未支付 | 手动审核 | 支付凭证或支付结果进入审核 | +| 用户未支付 | 已过期/已取消 | 超时任务或用户取消 | +| 手动审核 | 已入账 | 审批通过并写入入账台账 | +| 手动审核 | 手动驳回 | 审批驳回 | +| 已入账 | 已冲正 | 冲正审批通过并生成反向台账 | + +`已入账 → 已冲正` 是成功充值唯一允许的后续资金状态转换。已冲正记录不得再次冲正。 + +### 4.4 任务审核状态 + +保留现有状态编号: + +| 常量 | 值 | 含义 | +| --- | ---: | --- | +| `TaskSubmissionPending` | 1 | 待审核 | +| `TaskSubmissionApproved` | 2 | 已通过 | +| `TaskSubmissionRejected` | 3 | 已驳回 | +| `TaskSubmissionSettled` | 4 | 已结算 | +| `TaskSubmissionReversed` | 5 | 已冲正 | + +当前佣金为同步结算时,可在同一事务中完成“待审核 → 已通过 → 已结算”,数据库最终状态为“已结算”;若以后改为异步结算,“已通过”可作为中间状态。 + +允许转换: + +| 当前状态 | 目标状态 | 说明 | +| --- | --- | --- | +| 待审核 | 已驳回 | 保存驳回原因和是否允许重提 | +| 待审核 | 已结算 | 审核通过并成功写入佣金台账 | +| 已结算 | 已冲正 | 冲正审批通过并写入反向台账 | +| 已驳回 | 其他状态 | 禁止;重新提交需生成新提交记录 | +| 已冲正 | 其他状态 | 禁止 | + +### 4.5 内容状态 + +使用现有 `BizPublishStatusOptions`: + +```text +草稿 → 待审 → 已发布 → 已下线 → 已归档 +``` + +规则: + +- 只有草稿可以提交审核。 +- 只有审批通过的版本可以发布。 +- 只有已发布内容可以下线。 +- 只有已下线内容可以归档。 +- 已归档内容不可直接编辑、发布或恢复。 +- 历史版本回滚不修改历史版本,而是复制为一个版本号更大的新草稿。 + +站点配置继续使用现有“历史版本 → 新回滚草稿 → 审批 → 发布”流程,不增加直接覆盖线上配置的接口。 + +## 5. 数据库变更 + +新增迁移文件: + +```text +server/storage/data/migrations/20261008_009_state_machine_hardening.sql +``` + +### 5.1 `hg_recharge_order` + +| 字段 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `reversal_approval_id` | bigint unsigned | 0 | 冲正审批单 ID | +| `reversal_ledger_id` | bigint unsigned | 0 | 冲正台账 ID | +| `reversed_at` | datetime(3) | NULL | 冲正执行时间 | + +建议增加索引: + +```sql +KEY `idx_recharge_reversal_approval` (`tenant_id`,`reversal_approval_id`) +``` + +### 5.2 `hg_task_submission` + +保留已有 `reversal_approval_id`,新增: + +| 字段 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `reversal_ledger_id` | bigint unsigned | 0 | 佣金冲正台账 ID | +| `reversed_at` | datetime(3) | NULL | 冲正执行时间 | + +建议增加索引: + +```sql +KEY `idx_task_submission_reversal` (`tenant_id`,`reversal_approval_id`) +``` + +### 5.3 `hg_content_version` + +为了保留回滚来源,新增: + +| 字段 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| `base_version_id` | bigint unsigned | 0 | 新版本来源的历史版本 ID | + +正常新增版本可为 0;通过回滚生成的新草稿必须保存来源版本 ID。 + +### 5.4 生成代码 + +迁移执行后,使用 GoFrame CLI 重新生成下列表对应的代码: + +- `hg_recharge_order` +- `hg_task_submission` +- `hg_content_version` + +生成范围包括 `entity`、`do`、`dao/internal` 和 `dao`,禁止手工修改生成文件。 + +## 6. 后端接口设计 + +### 6.1 会员状态 + +复用现有接口: + +- `POST /appMember/statusRequest` +- `POST /appMember/statusExecute` + +补强内容: + +- `StatusRequest` 校验允许转换并写入申请时状态。 +- `StatusExecute` 锁定会员记录后再次校验允许转换。 +- 已注销会员禁止发起和执行任何恢复状态请求。 +- 业务审计必须记录 `before.memberStatus` 和 `after.memberStatus`。 + +### 6.2 充值冲正 + +新增接口: + +| 接口 | Method | 权限 | 说明 | +| --- | --- | --- | --- | +| `/rechargeOrder/reversalRequest` | POST | `/rechargeOrder/reversalRequest` | 发起充值冲正审批 | +| `/rechargeOrder/reversalExecute` | POST | `/rechargeOrder/reversalExecute` | 执行已通过的充值冲正 | + +申请参数至少包括: + +- 充值单 ID。 +- 标准原因编码。 +- 补充说明。 +- 幂等键。 +- 证据附件或证据摘要,按现有 JSON 附件格式保存。 + +申请前置条件: + +- 充值单状态为“已入账”。 +- 存在原入账台账。 +- 尚未发起或执行冲正。 +- 原审批和入账结果有效。 + +执行必须在同一事务中: + +1. 锁定审批单、充值单、钱包账户和原台账。 +2. 校验审批已通过且未执行。 +3. 校验充值单仍为“已入账”。 +4. 校验原台账属于该充值单和该钱包。 +5. 生成金额相同、方向相反的新台账,并设置 `reversal_of=原台账ID`。 +6. 更新钱包余额和乐观锁版本,余额不得为负数。 +7. 更新充值单为“已冲正”,保存冲正审批、台账和时间。 +8. 标记审批已执行并写入业务审计。 + +### 6.3 任务佣金冲正 + +新增接口: + +| 接口 | Method | 权限 | 说明 | +| --- | --- | --- | --- | +| `/taskSubmission/reversalRequest` | POST | `/taskSubmission/reversalRequest` | 发起佣金冲正审批 | +| `/taskSubmission/reversalExecute` | POST | `/taskSubmission/reversalExecute` | 执行已通过的佣金冲正 | + +申请前置条件: + +- 提交记录状态为“已结算”。 +- `ledger_id` 有效。 +- 尚未发起或执行冲正。 +- 必须填写标准原因和补充说明。 + +执行逻辑与充值冲正一致,但业务状态更新为 `TaskSubmissionReversed`,冲正流水指向佣金原流水。 + +### 6.4 内容归档与回滚 + +新增接口: + +| 接口 | Method | 权限 | 说明 | +| --- | --- | --- | --- | +| `/content/archive` | POST | `/content/archive` | 将已下线内容归档 | +| `/content/rollback` | POST | `/content/rollback` | 根据历史版本生成新草稿 | + +归档规则: + +- 当前内容必须为“已下线”。 +- 归档后不可编辑或直接重新发布。 +- 保存操作人、原因和审计记录。 + +回滚规则: + +- 指定内容 ID 和历史版本 ID。 +- 历史版本必须属于该内容。 +- 新版本号为当前最大版本号加 1。 +- 复制历史版本正文、摘要、封面和附件信息。 +- 新版本状态为“草稿”,`base_version_id` 指向来源版本。 +- 不修改当前线上版本和历史版本。 + +## 7. 后端代码影响范围 + +按 HotGo 分层补充以下内容: + +| 层级 | 主要文件 | +| --- | --- | +| 常量与字典 | `server/internal/consts/trade.go`、`product_task.go`、`portal.go` | +| API 契约 | `server/api/admin/rechargeorder/`、`tasksubmission/`、`portal/` | +| Controller | `server/internal/controller/admin/sys/` 对应薄控制器 | +| Service | 在 `server/internal/service/sys.go` 的现有接口中追加方法 | +| Input | `server/internal/model/input/sysin/` 对应模块文件 | +| Logic | `app_member.go`、`recharge_order.go`、`task_submission.go`、`portal.go` | +| 路由 | 复用现有模块控制器注册,无需新增一级菜单 | +| 迁移 | `20261008_009_state_machine_hardening.sql` | + +状态值合法性通过后端字典校验;字段名使用 `dao.Xxx.Columns()`,禁止在 Logic 中硬编码数据库列名。 + +## 8. 前端设计 + +不增加一级菜单,只在现有页面增加操作入口。 + +### 8.1 充值管理 + +- 状态为“已入账”且拥有申请权限时显示“申请冲正”。 +- 已存在冲正审批时显示审批状态,不允许重复申请。 +- 审批通过且拥有执行权限时显示“执行冲正”。 +- 已冲正记录显示原台账和冲正台账关联信息。 + +### 8.2 会员任务审核 + +- 状态为“已结算”时显示“申请冲正”。 +- 申请弹窗包含原因字典、补充说明和证据。 +- 审批通过后由有执行权限的管理员执行。 +- 状态为“已冲正”后不再显示审核或冲正按钮。 + +### 8.3 文本管理 + +- 已下线内容显示“归档”。 +- 历史版本列表显示“生成回滚草稿”。 +- 回滚前明确提示“不会覆盖当前线上版本”。 +- 已归档内容的编辑和发布按钮禁用。 + +前端继续使用 Naive UI、`BasicTable`、`BasicForm`、`BasicModal` 和 `http.request`,状态标签通过后端字典渲染。 + +## 9. 权限设计 + +新增操作权限: + +```text +/rechargeOrder/reversalRequest +/rechargeOrder/reversalExecute +/taskSubmission/reversalRequest +/taskSubmission/reversalExecute +/content/archive +/content/rollback +``` + +权限规则: + +- 申请权限和执行权限分离。 +- 申请人不得审批或执行自己的高风险申请。 +- 执行接口必须独立校验权限,不能只依赖前端按钮隐藏。 +- 新权限通过 `hg_admin_menu` 的按钮类型记录配置,并授权给管理角色和业务角色。 + +## 10. 台账、幂等与审计 + +### 10.1 台账要求 + +- 原台账不可更新和删除。 +- 冲正台账金额必须等于原台账金额。 +- 冲正台账方向必须与原台账相反。 +- `reversal_of` 必须指向原台账 ID。 +- 同一原台账只能存在一条有效冲正台账。 +- 钱包变更后必须满足 `after_amount = before_amount ± amount`。 +- 可用余额和冻结余额不得小于 0。 + +### 10.2 幂等要求 + +- 申请接口使用调用方传入的幂等键。 +- 执行接口以审批单 ID 和业务单 ID 共同保证幂等。 +- 重复执行已完成审批时返回清晰业务错误,不重复写钱包和台账。 +- 数据库唯一索引冲突必须转换为用户可理解的业务错误。 + +### 10.3 审计要求 + +业务审计至少记录: + +- 模块和动作。 +- 操作人、目标类型和目标 ID。 +- 原状态和目标状态。 +- 原台账 ID 和冲正台账 ID。 +- 冲正金额、原因和证据摘要。 +- 审批单号和 Trace ID。 +- 变更前后摘要与结构化差异。 + +## 11. 异常与并发处理 + +| 场景 | 处理要求 | +| --- | --- | +| 重复点击执行 | 只允许第一次成功,后续返回“审批已执行” | +| 两个管理员同时执行 | 通过事务行锁保证只有一个事务成功 | +| 审批后业务状态变化 | 拒绝执行,要求重新发起审批 | +| 钱包余额不足以冲正 | 整体回滚,不产生台账和状态变化 | +| 原台账不存在或归属不符 | 拒绝执行并记录 Warning 日志 | +| 审批通过但事务失败 | 审批保持未执行,可排查后重试 | +| 审计写入失败 | 核心事务整体回滚 | +| 通知发送失败 | 不回滚核心事务,记录 Warning 并通过队列重试 | + +## 12. 测试方案 + +### 12.1 状态机测试 + +每个状态机都要覆盖: + +- 所有允许转换成功。 +- 所有禁止转换失败。 +- 相同状态重复申请失败。 +- 注销会员恢复失败。 +- 申请后状态变化导致执行失败。 +- 已执行审批重复执行失败。 + +### 12.2 资金测试 + +- 充值入账冲正后余额正确。 +- 任务佣金冲正后余额正确。 +- 原台账保持不变。 +- 冲正台账方向、金额和 `reversal_of` 正确。 +- 幂等键重复提交不产生重复台账。 +- 两个并发执行请求只生成一条冲正台账。 +- 余额不足时业务状态、钱包和台账均不变化。 +- 审计日志、审批单和台账可以相互追溯。 + +### 12.3 内容测试 + +- 只有已下线内容可以归档。 +- 已归档内容不可编辑和发布。 +- 历史版本回滚生成新草稿。 +- 回滚不改变当前发布版本。 +- 新草稿的 `base_version_id`、版本号和内容正确。 + +### 12.4 回归测试 + +- 原充值审核、提现审核和任务审核流程不受影响。 +- 原会员禁用和恢复流程仍可使用。 +- 原站点配置版本与回滚流程不受影响。 +- 字典加载、权限菜单和审计列表正常。 + +## 13. 验收标准 + +满足以下条件后视为第 12 章补强完成: + +1. 注销会员无法通过任何后台状态接口恢复。 +2. 充值成功后只能通过审批冲正,不允许直接修改余额或原台账。 +3. 已结算任务佣金可以通过审批冲正,且不可重复冲正。 +4. 每次冲正都能从业务单追溯到原台账、冲正台账、审批单和审计记录。 +5. 已下线内容可以归档,历史版本可以生成新草稿且不覆盖线上数据。 +6. 所有新增接口具有后端权限校验和明确的中文业务错误。 +7. 非法转换、重复执行、并发执行和余额不足测试全部通过。 +8. 后端编译、前端类型检查和现有模块回归测试通过。 + +## 14. 推荐实施顺序 + +1. 新增数据库迁移并用 GoFrame CLI 重新生成模型和 DAO。 +2. 补充状态字典和统一转换判断。 +3. 修复会员状态申请与执行校验。 +4. 实现充值冲正申请与执行。 +5. 实现任务佣金冲正申请与执行。 +6. 实现内容归档和历史版本回滚草稿。 +7. 增加按钮权限和角色授权。 +8. 增加前端操作按钮、弹窗和状态展示。 +9. 完成状态机、资金并发和回归测试。 + diff --git a/docs/guide-zh-CN/README.md b/docs/guide-zh-CN/README.md new file mode 100644 index 0000000..2aff16d --- /dev/null +++ b/docs/guide-zh-CN/README.md @@ -0,0 +1,60 @@ +## 目录 + +#### 介绍安装 + +- [系统介绍](../../README.md) +- [环境搭建](start-environment.md) +- [系统安装](start-installation.md) +- [生产部署](start-deploy.md) +- [如何提问](start-questions.md) +- [常见问题](start-issue.md) +- [更新历史](start-update-log.md) + + +#### 系统开发 + +- [目录结构](sys-catalog.md) +- [开发规范](sys-exploit.md) +- [控制台](sys-console.md) +- [中间件/拦截器](sys-middleware.md) +- [WebHook](sys-webhook.md) +- [权限控制](sys-auth.md) +- [支付网关](sys-payment.md) +- [定时任务](sys-cron.md) +- [消息队列](sys-queue.md) +- [功能扩展库](sys-library.md) +- [工具方法](sys-utility.md) +- [WebSocket服务器](sys-websocket-server.md) +- [TCP服务器](sys-tcp-server.md) +- [SaaS多租户](sys-tenant.md) +- [国际化](sys-i18n.md) +- [单元测试](sys-test.md) + + +#### 插件模块开发 + +- [模块介绍及目录](addon-introduce-catalog.md) +- [模块开发流程](addon-flow.md) +- [模块辅助说明](addon-helper.md) + + +#### 生成代码 +- [使用前提](code-start.md) +- [数据库](sys-db.md) +- [生成配置](code-config.md) +- [生成CURD](code-curd.md) +- [生成关联表CURD](code-curd-join.md) +- [生成树型CURD](code-tree.md) +- [生成业务模板](code-business.md) +- [生成模板开发](code-template-dev.md) +- [生成常见问题](code-help.md) + + +### 前端开发 +- [表单组件](web-form.md) +- [WebSocket客户端](sys-websocket-client.md) +- [独立部署](web-deploy.md) + + +#### 附录 +- [网址收录](append-website.md) \ No newline at end of file diff --git a/docs/guide-zh-CN/addon-flow.md b/docs/guide-zh-CN/addon-flow.md new file mode 100644 index 0000000..209cc56 --- /dev/null +++ b/docs/guide-zh-CN/addon-flow.md @@ -0,0 +1,216 @@ +## 模块开发流程 + +目录 + +- 创建新插件 +- 开发 +- 调用主模块服务接口 +- 访问路径 +- 数据迁移 + + + +### 创建新插件 + +1、HotGo 后台进入 开发工具->插件管理->找到创建新插件,根据引导进行创建即可。 + +```shell +创建成功后默认情况下会在以下目录中生成插件文件,假设新生成的插件名为:hgexample + +1. /server/addons/hgexample/ # 插件模块目录 +2. /server/addons/modules/hgexample.go # 隐式注册插件文件 +3. /server/resource/addons/hgexample # 静态资源和页面模板目录,属于扩展功能选项,勾选对应选项后才会生成 +4. /web/src/api/addons/hgexample # webApi目录 +5. /web/src/views/addons/hgexample # web页面目录 + +# 默认情况下没有为web页面生成菜单权限,因为在实际场景中插件不一定需要用到web页面,所以如有需要请手动到后台 权限管理 -> 菜单权限->自行添加菜单和配置权限 +``` + + +2、创建插件完毕重启服务端后,插件管理中会出现你新创建的插件信息。操作栏有几个按钮,在此进行说明 +- 安装:会自动执行 server/hgexample/main.go 文件中的Install方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如生成后台菜单、生成插件配置表初始化数据、迁移home页面、web项目文件等。 +```go +// Install 安装模块 +func (m *module) Install(ctx context.Context) (err error) { + // ... + return +} +``` + +- 更新:会自动执行 server/hgexample/main.go 文件中的Upgrade方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。 +```go +// Upgrade 更新模块 +func (m *module) Upgrade(ctx context.Context) (err error) { + // ... + return +} +``` + +- 卸载:会自动执行 server/hgexample/main.go 文件中的UnInstall方法,方法中的具体逻辑默认为空,可以根据实际情况自行配置。如会清除所有的数据表和已安装的信息等。 +```go +// UnInstall 卸载模块 +func (m *module) UnInstall(ctx context.Context) (err error) { + // ... + return +} +``` + + + +### 开发 + +完全可以根据HotGo正常的开发流程去开发对应的API、控制器、业务逻辑、插件内的应用 + +### 调用主模块服务接口 + +这里推荐的方式是在插件input层新建一个结构,继承主模块中的input结构。这样做的目的是为了服务与服务之间的输入/输出关系解耦,便于参数扩展和避免插件模块下使用`gf gen service`时出现`import cycle not allowed`。 + +一个简单的例子: +> 假设hgexample插件模块要通过主模块的服务接口更新插件配置 + +插件模块input:\server\addons\hgexample\model\input\sysin\config.go +```go +package sysin + +import ( + "hotgo/internal/model/input/sysin" +) + +// UpdateConfigInp 更新指定配置 +type UpdateConfigInp struct { + sysin.UpdateAddonsConfigInp +} + +``` + +插件模块业务逻辑:\server\addons\hgexample\logic\sys\config.go +```go +package sys + +import ( + "context" + "hotgo/addons/hgexample/global" + "hotgo/addons/hgexample/model/input/sysin" + "hotgo/addons/hgexample/service" + isc "hotgo/internal/service" +) + +type sSysConfig struct{} + +func NewSysConfig() *sSysConfig { + return &sSysConfig{} +} + +func init() { + service.RegisterSysConfig(NewSysConfig()) +} + +// UpdateConfigByGroup 更新指定分组的配置 +func (s *sSysConfig) UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) error { + in.UpdateAddonsConfigInp.AddonName = global.GetSkeleton().Name + return isc.SysAddonsConfig().UpdateConfigByGroup(ctx, in.UpdateAddonsConfigInp) +} + +``` + +主模块input:\server\internal\model\input\sysin\addons_config.go +```go +package sysin + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// UpdateAddonsConfigInp 更新指定插件的配置 +type UpdateAddonsConfigInp struct { + AddonName string `json:"addonName"` + Group string `json:"group"` + List g.Map `json:"list"` +} + +``` + +主模块业务逻辑:\server\internal\logic\sys\addons_config.go +```go +package sys + +import ( + "context" + "hotgo/internal/model/input/sysin" + "hotgo/internal/service" +) + +type sSysAddonsConfig struct{} + +func NewSysAddonsConfig() *sSysAddonsConfig { + return &sSysAddonsConfig{} +} + +func init() { + service.RegisterSysAddonsConfig(NewSysAddonsConfig()) +} + +// UpdateConfigByGroup 更新指定分组的配置 +func (s *sSysAddonsConfig) UpdateConfigByGroup(ctx context.Context, in sysin.UpdateAddonsConfigInp) error { + // ... + return nil +} + +``` + + + + +### 访问路径 + +#### 后台插件访问路径 + +``` +// IP+端口或域名/admin/插件名称/API路径 +如:127.0.0.1:8000/admin/hgexample/index/test +``` + +- 对应控制器路径:`server/addons/hgexample/controller/admin/sys/index.go` + +#### 前端API插件访问路径 + +``` +// IP+端口或域名/api/插件名称/API路径 +如:127.0.0.1:8000/api/hgexample/index/test +``` + +- 对应控制器路径:`server/addons/hgexample/controller/api/index.go` + +#### 前台页面插件访问路径 + +``` +// IP+端口或域名/home/插件名称/API路径 +如:127.0.0.1:8000/home/hgexample/index/test +``` + +- 对应控制器路径:`server/addons/hgexample/controller/home/index.go` +- 对应模板路径:`server/addons/hgexample/resource/public/template` + +#### 静态资源插件访问路径 + +``` +// IP+端口或域名/home/插件名称/API路径 +如:127.0.0.1:8000/addons/hgexample/default +``` + +- 对应资源路径:`server/addons/hgexample/resource/public` + + +#### Websocket插件访问路径 + +``` +// IP+端口或域名/socket/插件名称/API路径 +如:127.0.0.1:8000/socket/hgexample/index/test +``` + +- 对应控制器路径:`server/addons/hgexample/controller/socket/index.go` + + +### 数据迁移 + +- 可以将数据迁移逻辑写进server/xxx插件/main.go 文件中的Install方法中,并遵循系统规范进行数据安装 diff --git a/docs/guide-zh-CN/addon-helper.md b/docs/guide-zh-CN/addon-helper.md new file mode 100644 index 0000000..57d4d56 --- /dev/null +++ b/docs/guide-zh-CN/addon-helper.md @@ -0,0 +1,86 @@ +## 模块辅助说明 + +目录 + +- 模块结构 +- 获取模块信息 +- 插件路由规则 + + +#### 模块结构 +- 文件路径:server/internal/library/addons/module.go + +```go +// Skeleton 模块骨架 +type Skeleton struct { + Label string `json:"label"` // 标识 + Name string `json:"name"` // 名称 + Group int `json:"group"` // 分组 + Logo string `json:"logo"` // logo + Brief string `json:"brief"` // 简介 + Description string `json:"description"` // 详细描述 + Author string `json:"author"` // 作者 + Version string `json:"version"` // 版本号 + RootPath string `json:"rootPath"` // 根路径 + View *gview.View `json:"view"` // 模板引擎 +} + +func (s *Skeleton) GetModule() Module { + return GetModule(s.Name) +} + +// Module 插件模块 +type Module interface { + Start(option *Option) (err error) // 启动模块 + Stop() (err error) // 停止模块 + Ctx() context.Context // 上下文 + GetSkeleton() *Skeleton // 获取模块 + Install(ctx context.Context) (err error) // 安装模块 + Upgrade(ctx context.Context) (err error) // 更新模块 + UnInstall(ctx context.Context) (err error) // 卸载模块 +} +``` + +#### 获取模块信息 + +- 在插件模块内 + +```go +package main + +import ( + "fmt" + "hotgo/addons/hgexample/global" +) + +func test() { + fmt.Printf("当前插件模块是:%+v", global.GetSkeleton()) +} +``` + +- 在插件模块外 + +```go +package main + +import ( + "context" + "fmt" + "hotgo/internal/library/addons" + "hotgo/internal/library/contexts" +) + +func test(ctx context.Context) { + fmt.Printf("当前是否为插件请求:%v", contexts.IsAddonRequest(ctx)) + if contexts.IsAddonRequest(ctx) { + fmt.Printf("当前插件名称:%v", contexts.GetAddonName(ctx)) + fmt.Printf("当前插件信息:%v", addons.GetModule(contexts.GetAddonName(ctx))) + } +} +``` + +- 更多辅助方法请参考插件功能库:server/internal/library/addons + +#### 插件路由规则 +- 如果你不喜欢现在的路由风格,可以自行调整。修改位置在:\server\internal\library\addons\addons.go的RouterPrefix方法。 +- 注意调整后如web前端页面中如有之前的路由风格也需同步修改。 diff --git a/docs/guide-zh-CN/addon-introduce-catalog.md b/docs/guide-zh-CN/addon-introduce-catalog.md new file mode 100644 index 0000000..c7f98f1 --- /dev/null +++ b/docs/guide-zh-CN/addon-introduce-catalog.md @@ -0,0 +1,40 @@ +## 模块介绍及目录 + +目录 + +- 模块介绍 +- 启动流程 +- 目录结构 + +### 模块介绍 + +> 定位:开发独立、临时性、工具类型的功能时推荐使用插件化开发,例如:小游戏(大转盘/消消乐/抽奖/大屏互动/红包等)、小插件(广告管理/文章管理/友情链接等等)、小模块(报名/投票/签到)、小程序、大型插件微商城等等。 + +> 插件模块方便多项目复用,同时完美支持多人协同开发,每个插件模块都有独立的微架构目录结构,多插件之间完全隔离。 + +### 启动流程 + +HotGo 入口文件->隐式注入(hotgo/addons/modules)->注册所有插件->初始化已安装的插件->写入路由组->根据 HotGo 正常的开发和访问流程去开发访问插件 + + +### 目录结构 +- 详细介绍请参考:[目录结构](sys-catalog.md) +``` +/server +├── addons +│ ├── modules +│ ├── xxx插件 +│ | ├── api +│ | ├── consts +│ | ├── controller +│ | ├── crons +│ | ├── global +│ | ├── logic +│ | ├── model +│ | ├── queues +│ | ├── resource +│ | ├── router +│ | ├── service +│ | ├── main.go +│ | └── README.md +``` diff --git a/docs/guide-zh-CN/append-website.md b/docs/guide-zh-CN/append-website.md new file mode 100644 index 0000000..8ca465f --- /dev/null +++ b/docs/guide-zh-CN/append-website.md @@ -0,0 +1,21 @@ +目录 + +- 框架文档 + - [goframe](https://goframe.org/pages/viewpage.action?pageId=1114119) + - [naiveui](https://www.naiveui.com) + - [naive-ui-admin](https://docs.naiveadmin.com/) + + +- 常用组件 + - [websocket](https://github.com/gorilla/websocket) + - [casbin](https://github.com/casbin/casbin) + - [tailwind](https://tailwind.docs.73zls.com/docs/flex-direction) + + +- 系统通用 + - [redis](https://redis.io/) + + +- 其他 + - [awesome-go](https://github.com/avelino/awesome-go) + \ No newline at end of file diff --git a/docs/guide-zh-CN/code-business.md b/docs/guide-zh-CN/code-business.md new file mode 100644 index 0000000..2564b47 --- /dev/null +++ b/docs/guide-zh-CN/code-business.md @@ -0,0 +1,5 @@ +## 生成业务模板 + +根据`api`接口文件一键生成业务模板:api、controller、logic、service + +待写。 diff --git a/docs/guide-zh-CN/code-config.md b/docs/guide-zh-CN/code-config.md new file mode 100644 index 0000000..273dca2 --- /dev/null +++ b/docs/guide-zh-CN/code-config.md @@ -0,0 +1,172 @@ +## 生成配置 + +目录 + +- 模板配置 +- CLI配置 +- 多数据库使用 + +### 模板配置 + +- 配置路径:server/manifest/config/config.yaml + +```yaml +# 生成代码 +hggen: + allowedIPs: [ "127.0.0.1", "*" ] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能 + selectDbs: [ "default" ] # 可选生成表的数据库配置名称,支持多库 + disableTables: [ "hg_sys_gen_codes","hg_admin_role_casbin" ] # 禁用的表,禁用以后将不会在选择表中看到 + delimiters: [ "@{", "}" ] # 模板引擎变量分隔符号 + # 生成应用模型,所有生成模板允许自定义,可以参考default模板进行改造 + application: + # CRUD和关系树列表模板 + crud: + templates: + # 默认的主包模板 + - group: "default" # 分组名称 + isAddon: false # 是否为插件模板 false|true + masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联 + templatePath: "./resource/generate/default/curd" # 模板路径 + apiPath: "./api/admin" # goApi生成路径 + controllerPath: "./internal/controller/admin/sys" # 控制器生成路径 + logicPath: "./internal/logic/sys" # 主要业务生成路径 + inputPath: "./internal/model/input/sysin" # 表单过滤器生成路径 + routerPath: "./internal/router/genrouter" # 生成路由表路径 + sqlPath: "./storage/data/generate" # 生成sql语句路径 + webApiPath: "../web/src/api" # webApi生成路径 + webViewsPath: "../web/src/views" # web页面生成路径 + + # 默认的插件包模板,{$name}会自动替换成实际的插件名称 + - group: "addon" # 分组名称 + isAddon: true # 是否为插件模板 false|true + masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联 + templatePath: "./resource/generate/default/curd" # 模板路径 + apiPath: "./addons/{$name}/api/admin" # goApi生成路径 + controllerPath: "./addons/{$name}/controller/admin/sys" # 控制器生成路径 + logicPath: "./addons/{$name}/logic/sys" # 主要业务生成路径 + inputPath: "./addons/{$name}/model/input/sysin" # 表单过滤器生成路径 + routerPath: "./addons/{$name}/router/genrouter" # 生成路由表路径 + sqlPath: "./storage/data/generate/addons" # 生成sql语句路径 + webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径 + webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径 + + # 消息队列模板 + queue: + templates: + - group: "default" + templatePath: "./resource/generate/default/queue" + + # 定时任务模板 + cron: + templates: + - group: "default" + templatePath: "./resource/generate/default/cron" + + # 生成插件模块,通过后台创建新插件时使用的模板,允许自定义,可以参考default模板进行改造 + addon: + srcPath: "./resource/generate/default/addon" # 生成模板路径 + webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径 + webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径 +``` + +### CLI配置 + +- hotgo在生成dao、service配置时,默认了和gf官方一致的配置方式和代码生成规则。所以无论你是通过hotgo亦或gf命令生成,最终代码格式完全一致,遵循一致的代码规范。 + +- 配置路径:[server/hack/config.yaml](../../server/hack/config.yaml) + +```yaml +gfcli: + build: + name: "hotgo" # 编译后的可执行文件名称 + # arch: "all" #不填默认当前系统架构,可选:386,amd64,arm,all + # system: "all" #不填默认当前系统平台,可选:linux,darwin,windows,all + mod: "none" + cgo: 0 + packSrc: "resource" # 将resource目录打包进可执行文件,静态资源无需单独部署 + packDst: "internal/packed/packed.go" # 打包后生成的Go文件路径,一般使用相对路径指定到本项目目录中 + version: "" + output: "./temp/hotgo" # 可执行文件生成路径 + extra: "" + + gen: + dao: + - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true" +# - link: "pgsql:postgres:hg123456@tcp(127.0.0.1:5432)/hotgo" + group: "default" # 分组 使用hotgo代码生成功能时必须填 + # tables: "" # 指定当前数据库中需要执行代码生成的数据表。如果为空,表示数据库的所有表都会生成。 + tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。 + removePrefix: "hg_" + descriptionTag: true + noModelComment: true + jsonCase: "CamelLower" + gJsonSupport: true + clear: true + + service: # 生成业务配置 + srcFolder: "internal/logic" + dstFolder: "internal/service" + dstFileNameCase: "CamelLower" + clear: true +``` + +### 多数据库使用 + +- 假设我们要增加一个库名为`hotgo2`、分组为`default2`的数据库,并要为其生成代码 + +1. 配置[server/hack/config.yaml](../../server/hack/config.yaml) 如下: +```yaml + gen: + dao: + - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true" + group: "default" # 分组 使用hotgo代码生成功能时必须填 + tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。 + removePrefix: "hg_" + descriptionTag: true + noModelComment: true + jsonCase: "CamelLower" + gJsonSupport: true + clear: false + - link: "mysql:hotgo2:hg123456.@tcp(127.0.0.1:3306)/hotgo2?loc=Local&parseTime=true" + group: "default2" # 分组 使用hotgo代码生成功能时必须填 + tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。 + removePrefix: "" + descriptionTag: true + noModelComment: true + jsonCase: "CamelLower" + gJsonSupport: true + clear: false +``` + +2. 配置`server/manifest/config/config.yaml`, + +`database`配置如下: +```yaml +database: + logger: + level: "all" + stdout: true + default: + link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true" +# link: "pgsql:postgres:hg123456@tcp(127.0.0.1:5432)/hotgo" + debug: true + Prefix: "hg_" + default2: + link: "mysql:hotgo2:hg123456.@tcp(127.0.0.1:3306)/hotgo2?loc=Local&parseTime=true" + debug: true + Prefix: "" +``` + +`hggen`配置如下: +```yaml +hggen: + allowedIPs: ["127.0.0.1", "*"] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能 + selectDbs: [ "default", "default2" ] # 可选生成表的数据库配置名称,支持多库 + disableTables : ["hg_sys_gen_codes","hg_admin_role_casbin"] # 禁用的表,禁用以后将不会在选择表中看到 + delimiters: ["@{", "}"] # 模板引擎变量分隔符号 +``` + +3. 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,就会发现`数据库`选项增加了一个`default2`,后续生成步骤和生成例子完全一样 + +> 注意:上述的配置中所有的`default2`名称必须保持一致 + diff --git a/docs/guide-zh-CN/code-curd-join.md b/docs/guide-zh-CN/code-curd-join.md new file mode 100644 index 0000000..a732158 --- /dev/null +++ b/docs/guide-zh-CN/code-curd-join.md @@ -0,0 +1,115 @@ +## 生成关联表CURD + +### 热编译启动 +- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启 + +```shell +# 服务端 +cd server +gf run main.go + +# web端 +cd web +pnpm run dev 或 npm run dev +``` + +以下是一个关联表的CURD生成流程 + +### 创建表结构 +- 以下表结构和数据为了方便功能演示已经内置无需再次创建 + + +- 创建主表:hg_sys_gen_curd_demo +```sql +CREATE TABLE `hg_sys_gen_curd_demo` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `category_id` bigint(20) DEFAULT '0' COMMENT '分类ID', + `title` varchar(64) NOT NULL COMMENT '标题', + `description` varchar(255) DEFAULT '' COMMENT '描述', + `content` text COMMENT '内容', + `image` varchar(255) DEFAULT NULL COMMENT '单图', + `attachfile` varchar(255) DEFAULT NULL COMMENT '附件', + `city_id` bigint(20) DEFAULT '0' COMMENT '所在城市', + `switch` int(11) DEFAULT '1' COMMENT '显示开关', + `sort` int(11) DEFAULT NULL COMMENT '排序', + `status` tinyint(1) DEFAULT '1' COMMENT '状态', + `created_by` bigint(20) DEFAULT '0' COMMENT '创建者', + `updated_by` bigint(20) DEFAULT '0' COMMENT '更新者', + `created_at` datetime DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime DEFAULT NULL COMMENT '修改时间', + `deleted_at` datetime DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COMMENT='系统_生成curd演示'; +``` + +创建关联表:hg_test_category +```sql +CREATE TABLE `hg_test_category` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID', + `name` varchar(255) NOT NULL COMMENT '分类名称', + `short_name` varchar(128) DEFAULT NULL COMMENT '简称', + `description` varchar(255) DEFAULT NULL COMMENT '描述', + `sort` int(11) NOT NULL COMMENT '排序', + `remark` varchar(255) DEFAULT NULL COMMENT '备注', + `status` tinyint(1) DEFAULT '1' COMMENT '状态', + `created_at` datetime DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime DEFAULT NULL COMMENT '修改时间', + `deleted_at` datetime DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='测试分类'; +``` + +### 表字段要求 +- 使用生成时,表中必须具有以下字段和属性 + +| 字段名称 | 字段含义 | 字段类型 | 可为空 | +|--------|----------------------|---------------------|-----| +| id | 主键ID | bigint(20) | 否 | + + + +### 创建生成配置 +- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数: + +![](images/code/join-add.png) + + +### 基本设置 +- 确认无误后,点击生成配置会跳转到生成配置页面,如下: +- 你可以在该页面调整生成表格表头/表列的接口功能、菜单权限、高级设置和关联表设置 + +![](images/code/join-init.png) + +### 主表字段设置 + +- 在该页面你可以调整生成表格字段名称、表单组件、表单编辑/验证项、列表展示/查询项、字段排序、重置和同步字段 + +![](images/code/join-fields.png) + + +### 关联表字段设置 +- 在该页面你可以调整生成表格关联表字段名称、列表展示/查询项、字段排序、重置和同步字段 +- 如果存在多个关联表,也可以对多个关联表字段进行设置 + +![](images/code/join-fields2.png) + +### 预览并生成 + +点击`预览代码`查看生成的代码内容。如果无需继续调整直接点击`提交生成`即可,以下是预览代码效果: + +![](images/code/join-preview.png) + +- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。 + + +### 生成完成 + +- 让我们看看生成的表格页面,效果如下: + +![](images/code/join-list.png) + +### 常见问题 + +- [生成常见问题](code-help.md) + + diff --git a/docs/guide-zh-CN/code-curd.md b/docs/guide-zh-CN/code-curd.md new file mode 100644 index 0000000..46a30bc --- /dev/null +++ b/docs/guide-zh-CN/code-curd.md @@ -0,0 +1,84 @@ +## 生成CURD + +### 热编译启动 +- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启 + +```shell +# 服务端 +cd server +gf run main.go + +# web端 +cd web +pnpm run dev 或 npm run dev +``` + +以下是一个基本的CURD生成流程 + +### 创建表结构 +- 以下表结构和数据为了方便功能演示已经内置无需再次创建 + +```sql +CREATE TABLE `hg_test_category` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID', + `name` varchar(255) NOT NULL COMMENT '分类名称', + `short_name` varchar(128) DEFAULT NULL COMMENT '简称', + `description` varchar(255) DEFAULT NULL COMMENT '描述', + `sort` int(11) NOT NULL COMMENT '排序', + `remark` varchar(255) DEFAULT NULL COMMENT '备注', + `status` tinyint(1) DEFAULT '1' COMMENT '状态', + `created_at` datetime DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime DEFAULT NULL COMMENT '修改时间', + `deleted_at` datetime DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='测试分类'; +``` + +### 表字段要求 +- 使用生成时,表中必须具有以下字段和属性 + +| 字段名称 | 字段含义 | 字段类型 | 可为空 | +|--------|----------------------|---------------------|-----| +| id | 主键ID | bigint(20) | 否 | + + +### 创建生成配置 +- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数: + +![](images/code/curd-add.png) + + +### 基本设置 +- 确认无误后,点击生成配置会跳转到生成配置页面,如下: +- 你可以在该页面调整生成表格表头/表列的接口功能、菜单权限、高级设置和关联表设置 + +![](images/code/curd-init.png) + +### 表字段设置 + +- 在该页面你可以调整生成表格字段名称、表单组件、表单编辑/验证项、列表展示/查询项、字段排序、重置和同步字段 + +![](images/code/curd-fields.png) + +### 预览并生成 + +点击`预览代码`查看生成的代码内容。如果无需继续调整直接点击`提交生成`即可,以下是预览代码效果: + +![](images/code/curd-preview.png) + +- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。 + + +### 生成完成 + +- 让我们看看生成的表格页面,效果如下: + +![](images/code/curd-list.png) + + +### 常见问题 + +- [生成常见问题](code-help.md) + + + diff --git a/docs/guide-zh-CN/code-help.md b/docs/guide-zh-CN/code-help.md new file mode 100644 index 0000000..8f220b1 --- /dev/null +++ b/docs/guide-zh-CN/code-help.md @@ -0,0 +1,18 @@ +## 生成常见问题 + +### 生成完后页面提示:服务器错误,请稍候重试! + +- 热编译环境下,web端往往会快于服务端重启并加载完成,此时接口访问会出现`服务器错误,请稍候重试!`或`404`。这是服务端正在重启导致的,一般稍等几秒就好,如果不行就手动重启下服务端。 + + +### fetching tables failed: SHOW TABLES: Error 1045 (28000): Access denied for user * (using password: YES) + +- 请去确认`server/manifest/config/config.yaml`和`server/hack/config.yaml`下的数据库配置一致并且权限正确 +- 参考:[生成配置](code-config.md) + + +### 为什么后台找不到开发工具菜单 + +- 请去确认`server/manifest/config/config.yaml`中的`system.mode`不为`product`。product模式下后台【开发工具】菜单自动隐藏 + + diff --git a/docs/guide-zh-CN/code-start.md b/docs/guide-zh-CN/code-start.md new file mode 100644 index 0000000..ddeea5e --- /dev/null +++ b/docs/guide-zh-CN/code-start.md @@ -0,0 +1,20 @@ +## 使用前提 + + +在HotGo中可以通过后台开发工具快速的一键生成CRUD,自动生成Api、控制器、业务逻辑、Web页面、表单组件、菜单权限等。 + + +- hotgo 版本号 >= 2.13.1 +- 使用前必须配置 [生成配置](code-config.md) +- 使用前必须了解 [数据库](sys-db.md) + + +相关目录 + +- [生成配置](code-config.md) +- [生成CURD](code-curd.md) +- [生成关联表CURD](code-curd-join.md) +- [生成树型CURD](code-tree.md) +- [生成业务模板](code-business.md) +- [生成模板开发](code-template-dev.md) +- [生成常见问题](code-help.md) \ No newline at end of file diff --git a/docs/guide-zh-CN/code-template-dev.md b/docs/guide-zh-CN/code-template-dev.md new file mode 100644 index 0000000..6747ac8 --- /dev/null +++ b/docs/guide-zh-CN/code-template-dev.md @@ -0,0 +1,59 @@ +## 生成模板开发 + +### 自定义生成模板 + +- HotGo允许你新建新的模板分组来满足你的需求,模板可根据现有模板基础拷贝一份出来做改造,默认模板目录:[server/resource/generate/default](../../server/resource/generate/default) + +- 系统内置了两组CURD生成模板,请参考[生成模板配置](sys-code.md#生成模板配置)。default:是默认的生成到主模块下;addon:是默认生成到指定的插件下 + + + +### 内置gf-cli + +- 为了确保生成代码的依赖稳定性,在面对`gf`版本更新可能导致向下不兼容情况时,HotGo将`gf-cli`工具内置到系统中并进行在线执行调整,从而提供更可靠和一致的生成代码功能。 + +- 后续我们也将开放在线运行`gf gen ...`功能。在做插件开发时也会支持到在线生成插件下的service接口,这将会使得插件开发更加方便 + + + +### 指定gf-cli版本 + +- HotGo多数情况下会和最新版本的gf-cli保持同步,如果更新不及时或你不想使用最新版本的gf-cli来生成代码,可以找到自己想要的版本进行替换即可。 + +- 下面大致做一些替换步骤说明: + +1. 打开`github.com/gogf/gf` 找到你想要使用的版本`clone`下来 +2. 将`clone`代码中`gf/cmd/gf/internal/`目录覆盖到`server/internal/library/hggen/internal` +3. 将覆盖过来的目录文件中引入包名`github.com/gogf/gf/cmd/gf/v2/`批量改为`hotgo/internal/library/hggen/` +4. 运行`go mod tidy` +5. 运行`go run main.go`,如果没有报错,那么恭喜你已经完成了。如果有报错一般都是版本差异带来的影响,需要根据情况自行调整 + + + +### 指定数据库驱动 + +> HotGo默认使用mysql驱动,如果你想用其他数据库驱动打开下方文件中注释即可,目前已支持mysql、pgsql + +- 修改文件路径:[server/internal/library/hggen/internal/cmd/cmd_gen_dao.go](../../server/internal/library/hggen/internal/cmd/cmd_gen_dao.go) + +```go +package cmd + +import ( + //_ "github.com/gogf/gf/contrib/drivers/clickhouse/v2" + //_ "github.com/gogf/gf/contrib/drivers/mssql/v2" + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + //_ "github.com/gogf/gf/contrib/drivers/oracle/v2" + _ "github.com/gogf/gf/contrib/drivers/pgsql/v2" + //_ "github.com/gogf/gf/contrib/drivers/sqlite/v2" + + "hotgo/internal/library/hggen/internal/cmd/gendao" +) + +type ( + cGenDao = gendao.CGenDao +) + +``` + +修改完成后运行`go mod tidy` diff --git a/docs/guide-zh-CN/code-tree.md b/docs/guide-zh-CN/code-tree.md new file mode 100644 index 0000000..6583660 --- /dev/null +++ b/docs/guide-zh-CN/code-tree.md @@ -0,0 +1,93 @@ +## 生成树型CURD + +### 热编译启动 +- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启 + +```shell +# 服务端 +cd server +gf run main.go + +# web端 +cd web +pnpm run dev 或 npm run dev +``` + +以下是一个基本的树形CURD生成流程 + +### 创建表结构 +- 以下表结构和数据为了方便功能演示已经内置无需再次创建 + +```sql +CREATE TABLE `hg_sys_gen_tree_demo` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `pid` bigint(20) DEFAULT NULL COMMENT '上级ID', + `level` int(11) DEFAULT '1' COMMENT '关系树级别', + `tree` varchar(512) COMMENT '关系树', + `category_id` bigint(20) DEFAULT '0' COMMENT '分类ID', + `title` varchar(64) NOT NULL COMMENT '标题', + `description` varchar(255) DEFAULT '' COMMENT '描述', + `sort` int(11) DEFAULT NULL COMMENT '排序', + `status` tinyint(1) DEFAULT '1' COMMENT '状态', + `created_by` bigint(20) DEFAULT '0' COMMENT '创建者', + `updated_by` bigint(20) DEFAULT '0' COMMENT '更新者', + `created_at` datetime DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime DEFAULT NULL COMMENT '修改时间', + `deleted_at` datetime DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8mb4 COMMENT='系统_生成树表演示'; +``` + +### 表字段要求 +- 使用生成时,表中必须具有以下字段和属性 + +| 字段名称 | 字段含义 | 字段类型 | 可为空 | +|--------|----------------------|---------------------|-----| +| id | 主键ID | bigint(20) | 否 | +| pid | 上级树ID | bigint(20) | 是 | +| level | 关系树级别 | int(11) | 否 | +| tree | 关系树 | varchar(512) | 是 | + +### 创建生成配置 +- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数: + +![](images/code/tree-add.png) + + +### 基本设置 +- 确认无误后,点击生成配置会跳转到生成配置页面,如下: +- 你可以在该页面调整生成表格表头/表列的接口功能、菜单权限、高级设置和关联表设置 +- 相比普通CURD,这里多了`树名称字段`和`树表格样式`选项 + +![](images/code/tree-init.png) + +### 表字段设置 + +- 在该页面你可以调整生成表格字段名称、表单组件、表单编辑/验证项、列表展示/查询项、字段排序、重置和同步字段 +- 树表中的pid、level、tree字段由系统维护,单独设置编辑表单功能无效! + +![](images/code/tree-fields.png) + +### 预览并生成 + +点击`预览代码`查看生成的代码内容。如果无需继续调整直接点击`提交生成`即可,以下是预览代码效果: + +![](images/code/tree-preview.png) + +- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。 + + +### 生成完成 + +- 让我们看看生成的表格页面,效果如下: + +- 普通树表 +![](images/code/tree-list.png) + +- 选项式树表 +![](images/code/tree-list2.png) + +### 常见问题 + +- [生成常见问题](code-help.md) + diff --git a/docs/guide-zh-CN/images/code/curd-add.png b/docs/guide-zh-CN/images/code/curd-add.png new file mode 100644 index 0000000..14ef083 Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-add.png differ diff --git a/docs/guide-zh-CN/images/code/curd-fields.png b/docs/guide-zh-CN/images/code/curd-fields.png new file mode 100644 index 0000000..00fe790 Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-fields.png differ diff --git a/docs/guide-zh-CN/images/code/curd-init.png b/docs/guide-zh-CN/images/code/curd-init.png new file mode 100644 index 0000000..c0179f2 Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-init.png differ diff --git a/docs/guide-zh-CN/images/code/curd-list.png b/docs/guide-zh-CN/images/code/curd-list.png new file mode 100644 index 0000000..11ed610 Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-list.png differ diff --git a/docs/guide-zh-CN/images/code/curd-preview.png b/docs/guide-zh-CN/images/code/curd-preview.png new file mode 100644 index 0000000..1d73d27 Binary files /dev/null and b/docs/guide-zh-CN/images/code/curd-preview.png differ diff --git a/docs/guide-zh-CN/images/code/join-add.png b/docs/guide-zh-CN/images/code/join-add.png new file mode 100644 index 0000000..07cab88 Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-add.png differ diff --git a/docs/guide-zh-CN/images/code/join-fields.png b/docs/guide-zh-CN/images/code/join-fields.png new file mode 100644 index 0000000..39dca79 Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-fields.png differ diff --git a/docs/guide-zh-CN/images/code/join-fields2.png b/docs/guide-zh-CN/images/code/join-fields2.png new file mode 100644 index 0000000..e078942 Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-fields2.png differ diff --git a/docs/guide-zh-CN/images/code/join-init.png b/docs/guide-zh-CN/images/code/join-init.png new file mode 100644 index 0000000..6e387e2 Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-init.png differ diff --git a/docs/guide-zh-CN/images/code/join-list.png b/docs/guide-zh-CN/images/code/join-list.png new file mode 100644 index 0000000..5d60678 Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-list.png differ diff --git a/docs/guide-zh-CN/images/code/join-preview.png b/docs/guide-zh-CN/images/code/join-preview.png new file mode 100644 index 0000000..741a44b Binary files /dev/null and b/docs/guide-zh-CN/images/code/join-preview.png differ diff --git a/docs/guide-zh-CN/images/code/tree-add.png b/docs/guide-zh-CN/images/code/tree-add.png new file mode 100644 index 0000000..b5dce67 Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-add.png differ diff --git a/docs/guide-zh-CN/images/code/tree-fields.png b/docs/guide-zh-CN/images/code/tree-fields.png new file mode 100644 index 0000000..a998a20 Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-fields.png differ diff --git a/docs/guide-zh-CN/images/code/tree-init.png b/docs/guide-zh-CN/images/code/tree-init.png new file mode 100644 index 0000000..d5c7bdc Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-init.png differ diff --git a/docs/guide-zh-CN/images/code/tree-list.png b/docs/guide-zh-CN/images/code/tree-list.png new file mode 100644 index 0000000..e93b4b3 Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-list.png differ diff --git a/docs/guide-zh-CN/images/code/tree-list2.png b/docs/guide-zh-CN/images/code/tree-list2.png new file mode 100644 index 0000000..ec4a7c4 Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-list2.png differ diff --git a/docs/guide-zh-CN/images/code/tree-preview.png b/docs/guide-zh-CN/images/code/tree-preview.png new file mode 100644 index 0000000..79d7115 Binary files /dev/null and b/docs/guide-zh-CN/images/code/tree-preview.png differ diff --git a/docs/guide-zh-CN/images/demo/1.png b/docs/guide-zh-CN/images/demo/1.png new file mode 100644 index 0000000..f140c18 Binary files /dev/null and b/docs/guide-zh-CN/images/demo/1.png differ diff --git a/docs/guide-zh-CN/images/demo/10.png b/docs/guide-zh-CN/images/demo/10.png new file mode 100644 index 0000000..90c4623 Binary files /dev/null and b/docs/guide-zh-CN/images/demo/10.png differ diff --git a/docs/guide-zh-CN/images/demo/11.png b/docs/guide-zh-CN/images/demo/11.png new file mode 100644 index 0000000..ef5d41c Binary files /dev/null and b/docs/guide-zh-CN/images/demo/11.png differ diff --git a/docs/guide-zh-CN/images/demo/12.png b/docs/guide-zh-CN/images/demo/12.png new file mode 100644 index 0000000..ff37ff4 Binary files /dev/null and b/docs/guide-zh-CN/images/demo/12.png differ diff --git a/docs/guide-zh-CN/images/demo/2.png b/docs/guide-zh-CN/images/demo/2.png new file mode 100644 index 0000000..722d49e Binary files /dev/null and b/docs/guide-zh-CN/images/demo/2.png differ diff --git a/docs/guide-zh-CN/images/demo/3.png b/docs/guide-zh-CN/images/demo/3.png new file mode 100644 index 0000000..c5580ca Binary files /dev/null and b/docs/guide-zh-CN/images/demo/3.png differ diff --git a/docs/guide-zh-CN/images/demo/4.png b/docs/guide-zh-CN/images/demo/4.png new file mode 100644 index 0000000..be6746c Binary files /dev/null and b/docs/guide-zh-CN/images/demo/4.png differ diff --git a/docs/guide-zh-CN/images/demo/5.png b/docs/guide-zh-CN/images/demo/5.png new file mode 100644 index 0000000..a1302b1 Binary files /dev/null and b/docs/guide-zh-CN/images/demo/5.png differ diff --git a/docs/guide-zh-CN/images/demo/6.png b/docs/guide-zh-CN/images/demo/6.png new file mode 100644 index 0000000..46501dc Binary files /dev/null and b/docs/guide-zh-CN/images/demo/6.png differ diff --git a/docs/guide-zh-CN/images/demo/7.png b/docs/guide-zh-CN/images/demo/7.png new file mode 100644 index 0000000..7d11c0e Binary files /dev/null and b/docs/guide-zh-CN/images/demo/7.png differ diff --git a/docs/guide-zh-CN/images/demo/8.png b/docs/guide-zh-CN/images/demo/8.png new file mode 100644 index 0000000..6e54af8 Binary files /dev/null and b/docs/guide-zh-CN/images/demo/8.png differ diff --git a/docs/guide-zh-CN/images/demo/9.png b/docs/guide-zh-CN/images/demo/9.png new file mode 100644 index 0000000..0dc54fb Binary files /dev/null and b/docs/guide-zh-CN/images/demo/9.png differ diff --git a/docs/guide-zh-CN/images/issue/1.4.0.png b/docs/guide-zh-CN/images/issue/1.4.0.png new file mode 100644 index 0000000..8033846 Binary files /dev/null and b/docs/guide-zh-CN/images/issue/1.4.0.png differ diff --git a/docs/guide-zh-CN/images/issue/1.4.1.png b/docs/guide-zh-CN/images/issue/1.4.1.png new file mode 100644 index 0000000..91af762 Binary files /dev/null and b/docs/guide-zh-CN/images/issue/1.4.1.png differ diff --git a/docs/guide-zh-CN/images/issue/1.4.2.png b/docs/guide-zh-CN/images/issue/1.4.2.png new file mode 100644 index 0000000..0368196 Binary files /dev/null and b/docs/guide-zh-CN/images/issue/1.4.2.png differ diff --git a/docs/guide-zh-CN/images/issue/1.4.3.png b/docs/guide-zh-CN/images/issue/1.4.3.png new file mode 100644 index 0000000..3627d52 Binary files /dev/null and b/docs/guide-zh-CN/images/issue/1.4.3.png differ diff --git a/docs/guide-zh-CN/images/start-deploy-report.png b/docs/guide-zh-CN/images/start-deploy-report.png new file mode 100644 index 0000000..dc39c3a Binary files /dev/null and b/docs/guide-zh-CN/images/start-deploy-report.png differ diff --git a/docs/guide-zh-CN/images/sys-db-by.png b/docs/guide-zh-CN/images/sys-db-by.png new file mode 100644 index 0000000..4dd6c3e Binary files /dev/null and b/docs/guide-zh-CN/images/sys-db-by.png differ diff --git a/docs/guide-zh-CN/images/sys-db-by2.png b/docs/guide-zh-CN/images/sys-db-by2.png new file mode 100644 index 0000000..bf25dfa Binary files /dev/null and b/docs/guide-zh-CN/images/sys-db-by2.png differ diff --git a/docs/guide-zh-CN/images/sys-library-dict.png b/docs/guide-zh-CN/images/sys-library-dict.png new file mode 100644 index 0000000..421973f Binary files /dev/null and b/docs/guide-zh-CN/images/sys-library-dict.png differ diff --git a/docs/guide-zh-CN/images/sys-middleware-com-response.png b/docs/guide-zh-CN/images/sys-middleware-com-response.png new file mode 100644 index 0000000..26d46a5 Binary files /dev/null and b/docs/guide-zh-CN/images/sys-middleware-com-response.png differ diff --git a/docs/guide-zh-CN/images/sys-middleware-error-log.png b/docs/guide-zh-CN/images/sys-middleware-error-log.png new file mode 100644 index 0000000..224dfb5 Binary files /dev/null and b/docs/guide-zh-CN/images/sys-middleware-error-log.png differ diff --git a/docs/guide-zh-CN/start-deploy.md b/docs/guide-zh-CN/start-deploy.md new file mode 100644 index 0000000..decee16 --- /dev/null +++ b/docs/guide-zh-CN/start-deploy.md @@ -0,0 +1,108 @@ +## 生产部署 + +目录 + +- 编译配置 +- 编译 +- 修改生产配置文件 +- 启动服务 +- Nginx配置 + +### 编译配置 + +- 配置文件:server/hack/config.yaml,以下是默认配置 +```yaml +gfcli: + build: + name: "hotgo" # 编译后的可执行文件名称 + # arch: "all" #不填默认当前系统架构,可选:386,amd64,arm,all + # system: "all" #不填默认当前系统平台,可选:linux,darwin,windows,all + mod: "none" + cgo: 0 + packSrc: "resource" # 将resource目录打包进可执行文件,静态资源无需单独部署 + packDst: "internal/packed/packed.go" # 打包后生成的Go文件路径,一般使用相对路径指定到本项目目录中 + version: "" + output: "./temp/hotgo" # 可执行文件生成路径 + extra: "" +``` + + +### 编译 + +- 以下方式任选其一即可 + +1、 make一键编译 (确认已安装`make环境`) +```shell +cd server && make build +``` + +2、 按步骤手动编译 +```shell +cd server # 切换到服务端目录下 +rm -rf ./resource/public/admin/ # 删除之前的web资源 +mkdir ./resource/public/admin/ # 重新创建web资源存放目录,除首次编译后续可以跳过执行此步骤 +cd ../web && pnpm run build # 切换到web项目下,编译web项目 +\cp -rf ./dist/* ../server/resource/public/admin/ # 将编译好的web资源复制到server对应的资源存放路径下 +cd ../server # 切换回服务端目录下 +echo "y" | gf build # 编译hotgo服务端 + +# 不出意外你已经编译好了hotgo可执行文件! +``` + +3、分端编译 (多人开发时推荐) +```shell +# 编译服务端 +cd server # 切换到服务端目录下 +rm -rf ./resource/public/admin/ # 删除之前的web资源,如果开发环境没有这个目录可以忽略此步骤 +echo "y" | gf build # 编译hotgo服务端 + +# 编译web端 +cd web +pnpm run build 或 npm run build + +# web端编译完成后,将web/dist/*中的文件上传到`server`端线上运行目录:/resource/public/admin即可 +# 至此,web端和server端都可以独立覆盖更新 +``` + + +### 修改生产配置文件 +- 配置文件:server/manifest/config/config.yaml +> 如关闭debug、mode设为生产环境、修改数据库地址、缓存驱动、队列驱动、日志路径等 + + + +### 启动服务 +> 这里可以接使用gf官方推荐的启动方式,请参考:https://goframe.org/pages/viewpage.action?pageId=1114403 + + +### Nginx配置 +``` + # websocket + location = /socket { + proxy_pass http://127.0.0.1:8000/socket; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-NginX-Proxy true; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_connect_timeout 600s; + proxy_read_timeout 600; + proxy_send_timeout 600s; + } + + # http + location ^~ / { + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://127.0.0.1:8000/; + proxy_redirect off; + proxy_buffering off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection upgrade; + } +``` diff --git a/docs/guide-zh-CN/start-environment.md b/docs/guide-zh-CN/start-environment.md new file mode 100644 index 0000000..7ceabb1 --- /dev/null +++ b/docs/guide-zh-CN/start-environment.md @@ -0,0 +1,31 @@ +## 环境搭建 + +目录 + +- 前端环境 +- 后端环境 +- 使用说明 + +### 前端环境 + +1. 前往https://nodejs.org/zh-cn/下载当前版本node +2. 命令行运行 `node -v` 若控制台输出版本号则node安装成功 +3. node 版本需大于等于 `16.0` +4. 安装pnpm:`npm install -g pnpm` +5. 命令行运行 `pnpm -v` 若控制台输出版本号则前端环境搭建成功 + +### 后端环境 +1. 下载golang安装 版本号需>=1.23 +2. 国际: https://golang.org/dl/ +3. 国内: https://golang.google.cn/dl/ +4. 命令行运行 go 若控制台输出各类提示命令 则安装成功 输入 `go version` 确认版本大于1.23 +5. 开发工具推荐 [Goland](https://www.jetbrains.com/go/) + +### 使用说明 + +> 需要本地具有 git node golang 环境 + +- node版本 >= 20.0.0 +- golang版本 >= 1.23 +- mysql版本 >= 5.7,引擎需要是 innoDB 或 postgresql版本 >=14 +- IDE推荐:Goland diff --git a/docs/guide-zh-CN/start-installation.md b/docs/guide-zh-CN/start-installation.md new file mode 100644 index 0000000..e527318 --- /dev/null +++ b/docs/guide-zh-CN/start-installation.md @@ -0,0 +1,107 @@ +## 系统安装 + +目录 + +- 环境要求 +- 安装 + +### 环境要求 + +- node版本 >= v20.0.0 +- golang版本 >= v1.23 +- goframe版本 >=v2.9.4 +- mysql版本 >=5.7 或 postgresql版本 >=14 + +> 必须先看[环境搭建文档](start-environment.md),如果安装遇到问题务必先查看[常见问题文档](start-issue.md) + +### 安装 + + +一、克隆项目 + +``` +git clone https://github.com/bufanyun/hotgo.git && cd hotgo +``` + +二、配置你的站点信息 + +1、服务端: +- 项目数据库文件 `storage/data/hotgo.sql` 创建数据库并导入 +- 将配置文件 `manifest/config/config.example.yaml` 复制后改为`manifest/config/config.yaml` +- 将`manifest/config/config.yaml`中的`database.default.link`数据库配置改为你自己的: +```yaml +# Database. 配置参考:https://goframe.org/pages/viewpage.action?pageId=1114245 +database: + logger: + path: "logs/database" # 日志文件路径。默认为空,表示关闭,仅输出到终端 + <<: *defaultLogger + stdout: true + default: +# link: "mysql:账号:密码@tcp(127.0.0.1:3306)/数据库名?loc=Local&parseTime=true&charset=utf8mb4" + link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true&charset=utf8mb4" + debug: true + Prefix: "hg_" +``` + +- 将`hack/config.yaml`中的`gfcli.gen.dao[0].link`数据库配置改为你自己的: +```yaml +gfcli: + gen: + dao: + - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true&charset=utf8mb4" + group: "default" # 分组 使用hotgo代码生成功能时必须填 + # tables: "" # 指定当前数据库中需要执行代码生成的数据表。如果为空,表示数据库的所有表都会生成。 + tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。 + removePrefix: "hg_" + descriptionTag: true + noModelComment: true + jsonCase: "CamelLower" + gJsonSupport: true + clear: false +``` + +2、web前端: +- 配置服务端地址,包含在以下文件中: +* /hotgo/web/.env.development +* /hotgo/web/.env.production +* /hotgo/web/.env + + +三、 启动服务 + +1、服务端: +```shell + cd server + + # 设置国内代理,如果已经设置好了代理可以跳过 + go env -w GOPROXY=https://goproxy.io,direct + + # 更新包 + go mod tidy + + # 查看命令行方法 + go run main.go help + + # 启动所有服务 + go run main.go # 热编译启动: gf run main.go +``` + +2、web前端: +```shell + cd web + # 首先确定你以安装node16.0以上版本并安装了包[npm、pnpm],否则可能会出现一些未知报错 + + # 安装依赖 + pnpm install + + # 启动web项目 + pnpm run dev + + # 如果顺利,至此到浏览器打开:http://你的IP:8001/admin + # 登录账号:admin, 密码:123456 +``` + + + + + diff --git a/docs/guide-zh-CN/start-issue.md b/docs/guide-zh-CN/start-issue.md new file mode 100644 index 0000000..08017c0 --- /dev/null +++ b/docs/guide-zh-CN/start-issue.md @@ -0,0 +1,93 @@ +## 常见问题 + +目录 + +- 一、后台相关 +- 二、数据库相关 +- 三、环境相关 + + +### 一、后台相关 + +#### 1、连接超时,请刷新重试。如仍未解决请检查websocket连接是否正确! + +线上或非本地运行时,请到 系统设置 -> 配置管理 -> 基本设置 -> 找到网站域名和websocket地址,改成你自己实际的地址,保存刷新页面即可 + +#### 2、web页面菜单切换后页面出现白屏 + +请参考:https://github.com/jekip/naive-ui-admin/issues/183 + + +### 二、数据库相关 + +#### 1、安装数据库出现 json 报错不支持 + +请安装 mysql5.7 及以上版本的数据库。如果你使用的是mariadb请确认版本号,mariadb从 10.2 版本开始支持 json 数据类型。 + + + +### 三、环境相关 + +#### 1、not found in resource manager or following system searching paths + +> 报错信息:panic: possible config files "config" or "config.toml/yaml/yml/json/ini/xml/properties" not found in resource manager or following system searching paths: + +系统没有找到配置文件,将配置文件 `manifest/config/config.yaml.bak` 复制后改为`manifest/config/config.yaml` + + +#### 2、net.DialTimeout failed with network + +> 报错信息:connect to 127.0.0.1:8099 error: net.DialTimeout failed with network "tcp", address "127.0.0.1:8099", timeout "10s": dial tcp + +- http服务没有启动或正在启动 +- 通过一键启动所有服务运行时属正常情况,多服务启动时存在先后顺序问题,`tcpClient`比`tcpServer`先启动完成导致的,等`tcpServer`启动完成后会自动重连 + +详细请参考 - [系统安装](start-installation.md) + + +#### 3、cannot find "hack/config.yaml" in following paths: +> 报错信息:get cli configuration file:hack/config.yaml, err:cannot find "hack/config.yaml" in following paths: + +系统运行目录下配置hack/config.yaml文件。如果是生产环境运行,并且不需要开发工具相关功能,可以将`manifest/config/config.yaml`配置文件中的`system.mode`值改为`product`,这样启动时不会加载开发工具相关功能 + +#### 4、非超管角色提示:你没有访问权限! +![1.4.0.png](./images/issue/1.4.0.png) + +此问题因当前角色访问的页面包含未分配权限的接口所致,在多角色场景中较为常见,解决步骤如下: + +1. 定位缺失权限的接口 +- 用超管账号进入系统应用 -> 日志管理 -> 全局日志,搜索状态码为`62 无访问权限`的记录 +- 示例:若请求接口为`/admin/hgexample/treeTable/list`,需处理的权限路径为`/hgexample/treeTable/list`(去除`/admin`前缀) + +![1.4.1.png](./images/issue/1.4.1.png) + +假如查到请求接口是:`/admin/hgexample/treeTable/list`,那需要分配的菜单权限就是:`/hgexample/treeTable/list`,要把`/admin`去掉 + +2. 配置菜单权限 +- 进入权限管理 -> 菜单权限: +- 若目标菜单不存在,先新增菜单 +- 若菜单已存在但未配置接口权限,就把步骤1中的权限路径添加到【分配权限】中(已配置则跳过) + +![1.4.2.png](./images/issue/1.4.2.png) + +3. 分配权限给角色 +- 进入权限管理 -> 角色权限,找到对应角色并点击【菜单权限】 +- 勾选步骤2中配置的菜单权限,保存即可 + +![1.4.3.png](./images/issue/1.4.3.png) + +### 四、前端相关 + +#### 1、Error: connect ECONNREFUSED ::1:8000 + +```text +11:44:52 [vite] http proxy error at /member/info: +Error: connect ECONNREFUSED ::1:8000 + at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1246:16) +``` + +- 服务端没有启动 +- `.\wen\.env.development`中的`VITE_PROXY`配置的服务器地址或端口与实际不一致 + + + diff --git a/docs/guide-zh-CN/start-questions.md b/docs/guide-zh-CN/start-questions.md new file mode 100644 index 0000000..39374f2 --- /dev/null +++ b/docs/guide-zh-CN/start-questions.md @@ -0,0 +1,21 @@ +## 如何提问 + +#### 说明环境情况 + +- 平台: windows/linux +- 软件与版本:golang 1.19, Mysql 5.7 ... +- 系统版本:hotgo 2.6.2 + +#### 你做了什么? + +- 我按文档(附链接)上写了一个控制器xxx,代码如下:xxxx +- 然后我使用xxx访问 +- 这其中我还做了啥 +- ... + +#### 出现什么错误? + +- 当我试图xxx的时候,xxx报了如下错误:(截图) +- 我确认过我的目录权限是正确的,xxx是正确的(必要时带上截图) + +以上是举例提问时所应该描述的情况,当别人看到详细的情况时,也便于快速定位问题所在。 \ No newline at end of file diff --git a/docs/guide-zh-CN/start-update-log.md b/docs/guide-zh-CN/start-update-log.md new file mode 100644 index 0000000..6711f6c --- /dev/null +++ b/docs/guide-zh-CN/start-update-log.md @@ -0,0 +1,333 @@ +## 更新历史 + +升级说明: + +> 创建一个Git版本库,并创建二个分支比如A、B,A分支默认为你的二开分支、B分支为你的版本库分支。 +> 等需要升级的时候,把版本库分支(B)升级为最新的版本,然后切换到A分支,把B分支合并过来,解决掉合并冲突即可。 + +注意: + +> 各个小版本升级例如 2.1.x 升级到 2.2.x 以上不能完美升级最好重新安装 + +> 如果升级(覆盖)代码后打开会出现 sql 报错, 请检查更新的数据库格式或自行调整 + + +### v2.18.9 +updated 2026.5.9 + +- 新增:新增 `AGENTS.md` 项目开发规范,统一前后端开发约定,作为 AI 辅助开发的唯一事实来源 +- 优化:naive-ui版本升级到2.44.1 +- 优化:gf版本升级到v2.10.0 + + +### v2.18.6 +updated 2025.10.25 + +- 新增:国际化(i18n)支持,内置简体中文、繁体中文、英文三种语言包 +- 新增:附件管理支持外链图片转存至系统存储驱动 +- 优化:全局微信公众号实例初始化逻辑,`AccessToken` 获取方式调整为稳定版接口 +- 优化:菜单权限搜索功能,支持按菜单名称或路由地址进行检索 +- 优化:角色权限管理中的菜单权限分配交互体验 +- 优化:naive-ui版本升级到2.43.1 +- 优化:gf版本升级到v2.9.4 +- 修复:删除角色导致关联用户无法登录的问题,新增角色删除前置校验(检查是否存在关联用户) + + +### v2.17.8 +updated 2025.7.13 + +- 优化:表格排序处理器兼容关联表别名 +- 优化:优化动态统计数字在0值时显示问题 +- 优化:优化首页快捷菜单点击事件范围 +- 优化:优化短信、邮件验证码相关验证类数据排序 +- 优化:优化Nginx配置,支持流式请求和兼容部分版本无法匹配到websocket规则问题 +- 优化:naive-ui版本升级到2.42.0 +- 优化:vue3-json-viewer版本升级到2.4.1 +- 修复:修复可选用户选项`Fields`生成错误问题 + + +### v2.16.10 +updated 2025.3.22 + +- 增加:增加组合下拉用户筛选组件 +- 优化:文件上传类型限制改为统一由后台配置控制 +- 优化:websocket重连时间调整为10秒,初始化数据库中的网站地址调整为`127.0.0.1` +- 优化:优化访问日志字段展示方式 +- 优化:系统公告发送编辑模态框调整为抽屉 +- 优化:gf版本升级到v2.9.0 +- 优化:naive-ui版本升级到2.41.0 +- 修复:修复vue路由`Redirect`命名重复问题 +- 修复:优化代码生成在插件中`使用生成字典选项`时model包名重复问题 + + +### v2.15.11 +updated 2024.11.27 + +- 增加:增加配置管理子页面选项参数记忆 +- 修复:修复定时任务中日志配置的读取并发读问题 +- 优化:gf版本升级到v2.8.2 +- 优化:优化数据卡片展示 + + +### v2.15.7 +updated 2024.7.21 + +- 增加:访问日志、服务日志增加关键词搜索 +- 增加:web端增加字典状态管理,重构字典选项使用方式,大幅减少冗余代码 +- 修复:修复生成代码选项式树表已知的一些小bug +- 优化:gf版本升级到v2.7.2 +- 优化:naive-ui版本升级到2.39.0 +- 优化:访问日志不再记录过大的请求头参数,减少日志大小 + + +### v2.15.1 +updated 2024.4.22 + +- 增加:生成代码增加树表生成、字段排序、字段重置、操作人维护,调整关联表生成方式 +- 增加:角色菜单权限增加一键导入支持 +- 增加:访问日志增加忽略请求方式 +- 增加:插件生成增加`consts`目录维护 +- 增加:IP归属地定位增加默认重试,增加新的缓存机制 +- 增加:登录日志增加UA信息、IP归属信息,解藕访问日志 +- 增加:消息队列`rocketmq`驱动更新版本问题,增加延迟队列,支持自动创建主题 +- 增加:部门增加类型支持,通过部门类型实现多租户业务支持 +- 增加:增加多租户业务开发演示 +- 修复:修复部门列表搜索算法在部分情况下无效问题 +- 修复:修复短信、邮件IP发送数量限制 +- 修复:修复分布式锁内存泄漏问题 +- 优化:gf版本升级到v2.7.0 +- 优化:naive-ui版本升级到2.38.1 +- 优化:优化`cmd`服务退出流程 +- 优化:优化生成代码格式化和移除未使用的包 +- 优化:服务端配置文件参数`hotgo`改为`system` +- 优化:web端表格/表单增加自适应宽度计算 +- 优化:日期选择组件不再使用默认值 +- 优化:优化菜单添加/编辑表单,使其操作更加简单方便 + + +### v2.13.1 +updated 2024.3.7 + +- 增加:增加内置数据字典类型:`枚举字典`和`自定义方法字典`,支持代码生成时关联选项使用 +- 增加:增加大文件上传,支持分片上传、断点续传,存储驱动已适配`本地存储` +- 增加:插件模块增加停止服务回调接口,调整静态资源默认存放位置,创建插件选项增加可选扩展功能 +- 增加:功能案例插件增加`30+`常用组件示例,增加`websocket`消息收发测试 +- 增加:文档增`加功能扩展库`、`websocket服务器`、`websocket客户端`使用说明,当前版本文档已完善 +- 修复:修复省市区无法添加地区问题 +- 优化:gf版本升级到v2.6.4 +- 优化:优化缓存组件依赖关系 +- 优化:调整部分前端表格自适应宽度 +- 优化:HTTP错误码接管统一改为由响应中间件处理 + +### v2.12.1 +updated 2023.12.29 + +- 修复:修复访问日志权限过滤 +- 优化:gf版本升级到v2.6.1 +- 优化:naive-ui版本升级到2.36.0,vue版本升级到3.4.0,vite版本升级到4.2.7 +- 优化:优化curd代码生成,编辑表单增加自适应,详情改为`抽屉`,修复菜单权限关系树,简化`State` +- 优化:优化字典编辑和查询 +- 优化:优化角色列表数据加载缓慢问题 + +### v2.11.5 +updated 2023.11.25 + +- 增加`minio`上传驱动支持 +- 增加定时任务调度日志,增加同方法多任务支持 +- 增加gf运行模式控制,生产环境时隐藏开发相关菜单 +- 增加树形表格新增/修改自动维护上下级关系通用处理 +- 增加树形表格使用例子 +- 优化:gf版本升级到v2.5.7 +- 优化`websocket`默认路径配置 +- 优化预处理中间件错误码跟随 +- 优化插件静态目录为可选项 +- 修复升级gf版本后服务日志记录内容为空问题 +- 修复角色权限选择数据重复问题 +- 修复服务日志无日志内容问题 +- 修复用户批量删除可以删除超管问题 +- 修复代码生成时偶尔找不到主键索引问题 +- 修复树形选项组件在`BasicForm`无法使用问题 +- 修复表格批量删除重复传参问题 +- 修复文件缓存`GetOrSet`和`GetOrSetFunc`缓存过期不刷新问题 + +### v2.9.8 +updated 2023.10.21 + +- 优化:gf版本升级到v2.5.5 +- 优化:优化代码生成表单滑动 +- 优化:优化字典列表编辑生效时机 +- 修复:修复日期组件默认值无效问题 +### v2.9.3 +updated 2023.10.08 + +- 优化:gf版本升级到v2.5.4 +- 优化:优化上传文件资源复用策略 +- 修复:修复登录角色的部门状态检查 +- 修复:修复配置参数`exceptLogin`命名 + +### v2.8.9 +updated 2023.09.06 + +- 优化:gf版本升级到v2.5.2 +- 优化:优化代码生成,自定义模板时自动过滤前缀,菜单权限生成预检查 +- 优化:服务日志内容长度超出最大限制截取 +- 修复:修复tcp服务器定时任务执行异常 +- 修复:修复文件选择器错误传参导致的显示异常问题 + +### v2.8.4 +updated 2023.07.20 + +- 增加:增加输入预处理中间件 +- 增加:增加文件选择器 +- 增加:增加在线服务监控,服务许可证管理 +- 增加:TCP服务器增加RPC路由消息注册、路由分发、拦截器注册,更方便的进行应用开发 +- 优化:gf版本升级到v2.5.0 +- 优化:优化CURD代码生成,简化控制器代码逻辑,升级列表数据查询方式 +- 修复:修复角色菜单子权限取消导致父级权限失效问题 +- 修复:修复上传驱动路径错误 +- 修复:修复CURD代码生成时间类型字段默认值获取异常 + +### v2.7.6 +updated 2023.06.19 + +- 优化:gf版本升级到v2.4.3 +- 修复:部门管理查询空指针问题 +- 优化:附件md5生成方式调整(与更新前已上传的文件md5不兼容,如果业务有影响请注意调整) + +### v2.7.3 +updated 2023.05.14 + +- 增加:增加注册、手机号登录、二维码邀请注册功能 +- 优化:优化管理员业务模块验证唯一属性接口,将登录相关功能拆分到管理员基础模块中 +- 移除:移除web端mock插件 + +### v2.6.10 +updated 2023.05.12 + +- 增加:增加jwt+缓存驱动实现的令牌认证,支持自动续约、可控过期失效策略 +- 移除:移除旧jwt功能库 +- 移除:移除`decimal`、`mahonia` package + +### v2.6.7 +updated 2023.05.10 + +- 增加:增加支付网关:集成支付宝、微信支付、QQ支付,可通过后台直接配置参数 +- 增加:增加资金管理模块,包含在线充值、申请提现、资金变动明细功能 +- 增加:增加redis延迟队列 +- 增加:tcp服务增加rpc协议通讯支持 +- 增加:通过微信访问后台自动获取openid,后续微信相关业务使用 +- 优化:gf版本升级到v2.4.1 +- 优化:优化CURD代码生成单表列表 +- 优化:优化菜单权限路由,解决偶尔修改权限不能实时生效的情况 +- 优化:优化后台导出功能搜索参数无效问题 +- 优化:将定时任务拆分成可独立部署的程序,通过tcp长连接方式与后台交互 +- 修复:修复相同文件多次上传页面无响应问题 +- 修复:修复新用户不更新活跃时间问题 +- 修复:修复本地缓存当缓存不存在时的异常错误问题 +- 修复:修复访问日志偶尔出现无法记录问题 + +### v2.5.3 +updated 2023.04.10 + +- 优化:优化IP地理位置定位,增加地理位置缓存,避免相同IP并发操作 +- 优化:优化日志管理中相关列表字段过多在小屏设备列表不显示滚动条情况 +- 增加:增加短信驱动:腾讯云短信,可通过后台一键切换 +- 修复:修复角色权限选择自定义部门时,部门选项无法显示问题 + +### v2.4.9 +updated 2023.04.05 + +- 优化:优化全局日志数据加载和分页加载,大分页增加分批次加载 +- 优化:优化TCP服务认证,超出多端登录上限时直接踢掉所有在线让其重连 +- 增加:增加上传驱动:腾讯云cos、阿里云oss、七牛云对象存储 +- 优化:优化代码生成文档,数据库文档增加多数据库生成配置 +- 修复:修复系统监控在苹果m1芯片空指针以及显示问题 + +### v2.4.4 +updated 2023.03.16 + +- 优化:优化代码生成多库生成时的菜单sql默认指向默认数据库分组 +- 优化:优化TCP服务认证机制 + +### v2.4.2 +updated 2023.03.11 + +- 修复:修复字典管理列表无法添加/编辑问题 +- 优化:优化代码生成文档 +- 优化:优化部门树形选项排序问题 +- 优化:优化基础设置中的LOGO图片上传组件 +- 优化:优化黑名单IP过滤策略 +- 增加:增加基于gtcp实现的C/S服务器,为后续多服务通讯建立基础库 +- 优化:gf版本升级到v2.3.3 + +### v2.3.5 +updated 2023.02.26 + +- 优化: 调整消息队列消费初始化方式,支持在插件模块下注册消费者 +- 修复:后台用户添加模态框无法正常显示问题 +- 优化:创建新插件生成增加web页面的生成 +- 增加:增加生产部署、开发规范、生成代码、表单组件等相关文档 +- 修复:修复后台已知的一些小bug + +### v2.2.10 +updated 2023.02.23 + +- 增加: 增加插件管理、设计新插件等插件模块 +- 增加: 增加插件应用:`功能案例` +- 增加: 增加使用文档 +- 优化: 生成代码适配插件模块 +- 优化:消息队列增加磁盘队列,默认队列从`redis`改为`disk` +- 优化:缓存驱动增加文件适配器,默认驱动从`redis`改为`file`,项目安装不再依赖`redis` +- 优化:优化系统监控加载流程 +- 优化:gf版本升级到v2.3.2 +- 修复:生成代码刷新后偶尔出现加载失败问题 + +### v2.2.1 +updated 2023.01.12 + +- 优化: 缓存清理各个应用的缓存文件夹读写判断 +- 修复: Linux 环境下创建插件报找不到模板文件 +- 修复: Excel 导入找不到最后一列数据 +- 增加: 无权限菜单不显示 +- 优化: 省市区数据为最新的 2023.01.11 的国家统计局省市区数据 +- 优化: 前台关于图片上传的 js 和 css 引入,避免资源依赖找不到 +- 优化: 插件模块查询机制, 增加数据缓存依赖,依赖时间为 360 秒 +- 优化: 文件上传的处理 +- 修复: 网站配置的多图上传引入路径错误 +- 修复: 定时任务协程问题导致执行失败 +- +### v2.1.1 +updated 2022.9.26 + +- 优化: 多图上传的显示样式及功能 +- 修复: 由于上传视频和语音开启了全域名返回导致的上传到服务器错误 +- 增加: 由增加home页面入口,前台页面 + +### v2.0.8 +updated 2022.9.21 + +- 优化: 优化菜单和角色数据权限,支持数据权限和按钮细分权限 +- 优化: 优化附件上传,增加文件上传选项 + +### v2.0.6 +updated 2022.9.18 + +- 增加: 增加阿里云短信配置和发送短信API +- 增加: 增加高德地图配置 +- 优化: 优化数据字典,增加数据类型和标签支持 +- +### v2.0.3 +updated 2022.9.10 + +- 2.0 全新上线 + +### v2.0.2 +updated 2022.6.11 + +- 增加: 增加生成代码功能,目前已支持一键生成CURD和关联表 +- 增加: 增加云存储UCloud配置,后台一键切换驱动 + +### v2.0.0 +updated 2022.5.20 + + - 初始化: 2.0 基础框架 diff --git a/docs/guide-zh-CN/sys-auth.md b/docs/guide-zh-CN/sys-auth.md new file mode 100644 index 0000000..27ac252 --- /dev/null +++ b/docs/guide-zh-CN/sys-auth.md @@ -0,0 +1,155 @@ +## 权限控制 + +目录 + +- 一、菜单权限 +- 二、数据权限 + + +### 一、菜单权限 + +> 菜单权限可以控制web后台菜单功能,每个菜单可以绑定一个或多个API权限,API权限决定了用户是否可以调用服务端接口。 + +#### 为角色添加菜单权限 + +1、HotGo后台 -> 权限管理 -> 菜单权限 -> 找到添加菜单,按照表单提示添加你的菜单信息。 + +2、添加菜单完成后,到 权限管理 -> 角色权限 -> 找到一位已有角色或者添加新角色 -> 在列表右侧操作栏找到菜单权限打开 -> 将第1步中添加的菜单分配给该角色即可。 + +#### 细粒度权限 + +> 一般用于web后台页面上的功能按钮,对不同的角色展示不同的按钮时会用到。 + +1、参考【为角色添加菜单权限】添加一个按钮类型的菜单。并为菜单分配API权限,假设你分配权限是:/test/index + +2、在web页面中创建一个按钮,如下: + +```vue + + + + +``` + +#### 菜单表主要字段解释 + +- 代码片段:[server/internal/model/input/adminin/menu.go](../../server/internal/model/input/adminin/menu.go) + +```go +type MenuRouteMeta struct { + // 解释参考:https://naive-ui-admin-docs.vercel.app/guide/router.html#%E5%A4%9A%E7%BA%A7%E8%B7%AF%E7%94%B1 + Title string `json:"title"` // 菜单名称 一般必填 + // Disabled bool `json:"disabled,omitempty"` // 禁用菜单 + Icon string `json:"icon,omitempty"` // 菜单图标 + KeepAlive bool `json:"keepAlive,omitempty"` // 缓存该路由 + Hidden bool `json:"hidden,omitempty"` // 隐藏菜单 + Sort int `json:"sort,omitempty"` // 排序越小越排前 + AlwaysShow bool `json:"alwaysShow,omitempty"` // 取消自动计算根路由模式 + ActiveMenu string `json:"activeMenu,omitempty"` // 当路由设置了该属性,则会高亮相对应的侧边栏。 + // 这在某些场景非常有用,比如:一个列表页路由为:/list/basic-list + // 点击进入详情页,这时候路由为/list/basic-info/1,但你想在侧边栏高亮列表的路由,就可以进行如下设置 + // 注意是配置高亮路由 `name`,不是path + IsRoot bool `json:"isRoot,omitempty"` // 是否跟路由 顶部混合菜单,必须传 true,否则左侧会显示异常(场景就是,分割菜单之后,当一级菜单没有子菜单) + FrameSrc string `json:"frameSrc,omitempty" ` // 内联外部地址 + Permissions string `json:"permissions,omitempty"` // 菜单包含权限集合,满足其中一个就会显示 + Affix bool `json:"affix,omitempty"` // 是否固定 设置为 true 之后 多页签不可删除 + Type int `json:"type"` // 菜单类型 +} +``` + + +#### API权限验证流程 + +```mermaid + +graph TD + A(用户请求API) --> B(验证中间件
server/internal/logic/middleware/admin_auth.go) + B -->|没有登录,但API需要登录| H(提示登录) + B -->|没有登录,但API不需要登录| G(验证通过,进入业务) + B -->|已登录| C(检查用户角色是否拥有API权限
server/internal/logic/admin/role.go,方法:Verify) -->|有权限| G(验证通过,进入业务) + C -->|API无需验证权限| G(验证通过,进入业务流程) + C -->|无权限| D(提示无权限) +``` + +#### 菜单权限添加或修改后多久生效? + +- API权限实时生效,web后台菜单刷新页面后生效,无需重启服务 + + +### 二、数据权限 + +> 数据权限是某人只能看到某些数据,可以为某人设置一定的数据范围,让其只能看到允许他看到的数据。 + +> 例如:公司存在多个销售部门,不同的部门仅能查看到自己所属部门下的数据;再如:用户有多级代理商,只能查看自己和自己下级的数据。 + +#### 目前已经支持的数据权限如下 + +| 数据范围 | 描述 | +|---------|--------------------------------------------| +| 全部权限 | 不做过滤,可以查看所有数据 | +| 当前部门 | 仅可以看到所属部门下的数据 | +| 当前以及下部门 | 仅可以看到所属以及下级部门的数据 | +| 自定义部门 | 可以看到设置的指定部门,支持多个 | +| 仅自己 | 仅可以看到自己的数据 | +| 自己和直属下级 | 仅可以看到自己和直属下级的数据,直属下级是指当前用户往下1级的用户 | +| 自己和全部下级 | 仅可以看到自己和全部下级的数据,全部下级是指当前用户所有的下级用户,包含其下级的下级 | + +#### 如何区分部门和下级用户? + +- 在实际使用时,部门更多的是在公司或机构中使用,可以通过在 组织管理 -> 后台用户 ->为用户绑定部门 +- 下级用户在代理商或分销系统中比较常见,后台用户由谁添加的,那么被添加的用户就是其下级用户 + +#### 如何判断数据是谁的? + +- 在数据库建表时,只要表中包含字段:`created_by`或`member_id`即可过滤 [仅自己、自己和直属下级、自己和全部下级] 类型的数据权限 + +#### 如何在具体业务中使用数据权限过滤? + +- 只需在查询、更新或删除等ORM链式操作中加入相应`Handler`方法即可 + +- 如果你想对Handler有更多详细了解,请查看:https://goframe.org/pages/viewpage.action?pageId=20087340 + +- 下面提供一个简单使用例子: + +```go +package main + +import ( + "context" + "hotgo/internal/dao" + "hotgo/internal/library/hgorm/handler" +) + +func test(ctx context.Context) { + dao.AdminPost.Ctx(ctx).Where("id", 1).Handler(handler.FilterAuth).Scan(&res) +} +``` + +- 如果表中没有字段:`created_by`或`member_id`,也可以使用自定义字段方法来过滤数据权限,如下: + +```go +package main + +import ( + "context" + "hotgo/internal/dao" + "hotgo/internal/library/hgorm/handler" +) + +func test(ctx context.Context) { + dao.AdminPost.Ctx(ctx).Where("id", 1).Handler(handler.FilterAuthWithField("test_field")).Scan(&res) +} +``` + + +- 过滤方法实现请参考:[server/internal/library/hgorm/handler/filter_auth.go](../../server/internal/library/hgorm/handler/filter_auth.go) + + diff --git a/docs/guide-zh-CN/sys-catalog.md b/docs/guide-zh-CN/sys-catalog.md new file mode 100644 index 0000000..bf9d040 --- /dev/null +++ b/docs/guide-zh-CN/sys-catalog.md @@ -0,0 +1,176 @@ +## 目录结构 + +目录 + +- 服务端 +- web前端 +- uniapp端(待开放) + + +#### 服务端 +``` +/server +├── addons +│ ├── modules +│ ├── xxx插件 +│ | ├── api +│ | ├── controller +│ | ├── crons +│ | ├── global +│ | ├── logic +│ | ├── model +│ | ├── queues +│ | ├── resource +│ | ├── router +│ | ├── service +│ | ├── main.go +│ | └── README.md +├── api +│ ├── admin +│ ├── api +│ ├── home +│ ├── websocket +├── hack +├── internal +│ ├── cmd +│ ├── consts +│ ├── controller +│ ├── crons +│ ├── dao +│ ├── global +│ ├── library +│ ├── logic +│ ├── model +│ | ├── do +│ │ ├── entity +│ │ └── input +│ ├── packed +│ ├── queues +│ ├── router +│ ├── service +│ └── websocket +├── manifest +├── resource +├── storage +├── utility +├── go.mod +├── main.go +├── Makefile +└── README.md +``` + +| 目录 | 描述 | +|--------------------------|-----------------------------------------------------------------| +| 基于gf的工程目录结构做了部分调整 | 参考地址: https://goframe.org/pages/viewpage.action?pageId=30740166 | +| **addons** | 所有的插件模块都放在这里 | +| --- modules | 为插件模块提供隐式初始化 | +| --- xxx插件 | 插件模块名称 | +| --- --- api | 对外接口。提供服务的输入/输出数据结构定义 | +| --- --- --- admin | 后台接口 | +| --- --- --- api | 前台通用接口,包含PC端、移动端接口等 | +| --- --- --- home | 前台PC端、H5端页面 | +| --- --- --- websocket | 可同时为多应用提供websocket接口 | +| --- --- consts | 插件内主要的常量定义 | +| --- --- controller | 接收/解析用户输入参数的入口/接口层,也可以理解为控制器 | +| --- --- crons | 项目中由系统统一接管的定时任务处理 | +| --- --- global | 项目内主要的全局变量和系统的一些初始化操作 | +| --- --- logic | 业务逻辑封装管理,特定的业务逻辑实现和封装往往是项目中最复杂的部分 | +| --- --- model | 数据结构管理模块,管理数据实体对象,以及输入与输出数据结构定义 | +| --- --- --- input | 对内接口。用于controller调用service或service之间调用时的输入/输出结构定义,以及输入过滤和预处理 | +| --- queues | 为项目内所有的消息队列的消费者提供统一的初始化和处理 | +| --- resource | 静态资源文件。这些文件往往可以通过 资源打包/镜像编译 的形式注入到发布文件中 | +| --- --- router | 注册对外接口和分组中间件 | +| --- --- service | 用于业务模块解耦的接口定义层具体的接口实现在logic中进行注入 | +| --- main.go | 插件始化文件和模块插拔接口 | +| **api** | 对外接口。提供服务的输入/输出数据结构定义 | +| --- admin | 后台接口 | +| --- api | 前台通用接口,包含PC端、移动端接口等 | +| --- home | 前台PC端、H5端页面 | +| --- websocket | 可同时为多应用提供websocket接口 | +| **hack** | 存放项目开发工具、脚本等内容例如,CLI工具的配置,各种shell/bat脚本等文件 | +| **internal** | 业务逻辑存放目录通过Golang internal特性对外部隐藏可见性 | +| --- cmd | 命令行管理目录可以管理维护多个命令行 | +| --- consts | 项目内主要的常量定义 | +| --- controller | 接收/解析用户输入参数的入口/接口层,也可以理解为控制器 | +| --- crons | 项目中由系统统一接管的定时任务处理 | +| --- dao | 数据访问对象,这是一层抽象对象,用于和底层数据库交互,仅包含最基础的 CURD 方法 | +| --- global | 项目内主要的全局变量和系统的一些初始化操作 | +| --- library | 项目内常用功能的扩展库 | +| --- logic | 业务逻辑封装管理,特定的业务逻辑实现和封装往往是项目中最复杂的部分 | +| --- model | 数据结构管理模块,管理数据实体对象,以及输入与输出数据结构定义 | +| --- --- do | 用于dao数据操作中业务模型与实例模型转换,由工具维护,用户不能修改 | +| --- --- entity | 与数据集合绑定的程序数据结构定义,通常和数据表一一对应 | +| --- --- input | 对内接口。用于controller调用service或service之间调用时的输入/输出结构定义,以及输入过滤和预处理 | +| --- packed | 将静态资源打包进可执行文件,无需单独部署 | +| --- queues | 为项目内所有的消息队列的消费者提供统一的初始化和处理 | +| --- router | 注册对外接口和分组中间件 | +| --- service | 用于业务模块解耦的接口定义层具体的接口实现在logic中进行注入 | +| **manifest** | 包含程序编译、部署、运行、配置的文件常见内容如下: | +| --- config | 配置文件存放目录 | +| --- docker | Docker镜像相关依赖文件,脚本文件等等 | +| --- deploy | 部署相关的文件默认提供了Kubernetes集群化部署的Yaml模板,通过kustomize管理 | +| **resource** | 静态资源文件。这些文件往往可以通过 资源打包/镜像编译 的形式注入到发布文件中 | +| **storage** | 本地数据存储目录,例如文件缓存、磁盘队列数据、sql数据文件、SSL证书等 | +| **utility** | 一些常用的工具方法 | +| go.mod | 使用Go Module包管理的依赖描述文件 | +| main.go | 程序入口文件 | +| Makefile | 程序构建发布和开发快捷指令 | +| README.md | 项目介绍文件 | + + + + +#### web前端 +``` +/web +├── build # 打包脚本相关 +│ ├── config # 配置文件 +│ ├── generate # 生成器 +│ ├── script # 脚本 +│ └── vite # vite配置 +├── mock # mock文件夹 +├── public # 公共静态资源目录 +├── src # 主目录 +│ ├── api # 接口文件 +│ ├── assets # 资源文件 +│ │ ├── icons # icon sprite 图标文件夹 +│ │ ├── images # 项目存放图片的文件夹 +│ │ └── svg # 项目存放svg图片的文件夹 +│ ├── components # 公共组件 +│ ├── design # 样式文件 +│ ├── directives # 指令 +│ ├── enums # 枚举/常量 +│ ├── hooks # hook +│ │ ├── component # 组件相关hook +│ │ ├── core # 基础hook +│ │ ├── event # 事件相关hook +│ │ ├── setting # 配置相关hook +│ │ └── web # web相关hook +│ ├── layouts # 布局文件 +│ │ ├── default # 默认布局 +│ │ ├── iframe # iframe布局 +│ │ └── page # 页面布局 +│ ├── locales # 多语言 +│ ├── logics # 逻辑 +│ ├── main.ts # 主入口 +│ ├── router # 路由配置 +│ ├── settings # 项目配置 +│ │ ├── componentSetting.ts # 组件配置 +│ │ ├── designSetting.ts # 样式配置 +│ │ ├── encryptionSetting.ts # 加密配置 +│ │ ├── localeSetting.ts # 多语言配置 +│ │ ├── projectSetting.ts # 项目配置 +│ │ └── siteSetting.ts # 站点配置 +│ ├── store # 数据仓库 +│ ├── utils # 工具类 +│ └── views # 页面 +├── types # 类型文件 +├── vite.config.ts # vite配置文件 +└── windi.config.ts # windcss配置文件 +``` + + +#### uniapp端 +``` +// 待开放 +``` \ No newline at end of file diff --git a/docs/guide-zh-CN/sys-code.md b/docs/guide-zh-CN/sys-code.md new file mode 100644 index 0000000..d76d2a3 --- /dev/null +++ b/docs/guide-zh-CN/sys-code.md @@ -0,0 +1,379 @@ +## 代码生成 + +目录 + +- 使用条件 +- 模板配置 +- CLI配置 +- 生成CRUD表格 +- 生成树形表格 +- 多数据库使用 +- 自定义生成模板 +- 内置gf-cli +- 指定gf-cli版本 +- 指定数据库驱动 + + +> 在HotGo中可以通过后台开发工具快速的一键生成CRUD,自动生成Api、控制器、业务逻辑、Web页面、表单组件、菜单权限等。 + + +### 使用条件 + +- hotgo 版本号 >= 2.13.1 +- 使用前必须先看 [数据库](sys-db.md) + + +### 生成模板配置 + +- 配置路径:server/manifest/config/config.yaml +```yaml +# 生成代码 +hggen: + allowedIPs: [ "127.0.0.1", "*" ] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能 + selectDbs: [ "default" ] # 可选生成表的数据库配置名称,支持多库 + disableTables: [ "hg_sys_gen_codes","hg_admin_role_casbin" ] # 禁用的表,禁用以后将不会在选择表中看到 + delimiters: [ "@{", "}" ] # 模板引擎变量分隔符号 + # 生成应用模型,所有生成模板允许自定义,可以参考default模板进行改造 + application: + # CRUD和关系树列表模板 + crud: + templates: + # 默认的主包模板 + - group: "default" # 分组名称 + isAddon: false # 是否为插件模板 false|true + masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联 + templatePath: "./resource/generate/default/curd" # 模板路径 + apiPath: "./api/admin" # goApi生成路径 + controllerPath: "./internal/controller/admin/sys" # 控制器生成路径 + logicPath: "./internal/logic/sys" # 主要业务生成路径 + inputPath: "./internal/model/input/sysin" # 表单过滤器生成路径 + routerPath: "./internal/router/genrouter" # 生成路由表路径 + sqlPath: "./storage/data/generate" # 生成sql语句路径 + webApiPath: "../web/src/api" # webApi生成路径 + webViewsPath: "../web/src/views" # web页面生成路径 + + # 默认的插件包模板,{$name}会自动替换成实际的插件名称 + - group: "addon" # 分组名称 + isAddon: true # 是否为插件模板 false|true + masterPackage: "sys" # 主包名称,需和controllerPath、logicPath、inputPath保持关联 + templatePath: "./resource/generate/default/curd" # 模板路径 + apiPath: "./addons/{$name}/api/admin" # goApi生成路径 + controllerPath: "./addons/{$name}/controller/admin/sys" # 控制器生成路径 + logicPath: "./addons/{$name}/logic/sys" # 主要业务生成路径 + inputPath: "./addons/{$name}/model/input/sysin" # 表单过滤器生成路径 + routerPath: "./addons/{$name}/router/genrouter" # 生成路由表路径 + sqlPath: "./storage/data/generate/addons" # 生成sql语句路径 + webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径 + webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径 + + # 消息队列模板 + queue: + templates: + - group: "default" + templatePath: "./resource/generate/default/queue" + + # 定时任务模板 + cron: + templates: + - group: "default" + templatePath: "./resource/generate/default/cron" + + # 生成插件模块,通过后台创建新插件时使用的模板,允许自定义,可以参考default模板进行改造 + addon: + srcPath: "./resource/generate/default/addon" # 生成模板路径 + webApiPath: "../web/src/api/addons/{$name}" # webApi生成路径 + webViewsPath: "../web/src/views/addons/{$name}" # web页面生成路径 +``` + +### 生成CLI配置 + +- hotgo在生成dao、service配置时,默认了和gf官方一致的配置方式和代码生成规则。所以无论你是通过hotgo亦或gf命令生成,最终代码格式完全一致,遵循一致的代码规范。 + +- 配置路径:[server/hack/config.yaml](../../server/hack/config.yaml) + +```yaml +gfcli: + build: + name: "hotgo" # 编译后的可执行文件名称 + # arch: "all" #不填默认当前系统架构,可选:386,amd64,arm,all + # system: "all" #不填默认当前系统平台,可选:linux,darwin,windows,all + mod: "none" + cgo: 0 + packSrc: "resource" # 将resource目录打包进可执行文件,静态资源无需单独部署 + packDst: "internal/packed/packed.go" # 打包后生成的Go文件路径,一般使用相对路径指定到本项目目录中 + version: "" + output: "./temp/hotgo" # 可执行文件生成路径 + extra: "" + + gen: + dao: + - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true" +# - link: "pgsql:postgres:hg123456@tcp(127.0.0.1:5432)/hotgo" + group: "default" # 分组 使用hotgo代码生成功能时必须填 + # tables: "" # 指定当前数据库中需要执行代码生成的数据表。如果为空,表示数据库的所有表都会生成。 + tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。 + removePrefix: "hg_" + descriptionTag: true + noModelComment: true + jsonCase: "CamelLower" + gJsonSupport: true + clear: true + + service: # 生成业务配置 + srcFolder: "internal/logic" + dstFolder: "internal/service" + dstFileNameCase: "CamelLower" + clear: true +``` + +### 生成CRUD表格 + +- 推荐使用热编译方式启动HotGo,这样生成完成页面自动刷新即可看到新生成内容,无需手动重启 +- 服务端热编译启动:`gf run main.go`, web前端启动:`pnpm run dev` 或 `npm run dev` + +1、创建数据表 + +1.1 创建测试表格表`hg_test_table` +```mysql +CREATE TABLE `hg_test_table` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', + `category_id` bigint(20) NOT NULL COMMENT '分类ID', + `title` varchar(64) NOT NULL COMMENT '标题', + `description` varchar(255) NOT NULL COMMENT '描述', + `content` text NOT NULL COMMENT '内容', + `image` varchar(255) DEFAULT NULL COMMENT '单图', + `attachfile` varchar(255) DEFAULT NULL COMMENT '附件', + `city_id` bigint(20) DEFAULT '0' COMMENT '所在城市', + `switch` int(11) DEFAULT '1' COMMENT '显示开关', + `sort` int(11) NOT NULL COMMENT '排序', + `status` tinyint(1) DEFAULT '1' COMMENT '状态', + `created_by` bigint(20) DEFAULT '0' COMMENT '创建者', + `updated_by` bigint(20) DEFAULT '0' COMMENT '更新者', + `created_at` datetime DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime DEFAULT NULL COMMENT '修改时间', + `deleted_at` datetime DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='测试表格'; + +``` + +1.2 测试分类表`hg_test_category`: +- 注意:该表为了方便功能演示已经内置无需再次创建,此处提示表结构只是为了方便大家梳理关联表关系 +```mysql +CREATE TABLE `hg_test_category` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID', + `name` varchar(255) NOT NULL COMMENT '分类名称', + `description` varchar(255) DEFAULT NULL COMMENT '描述', + `sort` int(11) NOT NULL COMMENT '排序', + `remark` varchar(255) DEFAULT NULL COMMENT '备注', + `status` tinyint(1) DEFAULT '1' COMMENT '状态', + `created_at` datetime DEFAULT NULL COMMENT '创建时间', + `updated_at` datetime DEFAULT NULL COMMENT '修改时间', + `deleted_at` datetime DEFAULT NULL COMMENT '删除时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COMMENT='测试分类'; + +``` + +1.3 插入测试数据 +```mysql +INSERT INTO `hg_test_table` (`id`, `category_id`, `title`, `description`, `content`, `image`, `attachfile`, `city_id`, `switch`, `sort`, `status`, `created_by`, `updated_by`, `created_at`, `updated_at`, `deleted_at`) VALUES (1, 1, '测试标题', '描述', '

不知道写点啥!



', 'https://gmycos.facms.cn/hotgo/logo.sig.png', 'https://gmycos.facms.cn/hotgo/attachment/2022-12-30/cpf1x44idoycrtajf2.xlsx', 110102, 1, 10, 1, 0, 1, '2022-12-15 19:30:14', '2023-02-23 13:55:32', NULL); + +``` + + +2、创建生成配置 +- 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,选择和填写如下参数: + +![生成添加演示图](images/sys-code-add.png) + + +3、自定义配置 +- 确认配置无误后,点击生成配置会自动跳转到生成配置页面,如下: + +![生成配置页面](images/sys-code-config-init.png) + +- 你可以在该页面点击`预览代码`查看生成的内容,如果无需调整亦可以直接点击`提交生成`,以下是预览代码效果: + +![生成预览](images/sys-code-preview.png) + +- 如果你对默认的生成配置不满意,可以根据页面表单提示,自定义表格属性和字段属性 + +- 这里假设我们要一个关联表,让表`hg_test_table`字段`category_id`去关联 表`hg_test_category`字段`id`,我们可以这样做: + +![生成关联配置](images/sys-code-config-join.png) + + +- 如果你想调整主表每列字段的显示方式,可以点击上方导航栏中的 [主表字段] 进行调整 +- 这里我们不做任何调整直接进入下一步。目的是为了后续演示对最终生成结果不满意的再次调整方案 + +![生成关联配置](images/sys-code-master.png) + +4、以上内容都已配置无误后,点击提交生成即可。 + +- 如果你使用的热编译,那么页面会在生成成功后立即刷新,刷新完成你即可在后台菜单栏中看到`测试表格`菜单。如果不是使用热编译启动,请手动重启服务后刷新。 + +- 注意:热编译环境下,web端往往会快于服务端重启并加载完成,此时访问新生成的菜单页面可能会存在某接口请求超时或404问题,这是服务端正在重启导致的,属于正常现象,一般稍等几秒再试即可。 + +- 接下来让我们看看生成的表格页面,效果如下: + +![生成测试表格页面](images/sys-code-list.png) + + +5、假设我们对生成结果不满意,有新的优化需求如下: +1. 把`单图`、`附件`改换成上传组件 +2. 把`创建者`、`更新者`隐藏 +3. 把`分类ID`隐藏,然后把关联表中的`分类名称`显示出来 + +> 那么我们可以回到 开发工具 -> 代码生成 -> 找到刚刚生成的记录 -> 在右侧操作栏中点击生成配置,再次进入配置页面做如下操作即可: + +1. 点击上方导航栏[主表字段],调整字段选项: + +![修改主表配置](images/sys-code-master-save.png) + + +2. 点击上方导航栏[关联表],调整字段选项: + +![修改关联表配置](images/sys-code-config-join-save.png) + + +3. 返回[基本信息],勾选强制覆盖,然后点击`预览代码`,确认生成代码无误后点击`提交生成` + +![强制覆盖并提交生成](images/sys-code-config-post.png) + + +- 生成完成刷新页面后,再次来到`测试表格`,发现表格已经调整到了我们预期效果 + +1. 列表效果: + +![最终列表效果](images/sys-code-list-ok.png) + +2. 编辑表单效果 + +![最终编辑表单效果](images/sys-code-list-edit-ok.png) + + +- 至此生成增删改查列表示例结束! + +### 生成树形表格 + +待写。 + +### 多数据库使用 + +- 假设我们要增加一个库名为`hotgo2`、分组为`default2`的数据库,并要为其生成代码 + +1. 配置[server/hack/config.yaml](../../server/hack/config.yaml) 如下: +```yaml + gen: + dao: + - link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true" +# - link: "pgsql:postgres:hg123456@tcp(127.0.0.1:5432)/hotgo" + group: "default" # 分组 使用hotgo代码生成功能时必须填 + tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。 + removePrefix: "hg_" + descriptionTag: true + noModelComment: true + jsonCase: "CamelLower" + gJsonSupport: true + clear: false + - link: "mysql:hotgo2:hg123456.@tcp(127.0.0.1:3306)/hotgo2?loc=Local&parseTime=true" + group: "default2" # 分组 使用hotgo代码生成功能时必须填 + tablesEx: "hg_sys_addons_install" # 指定当前数据库中需要排除代码生成的数据表。 + removePrefix: "" + descriptionTag: true + noModelComment: true + jsonCase: "CamelLower" + gJsonSupport: true + clear: false +``` + +2. 配置`server/manifest/config/config.yaml`, + +`database`配置如下: +```yaml +database: + logger: + level: "all" + stdout: true + default: + link: "mysql:hotgo:hg123456.@tcp(127.0.0.1:3306)/hotgo?loc=Local&parseTime=true" +# - link: "pgsql:postgres:hg123456@tcp(127.0.0.1:5432)/hotgo" + debug: true + Prefix: "hg_" + default2: + link: "mysql:hotgo2:hg123456.@tcp(127.0.0.1:3306)/hotgo2?loc=Local&parseTime=true" + debug: true + Prefix: "" +``` + +`hggen`配置如下: +```yaml +hggen: + allowedIPs: ["127.0.0.1", "*"] # 白名单,*代表所有,只有允许的IP后台才能使用生成代码功能 + selectDbs: [ "default", "default2" ] # 可选生成表的数据库配置名称,支持多库 + disableTables : ["hg_sys_gen_codes","hg_admin_role_casbin"] # 禁用的表,禁用以后将不会在选择表中看到 + delimiters: ["@{", "}"] # 模板引擎变量分隔符号 +``` + +3. 登录HotGo后台 -> 开发工具 -> 代码生成 -> 找到立即生成按钮并打开,就会发现`数据库`选项增加了一个`default2`,后续生成步骤和生成例子完全一样 + +> 注意:上述的配置中所有的`default2`名称必须保持一致 + + +### 自定义生成模板 + +> 系统内置了两组CURD生成模板,请参考[生成模板配置](sys-code.md#生成模板配置)。default:是默认的生成到主模块下;addon:是默认生成到指定的插件下 + +- HotGo允许你新建新的模板分组来满足你的需求,模板可根据现有模板基础拷贝一份出来做改造,默认模板目录:[server/resource/generate/default](../../server/resource/generate/default) + + + +### 内置gf-cli + +> 为了确保生成代码的依赖稳定性,在面对`gf`版本更新可能导致向下不兼容情况时,HotGo将`gf-cli`工具内置到系统中并进行在线执行调整,从而提供更可靠和一致的生成代码功能。 + +- 后续我们也将开放在线运行`gf gen ...`功能。在做插件开发时也会支持到在线生成插件下的service接口,这将会使得插件开发更加方便 + + + +### 指定gf-cli版本 + +> HotGo多数情况下会和最新版本的gf-cli保持同步,如果更新不及时或你不想使用最新版本的gf-cli来生成代码,可以找到自己想要的版本进行替换即可。 + +- 下面大致做一些替换步骤说明: + +1. 打开https://github.com/gogf/gf,找到你想要使用的版本`clone`下来 +2. 将`clone`代码中`gf/cmd/gf/internal/`目录覆盖到`server/internal/library/hggen/internal` +3. 将覆盖过来的目录文件中引入包名`github.com/gogf/gf/cmd/gf/v2/`批量改为`hotgo/internal/library/hggen/` +4. 运行`go mod tidy` +5. 运行`go run main.go`,如果没有报错,那么恭喜你已经完成了。如果有报错一般都是版本差异带来的影响,需要根据情况自行调整 + + + +### 指定数据库驱动 + +> HotGo默认使用mysql驱动,如果你想用其他数据库驱动打开下方文件中注释即可,目前已支持mysql、pgsql + +修改文件路径:server/internal/library/hggen/internal/cmd/cmd_gen_dao.go +```go +package cmd + +import ( + //_ "github.com/gogf/gf/contrib/drivers/clickhouse/v2" + //_ "github.com/gogf/gf/contrib/drivers/mssql/v2" + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + //_ "github.com/gogf/gf/contrib/drivers/oracle/v2" + _ "github.com/gogf/gf/contrib/drivers/pgsql/v2" + //_ "github.com/gogf/gf/contrib/drivers/sqlite/v2" + + "hotgo/internal/library/hggen/internal/cmd/gendao" +) + +type ( + cGenDao = gendao.CGenDao +) + +``` + +修改完成后运行`go mod tidy` diff --git a/docs/guide-zh-CN/sys-console.md b/docs/guide-zh-CN/sys-console.md new file mode 100644 index 0000000..33e8652 --- /dev/null +++ b/docs/guide-zh-CN/sys-console.md @@ -0,0 +1,75 @@ +## 控制台 + +目录 + +- 启动所有服务 +- HTTP服务 +- 消息队列 +- 定时任务 +- 常用工具 +- Makefile + +### 启动所有服务 +- 仅推荐在开发期间快速调试使用,线上实际部署时建议将各个服务分开部署,这样重新部署某个服务时无需全部重启。 + +```shell + +# 默认 +go run main.go + +# 通过热编译启动 +gf run main.go +``` + +### HTTP服务 +- 启动HTTP服务,包含websocket。 + +```shell +# 默认 +go run main.go http + +# 通过热编译启动 +gf run main.go --args "http" +``` + +### 消息队列 +- 启动消息队列的消费者。 + +```shell +# 默认 +go run main.go queue + +# 通过热编译启动 +gf run main.go --args "queue" +``` + +### 定时任务 +- 启动系统中统一注册的定时任务。 + +```shell +# 默认 +go run main.go cron + +# 通过热编译启动 +gf run main.go --args "cron" +``` + + +### 常用工具 +- 释放casbin权限,用于清理无效的权限设置。 + + +```shell +go run main.go tools -m=casbin -a1=refresh +``` + + +### Makefile +- 通过make提供一些快捷命令 + +```shell +# 一键编译,打包前后端代码到可执行文件 +make build + +# 更多请查看 /server/Makefile文件 +``` \ No newline at end of file diff --git a/docs/guide-zh-CN/sys-cron.md b/docs/guide-zh-CN/sys-cron.md new file mode 100644 index 0000000..efca8a8 --- /dev/null +++ b/docs/guide-zh-CN/sys-cron.md @@ -0,0 +1,70 @@ +## 定时任务 + +目录 + +- 实现接口 +- 一个例子 +- 更多 + +> 在实际的项目开发中,定时任务几乎成为不可或缺的一部分。HotGo为定时任务提供一个方便的后台操作界面,让您能够轻松地进行在线启停、修改和立即执行等操作。这样的设计可以极大地改善您在使用定时任务过程中的体验,让整个过程更加顺畅、高效。 + + +### 实现接口 +- 为了提供高度的扩展性,定时任务在设计上采用了接口化的思路。只需要实现以下接口,您就可以在任何地方注册和使用定时任务功能,从而实现更大的灵活性和可扩展性。 + +```go +// Cron 定时任务接口 +type Cron interface { + // GetName 获取任务名称 + GetName() string + // Execute 执行任务 + Execute(ctx context.Context, parser *Parser) (err error) +} +``` + + +### 一个例子 + +定时任务的文件结构可以根据具体需要进行调整,以下是一个常见的参考结构: + +- 文件路径:server/internal/crons/test.go + +```go +package crons + +import ( + "context" + "hotgo/internal/library/cron" + "time" +) + +func init() { + cron.Register(Test) +} + +// Test 测试任务(无参数) +var Test = &cTest{name: "test"} + +type cTest struct { + name string +} + +func (c *cTest) GetName() string { + return c.name +} + +// Execute 执行任务 +func (c *cTest) Execute(ctx context.Context, parser *cron.Parser) (err error) { + parser.Logger.Infof(ctx, "cron test Execute:%v", time.Now()) // 记录任务调度日志 + return +} +``` + +继续在后台系统设置-定时任务-添加任务,填写的任务名称需要和上面的名称保持一致,再进行简单的策略配置以后,一个后台可控的定时任务就添加好了! + + +### 更多 + +定时任务源码路径:server/internal/library/cron/cron.go + +更多文档请参考:https://goframe.org/pages/viewpage.action?pageId=1114187 \ No newline at end of file diff --git a/docs/guide-zh-CN/sys-db.md b/docs/guide-zh-CN/sys-db.md new file mode 100644 index 0000000..64a70ef --- /dev/null +++ b/docs/guide-zh-CN/sys-db.md @@ -0,0 +1,150 @@ +## 数据库 + +目录 + +- 字段类型 +- 特殊字段默认表单组件 +- 特殊字段默认表单验证器 +- SQL默认查询方式 +- 其他 + +### 字段类型 + +- 创建数据库表当按如下的规则进行字段命名、类型、属性设置和备注后,再生成CRUD代码时会自动生成对应的Api、控制器、业务逻辑、Web页面、[表单组件](web-form.md)等的一些默认属性 +- 当你了解这些默认技巧后,会有效提高你在实际开发中的生产效率 + +| 数据库类型 | 额外属性 | 转换Go类型 | 转换Ts类型 | 表单组件 | +|---------------------------------------------------------------|--------------|--------------|---------|-----------------------| +| int, tinyint,small_int,smallint,medium_int,mediumint,serial | / | int | number | InputNumber(数字输入框) | +| int, tinyint,small_int,smallint,medium_int,mediumint,serial | unsigned | uint | number | InputNumber(数字输入框) | +| big_int,bigint,bigserial | / | int64 | number | InputNumber(数字输入框) | +| big_int,bigint,bigserial | unsigned | uint64 | number | InputNumber(数字输入框) | +| real | / | float32 | number | InputNumber(数字输入框) | +| float,double,decimal,money,numeric,smallmoney | / | float64 | number | InputNumber(数字输入框) | +| bit(1) 、bit(true)、bit(false) | / | bool | boolean | Input(文本输入框,默认) | +| bit | / | int64-bytes | array | InputDynamic(动态KV表单) | +| bit | unsigned | uint64-bytes | array | InputDynamic (动态KV表单) | +| bool | / | bool | boolean | Input(文本输入框,默认) | +| date | / | *gtime.Time | string | Date(日期选择器) | +| datetime,timestamp,timestamptz | / | *gtime.Time | string | Time(时间选择器) | +| json | / | *gjson.Json | string | Input(文本输入框) | +| jsonb | / | *gjson.Json | string | Input(文本输入框) | +| 以下为物理类型中包含字段部分时的转换方式,默认情况 | / | / | / | / | +| text,char,character | / | string | string | Input(文本输入框) | +| float,double,numeric | / | string | string | Input(文本输入框) | +| bool | / | bool | boolean | Input(文本输入框,默认) | +| binary,blob | / | []byte | string | Input(文本输入框,默认) | +| int | / | int | number | InputNumber(数字输入框) | +| int | unsigned | int | number | InputNumber(数字输入框) | +| time | / | *gtime.Time | string | Time(时间选择器) | +| date | / | *gtime.Time | string | Date(日期选择器) | +| 没有满足以上任何条件的 | / | string | string | Input(文本输入框) | + + +### 特殊字段默认表单组件 +- 以下字段在不设置表单组件时会默认使用的表单组件 + +| 数据库字段 | 字段名称 | 表单组件 | +|--------------|----------------------|----------------------| +| status | 状态字段(任意int类型) | Select (单选下拉框) | +| created_at | 创建时间字段 | TimeRange (时间范围选择) | +| province_id | 省份ID字段(任意int类型) | CitySelector (省市区选择) | +| city_id | 城市ID字段(任意int类型) | CitySelector (省市区选择) | +| 任意字串符字段 | 长度>= 200 and <= 500 | InputTextarea (文本域) | +| 任意字串符字段 | 长度> 500 | InputEditor (富文本) | + + +### 特殊字段默认表单验证器 +- 以下字段在不设置表单组件时会默认使用的表单验证器 + +| 数据库字段/Go类型 | 字段名称 | 表单验证规则 | +|-------------------|--------|-----------------------| +| mobile | 手机号 | 不为空时必须是手机号码(国内) | +| qq | QQ | 不为空时必须是QQ号码 | +| email | 邮箱地址 | 不为空时必须是邮箱格式 | +| id_card | 身份证号码 | 不为空时必须是15或18位身份证号码 | +| bank_card | 银行卡号码 | 银行卡号码 | +| password | 密码 | 密码验证,必须包含6-18为字母和数字 | +| price | 价格 | 金额验证,最多允许输入10位整数及2位小数 | +| Go类型为uint、uint64 | 正整数 | 非零正整数验证 | + +### SQL默认查询方式 +- Go类型取决于数据库物理类型,请参考 [字段类型] 部分 + +| Go类型 | 查询方式 | +|-------------------------|--------------------------------------| +| string | LIKE | +| date,datetime | = | +| int,uint,int64,uint64 | = | +| []int,[]int64,[]uint64 | IN (...) | +| float32,float64 | = | +| []byte4 | =(默认) | +| time.Time,*gtime.Time | = | +| *gjson.Json | JSON_CONTAINS(json_doc, val[, path]) | + + + +### 其他 + +#### 默认字典选项 + +- 数据库字段为 `status`且类型为任意数字类型的会使用系统默认的状态字典 + +#### 默认属性 + +- 默认必填,当数据库字段存在非空`IS_NULLABLE`属性时,默认勾选必填验证 +- 默认唯一,当数据库字段索引存在`UNI`时,默认勾选唯一值验证 +- 默认主键,当数据库字段索引存在`PRI`时,默认为主键,不允许编辑 +- 默认排序,当数据库字段存在`sort`时,默认开启排序,添加表单自动获取最大排序增量值并填充表单 +- 默认列名,默认使用字段注释作为表格的列名。当数据库字段未设置注释时,默认使用字段名称作为列名 + +#### 自动更新/插入 + +- 自动更新,当数据库字段为`updated_at`(更新时间),`updated_by`(更新者) +- 自动插入,当数据库字段为`created_at`(创建时间),`created_by`(创建者) +- 软删除,表存在字段`deleted_at`时,使用表的Orm模型查询条件将会自动加入[ `deleted_at` IS NULL ],删除时只更新删除时间而不会真的删除数据 +- 树表:不论更新插入,都会根据表中字段`pid`(上级ID)自动维护`level`(树等级)和`tree`(关系树) + + +#### 操作人字段维护 + +- 生成列表中存在并且勾选展示字段`created_by`(创建者)、`updated_by`(修改者)、`deleted_by`(删除者)时,会自动到表`hg_admin_member`中获取操作人的基本信息摘要,并渲染到列表中,效果如下: + +![](images/sys-db-by.png) + +- 生成列表中存在并且勾选查询字段`created_by`(创建者)、`updated_by`(修改者)、`deleted_by`(删除者)时,会强制将查询表单改为关键词查询,从`hg_admin_member`查询操作人。效果如下: + +![](images/sys-db-by2.png) + +- 查询代码片段,参考路径:[server/internal/logic/admin/member.go](../../server/internal/logic/admin/member.go) + +```go + +// 查询创建者 +if in.CreatedBy != "" { + ids, err := service.AdminMember().GetIdsByKeyword(ctx, in.CreatedBy) + if err != nil { + return nil, 0, err + } + mod = mod.WhereIn(dao.SysGenCurdDemo.Columns().CreatedBy, ids) +} + +// GetIdsByKeyword 根据关键词查找符合条件的用户ID +func (s *sAdminMember) GetIdsByKeyword(ctx context.Context, ks string) (res []int64, err error) { + ks = gstr.Trim(ks) + if len(ks) == 0 { + return + } + array, err := dao.AdminMember.Ctx(ctx).Fields("id"). + Where("`id` = ? or `real_name` = ? or `username` = ? or `mobile` = ?", ks, ks, ks, ks). + Array() + if err != nil { + err = gerror.Wrap(err, "根据关键词获取用户ID失败,请稍后重试!") + } + res = gvar.New(array).Int64s() + return +} +``` + + +> 这里只列举了较为常用的默认规则,其他更多默认规则请参考:[server/internal/library/hggen/views/column_default.go](../../server/internal/library/hggen/views/column_default.go) diff --git a/docs/guide-zh-CN/sys-exploit.md b/docs/guide-zh-CN/sys-exploit.md new file mode 100644 index 0000000..70fb37b --- /dev/null +++ b/docs/guide-zh-CN/sys-exploit.md @@ -0,0 +1,201 @@ +## 开发规范 + +目录 + +- 规范说明 +- 规范建议 +- 命名规范 +- 编码规范 +- 框架规范 +- 插件规范 +- 数据库规范 +- 包功能规范 + +### 规范说明 + +- 开发规范意义是是让大家尽可能写出风格统一的代码,让团队代码有更好的可读性与可维护性。 +- 在实际业务开发中,除了要提高业务开发效率,保证线上业务高性能外,好的编程习惯也是一个开发人员基本素养之一。 + + +### 规范建议 + +- 在我们实际开发中,有很多开发人可能是由某一语言转到另外一个语言领域,在转到另外一门语言后, 我们都会保留着对旧语言的编程习惯,在这里,我建议的是,虽然不同语言之前的某些规范可能是相通的, 但是我们最好能够按照官方的一些demo来熟悉是渐渐适应当前语言的编程规范,而不是直接将原来语言的编程规范也随之迁移过来。 + + +### 命名规范 + + +#### 命名准则 + +* 当变量名称在定义和最后一次使用之间的距离很短时,简短的名称看起来会更好 +* 变量命名应尽量描述其内容,而不是类型 +* 常量命名应尽量描述其值,而不是如何使用这个值 +* 在遇到for,if等循环或分支时,推荐单个字母命名来标识参数和返回值 +* package名称也是命名的一部分,推荐全部使用小写,请尽量将其利用起来 +* 使用一致的命名风格 + +#### 文件命名规范 +* 全部小写 +* 除unit test外避免下划线(_) +* 文件名称不宜过长,但不必为了简短而忽略含义 + +#### 变量命名规范参考 +* 驼峰命名 +* 见名知义,避免拼音替代英文 +* 不建议包含下划线(_) +* 不建议包含数字 + + +#### 函数、常量命名规范 +* 驼峰式命名 +* 可导出的必须首字母大写 +* 不可导出的必须首字母小写 +* 避免全部大写与下划线(_)组合 + + +#### 参考文档 + +* [Practical Go: Real world advice for writing maintainable Go programs](https://dave.cheney.net/practical-go/presentations/gophercon-singapore-2019.html#_simplicity) + + +### 编码规范 + +#### import +* 单行import不建议用圆括号包裹 +* 按照`官方包`,NEW LINE,`当前工程包`,NEW LINE,`第三方依赖包`顺序引入 + +```go +import ( + "context" + "string" + + "greet/user/internal/config" + + "google.golang.org/grpc" +) +``` + +#### 函数返回 +* 对象避免非指针返回 +* 遵循有正常值返回则一定无error,有error则一定无正常值返回的原则 + +#### 错误处理 +* 有error必须处理,如果不能处理就必须抛出。 +* 避免下划线(_)接收error + +#### 函数体编码 +* 建议一个block结束空一行,如if、for等 + +```go + func main (){ + if x==1{ + // do something + } + + fmt.println("xxx") + } +``` +* return前尽可能空一行 + +```go +func getUser(id string)(string,error){ + .... + + return "xx",nil +} +``` + +### 框架规范 + +- 项目启动初始化方法都放在`server/internal/global/init.go`,插件放在:`server/addons/xxx插件/global/init.go` +- Api 对外接口层 都放在`server/api`,根据实际应用和应用具体功能划分子目录,插件放在:`server/addons/xxx插件/api` +- Router 路由注册方式应尽可能按gf2.0版本风格定义,参考:https://goframe.org/pages/viewpage.action?pageId=30736904 +- Controller 控制器层 都放在`server/internal/controller`,根据应用和具体功能拆分子目录,插件放在:`server/addons/xxx插件/controller` +- Input 对内接口层 都放在`server/internal/model/input`,根据应用和具体功能拆分子目录并以`in`结尾,插件放在:`server/addons/xxx插件/model/input` +- Logic 业务逻辑层 统一放在`server/internal/logic` 根据实际业务需求按logic/xxx/*.go拆分,插件放在:`server/addons/xxx插件/logic` +- Orm 模型实体层 统一放在`server/internal/model/entity` +- Dao 数据访问层 统一放在`server/internal/dao` +- 自定义工具函数都放在 `server/utility`,且应根据不同类型的方法做分包,相同类型的方法放在同一个包内 +- 状态枚举统一调用`server/internal/consts`中的常量和属性 + +### 插件规范 + +- 插件命名统一全部小写 +- 资源文件在 `addons/xxx插件/resource`或`server/resource` +- 每个插件之前应该是完全独立的,尽可能避免插件于插件之间的调用依赖 +- 插件和主模块之前的调用应尽可能通过 Service 接口来调用 +- 插件业务初始化顺序下应在主模块之后,应尽可能避免使用init方法隐式初始化 + +### 数据库规范 + +#### 命名规范 + +- 数据表名小写,多关键字使用下划线分割(关键字尽量全称) +- 字段名小写,多关键字使用下划线分割(关键字尽量全称) +- 禁止使用保留字并且尽量少用含有关键词来命名 +- 临时表必须以tmp_开头、以日期结尾,备份表必须以bak_开头、以日期结尾 +- 同数据库表名设置统一前缀,默认是`hg_` +- 插件模块表名建议以`hg_addon_`开头,如:`hg_addon_hgexample_table`,含义:`插件_案例_表格`。在生成代码时可自动识别实体命名为:`Table` + +#### 基础规范 + +- 所有的字段必须添加注释 +- 尽可能地使用InnoDB作为表的存储引擎 +- 所有的表和字段必须添加注释 +- 尽量控制表行数在500万以内 +- 尽可能采用冷热数据分离策略 +- 禁止以图片、文件等二进制数据 + +#### 表设计规范 + +- 尽可能每张表的索引数量控制在5个以内 +- 每一张InnoDB表都必须含有一个主键,自增主键必须是`bigint`类型 +- 尽可能避免使用外键约束 +- 设置数据表架构应考虑后期扩展型 +- 遵循范式与冗余平衡原则 + +#### 字段设计规范 + +- 尽可能将所有的数据列定义为 `NOT NULL` 类型 +- 避免 ENUM 数据类型 +- json 存储的数据用 `json`字段代替 `text` +- 表与表关联的键名保持一致或以关联表名的缩写为前缀 +- 固定长度的字符串字段务必使用 `char` +- 使用 `UNSIGNEG` 存储非负整数 +- 禁止敏感数据以明文形式存储 +- 金额相关的数据必须使用 `DECIMAL` 数据类型 +- 尽量所有表有 `status` 字段来标注数据状态(1:启用,2:禁用,3:已删除),业务状态请使用其他字段;`status`字段类型 为带符号的 `tinyint(1)`。如果还需要其他的数据状态 请先判断该状态的数据是有用的数据还是无意义的数据,有用的数据状态 > 0,无意义的数据状态 < -1 +- 所有的删除(除开清空回收站操作) 请标记 `status` 为 3 +- 创建时间字段为`created_at`,修改时间字段为`updated_at`,删除时间字段为`deleted_at`,类型`datetime` +- 用户关联 id 字段为 `member_id` 或 `created_by` +- 排序字段为 `sort` +- 区分应用字段为 `app_id` +- 区分插件来源需要增加字段为 `addon_name` 和 `is_addon` +- 关系树表中必须包含字段:`id`(主键)、`pid`(上级ID)、`level`(树等级)、`tree`(关系树) +- 多选、多图、多文件字段类型应尽量使用类型`json` + +### 包功能规范 + +##### Input(对内接口 - 过滤验证) + +- 接收Api 对外接口数据和参数过滤 +- 对提交的复杂数据参数进行验证处理返回 + +##### View(视图) + +- 数据处理显示 +- 页面渲染 + +##### Controller(控制器) + +- 数据接收 +- 服务调用 +- 简单CURD入库 +- 简单业务逻辑 + + +##### Logic(业务逻辑 - 服务接口功能) + +- 数据逻辑处理 +- 数据入库 +- 数据查询 \ No newline at end of file diff --git a/docs/guide-zh-CN/sys-i18n.md b/docs/guide-zh-CN/sys-i18n.md new file mode 100644 index 0000000..e3e7e73 --- /dev/null +++ b/docs/guide-zh-CN/sys-i18n.md @@ -0,0 +1,73 @@ +## 国际化 + + +目录 + +- 介绍 +- 配置文件 +- 服务端使用 +- Web端使用 +- 更多文档 + +### 介绍 + +从`v2.18.6`版本开始,HotGo 提供了完善的国际化(i18n)支持,内置简体中文、繁体中文和英文三种语言包,默认语言为简体中文。目前已对后台管理首页进行了多语言适配,开发者可基于该机制扩展实现业务模块的多语言功能。 + +### 配置文件 + +国际化配置位于 `server/manifest/config/config.yaml` 文件中: + +```yaml +# hotgo系统配置 +system: + # ... + # 国际化 + i18n: + switch: true # 国际化功能开关,可选值:false|true,默认:true + defaultLanguage: "zh-CN" # 默认语言,可选值:zh-CN|zh-TW|en,默认:zh-CN +``` + +### 服务端使用 + +多语言配置文件存放目录:`server/manifest/i18n`,您可以在该目录下添加自定义的语言映射文件。 + +**使用示例:** + +```go +// 设置当前上下文的语言为英文 +ctx := gctx.New() +gi18n.WithLanguage(ctx, "en") + +// 基础翻译 +gi18n.T(ctx, "你好,美丽世界") +// 输出:Hello, Beautiful World + +// 格式化翻译(支持参数替换) +gi18n.Tf(ctx, "剩余%v余额", 100) +// 输出:Remaining 100 Balance +``` + + +### Web端使用 + +多语言配置文件存放目录:`web/src/locale`,您可以在该目录下添加自定义的语言映射文件。 + +> 提示:启用国际化功能后,后台管理界面右上角将显示语言切换选项。 + +**使用示例(假设当前语言为英文):** + +```vue + +
{{ t('你好,美丽世界') }}
+ + + +
{{ t('剩余{num}余额', {num:100}) }}
+ +``` + + +### 更多文档 + +- [gi18n](https://goframe.org/docs/core/gi18n) +- [vie-i18n](https://vue-i18n.intlify.dev) \ No newline at end of file diff --git a/docs/guide-zh-CN/sys-library.md b/docs/guide-zh-CN/sys-library.md new file mode 100644 index 0000000..25255d9 --- /dev/null +++ b/docs/guide-zh-CN/sys-library.md @@ -0,0 +1,318 @@ +## 功能扩展库 + +目录 + +- 缓存驱动 +- 请求上下文 +- JWT +- 数据字典 +- 地理定位(待写) +- 通知(待写) + + +### 缓存驱动 + +> 系统默认的缓存驱动为file,目前已支持:memory|redis|file等多种驱动。请自行选择适合你的驱动使用。 + +- 配置文件:server/manifest/config/config.yaml + +```yaml +#缓存 +cache: + adapter: "file" # 缓存驱动方式,支持:memory|redis|file,不填默认memory + fileDir: "./storage/cache" # 文件缓存路径,adapter=file时必填 +``` + +#### 使用方式 +```go +package main + +import ( + "hotgo/internal/library/cache" + "github.com/gogf/gf/v2/os/gctx" +) + +func test() { + ctx := gctx.New() + + // 添加/修改 + cache.Instance().Set(ctx, "qwe", 123, 0) + + // 查询 + cache.Instance().Get(ctx, "qwe") + + // 删除 + cache.Instance().Remove(ctx, "qwe") + + // 更多方法请参考:https://goframe.org/pages/viewpage.action?pageId=27755640 +} + +``` + +### 请求上下文 + +- 主要用于在处理HTTP和websocket请求时通过中间件将用户、应用、插件等信息绑定到上下文中,方便在做业务处理时用到这些信息 + +```go +package admin + +import ( + "fmt" + "context" + "hotgo/internal/library/contexts" + "hotgo/internal/library/addons" +) + + +func test(ctx context.Context) { + // 获取当前请求的所有上下文变量 + var ctxModel = contexts.Get(ctx) + fmt.Printf("当前请求的所有上下文变量:%+v\n", ctxModel) + + // 获取当前请求的应用模块 + var module = contexts.GetModule(ctx) + fmt.Printf("当前请求的应用:%+v\n", module) + + // 获取当前请求的用户信息 + var member = contexts.GetUser(ctx) + fmt.Printf("当前访问用户信息:%+v\n", member) + + // 获取当前请求的插件模块 + fmt.Printf("当前是否为插件请求:%v", contexts.IsAddonRequest(ctx)) + if contexts.IsAddonRequest(ctx) { + fmt.Printf("当前插件名称:%v", contexts.GetAddonName(ctx)) + fmt.Printf("当前插件信息:%v", addons.GetModule(contexts.GetAddonName(ctx))) + } +} + +``` + +### JWT + +- 基于jwt+缓存驱动实现的用户登录令牌功能,支持自动续约,解决了jwt服务端无法退出问题和jwt令牌无法主动失效问题 + +#### 配置示例 +```yaml +# 登录令牌 +token: + secretKey: "hotgo123" # 令牌加密秘钥,考虑安全问题生产环境中请修改默认值 + expires: 604800 # 令牌有效期,单位:秒。默认7天 + autoRefresh: true # 是否开启自动刷新过期时间, false|true 默认为true + refreshInterval: 86400 # 刷新间隔,单位:秒。必须小于expires,否则无法触发。默认1天内只允许刷新一次 + maxRefreshTimes: 30 # 最大允许刷新次数,-1不限制。默认30次 + multiLogin: true # 是否允许多端登录, false|true 默认为true + +``` + +```go +package admin + +import ( + "fmt" + "context" + "hotgo/internal/library/token" + "hotgo/internal/model" +) + + +func test(ctx context.Context) { + // 登录 + user := &model.Identity{ + Id: mb.Id, + Pid: mb.Pid, + DeptId: mb.DeptId, + RoleId: ro.Id, + RoleKey: ro.Key, + Username: mb.Username, + RealName: mb.RealName, + Avatar: mb.Avatar, + Email: mb.Email, + Mobile: mb.Mobile, + App: consts.AppAdmin, + LoginAt: gtime.Now(), + } + + loginToken, expires, err := token.Login(ctx, user) + if err != nil { + return nil, err + } + + // gf请求对象 + r := *ghttp.Request + + // 获取登录用户信息 + user, err := token.ParseLoginUser(r) + if err != nil { + return + } + + // 注销登录 + err = token.Logout(r) +} + +``` + +### 数据字典 + +- hotgo增加了对枚举字典和自定义方法字典的内置支持,从而在系统中经常使用的一些特定数据上做出了增强。 + +#### 字典数据选项 +- 文件路径:server/internal/model/dict.go + +```go +package model + +// Option 字典数据选项 +type Option struct { + Key interface{} `json:"key"` + Label string `json:"label" description:"字典标签"` + Value interface{} `json:"value" description:"字典键值"` + ValueType string `json:"valueType" description:"键值数据类型"` + Type string `json:"type" description:"字典类型"` + ListClass string `json:"listClass" description:"表格回显样式"` +} +``` + +#### 枚举字典 +- 适用于系统开发期间内置的枚举数据,这样即维护了枚举值,又关联了数据字典 + +##### 一个例子 +- 定义枚举值和字典数据选项,并注册字典类型 +- 文件路径:server/internal/consts/credit_log.go + +```go +package consts + +import ( + "hotgo/internal/library/dict" + "hotgo/internal/model" +) + +func init() { + dict.RegisterEnums("creditType", "资金变动类型", CreditTypeOptions) + dict.RegisterEnums("creditGroup", "资金变动分组", CreditGroupOptions) +} + +const ( + CreditTypeBalance = "balance" // 余额 + CreditTypeIntegral = "integral" // 积分 +) + +const ( + CreditGroupDecr = "decr" // 扣款 + CreditGroupIncr = "incr" // 加款 + CreditGroupOpDecr = "op_decr" // 操作扣款 + CreditGroupOpIncr = "op_incr" // 操作加款 + CreditGroupBalanceRecharge = "balance_recharge" // 余额充值 + CreditGroupBalanceRefund = "balance_refund" // 余额退款 + CreditGroupApplyCash = "apply_cash" // 申请提现 +) + +// CreditTypeOptions 变动类型 +var CreditTypeOptions = []*model.Option{ + dict.GenSuccessOption(CreditTypeBalance, "余额"), + dict.GenInfoOption(CreditTypeIntegral, "积分"), +} + +// CreditGroupOptions 变动分组 +var CreditGroupOptions = []*model.Option{ + dict.GenWarningOption(CreditGroupDecr, "扣款"), + dict.GenSuccessOption(CreditGroupIncr, "加款"), + dict.GenWarningOption(CreditGroupOpDecr, "操作扣款"), + dict.GenSuccessOption(CreditGroupOpIncr, "操作加款"), + dict.GenWarningOption(CreditGroupBalanceRefund, "余额退款"), + dict.GenSuccessOption(CreditGroupBalanceRecharge, "余额充值"), + dict.GenInfoOption(CreditGroupApplyCash, "申请提现"), +} + +``` + + +#### 自定义方法字典 +- 适用于非固定选项,如数据是从某个表/文件读取或从第三方读取,数据需要进行转换时使用 + +##### 方法字典接口 +- 文件路径:server/internal/consts/credit_log.go + +```go +package dict + +// FuncDict 方法字典,实现本接口即可使用内置方法字典 +type FuncDict func(ctx context.Context) (res []*model.Option, err error) +``` + +##### 一个例子 +- 定义获取字典数据方法,并注册字典类型 +- 文件路径:server/internal/logic/admin/post.go + +```go +package admin + +import ( + "context" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "hotgo/internal/consts" + "hotgo/internal/dao" + "hotgo/internal/library/dict" + "hotgo/internal/model" + "hotgo/internal/model/entity" + "hotgo/internal/service" +) + +type sAdminPost struct{} + +func NewAdminPost() *sAdminPost { + return &sAdminPost{} +} + +func init() { + service.RegisterAdminPost(NewAdminPost()) + dict.RegisterFunc("adminPostOption", "岗位选项", service.AdminPost().Option) +} + +// Option 岗位选项 +func (s *sAdminPost) Option(ctx context.Context) (opts []*model.Option, err error) { + var list []*entity.AdminPost + if err = dao.AdminPost.Ctx(ctx).OrderAsc(dao.AdminPost.Columns().Sort).Scan(&list); err != nil { + return nil, err + } + + if len(list) == 0 { + opts = make([]*model.Option, 0) + return + } + + for _, v := range list { + opts = append(opts, dict.GenHashOption(v.Id, v.Name)) + } + return +} +``` + +#### 代码生成支持 +- 内置的枚举字典和自定义方法字典在生成代码时可以直接进行选择,生成代码格式和系统字典管理写法一致 + +![最终编辑表单效果](images/sys-library-dict.png) + + +#### 内置字典和系统字典的区分 + +##### 主要区别 +- 系统字典由表:`hg_sys_dict_type`和`hg_sys_dict_data`共同进行维护,使用时需通过后台到字典管理中进行添加 +- 内置字典是系统开发期间在代码层面事先定义和注册好的数据选项 + + +##### 数据格式区别 +- 系统字典所有ID都是大于0的int64类型 +- 内置字典ID都是小于0的int64类型。枚举字典以20000开头,如:-200001381053496;方法字典以30000开头,如:-30000892528327;开头以外数字是根据数据选项的`key`值进行哈希算法得出 + +### 地理定位 +```go +// 待写 +``` + +### 通知 +```go +// 待写 +``` \ No newline at end of file diff --git a/docs/guide-zh-CN/sys-middleware.md b/docs/guide-zh-CN/sys-middleware.md new file mode 100644 index 0000000..70f334a --- /dev/null +++ b/docs/guide-zh-CN/sys-middleware.md @@ -0,0 +1,250 @@ +## 中间件/拦截器 + +目录 + +- 介绍 +- 全局中间件 +- 鉴权中间件 +- 响应中间件 +- 更多 + +### 介绍 +- 在hotgo中,中间件/拦截器主要作用于web请求的上下文预设、跨域请求处理、鉴权处理、请求拦截和请求结束后统一响应处理等。 + + +### 全局中间件 +```go +package main + +import ( + "hotgo/internal/service" +) + +func main() { + + // 初始化请求上下文,一般需要第一个进行加载,后续中间件存在依赖关系 + service.Middleware().Ctx() + + // 跨域中间件,自动处理跨域问题 + service.Middleware().CORS() + + // IP黑名单中间件,如果请求IP被后台拉黑,所有请求将被拒绝 + service.Middleware().Blacklist() + + // 演示系統操作限制,当开启演示模式时,所有POST请求将被拒绝 + service.Middleware().DemoLimit() + + // 请求输入预处理,api使用gf规范路由并且XxxReq结构体实现了validate.Filter接口即可隐式预处理 + service.Middleware().PreFilter() + + // HTTP响应预处理,在业务处理完成后,对响应结果进行格式化和错误过滤,将处理后的数据发送给请求方 + service.Middleware().ResponseHandler() + +} + +``` +### 鉴权中间件 +```go +package main + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +func main() { + + // 在鉴权中间件下的路由如果没有通过权限验证,后续请求将被拒绝 + // 在hotgo中,鉴权中间件一般是配合一个业务模块下的路由组进行使用 + // 目前admin、api、home、websocket模块都已接入 + // 如果你需要创建一个新的模块也需要用到鉴权中间件,可以参考:server/internal/logic/middleware/admin_auth.go + + + // 一个简单例子 + s := g.Server() + s.Group("/api", func(group *ghttp.RouterGroup) { + group.Middleware(service.Middleware().ApiAuth) + group.Bind( + member.Member, // 管理员 + ) + }) + +} + +``` + +### 响应中间件 +- 文件路径:server/internal/logic/middleware/response.go + + +#### 常用响应类型 + +- hotgo为一些常用的响应类型做了统一格式封装,例如:`application/json`、`text/xml`、`text/html`、`text/event-stream`等,默认使用`application/json`。 +- 下面我们以`text/xml`为例简单演示几种使用方法: + +1. 当你使用规范化路由时,可直接在XxxRes结构体的`g.Meta`中声明响应类型: + +```go +type HelloReq struct { + g.Meta `path:"/hello" tags:"Hello" method:"get" summary:"You first hello api"` + Name string `json:"name" d:"hotgo" dc:"名字"` +} + +type HelloRes struct { + g.Meta `mime:"text/xml" type:"string"` + Tips string `json:"tips"` +} +``` + +2. 在响应前设置响应头: + +```go +var ( + Hello = cHello{} +) + +type cHello struct{} + +func (c *cHello) Hello(ctx context.Context, req *user.HelloReq) (res *user.HelloRes, err error) { + r := ghttp.RequestFromCtx(ctx) + r.Response.Header().Set("Content-Type", "text/xml") + + res = &user.HelloRes{ + Tips: fmt.Sprintf("hello %v, this is the api for %v applications.", req.Name, simple.AppName(ctx)), + } + return +} +``` + +- 浏览器中访问响应内容如下: + +![./images/sys-middleware-com-response.png](./images/sys-middleware-com-response.png) + + +#### 自定义响应 +- 在实际开发中,可能需要使用自定义的响应类型,由于响应中间件是全局的,因此您需要对其进行单独处理。 +- 推荐以下几种处理方案,可做参考: +1. 使用`ghttp.ExitAll()`,需要注意的是此方法会终止后续所有的http处理 + +```go +package main + +import ( + "github.com/gogf/gf/v2/net/ghttp" +) + +func main() { + r := new(ghttp.Request) // 当前请求对象 + + // 清空响应 + r.Response.ClearBuffer() + + // 写入响应 + r.Response.Write("自定义响应内容") + + // 终止后续http处理 + r.ExitAll() +} +``` + +2. 在`server/internal/logic/middleware/response.go`中根据请求的独有特征进行单独的处理,兼容后续http处理。 + +#### 重写响应错误提示 +- 在实际开发中,我们可能想要隐藏一些敏感错误,返回给客户端友好的错误提示,但开发者同时又想需要看到真实的敏感错误。对此hotgo已经进行了过滤处理,下面是一个简单的例子: + +```go +package main + +import ( + "github.com/gogf/gf/v2/errors/gerror" +) + +func test() error { + err = gerror.New("这是一个sql执行错误") + err = gerror.Wrap(err, "用户创建失败,请稍后重试!~") + return err +} +``` + +- 开启debug时的客户端响应: +```json +{ + "code": -1, + "message": "用户创建失败,请稍后重试!~", + "error": [ + "1. 用户创建失败,请稍后重试!~", + " 1). hotgo/internal/logic/admin.(*sAdminMember).List", + " E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/admin/member.go:526", + "2. 这是一个sql执行错误", " 1). hotgo/internal/logic/admin.(*sAdminMember).List", + " E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/admin/member.go:525", + " 2). hotgo/internal/controller/admin/admin.(*cMember).List", + " E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/controller/admin/admin/member.go:157", "" + ], + "timestamp": 1684145107, + "traceID": "084022730d495f17f19e550140f3e1a8" +} +``` + +- 关闭debug时的客户端响应: +```json +{ + "code": -1, + "message": "用户创建失败,请稍后重试!~", + "timestamp": 1684145107, + "traceID": "084022730d495f17f19e550140f3e1a8" +} +``` + +- 控制台的输出日志: + +```shell +2023-05-15 18:05:07.776 {084022730d495f17f19e550140f3e1a8} 200 "GET http localhost:8000 /admin/member/list?page=1&pageSize=10&roleId=-1 HTTP/1.1" 0.002, 127.0.0.1, "http://192.168.0.207:8001/login", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 Co +re/1.94.197.400 QQBrowser/11.7.5287.400", -1, "", "" +Stack: +1. 用户创建失败,请稍后重试!~ + 1). hotgo/internal/logic/admin.(*sAdminMember).List + E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/admin/member.go:526 +2. 这是一个sql执行错误 + 1). hotgo/internal/logic/admin.(*sAdminMember).List + E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/response.go:24 + 13). hotgo/internal/logic/middleware.(*sMiddleware).DemoLimit + E:/Users/Administrator/Desktop/gosrc/hotgo_dev/server/internal/logic/middleware/init.go:90 + +``` + +- 如果你开启了访问日志,那么日志记录中会详细记录本次请求的相关信息,内容如下: +![./images/sys-middleware-error-log.png](./images/sys-middleware-error-log.png) + + +#### 重写错误码 +- hotgo默认使用了gf内置的错误码进行业务处理,通常情况下成功状态码为`0`,失败状态码为`-1` +- 查看gf内置错误码:https://goframe.org/pages/viewpage.action?pageId=30739587 +- 以下是自定义错误码的简单例子: + +```go +package main + +import ( + "github.com/gogf/gf/v2/errors/gerror" +) + +func test() error { + // 使用自定义状态码30001响应客户端 + err = gerror.NewCode(gcode.New(30001, "用户创建失败,请稍后重试!~", nil)) + return err +} +``` + + +- 客户端响应如下: +```json +{ + "code": 30001, + "message": "用户创建失败,请稍后重试!~", + "timestamp": 1684146313, + "traceID": "b4f90e16264a5f17cd3fc27141aba448" +} +``` + +### 更多 +- 更多关于中间件/拦截器的介绍请参考:https://goframe.org/pages/viewpage.action?pageId=55289881 + diff --git a/docs/guide-zh-CN/sys-payment.md b/docs/guide-zh-CN/sys-payment.md new file mode 100644 index 0000000..e102b23 --- /dev/null +++ b/docs/guide-zh-CN/sys-payment.md @@ -0,0 +1,102 @@ +## 支付网关 + +目录 + +- 介绍 +- 一个简单的支付流程 +- 支付配置 +- 创建支付订单 +- 注册支付回调 +- 订单退款 +- 单笔转账(待更新) +- 其他 + + +### 介绍 +> 在web应用开发中,支付功能可以做为不可或缺的一部分,HotGo提供了相对通用的支付网关,目前已支持支付宝、微信支付、QQ支付,包含创建支付订单、支付回调、订单退款等功能,开发者几乎只需关注订单业务处理即可。 + +#### 一个简单的支付流程 + +```mermaid + +graph TD + A(用户下单) --> B(创建订单
创建成功后,将订单支付信息提交到支付网关处理) + B -->|网关返回失败| H(提示错误) + B -->|网关返回成功| G(拿到支付链接,用户侧跳转支付页面) + G -->|支付完成| C(支付平台通知支付网关
支付网关进行验签和效验支付处理状态) -->|验证通过,回调订单业务| E(订单更新状态,发放业务产品) + G -->|支付取消| D(订单支付超时关闭) + + +``` + + +### 支付配置 +> 请到 系统设置 -> 配置管理 -> 支付配置 -> 找到你需要的支付方式,进行配置即可 + + + +### 创建支付订单 + +```go +package main + +func main() { + // 创建支付网关订单 + create, err := service.Pay().Create(ctx, payin.PayCreateInp{ + Subject: "充值100元", + OrderSn: "唯一业务订单编号", + OrderGroup: "admin_order", // 订单分组,用于订单分类和绑定支付成功的回调方法 + PayType: "wxpay", // 微信支付 + TradeType: "scan", // 二维码扫码 + PayAmount: "100", // 100元 + }) +} +``` + + +### 注册支付回调 +- 在文件`server/internal/logic/pay/notify.go` 加入你的业务订单分组回调方法,当订单支付成功验签通过后会自动进行回调,参考以下: + +```go +package global + +import ( + "hotgo/internal/consts" + "hotgo/internal/library/payment" + "hotgo/internal/service" +) + +// RegisterNotifyCall 注册支付成功回调方法 +func (s *sPay) RegisterNotifyCall() { + payment.RegisterNotifyCallMap(map[string]payment.NotifyCallFunc{ + consts.OrderGroupAdminOrder: service.AdminOrder().PayNotify, // 后台充值订单 + // ... + }) +} +``` + +### 订单退款 + + +```go +package main + +func main() { + refund, err := service.PayRefund().Refund(ctx, payin.PayRefundInp{ + OrderSn: "唯一业务订单编号", + RefundMoney: "退款金额", + Reason: "买家申请退款原因", + Remark: "商家同意退款备注", + }) +} +``` + + +### 单笔转账(待更新) + + + +### 其他 +- 由于各个支付平台中交易方式较多,如果目前已有的交易方式不满足你的支付场景需求,请自行参考gopay文档对文件`server/internal/library/payment/payment.go`中的`PayClient`接口实现进行扩展 +- gopay文档地址:https://github.com/go-pay/gopay + diff --git a/docs/guide-zh-CN/sys-queue.md b/docs/guide-zh-CN/sys-queue.md new file mode 100644 index 0000000..e124805 --- /dev/null +++ b/docs/guide-zh-CN/sys-queue.md @@ -0,0 +1,203 @@ +## 消息队列 + +目录 + +- 配置文件 +- 实现接口 +- 一个例子 +- 控制台 +- 自定义队列驱动 + +> 系统默认的队列驱动为disk(磁盘队列),目前已支持:disk、redis、rocketmq、kafka等多种驱动。请自行选择适合你的驱动使用。 + +### 配置文件 +- 配置文件:server/manifest/config/config.yaml + +```yaml +# 消息队列 +queue: + switch: true # 队列开关,可选:true|false,默认为true + driver: "disk" # 队列驱动,可选:disk|redis|rocketmq|kafka,默认为disk + groupName: "hotgo" # mq群组名称 + # 磁盘队列 + disk: + path: "./storage/diskqueue" # 数据存放路径 + batchSize: 100 # 每100条消息同步一次,batchSize和batchTime满足其一就会同步一次 + batchTime: 1 # 每1秒消息同步一次 + segmentSize: 10485760 # 每个topic分片数据文件最大字节,默认10M + segmentLimit: 3000 # 每个topic最大分片数据文件数量,超出部分将会丢弃 + # redis,默认使用全局redis运行队列 + redis: + timeout: 0 # 队列超时时间以秒为单位,0表示永不超时。如果队列在设定的超时时间内没有被消费,则会被销毁 + rocketmq: + nameSrvAdders: ["127.0.0.1:9876"] # nameSrvAdder+端口,支持多个 + accessKey: "" # 选填。如果开启了acl控制就必填 + secretKey: "" # 选填。如果开启了acl控制就必填 + brokerAddr: "127.0.0.1:10911" # brokerAddr+端口,选填。用于消费者订阅主题前会检查主题是否存在,不存在会自动创建。你也可以在rocketmq控制台手动创建主题 + retry: 0 # 重试次数 + logLevel: "info" # 系统日志级别,可选:all|close|debug|info|warn|error|fatal + kafka: + address: "127.0.0.1:9092" # kafka地址+端口 + version: "2.0.0.0" # kafka专属配置,默认2.0.0.0 + randClient: true # 开启随机生成clientID,可以实现启动多实例同时一起消费相同topic,加速消费能力的特性,默认为true + multiConsumer: true # 是否支持创建多个消费者 +``` + +### 实现接口 +- 为了提供高度的扩展性,消费队列在设计上采用了接口化的思路。只需要实现以下接口,您就可以在任何地方注册和使用消费队列消费功能,从而实现更大的灵活性和可扩展性。 + +```go +// Consumer 消费者接口,实现该接口即可加入到消费队列中 +type Consumer interface { + GetTopic() string // 获取消费主题 + Handle(ctx context.Context, mqMsg MqMsg) (err error) // 处理消息的方法 +} +``` + + +### 一个例子 + +每个被发送到队列的消息应该被定义为一个单独的文件结构。 + +例如,如果您需要异步记录系统日志,内容大致如下: + +- 文件路径:server/internal/queues/sys_log.go + +```go +package queues + +import ( + "context" + "encoding/json" + "hotgo/internal/consts" + "hotgo/internal/library/queue" + "hotgo/internal/model/entity" + "hotgo/internal/service" +) + +func init() { + queue.RegisterConsumer(SysLog) +} + +// SysLog 系统日志 +var SysLog = &qSysLog{} + +type qSysLog struct{} + +// GetTopic 主题 +func (q *qSysLog) GetTopic() string { + return consts.QueueLogTopic +} + +// Handle 处理消息 +func (q *qSysLog) Handle(ctx context.Context, mqMsg queue.MqMsg) (err error) { + var data entity.SysLog + if err = json.Unmarshal(mqMsg.Body, &data); err != nil { + return err + } + return service.SysLog().RealWrite(ctx, data) +} + +``` + +下面是将消息添加到队列的方式,大概内容如下: + +```go +package main + +import ( + "fmt" + "hotgo/internal/consts" + "hotgo/internal/library/queue" + "hotgo/internal/model/entity" +) + +func test() { + data := &entity.SysLog{ + //... + } + if err := queue.Push(consts.QueueLogTopic, data); err != nil { + fmt.Printf("queue.Push err:%+v", err) + } +} + +``` + +延迟队列,目前只有redis、rocketmq驱动支持: + +```go +package main + +import ( + "fmt" + "hotgo/internal/consts" + "hotgo/internal/library/queue" + "hotgo/internal/model/entity" +) + +func test() { + data := &entity.SysLog{ + //... + } + + // redis 延迟10秒 + if err := queue.SendDelayMsg(consts.QueueLogTopic, data, 10); err != nil { + fmt.Printf("queue.Push err:%+v", err) + } + + // rocketmq 延迟5秒 + // 注意rocketmq这里传入的是延迟级别,而不是秒 + // 消息的延时级别level一共有18级,分别为: + // 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h + // 参考:https://github.com/apache/rocketmq-client-go/blob/0e19ee654819bda396a08d950c883f9008b8222b/primitive/message.go#L174 + if err := queue.SendDelayMsg(consts.QueueLogTopic, data, 2); err != nil { + fmt.Printf("queue.Push err:%+v", err) + } +} + +``` + +### 控制台 + +控制台用于处理队列消息,即消费者。 + +相关命令请参考: [控制台](sys-console.md) + + +### 自定义队列驱动 + +只需实现消息队列的生成者和消费者接口,并加入到初始化中进行相应调用即可。 + +- 接口片段:server/internal/library/queue/init.go + +```go +package queue + +import ( + "time" +) + +type MqMsg struct { + RunType int `json:"run_type"` + Topic string `json:"topic"` + MsgId string `json:"msg_id"` + Offset int64 `json:"offset"` + Partition int32 `json:"partition"` + Timestamp time.Time `json:"timestamp"` + Body []byte `json:"body"` +} + +type MqProducer interface { + SendMsg(topic string, body string) (mqMsg MqMsg, err error) + SendByteMsg(topic string, body []byte) (mqMsg MqMsg, err error) + SendDelayMsg(topic string, body string, delay int64) (mqMsg MqMsg, err error) +} + +type MqConsumer interface { + ListenReceiveMsgDo(topic string, receiveDo func(mqMsg MqMsg)) (err error) +} + +``` + +将实现过接口(MqProducer、MqConsumer)的实例方法分别加入到NewProducer、NewConsumer中进行相应调用即可。 + diff --git a/docs/guide-zh-CN/sys-tcp-server.md b/docs/guide-zh-CN/sys-tcp-server.md new file mode 100644 index 0000000..6f81a5b --- /dev/null +++ b/docs/guide-zh-CN/sys-tcp-server.md @@ -0,0 +1,271 @@ +## TCP服务器 + +目录 + +- 配置文件 +- 一个基本的消息收发例子 +- 注册路由 +- 拦截器 +- 服务认证 +- 更多 + +> HotGo基于GoFrame的TCP服务器组件,提供了一个简单而灵活的方式快速搭建基于TCP的服务应用。集成了许多常用功能,如长连接、服务认证、路由分发、RPC消息、拦截器和数据绑定等,大大简化和规范了服务器开发流程。 + +### 配置文件 +- 配置文件:server/manifest/config/config.yaml + +```yaml +tcp: + # 服务器 + server: + address: ":8099" + # 客户端 + client: + # 定时任务 + cron: + group: "cron" # 分组名称 + name: "cron1" # 客户端名称 + address: "127.0.0.1:8099" # 服务器地址 + appId: "1002" # 应用名称 + secretKey: "hotgo" # 密钥 + # 系统授权 + auth: + group: "auth" # 分组名称 + name: "auth1" # 客户端名称 + address: "127.0.0.1:8099" # 服务器地址 + appId: "mengshuai" # 应用名称 + secretKey: "123456" # 密钥 + +``` +- 可以看到,除了服务器配置外,还有两个客户端配置`cron` 和`auth` +- `cron`是HotGo内置的定时任务服务,和http服务通过RPC通讯以实现和后台交互,使其可以独立、集群部署。 +- `auth`可以为第三方平台提供授权服务。如果你需要他,可以将它部署在第三方程序中,在重要的位置进行授权验证。 + +### 一个基本的消息收发测试用例 + +- 文件路径:server/internal/library/network/tcp/tcp_example_test.go + +```go +package tcp_test + +import ( + "context" + "fmt" + "github.com/gogf/gf/v2/os/gctx" + "github.com/gogf/gf/v2/test/gtest" + "hotgo/internal/library/network/tcp" + "testing" + "time" +) + +var T *testing.T // 声明一个全局的 *testing.T 变量 + +type TestMsgReq struct { + Name string `json:"name"` +} + +type TestMsgRes struct { + tcp.ServerRes +} + +type TestRPCMsgReq struct { + Name string `json:"name"` +} + +type TestRPCMsgRes struct { + tcp.ServerRes +} + +func onTestMsg(ctx context.Context, req *TestMsgReq) { + fmt.Printf("服务器收到消息 ==> onTestMsg:%+v\n", req) + conn := tcp.ConnFromCtx(ctx) + gtest.C(T, func(t *gtest.T) { + t.AssertNE(conn, nil) + }) + + res := new(TestMsgRes) + res.Message = fmt.Sprintf("你的名字:%v", req.Name) + conn.Send(ctx, res) +} + +func onResponseTestMsg(ctx context.Context, req *TestMsgRes) { + fmt.Printf("客户端收到响应消息 ==> TestMsgRes:%+v\n", req) + err := req.GetError() + gtest.C(T, func(t *gtest.T) { + t.AssertNil(err) + }) +} + +func onTestRPCMsg(ctx context.Context, req *TestRPCMsgReq) (res *TestRPCMsgRes, err error) { + fmt.Printf("服务器收到消息 ==> onTestRPCMsg:%+v\n", req) + res = new(TestRPCMsgRes) + res.Message = fmt.Sprintf("你的名字:%v", req.Name) + return +} + +func startTCPServer() { + serv := tcp.NewServer(&tcp.ServerConfig{ + Name: "hotgo", + Addr: ":8002", + }) + + // 注册路由 + serv.RegisterRouter( + onTestMsg, + ) + + // 注册RPC路由 + serv.RegisterRPCRouter( + onTestRPCMsg, + ) + + // 服务监听 + err := serv.Listen() + gtest.C(T, func(t *gtest.T) { + t.AssertNil(err) + }) +} + +// 一个基本的消息收发 +func TestSendMsg(t *testing.T) { + T = t + go startTCPServer() + + ctx := gctx.New() + client := tcp.NewClient(&tcp.ClientConfig{ + Addr: "127.0.0.1:8002", + }) + + // 注册路由 + client.RegisterRouter( + onResponseTestMsg, + ) + + go func() { + err := client.Start() + gtest.C(T, func(t *gtest.T) { + t.AssertNil(err) + }) + }() + + // 确保服务都启动完成 + time.Sleep(time.Second * 1) + + // 拿到客户端的连接 + conn := client.Conn() + gtest.C(T, func(t *gtest.T) { + t.AssertNE(conn, nil) + }) + + // 向服务器发送tcp消息,不会阻塞程序执行 + err := conn.Send(ctx, &TestMsgReq{Name: "Tom"}) + gtest.C(T, func(t *gtest.T) { + t.AssertNil(err) + }) + + // 向服务器发送rpc消息,会等待服务器响应结果,直到拿到结果或响应超时才会继续 + var res TestRPCMsgRes + if err = conn.RequestScan(ctx, &TestRPCMsgReq{Name: "Tony"}, &res); err != nil { + gtest.C(T, func(t *gtest.T) { + t.AssertNil(err) + }) + } + + fmt.Printf("客户端收到RPC消息响应 ==> TestRPCMsgRes:%+v\n", res) + time.Sleep(time.Second * 1) +} + +``` + + +### 注册路由 + +- 从上面的例子可以看到,不管是普通TCP消息和RPC消息的请求/响应结构体都采用类似GF框架的规范路由的结构,请求`XxxRes`/响应`XxxRes`的格式,是不是很亲切? + + +### 拦截器 + +- 不管是服务端还是客户端,在初始化时都可以注册多个拦截器来满足更多场景的服务开发,下面是一个使用例子: + +```go +package main + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "hotgo/internal/library/network/tcp" +) + +func main() { + serv = tcp.NewServer(&tcp.ServerConfig{ + Name: "hotgo", + Addr: ":8002", + }) + + // 注册拦截器 + // 执行顺序是从前到后,即Interceptor -> Interceptor2 -> Interceptor3。如果中间有任意一个抛出错误,则会中断后续处理 + serv.RegisterInterceptor(Interceptor, Interceptor2, Interceptor3) + + // 服务监听 + if err := serv.Listen(); err != nil { + if !serv.IsClose() { + g.Log().Warningf(ctx, "TCPServer Listen err:%v", err) + } + } +} + +func Interceptor(ctx context.Context, msg *tcp.Message) (err error) { + // 可以在拦截器中通过上下文拿到连接 + conn := tcp.ConnFromCtx(ctx) + + // 拿到原始请求消息 + g.Dump(msg) + + // 如果想要中断后续处理只需返回一个错误即可,但注意两种情况 + // tcp消息:如果你还想对该消息进行回复应在拦截器中进行处理,例如:conn.Send(ctx, 回复消息内容) + // rpc消息:返回一个错误后系统会将错误自动回复到rpc响应中,无需单独处理 + return +} + +func Interceptor2(ctx context.Context, msg *tcp.Message) (err error) { + // ... + return +} + +func Interceptor3(ctx context.Context, msg *tcp.Message) (err error) { + // ... + return +} + +``` + + +### 服务认证 + +- 一般情况下,建议客户端连接到服务器时都通过`授权许可证`的方式进行登录认证,当初始化客户端配置认证数据时,连接成功后会自动进行登录认证。 + +```go + // 创建客户端配置 + clientConfig := &tcp.ClientConfig{ + Addr: "127.0.0.1:8002", + AutoReconnect: true, + // 认证数据 + // 认证数据可以在后台-系统监控-在线服务-许可证列表中添加,同一个授权支持多个服务使用,但多个服务不能使用相同的名称进行连接 + Auth: &tcp.AuthMeta{ + Name: "服务名称", + Group: "服务分组", + AppId: "APPID", + SecretKey: "SecretKey", + }, + } + + // 初始化客户端 + client = tcp.NewClient(clientConfig) +``` + + +### 更多 + +TCP服务器源码路径:server/internal/library/network/tcp + +更多文档请参考:https://goframe.org/pages/viewpage.action?pageId=1114625 diff --git a/docs/guide-zh-CN/sys-tenant.md b/docs/guide-zh-CN/sys-tenant.md new file mode 100644 index 0000000..6c97d79 --- /dev/null +++ b/docs/guide-zh-CN/sys-tenant.md @@ -0,0 +1,175 @@ +## SaaS多租户 + + +目录 + +- 介绍 +- 职责划分 +- 如何在HotGo开发多租户业务 +- 多租户数据库设计 +- 多租户功能演示 + +### 介绍 + +SaaS系统多租户多应用设计,已成为互联网企业的重要发展建设方向。核心在于多租户SAAS系统独立前台、共享后台、共享数据库的平台应用架构。 + +> 目前HotGo的部分基础功能并不完全支持多租户设计,但这并不妨碍开发者基于HotGo构建自己的多租户产品 + + +### 职责划分 + +在SaaS系统多租户中,不同身份用户的职责功能划分假设可以是这样: + +> 这只是一个粗略的概念,实际开发时应根据业务来进行调整,每个身份也都不是必须的。 + +| 身份 | 标识 | 职责和功能划分 | +|-----------------|----------|-------------------------------------------------------| +| 公司| company | 管理整个平台,包括商户和用户账户、系统设置以及其他全局性业务流程。 | +| 租户 | tenant | 多租户系统中顶层实体客户、组织或实体。有自己的多个商户、用户、产品、订单等。拥有独立的数据隔离和安全边界。 | +| 商户 | merchant | 受租户的监管和管理,可独立经营的实体。提供产品或服务,管理自己的业务,包括库存管理、订单处理、结算等。 | +| 用户 | user | 真正购买产品或享受服务的人,与商户互动,管理个人信息等个性化功能。 | + + +### 如何在HotGo开发多租户业务 +#### 一、应用功能 + +根据角色来划分用户的后台功能,在创建用户时为其绑定角色,然后为不同角色分配不同的功能菜单 + +请参考: [权限控制](sys-auth.md) + + +#### 二、 数据隔离 + +根据部门来划定用户的数据权限范围,在创建用户时为其绑定部门 + +- 在用户登录成功后,server端可通过上下文来获取用户部门类型来确定用户身份 +- 文件路径:server/internal/library/contexts/context.go + +```go +package contexts + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "hotgo/internal/consts" + "hotgo/internal/model" +) + +// GetDeptType 获取用户部门类型 +func GetDeptType(ctx context.Context) string { + user := GetUser(ctx) + if user == nil { + return "" + } + return user.DeptType +} + +// IsCompanyDept 是否为公司部门 +func IsCompanyDept(ctx context.Context) bool { + return GetDeptType(ctx) == consts.DeptTypeCompany +} + +// IsTenantDept 是否为租户部门 +func IsTenantDept(ctx context.Context) bool { + return GetDeptType(ctx) == consts.DeptTypeTenant +} + +// IsMerchantDept 是否为商户部门 +func IsMerchantDept(ctx context.Context) bool { + return GetDeptType(ctx) == consts.DeptTypeMerchant +} + +// IsUserDept 是否为普通用户部门 +func IsUserDept(ctx context.Context) bool { + return GetDeptType(ctx) == consts.DeptTypeUser +} +``` + +- 在用户登录成功后,web端可通`useUserStore`来获取用户部门类型来确定用户身份 +- 文件路径:web/src/store/modules/user.ts + +```vue + +``` + +### 多租户数据库设计 + +HotGo定位是中小型应用开发,推荐采用一套数据库不同Schema。就是在多租户业务表中加入用户标识字段,来区分不同用户的数据,如:`tenant_id` + +- 参考文章:https://blog.csdn.net/haponchang/article/details/104246317 + + +### 多租户功能演示 + +请登录后台【插件应用】-【功能案例】-【多租户功能演示】查看 + +#### 自动维护租户关系 + +- 只需在表设计时包含以下字段,即可使用handler和hook实现租户权限过滤和租户关系维护 + +| 字段名称 | 数据类型 | 字段注释 | 必选 | +|------|----------|------|-----------------------------------------------------| +| tenant_id | bigint(20) | 租户ID | 否 | +| merchant_id | bigint(20) | 商户ID | 否 | +| user_id | bigint(20) | 用户ID | 否 | + +下面是多租户功能演示例子代码中的使用片段 + +- 封装查询Model + +```go +// Model 多租户功能演示ORM模型 +func (s *sSysTenantOrder) Model(ctx context.Context, option ...*handler.Option) *gdb.Model { + if len(option) == 0 { + // 过滤多租户数据权限 + option = append(option, &handler.Option{ + FilterTenant: true, + //FilterAuth: true, // 如果还需要维护created_by、member_id等部门数据权限范围可放开注释 + }) + } + return handler.Model(dao.AddonHgexampleTenantOrder.Ctx(ctx), option...) +} +``` + +- 增改数据自动维护租户关系 +```go +// Edit 修改/新增多租户功能演示 +func (s *sSysTenantOrder) Edit(ctx context.Context, in *sysin.TenantOrderEditInp) (err error) { + return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { + + // 修改 + if in.Id > 0 { + if _, err = s.Model(ctx). + Fields(sysin.TenantOrderUpdateFields{}). + WherePri(in.Id).Data(in). + Hook(hook.SaveTenant). // 自动维护租户关系更新 + Update(); err != nil { + } + return + } + + // 新增 + if _, err = dao.AddonHgexampleTenantOrder.Ctx(ctx). + Fields(sysin.TenantOrderInsertFields{}). + Hook(hook.SaveTenant). // 自动维护租户关系更新 + Data(in). + Insert(); err != nil { + } + return + }) +} +``` + +相关代码文件:/server/addons/hgexample/logic/sys/tenant_order.go + diff --git a/docs/guide-zh-CN/sys-test.md b/docs/guide-zh-CN/sys-test.md new file mode 100644 index 0000000..26a1681 --- /dev/null +++ b/docs/guide-zh-CN/sys-test.md @@ -0,0 +1,3 @@ +## 单元测试 + +请参考:https://goframe.org/pages/viewpage.action?pageId=1114153 \ No newline at end of file diff --git a/docs/guide-zh-CN/sys-utility.md b/docs/guide-zh-CN/sys-utility.md new file mode 100644 index 0000000..4a66582 --- /dev/null +++ b/docs/guide-zh-CN/sys-utility.md @@ -0,0 +1,20 @@ +## 工具方法 + +HotGo还提供一些系统中常用的工具库方法,在这里简单说明: + +``` +/server +├── utility +│ ├── charset # 字符串处理 +│ ├── convert # 数据类型转换 +│ ├── encrypt # 数据加密/解密 +│ ├── excel # 电子表格导出/导入 +│ ├── file # 文件/目录处理 +│ ├── format # 数据格式化 +│ ├── simple # 一些简捷函数 +│ ├── tree # 树形结构 +│ ├── url # URL处理 +│ ├── useragent # 请求头代理处理 +└── └── validate # 数据验证 +``` + diff --git a/docs/guide-zh-CN/sys-webhook.md b/docs/guide-zh-CN/sys-webhook.md new file mode 100644 index 0000000..ef5d21e --- /dev/null +++ b/docs/guide-zh-CN/sys-webhook.md @@ -0,0 +1,3 @@ +## WebHook + +请参考:https://goframe.org/pages/viewpage.action?pageId=1114387 diff --git a/docs/guide-zh-CN/sys-websocket-client.md b/docs/guide-zh-CN/sys-websocket-client.md new file mode 100644 index 0000000..517c5e8 --- /dev/null +++ b/docs/guide-zh-CN/sys-websocket-client.md @@ -0,0 +1,213 @@ +## WebSocket客户端 + +目录 + +- 全局消息监听 +- 单页面消息监听 +- 发送消息 + +> 基于WebSocket服务器,hotgo还对客户端的上做了一些封装,使其使用起来更加方便 +- [WebSocket服务器](sys-websocket-server.md) + +### 全局消息监听 +- 所有全局的消息监听都在这里 +- 文件路径:web/src/utils/websocket/registerMessage.ts + +```ts +import { TABS_ROUTES } from '@/store/mutation-types'; +import { SocketEnum } from '@/enums/socketEnum'; +import { useUserStoreWidthOut } from '@/store/modules/user'; +import { notificationStoreWidthOut } from '@/store/modules/notification'; +import { addOnMessage, WebSocketMessage } from '@/utils/websocket/index'; + +// 注册全局消息监听 +export function registerGlobalMessage() { + // 心跳 + addOnMessage(SocketEnum.EventPing, function (_message: WebSocketMessage) { + // console.log('ping..'); + }); + + // 强制退出 + addOnMessage(SocketEnum.EventKick, function (_message: WebSocketMessage) { + const useUserStore = useUserStoreWidthOut(); + useUserStore.logout().then(() => { + // 移除标签页 + localStorage.removeItem(TABS_ROUTES); + location.reload(); + }); + }); + + // 消息通知 + addOnMessage(SocketEnum.EventNotice, function (message: WebSocketMessage) { + const notificationStore = notificationStoreWidthOut(); + notificationStore.triggerNewMessages(message.data); + }); + + // 更多全局消息处理都可以在这里注册 + // ... +} + +``` + +#### 单页面消息监听 +- 当你只需要某个页面使用WebSocket,这将是一个不错的选择,下面是一个简单的演示例子 +- 文件路径:web/src/views/addons/hgexample/portal/websocketTest.vue + +```vue + + + + + +``` + +#### 发送消息 +- 向服务器发送一条消息 + +```ts + import { sendMsg } from '@/utils/websocket'; + + const event = 'admin/addons/hgexample/testMessage'; // 消息路由 + const data: object | null = { // 消息内容 + message: 'message content...', + }; + const isRetry = false; // 发送失败是否重试,不传默认为true + + // 基本使用 + sendMsg(event, data); + + // 无消息内容 + sendMsg(event); + + // 发送失败不重试 + sendMsg(event, data, isRetry); +``` diff --git a/docs/guide-zh-CN/sys-websocket-server.md b/docs/guide-zh-CN/sys-websocket-server.md new file mode 100644 index 0000000..1e6db23 --- /dev/null +++ b/docs/guide-zh-CN/sys-websocket-server.md @@ -0,0 +1,148 @@ +## WebSocket服务器 + +目录 + +- 一个基本的消息收发例子 +- 常用方法 +- HTTP接口 +- 其他 + +> hotgo提供了一个WebSocket服务器,随`HTTP服务`启停。集成了许多常用功能,如JWT身份认证、路由消息处理器、一对一消息/群组消息/广播消息、在线用户管理、心跳保持等,大大简化和规范了WebSocket服务器的开发流程。 +- [Websocket客户端](sys-websocket-client.md) + +### 一个基本的消息收发例子 +- 这是一个基本的消息接收并进行处理的简单例子 + +#### 1.消息处理接口 +- 消息处理在设计上采用了接口化的思路。只需要实现以下接口,即可进行WebSocket消息注册 +- 文件路径:server/internal/websocket/model.go + +```go +package websocket + +// EventHandler 消息处理器 +type EventHandler func(client *Client, req *WRequest) +``` + +#### 2.定义消息处理方法 +- 以下是功能案例中的一个简单演示,实现了消息处理接口,并将收到的消息原样发送给客户端 +- 文件路径:server/addons/hgexample/controller/websocket/handler/index.go + +```go +package handler + +import ( + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/frame/g" + "hotgo/internal/websocket" +) + +var ( + Index = cIndex{} +) + +type cIndex struct{} + +// TestMessage 测试消息 +func (c *cIndex) TestMessage(client *websocket.Client, req *websocket.WRequest) { + g.Log().Infof(client.Context(), "收到客户端测试消息:%v", gjson.New(req).String()) + // 将收到的消息原样发送给客户端 + websocket.SendSuccess(client, req.Event, req.Data) +} +``` + +#### 3.注册消息 +- 定义消息处理方法后,需要将其注册到WebSocket消息处理器,一般放在对应应用模块的`router/websocket.go`下即可 +- 文件路径:server/addons/hgexample/router/websocket.go + +```go +package router + +import ( + "context" + "github.com/gogf/gf/v2/net/ghttp" + "hotgo/addons/hgexample/controller/websocket" + "hotgo/addons/hgexample/controller/websocket/handler" + ws "hotgo/internal/websocket" +) + +// WebSocket ws路由配置 +func WebSocket(ctx context.Context, group *ghttp.RouterGroup) { + // 注册消息路由 + ws.RegisterMsg(ws.EventHandlers{ + "admin/addons/hgexample/testMessage": handler.Index.TestMessage, // 测试消息 + }) + + // 这里"admin/addons/hgexample/testMessage"代表的是一个消息处理ID,可以自定义。建议的格式是和HTTP接口格式保持一致,这样还可以便于对用户请求的消息进行权限验证 + // 客户端连接后,向WebSocket服务器发送event为"admin/addons/hgexample/testMessage"的消息时,会调用TestMessage方法 +} +``` + +- 到此,你已了解了WebSocket消息接收并进行处理的基本流程 + + +### 常用方法 +- websocket服务器还提供了一些常用的方法,下面只对部分进行说明 + +```go +func test() { + websocket.SendToAll() // 发送全部客户端 + websocket.SendToClientID() // 发送单个客户端 + websocket.SendToUser() // 发送单个用户 + websocket.SendToTag() // 发送某个标签、群组 + + client := websocket.Manager().GetClient(id) // 通过连接ID获取客户端连接 + client := websocket.Manager().GetUserClient(userId) // 通过用户ID获取客户端连接,因为用户是可多端登录的,这里返回的是一个切片 + + websocket.SendSuccess(client, "admin/addons/hgexample/testMessage", "消息内容") // 向指定客户端发送一条成功的消息 + websocket.SendError(client, "admin/addons/hgexample/testMessage", gerror.New("错误内容")) // 向指定客户端发送一条失败的消息 + +} +``` + + +### HTTP接口 +- 你还可以通过http接口方式调用WebSocket发送消息 +- 参考文件:server/internal/controller/websocket/send.go + + +### 其他 +- WebSocket被连接时需验证用户认证中间件,所以用户必须登录成功后才能连接成功 +- 参考文件:server/internal/logic/middleware/weboscket_auth.go + +```go +package middleware + +import ( + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/text/gstr" + "hotgo/internal/consts" + "hotgo/internal/library/response" + "hotgo/utility/simple" +) + +// WebSocketAuth websocket鉴权中间件 +func (s *sMiddleware) WebSocketAuth(r *ghttp.Request) { + var ( + ctx = r.Context() + path = gstr.Replace(r.URL.Path, simple.RouterPrefix(ctx, consts.AppWebSocket), "", 1) + ) + + // 不需要验证登录的路由地址 + if s.IsExceptLogin(ctx, consts.AppWebSocket, path) { + r.Middleware.Next() + return + } + + // 将用户信息传递到上下文中 + if err := s.DeliverUserContext(r); err != nil { + response.JsonExit(r, gcode.CodeNotAuthorized.Code(), err.Error()) + return + } + + r.Middleware.Next() +} +``` + +- 如果您不要求用户进行登录即可使用 WebSocket,那么需要对身份验证中间件进行修改。然而,这样做会降低连接的安全性,并且无法应用于需要确定用户身份的情景,因此并不建议采取这种策略 diff --git a/docs/guide-zh-CN/web-deploy.md b/docs/guide-zh-CN/web-deploy.md new file mode 100644 index 0000000..51bc603 --- /dev/null +++ b/docs/guide-zh-CN/web-deploy.md @@ -0,0 +1,261 @@ +## 独立部署 + +目录 + +- 构建 +- 旧版浏览器兼容 +- 预览 +- 分析构建文件体积 +- 压缩 +- 部署 + +> 在实际开发中,web前端也可能需要独立部署,所以在此提供一下部署方案。 + +### 构建 + +项目开发完成之后,执行以下命令进行构建 +```shell +pnpm run build 或 npm run build +``` +构建打包成功之后,会在根目录生成 dist 文件夹,里面就是构建打包好的文件 + + +### 旧版浏览器兼容 + +在 .env.production 内 + +设置 `VITE_LEGACY=true` 即可打包出兼容旧版浏览器的代码 +```shell +VITE_LEGACY = true +``` + + + +### 预览 + +发布之前可以在本地进行预览,有多种方式,这里介绍两种 + +##### 不能直接打开构建后的 html 文件 + +使用项目自定的命令进行预览(推荐) +```shell +# 先打包在进行预览 +pnpm run preview 或 npm run preview +# 直接预览本地 dist 文件目录 +pnpm run preview:dist 或 npm run preview:dist +``` + +- 本地服务器预览(通过 live-server) +```shell +# 1.全局安装live-server +npm -g install live-server +# 2. 进入打包的后目录 +cd ./dist +# 本地预览,默认端口8080 +live-server +# 指定端口 +live-server --port 9000 +``` + +### 分析构建文件体积 +如果你的构建文件很大,可以通过项目内置 [rollup-plugin-analyzer](https://github.com/doesdev/rollup-plugin-analyzer) 插件进行代码体积分析,从而优化你的代码。 +```shell +pnpm run report 或 npm run report +``` +运行之后,在自动打开的页面可以看到具体的体积分布,以分析哪些依赖有问题。 + +- 左上角可以切换 显示 gzip 或者 brotli + +![start-deploy-report.png](./images/start-deploy-report.png) + + +### 压缩 + +#### 开启 gzip 压缩 + +开启 gzip,并配合 nginx 的 `gzip_static` 功能可以大大加快页面访问速度 + +- 只需开启 `VITE_BUILD_COMPRESS='gzip'` 即可在打包的同时生成 .gz 文件 + +```shell +# 根据自己路径来配置更改 +# 例如部署在nginx /next/路径下 则VITE_PUBLIC_PATH=/next/ +VITE_PUBLIC_PATH=/ +``` + +#### 开启 brotli 压缩 + +brotli 是比 gzip 压缩率更高的算法,可以与 gzip 共存不会冲突,需要 nginx 安装指定模块并开启即可。 + +- 只需开启 VITE_BUILD_COMPRESS='brotli' 即可在打包的同时生成 .br 文件 + +```shell +# 根据自己路径来配置更改 +# 例如部署在nginx /next/路径下 则VITE_PUBLIC_PATH=/next/ +VITE_PUBLIC_PATH=/ +``` + + +#### 同时开启 gzip 与 brotli + +只需开启 VITE_BUILD_COMPRESS='brotli,gzip' 即可在打包的同时生成 .gz 和 .br 文件。 + + +#### gzip 与 brotli 在 nginx 内的配置 +``` +http { + # 开启gzip + gzip on; + # 开启gzip_static + # gzip_static 开启后可能会报错,需要安装相应的模块, 具体安装方式可以自行查询 + # 只有这个开启,vue文件打包的.gz文件才会有效果,否则不需要开启gzip进行打包 + gzip_static on; + gzip_proxied any; + gzip_min_length 1k; + gzip_buffers 4 16k; + #如果nginx中使用了多层代理 必须设置这个才可以开启gzip。 + gzip_http_version 1.0; + gzip_comp_level 2; + gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; + gzip_vary off; + gzip_disable "MSIE [1-6]\."; + + # 开启 brotli压缩 + # 需要安装对应的nginx模块,具体安装方式可以自行查询 + # 可以与gzip共存不会冲突 + brotli on; + brotli_comp_level 6; + brotli_buffers 16 8k; + brotli_min_length 20; + brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml; +} + +``` + +### 部署 + +- 注意:项目默认是在生产环境开启 Mock,这样做非常不好,只是为了演示环境有数据,不建议在生产环境使用 Mock,而应该使用真实的后台接口,并将 Mock 关闭。 + +#### 发布 + +简单的部署只需要将最终生成的静态文件,dist 文件夹的静态文件发布到你的 cdn 或者静态服务器即可,需要注意的是其中的 index.html 通常会是你后台服务的入口页面,在确定了 js 和 css 的静态之后可能需要改变页面的引入路径。 + +例如上传到 nginx `/srv/www/project/index.html` + +``` +# nginx配置 +location / { + # 不缓存html,防止程序更新后缓存继续生效 + if ($request_filename ~* .*\.(?:htm|html)$) { + add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate"; + access_log on; + } + # 这里是vue打包文件dist内的文件的存放路径 + root /srv/www/project/; + index index.html index.htm; +} + + +``` + +部署时可能会发现资源路径不对,只需要修改`.env.production`文件即可。 + +```shell +# 根据自己路径来配置更改 +# 注意需要以 / 开头和结尾 +VITE_PUBLIC_PATH=/ +VITE_PUBLIC_PATH=/xxx/ + +``` + +#### 前端路由与服务端的结合 + +项目前端路由使用的是 vue-router,所以你可以选择两种方式:history 和 hash。 + +- hash 默认会在 url 后面拼接# +- history 则不会,不过 history 需要服务器配合 + +可在 src/router/index.ts 内进行 mode 修改 + +```vue +import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'; + +createRouter({ + history: createWebHashHistory(), + // or + history: createWebHistory(), +}); + +``` + +#### history 路由模式下服务端配置 + +开启 history 模式需要服务器配置,更多的服务器配置详情可以看 [history-mode](https://router.vuejs.org/guide/essentials/history-mode.html#html5-mode) + +这里以 nginx 配置为例 + + +##### 部署到根目录 +``` +server { + listen 80; + location / { + # 用于配合 History 使用 + try_files $uri $uri/ /index.html; + } +} +``` + +#### 部署到非根目录 + +1. 首先需要在打包的时候更改配置 + +```shell +# 在.env.production内,配置子目录路径 +VITE_PUBLIC_PATH = /sub/ +``` + +``` +server { + listen 80; + server_name localhost; + location /sub/ { + # 这里是vue打包文件dist内的文件的存放路径 + alias /srv/www/project/; + index index.html index.htm; + try_files $uri $uri/ /sub/index.html; + } +} +``` + +#### 使用 nginx 处理跨域 + +使用 nginx 处理项目部署后的跨域问题 + +1. 配置前端项目接口地址 + +``` +# 在.env.production内,配置接口地址 +VITE_GLOB_API_URL=/api +``` + +2. 在 nginx 配置请求转发到后台 + +``` +server { + listen 8080; + server_name localhost; + # 接口代理,用于解决跨域问题 + location /api { + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + # 后台接口地址 + proxy_pass http://110.110.1.1:8080/api; + proxy_redirect default; + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Headers X-Requested-With; + add_header Access-Control-Allow-Methods GET,POST,OPTIONS; + } +} +``` \ No newline at end of file diff --git a/docs/guide-zh-CN/web-form.md b/docs/guide-zh-CN/web-form.md new file mode 100644 index 0000000..17a8a19 --- /dev/null +++ b/docs/guide-zh-CN/web-form.md @@ -0,0 +1,879 @@ +## 表单组件 + +目录 + +- 文本输入 Input +- 数字输入 Input Number +- 文本域 InputTextarea +- 富文本 InputEditor +- 动态键值对 InputDynamic +- 日期选择 Date(Y-M-D) +- 日期范围选择 DateRange +- 时间选择 Time(Y-M-D H:i:s) +- 时间范围选择 TimeRange +- 单选按钮 Radio +- 复选框 Checkbox +- 单选下拉框 Select +- 多选下拉框 SelectMultiple +- 树型选择 Tree Select +- 单图上传 UploadImage +- 多图上传 UploadImage +- 单文件上传 UploadFile +- 多文件上传 UploadFile +- 文件选择器 FileChooser +- 大文件上传 MultipartUpload +- 开关 Switch +- 评分 Rate +- 省市区选择器 CitySelector +- 图标选择器 IconSelector + +### 文本输入 Input + +```vue + + + +``` + +### 数字输入 Input Number + +```vue + + + +``` + +### 文本域 InputTextarea + +```vue + + + +``` + +### 富文本 InputEditor + +```vue + + + +``` + +### 动态键值对 InputDynamic + +```vue + + + +``` + +### 日期选择 Date(Y-M-D) + +```vue + + + +``` + +### 日期范围选择 DateRange + +```vue + + + +``` + +### 时间选择 Time(Y-M-D H:i:s) + +```vue + + + +``` + +### 时间范围选择 TimeRange + +```vue + + + +``` + +### 单选按钮 Radio + +```vue + + + +``` + +### 复选框 Checkbox +```vue + + + +``` + + +### 单选下拉框 Select +```vue + + + +``` + +### 多选下拉框 SelectMultiple +```vue + + + +``` + +### 树型选择 Tree Select +```vue + + + + + +``` + + +### 单图上传 UploadImage +```vue + + + +``` + +### 多图上传 UploadImage +```vue + + + +``` + +### 单文件上传 UploadFile +```vue + + + +``` + +### 多文件上传 UploadFile +```vue + + + +``` + +### 文件选择器 FileChooser +- 基础用法 +```vue + + + +``` + +- 指定fileType,支持多种选择器类型,默认情况是全部都可以选择 +```ts +type FileType = 'image' | 'doc' | 'audio' | 'video' | 'zip' | 'other' | 'default'; +``` + +- 图片选择器 +```vue + +``` + +- 多选支持,指定`maxNumber`多选数量 +```vue + +``` + +### 大文件上传 MultipartUpload +- 基础用法 +```vue + + + +``` + +### 开关 Switch +```vue + + + +``` + +### 评分 Rate +```vue + + + +``` + +### 省市区选择器 CitySelector +```vue + + + +``` + +### 图标选择器 IconSelector +```vue + + + +``` + + +更多组件请参考:https://www.naiveui.com/zh-CN/os-theme/components/button + +