feat: 优化发货交付页面与凭据生成,支持一键复制结果图片及官方商品图片本地缓存
- 交付界面重构:隐藏内部订单号、流水号与发货渠道,展示聚焦于买家游戏UID、角色名与交付时间 - 发货结果凭据生成:采用 Canvas 生成高清发货存证图,优化存证说明文案与自适应排版 - 一键复制图片:支持直接将结果凭证图片写入系统剪贴板,方便客服在聊天窗口快捷粘贴 - 官方图片本地化缓存:全量下载乐玩商品官方主图并建立本地持久化路由,规避外部源站跨域及破损问题 - 修复乐玩渠道角色UID提取与履约状态幂等回填逻辑
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"affiliate_dash/internal/config"
|
||||
"affiliate_dash/internal/pkg/lewan"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := config.Load()
|
||||
client := lewan.NewClient(lewan.Config{
|
||||
BaseURL: cfg.LewanBaseURL,
|
||||
AccessKey: cfg.LewanAccessKey,
|
||||
SecretKey: cfg.LewanSecretKey,
|
||||
ApplicationID: cfg.LewanApplicationID,
|
||||
}, nil)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
list, err := client.ListProducts(ctx, 1, 100, "")
|
||||
if err != nil {
|
||||
fmt.Printf("ListProducts error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d products (total: %d)\n", len(list.Items), list.Total)
|
||||
uploadDir := cfg.UploadDir
|
||||
if uploadDir == "" {
|
||||
uploadDir = "data/uploads"
|
||||
}
|
||||
cacheDir := filepath.Join(uploadDir, "products")
|
||||
_ = os.MkdirAll(cacheDir, 0755)
|
||||
|
||||
httpClient := &http.Client{Timeout: 15 * time.Second}
|
||||
|
||||
for i, item := range list.Items {
|
||||
fmt.Printf("[%d/%d] ID: %s, Name: %s, Thumb: %s\n", i+1, len(list.Items), item.ID, item.Name, item.ThumbnailUrl)
|
||||
if item.ThumbnailUrl == "" {
|
||||
continue
|
||||
}
|
||||
// 存储为 item.ID + 原扩展名
|
||||
ext := filepath.Ext(item.ThumbnailUrl)
|
||||
if ext == "" || len(ext) > 5 {
|
||||
ext = ".webp"
|
||||
}
|
||||
destPath := filepath.Join(cacheDir, item.ID+ext)
|
||||
// 同时也用 url hash 存一个软链或备份,以便 proxy 按 url 精准命中
|
||||
urlHash := sha256.Sum256([]byte(item.ThumbnailUrl))
|
||||
hashPath := filepath.Join(cacheDir, hex.EncodeToString(urlHash[:])+ext)
|
||||
|
||||
if _, err := os.Stat(destPath); err == nil {
|
||||
fmt.Printf(" -> 已存在缓存: %s\n", destPath)
|
||||
continue
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, item.ThumbnailUrl, nil)
|
||||
if err != nil {
|
||||
fmt.Printf(" -> 构建请求失败: %v\n", err)
|
||||
continue
|
||||
}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
fmt.Printf(" -> 下载失败: %v\n", err)
|
||||
continue
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Printf(" -> 状态码错误: %d\n", resp.StatusCode)
|
||||
resp.Body.Close()
|
||||
continue
|
||||
}
|
||||
data, err := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
fmt.Printf(" -> 读取失败: %v\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
_ = os.WriteFile(destPath, data, 0644)
|
||||
_ = os.WriteFile(hashPath, data, 0644)
|
||||
fmt.Printf(" -> 成功缓存: %s (大小: %d 字节)\n", destPath, len(data))
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,7 @@ func main() {
|
||||
lewanProvider := service.NewLewanProvider(lewanClient)
|
||||
deliverySvc.RegisterProvider(lewanProvider)
|
||||
fulfillmentSvc.SetUpstreamCancel(deliverySvc.CancelUpstream)
|
||||
fulfillmentSvc.SetUpstreamSync(deliverySvc.SyncDeliveringOrder)
|
||||
|
||||
openlog.Init(cfg.OpenAPIDebug)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user