Browse Source

add update function

sainw 2 years ago
parent
commit
779511ce3b
12 changed files with 326 additions and 51 deletions
  1. 1 0
      .gitignore
  2. 21 2
      client/client.go
  3. 29 5
      client/listener.go
  4. 98 5
      client/model.go
  5. 25 0
      client/query.go
  6. 14 0
      client/update.go
  7. 40 10
      client/util.go
  8. 6 3
      example/go.mod
  9. 2 4
      example/go.sum
  10. 87 19
      example/main.go
  11. 1 1
      go.mod
  12. 2 2
      go.sum

+ 1 - 0
.gitignore

@@ -2,3 +2,4 @@ fho_proto/*.go
 fho_forward_sample/main
 example/example
 example/__debug_bin
+example/main

+ 21 - 2
client/client.go

@@ -37,7 +37,7 @@ func NewClient(address string, id string, key string) (*FhoClient, error) {
 }
 
 func (f *FhoClient) query(object string, updatedAfter time.Time) ([]map[string]interface{}, error) {
-	md := metadata.Pairs("id", f.id, "key", f.key)
+	md := metadata.Pairs("id", f.id, "key", f.key, "object", object)
 	ctx := metadata.NewOutgoingContext(context.Background(), md)
 
 	r, err := f.dataService.GetData(ctx, &pb.DataRequest{Object: object, UpdatedAfter: timestamppb.New(updatedAfter)})
@@ -73,7 +73,7 @@ func (c *FhoClient) QuerySnapshot(object string, updatedAfter time.Time) (*Snaps
 }
 
 func (s *Snapshot) assignStream() error {
-	md := metadata.Pairs("id", s.client.id, "key", s.client.key)
+	md := metadata.Pairs("id", s.client.id, "key", s.client.key, "object", s.object)
 	ctx := metadata.NewOutgoingContext(context.Background(), md)
 
 	stream, err := s.client.dataService.StreamData(ctx, &pb.DataRequest{Object: s.object, UpdatedAfter: timestamppb.New(s.updatedAfter)}, grpc_retry.WithMax(30))
@@ -122,3 +122,22 @@ start:
 	s.updatedAfter = r.LatestUpdatedTime.AsTime()
 	return &rec, nil
 }
+
+func (f *FhoClient) update(object string, record interface{}) error {
+	md := metadata.Pairs("id", f.id, "key", f.key, "object", object)
+	ctx := metadata.NewOutgoingContext(context.Background(), md)
+
+	bytes, err := json.Marshal(record)
+	if err != nil {
+		return err
+	}
+
+	r, err := f.dataService.UpdateData(ctx, &pb.DataRequest{Object: object, Data: string(bytes)})
+	if err != nil {
+		return err
+	}
+	if r.ReplyStatus == pb.DataReply_ERROR {
+		return errors.New(r.ErrorMsg)
+	}
+	return nil
+}

+ 29 - 5
client/listener.go

@@ -6,7 +6,7 @@ import (
 	"time"
 )
 
-func listen(c *FhoClient, object string, lastUpdatedTime time.Time, fn func(*[]map[string]interface{})) error {
+func listen(c *FhoClient, object string, lastUpdatedTime time.Time, fn func(*[]map[string]interface{}) error) error {
 	snap, err := c.QuerySnapshot(object, lastUpdatedTime)
 	if err != nil {
 		log.Println("listen error:", err.Error())
@@ -22,19 +22,43 @@ func listen(c *FhoClient, object string, lastUpdatedTime time.Time, fn func(*[]m
 			log.Println("Error:", err.Error())
 			break
 		}
-		fn(data)
+		return fn(data)
 	}
 	return nil
 }
 
-func ListenOnProducts(c *FhoClient, lastUpdatedTime time.Time, fn func([]*Product)) error {
-	listen(c, "products", lastUpdatedTime, func(data *[]map[string]interface{}) {
+func ListenOnProducts(c *FhoClient, lastUpdatedTime time.Time, fn func([]*Product) error) error {
+	listen(c, "products", lastUpdatedTime, func(data *[]map[string]interface{}) error {
 		var products []*Product
 		for _, rec := range *data {
 			_product := ProductFromMap(rec)
 			products = append(products, _product)
 		}
-		fn(products)
+		return fn(products)
 	})
 	return nil
 }
+
+func ListenOnPrice(c *FhoClient, lastUpdatedTime time.Time, fn func([]*PriceSchedule) error) error {
+	err := listen(c, "station_price_updates", lastUpdatedTime, func(data *[]map[string]interface{}) error {
+		var prices []*PriceSchedule
+		pricesMap := make(map[string]*PriceSchedule)
+		for _, rec := range *data {
+
+			_price, err := PriceScheduleFromMap(rec)
+			if err != nil {
+				return err
+			}
+			if _, ok := pricesMap[_price.ID]; ok {
+				_price = pricesMap[_price.ID]
+			} else {
+				pricesMap[_price.ID] = _price
+				prices = append(prices, _price)
+			}
+			_product := PriceScheduleProductFromMap(rec)
+			_price.PriceScheduleProducts = append(_price.PriceScheduleProducts, _product)
+		}
+		return fn(prices)
+	})
+	return err
+}

+ 98 - 5
client/model.go

@@ -5,10 +5,19 @@ import (
 )
 
 type Model struct {
-	ID                string
-	IsCreated         bool
-	IsDeleted         bool
-	IsUpdated         bool
+	// object unique id
+	ID string
+
+	// created event
+	IsCreated bool
+
+	// deleted event
+	IsDeleted bool
+
+	// updated event
+	IsUpdated bool
+
+	// updated time of this object
 	LatestUpdatedTime *time.Time
 }
 
@@ -34,7 +43,9 @@ func modelFromMap(m map[string]interface{}) *Model {
 
 type Product struct {
 	Model
-	Name  string
+	// product name
+	Name string
+	// product color
 	Color string
 }
 
@@ -43,3 +54,85 @@ func ProductFromMap(m map[string]interface{}) *Product {
 		Name:  ToString(m["name"]),
 		Color: ToString(m["color"])}
 }
+
+type PriceSchedule struct {
+	Model
+
+	// date
+	Date time.Time
+
+	// after shift change
+	AfterShiftChange bool
+
+	// product list
+	PriceScheduleProducts []*PriceScheduleProduct
+}
+
+func PriceScheduleFromMap(m map[string]interface{}) (*PriceSchedule, error) {
+	date, err := ToTimeFromISO(m["iso_date"])
+	if err != nil {
+		return nil, err
+	}
+	return &PriceSchedule{Model: *modelFromMap(m),
+		Date:             *date,
+		AfterShiftChange: ToBool(m["after_shift_change"])}, nil
+}
+
+type PriceScheduleProduct struct {
+	// product id
+	ProductID string
+
+	// product name
+	Name string
+
+	// product color
+	Color string
+
+	// product price
+	Price float32
+}
+
+func PriceScheduleProductFromMap(m map[string]interface{}) *PriceScheduleProduct {
+	return &PriceScheduleProduct{
+		ProductID: ToString(m["product_id"]),
+		Name:      ToString(m["name"]),
+		Color:     ToString(m["color"]),
+		Price:     ToFloat32(m["price"]),
+	}
+}
+
+type PriceChangeHistory struct {
+	// object unique id of local server
+	ID string `json:"id"`
+
+	// original schedule id from FHO
+	//
+	// omit this id, if price change is done by local server
+	PriceScheduleID string `json:"price_schedule_id"`
+
+	// price change time in DATETIME_FROM_ISO_FORMAT
+	PriceChangeTime string `json:"price_change_time"`
+
+	// price change status
+	Status string `json:"status"`
+
+	// products of price change history
+	PriceChangeHistoryProducts []*PriceChangeHistoryProduct `json:"products"`
+}
+
+type PriceChangeHistoryProduct struct {
+	// local database row id
+	ID string `json:"id"`
+
+	// product id
+	ProductID string `json:"product_id"`
+
+	// product name
+	Name string `json:"name"`
+
+	// product color
+	Color string `json:"color"`
+
+	// product price
+	Price float32 `json:"price"`
+}

+ 25 - 0
client/query.go

@@ -5,6 +5,31 @@ import (
 	"time"
 )
 
+func QueryPrice(client *FhoClient, lastUpdatedTime time.Time) ([]*PriceSchedule, error) {
+	data, err := client.query("station_price_updates", lastUpdatedTime)
+	if err != nil {
+		fmt.Println("query error:", err.Error())
+		return nil, err
+	}
+	var prices []*PriceSchedule
+	pricesMap := make(map[string]*PriceSchedule)
+	for _, rec := range data {
+		_price, err := PriceScheduleFromMap(rec)
+		if err != nil {
+			return nil, err
+		}
+		if _, ok := pricesMap[_price.ID]; ok {
+			_price = pricesMap[_price.ID]
+		} else {
+			pricesMap[_price.ID] = _price
+			prices = append(prices, _price)
+		}
+		_product := PriceScheduleProductFromMap(rec)
+		_price.PriceScheduleProducts = append(_price.PriceScheduleProducts, _product)
+	}
+	return prices, nil
+}
+
 func QueryProducts(client *FhoClient, lastUpdatedTime time.Time) ([]*Product, error) {
 	data, err := client.query("products", lastUpdatedTime)
 	if err != nil {

+ 14 - 0
client/update.go

@@ -0,0 +1,14 @@
+package client
+
+import (
+	"fmt"
+)
+
+func UpdatePriceHisotry(client *FhoClient, price *PriceChangeHistory) error {
+	err := client.update("station_price_updates", price)
+	if err != nil {
+		fmt.Println("update error:", err.Error())
+		return err
+	}
+	return nil
+}

+ 40 - 10
client/util.go

@@ -7,8 +7,8 @@ import (
 
 func ToStrings(data interface{}) []string {
 	var i []string
-	if vv, ok := data.([]interface{}); ok {
-		for _, in := range vv {
+	if v, ok := data.([]interface{}); ok {
+		for _, in := range v {
 			i = append(i, fmt.Sprint(in))
 		}
 	}
@@ -23,16 +23,13 @@ func ToString(data interface{}) string {
 }
 
 func ToFloat64(data interface{}) float64 {
-	var i float64
-	if vv, ok := data.(float64); ok {
-		i = vv
-		return i
+	if v, ok := data.(float64); ok {
+		return v
 	}
-	if vv, ok := data.(int64); ok {
-		i = float64(vv)
-		return i
+	if v, ok := data.(int64); ok {
+		return float64(v)
 	}
-	return i
+	return 0
 }
 
 func ToFloat32(data interface{}) float32 {
@@ -48,9 +45,33 @@ func ToFloat32(data interface{}) float32 {
 	return 0
 }
 
+func ToInt(data interface{}) int {
+	if v, ok := data.(int); ok {
+		return v
+	}
+	if v, ok := data.(int64); ok {
+		return int(v)
+	}
+	if v, ok := data.(float64); ok {
+		return int(v)
+	}
+	if v, ok := data.(float32); ok {
+		return int(v)
+	}
+	return 0
+}
+
+func ToBool(data interface{}) bool {
+	if v, ok := data.(bool); ok {
+		return v
+	}
+	return ToInt(data) == 1
+}
+
 const (
 	GRPC_DATETIME_FROM_SQL_FORMAT  = "2006-01-02T15:04:05.999999999-07:00"
 	GRPC_DATETIME_FROM_SQL_FORMAT2 = "2006-01-02T15:04:05.999999999Z"
+	DATETIME_FROM_ISO_FORMAT       = "2006-01-02T15:04:05.999-07:00"
 )
 
 func ToTime(data interface{}) (*time.Time, error) {
@@ -67,3 +88,12 @@ func ToTime(data interface{}) (*time.Time, error) {
 	}
 	return &dateTime, nil
 }
+
+func ToTimeFromISO(data interface{}) (*time.Time, error) {
+	str := ToString(data)
+	dateTime, err := time.Parse(DATETIME_FROM_ISO_FORMAT, str)
+	if err != nil {
+		return nil, err
+	}
+	return &dateTime, nil
+}

+ 6 - 3
example/go.mod

@@ -1,11 +1,14 @@
-module example
+module main
 
 go 1.17
 
-require git.mokkon.com/sainw/fho_forward.git v0.1.3
+// require git.mokkon.com/sainw/fho_forward.git v0.0.0
+replace git.mokkon.com/sainw/fho_forward.git => ../
+
+require git.mokkon.com/sainw/fho_forward.git v0.0.0-00010101000000-000000000000
 
 require (
-	git.mokkon.com/sainw/fho_forward_proto.git v0.1.2 // indirect
+	git.mokkon.com/sainw/fho_forward_proto.git v0.1.3 // indirect
 	github.com/golang/protobuf v1.5.0 // indirect
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
 	golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect

+ 2 - 4
example/go.sum

@@ -1,9 +1,7 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-git.mokkon.com/sainw/fho_forward.git v0.1.3 h1:l1HDuvoG1g2Ae/cBak5cYxaTVnJnWg8saopcFfUNwuM=
-git.mokkon.com/sainw/fho_forward.git v0.1.3/go.mod h1:7pY0EoEy5feVu8OdKlXrsWMZOaYtKG3fjN2GbfWBSpk=
-git.mokkon.com/sainw/fho_forward_proto.git v0.1.2 h1:6zLKZ1dojX/xa9BY+sUw2ZN81vHxe30QlSF7p0KQKcw=
-git.mokkon.com/sainw/fho_forward_proto.git v0.1.2/go.mod h1:3LuRfpY0mW5ZnDO7DxK34wAWmGZw9UX5B06h3skFRAc=
+git.mokkon.com/sainw/fho_forward_proto.git v0.1.3 h1:yn5e0kxpsNVnDOjFnN0floiM9sbt39P8A1nXSgfyCuw=
+git.mokkon.com/sainw/fho_forward_proto.git v0.1.3/go.mod h1:3LuRfpY0mW5ZnDO7DxK34wAWmGZw9UX5B06h3skFRAc=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=

+ 87 - 19
example/main.go

@@ -9,30 +9,65 @@ import (
 )
 
 const (
-	address = "fhogrpc-test.seinmaungengineering.com:7091"
-	// address = "localhost:7090"
+	// address = "fhogrpc-test.seinmaungengineering.com:7091"
+	address = "localhost:7090"
 )
 
-type RegionProduct struct {
-	ProductID string
-	Name      string
-	Color     string
-	Variant   float32
+func main() {
+	// client, err := fho.NewClient(address, "da910506-4bdc-4dc2-9481-58a4e3e3c8bf", "1adf4633-7e9e-4bd0-b512-7b087d9c9719")
+	client, err := fho.NewClient(address, "bd38e9ac-210a-4e85-865c-401270c07220", "8c95f7fa-3071-427d-a27a-6f96e1d9590e")
+	if err != nil {
+		panic(err)
+	}
+	// go listenProducts(client)
+	// go listenPrices(client)
+	// go query(client)
+	// update(client)
+	updateFromFHO(client)
+
+	select {}
 }
 
-func RegionProductFromMap(m map[string]interface{}) *RegionProduct {
-	return &RegionProduct{ProductID: fho.ToString(m["product_id"]),
-		Name:    fho.ToString(m["name"]),
-		Color:   fho.ToString(m["color"]),
-		Variant: fho.ToFloat32(m["variant"])}
+func update(client *fho.FhoClient) {
+	products := make([]*fho.PriceChangeHistoryProduct, 0)
+	products = append(products, &fho.PriceChangeHistoryProduct{ID: "127", ProductID: "id0001", Name: "92", Color: "2334444", Price: 1222})
+	products = append(products, &fho.PriceChangeHistoryProduct{ID: "128", ProductID: "id0002", Name: "95", Color: "2989834", Price: 1333})
+	products = append(products, &fho.PriceChangeHistoryProduct{ID: "129", ProductID: "id0003", Name: "Diesel", Color: "2989834", Price: 1444})
+	products = append(products, &fho.PriceChangeHistoryProduct{ID: "120", ProductID: "id0004", Name: "Premium", Color: "2989834", Price: 1555})
+	t := time.Now()
+	history := fho.PriceChangeHistory{ID: "1111113", PriceChangeTime: t.Format(fho.DATETIME_FROM_ISO_FORMAT),
+		Status: "Successful", PriceChangeHistoryProducts: products}
+
+	err := fho.UpdatePriceHisotry(client, &history)
+	if err != nil {
+		log.Println("Error query:", err.Error())
+	}
 }
-func main() {
-	client, err := fho.NewClient(address, "da910506-4bdc-4dc2-9481-58a4e3e3c8bf", "1adf4633-7e9e-4bd0-b512-7b087d9c9719")
-	// client, err := fho.NewClient(address, "bd38e9ac-210a-4e85-865c-401270c07220", "8c95f7fa-3071-427d-a27a-6f96e1d9590e")
+
+func updateFromFHO(client *fho.FhoClient) error {
+	prices, err := fho.QueryPrice(client, time.Now().Add(time.Hour*-114))
 	if err != nil {
-		panic(err)
+		log.Println("Error query:", err.Error())
+		return err
+	}
+	for _, price := range prices {
+		fmt.Println("Query=====>")
+		printPrice(price)
 	}
-	go listenProducts(client)
+	_price := prices[0]
+
+	t := time.Now()
+	history := fho.PriceChangeHistory{ID: "1111114", PriceChangeTime: t.Format(fho.DATETIME_FROM_ISO_FORMAT),
+		PriceScheduleID: _price.ID, Status: "Successful"}
+
+	err = fho.UpdatePriceHisotry(client, &history)
+	if err != nil {
+		log.Println("Error updatePriceHisotry:", err.Error())
+	}
+	return nil
+}
+
+func query(client *fho.FhoClient) {
 	products, err := fho.QueryProducts(client, time.Now().Add(time.Hour*-114))
 	if err != nil {
 		log.Println("Error query:", err.Error())
@@ -41,16 +76,30 @@ func main() {
 		fmt.Println("Query=====>")
 		printProduct(product)
 	}
-	select {}
 }
 
 func listenProducts(client *fho.FhoClient) error {
-	err := fho.ListenOnProducts(client, time.Now().Add(time.Hour*-114), func(products []*fho.Product) {
+	err := fho.ListenOnProducts(client, time.Now().Add(time.Hour*-114), func(products []*fho.Product) error {
 		for _, p := range products {
 			fmt.Println("Listening=====>")
 			printProduct(p)
 		}
+		return nil
+	})
+	return err
+}
+
+func listenPrices(client *fho.FhoClient) error {
+	err := fho.ListenOnPrice(client, time.Now().Add(time.Hour*-114), func(prices []*fho.PriceSchedule) error {
+		for _, p := range prices {
+			fmt.Println("Listening=====>")
+			printPrice(p)
+		}
+		return nil
 	})
+	if err != nil {
+		log.Println("error listenPrices:", err.Error())
+	}
 	return err
 }
 
@@ -64,3 +113,22 @@ func printProduct(p *fho.Product) {
 	fmt.Printf("IsDeleted : %v\n", p.IsDeleted)
 	fmt.Printf("LatestUpdatedTime : %v\n", p.LatestUpdatedTime)
 }
+
+func printPrice(p *fho.PriceSchedule) {
+	fmt.Printf("ID : %v\n", p.ID)
+	fmt.Printf("Date : %v\n", p.Date)
+	fmt.Printf("AfterShiftChange : %v\n", p.AfterShiftChange)
+
+	fmt.Printf("IsCreated : %v\n", p.IsCreated)
+	fmt.Printf("IsUpdated : %v\n", p.IsUpdated)
+	fmt.Printf("IsDeleted : %v\n", p.IsDeleted)
+	fmt.Printf("LatestUpdatedTime : %v\n", p.LatestUpdatedTime)
+
+	fmt.Printf("Products -------------------\n")
+	for _, prd := range p.PriceScheduleProducts {
+		fmt.Printf("prd.ProductID : %v\n", prd.ProductID)
+		fmt.Printf("prd.Name : %v\n", prd.Name)
+		fmt.Printf("prd.Color : %v\n", prd.Color)
+		fmt.Printf("prd.Price : %v\n", prd.Price)
+	}
+}

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module git.mokkon.com/sainw/fho_forward.git
 go 1.17
 
 require (
-	git.mokkon.com/sainw/fho_forward_proto.git v0.1.2
+	git.mokkon.com/sainw/fho_forward_proto.git v0.1.3
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
 	google.golang.org/grpc v1.40.0
 	google.golang.org/protobuf v1.27.1

+ 2 - 2
go.sum

@@ -1,7 +1,7 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-git.mokkon.com/sainw/fho_forward_proto.git v0.1.2 h1:6zLKZ1dojX/xa9BY+sUw2ZN81vHxe30QlSF7p0KQKcw=
-git.mokkon.com/sainw/fho_forward_proto.git v0.1.2/go.mod h1:3LuRfpY0mW5ZnDO7DxK34wAWmGZw9UX5B06h3skFRAc=
+git.mokkon.com/sainw/fho_forward_proto.git v0.1.3 h1:yn5e0kxpsNVnDOjFnN0floiM9sbt39P8A1nXSgfyCuw=
+git.mokkon.com/sainw/fho_forward_proto.git v0.1.3/go.mod h1:3LuRfpY0mW5ZnDO7DxK34wAWmGZw9UX5B06h3skFRAc=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=