feat(admin): 添加提示词批量删除和定时同步功能
- 后端新增批量删除提示词接口和实现 - 前端管理页面添加多选和批量删除功能 - 添加定时同步GitHub远程提示词源功能 - 新增系统设置中的提示词同步配置选项 - 更新文档说明新的批量操作和定时同步功能 - 添加相关的测试用例和依赖库
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
+ [新增] 管理后台提示词管理支持多选批量删除。
|
||||
+ [新增] 新增定义拉取GitHub提示词源功能。
|
||||
+ [新增] 新增awesome-gpt-image2-prompts提示词来源。
|
||||
|
||||
## v0.0.6 - 2026-05-22
|
||||
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
- 管理后台 `/admin/prompts` 新增提示词批量删除,需要确认多选、确认弹窗、删除后列表刷新和筛选条件下删除行为。
|
||||
- 管理后台 `/admin/settings` 的私有配置新增提示词定时同步开关和 Cron 表达式;开启后后端会按配置同步内置 GitHub 远程提示词源,需要确认保存配置和到点同步行为。
|
||||
- 管理后台 `/admin/settings` 中的渠道模型列表获取和模型测试已改为走后端接口,前端不再直接读取或转发 API Key,需要确认保存后、编辑中和已有渠道三种场景都能正常拉取模型和测试连通性。
|
||||
- 提示词远程源新增 `davidwuw0811-boop/awesome-gpt-image2-prompts`,同步时会读取仓库 `prompts.json` 的结构化数据,需要确认后台同步后分类、封面图、标签和提示词内容展示正常。
|
||||
|
||||
@@ -7,3 +7,4 @@
|
||||
| https://github.com/ImgEdify/Awesome-GPT4o-Image-Prompts | 已实现同步逻辑 |
|
||||
| https://github.com/YouMind-OpenLab/awesome-gpt-image-2 | 已实现同步逻辑 |
|
||||
| https://github.com/YouMind-OpenLab/awesome-nano-banana-pro-prompts | 已实现同步逻辑 |
|
||||
| https://github.com/davidwuw0811-boop/awesome-gpt-image2-prompts | 已实现同步逻辑 |
|
||||
|
||||
@@ -21,6 +21,7 @@ var promptCategories = []model.PromptCategory{
|
||||
{Category: "awesome-gpt4o-image-prompts", Name: "Awesome GPT4o Image Prompts", Description: "ImgEdify 的 GPT-4o 图像提示词分类", GithubURL: "https://github.com/ImgEdify/Awesome-GPT4o-Image-Prompts", Remote: true},
|
||||
{Category: "youmind-gpt-image-2", Name: "YouMind GPT Image 2", Description: "YouMind OpenLab 的 GPT Image 2 中文提示词分类", GithubURL: "https://github.com/YouMind-OpenLab/awesome-gpt-image-2", Remote: true},
|
||||
{Category: "youmind-nano-banana-pro", Name: "YouMind Nano Banana Pro", Description: "YouMind OpenLab 的 Nano Banana Pro 中文提示词分类", GithubURL: "https://github.com/YouMind-OpenLab/awesome-nano-banana-pro-prompts", Remote: true},
|
||||
{Category: "davidwu-gpt-image2-prompts", Name: "awesome-gpt-image2-prompts", Description: "davidwuw0811-boop 整理的 GPT Image 2 提示词分类", GithubURL: "https://github.com/davidwuw0811-boop/awesome-gpt-image2-prompts", Remote: true},
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -20,6 +20,7 @@ const (
|
||||
awesomeGpt4oImagePromptsBase = "https://raw.githubusercontent.com/ImgEdify/Awesome-GPT4o-Image-Prompts/main"
|
||||
youMindGptImage2RawBase = "https://raw.githubusercontent.com/YouMind-OpenLab/awesome-gpt-image-2/main"
|
||||
youMindNanoBananaProRawBase = "https://raw.githubusercontent.com/YouMind-OpenLab/awesome-nano-banana-pro-prompts/main"
|
||||
davidWuGptImage2RawBase = "https://raw.githubusercontent.com/davidwuw0811-boop/awesome-gpt-image2-prompts/main"
|
||||
)
|
||||
|
||||
var gptImage2CaseFiles = []string{"README.md", "cases/ad-creative.md", "cases/character.md", "cases/comparison.md", "cases/ecommerce.md", "cases/portrait.md", "cases/poster.md", "cases/ui.md"}
|
||||
@@ -34,6 +35,20 @@ type gptImage2Data struct {
|
||||
} `json:"records"`
|
||||
}
|
||||
|
||||
type davidWuGptImage2Prompt struct {
|
||||
ID int `json:"id"`
|
||||
TitleEN string `json:"title_en"`
|
||||
TitleCN string `json:"title_cn"`
|
||||
Category string `json:"category"`
|
||||
CategoryCN string `json:"category_cn"`
|
||||
Prompt string `json:"prompt"`
|
||||
Note string `json:"note"`
|
||||
Author string `json:"author"`
|
||||
Source string `json:"source"`
|
||||
NeedsRef bool `json:"needs_ref"`
|
||||
Image string `json:"image"`
|
||||
}
|
||||
|
||||
func SyncPromptCategory(category string) ([]model.PromptCategory, error) {
|
||||
for _, item := range repository.PromptCategories() {
|
||||
if item.Category != category {
|
||||
@@ -63,6 +78,8 @@ func buildPromptCategory(category string) ([]model.Prompt, error) {
|
||||
return buildYouMindGptImage2Prompts()
|
||||
case "youmind-nano-banana-pro":
|
||||
return buildYouMindNanoBananaProPrompts()
|
||||
case "davidwu-gpt-image2-prompts":
|
||||
return buildDavidWuGptImage2Prompts()
|
||||
}
|
||||
return nil, errors.New("未知提示词分类")
|
||||
}
|
||||
@@ -173,6 +190,31 @@ func buildYouMindNanoBananaProPrompts() ([]model.Prompt, error) {
|
||||
return buildYouMindPrompts(youMindNanoBananaProRawBase, "youmind-nano-banana-pro", "nano-banana-pro")
|
||||
}
|
||||
|
||||
func buildDavidWuGptImage2Prompts() ([]model.Prompt, error) {
|
||||
raw, err := fetchText(davidWuGptImage2RawBase, "prompts.json")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := []davidWuGptImage2Prompt{}
|
||||
if err := json.Unmarshal([]byte(raw), &data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := []model.Prompt{}
|
||||
for _, item := range data {
|
||||
title := strings.TrimSpace(item.TitleCN)
|
||||
if title == "" {
|
||||
title = strings.TrimSpace(item.TitleEN)
|
||||
}
|
||||
prompt := strings.TrimSpace(item.Prompt)
|
||||
if title == "" || prompt == "" {
|
||||
continue
|
||||
}
|
||||
image := absoluteImage(davidWuGptImage2RawBase, item.Image)
|
||||
items = append(items, model.Prompt{ID: "davidwu-gpt-image2-prompts-" + leftPad(item.ID), Title: title, CoverURL: image, Prompt: prompt, Tags: davidWuGptImage2Tags(item), Preview: davidWuGptImage2Preview(item, image)})
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func buildYouMindPrompts(baseURL, idPrefix, modelTag string) ([]model.Prompt, error) {
|
||||
markdown, err := fetchText(baseURL, "README_zh.md")
|
||||
if err != nil {
|
||||
@@ -234,6 +276,28 @@ func youMindTags(title, modelTag string) []string {
|
||||
return tags
|
||||
}
|
||||
|
||||
func davidWuGptImage2Tags(item davidWuGptImage2Prompt) []string {
|
||||
tags := splitTags(strings.Join([]string{item.CategoryCN, item.Category, item.Author, item.Source}, "/"), `/`)
|
||||
if item.NeedsRef {
|
||||
tags = append(tags, "需要参考图")
|
||||
}
|
||||
return tags
|
||||
}
|
||||
|
||||
func davidWuGptImage2Preview(item davidWuGptImage2Prompt, image string) string {
|
||||
lines := []string{}
|
||||
if item.TitleEN != "" {
|
||||
lines = append(lines, item.TitleEN)
|
||||
}
|
||||
if item.Note != "" {
|
||||
lines = append(lines, item.Note)
|
||||
}
|
||||
if image != "" {
|
||||
lines = append(lines, "")
|
||||
}
|
||||
return strings.Join(lines, "\n\n")
|
||||
}
|
||||
|
||||
func splitTags(value string, pattern string) []string {
|
||||
tags := []string{}
|
||||
for _, tag := range regexp.MustCompile(pattern).Split(value, -1) {
|
||||
|
||||
Reference in New Issue
Block a user