Parcourir la source

Add product locale support and order caching

Your Name il y a 3 semaines
Parent
commit
58c60f7327

+ 109 - 24
go/gopath/src/fohow.com/apps/controllers/order_controller/order_controller.go

@@ -6,6 +6,7 @@ import (
 	"fohow.com/apps/helpers"
 	"fohow.com/apps/models/cent_model"
 	"fohow.com/apps/models/sys_config"
+	"fohow.com/cache"
 	"fohow.com/libs/lib_redis"
 	// "fmt"
 	"strconv"
@@ -31,6 +32,25 @@ var MultreateOrder sync.Mutex
 var MultShopOrder sync.Mutex
 var MultCentOrder sync.Mutex
 
+type orderListCacheResult struct {
+	List      []*order_model.Order `json:"list"`
+	ListCount int64                `json:"list_count"`
+}
+
+func (self *orderListCacheResult) Clone() *orderListCacheResult {
+	if self == nil {
+		return nil
+	}
+	return &orderListCacheResult{
+		List:      order_model.CloneOrderList(self.List),
+		ListCount: self.ListCount,
+	}
+}
+
+func buildOrderListCacheKey(prefix string, wxUserId int64, status string, page, perPage int, lang string) string {
+	return order_model.OrderCacheKey(fmt.Sprintf("%s.wxUserId(%d).status(%s).page(%d).perPage(%d).lang(%s)", prefix, wxUserId, status, page, perPage, lang))
+}
+
 // 单品下单
 func (self *OrderController) Create() {
 	createOrder.Lock()
@@ -521,10 +541,40 @@ func (self *OrderController) MultCentCreate() {
 	self.ServeJSON()
 }
 
+func loadOrderProductContext(orders []*order_model.Order, lang string, useCache bool) (map[string][]*order_model.OrderDetail, map[int64]*product_model.OrderProductLite) {
+	orderIds := make([]string, 0, len(orders))
+	for _, order := range orders {
+		orderIds = append(orderIds, order.OrderId)
+	}
+	detailMap := order_model.GetAllDetailsByOrderIds(orderIds, useCache)
+	productIDs := make([]int64, 0)
+	for _, order := range orders {
+		orderDtList := detailMap[order.OrderId]
+		for _, item := range orderDtList {
+			productIDs = append(productIDs, item.ProductId)
+		}
+	}
+	return detailMap, product_model.GetOrderProductsByIds(productIDs, lang, useCache)
+}
+
+func loadOrderPackageContext(detailMap map[string][]*order_model.OrderDetail, productMap map[int64]*product_model.OrderProductLite, lang string, useCache bool) map[int64][]*order_model.ProductItem {
+	orderDtNums := make(map[int64]int64)
+	for _, orderDtList := range detailMap {
+		for _, item := range orderDtList {
+			product := productMap[item.ProductId]
+			if product != nil && product.Package {
+				orderDtNums[item.Id] = item.Count
+			}
+		}
+	}
+	return order_model.GetOrderDetailPackagesBatch(orderDtNums, lang, useCache)
+}
+
 // 获取用户订单详情
 func (self *OrderController) Detail() {
 
-	cache, _ := self.GetBool("cache", false)
+	useCache, _ := self.GetBool("cache", true)
+	lang := product_model.NormalizeLanguage(self.GetString("lang"))
 	oId := self.Ctx.Input.Param(":order_id")
 	o := order_model.GetOrderById(oId, false)
 	//beego.BeeLogger.Warn("order =[%v]", o)
@@ -532,9 +582,15 @@ func (self *OrderController) Detail() {
 		beego.BeeLogger.Error("order not exist id=[%s]", oId)
 		self.ReturnError(404, apps.OrderNotExist, "", nil)
 	}
-	orderDtList := order_model.GetAllDetailsOrderId(o.OrderId, false)
+	orderDtList := order_model.GetAllDetailsOrderId(o.OrderId, useCache)
+	productIDs := make([]int64, 0, len(orderDtList))
+	for _, item := range orderDtList {
+		productIDs = append(productIDs, item.ProductId)
+	}
+	productMap := product_model.GetOrderProductsByIds(productIDs, lang, useCache)
+	packageMap := loadOrderPackageContext(map[string][]*order_model.OrderDetail{o.OrderId: orderDtList}, productMap, lang, useCache)
 	for _, item := range orderDtList {
-		product := product_model.GetProductById(item.ProductId, cache)
+		product := productMap[item.ProductId]
 		if product == nil {
 			self.ReturnError(403, apps.ProductNotExist, "", nil)
 		}
@@ -542,16 +598,17 @@ func (self *OrderController) Detail() {
 		if item.Commend || o.Status != order_model.STATUS_COMPLETE {
 			item.Commend = true
 		}
+		item.ProductName = product.Name
 		item.Package = product.Package
 		if product.Package {
-			item.PacakageList = order_model.GetOrderDetailPackages(item.Id, item.Count, true)
+			item.PacakageList = packageMap[item.Id]
 		}
 		o.Count += item.Count
 		item.Cover = product.Cover
 		item.UseQuan = product.UseQuan
 		o.ProductList = append(o.ProductList, item)
 	}
-	wxUser := self.GetCurrentWxUser(cache)
+	wxUser := self.GetCurrentWxUser(useCache)
 	if wxUser == nil {
 		self.ReturnError(403, apps.UserNotExist, "", nil)
 	} else {
@@ -586,20 +643,32 @@ func (self *OrderController) WaitCommendList() {
 		page = 1
 	}
 	perPage := 100
-	cache, _ := self.GetBool("cache", false)
+	useCache, _ := self.GetBool("cache", true)
+	lang := product_model.NormalizeLanguage(self.GetString("lang"))
 	//uId := self.GetCurrentUserId()
 	wxUId := self.GetCurrentWxUserIdByToken()
+	cacheKey := buildOrderListCacheKey("order_controller.WaitCommendList", wxUId, status, page, perPage, lang)
+	if useCache {
+		if cached, ok := cache.Cache.Get(cacheKey).(*orderListCacheResult); ok && cached != nil {
+			self.Data["json"] = cached.Clone()
+			self.ServeJSON()
+			return
+		}
+	}
 	orders := order_model.GetUserOrders(wxUId, status, page, perPage)
 	count := order_model.GetUserOrdersCount(wxUId, status)
+	orderDetailMap, productMap := loadOrderProductContext(orders, lang, useCache)
+	packageMap := loadOrderPackageContext(orderDetailMap, productMap, lang, useCache)
 	var latestOrders []*order_model.Order
 	for _, item := range orders {
 		commend := false
-		orderDtList := order_model.GetAllDetailsOrderId(item.OrderId, false)
+		orderDtList := orderDetailMap[item.OrderId]
 		for _, orderDtItem := range orderDtList {
-			product := product_model.GetProductById(orderDtItem.ProductId, cache)
+			product := productMap[orderDtItem.ProductId]
 			if product == nil {
 				continue
 			}
+			orderDtItem.ProductName = product.Name
 			orderDtItem.Package = product.Package
 			item.Count += orderDtItem.Count
 			if orderDtItem.Commend || item.Status != order_model.STATUS_COMPLETE {
@@ -609,9 +678,10 @@ func (self *OrderController) WaitCommendList() {
 				commend = true
 			}
 			if product.Package {
-				orderDtItem.PacakageList = order_model.GetOrderDetailPackages(orderDtItem.Id, orderDtItem.Count, true)
+				orderDtItem.PacakageList = packageMap[orderDtItem.Id]
 			}
 			orderDtItem.Cover = product.Cover
+			orderDtItem.UseQuan = product.UseQuan
 			if !orderDtItem.Commend {
 				item.ProductList = append(item.ProductList, orderDtItem)
 			}
@@ -622,11 +692,11 @@ func (self *OrderController) WaitCommendList() {
 			count = count - 1
 		}
 	}
-	type Ret struct {
-		List      []*order_model.Order `json:"list"`
-		ListCount int64                `json:"list_count"`
+	ret := &orderListCacheResult{ListCount: count, List: latestOrders}
+	if useCache {
+		cache.Cache.Put(cacheKey, ret, 5*time.Minute)
 	}
-	self.Data["json"] = &Ret{ListCount: count, List: latestOrders}
+	self.Data["json"] = ret.Clone()
 	self.ServeJSON()
 }
 
@@ -649,30 +719,43 @@ func (self *OrderController) List() {
 	} else {
 		order_status = status
 	}
-	cache, _ := self.GetBool("cache", false)
+	useCache, _ := self.GetBool("cache", true)
+	lang := product_model.NormalizeLanguage(self.GetString("lang"))
 	//uId := self.GetCurrentUserId()
 	wxUId := self.GetCurrentWxUserIdByToken()
+	cacheKey := buildOrderListCacheKey("order_controller.List", wxUId, status, page, perPage, lang)
+	if useCache {
+		if cached, ok := cache.Cache.Get(cacheKey).(*orderListCacheResult); ok && cached != nil {
+			self.Data["json"] = cached.Clone()
+			self.ServeResultJSON()
+			return
+		}
+	}
 	orders := order_model.GetUserOrders(wxUId, order_status, page, perPage)
 	count := order_model.GetUserOrdersCount(wxUId, status)
+	orderDetailMap, productMap := loadOrderProductContext(orders, lang, useCache)
+	packageMap := loadOrderPackageContext(orderDetailMap, productMap, lang, useCache)
 	var latestOrders []*order_model.Order
 
 	if status != order_model.WAIT_COMMEN {
 		for _, item := range orders {
-			orderDtList := order_model.GetAllDetailsOrderId(item.OrderId, false)
+			orderDtList := orderDetailMap[item.OrderId]
 			for _, orderDtItem := range orderDtList {
-				product := product_model.GetProductById(orderDtItem.ProductId, cache)
+				product := productMap[orderDtItem.ProductId]
 				if product == nil {
 					continue
 				}
+				orderDtItem.ProductName = product.Name
 				orderDtItem.Package = product.Package
 				item.Count += orderDtItem.Count
 				if orderDtItem.Commend || item.Status != order_model.STATUS_COMPLETE {
 					orderDtItem.Commend = true
 				}
 				if product.Package {
-					orderDtItem.PacakageList = order_model.GetOrderDetailPackages(orderDtItem.Id, orderDtItem.Count, true)
+					orderDtItem.PacakageList = packageMap[orderDtItem.Id]
 				}
 				orderDtItem.Cover = product.Cover
+				orderDtItem.UseQuan = product.UseQuan
 				item.ProductList = append(item.ProductList, orderDtItem)
 			}
 			latestOrders = append(latestOrders, item)
@@ -682,12 +765,13 @@ func (self *OrderController) List() {
 
 		for _, item := range orders {
 			commend := false
-			orderDtList := order_model.GetAllDetailsOrderId(item.OrderId, false)
+			orderDtList := orderDetailMap[item.OrderId]
 			for _, orderDtItem := range orderDtList {
-				product := product_model.GetProductById(orderDtItem.ProductId, cache)
+				product := productMap[orderDtItem.ProductId]
 				if product == nil {
 					continue
 				}
+				orderDtItem.ProductName = product.Name
 				orderDtItem.Package = product.Package
 				item.Count += orderDtItem.Count
 				if orderDtItem.Commend || item.Status != order_model.STATUS_COMPLETE {
@@ -697,9 +781,10 @@ func (self *OrderController) List() {
 					commend = true
 				}
 				if product.Package {
-					orderDtItem.PacakageList = order_model.GetOrderDetailPackages(orderDtItem.Id, orderDtItem.Count, true)
+					orderDtItem.PacakageList = packageMap[orderDtItem.Id]
 				}
 				orderDtItem.Cover = product.Cover
+				orderDtItem.UseQuan = product.UseQuan
 				if !orderDtItem.Commend {
 					item.ProductList = append(item.ProductList, orderDtItem)
 				}
@@ -711,11 +796,11 @@ func (self *OrderController) List() {
 			}
 		}
 	}
-	type Ret struct {
-		List      []*order_model.Order `json:"list"`
-		ListCount int64                `json:"list_count"`
+	ret := &orderListCacheResult{ListCount: count, List: latestOrders}
+	if useCache {
+		cache.Cache.Put(cacheKey, ret, 5*time.Minute)
 	}
-	self.Data["json"] = &Ret{ListCount: count, List: latestOrders}
+	self.Data["json"] = ret.Clone()
 	self.ServeResultJSON()
 }
 

+ 30 - 11
go/gopath/src/fohow.com/apps/controllers/product_controller/product_controller.go

@@ -27,7 +27,9 @@ func (self *ProductController) Latest() {
 	price_sort, _ := self.GetInt64("price_sort")
 	sale_sort, _ := self.GetInt64("sale_sort")
 	sale_zone, _ := self.GetInt64("sale_zone")
+	lang := self.GetString("lang")
 
+	only_app, _ := self.GetBool("app", false)
 	if page <= 0 {
 		page = 1
 	}
@@ -51,9 +53,12 @@ func (self *ProductController) Latest() {
 		depart = wxUser.Depart
 	}
 
-	pds := product_model.GetLatest(page, perPage, recommend, price_sort, sale_sort, sale_zone, depart, ptype, cache)
-	count := product_model.GetNewLatestCount(recommend, sale_zone, depart, ptype, cache)
-	for _, pd := range pds {
+	pds := product_model.GetLatest(page, perPage, recommend, price_sort, sale_sort, sale_zone, depart, ptype, only_app, cache)
+	count := product_model.GetNewLatestCount(recommend, sale_zone, depart, ptype, only_app, cache)
+	list := make([]*product_model.Product, 0, len(pds))
+	for _, basePd := range pds {
+		pd := basePd.Clone()
+		pd.ApplyLanguage(lang)
 		pd.SoldCount = pd.SaleNums
 		if pd.Count > pd.SoldCount {
 			pd.LeftCount = pd.Count - pd.SoldCount
@@ -139,9 +144,10 @@ func (self *ProductController) Latest() {
 				}
 			}
 		}
+		list = append(list, pd)
 	}
 
-	self.Data["json"] = &Ret{List: pds, ListCount: count}
+	self.Data["json"] = &Ret{List: list, ListCount: count}
 	self.ServeJSON()
 }
 
@@ -149,12 +155,15 @@ func (self *ProductController) Latest() {
 func (self *ProductController) Get() {
 	_id := self.Ctx.Input.Param(":id")
 	cache, _ := self.GetBool("cache", true)
+	lang := self.GetString("lang")
 
 	pId, _ := strconv.ParseInt(_id, 10, 64)
-	pd := product_model.GetProductById(pId, cache)
-	if pd == nil {
+	basePd := product_model.GetProductById(pId, cache)
+	if basePd == nil {
 		self.ReturnError(403, apps.NoExist, "", nil)
 	}
+	pd := basePd.Clone()
+	pd.ApplyLanguage(lang)
 	//商品下架
 	if pd.Status == product_model.PRODUCT_STATUS_DOWN {
 		self.ReturnError(403, []string{apps.ProductOffSale[0], fmt.Sprintf("%s产品已经下架", pd.Name)}, "", nil)
@@ -247,21 +256,26 @@ func (self *ProductController) GetPdDetail() {
 	_color_id := self.Ctx.Input.Param(":color_id")
 	colorId, _ := strconv.ParseInt(_color_id, 10, 64)
 	cache, _ := self.GetBool("cache", false)
-	beginPd := product_model.GetProductById(pId, true)
-	if beginPd == nil {
+	lang := self.GetString("lang")
+	beginBasePd := product_model.GetProductById(pId, true)
+	if beginBasePd == nil {
 		self.ReturnError(403, apps.NoExist, "", nil)
 	}
+	beginPd := beginBasePd.Clone()
+	beginPd.ApplyLanguage(lang)
 
 	//商品下架
 	if beginPd.Status == product_model.PRODUCT_STATUS_DOWN {
 		self.ReturnError(403, []string{apps.ProductOffSale[0], fmt.Sprintf("%s产品已经下架", beginPd.Name)}, "", nil)
 	}
 
-	pd := product_model.GetProductByIdAndSizeAndColor(beginPd.RelateProductId, sizeId, colorId, cache)
+	basePd := product_model.GetProductByIdAndSizeAndColor(beginPd.RelateProductId, sizeId, colorId, cache)
 	type Ret struct {
 		Product *product_model.Product `json:"product"`
 	}
-	if pd != nil {
+	if basePd != nil {
+		pd := basePd.Clone()
+		pd.ApplyLanguage(lang)
 		//获取颜色,规格名称
 		if sizeId > int64(0) {
 			prd_size := product_model.GetProductAttrValueById(sizeId)
@@ -328,9 +342,14 @@ func (self *ProductController) GetPdDetail() {
 				pd.DeliverState = product_model.DELIVER_STATE_2
 			}
 		}
+		ret := &Ret{}
+		ret.Product = pd
+		self.Data["json"] = ret
+		self.ServeJSON()
+		return
 	}
 	ret := &Ret{}
-	ret.Product = pd
+	ret.Product = nil
 	self.Data["json"] = ret
 	self.ServeJSON()
 }

+ 6 - 5
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/cache_controller.go

@@ -3,6 +3,7 @@ package railsadmin_controller
 import (
 	"fmt"
 	"fohow.com/apps"
+	"fohow.com/apps/models/product_model"
 	"fohow.com/apps/models/user_model"
 	"fohow.com/cache"
 	"github.com/astaxie/beego"
@@ -26,7 +27,7 @@ func (self *RailsadminController) Init(ctx *context.Context, controllerName, act
 	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
 }
 
-//项目信息清除缓存
+// 项目信息清除缓存
 func (self *RailsadminController) CleanCache() {
 	t := self.Ctx.Input.Param(":type")
 	_id := self.Ctx.Input.Param(":id")
@@ -40,12 +41,12 @@ func (self *RailsadminController) CleanCache() {
 		cache.Cache.Delete(k)
 	case t == "package":
 		beego.BeeLogger.Warn("railsadmin after save clean package cache: %d", id)
-		k := cache.GetKey(cache.GetProductPackagetByPId, id)
-		cache.Cache.Delete(k)
+		version := product_model.InvalidateProductCache()
+		beego.BeeLogger.Warn("invalidate product cache version=%d", version)
 	case t == "product":
 		beego.BeeLogger.Warn("railsadmin after save clean product cache: %d", id)
-		k := cache.GetKey(cache.GetProductByPId, id)
-		cache.Cache.Delete(k)
+		version := product_model.InvalidateProductCache()
+		beego.BeeLogger.Warn("invalidate product cache version=%d", version)
 	case t == "user":
 		beego.BeeLogger.Warn("railsadmin after save clean user cache: %d", id)
 		wxUser := user_model.GetWxUserById(id, false)

+ 50 - 3
go/gopath/src/fohow.com/apps/models/order_model/order.go

@@ -8,6 +8,7 @@ import (
 	"fohow.com/apps/models/sys_config"
 	"strconv"
 	"strings"
+	"sync/atomic"
 	"time"
 
 	"github.com/astaxie/beego"
@@ -83,6 +84,8 @@ var ORDER_TYPE_CN_TEXT = map[int64]string{
 	ORDER_TYPE_SEKILL: "秒杀",
 }
 
+var orderCacheVersion int64 = time.Now().UnixNano()
+
 type Order struct {
 	Id                 int64              `orm:"column(id);pk"                                       json:"id"`       // int(11)
 	OrderId            string             `orm:"column(order_id)"                                    json:"order_id"` // varchar(255)
@@ -141,6 +144,46 @@ func (self *Order) TableName() string {
 	return orders_tablename
 }
 
+func orderCacheKey(key string) string {
+	return fmt.Sprintf("order_cache.v(%d).%s", atomic.LoadInt64(&orderCacheVersion), key)
+}
+
+func OrderCacheKey(key string) string {
+	return orderCacheKey(key)
+}
+
+func InvalidateOrderCache() int64 {
+	version := time.Now().UnixNano()
+	atomic.StoreInt64(&orderCacheVersion, version)
+	return version
+}
+
+func (self *Order) Clone() *Order {
+	if self == nil {
+		return nil
+	}
+	item := *self
+	if len(self.ProductList) > 0 {
+		item.ProductList = cloneOrderDetailList(self.ProductList)
+	}
+	if self.WxUser != nil {
+		wxUser := *self.WxUser
+		item.WxUser = &wxUser
+	}
+	return &item
+}
+
+func CloneOrderList(orders []*Order) []*Order {
+	if len(orders) == 0 {
+		return nil
+	}
+	ret := make([]*Order, 0, len(orders))
+	for _, order := range orders {
+		ret = append(ret, order.Clone())
+	}
+	return ret
+}
+
 // 创建订单
 func (self *Order) Create(wxUserId, uId, count, totalPrice, buyPrice int64, source string) *Order {
 	var oId string
@@ -162,6 +205,7 @@ func (self *Order) Create(wxUserId, uId, count, totalPrice, buyPrice int64, sour
 		return nil
 	}
 	order.Id = id
+	InvalidateOrderCache()
 	return order
 }
 
@@ -186,15 +230,16 @@ func (self *Order) CreateNew(wxUserId, uId, totalPrice, freight, orderType, depa
 		return nil
 	}
 	order.Id = id
+	InvalidateOrderCache()
 	return order
 }
 
 // 根据OrderId获取订单记录
 func GetOrderById(oId string, useCache bool) (order *Order) {
-	k := fmt.Sprintf("order_model.GetOrderByOId[%s]", oId)
+	k := orderCacheKey(fmt.Sprintf("order_model.GetOrderByOId[%s]", oId))
 	if useCache {
 		if v, ok := cache.Cache.Get(k).(*Order); ok {
-			return v
+			return v.Clone()
 		}
 	}
 	item := new(Order)
@@ -214,7 +259,7 @@ func GetOrderById(oId string, useCache bool) (order *Order) {
 		item.SpecialPro = true
 	}
 	cache.Cache.Put(k, item, 5*time.Minute)
-	return item
+	return item.Clone()
 
 }
 
@@ -244,6 +289,7 @@ func (self *Order) Save() bool {
 		beego.BeeLogger.Error("SaveOrder order=[%s] .err=[%s]", self.OrderId, err)
 		return false
 	}
+	InvalidateOrderCache()
 	return true
 }
 
@@ -253,6 +299,7 @@ func (self *Order) Delete() error {
 		beego.BeeLogger.Error("Delete Order id=[%d] .err=[%s]", self.Id, err)
 		return err
 	}
+	InvalidateOrderCache()
 	return nil
 }
 

+ 90 - 3
go/gopath/src/fohow.com/apps/models/order_model/order_detail.go

@@ -48,6 +48,29 @@ type OrderDetail struct {
 	PacakageList         []*ProductItem `orm:"-"                                                json:"package_list"` // varchar(255)
 }
 
+func (self *OrderDetail) Clone() *OrderDetail {
+	if self == nil {
+		return nil
+	}
+	item := *self
+	if len(self.PacakageList) > 0 {
+		item.PacakageList = make([]*ProductItem, len(self.PacakageList))
+		copy(item.PacakageList, self.PacakageList)
+	}
+	return &item
+}
+
+func cloneOrderDetailList(items []*OrderDetail) []*OrderDetail {
+	if len(items) == 0 {
+		return nil
+	}
+	ret := make([]*OrderDetail, 0, len(items))
+	for _, item := range items {
+		ret = append(ret, item.Clone())
+	}
+	return ret
+}
+
 func (self *OrderDetail) TableName() string {
 	return order_details_tablename
 }
@@ -76,6 +99,7 @@ func (self *OrderDetail) Create(oId string, orderId, pId, relatePId, pPrice, sil
 		return nil
 	}
 	item.Id = id
+	InvalidateOrderCache()
 	if item != nil {
 		GenerateOrderDtItem(item)
 	}
@@ -103,6 +127,7 @@ func SendCreate(oId string, orderId, pId, pPrice, unitRoboBalancePrice int64, pN
 		return nil
 	}
 	item.Id = id
+	InvalidateOrderCache()
 	if item != nil {
 		GenerateOrderDtItem(item)
 		//更新赠品已售数量
@@ -118,16 +143,17 @@ func (self *OrderDetail) Save() bool {
 		beego.BeeLogger.Error("Save OrderDetail order=[%s] .err=[%s]", self.OrderId, err)
 		return false
 	}
+	InvalidateOrderCache()
 	return true
 }
 
 // 根据订单Id,获取所有订单项
 func GetAllDetailsOrderId(oId string, useCache bool) (items []*OrderDetail) {
 
-	k := fmt.Sprintf("order_model.GetAllDetailsOrderId(%s)", oId)
+	k := orderCacheKey(fmt.Sprintf("order_model.GetAllDetailsOrderId(%s)", oId))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).([]*OrderDetail); ok {
-			return ret
+			return cloneOrderDetailList(ret)
 		}
 	}
 	item := new(OrderDetail)
@@ -137,7 +163,68 @@ func GetAllDetailsOrderId(oId string, useCache bool) (items []*OrderDetail) {
 		return nil
 	}
 	cache.Cache.Put(k, items, 60*time.Minute)
-	return items
+	return cloneOrderDetailList(items)
+}
+
+func GetAllDetailsByOrderIds(orderIds []string, useCache bool) map[string][]*OrderDetail {
+	ret := make(map[string][]*OrderDetail, len(orderIds))
+	if len(orderIds) == 0 {
+		return ret
+	}
+
+	missing := make([]string, 0, len(orderIds))
+	for _, orderId := range orderIds {
+		if orderId == "" {
+			continue
+		}
+		k := orderCacheKey(fmt.Sprintf("order_model.GetAllDetailsOrderId(%s)", orderId))
+		if useCache {
+			if cached, ok := cache.Cache.Get(k).([]*OrderDetail); ok {
+				ret[orderId] = cloneOrderDetailList(cached)
+				continue
+			}
+		}
+		missing = append(missing, orderId)
+	}
+
+	if len(missing) == 0 {
+		return ret
+	}
+
+	placeholders := strings.TrimRight(strings.Repeat("?,", len(missing)), ",")
+	sql := fmt.Sprintf(`
+		SELECT
+			*
+		FROM
+			order_details
+		WHERE
+			order_no IN (%s)
+	`, placeholders)
+
+	args := make([]interface{}, 0, len(missing))
+	for _, orderId := range missing {
+		args = append(args, orderId)
+	}
+
+	var items []*OrderDetail
+	if _, err := orm.NewOrm().Raw(sql, args...).QueryRows(&items); err != nil {
+		beego.BeeLogger.Debug("get order details batch err=[%s]", err)
+		return ret
+	}
+
+	grouped := make(map[string][]*OrderDetail, len(missing))
+	for _, item := range items {
+		grouped[item.OrderNo] = append(grouped[item.OrderNo], item)
+	}
+
+	for _, orderId := range missing {
+		orderItems := grouped[orderId]
+		ret[orderId] = orderItems
+		cache.Cache.Put(orderCacheKey(fmt.Sprintf("order_model.GetAllDetailsOrderId(%s)", orderId)), orderItems, 60*time.Minute)
+		ret[orderId] = cloneOrderDetailList(orderItems)
+	}
+
+	return ret
 }
 
 // 获取商品销售统计

+ 106 - 14
go/gopath/src/fohow.com/apps/models/order_model/order_dt_item.go

@@ -6,6 +6,7 @@ import (
 	"fohow.com/cache"
 	"github.com/astaxie/beego"
 	"github.com/astaxie/beego/orm"
+	"strings"
 	"time"
 )
 
@@ -41,7 +42,14 @@ type ProductItem struct {
 	Nums      int64  `orm:"column(nums);null"            json:"nums"`       // tinyint(1)
 }
 
-//创建订单明细项
+type packageItemRow struct {
+	OrderDtId int64  `orm:"column(order_dt_id)"`
+	ItemId    int64  `orm:"column(item_id)"`
+	ItemTitle string `orm:"column(item_title)"`
+	Nums      int64  `orm:"column(nums)"`
+}
+
+// 创建订单明细项
 func (self *OrderDtItem) Create(orderNo, title, sizeName, colorName string, pId, price, pCount, orderId, orderDtId int64, send bool) *OrderDtItem {
 	item := &OrderDtItem{
 		OrderNo:   orderNo,
@@ -61,10 +69,11 @@ func (self *OrderDtItem) Create(orderNo, title, sizeName, colorName string, pId,
 		return nil
 	}
 	item.Id = id
+	InvalidateOrderCache()
 	return item
 }
 
-//生成订单明细
+// 生成订单明细
 func GenerateOrderDtItem(detail_item *OrderDetail) error {
 	sizeName := ""
 	colorName := ""
@@ -113,22 +122,28 @@ func GenerateOrderDtItem(detail_item *OrderDetail) error {
 	return nil
 }
 
-//生成订单明细套装
-func GetOrderDetailPackages(orderDtId, nums int64, useCache bool) (list []*ProductItem) {
-	k := fmt.Sprintf("order_model.GetOrderDetailPackages(%d)", orderDtId)
+// 生成订单明细套装
+func GetOrderDetailPackages(orderDtId, nums int64, lang string, useCache bool) (list []*ProductItem) {
+	normalizedLang := product_model.NormalizeLanguage(lang)
+	k := orderCacheKey(fmt.Sprintf("order_model.GetOrderDetailPackages(%d,%s)", orderDtId, normalizedLang))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).([]*ProductItem); ok {
 			return ret
 		}
 	}
-	//sql := fmt.Sprintf("select total_price as investment, user_id, paied_at as invest_time from %s where project_id = ? and state = ? and is_refunded = ? and is_deleted = ? order by invest_time desc, id desc limit %d, %d",
-
-	sql :=
-		fmt.Sprintf(`
-		select product_id as item_id,product_name as item_title,ceil(nums/%d) as nums from
-		base_details
-		where dt_id = ?  ;
-		`, nums)
+	nameExpr := product_model.LocalizedProductNameExpr("p", normalizedLang)
+	sql := fmt.Sprintf(`
+		SELECT
+			bd.product_id AS item_id,
+			%s AS item_title,
+			ceil(bd.nums/%d) AS nums
+		FROM
+			base_details bd
+		LEFT JOIN
+			products p ON p.id = bd.product_id
+		WHERE
+			bd.dt_id = ?;
+	`, nameExpr, nums)
 	_, err := orm.NewOrm().Raw(sql, orderDtId).QueryRows(&list)
 	if err != nil {
 		beego.BeeLogger.Warn("order_model.GetOrderDetailPackages(%d) err=%s", orderDtId, err)
@@ -138,7 +153,84 @@ func GetOrderDetailPackages(orderDtId, nums int64, useCache bool) (list []*Produ
 	return list
 }
 
-//获取某个商品明细已售总数
+func GetOrderDetailPackagesBatch(orderDtNums map[int64]int64, lang string, useCache bool) map[int64][]*ProductItem {
+	ret := make(map[int64][]*ProductItem, len(orderDtNums))
+	if len(orderDtNums) == 0 {
+		return ret
+	}
+
+	normalizedLang := product_model.NormalizeLanguage(lang)
+	missing := make([]int64, 0, len(orderDtNums))
+	for orderDtId, nums := range orderDtNums {
+		if orderDtId <= 0 || nums <= 0 {
+			continue
+		}
+		k := orderCacheKey(fmt.Sprintf("order_model.GetOrderDetailPackages(%d,%s)", orderDtId, normalizedLang))
+		if useCache {
+			if cached, ok := cache.Cache.Get(k).([]*ProductItem); ok {
+				ret[orderDtId] = cached
+				continue
+			}
+		}
+		missing = append(missing, orderDtId)
+	}
+
+	if len(missing) == 0 {
+		return ret
+	}
+
+	nameExpr := product_model.LocalizedProductNameExpr("p", normalizedLang)
+	placeholders := strings.TrimRight(strings.Repeat("?,", len(missing)), ",")
+	sql := fmt.Sprintf(`
+		SELECT
+			bd.dt_id AS order_dt_id,
+			bd.product_id AS item_id,
+			%s AS item_title,
+			bd.nums AS nums
+		FROM
+			base_details bd
+		LEFT JOIN
+			products p ON p.id = bd.product_id
+		WHERE
+			bd.dt_id IN (%s)
+		ORDER BY
+			bd.dt_id ASC, bd.id ASC;
+	`, nameExpr, placeholders)
+
+	args := make([]interface{}, 0, len(missing))
+	for _, orderDtId := range missing {
+		args = append(args, orderDtId)
+	}
+
+	var rows []*packageItemRow
+	if _, err := orm.NewOrm().Raw(sql, args...).QueryRows(&rows); err != nil {
+		beego.BeeLogger.Warn("order_model.GetOrderDetailPackagesBatch(%d) err=%s", len(missing), err)
+		return ret
+	}
+
+	grouped := make(map[int64][]*ProductItem, len(missing))
+	for _, row := range rows {
+		divisor := orderDtNums[row.OrderDtId]
+		if divisor <= 0 {
+			divisor = 1
+		}
+		grouped[row.OrderDtId] = append(grouped[row.OrderDtId], &ProductItem{
+			ItemId:    row.ItemId,
+			ItemTitle: row.ItemTitle,
+			Nums:      (row.Nums + divisor - 1) / divisor,
+		})
+	}
+
+	for _, orderDtId := range missing {
+		list := grouped[orderDtId]
+		ret[orderDtId] = list
+		cache.Cache.Put(orderCacheKey(fmt.Sprintf("order_model.GetOrderDetailPackages(%d,%s)", orderDtId, normalizedLang)), list, 10*time.Minute)
+	}
+
+	return ret
+}
+
+// 获取某个商品明细已售总数
 func GetDetailSoldCountByPId(pId int64, useCache bool) int64 {
 	k := fmt.Sprintf("order_model.GetDetailSoldCountByPId(%d)", pId)
 	if useCache {

+ 230 - 31
go/gopath/src/fohow.com/apps/models/product_model/product.go

@@ -2,6 +2,9 @@ package product_model
 
 import (
 	"fmt"
+	"strconv"
+	"strings"
+	"sync/atomic"
 	"time"
 
 	"github.com/astaxie/beego"
@@ -43,19 +46,32 @@ const (
 
 	//Employ Sort
 	ProdutEmploySort = 25
+
+	LanguageZhCN = "zh-cn"
+	LanguageEn   = "en"
+	LanguageRu   = "ru"
+	LanguageZhTW = "zh-tw"
 )
 
+var productCacheVersion int64 = time.Now().UnixNano()
+
 func (self *Product) TableName() string {
 	return products_tablename
 }
 
 type Product struct {
-	Id               int64      `orm:"column(id);pk"                                       json:"id"`     // int(11)
-	Name             string     `orm:"column(name)"                                        json:"name"`   // varchar(100)
-	Ptype            string     `orm:"column(ptype)"                                       json:"ptype"`  // varchar(100)
-	CategoryId       int64      `orm:"column(category_id);null"                            json:"-"`      // int(11)
-	Detail           string     `orm:"column(detail);null"                                 json:"detail"` // text
-	Price            int64      `orm:"column(price)"                                       json:"price"`  // int(11)
+	Id               int64      `orm:"column(id);pk"                                       json:"id"`   // int(11)
+	Name             string     `orm:"column(name)"                                        json:"name"` // varchar(100)
+	NameEn           string     `orm:"column(name_en);null"                                json:"-"`
+	NameRu           string     `orm:"column(name_ru);null"                                json:"-"`
+	NameTw           string     `orm:"column(name_tw);null"                                json:"-"`
+	Ptype            string     `orm:"column(ptype)"                                       json:"ptype"` // varchar(100)
+	CategoryId       int64      `orm:"column(category_id);null"                            json:"-"`     // int(11)
+	Detail           string     `orm:"column(detail);null"                                 json:"detail"`
+	DetailEn         string     `orm:"column(detail_en);null"                              json:"-"`
+	DetailRu         string     `orm:"column(detail_ru);null"                              json:"-"`
+	DetailTw         string     `orm:"column(detail_tw);null"                              json:"-"`
+	Price            int64      `orm:"column(price)"                                       json:"price"` // int(11)
 	RoboBalancePrice int64      `orm:"column(robo_balance_price)"                          json:"robo_balance_price"`
 	BuyPrice         int64      `orm:"column(buy_price)"                                   json:"-"`               // int(11)
 	UserSalePrice    int64      `orm:"column(user_sale_price)"                             json:"user_sale_price"` // int(11)
@@ -129,17 +145,197 @@ type Product struct {
 	CanBuy           bool            `orm:"-"                              json:"can_buy"`           // bool
 }
 
+type OrderProductLite struct {
+	Id      int64  `orm:"column(id)" json:"id"`
+	Name    string `orm:"column(name)" json:"name"`
+	Cover   string `orm:"column(cover)" json:"cover"`
+	Package bool   `orm:"column(package)" json:"package"`
+	UseQuan bool   `orm:"column(use_quan)" json:"use_quan"`
+}
+
+func productCacheKey(key string) string {
+	return fmt.Sprintf("product_cache.v(%d).%s", atomic.LoadInt64(&productCacheVersion), key)
+}
+
+func InvalidateProductCache() int64 {
+	version := time.Now().UnixNano()
+	atomic.StoreInt64(&productCacheVersion, version)
+	return version
+}
+
+func NormalizeLanguage(lang string) string {
+	switch strings.ToLower(strings.TrimSpace(lang)) {
+	case "", "zh", "zh-cn", "zh_hans", "zh-hans", "cn":
+		return LanguageZhCN
+	case "en", "en-us", "en_us":
+		return LanguageEn
+	case "ru", "ru-ru", "ru_ru":
+		return LanguageRu
+	case "zh-tw", "zh_tw", "zh-hk", "zh_hk", "tw", "hk", "traditional":
+		return LanguageZhTW
+	default:
+		return LanguageZhCN
+	}
+}
+
+func LocalizedProductNameExpr(tableAlias, lang string) string {
+	prefix := ""
+	if strings.TrimSpace(tableAlias) != "" {
+		prefix = strings.TrimSpace(tableAlias) + "."
+	}
+
+	switch NormalizeLanguage(lang) {
+	case LanguageEn:
+		return fmt.Sprintf("COALESCE(NULLIF(TRIM(%sname_en), ''), %sname)", prefix, prefix)
+	case LanguageRu:
+		return fmt.Sprintf("COALESCE(NULLIF(TRIM(%sname_ru), ''), %sname)", prefix, prefix)
+	case LanguageZhTW:
+		return fmt.Sprintf("COALESCE(NULLIF(TRIM(%sname_tw), ''), %sname)", prefix, prefix)
+	default:
+		return prefix + "name"
+	}
+}
+
+func orderProductLiteCacheKey(id int64, lang string) string {
+	return productCacheKey(fmt.Sprintf("product_model.GetOrderProductLite(%d,%s)", id, NormalizeLanguage(lang)))
+}
+
+func formatInt64List(ids []int64) string {
+	formatted := make([]string, 0, len(ids))
+	for _, id := range ids {
+		formatted = append(formatted, strconv.FormatInt(id, 10))
+	}
+	return strings.Join(formatted, ",")
+}
+
+func uniquePositiveProductIDs(ids []int64) []int64 {
+	seen := make(map[int64]struct{}, len(ids))
+	list := make([]int64, 0, len(ids))
+	for _, id := range ids {
+		if id <= 0 {
+			continue
+		}
+		if _, ok := seen[id]; ok {
+			continue
+		}
+		seen[id] = struct{}{}
+		list = append(list, id)
+	}
+	return list
+}
+
+func GetOrderProductsByIds(ids []int64, lang string, useCache bool) map[int64]*OrderProductLite {
+	ret := make(map[int64]*OrderProductLite)
+	ids = uniquePositiveProductIDs(ids)
+	if len(ids) == 0 {
+		return ret
+	}
+
+	normalizedLang := NormalizeLanguage(lang)
+	missing := make([]int64, 0, len(ids))
+	for _, id := range ids {
+		if useCache {
+			if cached, ok := cache.Cache.Get(orderProductLiteCacheKey(id, normalizedLang)).(*OrderProductLite); ok && cached != nil {
+				ret[id] = cached
+				continue
+			}
+		}
+		missing = append(missing, id)
+	}
+
+	if len(missing) == 0 {
+		return ret
+	}
+
+	nameExpr := LocalizedProductNameExpr("p", normalizedLang)
+	sql := fmt.Sprintf(`
+		SELECT
+			p.id,
+			%s AS name,
+			p.package,
+			p.use_quan,
+			IFNULL((
+				SELECT img
+				FROM product_pictures pp
+				WHERE pp.product_id = p.id AND pp.pic_type = %d
+				ORDER BY pp.sort DESC
+				LIMIT 1
+			), '') AS cover
+		FROM
+			products p
+		WHERE
+			p.id IN (%s)
+	`, nameExpr, PIC_TYPE_ALBUM, formatInt64List(missing))
+
+	var list []*OrderProductLite
+	if _, err := orm.NewOrm().Raw(sql).QueryRows(&list); err != nil {
+		beego.BeeLogger.Warn("product_model.GetOrderProductsByIds(%d,%s) err=%s", len(missing), normalizedLang, err)
+		return ret
+	}
+
+	for _, item := range list {
+		item.Cover = GetCdnFullImgUrl(item.Cover)
+		ret[item.Id] = item
+		if useCache {
+			cache.Cache.Put(orderProductLiteCacheKey(item.Id, normalizedLang), item, 5*time.Minute)
+		}
+	}
+
+	return ret
+}
+
+func (self *Product) Clone() *Product {
+	if self == nil {
+		return nil
+	}
+	item := *self
+	return &item
+}
+
+func (self *Product) ApplyLanguage(lang string) {
+	if self == nil {
+		return
+	}
+
+	switch NormalizeLanguage(lang) {
+	case LanguageEn:
+		if strings.TrimSpace(self.NameEn) != "" {
+			self.Name = self.NameEn
+		}
+		if strings.TrimSpace(self.DetailEn) != "" {
+			self.Detail = self.DetailEn
+		}
+	case LanguageRu:
+		if strings.TrimSpace(self.NameRu) != "" {
+			self.Name = self.NameRu
+		}
+		if strings.TrimSpace(self.DetailRu) != "" {
+			self.Detail = self.DetailRu
+		}
+	case LanguageZhTW:
+		if strings.TrimSpace(self.NameTw) != "" {
+			self.Name = self.NameTw
+		}
+		if strings.TrimSpace(self.DetailTw) != "" {
+			self.Detail = self.DetailTw
+		}
+	}
+}
+
 // 获取最新推荐商品
-func GetLatest(page, perPage, recommend, priceSort, saleSort, saleZone, depart int64, ptype string, useCache bool) (products []*Product) {
-	k := fmt.Sprintf("product_model.GetLatest.page(%d).perPage(%d).recommend(%d)", page, perPage, recommend)
+func GetLatest(page, perPage, recommend, priceSort, saleSort, saleZone, depart int64, ptype string, onlyApp bool, useCache bool) (products []*Product) {
+	k := productCacheKey(fmt.Sprintf("product_model.GetLatest.page(%d).perPage(%d).recommend(%d).priceSort(%d).saleSort(%d).saleZone(%d).depart(%d).ptype(%s).onlyApp(%t)", page, perPage, recommend, priceSort, saleSort, saleZone, depart, ptype, onlyApp))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).([]*Product); ok {
 			return ret
 		}
 	}
-	var saleZoneSql, priceSql, saleSql, orderSql string
+	var extraSql, priceSql, saleSql, orderSql string
 	if saleZone > int64(0) {
-		saleZoneSql = fmt.Sprintf(" and a.sale_zone=%d", saleZone)
+		extraSql += fmt.Sprintf(" and a.sale_zone=%d", saleZone)
+	}
+	if onlyApp {
+		extraSql += " and a.allow_app=1"
 	}
 	if priceSort == int64(1) {
 		//降序
@@ -169,7 +365,7 @@ func GetLatest(page, perPage, recommend, priceSort, saleSort, saleZone, depart i
 		where a.status = ? and a.recommend > ? and a.show_flag=? and a.ptype=? and a.category_id!=? and (b.depart_record_id=? or b.depart_record_id is null ) %s
 		order by %s limit %d, %d; 
 	`
-	sql = fmt.Sprintf(sql, saleZoneSql, orderSql, (page-1)*perPage, perPage)
+	sql = fmt.Sprintf(sql, extraSql, orderSql, (page-1)*perPage, perPage)
 	//beego.BeeLogger.Warn("sql=%s", sql)
 	_, err := orm.NewOrm().Raw(sql, 1, recommend, true, ptype, ProdutEmploySort, depart).QueryRows(&products)
 
@@ -187,8 +383,8 @@ func GetLatest(page, perPage, recommend, priceSort, saleSort, saleZone, depart i
 	return products
 }
 
-func GetNewLatestCount(recommend, saleZone, depart int64, ptype string, useCache bool) int64 {
-	k := fmt.Sprintf("product_model.GetNewLatestCount.recommend(%d)(%d)(%d)", recommend, saleZone, depart)
+func GetNewLatestCount(recommend, saleZone, depart int64, ptype string, onlyApp bool, useCache bool) int64 {
+	k := productCacheKey(fmt.Sprintf("product_model.GetNewLatestCount.recommend(%d).saleZone(%d).depart(%d).ptype(%s).onlyApp(%t)", recommend, saleZone, depart, ptype, onlyApp))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).(int64); ok {
 			return ret
@@ -203,11 +399,14 @@ func GetNewLatestCount(recommend, saleZone, depart int64, ptype string, useCache
 		 select count(DISTINCT a.id) as count from products a left join depart_records_products b  on a.id=b.product_id
 			where a.status = ? and a.recommend > ? and a.show_flag=? and a.ptype=? and (b.depart_record_id=? or b.depart_record_id is null ) %s;
 	`
-	var saleZoneSql string
+	var extraSql string
 	if saleZone > int64(0) {
-		saleZoneSql = fmt.Sprintf(" and a.sale_zone=%d", saleZone)
+		extraSql += fmt.Sprintf(" and a.sale_zone=%d", saleZone)
 	}
-	sql = fmt.Sprintf(sql, saleZoneSql)
+	if onlyApp {
+		extraSql += " and a.allow_app=1"
+	}
+	sql = fmt.Sprintf(sql, extraSql)
 
 	err := o.Raw(sql, true, recommend, true, ptype, depart).QueryRow(ret)
 	if err != nil {
@@ -222,7 +421,7 @@ func GetNewLatestCount(recommend, saleZone, depart int64, ptype string, useCache
 }
 
 func GetLatestCount(recommend, saleZone int64, ptype string, useCache bool) int64 {
-	k := fmt.Sprintf("product_model.GetLatestCount.recommend(%d)", recommend)
+	k := productCacheKey(fmt.Sprintf("product_model.GetLatestCount.recommend(%d)", recommend))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).(int64); ok {
 			return ret
@@ -243,7 +442,7 @@ func GetLatestCount(recommend, saleZone int64, ptype string, useCache bool) int6
 // 根据商品Id,获取商品信息
 func GetProductById(id int64, useCache bool) *Product {
 	//k := fmt.Sprintf("product_model.GetProductById[%d]", id)
-	k := cache.GetKey(cache.GetProductByPId, id)
+	k := productCacheKey(cache.GetKey(cache.GetProductByPId, id))
 	if useCache {
 		if v, ok := cache.Cache.Get(k).(*Product); ok {
 			return v
@@ -262,7 +461,7 @@ func GetProductById(id int64, useCache bool) *Product {
 // 判断商品是否在当前省可发货
 func GetProductByIdAndProvince(id int64, province string, useCache bool) *Product {
 	//k := fmt.Sprintf("product_model.GetProductById[%d]", id)
-	k := cache.GetKey(cache.GetProductByPIdAndProvince, id, province)
+	k := productCacheKey(cache.GetKey(cache.GetProductByPIdAndProvince, id, province))
 	if useCache {
 		if v, ok := cache.Cache.Get(k).(*Product); ok {
 			return v
@@ -286,7 +485,7 @@ func GetProductByIdAndProvince(id int64, province string, useCache bool) *Produc
 // 判断商品是否在当前省受限
 func GetNoDeliveryPrd(id int64, province string, useCache bool) *Product {
 	//k := fmt.Sprintf("product_model.GetProductById[%d]", id)
-	k := cache.GetKey(cache.GetNoDeliveryPrd, id, province)
+	k := productCacheKey(cache.GetKey(cache.GetNoDeliveryPrd, id, province))
 	if useCache {
 		if v, ok := cache.Cache.Get(k).(*Product); ok {
 			return v
@@ -308,7 +507,7 @@ func GetNoDeliveryPrd(id int64, province string, useCache bool) *Product {
 }
 
 func GetProductsByCatId(cId, saleZone, page, perPage, priceSort, saleSort int64, words string, useCache bool) (products []*Product) {
-	k := fmt.Sprintf("product_model.GetProductsByCatId(%d).page(%d).perPage(%d).words(%d)", cId, page, perPage, words)
+	k := productCacheKey(fmt.Sprintf("product_model.GetProductsByCatId(%d).page(%d).perPage(%d).words(%s)", cId, page, perPage, words))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).([]*Product); ok {
 			return ret
@@ -371,7 +570,7 @@ func GetProductsByCatId(cId, saleZone, page, perPage, priceSort, saleSort int64,
 }
 
 func GetProductCountByCatId(cId, saleZone int64, words string, useCache bool) int64 {
-	k := fmt.Sprintf("product_model.GetProductCountByCatId(%d)", cId)
+	k := productCacheKey(fmt.Sprintf("product_model.GetProductCountByCatId(%d).saleZone(%d).words(%s)", cId, saleZone, words))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).(int64); ok {
 			return ret
@@ -398,7 +597,7 @@ func GetProductCountByCatId(cId, saleZone int64, words string, useCache bool) in
 }
 
 func GetSeckillProducts(queryDate time.Time, useCache bool) (list []*Product) {
-	k := fmt.Sprintf("product_model.GetSeckillProducts(%s)", queryDate.Format("2006-01-02"))
+	k := productCacheKey(fmt.Sprintf("product_model.GetSeckillProducts(%s)", queryDate.Format("2006-01-02")))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).([]*Product); ok {
 			return ret
@@ -425,7 +624,7 @@ func GetSeckillProducts(queryDate time.Time, useCache bool) (list []*Product) {
 }
 
 func GetSeckillDates(startDate, endDate time.Time, useCache bool) (list []string) {
-	k := fmt.Sprintf("product_model.GetSeckillDates(%s, %s)", startDate.Format("2006-01-02"), endDate.Format("2006-01-02"))
+	k := productCacheKey(fmt.Sprintf("product_model.GetSeckillDates(%s, %s)", startDate.Format("2006-01-02"), endDate.Format("2006-01-02")))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).([]string); ok {
 			return ret
@@ -456,7 +655,7 @@ type OnOffSeckillTime struct {
 }
 
 func GetSeckillOnOffSeckillTimeByDate(queryDate string, useCache bool) *OnOffSeckillTime {
-	k := fmt.Sprintf("product_model.GetSeckillOnOffSaleTimeByDate(%s)", queryDate)
+	k := productCacheKey(fmt.Sprintf("product_model.GetSeckillOnOffSaleTimeByDate(%s)", queryDate))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).(*OnOffSeckillTime); ok {
 			return ret
@@ -494,7 +693,7 @@ func (self *Product) Save() error {
 
 // 根据关联商品Id,sizeId,colorId,获取商品信息
 func GetProductByIdAndSizeAndColor(id, sizeId, colorId int64, useCache bool) *Product {
-	k := fmt.Sprintf("product_model.GetProductByIdAndSizeAndColor[%d]", id)
+	k := productCacheKey(fmt.Sprintf("product_model.GetProductByIdAndSizeAndColor[%d][%d][%d]", id, sizeId, colorId))
 	if useCache {
 		if v, ok := cache.Cache.Get(k).(*Product); ok {
 			return v
@@ -512,7 +711,7 @@ func GetProductByIdAndSizeAndColor(id, sizeId, colorId int64, useCache bool) *Pr
 
 // 根据商品 relate_Id 跟 sizeId,获取商品信息
 func GetProductByIdAndSizeId(relateId, sizeId int64, useCache bool) *Product {
-	k := fmt.Sprintf("product_model.GetProductByRelateId[%d]SizeId[%d]", relateId, sizeId)
+	k := productCacheKey(fmt.Sprintf("product_model.GetProductByRelateId[%d]SizeId[%d]", relateId, sizeId))
 	if useCache {
 		if v, ok := cache.Cache.Get(k).(*Product); ok {
 			return v
@@ -531,7 +730,7 @@ func GetProductByIdAndSizeId(relateId, sizeId int64, useCache bool) *Product {
 
 // 根据商品 relate_Id 跟 colorId,获取商品信息
 func GetProductByIdAndColorId(relateId, colorId int64, useCache bool) *Product {
-	k := fmt.Sprintf("product_model.GetProductByRelateId[%d]ColorId[%d]", relateId, colorId)
+	k := productCacheKey(fmt.Sprintf("product_model.GetProductByRelateId[%d]ColorId[%d]", relateId, colorId))
 	if useCache {
 		if v, ok := cache.Cache.Get(k).(*Product); ok {
 			return v
@@ -553,7 +752,7 @@ func GetProductCountByRelateId(relateId int64, useCache bool) int64 {
 		Count int64
 	}
 	ret := &Ret{}
-	k := fmt.Sprintf("product_model.GetProductCountByRelateId(%d)", relateId)
+	k := productCacheKey(fmt.Sprintf("product_model.GetProductCountByRelateId(%d)", relateId))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).(int64); ok {
 			return ret
@@ -573,7 +772,7 @@ func GetProductCountByRelateId(relateId int64, useCache bool) int64 {
 }
 
 func DepartGetProductsByCatId(cId, saleZone, page, perPage, priceSort, saleSort, depart int64, words string, useCache bool) (products []*Product) {
-	k := fmt.Sprintf("product_model.GetProductsByCatId(%d).page(%d).perPage(%d).words(%d)", cId, page, perPage, words)
+	k := productCacheKey(fmt.Sprintf("product_model.DepartGetProductsByCatId(%d).saleZone(%d).page(%d).perPage(%d).priceSort(%d).saleSort(%d).depart(%d).words(%s)", cId, saleZone, page, perPage, priceSort, saleSort, depart, words))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).([]*Product); ok {
 			return ret
@@ -637,7 +836,7 @@ func DepartGetProductsByCatId(cId, saleZone, page, perPage, priceSort, saleSort,
 }
 
 func DepartGetProductCountByCatId(cId, saleZone, depart int64, words string, useCache bool) int64 {
-	k := fmt.Sprintf("product_model.DepartGetProductCountByCatId(%d)saleZone(%d)depart(%d)", cId, saleZone, depart)
+	k := productCacheKey(fmt.Sprintf("product_model.DepartGetProductCountByCatId(%d).saleZone(%d).depart(%d).words(%s)", cId, saleZone, depart, words))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).(int64); ok {
 			return ret

+ 1 - 1
go/gopath/src/fohow.com/apps/models/product_model/product_item.go

@@ -29,7 +29,7 @@ func (self *ProductItem) TableName() string {
 // 根据商品Id找出商品对应套装明细
 func GetPackageList(pId int64, useCache bool) (items []*ProductItem) {
 	//k := fmt.Sprintf("product_model.GetPackageList(%d)", pId)
-	k := cache.GetKey(cache.GetProductPackagetByPId, pId)
+	k := productCacheKey(cache.GetKey(cache.GetProductPackagetByPId, pId))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).([]*ProductItem); ok {
 			return ret

+ 4 - 4
go/gopath/src/fohow.com/apps/models/product_model/product_picture.go

@@ -33,7 +33,7 @@ type Picture struct {
 }
 
 func GetPicturesByPIdAndPType(pId, pType int64, useCache bool) (list []*Picture) {
-	k := fmt.Sprintf("product_model.GetPicturesByPId(%d).PType(%d)", pId, pType)
+	k := productCacheKey(fmt.Sprintf("product_model.GetPicturesByPId(%d).PType(%d)", pId, pType))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).([]*Picture); ok {
 			return ret
@@ -53,7 +53,7 @@ func GetPicturesByPIdAndPType(pId, pType int64, useCache bool) (list []*Picture)
 }
 
 func GetCoverByPId(pId int64, useCache bool) string {
-	k := fmt.Sprintf("product_model.GetCoverByPId(%d)", pId)
+	k := productCacheKey(fmt.Sprintf("product_model.GetCoverByPId(%d)", pId))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).(string); ok {
 			return ret
@@ -70,9 +70,9 @@ func GetCoverByPId(pId int64, useCache bool) string {
 	return url
 }
 
-//获取视频封面图片
+// 获取视频封面图片
 func GetVideoCoverByPId(pId int64, useCache bool) string {
-	k := fmt.Sprintf("product_model.GetCoverByPId(%d)", pId)
+	k := productCacheKey(fmt.Sprintf("product_model.GetVideoCoverByPId(%d)", pId))
 	if useCache {
 		if ret, ok := cache.Cache.Get(k).(string); ok {
 			return ret