Files
wdh-home 8164eec650
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
服务端
2026-04-22 15:49:50 +08:00

159 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 系统参数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 配置直接裸透传为字符串给业务页面,除非该页面本身就是通用参数管理页。