[TASK] add buy option (delete without tag - by secret)
This commit is contained in:
parent
ca0d349c04
commit
9c57445eeb
|
@ -8,7 +8,7 @@ fouled_deleted = "0m"
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
type = "sqlite3"
|
type = "sqlite3"
|
||||||
#logging = true
|
logging = true
|
||||||
connection = "file::memory:?mode=memory&cache=shared"
|
connection = "file::memory:?mode=memory&cache=shared"
|
||||||
# For Master-Slave cluster
|
# For Master-Slave cluster
|
||||||
# read_connection = ""
|
# read_connection = ""
|
||||||
|
|
|
@ -20,4 +20,5 @@ func BindAPI(router *goji.Mux) {
|
||||||
|
|
||||||
router.HandleFunc(pat.Post("/api/goods/locking"), lockGoods)
|
router.HandleFunc(pat.Post("/api/goods/locking"), lockGoods)
|
||||||
router.HandleFunc(pat.Delete("/api/goods/locking"), releaseGoods)
|
router.HandleFunc(pat.Delete("/api/goods/locking"), releaseGoods)
|
||||||
|
router.HandleFunc(pat.Put("/api/goods/locking"), delLockedGoods)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/genofire/hs_master-kss-monolith/lib/database"
|
"github.com/genofire/hs_master-kss-monolith/lib/database"
|
||||||
lib "github.com/genofire/hs_master-kss-monolith/lib/http"
|
lib "github.com/genofire/hs_master-kss-monolith/lib/http"
|
||||||
|
@ -45,12 +46,12 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
tx := database.Write.Begin()
|
tx := database.Write.Begin()
|
||||||
|
defer tx.Rollback()
|
||||||
count := int64(0)
|
count := int64(0)
|
||||||
|
|
||||||
for _, good := range goods {
|
for _, good := range goods {
|
||||||
if good.ProductID <= 0 {
|
if good.ProductID <= 0 {
|
||||||
log.Warn("tried to log nothing")
|
log.Warn("tried to log nothing")
|
||||||
tx.Rollback()
|
|
||||||
http.Error(w, "tried to log nothing", http.StatusBadRequest)
|
http.Error(w, "tried to log nothing", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -59,7 +60,6 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
|
||||||
db := g.FilterAvailable(tx).First(g)
|
db := g.FilterAvailable(tx).First(g)
|
||||||
if db.RecordNotFound() {
|
if db.RecordNotFound() {
|
||||||
log.Warn("good not found")
|
log.Warn("good not found")
|
||||||
tx.Rollback()
|
|
||||||
http.Error(w, "the good was not found in database", http.StatusNotFound)
|
http.Error(w, "the good was not found in database", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,6 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if db.Error != nil || db.RowsAffected != 1 {
|
if db.Error != nil || db.RowsAffected != 1 {
|
||||||
http.Error(w, "the good was not found in database", http.StatusInternalServerError)
|
http.Error(w, "the good was not found in database", http.StatusInternalServerError)
|
||||||
tx.Rollback()
|
|
||||||
log.Panic("there is more than one good locked: ", db.Error)
|
log.Panic("there is more than one good locked: ", db.Error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -85,11 +84,71 @@ func lockGoods(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Function to release locked goods
|
// Function to release locked goods
|
||||||
func releaseGoods(w http.ResponseWriter, r *http.Request) {
|
func releaseGoods(w http.ResponseWriter, r *http.Request) {
|
||||||
|
log := logger.HTTP(r)
|
||||||
|
secret := r.Header.Get("secret")
|
||||||
|
if secret == "" {
|
||||||
|
log.Warn("no secred for locking given")
|
||||||
|
http.Error(w, "no secred for locking given", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log = log.WithField("lSecret", secret)
|
||||||
|
var goods []*LockGood
|
||||||
|
|
||||||
|
err := lib.Read(r, &goods)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Warn(err.Error())
|
||||||
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(goods) <= 0 {
|
||||||
|
log.Warn("tried to log nothing")
|
||||||
|
http.Error(w, "tried to log nothing", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := database.Write.Begin()
|
||||||
|
defer tx.Rollback()
|
||||||
|
for _, good := range goods {
|
||||||
|
if good.ProductID <= 0 {
|
||||||
|
log.Warn("tried to log nothing")
|
||||||
|
http.Error(w, "tried to release nothing", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < good.Count; i++ {
|
||||||
|
g := &models.Good{}
|
||||||
|
db := tx.Where(map[string]interface{}{"product_id": good.ProductID, "locked_secret": secret}).First(g)
|
||||||
|
if db.RecordNotFound() {
|
||||||
|
log.Warn("good not found")
|
||||||
|
http.Error(w, "the good was not found in database", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.Unlock(secret)
|
||||||
|
db = tx.Save(g)
|
||||||
|
|
||||||
|
if db.Error != nil || db.RowsAffected != 1 {
|
||||||
|
http.Error(w, "the good was not found in database", http.StatusInternalServerError)
|
||||||
|
log.Panic("there is more than one good released: ", db.Error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lib.Write(w, true)
|
||||||
|
tx.Commit()
|
||||||
|
log.Info("done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to del locked goods
|
||||||
|
func delLockedGoods(w http.ResponseWriter, r *http.Request) {
|
||||||
log := logger.HTTP(r)
|
log := logger.HTTP(r)
|
||||||
secret := r.Header.Get("secret")
|
secret := r.Header.Get("secret")
|
||||||
log = log.WithField("lSecret", secret)
|
log = log.WithField("lSecret", secret)
|
||||||
|
|
||||||
db := database.Write.Model(&models.Good{}).Where(&models.Good{LockedSecret: secret}).Updates(map[string]interface{}{"locked_secret": nil, "locked_at": nil})
|
db := database.Write.Model(&models.Good{}).Where(&models.Good{LockedSecret: secret}).Updates(map[string]interface{}{"deleted_at": time.Now(), "locked_secret": ""})
|
||||||
err := db.Error
|
err := db.Error
|
||||||
result := db.RowsAffected
|
result := db.RowsAffected
|
||||||
|
|
||||||
|
@ -101,7 +160,7 @@ func releaseGoods(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
if result <= 0 {
|
if result <= 0 {
|
||||||
log.Warn("no goods found")
|
log.Warn("no goods found")
|
||||||
http.Error(w, "there are no goods to release", http.StatusNotFound)
|
http.Error(w, "there are no goods to buy", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,54 @@ func TestLockGoods(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to test releaseGoods()
|
// Function to test releaseGoods()
|
||||||
func TestReleaseGoods(t *testing.T) {
|
func TestReleaseLockGoods(t *testing.T) {
|
||||||
|
assertion, router := test.Init(t)
|
||||||
|
good := &models.Good{
|
||||||
|
ProductID: 3,
|
||||||
|
Comment: "blabla",
|
||||||
|
LockedSecret: "hiddenLockTest",
|
||||||
|
}
|
||||||
|
database.Write.Create(good)
|
||||||
|
|
||||||
|
BindAPI(router)
|
||||||
|
session := test.NewSession(router)
|
||||||
|
|
||||||
|
_, w := session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 1}})
|
||||||
|
assertion.Equal(http.StatusBadRequest, w.StatusCode)
|
||||||
|
|
||||||
|
session.Header["secret"] = "hiddenLockTest"
|
||||||
|
|
||||||
|
_, w = session.JSONRequest("DELETE", "/api/goods/locking", 13)
|
||||||
|
assertion.Equal(http.StatusBadRequest, w.StatusCode)
|
||||||
|
|
||||||
|
_, w = session.JSONRequest("DELETE", "/api/goods/locking", nil)
|
||||||
|
assertion.Equal(http.StatusBadRequest, w.StatusCode)
|
||||||
|
|
||||||
|
_, w = session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 0, Count: 2}})
|
||||||
|
assertion.Equal(http.StatusBadRequest, w.StatusCode)
|
||||||
|
|
||||||
|
_, w = session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 2}})
|
||||||
|
assertion.Equal(http.StatusNotFound, w.StatusCode)
|
||||||
|
|
||||||
|
result, w := session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 1}})
|
||||||
|
assertion.Equal(http.StatusOK, w.StatusCode)
|
||||||
|
resultBool := result.(bool)
|
||||||
|
assertion.True(resultBool)
|
||||||
|
|
||||||
|
_, w = session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 1}})
|
||||||
|
assertion.Equal(http.StatusNotFound, w.StatusCode)
|
||||||
|
|
||||||
|
database.Close()
|
||||||
|
|
||||||
|
assertion.Panics(func() {
|
||||||
|
_, w = session.JSONRequest("DELETE", "/api/goods/locking", []interface{}{LockGood{ProductID: 3, Count: 1}})
|
||||||
|
assertion.Equal(http.StatusInternalServerError, w.StatusCode)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDelLockGoods(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
assertion, router := test.Init(t)
|
assertion, router := test.Init(t)
|
||||||
|
|
||||||
|
@ -75,18 +122,18 @@ func TestReleaseGoods(t *testing.T) {
|
||||||
session := test.NewSession(router)
|
session := test.NewSession(router)
|
||||||
|
|
||||||
session.Header["secret"] = "a"
|
session.Header["secret"] = "a"
|
||||||
result, w := session.JSONRequest("DELETE", "/api/goods/locking", nil)
|
result, w := session.JSONRequest("PUT", "/api/goods/locking", nil)
|
||||||
assertion.Equal(http.StatusNotFound, w.StatusCode)
|
assertion.Equal(http.StatusNotFound, w.StatusCode)
|
||||||
|
|
||||||
session.Header["secret"] = "hidden"
|
session.Header["secret"] = "hidden"
|
||||||
result, w = session.JSONRequest("DELETE", "/api/goods/locking", nil)
|
result, w = session.JSONRequest("PUT", "/api/goods/locking", nil)
|
||||||
assertion.Equal(http.StatusOK, w.StatusCode)
|
assertion.Equal(http.StatusOK, w.StatusCode)
|
||||||
resultMap := result.(map[string]interface{})
|
resultMap := result.(map[string]interface{})
|
||||||
count := resultMap["count"]
|
count := resultMap["count"]
|
||||||
assertion.Equal(float64(1), count)
|
assertion.Equal(float64(1), count)
|
||||||
|
|
||||||
database.Close()
|
database.Close()
|
||||||
result, w = session.JSONRequest("DELETE", "/api/goods/locking", nil)
|
result, w = session.JSONRequest("PUT", "/api/goods/locking", nil)
|
||||||
assertion.Equal(http.StatusInternalServerError, w.StatusCode)
|
assertion.Equal(http.StatusInternalServerError, w.StatusCode)
|
||||||
|
|
||||||
test.Close()
|
test.Close()
|
||||||
|
|
|
@ -16,6 +16,9 @@ var GoodFreshnessTemplate string
|
||||||
|
|
||||||
// Function to calculate a percent value from a given value and a maximum value
|
// Function to calculate a percent value from a given value and a maximum value
|
||||||
func tempPercent(value, max int) int {
|
func tempPercent(value, max int) int {
|
||||||
|
if value >= max {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
return value * 100 / max
|
return value * 100 / max
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,9 @@ func TestTempFuncs(t *testing.T) {
|
||||||
resultInt := tempPercent(3, 9)
|
resultInt := tempPercent(3, 9)
|
||||||
assert.Equal(33, resultInt)
|
assert.Equal(33, resultInt)
|
||||||
|
|
||||||
|
resultInt = tempPercent(13, 9)
|
||||||
|
assert.Equal(100, resultInt)
|
||||||
|
|
||||||
// TODO is there a other way to calc this?
|
// TODO is there a other way to calc this?
|
||||||
resultFloat := tempProcessRadius(3, 9, 0)
|
resultFloat := tempProcessRadius(3, 9, 0)
|
||||||
assert.Equal(float64(0), resultFloat)
|
assert.Equal(float64(0), resultFloat)
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
<div class="default text">Select Product</div>
|
<div class="default text">Select Product</div>
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
<div class="item" ng-repeat="item in products" data-value="{{item.id}}"><img class="icon"
|
<div class="item" ng-repeat="item in products" data-value="{{item.id}}"><img class="icon"
|
||||||
ng-src="{{'/api/good/availablity/'+item.id| reloadSrc}}"/>{{item.title}}
|
ng-src="{{'/api/good/availablity/'+item.id| reloadSrc}}"/>{{item.name}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,13 +55,14 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-repeat="item in cart">
|
<tr ng-repeat="item in cart">
|
||||||
<td>{{item.count}}</td>
|
<td>{{item.count}}</td>
|
||||||
<td>{{getProduct(item.product_id).title}}</td>
|
<td>{{getProduct(item.product_id).name}}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="ui button icon" ng-click="del(item)"><i class="icon trash"></i></div>
|
<div class="ui button icon" ng-click="del(item)"><i class="icon trash"></i></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<button class="ui icon button" tabindex="0" ng-click="buy()"><i class="icon shop"></i>Buy</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="ui vertical footer segment">
|
<footer class="ui vertical footer segment">
|
||||||
|
@ -81,7 +82,8 @@
|
||||||
'products': '/api-test/product/',
|
'products': '/api-test/product/',
|
||||||
'productById': '/api-test/product/%d/',
|
'productById': '/api-test/product/%d/',
|
||||||
'lockGoods': '/api/goods/locking',
|
'lockGoods': '/api/goods/locking',
|
||||||
'unlockGoods': '/api/goods/locking'
|
'unlockGoods': '/api/goods/locking',
|
||||||
|
'buyGoods': '/api/goods/locking',
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -127,8 +129,9 @@
|
||||||
})
|
})
|
||||||
.controller('MainCtrl',['$scope', '$http',function($scope, $http) {
|
.controller('MainCtrl',['$scope', '$http',function($scope, $http) {
|
||||||
$scope.products = [];
|
$scope.products = [];
|
||||||
$scope.goods = {count:1};
|
$scope.goods = {count: 1};
|
||||||
$scope.cart = [];
|
$scope.cart = [];
|
||||||
|
let secret = createUUID();
|
||||||
|
|
||||||
$http.get(config.microservice_dependencies.products).then(function(res) {
|
$http.get(config.microservice_dependencies.products).then(function(res) {
|
||||||
$scope.products = res.data;
|
$scope.products = res.data;
|
||||||
|
@ -136,19 +139,24 @@
|
||||||
|
|
||||||
function load() {
|
function load() {
|
||||||
const tmp = JSON.parse(localStorage.getItem("cart"));
|
const tmp = JSON.parse(localStorage.getItem("cart"));
|
||||||
|
const secretTmp = JSON.parse(localStorage.getItem("cart"));
|
||||||
if(tmp) {
|
if(tmp) {
|
||||||
$scope.cart = tmp.map((item) => {
|
$scope.cart = tmp.map((item) => {
|
||||||
delete item.$$hashKey
|
delete item.$$hashKey
|
||||||
return item;
|
return item;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if(secretTmp && secretTmp === "") {
|
||||||
|
secret = secretTmp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function save(){
|
function save() {
|
||||||
localStorage.setItem("cart",JSON.stringify($scope.cart));
|
localStorage.setItem("cart", JSON.stringify($scope.cart));
|
||||||
|
localStorage.setItem("secret", secret);
|
||||||
}
|
}
|
||||||
load();
|
load();
|
||||||
|
|
||||||
$scope.getProduct = function getProduct(id){
|
$scope.getProduct = function getProduct(id) {
|
||||||
if(id) {
|
if(id) {
|
||||||
return $scope.products.filter((item) => item.id === id)[0];
|
return $scope.products.filter((item) => item.id === id)[0];
|
||||||
}
|
}
|
||||||
|
@ -156,7 +164,6 @@
|
||||||
|
|
||||||
$scope.add = function add() {
|
$scope.add = function add() {
|
||||||
$scope.goods.product_id = selectedProduct;
|
$scope.goods.product_id = selectedProduct;
|
||||||
const secret = createUUID();
|
|
||||||
$http({
|
$http({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -166,10 +173,9 @@
|
||||||
data: [$scope.goods]
|
data: [$scope.goods]
|
||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
console.log('add', $scope.goods);
|
console.log('add', $scope.goods);
|
||||||
$scope.goods.secret = secret;
|
|
||||||
$scope.cart.push($scope.goods);
|
$scope.cart.push($scope.goods);
|
||||||
save();
|
save();
|
||||||
$scope.goods = {count:1};
|
$scope.goods = {count: 1};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -177,9 +183,11 @@
|
||||||
$http({
|
$http({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: {
|
headers: {
|
||||||
'secret': entry.secret
|
'secret': secret,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
url: config.microservice_dependencies.unlockGoods
|
url: config.microservice_dependencies.unlockGoods,
|
||||||
|
data: [entry]
|
||||||
}).then(function(res) {
|
}).then(function(res) {
|
||||||
console.log('del', entry);
|
console.log('del', entry);
|
||||||
$scope.cart = $scope.cart.filter((item) => item !== entry);
|
$scope.cart = $scope.cart.filter((item) => item !== entry);
|
||||||
|
@ -187,9 +195,25 @@
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.buy = function buy() {
|
||||||
|
$http({
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'secret': secret
|
||||||
|
},
|
||||||
|
url: config.microservice_dependencies.buyGoods,
|
||||||
|
}).then(function(res) {
|
||||||
|
console.log('buy');
|
||||||
|
$scope.reset();
|
||||||
|
},function(err) {
|
||||||
|
console.warn('buy', err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
$scope.reset = function reset() {
|
$scope.reset = function reset() {
|
||||||
console.log("reset");
|
console.log("reset");
|
||||||
localStorage.setItem("cart","[]");
|
localStorage.setItem("cart", "[]");
|
||||||
|
localStorage.setItem("secret", createUUID());
|
||||||
load();
|
load();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Reference in New Issue