From 26792be9dbcbd3b92b10874ce5d32d45fed0a167 Mon Sep 17 00:00:00 2001 From: NY Date: Mon, 17 Feb 2025 11:51:17 +0800 Subject: [PATCH] first --- .drone.yml | 1 + api/test.go | 6 ++ cache/api/currency.go | 16 ++++ cache/caches.go | 1 + main.go | 3 +- model/const.go | 5 ++ model/exchange.go | 21 +++++ model/userConfig.go | 65 ++++++++++++++ pkg/cron/cron.go | 6 +- pkg/exchange/exchangeRate.go | 160 +++++++++++++++++++++++++++++++++++ pkg/httpclient/httpclient.go | 28 ++++++ pkg/utils/time.go | 11 +++ router/routerFunc.go | 27 ++++++ 13 files changed, 348 insertions(+), 2 deletions(-) create mode 100644 cache/api/currency.go create mode 100644 model/const.go create mode 100644 model/exchange.go create mode 100644 model/userConfig.go create mode 100644 pkg/exchange/exchangeRate.go create mode 100644 pkg/httpclient/httpclient.go diff --git a/.drone.yml b/.drone.yml index dff3b94..d73304d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -62,6 +62,7 @@ steps: - echo "Building Docker image..." - cd /workspace/prdUploader - docker build -t prduploader:latest . + - docker image prune -f - echo "Running Docker container..." - docker-compose up -d - rm -rf myapp diff --git a/api/test.go b/api/test.go index e7c69f6..f8cea7e 100644 --- a/api/test.go +++ b/api/test.go @@ -3,6 +3,7 @@ package api import ( "epur-pay/model" "epur-pay/pkg/dapi" + "epur-pay/pkg/exchange" "epur-pay/pkg/logger" "github.com/shopspring/decimal" ) @@ -64,6 +65,11 @@ func Test1(a *dapi.ApiBase, data *Test1Params) error { return a.ReturnSuccessCustomResponse(Response) } +func Test2(a *dapi.ApiBase) error { + exchange.GetRate() + return a.ReturnSuccessCustomResponse(a.NewSuccessResponseCommon()) +} + // //type Test2Params struct { // model.Http diff --git a/cache/api/currency.go b/cache/api/currency.go new file mode 100644 index 0000000..47b12c1 --- /dev/null +++ b/cache/api/currency.go @@ -0,0 +1,16 @@ +package cacheApi + +import ( + "epur-pay/model" + "sync" +) + +type Currency struct { + List map[string]model.ExchangeRate + Map *sync.Map + duplicate *Currency +} + +func (p *Currency) Refresh() *Currency { + return p +} diff --git a/cache/caches.go b/cache/caches.go index d1d7e1b..505f694 100644 --- a/cache/caches.go +++ b/cache/caches.go @@ -32,6 +32,7 @@ type GlobalServerCache struct { RolePermission *cacheApi.RolePermission // 角色菜单权限 User *cacheApi.User BlackList *cacheApi.BlackList // 黑名单 + Currency *cacheApi.Currency } } diff --git a/main.go b/main.go index 5e7f1bc..344b055 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "epur-pay/cache" "epur-pay/pkg/async" "epur-pay/pkg/config" + "epur-pay/pkg/cron" "epur-pay/pkg/dapi" "epur-pay/pkg/mq" "epur-pay/pkg/server" @@ -21,8 +22,8 @@ func main() { app.LoadDB().LoadRedis().LoadCron().LoadMq().LoadCustomApp(func() { cache.New() dapi.New() + cron.New() //fileserver.New() - go func() { for !cache.Global.ProjectInitStatus { time.Sleep(100 * time.Millisecond) diff --git a/model/const.go b/model/const.go new file mode 100644 index 0000000..c3b2579 --- /dev/null +++ b/model/const.go @@ -0,0 +1,5 @@ +package model + +const ( + Normal = "1" +) diff --git a/model/exchange.go b/model/exchange.go new file mode 100644 index 0000000..0a3d0b4 --- /dev/null +++ b/model/exchange.go @@ -0,0 +1,21 @@ +package model + +import "github.com/shopspring/decimal" + +type ExchangeRate struct { + RateId int64 `gorm:"primary_key;column:rate_id" json:"rateId"` // 主键 ID + FromName string `gorm:"column:from_name" json:"fromName"` // 来源币种名称,例如人民币 + FromCurrency string `gorm:"column:from_currency" json:"fromCurrency"` // 来源币种代码,例如 CNY + FromAmt decimal.Decimal `gorm:"column:from_amt" json:"fromAmt"` // 来源币种的基准金额 + Unit string `gorm:"column:unit" json:"unit"` // 单位,例如 元 + ToName string `gorm:"column:to_name" json:"toName"` // 目标币种名称 + ToCurrency string `gorm:"column:to_currency" json:"toCurrency"` // 目标币种代码 + ToAmt decimal.Decimal `gorm:"column:to_amt" json:"toAmt"` // 目标币种的金额 + Date int64 `gorm:"column:date" json:"date"` // 汇率对应的日期(时间戳) + Status string `gorm:"column:status" json:"status"` // 状态值,例如 1 表示正常,2 表示禁用 + CreateTime int64 `gorm:"column:create_time" json:"create_time"` // 创建时间戳(秒) +} + +func (ExchangeRate) TableName() string { + return "exchange_rate" +} diff --git a/model/userConfig.go b/model/userConfig.go new file mode 100644 index 0000000..db11035 --- /dev/null +++ b/model/userConfig.go @@ -0,0 +1,65 @@ +package model + +import ( + "database/sql/driver" + "encoding/json" + "github.com/shopspring/decimal" +) + +type BannedWords struct { + Id int64 `gorm:"primary_key;column:id" json:"id"` // 违禁词ID` + Word string `gorm:"column:word" json:"word"` // 违禁词内容` + Category string `gorm:"column:category" json:"category"` // 违禁词分类` + Description string `gorm:"column:description" json:"description"` // 违禁词描述(可选)` + UserId int64 `gorm:"column:user_id" json:"userId"` // 用户ID,NULL 表示系统默认违禁词` + CreateTime int64 `gorm:"column:create_time" json:"createTime"` // 创建时间戳` +} + +func (BannedWords) TableName() string { + return "banned_words" +} + +type UserConfig struct { + Id int64 `gorm:"primary_key;column:id" json:"id"` // 用户配置ID` + Uid int64 `gorm:"column:uid" json:"uid"` // 用户ID,关联用户表` + ExchangeRates UserConfigChild `gorm:"column:exchange_rates" json:"exchangeRates"` // 汇率配置,存储不同货币的汇率` + PriceMultiplier decimal.Decimal `gorm:"column:price_multiplier;default:4.00" json:"priceMultiplier"` // 上品价格上浮倍率` + MinStock int64 `gorm:"column:min_stock;default:30" json:"minStock"` // 最小库存,低于该库存不允许上品` + FixedStock int64 `gorm:"column:fixed_stock" json:"fixedStock"` // 固定库存值,NULL 表示未设置` + EnableDeduplication string `gorm:"column:enable_deduplication;default:'0'" json:"enableDeduplication"` // 是否启用去重检查` + DefaultSize UserConfigChild `gorm:"column:default_size" json:"defaultSize"` // 默认尺寸配置,存储长、宽、高、重量等` + EnableBlacklistFilter string `gorm:"column:enable_blacklist_filter;default:'0'" json:"enableBlacklistFilter"` // 是否启用违禁词过滤` + ImportSource string `gorm:"column:import_source;default:'system'" json:"importSource"` // 导入方式` + ExtraConfig ExtraConfig `gorm:"column:extra_config" json:"extraConfig"` // 额外的扩展配置` + CreateTime int64 `gorm:"column:create_time" json:"createTime"` // 创建时间戳` + UpdateTime int64 `gorm:"column:update_time" json:"updateTime"` // 更新时间戳` +} + +func (UserConfig) TableName() string { + return "user_config" +} + +type ExtraConfig struct { +} + +func (j *ExtraConfig) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &j) +} +func (j ExtraConfig) Value() (driver.Value, error) { + return json.Marshal(j) +} + +type UserConfigChild struct { + Data []struct { + Code string `json:"code"` + Num decimal.Decimal `json:"num"` + Unit string `json:"unit"` + } `json:"data"` +} + +func (j *UserConfigChild) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), &j) +} +func (j UserConfigChild) Value() (driver.Value, error) { + return json.Marshal(j) +} diff --git a/pkg/cron/cron.go b/pkg/cron/cron.go index 1206def..229d580 100644 --- a/pkg/cron/cron.go +++ b/pkg/cron/cron.go @@ -1,6 +1,7 @@ package cron import ( + "epur-pay/pkg/exchange" "epur-pay/pkg/logger" "epur-pay/pkg/mq" "epur-pay/pkg/rds" @@ -18,13 +19,16 @@ var ( func New() { Cron = cron.New() Cron.Start() - { /* 每个月将任务表转移到历史数据表 */ utils.Error(Cron.AddFunc("* * * * 7", MqStoreToHistory)) } + // 每天凌晨 12 点更新汇率 + { + utils.Error(Cron.AddFunc("0 0 * * *", exchange.GetRate)) + } } func MqStoreToHistory() { diff --git a/pkg/exchange/exchangeRate.go b/pkg/exchange/exchangeRate.go new file mode 100644 index 0000000..7c757ef --- /dev/null +++ b/pkg/exchange/exchangeRate.go @@ -0,0 +1,160 @@ +package exchange + +import ( + "encoding/json" + "epur-pay/model" + "epur-pay/pkg/httpclient" + "epur-pay/pkg/logger" + "epur-pay/pkg/rds" + "epur-pay/pkg/utils" + "fmt" + "github.com/shopspring/decimal" + "gorm.io/gorm" + "time" +) + +type CurBody struct { + Name string `json:"name"` // 名称 + Rate string `json:"rate"` // 汇率 + Updatetime string `json:"updatetime"` // 时间2022-11-01 14:09:24 +} +type ResultMsg struct { + Currency string `json:"currency"` // 币种CNY + Name string `json:"name"` // 币种名称人民币 + List map[string]CurBody `json:"list"` // USD +} + +type CurResponse struct { + Status int `json:"status"` + Msg string `json:"msg"` + Result ResultMsg `json:"result"` +} + +type WanWeiYuanCurrency struct { + HuiIn string `json:"hui_in,omitempty"` // 现汇买入价 + HuiOut string `json:"hui_out,omitempty"` // 现汇卖出价 + ChaoOut string `json:"chao_out,omitempty"` // 现钞卖出价 + ChaoIn string `json:"chao_in,omitempty"` // 现钞买入价 + Name string `json:"name"` // 货币名称 + Zhesuan string `json:"zhesuan,omitempty"` // 中行折算价 + Code string `json:"code"` // 货币简码 + Day string `json:"day"` // 发布日期 + Time string `json:"time"` // 发布时间 +} +type WanWeiYuanCurrencyResponse struct { + ShowapiResError string `json:"showapi_res_error"` + ShowapiResId string `json:"showapi_res_id"` + ShowapiResCode int64 `json:"showapi_res_code"` + ShowapiFeeNum int64 `json:"showapi_fee_num"` + ShowapiResBody struct { + RetCode int64 `json:"ret_code"` + ListSize int64 `json:"listSize"` + List []WanWeiYuanCurrency `json:"list"` + } `json:"showapi_res_body"` +} + +type ExchangeResponse struct { + ShowapiResError string `json:"showapi_res_error"` + ShowapiResId string `json:"showapi_res_id"` + ShowapiResCode int `json:"showapi_res_code"` + ShowapiFeeNum int `json:"showapi_fee_num"` + ShowapiResBody ExchangeResBody `json:"showapi_res_body"` +} +type ExchangeResBody struct { + List []ExchangeList `json:"list"` + ListSize int `json:"listSize"` + RetCode int `json:"ret_code"` +} +type ExchangeList struct { + Code string `json:"code"` + HuiOut string `json:"hui_out"` + Zhesuan string `json:"zhesuan"` + ChaoOut string `json:"chao_out"` + Time string `json:"time"` + Name string `json:"name"` + HuiIn string `json:"hui_in"` + ChaoIn string `json:"chao_in"` + Day string `json:"day"` +} + +func GetRate() { + btime := time.Now().UnixNano() + logger.AccessLogger.Info("ExchangeRate...", btime) + if err := rds.DB.Transaction(func(ts *gorm.DB) error { + resp := ExchangeResponse{} + // TWD新台币 MYR马来西亚林吉特 SGD新加坡元 PHP菲律宾比索 IDR印度尼西亚盾 THB泰铢 BRL巴西雷亚尔 GBP英镑 MXN墨西哥比索 VND越南盾 + //Currency := []string{"TWD", "MYR", "SGD", "PHP", "IDR", "THB", "BRL", "GBP", "MXN", "VND"} + str, _ := AliyunExchangeApi() + err := json.Unmarshal([]byte(str), &resp) + if err != nil { + logger.AccessLogger.Error("ERROR:", err.Error()) + return err + } + if resp.ShowapiResCode != 0 { + logger.AccessLogger.Error("ERROR:", resp.ShowapiResError) + return err + } + for idx, tmp := range resp.ShowapiResBody.List { + logger.AccessLogger.Debug(idx, "处理币种:", tmp.Code, "汇率:", tmp.HuiIn) + if len(tmp.HuiIn) < 1 { + tmp.HuiIn = tmp.ChaoIn + } + rate := model.ExchangeRate{} + rate.RateId = 0 + rate.FromName = "人民币" + rate.FromCurrency = "CNY" + rate.FromAmt = decimal.NewFromFloat(1.00) + rate.Unit = "元" + rate.ToCurrency = tmp.Code + rate.ToName = tmp.Name + rate.Date = utils.Str2FormatDateTime(tmp.Day, "2006-01-02", nil).Unix() + rate.Status = model.Normal + rate.CreateTime = utils.Time2StampSecond() + rate.ToAmt, _ = decimal.NewFromString(tmp.HuiIn) + rate.ToAmt = decimal.NewFromInt(100).DivRound(rate.ToAmt, 6) + res := ts.Table(model.ExchangeRate{}.TableName()). + Where(model.ExchangeRate{FromCurrency: rate.FromCurrency, ToCurrency: rate.ToCurrency, Date: rate.Date, Status: model.Normal}). + FirstOrCreate(&rate) + if res.Error != nil { + logger.AccessLogger.Error("ERROR:", res.Error) + return res.Error + } + //// 刷新缓存 + //cache.Global.Caches.Currency.Refresh() + } + return nil + }); err != nil { + logger.AccessLogger.Error("ERROR:", err.Error()) + } + etime := time.Now().UnixNano() + logger.AccessLogger.Info("END...", etime-btime) + //logger.AccessLogger.Info("USD:", cache.Global.Caches.Currency.ToCNY("USD", decimal.NewFromInt(100))) +} + +// curl -i -k --get --include +// 'https://ali-waihui.showapi.com/waihui-transform?fromCode=GBP&money=100&toCode=EUR' +// -H 'Authorization:APPCODE 你自己的AppCode' +func HttpGet(toCode string) (str string, code int) { + header := map[string]string{} + header["Authorization"] = "APPCODE 63e68d5081144e0185139b496a999000" + url := fmt.Sprintf("https://ali-waihui.showapi.com/waihui-transform?fromCode=%s&money=%s&toCode=%s", + "CNY", "100", toCode) + str, code = httpclient.DoHttp(url, "GET", "", header) + logger.AccessLogger.Info("返回信息:", str, "http代码", code) + return str, code +} + +// https://jisuhuilv.market.alicloudapi.com/exchange/single +// curl -i -k -X GET'https://jisuhuilv.market.alicloudapi.com/exchange/single?currency=CNY' +// https://market.aliyun.com/products/57000002/cmapi010841.html?spm=5176.2020520132.101.3.2b9b72189hTyJH#sku=yuncode484100008 +// -H 'Authorization:APPCODE 你自己的AppCode' +func AliyunExchangeApi() (str string, code int) { + header := map[string]string{} + header["Authorization"] = "APPCODE 63e68d5081144e0185139b496a999000" + //url := "https://jisuhuilv.market.alicloudapi.com/exchange/single?currency=CNY" + url := "https://ali-waihui.showapi.com/waihui-list?code=code" + str, code = httpclient.DoHttp(url, "GET", "", header) + //logger.AccessLogger.Info("返回信息:", str, "http代码", code) + logger.AccessLogger.Info("http代码:", code, "\n", str) + return str, code +} diff --git a/pkg/httpclient/httpclient.go b/pkg/httpclient/httpclient.go new file mode 100644 index 0000000..71a313a --- /dev/null +++ b/pkg/httpclient/httpclient.go @@ -0,0 +1,28 @@ +package httpclient + +import ( + "bytes" + "io/ioutil" + "net/http" +) + +func DoHttp(url, method, body string, headers map[string]string) (string, int) { + req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(body))) + if err != nil { + return "", 0 + } + for key, value := range headers { + req.Header.Set(key, value) + } + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return "", 0 + } + defer resp.Body.Close() + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", resp.StatusCode + } + return string(respBody), resp.StatusCode +} diff --git a/pkg/utils/time.go b/pkg/utils/time.go index 5942ce0..58f996b 100644 --- a/pkg/utils/time.go +++ b/pkg/utils/time.go @@ -7,3 +7,14 @@ func Time2StampSecond() int64 { millisecond := t.UnixNano() / 1e6 return millisecond / 1000 } + +func Str2FormatDateTime(dateStr, format string, location *time.Location) time.Time { + if location == nil { + location = time.Local + } + parsedTime, err := time.ParseInLocation(format, dateStr, location) + if err != nil { + return time.Time{} + } + return parsedTime +} diff --git a/router/routerFunc.go b/router/routerFunc.go index dca9d49..4cfd818 100644 --- a/router/routerFunc.go +++ b/router/routerFunc.go @@ -35,6 +35,14 @@ func NewRouterFunc() { F: api.Test1, Params: &dapi.InParams{}, }, + { + Name: "测试2", + Method: "POST", + Path: "/test2", + Leaf: true, + F: api.Test2, + Params: &dapi.InParams{}, + }, }, }, ) @@ -69,6 +77,25 @@ func NewRouterFunc() { }, ) + RouterAppend( + &model.RouterMethod{ + Name: "配置", + Path: "/config", + Child: []*model.RouterMethod{ + { + Name: "获取配置", + Method: "POST", + Path: "/get", + Leaf: true, + F: api.GetPermissionList, + Params: &dapi.InParams{ + IsSaveLog: true, + }, + }, + }, + }, + ) + RouterAppend( &model.RouterMethod{ Name: "登陆",