feat: 初始化 Wails + React + Go 桌面应用模板

- Wails v2.16.0 + React 19 + TypeScript + Vite 7
- 插件系统(build tag 控制编译,plugin.json 运行时配置)
- 支付插件(授权、账号、订单、退款、账单)
- 高并发基础设施(任务池、批量处理器、限流器、环形缓冲、管道)
- Windows 系统托盘(Win32 API)
- CodeGraph 索引 + .memory 记忆包
- 默认图标 assets/icon.png
This commit is contained in:
2026-09-17 19:31:17 +08:00
commit 1ea8c9da9c
57 changed files with 6305 additions and 0 deletions

View File

@@ -0,0 +1,369 @@
package service
import (
"context"
"fmt"
"sync"
"sync/atomic"
"time"
)
// BatchResult 批量任务结果
type BatchResult struct {
Success []any
Fail []BatchError
}
// BatchError 批量任务失败记录
type BatchError struct {
Index int
Item any
Err error
}
// BatchWorker 批量处理器,适合文件处理、数据转换等高并发场景
type BatchWorker struct {
mu sync.Mutex
running atomic.Int64
total atomic.Int64
success atomic.Int64
failed atomic.Int64
maxConcurrency int
}
// NewBatchWorker 创建批量处理器
func NewBatchWorker(maxConcurrency int) *BatchWorker {
if maxConcurrency <= 0 {
maxConcurrency = 10
}
return &BatchWorker{
maxConcurrency: maxConcurrency,
}
}
// ProcessConcurrent 并发处理一批数据
func (bw *BatchWorker) ProcessConcurrent(
ctx context.Context,
items []any,
handler func(ctx context.Context, item any) (any, error),
) BatchResult {
bw.total.Add(int64(len(items)))
results := BatchResult{}
sem := make(chan struct{}, bw.maxConcurrency)
var wg sync.WaitGroup
var mu sync.Mutex
for i, item := range items {
select {
case <-ctx.Done():
break
default:
}
wg.Add(1)
sem <- struct{}{}
go func(index int, data any) {
defer wg.Done()
defer func() { <-sem }()
bw.running.Add(1)
defer bw.running.Add(-1)
result, err := handler(ctx, data)
bw.success.Add(1)
mu.Lock()
defer mu.Unlock()
if err != nil {
bw.failed.Add(1)
results.Fail = append(results.Fail, BatchError{
Index: index,
Item: data,
Err: err,
})
} else {
results.Success = append(results.Success, result)
}
}(i, item)
}
wg.Wait()
return results
}
// ProcessSequential 顺序处理(用于需要严格顺序的场景)
func (bw *BatchWorker) ProcessSequential(
ctx context.Context,
items []any,
handler func(ctx context.Context, item any) (any, error),
) BatchResult {
results := BatchResult{}
for i, item := range items {
select {
case <-ctx.Done():
return results
default:
}
result, err := handler(ctx, item)
if err != nil {
results.Fail = append(results.Fail, BatchError{Index: i, Item: item, Err: err})
} else {
results.Success = append(results.Success, result)
}
}
return results
}
// Stats 当前处理统计
func (bw *BatchWorker) Stats() (running, total, success, failed int64) {
return bw.running.Load(), bw.total.Load(), bw.success.Load(), bw.failed.Load()
}
// RingBuffer 无锁环形缓冲区,适合高频消息传递
type RingBuffer struct {
data []any
head int64
tail int64
capacity int
size atomic.Int64
}
// NewRingBuffer 创建环形缓冲区
func NewRingBuffer(capacity int) *RingBuffer {
if capacity <= 0 {
capacity = 1024
}
rb := &RingBuffer{
data: make([]any, capacity),
capacity: capacity,
}
return rb
}
// Enqueue 入队(阻塞直到有空位)
func (rb *RingBuffer) Enqueue(item any) {
for {
tail := atomic.LoadInt64(&rb.tail)
head := atomic.LoadInt64(&rb.head)
if tail-head >= int64(rb.capacity) {
time.Sleep(time.Microsecond)
continue
}
if atomic.CompareAndSwapInt64(&rb.tail, tail, tail+1) {
rb.data[tail%int64(rb.capacity)] = item
rb.size.Add(1)
return
}
}
}
// Dequeue 出队(阻塞直到有数据)
func (rb *RingBuffer) Dequeue() (any, bool) {
for {
head := atomic.LoadInt64(&rb.head)
tail := atomic.LoadInt64(&rb.tail)
if head >= tail {
return nil, false
}
if atomic.CompareAndSwapInt64(&rb.head, head, head+1) {
item := rb.data[head%int64(rb.capacity)]
rb.data[head%int64(rb.capacity)] = nil
rb.size.Add(-1)
return item, true
}
}
}
// Size 当前元素数量
func (rb *RingBuffer) Size() int64 {
return rb.size.Load()
}
// RateLimiter 令牌桶限流器
type RateLimiter struct {
tokens int64
maxTokens int64
refillRate int64 // 每秒补充数
lastRefill time.Time
mu sync.Mutex
}
// NewRateLimiter 创建限流器
func NewRateLimiter(maxTokens, refillRate int64) *RateLimiter {
rl := &RateLimiter{
tokens: maxTokens,
maxTokens: maxTokens,
refillRate: refillRate,
lastRefill: time.Now(),
}
go rl.refillLoop()
return rl
}
func (rl *RateLimiter) refillLoop() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for range ticker.C {
rl.mu.Lock()
elapsed := time.Since(rl.lastRefill).Seconds()
add := int64(elapsed * float64(rl.refillRate))
current := rl.tokens
if current+add > rl.maxTokens {
add = rl.maxTokens - current
}
rl.tokens += add
rl.lastRefill = time.Now()
rl.mu.Unlock()
}
}
// Acquire 获取一个令牌,返回是否成功
func (rl *RateLimiter) Acquire() bool {
rl.mu.Lock()
defer rl.mu.Unlock()
if rl.tokens <= 0 {
return false
}
rl.tokens--
return true
}
// Limit 带限制的包装函数
func (rl *RateLimiter) Limit(fn func() error) error {
for !rl.Acquire() {
time.Sleep(time.Millisecond * 10)
}
return fn()
}
// ConcurrentMap 线程安全的 map,支持并发读写
type ConcurrentMap struct {
mu sync.RWMutex
items map[any]any
}
// NewConcurrentMap 创建线程安全 map
func NewConcurrentMap() *ConcurrentMap {
return &ConcurrentMap{items: make(map[any]any)}
}
func (cm *ConcurrentMap) Set(k, v any) {
cm.mu.Lock()
defer cm.mu.Unlock()
cm.items[k] = v
}
func (cm *ConcurrentMap) Get(k any) (any, bool) {
cm.mu.RLock()
defer cm.mu.RUnlock()
v, ok := cm.items[k]
return v, ok
}
func (cm *ConcurrentMap) Delete(k any) {
cm.mu.Lock()
defer cm.mu.Unlock()
delete(cm.items, k)
}
func (cm *ConcurrentMap) Len() int {
cm.mu.RLock()
defer cm.mu.RUnlock()
return len(cm.items)
}
// Pipeline 函数式管道,链式处理数据流
type Pipeline struct {
stages []func(any) any
input chan any
output chan any
}
// NewPipeline 创建管道
func NewPipeline() *Pipeline {
return &Pipeline{
stages: make([]func(any) any, 0),
input: make(chan any, 256),
output: make(chan any, 256),
}
}
// AddStage 添加处理阶段
func (p *Pipeline) AddStage(fn func(any) any) *Pipeline {
p.stages = append(p.stages, fn)
return p
}
// Run 启动管道(阻塞,直到 input 关闭)
func (p *Pipeline) Run(items []any) []any {
go func() {
for _, item := range items {
p.input <- item
}
close(p.input)
}()
go p.process()
results := make([]any, 0)
for v := range p.output {
results = append(results, v)
}
return results
}
func (p *Pipeline) process() {
defer close(p.output)
for item := range p.input {
for _, stage := range p.stages {
item = stage(item)
}
p.output <- item
}
}
// GoCtx 便捷并发模式
type GoCtx struct {
wg sync.WaitGroup
errs sync.Map
results sync.Map
}
// Go 启动一个并发任务
func (gc *GoCtx) Go(id string, fn func() error) {
gc.wg.Add(1)
go func() {
defer gc.wg.Done()
if err := fn(); err != nil {
gc.errs.Store(id, err)
}
}()
}
// Wait 等待所有任务完成,返回错误列表
func (gc *GoCtx) Wait() []error {
gc.wg.Wait()
var errs []error
gc.errs.Range(func(_, v any) bool {
errs = append(errs, v.(error))
return true
})
return errs
}
// WaitWithTimeout 带超时的等待
func (gc *GoCtx) WaitWithTimeout(timeout time.Duration) []error {
done := make(chan struct{})
go func() {
gc.wg.Wait()
close(done)
}()
select {
case <-done:
var errs []error
gc.errs.Range(func(_, v any) bool {
errs = append(errs, v.(error))
return true
})
return errs
case <-time.After(timeout):
return []error{fmt.Errorf("并发任务超时: %v", timeout)}
}
}