...
1 package main
2
3 import (
4 "database/sql"
5 "fmt"
6 "log"
7
8 "github.com/kisielk/sqlstruct"
9 )
10
11 const ORDER_PENDING = 0
12 const ORDER_CANCELLED = 1
13
14 type User struct {
15 Id int `sql:"id"`
16 Username string `sql:"username"`
17 Balance float64 `sql:"balance"`
18 }
19
20 type Order struct {
21 Id int `sql:"id"`
22 Value float64 `sql:"value"`
23 ReservedFee float64 `sql:"reserved_fee"`
24 Status int `sql:"status"`
25 }
26
27 func cancelOrder(id int, db *sql.DB) (err error) {
28 tx, err := db.Begin()
29 if err != nil {
30 return
31 }
32
33 var order Order
34 var user User
35 sql := fmt.Sprintf(`
36 SELECT %s, %s
37 FROM orders AS o
38 INNER JOIN users AS u ON o.buyer_id = u.id
39 WHERE o.id = ?
40 FOR UPDATE`,
41 sqlstruct.ColumnsAliased(order, "o"),
42 sqlstruct.ColumnsAliased(user, "u"))
43
44
45 rows, err := tx.Query(sql, id)
46 if err != nil {
47 tx.Rollback()
48 return
49 }
50
51 defer rows.Close()
52
53 if !rows.Next() {
54 tx.Rollback()
55 return
56 }
57
58
59 err = sqlstruct.ScanAliased(&order, rows, "o")
60 if err != nil {
61 tx.Rollback()
62 return
63 }
64
65
66 if order.Status != ORDER_PENDING {
67 tx.Rollback()
68 return
69 }
70
71
72 err = sqlstruct.ScanAliased(&user, rows, "u")
73 if err != nil {
74 tx.Rollback()
75 return
76 }
77 rows.Close()
78
79
80 sql = "UPDATE users SET balance = balance + ? WHERE id = ?"
81 refundStmt, err := tx.Prepare(sql)
82 if err != nil {
83 tx.Rollback()
84 return
85 }
86 defer refundStmt.Close()
87 _, err = refundStmt.Exec(order.Value+order.ReservedFee, user.Id)
88 if err != nil {
89 tx.Rollback()
90 return
91 }
92
93
94 order.Status = ORDER_CANCELLED
95 sql = "UPDATE orders SET status = ?, updated = NOW() WHERE id = ?"
96 orderUpdStmt, err := tx.Prepare(sql)
97 if err != nil {
98 tx.Rollback()
99 return
100 }
101 defer orderUpdStmt.Close()
102 _, err = orderUpdStmt.Exec(order.Status, order.Id)
103 if err != nil {
104 tx.Rollback()
105 return
106 }
107 return tx.Commit()
108 }
109
110 func main() {
111
112 db, err := sql.Open("mysql", "root:@/orders")
113 if err != nil {
114 log.Fatal(err)
115 }
116 defer db.Close()
117 err = cancelOrder(1, db)
118 if err != nil {
119 log.Fatal(err)
120 }
121 }
122
View as plain text