Compare commits
4 Commits
5ca65e3004
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5520dafb56 | |||
| cec46cabbe | |||
| 16116a841d | |||
| 5e3380f5ef |
105
CLAUDE.md
Normal file
105
CLAUDE.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
云酒馆 (SillyTavern Cloud) — a full-stack AI role-playing chat platform. Frontend is React + TypeScript + Tailwind + Vite (`web-app/`). Backend is Go + Gin + GORM (`server/`). The two are fully separated and communicate via REST API + SSE streaming.
|
||||
|
||||
## Build & Run Commands
|
||||
|
||||
### Frontend (`web-app/`)
|
||||
```bash
|
||||
npm install
|
||||
npm run dev # Dev server at http://localhost:5174
|
||||
npm run build # tsc + vite build (type-check then bundle)
|
||||
npm run preview # Preview production build
|
||||
```
|
||||
|
||||
### Backend (`server/`)
|
||||
```bash
|
||||
go mod tidy
|
||||
go build -o server .
|
||||
./server -c config.yaml # Runs at http://localhost:8888
|
||||
```
|
||||
|
||||
### Type-check only (no emit)
|
||||
```bash
|
||||
# Frontend
|
||||
cd web-app && npx tsc --noEmit
|
||||
|
||||
# Backend
|
||||
cd server && go build ./...
|
||||
```
|
||||
|
||||
## Build Verification
|
||||
|
||||
After any code edit, always run the build/compile step before reporting success. For Go files: `go build ./...`. For TypeScript files: `npx tsc --noEmit`. Never assume edits are correct without verification.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Backend (`server/`)
|
||||
|
||||
Layered architecture: **routes → API handlers → services → models/DB**
|
||||
|
||||
```
|
||||
api/v1/app/ # App-facing HTTP handlers (auth, character, conversation, ai_config, etc.)
|
||||
api/v1/system/ # Admin/system handlers
|
||||
service/app/ # Business logic layer
|
||||
model/app/ # GORM data models (AppUser, AiCharacter, Conversation, Preset, Worldbook, RegexScript)
|
||||
router/app/ # Gin route registration
|
||||
middleware/ # JWT auth, CORS, logging, recovery
|
||||
initialize/ # DB, Redis, router, plugin setup (called from main.go)
|
||||
core/ # Zap logging, Viper config loading, server startup
|
||||
```
|
||||
|
||||
Key models: `AppUser`, `AiCharacter` (SillyTavern V2 card format), `Conversation`, `Preset` (sampling params), `Worldbook` (keyword-triggered context), `RegexScript` (message transforms).
|
||||
|
||||
AI providers: OpenAI-compatible + Anthropic. Unified through the AI config service. Conversations use SSE streaming for real-time token delivery.
|
||||
|
||||
### Frontend (`web-app/src/`)
|
||||
|
||||
MVU (Model-View-Update) pattern via **Zustand** with `persist` middleware (localStorage).
|
||||
|
||||
```
|
||||
store/index.ts # Single global Zustand store — source of truth for user, currentCharacter,
|
||||
# currentConversation, messages, variables, UI state
|
||||
api/ # One file per domain: auth, character, conversation, preset, worldbook, regex
|
||||
# All share the axios instance in api/client.ts (injects JWT, handles 401)
|
||||
pages/ # Route-level components (ChatPage, CharacterManagePage, AdminPage, etc.)
|
||||
components/ # Reusable UI — ChatArea, CharacterPanel, SettingsPanel, Sidebar, Navbar,
|
||||
# MessageContent (renders markdown + HTML safely via rehype-sanitize)
|
||||
```
|
||||
|
||||
Routes are defined in `App.tsx`. Auth guard redirects to `/login` when `isAuthenticated` is false.
|
||||
|
||||
The `variables: Record<string, string>` field in the store implements the MVU variable system used for template substitution in AI prompts (e.g., `{{user}}`, `{{char}}`).
|
||||
|
||||
### API Convention
|
||||
|
||||
- Base URL: `http://localhost:8888` (configured via `VITE_API_BASE_URL` in `.env.development`)
|
||||
- Auth: `Authorization: Bearer <jwt>` header, injected automatically by `api/client.ts`
|
||||
- App routes: `/app/auth/...`, `/app/user/...`, `/app/character/...`, `/app/conversation/...`
|
||||
- Admin routes: `/system/...` (Casbin RBAC enforced)
|
||||
|
||||
## Go Development
|
||||
|
||||
When editing Go files, always check all call sites of modified functions. If a function signature changes (parameters or return values), grep for all usages and update them in the same edit batch.
|
||||
|
||||
Run `go build ./...` from `server/` to verify after every edit.
|
||||
|
||||
## Frontend Development
|
||||
|
||||
When editing Vue/TypeScript frontend files, verify all imports are present and no duplicate code remains after edits. Read the file back after editing to confirm structural integrity.
|
||||
|
||||
Run `npx tsc --noEmit` from `web-app/` to verify after every edit.
|
||||
|
||||
## Third-Party Integrations
|
||||
|
||||
When integrating third-party SDKs or APIs (payment providers, AI model APIs), read the actual SDK source or docs first before writing code. Do not guess method names, parameter types, or key sizes.
|
||||
|
||||
## Configuration
|
||||
|
||||
Backend config file: `server/config.yaml` (not committed; copy from `config.example.yaml` if present). Supports MySQL, PostgreSQL, SQLite, SQL Server, Oracle via GORM. Redis for sessions/caching.
|
||||
|
||||
Frontend env files: `web-app/.env.development` and `web-app/.env.production` — set `VITE_API_BASE_URL`.
|
||||
BIN
docs/Clannad_v3.1.png
Normal file
BIN
docs/Clannad_v3.1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
357
docs/Clannad_v3.1_analysis.md
Normal file
357
docs/Clannad_v3.1_analysis.md
Normal file
@@ -0,0 +1,357 @@
|
||||
# Clannad_v3.1.png 案例分析与落地要点
|
||||
|
||||
## 目标角色卡概览
|
||||
|
||||
- **图像**:@docs/Clannad_v3.1.png(图片来源、版权信息见下方字段)
|
||||
- **风格**:Clannad 系列日系校园少女题材,具有较强的情感表达与背景设定需求
|
||||
- **已知需求**:在云酒馆系统中以“前端卡”方式呈现,并能注入背景信息、变量、互动 UI
|
||||
|
||||
---
|
||||
|
||||
## 10.1 数据字段与数据模型需求
|
||||
|
||||
| 字段名 | 说明 | 备注 |
|
||||
|--------|------|------|
|
||||
| portrait_url | 图片主地址 | 建议使用 CDN 托管,支持 webp/AVIF 高效格式 |
|
||||
| portrait_alt | 无障碍文本描述 | 供屏幕阅读器使用,描述角色外观特征 |
|
||||
| thumbnail_url | 缩略图地址 | 列表页展示使用,建议 200x200 以内 |
|
||||
| portrait_caption | 图片简短说明 | 显示在图片下方的简短描述 |
|
||||
| image_source | 版权信息/出处 | 记录图片来源、授权信息 |
|
||||
| worldbook_id | 关联的 Worldbook | 用于注入背景设定 |
|
||||
| worldbook_entries | 世界书条目集合 | 用于背景信息注入 |
|
||||
| name | 角色名称 | 现有字段 |
|
||||
| description | 角色描述 | 现有字段 |
|
||||
| personality | 性格设定 | 现有字段 |
|
||||
| scenario | 场景设定 | 现有字段 |
|
||||
| first_mes | 开场白 | 现有字段 |
|
||||
| mes_example | 对话示例 | 现有字段 |
|
||||
| system_prompt | 系统提示词 | 现有字段 |
|
||||
| extensions | 扩展数据 | MVU 相关字段 |
|
||||
| variables | 变量定义 | MVU 相关字段 |
|
||||
|
||||
---
|
||||
|
||||
## 10.2 后端改动要点
|
||||
|
||||
### 10.2.1 数据模型扩展
|
||||
|
||||
在 `model/app/ai_character.go` 中新增以下字段:
|
||||
|
||||
```go
|
||||
type AICharacter struct {
|
||||
// ... 现有字段 ...
|
||||
|
||||
// 图片相关字段
|
||||
PortraitURL string `json:"portraitUrl" gorm:"type:varchar(500)"`
|
||||
PortraitAlt string `json:"portraitAlt" gorm:"type:varchar(200)"`
|
||||
ThumbnailURL string `json:"thumbnailUrl" gorm:"type:varchar(500)"`
|
||||
PortraitCaption string `json:"portraitCaption" gorm:"type:varchar(200)"`
|
||||
ImageSource string `json:"imageSource" gorm:"type:varchar(500)"` // 版权信息
|
||||
|
||||
// Worldbook 关联
|
||||
WorldbookID *uint `json:"worldbookId"`
|
||||
}
|
||||
```
|
||||
|
||||
### 10.2.2 API 接口
|
||||
|
||||
- **上传图片**:`POST /api/v1/app/character/:id/portrait`
|
||||
- 鉴权:需要用户登录
|
||||
- 校验:文件大小(建议最大 5MB)、格式(支持 jpg/png/webp/gif)
|
||||
- 返回:portrait_url、thumbnail_url
|
||||
|
||||
- **获取角色图片信息**:`GET /api/v1/app/character/:id`
|
||||
- 返回字段包含 portrait_url、portrait_alt、thumbnail_url、portrait_caption、image_source
|
||||
|
||||
### 10.2.3 Worldbook 关联增强
|
||||
|
||||
在 `conversation.go` 的 system prompt 构建流程中,增加对角色关联 Worldbook 的背景注入:
|
||||
|
||||
- 读取 `character.WorldbookID`
|
||||
- 通过 WorldbookEngine 查询对应的启用条目
|
||||
- 将背景信息注入到 system_prompt 中(需控制 token budget)
|
||||
|
||||
### 10.2.4 数据迁移
|
||||
|
||||
为现有角色卡补充默认字段值:
|
||||
|
||||
```sql
|
||||
ALTER TABLE ai_characters
|
||||
ADD COLUMN IF NOT EXISTS portrait_url VARCHAR(500),
|
||||
ADD COLUMN IF NOT EXISTS portrait_alt VARCHAR(200),
|
||||
ADD COLUMN IF NOT EXISTS thumbnail_url VARCHAR(500),
|
||||
ADD COLUMN IF NOT EXISTS portrait_caption VARCHAR(200),
|
||||
ADD COLUMN IF NOT EXISTS image_source VARCHAR(500),
|
||||
ADD COLUMN IF NOT EXISTS worldbook_id UINT REFERENCES worldbooks(id);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10.3 前端实现要点
|
||||
|
||||
### 10.3.1 角色卡片组件
|
||||
|
||||
在 `web-app/src/components/` 中新增或扩展角色卡片组件:
|
||||
|
||||
```tsx
|
||||
interface CharacterCardProps {
|
||||
portraitUrl?: string;
|
||||
portraitAlt?: string;
|
||||
thumbnailUrl?: string;
|
||||
portraitCaption?: string;
|
||||
// ... 其他字段
|
||||
}
|
||||
|
||||
const CharacterPortrait: React.FC<CharacterCardProps> = ({
|
||||
portraitUrl,
|
||||
portraitAlt,
|
||||
thumbnailUrl,
|
||||
portraitCaption
|
||||
}) => {
|
||||
if (!portraitUrl) return null;
|
||||
|
||||
return (
|
||||
<div className="character-portrait">
|
||||
<img
|
||||
src={portraitUrl}
|
||||
alt={portraitAlt || '角色图像'}
|
||||
loading="lazy"
|
||||
className="portrait-image"
|
||||
/>
|
||||
{portraitCaption && (
|
||||
<p className="portrait-caption">{portraitCaption}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 10.3.2 样式隔离
|
||||
|
||||
使用 CSS Modules 或在组件级别定义样式:
|
||||
|
||||
```css
|
||||
/* CharacterPortrait.module.css */
|
||||
.character-portrait {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.portrait-image {
|
||||
max-width: 300px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.portrait-caption {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-top: 8px;
|
||||
}
|
||||
```
|
||||
|
||||
### 10.3.3 沙箱渲染
|
||||
|
||||
对于需要执行脚本的前端卡,使用 iframe 沙箱:
|
||||
|
||||
```tsx
|
||||
const FrontendCardSandbox: React.FC<{ htmlContent: string }> = ({ htmlContent }) => {
|
||||
return (
|
||||
<iframe
|
||||
srcDoc={htmlContent}
|
||||
sandbox="allow-scripts"
|
||||
title="前端卡内容"
|
||||
className="frontend-card-iframe"
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 10.3.4 postMessage 通信
|
||||
|
||||
定义消息类型:
|
||||
|
||||
```ts
|
||||
// types/frontend-card.ts
|
||||
interface FrontendCardMessage {
|
||||
type: 'GET_VARIABLE' | 'SET_VARIABLE' | 'SEND_MESSAGE' | 'TRIGGER_ACTION';
|
||||
payload: Record<string, any>;
|
||||
}
|
||||
|
||||
interface FrontendCardResponse {
|
||||
type: 'VARIABLE_VALUE' | 'ACTION_RESULT' | 'ERROR';
|
||||
payload: Record<string, any>;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10.4 Worldbook 与背景注入
|
||||
|
||||
### 10.4.1 背景信息转 Worldbook 条目
|
||||
|
||||
将 Clannad 角色的背景故事、人物关系、剧情要点等转换为 Worldbook 条目:
|
||||
|
||||
```json
|
||||
{
|
||||
"keys": ["冈崎汐", "汐", "女儿"],
|
||||
"content": "冈崎汐,主人公冈崎朋也的女儿。5岁时因事故去世,后来以幽灵的形式再次出现在父亲面前...",
|
||||
"enabled": true,
|
||||
"constant": true,
|
||||
"position": 1,
|
||||
"order": 1
|
||||
}
|
||||
```
|
||||
|
||||
### 10.4.2 Token Budget 控制
|
||||
|
||||
在 `buildContextManagedSystemPrompt` 中,对 Worldbook 注入的内容进行 token 预算控制:
|
||||
|
||||
- **优先级**:Worldbook 触发条目 > CharacterBook 内嵌条目 > MesExample
|
||||
- **预算分配**:system_prompt 总预算的 20-30% 用于 Worldbook 背景注入
|
||||
- **截断策略**:超出预算时,从最低优先级条目开始丢弃
|
||||
|
||||
---
|
||||
|
||||
## 10.5 安全性与合规
|
||||
|
||||
### 10.5.1 图片安全
|
||||
|
||||
- **来源限制**:只允许从受信任的 CDN 或本地上传
|
||||
- **内容审查**:上传图片需经过内容审核(可接入第三方审核服务)
|
||||
- **元数据清理**:清理图片 EXIF 信息,防止泄露拍摄设备、地点等隐私
|
||||
|
||||
### 10.5.2 脚本安全
|
||||
|
||||
- **沙箱隔离**:前端卡内的 JavaScript 必须在 iframe 沙箱中运行
|
||||
- **API 白名单**:只暴露受限的 API 接口,映射到后端受控服务
|
||||
- **CSP 策略**:
|
||||
|
||||
```
|
||||
Content-Security-Policy:
|
||||
default-src 'self';
|
||||
script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:;
|
||||
style-src 'self' 'unsafe-inline';
|
||||
img-src 'self' https://cdn.example.com data:;
|
||||
connect-src 'self' https://api.example.com;
|
||||
```
|
||||
|
||||
### 10.5.3 审核流程
|
||||
|
||||
- **新卡审核**:用户上传新角色卡后,需经过管理员审核
|
||||
- **定期检查**:对已上线卡片进行定期安全扫描
|
||||
- **举报机制**:提供用户举报入口
|
||||
|
||||
---
|
||||
|
||||
## 10.6 迁移策略
|
||||
|
||||
### 10.6.1 MVP 阶段(第一阶段)
|
||||
|
||||
- 只引入 portrait_url、portrait_alt、thumbnail_url 字段
|
||||
- 前端仅展示图片,不改变现有对话逻辑
|
||||
- 不支持前端卡脚本执行
|
||||
|
||||
### 10.6.2 进阶阶段(第二阶段)
|
||||
|
||||
- 接入 Worldbook 背景注入
|
||||
- 支持 portrait_caption 显示
|
||||
- 引入基础的 iframe 沙箱渲染
|
||||
|
||||
### 10.6.3 完整阶段(第三阶段)
|
||||
|
||||
- 支持完整的前端卡脚本执行
|
||||
- 实现 postMessage 通信
|
||||
- 支持 RPG、VN 等复杂交互系统
|
||||
|
||||
### 10.6.4 回滚策略
|
||||
|
||||
- 每个阶段保留回滚开关
|
||||
- 遇到兼容性问题时,可一键回退到上一阶段
|
||||
- 数据库字段支持版本标记
|
||||
|
||||
---
|
||||
|
||||
## 10.7 验证与测试
|
||||
|
||||
### 10.7.1 功能测试
|
||||
|
||||
| 测试用例 | 预期结果 |
|
||||
|---------|---------|
|
||||
| 上传角色图片 | 图片正确保存,返回正确的 portrait_url |
|
||||
| 图片加载失败 | 显示默认占位图,alt 文本正确 |
|
||||
| 无图片角色卡 | 正常显示,不崩溃 |
|
||||
| Worldbook 注入 | 背景信息正确注入到对话上下文 |
|
||||
| 懒加载图片 | 首屏不加载,滚动后加载 |
|
||||
|
||||
### 10.7.2 安全性测试
|
||||
|
||||
| 测试用例 | 预期结果 |
|
||||
|---------|---------|
|
||||
| XSS 注入(图片描述) | 被转义,不执行 |
|
||||
| 恶意脚本(前卡内) | 在沙箱中被阻止,无法访问父窗口 |
|
||||
| 跨域请求 | 被 CSP 拦截 |
|
||||
|
||||
### 10.7.3 性能测试
|
||||
|
||||
| 指标 | 目标 |
|
||||
|-----|------|
|
||||
| 图片首次加载 | < 1s(5G 网络) |
|
||||
| 懒加载触发 | 视口进入后 200ms 内 |
|
||||
| 内存占用 | 前端卡渲染时 < 50MB 额外 |
|
||||
|
||||
---
|
||||
|
||||
## 10.8 版权信息与来源
|
||||
|
||||
### 10.8.1 必填字段
|
||||
|
||||
- **image_source**:图片来源(如「官方美术集」「同人创作」)
|
||||
- **license_type**:许可证类型(如「MIT」「CC BY-SA 4.0」「原创」)
|
||||
- **original_artist**:原作者名称(若为同人创作)
|
||||
|
||||
---
|
||||
|
||||
## 10.9 附录/接口示例
|
||||
|
||||
### 10.9.1 角色图片上传 API
|
||||
|
||||
```bash
|
||||
# 请求
|
||||
POST /api/v1/app/character/1/portrait
|
||||
Authorization: Bearer <token>
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
# 响应
|
||||
{
|
||||
"portraitUrl": "https://cdn.example.com/characters/1/portrait.webp",
|
||||
"thumbnailUrl": "https://cdn.example.com/characters/1/thumb.webp",
|
||||
"portraitAlt": "粉色头发的少女,穿着校服"
|
||||
}
|
||||
```
|
||||
|
||||
### 10.9.2 角色详情 API 扩展
|
||||
|
||||
```bash
|
||||
# GET /api/v1/app/character/1
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"name": "冈崎汐",
|
||||
"portraitUrl": "https://cdn.example.com/characters/1/portrait.webp",
|
||||
"portraitAlt": "粉色头发的少女,穿着校服",
|
||||
"thumbnailUrl": "https://cdn.example.com/characters/1/thumb.webp",
|
||||
"portraitCaption": "Clannad 主角冈崎朋也的女儿",
|
||||
"imageSource": "官方美术集",
|
||||
"worldbookId": 5,
|
||||
// ... 其他字段
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 结论
|
||||
|
||||
通过独立分析文档分离,便于版本管理与跨团队协作。请在实现过程中严格遵循安全沙箱与版权规范,确保云酒馆的扩展能力可控且可维护。
|
||||
|
||||
本落地要点覆盖了从数据模型、后端 API、前端渲染、Worldbook 集成、安全合规到迁移测试的完整流程,建议按 MVP → 进阶 → 完整三阶段逐步落地。
|
||||
456
docs/flutter参考.md
Normal file
456
docs/flutter参考.md
Normal file
@@ -0,0 +1,456 @@
|
||||
### NativeTavern 功能设计文档(供 Go + React 集成参考)
|
||||
|
||||
---
|
||||
|
||||
## 总览
|
||||
|
||||
本项目中,这几个模块的作用和依赖关系如下:
|
||||
|
||||
- **聊天功能(Chat)**:统一负责从「用户消息」到「LLM 调用」再到「写回消息」的完整流程。
|
||||
- **提示词管理(Prompt Management)**:把系统提示、人设、世界信息、作者注释、历史等拆成可配置的「段」,决定顺序与注入深度。
|
||||
- **宏系统(Macros)**:在构建 prompt 时,根据上下文展开 `{{user}}`、`{{char}}`、`{{time}}`、`{{random}}` 等占位符。
|
||||
- **变量系统(Variables)**:提供全局 / 每会话变量 + `{{setvar}}` / `{{getvar}}` 等变量宏,支持状态机、计数等逻辑。
|
||||
- **思维链支持(Chain-of-Thought / Reasoning)**:对 o1/o3、Claude、Gemini 等的推理内容单独解析、存储,并在前端单独渲染。
|
||||
|
||||
Go + React 中建议同样分层:**数据模型(DB) + 领域服务(Go) + 状态/UI(React)**。
|
||||
|
||||
---
|
||||
|
||||
## 一、变量系统(Variables System)
|
||||
|
||||
### 1.1 目标
|
||||
|
||||
- 支持两类变量:
|
||||
- **全局变量**:跨所有聊天共用,用户级。
|
||||
- **本地变量**:每个 chat 独立,存到 chat metadata。
|
||||
- 通过宏语法在文本中读写变量,如:
|
||||
- `{{setvar::score::10}}`
|
||||
- `{{incvar::turn}}`
|
||||
- `{{getvar::user_name}}`
|
||||
|
||||
### 1.2 数据模型(示例)
|
||||
|
||||
后端可参考:
|
||||
|
||||
```go
|
||||
// 全局变量:按 userId 存
|
||||
type GlobalVariables map[string]any // name -> value
|
||||
|
||||
// 本地变量:按 chatId 存
|
||||
type LocalVariables map[string]map[string]any // chatId -> (name -> value)
|
||||
|
||||
type ChatMetadata struct {
|
||||
Variables map[string]any `json:"variables,omitempty"`
|
||||
// ... 其他 metadata 字段 ...
|
||||
}
|
||||
```
|
||||
|
||||
- 全局变量:存 Redis / Postgres JSONB / KV,按 userId 分片。
|
||||
- 本地变量:挂在 `chats.metadata.variables` 字段。
|
||||
|
||||
### 1.3 变量服务接口(Go)
|
||||
|
||||
```go
|
||||
type VariablesService interface {
|
||||
// 全局变量
|
||||
GetGlobal(userId, name string) any
|
||||
SetGlobal(userId, name string, v any) error
|
||||
AddGlobal(userId, name string, delta any) (any, error)
|
||||
IncGlobal(userId, name string) (any, error)
|
||||
DecGlobal(userId, name string) (any, error)
|
||||
|
||||
// 本地变量(带 chatId)
|
||||
GetLocal(userId, chatId, name string) any
|
||||
SetLocal(userId, chatId, name string, v any) error
|
||||
AddLocal(userId, chatId, name string, delta any) (any, error)
|
||||
IncLocal(userId, chatId, name string) (any, error)
|
||||
DecLocal(userId, chatId, name string) (any, error)
|
||||
|
||||
LoadLocalFromMetadata(chatId string, metaVars map[string]any)
|
||||
ExportLocalToMetadata(chatId string) map[string]any
|
||||
}
|
||||
```
|
||||
|
||||
### 1.4 变量宏语法
|
||||
|
||||
支持以下语法(与项目一致):
|
||||
|
||||
- 本地变量:
|
||||
- `{{setvar::name::value}}`
|
||||
- `{{addvar::name::value}}`
|
||||
- `{{incvar::name}}`
|
||||
- `{{decvar::name}}`
|
||||
- `{{getvar::name}}`
|
||||
- 全局变量:
|
||||
- `{{setglobalvar::name::value}}`
|
||||
- `{{addglobalvar::name::value}}`
|
||||
- `{{incglobalvar::name}}`
|
||||
- `{{decglobalvar::name}}`
|
||||
- `{{getglobalvar::name}}`
|
||||
|
||||
解析函数示例:
|
||||
|
||||
```go
|
||||
func ProcessVariableMacros(input, userId string, chatId *string, vars VariablesService) (string, error)
|
||||
```
|
||||
|
||||
实现建议:
|
||||
|
||||
1. 为每种宏写一个正则,比如:
|
||||
- `\{\{setvar::([^:]+)::([^}]*)\}\}`
|
||||
- `\{\{getvar::([^}]+)\}\}` 等。
|
||||
2. 按「写变量 → 读变量」顺序依次 `ReplaceAllStringFunc`:
|
||||
- set / add / inc / dec:调用 `VariablesService`,返回空串或新值字符串。
|
||||
- get:用变量值替换整段 `{{...}}`。
|
||||
|
||||
**集成位置**:在构建 prompt、处理用户输入时,先跑一遍 `ProcessVariableMacros`。
|
||||
|
||||
---
|
||||
|
||||
## 二、宏系统(Macro System)
|
||||
|
||||
### 2.1 目标
|
||||
|
||||
- 使用 `{{...}}` 宏在 prompt 中引入上下文信息,包括:
|
||||
- 用户/人设:`{{user}}`、`{{persona}}`、`{{user_description}}`
|
||||
- 角色:`{{char}}`、`{{char_description}}`、`{{system_prompt}}`、`{{jailbreak}}`
|
||||
- 时间:`{{time}}`、`{{date}}`、`{{weekday}}`
|
||||
- 聊天上下文:`{{lastMessage}}`、`{{lastUserMessage}}`、`{{messageCount}}` 等
|
||||
- 随机:`{{random}}`、`{{random::min::max}}`、`{{roll::2d6}}`、`{{pick::a::b}}`
|
||||
- 元信息:`{{model}}`、`{{provider}}`、`{{idle_duration}}` 等。
|
||||
|
||||
### 2.2 宏上下文(Go)
|
||||
|
||||
```go
|
||||
type MacroContext struct {
|
||||
UserName string
|
||||
UserDescription string
|
||||
|
||||
CharacterName string
|
||||
CharacterDescription string
|
||||
CharacterSystemPrompt string
|
||||
PostHistoryInstructions string
|
||||
|
||||
LastMessage string
|
||||
LastUserMessage string
|
||||
LastCharacterMessage string
|
||||
MessageCount int
|
||||
ChatID string
|
||||
|
||||
OriginalPrompt string
|
||||
CurrentInput string
|
||||
ModelName string
|
||||
ProviderName string
|
||||
IdleDurationMinutes int
|
||||
|
||||
Now time.Time
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 宏解析函数
|
||||
|
||||
```go
|
||||
func ProcessMacros(text string, ctx MacroContext) string
|
||||
```
|
||||
|
||||
推荐拆为多个子步骤(顺序类似原项目):
|
||||
|
||||
1. `_processRandomMacros`:
|
||||
- `{{random}}`
|
||||
- `{{random::min::max}}`
|
||||
- `{{roll::NdM}}`
|
||||
- `{{pick::opt1::opt2}}`
|
||||
- `{{uuid}}`
|
||||
2. `_processConditionalMacros`:
|
||||
- `{{if::cond::then}}`
|
||||
- `{{if::cond::then::else}}`(可以做简单布尔表达式解析)
|
||||
3. `_processTimeDateMacros`:
|
||||
- `{{time}}` / `{{date}}` / `{{weekday}}` / 自定义时间格式。
|
||||
4. `_processCharacterMacros`:
|
||||
- `{{char}}`、`{{char_description}}`、`{{system_prompt}}`、`{{post_history_instructions}}` 等。
|
||||
5. `_processUserMacros`:
|
||||
- `{{user}}` / `{{persona}}` / `{{user_description}}`。
|
||||
6. `_processChatMacros`:
|
||||
- `{{lastMessage}}` / `{{lastUserMessage}}` / `{{lastCharMessage}}` / `{{messageCount}}` / `{{chatId}}`。
|
||||
7. `_processSpecialMacros`:
|
||||
- `{{newline}}` / `{{nl}}`、`{{trim}}`、`{{noop}}`
|
||||
- `{{original}}`(原始 prompt,用于局部覆盖)
|
||||
- `{{input}}`(当前用户输入)
|
||||
- `{{model}}` / `{{provider}}`
|
||||
- `{{idle_duration}}`。
|
||||
|
||||
**集成顺序**(在构建 prompt 时):
|
||||
|
||||
1. 先跑变量宏 `ProcessVariableMacros`(会改变变量状态)。
|
||||
2. 再跑文本宏 `ProcessMacros`。
|
||||
|
||||
---
|
||||
|
||||
## 三、思维链支持(Chain-of-Thought / Reasoning)
|
||||
|
||||
### 3.1 目标
|
||||
|
||||
- 对支持推理的模型(o1/o3、Claude Thinking、Gemini 思考流):
|
||||
- 把推理内容单独抓出来(从 `reasoning_content` / `thinking` / `thought` 字段等)。
|
||||
- 和正常回复一起存在消息上。
|
||||
- 前端给一个可折叠的「思维链/推理」区域。
|
||||
|
||||
### 3.2 数据模型
|
||||
|
||||
```go
|
||||
type ChatMessage struct {
|
||||
ID string
|
||||
ChatID string
|
||||
Role string // "user" / "assistant" / "system"
|
||||
Content string
|
||||
Timestamp time.Time
|
||||
|
||||
Swipes []string
|
||||
CurrentSwipeIdx int
|
||||
|
||||
Reasoning *string // 默认推理文本
|
||||
ReasoningSwipes []string // 每个 swipe 的推理
|
||||
|
||||
// ... 其他字段(attachments、characterId 等)
|
||||
}
|
||||
```
|
||||
|
||||
前端便于使用的派生属性(在 React 里实现):
|
||||
|
||||
- `currentReasoning`:
|
||||
- 如果 `ReasoningSwipes` 长度 > `CurrentSwipeIdx` → 用对应项;
|
||||
- 否则用 `Reasoning`。
|
||||
- `hasReasoning`:
|
||||
- `Reasoning` 非空;或 `ReasoningSwipes` 中有任意非空字符串。
|
||||
|
||||
### 3.3 LLM 接口设计(Go)
|
||||
|
||||
**非流式:**
|
||||
|
||||
```go
|
||||
type LLMReasoningResponse struct {
|
||||
Content string
|
||||
Reasoning *string
|
||||
}
|
||||
|
||||
func GenerateWithReasoning(promptCtx PromptContext, cfg LLMConfig) (LLMReasoningResponse, error)
|
||||
```
|
||||
|
||||
**流式:**
|
||||
|
||||
```go
|
||||
type ReasoningChunk struct {
|
||||
Content *string
|
||||
Reasoning *string
|
||||
IsReasoningChunk bool
|
||||
}
|
||||
|
||||
func GenerateStreamWithReasoning(promptCtx PromptContext, cfg LLMConfig) (<-chan ReasoningChunk, error)
|
||||
```
|
||||
|
||||
解析逻辑示例(伪代码):
|
||||
|
||||
```go
|
||||
for chunk := range ch {
|
||||
if chunk.IsReasoningChunk && chunk.Reasoning != nil {
|
||||
reasoningBuffer.WriteString(*chunk.Reasoning)
|
||||
}
|
||||
if chunk.Content != nil {
|
||||
contentBuffer.WriteString(*chunk.Content)
|
||||
}
|
||||
// 把当前 buffer 写回当前 assistant 消息,前端即可实时显示
|
||||
}
|
||||
```
|
||||
|
||||
- OpenAI o1/o3:从 `choice.message.reasoning_content` 或自定义扩展字段中解析。
|
||||
- Claude:从 `thinking` 字段解析。
|
||||
- Gemini:从 `thought` 或增强字段解析。
|
||||
|
||||
### 3.4 前端渲染(React)
|
||||
|
||||
示例:
|
||||
|
||||
```tsx
|
||||
function MessageView({ message }: { message: ChatMessage }) {
|
||||
return (
|
||||
<div className="message">
|
||||
<div className="content">{renderMarkdownOrHtml(message.content)}</div>
|
||||
|
||||
{message.hasReasoning && (
|
||||
<ReasoningPanel
|
||||
label="Thinking"
|
||||
content={message.currentReasoning ?? ""}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
`ReasoningPanel`:
|
||||
|
||||
- 折叠 / 展开。
|
||||
- 文本可复制。
|
||||
- UI 上与主内容区明显区分(浅色背景、小字体等)。
|
||||
|
||||
---
|
||||
|
||||
## 四、聊天功能(Chat Flow)
|
||||
|
||||
### 4.1 目标
|
||||
|
||||
- 封装完整一轮对话的流程:
|
||||
1. 记录用户消息。
|
||||
2. 做摘要/裁剪历史(可选)。
|
||||
3. 根据 PromptManager + 世界信息 + 宏/变量 构造 prompt。
|
||||
4. 调用 LLM(流式/非流式 + reasoning)。
|
||||
5. 写入 assistant 消息,并更新前端状态。
|
||||
|
||||
### 4.2 核心流程(伪代码)
|
||||
|
||||
```go
|
||||
func (svc *ChatService) SendMessage(userId, chatId, input string, cfg LLMConfig, atts []ChatAttachment) error {
|
||||
chat := repo.GetChat(chatId)
|
||||
character := repo.GetCharacter(chat.CharacterID)
|
||||
history := repo.ListMessages(chatId)
|
||||
|
||||
// 1. 写 user 消息
|
||||
userMsg := ChatMessage{
|
||||
ID: newID(),
|
||||
ChatID: chatId,
|
||||
Role: "user",
|
||||
Content: input,
|
||||
Timestamp: time.Now(),
|
||||
Swipes: []string{input},
|
||||
// Attachments: atts, ...
|
||||
}
|
||||
repo.AddMessage(userMsg)
|
||||
|
||||
// 2. 可选:基于 token 限制做摘要或丢弃最老历史
|
||||
svc.checkAndSummarize(chat, history, cfg)
|
||||
|
||||
// 3. 构建 prompt 上下文
|
||||
promptCtx := svc.BuildPromptContext(userId, chat, character, history, input)
|
||||
|
||||
// 4. LLM 调用
|
||||
if cfg.Stream {
|
||||
return svc.streamAssistantResponse(chatId, promptCtx, cfg)
|
||||
}
|
||||
return svc.oneShotAssistantResponse(chatId, promptCtx, cfg)
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 BuildPromptContext 的职责
|
||||
|
||||
- 从 PromptManager 获取启用的 PromptSection(排序后)。
|
||||
- 从世界信息系统取匹配条目(按关键词、优先级、位置)。
|
||||
- 按「深度注入」规则遍历历史消息,决定在每条消息前插入哪些 PromptSection / 世界信息 / 作者注释:
|
||||
- 定义 `depthFromEnd = total-1 - i`。
|
||||
- 若 `section.InjectionDepth == depthFromEnd` → 在该消息之前插入对应提示。
|
||||
- 对所有文本块跑:
|
||||
1. `ProcessVariableMacros(...)`
|
||||
2. `ProcessMacros(...)`
|
||||
- 输出统一形式的 prompt,例如 `[]OpenAIMessage{ {role, content}, ... }`,交给 LLMProvider 层。
|
||||
|
||||
---
|
||||
|
||||
## 五、提示词管理(Prompt Management)
|
||||
|
||||
### 5.1 目标
|
||||
|
||||
- 把 prompt 拆成若干「段」,每段可:
|
||||
- 单独启用/禁用。
|
||||
- 调整顺序。
|
||||
- 指定注入深度(在倒数第几条消息前插入)。
|
||||
- 与 SillyTavern 的 `prompts` / `prompt_order` JSON 兼容。
|
||||
|
||||
### 5.2 数据模型
|
||||
|
||||
```go
|
||||
type PromptSectionType string
|
||||
|
||||
const (
|
||||
SectionSystemPrompt PromptSectionType = "systemPrompt"
|
||||
SectionPersona PromptSectionType = "persona"
|
||||
SectionCharacterDescription PromptSectionType = "characterDescription"
|
||||
SectionCharacterPersonality PromptSectionType = "characterPersonality"
|
||||
SectionCharacterScenario PromptSectionType = "characterScenario"
|
||||
SectionExampleMessages PromptSectionType = "exampleMessages"
|
||||
SectionWorldInfo PromptSectionType = "worldInfo"
|
||||
SectionWorldInfoAfter PromptSectionType = "worldInfoAfter"
|
||||
SectionAuthorNote PromptSectionType = "authorNote"
|
||||
SectionPostHistoryInstr PromptSectionType = "postHistoryInstructions"
|
||||
SectionNsfw PromptSectionType = "nsfw"
|
||||
SectionChatHistory PromptSectionType = "chatHistory"
|
||||
SectionEnhanceDefinitions PromptSectionType = "enhanceDefinitions"
|
||||
SectionCustom PromptSectionType = "custom"
|
||||
)
|
||||
|
||||
type PromptSection struct {
|
||||
Type PromptSectionType
|
||||
Name string
|
||||
Enabled bool
|
||||
Order int
|
||||
|
||||
Content *string
|
||||
Identifier *string // ST 中的 identifier
|
||||
Role *string // "system" / "user" / "assistant"
|
||||
InjectionPosition *int
|
||||
InjectionDepth *int
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 PromptManager 服务
|
||||
|
||||
```go
|
||||
type PromptManager struct {
|
||||
Sections []PromptSection
|
||||
}
|
||||
|
||||
func (pm *PromptManager) EnabledSections() []PromptSection {
|
||||
// 过滤 Enabled,并按 Order 排好
|
||||
}
|
||||
```
|
||||
|
||||
从 SillyTavern 导入:
|
||||
|
||||
1. 建一个 `identifier -> PromptSectionType` 映射。
|
||||
2. 解析 `prompts`(内容)和 `prompt_order`(顺序 & enabled),生成 `PromptSection`。
|
||||
3. 对不在映射表里的 identifier,统一归类为 `SectionCustom`。
|
||||
|
||||
### 5.4 深度注入逻辑
|
||||
|
||||
在构建 prompt 时,遍历历史消息 `messages`:
|
||||
|
||||
```go
|
||||
N := len(messages)
|
||||
for i, msg := range messages {
|
||||
depthFromEnd := N - 1 - i
|
||||
|
||||
// 1. 深度注入世界信息
|
||||
for _, entry := range depthWorldInfo {
|
||||
if entry.Depth == depthFromEnd {
|
||||
systemMsg := buildWorldInfoMessage(entry)
|
||||
promptMessages = append(promptMessages, systemMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 深度注入 PromptSection
|
||||
for _, sec := range depthBasedSections {
|
||||
if sec.InjectionDepth != nil && *sec.InjectionDepth == depthFromEnd {
|
||||
secMsgs := buildSectionMessages(sec, ctx)
|
||||
promptMessages = append(promptMessages, secMsgs...)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Author’s Note 深度注入
|
||||
// ...
|
||||
|
||||
// 4. 加入当前 chat 消息本身(user / assistant)
|
||||
promptMessages = append(promptMessages, convertChatMessage(msg))
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
1167
docs/html/index.html
Normal file
1167
docs/html/index.html
Normal file
File diff suppressed because it is too large
Load Diff
1222
docs/html/my.html
Normal file
1222
docs/html/my.html
Normal file
File diff suppressed because it is too large
Load Diff
1335
docs/html/st.html
Normal file
1335
docs/html/st.html
Normal file
File diff suppressed because it is too large
Load Diff
913
docs/init/pgsql_init.sql
Normal file
913
docs/init/pgsql_init.sql
Normal file
@@ -0,0 +1,913 @@
|
||||
-- SillyTavern Cloud / 云酒馆
|
||||
-- PostgreSQL 手动初始化脚本,对应后端 PgsqlInitHandler.InitData 全量初始化数据
|
||||
-- 使用方法(建议在全新空库上执行):
|
||||
-- psql -h <host> -U <user> -d <db> -f pgsql_init.sql
|
||||
--
|
||||
-- 注意:
|
||||
-- 1. 本脚本不会自动清理旧数据,如需在已有数据上重置,请自行 TRUNCATE 相关表。
|
||||
-- 2. 用户密码字段留空(''),初始化后请通过接口或 SQL 自行重置管理员密码。
|
||||
|
||||
BEGIN;
|
||||
|
||||
/* =========================
|
||||
* 1. 角色表 sys_authorities
|
||||
* ========================= */
|
||||
|
||||
INSERT INTO sys_authorities (authority_id, authority_name, parent_id, default_router)
|
||||
VALUES
|
||||
(888, '普通用户', 0, 'dashboard'),
|
||||
(9528, '测试角色', 0, 'dashboard'),
|
||||
(8881, '普通用户子角色', 888, 'dashboard');
|
||||
|
||||
|
||||
/* =========================
|
||||
* 2. 用户表 sys_users
|
||||
* 对应 initUser.InitializeData
|
||||
* ========================= */
|
||||
|
||||
INSERT INTO sys_users (uuid, username, password, nick_name, header_img, authority_id, phone, email, enable)
|
||||
VALUES
|
||||
('00000000-0000-0000-0000-000000000001', 'admin', '', 'Lee', 'https://qmplusimg.henrongyi.top/gva_header.jpg', 888, '17611111111', '333333333@qq.com', 1),
|
||||
('00000000-0000-0000-0000-000000000002', 'a303176530', '', '用户1', 'https://qmplusimg.henrongyi.top/1572075907logo.png', 9528, '17611111111', '333333333@qq.com', 1);
|
||||
|
||||
|
||||
/* =====================================================
|
||||
* 3. 字典表 & 字典详情表
|
||||
* sys_dictionaries, sys_dictionary_details
|
||||
* ===================================================== */
|
||||
|
||||
-- 3.1 字典表 sys_dictionaries
|
||||
INSERT INTO sys_dictionaries (name, type, status, "desc")
|
||||
VALUES
|
||||
('性别', 'gender', TRUE, '性别字典'),
|
||||
('数据库int类型', 'int', TRUE, 'int类型对应的数据库类型'),
|
||||
('数据库时间日期类型', 'time.Time', TRUE, '数据库时间日期类型'),
|
||||
('数据库浮点型', 'float64', TRUE, '数据库浮点型'),
|
||||
('数据库字符串', 'string', TRUE, '数据库字符串'),
|
||||
('数据库bool类型', 'bool', TRUE, '数据库bool类型');
|
||||
|
||||
-- 3.2 字典详情表 sys_dictionary_details
|
||||
-- 使用 INSERT ... SELECT,通过 type 反查 sys_dictionaries.id
|
||||
|
||||
-- 性别 gender
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT
|
||||
'男' AS label, '1' AS value, '' AS extend, TRUE AS status, 1 AS sort,
|
||||
d.id AS sys_dictionary_id, NULL::integer AS parent_id, 0 AS level, '' AS path
|
||||
FROM sys_dictionaries d WHERE d.type = 'gender';
|
||||
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT
|
||||
'女', '2', '', TRUE, 2,
|
||||
d.id, NULL::integer, 0, ''
|
||||
FROM sys_dictionaries d WHERE d.type = 'gender';
|
||||
|
||||
-- int
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'smallint', '1', 'mysql', TRUE, 1, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'int';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'mediumint', '2', 'mysql', TRUE, 2, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'int';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'int', '3', 'mysql', TRUE, 3, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'int';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'bigint', '4', 'mysql', TRUE, 4, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'int';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'int2', '5', 'pgsql', TRUE, 5, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'int';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'int4', '6', 'pgsql', TRUE, 6, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'int';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'int6', '7', 'pgsql', TRUE, 7, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'int';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'int8', '8', 'pgsql', TRUE, 8, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'int';
|
||||
|
||||
-- time.Time
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'date', '0', 'mysql', TRUE, 0, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'time.Time';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'time', '1', 'mysql', TRUE, 1, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'time.Time';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'year', '2', 'mysql', TRUE, 2, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'time.Time';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'datetime', '3', 'mysql', TRUE, 3, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'time.Time';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'timestamp', '5', 'mysql', TRUE, 5, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'time.Time';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'timestamptz', '6', 'pgsql', TRUE, 5, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'time.Time';
|
||||
|
||||
-- float64
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'float', '0', 'mysql', TRUE, 0, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'float64';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'double', '1', 'mysql', TRUE, 1, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'float64';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'decimal', '2', 'mysql', TRUE, 2, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'float64';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'numeric', '3', 'pgsql', TRUE, 3, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'float64';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'smallserial', '4', 'pgsql', TRUE, 4, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'float64';
|
||||
|
||||
-- string
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'char', '0', 'mysql', TRUE, 0, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'varchar', '1', 'mysql', TRUE, 1, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'tinyblob', '2', 'mysql', TRUE, 2, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'tinytext', '3', 'mysql', TRUE, 3, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'text', '4', 'mysql', TRUE, 4, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'blob', '5', 'mysql', TRUE, 5, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'mediumblob', '6', 'mysql', TRUE, 6, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'mediumtext', '7', 'mysql', TRUE, 7, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'longblob', '8', 'mysql', TRUE, 8, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'longtext', '9', 'mysql', TRUE, 9, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'string';
|
||||
|
||||
-- bool
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'tinyint', '1', 'mysql', TRUE, 0, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'bool';
|
||||
INSERT INTO sys_dictionary_details (label, value, extend, status, sort, sys_dictionary_id, parent_id, level, path)
|
||||
SELECT 'bool', '2', 'pgsql', TRUE, 0, d.id, NULL::integer, 0, '' FROM sys_dictionaries d WHERE d.type = 'bool';
|
||||
|
||||
|
||||
/* =========================
|
||||
* 4. API 表 sys_apis
|
||||
* ========================= */
|
||||
|
||||
INSERT INTO sys_apis (api_group, method, path, description)
|
||||
VALUES
|
||||
('jwt', 'POST', '/jwt/jsonInBlacklist', 'jwt加入黑名单(退出,必选)'),
|
||||
|
||||
('登录日志', 'DELETE', '/sysLoginLog/deleteLoginLog', '删除登录日志'),
|
||||
('登录日志', 'DELETE', '/sysLoginLog/deleteLoginLogByIds', '批量删除登录日志'),
|
||||
('登录日志', 'GET', '/sysLoginLog/findLoginLog', '根据ID获取登录日志'),
|
||||
('登录日志', 'GET', '/sysLoginLog/getLoginLogList', '获取登录日志列表'),
|
||||
|
||||
('API Token', 'POST', '/sysApiToken/createApiToken', '签发API Token'),
|
||||
('API Token', 'POST', '/sysApiToken/getApiTokenList', '获取API Token列表'),
|
||||
('API Token', 'POST', '/sysApiToken/deleteApiToken', '作废API Token'),
|
||||
|
||||
('系统用户', 'DELETE', '/user/deleteUser', '删除用户'),
|
||||
('系统用户', 'POST', '/user/admin_register', '用户注册'),
|
||||
('系统用户', 'POST', '/user/getUserList', '获取用户列表'),
|
||||
('系统用户', 'PUT', '/user/setUserInfo', '设置用户信息'),
|
||||
('系统用户', 'PUT', '/user/setSelfInfo', '设置自身信息(必选)'),
|
||||
('系统用户', 'GET', '/user/getUserInfo', '获取自身信息(必选)'),
|
||||
('系统用户', 'POST', '/user/setUserAuthorities', '设置权限组'),
|
||||
('系统用户', 'POST', '/user/changePassword', '修改密码(建议选择)'),
|
||||
('系统用户', 'POST', '/user/setUserAuthority', '修改用户角色(必选)'),
|
||||
('系统用户', 'POST', '/user/resetPassword', '重置用户密码'),
|
||||
('系统用户', 'PUT', '/user/setSelfSetting', '用户界面配置'),
|
||||
|
||||
('api', 'POST', '/api/createApi', '创建api'),
|
||||
('api', 'POST', '/api/deleteApi', '删除Api'),
|
||||
('api', 'POST', '/api/updateApi', '更新Api'),
|
||||
('api', 'POST', '/api/getApiList', '获取api列表'),
|
||||
('api', 'POST', '/api/getAllApis', '获取所有api'),
|
||||
('api', 'POST', '/api/getApiById', '获取api详细信息'),
|
||||
('api', 'DELETE', '/api/deleteApisByIds', '批量删除api'),
|
||||
('api', 'GET', '/api/syncApi', '获取待同步API'),
|
||||
('api', 'GET', '/api/getApiGroups', '获取路由组'),
|
||||
('api', 'POST', '/api/enterSyncApi', '确认同步API'),
|
||||
('api', 'POST', '/api/ignoreApi', '忽略API'),
|
||||
|
||||
('角色', 'POST', '/authority/copyAuthority', '拷贝角色'),
|
||||
('角色', 'POST', '/authority/createAuthority', '创建角色'),
|
||||
('角色', 'POST', '/authority/deleteAuthority', '删除角色'),
|
||||
('角色', 'PUT', '/authority/updateAuthority', '更新角色信息'),
|
||||
('角色', 'POST', '/authority/getAuthorityList', '获取角色列表'),
|
||||
('角色', 'POST', '/authority/setDataAuthority', '设置角色资源权限'),
|
||||
|
||||
('casbin', 'POST', '/casbin/updateCasbin', '更改角色api权限'),
|
||||
('casbin', 'POST', '/casbin/getPolicyPathByAuthorityId', '获取权限列表'),
|
||||
|
||||
('菜单', 'POST', '/menu/addBaseMenu', '新增菜单'),
|
||||
('菜单', 'POST', '/menu/getMenu', '获取菜单树(必选)'),
|
||||
('菜单', 'POST', '/menu/deleteBaseMenu', '删除菜单'),
|
||||
('菜单', 'POST', '/menu/updateBaseMenu', '更新菜单'),
|
||||
('菜单', 'POST', '/menu/getBaseMenuById', '根据id获取菜单'),
|
||||
('菜单', 'POST', '/menu/getMenuList', '分页获取基础menu列表'),
|
||||
('菜单', 'POST', '/menu/getBaseMenuTree', '获取用户动态路由'),
|
||||
('菜单', 'POST', '/menu/getMenuAuthority', '获取指定角色menu'),
|
||||
('菜单', 'POST', '/menu/addMenuAuthority', '增加menu和角色关联关系'),
|
||||
|
||||
('分片上传', 'GET', '/fileUploadAndDownload/findFile', '寻找目标文件(秒传)'),
|
||||
('分片上传', 'POST', '/fileUploadAndDownload/breakpointContinue', '断点续传'),
|
||||
('分片上传', 'POST', '/fileUploadAndDownload/breakpointContinueFinish', '断点续传完成'),
|
||||
('分片上传', 'POST', '/fileUploadAndDownload/removeChunk', '上传完成移除文件'),
|
||||
|
||||
('文件上传与下载', 'POST', '/fileUploadAndDownload/upload', '文件上传(建议选择)'),
|
||||
('文件上传与下载', 'POST', '/fileUploadAndDownload/deleteFile', '删除文件'),
|
||||
('文件上传与下载', 'POST', '/fileUploadAndDownload/editFileName', '文件名或者备注编辑'),
|
||||
('文件上传与下载', 'POST', '/fileUploadAndDownload/getFileList', '获取上传文件列表'),
|
||||
('文件上传与下载', 'POST', '/fileUploadAndDownload/importURL', '导入URL'),
|
||||
|
||||
('系统服务', 'POST', '/system/getServerInfo', '获取服务器信息'),
|
||||
('系统服务', 'POST', '/system/getSystemConfig', '获取配置文件内容'),
|
||||
('系统服务', 'POST', '/system/setSystemConfig', '设置配置文件内容'),
|
||||
|
||||
('skills', 'GET', '/skills/getTools', '获取技能工具列表'),
|
||||
('skills', 'POST', '/skills/getSkillList', '获取技能列表'),
|
||||
('skills', 'POST', '/skills/getSkillDetail', '获取技能详情'),
|
||||
('skills', 'POST', '/skills/saveSkill', '保存技能定义'),
|
||||
('skills', 'POST', '/skills/createScript', '创建技能脚本'),
|
||||
('skills', 'POST', '/skills/getScript', '读取技能脚本'),
|
||||
('skills', 'POST', '/skills/saveScript', '保存技能脚本'),
|
||||
('skills', 'POST', '/skills/createResource', '创建技能资源'),
|
||||
('skills', 'POST', '/skills/getResource', '读取技能资源'),
|
||||
('skills', 'POST', '/skills/saveResource', '保存技能资源'),
|
||||
('skills', 'POST', '/skills/createReference', '创建技能参考'),
|
||||
('skills', 'POST', '/skills/getReference', '读取技能参考'),
|
||||
('skills', 'POST', '/skills/saveReference', '保存技能参考'),
|
||||
('skills', 'POST', '/skills/createTemplate', '创建技能模板'),
|
||||
('skills', 'POST', '/skills/getTemplate', '读取技能模板'),
|
||||
('skills', 'POST', '/skills/saveTemplate', '保存技能模板'),
|
||||
('skills', 'POST', '/skills/getGlobalConstraint', '读取全局约束'),
|
||||
('skills', 'POST', '/skills/saveGlobalConstraint', '保存全局约束'),
|
||||
|
||||
('客户', 'PUT', '/customer/customer', '更新客户'),
|
||||
('客户', 'POST', '/customer/customer', '创建客户'),
|
||||
('客户', 'DELETE','/customer/customer', '删除客户'),
|
||||
('客户', 'GET', '/customer/customer', '获取单一客户'),
|
||||
('客户', 'GET', '/customer/customerList', '获取客户列表'),
|
||||
|
||||
('代码生成器', 'GET', '/autoCode/getDB', '获取所有数据库'),
|
||||
('代码生成器', 'GET', '/autoCode/getTables', '获取数据库表'),
|
||||
('代码生成器', 'POST', '/autoCode/createTemp', '自动化代码'),
|
||||
('代码生成器', 'POST', '/autoCode/preview', '预览自动化代码'),
|
||||
('代码生成器', 'GET', '/autoCode/getColumn', '获取所选table的所有字段'),
|
||||
('代码生成器', 'POST', '/autoCode/installPlugin', '安装插件'),
|
||||
('代码生成器', 'POST', '/autoCode/pubPlug', '打包插件'),
|
||||
('代码生成器', 'POST', '/autoCode/removePlugin', '卸载插件'),
|
||||
('代码生成器', 'GET', '/autoCode/getPluginList', '获取已安装插件'),
|
||||
('代码生成器', 'POST', '/autoCode/mcp', '自动生成 MCP Tool 模板'),
|
||||
('代码生成器', 'POST', '/autoCode/mcpTest', 'MCP Tool 测试'),
|
||||
('代码生成器', 'POST', '/autoCode/mcpList', '获取 MCP ToolList'),
|
||||
|
||||
('模板配置', 'POST', '/autoCode/createPackage', '配置模板'),
|
||||
('模板配置', 'GET', '/autoCode/getTemplates', '获取模板文件'),
|
||||
('模板配置', 'POST', '/autoCode/getPackage', '获取所有模板'),
|
||||
('模板配置', 'POST', '/autoCode/delPackage', '删除模板'),
|
||||
|
||||
('代码生成器历史', 'POST', '/autoCode/getMeta', '获取meta信息'),
|
||||
('代码生成器历史', 'POST', '/autoCode/rollback', '回滚自动生成代码'),
|
||||
('代码生成器历史', 'POST', '/autoCode/getSysHistory', '查询回滚记录'),
|
||||
('代码生成器历史', 'POST', '/autoCode/delSysHistory', '删除回滚记录'),
|
||||
('代码生成器历史', 'POST', '/autoCode/addFunc', '增加模板方法'),
|
||||
|
||||
('系统字典详情', 'PUT', '/sysDictionaryDetail/updateSysDictionaryDetail', '更新字典内容'),
|
||||
('系统字典详情', 'POST', '/sysDictionaryDetail/createSysDictionaryDetail', '新增字典内容'),
|
||||
('系统字典详情', 'DELETE','/sysDictionaryDetail/deleteSysDictionaryDetail', '删除字典内容'),
|
||||
('系统字典详情', 'GET', '/sysDictionaryDetail/findSysDictionaryDetail', '根据ID获取字典内容'),
|
||||
('系统字典详情', 'GET', '/sysDictionaryDetail/getSysDictionaryDetailList', '获取字典内容列表'),
|
||||
('系统字典详情', 'GET', '/sysDictionaryDetail/getDictionaryTreeList', '获取字典数列表'),
|
||||
('系统字典详情', 'GET', '/sysDictionaryDetail/getDictionaryTreeListByType', '根据分类获取字典数列表'),
|
||||
('系统字典详情', 'GET', '/sysDictionaryDetail/getDictionaryDetailsByParent', '根据父级ID获取字典详情'),
|
||||
('系统字典详情', 'GET', '/sysDictionaryDetail/getDictionaryPath', '获取字典详情的完整路径'),
|
||||
|
||||
('系统字典', 'POST', '/sysDictionary/createSysDictionary', '新增字典'),
|
||||
('系统字典', 'DELETE','/sysDictionary/deleteSysDictionary', '删除字典'),
|
||||
('系统字典', 'PUT', '/sysDictionary/updateSysDictionary', '更新字典'),
|
||||
('系统字典', 'GET', '/sysDictionary/findSysDictionary', '根据ID获取字典(建议选择)'),
|
||||
('系统字典', 'GET', '/sysDictionary/getSysDictionaryList', '获取字典列表'),
|
||||
('系统字典', 'POST', '/sysDictionary/importSysDictionary', '导入字典JSON'),
|
||||
('系统字典', 'GET', '/sysDictionary/exportSysDictionary', '导出字典JSON'),
|
||||
|
||||
('操作记录', 'POST', '/sysOperationRecord/createSysOperationRecord', '新增操作记录'),
|
||||
('操作记录', 'GET', '/sysOperationRecord/findSysOperationRecord', '根据ID获取操作记录'),
|
||||
('操作记录', 'GET', '/sysOperationRecord/getSysOperationRecordList', '获取操作记录列表'),
|
||||
('操作记录', 'DELETE','/sysOperationRecord/deleteSysOperationRecord', '删除操作记录'),
|
||||
('操作记录', 'DELETE','/sysOperationRecord/deleteSysOperationRecordByIds', '批量删除操作历史'),
|
||||
|
||||
('断点续传(插件版)', 'POST', '/simpleUploader/upload', '插件版分片上传'),
|
||||
('断点续传(插件版)', 'GET', '/simpleUploader/checkFileMd5', '文件完整度验证'),
|
||||
('断点续传(插件版)', 'GET', '/simpleUploader/mergeFileMd5', '上传完成合并文件'),
|
||||
|
||||
('email', 'POST', '/email/emailTest', '发送测试邮件'),
|
||||
('email', 'POST', '/email/sendEmail', '发送邮件'),
|
||||
|
||||
('按钮权限', 'POST', '/authorityBtn/setAuthorityBtn', '设置按钮权限'),
|
||||
('按钮权限', 'POST', '/authorityBtn/getAuthorityBtn', '获取已有按钮权限'),
|
||||
('按钮权限', 'POST', '/authorityBtn/canRemoveAuthorityBtn', '删除按钮'),
|
||||
|
||||
('导出模板', 'POST', '/sysExportTemplate/createSysExportTemplate', '新增导出模板'),
|
||||
('导出模板', 'DELETE','/sysExportTemplate/deleteSysExportTemplate', '删除导出模板'),
|
||||
('导出模板', 'DELETE','/sysExportTemplate/deleteSysExportTemplateByIds', '批量删除导出模板'),
|
||||
('导出模板', 'PUT', '/sysExportTemplate/updateSysExportTemplate', '更新导出模板'),
|
||||
('导出模板', 'GET', '/sysExportTemplate/findSysExportTemplate', '根据ID获取导出模板'),
|
||||
('导出模板', 'GET', '/sysExportTemplate/getSysExportTemplateList', '获取导出模板列表'),
|
||||
('导出模板', 'GET', '/sysExportTemplate/exportExcel', '导出Excel'),
|
||||
('导出模板', 'GET', '/sysExportTemplate/exportTemplate', '下载模板'),
|
||||
('导出模板', 'GET', '/sysExportTemplate/previewSQL', '预览SQL'),
|
||||
('导出模板', 'POST', '/sysExportTemplate/importExcel', '导入Excel'),
|
||||
|
||||
('错误日志', 'POST', '/sysError/createSysError', '新建错误日志'),
|
||||
('错误日志', 'DELETE','/sysError/deleteSysError', '删除错误日志'),
|
||||
('错误日志', 'DELETE','/sysError/deleteSysErrorByIds', '批量删除错误日志'),
|
||||
('错误日志', 'PUT', '/sysError/updateSysError', '更新错误日志'),
|
||||
('错误日志', 'GET', '/sysError/findSysError', '根据ID获取错误日志'),
|
||||
('错误日志', 'GET', '/sysError/getSysErrorList', '获取错误日志列表'),
|
||||
('错误日志', 'GET', '/sysError/getSysErrorSolution', '触发错误处理(异步)'),
|
||||
|
||||
('公告', 'POST', '/info/createInfo', '新建公告'),
|
||||
('公告', 'DELETE','/info/deleteInfo', '删除公告'),
|
||||
('公告', 'DELETE','/info/deleteInfoByIds', '批量删除公告'),
|
||||
('公告', 'PUT', '/info/updateInfo', '更新公告'),
|
||||
('公告', 'GET', '/info/findInfo', '根据ID获取公告'),
|
||||
('公告', 'GET', '/info/getInfoList', '获取公告列表'),
|
||||
|
||||
('参数管理', 'POST', '/sysParams/createSysParams', '新建参数'),
|
||||
('参数管理', 'DELETE','/sysParams/deleteSysParams', '删除参数'),
|
||||
('参数管理', 'DELETE','/sysParams/deleteSysParamsByIds', '批量删除参数'),
|
||||
('参数管理', 'PUT', '/sysParams/updateSysParams', '更新参数'),
|
||||
('参数管理', 'GET', '/sysParams/findSysParams', '根据ID获取参数'),
|
||||
('参数管理', 'GET', '/sysParams/getSysParamsList', '获取参数列表'),
|
||||
('参数管理', 'GET', '/sysParams/getSysParam', '获取参数列表'),
|
||||
|
||||
('媒体库分类', 'GET', '/attachmentCategory/getCategoryList', '分类列表'),
|
||||
('媒体库分类', 'POST', '/attachmentCategory/addCategory', '添加/编辑分类'),
|
||||
('媒体库分类', 'POST', '/attachmentCategory/deleteCategory', '删除分类'),
|
||||
|
||||
('版本控制', 'GET', '/sysVersion/findSysVersion', '获取单一版本'),
|
||||
('版本控制', 'GET', '/sysVersion/getSysVersionList', '获取版本列表'),
|
||||
('版本控制', 'GET', '/sysVersion/downloadVersionJson', '下载版本json'),
|
||||
('版本控制', 'POST', '/sysVersion/exportVersion', '创建版本'),
|
||||
('版本控制', 'POST', '/sysVersion/importVersion', '同步版本'),
|
||||
('版本控制', 'DELETE','/sysVersion/deleteSysVersion', '删除版本'),
|
||||
('版本控制', 'DELETE','/sysVersion/deleteSysVersionByIds', '批量删除版本');
|
||||
|
||||
|
||||
/* =========================
|
||||
* 5. 忽略 API 表 sys_ignore_apis
|
||||
* ========================= */
|
||||
|
||||
INSERT INTO sys_ignore_apis (method, path)
|
||||
VALUES
|
||||
('GET', '/swagger/*any'),
|
||||
('GET', '/api/freshCasbin'),
|
||||
('GET', '/uploads/file/*filepath'),
|
||||
('GET', '/health'),
|
||||
('HEAD', '/uploads/file/*filepath'),
|
||||
('POST', '/autoCode/llmAuto'),
|
||||
('POST', '/system/reloadSystem'),
|
||||
('POST', '/base/login'),
|
||||
('POST', '/base/captcha'),
|
||||
('POST', '/init/initdb'),
|
||||
('POST', '/init/checkdb'),
|
||||
('GET', '/info/getInfoDataSource'),
|
||||
('GET', '/info/getInfoPublic');
|
||||
|
||||
|
||||
/* =========================
|
||||
* 6. Casbin 规则表 casbin_rules
|
||||
* ========================= */
|
||||
|
||||
INSERT INTO casbin_rules (ptype, v0, v1, v2)
|
||||
VALUES
|
||||
-- 888
|
||||
('p', '888', '/user/admin_register', 'POST'),
|
||||
|
||||
('p', '888', '/sysLoginLog/deleteLoginLog', 'DELETE'),
|
||||
('p', '888', '/sysLoginLog/deleteLoginLogByIds', 'DELETE'),
|
||||
('p', '888', '/sysLoginLog/findLoginLog', 'GET'),
|
||||
('p', '888', '/sysLoginLog/getLoginLogList', 'GET'),
|
||||
|
||||
('p', '888', '/sysApiToken/createApiToken', 'POST'),
|
||||
('p', '888', '/sysApiToken/getApiTokenList', 'POST'),
|
||||
('p', '888', '/sysApiToken/deleteApiToken', 'POST'),
|
||||
|
||||
('p', '888', '/api/createApi', 'POST'),
|
||||
('p', '888', '/api/getApiList', 'POST'),
|
||||
('p', '888', '/api/getApiById', 'POST'),
|
||||
('p', '888', '/api/deleteApi', 'POST'),
|
||||
('p', '888', '/api/updateApi', 'POST'),
|
||||
('p', '888', '/api/getAllApis', 'POST'),
|
||||
('p', '888', '/api/deleteApisByIds', 'DELETE'),
|
||||
('p', '888', '/api/syncApi', 'GET'),
|
||||
('p', '888', '/api/getApiGroups', 'GET'),
|
||||
('p', '888', '/api/enterSyncApi', 'POST'),
|
||||
('p', '888', '/api/ignoreApi', 'POST'),
|
||||
|
||||
('p', '888', '/authority/copyAuthority', 'POST'),
|
||||
('p', '888', '/authority/updateAuthority', 'PUT'),
|
||||
('p', '888', '/authority/createAuthority', 'POST'),
|
||||
('p', '888', '/authority/deleteAuthority', 'POST'),
|
||||
('p', '888', '/authority/getAuthorityList', 'POST'),
|
||||
('p', '888', '/authority/setDataAuthority', 'POST'),
|
||||
|
||||
('p', '888', '/menu/getMenu', 'POST'),
|
||||
('p', '888', '/menu/getMenuList', 'POST'),
|
||||
('p', '888', '/menu/addBaseMenu', 'POST'),
|
||||
('p', '888', '/menu/getBaseMenuTree', 'POST'),
|
||||
('p', '888', '/menu/addMenuAuthority', 'POST'),
|
||||
('p', '888', '/menu/getMenuAuthority', 'POST'),
|
||||
('p', '888', '/menu/deleteBaseMenu', 'POST'),
|
||||
('p', '888', '/menu/updateBaseMenu', 'POST'),
|
||||
('p', '888', '/menu/getBaseMenuById', 'POST'),
|
||||
|
||||
('p', '888', '/user/getUserInfo', 'GET'),
|
||||
('p', '888', '/user/setUserInfo', 'PUT'),
|
||||
('p', '888', '/user/setSelfInfo', 'PUT'),
|
||||
('p', '888', '/user/getUserList', 'POST'),
|
||||
('p', '888', '/user/deleteUser', 'DELETE'),
|
||||
('p', '888', '/user/changePassword', 'POST'),
|
||||
('p', '888', '/user/setUserAuthority', 'POST'),
|
||||
('p', '888', '/user/setUserAuthorities', 'POST'),
|
||||
('p', '888', '/user/resetPassword', 'POST'),
|
||||
('p', '888', '/user/setSelfSetting', 'PUT'),
|
||||
|
||||
('p', '888', '/fileUploadAndDownload/findFile', 'GET'),
|
||||
('p', '888', '/fileUploadAndDownload/breakpointContinueFinish', 'POST'),
|
||||
('p', '888', '/fileUploadAndDownload/breakpointContinue', 'POST'),
|
||||
('p', '888', '/fileUploadAndDownload/removeChunk', 'POST'),
|
||||
|
||||
('p', '888', '/fileUploadAndDownload/upload', 'POST'),
|
||||
('p', '888', '/fileUploadAndDownload/deleteFile', 'POST'),
|
||||
('p', '888', '/fileUploadAndDownload/editFileName', 'POST'),
|
||||
('p', '888', '/fileUploadAndDownload/getFileList', 'POST'),
|
||||
('p', '888', '/fileUploadAndDownload/importURL', 'POST'),
|
||||
|
||||
('p', '888', '/casbin/updateCasbin', 'POST'),
|
||||
('p', '888', '/casbin/getPolicyPathByAuthorityId', 'POST'),
|
||||
|
||||
('p', '888', '/jwt/jsonInBlacklist', 'POST'),
|
||||
|
||||
('p', '888', '/system/getSystemConfig', 'POST'),
|
||||
('p', '888', '/system/setSystemConfig', 'POST'),
|
||||
('p', '888', '/system/getServerInfo', 'POST'),
|
||||
|
||||
('p', '888', '/skills/getTools', 'GET'),
|
||||
('p', '888', '/skills/getSkillList', 'POST'),
|
||||
('p', '888', '/skills/getSkillDetail', 'POST'),
|
||||
('p', '888', '/skills/saveSkill', 'POST'),
|
||||
('p', '888', '/skills/createScript', 'POST'),
|
||||
('p', '888', '/skills/getScript', 'POST'),
|
||||
('p', '888', '/skills/saveScript', 'POST'),
|
||||
('p', '888', '/skills/createResource', 'POST'),
|
||||
('p', '888', '/skills/getResource', 'POST'),
|
||||
('p', '888', '/skills/saveResource', 'POST'),
|
||||
('p', '888', '/skills/createReference', 'POST'),
|
||||
('p', '888', '/skills/getReference', 'POST'),
|
||||
('p', '888', '/skills/saveReference', 'POST'),
|
||||
('p', '888', '/skills/createTemplate', 'POST'),
|
||||
('p', '888', '/skills/getTemplate', 'POST'),
|
||||
('p', '888', '/skills/saveTemplate', 'POST'),
|
||||
('p', '888', '/skills/getGlobalConstraint', 'POST'),
|
||||
('p', '888', '/skills/saveGlobalConstraint', 'POST'),
|
||||
|
||||
('p', '888', '/customer/customer', 'GET'),
|
||||
('p', '888', '/customer/customer', 'PUT'),
|
||||
('p', '888', '/customer/customer', 'POST'),
|
||||
('p', '888', '/customer/customer', 'DELETE'),
|
||||
('p', '888', '/customer/customerList', 'GET'),
|
||||
|
||||
('p', '888', '/autoCode/getDB', 'GET'),
|
||||
('p', '888', '/autoCode/getMeta', 'POST'),
|
||||
('p', '888', '/autoCode/preview', 'POST'),
|
||||
('p', '888', '/autoCode/getTables', 'GET'),
|
||||
('p', '888', '/autoCode/getColumn', 'GET'),
|
||||
('p', '888', '/autoCode/rollback', 'POST'),
|
||||
('p', '888', '/autoCode/createTemp', 'POST'),
|
||||
('p', '888', '/autoCode/delSysHistory', 'POST'),
|
||||
('p', '888', '/autoCode/getSysHistory', 'POST'),
|
||||
('p', '888', '/autoCode/createPackage', 'POST'),
|
||||
('p', '888', '/autoCode/getTemplates', 'GET'),
|
||||
('p', '888', '/autoCode/getPackage', 'POST'),
|
||||
('p', '888', '/autoCode/delPackage', 'POST'),
|
||||
('p', '888', '/autoCode/createPlug', 'POST'),
|
||||
('p', '888', '/autoCode/installPlugin', 'POST'),
|
||||
('p', '888', '/autoCode/pubPlug', 'POST'),
|
||||
('p', '888', '/autoCode/removePlugin', 'POST'),
|
||||
('p', '888', '/autoCode/getPluginList', 'GET'),
|
||||
('p', '888', '/autoCode/addFunc', 'POST'),
|
||||
('p', '888', '/autoCode/mcp', 'POST'),
|
||||
('p', '888', '/autoCode/mcpTest', 'POST'),
|
||||
('p', '888', '/autoCode/mcpList', 'POST'),
|
||||
|
||||
('p', '888', '/sysDictionaryDetail/findSysDictionaryDetail', 'GET'),
|
||||
('p', '888', '/sysDictionaryDetail/updateSysDictionaryDetail', 'PUT'),
|
||||
('p', '888', '/sysDictionaryDetail/createSysDictionaryDetail', 'POST'),
|
||||
('p', '888', '/sysDictionaryDetail/getSysDictionaryDetailList', 'GET'),
|
||||
('p', '888', '/sysDictionaryDetail/deleteSysDictionaryDetail', 'DELETE'),
|
||||
('p', '888', '/sysDictionaryDetail/getDictionaryTreeList', 'GET'),
|
||||
('p', '888', '/sysDictionaryDetail/getDictionaryTreeListByType', 'GET'),
|
||||
('p', '888', '/sysDictionaryDetail/getDictionaryDetailsByParent', 'GET'),
|
||||
('p', '888', '/sysDictionaryDetail/getDictionaryPath', 'GET'),
|
||||
|
||||
('p', '888', '/sysDictionary/findSysDictionary', 'GET'),
|
||||
('p', '888', '/sysDictionary/updateSysDictionary', 'PUT'),
|
||||
('p', '888', '/sysDictionary/getSysDictionaryList', 'GET'),
|
||||
('p', '888', '/sysDictionary/createSysDictionary', 'POST'),
|
||||
('p', '888', '/sysDictionary/deleteSysDictionary', 'DELETE'),
|
||||
('p', '888', '/sysDictionary/importSysDictionary', 'POST'),
|
||||
('p', '888', '/sysDictionary/exportSysDictionary', 'GET'),
|
||||
|
||||
('p', '888', '/sysOperationRecord/findSysOperationRecord', 'GET'),
|
||||
('p', '888', '/sysOperationRecord/updateSysOperationRecord', 'PUT'),
|
||||
('p', '888', '/sysOperationRecord/createSysOperationRecord', 'POST'),
|
||||
('p', '888', '/sysOperationRecord/getSysOperationRecordList', 'GET'),
|
||||
('p', '888', '/sysOperationRecord/deleteSysOperationRecord', 'DELETE'),
|
||||
('p', '888', '/sysOperationRecord/deleteSysOperationRecordByIds', 'DELETE'),
|
||||
|
||||
('p', '888', '/email/emailTest', 'POST'),
|
||||
('p', '888', '/email/sendEmail', 'POST'),
|
||||
|
||||
('p', '888', '/simpleUploader/upload', 'POST'),
|
||||
('p', '888', '/simpleUploader/checkFileMd5', 'GET'),
|
||||
('p', '888', '/simpleUploader/mergeFileMd5', 'GET'),
|
||||
|
||||
('p', '888', '/authorityBtn/setAuthorityBtn', 'POST'),
|
||||
('p', '888', '/authorityBtn/getAuthorityBtn', 'POST'),
|
||||
('p', '888', '/authorityBtn/canRemoveAuthorityBtn', 'POST'),
|
||||
|
||||
('p', '888', '/sysExportTemplate/createSysExportTemplate', 'POST'),
|
||||
('p', '888', '/sysExportTemplate/deleteSysExportTemplate', 'DELETE'),
|
||||
('p', '888', '/sysExportTemplate/deleteSysExportTemplateByIds', 'DELETE'),
|
||||
('p', '888', '/sysExportTemplate/updateSysExportTemplate', 'PUT'),
|
||||
('p', '888', '/sysExportTemplate/findSysExportTemplate', 'GET'),
|
||||
('p', '888', '/sysExportTemplate/getSysExportTemplateList', 'GET'),
|
||||
('p', '888', '/sysExportTemplate/exportExcel', 'GET'),
|
||||
('p', '888', '/sysExportTemplate/exportTemplate', 'GET'),
|
||||
('p', '888', '/sysExportTemplate/previewSQL', 'GET'),
|
||||
('p', '888', '/sysExportTemplate/importExcel', 'POST'),
|
||||
|
||||
('p', '888', '/sysError/createSysError', 'POST'),
|
||||
('p', '888', '/sysError/deleteSysError', 'DELETE'),
|
||||
('p', '888', '/sysError/deleteSysErrorByIds', 'DELETE'),
|
||||
('p', '888', '/sysError/updateSysError', 'PUT'),
|
||||
('p', '888', '/sysError/findSysError', 'GET'),
|
||||
('p', '888', '/sysError/getSysErrorList', 'GET'),
|
||||
('p', '888', '/sysError/getSysErrorSolution', 'GET'),
|
||||
|
||||
('p', '888', '/info/createInfo', 'POST'),
|
||||
('p', '888', '/info/deleteInfo', 'DELETE'),
|
||||
('p', '888', '/info/deleteInfoByIds', 'DELETE'),
|
||||
('p', '888', '/info/updateInfo', 'PUT'),
|
||||
('p', '888', '/info/findInfo', 'GET'),
|
||||
('p', '888', '/info/getInfoList', 'GET'),
|
||||
|
||||
('p', '888', '/sysParams/createSysParams', 'POST'),
|
||||
('p', '888', '/sysParams/deleteSysParams', 'DELETE'),
|
||||
('p', '888', '/sysParams/deleteSysParamsByIds', 'DELETE'),
|
||||
('p', '888', '/sysParams/updateSysParams', 'PUT'),
|
||||
('p', '888', '/sysParams/findSysParams', 'GET'),
|
||||
('p', '888', '/sysParams/getSysParamsList', 'GET'),
|
||||
('p', '888', '/sysParams/getSysParam', 'GET'),
|
||||
('p', '888', '/attachmentCategory/getCategoryList', 'GET'),
|
||||
('p', '888', '/attachmentCategory/addCategory', 'POST'),
|
||||
('p', '888', '/attachmentCategory/deleteCategory', 'POST'),
|
||||
|
||||
('p', '888', '/sysVersion/findSysVersion', 'GET'),
|
||||
('p', '888', '/sysVersion/getSysVersionList', 'GET'),
|
||||
('p', '888', '/sysVersion/downloadVersionJson', 'GET'),
|
||||
('p', '888', '/sysVersion/exportVersion', 'POST'),
|
||||
('p', '888', '/sysVersion/importVersion', 'POST'),
|
||||
('p', '888', '/sysVersion/deleteSysVersion', 'DELETE'),
|
||||
('p', '888', '/sysVersion/deleteSysVersionByIds', 'DELETE'),
|
||||
|
||||
-- 8881
|
||||
('p', '8881', '/user/admin_register', 'POST'),
|
||||
('p', '8881', '/api/createApi', 'POST'),
|
||||
('p', '8881', '/api/getApiList', 'POST'),
|
||||
('p', '8881', '/api/getApiById', 'POST'),
|
||||
('p', '8881', '/api/deleteApi', 'POST'),
|
||||
('p', '8881', '/api/updateApi', 'POST'),
|
||||
('p', '8881', '/api/getAllApis', 'POST'),
|
||||
('p', '8881', '/authority/createAuthority', 'POST'),
|
||||
('p', '8881', '/authority/deleteAuthority', 'POST'),
|
||||
('p', '8881', '/authority/getAuthorityList', 'POST'),
|
||||
('p', '8881', '/authority/setDataAuthority', 'POST'),
|
||||
('p', '8881', '/menu/getMenu', 'POST'),
|
||||
('p', '8881', '/menu/getMenuList', 'POST'),
|
||||
('p', '8881', '/menu/addBaseMenu', 'POST'),
|
||||
('p', '8881', '/menu/getBaseMenuTree', 'POST'),
|
||||
('p', '8881', '/menu/addMenuAuthority', 'POST'),
|
||||
('p', '8881', '/menu/getMenuAuthority', 'POST'),
|
||||
('p', '8881', '/menu/deleteBaseMenu', 'POST'),
|
||||
('p', '8881', '/menu/updateBaseMenu', 'POST'),
|
||||
('p', '8881', '/menu/getBaseMenuById', 'POST'),
|
||||
('p', '8881', '/user/changePassword', 'POST'),
|
||||
('p', '8881', '/user/getUserList', 'POST'),
|
||||
('p', '8881', '/user/setUserAuthority', 'POST'),
|
||||
('p', '8881', '/fileUploadAndDownload/upload', 'POST'),
|
||||
('p', '8881', '/fileUploadAndDownload/getFileList', 'POST'),
|
||||
('p', '8881', '/fileUploadAndDownload/deleteFile', 'POST'),
|
||||
('p', '8881', '/fileUploadAndDownload/editFileName', 'POST'),
|
||||
('p', '8881', '/fileUploadAndDownload/importURL', 'POST'),
|
||||
('p', '8881', '/casbin/updateCasbin', 'POST'),
|
||||
('p', '8881', '/casbin/getPolicyPathByAuthorityId', 'POST'),
|
||||
('p', '8881', '/jwt/jsonInBlacklist', 'POST'),
|
||||
('p', '8881', '/system/getSystemConfig', 'POST'),
|
||||
('p', '8881', '/system/setSystemConfig', 'POST'),
|
||||
('p', '8881', '/customer/customer', 'POST'),
|
||||
('p', '8881', '/customer/customer', 'PUT'),
|
||||
('p', '8881', '/customer/customer', 'DELETE'),
|
||||
('p', '8881', '/customer/customer', 'GET'),
|
||||
('p', '8881', '/customer/customerList', 'GET'),
|
||||
('p', '8881', '/user/getUserInfo', 'GET'),
|
||||
|
||||
-- 9528
|
||||
('p', '9528', '/user/admin_register', 'POST'),
|
||||
('p', '9528', '/api/createApi', 'POST'),
|
||||
('p', '9528', '/api/getApiList', 'POST'),
|
||||
('p', '9528', '/api/getApiById', 'POST'),
|
||||
('p', '9528', '/api/deleteApi', 'POST'),
|
||||
('p', '9528', '/api/updateApi', 'POST'),
|
||||
('p', '9528', '/api/getAllApis', 'POST'),
|
||||
|
||||
('p', '9528', '/authority/createAuthority', 'POST'),
|
||||
('p', '9528', '/authority/deleteAuthority', 'POST'),
|
||||
('p', '9528', '/authority/getAuthorityList', 'POST'),
|
||||
('p', '9528', '/authority/setDataAuthority', 'POST'),
|
||||
|
||||
('p', '9528', '/menu/getMenu', 'POST'),
|
||||
('p', '9528', '/menu/getMenuList', 'POST'),
|
||||
('p', '9528', '/menu/addBaseMenu', 'POST'),
|
||||
('p', '9528', '/menu/getBaseMenuTree', 'POST'),
|
||||
('p', '9528', '/menu/addMenuAuthority', 'POST'),
|
||||
('p', '9528', '/menu/getMenuAuthority', 'POST'),
|
||||
('p', '9528', '/menu/deleteBaseMenu', 'POST'),
|
||||
('p', '9528', '/menu/updateBaseMenu', 'POST'),
|
||||
('p', '9528', '/menu/getBaseMenuById', 'POST'),
|
||||
('p', '9528', '/user/changePassword', 'POST'),
|
||||
('p', '9528', '/user/getUserList', 'POST'),
|
||||
('p', '9528', '/user/setUserAuthority', 'POST'),
|
||||
('p', '9528', '/fileUploadAndDownload/upload', 'POST'),
|
||||
('p', '9528', '/fileUploadAndDownload/getFileList', 'POST'),
|
||||
('p', '9528', '/fileUploadAndDownload/deleteFile', 'POST'),
|
||||
('p', '9528', '/fileUploadAndDownload/editFileName', 'POST'),
|
||||
('p', '9528', '/fileUploadAndDownload/importURL', 'POST'),
|
||||
('p', '9528', '/casbin/updateCasbin', 'POST'),
|
||||
('p', '9528', '/casbin/getPolicyPathByAuthorityId', 'POST'),
|
||||
('p', '9528', '/jwt/jsonInBlacklist', 'POST'),
|
||||
('p', '9528', '/system/getSystemConfig', 'POST'),
|
||||
('p', '9528', '/system/setSystemConfig', 'POST'),
|
||||
('p', '9528', '/customer/customer', 'PUT'),
|
||||
('p', '9528', '/customer/customer', 'GET'),
|
||||
('p', '9528', '/customer/customer', 'POST'),
|
||||
('p', '9528', '/customer/customer', 'DELETE'),
|
||||
('p', '9528', '/customer/customerList', 'GET'),
|
||||
('p', '9528', '/autoCode/createTemp', 'POST'),
|
||||
('p', '9528', '/user/getUserInfo', 'GET');
|
||||
|
||||
|
||||
/* =========================
|
||||
* 7. 菜单表 sys_base_menus
|
||||
* ========================= */
|
||||
|
||||
-- 7.1 顶级菜单
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
VALUES
|
||||
(0, FALSE, 0, 'dashboard', 'dashboard', 'view/dashboard/index.vue', 1, '仪表盘', 'odometer'),
|
||||
(0, FALSE, 0, 'about', 'about', 'view/about/index.vue', 9, '关于我们', 'info-filled'),
|
||||
(0, FALSE, 0, 'admin', 'superAdmin', 'view/superAdmin/index.vue', 3, '超级管理员', 'user'),
|
||||
(0, TRUE, 0, 'person', 'person', 'view/person/person.vue', 4, '个人信息', 'message'),
|
||||
(0, FALSE, 0, 'example', 'example', 'view/example/index.vue', 7, '示例文件', 'management'),
|
||||
(0, FALSE, 0, 'systemTools', 'systemTools', 'view/systemTools/index.vue', 5, '系统工具', 'tools'),
|
||||
(0, FALSE, 0, 'https://www.gin-vue-admin.com', 'https://www.gin-vue-admin.com', '/', 0, '官方网站', 'customer-gva'),
|
||||
(0, FALSE, 0, 'state', 'state', 'view/system/state.vue', 8, '服务器状态', 'cloudy'),
|
||||
(0, FALSE, 0, 'plugin', 'plugin', 'view/routerHolder.vue', 6, '插件系统', 'cherry');
|
||||
|
||||
-- 7.2 子菜单(通过名称反查父级 ID)
|
||||
|
||||
-- superAdmin 子菜单
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon, meta_keep_alive)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'authority', 'authority', 'view/superAdmin/authority/authority.vue', 1, '角色管理', 'avatar', FALSE
|
||||
FROM sys_base_menus m WHERE m.name = 'superAdmin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon, meta_keep_alive)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'menu', 'menu', 'view/superAdmin/menu/menu.vue', 2, '菜单管理', 'tickets', TRUE
|
||||
FROM sys_base_menus m WHERE m.name = 'superAdmin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon, meta_keep_alive)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'api', 'api', 'view/superAdmin/api/api.vue', 3, 'api管理', 'platform', TRUE
|
||||
FROM sys_base_menus m WHERE m.name = 'superAdmin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'user', 'user', 'view/superAdmin/user/user.vue', 4, '用户管理', 'coordinate'
|
||||
FROM sys_base_menus m WHERE m.name = 'superAdmin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'dictionary', 'dictionary', 'view/superAdmin/dictionary/sysDictionary.vue', 5, '字典管理', 'notebook'
|
||||
FROM sys_base_menus m WHERE m.name = 'superAdmin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'operation', 'operation', 'view/superAdmin/operation/sysOperationRecord.vue', 6, '操作历史', 'pie-chart'
|
||||
FROM sys_base_menus m WHERE m.name = 'superAdmin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'sysParams', 'sysParams', 'view/superAdmin/params/sysParams.vue', 7, '参数管理', 'compass'
|
||||
FROM sys_base_menus m WHERE m.name = 'superAdmin';
|
||||
|
||||
-- example 子菜单
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'upload', 'upload', 'view/example/upload/upload.vue', 5, '媒体库(上传下载)', 'upload'
|
||||
FROM sys_base_menus m WHERE m.name = 'example';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'breakpoint', 'breakpoint', 'view/example/breakpoint/breakpoint.vue', 6, '断点续传', 'upload-filled'
|
||||
FROM sys_base_menus m WHERE m.name = 'example';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'customer', 'customer', 'view/example/customer/customer.vue', 7, '客户列表(资源示例)', 'avatar'
|
||||
FROM sys_base_menus m WHERE m.name = 'example';
|
||||
|
||||
-- systemTools 子菜单
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon, meta_keep_alive)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'autoCode', 'autoCode', 'view/systemTools/autoCode/index.vue', 1, '代码生成器', 'cpu', TRUE
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon, meta_keep_alive)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'formCreate', 'formCreate', 'view/systemTools/formCreate/index.vue', 3, '表单生成器', 'magic-stick', TRUE
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'system', 'system', 'view/systemTools/system/system.vue', 4, '系统配置', 'operation'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'autoCodeAdmin', 'autoCodeAdmin', 'view/systemTools/autoCodeAdmin/index.vue', 2, '自动化代码管理', 'magic-stick'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'loginLog', 'loginLog', 'view/systemTools/loginLog/index.vue', 5, '登录日志', 'monitor'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'apiToken', 'apiToken', 'view/systemTools/apiToken/index.vue', 6, 'API Token', 'key'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, hidden, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, TRUE, m.id, 'autoCodeEdit/:id', 'autoCodeEdit', 'view/systemTools/autoCode/index.vue', 0, TRUE, '自动化代码-${id}', 'magic-stick'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'autoPkg', 'autoPkg', 'view/systemTools/autoPkg/autoPkg.vue', 0, '模板配置', 'folder'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'exportTemplate', 'exportTemplate', 'view/systemTools/exportTemplate/exportTemplate.vue', 5, '导出模板', 'reading'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'skills', 'skills', 'view/systemTools/skills/index.vue', 6, 'Skills管理', 'document'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'picture', 'picture', 'view/systemTools/autoCode/picture.vue', 6, 'AI页面绘制', 'picture-filled'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'mcpTool', 'mcpTool', 'view/systemTools/autoCode/mcp.vue', 7, 'Mcp Tools模板', 'magnet'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'mcpTest', 'mcpTest', 'view/systemTools/autoCode/mcpTest.vue', 7, 'Mcp Tools测试', 'partly-cloudy'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'sysVersion', 'sysVersion', 'view/systemTools/version/version.vue', 8, '版本管理', 'server'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'sysError', 'sysError', 'view/systemTools/sysError/sysError.vue', 9, '错误日志', 'warn'
|
||||
FROM sys_base_menus m WHERE m.name = 'systemTools';
|
||||
|
||||
-- plugin 子菜单
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'https://plugin.gin-vue-admin.com/', 'https://plugin.gin-vue-admin.com/', 'https://plugin.gin-vue-admin.com/', 0, '插件市场', 'shop'
|
||||
FROM sys_base_menus m WHERE m.name = 'plugin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'installPlugin', 'installPlugin', 'view/systemTools/installPlugin/index.vue', 1, '插件安装', 'box'
|
||||
FROM sys_base_menus m WHERE m.name = 'plugin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'pubPlug', 'pubPlug', 'view/systemTools/pubPlug/pubPlug.vue', 3, '打包插件', 'files'
|
||||
FROM sys_base_menus m WHERE m.name = 'plugin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'plugin-email', 'plugin-email', 'plugin/email/view/index.vue', 4, '邮件插件', 'message'
|
||||
FROM sys_base_menus m WHERE m.name = 'plugin';
|
||||
|
||||
INSERT INTO sys_base_menus (menu_level, hidden, parent_id, path, name, component, sort, meta_title, meta_icon)
|
||||
SELECT
|
||||
1, FALSE, m.id, 'anInfo', 'anInfo', 'plugin/announcement/view/info.vue', 5, '公告管理[示例]', 'scaleToOriginal'
|
||||
FROM sys_base_menus m WHERE m.name = 'plugin';
|
||||
|
||||
|
||||
/* =========================
|
||||
* 8. 示例文件表 exa_file_upload_and_downloads
|
||||
* ========================= */
|
||||
|
||||
INSERT INTO exa_file_upload_and_downloads (name, class_id, url, tag, key)
|
||||
VALUES
|
||||
('10.png', '0', 'https://qmplusimg.henrongyi.top/gvalogo.png', 'png', '158787308910.png'),
|
||||
('logo.png', '0', 'https://qmplusimg.henrongyi.top/1576554439myAvatar.png', 'png', '1587973709logo.png');
|
||||
|
||||
|
||||
/* =========================
|
||||
* 9. 导出模板表 sys_export_templates
|
||||
* ========================= */
|
||||
|
||||
INSERT INTO sys_export_templates (db_name, name, table_name, template_id, template_info)
|
||||
VALUES
|
||||
('', 'api', 'sys_apis', 'api',
|
||||
'{
|
||||
"path":"路径",
|
||||
"method":"方法(大写)",
|
||||
"description":"方法介绍",
|
||||
"api_group":"方法分组"
|
||||
}');
|
||||
|
||||
|
||||
/* =========================
|
||||
* 10. 多对多关联表
|
||||
* sys_data_authority_id, sys_user_authority, sys_authority_menus
|
||||
* ========================= */
|
||||
|
||||
-- 10.1 数据权限表 sys_data_authority_id
|
||||
INSERT INTO sys_data_authority_id (sys_authority_authority_id, data_authority_id)
|
||||
VALUES
|
||||
(888, 888),
|
||||
(888, 9528),
|
||||
(888, 8881),
|
||||
(9528, 9528),
|
||||
(9528, 8881);
|
||||
|
||||
|
||||
-- 10.2 用户-角色表 sys_user_authority
|
||||
INSERT INTO sys_user_authority (sys_user_id, sys_authority_authority_id)
|
||||
SELECT u.id, a.authority_id
|
||||
FROM sys_users u
|
||||
JOIN sys_authorities a ON a.authority_id IN (888, 9528, 8881)
|
||||
WHERE u.username = 'admin';
|
||||
|
||||
INSERT INTO sys_user_authority (sys_user_id, sys_authority_authority_id)
|
||||
SELECT u.id, a.authority_id
|
||||
FROM sys_users u
|
||||
JOIN sys_authorities a ON a.authority_id = 888
|
||||
WHERE u.username = 'a303176530';
|
||||
|
||||
|
||||
-- 10.3 角色-菜单表 sys_authority_menus
|
||||
|
||||
-- 888: 拥有所有菜单
|
||||
INSERT INTO sys_authority_menus (sys_authority_authority_id, sys_base_menu_id)
|
||||
SELECT 888, id FROM sys_base_menus;
|
||||
|
||||
-- 8881: 仅拥有 dashboard / about / person / state 顶级菜单
|
||||
INSERT INTO sys_authority_menus (sys_authority_authority_id, sys_base_menu_id)
|
||||
SELECT 8881, id
|
||||
FROM sys_base_menus
|
||||
WHERE parent_id = 0 AND name IN ('dashboard', 'about', 'person', 'state');
|
||||
|
||||
-- 9528: 所有顶级菜单 + systemTools/example 下的子菜单
|
||||
INSERT INTO sys_authority_menus (sys_authority_authority_id, sys_base_menu_id)
|
||||
SELECT 9528, id
|
||||
FROM sys_base_menus
|
||||
WHERE parent_id = 0;
|
||||
|
||||
INSERT INTO sys_authority_menus (sys_authority_authority_id, sys_base_menu_id)
|
||||
SELECT 9528, id
|
||||
FROM sys_base_menus
|
||||
WHERE parent_id IN (
|
||||
SELECT id FROM sys_base_menus WHERE name IN ('systemTools', 'example')
|
||||
);
|
||||
|
||||
COMMIT;
|
||||
|
||||
179
docs/前端卡.md
Normal file
179
docs/前端卡.md
Normal file
@@ -0,0 +1,179 @@
|
||||
在 SillyTavern(ST)的圈子里,**“前端卡”**(Frontend Card / HTML Character Card)是一个进阶概念,它打破了传统角色卡“纯文字描述”的限制。
|
||||
|
||||
作为开发者,在重构“云酒馆”时,理解前端卡至关重要,因为它是 ST 社区目前最活跃、最“花哨”的部分。
|
||||
|
||||
### 1. 什么是前端卡?
|
||||
|
||||
传统角色卡只包含姓名、描述、问候语等文本数据。而**前端卡**本质上是一个**以角色卡为载体的“微型网页应用”**。
|
||||
|
||||
它利用 ST 的扩展系统(Extensions)和 HTML 渲染能力,在聊天界面注入自定义的 HTML、CSS 和 JavaScript。常见的表现形式形式包括:
|
||||
|
||||
* **状态栏**:显示角色的好感度、心情、体力值。
|
||||
* **互动 UI**:点击按钮触发特定的指令或对话。
|
||||
* **视觉小说(VN)模式**:将背景、立绘和对话框完全重新设计。
|
||||
* **RPG 系统**:自带背包、商店、任务系统。
|
||||
|
||||
---
|
||||
|
||||
### 2. 实现原理(ST 源码逻辑)
|
||||
|
||||
前端卡的实现主要依赖以下几个技术点:
|
||||
|
||||
#### A. HTML 注入
|
||||
|
||||
ST 允许在 `World Info`(世界书)或者角色卡描述中使用特定的标签。最常用的是配合 **Quick Reply(快速回复)** 扩展或特定的 **Extension 钩子**。
|
||||
|
||||
* **逻辑**:后端读取到包含 HTML 字符串的数据,前端通过 `$.append()` 或 `innerHTML` 将其塞进 DOM。
|
||||
|
||||
#### B. JavaScript 脚本执行
|
||||
|
||||
这是前端卡的“灵魂”。制作者通常会将加密或压缩后的 JS 代码嵌入在角色卡的“深度提示词”(Depth Prompt)或世界书条目中。
|
||||
|
||||
* **执行机制**:ST 并没有为这些脚本提供沙箱环境。脚本运行在全局作用域下,可以直接通过 `window.SillyTavern` 对象访问 ST 的内部函数(如 `printMessage()`, `setVar()`, `token` 等)。
|
||||
|
||||
#### C. 通信与状态同步
|
||||
|
||||
前端卡通过以下方式与 ST 核心交互:
|
||||
|
||||
* **Macros(宏)**:利用 `{{getvar::hp}}` 渲染变量。
|
||||
* **Slash Commands**:通过 JS 调用 `/setvar key=value` 来更新后端数据。
|
||||
* **Event Listeners**:监听 `character_selected` 或 `message_received` 事件来刷新 UI。
|
||||
|
||||
---
|
||||
|
||||
### 3. 重构时的“重难点”与注意事项
|
||||
|
||||
如果你希望“云酒馆”完全兼容这些前端卡,你在使用 **React + Go** 重构时会面临巨大的架构挑战:
|
||||
|
||||
#### **1. 安全性(最致命的问题)**
|
||||
|
||||
* **ST 原版**:因为是单机运行,JS 随便跑,顶多是用户自己害自己。
|
||||
* **你的公共平台**:这是 **跨站脚本攻击(XSS)** 的重灾区。如果用户 A 上传了一张“前端卡”,用户 B 导入后,这张卡里的 JS 脚本可以直接偷走用户 B 的浏览器 `LocalStorage`(包含你的平台 Token)或者劫持 API Key。
|
||||
* **解决方案**:
|
||||
* **必须隔离**:绝对不能直接在 React 主页面插入角色卡的 HTML/JS。
|
||||
* **Iframe 沙箱**:将前端卡的渲染逻辑放在一个独立的、跨域的 `<iframe>` 中,限制 `sandbox` 属性(禁止访问父窗口)。
|
||||
* **消息传递**:React 宿主环境与 Iframe 之间通过 `postMessage` 进行受限的通信。
|
||||
|
||||
#### **2. API 兼容层**
|
||||
|
||||
前端卡通常会调用 `SillyTavern.XXX` 这种全局对象。
|
||||
|
||||
* **重构方案**:你需要在 Iframe 沙箱中预先注入一个“模拟对象”,模拟出 ST 的全局 API 环境,将这些调用映射到你自己的后端 API 或 Zustand Store。
|
||||
|
||||
#### **3. 数据持久化**
|
||||
|
||||
前端卡依赖大量的自定义变量(Variables)。
|
||||
|
||||
* **重构方案**:确保你的 PostgreSQL 数据库中 `user_variables` 表能够支持高频读写,因为一些前端卡会通过 JS 脚本每秒多次更新变量(例如计时器或动态动画)。
|
||||
|
||||
#### **4. 样式的冲突**
|
||||
|
||||
前端卡自带的 CSS 经常会全局污染样式。
|
||||
|
||||
* **重构方案**:使用 **Shadow DOM** 或 **CSS Modules**。当然,如果用了 Iframe,天然就解决了这个问题。
|
||||
|
||||
---
|
||||
|
||||
### 4. 架构要点(结合你们现有系统)
|
||||
|
||||
#### 安全与隔离
|
||||
|
||||
- 使用 iframe 沙箱模式来渲染前端卡内容,避免直接在宿主页面全局作用域执行脚本。
|
||||
- 通过 postMessage 实现前端卡与宿主系统的受控通信,限定可访问的接口集合。
|
||||
- 设定严格的内容策略(Content Security Policy),对内联脚本、外部资源、以及跨域请求进行管理。
|
||||
|
||||
#### 数据与 API 设计
|
||||
|
||||
- 后端存储前端卡的 HTML/CSS/JS 片段、扩展数据、以及变量定义。后端仅做存储与校验,不执行脚本。
|
||||
- 前端卡通过受控接口访问后端数据(如变量、角色信息、事件触发等)。
|
||||
- 提供可版本化的 API 接口,用于加载、更新、禁用前端卡,以及回滚到稳定版本。
|
||||
|
||||
#### 渲染与交互
|
||||
|
||||
- 前端卡在 iframe 内部渲染,用 Shadow DOM/CSS Modules/或独立的样式域来避免全局样式冲突。
|
||||
- 提供一个明确的 UI 生命周期:初始化、加载资源、渲染、更新、销毁。
|
||||
- 交互事件通过 postMessage 传递,宿主统一进行权限校验与路由。
|
||||
|
||||
#### 兼容性与迁移
|
||||
|
||||
- 对现有角色卡数据结构进行向后兼容处理,Gradual Migration 路线图:先支持最小化前端卡,再逐步引入扩展能力。
|
||||
- 做好版本回滚与降级策略,确保单卡或单次迁移失败时系统可继续工作。
|
||||
|
||||
#### 性能与监控
|
||||
|
||||
- 对前端卡资源进行容量与时间上的监控(加载时间、渲染时间、内存占用)。
|
||||
- 对复杂卡片设定资源上限,避免页面整体降级。
|
||||
|
||||
---
|
||||
|
||||
### 5. 实施工具与注意事项
|
||||
|
||||
#### 安全策略
|
||||
|
||||
- 明确规定 iframe sandbox 属性,例如:sandbox="allow-scripts"(可选限制更多能力),禁用同源策略外的访问,严格限制跨域能力。
|
||||
- 使用 CSP、Content Security Policy 报告与阻断机制,避免内联脚本被滥用。
|
||||
|
||||
#### 数据边界
|
||||
|
||||
- 设置前端卡大小的上限(HTML/JS 总长度、资源大小),避免 UI 注入导致页面阻塞或崩溃。
|
||||
|
||||
#### 日志与审计
|
||||
|
||||
- 记录哪些前端卡被加载、哪些代码被执行、以及潜在的安全事件。
|
||||
|
||||
#### 流程与回滚
|
||||
|
||||
- 卡片的版本化、灰度发布、快速回滚机制,确保单卡异常不会影响全局。
|
||||
|
||||
#### 测试要点
|
||||
|
||||
- 安全性测试(XSS/恶意脚本注入)、性能压测、跨域通信正确性测试、回滚演练。
|
||||
|
||||
---
|
||||
|
||||
### 6. 开发路线与任务清单(建议)
|
||||
|
||||
- 确定沙箱实现方案(iframe + postMessage)与接口白名单。
|
||||
- 设计并实现一个前端卡沙箱适配层:在宿主页面暴露受控 API,映射到后端或前端状态商店。
|
||||
- 定义前端卡数据模型:HTML/CSS/JS、Extensions、World Info 引用、变量定义等的数据库字段与版本控制。
|
||||
- 引入内容审核/过滤机制,对上传的前端卡进行静态/动态分析,防止危害。
|
||||
- 提供一个最小可用版本(MVP):只支持简单的 UI 注入与变量调用,逐步扩展互动组件、VN 模式、背包/商店等系统。
|
||||
- 编写迁移指南与回滚策略,保障升级过程的可控性。
|
||||
- 与前端团队协同,建立 UI/UX 一致性规范(渐进式增强、UI 风格、交互动画等)。
|
||||
|
||||
---
|
||||
|
||||
### 7. 风险与注意事项
|
||||
|
||||
- 安全性是第一位,绝不能让前端卡访问宿主的敏感 API、Token、Key 等。
|
||||
- 资源占用管理,避免长时间执行脚本导致内存/CPU 过高。
|
||||
- 兼容性测试要覆盖历史卡、扩展卡、以及新卡的回滚路径。
|
||||
- 版本与数据一致性,确保升级/回滚时的卡状态不会导致数据错乱。
|
||||
|
||||
---
|
||||
|
||||
### 8. 参考与链接
|
||||
|
||||
- ST 官方文档(前端卡相关章节)
|
||||
- 安全最佳实践(Iframe 沙箱、postMessage、Content Security Policy)
|
||||
- 你们的现有实现片段:后端 API、WorldbookEngine、Extensions、Macros、Variables
|
||||
|
||||
---
|
||||
|
||||
### 9. 结论
|
||||
|
||||
**前端卡 = 角色卡数据 + 动态 UI 脚本。**
|
||||
|
||||
在重构时,我建议你将其视为**“第三方不可信插件”**来处理。
|
||||
|
||||
1. **后端(Go)**:负责存储和校验 HTML/JS 字符串,但不执行。
|
||||
2. **前端(React)**:负责开辟一个“隔离区”(沙箱),把脚本关进去运行。
|
||||
3. **兼容性**:你需要实现一个适配器,让那些习惯于 ST 语境的脚本以为自己还在 ST 里,但实际上是在受你监控的“云酒馆”容器中。
|
||||
|
||||
如果你能完美解决“安全隔离”的同时保留“交互能力”,你的云酒馆将直接降维打击目前市面上大部分只能聊天的网页端酒馆。
|
||||
|
||||
---
|
||||
|
||||
### 10. 案例分析:Clannad_v3.1.png 落地要点
|
||||
|
||||
请参阅独立文档 [docs/Clannad_v3.1_analysis.md](./Clannad_v3.1_analysis.md) 以获取完整的落地要点、字段设计、实现步骤与验收标准。
|
||||
458
docs/重构文档.md
Normal file
458
docs/重构文档.md
Normal file
@@ -0,0 +1,458 @@
|
||||
这是一个需要长期演进、但方向已经比较清晰的重构计划。为了让“云酒馆”既能承载多用户公共场景,又能高度兼容 SillyTavern(以下简称 **ST**)生态,本方案结合当前代码与 `st/` 源码,对整体架构和落地路径进行整理与校准。
|
||||
|
||||
---
|
||||
|
||||
## 1. 总体架构与设计原则
|
||||
|
||||
### 1.1 技术栈与目录结构
|
||||
|
||||
- **前端**:React 18 + TypeScript + Tailwind CSS + Zustand
|
||||
- 代码目录:`web-app/`
|
||||
- **后端**:Go + Gin + GORM + pgvector
|
||||
- 代码目录:`server/`
|
||||
- **数据库**:PostgreSQL 15+(大量使用 JSONB,部分向量字段使用 pgvector)
|
||||
- **文件存储**:S3 兼容(MinIO / OSS 等),用于 PNG 角色卡 / 头像等
|
||||
- **ST 源码**:`st/` 目录,用作兼容参考与数据样例
|
||||
|
||||
### 1.2 云版 vs 本地版的根本差异
|
||||
|
||||
- **ST 当前形态**:
|
||||
- 本地应用,配置与数据以文件形式存在 `default/content/**` 与 `data/**`
|
||||
- 单机 / 小规模用户,无严格多租户和权限隔离
|
||||
- **云酒馆目标形态**:
|
||||
- **数据库优先**:所有业务数据(用户、角色、预设、世界书、对话、正则脚本等)统一落到 PostgreSQL
|
||||
- **多租户隔离**:所有核心表带 `user_id`,中间件强制 `WHERE user_id = ?`
|
||||
- **前后端分层清晰**:
|
||||
- 后端 → 唯一“真相来源”(SSOT):负责 Prompt pipeline、世界书触发、正则处理、变量替换、AI 调用
|
||||
- 前端 → 纯 UI 壳:负责编辑与展示,不再私自保存“内存版世界书/预设”
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前系统现状(结合现有代码)
|
||||
|
||||
### 2.1 后端(`server/`)
|
||||
|
||||
从 `server/model/app/README.md` 以及各模型文件来看,当前状态大致为:
|
||||
|
||||
- **用户与会话**(已完成)
|
||||
- 模型:`AppUser`、`AppUserSession`
|
||||
- JWT 登录 / 刷新 / 注销 / 用户资料修改等已具备
|
||||
- **角色卡管理(AICharacter)**(已完成)
|
||||
- 模型:`AICharacter`(使用 JSONB 存 ST V2 数据,如 `CardData` / `Tags` 等)
|
||||
- 支持 PNG / JSON 角色卡导入、角色列表、详情、编辑、删除、导出
|
||||
- **预设管理(AIPreset)**(模型已就绪,API 待补齐)
|
||||
- 模型:`AIPreset`,已拆分采样参数字段 + `Extensions JSONB`
|
||||
- **世界书(AIWorldInfo)与正则脚本(RegexScript)**(模型已存在,逻辑未完全落地)
|
||||
- 模型:`AIWorldInfo`(世界书实体)
|
||||
- 模型:`RegexScript`(完全对齐 ST 正则脚本字段)
|
||||
- **对话与消息**(基础模型已存在)
|
||||
- 模型:`AIChat`、`AIMessage`、`AIMessageSwipe`,用于会话与多候选
|
||||
- **AI 配置与向量记忆**(已具备)
|
||||
- 模型:`AIProvider`、`AIModel`、`AIMemoryVector` 等
|
||||
|
||||
> 小结:后端数据模型已经为「ST 兼容 + 云平台」打好了基础,但 Prompt pipeline、世界书触发引擎、正则处理引擎等运行时逻辑仍需集中落地。
|
||||
|
||||
### 2.2 前端(`web-app/`)
|
||||
|
||||
- **状态管理(Zustand)**:`src/store/index.ts`
|
||||
- 单一 Store,明确区分 `Model` / `Actions` / `Selectors`
|
||||
- 内置变量系统 `variables: { user, char, ... }` 与 `substituteVariables()` 工具
|
||||
- **用户系统**:登录 / 注册 / 用户资料页已接入后端
|
||||
- **角色卡管理**:角色列表 / 上传 PNG/JSON / 编辑 / 导出已接入 `/app/character`
|
||||
- **预设管理**:有 UI 骨架,但部分仍依赖前端假数据,需要接入真实 `/app/preset`
|
||||
- **聊天界面**:`ChatPage` + `ChatArea` + `MessageContent`
|
||||
- 布局与基本会话切换已完成
|
||||
- 近期已接入:
|
||||
- `regexEngine.ts`:前端正则脚本执行引擎
|
||||
- `textRenderer.ts`:文本渲染管线(解析 `<maintext>` / `<Status_block>` / choices 等)
|
||||
- `ChatArea.tsx` 中从后端加载 `RegexScript` 列表,将用户 / 角色变量与脚本一并传入 `MessageContent`
|
||||
|
||||
> 小结:前端已具备 MVU 风格的状态管理与 ST 风格文本渲染能力,下一步是把预设 / 世界书 / 正则选择与后端 pipeline 串联起来。
|
||||
|
||||
---
|
||||
|
||||
## 3. 目标架构(SillyTavern 兼容实现)
|
||||
|
||||
### 3.1 核心领域模型(后端视角)
|
||||
|
||||
基于当前 `server/model/app`,目标是让以下模型整体表达 ST 的角色卡 / 世界书 / 正则 / 预设 / 对话体系:
|
||||
|
||||
- **AICharacter**
|
||||
- 存 ST 角色卡 V2/V3 标准化版本(结构化字段)+ 原始 JSON 存档(用于无损导出)
|
||||
- 预留:
|
||||
- `raw_card_json JSONB`(若尚未添加,可在后续迁移中补上)
|
||||
- `bound_worldinfo_ids`:角色绑定世界书 ID 列表(可选)
|
||||
- `bound_regex_ids`:角色绑定正则脚本 ID 列表(可选)
|
||||
- **AIWorldInfo(Worldbook & WorldbookEntry)**
|
||||
- 支持:
|
||||
- 角色内世界书(挂在某个角色下)
|
||||
- 用户级 / 全局世界书(多个角色共享)
|
||||
- 字段需覆盖 ST 世界书的完整配置:
|
||||
- `keys / secondary_keys / comment / content / constant / disabled / use_regex / case_sensitive / match_whole_words / selective / selective_logic / position / depth / order / probability / sticky / cooldown / delay / group / 额外 JSON`
|
||||
- **RegexScript**
|
||||
- 已与 ST 字段对齐:
|
||||
- `findRegex / replaceWith / trimStrings(JSONB)`
|
||||
- `placement`(输入 / 输出 / 世界书 / 显示等阶段)
|
||||
- `disabled / markdownOnly / runOnEdit / promptOnly`
|
||||
- `substituteRegex / minDepth / maxDepth`
|
||||
- `scope (global | character | preset)` + `ownerCharId / ownerPresetId`
|
||||
- `order`(执行顺序)
|
||||
- **AIPreset + 绑定关系**
|
||||
- `AIPreset` 存采样参数 + systemPrompt + 其他配置
|
||||
- 预留 `PresetPrompt` / `PresetRegexBinding` 等概念,用于:
|
||||
- 描述 prompt 列表(不同位置、深度)
|
||||
- 绑定正则脚本到特定预设
|
||||
- **AIChat / AIMessage**
|
||||
- 对话(Conversation)与消息(Message)
|
||||
- 与 `AICharacter`、`AIPreset`、AI 配置等建立关联
|
||||
- `AIChat.Settings JSONB` 存储会话级设置:
|
||||
- 当前 presetId
|
||||
- 活动世界书 ID 列表
|
||||
- 活动正则脚本 ID 列表
|
||||
|
||||
### 3.2 运行时 Prompt Pipeline(目标形态)
|
||||
|
||||
所有客户端(目前只有 React Web,未来可能有更多)统一通过 Go 后端完成以下完整流程:
|
||||
|
||||
1. **加载上下文**:
|
||||
- 根据 conversationId 加载:`AIChat`、`AICharacter`、`AIPreset`、AIConfig、绑定的 `AIWorldInfo` 与 `RegexScript`
|
||||
2. **输入正则处理(placement = input)**:
|
||||
- 对用户输入文本先跑一遍 RegexScript(global + character + preset)
|
||||
3. **写入 user message** 到数据库
|
||||
4. **世界书扫描(World Info Engine)**:
|
||||
- 在最近 N 条消息 + 角色描述 / 场景 等组成的扫描文本上,套用 ST 的 world-info 算法:
|
||||
- 主 / 副关键词匹配,支持 `use_regex`、`caseSensitive`、`matchWholeWords`
|
||||
- `selectiveLogic` 控制主 / 副键组合逻辑
|
||||
- `depth`、`sticky`、`cooldown`、`delay`、`probability` 等控制触发频次与时间窗
|
||||
5. **Prompt 构建**:
|
||||
- system 部分:角色 system + scenario + 作者注 + preset.systemPrompt + world info
|
||||
- history 部分:最近若干 user/assistant 消息
|
||||
- 预设附加 prompt:按 depth / position / order 注入其他内容
|
||||
6. **模型调用**:
|
||||
- 根据 `AIProvider` / `AIModel` / `/app/ai-config` 配置,调用相应厂商 API(OpenAI 兼容 / Anthropic 等)
|
||||
- 支持 SSE 流式输出,将 token 流推送给前端
|
||||
7. **输出正则处理(placement = output)**:
|
||||
- 对完整 AI 输出再次执行 RegexScript(global + character + preset)
|
||||
8. **写入 assistant message** 到数据库
|
||||
9. **返回前端**:
|
||||
- 返回最终完整消息内容;若使用 SSE,还需流式推送增量 token
|
||||
|
||||
---
|
||||
|
||||
## 4. 关键子系统的详细设计
|
||||
|
||||
### 4.1 用户与多租户隔离
|
||||
|
||||
- 所有“用户私有”数据表必须带 `user_id`:
|
||||
- 角色:`ai_characters.user_id`
|
||||
- 预设:`ai_presets.user_id`
|
||||
- 世界书:`ai_world_info.user_id`
|
||||
- 正则脚本:`regex_scripts.user_id`
|
||||
- 对话 / 消息:`ai_chats.user_id`、`ai_messages.user_id`
|
||||
- **共有资源**:
|
||||
- 官方 Demo 角色 / 预设,可以 `user_id = 0` 或 `NULL`
|
||||
- **中间件**:
|
||||
- 在 Gin 中实现统一鉴权中间件,从 JWT 中解析 `user_id`
|
||||
- 在 Service 层封装“自动附带 `user_id` 条件”的查询方法,避免重复书写 `WHERE user_id = ?`
|
||||
|
||||
### 4.2 预设系统(AIPreset)与 ST 兼容
|
||||
|
||||
- **ST 现状**:
|
||||
- 预设分散在 `st/default/content/presets/**` 的 JSON 文件中,按 API 类型区分(`context` / `instruct` / `openai` / `textgen` 等)
|
||||
- 前端 `public/scripts/preset-manager.js` 在内存管理这些预设
|
||||
- **云酒馆设计**:
|
||||
- 使用 `AIPreset` 表存储统一预设:
|
||||
- 常见采样参数拆为字段(`Temperature/TopP/TopK/...`)
|
||||
- 复杂或厂商特定配置放入 `Extensions jsonb`
|
||||
- 导入工具:
|
||||
- 后台脚本扫描 ST `presets` 目录 → 解析 JSON → 映射为 `AIPreset`
|
||||
- 前端:
|
||||
- 新建 `src/api/preset.ts` 封装 `/app/preset` 系列接口
|
||||
- `PresetManagePage` 完全改为“后端驱动”(删除假数据 `useState`)
|
||||
|
||||
### 4.3 变量与宏系统(Variables & Macros)
|
||||
|
||||
> 这一块需要区分“现状”与“目标”。
|
||||
|
||||
- **ST 当前做法(前端)**:
|
||||
- `public/scripts/variables.js`:维护局部 / 全局变量(基于 `chat_metadata` 与 `extension_settings`)
|
||||
- `public/scripts/macros.js` + `scripts/macros/**`:处理 `{{user}} / {{char}} / {{random}} / {{pick:...}}` 等宏
|
||||
- 宏替换发生在前端构造 prompt 的阶段
|
||||
- **云酒馆现状(前端)**:
|
||||
- `web-app/src/store/index.ts`:
|
||||
- `variables: Record<string, string>` 存 `user` / `char` 等
|
||||
- `substituteVariables(text, customVars?)` 支持时间宏、随机数、`pick` 等
|
||||
- `web-app/src/lib/textRenderer.ts` 中也实现了一套独立的变量替换逻辑
|
||||
- **目标设计(后端统一化)**:
|
||||
- 中短期可以继续在前端做变量替换(已实现),减少一次性改动量
|
||||
- 中长期建议:
|
||||
- 后端增加 `user_variables` 表,存 `(user_id, key, value)`
|
||||
- 在 Prompt pipeline 最后一步由后端统一执行递归变量 / 宏替换(直到不再出现 `{{...}}`)
|
||||
- 文档中要清楚标注:**“后端统一替换变量”是重构目标,而非当前 ST 现状,也不是当前实现状态。**
|
||||
|
||||
### 4.4 世界书引擎(World Info Engine)
|
||||
|
||||
- **ST 实现(真实代码)**:`st/public/scripts/world-info.js`
|
||||
- 非常复杂,包含:
|
||||
- 多种扫描策略(按 persona / description / scenario / creatorNotes 等)
|
||||
- 关键字与正则混用(`use_regex`)
|
||||
- 主键 / 副键组合逻辑(`selectiveLogic`)
|
||||
- `depth` / `sticky` / `cooldown` / `delay` / `probability` / `position` / `group` 等参数
|
||||
- **云酒馆建议做法**:
|
||||
1. 从以下来源收集“激活世界书列表”:
|
||||
- 用户全局启用:例如用户偏好中的 `activeWorldInfoIds`
|
||||
- 角色绑定:`AICharacter` 上的世界书绑定字段
|
||||
- 会话级设置:`AIChat.Settings.activeWorldInfoIds`
|
||||
2. 遍历所有激活世界书的 entries:
|
||||
- 跳过 `disabled`
|
||||
- 若 `constant = true`,无视关键词匹配直接候选(仍受 sticky / cooldown / delay / probability 影响)
|
||||
- 根据 `use_regex` / `caseSensitive` / `matchWholeWords` 对 `keys` / `secondary_keys` 做匹配
|
||||
- 按 `selectiveLogic` 结合主副键匹配结果
|
||||
- 应用 `depth` / `sticky` / `cooldown` / `delay` / `probability` 等控制触发频次
|
||||
3. 将激活的 entries 按 `order` / `position` 规则,注入到 Prompt 中对应位置
|
||||
- **现实建议**:
|
||||
- 为保证兼容性,尽量参考 / 复刻 ST `world-info.js` 的排序和筛选逻辑,而不是“重写一套简化版”。
|
||||
|
||||
### 4.5 正则脚本引擎(Regex Scripts)
|
||||
|
||||
- **后端模型**:`RegexScript`(已实现,字段与 ST 对齐)
|
||||
- **前端执行(显示层)**:
|
||||
- `web-app/src/lib/regexEngine.ts`:
|
||||
- `applyRegexScripts(text, placement, scripts, depth?)`
|
||||
- `processAIOutput` / `processUserInput` 等便捷函数
|
||||
- 支持 `/pattern/flags`、捕获组 `$1/$2/...`、`{{match}}`、`trimStrings`
|
||||
- `ChatArea.tsx`:加载 `/api/regex` 列表,过滤掉 `disabled`,传给 `MessageContent`
|
||||
- **后端执行(Prompt 层)**:
|
||||
- 输入阶段:用户文本入库 / 调用模型前,对应 ST 的“input placement”
|
||||
- 输出阶段:模型输出保存 / 返回前,对应 ST 的“output placement”
|
||||
- 世界书阶段:若需要在构造 world info 前后也跑一次 Regex,可以利用 `placement` 细分
|
||||
|
||||
---
|
||||
|
||||
## 5. 前端重构与集成方案(`web-app/`)
|
||||
|
||||
### 5.1 MVU 状态管理(Zustand)
|
||||
|
||||
- **现状**:
|
||||
- `src/store/index.ts` 已以 MVU 思路实现:
|
||||
- `AppState` = Model
|
||||
- `AppActions` = Update
|
||||
- React 组件 = View
|
||||
- 使用 `persist` 与 `devtools` 扩展,变量系统内置在 `variables` 字段中
|
||||
- **建议**:
|
||||
- 将以下信息也纳入 Store,而不是零散地用本地 `useState`:
|
||||
- 当前会话使用的 `presetId`
|
||||
- 当前会话启用的世界书 ID 列表
|
||||
- 当前会话启用的正则脚本 ID 列表
|
||||
- 通过一个统一的 `ConversationSettings` 结构,与后端 `AIChat.Settings` 对应
|
||||
|
||||
### 5.2 角色卡管理页面(CharacterManagePage)
|
||||
|
||||
目标:前端字段与 ST V2/V3 完全对齐,为后端世界书 / 正则拆分做好数据基础。
|
||||
|
||||
- **字段映射建议**:
|
||||
- `firstMes` ↔ `first_mes`
|
||||
- `mesExample` ↔ `mes_example`
|
||||
- `systemPrompt` ↔ `system_prompt`
|
||||
- `postHistoryInstructions` ↔ `post_history_instructions`
|
||||
- `characterBook` ↔ `character_book`
|
||||
- `extensions` ↔ `extensions`
|
||||
- **世界书编辑 UI**:
|
||||
- 在现有 `keys / content / enabled / position` 基础上,逐步引入高级字段(折叠到“高级设置”):
|
||||
- `secondary_keys`、`constant`、`use_regex`、`case_sensitive`、`match_whole_words`
|
||||
- `selective`、`selective_logic`
|
||||
- `depth`、`order`、`probability`、`sticky`、`cooldown`、`delay`、`group`
|
||||
- 保存时将上述字段完整写入 `characterBook.entries` 中,后端再拆分到 `AIWorldInfo` 相关表
|
||||
- **导入/导出**:
|
||||
- 前端不对 ST JSON 结构做“智能改写”,只做表单 ↔ JSON 的字段映射
|
||||
- ST 兼容性由后端 `utils/character_card.go` + `AICharacter` 来保证
|
||||
|
||||
### 5.3 预设管理页面(PresetManagePage)
|
||||
|
||||
目标:彻底从“前端内存预设”升级为“后端驱动的 ST 预设系统”。
|
||||
|
||||
- 实施步骤:
|
||||
1. 后端按规划实现 `/app/preset` 的 CRUD + 导入/导出
|
||||
2. 前端新建 `src/api/preset.ts`,统一管理预设相关请求
|
||||
3. 将 `PresetManagePage` 中的本地假数据与 `useState` 初始值删除,全部改为从 API 加载
|
||||
4. 预设 JSON 字段对齐 ST:
|
||||
- 采样参数:`temperature/top_p/top_k/frequency_penalty/presence_penalty/...`
|
||||
- Prompt 配置:`system_prompt/stop_sequences/...`
|
||||
|
||||
### 5.4 聊天与设置面板(ChatPage / SettingsPanel / CharacterPanel)
|
||||
|
||||
- **SettingsPanel 增强**:
|
||||
- 增加:
|
||||
- 当前会话使用的 preset(下拉列表来自 `/app/preset`)
|
||||
- 启用的世界书列表(来自 `/app/worldinfo`,初期可只展示角色内世界书)
|
||||
- 启用的正则脚本列表(来自 `/app/regex`)
|
||||
- 保存时调用 `/app/chat/:id/settings`(或 `PUT /app/conversation/:id`)更新 `AIChat.Settings`
|
||||
- **ChatArea & MessageContent 集成**:
|
||||
- `ChatArea.tsx`:
|
||||
- 已从 `useAppStore` 取出 `user` 与 `variables`
|
||||
- 已加载 `RegexScript` 列表,并传给 `MessageContent`
|
||||
- `MessageContent.tsx`:
|
||||
- 利用 `regexEngine` + `textRenderer` 完成:
|
||||
- 正则处理(AI 输出修饰)
|
||||
- `<maintext>` / `<Status_block>` / choices 解析
|
||||
- 变量替换 / HTML 转义 / 代码块提取 / XSS 防护
|
||||
|
||||
---
|
||||
|
||||
## 6. 分阶段落地路线图
|
||||
|
||||
### 阶段 1:打通 CRUD 与数据流(短期)
|
||||
|
||||
- **后端**:
|
||||
- 巩固 `/app/character`(保证 ST V2/V3 导入/导出稳定)
|
||||
- 实现 `/app/preset` 全套 CRUD + 导入/导出
|
||||
- 补齐世界书 `/app/worldinfo` 与正则 `/app/regex` 接口(基于现有模型)
|
||||
- **前端**:
|
||||
- `PresetManagePage` 完全接入 `/app/preset`
|
||||
- 角色编辑页面补全世界书 entry 字段,保持与 ST 的字段对齐
|
||||
|
||||
### 阶段 2:实现 Prompt Pipeline 与对话 API(中期)
|
||||
|
||||
- **后端**:
|
||||
- 在 service 层实现统一的 `chatPipeline`,串接世界书、正则、变量、预设、AI 调用逻辑
|
||||
- 统一对话入口,例如:`POST /app/chat/:id/messages` 或 `POST /app/conversation/:id/message`
|
||||
- 支持 SSE 流式输出,将 token 流推送给前端
|
||||
- **前端**:
|
||||
- Chat 页面改造为消费 SSE 流(追加消息 / 局部更新)
|
||||
- SettingsPanel / CharacterPanel 配置的 preset / 世界书 / 正则 与后端 `AIChat.Settings` 打通
|
||||
|
||||
### 阶段 3:插件系统与高级特性(长期)
|
||||
|
||||
- **后端**:
|
||||
- 引入插件表与 Hook 定义(`onUserInput` / `onWorldInfoScan` / `beforePromptBuild` / `onAssistantDone`)
|
||||
- 使用 goja / WASM 等方式实现安全的插件执行沙箱
|
||||
- 增强审计、统计与限流机制
|
||||
- **前端**:
|
||||
- 插件管理页:安装 / 启用 / 配置 / 查看日志
|
||||
- 调试视图:展示某次回复中有哪些 world info / 正则 / 插件生效
|
||||
|
||||
---
|
||||
|
||||
## 7. 重要澄清与修正(相对早期文档)
|
||||
|
||||
- **关于 Zod**:
|
||||
- 当前 `st/` 源码并未使用 Zod,而是通过 `TavernCardValidator` 等自写校验器验证角色卡结构
|
||||
- 在 `web-app` 中引入 Zod 进行前端 schema 校验是一个“改进方向”,不是 ST 现状
|
||||
- **关于 MVU**:
|
||||
- ST 当前前端仍是 jQuery + 全局脚本,不是典型 MVU 架构
|
||||
- MVU 已在云酒馆前端的 Zustand Store 中落地,应把 MVU 归于“云酒馆前端架构”,而非 ST 现状
|
||||
- **关于变量 / 宏所在层级**:
|
||||
- ST 将变量与宏主要放在前端处理
|
||||
- 云酒馆的目标是:短期兼容这种做法,中长期逐步迁移到后端统一替换
|
||||
- **关于 PNG 块类型**:
|
||||
- ST 将角色卡 JSON 植入 PNG 文本 chunk(如 `tEXt` / 自定义块),不必限定为 `iTXt`
|
||||
- Go 侧应兼容多种文本块,保证最大兼容性
|
||||
|
||||
|
||||
## 8. 核心模块开发方案
|
||||
|
||||
### 8.1 用户管理与数据隔离
|
||||
|
||||
**ST 现状**: 物理文件夹隔离(`public/users/admin/...`)。
|
||||
**重构方案**:
|
||||
|
||||
* **数据库设计**: 所有表(`characters`, `chats`, `presets`, `lorebooks`)必须包含 `user_id` 字段。
|
||||
* **权限控制**: 后端中间件统一拦截请求,从 JWT 中提取 `user_id`,并在所有 SQL 查询中强制加入 `WHERE user_id = ?` 约束。
|
||||
* **隔离策略**:
|
||||
* **共有资源**: 官方提供的默认预设、系统角色卡(`user_id` 为 0 或 NULL)。
|
||||
* **私有资源**: 用户上传或创建的内容。
|
||||
|
||||
|
||||
|
||||
### 8.2 预设系统 (Presets) 兼容方案
|
||||
|
||||
**ST 现状**: 大量的 JSON 文件,分为 API、Instruct、Context 等类别。
|
||||
**重构方案**:
|
||||
|
||||
* **存储**: 在 PostgreSQL 中创建 `presets` 表,核心配置字段设为 `jsonb` 类型。
|
||||
* **导入逻辑**: 编写一个 Go 工具类,读取 ST 的 `presets` 文件夹,将 JSON 解析后存入数据库。
|
||||
* **注意事项**: ST 的预设经常更新字段,Go 的结构体定义要使用 `map[string]interface{}` 或冗余字段来保证在 ST 更新协议时,你的后端不会因为缺失字段而崩溃。
|
||||
|
||||
### 8.3 变量管理与宏引擎 (Variables & Macros)
|
||||
|
||||
这是兼容性的灵魂。
|
||||
**实现原理**:
|
||||
|
||||
1. **基础宏**: `{{user}}`, `{{char}}`, `{{description}}` 等。
|
||||
2. **动态变量**: 用户通过 `/setvar` 定义的变量。
|
||||
**重构方案**:
|
||||
|
||||
* **后端处理**: 不要在前端替换变量,而是在后端构建 Prompt 的最后一刻进行正则替换。
|
||||
* **变量表**: `user_variables` 表,存储 `(user_id, key, value)`。
|
||||
* **递归解析**: ST 支持嵌套宏,你的 Go 后端需要实现一个递归替换函数,直到字符串中不再包含 `{{...}}`。
|
||||
|
||||
### 8.4 前端渲染与通信兼容 (Iframe/Sandbox)
|
||||
|
||||
**ST 现状**: jQuery 操作 DOM,扩展直接注入 HTML。
|
||||
**重构方案**:
|
||||
|
||||
* **渲染分离**: 使用 React 开发 UI。消息列表应支持 Markdown 解析(推荐使用 `react-markdown` 并配合插件)。
|
||||
* **通信协议**:
|
||||
* 前端 React 通过 **WebSocket** 或 **SSE** 与 Go 后端保持长连接,模拟 ST 的流式输出。
|
||||
* 定义一套标准的 JSON API 响应格式,模仿 ST 的 `/api/...` 接口,以便未来的脚本迁移。
|
||||
|
||||
|
||||
|
||||
### 8.5 扩展系统 (Extensions) 的安全性重构
|
||||
|
||||
**ST 现状**: 动态加载 JS 脚本,安全性极低。
|
||||
**重构方案 (公共平台推荐)**:
|
||||
|
||||
* **后端扩展**: 模仿 ST 的插件钩子,但在 Go 后端实现。例如,在发送 Prompt 前,调用一个“拦截器”逻辑。
|
||||
* **前端扩展**: 如果要兼容 ST 的 JS 插件,**必须将其运行在沙箱(Sandbox)中**。
|
||||
* 使用 `<iframe>` 隔离扩展 UI。
|
||||
* 通过 `postMessage` 机制进行通信。
|
||||
* **注意事项**: 绝不允许第三方扩展直接访问浏览器的 `localStorage` 或获取用户的 `user_token`。
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 9. Zod 与 MVU 的重构实现
|
||||
|
||||
### 9.1 Zod 变量的作用与迁移
|
||||
|
||||
在 ST 源码中,Zod 用于 **Runtime Schema Validation**。
|
||||
|
||||
* **作用**: 当用户上传一个外部角色卡(JSON 或 PNG 嵌入数据)时,系统无法确定数据是否完整。Zod 会在前端校验数据格式,如果缺失关键字段,会提供默认值或报错。
|
||||
* **重构建议**:
|
||||
* **前端**: 继续在 React 中使用 `zod`。在处理角色卡导入逻辑时,定义与 ST 完全一致的 `CharacterSchema`。
|
||||
* **后端**: Go 端使用 `go-playground/validator` 或直接解析 `jsonb`。确保你的 Go 结构体(Struct)打上 `json:",omitempty"` 标签,以处理 ST 角色卡中不稳定的可选字段。
|
||||
|
||||
|
||||
|
||||
### 9.2 MVU (Model-View-Update) 的应用
|
||||
|
||||
* **作用**: ST 引入 MVU 是为了解决 jQuery 状态难以管理的问题。
|
||||
* **重构建议**:
|
||||
* 在 React 中,MVU 就是 **Zustand/Redux + React Components**。
|
||||
* **Model**: Zustand 中的 `store`。
|
||||
* **Update**: Store 中的 `actions`。
|
||||
* **View**: React 组件。
|
||||
* 你不需要在代码里写 `mvu` 变量名,只需遵循这种单向数据流即可实现比 ST 稳定得多的状态管理。
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 10. 开发注意事项 (踩坑预警)
|
||||
|
||||
1. **角色卡解析**: ST 的 PNG 角色卡数据存放在 PNG 的 `iTXt` 块中。Go 语言需要使用 `image/png` 包并手动读取这些 Metadata 块。不要只支持 JSON,因为大部分玩家的资源都是 PNG。
|
||||
2. **Lorebook (世界书) 逻辑**:
|
||||
* 这是最难兼容的部分。它涉及:关键字扫描、递归深度、插入位置、插入顺序。
|
||||
* **建议**: 直接复刻 ST 的 `world-info.js` 中的排序算法。如果这部分算法不一致,AI 的表现会与原版 ST 大相径庭。
|
||||
|
||||
|
||||
3. **计算 Token**: ST 使用 `Tiktoken` 或特定模型的 Tokenizer。为了节省服务器资源,建议在前端进行初次计算,后端在调用 API 前再次校准。
|
||||
4. **跨域与 API 代理**: 公共平台需要处理大量大模型 API 的转发。Go 后端要实现一个高性能的 **Proxy Layer**,处理超时、重试以及不同厂商(OpenAI, Anthropic, Google)的协议转换。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3893
docs/预设/Sudachi.Next1.21.json
Normal file
3893
docs/预设/Sudachi.Next1.21.json
Normal file
File diff suppressed because one or more lines are too long
267
docs/预设模块优化设计.md
Normal file
267
docs/预设模块优化设计.md
Normal file
@@ -0,0 +1,267 @@
|
||||
## 预设模块(Preset)导入与启用逻辑优化设计
|
||||
|
||||
### 一、背景与问题
|
||||
|
||||
当前系统支持从 SillyTavern / TavernAI 风格的 JSON 文件导入预设,并将其中的采样参数和提示词配置(prompts / prompt_order)存入后端 `AIPreset.Extensions` 字段中,前端在 `PresetManagePage` / `EditPresetModal` 中提供基本的编辑能力。
|
||||
|
||||
在实际使用中暴露出一个明显问题:
|
||||
|
||||
- **问题:导入预设后,提示词块(prompts)的启用 / 禁用状态与原始 JSON 文件不一致。**
|
||||
|
||||
具体表现:
|
||||
|
||||
- SillyTavern 预设中的「默认启用 / 禁用」信息主要体现在:
|
||||
- `prompts[*].enabled`(整体默认开关,可能存在)
|
||||
- `prompt_order[*].order[*].enabled`(更精细的 per-character 启用信息,是真正生效的一层)
|
||||
- 我们当前前端 UI 在编辑时:
|
||||
- 仅从 `extensions.prompts` 中取出 prompts
|
||||
- 用 `marker` 字段反向推导「启用」状态(`checked={!prompt.marker}`)
|
||||
- 完全没有解析和尊重 `prompt_order` 里的 `enabled` 状态
|
||||
|
||||
因此,对于像 `Sudachi.Next1.21.json` 这种复杂预设:
|
||||
|
||||
- 文件中已经通过 `prompt_order` 精确控制了某些块默认 `enabled: false`
|
||||
- 导入后数据虽然保存在 `extensions.prompt_order` 中,但前端不读取,导致 UI 展示的勾选状态与原始预设不一致
|
||||
|
||||
### 二、设计目标
|
||||
|
||||
1. **忠实还原 ST 预设的启用 / 禁用行为**
|
||||
- 导入后,在不改动 ST 原始结构的前提下,让 UI 所见的启用状态尽可能与 ST 一致。
|
||||
2. **保持数据的完整与可扩展性**
|
||||
- 保留 `prompts` 与 `prompt_order` 的原始结构,未来可以继续扩展更高级行为(多角色、多上下文)。
|
||||
3. **简化前端编辑模型**
|
||||
- 前端在编辑时只需要关注:
|
||||
- 哪些 prompt 当前“启用”
|
||||
- 顺序如何
|
||||
- 不强制实现 ST 全量复杂逻辑,但要保证基础行为正确。
|
||||
4. **向后兼容现有数据**
|
||||
- 对于已存在的 `AIPreset`,在没有 `prompt_order` / `enabled` 信息时,仍然能正常工作。
|
||||
|
||||
### 三、数据结构与语义梳理
|
||||
|
||||
#### 3.1 ST 预设结构(简化视图)
|
||||
|
||||
- 顶层采样 / 行为字段(已正确映射到 `AIPreset`)
|
||||
- `temperature`, `top_p`, `top_k`, `min_p`, `top_a`
|
||||
- `frequency_penalty`, `presence_penalty`, `repetition_penalty`
|
||||
- `openai_max_tokens`, `openai_max_context`, `stream_openai`, 等
|
||||
- `prompts: Prompt[]`
|
||||
- `name: string`
|
||||
- `identifier: string`(如 `"main"`, `"nsfw"`, `"jailbreak"` 或 UUID)
|
||||
- `role: "system" | "user" | "assistant"`
|
||||
- `content: string`
|
||||
- `system_prompt: boolean`
|
||||
- `marker: boolean`(占位块;本身不一定等于“禁用”)
|
||||
- `enabled?: boolean`(整体缺省启用的开关)
|
||||
- `injection_position: number`
|
||||
- `injection_depth: number`
|
||||
- `injection_order: number`
|
||||
- `injection_trigger: any[]`
|
||||
- `forbid_overrides: boolean`
|
||||
- `prompt_order: Array<{ character_id: number; order: Array<{ identifier: string; enabled: boolean }> }>`
|
||||
- 按「角色 / 场景」维度,指定各 identifier 的启用状态与顺序
|
||||
|
||||
**关键点:**
|
||||
真正决定「是否启用」的是 `prompt_order[*].order[*].enabled`,`prompts[*].enabled` 更多是全局默认值。
|
||||
|
||||
#### 3.2 当前系统存储结构
|
||||
|
||||
后端 `PresetService.ImportPresetFromJSON` 当前的行为:
|
||||
|
||||
- 将 `prompts` / `prompt_order` 原样塞入:
|
||||
|
||||
```go
|
||||
extensions := map[string]interface{}{
|
||||
"prompts": stPreset.Prompts,
|
||||
"prompt_order": stPreset.PromptOrder,
|
||||
}
|
||||
```
|
||||
|
||||
前端 `EditPresetModal` 当前的行为:
|
||||
|
||||
- 通过 `preset.extensions.prompts` 取得 `prompts` 列表
|
||||
- 「启用」复选框用的是:
|
||||
- `checked={!prompt.marker}`
|
||||
- 也就是:只要不是 marker,就视为启用
|
||||
- 没有考虑:
|
||||
- `prompt.enabled`
|
||||
- `prompt_order[*].order[*].enabled`
|
||||
|
||||
### 四、优化方案总览
|
||||
|
||||
整体策略:
|
||||
|
||||
- **后端保持“原样存储”原则**,继续把 ST 的 `prompts` 和 `prompt_order` 保存在 `extensions` 里,方便以后做高级兼容。
|
||||
- **新增一层“系统视角配置”**(推荐写在 `extensions.stMapping` 内),用于:
|
||||
- 提取出我们当前真正要用到的“默认启用 / 禁用信息”
|
||||
- 为前端提供简单直观的数据结构
|
||||
- **前端 UI 只依赖这层映射**来决定默认勾选状态、顺序等,避免直接耦合 ST 的复杂行为。
|
||||
|
||||
### 五、后端导入逻辑优化设计
|
||||
|
||||
#### 5.1 导入时的启用状态归一化
|
||||
|
||||
在 `PresetService.ImportPresetFromJSON` 中,对 `stPreset.Prompts` 与 `stPreset.PromptOrder` 做一次归一化处理,得到:
|
||||
|
||||
- `map[string]bool`:`identifier` → `enabled`(系统视角下的默认启用状态)
|
||||
|
||||
处理规则建议如下:
|
||||
|
||||
1. 从 `prompt_order` 中选出一个「主视角 character_id」
|
||||
- 优先策略:取 `prompt_order` 中 **第一个条目** 的 `character_id`(例如 Sudachi 文件中的 `100000`)。
|
||||
- 这样可理解为“该预设的主角 / 默认角色对应的一组配置”。
|
||||
2. 针对该 `character_id` 的 `order` 数组:
|
||||
- 遍历每一项 `{ identifier, enabled }`
|
||||
- 记入 `enabledMap[identifier] = enabled`
|
||||
3. 对于 `prompts` 中存在但在 `order` 中未出现的 identifier:
|
||||
- 若 `prompt.enabled` 明确存在,则用该字段
|
||||
- 否则默认 `true`(保持向后兼容,除非 ST 未来规范有特别说明)
|
||||
|
||||
归一化后的结果示意:
|
||||
|
||||
```json
|
||||
{
|
||||
"stMapping": {
|
||||
"mainCharacterId": 100000,
|
||||
"enabledByIdentifier": {
|
||||
"main": true,
|
||||
"worldInfoBefore": true,
|
||||
"enhanceDefinitions": false,
|
||||
"nsfw": true,
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意:
|
||||
|
||||
- 原始的 `prompts` / `prompt_order` 仍然原封不动地存放在 `extensions.prompts` / `extensions.prompt_order` 中。
|
||||
- `stMapping` 只是我们系统内部使用的“视图”,不影响原始数据。
|
||||
|
||||
#### 5.2 可选:为 prompts 注入统一的 `enabled` 字段
|
||||
|
||||
为了前端更好使用,也可以在导入时(或者在后端读取时)对 `prompts` 做一次增强:
|
||||
|
||||
- 对每个 `prompt`:
|
||||
- 查询 `enabled := stMapping.enabledByIdentifier[prompt.identifier]`
|
||||
- 如果找到了,就在该 `prompt` 上写入 / 覆盖 `prompt["enabled"] = enabled`
|
||||
|
||||
这样,前端只要拿到 `extensions.prompts`,就能直接看到每条的 `enabled` 字段,不必额外关联 `prompt_order`。
|
||||
|
||||
> 兼容建议:
|
||||
> - 不删除原有的 `enabled`,只是统一成一个规范值;
|
||||
> - 如果未来 ST 升级行为,我们仍保留原始 JSON,可以再次调整映射逻辑。
|
||||
|
||||
### 六、前端编辑行为优化设计
|
||||
|
||||
#### 6.1 EditPresetModal 中的启用逻辑修改
|
||||
|
||||
当前逻辑:
|
||||
|
||||
- 通过 `getPrompts()` 从 `extensions.prompts` 取出数组
|
||||
- 复选框逻辑:
|
||||
|
||||
```tsx
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!prompt.marker}
|
||||
onChange={(e) => handlePromptChange(index, 'marker', !e.target.checked)}
|
||||
/>
|
||||
```
|
||||
|
||||
问题:
|
||||
|
||||
- 把 “是否 marker” 当成了 “是否启用”,与 ST 语义不符。
|
||||
|
||||
优化方案:
|
||||
|
||||
1. 新增对 `enabled` 字段的支持
|
||||
- 解析时,优先使用:
|
||||
- `prompt.enabled`(如果存在)
|
||||
- 否则回退为 `!prompt.marker`(为了兼容老数据)
|
||||
2. UI 含义调整:
|
||||
- 勾选框表示「此 prompt 是否启用」,而非「是否 marker」
|
||||
- `marker` 仍然保留,用于区分“占位块”(如 `chatHistory`、`dialogueExamples`)和“实际有内容的块”,但不再被直接当作启用条件。
|
||||
|
||||
示例(伪代码):
|
||||
|
||||
```tsx
|
||||
const isEnabled = prompt.enabled !== undefined
|
||||
? prompt.enabled
|
||||
: !prompt.marker
|
||||
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={isEnabled}
|
||||
onChange={(e) => handlePromptChange(index, 'enabled', e.target.checked)}
|
||||
/>
|
||||
```
|
||||
|
||||
3. 保存时,将 `enabled` 写回 `extensions.prompts` 中对应项。
|
||||
|
||||
> 提醒:
|
||||
> - 短期内可以不在前端去写 `prompt_order`,只要我们的对话生成逻辑是“按 prompts + enabled 过滤”就足够;
|
||||
> - 如果未来要做到 ST 完整语义(如多角色不同顺序),再在前端提供更高级的顺序管理 UI。
|
||||
|
||||
#### 6.2 默认显示顺序
|
||||
|
||||
当前 UI 在 EditPresetModal 里对 prompts 没有太多排序逻辑,直接用数组顺序:
|
||||
|
||||
- 初期优化阶段,可以继续沿用 ST 原始 `prompts` 顺序
|
||||
- 如果希望更贴近 ST 行为,可以:
|
||||
- 根据 `injection_position` / `injection_depth` / `injection_order` 做一个简易排序
|
||||
- 或者读取 `stMapping.mainCharacterId` 对应 `prompt_order` 的顺序,按其中 identifier 顺序排列
|
||||
|
||||
建议分阶段:
|
||||
|
||||
1. **第一阶段(快速修复启用逻辑)**
|
||||
- 先只修复 `enabled` 使用,仍按原数组顺序展示
|
||||
2. **第二阶段(顺序优化)**
|
||||
- 再根据 `prompt_order` / injection 字段优化展示顺序
|
||||
|
||||
### 七、对话生成逻辑的使用建议(可选)
|
||||
|
||||
目前后端对话生成(Convesation Service)是如何使用预设参数,需要在代码中再具体确认。但从设计角度建议:
|
||||
|
||||
1. 生成最终 prompt 时:
|
||||
- 从 `extensions.prompts` 中筛选:`enabled != false` 的 prompt
|
||||
- 按既定顺序(可先用原始数组顺序,或更细致地用 `injection_*` / `prompt_order`)拼接成系统提示。
|
||||
2. 如需高度还原 ST 行为:
|
||||
- 在后端增加一个专门的「ST Prompt 编译器」:
|
||||
- 输入:`prompts` + `prompt_order` + 当前角色 / 会话上下文
|
||||
- 输出:一串系统消息数组(包含角色、内容、插入位置信息)
|
||||
- 这一步可以在后续迭代中实现,本次优化先以「启用状态正确」为目标。
|
||||
|
||||
### 八、兼容性与迁移策略
|
||||
|
||||
1. **已有预设数据**
|
||||
- 已有的 `AIPreset.Extensions` 中可能只有 `prompts`,没有 `prompt_order` / `stMapping` / `enabled`:
|
||||
- `EditPresetModal` 在解析时:
|
||||
- 若找不到 `prompt.enabled`,则默认 `enabled = !marker`
|
||||
- 新导入的预设,将会有完整的 `enabled` / `stMapping` 信息。
|
||||
2. **导出为 ST JSON**
|
||||
- 目前导出的逻辑是从 `AIPreset` 再组装为 ST 样式的 JSON:
|
||||
- 建议导出时仍以 `extensions.prompts` / `extensions.prompt_order` 为主
|
||||
- `enabled` 字段如果已更新,也可以同步写回到导出 JSON 中对应位置,保持一致性
|
||||
3. **前后端升级顺序**
|
||||
- 先在后端实现 `stMapping` 和 prompts 的 `enabled` 归一化逻辑
|
||||
- 再在前端切换到从 `prompt.enabled` 读取启用状态
|
||||
- 测试导入 Sudachi 预设后,界面上默认启用 / 禁用是否与原文件一致
|
||||
|
||||
### 九、后续扩展方向(可选)
|
||||
|
||||
1. **多角色 / 多场景支持**
|
||||
- 完整解析 `prompt_order` 中所有 `character_id` 条目,根据当前对话绑定的角色 ID 切换不同启用方案。
|
||||
2. **可视化 prompt 顺序编辑**
|
||||
- 在 Preset 管理页面增加「提示词顺序拖拽调整」、分组展示(角色定义 / 文风 / COT / 变量初始化等)。
|
||||
3. **预设版本管理与对比**
|
||||
- 由于 ST 预设经常迭代,可考虑为 `AIPreset` 增加版本号 / 变更日志,以便回滚与对比。
|
||||
|
||||
---
|
||||
|
||||
以上设计以「最小代价修复启用/禁用逻辑不一致」为优先目标,同时为后续更深入兼容 ST 行为(如多角色、多 prompt_order 视图)预留空间。后续具体实现时,可以按本文件拆分为:
|
||||
|
||||
- 后端改动:`PresetService.ImportPresetFromJSON` + 可选的 Prompt 编译辅助函数
|
||||
- 前端改动:`PresetManagePage` / `EditPresetModal` 中的 prompts 解析与复选框逻辑
|
||||
|
||||
@@ -321,3 +321,61 @@ func (s *SystemApiApi) FreshCasbin(c *gin.Context) {
|
||||
}
|
||||
response.OkWithMessage("刷新成功", c)
|
||||
}
|
||||
|
||||
// GetApiRoles
|
||||
// @Tags SysApi
|
||||
// @Summary 获取拥有指定API权限的角色ID列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param path query string true "API路径"
|
||||
// @Param method query string true "请求方法"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取成功"
|
||||
// @Router /api/getApiRoles [get]
|
||||
func (s *SystemApiApi) GetApiRoles(c *gin.Context) {
|
||||
path := c.Query("path")
|
||||
method := c.Query("method")
|
||||
if path == "" || method == "" {
|
||||
response.FailWithMessage("API路径和请求方法不能为空", c)
|
||||
return
|
||||
}
|
||||
authorityIds, err := casbinService.GetAuthoritiesByApi(path, method)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
if authorityIds == nil {
|
||||
authorityIds = []uint{}
|
||||
}
|
||||
response.OkWithDetailed(authorityIds, "获取成功", c)
|
||||
}
|
||||
|
||||
// SetApiRoles
|
||||
// @Tags SysApi
|
||||
// @Summary 全量覆盖某API关联的角色列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SetApiAuthorities true "API路径、请求方法和角色ID列表"
|
||||
// @Success 200 {object} response.Response{msg=string} "设置成功"
|
||||
// @Router /api/setApiRoles [post]
|
||||
func (s *SystemApiApi) SetApiRoles(c *gin.Context) {
|
||||
var req systemReq.SetApiAuthorities
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if req.Path == "" || req.Method == "" {
|
||||
response.FailWithMessage("API路径和请求方法不能为空", c)
|
||||
return
|
||||
}
|
||||
if err := casbinService.SetApiAuthorities(req.Path, req.Method, req.AuthorityIds); err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
// 刷新casbin缓存使策略立即生效
|
||||
_ = casbinService.FreshCasbin()
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"git.echol.cn/loser/st/server/model/common/response"
|
||||
"git.echol.cn/loser/st/server/model/system"
|
||||
systemReq "git.echol.cn/loser/st/server/model/system/request"
|
||||
systemRes "git.echol.cn/loser/st/server/model/system/response"
|
||||
"git.echol.cn/loser/st/server/utils"
|
||||
|
||||
@@ -200,3 +201,57 @@ func (a *AuthorityApi) SetDataAuthority(c *gin.Context) {
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
// GetUsersByAuthority
|
||||
// @Tags Authority
|
||||
// @Summary 获取拥有指定角色的用户ID列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param authorityId query uint true "角色ID"
|
||||
// @Success 200 {object} response.Response{data=[]uint,msg=string} "获取成功"
|
||||
// @Router /authority/getUsersByAuthority [get]
|
||||
func (a *AuthorityApi) GetUsersByAuthority(c *gin.Context) {
|
||||
var req systemReq.SetRoleUsers
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
userIds, err := authorityService.GetUserIdsByAuthorityId(req.AuthorityId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
if userIds == nil {
|
||||
userIds = []uint{}
|
||||
}
|
||||
response.OkWithDetailed(userIds, "获取成功", c)
|
||||
}
|
||||
|
||||
// SetRoleUsers
|
||||
// @Tags Authority
|
||||
// @Summary 全量覆盖某角色关联的用户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SetRoleUsers true "角色ID和用户ID列表"
|
||||
// @Success 200 {object} response.Response{msg=string} "设置成功"
|
||||
// @Router /authority/setRoleUsers [post]
|
||||
func (a *AuthorityApi) SetRoleUsers(c *gin.Context) {
|
||||
var req systemReq.SetRoleUsers
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if req.AuthorityId == 0 {
|
||||
response.FailWithMessage("角色ID不能为空", c)
|
||||
return
|
||||
}
|
||||
if err := authorityService.SetRoleUsers(req.AuthorityId, req.UserIds); err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
@@ -244,6 +244,76 @@ func (a *AuthorityMenuApi) GetBaseMenuById(c *gin.Context) {
|
||||
response.OkWithDetailed(systemRes.SysBaseMenuResponse{Menu: menu}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetMenuRoles
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 获取拥有指定菜单的角色ID列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param menuId query uint true "菜单ID"
|
||||
// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取成功"
|
||||
// @Router /menu/getMenuRoles [get]
|
||||
func (a *AuthorityMenuApi) GetMenuRoles(c *gin.Context) {
|
||||
var req systemReq.SetMenuAuthorities
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if req.MenuId == 0 {
|
||||
response.FailWithMessage("菜单ID不能为空", c)
|
||||
return
|
||||
}
|
||||
authorityIds, err := menuService.GetAuthoritiesByMenuId(req.MenuId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
if authorityIds == nil {
|
||||
authorityIds = []uint{}
|
||||
}
|
||||
defaultRouterAuthorityIds, err := menuService.GetDefaultRouterAuthorityIds(req.MenuId)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取首页角色失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
if defaultRouterAuthorityIds == nil {
|
||||
defaultRouterAuthorityIds = []uint{}
|
||||
}
|
||||
response.OkWithDetailed(gin.H{
|
||||
"authorityIds": authorityIds,
|
||||
"defaultRouterAuthorityIds": defaultRouterAuthorityIds,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// SetMenuRoles
|
||||
// @Tags AuthorityMenu
|
||||
// @Summary 全量覆盖某菜单关联的角色列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body systemReq.SetMenuAuthorities true "菜单ID和角色ID列表"
|
||||
// @Success 200 {object} response.Response{msg=string} "设置成功"
|
||||
// @Router /menu/setMenuRoles [post]
|
||||
func (a *AuthorityMenuApi) SetMenuRoles(c *gin.Context) {
|
||||
var req systemReq.SetMenuAuthorities
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
if req.MenuId == 0 {
|
||||
response.FailWithMessage("菜单ID不能为空", c)
|
||||
return
|
||||
}
|
||||
if err := menuService.SetMenuAuthorities(req.MenuId, req.AuthorityIds); err != nil {
|
||||
global.GVA_LOG.Error("设置失败!", zap.Error(err))
|
||||
response.FailWithMessage("设置失败"+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("设置成功", c)
|
||||
}
|
||||
|
||||
// GetMenuList
|
||||
// @Tags Menu
|
||||
// @Summary 分页获取基础menu列表
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"git.echol.cn/loser/st/server/model/common/response"
|
||||
"git.echol.cn/loser/st/server/model/system/request"
|
||||
@@ -55,6 +57,17 @@ func (s *SkillsApi) SaveSkill(c *gin.Context) {
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) DeleteSkill(c *gin.Context) {
|
||||
var req request.SkillDeleteRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if err := skillsService.Delete(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("删除技能失败", zap.Error(err))
|
||||
response.FailWithMessage("删除技能失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) CreateScript(c *gin.Context) {
|
||||
var req request.SkillScriptCreateRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
@@ -217,3 +230,34 @@ func (s *SkillsApi) SaveGlobalConstraint(c *gin.Context) {
|
||||
}
|
||||
response.OkWithMessage("保存成功", c)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) PackageSkill(c *gin.Context) {
|
||||
var req request.SkillPackageRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
|
||||
fileName, data, err := skillsService.Package(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("打包技能失败", zap.Error(err))
|
||||
response.FailWithMessage("打包技能失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "application/zip")
|
||||
c.Header("Content-Disposition", "attachment; filename=\""+fileName+"\"")
|
||||
c.Data(http.StatusOK, "application/zip", data)
|
||||
}
|
||||
|
||||
func (s *SkillsApi) DownloadOnlineSkill(c *gin.Context) {
|
||||
var req request.DownloadOnlineSkillReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.FailWithMessage("参数错误", c)
|
||||
return
|
||||
}
|
||||
|
||||
if err := skillsService.DownloadOnlineSkill(c.Request.Context(), req); err != nil {
|
||||
global.GVA_LOG.Error("下载在线技能失败", zap.Error(err))
|
||||
response.FailWithMessage("下载在线技能失败: "+err.Error(), c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("下载成功", c)
|
||||
}
|
||||
|
||||
@@ -12,5 +12,4 @@ type System struct {
|
||||
UseMongo bool `mapstructure:"use-mongo" json:"use-mongo" yaml:"use-mongo"` // 使用mongo
|
||||
UseStrictAuth bool `mapstructure:"use-strict-auth" json:"use-strict-auth" yaml:"use-strict-auth"` // 使用树形角色分配模式
|
||||
DisableAutoMigrate bool `mapstructure:"disable-auto-migrate" json:"disable-auto-migrate" yaml:"disable-auto-migrate"` // 自动迁移数据库表结构,生产环境建议设为false,手动迁移
|
||||
DataDir string `mapstructure:"data-dir" json:"data-dir" yaml:"data-dir"` // 数据目录
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ func RunServer() {
|
||||
address := fmt.Sprintf(":%d", global.GVA_CONFIG.System.Addr)
|
||||
|
||||
fmt.Printf(`
|
||||
欢迎使用 gin-vue-admin
|
||||
当前版本:%s
|
||||
插件市场:https://plugin.gin-vue-admin.com
|
||||
默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
|
||||
默认MCP SSE地址:http://127.0.0.1%s%s
|
||||
默认MCP Message地址:http://127.0.0.1%s%s
|
||||
|
||||
@@ -4,7 +4,7 @@ package global
|
||||
// 目前只有Version正式使用 其余为预留
|
||||
const (
|
||||
// Version 当前版本号
|
||||
Version = "v2.8.9"
|
||||
Version = "v2.9.0"
|
||||
// AppName 应用名称
|
||||
AppName = "Gin-Vue-Admin"
|
||||
// Description 应用描述
|
||||
|
||||
@@ -6,7 +6,7 @@ toolchain go1.24.2
|
||||
|
||||
require (
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
|
||||
github.com/aws/aws-sdk-go v1.55.6
|
||||
github.com/aws/aws-sdk-go v1.55.8
|
||||
github.com/casbin/casbin/v2 v2.103.0
|
||||
github.com/casbin/gorm-adapter/v3 v3.32.0
|
||||
github.com/dzwvip/gorm-oracle v0.1.2
|
||||
@@ -20,13 +20,11 @@ require (
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-obs v3.24.9+incompatible
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mark3labs/mcp-go v0.41.1
|
||||
github.com/mholt/archives v0.1.1
|
||||
github.com/minio/minio-go/v7 v7.0.84
|
||||
github.com/mojocn/base64Captcha v1.3.8
|
||||
github.com/otiai10/copy v1.14.1
|
||||
github.com/pgvector/pgvector-go v0.3.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/qiniu/go-sdk/v7 v7.25.2
|
||||
github.com/qiniu/qmgo v1.1.9
|
||||
@@ -53,6 +51,7 @@ require (
|
||||
gorm.io/driver/mysql v1.5.7
|
||||
gorm.io/driver/postgres v1.5.11
|
||||
gorm.io/driver/sqlserver v1.5.4
|
||||
gorm.io/gen v0.3.26
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
||||
@@ -121,7 +120,6 @@ require (
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16 // indirect
|
||||
github.com/microsoft/go-mssqldb v1.8.0 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minlz v1.0.0 // indirect
|
||||
@@ -174,13 +172,14 @@ require (
|
||||
golang.org/x/arch v0.13.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
||||
golang.org/x/image v0.23.0 // indirect
|
||||
golang.org/x/net v0.37.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.31.0 // indirect
|
||||
golang.org/x/tools v0.29.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gorm.io/driver/sqlite v1.5.0 // indirect
|
||||
gorm.io/hints v1.1.2 // indirect
|
||||
gorm.io/plugin/dbresolver v1.5.3 // indirect
|
||||
modernc.org/fileutil v1.3.0 // indirect
|
||||
modernc.org/libc v1.61.9 // indirect
|
||||
|
||||
@@ -15,8 +15,6 @@ cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
entgo.io/ent v0.14.3 h1:wokAV/kIlH9TeklJWGGS7AYJdVckr0DloWjIcO9iIIQ=
|
||||
entgo.io/ent v0.14.3/go.mod h1:aDPE/OziPEu8+OWbzy4UlvWmD2/kbRuWfK2A40hcxJM=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
@@ -56,8 +54,8 @@ github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F
|
||||
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
|
||||
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
|
||||
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
@@ -152,10 +150,6 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z
|
||||
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-pg/pg/v10 v10.11.0 h1:CMKJqLgTrfpE/aOVeLdybezR2om071Vh38OLZjsyMI0=
|
||||
github.com/go-pg/pg/v10 v10.11.0/go.mod h1:4BpHRoxE61y4Onpof3x1a2SQvi9c+q1dJnrNdMjsroA=
|
||||
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
|
||||
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
@@ -214,9 +208,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
@@ -284,8 +277,6 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@@ -322,8 +313,8 @@ github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgx
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
@@ -377,8 +368,6 @@ github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pgvector/pgvector-go v0.3.0 h1:Ij+Yt78R//uYqs3Zk35evZFvr+G0blW0OUN+Q2D1RWc=
|
||||
github.com/pgvector/pgvector-go v0.3.0/go.mod h1:duFy+PXWfW7QQd5ibqutBO4GxLsUZ9RVXhFZGIBsWSA=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
@@ -481,8 +470,6 @@ github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZ
|
||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
|
||||
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
@@ -492,24 +479,8 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU=
|
||||
github.com/unrolled/secure v1.17.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
|
||||
github.com/uptrace/bun v1.1.12 h1:sOjDVHxNTuM6dNGaba0wUuz7KvDE1BmNu9Gqs2gJSXQ=
|
||||
github.com/uptrace/bun v1.1.12/go.mod h1:NPG6JGULBeQ9IU6yHp7YGELRa5Agmd7ATZdz4tGZ6z0=
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.1.12 h1:m/CM1UfOkoBTglGO5CUTKnIKKOApOYxkcP2qn0F9tJk=
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.1.12/go.mod h1:Ij6WIxQILxLlL2frUBxUBOZJtLElD2QQNDcu/PWDHTc=
|
||||
github.com/uptrace/bun/driver/pgdriver v1.1.12 h1:3rRWB1GK0psTJrHwxzNfEij2MLibggiLdTqjTtfHc1w=
|
||||
github.com/uptrace/bun/driver/pgdriver v1.1.12/go.mod h1:ssYUP+qwSEgeDDS1xm2XBip9el1y9Mi5mTAvLoiADLM=
|
||||
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
|
||||
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
||||
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
|
||||
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
@@ -606,8 +577,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -636,8 +607,8 @@ golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -761,8 +732,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -827,12 +798,17 @@ gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c=
|
||||
gorm.io/driver/sqlite v1.5.0/go.mod h1:kDMDfntV9u/vuMmz8APHtHF0b4nyBB7sfCieC6G8k8I=
|
||||
gorm.io/driver/sqlserver v1.5.4 h1:xA+Y1KDNspv79q43bPyjDMUgHoYHLhXYmdFcYPobg8g=
|
||||
gorm.io/driver/sqlserver v1.5.4/go.mod h1:+frZ/qYmuna11zHPlh5oc2O6ZA/lS88Keb0XSH1Zh/g=
|
||||
gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY=
|
||||
gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
gorm.io/hints v1.1.2 h1:b5j0kwk5p4+3BtDtYqqfY+ATSxjj+6ptPgVveuynn9o=
|
||||
gorm.io/hints v1.1.2/go.mod h1:/ARdpUHAtyEMCh5NNi3tI7FsGh+Cj/MIUlvNxCNCFWg=
|
||||
gorm.io/plugin/dbresolver v1.5.3 h1:wFwINGZZmttuu9h7XpvbDHd8Lf9bb8GNzp/NpAMV2wU=
|
||||
gorm.io/plugin/dbresolver v1.5.3/go.mod h1:TSrVhaUg2DZAWP3PrHlDlITEJmNOkL0tFTjvTEsQ4XE=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -840,8 +816,6 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
|
||||
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
|
||||
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
|
||||
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.23.13 h1:PFiaemQwE/jdwi8XEHyEV+qYWoIuikLP3T4rvDeJb00=
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"git.echol.cn/loser/st/server/model/example"
|
||||
sysModel "git.echol.cn/loser/st/server/model/system"
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/model"
|
||||
"git.echol.cn/loser/st/server/service/system"
|
||||
adapter "github.com/casbin/gorm-adapter/v3"
|
||||
"gorm.io/gorm"
|
||||
@@ -64,6 +65,8 @@ func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error
|
||||
example.ExaFileChunk{},
|
||||
example.ExaFileUploadAndDownload{},
|
||||
example.ExaAttachmentCategory{},
|
||||
|
||||
model.Info{},
|
||||
}
|
||||
for _, t := range tables {
|
||||
_ = db.AutoMigrate(&t)
|
||||
@@ -103,6 +106,8 @@ func (e *ensureTables) TableCreated(ctx context.Context) bool {
|
||||
example.ExaFileChunk{},
|
||||
example.ExaFileUploadAndDownload{},
|
||||
example.ExaAttachmentCategory{},
|
||||
|
||||
model.Info{},
|
||||
}
|
||||
yes := true
|
||||
for _, t := range tables {
|
||||
|
||||
@@ -35,21 +35,12 @@ func (fs justFilesFilesystem) Open(name string) (http.File, error) {
|
||||
|
||||
func Routers() *gin.Engine {
|
||||
Router := gin.New()
|
||||
|
||||
// 设置文件上传大小限制(10MB)
|
||||
Router.MaxMultipartMemory = 10 << 20 // 10 MB
|
||||
|
||||
// 使用自定义的 Recovery 中间件,记录 panic 并入库
|
||||
Router.Use(middleware.GinRecovery(true))
|
||||
if gin.Mode() == gin.DebugMode {
|
||||
Router.Use(gin.Logger())
|
||||
}
|
||||
|
||||
// 跨域配置(前台应用需要)
|
||||
// 必须在静态文件路由之前注册,否则静态文件跨域会失败
|
||||
Router.Use(middleware.Cors())
|
||||
global.GVA_LOG.Info("use middleware cors")
|
||||
|
||||
if !global.GVA_CONFIG.MCP.Separate {
|
||||
|
||||
sseServer := McpRun()
|
||||
@@ -67,25 +58,6 @@ func Routers() *gin.Engine {
|
||||
systemRouter := router.RouterGroupApp.System
|
||||
exampleRouter := router.RouterGroupApp.Example
|
||||
appRouter := router.RouterGroupApp.App // 前台应用路由
|
||||
|
||||
// SillyTavern 核心脚本静态文件服务
|
||||
// 所有核心文件存储在 data/st-core-scripts/ 下,完全独立于 web-app/ 目录
|
||||
stCorePath := "data/st-core-scripts"
|
||||
if _, err := os.Stat(stCorePath); err == nil {
|
||||
Router.Static("/scripts", stCorePath+"/scripts")
|
||||
Router.Static("/css", stCorePath+"/css")
|
||||
Router.Static("/img", stCorePath+"/img")
|
||||
Router.Static("/webfonts", stCorePath+"/webfonts")
|
||||
Router.Static("/lib", stCorePath+"/lib") // SillyTavern 依赖的第三方库
|
||||
Router.Static("/locales", stCorePath+"/locales") // 国际化文件
|
||||
Router.StaticFile("/script.js", stCorePath+"/script.js") // SillyTavern 主入口
|
||||
Router.StaticFile("/lib.js", stCorePath+"/lib.js") // Webpack 编译后的 lib.js
|
||||
global.GVA_LOG.Info("SillyTavern 核心脚本服务已启动: " + stCorePath)
|
||||
} else {
|
||||
global.GVA_LOG.Warn("SillyTavern 核心脚本目录不存在: " + stCorePath)
|
||||
}
|
||||
|
||||
// 管理后台前端静态文件(web)
|
||||
// 如果想要不使用nginx代理前端网页,可以修改 web/.env.production 下的
|
||||
// VUE_APP_BASE_API = /
|
||||
// VUE_APP_BASE_PATH = http://localhost
|
||||
@@ -95,7 +67,10 @@ func Routers() *gin.Engine {
|
||||
// Router.StaticFile("/", "./dist/index.html") // 前端网页入口页面
|
||||
|
||||
Router.StaticFS(global.GVA_CONFIG.Local.StorePath, justFilesFilesystem{http.Dir(global.GVA_CONFIG.Local.StorePath)}) // Router.Use(middleware.LoadTls()) // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件")
|
||||
|
||||
// 跨域,如需跨域可以打开下面的注释
|
||||
Router.Use(middleware.Cors()) // 直接放行全部跨域请求
|
||||
//Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求
|
||||
global.GVA_LOG.Info("use middleware cors")
|
||||
docs.SwaggerInfo.BasePath = global.GVA_CONFIG.System.RouterPrefix
|
||||
Router.GET(global.GVA_CONFIG.System.RouterPrefix+"/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
global.GVA_LOG.Info("register swagger handler")
|
||||
@@ -137,13 +112,12 @@ func Routers() *gin.Engine {
|
||||
systemRouter.InitSysErrorRouter(PrivateGroup, PublicGroup) // 错误日志
|
||||
systemRouter.InitLoginLogRouter(PrivateGroup) // 登录日志
|
||||
systemRouter.InitApiTokenRouter(PrivateGroup) // apiToken签发
|
||||
systemRouter.InitSkillsRouter(PrivateGroup) // Skills 定义器
|
||||
systemRouter.InitSkillsRouter(PrivateGroup, PublicGroup) // Skills 定义器
|
||||
exampleRouter.InitCustomerRouter(PrivateGroup) // 客户路由
|
||||
exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
|
||||
exampleRouter.InitAttachmentCategoryRouterRouter(PrivateGroup) // 文件上传下载分类
|
||||
|
||||
}
|
||||
|
||||
// 前台应用路由(新增)
|
||||
{
|
||||
appGroup := PublicGroup.Group("app") // 统一使用 /app 前缀
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
|
||||
// PageInfo Paging common input parameter structure
|
||||
type PageInfo struct {
|
||||
Page int `json:"page" form:"page,default=1"` // 页码
|
||||
PageSize int `json:"pageSize" form:"pageSize,default=20"` // 每页大小
|
||||
Keyword string `json:"keyword" form:"keyword"` // 关键字
|
||||
Page int `json:"page" form:"page"` // 页码
|
||||
PageSize int `json:"pageSize" form:"pageSize"` // 每页大小
|
||||
Keyword string `json:"keyword" form:"keyword"` // 关键字
|
||||
}
|
||||
|
||||
func (r *PageInfo) Paginate() func(db *gorm.DB) *gorm.DB {
|
||||
|
||||
@@ -12,3 +12,10 @@ type SearchApiParams struct {
|
||||
OrderKey string `json:"orderKey"` // 排序
|
||||
Desc bool `json:"desc"` // 排序方式:升序false(默认)|降序true
|
||||
}
|
||||
|
||||
// SetApiAuthorities 通过API路径和方法全量覆盖关联角色列表
|
||||
type SetApiAuthorities struct {
|
||||
Path string `json:"path" form:"path"` // API路径
|
||||
Method string `json:"method" form:"method"` // 请求方法
|
||||
AuthorityIds []uint `json:"authorityIds" form:"authorityIds"` // 角色ID列表
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@ type AddMenuAuthorityInfo struct {
|
||||
AuthorityId uint `json:"authorityId"` // 角色ID
|
||||
}
|
||||
|
||||
// SetMenuAuthorities 通过菜单ID全量覆盖关联角色列表
|
||||
type SetMenuAuthorities struct {
|
||||
MenuId uint `json:"menuId" form:"menuId"` // 菜单ID
|
||||
AuthorityIds []uint `json:"authorityIds" form:"authorityIds"` // 角色ID列表
|
||||
}
|
||||
|
||||
func DefaultMenu() []system.SysBaseMenu {
|
||||
return []system.SysBaseMenu{{
|
||||
GVA_MODEL: global.GVA_MODEL{ID: 1},
|
||||
|
||||
@@ -11,6 +11,16 @@ type SkillDetailRequest struct {
|
||||
Skill string `json:"skill"`
|
||||
}
|
||||
|
||||
type SkillDeleteRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
}
|
||||
|
||||
type SkillPackageRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
}
|
||||
|
||||
type SkillSaveRequest struct {
|
||||
Tool string `json:"tool"`
|
||||
Skill string `json:"skill"`
|
||||
@@ -62,3 +72,9 @@ type SkillGlobalConstraintSaveRequest struct {
|
||||
Content string `json:"content"`
|
||||
SyncTools []string `json:"syncTools"`
|
||||
}
|
||||
|
||||
type DownloadOnlineSkillReq struct {
|
||||
Tool string `json:"tool" binding:"required"`
|
||||
ID uint `json:"id" binding:"required"`
|
||||
Version string `json:"version" binding:"required"`
|
||||
}
|
||||
|
||||
@@ -66,4 +66,12 @@ type GetUserList struct {
|
||||
NickName string `json:"nickName" form:"nickName"`
|
||||
Phone string `json:"phone" form:"phone"`
|
||||
Email string `json:"email" form:"email"`
|
||||
OrderKey string `json:"orderKey" form:"orderKey"` // 排序
|
||||
Desc bool `json:"desc" form:"desc"` // 排序方式:升序false(默认)|降序true
|
||||
}
|
||||
|
||||
// SetRoleUsers 通过角色ID全量覆盖关联用户列表
|
||||
type SetRoleUsers struct {
|
||||
AuthorityId uint `json:"authorityId" form:"authorityId"` // 角色ID
|
||||
UserIds []uint `json:"userIds" form:"userIds"` // 用户ID列表
|
||||
}
|
||||
|
||||
10
server/plugin/announcement/api/enter.go
Normal file
10
server/plugin/announcement/api/enter.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package api
|
||||
|
||||
import "git.echol.cn/loser/st/server/plugin/announcement/service"
|
||||
|
||||
var (
|
||||
Api = new(api)
|
||||
serviceInfo = service.Service.Info
|
||||
)
|
||||
|
||||
type api struct{ Info info }
|
||||
183
server/plugin/announcement/api/info.go
Normal file
183
server/plugin/announcement/api/info.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"git.echol.cn/loser/st/server/model/common/response"
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/model"
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/model/request"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var Info = new(info)
|
||||
|
||||
type info struct{}
|
||||
|
||||
// CreateInfo 创建公告
|
||||
// @Tags Info
|
||||
// @Summary 创建公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.Info true "创建公告"
|
||||
// @Success 200 {object} response.Response{msg=string} "创建成功"
|
||||
// @Router /info/createInfo [post]
|
||||
func (a *info) CreateInfo(c *gin.Context) {
|
||||
var info model.Info
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = serviceInfo.CreateInfo(&info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("创建失败!", zap.Error(err))
|
||||
response.FailWithMessage("创建失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("创建成功", c)
|
||||
}
|
||||
|
||||
// DeleteInfo 删除公告
|
||||
// @Tags Info
|
||||
// @Summary 删除公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.Info true "删除公告"
|
||||
// @Success 200 {object} response.Response{msg=string} "删除成功"
|
||||
// @Router /info/deleteInfo [delete]
|
||||
func (a *info) DeleteInfo(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
err := serviceInfo.DeleteInfo(ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("删除成功", c)
|
||||
}
|
||||
|
||||
// DeleteInfoByIds 批量删除公告
|
||||
// @Tags Info
|
||||
// @Summary 批量删除公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "批量删除成功"
|
||||
// @Router /info/deleteInfoByIds [delete]
|
||||
func (a *info) DeleteInfoByIds(c *gin.Context) {
|
||||
IDs := c.QueryArray("IDs[]")
|
||||
if err := serviceInfo.DeleteInfoByIds(IDs); err != nil {
|
||||
global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
|
||||
response.FailWithMessage("批量删除失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("批量删除成功", c)
|
||||
}
|
||||
|
||||
// UpdateInfo 更新公告
|
||||
// @Tags Info
|
||||
// @Summary 更新公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.Info true "更新公告"
|
||||
// @Success 200 {object} response.Response{msg=string} "更新成功"
|
||||
// @Router /info/updateInfo [put]
|
||||
func (a *info) UpdateInfo(c *gin.Context) {
|
||||
var info model.Info
|
||||
err := c.ShouldBindJSON(&info)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
err = serviceInfo.UpdateInfo(info)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("更新失败!", zap.Error(err))
|
||||
response.FailWithMessage("更新失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithMessage("更新成功", c)
|
||||
}
|
||||
|
||||
// FindInfo 用id查询公告
|
||||
// @Tags Info
|
||||
// @Summary 用id查询公告
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query model.Info true "用id查询公告"
|
||||
// @Success 200 {object} response.Response{data=model.Info,msg=string} "查询成功"
|
||||
// @Router /info/findInfo [get]
|
||||
func (a *info) FindInfo(c *gin.Context) {
|
||||
ID := c.Query("ID")
|
||||
reinfo, err := serviceInfo.GetInfo(ID)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(reinfo, c)
|
||||
}
|
||||
|
||||
// GetInfoList 分页获取公告列表
|
||||
// @Tags Info
|
||||
// @Summary 分页获取公告列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.InfoSearch true "分页获取公告列表"
|
||||
// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "获取成功"
|
||||
// @Router /info/getInfoList [get]
|
||||
func (a *info) GetInfoList(c *gin.Context) {
|
||||
var pageInfo request.InfoSearch
|
||||
err := c.ShouldBindQuery(&pageInfo)
|
||||
if err != nil {
|
||||
response.FailWithMessage(err.Error(), c)
|
||||
return
|
||||
}
|
||||
list, total, err := serviceInfo.GetInfoInfoList(pageInfo)
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("获取失败!", zap.Error(err))
|
||||
response.FailWithMessage("获取失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithDetailed(response.PageResult{
|
||||
List: list,
|
||||
Total: total,
|
||||
Page: pageInfo.Page,
|
||||
PageSize: pageInfo.PageSize,
|
||||
}, "获取成功", c)
|
||||
}
|
||||
|
||||
// GetInfoDataSource 获取Info的数据源
|
||||
// @Tags Info
|
||||
// @Summary 获取Info的数据源
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "查询成功"
|
||||
// @Router /info/getInfoDataSource [get]
|
||||
func (a *info) GetInfoDataSource(c *gin.Context) {
|
||||
// 此接口为获取数据源定义的数据
|
||||
dataSource, err := serviceInfo.GetInfoDataSource()
|
||||
if err != nil {
|
||||
global.GVA_LOG.Error("查询失败!", zap.Error(err))
|
||||
response.FailWithMessage("查询失败", c)
|
||||
return
|
||||
}
|
||||
response.OkWithData(dataSource, c)
|
||||
}
|
||||
|
||||
// GetInfoPublic 不需要鉴权的公告接口
|
||||
// @Tags Info
|
||||
// @Summary 不需要鉴权的公告接口
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.InfoSearch true "分页获取公告列表"
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /info/getInfoPublic [get]
|
||||
func (a *info) GetInfoPublic(c *gin.Context) {
|
||||
// 此接口不需要鉴权 示例为返回了一个固定的消息接口,一般本接口用于C端服务,需要自己实现业务逻辑
|
||||
response.OkWithDetailed(gin.H{"info": "不需要鉴权的公告接口信息"}, "获取成功", c)
|
||||
}
|
||||
4
server/plugin/announcement/config/config.go
Normal file
4
server/plugin/announcement/config/config.go
Normal file
@@ -0,0 +1,4 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
}
|
||||
18
server/plugin/announcement/gen/gen.go
Normal file
18
server/plugin/announcement/gen/gen.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath" //go:generate go mod tidy
|
||||
|
||||
"gorm.io/gen"
|
||||
//go:generate go mod download
|
||||
//go:generate go run gen.go
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/model"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g := gen.NewGenerator(gen.Config{OutPath: filepath.Join("..", "..", "..", "announcement", "blender", "model", "dao"), Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface})
|
||||
g.ApplyBasic(
|
||||
new(model.Info),
|
||||
)
|
||||
g.Execute()
|
||||
}
|
||||
50
server/plugin/announcement/initialize/api.go
Normal file
50
server/plugin/announcement/initialize/api.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
model "git.echol.cn/loser/st/server/model/system"
|
||||
"git.echol.cn/loser/st/server/plugin/plugin-tool/utils"
|
||||
)
|
||||
|
||||
func Api(ctx context.Context) {
|
||||
entities := []model.SysApi{
|
||||
{
|
||||
Path: "/info/createInfo",
|
||||
Description: "新建公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "POST",
|
||||
},
|
||||
{
|
||||
Path: "/info/deleteInfo",
|
||||
Description: "删除公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "DELETE",
|
||||
},
|
||||
{
|
||||
Path: "/info/deleteInfoByIds",
|
||||
Description: "批量删除公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "DELETE",
|
||||
},
|
||||
{
|
||||
Path: "/info/updateInfo",
|
||||
Description: "更新公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "PUT",
|
||||
},
|
||||
{
|
||||
Path: "/info/findInfo",
|
||||
Description: "根据ID获取公告",
|
||||
ApiGroup: "公告",
|
||||
Method: "GET",
|
||||
},
|
||||
{
|
||||
Path: "/info/getInfoList",
|
||||
Description: "获取公告列表",
|
||||
ApiGroup: "公告",
|
||||
Method: "GET",
|
||||
},
|
||||
}
|
||||
utils.RegisterApis(entities...)
|
||||
}
|
||||
13
server/plugin/announcement/initialize/dictionary.go
Normal file
13
server/plugin/announcement/initialize/dictionary.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
model "git.echol.cn/loser/st/server/model/system"
|
||||
"git.echol.cn/loser/st/server/plugin/plugin-tool/utils"
|
||||
)
|
||||
|
||||
func Dictionary(ctx context.Context) {
|
||||
entities := []model.SysDictionary{}
|
||||
utils.RegisterDictionaries(entities...)
|
||||
}
|
||||
21
server/plugin/announcement/initialize/gorm.go
Normal file
21
server/plugin/announcement/initialize/gorm.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/model"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Gorm(ctx context.Context) {
|
||||
err := global.GVA_DB.WithContext(ctx).AutoMigrate(
|
||||
new(model.Info),
|
||||
)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "注册表失败!")
|
||||
zap.L().Error(fmt.Sprintf("%+v", err))
|
||||
}
|
||||
}
|
||||
23
server/plugin/announcement/initialize/menu.go
Normal file
23
server/plugin/announcement/initialize/menu.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
model "git.echol.cn/loser/st/server/model/system"
|
||||
"git.echol.cn/loser/st/server/plugin/plugin-tool/utils"
|
||||
)
|
||||
|
||||
func Menu(ctx context.Context) {
|
||||
entities := []model.SysBaseMenu{
|
||||
{
|
||||
ParentId: 9,
|
||||
Path: "anInfo",
|
||||
Name: "anInfo",
|
||||
Hidden: false,
|
||||
Component: "plugin/announcement/view/info.vue",
|
||||
Sort: 5,
|
||||
Meta: model.Meta{Title: "公告管理", Icon: "box"},
|
||||
},
|
||||
}
|
||||
utils.RegisterMenus(entities...)
|
||||
}
|
||||
15
server/plugin/announcement/initialize/router.go
Normal file
15
server/plugin/announcement/initialize/router.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"git.echol.cn/loser/st/server/middleware"
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/router"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Router(engine *gin.Engine) {
|
||||
public := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("")
|
||||
private := engine.Group(global.GVA_CONFIG.System.RouterPrefix).Group("")
|
||||
private.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
|
||||
router.Router.Info.Init(public, private)
|
||||
}
|
||||
18
server/plugin/announcement/initialize/viper.go
Normal file
18
server/plugin/announcement/initialize/viper.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/plugin"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func Viper() {
|
||||
err := global.GVA_VP.UnmarshalKey("announcement", &plugin.Config)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "初始化配置文件失败!")
|
||||
zap.L().Error(fmt.Sprintf("%+v", err))
|
||||
}
|
||||
}
|
||||
20
server/plugin/announcement/model/info.go
Normal file
20
server/plugin/announcement/model/info.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"gorm.io/datatypes"
|
||||
)
|
||||
|
||||
// Info 公告 结构体
|
||||
type Info struct {
|
||||
global.GVA_MODEL
|
||||
Title string `json:"title" form:"title" gorm:"column:title;comment:公告标题;"` //标题
|
||||
Content string `json:"content" form:"content" gorm:"column:content;comment:公告内容;type:text;"` //内容
|
||||
UserID *int `json:"userID" form:"userID" gorm:"column:user_id;comment:发布者;"` //作者
|
||||
Attachments datatypes.JSON `json:"attachments" form:"attachments" gorm:"column:attachments;comment:相关附件;" swaggertype:"array,object"` //附件
|
||||
}
|
||||
|
||||
// TableName 公告 Info自定义表名 gva_announcements_info
|
||||
func (Info) TableName() string {
|
||||
return "gva_announcements_info"
|
||||
}
|
||||
13
server/plugin/announcement/model/request/info.go
Normal file
13
server/plugin/announcement/model/request/info.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.echol.cn/loser/st/server/model/common/request"
|
||||
)
|
||||
|
||||
type InfoSearch struct {
|
||||
StartCreatedAt *time.Time `json:"startCreatedAt" form:"startCreatedAt"`
|
||||
EndCreatedAt *time.Time `json:"endCreatedAt" form:"endCreatedAt"`
|
||||
request.PageInfo
|
||||
}
|
||||
33
server/plugin/announcement/plugin.go
Normal file
33
server/plugin/announcement/plugin.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package announcement
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/initialize"
|
||||
interfaces "git.echol.cn/loser/st/server/utils/plugin/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var _ interfaces.Plugin = (*plugin)(nil)
|
||||
|
||||
var Plugin = new(plugin)
|
||||
|
||||
type plugin struct{}
|
||||
|
||||
func init() {
|
||||
interfaces.Register(Plugin)
|
||||
}
|
||||
|
||||
func (p *plugin) Register(group *gin.Engine) {
|
||||
ctx := context.Background()
|
||||
// 如果需要配置文件,请到config.Config中填充配置结构,且到下方发放中填入其在config.yaml中的key
|
||||
// initialize.Viper()
|
||||
// 安装插件时候自动注册的api数据请到下方法.Api方法中实现
|
||||
initialize.Api(ctx)
|
||||
// 安装插件时候自动注册的Menu数据请到下方法.Menu方法中实现
|
||||
initialize.Menu(ctx)
|
||||
// 安装插件时候自动注册的Dictionary数据请到下方法.Dictionary方法中实现
|
||||
initialize.Dictionary(ctx)
|
||||
initialize.Gorm(ctx)
|
||||
initialize.Router(group)
|
||||
}
|
||||
5
server/plugin/announcement/plugin/plugin.go
Normal file
5
server/plugin/announcement/plugin/plugin.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package plugin
|
||||
|
||||
import "git.echol.cn/loser/st/server/plugin/announcement/config"
|
||||
|
||||
var Config config.Config
|
||||
10
server/plugin/announcement/router/enter.go
Normal file
10
server/plugin/announcement/router/enter.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package router
|
||||
|
||||
import "git.echol.cn/loser/st/server/plugin/announcement/api"
|
||||
|
||||
var (
|
||||
Router = new(router)
|
||||
apiInfo = api.Api.Info
|
||||
)
|
||||
|
||||
type router struct{ Info info }
|
||||
31
server/plugin/announcement/router/info.go
Normal file
31
server/plugin/announcement/router/info.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/st/server/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var Info = new(info)
|
||||
|
||||
type info struct{}
|
||||
|
||||
// Init 初始化 公告 路由信息
|
||||
func (r *info) Init(public *gin.RouterGroup, private *gin.RouterGroup) {
|
||||
{
|
||||
group := private.Group("info").Use(middleware.OperationRecord())
|
||||
group.POST("createInfo", apiInfo.CreateInfo) // 新建公告
|
||||
group.DELETE("deleteInfo", apiInfo.DeleteInfo) // 删除公告
|
||||
group.DELETE("deleteInfoByIds", apiInfo.DeleteInfoByIds) // 批量删除公告
|
||||
group.PUT("updateInfo", apiInfo.UpdateInfo) // 更新公告
|
||||
}
|
||||
{
|
||||
group := private.Group("info")
|
||||
group.GET("findInfo", apiInfo.FindInfo) // 根据ID获取公告
|
||||
group.GET("getInfoList", apiInfo.GetInfoList) // 获取公告列表
|
||||
}
|
||||
{
|
||||
group := public.Group("info")
|
||||
group.GET("getInfoDataSource", apiInfo.GetInfoDataSource) // 获取公告数据源
|
||||
group.GET("getInfoPublic", apiInfo.GetInfoPublic) // 获取公告列表
|
||||
}
|
||||
}
|
||||
5
server/plugin/announcement/service/enter.go
Normal file
5
server/plugin/announcement/service/enter.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package service
|
||||
|
||||
var Service = new(service)
|
||||
|
||||
type service struct{ Info info }
|
||||
78
server/plugin/announcement/service/info.go
Normal file
78
server/plugin/announcement/service/info.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"git.echol.cn/loser/st/server/global"
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/model"
|
||||
"git.echol.cn/loser/st/server/plugin/announcement/model/request"
|
||||
)
|
||||
|
||||
var Info = new(info)
|
||||
|
||||
type info struct{}
|
||||
|
||||
// CreateInfo 创建公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) CreateInfo(info *model.Info) (err error) {
|
||||
err = global.GVA_DB.Create(info).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteInfo 删除公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) DeleteInfo(ID string) (err error) {
|
||||
err = global.GVA_DB.Delete(&model.Info{}, "id = ?", ID).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteInfoByIds 批量删除公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) DeleteInfoByIds(IDs []string) (err error) {
|
||||
err = global.GVA_DB.Delete(&[]model.Info{}, "id in ?", IDs).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateInfo 更新公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) UpdateInfo(info model.Info) (err error) {
|
||||
err = global.GVA_DB.Model(&model.Info{}).Where("id = ?", info.ID).Updates(&info).Error
|
||||
return err
|
||||
}
|
||||
|
||||
// GetInfo 根据ID获取公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) GetInfo(ID string) (info model.Info, err error) {
|
||||
err = global.GVA_DB.Where("id = ?", ID).First(&info).Error
|
||||
return
|
||||
}
|
||||
|
||||
// GetInfoInfoList 分页获取公告记录
|
||||
// Author [piexlmax](https://github.com/piexlmax)
|
||||
func (s *info) GetInfoInfoList(info request.InfoSearch) (list []model.Info, total int64, err error) {
|
||||
limit := info.PageSize
|
||||
offset := info.PageSize * (info.Page - 1)
|
||||
// 创建db
|
||||
db := global.GVA_DB.Model(&model.Info{})
|
||||
var infos []model.Info
|
||||
// 如果有条件搜索 下方会自动创建搜索语句
|
||||
if info.StartCreatedAt != nil && info.EndCreatedAt != nil {
|
||||
db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
|
||||
}
|
||||
err = db.Count(&total).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
db = db.Limit(limit).Offset(offset)
|
||||
}
|
||||
err = db.Find(&infos).Error
|
||||
return infos, total, err
|
||||
}
|
||||
func (s *info) GetInfoDataSource() (res map[string][]map[string]any, err error) {
|
||||
res = make(map[string][]map[string]any)
|
||||
|
||||
userID := make([]map[string]any, 0)
|
||||
global.GVA_DB.Table("sys_users").Select("nick_name as label,id as value").Scan(&userID)
|
||||
res["userID"] = userID
|
||||
return
|
||||
}
|
||||
@@ -1 +1,5 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
_ "git.echol.cn/loser/st/server/plugin/announcement"
|
||||
)
|
||||
|
||||
@@ -22,10 +22,12 @@ func (s *ApiRouter) InitApiRouter(Router *gin.RouterGroup, RouterPub *gin.Router
|
||||
apiRouter.POST("getApiById", apiRouterApi.GetApiById) // 获取单条Api消息
|
||||
apiRouter.POST("updateApi", apiRouterApi.UpdateApi) // 更新api
|
||||
apiRouter.DELETE("deleteApisByIds", apiRouterApi.DeleteApisByIds) // 删除选中api
|
||||
apiRouter.POST("setApiRoles", apiRouterApi.SetApiRoles) // 全量覆盖API关联角色
|
||||
}
|
||||
{
|
||||
apiRouterWithoutRecord.POST("getAllApis", apiRouterApi.GetAllApis) // 获取所有api
|
||||
apiRouterWithoutRecord.POST("getApiList", apiRouterApi.GetApiList) // 获取Api列表
|
||||
apiRouterWithoutRecord.POST("getAllApis", apiRouterApi.GetAllApis) // 获取所有api
|
||||
apiRouterWithoutRecord.POST("getApiList", apiRouterApi.GetApiList) // 获取Api列表
|
||||
apiRouterWithoutRecord.GET("getApiRoles", apiRouterApi.GetApiRoles) // 获取API关联角色ID列表
|
||||
}
|
||||
{
|
||||
apiPublicRouterWithoutRecord.GET("freshCasbin", apiRouterApi.FreshCasbin) // 刷新casbin权限
|
||||
|
||||
@@ -16,8 +16,10 @@ func (s *AuthorityRouter) InitAuthorityRouter(Router *gin.RouterGroup) {
|
||||
authorityRouter.PUT("updateAuthority", authorityApi.UpdateAuthority) // 更新角色
|
||||
authorityRouter.POST("copyAuthority", authorityApi.CopyAuthority) // 拷贝角色
|
||||
authorityRouter.POST("setDataAuthority", authorityApi.SetDataAuthority) // 设置角色资源权限
|
||||
authorityRouter.POST("setRoleUsers", authorityApi.SetRoleUsers) // 全量覆盖角色关联用户
|
||||
}
|
||||
{
|
||||
authorityRouterWithoutRecord.POST("getAuthorityList", authorityApi.GetAuthorityList) // 获取角色列表
|
||||
authorityRouterWithoutRecord.POST("getAuthorityList", authorityApi.GetAuthorityList) // 获取角色列表
|
||||
authorityRouterWithoutRecord.GET("getUsersByAuthority", authorityApi.GetUsersByAuthority) // 获取角色关联用户ID列表
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ func (s *MenuRouter) InitMenuRouter(Router *gin.RouterGroup) (R gin.IRoutes) {
|
||||
menuRouter.POST("addMenuAuthority", authorityMenuApi.AddMenuAuthority) // 增加menu和角色关联关系
|
||||
menuRouter.POST("deleteBaseMenu", authorityMenuApi.DeleteBaseMenu) // 删除菜单
|
||||
menuRouter.POST("updateBaseMenu", authorityMenuApi.UpdateBaseMenu) // 更新菜单
|
||||
menuRouter.POST("setMenuRoles", authorityMenuApi.SetMenuRoles) // 全量覆盖菜单关联角色
|
||||
}
|
||||
{
|
||||
menuRouterWithoutRecord.POST("getMenu", authorityMenuApi.GetMenu) // 获取菜单树
|
||||
@@ -22,6 +23,7 @@ func (s *MenuRouter) InitMenuRouter(Router *gin.RouterGroup) (R gin.IRoutes) {
|
||||
menuRouterWithoutRecord.POST("getBaseMenuTree", authorityMenuApi.GetBaseMenuTree) // 获取用户动态路由
|
||||
menuRouterWithoutRecord.POST("getMenuAuthority", authorityMenuApi.GetMenuAuthority) // 获取指定角色menu
|
||||
menuRouterWithoutRecord.POST("getBaseMenuById", authorityMenuApi.GetBaseMenuById) // 根据id获取菜单
|
||||
menuRouterWithoutRecord.GET("getMenuRoles", authorityMenuApi.GetMenuRoles) // 获取菜单关联角色ID列表
|
||||
}
|
||||
return menuRouter
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ import "github.com/gin-gonic/gin"
|
||||
|
||||
type SkillsRouter struct{}
|
||||
|
||||
func (s *SkillsRouter) InitSkillsRouter(Router *gin.RouterGroup) {
|
||||
func (s *SkillsRouter) InitSkillsRouter(Router *gin.RouterGroup, pubRouter *gin.RouterGroup) {
|
||||
skillsRouter := Router.Group("skills")
|
||||
skillsRouterPub := pubRouter.Group("skills")
|
||||
{
|
||||
skillsRouter.GET("getTools", skillsApi.GetTools)
|
||||
skillsRouter.POST("getSkillList", skillsApi.GetSkillList)
|
||||
skillsRouter.POST("getSkillDetail", skillsApi.GetSkillDetail)
|
||||
skillsRouter.POST("saveSkill", skillsApi.SaveSkill)
|
||||
skillsRouter.POST("deleteSkill", skillsApi.DeleteSkill)
|
||||
skillsRouter.POST("createScript", skillsApi.CreateScript)
|
||||
skillsRouter.POST("getScript", skillsApi.GetScript)
|
||||
skillsRouter.POST("saveScript", skillsApi.SaveScript)
|
||||
@@ -25,5 +27,9 @@ func (s *SkillsRouter) InitSkillsRouter(Router *gin.RouterGroup) {
|
||||
skillsRouter.POST("saveTemplate", skillsApi.SaveTemplate)
|
||||
skillsRouter.POST("getGlobalConstraint", skillsApi.GetGlobalConstraint)
|
||||
skillsRouter.POST("saveGlobalConstraint", skillsApi.SaveGlobalConstraint)
|
||||
skillsRouter.POST("packageSkill", skillsApi.PackageSkill)
|
||||
}
|
||||
{
|
||||
skillsRouterPub.POST("downloadOnlineSkill", skillsApi.DownloadOnlineSkill)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,3 +331,82 @@ func (authorityService *AuthorityService) GetParentAuthorityID(authorityID uint)
|
||||
}
|
||||
return *authority.ParentId, nil
|
||||
}
|
||||
|
||||
// GetUserIdsByAuthorityId 获取拥有指定角色的所有用户ID
|
||||
func (authorityService *AuthorityService) GetUserIdsByAuthorityId(authorityId uint) (userIds []uint, err error) {
|
||||
var records []system.SysUserAuthority
|
||||
err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityId).Find(&records).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range records {
|
||||
userIds = append(userIds, r.SysUserId)
|
||||
}
|
||||
return userIds, nil
|
||||
}
|
||||
|
||||
// SetRoleUsers 全量覆盖某角色关联的用户列表
|
||||
// 入参:角色ID + 目标用户ID列表,保存时将该角色的关联关系完全替换为传入列表
|
||||
func (authorityService *AuthorityService) SetRoleUsers(authorityId uint, userIds []uint) error {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
// 1. 查出当前拥有该角色的所有用户ID
|
||||
var existingRecords []system.SysUserAuthority
|
||||
if err := tx.Where("sys_authority_authority_id = ?", authorityId).Find(&existingRecords).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentSet := make(map[uint]struct{})
|
||||
for _, r := range existingRecords {
|
||||
currentSet[r.SysUserId] = struct{}{}
|
||||
}
|
||||
|
||||
targetSet := make(map[uint]struct{})
|
||||
for _, id := range userIds {
|
||||
targetSet[id] = struct{}{}
|
||||
}
|
||||
|
||||
// 2. 删除该角色所有已有的用户关联
|
||||
if err := tx.Delete(&system.SysUserAuthority{}, "sys_authority_authority_id = ?", authorityId).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. 对被移除的用户:若该角色是其主角色,则将主角色切换为其剩余的其他角色
|
||||
for userId := range currentSet {
|
||||
if _, ok := targetSet[userId]; ok {
|
||||
continue // 仍在目标列表中,不处理
|
||||
}
|
||||
var user system.SysUser
|
||||
if err := tx.First(&user, "id = ?", userId).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
if user.AuthorityId == authorityId {
|
||||
// 从剩余关联(已删除当前角色后)中找另一个角色作为主角色
|
||||
var another system.SysUserAuthority
|
||||
if err := tx.Where("sys_user_id = ?", userId).First(&another).Error; err != nil {
|
||||
// 没有其他角色,主角色保持不变,不做处理
|
||||
continue
|
||||
}
|
||||
if err := tx.Model(&system.SysUser{}).Where("id = ?", userId).
|
||||
Update("authority_id", another.SysAuthorityAuthorityId).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 批量插入新的关联记录
|
||||
if len(userIds) > 0 {
|
||||
newRecords := make([]system.SysUserAuthority, 0, len(userIds))
|
||||
for _, userId := range userIds {
|
||||
newRecords = append(newRecords, system.SysUserAuthority{
|
||||
SysUserId: userId,
|
||||
SysAuthorityAuthorityId: authorityId,
|
||||
})
|
||||
}
|
||||
if err := tx.Create(&newRecords).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -171,3 +171,45 @@ func (casbinService *CasbinService) FreshCasbin() (err error) {
|
||||
err = e.LoadPolicy()
|
||||
return err
|
||||
}
|
||||
|
||||
// GetAuthoritiesByApi 获取拥有指定API权限的所有角色ID
|
||||
func (casbinService *CasbinService) GetAuthoritiesByApi(path, method string) (authorityIds []uint, err error) {
|
||||
var rules []gormadapter.CasbinRule
|
||||
err = global.GVA_DB.Where("ptype = 'p' AND v1 = ? AND v2 = ?", path, method).Find(&rules).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range rules {
|
||||
id, e := strconv.Atoi(r.V0)
|
||||
if e == nil {
|
||||
authorityIds = append(authorityIds, uint(id))
|
||||
}
|
||||
}
|
||||
return authorityIds, nil
|
||||
}
|
||||
|
||||
// SetApiAuthorities 全量覆盖某API关联的角色列表
|
||||
func (casbinService *CasbinService) SetApiAuthorities(path, method string, authorityIds []uint) error {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
// 1. 删除该API所有已有的角色关联
|
||||
if err := tx.Where("ptype = 'p' AND v1 = ? AND v2 = ?", path, method).Delete(&gormadapter.CasbinRule{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 2. 批量插入新的关联记录
|
||||
if len(authorityIds) > 0 {
|
||||
newRules := make([]gormadapter.CasbinRule, 0, len(authorityIds))
|
||||
for _, authorityId := range authorityIds {
|
||||
newRules = append(newRules, gormadapter.CasbinRule{
|
||||
Ptype: "p",
|
||||
V0: strconv.Itoa(int(authorityId)),
|
||||
V1: path,
|
||||
V2: method,
|
||||
})
|
||||
}
|
||||
if err := tx.Create(&newRules).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -315,6 +315,65 @@ func (menuService *MenuService) GetMenuAuthority(info *request.GetAuthorityId) (
|
||||
return menus, err
|
||||
}
|
||||
|
||||
// GetAuthoritiesByMenuId 获取拥有指定菜单的所有角色ID
|
||||
func (menuService *MenuService) GetAuthoritiesByMenuId(menuId uint) (authorityIds []uint, err error) {
|
||||
var records []system.SysAuthorityMenu
|
||||
err = global.GVA_DB.Where("sys_base_menu_id = ?", menuId).Find(&records).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, r := range records {
|
||||
id, e := strconv.Atoi(r.AuthorityId)
|
||||
if e == nil {
|
||||
authorityIds = append(authorityIds, uint(id))
|
||||
}
|
||||
}
|
||||
return authorityIds, nil
|
||||
}
|
||||
|
||||
// GetDefaultRouterAuthorityIds 获取将指定菜单设为首页的角色ID列表
|
||||
func (menuService *MenuService) GetDefaultRouterAuthorityIds(menuId uint) (authorityIds []uint, err error) {
|
||||
var menu system.SysBaseMenu
|
||||
err = global.GVA_DB.First(&menu, menuId).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var authorities []system.SysAuthority
|
||||
err = global.GVA_DB.Where("default_router = ?", menu.Name).Find(&authorities).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, auth := range authorities {
|
||||
authorityIds = append(authorityIds, auth.AuthorityId)
|
||||
}
|
||||
return authorityIds, nil
|
||||
}
|
||||
|
||||
// SetMenuAuthorities 全量覆盖某菜单关联的角色列表
|
||||
func (menuService *MenuService) SetMenuAuthorities(menuId uint, authorityIds []uint) error {
|
||||
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
|
||||
// 1. 删除该菜单所有已有的角色关联
|
||||
if err := tx.Where("sys_base_menu_id = ?", menuId).Delete(&system.SysAuthorityMenu{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
// 2. 批量插入新的关联记录
|
||||
if len(authorityIds) > 0 {
|
||||
menuIdStr := strconv.Itoa(int(menuId))
|
||||
newRecords := make([]system.SysAuthorityMenu, 0, len(authorityIds))
|
||||
for _, authorityId := range authorityIds {
|
||||
newRecords = append(newRecords, system.SysAuthorityMenu{
|
||||
MenuId: menuIdStr,
|
||||
AuthorityId: strconv.Itoa(int(authorityId)),
|
||||
})
|
||||
}
|
||||
if err := tx.Create(&newRecords).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UserAuthorityDefaultRouter 用户角色默认路由检查
|
||||
//
|
||||
// Author [SliverHorn](https://github.com/SliverHorn)
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -158,6 +163,107 @@ func (s *SkillsService) Save(_ context.Context, req request.SkillSaveRequest) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SkillsService) Delete(_ context.Context, req request.SkillDeleteRequest) error {
|
||||
if strings.TrimSpace(req.Tool) == "" {
|
||||
return errors.New("工具类型不能为空")
|
||||
}
|
||||
if !isSafeName(req.Skill) {
|
||||
return errors.New("技能名称不合法")
|
||||
}
|
||||
skillDir, err := s.skillDir(req.Tool, req.Skill)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := os.Stat(skillDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return errors.New("技能不存在")
|
||||
}
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return errors.New("技能目录异常")
|
||||
}
|
||||
return os.RemoveAll(skillDir)
|
||||
}
|
||||
|
||||
func (s *SkillsService) Package(_ context.Context, req request.SkillPackageRequest) (string, []byte, error) {
|
||||
if strings.TrimSpace(req.Tool) == "" {
|
||||
return "", nil, errors.New("工具类型不能为空")
|
||||
}
|
||||
if !isSafeName(req.Skill) {
|
||||
return "", nil, errors.New("技能名称不合法")
|
||||
}
|
||||
|
||||
skillDir, err := s.skillDir(req.Tool, req.Skill)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
info, err := os.Stat(skillDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return "", nil, errors.New("技能不存在")
|
||||
}
|
||||
return "", nil, err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return "", nil, errors.New("技能目录异常")
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
zw := zip.NewWriter(buf)
|
||||
|
||||
walkErr := filepath.WalkDir(skillDir, func(path string, d fs.DirEntry, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
rel, err := filepath.Rel(skillDir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rel == "." {
|
||||
return nil
|
||||
}
|
||||
zipName := filepath.ToSlash(rel)
|
||||
if d.IsDir() {
|
||||
_, err = zw.Create(strings.TrimSuffix(zipName, "/") + "/")
|
||||
return err
|
||||
}
|
||||
|
||||
fileInfo, err := d.Info()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header, err := zip.FileInfoHeader(fileInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header.Name = zipName
|
||||
header.Method = zip.Deflate
|
||||
|
||||
writer, err := zw.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = writer.Write(content)
|
||||
return err
|
||||
})
|
||||
if walkErr != nil {
|
||||
_ = zw.Close()
|
||||
return "", nil, walkErr
|
||||
}
|
||||
|
||||
if err = zw.Close(); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return req.Skill + ".zip", buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (s *SkillsService) CreateScript(_ context.Context, req request.SkillScriptCreateRequest) (string, string, error) {
|
||||
if !isSafeName(req.Skill) {
|
||||
return "", "", errors.New("技能名称不合法")
|
||||
@@ -279,6 +385,136 @@ func (s *SkillsService) SaveGlobalConstraint(_ context.Context, req request.Skil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SkillsService) DownloadOnlineSkill(_ context.Context, req request.DownloadOnlineSkillReq) error {
|
||||
skillsDir, err := s.toolSkillsDir(req.Tool)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
body, err := json.Marshal(map[string]interface{}{
|
||||
"plugin_id": req.ID,
|
||||
"version": req.Version,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("构建下载请求失败: %w", err)
|
||||
}
|
||||
|
||||
downloadReq, err := http.NewRequest(http.MethodPost, "https://plugin.gin-vue-admin.com/api/shopPlugin/downloadSkill", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("构建下载请求失败: %w", err)
|
||||
}
|
||||
downloadReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
downloadResp, err := http.DefaultClient.Do(downloadReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("下载技能失败: %w", err)
|
||||
}
|
||||
defer downloadResp.Body.Close()
|
||||
|
||||
if downloadResp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("下载技能失败, HTTP状态码: %d", downloadResp.StatusCode)
|
||||
}
|
||||
|
||||
metaBody, err := io.ReadAll(downloadResp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取下载结果失败: %w", err)
|
||||
}
|
||||
|
||||
var meta struct {
|
||||
Data struct {
|
||||
URL string `json:"url"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err = json.Unmarshal(metaBody, &meta); err != nil {
|
||||
return fmt.Errorf("解析下载结果失败: %w", err)
|
||||
}
|
||||
|
||||
realDownloadURL := strings.TrimSpace(meta.Data.URL)
|
||||
if realDownloadURL == "" {
|
||||
return errors.New("下载结果缺少 url")
|
||||
}
|
||||
|
||||
zipResp, err := http.Get(realDownloadURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("下载压缩包失败: %w", err)
|
||||
}
|
||||
defer zipResp.Body.Close()
|
||||
|
||||
if zipResp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("下载压缩包失败, HTTP状态码: %d", zipResp.StatusCode)
|
||||
}
|
||||
|
||||
tmpFile, err := os.CreateTemp("", "gva-skill-*.zip")
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建临时文件失败: %w", err)
|
||||
}
|
||||
tmpPath := tmpFile.Name()
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
if _, err = io.Copy(tmpFile, zipResp.Body); err != nil {
|
||||
tmpFile.Close()
|
||||
return fmt.Errorf("保存技能包失败: %w", err)
|
||||
}
|
||||
tmpFile.Close()
|
||||
|
||||
if err = extractZipToDir(tmpPath, skillsDir); err != nil {
|
||||
return fmt.Errorf("解压技能包失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractZipToDir(zipPath, destDir string) error {
|
||||
r, err := zip.OpenReader(zipPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
for _, f := range r.File {
|
||||
name := filepath.FromSlash(f.Name)
|
||||
if strings.Contains(name, "..") {
|
||||
continue
|
||||
}
|
||||
|
||||
target := filepath.Join(destDir, name)
|
||||
if !strings.HasPrefix(filepath.Clean(target), filepath.Clean(destDir)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
if err := os.MkdirAll(target, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(target), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
rc.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(out, rc)
|
||||
rc.Close()
|
||||
out.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SkillsService) toolSkillsDir(tool string) (string, error) {
|
||||
toolDir, ok := skillToolDirs[tool]
|
||||
if !ok {
|
||||
|
||||
@@ -109,7 +109,25 @@ func (userService *UserService) GetUserInfoList(info systemReq.GetUserList) (lis
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = db.Limit(limit).Offset(offset).Preload("Authorities").Preload("Authority").Find(&userList).Error
|
||||
|
||||
orderStr := "id desc"
|
||||
if info.OrderKey != "" {
|
||||
allowedOrders := map[string]bool{
|
||||
"id": true,
|
||||
"username": true,
|
||||
"nick_name": true,
|
||||
"phone": true,
|
||||
"email": true,
|
||||
}
|
||||
if allowedOrders[info.OrderKey] {
|
||||
orderStr = info.OrderKey
|
||||
if info.Desc {
|
||||
orderStr = info.OrderKey + " desc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = db.Limit(limit).Offset(offset).Order(orderStr).Preload("Authorities").Preload("Authority").Find(&userList).Error
|
||||
return userList, total, err
|
||||
}
|
||||
|
||||
|
||||
26
web-admin/.docker-compose/nginx/conf.d/my.conf
Normal file
26
web-admin/.docker-compose/nginx/conf.d/my.conf
Normal file
@@ -0,0 +1,26 @@
|
||||
server {
|
||||
listen 8080;
|
||||
server_name localhost;
|
||||
|
||||
#charset koi8-r;
|
||||
#access_log logs/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /api {
|
||||
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;
|
||||
rewrite ^/api/(.*)$ /$1 break; #重写
|
||||
proxy_pass http://177.7.0.12:8888; # 设置代理服务器的协议和地址
|
||||
}
|
||||
|
||||
location /api/swagger/index.html {
|
||||
proxy_pass http://127.0.0.1:8888/swagger/index.html;
|
||||
}
|
||||
}
|
||||
32
web-admin/.docker-compose/nginx/conf.d/nginx.conf
Normal file
32
web-admin/.docker-compose/nginx/conf.d/nginx.conf
Normal file
@@ -0,0 +1,32 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
#charset koi8-r;
|
||||
#access_log logs/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html/dist;
|
||||
add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0';
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
location /api {
|
||||
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;
|
||||
rewrite ^/api/(.*)$ /$1 break; #重写
|
||||
proxy_pass http://127.0.0.1:8888; # 设置代理服务器的协议和地址
|
||||
}
|
||||
location /form-generator {
|
||||
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:8888;
|
||||
}
|
||||
location /api/swagger/index.html {
|
||||
proxy_pass http://127.0.0.1:8888/swagger/index.html;
|
||||
}
|
||||
}
|
||||
1
web-admin/.dockerignore
Normal file
1
web-admin/.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
11
web-admin/.env.development
Normal file
11
web-admin/.env.development
Normal file
@@ -0,0 +1,11 @@
|
||||
ENV = 'development'
|
||||
VITE_CLI_PORT = 8080
|
||||
VITE_SERVER_PORT = 8888
|
||||
VITE_BASE_API = /api
|
||||
VITE_FILE_API = /api
|
||||
VITE_BASE_PATH = http://127.0.0.1
|
||||
VITE_POSITION = open
|
||||
VITE_EDITOR = code
|
||||
// VITE_EDITOR = webstorm 如果使用webstorm开发且要使用dom定位到代码行功能 请先自定添加 webstorm到环境变量 再将VITE_EDITOR值修改为webstorm
|
||||
// 如果使用docker-compose开发模式,设置为下面的地址或本机主机IP
|
||||
//VITE_BASE_PATH = http://177.7.0.12
|
||||
7
web-admin/.env.production
Normal file
7
web-admin/.env.production
Normal file
@@ -0,0 +1,7 @@
|
||||
ENV = 'production'
|
||||
|
||||
#下方为上线需要用到的程序代理前缀,一般用于nginx代理转发
|
||||
VITE_BASE_API = /api
|
||||
VITE_FILE_API = /api
|
||||
#下方修改为你的线上ip(如果需要在线使用表单构建工具时使用,其余情况无需使用以下环境变量)
|
||||
VITE_BASE_PATH = https://demo.gin-vue-admin.com
|
||||
5
web-admin/.gitignore
vendored
Normal file
5
web-admin/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/*
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
bun.lockb
|
||||
config.yaml
|
||||
12
web-admin/.prettierrc
Normal file
12
web-admin/.prettierrc
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "always",
|
||||
"vueIndentScriptAndStyle": true,
|
||||
"endOfLine": "lf"
|
||||
}
|
||||
25
web-admin/Dockerfile
Normal file
25
web-admin/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
||||
# 如果需要用 cicd ,请设置环境变量:
|
||||
# variables:
|
||||
# DOCKER_BUILDKIT: 1
|
||||
|
||||
FROM node:20-slim AS base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
FROM base AS prod-deps
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod
|
||||
|
||||
FROM base AS build
|
||||
COPY --from=prod-deps /app/node_modules /app/node_modules
|
||||
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install && pnpm run build
|
||||
|
||||
|
||||
FROM nginx:alpine
|
||||
LABEL MAINTAINER="bypanghu@163.com"
|
||||
COPY --from=base /app/.docker-compose/nginx/conf.d/my.conf /etc/nginx/conf.d/my.conf
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
RUN ls -al /usr/share/nginx/html
|
||||
106
web-admin/README.md
Normal file
106
web-admin/README.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# gin-vue-admin web
|
||||
|
||||
## Project setup
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Run your tests
|
||||
|
||||
```
|
||||
npm run test
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
整理代码结构
|
||||
|
||||
```lua
|
||||
web
|
||||
├── babel.config.js
|
||||
├── Dockerfile
|
||||
├── favicon.ico
|
||||
├── index.html -- 主页面
|
||||
├── limit.js -- 助手代码
|
||||
├── package.json -- 包管理器代码
|
||||
├── src -- 源代码
|
||||
│ ├── api -- api 组
|
||||
│ ├── App.vue -- 主页面
|
||||
│ ├── assets -- 静态资源
|
||||
│ ├── components -- 全局组件
|
||||
│ ├── core -- gva 组件包
|
||||
│ │ ├── config.js -- gva网站配置文件
|
||||
│ │ ├── gin-vue-admin.js -- 注册欢迎文件
|
||||
│ │ └── global.js -- 统一导入文件
|
||||
│ ├── directive -- v-auth 注册文件
|
||||
│ ├── main.js -- 主文件
|
||||
│ ├── permission.js -- 路由中间件
|
||||
│ ├── pinia -- pinia 状态管理器,取代vuex
|
||||
│ │ ├── index.js -- 入口文件
|
||||
│ │ └── modules -- modules
|
||||
│ │ ├── dictionary.js
|
||||
│ │ ├── router.js
|
||||
│ │ └── user.js
|
||||
│ ├── router -- 路由声明文件
|
||||
│ │ └── index.js
|
||||
│ ├── style -- 全局样式
|
||||
│ │ ├── base.scss
|
||||
│ │ ├── basics.scss
|
||||
│ │ ├── element_visiable.scss -- 此处可以全局覆盖 element-plus 样式
|
||||
│ │ ├── iconfont.css -- 顶部几个icon的样式文件
|
||||
│ │ ├── main.scss
|
||||
│ │ ├── mobile.scss
|
||||
│ │ └── newLogin.scss
|
||||
│ ├── utils -- 方法包库
|
||||
│ │ ├── asyncRouter.js -- 动态路由相关
|
||||
│ │ ├── bus.js -- 全局mitt声明文件
|
||||
│ │ ├── date.js -- 日期相关
|
||||
│ │ ├── dictionary.js -- 获取字典方法
|
||||
│ │ ├── downloadImg.js -- 下载图片方法
|
||||
│ │ ├── format.js -- 格式整理相关
|
||||
│ │ ├── image.js -- 图片相关方法
|
||||
│ │ ├── page.js -- 设置页面标题
|
||||
│ │ ├── request.js -- 请求
|
||||
│ │ └── stringFun.js -- 字符串文件
|
||||
| ├── view -- 主要view代码
|
||||
| | ├── about -- 关于我们
|
||||
| | ├── dashboard -- 面板
|
||||
| | ├── error -- 错误
|
||||
| | ├── example --上传案例
|
||||
| | ├── iconList -- icon列表
|
||||
| | ├── init -- 初始化数据
|
||||
| | | ├── index -- 新版本
|
||||
| | | ├── init -- 旧版本
|
||||
| | ├── layout -- layout约束页面
|
||||
| | | ├── aside
|
||||
| | | ├── bottomInfo -- bottomInfo
|
||||
| | | ├── screenfull -- 全屏设置
|
||||
| | | ├── setting -- 系统设置
|
||||
| | | └── index.vue -- base 约束
|
||||
| | ├── login --登录
|
||||
| | ├── person --个人中心
|
||||
| | ├── superAdmin -- 超级管理员操作
|
||||
| | ├── system -- 系统检测页面
|
||||
| | ├── systemTools -- 系统配置相关页面
|
||||
| | └── routerHolder.vue -- page 入口页面
|
||||
├── vite.config.js -- vite 配置文件
|
||||
└── yarn.lock
|
||||
|
||||
```
|
||||
4
web-admin/babel.config.js
Normal file
4
web-admin/babel.config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
presets: [],
|
||||
plugins: []
|
||||
}
|
||||
29
web-admin/eslint.config.mjs
Normal file
29
web-admin/eslint.config.mjs
Normal file
@@ -0,0 +1,29 @@
|
||||
import js from '@eslint/js'
|
||||
import pluginVue from 'eslint-plugin-vue'
|
||||
import globals from 'globals'
|
||||
|
||||
export default [
|
||||
js.configs.recommended,
|
||||
...pluginVue.configs['flat/essential'],
|
||||
{
|
||||
name: 'app/files-to-lint',
|
||||
files: ['**/*.{js,mjs,jsx,vue}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
globals: globals.node
|
||||
},
|
||||
rules: {
|
||||
'vue/max-attributes-per-line': 0,
|
||||
'vue/no-v-model-argument': 0,
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'no-lone-blocks': 'off',
|
||||
'no-extend-native': 'off',
|
||||
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'app/files-to-ignore',
|
||||
ignores: ['**/dist/**', '**/build/*.js', '**/src/assets/**', '**/public/**']
|
||||
}
|
||||
]
|
||||
115
web-admin/index.html
Normal file
115
web-admin/index.html
Normal file
@@ -0,0 +1,115 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-cn" class="transition-colors">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<meta
|
||||
content="Gin,Vue,Admin.Gin-Vue-Admin,GVA,gin-vue-admin,后台管理框架,vue后台管理框架,gin-vue-admin文档,gin-vue-admin首页,gin-vue-admin"
|
||||
name="keywords"
|
||||
/>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<title></title>
|
||||
<style>
|
||||
.transition-colors {
|
||||
transition-property: color, background-color, border-color,
|
||||
text-decoration-color, fill, stroke;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
--64f90c3645474bd5: #409eff;
|
||||
}
|
||||
#gva-loading-box {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
#loading-text {
|
||||
position: absolute;
|
||||
bottom: calc(50% - 100px);
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
#loading {
|
||||
position: absolute;
|
||||
top: calc(50% - 20px);
|
||||
left: calc(50% - 20px);
|
||||
}
|
||||
@keyframes loader {
|
||||
0% {
|
||||
left: -100px;
|
||||
}
|
||||
100% {
|
||||
left: 110%;
|
||||
}
|
||||
}
|
||||
#box {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: var(--64f90c3645474bd5);
|
||||
animation: animate 0.5s linear infinite;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
@keyframes animate {
|
||||
17% {
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
25% {
|
||||
transform: translateY(9px) rotate(22.5deg);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(18px) scale(1, 0.9) rotate(45deg);
|
||||
border-bottom-right-radius: 40px;
|
||||
}
|
||||
75% {
|
||||
transform: translateY(9px) rotate(67.5deg);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0) rotate(90deg);
|
||||
}
|
||||
}
|
||||
#shadow {
|
||||
width: 50px;
|
||||
height: 5px;
|
||||
background: #000;
|
||||
opacity: 0.1;
|
||||
position: absolute;
|
||||
top: 59px;
|
||||
left: 0;
|
||||
border-radius: 50%;
|
||||
animation: shadow 0.5s linear infinite;
|
||||
}
|
||||
.dark #shadow {
|
||||
background: #fff;
|
||||
}
|
||||
@keyframes shadow {
|
||||
50% {
|
||||
transform: scale(1.2, 1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="gva-loading-box">
|
||||
<div id="loading">
|
||||
<div id="shadow"></div>
|
||||
<div id="box"></div>
|
||||
</div>
|
||||
<div id="loading-text">系统正在加载中,请稍候...</div>
|
||||
</div>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="./src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
10
web-admin/jsconfig.json
Normal file
10
web-admin/jsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
38
web-admin/limit.js
Normal file
38
web-admin/limit.js
Normal file
@@ -0,0 +1,38 @@
|
||||
// 运行项目前通过node执行此脚本 (此脚本与 node_modules 目录同级)
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
const wfPath = path.resolve(__dirname, './node_modules/.bin')
|
||||
|
||||
fs.readdir(wfPath, (err, files) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
} else {
|
||||
if (files.length !== 0) {
|
||||
files.forEach((item) => {
|
||||
if (item.split('.')[1] === 'cmd') {
|
||||
replaceStr(`${wfPath}/${item}`, /"%_prog%"/, '%_prog%')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 参数:[文件路径、 需要修改的字符串、修改后的字符串] (替换对应文件内字符串的公共函数)
|
||||
function replaceStr(filePath, sourceRegx, targetSrt) {
|
||||
fs.readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
} else {
|
||||
let str = data.toString()
|
||||
str = str.replace(sourceRegx, targetSrt)
|
||||
fs.writeFile(filePath, str, (err) => {
|
||||
if (err) {
|
||||
console.log(err)
|
||||
} else {
|
||||
console.log('\x1B[42m%s\x1B[0m', '文件修改成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
20
web-admin/openDocument.js
Normal file
20
web-admin/openDocument.js
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
此文件受版权保护,未经授权禁止修改!如果您尚未获得授权,请通过微信(shouzi_1994)联系我们以购买授权。在未授权状态下,只需保留此代码,不会影响任何正常使用。
|
||||
未经授权的商用使用可能会被我们的资产搜索引擎爬取,并可能导致后续索赔。索赔金额将不低于高级授权费的十倍。请您遵守版权法律法规,尊重知识产权。
|
||||
*/
|
||||
|
||||
import child_process from 'child_process'
|
||||
|
||||
var url = 'https://www.gin-vue-admin.com'
|
||||
var cmd = ''
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
cmd = 'start'
|
||||
child_process.exec(cmd + ' ' + url)
|
||||
break
|
||||
|
||||
case 'darwin':
|
||||
cmd = 'open'
|
||||
child_process.exec(cmd + ' ' + url)
|
||||
break
|
||||
}
|
||||
87
web-admin/package.json
Normal file
87
web-admin/package.json
Normal file
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"name": "gin-vue-admin",
|
||||
"version": "2.9.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "node openDocument.js && vite --host --mode development",
|
||||
"serve": "node openDocument.js && vite --host --mode development",
|
||||
"build": "vite build --mode production",
|
||||
"limit-build": "npm install increase-memory-limit-fixbug cross-env -g && npm run fix-memory-limit && node ./limit && npm run build",
|
||||
"preview": "vite preview",
|
||||
"fix-memory-limit": "cross-env LIMIT=4096 increase-memory-limit"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@form-create/designer": "^3.2.6",
|
||||
"@form-create/element-ui": "^3.2.10",
|
||||
"@iconify/vue": "^5.0.0",
|
||||
"@unocss/transformer-directives": "^66.4.2",
|
||||
"@vue-office/docx": "^1.6.2",
|
||||
"@vue-office/excel": "^1.7.11",
|
||||
"@vue-office/pdf": "^2.0.2",
|
||||
"@vueuse/core": "^11.0.3",
|
||||
"@vueuse/integrations": "^12.0.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"ace-builds": "^1.36.4",
|
||||
"axios": "1.8.2",
|
||||
"chokidar": "^4.0.0",
|
||||
"core-js": "^3.38.1",
|
||||
"echarts": "5.5.1",
|
||||
"element-plus": "^2.10.2",
|
||||
"highlight.js": "^11.10.0",
|
||||
"install": "^0.13.0",
|
||||
"marked": "14.1.1",
|
||||
"marked-highlight": "^2.1.4",
|
||||
"mitt": "^3.0.1",
|
||||
"npm": "^11.3.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"path": "^0.12.7",
|
||||
"pinia": "^2.2.2",
|
||||
"qs": "^6.13.0",
|
||||
"screenfull": "^6.0.2",
|
||||
"sortablejs": "^1.15.3",
|
||||
"spark-md5": "^3.0.2",
|
||||
"universal-cookie": "^7",
|
||||
"vform3-builds": "^3.0.10",
|
||||
"vite-auto-import-svg": "^2.1.0",
|
||||
"vue": "^3.5.7",
|
||||
"vue-cropper": "^1.1.4",
|
||||
"vue-echarts": "^7.0.3",
|
||||
"vue-qr": "^4.0.9",
|
||||
"vue-router": "^4.4.3",
|
||||
"vue3-ace-editor": "^2.2.4",
|
||||
"vue3-sfc-loader": "^0.9.5",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.25.1",
|
||||
"@eslint/js": "^8.56.0",
|
||||
"@unocss/extractor-svelte": "^66.4.2",
|
||||
"@unocss/preset-wind3": "^66.4.2",
|
||||
"@unocss/vite": "^66.5.0",
|
||||
"@vitejs/plugin-legacy": "^6.0.0",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vue/cli-plugin-babel": "~5.0.8",
|
||||
"@vue/cli-plugin-eslint": "~5.0.8",
|
||||
"@vue/cli-plugin-router": "~5.0.8",
|
||||
"@vue/cli-plugin-vuex": "~5.0.8",
|
||||
"@vue/cli-service": "~5.0.8",
|
||||
"@vue/compiler-sfc": "^3.5.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"babel-plugin-import": "^1.13.8",
|
||||
"chalk": "^5.3.0",
|
||||
"dotenv": "^16.4.5",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-vue": "^9.19.2",
|
||||
"globals": "^16.3.0",
|
||||
"sass": "^1.78.0",
|
||||
"terser": "^5.31.6",
|
||||
"vite": "^6.2.3",
|
||||
"vite-check-multiple-dom": "0.2.1",
|
||||
"vite-plugin-banner": "^0.8.0",
|
||||
"vite-plugin-importer": "^0.2.5",
|
||||
"vite-plugin-vue-devtools": "^7.0.16"
|
||||
}
|
||||
}
|
||||
BIN
web-admin/public/favicon.ico
Normal file
BIN
web-admin/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
web-admin/public/logo.png
Normal file
BIN
web-admin/public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 75 KiB |
46
web-admin/src/App.vue
Normal file
46
web-admin/src/App.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div
|
||||
id="app"
|
||||
class="bg-gray-50 text-slate-700 !dark:text-slate-500 dark:bg-slate-800"
|
||||
>
|
||||
<el-config-provider :locale="zhCn" :size="appStore.config.global_size">
|
||||
<router-view />
|
||||
<Application />
|
||||
</el-config-provider>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||||
import Application from '@/components/application/index.vue'
|
||||
import {useAppStore} from '@/pinia'
|
||||
|
||||
const appStore = useAppStore()
|
||||
defineOptions({
|
||||
name: 'App'
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
// 引入初始化样式
|
||||
#app {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.gva-body-h {
|
||||
min-height: calc(100% - 3rem);
|
||||
}
|
||||
|
||||
.gva-container {
|
||||
height: calc(100% - 2.5rem);
|
||||
}
|
||||
|
||||
.gva-container2 {
|
||||
height: calc(100% - 4.5rem);
|
||||
}
|
||||
</style>
|
||||
206
web-admin/src/api/api.js
Normal file
206
web-admin/src/api/api.js
Normal file
@@ -0,0 +1,206 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
// @Tags api
|
||||
// @Summary 分页获取角色列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body modelInterface.PageInfo true "分页获取用户列表"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /api/getApiList [post]
|
||||
// {
|
||||
// page int
|
||||
// pageSize int
|
||||
// }
|
||||
export const getApiList = (data) => {
|
||||
return service({
|
||||
url: '/api/getApiList',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Api
|
||||
// @Summary 创建基础api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body api.CreateApiParams true "创建api"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /api/createApi [post]
|
||||
export const createApi = (data) => {
|
||||
return service({
|
||||
url: '/api/createApi',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags menu
|
||||
// @Summary 根据id获取菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body api.GetById true "根据id获取菜单"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /menu/getApiById [post]
|
||||
export const getApiById = (data) => {
|
||||
return service({
|
||||
url: '/api/getApiById',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Api
|
||||
// @Summary 更新api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body api.CreateApiParams true "更新api"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /api/updateApi [post]
|
||||
export const updateApi = (data) => {
|
||||
return service({
|
||||
url: '/api/updateApi',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Api
|
||||
// @Summary 更新api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body api.CreateApiParams true "更新api"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /api/setAuthApi [post]
|
||||
export const setAuthApi = (data) => {
|
||||
return service({
|
||||
url: '/api/setAuthApi',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Api
|
||||
// @Summary 获取所有的Api 不分页
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /api/getAllApis [post]
|
||||
export const getAllApis = (data) => {
|
||||
return service({
|
||||
url: '/api/getAllApis',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags Api
|
||||
// @Summary 删除指定api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body dbModel.Api true "删除api"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /api/deleteApi [post]
|
||||
export const deleteApi = (data) => {
|
||||
return service({
|
||||
url: '/api/deleteApi',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 删除选中Api
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /api/deleteApisByIds [delete]
|
||||
export const deleteApisByIds = (data) => {
|
||||
return service({
|
||||
url: '/api/deleteApisByIds',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// FreshCasbin
|
||||
// @Tags SysApi
|
||||
// @Summary 刷新casbin缓存
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {object} response.Response{msg=string} "刷新成功"
|
||||
// @Router /api/freshCasbin [get]
|
||||
export const freshCasbin = () => {
|
||||
return service({
|
||||
url: '/api/freshCasbin',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export const syncApi = () => {
|
||||
return service({
|
||||
url: '/api/syncApi',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export const getApiGroups = () => {
|
||||
return service({
|
||||
url: '/api/getApiGroups',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export const ignoreApi = (data) => {
|
||||
return service({
|
||||
url: '/api/ignoreApi',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const enterSyncApi = (data) => {
|
||||
return service({
|
||||
url: '/api/enterSyncApi',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拥有指定API权限的角色ID列表
|
||||
* @param {string} path API路径
|
||||
* @param {string} method 请求方法
|
||||
* @returns {Promise<number[]>} 角色ID数组
|
||||
*/
|
||||
export const getApiRoles = (path, method) => {
|
||||
return service({
|
||||
url: '/api/getApiRoles',
|
||||
method: 'get',
|
||||
params: { path, method }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 全量覆盖某API关联的角色列表
|
||||
* @param {Object} data
|
||||
* @param {string} data.path API路径
|
||||
* @param {string} data.method 请求方法
|
||||
* @param {number[]} data.authorityIds 角色ID列表
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const setApiRoles = (data) => {
|
||||
return service({
|
||||
url: '/api/setApiRoles',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
26
web-admin/src/api/attachmentCategory.js
Normal file
26
web-admin/src/api/attachmentCategory.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import service from '@/utils/request'
|
||||
// 分类列表
|
||||
export const getCategoryList = () => {
|
||||
return service({
|
||||
url: '/attachmentCategory/getCategoryList',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// 添加/编辑分类
|
||||
export const addCategory = (data) => {
|
||||
return service({
|
||||
url: '/attachmentCategory/addCategory',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除分类
|
||||
export const deleteCategory = (data) => {
|
||||
return service({
|
||||
url: '/attachmentCategory/deleteCategory',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
113
web-admin/src/api/authority.js
Normal file
113
web-admin/src/api/authority.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import service from '@/utils/request'
|
||||
// @Router /authority/getAuthorityList [post]
|
||||
export const getAuthorityList = (data) => {
|
||||
return service({
|
||||
url: '/authority/getAuthorityList',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 删除角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body {authorityId uint} true "删除角色"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /authority/deleteAuthority [post]
|
||||
export const deleteAuthority = (data) => {
|
||||
return service({
|
||||
url: '/authority/deleteAuthority',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 创建角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body api.CreateAuthorityPatams true "创建角色"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /authority/createAuthority [post]
|
||||
export const createAuthority = (data) => {
|
||||
return service({
|
||||
url: '/authority/createAuthority',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags authority
|
||||
// @Summary 拷贝角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body api.CreateAuthorityPatams true "拷贝角色"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"拷贝成功"}"
|
||||
// @Router /authority/copyAuthority [post]
|
||||
export const copyAuthority = (data) => {
|
||||
return service({
|
||||
url: '/authority/copyAuthority',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 设置角色资源权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body sysModel.SysAuthority true "设置角色资源权限"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
|
||||
// @Router /authority/setDataAuthority [post]
|
||||
export const setDataAuthority = (data) => {
|
||||
return service({
|
||||
url: '/authority/setDataAuthority',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 修改角色
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysAuthority true "修改角色"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
|
||||
// @Router /authority/setDataAuthority [post]
|
||||
export const updateAuthority = (data) => {
|
||||
return service({
|
||||
url: '/authority/updateAuthority',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拥有指定角色的用户ID列表
|
||||
* @param {number} authorityId 角色ID
|
||||
* @returns {Promise<number[]>} 用户ID数组
|
||||
*/
|
||||
export const getUsersByAuthorityId = (authorityId) => {
|
||||
return service({
|
||||
url: '/authority/getUsersByAuthority',
|
||||
method: 'get',
|
||||
params: { authorityId }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 全量覆盖某角色关联的用户列表
|
||||
* @param {Object} data
|
||||
* @param {number} data.authorityId 角色ID
|
||||
* @param {number[]} data.userIds 用户ID列表
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const setRoleUsers = (data) => {
|
||||
return service({
|
||||
url: '/authority/setRoleUsers',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
25
web-admin/src/api/authorityBtn.js
Normal file
25
web-admin/src/api/authorityBtn.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
export const getAuthorityBtnApi = (data) => {
|
||||
return service({
|
||||
url: '/authorityBtn/getAuthorityBtn',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const setAuthorityBtnApi = (data) => {
|
||||
return service({
|
||||
url: '/authorityBtn/setAuthorityBtn',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const canRemoveAuthorityBtnApi = (params) => {
|
||||
return service({
|
||||
url: '/authorityBtn/canRemoveAuthorityBtn',
|
||||
method: 'post',
|
||||
params
|
||||
})
|
||||
}
|
||||
242
web-admin/src/api/autoCode.js
Normal file
242
web-admin/src/api/autoCode.js
Normal file
@@ -0,0 +1,242 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
export const preview = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/preview',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const createTemp = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/createTemp',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 获取当前所有数据库
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/getDatabase [get]
|
||||
export const getDB = (params) => {
|
||||
return service({
|
||||
url: '/autoCode/getDB',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 获取当前数据库所有表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/getTables [get]
|
||||
export const getTable = (params) => {
|
||||
return service({
|
||||
url: '/autoCode/getTables',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 获取当前数据库所有表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /autoCode/getColumn [get]
|
||||
export const getColumn = (params) => {
|
||||
return service({
|
||||
url: '/autoCode/getColumn',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const getSysHistory = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/getSysHistory',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const rollback = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/rollback',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getMeta = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/getMeta',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const delSysHistory = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/delSysHistory',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const createPackageApi = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/createPackage',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getPackageApi = () => {
|
||||
return service({
|
||||
url: '/autoCode/getPackage',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export const deletePackageApi = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/delPackage',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getTemplatesApi = () => {
|
||||
return service({
|
||||
url: '/autoCode/getTemplates',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export const installPlug = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/installPlug',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const pubPlug = (params) => {
|
||||
return service({
|
||||
url: '/autoCode/pubPlug',
|
||||
method: 'post',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const llmAuto = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/llmAuto',
|
||||
method: 'post',
|
||||
data: { ...data },
|
||||
timeout: 1000 * 60 * 10,
|
||||
loadingOption: {
|
||||
lock: true,
|
||||
fullscreen: true,
|
||||
text: `小淼正在思考,请稍候...`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const addFunc = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/addFunc',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const initMenu = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/initMenu',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const initAPI = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/initAPI',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const initDictionary = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/initDictionary',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const mcp = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/mcp',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const mcpList = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/mcpList',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const mcpTest = (data) => {
|
||||
return service({
|
||||
url: '/autoCode/mcpTest',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 获取插件列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /autoCode/getPluginList [get]
|
||||
export const getPluginList = (params) => {
|
||||
return service({
|
||||
url: '/autoCode/getPluginList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 删除插件
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /autoCode/removePlugin [post]
|
||||
export const removePlugin = (params) => {
|
||||
return service({
|
||||
url: '/autoCode/removePlugin',
|
||||
method: 'post',
|
||||
params
|
||||
})
|
||||
}
|
||||
43
web-admin/src/api/breakpoint.js
Normal file
43
web-admin/src/api/breakpoint.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import service from '@/utils/request'
|
||||
// @Summary 设置角色资源权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body sysModel.SysAuthority true "设置角色资源权限"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
|
||||
// @Router /authority/setDataAuthority [post]
|
||||
|
||||
export const findFile = (params) => {
|
||||
return service({
|
||||
url: '/fileUploadAndDownload/findFile',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const breakpointContinue = (data) => {
|
||||
return service({
|
||||
url: '/fileUploadAndDownload/breakpointContinue',
|
||||
method: 'post',
|
||||
donNotShowLoading: true,
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const breakpointContinueFinish = (params) => {
|
||||
return service({
|
||||
url: '/fileUploadAndDownload/breakpointContinueFinish',
|
||||
method: 'post',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const removeChunk = (data, params) => {
|
||||
return service({
|
||||
url: '/fileUploadAndDownload/removeChunk',
|
||||
method: 'post',
|
||||
data,
|
||||
params
|
||||
})
|
||||
}
|
||||
32
web-admin/src/api/casbin.js
Normal file
32
web-admin/src/api/casbin.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags authority
|
||||
// @Summary 更改角色api权限
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body api.CreateAuthorityPatams true "更改角色api权限"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /casbin/UpdateCasbin [post]
|
||||
export const UpdateCasbin = (data) => {
|
||||
return service({
|
||||
url: '/casbin/updateCasbin',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags casbin
|
||||
// @Summary 获取权限列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body api.CreateAuthorityPatams true "获取权限列表"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /casbin/getPolicyPathByAuthorityId [post]
|
||||
export const getPolicyPathByAuthorityId = (data) => {
|
||||
return service({
|
||||
url: '/casbin/getPolicyPathByAuthorityId',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
80
web-admin/src/api/customer.js
Normal file
80
web-admin/src/api/customer.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags SysApi
|
||||
// @Summary 删除客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body dbModel.ExaCustomer true "删除客户"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /customer/customer [post]
|
||||
export const createExaCustomer = (data) => {
|
||||
return service({
|
||||
url: '/customer/customer',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 更新客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body dbModel.ExaCustomer true "更新客户信息"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /customer/customer [put]
|
||||
export const updateExaCustomer = (data) => {
|
||||
return service({
|
||||
url: '/customer/customer',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 创建客户
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body dbModel.ExaCustomer true "创建客户"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /customer/customer [delete]
|
||||
export const deleteExaCustomer = (data) => {
|
||||
return service({
|
||||
url: '/customer/customer',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 获取单一客户信息
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body dbModel.ExaCustomer true "获取单一客户信息"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /customer/customer [get]
|
||||
export const getExaCustomer = (params) => {
|
||||
return service({
|
||||
url: '/customer/customer',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysApi
|
||||
// @Summary 获取权限客户列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body modelInterface.PageInfo true "获取权限客户列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /customer/customerList [get]
|
||||
export const getExaCustomerList = (params) => {
|
||||
return service({
|
||||
url: '/customer/customerList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
14
web-admin/src/api/email.js
Normal file
14
web-admin/src/api/email.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags email
|
||||
// @Summary 发送测试邮件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}"
|
||||
// @Router /email/emailTest [post]
|
||||
export const emailTest = (data) => {
|
||||
return service({
|
||||
url: '/email/emailTest',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
145
web-admin/src/api/exportTemplate.js
Normal file
145
web-admin/src/api/exportTemplate.js
Normal file
@@ -0,0 +1,145 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 创建导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysExportTemplate true "创建导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /sysExportTemplate/createSysExportTemplate [post]
|
||||
export const createSysExportTemplate = (data) => {
|
||||
return service({
|
||||
url: '/sysExportTemplate/createSysExportTemplate',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 删除导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysExportTemplate true "删除导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysExportTemplate/deleteSysExportTemplate [delete]
|
||||
export const deleteSysExportTemplate = (data) => {
|
||||
return service({
|
||||
url: '/sysExportTemplate/deleteSysExportTemplate',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 批量删除导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysExportTemplate/deleteSysExportTemplate [delete]
|
||||
export const deleteSysExportTemplateByIds = (data) => {
|
||||
return service({
|
||||
url: '/sysExportTemplate/deleteSysExportTemplateByIds',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 更新导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysExportTemplate true "更新导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /sysExportTemplate/updateSysExportTemplate [put]
|
||||
export const updateSysExportTemplate = (data) => {
|
||||
return service({
|
||||
url: '/sysExportTemplate/updateSysExportTemplate',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 用id查询导出模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query model.SysExportTemplate true "用id查询导出模板"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysExportTemplate/findSysExportTemplate [get]
|
||||
export const findSysExportTemplate = (params) => {
|
||||
return service({
|
||||
url: '/sysExportTemplate/findSysExportTemplate',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 分页获取导出模板列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.PageInfo true "分页获取导出模板列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysExportTemplate/getSysExportTemplateList [get]
|
||||
export const getSysExportTemplateList = (params) => {
|
||||
return service({
|
||||
url: '/sysExportTemplate/getSysExportTemplateList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// ExportExcel 导出表格token
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 导出表格
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportExcel [get]
|
||||
export const exportExcel = (params) => {
|
||||
return service({
|
||||
url: '/sysExportTemplate/exportExcel',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// ExportTemplate 导出表格模板
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 导出表格模板
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/exportTemplate [get]
|
||||
export const exportTemplate = (params) => {
|
||||
return service({
|
||||
url: '/sysExportTemplate/exportTemplate',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// PreviewSQL 预览最终生成的SQL
|
||||
// @Tags SysExportTemplate
|
||||
// @Summary 预览最终生成的SQL
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Router /sysExportTemplate/previewSQL [get]
|
||||
// @Param templateID query string true "导出模板ID"
|
||||
// @Param params query string false "查询参数编码字符串,参考 ExportExcel 组件"
|
||||
export const previewSQL = (params) => {
|
||||
return service({
|
||||
url: '/sysExportTemplate/previewSQL',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
67
web-admin/src/api/fileUploadAndDownload.js
Normal file
67
web-admin/src/api/fileUploadAndDownload.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags FileUploadAndDownload
|
||||
// @Summary 分页文件列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body modelInterface.PageInfo true "分页获取文件户列表"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /fileUploadAndDownload/getFileList [post]
|
||||
export const getFileList = (data) => {
|
||||
return service({
|
||||
url: '/fileUploadAndDownload/getFileList',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags FileUploadAndDownload
|
||||
// @Summary 删除文件
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body dbModel.FileUploadAndDownload true "传入文件里面id即可"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"返回成功"}"
|
||||
// @Router /fileUploadAndDownload/deleteFile [post]
|
||||
export const deleteFile = (data) => {
|
||||
return service({
|
||||
url: '/fileUploadAndDownload/deleteFile',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑文件名或者备注
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const editFileName = (data) => {
|
||||
return service({
|
||||
url: '/fileUploadAndDownload/editFileName',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入URL
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const importURL = (data) => {
|
||||
return service({
|
||||
url: '/fileUploadAndDownload/importURL',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// 上传文件 暂时用于头像上传
|
||||
export const uploadFile = (data) => {
|
||||
return service({
|
||||
url: "/fileUploadAndDownload/upload",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
};
|
||||
19
web-admin/src/api/github.js
Normal file
19
web-admin/src/api/github.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const service = axios.create()
|
||||
|
||||
export function Commits(page) {
|
||||
return service({
|
||||
url:
|
||||
'https://api.github.com/repos/flipped-aurora/gin-vue-admin/commits?page=' +
|
||||
page,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function Members() {
|
||||
return service({
|
||||
url: 'https://api.github.com/orgs/FLIPPED-AURORA/members',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
27
web-admin/src/api/initdb.js
Normal file
27
web-admin/src/api/initdb.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags InitDB
|
||||
// @Summary 初始化用户数据库
|
||||
// @Produce application/json
|
||||
// @Param data body request.InitDB true "初始化数据库参数"
|
||||
// @Success 200 {string} string "{"code":0,"data":{},"msg":"自动创建数据库成功"}"
|
||||
// @Router /init/initdb [post]
|
||||
export const initDB = (data) => {
|
||||
return service({
|
||||
url: '/init/initdb',
|
||||
method: 'post',
|
||||
data,
|
||||
donNotShowLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags CheckDB
|
||||
// @Summary 初始化用户数据库
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"code":0,"data":{},"msg":"探测完成"}"
|
||||
// @Router /init/checkdb [post]
|
||||
export const checkDB = () => {
|
||||
return service({
|
||||
url: '/init/checkdb',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
14
web-admin/src/api/jwt.js
Normal file
14
web-admin/src/api/jwt.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags jwt
|
||||
// @Summary jwt加入黑名单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"拉黑成功"}"
|
||||
// @Router /jwt/jsonInBlacklist [post]
|
||||
export const jsonInBlacklist = () => {
|
||||
return service({
|
||||
url: '/jwt/jsonInBlacklist',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
141
web-admin/src/api/menu.js
Normal file
141
web-admin/src/api/menu.js
Normal file
@@ -0,0 +1,141 @@
|
||||
import service from '@/utils/request'
|
||||
// @Summary 用户登录 获取动态路由
|
||||
// @Produce application/json
|
||||
// @Param 可以什么都不填 调一下即可
|
||||
// @Router /menu/getMenu [post]
|
||||
export const asyncMenu = () => {
|
||||
return service({
|
||||
url: '/menu/getMenu',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 获取menu列表
|
||||
// @Produce application/json
|
||||
// @Param {
|
||||
// page int
|
||||
// pageSize int
|
||||
// }
|
||||
// @Router /menu/getMenuList [post]
|
||||
export const getMenuList = (data) => {
|
||||
return service({
|
||||
url: '/menu/getMenuList',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 新增基础menu
|
||||
// @Produce application/json
|
||||
// @Param menu Object
|
||||
// @Router /menu/getMenuList [post]
|
||||
export const addBaseMenu = (data) => {
|
||||
return service({
|
||||
url: '/menu/addBaseMenu',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 获取基础路由列表
|
||||
// @Produce application/json
|
||||
// @Param 可以什么都不填 调一下即可
|
||||
// @Router /menu/getBaseMenuTree [post]
|
||||
export const getBaseMenuTree = () => {
|
||||
return service({
|
||||
url: '/menu/getBaseMenuTree',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 添加用户menu关联关系
|
||||
// @Produce application/json
|
||||
// @Param menus Object authorityId string
|
||||
// @Router /menu/getMenuList [post]
|
||||
export const addMenuAuthority = (data) => {
|
||||
return service({
|
||||
url: '/menu/addMenuAuthority',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 获取用户menu关联关系
|
||||
// @Produce application/json
|
||||
// @Param authorityId string
|
||||
// @Router /menu/getMenuAuthority [post]
|
||||
export const getMenuAuthority = (data) => {
|
||||
return service({
|
||||
url: '/menu/getMenuAuthority',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 删除menu
|
||||
// @Produce application/json
|
||||
// @Param ID float64
|
||||
// @Router /menu/deleteBaseMenu [post]
|
||||
export const deleteBaseMenu = (data) => {
|
||||
return service({
|
||||
url: '/menu/deleteBaseMenu',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary 修改menu列表
|
||||
// @Produce application/json
|
||||
// @Param menu Object
|
||||
// @Router /menu/updateBaseMenu [post]
|
||||
export const updateBaseMenu = (data) => {
|
||||
return service({
|
||||
url: '/menu/updateBaseMenu',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags menu
|
||||
// @Summary 根据id获取菜单
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body api.GetById true "根据id获取菜单"
|
||||
// @Success 200 {string} json "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /menu/getBaseMenuById [post]
|
||||
export const getBaseMenuById = (data) => {
|
||||
return service({
|
||||
url: '/menu/getBaseMenuById',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拥有指定菜单的角色ID列表
|
||||
* @param {number} menuId 菜单ID
|
||||
* @returns {Promise<number[]>} 角色ID数组
|
||||
*/
|
||||
export const getMenuRoles = (menuId) => {
|
||||
return service({
|
||||
url: '/menu/getMenuRoles',
|
||||
method: 'get',
|
||||
params: { menuId }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 全量覆盖某菜单关联的角色列表
|
||||
* @param {Object} data
|
||||
* @param {number} data.menuId 菜单ID
|
||||
* @param {number[]} data.authorityIds 角色ID列表
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export const setMenuRoles = (data) => {
|
||||
return service({
|
||||
url: '/menu/setMenuRoles',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
10
web-admin/src/api/plugin/api.js
Normal file
10
web-admin/src/api/plugin/api.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
export const getShopPluginList = (params) => {
|
||||
return service({
|
||||
baseURL: "plugin",
|
||||
url: '/shopPlugin/getShopPluginList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
169
web-admin/src/api/skills.js
Normal file
169
web-admin/src/api/skills.js
Normal file
@@ -0,0 +1,169 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
export const getSkillTools = () => {
|
||||
return service({
|
||||
url: '/skills/getTools',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export const getSkillList = (data) => {
|
||||
return service({
|
||||
url: '/skills/getSkillList',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getSkillDetail = (data) => {
|
||||
return service({
|
||||
url: '/skills/getSkillDetail',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const saveSkill = (data) => {
|
||||
return service({
|
||||
url: '/skills/saveSkill',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteSkill = (data) => {
|
||||
return service({
|
||||
url: '/skills/deleteSkill',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const createSkillScript = (data) => {
|
||||
return service({
|
||||
url: '/skills/createScript',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getSkillScript = (data) => {
|
||||
return service({
|
||||
url: '/skills/getScript',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const saveSkillScript = (data) => {
|
||||
return service({
|
||||
url: '/skills/saveScript',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const createSkillResource = (data) => {
|
||||
return service({
|
||||
url: '/skills/createResource',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getSkillResource = (data) => {
|
||||
return service({
|
||||
url: '/skills/getResource',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const saveSkillResource = (data) => {
|
||||
return service({
|
||||
url: '/skills/saveResource',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const createSkillReference = (data) => {
|
||||
return service({
|
||||
url: '/skills/createReference',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getSkillReference = (data) => {
|
||||
return service({
|
||||
url: '/skills/getReference',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const saveSkillReference = (data) => {
|
||||
return service({
|
||||
url: '/skills/saveReference',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const createSkillTemplate = (data) => {
|
||||
return service({
|
||||
url: '/skills/createTemplate',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getSkillTemplate = (data) => {
|
||||
return service({
|
||||
url: '/skills/getTemplate',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const saveSkillTemplate = (data) => {
|
||||
return service({
|
||||
url: '/skills/saveTemplate',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getGlobalConstraint = (data) => {
|
||||
return service({
|
||||
url: '/skills/getGlobalConstraint',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const saveGlobalConstraint = (data) => {
|
||||
return service({
|
||||
url: '/skills/saveGlobalConstraint',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const packageSkill = (data) => {
|
||||
return service({
|
||||
url: '/skills/packageSkill',
|
||||
method: 'post',
|
||||
data,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
export const downloadOnlineSkill = (data) => {
|
||||
return service({
|
||||
url: '/skills/downloadOnlineSkill',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
25
web-admin/src/api/sysApiToken.js
Normal file
25
web-admin/src/api/sysApiToken.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
export const createApiToken = (data) => {
|
||||
return service({
|
||||
url: '/sysApiToken/createApiToken',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getApiTokenList = (data) => {
|
||||
return service({
|
||||
url: '/sysApiToken/getApiTokenList',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteApiToken = (data) => {
|
||||
return service({
|
||||
url: '/sysApiToken/deleteApiToken',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
112
web-admin/src/api/sysDictionary.js
Normal file
112
web-admin/src/api/sysDictionary.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags SysDictionary
|
||||
// @Summary 创建SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysDictionary true "创建SysDictionary"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionary/createSysDictionary [post]
|
||||
export const createSysDictionary = (data) => {
|
||||
return service({
|
||||
url: '/sysDictionary/createSysDictionary',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 删除SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysDictionary true "删除SysDictionary"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysDictionary/deleteSysDictionary [delete]
|
||||
export const deleteSysDictionary = (data) => {
|
||||
return service({
|
||||
url: '/sysDictionary/deleteSysDictionary',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 更新SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysDictionary true "更新SysDictionary"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /sysDictionary/updateSysDictionary [put]
|
||||
export const updateSysDictionary = (data) => {
|
||||
return service({
|
||||
url: '/sysDictionary/updateSysDictionary',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 用id查询SysDictionary
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysDictionary true "用id查询SysDictionary"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysDictionary/findSysDictionary [get]
|
||||
export const findSysDictionary = (params) => {
|
||||
return service({
|
||||
url: '/sysDictionary/findSysDictionary',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 分页获取SysDictionary列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "分页获取SysDictionary列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionary/getSysDictionaryList [get]
|
||||
export const getSysDictionaryList = (params) => {
|
||||
return service({
|
||||
url: '/sysDictionary/getSysDictionaryList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 导出字典JSON(包含字典详情)
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query model.SysDictionary true "字典ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"导出成功"}"
|
||||
// @Router /sysDictionary/exportSysDictionary [get]
|
||||
export const exportSysDictionary = (params) => {
|
||||
return service({
|
||||
url: '/sysDictionary/exportSysDictionary',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionary
|
||||
// @Summary 导入字典JSON(包含字典详情)
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body object true "字典JSON数据"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"导入成功"}"
|
||||
// @Router /sysDictionary/importSysDictionary [post]
|
||||
export const importSysDictionary = (data) => {
|
||||
return service({
|
||||
url: '/sysDictionary/importSysDictionary',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
145
web-admin/src/api/sysDictionaryDetail.js
Normal file
145
web-admin/src/api/sysDictionaryDetail.js
Normal file
@@ -0,0 +1,145 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 创建SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysDictionaryDetail true "创建SysDictionaryDetail"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionaryDetail/createSysDictionaryDetail [post]
|
||||
export const createSysDictionaryDetail = (data) => {
|
||||
return service({
|
||||
url: '/sysDictionaryDetail/createSysDictionaryDetail',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 删除SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysDictionaryDetail true "删除SysDictionaryDetail"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysDictionaryDetail/deleteSysDictionaryDetail [delete]
|
||||
export const deleteSysDictionaryDetail = (data) => {
|
||||
return service({
|
||||
url: '/sysDictionaryDetail/deleteSysDictionaryDetail',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 更新SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysDictionaryDetail true "更新SysDictionaryDetail"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /sysDictionaryDetail/updateSysDictionaryDetail [put]
|
||||
export const updateSysDictionaryDetail = (data) => {
|
||||
return service({
|
||||
url: '/sysDictionaryDetail/updateSysDictionaryDetail',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 用id查询SysDictionaryDetail
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysDictionaryDetail true "用id查询SysDictionaryDetail"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysDictionaryDetail/findSysDictionaryDetail [get]
|
||||
export const findSysDictionaryDetail = (params) => {
|
||||
return service({
|
||||
url: '/sysDictionaryDetail/findSysDictionaryDetail',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 分页获取SysDictionaryDetail列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "分页获取SysDictionaryDetail列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionaryDetail/getSysDictionaryDetailList [get]
|
||||
export const getSysDictionaryDetailList = (params) => {
|
||||
return service({
|
||||
url: '/sysDictionaryDetail/getSysDictionaryDetailList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 获取层级字典详情树形结构(根据字典ID)
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param sysDictionaryID query string true "字典ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionaryDetail/getDictionaryTreeList [get]
|
||||
export const getDictionaryTreeList = (params) => {
|
||||
return service({
|
||||
url: '/sysDictionaryDetail/getDictionaryTreeList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 获取层级字典详情树形结构(根据字典类型)
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param dictType query string true "字典类型"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionaryDetail/getDictionaryTreeListByType [get]
|
||||
export const getDictionaryTreeListByType = (params) => {
|
||||
return service({
|
||||
url: '/sysDictionaryDetail/getDictionaryTreeListByType',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 根据父级ID获取字典详情
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param parentID query string true "父级ID"
|
||||
// @Param includeChildren query boolean false "是否包含子级"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionaryDetail/getDictionaryDetailsByParent [get]
|
||||
export const getDictionaryDetailsByParent = (params) => {
|
||||
return service({
|
||||
url: '/sysDictionaryDetail/getDictionaryDetailsByParent',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysDictionaryDetail
|
||||
// @Summary 获取字典详情的完整路径
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param ID query string true "字典详情ID"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysDictionaryDetail/getDictionaryPath [get]
|
||||
export const getDictionaryPath = (params) => {
|
||||
return service({
|
||||
url: '/sysDictionaryDetail/getDictionaryPath',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
33
web-admin/src/api/sysLoginLog.js
Normal file
33
web-admin/src/api/sysLoginLog.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import service from '@/utils/request'
|
||||
|
||||
export const deleteLoginLog = (data) => {
|
||||
return service({
|
||||
url: '/sysLoginLog/deleteLoginLog',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteLoginLogByIds = (data) => {
|
||||
return service({
|
||||
url: '/sysLoginLog/deleteLoginLogByIds',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getLoginLogList = (params) => {
|
||||
return service({
|
||||
url: '/sysLoginLog/getLoginLogList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export const findLoginLog = (params) => {
|
||||
return service({
|
||||
url: '/sysLoginLog/findLoginLog',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
48
web-admin/src/api/sysOperationRecord.js
Normal file
48
web-admin/src/api/sysOperationRecord.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 删除SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysOperationRecord true "删除SysOperationRecord"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysOperationRecord/deleteSysOperationRecord [delete]
|
||||
export const deleteSysOperationRecord = (data) => {
|
||||
return service({
|
||||
url: '/sysOperationRecord/deleteSysOperationRecord',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 删除SysOperationRecord
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "删除SysOperationRecord"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysOperationRecord/deleteSysOperationRecord [delete]
|
||||
export const deleteSysOperationRecordByIds = (data) => {
|
||||
return service({
|
||||
url: '/sysOperationRecord/deleteSysOperationRecordByIds',
|
||||
method: 'delete',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysOperationRecord
|
||||
// @Summary 分页获取SysOperationRecord列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.PageInfo true "分页获取SysOperationRecord列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysOperationRecord/getSysOperationRecordList [get]
|
||||
export const getSysOperationRecordList = (params) => {
|
||||
return service({
|
||||
url: '/sysOperationRecord/getSysOperationRecordList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
111
web-admin/src/api/sysParams.js
Normal file
111
web-admin/src/api/sysParams.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags SysParams
|
||||
// @Summary 创建参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysParams true "创建参数"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /sysParams/createSysParams [post]
|
||||
export const createSysParams = (data) => {
|
||||
return service({
|
||||
url: '/sysParams/createSysParams',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysParams
|
||||
// @Summary 删除参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysParams true "删除参数"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysParams/deleteSysParams [delete]
|
||||
export const deleteSysParams = (params) => {
|
||||
return service({
|
||||
url: '/sysParams/deleteSysParams',
|
||||
method: 'delete',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysParams
|
||||
// @Summary 批量删除参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除参数"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysParams/deleteSysParams [delete]
|
||||
export const deleteSysParamsByIds = (params) => {
|
||||
return service({
|
||||
url: '/sysParams/deleteSysParamsByIds',
|
||||
method: 'delete',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysParams
|
||||
// @Summary 更新参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysParams true "更新参数"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /sysParams/updateSysParams [put]
|
||||
export const updateSysParams = (data) => {
|
||||
return service({
|
||||
url: '/sysParams/updateSysParams',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysParams
|
||||
// @Summary 用id查询参数
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query model.SysParams true "用id查询参数"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysParams/findSysParams [get]
|
||||
export const findSysParams = (params) => {
|
||||
return service({
|
||||
url: '/sysParams/findSysParams',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysParams
|
||||
// @Summary 分页获取参数列表
|
||||
// @Security ApiKeyAuth
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.PageInfo true "分页获取参数列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysParams/getSysParamsList [get]
|
||||
export const getSysParamsList = (params) => {
|
||||
return service({
|
||||
url: '/sysParams/getSysParamsList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysParams
|
||||
// @Summary 不需要鉴权的参数接口
|
||||
// @accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query systemReq.SysParamsSearch true "分页获取参数列表"
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /sysParams/getSysParam [get]
|
||||
export const getSysParam = (params) => {
|
||||
return service({
|
||||
url: '/sysParams/getSysParam',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
55
web-admin/src/api/system.js
Normal file
55
web-admin/src/api/system.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags systrm
|
||||
// @Summary 获取配置文件内容
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}"
|
||||
// @Router /system/getSystemConfig [post]
|
||||
export const getSystemConfig = () => {
|
||||
return service({
|
||||
url: '/system/getSystemConfig',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags system
|
||||
// @Summary 设置配置文件内容
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Param data body sysModel.System true
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}"
|
||||
// @Router /system/setSystemConfig [post]
|
||||
export const setSystemConfig = (data) => {
|
||||
return service({
|
||||
url: '/system/setSystemConfig',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags system
|
||||
// @Summary 获取服务器运行状态
|
||||
// @Security ApiKeyAuth
|
||||
// @Produce application/json
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"返回成功"}"
|
||||
// @Router /system/getServerInfo [post]
|
||||
export const getSystemState = () => {
|
||||
return service({
|
||||
url: '/system/getServerInfo',
|
||||
method: 'post',
|
||||
donNotShowLoading: true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载服务
|
||||
* @param data
|
||||
* @returns {*}
|
||||
*/
|
||||
export const reloadSystem = (data) => {
|
||||
return service({
|
||||
url: '/system/reloadSystem',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
126
web-admin/src/api/system/sysError.js
Normal file
126
web-admin/src/api/system/sysError.js
Normal file
@@ -0,0 +1,126 @@
|
||||
import service from '@/utils/request'
|
||||
// @Tags SysError
|
||||
// @Summary 创建错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysError true "创建错误日志"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
|
||||
// @Router /sysError/createSysError [post]
|
||||
export const createSysError = (data) => {
|
||||
return service({
|
||||
url: '/sysError/createSysError',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysError
|
||||
// @Summary 删除错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysError true "删除错误日志"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysError/deleteSysError [delete]
|
||||
export const deleteSysError = (params) => {
|
||||
return service({
|
||||
url: '/sysError/deleteSysError',
|
||||
method: 'delete',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysError
|
||||
// @Summary 批量删除错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body request.IdsReq true "批量删除错误日志"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
|
||||
// @Router /sysError/deleteSysError [delete]
|
||||
export const deleteSysErrorByIds = (params) => {
|
||||
return service({
|
||||
url: '/sysError/deleteSysErrorByIds',
|
||||
method: 'delete',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysError
|
||||
// @Summary 更新错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data body model.SysError true "更新错误日志"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
|
||||
// @Router /sysError/updateSysError [put]
|
||||
export const updateSysError = (data) => {
|
||||
return service({
|
||||
url: '/sysError/updateSysError',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysError
|
||||
// @Summary 用id查询错误日志
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query model.SysError true "用id查询错误日志"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
|
||||
// @Router /sysError/findSysError [get]
|
||||
export const findSysError = (params) => {
|
||||
return service({
|
||||
url: '/sysError/findSysError',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysError
|
||||
// @Summary 分页获取错误日志列表
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query request.PageInfo true "分页获取错误日志列表"
|
||||
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
|
||||
// @Router /sysError/getSysErrorList [get]
|
||||
export const getSysErrorList = (params) => {
|
||||
return service({
|
||||
url: '/sysError/getSysErrorList',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysError
|
||||
// @Summary 不需要鉴权的错误日志接口
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param data query systemReq.SysErrorSearch true "分页获取错误日志列表"
|
||||
// @Success 200 {object} response.Response{data=object,msg=string} "获取成功"
|
||||
// @Router /sysError/getSysErrorPublic [get]
|
||||
export const getSysErrorPublic = () => {
|
||||
return service({
|
||||
url: '/sysError/getSysErrorPublic',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags SysError
|
||||
// @Summary 触发错误处理(异步)
|
||||
// @Security ApiKeyAuth
|
||||
// @Accept application/json
|
||||
// @Produce application/json
|
||||
// @Param id query string true "错误日志ID"
|
||||
// @Success 200 {string} string "{\"success\":true,\"data\":{},\"msg\":\"处理已提交\"}"
|
||||
// @Router /sysError/getSysErrorSolution [get]
|
||||
export const getSysErrorSolution = (params) => {
|
||||
return service({
|
||||
url: '/sysError/getSysErrorSolution',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user