Эта статья изначально была опубликована по адресу: https://www.blog.duomly.com/golang-course-with-building-a-fintech-banking-app-lesson-5-bank-transactions-part-2/
Введение в курс Golang Банковские операции
Введение в курс Golang Банковские операции
В Уроке 5 курса Голанг мы продолжим банковские операции.
В предыдущих выпусках курса мы узнали, как выполнять миграцию:
Курс Golang с созданием банковского приложения для финансовых технологий - Урок 1: Запуск проекта
Мы узнали, как выполнить вход пользователя:
Курс Golang по созданию банковского приложения для финансовых технологий - Урок 2: Вход и REST API
Мы узнали, как регистрировать пользователей:
Курс Golang с созданием банковского приложения для финансовых технологий - Урок 3: Регистрация пользователя
Мы создали аутентификацию пользователей и начали с транзакций:
Курс Golang по созданию банковского приложения Fintech - Урок 4: Аутентификация пользователя и банковские транзакции, ЧАСТЬ 1
Также вам нужно помнить о курсе Angular 9, созданном моей подругой Анной:
Angular курс по созданию банковского приложения с Tailwind CSS - Урок 1: Запуск проекта
Сегодня мы остановимся на следующей части банковских операций.
Мы создадим новый модуль с именем transaction, который мы будем использовать для создания истории транзакций, но не только!
Как и в остальной части сегодняшнего урока, мы проведем небольшой рефакторинг, создадим логику осуществления банковских переводов и обработаем новые конечные точки API.
Давайте начнем!
Если вы предпочитаете видео, вот версия для YouTube:
Создайте интерфейс транзакции
В качестве первого шага нам нужно подготовить некоторый интерфейс транзакций, который мы будем использовать для логики, связанной с БД.
Для этого нам нужно зайти в файл interfaces.go и создать структуру с именем «Transaction».
Этот элемент должен содержать следующие реквизиты: gorm.Model, From as uint, To as uint и Amount as int.
Взгляните на пример:
type Transaction struct {
gorm.Model
From uint
To uint
Amount int
}
Создайте функцию MigrateTransactions в migrations.go
На следующем этапе мы должны сосредоточиться на реализации транзакций внутри базы данных.
Для этого мы могли бы обновить функцию с именем «Migrate», и даже это лучший способ сделать это.
Но в этом случае я создам отдельную функцию, чтобы не путать ваши миграции с предыдущими.
Нам нужно зайти в файл migrations.go и создать функцию с именем «MigrateTransactions».
Внутри этой функции мы должны создать логику миграции БД, но связанную с интерфейсом с именем «Транзакции».
func MigrateTransactions() { Transactions := &interfaces.Transaction{}
db := helpers.ConnectDB() db.AutoMigrate(&Transactions) defer db.Close() }
Создать функцию getAccount
Следующая логика, которая нам понадобится, - это функция, которая найдет аккаунт по ID и вернет его.
Для этого в качестве первого шага нам нужно зайти в useraccounts.go и создать функцию с именем «getAccount».
Внутри функции нам нужно подключиться к БД, найти учетную запись по id и вернуться.
func getAccount(id uint) *interfaces.Account{
db := helpers.ConnectDB()
account := &interfaces.Account{}
if db.Where("id = ? ", id).First(&account).RecordNotFound() {
return nil
}
defer db.Close()
return account
}
Создать функцию Transaction
Затем мы можем перейти к более увлекательной части урока - транзакции.
Чтобы начать работать с этой логикой, мы должны остаться в том же файле useraccounts.go.
В качестве первого шага мы должны создать функцию с именем «Транзакция» с несколькими параметрами и ответом в виде map [string] interface {}.
Params должны быть userId, from и to, в качестве uint, сумма должна быть int, а jwt - строкой.
func Transaction(userId uint, from uint, to uint, amount int, jwt string) map[string]interface{} {
}
Преобразовать uint в строку
Следующей частью функции Transaction должно быть действие, которое преобразует userId из uint в строку.
Мы делаем это только потому, что ValidateToken принимает userId как строку. Мы можем реорганизовать это позже, когда у нас будет больше мест, которые используют валидацию, а не из GET.
userIdString := fmt.Sprint(userId)
Проверить jwt
Как и в предыдущем эпизоде, сегодня мы должны проверить токен jwt.
Внутри функции «Транзакция» нам нужно создать такой же оператор if / else с проверкой.
func Transaction(userId uint, from uint, to uint, amount int, jwt string) map[string]interface{} { userIdString := fmt.Sprint(userId) isValid := helpers.ValidateToken(userIdString, jwt) if isValid {
} else { return map[string]interface{}{"message": "Not valid token"} } }
Возьмите отправителя и получателя
Затем мы должны взять две учетные записи, обе с использованием функции «getAccount», которую мы создали ранее.
Первая - это учетная запись отправителя, назначенная переменной с именем «fromAccount».
Второй - получатель, назначенный для «toAccount».
fromAccount := getAccount(from)
toAccount := getAccount(to)
Обработка ошибок
В следующей части мы должны поискать несколько ошибок и проверить, все ли в порядке.
Первый и очень важный момент - проверить, существуют ли обе учетные записи.
Следующий - проверить, являемся ли мы владельцем учетной записи.
Последний - проверить, достаточно ли у нас денег для создания перевода.
if fromAccount == nil || toAccount == nil {
return map[string]interface{}{"message": "Account not found"}
} else if fromAccount.UserID != userId {
return map[string]interface{}{"message": "You are not owner of the account"}
} else if int(fromAccount.Balance) < amount {
return map[string]interface{}{"message": "Account balance is too small"}
}
Обновить аккаунт
Вся логика проверена, если наши данные в порядке, и мы можем выполнить передачу.
Далее мы можем отправить деньги.
Чтобы сохранить в БД, мы отправили деньги, и получатель их получил, мы должны обновить наши банковские счета.
Нам необходимо обновить наш счет и списать деньги с нашего баланса.
И мы должны сделать то же самое со счетом получателя с одной небольшой разницей, мы должны увеличить его баланс.
updatedAccount := updateAccount(from, int(fromAccount.Balance) - amount)
updateAccount(to, int(toAccount.Balance) + amount)
Создать транзакцию
Когда передача будет завершена, мы должны где-нибудь сохранить информацию об этом.
Чтобы информация о переводе была в нашей истории, нам нужно создать транзакцию, вызвав функцию «CreateTransaction».
Мы создадим эту функцию на следующих шагах, а теперь просто добавьте вызов.
transactions.CreateTransaction(from, to, amount)
Ответить
Поскольку последний шаг в функции «Транзакция» - это подготовка ответа и его возврат.
var response = map[string]interface{}{"message": "all is fine"}
response["data"] = updatedAccount
return response
Обновить функцию updateAccount
Пока мы не перейдем от useraccounts.go, нам нужно обновить функцию с именем updateAccount.
Во-первых, нам нужно реорганизовать способ обновления учетной записи, чтобы позже у нас был более простой способ манипулировать ею с помощью обновленных данных.
Затем мы должны добавить некоторую логику, которая подготовит ответ учетной записи и вернет его.
func updateAccount(id uint, amount int) interfaces.ResponseAccount { db := helpers.ConnectDB() account := interfaces.Account{} responseAcc := interfaces.ResponseAccount{}
db.Where("id = ? ", id).First(&account) account.Balance = uint(amount) db.Save(&account)
responseAcc.ID = account.ID responseAcc.Name = account.Name responseAcc.Balance = int(account.Balance) defer db.Close() return responseAcc }
Создание транзакций модуля
Когда мы закончили с useraccounts.go, мы можем перейти в модуль «транзакции».
Во-первых, нам нужно создать каталог с именем «транзакции».
Внутри каталога нам нужно создать файл с таким же именем и объявить внутри пакет с именем «transaction».
package transactions
import ( "duomly.com/go-bank-backend/helpers" "duomly.com/go-bank-backend/interfaces" )
Создать функцию CreateTransaction
Затем внутри файла transaction.go мы должны создать функцию с именем «CreateTransaction».
Внутри функции нам нужно добавить логику, которая будет отвечать за создание новой записи в базе данных.
func CreateTransaction(From uint, To uint, Amount int) { db := helpers.ConnectDB() transaction := &interfaces.Transaction{From: From, To: To, Amount: Amount} db.Create(&transaction)
defer db.Close() }
Создайте интерфейс TransactionBody в api.go
На этом этапе мы можем перейти к api.go, это будет последний файл, в котором мы разрабатываем код.
В качестве первого шага в этом файле мы должны создать интерфейс с именем «TransactionBody».
Этот интерфейс будет отвечать за тело вызова транзакции.
type TransactionBody struct {
UserId uint
From uint
To uint
Amount int
}
Рефакторинг apiResponse
Затем нам нужно внести одно небольшое изменение в наш apiResponse.
Мы должны вернуть всю переменную вызова вместо «данных».
func apiResponse(call map[string]interface{}, w http.ResponseWriter) {
if call["message"] == "all is fine" {
resp := call
json.NewEncoder(w).Encode(resp)
} else {
resp := call
json.NewEncoder(w).Encode(resp)
}
}
Создать транзакцию функции
На последнем этапе создания логики нам нужно обработать вызов API.
Для этого мы должны создать функцию с именем «транзакция».
Практически вся логика будет аналогична остальным вызовам API. Тем не менее, в этом случае нам нужно помнить об обработке аутентификации и использовать интерфейс, который мы создали ранее.
func transaction(w http.ResponseWriter, r *http.Request) { body := readBody(r) auth := r.Header.Get("Authorization") var formattedBody TransactionBody err := json.Unmarshal(body, &formattedBody) helpers.HandleErr(err)
transaction := useraccounts.Transaction(formattedBody.UserId, formattedBody.From, formattedBody.To, formattedBody.Amount, auth) apiResponse(transaction, w) }
Обработка конечной точки в маршрутизаторе
Теперь осталось только обработать конечную точку «/ transaction» в маршрутизации.
Передайте функцию «транзакция» и используйте метод «POST».
func StartApi() {
router := mux.NewRouter()
router.Use(helpers.PanicHandler)
router.HandleFunc("/login", login).Methods("POST")
router.HandleFunc("/register", register).Methods("POST")
router.HandleFunc("/transaction", transaction).Methods("POST")
router.HandleFunc("/user/{id}", getUser).Methods("GET")
fmt.Println("App is working on port :8888")
log.Fatal(http.ListenAndServe(":8888", router))
}
Сделайте миграцию
Весь код готов!
Теперь мы можем приступить к миграции.
Как и в первом уроке, нам нужно зайти в main.go и добавить вызов «MigrateTransactions» в функцию «main».
Не забудьте прокомментировать «api.StartApi ()».
import "duomly.com/go-bank-backend/migrations"
func main() { migrations.MigrateTransactions() // api.StartApi() }
Вывод
Поздравляем, теперь вы можете сделать первый банковский перевод!
Я очень рад, что функции вашего проекта стали намного более продвинутыми, и он выглядит намного более законченным.
Вот репозиторий кода для текущего урока:
https://github.com/Duomly/go-bank-backend/tree/Golang-course-Lesson-5
В следующем выпуске мы сосредоточимся на оптимизации производительности и рефакторинге соединений с базой данных.
Это даст нам намного лучшую производительность, устранит некоторые опасности, такие как убийства DB при оверколе, и поможет нам сделать приложение намного более стабильным.
Будьте в курсе и подписывайтесь на нас, потому что я не могу дождаться, когда мы создадим еще больше кода!
Спасибо за внимание,
Радек из Дуомли.