fix: 修改充值提现配置和充值接口
This commit is contained in:
83
.agents/skills/gitnexus/gitnexus-cli/SKILL.md
Normal file
83
.agents/skills/gitnexus/gitnexus-cli/SKILL.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
name: gitnexus-cli
|
||||||
|
description: "Use when the user needs to run GitNexus CLI commands like analyze/index a repo, check status, clean the index, generate a wiki, or list indexed repos. Examples: \"Index this repo\", \"Reanalyze the codebase\", \"Generate a wiki\""
|
||||||
|
---
|
||||||
|
|
||||||
|
# GitNexus CLI Commands
|
||||||
|
|
||||||
|
All commands work via `npx` — no global install required.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### analyze — Build or refresh the index
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx gitnexus analyze
|
||||||
|
```
|
||||||
|
|
||||||
|
Run from the project root. This parses all source files, builds the knowledge graph, writes it to `.gitnexus/`, and generates AGENTS.md / AGENTS.md context files.
|
||||||
|
|
||||||
|
| Flag | Effect |
|
||||||
|
| -------------- | ---------------------------------------------------------------- |
|
||||||
|
| `--force` | Force full re-index even if up to date |
|
||||||
|
| `--embeddings` | Enable embedding generation for semantic search (off by default) |
|
||||||
|
| `--drop-embeddings` | Drop existing embeddings on rebuild. By default, an `analyze` without `--embeddings` preserves them. |
|
||||||
|
|
||||||
|
**When to run:** First time in a project, after major code changes, or when `gitnexus://repo/{name}/context` reports the index is stale. In Codex, a PostToolUse hook detects staleness after `git commit` and `git merge` and notifies the agent to run `analyze` — the hook does not run analyze itself, to avoid blocking the agent for up to 120s and risking KuzuDB corruption on timeout.
|
||||||
|
|
||||||
|
### status — Check index freshness
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx gitnexus status
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows whether the current repo has a GitNexus index, when it was last updated, and symbol/relationship counts. Use this to check if re-indexing is needed.
|
||||||
|
|
||||||
|
### clean — Delete the index
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx gitnexus clean
|
||||||
|
```
|
||||||
|
|
||||||
|
Deletes the `.gitnexus/` directory and unregisters the repo from the global registry. Use before re-indexing if the index is corrupt or after removing GitNexus from a project.
|
||||||
|
|
||||||
|
| Flag | Effect |
|
||||||
|
| --------- | ------------------------------------------------- |
|
||||||
|
| `--force` | Skip confirmation prompt |
|
||||||
|
| `--all` | Clean all indexed repos, not just the current one |
|
||||||
|
|
||||||
|
### wiki — Generate documentation from the graph
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx gitnexus wiki
|
||||||
|
```
|
||||||
|
|
||||||
|
Generates repository documentation from the knowledge graph using an LLM. Requires an API key (saved to `~/.gitnexus/config.json` on first use).
|
||||||
|
|
||||||
|
| Flag | Effect |
|
||||||
|
| ------------------- | ----------------------------------------- |
|
||||||
|
| `--force` | Force full regeneration |
|
||||||
|
| `--model <model>` | LLM model (default: minimax/minimax-m2.5) |
|
||||||
|
| `--base-url <url>` | LLM API base URL |
|
||||||
|
| `--api-key <key>` | LLM API key |
|
||||||
|
| `--concurrency <n>` | Parallel LLM calls (default: 3) |
|
||||||
|
| `--gist` | Publish wiki as a public GitHub Gist |
|
||||||
|
|
||||||
|
### list — Show all indexed repos
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx gitnexus list
|
||||||
|
```
|
||||||
|
|
||||||
|
Lists all repositories registered in `~/.gitnexus/registry.json`. The MCP `list_repos` tool provides the same information.
|
||||||
|
|
||||||
|
## After Indexing
|
||||||
|
|
||||||
|
1. **Read `gitnexus://repo/{name}/context`** to verify the index loaded
|
||||||
|
2. Use the other GitNexus skills (`exploring`, `debugging`, `impact-analysis`, `refactoring`) for your task
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **"Not inside a git repository"**: Run from a directory inside a git repo
|
||||||
|
- **Index is stale after re-analyzing**: Restart Codex to reload the MCP server
|
||||||
|
- **Embeddings slow**: Omit `--embeddings` (it's off by default) or set `OPENAI_API_KEY` for faster API-based embedding
|
||||||
89
.agents/skills/gitnexus/gitnexus-debugging/SKILL.md
Normal file
89
.agents/skills/gitnexus/gitnexus-debugging/SKILL.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
name: gitnexus-debugging
|
||||||
|
description: "Use when the user is debugging a bug, tracing an error, or asking why something fails. Examples: \"Why is X failing?\", \"Where does this error come from?\", \"Trace this bug\""
|
||||||
|
---
|
||||||
|
|
||||||
|
# Debugging with GitNexus
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- "Why is this function failing?"
|
||||||
|
- "Trace where this error comes from"
|
||||||
|
- "Who calls this method?"
|
||||||
|
- "This endpoint returns 500"
|
||||||
|
- Investigating bugs, errors, or unexpected behavior
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. gitnexus_query({query: "<error or symptom>"}) → Find related execution flows
|
||||||
|
2. gitnexus_context({name: "<suspect>"}) → See callers/callees/processes
|
||||||
|
3. READ gitnexus://repo/{name}/process/{name} → Trace execution flow
|
||||||
|
4. gitnexus_cypher({query: "MATCH path..."}) → Custom traces if needed
|
||||||
|
```
|
||||||
|
|
||||||
|
> If "Index is stale" → run `npx gitnexus analyze` in terminal.
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
```
|
||||||
|
- [ ] Understand the symptom (error message, unexpected behavior)
|
||||||
|
- [ ] gitnexus_query for error text or related code
|
||||||
|
- [ ] Identify the suspect function from returned processes
|
||||||
|
- [ ] gitnexus_context to see callers and callees
|
||||||
|
- [ ] Trace execution flow via process resource if applicable
|
||||||
|
- [ ] gitnexus_cypher for custom call chain traces if needed
|
||||||
|
- [ ] Read source files to confirm root cause
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging Patterns
|
||||||
|
|
||||||
|
| Symptom | GitNexus Approach |
|
||||||
|
| -------------------- | ---------------------------------------------------------- |
|
||||||
|
| Error message | `gitnexus_query` for error text → `context` on throw sites |
|
||||||
|
| Wrong return value | `context` on the function → trace callees for data flow |
|
||||||
|
| Intermittent failure | `context` → look for external calls, async deps |
|
||||||
|
| Performance issue | `context` → find symbols with many callers (hot paths) |
|
||||||
|
| Recent regression | `detect_changes` to see what your changes affect |
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
**gitnexus_query** — find code related to error:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitnexus_query({query: "payment validation error"})
|
||||||
|
→ Processes: CheckoutFlow, ErrorHandling
|
||||||
|
→ Symbols: validatePayment, handlePaymentError, PaymentException
|
||||||
|
```
|
||||||
|
|
||||||
|
**gitnexus_context** — full context for a suspect:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitnexus_context({name: "validatePayment"})
|
||||||
|
→ Incoming calls: processCheckout, webhookHandler
|
||||||
|
→ Outgoing calls: verifyCard, fetchRates (external API!)
|
||||||
|
→ Processes: CheckoutFlow (step 3/7)
|
||||||
|
```
|
||||||
|
|
||||||
|
**gitnexus_cypher** — custom call chain traces:
|
||||||
|
|
||||||
|
```cypher
|
||||||
|
MATCH path = (a)-[:CodeRelation {type: 'CALLS'}*1..2]->(b:Function {name: "validatePayment"})
|
||||||
|
RETURN [n IN nodes(path) | n.name] AS chain
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example: "Payment endpoint returns 500 intermittently"
|
||||||
|
|
||||||
|
```
|
||||||
|
1. gitnexus_query({query: "payment error handling"})
|
||||||
|
→ Processes: CheckoutFlow, ErrorHandling
|
||||||
|
→ Symbols: validatePayment, handlePaymentError
|
||||||
|
|
||||||
|
2. gitnexus_context({name: "validatePayment"})
|
||||||
|
→ Outgoing calls: verifyCard, fetchRates (external API!)
|
||||||
|
|
||||||
|
3. READ gitnexus://repo/my-app/process/CheckoutFlow
|
||||||
|
→ Step 3: validatePayment → calls fetchRates (external)
|
||||||
|
|
||||||
|
4. Root cause: fetchRates calls external API without proper timeout
|
||||||
|
```
|
||||||
78
.agents/skills/gitnexus/gitnexus-exploring/SKILL.md
Normal file
78
.agents/skills/gitnexus/gitnexus-exploring/SKILL.md
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
---
|
||||||
|
name: gitnexus-exploring
|
||||||
|
description: "Use when the user asks how code works, wants to understand architecture, trace execution flows, or explore unfamiliar parts of the codebase. Examples: \"How does X work?\", \"What calls this function?\", \"Show me the auth flow\""
|
||||||
|
---
|
||||||
|
|
||||||
|
# Exploring Codebases with GitNexus
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- "How does authentication work?"
|
||||||
|
- "What's the project structure?"
|
||||||
|
- "Show me the main components"
|
||||||
|
- "Where is the database logic?"
|
||||||
|
- Understanding code you haven't seen before
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. READ gitnexus://repos → Discover indexed repos
|
||||||
|
2. READ gitnexus://repo/{name}/context → Codebase overview, check staleness
|
||||||
|
3. gitnexus_query({query: "<what you want to understand>"}) → Find related execution flows
|
||||||
|
4. gitnexus_context({name: "<symbol>"}) → Deep dive on specific symbol
|
||||||
|
5. READ gitnexus://repo/{name}/process/{name} → Trace full execution flow
|
||||||
|
```
|
||||||
|
|
||||||
|
> If step 2 says "Index is stale" → run `npx gitnexus analyze` in terminal.
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
```
|
||||||
|
- [ ] READ gitnexus://repo/{name}/context
|
||||||
|
- [ ] gitnexus_query for the concept you want to understand
|
||||||
|
- [ ] Review returned processes (execution flows)
|
||||||
|
- [ ] gitnexus_context on key symbols for callers/callees
|
||||||
|
- [ ] READ process resource for full execution traces
|
||||||
|
- [ ] Read source files for implementation details
|
||||||
|
```
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
| Resource | What you get |
|
||||||
|
| --------------------------------------- | ------------------------------------------------------- |
|
||||||
|
| `gitnexus://repo/{name}/context` | Stats, staleness warning (~150 tokens) |
|
||||||
|
| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores (~300 tokens) |
|
||||||
|
| `gitnexus://repo/{name}/cluster/{name}` | Area members with file paths (~500 tokens) |
|
||||||
|
| `gitnexus://repo/{name}/process/{name}` | Step-by-step execution trace (~200 tokens) |
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
**gitnexus_query** — find execution flows related to a concept:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitnexus_query({query: "payment processing"})
|
||||||
|
→ Processes: CheckoutFlow, RefundFlow, WebhookHandler
|
||||||
|
→ Symbols grouped by flow with file locations
|
||||||
|
```
|
||||||
|
|
||||||
|
**gitnexus_context** — 360-degree view of a symbol:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitnexus_context({name: "validateUser"})
|
||||||
|
→ Incoming calls: loginHandler, apiMiddleware
|
||||||
|
→ Outgoing calls: checkToken, getUserById
|
||||||
|
→ Processes: LoginFlow (step 2/5), TokenRefresh (step 1/3)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example: "How does payment processing work?"
|
||||||
|
|
||||||
|
```
|
||||||
|
1. READ gitnexus://repo/my-app/context → 918 symbols, 45 processes
|
||||||
|
2. gitnexus_query({query: "payment processing"})
|
||||||
|
→ CheckoutFlow: processPayment → validateCard → chargeStripe
|
||||||
|
→ RefundFlow: initiateRefund → calculateRefund → processRefund
|
||||||
|
3. gitnexus_context({name: "processPayment"})
|
||||||
|
→ Incoming: checkoutHandler, webhookHandler
|
||||||
|
→ Outgoing: validateCard, chargeStripe, saveTransaction
|
||||||
|
4. Read src/payments/processor.ts for implementation details
|
||||||
|
```
|
||||||
64
.agents/skills/gitnexus/gitnexus-guide/SKILL.md
Normal file
64
.agents/skills/gitnexus/gitnexus-guide/SKILL.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
name: gitnexus-guide
|
||||||
|
description: "Use when the user asks about GitNexus itself — available tools, how to query the knowledge graph, MCP resources, graph schema, or workflow reference. Examples: \"What GitNexus tools are available?\", \"How do I use GitNexus?\""
|
||||||
|
---
|
||||||
|
|
||||||
|
# GitNexus Guide
|
||||||
|
|
||||||
|
Quick reference for all GitNexus MCP tools, resources, and the knowledge graph schema.
|
||||||
|
|
||||||
|
## Always Start Here
|
||||||
|
|
||||||
|
For any task involving code understanding, debugging, impact analysis, or refactoring:
|
||||||
|
|
||||||
|
1. **Read `gitnexus://repo/{name}/context`** — codebase overview + check index freshness
|
||||||
|
2. **Match your task to a skill below** and **read that skill file**
|
||||||
|
3. **Follow the skill's workflow and checklist**
|
||||||
|
|
||||||
|
> If step 1 warns the index is stale, run `npx gitnexus analyze` in the terminal first.
|
||||||
|
|
||||||
|
## Skills
|
||||||
|
|
||||||
|
| Task | Skill to read |
|
||||||
|
| -------------------------------------------- | ------------------- |
|
||||||
|
| Understand architecture / "How does X work?" | `gitnexus-exploring` |
|
||||||
|
| Blast radius / "What breaks if I change X?" | `gitnexus-impact-analysis` |
|
||||||
|
| Trace bugs / "Why is X failing?" | `gitnexus-debugging` |
|
||||||
|
| Rename / extract / split / refactor | `gitnexus-refactoring` |
|
||||||
|
| Tools, resources, schema reference | `gitnexus-guide` (this file) |
|
||||||
|
| Index, status, clean, wiki CLI commands | `gitnexus-cli` |
|
||||||
|
|
||||||
|
## Tools Reference
|
||||||
|
|
||||||
|
| Tool | What it gives you |
|
||||||
|
| ---------------- | ------------------------------------------------------------------------ |
|
||||||
|
| `query` | Process-grouped code intelligence — execution flows related to a concept |
|
||||||
|
| `context` | 360-degree symbol view — categorized refs, processes it participates in |
|
||||||
|
| `impact` | Symbol blast radius — what breaks at depth 1/2/3 with confidence |
|
||||||
|
| `detect_changes` | Git-diff impact — what do your current changes affect |
|
||||||
|
| `rename` | Multi-file coordinated rename with confidence-tagged edits |
|
||||||
|
| `cypher` | Raw graph queries (read `gitnexus://repo/{name}/schema` first) |
|
||||||
|
| `list_repos` | Discover indexed repos |
|
||||||
|
|
||||||
|
## Resources Reference
|
||||||
|
|
||||||
|
Lightweight reads (~100-500 tokens) for navigation:
|
||||||
|
|
||||||
|
| Resource | Content |
|
||||||
|
| ---------------------------------------------- | ----------------------------------------- |
|
||||||
|
| `gitnexus://repo/{name}/context` | Stats, staleness check |
|
||||||
|
| `gitnexus://repo/{name}/clusters` | All functional areas with cohesion scores |
|
||||||
|
| `gitnexus://repo/{name}/cluster/{clusterName}` | Area members |
|
||||||
|
| `gitnexus://repo/{name}/processes` | All execution flows |
|
||||||
|
| `gitnexus://repo/{name}/process/{processName}` | Step-by-step trace |
|
||||||
|
| `gitnexus://repo/{name}/schema` | Graph schema for Cypher |
|
||||||
|
|
||||||
|
## Graph Schema
|
||||||
|
|
||||||
|
**Nodes:** File, Function, Class, Interface, Method, Community, Process
|
||||||
|
**Edges (via CodeRelation.type):** CALLS, IMPORTS, EXTENDS, IMPLEMENTS, DEFINES, MEMBER_OF, STEP_IN_PROCESS
|
||||||
|
|
||||||
|
```cypher
|
||||||
|
MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "myFunc"})
|
||||||
|
RETURN caller.name, caller.filePath
|
||||||
|
```
|
||||||
97
.agents/skills/gitnexus/gitnexus-impact-analysis/SKILL.md
Normal file
97
.agents/skills/gitnexus/gitnexus-impact-analysis/SKILL.md
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
name: gitnexus-impact-analysis
|
||||||
|
description: "Use when the user wants to know what will break if they change something, or needs safety analysis before editing code. Examples: \"Is it safe to change X?\", \"What depends on this?\", \"What will break?\""
|
||||||
|
---
|
||||||
|
|
||||||
|
# Impact Analysis with GitNexus
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- "Is it safe to change this function?"
|
||||||
|
- "What will break if I modify X?"
|
||||||
|
- "Show me the blast radius"
|
||||||
|
- "Who uses this code?"
|
||||||
|
- Before making non-trivial code changes
|
||||||
|
- Before committing — to understand what your changes affect
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. gitnexus_impact({target: "X", direction: "upstream"}) → What depends on this
|
||||||
|
2. READ gitnexus://repo/{name}/processes → Check affected execution flows
|
||||||
|
3. gitnexus_detect_changes() → Map current git changes to affected flows
|
||||||
|
4. Assess risk and report to user
|
||||||
|
```
|
||||||
|
|
||||||
|
> If "Index is stale" → run `npx gitnexus analyze` in terminal.
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
```
|
||||||
|
- [ ] gitnexus_impact({target, direction: "upstream"}) to find dependents
|
||||||
|
- [ ] Review d=1 items first (these WILL BREAK)
|
||||||
|
- [ ] Check high-confidence (>0.8) dependencies
|
||||||
|
- [ ] READ processes to check affected execution flows
|
||||||
|
- [ ] gitnexus_detect_changes() for pre-commit check
|
||||||
|
- [ ] Assess risk level and report to user
|
||||||
|
```
|
||||||
|
|
||||||
|
## Understanding Output
|
||||||
|
|
||||||
|
| Depth | Risk Level | Meaning |
|
||||||
|
| ----- | ---------------- | ------------------------ |
|
||||||
|
| d=1 | **WILL BREAK** | Direct callers/importers |
|
||||||
|
| d=2 | LIKELY AFFECTED | Indirect dependencies |
|
||||||
|
| d=3 | MAY NEED TESTING | Transitive effects |
|
||||||
|
|
||||||
|
## Risk Assessment
|
||||||
|
|
||||||
|
| Affected | Risk |
|
||||||
|
| ------------------------------ | -------- |
|
||||||
|
| <5 symbols, few processes | LOW |
|
||||||
|
| 5-15 symbols, 2-5 processes | MEDIUM |
|
||||||
|
| >15 symbols or many processes | HIGH |
|
||||||
|
| Critical path (auth, payments) | CRITICAL |
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
**gitnexus_impact** — the primary tool for symbol blast radius:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitnexus_impact({
|
||||||
|
target: "validateUser",
|
||||||
|
direction: "upstream",
|
||||||
|
minConfidence: 0.8,
|
||||||
|
maxDepth: 3
|
||||||
|
})
|
||||||
|
|
||||||
|
→ d=1 (WILL BREAK):
|
||||||
|
- loginHandler (src/auth/login.ts:42) [CALLS, 100%]
|
||||||
|
- apiMiddleware (src/api/middleware.ts:15) [CALLS, 100%]
|
||||||
|
|
||||||
|
→ d=2 (LIKELY AFFECTED):
|
||||||
|
- authRouter (src/routes/auth.ts:22) [CALLS, 95%]
|
||||||
|
```
|
||||||
|
|
||||||
|
**gitnexus_detect_changes** — git-diff based impact analysis:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitnexus_detect_changes({scope: "staged"})
|
||||||
|
|
||||||
|
→ Changed: 5 symbols in 3 files
|
||||||
|
→ Affected: LoginFlow, TokenRefresh, APIMiddlewarePipeline
|
||||||
|
→ Risk: MEDIUM
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example: "What breaks if I change validateUser?"
|
||||||
|
|
||||||
|
```
|
||||||
|
1. gitnexus_impact({target: "validateUser", direction: "upstream"})
|
||||||
|
→ d=1: loginHandler, apiMiddleware (WILL BREAK)
|
||||||
|
→ d=2: authRouter, sessionManager (LIKELY AFFECTED)
|
||||||
|
|
||||||
|
2. READ gitnexus://repo/my-app/processes
|
||||||
|
→ LoginFlow and TokenRefresh touch validateUser
|
||||||
|
|
||||||
|
3. Risk: 2 direct callers, 2 processes = MEDIUM
|
||||||
|
```
|
||||||
121
.agents/skills/gitnexus/gitnexus-refactoring/SKILL.md
Normal file
121
.agents/skills/gitnexus/gitnexus-refactoring/SKILL.md
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
---
|
||||||
|
name: gitnexus-refactoring
|
||||||
|
description: "Use when the user wants to rename, extract, split, move, or restructure code safely. Examples: \"Rename this function\", \"Extract this into a module\", \"Refactor this class\", \"Move this to a separate file\""
|
||||||
|
---
|
||||||
|
|
||||||
|
# Refactoring with GitNexus
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
- "Rename this function safely"
|
||||||
|
- "Extract this into a module"
|
||||||
|
- "Split this service"
|
||||||
|
- "Move this to a new file"
|
||||||
|
- Any task involving renaming, extracting, splitting, or restructuring code
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. gitnexus_impact({target: "X", direction: "upstream"}) → Map all dependents
|
||||||
|
2. gitnexus_query({query: "X"}) → Find execution flows involving X
|
||||||
|
3. gitnexus_context({name: "X"}) → See all incoming/outgoing refs
|
||||||
|
4. Plan update order: interfaces → implementations → callers → tests
|
||||||
|
```
|
||||||
|
|
||||||
|
> If "Index is stale" → run `npx gitnexus analyze` in terminal.
|
||||||
|
|
||||||
|
## Checklists
|
||||||
|
|
||||||
|
### Rename Symbol
|
||||||
|
|
||||||
|
```
|
||||||
|
- [ ] gitnexus_rename({symbol_name: "oldName", new_name: "newName", dry_run: true}) — preview all edits
|
||||||
|
- [ ] Review graph edits (high confidence) and ast_search edits (review carefully)
|
||||||
|
- [ ] If satisfied: gitnexus_rename({..., dry_run: false}) — apply edits
|
||||||
|
- [ ] gitnexus_detect_changes() — verify only expected files changed
|
||||||
|
- [ ] Run tests for affected processes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extract Module
|
||||||
|
|
||||||
|
```
|
||||||
|
- [ ] gitnexus_context({name: target}) — see all incoming/outgoing refs
|
||||||
|
- [ ] gitnexus_impact({target, direction: "upstream"}) — find all external callers
|
||||||
|
- [ ] Define new module interface
|
||||||
|
- [ ] Extract code, update imports
|
||||||
|
- [ ] gitnexus_detect_changes() — verify affected scope
|
||||||
|
- [ ] Run tests for affected processes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Split Function/Service
|
||||||
|
|
||||||
|
```
|
||||||
|
- [ ] gitnexus_context({name: target}) — understand all callees
|
||||||
|
- [ ] Group callees by responsibility
|
||||||
|
- [ ] gitnexus_impact({target, direction: "upstream"}) — map callers to update
|
||||||
|
- [ ] Create new functions/services
|
||||||
|
- [ ] Update callers
|
||||||
|
- [ ] gitnexus_detect_changes() — verify affected scope
|
||||||
|
- [ ] Run tests for affected processes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
**gitnexus_rename** — automated multi-file rename:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true})
|
||||||
|
→ 12 edits across 8 files
|
||||||
|
→ 10 graph edits (high confidence), 2 ast_search edits (review)
|
||||||
|
→ Changes: [{file_path, edits: [{line, old_text, new_text, confidence}]}]
|
||||||
|
```
|
||||||
|
|
||||||
|
**gitnexus_impact** — map all dependents first:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitnexus_impact({target: "validateUser", direction: "upstream"})
|
||||||
|
→ d=1: loginHandler, apiMiddleware, testUtils
|
||||||
|
→ Affected Processes: LoginFlow, TokenRefresh
|
||||||
|
```
|
||||||
|
|
||||||
|
**gitnexus_detect_changes** — verify your changes after refactoring:
|
||||||
|
|
||||||
|
```
|
||||||
|
gitnexus_detect_changes({scope: "all"})
|
||||||
|
→ Changed: 8 files, 12 symbols
|
||||||
|
→ Affected processes: LoginFlow, TokenRefresh
|
||||||
|
→ Risk: MEDIUM
|
||||||
|
```
|
||||||
|
|
||||||
|
**gitnexus_cypher** — custom reference queries:
|
||||||
|
|
||||||
|
```cypher
|
||||||
|
MATCH (caller)-[:CodeRelation {type: 'CALLS'}]->(f:Function {name: "validateUser"})
|
||||||
|
RETURN caller.name, caller.filePath ORDER BY caller.filePath
|
||||||
|
```
|
||||||
|
|
||||||
|
## Risk Rules
|
||||||
|
|
||||||
|
| Risk Factor | Mitigation |
|
||||||
|
| ------------------- | ----------------------------------------- |
|
||||||
|
| Many callers (>5) | Use gitnexus_rename for automated updates |
|
||||||
|
| Cross-area refs | Use detect_changes after to verify scope |
|
||||||
|
| String/dynamic refs | gitnexus_query to find them |
|
||||||
|
| External/public API | Version and deprecate properly |
|
||||||
|
|
||||||
|
## Example: Rename `validateUser` to `authenticateUser`
|
||||||
|
|
||||||
|
```
|
||||||
|
1. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: true})
|
||||||
|
→ 12 edits: 10 graph (safe), 2 ast_search (review)
|
||||||
|
→ Files: validator.ts, login.ts, middleware.ts, config.json...
|
||||||
|
|
||||||
|
2. Review ast_search edits (config.json: dynamic reference!)
|
||||||
|
|
||||||
|
3. gitnexus_rename({symbol_name: "validateUser", new_name: "authenticateUser", dry_run: false})
|
||||||
|
→ Applied 12 edits across 8 files
|
||||||
|
|
||||||
|
4. gitnexus_detect_changes({scope: "all"})
|
||||||
|
→ Affected: LoginFlow, TokenRefresh
|
||||||
|
→ Risk: MEDIUM — run tests for these flows
|
||||||
|
```
|
||||||
4
pnpm-workspace.yaml
Normal file
4
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
allowBuilds:
|
||||||
|
esbuild: true
|
||||||
|
msw: true
|
||||||
|
sharp: true
|
||||||
@@ -14,6 +14,9 @@ import type {
|
|||||||
DepositWithdrawConfig,
|
DepositWithdrawConfig,
|
||||||
DepositWithdrawConfigDto,
|
DepositWithdrawConfigDto,
|
||||||
FinanceCurrencyConfigDto,
|
FinanceCurrencyConfigDto,
|
||||||
|
FinanceDepositConfigDto,
|
||||||
|
FinanceDepositFieldsDto,
|
||||||
|
FinanceDepositMethodDto,
|
||||||
FinanceOrderItemDto,
|
FinanceOrderItemDto,
|
||||||
FinanceOrderList,
|
FinanceOrderList,
|
||||||
FinanceOrderListDto,
|
FinanceOrderListDto,
|
||||||
@@ -80,7 +83,7 @@ function normalizePayChannel(dto: FinancePayChannelDto) {
|
|||||||
code: dto.code,
|
code: dto.code,
|
||||||
name: dto.name,
|
name: dto.name,
|
||||||
sort: Number.isFinite(dto.sort) ? dto.sort : 0,
|
sort: Number.isFinite(dto.sort) ? dto.sort : 0,
|
||||||
status: dto.status,
|
status: typeof dto.status === 'number' ? dto.status : 1,
|
||||||
tierIds: Array.isArray(dto.tier_ids) ? dto.tier_ids : [],
|
tierIds: Array.isArray(dto.tier_ids) ? dto.tier_ids : [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,6 +94,8 @@ function normalizeWithdrawBank(dto: FinanceWithdrawBankDto, index: number) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
code,
|
code,
|
||||||
|
currencyCode: dto.currency_code ?? null,
|
||||||
|
gatewayCode: dto.gateway_code ?? null,
|
||||||
label,
|
label,
|
||||||
sort:
|
sort:
|
||||||
typeof dto.sort === 'number' && Number.isFinite(dto.sort)
|
typeof dto.sort === 'number' && Number.isFinite(dto.sort)
|
||||||
@@ -100,15 +105,68 @@ function normalizeWithdrawBank(dto: FinanceWithdrawBankDto, index: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeDepositMethod(dto: FinanceDepositMethodDto) {
|
||||||
|
return {
|
||||||
|
code: dto.code,
|
||||||
|
currencyCode: dto.currency_code,
|
||||||
|
gatewayCode: dto.gateway_code,
|
||||||
|
name: dto.name,
|
||||||
|
requiresBank: dto.requires_bank,
|
||||||
|
requiresBankAccount: dto.requires_bank_account,
|
||||||
|
requiresDepositorName: dto.requires_depositor_name,
|
||||||
|
requiresFromAddress: dto.requires_from_address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDepositFields(dto: FinanceDepositFieldsDto | undefined = {}) {
|
||||||
|
return {
|
||||||
|
depositBankAccountParam:
|
||||||
|
dto.deposit_bank_account_param ?? 'deposit_bank_account',
|
||||||
|
depositBankParam: dto.deposit_bank_param ?? 'deposit_bank',
|
||||||
|
depositFromAddressParam:
|
||||||
|
dto.deposit_from_address_param ?? 'deposit_from_address',
|
||||||
|
depositNameServerSide: dto.deposit_name_server_side ?? false,
|
||||||
|
paymentTypeParam: dto.payment_type_param ?? 'payment_type',
|
||||||
|
requireChannelCode: dto.require_channel_code ?? true,
|
||||||
|
requireIdempotencyKey: dto.require_idempotency_key ?? true,
|
||||||
|
requirePaymentType: dto.require_payment_type ?? false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDepositConfig(dto: FinanceDepositConfigDto | undefined) {
|
||||||
|
return {
|
||||||
|
banks: (dto?.banks ?? []).map(normalizeWithdrawBank),
|
||||||
|
defaultChannelByCurrency: dto?.default_channel_by_currency ?? {},
|
||||||
|
fields: normalizeDepositFields(dto?.fields),
|
||||||
|
methods: (dto?.methods ?? []).map(normalizeDepositMethod),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeWithdrawFields(dto: FinanceWithdrawConfigDto['fields'] = {}) {
|
||||||
|
return {
|
||||||
|
receiveTypeBankOnly: dto.receive_type_bank_only ?? true,
|
||||||
|
requireBankBranch: dto.require_bank_branch ?? false,
|
||||||
|
requireBankCode: dto.require_bank_code ?? true,
|
||||||
|
requireChannelCode: dto.require_channel_code ?? true,
|
||||||
|
requireReceiveAccount: dto.require_receive_account ?? true,
|
||||||
|
requireReceiverEmail: dto.require_receiver_email ?? true,
|
||||||
|
requireReceiverMobile: dto.require_receiver_mobile ?? true,
|
||||||
|
requireReceiverName: dto.require_receiver_name ?? true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeWithdrawConfig(dto: FinanceWithdrawConfigDto) {
|
function normalizeWithdrawConfig(dto: FinanceWithdrawConfigDto) {
|
||||||
return {
|
return {
|
||||||
banks: (dto.banks ?? []).map(normalizeWithdrawBank),
|
banks: (dto.banks ?? []).map(normalizeWithdrawBank),
|
||||||
feeNote: dto.fee_note,
|
feeNote: dto.fee_note,
|
||||||
|
fields: normalizeWithdrawFields(dto.fields),
|
||||||
minBank: dto.min_bank,
|
minBank: dto.min_bank,
|
||||||
minEwallet: dto.min_ewallet,
|
minEwallet: dto.min_ewallet,
|
||||||
|
payChannels: (dto.pay_channels ?? []).map(normalizePayChannel),
|
||||||
processingNote: dto.processing_note,
|
processingNote: dto.processing_note,
|
||||||
rateHint: dto.rate_hint,
|
rateHint: dto.rate_hint,
|
||||||
rateMode: dto.rate_mode,
|
rateMode: dto.rate_mode,
|
||||||
|
reviewThresholdCoin: dto.review_threshold_coin ?? '0',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +175,8 @@ function normalizeDepositWithdrawConfig(
|
|||||||
): DepositWithdrawConfig {
|
): DepositWithdrawConfig {
|
||||||
return {
|
return {
|
||||||
currencies: (dto.currencies ?? []).map(normalizeCurrency),
|
currencies: (dto.currencies ?? []).map(normalizeCurrency),
|
||||||
|
defaultDepositChannelCode: dto.default_deposit_channel_code ?? '',
|
||||||
|
deposit: normalizeDepositConfig(dto.deposit),
|
||||||
payChannels: (dto.pay_channels ?? []).map(normalizePayChannel),
|
payChannels: (dto.pay_channels ?? []).map(normalizePayChannel),
|
||||||
platformCoinLabel: dto.platform_coin_label,
|
platformCoinLabel: dto.platform_coin_label,
|
||||||
rates: (dto.rates ?? []).map(normalizeRate),
|
rates: (dto.rates ?? []).map(normalizeRate),
|
||||||
|
|||||||
@@ -285,6 +285,22 @@ export const DEFAULT_WITHDRAW_CONFIG: DepositWithdrawConfig = {
|
|||||||
withdrawCoinsPerFiatValue: 100,
|
withdrawCoinsPerFiatValue: 100,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
defaultDepositChannelCode: '',
|
||||||
|
deposit: {
|
||||||
|
banks: [],
|
||||||
|
defaultChannelByCurrency: {},
|
||||||
|
fields: {
|
||||||
|
depositBankAccountParam: 'deposit_bank_account',
|
||||||
|
depositBankParam: 'deposit_bank',
|
||||||
|
depositFromAddressParam: 'deposit_from_address',
|
||||||
|
depositNameServerSide: false,
|
||||||
|
paymentTypeParam: 'payment_type',
|
||||||
|
requireChannelCode: true,
|
||||||
|
requireIdempotencyKey: true,
|
||||||
|
requirePaymentType: false,
|
||||||
|
},
|
||||||
|
methods: [],
|
||||||
|
},
|
||||||
payChannels: [],
|
payChannels: [],
|
||||||
platformCoinLabel: '钻石',
|
platformCoinLabel: '钻石',
|
||||||
rates: [
|
rates: [
|
||||||
@@ -297,11 +313,23 @@ export const DEFAULT_WITHDRAW_CONFIG: DepositWithdrawConfig = {
|
|||||||
withdraw: {
|
withdraw: {
|
||||||
banks: [],
|
banks: [],
|
||||||
feeNote: 'RM10 - RM99.99 之间的交易将收取最低RM 1的提现手续费',
|
feeNote: 'RM10 - RM99.99 之间的交易将收取最低RM 1的提现手续费',
|
||||||
|
fields: {
|
||||||
|
receiveTypeBankOnly: true,
|
||||||
|
requireBankBranch: false,
|
||||||
|
requireBankCode: true,
|
||||||
|
requireChannelCode: true,
|
||||||
|
requireReceiveAccount: true,
|
||||||
|
requireReceiverEmail: true,
|
||||||
|
requireReceiverMobile: true,
|
||||||
|
requireReceiverName: true,
|
||||||
|
},
|
||||||
minBank: '10',
|
minBank: '10',
|
||||||
minEwallet: '10',
|
minEwallet: '10',
|
||||||
|
payChannels: [],
|
||||||
processingNote: '30s即可到账',
|
processingNote: '30s即可到账',
|
||||||
rateHint: '汇率为参考价格,实际以提现时为准。',
|
rateHint: '汇率为参考价格,实际以提现时为准。',
|
||||||
rateMode: 'fixed',
|
rateMode: 'fixed',
|
||||||
|
reviewThresholdCoin: '0',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import { useRef } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { createDeposit } from '@/api'
|
import { createDeposit } from '@/api'
|
||||||
import { DataLoadingIndicator } from '@/components/ui/data-loading-indicator'
|
import { DataLoadingIndicator } from '@/components/ui/data-loading-indicator'
|
||||||
|
import { DEFAULT_WITHDRAW_CONFIG } from '@/constants'
|
||||||
import { useDepositTierList } from '@/hooks/use-deposit-tier-list'
|
import { useDepositTierList } from '@/hooks/use-deposit-tier-list'
|
||||||
|
import { useDepositWithdrawConfig } from '@/hooks/use-deposit-withdraw-config'
|
||||||
import { notify } from '@/lib/notify'
|
import { notify } from '@/lib/notify'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import type { DepositTierItem } from '@/type'
|
import type { DepositTierItem } from '@/type'
|
||||||
@@ -17,21 +19,39 @@ function formatNumber(value: number) {
|
|||||||
|
|
||||||
function DesktopTopup() {
|
function DesktopTopup() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const depositWithdrawConfigQuery = useDepositWithdrawConfig()
|
||||||
const tierListQuery = useDepositTierList()
|
const tierListQuery = useDepositTierList()
|
||||||
|
const depositWithdrawConfig =
|
||||||
|
depositWithdrawConfigQuery.data ?? DEFAULT_WITHDRAW_CONFIG
|
||||||
|
const isLoading =
|
||||||
|
tierListQuery.isLoading || depositWithdrawConfigQuery.isLoading
|
||||||
|
const isError = tierListQuery.isError || depositWithdrawConfigQuery.isError
|
||||||
const tiers = tierListQuery.data ?? []
|
const tiers = tierListQuery.data ?? []
|
||||||
const createDepositInFlightRef = useRef(false)
|
const createDepositInFlightRef = useRef(false)
|
||||||
const pendingPayWindowRef = useRef<Window | null>(null)
|
const pendingPayWindowRef = useRef<Window | null>(null)
|
||||||
const createDepositMutation = useMutation({
|
const createDepositMutation = useMutation({
|
||||||
mutationFn: ({
|
mutationFn: ({
|
||||||
channelCode,
|
channelCode,
|
||||||
|
depositBank,
|
||||||
|
depositBankAccount,
|
||||||
|
depositFromAddress,
|
||||||
|
paymentType,
|
||||||
tierId,
|
tierId,
|
||||||
}: {
|
}: {
|
||||||
channelCode: string
|
channelCode: string
|
||||||
|
depositBank?: string
|
||||||
|
depositBankAccount?: string
|
||||||
|
depositFromAddress?: string
|
||||||
|
paymentType: string
|
||||||
tierId: string
|
tierId: string
|
||||||
}) =>
|
}) =>
|
||||||
createDeposit({
|
createDeposit({
|
||||||
channel_code: channelCode,
|
channel_code: channelCode,
|
||||||
|
deposit_bank: depositBank,
|
||||||
|
deposit_bank_account: depositBankAccount,
|
||||||
|
deposit_from_address: depositFromAddress,
|
||||||
idempotency_key: String(Date.now()),
|
idempotency_key: String(Date.now()),
|
||||||
|
payment_type: paymentType,
|
||||||
tier_id: tierId,
|
tier_id: tierId,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
@@ -41,13 +61,34 @@ function DesktopTopup() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const channelCode = tier.payChannelCode ?? tier.channels[0]?.code ?? ''
|
const channelCode =
|
||||||
|
tier.payChannelCode ??
|
||||||
|
tier.channels[0]?.code ??
|
||||||
|
(tier.currency
|
||||||
|
? depositWithdrawConfig.deposit.defaultChannelByCurrency[tier.currency]
|
||||||
|
: undefined) ??
|
||||||
|
depositWithdrawConfig.defaultDepositChannelCode
|
||||||
|
const selectedPaymentMethod =
|
||||||
|
depositWithdrawConfig.deposit.methods.find(
|
||||||
|
(method) => method.currencyCode === tier.currency,
|
||||||
|
) ?? depositWithdrawConfig.deposit.methods[0]
|
||||||
|
const paymentType = selectedPaymentMethod?.code ?? ''
|
||||||
|
const depositBank = selectedPaymentMethod?.requiresBank
|
||||||
|
? depositWithdrawConfig.deposit.banks.find(
|
||||||
|
(bank) => bank.currencyCode === tier.currency,
|
||||||
|
)?.code
|
||||||
|
: undefined
|
||||||
|
|
||||||
if (!channelCode) {
|
if (!channelCode) {
|
||||||
notify.error(t('commonUi.toast.configError'))
|
notify.error(t('commonUi.toast.configError'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!paymentType) {
|
||||||
|
notify.error(t('commonUi.toast.configError'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
createDepositInFlightRef.current = true
|
createDepositInFlightRef.current = true
|
||||||
const payWindow = window.open('', '_blank')
|
const payWindow = window.open('', '_blank')
|
||||||
|
|
||||||
@@ -63,6 +104,8 @@ function DesktopTopup() {
|
|||||||
try {
|
try {
|
||||||
const result = await createDepositMutation.mutateAsync({
|
const result = await createDepositMutation.mutateAsync({
|
||||||
channelCode,
|
channelCode,
|
||||||
|
depositBank,
|
||||||
|
paymentType,
|
||||||
tierId: tier.id,
|
tierId: tier.id,
|
||||||
})
|
})
|
||||||
const payUrl = result.pay_url.trim()
|
const payUrl = result.pay_url.trim()
|
||||||
@@ -105,12 +148,12 @@ function DesktopTopup() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{tierListQuery.isLoading ? (
|
{isLoading ? (
|
||||||
<DataLoadingIndicator
|
<DataLoadingIndicator
|
||||||
label={t('gameDesktop.topup.tier.loading')}
|
label={t('gameDesktop.topup.tier.loading')}
|
||||||
className="h-full min-h-0 rounded-[calc(var(--design-unit)*6)] border border-[rgba(103,227,239,0.18)] bg-[rgba(6,24,35,0.52)]"
|
className="h-full min-h-0 rounded-[calc(var(--design-unit)*6)] border border-[rgba(103,227,239,0.18)] bg-[rgba(6,24,35,0.52)]"
|
||||||
/>
|
/>
|
||||||
) : tierListQuery.isError ? (
|
) : isError ? (
|
||||||
<div className="flex h-full min-h-0 items-center justify-center rounded-[calc(var(--design-unit)*6)] border border-[rgba(185,63,68,0.28)] bg-[rgba(34,13,16,0.42)] text-design-16 text-[#F4A9AE]">
|
<div className="flex h-full min-h-0 items-center justify-center rounded-[calc(var(--design-unit)*6)] border border-[rgba(185,63,68,0.28)] bg-[rgba(34,13,16,0.42)] text-design-16 text-[#F4A9AE]">
|
||||||
{t('gameDesktop.topup.tier.failed')}
|
{t('gameDesktop.topup.tier.failed')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import { useRef } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { createDeposit } from '@/api'
|
import { createDeposit } from '@/api'
|
||||||
import { DataLoadingIndicator } from '@/components/ui/data-loading-indicator'
|
import { DataLoadingIndicator } from '@/components/ui/data-loading-indicator'
|
||||||
|
import { DEFAULT_WITHDRAW_CONFIG } from '@/constants'
|
||||||
import { useDepositTierList } from '@/hooks/use-deposit-tier-list'
|
import { useDepositTierList } from '@/hooks/use-deposit-tier-list'
|
||||||
|
import { useDepositWithdrawConfig } from '@/hooks/use-deposit-withdraw-config'
|
||||||
import { notify } from '@/lib/notify'
|
import { notify } from '@/lib/notify'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import type { DepositTierItem } from '@/type'
|
import type { DepositTierItem } from '@/type'
|
||||||
@@ -17,21 +19,39 @@ function formatNumber(value: number) {
|
|||||||
|
|
||||||
function MobileTopup() {
|
function MobileTopup() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const depositWithdrawConfigQuery = useDepositWithdrawConfig()
|
||||||
const tierListQuery = useDepositTierList()
|
const tierListQuery = useDepositTierList()
|
||||||
|
const depositWithdrawConfig =
|
||||||
|
depositWithdrawConfigQuery.data ?? DEFAULT_WITHDRAW_CONFIG
|
||||||
|
const isLoading =
|
||||||
|
tierListQuery.isLoading || depositWithdrawConfigQuery.isLoading
|
||||||
|
const isError = tierListQuery.isError || depositWithdrawConfigQuery.isError
|
||||||
const tiers = tierListQuery.data ?? []
|
const tiers = tierListQuery.data ?? []
|
||||||
const createDepositInFlightRef = useRef(false)
|
const createDepositInFlightRef = useRef(false)
|
||||||
const pendingPayWindowRef = useRef<Window | null>(null)
|
const pendingPayWindowRef = useRef<Window | null>(null)
|
||||||
const createDepositMutation = useMutation({
|
const createDepositMutation = useMutation({
|
||||||
mutationFn: ({
|
mutationFn: ({
|
||||||
channelCode,
|
channelCode,
|
||||||
|
depositBank,
|
||||||
|
depositBankAccount,
|
||||||
|
depositFromAddress,
|
||||||
|
paymentType,
|
||||||
tierId,
|
tierId,
|
||||||
}: {
|
}: {
|
||||||
channelCode: string
|
channelCode: string
|
||||||
|
depositBank?: string
|
||||||
|
depositBankAccount?: string
|
||||||
|
depositFromAddress?: string
|
||||||
|
paymentType: string
|
||||||
tierId: string
|
tierId: string
|
||||||
}) =>
|
}) =>
|
||||||
createDeposit({
|
createDeposit({
|
||||||
channel_code: channelCode,
|
channel_code: channelCode,
|
||||||
|
deposit_bank: depositBank,
|
||||||
|
deposit_bank_account: depositBankAccount,
|
||||||
|
deposit_from_address: depositFromAddress,
|
||||||
idempotency_key: String(Date.now()),
|
idempotency_key: String(Date.now()),
|
||||||
|
payment_type: paymentType,
|
||||||
tier_id: tierId,
|
tier_id: tierId,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
@@ -41,13 +61,34 @@ function MobileTopup() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const channelCode = tier.payChannelCode ?? tier.channels[0]?.code ?? ''
|
const channelCode =
|
||||||
|
tier.payChannelCode ??
|
||||||
|
tier.channels[0]?.code ??
|
||||||
|
(tier.currency
|
||||||
|
? depositWithdrawConfig.deposit.defaultChannelByCurrency[tier.currency]
|
||||||
|
: undefined) ??
|
||||||
|
depositWithdrawConfig.defaultDepositChannelCode
|
||||||
|
const selectedPaymentMethod =
|
||||||
|
depositWithdrawConfig.deposit.methods.find(
|
||||||
|
(method) => method.currencyCode === tier.currency,
|
||||||
|
) ?? depositWithdrawConfig.deposit.methods[0]
|
||||||
|
const paymentType = selectedPaymentMethod?.code ?? ''
|
||||||
|
const depositBank = selectedPaymentMethod?.requiresBank
|
||||||
|
? depositWithdrawConfig.deposit.banks.find(
|
||||||
|
(bank) => bank.currencyCode === tier.currency,
|
||||||
|
)?.code
|
||||||
|
: undefined
|
||||||
|
|
||||||
if (!channelCode) {
|
if (!channelCode) {
|
||||||
notify.error(t('commonUi.toast.configError'))
|
notify.error(t('commonUi.toast.configError'))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!paymentType) {
|
||||||
|
notify.error(t('commonUi.toast.configError'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
createDepositInFlightRef.current = true
|
createDepositInFlightRef.current = true
|
||||||
const payWindow = window.open('', '_blank')
|
const payWindow = window.open('', '_blank')
|
||||||
|
|
||||||
@@ -63,6 +104,8 @@ function MobileTopup() {
|
|||||||
try {
|
try {
|
||||||
const result = await createDepositMutation.mutateAsync({
|
const result = await createDepositMutation.mutateAsync({
|
||||||
channelCode,
|
channelCode,
|
||||||
|
depositBank,
|
||||||
|
paymentType,
|
||||||
tierId: tier.id,
|
tierId: tier.id,
|
||||||
})
|
})
|
||||||
const payUrl = result.pay_url.trim()
|
const payUrl = result.pay_url.trim()
|
||||||
@@ -105,12 +148,12 @@ function MobileTopup() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{tierListQuery.isLoading ? (
|
{isLoading ? (
|
||||||
<DataLoadingIndicator
|
<DataLoadingIndicator
|
||||||
label={t('gameDesktop.topup.tier.loading')}
|
label={t('gameDesktop.topup.tier.loading')}
|
||||||
className="h-full min-h-0 rounded-[calc(var(--design-unit)*6)] border border-[rgba(103,227,239,0.18)] bg-[rgba(6,24,35,0.52)]"
|
className="h-full min-h-0 rounded-[calc(var(--design-unit)*6)] border border-[rgba(103,227,239,0.18)] bg-[rgba(6,24,35,0.52)]"
|
||||||
/>
|
/>
|
||||||
) : tierListQuery.isError ? (
|
) : isError ? (
|
||||||
<div className="flex h-full min-h-0 items-center justify-center rounded-[calc(var(--design-unit)*6)] border border-[rgba(185,63,68,0.28)] bg-[rgba(34,13,16,0.42)] px-design-12 text-center text-design-14 text-[#F4A9AE]">
|
<div className="flex h-full min-h-0 items-center justify-center rounded-[calc(var(--design-unit)*6)] border border-[rgba(185,63,68,0.28)] bg-[rgba(34,13,16,0.42)] px-design-12 text-center text-design-14 text-[#F4A9AE]">
|
||||||
{t('gameDesktop.topup.tier.failed')}
|
{t('gameDesktop.topup.tier.failed')}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export function useWithdrawVm() {
|
|||||||
withdraw: {
|
withdraw: {
|
||||||
...baseConfig.withdraw,
|
...baseConfig.withdraw,
|
||||||
banks: baseConfig.withdraw.banks,
|
banks: baseConfig.withdraw.banks,
|
||||||
|
payChannels: baseConfig.withdraw.payChannels,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [withdrawConfigQuery.data])
|
}, [withdrawConfigQuery.data])
|
||||||
@@ -109,17 +110,25 @@ export function useWithdrawVm() {
|
|||||||
const selectedRate = selectedCurrency.withdrawCoinsPerFiatValue || 1
|
const selectedRate = selectedCurrency.withdrawCoinsPerFiatValue || 1
|
||||||
const sortedPayChannels = useMemo(
|
const sortedPayChannels = useMemo(
|
||||||
() =>
|
() =>
|
||||||
[...config.payChannels]
|
[
|
||||||
|
...(config.withdraw.payChannels.length > 0
|
||||||
|
? config.withdraw.payChannels
|
||||||
|
: config.payChannels),
|
||||||
|
]
|
||||||
.filter((channel) => channel.status === 1)
|
.filter((channel) => channel.status === 1)
|
||||||
.sort((left, right) => left.sort - right.sort),
|
.sort((left, right) => left.sort - right.sort),
|
||||||
[config.payChannels],
|
[config.payChannels, config.withdraw.payChannels],
|
||||||
)
|
)
|
||||||
const sortedBanks = useMemo(
|
const sortedBanks = useMemo(
|
||||||
() =>
|
() =>
|
||||||
[...config.withdraw.banks]
|
[...config.withdraw.banks]
|
||||||
.filter((bank) => bank.status === 1)
|
.filter(
|
||||||
|
(bank) =>
|
||||||
|
bank.status === 1 &&
|
||||||
|
(!bank.currencyCode || bank.currencyCode === selectedCurrency.code),
|
||||||
|
)
|
||||||
.sort((left, right) => left.sort - right.sort),
|
.sort((left, right) => left.sort - right.sort),
|
||||||
[config.withdraw.banks],
|
[config.withdraw.banks, selectedCurrency.code],
|
||||||
)
|
)
|
||||||
const availableBalance = Number(currentUser?.coin ?? 0)
|
const availableBalance = Number(currentUser?.coin ?? 0)
|
||||||
const maxWithdrawAmount = Math.max(0, Math.floor(availableBalance))
|
const maxWithdrawAmount = Math.max(0, Math.floor(availableBalance))
|
||||||
|
|||||||
@@ -28,12 +28,14 @@ export interface FinancePayChannelDto {
|
|||||||
code: string
|
code: string
|
||||||
name: string
|
name: string
|
||||||
sort: number
|
sort: number
|
||||||
status: number
|
status?: number
|
||||||
tier_ids: number[]
|
tier_ids?: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FinanceWithdrawBankDto {
|
export interface FinanceWithdrawBankDto {
|
||||||
code?: string
|
code?: string
|
||||||
|
currency_code?: string
|
||||||
|
gateway_code?: string
|
||||||
id?: string
|
id?: string
|
||||||
label?: string
|
label?: string
|
||||||
name?: string
|
name?: string
|
||||||
@@ -41,18 +43,63 @@ export interface FinanceWithdrawBankDto {
|
|||||||
status?: number
|
status?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FinanceDepositMethodDto {
|
||||||
|
code: string
|
||||||
|
currency_code: string
|
||||||
|
gateway_code: string
|
||||||
|
name: string
|
||||||
|
requires_bank: boolean
|
||||||
|
requires_bank_account: boolean
|
||||||
|
requires_depositor_name: boolean
|
||||||
|
requires_from_address: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FinanceDepositFieldsDto {
|
||||||
|
deposit_bank_account_param?: string
|
||||||
|
deposit_bank_param?: string
|
||||||
|
deposit_from_address_param?: string
|
||||||
|
deposit_name_server_side?: boolean
|
||||||
|
payment_type_param?: string
|
||||||
|
require_channel_code?: boolean
|
||||||
|
require_idempotency_key?: boolean
|
||||||
|
require_payment_type?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FinanceDepositConfigDto {
|
||||||
|
banks?: FinanceWithdrawBankDto[]
|
||||||
|
default_channel_by_currency?: Record<string, string>
|
||||||
|
fields?: FinanceDepositFieldsDto
|
||||||
|
methods?: FinanceDepositMethodDto[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FinanceWithdrawFieldsDto {
|
||||||
|
receive_type_bank_only?: boolean
|
||||||
|
require_bank_branch?: boolean
|
||||||
|
require_bank_code?: boolean
|
||||||
|
require_channel_code?: boolean
|
||||||
|
require_receive_account?: boolean
|
||||||
|
require_receiver_email?: boolean
|
||||||
|
require_receiver_mobile?: boolean
|
||||||
|
require_receiver_name?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface FinanceWithdrawConfigDto {
|
export interface FinanceWithdrawConfigDto {
|
||||||
banks: FinanceWithdrawBankDto[]
|
banks: FinanceWithdrawBankDto[]
|
||||||
fee_note: string
|
fee_note: string
|
||||||
|
fields?: FinanceWithdrawFieldsDto
|
||||||
min_bank: string
|
min_bank: string
|
||||||
min_ewallet: string
|
min_ewallet: string
|
||||||
|
pay_channels?: FinancePayChannelDto[]
|
||||||
processing_note: string
|
processing_note: string
|
||||||
rate_hint: string
|
rate_hint: string
|
||||||
rate_mode: 'fixed' | 'live' | (string & {})
|
rate_mode: 'fixed' | 'live' | (string & {})
|
||||||
|
review_threshold_coin?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DepositWithdrawConfigDto {
|
export interface DepositWithdrawConfigDto {
|
||||||
currencies: FinanceCurrencyConfigDto[]
|
currencies: FinanceCurrencyConfigDto[]
|
||||||
|
default_deposit_channel_code?: string
|
||||||
|
deposit?: FinanceDepositConfigDto
|
||||||
pay_channels: FinancePayChannelDto[]
|
pay_channels: FinancePayChannelDto[]
|
||||||
platform_coin_label: string
|
platform_coin_label: string
|
||||||
rates: FinanceRateConfigDto[]
|
rates: FinanceRateConfigDto[]
|
||||||
@@ -104,23 +151,70 @@ export interface FinancePayChannel {
|
|||||||
|
|
||||||
export interface FinanceWithdrawBank {
|
export interface FinanceWithdrawBank {
|
||||||
code: string
|
code: string
|
||||||
|
currencyCode: string | null
|
||||||
|
gatewayCode: string | null
|
||||||
label: string
|
label: string
|
||||||
sort: number
|
sort: number
|
||||||
status: number
|
status: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FinanceDepositMethod {
|
||||||
|
code: string
|
||||||
|
currencyCode: string
|
||||||
|
gatewayCode: string
|
||||||
|
name: string
|
||||||
|
requiresBank: boolean
|
||||||
|
requiresBankAccount: boolean
|
||||||
|
requiresDepositorName: boolean
|
||||||
|
requiresFromAddress: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FinanceDepositFields {
|
||||||
|
depositBankAccountParam: string
|
||||||
|
depositBankParam: string
|
||||||
|
depositFromAddressParam: string
|
||||||
|
depositNameServerSide: boolean
|
||||||
|
paymentTypeParam: string
|
||||||
|
requireChannelCode: boolean
|
||||||
|
requireIdempotencyKey: boolean
|
||||||
|
requirePaymentType: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FinanceDepositConfig {
|
||||||
|
banks: FinanceWithdrawBank[]
|
||||||
|
defaultChannelByCurrency: Record<string, string>
|
||||||
|
fields: FinanceDepositFields
|
||||||
|
methods: FinanceDepositMethod[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FinanceWithdrawFields {
|
||||||
|
receiveTypeBankOnly: boolean
|
||||||
|
requireBankBranch: boolean
|
||||||
|
requireBankCode: boolean
|
||||||
|
requireChannelCode: boolean
|
||||||
|
requireReceiveAccount: boolean
|
||||||
|
requireReceiverEmail: boolean
|
||||||
|
requireReceiverMobile: boolean
|
||||||
|
requireReceiverName: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface FinanceWithdrawConfig {
|
export interface FinanceWithdrawConfig {
|
||||||
banks: FinanceWithdrawBank[]
|
banks: FinanceWithdrawBank[]
|
||||||
feeNote: string
|
feeNote: string
|
||||||
|
fields: FinanceWithdrawFields
|
||||||
minBank: string
|
minBank: string
|
||||||
minEwallet: string
|
minEwallet: string
|
||||||
|
payChannels: FinancePayChannel[]
|
||||||
processingNote: string
|
processingNote: string
|
||||||
rateHint: string
|
rateHint: string
|
||||||
rateMode: FinanceWithdrawConfigDto['rate_mode']
|
rateMode: FinanceWithdrawConfigDto['rate_mode']
|
||||||
|
reviewThresholdCoin: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DepositWithdrawConfig {
|
export interface DepositWithdrawConfig {
|
||||||
currencies: FinanceCurrencyConfig[]
|
currencies: FinanceCurrencyConfig[]
|
||||||
|
defaultDepositChannelCode: string
|
||||||
|
deposit: FinanceDepositConfig
|
||||||
payChannels: FinancePayChannel[]
|
payChannels: FinancePayChannel[]
|
||||||
platformCoinLabel: string
|
platformCoinLabel: string
|
||||||
rates: FinanceRateConfig[]
|
rates: FinanceRateConfig[]
|
||||||
@@ -147,7 +241,11 @@ export interface DepositTierItem {
|
|||||||
|
|
||||||
export interface DepositCreateRequestDto {
|
export interface DepositCreateRequestDto {
|
||||||
channel_code: string
|
channel_code: string
|
||||||
|
deposit_bank?: string
|
||||||
|
deposit_bank_account?: string
|
||||||
|
deposit_from_address?: string
|
||||||
idempotency_key: string
|
idempotency_key: string
|
||||||
|
payment_type: string
|
||||||
tier_id: string
|
tier_id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user