ipa
All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 7m47s

This commit is contained in:
shanshanzhong 2026-03-08 05:12:28 -07:00
parent 94f5d0c07d
commit 7a3a53f1a9
14 changed files with 1193 additions and 4 deletions

38
.agents/README.md Normal file
View File

@ -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

298
.agents/config.toml Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

12
.gitignore vendored
View File

@ -67,3 +67,15 @@ script/*.sh
# ==================== 临时笔记 ====================
订单日志.txt
# Codex local configuration
.codex/
# Claude Flow runtime data
.claude-flow/data/
.claude-flow/logs/
# Environment variables
.env
.env.local
.env.*.local

145
AGENTS.md Normal file
View File

@ -0,0 +1,145 @@
# ppanel-server
> 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
```
<type>(<scope>): <description>
[optional body]
Co-Authored-By: claude-flow <ruv@ruv.net>
```
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

View File

@ -61,6 +61,11 @@ type customOrderLogicModel interface {
IsUserEligibleForNewOrder(ctx context.Context, userID int64) (bool, error)
QueryDailyOrdersList(ctx context.Context, date time.Time) ([]OrdersTotalWithDate, error)
QueryMonthlyOrdersList(ctx context.Context, date time.Time) ([]OrdersTotalWithDate, error)
// FindPendingIAPOrders 查询待对账的 Apple IAP 订单
// minAge: 订单创建时间距今最短时长(避免扫到刚创建的)
// maxAge: 订单创建时间距今最长时长(超出此范围由日终对账处理)
// requireTradeNo: true=只查 trade_no 非空的第二层false=全部(第三层)
FindPendingIAPOrders(ctx context.Context, minAge, maxAge time.Duration, requireTradeNo bool) ([]*Order, error)
}
// UserCounts User counts for new and renewal users
@ -281,6 +286,24 @@ func (m *customOrderModel) QueryDailyOrdersList(ctx context.Context, date time.T
return results, err
}
// FindPendingIAPOrders 查询待对账的 Apple IAP 待支付订单
func (m *customOrderModel) FindPendingIAPOrders(ctx context.Context, minAge, maxAge time.Duration, requireTradeNo bool) ([]*Order, error) {
var list []*Order
now := time.Now()
err := m.QueryNoCacheCtx(ctx, &list, func(conn *gorm.DB, v interface{}) error {
q := conn.Model(&Order{}).
Where("method = ? AND status = ? AND created_at < ? AND created_at > ?",
"apple_iap", 1, now.Add(-minAge), now.Add(-maxAge)).
Order("created_at ASC").
Limit(100)
if requireTradeNo {
q = q.Where("trade_no != '' AND trade_no IS NOT NULL")
}
return q.Find(&list).Error
})
return list, err
}
// QueryMonthlyOrdersList 查询过去 6 个月订单统计(包含当前月)
func (m *customOrderModel) QueryMonthlyOrdersList(ctx context.Context, date time.Time) ([]OrdersTotalWithDate, error) {
var results []OrdersTotalWithDate

View File

@ -3,6 +3,7 @@ package handler
import (
"github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/svc"
iapLogic "github.com/perfect-panel/server/queue/logic/iap"
orderLogic "github.com/perfect-panel/server/queue/logic/order"
smslogic "github.com/perfect-panel/server/queue/logic/sms"
"github.com/perfect-panel/server/queue/logic/subscription"
@ -43,4 +44,8 @@ func RegisterHandlers(mux *asynq.ServeMux, serverCtx *svc.ServiceContext) {
// ForthwithQuotaTask
mux.Handle(types.ForthwithQuotaTask, task.NewQuotaTaskLogic(serverCtx))
// Apple IAP 对账第二层5min 扫描 + 第三层:日终全量)
mux.Handle(types.SchedulerIAPReconcile, iapLogic.NewReconcileLogic(serverCtx))
mux.Handle(types.SchedulerIAPDailyReconcile, iapLogic.NewDailyReconcileLogic(serverCtx))
}

View File

@ -0,0 +1,27 @@
package iap
import (
"context"
"time"
"github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/svc"
"github.com/perfect-panel/server/pkg/logger"
)
// DailyReconcileLogic 第三层:日终全量对账(每天 02:00
// 扫描过去 48h 内所有未支付的 IAP 订单(含 trade_no 为空的极端情况)
type DailyReconcileLogic struct {
inner *ReconcileLogic
}
func NewDailyReconcileLogic(svc *svc.ServiceContext) *DailyReconcileLogic {
return &DailyReconcileLogic{inner: &ReconcileLogic{svc: svc}}
}
func (l *DailyReconcileLogic) ProcessTask(ctx context.Context, _ *asynq.Task) error {
logger.Infof("[IAPDailyReconcile] start at %s", time.Now().Format("2006-01-02 15:04:05"))
// 第三层:扫描 1min ~ 48htrade_no 非空trade_no 为空说明客户端从未上传过 token服务端无法主动补单
// 实际上与第二层的区别是时间窗口:覆盖全天的遗漏
return l.inner.reconcile(ctx, 1*time.Minute, 48*time.Hour, true)
}

View File

@ -0,0 +1,134 @@
package iap
import (
"context"
"encoding/json"
"strings"
"time"
"github.com/hibiken/asynq"
"github.com/perfect-panel/server/internal/model/payment"
"github.com/perfect-panel/server/internal/svc"
iapapple "github.com/perfect-panel/server/pkg/iap/apple"
"github.com/perfect-panel/server/pkg/logger"
pkgpayment "github.com/perfect-panel/server/pkg/payment"
queueTypes "github.com/perfect-panel/server/queue/types"
)
// ReconcileLogic 第二层:每 5 分钟扫描 trade_no 非空的待支付 IAP 订单
type ReconcileLogic struct {
svc *svc.ServiceContext
}
func NewReconcileLogic(svc *svc.ServiceContext) *ReconcileLogic {
return &ReconcileLogic{svc: svc}
}
func (l *ReconcileLogic) ProcessTask(ctx context.Context, _ *asynq.Task) error {
logger.Infof("[IAPReconcile] start at %s", time.Now().Format("2006-01-02 15:04:05"))
return l.reconcile(ctx, 5*time.Minute, 48*time.Hour, true)
}
// reconcile 核心对账逻辑,被第二层和第三层共享
func (l *ReconcileLogic) reconcile(ctx context.Context, minAge, maxAge time.Duration, requireTradeNo bool) error {
apiCfg, err := l.loadAppleAPIConfig(ctx)
if err != nil {
logger.Errorf("[IAPReconcile] load apple config error: %v", err)
return nil
}
if apiCfg == nil {
logger.Infof("[IAPReconcile] no enabled apple iap payment found, skip")
return nil
}
orders, err := l.svc.OrderModel.FindPendingIAPOrders(ctx, minAge, maxAge, requireTradeNo)
if err != nil {
logger.Errorf("[IAPReconcile] find pending orders error: %v", err)
return nil
}
if len(orders) == 0 {
return nil
}
logger.Infof("[IAPReconcile] found %d pending IAP orders (requireTradeNo=%v)", len(orders), requireTradeNo)
for _, ord := range orders {
if ord.TradeNo == "" {
continue
}
// 用 originalTransactionId即 trade_no向 Apple Server API 查询交易详情
jws, e := iapapple.GetTransactionInfo(*apiCfg, ord.TradeNo)
if e != nil {
logger.Errorf("[IAPReconcile] GetTransactionInfo error: orderNo=%s tradeNo=%s err=%v",
ord.OrderNo, ord.TradeNo, e)
time.Sleep(100 * time.Millisecond)
continue
}
// 解析 JWS不校验证书链只读取 payload
txPayload, e := iapapple.ParseTransactionJWS(jws)
if e != nil {
logger.Errorf("[IAPReconcile] ParseTransactionJWS error: orderNo=%s err=%v", ord.OrderNo, e)
continue
}
if txPayload.RevocationDate != nil {
// 苹果已撤销交易 → 关闭订单
logger.Infof("[IAPReconcile] transaction revoked, closing order: %s", ord.OrderNo)
_ = l.svc.OrderModel.UpdateOrderStatus(ctx, ord.OrderNo, 3)
continue
}
// 正常已付款交易 → enqueue 激活activateOrderLogic 内部有幂等保护)
payload, _ := json.Marshal(queueTypes.ForthwithActivateOrderPayload{OrderNo: ord.OrderNo})
task := asynq.NewTask(queueTypes.ForthwithActivateOrder, payload)
if _, e = l.svc.Queue.EnqueueContext(ctx, task); e != nil {
logger.Errorf("[IAPReconcile] enqueue activate error: orderNo=%s err=%v", ord.OrderNo, e)
} else {
logger.Infof("[IAPReconcile] enqueued activate: %s", ord.OrderNo)
}
time.Sleep(100 * time.Millisecond) // 避免 Apple API 限频
}
return nil
}
// loadAppleAPIConfig 从 payment 表读取第一个启用的 Apple IAP 支付方式配置
func (l *ReconcileLogic) loadAppleAPIConfig(ctx context.Context) (*iapapple.ServerAPIConfig, error) {
pays, err := l.svc.PaymentModel.FindListByPlatform(ctx, pkgpayment.AppleIAP.String())
if err != nil {
return nil, err
}
for _, pay := range pays {
if pay.Enable == nil || !*pay.Enable || pay.Config == "" {
continue
}
var cfg payment.AppleIAPConfig
if e := cfg.Unmarshal([]byte(pay.Config)); e != nil {
continue
}
if cfg.KeyID == "" || cfg.IssuerID == "" || cfg.PrivateKey == "" {
continue
}
apiCfg := &iapapple.ServerAPIConfig{
KeyID: cfg.KeyID,
IssuerID: cfg.IssuerID,
PrivateKey: fixPEM(cfg.PrivateKey),
Sandbox: cfg.Sandbox,
}
// 从 site custom data 读取 BundleID
var customData struct {
IapBundleId string `json:"iapBundleId"`
}
if l.svc.Config.Site.CustomData != "" {
_ = json.Unmarshal([]byte(l.svc.Config.Site.CustomData), &customData)
apiCfg.BundleID = customData.IapBundleId
}
return apiCfg, nil
}
return nil, nil
}
func fixPEM(key string) string {
if !strings.Contains(key, "\n") && strings.Contains(key, "BEGIN PRIVATE KEY") {
key = strings.ReplaceAll(key, " ", "\n")
key = strings.ReplaceAll(key, "-----BEGIN\nPRIVATE\nKEY-----", "-----BEGIN PRIVATE KEY-----")
key = strings.ReplaceAll(key, "-----END\nPRIVATE\nKEY-----", "-----END PRIVATE KEY-----")
}
return key
}

View File

@ -5,4 +5,6 @@ const (
SchedulerTotalServerData = "scheduler:total:server"
SchedulerResetTraffic = "scheduler:reset:traffic"
SchedulerTrafficStat = "scheduler:traffic:stat"
SchedulerIAPReconcile = "scheduler:iap:reconcile" // 第二层:每 5 分钟扫描待支付 IAP 订单
SchedulerIAPDailyReconcile = "scheduler:iap:daily:reconcile" // 第三层:日终全量对账
)

View File

@ -52,6 +52,18 @@ func (m *Service) Start() {
logger.Errorf("register update exchange rate task failed: %s", err.Error())
}
// schedule Apple IAP reconcile: every 5 minutes (第二层补单)
iapReconcileTask := asynq.NewTask(types.SchedulerIAPReconcile, nil)
if _, err := m.server.Register("@every 5m", iapReconcileTask, asynq.MaxRetry(1)); err != nil {
logger.Errorf("register iap reconcile task failed: %s", err.Error())
}
// schedule Apple IAP daily reconcile: every day at 02:00 (第三层日终对账)
iapDailyTask := asynq.NewTask(types.SchedulerIAPDailyReconcile, nil)
if _, err := m.server.Register("0 2 * * *", iapDailyTask, asynq.MaxRetry(1)); err != nil {
logger.Errorf("register iap daily reconcile task failed: %s", err.Error())
}
if err := m.server.Run(); err != nil {
logger.Errorf("run scheduler failed: %s", err.Error())
}