mirror of
https://github.com/yangjian102621/geekai.git
synced 2026-08-12 18:50:58 +00:00
9ccff4efbc
- Sync backend and frontend from GeekAI Plus v4.3.0 - Remove commercial License flows and update open-source deployment defaults - Preserve Docker Compose deployment and bump image tags to v4.3.0 BREAKING CHANGE: commercial License configuration and related endpoints are removed
183 lines
4.3 KiB
Go
183 lines
4.3 KiB
Go
package ppt
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"geekai/core/types"
|
|
"geekai/store/model"
|
|
"geekai/store/vo"
|
|
"strings"
|
|
)
|
|
|
|
// EditSlideImage 基于当前激活图做图生图,追加 image_history 并将 image_url 设为新版。
|
|
func (s *PptService) EditSlideImage(ctx context.Context, taskID string, userID uint, slideIndex int, prompt string, oss types.OSSConfig, app *types.AppConfig) ([]SlideData, error) {
|
|
prompt = strings.TrimSpace(prompt)
|
|
if prompt == "" {
|
|
return nil, fmt.Errorf("请输入修改说明")
|
|
}
|
|
task, ok := s.GetTask(taskID)
|
|
if !ok {
|
|
return nil, ErrPptTaskNotFound
|
|
}
|
|
if task.UserID != userID {
|
|
return nil, ErrPptTaskNotFound
|
|
}
|
|
refURL := ""
|
|
for _, sl := range task.Slides {
|
|
if sl.SlideIndex == slideIndex {
|
|
normalizeSlideImageHistory(&sl)
|
|
refURL = strings.TrimSpace(sl.ImageURL)
|
|
break
|
|
}
|
|
}
|
|
if refURL == "" {
|
|
if slideExists(task.Slides, slideIndex) {
|
|
return nil, ErrPptSlideNoImage
|
|
}
|
|
return nil, ErrPptSlideNotFound
|
|
}
|
|
|
|
cfg, err := s.loadPPTConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
power, err := s.userPower(userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if cfg.PowerCostPerSlide > 0 && power < cfg.PowerCostPerSlide {
|
|
return nil, ErrInsufficientPower
|
|
}
|
|
|
|
generator, err := NewImageGenerator(cfg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
refInputs, err := PrepareReferenceInputsForImg2Img(refURL, oss, app)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("准备参考图失败:%w", err)
|
|
}
|
|
|
|
imgURL, err := generator.GenerateWithReference(ctx, prompt, refInputs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
storedURL, err := s.uploadManager.GetUploadHandler().PutUrlFile(imgURL, ".png", false)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("转存图片失败:%w", err)
|
|
}
|
|
|
|
if err := s.applySlideImageEdit(taskID, slideIndex, storedURL, prompt); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if cfg.PowerCostPerSlide > 0 {
|
|
err = s.userService.DecreasePower(userID, cfg.PowerCostPerSlide, model.PowerLog{
|
|
Type: types.PowerConsume,
|
|
Model: generator.Provider(),
|
|
Remark: fmt.Sprintf("PPT 任务 %s 第 %d 页图生图编辑", taskID, slideIndex),
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("扣减算力失败:%v", err)
|
|
}
|
|
}
|
|
|
|
task2, _ := s.GetTask(taskID)
|
|
return task2.Slides, nil
|
|
}
|
|
|
|
func slideExists(slides []SlideData, slideIndex int) bool {
|
|
for _, sl := range slides {
|
|
if sl.SlideIndex == slideIndex {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (s *PptService) applySlideImageEdit(taskID string, slideIndex int, newURL string, editPrompt string) error {
|
|
s.slidesLock.Lock()
|
|
defer s.slidesLock.Unlock()
|
|
|
|
var job model.PPTJob
|
|
if err := s.db.Where("task_id = ?", taskID).First(&job).Error; err != nil {
|
|
return err
|
|
}
|
|
slides := job.Slides
|
|
found := false
|
|
for i := range slides {
|
|
if slides[i].SlideIndex != slideIndex {
|
|
continue
|
|
}
|
|
found = true
|
|
sd := voToSlide(slides[i])
|
|
normalizeSlideImageHistory(&sd)
|
|
if strings.TrimSpace(sd.ImageURL) == "" {
|
|
return ErrPptSlideNoImage
|
|
}
|
|
sd.ImageHistory = append(sd.ImageHistory, vo.PPTSlideImageVersion{
|
|
ImageURL: newURL,
|
|
Prompt: editPrompt,
|
|
})
|
|
sd.ImageURL = newURL
|
|
slides[i] = slideToVO(sd)
|
|
break
|
|
}
|
|
if !found {
|
|
return ErrPptSlideNotFound
|
|
}
|
|
job.Slides = slides
|
|
biz := voSlidesToBiz(slides)
|
|
return s.refreshJobMeta(&job, biz)
|
|
}
|
|
|
|
// SetActiveSlideVersion 将 image_url 切换为 image_history[versionIndex]。
|
|
func (s *PptService) SetActiveSlideVersion(taskID string, userID uint, slideIndex int, versionIndex int) ([]SlideData, error) {
|
|
task, ok := s.GetTask(taskID)
|
|
if !ok {
|
|
return nil, ErrPptTaskNotFound
|
|
}
|
|
if task.UserID != userID {
|
|
return nil, ErrPptTaskNotFound
|
|
}
|
|
|
|
s.slidesLock.Lock()
|
|
defer s.slidesLock.Unlock()
|
|
|
|
var job model.PPTJob
|
|
if err := s.db.Where("task_id = ?", taskID).First(&job).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
slides := job.Slides
|
|
found := false
|
|
for i := range slides {
|
|
if slides[i].SlideIndex != slideIndex {
|
|
continue
|
|
}
|
|
found = true
|
|
sd := voToSlide(slides[i])
|
|
normalizeSlideImageHistory(&sd)
|
|
hist := sd.ImageHistory
|
|
if versionIndex < 0 || versionIndex >= len(hist) {
|
|
return nil, ErrPptInvalidVersionIndex
|
|
}
|
|
sd.ImageURL = hist[versionIndex].ImageURL
|
|
slides[i] = slideToVO(sd)
|
|
break
|
|
}
|
|
if !found {
|
|
return nil, ErrPptSlideNotFound
|
|
}
|
|
|
|
job.Slides = slides
|
|
biz := voSlidesToBiz(slides)
|
|
if err := s.refreshJobMeta(&job, biz); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out := voSlidesToBizNormalized(job.Slides)
|
|
return out, nil
|
|
}
|