Copilot commented on code in PR #95: URL: https://github.com/apache/incubator-seata-go-samples/pull/95#discussion_r3328549681
########## at/ecommerce/README.md: ########## @@ -0,0 +1,115 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +# AT E-commerce Sample + +This sample demonstrates an e-commerce order flow in Seata Go AT mode. + +The scenario contains three independent Go services: + +1. `order-service` creates the order record and starts the global transaction +2. `inventory-service` deducts stock +3. `account-service` deducts the user balance + +All three operations must succeed or fail together. If the account balance is insufficient, `account-service` rejects the request and Seata automatically rolls back the order creation and inventory deduction through `undo_log`. + +## Directory Layout + +- `order/`: `order-service` +- `inventory/`: `inventory-service` +- `account/`: `account-service` +- `sql/mysql_ecommerce.sql`: schema and seed data for the three MySQL databases +- `docker-compose.yml`: MySQL and Seata Server + +## Start the Infrastructure + +```bash +cd at/ecommerce +docker-compose up -d +``` + +Default ports: + +- MySQL: `3306` +- Seata Server: `8091` +- order-service: `18080` +- inventory-service: `18081` +- account-service: `18082` + +## Start the Three Services + +From the repository root, open three terminals and run: + +```bash +go run ./at/ecommerce/order +go run ./at/ecommerce/inventory +go run ./at/ecommerce/account +``` + +The services use `conf/seatago.yml`, so run them from the repository root as shown above. + +## Run the Success Scenario + +The account seed balance is `50`, so a `money` value below that limit commits successfully: + +```bash +curl -X POST http://127.0.0.1:18080/createOrder \ + -H 'Content-Type: application/json' \ + -d '{"userId":"U100001","commodityCode":"C100001","count":2,"money":30}' +``` + +Expected result: + +- a new row is inserted into `seata_ecommerce_order.order_tbl` +- `seata_ecommerce_inventory.inventory_tbl.stock` decreases from `100` to `98` +- `seata_ecommerce_account.account_tbl.balance` decreases from `50` to `20` + +## Run the Rollback Scenario + +This request exceeds the seed balance and makes `account-service` reject the deduction: + +```bash +curl -X POST http://127.0.0.1:18080/createOrder \ + -H 'Content-Type: application/json' \ + -d '{"userId":"U100001","commodityCode":"C100001","count":2,"money":100}' +``` + +Expected result: + +- `account-service` returns `balance not enough` +- the global transaction fails in `order-service` +- `seata_ecommerce_order.order_tbl` does not gain a new committed row +- `seata_ecommerce_inventory.inventory_tbl.stock` remains unchanged from its value before this request +- `seata_ecommerce_account.account_tbl.balance` remains unchanged from its value before this request + +## Reset the Demo Data + +If you want to re-run the checks from the initial database state, recreate the sample containers and volumes: + +```bash +cd at/ecommerce +docker compose down -v +docker compose up -d Review Comment: This code block uses `docker compose`, while earlier sections (and other samples in this repo) use `docker-compose`. Please use a single command style consistently to avoid confusing users / breaking on systems where only one variant is available. ########## at/ecommerce/order/create.go: ########## @@ -0,0 +1,143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/parnurzeal/gorequest" + "seata.apache.org/seata-go/pkg/constant" + "seata.apache.org/seata-go/pkg/tm" + "seata.apache.org/seata-go/pkg/util/log" +) + +type OrderRequest struct { + UserID string `json:"userId"` + CommodityCode string `json:"commodityCode"` + Count int `json:"count"` + Money int `json:"money"` +} + +type InventoryRequest struct { + CommodityCode string `json:"commodityCode"` + Count int `json:"count"` +} + +type AccountRequest struct { + UserID string `json:"userId"` + Money int `json:"money"` +} + +func createOrder(c *gin.Context) error { + var req OrderRequest + if err := c.ShouldBindJSON(&req); err != nil { + return err + } + Review Comment: Order request fields are not validated. In particular, non-positive `count`/`money` can lead to downstream updates increasing stock/balance (`x - (-n)`) if the downstream services don’t guard against it. Validate required fields and ensure `count`/`money` are positive before starting the global transaction. ########## at/ecommerce/inventory/deduct.go: ########## @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +type InventoryRequest struct { + CommodityCode string `json:"commodityCode"` + Count int `json:"count"` +} + +func deductInventory(c *gin.Context) error { + var req InventoryRequest + if err := c.ShouldBindJSON(&req); err != nil { + return err + } + Review Comment: `count` is not validated. A zero/negative `count` would either be a no-op or *increase* stock (`stock - (-n)`), and the `stock >= ?` guard would pass for negative values. Reject non-positive counts (and empty commodity codes) to prevent incorrect inventory updates. ########## at/ecommerce/account/deduct.go: ########## @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "fmt" + + "github.com/gin-gonic/gin" +) + +type AccountRequest struct { + UserID string `json:"userId"` + Money int `json:"money"` +} + +func deductAccount(c *gin.Context) error { + var req AccountRequest + if err := c.ShouldBindJSON(&req); err != nil { + return err + } + Review Comment: `money` is not validated. A zero/negative `money` would either be a no-op or *increase* balance (`balance - (-n)`), and the `balance >= ?` guard would pass for negative values. Reject non-positive amounts (and empty user IDs) to prevent incorrect account updates. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
