From 23a8051e9633d6f95424034249e95204ab1cf545 Mon Sep 17 00:00:00 2001 From: zerosaturation Date: Thu, 4 Jun 2026 16:41:20 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E5=A4=B4=E5=83=8F?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=B8=8D=E5=85=A8=EF=BC=8C=E4=BF=AE=E6=94=B9?= =?UTF-8?q?docker=E7=9A=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/.env.example | 5 +- .../controller/laser_generate_controller.go | 8 +- .../repository/social_repository.go | 2 +- backend/services/taskService/main.go | 14 +- docker/.env.prod | 13 + docker/Dockerfile.services | 20 +- docker/SCHEMA_DIFF_REPORT.txt | 472 ++++++++++++++++++ docker/build.sh | 3 +- docker/deploy.sh | 1 + docker/docker-compose.local.yml | 53 ++ docker/docker-compose.prod.yml | 61 ++- frontend/manifest.json | 2 +- frontend/pages/profile/myWorks.vue | 59 ++- 13 files changed, 666 insertions(+), 47 deletions(-) create mode 100644 docker/SCHEMA_DIFF_REPORT.txt diff --git a/backend/.env.example b/backend/.env.example index f4e45fa..79e798d 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -72,4 +72,7 @@ DIFY_API_BASE=https://api.dify.ai/v1 # Dify App API Key(laser_card_variants_v1 工作流) DIFY_API_KEY= # laser-compositor 内网地址 -LASER_COMPOSITOR_URL=http://127.0.0.1:7000 +# 注意:7000 端口在 macOS 上被 AirPlay Receiver 占用,因此改用 7002 +LASER_COMPOSITOR_URL=http://127.0.0.1:7002 +# laser-compositor 监听端口(与 URL 端口一致) +COMPOSITOR_PORT=7002 diff --git a/backend/gateway/controller/laser_generate_controller.go b/backend/gateway/controller/laser_generate_controller.go index cadaf9d..e16d063 100644 --- a/backend/gateway/controller/laser_generate_controller.go +++ b/backend/gateway/controller/laser_generate_controller.go @@ -295,8 +295,12 @@ func (ctrl *LaserGenerateController) runParallelGeneration(userID, starID int64, logger.Logger.Info("Prompt merged", zap.String("preset", vc.PresetID), - zap.String("bg_full", safePrefix(finalBgPrompt, 80)), - zap.String("ol_full", safePrefix(finalOverlayPrompt, 80)), + zap.String("bg_preview", safePrefix(finalBgPrompt, 80)), + zap.String("ol_preview", safePrefix(finalOverlayPrompt, 80)), + zap.String("bg_full", finalBgPrompt), + zap.String("ol_full", finalOverlayPrompt), + zap.Int("bg_len", len(finalBgPrompt)), + zap.Int("ol_len", len(finalOverlayPrompt)), ) // Step 1: MiniMax 生成背景图(纯文生图,不含人物) diff --git a/backend/services/socialService/repository/social_repository.go b/backend/services/socialService/repository/social_repository.go index 39c95e9..936a8a1 100644 --- a/backend/services/socialService/repository/social_repository.go +++ b/backend/services/socialService/repository/social_repository.go @@ -1054,7 +1054,7 @@ func (r *socialRepositoryImpl) GetByAssetWithUsers(assetID, starID int64, cursor Select(`al.user_id, al.star_id, COALESCE(fp.nickname, '匿名用户') as nickname, - u.avatar_url, + COALESCE(fp.avatar_url, u.avatar_url) as avatar_url, COALESCE(fp.level, 1) as fan_level, al.created_at as liked_at`). Joins("JOIN users u ON al.user_id = u.id"). diff --git a/backend/services/taskService/main.go b/backend/services/taskService/main.go index 64189c1..71dc29d 100644 --- a/backend/services/taskService/main.go +++ b/backend/services/taskService/main.go @@ -13,6 +13,7 @@ import ( "dubbo.apache.org/dubbo-go/v3/server" "github.com/topfans/backend/pkg/database" + "github.com/topfans/backend/pkg/health" "github.com/topfans/backend/pkg/logger" pb "github.com/topfans/backend/pkg/proto/task" pbGallery "github.com/topfans/backend/pkg/proto/gallery" @@ -28,7 +29,10 @@ import ( "github.com/topfans/backend/services/taskService/worker" ) -var port = flag.Int("port", 20006, "Dubbo service port") +var ( + port = flag.Int("port", 20006, "Dubbo service port") + healthHandler *health.Handler +) func main() { // 1. Init logger(必须最前) @@ -123,6 +127,11 @@ func main() { go resetWorker.Start() logger.Logger.Info("Reset worker started") + // 7.1 启动健康检查 HTTP 服务器(端口 20006+1000=21006,路径 /health) + healthPort := *port + 1000 + healthHandler = health.NewHandler("task-service", healthPort) + healthHandler.Start() + // 8. Init providers mobileProvider := provider.NewTaskMobileProvider(dailySvc, onboardingSvc, revenueSvc) internalProvider := provider.NewTaskInternalProvider(onboardingSvc, revenueSvc) @@ -160,5 +169,8 @@ func main() { signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit logger.Logger.Info("Shutting down taskService...") + if healthHandler != nil { + healthHandler.Stop() + } resetWorker.Stop() } diff --git a/docker/.env.prod b/docker/.env.prod index 6ddd6af..5e7ecf6 100644 --- a/docker/.env.prod +++ b/docker/.env.prod @@ -31,6 +31,19 @@ REDIS_PORT=6379 REDIS_PASSWORD=123456 REDIS_DB=0 +# ==================== Dify Workflow (laser_card_v1) ==================== +# 注意:当前 laser 卡生成走的是 gateway 内的 MiniMax 直接调用(不是 Dify), +# 这两个变量仅为 Dify 工作流调用保留,目前未使用 +DIFY_API_BASE=https://api.dify.ai/v1 +DIFY_API_KEY= + +# ==================== Laser Card ==================== +# 镭射卡 6 层合成服务(gateway 通过容器名 + 端口访问) +LASER_COMPOSITOR_URL=http://lasercompositor:7002 +COMPOSITOR_PORT=7002 +# 抠图服务:imageseg=阿里云 SegmentHDBody / ivpd=IVPD SegmentImage / auto=自部署HTTP→imageseg→IVPD +SEGMENT_PROVIDER=imageseg + # ==================== SMS Configuration ==================== SMS_ACCESS_KEY_ID=LTAI5t6QcdJHpYbCPxM8SXYE SMS_ACCESS_KEY_SECRET=ybvjSEb7wilMt3qT5nOppYPoNVayCD diff --git a/docker/Dockerfile.services b/docker/Dockerfile.services index e2da719..f11fac2 100644 --- a/docker/Dockerfile.services +++ b/docker/Dockerfile.services @@ -51,7 +51,10 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" \ echo "Built starbookservice" && \ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" \ -o /tmp/aichatservice services/aiChatService/main.go && \ - echo "Built aichatservice" + echo "Built aichatservice" && \ + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" \ + -o /tmp/lasercompositor services/laserCompositor/main.go && \ + echo "Built lasercompositor" # ---- Runtime Stage: Gateway ---- FROM --platform=linux/amd64 alpine:3.19 AS gateway @@ -189,3 +192,18 @@ HEALTHCHECK --interval=10s --timeout=5s --start-period=30s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:21008 || exit 1 ENTRYPOINT ["/app/aichatservice"] + +# ---- Runtime Stage: LaserCompositor (镭射卡 6 层合成服务) ---- +FROM --platform=linux/amd64 alpine:3.19 AS lasercompositor + +RUN apk add --no-cache ca-certificates tzdata + +WORKDIR /app +COPY --from=builder /tmp/lasercompositor /app/lasercompositor + +EXPOSE 7002 + +HEALTHCHECK --interval=10s --timeout=5s --start-period=15s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:7002/health || exit 1 + +ENTRYPOINT ["/app/lasercompositor"] diff --git a/docker/SCHEMA_DIFF_REPORT.txt b/docker/SCHEMA_DIFF_REPORT.txt new file mode 100644 index 0000000..2ba6cd3 --- /dev/null +++ b/docker/SCHEMA_DIFF_REPORT.txt @@ -0,0 +1,472 @@ +============================================================================== +SCHEMA DIFF REPORT — 本地 top-fans vs 服务器 topfans +============================================================================== + +仅本地有 (0): 无 +仅服务器有 (0): 无 + +────────────────────────────────────────────────────────────────────────────── +📋 activity_assets +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(3): + + activity_assets_asset_id_key CREATE UNIQUE INDEX activity_assets_asset_id_key ON public.activity_assets USING btree (asset_id) NOT NULL default=∅ + + idx_activity_asset_id CREATE INDEX idx_activity_asset_id ON public.activity_assets USING btree (asset_id) NOT NULL default=∅ + + uk_activity_owner_activity_name CREATE UNIQUE INDEX uk_activity_owner_activity_name ON public.activity_assets USING btree (owner_uid, activity_id, name) NOT NULL default=∅ + ⚠️ 服务器多列(1)— 本地无: + - uni_activity_assets_asset_id CREATE UNIQUE INDEX uni_activity_assets_asset_id ON public.activity_assets USING btree (asset_id) NOT NULL default=∅ + 🔄 列定义不同(3): + ~ idx_activity_star + 本地: CREATE INDEX idx_activity_star ON public.activity_assets USING btree (star_id, activity_id) NOT NULL default=∅ + 服务器: CREATE INDEX idx_activity_star ON public.activity_assets USING btree (star_id) NOT NULL default=∅ + ~ like_count + 本地: integer NOT NULL default=0 + 服务器: integer NULL default=0 + ~ status + 本地: smallint NOT NULL default=0 + 服务器: integer NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 ai_chat_configs +────────────────────────────────────────────────────────────────────────────── + ⚠️ 服务器多列(1)— 本地无: + - idx_ai_chat_configs_config_key CREATE UNIQUE INDEX idx_ai_chat_configs_config_key ON public.ai_chat_configs USING btree (config_key) NOT NULL default=∅ + 🔄 列定义不同(4): + ~ category + 本地: character varying NOT NULL default=∅ + 服务器: character varying NULL default=∅ + ~ config_type + 本地: character varying NOT NULL default='string'::character varying + 服务器: character varying NULL default='string'::character varying + ~ created_at + 本地: bigint NOT NULL default=(EXTRACT(epoch FROM now()) * (1000)::numeric) + 服务器: bigint NULL default=∅ + ~ updated_at + 本地: bigint NOT NULL default=(EXTRACT(epoch FROM now()) * (1000)::numeric) + 服务器: bigint NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 asset_level_change_logs +────────────────────────────────────────────────────────────────────────────── + 🔄 列定义不同(2): + ~ trigger_hours + 本地: bigint NULL default=0 + 服务器: integer NULL default=0 + ~ trigger_likes + 本地: bigint NULL default=0 + 服务器: integer NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 asset_level_records +────────────────────────────────────────────────────────────────────────────── + 🔄 列定义不同(4): + ~ lifetime_exhibition_hours + 本地: bigint NOT NULL default=0 + 服务器: integer NOT NULL default=0 + ~ lifetime_likes + 本地: bigint NOT NULL default=0 + 服务器: integer NOT NULL default=0 + ~ season_exhibition_hours + 本地: bigint NOT NULL default=0 + 服务器: integer NOT NULL default=0 + ~ season_likes + 本地: bigint NOT NULL default=0 + 服务器: integer NOT NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 asset_likes +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(2): + + idx_asset_likes_asset_created CREATE INDEX idx_asset_likes_asset_created ON public.asset_likes USING btree (asset_id, created_at DESC) NOT NULL default=∅ + + uk_asset_likes_user_asset_exhibition CREATE UNIQUE INDEX uk_asset_likes_user_asset_exhibition ON public.asset_likes USING btree (user_id, asset_id, exhibition_id) NOT NULL default=∅ + ⚠️ 服务器多列(2)— 本地无: + - uk_asset_likes_user_asset CREATE UNIQUE INDEX uk_asset_likes_user_asset ON public.asset_likes USING btree (asset_id, user_id) NOT NULL default=∅ + - uk_asset_likes_user_asset_star CREATE UNIQUE INDEX uk_asset_likes_user_asset_star ON public.asset_likes USING btree (user_id, asset_id, star_id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 asset_material_relations +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(3): + + idx_amr_asset_type_layer CREATE INDEX idx_amr_asset_type_layer ON public.asset_material_relations USING btree (asset_id, material_type, layer_order) WHERE (deleted_at IS NULL) NOT NULL default=∅ + + uk_amr_asset_layer_active CREATE UNIQUE INDEX uk_amr_asset_layer_active ON public.asset_material_relations USING btree (asset_id, layer_order) WHERE (deleted_at IS NULL) NOT NULL default=∅ + + uk_amr_asset_type_active CREATE UNIQUE INDEX uk_amr_asset_type_active ON public.asset_material_relations USING btree (asset_id, material_type) WHERE (deleted_at IS NULL) NOT NULL default=∅ + 🔄 列定义不同(8): + ~ idx_amr_asset_id + 本地: CREATE INDEX idx_amr_asset_id ON public.asset_material_relations USING btree (asset_id) WHERE (deleted_at IS NULL) NOT NULL default=∅ + 服务器: CREATE INDEX idx_amr_asset_id ON public.asset_material_relations USING btree (asset_id) NOT NULL default=∅ + ~ idx_amr_material_id + 本地: CREATE INDEX idx_amr_material_id ON public.asset_material_relations USING btree (material_id) WHERE (deleted_at IS NULL) NOT NULL default=∅ + 服务器: CREATE INDEX idx_amr_material_id ON public.asset_material_relations USING btree (material_id) NOT NULL default=∅ + ~ layer_order + 本地: integer NOT NULL default=0 + 服务器: bigint NOT NULL default=0 + ~ opacity + 本地: double precision NULL default=1.0 + 服务器: numeric NULL default=1 + ~ rotation + 本地: double precision NULL default=0 + 服务器: numeric NULL default=0 + ~ scale_x + 本地: double precision NULL default=1.0 + 服务器: numeric NULL default=1 + ~ scale_y + 本地: double precision NULL default=1.0 + 服务器: numeric NULL default=1 + ~ version + 本地: integer NOT NULL default=1 + 服务器: bigint NOT NULL default=1 + +────────────────────────────────────────────────────────────────────────────── +📋 asset_registry +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(5): + + idx_registry_star_activity CREATE INDEX idx_registry_star_activity ON public.asset_registry USING btree (star_id, activity_id) WHERE ((asset_type) NOT NULL default=text = 'activity'::text) + + idx_registry_star_grade CREATE INDEX idx_registry_star_grade ON public.asset_registry USING btree (star_id, grade) WHERE ((asset_type) NOT NULL default=text = 'regular'::text) + + idx_registry_type_star CREATE INDEX idx_registry_type_star ON public.asset_registry USING btree (asset_type, star_id) NOT NULL default=∅ + + uk_registry_asset_type_id CREATE UNIQUE INDEX uk_registry_asset_type_id ON public.asset_registry USING btree (asset_type, asset_id) NOT NULL default=∅ + + uk_registry_owner_star_type_asset CREATE UNIQUE INDEX uk_registry_owner_star_type_asset ON public.asset_registry USING btree (owner_uid, star_id, asset_type, asset_id) NOT NULL default=∅ + 🔄 列定义不同(3): + ~ display_status + 本地: integer NOT NULL default=0 + 服务器: integer NULL default=0 + ~ like_count + 本地: integer NOT NULL default=0 + 服务器: integer NULL default=0 + ~ status + 本地: smallint NOT NULL default=0 + 服务器: integer NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 assets +────────────────────────────────────────────────────────────────────────────── + ⚠️ 服务器多列(1)— 本地无: + - rarity integer NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 booth_slots +────────────────────────────────────────────────────────────────────────────── + 🔄 列定义不同(2): + ~ star_id + 本地: bigint NULL default=∅ + 服务器: bigint NOT NULL default=∅ + ~ user_id + 本地: bigint NULL default=∅ + 服务器: bigint NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 coin_transaction_records +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(6): + + change_type character varying NOT NULL default=∅ + + coin_transaction_records_pkey CREATE UNIQUE INDEX coin_transaction_records_pkey ON public.coin_transaction_records USING btree (id) NOT NULL default=∅ + + delta bigint NOT NULL default=∅ + + ix_coin_tx_created CREATE INDEX ix_coin_tx_created ON public.coin_transaction_records USING btree (created_at DESC) NOT NULL default=∅ + + ix_coin_tx_user_star CREATE INDEX ix_coin_tx_user_star ON public.coin_transaction_records USING btree (user_id, star_id) NOT NULL default=∅ + + source_id character varying NULL default=∅ + ⚠️ 服务器多列(8)— 本地无: + - amount bigint NOT NULL default=∅ + - idx_coin_transaction_records_created CREATE INDEX idx_coin_transaction_records_created ON public.coin_transaction_records USING btree (created_at DESC) NOT NULL default=∅ + - idx_coin_transaction_records_star CREATE INDEX idx_coin_transaction_records_star ON public.coin_transaction_records USING btree (star_id) NOT NULL default=∅ + - idx_coin_transaction_records_user CREATE INDEX idx_coin_transaction_records_user ON public.coin_transaction_records USING btree (user_id) NOT NULL default=∅ + - reference_id character varying NULL default=∅ + - reference_type character varying NULL default=∅ + - transaction_type character varying NOT NULL default=∅ + - uk_coin_transaction_records_id CREATE UNIQUE INDEX uk_coin_transaction_records_id ON public.coin_transaction_records USING btree (id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 collection_assets +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(3): + + collection_assets_asset_id_key CREATE UNIQUE INDEX collection_assets_asset_id_key ON public.collection_assets USING btree (asset_id) NOT NULL default=∅ + + idx_collection_asset_id CREATE INDEX idx_collection_asset_id ON public.collection_assets USING btree (asset_id) NOT NULL default=∅ + + uk_collection_owner_star_name CREATE UNIQUE INDEX uk_collection_owner_star_name ON public.collection_assets USING btree (owner_uid, star_id, name) NOT NULL default=∅ + ⚠️ 服务器多列(1)— 本地无: + - uni_collection_assets_asset_id CREATE UNIQUE INDEX uni_collection_assets_asset_id ON public.collection_assets USING btree (asset_id) NOT NULL default=∅ + 🔄 列定义不同(2): + ~ like_count + 本地: integer NOT NULL default=0 + 服务器: integer NULL default=0 + ~ status + 本地: smallint NOT NULL default=0 + 服务器: integer NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 crystal_transaction_records +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(4): + + crystal_transaction_records_pkey CREATE UNIQUE INDEX crystal_transaction_records_pkey ON public.crystal_transaction_records USING btree (id) NOT NULL default=∅ + + ix_crystal_tx_change_type CREATE INDEX ix_crystal_tx_change_type ON public.crystal_transaction_records USING btree (change_type) NOT NULL default=∅ + + ix_crystal_tx_created CREATE INDEX ix_crystal_tx_created ON public.crystal_transaction_records USING btree (created_at DESC) NOT NULL default=∅ + + ix_crystal_tx_user_star CREATE INDEX ix_crystal_tx_user_star ON public.crystal_transaction_records USING btree (user_id, star_id) NOT NULL default=∅ + ⚠️ 服务器多列(5)— 本地无: + - idx_crystal_transaction_records_created CREATE INDEX idx_crystal_transaction_records_created ON public.crystal_transaction_records USING btree (created_at DESC) NOT NULL default=∅ + - idx_crystal_transaction_records_star CREATE INDEX idx_crystal_transaction_records_star ON public.crystal_transaction_records USING btree (star_id) NOT NULL default=∅ + - idx_crystal_transaction_records_user CREATE INDEX idx_crystal_transaction_records_user ON public.crystal_transaction_records USING btree (user_id) NOT NULL default=∅ + - reference_type character varying NULL default=∅ + - uk_crystal_transaction_records_id CREATE UNIQUE INDEX uk_crystal_transaction_records_id ON public.crystal_transaction_records USING btree (id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 dazi_level_thresholds +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(3): + + condition_param integer NULL default=0 + + description character varying NULL default=∅ + + upgrade_condition character varying NULL default=∅ + ⚠️ 服务器多列(5)— 本地无: + - exp_requirement bigint NOT NULL default=0 + - id bigint NOT NULL default=nextval('dazi_level_thresholds_id_seq'::regclass) + - title character varying NULL default=∅ + - uk_dazi_level_thresholds_level CREATE UNIQUE INDEX uk_dazi_level_thresholds_level ON public.dazi_level_thresholds USING btree (level) NOT NULL default=∅ + - updated_at bigint NOT NULL default=∅ + 🔄 列定义不同(1): + ~ dazi_level_thresholds_pkey + 本地: CREATE UNIQUE INDEX dazi_level_thresholds_pkey ON public.dazi_level_thresholds USING btree (level) NOT NULL default=∅ + 服务器: CREATE UNIQUE INDEX dazi_level_thresholds_pkey ON public.dazi_level_thresholds USING btree (id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 exhibitions +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + idx_asset CREATE INDEX idx_asset ON public.exhibitions USING btree (asset_id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 fan_profiles +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + revenue_boost_bps integer NOT NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 laser_card_instances +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + uk_lci_user_client_req CREATE UNIQUE INDEX uk_lci_user_client_req ON public.laser_card_instances USING btree (owner_user_id, client_request_id) WHERE ((deleted_at IS NULL) AND (client_request_id IS NOT NULL)) NOT NULL default=∅ + 🔄 列定义不同(3): + ~ idx_lci_asset + 本地: CREATE INDEX idx_lci_asset ON public.laser_card_instances USING btree (asset_id) WHERE ((asset_id IS NOT NULL) AND (deleted_at IS NULL)) NOT NULL default=∅ + 服务器: CREATE INDEX idx_lci_asset ON public.laser_card_instances USING btree (asset_id) NOT NULL default=∅ + ~ idx_lci_owner_status + 本地: CREATE INDEX idx_lci_owner_status ON public.laser_card_instances USING btree (owner_user_id, status) WHERE (deleted_at IS NULL) NOT NULL default=∅ + 服务器: CREATE INDEX idx_lci_owner_status ON public.laser_card_instances USING btree (owner_user_id, status) NOT NULL default=∅ + ~ idx_lci_star_created + 本地: CREATE INDEX idx_lci_star_created ON public.laser_card_instances USING btree (star_id, created_at DESC) WHERE (deleted_at IS NULL) NOT NULL default=∅ + 服务器: CREATE INDEX idx_lci_star_created ON public.laser_card_instances USING btree (star_id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 laser_card_operation_logs +────────────────────────────────────────────────────────────────────────────── + 🔄 列定义不同(3): + ~ idx_lclog_action_time + 本地: CREATE INDEX idx_lclog_action_time ON public.laser_card_operation_logs USING btree (action, created_at DESC) NOT NULL default=∅ + 服务器: CREATE INDEX idx_lclog_action_time ON public.laser_card_operation_logs USING btree (action) NOT NULL default=∅ + ~ idx_lclog_instance_time + 本地: CREATE INDEX idx_lclog_instance_time ON public.laser_card_operation_logs USING btree (instance_id, created_at DESC) NOT NULL default=∅ + 服务器: CREATE INDEX idx_lclog_instance_time ON public.laser_card_operation_logs USING btree (instance_id) NOT NULL default=∅ + ~ idx_lclog_operator_time + 本地: CREATE INDEX idx_lclog_operator_time ON public.laser_card_operation_logs USING btree (operator_user_id, created_at DESC) NOT NULL default=∅ + 服务器: CREATE INDEX idx_lclog_operator_time ON public.laser_card_operation_logs USING btree (operator_user_id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 laser_card_templates +────────────────────────────────────────────────────────────────────────────── + 🔄 列定义不同(2): + ~ idx_lct_status_star + 本地: CREATE INDEX idx_lct_status_star ON public.laser_card_templates USING btree (status, star_id) WHERE (deleted_at IS NULL) NOT NULL default=∅ + 服务器: CREATE INDEX idx_lct_status_star ON public.laser_card_templates USING btree (status, star_id) NOT NULL default=∅ + ~ uk_lct_code_version + 本地: CREATE UNIQUE INDEX uk_lct_code_version ON public.laser_card_templates USING btree (template_code, version) WHERE (deleted_at IS NULL) NOT NULL default=∅ + 服务器: CREATE UNIQUE INDEX uk_lct_code_version ON public.laser_card_templates USING btree (template_code, version) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 level_cap_config +────────────────────────────────────────────────────────────────────────────── + ⚠️ 服务器多列(3)— 本地无: + - exp_multiplier numeric NOT NULL default=1.0 + - star_id bigint NOT NULL default=∅ + - uk_level_cap_config_star CREATE UNIQUE INDEX uk_level_cap_config_star ON public.level_cap_config USING btree (star_id) NOT NULL default=∅ + 🔄 列定义不同(1): + ~ max_level + 本地: integer NOT NULL default=20 + 服务器: integer NOT NULL default=50 + +────────────────────────────────────────────────────────────────────────────── +📋 level_thresholds +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(2): + + description character varying NULL default=∅ + + like_bet_count integer NOT NULL default=∅ + ⚠️ 服务器多列(7)— 本地无: + - badge_url character varying NULL default=∅ + - exp_requirement bigint NOT NULL default=0 + - id bigint NOT NULL default=nextval('level_thresholds_id_seq'::regclass) + - privileges jsonb NULL default=∅ + - title character varying NULL default=∅ + - uk_level_thresholds_level CREATE UNIQUE INDEX uk_level_thresholds_level ON public.level_thresholds USING btree (level) NOT NULL default=∅ + - updated_at bigint NOT NULL default=∅ + 🔄 列定义不同(2): + ~ level_thresholds_pkey + 本地: CREATE UNIQUE INDEX level_thresholds_pkey ON public.level_thresholds USING btree (level) NOT NULL default=∅ + 服务器: CREATE UNIQUE INDEX level_thresholds_pkey ON public.level_thresholds USING btree (id) NOT NULL default=∅ + ~ max_exhibition_hours + 本地: bigint NOT NULL default=∅ + 服务器: integer NOT NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 level_up_reward_config +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(3): + + is_enabled boolean NULL default=true + + reward_value bigint NOT NULL default=0 + + uk_level_reward_type CREATE UNIQUE INDEX uk_level_reward_type ON public.level_up_reward_config USING btree (level, reward_type) NOT NULL default=∅ + ⚠️ 服务器多列(3)— 本地无: + - description character varying NULL default=∅ + - reward_amount bigint NOT NULL default=0 + - uk_level_up_reward_level_type CREATE UNIQUE INDEX uk_level_up_reward_level_type ON public.level_up_reward_config USING btree (level, reward_type) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 level_upgrade_conditions +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(2): + + require_dazi_level integer NULL default=0 + + require_total_hours bigint NOT NULL default=∅ + ⚠️ 服务器多列(4)— 本地无: + - condition_type character varying NOT NULL default=∅ + - condition_value bigint NOT NULL default=0 + - id bigint NOT NULL default=nextval('level_upgrade_conditions_id_seq'::regclass) + - uk_level_upgrade_conditions_level_type CREATE UNIQUE INDEX uk_level_upgrade_conditions_level_type ON public.level_upgrade_conditions USING btree (level, condition_type) NOT NULL default=∅ + 🔄 列定义不同(1): + ~ level_upgrade_conditions_pkey + 本地: CREATE UNIQUE INDEX level_upgrade_conditions_pkey ON public.level_upgrade_conditions USING btree (level) NOT NULL default=∅ + 服务器: CREATE UNIQUE INDEX level_upgrade_conditions_pkey ON public.level_upgrade_conditions USING btree (id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 materials +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + uk_materials_oss_key CREATE UNIQUE INDEX uk_materials_oss_key ON public.materials USING btree (oss_key) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 mint_cost_config +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + mint_cost_config_mint_count_key CREATE UNIQUE INDEX mint_cost_config_mint_count_key ON public.mint_cost_config USING btree (mint_count) NOT NULL default=∅ + ⚠️ 服务器多列(1)— 本地无: + - uk_mint_cost_config_mint_count CREATE UNIQUE INDEX uk_mint_cost_config_mint_count ON public.mint_cost_config USING btree (mint_count) NOT NULL default=∅ + 🔄 列定义不同(2): + ~ probability + 本地: bigint NULL default=0 + 服务器: bigint NOT NULL default=0 + ~ reward_value + 本地: bigint NULL default=0 + 服务器: bigint NOT NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 mint_milestone_config +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(5): + + bonus_reward bigint NOT NULL default=∅ + + created_at bigint NOT NULL default=∅ + + milestone_count integer NOT NULL default=∅ + + star_id bigint NOT NULL default=∅ + + uk_milestone_star_count CREATE UNIQUE INDEX uk_milestone_star_count ON public.mint_milestone_config USING btree (star_id, milestone_count) NOT NULL default=∅ + ⚠️ 服务器多列(6)— 本地无: + - description character varying NULL default=∅ + - milestone integer NOT NULL default=∅ + - reward_amount bigint NOT NULL default=0 + - reward_type character varying NOT NULL default=∅ + - uk_mint_milestone_config_milestone CREATE UNIQUE INDEX uk_mint_milestone_config_milestone ON public.mint_milestone_config USING btree (milestone) NOT NULL default=∅ + - updated_at bigint NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 mint_reward_config +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(4): + + base_reward bigint NOT NULL default=0 + + is_enabled boolean NULL default=true + + mint_reward_config_star_id_key CREATE UNIQUE INDEX mint_reward_config_star_id_key ON public.mint_reward_config USING btree (star_id) NOT NULL default=∅ + + star_id bigint NOT NULL default=∅ + ⚠️ 服务器多列(6)— 本地无: + - description character varying NULL default=∅ + - mint_count integer NOT NULL default=∅ + - probability bigint NOT NULL default=0 + - reward_amount bigint NOT NULL default=0 + - reward_type character varying NOT NULL default=∅ + - uk_mint_reward_config_mint_count_type CREATE UNIQUE INDEX uk_mint_reward_config_mint_count_type ON public.mint_reward_config USING btree (mint_count, reward_type) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 minting_activities +────────────────────────────────────────────────────────────────────────────── + ⚠️ 服务器多列(2)— 本地无: + - idx_minting_activities_sort_order CREATE INDEX idx_minting_activities_sort_order ON public.minting_activities USING btree (sort_order DESC) NOT NULL default=∅ + - sort_order integer NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 onboarding_stage_config +────────────────────────────────────────────────────────────────────────────── + 🔄 列定义不同(1): + ~ sort_order + 本地: integer NULL default=0 + 服务器: bigint NULL default=0 + +────────────────────────────────────────────────────────────────────────────── +📋 season_decay_config +────────────────────────────────────────────────────────────────────────────── + 🔄 列定义不同(1): + ~ preserve_percent + 本地: bigint NOT NULL default=100 + 服务器: integer NOT NULL default=100 + +────────────────────────────────────────────────────────────────────────────── +📋 task_definitions +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + ix_task_def_star_key CREATE UNIQUE INDEX ix_task_def_star_key ON public.task_definitions USING btree (star_id, task_key) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 user_account_status +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + idx_user_account_status_user_id CREATE INDEX idx_user_account_status_user_id ON public.user_account_status USING btree (user_id) NOT NULL default=∅ + 🔄 列定义不同(1): + ~ id + 本地: bigint NOT NULL default=nextval('user_account_status_id_seq'::regclass) + 服务器: bigint NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 user_daily_task_progress +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + ix_daily_progress_user_star_key CREATE UNIQUE INDEX ix_daily_progress_user_star_key ON public.user_daily_task_progress USING btree (user_id, star_id, task_key) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 user_dazi_level +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(2): + + dazi_level integer NOT NULL default=1 + + uk_dazi_user_star CREATE UNIQUE INDEX uk_dazi_user_star ON public.user_dazi_level USING btree (user_id, star_id) NOT NULL default=∅ + ⚠️ 服务器多列(5)— 本地无: + - exp bigint NOT NULL default=0 + - idx_user_dazi_level_star CREATE INDEX idx_user_dazi_level_star ON public.user_dazi_level USING btree (star_id) NOT NULL default=∅ + - idx_user_dazi_level_user CREATE INDEX idx_user_dazi_level_user ON public.user_dazi_level USING btree (user_id) NOT NULL default=∅ + - level integer NOT NULL default=1 + - uk_user_dazi_level_user_star CREATE UNIQUE INDEX uk_user_dazi_level_user_star ON public.user_dazi_level USING btree (user_id, star_id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 user_mint_count +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + uk_user_mint_star CREATE UNIQUE INDEX uk_user_mint_star ON public.user_mint_count USING btree (user_id, star_id) NOT NULL default=∅ + ⚠️ 服务器多列(3)— 本地无: + - idx_user_mint_count_star CREATE INDEX idx_user_mint_count_star ON public.user_mint_count USING btree (star_id) NOT NULL default=∅ + - idx_user_mint_count_user CREATE INDEX idx_user_mint_count_user ON public.user_mint_count USING btree (user_id) NOT NULL default=∅ + - uk_user_mint_count_user_star CREATE UNIQUE INDEX uk_user_mint_count_user_star ON public.user_mint_count USING btree (user_id, star_id) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 user_onboarding_progress +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + ix_onboard_progress_user_key CREATE UNIQUE INDEX ix_onboard_progress_user_key ON public.user_onboarding_progress USING btree (user_id, task_key) NOT NULL default=∅ + +────────────────────────────────────────────────────────────────────────────── +📋 users +────────────────────────────────────────────────────────────────────────────── + ❌ 服务器缺列(1): + + uk_users_mobile_active CREATE UNIQUE INDEX uk_users_mobile_active ON public.users USING btree (mobile) WHERE (deleted_at IS NULL) NOT NULL default=∅ diff --git a/docker/build.sh b/docker/build.sh index c581835..9b087d3 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -117,7 +117,7 @@ done # ==================== 服务列表 ==================== # 所有可用服务及其配置(使用小写 target 名) -ALL_SERVICES_NAME=("gateway" "userservice" "socialservice" "assetservice" "galleryservice" "activityservice" "taskservice" "starbookservice" "aichatservice") +ALL_SERVICES_NAME=("gateway" "userservice" "socialservice" "assetservice" "galleryservice" "activityservice" "taskservice" "starbookservice" "aichatservice" "lasercompositor") # 确定要构建的服务 if [ ${#SERVICES[@]} -eq 0 ]; then @@ -207,6 +207,7 @@ main() { taskservice) docker_target="taskservice" ;; starbookservice) docker_target="starbookservice" ;; aichatservice) docker_target="aichatservice" ;; + lasercompositor) docker_target="lasercompositor" ;; # 兼容旧的大写服务名 userService) docker_target="userservice" ;; socialService) docker_target="socialservice" ;; diff --git a/docker/deploy.sh b/docker/deploy.sh index aa7824e..54930fe 100755 --- a/docker/deploy.sh +++ b/docker/deploy.sh @@ -79,6 +79,7 @@ SERVICES=( "taskservice" "starbookservice" "aichatservice" + "lasercompositor" ) # ==================== 服务器配置 ==================== diff --git a/docker/docker-compose.local.yml b/docker/docker-compose.local.yml index 48e3586..026305b 100644 --- a/docker/docker-compose.local.yml +++ b/docker/docker-compose.local.yml @@ -298,6 +298,41 @@ services: reservations: memory: 256M + # ==================== Laser Compositor Service (镭射卡 6 层合成) ==================== + lasercompositor: + image: topfans/lasercompositor:latest + build: + context: .. + dockerfile: docker/Dockerfile.services + target: lasercompositor + # 跳过 pull(本地无 push 到 registry),直接走 build + pull_policy: never + container_name: topfans-lasercompositor + restart: unless-stopped + environment: + <<: *common-env + COMPOSITOR_PORT: 7002 + OSS_REGION: ${OSS_REGION:-cn-shanghai} + OSS_BUCKET_NAME: ${OSS_BUCKET_NAME:-top-fans-test} + OSS_ACCESS_KEY_ID: ${OSS_ACCESS_KEY_ID:-} + OSS_ACCESS_KEY_SECRET: ${OSS_ACCESS_KEY_SECRET:-} + OSS_ASSET_DIR: ${OSS_ASSET_DIR:-asset/} + extra_hosts: + - "host.docker.internal:host-gateway" + networks: + - topfans-net + expose: + - "7002" + healthcheck: + test: ["CMD-SHELL", "nc -z localhost 7002 || exit 1"] + <<: *healthcheck + deploy: + resources: + limits: + memory: 512M + reservations: + memory: 256M + # ==================== API Gateway ==================== gateway: image: topfans/gateway:latest @@ -321,6 +356,22 @@ services: DIFY_API_BASE: ${DIFY_API_BASE:-http://host.docker.internal:8081/v1} DIFY_API_KEY: ${DIFY_API_KEY:-} DUBBO_AI_CHAT_SERVICE_URL: tri://aichatservice:20008 + # 镭射卡 AI 生成(MiniMax 文生图) + MINIMAX_API_KEY: ${MINIMAX_API_KEY:-} + MINIMAX_API_URL: ${MINIMAX_API_URL:-https://api.minimaxi.com/v1/image_generation} + # 镭射卡 6 层合成微服务(容器内通过 service name 访问) + LASER_COMPOSITOR_URL: http://lasercompositor:7002 + # 抠图(人像扣底) + SEGMENT_PROVIDER: ${SEGMENT_PROVIDER:-imageseg} + # OSS 配置(gateway 用于签名 + 抠图上传) + OSS_REGION: ${OSS_REGION:-cn-shanghai} + OSS_BUCKET_NAME: ${OSS_BUCKET_NAME:-top-fans-test} + OSS_STS_ROLE_ARN: ${OSS_STS_ROLE_ARN:-} + OSS_ACCESS_KEY_ID: ${OSS_ACCESS_KEY_ID:-} + OSS_ACCESS_KEY_SECRET: ${OSS_ACCESS_KEY_SECRET:-} + OSS_AVATAR_DIR: ${OSS_AVATAR_DIR:-avatar/} + OSS_ASSET_DIR: ${OSS_ASSET_DIR:-asset/} + OSS_TOKEN_EXPIRE_TIME: ${OSS_TOKEN_EXPIRE_TIME:-3600} depends_on: userservice: condition: service_healthy @@ -338,6 +389,8 @@ services: condition: service_healthy aichatservice: condition: service_healthy + lasercompositor: + condition: service_healthy networks: - topfans-net ports: diff --git a/docker/docker-compose.prod.yml b/docker/docker-compose.prod.yml index 6393977..5ceaa61 100644 --- a/docker/docker-compose.prod.yml +++ b/docker/docker-compose.prod.yml @@ -135,7 +135,7 @@ services: expose: - "20000" healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21000 || exit 1"] + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21000/health || exit 1"] <<: *healthcheck deploy: resources: @@ -173,7 +173,7 @@ services: expose: - "20003" healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21003 || exit 1"] + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21003/health || exit 1"] <<: *healthcheck deploy: resources: @@ -212,7 +212,7 @@ services: expose: - "20002" healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21002 || exit 1"] + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21002/health || exit 1"] <<: *healthcheck deploy: resources: @@ -252,7 +252,7 @@ services: expose: - "20001" healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21001 || exit 1"] + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21001/health || exit 1"] <<: *healthcheck deploy: resources: @@ -294,7 +294,7 @@ services: expose: - "20004" healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21004 || exit 1"] + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21004/health || exit 1"] <<: *healthcheck deploy: resources: @@ -331,7 +331,7 @@ services: expose: - "20006" healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21006 || exit 1"] + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21006/health || exit 1"] <<: *healthcheck deploy: resources: @@ -369,7 +369,7 @@ services: expose: - "20005" healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21005 || exit 1"] + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21005/health || exit 1"] <<: *healthcheck deploy: resources: @@ -413,7 +413,7 @@ services: expose: - "20008" healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21008 || exit 1"] + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21008/health || exit 1"] <<: *healthcheck deploy: resources: @@ -424,6 +424,39 @@ services: memory: 256M cpus: '0.5' + # ==================== Laser Compositor Service (镭射卡 6 层合成) ==================== + lasercompositor: + image: topfans/lasercompositor:latest + build: + context: .. + dockerfile: docker/Dockerfile.services + target: lasercompositor + # 跳过 pull(本地无 push 到 registry),直接走 build + pull_policy: never + container_name: topfans-lasercompositor + restart: always + env_file: + - .env.prod + environment: + <<: *common-env + COMPOSITOR_PORT: 7002 + # OSS_* 全部走 env_file: .env.prod + networks: + - topfans-net + expose: + - "7002" + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:7002/health || exit 1"] + <<: *healthcheck + deploy: + resources: + limits: + memory: 300M + cpus: '0.5' + reservations: + memory: 128M + cpus: '0.25' + # ==================== API Gateway ==================== gateway: image: topfans/gateway:latest @@ -438,7 +471,6 @@ services: environment: <<: *common-env SERVER_PORT: 8080 - JWT_SECRET: ${JWT_SECRET:-topfans-secret-key-please-change-in-production} DUBBO_USER_SERVICE_URL: tri://userservice:20000 DUBBO_SOCIAL_SERVICE_URL: tri://socialservice:20002 DUBBO_ASSET_SERVICE_URL: tri://assetservice:20003 @@ -446,12 +478,11 @@ services: DUBBO_ACTIVITY_SERVICE_URL: tri://activityservice:20004 DUBBO_TASK_SERVICE_URL: tri://taskservice:20006 DUBBO_STARBOOK_SERVICE_URL: tri://starbookservice:20005 - DIFY_API_BASE: ${DIFY_API_BASE:-http://dify-api:8081/v1} - DIFY_API_KEY: ${DIFY_API_KEY:-} DUBBO_AI_CHAT_SERVICE_URL: tri://aichatservice:20008 + LASER_COMPOSITOR_URL: http://lasercompositor:7002 + # 抠图(人像扣底)、OSS、Dify、JWT、Redis 全部走 env_file: .env.prod REDIS_HOST: topfans-redis REDIS_PORT: 6379 - REDIS_PASSWORD: ${REDIS_PASSWORD:-123456} REDIS_DB: 0 depends_on: userservice: @@ -470,6 +501,8 @@ services: condition: service_started aichatservice: condition: service_started + lasercompositor: + condition: service_started redis: condition: service_healthy networks: @@ -482,10 +515,10 @@ services: deploy: resources: limits: - memory: 200M + memory: 300M cpus: '0.5' reservations: - memory: 64M + memory: 128M cpus: '0.25' volumes: diff --git a/frontend/manifest.json b/frontend/manifest.json index 183b0c6..9b7c026 100644 --- a/frontend/manifest.json +++ b/frontend/manifest.json @@ -3,7 +3,7 @@ "appid" : "__UNI__F199FF4", "description" : "", "versionName" : "1.0.5", - "versionCode" : 108, + "versionCode" : 109, "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { diff --git a/frontend/pages/profile/myWorks.vue b/frontend/pages/profile/myWorks.vue index 08ea8c7..25f4574 100644 --- a/frontend/pages/profile/myWorks.vue +++ b/frontend/pages/profile/myWorks.vue @@ -34,7 +34,7 @@ :skip-built-in-touch="true" :shimmer-mid-opacity="0.16" @simulate="(x, y) => onLenticularSimulate(exhibitionAtSlot[0].id, x, y)" /> + :src="exhibitionAtSlot[0].realCoverUrl || exhibitionAtSlot[0].cover_url || '/static/nft/placeholder.png'" mode="aspectFill"> @@ -79,7 +79,7 @@ :skip-built-in-touch="true" :shimmer-mid-opacity="0.16" @simulate="(x, y) => onLenticularSimulate(exhibitionAtSlot[1].id, x, y)" /> + :src="exhibitionAtSlot[1].realCoverUrl || exhibitionAtSlot[1].cover_url || '/static/nft/placeholder.png'" mode="aspectFill"> @@ -156,7 +156,7 @@ :transforms="getLikedLenticularTransforms(item.id)" :gyro-source="gyroSourceLabel" :skip-built-in-touch="true" :shimmer-mid-opacity="0.16" @simulate="(x, y) => onLikedLenticularSimulate(item.id, x, y)" /> - @@ -214,6 +214,7 @@ import { getExhibitionRevenue, claimExhibitionRevenue } from '@/utils/task-api.j import AssetSelector from '../components/AssetSelector.vue'; import { onShow } from '@dcloudio/uni-app'; import { doubleTapLike } from '@/utils/likeHelper.js'; +import { getAssetCoverRealUrl } from '@/utils/assetImageHelper.js'; import LenticularCard from '@/components/lenticular/LenticularCard.vue'; import { useLenticularCraftTiltPreview } from '@/composables/useLenticularCraftTiltPreview.js'; import { buildLenticularLayers } from '@/utils/castloveMintForm.js'; @@ -658,7 +659,7 @@ async function loadLikedLenticularLayersForAsset(assetId) { const materialsRes = await getAssetMaterialsApi(assetId); if (materialsRes.code === 200 && materialsRes.data) { const materialsList = Array.isArray(materialsRes.data) ? materialsRes.data : materialsRes.data.materials || []; - let subjectUrl = item.cover_url; + let subjectUrl = item.realCoverUrl || item.cover_url; let bgUrl = ''; for (const mat of materialsList) { if (mat.material_type === 'main' && mat.material_url_signed) { @@ -675,12 +676,12 @@ async function loadLikedLenticularLayersForAsset(assetId) { } initLikedTransformsForAsset(assetId); } else { - likedLenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.cover_url); + likedLenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.realCoverUrl || item.cover_url); initLikedTransformsForAsset(assetId); } } catch (e) { console.error('[myWorks] 获取点赞作品素材列表失败:', e); - likedLenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.cover_url); + likedLenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.realCoverUrl || item.cover_url); initLikedTransformsForAsset(assetId); } } @@ -706,7 +707,7 @@ async function loadLenticularLayersForAsset(assetId) { const materialsRes = await getAssetMaterialsApi(assetId); if (materialsRes.code === 200 && materialsRes.data) { const materialsList = Array.isArray(materialsRes.data) ? materialsRes.data : materialsRes.data.materials || []; - let subjectUrl = item.cover_url; + let subjectUrl = item.realCoverUrl || item.cover_url; let bgUrl = ''; for (const mat of materialsList) { if (mat.material_type === 'main' && mat.material_url_signed) { @@ -725,12 +726,12 @@ async function loadLenticularLayersForAsset(assetId) { // 初始化 transforms initTransformsForAsset(assetId); } else { - lenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.cover_url); + lenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.realCoverUrl || item.cover_url); initTransformsForAsset(assetId); } } catch (e) { console.error('[myWorks] 获取素材列表失败:', e); - lenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.cover_url); + lenticularLayersByAsset.value[assetId] = buildLenticularLayers(item.realCoverUrl || item.cover_url); } } @@ -857,21 +858,24 @@ const loadExhibitedAssets = async () => { const res = await getMyExhibitedAssetsApi(1, 20); console.log('[DEBUG] 展出作品API返回:', res); if (res.data && res.data.items) { - exhibitionWorks.value = res.data.items - .map(item => ({ - id: item.asset_id, - exhibition_id: item.exhibition_id, - cover_url: item.cover_url, - like_count: item.like_count, - earnings: item.earnings, - hourly_earnings: item.hourly_earnings, - exhibited_at: item.exhibited_at, - expire_at: item.expire_at, - name: item.name, - slot_index: item.slot_index ?? 0, - is_lenticular: item.is_lenticular ?? false, - })) - .sort((a, b) => (a.slot_index ?? 0) - (b.slot_index ?? 0)); + const mapped = res.data.items.map(item => ({ + id: item.asset_id, + exhibition_id: item.exhibition_id, + cover_url: item.cover_url, + like_count: item.like_count, + earnings: item.earnings, + hourly_earnings: item.hourly_earnings, + exhibited_at: item.exhibited_at, + expire_at: item.expire_at, + name: item.name, + slot_index: item.slot_index ?? 0, + is_lenticular: item.is_lenticular ?? false, + })); + // 把后端返回的相对 cover_url 解析成可访问的 URL(OSS 预签名 / 完整URL / 静态资源) + for (const item of mapped) { + item.realCoverUrl = await getAssetCoverRealUrl(item.cover_url); + } + exhibitionWorks.value = mapped.sort((a, b) => (a.slot_index ?? 0) - (b.slot_index ?? 0)); console.log('[DEBUG] 整理后的 exhibitionWorks:', exhibitionWorks.value); // 为每个光栅卡加载层级数据 @@ -903,7 +907,7 @@ const loadLikedAssets = async () => { res = await getMyLikedAssetsApi(1, 20); } if (res.data && res.data.items) { - likedWorks.value = res.data.items.map((item, index) => ({ + const mappedLiked = res.data.items.map((item, index) => ({ id: item.asset_id, cover_url: item.cover_url, like_count: item.like_count, @@ -916,6 +920,11 @@ const loadLikedAssets = async () => { score: item.like_count, reward: Math.floor(item.earnings || 0), })); + // 把后端返回的相对 cover_url 解析成可访问的 URL + for (const item of mappedLiked) { + item.realCoverUrl = await getAssetCoverRealUrl(item.cover_url); + } + likedWorks.value = mappedLiked; // 为每个光栅卡加载层级数据 for (const item of likedWorks.value) {