ipa
Build docker and publish / build (20.15.1) (push) Successful in 7m47s

This commit is contained in:
2026-03-08 05:12:28 -07:00
parent 94f5d0c07d
commit 7a3a53f1a9
14 changed files with 1193 additions and 4 deletions
+38
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
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
+126
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
+135
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
+118
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
+114
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
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
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
+23
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
+5
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))
}
+27
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)
}
+134
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
}
+2
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" // 第三层:日终全量对账
)
+12
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())
}