From b21e2db8efb6e9e8a6f061ba2cad79120e2f6e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=82=B9=E6=96=B9=E6=88=90?= Date: Thu, 8 Jan 2026 10:13:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=8A=96=E9=9F=B3?= =?UTF-8?q?=E5=95=86=E5=93=81=E5=A5=96=E5=8A=B1=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E5=A2=9E=E5=BC=BA=E5=90=8E=E5=8F=B0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E7=BB=9F=E8=AE=A1=E6=95=B0=E6=8D=AE=E3=80=81?= =?UTF-8?q?=E9=82=80=E8=AF=B7=E4=BA=BA=E6=95=B0=E5=8F=8A=E9=81=93=E5=85=B7?= =?UTF-8?q?=E6=95=B0=E9=87=8F=E5=B1=95=E7=A4=BA=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .DS_Store | Bin 10244 -> 10244 bytes Dockerfile | 26 +- backend.log | 21 + backend_assets.log | 127 ++++++ backend_debug.log | 397 +++++++++++++++++ backend_debug_sql.log | 193 ++++++++ backend_final.log | 4 + backend_fix_sql.log | 360 +++++++++++++++ backend_new.log | 195 +++++++++ backend_prod.log | 147 +++++++ check_config.sql | 1 + cmd/diagnose_order/main.go | 74 ++++ configs/configs.go | 37 ++ configs/pro_configs.toml | 72 +-- .../ALIGNMENT_loki_integration.md | 47 ++ .../DESIGN_standardize_points_ratio.md | 67 +++ .../TASK_standardize_points_ratio.md | 45 ++ docs/standardize_points_ratio/migration.sql | 62 +++ docs/standardize_points_ratio/walkthrough.md | 52 +++ .../ALIGNMENT_yifanshang_count_card_fix.md | 46 ++ .../CONSENSUS_yifanshang_count_card_fix.md | 43 ++ .../DESIGN_yifanshang_count_card_fix.md | 85 ++++ .../FINAL_yifanshang_count_card_fix.md | 28 ++ .../TASK_yifanshang_count_card_fix.md | 40 ++ .../TODO_yifanshang_count_card_fix.md | 12 + .../ALIGNMENT_玩家管理Bug修复.md | 95 ++++ .../CONSENSUS_玩家管理Bug修复.md | 59 +++ .../玩家管理Bug修复/DESIGN_玩家管理Bug修复.md | 177 ++++++++ go.mod | 40 +- go.sum | 105 ++++- internal/api/activity/lottery_app.go | 91 ++-- internal/api/activity/matching_game_app.go | 7 + internal/api/admin/dashboard_activity.go | 413 ++++++++++++++++++ internal/api/admin/dashboard_admin.go | 25 +- internal/api/admin/dashboard_spending.go | 256 +++++++++++ internal/api/admin/pay_orders_admin.go | 35 +- internal/api/admin/pay_orders_export.go | 26 +- internal/api/admin/pay_refund_admin.go | 38 +- internal/api/admin/system_coupons.go | 29 +- internal/api/admin/users_admin.go | 236 +++++++++- internal/api/admin/users_batch_admin.go | 244 ++++++----- internal/api/admin/users_profile.go | 24 +- internal/api/admin/users_profit_loss.go | 307 ++++++++++++- internal/api/app/product.go | 22 +- internal/api/app/store.go | 77 +++- internal/api/common/config.go | 38 ++ internal/api/user/points_app.go | 30 +- internal/api/user/points_redeem_coupon_app.go | 16 +- .../api/user/points_redeem_product_app.go | 13 +- internal/api/user/profile_app.go | 48 +- internal/pkg/core/core.go | 5 + internal/pkg/env/env.go | 5 + internal/pkg/otel/middleware.go | 66 +++ internal/pkg/otel/otel.go | 114 +++++ internal/pkg/points/convert.go | 34 +- internal/pkg/points/convert_test.go | 22 +- internal/router/router.go | 13 + internal/service/activity/scheduler.go | 61 +++ internal/service/task_center/service.go | 54 ++- .../service/task_center/task_center_test.go | 101 ++++- internal/service/user/item_card_redeem.go | 13 +- internal/service/user/orders_action.go | 8 +- internal/service/user/points_convert.go | 51 ++- internal/service/user/user.go | 2 + main.go | 171 +------- migration_add_template_id.sql | 1 + response.json | 1 + response_new.json | 1 + scripts/check_points_integrity.go | 70 +++ 69 files changed, 4831 insertions(+), 594 deletions(-) create mode 100644 backend.log create mode 100644 backend_assets.log create mode 100644 backend_debug.log create mode 100644 backend_debug_sql.log create mode 100644 backend_final.log create mode 100644 backend_fix_sql.log create mode 100644 backend_new.log create mode 100644 backend_prod.log create mode 100644 check_config.sql create mode 100644 cmd/diagnose_order/main.go create mode 100644 docs/loki_integration/ALIGNMENT_loki_integration.md create mode 100644 docs/standardize_points_ratio/DESIGN_standardize_points_ratio.md create mode 100644 docs/standardize_points_ratio/TASK_standardize_points_ratio.md create mode 100644 docs/standardize_points_ratio/migration.sql create mode 100644 docs/standardize_points_ratio/walkthrough.md create mode 100644 docs/yifanshang_count_card_fix/ALIGNMENT_yifanshang_count_card_fix.md create mode 100644 docs/yifanshang_count_card_fix/CONSENSUS_yifanshang_count_card_fix.md create mode 100644 docs/yifanshang_count_card_fix/DESIGN_yifanshang_count_card_fix.md create mode 100644 docs/yifanshang_count_card_fix/FINAL_yifanshang_count_card_fix.md create mode 100644 docs/yifanshang_count_card_fix/TASK_yifanshang_count_card_fix.md create mode 100644 docs/yifanshang_count_card_fix/TODO_yifanshang_count_card_fix.md create mode 100644 docs/玩家管理Bug修复/ALIGNMENT_玩家管理Bug修复.md create mode 100644 docs/玩家管理Bug修复/CONSENSUS_玩家管理Bug修复.md create mode 100644 docs/玩家管理Bug修复/DESIGN_玩家管理Bug修复.md create mode 100644 internal/api/admin/dashboard_activity.go create mode 100644 internal/api/admin/dashboard_spending.go create mode 100644 internal/api/common/config.go create mode 100644 internal/pkg/otel/middleware.go create mode 100644 internal/pkg/otel/otel.go create mode 100644 migration_add_template_id.sql create mode 100644 response.json create mode 100644 response_new.json create mode 100644 scripts/check_points_integrity.go diff --git a/.DS_Store b/.DS_Store index 5fc34f2bb06ba79b597f4698dfdfad4f662c43c7..ac017fc8fad14fe7899722db3b23732f35d41868 100644 GIT binary patch delta 34 qcmZn(XbIRbRdDii@jaV^g|_f+W>xsZ!fa$|JXt~9ezU9SK_&q6hzxfC delta 39 vcmZn(XbIRbRd6!5a5;yCrGbuuk*USxa{~681BJHmZe~~b#j^Ro2s1MP17-~7 diff --git a/Dockerfile b/Dockerfile index 2769856..de07616 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build stage -FROM golang:1.23-alpine AS builder +FROM golang:1.24-alpine AS builder # Set working directory WORKDIR /app @@ -12,20 +12,20 @@ COPY go.mod go.sum ./ # Set Go environment variables and proxy ENV GO111MODULE=on \ - CGO_ENABLED=0 \ - GOOS=linux \ - GOARCH=amd64 \ - GOPROXY=https://goproxy.cn,https://goproxy.io,direct \ - GOSUMDB=sum.golang.google.cn + CGO_ENABLED=0 \ + GOOS=linux \ + GOARCH=amd64 \ + GOPROXY=https://goproxy.cn,https://goproxy.io,direct \ + GOSUMDB=sum.golang.google.cn # Download dependencies with retry mechanism RUN go mod download || \ - (echo "Retrying with different proxy..." && \ - go env -w GOPROXY=https://goproxy.io,https://mirrors.aliyun.com/goproxy/,direct && \ - go mod download) || \ - (echo "Final retry with direct mode..." && \ - go env -w GOPROXY=direct && \ - go mod download) + (echo "Retrying with different proxy..." && \ + go env -w GOPROXY=https://goproxy.io,https://mirrors.aliyun.com/goproxy/,direct && \ + go mod download) || \ + (echo "Final retry with direct mode..." && \ + go env -w GOPROXY=direct && \ + go mod download) # Copy source code 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 # Run the application -CMD ["./miniChat"] \ No newline at end of file +CMD ["sh", "-c", "./miniChat -env=${ACTIVE_ENV}"] \ No newline at end of file diff --git a/backend.log b/backend.log new file mode 100644 index 0000000..4590a6e --- /dev/null +++ b/backend.log @@ -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 diff --git a/backend_assets.log b/backend_assets.log new file mode 100644 index 0000000..47bb713 --- /dev/null +++ b/backend_assets.log @@ -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 diff --git a/backend_debug.log b/backend_debug.log new file mode 100644 index 0000000..1ae7686 --- /dev/null +++ b/backend_debug.log @@ -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} diff --git a/backend_debug_sql.log b/backend_debug_sql.log new file mode 100644 index 0000000..468b276 --- /dev/null +++ b/backend_debug_sql.log @@ -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} diff --git a/backend_final.log b/backend_final.log new file mode 100644 index 0000000..67a73ae --- /dev/null +++ b/backend_final.log @@ -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 diff --git a/backend_fix_sql.log b/backend_fix_sql.log new file mode 100644 index 0000000..340981a --- /dev/null +++ b/backend_fix_sql.log @@ -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 diff --git a/backend_new.log b/backend_new.log new file mode 100644 index 0000000..e128b26 --- /dev/null +++ b/backend_new.log @@ -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 diff --git a/backend_prod.log b/backend_prod.log new file mode 100644 index 0000000..06a6747 --- /dev/null +++ b/backend_prod.log @@ -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} diff --git a/check_config.sql b/check_config.sql new file mode 100644 index 0000000..ed6ef3d --- /dev/null +++ b/check_config.sql @@ -0,0 +1 @@ +SELECT config_key, config_value FROM sys_configs WHERE config_key = 'wechat_miniprogram_lottery_result_template_id'; diff --git a/cmd/diagnose_order/main.go b/cmd/diagnose_order/main.go new file mode 100644 index 0000000..6e8d022 --- /dev/null +++ b/cmd/diagnose_order/main.go @@ -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") +} diff --git a/configs/configs.go b/configs/configs.go index 86cd08d..778c954 100644 --- a/configs/configs.go +++ b/configs/configs.go @@ -89,6 +89,11 @@ type Config struct { AppSecret string `mapstructure:"app_secret" toml:"app_secret"` NotifyURL string `mapstructure:"notify_url" toml:"notify_url"` } `mapstructure:"douyin" toml:"douyin"` + + Otel struct { + Enabled bool `mapstructure:"enabled" toml:"enabled"` + Endpoint string `mapstructure:"endpoint" toml:"endpoint"` + } `mapstructure:"otel" toml:"otel"` } var ( @@ -166,6 +171,38 @@ func init() { if v := os.Getenv("ALIYUN_SMS_TEMPLATE_CODE"); 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 { diff --git a/configs/pro_configs.toml b/configs/pro_configs.toml index 107839e..d1317f1 100644 --- a/configs/pro_configs.toml +++ b/configs/pro_configs.toml @@ -1,42 +1,60 @@ -[mysql] - [mysql.read] - addr = "127.0.0.1:3306" - user = "root" - pass = "123456" - name = "bindbox_game" - [mysql.write] - addr = "127.0.0.1:3306" - user = "root" - pass = "123456" - name = "bindbox_game" +[language] +local = 'zh-cn' + +[mysql.read] +addr = "mysql:3306" +user = "root" +pass = "bindbox2025kdy" +name = "bindbox_game" + +[mysql.write] +addr = "mysql:3306" +user = "root" +pass = "bindbox2025kdy" +name = "bindbox_game" [redis] - addr = "127.0.0.1:6379" - pass = "" - db = 0 +addr = "redis:6379" +pass = "" +db = 0 [jwt] admin_secret = "m9ycX9RTPyuYTWw9FrCc" patient_secret = "AppUserJwtSecret2025" -[random] - - commit_master_key = "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" - [wechat] - app_id = "" - app_secret = "" - lottery_result_template_id = "" +app_id = "wx26ad074017e1e63f" +app_secret = "026c19ce4f3bb090c56573024c59a8be" +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] - mchid = "" - serial_no = "" - private_key_path = "" - api_v3_key = "" - notify_url = "https://example.com/api/pay/wechat/notify" +mchid = "1610439635" +serial_no = "3AFD505D597831F8E931EBFFEEB5976B81F66F03" +private_key_path = "./configs/cert/apiclient_key.pem" +api_v3_key = "3tbwEFZV3fZtOslpUJC7Sacb8qjzhm05" +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] access_key_id = "" access_key_secret = "" sign_name = "" -template_code = "" \ No newline at end of file +template_code = "" + +[internal] +api_key = "bindbox-internal-secret-2024" + +[otel] +enabled = true +endpoint = "tempo:4318" \ No newline at end of file diff --git a/docs/loki_integration/ALIGNMENT_loki_integration.md b/docs/loki_integration/ALIGNMENT_loki_integration.md new file mode 100644 index 0000000..03acfd9 --- /dev/null +++ b/docs/loki_integration/ALIGNMENT_loki_integration.md @@ -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 和磁盘的影响极小,完全适合本地开发和中小型服务器部署。 diff --git a/docs/standardize_points_ratio/DESIGN_standardize_points_ratio.md b/docs/standardize_points_ratio/DESIGN_standardize_points_ratio.md new file mode 100644 index 0000000..de3108e --- /dev/null +++ b/docs/standardize_points_ratio/DESIGN_standardize_points_ratio.md @@ -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. **验证**: 检查某测试账号积分是否符合预期。 diff --git a/docs/standardize_points_ratio/TASK_standardize_points_ratio.md b/docs/standardize_points_ratio/TASK_standardize_points_ratio.md new file mode 100644 index 0000000..066ea32 --- /dev/null +++ b/docs/standardize_points_ratio/TASK_standardize_points_ratio.md @@ -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)。 diff --git a/docs/standardize_points_ratio/migration.sql b/docs/standardize_points_ratio/migration.sql new file mode 100644 index 0000000..95ac626 --- /dev/null +++ b/docs/standardize_points_ratio/migration.sql @@ -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. diff --git a/docs/standardize_points_ratio/walkthrough.md b/docs/standardize_points_ratio/walkthrough.md new file mode 100644 index 0000000..9b5df23 --- /dev/null +++ b/docs/standardize_points_ratio/walkthrough.md @@ -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**. diff --git a/docs/yifanshang_count_card_fix/ALIGNMENT_yifanshang_count_card_fix.md b/docs/yifanshang_count_card_fix/ALIGNMENT_yifanshang_count_card_fix.md new file mode 100644 index 0000000..6d9330f --- /dev/null +++ b/docs/yifanshang_count_card_fix/ALIGNMENT_yifanshang_count_card_fix.md @@ -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。 diff --git a/docs/yifanshang_count_card_fix/CONSENSUS_yifanshang_count_card_fix.md b/docs/yifanshang_count_card_fix/CONSENSUS_yifanshang_count_card_fix.md new file mode 100644 index 0000000..48a0672 --- /dev/null +++ b/docs/yifanshang_count_card_fix/CONSENSUS_yifanshang_count_card_fix.md @@ -0,0 +1,43 @@ +# 共识:修复一番赏次数卡支付与退款逻辑 + +## 1. 需求描述与验收标准 +### 需求描述 +- **不掉支付**: 使用次数卡支付时,若订单金额为0,前端不应拉起微信支付,直接视为支付成功。 +- **退款退卡**: 次数卡支付的订单退款时,需准确退还使用的次数卡次数。 + +### 验收标准 +1. **前端支付流程**: + - [ ] 使用次数卡全额抵扣时,点击“去支付”后直接弹出成功/翻牌界面,无微信支付弹窗。 + - [ ] 混合支付(次数卡不足补差价)时,仍正常拉起支付(本次主要关注全额抵扣)。 +2. **后端订单记录**: + - [ ] 数据库 `orders.remark` 字段正确记录 `gp_use::`。 + - [ ] 支持单次使用多张次数卡的情况(如 `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 逻辑需升级以支持多卡/多数量退还。 diff --git a/docs/yifanshang_count_card_fix/DESIGN_yifanshang_count_card_fix.md b/docs/yifanshang_count_card_fix/DESIGN_yifanshang_count_card_fix.md new file mode 100644 index 0000000..c6e2fc4 --- /dev/null +++ b/docs/yifanshang_count_card_fix/DESIGN_yifanshang_count_card_fix.md @@ -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". diff --git a/docs/yifanshang_count_card_fix/FINAL_yifanshang_count_card_fix.md b/docs/yifanshang_count_card_fix/FINAL_yifanshang_count_card_fix.md new file mode 100644 index 0000000..aa02c0b --- /dev/null +++ b/docs/yifanshang_count_card_fix/FINAL_yifanshang_count_card_fix.md @@ -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. 交付结论 +以完成所有关键路径的修复,代码已准备就绪,可以部署测试。 diff --git a/docs/yifanshang_count_card_fix/TASK_yifanshang_count_card_fix.md b/docs/yifanshang_count_card_fix/TASK_yifanshang_count_card_fix.md new file mode 100644 index 0000000..a36a95a --- /dev/null +++ b/docs/yifanshang_count_card_fix/TASK_yifanshang_count_card_fix.md @@ -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). diff --git a/docs/yifanshang_count_card_fix/TODO_yifanshang_count_card_fix.md b/docs/yifanshang_count_card_fix/TODO_yifanshang_count_card_fix.md new file mode 100644 index 0000000..66b4740 --- /dev/null +++ b/docs/yifanshang_count_card_fix/TODO_yifanshang_count_card_fix.md @@ -0,0 +1,12 @@ +# 待办事项:一番赏次数卡修复后续 + +## 1. 测试与验证 +- [ ] **真机验证**: 需要在真机小程序环境测试使用次数卡购买一番赏,确认是否直接跳过支付。 +- [ ] **退款验证**: 需要在管理后台对生成的次数卡订单进行退款,并检查数据库 `user_game_passes` 表确认次数是否正确恢复。 + +## 2. 遗留/潜在问题 +- [ ] **历史订单处理**: 此修复仅对新生成的订单生效(因为旧订单缺少 `gp_use:ID:Count` 记录)。旧订单退款仍将使用旧逻辑(仅退 1 次)。如果需要处理大量历史订单的批量退款,建议通过 SQL 手动修复或编写一次性脚本。 +- [ ] **多端兼容**: 目前仅修改了 `bindbox-mini`(小程序端)。如果存在 App 端或 H5 端,需确认是否复用相同逻辑或需要单独修改。 + +## 3. 配置建议 +- 无新增配置项。 diff --git a/docs/玩家管理Bug修复/ALIGNMENT_玩家管理Bug修复.md b/docs/玩家管理Bug修复/ALIGNMENT_玩家管理Bug修复.md new file mode 100644 index 0000000..a2d7445 --- /dev/null +++ b/docs/玩家管理Bug修复/ALIGNMENT_玩家管理Bug修复.md @@ -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** 资产搜索: + - 支持哪些搜索条件?商品名称?订单号? + - 是否需要后端支持模糊搜索? diff --git a/docs/玩家管理Bug修复/CONSENSUS_玩家管理Bug修复.md b/docs/玩家管理Bug修复/CONSENSUS_玩家管理Bug修复.md new file mode 100644 index 0000000..854292f --- /dev/null +++ b/docs/玩家管理Bug修复/CONSENSUS_玩家管理Bug修复.md @@ -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**: 用户资产列表可以按商品名称搜索 diff --git a/docs/玩家管理Bug修复/DESIGN_玩家管理Bug修复.md b/docs/玩家管理Bug修复/DESIGN_玩家管理Bug修复.md new file mode 100644 index 0000000..0290fbd --- /dev/null +++ b/docs/玩家管理Bug修复/DESIGN_玩家管理Bug修复.md @@ -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` | 修改 | 资产列表添加搜索 | diff --git a/go.mod b/go.mod index e2350d6..d340f20 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module bindbox-game -go 1.19 +go 1.24.0 + +toolchain go1.24.2 require ( 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/spf13/cast v1.5.1 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/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.2 github.com/tealeg/xlsx v1.0.5 github.com/tencentyun/cos-go-sdk-v5 v0.7.37 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/zap v1.26.0 - golang.org/x/crypto v0.27.0 - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d + golang.org/x/crypto v0.44.0 + golang.org/x/tools v0.38.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c gorm.io/driver/mysql v1.5.2 @@ -58,6 +65,7 @@ require ( github.com/aliyun/credentials-go v1.4.5 // indirect github.com/beorn7/perks v1.0.1 // 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/clbanning/mxj v1.8.4 // 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/gabriel-vasile/mimetype v1.4.2 // 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/jsonreference v0.19.6 // indirect github.com/go-openapi/spec v0.20.4 // indirect github.com/go-openapi/swag v0.19.15 // indirect github.com/go-sql-driver/mysql v1.7.0 // 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/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/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect @@ -107,14 +119,20 @@ require ( github.com/tjfoc/gmsm v1.4.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // 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/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + golang.org/x/mod v0.29.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.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/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index fa03f34..b5587dc 100644 --- a/go.sum +++ b/go.sum @@ -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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0= github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE= 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.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 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/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= 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/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/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 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/go.mod h1:RrehPJasUVBPK6yTUwOl8/NP6i0vbUgmxtis+Z5KE90= 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/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-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.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= 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-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/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.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 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/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/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 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/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= @@ -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.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +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 v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 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.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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 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/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 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/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.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.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.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/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/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.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 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-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/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/go.mod h1:h5JXMtcgkqxltElhpF7PPicNyvFDWzi8VCSHdNjG7KY= 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/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= 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/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/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/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 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/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= 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/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.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 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/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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/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/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= 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/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.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.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/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= 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.1/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.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/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 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.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 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.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 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.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= +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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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.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.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.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-20180826012351-8a410e7b638d/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.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 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.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +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-20190226205417-e64efc72b421/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.6.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.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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.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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +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/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= @@ -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.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +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-20190308202827-9d24e82272b4/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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 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.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-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-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.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 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-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/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.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 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.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.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-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 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.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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +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 v1.0.0-20180628173108-788fd7840127/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/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= 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.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU= 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/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig= gorm.io/gen v0.3.26 h1:sFf1j7vNStimPRRAtH4zz5NiHM+1dr6eA9aaRdplyhY= gorm.io/gen v0.3.26/go.mod h1:a5lq5y3w4g5LMxBcw0wnO6tYUCdNutWODq5LrIt75LE= gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= diff --git a/internal/api/activity/lottery_app.go b/internal/api/activity/lottery_app.go index 36bbc96..7f39647 100644 --- a/internal/api/activity/lottery_app.go +++ b/internal/api/activity/lottery_app.go @@ -64,6 +64,10 @@ func (h *handler) JoinLottery() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) 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) 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) @@ -263,6 +267,13 @@ func (h *handler) JoinLottery() core.HandlerFunc { return err } 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 { @@ -273,15 +284,8 @@ func (h *handler) JoinLottery() core.HandlerFunc { order.ActualAmount = 0 order.SourceType = 4 // Cleanly mark as Game Pass source - // existing lottery logic sets SourceType based on "h.orderModel" which defaults to something? - // h.orderModel(..., c) implementation needs to be checked or inferred. - // 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" - } + // Legacy marker for backward compatibility or simple check + order.Remark += "|use_game_pass" // Note: If we change SourceType to 4, ProcessOrderLottery might skip it if checks SourceType. // 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. @@ -289,24 +293,21 @@ func (h *handler) JoinLottery() core.HandlerFunc { if !useGamePass && req.UsePoints != nil && *req.UsePoints > 0 { bal, _ := h.user.GetPointsBalance(ctx.RequestContext(), userID) - usePts := *req.UsePoints - if bal > 0 && usePts > bal { - usePts = bal + // req.UsePoints 是前端传入的积分数,需要转换为分 + usePtsCents, _ := h.user.PointsToCents(ctx.RequestContext(), *req.UsePoints) + // bal 已经是分单位 + if bal > 0 && usePtsCents > bal { + usePtsCents = bal } - ratePtsPerCent, _ := h.user.CentsToPoints(ctx.RequestContext(), 1) - if ratePtsPerCent <= 0 { - ratePtsPerCent = 1 - } - deductCents := usePts / ratePtsPerCent + // deductCents 是要从订单金额中抵扣的分数 + deductCents := usePtsCents if deductCents > order.ActualAmount { deductCents = order.ActualAmount } if deductCents > 0 { - needPts := deductCents * ratePtsPerCent - if needPts > usePts { - needPts = usePts - } + // needPts 是实际需要扣除的分数 + needPts := deductCents // Inline ConsumePointsFor logic using tx // 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() @@ -618,32 +619,34 @@ func (h *handler) validateIchibanSlots(ctx core.Context, req *joinLotteryRequest if totalSlots <= 0 { return core.Error(http.StatusBadRequest, 170008, "no slots") } - if len(req.SlotIndex) > 0 { - if req.Count <= 0 || req.Count != int64(len(req.SlotIndex)) { - return core.Error(http.StatusBadRequest, code.ParamBindError, "参数错误") - } + // 1. 强制校验:必须选择位置 + if len(req.SlotIndex) == 0 { + 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. 内存中去重和范围检查 - selectedSlots := make([]int64, 0, len(req.SlotIndex)) - seen := make(map[int64]struct{}, len(req.SlotIndex)) - for _, si := range req.SlotIndex { - if _, ok := seen[si]; ok { - 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) + // 2. 内存中去重和范围检查 + selectedSlots := make([]int64, 0, len(req.SlotIndex)) + seen := make(map[int64]struct{}, len(req.SlotIndex)) + for _, si := range req.SlotIndex { + if _, ok := seen[si]; ok { + 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) + } - // 2. 批量查询数据库检查格位是否已被占用 - 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 - if occupiedCount > 0 { - // 如果有占用,为了告知具体是哪个位置,可以打个 log 或者简单的直接返回错误 - return core.Error(http.StatusBadRequest, 170007, "部分位置已被占用,请刷新重试") - } + // 3. 批量查询数据库检查格位是否已被占用 + 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 + if occupiedCount > 0 { + // 即使是并发场景,这里做一个 Pre-check 也能拦截大部分冲突 + return core.Error(http.StatusBadRequest, 170007, "部分位置已被占用,请刷新重试") } return nil } diff --git a/internal/api/activity/matching_game_app.go b/internal/api/activity/matching_game_app.go index 30d06f5..90cbd18 100644 --- a/internal/api/activity/matching_game_app.go +++ b/internal/api/activity/matching_game_app.go @@ -30,6 +30,7 @@ type matchingGamePreOrderRequest struct { CouponID *int64 `json:"coupon_id"` ItemCardID *int64 `json:"item_card_id"` UseGamePass bool `json:"use_game_pass"` // 新增:是否使用次数卡 + Count int64 `json:"count"` // 新增:购买数量 } type matchingGamePreOrderResponse struct { @@ -82,6 +83,12 @@ func (h *handler) PreOrderMatchingGame() core.HandlerFunc { 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) // 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. diff --git a/internal/api/admin/dashboard_activity.go b/internal/api/admin/dashboard_activity.go new file mode 100644 index 0000000..5470b59 --- /dev/null +++ b/internal/api/admin/dashboard_activity.go @@ -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}) + } +} diff --git a/internal/api/admin/dashboard_admin.go b/internal/api/admin/dashboard_admin.go index 2aaab0c..b2e79f9 100644 --- a/internal/api/admin/dashboard_admin.go +++ b/internal/api/admin/dashboard_admin.go @@ -163,7 +163,7 @@ func (h *handler) DashboardCards() core.HandlerFunc { rsp.ItemCardSales = icCur rsp.DrawCount = dlCur rsp.NewUsers = nuCur - rsp.TotalPoints = tpCur + rsp.TotalPoints = int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), tpCur)) rsp.TotalCoupons = tcCur rsp.TotalItemCards = ticCur rsp.TotalGamePasses = tgpCur @@ -1695,10 +1695,17 @@ func (h *handler) OperationsPointsEconomySummary() core.HandlerFunc { 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{ - TotalIssued: issued, - TotalConsumed: consumed, - NetChange: issued - consumed, + TotalIssued: int64(issuedPoints), + TotalConsumed: int64(consumedPoints), + NetChange: int64(netChangePoints), ActiveUsersWithPoints: activeUsers, ConversionRate: float64(int(conversionRate*10)) / 10.0, }) @@ -1757,11 +1764,11 @@ func (h *handler) OperationsPointsTrend() core.HandlerFunc { out[i] = pointsTrendItem{ Date: b.Label, - Issued: issued, - Consumed: consumed, + Issued: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), issued)), + Consumed: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), consumed)), Expired: 0, - NetChange: netChange, - Balance: runningBalance, + NetChange: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), netChange)), + Balance: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), runningBalance)), } } @@ -1834,7 +1841,7 @@ func (h *handler) OperationsPointsStructure() core.HandlerFunc { } out = append(out, pointsStructureItem{ Category: name, - Amount: r.Total, + Amount: int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), r.Total)), Percentage: float64(int(pct*10)) / 10.0, Trend: "+0%", }) diff --git a/internal/api/admin/dashboard_spending.go b/internal/api/admin/dashboard_spending.go new file mode 100644 index 0000000..51ef8f8 --- /dev/null +++ b/internal/api/admin/dashboard_spending.go @@ -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, + }) + } +} diff --git a/internal/api/admin/pay_orders_admin.go b/internal/api/admin/pay_orders_admin.go index 32fb8f0..01969e4 100644 --- a/internal/api/admin/pay_orders_admin.go +++ b/internal/api/admin/pay_orders_admin.go @@ -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 { - Order *model.Orders `json:"order"` - Items []*model.OrderItems `json:"items"` - Shipments []*model.ShippingRecords `json:"shipments"` - Ledgers []*model.UserPointsLedger `json:"ledgers"` - User *model.Users `json:"user"` + Order *model.Orders `json:"order"` + Items []*model.OrderItems `json:"items"` + Shipments []*model.ShippingRecords `json:"shipments"` + Ledgers []adminOrderPointsLedgerItem `json:"ledgers"` + User *model.Users `json:"user"` Coupons []*struct { UserCouponID int64 `json:"user_coupon_id"` AppliedAmount int64 `json:"applied_amount"` @@ -644,7 +655,19 @@ func (h *handler) GetPayOrderDetail() core.HandlerFunc { rsp.Items = items rsp.Coupons = couponList 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 { unit := int64(0) if count > 0 { diff --git a/internal/api/admin/pay_orders_export.go b/internal/api/admin/pay_orders_export.go index f39186a..a357a8b 100644 --- a/internal/api/admin/pay_orders_export.go +++ b/internal/api/admin/pay_orders_export.go @@ -2,7 +2,6 @@ package admin import ( "bytes" - "fmt" "net/http" "time" @@ -50,14 +49,7 @@ func (h *handler) ExportPayOrders() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, 23001, err.Error())) 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() sheet, _ := file.AddSheet("orders") header := []string{"订单号", "用户ID", "来源", "状态", "总金额", "折扣", "积分抵扣(分)", "积分抵扣(积分)", "优惠券抵扣(分)", "实付", "支付时间", "创建时间"} @@ -76,13 +68,17 @@ func (h *handler) ExportPayOrders() core.HandlerFunc { } pa := o.PointsAmount 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) - if consumePointsSum > 0 { - pu = consumePointsSum - } else if pa > 0 { - pu = pa * pointsRate + // pu is Points Unit + pu := int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), consumePointsSum)) + if pu == 0 && pa > 0 { + // If no ledger, try converting from Cents + 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() var couponApplied int64 diff --git a/internal/api/admin/pay_refund_admin.go b/internal/api/admin/pay_refund_admin.go index c618e10..1488a49 100644 --- a/internal/api/admin/pay_refund_admin.go +++ b/internal/api/admin/pay_refund_admin.go @@ -239,17 +239,33 @@ func (h *handler) CreateRefund() core.HandlerFunc { } // 全额退款:回退次数卡(user_game_passes) - // 解析订单 remark 中的 game_pass:xxx ID - 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 { - // 恢复次数卡:remaining +1, total_used -1 - 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)) + // 优先解析新格式: gp_use:ID:Count (支持多张卡、多数量) + reGpNew := regexp.MustCompile(`gp_use:(\d+):(\d+)`) + matchesNew := reGpNew.FindAllStringSubmatch(order.Remark, -1) + if len(matchesNew) > 0 { + for _, m := range matchesNew { + gpID, _ := strconv.ParseInt(m[1], 10, 64) + gpCount, _ := strconv.ParseInt(m[2], 10, 64) + if gpID > 0 && gpCount > 0 { + 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 { + h.logger.Error(fmt.Sprintf("refund restore game_pass failed: order=%s gp_id=%d count=%d err=%v", order.OrderNo, gpID, gpCount, err)) + } 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)) + } } } } diff --git a/internal/api/admin/system_coupons.go b/internal/api/admin/system_coupons.go index f972acd..17b4c5c 100644 --- a/internal/api/admin/system_coupons.go +++ b/internal/api/admin/system_coupons.go @@ -132,21 +132,20 @@ func (h *handler) ModifySystemCoupon() core.HandlerFunc { } func (h *handler) DeleteSystemCoupon() core.HandlerFunc { - return func(ctx core.Context) { - idStr := ctx.Param("coupon_id") - id, _ := strconv.ParseInt(idStr, 10, 64) - if ctx.SessionUserInfo().IsSuper != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) - return - } - uid := int64(ctx.SessionUserInfo().Id) - 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 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) - return - } - ctx.Payload(pcSimpleMessage{Message: "操作成功"}) - } + return func(ctx core.Context) { + idStr := ctx.Param("coupon_id") + id, _ := strconv.ParseInt(idStr, 10, 64) + if ctx.SessionUserInfo().IsSuper != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) + return + } + set := map[string]any{"deleted_at": time.Now()} + 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())) + return + } + ctx.Payload(pcSimpleMessage{Message: "操作成功"}) + } } type listSystemCouponsRequest struct { diff --git a/internal/api/admin/users_admin.go b/internal/api/admin/users_admin.go index f021ecf..def1d70 100644 --- a/internal/api/admin/users_admin.go +++ b/internal/api/admin/users_admin.go @@ -1,6 +1,7 @@ package admin import ( + "math" "net/http" "strconv" "time" @@ -150,7 +151,7 @@ func (h *handler) ListAppUsers() core.HandlerFunc { Group(h.readDB.UserPoints.UserID). Scan(&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) inviterIDs := make([]int64, 0) @@ -318,6 +378,16 @@ func (h *handler) ListAppUsers() core.HandlerFunc { rsp.Total = total rsp.List = make([]adminUserItem, len(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{ ID: v.ID, Nickname: v.Nickname, @@ -329,7 +399,7 @@ func (h *handler) ListAppUsers() core.HandlerFunc { DouyinID: v.DouyinID, ChannelName: v.ChannelName, ChannelCode: v.ChannelCode, - PointsBalance: pointBalances[v.ID], + PointsBalance: pointsBal, CouponsCount: couponCounts[v.ID], ItemCardsCount: cardCounts[v.ID], TodayConsume: todayConsume[v.ID], @@ -337,8 +407,10 @@ func (h *handler) ListAppUsers() core.HandlerFunc { ThirtyDayConsume: thirtyDayConsume[v.ID], TotalConsume: totalConsume[v.ID], InviteCount: inviteCounts[v.ID], - GamePassCount: gamePassCounts[v.ID], - GameTicketCount: gameTicketCounts[v.ID], + GamePassCount: gpCount, + GameTicketCount: gtCount, + InventoryValue: invVal, + TotalAssetValue: assetVal, } } ctx.Payload(rsp) @@ -410,8 +482,9 @@ type listOrdersResponse struct { } type listInventoryRequest struct { - Page int `form:"page"` - PageSize int `form:"page_size"` + Page int `form:"page"` + PageSize int `form:"page_size"` + Keyword string `form:"keyword"` // 搜索关键词(商品名称) } type listInventoryResponse struct { Page int `json:"page"` @@ -485,6 +558,96 @@ func (h *handler) ListUserInventory() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "未传递用户ID")) 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) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 20105, err.Error())) @@ -680,11 +843,22 @@ type listPointsRequest struct { Page int `form:"page"` 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 { - Page int `json:"page"` - PageSize int `json:"page_size"` - Total int64 `json:"total"` - List []*model.UserPointsLedger `json:"list"` + Page int `json:"page"` + PageSize int `json:"page_size"` + Total int64 `json:"total"` + List []adminUserPointsLedgerItem `json:"list"` } // ListUserPoints 查看用户积分记录 @@ -721,7 +895,20 @@ func (h *handler) ListUserPoints() core.HandlerFunc { rsp.Page = req.Page rsp.PageSize = req.PageSize 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) } } @@ -751,6 +938,8 @@ type adminUserItem struct { InviteCount int64 `json:"invite_count"` // 邀请人数 GamePassCount int64 `json:"game_pass_count"` // 次数卡数量 GameTicketCount int64 `json:"game_ticket_count"` // 游戏资格数量 + InventoryValue int64 `json:"inventory_value"` // 持有商品总价值 + TotalAssetValue int64 `json:"total_asset_value"` // 总资产估值 } // ListAppUsers 管理端用户列表GetUserPointsBalance 查看用户积分余额 @@ -777,16 +966,16 @@ func (h *handler) GetUserPointsBalance() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, 20107, err.Error())) return } - rsp.Balance = total + rsp.Balance = int64(h.userSvc.CentsToPointsFloat(ctx.RequestContext(), total)) ctx.Payload(rsp) } } type addPointsRequest struct { - Points int64 `json:"points"` // 正数=增加,负数=扣减 - Kind string `json:"kind"` - Remark string `json:"remark"` - ValidDays *int `json:"valid_days"` + Points float64 `json:"points"` // 正数=增加,负数=扣减 + Kind string `json:"kind"` + Remark string `json:"remark"` + ValidDays *int `json:"valid_days"` } type addPointsResponse struct { Success bool `json:"success"` @@ -822,10 +1011,16 @@ func (h *handler) AddUserPoints() core.HandlerFunc { 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) - if balance+req.Points < 0 { + deductCents := -pointsCents + if balance < deductCents { ctx.AbortWithError(core.Error(http.StatusBadRequest, 20108, "积分余额不足,无法扣减")) return } @@ -835,14 +1030,15 @@ func (h *handler) AddUserPoints() core.HandlerFunc { var validEnd *time.Time now := time.Now() // 只有增加积分时才设置有效期 - if req.Points > 0 { + if pointsCents > 0 { validStart = &now if req.ValidDays != nil && *req.ValidDays > 0 { ve := now.Add(time.Duration(*req.ValidDays) * 24 * time.Hour) 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())) return } diff --git a/internal/api/admin/users_batch_admin.go b/internal/api/admin/users_batch_admin.go index 20eac4f..89b3ebf 100644 --- a/internal/api/admin/users_batch_admin.go +++ b/internal/api/admin/users_batch_admin.go @@ -1,144 +1,156 @@ package admin import ( - "net/http" + "net/http" - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/validation" - usersvc "bindbox-game/internal/service/user" + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/validation" + usersvc "bindbox-game/internal/service/user" ) type batchPointsRequest struct { - Users []int64 `json:"users" binding:"required"` - Amount int64 `json:"amount" binding:"min=1"` - Reason string `json:"reason"` - IdempotencyKey string `json:"idempotency_key"` + Users []int64 `json:"users" binding:"required"` + Amount int64 `json:"amount" binding:"min=1"` + Reason string `json:"reason"` + IdempotencyKey string `json:"idempotency_key"` } type batchCouponsRequest struct { - Users []int64 `json:"users" binding:"required"` - CouponID int64 `json:"coupon_id" binding:"required"` - QuantityPerUser int `json:"quantity_per_user"` - IdempotencyKey string `json:"idempotency_key"` + Users []int64 `json:"users" binding:"required"` + CouponID int64 `json:"coupon_id" binding:"required"` + QuantityPerUser int `json:"quantity_per_user"` + IdempotencyKey string `json:"idempotency_key"` } type batchRewardsRequest struct { - Users []int64 `json:"users" binding:"required"` - ProductID int64 `json:"product_id" binding:"required"` - Quantity int `json:"quantity" binding:"min=1"` - ActivityID *int64 `json:"activity_id"` - RewardID *int64 `json:"reward_id"` - AddressID *int64 `json:"address_id"` - Remark string `json:"remark"` - IdempotencyKey string `json:"idempotency_key"` + Users []int64 `json:"users" binding:"required"` + ProductID int64 `json:"product_id" binding:"required"` + Quantity int `json:"quantity" binding:"min=1"` + ActivityID *int64 `json:"activity_id"` + RewardID *int64 `json:"reward_id"` + AddressID *int64 `json:"address_id"` + Remark string `json:"remark"` + IdempotencyKey string `json:"idempotency_key"` } type batchItemResult struct { - UserID int64 `json:"user_id"` - Status string `json:"status"` - Message string `json:"message"` + UserID int64 `json:"user_id"` + Status string `json:"status"` + Message string `json:"message"` } type batchResponse struct { - Success int `json:"success"` - Failed int `json:"failed"` - Details []batchItemResult `json:"details"` + Success int `json:"success"` + Failed int `json:"failed"` + Details []batchItemResult `json:"details"` } func (h *handler) BatchAddUserPoints() core.HandlerFunc { - return func(ctx core.Context) { - if ctx.SessionUserInfo().IsSuper != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) - return - } - req := new(batchPointsRequest) - if err := ctx.ShouldBindJSON(req); err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) - return - } - if len(req.Users) != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作,请仅选择1位用户")) - return - } - 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 { - res.Failed++ - res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed", Message: err.Error()}) - } else { - res.Success++ - res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"}) - } - } - ctx.Payload(res) - } + return func(ctx core.Context) { + if ctx.SessionUserInfo().IsSuper != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) + return + } + req := new(batchPointsRequest) + if err := ctx.ShouldBindJSON(req); err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) + return + } + if len(req.Users) != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作,请仅选择1位用户")) + return + } + res := &batchResponse{Details: make([]batchItemResult, 0, len(req.Users))} + // 将管理员输入的积分转换为分 + amountCents, _ := h.userSvc.PointsToCents(ctx.RequestContext(), req.Amount) + for _, uid := range req.Users { + if err := h.userSvc.AddPoints(ctx.RequestContext(), uid, amountCents, "manual", req.Reason, nil, nil); err != nil { + res.Failed++ + res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed", Message: err.Error()}) + } else { + res.Success++ + res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"}) + } + } + ctx.Payload(res) + } } func (h *handler) BatchAddUserCoupons() core.HandlerFunc { - return func(ctx core.Context) { - if ctx.SessionUserInfo().IsSuper != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) - return - } - req := new(batchCouponsRequest) - if err := ctx.ShouldBindJSON(req); err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) - return - } - if len(req.Users) != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作,请仅选择1位用户")) - return - } - if req.QuantityPerUser <= 0 { req.QuantityPerUser = 1 } - if req.QuantityPerUser > 5 { req.QuantityPerUser = 5 } - res := &batchResponse{Details: make([]batchItemResult, 0, len(req.Users))} - for _, uid := range req.Users { - ok := true - for i := 0; i < req.QuantityPerUser; i++ { - if err := h.userSvc.AddCoupon(ctx.RequestContext(), uid, req.CouponID); err != nil { ok = false } - } - if ok { - res.Success++ - 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) - } + return func(ctx core.Context) { + if ctx.SessionUserInfo().IsSuper != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) + return + } + req := new(batchCouponsRequest) + if err := ctx.ShouldBindJSON(req); err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) + return + } + if len(req.Users) != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作,请仅选择1位用户")) + return + } + if req.QuantityPerUser <= 0 { + req.QuantityPerUser = 1 + } + if req.QuantityPerUser > 5 { + req.QuantityPerUser = 5 + } + res := &batchResponse{Details: make([]batchItemResult, 0, len(req.Users))} + for _, uid := range req.Users { + ok := true + for i := 0; i < req.QuantityPerUser; i++ { + if err := h.userSvc.AddCoupon(ctx.RequestContext(), uid, req.CouponID); err != nil { + ok = false + } + } + if ok { + res.Success++ + 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 { - return func(ctx core.Context) { - if ctx.SessionUserInfo().IsSuper != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) - return - } - req := new(batchRewardsRequest) - if err := ctx.ShouldBindJSON(req); err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) - return - } - if len(req.Users) != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作,请仅选择1位用户")) - return - } - if req.Quantity <= 0 { req.Quantity = 1 } - if req.Quantity > 10 { req.Quantity = 10 } - 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} - for _, uid := range req.Users { - _, err := h.userSvc.GrantReward(ctx.RequestContext(), uid, r) - if err != nil { - res.Failed++ - res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed", Message: err.Error()}) - } else { - res.Success++ - res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"}) - } - } - ctx.Payload(res) - } -} \ No newline at end of file + return func(ctx core.Context) { + if ctx.SessionUserInfo().IsSuper != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) + return + } + req := new(batchRewardsRequest) + if err := ctx.ShouldBindJSON(req); err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) + return + } + if len(req.Users) != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, "当前仅支持单用户操作,请仅选择1位用户")) + return + } + if req.Quantity <= 0 { + req.Quantity = 1 + } + if req.Quantity > 10 { + req.Quantity = 10 + } + 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} + for _, uid := range req.Users { + _, err := h.userSvc.GrantReward(ctx.RequestContext(), uid, r) + if err != nil { + res.Failed++ + res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "failed", Message: err.Error()}) + } else { + res.Success++ + res.Details = append(res.Details, batchItemResult{UserID: uid, Status: "success"}) + } + } + ctx.Payload(res) + } +} diff --git a/internal/api/admin/users_profile.go b/internal/api/admin/users_profile.go index 0a5748c..18508fb 100644 --- a/internal/api/admin/users_profile.go +++ b/internal/api/admin/users_profile.go @@ -104,11 +104,11 @@ func (h *handler) GetUserProfile() core.HandlerFunc { // 3. 生命周期财务指标 // 3.1 消费统计 type orderStats struct { - TotalPaid int64 + TotalPaid *int64 OrderCount int64 - TodayPaid int64 - SevenDayPaid int64 - ThirtyDayPaid int64 + TodayPaid *int64 + SevenDayPaid *int64 + ThirtyDayPaid *int64 } var os orderStats now := time.Now() @@ -147,11 +147,19 @@ func (h *handler) GetUserProfile() core.HandlerFunc { Where(h.readDB.Orders.CreatedAt.Gte(thirtyDayStart)). Scan(&os.ThirtyDayPaid) - rsp.LifetimeStats.TotalPaid = os.TotalPaid + if os.TotalPaid != nil { + rsp.LifetimeStats.TotalPaid = *os.TotalPaid + } rsp.LifetimeStats.OrderCount = os.OrderCount - rsp.LifetimeStats.TodayPaid = os.TodayPaid - rsp.LifetimeStats.SevenDayPaid = os.SevenDayPaid - rsp.LifetimeStats.ThirtyDayPaid = os.ThirtyDayPaid + if os.TodayPaid != nil { + rsp.LifetimeStats.TodayPaid = *os.TodayPaid + } + if os.SevenDayPaid != nil { + rsp.LifetimeStats.SevenDayPaid = *os.SevenDayPaid + } + if os.ThirtyDayPaid != nil { + rsp.LifetimeStats.ThirtyDayPaid = *os.ThirtyDayPaid + } // 3.2 累计退款 var totalRefunded int64 diff --git a/internal/api/admin/users_profit_loss.go b/internal/api/admin/users_profit_loss.go index 3b04e53..4559e4f 100644 --- a/internal/api/admin/users_profit_loss.go +++ b/internal/api/admin/users_profit_loss.go @@ -94,12 +94,16 @@ func (h *handler) GetUserProfitLossTrend() core.HandlerFunc { // --- 2. 获取订单数据(仅 status=2 已支付) --- // 注意:为了计算累计趋势,我们需要获取 start 之前的所有已支付订单总额作为基数 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()). Where(h.readDB.Orders.UserID.Eq(userID)). Where(h.readDB.Orders.Status.Eq(2)). Where(h.readDB.Orders.CreatedAt.Lt(start)). - Scan(&baseCost) + Scan(&baseCostPtr) + if baseCostPtr != nil { + baseCost = *baseCostPtr + } // 扣除历史退款 (如果有的话,此处简化处理,主要关注当前范围内的波动) var baseRefund int64 = 0 @@ -186,11 +190,15 @@ func (h *handler) GetUserProfitLossTrend() core.HandlerFunc { // 汇总数据 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()). Where(h.readDB.Orders.UserID.Eq(userID)). Where(h.readDB.Orders.Status.Eq(2)). - Scan(&totalCost) + Scan(&totalCostPtr) + if totalCostPtr != nil { + totalCost = *totalCostPtr + } var totalRefund int64 = 0 _ = h.repo.GetDbR().Raw(` @@ -227,3 +235,294 @@ func (h *handler) GetUserProfitLossTrend() core.HandlerFunc { 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] +} diff --git a/internal/api/app/product.go b/internal/api/app/product.go index 15082e1..e579ab0 100644 --- a/internal/api/app/product.go +++ b/internal/api/app/product.go @@ -38,13 +38,13 @@ type listAppProductsRequest struct { } type listAppProductsItem struct { - ID int64 `json:"id"` - Name string `json:"name"` - MainImage string `json:"main_image"` - Price int64 `json:"price"` - PointsRequired int64 `json:"points_required"` - Sales int64 `json:"sales"` - InStock bool `json:"in_stock"` + ID int64 `json:"id"` + Name string `json:"name"` + MainImage string `json:"main_image"` + Price int64 `json:"price"` + PointsRequired float64 `json:"points_required"` // 积分(分/rate)` + Sales int64 `json:"sales"` + InStock bool `json:"in_stock"` } 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))} 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} } ctx.Payload(rsp) @@ -99,7 +99,7 @@ type getAppProductDetailResponse struct { Name string `json:"name"` Album []string `json:"album"` Price int64 `json:"price"` - PointsRequired int64 `json:"points_required"` + PointsRequired float64 `json:"points_required"` // 积分(分/rate)` Sales int64 `json:"sales"` Stock int64 `json:"stock"` 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))) 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))} 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} } ctx.Payload(rsp) diff --git a/internal/api/app/store.go b/internal/api/app/store.go index 94c1aaf..232d289 100644 --- a/internal/api/app/store.go +++ b/internal/api/app/store.go @@ -25,21 +25,24 @@ type listStoreItemsRequest struct { Kind string `form:"kind"` Page int `form:"page"` PageSize int `form:"page_size"` + Keyword string `form:"keyword"` // 关键词搜索 + PriceMin *int64 `form:"price_min"` // 最低积分价格(积分单位) + PriceMax *int64 `form:"price_max"` // 最高积分价格(积分单位) } type listStoreItem struct { - ID int64 `json:"id"` - Kind string `json:"kind"` - Name string `json:"name"` - MainImage string `json:"main_image"` - Price int64 `json:"price"` - PointsRequired int64 `json:"points_required"` - InStock bool `json:"in_stock"` - Status int32 `json:"status"` - DiscountType int32 `json:"discount_type"` - DiscountValue int64 `json:"discount_value"` - MinSpend int64 `json:"min_spend"` - Supported bool `json:"supported"` + ID int64 `json:"id"` + Kind string `json:"kind"` + Name string `json:"name"` + MainImage string `json:"main_image"` + Price int64 `json:"price"` + PointsRequired float64 `json:"points_required"` // 积分(分/rate)` + InStock bool `json:"in_stock"` + Status int32 `json:"status"` + DiscountType int32 `json:"discount_type"` + DiscountValue int64 `json:"discount_value"` + MinSpend int64 `json:"min_spend"` + Supported bool `json:"supported"` } type listStoreItemsResponse struct { @@ -83,32 +86,76 @@ func (h *storeHandler) ListStoreItemsForApp() core.HandlerFunc { offset := (req.Page - 1) * 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 { case "item_card": 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() rows, _ := q.Order(h.readDB.SystemItemCards.ID.Desc()).Offset(offset).Limit(limit).Find() list = make([]listStoreItem, len(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} } case "coupon": 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() rows, _ := q.Order(h.readDB.SystemCoupons.ID.Desc()).Offset(offset).Limit(limit).Find() list = make([]listStoreItem, len(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} } default: // product 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() rows, _ := q.Order(h.readDB.Products.ID.Desc()).Offset(offset).Limit(limit).Find() list = make([]listStoreItem, len(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} } } diff --git a/internal/api/common/config.go b/internal/api/common/config.go new file mode 100644 index 0000000..67713d3 --- /dev/null +++ b/internal/api/common/config.go @@ -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) + } +} diff --git a/internal/api/user/points_app.go b/internal/api/user/points_app.go index 6ae221f..812861c 100644 --- a/internal/api/user/points_app.go +++ b/internal/api/user/points_app.go @@ -1,12 +1,12 @@ package app import ( - "net/http" + "net/http" - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/validation" - "bindbox-game/internal/repository/mysql/model" + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/validation" + "bindbox-game/internal/repository/mysql/model" ) type listPointsRequest struct { @@ -20,7 +20,7 @@ type listPointsResponse struct { List []*model.UserPointsLedger `json:"list"` } type pointsBalanceResponse struct { - Balance int64 `json:"balance"` + Balance float64 `json:"balance"` // 积分(分/rate) } // ListUserPoints 查看用户积分记录 @@ -29,8 +29,8 @@ type pointsBalanceResponse struct { // @Tags APP端.用户 // @Accept json // @Produce json - // @Param user_id path integer true "用户ID" - // @Security LoginVerifyToken +// @Param user_id path integer true "用户ID" +// @Security LoginVerifyToken // @Param page query int true "页码" default(1) // @Param page_size query int true "每页数量,最多100" default(20) // @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))) return } - userID := int64(ctx.SessionUserInfo().Id) - items, total, err := h.user.ListPointsLedger(ctx.RequestContext(), userID, req.Page, req.PageSize) + userID := int64(ctx.SessionUserInfo().Id) + items, total, err := h.user.ListPointsLedger(ctx.RequestContext(), userID, req.Page, req.PageSize) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 10004, err.Error())) return @@ -64,21 +64,21 @@ func (h *handler) ListUserPoints() core.HandlerFunc { // @Tags APP端.用户 // @Accept json // @Produce json - // @Param user_id path integer true "用户ID" - // @Security LoginVerifyToken +// @Param user_id path integer true "用户ID" +// @Security LoginVerifyToken // @Success 200 {object} pointsBalanceResponse // @Failure 400 {object} code.Failure // @Router /api/app/users/{user_id}/points/balance [get] func (h *handler) GetUserPointsBalance() core.HandlerFunc { return func(ctx core.Context) { rsp := new(pointsBalanceResponse) - userID := int64(ctx.SessionUserInfo().Id) - total, err := h.user.GetPointsBalance(ctx.RequestContext(), userID) + userID := int64(ctx.SessionUserInfo().Id) + total, err := h.user.GetPointsBalance(ctx.RequestContext(), userID) if err != nil { ctx.AbortWithError(core.Error(http.StatusBadRequest, 10005, err.Error())) return } - rsp.Balance = total + rsp.Balance = h.user.CentsToPointsFloat(ctx.RequestContext(), total) ctx.Payload(rsp) } } diff --git a/internal/api/user/points_redeem_coupon_app.go b/internal/api/user/points_redeem_coupon_app.go index a202a6a..651c1c8 100644 --- a/internal/api/user/points_redeem_coupon_app.go +++ b/internal/api/user/points_redeem_coupon_app.go @@ -52,13 +52,19 @@ func (h *handler) RedeemPointsToCoupon() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, 150002, "only amount coupons supported")) return } - needPoints, _ := h.user.CentsToPoints(ctx.RequestContext(), sc.DiscountValue) - if needPoints <= 0 { - needPoints = 1 + // sc.DiscountValue 是优惠券面值(分),直接用于扣除 + // 例如:30 元优惠券 = 3000 分 + 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 { - 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 } if err := h.user.AddCoupon(ctx.RequestContext(), userID, req.CouponID); err != nil { diff --git a/internal/api/user/points_redeem_product_app.go b/internal/api/user/points_redeem_product_app.go index 1541daf..16f6932 100644 --- a/internal/api/user/points_redeem_product_app.go +++ b/internal/api/user/points_redeem_product_app.go @@ -59,12 +59,13 @@ func (h *handler) RedeemPointsToProduct() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, 150105, "商品库存不足,请联系客服处理")) return } - ptsPerUnit, _ := h.user.CentsToPoints(ctx.RequestContext(), prod.Price) - needPoints := ptsPerUnit * int64(req.Quantity) - if needPoints <= 0 { - needPoints = 1 + // prod.Price 是商品价格(分),直接用于扣除 + // 例如:30 元商品 = 3000 分 + needCents := prod.Price * int64(req.Quantity) + 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 { errMsg := err.Error() if errMsg == "insufficient_points" { @@ -73,7 +74,7 @@ func (h *handler) RedeemPointsToProduct() core.HandlerFunc { ctx.AbortWithError(core.Error(http.StatusBadRequest, 150102, errMsg)) 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 { ctx.AbortWithError(core.Error(http.StatusBadRequest, 150103, err.Error())) return diff --git a/internal/api/user/profile_app.go b/internal/api/user/profile_app.go index 77d903c..7d3111a 100644 --- a/internal/api/user/profile_app.go +++ b/internal/api/user/profile_app.go @@ -35,15 +35,18 @@ func (h *handler) GetUserProfile() core.HandlerFunc { } balance, _ := h.user.GetPointsBalance(ctx.RequestContext(), userID) + // 转换为积分(浮点)用于显示 + balancePoints := h.user.CentsToPointsFloat(ctx.RequestContext(), balance) res := userItem{ - ID: user.ID, - Nickname: user.Nickname, - Avatar: user.Avatar, - InviteCode: user.InviteCode, - InviterID: user.InviterID, - Mobile: phone, - Balance: balance, + ID: user.ID, + Nickname: user.Nickname, + Avatar: user.Avatar, + InviteCode: user.InviteCode, + InviterID: user.InviterID, + Mobile: phone, + DouyinUserID: user.DouyinUserID, + Balance: balancePoints, } ctx.Payload(res) } @@ -54,13 +57,14 @@ type modifyUserRequest struct { Avatar *string `json:"avatar"` } type userItem struct { - ID int64 `json:"id"` - Nickname string `json:"nickname"` - Avatar string `json:"avatar"` - InviteCode string `json:"invite_code"` - InviterID int64 `json:"inviter_id"` - Mobile string `json:"mobile"` - Balance int64 `json:"balance"` // Points + ID int64 `json:"id"` + Nickname string `json:"nickname"` + Avatar string `json:"avatar"` + InviteCode string `json:"invite_code"` + InviterID int64 `json:"inviter_id"` + Mobile string `json:"mobile"` + DouyinUserID string `json:"douyin_user_id"` + Balance float64 `json:"balance"` // 积分(分/rate) } type modifyUserResponse struct { User userItem `json:"user"` @@ -101,15 +105,17 @@ func (h *handler) ModifyUser() core.HandlerFunc { } balance, _ := h.user.GetPointsBalance(ctx.RequestContext(), userID) + balancePoints := h.user.CentsToPointsFloat(ctx.RequestContext(), balance) rsp.User = userItem{ - ID: item.ID, - Nickname: item.Nickname, - Avatar: item.Avatar, - InviteCode: item.InviteCode, - InviterID: item.InviterID, - Mobile: maskedPhone, - Balance: balance, + ID: item.ID, + Nickname: item.Nickname, + Avatar: item.Avatar, + InviteCode: item.InviteCode, + InviterID: item.InviterID, + Mobile: maskedPhone, + DouyinUserID: item.DouyinUserID, + Balance: balancePoints, } ctx.Payload(rsp) } diff --git a/internal/pkg/core/core.go b/internal/pkg/core/core.go index 64b73c6..f689ec7 100644 --- a/internal/pkg/core/core.go +++ b/internal/pkg/core/core.go @@ -200,6 +200,7 @@ type Mux interface { ServeHTTP(w http.ResponseWriter, req *http.Request) Group(relativePath string, handlers ...HandlerFunc) RouterGroup Routes() gin.RoutesInfo + Engine() *gin.Engine } type mux struct { @@ -220,6 +221,10 @@ func (m *mux) Routes() gin.RoutesInfo { return m.engine.Routes() } +func (m *mux) Engine() *gin.Engine { + return m.engine +} + func New(logger logger.CustomLogger, options ...Option) (Mux, error) { if logger == nil { return nil, errors.New("logger required") diff --git a/internal/pkg/env/env.go b/internal/pkg/env/env.go index 0a6dcfb..3868323 100644 --- a/internal/pkg/env/env.go +++ b/internal/pkg/env/env.go @@ -3,6 +3,7 @@ package env import ( "flag" "fmt" + "os" "strings" "sync" ) @@ -65,6 +66,10 @@ func setup() { val = *envFlag } + if val == "" { + val = os.Getenv("ACTIVE_ENV") + } + switch strings.ToLower(strings.TrimSpace(val)) { case "dev": active = dev diff --git a/internal/pkg/otel/middleware.go b/internal/pkg/otel/middleware.go new file mode 100644 index 0000000..728dc41 --- /dev/null +++ b/internal/pkg/otel/middleware.go @@ -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())) + } + } +} diff --git a/internal/pkg/otel/otel.go b/internal/pkg/otel/otel.go new file mode 100644 index 0000000..8a56454 --- /dev/null +++ b/internal/pkg/otel/otel.go @@ -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) +} diff --git a/internal/pkg/points/convert.go b/internal/pkg/points/convert.go index f08d860..63cc823 100644 --- a/internal/pkg/points/convert.go +++ b/internal/pkg/points/convert.go @@ -1,19 +1,25 @@ package points -func CentsToPoints(cents int64, rate int64) int64 { - if cents <= 0 || rate <= 0 { return 0 } - return cents * rate +import "math" + +// 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 { - if points <= 0 || rate <= 0 { return 0 } - return points / rate +// PointsToCents converts points to monetary value (in cents). +// If Rate = 1 (1 Point = 1 Yuan), then 1 Point = 100 Cents. +// 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 -} - diff --git a/internal/pkg/points/convert_test.go b/internal/pkg/points/convert_test.go index 2cb2500..e8761ab 100644 --- a/internal/pkg/points/convert_test.go +++ b/internal/pkg/points/convert_test.go @@ -3,20 +3,22 @@ package points import "testing" func TestCentsToPoints_DefaultRate(t *testing.T) { - if got := CentsToPoints(12345, 1); got != 12345 { - t.Fatalf("expected 12345, got %d", got) - } + if got := CentsToPoints(100, 1); got != 1 { + t.Fatalf("expected 1, got %d", got) + } } func TestPointsToCents_DefaultRate(t *testing.T) { - if got := PointsToCents(100, 1); got != 100 { - t.Fatalf("expected 100, got %d", got) - } + if got := PointsToCents(1, 1); got != 100 { + t.Fatalf("expected 100, got %d", got) + } } func TestRefundPointsAmount(t *testing.T) { - pts := RefundPointsAmount(5000, 2500, 10000, 1) - if pts != 12 { - t.Fatalf("expected 12, got %d", pts) - } + // 100 Points used. Refund 25 Yuan out of 100 Yuan paid. + // Expect 25 Points back. + pts := RefundPointsAmount(100, 2500, 10000, 1) + if pts != 25 { + t.Fatalf("expected 25, got %d", pts) + } } diff --git a/internal/router/router.go b/internal/router/router.go index 780515d..e4b8b23 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -14,6 +14,7 @@ import ( "bindbox-game/internal/dblogger" "bindbox-game/internal/pkg/core" "bindbox-game/internal/pkg/logger" + "bindbox-game/internal/pkg/otel" "bindbox-game/internal/pkg/redis" "bindbox-game/internal/repository/mysql" "bindbox-game/internal/router/interceptor" @@ -47,6 +48,12 @@ func NewHTTPMux(logger logger.CustomLogger, db mysql.Repo) (core.Mux, func(), er panic(err) } + // 添加 OpenTelemetry 链路追踪中间件 + cfg := configs.Get() + if cfg.Otel.Enabled { + mux.Engine().Use(otel.Middleware(configs.ProjectName)) + } + // Redis is initialized in main.go 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/activity_prize_analysis", adminHandler.DashboardActivityPrizeAnalysis()) 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()) @@ -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.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_details", intc.RequireAdminAction("user:view"), adminHandler.GetUserProfitLossDetails()) 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()) @@ -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("/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.GET("/config/public", commonHandler.GetPublicConfig()) } // APP 端认证接口路由组 diff --git a/internal/service/activity/scheduler.go b/internal/service/activity/scheduler.go index dd23869..19d6021 100644 --- a/internal/service/activity/scheduler.go +++ b/internal/service/activity/scheduler.go @@ -46,6 +46,9 @@ func StartScheduledSettlement(l logger.CustomLogger, repo mysql.Repo, rdb *redis // 【独立检查】一番赏格位重置:每30秒检查所有售罄的一番赏期号 checkAndResetIchibanSlots(ctx, l, repo, r) + + // 【安全优化】清理超过30分钟未支付的一番赏订单,释放预锁 + cleanupExpiredIchibanOrders(ctx, l, repo, rdb) var acts []struct { ID int64 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 { if s == "" { return time.Time{} diff --git a/internal/service/task_center/service.go b/internal/service/task_center/service.go index d8ed8b5..67f5cb5 100644 --- a/internal/service/task_center/service.go +++ b/internal/service/task_center/service.go @@ -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) { 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. 实时统计订单数据 var orderCount int64 var orderAmount int64 - db.Model(&model.Orders{}).Where("user_id = ? AND status = 2", userID).Count(&orderCount) - db.Model(&model.Orders{}).Where("user_id = ? AND status = 2", userID).Select("COALESCE(SUM(actual_amount), 0)").Scan(&orderAmount) + query := db.Model(&model.Orders{}).Where("user_id = ? AND status = 2", userID) + 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. 实时统计邀请数据(有效邀请:被邀请人有消费记录) + // 注意:邀请统计是否也要过滤 ActivityID? + // 需求是“消费的是对对碰”,通常指邀请带来的“有效用户”需要在该活动消费。 + // 之前的逻辑是:INNER JOIN orders,只要有任意消费就算有效。 + // 如果任务是“对对碰”任务,那么被邀请人应该在“对对碰”消费才算有效邀请吗? + // 暂时保持原样,或者也加上过滤。根据常理,特定活动拉新通常要求在该活动消费。 + // 这里加上过滤更安全。 + var inviteCount int64 - db.Raw(` + 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 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. 首单判断 hasFirstOrder := orderCount > 0 diff --git a/internal/service/task_center/task_center_test.go b/internal/service/task_center/task_center_test.go index ceda825..79bf59d 100644 --- a/internal/service/task_center/task_center_test.go +++ b/internal/service/task_center/task_center_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "bindbox-game/internal/repository/mysql" tcmodel "bindbox-game/internal/repository/mysql/task_center" "gorm.io/datatypes" @@ -72,6 +73,13 @@ func CreateTestDB(t *testing.T) *gorm.DB { 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 ( id INTEGER PRIMARY KEY AUTOINCREMENT, @@ -150,8 +158,6 @@ func CreateTestDB(t *testing.T) *gorm.DB { );`).Error; err != nil { t.Fatalf("创建 task_center_event_logs 表失败: %v", err) } - - return db } // InsertTaskWithTierAndReward 插入一个完整的任务配置(任务+档位+奖励) @@ -334,7 +340,13 @@ func TestOrderCountMetric(t *testing.T) { ClaimedTiers: datatypes.JSON("[]"), } // 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 @@ -629,3 +641,86 @@ func TestClaimedTiersTracking(t *testing.T) { 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) +} diff --git a/internal/service/user/item_card_redeem.go b/internal/service/user/item_card_redeem.go index 1aed7f2..4b9a962 100644 --- a/internal/service/user/item_card_redeem.go +++ b/internal/service/user/item_card_redeem.go @@ -20,15 +20,14 @@ func (s *service) RedeemItemCard(ctx context.Context, userID int64, cardID int64 return 0, errors.New("item card not found") } - // 2. 算分 - ptsPerUnit, _ := s.CentsToPoints(ctx, tpl.Price) - needPoints := ptsPerUnit * int64(quantity) - if needPoints <= 0 { - needPoints = 1 + // 2. 算分 - tpl.Price 是道具卡价格(分),直接用于扣除 + needCents := tpl.Price * int64(quantity) + if needCents <= 0 { + needCents = 1 } // 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 { return 0, err } @@ -42,7 +41,7 @@ func (s *service) RedeemItemCard(ctx context.Context, userID int64, cardID int64 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()) } diff --git a/internal/service/user/orders_action.go b/internal/service/user/orders_action.go index 8f48ca8..2c89a68 100644 --- a/internal/service/user/orders_action.go +++ b/internal/service/user/orders_action.go @@ -36,12 +36,8 @@ func (s *service) CancelOrder(ctx context.Context, userID int64, orderID int64, refundReason = refundReason + ":" + reason } - // Get Rate - ratePtsPerCent, _ := s.CentsToPoints(ctx, 1) - if ratePtsPerCent <= 0 { - ratePtsPerCent = 1 - } - pointsToRefund := order.PointsAmount * ratePtsPerCent + // order.PointsAmount 已经是分单位,直接退还 + pointsToRefund := order.PointsAmount // Update User Points existing, _ := tx.UserPoints.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).Where(tx.UserPoints.UserID.Eq(userID)).First() diff --git a/internal/service/user/points_convert.go b/internal/service/user/points_convert.go index 728f4da..bb43d2b 100644 --- a/internal/service/user/points_convert.go +++ b/internal/service/user/points_convert.go @@ -5,12 +5,15 @@ import ( "fmt" ) -func (s *service) CentsToPoints(ctx context.Context, cents int64) (int64, error) { - if cents <= 0 { - return 0, nil - } - cfg, _ := s.readDB.SystemConfigs.WithContext(ctx).Where(s.readDB.SystemConfigs.ConfigKey.Eq("points_exchange_per_cent")).First() - rate := int64(1) +// getExchangeRate 获取积分兑换比率 +// 配置含义:1 元 = X 积分(后台配置的值) +// 计算:分 / (100 / rate) = 分 * rate / 100 +// 当 rate=1 时:1元=1积分,100分=1积分 +// 当 rate=2 时:1元=2积分,50分=1积分 +func (s *service) getExchangeRate(ctx context.Context) int64 { + // 读取后台配置的 key:points.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 { var r int64 _, _ = fmt.Sscanf(cfg.ConfigValue, "%d", &r) @@ -18,5 +21,39 @@ func (s *service) CentsToPoints(ctx context.Context, cents int64) (int64, error) 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 } diff --git a/internal/service/user/user.go b/internal/service/user/user.go index 206d317..654041c 100644 --- a/internal/service/user/user.go +++ b/internal/service/user/user.go @@ -71,6 +71,8 @@ type Service interface { GetOrderWithItems(ctx context.Context, userID, orderID int64) (*OrderWithItems, error) CancelOrder(ctx context.Context, userID, orderID int64, reason string) (*model.Orders, 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) ConsumePointsForRefund(ctx context.Context, userID int64, points int64, refTable string, refID string, remark string) (int64, int64, error) // 短信登录 diff --git a/main.go b/main.go index 4d11963..bf3a8e7 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "bindbox-game/configs" "bindbox-game/internal/pkg/env" "bindbox-game/internal/pkg/logger" + "bindbox-game/internal/pkg/otel" "bindbox-game/internal/pkg/redis" "bindbox-game/internal/pkg/shutdown" "bindbox-game/internal/pkg/timeutil" @@ -35,163 +36,30 @@ import ( // @BasePath / func main() { 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 dbRepo, err := mysql.New() if err != nil { 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 customLogger, err := logger.NewCustomLogger(dao.Use(dbRepo.GetDbW()), - logger.WithDebugLevel(), // 启用调试级别日志 - // logger.WithOutputInConsole(), // 启用控制台输出 + logger.WithDebugLevel(), // 启用调试级别日志 + logger.WithOutputInConsole(), // 启用控制台输出 logger.WithField("domain", fmt.Sprintf("%s[%s]", configs.ProjectName, env.Active().Value())), logger.WithTimeLayout(timeutil.CSTLayout), logger.WithFileRotationP(configs.ProjectAccessLogFile), @@ -248,6 +116,11 @@ func main() { cleanup() } + // 关闭 OpenTelemetry + if otelShutdown != nil { + _ = otelShutdown(context.Background()) + } + // 关闭 http server if err := server.Shutdown(context.TODO()); err != nil { customLogger.Error("server shutdown err", zap.Error(err)) diff --git a/migration_add_template_id.sql b/migration_add_template_id.sql new file mode 100644 index 0000000..8ae95bc --- /dev/null +++ b/migration_add_template_id.sql @@ -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'; diff --git a/response.json b/response.json new file mode 100644 index 0000000..89ce6cf --- /dev/null +++ b/response.json @@ -0,0 +1 @@ +{"page":1,"page_size":10,"total":13,"list":[{"id":29317,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":0,"profit":-600,"created_at":"2026-01-07T21:34:46.893+08:00"},{"id":29306,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":995,"profit":395,"created_at":"2026-01-07T18:29:16.749+08:00"},{"id":29304,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":995,"profit":395,"created_at":"2026-01-07T18:24:41.573+08:00"},{"id":29303,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":47,"product_name":"支架","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763456658131436800.png","product_price":800,"order_amount":0,"profit":-800,"created_at":"2026-01-07T18:23:42.066+08:00"},{"id":29291,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":44,"product_name":"SD随机款","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763399757943486800.png","product_price":4000,"order_amount":0,"profit":-4000,"created_at":"2026-01-07T09:18:11.808+08:00"},{"id":29290,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":1990,"profit":1390,"created_at":"2026-01-07T09:15:49.616+08:00"},{"id":29289,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":47,"product_name":"支架","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763456658131436800.png","product_price":800,"order_amount":0,"profit":-800,"created_at":"2026-01-06T20:38:09.348+08:00"},{"id":29288,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":47,"product_name":"支架","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763456658131436800.png","product_price":800,"order_amount":1990,"profit":1190,"created_at":"2026-01-06T20:36:35.439+08:00"},{"id":29287,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":1990,"profit":1390,"created_at":"2026-01-06T20:34:10.129+08:00"},{"id":29286,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":47,"product_name":"支架","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763456658131436800.png","product_price":800,"order_amount":1990,"profit":1190,"created_at":"2026-01-06T20:32:00.823+08:00"}]} \ No newline at end of file diff --git a/response_new.json b/response_new.json new file mode 100644 index 0000000..c851cc5 --- /dev/null +++ b/response_new.json @@ -0,0 +1 @@ +{"page":1,"page_size":10,"total":13,"list":[{"id":29317,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":0,"discount_amount":1990,"pay_type":"现金支付","used_card":"","profit":-600,"created_at":"2026-01-07T21:34:46.893+08:00"},{"id":29306,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":995,"discount_amount":995,"pay_type":"现金支付","used_card":"","profit":395,"created_at":"2026-01-07T18:29:16.749+08:00"},{"id":29304,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":995,"discount_amount":995,"pay_type":"现金支付","used_card":"","profit":395,"created_at":"2026-01-07T18:24:41.573+08:00"},{"id":29303,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":47,"product_name":"支架","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763456658131436800.png","product_price":800,"order_amount":0,"discount_amount":1990,"pay_type":"现金支付","used_card":"","profit":-800,"created_at":"2026-01-07T18:23:42.066+08:00"},{"id":29291,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":44,"product_name":"SD随机款","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763399757943486800.png","product_price":4000,"order_amount":0,"discount_amount":1990,"pay_type":"现金支付","used_card":"","profit":-4000,"created_at":"2026-01-07T09:18:11.808+08:00"},{"id":29290,"user_id":9019,"nickname":"约翰掐指一算","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAABmAADpPQ5dAAAAAXRSTlMAQObYZgAAAD9JREFUeJxiAIH//xHkSBdAEofwRrjAKEAFsMQCTzQjXgCRSMDsES4AoWAALj6iBRABNCqAnHlGBRgAAQAA//8HSv0f1XCh/AAAAABJRU5ErkJggg==","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":1990,"discount_amount":0,"pay_type":"现金支付","used_card":"","profit":1390,"created_at":"2026-01-07T09:15:49.616+08:00"},{"id":29289,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":47,"product_name":"支架","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763456658131436800.png","product_price":800,"order_amount":0,"discount_amount":1990,"pay_type":"现金支付","used_card":"","profit":-800,"created_at":"2026-01-06T20:38:09.348+08:00"},{"id":29288,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":47,"product_name":"支架","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763456658131436800.png","product_price":800,"order_amount":1990,"discount_amount":0,"pay_type":"现金支付","used_card":"","profit":1190,"created_at":"2026-01-06T20:36:35.439+08:00"},{"id":29287,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":297,"product_name":"木质拼装模型积木蝴蝶折叠爪刀","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/12/29/1767016445618491389.jpg","product_price":600,"order_amount":1990,"discount_amount":0,"pay_type":"现金支付","used_card":"","profit":1390,"created_at":"2026-01-06T20:34:10.129+08:00"},{"id":29286,"user_id":9017,"nickname":"托尼","avatar":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAQMAAAD58POIAAAABlBMVEUAAADM/2YKJ+/0AAAAAXRSTlMAQObYZgAAAD1JREFUeJxiYGD4jwQYGEa6AFwcxh4VQAGjAqAAQiJHuMBgybeDR4ABrRQZ2QL/0cCowOAp1weBACAAAP//Rbt6zOkuCaIAAAAASUVORK5CYII=","product_id":47,"product_name":"支架","product_image":"https://keaiya-1259195914.cos.ap-shanghai.myqcloud.com/images/2025/11/18/1763456658131436800.png","product_price":800,"order_amount":1990,"discount_amount":0,"pay_type":"现金支付","used_card":"","profit":1190,"created_at":"2026-01-06T20:32:00.823+08:00"}]} \ No newline at end of file diff --git a/scripts/check_points_integrity.go b/scripts/check_points_integrity.go new file mode 100644 index 0000000..d1aaa29 --- /dev/null +++ b/scripts/check_points_integrity.go @@ -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) + } +}