diff --git a/.DS_Store b/.DS_Store index 8eb634c..257f8cb 100755 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.agents/README.md b/.agents/README.md new file mode 100644 index 0000000..551f06d --- /dev/null +++ b/.agents/README.md @@ -0,0 +1,38 @@ +# .agents Directory + +This directory contains agent configuration and skills for OpenAI Codex CLI. + +## Structure + +``` +.agents/ + config.toml # Main configuration file + skills/ # Skill definitions + skill-name/ + SKILL.md # Skill instructions + scripts/ # Optional scripts + docs/ # Optional documentation + README.md # This file +``` + +## Configuration + +The `config.toml` file controls: +- Model selection +- Approval policies +- Sandbox modes +- MCP server connections +- Skills configuration + +## Skills + +Skills are invoked using `$skill-name` syntax. Each skill has: +- YAML frontmatter with metadata +- Trigger and skip conditions +- Commands and examples + +## Documentation + +- Main instructions: `AGENTS.md` (project root) +- Local overrides: `.codex/AGENTS.override.md` (gitignored) +- Claude Flow: https://github.com/ruvnet/claude-flow diff --git a/.agents/config.toml b/.agents/config.toml new file mode 100644 index 0000000..9110b8e --- /dev/null +++ b/.agents/config.toml @@ -0,0 +1,298 @@ +# ============================================================================= +# Claude Flow V3 - Codex Configuration +# ============================================================================= +# Generated by: @claude-flow/codex +# Documentation: https://github.com/ruvnet/claude-flow +# +# This file configures the Codex CLI for Claude Flow integration. +# Place in .agents/config.toml (project) or .codex/config.toml (user). +# ============================================================================= + +# ============================================================================= +# Core Settings +# ============================================================================= + +# Model selection - the AI model to use for code generation +# Options: gpt-5.3-codex, gpt-4o, claude-sonnet, claude-opus +model = "gpt-5.3-codex" + +# Approval policy determines when human approval is required +# - untrusted: Always require approval +# - on-failure: Require approval only after failures +# - on-request: Require approval for significant changes +# - never: Auto-approve all actions (use with caution) +approval_policy = "on-request" + +# Sandbox mode controls file system access +# - read-only: Can only read files, no modifications +# - workspace-write: Can write within workspace directory +# - danger-full-access: Full file system access (dangerous) +sandbox_mode = "workspace-write" + +# Web search enables internet access for research +# - disabled: No web access +# - cached: Use cached results when available +# - live: Always fetch fresh results +web_search = "cached" + +# ============================================================================= +# Project Documentation +# ============================================================================= + +# Maximum bytes to read from AGENTS.md files +project_doc_max_bytes = 65536 + +# Fallback filenames if AGENTS.md not found +project_doc_fallback_filenames = [ + "AGENTS.md", + "TEAM_GUIDE.md", + ".agents.md" +] + +# ============================================================================= +# Features +# ============================================================================= + +[features] +# Enable child AGENTS.md guidance +child_agents_md = true + +# Cache shell environment for faster repeated commands +shell_snapshot = true + +# Smart approvals based on request context +request_rule = true + +# Enable remote compaction for large histories +remote_compaction = true + +# ============================================================================= +# MCP Servers +# ============================================================================= + +[mcp_servers.claude-flow] +command = "npx" +args = ["-y", "@claude-flow/cli@latest"] +enabled = true +tool_timeout_sec = 120 + +# ============================================================================= +# Skills Configuration +# ============================================================================= + +[[skills.config]] +path = ".agents/skills/swarm-orchestration" +enabled = true + +[[skills.config]] +path = ".agents/skills/memory-management" +enabled = true + +[[skills.config]] +path = ".agents/skills/sparc-methodology" +enabled = true + +[[skills.config]] +path = ".agents/skills/security-audit" +enabled = true + +# ============================================================================= +# Profiles +# ============================================================================= + +# Development profile - more permissive for local work +[profiles.dev] +approval_policy = "never" +sandbox_mode = "danger-full-access" +web_search = "live" + +# Safe profile - maximum restrictions +[profiles.safe] +approval_policy = "untrusted" +sandbox_mode = "read-only" +web_search = "disabled" + +# CI profile - for automated pipelines +[profiles.ci] +approval_policy = "never" +sandbox_mode = "workspace-write" +web_search = "cached" + +# ============================================================================= +# History +# ============================================================================= + +[history] +# Save all session transcripts +persistence = "save-all" + +# ============================================================================= +# Shell Environment +# ============================================================================= + +[shell_environment_policy] +# Inherit environment variables +inherit = "core" + +# Exclude sensitive variables +exclude = ["*_KEY", "*_SECRET", "*_TOKEN", "*_PASSWORD"] + +# ============================================================================= +# Sandbox Workspace Write Settings +# ============================================================================= + +[sandbox_workspace_write] +# Additional writable paths beyond workspace +writable_roots = [] + +# Allow network access +network_access = true + +# Exclude temp directories +exclude_slash_tmp = false + +# ============================================================================= +# Security Settings +# ============================================================================= + +[security] +# Enable input validation for all user inputs +input_validation = true + +# Prevent directory traversal attacks +path_traversal_prevention = true + +# Scan for hardcoded secrets +secret_scanning = true + +# Scan dependencies for known CVEs +cve_scanning = true + +# Maximum file size for operations (bytes) +max_file_size = 10485760 + +# Allowed file extensions (empty = allow all) +allowed_extensions = [] + +# Blocked file patterns (regex) +blocked_patterns = ["\\.env$", "credentials\\.json$", "\\.pem$", "\\.key$"] + +# ============================================================================= +# Performance Settings +# ============================================================================= + +[performance] +# Maximum concurrent agents +max_agents = 8 + +# Task timeout in seconds +task_timeout = 300 + +# Memory limit per agent +memory_limit = "512MB" + +# Enable response caching +cache_enabled = true + +# Cache TTL in seconds +cache_ttl = 3600 + +# Enable parallel task execution +parallel_execution = true + +# ============================================================================= +# Logging Settings +# ============================================================================= + +[logging] +# Log level: debug, info, warn, error +level = "info" + +# Log format: json, text, pretty +format = "pretty" + +# Log destination: stdout, file, both +destination = "stdout" + +# ============================================================================= +# Neural Intelligence Settings +# ============================================================================= + +[neural] +# Enable SONA (Self-Optimizing Neural Architecture) +sona_enabled = true + +# Enable HNSW vector search +hnsw_enabled = true + +# HNSW index parameters +hnsw_m = 16 +hnsw_ef_construction = 200 +hnsw_ef_search = 100 + +# Enable pattern learning +pattern_learning = true + +# Learning rate for neural adaptation +learning_rate = 0.01 + +# ============================================================================= +# Swarm Orchestration Settings +# ============================================================================= + +[swarm] +# Default topology: hierarchical, mesh, ring, star +default_topology = "hierarchical" + +# Default strategy: balanced, specialized, adaptive +default_strategy = "specialized" + +# Consensus algorithm: raft, byzantine, gossip +consensus = "raft" + +# Enable anti-drift measures +anti_drift = true + +# Checkpoint interval (tasks) +checkpoint_interval = 10 + +# ============================================================================= +# Hooks Configuration +# ============================================================================= + +[hooks] +# Enable lifecycle hooks +enabled = true + +# Pre-task hook +pre_task = true + +# Post-task hook (for learning) +post_task = true + +# Enable neural training on post-edit +train_on_edit = true + +# ============================================================================= +# Background Workers +# ============================================================================= + +[workers] +# Enable background workers +enabled = true + +# Worker configuration +[workers.audit] +enabled = true +priority = "critical" +interval = 300 + +[workers.optimize] +enabled = true +priority = "high" +interval = 600 + +[workers.consolidate] +enabled = true +priority = "low" +interval = 1800 diff --git a/.agents/skills/memory-management/SKILL.md b/.agents/skills/memory-management/SKILL.md new file mode 100644 index 0000000..c337ff9 --- /dev/null +++ b/.agents/skills/memory-management/SKILL.md @@ -0,0 +1,126 @@ +--- +name: memory-management +description: > + AgentDB memory system with HNSW vector search. Provides 150x-12,500x faster pattern retrieval, persistent storage, and semantic search capabilities for learning and knowledge management. + Use when: need to store successful patterns, searching for similar solutions, semantic lookup of past work, learning from previous tasks, sharing knowledge between agents, building knowledge base. + Skip when: no learning needed, ephemeral one-off tasks, external data sources available, read-only exploration. +--- + +# Memory Management Skill + +## Purpose +AgentDB memory system with HNSW vector search. Provides 150x-12,500x faster pattern retrieval, persistent storage, and semantic search capabilities for learning and knowledge management. + +## When to Trigger +- need to store successful patterns +- searching for similar solutions +- semantic lookup of past work +- learning from previous tasks +- sharing knowledge between agents +- building knowledge base + +## When to Skip +- no learning needed +- ephemeral one-off tasks +- external data sources available +- read-only exploration + +## Commands + +### Store Pattern +Store a pattern or knowledge item in memory + +```bash +npx @claude-flow/cli memory store --key "[key]" --value "[value]" --namespace patterns +``` + +**Example:** +```bash +npx @claude-flow/cli memory store --key "auth-jwt-pattern" --value "JWT validation with refresh tokens" --namespace patterns +``` + +### Semantic Search +Search memory using semantic similarity + +```bash +npx @claude-flow/cli memory search --query "[search terms]" --limit 10 +``` + +**Example:** +```bash +npx @claude-flow/cli memory search --query "authentication best practices" --limit 5 +``` + +### Retrieve Entry +Retrieve a specific memory entry by key + +```bash +npx @claude-flow/cli memory get --key "[key]" --namespace [namespace] +``` + +**Example:** +```bash +npx @claude-flow/cli memory get --key "auth-jwt-pattern" --namespace patterns +``` + +### List Entries +List all entries in a namespace + +```bash +npx @claude-flow/cli memory list --namespace [namespace] +``` + +**Example:** +```bash +npx @claude-flow/cli memory list --namespace patterns --limit 20 +``` + +### Delete Entry +Delete a memory entry + +```bash +npx @claude-flow/cli memory delete --key "[key]" --namespace [namespace] +``` + +### Initialize HNSW Index +Initialize HNSW vector search index + +```bash +npx @claude-flow/cli memory init --enable-hnsw +``` + +### Memory Stats +Show memory usage statistics + +```bash +npx @claude-flow/cli memory stats +``` + +### Export Memory +Export memory to JSON + +```bash +npx @claude-flow/cli memory export --output memory-backup.json +``` + + +## Scripts + +| Script | Path | Description | +|--------|------|-------------| +| `memory-backup` | `.agents/scripts/memory-backup.sh` | Backup memory to external storage | +| `memory-consolidate` | `.agents/scripts/memory-consolidate.sh` | Consolidate and optimize memory | + + +## References + +| Document | Path | Description | +|----------|------|-------------| +| `HNSW Guide` | `docs/hnsw.md` | HNSW vector search configuration | +| `Memory Schema` | `docs/memory-schema.md` | Memory namespace and schema reference | + +## Best Practices +1. Check memory for existing patterns before starting +2. Use hierarchical topology for coordination +3. Store successful patterns after completion +4. Document any new learnings diff --git a/.agents/skills/memory-management/scripts/memory-backup.sh b/.agents/skills/memory-management/scripts/memory-backup.sh new file mode 100644 index 0000000..de09ebc --- /dev/null +++ b/.agents/skills/memory-management/scripts/memory-backup.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Memory Management - Backup Script +# Export memory to backup file + +set -e + +BACKUP_DIR="${BACKUP_DIR:-./.backups}" +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +BACKUP_FILE="${BACKUP_DIR}/memory_${TIMESTAMP}.json" + +mkdir -p "$BACKUP_DIR" + +echo "Backing up memory to $BACKUP_FILE..." +npx @claude-flow/cli memory export --output "$BACKUP_FILE" + +echo "Backup complete: $BACKUP_FILE" diff --git a/.agents/skills/memory-management/scripts/memory-consolidate.sh b/.agents/skills/memory-management/scripts/memory-consolidate.sh new file mode 100644 index 0000000..c0b76d8 --- /dev/null +++ b/.agents/skills/memory-management/scripts/memory-consolidate.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Memory Management - Consolidate Script +# Optimize and consolidate memory + +set -e + +echo "Running memory consolidation..." +npx @claude-flow/cli hooks worker dispatch --trigger consolidate + +echo "Memory consolidation complete" +npx @claude-flow/cli memory stats diff --git a/.agents/skills/security-audit/SKILL.md b/.agents/skills/security-audit/SKILL.md new file mode 100644 index 0000000..83f7d64 --- /dev/null +++ b/.agents/skills/security-audit/SKILL.md @@ -0,0 +1,135 @@ +--- +name: security-audit +description: > + Comprehensive security scanning and vulnerability detection. Includes input validation, path traversal prevention, CVE detection, and secure coding pattern enforcement. + Use when: authentication implementation, authorization logic, payment processing, user data handling, API endpoint creation, file upload handling, database queries, external API integration. + Skip when: read-only operations on public data, internal development tooling, static documentation, styling changes. +--- + +# Security Audit Skill + +## Purpose +Comprehensive security scanning and vulnerability detection. Includes input validation, path traversal prevention, CVE detection, and secure coding pattern enforcement. + +## When to Trigger +- authentication implementation +- authorization logic +- payment processing +- user data handling +- API endpoint creation +- file upload handling +- database queries +- external API integration + +## When to Skip +- read-only operations on public data +- internal development tooling +- static documentation +- styling changes + +## Commands + +### Full Security Scan +Run comprehensive security analysis on the codebase + +```bash +npx @claude-flow/cli security scan --depth full +``` + +**Example:** +```bash +npx @claude-flow/cli security scan --depth full --output security-report.json +``` + +### Input Validation Check +Check for input validation issues + +```bash +npx @claude-flow/cli security scan --check input-validation +``` + +**Example:** +```bash +npx @claude-flow/cli security scan --check input-validation --path ./src/api +``` + +### Path Traversal Check +Check for path traversal vulnerabilities + +```bash +npx @claude-flow/cli security scan --check path-traversal +``` + +### SQL Injection Check +Check for SQL injection vulnerabilities + +```bash +npx @claude-flow/cli security scan --check sql-injection +``` + +### XSS Check +Check for cross-site scripting vulnerabilities + +```bash +npx @claude-flow/cli security scan --check xss +``` + +### CVE Scan +Scan dependencies for known CVEs + +```bash +npx @claude-flow/cli security cve --scan +``` + +**Example:** +```bash +npx @claude-flow/cli security cve --scan --severity high +``` + +### Security Audit Report +Generate full security audit report + +```bash +npx @claude-flow/cli security audit --report +``` + +**Example:** +```bash +npx @claude-flow/cli security audit --report --format markdown --output SECURITY.md +``` + +### Threat Modeling +Run threat modeling analysis + +```bash +npx @claude-flow/cli security threats --analyze +``` + +### Validate Secrets +Check for hardcoded secrets + +```bash +npx @claude-flow/cli security validate --check secrets +``` + + +## Scripts + +| Script | Path | Description | +|--------|------|-------------| +| `security-scan` | `.agents/scripts/security-scan.sh` | Run full security scan pipeline | +| `cve-remediate` | `.agents/scripts/cve-remediate.sh` | Auto-remediate known CVEs | + + +## References + +| Document | Path | Description | +|----------|------|-------------| +| `Security Checklist` | `docs/security-checklist.md` | Security review checklist | +| `OWASP Guide` | `docs/owasp-top10.md` | OWASP Top 10 mitigation guide | + +## Best Practices +1. Check memory for existing patterns before starting +2. Use hierarchical topology for coordination +3. Store successful patterns after completion +4. Document any new learnings diff --git a/.agents/skills/security-audit/scripts/cve-remediate.sh b/.agents/skills/security-audit/scripts/cve-remediate.sh new file mode 100644 index 0000000..60b8dea --- /dev/null +++ b/.agents/skills/security-audit/scripts/cve-remediate.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Security Audit - CVE Remediation Script +# Auto-remediate known CVEs + +set -e + +echo "Scanning for CVEs..." +npx @claude-flow/cli security cve --scan --severity high + +echo "Attempting auto-remediation..." +npm audit fix + +echo "Re-scanning after remediation..." +npx @claude-flow/cli security cve --scan + +echo "CVE remediation complete" diff --git a/.agents/skills/security-audit/scripts/security-scan.sh b/.agents/skills/security-audit/scripts/security-scan.sh new file mode 100644 index 0000000..2601db1 --- /dev/null +++ b/.agents/skills/security-audit/scripts/security-scan.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Security Audit - Full Scan Script +# Run comprehensive security scan pipeline + +set -e + +echo "Running full security scan..." + +# Input validation +echo "Checking input validation..." +npx @claude-flow/cli security scan --check input-validation + +# Path traversal +echo "Checking path traversal..." +npx @claude-flow/cli security scan --check path-traversal + +# SQL injection +echo "Checking SQL injection..." +npx @claude-flow/cli security scan --check sql-injection + +# XSS +echo "Checking XSS..." +npx @claude-flow/cli security scan --check xss + +# Secrets +echo "Checking for hardcoded secrets..." +npx @claude-flow/cli security validate --check secrets + +# CVE scan +echo "Scanning dependencies for CVEs..." +npx @claude-flow/cli security cve --scan + +echo "Security scan complete" diff --git a/.agents/skills/sparc-methodology/SKILL.md b/.agents/skills/sparc-methodology/SKILL.md new file mode 100644 index 0000000..d48e6ef --- /dev/null +++ b/.agents/skills/sparc-methodology/SKILL.md @@ -0,0 +1,118 @@ +--- +name: sparc-methodology +description: > + SPARC development workflow: Specification, Pseudocode, Architecture, Refinement, Completion. A structured approach for complex implementations that ensures thorough planning before coding. + Use when: new feature implementation, complex implementations, architectural changes, system redesign, integration work, unclear requirements. + Skip when: simple bug fixes, documentation updates, configuration changes, well-defined small tasks, routine maintenance. +--- + +# Sparc Methodology Skill + +## Purpose +SPARC development workflow: Specification, Pseudocode, Architecture, Refinement, Completion. A structured approach for complex implementations that ensures thorough planning before coding. + +## When to Trigger +- new feature implementation +- complex implementations +- architectural changes +- system redesign +- integration work +- unclear requirements + +## When to Skip +- simple bug fixes +- documentation updates +- configuration changes +- well-defined small tasks +- routine maintenance + +## Commands + +### Specification Phase +Define requirements, acceptance criteria, and constraints + +```bash +npx @claude-flow/cli hooks route --task "specification: [requirements]" +``` + +**Example:** +```bash +npx @claude-flow/cli hooks route --task "specification: user authentication with OAuth2, MFA, and session management" +``` + +### Pseudocode Phase +Write high-level pseudocode for the implementation + +```bash +npx @claude-flow/cli hooks route --task "pseudocode: [feature]" +``` + +**Example:** +```bash +npx @claude-flow/cli hooks route --task "pseudocode: OAuth2 login flow with token refresh" +``` + +### Architecture Phase +Design system structure, interfaces, and dependencies + +```bash +npx @claude-flow/cli hooks route --task "architecture: [design]" +``` + +**Example:** +```bash +npx @claude-flow/cli hooks route --task "architecture: auth module with service layer, repository, and API endpoints" +``` + +### Refinement Phase +Iterate on the design based on feedback + +```bash +npx @claude-flow/cli hooks route --task "refinement: [feedback]" +``` + +**Example:** +```bash +npx @claude-flow/cli hooks route --task "refinement: add rate limiting and brute force protection" +``` + +### Completion Phase +Finalize implementation with tests and documentation + +```bash +npx @claude-flow/cli hooks route --task "completion: [final checks]" +``` + +**Example:** +```bash +npx @claude-flow/cli hooks route --task "completion: verify all tests pass, update API docs, security review" +``` + +### SPARC Coordinator +Spawn SPARC coordinator agent + +```bash +npx @claude-flow/cli agent spawn --type sparc-coord --name sparc-lead +``` + + +## Scripts + +| Script | Path | Description | +|--------|------|-------------| +| `sparc-init` | `.agents/scripts/sparc-init.sh` | Initialize SPARC workflow for a new feature | +| `sparc-review` | `.agents/scripts/sparc-review.sh` | Run SPARC phase review checklist | + + +## References + +| Document | Path | Description | +|----------|------|-------------| +| `SPARC Overview` | `docs/sparc.md` | Complete SPARC methodology guide | +| `Phase Templates` | `docs/sparc-templates.md` | Templates for each SPARC phase | + +## Best Practices +1. Check memory for existing patterns before starting +2. Use hierarchical topology for coordination +3. Store successful patterns after completion +4. Document any new learnings diff --git a/.agents/skills/sparc-methodology/scripts/sparc-init.sh b/.agents/skills/sparc-methodology/scripts/sparc-init.sh new file mode 100644 index 0000000..c861d92 --- /dev/null +++ b/.agents/skills/sparc-methodology/scripts/sparc-init.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# SPARC Methodology - Init Script +# Initialize SPARC workflow for a new feature + +set -e + +FEATURE_NAME="${1:-new-feature}" + +echo "Initializing SPARC workflow for: $FEATURE_NAME" + +# Create SPARC documentation directory +mkdir -p "./docs/sparc/$FEATURE_NAME" + +# Create phase files +touch "./docs/sparc/$FEATURE_NAME/1-specification.md" +touch "./docs/sparc/$FEATURE_NAME/2-pseudocode.md" +touch "./docs/sparc/$FEATURE_NAME/3-architecture.md" +touch "./docs/sparc/$FEATURE_NAME/4-refinement.md" +touch "./docs/sparc/$FEATURE_NAME/5-completion.md" + +echo "SPARC workflow initialized in ./docs/sparc/$FEATURE_NAME" diff --git a/.agents/skills/sparc-methodology/scripts/sparc-review.sh b/.agents/skills/sparc-methodology/scripts/sparc-review.sh new file mode 100644 index 0000000..f268d9e --- /dev/null +++ b/.agents/skills/sparc-methodology/scripts/sparc-review.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# SPARC Methodology - Review Script +# Run SPARC phase review checklist + +set -e + +FEATURE_DIR="${1:-.}" + +echo "SPARC Phase Review Checklist" +echo "=============================" + +for phase in specification pseudocode architecture refinement completion; do + if [ -f "$FEATURE_DIR/${phase}.md" ]; then + echo "[x] $phase - found" + else + echo "[ ] $phase - missing" + fi +done diff --git a/.agents/skills/swarm-orchestration/SKILL.md b/.agents/skills/swarm-orchestration/SKILL.md new file mode 100644 index 0000000..1e2a9df --- /dev/null +++ b/.agents/skills/swarm-orchestration/SKILL.md @@ -0,0 +1,114 @@ +--- +name: swarm-orchestration +description: > + Multi-agent swarm coordination for complex tasks. Uses hierarchical topology with specialized agents to break down and execute complex work across multiple files and modules. + Use when: 3+ files need changes, new feature implementation, cross-module refactoring, API changes with tests, security-related changes, performance optimization across codebase, database schema changes. + Skip when: single file edits, simple bug fixes (1-2 lines), documentation updates, configuration changes, quick exploration. +--- + +# Swarm Orchestration Skill + +## Purpose +Multi-agent swarm coordination for complex tasks. Uses hierarchical topology with specialized agents to break down and execute complex work across multiple files and modules. + +## When to Trigger +- 3+ files need changes +- new feature implementation +- cross-module refactoring +- API changes with tests +- security-related changes +- performance optimization across codebase +- database schema changes + +## When to Skip +- single file edits +- simple bug fixes (1-2 lines) +- documentation updates +- configuration changes +- quick exploration + +## Commands + +### Initialize Swarm +Start a new swarm with hierarchical topology (anti-drift) + +```bash +npx @claude-flow/cli swarm init --topology hierarchical --max-agents 8 --strategy specialized +``` + +**Example:** +```bash +npx @claude-flow/cli swarm init --topology hierarchical --max-agents 6 --strategy specialized +``` + +### Route Task +Route a task to the appropriate agents based on task type + +```bash +npx @claude-flow/cli hooks route --task "[task description]" +``` + +**Example:** +```bash +npx @claude-flow/cli hooks route --task "implement OAuth2 authentication flow" +``` + +### Spawn Agent +Spawn a specific agent type + +```bash +npx @claude-flow/cli agent spawn --type [type] --name [name] +``` + +**Example:** +```bash +npx @claude-flow/cli agent spawn --type coder --name impl-auth +``` + +### Monitor Status +Check the current swarm status + +```bash +npx @claude-flow/cli swarm status --verbose +``` + +### Orchestrate Task +Orchestrate a task across multiple agents + +```bash +npx @claude-flow/cli task orchestrate --task "[task]" --strategy adaptive +``` + +**Example:** +```bash +npx @claude-flow/cli task orchestrate --task "refactor auth module" --strategy parallel --max-agents 4 +``` + +### List Agents +List all active agents + +```bash +npx @claude-flow/cli agent list --filter active +``` + + +## Scripts + +| Script | Path | Description | +|--------|------|-------------| +| `swarm-start` | `.agents/scripts/swarm-start.sh` | Initialize swarm with default settings | +| `swarm-monitor` | `.agents/scripts/swarm-monitor.sh` | Real-time swarm monitoring dashboard | + + +## References + +| Document | Path | Description | +|----------|------|-------------| +| `Agent Types` | `docs/agents.md` | Complete list of agent types and capabilities | +| `Topology Guide` | `docs/topology.md` | Swarm topology configuration guide | + +## Best Practices +1. Check memory for existing patterns before starting +2. Use hierarchical topology for coordination +3. Store successful patterns after completion +4. Document any new learnings diff --git a/.agents/skills/swarm-orchestration/scripts/swarm-monitor.sh b/.agents/skills/swarm-orchestration/scripts/swarm-monitor.sh new file mode 100644 index 0000000..5af1014 --- /dev/null +++ b/.agents/skills/swarm-orchestration/scripts/swarm-monitor.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Swarm Orchestration - Monitor Script +# Real-time swarm monitoring + +set -e + +echo "Starting swarm monitor..." +npx @claude-flow/cli swarm status --watch --interval 5 diff --git a/.agents/skills/swarm-orchestration/scripts/swarm-start.sh b/.agents/skills/swarm-orchestration/scripts/swarm-start.sh new file mode 100644 index 0000000..6755acc --- /dev/null +++ b/.agents/skills/swarm-orchestration/scripts/swarm-start.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Swarm Orchestration - Start Script +# Initialize swarm with default anti-drift settings + +set -e + +echo "Initializing hierarchical swarm..." +npx @claude-flow/cli swarm init \ + --topology hierarchical \ + --max-agents 8 \ + --strategy specialized + +echo "Swarm initialized successfully" +npx @claude-flow/cli swarm status diff --git a/.claude-flow/tasks/store.json b/.claude-flow/tasks/store.json new file mode 100644 index 0000000..0b3075d --- /dev/null +++ b/.claude-flow/tasks/store.json @@ -0,0 +1,144 @@ +{ + "tasks": { + "task-1773164482180-yqy5i7": { + "taskId": "task-1773164482180-yqy5i7", + "type": "feature", + "description": "Add PkgID field to OrderRemark struct and parse pkg_id:XX in remark.Parse()", + "priority": "high", + "status": "completed", + "progress": 100, + "assignedTo": [], + "tags": [ + "remark", + "parsing", + "ichiban" + ], + "createdAt": "2026-03-10T17:41:22.180Z", + "startedAt": null, + "completedAt": "2026-03-10T17:41:55.018Z", + "result": { + "reason": "Added PkgID field to OrderRemark struct and pkg_id: parsing branch in Parse()" + } + }, + "task-1773164482201-s9jemx": { + "taskId": "task-1773164482201-s9jemx", + "type": "feature", + "description": "Extend calcPaidByPriceDraw with three-way classification: Case1 ActivityID>0 (lottery), Case2 IssueID>0 (matching game via activity_issues), Case3 PkgID>0 (ichiban via game_pass_packages)", + "priority": "high", + "status": "completed", + "progress": 100, + "assignedTo": [], + "tags": [ + "channel-stats", + "matching-game", + "ichiban", + "calcPaidByPriceDraw" + ], + "createdAt": "2026-03-10T17:41:22.201Z", + "startedAt": null, + "completedAt": "2026-03-10T17:42:34.567Z", + "result": { + "reason": "Extended calcPaidByPriceDraw with three-way classification: lottery (ActivityID), matching game (IssueID→activity_issues→activities), ichiban (PkgID→game_pass_packages)" + } + }, + "task-1773164482206-mhmqsb": { + "taskId": "task-1773164482206-mhmqsb", + "type": "feature", + "description": "Build verification: run make build-mac to ensure compilation passes after changes", + "priority": "normal", + "status": "completed", + "progress": 100, + "assignedTo": [], + "tags": [ + "build", + "verification" + ], + "createdAt": "2026-03-10T17:41:22.206Z", + "startedAt": null, + "completedAt": "2026-03-10T17:43:27.419Z", + "result": { + "reason": "Build passed successfully on macOS" + } + }, + "task-1773166041411-fmshox": { + "taskId": "task-1773166041411-fmshox", + "type": "feature", + "description": "Extend StatsOverview and StatsDailyItem structs with cost_cents, profit_cents, total_cost, total_profit fields", + "priority": "high", + "status": "completed", + "progress": 100, + "assignedTo": [], + "tags": [ + "channel-stats", + "profit-loss", + "structs" + ], + "createdAt": "2026-03-10T18:07:21.411Z", + "startedAt": null, + "completedAt": "2026-03-10T18:07:47.297Z", + "result": { + "reason": "Extended StatsOverview with TotalCostCents/TotalProfitCents/TotalCost/TotalProfit and StatsDailyItem with CostCents/ProfitCents" + } + }, + "task-1773166041417-di6rsd": { + "taskId": "task-1773166041417-di6rsd", + "type": "feature", + "description": "Implement calcCostByInventory helper function: query user_inventory with item card multiplier, grouped by date", + "priority": "high", + "status": "completed", + "progress": 100, + "assignedTo": [], + "tags": [ + "channel-stats", + "profit-loss", + "cost-calculation" + ], + "createdAt": "2026-03-10T18:07:21.417Z", + "startedAt": null, + "completedAt": "2026-03-10T18:08:23.196Z", + "result": { + "reason": "Implemented calcCostByInventory with 6-table JOIN chain, item card multiplier, COALESCE fallback, and optional date range" + } + }, + "task-1773166041422-efwp8w": { + "taskId": "task-1773166041422-efwp8w", + "type": "feature", + "description": "Integrate calcCostByInventory into GetStats: Overview all-time cost + daily trend cost/profit", + "priority": "high", + "status": "completed", + "progress": 100, + "assignedTo": [], + "tags": [ + "channel-stats", + "profit-loss", + "integration" + ], + "createdAt": "2026-03-10T18:07:21.422Z", + "startedAt": null, + "completedAt": "2026-03-10T18:09:08.231Z", + "result": { + "reason": "Integrated calcCostByInventory into GetStats: Overview all-time cost/profit + daily trend cost/profit" + } + }, + "task-1773166041428-fwp50t": { + "taskId": "task-1773166041428-fwp50t", + "type": "feature", + "description": "Build verification and integration test against dev_game database", + "priority": "normal", + "status": "completed", + "progress": 100, + "assignedTo": [], + "tags": [ + "build", + "verification" + ], + "createdAt": "2026-03-10T18:07:21.428Z", + "startedAt": null, + "completedAt": "2026-03-10T18:10:06.759Z", + "result": { + "reason": "Build passed. Integration test shows: 754 inventory records, 2 with double card (双倍快乐水 ×2.0), cost=5100.50元, revenue(actual_amount)=545.00元" + } + } + }, + "version": "3.0.0" +} \ No newline at end of file diff --git a/.claude/plan/channel-stats-frontend.md b/.claude/plan/channel-stats-frontend.md new file mode 100644 index 0000000..d1a5f54 --- /dev/null +++ b/.claude/plan/channel-stats-frontend.md @@ -0,0 +1,249 @@ +# 渠道统计 — 前端盈亏展示 + +## 📋 实施计划:渠道统计页面新增成本/盈亏展示 + +### 任务类型 +- [x] 前端 +- [ ] 后端 +- [ ] 全栈 + +### 需求概述 + +后端 `/admin/channels/:id/stats` 接口已新增以下字段: + +**Overview 新增**: +| 字段 | 类型 | 说明 | +|------|------|------| +| `total_cost_cents` | number | 总成本(分) | +| `total_profit_cents` | number | 盈亏(分) = paid - cost | +| `total_cost` | number | 总成本(元) | +| `total_profit` | number | 盈亏(元) | + +**趋势图每日新增**: +| 字段 | 类型 | 说明 | +|------|------|------| +| `cost_cents` | number | 当日成本(分) | +| `profit_cents` | number | 当日盈亏(分) | + +### 技术方案 + +#### UI 设计 + +**Overview 区域**: +- 现有 3 个卡片(用户、订单、实付金额)→ 扩展为 **5 个卡片** +- 新增:**总成本** 卡片 + **盈亏** 卡片 +- 布局:从 `grid-cols-3` 改为 `grid-cols-5`(或在移动端自适应 `grid-cols-2 md:grid-cols-5`) +- 盈亏卡片需根据正/负值显示不同颜色(盈利=绿色,亏损=红色) + +**趋势图区域**: +- 现有 2 个 Tab(用户增长、付费数据)→ 新增第 3 个 Tab:**盈亏分析** +- 盈亏分析 Tab 包含 3 条曲线:实付金额、成本、盈亏 +- 盈亏曲线可使用虚线区分 + +### 实施步骤 + +#### Step 1: 更新 TypeScript 类型定义 + +**文件**:`web/admin/src/api/channels.ts` + +在 `StatsOverview` 接口新增: +```typescript +export interface StatsOverview { + total_users: number + total_orders: number + total_gmv: number + total_paid_cents?: number + // 新增 + total_cost_cents?: number // 总成本(分) + total_profit_cents?: number // 盈亏(分) + total_cost?: number // 总成本(元) + total_profit?: number // 盈亏(元) +} +``` + +在 `StatsDailyItem` 接口新增: +```typescript +export interface StatsDailyItem { + date: string + user_count: number + order_count: number + gmv: number + paid_cents?: number + // 新增 + cost_cents?: number // 当日成本(分) + profit_cents?: number // 当日盈亏(分) +} +``` + +#### Step 2: 更新 Overview 卡片区域 + +**文件**:`web/admin/src/views/operations/channels/index.vue` + +**2.1** 布局从 `grid-cols-3` 改为 `grid-cols-5` + +**2.2** 新增两个 `ArtStatsCard`: + +```vue + + + + + +``` + +**2.3** 新增 computed 属性: + +```typescript +const totalCostYuan = computed(() => { + const cents = statsData.value.overview.total_cost_cents + if (typeof cents === 'number') { + return Number((cents / 100).toFixed(2)) + } + return 0 +}) + +const totalProfitYuan = computed(() => { + const cents = statsData.value.overview.total_profit_cents + if (typeof cents === 'number') { + return Number((cents / 100).toFixed(2)) + } + return 0 +}) + +const profitCardStyle = computed(() => + totalProfitYuan.value >= 0 ? 'bg-green-50' : 'bg-red-50' +) + +const profitTextColor = computed(() => + totalProfitYuan.value >= 0 ? '#10B981' : '#EF4444' +) + +const profitIconStyle = computed(() => + totalProfitYuan.value >= 0 ? 'bg-green-500' : 'bg-red-500' +) + +const profitDescription = computed(() => + totalProfitYuan.value >= 0 ? '盈利' : '亏损' +) +``` + +#### Step 3: 更新趋势图 Tab + +**文件**:`web/admin/src/views/operations/channels/index.vue` + +**3.1** 在 `el-radio-group` 新增 Tab: + +```vue + + 用户增长 + 付费数据 + 盈亏分析 + +``` + +**3.2** 在 `chartData` computed 中新增 `profit` 分支: + +```typescript +const chartData = computed(() => { + if (statsTab.value === 'growth') { + return [ + { name: '新增用户', data: statsData.value.daily.map(i => i.user_count), smooth: true, color: '#409EFF' } + ] + } else if (statsTab.value === 'revenue') { + return [ + { name: '订单数', data: statsData.value.daily.map(i => i.order_count), smooth: true, color: '#67C23A' }, + { name: '实付金额', data: statsData.value.daily.map(i => getDailyPaidYuan(i)), smooth: true, color: '#E6A23C' } + ] + } else { + // profit tab + return [ + { name: '实付(元)', data: statsData.value.daily.map(i => getDailyPaidYuan(i)), smooth: true, color: '#E6A23C' }, + { name: '成本(元)', data: statsData.value.daily.map(i => getDailyCostYuan(i)), smooth: true, color: '#7C3AED' }, + { name: '盈亏(元)', data: statsData.value.daily.map(i => getDailyProfitYuan(i)), smooth: true, color: '#10B981' } + ] + } +}) +``` + +**3.3** 新增辅助函数: + +```typescript +function getDailyCostYuan(item: { cost_cents?: number }) { + if (typeof item.cost_cents === 'number') { + return Number((item.cost_cents / 100).toFixed(2)) + } + return 0 +} + +function getDailyProfitYuan(item: { profit_cents?: number }) { + if (typeof item.profit_cents === 'number') { + return Number((item.profit_cents / 100).toFixed(2)) + } + return 0 +} +``` + +#### Step 4: 更新 statsData 初始值 + +**文件**:`web/admin/src/views/operations/channels/index.vue` + +```typescript +const statsData = ref({ + overview: { + total_users: 0, total_orders: 0, total_gmv: 0, total_paid_cents: 0, + total_cost_cents: 0, total_profit_cents: 0, total_cost: 0, total_profit: 0 + }, + daily: [] +}) +``` + +### 关键文件 + +| 文件 | 操作 | 说明 | +|------|------|------| +| `web/admin/src/api/channels.ts:L68-86` | 修改 | 扩展 StatsOverview 和 StatsDailyItem 接口 | +| `web/admin/src/views/operations/channels/index.vue:L155-184` | 修改 | Overview 卡片区域新增成本/盈亏卡 | +| `web/admin/src/views/operations/channels/index.vue:L202-205` | 修改 | 趋势图新增盈亏分析 Tab | +| `web/admin/src/views/operations/channels/index.vue:L482-558` | 修改 | 新增 computed 属性和辅助函数 | + +### 风险与缓解 + +| 风险 | 严重程度 | 缓解措施 | +|------|---------|----------| +| 5列卡片在窄屏溢出 | 低 | 使用响应式 `grid-cols-2 md:grid-cols-5`,必要时改为 `grid-cols-3` + 第二行 `grid-cols-2` | +| 后端字段为空(旧数据) | 已解决 | 所有新字段使用 `?` 可选,computed 中做 `typeof` 检查,默认 0 | +| ArtStatsCard 不支持负数展示 | 低 | ArtCountTo 组件底层支持负数(基于 countUp.js),无需额外处理 | +| 盈亏曲线可能有负值 | 低 | ECharts 原生支持负值 Y 轴,图表会自动适配 | + +### 验收标准 + +- [ ] TypeScript 类型定义包含新字段 +- [ ] Overview 展示 5 个卡片(用户、订单、实付、成本、盈亏) +- [ ] 盈亏卡片根据正/负值动态切换颜色(绿/红) +- [ ] 趋势图新增"盈亏分析"Tab +- [ ] 盈亏分析 Tab 展示 3 条曲线(实付、成本、盈亏) +- [ ] `pnpm build` 编译通过 +- [ ] `pnpm type-check` 类型检查通过 + +### SESSION_ID(供 /ccg:execute 使用) + +- CODEX_SESSION: N/A +- GEMINI_SESSION: N/A diff --git a/.claude/plan/channel-stats-optimization.md b/.claude/plan/channel-stats-optimization.md new file mode 100644 index 0000000..b4bc1ca --- /dev/null +++ b/.claude/plan/channel-stats-optimization.md @@ -0,0 +1,311 @@ +# 渠道统计接口优化计划 + +## 需求概述 + +优化 `/admin/channels/:channel_id/stats` 接口: + +| 指标 | 当前实现 | 优化后 | +|------|---------|--------| +| 累计用户 | `COUNT(users WHERE channel_id = X)` | 保持不变 — **全量统计,不限时间** | +| 累计订单 | `COUNT(orders JOIN users ...)` | 保持不变 — **全量统计,不限时间** | +| 累计实付金额 | `SUM(orders.actual_amount)` | remark → activityID → `activities.price_draw × count` — **全量统计,不限时间** | +| 趋势图表 | 按**月**分组(`days` 参数实际当月用) | 修正为按**天**分组,`days` 参数控制天数范围 | + +## 确认的决策 + +- ✅ 直接用 remark 中 activityID 查 `activities.price_draw` +- ✅ 软删除活动也计入(使用 `Unscoped`) +- ✅ `days` 参数修正为按天计算 +- ✅ Overview 三个指标为全量(不受 days 限制) + +## 受影响的代码 + +| 方法 | 文件 | 行号 | 改动内容 | +|------|------|------|---------| +| `GetStats` | `internal/service/channel/channel.go` | L238-355 | 核心改动:金额计算 + days 修正 + 按天分组 | +| `List` | `internal/service/channel/channel.go` | L157-236 | 同步改动:列表 paid_amount 用 price_draw 计算 | +| `StatsOutput` / `StatsDailyItem` | `internal/service/channel/channel.go` | L66-84 | 结构体不变,`Daily` 改为按天粒度 | + +## 实施步骤 + +### Step 1: 新增 `orderRemarkRow` 类型和 `calcPaidByPriceDraw` 辅助函数 + +**文件**:`internal/service/channel/channel.go` + +```go +type orderRemarkRow struct { + Remark string + CreatedAt time.Time +} + +// calcPaidByPriceDraw 解析订单 remark 中的 activityID + count, +// 批量查 activities.price_draw(含软删除),计算实付金额 +// 返回:总金额(分)、按日期key分组的金额 +func (s *service) calcPaidByPriceDraw(ctx context.Context, rows []orderRemarkRow, dateFmt string) (int64, map[string]int64, error) { + if len(rows) == 0 { + return 0, nil, nil + } + + // 1. 解析 remark,收集 unique activityIDs + type parsed struct { + activityID int64 + count int64 + dateKey string + } + var items []parsed + idSet := make(map[int64]struct{}) + + for _, r := range rows { + rmk := remark.Parse(r.Remark) + if rmk.ActivityID > 0 { + items = append(items, parsed{ + activityID: rmk.ActivityID, + count: rmk.Count, + dateKey: r.CreatedAt.Format(dateFmt), + }) + idSet[rmk.ActivityID] = struct{}{} + } + } + + // 2. 批量查 activities.price_draw(含软删除 Unscoped) + actIDs := make([]int64, 0, len(idSet)) + for id := range idSet { + actIDs = append(actIDs, id) + } + priceMap := make(map[int64]int64) + if len(actIDs) > 0 { + var acts []model.Activities + s.readDB.Activities.WithContext(ctx).UnderlyingDB(). + Unscoped(). + Table("activities"). + Select("id, price_draw"). + Where("id IN ?", actIDs). + Find(&acts) + for _, a := range acts { + priceMap[a.ID] = a.PriceDraw + } + } + + // 3. 计算 + var total int64 + byDate := make(map[string]int64) + for _, item := range items { + if price, ok := priceMap[item.activityID]; ok { + amt := price * item.count + total += amt + byDate[item.dateKey] += amt + } + } + return total, byDate, nil +} +``` + +### Step 2: 重写 `GetStats` — 日期逻辑修正 + 金额计算 + +**改动要点**: + +1. **参数 `days` 真正按天**:`startDate = now.AddDate(0, 0, -days+1)` +2. **Overview 全量不限时间**:用户数、订单数、实付金额均查全量 +3. **趋势按天分组**:`DATE_FORMAT(..., '%Y-%m-%d')` 替代 `'%Y-%m'` +4. **金额用 price_draw**:调用 `calcPaidByPriceDraw` + +```go +func (s *service) GetStats(ctx context.Context, channelID int64, days int, startDateStr, endDateStr string) (*StatsOutput, error) { + now := time.Now() + + // 校验渠道存在 + _, err := s.readDB.Channels.WithContext(ctx).Where(s.readDB.Channels.ID.Eq(channelID)).First() + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, ErrChannelNotFound + } + return nil, err + } + + out := &StatsOutput{} + + // ========== 1. Overview(全量,不限时间)========== + + // 1a. 累计用户 + userCount, _ := s.readDB.Users.WithContext(ctx). + Where(s.readDB.Users.ChannelID.Eq(channelID)).Count() + out.Overview.TotalUsers = userCount + + // 1b. 累计订单数 + orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.actual_amount > 0 AND orders.source_type IN (1,2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + type countResult struct{ Count int64 } + var cr countResult + s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("count(*) as count"). + Where(orderFilter, channelID). + Scan(&cr) + out.Overview.TotalOrders = cr.Count + + // 1c. 累计实付金额(全量订单 remark → price_draw × count) + var allRemarks []orderRemarkRow + s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("orders.remark, orders.created_at"). + Where(orderFilter, channelID). + Scan(&allRemarks) + + totalPaid, _, _ := s.calcPaidByPriceDraw(ctx, allRemarks, "2006-01-02") + out.Overview.TotalPaidCents = totalPaid + out.Overview.TotalGMV = totalPaid / 100 + + // ========== 2. 趋势图(按天分组,受 days 限制)========== + + // 2a. 计算日期范围 + var startDate, endDate time.Time + if startDateStr != "" && endDateStr != "" { + startDate, _ = time.Parse("2006-01-02", startDateStr) + endDate, _ = time.Parse("2006-01-02", endDateStr) + endDate = endDate.Add(24*time.Hour - time.Second) + } else { + if days <= 0 { + days = 12 + } + startDate = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()). + AddDate(0, 0, -days+1) + endDate = now + } + + // 2b. 初始化日期桶(每天一个) + dateMap := make(map[string]*StatsDailyItem) + var dateList []string + for d := startDate; !d.After(endDate); d = d.AddDate(0, 0, 1) { + key := d.Format("2006-01-02") + dateList = append(dateList, key) + dateMap[key] = &StatsDailyItem{Date: key} + } + + // 2c. 每日新增用户 + type dailyCount struct { + Date string + Count int64 + } + var dailyUsers []dailyCount + s.readDB.Users.WithContext(ctx).UnderlyingDB().Table("users"). + Select("DATE_FORMAT(created_at, '%Y-%m-%d') as date, count(*) as count"). + Where("channel_id = ? AND deleted_at IS NULL AND created_at >= ? AND created_at <= ?", + channelID, startDate, endDate). + Group("date").Scan(&dailyUsers) + for _, u := range dailyUsers { + if item, ok := dateMap[u.Date]; ok { + item.UserCount = u.Count + } + } + + // 2d. 每日订单数 + var dailyOrders []dailyCount + s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("DATE_FORMAT(orders.created_at, '%Y-%m-%d') as date, count(*) as count"). + Where(orderFilter+" AND orders.created_at >= ? AND orders.created_at <= ?", + channelID, startDate, endDate). + Group("date").Scan(&dailyOrders) + for _, o := range dailyOrders { + if item, ok := dateMap[o.Date]; ok { + item.OrderCount = o.Count + } + } + + // 2e. 每日实付金额(remark → price_draw) + var rangeRemarks []orderRemarkRow + s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("orders.remark, orders.created_at"). + Where(orderFilter+" AND orders.created_at >= ? AND orders.created_at <= ?", + channelID, startDate, endDate). + Scan(&rangeRemarks) + + _, dailyPaid, _ := s.calcPaidByPriceDraw(ctx, rangeRemarks, "2006-01-02") + for dateKey, paid := range dailyPaid { + if item, ok := dateMap[dateKey]; ok { + item.PaidCents = paid + item.GMV = paid / 100 + } + } + + // 2f. 组装输出 + for _, d := range dateList { + out.Daily = append(out.Daily, *dateMap[d]) + } + + return out, nil +} +``` + +### Step 3: 同步修改 `List` 方法的金额计算 + +**文件**:`internal/service/channel/channel.go`,L206-223 + +**当前**:`SUM(orders.actual_amount)` 聚合。 + +**修改为**:按渠道查询所有订单 remark,分渠道调用 `calcPaidByPriceDraw`。 + +```go +// 替换原有 paidResults 查询逻辑: +if len(channelIDs) > 0 { + // ... userCount 查询保持不变 ... + + // 实付金额:查所有渠道的订单 remark + type remarkWithChannel struct { + ChannelID int64 + Remark string + CreatedAt time.Time + } + var chRemarks []remarkWithChannel + s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("users.channel_id, orders.remark, orders.created_at"). + Where("users.channel_id IN ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.actual_amount > 0 AND orders.source_type IN (1,2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)", channelIDs). + Scan(&chRemarks) + + // 按渠道分组 + grouped := make(map[int64][]orderRemarkRow) + for _, r := range chRemarks { + grouped[r.ChannelID] = append(grouped[r.ChannelID], orderRemarkRow{ + Remark: r.Remark, CreatedAt: r.CreatedAt, + }) + } + + for chID, rows := range grouped { + total, _, _ := s.calcPaidByPriceDraw(ctx, rows, "2006-01-02") + paidStats[chID] = total + } +} +``` + +### Step 4: 添加 remark import + +确保文件顶部 import 包含: +```go +"bindbox-game/internal/pkg/util/remark" +``` + +## 风险与缓解 + +| 风险 | 严重程度 | 缓解措施 | +|------|---------|----------| +| remark 格式不一致 | 中 | `remark.Parse()` 已处理 `activity:` 和 `lottery:activity:` 两种前缀 | +| 软删除活动 | 已解决 | 使用 `Unscoped()` 查询,确保被删活动仍有 price_draw | +| List 方法大量订单性能 | 中 | 单次查询所有渠道订单 remark,Go 中分组计算,比 N+1 高效 | +| days 参数前端兼容 | 低 | 前端传 `days=12` 原意应为12天,修正后行为与参数名一致 | + +## 验收标准 + +- [ ] Overview 累计用户:全量统计 `users.channel_id = X` 的用户数(不限时间) +- [ ] Overview 累计订单:全量统计有效订单数(不限时间) +- [ ] Overview 累计实付金额:全量基于 `activities.price_draw × count` 计算(包含软删除活动) +- [ ] 趋势图按**天**分组,`days` 参数控制显示天数 +- [ ] 渠道列表页 `paid_amount` 同步使用 price_draw 计算 +- [ ] 编译通过 `make build-mac` +- [ ] 现有功能无回归 + +## SESSION_ID(供 /ccg:execute 使用) + +- CODEX_SESSION: N/A +- GEMINI_SESSION: N/A diff --git a/.claude/plan/channel-stats-profit-loss.md b/.claude/plan/channel-stats-profit-loss.md new file mode 100644 index 0000000..53efaa5 --- /dev/null +++ b/.claude/plan/channel-stats-profit-loss.md @@ -0,0 +1,187 @@ +# 渠道统计 — 盈亏计算 + +## 需求概述 + +在 `/admin/channels/:id/stats` 接口的 Overview 和趋势图中新增盈亏指标。 + +### 盈亏公式 + +``` +盈亏 = 收入(price_draw × count) - 成本(奖品价值 × 道具卡倍数) +``` + +### 数据源 + +| 维度 | 来源 | 说明 | +|------|------|------| +| **收入** | 已有 `calcPaidByPriceDraw` | 三路分类:抽奖/对对碰/一番赏 | +| **成本** | `user_inventory.value_cents` | 奖品价值快照(分),fallback: `activity_reward_settings.price_snapshot_cents` → `products.price` | +| **道具卡倍数** | `orders.item_card_id` → `user_item_cards.card_id` → `system_item_cards.reward_multiplier_x1000` | 双倍卡 = 2000(千分比),无卡 = 1000 | + +### 成本计算公式(参考已有 dashboard_activity.go:L234-239) + +```sql +单件成本 = COALESCE(NULLIF(user_inventory.value_cents, 0), + activity_reward_settings.price_snapshot_cents, + products.price, 0) + +道具卡倍数 = GREATEST(COALESCE(system_item_cards.reward_multiplier_x1000, 1000), 1000) / 1000 + +总成本 = SUM(单件成本 × 道具卡倍数) +``` + +## 实施步骤 + +### Step 1: 扩展响应结构体 + +**文件**:`internal/service/channel/channel.go` + +```go +type StatsOverview struct { + TotalUsers int64 `json:"total_users"` + TotalOrders int64 `json:"total_orders"` + TotalGMV int64 `json:"total_gmv"` + TotalPaidCents int64 `json:"total_paid_cents"` + // 新增 + TotalCostCents int64 `json:"total_cost_cents"` // 总成本(分) + TotalProfitCents int64 `json:"total_profit_cents"` // 盈亏(分) = paid - cost + TotalCost int64 `json:"total_cost"` // 总成本(元) + TotalProfit int64 `json:"total_profit"` // 盈亏(元) +} + +type StatsDailyItem struct { + Date string `json:"date"` + UserCount int64 `json:"user_count"` + OrderCount int64 `json:"order_count"` + GMV int64 `json:"gmv"` + PaidCents int64 `json:"paid_cents"` + // 新增 + CostCents int64 `json:"cost_cents"` // 当日成本(分) + ProfitCents int64 `json:"profit_cents"` // 当日盈亏(分) +} +``` + +### Step 2: 新增 `calcCostByInventory` 辅助函数 + +**文件**:`internal/service/channel/channel.go` + +**输入**:渠道用户 ID 列表 + 日期范围(可选) +**输出**:总成本(分)、按日期分组的成本 + +```go +type costRow struct { + ValueCents int64 + Multiplier int64 // reward_multiplier_x1000,无卡时=1000 + CreatedAt time.Time +} + +func (s *service) calcCostByInventory(ctx context.Context, channelID int64, dateFmt string, startDate, endDate *time.Time) (int64, map[string]int64) { + // SQL 核心逻辑(复用 dashboard_activity.go:L234-239 模式): + // + // SELECT + // COALESCE(NULLIF(ui.value_cents, 0), ars.price_snapshot_cents, p.price, 0) AS unit_cost, + // GREATEST(COALESCE(sic.reward_multiplier_x1000, 1000), 1000) AS multiplier, + // ui.created_at + // FROM user_inventory ui + // JOIN users u ON u.id = ui.user_id + // LEFT JOIN orders o ON o.id = ui.order_id + // LEFT JOIN activity_reward_settings ars ON ars.id = ui.reward_id + // LEFT JOIN products p ON p.id = ui.product_id + // LEFT JOIN user_item_cards uic ON uic.id = o.item_card_id + // LEFT JOIN system_item_cards sic ON sic.id = uic.card_id + // WHERE u.channel_id = ? AND u.deleted_at IS NULL + // AND ui.status IN (1, 3) -- 持有 or 已使用/发货 + // AND COALESCE(ui.remark, '') NOT LIKE '%void%' + // AND (o.status = 2 OR ui.order_id = 0 OR ui.order_id IS NULL) -- 兼容历史 + // [AND ui.created_at >= ? AND ui.created_at <= ?] -- 可选时间范围 + + // Go 侧计算: + // for each row: + // cost += unit_cost * multiplier / 1000 + // byDate[dateKey] += unit_cost * multiplier / 1000 +} +``` + +**关键点**: +- 通过 `users.channel_id` 过滤渠道用户 +- `ui.status IN (1, 3)`:只统计有效资产(持有 + 已发货),排除作废 +- `NOT LIKE '%void%'`:排除作废备注 +- `(o.status = 2 OR ui.order_id = 0 OR ui.order_id IS NULL)`:兼容历史数据 +- 道具卡倍数通过 `orders.item_card_id` → `user_item_cards.card_id` → `system_item_cards.reward_multiplier_x1000` 链路获取 + +### Step 3: 在 `GetStats` 中调用成本计算 + +**文件**:`internal/service/channel/channel.go`,`GetStats` 方法 + +```go +// ========== Overview 全量成本 ========== +totalCost, _ := s.calcCostByInventory(ctx, channelID, "2006-01-02", nil, nil) +out.Overview.TotalCostCents = totalCost +out.Overview.TotalCost = totalCost / 100 +out.Overview.TotalProfitCents = out.Overview.TotalPaidCents - totalCost +out.Overview.TotalProfit = out.Overview.TotalProfitCents / 100 + +// ========== 趋势图日维度成本 ========== +_, dailyCost := s.calcCostByInventory(ctx, channelID, "2006-01-02", &startDate, &endDate) +for dateKey, cost := range dailyCost { + if item, ok := dateMap[dateKey]; ok { + item.CostCents = cost + item.ProfitCents = item.PaidCents - cost + } +} +``` + +### Step 4: 在 `List` 中可选加入成本(列表页) + +**暂不实施**。列表页已有 `paid_amount`,盈亏是详情页指标,列表页展示所有渠道的成本查询开销较大。后续按需添加。 + +## 关键文件 + +| 文件 | 操作 | 说明 | +|------|------|------| +| `internal/service/channel/channel.go` | 修改 | 扩展结构体 + 新增 `calcCostByInventory` + 修改 `GetStats` | + +## 查询关系链 + +``` +user_inventory + ├── JOIN users ON users.id = ui.user_id (过滤渠道) + ├── LEFT JOIN orders ON orders.id = ui.order_id (获取 item_card_id) + ├── LEFT JOIN activity_reward_settings ON ars.id = ui.reward_id (价格快照) + ├── LEFT JOIN products ON p.id = ui.product_id (商品价格 fallback) + ├── LEFT JOIN user_item_cards ON uic.id = o.item_card_id (道具卡实例) + └── LEFT JOIN system_item_cards ON sic.id = uic.card_id (道具卡倍数) +``` + +## 道具卡逻辑说明 + +| 场景 | `reward_multiplier_x1000` | 效果 | 成本影响 | +|------|---------------------------|------|---------| +| 无道具卡 | NULL → COALESCE → 1000 | ×1.0 | 成本 = 奖品原价 | +| 双倍卡 | 2000 | ×2.0 | 成本 = 奖品原价 × 2 | +| 三倍卡(如有) | 3000 | ×3.0 | 成本 = 奖品原价 × 3 | + +**原理**:双倍卡让用户以相同支付价格获得双倍奖品,收入不变但成本翻倍,利润下降。 + +## 风险与缓解 + +| 风险 | 严重程度 | 缓解措施 | +|------|---------|----------| +| `user_inventory` 数据量大,全量查询慢 | 中 | 通过 `users.channel_id` 索引过滤,只查渠道用户 | +| 历史资产无 `order_id` | 已解决 | `(o.status = 2 OR ui.order_id = 0 OR ui.order_id IS NULL)` 兼容 | +| `value_cents = 0` 的历史数据 | 已解决 | COALESCE 链式 fallback 到 `price_snapshot_cents` → `products.price` | +| 概率提升卡(EffectType=2)不影响成本 | 低 | `reward_multiplier_x1000` 只在 EffectType=1 时 > 1000,概率卡该字段为 1000,GREATEST 确保最小为 1.0 | + +## 验收标准 + +- [ ] Overview 新增 `total_cost_cents`、`total_profit_cents`、`total_cost`、`total_profit` +- [ ] 趋势图每天新增 `cost_cents`、`profit_cents` +- [ ] 道具卡(双倍)正确计入成本(×2) +- [ ] 无道具卡时成本不受影响(×1) +- [ ] 成本计算排除 status=2(作废)和 void 备注的资产 +- [ ] 编译通过 `make build-mac` + +## SESSION_ID(供 /ccg:execute 使用) + +- CODEX_SESSION: N/A +- GEMINI_SESSION: N/A diff --git a/.gitignore b/.gitignore index 52ae484..a417d65 100755 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,15 @@ configs/*.toml .env .env.* !.env.example + +# Codex local configuration +.codex/ + +# Claude Flow runtime data +.claude-flow/data/ +.claude-flow/logs/ + +# Environment variables +.env +.env.local +.env.*.local diff --git a/.swarm/model-router-state.json b/.swarm/model-router-state.json new file mode 100644 index 0000000..4d33d75 --- /dev/null +++ b/.swarm/model-router-state.json @@ -0,0 +1,14 @@ +{ + "totalDecisions": 3, + "modelDistribution": { + "haiku": 0, + "sonnet": 0, + "opus": 3, + "inherit": 0 + }, + "avgComplexity": 0.42307874564459924, + "avgConfidence": 0.5675513529812717, + "circuitBreakerTrips": 0, + "lastUpdated": "2026-03-10T18:07:21.401Z", + "learningHistory": [] +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..48ea3ab --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,145 @@ +# bindbox_game + +> Multi-agent orchestration framework for agentic coding + +## Project Overview + +A Claude Flow powered project + +**Tech Stack**: TypeScript, Node.js +**Architecture**: Domain-Driven Design with bounded contexts + +## Quick Start + +### Installation +```bash +npm install +``` + +### Build +```bash +npm run build +``` + +### Test +```bash +npm test +``` + +### Development +```bash +npm run dev +``` + +## Agent Coordination + +### Swarm Configuration + +This project uses hierarchical swarm coordination for complex tasks: + +| Setting | Value | Purpose | +|---------|-------|---------| +| Topology | `hierarchical` | Queen-led coordination (anti-drift) | +| Max Agents | 8 | Optimal team size | +| Strategy | `specialized` | Clear role boundaries | +| Consensus | `raft` | Leader-based consistency | + +### When to Use Swarms + +**Invoke swarm for:** +- Multi-file changes (3+ files) +- New feature implementation +- Cross-module refactoring +- API changes with tests +- Security-related changes +- Performance optimization + +**Skip swarm for:** +- Single file edits +- Simple bug fixes (1-2 lines) +- Documentation updates +- Configuration changes + +### Available Skills + +Use `$skill-name` syntax to invoke: + +| Skill | Use Case | +|-------|----------| +| `$swarm-orchestration` | Multi-agent task coordination | +| `$memory-management` | Pattern storage and retrieval | +| `$sparc-methodology` | Structured development workflow | +| `$security-audit` | Security scanning and CVE detection | + +### Agent Types + +| Type | Role | Use Case | +|------|------|----------| +| `researcher` | Requirements analysis | Understanding scope | +| `architect` | System design | Planning structure | +| `coder` | Implementation | Writing code | +| `tester` | Test creation | Quality assurance | +| `reviewer` | Code review | Security and quality | + +## Code Standards + +### File Organization +- **NEVER** save to root folder +- `/src` - Source code files +- `/tests` - Test files +- `/docs` - Documentation +- `/config` - Configuration files + +### Quality Rules +- Files under 500 lines +- No hardcoded secrets +- Input validation at boundaries +- Typed interfaces for public APIs +- TDD London School (mock-first) preferred + +### Commit Messages +``` +(): + +[optional body] + +Co-Authored-By: claude-flow +``` + +Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore` + +## Security + +### Critical Rules +- NEVER commit secrets, credentials, or .env files +- NEVER hardcode API keys +- Always validate user input +- Use parameterized queries for SQL +- Sanitize output to prevent XSS + +### Path Security +- Validate all file paths +- Prevent directory traversal (../) +- Use absolute paths internally + +## Memory System + +### Storing Patterns +```bash +npx @claude-flow/cli memory store \ + --key "pattern-name" \ + --value "pattern description" \ + --namespace patterns +``` + +### Searching Memory +```bash +npx @claude-flow/cli memory search \ + --query "search terms" \ + --namespace patterns +``` + +## Links + +- Documentation: https://github.com/ruvnet/claude-flow +- Issues: https://github.com/ruvnet/claude-flow/issues diff --git a/bindboxgame_api b/bindboxgame_api new file mode 100755 index 0000000..5d2c3f7 Binary files /dev/null and b/bindboxgame_api differ diff --git a/cmd/channel_stats_compare/detail.go b/cmd/channel_stats_compare/detail.go new file mode 100644 index 0000000..058c2cd --- /dev/null +++ b/cmd/channel_stats_compare/detail.go @@ -0,0 +1,61 @@ +package main + +import ( + "fmt" + + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +func main() { + dsn := "root:bindbox2025kdy@tcp(150.158.78.154:3306)/dev_game?charset=utf8mb4&parseTime=True&loc=Local" + db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + + channelID := 3 + filter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.actual_amount > 0 AND orders.source_type IN (1,2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + type SourceStat struct { + SourceType int32 + HasRemark string + Count int64 + TotalCents int64 + } + var stats []SourceStat + db.Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("orders.source_type, CASE WHEN orders.remark LIKE '%lottery:activity:%' OR orders.remark LIKE '%activity:%' THEN 'Y' ELSE 'N' END as has_remark, COUNT(*) as count, SUM(orders.actual_amount) as total_cents"). + Where(filter, channelID). + Group("orders.source_type, has_remark"). + Order("orders.source_type, has_remark"). + Scan(&stats) + + fmt.Println("source_type: 1=直购, 2=抽奖, 3=翻牌, 4=一番赏") + fmt.Printf("%-12s %-12s %-10s %-15s\n", "source_type", "有remark", "订单数", "actual_amount(分)") + fmt.Println("---------------------------------------------------") + for _, s := range stats { + fmt.Printf("%-12d %-12s %-10d %-15d\n", s.SourceType, s.HasRemark, s.Count, s.TotalCents) + } + + type Sample struct { + ID int64 + SourceType int32 + ActualAmount int64 + Remark string + } + var samples []Sample + db.Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("orders.id, orders.source_type, orders.actual_amount, orders.remark"). + Where(filter+" AND (orders.remark = '' OR orders.remark NOT LIKE '%activity:%')", channelID). + Limit(10). + Scan(&samples) + + fmt.Println("\n无 activity remark 的订单示例:") + for _, s := range samples { + rmk := s.Remark + if len(rmk) > 80 { + rmk = rmk[:80] + "..." + } + fmt.Printf(" ID=%-6d type=%d amount=%-8d remark=[%s]\n", s.ID, s.SourceType, s.ActualAmount, rmk) + } +} diff --git a/cmd/channel_stats_compare/ichiban.go b/cmd/channel_stats_compare/ichiban.go new file mode 100644 index 0000000..a49dc92 --- /dev/null +++ b/cmd/channel_stats_compare/ichiban.go @@ -0,0 +1,55 @@ +package main + +import ( + "fmt" + + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +func main() { + dsn := "root:bindbox2025kdy@tcp(150.158.78.154:3306)/dev_game?charset=utf8mb4&parseTime=True&loc=Local" + db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + + channelID := 3 + filter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.actual_amount > 0 AND orders.source_type IN (1,2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + type Sample struct { + ID int64 + SourceType int32 + ActualAmount int64 + Remark string + } + + // 一番赏 remark + var ichiban []Sample + db.Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("orders.id, orders.source_type, orders.actual_amount, orders.remark"). + Where(filter+" AND orders.source_type = 4", channelID). + Limit(5). + Scan(&ichiban) + + fmt.Println("=== 一番赏 (source_type=4) remark 示例 ===") + for _, s := range ichiban { + fmt.Printf(" ID=%-6d amount=%-8d remark=[%s]\n", s.ID, s.ActualAmount, s.Remark) + } + + // 翻牌 matching_game 的 issue 对应关系 + type IssueActivity struct { + IssueID int64 + ActivityID int64 + PriceDraw int64 + } + var ia []IssueActivity + db.Table("activity_issues"). + Joins("JOIN activities ON activities.id = activity_issues.activity_id"). + Select("activity_issues.id as issue_id, activity_issues.activity_id, activities.price_draw"). + Where("activity_issues.id IN (92, 96, 104)"). + Scan(&ia) + + fmt.Println("\n=== 翻牌 issue → activity → price_draw ===") + for _, r := range ia { + fmt.Printf(" issue_id=%d → activity_id=%d → price_draw=%d\n", r.IssueID, r.ActivityID, r.PriceDraw) + } +} diff --git a/cmd/channel_stats_compare/main.go b/cmd/channel_stats_compare/main.go new file mode 100644 index 0000000..106b0d1 --- /dev/null +++ b/cmd/channel_stats_compare/main.go @@ -0,0 +1,199 @@ +package main + +import ( + "fmt" + "strings" + + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +func main() { + dsn := "root:bindbox2025kdy@tcp(150.158.78.154:3306)/dev_game?charset=utf8mb4&parseTime=True&loc=Local" + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + if err != nil { + fmt.Println("连接失败:", err) + return + } + + channelID := 3 + orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.actual_amount > 0 AND orders.source_type IN (1,2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + // 1. actual_amount 统计 + type AmountResult struct { + OrderCount int64 + TotalCents int64 + } + var ar AmountResult + db.Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("COUNT(DISTINCT orders.id) as order_count, COALESCE(SUM(orders.actual_amount), 0) as total_cents"). + Where(orderFilter, channelID). + Scan(&ar) + + fmt.Println("========================================") + fmt.Printf("渠道 %d 数据对比\n", channelID) + fmt.Println("========================================") + fmt.Println() + fmt.Println("【方式1】SUM(actual_amount) — 用户实际支付") + fmt.Printf(" 订单数: %d\n", ar.OrderCount) + fmt.Printf(" 金额: %d 分 = %.2f 元\n", ar.TotalCents, float64(ar.TotalCents)/100) + fmt.Println() + + // 2. 取所有订单的 remark,Go 中解析 + type RemarkRow struct { + Remark string + } + var remarks []RemarkRow + db.Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("orders.remark"). + Where(orderFilter, channelID). + Scan(&remarks) + + // 解析 remark 收集 activityIDs + type parsed struct { + activityID int64 + count int64 + } + var items []parsed + idSet := make(map[int64]struct{}) + noRemarkCount := 0 + + for _, r := range remarks { + aid, cnt := parseRemark(r.Remark) + if aid > 0 { + items = append(items, parsed{activityID: aid, count: cnt}) + idSet[aid] = struct{}{} + } else { + noRemarkCount++ + } + } + + // 批量查 price_draw(含软删除) + actIDs := make([]int64, 0, len(idSet)) + for id := range idSet { + actIDs = append(actIDs, id) + } + + type ActPrice struct { + ID int64 + PriceDraw int64 + } + priceMap := make(map[int64]int64) + if len(actIDs) > 0 { + var acts []ActPrice + db.Unscoped().Table("activities"). + Select("id, price_draw"). + Where("id IN ?", actIDs). + Find(&acts) + for _, a := range acts { + priceMap[a.ID] = a.PriceDraw + } + } + + // 计算 price_draw × count + var totalPriceDraw int64 + matchedCount := 0 + unmatchedCount := 0 + for _, item := range items { + if price, ok := priceMap[item.activityID]; ok { + totalPriceDraw += price * item.count + matchedCount++ + } else { + unmatchedCount++ + } + } + + fmt.Println("【方式2】price_draw × count — 门票原价(当前实现)") + fmt.Printf(" 有效订单: %d (有 remark 且匹配活动)\n", matchedCount) + fmt.Printf(" 无 remark: %d\n", noRemarkCount) + fmt.Printf(" 活动不存在: %d\n", unmatchedCount) + fmt.Printf(" 金额: %d 分 = %.2f 元\n", totalPriceDraw, float64(totalPriceDraw)/100) + fmt.Println() + + // 3. 差额 + diff := totalPriceDraw - ar.TotalCents + fmt.Println("【差异分析】") + fmt.Printf(" price_draw×count - actual_amount = %d 分 = %.2f 元\n", diff, float64(diff)/100) + if diff > 0 { + fmt.Printf(" 说明: 用户总共享受了 %.2f 元优惠(优惠券/积分/折扣)\n", float64(diff)/100) + } else if diff < 0 { + fmt.Printf(" 说明: actual_amount 比 price_draw×count 多 %.2f 元(可能有额外费用)\n", float64(-diff)/100) + } else { + fmt.Println(" 说明: 两者完全一致,无优惠抵扣") + } + fmt.Println() + + // 4. 抽样展示前10条差异订单 + type DetailRow struct { + OrderID int64 + ActualAmount int64 + Remark string + } + var details []DetailRow + db.Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("orders.id as order_id, orders.actual_amount, orders.remark"). + Where(orderFilter, channelID). + Limit(200). + Scan(&details) + + fmt.Println("【差异订单抽样(前10条有差异的)】") + fmt.Printf("%-10s %-15s %-15s %-10s %s\n", "订单ID", "actual_amount", "price×count", "差额", "remark摘要") + fmt.Println(strings.Repeat("-", 90)) + shown := 0 + for _, d := range details { + aid, cnt := parseRemark(d.Remark) + if aid <= 0 { + continue + } + price, ok := priceMap[aid] + if !ok { + continue + } + priceTotal := price * cnt + orderDiff := priceTotal - d.ActualAmount + if orderDiff != 0 && shown < 10 { + remarkShort := d.Remark + if len(remarkShort) > 40 { + remarkShort = remarkShort[:40] + "..." + } + fmt.Printf("%-10d %-15d %-15d %-10d %s\n", d.OrderID, d.ActualAmount, priceTotal, orderDiff, remarkShort) + shown++ + } + } + if shown == 0 { + fmt.Println(" (前200条订单中无差异)") + } +} + +func parseRemark(rm string) (activityID, count int64) { + count = 1 + parts := strings.Split(rm, "|") + for _, p := range parts { + if strings.HasPrefix(p, "lottery:activity:") { + activityID = parseInt64(p[17:]) + } else if strings.HasPrefix(p, "activity:") { + activityID = parseInt64(p[9:]) + } else if strings.HasPrefix(p, "count:") { + n := parseInt64(p[6:]) + if n > 0 { + count = n + } + } + } + return +} + +func parseInt64(s string) int64 { + var n int64 + for _, c := range s { + if c >= '0' && c <= '9' { + n = n*10 + int64(c-'0') + } else { + break + } + } + return n +} diff --git a/cmd/channel_stats_compare/profit_loss.go b/cmd/channel_stats_compare/profit_loss.go index ee306ce..a04ecb4 100644 --- a/cmd/channel_stats_compare/profit_loss.go +++ b/cmd/channel_stats_compare/profit_loss.go @@ -40,6 +40,7 @@ func main() { Where("COALESCE(user_inventory.remark, '') NOT LIKE ?", "%void%"). Where("(orders.status = 2 OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)"). Where("(orders.source_type IN (1,2,3,4) OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)"). + Where("(orders.actual_amount > 0 OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)"). Scan(&rows) var totalCostBase, totalCostFinal int64 @@ -128,6 +129,7 @@ func main() { Where("COALESCE(user_inventory.remark, '') NOT LIKE ?", "%void%"). Where("(orders.status = 2 OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)"). Where("(orders.source_type IN (1,2,3,4) OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)"). + Where("(orders.actual_amount > 0 OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)"). Where("system_item_cards.id IS NOT NULL"). Group("system_item_cards.id"). Scan(&cards) diff --git a/cmd/channel_stats_compare/verify.go b/cmd/channel_stats_compare/verify.go new file mode 100644 index 0000000..a866da3 --- /dev/null +++ b/cmd/channel_stats_compare/verify.go @@ -0,0 +1,246 @@ +package main + +import ( + "fmt" + "strings" + + "gorm.io/driver/mysql" + "gorm.io/gorm" + + "bindbox-game/internal/pkg/util/remark" +) + +func main() { + dsn := "root:bindbox2025kdy@tcp(150.158.78.154:3306)/dev_game?charset=utf8mb4&parseTime=True&loc=Local" + db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) + if err != nil { + fmt.Println("连接失败:", err) + return + } + + channelID := 3 + orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.actual_amount > 0 AND orders.source_type IN (1,2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + // 1. 查所有订单 remark + source_type + type RemarkRow struct { + ID int64 + Remark string + SourceType int32 + } + var rows []RemarkRow + db.Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("orders.id, orders.remark, orders.source_type"). + Where(orderFilter, channelID). + Scan(&rows) + + fmt.Printf("渠道 %d 总有效订单: %d\n\n", channelID, len(rows)) + + // 2. 三路分类统计 + var case1, case2, case3, unmatched int + actIDSet := make(map[int64]struct{}) + issueIDSet := make(map[int64]struct{}) + pkgIDSet := make(map[int64]struct{}) + + type parsed struct { + orderID int64 + caseType int + activityID int64 + issueID int64 + pkgID int64 + count int64 + } + var items []parsed + + for _, r := range rows { + rmk := remark.Parse(r.Remark) + p := parsed{orderID: r.ID, count: rmk.Count} + + if rmk.ActivityID > 0 { + p.caseType = 1 + p.activityID = rmk.ActivityID + actIDSet[rmk.ActivityID] = struct{}{} + case1++ + } else if rmk.IssueID > 0 { + p.caseType = 2 + p.issueID = rmk.IssueID + issueIDSet[rmk.IssueID] = struct{}{} + case2++ + } else if rmk.PkgID > 0 { + p.caseType = 3 + p.pkgID = rmk.PkgID + pkgIDSet[rmk.PkgID] = struct{}{} + case3++ + } else { + unmatched++ + } + items = append(items, p) + } + + fmt.Println("=== 三路分类统计 ===") + fmt.Printf(" Case1 (抽奖/直购, ActivityID>0): %d 笔\n", case1) + fmt.Printf(" Case2 (对对碰, IssueID>0): %d 笔\n", case2) + fmt.Printf(" Case3 (一番赏, PkgID>0): %d 笔\n", case3) + fmt.Printf(" 未匹配: %d 笔\n", unmatched) + fmt.Println() + + // 3. 查 activity_issues (Case2) + issueActivityMap := make(map[int64]int64) + if len(issueIDSet) > 0 { + issueIDs := make([]int64, 0, len(issueIDSet)) + for id := range issueIDSet { + issueIDs = append(issueIDs, id) + } + type IssueRow struct { + ID int64 + ActivityID int64 + } + var issueRows []IssueRow + db.Table("activity_issues"). + Select("id, activity_id"). + Where("id IN ?", issueIDs). + Scan(&issueRows) + for _, ir := range issueRows { + issueActivityMap[ir.ID] = ir.ActivityID + actIDSet[ir.ActivityID] = struct{}{} + } + fmt.Printf("activity_issues 查到: %d / %d\n", len(issueRows), len(issueIDs)) + } + + // 4. 查 activities.price_draw (Case1+2) + priceMap := make(map[int64]int64) + if len(actIDSet) > 0 { + actIDs := make([]int64, 0, len(actIDSet)) + for id := range actIDSet { + actIDs = append(actIDs, id) + } + type ActRow struct { + ID int64 + PriceDraw int64 + } + var actRows []ActRow + db.Unscoped().Table("activities"). + Select("id, price_draw"). + Where("id IN ?", actIDs). + Scan(&actRows) + for _, a := range actRows { + priceMap[a.ID] = a.PriceDraw + } + fmt.Printf("activities 查到: %d / %d\n", len(actRows), len(actIDs)) + } + + // 5. 查 game_pass_packages.price (Case3) + pkgPriceMap := make(map[int64]int64) + if len(pkgIDSet) > 0 { + pkgIDs := make([]int64, 0, len(pkgIDSet)) + for id := range pkgIDSet { + pkgIDs = append(pkgIDs, id) + } + type PkgRow struct { + ID int64 + Price int64 + } + var pkgRows []PkgRow + db.Unscoped().Table("game_pass_packages"). + Select("id, price"). + Where("id IN ?", pkgIDs). + Scan(&pkgRows) + for _, p := range pkgRows { + pkgPriceMap[p.ID] = p.Price + } + fmt.Printf("game_pass_packages 查到: %d / %d\n", len(pkgRows), len(pkgIDs)) + } + fmt.Println() + + // 6. 计算金额 + var totalCase1, totalCase2, totalCase3 int64 + var matchedCase1, matchedCase2, matchedCase3 int + var unmatchedCase1, unmatchedCase2, unmatchedCase3 int + + for _, item := range items { + switch item.caseType { + case 1: + if price, ok := priceMap[item.activityID]; ok { + totalCase1 += price * item.count + matchedCase1++ + } else { + unmatchedCase1++ + } + case 2: + if actID, ok := issueActivityMap[item.issueID]; ok { + if price, ok := priceMap[actID]; ok { + totalCase2 += price * item.count + matchedCase2++ + } else { + unmatchedCase2++ + } + } else { + unmatchedCase2++ + } + case 3: + if price, ok := pkgPriceMap[item.pkgID]; ok { + totalCase3 += price * item.count + matchedCase3++ + } else { + unmatchedCase3++ + } + } + } + + total := totalCase1 + totalCase2 + totalCase3 + + fmt.Println("=== 金额统计 (price_draw/price × count) ===") + fmt.Printf(" Case1 抽奖/直购: %d 分 = %.2f 元 (匹配 %d, 未匹配 %d)\n", + totalCase1, float64(totalCase1)/100, matchedCase1, unmatchedCase1) + fmt.Printf(" Case2 对对碰: %d 分 = %.2f 元 (匹配 %d, 未匹配 %d)\n", + totalCase2, float64(totalCase2)/100, matchedCase2, unmatchedCase2) + fmt.Printf(" Case3 一番赏: %d 分 = %.2f 元 (匹配 %d, 未匹配 %d)\n", + totalCase3, float64(totalCase3)/100, matchedCase3, unmatchedCase3) + fmt.Println(strings.Repeat("-", 60)) + fmt.Printf(" 合计: %d 分 = %.2f 元\n", total, float64(total)/100) + fmt.Printf(" 覆盖订单: %d / %d (%.1f%%)\n", + matchedCase1+matchedCase2+matchedCase3, len(rows), + float64(matchedCase1+matchedCase2+matchedCase3)/float64(len(rows))*100) + fmt.Println() + + // 7. 对比 actual_amount + type AmountResult struct { + TotalCents int64 + } + var ar AmountResult + db.Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("COALESCE(SUM(orders.actual_amount), 0) as total_cents"). + Where(orderFilter, channelID). + Scan(&ar) + + fmt.Println("=== 对比 ===") + fmt.Printf(" SUM(actual_amount): %d 分 = %.2f 元\n", ar.TotalCents, float64(ar.TotalCents)/100) + fmt.Printf(" price_draw/price × count: %d 分 = %.2f 元\n", total, float64(total)/100) + diff := total - ar.TotalCents + fmt.Printf(" 差额: %d 分 = %.2f 元\n", diff, float64(diff)/100) + if diff > 0 { + fmt.Printf(" 说明: 用户享受了 %.2f 元优惠\n", float64(diff)/100) + } + + // 8. 打印未匹配订单示例 + if unmatched > 0 { + fmt.Printf("\n=== 未匹配 remark 示例 (共 %d 笔) ===\n", unmatched) + shown := 0 + for _, item := range items { + if item.caseType == 0 && shown < 5 { + for _, r := range rows { + if r.ID == item.orderID { + rmk := r.Remark + if len(rmk) > 80 { + rmk = rmk[:80] + "..." + } + fmt.Printf(" ID=%-6d type=%d remark=[%s]\n", r.ID, r.SourceType, rmk) + shown++ + break + } + } + } + } + } +} diff --git a/cmd/debug_check_coupon_22/main.go b/cmd/debug_check_coupon_22/main.go index df03844..d0686b9 100755 --- a/cmd/debug_check_coupon_22/main.go +++ b/cmd/debug_check_coupon_22/main.go @@ -1,12 +1,13 @@ package main import ( - "bindbox-game/configs" - "bindbox-game/internal/repository/mysql" - "bindbox-game/internal/repository/mysql/dao" "context" "flag" "fmt" + + "bindbox-game/configs" + "bindbox-game/internal/repository/mysql" + "bindbox-game/internal/repository/mysql/dao" ) func main() { diff --git a/cmd/douyin_sync_debug/main.go b/cmd/douyin_sync_debug/main.go index 6c5bb42..ce1b698 100644 --- a/cmd/douyin_sync_debug/main.go +++ b/cmd/douyin_sync_debug/main.go @@ -64,7 +64,7 @@ func main() { env.Active() // 初始化 env flag(依赖已有的全局 -env/ACTIVE_ENV 配置) configs.Init() - cookie := "s_v_web_id=verify_mm0pjkt7_rRCYDU7B_F5Yl_4UYj_8yQ0_ue0vAcKwYt3z; csrf_session_id=86df5285aa04dec74fe5ac89d1b0d5c0; passport_csrf_token=fe2b51efeb70763190b402f49ad9f0e9; passport_csrf_token_default=fe2b51efeb70763190b402f49ad9f0e9; x-web-secsdk-uid=749f802e-47b8-4221-98af-b726e5631036; Hm_lvt_b6520b076191ab4b36812da4c90f7a5e=1771943727; HMACCOUNT=74DD13C46DE836FC; ttcid=c1ec90610ace481ba60dd8303b332c8e40; odin_tt=f01a3108f23b93c70d9d41eb2536553aa6550eb007cfc2bc3ba6319e82ad90eef45e1f581adbb6d1fb1fc2bdcce6d8cdd72b475fa9943bab1df0efe1ea035355; passport_auth_status=07988630820adb6946c4969658ab8b4d%2C; passport_auth_status_ss=07988630820adb6946c4969658ab8b4d%2C; uid_tt=f8cbc1387650f5a331a9a2943293d5f2; uid_tt_ss=f8cbc1387650f5a331a9a2943293d5f2; sid_tt=d1e48959ea47e34970be1d9b0aa801a2; sessionid=d1e48959ea47e34970be1d9b0aa801a2; sessionid_ss=d1e48959ea47e34970be1d9b0aa801a2; is_staff_user=false; PHPSESSID=294f6cf83ec2a4fefc1222321590b3e7; PHPSESSID_SS=294f6cf83ec2a4fefc1222321590b3e7; ucas_c0=CkEKBTEuMC4wEKOIj97Q5-3OaRjmJiD61rDnqc2DBCiwITCb1oDYuM3aB0DL7vbMBkjLorPPBlC_vL6Ekt3t1GdYbhIUPQw_elr7IsxkcNFj3v1rRHn03qs; ucas_c0_ss=CkEKBTEuMC4wEKOIj97Q5-3OaRjmJiD61rDnqc2DBCiwITCb1oDYuM3aB0DL7vbMBkjLorPPBlC_vL6Ekt3t1GdYbhIUPQw_elr7IsxkcNFj3v1rRHn03qs; ecom_gray_shop_id=156231010; sid_guard=d1e48959ea47e34970be1d9b0aa801a2%7C1772108860%7C5184000%7CMon%2C+27-Apr-2026+12%3A27%3A40+GMT; session_tlb_tag=sttt%7C9%7C0eSJWepH40lwvh2bCqgBov_________yZl6I8Equ6hjlsXft1nWEmcwpzFQYKIutCIRtYdHffp0%3D; sid_ucp_v1=1.0.0-KDc4Mjc1ZjFkNTg4NjFkZWQwYjUzMTJmNWFjN2U4ZmM1NzYzODEwNTcKGQib1oDYuM3aBxC8-IDNBhiwISAMOAZA9AcaAmxmIiBkMWU0ODk1OWVhNDdlMzQ5NzBiZTFkOWIwYWE4MDFhMg; ssid_ucp_v1=1.0.0-KDc4Mjc1ZjFkNTg4NjFkZWQwYjUzMTJmNWFjN2U4ZmM1NzYzODEwNTcKGQib1oDYuM3aBxC8-IDNBhiwISAMOAZA9AcaAmxmIiBkMWU0ODk1OWVhNDdlMzQ5NzBiZTFkOWIwYWE4MDFhMg; COMPASS_LUOPAN_DT=session_7611148681122726154; BUYIN_SASID=SID2_7611148178737856777; gfkadpd=4272,23756; zsgw_business_data=%7B%22uuid%22%3A%2267d1b0e4-dca5-484d-997a-b70cb555e396%22%2C%22platform%22%3A%22pc%22%2C%22source%22%3A%22seo.google%22%7D; source=seo.google; Hm_lpvt_b6520b076191ab4b36812da4c90f7a5e=1772115439; ttwid=1%7C71OUHp7yB34JMc3dVW9XMZxKJfcmzgfSzG407fx6Gqo%7C1772115438%7C5c6372575550a6bddb3a4eb25fff2fdc9f0d0954e0ce795eb1eb15e121a9ca53; tt_scid=bAGxaUh7d5HftS77rQMVwbdERMWrYT63ZZMLaRlsZiLgbOweJMjw-1IEYQvEO1Qz836d; op_session=" + cookie := "passport_csrf_token=40ba4a1be914a9f167320ed28b8c93d7; passport_csrf_token_default=40ba4a1be914a9f167320ed28b8c93d7; is_staff_user=false; s_v_web_id=verify_mkf83bbo_zfQ3q1Gp_5irf_4OOI_9y4N_C253269yUIJy; SHOP_ID=156231010; PIGEON_CID=4339134776748827; __security_mc_1_s_sdk_crypt_sdk=db47f387-4d0b-bf21; bd_ticket_guard_client_web_domain=2; bd_ticket_guard_client_data=eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtcmVlLXB1YmxpYy1rZXkiOiJCTHVTREdkVFRHWUdNMVY3ZDZKS2M4V2FwWGJ1K3JVYmVqRThONTZoeTI4SUJXdmVxZjBLMS9GczE0dWx5RTVRd2d4cjdnaDd6SXdMZjlsWDkwOFZQQWs9IiwiYmQtdGlja2V0LWd1YXJkLXdlYi12ZXJzaW9uIjoyfQ%3D%3D; bd_ticket_guard_web_domain=3; gfkadpd=4272,23756; ecom_gray_shop_id=156231010; zsgw_business_data=%7B%22uuid%22%3A%226756720f-c380-4bda-ab81-3dd27ca08a2d%22%2C%22platform%22%3A%22pc%22%2C%22source%22%3A%22seo.baidu.069%22%7D; source=seo.baidu.069; Hm_lvt_b6520b076191ab4b36812da4c90f7a5e=1771350555,1772107597,1772794481,1773223394; HMACCOUNT=9C6B7571794A6624; csrf_session_id=8173f094b830570b2b64e98900924731; passport_mfa_token=CjcMUe8O6Zz52W9O1T3zlEkIxpWSHBCB4dHw9XBdiDU%2BIPU1pzwEXLpVjGth2W2nXGHC8OM6ffSmGkoKPAAAAAAAAAAAAABQK6uUDAbmPNiLgEkCaMWLdiWMpTEiK%2Fm1NGLpqOUmR4vBZtoNbJWrAhzjfim%2BBtfMlxCj6IsOGPax0WwgAiIBA8pTDDU%3D; Hm_lpvt_b6520b076191ab4b36812da4c90f7a5e=1773224382; ttwid=1%7CNnXcElGkMBE8UTpDOFYR5OfCUYkFjQaLyn1EagPBZgM%7C1773224307%7C18bc27eb78d0a5da332f8c3ec951f81229670377d82025fcb5e600e3766e367b; tt_scid=uSkT0B7AzW.AKqYpEsRrpTqtws.7fqp2P4-gBF1FyffuNMOl1AKuRvuymbUWzXRvcc00; odin_tt=6edadb78040b4604bed517fc3edef437495387c8a3bf60fa177788ff81dd88daaed661705eb0729801e665c086b098b263c3090fef72c26e872d2f3172f6e364; passport_auth_status=581a8676e64d918c69ee3930f4dacf8b%2C4bb14205ac4179b872cba76a97208a7e; passport_auth_status_ss=581a8676e64d918c69ee3930f4dacf8b%2C4bb14205ac4179b872cba76a97208a7e; bd_ticket_guard_server_data=eyJ0aWNrZXQiOiJoYXNoLk1SWGtrczRwYTZpWG91ODhuZENOT05idm9iSjI2SHlXOXRYN2JKNTdZMWM9IiwidHNfc2lnbiI6InRzLjIuMDg1MDhmMjljNWI2MjkzMjQ4ZTAwNGY0YjdiNjMwODI4ODk1YjFkZWQ1ZTRlYmFiZTc3NmYzZTUxYWJjZjZhNGM0ZmJlODdkMjMxOWNmMDUzMTg2MjRjZWRhMTQ5MTFjYTQwNmRlZGJlYmVkZGIyZTMwZmNlOGQ0ZmEwMjU3NWQiLCJjbGllbnRfY2VydCI6InB1Yi5CTHVTREdkVFRHWUdNMVY3ZDZKS2M4V2FwWGJ1K3JVYmVqRThONTZoeTI4SUJXdmVxZjBLMS9GczE0dWx5RTVRd2d4cjdnaDd6SXdMZjlsWDkwOFZQQWs9IiwibG9nX2lkIjoiMjAyNjAzMTExODE4NDBGQUVGNkZGMDBCMkUwQTJEQTU2QSIsImNyZWF0ZV90aW1lIjoxNzczMjI0MzIwfQ%3D%3D; uid_tt=e8ca5ad2e6032b72a0fd8c0843ff5e9b; uid_tt_ss=e8ca5ad2e6032b72a0fd8c0843ff5e9b; sid_tt=c1a29f1f0f71ea4ed9fbcde60bc2b390; sessionid=c1a29f1f0f71ea4ed9fbcde60bc2b390; sessionid_ss=c1a29f1f0f71ea4ed9fbcde60bc2b390; PHPSESSID=05ca4c3439dacd9ac5f1d86a78516abb; PHPSESSID_SS=05ca4c3439dacd9ac5f1d86a78516abb; ucas_c0=CkEKBTEuMC4wELaIgqLTr9DYaRjmJiD61rDnqc2DBCiwITCb1oDYuM3aB0CCg8XNBkiCt4HQBlC_vL6Ekt3t1GdYbhIUI1wJXqAsE71YWUNwS6OvJ9dOEVE; ucas_c0_ss=CkEKBTEuMC4wELaIgqLTr9DYaRjmJiD61rDnqc2DBCiwITCb1oDYuM3aB0CCg8XNBkiCt4HQBlC_vL6Ekt3t1GdYbhIUI1wJXqAsE71YWUNwS6OvJ9dOEVE; sid_guard=c1a29f1f0f71ea4ed9fbcde60bc2b390%7C1773224328%7C5184000%7CSun%2C+10-May-2026+10%3A18%3A48+GMT; sid_ucp_v1=1.0.0-KDA3MGQyMjJkNmQ1NDUxOGQ1MWRhYTFjMzBkZTZkMDBlMTNlYWJhYWUKGwib1oDYuM3aBxCIg8XNBhiwISAMOAZA9AdIBBoCaGwiIGMxYTI5ZjFmMGY3MWVhNGVkOWZiY2RlNjBiYzJiMzkw; ssid_ucp_v1=1.0.0-KDA3MGQyMjJkNmQ1NDUxOGQ1MWRhYTFjMzBkZTZkMDBlMTNlYWJhYWUKGwib1oDYuM3aBxCIg8XNBhiwISAMOAZA9AdIBBoCaGwiIGMxYTI5ZjFmMGY3MWVhNGVkOWZiY2RlNjBiYzJiMzkw; session_tlb_tag=sttt%7C17%7CwaKfHw9x6k7Z-83mC8KzkP________-tSxexYwusSRjOrIMuB3YiA6EaLnfr1fbbR8LfwAsAu74%3D; BUYIN_SASID=SID2_7615938059562205474; COMPASS_LUOPAN_DT=session_7615939876688511241" if cookie == "" { fmt.Println("请通过环境变量 DOUYIN_COOKIE 提供抖店 Cookie") os.Exit(1) diff --git a/cmd/exploit_verify/main.go b/cmd/exploit_verify/main.go new file mode 100644 index 0000000..e26adad --- /dev/null +++ b/cmd/exploit_verify/main.go @@ -0,0 +1,219 @@ +package main + +import ( + "database/sql" + "fmt" + "os" + "strings" + "time" + + _ "github.com/go-sql-driver/mysql" +) + +func main() { + dsn := "root:bindbox2025kdy@tcp(150.158.78.154:3306)/dev_game?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai" + db, err := sql.Open("mysql", dsn) + if err != nil { + fmt.Println("连接失败:", err) + os.Exit(1) + } + defer db.Close() + fmt.Println("✅ 数据库连接成功\n") + + // ============ 1. 全局汇总 ============ + fmt.Println("【1】全局汇总") + var userCount, itemCount int64 + var totalYuan float64 + db.QueryRow(` + SELECT COUNT(DISTINCT t.to_user_id), COUNT(DISTINCT t.inventory_id), IFNULL(SUM(i.value_cents)/100.0, 0) + FROM user_inventory_transfers t + JOIN user_inventory i ON i.id = t.inventory_id + WHERE i.remark LIKE '%redeemed%' + `).Scan(&userCount, &itemCount, &totalYuan) + fmt.Printf(" 涉及用户: %d | 涉及资产: %d | 总薅取金额: %.2f 元\n\n", userCount, itemCount, totalYuan) + + // ============ 2. 按用户汇总 ============ + fmt.Println("【2】按用户汇总薅取金额") + fmt.Println(strings.Repeat("-", 95)) + fmt.Printf(" %-8s %-16s %-15s %-10s %-12s %-12s %s\n", + "用户ID", "昵称", "手机号", "兑换资产数", "薅取金额(元)", "当前余额", "可扣回?") + fmt.Println(" " + strings.Repeat("-", 90)) + + rows2, _ := db.Query(` + SELECT + sub.user_id, + IFNULL(u.nickname, '') AS nickname, + IFNULL(u.mobile, '') AS mobile, + sub.redeem_count, + sub.total_yuan, + IFNULL(pts.balance, 0) AS balance + FROM ( + SELECT t.to_user_id AS user_id, + COUNT(DISTINCT t.inventory_id) AS redeem_count, + SUM(i.value_cents) / 100.0 AS total_yuan, + SUM(i.value_cents) AS total_cents + FROM user_inventory_transfers t + JOIN user_inventory i ON i.id = t.inventory_id + WHERE i.remark LIKE '%redeemed%' + GROUP BY t.to_user_id + ) sub + LEFT JOIN users u ON u.id = sub.user_id + LEFT JOIN (SELECT user_id, SUM(points) AS balance FROM user_points GROUP BY user_id) pts ON pts.user_id = sub.user_id + ORDER BY sub.total_yuan DESC + `) + if rows2 != nil { + defer rows2.Close() + for rows2.Next() { + var uid, redeemCnt, balance int64 + var totalY float64 + var nick, mobile string + rows2.Scan(&uid, &nick, &mobile, &redeemCnt, &totalY, &balance) + canDeduct := "✅ 可全额" + exploitCents := int64(totalY * 100) + if balance < exploitCents { + canDeduct = fmt.Sprintf("⚠️ 仅可扣%d", balance) + } + fmt.Printf(" %-8d %-16s %-15s %-10d %-12.2f %-12d %s\n", + uid, nick, mobile, redeemCnt, totalY, balance, canDeduct) + } + } + + // ============ 3. 并发漏洞证据 ============ + fmt.Println("\n【3】并发漏洞证据 — 同一资产被多次转赠") + fmt.Println(strings.Repeat("-", 100)) + rows3, _ := db.Query(` + SELECT t.inventory_id, COUNT(*) AS cnt, + GROUP_CONCAT(CONCAT(t.from_user_id,'→',t.to_user_id) ORDER BY t.created_at SEPARATOR ' | ') AS path, + i.value_cents, IFNULL(p.name,'') AS pname + FROM user_inventory_transfers t + JOIN user_inventory i ON i.id = t.inventory_id + LEFT JOIN products p ON p.id = i.product_id + GROUP BY t.inventory_id, i.value_cents, p.name + HAVING COUNT(*) > 1 + ORDER BY cnt DESC, i.value_cents DESC + `) + if rows3 != nil { + defer rows3.Close() + fmt.Printf(" %-10s %-6s %-10s %-28s %s\n", "资产ID", "次数", "价值(元)", "商品", "转赠路径") + fmt.Println(" " + strings.Repeat("-", 95)) + for rows3.Next() { + var invID, cnt, vc int64 + var path, pname string + rows3.Scan(&invID, &cnt, &path, &vc, &pname) + if len([]rune(pname)) > 14 { + pname = string([]rune(pname)[:14]) + ".." + } + fmt.Printf(" %-10d %-6d %-10.2f %-28s %s\n", invID, cnt, float64(vc)/100.0, pname, path) + } + } + + // ============ 4. 转赠关系网络 Top15 ============ + fmt.Println("\n【4】转赠关系网络 Top15") + fmt.Println(strings.Repeat("-", 110)) + rows4, _ := db.Query(` + SELECT t.from_user_id, IFNULL(fu.nickname,'') AS fn, + t.to_user_id, IFNULL(tu.nickname,'') AS tn, + COUNT(*) AS xfer_cnt, COUNT(DISTINCT t.inventory_id) AS item_cnt, + SUM(i.value_cents)/100.0 AS total_yuan, + MIN(t.created_at) AS first_t, MAX(t.created_at) AS last_t + FROM user_inventory_transfers t + JOIN user_inventory i ON i.id = t.inventory_id + LEFT JOIN users fu ON fu.id = t.from_user_id + LEFT JOIN users tu ON tu.id = t.to_user_id + GROUP BY t.from_user_id, fu.nickname, t.to_user_id, tu.nickname + ORDER BY total_yuan DESC LIMIT 15 + `) + if rows4 != nil { + defer rows4.Close() + fmt.Printf(" %-20s → %-20s %-6s %-6s %-12s %-12s %-12s\n", + "赠送方", "接收方", "转赠次", "资产数", "金额(元)", "首次", "末次") + fmt.Println(" " + strings.Repeat("-", 105)) + for rows4.Next() { + var fuid, tuid, xcnt, icnt int64 + var yuan float64 + var fn, tn string + var ft, lt time.Time + rows4.Scan(&fuid, &fn, &tuid, &tn, &xcnt, &icnt, &yuan, &ft, <) + from := fmt.Sprintf("%d(%s)", fuid, truncStr(fn, 6)) + to := fmt.Sprintf("%d(%s)", tuid, truncStr(tn, 6)) + fmt.Printf(" %-20s → %-20s %-6d %-6d %-12.2f %-12s %-12s\n", + from, to, xcnt, icnt, yuan, + ft.Format("01-02 15:04"), lt.Format("01-02 15:04")) + } + } + + // ============ 5. 典型利用链路样本(前10条) ============ + fmt.Println("\n【5】典型利用链路样本(转赠→取消发货→兑换积分)") + fmt.Println(strings.Repeat("-", 130)) + rows5, _ := db.Query(` + SELECT i.id, i.user_id, IFNULL(u.nickname,'') AS nick, + i.value_cents, i.status, i.remark + FROM user_inventory i + LEFT JOIN users u ON u.id = i.user_id + WHERE i.remark LIKE '%transferred_from_%' + AND i.remark LIKE '%shipping_cancelled%' + AND i.remark LIKE '%redeemed%' + ORDER BY i.value_cents DESC + LIMIT 10 + `) + if rows5 != nil { + defer rows5.Close() + fmt.Printf(" %-8s %-8s %-14s %-10s %-6s %s\n", "资产ID", "用户ID", "昵称", "价值(元)", "状态", "操作链路") + fmt.Println(" " + strings.Repeat("-", 125)) + for rows5.Next() { + var id, uid, vc int64 + var status int32 + var nick, remark string + rows5.Scan(&id, &uid, &nick, &vc, &status, &remark) + fmt.Printf(" %-8d %-8d %-14s %-10.2f %-6s %s\n", + id, uid, truncStr(nick, 12), float64(vc)/100.0, + statusText(status), parseActions(remark)) + } + } + + fmt.Println("\n✅ 核对完毕") +} + +func truncStr(s string, maxRunes int) string { + runes := []rune(s) + if len(runes) > maxRunes { + return string(runes[:maxRunes]) + ".." + } + return s +} + +func parseActions(remark string) string { + parts := strings.Split(remark, "|") + actions := make([]string, 0, len(parts)) + for _, p := range parts { + p = strings.TrimSpace(p) + if p == "" { + continue + } + if strings.HasPrefix(p, "transferred_from_") { + actions = append(actions, "转赠") + } else if p == "shipping_requested" { + actions = append(actions, "发货") + } else if strings.HasPrefix(p, "shipping_cancelled") { + actions = append(actions, "取消发货") + } else if strings.Contains(p, "redeemed") { + actions = append(actions, "✖兑换积分") + } else { + actions = append(actions, p) + } + } + return strings.Join(actions, " → ") +} + +func statusText(s int32) string { + switch s { + case 1: + return "持有" + case 2: + return "作废" + case 3: + return "已用" + default: + return fmt.Sprintf("%d", s) + } +} diff --git a/docs/赠送资产漏洞核查报告.md b/docs/赠送资产漏洞核查报告.md new file mode 100644 index 0000000..b84026d --- /dev/null +++ b/docs/赠送资产漏洞核查报告.md @@ -0,0 +1,162 @@ +# 赠送资产漏洞核查报告 + +> 数据源: dev_game 数据库 | 核查日期: 2026-03-11 + +--- + +## 一、结论摘要 + +| 项目 | 结论 | +|------|------| +| 并发漏洞 | **确实存在**,已修复(SELECT FOR UPDATE + RowsAffected 检查) | +| 实际货物损失(一份发两份) | **0 元** — 18 个重复发货资产中,没有一个真正被两方都发了货 | +| 积分重复兑换 | **0 元** — 没有任何资产被多人兑换积分,也没有同一资产被兑换多次 | +| 发送方转赠后又兑换同一资产 | **0 笔** — 发送方没有在转赠后兑换过同一资产 | +| 转赠后接收方兑换积分 | 91 笔 / 12,088.80 元 — **合法行为**,资产转赠后归接收方所有 | + +**总实际损失: 0 元** + +--- + +## 二、漏洞技术分析 + +### 2.1 Bug 描述 + +文件: `internal/service/user/address_share.go` + +| Bug | 位置 | 描述 | 后果 | +|-----|------|------|------| +| readDB 竞态 | 原 L116-133 | 反重复检查使用从库(readDB),主从延迟 10-100ms 内并发请求可绕过 | 同一资产产生重复转赠记录和重复发货记录 | +| RowsAffected 未检查 | 原 L181-189 | `Updates()` 返回 0 行影响时不报错,后续操作继续执行 | 资产状态未变但发货记录已创建 | + +### 2.2 修复方案(已合并 zuncle 分支) + +| 修复 | 方式 | +|------|------| +| 竞态条件 | 在事务内使用 `SELECT FOR UPDATE` 锁行 + 写库查询发货记录 | +| RowsAffected | 转赠和原主发货两个分支都检查 `result.RowsAffected == 0` 后回滚 | + +### 2.3 关于"转赠资产禁止兑换积分" + +zuncle 分支原本还包含第三个修复:禁止通过转赠获得的资产兑换积分。经核实,**这是业务策略而非修漏洞**: +- 资产转赠后归接收方所有,接收方有权决定发货或兑换积分 +- 发送方没有对已转赠的资产做任何兑换操作 +- 不存在"一份资产两边都兑换"的情况 + +**已回退此限制。** + +--- + +## 三、数据核查 + +### 3.1 全局概况 + +| 指标 | 数值 | +|------|------| +| 总转赠记录数 | 157 条 | +| 涉及资产数 | 135 个 | +| 涉及用户数 | 15 人 | +| 多次转赠资产 | 13 个(并发 bug 导致的重复记录) | +| 两方都产生发货记录的资产 | 18 个 | + +### 3.2 重复发货资产明细(18 个) + +> 同一资产在发送方和接收方名下都产生了发货记录 + +#### 仅接收方有效发货,发送方全取消(8 个)— 无损失 + +| 资产ID | 价值(元) | 发送方 | 接收方 | 接收方状态 | 发送方状态 | 兑换积分 | +|--------|---------|--------|--------|-----------|-----------|---------| +| 49426 | 1,315.00 | 9248(不出last退了) | 9116(非洲人) | 有效1/取消1 | 全取消(2) | 否 | +| 47096 | 900.00 | 9305(新人) | 9116(非洲人) | 有效1/取消1 | 全取消(3) | 否 | +| 51038 | 605.00 | 9210(非酋) | 9116(非洲人) | 有效1/取消3 | 全取消(1) | 否 | +| 44153 | 375.00 | 9248(不出last退了) | 9116(非洲人) | 有效1/取消2 | 全取消(1) | 否 | +| 44152 | 375.00 | 9248(不出last退了) | 9116(非洲人) | 有效1/取消4 | 全取消(1) | 否 | +| 44151 | 375.00 | 9248(不出last退了) | 9116(非洲人) | 有效1/取消2 | 全取消(1) | 否 | +| 44150 | 375.00 | 9248(不出last退了) | 9116(非洲人) | 有效1/取消2 | 全取消(1) | 否 | + +小计: 4,320 元,全部仅接收方 9116 有效发货,**无实际损失**。 + +#### 双方全取消(10 个)— 无损失 + +| 资产ID | 价值(元) | 发送方 | 接收方 | 接收方兑换积分 | +|--------|---------|--------|--------|------------| +| 42746 | 375.00 | 9336(有冰的帝君) | 9116(非洲人) | 是 | +| 42757 | 375.00 | 9336(有冰的帝君) | 9116(非洲人) | 是 | +| 42758 | 375.00 | 9336(有冰的帝君) | 9116(非洲人) | 是 | +| 43304 | 375.00 | 9305(新人) | 9116(非洲人) | 是 | +| 42761 | 375.00 | 9116(非洲人) | 9305(新人) | 是 | +| 46445 | 375.00 | 9230(巨欧小肥龙) | 9116(非洲人) | 是 | +| 46446 | 375.00 | 9230(巨欧小肥龙) | 9116(非洲人) | 是 | +| 46447 | 375.00 | 9230(巨欧小肥龙) | 9116(非洲人) | 是 | +| 46506 | 375.00 | 9209(程c) | 9116(非洲人) | 是 | +| 46507 | 375.00 | 9209(程c) | 9116(非洲人) | 是 | +| 52338 | 12.50 | 9094(范巴斯滕) | 9449(古利特) | 是 | + +小计: 双方发货全取消,接收方后续兑换了积分 — 这属于**接收方对自有资产的合法操作**。 + +### 3.3 积分兑换核查 + +| 核查项 | 结果 | +|--------|------| +| 同一资产被多个用户兑换积分 | **0 个** | +| 同一资产被同一用户多次兑换积分 | **0 个** | +| 发送方在 points_ledger 中兑换已转赠资产 | **0 笔** | +| 发送方的已转赠资产 remark 含 redeemed | **1 笔**(用户 9116 转出给 9305 的资产 43304,后又转回 9116 兑换,属于正常来回转赠) | + +### 3.4 转赠后接收方兑换积分明细 + +> 以下为合法行为,资产转赠后归接收方所有,接收方有权兑换 + +| 用户ID | 昵称 | 兑换笔数 | 兑换金额(元) | 性质 | +|--------|------|---------|------------|------| +| 9116 | 非洲人 | 30 | 10,737.00 | 合法 — 接收转赠后兑换 | +| 9110 | 极品官方内部号 | 24 | 446.60 | 合法 | +| 9305 | 新人 | 1 | 375.00 | 合法 | +| 9209 | 程c | 9 | 220.00 | 合法 | +| 9222 | 嗯?!!!! | 15 | 180.00 | 合法 | +| 9336 | 有冰的帝君 | 1 | 60.00 | 合法 | +| 9094 | 范巴斯滕救了一个美女 | 2 | 25.00 | 合法 | +| 9210 | 非酋 | 3 | 22.50 | 合法 | +| 9449 | 古利特使出了佛怒火莲 | 1 | 12.50 | 合法 | +| 9248 | 不出last退了 | 3 | 8.60 | 合法 | +| 9365 | 未命名 | 1 | 1.00 | 合法 | +| 9230 | 巨欧小肥龙 | 1 | 0.60 | 合法 | +| **合计** | | **91** | **12,088.80** | **全部合法** | + +### 3.5 多次转赠记录(并发 bug 产生的脏数据) + +| 资产ID | 转赠次数 | 路径 | 说明 | +|--------|---------|------|------| +| 42746 | 4 | 9336→9116 x4 | 同一操作并发触发4次 | +| 46668 | 4 | 9210→9116 x4 | 同上 | +| 43304 | 3 | 9305→9116, 9116→9305, 9305→9116 | 正常来回转赠 | +| 44152 | 3 | 9248→9116 x3 | 并发触发3次 | +| 46445 | 3 | 9230→9116 x3 | 并发触发3次 | +| 46667 | 3 | 9210→9116 x3 | 并发触发3次 | +| 51038 | 3 | 9210→9116, 9116→9210, 9210→9116 | 正常来回转赠 | +| 其他 6 个 | 2 | — | 并发触发2次 | + +这些重复记录是脏数据,但未造成资产复制或积分重复。 + +--- + +## 四、修复状态 + +| 修复项 | 状态 | Commit | +|--------|------|--------| +| SELECT FOR UPDATE 防并发转赠 | ✅ 已合并 main | `8229b41` | +| RowsAffected 检查防静默失败 | ✅ 已合并 main | `8229b41` | +| ~~禁止转赠资产兑换积分~~ | ❌ 已回退 | `749464c`,属业务策略非 bug 修复 | + +--- + +## 五、结论 + +1. **并发 bug 确实存在**:readDB 竞态 + RowsAffected 未检查,导致同一资产产生重复转赠记录和重复发货记录 +2. **实际经济损失为 0 元**: + - 没有任何资产被真正发了两份货(重复发货记录中,发送方一侧全部已取消) + - 没有任何资产被重复兑换积分 + - 发送方没有在转赠后又兑换同一资产的积分 +3. **转赠后兑换积分是合法行为**:资产转赠后归接收方,接收方有权兑换 +4. **Bug 已修复**,防止未来可能的真正损失 diff --git a/exploit_report_20260311.txt b/exploit_report_20260311.txt new file mode 100644 index 0000000..16f65c0 --- /dev/null +++ b/exploit_report_20260311.txt @@ -0,0 +1,1518 @@ +======================================================================================================================== + 赠送资产薅积分漏洞 — 完整链路明细报告 + 生成时间: 2026-03-11 16:10:40 +======================================================================================================================== + +【一】涉及用户信息 +---------------------------------------------------------------------------------------------------- + UID 昵称 手机号 状态 注册时间 + ------------------------------------------------------------------------------------------ + 9094 范巴斯滕救了一个美女 19382285440 正常 2026-01-19 07:13 + 9110 极品官方内部号 19105657735 正常 2026-01-22 04:10 + 9111 嘟嘟 18754515699 正常 2026-01-22 23:10 + 9113 盖德·穆勒大力出奇迹 19164685626 正常 2026-01-23 03:13 + 9116 非洲人 17381086073 正常 2026-01-23 04:54 + 9117 小叶 18783929192 正常 2026-01-23 04:55 + 9138 卷卷大魔王 15021159954 正常 2026-01-31 01:19 + 9160 肖申克在武汉看电影 17331603630 正常 2026-02-03 14:59 + 9199 欣喜的罗马里奥 18852772482 正常 2026-02-15 00:54 + 9209 程c 13979106467 正常 2026-02-16 00:24 + 9210 非酋 17303115993 正常 2026-02-16 00:26 + 9222 嗯?!!!! 19275742612 正常 2026-02-16 11:14 + 9230 巨欧小肥龙 15688032220 正常 2026-02-16 12:38 + 9248 不出last退了 19874056809 正常 2026-02-16 22:27 + 9280 诚心的肯尼 15243958112 正常 2026-02-17 02:04 + 9305 新人 15200690367 正常 2026-02-17 20:21 + 9330 拼搏的内马尔 13771103216 正常 2026-02-18 02:49 + 9336 有冰的帝君 17708495187 正常 2026-02-18 21:40 + 9347 约翰在武汉看电影 19527539116 正常 2026-02-19 00:34 + 9359 雅典娜心花怒放 13829254006 正常 2026-02-19 17:40 + 9365 未命名 18306540135 正常 2026-02-19 22:34 + 9446 弗兰科望穿秋水 15957130050 正常 2026-03-03 10:25 + 9449 古利特使出了佛怒火莲 18439413431 正常 2026-03-04 01:05 + + +【二】完整转赠链路明细(按时间排序) +---------------------------------------------------------------------------------------------------------------------------------------------------------------- + # 转赠时间 赠送方 接收方 资产ID 商品名称 价值(元) 状态 后续操作链路 + ----------------------------------------------------------------------------------------------------------------------------------------------------------- + 1 01-24 22:59:58 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30255 万代 HG GN-000 g.. 70.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 2 01-24 23:00:47 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30233 捏捏 1.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 3 01-24 23:01:29 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30241 SD随机款 40.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 4 01-24 23:07:27 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30256 模型改色马克笔7支 23.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 5 01-24 23:07:55 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30257 捏捏 1.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 6 01-24 23:21:20 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30264 麦当当 10.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 7 01-25 04:17:53 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30289 SD随机款 40.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 8 01-25 04:18:10 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30290 按压式金属丙烯马克笔1支 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 9 01-25 04:21:58 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30302 心动的王者假面绮宴主题马口铁.. 20.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 10 01-25 04:22:03 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30301 晨光水性笔 2.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 11 01-25 19:16:23 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30316 万代 HG 1/144 00.. 78.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 12 01-25 19:16:32 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 30317 小米巨能写中性笔0.5mm(.. 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 13 02-04 01:01:03 9110(极品官方内部号) 9160(肖申克在武汉看电..) 31938 摩动核 赤热 拼装幻赐 超可.. 175.00 已用 shipping_requested_via_share + 14 02-16 21:23:06 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 34111 晨光水性笔 2.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 15 02-16 21:23:12 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 34110 吧唧1个 1.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 16 02-16 21:23:16 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 34109 HG 风灵高达 88.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 17 02-17 11:38:39 9222(嗯?!!!!) 9116(非洲人) 37377 PG天使飞翼零式高达Wing.. 580.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → ✖兑换积分 + 18 02-17 23:37:14 9280(诚心的肯尼) 9222(嗯?!!!!) 40389 柒匠 模型水性渗线液 高达模.. 15.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 19 02-17 23:37:47 9280(诚心的肯尼) 9222(嗯?!!!!) 40388 晨光水性笔 2.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 20 02-17 23:38:01 9280(诚心的肯尼) 9222(嗯?!!!!) 40390 PP夹1个 1.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 21 02-17 23:39:50 9280(诚心的肯尼) 9222(嗯?!!!!) 40391 模型改色马克笔7支 23.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 22 02-17 23:39:59 9280(诚心的肯尼) 9222(嗯?!!!!) 40393 柒匠 模型水性渗线液 高达模.. 15.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 23 02-17 23:40:06 9280(诚心的肯尼) 9222(嗯?!!!!) 40394 冰箱贴 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 24 02-17 23:40:12 9280(诚心的肯尼) 9222(嗯?!!!!) 40395 晨光水性笔 2.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 25 02-17 23:41:11 9280(诚心的肯尼) 9222(嗯?!!!!) 40392 模型改色马克笔7支 23.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 26 02-17 23:52:34 9110(极品官方内部号) 9222(嗯?!!!!) 40417 木质拼装模型积木蝴蝶折叠爪刀 6.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 27 02-17 23:52:39 9110(极品官方内部号) 9222(嗯?!!!!) 40416 麦当当 10.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 28 02-17 23:53:59 9209(程c) 9110(极品官方内部号) 40418 维达手帕纸1包 0.60 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 29 02-19 01:12:12 9347(约翰在武汉看电影) 9222(嗯?!!!!) 43226 尖头镊子1个 5.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 30 02-19 01:12:18 9347(约翰在武汉看电影) 9222(嗯?!!!!) 43225 高达电动小型打磨机器 16.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 31 02-19 01:12:23 9347(约翰在武汉看电影) 9222(嗯?!!!!) 43224 柒匠 模型水性渗线液 高达模.. 15.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 32 02-19 01:38:48 9347(约翰在武汉看电影) 9222(嗯?!!!!) 43291 万代 机动战士高达联名UVP.. 40.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 33 02-19 01:38:53 9347(约翰在武汉看电影) 9222(嗯?!!!!) 43292 冰箱贴 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 34 02-19 02:18:17 9305(新人) 9116(非洲人) 43304 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 35 02-19 02:24:28 9116(非洲人) 9305(新人) 43304 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 36 02-19 02:40:18 9305(新人) 9116(非洲人) 43304 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 37 02-19 02:53:03 9110(极品官方内部号) 9116(非洲人) 43423 HG 熊霸3 BEARGGU.. 79.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 38 02-19 13:26:50 9116(非洲人) 9248(不出last退了) 44155 冰箱贴 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 39 02-19 13:27:16 9116(非洲人) 9248(不出last退了) 44154 尖头镊子1个 5.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 40 02-20 22:13:58 9330(拼搏的内马尔) 9094(范巴斯滕救了一个..) 45366 三丽鸥保温杯公仔冰霸杯 88.90 已用 转赠 → 发货 + 41 02-20 23:37:47 9110(极品官方内部号) 9365(未命名) 45408 捏捏 1.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 42 02-21 00:21:53 9209(程c) 9230(巨欧小肥龙) 45587 维达手帕纸1包 0.60 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 43 02-21 01:16:44 9116(非洲人) 9305(新人) 42761 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 44 02-21 14:53:09 9336(有冰的帝君) 9116(非洲人) 42746 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → 取消发货 → ✖兑换积分 + 45 02-21 14:54:33 9336(有冰的帝君) 9116(非洲人) 42746 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → 取消发货 → ✖兑换积分 + 46 02-21 14:54:56 9336(有冰的帝君) 9116(非洲人) 42746 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → 取消发货 → ✖兑换积分 + 47 02-21 14:55:34 9336(有冰的帝君) 9116(非洲人) 42746 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → 取消发货 → ✖兑换积分 + 48 02-21 14:58:16 9336(有冰的帝君) 9116(非洲人) 42757 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 49 02-21 15:00:46 9336(有冰的帝君) 9116(非洲人) 42758 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 50 02-21 15:04:28 9222(嗯?!!!!) 9116(非洲人) 45097 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 51 02-21 15:04:43 9222(嗯?!!!!) 9116(非洲人) 45098 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 52 02-21 15:05:04 9222(嗯?!!!!) 9116(非洲人) 45099 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 53 02-21 15:07:44 9222(嗯?!!!!) 9116(非洲人) 45100 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 54 02-21 15:09:10 9222(嗯?!!!!) 9116(非洲人) 46493 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 55 02-21 15:11:10 9222(嗯?!!!!) 9116(非洲人) 46494 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 56 02-21 15:12:36 9222(嗯?!!!!) 9116(非洲人) 46495 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 57 02-21 15:12:54 9222(嗯?!!!!) 9116(非洲人) 44285 万代 魂限 GFFMC PH.. 1450.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 58 02-21 16:43:08 9230(巨欧小肥龙) 9116(非洲人) 46445 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → ✖兑换积分 + 59 02-21 16:43:17 9230(巨欧小肥龙) 9116(非洲人) 46445 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → ✖兑换积分 + 60 02-21 16:43:37 9230(巨欧小肥龙) 9116(非洲人) 46445 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → ✖兑换积分 + 61 02-21 16:44:28 9230(巨欧小肥龙) 9116(非洲人) 46446 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 62 02-21 16:47:04 9230(巨欧小肥龙) 9116(非洲人) 46447 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 63 02-21 16:49:56 9209(程c) 9116(非洲人) 46506 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → ✖兑换积分 + 64 02-21 16:50:09 9209(程c) 9116(非洲人) 46506 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → ✖兑换积分 + 65 02-21 16:50:58 9209(程c) 9116(非洲人) 46507 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 66 02-21 16:51:25 9209(程c) 9116(非洲人) 46508 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 67 02-21 16:52:06 9209(程c) 9116(非洲人) 46509 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 68 02-21 23:49:13 9210(非酋) 9116(非洲人) 44115 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 69 02-21 23:49:40 9210(非酋) 9116(非洲人) 46665 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 70 02-21 23:49:50 9210(非酋) 9116(非洲人) 46666 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 71 02-21 23:49:58 9210(非酋) 9116(非洲人) 46666 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 72 02-21 23:50:06 9210(非酋) 9116(非洲人) 46667 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 73 02-21 23:50:12 9210(非酋) 9116(非洲人) 46667 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 74 02-21 23:50:20 9210(非酋) 9116(非洲人) 46668 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 75 02-21 23:50:28 9210(非酋) 9116(非洲人) 46668 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 76 02-21 23:50:35 9210(非酋) 9116(非洲人) 46668 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 77 02-21 23:51:11 9210(非酋) 9116(非洲人) 46668 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 78 02-21 23:51:31 9210(非酋) 9116(非洲人) 46667 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 79 02-21 23:51:43 9210(非酋) 9116(非洲人) 46669 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 80 02-21 23:51:51 9210(非酋) 9116(非洲人) 46669 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 81 02-21 23:52:12 9210(非酋) 9116(非洲人) 46670 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 82 02-21 23:52:25 9210(非酋) 9116(非洲人) 46671 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 83 02-21 23:52:46 9210(非酋) 9116(非洲人) 46672 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 84 02-21 23:53:04 9210(非酋) 9116(非洲人) 46673 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 85 02-21 23:53:11 9210(非酋) 9116(非洲人) 46672 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 86 02-21 23:53:24 9210(非酋) 9116(非洲人) 46673 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 87 02-21 23:53:36 9210(非酋) 9116(非洲人) 46674 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 88 02-21 23:53:51 9210(非酋) 9116(非洲人) 46675 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 89 02-21 23:54:15 9210(非酋) 9116(非洲人) 46676 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 90 02-22 00:02:31 9210(非酋) 9116(非洲人) 46677 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 91 02-22 00:02:51 9210(非酋) 9116(非洲人) 46678 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 92 02-22 00:03:14 9210(非酋) 9116(非洲人) 46679 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 93 02-22 00:03:43 9210(非酋) 9116(非洲人) 46681 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 94 02-22 00:04:06 9210(非酋) 9116(非洲人) 46682 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 95 02-22 00:04:51 9210(非酋) 9116(非洲人) 46683 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 96 02-22 00:05:25 9210(非酋) 9116(非洲人) 46684 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 97 02-22 00:05:52 9210(非酋) 9116(非洲人) 46685 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 98 02-22 00:06:17 9210(非酋) 9116(非洲人) 46686 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 99 02-22 00:06:30 9210(非酋) 9116(非洲人) 46686 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 100 02-22 00:06:43 9210(非酋) 9116(非洲人) 46687 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 101 02-22 00:07:03 9210(非酋) 9116(非洲人) 46688 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 102 02-22 00:07:23 9210(非酋) 9116(非洲人) 46689 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 103 02-22 00:07:44 9210(非酋) 9116(非洲人) 46690 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 104 02-22 00:08:04 9210(非酋) 9116(非洲人) 46691 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 105 02-22 23:46:20 9248(不出last退了) 9116(非洲人) 44150 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 106 02-22 23:46:50 9248(不出last退了) 9116(非洲人) 44151 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 107 02-22 23:47:03 9248(不出last退了) 9116(非洲人) 44152 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 108 02-22 23:47:10 9248(不出last退了) 9116(非洲人) 44152 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 109 02-22 23:47:23 9248(不出last退了) 9116(非洲人) 44152 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → 取消发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 110 02-22 23:47:49 9248(不出last退了) 9116(非洲人) 44153 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 111 02-23 19:39:36 9116(非洲人) 9210(非酋) 47297 兰博基尼乐高积木益智拼装模型 12.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 112 02-23 20:17:10 9116(非洲人) 9210(非酋) 47299 木质拼装模型积木蝴蝶折叠爪刀 6.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 113 02-23 20:18:01 9210(非酋) 9116(非洲人) 47249 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 114 02-23 20:18:33 9210(非酋) 9116(非洲人) 47298 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 115 02-23 20:19:07 9210(非酋) 9116(非洲人) 47300 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 116 02-24 01:23:17 9336(有冰的帝君) 9110(极品官方内部号) 47721 藏道模型 创神天未 天未动漫 295.00 已用 转赠 → 发货 + 117 02-24 21:22:46 9209(程c) 9210(非酋) 47927 高达贴纸(10张) 4.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 118 02-25 18:06:37 9336(有冰的帝君) 9110(极品官方内部号) 48197 打磨工具五件套 15.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 119 02-25 18:08:55 9116(非洲人) 9110(极品官方内部号) 48198 得力NS755纽赛臻干净橡皮.. 2.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 120 02-25 18:28:25 9116(非洲人) 9110(极品官方内部号) 48203 冰箱贴 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 121 02-25 18:30:53 9116(非洲人) 9110(极品官方内部号) 48204 冰箱贴 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 122 02-25 18:32:59 9116(非洲人) 9110(极品官方内部号) 48206 晨光水性笔 2.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 123 02-26 03:02:50 9209(程c) 9110(极品官方内部号) 48309 晨光水性笔 2.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 124 02-26 16:02:33 9222(嗯?!!!!) 9347(约翰在武汉看电影) 48207 摩动核魔神四将魔礼青合金骨架 420.00 已用 转赠 → 发货 + 125 02-27 14:31:46 9116(非洲人) 9209(程c) 49166 王者旅行日记吊坠 21.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 126 02-27 17:51:23 9330(拼搏的内马尔) 9094(范巴斯滕救了一个..) 45343 兰博基尼乐高积木益智拼装模型 12.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 127 02-27 18:01:22 9330(拼搏的内马尔) 9094(范巴斯滕救了一个..) 49120 兰博基尼乐高积木益智拼装模型 12.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 128 02-28 01:25:41 9230(巨欧小肥龙) 9209(程c) 49296 聚匠模型柯西HG1/144国.. 120.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 129 02-28 01:25:48 9230(巨欧小肥龙) 9209(程c) 49295 高达贴纸(10张) 4.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 130 02-28 01:46:26 9210(非酋) 9209(程c) 49303 按压式金属丙烯马克笔1支 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 131 02-28 01:46:39 9210(非酋) 9209(程c) 49304 按压式金属丙烯马克笔1支 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 132 02-28 01:48:12 9210(非酋) 9209(程c) 49305 按压式金属丙烯马克笔1支 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 133 02-28 01:48:21 9210(非酋) 9209(程c) 49306 按压式金属丙烯马克笔1支 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 134 02-28 02:09:30 9110(极品官方内部号) 9209(程c) 49318 按压式金属丙烯马克笔1支 3.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 135 02-28 02:09:35 9110(极品官方内部号) 9209(程c) 49317 SD BB战士非凡强袭自由高.. 60.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 136 02-28 20:00:59 9336(有冰的帝君) 9110(极品官方内部号) 49508 万达PG菲尼克斯 NT 1/.. 3400.00 已用 转赠 → 发货 + 137 02-28 20:15:05 9305(新人) 9116(非洲人) 47096 万代PG 双O莱泽器 900.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 138 03-01 00:04:28 9248(不出last退了) 9116(非洲人) 49426 万代MB海盗X1高达拼凑型 .. 1315.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 139 03-02 12:52:23 9210(非酋) 9116(非洲人) 51038 PB限定 MG 红龙形态红龙.. 605.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 140 03-02 14:06:18 9116(非洲人) 9210(非酋) 51038 PB限定 MG 红龙形态红龙.. 605.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 141 03-02 23:11:37 9138(卷卷大魔王) 9116(非洲人) 51316 万代PG MK2奥古配色 .. 1430.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 142 03-02 23:11:47 9138(卷卷大魔王) 9116(非洲人) 51311 pgu1/60RX78元祖2.. 1700.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 143 03-03 00:04:28 9210(非酋) 9116(非洲人) 51038 PB限定 MG 红龙形态红龙.. 605.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 144 03-03 00:04:35 9210(非酋) 9116(非洲人) 51358 摩动核 拼装吕布 合金骨架拼.. 460.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested → 取消发货 → batch_shipping_requested + 145 03-03 00:42:07 9111(嘟嘟) 9336(有冰的帝君) 51418 SD BB战士非凡强袭自由高.. 60.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 146 03-03 02:32:09 9117(小叶) 9116(非洲人) 51590 PP夹1个 1.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 147 03-03 02:32:28 9117(小叶) 9116(非洲人) 51591 PP夹1个 1.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 148 03-03 11:03:13 9336(有冰的帝君) 9446(弗兰科望穿秋水) 52094 美佳龙MJL原装单刃水口钳龙.. 29.00 已用 转赠 → 发货 + 149 03-03 11:27:42 9111(嘟嘟) 9210(非酋) 52104 长龙再版7701黑强袭自由带.. 320.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 150 03-03 12:14:43 9359(雅典娜心花怒放) 9248(不出last退了) 48196 维达手帕纸1包 0.60 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 151 03-04 02:36:58 9336(有冰的帝君) 9116(非洲人) 52406 模寿 至臻 空呪 空况罗 国.. 700.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 152 03-04 22:34:54 9094(范巴斯滕救了一个..) 9449(古利特使出了佛怒..) 52338 兰博基尼乐高积木益智拼装模型 12.50 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 153 03-05 19:40:40 9199(欣喜的罗马里奥) 9116(非洲人) 46467 雷霆魔鬼鱼 无限新星 IN .. 308.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 154 03-05 19:40:49 9199(欣喜的罗马里奥) 9116(非洲人) 47719 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 155 03-05 19:41:23 9199(欣喜的罗马里奥) 9116(非洲人) 47720 MG 牛高达VER.KA 卡.. 375.00 已用 转赠 → 发货 → 取消发货 → batch_shipping_requested + 156 03-05 22:21:39 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 53361 绿扎古Q版-SDCS 18.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + 157 03-05 22:21:56 9113(盖德·穆勒大力出..) 9110(极品官方内部号) 53362 绿扎古Q版-SDCS 18.00 已用 转赠 → 发货 → 取消发货 → ✖兑换积分 + + 共 157 条转赠记录 + + +【三】转赠资产的发货/取消发货记录 +-------------------------------------------------------------------------------------------------------------------------------------------- + + +【四】薅取积分入账明细 +------------------------------------------------------------------------------------------------------------------------ + 流水ID 用户ID 昵称 入账积分 类型 关联信息 入账时间 + ------------------------------------------------------------------------------------------------------------------- + 5203 9094 范巴斯滕救了一个美女 3000 redeem_reward batch_redeem_8_items 01-28 23:57:11 + 5204 9094 范巴斯滕救了一个美女 500 redeem_reward batch_redeem_1_items 01-28 23:57:15 + 5358 9094 范巴斯滕救了一个美女 1800 redeem_reward batch_redeem_4_items 02-02 01:20:09 + 5553 9094 范巴斯滕救了一个美女 5800 redeem_reward batch_redeem_5_items 02-14 20:41:43 + 5612 9094 范巴斯滕救了一个美女 1700 redeem_reward batch_redeem_5_items 02-15 22:55:30 + 6743 9094 范巴斯滕救了一个美女 2300 redeem_reward batch_redeem_7_items 02-18 21:15:49 + 6745 9094 范巴斯滕救了一个美女 600 redeem_reward batch_redeem_2_items 02-18 21:32:50 + 7172 9094 范巴斯滕救了一个美女 800 redeem_reward batch_redeem_2_items 02-20 22:14:34 + 7604 9094 范巴斯滕救了一个美女 300 redeem_reward batch_redeem_1_items 02-22 02:45:33 + 7628 9094 范巴斯滕救了一个美女 2000 redeem_reward batch_redeem_6_items 02-22 20:19:58 + 7660 9094 范巴斯滕救了一个美女 300 redeem_reward batch_redeem_1_items 02-23 01:34:48 + 7749 9094 范巴斯滕救了一个美女 1100 redeem_reward batch_redeem_3_items 02-23 22:57:08 + 7751 9094 范巴斯滕救了一个美女 600 redeem_reward batch_redeem_2_items 02-23 23:37:14 + 7753 9094 范巴斯滕救了一个美女 300 redeem_reward batch_redeem_5_items 02-23 23:39:18 + 8004 9094 范巴斯滕救了一个美女 3500 redeem_reward batch_redeem_9_items 02-26 22:33:24 + 8049 9094 范巴斯滕救了一个美女 2500 redeem_reward batch_redeem_2_items 02-27 18:01:41 + 8070 9094 范巴斯滕救了一个美女 1100 redeem_reward batch_redeem_3_items 02-27 21:01:16 + 8192 9094 范巴斯滕救了一个美女 1000 redeem_reward batch_redeem_1_items 03-01 02:04:47 + 8193 9094 范巴斯滕救了一个美女 1000 redeem_reward batch_redeem_1_items 03-01 02:08:18 + 8240 9094 范巴斯滕救了一个美女 800 redeem_reward batch_redeem_2_items 03-01 22:05:34 + 8333 9094 范巴斯滕救了一个美女 900 redeem_reward batch_redeem_2_items 03-02 21:38:40 + 8488 9094 范巴斯滕救了一个美女 700 redeem_reward batch_redeem_2_items 03-03 02:47:03 + 8569 9094 范巴斯滕救了一个美女 1600 redeem_reward batch_redeem_3_items 03-04 01:28:18 + 8649 9094 范巴斯滕救了一个美女 2100 redeem_reward batch_redeem_2_items 03-04 20:27:33 + 8666 9094 范巴斯滕救了一个美女 1000 redeem_reward batch_redeem_1_items 03-04 21:18:10 + 8675 9094 范巴斯滕救了一个美女 600 redeem_reward batch_redeem_2_items 03-04 22:13:46 + 8763 9094 范巴斯滕救了一个美女 700 redeem_reward batch_redeem_2_items 03-05 01:37:49 + 8764 9094 范巴斯滕救了一个美女 300 redeem_reward batch_redeem_5_items 03-05 01:39:12 + 8812 9094 范巴斯滕救了一个美女 600 redeem_reward batch_redeem_2_items 03-05 20:57:11 + 8851 9094 范巴斯滕救了一个美女 3000 redeem_reward batch_redeem_8_items 03-06 00:05:23 + 8853 9094 范巴斯滕救了一个美女 960 redeem_reward batch_redeem_3_items 03-06 02:22:29 + 8854 9094 范巴斯滕救了一个美女 1400 redeem_reward batch_redeem_9_items 03-06 02:25:45 + 用户 9094 小计: 44860 积分 (448.60 元) + + 4945 9110 极品官方内部号 71700 redeem_reward batch_redeem_127_items 01-22 05:04:45 + 4946 9110 极品官方内部号 9600 redeem_reward batch_redeem_30_items 01-22 05:12:50 + 4947 9110 极品官方内部号 2900 redeem_reward batch_redeem_11_items 01-22 05:13:31 + 4948 9110 极品官方内部号 9100 redeem_reward batch_redeem_16_items 01-22 05:16:10 + 4949 9110 极品官方内部号 7200 redeem_reward batch_redeem_19_items 01-22 05:19:08 + 4950 9110 极品官方内部号 2150 redeem_reward batch_redeem_10_items 01-22 05:19:55 + 4951 9110 极品官方内部号 3600 redeem_reward batch_redeem_7_items 01-22 05:22:43 + 4952 9110 极品官方内部号 300 redeem_reward batch_redeem_3_items 01-22 05:27:40 + 4953 9110 极品官方内部号 5600 redeem_reward batch_redeem_40_items 01-22 05:28:33 + 4954 9110 极品官方内部号 8200 redeem_reward batch_redeem_30_items 01-22 05:29:27 + 4955 9110 极品官方内部号 3000 redeem_reward batch_redeem_1_items 01-22 05:33:05 + 4956 9110 极品官方内部号 2550 redeem_reward batch_redeem_10_items 01-22 05:34:39 + 4957 9110 极品官方内部号 6250 redeem_reward batch_redeem_10_items 01-22 05:35:19 + 4958 9110 极品官方内部号 3600 redeem_reward batch_redeem_28_items 01-22 05:37:08 + 4959 9110 极品官方内部号 2400 redeem_reward batch_redeem_4_items 01-22 05:39:23 + 4960 9110 极品官方内部号 5000 redeem_reward batch_redeem_1_items 01-22 05:40:35 + 4961 9110 极品官方内部号 2100 redeem_reward batch_redeem_1_items 01-22 05:42:10 + 4962 9110 极品官方内部号 1400 redeem_reward batch_redeem_2_items 01-22 05:54:54 + 4964 9110 极品官方内部号 1000 redeem_reward batch_redeem_1_items 01-22 09:20:46 + 4971 9110 极品官方内部号 150 redeem_reward batch_redeem_1_items 01-22 18:36:48 + 4974 9110 极品官方内部号 3200 redeem_reward batch_redeem_6_items 01-22 23:25:44 + 4975 9110 极品官方内部号 300 redeem_reward batch_redeem_1_items 01-22 23:26:37 + 4976 9110 极品官方内部号 2400 redeem_reward batch_redeem_10_items 01-22 23:27:11 + 4977 9110 极品官方内部号 3000 redeem_reward batch_redeem_1_items 01-23 00:41:43 + 4978 9110 极品官方内部号 6600 redeem_reward batch_redeem_5_items 01-23 00:46:52 + 4979 9110 极品官方内部号 5400 redeem_reward batch_redeem_5_items 01-23 01:07:11 + 4980 9110 极品官方内部号 700 redeem_reward batch_redeem_2_items 01-23 01:18:13 + 4982 9110 极品官方内部号 600 redeem_reward batch_redeem_2_items 01-23 01:20:05 + 4983 9110 极品官方内部号 16000 redeem_reward batch_redeem_1_items 01-23 01:21:54 + 4994 9110 极品官方内部号 500 redeem_reward batch_redeem_2_items 01-23 02:21:28 + 4995 9110 极品官方内部号 700 redeem_reward batch_redeem_2_items 01-23 02:25:08 + 4997 9110 极品官方内部号 2550 redeem_reward batch_redeem_5_items 01-23 04:03:10 + 4998 9110 极品官方内部号 500 redeem_reward batch_redeem_1_items 01-23 04:03:46 + 4999 9110 极品官方内部号 1000 redeem_reward batch_redeem_1_items 01-23 04:16:11 + 5000 9110 极品官方内部号 100 redeem_reward batch_redeem_1_items 01-23 04:18:08 + 5001 9110 极品官方内部号 100 redeem_reward batch_redeem_1_items 01-23 04:18:32 + 5002 9110 极品官方内部号 33000 redeem_reward batch_redeem_1_items 01-23 04:44:58 + 5003 9110 极品官方内部号 150 redeem_reward batch_redeem_1_items 01-23 04:45:17 + 5013 9110 极品官方内部号 600 redeem_reward batch_redeem_1_items 01-23 05:39:39 + 5014 9110 极品官方内部号 800 redeem_reward batch_redeem_1_items 01-23 05:43:44 + 5015 9110 极品官方内部号 2100 redeem_reward batch_redeem_1_items 01-23 05:45:05 + 5019 9110 极品官方内部号 800 redeem_reward batch_redeem_1_items 01-23 22:24:00 + 5020 9110 极品官方内部号 1500 redeem_reward batch_redeem_1_items 01-23 22:24:49 + 5021 9110 极品官方内部号 600 redeem_reward batch_redeem_1_items 01-23 22:25:45 + 5022 9110 极品官方内部号 300 redeem_reward batch_redeem_1_items 01-24 00:45:02 + 5023 9110 极品官方内部号 1300 redeem_reward batch_redeem_3_items 01-24 00:59:12 + 5024 9110 极品官方内部号 11000 redeem_reward batch_redeem_1_items 01-24 01:53:00 + 5027 9110 极品官方内部号 300 redeem_reward batch_redeem_1_items 01-24 02:55:36 + 5030 9110 极品官方内部号 2500 redeem_reward batch_redeem_6_items 01-24 22:22:10 + 5031 9110 极品官方内部号 15100 redeem_reward batch_redeem_4_items 01-24 23:05:03 + 5035 9110 极品官方内部号 2400 redeem_reward batch_redeem_2_items 01-24 23:08:05 + 5038 9110 极品官方内部号 1000 redeem_reward batch_redeem_1_items 01-24 23:21:28 + 5050 9110 极品官方内部号 12300 redeem_reward batch_redeem_4_items 01-25 04:18:28 + 5054 9110 极品官方内部号 2200 redeem_reward batch_redeem_2_items 01-25 04:22:17 + 5055 9110 极品官方内部号 500 redeem_reward batch_redeem_1_items 01-25 04:31:03 + 5056 9110 极品官方内部号 100 redeem_reward batch_redeem_1_items 01-25 04:32:11 + 5061 9110 极品官方内部号 24100 redeem_reward batch_redeem_3_items 01-25 19:16:59 + 5062 9110 极品官方内部号 4000 redeem_reward batch_redeem_1_items 01-25 19:28:11 + 5063 9110 极品官方内部号 1250 redeem_reward batch_redeem_1_items 01-25 19:29:16 + 5064 9110 极品官方内部号 300 redeem_reward batch_redeem_1_items 01-25 19:29:40 + 5072 9110 极品官方内部号 3300 redeem_reward batch_redeem_1_items 01-25 20:19:21 + 5084 9110 极品官方内部号 4850 redeem_reward batch_redeem_8_items 01-25 20:22:54 + 5096 9110 极品官方内部号 3300 redeem_reward batch_redeem_10_items 01-25 20:26:51 + 5107 9110 极品官方内部号 6000 redeem_reward batch_redeem_10_items 01-25 20:32:57 + 5109 9110 极品官方内部号 1600 redeem_reward batch_redeem_2_items 01-25 20:34:32 + 5230 9110 极品官方内部号 1400 redeem_reward batch_redeem_4_items 01-29 12:54:14 + 5413 9110 极品官方内部号 1000 redeem_reward batch_redeem_1_items 02-03 14:28:09 + 5415 9110 极品官方内部号 1200 redeem_reward batch_redeem_4_items 02-03 15:59:27 + 5416 9110 极品官方内部号 2000 redeem_reward batch_redeem_12_items 02-03 16:01:06 + 5417 9110 极品官方内部号 1000 redeem_reward batch_redeem_10_items 02-03 16:05:45 + 5418 9110 极品官方内部号 2400 redeem_reward batch_redeem_10_items 02-03 16:08:23 + 5419 9110 极品官方内部号 1400 redeem_reward batch_redeem_10_items 02-03 16:08:39 + 5420 9110 极品官方内部号 1400 redeem_reward batch_redeem_10_items 02-03 16:08:57 + 5421 9110 极品官方内部号 1000 redeem_reward batch_redeem_10_items 02-03 16:09:15 + 5422 9110 极品官方内部号 1000 redeem_reward batch_redeem_10_items 02-03 16:09:34 + 5423 9110 极品官方内部号 1000 redeem_reward batch_redeem_10_items 02-03 16:09:57 + 5424 9110 极品官方内部号 300 redeem_reward batch_redeem_3_items 02-03 16:10:20 + 5729 9110 极品官方内部号 17000 redeem_reward batch_redeem_22_items 02-16 20:50:11 + 5730 9110 极品官方内部号 19000 redeem_reward batch_redeem_9_items 02-16 20:50:18 + 5747 9110 极品官方内部号 42390 redeem_reward batch_redeem_8_items 02-16 21:23:43 + 5961 9110 极品官方内部号 16900 redeem_reward batch_redeem_72_items 02-17 01:45:37 + 6366 9110 极品官方内部号 1500 redeem_reward batch_redeem_17_items 02-17 23:29:09 + 6394 9110 极品官方内部号 60 redeem_reward batch_redeem_1_items 02-17 23:54:11 + 6528 9110 极品官方内部号 900 redeem_reward batch_redeem_9_items 02-18 16:53:10 + 6533 9110 极品官方内部号 360 redeem_reward batch_redeem_6_items 02-18 16:54:24 + 6825 9110 极品官方内部号 7200 redeem_reward batch_redeem_1_items 02-19 01:44:36 + 7165 9110 极品官方内部号 300 redeem_reward batch_redeem_1_items 02-20 21:23:44 + 7597 9110 极品官方内部号 3000 redeem_reward batch_redeem_2_items 02-22 00:50:46 + 7598 9110 极品官方内部号 500 redeem_reward batch_redeem_1_items 02-22 00:51:43 + 7599 9110 极品官方内部号 2500 redeem_reward batch_redeem_1_items 02-22 00:52:44 + 7600 9110 极品官方内部号 2800 redeem_reward batch_redeem_5_items 02-22 00:55:30 + 7635 9110 极品官方内部号 1600 redeem_reward batch_redeem_5_items 02-22 20:46:22 + 7636 9110 极品官方内部号 2500 redeem_reward batch_redeem_2_items 02-22 20:47:04 + 7637 9110 极品官方内部号 800 redeem_reward batch_redeem_1_items 02-22 20:48:06 + 7639 9110 极品官方内部号 13200 redeem_reward batch_redeem_8_items 02-22 20:53:03 + 7780 9110 极品官方内部号 4000 redeem_reward batch_redeem_1_items 02-24 02:47:38 + 7781 9110 极品官方内部号 400 redeem_reward batch_redeem_1_items 02-24 03:20:45 + 7784 9110 极品官方内部号 1600 redeem_reward batch_redeem_2_items 02-24 03:25:52 + 7825 9110 极品官方内部号 400 redeem_reward batch_redeem_1_items 02-24 21:19:55 + 7873 9110 极品官方内部号 1500 redeem_reward batch_redeem_1_items 02-25 18:06:56 + 7875 9110 极品官方内部号 250 redeem_reward batch_redeem_1_items 02-25 18:09:12 + 7880 9110 极品官方内部号 240 redeem_reward batch_redeem_4_items 02-25 18:26:55 + 7883 9110 极品官方内部号 300 redeem_reward batch_redeem_1_items 02-25 18:29:03 + 7884 9110 极品官方内部号 300 redeem_reward batch_redeem_1_items 02-25 18:31:08 + 7887 9110 极品官方内部号 200 redeem_reward batch_redeem_1_items 02-25 18:33:06 + 8084 9110 极品官方内部号 800 redeem_reward batch_redeem_3_items 02-28 00:14:30 + 8086 9110 极品官方内部号 27000 redeem_reward batch_redeem_2_items 02-28 00:23:03 + 8088 9110 极品官方内部号 21600 redeem_reward batch_redeem_2_items 02-28 00:45:44 + 8188 9110 极品官方内部号 1000 redeem_reward batch_redeem_2_items 03-01 01:01:39 + 8194 9110 极品官方内部号 16900 redeem_reward batch_redeem_4_items 03-01 02:10:32 + 8268 9110 极品官方内部号 5600 redeem_reward batch_redeem_4_items 03-02 00:36:22 + 8365 9110 极品官方内部号 300 redeem_reward batch_redeem_1_items 03-02 22:46:07 + 8690 9110 极品官方内部号 40800 redeem_reward batch_redeem_9_items 03-04 22:28:26 + 8751 9110 极品官方内部号 26300 redeem_reward batch_redeem_10_items 03-05 00:14:02 + 8766 9110 极品官方内部号 23000 redeem_reward batch_redeem_1_items 03-05 01:55:59 + 8799 9110 极品官方内部号 600 redeem_reward batch_redeem_2_items 03-05 10:01:07 + 8813 9110 极品官方内部号 5000 redeem_reward batch_redeem_4_items 03-05 21:01:20 + 8814 9110 极品官方内部号 1300 redeem_reward batch_redeem_2_items 03-05 21:02:40 + 8837 9110 极品官方内部号 15400 redeem_reward batch_redeem_4_items 03-05 22:18:24 + 8838 9110 极品官方内部号 3600 redeem_reward batch_redeem_2_items 03-05 22:22:12 + 8875 9110 极品官方内部号 300 redeem_reward batch_redeem_1_items 03-06 07:45:13 + 8877 9110 极品官方内部号 100 redeem_reward batch_redeem_1_items 03-06 09:01:29 + 8910 9110 极品官方内部号 2800 redeem_reward batch_redeem_5_items 03-07 15:13:49 + 8915 9110 极品官方内部号 8400 redeem_reward batch_redeem_3_items 03-07 15:16:00 + 8934 9110 极品官方内部号 1000 redeem_reward batch_redeem_2_items 03-07 21:13:48 + 8936 9110 极品官方内部号 2700 redeem_reward batch_redeem_4_items 03-07 21:16:23 + 8938 9110 极品官方内部号 2300 redeem_reward batch_redeem_4_items 03-07 21:17:59 + 8940 9110 极品官方内部号 2000 redeem_reward batch_redeem_4_items 03-07 21:20:33 + 8942 9110 极品官方内部号 1300 redeem_reward batch_redeem_4_items 03-07 21:22:44 + 用户 9110 小计: 695600 积分 (6956.00 元) + + 5005 9116 非洲人 1300 redeem_reward batch_redeem_4_items 01-23 05:03:12 + 5006 9116 非洲人 1100 redeem_reward batch_redeem_2_items 01-23 05:06:42 + 5008 9116 非洲人 600 redeem_reward batch_redeem_1_items 01-23 05:08:08 + 5011 9116 非洲人 3700 redeem_reward batch_redeem_3_items 01-23 05:12:51 + 5065 9116 非洲人 3440 redeem_reward batch_redeem_2_items 01-25 20:07:40 + 5066 9116 非洲人 5000 redeem_reward batch_redeem_5_items 01-25 20:14:31 + 5068 9116 非洲人 600 redeem_reward batch_redeem_1_items 01-25 20:17:55 + 5070 9116 非洲人 600 redeem_reward batch_redeem_1_items 01-25 20:18:53 + 5074 9116 非洲人 600 redeem_reward batch_redeem_1_items 01-25 20:19:36 + 5078 9116 非洲人 8900 redeem_reward batch_redeem_1_items 01-25 20:20:48 + 5095 9116 非洲人 1000 redeem_reward batch_redeem_1_items 01-25 20:26:09 + 5776 9116 非洲人 600 redeem_reward batch_redeem_2_items 02-16 22:17:21 + 5778 9116 非洲人 300 redeem_reward batch_redeem_1_items 02-16 22:18:02 + 5780 9116 非洲人 400 redeem_reward batch_redeem_1_items 02-16 22:18:56 + 5782 9116 非洲人 400 redeem_reward batch_redeem_1_items 02-16 22:19:39 + 5785 9116 非洲人 300 redeem_reward batch_redeem_1_items 02-16 22:21:09 + 6213 9116 非洲人 550 redeem_reward batch_redeem_3_items 02-17 17:53:39 + 6215 9116 非洲人 10300 redeem_reward batch_redeem_10_items 02-17 18:04:46 + 6219 9116 非洲人 1600 redeem_reward batch_redeem_8_items 02-17 18:10:42 + 6222 9116 非洲人 10800 redeem_reward batch_redeem_11_items 02-17 18:15:36 + 6223 9116 非洲人 3300 redeem_reward batch_redeem_10_items 02-17 18:18:04 + 6225 9116 非洲人 3000 redeem_reward batch_redeem_10_items 02-17 18:23:28 + 6229 9116 非洲人 240 redeem_reward batch_redeem_4_items 02-17 18:28:10 + 6277 9116 非洲人 1500 redeem_reward batch_redeem_1_items 02-17 20:10:08 + 6485 9116 非洲人 300 redeem_reward batch_redeem_5_items 02-18 11:18:05 + 6512 9116 非洲人 200 redeem_reward batch_redeem_2_items 02-18 16:44:40 + 6536 9116 非洲人 200 redeem_reward batch_redeem_2_items 02-18 16:57:30 + 6593 9116 非洲人 3300 redeem_reward batch_redeem_11_items 02-18 17:34:57 + 6595 9116 非洲人 3000 redeem_reward batch_redeem_10_items 02-18 17:35:45 + 6603 9116 非洲人 1800 redeem_reward batch_redeem_30_items 02-18 17:47:22 + 6606 9116 非洲人 600 redeem_reward batch_redeem_10_items 02-18 17:47:43 + 6608 9116 非洲人 1200 redeem_reward batch_redeem_20_items 02-18 17:48:16 + 6610 9116 非洲人 600 redeem_reward batch_redeem_10_items 02-18 17:48:33 + 6612 9116 非洲人 600 redeem_reward batch_redeem_10_items 02-18 17:48:51 + 6704 9116 非洲人 3660 redeem_reward batch_redeem_12_items 02-18 18:18:07 + 6729 9116 非洲人 300 redeem_reward batch_redeem_5_items 02-18 19:25:41 + 6751 9116 非洲人 300 redeem_reward batch_redeem_1_items 02-18 22:12:46 + 6754 9116 非洲人 900 redeem_reward batch_redeem_15_items 02-18 22:30:19 + 6775 9116 非洲人 33240 redeem_reward batch_redeem_5_items 02-18 23:15:35 + 6815 9116 非洲人 7400 redeem_reward batch_redeem_4_items 02-19 01:32:23 + 6845 9116 非洲人 37740 redeem_reward batch_redeem_5_items 02-19 02:42:09 + 6858 9116 非洲人 7900 redeem_reward batch_redeem_1_items 02-19 02:53:20 + 6879 9116 非洲人 960 redeem_reward batch_redeem_10_items 02-19 03:07:53 + 6880 9116 非洲人 60 redeem_reward batch_redeem_1_items 02-19 03:15:08 + 6884 9116 非洲人 1650 redeem_reward batch_redeem_10_items 02-19 03:17:41 + 6894 9116 非洲人 120 redeem_reward batch_redeem_2_items 02-19 03:26:40 + 6966 9116 非洲人 1140 redeem_reward batch_redeem_19_items 02-19 15:00:14 + 6967 9116 非洲人 60 redeem_reward batch_redeem_1_items 02-19 15:00:17 + 6992 9116 非洲人 2000 redeem_reward batch_redeem_4_items 02-19 18:18:55 + 7023 9116 非洲人 300 redeem_reward batch_redeem_5_items 02-19 18:45:33 + 7026 9116 非洲人 600 redeem_reward batch_redeem_10_items 02-19 18:47:18 + 7150 9116 非洲人 300 redeem_reward batch_redeem_1_items 02-20 20:27:10 + 7330 9116 非洲人 139000 redeem_reward batch_redeem_5_items 02-21 01:39:30 + 7449 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 14:56:14 + 7450 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 14:58:24 + 7451 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 15:00:57 + 7452 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 15:04:38 + 7453 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 15:04:50 + 7454 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 15:05:54 + 7455 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 15:07:50 + 7456 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 15:09:16 + 7457 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 15:11:17 + 7458 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 15:12:43 + 7474 9116 非洲人 412500 redeem_reward batch_redeem_11_items 02-21 16:43:43 + 7475 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 16:44:33 + 7476 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 16:47:13 + 7477 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 16:50:15 + 7478 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 16:51:03 + 7479 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 16:51:31 + 7480 9116 非洲人 37500 redeem_reward batch_redeem_1_items 02-21 16:52:12 + 7645 9116 非洲人 2790 redeem_reward batch_redeem_3_items 02-22 21:52:59 + 7710 9116 非洲人 120 redeem_reward batch_redeem_2_items 02-23 18:03:07 + 7995 9116 非洲人 450 redeem_reward batch_redeem_2_items 02-26 22:03:02 + 7996 9116 非洲人 300 redeem_reward batch_redeem_1_items 02-26 22:04:20 + 7998 9116 非洲人 600 redeem_reward batch_redeem_10_items 02-26 22:04:49 + 8005 9116 非洲人 400 redeem_reward batch_redeem_1_items 02-26 22:34:56 + 8044 9116 非洲人 1250 redeem_reward batch_redeem_1_items 02-27 14:30:54 + 8082 9116 非洲人 300 redeem_reward batch_redeem_1_items 02-28 00:01:15 + 8102 9116 非洲人 900 redeem_reward batch_redeem_3_items 02-28 01:53:40 + 8231 9116 非洲人 300 redeem_reward batch_redeem_1_items 03-01 21:00:11 + 8232 9116 非洲人 3000 redeem_reward batch_redeem_10_items 03-01 21:01:16 + 8234 9116 非洲人 3000 redeem_reward batch_redeem_10_items 03-01 21:01:34 + 8236 9116 非洲人 3000 redeem_reward batch_redeem_10_items 03-01 21:01:59 + 8238 9116 非洲人 2700 redeem_reward batch_redeem_9_items 03-01 21:03:55 + 8305 9116 非洲人 7200 redeem_reward batch_redeem_120_items 03-02 12:31:45 + 8440 9116 非洲人 300 redeem_reward batch_redeem_1_items 03-03 02:31:13 + 8444 9116 非洲人 300 redeem_reward batch_redeem_2_items 03-03 02:32:41 + 8446 9116 非洲人 600 redeem_reward batch_redeem_2_items 03-03 02:34:10 + 8448 9116 非洲人 300 redeem_reward batch_redeem_1_items 03-03 02:34:39 + 8450 9116 非洲人 600 redeem_reward batch_redeem_2_items 03-03 02:35:39 + 8452 9116 非洲人 500 redeem_reward batch_redeem_1_items 03-03 02:36:17 + 8454 9116 非洲人 3200 redeem_reward batch_redeem_10_items 03-03 02:37:23 + 8458 9116 非洲人 4450 redeem_reward batch_redeem_10_items 03-03 02:38:05 + 8465 9116 非洲人 10250 redeem_reward batch_redeem_20_items 03-03 02:40:05 + 8471 9116 非洲人 8750 redeem_reward batch_redeem_18_items 03-03 02:41:59 + 8473 9116 非洲人 21000 redeem_reward batch_redeem_2_items 03-03 02:42:13 + 8523 9116 非洲人 30000 redeem_reward batch_redeem_1_items 03-03 16:34:57 + 8535 9116 非洲人 12050 redeem_reward batch_redeem_31_items 03-03 22:33:08 + 8573 9116 非洲人 7000 redeem_reward batch_redeem_20_items 03-04 01:54:15 + 8575 9116 非洲人 3600 redeem_reward batch_redeem_10_items 03-04 02:08:02 + 8578 9116 非洲人 70000 redeem_reward batch_redeem_1_items 03-04 02:38:17 + 8691 9116 非洲人 1250 redeem_reward batch_redeem_1_items 03-04 22:28:48 + 8908 9116 非洲人 1800 redeem_reward batch_redeem_1_items 03-07 14:30:54 + 用户 9116 小计: 1524320 积分 (15243.20 元) + + 5620 9209 程c 500 redeem_reward batch_redeem_1_items 02-16 00:29:53 + 5621 9209 程c 500 redeem_reward batch_redeem_1_items 02-16 00:41:42 + 5628 9209 程c 2810 redeem_reward batch_redeem_14_items 02-16 00:51:33 + 5641 9209 程c 500 redeem_reward batch_redeem_1_items 02-16 01:10:16 + 5643 9209 程c 300 redeem_reward batch_redeem_1_items 02-16 01:11:03 + 5645 9209 程c 300 redeem_reward batch_redeem_1_items 02-16 01:11:51 + 5646 9209 程c 900 redeem_reward batch_redeem_3_items 02-16 01:14:49 + 5647 9209 程c 300 redeem_reward batch_redeem_1_items 02-16 01:15:35 + 5724 9209 程c 9750 redeem_reward batch_redeem_19_items 02-16 20:45:50 + 5731 9209 程c 3000 redeem_reward batch_redeem_10_items 02-16 20:52:41 + 5732 9209 程c 3000 redeem_reward batch_redeem_10_items 02-16 20:53:48 + 5733 9209 程c 21500 redeem_reward batch_redeem_4_items 02-16 20:55:03 + 5737 9209 程c 23600 redeem_reward batch_redeem_17_items 02-16 21:07:09 + 5765 9209 程c 52700 redeem_reward batch_redeem_2_items 02-16 21:38:10 + 5770 9209 程c 600 redeem_reward batch_redeem_2_items 02-16 21:45:42 + 5772 9209 程c 1500 redeem_reward batch_redeem_5_items 02-16 21:46:34 + 5796 9209 程c 1300 redeem_reward batch_redeem_7_items 02-16 22:41:06 + 5809 9209 程c 5200 redeem_reward batch_redeem_14_items 02-16 22:48:41 + 5922 9209 程c 600 redeem_reward batch_redeem_2_items 02-17 01:20:45 + 5940 9209 程c 1500 redeem_reward batch_redeem_25_items 02-17 01:33:28 + 5948 9209 程c 72110 redeem_reward batch_redeem_11_items 02-17 01:39:41 + 6095 9209 程c 800 redeem_reward batch_redeem_2_items 02-17 02:40:19 + 6102 9209 程c 600 redeem_reward batch_redeem_10_items 02-17 02:42:19 + 6103 9209 程c 1200 redeem_reward batch_redeem_20_items 02-17 02:44:18 + 6104 9209 程c 600 redeem_reward batch_redeem_10_items 02-17 02:44:43 + 6106 9209 程c 1900 redeem_reward batch_redeem_5_items 02-17 02:57:17 + 6176 9209 程c 300 redeem_reward batch_redeem_5_items 02-17 13:21:41 + 6177 9209 程c 720 redeem_reward batch_redeem_4_items 02-17 13:24:00 + 6178 9209 程c 150 redeem_reward batch_redeem_1_items 02-17 13:24:26 + 6180 9209 程c 600 redeem_reward batch_redeem_3_items 02-17 13:25:20 + 6190 9209 程c 1100 redeem_reward batch_redeem_2_items 02-17 14:55:18 + 6191 9209 程c 300 redeem_reward batch_redeem_1_items 02-17 14:56:37 + 6192 9209 程c 480 redeem_reward batch_redeem_6_items 02-17 14:57:43 + 6193 9209 程c 1200 redeem_reward batch_redeem_4_items 02-17 14:59:28 + 6214 9209 程c 1200 redeem_reward batch_redeem_2_items 02-17 18:02:37 + 6241 9209 程c 34500 redeem_reward batch_redeem_3_items 02-17 18:39:47 + 6242 9209 程c 1100 redeem_reward batch_redeem_3_items 02-17 18:40:22 + 6243 9209 程c 600 redeem_reward batch_redeem_2_items 02-17 18:40:54 + 6244 9209 程c 60 redeem_reward batch_redeem_1_items 02-17 18:41:15 + 6245 9209 程c 180 redeem_reward batch_redeem_3_items 02-17 18:41:35 + 6271 9209 程c 3300 redeem_reward batch_redeem_10_items 02-17 19:55:29 + 6274 9209 程c 1500 redeem_reward batch_redeem_1_items 02-17 20:01:42 + 6412 9209 程c 1200 redeem_reward batch_redeem_3_items 02-18 01:16:59 + 6414 9209 程c 3350 redeem_reward batch_redeem_2_items 02-18 01:17:12 + 6432 9209 程c 1700 redeem_reward batch_redeem_5_items 02-18 02:04:10 + 6436 9209 程c 5650 redeem_reward batch_redeem_3_items 02-18 02:07:10 + 6461 9209 程c 75000 redeem_reward batch_redeem_2_items 02-18 05:27:59 + 6486 9209 程c 75000 redeem_reward batch_redeem_2_items 02-18 11:30:48 + 6489 9209 程c 480 redeem_reward batch_redeem_8_items 02-18 11:58:53 + 6490 9209 程c 1100 redeem_reward batch_redeem_3_items 02-18 11:59:57 + 6491 9209 程c 2400 redeem_reward batch_redeem_10_items 02-18 12:01:18 + 6492 9209 程c 360 redeem_reward batch_redeem_2_items 02-18 12:02:20 + 6493 9209 程c 3000 redeem_reward batch_redeem_1_items 02-18 12:02:40 + 6494 9209 程c 300 redeem_reward batch_redeem_1_items 02-18 12:03:07 + 6495 9209 程c 150 redeem_reward batch_redeem_1_items 02-18 12:08:00 + 6618 9209 程c 3200 redeem_reward batch_redeem_10_items 02-18 18:05:13 + 6619 9209 程c 300 redeem_reward batch_redeem_5_items 02-18 18:06:31 + 6621 9209 程c 720 redeem_reward batch_redeem_12_items 02-18 18:08:08 + 6748 9209 程c 1200 redeem_reward batch_redeem_20_items 02-18 22:06:50 + 6750 9209 程c 300 redeem_reward batch_redeem_1_items 02-18 22:08:49 + 6753 9209 程c 540 redeem_reward batch_redeem_9_items 02-18 22:23:45 + 6756 9209 程c 1100 redeem_reward batch_redeem_2_items 02-18 22:32:24 + 6758 9209 程c 1100 redeem_reward batch_redeem_2_items 02-18 22:37:47 + 6759 9209 程c 200 redeem_reward batch_redeem_2_items 02-18 22:42:22 + 6760 9209 程c 200 redeem_reward batch_redeem_2_items 02-18 22:44:13 + 6766 9209 程c 300 redeem_reward batch_redeem_1_items 02-18 22:57:15 + 6769 9209 程c 4100 redeem_reward batch_redeem_2_items 02-18 23:04:26 + 6774 9209 程c 300 redeem_reward batch_redeem_1_items 02-18 23:12:44 + 6784 9209 程c 1700 redeem_reward batch_redeem_5_items 02-18 23:31:11 + 6790 9209 程c 18850 redeem_reward batch_redeem_31_items 02-18 23:42:10 + 6804 9209 程c 64500 redeem_reward batch_redeem_1_items 02-19 00:48:52 + 6816 9209 程c 1500 redeem_reward batch_redeem_5_items 02-19 01:32:28 + 6911 9209 程c 4200 redeem_reward batch_redeem_70_items 02-19 04:05:21 + 6913 9209 程c 3000 redeem_reward batch_redeem_10_items 02-19 04:25:52 + 6914 9209 程c 60 redeem_reward batch_redeem_1_items 02-19 04:27:23 + 6916 9209 程c 1500 redeem_reward batch_redeem_5_items 02-19 04:30:24 + 7088 9209 程c 300 redeem_reward batch_redeem_5_items 02-20 01:01:13 + 7090 9209 程c 800 redeem_reward batch_redeem_2_items 02-20 01:01:17 + 7140 9209 程c 1200 redeem_reward batch_redeem_2_items 02-20 20:08:35 + 7143 9209 程c 600 redeem_reward batch_redeem_1_items 02-20 20:11:58 + 7144 9209 程c 60 redeem_reward batch_redeem_1_items 02-20 20:12:16 + 7340 9209 程c 22700 redeem_reward batch_redeem_13_items 02-21 01:49:19 + 7351 9209 程c 37740 redeem_reward batch_redeem_5_items 02-21 02:02:57 + 7352 9209 程c 37740 redeem_reward batch_redeem_5_items 02-21 02:03:03 + 7353 9209 程c 92640 redeem_reward batch_redeem_7_items 02-21 02:03:09 + 7358 9209 程c 480 redeem_reward batch_redeem_8_items 02-21 03:18:13 + 7471 9209 程c 75000 redeem_reward batch_redeem_2_items 02-21 15:25:18 + 7503 9209 程c 600 redeem_reward batch_redeem_10_items 02-21 17:21:56 + 7642 9209 程c 1200 redeem_reward batch_redeem_2_items 02-22 21:04:59 + 7771 9209 程c 180 redeem_reward batch_redeem_3_items 02-24 02:37:54 + 7772 9209 程c 180 redeem_reward batch_redeem_3_items 02-24 02:39:04 + 7773 9209 程c 4000 redeem_reward batch_redeem_1_items 02-24 02:39:22 + 7776 9209 程c 2900 redeem_reward batch_redeem_1_items 02-24 02:44:02 + 7779 9209 程c 4380 redeem_reward batch_redeem_2_items 02-24 02:45:10 + 7849 9209 程c 300 redeem_reward batch_redeem_5_items 02-24 23:07:22 + 7894 9209 程c 3000 redeem_reward batch_redeem_50_items 02-26 01:02:08 + 7896 9209 程c 900 redeem_reward batch_redeem_3_items 02-26 01:05:24 + 7897 9209 程c 60 redeem_reward batch_redeem_1_items 02-26 01:05:50 + 7949 9209 程c 1400 redeem_reward batch_redeem_5_items 02-26 14:22:25 + 8014 9209 程c 4000 redeem_reward batch_redeem_1_items 02-27 03:53:47 + 8043 9209 程c 18000 redeem_reward batch_redeem_1_items 02-27 14:29:57 + 8046 9209 程c 2100 redeem_reward batch_redeem_1_items 02-27 14:32:24 + 8093 9209 程c 32400 redeem_reward batch_redeem_3_items 02-28 01:30:19 + 8100 9209 程c 1200 redeem_reward batch_redeem_4_items 02-28 01:49:02 + 8117 9209 程c 6300 redeem_reward batch_redeem_2_items 02-28 02:09:58 + 8619 9209 程c 12500 redeem_reward batch_redeem_6_items 03-04 12:48:04 + 8697 9209 程c 400 redeem_reward batch_redeem_1_items 03-04 22:41:42 + 8701 9209 程c 500 redeem_reward batch_redeem_1_items 03-04 22:43:39 + 8702 9209 程c 120 redeem_reward batch_redeem_2_items 03-04 22:44:26 + 8709 9209 程c 1200 redeem_reward batch_redeem_2_items 03-04 22:45:52 + 8711 9209 程c 8600 redeem_reward batch_redeem_10_items 03-04 22:48:01 + 8712 9209 程c 300 redeem_reward batch_redeem_3_items 03-04 22:48:43 + 8714 9209 程c 24250 redeem_reward batch_redeem_10_items 03-04 22:49:47 + 8716 9209 程c 1500 redeem_reward batch_redeem_11_items 03-04 22:53:08 + 8719 9209 程c 3100 redeem_reward batch_redeem_17_items 03-04 22:57:47 + 8721 9209 程c 2400 redeem_reward batch_redeem_6_items 03-04 22:59:58 + 8722 9209 程c 60 redeem_reward batch_redeem_1_items 03-04 23:00:26 + 8723 9209 程c 15610 redeem_reward batch_redeem_11_items 03-04 23:03:06 + 8770 9209 程c 3600 redeem_reward batch_redeem_4_items 03-05 02:18:40 + 8772 9209 程c 1500 redeem_reward batch_redeem_4_items 03-05 02:20:49 + 8776 9209 程c 1400 redeem_reward batch_redeem_3_items 03-05 02:22:59 + 8778 9209 程c 4250 redeem_reward batch_redeem_8_items 03-05 02:25:15 + 8779 9209 程c 14400 redeem_reward batch_redeem_2_items 03-05 02:26:12 + 8782 9209 程c 6400 redeem_reward batch_redeem_20_items 03-05 02:29:24 + 8784 9209 程c 4200 redeem_reward batch_redeem_10_items 03-05 02:30:45 + 8786 9209 程c 3800 redeem_reward batch_redeem_20_items 03-05 02:31:47 + 8787 9209 程c 300 redeem_reward batch_redeem_1_items 03-05 02:32:14 + 8790 9209 程c 4450 redeem_reward batch_redeem_10_items 03-05 02:48:57 + 8793 9209 程c 9000 redeem_reward batch_redeem_20_items 03-05 02:50:32 + 8795 9209 程c 20300 redeem_reward batch_redeem_20_items 03-05 02:52:22 + 8801 9209 程c 600 redeem_reward batch_redeem_2_items 03-05 10:17:07 + 8803 9209 程c 10500 redeem_reward batch_redeem_10_items 03-05 10:19:41 + 用户 9209 小计: 1041020 积分 (10410.20 元) + + 5635 9210 非酋 2700 redeem_reward batch_redeem_3_items 02-16 00:58:32 + 5665 9210 非酋 4700 redeem_reward batch_redeem_7_items 02-16 09:30:49 + 5703 9210 非酋 44050 redeem_reward batch_redeem_28_items 02-16 15:07:59 + 5718 9210 非酋 14850 redeem_reward batch_redeem_12_items 02-16 20:25:43 + 5719 9210 非酋 44050 redeem_reward batch_redeem_8_items 02-16 20:25:57 + 5738 9210 非酋 18500 redeem_reward batch_redeem_24_items 02-16 21:11:11 + 5798 9210 非酋 35900 redeem_reward batch_redeem_46_items 02-16 22:43:10 + 5818 9210 非酋 69970 redeem_reward batch_redeem_131_items 02-16 22:57:53 + 5833 9210 非酋 41150 redeem_reward batch_redeem_30_items 02-16 22:59:30 + 5836 9210 非酋 112500 redeem_reward batch_redeem_3_items 02-16 23:00:51 + 5846 9210 非酋 31000 redeem_reward batch_redeem_1_items 02-16 23:43:26 + 5856 9210 非酋 1900 redeem_reward batch_redeem_1_items 02-16 23:57:41 + 5861 9210 非酋 47400 redeem_reward batch_redeem_11_items 02-17 00:19:21 + 5865 9210 非酋 10650 redeem_reward batch_redeem_10_items 02-17 00:20:33 + 5868 9210 非酋 6900 redeem_reward batch_redeem_10_items 02-17 00:21:08 + 5870 9210 非酋 33000 redeem_reward batch_redeem_10_items 02-17 00:21:48 + 5871 9210 非酋 18100 redeem_reward batch_redeem_3_items 02-17 00:22:53 + 5875 9210 非酋 8000 redeem_reward batch_redeem_20_items 02-17 00:23:59 + 5878 9210 非酋 12800 redeem_reward batch_redeem_19_items 02-17 00:24:51 + 5927 9210 非酋 30300 redeem_reward batch_redeem_18_items 02-17 01:22:46 + 5978 9210 非酋 6000 redeem_reward batch_redeem_100_items 02-17 01:50:12 + 5981 9210 非酋 11500 redeem_reward batch_redeem_10_items 02-17 01:50:57 + 5994 9210 非酋 3900 redeem_reward batch_redeem_1_items 02-17 01:55:37 + 6005 9210 非酋 10990 redeem_reward batch_redeem_8_items 02-17 01:59:50 + 6056 9210 非酋 3720 redeem_reward batch_redeem_13_items 02-17 02:23:08 + 6067 9210 非酋 2000 redeem_reward batch_redeem_6_items 02-17 02:25:26 + 6121 9210 非酋 3150 redeem_reward batch_redeem_7_items 02-17 08:58:50 + 6123 9210 非酋 8000 redeem_reward batch_redeem_6_items 02-17 09:00:10 + 6124 9210 非酋 6000 redeem_reward batch_redeem_100_items 02-17 09:01:55 + 6125 9210 非酋 1400 redeem_reward batch_redeem_4_items 02-17 09:03:02 + 6127 9210 非酋 1250 redeem_reward batch_redeem_1_items 02-17 09:04:33 + 6128 9210 非酋 3000 redeem_reward batch_redeem_10_items 02-17 09:05:17 + 6131 9210 非酋 38950 redeem_reward batch_redeem_39_items 02-17 09:06:52 + 6133 9210 非酋 10100 redeem_reward batch_redeem_10_items 02-17 09:07:00 + 6134 9210 非酋 17800 redeem_reward batch_redeem_10_items 02-17 09:12:00 + 6140 9210 非酋 1000 redeem_reward batch_redeem_1_items 02-17 09:22:14 + 6142 9210 非酋 53500 redeem_reward batch_redeem_19_items 02-17 09:23:49 + 6147 9210 非酋 1250 redeem_reward batch_redeem_1_items 02-17 10:57:28 + 6148 9210 非酋 40000 redeem_reward batch_redeem_2_items 02-17 10:58:01 + 6149 9210 非酋 500 redeem_reward batch_redeem_1_items 02-17 11:31:56 + 6199 9210 非酋 2320 redeem_reward batch_redeem_9_items 02-17 16:17:43 + 6216 9210 非酋 97500 redeem_reward batch_redeem_38_items 02-17 18:07:30 + 6259 9210 非酋 9700 redeem_reward batch_redeem_11_items 02-17 19:39:16 + 6279 9210 非酋 75000 redeem_reward batch_redeem_2_items 02-17 20:12:33 + 6285 9210 非酋 1650 redeem_reward batch_redeem_1_items 02-17 20:13:27 + 6295 9210 非酋 17360 redeem_reward batch_redeem_44_items 02-17 20:26:01 + 6297 9210 非酋 800 redeem_reward batch_redeem_1_items 02-17 20:27:12 + 6395 9210 非酋 10500 redeem_reward batch_redeem_1_items 02-17 23:55:13 + 6441 9210 非酋 27400 redeem_reward batch_redeem_39_items 02-18 02:30:37 + 6442 9210 非酋 100 redeem_reward batch_redeem_1_items 02-18 02:30:48 + 6443 9210 非酋 14500 redeem_reward batch_redeem_1_items 02-18 02:31:01 + 6444 9210 非酋 187500 redeem_reward batch_redeem_5_items 02-18 02:42:29 + 6448 9210 非酋 14500 redeem_reward batch_redeem_1_items 02-18 02:43:11 + 6449 9210 非酋 129190 redeem_reward batch_redeem_5_items 02-18 02:43:17 + 6450 9210 非酋 150000 redeem_reward batch_redeem_4_items 02-18 02:43:21 + 6460 9210 非酋 300 redeem_reward batch_redeem_1_items 02-18 03:41:14 + 6497 9210 非酋 145000 redeem_reward batch_redeem_1_items 02-18 14:12:05 + 6527 9210 非酋 1300 redeem_reward batch_redeem_13_items 02-18 16:52:57 + 6555 9210 非酋 91850 redeem_reward batch_redeem_12_items 02-18 17:07:18 + 6561 9210 非酋 7020 redeem_reward batch_redeem_117_items 02-18 17:14:34 + 6563 9210 非酋 1500 redeem_reward batch_redeem_1_items 02-18 17:15:55 + 6566 9210 非酋 900 redeem_reward batch_redeem_3_items 02-18 17:19:02 + 6589 9210 非酋 5960 redeem_reward batch_redeem_14_items 02-18 17:30:10 + 6613 9210 非酋 3000 redeem_reward batch_redeem_10_items 02-18 17:52:36 + 6614 9210 非酋 72700 redeem_reward batch_redeem_50_items 02-18 17:54:23 + 6716 9210 非酋 492800 redeem_reward batch_redeem_16_items 02-18 18:40:33 + 6813 9210 非酋 11450 redeem_reward batch_redeem_24_items 02-19 01:28:13 + 6932 9210 非酋 106000 redeem_reward batch_redeem_2_items 02-19 08:54:52 + 7099 9210 非酋 33900 redeem_reward batch_redeem_10_items 02-20 10:27:38 + 7131 9210 非酋 58000 redeem_reward batch_redeem_1_items 02-20 18:43:40 + 7146 9210 非酋 800 redeem_reward batch_redeem_2_items 02-20 20:15:09 + 7212 9210 非酋 200 redeem_reward batch_redeem_1_items 02-20 23:52:17 + 7432 9210 非酋 32000 redeem_reward batch_redeem_1_items 02-21 11:13:58 + 7433 9210 非酋 300 redeem_reward batch_redeem_1_items 02-21 11:14:06 + 7534 9210 非酋 85950 redeem_reward batch_redeem_59_items 02-21 22:43:21 + 7540 9210 非酋 3300 redeem_reward batch_redeem_2_items 02-21 23:05:28 + 7542 9210 非酋 843800 redeem_reward batch_redeem_18_items 02-21 23:09:43 + 7577 9210 非酋 3900 redeem_reward batch_redeem_2_items 02-21 23:23:55 + 7643 9210 非酋 21250 redeem_reward batch_redeem_10_items 02-22 21:22:31 + 7715 9210 非酋 6350 redeem_reward batch_redeem_3_items 02-23 19:04:19 + 7718 9210 非酋 91650 redeem_reward batch_redeem_122_items 02-23 19:19:41 + 7728 9210 非酋 1250 redeem_reward batch_redeem_1_items 02-23 19:39:53 + 7731 9210 非酋 600 redeem_reward batch_redeem_1_items 02-23 20:17:24 + 7807 9210 非酋 1800 redeem_reward batch_redeem_1_items 02-24 14:51:36 + 7813 9210 非酋 600 redeem_reward batch_redeem_1_items 02-24 15:45:20 + 7827 9210 非酋 400 redeem_reward batch_redeem_1_items 02-24 21:23:01 + 8095 9210 非酋 1220 redeem_reward batch_redeem_5_items 02-28 01:42:51 + 8309 9210 非酋 65540 redeem_reward batch_redeem_210_items 03-02 12:48:49 + 8374 9210 非酋 43650 redeem_reward batch_redeem_50_items 03-02 22:58:30 + 8618 9210 非酋 3700 redeem_reward batch_redeem_9_items 03-04 12:46:16 + 8620 9210 非酋 3400 redeem_reward batch_redeem_3_items 03-04 13:00:30 + 8621 9210 非酋 1800 redeem_reward batch_redeem_3_items 03-04 13:01:50 + 8622 9210 非酋 14600 redeem_reward batch_redeem_2_items 03-04 13:01:53 + 8623 9210 非酋 480 redeem_reward batch_redeem_8_items 03-04 13:02:41 + 用户 9210 小计: 3810670 积分 (38106.70 元) + + 5672 9222 嗯?!!!! 36300 redeem_reward batch_redeem_10_items 02-16 11:35:51 + 5673 9222 嗯?!!!! 2850 redeem_reward batch_redeem_3_items 02-16 12:02:13 + 5674 9222 嗯?!!!! 600 redeem_reward batch_redeem_1_items 02-16 12:03:58 + 5679 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-16 13:52:13 + 5682 9222 嗯?!!!! 6000 redeem_reward batch_redeem_20_items 02-16 13:54:14 + 5685 9222 嗯?!!!! 1200 redeem_reward batch_redeem_20_items 02-16 14:02:00 + 5689 9222 嗯?!!!! 9000 redeem_reward batch_redeem_30_items 02-16 14:03:55 + 5692 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-16 14:11:05 + 5694 9222 嗯?!!!! 6000 redeem_reward batch_redeem_20_items 02-16 14:20:26 + 5701 9222 嗯?!!!! 6720 redeem_reward batch_redeem_72_items 02-16 14:25:51 + 5723 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-16 20:42:43 + 5891 9222 嗯?!!!! 2400 redeem_reward batch_redeem_40_items 02-17 00:57:45 + 5892 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-17 00:58:18 + 5894 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-17 00:58:50 + 5895 9222 嗯?!!!! 1800 redeem_reward batch_redeem_30_items 02-17 00:59:33 + 5898 9222 嗯?!!!! 1800 redeem_reward batch_redeem_30_items 02-17 01:00:36 + 5902 9222 嗯?!!!! 6000 redeem_reward batch_redeem_20_items 02-17 01:03:54 + 5904 9222 嗯?!!!! 145300 redeem_reward batch_redeem_6_items 02-17 01:06:18 + 5912 9222 嗯?!!!! 300 redeem_reward batch_redeem_5_items 02-17 01:11:20 + 6024 9222 嗯?!!!! 11700 redeem_reward batch_redeem_39_items 02-17 02:11:02 + 6026 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-17 02:11:52 + 6028 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-17 02:12:24 + 6032 9222 嗯?!!!! 3000 redeem_reward batch_redeem_50_items 02-17 02:14:16 + 6035 9222 嗯?!!!! 1800 redeem_reward batch_redeem_30_items 02-17 02:15:01 + 6037 9222 嗯?!!!! 1200 redeem_reward batch_redeem_20_items 02-17 02:15:44 + 6039 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-17 02:17:04 + 6042 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-17 02:18:05 + 6049 9222 嗯?!!!! 180 redeem_reward batch_redeem_3_items 02-17 02:18:57 + 6058 9222 嗯?!!!! 3000 redeem_reward batch_redeem_1_items 02-17 02:23:14 + 6081 9222 嗯?!!!! 1800 redeem_reward batch_redeem_30_items 02-17 02:31:17 + 6083 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-17 02:32:24 + 6085 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-17 02:33:32 + 6089 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-17 02:37:08 + 6092 9222 嗯?!!!! 60 redeem_reward batch_redeem_1_items 02-17 02:39:55 + 6097 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-17 02:40:35 + 6100 9222 嗯?!!!! 3180 redeem_reward batch_redeem_53_items 02-17 02:42:02 + 6120 9222 嗯?!!!! 1800 redeem_reward batch_redeem_30_items 02-17 08:05:37 + 6151 9222 嗯?!!!! 19450 redeem_reward batch_redeem_51_items 02-17 11:42:15 + 6197 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-17 15:36:04 + 6260 9222 嗯?!!!! 3180 redeem_reward batch_redeem_53_items 02-17 19:40:00 + 6261 9222 嗯?!!!! 9000 redeem_reward batch_redeem_30_items 02-17 19:40:49 + 6264 9222 嗯?!!!! 6000 redeem_reward batch_redeem_20_items 02-17 19:41:51 + 6266 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-17 19:42:33 + 6377 9222 嗯?!!!! 1850 redeem_reward batch_redeem_3_items 02-17 23:39:11 + 6383 9222 嗯?!!!! 4300 redeem_reward batch_redeem_4_items 02-17 23:41:00 + 6385 9222 嗯?!!!! 5300 redeem_reward batch_redeem_11_items 02-17 23:44:41 + 6387 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-17 23:45:05 + 6393 9222 嗯?!!!! 1600 redeem_reward batch_redeem_2_items 02-17 23:53:09 + 6397 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-18 00:11:48 + 6400 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-18 00:18:24 + 6405 9222 嗯?!!!! 200 redeem_reward batch_redeem_2_items 02-18 00:54:48 + 6410 9222 嗯?!!!! 1360 redeem_reward batch_redeem_3_items 02-18 01:16:15 + 6411 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-18 01:16:46 + 6413 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-18 01:17:09 + 6415 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-18 01:17:30 + 6417 9222 嗯?!!!! 3300 redeem_reward batch_redeem_11_items 02-18 01:18:17 + 6438 9222 嗯?!!!! 600 redeem_reward batch_redeem_2_items 02-18 02:10:23 + 6468 9222 嗯?!!!! 1260 redeem_reward batch_redeem_21_items 02-18 08:04:30 + 6469 9222 嗯?!!!! 1200 redeem_reward batch_redeem_20_items 02-18 08:05:09 + 6470 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-18 08:05:28 + 6471 9222 嗯?!!!! 180 redeem_reward batch_redeem_3_items 02-18 08:05:56 + 6473 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-18 08:06:31 + 6474 9222 嗯?!!!! 6000 redeem_reward batch_redeem_20_items 02-18 08:07:40 + 6476 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-18 08:08:32 + 6477 9222 嗯?!!!! 780 redeem_reward batch_redeem_13_items 02-18 08:13:52 + 6478 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-18 08:14:12 + 6480 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-18 08:14:39 + 6509 9222 嗯?!!!! 4500 redeem_reward batch_redeem_15_items 02-18 16:37:01 + 6534 9222 嗯?!!!! 4300 redeem_reward batch_redeem_4_items 02-18 16:55:27 + 6539 9222 嗯?!!!! 7300 redeem_reward batch_redeem_1_items 02-18 16:58:05 + 6543 9222 嗯?!!!! 120 redeem_reward batch_redeem_2_items 02-18 17:03:54 + 6544 9222 嗯?!!!! 600 redeem_reward batch_redeem_10_items 02-18 17:04:44 + 6554 9222 嗯?!!!! 9060 redeem_reward batch_redeem_31_items 02-18 17:06:57 + 6559 9222 嗯?!!!! 5000 redeem_reward batch_redeem_1_items 02-18 17:08:19 + 6560 9222 嗯?!!!! 800 redeem_reward batch_redeem_1_items 02-18 17:09:19 + 6562 9222 嗯?!!!! 1800 redeem_reward batch_redeem_1_items 02-18 17:14:40 + 6564 9222 嗯?!!!! 2500 redeem_reward batch_redeem_2_items 02-18 17:16:16 + 6580 9222 嗯?!!!! 3000 redeem_reward batch_redeem_10_items 02-18 17:25:00 + 6584 9222 嗯?!!!! 12000 redeem_reward batch_redeem_40_items 02-18 17:26:04 + 6599 9222 嗯?!!!! 6120 redeem_reward batch_redeem_22_items 02-18 17:45:29 + 6681 9222 嗯?!!!! 10200 redeem_reward batch_redeem_170_items 02-18 18:15:35 + 6764 9222 嗯?!!!! 180 redeem_reward batch_redeem_3_items 02-18 22:56:48 + 6783 9222 嗯?!!!! 38350 redeem_reward batch_redeem_84_items 02-18 23:30:36 + 6788 9222 嗯?!!!! 19400 redeem_reward batch_redeem_56_items 02-18 23:37:29 + 6793 9222 嗯?!!!! 6200 redeem_reward batch_redeem_10_items 02-18 23:47:46 + 6796 9222 嗯?!!!! 6400 redeem_reward batch_redeem_5_items 02-18 23:55:57 + 6824 9222 嗯?!!!! 7950 redeem_reward batch_redeem_5_items 02-19 01:44:12 + 6881 9222 嗯?!!!! 18900 redeem_reward batch_redeem_61_items 02-19 03:15:29 + 6883 9222 嗯?!!!! 34150 redeem_reward batch_redeem_40_items 02-19 03:17:34 + 6888 9222 嗯?!!!! 23900 redeem_reward batch_redeem_67_items 02-19 03:23:07 + 6893 9222 嗯?!!!! 40150 redeem_reward batch_redeem_77_items 02-19 03:25:42 + 6902 9222 嗯?!!!! 7980 redeem_reward batch_redeem_33_items 02-19 03:33:21 + 6978 9222 嗯?!!!! 2000 redeem_reward batch_redeem_4_items 02-19 15:24:42 + 6984 9222 嗯?!!!! 145000 redeem_reward batch_redeem_1_items 02-19 16:10:52 + 6993 9222 嗯?!!!! 265000 redeem_reward batch_redeem_1_items 02-19 18:23:18 + 7292 9222 嗯?!!!! 71250 redeem_reward batch_redeem_33_items 02-21 00:28:45 + 7313 9222 嗯?!!!! 25040 redeem_reward batch_redeem_74_items 02-21 00:57:55 + 7640 9222 嗯?!!!! 3400 redeem_reward batch_redeem_5_items 02-22 20:53:52 + 7723 9222 嗯?!!!! 16100 redeem_reward batch_redeem_6_items 02-23 19:29:19 + 7726 9222 嗯?!!!! 42300 redeem_reward batch_redeem_40_items 02-23 19:31:52 + 8302 9222 嗯?!!!! 11580 redeem_reward batch_redeem_189_items 03-02 12:24:24 + 用户 9222 小计: 1216480 积分 (12164.80 元) + + 7240 9230 巨欧小肥龙 2450 redeem_reward batch_redeem_31_items 02-21 00:05:36 + 7247 9230 巨欧小肥龙 1200 redeem_reward batch_redeem_20_items 02-21 00:07:28 + 7257 9230 巨欧小肥龙 4000 redeem_reward batch_redeem_1_items 02-21 00:16:04 + 7268 9230 巨欧小肥龙 600 redeem_reward batch_redeem_1_items 02-21 00:19:54 + 7289 9230 巨欧小肥龙 3800 redeem_reward batch_redeem_10_items 02-21 00:27:45 + 7295 9230 巨欧小肥龙 20500 redeem_reward batch_redeem_10_items 02-21 00:31:11 + 7304 9230 巨欧小肥龙 2700 redeem_reward batch_redeem_45_items 02-21 00:42:18 + 7307 9230 巨欧小肥龙 1800 redeem_reward batch_redeem_12_items 02-21 00:47:09 + 7310 9230 巨欧小肥龙 40650 redeem_reward batch_redeem_10_items 02-21 00:53:51 + 7325 9230 巨欧小肥龙 900 redeem_reward batch_redeem_11_items 02-21 01:34:57 + 7360 9230 巨欧小肥龙 3150 redeem_reward batch_redeem_10_items 02-21 03:41:45 + 7362 9230 巨欧小肥龙 13600 redeem_reward batch_redeem_10_items 02-21 03:42:56 + 7364 9230 巨欧小肥龙 3200 redeem_reward batch_redeem_10_items 02-21 03:47:07 + 7366 9230 巨欧小肥龙 3900 redeem_reward batch_redeem_10_items 02-21 03:48:13 + 7368 9230 巨欧小肥龙 10500 redeem_reward batch_redeem_10_items 02-21 03:49:42 + 7370 9230 巨欧小肥龙 24300 redeem_reward batch_redeem_10_items 02-21 03:51:27 + 7373 9230 巨欧小肥龙 3200 redeem_reward batch_redeem_10_items 02-21 04:09:14 + 7375 9230 巨欧小肥龙 3200 redeem_reward batch_redeem_10_items 02-21 04:09:55 + 7378 9230 巨欧小肥龙 13000 redeem_reward batch_redeem_20_items 02-21 04:10:58 + 7381 9230 巨欧小肥龙 12300 redeem_reward batch_redeem_20_items 02-21 04:12:17 + 7385 9230 巨欧小肥龙 8750 redeem_reward batch_redeem_30_items 02-21 04:13:25 + 7389 9230 巨欧小肥龙 8200 redeem_reward batch_redeem_30_items 02-21 04:14:35 + 7392 9230 巨欧小肥龙 4450 redeem_reward batch_redeem_10_items 02-21 04:15:25 + 7393 9230 巨欧小肥龙 3100 redeem_reward batch_redeem_10_items 02-21 04:15:48 + 7396 9230 巨欧小肥龙 7500 redeem_reward batch_redeem_20_items 02-21 04:16:32 + 7399 9230 巨欧小肥龙 13650 redeem_reward batch_redeem_10_items 02-21 04:17:10 + 7400 9230 巨欧小肥龙 1850 redeem_reward batch_redeem_10_items 02-21 04:17:33 + 7402 9230 巨欧小肥龙 3400 redeem_reward batch_redeem_10_items 02-21 04:18:06 + 7405 9230 巨欧小肥龙 24600 redeem_reward batch_redeem_10_items 02-21 04:18:58 + 7406 9230 巨欧小肥龙 3500 redeem_reward batch_redeem_10_items 02-21 04:19:21 + 7408 9230 巨欧小肥龙 3950 redeem_reward batch_redeem_10_items 02-21 04:19:55 + 7410 9230 巨欧小肥龙 1650 redeem_reward batch_redeem_10_items 02-21 04:20:36 + 7413 9230 巨欧小肥龙 3000 redeem_reward batch_redeem_10_items 02-21 04:21:34 + 7416 9230 巨欧小肥龙 6200 redeem_reward batch_redeem_20_items 02-21 04:22:42 + 7419 9230 巨欧小肥龙 15100 redeem_reward batch_redeem_20_items 02-21 04:23:53 + 7420 9230 巨欧小肥龙 120 redeem_reward batch_redeem_2_items 02-21 04:32:54 + 7421 9230 巨欧小肥龙 3000 redeem_reward batch_redeem_1_items 02-21 04:33:27 + 7584 9230 巨欧小肥龙 2080 redeem_reward batch_redeem_13_items 02-22 00:18:55 + 7588 9230 巨欧小肥龙 1800 redeem_reward batch_redeem_6_items 02-22 00:21:06 + 7590 9230 巨欧小肥龙 1500 redeem_reward batch_redeem_5_items 02-22 00:21:38 + 7638 9230 巨欧小肥龙 4300 redeem_reward batch_redeem_10_items 02-22 20:49:30 + 7775 9230 巨欧小肥龙 4000 redeem_reward batch_redeem_1_items 02-24 02:43:38 + 7787 9230 巨欧小肥龙 8750 redeem_reward batch_redeem_32_items 02-24 03:28:27 + 7790 9230 巨欧小肥龙 9000 redeem_reward batch_redeem_10_items 02-24 03:29:50 + 7791 9230 巨欧小肥龙 1700 redeem_reward batch_redeem_10_items 02-24 03:31:09 + 7798 9230 巨欧小肥龙 9000 redeem_reward batch_redeem_30_items 02-24 03:33:59 + 8011 9230 巨欧小肥龙 1000 redeem_reward batch_redeem_1_items 02-27 01:13:01 + 用户 9230 小计: 324100 积分 (3241.00 元) + + 5797 9248 不出last退了 500 redeem_reward batch_redeem_1_items 02-16 22:41:35 + 5806 9248 不出last退了 6500 redeem_reward batch_redeem_2_items 02-16 22:47:56 + 5817 9248 不出last退了 3800 redeem_reward batch_redeem_51_items 02-16 22:56:59 + 5832 9248 不出last退了 300 redeem_reward batch_redeem_1_items 02-16 22:58:47 + 5838 9248 不出last退了 1000 redeem_reward batch_redeem_1_items 02-16 23:04:20 + 5839 9248 不出last退了 300 redeem_reward batch_redeem_1_items 02-16 23:04:55 + 6200 9248 不出last退了 360 redeem_reward batch_redeem_2_items 02-17 16:29:41 + 6201 9248 不出last退了 1140 redeem_reward batch_redeem_19_items 02-17 16:33:45 + 6202 9248 不出last退了 3000 redeem_reward batch_redeem_50_items 02-17 16:41:26 + 6203 9248 不出last退了 3000 redeem_reward batch_redeem_1_items 02-17 16:42:47 + 6212 9248 不出last退了 400 redeem_reward batch_redeem_1_items 02-17 17:36:10 + 6255 9248 不出last退了 5520 redeem_reward batch_redeem_92_items 02-17 19:19:30 + 6464 9248 不出last退了 7300 redeem_reward batch_redeem_1_items 02-18 06:23:12 + 6465 9248 不出last退了 7700 redeem_reward batch_redeem_19_items 02-18 06:29:14 + 6466 9248 不出last退了 2500 redeem_reward batch_redeem_3_items 02-18 06:29:19 + 6467 9248 不出last退了 2450 redeem_reward batch_redeem_5_items 02-18 06:31:24 + 6744 9248 不出last退了 600 redeem_reward batch_redeem_10_items 02-18 21:20:15 + 6765 9248 不出last退了 6300 redeem_reward batch_redeem_5_items 02-18 22:57:09 + 6770 9248 不出last退了 53600 redeem_reward batch_redeem_4_items 02-18 23:08:27 + 6921 9248 不出last退了 6360 redeem_reward batch_redeem_106_items 02-19 07:09:04 + 6923 9248 不出last退了 4600 redeem_reward batch_redeem_10_items 02-19 07:11:39 + 6925 9248 不出last退了 4400 redeem_reward batch_redeem_10_items 02-19 07:13:24 + 6927 9248 不出last退了 8400 redeem_reward batch_redeem_12_items 02-19 07:23:38 + 6929 9248 不出last退了 10700 redeem_reward batch_redeem_5_items 02-19 07:24:19 + 6930 9248 不出last退了 2200 redeem_reward batch_redeem_5_items 02-19 07:25:13 + 6931 9248 不出last退了 3600 redeem_reward batch_redeem_14_items 02-19 07:42:01 + 6950 9248 不出last退了 17540 redeem_reward batch_redeem_5_items 02-19 13:06:35 + 6951 9248 不出last退了 83500 redeem_reward batch_redeem_2_items 02-19 13:20:15 + 6956 9248 不出last退了 150000 redeem_reward batch_redeem_4_items 02-19 13:22:13 + 6963 9248 不出last退了 800 redeem_reward batch_redeem_2_items 02-19 13:27:30 + 7018 9248 不出last退了 1800 redeem_reward batch_redeem_30_items 02-19 18:37:38 + 7019 9248 不出last退了 600 redeem_reward batch_redeem_10_items 02-19 18:38:10 + 7022 9248 不出last退了 17000 redeem_reward batch_redeem_1_items 02-19 18:45:28 + 7028 9248 不出last退了 4000 redeem_reward batch_redeem_10_items 02-19 18:48:28 + 7030 9248 不出last退了 5000 redeem_reward batch_redeem_4_items 02-19 18:55:54 + 7031 9248 不出last退了 37400 redeem_reward batch_redeem_3_items 02-19 18:55:58 + 7233 9248 不出last退了 21160 redeem_reward batch_redeem_21_items 02-21 00:02:47 + 7235 9248 不出last退了 3050 redeem_reward batch_redeem_2_items 02-21 00:04:13 + 7239 9248 不出last退了 1400 redeem_reward batch_redeem_2_items 02-21 00:05:20 + 7241 9248 不出last退了 180 redeem_reward batch_redeem_3_items 02-21 00:05:49 + 7543 9248 不出last退了 2100 redeem_reward batch_redeem_5_items 02-21 23:10:03 + 7563 9248 不出last退了 800 redeem_reward batch_redeem_1_items 02-21 23:10:41 + 7591 9248 不出last退了 740 redeem_reward batch_redeem_5_items 02-22 00:47:01 + 7594 9248 不出last退了 22400 redeem_reward batch_redeem_20_items 02-22 00:48:51 + 7595 9248 不出last退了 2400 redeem_reward batch_redeem_3_items 02-22 00:49:30 + 7596 9248 不出last退了 500 redeem_reward batch_redeem_1_items 02-22 00:49:59 + 7606 9248 不出last退了 7900 redeem_reward batch_redeem_5_items 02-22 07:57:02 + 7633 9248 不出last退了 3600 redeem_reward batch_redeem_2_items 02-22 20:30:53 + 7652 9248 不出last退了 32240 redeem_reward batch_redeem_5_items 02-22 23:46:16 + 7806 9248 不出last退了 4000 redeem_reward batch_redeem_1_items 02-24 14:34:33 + 7808 9248 不出last退了 4000 redeem_reward batch_redeem_1_items 02-24 14:51:39 + 7810 9248 不出last退了 7500 redeem_reward batch_redeem_1_items 02-24 15:00:51 + 7811 9248 不出last退了 3060 redeem_reward batch_redeem_51_items 02-24 15:01:59 + 7812 9248 不出last退了 9200 redeem_reward batch_redeem_5_items 02-24 15:08:57 + 8177 9248 不出last退了 2190 redeem_reward batch_redeem_1_items 02-28 21:59:42 + 8179 9248 不出last退了 1200 redeem_reward batch_redeem_2_items 02-28 22:02:28 + 8616 9248 不出last退了 10410 redeem_reward batch_redeem_11_items 03-04 12:13:49 + 8617 9248 不出last退了 9300 redeem_reward batch_redeem_2_items 03-04 12:15:51 + 8625 9248 不出last退了 1800 redeem_reward batch_redeem_2_items 03-04 14:13:47 + 8628 9248 不出last退了 3440 redeem_reward batch_redeem_2_items 03-04 17:10:16 + 8629 9248 不出last退了 25300 redeem_reward batch_redeem_35_items 03-04 17:14:38 + 8630 9248 不出last退了 600 redeem_reward batch_redeem_1_items 03-04 17:19:26 + 8631 9248 不出last退了 150 redeem_reward batch_redeem_1_items 03-04 17:24:25 + 用户 9248 小计: 644790 积分 (6447.90 元) + + 6616 9305 新人 500 redeem_reward batch_redeem_1_items 02-18 18:00:55 + 6620 9305 新人 500 redeem_reward batch_redeem_1_items 02-18 18:06:37 + 6622 9305 新人 1250 redeem_reward batch_redeem_1_items 02-18 18:10:02 + 6703 9305 新人 300 redeem_reward batch_redeem_2_items 02-18 18:17:16 + 6706 9305 新人 300 redeem_reward batch_redeem_3_items 02-18 18:19:46 + 6708 9305 新人 4240 redeem_reward batch_redeem_5_items 02-18 18:24:08 + 6709 9305 新人 7000 redeem_reward batch_redeem_1_items 02-18 18:35:08 + 6712 9305 新人 300 redeem_reward batch_redeem_5_items 02-18 18:38:05 + 6714 9305 新人 2300 redeem_reward batch_redeem_6_items 02-18 18:39:44 + 6717 9305 新人 1800 redeem_reward batch_redeem_6_items 02-18 18:41:19 + 6719 9305 新人 300 redeem_reward batch_redeem_1_items 02-18 18:42:06 + 6721 9305 新人 1500 redeem_reward batch_redeem_5_items 02-18 18:43:27 + 6723 9305 新人 700 redeem_reward batch_redeem_2_items 02-18 18:44:20 + 6725 9305 新人 600 redeem_reward batch_redeem_2_items 02-18 18:45:21 + 6728 9305 新人 600 redeem_reward batch_redeem_10_items 02-18 18:50:18 + 6809 9305 新人 7200 redeem_reward batch_redeem_1_items 02-19 01:13:39 + 6811 9305 新人 1500 redeem_reward batch_redeem_3_items 02-19 01:20:17 + 6812 9305 新人 300 redeem_reward batch_redeem_2_items 02-19 01:21:32 + 6818 9305 新人 7100 redeem_reward batch_redeem_1_items 02-19 01:37:28 + 6822 9305 新人 300 redeem_reward batch_redeem_2_items 02-19 01:39:27 + 6826 9305 新人 16000 redeem_reward batch_redeem_1_items 02-19 01:52:47 + 6827 9305 新人 16000 redeem_reward batch_redeem_1_items 02-19 01:54:21 + 6831 9305 新人 39700 redeem_reward batch_redeem_2_items 02-19 02:11:23 + 6836 9305 新人 900 redeem_reward batch_redeem_3_items 02-19 02:25:38 + 6838 9305 新人 1800 redeem_reward batch_redeem_10_items 02-19 02:26:29 + 6840 9305 新人 2450 redeem_reward batch_redeem_4_items 02-19 02:27:25 + 6842 9305 新人 500 redeem_reward batch_redeem_2_items 02-19 02:28:23 + 6843 9305 新人 990 redeem_reward batch_redeem_9_items 02-19 02:41:48 + 6846 9305 新人 1750 redeem_reward batch_redeem_10_items 02-19 02:43:07 + 6848 9305 新人 550 redeem_reward batch_redeem_3_items 02-19 02:43:46 + 6850 9305 新人 500 redeem_reward batch_redeem_3_items 02-19 02:44:22 + 6851 9305 新人 600 redeem_reward batch_redeem_1_items 02-19 02:45:39 + 6852 9305 新人 600 redeem_reward batch_redeem_1_items 02-19 02:46:31 + 6854 9305 新人 1800 redeem_reward batch_redeem_11_items 02-19 02:47:37 + 6856 9305 新人 1300 redeem_reward batch_redeem_2_items 02-19 02:51:28 + 6857 9305 新人 300 redeem_reward batch_redeem_1_items 02-19 02:52:22 + 6859 9305 新人 700 redeem_reward batch_redeem_2_items 02-19 02:53:51 + 6862 9305 新人 500 redeem_reward batch_redeem_1_items 02-19 02:59:24 + 6864 9305 新人 650 redeem_reward batch_redeem_1_items 02-19 03:00:15 + 6868 9305 新人 2500 redeem_reward batch_redeem_1_items 02-19 03:01:17 + 6870 9305 新人 1350 redeem_reward batch_redeem_7_items 02-19 03:03:07 + 6872 9305 新人 1650 redeem_reward batch_redeem_8_items 02-19 03:04:25 + 6874 9305 新人 3450 redeem_reward batch_redeem_4_items 02-19 03:05:20 + 6876 9305 新人 1650 redeem_reward batch_redeem_10_items 02-19 03:05:46 + 6878 9305 新人 450 redeem_reward batch_redeem_3_items 02-19 03:06:15 + 6991 9305 新人 500 redeem_reward batch_redeem_3_items 02-19 17:57:17 + 7061 9305 新人 550 redeem_reward batch_redeem_3_items 02-19 23:33:40 + 7062 9305 新人 300 redeem_reward batch_redeem_1_items 02-19 23:34:39 + 7174 9305 新人 600 redeem_reward batch_redeem_4_items 02-20 23:31:20 + 7176 9305 新人 1250 redeem_reward batch_redeem_4_items 02-20 23:32:33 + 7177 9305 新人 4000 redeem_reward batch_redeem_1_items 02-20 23:32:40 + 7178 9305 新人 300 redeem_reward batch_redeem_1_items 02-20 23:35:08 + 7180 9305 新人 8230 redeem_reward batch_redeem_5_items 02-20 23:35:17 + 7183 9305 新人 1500 redeem_reward batch_redeem_1_items 02-20 23:36:20 + 7185 9305 新人 1600 redeem_reward batch_redeem_3_items 02-20 23:36:52 + 7188 9305 新人 550 redeem_reward batch_redeem_2_items 02-20 23:37:53 + 7189 9305 新人 150 redeem_reward batch_redeem_1_items 02-20 23:37:56 + 7191 9305 新人 500 redeem_reward batch_redeem_3_items 02-20 23:38:56 + 7193 9305 新人 600 redeem_reward batch_redeem_4_items 02-20 23:39:32 + 7194 9305 新人 300 redeem_reward batch_redeem_1_items 02-20 23:45:44 + 7196 9305 新人 600 redeem_reward batch_redeem_1_items 02-20 23:46:32 + 7198 9305 新人 600 redeem_reward batch_redeem_1_items 02-20 23:46:58 + 7200 9305 新人 500 redeem_reward batch_redeem_1_items 02-20 23:47:21 + 7202 9305 新人 3600 redeem_reward batch_redeem_10_items 02-20 23:47:50 + 7205 9305 新人 300 redeem_reward batch_redeem_1_items 02-20 23:49:08 + 7208 9305 新人 600 redeem_reward batch_redeem_2_items 02-20 23:50:07 + 7213 9305 新人 900 redeem_reward batch_redeem_2_items 02-20 23:52:38 + 7215 9305 新人 500 redeem_reward batch_redeem_1_items 02-20 23:53:13 + 7218 9305 新人 1500 redeem_reward batch_redeem_5_items 02-20 23:54:27 + 7219 9305 新人 2000 redeem_reward batch_redeem_1_items 02-20 23:54:34 + 7222 9305 新人 1200 redeem_reward batch_redeem_4_items 02-20 23:55:16 + 7224 9305 新人 900 redeem_reward batch_redeem_3_items 02-20 23:56:04 + 7225 9305 新人 300 redeem_reward batch_redeem_1_items 02-20 23:56:28 + 7226 9305 新人 7500 redeem_reward batch_redeem_1_items 02-20 23:56:34 + 7228 9305 新人 400 redeem_reward batch_redeem_1_items 02-20 23:57:40 + 7242 9305 新人 300 redeem_reward batch_redeem_1_items 02-21 00:06:26 + 7244 9305 新人 300 redeem_reward batch_redeem_1_items 02-21 00:07:06 + 7248 9305 新人 300 redeem_reward batch_redeem_1_items 02-21 00:07:58 + 7250 9305 新人 900 redeem_reward batch_redeem_2_items 02-21 00:09:18 + 7252 9305 新人 650 redeem_reward batch_redeem_2_items 02-21 00:10:06 + 7254 9305 新人 1200 redeem_reward batch_redeem_3_items 02-21 00:10:46 + 7258 9305 新人 500 redeem_reward batch_redeem_1_items 02-21 00:16:12 + 7260 9305 新人 300 redeem_reward batch_redeem_1_items 02-21 00:17:17 + 7262 9305 新人 1200 redeem_reward batch_redeem_4_items 02-21 00:17:53 + 7266 9305 新人 1000 redeem_reward batch_redeem_1_items 02-21 00:19:07 + 7269 9305 新人 600 redeem_reward batch_redeem_1_items 02-21 00:20:19 + 7273 9305 新人 4000 redeem_reward batch_redeem_1_items 02-21 00:21:18 + 7275 9305 新人 600 redeem_reward batch_redeem_2_items 02-21 00:22:14 + 7279 9305 新人 700 redeem_reward batch_redeem_2_items 02-21 00:23:56 + 7283 9305 新人 1200 redeem_reward batch_redeem_2_items 02-21 00:25:29 + 7287 9305 新人 2200 redeem_reward batch_redeem_3_items 02-21 00:27:29 + 7290 9305 新人 1200 redeem_reward batch_redeem_2_items 02-21 00:28:37 + 7293 9305 新人 9400 redeem_reward batch_redeem_3_items 02-21 00:29:35 + 7296 9305 新人 2200 redeem_reward batch_redeem_2_items 02-21 00:31:46 + 7297 9305 新人 1000 redeem_reward batch_redeem_2_items 02-21 00:34:02 + 7298 9305 新人 300 redeem_reward batch_redeem_1_items 02-21 00:34:38 + 7299 9305 新人 300 redeem_reward batch_redeem_1_items 02-21 00:35:04 + 7314 9305 新人 4000 redeem_reward batch_redeem_1_items 02-21 01:01:10 + 7319 9305 新人 37500 redeem_reward batch_redeem_1_items 02-21 01:17:13 + 7326 9305 新人 1500 redeem_reward batch_redeem_1_items 02-21 01:34:59 + 7329 9305 新人 450 redeem_reward batch_redeem_2_items 02-21 01:39:24 + 7349 9305 新人 60 redeem_reward batch_redeem_1_items 02-21 01:57:32 + 7427 9305 新人 9500 redeem_reward batch_redeem_1_items 02-21 04:51:41 + 7499 9305 新人 300 redeem_reward batch_redeem_1_items 02-21 17:10:25 + 7500 9305 新人 300 redeem_reward batch_redeem_1_items 02-21 17:11:18 + 7501 9305 新人 16000 redeem_reward batch_redeem_1_items 02-21 17:12:32 + 7510 9305 新人 600 redeem_reward batch_redeem_1_items 02-21 19:33:02 + 7512 9305 新人 300 redeem_reward batch_redeem_1_items 02-21 19:34:40 + 7587 9305 新人 600 redeem_reward batch_redeem_1_items 02-22 00:20:30 + 7622 9305 新人 250 redeem_reward batch_redeem_1_items 02-22 15:28:51 + 7661 9305 新人 450 redeem_reward batch_redeem_3_items 02-23 05:16:03 + 7662 9305 新人 300 redeem_reward batch_redeem_1_items 02-23 05:16:53 + 7663 9305 新人 150 redeem_reward batch_redeem_1_items 02-23 05:17:45 + 7664 9305 新人 1200 redeem_reward batch_redeem_2_items 02-23 05:19:32 + 7665 9305 新人 600 redeem_reward batch_redeem_3_items 02-23 05:20:45 + 7666 9305 新人 500 redeem_reward batch_redeem_3_items 02-23 08:13:57 + 7667 9305 新人 1100 redeem_reward batch_redeem_6_items 02-23 08:15:52 + 7668 9305 新人 750 redeem_reward batch_redeem_5_items 02-23 08:16:59 + 7669 9305 新人 150 redeem_reward batch_redeem_1_items 02-23 08:17:44 + 7670 9305 新人 1500 redeem_reward batch_redeem_1_items 02-23 08:19:21 + 7694 9305 新人 150 redeem_reward batch_redeem_1_items 02-23 17:22:54 + 7695 9305 新人 73100 redeem_reward batch_redeem_5_items 02-23 17:23:01 + 7696 9305 新人 4100 redeem_reward batch_redeem_3_items 02-23 17:27:01 + 7697 9305 新人 1500 redeem_reward batch_redeem_1_items 02-23 17:27:44 + 7698 9305 新人 450 redeem_reward batch_redeem_3_items 02-23 17:28:31 + 7699 9305 新人 300 redeem_reward batch_redeem_1_items 02-23 17:29:13 + 7700 9305 新人 200 redeem_reward batch_redeem_1_items 02-23 17:29:39 + 7701 9305 新人 150 redeem_reward batch_redeem_1_items 02-23 17:30:19 + 7702 9305 新人 300 redeem_reward batch_redeem_1_items 02-23 17:30:55 + 7706 9305 新人 150 redeem_reward batch_redeem_1_items 02-23 17:32:05 + 用户 9305 小计: 368370 积分 (3683.70 元) + + 6752 9336 有冰的帝君 128200 redeem_reward batch_redeem_370_items 02-18 22:22:59 + 6768 9336 有冰的帝君 400 redeem_reward batch_redeem_4_items 02-18 23:04:11 + 6782 9336 有冰的帝君 12400 redeem_reward batch_redeem_36_items 02-18 23:24:51 + 6785 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-18 23:31:18 + 6834 9336 有冰的帝君 3900 redeem_reward batch_redeem_53_items 02-19 02:15:50 + 6971 9336 有冰的帝君 3000 redeem_reward batch_redeem_10_items 02-19 15:11:08 + 6972 9336 有冰的帝君 3000 redeem_reward batch_redeem_10_items 02-19 15:15:26 + 6979 9336 有冰的帝君 6000 redeem_reward batch_redeem_20_items 02-19 15:24:46 + 6983 9336 有冰的帝君 7800 redeem_reward batch_redeem_26_items 02-19 15:32:13 + 7102 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 02-20 12:32:28 + 7103 9336 有冰的帝君 400 redeem_reward batch_redeem_1_items 02-20 12:33:17 + 7104 9336 有冰的帝君 2900 redeem_reward batch_redeem_5_items 02-20 12:36:42 + 7105 9336 有冰的帝君 1700 redeem_reward batch_redeem_5_items 02-20 12:42:30 + 7106 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-20 12:43:46 + 7107 9336 有冰的帝君 500 redeem_reward batch_redeem_1_items 02-20 12:44:22 + 7139 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 02-20 20:08:35 + 7151 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-20 20:29:30 + 7153 9336 有冰的帝君 66750 redeem_reward batch_redeem_69_items 02-20 21:00:50 + 7155 9336 有冰的帝君 1200 redeem_reward batch_redeem_20_items 02-20 21:03:30 + 7230 9336 有冰的帝君 1600 redeem_reward batch_redeem_5_items 02-21 00:00:56 + 7231 9336 有冰的帝君 2000 redeem_reward batch_redeem_1_items 02-21 00:01:45 + 7232 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-21 00:02:36 + 7234 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 02-21 00:03:23 + 7236 9336 有冰的帝君 500 redeem_reward batch_redeem_1_items 02-21 00:04:31 + 7238 9336 有冰的帝君 6000 redeem_reward batch_redeem_1_items 02-21 00:05:11 + 7246 9336 有冰的帝君 2300 redeem_reward batch_redeem_4_items 02-21 00:07:26 + 7251 9336 有冰的帝君 1300 redeem_reward batch_redeem_2_items 02-21 00:09:27 + 7255 9336 有冰的帝君 1000 redeem_reward batch_redeem_2_items 02-21 00:12:36 + 7265 9336 有冰的帝君 12200 redeem_reward batch_redeem_20_items 02-21 00:18:52 + 7277 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-21 00:22:47 + 7278 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-21 00:22:56 + 7281 9336 有冰的帝君 100 redeem_reward batch_redeem_1_items 02-21 00:24:44 + 7285 9336 有冰的帝君 650 redeem_reward batch_redeem_1_items 02-21 00:26:40 + 7311 9336 有冰的帝君 180 redeem_reward batch_redeem_3_items 02-21 00:55:14 + 7315 9336 有冰的帝君 5500 redeem_reward batch_redeem_3_items 02-21 01:04:22 + 7316 9336 有冰的帝君 1200 redeem_reward batch_redeem_3_items 02-21 01:13:51 + 7318 9336 有冰的帝君 32000 redeem_reward batch_redeem_1_items 02-21 01:16:27 + 7320 9336 有冰的帝君 360 redeem_reward batch_redeem_6_items 02-21 01:21:08 + 7603 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-22 01:44:31 + 7691 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-23 15:48:11 + 7692 9336 有冰的帝君 400 redeem_reward batch_redeem_1_items 02-23 15:51:52 + 7693 9336 有冰的帝君 400 redeem_reward batch_redeem_1_items 02-23 15:52:32 + 7757 9336 有冰的帝君 9800 redeem_reward batch_redeem_10_items 02-24 00:37:05 + 7759 9336 有冰的帝君 18000 redeem_reward batch_redeem_300_items 02-24 01:07:27 + 7766 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 02-24 01:46:21 + 7774 9336 有冰的帝君 4000 redeem_reward batch_redeem_1_items 02-24 02:42:09 + 7778 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 02-24 02:44:36 + 7817 9336 有冰的帝君 1300 redeem_reward batch_redeem_2_items 02-24 18:26:58 + 7843 9336 有冰的帝君 38200 redeem_reward batch_redeem_38_items 02-24 22:44:33 + 7859 9336 有冰的帝君 31200 redeem_reward batch_redeem_38_items 02-25 02:50:57 + 7861 9336 有冰的帝君 2700 redeem_reward batch_redeem_7_items 02-25 02:57:48 + 7863 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-25 03:25:08 + 7899 9336 有冰的帝君 2190 redeem_reward batch_redeem_1_items 02-26 02:09:28 + 7901 9336 有冰的帝君 7300 redeem_reward batch_redeem_1_items 02-26 02:11:31 + 7906 9336 有冰的帝君 4500 redeem_reward batch_redeem_15_items 02-26 02:20:43 + 7909 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 02-26 02:22:07 + 7910 9336 有冰的帝君 1850 redeem_reward batch_redeem_2_items 02-26 02:23:31 + 7912 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-26 02:24:06 + 7914 9336 有冰的帝君 1800 redeem_reward batch_redeem_1_items 02-26 02:25:05 + 7916 9336 有冰的帝君 2190 redeem_reward batch_redeem_1_items 02-26 02:25:54 + 7918 9336 有冰的帝君 2190 redeem_reward batch_redeem_1_items 02-26 02:26:38 + 7920 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-26 02:27:27 + 7927 9336 有冰的帝君 4900 redeem_reward batch_redeem_10_items 02-26 02:30:42 + 7928 9336 有冰的帝君 250 redeem_reward batch_redeem_1_items 02-26 02:32:19 + 7929 9336 有冰的帝君 600 redeem_reward batch_redeem_3_items 02-26 02:33:20 + 7932 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-26 03:04:11 + 7933 9336 有冰的帝君 4000 redeem_reward batch_redeem_1_items 02-26 03:23:39 + 7956 9336 有冰的帝君 7300 redeem_reward batch_redeem_1_items 02-26 17:40:17 + 7958 9336 有冰的帝君 5000 redeem_reward batch_redeem_1_items 02-26 17:41:14 + 7960 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-26 17:43:08 + 7962 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-26 17:43:40 + 7964 9336 有冰的帝君 1800 redeem_reward batch_redeem_1_items 02-26 17:44:17 + 7966 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-26 17:44:54 + 7968 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-26 17:45:31 + 7970 9336 有冰的帝君 5000 redeem_reward batch_redeem_1_items 02-26 17:47:37 + 7971 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-26 17:48:49 + 7973 9336 有冰的帝君 1800 redeem_reward batch_redeem_1_items 02-26 17:49:52 + 7974 9336 有冰的帝君 650 redeem_reward batch_redeem_1_items 02-26 17:50:44 + 7975 9336 有冰的帝君 17000 redeem_reward batch_redeem_1_items 02-26 17:52:35 + 7976 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 02-26 17:53:25 + 7977 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 02-26 17:53:55 + 7978 9336 有冰的帝君 800 redeem_reward batch_redeem_1_items 02-26 17:54:48 + 7979 9336 有冰的帝君 1500 redeem_reward batch_redeem_1_items 02-26 17:55:36 + 7980 9336 有冰的帝君 1000 redeem_reward batch_redeem_1_items 02-26 17:56:08 + 7981 9336 有冰的帝君 400 redeem_reward batch_redeem_1_items 02-26 17:56:40 + 7982 9336 有冰的帝君 1000 redeem_reward batch_redeem_1_items 02-26 17:57:14 + 7983 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-26 17:57:47 + 7984 9336 有冰的帝君 1500 redeem_reward batch_redeem_3_items 02-26 17:58:45 + 7985 9336 有冰的帝君 700 redeem_reward batch_redeem_2_items 02-26 17:59:34 + 7986 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-26 18:00:02 + 7987 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 02-26 18:00:46 + 8001 9336 有冰的帝君 233700 redeem_reward batch_redeem_426_items 02-26 22:17:58 + 8010 9336 有冰的帝君 48000 redeem_reward batch_redeem_3_items 02-26 23:53:16 + 8012 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-27 01:18:21 + 8013 9336 有冰的帝君 1600 redeem_reward batch_redeem_4_items 02-27 03:16:44 + 8067 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-27 20:39:27 + 8068 9336 有冰的帝君 500 redeem_reward batch_redeem_1_items 02-27 20:44:54 + 8077 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 02-27 22:40:17 + 8089 9336 有冰的帝君 500 redeem_reward batch_redeem_1_items 02-28 01:00:59 + 8103 9336 有冰的帝君 800 redeem_reward batch_redeem_1_items 02-28 01:55:41 + 8104 9336 有冰的帝君 4000 redeem_reward batch_redeem_1_items 02-28 01:56:19 + 8106 9336 有冰的帝君 2000 redeem_reward batch_redeem_1_items 02-28 01:57:39 + 8108 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 02-28 01:58:22 + 8110 9336 有冰的帝君 1800 redeem_reward batch_redeem_1_items 02-28 01:59:07 + 8112 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 02-28 02:00:17 + 8114 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 02-28 02:00:47 + 8128 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-28 12:21:55 + 8130 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 02-28 12:22:31 + 8131 9336 有冰的帝君 147100 redeem_reward batch_redeem_8_items 02-28 12:51:51 + 8132 9336 有冰的帝君 247600 redeem_reward batch_redeem_38_items 02-28 12:54:46 + 8137 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 02-28 16:54:42 + 8141 9336 有冰的帝君 1200 redeem_reward batch_redeem_4_items 02-28 19:57:16 + 8195 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-01 05:35:16 + 8196 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-01 05:35:59 + 8197 9336 有冰的帝君 800 redeem_reward batch_redeem_1_items 03-01 05:36:38 + 8200 9336 有冰的帝君 2400 redeem_reward batch_redeem_8_items 03-01 10:03:51 + 8202 9336 有冰的帝君 20800 redeem_reward batch_redeem_1_items 03-01 10:04:50 + 8203 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 03-01 10:08:35 + 8204 9336 有冰的帝君 800 redeem_reward batch_redeem_1_items 03-01 10:14:01 + 8205 9336 有冰的帝君 4000 redeem_reward batch_redeem_1_items 03-01 10:15:11 + 8206 9336 有冰的帝君 1500 redeem_reward batch_redeem_1_items 03-01 10:16:19 + 8207 9336 有冰的帝君 4000 redeem_reward batch_redeem_1_items 03-01 10:17:22 + 8208 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-01 10:26:09 + 8209 9336 有冰的帝君 100 redeem_reward batch_redeem_1_items 03-01 10:29:24 + 8210 9336 有冰的帝君 6200 redeem_reward batch_redeem_10_items 03-01 10:30:15 + 8211 9336 有冰的帝君 1000 redeem_reward batch_redeem_10_items 03-01 10:31:39 + 8213 9336 有冰的帝君 800 redeem_reward batch_redeem_1_items 03-01 10:34:17 + 8214 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-01 10:44:11 + 8215 9336 有冰的帝君 800 redeem_reward batch_redeem_1_items 03-01 10:44:42 + 8217 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-01 16:07:28 + 8218 9336 有冰的帝君 400 redeem_reward batch_redeem_1_items 03-01 16:08:53 + 8219 9336 有冰的帝君 1000 redeem_reward batch_redeem_1_items 03-01 16:10:13 + 8220 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-01 16:10:46 + 8221 9336 有冰的帝君 1000 redeem_reward batch_redeem_1_items 03-01 16:11:37 + 8223 9336 有冰的帝君 5300 redeem_reward batch_redeem_6_items 03-01 16:15:41 + 8230 9336 有冰的帝君 42000 redeem_reward batch_redeem_1_items 03-01 20:25:47 + 8243 9336 有冰的帝君 4600 redeem_reward batch_redeem_10_items 03-01 22:23:30 + 8246 9336 有冰的帝君 6200 redeem_reward batch_redeem_5_items 03-01 22:25:56 + 8249 9336 有冰的帝君 1500 redeem_reward batch_redeem_1_items 03-01 22:32:51 + 8252 9336 有冰的帝君 600 redeem_reward batch_redeem_10_items 03-01 22:38:48 + 8256 9336 有冰的帝君 5200 redeem_reward batch_redeem_3_items 03-01 23:56:51 + 8258 9336 有冰的帝君 3600 redeem_reward batch_redeem_10_items 03-01 23:58:17 + 8260 9336 有冰的帝君 2200 redeem_reward batch_redeem_5_items 03-02 00:00:14 + 8262 9336 有冰的帝君 2400 redeem_reward batch_redeem_6_items 03-02 00:03:27 + 8264 9336 有冰的帝君 2100 redeem_reward batch_redeem_6_items 03-02 00:04:08 + 8266 9336 有冰的帝君 900 redeem_reward batch_redeem_3_items 03-02 00:05:01 + 8270 9336 有冰的帝君 1200 redeem_reward batch_redeem_12_items 03-02 00:54:57 + 8272 9336 有冰的帝君 600 redeem_reward batch_redeem_10_items 03-02 00:55:39 + 8273 9336 有冰的帝君 900 redeem_reward batch_redeem_5_items 03-02 00:58:18 + 8275 9336 有冰的帝君 700 redeem_reward batch_redeem_2_items 03-02 00:59:31 + 8277 9336 有冰的帝君 800 redeem_reward batch_redeem_1_items 03-02 01:00:14 + 8279 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-02 01:00:59 + 8280 9336 有冰的帝君 2100 redeem_reward batch_redeem_5_items 03-02 01:15:20 + 8282 9336 有冰的帝君 1900 redeem_reward batch_redeem_5_items 03-02 01:21:49 + 8285 9336 有冰的帝君 4800 redeem_reward batch_redeem_14_items 03-02 01:25:22 + 8287 9336 有冰的帝君 3200 redeem_reward batch_redeem_5_items 03-02 01:27:54 + 8289 9336 有冰的帝君 5400 redeem_reward batch_redeem_3_items 03-02 01:29:19 + 8292 9336 有冰的帝君 48350 redeem_reward batch_redeem_100_items 03-02 02:02:30 + 8293 9336 有冰的帝君 4490 redeem_reward batch_redeem_2_items 03-02 02:02:56 + 8294 9336 有冰的帝君 360 redeem_reward batch_redeem_6_items 03-02 02:25:26 + 8295 9336 有冰的帝君 120 redeem_reward batch_redeem_2_items 03-02 02:26:08 + 8313 9336 有冰的帝君 150 redeem_reward batch_redeem_1_items 03-02 14:22:48 + 8314 9336 有冰的帝君 2300 redeem_reward batch_redeem_2_items 03-02 14:25:36 + 8316 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 03-02 14:26:32 + 8318 9336 有冰的帝君 900 redeem_reward batch_redeem_3_items 03-02 14:27:23 + 8320 9336 有冰的帝君 600 redeem_reward batch_redeem_10_items 03-02 14:28:00 + 8322 9336 有冰的帝君 600 redeem_reward batch_redeem_10_items 03-02 14:28:25 + 8323 9336 有冰的帝君 400 redeem_reward batch_redeem_1_items 03-02 14:35:24 + 8325 9336 有冰的帝君 700 redeem_reward batch_redeem_2_items 03-02 14:36:24 + 8326 9336 有冰的帝君 120 redeem_reward batch_redeem_2_items 03-02 14:37:04 + 8328 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-02 14:37:38 + 8334 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 03-02 21:40:05 + 8335 9336 有冰的帝君 150 redeem_reward batch_redeem_1_items 03-02 21:40:25 + 8337 9336 有冰的帝君 2000 redeem_reward batch_redeem_4_items 03-02 21:41:59 + 8339 9336 有冰的帝君 1400 redeem_reward batch_redeem_2_items 03-02 21:42:59 + 8341 9336 有冰的帝君 1400 redeem_reward batch_redeem_2_items 03-02 21:44:42 + 8343 9336 有冰的帝君 5200 redeem_reward batch_redeem_11_items 03-02 21:49:16 + 8345 9336 有冰的帝君 9050 redeem_reward batch_redeem_16_items 03-02 21:54:33 + 8347 9336 有冰的帝君 27700 redeem_reward batch_redeem_20_items 03-02 22:00:17 + 8348 9336 有冰的帝君 600 redeem_reward batch_redeem_10_items 03-02 22:03:28 + 8349 9336 有冰的帝君 300 redeem_reward batch_redeem_5_items 03-02 22:03:44 + 8350 9336 有冰的帝君 180 redeem_reward batch_redeem_3_items 03-02 22:04:02 + 8357 9336 有冰的帝君 28800 redeem_reward batch_redeem_1_items 03-02 22:41:40 + 8360 9336 有冰的帝君 10100 redeem_reward batch_redeem_10_items 03-02 22:43:38 + 8363 9336 有冰的帝君 38000 redeem_reward batch_redeem_1_items 03-02 22:44:14 + 8368 9336 有冰的帝君 1500 redeem_reward batch_redeem_5_items 03-02 22:54:13 + 8370 9336 有冰的帝君 1500 redeem_reward batch_redeem_4_items 03-02 22:55:54 + 8371 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-02 22:56:50 + 8373 9336 有冰的帝君 8100 redeem_reward batch_redeem_4_items 03-02 22:58:08 + 8381 9336 有冰的帝君 16700 redeem_reward batch_redeem_2_items 03-02 23:15:20 + 8382 9336 有冰的帝君 900 redeem_reward batch_redeem_3_items 03-02 23:19:32 + 8383 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-02 23:19:47 + 8384 9336 有冰的帝君 900 redeem_reward batch_redeem_3_items 03-02 23:20:06 + 8387 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-02 23:37:33 + 8388 9336 有冰的帝君 16700 redeem_reward batch_redeem_10_items 03-02 23:38:55 + 8391 9336 有冰的帝君 4400 redeem_reward batch_redeem_10_items 03-02 23:43:50 + 8392 9336 有冰的帝君 38000 redeem_reward batch_redeem_1_items 03-02 23:45:56 + 8396 9336 有冰的帝君 5890 redeem_reward batch_redeem_1_items 03-02 23:59:49 + 8397 9336 有冰的帝君 3000 redeem_reward batch_redeem_10_items 03-03 00:08:06 + 8398 9336 有冰的帝君 9750 redeem_reward batch_redeem_10_items 03-03 00:08:25 + 8399 9336 有冰的帝君 3000 redeem_reward batch_redeem_10_items 03-03 00:08:47 + 8400 9336 有冰的帝君 11200 redeem_reward batch_redeem_10_items 03-03 00:09:13 + 8403 9336 有冰的帝君 3300 redeem_reward batch_redeem_4_items 03-03 00:12:04 + 8405 9336 有冰的帝君 2000 redeem_reward batch_redeem_6_items 03-03 00:12:45 + 8407 9336 有冰的帝君 2800 redeem_reward batch_redeem_6_items 03-03 00:13:24 + 8409 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-03 00:31:41 + 8410 9336 有冰的帝君 650 redeem_reward batch_redeem_1_items 03-03 00:34:40 + 8411 9336 有冰的帝君 2000 redeem_reward batch_redeem_1_items 03-03 00:35:39 + 8412 9336 有冰的帝君 1800 redeem_reward batch_redeem_1_items 03-03 00:36:28 + 8413 9336 有冰的帝君 1800 redeem_reward batch_redeem_1_items 03-03 00:37:14 + 8415 9336 有冰的帝君 14900 redeem_reward batch_redeem_3_items 03-03 00:46:45 + 8416 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-03 00:47:34 + 8417 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-03 00:47:56 + 8419 9336 有冰的帝君 16000 redeem_reward batch_redeem_1_items 03-03 01:02:05 + 8420 9336 有冰的帝君 3050 redeem_reward batch_redeem_7_items 03-03 01:02:56 + 8426 9336 有冰的帝君 6500 redeem_reward batch_redeem_20_items 03-03 02:20:31 + 8427 9336 有冰的帝君 18300 redeem_reward batch_redeem_1_items 03-03 02:20:54 + 8430 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 03-03 02:21:56 + 8432 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 03-03 02:22:22 + 8433 9336 有冰的帝君 54000 redeem_reward batch_redeem_2_items 03-03 02:23:34 + 8438 9336 有冰的帝君 91950 redeem_reward batch_redeem_110_items 03-03 02:27:02 + 8451 9336 有冰的帝君 5300 redeem_reward batch_redeem_13_items 03-03 02:36:10 + 8453 9336 有冰的帝君 3500 redeem_reward batch_redeem_10_items 03-03 02:37:05 + 8456 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-03 02:37:43 + 8461 9336 有冰的帝君 6600 redeem_reward batch_redeem_18_items 03-03 02:39:25 + 8463 9336 有冰的帝君 4600 redeem_reward batch_redeem_10_items 03-03 02:39:53 + 8467 9336 有冰的帝君 4650 redeem_reward batch_redeem_10_items 03-03 02:40:27 + 8469 9336 有冰的帝君 6500 redeem_reward batch_redeem_13_items 03-03 02:41:34 + 8472 9336 有冰的帝君 4900 redeem_reward batch_redeem_10_items 03-03 02:42:05 + 8475 9336 有冰的帝君 3400 redeem_reward batch_redeem_10_items 03-03 02:43:00 + 8476 9336 有冰的帝君 500 redeem_reward batch_redeem_1_items 03-03 02:43:18 + 8479 9336 有冰的帝君 3500 redeem_reward batch_redeem_10_items 03-03 02:43:41 + 8482 9336 有冰的帝君 3600 redeem_reward batch_redeem_3_items 03-03 02:45:06 + 8487 9336 有冰的帝君 8550 redeem_reward batch_redeem_2_items 03-03 02:46:44 + 8489 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-03 02:47:28 + 8491 9336 有冰的帝君 13900 redeem_reward batch_redeem_20_items 03-03 02:48:02 + 8493 9336 有冰的帝君 58300 redeem_reward batch_redeem_20_items 03-03 02:49:53 + 8503 9336 有冰的帝君 3600 redeem_reward batch_redeem_10_items 03-03 10:42:57 + 8504 9336 有冰的帝君 2500 redeem_reward batch_redeem_2_items 03-03 10:59:41 + 8507 9336 有冰的帝君 5100 redeem_reward batch_redeem_9_items 03-03 11:12:36 + 8511 9336 有冰的帝君 3600 redeem_reward batch_redeem_60_items 03-03 11:40:18 + 8513 9336 有冰的帝君 1500 redeem_reward batch_redeem_5_items 03-03 11:40:51 + 8515 9336 有冰的帝君 1500 redeem_reward batch_redeem_5_items 03-03 11:41:27 + 8517 9336 有冰的帝君 800 redeem_reward batch_redeem_2_items 03-03 11:41:59 + 8519 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-03 11:42:24 + 8521 9336 有冰的帝君 800 redeem_reward batch_redeem_1_items 03-03 11:43:00 + 8528 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-03 21:43:47 + 8530 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-03 21:44:24 + 8532 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-03 21:44:55 + 8533 9336 有冰的帝君 3350 redeem_reward batch_redeem_5_items 03-03 22:31:54 + 8536 9336 有冰的帝君 14500 redeem_reward batch_redeem_6_items 03-03 22:33:54 + 8537 9336 有冰的帝君 500 redeem_reward batch_redeem_1_items 03-03 22:35:41 + 8538 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 03-03 22:36:57 + 8539 9336 有冰的帝君 800 redeem_reward batch_redeem_1_items 03-03 22:37:27 + 8540 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-03 22:37:57 + 8541 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-03 22:38:41 + 8544 9336 有冰的帝君 32900 redeem_reward batch_redeem_20_items 03-03 22:41:41 + 8545 9336 有冰的帝君 2300 redeem_reward batch_redeem_1_items 03-03 22:42:34 + 8552 9336 有冰的帝君 8950 redeem_reward batch_redeem_2_items 03-03 23:34:26 + 8558 9336 有冰的帝君 40000 redeem_reward batch_redeem_1_items 03-03 23:49:53 + 8560 9336 有冰的帝君 66850 redeem_reward batch_redeem_66_items 03-03 23:53:43 + 8565 9336 有冰的帝君 78500 redeem_reward batch_redeem_1_items 03-04 00:21:14 + 8567 9336 有冰的帝君 5000 redeem_reward batch_redeem_1_items 03-04 01:23:07 + 8568 9336 有冰的帝君 38000 redeem_reward batch_redeem_1_items 03-04 01:28:08 + 8571 9336 有冰的帝君 98000 redeem_reward batch_redeem_1_items 03-04 01:36:21 + 8576 9336 有冰的帝君 45700 redeem_reward batch_redeem_6_items 03-04 02:09:02 + 8584 9336 有冰的帝君 3950 redeem_reward batch_redeem_10_items 03-04 05:24:32 + 8588 9336 有冰的帝君 31600 redeem_reward batch_redeem_3_items 03-04 07:50:39 + 8590 9336 有冰的帝君 6500 redeem_reward batch_redeem_2_items 03-04 07:53:26 + 8592 9336 有冰的帝君 1900 redeem_reward batch_redeem_5_items 03-04 07:55:43 + 8594 9336 有冰的帝君 1500 redeem_reward batch_redeem_4_items 03-04 08:06:33 + 8596 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-04 08:07:31 + 8598 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-04 08:08:00 + 8600 9336 有冰的帝君 21750 redeem_reward batch_redeem_60_items 03-04 08:09:30 + 8601 9336 有冰的帝君 900 redeem_reward batch_redeem_3_items 03-04 08:27:19 + 8602 9336 有冰的帝君 1000 redeem_reward batch_redeem_3_items 03-04 08:27:38 + 8603 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-04 08:27:51 + 8604 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-04 08:28:06 + 8605 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-04 08:55:42 + 8606 9336 有冰的帝君 60 redeem_reward batch_redeem_1_items 03-04 09:43:25 + 8607 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-04 09:43:49 + 8608 9336 有冰的帝君 1000 redeem_reward batch_redeem_1_items 03-04 09:44:46 + 8610 9336 有冰的帝君 6300 redeem_reward batch_redeem_2_items 03-04 09:45:21 + 8611 9336 有冰的帝君 1200 redeem_reward batch_redeem_3_items 03-04 10:02:51 + 8613 9336 有冰的帝君 3150 redeem_reward batch_redeem_6_items 03-04 10:03:38 + 8614 9336 有冰的帝君 900 redeem_reward batch_redeem_3_items 03-04 10:03:59 + 8615 9336 有冰的帝君 1000 redeem_reward batch_redeem_1_items 03-04 10:06:55 + 8632 9336 有冰的帝君 1200 redeem_reward batch_redeem_3_items 03-04 17:26:59 + 8634 9336 有冰的帝君 900 redeem_reward batch_redeem_2_items 03-04 17:28:15 + 8636 9336 有冰的帝君 1200 redeem_reward batch_redeem_3_items 03-04 17:28:40 + 8638 9336 有冰的帝君 15800 redeem_reward batch_redeem_5_items 03-04 17:29:38 + 8639 9336 有冰的帝君 500 redeem_reward batch_redeem_1_items 03-04 17:32:04 + 8642 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-04 19:46:17 + 8644 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-04 19:47:49 + 8645 9336 有冰的帝君 2200 redeem_reward batch_redeem_5_items 03-04 20:26:11 + 8647 9336 有冰的帝君 1200 redeem_reward batch_redeem_2_items 03-04 20:27:24 + 8650 9336 有冰的帝君 1200 redeem_reward batch_redeem_2_items 03-04 20:28:21 + 8652 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-04 20:29:03 + 8654 9336 有冰的帝君 30000 redeem_reward batch_redeem_1_items 03-04 20:30:06 + 8657 9336 有冰的帝君 20600 redeem_reward batch_redeem_20_items 03-04 20:50:54 + 8659 9336 有冰的帝君 12200 redeem_reward batch_redeem_19_items 03-04 20:57:11 + 8663 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-04 21:10:41 + 8665 9336 有冰的帝君 13950 redeem_reward batch_redeem_18_items 03-04 21:12:06 + 8676 9336 有冰的帝君 2100 redeem_reward batch_redeem_5_items 03-04 22:16:40 + 8678 9336 有冰的帝君 9700 redeem_reward batch_redeem_6_items 03-04 22:17:34 + 8680 9336 有冰的帝君 6700 redeem_reward batch_redeem_20_items 03-04 22:18:33 + 8682 9336 有冰的帝君 10000 redeem_reward batch_redeem_13_items 03-04 22:19:09 + 8685 9336 有冰的帝君 12260 redeem_reward batch_redeem_21_items 03-04 22:20:01 + 8687 9336 有冰的帝君 4500 redeem_reward batch_redeem_5_items 03-04 22:23:28 + 8688 9336 有冰的帝君 24500 redeem_reward batch_redeem_10_items 03-04 22:26:14 + 8694 9336 有冰的帝君 66000 redeem_reward batch_redeem_2_items 03-04 22:31:39 + 8698 9336 有冰的帝君 112500 redeem_reward batch_redeem_1_items 03-04 22:42:05 + 8699 9336 有冰的帝君 12800 redeem_reward batch_redeem_1_items 03-04 22:42:23 + 8703 9336 有冰的帝君 1900 redeem_reward batch_redeem_5_items 03-04 22:44:40 + 8704 9336 有冰的帝君 900 redeem_reward batch_redeem_3_items 03-04 22:44:57 + 8706 9336 有冰的帝君 900 redeem_reward batch_redeem_3_items 03-04 22:45:15 + 8707 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-04 22:45:30 + 8725 9336 有冰的帝君 2300 redeem_reward batch_redeem_7_items 03-04 23:15:46 + 8728 9336 有冰的帝君 1500 redeem_reward batch_redeem_4_items 03-04 23:16:27 + 8730 9336 有冰的帝君 1400 redeem_reward batch_redeem_4_items 03-04 23:17:12 + 8731 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-05 00:02:25 + 8733 9336 有冰的帝君 1800 redeem_reward batch_redeem_2_items 03-05 00:02:56 + 8735 9336 有冰的帝君 1400 redeem_reward batch_redeem_4_items 03-05 00:03:40 + 8737 9336 有冰的帝君 1700 redeem_reward batch_redeem_4_items 03-05 00:05:43 + 8739 9336 有冰的帝君 2600 redeem_reward batch_redeem_4_items 03-05 00:07:12 + 8741 9336 有冰的帝君 2000 redeem_reward batch_redeem_6_items 03-05 00:09:00 + 8742 9336 有冰的帝君 1800 redeem_reward batch_redeem_1_items 03-05 00:10:11 + 8744 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 03-05 00:11:33 + 8745 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 03-05 00:12:02 + 8747 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 03-05 00:12:59 + 8749 9336 有冰的帝君 1250 redeem_reward batch_redeem_1_items 03-05 00:13:32 + 8752 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 03-05 00:14:04 + 8754 9336 有冰的帝君 300 redeem_reward batch_redeem_5_items 03-05 00:14:43 + 8756 9336 有冰的帝君 300 redeem_reward batch_redeem_5_items 03-05 00:15:05 + 8758 9336 有冰的帝君 300 redeem_reward batch_redeem_5_items 03-05 00:15:26 + 8805 9336 有冰的帝君 7100 redeem_reward batch_redeem_4_items 03-05 12:29:59 + 8810 9336 有冰的帝君 19100 redeem_reward batch_redeem_13_items 03-05 20:10:49 + 8811 9336 有冰的帝君 60 redeem_reward batch_redeem_1_items 03-05 20:11:13 + 8821 9336 有冰的帝君 8800 redeem_reward batch_redeem_18_items 03-05 21:15:59 + 8823 9336 有冰的帝君 9400 redeem_reward batch_redeem_10_items 03-05 21:16:23 + 8841 9336 有冰的帝君 900 redeem_reward batch_redeem_2_items 03-05 23:12:26 + 8843 9336 有冰的帝君 700 redeem_reward batch_redeem_2_items 03-05 23:12:59 + 8844 9336 有冰的帝君 42400 redeem_reward batch_redeem_20_items 03-05 23:19:00 + 8846 9336 有冰的帝君 20600 redeem_reward batch_redeem_10_items 03-05 23:20:06 + 8848 9336 有冰的帝君 12100 redeem_reward batch_redeem_11_items 03-05 23:21:01 + 8858 9336 有冰的帝君 800 redeem_reward batch_redeem_2_items 03-06 06:08:31 + 8860 9336 有冰的帝君 1300 redeem_reward batch_redeem_2_items 03-06 06:08:56 + 8862 9336 有冰的帝君 1400 redeem_reward batch_redeem_4_items 03-06 06:09:31 + 8864 9336 有冰的帝君 600 redeem_reward batch_redeem_2_items 03-06 06:50:26 + 8866 9336 有冰的帝君 500 redeem_reward batch_redeem_1_items 03-06 06:50:43 + 8868 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-06 06:51:26 + 8870 9336 有冰的帝君 600 redeem_reward batch_redeem_1_items 03-06 06:51:59 + 8872 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-06 06:52:29 + 8874 9336 有冰的帝君 300 redeem_reward batch_redeem_1_items 03-06 06:53:11 + 8897 9336 有冰的帝君 100 redeem_reward batch_redeem_1_items 03-07 02:14:57 + 8916 9336 有冰的帝君 4000 redeem_reward batch_redeem_1_items 03-07 17:31:30 + 8919 9336 有冰的帝君 11650 redeem_reward batch_redeem_10_items 03-07 17:38:52 + 用户 9336 小计: 3090650 积分 (30906.50 元) + + 7057 9365 未命名 1250 redeem_reward batch_redeem_1_items 02-19 22:48:37 + 7098 9365 未命名 1250 redeem_reward batch_redeem_1_items 02-20 09:19:37 + 7173 9365 未命名 7200 redeem_reward batch_redeem_1_items 02-20 23:13:03 + 7203 9365 未命名 100 redeem_reward batch_redeem_1_items 02-20 23:48:32 + 7514 9365 未命名 180 redeem_reward batch_redeem_3_items 02-21 22:05:14 + 7515 9365 未命名 60 redeem_reward batch_redeem_1_items 02-21 22:06:28 + 7516 9365 未命名 60 redeem_reward batch_redeem_1_items 02-21 22:07:25 + 7517 9365 未命名 60 redeem_reward batch_redeem_1_items 02-21 22:08:25 + 7522 9365 未命名 180 redeem_reward batch_redeem_3_items 02-21 22:10:20 + 7528 9365 未命名 320 redeem_reward batch_redeem_3_items 02-21 22:13:15 + 用户 9365 小计: 10660 积分 (106.60 元) + + 8693 9449 古利特使出了佛怒火莲 2500 redeem_reward batch_redeem_7_items 03-04 22:30:30 + 8695 9449 古利特使出了佛怒火莲 600 redeem_reward batch_redeem_2_items 03-04 22:35:23 + 8724 9449 古利特使出了佛怒火莲 1750 redeem_reward batch_redeem_2_items 03-04 23:15:24 + 8818 9449 古利特使出了佛怒火莲 600 redeem_reward batch_redeem_10_items 03-05 21:14:48 + 8900 9449 古利特使出了佛怒火莲 600 redeem_reward batch_redeem_2_items 03-07 07:46:59 + 用户 9449 小计: 6050 积分 (60.50 元) + + +【五】并发漏洞证据 — 同一资产被多次转赠 +---------------------------------------------------------------------------------------------------- + 资产ID 转赠次数 价值(元) 商品名称 转赠路径 + ----------------------------------------------------------------------------------------------- + 42746 4 375.00 MG 牛高达VER.KA 卡.. 9336→9116 | 9336→9116 | 9336→9116 | 9336→9116 + 46668 4 375.00 MG 牛高达VER.KA 卡.. 9210→9116 | 9210→9116 | 9210→9116 | 9210→9116 + 51038 3 605.00 PB限定 MG 红龙形态红龙.. 9210→9116 | 9116→9210 | 9210→9116 + 43304 3 375.00 MG 牛高达VER.KA 卡.. 9305→9116 | 9116→9305 | 9305→9116 + 44152 3 375.00 MG 牛高达VER.KA 卡.. 9248→9116 | 9248→9116 | 9248→9116 + 46445 3 375.00 MG 牛高达VER.KA 卡.. 9230→9116 | 9230→9116 | 9230→9116 + 46667 3 375.00 MG 牛高达VER.KA 卡.. 9210→9116 | 9210→9116 | 9210→9116 + 46506 2 375.00 MG 牛高达VER.KA 卡.. 9209→9116 | 9209→9116 + 46666 2 375.00 MG 牛高达VER.KA 卡.. 9210→9116 | 9210→9116 + 46669 2 375.00 MG 牛高达VER.KA 卡.. 9210→9116 | 9210→9116 + 46672 2 375.00 MG 牛高达VER.KA 卡.. 9210→9116 | 9210→9116 + 46673 2 375.00 MG 牛高达VER.KA 卡.. 9210→9116 | 9210→9116 + 46686 2 375.00 MG 牛高达VER.KA 卡.. 9210→9116 | 9210→9116 + + 共 13 个资产被多次转赠(并发漏洞证据) + + +【六】转赠关系网络汇总 +---------------------------------------------------------------------------------------------------- + 赠送方 → 接收方 转赠次 资产数 总价值(元) 首次转赠 末次转赠 + ------------------------------------------------------------------------------------------------------------------------ + 9210(非酋) → 9116(非洲人) 43 32 16670.00 02-21 23:49 03-03 00:04 + 9222(嗯?!!!!) → 9116(非洲人) 9 9 4655.00 02-17 11:38 02-21 15:12 + 9336(有冰的帝君) → 9110(极品官方内部号) 3 3 3710.00 02-24 01:23 02-28 20:00 + 9248(不出last退了) → 9116(非洲人) 7 5 3565.00 02-22 23:46 03-01 00:04 + 9138(卷卷大魔王) → 9116(非洲人) 2 2 3130.00 03-02 23:11 03-02 23:11 + 9336(有冰的帝君) → 9116(非洲人) 7 4 2950.00 02-21 14:53 03-04 02:36 + 9209(程c) → 9116(非洲人) 5 4 1875.00 02-21 16:49 02-21 16:52 + 9230(巨欧小肥龙) → 9116(非洲人) 5 3 1875.00 02-21 16:43 02-21 16:47 + 9305(新人) → 9116(非洲人) 3 2 1650.00 02-19 02:18 02-28 20:15 + 9199(欣喜的罗马里奥) → 9116(非洲人) 3 3 1058.00 03-05 19:40 03-05 19:41 + 9116(非洲人) → 9305(新人) 2 2 750.00 02-19 02:24 02-21 01:16 + 9116(非洲人) → 9210(非酋) 3 3 623.50 02-23 19:39 03-02 14:06 + 9222(嗯?!!!!) → 9347(约翰在武汉看电影) 1 1 420.00 02-26 16:02 02-26 16:02 + 9113(盖德·穆勒大力出..) → 9110(极品官方内部号) 17 17 418.50 01-24 22:59 03-05 22:21 + 9111(嘟嘟) → 9210(非酋) 1 1 320.00 03-03 11:27 03-03 11:27 + 9110(极品官方内部号) → 9160(肖申克在武汉看电..) 1 1 175.00 02-04 01:01 02-04 01:01 + 9230(巨欧小肥龙) → 9209(程c) 2 2 124.00 02-28 01:25 02-28 01:25 + 9330(拼搏的内马尔) → 9094(范巴斯滕救了一个..) 3 3 113.90 02-20 22:13 02-27 18:01 + 9280(诚心的肯尼) → 9222(嗯?!!!!) 8 8 84.50 02-17 23:37 02-17 23:41 + 9347(约翰在武汉看电影) → 9222(嗯?!!!!) 5 5 79.50 02-19 01:12 02-19 01:38 + 9110(极品官方内部号) → 9116(非洲人) 1 1 79.00 02-19 02:53 02-19 02:53 + 9110(极品官方内部号) → 9209(程c) 2 2 63.00 02-28 02:09 02-28 02:09 + 9111(嘟嘟) → 9336(有冰的帝君) 1 1 60.00 03-03 00:42 03-03 00:42 + 9336(有冰的帝君) → 9446(弗兰科望穿秋水) 1 1 29.00 03-03 11:03 03-03 11:03 + 9116(非洲人) → 9209(程c) 1 1 21.00 02-27 14:31 02-27 14:31 + 9110(极品官方内部号) → 9222(嗯?!!!!) 2 2 16.00 02-17 23:52 02-17 23:52 + 9094(范巴斯滕救了一个..) → 9449(古利特使出了佛怒..) 1 1 12.50 03-04 22:34 03-04 22:34 + 9210(非酋) → 9209(程c) 4 4 12.00 02-28 01:46 02-28 01:48 + 9116(非洲人) → 9110(极品官方内部号) 4 4 10.50 02-25 18:08 02-25 18:32 + 9116(非洲人) → 9248(不出last退了) 2 2 8.00 02-19 13:26 02-19 13:27 + 9209(程c) → 9210(非酋) 1 1 4.00 02-24 21:22 02-24 21:22 + 9117(小叶) → 9116(非洲人) 2 2 3.00 03-03 02:32 03-03 02:32 + 9209(程c) → 9110(极品官方内部号) 2 2 2.60 02-17 23:53 02-26 03:02 + 9110(极品官方内部号) → 9365(未命名) 1 1 1.00 02-20 23:37 02-20 23:37 + 9209(程c) → 9230(巨欧小肥龙) 1 1 0.60 02-21 00:21 02-21 00:21 + 9359(雅典娜心花怒放) → 9248(不出last退了) 1 1 0.60 03-03 12:14 03-03 12:14 + + +【七】用户积分余额 vs 薅取金额对比 +------------------------------------------------------------------------------------------ + 用户ID 昵称 薅取积分 薅取金额(元) 当前余额 可扣回? + ------------------------------------------------------------------------------------- + 9116 非洲人 1073700 10737.00 340 ⚠️ 仅可扣 340 + 9305 新人 75000 750.00 80 ⚠️ 仅可扣 80 + 9110 极品官方内部号 44660 446.60 11030 ⚠️ 仅可扣 11030 + 9209 程c 22000 220.00 39260 ✅ 可全额扣回 + 9222 嗯?!!!! 18000 180.00 580 ⚠️ 仅可扣 580 + 9336 有冰的帝君 6000 60.00 10770 ✅ 可全额扣回 + 9094 范巴斯滕救了一个美女 2500 25.00 160 ⚠️ 仅可扣 160 + 9210 非酋 2250 22.50 190 ⚠️ 仅可扣 190 + 9449 古利特使出了佛怒火莲 1250 12.50 1550 ✅ 可全额扣回 + 9248 不出last退了 860 8.60 53190 ✅ 可全额扣回 + 9365 未命名 100 1.00 10 ⚠️ 仅可扣 10 + 9230 巨欧小肥龙 60 0.60 80 ✅ 可全额扣回 + +======================================================================================================================== + 报告结束 +======================================================================================================================== diff --git a/internal/api/activity/issue_choices_app.go b/internal/api/activity/issue_choices_app.go index f5c1fbb..4bcfb76 100755 --- a/internal/api/activity/issue_choices_app.go +++ b/internal/api/activity/issue_choices_app.go @@ -1,11 +1,12 @@ package app import ( - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" "fmt" "net/http" "strconv" + + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" ) type listIssueChoicesResponse struct { diff --git a/internal/api/activity/lottery_app.go b/internal/api/activity/lottery_app.go index 5fad912..b78d512 100755 --- a/internal/api/activity/lottery_app.go +++ b/internal/api/activity/lottery_app.go @@ -1,11 +1,6 @@ package app import ( - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/validation" - "bindbox-game/internal/repository/mysql/dao" - "bindbox-game/internal/repository/mysql/model" "context" "crypto/hmac" "crypto/sha256" @@ -17,6 +12,11 @@ import ( "net/http" "time" + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/validation" + "bindbox-game/internal/repository/mysql/dao" + "bindbox-game/internal/repository/mysql/model" titlesvc "bindbox-game/internal/service/title" "gorm.io/gorm/clause" diff --git a/internal/api/activity/lottery_helper.go b/internal/api/activity/lottery_helper.go index 517812b..c25568a 100755 --- a/internal/api/activity/lottery_helper.go +++ b/internal/api/activity/lottery_helper.go @@ -1,11 +1,12 @@ package app import ( + "fmt" + "time" + "bindbox-game/internal/pkg/core" "bindbox-game/internal/pkg/util/remark" "bindbox-game/internal/repository/mysql/model" - "fmt" - "time" ) // couponJoinResult 优惠券联合查询结果 diff --git a/internal/api/activity/lottery_result_order_app.go b/internal/api/activity/lottery_result_order_app.go index 7f4b4fc..32e18bb 100755 --- a/internal/api/activity/lottery_result_order_app.go +++ b/internal/api/activity/lottery_result_order_app.go @@ -1,11 +1,6 @@ package app import ( - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/util/remark" - "bindbox-game/internal/pkg/validation" - "bindbox-game/internal/repository/mysql/model" "crypto/hmac" "crypto/sha256" "encoding/base64" @@ -13,6 +8,12 @@ import ( "encoding/json" "strings" "time" + + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/util/remark" + "bindbox-game/internal/pkg/validation" + "bindbox-game/internal/repository/mysql/model" ) type orderResultQuery struct { diff --git a/internal/api/activity/matching_game_app.go b/internal/api/activity/matching_game_app.go index 65884eb..900d055 100755 --- a/internal/api/activity/matching_game_app.go +++ b/internal/api/activity/matching_game_app.go @@ -1,13 +1,6 @@ package app import ( - "bindbox-game/configs" - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/validation" - "bindbox-game/internal/pkg/wechat" - "bindbox-game/internal/repository/mysql/model" - activitysvc "bindbox-game/internal/service/activity" "context" "crypto/rand" "encoding/binary" @@ -18,6 +11,14 @@ import ( "sort" "time" + "bindbox-game/configs" + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/validation" + "bindbox-game/internal/pkg/wechat" + "bindbox-game/internal/repository/mysql/model" + activitysvc "bindbox-game/internal/service/activity" + "github.com/redis/go-redis/v9" "go.uber.org/zap" ) diff --git a/internal/api/activity/matching_game_app_test.go b/internal/api/activity/matching_game_app_test.go index ac277ac..dcd1606 100755 --- a/internal/api/activity/matching_game_app_test.go +++ b/internal/api/activity/matching_game_app_test.go @@ -1,8 +1,9 @@ package app import ( - "bindbox-game/internal/repository/mysql/model" "testing" + + "bindbox-game/internal/repository/mysql/model" ) // TestSelectRewardExact 测试对对碰选奖逻辑:精确匹配 TotalPairs == MinScore diff --git a/internal/api/activity/matching_game_helper.go b/internal/api/activity/matching_game_helper.go index e820eb5..2c01375 100755 --- a/internal/api/activity/matching_game_helper.go +++ b/internal/api/activity/matching_game_helper.go @@ -1,17 +1,18 @@ package app import ( - "bindbox-game/configs" - "bindbox-game/internal/pkg/wechat" - "bindbox-game/internal/repository/mysql/model" - activitysvc "bindbox-game/internal/service/activity" - usersvc "bindbox-game/internal/service/user" "context" "fmt" "strings" "sync" "time" + "bindbox-game/configs" + "bindbox-game/internal/pkg/wechat" + "bindbox-game/internal/repository/mysql/model" + activitysvc "bindbox-game/internal/service/activity" + usersvc "bindbox-game/internal/service/user" + "go.uber.org/zap" ) diff --git a/internal/api/admin/activity_commitment_admin.go b/internal/api/admin/activity_commitment_admin.go index 4482ce2..0441a47 100755 --- a/internal/api/admin/activity_commitment_admin.go +++ b/internal/api/admin/activity_commitment_admin.go @@ -1,12 +1,13 @@ package admin import ( + "net/http" + "strconv" + "bindbox-game/internal/code" "bindbox-game/internal/pkg/core" "bindbox-game/internal/repository/mysql/dao" activitysvc "bindbox-game/internal/service/activity" - "net/http" - "strconv" ) type activityCommitGenerateResp struct { diff --git a/internal/api/admin/auth_refresh.go b/internal/api/admin/auth_refresh.go index 7a71199..89b865c 100755 --- a/internal/api/admin/auth_refresh.go +++ b/internal/api/admin/auth_refresh.go @@ -1,19 +1,19 @@ package admin import ( - "net/http" - "time" + "net/http" + "time" - "bindbox-game/configs" - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/jwtoken" - "bindbox-game/internal/pkg/utils" + "bindbox-game/configs" + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/jwtoken" + "bindbox-game/internal/pkg/utils" ) type refreshResponse struct { - Token string `json:"token"` - ExpiresIn int64 `json:"expires_in"` + Token string `json:"token"` + ExpiresIn int64 `json:"expires_in"` } // RefreshToken 刷新管理员访问令牌 @@ -26,25 +26,25 @@ type refreshResponse struct { // @Router /api/admin/auth/refresh [post] // @Security LoginVerifyToken func (h *handler) RefreshToken() core.HandlerFunc { - return func(ctx core.Context) { - auth := ctx.Request().Header.Get("Authorization") - if auth == "" { - ctx.AbortWithError(core.Error(http.StatusUnauthorized, code.AdminLoginError, "未携带令牌")) - return - } - newToken, err := jwtoken.New(configs.Get().JWT.AdminSecret).Refresh(auth) - if err != nil || newToken == "" { - ctx.AbortWithError(core.Error(http.StatusUnauthorized, code.AdminLoginError, "令牌刷新失败")) - return - } - info := ctx.SessionUserInfo() - if info.Id > 0 { - _, _ = h.writeDB.Admin.WithContext(ctx.RequestContext()).Where(h.writeDB.Admin.ID.Eq(int32(info.Id))).Updates(map[string]any{ - "last_login_time": time.Now(), - "last_login_ip": utils.GetIP(ctx.Request()), - "last_login_hash": utils.MD5(newToken), - }) - } - ctx.Payload(refreshResponse{Token: newToken, ExpiresIn: int64(24 * 3600)}) - } -} \ No newline at end of file + return func(ctx core.Context) { + auth := ctx.Request().Header.Get("Authorization") + if auth == "" { + ctx.AbortWithError(core.Error(http.StatusUnauthorized, code.AdminLoginError, "未携带令牌")) + return + } + newToken, err := jwtoken.New(configs.Get().JWT.AdminSecret).Refresh(auth) + if err != nil || newToken == "" { + ctx.AbortWithError(core.Error(http.StatusUnauthorized, code.AdminLoginError, "令牌刷新失败")) + return + } + info := ctx.SessionUserInfo() + if info.Id > 0 { + _, _ = h.writeDB.Admin.WithContext(ctx.RequestContext()).Where(h.writeDB.Admin.ID.Eq(int32(info.Id))).Updates(map[string]any{ + "last_login_time": time.Now(), + "last_login_ip": utils.GetIP(ctx.Request()), + "last_login_hash": utils.MD5(newToken), + }) + } + ctx.Payload(refreshResponse{Token: newToken, ExpiresIn: int64(24 * 3600)}) + } +} diff --git a/internal/api/admin/banner.go b/internal/api/admin/banner.go index e8d4f01..e9730e0 100755 --- a/internal/api/admin/banner.go +++ b/internal/api/admin/banner.go @@ -1,26 +1,26 @@ package admin import ( - "net/http" - "strconv" + "net/http" + "strconv" - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/validation" - bannersvc "bindbox-game/internal/service/banner" + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/validation" + bannersvc "bindbox-game/internal/service/banner" ) type createBannerRequest struct { - Title string `json:"title" binding:"required"` - ImageURL string `json:"image_url" binding:"required"` - LinkURL string `json:"link_url"` - Sort int32 `json:"sort"` - Status int32 `json:"status"` + Title string `json:"title" binding:"required"` + ImageURL string `json:"image_url" binding:"required"` + LinkURL string `json:"link_url"` + Sort int32 `json:"sort"` + Status int32 `json:"status"` } type createBannerResponse struct { - ID int64 `json:"id"` - Message string `json:"message"` + ID int64 `json:"id"` + Message string `json:"message"` } // CreateBanner 创建轮播图 @@ -34,34 +34,34 @@ type createBannerResponse struct { // @Router /api/admin/banners [post] // @Security LoginVerifyToken func (h *handler) CreateBanner() core.HandlerFunc { - return func(ctx core.Context) { - req := new(createBannerRequest) - res := new(createBannerResponse) - if err := ctx.ShouldBindJSON(req); err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) - return - } - if ctx.SessionUserInfo().IsSuper != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) - return - } - item, err := h.banner.Create(ctx.RequestContext(), bannersvc.CreateInput{Title: req.Title, ImageURL: req.ImageURL, LinkURL: req.LinkURL, Sort: req.Sort, Status: req.Status}) - if err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) - return - } - res.ID = item.ID - res.Message = "操作成功" - ctx.Payload(res) - } + return func(ctx core.Context) { + req := new(createBannerRequest) + res := new(createBannerResponse) + if err := ctx.ShouldBindJSON(req); err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) + return + } + if ctx.SessionUserInfo().IsSuper != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) + return + } + item, err := h.banner.Create(ctx.RequestContext(), bannersvc.CreateInput{Title: req.Title, ImageURL: req.ImageURL, LinkURL: req.LinkURL, Sort: req.Sort, Status: req.Status}) + if err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) + return + } + res.ID = item.ID + res.Message = "操作成功" + ctx.Payload(res) + } } type modifyBannerRequest struct { - Title *string `json:"title"` - ImageURL *string `json:"image_url"` - LinkURL *string `json:"link_url"` - Sort *int32 `json:"sort"` - Status *int32 `json:"status"` + Title *string `json:"title"` + ImageURL *string `json:"image_url"` + LinkURL *string `json:"link_url"` + Sort *int32 `json:"sort"` + Status *int32 `json:"status"` } // ModifyBanner 修改轮播图 @@ -76,24 +76,24 @@ type modifyBannerRequest struct { // @Router /api/admin/banners/{banner_id} [put] // @Security LoginVerifyToken func (h *handler) ModifyBanner() core.HandlerFunc { - return func(ctx core.Context) { - req := new(modifyBannerRequest) - if err := ctx.ShouldBindJSON(req); err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) - return - } - idStr := ctx.Param("banner_id") - id, _ := strconv.ParseInt(idStr, 10, 64) - if ctx.SessionUserInfo().IsSuper != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) - return - } - if err := h.banner.Modify(ctx.RequestContext(), id, bannersvc.ModifyInput{Title: req.Title, ImageURL: req.ImageURL, LinkURL: req.LinkURL, Sort: req.Sort, Status: req.Status}); err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) - return - } - ctx.Payload(pcSimpleMessage{Message: "操作成功"}) - } + return func(ctx core.Context) { + req := new(modifyBannerRequest) + if err := ctx.ShouldBindJSON(req); err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) + return + } + idStr := ctx.Param("banner_id") + id, _ := strconv.ParseInt(idStr, 10, 64) + if ctx.SessionUserInfo().IsSuper != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) + return + } + if err := h.banner.Modify(ctx.RequestContext(), id, bannersvc.ModifyInput{Title: req.Title, ImageURL: req.ImageURL, LinkURL: req.LinkURL, Sort: req.Sort, Status: req.Status}); err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) + return + } + ctx.Payload(pcSimpleMessage{Message: "操作成功"}) + } } // DeleteBanner 删除轮播图 @@ -107,41 +107,41 @@ func (h *handler) ModifyBanner() core.HandlerFunc { // @Router /api/admin/banners/{banner_id} [delete] // @Security LoginVerifyToken func (h *handler) DeleteBanner() core.HandlerFunc { - return func(ctx core.Context) { - idStr := ctx.Param("banner_id") - id, _ := strconv.ParseInt(idStr, 10, 64) - if ctx.SessionUserInfo().IsSuper != 1 { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) - return - } - if err := h.banner.Delete(ctx.RequestContext(), id); 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("banner_id") + id, _ := strconv.ParseInt(idStr, 10, 64) + if ctx.SessionUserInfo().IsSuper != 1 { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, "禁止操作")) + return + } + if err := h.banner.Delete(ctx.RequestContext(), id); err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) + return + } + ctx.Payload(pcSimpleMessage{Message: "操作成功"}) + } } type listBannersRequest struct { - Status *int32 `form:"status"` - Page int `form:"page"` - PageSize int `form:"page_size"` + Status *int32 `form:"status"` + Page int `form:"page"` + PageSize int `form:"page_size"` } type bannerItem struct { - ID int64 `json:"id"` - Title string `json:"title"` - ImageURL string `json:"image_url"` - LinkURL string `json:"link_url"` - Sort int32 `json:"sort"` - Status int32 `json:"status"` + ID int64 `json:"id"` + Title string `json:"title"` + ImageURL string `json:"image_url"` + LinkURL string `json:"link_url"` + Sort int32 `json:"sort"` + Status int32 `json:"status"` } type listBannersResponse struct { - Page int `json:"page"` - PageSize int `json:"page_size"` - Total int64 `json:"total"` - List []bannerItem `json:"list"` + Page int `json:"page"` + PageSize int `json:"page_size"` + Total int64 `json:"total"` + List []bannerItem `json:"list"` } // ListBanners 查看轮播图列表 @@ -157,25 +157,25 @@ type listBannersResponse struct { // @Router /api/admin/banners [get] // @Security LoginVerifyToken func (h *handler) ListBanners() core.HandlerFunc { - return func(ctx core.Context) { - req := new(listBannersRequest) - res := new(listBannersResponse) - if err := ctx.ShouldBindForm(req); err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) - return - } - items, total, err := h.banner.List(ctx.RequestContext(), bannersvc.ListInput{Status: req.Status, Page: req.Page, PageSize: req.PageSize}) - if err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) - return - } - res.Page = req.Page - res.PageSize = req.PageSize - res.Total = total - res.List = make([]bannerItem, len(items)) - for i, it := range items { - res.List[i] = bannerItem{ID: it.ID, Title: it.Title, ImageURL: it.ImageURL, LinkURL: it.LinkURL, Sort: it.Sort, Status: it.Status} - } - ctx.Payload(res) - } -} \ No newline at end of file + return func(ctx core.Context) { + req := new(listBannersRequest) + res := new(listBannersResponse) + if err := ctx.ShouldBindForm(req); err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.ParamBindError, validation.Error(err))) + return + } + items, total, err := h.banner.List(ctx.RequestContext(), bannersvc.ListInput{Status: req.Status, Page: req.Page, PageSize: req.PageSize}) + if err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) + return + } + res.Page = req.Page + res.PageSize = req.PageSize + res.Total = total + res.List = make([]bannerItem, len(items)) + for i, it := range items { + res.List[i] = bannerItem{ID: it.ID, Title: it.Title, ImageURL: it.ImageURL, LinkURL: it.LinkURL, Sort: it.Sort, Status: it.Status} + } + ctx.Payload(res) + } +} diff --git a/internal/api/admin/dashboard_activity.go b/internal/api/admin/dashboard_activity.go index 83f685e..dd0e922 100755 --- a/internal/api/admin/dashboard_activity.go +++ b/internal/api/admin/dashboard_activity.go @@ -1,11 +1,6 @@ package admin import ( - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/validation" - "bindbox-game/internal/repository/mysql/model" - financesvc "bindbox-game/internal/service/finance" "encoding/json" "fmt" "net/http" @@ -14,6 +9,12 @@ import ( "strings" "time" + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/validation" + "bindbox-game/internal/repository/mysql/model" + financesvc "bindbox-game/internal/service/finance" + "gorm.io/gorm" ) diff --git a/internal/api/admin/dashboard_spending.go b/internal/api/admin/dashboard_spending.go index eafac06..ae9883b 100755 --- a/internal/api/admin/dashboard_spending.go +++ b/internal/api/admin/dashboard_spending.go @@ -1,16 +1,17 @@ package admin import ( - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/validation" - "bindbox-game/internal/repository/mysql/model" - financesvc "bindbox-game/internal/service/finance" "fmt" "net/http" "sort" "strings" "time" + + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/validation" + "bindbox-game/internal/repository/mysql/model" + financesvc "bindbox-game/internal/service/finance" ) type spendingLeaderboardRequest struct { diff --git a/internal/api/admin/douyin_orders_admin.go b/internal/api/admin/douyin_orders_admin.go index cb6d05b..896335a 100755 --- a/internal/api/admin/douyin_orders_admin.go +++ b/internal/api/admin/douyin_orders_admin.go @@ -1,10 +1,6 @@ package admin import ( - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - "bindbox-game/internal/pkg/validation" - "bindbox-game/internal/service/douyin" "context" "errors" "fmt" @@ -13,6 +9,11 @@ import ( "strconv" "strings" "time" + + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + "bindbox-game/internal/pkg/validation" + "bindbox-game/internal/service/douyin" ) // ---------- 抖店配置 API ---------- diff --git a/internal/api/admin/ichiban_slots_admin.go b/internal/api/admin/ichiban_slots_admin.go index 67379cd..5163221 100755 --- a/internal/api/admin/ichiban_slots_admin.go +++ b/internal/api/admin/ichiban_slots_admin.go @@ -1,12 +1,13 @@ package admin import ( + "net/http" + "strconv" + "bindbox-game/internal/code" "bindbox-game/internal/pkg/core" "bindbox-game/internal/repository/mysql/dao" activitysvc "bindbox-game/internal/service/activity" - "net/http" - "strconv" ) type listSlotsRequest struct { diff --git a/internal/api/admin/issues_admin.go b/internal/api/admin/issues_admin.go index 09cf176..4435b11 100755 --- a/internal/api/admin/issues_admin.go +++ b/internal/api/admin/issues_admin.go @@ -23,11 +23,11 @@ type listIssuesResponse struct { } type activitysvcIssueData struct { - ID int64 `json:"id"` - IssueNumber string `json:"issue_number"` - Status int32 `json:"status"` - Sort int32 `json:"sort"` - PrizeCount int64 `json:"prize_count"` + ID int64 `json:"id"` + IssueNumber string `json:"issue_number"` + Status int32 `json:"status"` + Sort int32 `json:"sort"` + PrizeCount int64 `json:"prize_count"` } // ListActivityIssues 查看活动期数 @@ -70,21 +70,21 @@ func (h *handler) ListActivityIssues() core.HandlerFunc { res.Page = req.Page res.PageSize = req.PageSize res.Total = total - res.List = make([]*activitysvcIssueData, len(items)) - for i, v := range items { - var prizeCount int64 - count, err := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityRewardSettings.IssueID.Eq(v.ID)).Count() - if err == nil { - prizeCount = count - } - res.List[i] = &activitysvcIssueData{ - ID: v.ID, - IssueNumber: v.IssueNumber, - Status: v.Status, - Sort: v.Sort, - PrizeCount: prizeCount, - } - } + res.List = make([]*activitysvcIssueData, len(items)) + for i, v := range items { + var prizeCount int64 + count, err := h.readDB.ActivityRewardSettings.WithContext(ctx.RequestContext()).ReadDB().Where(h.readDB.ActivityRewardSettings.IssueID.Eq(v.ID)).Count() + if err == nil { + prizeCount = count + } + res.List[i] = &activitysvcIssueData{ + ID: v.ID, + IssueNumber: v.IssueNumber, + Status: v.Status, + Sort: v.Sort, + PrizeCount: prizeCount, + } + } ctx.Payload(res) } } diff --git a/internal/api/admin/item_cards_admin.go b/internal/api/admin/item_cards_admin.go index 278f6e4..de50537 100755 --- a/internal/api/admin/item_cards_admin.go +++ b/internal/api/admin/item_cards_admin.go @@ -230,21 +230,21 @@ func (h *handler) ModifySystemItemCard() core.HandlerFunc { // @Failure 500 {object} code.Failure "服务器内部错误" // @Router /api/admin/system_item_cards/{item_card_id} [delete] func (h *handler) DeleteSystemItemCard() core.HandlerFunc { - return func(ctx core.Context) { - idStr := ctx.Param("item_card_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.SystemItemCards.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemItemCards.ID.Eq(id)).Updates(set); err != nil { - ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) - return - } - ctx.Payload(simpleMessageResponse{Message: "操作成功"}) - } + return func(ctx core.Context) { + idStr := ctx.Param("item_card_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.SystemItemCards.WithContext(ctx.RequestContext()).Where(h.writeDB.SystemItemCards.ID.Eq(id)).Updates(set); err != nil { + ctx.AbortWithError(core.Error(http.StatusBadRequest, code.CreateAdminError, err.Error())) + return + } + ctx.Payload(simpleMessageResponse{Message: "操作成功"}) + } } type listItemCardsRequest struct { @@ -257,25 +257,25 @@ type listItemCardsRequest struct { } type itemCardListItem struct { - ID int64 `json:"id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Name string `json:"name"` - Status int32 `json:"status"` - CardType int32 `json:"card_type"` - ScopeType int32 `json:"scope_type"` - ActivityCategoryID int64 `json:"activity_category_id"` - ActivityID int64 `json:"activity_id"` - IssueID int64 `json:"issue_id"` - Price int64 `json:"price"` - ValidStart time.Time `json:"valid_start"` - ValidEnd time.Time `json:"valid_end"` - EffectType int32 `json:"effect_type"` - RewardMultiplierX1000 int32 `json:"reward_multiplier_x1000"` - BoostRateX1000 int32 `json:"boost_rate_x1000"` - StackingStrategy int32 `json:"stacking_strategy"` - MaxEffectValueX1000 int32 `json:"max_effect_value_x1000"` - Remark string `json:"remark"` + ID int64 `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Name string `json:"name"` + Status int32 `json:"status"` + CardType int32 `json:"card_type"` + ScopeType int32 `json:"scope_type"` + ActivityCategoryID int64 `json:"activity_category_id"` + ActivityID int64 `json:"activity_id"` + IssueID int64 `json:"issue_id"` + Price int64 `json:"price"` + ValidStart time.Time `json:"valid_start"` + ValidEnd time.Time `json:"valid_end"` + EffectType int32 `json:"effect_type"` + RewardMultiplierX1000 int32 `json:"reward_multiplier_x1000"` + BoostRateX1000 int32 `json:"boost_rate_x1000"` + StackingStrategy int32 `json:"stacking_strategy"` + MaxEffectValueX1000 int32 `json:"max_effect_value_x1000"` + Remark string `json:"remark"` } type listItemCardsResponse struct { @@ -345,30 +345,30 @@ func (h *handler) ListSystemItemCards() core.HandlerFunc { res.Page = req.Page res.PageSize = req.PageSize res.Total = total - res.List = make([]itemCardListItem, len(rows)) - for i, r := range rows { - res.List[i] = itemCardListItem{ - ID: r.ID, - CreatedAt: r.CreatedAt, - UpdatedAt: r.UpdatedAt, - Name: r.Name, - Status: r.Status, - CardType: r.CardType, - ScopeType: r.ScopeType, - ActivityCategoryID: r.ActivityCategoryID, - ActivityID: r.ActivityID, - IssueID: r.IssueID, - Price: r.Price, - ValidStart: r.ValidStart, - ValidEnd: r.ValidEnd, - EffectType: r.EffectType, - RewardMultiplierX1000: r.RewardMultiplierX1000, - BoostRateX1000: r.BoostRateX1000, - StackingStrategy: r.StackingStrategy, - MaxEffectValueX1000: r.MaxEffectValueX1000, - Remark: r.Remark, - } - } + res.List = make([]itemCardListItem, len(rows)) + for i, r := range rows { + res.List[i] = itemCardListItem{ + ID: r.ID, + CreatedAt: r.CreatedAt, + UpdatedAt: r.UpdatedAt, + Name: r.Name, + Status: r.Status, + CardType: r.CardType, + ScopeType: r.ScopeType, + ActivityCategoryID: r.ActivityCategoryID, + ActivityID: r.ActivityID, + IssueID: r.IssueID, + Price: r.Price, + ValidStart: r.ValidStart, + ValidEnd: r.ValidEnd, + EffectType: r.EffectType, + RewardMultiplierX1000: r.RewardMultiplierX1000, + BoostRateX1000: r.BoostRateX1000, + StackingStrategy: r.StackingStrategy, + MaxEffectValueX1000: r.MaxEffectValueX1000, + Remark: r.Remark, + } + } ctx.Payload(res) } } diff --git a/internal/api/admin/livestream_admin.go b/internal/api/admin/livestream_admin.go index ef84eea..bc157d8 100755 --- a/internal/api/admin/livestream_admin.go +++ b/internal/api/admin/livestream_admin.go @@ -915,13 +915,13 @@ func (h *handler) ListLivestreamDrawLogs() core.HandlerFunc { Remark string } var invRows []invRow - _ = h.repo.GetDbR().Table("user_inventory"). - Select("user_inventory.user_id, COALESCE(NULLIF(user_inventory.value_cents, 0), activity_reward_settings.price_snapshot_cents, products.price, 0) as value_cents, user_inventory.remark"). - Joins("LEFT JOIN activity_reward_settings ON activity_reward_settings.id = user_inventory.reward_id"). - Joins("LEFT JOIN products ON products.id = user_inventory.product_id"). - Where("user_inventory.status IN (1,3)"). - Where("COALESCE(user_inventory.remark, '') NOT LIKE ?", "%void%"). - Where("user_inventory.user_id > 0"). + _ = h.repo.GetDbR().Table("user_inventory"). + Select("user_inventory.user_id, COALESCE(NULLIF(user_inventory.value_cents, 0), activity_reward_settings.price_snapshot_cents, products.price, 0) as value_cents, user_inventory.remark"). + Joins("LEFT JOIN activity_reward_settings ON activity_reward_settings.id = user_inventory.reward_id"). + Joins("LEFT JOIN products ON products.id = user_inventory.product_id"). + Where("user_inventory.status IN (1,3)"). + Where("COALESCE(user_inventory.remark, '') NOT LIKE ?", "%void%"). + Where("user_inventory.user_id > 0"). Scan(&invRows).Error invByUser := make(map[int64][]invRow) for _, v := range invRows { diff --git a/internal/api/admin/livestream_stats.go b/internal/api/admin/livestream_stats.go index a6abf74..4f4c009 100755 --- a/internal/api/admin/livestream_stats.go +++ b/internal/api/admin/livestream_stats.go @@ -5,11 +5,11 @@ import ( "net/http" "strconv" "strings" + "time" "bindbox-game/internal/code" "bindbox-game/internal/pkg/core" "bindbox-game/internal/repository/mysql/model" - "time" ) type dailyLivestreamStats struct { diff --git a/internal/api/admin/lottery_admin.go b/internal/api/admin/lottery_admin.go index ac5f4c0..c11ac34 100755 --- a/internal/api/admin/lottery_admin.go +++ b/internal/api/admin/lottery_admin.go @@ -1,13 +1,6 @@ package admin import ( - "bindbox-game/internal/code" - "bindbox-game/internal/pkg/core" - paypkg "bindbox-game/internal/pkg/pay" - "bindbox-game/internal/pkg/validation" - "bindbox-game/internal/repository/mysql/model" - strat "bindbox-game/internal/service/activity/strategy" - usersvc "bindbox-game/internal/service/user" "crypto/hmac" "crypto/sha256" "encoding/binary" @@ -15,6 +8,14 @@ import ( "fmt" "net/http" "time" + + "bindbox-game/internal/code" + "bindbox-game/internal/pkg/core" + paypkg "bindbox-game/internal/pkg/pay" + "bindbox-game/internal/pkg/validation" + "bindbox-game/internal/repository/mysql/model" + strat "bindbox-game/internal/service/activity/strategy" + usersvc "bindbox-game/internal/service/user" ) type participantsResponse struct { diff --git a/internal/api/admin/matching_audit_admin.go b/internal/api/admin/matching_audit_admin.go index 5deabe3..9a1184a 100755 --- a/internal/api/admin/matching_audit_admin.go +++ b/internal/api/admin/matching_audit_admin.go @@ -1,9 +1,10 @@ package admin import ( + "net/http" + "bindbox-game/internal/code" "bindbox-game/internal/pkg/core" - "net/http" ) // GetMatchingAudit 获取对对碰审计数据 diff --git a/internal/service/channel/channel.go b/internal/service/channel/channel.go index a271ffb..e2a5b90 100755 --- a/internal/service/channel/channel.go +++ b/internal/service/channel/channel.go @@ -8,7 +8,6 @@ import ( "time" "bindbox-game/internal/pkg/logger" - "bindbox-game/internal/pkg/util/remark" "bindbox-game/internal/repository/mysql" "bindbox-game/internal/repository/mysql/dao" "bindbox-game/internal/repository/mysql/model" @@ -129,163 +128,36 @@ var ( ErrSearchKeywordEmpty = errors.New("search_keyword_empty") ) -type orderRemarkRow struct { - Remark string - CreatedAt time.Time +type orderAmountRow struct { + ActualAmount int64 + CreatedAt time.Time } -// calcPaidByPriceDraw 解析订单 remark,按游戏类型分三路计算实付金额: -// - Case 1 (抽奖/直购): ActivityID > 0 → activities.price_draw × count -// - Case 2 (对对碰): IssueID > 0 → activity_issues → activities.price_draw × count -// - Case 3 (一番赏): PkgID > 0 → game_pass_packages.price × count -// +// calcGMVByTotalAmount 按订单原价(total_amount)统计渠道GMV,涵盖全部游戏类型(抽奖、对对碰、一番赏)。 +// 使用 total_amount(活动原价)而非 actual_amount,确保优惠券、道具卡、积分抵扣的订单也完整计入, +// 与成本(商品价值)保持同一口径,使盈亏计算真实反映业务健康度。 // 返回:总金额(分)、按 dateFmt 格式分组的金额。 -func (s *service) calcPaidByPriceDraw(ctx context.Context, rows []orderRemarkRow, dateFmt string) (int64, map[string]int64) { - if len(rows) == 0 { - return 0, nil +func (s *service) calcGMVByTotalAmount(ctx context.Context, channelID int64, dateFmt string, orderFilter string, startDate, endDate *time.Time) (int64, map[string]int64) { + type row struct { + TotalAmount int64 + CreatedAt time.Time } - - type parsedActivity struct { - activityID int64 - count int64 - dateKey string - } - type parsedIssue struct { - issueID int64 - count int64 - dateKey string - } - type parsedPkg struct { - pkgID int64 - count int64 - dateKey string + q := s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders"). + Joins("JOIN users ON users.id = orders.user_id"). + Select("orders.total_amount, orders.created_at"). + Where(orderFilter, channelID) + if startDate != nil && endDate != nil { + q = q.Where("orders.created_at >= ? AND orders.created_at <= ?", *startDate, *endDate) } + var rows []row + q.Scan(&rows) - var actItems []parsedActivity - var issueItems []parsedIssue - var pkgItems []parsedPkg - - actIDSet := make(map[int64]struct{}) - issueIDSet := make(map[int64]struct{}) - pkgIDSet := make(map[int64]struct{}) - - for _, r := range rows { - rmk := remark.Parse(r.Remark) - dateKey := r.CreatedAt.Format(dateFmt) - - if rmk.ActivityID > 0 { - // Case 1: 抽奖/直购 — 直接有 activityID - actItems = append(actItems, parsedActivity{rmk.ActivityID, rmk.Count, dateKey}) - actIDSet[rmk.ActivityID] = struct{}{} - } else if rmk.IssueID > 0 { - // Case 2: 对对碰付费路径 — 只有 issueID,需查 activity_issues - issueItems = append(issueItems, parsedIssue{rmk.IssueID, rmk.Count, dateKey}) - issueIDSet[rmk.IssueID] = struct{}{} - } else if rmk.PkgID > 0 { - // Case 3: 一番赏 — 有 pkgID,需查 game_pass_packages - pkgItems = append(pkgItems, parsedPkg{rmk.PkgID, rmk.Count, dateKey}) - pkgIDSet[rmk.PkgID] = struct{}{} - } - } - - // ── Case 2: 批量查 activity_issues → 拿到 activityID ── - issueActivityMap := make(map[int64]int64) // issueID → activityID - if len(issueIDSet) > 0 { - issueIDs := make([]int64, 0, len(issueIDSet)) - for id := range issueIDSet { - issueIDs = append(issueIDs, id) - } - type issueRow struct { - ID int64 - ActivityID int64 - } - var issueRows []issueRow - s.readDB.ActivityIssues.WithContext(ctx).UnderlyingDB(). - Table("activity_issues"). - Select("id, activity_id"). - Where("id IN ?", issueIDs). - Scan(&issueRows) - for _, ir := range issueRows { - issueActivityMap[ir.ID] = ir.ActivityID - actIDSet[ir.ActivityID] = struct{}{} // 合并到 actIDSet 一起查 price_draw - } - } - - // ── Case 1+2: 批量查 activities.price_draw(含软删除)── - priceMap := make(map[int64]int64) // activityID → price_draw - if len(actIDSet) > 0 { - actIDs := make([]int64, 0, len(actIDSet)) - for id := range actIDSet { - actIDs = append(actIDs, id) - } - var acts []model.Activities - s.readDB.Activities.WithContext(ctx).UnderlyingDB(). - Unscoped(). - Table("activities"). - Select("id, price_draw"). - Where("id IN ?", actIDs). - Find(&acts) - for _, a := range acts { - priceMap[a.ID] = a.PriceDraw - } - } - - // ── Case 3: 批量查 game_pass_packages.price ── - pkgPriceMap := make(map[int64]int64) // pkgID → price - if len(pkgIDSet) > 0 { - pkgIDs := make([]int64, 0, len(pkgIDSet)) - for id := range pkgIDSet { - pkgIDs = append(pkgIDs, id) - } - type pkgRow struct { - ID int64 - Price int64 - } - var pkgRows []pkgRow - s.readDB.Activities.WithContext(ctx).UnderlyingDB(). - Unscoped(). - Table("game_pass_packages"). - Select("id, price"). - Where("id IN ?", pkgIDs). - Scan(&pkgRows) - for _, pr := range pkgRows { - pkgPriceMap[pr.ID] = pr.Price - } - } - - // ── 累加金额 ── var total int64 byDate := make(map[string]int64) - - // Case 1: 抽奖/直购 - for _, item := range actItems { - if price, ok := priceMap[item.activityID]; ok { - amt := price * item.count - total += amt - byDate[item.dateKey] += amt - } + for _, r := range rows { + total += r.TotalAmount + byDate[r.CreatedAt.Format(dateFmt)] += r.TotalAmount } - - // Case 2: 对对碰 - for _, item := range issueItems { - if actID, ok := issueActivityMap[item.issueID]; ok { - if price, ok := priceMap[actID]; ok { - amt := price * item.count - total += amt - byDate[item.dateKey] += amt - } - } - } - - // Case 3: 一番赏 - for _, item := range pkgItems { - if price, ok := pkgPriceMap[item.pkgID]; ok { - amt := price * item.count - total += amt - byDate[item.dateKey] += amt - } - } - return total, byDate } @@ -304,7 +176,7 @@ func (s *service) calcCostByInventory(ctx context.Context, channelID int64, date Table("user_inventory"). Select(` COALESCE(NULLIF(user_inventory.value_cents, 0), activity_reward_settings.price_snapshot_cents, products.price, 0) AS unit_cost, - GREATEST(COALESCE(system_item_cards.reward_multiplier_x1000, 1000), 1000) AS multiplier, + CASE WHEN COALESCE(system_item_cards.reward_multiplier_x1000, 1000) < 1000 THEN 1000 ELSE COALESCE(system_item_cards.reward_multiplier_x1000, 1000) END AS multiplier, user_inventory.created_at `). Joins("JOIN users ON users.id = user_inventory.user_id"). @@ -317,7 +189,8 @@ func (s *service) calcCostByInventory(ctx context.Context, channelID int64, date Where("user_inventory.status IN ?", []int{1, 3}). Where("COALESCE(user_inventory.remark, '') NOT LIKE ?", "%void%"). Where("(orders.status = 2 OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)"). - Where("(orders.source_type IN (1,2,3,4) OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)") + Where("(orders.source_type IN (2,3,4) OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)"). + Where("(orders.total_amount > 0 OR user_inventory.order_id = 0 OR user_inventory.order_id IS NULL)") if startDate != nil && endDate != nil { q = q.Where("user_inventory.created_at >= ? AND user_inventory.created_at <= ?", *startDate, *endDate) @@ -418,28 +291,20 @@ func (s *service) List(ctx context.Context, in ListInput) (items []*ChannelWithS } } - type PaidResult struct { - ChannelID int64 - Remark string - CreatedAt time.Time + type GMVResult struct { + ChannelID int64 + TotalAmount int64 } - var paidResults []PaidResult + var gmvResults []GMVResult err = s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders"). Joins("JOIN users ON users.id = orders.user_id"). - Select("users.channel_id, orders.remark, orders.created_at"). + Select("users.channel_id, orders.total_amount"). Where("users.channel_id IN ?", channelIDs). - Where("users.deleted_at IS NULL AND orders.status = 2 AND orders.actual_amount > 0 AND orders.source_type IN (1,2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)"). - Scan(&paidResults).Error + Where("users.deleted_at IS NULL AND orders.status = 2 AND orders.total_amount > 0 AND orders.actual_amount > 0 AND orders.source_type IN (2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)"). + Scan(&gmvResults).Error if err == nil { - grouped := make(map[int64][]orderRemarkRow) - for _, r := range paidResults { - grouped[r.ChannelID] = append(grouped[r.ChannelID], orderRemarkRow{ - Remark: r.Remark, CreatedAt: r.CreatedAt, - }) - } - for chID, rows := range grouped { - total, _ := s.calcPaidByPriceDraw(ctx, rows, "2006-01-02") - paidStats[chID] = total + for _, r := range gmvResults { + paidStats[r.ChannelID] += r.TotalAmount } } } @@ -468,7 +333,9 @@ func (s *service) GetStats(ctx context.Context, channelID int64, days int, start } out := &StatsOutput{} - orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.actual_amount > 0 AND orders.source_type IN (1,2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + // source_type: 2=小程序抽奖 3=对对碰 4=一番赏/次卡 5=直播间抽奖抖店(不计入);排除商城直购(1) + // actual_amount>0 排除次卡免费使用的订单(避免与购买次卡的订单重复计入GMV) + orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.total_amount > 0 AND orders.actual_amount > 0 AND orders.source_type IN (2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" // ========== 1. Overview(全量,不限时间)========== @@ -484,14 +351,7 @@ func (s *service) GetStats(ctx context.Context, channelID int64, days int, start Scan(&cr) out.Overview.TotalOrders = cr.Count - var allRemarks []orderRemarkRow - s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders"). - Joins("JOIN users ON users.id = orders.user_id"). - Select("orders.remark, orders.created_at"). - Where(orderFilter, channelID). - Scan(&allRemarks) - - totalPaid, _ := s.calcPaidByPriceDraw(ctx, allRemarks, "2006-01-02") + totalPaid, _ := s.calcGMVByTotalAmount(ctx, channelID, "2006-01-02", orderFilter, nil, nil) out.Overview.TotalPaidCents = totalPaid out.Overview.TotalGMV = totalPaid / 100 @@ -553,14 +413,7 @@ func (s *service) GetStats(ctx context.Context, channelID int64, days int, start } } - var rangeRemarks []orderRemarkRow - s.readDB.Orders.WithContext(ctx).UnderlyingDB().Table("orders"). - Joins("JOIN users ON users.id = orders.user_id"). - Select("orders.remark, orders.created_at"). - Where(orderFilter+" AND orders.created_at >= ? AND orders.created_at <= ?", channelID, startDate, endDate). - Scan(&rangeRemarks) - - _, dailyPaid := s.calcPaidByPriceDraw(ctx, rangeRemarks, "2006-01-02") + _, dailyPaid := s.calcGMVByTotalAmount(ctx, channelID, "2006-01-02", orderFilter, &startDate, &endDate) for dateKey, paid := range dailyPaid { if item, ok := dateMap[dateKey]; ok { item.PaidCents = paid diff --git a/internal/service/channel/channel_stats_test.go b/internal/service/channel/channel_stats_test.go new file mode 100644 index 0000000..1230d8d --- /dev/null +++ b/internal/service/channel/channel_stats_test.go @@ -0,0 +1,283 @@ +package channel + +import ( + "context" + "testing" + "time" + + "bindbox-game/internal/pkg/logger" + "bindbox-game/internal/repository/mysql" + "bindbox-game/internal/repository/mysql/dao" +) + +// setupTestService 创建使用 SQLite 内存库的 service 实例及基础表结构。 +func setupTestService(t *testing.T) (*service, mysql.Repo) { + t.Helper() + repo, err := mysql.NewSQLiteRepoForTest() + if err != nil { + t.Fatal(err) + } + ddls := []string{ + `CREATE TABLE channels ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + code TEXT NOT NULL, + type TEXT NOT NULL DEFAULT 'other', + remarks TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + deleted_at DATETIME + )`, + `CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + deleted_at DATETIME, + nickname TEXT NOT NULL, + avatar TEXT, + mobile TEXT, + openid TEXT, + unionid TEXT, + invite_code TEXT NOT NULL, + inviter_id INTEGER DEFAULT 0, + status INTEGER NOT NULL DEFAULT 1, + douyin_id TEXT, + channel_id INTEGER DEFAULT 0, + douyin_user_id TEXT, + remark TEXT NOT NULL DEFAULT '' + )`, + `CREATE TABLE orders ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + status INTEGER NOT NULL, + actual_amount INTEGER NOT NULL DEFAULT 0, + total_amount INTEGER NOT NULL DEFAULT 0, + source_type INTEGER NOT NULL DEFAULT 1, + ext_order_id TEXT NOT NULL DEFAULT '', + remark TEXT NOT NULL DEFAULT '', + item_card_id INTEGER DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + )`, + `CREATE TABLE user_inventory ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + order_id INTEGER DEFAULT 0, + reward_id INTEGER DEFAULT 0, + product_id INTEGER DEFAULT 0, + status INTEGER NOT NULL DEFAULT 1, + value_cents INTEGER DEFAULT 0, + remark TEXT NOT NULL DEFAULT '', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + )`, + `CREATE TABLE activity_reward_settings ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + price_snapshot_cents INTEGER DEFAULT 0 + )`, + `CREATE TABLE products ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + price INTEGER DEFAULT 0 + )`, + `CREATE TABLE user_item_cards ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + card_id INTEGER DEFAULT 0 + )`, + `CREATE TABLE system_item_cards ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + reward_multiplier_x1000 INTEGER DEFAULT 1000 + )`, + } + for _, ddl := range ddls { + if err := repo.GetDbW().Exec(ddl).Error; err != nil { + t.Fatalf("DDL failed: %v\nSQL: %s", err, ddl) + } + } + lg, err := logger.NewCustomLogger(logger.WithOutputInConsole()) + if err != nil { + t.Fatal(err) + } + q := dao.Use(repo.GetDbR()) + svc := &service{logger: lg, readDB: q, writeDB: dao.Use(repo.GetDbW())} + return svc, repo +} + +// mustExec 执行 SQL,失败则 Fatal。 +func mustExec(t *testing.T, repo mysql.Repo, sql string, args ...interface{}) { + t.Helper() + if err := repo.GetDbW().Exec(sql, args...).Error; err != nil { + t.Fatalf("exec failed: %v\nSQL: %s", err, sql) + } +} + +// TestCalcGMVByTotalAmount_ThreeGameTypes 验证三种游戏类型的原价都被正确统计。 +// 使用 total_amount(活动原价)确保优惠券、道具卡免单的订单也完整计入。 +func TestCalcGMVByTotalAmount_ThreeGameTypes(t *testing.T) { + svc, repo := setupTestService(t) + + mustExec(t, repo, `INSERT INTO channels (id, name, code, type, remarks) VALUES (1, '测试渠道', 'TEST', 'other', '')`) + mustExec(t, repo, `INSERT INTO users (id, nickname, invite_code, status, channel_id) VALUES (1, 'u1', 'I1', 1, 1)`) + + orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.total_amount > 0 AND orders.actual_amount > 0 AND orders.source_type IN (2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + // 抽奖订单 source=2,actual_amount < total_amount(道具卡折扣) + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (1, 2, 800, 1000, 2, '', 'lottery:activity:10|count:1')`) + // 对对碰付费 source=3 + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (1, 2, 500, 500, 3, '', 'matching_game:issue:50')`) + // 一番赏 source=4 + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (1, 2, 800, 800, 4, '', 'game_pass_package:幸运|pkg_id:7|count:2')`) + // 次卡免费使用:actual_amount=0 但 total_amount=600,不应计入GMV(避免与购买次卡重复计数) + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (1, 2, 0, 600, 2, '', 'lottery:activity:10|count:1|use_game_pass')`) + // 过滤条件:status!=2,不应计入 + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (1, 1, 9999, 9999, 2, '', 'lottery:activity:10|count:1')`) + // 过滤条件:total_amount=0,不应计入 + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (1, 2, 0, 0, 2, '', 'lottery:activity:10|count:1')`) + // 过滤条件:有 ext_order_id,不应计入 + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (1, 2, 9999, 9999, 2, 'EXT-1', 'lottery:activity:10|count:1')`) + + total, byDate := svc.calcGMVByTotalAmount(context.Background(), 1, "2006-01-02", orderFilter, nil, nil) + + // 1000 + 500 + 800 = 2300(次卡免费使用actual=0的600不计入) + if total != 2300 { + t.Errorf("total = %d, want 2300 (抽奖1000 + 对对碰500 + 一番赏800)", total) + } + if len(byDate) == 0 { + t.Error("byDate should not be empty") + } +} + +// TestCalcGMVByTotalAmount_DateFilter 验证时间范围过滤正确。 +func TestCalcGMVByTotalAmount_DateFilter(t *testing.T) { + svc, repo := setupTestService(t) + + mustExec(t, repo, `INSERT INTO channels (id, name, code, type, remarks) VALUES (1, '测试渠道', 'TEST', 'other', '')`) + mustExec(t, repo, `INSERT INTO users (id, nickname, invite_code, status, channel_id) VALUES (1, 'u1', 'I1', 1, 1)`) + + orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.total_amount > 0 AND orders.actual_amount > 0 AND orders.source_type IN (2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, created_at) VALUES (1, 2, 500, 500, 2, '', '2026-03-01 10:00:00')`) + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, created_at) VALUES (1, 2, 300, 300, 3, '', '2026-03-05 10:00:00')`) + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id, created_at) VALUES (1, 2, 700, 700, 4, '', '2026-03-10 10:00:00')`) + + start, _ := time.Parse("2006-01-02", "2026-03-02") + end, _ := time.Parse("2006-01-02", "2026-03-09") + end = end.Add(24*time.Hour - time.Second) + + total, byDate := svc.calcGMVByTotalAmount(context.Background(), 1, "2006-01-02", orderFilter, &start, &end) + + // 只有 03-05 的 300 在范围内 + if total != 300 { + t.Errorf("total = %d, want 300 (only 2026-03-05 order)", total) + } + if byDate["2026-03-05"] != 300 { + t.Errorf("byDate[2026-03-05] = %d, want 300", byDate["2026-03-05"]) + } + if byDate["2026-03-01"] != 0 && byDate["2026-03-10"] != 0 { + t.Error("dates outside range should not appear") + } +} + +// TestCalcGMVByTotalAmount_MultiChannel 验证不同渠道数据互不干扰。 +func TestCalcGMVByTotalAmount_MultiChannel(t *testing.T) { + svc, repo := setupTestService(t) + + mustExec(t, repo, `INSERT INTO channels (id, name, code, type, remarks) VALUES (1, '渠道A', 'CA', 'other', ''), (2, '渠道B', 'CB', 'other', '')`) + mustExec(t, repo, `INSERT INTO users (id, nickname, invite_code, status, channel_id) VALUES (1, 'u1', 'I1', 1, 1)`) + mustExec(t, repo, `INSERT INTO users (id, nickname, invite_code, status, channel_id) VALUES (2, 'u2', 'I2', 1, 2)`) + + orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.total_amount > 0 AND orders.actual_amount > 0 AND orders.source_type IN (2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id) VALUES (1, 2, 1000, 1000, 2, '')`) + mustExec(t, repo, `INSERT INTO orders (user_id, status, actual_amount, total_amount, source_type, ext_order_id) VALUES (2, 2, 2000, 2000, 3, '')`) + + total1, _ := svc.calcGMVByTotalAmount(context.Background(), 1, "2006-01-02", orderFilter, nil, nil) + total2, _ := svc.calcGMVByTotalAmount(context.Background(), 2, "2006-01-02", orderFilter, nil, nil) + + if total1 != 1000 { + t.Errorf("channel1 total = %d, want 1000", total1) + } + if total2 != 2000 { + t.Errorf("channel2 total = %d, want 2000", total2) + } +} + +// TestCalcCostByInventory_Basic 验证成本从 value_cents 读取。 +func TestCalcCostByInventory_Basic(t *testing.T) { + svc, repo := setupTestService(t) + + mustExec(t, repo, `INSERT INTO channels (id, name, code, type, remarks) VALUES (1, '测试渠道', 'TEST', 'other', '')`) + mustExec(t, repo, `INSERT INTO users (id, nickname, invite_code, status, channel_id) VALUES (1, 'u1', 'I1', 1, 1)`) + + // status=1(待发货) 和 status=3(已发货) 都计入成本 + mustExec(t, repo, `INSERT INTO user_inventory (user_id, order_id, status, value_cents, remark) VALUES (1, 0, 1, 500, '')`) + mustExec(t, repo, `INSERT INTO user_inventory (user_id, order_id, status, value_cents, remark) VALUES (1, 0, 3, 300, '')`) + // status=2 不计入 + mustExec(t, repo, `INSERT INTO user_inventory (user_id, order_id, status, value_cents, remark) VALUES (1, 0, 2, 999, '')`) + // remark含void 不计入 + mustExec(t, repo, `INSERT INTO user_inventory (user_id, order_id, status, value_cents, remark) VALUES (1, 0, 1, 888, 'void-item')`) + + total, byDate := svc.calcCostByInventory(context.Background(), 1, "2006-01-02", nil, nil) + + // 500 + 300 = 800 + if total != 800 { + t.Errorf("cost total = %d, want 800", total) + } + if len(byDate) == 0 { + t.Error("byDate should not be empty") + } +} + +// TestProfitLoss_AllGameTypes 端到端验证盈亏 = GMV(原价) - 成本,覆盖三种游戏类型及道具卡免单。 +// 核心场景:道具卡免单订单 actual_amount=0 但 total_amount=活动原价,成本真实存在, +// 使用 total_amount 口径确保盈亏计算准确。 +func TestProfitLoss_AllGameTypes(t *testing.T) { + svc, repo := setupTestService(t) + + mustExec(t, repo, `INSERT INTO channels (id, name, code, type, remarks) VALUES (1, '测试渠道', 'TEST', 'other', '')`) + mustExec(t, repo, `INSERT INTO users (id, nickname, invite_code, status, channel_id) VALUES (1, 'u1', 'I1', 1, 1)`) + + orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.total_amount > 0 AND orders.actual_amount > 0 AND orders.source_type IN (2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + // 收入:3种游戏(total_amount = 活动原价) + mustExec(t, repo, `INSERT INTO orders (id, user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (1, 1, 2, 4600, 4600, 2, '', 'lottery:activity:10|count:1')`) // 抽奖 46元 + mustExec(t, repo, `INSERT INTO orders (id, user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (2, 1, 2, 1086, 1086, 3, '', 'matching_game:issue:50')`) // 对对碰 10.86元 + mustExec(t, repo, `INSERT INTO orders (id, user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (3, 1, 2, 3320, 3320, 4, '', 'game_pass_package:x|pkg_id:7|count:2')`) // 一番赏 33.20元 + // 次卡免费使用:actual_amount=0,total_amount=2000,不计入GMV(避免重复计数),但成本仍计入 + mustExec(t, repo, `INSERT INTO orders (id, user_id, status, actual_amount, total_amount, source_type, ext_order_id, remark) VALUES (4, 1, 2, 0, 2000, 2, '', 'lottery:activity:10|count:1|use_game_pass')`) + + // 成本:库存资产 + mustExec(t, repo, `INSERT INTO user_inventory (user_id, order_id, status, value_cents, remark) VALUES (1, 0, 1, 8000, '')`) // 成本 80元 + + totalGMV, _ := svc.calcGMVByTotalAmount(context.Background(), 1, "2006-01-02", orderFilter, nil, nil) + totalCost, _ := svc.calcCostByInventory(context.Background(), 1, "2006-01-02", nil, nil) + profit := totalGMV - totalCost + + // GMV = 4600 + 1086 + 3320 = 9006(次卡免费使用的2000不计入) + if totalGMV != 9006 { + t.Errorf("totalGMV = %d, want 9006 (抽奖4600 + 对对碰1086 + 一番赏3320)", totalGMV) + } + // 成本 = 8000 + if totalCost != 8000 { + t.Errorf("totalCost = %d, want 8000", totalCost) + } + // 盈亏 = 9006 - 8000 = 1006 + if profit != 1006 { + t.Errorf("profit = %d, want 1006", profit) + } +} + +// TestCalcGMVByTotalAmount_Empty 验证无订单时返回零值。 +func TestCalcGMVByTotalAmount_Empty(t *testing.T) { + svc, repo := setupTestService(t) + + mustExec(t, repo, `INSERT INTO channels (id, name, code, type, remarks) VALUES (1, '空渠道', 'EMPTY', 'other', '')`) + + orderFilter := "users.channel_id = ? AND users.deleted_at IS NULL AND orders.status = 2 AND orders.total_amount > 0 AND orders.actual_amount > 0 AND orders.source_type IN (2,3,4) AND (orders.ext_order_id = '' OR orders.ext_order_id IS NULL)" + + total, byDate := svc.calcGMVByTotalAmount(context.Background(), 1, "2006-01-02", orderFilter, nil, nil) + + if total != 0 { + t.Errorf("empty channel total = %d, want 0", total) + } + if len(byDate) != 0 { + t.Errorf("byDate should be empty, got %v", byDate) + } +} diff --git a/web/.DS_Store b/web/.DS_Store index 9915eea..164f4c7 100755 Binary files a/web/.DS_Store and b/web/.DS_Store differ