From 798f56654276d05db9a8dfe918456a3b93e1a140 Mon Sep 17 00:00:00 2001 From: 474420502 <474420502@qq.com> Date: Tue, 16 Apr 2024 18:00:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=AF=B9=E5=BA=94=E4=BA=A4?= =?UTF-8?q?=E6=98=93=E7=9A=84=E4=BB=A3=E7=A0=81=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- model/killara_customer_balance_logic.go | 21 ++ model/killara_customer_hold_logic.go | 46 ++++ model/killara_customer_logic.go | 134 ++++++++--- model/killara_customer_token_logic.go | 11 +- model/killara_customer_transaction_logic.go | 129 +++++++++++ model/killara_stock_logic.go | 82 +++++++ proto/stock.go | 94 ++++++++ proto/stock_test.go | 11 + server/app/internal/handlers/actions/auth.go | 8 +- .../internal/handlers/actions/securities.go | 211 +++++++++++++++++- test/gorm_test.go | 12 +- translator/tag_test.go | 19 ++ translator/translator.go | 4 + translator/translator_test.go | 7 + utils/constants/constants.go | 6 + utils/convert/model2map.go | 27 +++ utils/marketdate/marketdate.go | 76 +++++++ 18 files changed, 858 insertions(+), 43 deletions(-) create mode 100644 proto/stock.go create mode 100644 proto/stock_test.go create mode 100644 translator/tag_test.go create mode 100644 utils/constants/constants.go create mode 100644 utils/convert/model2map.go create mode 100644 utils/marketdate/marketdate.go diff --git a/.gitignore b/.gitignore index f7bcef7..34646e9 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,5 @@ go.work __debug_bin* .vscode -*.php \ No newline at end of file +*.php +*.txt \ No newline at end of file diff --git a/model/killara_customer_balance_logic.go b/model/killara_customer_balance_logic.go index 40d7e4b..dba000f 100644 --- a/model/killara_customer_balance_logic.go +++ b/model/killara_customer_balance_logic.go @@ -9,3 +9,24 @@ type KillaraCustomerBalanceModel struct { db *gorm.DB TableName string // 表名 } + +func (m *KillaraCustomerBalanceModel) SumFreeze(tx *gorm.DB, customerID uint64, market string, currencyID uint64) (float64, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + + var total float64 + err := db.Model(&KillaraCustomerBalance{}). + Select("IFNULL(SUM(total), 0.000) AS total"). + Where("customer_id = ? AND market = ? AND freeze = 1 AND currency_id = ?", customerID, market, currencyID). + Scan(&total).Error + + if err != nil { + return 0, err + } + + return total, nil +} diff --git a/model/killara_customer_hold_logic.go b/model/killara_customer_hold_logic.go index c0f68df..345ec56 100644 --- a/model/killara_customer_hold_logic.go +++ b/model/killara_customer_hold_logic.go @@ -9,3 +9,49 @@ type KillaraCustomerHoldModel struct { db *gorm.DB TableName string // 表名 } + +func (m *KillaraCustomerHoldModel) GetHoldsForFrontEnd(tx *gorm.DB, data map[string]interface{}) ([]*KillaraCustomerHold, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + + var holds []*KillaraCustomerHold + query := db.Model(&KillaraCustomerHold{}) + + if customerID, ok := data["customer_id"].(uint64); ok && customerID > 0 { + query = query.Where("customer_id = ?", customerID) + } + + if market, ok := data["market"].(string); ok && market != "" { + query = query.Where("market = ?", market) + } + + if holdType, ok := data["type"].(uint64); ok && holdType > 0 { + query = query.Where("type = ?", holdType) + } + + if status, ok := data["status"].(uint64); ok && status > 0 { + query = query.Where("status = ?", status) + } + + query = query.Order("customer_hold_id DESC") + + if start, ok := data["start"].(int); ok && start > 0 { + query = query.Offset(start) + } + + if limit, ok := data["limit"].(int); ok && limit > 0 { + query = query.Limit(limit) + } else { + query = query.Limit(10) + } + + err := query.Find(&holds).Error + if err == gorm.ErrRecordNotFound { + return nil, nil + } + return holds, err +} diff --git a/model/killara_customer_logic.go b/model/killara_customer_logic.go index 63366d4..b679b5f 100644 --- a/model/killara_customer_logic.go +++ b/model/killara_customer_logic.go @@ -12,90 +12,163 @@ type KillaraCustomerModel struct { TableName string // 表名 } -func (m *KillaraCustomerModel) InsertCustomer(customer *KillaraCustomer) (uint64, error) { +func (m *KillaraCustomerModel) InsertCustomer(tx *gorm.DB, customer *KillaraCustomer) (uint64, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } - if err := m.db.Model(&KillaraCustomer{}).Create(customer).Error; err != nil { + if err := db.Model(&KillaraCustomer{}).Create(customer).Error; err != nil { return 0, err } return *customer.CustomerId, nil } -func (m *KillaraCustomerModel) UpdateCustomer(customer *KillaraCustomer) error { - return m.db.Model(&KillaraCustomer{}).Where("customer_id = ?", customer.CustomerId).Updates(customer).Error +func (m *KillaraCustomerModel) UpdateCustomer(tx *gorm.DB, customer *KillaraCustomer) error { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + + return db.Model(&KillaraCustomer{}).Where("customer_id = ?", customer.CustomerId).Updates(customer).Error } -func (m *KillaraCustomerModel) DeleteCustomer(customer *KillaraCustomer) error { - return m.db.Where("customer_id = ?", customer.CustomerId).Delete(&KillaraCustomer{}).Error +func (m *KillaraCustomerModel) DeleteCustomer(tx *gorm.DB, customer *KillaraCustomer) error { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + + return db.Where("customer_id = ?", customer.CustomerId).Delete(&KillaraCustomer{}).Error } -func (m *KillaraCustomerModel) GetCustomer(customerID uint64) (*KillaraCustomer, error) { +func (m *KillaraCustomerModel) GetCustomer(tx *gorm.DB, customerID uint64) (*KillaraCustomer, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + var customer KillaraCustomer - err := m.db.Where("customer_id = ? AND status = 1", customerID).First(&customer).Error + err := db.Where("customer_id = ? AND status = 1", customerID).First(&customer).Error if err == gorm.ErrRecordNotFound { return nil, nil } return &customer, err } -func (m *KillaraCustomerModel) CheckTelephoneExists(telephone string) (bool, error) { - // var count int64 - // err := m.db.Model(&KillaraCustomer{}).Where("telephone = ?", telephone).Count(&count).Error - err := m.db.Model(&KillaraCustomer{}).Where("telephone = ?", telephone).First(nil).Error +func (m *KillaraCustomerModel) CheckTelephoneExists(tx *gorm.DB, telephone string) (bool, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + + err := db.Model(&KillaraCustomer{}).Where("telephone = ?", telephone).First(nil).Error if err == gorm.ErrRecordNotFound { return false, nil } return err != nil, err } -func (m *KillaraCustomerModel) CheckEmailExists(email string) (bool, error) { - // var count int64 - err := m.db.Model(&KillaraCustomer{}).Where("email = ?", email).First(nil).Error +func (m *KillaraCustomerModel) CheckEmailExists(tx *gorm.DB, email string) (bool, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + + err := db.Model(&KillaraCustomer{}).Where("email = ?", email).First(nil).Error if err == gorm.ErrRecordNotFound { return false, nil } return err != nil, err } -func (m *KillaraCustomerModel) GetCustomerByCode(code string) (*KillaraCustomer, error) { +func (m *KillaraCustomerModel) GetCustomerByCode(tx *gorm.DB, code string) (*KillaraCustomer, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + var customer KillaraCustomer - err := m.db.Where("code = ? AND status = 1", code).First(&customer).Error + err := db.Where("code = ? AND status = 1", code).First(&customer).Error if err == gorm.ErrRecordNotFound { return nil, nil } return &customer, err } -func (m *KillaraCustomerModel) GetCustomerByTelephone(telephone string) (*KillaraCustomer, error) { +func (m *KillaraCustomerModel) GetCustomerByTelephone(tx *gorm.DB, telephone string) (*KillaraCustomer, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + var customer KillaraCustomer - err := m.db.Where("telephone = ? AND status = 1", telephone).Order("customer_id").First(&customer).Error + err := db.Where("telephone = ? AND status = 1", telephone).Order("customer_id").First(&customer).Error if err == gorm.ErrRecordNotFound { return nil, nil } return &customer, err } -func (m *KillaraCustomerModel) GetCustomerByTelephoneForBackEnd(telephone string) (*KillaraCustomer, error) { +func (m *KillaraCustomerModel) GetCustomerByTelephoneForBackEnd(tx *gorm.DB, telephone string) (*KillaraCustomer, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + var customer KillaraCustomer - err := m.db.Where("telephone = ?", telephone).First(&customer).Error + err := db.Where("telephone = ?", telephone).First(&customer).Error if err == gorm.ErrRecordNotFound { return nil, nil } return &customer, err } -func (m *KillaraCustomerModel) GetCustomerByEmailForBackEnd(email string) (*KillaraCustomer, error) { +func (m *KillaraCustomerModel) GetCustomerByEmailForBackEnd(tx *gorm.DB, email string) (*KillaraCustomer, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + var customer KillaraCustomer - err := m.db.Where("email = ?", email).First(&customer).Error + err := db.Where("email = ?", email).First(&customer).Error if err == gorm.ErrRecordNotFound { return nil, nil } return &customer, err } -func (m *KillaraCustomerModel) GetCustomerForBackEnd(customerID uint64) (*KillaraCustomer, error) { +func (m *KillaraCustomerModel) GetCustomerForBackEnd(tx *gorm.DB, customerID uint64) (*KillaraCustomer, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + var customer KillaraCustomer - err := m.db.Where("customer_id = ?", customerID).First(&customer).Error + err := db.Where("customer_id = ?", customerID).First(&customer).Error if err == gorm.ErrRecordNotFound { return nil, nil } @@ -327,7 +400,7 @@ func (m *KillaraCustomerModel) GetFullParents(parentID uint64) ([]*KillaraCustom var parents []*KillaraCustomer // 获取当前父级客户 - parent, err := m.GetCustomer(parentID) + parent, err := m.GetCustomer(nil, parentID) if err != nil { return nil, err } @@ -349,7 +422,7 @@ func (m *KillaraCustomerModel) GetFullParents(parentID uint64) ([]*KillaraCustom // 获取客户自身及所有上级 func (m *KillaraCustomerModel) GetFullParentsFromSelf(customerID uint64) ([]*KillaraCustomer, error) { - customer, err := m.GetCustomer(customerID) + customer, err := m.GetCustomer(nil, customerID) if err != nil { return nil, err } @@ -394,7 +467,7 @@ func (m *KillaraCustomerModel) GetFullChildren(parentID uint64) ([]*KillaraCusto // 获取客户自身及所有下级 func (m *KillaraCustomerModel) GetFullChildrenWithSelf(customerID uint64) ([]*KillaraCustomer, error) { - customer, err := m.GetCustomer(customerID) + customer, err := m.GetCustomer(nil, customerID) if err != nil { return nil, err } @@ -411,3 +484,8 @@ func (m *KillaraCustomerModel) GetFullChildrenWithSelf(customerID uint64) ([]*Ki return children, nil } + +// 用于客户的全局事物 +func (m *KillaraCustomerModel) Transaction(do func(tx *gorm.DB) error) error { + return m.db.Transaction(do) +} diff --git a/model/killara_customer_token_logic.go b/model/killara_customer_token_logic.go index d417d19..7c20eca 100644 --- a/model/killara_customer_token_logic.go +++ b/model/killara_customer_token_logic.go @@ -86,9 +86,16 @@ func (m *KillaraCustomerTokenModel) DeleteToken(token string) error { return err } -func (m *KillaraCustomerTokenModel) GetToken(token string) (*KillaraCustomerToken, error) { +func (m *KillaraCustomerTokenModel) GetToken(tx *gorm.DB, token string) (*KillaraCustomerToken, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + var tokenData KillaraCustomerToken - err := m.db.Where("token = ?", token).First(&tokenData).Error + err := db.Model(&tokenData).Where("token = ?", token).First(&tokenData).Error if err == gorm.ErrRecordNotFound { return nil, nil } diff --git a/model/killara_customer_transaction_logic.go b/model/killara_customer_transaction_logic.go index 6a34acf..e25516a 100644 --- a/model/killara_customer_transaction_logic.go +++ b/model/killara_customer_transaction_logic.go @@ -1,6 +1,10 @@ package model import ( + "errors" + "fmt" + "strconv" + "gorm.io/gorm" ) @@ -9,3 +13,128 @@ type KillaraCustomerTransactionModel struct { db *gorm.DB TableName string // 表名 } + +func (m *KillaraCustomerTransactionModel) GetHistoryProfit(data map[string]interface{}) (float64, error) { + var profitLoss float64 + query := m.db.Model(&KillaraCustomerTransaction{}). + Select("SUM(profit_loss) AS profit_loss"). + Where("type = 2 AND status = 3") + + if customerID, ok := data["customer_id"].(uint64); ok && customerID > 0 { + query = query.Where("customer_id = ?", customerID) + } + + if customerHoldID, ok := data["customer_hold_id"].(uint64); ok && customerHoldID > 0 { + query = query.Where("customer_hold_id = ?", customerHoldID) + } + + if market, ok := data["market"].(string); ok && market != "" { + query = query.Where("market = ?", market) + } + + if stockSymbol, ok := data["stock_symbol"].(string); ok && stockSymbol != "" { + query = query.Where("stock_symbol = ?", stockSymbol) + } + + if orderType, ok := data["order_type"].(uint64); ok && orderType > 0 { + query = query.Where("order_type = ?", orderType) + } + + if exchangeOrder, ok := data["exchange_order"].(uint64); ok { + query = query.Where("exchange_order = ?", exchangeOrder) + } + + if exchangeStatus, ok := data["exchange_status"].(uint64); ok { + query = query.Where("exchange_status = ?", exchangeStatus) + } + + err := query.Scan(&profitLoss).Error + if err != nil { + return 0, err + } + + return profitLoss, nil +} + +func (m *KillaraCustomerTransactionModel) GetTransactionsForFrontEnd(tx *gorm.DB, data map[string]interface{}) ([]*KillaraCustomerTransaction, error) { + var db *gorm.DB + if tx != nil { + db = tx + } else { + db = m.db + } + + var transactions []*KillaraCustomerTransaction + query := db.Model(&KillaraCustomerTransaction{}) + + if customerID, ok := data["customer_id"]; ok && customerID != "" { + query = query.Where("customer_id = ?", customerID) + } + + if customerHoldID, ok := data["customer_hold_id"]; ok && customerHoldID != "" { + query = query.Where("customer_hold_id = ?", customerHoldID) + } + + if market, ok := data["market"]; ok && market != "" { + if markets, ok := market.([]string); ok { + query = query.Where("market IN (?)", markets) + } else { + query = query.Where("market = ?", market) + } + } + + if stockSymbol, ok := data["stock_symbol"]; ok && stockSymbol != "" { + query = query.Where("stock_symbol = ?", stockSymbol) + } + + if transType, ok := data["type"]; ok && transType != "" { + query = query.Where("type = ?", transType) + } + + if orderType, ok := data["order_type"]; ok && orderType != "" { + query = query.Where("order_type = ?", orderType) + } + + if status, ok := data["status"]; ok && status != "" { + query = query.Where("status = ?", status) + } + + if exchangeOrder, ok := data["exchange_order"]; ok { + query = query.Where("exchange_order = ?", exchangeOrder) + } + + if exchangeStatus, ok := data["exchange_status"]; ok { + query = query.Where("exchange_status = ?", exchangeStatus) + } + + if sort, ok := data["sort"]; ok && sort != "" { + if order, ok := data["order"]; ok && order != "" { + query = query.Order(fmt.Sprintf("%s %s", sort, order)) + } else { + query = query.Order(fmt.Sprintf("%s DESC", sort)) + } + } + + if start, ok := data["start"]; ok && start != "" { + startInt, _ := strconv.Atoi(start.(string)) + if limit, ok := data["limit"]; ok && limit != "" { + limitInt, _ := strconv.Atoi(limit.(string)) + query = query.Offset(startInt).Limit(limitInt) + } else { + query = query.Offset(startInt).Limit(10) + } + } else if limit, ok := data["limit"]; ok && limit != "" { + limitInt, _ := strconv.Atoi(limit.(string)) + query = query.Limit(limitInt) + } + + err := query.Find(&transactions).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + + return transactions, nil +} diff --git a/model/killara_stock_logic.go b/model/killara_stock_logic.go index 1e3d82e..4e22fc9 100644 --- a/model/killara_stock_logic.go +++ b/model/killara_stock_logic.go @@ -1,6 +1,8 @@ package model import ( + "github.com/iapologizewhenimwrong/Vestmore_GO/proto" + "github.com/iapologizewhenimwrong/Vestmore_GO/utils/constants" "gorm.io/gorm" ) @@ -9,3 +11,83 @@ type KillaraStockModel struct { db *gorm.DB TableName string // 表名 } + +// , stockLocalHistoryID uint64, source string +func (m *KillaraStockModel) StockDetail(tx *gorm.DB, market string, stockType int, stockSymbol string) (map[string]interface{}, error) { + // var db *gorm.DB + // if tx != nil { + // db = tx + // } else { + // db = m.db + // } + + trueStockSymbol := stockSymbol + + // var localData *KillaraStockLocal + + // if stockLocalHistoryID != 0 { + // localData = StockLocalHistoryMapLocalModel.Find(stockLocalHistoryID) + // } else { + // localData = StockLocalDao.FindSymbolWithTrashed(market, stockSymbol) + // } + + // if localData != nil { + // trueStockSymbol = *localData.OriginStockSymbol + // } + + // modelStockTool := &StockTool{} + + marketForSearch := market + if market == "HK" { + marketForSearch = constants.HKEX + } + + // data := modelStockTool.Info(fmt.Sprintf("%s|%d|%s", marketForSearch, stockType, trueStockSymbol)) + data, err := proto.StockDetail(marketForSearch, stockType, trueStockSymbol) + if err != nil { + return nil, err + } + + data["symbol_empty"] = data["symbol"] == nil // 空 + + data["symbol"] = stockSymbol // 覆盖 + data["true_symbol"] = trueStockSymbol // 真实 + + // if localData != nil && !localData.DeleteDate.IsZero() { + // // 这个已经删除了 + // data["local_delete_date"] = localData.DeleteDate + // } + + // 空股票也会返回数据 + if data["symbol_empty"].(bool) { + return data, nil + } + + // if localData != nil { + // if source == "app" { + // data["name"] = localData.LanguageAppName + // } else { + // data["name"] = localData.LanguageAdminName + // } + // data["origin_price"] = data["price"] + // price, _ := decimal.NewFromString(fmt.Sprintf("%v", data["price"])) + // data["price"] = StockLocalLogic.CalPrice(price, localData.Diff, market).String() + + // if localData.Stop == StockLocalModel.StopNo { + // data["securityStatus"] = 1 + // } else { + // data["securityStatus"] = 3 + // } + // data["msgs"] = localData.GroupNum + + // data["local_stock_symbol"] = localData.StockSymbol + // data["local_origin_stock_symbol"] = localData.OriginStockSymbol + // data["local_stock_market"] = localData.Market + // data["stock_local_id"] = localData.ID + // data["stock_local_diff"] = localData.Diff + // data["stock_group_num"] = localData.GroupNum + // data["stock_name_json"] = localData.StockNameJSON + // } + + return data, nil +} diff --git a/proto/stock.go b/proto/stock.go new file mode 100644 index 0000000..756277f --- /dev/null +++ b/proto/stock.go @@ -0,0 +1,94 @@ +package proto + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" +) + +var appId = "2D64E3B7A9884A8C5E2BFF437763E76A" +var StockApiHost = "http://kindics.com:7040" +var appSecret = "5105D41CE0527A9AF6A307341B3ADA10" + +func StockDetail(market string, stockType uint64, stockSymbol string) (map[string]any, error) { + instrument := fmt.Sprintf("%s|%d|%s", market, stockType, stockSymbol) + + data := url.Values{ + "appId": {appId}, + "appSecret": {appSecret}, + "action": {"/V2/Quotation/Detail"}, + "instrument": {instrument}, + "lang": {"zh-CN"}, + } + + resp, err := http.PostForm(StockApiHost, data) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var result map[string]interface{} + err = json.Unmarshal(body, &result) + if err != nil { + return nil, err + } + + if result["code"].(float64) == 1 { + response := result["data"].(map[string]interface{}) + + lastTradeDate := "1991-01-01" + if date, ok := response["lastTradeDate"].(string); ok && date != "" { + lastTradeDate = date[:4] + "-" + date[4:6] + "-" + date[6:] + } + + maturityDate := "1991-01-01" + if date, ok := response["maturityDate"].(string); ok && date != "" { + maturityDate = date[:4] + "-" + date[4:6] + "-" + date[6:] + } + + inlineFlag := 2 + if flag, ok := response["inlineFlag"].(float64); ok { + inlineFlag = int(flag) + } + + return map[string]interface{}{ + "symbol": response["symbol"], + "name": response["name"], + "msgs": response["lotSize"], + "price": response["latestPrice"], + "securityStatus": response["securityStatus"], + "exercisePrice": getFloat(response, "exercisePrice"), + "makePeace": getFloat(response, "makePeace"), + "exchangeRatio": getFloat(response, "exchangeRatio"), + "exchangePrice": getFloat(response, "exchangePrice"), + "callPrice": getFloat(response, "callPrice"), + "lastTradeDate": lastTradeDate, + "maturityDate": maturityDate, + "callOrPut": getString(response, "callOrPut"), + "inlineFlag": inlineFlag, + }, nil + } + + return nil, nil +} + +func getFloat(m map[string]interface{}, key string) float64 { + if val, ok := m[key].(float64); ok { + return val + } + return 0 +} + +func getString(m map[string]interface{}, key string) string { + if val, ok := m[key].(string); ok { + return val + } + return "" +} diff --git a/proto/stock_test.go b/proto/stock_test.go new file mode 100644 index 0000000..01f0f56 --- /dev/null +++ b/proto/stock_test.go @@ -0,0 +1,11 @@ +package proto + +import "testing" + +func TestStockAPI(t *testing.T) { + result, err := StockDetail("HKEX", 1, "01237") + if err != nil { + panic(err) + } + t.Error(result) +} diff --git a/server/app/internal/handlers/actions/auth.go b/server/app/internal/handlers/actions/auth.go index 3ef0df4..e11ee65 100644 --- a/server/app/internal/handlers/actions/auth.go +++ b/server/app/internal/handlers/actions/auth.go @@ -69,7 +69,7 @@ func BaseGetToken(ctx *ActionContext[BaseGetTokenParam]) (resp *basic.Response) if result.CustomerId != nil && *result.CustomerId != 0 { modelCustomer := model.Models.KillaraCustomerModel - customer, err := modelCustomer.GetCustomer(*result.CustomerId) + customer, err := modelCustomer.GetCustomer(nil, *result.CustomerId) if err == nil && customer != nil { isLogin = true } else { @@ -155,13 +155,13 @@ func AccountLoginWithTelephonePassword(ctx *ActionContext[AccountLoginWithTeleph var customer *model.KillaraCustomer var err error - customer, err = model.Models.KillaraCustomerModel.GetCustomerByTelephoneForBackEnd(telephone) + customer, err = model.Models.KillaraCustomerModel.GetCustomerByTelephoneForBackEnd(nil, telephone) if err != nil { resp.ErrorMsg(1, err.Error()) return } if customer == nil { - customer, err = model.Models.KillaraCustomerModel.GetCustomerByCode(telephone) + customer, err = model.Models.KillaraCustomerModel.GetCustomerByCode(nil, telephone) if err != nil { resp.ErrorMsg(1, err.Error()) return @@ -335,7 +335,7 @@ func AccountLoginWithEmailPassword(ctx *ActionContext[AccountLoginWithEmailPassw var customer *model.KillaraCustomer var err error - customer, err = model.Models.KillaraCustomerModel.GetCustomerByEmailForBackEnd(email) + customer, err = model.Models.KillaraCustomerModel.GetCustomerByEmailForBackEnd(nil, email) if err != nil { return resp.ErrorErr(1, err) } diff --git a/server/app/internal/handlers/actions/securities.go b/server/app/internal/handlers/actions/securities.go index dba6b19..029299e 100644 --- a/server/app/internal/handlers/actions/securities.go +++ b/server/app/internal/handlers/actions/securities.go @@ -1,6 +1,15 @@ package actions -import "github.com/iapologizewhenimwrong/Vestmore_GO/utils/basic" +import ( + "math" + "strconv" + "strings" + + "github.com/iapologizewhenimwrong/Vestmore_GO/model" + "github.com/iapologizewhenimwrong/Vestmore_GO/utils/basic" + "github.com/iapologizewhenimwrong/Vestmore_GO/utils/marketdate" + "gorm.io/gorm" +) // @Action securities/account // SecuritiesAccount @@ -13,7 +22,205 @@ import "github.com/iapologizewhenimwrong/Vestmore_GO/utils/basic" // token: string; func SecuritiesAccount(ctx *ActionContext[SecuritiesAccountParam]) (resp *basic.Response) { - return resp.Success() + market := ctx.Param.Market + if market == "" { + return resp.ErrorMsg(1, "market 参数缺失") + } + + if ctx.Param.Token == "" { + return resp.ErrorMsg(3, "token 参数缺失") + } + + var modelHold = model.Models.KillaraCustomerHoldModel + var modelTransaction = model.Models.KillaraCustomerTransactionModel + var modelBalance = model.Models.KillaraCustomerBalanceModel + + market = strings.ToUpper(strings.TrimSpace(market)) + + var respData map[string]any + model.Models.KillaraCustomerModel.Transaction(func(tx *gorm.DB) error { + + customerToken, err := model.Models.KillaraCustomerTokenModel.GetToken(tx, ctx.Param.Token) + if err != nil { + return err + } + customer, err := model.Models.KillaraCustomerModel.GetCustomer(tx, *customerToken.CustomerId) + if err != nil { + return err + } + + var balance float64 + if market == "HK" { + balance = *customer.HkBalance + } else if market == "US" { + balance = *customer.UsBalance + } else { + balance = 0 + } + + assetValue := balance + + balanceFreeze, err := modelBalance.SumFreeze(tx, *customer.CustomerId, market, 0) + if err != nil { + return err + } + + holdValue := 0.0 + profitToday := 0.0 + + profitHistory, err := modelTransaction.GetHistoryProfit(map[string]interface{}{ + "customer_id": *customer.CustomerId, + "market": market, + }) + if err != nil { + return err + } + profitHistory = math.Round(profitHistory*1000) / 1000 + + var holds []map[string]interface{} + var orders []map[string]interface{} + + // 当前持仓 + holdResults, err := modelHold.GetHoldsForFrontEnd(tx, map[string]interface{}{ + "customer_id": *customer.CustomerId, + "market": market, + "status": 1, + "sort": "customer_hold_id", + }) + if err != nil { + return err + } + + for _, result := range holdResults { + var stock map[string]interface{} + var nowPrice float64 + + if market == "HK" || market == "US" { + stock, err = model.Models.KillaraStockModel.StockDetail(nil, + *result.Market, *result.StockType, *result.StockSymbol, + ) + if err != nil { + return err + } + if stock != nil { + nowPrice = stock["price"].(float64) + } else { + nowPrice = 0 + } + } else { + // 调用 其一个方法查询股票详情 TODO: + // stock, err = (result.StockSymbol) + // if err != nil { + // return resp.ErrorMsg(1, err.Error()) + // } + // if stock != nil { + // if result.Type == 0 || result.Type == 1 { + // nowPrice = stock["ask"].(float64) + // } else { + // nowPrice = stock["bid"].(float64) + // } + // } else { + // nowPrice = 0 + // } + } + + var value, originValue, profitLoss, profitLossRate float64 + if nowPrice > 0 { + value = math.Round(nowPrice*float64(*result.Quantity)*1000) / 1000 + originValue = math.Round(*result.Price*float64(*result.Quantity)*1000) / 1000 + profitLoss = math.Round((value-originValue)*1000) / 1000 + + if profitLoss != 0 && originValue != 0 { + if *result.IsWithfunding == 1 { + profitLossRate = math.Round(profitLoss/originValue*100*float64(*result.WithfundingMagnification)*1000) / 1000 + } else { + profitLossRate = math.Round(profitLoss/originValue*100*1000) / 1000 + } + } else { + profitLossRate = 0 + } + } else { + value = 0 + originValue = 0 + profitLoss = 0 + profitLossRate = 0 + } + + isWithfunding := false + var withfundingData map[string]interface{} + if *result.IsWithfunding == 1 { + isWithfunding = true + withfundingData = map[string]interface{}{ + "withfunding_total": result.WithfundingTotal, + "withfunding_magnification": result.WithfundingMagnification, + "withfunding_day": result.WithfundingDay, + "interest": result.Interest, + "warning_line_price": result.WarningLinePrice, + "clear_line_price": result.ClearLinePrice, + "withfunding_start_date": result.WithfundingStartDate, + "withfunding_end_date": result.WithfundingEndDate, + } + } + + holds = append(holds, map[string]interface{}{ + "id": result.CustomerHoldId, + "stock_name": result.StockName, + "stock_symbol": result.StockSymbol, + "quantity": result.Quantity, + "avg_price": result.Price, + "now_price": nowPrice, + "value": value, + "profit_loss": profitLoss, + "profit_loss_rate": profitLossRate, + "is_withfunding": isWithfunding, + "withfunding_data": withfundingData, + }) + + holdValue += value + profitToday += profitLoss + } + + // // 当前委托 + transactionResults, err := modelTransaction.GetTransactionsForFrontEnd(tx, map[string]interface{}{ + "customer_id": *customer.CustomerId, + "market": market, + "status": 1, + "sort": "customer_transaction_id", + }) + if err != nil { + return err + } + + for _, result := range transactionResults { + + orders = append(orders, map[string]interface{}{ + "id": result.CustomerTransactionId, + "stock_name": result.StockName, + "stock_symbol": result.StockSymbol, + "quantity": result.Quantity, + "price": result.Price, + "date": marketdate.HandleMarketDatetime(result), + "type": result.Type, + }) + } + + assetValue += holdValue + balanceFreeze + + respData = map[string]interface{}{ + "balance": balance, + "balance_freeze": balanceFreeze, + "hold_value": holdValue, + "asset_value": assetValue, + "profit_today": strconv.FormatFloat(profitToday, 'f', -1, 64), + "profit_history": profitHistory, + "orders": orders, + "holds": holds, + } + + return nil + }) + + return resp.Success(respData) } // @Action securities/currency diff --git a/test/gorm_test.go b/test/gorm_test.go index 22f69af..16a5367 100644 --- a/test/gorm_test.go +++ b/test/gorm_test.go @@ -9,28 +9,28 @@ import ( ) func TestCaseInsert(t *testing.T) { - c, err := model.Models.KillaraCustomerModel.GetCustomer(1) + c, err := model.Models.KillaraCustomerModel.GetCustomer(nil, 1) if err != nil { panic(err) } log.Printf("%#v", c) c.CustomerId = nil c.Email = basic.StringPtr("474420502@qq.com") - model.Models.KillaraCustomerModel.InsertCustomer(c) + model.Models.KillaraCustomerModel.InsertCustomer(nil, c) } func TestUpdate(t *testing.T) { - c, err := model.Models.KillaraCustomerModel.GetCustomer(6) + c, err := model.Models.KillaraCustomerModel.GetCustomer(nil, 6) if err != nil { panic(err) } uc := &model.KillaraCustomer{} uc.CustomerId = c.CustomerId uc.Email = basic.StringPtr("474420502@gmail.com") - log.Println(model.Models.KillaraCustomerModel.UpdateCustomer(uc)) + log.Println(model.Models.KillaraCustomerModel.UpdateCustomer(nil, uc)) } func TestExist(t *testing.T) { - log.Println(model.Models.KillaraCustomerModel.CheckEmailExists("474420502@gmail.com")) - log.Println(model.Models.KillaraCustomerModel.CheckEmailExists("474420502@qq.com")) + log.Println(model.Models.KillaraCustomerModel.CheckEmailExists(nil, "474420502@gmail.com")) + log.Println(model.Models.KillaraCustomerModel.CheckEmailExists(nil, "474420502@qq.com")) } diff --git a/translator/tag_test.go b/translator/tag_test.go new file mode 100644 index 0000000..0dcdc00 --- /dev/null +++ b/translator/tag_test.go @@ -0,0 +1,19 @@ +package translator_test + +import ( + "log" + "reflect" + "testing" + + "github.com/iapologizewhenimwrong/Vestmore_GO/translator" +) + +func TestCase1(t *testing.T) { + a := translator.A{} + + at := reflect.ValueOf(a).Type() + if av, ok := at.FieldByName("a"); ok { + log.Println(av.Tag.Get("json")) + } + +} diff --git a/translator/translator.go b/translator/translator.go index 1b0a0fe..fe0969b 100644 --- a/translator/translator.go +++ b/translator/translator.go @@ -9,6 +9,10 @@ import ( "golang.org/x/text/language" ) +type A struct { + a int `json:"*killaraCustomer"` +} + var Bundle *i18n.Bundle func init() { diff --git a/translator/translator_test.go b/translator/translator_test.go index 3393757..734a739 100644 --- a/translator/translator_test.go +++ b/translator/translator_test.go @@ -125,6 +125,13 @@ func TestTrOnline(t *testing.T) { })) } +func TestTrOnlinex(t *testing.T) { + var localizer = i18n.NewLocalizer(Bundle, "zh_cn") + log.Println(localizer.Localize(&i18n.LocalizeConfig{ + MessageID: string("account_not_registered"), + })) +} + func toCamelCase(s string) string { var sb strings.Builder capitalizeNext := true diff --git a/utils/constants/constants.go b/utils/constants/constants.go new file mode 100644 index 0000000..c14cfe5 --- /dev/null +++ b/utils/constants/constants.go @@ -0,0 +1,6 @@ +package constants + +const ( + US = "US" + HKEX = "HKEX" +) diff --git a/utils/convert/model2map.go b/utils/convert/model2map.go new file mode 100644 index 0000000..34f8d4f --- /dev/null +++ b/utils/convert/model2map.go @@ -0,0 +1,27 @@ +package convert + +import ( + "reflect" + "strings" +) + +func ModelToMap(obj any) map[string]any { + result := make(map[string]any) + val := reflect.ValueOf(obj).Elem() // 获取结构体的值 + typ := val.Type() // 获取结构体的类型 + + for i := 0; i < val.NumField(); i++ { // 遍历结构体的每个字段 + field := val.Field(i) + if !field.IsNil() { // 如果字段不为nil + fieldType := typ.Field(i) + tagValue := fieldType.Tag.Get("gorm") // 获取gorm标签值 + key := strings.Split(tagValue, ",")[0] // 假设tag格式为"column:value,other_options" + if key == "" { + key = fieldType.Name // 如果没有指定tag,则使用字段名作为key + } + result[key] = field.Elem().Interface() // 将字段值存入map + } + } + + return result +} diff --git a/utils/marketdate/marketdate.go b/utils/marketdate/marketdate.go new file mode 100644 index 0000000..df03fcc --- /dev/null +++ b/utils/marketdate/marketdate.go @@ -0,0 +1,76 @@ +package marketdate + +import ( + "reflect" + "strings" + "time" +) + +// HandleMarketDatetime 根据市场和时间字段处理datetime格式 +func HandleMarketDatetimeEx(data map[string]interface{}, datetimeField string) string { + + marketField := "market" + + format := "2006-01-02 15:04:05" + + market, ok := data[marketField].(string) + if !ok || market == "" { + if datetimeStr, ok := data[datetimeField].(string); ok { + return datetimeStr + } + return "" + } + + datetimeStr, ok := data[datetimeField].(string) + if !ok || datetimeStr == "" { + return "" + } + + if strings.Contains(strings.ToUpper(market), "HK") { + loc, _ := time.LoadLocation("Asia/Hong_Kong") + t, _ := time.ParseInLocation("2006-01-02 15:04:05", datetimeStr, time.Local) + return t.In(loc).Format(format) + } + + return datetimeStr +} + +func HandleMarketDatetime(data any) string { + format := "2006-01-02 15:04:05" + dataVal := reflect.ValueOf(data) + dataType := dataVal.Type() + + var datetimeField, marketField reflect.Value + + // 通过反射找到标签为"datetimeField"和"market"的字段 + for i := 0; i < dataType.NumField(); i++ { + field := dataType.Field(i) + jsonTag := field.Tag.Get("json") + + if jsonTag == "datetimeField" { + datetimeField = dataVal.Field(i) + } else if jsonTag == "market" { + marketField = dataVal.Field(i) + } + } + + // 检查字段是否为期望的类型 + if datetimeField.Kind() != reflect.Ptr || datetimeField.Type().Elem().Kind() != reflect.Struct || datetimeField.IsNil() { + return "" + } + if marketField.Kind() != reflect.Ptr || marketField.Type().Elem().Kind() != reflect.String || marketField.IsNil() { + return "" + } + + // 获取字段值 + datetimeVal := datetimeField.Elem().Interface().(*time.Time) + marketVal := marketField.Elem().Interface().(*string) + + // 判断市场和格式化时间 + if strings.Contains(strings.ToUpper(*marketVal), "HK") { + loc, _ := time.LoadLocation("Asia/Hong_Kong") + return datetimeVal.In(loc).Format(format) + } + + return datetimeVal.Format(format) +}