diff --git a/server/.ai-specs/coding-specs/api-auth-control.md b/server/.ai-specs/coding-specs/api-auth-control.md new file mode 100644 index 0000000..7789276 --- /dev/null +++ b/server/.ai-specs/coding-specs/api-auth-control.md @@ -0,0 +1,96 @@ +# API 鉴权与权限修改规范 + +## 适用范围 + +- 新增接口时,需要判定它是“公开访问”“仅登录可访问”还是“登录且有权限才可访问”时,使用本文。 +- 已有接口在“不需要登录 / 需要登录 / 需要权限”之间切换时,使用本文。 + +## 现有鉴权基线 + +- `initialize/router.go` 当前只创建两类顶层路由组: + - `PublicGroup`:不挂鉴权中间件。 + - `PrivateGroup`:统一挂 `middleware.JWTAuth()` 和 `middleware.CasbinHandler()`。 +- `JWTAuth` 定义在 `middleware/jwt.go`。 +- `CasbinHandler` 定义在 `middleware/casbin_rbac.go`。 +- 结论: + - 挂到 `PublicGroup` = 不需要登录。 + - 挂到 `PrivateGroup` = 需要登录,且需要角色权限。 + - 当前仓库没有默认内置的“只登录、不校验权限”公共组。 + - 不存在“只校验权限、不登录”的合法模式;`CasbinHandler` 依赖 `JWTAuth` 写入的用户 claims。 + +## 权限模式判定 + +| 模式 | 路由挂载方式 | JWT | Casbin | 后续要求 | +|:---|:---|:---|:---|:---| +| 不需要登录 | `PublicGroup` / `PublicRouter` | 否 | 否 | 默认不作为角色权限点长期维护 | +| 需要登录,不需要权限 | 基于 `PublicRouter` 单独加 `middleware.JWTAuth()` | 是 | 否 | 默认不进入角色权限维护 | +| 需要登录且需要权限 | `PrivateGroup` / `Router` | 是 | 是 | 必须进入 `sys_apis` 并分配角色 | + +## 强制规则 + +- 同一个 `path + method` 只能落一种鉴权模式;禁止同时注册到公开组和鉴权组。 +- `middleware.OperationRecord()` 只负责操作审计,不改变登录与权限判定。 +- 鉴权逻辑统一落在 `router` 层;禁止在 `API` 或 `Service` 中靠 `if token != ""`、`if authorityId == ...` 兜底模拟权限。 +- 修改接口鉴权时,Swagger 注解必须同步: + - 不需要登录:移除 `@Security ApiKeyAuth` + - 需要登录:保留 `@Security ApiKeyAuth` +- 业务路由统一在 `router/` 调整;业务模块接入点统一在 `initialize/router_biz.go` 注册。 + +## 改成不需要登录 + +- 把路由挂载从 `PrivateGroup` 或“仅登录组”移到 `PublicRouter`。 +- 如果只是业务模块内部路由调整,不要改 `API`、`Service` 方法签名。 +- 该接口如果不再作为后台角色权限点维护,必须把对应 `path + method` 从权限维护中移走,二选一即可: + - 删除对应 `sys_apis` 记录。 + - 加入 `sys_ignore_apis`,避免后续 `SyncApi` 再次纳入权限页。 +- 若历史上已经给该接口分配过角色,必须同步清理对应 Casbin 规则,避免后台权限配置出现“看起来要授权、实际上公开可调”的假象。 + +## 改成需要登录,不需要权限 + +- 当前项目没有全局现成的 `LoginGroup`;少量接口优先在对应 router 中基于 `PublicRouter` 单独挂 `JWTAuth`,不要为了一两个接口改全局入口。 +- 推荐写法: + +```go +func (r *BookRouter) InitBookRouter(Router *gin.RouterGroup, PublicRouter *gin.RouterGroup) { + bookLoginRouter := PublicRouter.Group("book").Use(middleware.JWTAuth()) + { + bookLoginRouter.GET("profile", bookApi.GetProfile) + } +} +``` + +- 同一模块里如果“仅登录接口”很多,再考虑在 `initialize/router.go` 新增单独的 `LoginGroup`,并把它显式传入对应 router 初始化函数。 +- 这类接口禁止挂 `middleware.CasbinHandler()`。 +- 这类接口默认不进入 `sys_apis` 的角色权限维护;如果历史上已经进入,必须删除或加入 `sys_ignore_apis`,不要继续让后台把它当作“需要分配角色”的接口。 + +## 改成需要登录且需要权限 + +- 把路由挂到 `PrivateGroup` 或业务 router 的 `Router` 参数上。 +- 只把路由挪进 `PrivateGroup` 还不够;`CasbinHandler` 按 `path + method` 校验权限,没有角色策略就会直接返回“权限不足”。 +- 因此必须同时完成以下动作: + - 确保该 `path + method` 存在于 `sys_apis`。 + - 确保该接口已分配允许访问的角色。 + - 刷新 Casbin 策略缓存。 +- 系统现成接口的完整路径 = `/api/...`;如果 `router-prefix` 为空,则完整路径就是 `/api/...`。不要把是否存在额外前缀写死成项目事实。 +- 常用管理接口: + - `GET /api/syncApi`:对比内存路由和 `sys_apis` + - `POST /api/enterSyncApi`:确认把新增路由写入 `sys_apis` + - `POST /api/setApiRoles`:全量覆盖某个接口的角色列表 + - `GET /api/freshCasbin`:刷新 Casbin 策略缓存 +- 若接口此前在 `sys_ignore_apis` 中,改成“需要权限”前必须先取消 ignore,再同步到 `sys_apis`。 +- 若不给任何角色分配该接口,则结果不是“仅登录可访问”,而是“所有已登录用户都权限不足”。 + +## 验证要求 + +- 修改完成后,至少验证下面三类请求结果: + - 公开接口:不带 token 可访问成功。 + - 仅登录接口:不带 token 返回未登录;带有效 token 可访问成功。 + - 权限接口:不带 token 返回未登录;带无权角色 token 返回“权限不足”;带有权角色 token 可访问成功。 +- 如果改了接口鉴权方式,同时该接口出现在 Swagger、前端权限配置页、角色配置页,也必须同步核对展示结果是否一致。 + +## 禁止事项 + +- 禁止只改 `sys_apis` 或 `casbin_rule`,却不改真实 router 挂载。 +- 禁止只把路由从 `PublicGroup` 挪到 `PrivateGroup`,却不补 `sys_apis` 和角色策略。 +- 禁止把“仅登录接口”偷懒挂到 `PrivateGroup`,再靠给所有角色放权来模拟“无权限限制”。 +- 禁止让公开接口长期保留在角色权限维护页里,造成权限含义失真。 diff --git a/server/.ai-specs/coding-specs/module-admin-app-split.md b/server/.ai-specs/coding-specs/module-admin-app-split.md new file mode 100644 index 0000000..7cecaac --- /dev/null +++ b/server/.ai-specs/coding-specs/module-admin-app-split.md @@ -0,0 +1,42 @@ +# 同模块 admin/app 接口分层规范 + +## 适用范围 + +- 同一业务模块同时存在 `admin` 管理端接口和 `app` 用户端接口时,使用本文。 + +## 强制规则 + +- 顶层目录一律按业务模块落点;禁止因为有 `app` 端,就单独新建顶层 `router/app`、`api/v1/app`、`service/app`、`model/app` 来承载 `book`、`order`、`author` 这类业务。 +- 同一业务模块的实体仍统一放在 `model/`;只有接口入参、出参和流程按 `admin` / `app` 分开。 +- `router/`、`api/v1/`、`service/` 内,`admin` 与 `app` 接口必须分文件或分承载结构体;禁止长期混写在同一个大文件里。 +- 可复用逻辑优先下沉到 `service/` 的公共方法;公共逻辑只抽业务共性,不要把 `admin` / `app` 的鉴权、返回、分页口径硬揉成一套。 + +## 推荐落点 + +- 推荐文件名:`_admin.go`、`_app.go`、`enter.go`。 +- 示例: + - `router/book/book_admin.go` + - `router/book/book_app.go` + - `api/v1/book/book_admin.go` + - `api/v1/book/book_app.go` + - `service/book/book_admin.go` + - `service/book/book_app.go` + - `service/book/book_common.go` + - `model/book/book.go` + - `model/book/request/book_admin.go` + - `model/book/request/book_app.go` + - `model/book/response/book_admin.go` + - `model/book/response/book_app.go` + +## 路由与鉴权 + +- `admin` 接口默认挂业务 router 的 `PrivateGroup`,走后台 `JWT + Casbin`。 +- `app` 公开接口挂 `PublicGroup`。 +- `app` 仅登录接口,基于 `PublicGroup` 单独加 `middleware.JWTAuth()`;不要直接挂 `PrivateGroup`。 +- 如果 `app` 用户体系不等于 `sys_users`,禁止直接复用后台登录、claims、角色权限链路;必须单独实现用户端认证链路。 + +## 禁止事项 + +- 禁止把“同一业务模块的 app 端接口”误建成独立业务模块。 +- 禁止为了省事,把所有 `app` 接口都挂到 `PrivateGroup`,再用后台角色权限去模拟用户端登录态。 +- 禁止同一个业务实体在 `model` 层拆出两套重复表结构,仅因接口面向 `admin` / `app` 不同。 diff --git a/server/.ai-specs/coding-specs/module-admin-crud-default.md b/server/.ai-specs/coding-specs/module-admin-crud-default.md new file mode 100644 index 0000000..d8ccd70 --- /dev/null +++ b/server/.ai-specs/coding-specs/module-admin-crud-default.md @@ -0,0 +1,54 @@ +# 业务 admin 端默认 CRUD 接口规范 + +## 适用范围 + +- 新增业务模块的 `admin` 端管理接口时,使用本文。 +- 判断某个业务模块默认应该提供哪些后台 CRUD 接口时,使用本文。 +- 业务模块没有明确说明“只读”“无详情”“禁止批量删除”等例外时,默认按本文落地。 + +## 默认接口基线 + +- 默认 CRUD = `创建`、`单删`、`批量删除`、`更新`、`详情`、`分页列表` 6 个接口。 +- 完整路径 = `//...`;如果 `router-prefix` 为空,则完整路径不带额外前缀。禁止把 `/api` 写死为项目事实。 +- `admin` 接口默认挂 `PrivateGroup`,统一走后台 `JWT + Casbin`。 +- 写操作默认挂 `middleware.OperationRecord()`;读操作默认不挂操作审计。 + +| 动作 | Method | 默认路径 | API 方法名 | Service 方法名 | +|:---|:---|:---|:---|:---| +| 创建 | `POST` | `//create` | `Create` | `Create` | +| 单删 | `DELETE` | `//delete` | `Delete` | `Delete` | +| 批量删除 | `DELETE` | `//deleteByIds` | `DeleteByIds` | `DeleteByIds` | +| 更新 | `PUT` | `//update` | `Update` | `Update` | +| 详情 | `GET` | `//find` | `Find` | `Get` | +| 分页列表 | `GET` | `//getList` | `GetList` | `GetInfoList` | + +## 参数约定 + +- `Create`、`Update` 默认使用 `body` 传实体或业务 `request`。 +- `Delete` 默认用主键 query 参数删除;主键名必须和实体主键字段保持一致。 +- `DeleteByIds` 默认用主键数组 query 参数删除,格式统一为 `s[]`。 +- `Find` 默认用主键 query 参数查询详情。 +- `GetList` 默认接收 `model//request` 下的搜索结构;普通列表返回分页结果,树形列表可不接分页参数,但接口名仍保持 `getList`。 + +## 强制规则 + +- 新增业务 `admin` 模块时,如无明确例外,先提供这 6 个接口,再叠加业务特有接口。 +- 路由统一放在 `router/`,接口统一放在 `api/v1/`,业务统一放在 `service/`,模型统一放在 `model/`。 +- 新增业务路由后,必须同步在 `initialize/router_biz.go` 注册。 +- 同一模块如果同时有 `admin/app` 两套接口,目录仍按业务模块落点,`admin` 与 `app` 必须分文件或分承载结构体,不能长期混写。 +- Swagger 注解里的 `@Router`、`@Security ApiKeyAuth`、`Method` 必须和真实 router 挂载一致。 + +## 允许例外 + +- 业务天然只读时,可以不做 `Create`、`Update`、`Delete`、`DeleteByIds`,但必须在对应业务文档或接口文档中明确说明。 +- 业务明确禁止批量删除时,可以去掉 `DeleteByIds`,但 `router`、`api`、`service`、前端调用、权限点必须同步移除,不能只删单层。 +- 树形实体的列表接口可以返回整棵树,不强制分页;但接口名仍保持 `getList`,不要随意改成 `tree`、`all`、`queryList`。 +- 配置型场景如果本质不是独立业务实体,优先复用已有能力,例如 `sys_params`;不要为了凑 CRUD 强行建完整业务模块。 + +## 禁止事项 + +- 禁止把默认 `admin` CRUD 直接挂到 `PublicGroup`。 +- 禁止同一批后台 CRUD 同时出现 `createXxx`、`addXxx`、`saveXxx` 多套命名。 +- 禁止把列表接口随意命名成 `page`、`list`、`query`,导致不同模块接口风格漂移。 +- 禁止只新增 `API` 或只新增 `Router`,不补齐 `Service`、`Model`、注册入口。 +- 禁止因为有 `app` 端,就把 `admin` 端默认 CRUD 改落到 `router/app`、`api/v1/app` 这类顶层目录。 diff --git a/server/.ai-specs/coding-specs/sys-params.md b/server/.ai-specs/coding-specs/sys-params.md new file mode 100644 index 0000000..94be83a --- /dev/null +++ b/server/.ai-specs/coding-specs/sys-params.md @@ -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` + +## 现有接口 + +- 实际完整路径 = `/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` 必须稳定、唯一、可读,建议使用 `.`,例如 `device.runtimeConfig`。 +- 参数不存在时必须显式处理:返回业务错误、初始化默认值、或创建后再返回;禁止静默吞掉。 +- 通用参数管理页面可以直接调用 `/sysParams`;具体业务页面优先调用该业务自己的独立 API。 +- 参数属于某个业务模块时,落点放到该业务模块;只有平台级通用参数才继续放在 `system` 模块。 + +## 代码内直接读取 + +- 读取动作统一放在 `service/`。 +- 调用入口: + +```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//request`、`model//response`、`service/`、`api/v1/`、`router/`。 +- 新增业务路由统一注册到 `initialize/router_biz.go`。 + +## 推荐落地方式 + +- 对外提供两个独立接口即可:一个“获取配置”,一个“保存配置”。 +- 接口名使用业务语义,不使用 `sysParams` 命名。 +- 示例接口:`GET /deviceConfig/getRuntimeConfig`、`PUT /deviceConfig/updateRuntimeConfig`。 +- `GET` 接口返回业务结构体。 +- `PUT` 接口接收业务结构体。 +- 底层统一在 `service/device` 中转成 `sys_params.value` 的 JSON 字符串。 + +## 禁止事项 + +- 禁止在业务代码里直接写 SQL 操作 `sys_params`,绕开现有 `Service` 分层。 +- 禁止在业务 `API` 中直接处理 `sys_params` 的 `ID`。 +- 禁止让前端把一个固定配置当成“参数管理列表中的某一行”去长期维护。 +- 禁止把结构化 JSON 配置直接裸透传为字符串给业务页面,除非该页面本身就是通用参数管理页。 diff --git a/server/.ai-specs/coding-specs/vo-model-request-response.md b/server/.ai-specs/coding-specs/vo-model-request-response.md new file mode 100644 index 0000000..112a243 --- /dev/null +++ b/server/.ai-specs/coding-specs/vo-model-request-response.md @@ -0,0 +1,54 @@ +# Model / Request / Response 组织规范 + +## 适用范围 + +- 判断项目中的 `vo`、实体、接口入参、接口出参应该落在哪一层时,使用本文。 +- 新增或修改业务模块的 `model/`、`model//request`、`model//response` 时,使用本文。 +- 判断某个 API 是直接复用实体,还是新增 `request/response` 结构时,使用本文。 + +## 结论基线 + +- 本项目不单独维护顶层 `vo` 目录。 +- 项目里的数据载体统一归到 `model` 体系,不额外拆一套平行 `vo` 层。 +- 标准落点如下: + +| 场景 | 落点 | 说明 | +|:---|:---|:---| +| 数据库实体 / 业务实体 | `model/` | 承载表结构映射、业务实体字段 | +| API 入参 | `model//request` | 承载查询条件、分页条件、保存参数等 | +| API 出参 | `model//response` | 承载详情包装、列表项包装、聚合展示结构等 | +| 跨模块通用入参 | `model/common/request` | 例如 `PageInfo`、`GetById`、`IdsReq` | +| 跨模块通用出参 | `model/common/response` | 例如统一响应壳、分页结果 | + +## 判定规则 + +- 请求参数如果就是业务实体本身,且不会引入多余字段、敏感字段或语义歧义,可以直接复用 `model/` 实体。 +- 请求参数如果只是“分页 + 条件筛选 + 排序”这类接口 contract,应定义到 `model//request`。 +- 返回结果如果只是直接返回实体本身,可以直接返回实体或列表,不强制为了“像 VO”再包一层空结构。 +- 返回结果如果需要额外包装、聚合字段、嵌套结构、展示字段转换,应定义到 `model//response`。 +- 多个 API 只要 contract 一致,可以共用同一个 `request` 或 `response` 结构;不是每个 API 都必须单独建一份。 + +## 强制规则 + +- 新增业务模块时,实体统一放 `model/`;禁止把实体落到 `api`、`service`、`router`。 +- `API` 层入参、出参需要独立结构时,统一放 `model//request`、`model//response`;禁止在 `API` 文件里长期维护临时匿名结构体当正式 contract。 +- 跨模块都能稳定复用的分页、主键、统一响应、分页结果等结构,统一复用 `model/common/request`、`model/common/response`;不要每个模块各复制一份。 +- 同一业务模块如果同时存在 `admin/app` 两套接口,实体仍统一放 `model/`;只有 `request/response` 按 `admin` / `app` 分文件区分。 +- 新增 `request/response` 前,先检查同模块现有结构是否可复用;只有接口字段、校验语义、返回语义明显不同,才新增结构。 +- 改实体字段时,必须同步检查 `service` 查询/写入、`API` 绑定/返回、`doc-sql` 是否仍一致。 +- 改 `request/response` 时,必须同步检查 `API` 绑定、`Service` 方法签名、`doc-api` 是否仍一致。 + +## 推荐做法 + +- `Create`、`Update` 这类保存接口,如果直接面向实体字段,可优先复用实体。 +- `GetList`、搜索、筛选、排序接口,优先使用 `request` 结构,不要把分页筛选字段硬塞进实体。 +- `Find`、详情、聚合展示、树结构、联表结果等返回,优先使用 `response` 结构,不要把纯展示字段反向塞进数据库实体。 +- `response` 结构命名优先体现业务语义,例如 `BookDetailResponse`、`BookListItem`、`AuthorOption`;不要机械统一叫 `XxxVO`。 + +## 禁止事项 + +- 禁止单独新建顶层 `vo` 目录,与 `model` 并行维护两套数据结构体系。 +- 禁止为了“每个 API 都有专属 VO”而机械性给每个接口复制一份几乎相同的 `request/response`。 +- 禁止把分页、排序、筛选字段直接加进数据库实体,只为了省掉 `request` 结构。 +- 禁止把纯展示字段、聚合字段、临时返回字段长期塞进实体,只为了省掉 `response` 结构。 +- 禁止 `API`、`Service` 长期返回 `map[string]interface{}`、`gin.H` 充当正式业务出参,导致 contract 漂移。 diff --git a/server/.ai-specs/doc-dict/book_author_status.md b/server/.ai-specs/doc-dict/book_author_status.md new file mode 100644 index 0000000..02b72ea --- /dev/null +++ b/server/.ai-specs/doc-dict/book_author_status.md @@ -0,0 +1,9 @@ +# 作者状态 + +- 模块:book +- 字典编码:`book_author_status` + +| Label | Value | Sort | Status | Desc | +|:---|:---|:---|:---|:---| +| 启用 | `enabled` | 10 | true | 作者可正常展示,并可被书籍继续关联 | +| 禁用 | `disabled` | 20 | true | 作者不再对外展示,且不应再被新增书籍关联 | diff --git a/server/.ai-specs/doc-dict/book_completion_status.md b/server/.ai-specs/doc-dict/book_completion_status.md new file mode 100644 index 0000000..e112720 --- /dev/null +++ b/server/.ai-specs/doc-dict/book_completion_status.md @@ -0,0 +1,9 @@ +# 书籍完结状态 + +- 模块:book +- 字典编码:`book_completion_status` + +| Label | Value | Sort | Status | Desc | +|:---|:---|:---|:---|:---| +| 完结 | `completed` | 10 | true | 书籍内容已全部发布完成,不再持续新增章节 | +| 连载 | `serializing` | 20 | true | 书籍内容仍在持续发布,后续还会新增章节 | diff --git a/server/.ai-specs/doc-sql/book_author.md b/server/.ai-specs/doc-sql/book_author.md new file mode 100644 index 0000000..738efb4 --- /dev/null +++ b/server/.ai-specs/doc-sql/book_author.md @@ -0,0 +1,39 @@ +# 书籍作者表 + +## 基本信息 + +- 模块:book +- 表名:`book_author` +- 模型:`model/book/book_author.go` +- 迁移接入:`initialize/gorm_biz.go` +- 状态字典:`book_author_status` +- 职责:承载书籍作者主体信息,用于作者资料展示、书籍作者关联和后台作者管理。 + +## 建议 SQL + +> 以下 SQL 以当前项目 PostgreSQL 为准,主要表达字段、约束和索引语义;实际落库以 `GORM Model` 和 `initialize/gorm_biz.go` 为准。 + +```sql +CREATE TABLE book_author ( + id bigserial PRIMARY KEY, + created_at timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + name varchar(128) NOT NULL, + author_status varchar(32) NOT NULL DEFAULT 'enabled', + intro text, + cover_url varchar(500) +); + +COMMENT ON TABLE book_author IS '书籍作者表'; +COMMENT ON COLUMN book_author.id IS '主键'; +COMMENT ON COLUMN book_author.created_at IS '创建时间'; +COMMENT ON COLUMN book_author.updated_at IS '更新时间'; +COMMENT ON COLUMN book_author.name IS '作者名称'; +COMMENT ON COLUMN book_author.author_status IS '作者状态字典值,对应 book_author_status'; +COMMENT ON COLUMN book_author.intro IS '作者简介'; +COMMENT ON COLUMN book_author.cover_url IS '作者封面图片 URL'; + +CREATE UNIQUE INDEX uk_book_author_name ON book_author (name); +CREATE INDEX idx_book_author_author_status ON book_author (author_status); +CREATE INDEX idx_book_author_created_at ON book_author (created_at); +``` diff --git a/server/.ai-specs/logic-specs/book.md b/server/.ai-specs/logic-specs/book.md new file mode 100644 index 0000000..d680a3f --- /dev/null +++ b/server/.ai-specs/logic-specs/book.md @@ -0,0 +1,47 @@ +书名: +子标题 +书籍类型:字典 +书籍标签 : 字典 ,标签是多个 要考虑用标签搜索书籍的性能问题 +书有系列:第一部 第二部, 上部 下部 , 所以有排序功能 +章节名: 章节是否能观看 +文件路径:oss url +封面图片: url +作者名(非id): 多个名字用,隔开 +出版社: +出版时间: +简介: +热度: +评分: 0-10分 +点评数: +字数: +书籍完结状态: 字典, 完结/连载 +- 书籍系列Id + +# 书籍系列表 +- 名字 +- 封面图片 + +# 书籍作者表 +- 名字 +- 状态:字典 +- 简介 +- 封面图片: url + +# 用户观看书籍历史表 +- 会员用户Id +- 观看书籍进度 : 前端显示 50% +- 观看章节Id +- 观看章节行数 + + +# 用户收藏书籍历 +- 会员用户Id +- 书籍Id + +# 书籍评论表 +- 评论书籍id +- 评论章节id +- 评论章节行数 : 比如第二章第5行 +- 评论内容 +- 评论点赞数 +- 作者是否点赞 diff --git a/server/.ai-specs/sys-specs/business-dictionary-spec.md b/server/.ai-specs/sys-specs/business-dictionary-spec.md new file mode 100644 index 0000000..72945e4 --- /dev/null +++ b/server/.ai-specs/sys-specs/business-dictionary-spec.md @@ -0,0 +1,53 @@ +# 业务字典规范 + +## 适用范围 + +- 当任务是新增业务状态、类型、级别、来源、模式、分类等值域时,先读本文件。 +- 本文件只规定新增业务字典怎么写,不重复说明系统字典实现细节。 + +## 强制规则 + +- 业务枚举就是业务字典。禁止在代码中脱离字典单独发明枚举值域。 +- 新增业务值域时,先写字典文档,再写表结构、接口、校验和前端展示。 +- 具体业务字典必须写到 `.ai-specs/doc-dict/` 下。 +- 一个 `.md` 文件只能写一个字典。 +- 字典文件名必须等于字典编码,推荐路径:`.ai-specs/doc-dict/.md`。 +- 字典编码使用 `snake_case`,固定格式 `_`,例如 `device_status`。 +- `` 就是 `_`。 +- 字典项 `Value` 使用稳定 machine value;代码常量值必须与字典项 `Value` 完全一致。 +- 代码判断统一使用字典项 `Value`;禁止使用 `Label` 做逻辑分支。 +- 已上线字典的编码和字典项 `Value` 默认不可变;下线优先禁用,不直接删除。 +- 禁止出现数据库存 `1/2/3`,但没有对应字典文档说明语义。 +- 禁止代码新增枚举值,但未同步字典文档和字典数据。 + +## MD 模板 + +.ai-specs/doc-dict/.md +```md +# <字典中文名> + +- 模块: +- 字典编码:`_` + +| Label | Value | Sort | Status | Desc | +|:---|:---|:---|:---|:---| +| <中文名> | `` | 10 | true | <说明> | +``` + +## 代码模板 + +```go +package + +type string + +const ( + = "" + = "" +) +``` + +## 与 SQL 的关系 + +- 值域字段先有字典定义,再进入表设计。 +- 值域字段如何落库,按 `.ai-specs/sys-specs/business-table-spec.md` 执行。 diff --git a/server/.ai-specs/sys-specs/business-table-spec.md b/server/.ai-specs/sys-specs/business-table-spec.md new file mode 100644 index 0000000..3acf441 --- /dev/null +++ b/server/.ai-specs/sys-specs/business-table-spec.md @@ -0,0 +1,77 @@ +# 业务表 SQL 规范 + +## 适用范围 + +- 当任务是新增/修改 `.ai-specs/doc-sql/*.md` 时,先读本文件。 +- 当前项目关系型数据库默认是 `PostgreSQL`,`doc-sql` 的 `建议 SQL` 默认输出 `PostgreSQL` 写法。 +- 涉及状态、类型、级别、来源、模式、分类等值域字段时,必须先读 `.ai-specs/sys-specs/business-dictionary-spec.md`,并先补 `.ai-specs/doc-dict/.md`。 + +## 强制输出 + +- 文档路径固定:`.ai-specs/doc-sql/.md` +- 文档结构固定只保留:`# 标题`、`## 基本信息`、`## 建议 SQL` +- `## 基本信息` 固定顺序:`模块` → `表名` → `模型` → `迁移接入` → 按需补 `字典` 行 → `职责` +- 有字典字段时,每个字典单独占一行:`- <字段中文名>字典:\`\`` +- `## 建议 SQL` 前的说明固定写为:`> 以下 SQL 以当前项目 PostgreSQL 为准,主要表达字段、约束和索引语义;实际落库以 \`GORM Model\` 和 \`initialize/gorm_biz.go\` 为准。` +- `SQL` 代码块固定按这个顺序组织:`CREATE TABLE` → `COMMENT ON TABLE` → `COMMENT ON COLUMN` → `CREATE UNIQUE INDEX` / `CREATE INDEX` +- 表名、字段名、索引名统一使用 `snake_case` +- 唯一索引命名固定:`uk__` +- 普通索引命名固定:`idx__` +- 字典字段只存字典项 `Value`,禁止存 `Label` +- 文档只写已确认字段、约束、索引;禁止再补 `字段设计`、`索引设计`、`关联关系`、`删除与兼容`、`自检` 等重复章节 + +## PostgreSQL 写法 + +- 主键自增优先写 `bigserial PRIMARY KEY` +- 时间字段优先写 `timestamp with time zone` +- 注释统一写 `COMMENT ON TABLE`、`COMMENT ON COLUMN` +- 索引统一单独写 `CREATE UNIQUE INDEX`、`CREATE INDEX` +- 禁止混入 `AUTO_INCREMENT`、反引号、`ENGINE=InnoDB`、行内 `COMMENT`、`UNIQUE KEY`、`KEY ...` 等 `MySQL` 方言 + +## 输出模板 + +````md +# <表中文名> + +## 基本信息 + +- 模块: +- 表名:`` +- 模型:`model//.go` +- 迁移接入:`initialize/gorm_biz.go` +- <字段中文名>字典:`` +- 职责:<一句话职责> + +## 建议 SQL + +> 以下 SQL 以当前项目 PostgreSQL 为准,主要表达字段、约束和索引语义;实际落库以 `GORM Model` 和 `initialize/gorm_biz.go` 为准。 + +```sql +CREATE TABLE ( + id bigserial PRIMARY KEY, + created_at timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + , + +); + +COMMENT ON TABLE IS '<表中文名>'; +COMMENT ON COLUMN .id IS '主键'; +COMMENT ON COLUMN .created_at IS '创建时间'; +COMMENT ON COLUMN .updated_at IS '更新时间'; +COMMENT ON COLUMN . IS '<字段说明>'; +COMMENT ON COLUMN . IS '<字段说明>'; + +CREATE UNIQUE INDEX uk__ ON (); +CREATE INDEX idx__ ON (); +``` +```` + +- 没有字典字段,就删除对应“字典”行 +- 没有唯一索引或普通索引,就删除对应 SQL 行 +- 如果字段默认值来自字典,直接写字典项 `Value`,例如 `DEFAULT 'enabled'` + +## 复刻目标 + +- 只看本文件,应能直接写出类似 `.ai-specs/doc-sql/book_author.md` 的文档 +- 先定字典,再写 `doc-sql`,再进入 `Model`、迁移和业务代码 diff --git a/server/.ai-specs/sys-specs/module-naming-spec.md b/server/.ai-specs/sys-specs/module-naming-spec.md new file mode 100644 index 0000000..04b7925 --- /dev/null +++ b/server/.ai-specs/sys-specs/module-naming-spec.md @@ -0,0 +1,20 @@ +# 模块命名规范 + +## 适用范围 + +- 当任务涉及新增/修改业务模块命名、中英文对照、目录命名、表前缀、字典编码时,先读本文件。 + +## 强制规则 + +- 模块中文名与英文名的映射统一登记在本文件;禁止同一业务出现多个英文命名。 +- 同一业务模块在 `router`、`api`、`service`、`model`、表名、字典编码、接口路径中必须使用同一英文词根;禁止混用近义词或复数变体。 +- 模块英文名统一使用小写登记值;需要拼接时,按目标位置使用既有规范展开,不单独发明新缩写。 +- 新增模块词汇时,必须先更新本文件,再进入代码实现。 +- 已登记模块命名默认视为稳定约束;如需改名,必须先更新本文件并评估受影响目录、表、接口、字典和文档。 + +## 模块词汇 + +| 中文 | 英文 | 说明 | +|:---|:---|:---| +| 书籍 | book | 书籍与章节相关业务模块 | +| 设备 | device | 设备管理模块 | diff --git a/server/AGENTS.md b/server/AGENTS.md new file mode 100644 index 0000000..be59561 --- /dev/null +++ b/server/AGENTS.md @@ -0,0 +1,141 @@ +# AI 开发入口 [!IMPORTANT] + +- **本文档要求**:本文档为项目级别规范和重要导航,必须严格参考 +- **本文档要求**:本文档只允许在已有的结构上CURD,不允许增加其他标题区 +- **本文档要求**:.ai-specs 目录下新增/删除任何文档的时候都应该 在本文档中修改 `## 项目文档` +- **文档要求**:规范型文档是给 AI 的顶层入口文档,不是“解释得更全”就更好,而是要 更短、更硬、更可判定 +- **代码优化**:先复用再新增,允许抽公共逻辑,但公共逻辑必须保证边界仍清晰。 +- **代码优化**:优化代码时必须同时考虑冗余、孤岛代码、代码清晰度、复杂度、边界条件和兼容性,不能只追求功能跑通。不要修改ui/ux 视觉效果,除非明确要求。 +- **代码默认遵循**:业务流程需要遵循 `主流做法` `工业级正规` +- **代码默认遵循**:强制工作流:按`## 项目文档`定位需要的文档 → 读取匹配文档 → 进入源码 + +## 工具使用规则 + +- **搜索范围限制**:Grep/Glob 严禁全盘搜索,绝对禁止扫描 `.gitignore` 忽略的目录,以避免性能卡顿。 +- **读写**:所有文件读取/写入统一使用 UTF-8(建议无 BOM) +- **读写**:PowerShell/脚本读取项目文件必须显式指定 `-Encoding utf8` +- **画图**:优先使用 `mermaid flowchart ` +- **对话/文档编写**:必须是 中文为主体语言,技术术语保留英文原文 + +## 项目架构 +- **技术栈**:本项目为 `Go` 服务端,基于 `gin-vue-admin` 体系;Web 框架使用 `Gin`,ORM 使用 `GORM`,配置使用 `Viper`,日志使用 `Zap`,鉴权与权限控制使用 `JWT + Casbin`,接口文档使用 `Swagger`,缓存优先使用 `Redis`;关系型数据库统一通过 `GORM` 接入,数据库使用 `PostgreSQL` ;按配置可启用 `MongoDB`、`cron`、`Excelize`、多云 `OSS/S3`,部署默认支持 `Docker`。 +- **代码链路**:本项目采用 `Router → API → Service → GORM → Database` 分层。 +- `Model` 不作为独立调用层,负责承载实体、`request`、`response`,贯穿 `API`、`Service`、数据库映射全过程。 + + ├── server + ├── api (api层) + │ └── v1 (v1版本接口) + ├── config (配置包) + ├── core (核心文件) + ├── docs (swagger文档目录) + ├── global (全局对象) + ├── initialize (初始化) + │ └── internal (初始化内部函数) + ├── middleware (中间件层) + ├── model (模型层) + │ ├── request (入参结构体) + │ └── response (出参结构体) + ├── packfile (静态文件打包) + ├── resource (静态资源文件夹) + │ ├── excel (excel导入导出默认路径) + │ ├── page (表单生成器) + │ └── template (模板) + ├── router (路由层) + ├── service (service层) + ├── source (source层) + └── utils (工具包) + ├── timer (定时器接口封装) + └── upload (oss接口封装) + +| 层级 | 职责 | 命名规范 | +|:---|:---|:---| +| Router | 定义路由、路由分组、中间件、接口挂载关系;禁止承载业务逻辑 | 目录固定 `router/`;聚合入口固定 `enter.go`;路由承载结构体使用 `*Router` | +| API | 接收请求、参数绑定、基础校验、调用 `Service`、返回统一响应 | 目录固定 `api/v1/`;聚合入口固定 `enter.go`;接口承载结构体使用 `*Api` | +| Service | 实现业务规则、流程编排、事务控制、数据库读写;禁止把复杂业务长期堆在 `API` | 目录固定 `service/`;聚合入口固定 `enter.go`;服务承载结构体使用 `*Service` | +| Model | 定义表结构、`request`、`response` 结构体,作为分层之间的数据载体 | 实体放在 `model/`;入参放在 `model//request`;出参放在 `model//response`;结构体名使用 PascalCase | +| Database | 持久化存储,由 `Service` 通过 `GORM` 直接访问 | 数据库访问统一走 `global.GVA_DB`;表迁移统一在 `initialize/gorm_biz.go` 注册 | + +- 新增业务模块时,主落点一律放在 `service/`。 +- 新增业务模块时,接口配套放在 `api/v1/`。 +- 新增业务模块时,路由配套放在 `router/`。 +- 新增业务模块时,数据模型配套放在 `model/`。 +- 新增业务路由时,统一在 `initialize/router_biz.go` 注册。 +- 涉及表结构变更时,统一在 `initialize/gorm_biz.go` 注册迁移。 +- 本项目有`admin/app` 2套接口,一般默认是admin接口,app接口按需增加 + +### 架构关系 +- 关系总线:`doc-sql / doc-dict → Model → Service → API → Router`,其中 `Model` 贯穿 `API`、`Service` 与数据库映射;`doc-sql` 约束表结构,`doc-dict` 约束值域与枚举。 + +```mermaid +flowchart LR + DOCSQL["doc-sql
表结构/字段/索引/约束"] --> Model + DOCDICT["doc-dict
状态/类型/枚举值域"] --> Model + DOCDICT --> Service + DOCDICT --> API + Model --> Service + Service --> API + API --> Router 无法显示出图像啊 +``` + + +- 改 `doc-sql`:不能只改文档;必须同步检查并修改 `model/` 实体字段、`service/` 读写逻辑、`initialize/gorm_biz.go` 迁移注册。若字段名、类型、默认值、索引、唯一约束变化,相关查询条件、排序、唯一性校验、兼容逻辑都要一起改。 +- 改 `doc-dict`:必须同步修改代码里的枚举/常量、`Model` 字段注释与值域约束、`API` 参数校验、`Service` 分支判断与展示转换。若接口出入参暴露该值域,相关 `doc-api` 也必须同步。 +- 改 `Model`:必须区分是改实体、`request` 还是 `response`。改实体时,同步检查 `Service` 的查询/写入/扫描字段,以及 `doc-sql` 是否仍一致;改 `request/response` 时,同步检查 `API` 绑定、返回和 `doc-api`。 +- 改 `Service`:同步检查 `Model` 是否足够承载新字段/新状态,`API` 调用参数和返回值是否需要变化。若业务规则新增状态、类型、来源等值域,必须回头修改 `doc-dict`;若涉及落库结构变化,必须回头修改 `doc-sql` 与迁移注册。 +- 改 `API`:同步检查 `Router` 挂载的方法、路径、鉴权与分组是否一致;同步检查 `Model/request`、`Model/response` 和 `Service` 方法签名。若接口 contract 变化,`doc-api` 必须同步。 +- 改 `Router`:同步检查 `API` 是否已有对应处理函数;新增或调整业务路由时,必须同步修改 `initialize/router_biz.go` 注册。若分组、版本、权限中间件变化,相关接口文档与调用方也要一起检查。 +- 新增/修改业务功能时,默认顺序是:先定 `doc-sql` / `doc-dict`,再落 `Model`,再写 `Service`,再接 `API`,最后挂 `Router`;禁止只改链路中的单点而不回查上下游。 + +## 项目文档 + +- **根目录**:.ai-specs +- **要求**:开始写代码前,根据任务类型先定位目录,再读取对应文档;有对应文档必须先读。 +- **兜底**:索引表无匹配时,按本文件通用规则和现有同层代码风格实现 + +### coding-specs 存放对功能代码的说明/限制/要求 + +| 路径 | 用途 | 说明 | +|:---|:---|:---| +| `.ai-specs\coding-specs\api-auth-control.md` | 规定 API 在“不需要登录 / 需要登录 / 需要权限”之间切换时的路由挂载与权限同步方式 | 涉及接口鉴权方式调整、公开接口、仅登录接口、角色权限接口时必读 | +| `.ai-specs\coding-specs\module-admin-crud-default.md` | 规定业务模块 `admin` 端默认 CRUD 接口集合、命名、Method 与挂载方式 | 涉及新增业务后台 CRUD、判断默认后台接口应该有哪些时必读 | +| `.ai-specs\coding-specs\module-admin-app-split.md` | 规定同一业务模块下 `admin` 管理端接口与 `app` 用户端接口的目录落点、分层方式与鉴权边界 | 涉及 `book` 等同模块同时提供 `admin/app` 两套接口时必读 | +| `.ai-specs\coding-specs\sys-params.md` | 规定 `sys_params` 的读写方式与单参数独立 API 的封装方式 | 涉及系统参数读写、基于 `sys_params` 封装业务配置接口时必读 | +| `.ai-specs\coding-specs\vo-model-request-response.md` | 规定项目中实体、API 入参、API 出参与通用结构在 `model` 体系内的落点与复用边界 | 涉及 `vo` 放置方式、是否复用实体、何时新增 `request/response` 结构时必读 | + +### logic-specs 存放业务说明文档不设计代码 + +| 路径 | 用途 | 说明 | +|:---|:---|:---| +| `.ai-specs\logic-specs\book.md` | 记录书籍相关主体与字段草案 | 涉及书籍、书籍系列、书籍作者、书籍评论等业务设计时先读 | + +### sys-specs 存放系统级文档 + +| 路径 | 用途 | 说明 | +|:---|:---|:---| +| `.ai-specs\sys-specs\business-table-spec.md` | 规定新增业务表的 SQL 设计、索引、约束、迁移和兼容要求 | 涉及新增/修改业务表、字段、索引、唯一约束、迁移注册时必读 | +| `.ai-specs\sys-specs\business-dictionary-spec.md` | 规定新增业务字典的定义方式,以及代码枚举与字典值的一一对应关系 | 涉及新增业务状态、类型、级别、来源、模式、分类等值域时必读 | +| `.ai-specs\sys-specs\module-naming-spec.md` | 规定业务模块中文名与英文名的统一登记方式 | 涉及新增/修改业务模块命名、中英文对照、目录命名时必读 | + +### doc-api + +| 路径 | 用途 | 说明 | +|:---|:---|:---| + +### doc-sql 存放具体业务表文档 + +| 路径 | 用途 | 说明 | +|:---|:---|:---| +| `.ai-specs\doc-sql\book_author.md` | 定义书籍作者表的字段、索引、SQL 基线和兼容要求 | 涉及书籍作者表建模、迁移、唯一性约束和书籍作者关系改造时必读 | + +### doc-dict 存放具体业务字典文档 + +| 路径 | 用途 | 说明 | +|:---|:---|:---| +| `.ai-specs\doc-dict\book_author_status.md` | 定义作者状态的标准值域 | 涉及作者状态的存储、校验、展示和接口出入参时必读 | +| `.ai-specs\doc-dict\book_completion_status.md` | 定义书籍完结状态的标准值域 | 涉及书籍完结状态的存储、校验、展示和接口出入参时必读 | + + +## 可复用代码/组件 + +| 中文 | 代码文件名 | 说明 | +|:---|:---|:---| diff --git a/server/README.md b/server/README.md index 9a34870..b43315b 100644 --- a/server/README.md +++ b/server/README.md @@ -1,3 +1,10 @@ + +# 下载并整理依赖 +go mod tidy +# 在 server 目录下运行 +go run main.go + + ## server项目结构 ```shell diff --git a/server/config.yaml b/server/config.yaml index c4574f0..bd3da7e 100644 --- a/server/config.yaml +++ b/server/config.yaml @@ -1,212 +1,3 @@ -# github.com/flipped-aurora/gin-vue-admin/server Global Configuration - -# jwt configuration -jwt: - signing-key: qmPlus - expires-time: 7d - buffer-time: 1d - issuer: qmPlus -# zap logger configuration -zap: - level: info - format: console - prefix: "[github.com/flipped-aurora/gin-vue-admin/server]" - director: log - show-line: true - encode-level: LowercaseColorLevelEncoder - stacktrace-key: stacktrace - log-in-console: true - retention-day: -1 - -# redis configuration -redis: - #是否使用redis集群模式 - useCluster: false - #使用集群模式addr和db默认无效 - addr: 127.0.0.1:6379 - password: "" - db: 0 - clusterAddrs: - - "172.21.0.3:7000" - - "172.21.0.4:7001" - - "172.21.0.2:7002" - -# redis-list configuration -redis-list: - - name: cache # 数据库的名称,注意: name 需要在 redis-list 中唯一 - useCluster: false # 是否使用redis集群模式 - addr: 127.0.0.1:6379 # 使用集群模式addr和db默认无效 - password: "" - db: 0 - clusterAddrs: - - "172.21.0.3:7000" - - "172.21.0.4:7001" - - "172.21.0.2:7002" - -# mongo configuration -mongo: - coll: '' - options: '' - database: '' - username: '' - password: '' - auth-source: '' - min-pool-size: 0 - max-pool-size: 100 - socket-timeout-ms: 0 - connect-timeout-ms: 0 - is-zap: false - hosts: - - host: '' - port: '' - -# email configuration -email: - to: xxx@qq.com - port: 465 - from: xxx@163.com - host: smtp.163.com - is-ssl: true - secret: xxx - nickname: test - -# system configuration -system: - env: local # 修改为public可以关闭路由日志输出 - addr: 8888 - db-type: mysql - oss-type: local # 控制oss选择走本地还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置 - use-redis: false # 使用redis - use-mongo: false # 使用mongo - use-multipoint: false - # IP限制次数 一个小时15000次 - iplimit-count: 15000 - # IP限制一个小时 - iplimit-time: 3600 - # 路由全局前缀 - router-prefix: "" - # 严格角色模式 打开后权限将会存在上下级关系 - use-strict-auth: false - # 禁用自动迁移数据库表结构,生产环境建议设为true,手动迁移 - disable-auto-migrate: false - -# captcha configuration -captcha: - key-long: 6 - img-width: 240 - img-height: 80 - open-captcha: 0 # 0代表一直开启,大于0代表限制次数 - open-captcha-timeout: 3600 # open-captcha大于0时才生效 - -# mysql connect configuration -# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master) -mysql: - path: "" - port: "" - config: "" - db-name: "" - username: "" - password: "" - max-idle-conns: 10 - max-open-conns: 100 - log-mode: "" - log-zap: false - -# pgsql connect configuration -# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://gin-vue-admin.com/docs/first_master) -pgsql: - path: "" - port: "" - config: "" - db-name: "" - username: "" - password: "" - max-idle-conns: 10 - max-open-conns: 100 - log-mode: "" - log-zap: false -oracle: - path: "" - port: "" - config: "" - db-name: "" - username: "" - password: "" - max-idle-conns: 10 - max-open-conns: 100 - log-mode: "" - log-zap: false -mssql: - path: "" - port: "" - config: "" - db-name: "" - username: "" - password: "" - max-idle-conns: 10 - max-open-conns: 100 - log-mode: "" - log-zap: false -sqlite: - path: "" - port: "" - config: "" - db-name: "" - username: "" - password: "" - max-idle-conns: 10 - max-open-conns: 100 - log-mode: "" - log-zap: false -db-list: - - disable: true # 是否禁用 - type: "" # 数据库的类型,目前支持mysql、pgsql、mssql、oracle - alias-name: "" # 数据库的名称,注意: alias-name 需要在db-list中唯一 - path: "" - port: "" - config: "" - db-name: "" - username: "" - password: "" - max-idle-conns: 10 - max-open-conns: 100 - log-mode: "" - log-zap: false - -# local configuration -local: - path: uploads/file - store-path: uploads/file - -# autocode configuration -autocode: - web: web/src - root: "" # root 自动适配项目根目录, 请不要手动配置,他会在项目加载的时候识别出根路径 - server: server - module: 'github.com/flipped-aurora/gin-vue-admin/server' - ai-path: "" # AI服务路径 - -# qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址) -qiniu: - zone: ZoneHuaDong - bucket: "" - img-path: "" - use-https: false - access-key: "" - secret-key: "" - use-cdn-domains: false - -# minio oss configuration -minio: - endpoint: yourEndpoint - access-key-id: yourAccessKeyId - access-key-secret: yourAccessKeySecret - bucket-name: yourBucketName - use-ssl: false - base-path: "" - bucket-url: "http://host:9000/yourBucketName" - -# aliyun oss configuration aliyun-oss: endpoint: yourEndpoint access-key-id: yourAccessKeyId @@ -214,29 +5,28 @@ aliyun-oss: bucket-name: yourBucketName bucket-url: yourBucketUrl base-path: yourBasePath - -# tencent cos configuration -tencent-cos: - bucket: xxxxx-10005608 - region: ap-shanghai - secret-id: your-secret-id - secret-key: your-secret-key - base-url: https://gin.vue.admin - path-prefix: github.com/flipped-aurora/gin-vue-admin/server - -# aws s3 configuration (minio compatible) +autocode: + web: web/src + root: D:\Code3\wdp\xuanzhi-service + server: server + module: github.com/flipped-aurora/gin-vue-admin/server + ai-path: "" aws-s3: bucket: xxxxx-10005608 region: ap-shanghai endpoint: "" - s3-force-path-style: false - disable-ssl: false secret-id: your-secret-id secret-key: your-secret-key base-url: https://gin.vue.admin path-prefix: github.com/flipped-aurora/gin-vue-admin/server - -# cloudflare r2 configuration + s3-force-path-style: false + disable-ssl: false +captcha: + key-long: 6 + img-width: 240 + img-height: 80 + open-captcha: 0 + open-captcha-timeout: 3600 cloudflare-r2: bucket: xxxx0bucket base-url: https://gin.vue.admin.com @@ -244,40 +34,223 @@ cloudflare-r2: account-id: xxx_account_id access-key-id: xxx_key_id secret-access-key: xxx_secret_key - -# huawei obs configuration +cors: + mode: strict-whitelist + whitelist: + - allow-origin: example1.com + allow-methods: POST, GET + allow-headers: Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id + expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type + allow-credentials: true + - allow-origin: example2.com + allow-methods: GET, POST + allow-headers: content-type + expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type + allow-credentials: true +db-list: + - type: "" + alias-name: "" + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false + disable: true +disk-list: + - mount-point: / +email: + to: xxx@qq.com + from: xxx@163.com + host: smtp.163.com + secret: xxx + nickname: test + port: 465 + is-ssl: true + is-loginauth: false +excel: + dir: ./resource/excel/ hua-wei-obs: path: you-path bucket: you-bucket endpoint: you-endpoint access-key: you-access-key secret-key: you-secret-key - -# excel configuration -excel: - dir: ./resource/excel/ - -# disk usage configuration -disk-list: - - mount-point: "/" - -# 跨域配置 -# 需要配合 server/initialize/router.go -> `Router.Use(middleware.CorsByRules())` 使用 -cors: - mode: strict-whitelist # 放行模式: allow-all, 放行全部; whitelist, 白名单模式, 来自白名单内域名的请求添加 cors 头; strict-whitelist 严格白名单模式, 白名单外的请求一律拒绝 - whitelist: - - allow-origin: example1.com - allow-headers: Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id - allow-methods: POST, GET - expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type - allow-credentials: true # 布尔值 - - allow-origin: example2.com - allow-headers: content-type - allow-methods: GET, POST - expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type - allow-credentials: true # 布尔值 +jwt: + signing-key: 725c9404-a8d2-4a4c-9496-745d11b65b3d + expires-time: 7d + buffer-time: 1d + issuer: qmPlus +local: + path: uploads/file + store-path: uploads/file mcp: name: GVA_MCP version: v1.0.0 + path: "" addr: 8889 + base_url: "" + upstream_base_url: "" + auth_header: "" + request_timeout: 0 + sse_path: "" + message_path: "" + url_prefix: "" separate: false +minio: + endpoint: yourEndpoint + access-key-id: yourAccessKeyId + access-key-secret: yourAccessKeySecret + bucket-name: yourBucketName + use-ssl: false + base-path: "" + bucket-url: http://host:9000/yourBucketName +mongo: + coll: "" + options: "" + database: "" + username: "" + password: "" + auth-source: "" + min-pool-size: 0 + max-pool-size: 100 + socket-timeout-ms: 0 + connect-timeout-ms: 0 + is-zap: false + hosts: + - host: "" + port: "" +mssql: + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +mysql: + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +oracle: + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +pgsql: + prefix: "" + port: "5432" + config: sslmode=disable TimeZone=Asia/Shanghai + db-name: xuanzhi + username: admin + password: admin@123456 + path: 10.1.0.1 + engine: "" + log-mode: error + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +qiniu: + zone: ZoneHuaDong + bucket: "" + img-path: "" + access-key: "" + secret-key: "" + use-https: false + use-cdn-domains: false +redis: + name: "" + addr: 127.0.0.1:6379 + password: "" + db: 0 + useCluster: false + clusterAddrs: + - 172.21.0.3:7000 + - 172.21.0.4:7001 + - 172.21.0.2:7002 +redis-list: + - name: cache + addr: 127.0.0.1:6379 + password: "" + db: 0 + useCluster: false + clusterAddrs: + - 172.21.0.3:7000 + - 172.21.0.4:7001 + - 172.21.0.2:7002 +sqlite: + prefix: "" + port: "" + config: "" + db-name: "" + username: "" + password: "" + path: "" + engine: "" + log-mode: "" + max-idle-conns: 10 + max-open-conns: 100 + singular: false + log-zap: false +system: + db-type: pgsql + oss-type: local + router-prefix: "" + addr: 8888 + iplimit-count: 15000 + iplimit-time: 3600 + use-multipoint: false + use-redis: false + use-mongo: false + use-strict-auth: false + disable-auto-migrate: false +tencent-cos: + bucket: xxxxx-10005608 + region: ap-shanghai + secret-id: your-secret-id + secret-key: your-secret-key + base-url: https://gin.vue.admin + path-prefix: github.com/flipped-aurora/gin-vue-admin/server +zap: + level: info + prefix: '[github.com/flipped-aurora/gin-vue-admin/server]' + format: console + director: log + encode-level: LowercaseColorLevelEncoder + stacktrace-key: stacktrace + show-line: true + log-in-console: true + retention-day: -1 diff --git a/server/go.mod b/server/go.mod index fc2d09d..308d285 100644 --- a/server/go.mod +++ b/server/go.mod @@ -50,6 +50,7 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/crypto v0.37.0 golang.org/x/sync v0.13.0 + golang.org/x/sys v0.32.0 golang.org/x/text v0.24.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/datatypes v1.2.5 @@ -192,7 +193,6 @@ require ( golang.org/x/image v0.23.0 // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.35.0 // indirect - golang.org/x/sys v0.32.0 // indirect golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.29.0 // indirect google.golang.org/protobuf v1.36.6 // indirect diff --git a/web/AGENTS.md b/web/AGENTS.md new file mode 100644 index 0000000..7d1eb54 --- /dev/null +++ b/web/AGENTS.md @@ -0,0 +1,73 @@ +# AI 开发入口 [!IMPORTANT] + +- 本文档为项目级别规范和重要导航,必须严格参考 +- 对我的回复/文档编写 ,必须是 中文为主体语言,技术术语保留英文原文 +- 强制工作流:按下方索引表定位需要的文档 → 读取匹配文档 → 进入源码 +- 禁止跳过文档检查直接写复杂业务;禁止已有对应文档却不读 +- 索引表无匹配时,按本文件通用规则和现有同层代码风格实现 +- 业务流程需要遵循 `主流做法` `工业级正规` +- 规范型文档是给 AI 的顶层入口文档,不是“解释得更全”就更好,而是要 更短、更硬、更可判定 +- 本文档只允许在已有的结构上CURD,不允许增加其他标题区 + + +## 工具使用规则 + +- **搜索范围限制**:Grep/Glob 严禁全盘搜索,绝对禁止扫描 `.gitignore` 忽略的目录,以避免性能卡顿。 +- **读写**:所有文件读取/写入统一使用 UTF-8(建议无 BOM) +- **读写**:PowerShell/脚本读取项目文件必须显式指定 `-Encoding utf8` + +## 当前项目代码要求 + +### 通用要求 + +- 优化代码时必须同时考虑冗余、孤岛代码、代码清晰度、复杂度、边界条件和兼容性,不能只追求功能跑通。 +- 优化代码时不要修改ui/ux 视觉效果,除非明确要求。 +- 先复用再新增 +- 允许抽公共逻辑,但公共逻辑必须放在根级共享目录,并保证边界仍清晰。 + +### 项目偏好 +- + +## 文档体系 + +开始写代码前,根据下表判断当前任务涉及哪些文档,有对应文档必须先读。 +- .ai-specs 为规范根目录 +- .ai-specs\coding-specs +- .ai-specs\logic-specs + +### 目录索引表 + +| 类型 | 路径 | 用途 | 说明 | +|:---|:---|:---|:---| + +## 项目架构 + +### 模块说明 + + +### 业务流转 + +#### 1. xxx + +- + +## 命名规范 + +- + + +### 模块词汇 + +| 中文 | 英文 | 说明 | +|:---|:---|:---| +| 设备 | device | 设备管理模块 | + + +## 可复用组件 + +- + +## 技术栈专项规范 + +### xxx +- diff --git a/web/README.md b/web/README.md index 06f1a8c..ddf50c0 100644 --- a/web/README.md +++ b/web/README.md @@ -1,3 +1,15 @@ +# 安装项目依赖 +npm install + +# 或使用 yarn(如果已安装) +yarn install + +# 或使用 pnpm(推荐,速度更快) +pnpm install + + + + # gin-vue-admin web ## Project setup