feat:新增docker密钥登录部署
This commit is contained in:
parent
e761bde30b
commit
f6e1caad8b
@ -98,10 +98,11 @@ func (r *rankingRepository) getHotRankingByDimension(starID int64, dimension str
|
||||
now := time.Now().UnixMilli()
|
||||
startOfMonth := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Local).UnixMilli()
|
||||
|
||||
// 构建基础查询,JOIN 用户表和粉丝档案表获取昵称和头像(一次查询,避免 N+1 问题)
|
||||
// 构建基础查询,JOIN 用户表和粉丝档案表获取昵称和头像
|
||||
// 使用 GROUP BY 去重,并用聚合函数获取 owner 信息
|
||||
// is_original 是布尔值,需要转为 int 再取 MAX
|
||||
db := r.db.Model(&models.Asset{}).
|
||||
Select("assets.id as asset_id, assets.name as asset_name, assets.cover_url, assets.owner_uid, assets.like_count, assets.is_original, fp.nickname as owner_nickname, fp.avatar_url as owner_avatar").
|
||||
|
||||
Select("assets.id as asset_id, assets.name as asset_name, assets.cover_url, assets.owner_uid, MAX(assets.like_count) as like_count, MAX(assets.is_original::int) as is_original, MAX(fp.nickname) as owner_nickname, MAX(fp.avatar_url) as owner_avatar").
|
||||
Joins("LEFT JOIN fan_profiles fp ON fp.user_id = assets.owner_uid AND fp.star_id = ?", starID).
|
||||
Where("assets.star_id = ? AND assets.is_active = ? AND assets.status = ?", starID, true, models.AssetStatusActive)
|
||||
|
||||
@ -112,20 +113,21 @@ func (r *rankingRepository) getHotRankingByDimension(starID int64, dimension str
|
||||
// 根据维度添加条件
|
||||
switch dimension {
|
||||
case "displaying":
|
||||
// 展示中:关联 Exhibition 表,筛选未过期的,且是当前star的展馆
|
||||
db = db.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
||||
Joins("INNER JOIN fan_profiles ON fan_profiles.id = exhibitions.host_profile_id").
|
||||
// 展示中:关联 Exhibition 表,筛选未过期的、未删除的,且是当前star的展品
|
||||
// occupier_star_id 表示展品所属的明星
|
||||
db = db.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id AND exhibitions.deleted_at IS NULL").
|
||||
Where("exhibitions.expire_at > ?", now).
|
||||
Where("fan_profiles.star_id = ?", starID)
|
||||
Where("exhibitions.occupier_star_id = ?", starID)
|
||||
case "month":
|
||||
// 本月:本月内展览过的(未下架或本月内下架的)
|
||||
// 本月:本月内展览过的藏品(包括已下架的,只要 expire_at 在本月内即可)
|
||||
db = db.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
||||
Where("exhibitions.expire_at >= ?", startOfMonth)
|
||||
Where("exhibitions.expire_at >= ?", startOfMonth).
|
||||
Where("exhibitions.occupier_star_id = ?", starID)
|
||||
case "total":
|
||||
// 全部:直接使用 assets 表的 like_count,无需额外条件
|
||||
}
|
||||
|
||||
// 统计总数
|
||||
// 统计总数(先查询 ID 列表再 Count,避免 DISTINCT 干扰)
|
||||
var total int64
|
||||
countDB := r.db.Model(&models.Asset{}).
|
||||
Select("assets.id").
|
||||
@ -136,22 +138,23 @@ func (r *rankingRepository) getHotRankingByDimension(starID int64, dimension str
|
||||
}
|
||||
switch dimension {
|
||||
case "displaying":
|
||||
countDB = countDB.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
||||
Joins("INNER JOIN fan_profiles AS host_fp ON host_fp.id = exhibitions.host_profile_id").
|
||||
countDB = countDB.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id AND exhibitions.deleted_at IS NULL").
|
||||
Where("exhibitions.expire_at > ?", now).
|
||||
Where("host_fp.star_id = ?", starID)
|
||||
Where("exhibitions.occupier_star_id = ?", starID)
|
||||
case "month":
|
||||
// 本月:本月内展览过的(未下架或本月内下架的)
|
||||
// 本月:本月内展览过的藏品(包括已下架的)
|
||||
countDB = countDB.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
||||
Where("exhibitions.expire_at >= ?", startOfMonth)
|
||||
Where("exhibitions.expire_at >= ?", startOfMonth).
|
||||
Where("exhibitions.occupier_star_id = ?", starID)
|
||||
}
|
||||
if err := countDB.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
// 查询列表
|
||||
// 查询列表(使用 GROUP BY 去重,按点赞数排序)
|
||||
var results []*RankingItem
|
||||
err := db.Order("assets.like_count DESC, assets.id ASC").
|
||||
err := db.Group("assets.id").
|
||||
Order("MAX(assets.like_count) DESC, assets.id ASC").
|
||||
Limit(limit).
|
||||
Offset(offset).
|
||||
Scan(&results).Error
|
||||
@ -187,15 +190,15 @@ func (r *rankingRepository) GetMyBestRanking(userID, starID int64, dimension str
|
||||
// 根据维度添加条件,与 getHotRankingByDimension 保持一致
|
||||
switch dimension {
|
||||
case "displaying":
|
||||
// 展示中:关联 Exhibition 表,筛选未过期的,且是当前star的展馆
|
||||
db = db.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
||||
Joins("INNER JOIN fan_profiles AS host_fp ON host_fp.id = exhibitions.host_profile_id").
|
||||
// 展示中:关联 Exhibition 表,筛选未过期的、未删除的,且是当前star的展品
|
||||
db = db.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id AND exhibitions.deleted_at IS NULL").
|
||||
Where("exhibitions.expire_at > ?", now).
|
||||
Where("host_fp.star_id = ?", starID)
|
||||
Where("exhibitions.occupier_star_id = ?", starID)
|
||||
case "month":
|
||||
// 本月:本月内展览过的(未下架或本月内下架的)
|
||||
// 本月:本月内展览过的藏品(包括已下架的)
|
||||
db = db.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
||||
Where("exhibitions.expire_at >= ?", startOfMonth)
|
||||
Where("exhibitions.expire_at >= ?", startOfMonth).
|
||||
Where("exhibitions.occupier_star_id = ?", starID)
|
||||
}
|
||||
|
||||
// 获取用户在该star下点赞数最高的藏品
|
||||
@ -231,14 +234,14 @@ func (r *rankingRepository) GetMyBestRanking(userID, starID int64, dimension str
|
||||
|
||||
switch dimension {
|
||||
case "displaying":
|
||||
rankingDB = rankingDB.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
||||
Joins("INNER JOIN fan_profiles AS host_fp ON host_fp.id = exhibitions.host_profile_id").
|
||||
rankingDB = rankingDB.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id AND exhibitions.deleted_at IS NULL").
|
||||
Where("exhibitions.expire_at > ?", now).
|
||||
Where("host_fp.star_id = ?", starID)
|
||||
Where("exhibitions.occupier_star_id = ?", starID)
|
||||
case "month":
|
||||
// 本月:本月内展览过的(未下架或本月内下架的)
|
||||
// 本月:本月内展览过的藏品(包括已下架的)
|
||||
rankingDB = rankingDB.Joins("INNER JOIN exhibitions ON exhibitions.asset_id = assets.id").
|
||||
Where("exhibitions.expire_at >= ?", startOfMonth)
|
||||
Where("exhibitions.expire_at >= ?", startOfMonth).
|
||||
Where("exhibitions.occupier_star_id = ?", starID)
|
||||
}
|
||||
|
||||
if err := rankingDB.Count(&rank).Error; err != nil {
|
||||
|
||||
@ -42,7 +42,13 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" \
|
||||
echo "Built galleryservice" && \
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" \
|
||||
-o /tmp/activityservice services/activityService/main.go && \
|
||||
echo "Built activityservice"
|
||||
echo "Built activityservice" && \
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" \
|
||||
-o /tmp/taskservice services/taskService/main.go && \
|
||||
echo "Built taskservice" && \
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" \
|
||||
-o /tmp/starbookservice services/starbookService/main.go && \
|
||||
echo "Built starbookservice"
|
||||
|
||||
# ---- Runtime Stage: Gateway ----
|
||||
FROM --platform=linux/amd64 alpine:3.19 AS gateway
|
||||
@ -135,3 +141,33 @@ HEALTHCHECK --interval=10s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:20005 || exit 1
|
||||
|
||||
ENTRYPOINT ["/app/activityservice"]
|
||||
|
||||
# ---- Runtime Stage: TaskService ----
|
||||
FROM --platform=linux/amd64 alpine:3.19 AS taskservice
|
||||
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /tmp/taskservice /app/taskservice
|
||||
|
||||
EXPOSE 20006
|
||||
|
||||
HEALTHCHECK --interval=10s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:21006 || exit 1
|
||||
|
||||
ENTRYPOINT ["/app/taskservice"]
|
||||
|
||||
# ---- Runtime Stage: StarbookService ----
|
||||
FROM --platform=linux/amd64 alpine:3.19 AS starbookservice
|
||||
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
WORKDIR /app
|
||||
COPY --from=builder /tmp/starbookservice /app/starbookservice
|
||||
|
||||
EXPOSE 20007
|
||||
|
||||
HEALTHCHECK --interval=10s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:21007 || exit 1
|
||||
|
||||
ENTRYPOINT ["/app/starbookservice"]
|
||||
|
||||
@ -77,7 +77,7 @@ while [[ $# -gt 0 ]]; do
|
||||
echo ""
|
||||
echo "服务名 (可选):"
|
||||
echo " gateway, userService, socialService, assetService,"
|
||||
echo " galleryService, activityService"
|
||||
echo " galleryService, activityService, taskService, starbookService"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 # 构建所有服务"
|
||||
@ -96,6 +96,8 @@ while [[ $# -gt 0 ]]; do
|
||||
asset|assetService) SERVICES+=("assetService") ;;
|
||||
gallery|galleryService) SERVICES+=("galleryService") ;;
|
||||
activity|activityService) SERVICES+=("activityService") ;;
|
||||
task|taskService) SERVICES+=("taskService") ;;
|
||||
starbook|starbookService) SERVICES+=("starbookService") ;;
|
||||
all)
|
||||
# all 关键字,构建所有服务
|
||||
SERVICES=()
|
||||
@ -114,7 +116,7 @@ done
|
||||
|
||||
# ==================== 服务列表 ====================
|
||||
# 所有可用服务及其配置(使用小写 target 名)
|
||||
ALL_SERVICES_NAME=("gateway" "userservice" "socialservice" "assetservice" "galleryservice" "activityservice")
|
||||
ALL_SERVICES_NAME=("gateway" "userservice" "socialservice" "assetservice" "galleryservice" "activityservice" "taskservice" "starbookservice")
|
||||
|
||||
# 确定要构建的服务
|
||||
if [ ${#SERVICES[@]} -eq 0 ]; then
|
||||
@ -201,12 +203,16 @@ main() {
|
||||
assetservice) docker_target="assetservice" ;;
|
||||
galleryservice) docker_target="galleryservice" ;;
|
||||
activityservice) docker_target="activityservice" ;;
|
||||
taskservice) docker_target="taskservice" ;;
|
||||
starbookservice) docker_target="starbookservice" ;;
|
||||
# 兼容旧的大写服务名
|
||||
userService) docker_target="userservice" ;;
|
||||
socialService) docker_target="socialservice" ;;
|
||||
assetService) docker_target="assetservice" ;;
|
||||
galleryService) docker_target="galleryservice" ;;
|
||||
activityService) docker_target="activityservice" ;;
|
||||
taskService) docker_target="taskservice" ;;
|
||||
starbookService) docker_target="starbookservice" ;;
|
||||
*) docker_target="$service" ;;
|
||||
esac
|
||||
|
||||
|
||||
@ -76,6 +76,8 @@ SERVICES=(
|
||||
"assetservice"
|
||||
"galleryservice"
|
||||
"activityservice"
|
||||
"taskservice"
|
||||
"starbookservice"
|
||||
)
|
||||
|
||||
# ==================== 服务器配置 ====================
|
||||
@ -83,19 +85,32 @@ SERVICES=(
|
||||
SERVER_HOST="101.132.250.62" # 服务器 IP 或域名
|
||||
SERVER_PORT="22" # SSH 端口
|
||||
SERVER_USER="root" # SSH 用户名
|
||||
SERVER_PASSWORD=">n73qBnCja-,#VF+Wq" # 服务器密码
|
||||
SERVER_PASSWORD="" # 服务器密码(仅在未配置 SSH 密钥时使用)
|
||||
SERVER_PATH="/opt/topfans/docker" # 服务器上 docker 目录路径
|
||||
SSH_KEY_PATH="$HOME/.ssh/id_rsa" # SSH 密钥路径,默认使用 ~/.ssh/id_rsa
|
||||
|
||||
# ==================== SSH 别名 ====================
|
||||
# 使用 sshpass 执行 SSH 命令
|
||||
# 优先使用 SSH 密钥,如果失败则使用密码
|
||||
ssh_cmd() {
|
||||
sshpass -p "$SERVER_PASSWORD" ssh -o StrictHostKeyChecking=no -p "$SERVER_PORT" "$SERVER_USER@$SERVER_HOST" "$@"
|
||||
if [ -n "$SERVER_PASSWORD" ]; then
|
||||
sshpass -p "$SERVER_PASSWORD" ssh -o StrictHostKeyChecking=no -p "$SERVER_PORT" "$SERVER_USER@$SERVER_HOST" "$@"
|
||||
else
|
||||
ssh -o StrictHostKeyChecking=no -p "$SERVER_PORT" "$SERVER_USER@$SERVER_HOST" "$@"
|
||||
fi
|
||||
}
|
||||
ssh_cmd_batch() {
|
||||
sshpass -p "$SERVER_PASSWORD" ssh -o StrictHostKeyChecking=no -p "$SERVER_PORT" "$SERVER_USER@$SERVER_HOST" "$@"
|
||||
if [ -n "$SERVER_PASSWORD" ]; then
|
||||
sshpass -p "$SERVER_PASSWORD" ssh -o StrictHostKeyChecking=no -p "$SERVER_PORT" "$SERVER_USER@$SERVER_HOST" "$@"
|
||||
else
|
||||
ssh -o StrictHostKeyChecking=no -p "$SERVER_PORT" "$SERVER_USER@$SERVER_HOST" "$@"
|
||||
fi
|
||||
}
|
||||
scp_cmd() {
|
||||
sshpass -p "$SERVER_PASSWORD" scp -P "$SERVER_PORT" "$@"
|
||||
if [ -n "$SERVER_PASSWORD" ]; then
|
||||
sshpass -p "$SERVER_PASSWORD" scp -o StrictHostKeyChecking=no -P "$SERVER_PORT" "$@"
|
||||
else
|
||||
scp -o StrictHostKeyChecking=no -P "$SERVER_PORT" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# ==================== 打印函数 ====================
|
||||
@ -148,7 +163,11 @@ ${YELLOW}示例:${NC}
|
||||
|
||||
${YELLOW}前提准备:${NC}
|
||||
1. 修改 SERVER_HOST 为你的服务器 IP
|
||||
2. 配置服务器 SSH 免密登录(建议)
|
||||
2. 配置 SSH 密钥登录(推荐):
|
||||
- 生成密钥:ssh-keygen -t rsa
|
||||
- 上传公钥:ssh-copy-id -i ~/.ssh/id_rsa.pub root@你的服务器IP
|
||||
- 设置 SSH_KEY_PATH="~/.ssh/id_rsa"
|
||||
3. 或使用密码登录:设置 SERVER_PASSWORD
|
||||
|
||||
EOF
|
||||
}
|
||||
@ -341,10 +360,11 @@ ENDSSH
|
||||
|
||||
# 上传配置文件
|
||||
print_step "📤 上传配置文件"
|
||||
print_msg "$YELLOW" "上传 docker-compose.prod.yml 和 .env.prod..."
|
||||
print_msg "$YELLOW" "上传 docker-compose.prod.yml, .env.prod, init-db.sql..."
|
||||
scp_cmd "${SCRIPT_DIR}/docker-compose.prod.yml" "${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}/"
|
||||
scp_cmd "${SCRIPT_DIR}/.env.prod" "${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}/"
|
||||
scp_cmd "${SCRIPT_DIR}/init-db.sql" "${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}/"
|
||||
scp_cmd -r "${SCRIPT_DIR}/sql" "${SERVER_USER}@${SERVER_HOST}:${SERVER_PATH}/"
|
||||
print_msg "$GREEN" "✅ 配置文件上传完成"
|
||||
|
||||
# 从 tar 文件加载镜像
|
||||
|
||||
@ -192,6 +192,70 @@ services:
|
||||
reservations:
|
||||
memory: 256M
|
||||
|
||||
taskservice:
|
||||
image: topfans/taskservice:latest
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.services
|
||||
target: taskservice
|
||||
container_name: topfans-taskservice
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
<<: *common-env
|
||||
PORT: 20006
|
||||
USER_SERVICE_URL: tri://userservice:20000
|
||||
depends_on:
|
||||
userservice:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- topfans-net
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
expose:
|
||||
- "20006"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "nc -z localhost 20006 || exit 1"]
|
||||
<<: *healthcheck
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
reservations:
|
||||
memory: 256M
|
||||
|
||||
starbookservice:
|
||||
image: topfans/starbookservice:latest
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.services
|
||||
target: starbookservice
|
||||
container_name: topfans-starbookservice
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
<<: *common-env
|
||||
PORT: 20007
|
||||
ASSET_SERVICE_URL: tri://assetservice:20003
|
||||
depends_on:
|
||||
userservice:
|
||||
condition: service_healthy
|
||||
assetservice:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- topfans-net
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
expose:
|
||||
- "20007"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "nc -z localhost 20007 || exit 1"]
|
||||
<<: *healthcheck
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
reservations:
|
||||
memory: 256M
|
||||
|
||||
# ==================== API Gateway ====================
|
||||
gateway:
|
||||
image: topfans/gateway:latest
|
||||
@ -210,6 +274,8 @@ services:
|
||||
DUBBO_ASSET_SERVICE_URL: tri://assetservice:20003
|
||||
DUBBO_GALLERY_SERVICE_URL: tri://galleryservice:20004
|
||||
DUBBO_ACTIVITY_SERVICE_URL: tri://activityservice:20005
|
||||
DUBBO_TASK_SERVICE_URL: tri://taskservice:20006
|
||||
DUBBO_STARBOOK_SERVICE_URL: tri://starbookservice:20007
|
||||
depends_on:
|
||||
userservice:
|
||||
condition: service_healthy
|
||||
@ -221,6 +287,10 @@ services:
|
||||
condition: service_healthy
|
||||
activityservice:
|
||||
condition: service_healthy
|
||||
taskservice:
|
||||
condition: service_healthy
|
||||
starbookservice:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- topfans-net
|
||||
ports:
|
||||
|
||||
@ -44,7 +44,6 @@ services:
|
||||
<<: *postgres-env
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql
|
||||
- ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro
|
||||
ports:
|
||||
- "5432:5432"
|
||||
networks:
|
||||
@ -59,6 +58,27 @@ services:
|
||||
reservations:
|
||||
memory: 128M
|
||||
|
||||
# ==================== Flyway Migration ====================
|
||||
flyway:
|
||||
image: flyway/flyway:10
|
||||
container_name: topfans-flyway
|
||||
restart: "no"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- FLYWAY_URL=jdbc:postgresql://postgres:5432/topfans
|
||||
- FLYWAY_USER=postgres
|
||||
- FLYWAY_PASSWORD=${DB_PASSWORD:-postgres123}
|
||||
- FLYWAY_SCHEMAS=public
|
||||
- FLYWAY_PLACEHOLDER_REPLACEMENT=true
|
||||
volumes:
|
||||
- ./sql/migrations:/flyway/sql
|
||||
- flyway_data:/flyway/data
|
||||
command: migrate -baselineOnMigrate=true
|
||||
networks:
|
||||
- topfans-net
|
||||
|
||||
# ==================== Dubbo Services ====================
|
||||
userservice:
|
||||
image: topfans/userservice:latest
|
||||
@ -77,8 +97,8 @@ services:
|
||||
DB_PASSWORD: ${DB_PASSWORD:-postgres123}
|
||||
DB_NAME: topfans
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_started
|
||||
flyway:
|
||||
condition: service_completed_successfully
|
||||
networks:
|
||||
- topfans-net
|
||||
expose:
|
||||
@ -253,6 +273,80 @@ services:
|
||||
memory: 32M
|
||||
cpus: '0.25'
|
||||
|
||||
taskservice:
|
||||
image: topfans/taskservice:latest
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.services
|
||||
target: taskservice
|
||||
container_name: topfans-taskservice
|
||||
restart: always
|
||||
environment:
|
||||
<<: *common-env
|
||||
PORT: 20006
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: ${DB_PASSWORD:-postgres123}
|
||||
DB_NAME: topfans
|
||||
USER_SERVICE_URL: tri://userservice:20000
|
||||
depends_on:
|
||||
userservice:
|
||||
condition: service_started
|
||||
networks:
|
||||
- topfans-net
|
||||
expose:
|
||||
- "20006"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21006 || exit 1"]
|
||||
<<: *healthcheck
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 150M
|
||||
cpus: '0.5'
|
||||
reservations:
|
||||
memory: 64M
|
||||
cpus: '0.25'
|
||||
|
||||
starbookservice:
|
||||
image: topfans/starbookservice:latest
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.services
|
||||
target: starbookservice
|
||||
container_name: topfans-starbookservice
|
||||
restart: always
|
||||
environment:
|
||||
<<: *common-env
|
||||
PORT: 20007
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
DB_USER: postgres
|
||||
DB_PASSWORD: ${DB_PASSWORD:-postgres123}
|
||||
DB_NAME: topfans
|
||||
ASSET_SERVICE_URL: tri://assetservice:20003
|
||||
depends_on:
|
||||
userservice:
|
||||
condition: service_started
|
||||
assetservice:
|
||||
condition: service_started
|
||||
networks:
|
||||
- topfans-net
|
||||
expose:
|
||||
- "20007"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21007 || exit 1"]
|
||||
<<: *healthcheck
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 150M
|
||||
cpus: '0.5'
|
||||
reservations:
|
||||
memory: 64M
|
||||
cpus: '0.25'
|
||||
|
||||
# ==================== API Gateway ====================
|
||||
gateway:
|
||||
image: topfans/gateway:latest
|
||||
@ -273,6 +367,8 @@ services:
|
||||
DUBBO_ASSET_SERVICE_URL: tri://assetservice:20003
|
||||
DUBBO_GALLERY_SERVICE_URL: tri://galleryservice:20001
|
||||
DUBBO_ACTIVITY_SERVICE_URL: tri://activityservice:20004
|
||||
DUBBO_TASK_SERVICE_URL: tri://taskservice:20006
|
||||
DUBBO_STARBOOK_SERVICE_URL: tri://starbookservice:20007
|
||||
depends_on:
|
||||
userservice:
|
||||
condition: service_started
|
||||
@ -284,6 +380,10 @@ services:
|
||||
condition: service_started
|
||||
activityservice:
|
||||
condition: service_started
|
||||
taskservice:
|
||||
condition: service_started
|
||||
starbookservice:
|
||||
condition: service_started
|
||||
networks:
|
||||
- topfans-net
|
||||
ports:
|
||||
@ -300,10 +400,11 @@ services:
|
||||
memory: 64M
|
||||
cpus: '0.25'
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
flyway_data:
|
||||
|
||||
networks:
|
||||
topfans-net:
|
||||
driver: bridge
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
||||
@ -253,6 +253,54 @@ CREATE SEQUENCE public.asset_likes_id_seq
|
||||
ALTER SEQUENCE public.asset_likes_id_seq OWNED BY public.asset_likes.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: asset_registry; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE TABLE public.asset_registry (
|
||||
id bigint NOT NULL,
|
||||
asset_id bigint NOT NULL,
|
||||
asset_type character varying(20) NOT NULL,
|
||||
owner_uid bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
grade integer,
|
||||
collection_category character varying(50),
|
||||
activity_id bigint,
|
||||
activity_type character varying(50),
|
||||
status integer DEFAULT 0 NOT NULL,
|
||||
like_count integer DEFAULT 0 NOT NULL,
|
||||
display_status integer DEFAULT 0 NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
|
||||
--
|
||||
-- Name: TABLE asset_registry; Type: COMMENT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
COMMENT ON TABLE public.asset_registry IS '资产注册表(星册体系)';
|
||||
|
||||
|
||||
--
|
||||
-- Name: asset_registry_id_seq; Type: SEQUENCE; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.asset_registry_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
--
|
||||
-- Name: asset_registry_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.asset_registry_id_seq OWNED BY public.asset_registry.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: assets; Type: TABLE; Schema: public; Owner: -
|
||||
--
|
||||
@ -408,7 +456,8 @@ CREATE TABLE public.exhibitions (
|
||||
start_time bigint NOT NULL,
|
||||
expire_at bigint NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
updated_at bigint NOT NULL,
|
||||
deleted_at bigint
|
||||
);
|
||||
|
||||
|
||||
@ -845,6 +894,13 @@ ALTER TABLE ONLY public.activity_user_stats ALTER COLUMN id SET DEFAULT nextval(
|
||||
ALTER TABLE ONLY public.asset_likes ALTER COLUMN id SET DEFAULT nextval('public.asset_likes_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: asset_registry id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.asset_registry ALTER COLUMN id SET DEFAULT nextval('public.asset_registry_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: assets id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
@ -852,6 +908,13 @@ ALTER TABLE ONLY public.asset_likes ALTER COLUMN id SET DEFAULT nextval('public.
|
||||
ALTER TABLE ONLY public.assets ALTER COLUMN id SET DEFAULT nextval('public.assets_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: asset_registry id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.asset_registry ALTER COLUMN id SET DEFAULT nextval('public.asset_registry_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Name: booth_slots slot_id; Type: DEFAULT; Schema: public; Owner: -
|
||||
--
|
||||
@ -969,6 +1032,14 @@ ALTER TABLE ONLY public.asset_likes
|
||||
ADD CONSTRAINT asset_likes_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: asset_registry asset_registry_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.asset_registry
|
||||
ADD CONSTRAINT asset_registry_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: assets assets_pkey; Type: CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
@ -1180,6 +1251,13 @@ CREATE INDEX idx_asset_likes_asset ON public.asset_likes USING btree (asset_id);
|
||||
CREATE INDEX idx_asset_likes_user_star ON public.asset_likes USING btree (user_id, star_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: idx_registry_owner_star; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
CREATE INDEX idx_registry_owner_star ON public.asset_registry USING btree (owner_uid, star_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: idx_assets_created_at; Type: INDEX; Schema: public; Owner: -
|
||||
--
|
||||
@ -1549,6 +1627,30 @@ ALTER TABLE ONLY public.asset_likes
|
||||
ADD CONSTRAINT fk_asset_likes_user FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: asset_registry fk_registry_asset; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.asset_registry
|
||||
ADD CONSTRAINT fk_registry_asset FOREIGN KEY (asset_id) REFERENCES public.assets(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: asset_registry fk_registry_owner; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.asset_registry
|
||||
ADD CONSTRAINT fk_registry_owner FOREIGN KEY (owner_uid) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: asset_registry fk_registry_star; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.asset_registry
|
||||
ADD CONSTRAINT fk_registry_star FOREIGN KEY (star_id) REFERENCES public.stars(star_id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
--
|
||||
-- Name: assets fk_assets_owner; Type: FK CONSTRAINT; Schema: public; Owner: -
|
||||
--
|
||||
|
||||
491
docker/sql/migrations/V1__init_schema.sql
Normal file
491
docker/sql/migrations/V1__init_schema.sql
Normal file
@ -0,0 +1,491 @@
|
||||
-- V1__init_schema.sql
|
||||
-- 初始表结构
|
||||
|
||||
-- postgres 扩展
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- =====================================================
|
||||
-- 用户相关表
|
||||
-- =====================================================
|
||||
|
||||
-- 用户表
|
||||
CREATE TABLE public.users (
|
||||
id bigint NOT NULL,
|
||||
mobile character varying(11) NOT NULL,
|
||||
password_hash character varying(255) NOT NULL,
|
||||
access_token text,
|
||||
token_expires_at bigint,
|
||||
avatar_url character varying(500),
|
||||
global_wallet_address character varying(100),
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL,
|
||||
deleted_at bigint
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.users_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public.users_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.users ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
||||
CREATE UNIQUE INDEX uk_users_mobile ON public.users USING btree (mobile);
|
||||
CREATE INDEX idx_users_deleted_at ON public.users USING btree (deleted_at);
|
||||
ALTER TABLE ONLY public.users ADD CONSTRAINT fk_users FOREIGN KEY (owner_uid) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
-- 明星信息表
|
||||
CREATE TABLE public.stars (
|
||||
star_id bigint NOT NULL,
|
||||
name character varying(100) NOT NULL,
|
||||
tag character varying(100),
|
||||
name_en character varying(100),
|
||||
pic_url character varying(500),
|
||||
description text,
|
||||
identity_id character varying(50) NOT NULL,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.stars_star_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.stars ALTER COLUMN star_id SET DEFAULT nextval('public.stars_star_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.stars ADD CONSTRAINT stars_pkey PRIMARY KEY (star_id);
|
||||
CREATE UNIQUE INDEX uk_stars_identity_id ON public.stars USING btree (identity_id);
|
||||
|
||||
-- 粉丝档案表
|
||||
CREATE TABLE public.fan_profiles (
|
||||
id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
nickname character varying(50) NOT NULL,
|
||||
level integer DEFAULT 1 NOT NULL,
|
||||
times integer DEFAULT 1 NOT NULL,
|
||||
social integer DEFAULT 0 NOT NULL,
|
||||
experience bigint DEFAULT 0 NOT NULL,
|
||||
coin_balance bigint DEFAULT 0 NOT NULL,
|
||||
crystal_balance bigint DEFAULT 0 NOT NULL,
|
||||
tags jsonb,
|
||||
starbook_limit integer DEFAULT 3 NOT NULL,
|
||||
slot_limit integer DEFAULT 3 NOT NULL,
|
||||
assets_count integer DEFAULT 0 NOT NULL,
|
||||
chain_address character varying(100),
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL,
|
||||
avatar_url character varying(500)
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.fan_profiles_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.fan_profiles ALTER COLUMN id SET DEFAULT nextval('public.fan_profiles_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.fan_profiles ADD CONSTRAINT fan_profiles_pkey PRIMARY KEY (id);
|
||||
CREATE UNIQUE INDEX uk_fan_profiles_user_star ON public.fan_profiles USING btree (user_id, star_id);
|
||||
CREATE UNIQUE INDEX uk_fan_profiles_star_nickname ON public.fan_profiles USING btree (star_id, nickname);
|
||||
CREATE INDEX idx_fan_profiles_user_id ON public.fan_profiles USING btree (user_id);
|
||||
CREATE INDEX idx_fan_profiles_star_id ON public.fan_profiles USING btree (star_id);
|
||||
ALTER TABLE ONLY public.fan_profiles ADD CONSTRAINT fk_fan_profiles_user FOREIGN KEY (user_id) REFERENCES public.users(id);
|
||||
ALTER TABLE ONLY public.fan_profiles ADD CONSTRAINT fk_fan_profiles_star FOREIGN KEY (star_id) REFERENCES public.stars(star_id);
|
||||
|
||||
-- =====================================================
|
||||
-- 藏品相关表
|
||||
-- =====================================================
|
||||
|
||||
-- 资产表(藏品)
|
||||
CREATE TABLE public.assets (
|
||||
id bigint NOT NULL,
|
||||
owner_uid bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
name character varying(100) NOT NULL,
|
||||
cover_url character varying(500) NOT NULL,
|
||||
material_url character varying(500),
|
||||
description text,
|
||||
rarity integer,
|
||||
tags jsonb,
|
||||
visibility character varying(20) DEFAULT 'private'::character varying,
|
||||
status integer DEFAULT 0 NOT NULL,
|
||||
tx_hash character varying(100),
|
||||
block_number bigint,
|
||||
like_count integer DEFAULT 0 NOT NULL,
|
||||
is_original boolean DEFAULT false NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL,
|
||||
minted_at bigint,
|
||||
deleted_at bigint,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
info text
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.assets_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.assets ALTER COLUMN id SET DEFAULT nextval('public.assets_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.assets ADD CONSTRAINT assets_pkey PRIMARY KEY (id);
|
||||
CREATE INDEX idx_assets_created_at ON public.assets USING btree (created_at DESC);
|
||||
CREATE INDEX idx_assets_deleted_at ON public.assets USING btree (deleted_at);
|
||||
CREATE INDEX idx_assets_owner_star ON public.assets USING btree (owner_uid, star_id);
|
||||
CREATE INDEX idx_assets_star_active ON public.assets USING btree (star_id, is_active);
|
||||
CREATE INDEX idx_assets_status ON public.assets USING btree (status);
|
||||
CREATE INDEX idx_assets_tx_hash ON public.assets USING btree (tx_hash);
|
||||
ALTER TABLE ONLY public.assets ADD CONSTRAINT fk_assets_owner FOREIGN KEY (owner_uid) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.assets ADD CONSTRAINT fk_assets_star FOREIGN KEY (star_id) REFERENCES public.stars(star_id) ON DELETE CASCADE;
|
||||
|
||||
-- 资产注册表(星册体系)
|
||||
CREATE TABLE public.asset_registry (
|
||||
id bigint NOT NULL,
|
||||
asset_id bigint NOT NULL,
|
||||
asset_type character varying(20) NOT NULL,
|
||||
owner_uid bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
grade integer,
|
||||
collection_category character varying(50),
|
||||
activity_id bigint,
|
||||
activity_type character varying(50),
|
||||
status integer DEFAULT 0 NOT NULL,
|
||||
like_count integer DEFAULT 0 NOT NULL,
|
||||
display_status integer DEFAULT 0 NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.asset_registry_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.asset_registry ALTER COLUMN id SET DEFAULT nextval('public.asset_registry_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.asset_registry ADD CONSTRAINT asset_registry_pkey PRIMARY KEY (id);
|
||||
CREATE INDEX idx_registry_owner_star ON public.asset_registry USING btree (owner_uid, star_id);
|
||||
ALTER TABLE ONLY public.asset_registry ADD CONSTRAINT fk_registry_asset FOREIGN KEY (asset_id) REFERENCES public.assets(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.asset_registry ADD CONSTRAINT fk_registry_owner FOREIGN KEY (owner_uid) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.asset_registry ADD CONSTRAINT fk_registry_star FOREIGN KEY (star_id) REFERENCES public.stars(star_id) ON DELETE CASCADE;
|
||||
|
||||
-- 点赞记录表
|
||||
CREATE TABLE public.asset_likes (
|
||||
id bigint NOT NULL,
|
||||
asset_id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
created_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.asset_likes_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.asset_likes ALTER COLUMN id SET DEFAULT nextval('public.asset_likes_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.asset_likes ADD CONSTRAINT asset_likes_pkey PRIMARY KEY (id);
|
||||
CREATE UNIQUE INDEX uk_asset_likes_user_asset ON public.asset_likes USING btree (user_id, asset_id);
|
||||
CREATE INDEX idx_asset_likes_asset ON public.asset_likes USING btree (asset_id);
|
||||
CREATE INDEX idx_asset_likes_user_star ON public.asset_likes USING btree (user_id, star_id);
|
||||
ALTER TABLE ONLY public.asset_likes ADD CONSTRAINT fk_asset_likes_asset FOREIGN KEY (asset_id) REFERENCES public.assets(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.asset_likes ADD CONSTRAINT fk_asset_likes_star FOREIGN KEY (star_id) REFERENCES public.stars(star_id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.asset_likes ADD CONSTRAINT fk_asset_likes_user FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
-- 铸造订单表
|
||||
CREATE TABLE public.mint_orders (
|
||||
order_id character varying(100) NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
asset_id bigint,
|
||||
star_id bigint NOT NULL,
|
||||
status character varying(20) DEFAULT 'PENDING'::character varying NOT NULL,
|
||||
cost_crystal bigint DEFAULT 0,
|
||||
error_message text,
|
||||
retry_count integer DEFAULT 0,
|
||||
material_url character varying(500),
|
||||
name character varying(100),
|
||||
description text,
|
||||
material_type character varying(50),
|
||||
event character varying(100),
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL,
|
||||
minted_at bigint,
|
||||
info text
|
||||
);
|
||||
|
||||
ALTER TABLE ONLY public.mint_orders ADD CONSTRAINT mint_orders_pkey PRIMARY KEY (order_id);
|
||||
CREATE INDEX idx_mint_orders_asset ON public.mint_orders USING btree (asset_id);
|
||||
CREATE INDEX idx_mint_orders_created_at ON public.mint_orders USING btree (created_at DESC);
|
||||
CREATE INDEX idx_mint_orders_status ON public.mint_orders USING btree (status);
|
||||
CREATE INDEX idx_mint_orders_user_star ON public.mint_orders USING btree (user_id, star_id);
|
||||
ALTER TABLE ONLY public.mint_orders ADD CONSTRAINT fk_mint_orders_asset FOREIGN KEY (asset_id) REFERENCES public.assets(id) ON DELETE SET NULL;
|
||||
ALTER TABLE ONLY public.mint_orders ADD CONSTRAINT fk_mint_orders_star FOREIGN KEY (star_id) REFERENCES public.stars(star_id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.mint_orders ADD CONSTRAINT fk_mint_orders_user FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
-- =====================================================
|
||||
-- 展馆相关表
|
||||
-- =====================================================
|
||||
|
||||
-- 展位表
|
||||
CREATE TABLE public.booth_slots (
|
||||
slot_id bigint NOT NULL,
|
||||
host_profile_id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
slot_index bigint NOT NULL,
|
||||
visibility character varying(20) DEFAULT 'public'::character varying,
|
||||
is_enabled boolean DEFAULT false,
|
||||
unlock_type character varying(20),
|
||||
unlock_value bigint,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.booth_slots_slot_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.booth_slots ALTER COLUMN slot_id SET DEFAULT nextval('public.booth_slots_slot_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.booth_slots ADD CONSTRAINT booth_slots_pkey PRIMARY KEY (slot_id);
|
||||
CREATE UNIQUE INDEX idx_host_slot ON public.booth_slots USING btree (host_profile_id, slot_index);
|
||||
CREATE INDEX idx_star_enabled ON public.booth_slots USING btree (star_id);
|
||||
CREATE INDEX idx_user_star ON public.booth_slots USING btree (user_id, star_id);
|
||||
ALTER TABLE ONLY public.booth_slots ADD CONSTRAINT fk_booth_slots_profile FOREIGN KEY (host_profile_id) REFERENCES public.fan_profiles(id) ON DELETE CASCADE;
|
||||
|
||||
-- 展品展示表
|
||||
CREATE TABLE public.exhibitions (
|
||||
id bigint NOT NULL,
|
||||
asset_id bigint NOT NULL,
|
||||
slot_id bigint NOT NULL,
|
||||
host_profile_id bigint NOT NULL,
|
||||
occupier_uid bigint NOT NULL,
|
||||
occupier_star_id bigint NOT NULL,
|
||||
start_time bigint NOT NULL,
|
||||
expire_at bigint NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL,
|
||||
deleted_at bigint
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.exhibitions_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.exhibitions ALTER COLUMN id SET DEFAULT nextval('public.exhibitions_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.exhibitions ADD CONSTRAINT exhibitions_pkey PRIMARY KEY (id);
|
||||
CREATE UNIQUE INDEX uk_asset ON public.exhibitions USING btree (asset_id);
|
||||
CREATE INDEX idx_expire ON public.exhibitions USING btree (expire_at);
|
||||
CREATE INDEX idx_host ON public.exhibitions USING btree (host_profile_id);
|
||||
CREATE INDEX idx_occupier ON public.exhibitions USING btree (occupier_uid, occupier_star_id);
|
||||
CREATE INDEX idx_slot ON public.exhibitions USING btree (slot_id);
|
||||
ALTER TABLE ONLY public.exhibitions ADD CONSTRAINT fk_exhibitions_asset FOREIGN KEY (asset_id) REFERENCES public.assets(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.exhibitions ADD CONSTRAINT fk_exhibitions_slot FOREIGN KEY (slot_id) REFERENCES public.booth_slots(slot_id) ON DELETE CASCADE;
|
||||
|
||||
-- 展览收入记录表
|
||||
CREATE TABLE public.exhibition_revenue_records (
|
||||
id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
exhibition_id bigint NOT NULL,
|
||||
asset_id bigint NOT NULL,
|
||||
slot_id bigint NOT NULL,
|
||||
slot_owner_uid bigint NOT NULL,
|
||||
slot_type character varying(20) NOT NULL,
|
||||
crystal_amount bigint NOT NULL,
|
||||
cycle_start_time bigint NOT NULL,
|
||||
cycle_end_time bigint NOT NULL,
|
||||
status character varying(20) DEFAULT 'claimable'::character varying NOT NULL,
|
||||
claimed_at bigint,
|
||||
created_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.exhibition_revenue_records_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.exhibition_revenue_records ALTER COLUMN id SET DEFAULT nextval('public.exhibition_revenue_records_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.exhibition_revenue_records ADD CONSTRAINT exhibition_revenue_records_pkey PRIMARY KEY (id);
|
||||
CREATE INDEX idx_user_revenue ON public.exhibition_revenue_records USING btree (user_id, star_id);
|
||||
|
||||
-- =====================================================
|
||||
-- 社交相关表
|
||||
-- =====================================================
|
||||
|
||||
-- 好友请求表
|
||||
CREATE TABLE public.friend_requests (
|
||||
id bigint NOT NULL,
|
||||
from_user_id bigint NOT NULL,
|
||||
to_user_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
message character varying(200),
|
||||
status character varying(20) DEFAULT 'pending'::character varying NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL,
|
||||
expires_at bigint,
|
||||
processed_at bigint
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.friend_requests_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.friend_requests ALTER COLUMN id SET DEFAULT nextval('public.friend_requests_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.friend_requests ADD CONSTRAINT friend_requests_pkey PRIMARY KEY (id);
|
||||
CREATE INDEX idx_friend_requests_expires ON public.friend_requests USING btree (expires_at);
|
||||
CREATE INDEX idx_friend_requests_from_status ON public.friend_requests USING btree (from_user_id, status, created_at DESC);
|
||||
CREATE INDEX idx_friend_requests_star ON public.friend_requests USING btree (star_id);
|
||||
CREATE INDEX idx_friend_requests_to_status ON public.friend_requests USING btree (to_user_id, status, created_at DESC);
|
||||
CREATE INDEX idx_friend_requests_users_star ON public.friend_requests USING btree (from_user_id, to_user_id, star_id, created_at DESC);
|
||||
ALTER TABLE ONLY public.friend_requests ADD CONSTRAINT fk_friend_requests_from_user FOREIGN KEY (from_user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.friend_requests ADD CONSTRAINT fk_friend_requests_to_user FOREIGN KEY (to_user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
-- 好友关系表
|
||||
CREATE TABLE public.friendships (
|
||||
id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
friend_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
status character varying(20) DEFAULT 'accepted'::character varying NOT NULL,
|
||||
remark character varying(50),
|
||||
intimacy integer DEFAULT 0 NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.friendships_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.friendships ALTER COLUMN id SET DEFAULT nextval('public.friendships_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.friendships ADD CONSTRAINT friendships_pkey PRIMARY KEY (id);
|
||||
CREATE UNIQUE INDEX uk_friendships_user_friend_star ON public.friendships USING btree (user_id, friend_id, star_id);
|
||||
CREATE INDEX idx_friendships_friend_star ON public.friendships USING btree (friend_id);
|
||||
CREATE INDEX idx_friendships_list_query ON public.friendships USING btree (user_id, friend_id, star_id, status, remark, created_at);
|
||||
CREATE INDEX idx_friendships_user_star_created ON public.friendships USING btree (user_id, star_id, created_at DESC);
|
||||
CREATE INDEX idx_friendships_user_star_status ON public.friendships USING btree (user_id, star_id, status);
|
||||
ALTER TABLE ONLY public.friendships ADD CONSTRAINT fk_friendships_friend FOREIGN KEY (friend_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.friendships ADD CONSTRAINT fk_friendships_user FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
-- =====================================================
|
||||
-- 任务相关表
|
||||
-- =====================================================
|
||||
|
||||
-- 任务定义表
|
||||
CREATE TABLE public.task_definitions (
|
||||
id bigint NOT NULL,
|
||||
task_key character varying(50) NOT NULL,
|
||||
task_type character varying(20) NOT NULL,
|
||||
name character varying(100) NOT NULL,
|
||||
description text,
|
||||
crystal_reward bigint DEFAULT 0 NOT NULL,
|
||||
exp_reward bigint DEFAULT 0 NOT NULL,
|
||||
sort_order integer DEFAULT 0 NOT NULL,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.task_definitions_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.task_definitions ALTER COLUMN id SET DEFAULT nextval('public.task_definitions_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.task_definitions ADD CONSTRAINT task_definitions_pkey PRIMARY KEY (id);
|
||||
CREATE UNIQUE INDEX idx_task_definitions_task_key ON public.task_definitions USING btree (task_key);
|
||||
|
||||
-- 用户任务进度表
|
||||
CREATE TABLE public.user_task_progress (
|
||||
id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
task_key character varying(50) NOT NULL,
|
||||
status character varying(20) DEFAULT 'pending'::character varying NOT NULL,
|
||||
completed_at bigint,
|
||||
claimed_at bigint,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.user_task_progress_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.user_task_progress ALTER COLUMN id SET DEFAULT nextval('public.user_task_progress_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.user_task_progress ADD CONSTRAINT user_task_progress_pkey PRIMARY KEY (id);
|
||||
CREATE UNIQUE INDEX uk_user_task ON public.user_task_progress USING btree (user_id, star_id, task_key);
|
||||
|
||||
-- 用户引导状态表
|
||||
CREATE TABLE public.user_onboarding_status (
|
||||
id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
is_onboarding_completed boolean DEFAULT false NOT NULL,
|
||||
is_onboarding_claimed boolean DEFAULT false NOT NULL,
|
||||
has_friend_display_bonus boolean DEFAULT false NOT NULL,
|
||||
onboarding_completed_at bigint,
|
||||
onboarding_claimed_at bigint,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.user_onboarding_status_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.user_onboarding_status ALTER COLUMN id SET DEFAULT nextval('public.user_onboarding_status_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.user_onboarding_status ADD CONSTRAINT user_onboarding_status_pkey PRIMARY KEY (id);
|
||||
CREATE UNIQUE INDEX uk_user_star_onboarding ON public.user_onboarding_status USING btree (user_id, star_id);
|
||||
|
||||
-- =====================================================
|
||||
-- 活动相关表
|
||||
-- =====================================================
|
||||
|
||||
-- 活动表
|
||||
CREATE TABLE public.activities (
|
||||
id bigint NOT NULL,
|
||||
activity_type character varying(50) NOT NULL,
|
||||
title character varying(100) NOT NULL,
|
||||
description text,
|
||||
star_id bigint NOT NULL,
|
||||
start_time bigint NOT NULL,
|
||||
end_time bigint NOT NULL,
|
||||
target_progress bigint DEFAULT 1000 NOT NULL,
|
||||
current_progress bigint DEFAULT 0 NOT NULL,
|
||||
status character varying(20) DEFAULT 'pending'::character varying,
|
||||
stage_configs jsonb,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL,
|
||||
theme character varying(100),
|
||||
overall_end_time bigint DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.activities_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.activities ALTER COLUMN id SET DEFAULT nextval('public.activities_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.activities ADD CONSTRAINT activities_pkey PRIMARY KEY (id);
|
||||
CREATE INDEX idx_activities_star_id ON public.activities USING btree (star_id);
|
||||
CREATE INDEX idx_activities_start_end ON public.activities USING btree (start_time, end_time);
|
||||
CREATE INDEX idx_activities_status ON public.activities USING btree (status);
|
||||
|
||||
-- 活动道具表
|
||||
CREATE TABLE public.activity_items (
|
||||
id bigint NOT NULL,
|
||||
activity_id bigint NOT NULL,
|
||||
item_type character varying(50) NOT NULL,
|
||||
item_name character varying(50) NOT NULL,
|
||||
icon_url character varying(500),
|
||||
crystal_cost bigint NOT NULL,
|
||||
contribution_points bigint NOT NULL,
|
||||
sort_order integer DEFAULT 0 NOT NULL,
|
||||
is_active boolean DEFAULT true NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.activity_items_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.activity_items ALTER COLUMN id SET DEFAULT nextval('public.activity_items_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.activity_items ADD CONSTRAINT activity_items_pkey PRIMARY KEY (id);
|
||||
CREATE INDEX idx_activity_items_activity ON public.activity_items USING btree (activity_id);
|
||||
CREATE INDEX idx_activity_items_type ON public.activity_items USING btree (item_type);
|
||||
ALTER TABLE ONLY public.activity_items ADD CONSTRAINT fk_activity_items_activity FOREIGN KEY (activity_id) REFERENCES public.activities(id) ON DELETE CASCADE;
|
||||
|
||||
-- 活动贡献记录表
|
||||
CREATE TABLE public.activity_contributions (
|
||||
id bigint NOT NULL,
|
||||
activity_id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
item_id bigint NOT NULL,
|
||||
item_type character varying(50) NOT NULL,
|
||||
quantity integer DEFAULT 1 NOT NULL,
|
||||
crystal_spent bigint NOT NULL,
|
||||
contribution_points bigint NOT NULL,
|
||||
created_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.activity_contributions_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.activity_contributions ALTER COLUMN id SET DEFAULT nextval('public.activity_contributions_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.activity_contributions ADD CONSTRAINT activity_contributions_pkey PRIMARY KEY (id);
|
||||
CREATE INDEX idx_activity_contributions_activity ON public.activity_contributions USING btree (activity_id);
|
||||
CREATE INDEX idx_activity_contributions_created ON public.activity_contributions USING btree (created_at DESC);
|
||||
CREATE INDEX idx_activity_contributions_user_star ON public.activity_contributions USING btree (user_id, star_id);
|
||||
ALTER TABLE ONLY public.activity_contributions ADD CONSTRAINT fk_activity_contributions_activity FOREIGN KEY (activity_id) REFERENCES public.activities(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.activity_contributions ADD CONSTRAINT fk_activity_contributions_item FOREIGN KEY (item_id) REFERENCES public.activity_items(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.activity_contributions ADD CONSTRAINT fk_activity_contributions_star FOREIGN KEY (star_id) REFERENCES public.stars(star_id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.activity_contributions ADD CONSTRAINT fk_activity_contributions_user FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
|
||||
-- 用户活动统计表
|
||||
CREATE TABLE public.activity_user_stats (
|
||||
id bigint NOT NULL,
|
||||
activity_id bigint NOT NULL,
|
||||
user_id bigint NOT NULL,
|
||||
star_id bigint NOT NULL,
|
||||
total_contribution bigint DEFAULT 0 NOT NULL,
|
||||
total_crystal_spent bigint DEFAULT 0 NOT NULL,
|
||||
total_items integer DEFAULT 0 NOT NULL,
|
||||
last_contribute_at bigint NOT NULL,
|
||||
created_at bigint NOT NULL,
|
||||
updated_at bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE SEQUENCE public.activity_user_stats_id_seq START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE CACHE 1;
|
||||
ALTER TABLE ONLY public.activity_user_stats ALTER COLUMN id SET DEFAULT nextval('public.activity_user_stats_id_seq'::regclass);
|
||||
ALTER TABLE ONLY public.activity_user_stats ADD CONSTRAINT activity_user_stats_pkey PRIMARY KEY (id);
|
||||
CREATE UNIQUE INDEX uk_activity_user_star ON public.activity_user_stats UNIQUE (activity_id, user_id, star_id);
|
||||
CREATE INDEX idx_activity_user_stats_activity ON public.activity_user_stats USING btree (activity_id);
|
||||
CREATE INDEX idx_activity_user_stats_contribution ON public.activity_user_stats USING btree (activity_id, total_contribution DESC);
|
||||
CREATE INDEX idx_activity_user_stats_user_star ON public.activity_user_stats USING btree (user_id, star_id);
|
||||
ALTER TABLE ONLY public.activity_user_stats ADD CONSTRAINT fk_activity_user_stats_activity FOREIGN KEY (activity_id) REFERENCES public.activities(id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.activity_user_stats ADD CONSTRAINT fk_activity_user_stats_star FOREIGN KEY (star_id) REFERENCES public.stars(star_id) ON DELETE CASCADE;
|
||||
ALTER TABLE ONLY public.activity_user_stats ADD CONSTRAINT fk_activity_user_stats_user FOREIGN KEY (user_id) REFERENCES public.users(id) ON DELETE CASCADE;
|
||||
@ -0,0 +1,4 @@
|
||||
-- V2__add_deleted_at_to_exhibitions.sql
|
||||
-- 为 exhibitions 表添加 deleted_at 字段(软删除)
|
||||
|
||||
ALTER TABLE public.exhibitions ADD COLUMN IF NOT EXISTS deleted_at bigint;
|
||||
17
docker/sql/migrations/V3__seed_data.sql
Normal file
17
docker/sql/migrations/V3__seed_data.sql
Normal file
@ -0,0 +1,17 @@
|
||||
-- V3__seed_data.sql
|
||||
-- 种子数据(测试用)
|
||||
|
||||
-- 插入测试明星
|
||||
INSERT INTO public.stars (star_id, name, tag, identity_id, is_active, created_at, updated_at) VALUES
|
||||
(87, '测试明星', '测试', 'test_star_001', true, 1704067200000, 1704067200000)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 插入测试用户
|
||||
INSERT INTO public.users (id, mobile, password_hash, is_active, created_at, updated_at) VALUES
|
||||
(1, '13800138000', '$2a$10$N9qo8uLOickgx2ZMRZoMye6e7q5R5pR6Vx9qPCr5dLq5R5pR6Vx9q', true, 1704067200000, 1704067200000)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- 插入测试粉丝档案
|
||||
INSERT INTO public.fan_profiles (id, user_id, star_id, nickname, level, times, social, experience, coin_balance, crystal_balance, starbook_limit, slot_limit, assets_count, is_active, created_at, updated_at, avatar_url) VALUES
|
||||
(1, 1, 87, '测试用户', 5, 3, 10, 1000, 500, 100, 3, 3, 5, true, 1704067200000, 1704067200000, 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/avatar/1/87/avatar.png')
|
||||
ON CONFLICT DO NOTHING;
|
||||
53
docker/sql/migrations/V4__seed_data.sql
Normal file
53
docker/sql/migrations/V4__seed_data.sql
Normal file
@ -0,0 +1,53 @@
|
||||
-- V4__seed_data.sql
|
||||
-- 种子数据
|
||||
|
||||
-- 插入明星数据
|
||||
INSERT INTO public.stars (star_id, name, tag, name_en, pic_url, description, identity_id, is_active, created_at, updated_at) VALUES
|
||||
(87, '肖战', '小飞侠', 'Xiao Zhan', NULL, NULL, 'xz', true, 1773322573872, 1773322573872),
|
||||
(88, '王一博', '小摩托', 'Wang Yibo', NULL, NULL, 'wyb', true, 1773322573872, 1773322573872),
|
||||
(89, '杨洋', NULL, 'Yang Yang', NULL, NULL, 'yy', true, 1773322573872, 1773322573872),
|
||||
(93, 'Lisa', 'BLACKPINK', 'Lalisa Manoban', 'https://example.com/lisa.jpg', 'BLACKPINK成员', 'lisa_test_001', true, 1773407317000, 1773407317000),
|
||||
(94, 'Jennie', 'BLACKPINK', 'Jennie Kim', 'https://example.com/jennie.jpg', 'BLACKPINK成员', 'jennie_test_001', true, 1773407317000, 1773407317000),
|
||||
(95, 'Rosé', 'BLACKPINK', 'Park Chae-young', 'https://example.com/rose.jpg', 'BLACKPINK成员', 'rose_test_001', true, 1773407317000, 1773407317000)
|
||||
ON CONFLICT (star_id) DO UPDATE SET name = EXCLUDED.name, tag = EXCLUDED.tag;
|
||||
|
||||
-- 插入用户数据
|
||||
INSERT INTO public.users (id, mobile, password_hash, access_token, token_expires_at, avatar_url, global_wallet_address, is_active, created_at, updated_at, deleted_at) VALUES
|
||||
(1, '13900000001', '$2a$10$bNqpiVCSSrhxAsUelgkEp.j1untPPWMDT5208fmcUPKpsFHZlhjrW', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJzdGFyX2lkIjo4NywidXBkYXRlZF9hdCI6MTc3MzU4MTI5MiwiZXhwIjoxNzc3NTI5OTg5LCJpYXQiOjE3NzY5MjUxODl9.LooVCY-ST2y02qKud8A6GRVL1RxOy4pXVD6KC7NLs6w', 1777529989817, 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/avatar/1/87/avatar.png', NULL, true, 1773322609887, 1773581292, NULL),
|
||||
(2, '17319409126', '$2a$10$bNqpiVCSSrhxAsUelgkEp.j1untPPWMDT5208fmcUPKpsFHZlhjrW', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoyLCJzdGFyX2lkIjo4NywidXBkYXRlZF9hdCI6MTc3MzMyNDEwMTU5OCwiZXhwIjoxNzc0NDIxOTIxLCJpYXQiOjE3NzM4MTcxMjF9.GnAL9exh-VcXDHqwTn1bNKVAEAg6xfMMpd6JTqkwugk', 1774421921871, NULL, NULL, true, 1773324101598, 1773324101598, NULL),
|
||||
(8, '13800000001', '$2a$10$LC8XORj6Nx9nT6j5QHfN5u2nB4z8e0v2dWm7X2uF5m6W6br8NnDPS', NULL, NULL, NULL, NULL, true, 1773407317000, 1776649807972, NULL),
|
||||
(9, '13800000002', '$2a$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', NULL, NULL, NULL, NULL, true, 1773407317000, 1773407317000, NULL),
|
||||
(10, '13800000003', '$2a$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', NULL, NULL, NULL, NULL, true, 1773407317000, 1773407317000, NULL),
|
||||
(11, '13800000004', '$2a$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', NULL, NULL, NULL, NULL, true, 1773407317000, 1773407317000, NULL),
|
||||
(12, '13800000005', '$2a$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', NULL, NULL, NULL, NULL, true, 1773407317000, 1773407317000, NULL)
|
||||
ON CONFLICT (id) DO UPDATE SET access_token = EXCLUDED.access_token, updated_at = EXCLUDED.updated_at;
|
||||
|
||||
-- 插入粉丝档案数据
|
||||
INSERT INTO public.fan_profiles (id, user_id, star_id, nickname, level, times, social, experience, coin_balance, crystal_balance, tags, starbook_limit, slot_limit, assets_count, chain_address, is_active, created_at, updated_at, avatar_url) VALUES
|
||||
(1, 1, 87, '测试用户', 6, 1, 0, 1750, 0, 10919, '[]', 3, 3, 42, NULL, true, 1773322609890, 1776926969, 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/avatar/1/87/avatar.png'),
|
||||
(2, 2, 87, 'topfans1', 1, 1, 0, 0, 0, 0, '[]', 3, 3, 0, NULL, true, 1773324101600, 1773324101600, NULL)
|
||||
ON CONFLICT (id) DO UPDATE SET level = EXCLUDED.level, experience = EXCLUDED.experience, updated_at = EXCLUDED.updated_at;
|
||||
|
||||
-- 插入 Booth Slots 数据
|
||||
INSERT INTO public.booth_slots (slot_id, host_profile_id, user_id, star_id, slot_index, visibility, is_enabled, unlock_type, unlock_value, created_at, updated_at) VALUES
|
||||
(37, 1, 1, 87, 1, 'public', true, 'free', 0, 1773748940460, 1773748940460),
|
||||
(38, 1, 1, 87, 2, 'public', true, 'free', 0, 1773748940460, 1773748940460),
|
||||
(39, 1, 1, 87, 3, 'public', true, 'free', 0, 1773748940460, 1773748940460),
|
||||
(40, 1, 1, 87, 4, 'private', true, 'free', 0, 1773748940460, 1773748940460),
|
||||
(41, 1, 1, 87, 5, 'private', true, 'free', 0, 1773748940460, 1773748940460),
|
||||
(42, 1, 1, 87, 6, 'private', true, 'free', 0, 1773748940460, 1773748940460)
|
||||
ON CONFLICT (slot_id) DO NOTHING;
|
||||
|
||||
-- 插入 Assets 数据
|
||||
INSERT INTO public.assets (id, owner_uid, star_id, name, cover_url, material_url, description, grade, tags, visibility, status, tx_hash, block_number, like_count, is_original, created_at, updated_at, minted_at, deleted_at, is_active, info) VALUES
|
||||
(17, 1, 87, '1', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/covers/17_1775472017.png', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/ai_generated_1775472013603_g87iwk.png', NULL, NULL, NULL, 'private', 1, '0x1922ffb92fcd1a7e4a8c6b3347e8e0791f6107fa44cbde1734ecb1bcd861d63c', 1493537, 4, false, 1775472014756, 1775472018619, 1775472018619, NULL, true, NULL),
|
||||
(18, 1, 87, '1', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/covers/18_1775539093.png', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/ai_generated_1775539090338_fwzxhs.png', NULL, NULL, NULL, 'private', 1, '0x40ca6efcf29f47731453f01b5c364cde52acbce597c1fbf8261301cc44ec4b7a', 1406147, 2, false, 1775539090589, 1775539093945, 1775539093945, NULL, true, NULL),
|
||||
(19, 1, 87, '未命名藏品', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/covers/19_1775812743.png', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/ai_generated_1775812740051_gf8ew2.png', NULL, NULL, NULL, 'private', 1, '0x0e3e47c58814e52465ac1e928f3392ff1c692ea448103fcb03c4a10077141308', 1219083, 3, false, 1775812740261, 1775812743690, 1775812743690, NULL, true, '111'),
|
||||
(20, 1, 87, '星卡', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/covers/20_1775812994.png', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/1775812991404.jpg', NULL, NULL, NULL, 'private', 1, '0x49d04d7463cf03374f6e622d739f69e83cd4ba84b145627f039a22340daead93', 1938046, 2, false, 1775812991507, 1775812994841, 1775812994841, NULL, true, '11'),
|
||||
(21, 1, 87, '星卡', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/covers/21_1775813155.png', 'https://top-fans-test.oss-cn-shanghai.aliyuncs.com/asset/1/87/1775813152104.jpg', NULL, NULL, NULL, 'private', 1, '0x6d7c0c276d7485d17e6dea4c9d1e394315613d69114699d341c221a22e8156f8', 1118388, 2, false, 1775813152198, 1775813155515, 1775813155515, NULL, true, '2')
|
||||
ON CONFLICT (id) DO UPDATE SET like_count = EXCLUDED.like_count;
|
||||
|
||||
-- 插入 Exhibitions 数据
|
||||
INSERT INTO public.exhibitions (id, asset_id, slot_id, host_profile_id, occupier_uid, occupier_star_id, start_time, expire_at, created_at, updated_at, deleted_at) VALUES
|
||||
(1, 17, 37, 1, 1, 87, 1776926400000, 1777555200000, 1776926400000, 1776926400000, NULL)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
@ -161,18 +161,18 @@
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tasks/daily-tasks",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/tasks/guide",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
},
|
||||
// {
|
||||
// "path": "pages/tasks/daily-tasks",
|
||||
// "style": {
|
||||
// "navigationStyle": "custom"
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "path": "pages/tasks/guide",
|
||||
// "style": {
|
||||
// "navigationStyle": "custom"
|
||||
// }
|
||||
// },
|
||||
{
|
||||
"path": "pages/tasks/revenue",
|
||||
"style": {
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
<view class="user-info-with-artwork">
|
||||
<image
|
||||
class="user-avatar-small"
|
||||
:src="item.avatar_url || '/static/avatar/1.jpeg'"
|
||||
:src="item.owner_avatar || '/static/avatar/1.jpeg'"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<text class="user-nickname">用户 :
|
||||
|
||||
@ -66,11 +66,12 @@
|
||||
>
|
||||
<image
|
||||
class="nft-image"
|
||||
:class="{ 'nft-image-displayed': item.display_status === 1 }"
|
||||
:src="item.coverUrl"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="status-badge" :class="item.display_status === 1 ? 'status-badge-active' : 'status-badge-pending'">
|
||||
<text class="status-text">{{ item.display_status === 1 ? '已展示' : '待展示' }}</text>
|
||||
<view v-if="item.display_status === 1" class="status-overlay">
|
||||
<text class="status-text-center">已展示</text>
|
||||
</view>
|
||||
<view class="nft-info">
|
||||
<text class="nft-name">{{ item.name }}</text>
|
||||
@ -89,7 +90,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 典藏藏品:按 category 分组 -->
|
||||
<!-- 典藏藏品:按 category 分组 -->
|
||||
<view v-if="currentType === 'collection'">
|
||||
<view v-for="group in collectionGroups" :key="group.category" class="nft-group">
|
||||
<!-- 分组标题 -->
|
||||
@ -108,11 +109,12 @@
|
||||
>
|
||||
<image
|
||||
class="nft-image"
|
||||
:class="{ 'nft-image-displayed': item.display_status === 1 }"
|
||||
:src="item.coverUrl"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="status-badge" :class="item.display_status === 1 ? 'status-badge-active' : 'status-badge-pending'">
|
||||
<text class="status-text">{{ item.display_status === 1 ? '已展示' : '待展示' }}</text>
|
||||
<view v-if="item.display_status === 1" class="status-overlay">
|
||||
<text class="status-text-center">已展示</text>
|
||||
</view>
|
||||
<view class="nft-info">
|
||||
<text class="nft-name">{{ item.name }}</text>
|
||||
@ -131,7 +133,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 活动藏品:按 activity_type 分组 -->
|
||||
<!-- 活动藏品:按 activity_type 分组 -->
|
||||
<view v-if="currentType === 'activity'">
|
||||
<view v-for="group in activityGroups" :key="group.category" class="nft-group">
|
||||
<!-- 分组标题 -->
|
||||
@ -150,11 +152,12 @@
|
||||
>
|
||||
<image
|
||||
class="nft-image"
|
||||
:class="{ 'nft-image-displayed': item.display_status === 1 }"
|
||||
:src="item.coverUrl"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<view class="status-badge" :class="item.display_status === 1 ? 'status-badge-active' : 'status-badge-pending'">
|
||||
<text class="status-text">{{ item.display_status === 1 ? '已展示' : '待展示' }}</text>
|
||||
<view v-if="item.display_status === 1" class="status-overlay">
|
||||
<text class="status-text-center">已展示</text>
|
||||
</view>
|
||||
<view class="nft-info">
|
||||
<text class="nft-name">{{ item.name }}</text>
|
||||
@ -540,10 +543,15 @@ watch(() => props.isActive, (newVal) => {
|
||||
width: 192rpx;
|
||||
height: 224rpx;
|
||||
border-radius: 16rpx;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
/* background: rgba(255, 255, 255, 0.05); */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 已展示的图片 - 灰色滤镜 */
|
||||
.nft-image-displayed {
|
||||
filter: grayscale(23%);
|
||||
}
|
||||
|
||||
/* 更多占位符 */
|
||||
.more-placeholder {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
@ -559,26 +567,45 @@ watch(() => props.isActive, (newVal) => {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 展示状态标签 */
|
||||
.status-badge {
|
||||
/* 展示状态覆盖层 - 居中显示 */
|
||||
.status-overlay {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
border-radius: 8rpx;
|
||||
padding: 4rpx 8rpx;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 192rpx;
|
||||
height: 224rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 16rpx;
|
||||
z-index: 1;
|
||||
|
||||
}
|
||||
.status-badge-active {
|
||||
background: linear-gradient(135deg, #FFD700, #FFA500);
|
||||
box-shadow: 0 0 12rpx rgba(255, 215, 0, 0.6);
|
||||
}
|
||||
.status-badge-pending {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.status-text {
|
||||
font-size: 18rpx;
|
||||
|
||||
.status-text-center {
|
||||
font-size: 24rpx;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.8);
|
||||
/* 渐变:左浅橙粉 → 右柔粉红 */
|
||||
background: linear-gradient(to bottom right,
|
||||
#F0E4B1 0%, /* 左:浅橙粉 */
|
||||
#F08399 50%,
|
||||
#B94E73 100% /* 右:柔粉红 */
|
||||
);
|
||||
|
||||
border-radius: 24rpx; /* 胶囊形 */
|
||||
|
||||
/* 立体感核心:多层阴影 + 内阴影模拟凸起 */
|
||||
box-shadow:
|
||||
/* 外层投影 - 让按钮浮起 */
|
||||
0 4rpx 12rpx rgba(255, 143, 158, 0.2),
|
||||
0 2rpx 6rpx rgba(255, 143, 158, 0.15),
|
||||
|
||||
/* 内阴影 - 模拟顶部受光 + 底部凹陷 */
|
||||
inset 0 2rpx 4rpx rgba(255, 255, 255, 0.4), /* 顶部高光 */
|
||||
inset 0 -2rpx 4rpx rgba(0, 0, 0, 0.05); /* 底部暗部 */
|
||||
padding: 16rpx;
|
||||
}
|
||||
|
||||
.nft-name {
|
||||
|
||||
@ -5,15 +5,7 @@
|
||||
|
||||
<!-- 礼盒区域 -->
|
||||
<view class="gift-box">
|
||||
<view class="gift-container">
|
||||
<view class="gift-bow"></view>
|
||||
<view class="gift-body">
|
||||
<text class="gift-text">TOPFANS</text>
|
||||
<view class="gift-window">
|
||||
<text class="question-mark">?</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<image class="gift-image" src="/static/nft/lihe.png" mode="aspectFit" />
|
||||
</view>
|
||||
|
||||
<!-- 进度条区域 -->
|
||||
@ -206,11 +198,11 @@ onMounted(() => {
|
||||
|
||||
.gift-box {
|
||||
position: absolute;
|
||||
top: 20%;
|
||||
top: 25%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 450rpx;
|
||||
height: 450rpx;
|
||||
width: 480rpx;
|
||||
height: 480rpx;
|
||||
z-index: 2;
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
@ -224,125 +216,9 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.gift-container {
|
||||
position: relative;
|
||||
.gift-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.gift-bow {
|
||||
position: absolute;
|
||||
top: -30rpx;
|
||||
width: 200rpx;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(135deg, #FFB6D9 0%, #FFA8C5 50%, #FFB6D9 100%);
|
||||
border-radius: 50rpx 50rpx 20rpx 20rpx;
|
||||
box-shadow: 0 8rpx 24rpx rgba(255, 107, 157, 0.4);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.gift-bow::before,
|
||||
.gift-bow::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 10rpx;
|
||||
width: 80rpx;
|
||||
height: 60rpx;
|
||||
background: linear-gradient(135deg, #FFB6D9 0%, #FFA8C5 100%);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.gift-bow::before {
|
||||
left: -40rpx;
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
|
||||
.gift-bow::after {
|
||||
right: -40rpx;
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
|
||||
.gift-body {
|
||||
position: relative;
|
||||
width: 350rpx;
|
||||
height: 350rpx;
|
||||
background: linear-gradient(135deg, #FFE5F0 0%, #FFD4E8 50%, #FFC9E3 100%);
|
||||
border-radius: 30rpx;
|
||||
box-shadow: 0 16rpx 48rpx rgba(255, 107, 157, 0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gift-body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 60rpx;
|
||||
height: 100%;
|
||||
background: linear-gradient(180deg, rgba(255, 182, 217, 0.6) 0%, rgba(255, 168, 197, 0.6) 100%);
|
||||
}
|
||||
|
||||
.gift-body::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
width: 100%;
|
||||
height: 60rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 182, 217, 0.6) 0%, rgba(255, 168, 197, 0.6) 100%);
|
||||
}
|
||||
|
||||
.gift-text {
|
||||
position: absolute;
|
||||
top: 40rpx;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #FF6B9D;
|
||||
letter-spacing: 4rpx;
|
||||
text-shadow: 0 2rpx 8rpx rgba(255, 107, 157, 0.3);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.gift-window {
|
||||
position: relative;
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
background: linear-gradient(135deg, #FFF5FA 0%, #FFEBF3 100%);
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: inset 0 4rpx 12rpx rgba(255, 107, 157, 0.2);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.question-mark {
|
||||
font-size: 120rpx;
|
||||
font-weight: bold;
|
||||
color: #FFB6D9;
|
||||
text-shadow: 0 4rpx 12rpx rgba(255, 107, 157, 0.3);
|
||||
animation: questionPulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes questionPulse {
|
||||
0%, 100% {
|
||||
opacity: 0.8;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
|
||||
@ -49,19 +49,18 @@
|
||||
|
||||
<!-- 礼盒 -->
|
||||
<view class="gift-box" :class="{ 'gift-opened': isGiftOpened }">
|
||||
<view class="gift-container">
|
||||
<view class="gift-lid">
|
||||
<view class="gift-bow"></view>
|
||||
<view class="gift-lid-top">
|
||||
<text class="gift-text">TOPFANS</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="gift-body">
|
||||
<view class="gift-window">
|
||||
<text class="question-mark">?</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<image
|
||||
v-if="!isGiftOpened"
|
||||
class="gift-image"
|
||||
src="/static/nft/lihe.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
<image
|
||||
v-else
|
||||
class="gift-image gift-image-opened"
|
||||
src="/static/nft/lihe_kaiqi.png"
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
@ -1086,16 +1085,16 @@ onMounted(() => {
|
||||
bottom: 20%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 420rpx;
|
||||
height: 450rpx;
|
||||
width: 480rpx;
|
||||
height: 480rpx;
|
||||
z-index: 3;
|
||||
animation: giftFloat 3s ease-in-out infinite;
|
||||
transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
perspective: 1000rpx;
|
||||
}
|
||||
|
||||
.gift-box.gift-opened {
|
||||
bottom: 5%;
|
||||
width: 17rem;
|
||||
bottom: 15%;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@ -1108,192 +1107,29 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.gift-container {
|
||||
position: relative;
|
||||
.gift-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
transition: opacity 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.gift-lid {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 340rpx;
|
||||
height: 140rpx;
|
||||
z-index: 3;
|
||||
transition: all 1s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform-style: preserve-3d;
|
||||
transform-origin: bottom left;
|
||||
.gift-image-opened {
|
||||
width: 17rem;
|
||||
animation: giftOpenPop 0.5s ease-out;
|
||||
}
|
||||
|
||||
.gift-opened .gift-lid {
|
||||
transform: translateX(-70%) translateY(140rpx) rotateZ(-45deg);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.gift-bow {
|
||||
position: absolute;
|
||||
top: -30rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 200rpx;
|
||||
height: 80rpx;
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.gift-bow::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 20rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 60rpx;
|
||||
background: linear-gradient(135deg, #FFB6D9 0%, #FF9DC5 100%);
|
||||
border-radius: 8rpx;
|
||||
box-shadow: 0 4rpx 15rpx rgba(255, 107, 157, 0.4);
|
||||
}
|
||||
|
||||
.gift-bow::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 180rpx;
|
||||
height: 50rpx;
|
||||
background: linear-gradient(135deg, #FFB6D9 0%, #FF9DC5 50%, #FFB6D9 100%);
|
||||
border-radius: 30rpx;
|
||||
box-shadow: 0 6rpx 20rpx rgba(255, 107, 157, 0.5);
|
||||
}
|
||||
|
||||
.gift-lid-top {
|
||||
position: relative;
|
||||
width: 340rpx;
|
||||
height: 100rpx;
|
||||
background: linear-gradient(180deg,
|
||||
#FFF0F5 0%,
|
||||
#FFE5F0 30%,
|
||||
#FFD4E8 100%);
|
||||
border-radius: 20rpx 20rpx 8rpx 8rpx;
|
||||
box-shadow:
|
||||
0 10rpx 40rpx rgba(255, 107, 157, 0.4),
|
||||
inset 0 -5rpx 15rpx rgba(255, 182, 217, 0.3),
|
||||
inset 0 5rpx 15rpx rgba(255, 255, 255, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: visible;
|
||||
border: 3rpx solid rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.gift-lid-top::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 280rpx;
|
||||
height: 60rpx;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(255, 182, 217, 0.4) 50%,
|
||||
transparent 100%);
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
|
||||
.gift-text {
|
||||
position: relative;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #FF6B9D;
|
||||
letter-spacing: 4rpx;
|
||||
text-shadow:
|
||||
0 2rpx 8rpx rgba(255, 107, 157, 0.5),
|
||||
0 0 20rpx rgba(255, 182, 217, 0.3);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.gift-body {
|
||||
position: relative;
|
||||
width: 340rpx;
|
||||
height: 280rpx;
|
||||
background: linear-gradient(180deg,
|
||||
#FFE5F0 0%,
|
||||
#FFD4E8 50%,
|
||||
#FFC9E3 100%);
|
||||
border-radius: 8rpx 8rpx 20rpx 20rpx;
|
||||
box-shadow:
|
||||
0 15rpx 50rpx rgba(255, 107, 157, 0.4),
|
||||
inset 0 5rpx 20rpx rgba(255, 255, 255, 0.3),
|
||||
inset 0 -5rpx 20rpx rgba(255, 182, 217, 0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
border: 3rpx solid rgba(255, 255, 255, 0.5);
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.gift-body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 8rpx;
|
||||
background: linear-gradient(90deg,
|
||||
rgba(255, 107, 157, 0.3) 0%,
|
||||
rgba(255, 182, 217, 0.5) 50%,
|
||||
rgba(255, 107, 157, 0.3) 100%);
|
||||
}
|
||||
|
||||
.gift-body::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
background: radial-gradient(circle at center,
|
||||
rgba(255, 255, 255, 0.2) 0%,
|
||||
transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.gift-window {
|
||||
position: relative;
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
background: linear-gradient(135deg,
|
||||
#FFF5FA 0%,
|
||||
#FFEBF3 50%,
|
||||
#FFE0ED 100%);
|
||||
border-radius: 20rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow:
|
||||
inset 0 4rpx 15rpx rgba(255, 107, 157, 0.2),
|
||||
0 4rpx 20rpx rgba(255, 182, 217, 0.3);
|
||||
z-index: 2;
|
||||
border: 2rpx solid rgba(255, 182, 217, 0.4);
|
||||
}
|
||||
|
||||
.question-mark {
|
||||
font-size: 120rpx;
|
||||
font-weight: bold;
|
||||
color: #FFB6D9;
|
||||
text-shadow:
|
||||
0 4rpx 15rpx rgba(255, 107, 157, 0.4),
|
||||
0 0 30rpx rgba(255, 182, 217, 0.3);
|
||||
@keyframes giftOpenPop {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-action {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// API 基础配置
|
||||
// const baseURL = 'http://101.132.250.62:8080'
|
||||
const baseURL = 'http://101.132.250.62:8080'
|
||||
// const baseURL = 'http://192.168.110.60:8080'
|
||||
const baseURL = 'http://localhost:8080'
|
||||
// const baseURL = 'http://localhost:8080'
|
||||
|
||||
// 是否使用模拟数据(开发调试时设为 true,后端API准备好后改为 false)
|
||||
const USE_MOCK_API = false
|
||||
|
||||
Loading…
Reference in New Issue
Block a user