服务端
Some checks failed
CI / init (push) Has been cancelled
CI / Frontend node 18.16.0 (push) Has been cancelled
CI / Backend go (1.22) (push) Has been cancelled
CI / devops-test (1.22, 18.16.0) (push) Has been cancelled
CI / release-pr (push) Has been cancelled
CI / release-please (push) Has been cancelled
CI / devops-prod (1.22, 18.x) (push) Has been cancelled
CI / docker (push) Has been cancelled

This commit is contained in:
2026-04-22 15:49:50 +08:00
parent 05ee541420
commit 8164eec650
18 changed files with 1115 additions and 251 deletions

View File

@@ -0,0 +1,158 @@
# 系统参数SysParams使用规范
## 适用范围
- 需要读取或写入 `sys_params` 表中的系统参数时,使用本文。
- 需要给某一个固定参数提供独立业务 API但底层仍保存到 `sys_params` 时,使用本文。
## 现有落点
- 表结构:`model/system/sys_params.go`
- Service`service/system/sys_params.go`
- API`api/v1/system/sys_params.go`
- Router`router/system/sys_params.go`
## 现有接口
- 实际完整路径 = `<router-prefix>/sysParams/...`
- 如果环境把 `router-prefix` 配成 `/api`,则完整路径就是 `/api/sysParams/...`
- 当前仓库 `config.yaml``router-prefix` 默认值为空字符串;不要把 `/api` 写死为项目事实。
- 这组路由在 `initialize/router.go` 中挂到 `PrivateGroup`,默认需要鉴权。
- `GET /sysParams/getSysParam?key=xxx`:按 `key` 读取单条参数。
- `GET /sysParams/getSysParamsList`:分页读取参数列表。
- `POST /sysParams/createSysParams`:创建参数。
- `PUT /sysParams/updateSysParams`:按 `ID` 更新参数。
- `DELETE /sysParams/deleteSysParams`:按 `ID` 删除参数。
- 结论:这组接口适合“通用参数管理页”,不适合业务围绕某一个固定参数直接做长期读写。
## 强制规则
- 后端代码禁止通过 HTTP 反调自己的 `/sysParams` 接口;统一在 `Service` 层直接调用 `service.ServiceGroupApp.SystemServiceGroup.SysParamsService`
- `API` 层禁止直接操作 `global.GVA_DB`
- `Value` 是字符串存储;简单值可直接存字符串,结构化配置必须由业务 `Service` 负责 `json.Marshal` / `json.Unmarshal`
- 不要把 `sys_params``ID` 暴露给前端,作为某个固定配置的长期主键。
- 固定配置统一按 `key` 识别;`key` 必须稳定、唯一、可读,建议使用 `<module>.<scene>`,例如 `device.runtimeConfig`
- 参数不存在时必须显式处理:返回业务错误、初始化默认值、或创建后再返回;禁止静默吞掉。
- 通用参数管理页面可以直接调用 `/sysParams`;具体业务页面优先调用该业务自己的独立 API。
- 参数属于某个业务模块时,落点放到该业务模块;只有平台级通用参数才继续放在 `system` 模块。
## 代码内直接读取
- 读取动作统一放在 `service/<module>`
- 调用入口:
```go
param, err := service.ServiceGroupApp.SystemServiceGroup.SysParamsService.GetSysParam("device.runtimeConfig")
```
- `param.Value` 就是最终存储值。
- 如果 `Value` 存的是 JSON必须先反序列化为业务结构体再继续使用。
```go
package device
import (
"encoding/json"
"github.com/flipped-aurora/gin-vue-admin/server/service"
)
type RuntimeConfig struct {
Enable bool `json:"enable"`
Mode string `json:"mode"`
}
func (s *DeviceService) GetRuntimeConfig() (RuntimeConfig, error) {
param, err := service.ServiceGroupApp.SystemServiceGroup.SysParamsService.GetSysParam("device.runtimeConfig")
if err != nil {
return RuntimeConfig{}, err
}
var cfg RuntimeConfig
if err = json.Unmarshal([]byte(param.Value), &cfg); err != nil {
return RuntimeConfig{}, err
}
return cfg, nil
}
```
## 代码内直接写入
- 当前 `SysParamsService` 没有“按 `key` 直接写入”的现成能力。
- 现在只有 `CreateSysParams``UpdateSysParams``GetSysParam`
- 这意味着:业务写入固定参数时,必须先按 `key` 查询,再决定 `create` 还是 `update`
- 这段逻辑必须收口到业务 `Service`,不要放在 `API`,也不要交给前端拼 `ID`
```go
package device
import (
"encoding/json"
"errors"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/service"
"gorm.io/gorm"
)
const runtimeConfigKey = "device.runtimeConfig"
func (s *DeviceService) SaveRuntimeConfig(cfg RuntimeConfig) error {
valueBytes, err := json.Marshal(cfg)
if err != nil {
return err
}
sysParamsSvc := service.ServiceGroupApp.SystemServiceGroup.SysParamsService
param, err := sysParamsSvc.GetSysParam(runtimeConfigKey)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return sysParamsSvc.CreateSysParams(&system.SysParams{
Name: "设备运行配置",
Key: runtimeConfigKey,
Value: string(valueBytes),
Desc: "device 模块运行配置",
})
}
return err
}
param.Value = string(valueBytes)
param.Name = "设备运行配置"
param.Desc = "device 模块运行配置"
return sysParamsSvc.UpdateSysParams(param)
}
```
## 什么时候必须写独立 API
- 前端只关心某一个固定参数,不应该先查列表、拿 `ID`、再更新时。
- 参数值是 JSON需要强类型请求体和响应体时。
- 这个参数有明确业务语义,例如“设备运行配置”“首页配置”“第三方回调配置”时。
- 该参数需要单独做权限控制、默认值处理、字段校验、审计说明时。
## 独立 API 规范
- 独立 API 仍按 `Router → API → Service → GORM → Database` 分层实现。
- 底层存储仍使用 `sys_params`;不要因为一个配置项就单独建表,除非它已经演变成独立业务实体。
- 对外接口暴露业务语义,不暴露 `sys_params``ID`、通用 CRUD 细节。
- `Service` 负责固定 `key`、处理 JSON 转换、决定 create/update、处理不存在时的策略。
- 参数属于业务模块时,文件落点统一为 `model/<module>/request``model/<module>/response``service/<module>``api/v1/<module>``router/<module>`
- 新增业务路由统一注册到 `initialize/router_biz.go`
## 推荐落地方式
- 对外提供两个独立接口即可:一个“获取配置”,一个“保存配置”。
- 接口名使用业务语义,不使用 `sysParams` 命名。
- 示例接口:`GET <router-prefix>/deviceConfig/getRuntimeConfig``PUT <router-prefix>/deviceConfig/updateRuntimeConfig`
- `GET` 接口返回业务结构体。
- `PUT` 接口接收业务结构体。
- 底层统一在 `service/device` 中转成 `sys_params.value` 的 JSON 字符串。
## 禁止事项
- 禁止在业务代码里直接写 SQL 操作 `sys_params`,绕开现有 `Service` 分层。
- 禁止在业务 `API` 中直接处理 `sys_params``ID`
- 禁止让前端把一个固定配置当成“参数管理列表中的某一行”去长期维护。
- 禁止把结构化 JSON 配置直接裸透传为字符串给业务页面,除非该页面本身就是通用参数管理页。