feat: 添加抖音商品奖励功能,并增强后台用户列表统计数据、邀请人数及道具数量展示。

This commit is contained in:
邹方成 2026-01-08 10:13:29 +08:00
parent e3a96e68d8
commit b21e2db8ef
69 changed files with 4831 additions and 594 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -1,5 +1,5 @@
# Build stage # Build stage
FROM golang:1.23-alpine AS builder FROM golang:1.24-alpine AS builder
# Set working directory # Set working directory
WORKDIR /app WORKDIR /app
@ -12,20 +12,20 @@ COPY go.mod go.sum ./
# Set Go environment variables and proxy # Set Go environment variables and proxy
ENV GO111MODULE=on \ ENV GO111MODULE=on \
CGO_ENABLED=0 \ CGO_ENABLED=0 \
GOOS=linux \ GOOS=linux \
GOARCH=amd64 \ GOARCH=amd64 \
GOPROXY=https://goproxy.cn,https://goproxy.io,direct \ GOPROXY=https://goproxy.cn,https://goproxy.io,direct \
GOSUMDB=sum.golang.google.cn GOSUMDB=sum.golang.google.cn
# Download dependencies with retry mechanism # Download dependencies with retry mechanism
RUN go mod download || \ RUN go mod download || \
(echo "Retrying with different proxy..." && \ (echo "Retrying with different proxy..." && \
go env -w GOPROXY=https://goproxy.io,https://mirrors.aliyun.com/goproxy/,direct && \ go env -w GOPROXY=https://goproxy.io,https://mirrors.aliyun.com/goproxy/,direct && \
go mod download) || \ go mod download) || \
(echo "Final retry with direct mode..." && \ (echo "Final retry with direct mode..." && \
go env -w GOPROXY=direct && \ go env -w GOPROXY=direct && \
go mod download) go mod download)
# Copy source code # Copy source code
COPY . . COPY . .
@ -62,4 +62,4 @@ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:9991/system/health || exit 1 CMD wget --no-verbose --tries=1 --spider http://localhost:9991/system/health || exit 1
# Run the application # Run the application
CMD ["./miniChat"] CMD ["sh", "-c", "./miniChat -env=${ACTIVE_ENV}"]

21
backend.log Normal file
View File

@ -0,0 +1,21 @@
{"level":"info","time":"2026-01-08 00:53:15","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
 ____ _ _ _ ____
 | __ ) (_) _ __ __| | | |__ ___ __ __ / ___| __ _ _ __ ___ ___
 | _ \ | | | '_ \ / _` | | '_ \ / _ \ \ \/ / | | _ / _` | | '_ ` _ \ / _ \
 | |_) | | | | | | | | (_| | | |_) | | (_) | > < | |_| | | (_| | | | | | | | | __/
 |____/ |_| |_| |_| \__,_| |_.__/ \___/ /_/\_\ \____| \__,_| |_| |_| |_| \___|
▌ 客户项目: 盲盒游戏
▌ 项目版本: Release-2025111111
▌ 启动时间: 2026-01-08 00:53:15
▌ 运行环境: darwin go1.24.2
▌ 服务端口: [:9991]
▌ 服务配置: [fat]
▌ 数据库连接: ✔ 已建立
{"level":"info","time":"2026-01-08 00:53:15","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 00:53:15","caller":"logger/logger.go:309","msg":"对对碰自动开奖: 后台任务已启动","domain":"mini-chat[fat]"}
{"level":"fatal","time":"2026-01-08 00:53:15","caller":"logger/logger.go:333","msg":"http server startup err","domain":"mini-chat[fat]","error":"listen tcp :9991: bind: address already in use"}
{"level":"info","time":"2026-01-08 00:53:15","caller":"logger/logger.go:309","msg":"[抖店定时同步] 定时任务已启动","domain":"mini-chat[fat]"}
exit status 1

127
backend_assets.log Normal file
View File

@ -0,0 +1,127 @@
{"level":"info","time":"2026-01-08 01:00:31","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
 ____ _ _ _ ____
 | __ ) (_) _ __ __| | | |__ ___ __ __ / ___| __ _ _ __ ___ ___
 | _ \ | | | '_ \ / _` | | '_ \ / _ \ \ \/ / | | _ / _` | | '_ ` _ \ / _ \
 | |_) | | | | | | | | (_| | | |_) | | (_) | > < | |_| | | (_| | | | | | | | | __/
 |____/ |_| |_| |_| \__,_| |_.__/ \___/ /_/\_\ \____| \__,_| |_| |_| |_| \___|
▌ 客户项目: 盲盒游戏
▌ 项目版本: Release-2025111111
▌ 启动时间: 2026-01-08 01:00:31
▌ 运行环境: darwin go1.24.2
▌ 服务端口: [:9991]
▌ 服务配置: [fat]
▌ 数据库连接: ✔ 已建立
{"level":"info","time":"2026-01-08 01:00:31","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:31","caller":"logger/logger.go:309","msg":"对对碰自动开奖: 后台任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:31","caller":"logger/logger.go:309","msg":"[抖店定时同步] 定时任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:31","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
{"level":"info","time":"2026-01-08 01:00:31","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
{"level":"info","time":"2026-01-08 01:00:31","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
{"level":"info","time":"2026-01-08 01:00:31","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
{"level":"info","time":"2026-01-08 01:00:31","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
{"level":"error","time":"2026-01-08 01:00:32","caller":"logger/logger.go:327","msg":"解密配置失败","domain":"mini-chat[fat]","key":"douyin.app_secret","error":"ciphertext is not a multiple of the block size"}
{"level":"info","time":"2026-01-08 01:00:32","caller":"logger/logger.go:309","msg":"动态配置加载完成","domain":"mini-chat[fat]","count":26}
{"level":"info","time":"2026-01-08 01:00:44","caller":"logger/logger.go:309","msg":"refund: ActualAmount=0, skip wechat refund: order=O20260107213341575","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:45","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":4002,"rows":0}
{"level":"info","time":"2026-01-08 01:00:45","caller":"logger/logger.go:309","msg":"refund restore game_pass success: order=O20260107213341575 gp_id=66 count=1","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:45","caller":"logger/logger.go:309","msg":"refund: ActualAmount=0, skip wechat refund: order=O20260107213340076","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:45","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":4001,"rows":0}
{"level":"info","time":"2026-01-08 01:00:45","caller":"logger/logger.go:309","msg":"refund restore game_pass success: order=O20260107213340076 gp_id=66 count=1","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:45","caller":"logger/logger.go:309","msg":"refund: ActualAmount=0, skip wechat refund: order=O20260107213339935","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:45","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":4000,"rows":0}
{"level":"info","time":"2026-01-08 01:00:46","caller":"logger/logger.go:309","msg":"refund restore game_pass success: order=O20260107213339935 gp_id=66 count=1","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:46","caller":"logger/logger.go:309","msg":"refund: ActualAmount=0, skip wechat refund: order=O20260107213338161","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:46","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3999,"rows":0}
{"level":"info","time":"2026-01-08 01:00:46","caller":"logger/logger.go:309","msg":"refund restore game_pass success: order=O20260107213338161 gp_id=66 count=1","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:46","caller":"logger/logger.go:309","msg":"refund: ActualAmount=0, skip wechat refund: order=O20260107213337885","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:46","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3998,"rows":0}
{"level":"info","time":"2026-01-08 01:00:47","caller":"logger/logger.go:309","msg":"refund restore game_pass success: order=O20260107213337885 gp_id=66 count=1","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:49","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3997,"rows":0}
{"level":"info","time":"2026-01-08 01:00:51","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3996,"rows":0}
{"level":"info","time":"2026-01-08 01:00:52","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3995,"rows":0}
{"level":"info","time":"2026-01-08 01:00:52","caller":"logger/logger.go:309","msg":"refund: ActualAmount=0, skip wechat refund: order=GP202601071823419722","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:53","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3994,"rows":0}
{"level":"info","time":"2026-01-08 01:00:53","caller":"logger/logger.go:309","msg":"refund restore game_pass success: order=GP202601071823419722 game_pass_id=65","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:54","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3993,"rows":0}
{"level":"info","time":"2026-01-08 01:00:54","caller":"logger/logger.go:309","msg":"refund: ActualAmount=0, skip wechat refund: order=O20260107182203580","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:54","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3992,"rows":0}
{"level":"info","time":"2026-01-08 01:00:54","caller":"logger/logger.go:309","msg":"refund restore game_pass success: order=O20260107182203580 gp_id=64 count=1","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:56","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3991,"rows":0}
{"level":"info","time":"2026-01-08 01:00:57","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3984,"rows":0}
{"level":"info","time":"2026-01-08 01:00:57","caller":"logger/logger.go:309","msg":"refund: ActualAmount=0, skip wechat refund: order=RG20260107092128879615","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:00:57","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3973,"rows":0}
{"level":"info","time":"2026-01-08 01:00:59","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3972,"rows":0}
{"level":"info","time":"2026-01-08 01:01:00","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3971,"rows":0}
{"level":"info","time":"2026-01-08 01:01:00","caller":"logger/logger.go:309","msg":"refund: ActualAmount=0, skip wechat refund: order=GP202601070918119626","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:01:00","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3969,"rows":0}
{"level":"info","time":"2026-01-08 01:01:01","caller":"logger/logger.go:309","msg":"refund restore game_pass success: order=GP202601070918119626 game_pass_id=62","domain":"mini-chat[fat]"}
{"level":"debug","time":"2026-01-08 01:01:01","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:01:01"}
{"level":"info","time":"2026-01-08 01:01:01","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:03:14.807+08:00","last_settled":"2026-01-08T01:00:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:03:14","now":"2026-01-08 01:01:01","skip":true}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:02:59.385+08:00","last_settled":"2026-01-08T00:59:59.385+08:00"}
{"level":"info","time":"2026-01-08 01:01:02","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3968,"rows":0}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:02:59","now":"2026-01-08 01:01:01","skip":true}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:03:44.807+08:00","last_settled":"2026-01-08T00:58:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:03:44","now":"2026-01-08 01:01:01","skip":true}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:05:14.807+08:00","last_settled":"2026-01-08T01:00:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:05:14","now":"2026-01-08 01:01:01","skip":true}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 01:01:01","skip":true}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:01:01","skip":true}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:01:01","skip":true}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 01:01:01","skip":true}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:01:02","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:01:01","skip":true}
{"level":"info","time":"2026-01-08 01:01:03","caller":"logger/logger.go:309","msg":"清理一番赏占位成功","domain":"mini-chat[fat]","order_id":3965,"rows":0}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:01:06","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:01:06","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
2026/01/08 01:01:06 /Users/win/aicode/bindbox/bindbox_game/internal/api/admin/dashboard_spending.go:113 Error 1054 (42S22): Unknown column 'orders.activity_id' in 'on clause'
[11.786ms] [rows:-] SELECT
orders.user_id,
SUM(orders.actual_amount) as total_amount,
COUNT(orders.id) as order_count,
SUM(orders.discount_amount) as total_discount,
SUM(orders.points_amount) as total_points,
SUM(CASE WHEN orders.source_type = 4 THEN 1 ELSE 0 END) as game_pass_count,
SUM(CASE WHEN orders.item_card_id > 0 THEN 1 ELSE 0 END) as item_card_count,
SUM(CASE WHEN activities.play_type = 'ichiban' THEN orders.actual_amount ELSE 0 END) as ichiban_spending,
SUM(CASE WHEN activities.play_type = 'ichiban' THEN 1 ELSE 0 END) as ichiban_count,
SUM(CASE WHEN activities.play_type IN ('infinite', 'box') THEN orders.actual_amount ELSE 0 END) as infinite_spending,
SUM(CASE WHEN activities.play_type IN ('infinite', 'box') THEN 1 ELSE 0 END) as infinite_count,
SUM(CASE WHEN activities.play_type = 'matching' THEN orders.actual_amount ELSE 0 END) as matching_spending,
SUM(CASE WHEN activities.play_type = 'matching' THEN 1 ELSE 0 END) as matching_count
FROM `orders` LEFT JOIN activities ON activities.id = orders.activity_id WHERE orders.status = 2 AND orders.created_at >= '2026-01-01 01:01:06.706' AND orders.created_at <= '2026-01-08 01:01:06.706' GROUP BY `orders`.`user_id` ORDER BY total_amount DESC LIMIT 500
{"level":"debug","time":"2026-01-08 01:01:31","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:01:31"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:03:14.807+08:00","last_settled":"2026-01-08T01:00:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:03:14","now":"2026-01-08 01:01:31","skip":true}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:02:59.385+08:00","last_settled":"2026-01-08T00:59:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:02:59","now":"2026-01-08 01:01:31","skip":true}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:03:44.807+08:00","last_settled":"2026-01-08T00:58:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:03:44","now":"2026-01-08 01:01:31","skip":true}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:05:14.807+08:00","last_settled":"2026-01-08T01:00:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:05:14","now":"2026-01-08 01:01:31","skip":true}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 01:01:31","skip":true}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:01:31","skip":true}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:01:31","skip":true}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 01:01:31","skip":true}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:01:32","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:01:31","skip":true}
signal: killed

397
backend_debug.log Normal file
View File

@ -0,0 +1,397 @@
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
 ____ _ _ _ ____
 | __ ) (_) _ __ __| | | |__ ___ __ __ / ___| __ _ _ __ ___ ___
 | _ \ | | | '_ \ / _` | | '_ \ / _ \ \ \/ / | | _ / _` | | '_ ` _ \ / _ \
 | |_) | | | | | | | | (_| | | |_) | | (_) | > < | |_| | | (_| | | | | | | | | __/
 |____/ |_| |_| |_| \__,_| |_.__/ \___/ /_/\_\ \____| \__,_| |_| |_| |_| \___|
▌ 客户项目: 盲盒游戏
▌ 项目版本: Release-2025111111
▌ 启动时间: 2026-01-08 01:32:46
▌ 运行环境: darwin go1.24.2
▌ 服务端口: [:9991]
▌ 服务配置: [fat]
▌ 数据库连接: ✔ 已建立
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"对对碰自动开奖: 后台任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"[抖店定时同步] 定时任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
{"level":"error","time":"2026-01-08 01:32:46","caller":"logger/logger.go:327","msg":"解密配置失败","domain":"mini-chat[fat]","key":"douyin.app_secret","error":"ciphertext is not a multiple of the block size"}
{"level":"info","time":"2026-01-08 01:32:46","caller":"logger/logger.go:309","msg":"动态配置加载完成","domain":"mini-chat[fat]","count":26}
{"level":"info","time":"2026-01-08 01:33:08","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: start=2026-01-01 01:33:08.823091 +0800 CST m=-604777.351307833, end=2026-01-08 01:33:08.823091 +0800 CST m=+22.648692167, type=7d","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:08","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:09","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: start=2026-01-01 01:33:09.794643 +0800 CST m=-604776.379748124, end=2026-01-08 01:33:09.794643 +0800 CST m=+23.620251876, type=7d","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:09","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:10","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: start=2026-01-01 01:33:10.404066 +0800 CST m=-604775.770320333, end=2026-01-08 01:33:10.404066 +0800 CST m=+24.229679667, type=7d","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:10","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:11","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: start=2026-01-01 01:33:11.624776 +0800 CST m=-604774.549601249, end=2026-01-08 01:33:11.624776 +0800 CST m=+25.450398751, type=7d","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:11","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:33:16"}
{"level":"info","time":"2026-01-08 01:33:16","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:33:59.385+08:00","last_settled":"2026-01-08T01:30:59.385+08:00"}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:33:59","now":"2026-01-08 01:33:16","skip":true}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:33:29.385+08:00","last_settled":"2026-01-08T01:30:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:33:29","now":"2026-01-08 01:33:16","skip":true}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:33:59.385+08:00","last_settled":"2026-01-08T01:28:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:33:59","now":"2026-01-08 01:33:16","skip":true}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:35:59.385+08:00","last_settled":"2026-01-08T01:30:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:35:59","now":"2026-01-08 01:33:16","skip":true}
{"level":"debug","time":"2026-01-08 01:33:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:34:29.385+08:00","last_settled":"2026-01-08T01:24:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:34:29","now":"2026-01-08 01:33:16","skip":true}
{"level":"debug","time":"2026-01-08 01:33:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:33:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:33:16","skip":true}
{"level":"debug","time":"2026-01-08 01:33:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:33:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:33:16","skip":true}
{"level":"debug","time":"2026-01-08 01:33:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:33:16","skip":true}
{"level":"debug","time":"2026-01-08 01:33:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:33:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:33:16","skip":true}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:33:21","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:33:21","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:33:21","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:22","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:22","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:33:22","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
2026/01/08 01:33:27 /Users/win/aicode/bindbox/bindbox_game/internal/api/admin/dashboard_activity.go:269 Error 1054 (42S22): Unknown column 'products.image' in 'field list'
[10.836ms] [rows:-] SELECT
activity_draw_logs.id,
activity_draw_logs.user_id,
users.nickname,
users.avatar,
activity_reward_settings.product_id,
products.name as product_name,
products.image as product_image,
products.price as product_price,
orders.actual_amount as order_amount,
activity_draw_logs.created_at
FROM `activity_draw_logs` JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id LEFT JOIN users ON users.id = activity_draw_logs.user_id LEFT JOIN activity_reward_settings ON activity_reward_settings.id = activity_draw_logs.reward_id LEFT JOIN products ON products.id = activity_reward_settings.product_id LEFT JOIN orders ON orders.id = activity_draw_logs.order_id WHERE activity_issues.activity_id = 88 ORDER BY activity_draw_logs.id DESC LIMIT 10
{"level":"error","time":"2026-01-08 01:33:27","caller":"logger/logger.go:327","msg":"GetActivityLogs error: Error 1054 (42S22): Unknown column 'products.image' in 'field list'","domain":"mini-chat[fat]"}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:33:46"}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:33:59.385+08:00","last_settled":"2026-01-08T01:30:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:33:59","now":"2026-01-08 01:33:46","skip":true}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:36:44.808+08:00","last_settled":"2026-01-08T01:33:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:36:44","now":"2026-01-08 01:33:46","skip":true}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:33:59.385+08:00","last_settled":"2026-01-08T01:28:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:33:59","now":"2026-01-08 01:33:46","skip":true}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:35:59.385+08:00","last_settled":"2026-01-08T01:30:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:35:59","now":"2026-01-08 01:33:46","skip":true}
{"level":"debug","time":"2026-01-08 01:33:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:34:29.385+08:00","last_settled":"2026-01-08T01:24:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:34:29","now":"2026-01-08 01:33:46","skip":true}
{"level":"debug","time":"2026-01-08 01:33:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:33:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:33:46","skip":true}
{"level":"debug","time":"2026-01-08 01:33:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:33:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:33:46","skip":true}
{"level":"debug","time":"2026-01-08 01:33:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:33:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:33:46","skip":true}
{"level":"debug","time":"2026-01-08 01:33:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:33:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:33:46","skip":true}
2026/01/08 01:34:09 /Users/win/aicode/bindbox/bindbox_game/internal/api/admin/dashboard_activity.go:269 Error 1054 (42S22): Unknown column 'products.image' in 'field list'
[11.527ms] [rows:-] SELECT
activity_draw_logs.id,
activity_draw_logs.user_id,
users.nickname,
users.avatar,
activity_reward_settings.product_id,
products.name as product_name,
products.image as product_image,
products.price as product_price,
orders.actual_amount as order_amount,
activity_draw_logs.created_at
FROM `activity_draw_logs` JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id LEFT JOIN users ON users.id = activity_draw_logs.user_id LEFT JOIN activity_reward_settings ON activity_reward_settings.id = activity_draw_logs.reward_id LEFT JOIN products ON products.id = activity_reward_settings.product_id LEFT JOIN orders ON orders.id = activity_draw_logs.order_id WHERE activity_issues.activity_id = 88 ORDER BY activity_draw_logs.id DESC LIMIT 10
{"level":"error","time":"2026-01-08 01:34:09","caller":"logger/logger.go:327","msg":"GetActivityLogs error: Error 1054 (42S22): Unknown column 'products.image' in 'field list'","domain":"mini-chat[fat]"}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:34:16"}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:37:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:37:14","now":"2026-01-08 01:34:16","skip":true}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:36:44.808+08:00","last_settled":"2026-01-08T01:33:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:36:44","now":"2026-01-08 01:34:16","skip":true}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:34:16","skip":true}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:35:59.385+08:00","last_settled":"2026-01-08T01:30:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:35:59","now":"2026-01-08 01:34:16","skip":true}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:34:29.385+08:00","last_settled":"2026-01-08T01:24:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:34:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:34:29","now":"2026-01-08 01:34:16","skip":true}
{"level":"debug","time":"2026-01-08 01:34:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:34:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:34:16","skip":true}
{"level":"debug","time":"2026-01-08 01:34:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:34:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:34:16","skip":true}
{"level":"debug","time":"2026-01-08 01:34:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:34:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:34:16","skip":true}
{"level":"debug","time":"2026-01-08 01:34:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:34:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:34:16","skip":true}
{"level":"info","time":"2026-01-08 01:34:21","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"info","time":"2026-01-08 01:34:22","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:34:22","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:34:23","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:34:23","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:34:26","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:34:26","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:34:46"}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:37:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:37:14","now":"2026-01-08 01:34:46","skip":true}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:36:44.808+08:00","last_settled":"2026-01-08T01:33:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:36:44","now":"2026-01-08 01:34:46","skip":true}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:34:46","skip":true}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:35:59.385+08:00","last_settled":"2026-01-08T01:30:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:35:59","now":"2026-01-08 01:34:46","skip":true}
{"level":"debug","time":"2026-01-08 01:34:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:44:44.807+08:00","last_settled":"2026-01-08T01:34:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:34:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:44:44","now":"2026-01-08 01:34:46","skip":true}
{"level":"debug","time":"2026-01-08 01:34:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:34:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:34:46","skip":true}
{"level":"debug","time":"2026-01-08 01:34:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:34:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:34:46","skip":true}
{"level":"debug","time":"2026-01-08 01:34:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:34:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:34:46","skip":true}
{"level":"debug","time":"2026-01-08 01:34:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:34:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:34:46","skip":true}
{"level":"info","time":"2026-01-08 01:34:54","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:34:54","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:35:16"}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:37:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:37:14","now":"2026-01-08 01:35:16","skip":true}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:36:44.808+08:00","last_settled":"2026-01-08T01:33:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:36:44","now":"2026-01-08 01:35:16","skip":true}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:35:16","skip":true}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:35:59.385+08:00","last_settled":"2026-01-08T01:30:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:35:59","now":"2026-01-08 01:35:16","skip":true}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:44:44.807+08:00","last_settled":"2026-01-08T01:34:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:35:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:44:44","now":"2026-01-08 01:35:16","skip":true}
{"level":"debug","time":"2026-01-08 01:35:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:35:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:35:16","skip":true}
{"level":"debug","time":"2026-01-08 01:35:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:35:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:35:16","skip":true}
{"level":"debug","time":"2026-01-08 01:35:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:35:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:35:16","skip":true}
{"level":"debug","time":"2026-01-08 01:35:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:35:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:35:16","skip":true}
{"level":"info","time":"2026-01-08 01:35:26","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:35:31","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:35:31","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:35:46"}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:37:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:37:14","now":"2026-01-08 01:35:46","skip":true}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:36:44.808+08:00","last_settled":"2026-01-08T01:33:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:36:44","now":"2026-01-08 01:35:46","skip":true}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:35:46","skip":true}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:35:59.385+08:00","last_settled":"2026-01-08T01:30:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:35:59","now":"2026-01-08 01:35:46","skip":true}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:44:44.807+08:00","last_settled":"2026-01-08T01:34:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:35:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:44:44","now":"2026-01-08 01:35:46","skip":true}
{"level":"debug","time":"2026-01-08 01:35:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:35:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:35:46","skip":true}
{"level":"debug","time":"2026-01-08 01:35:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:35:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:35:46","skip":true}
{"level":"debug","time":"2026-01-08 01:35:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T02:05:29.386+08:00","last_settled":"2026-01-08T01:35:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:35:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 02:05:29","now":"2026-01-08 01:35:46","skip":true}
{"level":"debug","time":"2026-01-08 01:35:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:35:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:35:46","skip":true}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:36:16"}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:37:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:37:14","now":"2026-01-08 01:36:16","skip":true}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:36:44.808+08:00","last_settled":"2026-01-08T01:33:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:36:44","now":"2026-01-08 01:36:16","skip":true}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:36:16","skip":true}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:41:14.808+08:00","last_settled":"2026-01-08T01:36:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:41:14","now":"2026-01-08 01:36:16","skip":true}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:44:44.807+08:00","last_settled":"2026-01-08T01:34:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:36:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:44:44","now":"2026-01-08 01:36:16","skip":true}
{"level":"debug","time":"2026-01-08 01:36:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:36:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:36:16","skip":true}
{"level":"debug","time":"2026-01-08 01:36:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:36:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:36:16","skip":true}
{"level":"debug","time":"2026-01-08 01:36:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T02:05:29.386+08:00","last_settled":"2026-01-08T01:35:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:36:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 02:05:29","now":"2026-01-08 01:36:16","skip":true}
{"level":"debug","time":"2026-01-08 01:36:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:36:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:36:16","skip":true}
{"level":"info","time":"2026-01-08 01:36:23","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:23","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:24","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:24","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:24","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:24","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:25","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:26","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:31","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: start=2026-01-08 00:00:00 +0800 CST, end=2026-01-08 23:59:59 +0800 CST, type=today","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:31","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:31","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"info","time":"2026-01-08 01:36:32","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: start=2026-01-01 01:36:32.749472 +0800 CST m=-604573.445416541, end=2026-01-08 01:36:32.749472 +0800 CST m=+226.554583459, type=7d","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:36:32","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:36:35","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:36:35","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:36:46","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:36:46"}
{"level":"debug","time":"2026-01-08 01:36:46","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:36:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:37:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:36:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:37:14","now":"2026-01-08 01:36:46","skip":true}
{"level":"debug","time":"2026-01-08 01:36:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:36:44.808+08:00","last_settled":"2026-01-08T01:33:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:36:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:36:44","now":"2026-01-08 01:36:46","skip":false}
{"level":"debug","time":"2026-01-08 01:36:46","caller":"logger/logger.go:315","msg":"定时开奖: 查询订单范围","domain":"mini-chat[fat]","id":65,"last":"2026-01-08 01:33:44","now":"2026-01-08 01:36:46"}
{"level":"debug","time":"2026-01-08 01:36:46","caller":"logger/logger.go:315","msg":"定时开奖: 查询到订单","domain":"mini-chat[fat]","id":65,"count":0,"min":1}
{"level":"info","time":"2026-01-08 01:36:46","caller":"logger/logger.go:309","msg":"定时开奖: 人数满足,开始开奖处理","domain":"mini-chat[fat]","id":65}
{"level":"info","time":"2026-01-08 01:36:46","caller":"logger/logger.go:309","msg":"定时开奖: 更新活动下次结算时间","domain":"mini-chat[fat]","id":65,"last":"2026-01-08 01:36:46","next":"2026-01-08 01:39:46"}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:36:46","skip":true}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:41:14.808+08:00","last_settled":"2026-01-08T01:36:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:41:14","now":"2026-01-08 01:36:46","skip":true}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:44:44.807+08:00","last_settled":"2026-01-08T01:34:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:44:44","now":"2026-01-08 01:36:46","skip":true}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:36:46","skip":true}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:36:46","skip":true}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T02:05:29.386+08:00","last_settled":"2026-01-08T01:35:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 02:05:29","now":"2026-01-08 01:36:46","skip":true}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:36:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:36:46","skip":true}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:37:16"}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:40:14.807+08:00","last_settled":"2026-01-08T01:37:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:40:14","now":"2026-01-08 01:37:16","skip":true}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:39:46.48+08:00","last_settled":"2026-01-08T01:36:46.48+08:00"}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:39:46","now":"2026-01-08 01:37:16","skip":true}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:37:16","skip":true}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:41:14.808+08:00","last_settled":"2026-01-08T01:36:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:41:14","now":"2026-01-08 01:37:16","skip":true}
{"level":"debug","time":"2026-01-08 01:37:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:44:44.807+08:00","last_settled":"2026-01-08T01:34:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:37:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:44:44","now":"2026-01-08 01:37:16","skip":true}
{"level":"debug","time":"2026-01-08 01:37:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:37:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:37:16","skip":true}
{"level":"debug","time":"2026-01-08 01:37:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:37:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:37:16","skip":true}
{"level":"debug","time":"2026-01-08 01:37:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T02:05:29.386+08:00","last_settled":"2026-01-08T01:35:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:37:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 02:05:29","now":"2026-01-08 01:37:16","skip":true}
{"level":"debug","time":"2026-01-08 01:37:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:37:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:37:16","skip":true}
{"level":"info","time":"2026-01-08 01:37:24","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: start=2026-01-01 01:37:24.748226 +0800 CST m=-604521.447255708, end=2026-01-08 01:37:24.748226 +0800 CST m=+278.552744292, type=7d","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:37:24","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:37:25","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: start=2026-01-01 01:37:25.231434 +0800 CST m=-604520.964047291, end=2026-01-08 01:37:25.231434 +0800 CST m=+279.035952709, type=7d","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:37:25","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:37:25","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: start=2026-01-01 01:37:25.639354 +0800 CST m=-604520.556125666, end=2026-01-08 01:37:25.639354 +0800 CST m=+279.443874334, type=7d","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:37:25","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
2026/01/08 01:37:28 /Users/win/aicode/bindbox/bindbox_game/internal/api/admin/dashboard_activity.go:269 Error 1054 (42S22): Unknown column 'products.image' in 'field list'
[16.963ms] [rows:-] SELECT
activity_draw_logs.id,
activity_draw_logs.user_id,
users.nickname,
users.avatar,
activity_reward_settings.product_id,
products.name as product_name,
products.image as product_image,
products.price as product_price,
orders.actual_amount as order_amount,
activity_draw_logs.created_at
FROM `activity_draw_logs` JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id LEFT JOIN users ON users.id = activity_draw_logs.user_id LEFT JOIN activity_reward_settings ON activity_reward_settings.id = activity_draw_logs.reward_id LEFT JOIN products ON products.id = activity_reward_settings.product_id LEFT JOIN orders ON orders.id = activity_draw_logs.order_id WHERE activity_issues.activity_id = 89 ORDER BY activity_draw_logs.id DESC LIMIT 10
{"level":"error","time":"2026-01-08 01:37:28","caller":"logger/logger.go:327","msg":"GetActivityLogs error: Error 1054 (42S22): Unknown column 'products.image' in 'field list'","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:37:35","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:37:40","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:37:40","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:37:46","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:37:46"}
{"level":"debug","time":"2026-01-08 01:37:46","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:37:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:40:14.807+08:00","last_settled":"2026-01-08T01:37:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:37:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:40:14","now":"2026-01-08 01:37:46","skip":true}
{"level":"debug","time":"2026-01-08 01:37:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:39:46.48+08:00","last_settled":"2026-01-08T01:36:46.48+08:00"}
{"level":"debug","time":"2026-01-08 01:37:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:39:46","now":"2026-01-08 01:37:46","skip":true}
{"level":"debug","time":"2026-01-08 01:37:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:37:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:37:46","skip":true}
{"level":"info","time":"2026-01-08 01:37:47","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:41:14.808+08:00","last_settled":"2026-01-08T01:36:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:41:14","now":"2026-01-08 01:37:46","skip":true}
{"level":"info","time":"2026-01-08 01:37:47","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:44:44.807+08:00","last_settled":"2026-01-08T01:34:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:44:44","now":"2026-01-08 01:37:46","skip":true}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:37:46","skip":true}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:37:46","skip":true}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T02:05:29.386+08:00","last_settled":"2026-01-08T01:35:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 02:05:29","now":"2026-01-08 01:37:46","skip":true}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:37:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:37:46","skip":true}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:38:16"}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:40:14.807+08:00","last_settled":"2026-01-08T01:37:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:40:14","now":"2026-01-08 01:38:16","skip":true}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:39:46.48+08:00","last_settled":"2026-01-08T01:36:46.48+08:00"}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:39:46","now":"2026-01-08 01:38:16","skip":true}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:38:16","skip":true}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:41:14.808+08:00","last_settled":"2026-01-08T01:36:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:41:14","now":"2026-01-08 01:38:16","skip":true}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:44:44.807+08:00","last_settled":"2026-01-08T01:34:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:44:44","now":"2026-01-08 01:38:16","skip":true}
{"level":"debug","time":"2026-01-08 01:38:16","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:38:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:38:16","skip":true}
{"level":"debug","time":"2026-01-08 01:38:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:38:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:38:16","skip":true}
{"level":"debug","time":"2026-01-08 01:38:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T02:05:29.386+08:00","last_settled":"2026-01-08T01:35:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:38:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 02:05:29","now":"2026-01-08 01:38:16","skip":true}
{"level":"debug","time":"2026-01-08 01:38:17","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:38:17","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:38:16","skip":true}
{"level":"info","time":"2026-01-08 01:38:40","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"info","time":"2026-01-08 01:38:44","caller":"logger/logger.go:309","msg":"SpendingLeaderboard range: ALL TIME","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:38:44","caller":"logger/logger.go:309","msg":"SpendingLeaderboard SQL done: count=0","domain":"mini-chat[fat]"}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:38:45","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:38:45","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:38:46"}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:40:14.807+08:00","last_settled":"2026-01-08T01:37:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:40:14","now":"2026-01-08 01:38:46","skip":true}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:39:46.48+08:00","last_settled":"2026-01-08T01:36:46.48+08:00"}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:39:46","now":"2026-01-08 01:38:46","skip":true}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:39:14.807+08:00","last_settled":"2026-01-08T01:34:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:39:14","now":"2026-01-08 01:38:46","skip":true}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:41:14.808+08:00","last_settled":"2026-01-08T01:36:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:41:14","now":"2026-01-08 01:38:46","skip":true}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:44:44.807+08:00","last_settled":"2026-01-08T01:34:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:38:46","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:44:44","now":"2026-01-08 01:38:46","skip":true}
{"level":"debug","time":"2026-01-08 01:38:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:38:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:38:46","skip":true}
{"level":"debug","time":"2026-01-08 01:38:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:40:44.808+08:00","last_settled":"2026-01-08T01:25:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:38:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:40:44","now":"2026-01-08 01:38:46","skip":true}
{"level":"debug","time":"2026-01-08 01:38:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T02:05:29.386+08:00","last_settled":"2026-01-08T01:35:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:38:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 02:05:29","now":"2026-01-08 01:38:46","skip":true}
{"level":"debug","time":"2026-01-08 01:38:47","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T02:27:59.386+08:00","last_settled":"2026-01-08T01:27:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:38:47","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 02:27:59","now":"2026-01-08 01:38:46","skip":true}

193
backend_debug_sql.log Normal file
View File

@ -0,0 +1,193 @@
{"level":"info","time":"2026-01-08 01:15:05","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
 ____ _ _ _ ____
 | __ ) (_) _ __ __| | | |__ ___ __ __ / ___| __ _ _ __ ___ ___
 | _ \ | | | '_ \ / _` | | '_ \ / _ \ \ \/ / | | _ / _` | | '_ ` _ \ / _ \
 | |_) | | | | | | | | (_| | | |_) | | (_) | > < | |_| | | (_| | | | | | | | | __/
 |____/ |_| |_| |_| \__,_| |_.__/ \___/ /_/\_\ \____| \__,_| |_| |_| |_| \___|
▌ 客户项目: 盲盒游戏
▌ 项目版本: Release-2025111111
▌ 启动时间: 2026-01-08 01:15:05
▌ 运行环境: darwin go1.24.2
▌ 服务端口: [:9991]
▌ 服务配置: [fat]
▌ 数据库连接: ✔ 已建立
{"level":"info","time":"2026-01-08 01:15:05","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:15:05","caller":"logger/logger.go:309","msg":"对对碰自动开奖: 后台任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:15:05","caller":"logger/logger.go:309","msg":"[抖店定时同步] 定时任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:15:05","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
{"level":"info","time":"2026-01-08 01:15:05","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
{"level":"info","time":"2026-01-08 01:15:05","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
{"level":"info","time":"2026-01-08 01:15:05","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
{"level":"info","time":"2026-01-08 01:15:05","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
{"level":"error","time":"2026-01-08 01:15:06","caller":"logger/logger.go:327","msg":"解密配置失败","domain":"mini-chat[fat]","key":"douyin.app_secret","error":"ciphertext is not a multiple of the block size"}
{"level":"info","time":"2026-01-08 01:15:06","caller":"logger/logger.go:309","msg":"动态配置加载完成","domain":"mini-chat[fat]","count":26}
2026/01/08 01:15:11 /Users/win/aicode/bindbox/bindbox_game/internal/api/admin/dashboard_spending.go:116
[22.453ms] [rows:0] SELECT
orders.user_id,
SUM(orders.actual_amount) as total_amount,
COUNT(orders.id) as order_count,
SUM(orders.discount_amount) as total_discount,
SUM(orders.points_amount) as total_points,
SUM(CASE WHEN orders.source_type = 4 THEN 1 ELSE 0 END) as game_pass_count,
SUM(CASE WHEN orders.item_card_id > 0 THEN 1 ELSE 0 END) as item_card_count,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN orders.actual_amount ELSE 0 END) as ichiban_spending,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN 1 ELSE 0 END) as ichiban_count,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN orders.actual_amount ELSE 0 END) as infinite_spending,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN 1 ELSE 0 END) as infinite_count,
SUM(CASE WHEN oa.play_type = 'matching' THEN orders.actual_amount ELSE 0 END) as matching_spending,
SUM(CASE WHEN oa.play_type = 'matching' THEN 1 ELSE 0 END) as matching_count
FROM `orders` LEFT JOIN (SELECT l.order_id, MAX(a.play_type) as play_type FROM activity_draw_logs l JOIN activity_issues i ON i.id = l.issue_id JOIN activities a ON a.id = i.activity_id WHERE l.created_at >= '2025-12-08 01:15:11.202' GROUP BY l.order_id) oa ON oa.order_id = orders.id WHERE orders.status = 2 AND orders.created_at >= '2025-12-09 01:15:11.202' AND orders.created_at <= '2026-01-08 01:15:11.202' GROUP BY `orders`.`user_id` ORDER BY total_amount DESC LIMIT 50
2026/01/08 01:15:13 /Users/win/aicode/bindbox/bindbox_game/internal/api/admin/dashboard_spending.go:116
[23.908ms] [rows:0] SELECT
orders.user_id,
SUM(orders.actual_amount) as total_amount,
COUNT(orders.id) as order_count,
SUM(orders.discount_amount) as total_discount,
SUM(orders.points_amount) as total_points,
SUM(CASE WHEN orders.source_type = 4 THEN 1 ELSE 0 END) as game_pass_count,
SUM(CASE WHEN orders.item_card_id > 0 THEN 1 ELSE 0 END) as item_card_count,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN orders.actual_amount ELSE 0 END) as ichiban_spending,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN 1 ELSE 0 END) as ichiban_count,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN orders.actual_amount ELSE 0 END) as infinite_spending,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN 1 ELSE 0 END) as infinite_count,
SUM(CASE WHEN oa.play_type = 'matching' THEN orders.actual_amount ELSE 0 END) as matching_spending,
SUM(CASE WHEN oa.play_type = 'matching' THEN 1 ELSE 0 END) as matching_count
FROM `orders` LEFT JOIN (SELECT l.order_id, MAX(a.play_type) as play_type FROM activity_draw_logs l JOIN activity_issues i ON i.id = l.issue_id JOIN activities a ON a.id = i.activity_id WHERE l.created_at >= '2025-12-08 01:15:13.54' GROUP BY l.order_id) oa ON oa.order_id = orders.id WHERE orders.status = 2 AND orders.created_at >= '2025-12-09 01:15:13.54' AND orders.created_at <= '2026-01-08 01:15:13.54' GROUP BY `orders`.`user_id` ORDER BY total_amount DESC LIMIT 50
2026/01/08 01:15:14 /Users/win/aicode/bindbox/bindbox_game/internal/api/admin/dashboard_spending.go:116
[21.376ms] [rows:0] SELECT
orders.user_id,
SUM(orders.actual_amount) as total_amount,
COUNT(orders.id) as order_count,
SUM(orders.discount_amount) as total_discount,
SUM(orders.points_amount) as total_points,
SUM(CASE WHEN orders.source_type = 4 THEN 1 ELSE 0 END) as game_pass_count,
SUM(CASE WHEN orders.item_card_id > 0 THEN 1 ELSE 0 END) as item_card_count,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN orders.actual_amount ELSE 0 END) as ichiban_spending,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN 1 ELSE 0 END) as ichiban_count,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN orders.actual_amount ELSE 0 END) as infinite_spending,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN 1 ELSE 0 END) as infinite_count,
SUM(CASE WHEN oa.play_type = 'matching' THEN orders.actual_amount ELSE 0 END) as matching_spending,
SUM(CASE WHEN oa.play_type = 'matching' THEN 1 ELSE 0 END) as matching_count
FROM `orders` LEFT JOIN (SELECT l.order_id, MAX(a.play_type) as play_type FROM activity_draw_logs l JOIN activity_issues i ON i.id = l.issue_id JOIN activities a ON a.id = i.activity_id WHERE l.created_at >= '2025-12-08 01:15:13.982' GROUP BY l.order_id) oa ON oa.order_id = orders.id WHERE orders.status = 2 AND orders.created_at >= '2025-12-09 01:15:13.982' AND orders.created_at <= '2026-01-08 01:15:13.982' GROUP BY `orders`.`user_id` ORDER BY total_amount DESC LIMIT 50
2026/01/08 01:15:14 /Users/win/aicode/bindbox/bindbox_game/internal/api/admin/dashboard_spending.go:116
[20.622ms] [rows:0] SELECT
orders.user_id,
SUM(orders.actual_amount) as total_amount,
COUNT(orders.id) as order_count,
SUM(orders.discount_amount) as total_discount,
SUM(orders.points_amount) as total_points,
SUM(CASE WHEN orders.source_type = 4 THEN 1 ELSE 0 END) as game_pass_count,
SUM(CASE WHEN orders.item_card_id > 0 THEN 1 ELSE 0 END) as item_card_count,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN orders.actual_amount ELSE 0 END) as ichiban_spending,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN 1 ELSE 0 END) as ichiban_count,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN orders.actual_amount ELSE 0 END) as infinite_spending,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN 1 ELSE 0 END) as infinite_count,
SUM(CASE WHEN oa.play_type = 'matching' THEN orders.actual_amount ELSE 0 END) as matching_spending,
SUM(CASE WHEN oa.play_type = 'matching' THEN 1 ELSE 0 END) as matching_count
FROM `orders` LEFT JOIN (SELECT l.order_id, MAX(a.play_type) as play_type FROM activity_draw_logs l JOIN activity_issues i ON i.id = l.issue_id JOIN activities a ON a.id = i.activity_id WHERE l.created_at >= '2025-12-08 01:15:14.549' GROUP BY l.order_id) oa ON oa.order_id = orders.id WHERE orders.status = 2 AND orders.created_at >= '2025-12-09 01:15:14.549' AND orders.created_at <= '2026-01-08 01:15:14.549' GROUP BY `orders`.`user_id` ORDER BY total_amount DESC LIMIT 50
{"level":"debug","time":"2026-01-08 01:15:35","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:15:35"}
{"level":"info","time":"2026-01-08 01:15:35","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
{"level":"debug","time":"2026-01-08 01:15:35","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:15:35","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:44.807+08:00","last_settled":"2026-01-08T01:12:44.807+08:00"}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:15:44","now":"2026-01-08 01:15:35","skip":true}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:18:29.385+08:00","last_settled":"2026-01-08T01:15:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:18:29","now":"2026-01-08 01:15:35","skip":true}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:18:44.808+08:00","last_settled":"2026-01-08T01:13:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:18:44","now":"2026-01-08 01:15:35","skip":true}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:15:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:15:29","now":"2026-01-08 01:15:35","skip":false}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 查询订单范围","domain":"mini-chat[fat]","id":79,"last":"2026-01-08 01:10:29","now":"2026-01-08 01:15:35"}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 查询到订单","domain":"mini-chat[fat]","id":79,"count":0,"min":1}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖-一番赏: 检查售罄","domain":"mini-chat[fat]","issue_id":86,"sold":0,"total":5}
{"level":"info","time":"2026-01-08 01:15:36","caller":"logger/logger.go:309","msg":"定时开奖-一番赏: 未售罄,执行全额退款","domain":"mini-chat[fat]","issue_id":86}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖-一番赏: 剩余未处理订单记录","domain":"mini-chat[fat]","issue_id":86,"count":0}
{"level":"info","time":"2026-01-08 01:15:36","caller":"logger/logger.go:309","msg":"定时开奖-一番赏: 格位已重置,新一轮可以开始","domain":"mini-chat[fat]","issue_id":86}
{"level":"info","time":"2026-01-08 01:15:36","caller":"logger/logger.go:309","msg":"定时开奖: 人数满足,开始开奖处理","domain":"mini-chat[fat]","id":79}
{"level":"info","time":"2026-01-08 01:15:36","caller":"logger/logger.go:309","msg":"定时开奖: 更新活动下次结算时间","domain":"mini-chat[fat]","id":79,"last":"2026-01-08 01:15:35","next":"2026-01-08 01:20:35"}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:24:29.385+08:00","last_settled":"2026-01-08T01:14:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:24:29","now":"2026-01-08 01:15:35","skip":true}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:15:35","skip":true}
{"level":"debug","time":"2026-01-08 01:15:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:15:37","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:15:35","skip":true}
{"level":"debug","time":"2026-01-08 01:15:37","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:15:37","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:15:35","skip":true}
{"level":"debug","time":"2026-01-08 01:15:37","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:15:37","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:15:35","skip":true}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:15:40","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:15:40","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:16:05","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:16:05"}
{"level":"debug","time":"2026-01-08 01:16:05","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:16:05","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:18:44.807+08:00","last_settled":"2026-01-08T01:15:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:18:44","now":"2026-01-08 01:16:05","skip":true}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:18:29.385+08:00","last_settled":"2026-01-08T01:15:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:18:29","now":"2026-01-08 01:16:05","skip":true}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:18:44.808+08:00","last_settled":"2026-01-08T01:13:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:18:44","now":"2026-01-08 01:16:05","skip":true}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:20:35.866+08:00","last_settled":"2026-01-08T01:15:35.866+08:00"}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:20:35","now":"2026-01-08 01:16:05","skip":true}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:24:29.385+08:00","last_settled":"2026-01-08T01:14:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:24:29","now":"2026-01-08 01:16:05","skip":true}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:16:05","skip":true}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:16:05","skip":true}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:16:05","skip":true}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:16:06","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:16:05","skip":true}
{"level":"debug","time":"2026-01-08 01:16:35","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:16:35"}
{"level":"debug","time":"2026-01-08 01:16:35","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:16:35","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:18:44.807+08:00","last_settled":"2026-01-08T01:15:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:18:44","now":"2026-01-08 01:16:35","skip":true}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:18:29.385+08:00","last_settled":"2026-01-08T01:15:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:18:29","now":"2026-01-08 01:16:35","skip":true}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:18:44.808+08:00","last_settled":"2026-01-08T01:13:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:18:44","now":"2026-01-08 01:16:35","skip":true}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:20:35.866+08:00","last_settled":"2026-01-08T01:15:35.866+08:00"}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:20:35","now":"2026-01-08 01:16:35","skip":true}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:24:29.385+08:00","last_settled":"2026-01-08T01:14:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:24:29","now":"2026-01-08 01:16:35","skip":true}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:16:35","skip":true}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:16:35","skip":true}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:16:35","skip":true}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:16:36","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:16:35","skip":true}
2026/01/08 01:16:38 /Users/win/aicode/bindbox/bindbox_game/internal/api/admin/dashboard_spending.go:116
[24.774ms] [rows:0] SELECT
orders.user_id,
SUM(orders.actual_amount) as total_amount,
COUNT(orders.id) as order_count,
SUM(orders.discount_amount) as total_discount,
SUM(orders.points_amount) as total_points,
SUM(CASE WHEN orders.source_type = 4 THEN 1 ELSE 0 END) as game_pass_count,
SUM(CASE WHEN orders.item_card_id > 0 THEN 1 ELSE 0 END) as item_card_count,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN orders.actual_amount ELSE 0 END) as ichiban_spending,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN 1 ELSE 0 END) as ichiban_count,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN orders.actual_amount ELSE 0 END) as infinite_spending,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN 1 ELSE 0 END) as infinite_count,
SUM(CASE WHEN oa.play_type = 'matching' THEN orders.actual_amount ELSE 0 END) as matching_spending,
SUM(CASE WHEN oa.play_type = 'matching' THEN 1 ELSE 0 END) as matching_count
FROM `orders` LEFT JOIN (SELECT l.order_id, MAX(a.play_type) as play_type FROM activity_draw_logs l JOIN activity_issues i ON i.id = l.issue_id JOIN activities a ON a.id = i.activity_id WHERE l.created_at >= '2025-12-31 01:16:38.634' GROUP BY l.order_id) oa ON oa.order_id = orders.id WHERE orders.status = 2 AND orders.created_at >= '2026-01-01 01:16:38.634' AND orders.created_at <= '2026-01-08 01:16:38.634' GROUP BY `orders`.`user_id` ORDER BY total_amount DESC LIMIT 50
{"level":"info","time":"2026-01-08 01:16:40","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:16:45","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:16:45","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}

4
backend_final.log Normal file
View File

@ -0,0 +1,4 @@
# bindbox-game/internal/api/admin
internal/api/admin/dashboard_spending.go:118:68: undefined: logger.Any
internal/api/admin/users_admin.go:112:58: undefined: logger.Any
internal/api/admin/users_admin.go:319:58: undefined: logger.Any

360
backend_fix_sql.log Normal file
View File

@ -0,0 +1,360 @@
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
 ____ _ _ _ ____
 | __ ) (_) _ __ __| | | |__ ___ __ __ / ___| __ _ _ __ ___ ___
 | _ \ | | | '_ \ / _` | | '_ \ / _ \ \ \/ / | | _ / _` | | '_ ` _ \ / _ \
 | |_) | | | | | | | | (_| | | |_) | | (_) | > < | |_| | | (_| | | | | | | | | __/
 |____/ |_| |_| |_| \__,_| |_.__/ \___/ /_/\_\ \____| \__,_| |_| |_| |_| \___|
▌ 客户项目: 盲盒游戏
▌ 项目版本: Release-2025111111
▌ 启动时间: 2026-01-08 01:03:00
▌ 运行环境: darwin go1.24.2
▌ 服务端口: [:9991]
▌ 服务配置: [fat]
▌ 数据库连接: ✔ 已建立
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"对对碰自动开奖: 后台任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"[抖店定时同步] 定时任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
{"level":"error","time":"2026-01-08 01:03:00","caller":"logger/logger.go:327","msg":"解密配置失败","domain":"mini-chat[fat]","key":"douyin.app_secret","error":"ciphertext is not a multiple of the block size"}
{"level":"info","time":"2026-01-08 01:03:00","caller":"logger/logger.go:309","msg":"动态配置加载完成","domain":"mini-chat[fat]","count":26}
{"level":"debug","time":"2026-01-08 01:03:30","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:03:30"}
{"level":"info","time":"2026-01-08 01:03:30","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"debug","time":"2026-01-08 01:03:30","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:03:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:06:14.807+08:00","last_settled":"2026-01-08T01:03:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:03:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:06:14","now":"2026-01-08 01:03:30","skip":true}
{"level":"debug","time":"2026-01-08 01:03:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:05:59.385+08:00","last_settled":"2026-01-08T01:02:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:03:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:05:59","now":"2026-01-08 01:03:30","skip":true}
{"level":"debug","time":"2026-01-08 01:03:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:03:44.807+08:00","last_settled":"2026-01-08T00:58:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:03:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:03:44","now":"2026-01-08 01:03:30","skip":true}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:05:14.807+08:00","last_settled":"2026-01-08T01:00:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:05:14","now":"2026-01-08 01:03:30","skip":true}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 01:03:30","skip":true}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:03:30","skip":true}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:03:30","skip":true}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 01:03:30","skip":true}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:03:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:03:30","skip":true}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:03:35","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:03:35","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:04:00","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:04:00"}
{"level":"debug","time":"2026-01-08 01:04:00","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:04:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:06:14.807+08:00","last_settled":"2026-01-08T01:03:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:06:14","now":"2026-01-08 01:04:00","skip":true}
{"level":"debug","time":"2026-01-08 01:04:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:05:59.385+08:00","last_settled":"2026-01-08T01:02:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:04:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:05:59","now":"2026-01-08 01:04:00","skip":true}
{"level":"debug","time":"2026-01-08 01:04:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:04:00","skip":true}
{"level":"debug","time":"2026-01-08 01:04:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:05:14.807+08:00","last_settled":"2026-01-08T01:00:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:05:14","now":"2026-01-08 01:04:00","skip":true}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 01:04:00","skip":true}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:04:00","skip":true}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:04:00","skip":true}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 01:04:00","skip":true}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:04:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:04:00","skip":true}
{"level":"debug","time":"2026-01-08 01:04:30","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:04:30"}
{"level":"debug","time":"2026-01-08 01:04:30","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:04:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:06:14.807+08:00","last_settled":"2026-01-08T01:03:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:06:14","now":"2026-01-08 01:04:30","skip":true}
{"level":"debug","time":"2026-01-08 01:04:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:05:59.385+08:00","last_settled":"2026-01-08T01:02:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:04:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:05:59","now":"2026-01-08 01:04:30","skip":true}
{"level":"debug","time":"2026-01-08 01:04:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:04:30","skip":true}
{"level":"debug","time":"2026-01-08 01:04:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:05:14.807+08:00","last_settled":"2026-01-08T01:00:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:05:14","now":"2026-01-08 01:04:30","skip":true}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:04:30","skip":true}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:04:30","skip":true}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:04:30","skip":true}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 01:04:30","skip":true}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:04:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:04:30","skip":true}
{"level":"info","time":"2026-01-08 01:04:35","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:04:40","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:04:40","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:05:00","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:05:00"}
{"level":"debug","time":"2026-01-08 01:05:00","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:05:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:06:14.807+08:00","last_settled":"2026-01-08T01:03:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:05:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:06:14","now":"2026-01-08 01:05:00","skip":true}
{"level":"debug","time":"2026-01-08 01:05:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:05:59.385+08:00","last_settled":"2026-01-08T01:02:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:05:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:05:59","now":"2026-01-08 01:05:00","skip":true}
{"level":"debug","time":"2026-01-08 01:05:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:05:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:05:00","skip":true}
{"level":"debug","time":"2026-01-08 01:05:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:05:14.807+08:00","last_settled":"2026-01-08T01:00:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:05:14","now":"2026-01-08 01:05:00","skip":true}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:05:00","skip":true}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:05:00","skip":true}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:05:00","skip":true}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 01:05:00","skip":true}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:05:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:05:00","skip":true}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:05:30"}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:06:14.807+08:00","last_settled":"2026-01-08T01:03:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:06:14","now":"2026-01-08 01:05:30","skip":true}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:05:59.385+08:00","last_settled":"2026-01-08T01:02:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:05:59","now":"2026-01-08 01:05:30","skip":true}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:05:30","skip":true}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:05:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:05:30","skip":true}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:05:30","skip":true}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:05:30","skip":true}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:05:30","skip":true}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:05:30","skip":true}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:05:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:05:30","skip":true}
{"level":"info","time":"2026-01-08 01:05:40","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:05:45","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:05:45","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:06:00"}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:06:14.807+08:00","last_settled":"2026-01-08T01:03:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:06:14","now":"2026-01-08 01:06:00","skip":true}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:08:59.386+08:00","last_settled":"2026-01-08T01:05:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:08:59","now":"2026-01-08 01:06:00","skip":true}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:06:00","skip":true}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:06:00","skip":true}
{"level":"debug","time":"2026-01-08 01:06:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:06:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:06:00","skip":true}
{"level":"debug","time":"2026-01-08 01:06:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:06:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:06:00","skip":true}
{"level":"debug","time":"2026-01-08 01:06:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:06:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:06:00","skip":true}
{"level":"debug","time":"2026-01-08 01:06:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:06:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:06:00","skip":true}
{"level":"debug","time":"2026-01-08 01:06:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:06:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:06:00","skip":true}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:06:30"}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:09:14.808+08:00","last_settled":"2026-01-08T01:06:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:09:14","now":"2026-01-08 01:06:30","skip":true}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:08:59.386+08:00","last_settled":"2026-01-08T01:05:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:08:59","now":"2026-01-08 01:06:30","skip":true}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:06:30","skip":true}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:06:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:06:30","skip":true}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:06:30","skip":true}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:06:30","skip":true}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:06:30","skip":true}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:06:30","skip":true}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:06:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:06:30","skip":true}
{"level":"info","time":"2026-01-08 01:06:45","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:06:50","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:06:50","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:07:00"}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:09:14.808+08:00","last_settled":"2026-01-08T01:06:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:09:14","now":"2026-01-08 01:07:00","skip":true}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:08:59.386+08:00","last_settled":"2026-01-08T01:05:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:08:59","now":"2026-01-08 01:07:00","skip":true}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:07:00","skip":true}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:07:00","skip":true}
{"level":"debug","time":"2026-01-08 01:07:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:07:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:07:00","skip":true}
{"level":"debug","time":"2026-01-08 01:07:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:07:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:07:00","skip":true}
{"level":"debug","time":"2026-01-08 01:07:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:07:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:07:00","skip":true}
{"level":"debug","time":"2026-01-08 01:07:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:07:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:07:00","skip":true}
{"level":"debug","time":"2026-01-08 01:07:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:07:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:07:00","skip":true}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:07:30"}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:09:14.808+08:00","last_settled":"2026-01-08T01:06:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:09:14","now":"2026-01-08 01:07:30","skip":true}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:08:59.386+08:00","last_settled":"2026-01-08T01:05:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:08:59","now":"2026-01-08 01:07:30","skip":true}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:07:30","skip":true}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:07:30","skip":true}
{"level":"debug","time":"2026-01-08 01:07:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:07:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:07:30","skip":true}
{"level":"debug","time":"2026-01-08 01:07:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:07:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:07:30","skip":true}
{"level":"debug","time":"2026-01-08 01:07:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:07:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:07:30","skip":true}
{"level":"debug","time":"2026-01-08 01:07:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:07:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:07:30","skip":true}
{"level":"debug","time":"2026-01-08 01:07:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:07:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:07:30","skip":true}
{"level":"info","time":"2026-01-08 01:07:50","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:07:54","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:07:54","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:08:00","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:08:00"}
{"level":"debug","time":"2026-01-08 01:08:00","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:08:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:09:14.808+08:00","last_settled":"2026-01-08T01:06:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:08:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:09:14","now":"2026-01-08 01:08:00","skip":true}
{"level":"debug","time":"2026-01-08 01:08:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:08:59.386+08:00","last_settled":"2026-01-08T01:05:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:08:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:08:59","now":"2026-01-08 01:08:00","skip":true}
{"level":"debug","time":"2026-01-08 01:08:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:08:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:08:00","skip":true}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:08:00","skip":true}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:08:00","skip":true}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:08:00","skip":true}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:08:00","skip":true}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:08:00","skip":true}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:08:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:08:00","skip":true}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:08:30"}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:09:14.808+08:00","last_settled":"2026-01-08T01:06:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:09:14","now":"2026-01-08 01:08:30","skip":true}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:08:59.386+08:00","last_settled":"2026-01-08T01:05:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:08:59","now":"2026-01-08 01:08:30","skip":true}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:08:44.807+08:00","last_settled":"2026-01-08T01:03:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:08:44","now":"2026-01-08 01:08:30","skip":true}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:08:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:08:30","skip":true}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:08:30","skip":true}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:08:30","skip":true}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:08:30","skip":true}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:08:30","skip":true}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:08:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:08:30","skip":true}
{"level":"info","time":"2026-01-08 01:08:54","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:08:59","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:08:59","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:09:00","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:09:00"}
{"level":"debug","time":"2026-01-08 01:09:00","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:09:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:09:14.808+08:00","last_settled":"2026-01-08T01:06:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:09:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:09:14","now":"2026-01-08 01:09:00","skip":true}
{"level":"debug","time":"2026-01-08 01:09:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:08:59.386+08:00","last_settled":"2026-01-08T01:05:59.386+08:00"}
{"level":"debug","time":"2026-01-08 01:09:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:08:59","now":"2026-01-08 01:09:00","skip":false}
{"level":"debug","time":"2026-01-08 01:09:00","caller":"logger/logger.go:315","msg":"定时开奖: 查询订单范围","domain":"mini-chat[fat]","id":65,"last":"2026-01-08 01:05:59","now":"2026-01-08 01:09:00"}
{"level":"debug","time":"2026-01-08 01:09:00","caller":"logger/logger.go:315","msg":"定时开奖: 查询到订单","domain":"mini-chat[fat]","id":65,"count":0,"min":1}
{"level":"info","time":"2026-01-08 01:09:00","caller":"logger/logger.go:309","msg":"定时开奖: 人数满足,开始开奖处理","domain":"mini-chat[fat]","id":65}
{"level":"info","time":"2026-01-08 01:09:00","caller":"logger/logger.go:309","msg":"定时开奖: 更新活动下次结算时间","domain":"mini-chat[fat]","id":65,"last":"2026-01-08 01:09:00","next":"2026-01-08 01:12:00"}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:13:44.807+08:00","last_settled":"2026-01-08T01:08:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:13:44","now":"2026-01-08 01:09:00","skip":true}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:09:00","skip":true}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:09:00","skip":true}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:09:00","skip":true}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:09:00","skip":true}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:09:00","skip":true}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:09:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:09:00","skip":true}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:09:30"}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:12:29.386+08:00","last_settled":"2026-01-08T01:09:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:12:29","now":"2026-01-08 01:09:30","skip":true}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:12:00.509+08:00","last_settled":"2026-01-08T01:09:00.509+08:00"}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:12:00","now":"2026-01-08 01:09:30","skip":true}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:13:44.807+08:00","last_settled":"2026-01-08T01:08:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:13:44","now":"2026-01-08 01:09:30","skip":true}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:09:30","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:09:30","skip":true}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:09:30","skip":true}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:09:30","skip":true}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:09:30","skip":true}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:09:30","skip":true}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:09:31","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:09:30","skip":true}
{"level":"info","time":"2026-01-08 01:09:59","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:10:00"}
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:12:29.386+08:00","last_settled":"2026-01-08T01:09:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:12:29","now":"2026-01-08 01:10:00","skip":true}
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:12:00.509+08:00","last_settled":"2026-01-08T01:09:00.509+08:00"}
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:12:00","now":"2026-01-08 01:10:00","skip":true}
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:13:44.807+08:00","last_settled":"2026-01-08T01:08:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:13:44","now":"2026-01-08 01:10:00","skip":true}
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:10:14.808+08:00","last_settled":"2026-01-08T01:05:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:10:00","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:10:00","skip":true}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:10:00","skip":true}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:10:00","skip":true}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:10:00","skip":true}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:10:00","skip":true}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:10:01","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:10:00","skip":true}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:10:04","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:10:04","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
signal: killed

195
backend_new.log Normal file
View File

@ -0,0 +1,195 @@
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
 ____ _ _ _ ____
 | __ ) (_) _ __ __| | | |__ ___ __ __ / ___| __ _ _ __ ___ ___
 | _ \ | | | '_ \ / _` | | '_ \ / _ \ \ \/ / | | _ / _` | | '_ ` _ \ / _ \
 | |_) | | | | | | | | (_| | | |_) | | (_) | > < | |_| | | (_| | | | | | | | | __/
 |____/ |_| |_| |_| \__,_| |_.__/ \___/ /_/\_\ \____| \__,_| |_| |_| |_| \___|
▌ 客户项目: 盲盒游戏
▌ 项目版本: Release-2025111111
▌ 启动时间: 2026-01-08 00:56:40
▌ 运行环境: darwin go1.24.2
▌ 服务端口: [:9991]
▌ 服务配置: [fat]
▌ 数据库连接: ✔ 已建立
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"对对碰自动开奖: 后台任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"[抖店定时同步] 定时任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
{"level":"error","time":"2026-01-08 00:56:40","caller":"logger/logger.go:327","msg":"解密配置失败","domain":"mini-chat[fat]","key":"douyin.app_secret","error":"ciphertext is not a multiple of the block size"}
{"level":"info","time":"2026-01-08 00:56:40","caller":"logger/logger.go:309","msg":"动态配置加载完成","domain":"mini-chat[fat]","count":26}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 00:57:10"}
{"level":"info","time":"2026-01-08 00:57:10","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T00:56:59.386+08:00","last_settled":"2026-01-08T00:53:59.386+08:00"}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 00:56:59","now":"2026-01-08 00:57:10","skip":false}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 查询订单范围","domain":"mini-chat[fat]","id":52,"last":"2026-01-08 00:53:59","now":"2026-01-08 00:57:10"}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 查询到订单","domain":"mini-chat[fat]","id":52,"count":0,"min":1}
{"level":"info","time":"2026-01-08 00:57:10","caller":"logger/logger.go:309","msg":"定时开奖: 人数满足,开始开奖处理","domain":"mini-chat[fat]","id":52}
{"level":"info","time":"2026-01-08 00:57:10","caller":"logger/logger.go:309","msg":"定时开奖: 更新活动下次结算时间","domain":"mini-chat[fat]","id":52,"last":"2026-01-08 00:57:10","next":"2026-01-08 01:00:10"}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T00:59:44.808+08:00","last_settled":"2026-01-08T00:56:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 00:59:44","now":"2026-01-08 00:57:10","skip":true}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T00:58:44.807+08:00","last_settled":"2026-01-08T00:53:44.807+08:00"}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 00:58:44","now":"2026-01-08 00:57:10","skip":true}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:00:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:00:14","now":"2026-01-08 00:57:10","skip":true}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 00:57:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 00:57:10","skip":true}
{"level":"debug","time":"2026-01-08 00:57:11","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:57:11","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:57:10","skip":true}
{"level":"debug","time":"2026-01-08 00:57:11","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:57:11","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:57:10","skip":true}
{"level":"debug","time":"2026-01-08 00:57:11","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 00:57:11","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 00:57:10","skip":true}
{"level":"debug","time":"2026-01-08 00:57:11","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:57:11","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 00:57:10","skip":true}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 00:57:14","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 00:57:14","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 00:57:40"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:00:10.204+08:00","last_settled":"2026-01-08T00:57:10.204+08:00"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:00:10","now":"2026-01-08 00:57:40","skip":true}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T00:59:44.808+08:00","last_settled":"2026-01-08T00:56:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 00:59:44","now":"2026-01-08 00:57:40","skip":true}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T00:58:44.807+08:00","last_settled":"2026-01-08T00:53:44.807+08:00"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 00:58:44","now":"2026-01-08 00:57:40","skip":true}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:00:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:00:14","now":"2026-01-08 00:57:40","skip":true}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 00:57:40","skip":true}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:57:40","skip":true}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:57:40","skip":true}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 00:57:40","skip":true}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:57:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 00:57:40","skip":true}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 00:58:10"}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:00:10.204+08:00","last_settled":"2026-01-08T00:57:10.204+08:00"}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:00:10","now":"2026-01-08 00:58:10","skip":true}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T00:59:44.808+08:00","last_settled":"2026-01-08T00:56:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 00:59:44","now":"2026-01-08 00:58:10","skip":true}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T00:58:44.807+08:00","last_settled":"2026-01-08T00:53:44.807+08:00"}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 00:58:44","now":"2026-01-08 00:58:10","skip":true}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:00:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:00:14","now":"2026-01-08 00:58:10","skip":true}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 00:58:10","skip":true}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:58:10","skip":true}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:58:10","skip":true}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 00:58:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 00:58:10","skip":true}
{"level":"debug","time":"2026-01-08 00:58:11","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:58:11","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 00:58:10","skip":true}
{"level":"info","time":"2026-01-08 00:58:14","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 00:58:19","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 00:58:19","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 00:58:40"}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:00:10.204+08:00","last_settled":"2026-01-08T00:57:10.204+08:00"}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:00:10","now":"2026-01-08 00:58:40","skip":true}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T00:59:44.808+08:00","last_settled":"2026-01-08T00:56:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 00:59:44","now":"2026-01-08 00:58:40","skip":true}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T00:58:44.807+08:00","last_settled":"2026-01-08T00:53:44.807+08:00"}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 00:58:44","now":"2026-01-08 00:58:40","skip":true}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:00:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:00:14","now":"2026-01-08 00:58:40","skip":true}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 00:58:40","skip":true}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:58:40","skip":true}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:58:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:58:40","skip":true}
{"level":"debug","time":"2026-01-08 00:58:41","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 00:58:41","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 00:58:40","skip":true}
{"level":"debug","time":"2026-01-08 00:58:41","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:58:41","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 00:58:40","skip":true}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 00:59:10"}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:00:10.204+08:00","last_settled":"2026-01-08T00:57:10.204+08:00"}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:00:10","now":"2026-01-08 00:59:10","skip":true}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T00:59:44.808+08:00","last_settled":"2026-01-08T00:56:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 00:59:44","now":"2026-01-08 00:59:10","skip":true}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:03:44.807+08:00","last_settled":"2026-01-08T00:58:44.807+08:00"}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:03:44","now":"2026-01-08 00:59:10","skip":true}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:00:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:00:14","now":"2026-01-08 00:59:10","skip":true}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 00:59:10","skip":true}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:59:10","skip":true}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:59:10","skip":true}
{"level":"debug","time":"2026-01-08 00:59:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 00:59:11","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 00:59:10","skip":true}
{"level":"debug","time":"2026-01-08 00:59:11","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:59:11","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 00:59:10","skip":true}
{"level":"info","time":"2026-01-08 00:59:19","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 00:59:24","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 00:59:24","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 00:59:40"}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:00:10.204+08:00","last_settled":"2026-01-08T00:57:10.204+08:00"}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:00:10","now":"2026-01-08 00:59:40","skip":true}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T00:59:44.808+08:00","last_settled":"2026-01-08T00:56:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 00:59:44","now":"2026-01-08 00:59:40","skip":true}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:03:44.807+08:00","last_settled":"2026-01-08T00:58:44.807+08:00"}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:03:44","now":"2026-01-08 00:59:40","skip":true}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:00:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:00:14","now":"2026-01-08 00:59:40","skip":true}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 00:59:40","skip":true}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:59:40","skip":true}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 00:59:40","skip":true}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 00:59:40","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 00:59:40","skip":true}
{"level":"debug","time":"2026-01-08 00:59:41","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 00:59:41","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 00:59:40","skip":true}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:00:10"}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:00:10.204+08:00","last_settled":"2026-01-08T00:57:10.204+08:00"}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:00:10","now":"2026-01-08 01:00:10","skip":true}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:02:59.385+08:00","last_settled":"2026-01-08T00:59:59.385+08:00"}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:02:59","now":"2026-01-08 01:00:10","skip":true}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:03:44.807+08:00","last_settled":"2026-01-08T00:58:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:03:44","now":"2026-01-08 01:00:10","skip":true}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:00:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:00:14","now":"2026-01-08 01:00:10","skip":true}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:04:18.148+08:00","last_settled":"2026-01-08T00:54:18.148+08:00"}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:04:18","now":"2026-01-08 01:00:10","skip":true}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:00:10","skip":true}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:10:14.807+08:00","last_settled":"2026-01-08T00:55:14.807+08:00"}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:10:14","now":"2026-01-08 01:00:10","skip":true}
{"level":"debug","time":"2026-01-08 01:00:10","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:05:26.489+08:00","last_settled":"2026-01-08T00:35:26.489+08:00"}
{"level":"debug","time":"2026-01-08 01:00:11","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:05:26","now":"2026-01-08 01:00:10","skip":true}
{"level":"debug","time":"2026-01-08 01:00:11","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:00:11","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:00:10","skip":true}
{"level":"info","time":"2026-01-08 01:00:24","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
signal: killed

147
backend_prod.log Normal file
View File

@ -0,0 +1,147 @@
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"Connected to Redis","domain":"mini-chat[fat]","addr":"118.25.13.43:8379"}
 ____ _ _ _ ____
 | __ ) (_) _ __ __| | | |__ ___ __ __ / ___| __ _ _ __ ___ ___
 | _ \ | | | '_ \ / _` | | '_ \ / _ \ \ \/ / | | _ / _` | | '_ ` _ \ / _ \
 | |_) | | | | | | | | (_| | | |_) | | (_) | > < | |_| | | (_| | | | | | | | | __/
 |____/ |_| |_| |_| \__,_| |_.__/ \___/ /_/\_\ \____| \__,_| |_| |_| |_| \___|
▌ 客户项目: 盲盒游戏
▌ 项目版本: Release-2025111111
▌ 启动时间: 2026-01-08 01:11:58
▌ 运行环境: darwin go1.24.2
▌ 服务端口: [:9991]
▌ 服务配置: [fat]
▌ 数据库连接: ✔ 已建立
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"Task center worker started","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"对对碰自动开奖: 后台任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"[抖店定时同步] 定时任务已启动","domain":"mini-chat[fat]"}
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":4}
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":1}
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":2}
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":0}
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"Worker routine started","domain":"mini-chat[fat]","worker_id":3}
{"level":"error","time":"2026-01-08 01:11:58","caller":"logger/logger.go:327","msg":"解密配置失败","domain":"mini-chat[fat]","key":"douyin.app_secret","error":"ciphertext is not a multiple of the block size"}
{"level":"info","time":"2026-01-08 01:11:58","caller":"logger/logger.go:309","msg":"动态配置加载完成","domain":"mini-chat[fat]","count":26}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:12:28"}
{"level":"info","time":"2026-01-08 01:12:28","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:12:29.386+08:00","last_settled":"2026-01-08T01:09:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:12:29","now":"2026-01-08 01:12:28","skip":true}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:14.808+08:00","last_settled":"2026-01-08T01:12:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:15:14","now":"2026-01-08 01:12:28","skip":true}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:13:44.807+08:00","last_settled":"2026-01-08T01:08:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:13:44","now":"2026-01-08 01:12:28","skip":true}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:15:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:15:29","now":"2026-01-08 01:12:28","skip":true}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:12:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:12:28","skip":true}
{"level":"debug","time":"2026-01-08 01:12:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:12:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:12:28","skip":true}
{"level":"debug","time":"2026-01-08 01:12:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:12:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:12:28","skip":true}
{"level":"debug","time":"2026-01-08 01:12:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:12:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:12:28","skip":true}
{"level":"debug","time":"2026-01-08 01:12:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:12:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:12:28","skip":true}
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:12:32","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:12:32","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:12:58"}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:44.807+08:00","last_settled":"2026-01-08T01:12:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:15:44","now":"2026-01-08 01:12:58","skip":true}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:14.808+08:00","last_settled":"2026-01-08T01:12:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:15:14","now":"2026-01-08 01:12:58","skip":true}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:13:44.807+08:00","last_settled":"2026-01-08T01:08:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:13:44","now":"2026-01-08 01:12:58","skip":true}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:15:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:15:29","now":"2026-01-08 01:12:58","skip":true}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:12:58","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:12:58","skip":true}
{"level":"debug","time":"2026-01-08 01:12:59","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:12:59","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:12:58","skip":true}
{"level":"debug","time":"2026-01-08 01:12:59","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:12:59","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:12:58","skip":true}
{"level":"debug","time":"2026-01-08 01:12:59","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:12:59","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:12:58","skip":true}
{"level":"debug","time":"2026-01-08 01:12:59","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:12:59","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:12:58","skip":true}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:13:28"}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:44.807+08:00","last_settled":"2026-01-08T01:12:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:15:44","now":"2026-01-08 01:13:28","skip":true}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:14.808+08:00","last_settled":"2026-01-08T01:12:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:15:14","now":"2026-01-08 01:13:28","skip":true}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:13:44.807+08:00","last_settled":"2026-01-08T01:08:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:13:44","now":"2026-01-08 01:13:28","skip":true}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:15:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:15:29","now":"2026-01-08 01:13:28","skip":true}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:13:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:13:28","skip":true}
{"level":"debug","time":"2026-01-08 01:13:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:13:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:13:28","skip":true}
{"level":"debug","time":"2026-01-08 01:13:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:13:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:13:28","skip":true}
{"level":"debug","time":"2026-01-08 01:13:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:13:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:13:28","skip":true}
{"level":"debug","time":"2026-01-08 01:13:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:13:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:13:28","skip":true}
{"level":"info","time":"2026-01-08 01:13:32","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:13:37","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:13:37","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:13:58"}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:44.807+08:00","last_settled":"2026-01-08T01:12:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:15:44","now":"2026-01-08 01:13:58","skip":true}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:14.808+08:00","last_settled":"2026-01-08T01:12:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:15:14","now":"2026-01-08 01:13:58","skip":true}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:18:44.808+08:00","last_settled":"2026-01-08T01:13:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:18:44","now":"2026-01-08 01:13:58","skip":true}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:15:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:15:29","now":"2026-01-08 01:13:58","skip":true}
{"level":"debug","time":"2026-01-08 01:13:58","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:13:59","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:13:58","skip":true}
{"level":"debug","time":"2026-01-08 01:13:59","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:13:59","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:13:58","skip":true}
{"level":"debug","time":"2026-01-08 01:13:59","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:13:59","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:13:58","skip":true}
{"level":"debug","time":"2026-01-08 01:13:59","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:13:59","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:13:58","skip":true}
{"level":"debug","time":"2026-01-08 01:13:59","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:13:59","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:13:58","skip":true}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 开始检查","domain":"mini-chat[fat]","now":"2026-01-08 01:14:28"}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 查询到活动","domain":"mini-chat[fat]","count":9}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":52,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:44.807+08:00","last_settled":"2026-01-08T01:12:44.807+08:00"}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":52,"st":"2026-01-08 01:15:44","now":"2026-01-08 01:14:28","skip":true}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":65,"play_type":"ichiban","interval":3,"scheduled_time":"2026-01-08T01:15:14.808+08:00","last_settled":"2026-01-08T01:12:14.808+08:00"}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":65,"st":"2026-01-08 01:15:14","now":"2026-01-08 01:14:28","skip":true}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":77,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:18:44.808+08:00","last_settled":"2026-01-08T01:13:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":77,"st":"2026-01-08 01:18:44","now":"2026-01-08 01:14:28","skip":true}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":79,"play_type":"ichiban","interval":5,"scheduled_time":"2026-01-08T01:15:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":79,"st":"2026-01-08 01:15:29","now":"2026-01-08 01:14:28","skip":true}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":80,"play_type":"ichiban","interval":10,"scheduled_time":"2026-01-08T01:14:29.385+08:00","last_settled":"2026-01-08T01:04:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:14:28","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":80,"st":"2026-01-08 01:14:29","now":"2026-01-08 01:14:28","skip":true}
{"level":"debug","time":"2026-01-08 01:14:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":81,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:14:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":81,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:14:28","skip":true}
{"level":"debug","time":"2026-01-08 01:14:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":82,"play_type":"ichiban","interval":15,"scheduled_time":"2026-01-08T01:25:29.386+08:00","last_settled":"2026-01-08T01:10:29.386+08:00"}
{"level":"debug","time":"2026-01-08 01:14:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":82,"st":"2026-01-08 01:25:29","now":"2026-01-08 01:14:28","skip":true}
{"level":"debug","time":"2026-01-08 01:14:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":83,"play_type":"ichiban","interval":30,"scheduled_time":"2026-01-08T01:35:29.385+08:00","last_settled":"2026-01-08T01:05:29.385+08:00"}
{"level":"debug","time":"2026-01-08 01:14:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":83,"st":"2026-01-08 01:35:29","now":"2026-01-08 01:14:28","skip":true}
{"level":"debug","time":"2026-01-08 01:14:29","caller":"logger/logger.go:315","msg":"定时开奖: 检查活动","domain":"mini-chat[fat]","id":84,"play_type":"ichiban","interval":60,"scheduled_time":"2026-01-08T01:27:44.808+08:00","last_settled":"2026-01-08T00:27:44.808+08:00"}
{"level":"debug","time":"2026-01-08 01:14:29","caller":"logger/logger.go:315","msg":"定时开奖: 计算开奖时间","domain":"mini-chat[fat]","id":84,"st":"2026-01-08 01:27:44","now":"2026-01-08 01:14:28","skip":true}
{"level":"info","time":"2026-01-08 01:14:37","caller":"logger/logger.go:309","msg":"[抖店定时同步] 开始同步","domain":"mini-chat[fat]","interval_minutes":1}
[DEBUG] 开始全量同步,共 3 个绑定用户
[DEBUG] 正在同步用户 ID: 9018 (昵称: 现实的迪斯蒂法诺, 抖音号: wyl0423333) 的订单...
[DEBUG] 正在同步用户 ID: 9019 (昵称: 约翰掐指一算, 抖音号: xrw200947752) 的订单...
[DEBUG] 正在同步用户 ID: 9047 (昵称: 巴乔横扫六合, 抖音号: 请输入您的抖店订单号即可完成绑定) 的订单...
{"level":"info","time":"2026-01-08 01:14:42","caller":"logger/logger.go:309","msg":"[抖店同步] 全量同步完成","domain":"mini-chat[fat]","users_count":3,"total_fetched":45,"new_orders":0,"matched_users":45}
{"level":"info","time":"2026-01-08 01:14:42","caller":"logger/logger.go:309","msg":"[抖店定时同步] 同步成功","domain":"mini-chat[fat]","total_fetched":45,"new_orders":0,"matched_users":45}

1
check_config.sql Normal file
View File

@ -0,0 +1 @@
SELECT config_key, config_value FROM sys_configs WHERE config_key = 'wechat_miniprogram_lottery_result_template_id';

View File

@ -0,0 +1,74 @@
package main
import (
"fmt"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// 简单的 struct 映射
type Orders struct {
ID int64
OrderNo string
SourceType int32
Status int32
Remark string
UserID int64
TotalAmount int64
ActualAmount int64
CreatedAt time.Time
}
type IssuePositionClaims struct {
ID int64
IssueID int64
SlotIndex int64
OrderID int64
UserID int64
}
// 表名映射
func (Orders) TableName() string { return "orders" }
func (IssuePositionClaims) TableName() string { return "issue_position_claims" }
func main() {
// 尝试使用 config 中的密码
dsn := "root:api2api..@tcp(sh-cynosdbmysql-grp-88th45wy.sql.tencentcdb.com:28555)/bindbox_game?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database: " + err.Error())
}
targetOrderNo := "O20260107092217073"
var order Orders
if err := db.Where("order_no = ?", targetOrderNo).First(&order).Error; err != nil {
fmt.Printf("Error finding order: %v\n", err)
return
}
fmt.Printf("========== Order Info ==========\n")
fmt.Printf("ID: %d\n", order.ID)
fmt.Printf("OrderNo: %s\n", order.OrderNo)
fmt.Printf("UserID: %d\n", order.UserID)
fmt.Printf("SourceType: %d\n", order.SourceType)
fmt.Printf("Status: %d (2=Paid)\n", order.Status)
fmt.Printf("Amount: Total=%d, Actual=%d\n", order.TotalAmount, order.ActualAmount)
fmt.Printf("Remark Length: %d\n", len(order.Remark))
fmt.Printf("Remark Content: %s\n", order.Remark)
fmt.Printf("================================\n")
var claims []IssuePositionClaims
if err := db.Where("order_id = ?", order.ID).Find(&claims).Error; err != nil {
fmt.Printf("Error checking claims: %v\n", err)
}
fmt.Printf("========== Claims Info ==========\n")
fmt.Printf("Claims Count: %d\n", len(claims))
for _, c := range claims {
fmt.Printf("- Claim: SlotIndex=%d IssueID=%d\n", c.SlotIndex, c.IssueID)
}
fmt.Printf("=================================\n")
}

View File

@ -89,6 +89,11 @@ type Config struct {
AppSecret string `mapstructure:"app_secret" toml:"app_secret"` AppSecret string `mapstructure:"app_secret" toml:"app_secret"`
NotifyURL string `mapstructure:"notify_url" toml:"notify_url"` NotifyURL string `mapstructure:"notify_url" toml:"notify_url"`
} `mapstructure:"douyin" toml:"douyin"` } `mapstructure:"douyin" toml:"douyin"`
Otel struct {
Enabled bool `mapstructure:"enabled" toml:"enabled"`
Endpoint string `mapstructure:"endpoint" toml:"endpoint"`
} `mapstructure:"otel" toml:"otel"`
} }
var ( var (
@ -166,6 +171,38 @@ func init() {
if v := os.Getenv("ALIYUN_SMS_TEMPLATE_CODE"); v != "" { if v := os.Getenv("ALIYUN_SMS_TEMPLATE_CODE"); v != "" {
config.AliyunSMS.TemplateCode = v config.AliyunSMS.TemplateCode = v
} }
// MySQL 配置环境变量覆盖
if v := os.Getenv("MYSQL_ADDR"); v != "" {
config.MySQL.Read.Addr = v
config.MySQL.Write.Addr = v
}
if v := os.Getenv("MYSQL_READ_ADDR"); v != "" {
config.MySQL.Read.Addr = v
}
if v := os.Getenv("MYSQL_WRITE_ADDR"); v != "" {
config.MySQL.Write.Addr = v
}
if v := os.Getenv("MYSQL_USER"); v != "" {
config.MySQL.Read.User = v
config.MySQL.Write.User = v
}
if v := os.Getenv("MYSQL_PASS"); v != "" {
config.MySQL.Read.Pass = v
config.MySQL.Write.Pass = v
}
if v := os.Getenv("MYSQL_NAME"); v != "" {
config.MySQL.Read.Name = v
config.MySQL.Write.Name = v
}
// Redis 配置环境变量覆盖
if v := os.Getenv("REDIS_ADDR"); v != "" {
config.Redis.Addr = v
}
if v := os.Getenv("REDIS_PASS"); v != "" {
config.Redis.Pass = v
}
} }
func Get() Config { func Get() Config {

View File

@ -1,42 +1,60 @@
[mysql] [language]
[mysql.read] local = 'zh-cn'
addr = "127.0.0.1:3306"
user = "root" [mysql.read]
pass = "123456" addr = "mysql:3306"
name = "bindbox_game" user = "root"
[mysql.write] pass = "bindbox2025kdy"
addr = "127.0.0.1:3306" name = "bindbox_game"
user = "root"
pass = "123456" [mysql.write]
name = "bindbox_game" addr = "mysql:3306"
user = "root"
pass = "bindbox2025kdy"
name = "bindbox_game"
[redis] [redis]
addr = "127.0.0.1:6379" addr = "redis:6379"
pass = "" pass = ""
db = 0 db = 0
[jwt] [jwt]
admin_secret = "m9ycX9RTPyuYTWw9FrCc" admin_secret = "m9ycX9RTPyuYTWw9FrCc"
patient_secret = "AppUserJwtSecret2025" patient_secret = "AppUserJwtSecret2025"
[random]
commit_master_key = "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456"
[wechat] [wechat]
app_id = "" app_id = "wx26ad074017e1e63f"
app_secret = "" app_secret = "026c19ce4f3bb090c56573024c59a8be"
lottery_result_template_id = "" lottery_result_template_id = "O2eqJQD3pn-vQ6g2z9DWzINVwOmPoz8yW-172J_YcpI"
[cos]
bucket = "keaiya-1259195914"
region = "ap-shanghai"
secret_id = "AKIDtjPtAFPNDuR1UnxvoUCoRAnJgw164Zv6"
secret_key = "B0vvjMoMsKcipnJlLnFyWt6A2JRSJ0Wr"
base_url = ""
[random]
commit_master_key = "4d7a3b8f9c2e1a5d6b4f8c0e3a7d2b1c6f9e4a5d8c1b3f7a2e5d6c4b8f0e3a7d2b1c"
[wechatpay] [wechatpay]
mchid = "" mchid = "1610439635"
serial_no = "" serial_no = "3AFD505D597831F8E931EBFFEEB5976B81F66F03"
private_key_path = "" private_key_path = "./configs/cert/apiclient_key.pem"
api_v3_key = "" api_v3_key = "3tbwEFZV3fZtOslpUJC7Sacb8qjzhm05"
notify_url = "https://example.com/api/pay/wechat/notify" notify_url = "https://kdy.1024tool.vip/api/pay/wechat/notify"
public_key_id = "PUB_KEY_ID_0116104396352025041000211519001600"
public_key_path = "./configs/cert/pub_key.pem"
[aliyun_sms] [aliyun_sms]
access_key_id = "" access_key_id = ""
access_key_secret = "" access_key_secret = ""
sign_name = "" sign_name = ""
template_code = "" template_code = ""
[internal]
api_key = "bindbox-internal-secret-2024"
[otel]
enabled = true
endpoint = "tempo:4318"

View File

@ -0,0 +1,47 @@
# 任务后台Go项目接入 Loki 成本评估与实施方案 (ALIGNMENT) - 性能分析版
## 1. 核心问题:性能消耗分析
用户疑问:**"Loki + Promtail + Grafana 这个组合消耗性能么"**
### 1.1 结论先行
在**极简模式**(只采服务端日志)下,**消耗非常低**。
对于现代开发机(或 2核4G 以上服务器),**几乎无感**。
### 1.2 详细资源开销预估 (以每天产生 100MB 日志为例)
| 组件 | 作用 | 内存 (RAM) | CPU (平时) | CPU (查询时) | 磁盘 IO | 评价 |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| **Promtail** | 搬运工 | ~50MB | < 1% | - | 极低 (仅读取) | **极轻**就像运行了一个 `tail -f` 命令 |
| **Loki** | 存储 | ~150MB - 300MB | < 2% | 10% - 30% | (顺序写入) | **轻量**不像 ES 那样建全文索引只压缩存储不做繁重计算 |
| **Grafana** | 界面 | ~100MB - 200MB | 0% (Idle) | 5% - 10% | 忽略不计 | **静默**,没人访问网页时几乎不干活 |
| **总计** | **全套** | **约 400MB - 600MB** | **日常忽略不计** | **查询时微升** | **可控** | **安全** |
### 1.3 为什么这么省?
1. **不建全文索引**: Loki 的设计哲学是 "Log everything, index only labels"。它不像 Elasticsearch (ELK) 那样对每个词都建索引那样极耗内存和CPU。Loki 只索引 "时间" 和 "标签" (如 `app=bindbox-game`)。
2. **流式压缩**: 日志被压缩成块存储,占用很少写 IO。
3. **按需计算**: 只有当你发起查询(比如搜 "error"CPU 才会工作去解压和匹配数据。平时它只是在静静地写文件。
### 1.4 极端情况
- **甚至可以更省**: 我们可以限制 docker 容器的内存上限。
- **对比 ELK**: ELK 动辄需要 2G-4G 起步内存Loki 是专为云原生和低资源环境设计的轻量级替代品。
---
## 2. 极简实施方案 (Server Logs Only)
### 2.1 架构调整
- **Log Source**: 仅采集 `bindbox-game` 容器。
- **采集过滤**: 在 Promtail 配置中设置 Filter**丢弃** 所有非 `bindbox-game` 的日志。
### 2.2 资源进一步优化
- 由于只采集核心服务Loki 和 Promtail 的 CPU/内存消耗将降至最低。
- 磁盘占用将非常小。
---
## 3. 实施步骤
1. **配置 Promtail**: 编写 `promtail-config.yaml`,只监听 `container_name="bindbox-game"`
2. **配置 Docker Compose**: 添加 Loki, Promtail, Grafana 服务。
3. **配置 Grafana**: 预配置好 Loki 数据源,开箱即用。
## 4. 结论
可以放心接入。这套组合主要消耗的是**几百兆内存**,对 CPU 和磁盘的影响极小,完全适合本地开发和中小型服务器部署。

View File

@ -0,0 +1,67 @@
# 架构设计:统一积分与元比例 (Design)
## 1. 总体架构
本次改造主要涉及 **计算逻辑层的标准化****配置数据的动态化**,不涉及大规模架构重构。
核心思路:**后端收敛计算逻辑,前端收敛展示逻辑**。
```mermaid
graph TD
A[Admin User] -->|配置 ExchangeRate| B(Admin Panel)
B -->|API: upsertSystemConfig| C(Backend API)
C -->|Update| D[(MySQL: sys_configs)]
E[App User] -->|Action: Pay/Refund| F(Backend Service)
F -->|Read Config| D
F -->|Call| G{pkg/points/convert}
G -->|Calculate based on Rate| F
F -->|Save Integer Points| D
H[Mini App] -->|Read Points| F
H -->|Display Raw Integer| I[UI Display]
```
## 2. 模块设计
### 2.1 后端 (`bindbox_game`)
- **`internal/pkg/points`**: 核心计算包。
- 职责:提供基于汇率的 `Cents <-> Points` 转换函数。
- 变更:
- `CentsToPoints(cents, rate)`: 逻辑改为 `cents * rate / 100`
- `PointsToCents(points, rate)`: 逻辑改为 `points * 100 / rate`
- **`internal/service/user`**: 业务服务层。
- 职责:在积分变动(增加、扣减、退款)时,从 `sys_configs` 读取最新 `points_exchange_rate` 并传入计算包。
- 变更:检查所有调用点,确保不再硬编码。
### 2.2 数据库 (`MySQL`)
- **Schema**: 无变更。
- **Data Migration**:
- 需执行数据清洗,将现有的积分数值 `x` 更新为 `x / 100` (假设之前是按分存的)。
- **Risk**: 需要用户确认是否执行此 SQL。**默认提供 SQL 但不自动运行**。
### 2.3 管理后台 (`web/admin`)
- **SystemConfigs**:
- 新增 "积分配置" Section。
- Key: `points_exchange_rate`
- 验证:必须为正整数,默认为 1。
### 2.4 小程序 (`bindbox-mini`)
- **Utils**:
- `formatPoints`: 移除除以 100 的逻辑,仅保留千分位格式化。
- **Pages**:
- 检查积分明细、下单抵扣、商品兑换等页面的展示。
- **Vue Filters/Formatters**: 全局搜索使用 `/ 100` 展示积分的地方进行替换。
## 3. 接口规范
- **API**: `POST /admin/system/configs` (现有)
- Payload: `{ "key": "points_exchange_rate", "value": "1" }`
## 4. 迁移策略
1. **停机维护** (建议): 防止数据在迁移过程中变动。
2. **代码部署**: 部署新版后端(新算法)。
3. **数据清洗**:
```sql
-- 假设所有用户的积分都需要缩小 100 倍以适配新算法
UPDATE user_points SET points = FLOOR(points / 100);
UPDATE user_points_ledger SET points = FLOOR(points / 100);
```
4. **验证**: 检查某测试账号积分是否符合预期。

View File

@ -0,0 +1,45 @@
# 任务任务:统一积分与元比例 (Task List)
## 0. 预备工作
- [ ] **确认数据备份**: 提醒用户备份数据库。
- [ ] **代码同步**: 确保本地代码是最新的。
## 1. 后端改造 (Backend)
- [ ] **修改核心计算包 (`internal/pkg/points/convert.go`)**
- [ ] 修改 `CentsToPoints``(cents * rate) / 100`
- [ ] 修改 `PointsToCents``(points * 100) / rate`
- [ ] 修改 `RefundPointsAmount` 适配新公式
- [ ] **业务逻辑适配 (`internal/service/user`)**
- [ ] 检查 `points_convert.go` 中的 `CentsToPoints` 调用,确保读取配置 Key `points_exchange_rate` (或复用旧 Key 但明确含义)。
- [ ] 检查 `points_consume.go` 等文件,确保无其他硬编码。
- [ ] **单元测试**
- [ ] 运行 `internal/pkg/points` 的测试,确保 100 分钱在 Rate=1 时转为 1 积分。
## 2. 管理后台改造 (Admin Frontend)
- [ ] **更新系统配置页 (`web/admin/src/views/system/configs/index.vue`)**
- [ ] 新增 "积分配置" 卡片。
- [ ] 添加 `points_exchange_rate` 编辑项。
- [ ] 添加说明: "1元 = ? 积分"。
## 3. 小程序改造 (Mini Program)
- [ ] **全局搜索积分展示**
- [ ] 搜索 `/ 100``* 0.01` 相关的积分代码。
- [ ] **修复工具函数**
- [ ] 修改 `utils/format.js` 或类似文件中的 `formatPoints`
- [ ] **修复页面展示**
- [ ] `pages-user/points/index.vue` (积分明细)
- [ ] `pages-user/orders/detail.vue` (如果有积分抵扣展示)
- [ ] 其他涉及积分展示的页面。
## 4. 数据迁移 (Migration)
- [ ] **提供 SQL 脚本**
- [ ] 存入 `docs/standardize_points_ratio/migration.sql`
- [ ] **(Optional) 执行迁移**
- [ ] 根据用户指示决定是否执行。
## 5. 验证 (Verification)
- [ ] **后端验证**
- [ ] 重启服务。
- [ ] 模拟支付 1 元,查看数据库增加 1 积分。
- [ ] **前端验证**
- [ ] 查看积分列表,显示为 1 (而不是 0.01 或 100)。

View File

@ -0,0 +1,62 @@
-- Standardize Points Ratio Migration Script
-- Purpose: Convert point values from "1 Yuan = 100 Points" (Cents) to "1 Yuan = 1 Point" (Integer).
-- Target Tables: user_points, user_points_ledger, orders.
BEGIN;
-- 1. Update User Points Current Balance
-- Description: Reduce all current point balances by factor of 100.
UPDATE user_points
SET points = FLOOR(points / 100);
-- 2. Update User Points Ledger (History)
-- Description: Adjust historical records to reflect the new unit.
UPDATE user_points_ledger
SET points = FLOOR(points / 100);
-- 3. Update Orders Points Usage
-- Description: Adjust recorded points usage in orders.
UPDATE orders
SET points_amount = FLOOR(points_amount / 100)
WHERE points_amount > 0;
-- 4. Update Task Center Rewards (Points)
-- Description: Adjust configured point rewards in Task Center.
-- Note: Logic prioritizes 'points' in JSON payload, then 'quantity'.
-- Updating 'quantity' is safe. Updating JSON is complex in standard SQL without knowing exact structure/version.
-- Assuming simple structure {"points": 100} or similar.
-- 4a. Update Quantity for type 'points'
UPDATE task_center_task_rewards
SET quantity = FLOOR(quantity / 100)
WHERE reward_type = 'points' AND quantity >= 100;
-- 4b. Update RewardPayload? (Optional/Manual)
-- Warning: Modifying JSON string requires robust parsing.
-- Providing a best-effort text replacement for simple cases {"points": 100} -> {"points": 1}
-- This relies on the pattern '"points": ' followed by numbers.
-- Recommendation: Manually verify Task Center configurations in Admin Panel after migration.
-- 5. System Config Update (Values)
-- Description: Convert known config values if they represent points.
-- Example: 'register_points', 'daily_sign_in_points' (if they exist).
-- This attempts to update common point-related configs.
UPDATE system_configs
SET config_value = FLOOR(config_value / 100)
WHERE config_key IN ('register_reward_points', 'daily_sign_in_reward_points') AND config_value REGEXP '^[0-9]+$';
-- 6. System Config Update (Exchange Rate)
-- Description: Ensure the new config key is set.
INSERT INTO system_configs (config_key, config_value, remark, created_at, updated_at)
VALUES ('points_exchange_rate', '1', '1元对应多少积分', NOW(), NOW())
ON DUPLICATE KEY UPDATE config_value = '1';
COMMIT;
-- NOTE on 'user_inventory':
-- Some points data might be embedded in 'remark' field (e.g. '|redeemed_points=100').
-- Updating these embedded strings via SQL is complex and risky.
-- Refunds for OLD items (redeemed before migration) might fail or deduct excessive points
-- if this field is not updated.
-- Recommendation: Handle 'refund' logic gracefully for legacy items in code if possible,
-- or accept that old items cannot be refunded automatically.

View File

@ -0,0 +1,52 @@
# Walkthrough: Standardize Points Ratio
## 1. Goal
Standardize the Points-to-Yuan ratio to **1 Yuan = 1 Point**, and remove the decimal display logic (previous 100 Points = 1 Yuan) across the system.
## 2. Changes
### Backend (`bindbox_game`)
- **Core Logic (`internal/pkg/points/convert.go`)**:
- Updated `CentsToPoints` to `(cents * rate) / 100`.
- Updated `PointsToCents` to `(points * 100) / rate`.
- Added unit tests to verify 100 Cents = 1 Point.
- **Service Layer (`internal/service/user`)**:
- Updated `points_convert.go` to use `points_exchange_rate` config key.
- Added `PointsToCents` method for reverse calculation.
- Updated `lottery_app.go` to use `PointsToCents` for accurate deduction calculation.
- **DB Config**:
- Expects `points_exchange_rate` (default 1) instead of `points_exchange_per_cent`.
### Admin Frontend (`web/admin`)
- **System Configs**:
- Added "Points Configuration" section.
- Allows setting "1 Yuan = N Points" (Default 1).
### Mini-Program Frontend Display Logic
- **Goal**: Ensure Points are displayed as integers and values are consistent.
- **Files Modified**:
- `pages-user/points/index.vue`
- `pages-user/orders/detail.vue`
- `pages-user/tasks/index.vue`
- `pages/shop/index.vue` (Fixed `/ 100` division for points)
- `pages-shop/shop/detail.vue` (Fixed `/ 100` division for points)
- **Changes**:
- Removed incorrect `/ 100` division for Points display while keeping it for Money (Yuan) display.
- formated points to use `.toFixed(0)` to remove decimal places.
### Database Migration
- **Script**: `docs/standardize_points_ratio/migration.sql`
- **Action Required**: Run this script to shrink existing point values by 100x to match the new 1:1 definition.
## 3. Verification
- **Unit Tests**:
- `go test internal/pkg/points/...` passed.
- **Manual Check**:
- `TestCentsToPoints` confirmed 100 Cents -> 1 Point.
- `TestRefundPointsAmount` confirmed proportional refund works with integer points.
## 4. Next Steps for User
1. **Backup Database**.
2. **Deploy Backend & Admin**.
3. **Run Migration Script** (`docs/standardize_points_ratio/migration.sql`).
4. **Deploy Mini Program**.

View File

@ -0,0 +1,46 @@
# 任务:修复一番赏次数卡支付与退款逻辑
## 1. 项目上下文分析
- **项目**: BindBox (Blind Box / Ichiban Kuji Game Platform)
- **技术栈**: Go (Backend), Vue3/UniApp (Frontend)
- **涉及模块**:
- 前端:一番赏活动页 (`bindbox-mini/pages-activity/activity/yifanshang/index.vue`)
- 后端:抽奖接口 (`internal/api/activity/lottery_app.go`)
- 后端:退款接口 (`internal/api/admin/pay_refund_admin.go`)
## 2. 需求理解与确认
### 原始需求
1. **不掉支付**: 使用次数卡Count Card进行一番赏抽奖时不应拉起微信支付因为金额应为0
2. **退款退卡**: 对使用次数卡支付的订单进行退款时应退还次数卡次数而非退款金额因为金额为0或无操作。
### 问题分析
通过代码审查,发现以下问题:
1. **前端支付逻辑缺陷**: `index.vue` 在调用 `joinLottery` 后,未根据返回的订单状态或金额判断是否需要支付,而是无条件调用 `createWechatOrder` 并拉起支付。
2. **后端抽奖逻辑缺陷**: `lottery_app.go` 在使用次数卡扣费时,虽然将 `ActualAmount` 设为 0但在 `order.Remark` 中仅记录了 `use_game_pass`,未记录具体使用的次数卡 ID 和扣除数量。
3. **后端退款逻辑缺陷**: `pay_refund_admin.go` 在退款时尝试从 `order.Remark` 解析 `game_pass:ID`,但由于上述原因无法解析。且当前逻辑仅尝试恢复 `remaining + 1`未考虑到一单多抽Count > 1的情况。
## 3. 智能决策策略
### 决策点 1前端如何判断跳过支付
- **方案**: 检查 `joinLottery` 返回的 `actual_amount``status`
- **依据**: `lottery_app.go` 中,若金额为 0`Status` 会被置为 2 (Paid),且 `ActualAmount` 为 0。
- **实现**: 若 `res.actual_amount === 0``res.status === 2`,则直接进入抽奖结果展示流程。
### 决策点 2后端如何记录次数卡使用情况
- **方案**: 修改 `lottery_app.go`,在 Deduction 循环中记录所有使用的 Pass ID 和对应扣除数。
- **格式建议**: `gp_use:ID1:Count1|gp_use:ID2:Count2`
- **兼容性**: 需确保不破坏现有 Remark 的其他信息。
### 决策点 3退款如何恢复次数
- **方案**: 修改 `pay_refund_admin.go`,解析新的 Remark 格式。
- **逻辑**: 遍历所有记录的 ID按记录的扣除数执行 `UPDATE user_game_passes SET remaining = remaining + ?, total_used = total_used - ? WHERE id = ?`
## 4. 最终共识 (Consensus)
### 任务边界
1. **Frontend**: 修改 `yifanshang/index.vue``onPaymentConfirm` 方法。
2. **Backend**: 修改 `lottery_app.go` 记录 Game Pass Usage。
3. **Backend**: 修改 `pay_refund_admin.go` 实现精准的次数卡退还。
### 验收标准
1. **支付测试**: 使用次数卡购买一番赏(单抽或多抽),前端不弹出微信支付,直接显示抽奖结果。
2. **数据验证**: 数据库 `orders` 表的 `remark` 字段包含具体的次数卡使用记录(如 `gp_use:101:5`)。
3. **退款测试**: 对该订单执行全额退款对应的次数卡ID 101`remaining` 增加 5`total_used` 减少 5。

View File

@ -0,0 +1,43 @@
# 共识:修复一番赏次数卡支付与退款逻辑
## 1. 需求描述与验收标准
### 需求描述
- **不掉支付**: 使用次数卡支付时若订单金额为0前端不应拉起微信支付直接视为支付成功。
- **退款退卡**: 次数卡支付的订单退款时,需准确退还使用的次数卡次数。
### 验收标准
1. **前端支付流程**:
- [ ] 使用次数卡全额抵扣时,点击“去支付”后直接弹出成功/翻牌界面,无微信支付弹窗。
- [ ] 混合支付(次数卡不足补差价)时,仍正常拉起支付(本次主要关注全额抵扣)。
2. **后端订单记录**:
- [ ] 数据库 `orders.remark` 字段正确记录 `gp_use:<ID>:<Count>`
- [ ] 支持单次使用多张次数卡的情况(如 `gp_use:101:5|gp_use:102:1`)。
3. **退款流程**:
- [ ] 对次数卡订单执行退款,对应的 `user_game_passes` 记录 `remaining` 增加,`total_used` 减少。
- [ ] 退还数量与扣除数量完全一致。
- [ ] 退款流水清晰。
## 2. 技术实现方案
### 前端 (Mini-Program)
- 修改 `bindbox-mini/pages-activity/activity/yifanshang/index.vue`
- 在 `onPaymentConfirm` 中,接收 `joinLottery` 响应后,判断 `order.ActualAmount == 0``Status == 2`
- 若满足,直接调用 `onPaymentSuccess` 或模拟成功逻辑,跳过 `createWechatOrder`
### 后端 (Go)
- **抽奖 (Lottery)**:
- 修改 `internal/api/activity/lottery_app.go`
- 在扣除 Game Pass 的循环中,动态构建 Remark 字符串,记录每个 Pass 的扣除量。
- **退款 (Refund)**:
- 修改 `internal/api/admin/pay_refund_admin.go`
- 升级 Remark 解析逻辑,支持 `gp_use:ID:Count` 格式。
- 遍历所有使用的 Pass逐一执行恢复 SQL。
## 3. 任务边界限制
- 仅针对一番赏业务Ichiban
- 仅针对 Game Pass (次数卡) 支付方式。
- 不涉及优惠券退款逻辑变更(除非受全额退款逻辑影响,需确保兼容)。
## 4. 确认所有不确定性已解决
- 已确认当前 Frontend 无条件拉起支付是 Bug。
- 已确认当前 Backend 未记录详细 Pass ID 是导致无法精确退款的原因。
- 已确认 Refund 逻辑需升级以支持多卡/多数量退还。

View File

@ -0,0 +1,85 @@
# 设计:修复一番赏次数卡支付与退款逻辑
## 1. 整体架构与流程
### 支付流程 (Modified)
```mermaid
sequenceDiagram
participant User
participant Frontend (Vue)
participant Backend (API)
participant DB
User->>Frontend: 选择一番赏格位,点击支付 (使用次数卡)
Frontend->>Backend: API: /api/app/lottery/join (use_game_pass=true)
Backend->>DB: Check Game Pass Balance
Backend->>DB: Deduct Game Pass (Remaining - N)
Backend->>DB: Create Order (Amount=0, Status=2, Remark="gp_use:ID:N")
Backend-->>Frontend: Response (Success, Amount=0, Status=2)
alt Amount == 0
Frontend->>Frontend: Skip WeChat Pay
Frontend->>Frontend: Show Result / Flip Cards
else Amount > 0
Frontend->>Backend: Create WeChat Order
Frontend->>WeChat: Request Payment
end
```
### 退款流程 (Modified)
```mermaid
sequenceDiagram
participant Admin
participant Backend (Refund API)
participant DB
Admin->>Backend: API: /api/admin/refund (OrderNo)
Backend->>DB: Get Order & Remark
Backend->>Backend: Parse Remark for "gp_use:ID:Count" pairs
loop For Each Pass
Backend->>DB: Update user_game_passes SET remaining+=Count
end
Backend->>DB: Update Order Status = Refunded
Backend-->>Admin: Success
```
## 2. 核心组件设计
### 2.1 Lottery App (`lottery_app.go`)
- **Logic**: In `JoinLottery` handler, inside the transaction where `useGamePass` is true.
- **Change**:
```go
// Before
deducted += canDeduct
// After
deducted += canDeduct
gamePassUsage = append(gamePassUsage, fmt.Sprintf("gp_use:%d:%d", p.ID, canDeduct))
...
order.Remark += "|" + strings.Join(gamePassUsage, "|")
```
### 2.2 Refund Admin (`pay_refund_admin.go`)
- **Logic**: In `CreateRefund`.
- **Change**:
- Remove simple regex `game_pass:(\d+)`.
- Add loop to find all `gp_use:(\d+):(\d+)`.
- Execute restoration for each match.
### 2.3 Frontend (`index.vue`)
- **Logic**: `onPaymentConfirm`.
- **Change**:
```javascript
if (joinResult.actual_amount === 0 || joinResult.status === 2 || joinResult.status === 'paid') {
// Direct Success
onPaymentSuccess({ result: lotteryResult })
return
}
```
## 3. 接口契约
- 无新增接口。
- `JoinLottery` 响应保持不变,前端需利用现有的 `actual_amount``status` 字段。
## 4. 异常处理
- **Refund Partial Failure**: Cannot easily happen in transaction. If one update fails, whole refund fails.
- **Legacy Orders**: Old orders have `use_game_pass` but no `gp_use:ID`. Refund logic should fallback to old behavior (try to find 1 pass or just warn/skip).
- *Fallback Strategy*: If no `gp_use` found but `use_game_pass` is present, log warning or try to restore 1 count to *any* valid pass of user?
- *Decision*: Since user specifically asked for this fix, we assume it's for FUTURE/NEW orders or current testing. For legacy orders, we can leave as is or try best effort. Given "Strict" requirement, we will implement the new logic. Legacy fallback: Scan `game_pass:ID` (old format if any?) - wait, old code didn't write ID at all. So legacy orders cannot be automatically restored safely. This is acceptable for a "Fix".

View File

@ -0,0 +1,28 @@
# 项目总结报告:修复一番赏次数卡支付与退款逻辑
## 1. 任务概述
本任务旨在修复“一番赏”业务中使用次数卡Game Pass支付时前端仍拉起微信支付的问题以及退款时未能正确退还次数卡次数的问题。
## 2. 完成情况
### 2.1 需求实现
- [x] **前端支付优化**: `yifanshang/index.vue` 已增加判断逻辑,当 `ActualAmount == 0``Status == 2` 时,直接跳过微信支付流程,进入开奖结果查询。
- [x] **后端记录优化**: `lottery_app.go` 现在会在订单 `Remark` 中以 `gp_use:ID:Count` 格式记录具体的次数卡使用明细。
- [x] **后端退款优化**: `pay_refund_admin.go` 已支持解析新的 `gp_use` 格式,并能准确恢复多张卡、多数量的消耗。同时保留了对旧格式 `game_pass:ID` 的兼容支持。
### 2.2 代码变更
- `internal/api/activity/lottery_app.go`: 记录 Game Pass 扣除明细。
- `internal/api/admin/pay_refund_admin.go`: 增强退款逻辑,支持多卡恢复。
- `bindbox-mini/pages-activity/activity/yifanshang/index.vue`: 优化支付流程,支持 0 元订单。
## 3. 质量评估
- **编译通过**: 后端代码 `go build` 成功,无语法错误。
- **逻辑完备性**:
- 覆盖了“不掉支付”的核心诉求。
- 覆盖了“精准退卡”的核心诉求。
- 考虑了新旧数据格式兼容性。
- **风险控制**:
- 仅针对 `Ichiban` 逻辑生效,不影响其他业务。
- 前端改动范围局限于支付确认回调,风险可控。
## 4. 交付结论
以完成所有关键路径的修复,代码已准备就绪,可以部署测试。

View File

@ -0,0 +1,40 @@
# 任务拆解:修复一番赏次数卡支付与退款逻辑
## 1. Backend Tasks
### 1.1 Update Lottery Logic (Atomize)
- **File**: `internal/api/activity/lottery_app.go`
- **Goal**: Record specific Game Pass usage in Order Remark.
- **Steps**:
- Locate `JoinLottery` function.
- Inside `useGamePass` block, accumulate used pass IDs and counts.
- Append formatted string `gp_use:ID:Count` to `order.Remark`.
- **Verification**: Run local test, buy with count card, check DB `orders` table remark.
### 1.2 Update Refund Logic (Atomize)
- **File**: `internal/api/admin/pay_refund_admin.go`
- **Goal**: Parse `gp_use` and restore counts.
- **Steps**:
- Locate `CreateRefund` function.
- Replace/Extend existing Game Pass restoration logic.
- Implement regex to find all `gp_use:(\d+):(\d+)`.
- Loop and execute SQL updates.
- **Verification**: Create order with count card (using 1.1), then call refund API, check `user_game_passes` table restoration.
## 2. Frontend Tasks
### 2.1 Update Payment Flow (Atomize)
- **File**: `bindbox-mini/pages-activity/activity/yifanshang/index.vue`
- **Goal**: Skip WeChat Pay for 0-amount orders.
- **Steps**:
- Locate `onPaymentConfirm`.
- After `joinLottery` returns, check `joinResult.actual_amount === 0` or `status`.
- If true, directly call logic to show results (e.g. `onPaymentSuccess` or equivalent logic to flip cards).
- Ensure `getLotteryResult` is called (which is already there in logic, just need to skip `createWechatOrder`).
- **Verification**: UI test with count card.
## 3. Dependency Graph
```mermaid
graph TD
B1[Backend: Lottery Logic] --> B2[Backend: Refund Logic]
B1 --> F1[Frontend: Payment Flow]
```
(B2 and F1 can be parallel, but B1 is prerequisite for B2 to be testable with new data).

View File

@ -0,0 +1,12 @@
# 待办事项:一番赏次数卡修复后续
## 1. 测试与验证
- [ ] **真机验证**: 需要在真机小程序环境测试使用次数卡购买一番赏,确认是否直接跳过支付。
- [ ] **退款验证**: 需要在管理后台对生成的次数卡订单进行退款,并检查数据库 `user_game_passes` 表确认次数是否正确恢复。
## 2. 遗留/潜在问题
- [ ] **历史订单处理**: 此修复仅对新生成的订单生效(因为旧订单缺少 `gp_use:ID:Count` 记录)。旧订单退款仍将使用旧逻辑(仅退 1 次)。如果需要处理大量历史订单的批量退款,建议通过 SQL 手动修复或编写一次性脚本。
- [ ] **多端兼容**: 目前仅修改了 `bindbox-mini`(小程序端)。如果存在 App 端或 H5 端,需确认是否复用相同逻辑或需要单独修改。
## 3. 配置建议
- 无新增配置项。

View File

@ -0,0 +1,95 @@
# 玩家管理Bug修复 - 需求对齐
## 项目上下文
- **前端**: Vue 3 + TypeScript + Element Plus
- **后端**: Go (Gin框架)
- **关键文件**:
- 玩家列表: `web/admin/src/views/player-manage/index.vue`
- 分页Hook: `web/admin/src/hooks/core/useTable.ts`
- 用户详情抽屉: `web/admin/src/views/player-manage/modules/player-detail-drawer.vue`
- 用户盈亏图表: `web/admin/src/views/player-manage/modules/player-profit-loss-chart.vue`
- 活动分析抽屉: `web/admin/src/views/activity/manage/components/ActivityAnalysisDrawer.vue`
---
## Bug列表
### Bug 1: 玩家列表分页失效
**现象**: 点击下一页后会自动跳回第1页
**初步分析**:
- 玩家列表使用 `useTable` hook 管理分页
- `handleCurrentChange` 函数会修改 `pagination.current` 并调用 `getData`
- 可能原因:
1. `handleSearch` 函数在搜索时重置了页码但没有正确更新
2. `getDataDebounced` 使用的参数可能覆盖了新的页码值
3. 搜索参数和分页参数同步问题
**需要确认**: 具体是在什么场景下触发?是否有搜索条件?
---
### Bug 2: 活动的游戏盈亏仪表盘
**现象描述不清,需要澄清**
**可能的理解**:
1. 需要在仪表盘Dashboard添加活动的游戏盈亏分析组件
2. 现有的 `ActivityAnalysisDrawer` 有问题需要修复?
3. 需要一个全局的活动盈亏汇总仪表盘?
**当前现有功能**:
- `ActivityAnalysisDrawer.vue`: 单个活动的数据分析抽屉,包含总营收、总成本、毛利润、参与人数等
**需要澄清**: 具体需要什么功能?是新增组件还是修复现有问题?
---
### Bug 3: 用户盈亏分析需要明细
**需求理解**:
- 当前 `player-profit-loss-chart.vue` 显示用户盈亏趋势图表和汇总数据
- 需要增加订单明细列表,可以点击查看每笔订单的盈亏
- 明细需包含: 道具卡、优惠券等使用情况
**当前支持**:
- 有资产分项概览: 商品产出、积分收益、道具卡价值、优惠券价值
- 有趋势图表展示投入、产出、净盈亏
**待实现**:
- 盈亏明细列表(可分页、可搜索)
- 每条记录显示: 订单信息、支付金额、获得奖品价值、使用的优惠券/道具卡及其价值
---
### Bug 4: 用户资产加一个搜索
**需求理解**:
- 在用户详情抽屉的"资产"Tab中增加搜索功能
- 当前资产列表使用 `ArtDataListCard` 组件展示
**待实现**:
- 搜索框(按商品名称、订单号等搜索)
- 可能需要修改后端API支持搜索参数
---
## 疑问澄清
### 优先级问题
1. **Bug 2** 描述不够清晰,需要进一步说明具体需求:
- 是否需要在主仪表盘添加新组件?
- 还是修复现有 `ActivityAnalysisDrawer` 的问题?
- 需要展示哪些数据?
### 技术问题
2. **Bug 1** 分页问题:
- 是否只在有搜索条件时出现?
- 是否与特定浏览器相关?
### 范围确认
3. **Bug 3** 盈亏明细:
- 明细是否需要导出功能?
- 是否需要按时间范围筛选?
- 每条明细需要展示哪些具体字段?
4. **Bug 4** 资产搜索:
- 支持哪些搜索条件?商品名称?订单号?
- 是否需要后端支持模糊搜索?

View File

@ -0,0 +1,59 @@
# 玩家管理Bug修复 - 共识文档
## 需求确认
### Bug 1: 玩家列表分页失效
**现象**: 点击下一页后自动跳回第1页
**确认范围**: 在玩家列表页面点击分页控件的下一页时出现
### Bug 2: 活动游戏盈亏仪表盘
**确认理解**: 需要修复/完善活动的盈亏分析功能
- 目标:完善现有的 `ActivityAnalysisDrawer` 组件功能
### Bug 3: 用户盈亏分析明细
**确认理解**: 在用户盈亏分析中增加订单级明细列表
- 每条明细显示:订单信息、支付金额、获得奖品价值
- 包含使用的优惠券、道具卡及其价值
### Bug 4: 用户资产搜索
**确认理解**: 在用户详情抽屉的资产Tab中增加搜索功能
---
## 技术实现方案
### Bug 1 修复方案
**根因分析**:
- 玩家管理页面使用 `useTable` hook 管理分页
- `ArtTable` 组件通过 `pagination:current-change` 事件通知页码变化
- 页面监听该事件调用 `handleCurrentChange``getData(params)`
- 问题可能出在 `useTable.ts` 中搜索参数和分页参数的同步逻辑
**修复方案**:
- 检查 `handleCurrentChange` 函数中页码参数的传递
- 确保分页参数不被搜索参数覆盖
### Bug 2 修复方案
**现有功能**: `ActivityAnalysisDrawer.vue` 已有活动数据分析功能
**待完善**: 确认功能是否正常工作,是否需要增强
### Bug 3 实现方案
**新增功能**:
1. 后端新增API: `GET /api/admin/users/{user_id}/profit_loss/details`
- 返回每笔订单的盈亏明细
- 包含:订单信息、支付金额、获得价值、使用的优惠券/道具卡
2. 前端增加明细列表组件
### Bug 4 实现方案
**新增功能**:
1. 后端API修改: `GET /api/admin/users/{user_id}/inventory` 支持搜索参数
2. 前端在资产Tab增加搜索框
---
## 验收标准
1. **Bug 1**: 玩家列表可以正常翻页,页码不会跳回第一页
2. **Bug 2**: 活动盈亏仪表盘功能正常工作
3. **Bug 3**: 用户盈亏分析页可以查看订单明细列表
4. **Bug 4**: 用户资产列表可以按商品名称搜索

View File

@ -0,0 +1,177 @@
# 玩家管理Bug修复 - 架构设计
## 整体架构图
```mermaid
graph TB
subgraph Frontend["前端 Vue3"]
PM[玩家管理页面]
PD[用户详情抽屉]
PL[盈亏分析组件]
AD["活动分析抽屉(已有)"]
end
subgraph Backend["后端 Go/Gin"]
UA[users_admin.go]
UP[users_profit_loss.go]
AA[activities_admin.go]
end
PM --> |分页请求| UA
PD --> |资产列表+搜索| UA
PL --> |盈亏趋势| UP
PL --> |盈亏明细| UP
```
---
## Bug 1: 分页修复
### 问题分析
```
player-manage/index.vue
└── useTable hook
├── handleCurrentChange(newCurrent)
│ └── getData(params) // params中需要包含正确的page参数
└── 问题searchParams可能覆盖新的页码
```
### 修复方案
`index.vue` 中,`handleSearch` 函数调用 `getDataDebounced` 时传递了搜索参数,这可能导致分页参数被覆盖。
需要检查的代码路径:
1. `ArtTable` 组件发出 `pagination:current-change` 事件
2. `useTable``handleCurrentChange` 接收新页码
3. 确保页码正确传递给 API 请求
---
## Bug 2: 活动盈亏仪表盘
### 现有组件
- `ActivityAnalysisDrawer.vue`: 单活动数据分析
- 计算: 总营收、总成本、毛利润、参与人数
### 待确认/完善
- 检查计算逻辑是否正确
- 确保数据展示完整
---
## Bug 3: 用户盈亏明细
### 新增API
#### 后端: `GET /api/admin/users/{user_id}/stats/profit_loss_details`
**请求参数**:
```go
type profitLossDetailsRequest struct {
Page int `form:"page"`
PageSize int `form:"page_size"`
RangeType string `form:"rangeType"` // today, 7d, 30d, all
}
```
**响应结构**:
```go
type profitLossDetailItem struct {
OrderID int64 `json:"order_id"`
OrderNo string `json:"order_no"`
CreatedAt string `json:"created_at"`
ActualAmount int64 `json:"actual_amount"` // 实际支付金额(分)
RefundAmount int64 `json:"refund_amount"` // 退款金额(分)
NetCost int64 `json:"net_cost"` // 净投入(分)
PrizeValue int64 `json:"prize_value"` // 获得奖品价值(分)
PointsEarned int64 `json:"points_earned"` // 获得积分
PointsValue int64 `json:"points_value"` // 积分价值(分)
CouponUsedValue int64 `json:"coupon_used_value"` // 使用优惠券价值(分)
ItemCardUsed string `json:"item_card_used"` // 使用的道具卡名称
ItemCardValue int64 `json:"item_card_value"` // 道具卡价值(分)
NetProfit int64 `json:"net_profit"` // 净盈亏
ActivityName string `json:"activity_name"` // 活动名称
PrizeName string `json:"prize_name"` // 奖品名称
SourceType int `json:"source_type"` // 来源类型
}
type profitLossDetailsResponse struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int64 `json:"total"`
List []profitLossDetailItem `json:"list"`
Summary struct {
TotalCost int64 `json:"total_cost"`
TotalValue int64 `json:"total_value"`
TotalProfit int64 `json:"total_profit"`
} `json:"summary"`
}
```
### 前端组件修改
`player-profit-loss-chart.vue` 中增加:
1. "查看明细" 按钮
2. 明细表格(支持分页)
3. 显示字段:时间、订单号、支付金额、获得价值、使用优惠券/道具卡、净盈亏
---
## Bug 4: 资产搜索
### 后端修改
修改 `users_admin.go` 中的 `ListUserInventory`:
```go
type listInventoryRequest struct {
Page int `form:"page"`
PageSize int `form:"page_size"`
Keyword string `form:"keyword"` // 新增:搜索关键词
}
```
查询逻辑增加:
```go
if req.Keyword != "" {
query = query.Where(db.Products.Name.Like("%" + req.Keyword + "%"))
}
```
### 前端修改
`player-detail-drawer.vue` 资产Tab中:
1. 增加搜索输入框
2. 调用API时传递 `keyword` 参数
---
## 数据流图
```mermaid
sequenceDiagram
participant User as 用户
participant FE as 前端
participant BE as 后端
participant DB as 数据库
Note over User, DB: Bug 3: 盈亏明细查询
User->>FE: 点击"查看明细"
FE->>BE: GET /users/:id/stats/profit_loss_details
BE->>DB: 查询订单+奖品+优惠券+道具卡
DB-->>BE: 返回数据
BE-->>FE: JSON响应
FE-->>User: 展示明细列表
```
---
## 文件变更清单
| 文件 | 变更类型 | 说明 |
|------|---------|------|
| `web/admin/src/views/player-manage/index.vue` | 修改 | 修复分页问题 |
| `web/admin/src/hooks/core/useTable.ts` | 检查 | 确认分页逻辑 |
| `web/admin/src/views/player-manage/modules/player-profit-loss-chart.vue` | 修改 | 添加明细列表 |
| `web/admin/src/views/player-manage/modules/player-detail-drawer.vue` | 修改 | 添加资产搜索 |
| `web/admin/src/api/player-manage.ts` | 修改 | 添加明细API调用 |
| `internal/api/admin/users_profit_loss.go` | 修改 | 添加明细API |
| `internal/api/admin/users_admin.go` | 修改 | 资产列表添加搜索 |

40
go.mod
View File

@ -1,6 +1,8 @@
module bindbox-game module bindbox-game
go 1.19 go 1.24.0
toolchain go1.24.2
require ( require (
github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/DATA-DOG/go-sqlmock v1.5.2
@ -26,17 +28,22 @@ require (
github.com/rs/cors/wrapper/gin v0.0.0-20231013084403-73f81b45a644 github.com/rs/cors/wrapper/gin v0.0.0-20231013084403-73f81b45a644
github.com/spf13/cast v1.5.1 github.com/spf13/cast v1.5.1
github.com/spf13/viper v1.17.0 github.com/spf13/viper v1.17.0
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.11.1
github.com/swaggo/files v1.0.1 github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/gin-swagger v1.6.0
github.com/swaggo/swag v1.16.2 github.com/swaggo/swag v1.16.2
github.com/tealeg/xlsx v1.0.5 github.com/tealeg/xlsx v1.0.5
github.com/tencentyun/cos-go-sdk-v5 v0.7.37 github.com/tencentyun/cos-go-sdk-v5 v0.7.37
github.com/wechatpay-apiv3/wechatpay-go v0.2.21 github.com/wechatpay-apiv3/wechatpay-go v0.2.21
go.opentelemetry.io/otel v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0
go.opentelemetry.io/otel/sdk v1.39.0
go.opentelemetry.io/otel/trace v1.39.0
go.uber.org/multierr v1.10.0 go.uber.org/multierr v1.10.0
go.uber.org/zap v1.26.0 go.uber.org/zap v1.26.0
golang.org/x/crypto v0.27.0 golang.org/x/crypto v0.44.0
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d golang.org/x/tools v0.38.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c
gorm.io/driver/mysql v1.5.2 gorm.io/driver/mysql v1.5.2
@ -58,6 +65,7 @@ require (
github.com/aliyun/credentials-go v1.4.5 // indirect github.com/aliyun/credentials-go v1.4.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj v1.8.4 // indirect github.com/clbanning/mxj v1.8.4 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/clbanning/mxj/v2 v2.7.0 // indirect
@ -67,14 +75,18 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-querystring v1.0.0 // indirect github.com/google/go-querystring v1.0.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
@ -107,14 +119,20 @@ require (
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.11 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
golang.org/x/arch v0.4.0 // indirect golang.org/x/arch v0.4.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/mod v0.17.0 // indirect golang.org/x/mod v0.29.0 // indirect
golang.org/x/net v0.29.0 // indirect golang.org/x/net v0.47.0 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.25.0 // indirect golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.18.0 // indirect golang.org/x/text v0.31.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/grpc v1.77.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect

105
go.sum
View File

@ -104,7 +104,9 @@ github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQ
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
@ -112,6 +114,8 @@ github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -148,11 +152,13 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4= github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg= github.com/gin-contrib/pprof v1.4.0 h1:XxiBSf5jWZ5i16lNOPbMTVdgHBdhfGRD5PZ1LWazzvg=
github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90= github.com/gin-contrib/pprof v1.4.0/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@ -163,6 +169,11 @@ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SU
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@ -175,6 +186,7 @@ github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyr
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
@ -195,7 +207,9 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -222,8 +236,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -236,8 +250,9 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -256,13 +271,16 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@ -270,16 +288,25 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/issue9/assert/v4 v4.1.1 h1:OhPE8SB8n/qZCNGLQa+6MQtr/B3oON0JAVj68k8jJlc= github.com/issue9/assert/v4 v4.1.1 h1:OhPE8SB8n/qZCNGLQa+6MQtr/B3oON0JAVj68k8jJlc=
github.com/issue9/assert/v4 v4.1.1/go.mod h1:v7qDRXi7AsaZZNh8eAK2rkLJg5/clztqQGA1DRv9Lv4=
github.com/issue9/identicon/v2 v2.1.2 h1:tu+4vveoiJNXfmWYvl1pDcZSAHCG37+lsoEc2UfCzkI= github.com/issue9/identicon/v2 v2.1.2 h1:tu+4vveoiJNXfmWYvl1pDcZSAHCG37+lsoEc2UfCzkI=
github.com/issue9/identicon/v2 v2.1.2/go.mod h1:h5JXMtcgkqxltElhpF7PPicNyvFDWzi8VCSHdNjG7KY= github.com/issue9/identicon/v2 v2.1.2/go.mod h1:h5JXMtcgkqxltElhpF7PPicNyvFDWzi8VCSHdNjG7KY=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
@ -305,6 +332,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -330,6 +358,7 @@ github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@ -367,7 +396,8 @@ github.com/redis/go-redis/v9 v9.17.2/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/cors v1.8.1 h1:OrP+y5H+5Md29ACTA9imbALaKHwOSUZkcizaG0LT5ow= github.com/rs/cors v1.8.1 h1:OrP+y5H+5Md29ACTA9imbALaKHwOSUZkcizaG0LT5ow=
github.com/rs/cors v1.8.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/cors v1.8.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/cors/wrapper/gin v0.0.0-20231013084403-73f81b45a644 h1:BBwREPixt0iE77C9z7DOenoeh5OGFrzyL1cWOp5oQTs= github.com/rs/cors/wrapper/gin v0.0.0-20231013084403-73f81b45a644 h1:BBwREPixt0iE77C9z7DOenoeh5OGFrzyL1cWOp5oQTs=
@ -402,8 +432,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
@ -441,7 +472,26 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
@ -467,8 +517,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -508,8 +558,9 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -555,8 +606,8 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -581,8 +632,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -635,8 +686,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -663,8 +714,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -722,12 +773,15 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@ -790,6 +844,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -806,6 +864,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@ -817,10 +877,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -848,10 +907,12 @@ gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10
gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs=
gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8=
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc= gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8=
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI= gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0=
gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY= gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY=
gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE= gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE=
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=

View File

@ -64,6 +64,10 @@ func (h *handler) JoinLottery() core.HandlerFunc {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return return
} }
// DEBUG LOG: Print request params to diagnose frontend issue
reqBytes, _ := json.Marshal(req)
h.logger.Info(fmt.Sprintf("JoinLottery Request Params: UserID=%d Payload=%s", ctx.SessionUserInfo().Id, string(reqBytes)))
userID := int64(ctx.SessionUserInfo().Id) userID := int64(ctx.SessionUserInfo().Id)
h.logger.Info(fmt.Sprintf("JoinLottery Start: UserID=%d ActivityID=%d IssueID=%d", userID, req.ActivityID, req.IssueID)) h.logger.Info(fmt.Sprintf("JoinLottery Start: UserID=%d ActivityID=%d IssueID=%d", userID, req.ActivityID, req.IssueID))
activity, err := h.activity.GetActivity(ctx.RequestContext(), req.ActivityID) activity, err := h.activity.GetActivity(ctx.RequestContext(), req.ActivityID)
@ -263,6 +267,13 @@ func (h *handler) JoinLottery() core.HandlerFunc {
return err return err
} }
deducted += canDeduct deducted += canDeduct
// Record usage for remark (Format: gp_use:ID:Count)
if order.Remark == "" {
order.Remark = fmt.Sprintf("gp_use:%d:%d", p.ID, canDeduct)
} else {
order.Remark += fmt.Sprintf("|gp_use:%d:%d", p.ID, canDeduct)
}
} }
if deducted < count { if deducted < count {
@ -273,15 +284,8 @@ func (h *handler) JoinLottery() core.HandlerFunc {
order.ActualAmount = 0 order.ActualAmount = 0
order.SourceType = 4 // Cleanly mark as Game Pass source order.SourceType = 4 // Cleanly mark as Game Pass source
// existing lottery logic sets SourceType based on "h.orderModel" which defaults to something? // Legacy marker for backward compatibility or simple check
// h.orderModel(..., c) implementation needs to be checked or inferred. order.Remark += "|use_game_pass"
// Assuming orderModel sets SourceType based on activity or defaults.
// Let's explicitly mark it or rely on Remark.
if order.Remark == "" {
order.Remark = "use_game_pass"
} else {
order.Remark += "|use_game_pass"
}
// Note: If we change SourceType to 4, ProcessOrderLottery might skip it if checks SourceType. // Note: If we change SourceType to 4, ProcessOrderLottery might skip it if checks SourceType.
// Lottery app usually expects SourceType=2 or similar. // Lottery app usually expects SourceType=2 or similar.
// Let's KEEP SourceType as is (likely 2 for ichiban), but Amount=0 ensures it's treated as Paid. // Let's KEEP SourceType as is (likely 2 for ichiban), but Amount=0 ensures it's treated as Paid.
@ -289,24 +293,21 @@ func (h *handler) JoinLottery() core.HandlerFunc {
if !useGamePass && req.UsePoints != nil && *req.UsePoints > 0 { if !useGamePass && req.UsePoints != nil && *req.UsePoints > 0 {
bal, _ := h.user.GetPointsBalance(ctx.RequestContext(), userID) bal, _ := h.user.GetPointsBalance(ctx.RequestContext(), userID)
usePts := *req.UsePoints // req.UsePoints 是前端传入的积分数,需要转换为分
if bal > 0 && usePts > bal { usePtsCents, _ := h.user.PointsToCents(ctx.RequestContext(), *req.UsePoints)
usePts = bal // bal 已经是分单位
if bal > 0 && usePtsCents > bal {
usePtsCents = bal
} }
ratePtsPerCent, _ := h.user.CentsToPoints(ctx.RequestContext(), 1) // deductCents 是要从订单金额中抵扣的分数
if ratePtsPerCent <= 0 { deductCents := usePtsCents
ratePtsPerCent = 1
}
deductCents := usePts / ratePtsPerCent
if deductCents > order.ActualAmount { if deductCents > order.ActualAmount {
deductCents = order.ActualAmount deductCents = order.ActualAmount
} }
if deductCents > 0 { if deductCents > 0 {
needPts := deductCents * ratePtsPerCent // needPts 是实际需要扣除的分数
if needPts > usePts { needPts := deductCents
needPts = usePts
}
// Inline ConsumePointsFor logic using tx // Inline ConsumePointsFor logic using tx
// Lock rows // Lock rows
rows, errFind := tx.UserPoints.WithContext(ctx.RequestContext()).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.UserPoints.UserID.Eq(userID)).Order(tx.UserPoints.ValidEnd.Asc()).Find() rows, errFind := tx.UserPoints.WithContext(ctx.RequestContext()).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.UserPoints.UserID.Eq(userID)).Order(tx.UserPoints.ValidEnd.Asc()).Find()
@ -618,32 +619,34 @@ func (h *handler) validateIchibanSlots(ctx core.Context, req *joinLotteryRequest
if totalSlots <= 0 { if totalSlots <= 0 {
return core.Error(http.StatusBadRequest, 170008, "no slots") return core.Error(http.StatusBadRequest, 170008, "no slots")
} }
if len(req.SlotIndex) > 0 { // 1. 强制校验:必须选择位置
if req.Count <= 0 || req.Count != int64(len(req.SlotIndex)) { if len(req.SlotIndex) == 0 {
return core.Error(http.StatusBadRequest, code.ParamBindError, "参数错误") return core.Error(http.StatusBadRequest, code.ParamBindError, "一番赏必须选择位置")
} }
if req.Count <= 0 || req.Count != int64(len(req.SlotIndex)) {
return core.Error(http.StatusBadRequest, code.ParamBindError, "参数错误:数量与位置不匹配")
}
// 1. 内存中去重和范围检查 // 2. 内存中去重和范围检查
selectedSlots := make([]int64, 0, len(req.SlotIndex)) selectedSlots := make([]int64, 0, len(req.SlotIndex))
seen := make(map[int64]struct{}, len(req.SlotIndex)) seen := make(map[int64]struct{}, len(req.SlotIndex))
for _, si := range req.SlotIndex { for _, si := range req.SlotIndex {
if _, ok := seen[si]; ok { if _, ok := seen[si]; ok {
return core.Error(http.StatusBadRequest, 170011, "duplicate slots not allowed") return core.Error(http.StatusBadRequest, 170011, "duplicate slots not allowed")
}
seen[si] = struct{}{}
if si < 1 || si > totalSlots {
return core.Error(http.StatusBadRequest, 170008, "slot out of range")
}
selectedSlots = append(selectedSlots, si-1)
} }
seen[si] = struct{}{}
if si < 1 || si > totalSlots {
return core.Error(http.StatusBadRequest, 170008, "slot out of range")
}
selectedSlots = append(selectedSlots, si-1)
}
// 2. 批量查询数据库检查格位是否已被占用 // 3. 批量查询数据库检查格位是否已被占用
var occupiedCount int64 var occupiedCount int64
_ = h.repo.GetDbR().Raw("SELECT COUNT(*) FROM issue_position_claims WHERE issue_id=? AND slot_index IN ?", req.IssueID, selectedSlots).Scan(&occupiedCount).Error _ = h.repo.GetDbR().Raw("SELECT COUNT(*) FROM issue_position_claims WHERE issue_id=? AND slot_index IN ?", req.IssueID, selectedSlots).Scan(&occupiedCount).Error
if occupiedCount > 0 { if occupiedCount > 0 {
// 如果有占用,为了告知具体是哪个位置,可以打个 log 或者简单的直接返回错误 // 即使是并发场景,这里做一个 Pre-check 也能拦截大部分冲突
return core.Error(http.StatusBadRequest, 170007, "部分位置已被占用,请刷新重试") return core.Error(http.StatusBadRequest, 170007, "部分位置已被占用,请刷新重试")
}
} }
return nil return nil
} }

View File

@ -30,6 +30,7 @@ type matchingGamePreOrderRequest struct {
CouponID *int64 `json:"coupon_id"` CouponID *int64 `json:"coupon_id"`
ItemCardID *int64 `json:"item_card_id"` ItemCardID *int64 `json:"item_card_id"`
UseGamePass bool `json:"use_game_pass"` // 新增:是否使用次数卡 UseGamePass bool `json:"use_game_pass"` // 新增:是否使用次数卡
Count int64 `json:"count"` // 新增:购买数量
} }
type matchingGamePreOrderResponse struct { type matchingGamePreOrderResponse struct {
@ -82,6 +83,12 @@ func (h *handler) PreOrderMatchingGame() core.HandlerFunc {
return return
} }
// 校验 Count对对碰只能单次购买
if req.Count > 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 170013, "对对碰游戏暂不支持批量购买,请单次支付"))
return
}
// 1. Get Activity/Issue Info (Mocking price for now or fetching if available) // 1. Get Activity/Issue Info (Mocking price for now or fetching if available)
// Assuming price is fixed or fetched. Let's fetch basic activity info if possible, or assume config. // Assuming price is fixed or fetched. Let's fetch basic activity info if possible, or assume config.
// Since Request has IssueID, let's fetch Issue to get ActivityID and Price. // Since Request has IssueID, let's fetch Issue to get ActivityID and Price.

View File

@ -0,0 +1,413 @@
package admin
import (
"bindbox-game/internal/code"
"bindbox-game/internal/pkg/core"
"bindbox-game/internal/pkg/validation"
"bindbox-game/internal/repository/mysql/model"
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"
)
type activityProfitLossRequest struct {
Page int `form:"page"`
PageSize int `form:"page_size"`
Name string `form:"name"`
Status int32 `form:"status"` // 1进行中 2下线
}
type activityProfitLossItem struct {
ActivityID int64 `json:"activity_id"`
ActivityName string `json:"activity_name"`
Status int32 `json:"status"`
DrawCount int64 `json:"draw_count"`
PlayerCount int64 `json:"player_count"`
TotalRevenue int64 `json:"total_revenue"` // 实际支付金额 (分)
TotalCost int64 `json:"total_cost"` // 奖品标价总和 (分)
Profit int64 `json:"profit"` // Revenue - Cost
ProfitRate float64 `json:"profit_rate"` // Profit / Revenue
}
type activityProfitLossResponse struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int64 `json:"total"`
List []activityProfitLossItem `json:"list"`
}
func (h *handler) DashboardActivityProfitLoss() core.HandlerFunc {
return func(ctx core.Context) {
req := new(activityProfitLossRequest)
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
db := h.repo.GetDbR().WithContext(ctx.RequestContext())
// 1. 获取活动列表基础信息
var activities []model.Activities
query := db.Table(model.TableNameActivities)
if req.Name != "" {
query = query.Where("name LIKE ?", "%"+req.Name+"%")
}
if req.Status > 0 {
query = query.Where("status = ?", req.Status)
}
var total int64
query.Count(&total)
if err := query.Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Order("id DESC").Find(&activities).Error; err != nil {
h.logger.Error(fmt.Sprintf("GetActivityProfitLoss activities error: %v", err))
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 21021, err.Error()))
return
}
if len(activities) == 0 {
ctx.Payload(&activityProfitLossResponse{
Page: req.Page,
PageSize: req.PageSize,
Total: total,
List: []activityProfitLossItem{},
})
return
}
activityIDs := make([]int64, len(activities))
activityMap := make(map[int64]*activityProfitLossItem)
for i, a := range activities {
activityIDs[i] = a.ID
activityMap[a.ID] = &activityProfitLossItem{
ActivityID: a.ID,
ActivityName: a.Name,
Status: a.Status,
}
}
// 2. 统计抽奖次数和人数 (通过 activity_draw_logs 关联 activity_issues)
type drawStat struct {
ActivityID int64
DrawCount int64
PlayerCount int64
}
var drawStats []drawStat
db.Table(model.TableNameActivityDrawLogs).
Select("activity_issues.activity_id, COUNT(activity_draw_logs.id) as draw_count, COUNT(DISTINCT activity_draw_logs.user_id) as player_count").
Joins("JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id").
Where("activity_issues.activity_id IN ?", activityIDs).
Group("activity_issues.activity_id").
Scan(&drawStats)
for _, s := range drawStats {
if item, ok := activityMap[s.ActivityID]; ok {
item.DrawCount = s.DrawCount
item.PlayerCount = s.PlayerCount
}
}
// 3. 统计营收 (通过 orders 关联 activity_draw_logs)
type revenueStat struct {
ActivityID int64
TotalRevenue int64
}
var revenueStats []revenueStat
// 修正: 先找到每个订单对应的一个 activity_id (去重),再关联 orders 统计 actual_amount。
// 避免一个订单包含多个 draw logs 时导致 orders.actual_amount 被重复累加。
// 子查询: SELECT order_id, MAX(issue_id) as issue_id FROM activity_draw_logs GROUP BY order_id
// 然后通过 issue_id 关联 activity_issues 找到 activity_id
var err error
err = db.Table(model.TableNameOrders).
Select("activity_issues.activity_id, SUM(orders.actual_amount) as total_revenue").
Joins("JOIN (SELECT order_id, MAX(issue_id) as issue_id FROM activity_draw_logs GROUP BY order_id) dl ON dl.order_id = orders.id").
Joins("JOIN activity_issues ON activity_issues.id = dl.issue_id").
Where("orders.status = ?", 2). // 已支付
Where("activity_issues.activity_id IN ?", activityIDs).
Group("activity_issues.activity_id").
Scan(&revenueStats).Error
if err != nil {
h.logger.Error(fmt.Sprintf("GetActivityProfitLoss revenue stats error: %v", err))
}
for _, s := range revenueStats {
if item, ok := activityMap[s.ActivityID]; ok {
item.TotalRevenue = s.TotalRevenue
}
}
// 4. 统计成本 (通过 user_inventory 关联 products)
type costStat struct {
ActivityID int64
TotalCost int64
}
var costStats []costStat
db.Table(model.TableNameUserInventory).
Select("user_inventory.activity_id, SUM(products.price) as total_cost").
Joins("JOIN products ON products.id = user_inventory.product_id").
Where("user_inventory.activity_id IN ?", activityIDs).
Group("user_inventory.activity_id").
Scan(&costStats)
for _, s := range costStats {
if item, ok := activityMap[s.ActivityID]; ok {
item.TotalCost = s.TotalCost
}
}
// 5. 计算盈亏和比率
finalList := make([]activityProfitLossItem, 0, len(activities))
for _, a := range activities {
item := activityMap[a.ID]
item.Profit = item.TotalRevenue - item.TotalCost
if item.TotalRevenue > 0 {
item.ProfitRate = float64(item.Profit) / float64(item.TotalRevenue)
}
finalList = append(finalList, *item)
}
ctx.Payload(&activityProfitLossResponse{
Page: req.Page,
PageSize: req.PageSize,
Total: total,
List: finalList,
})
}
}
type activityLogsRequest struct {
Page int `form:"page"`
PageSize int `form:"page_size"`
}
type activityLogItem struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
ProductID int64 `json:"product_id"`
ProductName string `json:"product_name"`
ProductImage string `json:"product_image"`
ProductPrice int64 `json:"product_price"`
OrderAmount int64 `json:"order_amount"`
DiscountAmount int64 `json:"discount_amount"` // New: 优惠金额
PayType string `json:"pay_type"` // New: 支付方式/类型 (现金/道具卡/次数卡)
UsedCard string `json:"used_card"` // New: 使用的卡券名称
Profit int64 `json:"profit"`
CreatedAt time.Time `json:"created_at"`
}
type activityLogsResponse struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int64 `json:"total"`
List []activityLogItem `json:"list"`
}
func (h *handler) DashboardActivityLogs() core.HandlerFunc {
return func(ctx core.Context) {
activityID, _ := strconv.ParseInt(ctx.Param("activity_id"), 10, 64)
if activityID <= 0 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "Invalid activity ID"))
return
}
req := new(activityLogsRequest)
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
db := h.repo.GetDbR().WithContext(ctx.RequestContext())
var total int64
db.Table(model.TableNameActivityDrawLogs).
Joins("JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id").
Where("activity_issues.activity_id = ?", activityID).
Count(&total)
var logs []struct {
ID int64
UserID int64
Nickname string
Avatar string
ProductID int64
ProductName string
ImagesJSON string
ProductPrice int64
OrderAmount int64
DiscountAmount int64
SourceType int32
CouponName string
ItemCardName string
CreatedAt time.Time
}
err := db.Table(model.TableNameActivityDrawLogs).
Select(`
activity_draw_logs.id,
activity_draw_logs.user_id,
COALESCE(users.nickname, '') as nickname,
COALESCE(users.avatar, '') as avatar,
activity_reward_settings.product_id,
COALESCE(products.name, '') as product_name,
COALESCE(products.images_json, '[]') as images_json,
COALESCE(products.price, 0) as product_price,
COALESCE(orders.actual_amount, 0) as order_amount,
COALESCE(orders.discount_amount, 0) as discount_amount,
orders.source_type,
COALESCE(system_coupons.name, '') as coupon_name,
COALESCE(system_item_cards.name, '') as item_card_name,
activity_draw_logs.created_at
`).
Joins("JOIN activity_issues ON activity_issues.id = activity_draw_logs.issue_id").
Joins("LEFT JOIN users ON users.id = activity_draw_logs.user_id").
Joins("LEFT JOIN activity_reward_settings ON activity_reward_settings.id = activity_draw_logs.reward_id").
Joins("LEFT JOIN products ON products.id = activity_reward_settings.product_id").
Joins("LEFT JOIN orders ON orders.id = activity_draw_logs.order_id").
Joins("LEFT JOIN system_coupons ON system_coupons.id = orders.coupon_id").
Joins("LEFT JOIN system_item_cards ON system_item_cards.id = orders.item_card_id").
Where("activity_issues.activity_id = ?", activityID).
Order("activity_draw_logs.id DESC").
Offset((req.Page - 1) * req.PageSize).
Limit(req.PageSize).
Scan(&logs).Error
if err != nil {
h.logger.Error(fmt.Sprintf("GetActivityLogs error: %v", err))
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 21022, err.Error()))
return
}
list := make([]activityLogItem, len(logs))
for i, l := range logs {
var images []string
_ = json.Unmarshal([]byte(l.ImagesJSON), &images)
productImage := ""
if len(images) > 0 {
productImage = images[0]
}
// Determine PayType and UsedCard
payType := "现金支付"
usedCard := ""
if l.SourceType == 2 { // Order SourceType 2 = Ticket/Count Card
payType = "次数卡"
}
if l.ItemCardName != "" {
usedCard = l.ItemCardName
if payType == "现金支付" {
payType = "道具卡" // Override if item card is explicitly present
}
} else if l.CouponName != "" {
usedCard = l.CouponName
payType = "优惠券"
}
list[i] = activityLogItem{
ID: l.ID,
UserID: l.UserID,
Nickname: l.Nickname,
Avatar: l.Avatar,
ProductID: l.ProductID,
ProductName: l.ProductName,
ProductImage: productImage,
ProductPrice: l.ProductPrice,
OrderAmount: l.OrderAmount,
DiscountAmount: l.DiscountAmount,
PayType: payType,
UsedCard: usedCard,
Profit: l.OrderAmount - l.ProductPrice,
CreatedAt: l.CreatedAt,
}
}
ctx.Payload(&activityLogsResponse{
Page: req.Page,
PageSize: req.PageSize,
Total: total,
List: list,
})
}
}
type ensureActivityProfitLossMenuResponse struct {
Ensured bool `json:"ensured"`
Parent int64 `json:"parent_id"`
MenuID int64 `json:"menu_id"`
}
// EnsureActivityProfitLossMenu 确保运营分析下存在“活动盈亏”菜单
func (h *handler) EnsureActivityProfitLossMenu() core.HandlerFunc {
return func(ctx core.Context) {
// 1. 查找是否存在“控制台”或者“运营中心”类的父菜单
// 很多系统会将概览放在 Dashboard 下。根据 titles_seed.go运营是 Operations。
parent, _ := h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Name.Eq("Operations")).First()
var parentID int64
if parent == nil {
// 如果没有 Operations尝试查找 Dashboard
parent, _ = h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Name.Eq("Dashboard")).First()
}
if parent != nil {
parentID = parent.ID
}
// 2. 查找活动盈亏菜单
// 路径指向控制台并带上查参数
menuPath := "/dashboard/console?tab=activity-profit"
exists, _ := h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Path.Eq(menuPath)).First()
if exists != nil {
ctx.Payload(&ensureActivityProfitLossMenuResponse{Ensured: true, Parent: parentID, MenuID: exists.ID})
return
}
// 3. 创建菜单
newMenu := &model.Menus{
ParentID: parentID,
Path: menuPath,
Name: "活动盈亏",
Component: "/dashboard/console/index",
Icon: "ri:pie-chart-2-fill",
Sort: 60, // 排序在称号之后
Status: true,
KeepAlive: true,
IsHide: false,
IsHideTab: false,
CreatedUser: "system",
UpdatedUser: "system",
}
if err := h.writeDB.Menus.WithContext(ctx.RequestContext()).Create(newMenu); err != nil {
ctx.AbortWithError(core.Error(http.StatusInternalServerError, 21023, "创建菜单失败: "+err.Error()))
return
}
// 读取新创建的 ID
created, _ := h.readDB.Menus.WithContext(ctx.RequestContext()).Where(h.readDB.Menus.Path.Eq(menuPath)).First()
menuID := int64(0)
if created != nil {
menuID = created.ID
}
ctx.Payload(&ensureActivityProfitLossMenuResponse{Ensured: true, Parent: parentID, MenuID: menuID})
}
}

View File

@ -163,7 +163,7 @@ func (h *handler) DashboardCards() core.HandlerFunc {
rsp.ItemCardSales = icCur rsp.ItemCardSales = icCur
rsp.DrawCount = dlCur rsp.DrawCount = dlCur
rsp.NewUsers = nuCur rsp.NewUsers = nuCur
rsp.TotalPoints = tpCur rsp.TotalPoints = int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), tpCur))
rsp.TotalCoupons = tcCur rsp.TotalCoupons = tcCur
rsp.TotalItemCards = ticCur rsp.TotalItemCards = ticCur
rsp.TotalGamePasses = tgpCur rsp.TotalGamePasses = tgpCur
@ -1695,10 +1695,17 @@ func (h *handler) OperationsPointsEconomySummary() core.HandlerFunc {
conversionRate = float64(activeUsers) / float64(totalUsers) * 100 conversionRate = float64(activeUsers) / float64(totalUsers) * 100
} }
// Convert from Store Units (Cents) to Display Units (Points)
// Assuming 100 StoreUnits = 1 Point (if Rate=1)
// Because Store is Cents. 1 Point = 1 Yuan = 100 Cents.
issuedPoints := h.userSvc.CentsToPointsFloat(ctx.RequestContext(), issued)
consumedPoints := h.userSvc.CentsToPointsFloat(ctx.RequestContext(), consumed)
netChangePoints := issuedPoints - consumedPoints
ctx.Payload(pointsEconomySummaryResponse{ ctx.Payload(pointsEconomySummaryResponse{
TotalIssued: issued, TotalIssued: int64(issuedPoints),
TotalConsumed: consumed, TotalConsumed: int64(consumedPoints),
NetChange: issued - consumed, NetChange: int64(netChangePoints),
ActiveUsersWithPoints: activeUsers, ActiveUsersWithPoints: activeUsers,
ConversionRate: float64(int(conversionRate*10)) / 10.0, ConversionRate: float64(int(conversionRate*10)) / 10.0,
}) })
@ -1757,11 +1764,11 @@ func (h *handler) OperationsPointsTrend() core.HandlerFunc {
out[i] = pointsTrendItem{ out[i] = pointsTrendItem{
Date: b.Label, Date: b.Label,
Issued: issued, Issued: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), issued)),
Consumed: consumed, Consumed: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), consumed)),
Expired: 0, Expired: 0,
NetChange: netChange, NetChange: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), netChange)),
Balance: runningBalance, Balance: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), runningBalance)),
} }
} }
@ -1834,7 +1841,7 @@ func (h *handler) OperationsPointsStructure() core.HandlerFunc {
} }
out = append(out, pointsStructureItem{ out = append(out, pointsStructureItem{
Category: name, Category: name,
Amount: r.Total, Amount: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), r.Total)),
Percentage: float64(int(pct*10)) / 10.0, Percentage: float64(int(pct*10)) / 10.0,
Trend: "+0%", Trend: "+0%",
}) })

View File

@ -0,0 +1,256 @@
package admin
import (
"bindbox-game/internal/code"
"bindbox-game/internal/pkg/core"
"bindbox-game/internal/pkg/validation"
"bindbox-game/internal/repository/mysql/model"
"fmt"
"net/http"
"sort"
"time"
)
type spendingLeaderboardRequest struct {
Page int `form:"page"`
PageSize int `form:"page_size"`
RangeType string `form:"rangeType"` // today, 7d, 30d, custom
StartDate string `form:"start"`
EndDate string `form:"end"`
SortBy string `form:"sort_by"` // spending, profit
}
type spendingLeaderboardItem struct {
UserID int64 `json:"user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
OrderCount int64 `json:"order_count"`
TotalSpending int64 `json:"total_spending"` // Total Paid Amount (Fen)
TotalPrizeValue int64 `json:"total_prize_value"` // Total Product Price (Fen)
TotalDiscount int64 `json:"total_discount"` // Total Coupon Discount (Fen)
TotalPoints int64 `json:"total_points"` // Total Points Discount (Fen)
GamePassCount int64 `json:"game_pass_count"` // Count of SourceType=4
ItemCardCount int64 `json:"item_card_count"` // Count where ItemCardID > 0
// Breakdown by game type
IchibanSpending int64 `json:"ichiban_spending"`
IchibanPrize int64 `json:"ichiban_prize"`
IchibanCount int64 `json:"ichiban_count"`
InfiniteSpending int64 `json:"infinite_spending"`
InfinitePrize int64 `json:"infinite_prize"`
InfiniteCount int64 `json:"infinite_count"`
MatchingSpending int64 `json:"matching_spending"`
MatchingPrize int64 `json:"matching_prize"`
MatchingCount int64 `json:"matching_count"`
Profit int64 `json:"profit"` // Spending - PrizeValue
ProfitRate float64 `json:"profit_rate"` // Profit / Spending
}
type spendingLeaderboardResponse struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int64 `json:"total"`
List []spendingLeaderboardItem `json:"list"`
}
func (h *handler) DashboardPlayerSpendingLeaderboard() core.HandlerFunc {
return func(ctx core.Context) {
req := new(spendingLeaderboardRequest)
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
var start, end time.Time
if req.RangeType != "all" {
start, end = parseRange(req.RangeType, req.StartDate, req.EndDate)
h.logger.Info(fmt.Sprintf("SpendingLeaderboard range: start=%v, end=%v, type=%s", start, end, req.RangeType))
} else {
h.logger.Info("SpendingLeaderboard range: ALL TIME")
}
db := h.repo.GetDbR().WithContext(ctx.RequestContext())
// 1. Get Top Spenders from Orders
type orderStat struct {
UserID int64
TotalAmount int64 // ActualAmount
OrderCount int64
TotalDiscount int64
TotalPoints int64
GamePassCount int64
ItemCardCount int64
IchibanSpending int64
IchibanCount int64
InfiniteSpending int64
InfiniteCount int64
MatchingSpending int64
MatchingCount int64
}
var stats []orderStat
query := db.Table(model.TableNameOrders).
Joins("LEFT JOIN (SELECT l.order_id, MAX(a.play_type) as play_type FROM activity_draw_logs l JOIN activity_issues i ON i.id = l.issue_id JOIN activities a ON a.id = i.activity_id GROUP BY l.order_id) oa ON oa.order_id = orders.id").
Where("orders.status = ?", 2)
if req.RangeType != "all" {
query = query.Where("orders.created_at >= ?", start).Where("orders.created_at <= ?", end)
}
if err := query.Select(`
orders.user_id,
SUM(orders.actual_amount) as total_amount,
COUNT(orders.id) as order_count,
SUM(orders.discount_amount) as total_discount,
SUM(orders.points_amount) as total_points,
SUM(CASE WHEN orders.source_type = 4 THEN 1 ELSE 0 END) as game_pass_count,
SUM(CASE WHEN orders.item_card_id > 0 THEN 1 ELSE 0 END) as item_card_count,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN orders.actual_amount ELSE 0 END) as ichiban_spending,
SUM(CASE WHEN oa.play_type = 'ichiban' THEN 1 ELSE 0 END) as ichiban_count,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN orders.actual_amount ELSE 0 END) as infinite_spending,
SUM(CASE WHEN oa.play_type IN ('infinite', 'box') THEN 1 ELSE 0 END) as infinite_count,
SUM(CASE WHEN oa.play_type = 'matching' THEN orders.actual_amount ELSE 0 END) as matching_spending,
SUM(CASE WHEN oa.play_type = 'matching' THEN 1 ELSE 0 END) as matching_count
`).
Group("orders.user_id").
Order("total_amount DESC").
Limit(50).
Scan(&stats).Error; err != nil {
h.logger.Error(fmt.Sprintf("SpendingLeaderboard SQL error: %v", err))
ctx.AbortWithError(core.Error(http.StatusBadRequest, 21020, err.Error()))
return
}
h.logger.Info(fmt.Sprintf("SpendingLeaderboard SQL done: count=%d", len(stats)))
// 2. Collect User IDs
userIDs := make([]int64, len(stats))
statMap := make(map[int64]*spendingLeaderboardItem)
for i, s := range stats {
userIDs[i] = s.UserID
statMap[s.UserID] = &spendingLeaderboardItem{
UserID: s.UserID,
TotalSpending: s.TotalAmount,
OrderCount: s.OrderCount,
TotalDiscount: s.TotalDiscount,
TotalPoints: s.TotalPoints,
GamePassCount: s.GamePassCount,
ItemCardCount: s.ItemCardCount,
IchibanSpending: s.IchibanSpending,
IchibanCount: s.IchibanCount,
InfiniteSpending: s.InfiniteSpending,
InfiniteCount: s.InfiniteCount,
MatchingSpending: s.MatchingSpending,
MatchingCount: s.MatchingCount,
}
}
if len(userIDs) > 0 {
// 3. Get User Info
// Use h.readDB.Users (GEN) as it's simple
users, _ := h.readDB.Users.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Users.ID.In(userIDs...)).Find()
for _, u := range users {
if item, ok := statMap[u.ID]; ok {
item.Nickname = u.Nickname
item.Avatar = u.Avatar
}
}
// 4. Calculate Prize Value (Inventory)
type invStat struct {
UserID int64
TotalValue int64
IchibanPrize int64
InfinitePrize int64
MatchingPrize int64
}
var invStats []invStat
// Join with Products and Activities
query := db.Table(model.TableNameUserInventory).
Joins("JOIN products ON products.id = user_inventory.product_id").
Joins("LEFT JOIN activities ON activities.id = user_inventory.activity_id").
Where("user_inventory.user_id IN ?", userIDs)
if req.RangeType != "all" {
query = query.Where("user_inventory.created_at >= ?", start).
Where("user_inventory.created_at <= ?", end)
}
err := query.Select(`
user_inventory.user_id,
SUM(products.price) as total_value,
SUM(CASE WHEN activities.play_type = 'ichiban' THEN products.price ELSE 0 END) as ichiban_prize,
SUM(CASE WHEN activities.play_type IN ('infinite', 'box') THEN products.price ELSE 0 END) as infinite_prize,
SUM(CASE WHEN activities.play_type = 'matching' THEN products.price ELSE 0 END) as matching_prize
`).
Group("user_inventory.user_id").
Scan(&invStats).Error
if err == nil {
for _, is := range invStats {
if item, ok := statMap[is.UserID]; ok {
item.TotalPrizeValue = is.TotalValue
item.IchibanPrize = is.IchibanPrize
item.InfinitePrize = is.InfinitePrize
item.MatchingPrize = is.MatchingPrize
}
}
}
}
// 5. Calculate Profit and Final List
list := make([]spendingLeaderboardItem, 0, len(statMap))
for _, item := range statMap {
item.Profit = item.TotalSpending - item.TotalPrizeValue
if item.TotalSpending > 0 {
item.ProfitRate = float64(item.Profit) / float64(item.TotalSpending)
}
list = append(list, *item)
}
// 6. Sort (in memory since we only have top N spenders)
sortBy := req.SortBy
if sortBy == "" {
sortBy = "spending"
}
sort.Slice(list, func(i, j int) bool {
switch sortBy {
case "profit":
return list[i].Profit > list[j].Profit // Higher profit first
case "profit_asc":
return list[i].Profit < list[j].Profit // Lower profit (loss) first
default:
return list[i].TotalSpending > list[j].TotalSpending
}
})
// Pagination on the result list
startIdx := (req.Page - 1) * req.PageSize
if startIdx >= len(list) {
startIdx = len(list)
}
endIdx := startIdx + req.PageSize
if endIdx > len(list) {
endIdx = len(list)
}
finalList := list[startIdx:endIdx]
if finalList == nil {
finalList = []spendingLeaderboardItem{}
}
ctx.Payload(&spendingLeaderboardResponse{
Page: req.Page,
PageSize: req.PageSize,
Total: int64(len(list)), // Total of the fetched top batch
List: finalList,
})
}
}

View File

@ -277,12 +277,23 @@ func (h *handler) ListPayOrders() core.HandlerFunc {
} }
} }
type adminOrderPointsLedgerItem struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Action string `json:"action"`
Points int64 `json:"points"`
RefTable string `json:"ref_table"`
RefID string `json:"ref_id"`
Remark string `json:"remark"`
CreatedAt string `json:"created_at"`
}
type getPayOrderResponse struct { type getPayOrderResponse struct {
Order *model.Orders `json:"order"` Order *model.Orders `json:"order"`
Items []*model.OrderItems `json:"items"` Items []*model.OrderItems `json:"items"`
Shipments []*model.ShippingRecords `json:"shipments"` Shipments []*model.ShippingRecords `json:"shipments"`
Ledgers []*model.UserPointsLedger `json:"ledgers"` Ledgers []adminOrderPointsLedgerItem `json:"ledgers"`
User *model.Users `json:"user"` User *model.Users `json:"user"`
Coupons []*struct { Coupons []*struct {
UserCouponID int64 `json:"user_coupon_id"` UserCouponID int64 `json:"user_coupon_id"`
AppliedAmount int64 `json:"applied_amount"` AppliedAmount int64 `json:"applied_amount"`
@ -644,7 +655,19 @@ func (h *handler) GetPayOrderDetail() core.HandlerFunc {
rsp.Items = items rsp.Items = items
rsp.Coupons = couponList rsp.Coupons = couponList
rsp.Shipments = shipments rsp.Shipments = shipments
rsp.Ledgers = ledgers rsp.Ledgers = make([]adminOrderPointsLedgerItem, len(ledgers))
for i, l := range ledgers {
rsp.Ledgers[i] = adminOrderPointsLedgerItem{
ID: l.ID,
UserID: l.UserID,
Action: l.Action,
Points: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), l.Points)),
RefTable: l.RefTable,
RefID: l.RefID,
Remark: l.Remark,
CreatedAt: l.CreatedAt.Format("2006-01-02 15:04:05"),
}
}
if order.SourceType == 2 { if order.SourceType == 2 {
unit := int64(0) unit := int64(0)
if count > 0 { if count > 0 {

View File

@ -2,7 +2,6 @@ package admin
import ( import (
"bytes" "bytes"
"fmt"
"net/http" "net/http"
"time" "time"
@ -50,14 +49,7 @@ func (h *handler) ExportPayOrders() core.HandlerFunc {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 23001, err.Error())) ctx.AbortWithError(core.Error(http.StatusBadRequest, 23001, err.Error()))
return return
} }
var pointsRate int64 = 1
if cfgRate, _ := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).Where(h.readDB.SystemConfigs.ConfigKey.Eq("points_exchange_per_cent")).First(); cfgRate != nil {
var r int64
_, _ = fmt.Sscanf(cfgRate.ConfigValue, "%d", &r)
if r > 0 {
pointsRate = r
}
}
file := xlsx.NewFile() file := xlsx.NewFile()
sheet, _ := file.AddSheet("orders") sheet, _ := file.AddSheet("orders")
header := []string{"订单号", "用户ID", "来源", "状态", "总金额", "折扣", "积分抵扣(分)", "积分抵扣(积分)", "优惠券抵扣(分)", "实付", "支付时间", "创建时间"} header := []string{"订单号", "用户ID", "来源", "状态", "总金额", "折扣", "积分抵扣(分)", "积分抵扣(积分)", "优惠券抵扣(分)", "实付", "支付时间", "创建时间"}
@ -76,13 +68,17 @@ func (h *handler) ExportPayOrders() core.HandlerFunc {
} }
pa := o.PointsAmount pa := o.PointsAmount
if pa == 0 && consumePointsSum > 0 { if pa == 0 && consumePointsSum > 0 {
pa = consumePointsSum / pointsRate {
// Backwards compatibility if o.PointsAmount is missing
// If consumePointsSum is Cents, pa is Cents.
pa = consumePointsSum
}
} }
pu := int64(0) // pu is Points Unit
if consumePointsSum > 0 { pu := int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), consumePointsSum))
pu = consumePointsSum if pu == 0 && pa > 0 {
} else if pa > 0 { // If no ledger, try converting from Cents
pu = pa * pointsRate pu = int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), pa))
} }
ocs, _ := h.readDB.OrderCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.OrderCoupons.OrderID.Eq(o.ID)).Find() ocs, _ := h.readDB.OrderCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.OrderCoupons.OrderID.Eq(o.ID)).Find()
var couponApplied int64 var couponApplied int64

View File

@ -239,17 +239,33 @@ func (h *handler) CreateRefund() core.HandlerFunc {
} }
// 全额退款回退次数卡user_game_passes // 全额退款回退次数卡user_game_passes
// 解析订单 remark 中的 game_pass:xxx ID // 优先解析新格式: gp_use:ID:Count (支持多张卡、多数量)
reGamePass := regexp.MustCompile(`game_pass:(\d+)`) reGpNew := regexp.MustCompile(`gp_use:(\d+):(\d+)`)
gamePassMatches := reGamePass.FindStringSubmatch(order.Remark) matchesNew := reGpNew.FindAllStringSubmatch(order.Remark, -1)
if len(gamePassMatches) > 1 { if len(matchesNew) > 0 {
gamePassID, _ := strconv.ParseInt(gamePassMatches[1], 10, 64) for _, m := range matchesNew {
if gamePassID > 0 { gpID, _ := strconv.ParseInt(m[1], 10, 64)
// 恢复次数卡remaining +1, total_used -1 gpCount, _ := strconv.ParseInt(m[2], 10, 64)
if err := h.repo.GetDbW().Exec("UPDATE user_game_passes SET remaining = remaining + 1, total_used = GREATEST(total_used - 1, 0), updated_at = NOW(3) WHERE id = ?", gamePassID).Error; err != nil { if gpID > 0 && gpCount > 0 {
h.logger.Error(fmt.Sprintf("refund restore game_pass failed: order=%s game_pass_id=%d err=%v", order.OrderNo, gamePassID, err)) if err := h.repo.GetDbW().Exec("UPDATE user_game_passes SET remaining = remaining + ?, total_used = GREATEST(total_used - ?, 0), updated_at = NOW(3) WHERE id = ?", gpCount, gpCount, gpID).Error; err != nil {
} else { h.logger.Error(fmt.Sprintf("refund restore game_pass failed: order=%s gp_id=%d count=%d err=%v", order.OrderNo, gpID, gpCount, err))
h.logger.Info(fmt.Sprintf("refund restore game_pass success: order=%s game_pass_id=%d", order.OrderNo, gamePassID)) } else {
h.logger.Info(fmt.Sprintf("refund restore game_pass success: order=%s gp_id=%d count=%d", order.OrderNo, gpID, gpCount))
}
}
}
} else {
// 兼容旧格式: game_pass:ID (仅恢复 1 次)
reGamePass := regexp.MustCompile(`game_pass:(\d+)`)
gamePassMatches := reGamePass.FindStringSubmatch(order.Remark)
if len(gamePassMatches) > 1 {
gamePassID, _ := strconv.ParseInt(gamePassMatches[1], 10, 64)
if gamePassID > 0 {
if err := h.repo.GetDbW().Exec("UPDATE user_game_passes SET remaining = remaining + 1, total_used = GREATEST(total_used - 1, 0), updated_at = NOW(3) WHERE id = ?", gamePassID).Error; err != nil {
h.logger.Error(fmt.Sprintf("refund restore game_pass failed: order=%s game_pass_id=%d err=%v", order.OrderNo, gamePassID, err))
} else {
h.logger.Info(fmt.Sprintf("refund restore game_pass success: order=%s game_pass_id=%d", order.OrderNo, gamePassID))
}
} }
} }
} }

View File

@ -132,21 +132,20 @@ func (h *handler) ModifySystemCoupon() core.HandlerFunc {
} }
func (h *handler) DeleteSystemCoupon() core.HandlerFunc { func (h *handler) DeleteSystemCoupon() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
idStr := ctx.Param("coupon_id") idStr := ctx.Param("coupon_id")
id, _ := strconv.ParseInt(idStr, 10, 64) id, _ := strconv.ParseInt(idStr, 10, 64)
if ctx.SessionUserInfo().IsSuper != 1 { if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
return return
} }
uid := int64(ctx.SessionUserInfo().Id) set := map[string]any{"deleted_at": time.Now()}
set := map[string]any{"deleted_at": time.Now(), "deleted_by": uid} if _, err := h.writeDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemCoupons.ID.Eq(id)).Updates(set); err != nil {
if _, err := h.writeDB.SystemCoupons.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemCoupons.ID.Eq(id)).Updates(set); err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error()))
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) return
return }
} ctx.Payload(pcSimpleMessage{Message: "操作成功"})
ctx.Payload(pcSimpleMessage{Message: "操作成功"}) }
}
} }
type listSystemCouponsRequest struct { type listSystemCouponsRequest struct {

View File

@ -1,6 +1,7 @@
package admin package admin
import ( import (
"math"
"net/http" "net/http"
"strconv" "strconv"
"time" "time"
@ -150,7 +151,7 @@ func (h *handler) ListAppUsers() core.HandlerFunc {
Group(h.readDB.UserPoints.UserID). Group(h.readDB.UserPoints.UserID).
Scan(&bRes) Scan(&bRes)
for _, b := range bRes { for _, b := range bRes {
pointBalances[b.UserID] = b.Points pointBalances[b.UserID] = int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), b.Points))
} }
} }
@ -298,6 +299,65 @@ func (h *handler) ListAppUsers() core.HandlerFunc {
} }
} }
// 批量查询持有商品价值
inventoryValues := make(map[int64]int64)
if len(userIDs) > 0 {
type invResult struct {
UserID int64
Value int64
}
var invRes []invResult
h.readDB.UserInventory.WithContext(ctx.RequestContext()).ReadDB().
LeftJoin(h.readDB.Products, h.readDB.Products.ID.EqCol(h.readDB.UserInventory.ProductID)).
Select(h.readDB.UserInventory.UserID, h.readDB.Products.Price.Sum().As("value")).
Where(h.readDB.UserInventory.UserID.In(userIDs...)).
Where(h.readDB.UserInventory.Status.Eq(1)). // 1=持有
Group(h.readDB.UserInventory.UserID).
Scan(&invRes)
for _, r := range invRes {
inventoryValues[r.UserID] = r.Value
}
}
// 批量查询优惠券价值(余额之和)
couponValues := make(map[int64]int64)
if len(userIDs) > 0 {
type valResult struct {
UserID int64
Value int64
}
var vRes []valResult
h.readDB.UserCoupons.WithContext(ctx.RequestContext()).ReadDB().
Select(h.readDB.UserCoupons.UserID, h.readDB.UserCoupons.BalanceAmount.Sum().As("value")).
Where(h.readDB.UserCoupons.UserID.In(userIDs...)).
Where(h.readDB.UserCoupons.Status.Eq(1)).
Group(h.readDB.UserCoupons.UserID).
Scan(&vRes)
for _, v := range vRes {
couponValues[v.UserID] = v.Value
}
}
// 批量查询道具卡价值
itemCardValues := make(map[int64]int64)
if len(userIDs) > 0 {
type valResult struct {
UserID int64
Value int64
}
var vRes []valResult
h.readDB.UserItemCards.WithContext(ctx.RequestContext()).ReadDB().
LeftJoin(h.readDB.SystemItemCards, h.readDB.SystemItemCards.ID.EqCol(h.readDB.UserItemCards.CardID)).
Select(h.readDB.UserItemCards.UserID, h.readDB.SystemItemCards.Price.Sum().As("value")).
Where(h.readDB.UserItemCards.UserID.In(userIDs...)).
Where(h.readDB.UserItemCards.Status.Eq(1)).
Group(h.readDB.UserItemCards.UserID).
Scan(&vRes)
for _, v := range vRes {
itemCardValues[v.UserID] = v.Value
}
}
// 批量查询所有用户的邀请人昵称 // 批量查询所有用户的邀请人昵称
inviterNicknames := make(map[int64]string) inviterNicknames := make(map[int64]string)
inviterIDs := make([]int64, 0) inviterIDs := make([]int64, 0)
@ -318,6 +378,16 @@ func (h *handler) ListAppUsers() core.HandlerFunc {
rsp.Total = total rsp.Total = total
rsp.List = make([]adminUserItem, len(rows)) rsp.List = make([]adminUserItem, len(rows))
for i, v := range rows { for i, v := range rows {
pointsBal := pointBalances[v.ID]
invVal := inventoryValues[v.ID]
cpVal := couponValues[v.ID]
icVal := itemCardValues[v.ID]
gpCount := gamePassCounts[v.ID]
gtCount := gameTicketCounts[v.ID]
// 总资产估值逻辑:积分余额 + 商品价值 + 优惠券价值 + 道具卡价值 + 次数卡(2元/次) + 游戏资格(1元/场)
assetVal := pointsBal*100 + invVal + cpVal + icVal + gpCount*200 + gtCount*100
rsp.List[i] = adminUserItem{ rsp.List[i] = adminUserItem{
ID: v.ID, ID: v.ID,
Nickname: v.Nickname, Nickname: v.Nickname,
@ -329,7 +399,7 @@ func (h *handler) ListAppUsers() core.HandlerFunc {
DouyinID: v.DouyinID, DouyinID: v.DouyinID,
ChannelName: v.ChannelName, ChannelName: v.ChannelName,
ChannelCode: v.ChannelCode, ChannelCode: v.ChannelCode,
PointsBalance: pointBalances[v.ID], PointsBalance: pointsBal,
CouponsCount: couponCounts[v.ID], CouponsCount: couponCounts[v.ID],
ItemCardsCount: cardCounts[v.ID], ItemCardsCount: cardCounts[v.ID],
TodayConsume: todayConsume[v.ID], TodayConsume: todayConsume[v.ID],
@ -337,8 +407,10 @@ func (h *handler) ListAppUsers() core.HandlerFunc {
ThirtyDayConsume: thirtyDayConsume[v.ID], ThirtyDayConsume: thirtyDayConsume[v.ID],
TotalConsume: totalConsume[v.ID], TotalConsume: totalConsume[v.ID],
InviteCount: inviteCounts[v.ID], InviteCount: inviteCounts[v.ID],
GamePassCount: gamePassCounts[v.ID], GamePassCount: gpCount,
GameTicketCount: gameTicketCounts[v.ID], GameTicketCount: gtCount,
InventoryValue: invVal,
TotalAssetValue: assetVal,
} }
} }
ctx.Payload(rsp) ctx.Payload(rsp)
@ -410,8 +482,9 @@ type listOrdersResponse struct {
} }
type listInventoryRequest struct { type listInventoryRequest struct {
Page int `form:"page"` Page int `form:"page"`
PageSize int `form:"page_size"` PageSize int `form:"page_size"`
Keyword string `form:"keyword"` // 搜索关键词(商品名称)
} }
type listInventoryResponse struct { type listInventoryResponse struct {
Page int `json:"page"` Page int `json:"page"`
@ -485,6 +558,96 @@ func (h *handler) ListUserInventory() core.HandlerFunc {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递用户ID")) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递用户ID"))
return return
} }
// 处理分页参数
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
if req.PageSize > 100 {
req.PageSize = 100
}
// 如果有搜索关键词,使用带搜索的查询
if req.Keyword != "" {
// 联表查询以支持按商品名称搜索
ui := h.readDB.UserInventory
p := h.readDB.Products
// 首先统计符合条件的总数
countQ := h.readDB.UserInventory.WithContext(ctx.RequestContext()).ReadDB().
LeftJoin(p, p.ID.EqCol(ui.ProductID)).
Where(ui.UserID.Eq(userID)).
Where(p.Name.Like("%" + req.Keyword + "%"))
total, err := countQ.Count()
if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 20105, err.Error()))
return
}
// 查询资产数据
type inventoryRow struct {
ID int64
UserID int64
ProductID int64
OrderID int64
ActivityID int64
RewardID int64
Status int32
Remark string
CreatedAt string
UpdatedAt string
ProductName string
ProductImages string
ProductPrice int64
}
var rows []inventoryRow
err = h.repo.GetDbR().Raw(`
SELECT ui.id, ui.user_id, ui.product_id, ui.order_id, ui.activity_id, ui.reward_id,
ui.status, ui.remark, ui.created_at, ui.updated_at,
p.name as product_name, p.images_json as product_images, p.price as product_price
FROM user_inventory ui
LEFT JOIN products p ON p.id = ui.product_id
WHERE ui.user_id = ? AND p.name LIKE ?
ORDER BY ui.id DESC
LIMIT ? OFFSET ?
`, userID, "%"+req.Keyword+"%", req.PageSize, (req.Page-1)*req.PageSize).Scan(&rows).Error
if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 20105, err.Error()))
return
}
// 转换结果
items := make([]*user.InventoryWithProduct, len(rows))
for i, r := range rows {
items[i] = &user.InventoryWithProduct{
UserInventory: &model.UserInventory{
ID: r.ID,
UserID: r.UserID,
ProductID: r.ProductID,
OrderID: r.OrderID,
ActivityID: r.ActivityID,
RewardID: r.RewardID,
Status: r.Status,
Remark: r.Remark,
},
ProductName: r.ProductName,
ProductImages: r.ProductImages,
ProductPrice: r.ProductPrice,
}
}
rsp.Page = req.Page
rsp.PageSize = req.PageSize
rsp.Total = total
rsp.List = items
ctx.Payload(rsp)
return
}
// 无搜索关键词时使用原有逻辑
rows, total, err := h.userSvc.ListInventoryWithProduct(ctx.RequestContext(), userID, req.Page, req.PageSize) rows, total, err := h.userSvc.ListInventoryWithProduct(ctx.RequestContext(), userID, req.Page, req.PageSize)
if err != nil { if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 20105, err.Error())) ctx.AbortWithError(core.Error(http.StatusBadRequest, 20105, err.Error()))
@ -680,11 +843,22 @@ type listPointsRequest struct {
Page int `form:"page"` Page int `form:"page"`
PageSize int `form:"page_size"` PageSize int `form:"page_size"`
} }
type adminUserPointsLedgerItem struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Action string `json:"action"`
Points int64 `json:"points"`
RefTable string `json:"ref_table"`
RefID string `json:"ref_id"`
Remark string `json:"remark"`
CreatedAt string `json:"created_at"`
}
type listPointsResponse struct { type listPointsResponse struct {
Page int `json:"page"` Page int `json:"page"`
PageSize int `json:"page_size"` PageSize int `json:"page_size"`
Total int64 `json:"total"` Total int64 `json:"total"`
List []*model.UserPointsLedger `json:"list"` List []adminUserPointsLedgerItem `json:"list"`
} }
// ListUserPoints 查看用户积分记录 // ListUserPoints 查看用户积分记录
@ -721,7 +895,20 @@ func (h *handler) ListUserPoints() core.HandlerFunc {
rsp.Page = req.Page rsp.Page = req.Page
rsp.PageSize = req.PageSize rsp.PageSize = req.PageSize
rsp.Total = total rsp.Total = total
rsp.List = items // Convert ledger items
rsp.List = make([]adminUserPointsLedgerItem, len(items))
for i, v := range items {
rsp.List[i] = adminUserPointsLedgerItem{
ID: v.ID,
UserID: v.UserID,
Action: v.Action,
Points: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), v.Points)),
RefTable: v.RefTable,
RefID: v.RefID,
Remark: v.Remark,
CreatedAt: v.CreatedAt.Format("2006-01-02 15:04:05"),
}
}
ctx.Payload(rsp) ctx.Payload(rsp)
} }
} }
@ -751,6 +938,8 @@ type adminUserItem struct {
InviteCount int64 `json:"invite_count"` // 邀请人数 InviteCount int64 `json:"invite_count"` // 邀请人数
GamePassCount int64 `json:"game_pass_count"` // 次数卡数量 GamePassCount int64 `json:"game_pass_count"` // 次数卡数量
GameTicketCount int64 `json:"game_ticket_count"` // 游戏资格数量 GameTicketCount int64 `json:"game_ticket_count"` // 游戏资格数量
InventoryValue int64 `json:"inventory_value"` // 持有商品总价值
TotalAssetValue int64 `json:"total_asset_value"` // 总资产估值
} }
// ListAppUsers 管理端用户列表GetUserPointsBalance 查看用户积分余额 // ListAppUsers 管理端用户列表GetUserPointsBalance 查看用户积分余额
@ -777,16 +966,16 @@ func (h *handler) GetUserPointsBalance() core.HandlerFunc {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 20107, err.Error())) ctx.AbortWithError(core.Error(http.StatusBadRequest, 20107, err.Error()))
return return
} }
rsp.Balance = total rsp.Balance = int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), total))
ctx.Payload(rsp) ctx.Payload(rsp)
} }
} }
type addPointsRequest struct { type addPointsRequest struct {
Points int64 `json:"points"` // 正数=增加,负数=扣减 Points float64 `json:"points"` // 正数=增加,负数=扣减
Kind string `json:"kind"` Kind string `json:"kind"`
Remark string `json:"remark"` Remark string `json:"remark"`
ValidDays *int `json:"valid_days"` ValidDays *int `json:"valid_days"`
} }
type addPointsResponse struct { type addPointsResponse struct {
Success bool `json:"success"` Success bool `json:"success"`
@ -822,10 +1011,16 @@ func (h *handler) AddUserPoints() core.HandlerFunc {
return return
} }
// 将浮点数积分转换为分Cents
// 1 积分 = 1 元 = 100 分
// 使用 math.Round 避免精度问题
pointsCents := int64(math.Round(req.Points * 100))
// 如果是扣减积分,先检查余额 // 如果是扣减积分,先检查余额
if req.Points < 0 { if pointsCents < 0 {
balance, _ := h.userSvc.GetPointsBalance(ctx.RequestContext(), userID) balance, _ := h.userSvc.GetPointsBalance(ctx.RequestContext(), userID)
if balance+req.Points < 0 { deductCents := -pointsCents
if balance < deductCents {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 20108, "积分余额不足,无法扣减")) ctx.AbortWithError(core.Error(http.StatusBadRequest, 20108, "积分余额不足,无法扣减"))
return return
} }
@ -835,14 +1030,15 @@ func (h *handler) AddUserPoints() core.HandlerFunc {
var validEnd *time.Time var validEnd *time.Time
now := time.Now() now := time.Now()
// 只有增加积分时才设置有效期 // 只有增加积分时才设置有效期
if req.Points > 0 { if pointsCents > 0 {
validStart = &now validStart = &now
if req.ValidDays != nil && *req.ValidDays > 0 { if req.ValidDays != nil && *req.ValidDays > 0 {
ve := now.Add(time.Duration(*req.ValidDays) * 24 * time.Hour) ve := now.Add(time.Duration(*req.ValidDays) * 24 * time.Hour)
validEnd = &ve validEnd = &ve
} }
} }
if err := h.userSvc.AddPoints(ctx.RequestContext(), userID, req.Points, req.Kind, req.Remark, validStart, validEnd); err != nil {
if err := h.userSvc.AddPoints(ctx.RequestContext(), userID, pointsCents, req.Kind, req.Remark, validStart, validEnd); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 20108, err.Error())) ctx.AbortWithError(core.Error(http.StatusBadRequest, 20108, err.Error()))
return return
} }

View File

@ -1,144 +1,156 @@
package admin package admin
import ( import (
"net/http" "net/http"
"bindbox-game/internal/code" "bindbox-game/internal/code"
"bindbox-game/internal/pkg/core" "bindbox-game/internal/pkg/core"
"bindbox-game/internal/pkg/validation" "bindbox-game/internal/pkg/validation"
usersvc "bindbox-game/internal/service/user" usersvc "bindbox-game/internal/service/user"
) )
type batchPointsRequest struct { type batchPointsRequest struct {
Users []int64 `json:"users" binding:"required"` Users []int64 `json:"users" binding:"required"`
Amount int64 `json:"amount" binding:"min=1"` Amount int64 `json:"amount" binding:"min=1"`
Reason string `json:"reason"` Reason string `json:"reason"`
IdempotencyKey string `json:"idempotency_key"` IdempotencyKey string `json:"idempotency_key"`
} }
type batchCouponsRequest struct { type batchCouponsRequest struct {
Users []int64 `json:"users" binding:"required"` Users []int64 `json:"users" binding:"required"`
CouponID int64 `json:"coupon_id" binding:"required"` CouponID int64 `json:"coupon_id" binding:"required"`
QuantityPerUser int `json:"quantity_per_user"` QuantityPerUser int `json:"quantity_per_user"`
IdempotencyKey string `json:"idempotency_key"` IdempotencyKey string `json:"idempotency_key"`
} }
type batchRewardsRequest struct { type batchRewardsRequest struct {
Users []int64 `json:"users" binding:"required"` Users []int64 `json:"users" binding:"required"`
ProductID int64 `json:"product_id" binding:"required"` ProductID int64 `json:"product_id" binding:"required"`
Quantity int `json:"quantity" binding:"min=1"` Quantity int `json:"quantity" binding:"min=1"`
ActivityID *int64 `json:"activity_id"` ActivityID *int64 `json:"activity_id"`
RewardID *int64 `json:"reward_id"` RewardID *int64 `json:"reward_id"`
AddressID *int64 `json:"address_id"` AddressID *int64 `json:"address_id"`
Remark string `json:"remark"` Remark string `json:"remark"`
IdempotencyKey string `json:"idempotency_key"` IdempotencyKey string `json:"idempotency_key"`
} }
type batchItemResult struct { type batchItemResult struct {
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`
Status string `json:"status"` Status string `json:"status"`
Message string `json:"message"` Message string `json:"message"`
} }
type batchResponse struct { type batchResponse struct {
Success int `json:"success"` Success int `json:"success"`
Failed int `json:"failed"` Failed int `json:"failed"`
Details []batchItemResult `json:"details"` Details []batchItemResult `json:"details"`
} }
func (h *handler) BatchAddUserPoints() core.HandlerFunc { func (h *handler) BatchAddUserPoints() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
if ctx.SessionUserInfo().IsSuper != 1 { if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
return return
} }
req := new(batchPointsRequest) req := new(batchPointsRequest)
if err := ctx.ShouldBindJSON(req); err != nil { if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return return
} }
if len(req.Users) != 1 { if len(req.Users) != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作请仅选择1位用户")) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作请仅选择1位用户"))
return return
} }
res := &batchResponse{Details: make([]batchItemResult, 0, len(req.Users))} res := &batchResponse{Details: make([]batchItemResult, 0, len(req.Users))}
for _, uid := range req.Users { // 将管理员输入的积分转换为分
if err := h.userSvc.AddPoints(ctx.RequestContext(), uid, req.Amount, "manual", req.Reason, nil, nil); err != nil { amountCents, _ := h.userSvc.PointsToCents(ctx.RequestContext(), req.Amount)
res.Failed++ for _, uid := range req.Users {
res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed", Message: err.Error()}) if err := h.userSvc.AddPoints(ctx.RequestContext(), uid, amountCents, "manual", req.Reason, nil, nil); err != nil {
} else { res.Failed++
res.Success++ res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed", Message: err.Error()})
res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"}) } else {
} res.Success++
} res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"})
ctx.Payload(res) }
} }
ctx.Payload(res)
}
} }
func (h *handler) BatchAddUserCoupons() core.HandlerFunc { func (h *handler) BatchAddUserCoupons() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
if ctx.SessionUserInfo().IsSuper != 1 { if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
return return
} }
req := new(batchCouponsRequest) req := new(batchCouponsRequest)
if err := ctx.ShouldBindJSON(req); err != nil { if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return return
} }
if len(req.Users) != 1 { if len(req.Users) != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作请仅选择1位用户")) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作请仅选择1位用户"))
return return
} }
if req.QuantityPerUser <= 0 { req.QuantityPerUser = 1 } if req.QuantityPerUser <= 0 {
if req.QuantityPerUser > 5 { req.QuantityPerUser = 5 } req.QuantityPerUser = 1
res := &batchResponse{Details: make([]batchItemResult, 0, len(req.Users))} }
for _, uid := range req.Users { if req.QuantityPerUser > 5 {
ok := true req.QuantityPerUser = 5
for i := 0; i < req.QuantityPerUser; i++ { }
if err := h.userSvc.AddCoupon(ctx.RequestContext(), uid, req.CouponID); err != nil { ok = false } res := &batchResponse{Details: make([]batchItemResult, 0, len(req.Users))}
} for _, uid := range req.Users {
if ok { ok := true
res.Success++ for i := 0; i < req.QuantityPerUser; i++ {
res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"}) if err := h.userSvc.AddCoupon(ctx.RequestContext(), uid, req.CouponID); err != nil {
} else { ok = false
res.Failed++ }
res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed"}) }
} if ok {
} res.Success++
ctx.Payload(res) res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"})
} } else {
res.Failed++
res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed"})
}
}
ctx.Payload(res)
}
} }
func (h *handler) BatchGrantUserRewards() core.HandlerFunc { func (h *handler) BatchGrantUserRewards() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
if ctx.SessionUserInfo().IsSuper != 1 { if ctx.SessionUserInfo().IsSuper != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作"))
return return
} }
req := new(batchRewardsRequest) req := new(batchRewardsRequest)
if err := ctx.ShouldBindJSON(req); err != nil { if err := ctx.ShouldBindJSON(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return return
} }
if len(req.Users) != 1 { if len(req.Users) != 1 {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作请仅选择1位用户")) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作请仅选择1位用户"))
return return
} }
if req.Quantity <= 0 { req.Quantity = 1 } if req.Quantity <= 0 {
if req.Quantity > 10 { req.Quantity = 10 } req.Quantity = 1
res := &batchResponse{Details: make([]batchItemResult, 0, len(req.Users))} }
r := usersvc.GrantRewardRequest{ProductID: req.ProductID, Quantity: req.Quantity, ActivityID: req.ActivityID, RewardID: req.RewardID, AddressID: req.AddressID, Remark: req.Remark} if req.Quantity > 10 {
for _, uid := range req.Users { req.Quantity = 10
_, err := h.userSvc.GrantReward(ctx.RequestContext(), uid, r) }
if err != nil { res := &batchResponse{Details: make([]batchItemResult, 0, len(req.Users))}
res.Failed++ r := usersvc.GrantRewardRequest{ProductID: req.ProductID, Quantity: req.Quantity, ActivityID: req.ActivityID, RewardID: req.RewardID, AddressID: req.AddressID, Remark: req.Remark}
res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed", Message: err.Error()}) for _, uid := range req.Users {
} else { _, err := h.userSvc.GrantReward(ctx.RequestContext(), uid, r)
res.Success++ if err != nil {
res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"}) res.Failed++
} res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed", Message: err.Error()})
} } else {
ctx.Payload(res) res.Success++
} res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"})
}
}
ctx.Payload(res)
}
} }

View File

@ -104,11 +104,11 @@ func (h *handler) GetUserProfile() core.HandlerFunc {
// 3. 生命周期财务指标 // 3. 生命周期财务指标
// 3.1 消费统计 // 3.1 消费统计
type orderStats struct { type orderStats struct {
TotalPaid int64 TotalPaid *int64
OrderCount int64 OrderCount int64
TodayPaid int64 TodayPaid *int64
SevenDayPaid int64 SevenDayPaid *int64
ThirtyDayPaid int64 ThirtyDayPaid *int64
} }
var os orderStats var os orderStats
now := time.Now() now := time.Now()
@ -147,11 +147,19 @@ func (h *handler) GetUserProfile() core.HandlerFunc {
Where(h.readDB.Orders.CreatedAt.Gte(thirtyDayStart)). Where(h.readDB.Orders.CreatedAt.Gte(thirtyDayStart)).
Scan(&os.ThirtyDayPaid) Scan(&os.ThirtyDayPaid)
rsp.LifetimeStats.TotalPaid = os.TotalPaid if os.TotalPaid != nil {
rsp.LifetimeStats.TotalPaid = *os.TotalPaid
}
rsp.LifetimeStats.OrderCount = os.OrderCount rsp.LifetimeStats.OrderCount = os.OrderCount
rsp.LifetimeStats.TodayPaid = os.TodayPaid if os.TodayPaid != nil {
rsp.LifetimeStats.SevenDayPaid = os.SevenDayPaid rsp.LifetimeStats.TodayPaid = *os.TodayPaid
rsp.LifetimeStats.ThirtyDayPaid = os.ThirtyDayPaid }
if os.SevenDayPaid != nil {
rsp.LifetimeStats.SevenDayPaid = *os.SevenDayPaid
}
if os.ThirtyDayPaid != nil {
rsp.LifetimeStats.ThirtyDayPaid = *os.ThirtyDayPaid
}
// 3.2 累计退款 // 3.2 累计退款
var totalRefunded int64 var totalRefunded int64

View File

@ -94,12 +94,16 @@ func (h *handler) GetUserProfitLossTrend() core.HandlerFunc {
// --- 2. 获取订单数据(仅 status=2 已支付) --- // --- 2. 获取订单数据(仅 status=2 已支付) ---
// 注意:为了计算累计趋势,我们需要获取 start 之前的所有已支付订单总额作为基数 // 注意:为了计算累计趋势,我们需要获取 start 之前的所有已支付订单总额作为基数
var baseCost int64 = 0 var baseCost int64 = 0
_ = h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB(). var baseCostPtr *int64
h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().
Select(h.readDB.Orders.ActualAmount.Sum()). Select(h.readDB.Orders.ActualAmount.Sum()).
Where(h.readDB.Orders.UserID.Eq(userID)). Where(h.readDB.Orders.UserID.Eq(userID)).
Where(h.readDB.Orders.Status.Eq(2)). Where(h.readDB.Orders.Status.Eq(2)).
Where(h.readDB.Orders.CreatedAt.Lt(start)). Where(h.readDB.Orders.CreatedAt.Lt(start)).
Scan(&baseCost) Scan(&baseCostPtr)
if baseCostPtr != nil {
baseCost = *baseCostPtr
}
// 扣除历史退款 (如果有的话,此处简化处理,主要关注当前范围内的波动) // 扣除历史退款 (如果有的话,此处简化处理,主要关注当前范围内的波动)
var baseRefund int64 = 0 var baseRefund int64 = 0
@ -186,11 +190,15 @@ func (h *handler) GetUserProfitLossTrend() core.HandlerFunc {
// 汇总数据 // 汇总数据
var totalCost int64 = 0 var totalCost int64 = 0
_ = h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB(). var totalCostPtr *int64
h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().
Select(h.readDB.Orders.ActualAmount.Sum()). Select(h.readDB.Orders.ActualAmount.Sum()).
Where(h.readDB.Orders.UserID.Eq(userID)). Where(h.readDB.Orders.UserID.Eq(userID)).
Where(h.readDB.Orders.Status.Eq(2)). Where(h.readDB.Orders.Status.Eq(2)).
Scan(&totalCost) Scan(&totalCostPtr)
if totalCostPtr != nil {
totalCost = *totalCostPtr
}
var totalRefund int64 = 0 var totalRefund int64 = 0
_ = h.repo.GetDbR().Raw(` _ = h.repo.GetDbR().Raw(`
@ -227,3 +235,294 @@ func (h *handler) GetUserProfitLossTrend() core.HandlerFunc {
ctx.Payload(resp) ctx.Payload(resp)
} }
} }
// 盈亏明细请求
type profitLossDetailsRequest struct {
Page int `form:"page"`
PageSize int `form:"page_size"`
RangeType string `form:"rangeType"`
}
// 盈亏明细项
type profitLossDetailItem struct {
OrderID int64 `json:"order_id"`
OrderNo string `json:"order_no"`
CreatedAt string `json:"created_at"`
SourceType int32 `json:"source_type"` // 来源类型 1商城 2抽奖 3系统
ActivityName string `json:"activity_name"` // 活动名称
ActualAmount int64 `json:"actual_amount"` // 实际支付金额(分)
RefundAmount int64 `json:"refund_amount"` // 退款金额(分)
NetCost int64 `json:"net_cost"` // 净投入(分)
PrizeValue int64 `json:"prize_value"` // 获得奖品价值(分)
PrizeName string `json:"prize_name"` // 奖品名称
PointsEarned int64 `json:"points_earned"` // 获得积分
PointsValue int64 `json:"points_value"` // 积分价值(分)
CouponUsedValue int64 `json:"coupon_used_value"` // 使用优惠券价值(分)
CouponUsedName string `json:"coupon_used_name"` // 使用的优惠券名称
ItemCardUsed string `json:"item_card_used"` // 使用的道具卡名称
ItemCardValue int64 `json:"item_card_value"` // 道具卡价值(分)
NetProfit int64 `json:"net_profit"` // 净盈亏
}
// 盈亏明细响应
type profitLossDetailsResponse struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int64 `json:"total"`
List []profitLossDetailItem `json:"list"`
Summary struct {
TotalCost int64 `json:"total_cost"`
TotalValue int64 `json:"total_value"`
TotalProfit int64 `json:"total_profit"`
} `json:"summary"`
}
// GetUserProfitLossDetails 获取用户盈亏明细
// @Summary 获取用户盈亏明细
// @Description 获取用户每笔订单的详细盈亏信息
// @Tags 管理端.用户
// @Accept json
// @Produce json
// @Param user_id path integer true "用户ID"
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(20)
// @Param rangeType query string false "时间范围" default("all")
// @Success 200 {object} profitLossDetailsResponse
// @Failure 400 {object} code.Failure
// @Router /api/admin/users/{user_id}/stats/profit_loss_details [get]
// @Security LoginVerifyToken
func (h *handler) GetUserProfitLossDetails() core.HandlerFunc {
return func(ctx core.Context) {
userID, err := strconv.ParseInt(ctx.Param("user_id"), 10, 64)
if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递用户ID"))
return
}
req := new(profitLossDetailsRequest)
if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return
}
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 20
}
if req.PageSize > 100 {
req.PageSize = 100
}
// 解析时间范围
start, end := parseRange(req.RangeType, "", "")
if req.RangeType == "all" {
u, _ := h.readDB.Users.WithContext(ctx.RequestContext()).Where(h.readDB.Users.ID.Eq(userID)).First()
if u != nil {
start = u.CreatedAt
} else {
start = time.Date(2025, 1, 1, 0, 0, 0, 0, time.Local)
}
}
// 查询订单总数
orderQ := h.readDB.Orders.WithContext(ctx.RequestContext()).ReadDB().
Where(h.readDB.Orders.UserID.Eq(userID)).
Where(h.readDB.Orders.Status.Eq(2)).
Where(h.readDB.Orders.CreatedAt.Gte(start)).
Where(h.readDB.Orders.CreatedAt.Lte(end))
total, err := orderQ.Count()
if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 20108, err.Error()))
return
}
// 分页查询订单
orders, err := orderQ.Order(h.readDB.Orders.CreatedAt.Desc()).
Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Find()
if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 20108, err.Error()))
return
}
// 收集订单ID
orderIDs := make([]int64, len(orders))
orderNos := make([]string, len(orders))
for i, o := range orders {
orderIDs[i] = o.ID
orderNos[i] = o.OrderNo
}
// 批量查询退款信息
refundMap := make(map[string]int64)
if len(orderNos) > 0 {
type refundRow struct {
OrderNo string
Amount int64
}
var refunds []refundRow
_ = h.repo.GetDbR().Raw(`
SELECT order_no, COALESCE(SUM(amount_refund), 0) as amount
FROM payment_refunds
WHERE order_no IN ? AND status = 'SUCCESS'
GROUP BY order_no
`, orderNos).Scan(&refunds).Error
for _, r := range refunds {
refundMap[r.OrderNo] = r.Amount
}
}
// 批量查询库存价值(获得的奖品)
prizeValueMap := make(map[int64]int64)
prizeNameMap := make(map[int64]string)
if len(orderIDs) > 0 {
type prizeRow struct {
OrderID int64
Value int64
Name string
}
var prizes []prizeRow
_ = h.repo.GetDbR().Raw(`
SELECT ui.order_id, COALESCE(SUM(p.price), 0) as value,
GROUP_CONCAT(p.name SEPARATOR ', ') as name
FROM user_inventory ui
LEFT JOIN products p ON p.id = ui.product_id
WHERE ui.order_id IN ?
GROUP BY ui.order_id
`, orderIDs).Scan(&prizes).Error
for _, p := range prizes {
prizeValueMap[p.OrderID] = p.Value
prizeNameMap[p.OrderID] = p.Name
}
}
// 批量查询使用的优惠券
couponValueMap := make(map[int64]int64)
couponNameMap := make(map[int64]string)
if len(orderIDs) > 0 {
type couponRow struct {
OrderID int64
Value int64
Name string
}
var coupons []couponRow
_ = h.repo.GetDbR().Raw(`
SELECT ucu.order_id, COALESCE(SUM(ABS(ucu.change_amount)), 0) as value,
GROUP_CONCAT(DISTINCT sc.name SEPARATOR ', ') as name
FROM user_coupon_usage ucu
LEFT JOIN user_coupons uc ON uc.id = ucu.user_coupon_id
LEFT JOIN system_coupons sc ON sc.id = uc.coupon_id
WHERE ucu.order_id IN ?
GROUP BY ucu.order_id
`, orderIDs).Scan(&coupons).Error
for _, c := range coupons {
couponValueMap[c.OrderID] = c.Value
couponNameMap[c.OrderID] = c.Name
}
}
// 批量查询活动信息
activityNameMap := make(map[int64]string)
if len(orderIDs) > 0 {
type actRow struct {
OrderID int64
ActivityName string
}
var acts []actRow
_ = h.repo.GetDbR().Raw(`
SELECT o.id as order_id, a.name as activity_name
FROM orders o
LEFT JOIN activities a ON a.id = o.activity_id
WHERE o.id IN ? AND o.activity_id > 0
`, orderIDs).Scan(&acts).Error
for _, a := range acts {
activityNameMap[a.OrderID] = a.ActivityName
}
}
// 组装明细数据
list := make([]profitLossDetailItem, len(orders))
var totalCost, totalValue int64
for i, o := range orders {
refund := refundMap[o.OrderNo]
prizeValue := prizeValueMap[o.ID]
couponValue := couponValueMap[o.ID]
netCost := o.ActualAmount - refund
netProfit := prizeValue - netCost
list[i] = profitLossDetailItem{
OrderID: o.ID,
OrderNo: o.OrderNo,
CreatedAt: o.CreatedAt.Format("2006-01-02 15:04:05"),
SourceType: o.SourceType,
ActivityName: activityNameMap[o.ID],
ActualAmount: o.ActualAmount,
RefundAmount: refund,
NetCost: netCost,
PrizeValue: prizeValue,
PrizeName: prizeNameMap[o.ID],
PointsEarned: 0, // 简化处理
PointsValue: 0,
CouponUsedValue: couponValue,
CouponUsedName: couponNameMap[o.ID],
ItemCardUsed: "", // 从订单备注中解析
ItemCardValue: 0,
NetProfit: netProfit,
}
// 解析道具卡信息(从订单备注)
if o.Remark != "" {
list[i].ItemCardUsed = parseItemCardFromRemark(o.Remark)
}
totalCost += netCost
totalValue += prizeValue
}
resp := profitLossDetailsResponse{
Page: req.Page,
PageSize: req.PageSize,
Total: total,
List: list,
}
resp.Summary.TotalCost = totalCost
resp.Summary.TotalValue = totalValue
resp.Summary.TotalProfit = totalValue - totalCost
ctx.Payload(resp)
}
}
// 从订单备注中解析道具卡信息
func parseItemCardFromRemark(remark string) string {
// 格式: itemcard:xxx|...
if len(remark) == 0 {
return ""
}
idx := 0
for i := 0; i < len(remark); i++ {
if remark[i:] == "itemcard:" || (i+9 <= len(remark) && remark[i:i+9] == "itemcard:") {
idx = i
break
}
}
if idx == 0 && len(remark) < 9 {
return ""
}
if idx+9 >= len(remark) {
return ""
}
seg := remark[idx+9:]
// 找到 | 分隔符
end := len(seg)
for i := 0; i < len(seg); i++ {
if seg[i] == '|' {
end = i
break
}
}
return seg[:end]
}

View File

@ -38,13 +38,13 @@ type listAppProductsRequest struct {
} }
type listAppProductsItem struct { type listAppProductsItem struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Name string `json:"name"` Name string `json:"name"`
MainImage string `json:"main_image"` MainImage string `json:"main_image"`
Price int64 `json:"price"` Price int64 `json:"price"`
PointsRequired int64 `json:"points_required"` PointsRequired float64 `json:"points_required"` // 积分(分/rate`
Sales int64 `json:"sales"` Sales int64 `json:"sales"`
InStock bool `json:"in_stock"` InStock bool `json:"in_stock"`
} }
type listAppProductsResponse struct { type listAppProductsResponse struct {
@ -87,7 +87,7 @@ func (h *productHandler) ListProductsForApp() core.HandlerFunc {
} }
rsp := &listAppProductsResponse{Total: total, CurrentPage: req.Page, PageSize: req.PageSize, List: make([]listAppProductsItem, len(items))} rsp := &listAppProductsResponse{Total: total, CurrentPage: req.Page, PageSize: req.PageSize, List: make([]listAppProductsItem, len(items))}
for i, it := range items { for i, it := range items {
pts, _ := h.user.CentsToPoints(ctx.RequestContext(), it.Price) pts := h.user.CentsToPointsFloat(ctx.RequestContext(), it.Price)
rsp.List[i] = listAppProductsItem{ID: it.ID, Name: it.Name, MainImage: it.MainImage, Price: it.Price, PointsRequired: pts, Sales: it.Sales, InStock: it.InStock} rsp.List[i] = listAppProductsItem{ID: it.ID, Name: it.Name, MainImage: it.MainImage, Price: it.Price, PointsRequired: pts, Sales: it.Sales, InStock: it.InStock}
} }
ctx.Payload(rsp) ctx.Payload(rsp)
@ -99,7 +99,7 @@ type getAppProductDetailResponse struct {
Name string `json:"name"` Name string `json:"name"`
Album []string `json:"album"` Album []string `json:"album"`
Price int64 `json:"price"` Price int64 `json:"price"`
PointsRequired int64 `json:"points_required"` PointsRequired float64 `json:"points_required"` // 积分(分/rate`
Sales int64 `json:"sales"` Sales int64 `json:"sales"`
Stock int64 `json:"stock"` Stock int64 `json:"stock"`
Description string `json:"description"` Description string `json:"description"`
@ -135,10 +135,10 @@ func (h *productHandler) GetProductDetailForApp() core.HandlerFunc {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, validation.Error(err))) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ServerError, validation.Error(err)))
return return
} }
ptsDetail, _ := h.user.CentsToPoints(ctx.RequestContext(), d.Price) ptsDetail := h.user.CentsToPointsFloat(ctx.RequestContext(), d.Price)
rsp := &getAppProductDetailResponse{ID: d.ID, Name: d.Name, Album: d.Album, Price: d.Price, PointsRequired: ptsDetail, Sales: d.Sales, Stock: d.Stock, Description: d.Description, Service: d.Service, Recommendations: make([]listAppProductsItem, len(d.Recommendations))} rsp := &getAppProductDetailResponse{ID: d.ID, Name: d.Name, Album: d.Album, Price: d.Price, PointsRequired: ptsDetail, Sales: d.Sales, Stock: d.Stock, Description: d.Description, Service: d.Service, Recommendations: make([]listAppProductsItem, len(d.Recommendations))}
for i, it := range d.Recommendations { for i, it := range d.Recommendations {
ptsRec, _ := h.user.CentsToPoints(ctx.RequestContext(), it.Price) ptsRec := h.user.CentsToPointsFloat(ctx.RequestContext(), it.Price)
rsp.Recommendations[i] = listAppProductsItem{ID: it.ID, Name: it.Name, MainImage: it.MainImage, Price: it.Price, PointsRequired: ptsRec, Sales: it.Sales, InStock: it.InStock} rsp.Recommendations[i] = listAppProductsItem{ID: it.ID, Name: it.Name, MainImage: it.MainImage, Price: it.Price, PointsRequired: ptsRec, Sales: it.Sales, InStock: it.InStock}
} }
ctx.Payload(rsp) ctx.Payload(rsp)

View File

@ -25,21 +25,24 @@ type listStoreItemsRequest struct {
Kind string `form:"kind"` Kind string `form:"kind"`
Page int `form:"page"` Page int `form:"page"`
PageSize int `form:"page_size"` PageSize int `form:"page_size"`
Keyword string `form:"keyword"` // 关键词搜索
PriceMin *int64 `form:"price_min"` // 最低积分价格(积分单位)
PriceMax *int64 `form:"price_max"` // 最高积分价格(积分单位)
} }
type listStoreItem struct { type listStoreItem struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Kind string `json:"kind"` Kind string `json:"kind"`
Name string `json:"name"` Name string `json:"name"`
MainImage string `json:"main_image"` MainImage string `json:"main_image"`
Price int64 `json:"price"` Price int64 `json:"price"`
PointsRequired int64 `json:"points_required"` PointsRequired float64 `json:"points_required"` // 积分(分/rate`
InStock bool `json:"in_stock"` InStock bool `json:"in_stock"`
Status int32 `json:"status"` Status int32 `json:"status"`
DiscountType int32 `json:"discount_type"` DiscountType int32 `json:"discount_type"`
DiscountValue int64 `json:"discount_value"` DiscountValue int64 `json:"discount_value"`
MinSpend int64 `json:"min_spend"` MinSpend int64 `json:"min_spend"`
Supported bool `json:"supported"` Supported bool `json:"supported"`
} }
type listStoreItemsResponse struct { type listStoreItemsResponse struct {
@ -83,32 +86,76 @@ func (h *storeHandler) ListStoreItemsForApp() core.HandlerFunc {
offset := (req.Page - 1) * req.PageSize offset := (req.Page - 1) * req.PageSize
limit := req.PageSize limit := req.PageSize
// 将积分价格转换为分进行查询
var priceMinCents, priceMaxCents int64
if req.PriceMin != nil && *req.PriceMin > 0 {
centsVal, _ := h.user.PointsToCents(ctx.RequestContext(), *req.PriceMin)
priceMinCents = centsVal
}
if req.PriceMax != nil && *req.PriceMax > 0 {
centsVal, _ := h.user.PointsToCents(ctx.RequestContext(), *req.PriceMax)
priceMaxCents = centsVal
}
switch req.Kind { switch req.Kind {
case "item_card": case "item_card":
q := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemItemCards.Status.Eq(1)) q := h.readDB.SystemItemCards.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemItemCards.Status.Eq(1))
// 关键词筛选
if req.Keyword != "" {
q = q.Where(h.readDB.SystemItemCards.Name.Like("%" + req.Keyword + "%"))
}
// 价格区间筛选
if priceMinCents > 0 {
q = q.Where(h.readDB.SystemItemCards.Price.Gte(priceMinCents))
}
if priceMaxCents > 0 {
q = q.Where(h.readDB.SystemItemCards.Price.Lte(priceMaxCents))
}
total, _ = q.Count() total, _ = q.Count()
rows, _ := q.Order(h.readDB.SystemItemCards.ID.Desc()).Offset(offset).Limit(limit).Find() rows, _ := q.Order(h.readDB.SystemItemCards.ID.Desc()).Offset(offset).Limit(limit).Find()
list = make([]listStoreItem, len(rows)) list = make([]listStoreItem, len(rows))
for i, it := range rows { for i, it := range rows {
pts, _ := h.user.CentsToPoints(ctx.RequestContext(), it.Price) pts := h.user.CentsToPointsFloat(ctx.RequestContext(), it.Price)
list[i] = listStoreItem{ID: it.ID, Kind: "item_card", Name: it.Name, Price: it.Price, PointsRequired: pts, Status: it.Status, Supported: true} list[i] = listStoreItem{ID: it.ID, Kind: "item_card", Name: it.Name, Price: it.Price, PointsRequired: pts, Status: it.Status, Supported: true}
} }
case "coupon": case "coupon":
q := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemCoupons.Status.Eq(1)) q := h.readDB.SystemCoupons.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.SystemCoupons.Status.Eq(1))
// 关键词筛选
if req.Keyword != "" {
q = q.Where(h.readDB.SystemCoupons.Name.Like("%" + req.Keyword + "%"))
}
// 价格区间筛选 (优惠券用 DiscountValue)
if priceMinCents > 0 {
q = q.Where(h.readDB.SystemCoupons.DiscountValue.Gte(priceMinCents))
}
if priceMaxCents > 0 {
q = q.Where(h.readDB.SystemCoupons.DiscountValue.Lte(priceMaxCents))
}
total, _ = q.Count() total, _ = q.Count()
rows, _ := q.Order(h.readDB.SystemCoupons.ID.Desc()).Offset(offset).Limit(limit).Find() rows, _ := q.Order(h.readDB.SystemCoupons.ID.Desc()).Offset(offset).Limit(limit).Find()
list = make([]listStoreItem, len(rows)) list = make([]listStoreItem, len(rows))
for i, it := range rows { for i, it := range rows {
pts, _ := h.user.CentsToPoints(ctx.RequestContext(), it.DiscountValue) pts := h.user.CentsToPointsFloat(ctx.RequestContext(), it.DiscountValue)
list[i] = listStoreItem{ID: it.ID, Kind: "coupon", Name: it.Name, DiscountType: it.DiscountType, DiscountValue: it.DiscountValue, PointsRequired: pts, MinSpend: it.MinSpend, Status: it.Status, Supported: it.DiscountType == 1} list[i] = listStoreItem{ID: it.ID, Kind: "coupon", Name: it.Name, DiscountType: it.DiscountType, DiscountValue: it.DiscountValue, PointsRequired: pts, MinSpend: it.MinSpend, Status: it.Status, Supported: it.DiscountType == 1}
} }
default: // product default: // product
q := h.readDB.Products.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Products.Status.Eq(1)) q := h.readDB.Products.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.Products.Status.Eq(1))
// 关键词筛选
if req.Keyword != "" {
q = q.Where(h.readDB.Products.Name.Like("%" + req.Keyword + "%"))
}
// 价格区间筛选
if priceMinCents > 0 {
q = q.Where(h.readDB.Products.Price.Gte(priceMinCents))
}
if priceMaxCents > 0 {
q = q.Where(h.readDB.Products.Price.Lte(priceMaxCents))
}
total, _ = q.Count() total, _ = q.Count()
rows, _ := q.Order(h.readDB.Products.ID.Desc()).Offset(offset).Limit(limit).Find() rows, _ := q.Order(h.readDB.Products.ID.Desc()).Offset(offset).Limit(limit).Find()
list = make([]listStoreItem, len(rows)) list = make([]listStoreItem, len(rows))
for i, it := range rows { for i, it := range rows {
pts, _ := h.user.CentsToPoints(ctx.RequestContext(), it.Price) pts := h.user.CentsToPointsFloat(ctx.RequestContext(), it.Price)
list[i] = listStoreItem{ID: it.ID, Kind: "product", Name: it.Name, MainImage: parseFirstImage(it.ImagesJSON), Price: it.Price, PointsRequired: pts, InStock: it.Stock > 0 && it.Status == 1, Status: it.Status, Supported: true} list[i] = listStoreItem{ID: it.ID, Kind: "product", Name: it.Name, MainImage: parseFirstImage(it.ImagesJSON), Price: it.Price, PointsRequired: pts, InStock: it.Stock > 0 && it.Status == 1, Status: it.Status, Supported: true}
} }
} }

View File

@ -0,0 +1,38 @@
package common
import (
"bindbox-game/internal/pkg/core"
)
type ConfigResponse struct {
SubscribeTemplates map[string]string `json:"subscribe_templates"`
}
// GetPublicConfig 获取公开配置(包含订阅模板ID)
// @Summary 获取公开配置
// @Description 获取小程序前端需要用到的公开配置如订阅消息模板ID
// @Tags 公共
// @Accept json
// @Produce json
// @Success 200 {object} ConfigResponse
// @Router /api/app/config/public [get]
func (h *handler) GetPublicConfig() core.HandlerFunc {
return func(ctx core.Context) {
// 查询订阅消息模板 ID
var val string
cfg, err := h.readDB.SystemConfigs.WithContext(ctx.RequestContext()).
Where(h.readDB.SystemConfigs.ConfigKey.Eq("wechat.lottery_result_template_id")).
First()
if err == nil && cfg != nil {
val = cfg.ConfigValue
}
rsp := ConfigResponse{
SubscribeTemplates: map[string]string{
"lottery_result": val,
},
}
ctx.Payload(rsp)
}
}

View File

@ -1,12 +1,12 @@
package app package app
import ( import (
"net/http" "net/http"
"bindbox-game/internal/code" "bindbox-game/internal/code"
"bindbox-game/internal/pkg/core" "bindbox-game/internal/pkg/core"
"bindbox-game/internal/pkg/validation" "bindbox-game/internal/pkg/validation"
"bindbox-game/internal/repository/mysql/model" "bindbox-game/internal/repository/mysql/model"
) )
type listPointsRequest struct { type listPointsRequest struct {
@ -20,7 +20,7 @@ type listPointsResponse struct {
List []*model.UserPointsLedger `json:"list"` List []*model.UserPointsLedger `json:"list"`
} }
type pointsBalanceResponse struct { type pointsBalanceResponse struct {
Balance int64 `json:"balance"` Balance float64 `json:"balance"` // 积分(分/rate
} }
// ListUserPoints 查看用户积分记录 // ListUserPoints 查看用户积分记录
@ -29,8 +29,8 @@ type pointsBalanceResponse struct {
// @Tags APP端.用户 // @Tags APP端.用户
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param user_id path integer true "用户ID" // @Param user_id path integer true "用户ID"
// @Security LoginVerifyToken // @Security LoginVerifyToken
// @Param page query int true "页码" default(1) // @Param page query int true "页码" default(1)
// @Param page_size query int true "每页数量最多100" default(20) // @Param page_size query int true "每页数量最多100" default(20)
// @Success 200 {object} listPointsResponse // @Success 200 {object} listPointsResponse
@ -44,8 +44,8 @@ func (h *handler) ListUserPoints() core.HandlerFunc {
ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err)))
return return
} }
userID := int64(ctx.SessionUserInfo().Id) userID := int64(ctx.SessionUserInfo().Id)
items, total, err := h.user.ListPointsLedger(ctx.RequestContext(), userID, req.Page, req.PageSize) items, total, err := h.user.ListPointsLedger(ctx.RequestContext(), userID, req.Page, req.PageSize)
if err != nil { if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 10004, err.Error())) ctx.AbortWithError(core.Error(http.StatusBadRequest, 10004, err.Error()))
return return
@ -64,21 +64,21 @@ func (h *handler) ListUserPoints() core.HandlerFunc {
// @Tags APP端.用户 // @Tags APP端.用户
// @Accept json // @Accept json
// @Produce json // @Produce json
// @Param user_id path integer true "用户ID" // @Param user_id path integer true "用户ID"
// @Security LoginVerifyToken // @Security LoginVerifyToken
// @Success 200 {object} pointsBalanceResponse // @Success 200 {object} pointsBalanceResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/app/users/{user_id}/points/balance [get] // @Router /api/app/users/{user_id}/points/balance [get]
func (h *handler) GetUserPointsBalance() core.HandlerFunc { func (h *handler) GetUserPointsBalance() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
rsp := new(pointsBalanceResponse) rsp := new(pointsBalanceResponse)
userID := int64(ctx.SessionUserInfo().Id) userID := int64(ctx.SessionUserInfo().Id)
total, err := h.user.GetPointsBalance(ctx.RequestContext(), userID) total, err := h.user.GetPointsBalance(ctx.RequestContext(), userID)
if err != nil { if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 10005, err.Error())) ctx.AbortWithError(core.Error(http.StatusBadRequest, 10005, err.Error()))
return return
} }
rsp.Balance = total rsp.Balance = h.user.CentsToPointsFloat(ctx.RequestContext(), total)
ctx.Payload(rsp) ctx.Payload(rsp)
} }
} }

View File

@ -52,13 +52,19 @@ func (h *handler) RedeemPointsToCoupon() core.HandlerFunc {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 150002, "only amount coupons supported")) ctx.AbortWithError(core.Error(http.StatusBadRequest, 150002, "only amount coupons supported"))
return return
} }
needPoints, _ := h.user.CentsToPoints(ctx.RequestContext(), sc.DiscountValue) // sc.DiscountValue 是优惠券面值(分),直接用于扣除
if needPoints <= 0 { // 例如30 元优惠券 = 3000 分
needPoints = 1 needCents := sc.DiscountValue
if needCents <= 0 {
needCents = 1
} }
ledgerID, err := h.user.ConsumePointsFor(ctx.RequestContext(), userID, needPoints, "system_coupons", strconv.FormatInt(req.CouponID, 10), "redeem coupon", "redeem_coupon") ledgerID, err := h.user.ConsumePointsFor(ctx.RequestContext(), userID, needCents, "system_coupons", strconv.FormatInt(req.CouponID, 10), "redeem coupon", "redeem_coupon")
if err != nil { if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 150003, err.Error())) errMsg := err.Error()
if errMsg == "insufficient_points" {
errMsg = "积分不足,无法兑换"
}
ctx.AbortWithError(core.Error(http.StatusBadRequest, 150003, errMsg))
return return
} }
if err := h.user.AddCoupon(ctx.RequestContext(), userID, req.CouponID); err != nil { if err := h.user.AddCoupon(ctx.RequestContext(), userID, req.CouponID); err != nil {

View File

@ -59,12 +59,13 @@ func (h *handler) RedeemPointsToProduct() core.HandlerFunc {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 150105, "商品库存不足,请联系客服处理")) ctx.AbortWithError(core.Error(http.StatusBadRequest, 150105, "商品库存不足,请联系客服处理"))
return return
} }
ptsPerUnit, _ := h.user.CentsToPoints(ctx.RequestContext(), prod.Price) // prod.Price 是商品价格(分),直接用于扣除
needPoints := ptsPerUnit * int64(req.Quantity) // 例如30 元商品 = 3000 分
if needPoints <= 0 { needCents := prod.Price * int64(req.Quantity)
needPoints = 1 if needCents <= 0 {
needCents = 1
} }
ledgerID, err := h.user.ConsumePointsFor(ctx.RequestContext(), userID, needPoints, "products", strconv.FormatInt(req.ProductID, 10), "redeem product", "redeem_product") ledgerID, err := h.user.ConsumePointsFor(ctx.RequestContext(), userID, needCents, "products", strconv.FormatInt(req.ProductID, 10), "redeem product", "redeem_product")
if err != nil { if err != nil {
errMsg := err.Error() errMsg := err.Error()
if errMsg == "insufficient_points" { if errMsg == "insufficient_points" {
@ -73,7 +74,7 @@ func (h *handler) RedeemPointsToProduct() core.HandlerFunc {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 150102, errMsg)) ctx.AbortWithError(core.Error(http.StatusBadRequest, 150102, errMsg))
return return
} }
resp, err := h.user.GrantReward(ctx.RequestContext(), userID, usersvc.GrantRewardRequest{ProductID: req.ProductID, Quantity: req.Quantity, Remark: prod.Name, PointsAmount: needPoints}) resp, err := h.user.GrantReward(ctx.RequestContext(), userID, usersvc.GrantRewardRequest{ProductID: req.ProductID, Quantity: req.Quantity, Remark: prod.Name, PointsAmount: needCents})
if err != nil { if err != nil {
ctx.AbortWithError(core.Error(http.StatusBadRequest, 150103, err.Error())) ctx.AbortWithError(core.Error(http.StatusBadRequest, 150103, err.Error()))
return return

View File

@ -35,15 +35,18 @@ func (h *handler) GetUserProfile() core.HandlerFunc {
} }
balance, _ := h.user.GetPointsBalance(ctx.RequestContext(), userID) balance, _ := h.user.GetPointsBalance(ctx.RequestContext(), userID)
// 转换为积分(浮点)用于显示
balancePoints := h.user.CentsToPointsFloat(ctx.RequestContext(), balance)
res := userItem{ res := userItem{
ID: user.ID, ID: user.ID,
Nickname: user.Nickname, Nickname: user.Nickname,
Avatar: user.Avatar, Avatar: user.Avatar,
InviteCode: user.InviteCode, InviteCode: user.InviteCode,
InviterID: user.InviterID, InviterID: user.InviterID,
Mobile: phone, Mobile: phone,
Balance: balance, DouyinUserID: user.DouyinUserID,
Balance: balancePoints,
} }
ctx.Payload(res) ctx.Payload(res)
} }
@ -54,13 +57,14 @@ type modifyUserRequest struct {
Avatar *string `json:"avatar"` Avatar *string `json:"avatar"`
} }
type userItem struct { type userItem struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Nickname string `json:"nickname"` Nickname string `json:"nickname"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
InviteCode string `json:"invite_code"` InviteCode string `json:"invite_code"`
InviterID int64 `json:"inviter_id"` InviterID int64 `json:"inviter_id"`
Mobile string `json:"mobile"` Mobile string `json:"mobile"`
Balance int64 `json:"balance"` // Points DouyinUserID string `json:"douyin_user_id"`
Balance float64 `json:"balance"` // 积分(分/rate
} }
type modifyUserResponse struct { type modifyUserResponse struct {
User userItem `json:"user"` User userItem `json:"user"`
@ -101,15 +105,17 @@ func (h *handler) ModifyUser() core.HandlerFunc {
} }
balance, _ := h.user.GetPointsBalance(ctx.RequestContext(), userID) balance, _ := h.user.GetPointsBalance(ctx.RequestContext(), userID)
balancePoints := h.user.CentsToPointsFloat(ctx.RequestContext(), balance)
rsp.User = userItem{ rsp.User = userItem{
ID: item.ID, ID: item.ID,
Nickname: item.Nickname, Nickname: item.Nickname,
Avatar: item.Avatar, Avatar: item.Avatar,
InviteCode: item.InviteCode, InviteCode: item.InviteCode,
InviterID: item.InviterID, InviterID: item.InviterID,
Mobile: maskedPhone, Mobile: maskedPhone,
Balance: balance, DouyinUserID: item.DouyinUserID,
Balance: balancePoints,
} }
ctx.Payload(rsp) ctx.Payload(rsp)
} }

View File

@ -200,6 +200,7 @@ type Mux interface {
ServeHTTP(w http.ResponseWriter, req *http.Request) ServeHTTP(w http.ResponseWriter, req *http.Request)
Group(relativePath string, handlers ...HandlerFunc) RouterGroup Group(relativePath string, handlers ...HandlerFunc) RouterGroup
Routes() gin.RoutesInfo Routes() gin.RoutesInfo
Engine() *gin.Engine
} }
type mux struct { type mux struct {
@ -220,6 +221,10 @@ func (m *mux) Routes() gin.RoutesInfo {
return m.engine.Routes() return m.engine.Routes()
} }
func (m *mux) Engine() *gin.Engine {
return m.engine
}
func New(logger logger.CustomLogger, options ...Option) (Mux, error) { func New(logger logger.CustomLogger, options ...Option) (Mux, error) {
if logger == nil { if logger == nil {
return nil, errors.New("logger required") return nil, errors.New("logger required")

View File

@ -3,6 +3,7 @@ package env
import ( import (
"flag" "flag"
"fmt" "fmt"
"os"
"strings" "strings"
"sync" "sync"
) )
@ -65,6 +66,10 @@ func setup() {
val = *envFlag val = *envFlag
} }
if val == "" {
val = os.Getenv("ACTIVE_ENV")
}
switch strings.ToLower(strings.TrimSpace(val)) { switch strings.ToLower(strings.TrimSpace(val)) {
case "dev": case "dev":
active = dev active = dev

View File

@ -0,0 +1,66 @@
package otel
import (
"time"
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/propagation"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
)
// Middleware 返回 Gin 中间件,用于自动创建 span
func Middleware(serviceName string) gin.HandlerFunc {
return func(c *gin.Context) {
// 从请求头提取 trace context
ctx := c.Request.Context()
propagator := propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
)
ctx = propagator.Extract(ctx, propagation.HeaderCarrier(c.Request.Header))
// 创建 span
spanName := c.Request.Method + " " + c.FullPath()
if c.FullPath() == "" {
spanName = c.Request.Method + " " + c.Request.URL.Path
}
ctx, span := Tracer().Start(ctx, spanName,
trace.WithSpanKind(trace.SpanKindServer),
trace.WithAttributes(
semconv.HTTPRequestMethodKey.String(c.Request.Method),
semconv.URLFull(c.Request.URL.String()),
semconv.HTTPRoute(c.FullPath()),
semconv.ServerAddress(c.Request.Host),
attribute.String("http.client_ip", c.ClientIP()),
),
)
defer span.End()
// 将新的 context 放入请求
c.Request = c.Request.WithContext(ctx)
// 记录开始时间
start := time.Now()
// 执行后续处理
c.Next()
// 记录响应信息
duration := time.Since(start)
statusCode := c.Writer.Status()
span.SetAttributes(
semconv.HTTPResponseStatusCode(statusCode),
attribute.Int64("http.response_size", int64(c.Writer.Size())),
attribute.Float64("http.duration_ms", float64(duration.Milliseconds())),
)
// 如果有错误,记录错误信息
if len(c.Errors) > 0 {
span.SetAttributes(attribute.String("error.message", c.Errors.String()))
}
}
}

114
internal/pkg/otel/otel.go Normal file
View File

@ -0,0 +1,114 @@
// Package otel 提供 OpenTelemetry 链路追踪功能
package otel
import (
"context"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
)
var tracer trace.Tracer
// Config OpenTelemetry 配置
type Config struct {
ServiceName string
ServiceVersion string
Environment string
Endpoint string // Tempo OTLP HTTP endpoint, e.g., "tempo:4318"
Enabled bool
}
// Init 初始化 OpenTelemetry
// 返回 shutdown 函数,在程序退出时调用
func Init(cfg Config) (func(context.Context) error, error) {
if !cfg.Enabled {
// 如果未启用,返回空的 shutdown 函数
tracer = otel.Tracer(cfg.ServiceName)
return func(ctx context.Context) error { return nil }, nil
}
ctx := context.Background()
// 创建 OTLP HTTP exporter
client := otlptracehttp.NewClient(
otlptracehttp.WithEndpoint(cfg.Endpoint),
otlptracehttp.WithInsecure(), // 内网使用,不需要 TLS
)
exporter, err := otlptrace.New(ctx, client)
if err != nil {
return nil, err
}
// 创建 resource
res, err := resource.Merge(
resource.Default(),
resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName(cfg.ServiceName),
semconv.ServiceVersion(cfg.ServiceVersion),
attribute.String("environment", cfg.Environment),
),
)
if err != nil {
return nil, err
}
// 创建 TracerProvider
tp := sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exporter,
sdktrace.WithBatchTimeout(5*time.Second),
),
sdktrace.WithResource(res),
sdktrace.WithSampler(sdktrace.AlwaysSample()), // 生产环境可改为采样
)
// 设置全局 TracerProvider 和 Propagator
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{},
))
tracer = tp.Tracer(cfg.ServiceName)
return tp.Shutdown, nil
}
// Tracer 获取全局 Tracer
func Tracer() trace.Tracer {
if tracer == nil {
tracer = otel.Tracer("bindbox-game")
}
return tracer
}
// StartSpan 开始一个新的 span
func StartSpan(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
return Tracer().Start(ctx, name, opts...)
}
// SpanFromContext 从 context 获取当前 span
func SpanFromContext(ctx context.Context) trace.Span {
return trace.SpanFromContext(ctx)
}
// AddEvent 为当前 span 添加事件
func AddEvent(ctx context.Context, name string, attrs ...attribute.KeyValue) {
span := trace.SpanFromContext(ctx)
span.AddEvent(name, trace.WithAttributes(attrs...))
}
// SetError 设置 span 错误状态
func SetError(ctx context.Context, err error) {
span := trace.SpanFromContext(ctx)
span.RecordError(err)
}

View File

@ -1,19 +1,25 @@
package points package points
func CentsToPoints(cents int64, rate int64) int64 { import "math"
if cents <= 0 || rate <= 0 { return 0 }
return cents * rate // CentsToPoints converts monetary value (in cents) to points based on the exchange rate (X Points per 1 Yuan).
// Now: 1 Yuan = 100 Cents.
// If Rate = 1 (1 Point = 1 Yuan), then 100 Cents = 1 Point.
// Formula: points = (cents * rate) / 100
func CentsToPoints(cents int64, rate float64) int64 {
if rate <= 0 {
rate = 1
}
// Use rounding to avoid precision loss on division
return int64(math.Round((float64(cents) * rate) / 100.0))
} }
func PointsToCents(points int64, rate int64) int64 { // PointsToCents converts points to monetary value (in cents).
if points <= 0 || rate <= 0 { return 0 } // If Rate = 1 (1 Point = 1 Yuan), then 1 Point = 100 Cents.
return points / rate // Formula: cents = (points * 100) / rate
func PointsToCents(points int64, rate float64) int64 {
if rate <= 0 {
rate = 1
}
return int64(math.Round((float64(points) * 100.0) / rate))
} }
func RefundPointsAmount(pointsAmountCents int64, refundedCents int64, totalPaidCents int64, rate int64) int64 {
if pointsAmountCents <= 0 || refundedCents <= 0 || totalPaidCents <= 0 || rate <= 0 { return 0 }
if refundedCents > totalPaidCents { refundedCents = totalPaidCents }
targetCents := (pointsAmountCents * refundedCents) / totalPaidCents
return targetCents / 100
}

View File

@ -3,20 +3,22 @@ package points
import "testing" import "testing"
func TestCentsToPoints_DefaultRate(t *testing.T) { func TestCentsToPoints_DefaultRate(t *testing.T) {
if got := CentsToPoints(12345, 1); got != 12345 { if got := CentsToPoints(100, 1); got != 1 {
t.Fatalf("expected 12345, got %d", got) t.Fatalf("expected 1, got %d", got)
} }
} }
func TestPointsToCents_DefaultRate(t *testing.T) { func TestPointsToCents_DefaultRate(t *testing.T) {
if got := PointsToCents(100, 1); got != 100 { if got := PointsToCents(1, 1); got != 100 {
t.Fatalf("expected 100, got %d", got) t.Fatalf("expected 100, got %d", got)
} }
} }
func TestRefundPointsAmount(t *testing.T) { func TestRefundPointsAmount(t *testing.T) {
pts := RefundPointsAmount(5000, 2500, 10000, 1) // 100 Points used. Refund 25 Yuan out of 100 Yuan paid.
if pts != 12 { // Expect 25 Points back.
t.Fatalf("expected 12, got %d", pts) pts := RefundPointsAmount(100, 2500, 10000, 1)
} if pts != 25 {
t.Fatalf("expected 25, got %d", pts)
}
} }

View File

@ -14,6 +14,7 @@ import (
"bindbox-game/internal/dblogger" "bindbox-game/internal/dblogger"
"bindbox-game/internal/pkg/core" "bindbox-game/internal/pkg/core"
"bindbox-game/internal/pkg/logger" "bindbox-game/internal/pkg/logger"
"bindbox-game/internal/pkg/otel"
"bindbox-game/internal/pkg/redis" "bindbox-game/internal/pkg/redis"
"bindbox-game/internal/repository/mysql" "bindbox-game/internal/repository/mysql"
"bindbox-game/internal/router/interceptor" "bindbox-game/internal/router/interceptor"
@ -47,6 +48,12 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), er
panic(err) panic(err)
} }
// 添加 OpenTelemetry 链路追踪中间件
cfg := configs.Get()
if cfg.Otel.Enabled {
mux.Engine().Use(otel.Middleware(configs.ProjectName))
}
// Redis is initialized in main.go // Redis is initialized in main.go
rdb := redis.GetClient() rdb := redis.GetClient()
@ -140,6 +147,9 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), er
adminAuthApiRouter.GET("/dashboard/activities", adminHandler.DashboardActivities()) adminAuthApiRouter.GET("/dashboard/activities", adminHandler.DashboardActivities())
adminAuthApiRouter.GET("/dashboard/activity_prize_analysis", adminHandler.DashboardActivityPrizeAnalysis()) adminAuthApiRouter.GET("/dashboard/activity_prize_analysis", adminHandler.DashboardActivityPrizeAnalysis())
adminAuthApiRouter.GET("/dashboard/user_overview", adminHandler.DashboardUserOverview()) adminAuthApiRouter.GET("/dashboard/user_overview", adminHandler.DashboardUserOverview())
adminAuthApiRouter.GET("/dashboard/player_spending_leaderboard", adminHandler.DashboardPlayerSpendingLeaderboard())
adminAuthApiRouter.GET("/dashboard/activity-profit-loss", adminHandler.DashboardActivityProfitLoss())
adminAuthApiRouter.GET("/dashboard/activity-profit-loss/:activity_id/logs", adminHandler.DashboardActivityLogs())
// 运营分析 // 运营分析
adminAuthApiRouter.GET("/operations/user_economics", adminHandler.DashboardUserEconomics()) adminAuthApiRouter.GET("/operations/user_economics", adminHandler.DashboardUserEconomics())
@ -231,6 +241,7 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), er
adminAuthApiRouter.POST("/users/:user_id/rewards/grant", intc.RequireAdminAction("user:rewards:grant"), adminHandler.GrantReward()) adminAuthApiRouter.POST("/users/:user_id/rewards/grant", intc.RequireAdminAction("user:rewards:grant"), adminHandler.GrantReward())
adminAuthApiRouter.GET("/users/:user_id/titles", intc.RequireAdminAction("user:view"), adminHandler.ListUserTitles()) adminAuthApiRouter.GET("/users/:user_id/titles", intc.RequireAdminAction("user:view"), adminHandler.ListUserTitles())
adminAuthApiRouter.GET("/users/:user_id/stats/profit_loss", intc.RequireAdminAction("user:view"), adminHandler.GetUserProfitLossTrend()) adminAuthApiRouter.GET("/users/:user_id/stats/profit_loss", intc.RequireAdminAction("user:view"), adminHandler.GetUserProfitLossTrend())
adminAuthApiRouter.GET("/users/:user_id/stats/profit_loss_details", intc.RequireAdminAction("user:view"), adminHandler.GetUserProfitLossDetails())
adminAuthApiRouter.GET("/users/:user_id/profile", intc.RequireAdminAction("user:view"), adminHandler.GetUserProfile()) adminAuthApiRouter.GET("/users/:user_id/profile", intc.RequireAdminAction("user:view"), adminHandler.GetUserProfile())
adminAuthApiRouter.POST("/users/:user_id/token", intc.RequireAdminAction("user:token:issue"), adminHandler.IssueUserToken()) adminAuthApiRouter.POST("/users/:user_id/token", intc.RequireAdminAction("user:token:issue"), adminHandler.IssueUserToken())
@ -333,6 +344,7 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), er
// 通用上传 // 通用上传
systemApiRouter.POST("/common/upload/wangeditor", commonHandler.UploadWangEditorImage()) systemApiRouter.POST("/common/upload/wangeditor", commonHandler.UploadWangEditorImage())
systemApiRouter.POST("/menu/ensure_titles", adminHandler.EnsureTitlesMenu()) systemApiRouter.POST("/menu/ensure_titles", adminHandler.EnsureTitlesMenu())
systemApiRouter.POST("/menu/ensure_activity_profit_loss", adminHandler.EnsureActivityProfitLossMenu())
} }
// 系统管理:用户/角色/菜单 // 系统管理:用户/角色/菜单
@ -388,6 +400,7 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), er
// 公共工具 // 公共工具
appPublicApiRouter.POST("/common/openid", commonHandler.GetOpenID()) appPublicApiRouter.POST("/common/openid", commonHandler.GetOpenID())
appPublicApiRouter.GET("/config/public", commonHandler.GetPublicConfig())
} }
// APP 端认证接口路由组 // APP 端认证接口路由组

View File

@ -46,6 +46,9 @@ func StartScheduledSettlement(l logger.CustomLogger, repo mysql.Repo, rdb *redis
// 【独立检查】一番赏格位重置每30秒检查所有售罄的一番赏期号 // 【独立检查】一番赏格位重置每30秒检查所有售罄的一番赏期号
checkAndResetIchibanSlots(ctx, l, repo, r) checkAndResetIchibanSlots(ctx, l, repo, r)
// 【安全优化】清理超过30分钟未支付的一番赏订单释放预锁
cleanupExpiredIchibanOrders(ctx, l, repo, rdb)
var acts []struct { var acts []struct {
ID int64 ID int64
PlayType string PlayType string
@ -383,6 +386,64 @@ func checkAndResetIchibanSlots(ctx context.Context, l logger.CustomLogger, repo
} }
} }
// cleanupExpiredIchibanOrders 清理超过30分钟未支付的一番赏订单释放 Redis 预锁
func cleanupExpiredIchibanOrders(ctx context.Context, l logger.CustomLogger, repo mysql.Repo, rdb *redis.Client) {
cutoff := time.Now().Add(-30 * time.Minute)
// 查找过期未支付的一番赏订单
var expiredOrders []struct {
ID int64
Remark string
UserID int64
PlayType string
}
err := repo.GetDbR().Raw(`
SELECT o.id, o.remark, o.user_id, a.play_type
FROM orders o
INNER JOIN activities a ON o.remark LIKE CONCAT('lottery:activity:', a.id, '|%')
WHERE o.status = 1
AND o.source_type = 2
AND o.created_at < ?
AND a.play_type = 'ichiban'
LIMIT 100
`, cutoff).Scan(&expiredOrders).Error
if err != nil {
l.Error("清理过期一番赏订单: 查询失败", zap.Error(err))
return
}
if len(expiredOrders) == 0 {
return
}
l.Info("清理过期一番赏订单: 开始处理", zap.Int("count", len(expiredOrders)))
for _, o := range expiredOrders {
// 解析 Remark 获取 IssueID 和 Slots
rmk := remark.Parse(o.Remark)
// 释放 Redis 预锁
if rdb != nil && rmk.IssueID > 0 {
for _, slot := range rmk.Slots {
lockKey := fmt.Sprintf("ichiban:slot_lock:%d:%d", rmk.IssueID, slot.SlotIndex)
// 只删除属于该用户的锁
existingVal, err := rdb.Get(ctx, lockKey).Result()
if err == nil && existingVal == fmt.Sprintf("%d", o.UserID) {
rdb.Del(ctx, lockKey)
}
}
}
// 将订单状态改为已关闭 (status=5)
if err := repo.GetDbW().Exec("UPDATE orders SET status = 5, cancelled_at = NOW() WHERE id = ? AND status = 1", o.ID).Error; err != nil {
l.Error("清理过期一番赏订单: 更新订单状态失败", zap.Int64("order_id", o.ID), zap.Error(err))
} else {
l.Info("清理过期一番赏订单: 订单已关闭", zap.Int64("order_id", o.ID))
}
}
}
func parseTime(s string) time.Time { func parseTime(s string) time.Time {
if s == "" { if s == "" {
return time.Time{} return time.Time{}

View File

@ -290,20 +290,66 @@ func (s *service) ListTasks(ctx context.Context, in ListTasksInput) (items []Tas
func (s *service) GetUserProgress(ctx context.Context, userID int64, taskID int64) (*UserProgress, error) { func (s *service) GetUserProgress(ctx context.Context, userID int64, taskID int64) (*UserProgress, error) {
db := s.repo.GetDbR() db := s.repo.GetDbR()
// 3.0 获取任务的 ActivityID 限制
var tiers []tcmodel.TaskTier
// 只需要查 ActivityID > 0 的记录即可判断
db.Select("activity_id").Where("task_id=? AND activity_id > 0", taskID).Limit(1).Find(&tiers)
targetActivityID := int64(0)
if len(tiers) > 0 {
targetActivityID = tiers[0].ActivityID
}
// 1. 实时统计订单数据 // 1. 实时统计订单数据
var orderCount int64 var orderCount int64
var orderAmount int64 var orderAmount int64
db.Model(&model.Orders{}).Where("user_id = ? AND status = 2", userID).Count(&orderCount) query := db.Model(&model.Orders{}).Where("user_id = ? AND status = 2", userID)
db.Model(&model.Orders{}).Where("user_id = ? AND status = 2", userID).Select("COALESCE(SUM(actual_amount), 0)").Scan(&orderAmount) if targetActivityID > 0 {
// 增加 activity_id 过滤
// 格式匹配 activity:{id} 或 lottery:activity:{id}
// remark 包分隔符为 |,所以匹配 activity:{id}|... 或 activity:{id} 结尾
likePattern := fmt.Sprintf("%%activity:%d%%", targetActivityID)
query = query.Where("remark LIKE ?", likePattern)
}
query.Count(&orderCount)
// 复用 query 对象需要 clone 或者重新构造GORM 的 query 是可变的
// 这里简单起见,重新构造 query 或者使用 session
// 上面的 query.Count 可能会修改 query保险起见重新构造
queryAmount := db.Model(&model.Orders{}).Where("user_id = ? AND status = 2", userID)
if targetActivityID > 0 {
likePattern := fmt.Sprintf("%%activity:%d%%", targetActivityID)
queryAmount = queryAmount.Where("remark LIKE ?", likePattern)
}
queryAmount.Select("COALESCE(SUM(actual_amount), 0)").Scan(&orderAmount)
// 2. 实时统计邀请数据(有效邀请:被邀请人有消费记录) // 2. 实时统计邀请数据(有效邀请:被邀请人有消费记录)
// 注意:邀请统计是否也要过滤 ActivityID
// 需求是“消费的是对对碰”,通常指邀请带来的“有效用户”需要在该活动消费。
// 之前的逻辑是INNER JOIN orders只要有任意消费就算有效。
// 如果任务是“对对碰”任务,那么被邀请人应该在“对对碰”消费才算有效邀请吗?
// 暂时保持原样,或者也加上过滤。根据常理,特定活动拉新通常要求在该活动消费。
// 这里加上过滤更安全。
var inviteCount int64 var inviteCount int64
db.Raw(` inviteQuery := fmt.Sprintf(`
SELECT COUNT(DISTINCT ui.invitee_id) SELECT COUNT(DISTINCT ui.invitee_id)
FROM user_invites ui FROM user_invites ui
INNER JOIN orders o ON o.user_id = ui.invitee_id AND o.status = 2 INNER JOIN orders o ON o.user_id = ui.invitee_id AND o.status = 2
WHERE ui.inviter_id = ? WHERE ui.inviter_id = ?
`, userID).Scan(&inviteCount) `)
var args []interface{}
args = append(args, userID)
if targetActivityID > 0 {
inviteQuery = fmt.Sprintf(`
SELECT COUNT(DISTINCT ui.invitee_id)
FROM user_invites ui
INNER JOIN orders o ON o.user_id = ui.invitee_id AND o.status = 2 AND o.remark LIKE ?
WHERE ui.inviter_id = ?
`)
args = []interface{}{fmt.Sprintf("%%activity:%d%%", targetActivityID), userID}
}
db.Raw(inviteQuery, args...).Scan(&inviteCount)
// 3. 首单判断 // 3. 首单判断
hasFirstOrder := orderCount > 0 hasFirstOrder := orderCount > 0

View File

@ -7,6 +7,7 @@ import (
"testing" "testing"
"time" "time"
"bindbox-game/internal/repository/mysql"
tcmodel "bindbox-game/internal/repository/mysql/task_center" tcmodel "bindbox-game/internal/repository/mysql/task_center"
"gorm.io/datatypes" "gorm.io/datatypes"
@ -72,6 +73,13 @@ func CreateTestDB(t *testing.T) *gorm.DB {
t.Fatalf("创建数据库失败: %v", err) t.Fatalf("创建数据库失败: %v", err)
} }
initTestTables(t, db)
return db
}
// initTestTables initializes the task center related tables in the given DB.
func initTestTables(t *testing.T, db *gorm.DB) {
// 创建任务中心相关表 // 创建任务中心相关表
if err := db.Exec(`CREATE TABLE task_center_tasks ( if err := db.Exec(`CREATE TABLE task_center_tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@ -150,8 +158,6 @@ func CreateTestDB(t *testing.T) *gorm.DB {
);`).Error; err != nil { );`).Error; err != nil {
t.Fatalf("创建 task_center_event_logs 表失败: %v", err) t.Fatalf("创建 task_center_event_logs 表失败: %v", err)
} }
return db
} }
// InsertTaskWithTierAndReward 插入一个完整的任务配置(任务+档位+奖励) // InsertTaskWithTierAndReward 插入一个完整的任务配置(任务+档位+奖励)
@ -334,7 +340,13 @@ func TestOrderCountMetric(t *testing.T) {
ClaimedTiers: datatypes.JSON("[]"), ClaimedTiers: datatypes.JSON("[]"),
} }
// Upsert 模拟 // Upsert 模拟
db.Where("user_id = ? AND task_id = ?", userID, taskID).Assign(progress).FirstOrCreate(progress) var existing tcmodel.UserTaskProgress
if err := db.Where("user_id = ? AND task_id = ?", userID, taskID).First(&existing).Error; err == nil {
existing.OrderCount = int64(i)
db.Save(&existing)
} else {
db.Create(progress)
}
} }
var p tcmodel.UserTaskProgress var p tcmodel.UserTaskProgress
@ -629,3 +641,86 @@ func TestClaimedTiersTracking(t *testing.T) {
t.Logf("已领取档位追踪测试通过: %v", claimed) t.Logf("已领取档位追踪测试通过: %v", claimed)
} }
func TestGetUserProgress_ActivityFilter_Integration(t *testing.T) {
repo, err := mysql.NewSQLiteRepoForTest()
if err != nil {
t.Fatalf("创建 repo 失败: %v", err)
}
db := repo.GetDbW()
// 初始化表结构 (直接复用 CreateTestDB 的逻辑,手动执行)
initTestTables(t, db)
// Create orders table manually since initTestTables doesn't create it
if !db.Migrator().HasTable("orders") {
if err := db.Exec(`CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
status INTEGER NOT NULL DEFAULT 1,
actual_amount INTEGER NOT NULL DEFAULT 0,
remark TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`).Error; err != nil {
t.Fatalf("创建 orders 表失败: %v", err)
}
}
// Create user_invites table
if !db.Migrator().HasTable("user_invites") {
if err := db.Exec(`CREATE TABLE user_invites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
inviter_id INTEGER NOT NULL,
invitee_id INTEGER NOT NULL,
accumulated_amount INTEGER NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);`).Error; err != nil {
t.Fatalf("创建 user_invites 表失败: %v", err)
}
}
svc := New(nil, repo, nil, nil, nil)
// 1. 创建带有 ActivityID=100 的任务
combo := TaskCombination{
Name: "活动专属任务",
Metric: MetricOrderCount,
Operator: OperatorGTE,
Threshold: 1,
Window: WindowLifetime,
RewardType: RewardTypePoints,
}
// 手动插入带 ActivityID 的 Tier
taskID, _ := InsertTaskWithTierAndReward(t, db, combo)
db.Model(&tcmodel.TaskTier{}).Where("task_id=?", taskID).Update("activity_id", 100)
userID := int64(999)
// 2. 插入不同类型的订单
// 订单 A: 匹配活动 100
db.Exec("INSERT INTO orders (user_id, status, actual_amount, remark) VALUES (?, 2, 100, ?)", userID, "activity:100|count:1")
// 订单 B: 匹配活动 200 (不应被统计)
db.Exec("INSERT INTO orders (user_id, status, actual_amount, remark) VALUES (?, 2, 200, ?)", userID, "activity:200|count:1")
// 订单 C: 普通订单 (不应被统计)
db.Exec("INSERT INTO orders (user_id, status, actual_amount, remark) VALUES (?, 2, 300, ?)", userID, "normal_order")
// 订单 D: 匹配活动 100 但未支付 (不应被统计)
db.Exec("INSERT INTO orders (user_id, status, actual_amount, remark) VALUES (?, 1, 100, ?)", userID, "activity:100|count:1")
// 3. 调用 GetUserProgress
progress, err := svc.GetUserProgress(context.Background(), userID, taskID)
if err != nil {
t.Fatalf("GetUserProgress 失败: %v", err)
}
// 4. 验证
if progress.OrderCount != 1 {
t.Errorf("OrderCount 错误: 期望 1, 实际 %d", progress.OrderCount)
}
if progress.OrderAmount != 100 {
t.Errorf("OrderAmount 错误: 期望 100, 实际 %d", progress.OrderAmount)
}
t.Logf("ActivityID 过滤测试通过: OrderCount=%d, OrderAmount=%d", progress.OrderCount, progress.OrderAmount)
}

View File

@ -20,15 +20,14 @@ func (s *service) RedeemItemCard(ctx context.Context, userID int64, cardID int64
return 0, errors.New("item card not found") return 0, errors.New("item card not found")
} }
// 2. 算分 // 2. 算分 - tpl.Price 是道具卡价格(分),直接用于扣除
ptsPerUnit, _ := s.CentsToPoints(ctx, tpl.Price) needCents := tpl.Price * int64(quantity)
needPoints := ptsPerUnit * int64(quantity) if needCents <= 0 {
if needPoints <= 0 { needCents = 1
needPoints = 1
} }
// 3. 扣分 (调用现有 Service) // 3. 扣分 (调用现有 Service)
ledgerID, err = s.ConsumePointsFor(ctx, userID, needPoints, "system_item_cards", strconv.FormatInt(cardID, 10), "redeem item card", "redeem_item_card") ledgerID, err = s.ConsumePointsFor(ctx, userID, needCents, "system_item_cards", strconv.FormatInt(cardID, 10), "redeem item card", "redeem_item_card")
if err != nil { if err != nil {
return 0, err return 0, err
} }
@ -42,7 +41,7 @@ func (s *service) RedeemItemCard(ctx context.Context, userID int64, cardID int64
zap.Error(err), zap.Error(err),
) )
// 自动退分 // 自动退分
_, _ = s.RefundPoints(ctx, userID, needPoints, strconv.FormatInt(ledgerID, 10), "refund: grant item card failed") _, _ = s.RefundPoints(ctx, userID, needCents, strconv.FormatInt(ledgerID, 10), "refund: grant item card failed")
return ledgerID, errors.New("failed to grant item card: " + err.Error()) return ledgerID, errors.New("failed to grant item card: " + err.Error())
} }

View File

@ -36,12 +36,8 @@ func (s *service) CancelOrder(ctx context.Context, userID int64, orderID int64,
refundReason = refundReason + ":" + reason refundReason = refundReason + ":" + reason
} }
// Get Rate // order.PointsAmount 已经是分单位,直接退还
ratePtsPerCent, _ := s.CentsToPoints(ctx, 1) pointsToRefund := order.PointsAmount
if ratePtsPerCent <= 0 {
ratePtsPerCent = 1
}
pointsToRefund := order.PointsAmount * ratePtsPerCent
// Update User Points // Update User Points
existing, _ := tx.UserPoints.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.UserPoints.UserID.Eq(userID)).First() existing, _ := tx.UserPoints.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.UserPoints.UserID.Eq(userID)).First()

View File

@ -5,12 +5,15 @@ import (
"fmt" "fmt"
) )
func (s *service) CentsToPoints(ctx context.Context, cents int64) (int64, error) { // getExchangeRate 获取积分兑换比率
if cents <= 0 { // 配置含义1 元 = X 积分(后台配置的值)
return 0, nil // 计算:分 / (100 / rate) = 分 * rate / 100
} // 当 rate=1 时1元=1积分100分=1积分
cfg, _ := s.readDB.SystemConfigs.WithContext(ctx).Where(s.readDB.SystemConfigs.ConfigKey.Eq("points_exchange_per_cent")).First() // 当 rate=2 时1元=2积分50分=1积分
rate := int64(1) func (s *service) getExchangeRate(ctx context.Context) int64 {
// 读取后台配置的 keypoints.exchange_rate
cfg, _ := s.readDB.SystemConfigs.WithContext(ctx).Where(s.readDB.SystemConfigs.ConfigKey.Eq("points.exchange_rate")).First()
rate := int64(1) // 默认 1 元 = 1 积分
if cfg != nil { if cfg != nil {
var r int64 var r int64
_, _ = fmt.Sscanf(cfg.ConfigValue, "%d", &r) _, _ = fmt.Sscanf(cfg.ConfigValue, "%d", &r)
@ -18,5 +21,39 @@ func (s *service) CentsToPoints(ctx context.Context, cents int64) (int64, error)
rate = r rate = r
} }
} }
return cents * rate, nil return rate
}
// CentsToPointsFloat 分 → 积分(浮点数,用于 API 返回展示)
// 公式:积分 = 分 * rate / 100
// 例如rate=1, 3580分 → 35.8积分
func (s *service) CentsToPointsFloat(ctx context.Context, cents int64) float64 {
if cents <= 0 {
return 0
}
rate := s.getExchangeRate(ctx)
return float64(cents) * float64(rate) / 100
}
// CentsToPoints 分 → 积分(整数,向下取整,用于计算)
// 公式:积分 = 分 * rate / 100
func (s *service) CentsToPoints(ctx context.Context, cents int64) (int64, error) {
if cents <= 0 {
return 0, nil
}
rate := s.getExchangeRate(ctx)
return (cents * rate) / 100, nil
}
// PointsToCents 积分 → 分(用于将用户输入的积分转为分)
// 公式:分 = 积分 * 100 / rate
func (s *service) PointsToCents(ctx context.Context, pts int64) (int64, error) {
if pts <= 0 {
return 0, nil
}
rate := s.getExchangeRate(ctx)
if rate == 0 {
rate = 1
}
return (pts * 100) / rate, nil
} }

View File

@ -71,6 +71,8 @@ type Service interface {
GetOrderWithItems(ctx context.Context, userID, orderID int64) (*OrderWithItems, error) GetOrderWithItems(ctx context.Context, userID, orderID int64) (*OrderWithItems, error)
CancelOrder(ctx context.Context, userID, orderID int64, reason string) (*model.Orders, error) CancelOrder(ctx context.Context, userID, orderID int64, reason string) (*model.Orders, error)
CentsToPoints(ctx context.Context, cents int64) (int64, error) CentsToPoints(ctx context.Context, cents int64) (int64, error)
CentsToPointsFloat(ctx context.Context, cents int64) float64
PointsToCents(ctx context.Context, points int64) (int64, error)
ListUserShipmentGroups(ctx context.Context, userID int64, page, pageSize int) (items []*ShipmentGroup, total int64, err error) ListUserShipmentGroups(ctx context.Context, userID int64, page, pageSize int) (items []*ShipmentGroup, total int64, err error)
ConsumePointsForRefund(ctx context.Context, userID int64, points int64, refTable string, refID string, remark string) (int64, int64, error) ConsumePointsForRefund(ctx context.Context, userID int64, points int64, refTable string, refID string, remark string) (int64, int64, error)
// 短信登录 // 短信登录

171
main.go
View File

@ -8,6 +8,7 @@ import (
"bindbox-game/configs" "bindbox-game/configs"
"bindbox-game/internal/pkg/env" "bindbox-game/internal/pkg/env"
"bindbox-game/internal/pkg/logger" "bindbox-game/internal/pkg/logger"
"bindbox-game/internal/pkg/otel"
"bindbox-game/internal/pkg/redis" "bindbox-game/internal/pkg/redis"
"bindbox-game/internal/pkg/shutdown" "bindbox-game/internal/pkg/shutdown"
"bindbox-game/internal/pkg/timeutil" "bindbox-game/internal/pkg/timeutil"
@ -35,163 +36,30 @@ import (
// @BasePath / // @BasePath /
func main() { func main() {
flag.Parse() flag.Parse()
// 初始化 OpenTelemetry
cfg := configs.Get()
otelShutdown, err := otel.Init(otel.Config{
ServiceName: configs.ProjectName,
ServiceVersion: "1.0.0",
Environment: env.Active().Value(),
Endpoint: cfg.Otel.Endpoint,
Enabled: cfg.Otel.Enabled,
})
if err != nil {
fmt.Printf("OpenTelemetry init failed: %v\n", err)
}
// 初始化 MySQL // 初始化 MySQL
dbRepo, err := mysql.New() dbRepo, err := mysql.New()
if err != nil { if err != nil {
panic(err) panic(err)
} }
// 修复缺失的列以避免创建活动时报错
{
db := dbRepo.GetDbW()
var cnt int64
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'activities' AND column_name = 'image'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE `activities` ADD COLUMN `image` VARCHAR(512) NULL COMMENT '活动主图URL' AFTER `banner`").Error
}
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'activities' AND column_name = 'gameplay_intro'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE `activities` ADD COLUMN `gameplay_intro` TEXT NULL COMMENT '玩法介绍' AFTER `image`").Error
}
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'activities' AND column_name = 'allow_item_cards'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE `activities` ADD COLUMN `allow_item_cards` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许使用道具卡' AFTER `is_boss`").Error
}
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'activities' AND column_name = 'allow_coupons'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE `activities` ADD COLUMN `allow_coupons` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许使用优惠券' AFTER `allow_item_cards`").Error
}
// 检查并创建 channels 表
if !db.Migrator().HasTable("channels") {
_ = db.Exec(`CREATE TABLE IF NOT EXISTS channels (
id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
name varchar(255) NOT NULL COMMENT '渠道名称',
code varchar(255) NOT NULL COMMENT '渠道唯一标识',
type varchar(50) NOT NULL DEFAULT 'other' COMMENT '渠道类型',
remarks varchar(512) DEFAULT NULL COMMENT '备注',
created_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
updated_at datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '更新时间',
deleted_at datetime(3) DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (id),
UNIQUE KEY uk_code (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='推广渠道表';`).Error
}
// users 表新增 channel_id 字段
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'users' AND column_name = 'channel_id'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE users ADD COLUMN channel_id bigint(20) DEFAULT 0 COMMENT '渠道ID' AFTER douyin_id").Error
}
// shipping_records 表新增 batch_no 字段(批量发货分组用)
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'shipping_records' AND column_name = 'batch_no'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE shipping_records ADD COLUMN batch_no VARCHAR(64) NULL COMMENT '批次号(批量发货时用于聚合)' AFTER express_no").Error
_ = db.Exec("CREATE INDEX idx_shipping_records_batch_no ON shipping_records(batch_no)").Error
}
// 抖店订单表
if !db.Migrator().HasTable("douyin_orders") {
_ = db.Exec(`CREATE TABLE IF NOT EXISTS douyin_orders (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
shop_order_id VARCHAR(64) NOT NULL COMMENT '抖店订单号',
order_status INT NOT NULL DEFAULT 0 COMMENT '订单状态: 5=已完成',
douyin_user_id VARCHAR(256) NOT NULL COMMENT '抖店用户ID',
local_user_id BIGINT DEFAULT 0 COMMENT '匹配到的本地用户ID',
actual_receive_amount BIGINT DEFAULT 0 COMMENT '实收金额()',
pay_type_desc VARCHAR(64) DEFAULT '' COMMENT '支付方式描述',
remark VARCHAR(512) DEFAULT '' COMMENT '备注',
user_nickname VARCHAR(128) DEFAULT '' COMMENT '抖音昵称',
raw_data JSON COMMENT '原始响应数据',
created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
UNIQUE KEY uk_shop_order_id (shop_order_id),
INDEX idx_local_user_id (local_user_id),
INDEX idx_douyin_user_id (douyin_user_id),
INDEX idx_order_status (order_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='抖店订单表';`).Error
} else {
// 检查并修改 douyin_user_id 列长度
_ = db.Exec("ALTER TABLE douyin_orders MODIFY COLUMN douyin_user_id VARCHAR(256) NOT NULL COMMENT '抖店用户ID'").Error
// 检查并添加 reward_granted 字段
cnt = 0
_ = db.Raw(
"SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = 'douyin_orders' AND column_name = 'reward_granted'",
configs.Get().MySQL.Write.Name,
).Scan(&cnt).Error
if cnt == 0 {
_ = db.Exec("ALTER TABLE douyin_orders ADD COLUMN reward_granted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '奖励已发放: 0=否, 1=是'").Error
}
}
// 抽奖退款日志表
if !db.Migrator().HasTable("lottery_refund_logs") {
_ = db.Exec(`CREATE TABLE IF NOT EXISTS lottery_refund_logs (
id bigint unsigned AUTO_INCREMENT PRIMARY KEY,
issue_id bigint NOT NULL DEFAULT 0,
order_id bigint NOT NULL DEFAULT 0,
user_id bigint NOT NULL DEFAULT 0,
amount bigint NOT NULL DEFAULT 0,
coupon_type varchar(64) DEFAULT '',
coupon_amount bigint DEFAULT 0,
reason varchar(255) DEFAULT '',
status varchar(32) DEFAULT '',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_issue (issue_id),
INDEX idx_order (order_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='抽奖退款记录表';`).Error
}
// 抖店商品奖励规则表
if !db.Migrator().HasTable("douyin_product_rewards") {
_ = db.Exec(`CREATE TABLE IF NOT EXISTS douyin_product_rewards (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
product_id VARCHAR(64) NOT NULL COMMENT '抖店商品ID',
product_name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '商品名称',
reward_type VARCHAR(32) NOT NULL COMMENT '奖励类型',
reward_payload JSON COMMENT '奖励参数JSON',
quantity INT NOT NULL DEFAULT 1 COMMENT '发放数量',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态: 1=启用 0=禁用',
created_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
UNIQUE KEY uk_product_id (product_id),
KEY idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='抖店商品奖励规则';`).Error
}
}
// 初始化 自定义 Logger // 初始化 自定义 Logger
customLogger, err := logger.NewCustomLogger(dao.Use(dbRepo.GetDbW()), customLogger, err := logger.NewCustomLogger(dao.Use(dbRepo.GetDbW()),
logger.WithDebugLevel(), // 启用调试级别日志 logger.WithDebugLevel(), // 启用调试级别日志
// logger.WithOutputInConsole(), // 启用控制台输出 logger.WithOutputInConsole(), // 启用控制台输出
logger.WithField("domain", fmt.Sprintf("%s[%s]", configs.ProjectName, env.Active().Value())), logger.WithField("domain", fmt.Sprintf("%s[%s]", configs.ProjectName, env.Active().Value())),
logger.WithTimeLayout(timeutil.CSTLayout), logger.WithTimeLayout(timeutil.CSTLayout),
logger.WithFileRotationP(configs.ProjectAccessLogFile), logger.WithFileRotationP(configs.ProjectAccessLogFile),
@ -248,6 +116,11 @@ func main() {
cleanup() cleanup()
} }
// 关闭 OpenTelemetry
if otelShutdown != nil {
_ = otelShutdown(context.Background())
}
// 关闭 http server // 关闭 http server
if err := server.Shutdown(context.TODO()); err != nil { if err := server.Shutdown(context.TODO()); err != nil {
customLogger.Error("server shutdown err", zap.Error(err)) customLogger.Error("server shutdown err", zap.Error(err))

View File

@ -0,0 +1 @@
INSERT INTO sys_configs (config_key, config_group, config_value, remark, is_encrypted) VALUES ('wechat_miniprogram_lottery_result_template_id', 'miniprogram', 'O2eqJQD3pn-vQ6g2z9DWzINVwOmPoz8yW-172J_YcpI', '微信小程序开奖结果通知模板ID', 0) ON DUPLICATE KEY UPDATE config_value='O2eqJQD3pn-vQ6g2z9DWzINVwOmPoz8yW-172J_YcpI';

1
response.json Normal file

File diff suppressed because one or more lines are too long

1
response_new.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,70 @@
package main
import (
"bindbox-game/internal/repository/mysql"
"bindbox-game/internal/repository/mysql/model"
"fmt"
"log"
)
func main() {
// Initialize DB (Implicitly loads configs via init())
db, err := mysql.New()
if err != nil {
log.Fatalf("failed to init db: %v", err)
}
// 1. Get all users
var userIDs []int64
if err := db.GetDbR().Model(&model.Users{}).Pluck("id", &userIDs).Error; err != nil {
log.Fatalf("failed to get users: %v", err)
}
fmt.Printf("Checking points integrity for %d users...\n", len(userIDs))
fmt.Println("UserID | LedgerSum | BalanceSum | Diff")
fmt.Println("-------|-----------|------------|------")
errorCount := 0
for _, uid := range userIDs {
// Sum Ledger
var ledgerSum int64
if err := db.GetDbR().Model(&model.UserPointsLedger{}).
Where("user_id = ?", uid).
Select("COALESCE(SUM(points), 0)").
Scan(&ledgerSum).Error; err != nil {
log.Printf("failed to sum ledger for user %d: %v", uid, err)
continue
}
// Sum Balance
var balanceSum int64
if err := db.GetDbR().Model(&model.UserPoints{}).
Where("user_id = ?", uid).
Select("COALESCE(SUM(points), 0)").
Scan(&balanceSum).Error; err != nil {
log.Printf("failed to sum balance for user %d: %v", uid, err)
continue
}
diff := ledgerSum - balanceSum
if diff != 0 || uid == 9018 {
errorCount++
fmt.Printf("%6d | %9d | %10d | %4d\n", uid, ledgerSum, balanceSum, diff)
// Show ledgers for 9018
if uid == 9018 {
var ledgers []model.UserPointsLedger
db.GetDbR().Where("user_id = ?", uid).Find(&ledgers)
for _, l := range ledgers {
fmt.Printf(" -> Ledger ID: %d, Action: %s, Points: %d\n", l.ID, l.Action, l.Points)
}
}
}
}
if errorCount == 0 {
fmt.Println("\nAll users verified. No discrepancies found.")
} else {
fmt.Printf("\nFound %d users with point discrepancies.\n", errorCount)
}
}