From 911e58c29b7b61d1a2d272ae5dd9157f8c5264fd Mon Sep 17 00:00:00 2001 From: hanxuanyu <2252193204@qq.com> Date: Tue, 27 Jan 2026 12:21:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E9=A1=B5=E9=9D=A2=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E6=9B=B4=E6=96=B0=EF=BC=8C=E4=BD=BF=E7=94=A8?= =?UTF-8?q?vue=E8=BF=9B=E8=A1=8C=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/repo/migration.go | 94 + web/index.html | 404 --- webapp/.gitignore | 24 + webapp/README.md | 5 + webapp/components.json | 21 + webapp/index.html | 13 + webapp/package-lock.json | 2381 +++++++++++++++++ webapp/package.json | 33 + webapp/public/vite.svg | 1 + webapp/src/App.vue | 23 + webapp/src/assets/vue.svg | 1 + .../ui/alert-dialog/AlertDialog.vue | 15 + .../ui/alert-dialog/AlertDialogAction.vue | 18 + .../ui/alert-dialog/AlertDialogCancel.vue | 25 + .../ui/alert-dialog/AlertDialogContent.vue | 44 + .../alert-dialog/AlertDialogDescription.vue | 23 + .../ui/alert-dialog/AlertDialogFooter.vue | 22 + .../ui/alert-dialog/AlertDialogHeader.vue | 17 + .../ui/alert-dialog/AlertDialogTitle.vue | 21 + .../ui/alert-dialog/AlertDialogTrigger.vue | 12 + .../src/components/ui/alert-dialog/index.ts | 9 + webapp/src/components/ui/avatar/Avatar.vue | 18 + .../components/ui/avatar/AvatarFallback.vue | 21 + .../src/components/ui/avatar/AvatarImage.vue | 16 + webapp/src/components/ui/avatar/index.ts | 3 + webapp/src/components/ui/badge/Badge.vue | 26 + webapp/src/components/ui/badge/index.ts | 26 + webapp/src/components/ui/button/Button.vue | 29 + webapp/src/components/ui/button/index.ts | 38 + webapp/src/components/ui/card/Card.vue | 22 + webapp/src/components/ui/card/CardAction.vue | 17 + webapp/src/components/ui/card/CardContent.vue | 17 + .../components/ui/card/CardDescription.vue | 17 + webapp/src/components/ui/card/CardFooter.vue | 17 + webapp/src/components/ui/card/CardHeader.vue | 17 + webapp/src/components/ui/card/CardTitle.vue | 17 + webapp/src/components/ui/card/index.ts | 7 + .../src/components/ui/checkbox/Checkbox.vue | 35 + webapp/src/components/ui/checkbox/index.ts | 1 + webapp/src/components/ui/command/Command.vue | 87 + .../components/ui/command/CommandDialog.vue | 31 + .../components/ui/command/CommandEmpty.vue | 27 + .../components/ui/command/CommandGroup.vue | 45 + .../components/ui/command/CommandInput.vue | 39 + .../src/components/ui/command/CommandItem.vue | 76 + .../src/components/ui/command/CommandList.vue | 25 + .../ui/command/CommandSeparator.vue | 21 + .../components/ui/command/CommandShortcut.vue | 17 + webapp/src/components/ui/command/index.ts | 25 + webapp/src/components/ui/dialog/Dialog.vue | 19 + .../src/components/ui/dialog/DialogClose.vue | 15 + .../components/ui/dialog/DialogContent.vue | 53 + .../ui/dialog/DialogDescription.vue | 23 + .../src/components/ui/dialog/DialogFooter.vue | 15 + .../src/components/ui/dialog/DialogHeader.vue | 17 + .../components/ui/dialog/DialogOverlay.vue | 21 + .../ui/dialog/DialogScrollContent.vue | 59 + .../src/components/ui/dialog/DialogTitle.vue | 23 + .../components/ui/dialog/DialogTrigger.vue | 15 + webapp/src/components/ui/dialog/index.ts | 10 + webapp/src/components/ui/input/Input.vue | 33 + webapp/src/components/ui/input/index.ts | 1 + webapp/src/components/ui/label/Label.vue | 26 + webapp/src/components/ui/label/index.ts | 1 + .../src/components/ui/progress/Progress.vue | 38 + webapp/src/components/ui/progress/index.ts | 1 + .../components/ui/radio-group/RadioGroup.vue | 25 + .../ui/radio-group/RadioGroupItem.vue | 40 + webapp/src/components/ui/radio-group/index.ts | 2 + webapp/src/components/ui/select/Select.vue | 19 + .../components/ui/select/SelectContent.vue | 51 + .../src/components/ui/select/SelectGroup.vue | 15 + .../src/components/ui/select/SelectItem.vue | 44 + .../components/ui/select/SelectItemText.vue | 15 + .../src/components/ui/select/SelectLabel.vue | 17 + .../ui/select/SelectScrollDownButton.vue | 26 + .../ui/select/SelectScrollUpButton.vue | 26 + .../components/ui/select/SelectSeparator.vue | 19 + .../components/ui/select/SelectTrigger.vue | 33 + .../src/components/ui/select/SelectValue.vue | 15 + webapp/src/components/ui/select/index.ts | 11 + .../src/components/ui/separator/Separator.vue | 29 + webapp/src/components/ui/separator/index.ts | 1 + webapp/src/components/ui/sheet/Sheet.vue | 19 + webapp/src/components/ui/sheet/SheetClose.vue | 15 + .../src/components/ui/sheet/SheetContent.vue | 62 + .../components/ui/sheet/SheetDescription.vue | 21 + .../src/components/ui/sheet/SheetFooter.vue | 16 + .../src/components/ui/sheet/SheetHeader.vue | 15 + .../src/components/ui/sheet/SheetOverlay.vue | 21 + webapp/src/components/ui/sheet/SheetTitle.vue | 21 + .../src/components/ui/sheet/SheetTrigger.vue | 15 + webapp/src/components/ui/sheet/index.ts | 8 + .../src/components/ui/skeleton/Skeleton.vue | 17 + webapp/src/components/ui/skeleton/index.ts | 1 + webapp/src/components/ui/sonner/Sonner.vue | 42 + webapp/src/components/ui/sonner/index.ts | 1 + webapp/src/components/ui/switch/Switch.vue | 38 + webapp/src/components/ui/switch/index.ts | 1 + webapp/src/components/ui/table/Table.vue | 16 + webapp/src/components/ui/table/TableBody.vue | 17 + .../src/components/ui/table/TableCaption.vue | 17 + webapp/src/components/ui/table/TableCell.vue | 22 + webapp/src/components/ui/table/TableEmpty.vue | 34 + .../src/components/ui/table/TableFooter.vue | 17 + webapp/src/components/ui/table/TableHead.vue | 17 + .../src/components/ui/table/TableHeader.vue | 17 + webapp/src/components/ui/table/TableRow.vue | 17 + webapp/src/components/ui/table/index.ts | 9 + webapp/src/components/ui/table/utils.ts | 10 + webapp/src/components/ui/tabs/Tabs.vue | 24 + webapp/src/components/ui/tabs/TabsContent.vue | 21 + webapp/src/components/ui/tabs/TabsList.vue | 24 + webapp/src/components/ui/tabs/TabsTrigger.vue | 26 + webapp/src/components/ui/tabs/index.ts | 4 + .../src/components/ui/textarea/Textarea.vue | 28 + webapp/src/components/ui/textarea/index.ts | 1 + webapp/src/lib/utils.ts | 7 + webapp/src/main.ts | 5 + webapp/src/style.css | 120 + webapp/src/views/ComponentShowcase.vue | 734 +++++ webapp/tsconfig.app.json | 21 + webapp/tsconfig.json | 13 + webapp/tsconfig.node.json | 32 + webapp/vite.config.ts | 14 + 125 files changed, 5877 insertions(+), 404 deletions(-) create mode 100644 internal/repo/migration.go delete mode 100644 web/index.html create mode 100644 webapp/.gitignore create mode 100644 webapp/README.md create mode 100644 webapp/components.json create mode 100644 webapp/index.html create mode 100644 webapp/package-lock.json create mode 100644 webapp/package.json create mode 100644 webapp/public/vite.svg create mode 100644 webapp/src/App.vue create mode 100644 webapp/src/assets/vue.svg create mode 100644 webapp/src/components/ui/alert-dialog/AlertDialog.vue create mode 100644 webapp/src/components/ui/alert-dialog/AlertDialogAction.vue create mode 100644 webapp/src/components/ui/alert-dialog/AlertDialogCancel.vue create mode 100644 webapp/src/components/ui/alert-dialog/AlertDialogContent.vue create mode 100644 webapp/src/components/ui/alert-dialog/AlertDialogDescription.vue create mode 100644 webapp/src/components/ui/alert-dialog/AlertDialogFooter.vue create mode 100644 webapp/src/components/ui/alert-dialog/AlertDialogHeader.vue create mode 100644 webapp/src/components/ui/alert-dialog/AlertDialogTitle.vue create mode 100644 webapp/src/components/ui/alert-dialog/AlertDialogTrigger.vue create mode 100644 webapp/src/components/ui/alert-dialog/index.ts create mode 100644 webapp/src/components/ui/avatar/Avatar.vue create mode 100644 webapp/src/components/ui/avatar/AvatarFallback.vue create mode 100644 webapp/src/components/ui/avatar/AvatarImage.vue create mode 100644 webapp/src/components/ui/avatar/index.ts create mode 100644 webapp/src/components/ui/badge/Badge.vue create mode 100644 webapp/src/components/ui/badge/index.ts create mode 100644 webapp/src/components/ui/button/Button.vue create mode 100644 webapp/src/components/ui/button/index.ts create mode 100644 webapp/src/components/ui/card/Card.vue create mode 100644 webapp/src/components/ui/card/CardAction.vue create mode 100644 webapp/src/components/ui/card/CardContent.vue create mode 100644 webapp/src/components/ui/card/CardDescription.vue create mode 100644 webapp/src/components/ui/card/CardFooter.vue create mode 100644 webapp/src/components/ui/card/CardHeader.vue create mode 100644 webapp/src/components/ui/card/CardTitle.vue create mode 100644 webapp/src/components/ui/card/index.ts create mode 100644 webapp/src/components/ui/checkbox/Checkbox.vue create mode 100644 webapp/src/components/ui/checkbox/index.ts create mode 100644 webapp/src/components/ui/command/Command.vue create mode 100644 webapp/src/components/ui/command/CommandDialog.vue create mode 100644 webapp/src/components/ui/command/CommandEmpty.vue create mode 100644 webapp/src/components/ui/command/CommandGroup.vue create mode 100644 webapp/src/components/ui/command/CommandInput.vue create mode 100644 webapp/src/components/ui/command/CommandItem.vue create mode 100644 webapp/src/components/ui/command/CommandList.vue create mode 100644 webapp/src/components/ui/command/CommandSeparator.vue create mode 100644 webapp/src/components/ui/command/CommandShortcut.vue create mode 100644 webapp/src/components/ui/command/index.ts create mode 100644 webapp/src/components/ui/dialog/Dialog.vue create mode 100644 webapp/src/components/ui/dialog/DialogClose.vue create mode 100644 webapp/src/components/ui/dialog/DialogContent.vue create mode 100644 webapp/src/components/ui/dialog/DialogDescription.vue create mode 100644 webapp/src/components/ui/dialog/DialogFooter.vue create mode 100644 webapp/src/components/ui/dialog/DialogHeader.vue create mode 100644 webapp/src/components/ui/dialog/DialogOverlay.vue create mode 100644 webapp/src/components/ui/dialog/DialogScrollContent.vue create mode 100644 webapp/src/components/ui/dialog/DialogTitle.vue create mode 100644 webapp/src/components/ui/dialog/DialogTrigger.vue create mode 100644 webapp/src/components/ui/dialog/index.ts create mode 100644 webapp/src/components/ui/input/Input.vue create mode 100644 webapp/src/components/ui/input/index.ts create mode 100644 webapp/src/components/ui/label/Label.vue create mode 100644 webapp/src/components/ui/label/index.ts create mode 100644 webapp/src/components/ui/progress/Progress.vue create mode 100644 webapp/src/components/ui/progress/index.ts create mode 100644 webapp/src/components/ui/radio-group/RadioGroup.vue create mode 100644 webapp/src/components/ui/radio-group/RadioGroupItem.vue create mode 100644 webapp/src/components/ui/radio-group/index.ts create mode 100644 webapp/src/components/ui/select/Select.vue create mode 100644 webapp/src/components/ui/select/SelectContent.vue create mode 100644 webapp/src/components/ui/select/SelectGroup.vue create mode 100644 webapp/src/components/ui/select/SelectItem.vue create mode 100644 webapp/src/components/ui/select/SelectItemText.vue create mode 100644 webapp/src/components/ui/select/SelectLabel.vue create mode 100644 webapp/src/components/ui/select/SelectScrollDownButton.vue create mode 100644 webapp/src/components/ui/select/SelectScrollUpButton.vue create mode 100644 webapp/src/components/ui/select/SelectSeparator.vue create mode 100644 webapp/src/components/ui/select/SelectTrigger.vue create mode 100644 webapp/src/components/ui/select/SelectValue.vue create mode 100644 webapp/src/components/ui/select/index.ts create mode 100644 webapp/src/components/ui/separator/Separator.vue create mode 100644 webapp/src/components/ui/separator/index.ts create mode 100644 webapp/src/components/ui/sheet/Sheet.vue create mode 100644 webapp/src/components/ui/sheet/SheetClose.vue create mode 100644 webapp/src/components/ui/sheet/SheetContent.vue create mode 100644 webapp/src/components/ui/sheet/SheetDescription.vue create mode 100644 webapp/src/components/ui/sheet/SheetFooter.vue create mode 100644 webapp/src/components/ui/sheet/SheetHeader.vue create mode 100644 webapp/src/components/ui/sheet/SheetOverlay.vue create mode 100644 webapp/src/components/ui/sheet/SheetTitle.vue create mode 100644 webapp/src/components/ui/sheet/SheetTrigger.vue create mode 100644 webapp/src/components/ui/sheet/index.ts create mode 100644 webapp/src/components/ui/skeleton/Skeleton.vue create mode 100644 webapp/src/components/ui/skeleton/index.ts create mode 100644 webapp/src/components/ui/sonner/Sonner.vue create mode 100644 webapp/src/components/ui/sonner/index.ts create mode 100644 webapp/src/components/ui/switch/Switch.vue create mode 100644 webapp/src/components/ui/switch/index.ts create mode 100644 webapp/src/components/ui/table/Table.vue create mode 100644 webapp/src/components/ui/table/TableBody.vue create mode 100644 webapp/src/components/ui/table/TableCaption.vue create mode 100644 webapp/src/components/ui/table/TableCell.vue create mode 100644 webapp/src/components/ui/table/TableEmpty.vue create mode 100644 webapp/src/components/ui/table/TableFooter.vue create mode 100644 webapp/src/components/ui/table/TableHead.vue create mode 100644 webapp/src/components/ui/table/TableHeader.vue create mode 100644 webapp/src/components/ui/table/TableRow.vue create mode 100644 webapp/src/components/ui/table/index.ts create mode 100644 webapp/src/components/ui/table/utils.ts create mode 100644 webapp/src/components/ui/tabs/Tabs.vue create mode 100644 webapp/src/components/ui/tabs/TabsContent.vue create mode 100644 webapp/src/components/ui/tabs/TabsList.vue create mode 100644 webapp/src/components/ui/tabs/TabsTrigger.vue create mode 100644 webapp/src/components/ui/tabs/index.ts create mode 100644 webapp/src/components/ui/textarea/Textarea.vue create mode 100644 webapp/src/components/ui/textarea/index.ts create mode 100644 webapp/src/lib/utils.ts create mode 100644 webapp/src/main.ts create mode 100644 webapp/src/style.css create mode 100644 webapp/src/views/ComponentShowcase.vue create mode 100644 webapp/tsconfig.app.json create mode 100644 webapp/tsconfig.json create mode 100644 webapp/tsconfig.node.json create mode 100644 webapp/vite.config.ts diff --git a/internal/repo/migration.go b/internal/repo/migration.go new file mode 100644 index 0000000..698dde2 --- /dev/null +++ b/internal/repo/migration.go @@ -0,0 +1,94 @@ +package repo + +import ( + "BingPaper/internal/config" + "BingPaper/internal/model" + "BingPaper/internal/util" + "fmt" + + "go.uber.org/zap" + "gorm.io/gorm" +) + +// MigrateDataToNewDB 将数据从旧数据库迁移到新数据库 +func MigrateDataToNewDB(oldDB *gorm.DB, newConfig *config.Config) error { + util.Logger.Info("Starting data migration to new database", + zap.String("new_type", newConfig.DB.Type), + zap.String("new_dsn", newConfig.DB.DSN)) + + // 1. 初始化新数据库连接 + dialector, err := GetDialector(newConfig.DB.Type, newConfig.DB.DSN) + if err != nil { + return fmt.Errorf("failed to get dialector for new DB: %w", err) + } + + gormConfig := GetGormConfig(newConfig) + newDB, err := gorm.Open(dialector, gormConfig) + if err != nil { + return fmt.Errorf("failed to connect to new DB: %w", err) + } + + // 2. 自动迁移结构 + if err := newDB.AutoMigrate(&model.Image{}, &model.ImageVariant{}, &model.Token{}); err != nil { + return fmt.Errorf("failed to migrate schema in new DB: %w", err) + } + + // 3. 清空新数据库中的现有数据(防止冲突) + util.Logger.Info("Cleaning up destination database before migration") + // 备份或清空目标数据库。由于用户要求“可能需要清空或备份”, + // 这里我们选择在迁移前清空目标表,以确保迁移过来的数据是完整且不冲突的。 + if err := newDB.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&model.ImageVariant{}).Error; err != nil { + return fmt.Errorf("failed to clear ImageVariants: %w", err) + } + if err := newDB.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&model.Image{}).Error; err != nil { + return fmt.Errorf("failed to clear Images: %w", err) + } + if err := newDB.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&model.Token{}).Error; err != nil { + return fmt.Errorf("failed to clear Tokens: %w", err) + } + + // 4. 开始迁移数据 + // 使用事务确保迁移的原子性 + return newDB.Transaction(func(tx *gorm.DB) error { + // 迁移 Images + var images []model.Image + if err := oldDB.Find(&images).Error; err != nil { + return fmt.Errorf("failed to fetch images from old DB: %w", err) + } + if len(images) > 0 { + util.Logger.Info("Migrating images", zap.Int("count", len(images))) + if err := tx.Create(&images).Error; err != nil { + return fmt.Errorf("failed to insert images into new DB: %w", err) + } + } + + // 迁移 ImageVariants + var variants []model.ImageVariant + if err := oldDB.Find(&variants).Error; err != nil { + return fmt.Errorf("failed to fetch variants from old DB: %w", err) + } + if len(variants) > 0 { + util.Logger.Info("Migrating variants", zap.Int("count", len(variants))) + if err := tx.Create(&variants).Error; err != nil { + return fmt.Errorf("failed to insert variants into new DB: %w", err) + } + } + + // 迁移 Tokens + var tokens []model.Token + if err := oldDB.Find(&tokens).Error; err != nil { + return fmt.Errorf("failed to fetch tokens from old DB: %w", err) + } + if len(tokens) > 0 { + util.Logger.Info("Migrating tokens", zap.Int("count", len(tokens))) + if err := tx.Create(&tokens).Error; err != nil { + return fmt.Errorf("failed to insert tokens into new DB: %w", err) + } + } + + // 更新全局 DB 指针 + DB = newDB + util.Logger.Info("Data migration completed successfully") + return nil + }) +} diff --git a/web/index.html b/web/index.html deleted file mode 100644 index fb46007..0000000 --- a/web/index.html +++ /dev/null @@ -1,404 +0,0 @@ - - -
- - -