abiao лет назад: 5
Родитель
Сommit
4cca7d0f36
100 измененных файлов с 11428 добавлено и 0 удалено
  1. 11 0
      go/gopath/src/fohow.com/Makefile
  2. 217 0
      go/gopath/src/fohow.com/apps/controllers/ad_controller/ad_controller.go
  3. 178 0
      go/gopath/src/fohow.com/apps/controllers/address_controller/address_controller.go
  4. 73 0
      go/gopath/src/fohow.com/apps/controllers/ali_controller/ali_controller.go
  5. 134 0
      go/gopath/src/fohow.com/apps/controllers/article_controller/article_controller.go
  6. 24 0
      go/gopath/src/fohow.com/apps/controllers/article_controller/init.go
  7. 249 0
      go/gopath/src/fohow.com/apps/controllers/balance_controller/balance_controller.go
  8. 32 0
      go/gopath/src/fohow.com/apps/controllers/balance_controller/init.go
  9. 62 0
      go/gopath/src/fohow.com/apps/controllers/captcha_controller/captcha_controller.go
  10. 42 0
      go/gopath/src/fohow.com/apps/controllers/category_controller/category_controller.go
  11. 23 0
      go/gopath/src/fohow.com/apps/controllers/category_controller/init.go
  12. 12 0
      go/gopath/src/fohow.com/apps/controllers/category_controller/navigate_icon_config_controller.go
  13. 30 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/comb_user_relation.go
  14. 86 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/init.go
  15. 48 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/order_receive_automatically.go
  16. 46 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/register_wxuser.go
  17. 73 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/sync_balance.go
  18. 46 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/three_user_relation.go
  19. 35 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/update_sold_count.go
  20. 72 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/user_complete_info.go
  21. 46 0
      go/gopath/src/fohow.com/apps/controllers/customer_service_controller/customer_service_controller.go
  22. 71 0
      go/gopath/src/fohow.com/apps/controllers/form_id_controller/form_id_controller.go
  23. 187 0
      go/gopath/src/fohow.com/apps/controllers/mp_controller/mp_controller.go
  24. 315 0
      go/gopath/src/fohow.com/apps/controllers/order_controller/cart_controller.go
  25. 31 0
      go/gopath/src/fohow.com/apps/controllers/order_controller/init.go
  26. 351 0
      go/gopath/src/fohow.com/apps/controllers/order_controller/order_controller.go
  27. 122 0
      go/gopath/src/fohow.com/apps/controllers/order_controller/settle_order_controller.go
  28. 402 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/after_pay_controller.go
  29. 47 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/init.go
  30. 52 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_balance_controller.go
  31. 48 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_cashcz_controller.go
  32. 82 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_controller.go
  33. 227 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_exchange_controller.go
  34. 128 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/recharge_controller.go
  35. 863 0
      go/gopath/src/fohow.com/apps/controllers/permit_controller/permit_controller.go
  36. 180 0
      go/gopath/src/fohow.com/apps/controllers/poster_controller/poster_controller.go
  37. 32 0
      go/gopath/src/fohow.com/apps/controllers/product_controller/init.go
  38. 231 0
      go/gopath/src/fohow.com/apps/controllers/product_controller/product_controller.go
  39. 40 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/cache_controller.go
  40. 175 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/generate_controller.go
  41. 32 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/intro_user_controller.go
  42. 54 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/order_dispatch_controller.go
  43. 28 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/test_controller.go
  44. 13 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/test_test.go
  45. 209 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/tmpl_controller.go
  46. 148 0
      go/gopath/src/fohow.com/apps/controllers/seckill_controller/seckill_controller.go
  47. 57 0
      go/gopath/src/fohow.com/apps/controllers/share_controller/share_controller.go
  48. 110 0
      go/gopath/src/fohow.com/apps/controllers/share_material_controller/share_material_controller.go
  49. 148 0
      go/gopath/src/fohow.com/apps/controllers/sms_controller/sms_controller.go
  50. 23 0
      go/gopath/src/fohow.com/apps/controllers/test_controller/init.go
  51. 82 0
      go/gopath/src/fohow.com/apps/controllers/test_controller/test_controller.go
  52. 30 0
      go/gopath/src/fohow.com/apps/controllers/tool_controller/init.go
  53. 91 0
      go/gopath/src/fohow.com/apps/controllers/tool_controller/qrcode_controller.go
  54. 26 0
      go/gopath/src/fohow.com/apps/controllers/tool_controller/tool_controller.go
  55. 140 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/binding_controller.go
  56. 189 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/binding_wx_phone_controller.go
  57. 37 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/init.go
  58. 296 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/invite_controller.go
  59. 60 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/one_click_binding_controller.go
  60. 383 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/user_controller.go
  61. 165 0
      go/gopath/src/fohow.com/apps/controllers/wxku_commodity_controller/wxku_commodity_controller.go
  62. 35 0
      go/gopath/src/fohow.com/apps/controllers/xcx_controller/init.go
  63. 138 0
      go/gopath/src/fohow.com/apps/controllers/xcx_controller/xcx_controller.go
  64. 53 0
      go/gopath/src/fohow.com/apps/helpers/invite_helper.go
  65. 179 0
      go/gopath/src/fohow.com/apps/helpers/notify_helper.go
  66. 96 0
      go/gopath/src/fohow.com/apps/helpers/wxbizdatacrypt.go
  67. 614 0
      go/gopath/src/fohow.com/apps/init.go
  68. 182 0
      go/gopath/src/fohow.com/apps/models/ad_model/ad.go
  69. 10 0
      go/gopath/src/fohow.com/apps/models/ad_model/init.go
  70. 118 0
      go/gopath/src/fohow.com/apps/models/ad_model/statistic.go
  71. 157 0
      go/gopath/src/fohow.com/apps/models/address_model/address.go
  72. 9 0
      go/gopath/src/fohow.com/apps/models/address_model/init.go
  73. 391 0
      go/gopath/src/fohow.com/apps/models/article_model/article.go
  74. 77 0
      go/gopath/src/fohow.com/apps/models/article_model/article_cat.go
  75. 125 0
      go/gopath/src/fohow.com/apps/models/article_model/category.go
  76. 11 0
      go/gopath/src/fohow.com/apps/models/article_model/init.go
  77. 350 0
      go/gopath/src/fohow.com/apps/models/balance_model/balance.go
  78. 359 0
      go/gopath/src/fohow.com/apps/models/balance_model/cash_balance.go
  79. 10 0
      go/gopath/src/fohow.com/apps/models/balance_model/init.go
  80. 212 0
      go/gopath/src/fohow.com/apps/models/balance_model/takecash.go
  81. 74 0
      go/gopath/src/fohow.com/apps/models/category_model/category.go
  82. 24 0
      go/gopath/src/fohow.com/apps/models/category_model/init.go
  83. 48 0
      go/gopath/src/fohow.com/apps/models/category_model/navigate_icon_config.go
  84. 106 0
      go/gopath/src/fohow.com/apps/models/channel_gzh_qrcode_model/channel_qrcode.go
  85. 66 0
      go/gopath/src/fohow.com/apps/models/channel_gzh_qrcode_model/channel_qrcode_result.go
  86. 13 0
      go/gopath/src/fohow.com/apps/models/channel_gzh_qrcode_model/init.go
  87. 53 0
      go/gopath/src/fohow.com/apps/models/channel_gzh_qrcode_model/push_after_sub.go
  88. 9 0
      go/gopath/src/fohow.com/apps/models/channel_parent_model/init.go
  89. 40 0
      go/gopath/src/fohow.com/apps/models/channel_parent_model/parent_signup_channel.go
  90. 11 0
      go/gopath/src/fohow.com/apps/models/channel_xcx_qrcode_model/init.go
  91. 47 0
      go/gopath/src/fohow.com/apps/models/channel_xcx_qrcode_model/sign_up_channel.go
  92. 35 0
      go/gopath/src/fohow.com/apps/models/channel_xcx_qrcode_model/sign_up_channel_qrcode_result.go
  93. 10 0
      go/gopath/src/fohow.com/apps/models/key_word_push_model/init.go
  94. 56 0
      go/gopath/src/fohow.com/apps/models/key_word_push_model/key_word_push.go
  95. 9 0
      go/gopath/src/fohow.com/apps/models/merchant_model/init.go
  96. 45 0
      go/gopath/src/fohow.com/apps/models/merchant_model/merchant.go
  97. 67 0
      go/gopath/src/fohow.com/apps/models/merchant_model/merchant_user.go
  98. 133 0
      go/gopath/src/fohow.com/apps/models/order_model/cart.go
  99. 12 0
      go/gopath/src/fohow.com/apps/models/order_model/init.go
  100. 0 0
      go/gopath/src/fohow.com/apps/models/order_model/order.go

+ 11 - 0
go/gopath/src/fohow.com/Makefile

@@ -0,0 +1,11 @@
+all: build  
+
+build:
+	go build -o fohowmall.com ./main.go
+
+clean:
+	go clean -i ./...
+
+test:
+	go test -race ./...
+

+ 217 - 0
go/gopath/src/fohow.com/apps/controllers/ad_controller/ad_controller.go

@@ -0,0 +1,217 @@
+package ad_controller
+
+import (
+	// "fmt"
+	// "math/rand"
+	"strconv"
+	// "strings"
+	"time"
+
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+	// "fohow.com/apps/models/activity_model"
+	"fohow.com/apps/models/ad_model"
+	// "fohow.com/apps/models/product_model"
+	// "fohow.com/cache"
+)
+
+var (
+	//不需要校验登录的Action
+	exceptCheckUserLoginAction   = []string{"GetItems", "GetItemsById", "Click", "Show"}
+	exceptCheckWxUserLoginAction = []string{"GetItems", "GetItemsById", "Click", "Show"}
+)
+
+type AdController struct {
+	apps.BaseController
+}
+
+func (self *AdController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+//根据广告图位置,获取广告图
+func (self *AdController) GetItems() {
+	cache, _ := self.GetBool("cache", true)
+	adCode := self.Ctx.Input.Param(":ad_code")
+	// k := fmt.Sprintf("/v1/ad/%s", adCode)
+	// if pCache == "false" {
+	// 	cache.Cache.Delete(k)
+	// }
+	ads := []*ad_model.AdItem{}
+
+	_ads := ad_model.GetAdsByCode(adCode, cache)
+	if _ads != nil {
+		ads = _ads
+	}
+
+	switch adCode {
+	case "pc_home":
+		//首页返回三个数组
+		type PcHome struct {
+			Position1 []*ad_model.AdItem `orm:"_"     json:"position_1"`
+			Position2 []*ad_model.AdItem `orm:"_"     json:"position_2"`
+			Position3 []*ad_model.AdItem `orm:"_"     json:"position_3"`
+			Position4 []*ad_model.AdItem `orm:"_"     json:"position_4"`
+		}
+		var pArr1 []*ad_model.AdItem
+		var pArr2 []*ad_model.AdItem
+		var pArr3 []*ad_model.AdItem
+		var pArr4 []*ad_model.AdItem
+		for i, item := range ads {
+			pos := i % 4
+			switch pos {
+			case 0:
+				pArr1 = append(pArr1, item)
+			case 1:
+				pArr2 = append(pArr2, item)
+			case 2:
+				pArr3 = append(pArr3, item)
+			case 3:
+				item.ClickUrl = ""
+				pArr4 = append(pArr4, item)
+			}
+		}
+		pcHome := new(PcHome)
+		pcHome.Position1 = pArr1
+		pcHome.Position2 = pArr2
+		pcHome.Position3 = pArr3
+		pcHome.Position4 = pArr4
+		self.Data["json"] = pcHome
+	default:
+		self.Data["json"] = ads
+	}
+	self.ServeJSON()
+}
+
+//根据广告位id,获取广告图
+func (self *AdController) GetItemsById() {
+	id, _ := self.GetInt64("ad_pid")
+	useCache, _ := self.GetBool("cache", true)
+
+	var ads []*ad_model.AdItem
+	ads = ad_model.GetAdsByPosId(id, useCache)
+	self.Data["json"] = ads
+	self.ServeJSON()
+}
+
+//点击广告图
+func (self *AdController) Click() {
+	useCache := false //self.GetString("cache") != "false"
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":ad_item_id"), 10, 64)
+	// 当大后台修改链接时,这里如果取了缓存数据后再Save会重新覆盖旧的链接进入。
+	// 所以不用缓存,userCache恒等于false
+	ad := ad_model.GetItemById(id, useCache)
+	if ad == nil {
+		self.ServeJSON()
+		return
+	}
+	ad.ClickTimes = ad.ClickTimes + 1
+	ad.Save()
+	wxUId := self.GetCurrentWxUserId()
+	//if wxUser != nil {
+	clickStat := ad_model.GetClickStatByWxUId(ad.Id, wxUId)
+	if clickStat == nil {
+		go new(ad_model.AdItemClickStat).Create(ad.Id, wxUId, self.Ctx.Input.IP())
+	} else {
+		clickStat.ClickTimes = clickStat.ClickTimes + 1
+		clickStat.ClickLastTime = time.Now()
+		clickStat.Save()
+	}
+	//}
+	//self.Redirect(ad.ClickUrl, 302)
+	self.ServeJSON()
+}
+
+//广告图展示统计
+func (self *AdController) Show() {
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":ad_item_id"), 10, 64)
+	ad := ad_model.GetItemById(id, false)
+	if ad == nil {
+		self.ServeJSON()
+		return
+	}
+	ad.ShowTimes = ad.ShowTimes + 1
+	ad.Save()
+	wxUId := self.GetCurrentWxUserId()
+	//if wxUser != nil {
+	showStat := ad_model.GetShowStatByWxUId(ad.Id, wxUId)
+	if showStat == nil {
+		go new(ad_model.AdItemShowStat).Create(ad.Id, wxUId, self.Ctx.Input.IP())
+	} else {
+		showStat.ShowTimes = showStat.ShowTimes + 1
+		showStat.ShowLastTime = time.Now()
+		showStat.Save()
+	}
+	//}
+	self.ServeJSON()
+}
+
+func getPcHomeAds(code string) (ads []*ad_model.AdItem) {
+	// adCodeArray := strings.Split(code, "_")
+	// adType, _ := adCodeArray[3]
+	// switch adType {
+	// // case "banner":
+	// default:
+	// 	ads = ad_model.GetAdsByCode(code)
+	// }
+
+	// return ads
+	return nil
+}
+
+// // 获取砍价活动show、order两个页面banner图;由以下几个部分组成:
+// // 1、砍价自定义的图片,在ad_position表中找出code="activity_123"(123是活动ID)
+// // 2、如果是代理商的图片,在ad_position表中找出code="activity_dailishop_123"(123是商家ID)
+// // 3、砍价默认图,在ad_position表中找出code="activity_banner_default"
+// func getAdsOfActivityBanner(code string) (ads []*ad_model.AdItem) {
+// 	adCodeArray := strings.Split(code, "_")
+// 	actId, _ := strconv.ParseInt(adCodeArray[1], 10, 64)
+// 	act := activity_model.GetActivityById(actId, true)
+// 	var sId int64
+// 	if act != nil {
+// 		sId = act.ShopId
+// 	} else {
+// 		sId = 0
+// 	}
+// 	dailishop := fmt.Sprintf("activity_dailishop_%d", sId)
+// 	actBannerDdefault := "activity_banner_default"
+// 	ads = ad_model.GetAdsOfActivityBanner(code, dailishop, actBannerDdefault)
+// 	return ads
+// }
+
+// // 获取BK砍价show、order两个页面banner图;由以下几个部分组成:
+// // 1、砍价自定义的图片,在ad_position表中找出code="activity_bk_123"(123是商品ID)
+// // 2、如果是代理商的图片,在ad_position表中找出code="activity_dailishop_123"(123是商家ID)
+// // 3、砍价默认图,在ad_position表中找出code="bk_banner_default"
+// func getAdsOfBkBanner(code string) (ads []*ad_model.AdItem) {
+// 	adCodeArray := strings.Split(code, "_")
+// 	pId, _ := strconv.ParseInt(adCodeArray[2], 10, 64)
+// 	pd := product_model.GetProductById(pId, true)
+// 	dailishop := fmt.Sprintf("activity_dailishop_%d", pd.ShopId)
+// 	bkBannerDdefault := "bk_banner_default"
+// 	ads = ad_model.GetAdsOfActivityBanner(code, dailishop, bkBannerDdefault)
+// 	return ads
+// }
+
+// // 获取夺宝详情页面banner图;由以下几个部分组成:
+// // 1、宝贝特殊广告,在ad_position表中找出code="duobao_123"(123是商品ID)
+// // 2、砍价默认图,在ad_position表中找出code="duobao_banner_default"
+// func getAdsOfDuobaoDetailBanner(code string, shopId int64) (ads []*ad_model.AdItem) {
+// 	bannerDdefault := "duobao_banner_default"
+// 	dailiShop := fmt.Sprintf("duobao_shop_%d", shopId)
+// 	ads = ad_model.GetAdsOfActivityBanner(code, dailiShop, bannerDdefault)
+// 	return ads
+// }
+
+// // 获取拼团详情页面banner图;由以下几个部分组成:
+// // 1、宝贝特殊广告,在ad_position表中找出code="pintuan_123"(123是商品ID)
+// // 2、砍价默认图,在ad_position表中找出code="duobao_banner_default"
+// func getAdsOfPintuanDetailBanner(code string) (ads []*ad_model.AdItem) {
+// 	bannerDdefault := "pintuan_banner_default"
+// 	ads = ad_model.GetAdsOfActivityBanner(code, "noexist", bannerDdefault)
+// 	return ads
+// }

+ 178 - 0
go/gopath/src/fohow.com/apps/controllers/address_controller/address_controller.go

@@ -0,0 +1,178 @@
+package address_controller
+
+import (
+	// "fmt"
+	"strconv"
+	// "time"
+	// "d"
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+	// "fohow.com/apps/controllers/user_controller"
+	// "fohow.com/apps/models/activity_model"
+	// "fohow.com/apps/models/user_model"
+	// "fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/address_model"
+	// "fohow.com/libs/wx_mp"
+)
+
+var (
+	//不需要校验登录的Action
+	exceptCheckUserLoginAction   = []string{"List", "DefaultAddress", "CreateAddress", "UpdateAddress", "DeleteAddress", "SetDefault"}
+	exceptCheckWxUserLoginAction = []string{}
+)
+
+type AddressController struct {
+	apps.BaseController
+}
+
+func (self *AddressController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+//获取用户地址列表
+func (self *AddressController) List() {
+	sort := self.GetString("sort")
+	if sort == "" {
+		sort = "created_at"
+	}
+	//uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	addresses := address_model.GetUserAddressList(wxUId, sort)
+	self.Data["json"] = addresses
+	self.ServeJSON()
+}
+
+//取默认地址
+func (self *AddressController) DefaultAddress() {
+	//uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	address := address_model.GetUserDefaultAddress(wxUId)
+	self.Data["json"] = address
+	self.ServeJSON()
+}
+
+func (self *AddressController) CreateAddress() {
+	contact := self.GetString("contact")
+	tel := self.GetString("tel")
+	province := self.GetString("province")
+	city := self.GetString("city")
+	address := self.GetString("address")
+	district := self.GetString("district")
+	setDefault, _ := self.GetInt64("set_default")
+
+	if len(tel) != 11 {
+		self.ReturnError(404, apps.PhoneInvalid, "", nil)
+	}
+	if address == "" || contact == "" || tel == "" {
+		self.ReturnError(404, apps.ParamsRequired, "", nil)
+	}
+	uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+
+	addresses := address_model.GetAddressesByWxUId(wxUId)
+	if len(addresses) >= 5 {
+		self.ReturnError(404, apps.UserAddressFull, "", nil)
+	}
+
+	//无默认地址的情况下,新增的地址为默认地址
+	hasDefaultAddr := false
+	for _, item := range addresses {
+		if item.State == 1 {
+			hasDefaultAddr = true
+			break
+		}
+	}
+
+	item := address_model.CreateAddress(wxUId, uId, contact, tel, province, city, address, "", "", district)
+	if (setDefault == 1 || !hasDefaultAddr) && item != nil {
+		item.SetDefault()
+		item.State = 1
+	}
+	self.Data["json"] = item
+	self.ServeJSON()
+}
+
+func (self *AddressController) UpdateAddress() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	item := address_model.GetUserAddressById(id)
+	//uId := self.GetCurrentUserId()
+	//if item.UserId != uId {
+	//	self.ReturnError(404, apps.NoExist, "", nil)
+	//}
+	wxUId := self.GetCurrentWxUserId()
+	if item.WxUserId != wxUId {
+		self.ReturnError(404, apps.NoExist, "", nil)
+	}
+	contact := self.GetString("contact")
+	tel := self.GetString("tel")
+	province := self.GetString("province")
+	city := self.GetString("city")
+	address := self.GetString("address")
+	district := self.GetString("district")
+	setDefault, _ := self.GetInt64("set_default")
+
+	if len(tel) != 11 {
+		self.ReturnError(404, apps.ParamsError, "", nil)
+	}
+	if address == "" || contact == "" || tel == "" {
+		self.ReturnError(404, apps.ParamsRequired, "", nil)
+	}
+	item = item.Update(contact, tel, province, city, address, "", "", district)
+
+	if setDefault == 1 {
+		item.SetDefault()
+		item.State = 1
+	}
+
+	self.Data["json"] = item
+	self.ServeJSON()
+}
+
+func (self *AddressController) DeleteAddress() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	item := address_model.GetUserAddressById(id)
+	//uId := self.GetCurrentUserId()
+	//if item.UserId != uId {
+	//	self.ReturnError(403, apps.AddressNotMatch, "", nil)
+	//}
+	wxUId := self.GetCurrentWxUserId()
+	if item.WxUserId != wxUId {
+		self.ReturnError(404, apps.NoExist, "", nil)
+	}
+	//如果删除的是默认地址,设置一个默认地址
+	if item.State == 1 {
+		allAddress := address_model.GetUserAddressList(wxUId, "id")
+		for _, address := range allAddress {
+			if address.State == 0 {
+				address.State = 1
+				address.Save()
+				break
+			}
+		}
+	}
+	go item.Delete()
+	self.ServeJSON()
+}
+
+func (self *AddressController) SetDefault() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	//uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	address := address_model.GetAddressByWxUIdAndId(wxUId, id)
+
+	if address != nil {
+		address.SetDefault()
+		address.State = 1
+	} else {
+		self.ReturnError(404, apps.NoExist, "", nil)
+	}
+
+	self.Data["json"] = address
+	self.ServeJSON()
+}

+ 73 - 0
go/gopath/src/fohow.com/apps/controllers/ali_controller/ali_controller.go

@@ -0,0 +1,73 @@
+package ali_controller
+
+import (
+	"fmt"
+	"math/rand"
+	// "strconv"
+	// "strings"
+	"time"
+
+	// "d"
+	"github.com/alidayu"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+	// "fohow.com/apps/controllers/user_controller"
+	// "fohow.com/apps/models/activity_model"
+	// "fohow.com/apps/models/blacklist_model"
+	// "fohow.com/apps/models/order_model"
+	// "fohow.com/apps/models/product_model"
+	// "fohow.com/apps/models/shop_model"
+	"fohow.com/apps/models/user_model"
+	// "fohow.com/apps/models/vas_model"
+	"fohow.com/cache"
+	// "fohow.com/libs/wx_mp"
+	// "fohow.com/libs/zhyidong"
+)
+
+var (
+	//不需要校验登录的Action
+	exceptCheckUserLoginAction   = []string{"*"}
+	exceptCheckWxUserLoginAction = []string{"*"}
+)
+
+type AliController struct {
+	apps.BaseController
+}
+
+func (self *AliController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+func (self *AliController) Dayu() {
+
+	tel := self.Ctx.Input.Param(":tel")
+	if len(tel) != 11 {
+		self.ReturnError(403, apps.PhoneInvalid, "", nil)
+	}
+	user := self.GetCurrentUser(false)
+	if user.Tel != "" {
+		self.ReturnError(403, apps.PhoneExist, "", nil)
+	}
+	if user_model.IsTelExist(tel, false) {
+		self.ReturnError(403, apps.PhoneExist, "", nil)
+	}
+	code := fmt.Sprintf("%06d", rand.Int63n(999999))
+	k := fmt.Sprintf("dayu[%s]", tel)
+	cache.Cache.Put(k, code, 5*time.Minute)
+
+	alidayu.AppKey = "23297873"
+	alidayu.AppSecret = "896c8a056098464963f2e6f142dea159"
+	sms := fmt.Sprintf("{\"code\": \"%s\",\"product\":\"美月盒子\"}", code)
+	success, resp := alidayu.SendSMS(tel, "注册验证", "SMS_4420686", sms)
+	if !success {
+		beego.BeeLogger.Error("alidayu send msg err, user_id=%d,tel=%s, resp=%s", user.Id, tel, resp)
+	} else {
+		beego.BeeLogger.Warn("alidayu send msg ok,user_id=%d,tel=%s", user.Id, tel)
+	}
+	// self.Data["json"] = code
+	self.ServeJSON()
+}

+ 134 - 0
go/gopath/src/fohow.com/apps/controllers/article_controller/article_controller.go

@@ -0,0 +1,134 @@
+package article_controller
+
+import (
+	//"fmt"
+	// "math/rand"
+	"strconv"
+	// "strings"
+	"time"
+
+	// "d"
+	// "github.com/alidayu"
+	// "github.com/astaxie/beego"
+	// "github.com/astaxie/beego/context"
+	// "github.com/astaxie/beego/httplib"
+
+	"fohow.com/apps"
+	// "fohow.com/apps/controllers/user_controller"
+	"fohow.com/apps/models/article_model"
+	//"fohow.com/apps/models/balance_model"
+	// "fohow.com/apps/models/order_model"
+	// "fohow.com/apps/models/product_model"
+	//"fohow.com/apps/models/user_model"
+	// "fohow.com/apps/models/vas_model"
+	"fohow.com/cache"
+	//"fohow.com/libs/tool"
+	//"fohow.com/libs/wx_mp"
+)
+
+//文章列表
+// func (self *ArticleController) GetList() {
+// 	_catId := self.Ctx.Input.Param(":cat_id")
+// 	catId, _ := strconv.ParseInt(_catId, 10, 64)
+// 	// cat_id, _ := self.GetInt64("cat_id", 12)
+// 	page, _ := self.GetInt64("page", 1)
+// 	perPage, _ := self.GetInt64("per_page", 20)
+// 	if perPage <= 0 || perPage > 100 {
+// 		perPage = 20
+// 	}
+// 	type ArticlesWithCat struct {
+// 		Articles     []*article_model.Article  `orm:"-"         json:"articles"`
+// 		ArticleCat   *article_model.ArticleCat `orm:"-"         json:"article_cat"`
+// 		ArticleCount int64                     `orm:"-"         json:"article_count"`
+// 	}
+// 	articleList := article_model.GetListByCatId(catId, page, perPage, (true && !self.IsDev()))
+// 	articleCat := article_model.GetArticleCatById(catId, true)
+// 	articleCount := article_model.GetListCountByCatId(catId)
+
+// 	list := new(ArticlesWithCat)
+// 	list.Articles = articleList
+// 	list.ArticleCat = articleCat
+// 	list.ArticleCount = articleCount
+// 	self.Data["json"] = list
+// 	self.ServeJSON()
+// }
+
+// //热门新闻
+// func (self *ArticleController) GetHotest() {
+// 	useCache, _ := self.GetBool("cache", true)
+// 	list := article_model.Hotest(useCache)
+// 	self.Data["json"] = list
+// 	self.ServeJSON()
+// }
+
+//文章详情
+func (self *ArticleController) GetDetail() {
+	_id := self.Ctx.Input.Param(":article_id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	//2018/1/5暂不获取
+	// showAround, _ := self.GetBool("show_around", true)
+	// showCatInfo, _ := self.GetBool("show_cat", true)
+	article := article_model.GetById(id, useCache)
+	if article == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	if self.IsWxClient() {
+		wxUId := self.GetCurrentWxUserId()
+		if wxUId > 0 {
+			now := time.Now().Unix()
+			k := cache.GetKey(cache.ArticleDetailOpenBenefitByAId, id, wxUId, now)
+			v := (now + wxUId*3) * 2
+			cache.Cache.Put(k, v, 10*time.Minute)
+			article.OTime = now
+		}
+	}
+
+	type ArticleDetail struct {
+		Article *article_model.Article `json:"article"`
+		// 2018/1/5暂不获取拓展阅读
+		// PreOne  *article_model.Article `json:"pre_one"`
+		// NextOne *article_model.Article `json:"next_one"`
+	}
+
+	if article != nil {
+		article.Cover = self.GetFullImgUrl(article.Cover)
+		article.SImg = self.GetFullImgUrl(article.SImg)
+		article.CTime = article.CreatedAt.Unix()
+		// 2018/1/5暂不不显示上级
+		// if showCatInfo {
+		// 	articleCat := article_model.GetParentCats(article.ArticleCatId)
+		// 	article.ArticleCat = articleCat
+		// }
+		go article.AddClick()
+	}
+
+	ad := new(ArticleDetail)
+	ad.Article = article
+	// 2018/1/5暂不获取拓展阅读
+	// if showAround {
+	// 	ad.PreOne, ad.NextOne = article.GetArtcleIdForDetailPage(useCache)
+	// 	if ad.PreOne != nil {
+	// 		ad.PreOne.Cover = self.GetFullImgUrl(ad.PreOne.Cover)
+	// 		ad.PreOne.CTime = ad.PreOne.CreatedAt.Unix()
+	// 	}
+	// 	if ad.NextOne != nil {
+	// 		ad.NextOne.Cover = self.GetFullImgUrl(ad.NextOne.Cover)
+	// 		ad.NextOne.CTime = ad.NextOne.CreatedAt.Unix()
+	// 	}
+	// }
+
+	self.Data["json"] = ad
+	self.ServeJSON()
+}
+
+//通知类文章
+// func (self *ArticleController) GetNotices() {
+// 	_id := self.Ctx.Input.Param(":cat_id")
+// 	cId, _ := strconv.ParseInt(_id, 10, 64)
+// 	n, _ := self.GetInt64("n", 5)
+// 	useCache, _ := self.GetBool("cache", true)
+// 	list := article_model.Notices(cId, n, useCache)
+// 	self.Data["json"] = list
+// 	self.ServeJSON()
+// }

+ 24 - 0
go/gopath/src/fohow.com/apps/controllers/article_controller/init.go

@@ -0,0 +1,24 @@
+package article_controller
+
+import (
+	"github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction = []string{"GetList",
+		"GetHotest", "GetNotices", "GetDetail"}
+	exceptCheckWxUserLoginAction = []string{"GetDetail"}
+)
+
+type ArticleController struct {
+	apps.BaseController
+}
+
+func (self *ArticleController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 249 - 0
go/gopath/src/fohow.com/apps/controllers/balance_controller/balance_controller.go

@@ -0,0 +1,249 @@
+package balance_controller
+
+import (
+	// "crypto/md5"
+	// "encoding/hex"
+	"strconv"
+	"sync"
+
+	// "github.com/astaxie/beego"
+	// "github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+	// 	// "fohow.com/apps/controllers/user_controller"
+	// 	"fohow.com/apps/models/activity_model"
+	// 	"fohow.com/apps/models/address_model"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/user_model"
+	"time"
+)
+
+const (
+	TAKE_CASH_AMOUNT_LIMIT_MIN = 5000    //50元
+	TAKE_CASH_AMOUNT_LIMIT_MAX = 2000000 //2w
+)
+
+//代金券和代金券余额
+func (self *BalanceController) GetBalanceInfo() {
+	type BalanceInfo struct {
+		Total          int64 `orm:"-" json:"total"`            //余额,单位分
+		ShowInviteMode int64 `orm:"-" json:"show_invite_mode"` //是否群主
+	}
+	//user := self.GetCurrentUser(true)
+	wxUId := self.GetCurrentWxUserId()
+	wxUser := user_model.GetWxUserById(wxUId, true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+	info := new(BalanceInfo)
+	info.Total = balance_model.GetUserTotalBalance(wxUId)
+	info.ShowInviteMode = wxUser.ShowInviteMode
+	self.Data["json"] = info
+	self.ServeJSON()
+}
+
+//代金券变动列表
+func (self *BalanceController) GetBalanceList() {
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	cache, _ := self.GetBool("cache", false)
+
+	//user := self.GetCurrentUser(cache)
+	wxUId := self.GetCurrentWxUserId()
+
+	list := balance_model.GetBalanceListByWxUId(wxUId, page, perPage, cache)
+	count := balance_model.GetBalanceCountByWxUId(wxUId)
+	type BalanceInfo struct {
+		BalanceList  []*balance_model.Balance `orm:"-" json:"balance_list"`
+		BalanceCount int64                    `orm:"-" json:"balance_count"`
+	}
+	for _, item := range list {
+		item.SourceName = item.GetSourceName()
+		item.CTime = item.CreatedAt.Unix()
+	}
+
+	self.Data["json"] = &BalanceInfo{BalanceList: list, BalanceCount: count}
+	self.ServeJSON()
+}
+
+//资金详情
+func (self *BalanceController) GetBalanceDetail() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	//uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	item := balance_model.GetBalanceById(id)
+	if item == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	if item.WxUserId != wxUId {
+		self.ReturnError(403, apps.BalanceNotExist, "", nil)
+	}
+	item.CTime = item.CreatedAt.Unix()
+	item.SourceName = item.GetSourceName()
+	self.Data["json"] = item
+	self.ServeJSON()
+}
+
+//现金账户变动列表
+func (self *BalanceController) GetCashBalanceList() {
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	cache, _ := self.GetBool("cache", false)
+
+	wxUser := self.GetCurrentWxUser(cache)
+	// wxUser = user_model.GetWxUserById(2, true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+	list := balance_model.GetCashBalanceListByWxUId(wxUser.Id, page, perPage, cache)
+	count := balance_model.GetCashBalanceCountByWxUId(wxUser.Id)
+	type BalanceInfo struct {
+		BalanceList  []*balance_model.CashBalance `orm:"-" json:"balance_list"`
+		BalanceCount int64                        `orm:"-" json:"balance_count"`
+	}
+	for _, item := range list {
+		// item.SourceName = item.GetSourceName()
+		item.CTime = item.CreatedAt.Unix()
+	}
+
+	self.Data["json"] = &BalanceInfo{BalanceList: list, BalanceCount: count}
+	self.ServeJSON()
+}
+
+//现金账户信息
+func (self *BalanceController) GetCashBalanceInfo() {
+	type BalanceInfo struct {
+		Available int64 `orm:"-" json:"available"` //对应页面上可提现余额,单位分,进账+出账
+		Total     int64 `orm:"-" json:"total"`     //对应页面上累积收入,单位分,只算进账
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+	// wxUser = user_model.GetWxUserById(2, true)
+	info := new(BalanceInfo)
+	//余额
+	info.Available = balance_model.GetCashTotalBalance(wxUser.Id)
+	info.Total = balance_model.GetCashEnterBalance(wxUser.Id)
+
+	self.Data["json"] = info
+	self.ServeJSON()
+}
+
+var takeCashLock sync.Mutex
+
+// 提现
+func (self *BalanceController) TakeCash() {
+	//单位:分
+	amount, _ := self.GetInt64("amount")
+	if amount <= 0 {
+		self.ReturnError(403, apps.TakeCashAmountInvalid, "", nil)
+	}
+
+	if amount < TAKE_CASH_AMOUNT_LIMIT_MIN || amount > TAKE_CASH_AMOUNT_LIMIT_MAX {
+		self.ReturnError(403, []string{"amountLimit", "提现单日限额2W,最低50元"}, "", nil)
+	}
+	user := self.GetCurrentUser(false)
+
+	/*	if user.IsCertification != 1 {
+		self.ReturnError(403, []string{"notCertificate", "用户尚未认证"}, "", nil)
+	}*/
+
+	wxUser := self.GetCurrentWxUser(false)
+	todayOrderList := balance_model.GetTakeCashOrderListByWxUIdAndTime(wxUser.Id, time.Now(), false)
+
+	if len(todayOrderList) > 0 {
+		self.ReturnError(403, []string{"todaySubmitedTakeCashOrder", "温馨提示,目前单日仅限提现1次"}, "", nil)
+	}
+
+	if user.BankAccount == "" || user.AccountName == "" {
+		self.ReturnError(403, []string{"notBindbank", "请完善您的银行卡信息"}, "", nil)
+	}
+
+	canExtract := balance_model.GetCashTotalBalance(wxUser.Id)
+
+	//余额不足
+	if canExtract < amount {
+		self.ReturnError(403, apps.BalanceNotEnough, "", nil)
+	}
+
+	takeCashLock.Lock()
+	defer takeCashLock.Unlock()
+
+	o := new(balance_model.TakeCashOrder).Create(wxUser.Id, amount)
+	if o != nil {
+		new(balance_model.CashBalance).Create(wxUser.Id, -amount, balance_model.CASH_SOURCE_TAKE_CASH,
+			o.OrderId, balance_model.CASH_SOURCE_TAKE_CASH_NAME)
+		o.AccountName = user.AccountName
+		o.BankAccount = user.BankAccount
+		o.BankName = user.BankName
+		o.Save()
+	}
+
+	self.Data["json"] = o
+	self.ServeJSON()
+}
+
+//提现流列表
+func (self *BalanceController) GetTakeCashOrders() {
+
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	cache, _ := self.GetBool("cache", false)
+
+	//user := self.GetCurrentUser(cache)
+	wxUId := self.GetCurrentWxUserId()
+
+	list := balance_model.GetTakeCashOrderListByWxUId(wxUId, page, perPage, cache)
+	listCount := balance_model.GetTakeCashOrderCountByWxUId(wxUId, cache)
+
+	type Order struct {
+		Id    int64 ` json:"id"`     // int(11)
+		WxUId int64 ` json:"wx_uid"` // int(11)
+		//OrderId      string ` json:"order_id"`    // varchar(64)
+		//TradeNo      string ` json:"trade_no"`    // varchar(64)
+		Count      int64  ` json:"count"`       // bigint(20)
+		State      int64  ` json:"pay_state"`   // tinyint(1)
+		AuditState int64  ` json:"audit_state"` // tinyint(1)
+		StateCN    string ` json:"state_cn"`
+		PaiedAt    int64  ` json:"paied_at"` // int(11)
+		Remark     string ` json:"remark"`   // varchar(255)
+		//ExpcPayAt  time.Time ` json:"expc_pay_at"` // datetime
+		CreatedAt int64 ` json:"created_at"` // datetime
+	}
+
+	retList := make([]*Order, 0, 0)
+
+	for _, item := range list {
+		order := new(Order)
+
+		order.Id = item.Id
+		order.WxUId = item.WxUId
+		order.Count = item.Count
+		order.StateCN = item.GetStateCn()
+		order.PaiedAt = item.PaiedAt
+		order.CreatedAt = item.CreatedAt.Unix()
+		order.State = item.State
+		order.AuditState = item.AuditState
+
+		retList = append(retList, order)
+	}
+
+	type Ret struct {
+		List      []*Order `json:"list"`
+		ListCount int64    `json:"list_count"`
+	}
+
+	self.Data["json"] = &Ret{ListCount: listCount, List: retList}
+	self.ServeJSON()
+}

+ 32 - 0
go/gopath/src/fohow.com/apps/controllers/balance_controller/init.go

@@ -0,0 +1,32 @@
+package balance_controller
+
+import (
+	// "fmt"
+	// "os"
+	// "net/url"
+	// "strings"
+
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	// "github.com/go-wkhtmltoimage"
+	// "github.com/skip2/go-qrcode"
+
+	"fohow.com/apps"
+)
+
+var (
+	//需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"GetCashBalanceList", "GetCashBalanceInfo", "GetBalanceInfo", "GetBalanceDetail", "GetBalanceList", "GetTakeCashOrders"}
+	exceptCheckWxUserLoginAction = []string{""}
+)
+
+type BalanceController struct {
+	apps.BaseController
+}
+
+func (self *BalanceController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	// beego.BeeLogger.Info("invote controller Init func")
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 62 - 0
go/gopath/src/fohow.com/apps/controllers/captcha_controller/captcha_controller.go

@@ -0,0 +1,62 @@
+package captcha_controller
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/astaxie/beego"
+	// "github.com/astaxie/beego/cache"
+	"github.com/astaxie/beego/context"
+	"github.com/astaxie/beego/utils/captcha"
+
+	"fohow.com/apps"
+	"fohow.com/cache"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction   = []string{"*"}
+	exceptCheckWxUserLoginAction = []string{"*"}
+	cpt                          *captcha.Captcha
+)
+
+func (self *CaptchaController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+	cpt = captcha.NewWithFilter("/captcha/", cache.Cache)
+}
+
+type CaptchaController struct {
+	apps.BaseController
+}
+
+//获取图片验证码
+func (self *CaptchaController) GetCaptcha() {
+	id, err := cpt.CreateCaptcha()
+	var img string
+	if err != nil {
+		beego.BeeLogger.Error("get captcha err=%s", err)
+		self.ReturnError(403, apps.GetCaptchaError, "", nil)
+	}
+	img = fmt.Sprintf("%s%s%s.png?reload=%d",
+		beego.AppConfig.String("ApiHost"), cpt.URLPrefix, id, time.Now().Unix())
+	self.SetSession("captcha_id", id)
+	type VC struct {
+		Id  string `json:"id"`
+		Src string `json:"src"`
+	}
+	vc := &VC{Id: id, Src: img}
+	self.Data["json"] = vc
+	self.ServeJSON()
+}
+
+//校验图片验证码
+func (self *CaptchaController) VerifyCaptcha() {
+	id := self.GetString("id")
+	code := self.GetString("code")
+	result := cpt.Verify(id, code)
+	self.Data["json"] = result
+	self.ServeJSON()
+}

+ 42 - 0
go/gopath/src/fohow.com/apps/controllers/category_controller/category_controller.go

@@ -0,0 +1,42 @@
+package category_controller
+
+import (
+	// "fmt"
+	"strings"
+	// "time"
+
+	// "github.com/astaxie/beego"
+	// "github.com/astaxie/beego/context"
+
+	"fohow.com/apps/models/category_model"
+)
+
+func (self *CategoryController) Get() {
+	platforms := self.GetString("platform")
+	cache, _ := self.GetBool("cache", true)
+	var res map[string]interface{} = make(map[string]interface{})
+	for _, p := range strings.Split(platforms, ",") {
+		cats := category_model.GetCatsByPlatform(p, cache)
+		res[p] = cats
+	}
+	// k := fmt.Sprintf("/v1/menus/?platform=%s", platforms)
+	// if self.GetString("cache") == "false" {
+	// 	cache.Cache.Delete(k)
+	// }
+	// if res, ok := cache.Cache.Get(k).(map[string]interface{}); ok {
+	// 	self.Data["json"] = res
+	// 	self.ServeJSON()
+	// 	return
+	// } else {
+	// 	beego.BeeLogger.Info("get menus from cache err.cache key=[%s]", k)
+	// }
+	// var res map[string]interface{} = make(map[string]interface{})
+
+	// sec, _ := beego.AppConfig.Int64("MenusCacheSecond")
+	// err := cache.Cache.Put(k, res, 60*time.Minute)
+	// if err != nil {
+	// 	beego.BeeLogger.Error("CtrCache.Put", k, err)
+	// }
+	self.Data["json"] = res
+	self.ServeJSON()
+}

+ 23 - 0
go/gopath/src/fohow.com/apps/controllers/category_controller/init.go

@@ -0,0 +1,23 @@
+package category_controller
+
+import (
+	"github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction   = []string{"Get", "GetNavigateIcons"}
+	exceptCheckWxUserLoginAction = []string{"Get", "GetNavigateIcons"}
+)
+
+type CategoryController struct {
+	apps.BaseController
+}
+
+func (self *CategoryController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 12 - 0
go/gopath/src/fohow.com/apps/controllers/category_controller/navigate_icon_config_controller.go

@@ -0,0 +1,12 @@
+package category_controller
+
+import "fohow.com/apps/models/category_model"
+
+func (self *CategoryController) GetNavigateIcons() {
+
+	cache, _ := self.GetBool("cache", true)
+	icons := category_model.GetNavigateIconsByState(1, cache)
+
+	self.Data["json"] = icons
+	self.ServeJSON()
+}

+ 30 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/comb_user_relation.go

@@ -0,0 +1,30 @@
+package cron_controller
+
+import (
+	"fohow.com/apps/models/user_model"
+)
+
+//修复会员内部关系
+func combUserRelation() {
+	wxUserId := int64(1)
+	//从公司会员开始修复
+	wxUser := user_model.GetWxUserById(wxUserId, true)
+	//更改下级群主关系
+	inviteList := user_model.GetWxUsersByInviteIdAll(wxUser.Id, false)
+	go UpdateClassInner(inviteList, wxUser.IntroInnerNo, wxUser.Id)
+}
+
+func UpdateClassInner(list []*user_model.WxUser, innerNo string, inviteId int64) {
+	for _, item := range list {
+		if item != nil {
+			item.IntroInnerNo = innerNo
+			introArea := user_model.GetAvailableIntroArea(inviteId)
+			introInnerNo := innerNo + introArea
+			item.IntroArea = introArea
+			item.IntroInnerNo = introInnerNo
+			item.Save()
+			secondList := user_model.GetWxUsersByInviteIdAll(item.Id, false)
+			UpdateClassInner(secondList, item.IntroInnerNo, item.Id)
+		}
+	}
+}

+ 86 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/init.go

@@ -0,0 +1,86 @@
+package cron_controller
+
+import (
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+	"fohow.com/libs/tool"
+	"strings"
+)
+
+var (
+	//不需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"Cron"}
+	exceptCheckWxUserLoginAction = []string{"Cron"}
+)
+
+type CronController struct {
+	apps.BaseController
+}
+
+func (self *CronController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	// beego.BeeLogger.Info("invote controller Init func")
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+// http://api.d5c360.com/v1/cron/:name(project_loan)
+func (self *CronController) Cron() {
+	name := self.Ctx.Input.Param(":name")
+	ip := self.Ctx.Input.IP()
+	//beego.BeeLogger.Error("get ip: %s", ip)
+	beego.BeeLogger.Info("Request ip ", strings.Split(beego.AppConfig.String("inetIp"), ","))
+	// 该接口要指定的ip列表访问
+	if inAllowedList, _ := tool.Contain(ip, strings.Split(beego.AppConfig.String("inetIp"), ",")); inAllowedList {
+		//beego.BeeLogger.Error("inAllowedList: %v", inAllowedList)
+		if name == "orderCompleteDispatchTime" {
+			isUpdateTime, _ := self.GetBool("is_update_time", false)
+			OrderCompleteDispatchTime(isUpdateTime)
+		} else {
+			exec(name)
+		}
+		beego.BeeLogger.Info("Request ip[%s] in AllowedList ip, task execute", ip)
+	} else {
+		beego.BeeLogger.Warn("Request ip[%s] is not intranet ip, check current user", ip)
+		currentUser := self.GetCurrentUser(false)
+		beego.BeeLogger.Warn("currentUser=%v", currentUser)
+		// 白名单36005:Lane, 28286: 宇斯
+		var whiteList = []int64{36005, 28286}
+		if currentUser != nil {
+			for _, id := range whiteList {
+				beego.BeeLogger.Warn("id=%d,currentUserId=%d", id, currentUser.Id)
+				if currentUser.Id == id {
+					beego.BeeLogger.Info("current user id=[%d], exec task", id)
+					exec(name)
+					break
+				}
+			}
+		}
+	}
+	self.ServeJSON()
+}
+
+func exec(name string) {
+	switch name {
+	case "take_cash": //提现
+		takeCash()
+	case "orderReceiveAutomatically": //已发货订单7天自动收货
+		OrderReceiveAutomatically()
+	case "updateUserNicknameAndHead": //修复数据-只执行一次
+		updateUserNicknameAndHead()
+	case "udpate_sold_count": //更新已售数量
+		updateSoldCount()
+	case "comb_user_relation": //梳理内部推荐关系
+		combUserRelation()
+	case "test_inser_three": //测试三位关系
+		insertThreeWxusers()
+	case "comb_three_user": //梳理三位关系
+		threebUserRelation()
+	case "register_wxuser": //修复数据--注册会员
+		registerWxUser()
+	default:
+		beego.BeeLogger.Error("Didn't get task wity name:[%s]", name)
+	}
+}

+ 48 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/order_receive_automatically.go

@@ -0,0 +1,48 @@
+package cron_controller
+
+import (
+	"time"
+
+	"github.com/astaxie/beego"
+
+	"fohow.com/apps/models/order_model"
+)
+
+//发货后,满7天,自动收货(修改状态和设置收货时间)
+func OrderReceiveAutomatically() {
+
+	orders := order_model.GetSevenDaysFullOrdersByDispatchTime()
+	beego.BeeLogger.Warn("cron time task:---OrderReceiveAutomatically---orders_num: %d", len(orders))
+	for _, item := range orders {
+
+		item.Status = order_model.STATUS_COMPLETE
+		item.ReceiveTime = item.DispatchTime.AddDate(0, 0, 7)
+		item.Save()
+
+		beego.BeeLogger.Warn("cron time task:---OrderReceiveAutomatically---item_id: %d, finished.", item.Id)
+	}
+}
+
+//处理以前已经发货但发货时间为空的订单,完善发货时间
+func OrderCompleteDispatchTime(isUpdateTime bool) {
+
+	//	查询已经发货、发货时间是空的订单
+	orders := order_model.GetOrdersByDispatchStatusAndDispatchTimeIsNull()
+
+	beego.BeeLogger.Warn("one time task:---OrderCompleteDispatchTime---orders_num: %d", len(orders))
+
+	//  更新订单的发货时间,保存
+	if isUpdateTime && orders != nil && len(orders) > 0 {
+
+		//更新发货时间为支付时间两天后
+		for _, item := range orders {
+
+			dTime := time.Unix(item.PaiedAt, 0).AddDate(0, 0, 2)
+			item.DispatchTime = dTime
+			item.Save()
+			beego.BeeLogger.Warn("one time task:---OrderCompleteDispatchTime---item_id: %d, finished.", item.Id)
+		}
+
+	}
+
+}

+ 46 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/register_wxuser.go

@@ -0,0 +1,46 @@
+package cron_controller
+
+import (
+	"fohow.com/apps/models/user_model"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
+
+/*
+处理未注册的微信用户
+*/
+func registerWxUser() {
+
+	var list []*user_model.WxUser
+	sql := `
+		select *
+		  from wx_users
+		 where user_id<=0;
+	`
+	_, err := orm.NewOrm().Raw(sql).QueryRows(&list)
+	if err != nil {
+		beego.Debug("registerWxUser err=[%s]", err)
+		return
+	}
+
+	beego.BeeLogger.Warn("registerWxUser.len(list):%d", len(list))
+
+	for _, wxUser := range list {
+		if wxUser.Id == 1 {
+			continue
+		}
+		//注册会员
+		user := user_model.Create("", wxUser.SignupIp)
+		if user != nil {
+			wxUser.UserId = user.Id
+			user.Nickname = wxUser.Nickname
+			user.Country = wxUser.Country
+			user.Province = wxUser.Province
+			user.City = wxUser.City
+			user.Sex = wxUser.Sex
+			user.Save()
+			wxUser.Save()
+			user.CopyWxUserHead(wxUser.Head)
+		}
+	}
+}

+ 73 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/sync_balance.go

@@ -0,0 +1,73 @@
+package cron_controller
+
+import (
+	"crypto/md5"
+	"fmt"
+	"io"
+	"time"
+
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/wx_mp"
+	"github.com/astaxie/beego"
+)
+
+type SyncData struct {
+	RecordID  int64     `orm:"column(id)"`
+	UnionId   string    `orm:"column(unionid)"`
+	Amount    int64     `orm:"column(receive_bonus)"`
+	CreatedAt time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)"`
+}
+
+func (self *SyncData) Sign() string {
+	h := md5.New()
+	s := fmt.Sprintf("%s,%s,%d,!iGUESS", self.UnionId, self.Amount, self.CreatedAt.Unix())
+	io.WriteString(h, s)
+	return fmt.Sprintf("%x", h.Sum(nil))
+}
+
+//提现打款的定时任务
+func takeCash() {
+	//选择出已审批通过的提现中订单
+	beego.BeeLogger.Warn("********** take cash cron task start at: %s********", time.Now())
+
+	//取出所有正在提现、正在受理的订单
+	list := balance_model.GetAllTakingCashOrders()
+	beego.BeeLogger.Warn("Take cash list length[%d]", len(list))
+	for _, item := range list {
+		wxUser := user_model.GetWxUserById(item.WxUId, false)
+		if wxUser == nil {
+			beego.BeeLogger.Error("Take cash wxUser[%d] is not exist", item.WxUId)
+			continue
+		}
+		user := user_model.GetUserById(wxUser.UserId, false)
+		if user == nil {
+			beego.BeeLogger.Error("Take cash User[%d] is not exist", wxUser.UserId)
+			continue
+		}
+		var check_state string
+
+		if item.RealState == balance_model.REAL_STATE {
+			if user.RealName == "" {
+				beego.BeeLogger.Error("Take cash User[%d] has no real name", wxUser.UserId)
+				continue
+			}
+			check_state = wx_mp.PAY_FORCE_CHECK
+		} else {
+			check_state = wx_mp.PAY_NO_CHECK
+		}
+		ret := wx_mp.Transfers(wxUser.Openid, item.Count, item.OrderId, check_state, user.RealName, "提现")
+		if ret["result_code"] == wx_mp.PAY_SUCCESS {
+			item.State = 1
+			item.TradeNo = ret["payment_no"]
+			// paiedAt, _ := time.Parse("2006-01-02 15:04:05", ret["payment_time"])
+			item.Remark = "提现已打款"
+			item.PaiedAt = time.Now().Unix()
+			item.Save()
+		} else {
+			item.State = 2
+			item.Remark = ret["err_code_des"]
+			item.Save()
+		}
+	}
+}

+ 46 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/three_user_relation.go

@@ -0,0 +1,46 @@
+package cron_controller
+
+import (
+	"fohow.com/apps/models/user_model"
+)
+
+/*
+1.确定下属会员所属排名(invite_rank);
+2.invite_rank (1-100) 分配A,(100-200) 分配B (300-400) 分配C;算法 string(rune(int((rank)/100)+int(65)))
+3.确定下属会员introArea;
+4.递归更新intro_inner_no;
+*/
+
+func insertThreeWxusers() {
+	//转移微信会员
+	sql := "select * from wx_users where 1;"
+	wxUserList := user_model.GetWxUsersBySql(sql)
+	for _, item := range wxUserList {
+		new(user_model.ThreeWxUser).QuickCreate(item.Nickname, item.Id, item.InviteId)
+	}
+}
+
+//三位会员内部关系
+func threebUserRelation() {
+	wxUserId := int64(1)
+	//从公司会员开始修复
+	wxUser := user_model.GetThreeWxUserById(wxUserId, true)
+	//更改下级群主关系
+	inviteList := user_model.GetThreeWxUsersByInviteIdAll(wxUser.Id, false)
+	go UpdateClassThreeInner(inviteList, wxUser.IntroInnerNo, wxUser.Id)
+}
+
+func UpdateClassThreeInner(list []*user_model.ThreeWxUser, innerNo string, inviteId int64) {
+	for _, item := range list {
+		if item != nil {
+			item.IntroInnerNo = innerNo
+			introArea := user_model.GetAvailableThreeIntroArea(inviteId)
+			introInnerNo := innerNo + introArea
+			item.IntroArea = introArea
+			item.IntroInnerNo = introInnerNo
+			item.Save()
+			secondList := user_model.GetThreeWxUsersByInviteIdAll(item.Id, false)
+			UpdateClassThreeInner(secondList, item.IntroInnerNo, item.Id)
+		}
+	}
+}

+ 35 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/update_sold_count.go

@@ -0,0 +1,35 @@
+package cron_controller
+
+import (
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/product_model"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
+
+/*
+处理商品已销售数量
+*/
+func updateSoldCount() {
+
+	var list []*product_model.Product
+	sql := `
+		select *
+		  from products ;
+	`
+	_, err := orm.NewOrm().Raw(sql).QueryRows(&list)
+	if err != nil {
+		beego.Debug("updateSoldCount err=[%s]", err)
+		return
+	}
+	beego.BeeLogger.Warn("updateSoldCount.len(list):%d", len(list))
+	for _, product := range list {
+		//统计商品总销量
+		soldCount := order_model.GetSoldCountByPId(product.Id, false)
+		product.SaleNums = soldCount
+		beego.BeeLogger.Warn("updateSoldCount.productId:%d", product.Id)
+		beego.BeeLogger.Warn("updateSoldCount.SaleNums:%d", soldCount)
+		product.Save()
+	}
+
+}

+ 72 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/user_complete_info.go

@@ -0,0 +1,72 @@
+package cron_controller
+
+import (
+	"fohow.com/apps/models/user_model"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
+
+/*
+处理用户的昵称和头像,从微信用户那里同步过来
+*/
+func updateUserNicknameAndHead() {
+
+	var list []*user_model.User
+	sql := `
+		select *
+		  from users
+		 where nickname= ""
+			or head= "";
+	`
+
+	_, err := orm.NewOrm().Raw(sql).QueryRows(&list)
+	if err != nil {
+		beego.Debug("GetUsersByEmptyNicknameOrHead err=[%s]", err)
+		return
+	}
+
+	beego.BeeLogger.Warn("GetUsersByEmptyNicknameOrHead.len(list):%d", len(list))
+
+	for _, user := range list {
+
+		wxUser := user_model.GetWxUserByUserId(user.Id, true)
+		if wxUser == nil {
+			continue
+		}
+		needUpdate := false
+		if user.Head == "" && wxUser.Head != "" {
+			user.CopyWxUserHead(wxUser.Head)
+			needUpdate = true
+		}
+		if user.Nickname == "" && wxUser.Nickname != "" {
+
+			user.Nickname = wxUser.Nickname
+			needUpdate = true
+		}
+		if user.Country == "" && wxUser.Country != "" {
+
+			user.Country = wxUser.Country
+			needUpdate = true
+		}
+		if user.Province == "" && wxUser.Province != "" {
+
+			user.Province = wxUser.Province
+			needUpdate = true
+		}
+		if user.City == "" && wxUser.City != "" {
+
+			user.City = wxUser.City
+			needUpdate = true
+		}
+		if user.Sex == 0 && wxUser.Sex != 0 {
+			user.Sex = wxUser.Sex
+			needUpdate = true
+		}
+		if needUpdate == true {
+
+			user.Save()
+			beego.BeeLogger.Warn("GetUsersByEmptyNicknameOrHead.user:%d", user.Id)
+		}
+
+	}
+}

+ 46 - 0
go/gopath/src/fohow.com/apps/controllers/customer_service_controller/customer_service_controller.go

@@ -0,0 +1,46 @@
+package customer_service_controller
+
+import (
+	"fohow.com/apps"
+	"github.com/astaxie/beego/context"
+	//"fohow.com/libs/kefu"
+	//"github.com/astaxie/beego"
+)
+
+var (
+	//需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"*"}
+	exceptCheckWxUserLoginAction = []string{"*"}
+)
+
+type CustomerServiceController struct {
+	apps.BaseController
+}
+
+func (self *CustomerServiceController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+func (self *CustomerServiceController) GetRabbitKfImgByUnionid() {
+
+	// 2018/09/15这里先返回小兔二维码,不查询第五创
+	qrCodeImgUrl := "https://fohow.oss-cn-shenzhen.aliyuncs.com/xcx/kf/kfewm.jpg"
+	//qrCodeImgUrl := "http://rabbit-mall.oss-cn-shenzhen.aliyuncs.com/xcx/kf/xcx_xiaola.jpg"
+	//wxUser := self.GetCurrentWxUser(true)
+	//if wxUser != nil{
+	//	result := kefu.GetKfQrcodeImgFromD5c(wxUser.Unionid)
+	//
+	//	if result!= nil && result.CheckCode == "0000"{
+	//
+	//		qrCodeImgUrl = result.ImgPath
+	//	}
+	//}
+	//
+	//if wxUser == nil{
+	//	beego.BeeLogger.Warn("wxUser not Login, cannot get QrcodeImgFromD5c!")
+	//}
+	self.Data["json"] = qrCodeImgUrl
+	self.ServeJSON()
+}

+ 71 - 0
go/gopath/src/fohow.com/apps/controllers/form_id_controller/form_id_controller.go

@@ -0,0 +1,71 @@
+package form_id_controller
+
+import (
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+
+	"fmt"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/apps/models/wx_message_model"
+	"strings"
+)
+
+var (
+	exceptCheckUserLoginAction   = []string{"Create", "TestSendingTemplatemsg"}
+	exceptCheckWxUserLoginAction = []string{"TestSendingTemplatemsg"}
+)
+
+type FromIdController struct {
+	apps.BaseController
+}
+
+func (self *FromIdController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+func (self *FromIdController) Create() {
+	beego.BeeLogger.Info("FromIdController")
+	wxUser := self.GetCurrentWxUser(false)
+	if wxUser == nil {
+		self.ServeJSON()
+		return
+	}
+	formIds := strings.Split(self.GetString("formIds"), ",")
+	beego.BeeLogger.Info("FromIdController formIds %s", formIds)
+	for _, formId := range formIds {
+		formIdInstance := wx_message_model.Create(wxUser.Openid, formId)
+		beego.BeeLogger.Info("FromIdController formid created: [%d,%s]", formIdInstance.Id, formIdInstance.FormID)
+	}
+	self.ServeJSON()
+}
+
+func (self *FromIdController) TestSendingTemplatemsg() {
+	if beego.BConfig.RunMode != beego.DEV {
+		return
+	}
+	//wxUser := self.GetCurrentWxUser(false)
+
+	wxUser := user_model.GetWxUserByUserId(600275, false)
+
+	amount := fmt.Sprintf("%d元", 22)
+	helpers.UserBalanceChangedNotify(*wxUser, amount, "新春红包", "满1元即可提现", "pages/home/home")
+
+	/*
+		time.Sleep(1 * time.Second)
+
+		helpers.ProductLogisticsChangedNotify(*wxUser, time.Now(), "测试", "123456", "申通", "654321", "可以吗~", "pages/start/start?url=packageUser/pages/user/order/order&id=EX201801246CFCDFD8")
+
+		time.Sleep(1 * time.Second)
+
+		helpers.OrderCreateNotify(*wxUser, time.Now(), "商品1", "订单号1", "快点发货", "待发货", 12, "pages/start/start?url=packageMerchant/pages/merchant/orders/orders&id=4")
+
+		time.Sleep(1 * time.Second)
+
+		helpers.GranaryProductDealNotify(*wxUser, "柿子项目xx", "自用1份起", "为防止商品过季影响口感,请赶紧到【个人中心】-【我的粮仓】进行下单自用哦", "可爱的小兔和小拉", "-", "pages/start/start?url=packageUser/pages/user/granary/granary")
+	*/
+	self.ServeJSON()
+}

+ 187 - 0
go/gopath/src/fohow.com/apps/controllers/mp_controller/mp_controller.go

@@ -0,0 +1,187 @@
+package mp_controller
+
+import (
+	"fmt"
+	"strconv"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/chanxuehong/wechat/mp/jssdk"
+	"github.com/chanxuehong/wechat/util"
+	"github.com/uuid"
+
+	"fohow.com/apps/models/user_model"
+	"fohow.com/apps/models/wx_gongzhonghao_model"
+	"fohow.com/libs/wx_mp"
+)
+
+type MpController struct {
+	beego.Controller
+}
+
+// "signature":"4b196dfa7023b75cdebd8a9d2dbccfff601bae7e"
+// "echostr":"1479928067271949302"
+// "timestamp":"1452007620"
+// "nonce":"1530712726"
+// "mpid":"zhppp0756"
+// "token":"1438853439"
+//测试地址http://tgo.xikego.com:28888/v1/mp/wxb85becdc77d227c1?timestamp=1452007620&nonce=1530712726&signature=4b196dfa7023b75cdebd8a9d2dbccfff601bae7e&echostr=1479928067271949302
+//微信公众平台验证url和token的有效性
+func (self *MpController) Handler() {
+	t := self.GetString("timestamp")
+	n := self.GetString("nonce")
+	s := self.GetString("signature")
+	e := self.GetString("echostr")
+	mpappid := self.Ctx.Input.Param(":mpappid")
+	if mpappid == "" {
+		m := "mpappid is null"
+		beego.BeeLogger.Error(m)
+		self.Ctx.WriteString(m)
+		return
+	}
+	gzh := wx_gongzhonghao_model.GetGZHByAppId(mpappid, false)
+
+	//beego.BeeLogger.Warn("Request.Method:%s", self.Ctx.Request.Method)
+	//在开发者首次提交验证申请时,微信服务器将发送GET请求到填写的URL上,
+	//并且带上四个参数(signature、timestamp、nonce、echostr),
+	//开发者通过对签名(即signature)的效验,来判断此条消息的真实性。
+	if self.Ctx.Request.Method == "GET" {
+		if t == "" || n == "" || s == "" || e == "" {
+			info := fmt.Sprintf("params,timestamp=[%s],nonce=[%s],signature=[%s],echostr=[%s]", t, n, s, e)
+			beego.BeeLogger.Error(info)
+			self.Ctx.WriteString(info)
+			return
+		}
+		//beego.BeeLogger.Warn("Request.Method:%s", self.Ctx.Request.Method)
+		// 验证签名
+		//密文模式
+		// sign := util.MsgSign(gzh.Token, t, n, encryptedMsg)
+		//明文模式
+		sign := util.Sign(gzh.Token, t, n)
+		if sign != s {
+			text := fmt.Sprintf("check signature fail,sign=[%s],params[sign]=[%s]", sign, s)
+			self.Ctx.WriteString(text)
+			return
+		}
+		self.Ctx.WriteString(e)
+	} else {
+		if err := wx_mp.HandleWithRequest(self.Ctx, gzh); err != nil {
+			beego.BeeLogger.Error("Handle with request error[%s]", err)
+		}
+		self.Ctx.WriteString("success")
+		return
+	}
+
+}
+
+//微信JS-SDK配置注入
+func (self *MpController) Config() {
+	url := self.GetString("url")
+	appId := beego.AppConfig.String("JsSDKConfigTicketAppId")
+	ticket := wx_mp.GetTicket()
+	type apiData struct {
+		Timestamp   int64  `json:"timestamp"`
+		NonceStr    string `json:"nonceStr"`
+		JsapiTicket string `json:"-"`
+		Url         string `json:"-"`
+		AppId       string `json:"appId"`
+		Signature   string `json:"signature"`
+	}
+	nonceStr := uuid.NewV4().String()
+	t := time.Now().Unix()
+	data := &apiData{
+		Timestamp:   t,
+		NonceStr:    nonceStr,
+		JsapiTicket: ticket,
+		Url:         url}
+	data.AppId = appId
+	timestamp := fmt.Sprintf("%d", t)
+	data.Signature = jssdk.WXConfigSign(ticket, nonceStr, timestamp, url)
+	self.Data["json"] = data
+	self.ServeJSON()
+}
+
+//微信JS-SDK配置注入
+func (self *MpController) GetAccessToken() {
+	// type Ret struct {
+	// 	Token string `json:"token"`
+	// }
+	// appId := self.GetString("a")
+	// appSecret := self.GetString("s")
+	// token := wx_mp.GetAccessToken(appId, appSecret)
+	// beego.BeeLogger.Warn("rails get token(%s, %s)=%s", appId, appSecret, token)
+	// ret := &Ret{Token: token}
+	// self.Data["json"] = ret
+	// self.ServeJSON()
+}
+
+//是否关注公众号
+func (self *MpController) CheckSubscribe() {
+	_wx_uid := self.Ctx.Input.Param(":wx_uid")
+	wx_uid, _ := strconv.ParseInt(_wx_uid, 10, 64)
+	wxUser := user_model.GetWxUserById(wx_uid, false)
+	isSub := 0
+	if wxUser != nil {
+		info := wx_mp.UserInfo(
+			wxUser.Openid,
+			beego.AppConfig.String("WxMPAppId"),
+			beego.AppConfig.String("WxMPAppSecret"))
+
+		if info != nil {
+			isSub = info.IsSubscriber
+		}
+	}
+	type Ret struct {
+		IsSub int `json:"is_sub"`
+	}
+	self.Data["json"] = &Ret{IsSub: isSub}
+	self.ServeJSON()
+}
+
+//是否关注公众号-通用
+func (self *MpController) CheckSub() {
+	_wx_uid := self.Ctx.Input.Param(":wx_uid")
+	wx_uid, _ := strconv.ParseInt(_wx_uid, 10, 64)
+	wxUser := user_model.GetWxUserById(wx_uid, false)
+	isSub := 0
+	if wxUser != nil {
+		_id := self.Ctx.Input.Param(":gzh_id")
+		id, _ := strconv.ParseInt(_id, 10, 64)
+		openid := wxUser.Openid
+		gzh := wx_gongzhonghao_model.GetGZHById(id, true)
+		if gzh != nil {
+			// if gzh.AppId != beego.AppConfig.String("WxMPAppId") {
+			// 	authWxUser := user_model.GetAuthWxUserByMpIdAndUnionId(gzh.WxHao, wxUser.Unionid, true)
+			// 	if authWxUser != nil {
+			// 		openid = authWxUser.MpOpenid
+			// 	}
+			// }
+			info := wx_mp.UserInfo(
+				openid,
+				gzh.AppId,
+				gzh.AppSecret)
+			if info != nil {
+				isSub = info.IsSubscriber
+			}
+		}
+
+	}
+	type Ret struct {
+		IsSub int `json:"is_sub"`
+	}
+	self.Data["json"] = &Ret{IsSub: isSub}
+	self.ServeJSON()
+}
+
+//取客服账号列表
+func (self *MpController) GetCustomServices() {
+	appId := self.GetString("appid")
+	appSecret := self.GetString("appsecret")
+	if appId == "" || appSecret == "" {
+		self.Data["json"] = "params error"
+	} else {
+		list := wx_mp.GetCustomServices(appId, appSecret)
+		self.Data["json"] = list
+	}
+	self.ServeJSON()
+}

+ 315 - 0
go/gopath/src/fohow.com/apps/controllers/order_controller/cart_controller.go

@@ -0,0 +1,315 @@
+package order_controller
+
+import (
+	"fohow.com/apps"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/user_model"
+	"github.com/astaxie/beego"
+	"strconv"
+	"strings"
+)
+
+//加入购物车
+func (self *OrderController) CreateCart() {
+	_pid := self.Ctx.Input.Param(":id")
+	pId, _ := strconv.ParseInt(_pid, 10, 64)
+	_count := self.Ctx.Input.Param(":count")
+	count, _ := strconv.ParseInt(_count, 10, 64)
+	if count <= 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	product := product_model.GetProductById(pId, false)
+	if product == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+
+	//限新逻辑: 微信支付完成购买过商品的用户
+	if product.IsOnlyNew {
+		paiedOrder := order_model.GetPaiedOrderByWxUIdAndPayWayLimitOne(wxUId, order_model.PAY_WAY_WEIXIN, false)
+		if paiedOrder != nil {
+			self.ReturnError(403, apps.OnlyNew, "", nil)
+		}
+	}
+	cart := new(order_model.Cart).Create(wxUId, uId, product.Id, count)
+	result := false
+	if cart != nil {
+		result = true
+	}
+	type Ret struct {
+		Result bool `json:"result"`
+	}
+	ret := new(Ret)
+	ret.Result = result
+	self.Data["json"] = ret
+	self.ServeJSON()
+}
+
+//调整产品数量
+func (self *OrderController) ChangeItemNums() {
+	_cid := self.Ctx.Input.Param(":id")
+	cId, _ := strconv.ParseInt(_cid, 10, 64)
+	_count := self.Ctx.Input.Param(":count")
+	count, _ := strconv.ParseInt(_count, 10, 64)
+	if count <= 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	cartItem := order_model.GetCartById(cId)
+	if cartItem == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	wxUId := self.GetCurrentWxUserId()
+	if cartItem.WxUserId != wxUId {
+		self.ReturnError(403, apps.AccountError, "", nil)
+	}
+	cartItem.Count = count
+	cartItem.Save()
+	result := false
+	if cartItem != nil {
+		result = true
+	}
+	type Ret struct {
+		Result bool `json:"result"`
+	}
+	ret := new(Ret)
+	ret.Result = result
+	self.Data["json"] = ret
+	self.ServeJSON()
+}
+
+//调整购物车项目是否购买
+func (self *OrderController) ChangeItemState() {
+	_cid := self.Ctx.Input.Param(":id")
+	cId, _ := strconv.ParseInt(_cid, 10, 64)
+	state, _ := self.GetBool("state", false)
+	if cId <= 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	cartItem := order_model.GetCartById(cId)
+	if cartItem == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	wxUId := self.GetCurrentWxUserId()
+	if cartItem.WxUserId != wxUId {
+		self.ReturnError(403, apps.AccountError, "", nil)
+	}
+	cartItem.IsBuy = state
+	cartItem.Save()
+	result := false
+	if cartItem != nil {
+		result = true
+	}
+	type Ret struct {
+		Result bool `json:"result"`
+	}
+	ret := new(Ret)
+	ret.Result = result
+	self.Data["json"] = ret
+	self.ServeJSON()
+}
+
+//批量调整购物车项目是否购买
+func (self *OrderController) MultChangeItemState() {
+
+	uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	ids := self.GetString("ids")
+	nums := self.GetString("nums")
+
+	if len(nums) <= 0 || len(ids) <= 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	//先取消,后设置
+	list := order_model.GetCartItemsByUserId(uId)
+	for _, item := range list {
+		item.IsBuy = false
+		item.Save()
+	}
+	if len(ids) > 0 {
+		c_arr := strings.Split(ids, ",")
+		beego.BeeLogger.Warn("c_arr(%s)", c_arr)
+		c_nums := strings.Split(nums, ",")
+		beego.BeeLogger.Warn("c_nums(%s)", c_nums)
+		for key, s_item := range c_arr {
+			cId, _ := strconv.ParseInt(s_item, 10, 64)
+			cNums := int64(1)
+			cNums, _ = strconv.ParseInt(c_nums[key], 10, 64)
+			cartItem := order_model.GetCartById(cId)
+			if cartItem == nil {
+				self.ReturnError(403, apps.NoExist, "", nil)
+			}
+			if cartItem.WxUserId != wxUId {
+				self.ReturnError(403, apps.AccountError, "", nil)
+			}
+			cartItem.IsBuy = true
+			cartItem.Count = cNums
+			cartItem.Save()
+		}
+	}
+	result := true
+	type Ret struct {
+		Result bool `json:"result"`
+	}
+	ret := new(Ret)
+	ret.Result = result
+	self.Data["json"] = ret
+	self.ServeJSON()
+}
+
+//全选购买接口
+func (self *OrderController) ChangeAllState() {
+
+	state, _ := self.GetBool("state", false)
+	uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	list := order_model.GetCartItemsByUserId(uId)
+	for _, item := range list {
+		cartItem := order_model.GetCartById(item.Id)
+		if cartItem == nil {
+			self.ReturnError(403, apps.NoExist, "", nil)
+		}
+		if cartItem.WxUserId != wxUId {
+			self.ReturnError(403, apps.AccountError, "", nil)
+		}
+		cartItem.IsBuy = state
+		cartItem.Save()
+	}
+	result := true
+	type Ret struct {
+		Result bool `json:"result"`
+	}
+	ret := new(Ret)
+	ret.Result = result
+	self.Data["json"] = ret
+	self.ServeJSON()
+}
+
+//获取会员购物车信息
+func (self *OrderController) GetCartList() {
+	cache, _ := self.GetBool("cache", false)
+	uId := self.GetCurrentUserId()
+	list := order_model.GetCartItemsByUserId(uId)
+
+	for _, item := range list {
+		product := product_model.GetProductById(item.ProductId, cache)
+		if product == nil {
+			go ClearProductCart(uId, item.ProductId)
+			continue
+		}
+		wxUser := user_model.GetWxUserById(item.WxUserId, cache)
+		if wxUser != nil {
+			wxUser.Head = user_model.GetFullImgUrl(wxUser.Head)
+		}
+		item.Cover = product_model.GetCoverByPId(item.ProductId, cache)
+		item.OriginalPrice = product.Price
+		item.ProductName = product.Name
+
+	}
+	count := int64(0)
+	total := int64(0)
+	buylist := order_model.GetCartItemsByUserIdAndBuy(uId, true)
+	for _, item := range buylist {
+		product := product_model.GetProductById(item.ProductId, cache)
+		count += item.Count
+		total += item.Count * product.Price
+	}
+
+	type Ret struct {
+		List  []*order_model.Cart `json:"list"`
+		Count int64               `json:"count"`
+		Total int64               `json:"total"`
+	}
+	self.Data["json"] = &Ret{Total: total, Count: count, List: list}
+	self.ServeJSON()
+}
+
+func ClearProductCart(userId, productId int64) {
+	cartItem := order_model.GetCartByUidAndPid(userId, productId)
+	if cartItem != nil {
+		cartItem.Delete()
+	}
+}
+
+//获取会员已选购购物车信息
+func (self *OrderController) GetCartBuyList() {
+	cache, _ := self.GetBool("cache", false)
+	uId := self.GetCurrentUserId()
+	isBuy := true
+	list := order_model.GetCartItemsByUserIdAndBuy(uId, isBuy)
+	count := int64(0)
+	total := int64(0)
+	for _, item := range list {
+		count += item.Count
+		product := product_model.GetProductById(item.ProductId, cache)
+		wxUser := user_model.GetWxUserById(item.WxUserId, cache)
+		if wxUser != nil {
+			wxUser.Head = user_model.GetFullImgUrl(wxUser.Head)
+		}
+		item.Cover = product_model.GetCoverByPId(item.ProductId, cache)
+		item.OriginalPrice = product.Price
+		item.ProductName = product.Name
+		total += item.Count * product.Price
+	}
+
+	type Ret struct {
+		List  []*order_model.Cart `json:"list"`
+		Count int64               `json:"count"`
+		Total int64               `json:"total"`
+	}
+	self.Data["json"] = &Ret{Total: total, Count: count, List: list}
+	self.ServeJSON()
+}
+
+//删除购物车产品项
+func (self *OrderController) DeleteItem() {
+	_cid := self.Ctx.Input.Param(":id")
+	cId, _ := strconv.ParseInt(_cid, 10, 64)
+	if cId <= 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	cartItem := order_model.GetCartById(cId)
+	if cartItem == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	wxUId := self.GetCurrentWxUserId()
+	if cartItem.WxUserId != wxUId {
+		self.ReturnError(403, apps.AccountError, "", nil)
+	}
+	cartItem.Delete()
+	result := true
+	type Ret struct {
+		Result bool `json:"result"`
+	}
+	ret := new(Ret)
+	ret.Result = result
+	self.Data["json"] = ret
+	self.ServeJSON()
+}
+
+//批量删除购物车
+func (self *OrderController) MultChangeItemDelete() {
+	wxUId := self.GetCurrentWxUserId()
+	ids := self.GetString("ids")
+	s_arr := strings.Split(ids, ",")
+	for _, s_item := range s_arr {
+		sId, _ := strconv.ParseInt(s_item, 10, 64)
+		cartItem := order_model.GetCartById(sId)
+		if cartItem == nil {
+			continue
+		}
+		if cartItem.WxUserId != wxUId {
+			self.ReturnError(403, apps.AccountError, "", nil)
+		}
+		cartItem.Delete()
+	}
+	result := true
+	type Ret struct {
+		Result bool `json:"result"`
+	}
+	ret := new(Ret)
+	ret.Result = result
+	self.Data["json"] = ret
+	self.ServeJSON()
+}

+ 31 - 0
go/gopath/src/fohow.com/apps/controllers/order_controller/init.go

@@ -0,0 +1,31 @@
+package order_controller
+
+import (
+	// "fmt"
+	// "os"
+	// "net/url"
+	// "strings"
+
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	// "github.com/go-wkhtmltoimage"
+	// "github.com/skip2/go-qrcode"
+
+	"fohow.com/apps"
+)
+
+var (
+	//需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"Create", "Detail", "List", "Operate", "GetExpressNo", "MultChangeItemState", "GetCartList"}
+	exceptCheckWxUserLoginAction = []string{"GetExpressNo", "MultChangeItemState", "GetCartList"}
+)
+
+type OrderController struct {
+	apps.BaseController
+}
+
+func (self *OrderController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 351 - 0
go/gopath/src/fohow.com/apps/controllers/order_controller/order_controller.go

@@ -0,0 +1,351 @@
+package order_controller
+
+import (
+	// "fmt"
+	"strconv"
+	// "time"
+	// "d"
+	"github.com/astaxie/beego"
+	// "github.com/astaxie/beego/context"
+	"fohow.com/apps"
+	// "fohow.com/apps/controllers/user_controller"
+	// "fohow.com/apps/models/activity_model"
+	"fohow.com/apps/models/merchant_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/user_model"
+	"strings"
+	"sync"
+	"time"
+)
+
+var updateExpressLock sync.Mutex
+var createOrder sync.Mutex
+var MultreateOrder sync.Mutex
+
+//下单
+func (self *OrderController) Create() {
+	createOrder.Lock()
+	defer createOrder.Unlock()
+	_pid := self.Ctx.Input.Param(":id")
+	pId, _ := strconv.ParseInt(_pid, 10, 64)
+	_count := self.Ctx.Input.Param(":count")
+	count, _ := strconv.ParseInt(_count, 10, 64)
+	if count <= 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	product := product_model.GetProductById(pId, false)
+	if product == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	//user := self.GetCurrentUser(true)
+	//黑名单用户返回账户异常
+	//if user.IsBlackUser == 1 {
+	//	self.ReturnError(403, apps.AccountError, "", nil)
+	//}
+	//秒杀逻辑: 判断是否处于秒杀时间段内
+	if product.SeckilShowPrice > 0 {
+		now := time.Now()
+		if now.Unix() < product.SeckillStart.Unix() {
+			self.ReturnError(403, apps.SeckillNotStart, "", nil)
+		} else if now.Unix() > product.SeckillEnd.Unix() {
+			self.ReturnError(403, apps.SeckillIsEnd, "", nil)
+		}
+	}
+	totalPrice := product.Price * count
+	//小兔微信,测试微信支付
+	if wxUId == 76 {
+		totalPrice = 1
+	}
+	freight := order_model.FREIGHT
+	if totalPrice >= order_model.FREIGHT_LIMIT || beego.AppConfig.String("RunMode") == "dev" {
+		freight = int64(0)
+	}
+
+	order := new(order_model.Order).CreateNew(wxUId, uId,
+		totalPrice, freight, order_model.SOURCE_XCX)
+	if order == nil {
+		self.ReturnError(403, apps.CreateOrderFail, "", nil)
+	}
+	order.Save()
+	//创建订单明细
+	go new(order_model.OrderDetail).Create(order.OrderId, order.Id, product.Id, product.Price, product.RoboBalancePrice, product.Name,
+		count)
+
+	type Order struct {
+		OrderId string `json:"order_id"`
+	}
+	self.Data["json"] = &Order{OrderId: order.OrderId}
+	self.ServeJSON()
+}
+
+func (self *OrderController) GetExpressNo() {
+
+	oId := self.Ctx.Input.Param(":order_id")
+	o := order_model.GetOrderById(oId)
+
+	type Ret struct {
+		ExpressNo      string `json:"express_no"`
+		ExpressCompany string `json:"express_company"`
+	}
+
+	var expressNo, expressCompany string
+	if o != nil {
+		expressNo = o.ExpressOrderNo
+		expressCompany = o.ExpressCompany
+	}
+
+	self.Data["json"] = &Ret{ExpressNo: expressNo, ExpressCompany: expressCompany}
+	self.ServeJSON()
+}
+
+//购物车下单
+func (self *OrderController) MultipleCreate() {
+	MultreateOrder.Lock()
+	defer MultreateOrder.Unlock()
+	uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	ids := self.GetString("ids")
+	nums := self.GetString("nums")
+	if len(nums) <= 0 || len(ids) <= 0 {
+		self.ReturnError(403, apps.NoCart, "", nil)
+	}
+	totalPrice := int64(0)
+	//创建订单
+	order := new(order_model.Order).CreateNew(wxUId, uId,
+		totalPrice, int64(0), order_model.SOURCE_XCX)
+	if order == nil {
+		self.ReturnError(403, apps.CreateOrderFail, "", nil)
+	}
+	c_arr := strings.Split(ids, ",")
+	c_nums := strings.Split(nums, ",")
+	for key, s_item := range c_arr {
+		cId, _ := strconv.ParseInt(s_item, 10, 64)
+		cNums := int64(1)
+		cNums, _ = strconv.ParseInt(c_nums[key], 10, 64)
+		cartItem := order_model.GetCartById(cId)
+		if cartItem == nil {
+			self.ReturnError(403, apps.NoExist, "", nil)
+		}
+		if cartItem.WxUserId != wxUId {
+			self.ReturnError(403, apps.AccountError, "", nil)
+		}
+		product := product_model.GetProductById(cartItem.ProductId, false)
+		if product == nil {
+			self.ReturnError(403, apps.NoExist, "", nil)
+		}
+		totalPrice += product.Price * cNums
+		go new(order_model.OrderDetail).Create(order.OrderId, order.Id, cartItem.ProductId, product.Price, product.RoboBalancePrice, product.Name,
+			cNums)
+	}
+	freight := order_model.FREIGHT
+	if totalPrice >= order_model.FREIGHT_LIMIT || beego.AppConfig.String("RunMode") == "dev" {
+		freight = int64(0)
+	}
+	order.TotalPrice = totalPrice
+	order.Freight = freight
+	order.Save()
+	go ClearCart(order.UserId, order.OrderId)
+	type Order struct {
+		OrderId string `json:"order_id"`
+	}
+	self.Data["json"] = &Order{OrderId: order.OrderId}
+	self.ServeJSON()
+}
+
+func ClearCart(userId int64, orderId string) {
+	orderDetails := order_model.GetAllDetailsOrderId(orderId)
+	for _, item := range orderDetails {
+		cartItem := order_model.GetCartByUidAndPid(userId, item.ProductId)
+		if cartItem != nil {
+			cartItem.Delete()
+		}
+	}
+}
+
+//获取用户订单详情
+func (self *OrderController) Detail() {
+
+	cache, _ := self.GetBool("cache", false)
+	oId := self.Ctx.Input.Param(":order_id")
+	o := order_model.GetOrderById(oId)
+	if o == nil {
+		beego.BeeLogger.Error("order not exist id=[%s]", oId)
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	}
+	orderList := order_model.GetAllDetailsOrderId(o.OrderId)
+	for _, item := range orderList {
+		product := product_model.GetProductById(item.ProductId, cache)
+		if product == nil {
+			self.ReturnError(403, apps.ProductNotExist, "", nil)
+		}
+		product.OrderCount = item.Count
+		o.Count += item.Count
+		if product.SeckilShowPrice > 0 {
+			now := time.Now()
+			product.SeckillStartAt = product.SeckillStart.Unix()
+			product.SeckillEndAt = product.SeckillEnd.Unix()
+			if product.SeckillStart.Unix() > now.Unix() {
+				product.SeckillState = product_model.SECKILL_PREPARING_STATE
+				product.SeckillStateCn = product_model.SECKILL_PREPARING_STATE_CN
+			} else if product.SeckillStart.Unix() <= now.Unix() && now.Unix() < product.SeckillEnd.Unix() {
+				product.IsUnderSeckill = true
+				product.SeckillState = product_model.SECKILL_UNDER_STATE
+				product.SeckillStateCn = product_model.SECKILL_UNDER_STATE_CN
+			} else {
+				product.SeckillState = product_model.SECKILL_END_STATE
+				product.SeckillStateCn = product_model.SECKILL_END_STATE_CN
+			}
+		}
+		o.ProductList = append(o.ProductList, product)
+	}
+	wxUser := self.GetCurrentWxUser(cache)
+	if wxUser == nil {
+		self.ReturnError(403, apps.UserNotExist, "", nil)
+	} else {
+		if wxUser.Id != o.WxUserId {
+			beego.BeeLogger.Error("order not owner id=[%s]", oId)
+			self.ReturnError(404, apps.OrderNotExist, "", nil)
+		}
+		wxUser.Head = user_model.GetFullImgUrl(wxUser.Head)
+	}
+
+	o.StatusCn = order_model.STATUS_CN_TEXT[o.Status]
+	o.CTime = o.CreatedAt.Unix()
+	o.DTime = o.DispatchTime.Unix()
+	if o.DTime < 0 {
+		o.DTime = 0
+	}
+	o.WxUser = wxUser
+	self.Data["json"] = o
+	self.ServeJSON()
+}
+
+//获取用户订单列表
+func (self *OrderController) List() {
+
+	status := self.GetString("status")
+	page, _ := self.GetInt("page")
+	perPage, _ := self.GetInt("per_page")
+	if page <= 0 {
+		page = 1
+	}
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	cache, _ := self.GetBool("cache", false)
+	//uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	orders := order_model.GetUserOrders(wxUId, status, page, perPage)
+	count := order_model.GetUserOrdersCount(wxUId, status)
+
+	for _, item := range orders {
+		orderList := order_model.GetAllDetailsOrderId(item.OrderId)
+		for _, orderItem := range orderList {
+			product := product_model.GetProductById(orderItem.ProductId, cache)
+			if product == nil {
+				continue
+			}
+			item.Count += orderItem.Count
+			product.OrderCount = orderItem.Count
+			if product.SeckilShowPrice > 0 {
+				now := time.Now()
+				product.SeckillStartAt = product.SeckillStart.Unix()
+				product.SeckillEndAt = product.SeckillEnd.Unix()
+				if product.SeckillStart.Unix() > now.Unix() {
+					product.SeckillState = product_model.SECKILL_PREPARING_STATE
+					product.SeckillStateCn = product_model.SECKILL_PREPARING_STATE_CN
+				} else if product.SeckillStart.Unix() <= now.Unix() && now.Unix() < product.SeckillEnd.Unix() {
+					product.IsUnderSeckill = true
+					product.SeckillState = product_model.SECKILL_UNDER_STATE
+					product.SeckillStateCn = product_model.SECKILL_UNDER_STATE_CN
+				} else {
+					product.SeckillState = product_model.SECKILL_END_STATE
+					product.SeckillStateCn = product_model.SECKILL_END_STATE_CN
+				}
+			}
+			item.ProductList = append(item.ProductList, product)
+		}
+	}
+	type Ret struct {
+		List      []*order_model.Order `json:"list"`
+		ListCount int64                `json:"list_count"`
+	}
+	self.Data["json"] = &Ret{ListCount: count, List: orders}
+	self.ServeJSON()
+}
+
+//用户更改订单状态
+func (self *OrderController) Operate() {
+	oId := self.Ctx.Input.Param(":order_id")
+	o := order_model.GetOrderById(oId)
+	if o == nil {
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	}
+	//uId := self.GetCurrentUserId()
+	//if uId != o.UserId {
+	//	self.ReturnError(404, apps.OrderNotExist, "", nil)
+	//}
+	wxUId := self.GetCurrentWxUserId()
+	if wxUId != o.WxUserId {
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	}
+	operate := self.GetString(":operate")
+	if operate != order_model.OPERATE_CONFIRM && operate != order_model.OPERATE_CANCEL {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	if operate == order_model.OPERATE_CONFIRM && o.Status == order_model.STATUS_DISPATCH {
+		o.Status = order_model.STATUS_COMPLETE
+		o.ReceiveTime = time.Now()
+	} else if operate == order_model.OPERATE_CANCEL && o.Status == order_model.STATUS_UNPAY {
+		o.Status = order_model.STATUS_CLOSED
+	}
+
+	if !o.Save() {
+		beego.BeeLogger.Error("wx_user[%d] complete order[%s] fail", wxUId, oId)
+	}
+	//已确认收货的订单、发放代销金给卖方
+	//if o.Status == order_model.STATUS_COMPLETE && o.OrderType == order_model.ORDER_TYPE_SALE {
+	//helpers.SendBalanceWhileSaleOrderCompleteHandler(o)
+	//}
+
+	//if o.Status == order_model.STATUS_COMPLETE{
+	//	helpers.HandleProductBenefitIntoCashBalance(o)
+	//}
+
+	type apiRet struct {
+		Status       string `json:"status"`
+		StatusCnText string `json:"status_cn_text"`
+	}
+	self.Data["json"] = &apiRet{
+		Status:       o.Status,
+		StatusCnText: order_model.STATUS_CN_TEXT[o.Status]}
+	self.ServeJSON()
+}
+
+// 商家处理中订单数量提示
+func (self *OrderController) MerchantListCount() {
+	merchantId, _ := self.GetInt64("merchant_id", 0)
+
+	uId := self.GetCurrentUserId()
+
+	count := int64(0)
+	merchantUser := merchant_model.GetMerchantUserRelationByUserId(uId, true)
+	if merchantUser != nil {
+		if merchantUser.MerchantId != merchantId {
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+	} else {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	count = order_model.GetMerchantOrdersCount(merchantId, 0, 0, 0, 0, order_model.STATUS_PROCESSING, "",
+		strings.TrimSpace(merchantUser.ManageProductIds), "", "", "", "", "", "")
+
+	type Ret struct {
+		ListCount int64 `json:"list_count"`
+	}
+	self.Data["json"] = &Ret{ListCount: count}
+	self.ServeJSON()
+}

+ 122 - 0
go/gopath/src/fohow.com/apps/controllers/order_controller/settle_order_controller.go

@@ -0,0 +1,122 @@
+package order_controller
+
+import (
+	"fohow.com/apps"
+	"fohow.com/apps/models/merchant_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/libs/tool"
+	"time"
+)
+
+//根据订单结算模块
+type SettleOrderController struct {
+	apps.BaseController
+}
+
+//每月结算列表:当月以前月份的每月该商家已收货总额
+func (self *SettleOrderController) SettleList() {
+
+	type Ret struct {
+		List      []*order_model.SettleItem `json:"list"`
+		ListCount int64                     `json:"list_count"`
+	}
+
+	mId, _ := self.GetInt64("merchant_id")
+
+	page, _ := self.GetInt64("page")
+	perPage, _ := self.GetInt64("per_page")
+	if page <= 0 {
+		page = 1
+	}
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+
+	useCache, _ := self.GetBool("cache", false)
+
+	now := time.Now()
+	endTime := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.Local)
+
+	//校验商家
+	user := self.GetCurrentUser(useCache)
+	if user == nil {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+	merchantUser := merchant_model.GetMerchantUserRelationByUserId(user.Id, useCache)
+	if merchantUser == nil {
+		self.ReturnError(403, apps.OrderNotBelongToCurrentUser, "", nil)
+	}
+
+	if !merchantUser.IsSuperAdmin {
+		self.ReturnError(403, apps.CurrentMerIsNotSuperAdminMerchant, "", nil)
+	}
+
+	mId = merchantUser.MerchantId
+	var list []*order_model.SettleItem = []*order_model.SettleItem{}
+	list_tmp := order_model.GetSettleListByMerchantId(mId, page, perPage, endTime.AddDate(0, 1, 0), useCache)
+
+	list_count := order_model.GetSettleListCountByMerchantId(mId, endTime.AddDate(0, 1, 0), useCache)
+
+	if list_tmp != nil {
+		list = list_tmp
+	}
+
+	for _, item := range list {
+		item.SettlePrice = tool.RoundFloat64(item.SettlePrice, 2)
+	}
+
+	self.Data["json"] = &Ret{List: list, ListCount: list_count}
+	self.ServeJSON()
+}
+
+/*
+1. 已结算金额:是以前月份的已收货总额
+2. 未结算金额:是当前月的已收货总额+所有待收货总额
+3. 销售总额:是所有的订单总额,包括处理中、待收货、已收货
+*/
+//已结算、未结算、销售总额三个分析数据
+func (self *SettleOrderController) SettleStat() {
+
+	type Ret struct {
+		AlreadySettle float64 `json:"already_settle"`
+		NotSettle     float64 `json:"not_settle"`
+		TotalSale     float64 `json:"total_sale"`
+	}
+
+	mId, _ := self.GetInt64("merchant_id")
+
+	useCache, _ := self.GetBool("cache", false)
+
+	now := time.Now()
+	endTime := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, time.Local)
+
+	//校验商家
+	user := self.GetCurrentUser(useCache)
+	if user == nil {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+	merchantUser := merchant_model.GetMerchantUserRelationByUserId(user.Id, useCache)
+	if merchantUser == nil {
+		self.ReturnError(403, apps.OrderNotBelongToCurrentUser, "", nil)
+	}
+
+	if !merchantUser.IsSuperAdmin {
+		self.ReturnError(403, apps.CurrentMerIsNotSuperAdminMerchant, "", nil)
+	}
+
+	mId = merchantUser.MerchantId
+
+	alreadySettleAmount := order_model.GetSettledAmountByMIdAndTime(mId, endTime, useCache)
+
+	totalSellAmount := order_model.GeTotalSellAmountByMId(mId, useCache)
+
+	notSettleList := order_model.GetNotSettleListByMerchantId(mId, endTime, endTime.AddDate(0, 1, 0), useCache)
+
+	notSettle := float64(0)
+	for _, item := range notSettleList {
+		notSettle += item.SettlePrice
+	}
+
+	self.Data["json"] = &Ret{AlreadySettle: tool.RoundFloat64(alreadySettleAmount, 2), NotSettle: tool.RoundFloat64(notSettle, 2), TotalSale: tool.RoundFloat64(totalSellAmount, 2)}
+	self.ServeJSON()
+}

+ 402 - 0
go/gopath/src/fohow.com/apps/controllers/pay_controller/after_pay_controller.go

@@ -0,0 +1,402 @@
+package pay_controller
+
+import (
+	// "fmt"
+	// "strings"
+
+	// "github.com/astaxie/beego"
+
+	"fohow.com/apps"
+	// "fohow.com/apps/models/balance_model"
+	// "fohow.com/apps/models/project_join_model"
+	// "fohow.com/apps/models/project_model"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/pay_model"
+	"fohow.com/libs/wx_mp"
+	"github.com/astaxie/beego"
+	"strconv"
+	"strings"
+	"time"
+	//"fohow.com/apps/models/product_model"
+	"fmt"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/user_model"
+)
+
+// 支付异步回调
+func (self *PayController) PayAsync() {
+	target := self.Ctx.Input.Param(":target")
+	payWay := self.Ctx.Input.Param(":payway")
+
+	beego.BeeLogger.Warn("payAsync:%s,%s", target, payWay)
+	switch target {
+	case "balance": // 余额充值
+
+		switch payWay { //充值方式
+
+		case balance_model.PAY_WAY_TYPE_SERVICE_WXPAY:
+			self.wxPayBalanceAsync()
+		default:
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+
+	case "exchange":
+
+		switch payWay {
+
+		case pay_model.PAYWAY_WEIXINPAY:
+			self.wxPayExchangeAsync()
+		default:
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+
+	case CASH_TARGET:
+
+		switch payWay {
+		case balance_model.PAY_WAY_TYPE_RECHARGE_WXPAY:
+			self.wxPayCashczAsync()
+		default:
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+
+	default:
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+}
+
+//微信购买代金券
+func (self *PayController) wxPayBalanceAsync() {
+
+	var notifyResponse = map[string]string{
+		"return_code": wx_mp.PAY_FAIL,
+		"return_msg":  "",
+	}
+	body := self.Ctx.Input.CopyBody(102400)
+	params, err := wx_mp.ParsePayResult(body)
+	beego.BeeLogger.Warn("after_pay_controller.wxPayBalanceAsync(%v)", params)
+
+	if err != nil {
+		beego.BeeLogger.Error("parsePayResult=[%s] err=[%s]", string(body), err)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	// 签名校验参数
+	if !wx_mp.VerifyPayResult(params) {
+		beego.BeeLogger.Error("VerifyPayResult not pass")
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	orderId := strings.Split(params.OutTradeNO, "_")[0]
+	order := balance_model.GetBalanceOrderByOId(orderId, false)
+	totalFee, _ := strconv.ParseInt(params.TotalFee, 10, 64)
+	//totalFee := int64(300000)
+	if order == nil {
+		beego.BeeLogger.Error("order err: %v", order)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	//重复回调通知时,判断订单状态是否处理过了
+	beego.BeeLogger.Warn("小程序购买代金券微信支付回调通知,订单编号=%s", order.OrderId)
+	if order.State == 1 {
+		notifyResponse["return_code"] = wx_mp.PAY_SUCCESS
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+	order.PaiedPrice = totalFee
+	order.State = 1
+	order.TradeNo = params.TransactionId
+	order.PayWay = balance_model.PAY_WAY_TYPE_SERVICE_WXPAY
+	order.PaiedAt = time.Now().Unix()
+	if err := order.Save(); err != nil {
+		beego.BeeLogger.Error("weixinpay async return. save oId=%s fail", order.OrderId)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+	go recharge(order.WxUserId, order.UserId, order.TotalPrice, order.OrderId)
+	wxUser := user_model.GetWxUserById(order.WxUserId, false)
+	if wxUser != nil {
+		newSendInviterBenefit(wxUser, order.OrderId, user_model.BALANCE_BENEFIT)
+	}
+	if totalFee >= balance_model.BALANCE_PAIED && wxUser.ShowInviteMode != 1 {
+		//升级群主
+		UpdateIntroUser(wxUser)
+	}
+
+	notifyResponse["return_code"] = wx_mp.PAY_SUCCESS
+	beego.BeeLogger.Warn("小程序购买代金券微信支付回调通知,订单编号=%s  订单状态=%d", order.OrderId, order.State)
+	self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+	return
+
+}
+
+//升级群主
+func UpdateIntroUser(wxUser *user_model.WxUser) {
+
+	if wxUser == nil || wxUser.ShowInviteMode == int64(1) {
+		return
+	}
+	//升级群主,打开推广模块
+	wxUser.ShowInviteMode = int64(1)
+	wxUser.Save()
+	//更改下级群主关系
+	inviteList := user_model.GetWxUsersByInviteIdAll(wxUser.Id, false)
+	go user_model.UpdateClass(inviteList, wxUser.Id, wxUser.IntroUserId)
+}
+
+func FindInviter(wxUser *user_model.WxUser) *user_model.WxUser {
+	inviter := user_model.GetWxUserById(wxUser.InviteId, false)
+	if inviter == nil {
+		return nil
+	}
+	if inviter.ShowInviteMode == int64(1) {
+		return inviter
+	} else {
+		return FindInviter(inviter)
+	}
+}
+
+func newSendInviterBenefit(wxUser *user_model.WxUser, orderId, source string) {
+	if wxUser == nil {
+		return
+	}
+	var benefitWxUser *user_model.WxUser
+	if wxUser.ShowInviteMode == int64(1) && source == user_model.SOURCE_PRODUCT_BENEFIT {
+		benefitWxUser = wxUser
+	} else {
+		benefitWxUser = FindInviter(wxUser)
+	}
+	if benefitWxUser == nil || benefitWxUser.Id == int64(1) {
+		return
+	}
+	beego.BeeLogger.Warn("benefitWxUser: %s", benefitWxUser)
+	beego.BeeLogger.Warn("newSendInviterBenefit:%v,%v,%s", wxUser, benefitWxUser, orderId)
+	if source == user_model.SOURCE_PRODUCT_BENEFIT {
+		productOrder := order_model.GetOrderById(orderId)
+		if productOrder == nil {
+			return
+		}
+
+		if benefitWxUser != nil {
+			//发放群主收益,群主代金券余额 > 大于订单金额,则全额发放佣金,否则发放佣金
+			count := int64(0)
+			balance_total := balance_model.GetUserTotalBalance(benefitWxUser.Id)
+			if balance_total >= productOrder.PaiedPrice {
+				count = productOrder.PaiedPrice
+			}
+			beego.BeeLogger.Warn("count %d", count)
+			if count > 0 {
+				//先扣减群主代金券
+				s := balance_model.BALANCE_FREND_BUY
+				remark := fmt.Sprintf("%s%s", wxUser.Nickname, "购物")
+				qb := new(balance_model.Balance).Create(benefitWxUser.Id, benefitWxUser.UserId, -count, s, productOrder.OrderId, remark)
+				if qb != nil {
+					//发放佣金
+					inviteOrder := new(user_model.InviteOrder).Create(benefitWxUser.Id, wxUser.Id, wxUser.Id, count, productOrder.TotalPrice, user_model.SOURCE_PRODUCT_BENEFIT, productOrder.OrderId)
+					s = balance_model.CASH_SOURCE_PRODUCT_BENEFIT
+					cb := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(benefitWxUser.Id, productOrder.OrderId, s)
+					if cb == nil {
+						cb = new(balance_model.CashBalance).Create(inviteOrder.BenefitWxUId, count, s, productOrder.OrderId, remark)
+						if cb != nil {
+							//标志进账
+							inviteOrder.IsEnterBalance = true
+							inviteOrder.EnterTime = cb.CreatedAt
+							inviteOrder.Save()
+						}
+					}
+
+				}
+
+			}
+		}
+	} else if source == user_model.BALANCE_BENEFIT {
+		balanceOrder := balance_model.GetBalanceOrderByOId(orderId, false)
+		if balanceOrder == nil {
+			return
+		}
+		//下线充值,上线获得540返利
+		if benefitWxUser != nil {
+			count := int64(0)
+			//发放一级收益, 上级群主返利540
+			if balanceOrder.PaiedPrice == balance_model.BALANCE_PAIED {
+				count = balance_model.AWARD_UPGRADE
+			}
+			if count > 0 {
+				inviteOrder := new(user_model.InviteOrder).Create(benefitWxUser.Id, wxUser.Id, wxUser.Id, count, balanceOrder.TotalPrice, user_model.BALANCE_BENEFIT, balanceOrder.OrderId)
+				//发放现金佣金
+				s := balance_model.BALANCE_SOURCE_BENEFIT
+				remark := fmt.Sprintf("%s%s", wxUser.Nickname, "充值代金券")
+				b := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(benefitWxUser.Id, balanceOrder.OrderId, s)
+				if b == nil {
+					b = new(balance_model.CashBalance).Create(inviteOrder.BenefitWxUId, count, s, balanceOrder.OrderId, remark)
+					if b != nil {
+						//标志进账
+						inviteOrder.IsEnterBalance = true
+						inviteOrder.EnterTime = b.CreatedAt
+						inviteOrder.Save()
+					}
+				}
+			}
+		}
+	}
+}
+
+//微信购买商品
+func (self *PayController) wxPayExchangeAsync() {
+	var notifyResponse = map[string]string{
+		"return_code": wx_mp.PAY_FAIL,
+		"return_msg":  "",
+	}
+	body := self.Ctx.Input.CopyBody(102400)
+	params, err := wx_mp.ParsePayResult(body)
+	beego.BeeLogger.Warn("after_pay_controller.wxPayExchangeAsync(%v)", params)
+
+	if err != nil {
+		beego.BeeLogger.Error("parsePayResult=[%s] err=[%s]", string(body), err)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	// 签名校验参数
+	if !wx_mp.VerifyPayResult(params) && !wx_mp.VerifyGzhPayResult(params) {
+		beego.BeeLogger.Error("VerifyPayResult not pass")
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	orderId := strings.Split(params.OutTradeNO, "_")[0]
+	order := order_model.GetOrderById(orderId)
+	totalFee, _ := strconv.ParseInt(params.TotalFee, 10, 64)
+	settlementTotalFee, _ := strconv.ParseInt(params.SettlementTotalFee, 10, 64)
+	beego.BeeLogger.Warn("after_pay_controller.wxPayExchangeAsync.settlementTotalFee(%d)", settlementTotalFee)
+	if order == nil || (order.TotalPrice+order.Freight) != totalFee {
+		beego.BeeLogger.Error("order err: %v", order)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	//重复回调通知时,判断订单状态是否已经支付
+	beego.BeeLogger.Warn("小程序商品购买微信支付回调通知,订单编号=%s", order.OrderId)
+	if order.Status != order_model.STATUS_UNPAY {
+		notifyResponse["return_code"] = wx_mp.PAY_SUCCESS
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	order.Status = order_model.STATUS_PROCESSING
+	order.TradeNo = params.TransactionId
+	order.PayWay = order_model.PAY_WAY_WEIXIN
+	order.PaiedAt = time.Now().Unix()
+	order.PaiedTime = time.Now()
+	order.PaiedPrice = order.TotalPrice
+	if isSuccess := order.Save(); !isSuccess {
+		beego.BeeLogger.Error("weixinpay async return. save oId=%s fail", order.OrderId)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	//go notice()
+	wxUser := user_model.GetWxUserById(order.WxUserId, true)
+	go newSendInviterBenefit(wxUser, order.OrderId, user_model.SOURCE_PRODUCT_BENEFIT)
+	//发放赠品
+	productId := int64(0)
+	if beego.AppConfig.String("RunMode") == "dev" {
+		productId = int64(91)
+	} else {
+		productId = order_model.SEND_PRODUCT_ID
+	}
+	saleList := order_model.GetWxUserOrders(wxUser.Id)
+	//beego.BeeLogger.Error("en(saleList)=%d", len(saleList))
+	//	七月注册会员首单赠送赠品
+	if order.TotalPrice >= order_model.PROMOTION_TOTAL && len(saleList) == 1 {
+		product := product_model.GetProductById(productId, false)
+		go order_model.SendCreate(order.OrderId, order.Id, productId, product.Price, product.Price, product.Name, int64(1))
+	}
+	//更新商品售量
+	go UpdatePdSaleNums(order)
+	notifyResponse["return_code"] = wx_mp.PAY_SUCCESS
+	beego.BeeLogger.Warn("商品购买微信支付回调通知,订单编号=%s  订单状态=%s", order.OrderId, order.Status)
+	self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+	return
+}
+
+//更新已售数量
+func UpdatePdSaleNums(order *order_model.Order) {
+	if order == nil {
+		return
+	}
+
+	orderDtList := order_model.GetAllDetailsOrderId(order.OrderId)
+
+	if len(orderDtList) == 0 {
+		return
+	}
+	SaleNumsMap := make(map[int64]int64)
+	for _, item := range orderDtList {
+		SaleNumsMap[item.ProductId] = item.Count
+	}
+
+	go order_model.UpdateSaleNums(SaleNumsMap)
+}
+
+//微信余额充值
+func (self *PayController) wxPayCashczAsync() {
+
+	var notifyResponse = map[string]string{
+		"return_code": wx_mp.PAY_FAIL,
+		"return_msg":  "",
+	}
+	body := self.Ctx.Input.CopyBody(102400)
+	params, err := wx_mp.ParsePayResult(body)
+	beego.BeeLogger.Warn("after_pay_controller.wxPayCashczAsync(%v)", params)
+
+	if err != nil {
+		beego.BeeLogger.Error("wxPayCashczAsync.parsePayResult=[%s] err=[%s]", string(body), err)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	// 签名校验参数
+	if !wx_mp.VerifyPayResult(params) {
+		beego.BeeLogger.Error("wxPayCashczAsync.VerifyPayResult not pass")
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	orderId := strings.Split(params.OutTradeNO, "_")[0]
+	order := balance_model.GetRechargeCashOrderByOId(orderId, false)
+	totalFee, _ := strconv.ParseInt(params.TotalFee, 10, 64)
+	if order == nil || order.TotalPrice != totalFee {
+		beego.BeeLogger.Error("wxPayCashczAsync.order err: %v", order)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	//重复回调通知时,判断订单状态是否处理过了
+	beego.BeeLogger.Warn("小程序余额充值微信支付回调通知,订单编号=%s", order.OrderId)
+	if order.State == 1 {
+		notifyResponse["return_code"] = wx_mp.PAY_SUCCESS
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	order.State = 1
+	order.TradeNo = params.TransactionId
+	order.PayWay = balance_model.PAY_WAY_TYPE_RECHARGE_WXPAY
+	order.PaiedAt = time.Now().Unix()
+	if err := order.Save(); err != nil {
+		beego.BeeLogger.Error("wxPayCashczAsync.weixinpay async return. save oId=%s fail", order.OrderId)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+	rechargeCash(order.WxUserId, order.UserId, order.TotalPrice, order.OrderId)
+
+	notifyResponse["return_code"] = wx_mp.PAY_SUCCESS
+	beego.BeeLogger.Warn("小程序余额充值微信支付回调通知,订单编号=%s  订单状态=%d", order.OrderId, order.State)
+	self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+	return
+
+}

+ 47 - 0
go/gopath/src/fohow.com/apps/controllers/pay_controller/init.go

@@ -0,0 +1,47 @@
+package pay_controller
+
+import (
+	// "fmt"
+	// 	// "math/rand"
+	// 	"encoding/xml"
+	// 	"net/url"
+	// 	// "strconv"
+	// "strings"
+	// "time"
+
+	// 	// // "d"
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+	// "fohow.com/cache"
+	// 	// "fohow.com/apps/controllers/user_controller"
+	// 	"fohow.com/apps/models/activity_model"
+	// 	"fohow.com/apps/models/address_model"
+	// 	"fohow.com/apps/models/order_model"
+	// "fohow.com/apps/models/project_join_model"
+	// 	"fohow.com/apps/models/shop_model"
+	// 	"fohow.com/apps/models/vas_model"
+	// 	"fohow.com/libs/alipay"
+	// 	"fohow.com/libs/wx_mp"
+)
+
+var (
+	//不需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"Pay", "PayAsync"}
+	exceptCheckWxUserLoginAction = []string{"Pay", "PayAsync"}
+)
+
+type PayController struct {
+	apps.BaseController
+}
+
+func (self *PayController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	// beego.BeeLogger.Info("invote controller Init func")
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+func (self *PayController) Home() {
+	self.Redirect("http://iwap.d5ct.com", 302)
+}

+ 52 - 0
go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_balance_controller.go

@@ -0,0 +1,52 @@
+package pay_controller
+
+import (
+	"fmt"
+
+	"github.com/astaxie/beego"
+
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/libs/wx_mp"
+)
+
+//代金券充值-微信支付
+func (self *PayController) wxPayBalance(orderId string) {
+	//user := self.GetCurrentUser(true)
+	//if user == nil{
+	//	self.ReturnError(403, apps.NoExist, "", nil)
+	//}
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	//查询充值订单信息
+	balanceOrder := balance_model.GetBalanceOrderByOId(orderId, false)
+
+	if balanceOrder == nil {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+	//不属于当前用户
+	if balanceOrder.WxUserId != wxUser.Id {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+	//无法支付,该订单已支付或者已过期
+	if balanceOrder.State == 1 {
+		self.ReturnError(403, apps.NotUnPay, "", nil)
+	}
+	totalPrice := balanceOrder.PaiedPrice
+
+	beego.BeeLogger.Debug("pay.total_price(%d)", totalPrice)
+	//获取预支付信息/v1/pay/:target:string/async/:payway:string
+	notifyUrl := fmt.Sprintf("%s/v1/pay/%s/async/%s", beego.AppConfig.String("ApiHost"), BALANCE_TARGET, balance_model.PAY_WAY_TYPE_SERVICE_WXPAY) // balance "service_wxpay"
+	body := "FOHOW玖玖-购买代金券"
+	//payData := wx_mp.GetPayDataLimitPay(wxUser.Openid, balanceOrder.OrderId, totalPrice, body, notifyUrl, self.Ctx.Input.IP())
+	payData := wx_mp.GetPayDataLimitPay(wxUser.Openid, balanceOrder.OrderId, totalPrice, body, notifyUrl, self.Ctx.Input.IP())
+	//返回数据
+	type PayData struct {
+		PayData map[string]string `json:"pay_data"`
+	}
+	self.Data["json"] = &PayData{PayData: payData}
+	self.ServeJSON()
+}

+ 48 - 0
go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_cashcz_controller.go

@@ -0,0 +1,48 @@
+package pay_controller
+
+import (
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/libs/wx_mp"
+	"github.com/astaxie/beego"
+)
+
+//余额充值-微信支付
+func (self *PayController) wxPayCashcz(orderId string) {
+	//user := self.GetCurrentUser(true)
+	//if user == nil{
+	//	self.ReturnError(403, apps.NoExist, "", nil)
+	//}
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	//查询充值订单信息
+	reCashOrder := balance_model.GetRechargeCashOrderByOId(orderId, false)
+
+	if reCashOrder == nil {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+	//不属于当前用户
+	if reCashOrder.WxUserId != wxUser.Id {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+	//无法支付,该订单已支付或者已过期
+	if reCashOrder.State == 1 {
+		self.ReturnError(403, apps.NotUnPay, "", nil)
+
+	}
+	//获取预支付信息/v1/pay/:target:string/async/:payway:string
+	notifyUrl := fmt.Sprintf("%s/v1/pay/%s/async/%s", beego.AppConfig.String("ApiHost"), CASH_TARGET, balance_model.PAY_WAY_TYPE_RECHARGE_WXPAY) // rechargeCashOrder "recharge_wxpay"
+	body := "FOHOW玖玖-余额充值"
+	payData := wx_mp.GetPayDataLimitPay(wxUser.Openid, reCashOrder.OrderId, reCashOrder.TotalPrice, body, notifyUrl, self.Ctx.Input.IP())
+
+	//返回数据
+	type PayData struct {
+		PayData map[string]string `json:"pay_data"`
+	}
+	self.Data["json"] = &PayData{PayData: payData}
+	self.ServeJSON()
+}

+ 82 - 0
go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_controller.go

@@ -0,0 +1,82 @@
+package pay_controller
+
+import (
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/order_model"
+	"github.com/astaxie/beego"
+	"strings"
+)
+
+const (
+	BALANCE_TARGET  = "balance"
+	EXCHANGE_TARGET = "exchange"
+	CASH_TARGET     = "cashcz"
+)
+
+type PayUrl struct {
+	PayUrl  string            `json:"pay_url"`
+	PayData map[string]string `json:"pay_data"`
+	OrderId string            `json:"order_id"`
+}
+
+func (self *PayController) Pay() {
+	oId := self.GetString("order_id")
+	payWay := self.GetString("pay_way")
+	tradPwd := self.GetString("trad_pwd")
+	source := self.GetString("s")
+
+	beego.BeeLogger.Debug("pay.oId(%s).payway(%s).(%v)", oId, payWay, payWay == balance_model.PAY_WAY_TYPE_RECHARGE_WXPAY)
+	if oId == "" || payWay == "" {
+		self.ReturnError(500, apps.ParamsError, "", nil)
+	}
+	target := getTargetByOId(oId)
+	returnUrl := fmt.Sprintf("%s/v1/pay/%s/sync/%s", beego.AppConfig.String("ApiHost"), target, payWay)
+	// notifyUrl := fmt.Sprintf("%s/v1/pay/%s/async/%s", beego.AppConfig.String("ApiHost"), target, payWay)
+
+	switch target {
+
+	case BALANCE_TARGET: // 代金券余额充值
+
+		switch payWay {
+		case balance_model.PAY_WAY_TYPE_SERVICE_WXPAY: //微信支付不需要输入交易密码
+			//暂只支付微信支付
+			self.wxPayBalance(oId)
+		default:
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+	case EXCHANGE_TARGET: // 商品购买
+		self.payExchange(oId, payWay, tradPwd, returnUrl, source)
+
+	case CASH_TARGET:
+		switch payWay {
+		case balance_model.PAY_WAY_TYPE_RECHARGE_WXPAY: //微信支付不需要输入交易密码
+			//暂只支付微信支付
+			self.wxPayCashcz(oId)
+		default:
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+
+	default:
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+}
+
+func getTargetByOId(oId string) string {
+
+	//余额充值
+	if strings.HasPrefix(oId, balance_model.ORDER_ID_PREFIX_CZCB) {
+		return CASH_TARGET
+	}
+
+	// 代金券充值
+	if strings.HasPrefix(oId, balance_model.ORDER_ID_PREFIX_CZ) {
+		return BALANCE_TARGET
+	}
+	// 兑换
+	if strings.HasPrefix(oId, order_model.ORDER_ID_PREFIX_EX) {
+		return EXCHANGE_TARGET
+	}
+	return ""
+}

+ 227 - 0
go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_exchange_controller.go

@@ -0,0 +1,227 @@
+package pay_controller
+
+import (
+	"fmt"
+	// "net/url"
+	// "strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	// "github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/address_model"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/merchant_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/pay_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/tool"
+	"fohow.com/libs/wx_mp"
+	"strings"
+	"sync"
+)
+
+var createDrawCode sync.Mutex
+var payOrder sync.Mutex
+
+//支付订单
+func (self *PayController) payExchange(oId, payWay, tradPwd, returnUrl, source string) {
+	payOrder.Lock()
+	defer payOrder.Unlock()
+	var payUrl string
+	var payData map[string]string
+	wxUId := self.GetCurrentWxUserId()
+	uId := self.GetCurrentUserId()
+
+	//地址
+	addressId, _ := self.GetInt64("address_id")
+	address := address_model.GetUserAddressById(addressId)
+	if address == nil || address.WxUserId != wxUId {
+		self.ReturnError(403, apps.AddressNotMatch, "", nil)
+	}
+
+	if source != order_model.SOURCE_XCX && source != order_model.SOURCE_GZH {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	//订单状态
+	order := order_model.GetOrderById(oId)
+	if order == nil || order.WxUserId != wxUId {
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	}
+	if order.Status != order_model.STATUS_UNPAY {
+		self.ReturnError(403, apps.NotUnPay, "", nil)
+	}
+	SaleNumsMap := make(map[int64]int64)
+
+	//获取购物商品明细
+	buy_price_total := int64(0)
+	total_price := int64(0)
+	list := order_model.GetAllDetailsOrderId(order.OrderId)
+	for _, item := range list {
+		//商品状态
+		product := product_model.GetProductById(item.ProductId, false)
+		if product == nil {
+			self.ReturnError(403, []string{apps.ProductNotExist[0], fmt.Sprintf("%s产品不存在", product.Name)}, "", nil)
+		}
+		//商品下架
+		if product.Status == product_model.PRODUCT_STATUS_DOWN {
+			self.ReturnError(403, []string{apps.ProductOffSale[0], fmt.Sprintf("%s产品已经下架", product.Name)}, "", nil)
+		}
+		//秒杀逻辑: 判断是否处于秒杀时间段内
+		if product.SeckilShowPrice > 0 {
+			now := time.Now()
+			if now.Unix() < product.SeckillStart.Unix() {
+				self.ReturnError(403, []string{apps.SeckillNotStart[0], fmt.Sprintf("%s秒杀活动尚未开始", product.Name)}, "", nil)
+			} else if now.Unix() > product.SeckillEnd.Unix() {
+				self.ReturnError(403, []string{apps.SeckillIsEnd[0], fmt.Sprintf("%s秒杀活动已经结束", product.Name)}, "", nil)
+			}
+		}
+		buy_price_total += product.BuyPrice * item.Count
+		if payWay == order_model.PAY_WAY_WEIXIN { //微信支付总价
+			total_price += product.Price * item.Count
+		} else if payWay == order_model.PAY_WAY_BALANCE { //代金券支付总价
+			total_price += product.Price * item.Count
+		}
+		SaleNumsMap[product.Id] = item.Count
+	}
+	order.PayWay = payWay
+	order.Contact = address.Contact
+	order.Tel = address.Tel
+	order.Address = fmt.Sprintf("%s%s%s%s", address.Province, address.City, address.District, address.Address)
+	order.BuyPrice = buy_price_total
+	order.TotalPrice = total_price
+
+	switch payWay { // 1.代金券购买 2.微信支付
+	case pay_model.PAYWAY_BALANCE: // 代金券购买
+
+		curWxUser := user_model.GetWxUserById(wxUId, false)
+		if curWxUser == nil {
+			self.ReturnError(403, apps.UserNeedLogin, "", nil)
+		}
+		//黑名单用户
+		curUser := user_model.GetUserById(curWxUser.UserId, false)
+		if curUser != nil && curUser.IsBlackUser == 1 {
+			self.ReturnError(403, apps.NetworkBusy, "", nil)
+		}
+		//再次增加订单状态判断
+		if order.Status != order_model.STATUS_UNPAY {
+			self.ReturnError(403, apps.NotUnPay, "", nil)
+		}
+
+		userLeftBalanceCount := balance_model.GetUserTotalBalance(wxUId)
+		tp := order.TotalPrice
+		freight := order_model.FREIGHT
+		if tp >= order_model.FREIGHT_LIMIT || beego.AppConfig.String("RunMode") == "dev" {
+			freight = int64(0)
+		}
+		tp += freight
+		if userLeftBalanceCount < tp {
+			self.ReturnError(403, apps.BalanceNotEnough, "", nil)
+		}
+
+		if tp > 0 {
+			source := balance_model.BALANCE_SOURCE_EXCHANGE_PRODUCT
+			remark := fmt.Sprintf("代金券兑换商品")
+			new(balance_model.Balance).Create(wxUId, uId, -tp, source, oId, remark)
+		}
+
+		//更新订单状态
+		order.Status = order_model.STATUS_PROCESSING
+		order.PaiedAt = time.Now().Unix()
+		order.PaiedTime = time.Now()
+		order.PaiedPrice = order.TotalPrice
+		order.PayWay = pay_model.PAYWAY_BALANCE
+		order.Source = source
+		order.Save()
+		//发放赠品
+		productId := int64(0)
+		if beego.AppConfig.String("RunMode") == "dev" {
+			productId = int64(91)
+		} else {
+			productId = order_model.SEND_PRODUCT_ID
+		}
+		saleList := order_model.GetWxUserOrders(curWxUser.Id)
+		//beego.BeeLogger.Error("en(saleList)=%d", len(saleList))
+		//	七月注册会员首单赠送赠品
+		if order.TotalPrice >= order_model.PROMOTION_TOTAL && len(saleList) == 1 {
+			product := product_model.GetProductById(productId, false)
+			go order_model.SendCreate(order.OrderId, order.Id, productId, product.Price, product.Price, product.Name, int64(1))
+		}
+
+		//更新已售数量
+		go order_model.UpdateSaleNums(SaleNumsMap)
+		//go CreateOrderNotify(order, product)
+		//wxUser := user_model.GetWxUserById(order.WxUserId, true)
+		//go sendInviterBenefit(wxUser, order.OrderId, user_model.SOURCE_PRODUCT_BENEFIT)
+		payUrl, payData = fmt.Sprintf("%s?order_id=%s", returnUrl, order.OrderId), nil
+		result := PayUrl{PayUrl: payUrl, PayData: payData, OrderId: order.OrderId}
+		self.Data["json"] = self.FormatResult([]interface{}{result})
+	case pay_model.PAYWAY_WEIXINPAY: // 微信支付
+
+		wxUser := self.GetCurrentWxUser(false)
+
+		order.Contact = address.Contact
+		order.Tel = address.Tel
+		order.Address = fmt.Sprintf("%s%s%s%s", address.Province, address.City, address.District, address.Address)
+		order.Source = source
+		order.Save()
+
+		if order.Source == order_model.SOURCE_XCX { //小程序微信支付
+			freight := order_model.FREIGHT
+			if order.TotalPrice >= order_model.FREIGHT_LIMIT || beego.AppConfig.String("RunMode") == "dev" {
+				freight = int64(0)
+			}
+			order.TotalPrice += freight
+			notifyUrl := fmt.Sprintf("%s/v1/pay/%s/async/%s", beego.AppConfig.String("ApiHost"), EXCHANGE_TARGET, pay_model.PAYWAY_WEIXINPAY)
+			body := fmt.Sprintf("FOHOW玖玖-购买商品")
+			payData := wx_mp.GetPayData(wxUser.Openid, order.OrderId, order.TotalPrice, body, notifyUrl, self.Ctx.Input.IP())
+
+			//返回数据
+			type PayData struct {
+				PayData map[string]string `json:"pay_data"`
+			}
+			self.Data["json"] = &PayData{PayData: payData}
+
+		} else { //公众号微信支付
+
+			notifyUrl := fmt.Sprintf("%s/v1/pay/%s/async/%s", beego.AppConfig.String("ApiHost"), EXCHANGE_TARGET, pay_model.PAYWAY_WEIXINPAY)
+			body := fmt.Sprintf("FOHOW玖玖-购买商品")
+			wxGzh := user_model.GetWxUserGzhByWxUIdAndAppId(wxUser.Id, beego.AppConfig.String("WxMPAppId"), false)
+			if wxGzh == nil {
+				self.ReturnError(403, apps.NoExist, "", nil)
+			}
+			//payData := wx_mp.GetPayData(wxGzh.GzhOpenId, order.OrderId , order.TotalPrice, body,notifyUrl, self.Ctx.Input.IP())
+			payData := wx_mp.GetGzhPayData(wxGzh.GzhOpenId, order.OrderId, order.TotalPrice, body, notifyUrl, self.Ctx.Input.IP())
+
+			//返回数据
+			type PayData struct {
+				PayData map[string]string `json:"pay_data"`
+			}
+			self.Data["json"] = &PayData{PayData: payData}
+		}
+	default:
+		beego.BeeLogger.Error("pay way not match, payway:%s", payWay)
+	}
+	self.ServeJSON()
+}
+
+//用户支付成功,后给卖家所有管理员发下单通知
+func CreateOrderNotify(order *order_model.Order, product *product_model.Product) {
+	merchantUserList := merchant_model.GetMerchantUserRelationListByMerchantId(product.MerchantId, true)
+	for _, item := range merchantUserList {
+		if item == nil {
+			continue
+		}
+		isManageTheProduct, _ := tool.Contain(fmt.Sprintf("%d", product.Id), strings.Split(item.ManageProductIds, ","))
+		if item.ManageProductIds == "0" || isManageTheProduct {
+			sellerWxUser := user_model.GetWxUserByUserId(item.UserId, true)
+			if sellerWxUser != nil {
+				helpers.OrderCreateNotify(*sellerWxUser, order.CreatedAt, product.Name, order.OrderId, "您有新的商品订单,请及时处理发货。", order_model.STATUS_CN_TEXT[order.Status], order.Count, fmt.Sprintf("pages/start/start?url=packageMerchant/pages/merchant/orders/orders&id=%d", item.MerchantId))
+			}
+		}
+	}
+}

+ 128 - 0
go/gopath/src/fohow.com/apps/controllers/pay_controller/recharge_controller.go

@@ -0,0 +1,128 @@
+package pay_controller
+
+import (
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"github.com/astaxie/beego"
+	"strings"
+)
+
+//代金券充值订单
+func (self *PayController) CreateBalanceOrder() {
+
+	//校验兑换代金券数量,为正整数
+	//count, _ := self.GetInt64("count") //兑换数量
+	count := balance_model.BALANCE_PAIED
+
+	if count <= 0 {
+		self.ReturnError(403, apps.RechargeCountWrong, "", nil)
+	}
+	payway := self.GetString("payway")
+
+	//buyId, _ := self.GetInt64("buy_id")代金券购买类型:10000代金券花费100元...
+	//展开购买类型的业务逻辑
+
+	user := self.GetCurrentUser(true)
+	if user == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	if payway != balance_model.PAY_WAY_TYPE_SERVICE_WXPAY { //暂只支持微信支付方式
+		self.ReturnError(403, apps.PayWayNoMatch, "", nil)
+	}
+	tp := int64(0)
+	if count == balance_model.BALANCE_PAIED {
+		tp = balance_model.BALANCE_UPGRADE
+	} else {
+		tp = count
+	}
+	//初始化充值订单信息
+	balanceOrder := new(balance_model.BalanceOrder).Create(wxUser.Id, user.Id, tp, count, balance_model.PAY_WAY_TYPE_SERVICE_WXPAY)
+
+	self.Data["json"] = balanceOrder
+	self.ServeJSON()
+}
+
+//添加代金券记录
+func recharge(wxUId, uId, c int64, rId string) {
+	source := balance_model.BALANCE_SOURCE_RECHARGE
+	item := balance_model.GetBalanceBySourceAndRId(source, rId)
+	if item != nil {
+		return
+	}
+	b := new(balance_model.Balance).Create(wxUId, uId, c, source, rId, fmt.Sprintf("¥%0.2f", float64(c)/100))
+	if b != nil {
+		go rechargeNotice(uId, b)
+	}
+}
+
+//代金券充值成功通知
+func rechargeNotice(uId int64, balance *balance_model.Balance) {
+	//user := user_model.GetUserById(uId, false)
+	//openId := user.GetOpenid()
+	//if openId == "" {
+	//	beego.BeeLogger.Error("Recharge notice, didn't get openId of user:[%s]", user.Tel)
+	//	return
+	//}
+	/*url := ""
+	if beego.AppConfig.String("RunMode") == "prod" {
+		// url = "http://api.d5c360.com/v1/cfc/25"
+		url = fmt.Sprintf("%s/v1/cfc/25", beego.AppConfig.String("ApiHost"))
+	} else {
+		url = fmt.Sprintf("%s/user/balance", beego.AppConfig.String("MHost"))
+	}
+	title := "成功充值到账, 快去选购众筹项目吧!\n"
+	amount := fmt.Sprintf("%0.2f 元", float64(balance.Count)/100)
+	account := user.Tel
+	rechargeType := "线上充值"
+	cTime := balance.CreatedAt.Format("2006-01-02 15:04:05")
+	remark := "\n点击可查看交易明细>"
+	beego.BeeLogger.Warn("Recharge notice to user:[%s], openId:[%s], amount:[%s]", user.RealName, openId, amount)*/
+}
+
+//余额充值订单
+func (self *PayController) CreateRechargeCashOrder() {
+
+	//校验兑换代金券数量,为正整数
+	count, _ := self.GetInt64("count") //兑换数量
+	//count := balance_model.BALANCE_PAIED
+	if count <= 0 {
+		self.ReturnError(403, apps.RechargeCountWrong, "", nil)
+	}
+	payway := strings.TrimSpace(self.GetString("payway"))
+	beego.BeeLogger.Debug("createRechargeCashOrder.payway: %s, %s, %v", payway, balance_model.PAY_WAY_TYPE_RECHARGE_WXPAY, payway != balance_model.PAY_WAY_TYPE_RECHARGE_WXPAY)
+
+	user := self.GetCurrentUser(true)
+	if user == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	if payway != balance_model.PAY_WAY_TYPE_RECHARGE_WXPAY { //暂只支持微信支付方式
+		self.ReturnError(403, apps.PayWayNoMatch, "", nil)
+	}
+
+	//初始化充值订单信息
+	rechargeCashOrder := new(balance_model.RechargeCashOrder).Create(wxUser.Id, user.Id, count, balance_model.PAY_WAY_TYPE_RECHARGE_WXPAY)
+
+	self.Data["json"] = rechargeCashOrder
+	self.ServeJSON()
+}
+
+//余额记录
+func rechargeCash(wxUId, uId, c int64, rId string) {
+	source := balance_model.CASH_SOURCE_RECHARGE_CASH
+	item := balance_model.GetCashBalanceBySourceAndRId(source, rId)
+	if item != nil {
+		return
+	}
+	new(balance_model.CashBalance).Create(wxUId, c, source, rId, fmt.Sprintf("¥%0.2f", float64(c)/100))
+}

+ 863 - 0
go/gopath/src/fohow.com/apps/controllers/permit_controller/permit_controller.go

@@ -0,0 +1,863 @@
+package permit_controller
+
+import (
+	"fmt"
+	"strconv"
+
+	// "math/rand"
+	// "crypto/md5"
+	// "encoding/hex"
+	// "strconv"
+	"encoding/json"
+	"strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"github.com/uuid"
+
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	// "fohow.com/apps/controllers/user_controller"
+	// "fohow.com/apps/models/activity_model"
+	// "fohow.com/apps/models/order_model"
+	// "fohow.com/apps/models/product_model"
+	// "fohow.com/apps/models/shop_model"
+	// "fohow.com/apps/models/sms_model"
+	"fohow.com/apps/models/user_model"
+	// "fohow.com/apps/models/wx_gongzhonghao_model"
+	"fohow.com/cache"
+	// "fohow.com/libs/tool"
+	"fohow.com/libs/wx_mp"
+	// "fohow.com/libs/wx_open"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction   = []string{"*"}
+	exceptCheckWxUserLoginAction = []string{"*"}
+)
+
+type PermitController struct {
+	apps.BaseController
+}
+
+func (self *PermitController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+//小程序授权
+func (self *PermitController) XcxAuthorize() {
+	params := self.GetString("userinfo")
+	channel, _ := self.GetInt64("channel", 0)
+	// beego.BeeLogger.Warn("XcxAuthorize userinfo: %s", params)
+	inviteId, _ := self.GetInt64("invite_id", 0)
+	beego.BeeLogger.Warn("XcxAuthorize inviteId: %d", inviteId)
+	type UserInfo struct {
+		NickName  string `json:"nickName"`  // 用户的昵称
+		Gender    int64  `json:"gender"`    // 用户的性别, 值为1时是男性, 值为2时是女性, 值为0时是未知
+		Language  string `json:"language"`  // 用户的语言, zh_CN, zh_TW, en
+		City      string `json:"city"`      // 用户所在城市
+		Province  string `json:"province"`  // 用户所在省份
+		Country   string `json:"country"`   // 用户所在国家
+		AvatarUrl string `json:"avatarUrl"` // 头像
+	}
+	type Info struct {
+		ErrMsg        string    `json:"errMsg"`
+		RawData       string    `json:"rawData"`
+		Signature     string    `json:"signature"`
+		Iv            string    `json:"iv"`
+		EncryptedData string    `json:"encryptedData"`
+		UserInfo      *UserInfo `json:"userInfo"`
+	}
+	info := new(Info)
+	err := json.Unmarshal([]byte(params), &info)
+
+	if err != nil {
+		beego.BeeLogger.Error("XcxAuthorize err: %s, info:%s", err, info)
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	sessionKey, _ := self.GetSession(apps.XcxSessionKey).(string)
+	// beego.BeeLogger.Warn("sessionKey:%s", sessionKey)
+	type EncryptedData struct {
+		UnionId string `json:"unionId"`
+		OpenId  string `json:"openId"`
+	}
+
+	if sessionKey == "" {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+
+	pc := helpers.WxBizDataCrypt{AppID: beego.AppConfig.String("WxFohowXcxAppId"), SessionKey: sessionKey}
+	// beego.BeeLogger.Warn("EncryptedData:%s", info.EncryptedData)
+	// beego.BeeLogger.Warn("Iv:%s", info.Iv)
+	result, err := pc.Decrypt(info.EncryptedData, info.Iv, true) //第三个参数解释: 需要返回 JSON 数据类型时 使用 true, 需要返回 map 数据类型时 使用 false
+	if err != nil {
+		beego.BeeLogger.Error("xcx XcxAuthorize descrypt failed, err:%s", err)
+		self.ReturnError(403, apps.XcxAuthorizeError, "", nil)
+	}
+	encryptedData := &EncryptedData{}
+	json.Unmarshal([]byte(result.(string)), encryptedData)
+	if encryptedData.UnionId == "" || encryptedData.OpenId == "" {
+		self.ReturnError(403, apps.UserAuthorizeFailed, "", nil)
+	}
+	wxUser := user_model.GetWxUserByUnionid(encryptedData.UnionId, false)
+	ip := self.Ctx.Input.IP()
+	//注册会员
+	user := user_model.Create("", ip)
+	if user == nil {
+		self.ReturnError(403, apps.RegisterUserError, "", nil)
+	}
+	if wxUser != nil {
+		wxUser.Openid = encryptedData.OpenId
+	} else {
+		introUserId := int64(1)
+		if inviteId == int64(0) {
+			inviteId = int64(1)
+		}
+		beego.BeeLogger.Warn("XcxAuthorize_quickCreate_inviteId:%d", inviteId)
+		inviter := user_model.GetWxUserById(inviteId, false)
+		if inviter != nil {
+			if inviter.ShowInviteMode == 1 && inviteId != int64(1) {
+				introUserId = inviter.Id
+			} else {
+				introUserId = inviter.IntroUserId
+			}
+		}
+		wxUser = new(user_model.WxUser).QuickCreate(encryptedData.OpenId, encryptedData.UnionId, ip, channel, time.Now().Unix(), user.Id, inviteId, introUserId)
+	}
+	user.Nickname = wxUser.Nickname
+	user.Country = wxUser.Country
+	user.Province = wxUser.Province
+	user.City = wxUser.City
+	user.Sex = wxUser.Sex
+	//参数第一,cookie第二
+	cId, _ := strconv.ParseInt(self.Ctx.GetCookie("sign_up_channel"), 10, 64)
+	user.SignupChannelId = cId
+	user.Save()
+	if user != nil {
+		self.SetSession(apps.SessionUserKey, user.Id)
+	}
+	//wxUser.UserId = user.Id
+	wxUser.Nickname = info.UserInfo.NickName
+	wxUser.Sex = info.UserInfo.Gender
+	wxUser.City = info.UserInfo.City
+	wxUser.Province = info.UserInfo.Province
+	wxUser.Country = info.UserInfo.Country
+	// beego.BeeLogger.Warn("XcxAuthorize wxUser before save() Nickname:%s, Sex:%s, City:%s, Province:%s, Country:%s ", wxUser.Nickname, wxUser.Sex, wxUser.City, wxUser.Province, wxUser.Country)
+	wxUser.Save()
+	// beego.BeeLogger.Warn("XcxAuthorize wxUser after save() Nickname:%s, Sex:%s, City:%s, Province:%s, Country:%s ", wxUser.Nickname, wxUser.Sex, wxUser.City, wxUser.Province, wxUser.Country)
+	wxUser.UploadHead(info.UserInfo.AvatarUrl)
+
+	if wxUser != nil {
+		self.SetSession(apps.SessionWxUserKey, wxUser.Id)
+	}
+	// 如果微信用户已绑定手机,则找出userId,并且赋值给session[userId]
+	if wxUser != nil && wxUser.UserId > 0 {
+		user := user_model.GetUserById(wxUser.UserId, false)
+		user.CopyWxUserHead(wxUser.Head)
+		self.SetSession(apps.SessionUserKey, wxUser.UserId)
+	}
+	self.Data["json"] = encryptedData
+	self.ServeJSON()
+}
+
+//小程序登录
+func (self *PermitController) XcxLogin() {
+	code := self.GetString("code")
+	if code == "" {
+		self.ReturnError(403, apps.ParamsRequired, "", nil)
+	}
+	appId := beego.AppConfig.String("WxFohowXcxAppId")
+	appSecret := beego.AppConfig.String("WxFohowXcxAppSecret")
+	key := wx_mp.GetXcxSessionKey(appId, appSecret, code)
+	if key == nil {
+		self.ReturnError(403, apps.XcxGetSessionKeyError, "", nil)
+	}
+	// beego.BeeLogger.Warn("XcxLogin key=%s", key)
+	// beego.BeeLogger.Warn("XcxLogin key=%s, key.Openid=%s", key, key.Openid)
+	wxUser := user_model.GetByOpenid(key.Openid, false)
+	// beego.BeeLogger.Warn("XcxLogin key=[%s], key.Openid=[%s], wxUser= [%s]", key, key.Openid, wxUser)
+
+	if wxUser != nil {
+		wxUser.FullHead = self.GetFullImgUrl(wxUser.Head)
+		self.SetSession(apps.SessionWxUserKey, wxUser.Id)
+	}
+
+	// 如果微信用户已绑定手机,则找出userId,并且赋值给session[userId]
+	if wxUser != nil && wxUser.UserId > 0 {
+		self.SetSession(apps.SessionUserKey, wxUser.UserId)
+	}
+
+	self.SetSession(apps.XcxSessionKey, key.SessionKey)
+	// beego.BeeLogger.Warn("XcxLogin SessionKey=%s", key.SessionKey)
+	if self.CruSession == nil {
+		self.ReturnError(200, apps.NoExist, "", nil)
+	}
+	sId := self.CruSession.SessionID()
+	// beego.BeeLogger.Warn("XcxLogin sId=%s", sId)
+	type Ret struct {
+		SessionKey string             `json:"session_key"`
+		WxUser     *user_model.WxUser `json:"wx_user"`
+	}
+	self.Data["json"] = &Ret{SessionKey: sId, WxUser: wxUser}
+
+	self.ServeJSON()
+}
+
+//生成订单ID
+func createUnionId(prefix string) string {
+	n := time.Now().Format("20060102150405")
+	u := uuid.NewV4().String()
+	c := strings.Split(u, "-")
+	oId := strings.ToUpper(fmt.Sprintf("%s%s%s", prefix, n, c[0]))
+	beego.BeeLogger.Info("createUnionId=%s", oId)
+	return oId
+}
+
+func (self *PermitController) XcxTest() {
+	wxUser := self.GetCurrentWxUser(true)
+	self.Data["json"] = wxUser
+	self.ServeJSON()
+}
+
+// // 统一登录路径
+// // 该接口尽量不被前端调用,供服务器开发者使用
+// func (self *PermitController) Login() {
+// 	cb := self.GetString("cb")
+// 	url := ""
+// 	if self.IsWxClient() {
+// 		url = fmt.Sprintf("%s/login/mp?cb=%s", beego.AppConfig.String("ApiHost"), cb)
+// 	}
+// 	self.Redirect(url, 302)
+// 	return
+// }
+
+// // 退出登录
+// func (self *PermitController) Logout() {
+// 	self.DelSession(apps.SessionUserKey)
+// 	self.DelSession(apps.SessionWxUserKey)
+// 	self.ReturnError(200, apps.HasLogout, "", nil)
+// }
+
+//公众号静默授权回调
+func (self *PermitController) AfterWxMpAuth() {
+	// beego.BeeLogger.Warn("AfterWxMpAuth........")
+	code := self.GetString("code")
+	// state := self.GetString("state")
+	// _id := self.Ctx.Input.Param(":id")
+	// id, _ := strconv.ParseInt(_id, 10, 64)
+	// gzh := wx_gongzhonghao_model.GetGZHById(id, true)
+	// if gzh == nil {
+	// 	self.ReturnError(403, apps.GongZhongHaoNoExist, "", nil)
+	// }
+	a := beego.AppConfig.String("WxMPAppId")
+	s := beego.AppConfig.String("WxMPAppSecret")
+	// mpId := gzh.WxHao
+	token := wx_mp.AuthExchangeToken(code, a, s)
+	if token == nil {
+		beego.BeeLogger.Error("AfterSilenceMpAuth, exchangetoken err")
+		self.ReturnError(403, apps.NetworkBusy, "", nil)
+	}
+
+	// mpOpenid := token.OpenId
+	// unionId := token.UnionId
+	// beego.BeeLogger.Warn("openid: %s", mpOpenid)
+	// beego.BeeLogger.Warn("unionid: %s", unionId)
+	// beego.BeeLogger.Warn("token: %v", token)
+	// wxUser := user_model.GetWxUserByUnionid(unionId, false)
+	// authWxUser := user_model.GetAuthWxUserByMpIdAndUnionId(mpId, unionId, false)
+	// if authWxUser == nil && wxUser != nil {
+	// 	go new(user_model.AuthWxUser).Create(mpId, mpOpenid, unionId, wxUser.Id)
+	// }
+	// if cbUrl, ok := cache.Cache.Get(state).(string); ok {
+	// 	if cbUrl == "" {
+	// 		self.Redirect(beego.AppConfig.String("MHost"), 302)
+	// 		return
+	// 	}
+	// 	if !strings.HasPrefix(cbUrl, "http://") {
+	// 		cbUrl = fmt.Sprintf("%s%s", beego.AppConfig.String("MHost"), cbUrl)
+	// 	}
+	// 	self.Redirect(cbUrl, 302)
+	// 	return
+	// } else {
+	// 	self.Redirect(beego.AppConfig.String("MHost"), 302)
+	// 	return
+	// }
+	self.ServeJSON()
+}
+
+// //公众号静默授权
+// func (self *PermitController) WxMpAuth() {
+// 	// beego.BeeLogger.Warn("WxMpAuth.......")
+// 	if !self.IsWxClient() {
+// 		self.ReturnError(403, apps.NotWeixinClient, "", nil)
+// 	}
+// 	_id := self.Ctx.Input.Param(":id")
+// 	cb := self.GetString("cb")
+// 	id, _ := strconv.ParseInt(_id, 10, 64)
+// 	gzh := wx_gongzhonghao_model.GetGZHById(id, true)
+// 	if gzh == nil {
+// 		self.ReturnError(403, apps.GongZhongHaoNoExist, "", nil)
+// 	}
+// 	appId := gzh.AppId
+// 	u := strings.Split(uuid.NewV4().String(), "-")[0]
+// 	state := fmt.Sprintf("AuthCb[%s]", u)
+// 	cache.Cache.Put(state, cb, 60*time.Second)
+// 	redirectURI := fmt.Sprintf("%s/auth/mp/%d/after",
+// 		beego.AppConfig.String("ApiHost"), gzh.Id)
+// 	scope := "snsapi_base"
+// 	url := wx_mp.AuthCodeURL(appId, redirectURI, scope, state)
+// 	// beego.BeeLogger.Warn("wx mp auth, redirect url: %s", url)
+// 	self.Redirect(url, 302)
+// }
+
+// //check是否授权过某个公众号
+// func (self *PermitController) CheckWxAuth() {
+// 	_gId := self.Ctx.Input.Param(":id")
+// 	gId, _ := strconv.ParseInt(_gId, 10, 64)
+// 	gzh := wx_gongzhonghao_model.GetGZHById(gId, true)
+// 	type Ret struct {
+// 		IsAuth int64 `json:"is_auth"`
+// 	}
+// 	var auth int64 = 0
+// 	wxUser := self.GetCurrentWxUser(true)
+// 	if gzh != nil {
+// 		authWxUser := user_model.GetAuthWxUserByMpIdAndUnionId(gzh.WxHao, wxUser.Unionid, false)
+// 		if authWxUser != nil {
+// 			auth = 1
+// 		}
+// 	}
+// 	// beego.BeeLogger.Warn("check auth: %v", auth)
+// 	self.Data["json"] = &Ret{IsAuth: auth}
+// 	self.ServeJSON()
+// }
+
+// 微信公众号平台登录
+func (self *PermitController) WxMpLogin() {
+	cb := self.GetString("cb")
+	if self.IsWxClient() {
+		_, ok := self.Ctx.Input.Session(apps.SessionWxUserKey).(int64)
+
+		wxUser := self.GetCurrentWxUser(true)
+		// beego.BeeLogger.Warn("wxMpLogin_wxuser_info:%v", wxUser)
+
+		if ok {
+			if wxUser != nil {
+				wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(wxUser.Id, beego.AppConfig.String("WxMPAppId"), false)
+				if wxUserGzh != nil && wxUserGzh.GzhOpenId != "" {
+					self.ReturnError(403, apps.HasLogin, "", nil)
+				}
+			}
+		}
+		u := strings.Split(uuid.NewV4().String(), "-")[0]
+		state := fmt.Sprintf("loginCb[%s]", u)
+		cache.Cache.Put(state, cb, 60*time.Second)
+		appId := beego.AppConfig.String("WxMPAppId")
+		redirectURI := fmt.Sprintf("%s/login/mp/after",
+			beego.AppConfig.String("ApiHost"))
+
+		scope := "snsapi_userinfo"
+		url := wx_mp.AuthCodeURL(wx_mp.DefaultAuthCodeRequestUrl, appId, redirectURI, scope, state)
+		self.Redirect(url, 302)
+		return
+	} else {
+		self.ReturnError(403, apps.NotWeixinClient, "", nil)
+	}
+}
+
+// 微信公众号平台登录回调
+func (self *PermitController) AfterWxMpLogin() {
+	code := self.GetString("code")
+	state := self.GetString("state")
+	a := beego.AppConfig.String("WxMPAppId")
+	s := beego.AppConfig.String("WxMPAppSecret")
+	token := wx_mp.AuthExchangeToken(code, a, s)
+	// beego.BeeLogger.Warn("afterWxMpLogin_token: %v", token)
+	if token == nil {
+		beego.BeeLogger.Error("AfterWxAutoLogin, after login, exchangetoken err")
+		self.ReturnError(403, apps.NetworkBusy, "", nil)
+	}
+	mpOpenid := token.OpenId
+	unionId := token.UnionId
+	// beego.BeeLogger.Warn("AfterWxMpLogin_token: %s", unionId)
+	wxUser := user_model.GetWxUserByUnionid(unionId, false)
+	var wxUserGzh *user_model.WxUserGongzhonghao
+	if wxUser != nil {
+		wxUserGzh = user_model.GetWxUserGzhByOpenId(mpOpenid, false)
+	}
+	// beego.BeeLogger.Warn("AfterWxMpLogin_wxUserGzh:%v ", wxUserGzh)
+
+	if wxUser == nil {
+		info := wx_mp.AuthUserInfo(token, mpOpenid, a, s)
+		if info != nil {
+			// beego.BeeLogger.Warn("AfterWxAutoLogin, info=%v", info)
+			ip := self.Ctx.Input.IP()
+			wxUser = new(user_model.WxUser).Create("", unionId, info.Nickname,
+				info.City, info.Country, info.Province, ip, info.Sex, 0, 0, 0)
+
+			beego.BeeLogger.Info("AfterWxAutoLogin, upload WxUserHead with URL: %s", info.HeadImageURL)
+			if wxUser != nil {
+				go wxUser.UploadHead(info.HeadImageURL)
+			}
+			if wxUserGzh == nil {
+				beego.BeeLogger.Info("wxUser: %v, info: %v")
+				wxUserGzh = new(user_model.WxUserGongzhonghao).Create(mpOpenid, a, wxUser.Id, info.Subscribe, info.SubscribeTime)
+			}
+
+			if wxUserGzh != nil && wxUserGzh.WxUserId != wxUser.Id {
+				wxUserGzh.WxUserId = wxUser.Id
+				wxUserGzh.Save()
+			}
+		}
+	} else {
+		if wxUser.Head == "" {
+			info := wx_mp.AuthUserInfo(token, mpOpenid, a, s)
+			if info != nil {
+				go wxUser.UploadHead(info.HeadImageURL)
+			}
+		}
+		if wxUserGzh == nil {
+			info := wx_mp.AuthUserInfo(token, mpOpenid, a, s)
+			if info != nil {
+				wxUserGzh = new(user_model.WxUserGongzhonghao).Create(mpOpenid, a, wxUser.Id, info.Subscribe, info.SubscribeTime)
+			}
+		}
+
+		if wxUserGzh != nil && wxUserGzh.WxUserId != wxUser.Id {
+			wxUserGzh.WxUserId = wxUser.Id
+			wxUserGzh.Save()
+		}
+	}
+
+	if wxUser != nil && wxUserGzh != nil && wxUserGzh.GzhOpenId != mpOpenid {
+		wxUserGzh.GzhOpenId = mpOpenid
+		go wxUserGzh.UpdateField("GzhOpenId")
+	}
+	if wxUser != nil {
+		self.SetSession(apps.SessionWxUserKey, wxUser.Id)
+	}
+
+	// 如果微信用户已绑定手机,则找出userId,并且赋值给session[userId]
+	if wxUser != nil && wxUser.UserId > 0 {
+		self.SetSession(apps.SessionUserKey, wxUser.UserId)
+		// beego.BeeLogger.Warn("AfterWxAutoLogin, wxUser.UserId=%d", wxUser.UserId)
+	}
+	if cbUrl, ok := cache.Cache.Get(state).(string); ok {
+		if cbUrl == "" {
+			self.Redirect(beego.AppConfig.String("WxHost"), 302)
+			return
+		}
+		if !strings.HasPrefix(cbUrl, "http://") && !strings.HasPrefix(cbUrl, "https://") {
+			cbUrl = fmt.Sprintf("%s%s", beego.AppConfig.String("WxHost"), cbUrl)
+		}
+		self.Redirect(cbUrl, 302)
+		return
+	} else {
+		self.Redirect(beego.AppConfig.String("WxHost"), 302)
+		return
+	}
+	// beego.BeeLogger.Error("AfterWxAutoLogin, after login, something err")
+	self.ReturnError(403, apps.NetworkBusy, "", nil)
+}
+
+// 网站微信登录
+func (self *PermitController) PcWxLogin() {
+	cb := self.GetString("cb")
+	beego.BeeLogger.Warn("PcWxLogin %s", self.Ctx.Input.UserAgent())
+
+	//已经登录了的情况
+	_, ok := self.Ctx.Input.Session(apps.SessionWxUserKey).(int64)
+	wxUser := self.GetCurrentWxUser(true)
+	if ok && wxUser != nil {
+		if cb == "" {
+			self.Redirect(beego.AppConfig.String("PcHost"), 302)
+			return
+		}
+		if !strings.HasPrefix(cb, "http://") && !strings.HasPrefix(cb, "https://") {
+			cb = fmt.Sprintf("%s%s", beego.AppConfig.String("PcHost"), cb)
+		}
+		self.Redirect(cb, 302)
+		return
+	}
+
+	//内网登录的情况
+	if beego.BConfig.RunMode == beego.DEV {
+		wxUId, _ := self.GetInt64("wxuid")
+		wxUser := user_model.GetWxUserById(wxUId, true)
+		if wxUser != nil {
+			if wxUser.UserId > 0 {
+				self.SetSession(apps.SessionUserKey, wxUser.UserId)
+			}
+			beego.BeeLogger.Warn("beego.AppConfig.String(PcHost) %s", beego.AppConfig.String("PcHost"))
+			if cb == "" {
+				self.Redirect(beego.AppConfig.String("PcHost"), 302)
+				return
+			}
+			if !strings.HasPrefix(cb, "http://") && !strings.HasPrefix(cb, "https://") {
+				cb = fmt.Sprintf("%s%s", beego.AppConfig.String("PcHost"), cb)
+			}
+			beego.BeeLogger.Warn("beego.AppConfig.String(PcHost) cb= %s", cb)
+			self.Redirect(cb, 302)
+			return
+		} else {
+			self.ReturnError(403, apps.UserNotExist, "", nil)
+		}
+	}
+
+	u := strings.Split(uuid.NewV4().String(), "-")[0]
+	state := fmt.Sprintf("pcLoginCb[%s]", u)
+	cache.Cache.Put(state, cb, 60*time.Second)
+
+	appId := beego.AppConfig.String("PcWxMPAppId")
+	redirectURI := fmt.Sprintf("%s/pc/login/after", beego.AppConfig.String("ApiHost"))
+	scope := "snsapi_login"
+
+	url := wx_mp.AuthCodeURL(wx_mp.PcAuthCodeRequestUrl, appId, redirectURI, scope, state)
+	self.Redirect(url, 302)
+	return
+}
+
+// 网站微信登录回调
+func (self *PermitController) AfterPcWxLogin() {
+	code := self.GetString("code")
+	state := self.GetString("state")
+	a := beego.AppConfig.String("PcWxMPAppId")
+	s := beego.AppConfig.String("PcWxMPAppSecret")
+	token := wx_mp.AuthExchangeToken(code, a, s)
+	// beego.BeeLogger.Warn("afterWxMpLogin_token: %v", token)
+	if token == nil {
+		beego.BeeLogger.Error("PcWxMpLogin login, exchangetoken err")
+		self.ReturnError(403, apps.NetworkBusy, "", nil)
+	}
+	mpOpenid := token.OpenId
+	unionId := token.UnionId
+	wxUser := user_model.GetWxUserByUnionid(unionId, false)
+
+	if wxUser == nil {
+		info := wx_mp.AuthUserInfo(token, mpOpenid, a, s)
+		if info != nil {
+			// beego.BeeLogger.Warn("AfterWxAutoLogin, info=%v", info)
+			ip := self.Ctx.Input.IP()
+			wxUser = new(user_model.WxUser).Create("", unionId, info.Nickname,
+				info.City, info.Country, info.Province, ip, info.Sex, 0, 0, 0)
+
+			beego.BeeLogger.Info("PcWxMpLogin, upload WxUserHead with URL: %s", info.HeadImageURL)
+			if wxUser != nil {
+				go wxUser.UploadHead(info.HeadImageURL)
+			}
+			wxUserGzh := user_model.GetWxUserGzhByOpenId(mpOpenid, false)
+			if wxUserGzh == nil {
+				beego.BeeLogger.Info("wxUser: %v, info: %v")
+				wxUserGzh = new(user_model.WxUserGongzhonghao).Create(mpOpenid, a, wxUser.Id, info.Subscribe, info.SubscribeTime)
+			}
+
+			if wxUserGzh != nil && wxUserGzh.WxUserId != wxUser.Id {
+				wxUserGzh.WxUserId = wxUser.Id
+				wxUserGzh.Save()
+			}
+		}
+	} else {
+		if wxUser.Head == "" {
+			info := wx_mp.AuthUserInfo(token, mpOpenid, a, s)
+			if info != nil {
+				go wxUser.UploadHead(info.HeadImageURL)
+			}
+		}
+		wxUserGzh := user_model.GetWxUserGzhByOpenId(mpOpenid, false)
+		if wxUserGzh == nil {
+			info := wx_mp.AuthUserInfo(token, mpOpenid, a, s)
+			if info != nil {
+				wxUserGzh = new(user_model.WxUserGongzhonghao).Create(mpOpenid, a, wxUser.Id, info.Subscribe, info.SubscribeTime)
+			}
+		}
+
+		if wxUserGzh != nil && wxUserGzh.WxUserId != wxUser.Id {
+			wxUserGzh.WxUserId = wxUser.Id
+			wxUserGzh.Save()
+		}
+	}
+
+	if wxUser != nil {
+		self.SetSession(apps.SessionWxUserKey, wxUser.Id)
+	}
+
+	// 如果微信用户已绑定手机,则找出userId,并且赋值给session[userId]
+	if wxUser != nil && wxUser.UserId > 0 {
+		self.SetSession(apps.SessionUserKey, wxUser.UserId)
+	}
+
+	if cbUrl, ok := cache.Cache.Get(state).(string); ok {
+		if cbUrl == "" {
+			self.Redirect(beego.AppConfig.String("PcHost"), 302)
+			return
+		}
+		if !strings.HasPrefix(cbUrl, "http://") && !strings.HasPrefix(cbUrl, "https://") {
+			cbUrl = fmt.Sprintf("%s%s", beego.AppConfig.String("PcHost"), cbUrl)
+		}
+		self.Redirect(cbUrl, 302)
+		return
+	} else {
+		self.Redirect(beego.AppConfig.String("PcHost"), 302)
+		return
+	}
+	self.ReturnError(403, apps.NetworkBusy, "", nil)
+}
+
+// 退出登录
+func (self *PermitController) Logout() {
+	self.DelSession(apps.SessionUserKey)
+	self.DelSession(apps.SessionWxUserKey)
+	self.ReturnError(200, apps.HasLogout, "", nil)
+}
+
+// // 微信开放平台登录
+// func (self *PermitController) WxOpenLogin() {
+// 	cb := self.GetString("cb")
+// 	u := strings.Split(uuid.NewV4().String(), "-")[0]
+// 	state := fmt.Sprintf("loginCb[%s]", u)
+// 	cache.Cache.Put(state, cb, 60*time.Second)
+// 	appId := beego.AppConfig.String("WxOpenAppId")
+// 	redirectURI := fmt.Sprintf("%s/login/open/after",
+// 		beego.AppConfig.String("ApiHost"))
+// 	url := wx_open.AuthCodeURL(appId, redirectURI, "snsapi_login", state)
+// 	self.Redirect(url, 302)
+// 	return
+// }
+
+// //微信开放平台登录
+// func (self *PermitController) AfterWxOpenLogin() {
+// 	scope := "snsapi_login"
+// 	code := self.GetString("code")
+// 	state := self.GetString("state")
+// 	a := beego.AppConfig.String("WxOpenAppId")
+// 	s := beego.AppConfig.String("WxOpenAppSecret")
+// 	redirectURI := fmt.Sprintf("%s/after_wx_open_login",
+// 		beego.AppConfig.String("ApiHost"))
+// 	token, err := wx_open.AuthExchangeToken(code, a, s, redirectURI, scope)
+// 	if err != nil {
+// 		self.ReturnError(403, apps.NetworkBusy, "", nil)
+// 	}
+// 	openOpenid := token.OpenId
+// 	unionId := token.UnionId
+// 	// 这里不能取缓存数据,因为UserId这个字段在外部有可能已被赋值
+// 	wxUser := user_model.GetWxUserByUnionid(unionId, false)
+// 	if wxUser == nil {
+// 		info, err := wx_open.AuthUserInfo(token.AccessToken, redirectURI, scope, openOpenid, a, s)
+// 		if err == nil {
+// 			ip := self.Ctx.Input.IP()
+// 			wxUser = new(user_model.WxUser).Create("", openOpenid, token.UnionId, info.Nickname,
+// 				info.City, info.Country, info.Province, ip, int64(info.Sex), 1, time.Now().Unix())
+// 			//上传头像至alioss
+// 			go wxUser.UploadHead(info.HeadImageURL)
+// 		}
+// 	} else {
+// 		if wxUser.OpenOpenid != openOpenid {
+// 			wxUser.OpenOpenid = openOpenid
+// 			go wxUser.UpdateField("OpenOpenid")
+// 		}
+// 	}
+// 	self.SetSession(apps.SessionWxUserKey, wxUser.Id)
+// 	// 如果微信用户已绑定手机,则找出userId,并且赋值给session[userId]
+// 	if wxUser.UserId > 0 {
+// 		self.SetSession(apps.SessionUserKey, wxUser.UserId)
+// 	}
+// 	if cbUrl, ok := cache.Cache.Get(state).(string); ok {
+// 		if cbUrl == "" {
+// 			if wxUser.UserId <= 0 {
+// 				self.Redirect(fmt.Sprintf("%s/user/binding/state", beego.AppConfig.String("WWWHost")), 302)
+// 				return
+
+// 			}
+// 			self.Redirect(beego.AppConfig.String("WWWHost"), 302)
+// 			return
+// 		}
+// 		self.Redirect(cbUrl, 302)
+// 		return
+// 	} else {
+// 		self.Redirect(beego.AppConfig.String("WWWHost"), 302)
+// 		return
+// 	}
+// 	self.ReturnError(403, apps.NetworkBusy, "", nil)
+// }
+
+// //PC端手机号码登录
+// func (self *PermitController) TelLogin() {
+// 	//防止用户先登录微信user,未绑定手机,同时又使用tel登录,默认清除wxuser的session
+// 	self.DelSession(apps.SessionWxUserKey)
+
+// 	tel := self.GetString("tel")
+// 	pwd := self.GetString("pwd")
+
+// 	user := user_model.GetByTel(tel, false)
+// 	signUpURL := fmt.Sprintf("%s/v1/signup", beego.AppConfig.String("ApiHost"))
+// 	if user == nil {
+// 		self.ReturnError(403, apps.UserNotExist, signUpURL, nil)
+// 	}
+// 	md5Ctx := md5.New()
+// 	md5Ctx.Write([]byte(pwd))
+// 	cipherStr := md5Ctx.Sum(nil)
+// 	md5Pwd := hex.EncodeToString(cipherStr)
+
+// 	if user.Pwd != md5Pwd {
+// 		self.ReturnError(403, apps.LoginPasswordError, "", nil)
+// 	}
+// 	self.SetSession(apps.SessionUserKey, user.Id)
+// 	// 找出微信用户
+// 	wxUser := user_model.GetWxUserByUserId(user.Id, false)
+// 	if wxUser != nil {
+// 		self.SetSession(apps.SessionWxUserKey, wxUser.Id)
+// 	}
+// 	// self.Data["json"] = "ok"
+// 	self.ServeJSON()
+// }
+
+// //使用key登录,key通过GenerateLoginKey生成
+// func (self *PermitController) KeyLogin() {
+// 	//不支持微信端
+// 	if self.IsWxClient() {
+// 		self.ReturnError(403, apps.NoExist, "", nil)
+// 	}
+// 	key := self.Ctx.Input.Param(":key")
+// 	uId, _ := self.GetInt64("user_id")
+
+// 	if key == "" || uId == 0 {
+// 		self.ReturnError(403, apps.ParamsRequired, "", nil)
+// 	}
+
+// 	k := cache.GetKey(cache.WapAutoLoginKey, uId)
+// 	//校验key
+// 	if s, ok := cache.Cache.Get(k).(string); ok {
+// 		// beego.BeeLogger.Warn("11111111, k:%s, s: %s, key: %s, uId: %d", k, s, key, uId)
+// 		if s == key {
+// 			// self.DelSession(apps.SessionUserKey)
+// 			self.SetSession(apps.SessionUserKey, uId)
+// 			//销毁缓存
+// 			cache.Cache.Delete(k)
+// 		} else {
+// 			self.ReturnError(403, apps.NoExist, "", nil)
+// 		}
+// 	} else {
+// 		self.ReturnError(403, apps.NoExist, "", nil)
+// 	}
+
+// 	// self.Data["json"] = "ok"
+// 	self.ServeJSON()
+// }
+
+// //PC端注册
+// func (self *PermitController) SignUp() {
+// 	code := self.GetString("code")
+// 	tel := self.GetString("tel")
+// 	k := fmt.Sprintf("%s%s", sms_model.SIGN_UP, tel)
+// 	if cacheCode, ok := cache.Cache.Get(k).(string); ok {
+// 		if code != cacheCode {
+// 			self.ReturnError(403, apps.TelCodesError, "", nil)
+// 		}
+// 	} else {
+// 		//验证码过期
+// 		self.ReturnError(403, apps.TelCodesExpired, "", nil)
+// 	}
+
+// 	user := user_model.GetByTel(tel, false)
+// 	if user == nil {
+// 		loginPwd := tool.Get8Uuid()
+// 		md5Ctx := md5.New()
+// 		md5Ctx.Write([]byte(loginPwd))
+// 		cipherStr := md5Ctx.Sum(nil)
+// 		md5Pwd := hex.EncodeToString(cipherStr)
+// 		ip := self.Ctx.Input.IP()
+// 		user = user_model.Create(tel, md5Pwd, ip)
+// 		// 注册渠道处理
+// 		cId, _ := strconv.ParseInt(self.Ctx.GetCookie("sign_up_channel"), 10, 64)
+// 		channel := user_model.GetSignUpChannelById(cId, true)
+// 		if channel != nil {
+// 			user.SignupChannelId = cId
+// 			user.Save()
+// 			// wpsvip注册的用户,因为真功夫项目的,需要通知赠送稻米
+// 			if cId == 8 {
+// 				key1 := beego.AppConfig.String("CookieWpsVipUId")
+// 				key2 := beego.AppConfig.String("CookieWpsVipExtra")
+// 				wpsUserId, _ := strconv.ParseInt(self.Ctx.GetCookie(key1), 10, 64)
+// 				extra := self.Ctx.GetCookie(key2)
+// 				go wps.Reward(wpsUserId, extra, wps.OT_ZGF_ZHUC)
+// 				go wps_user_model.CreateWpsUser(wpsUserId, user.Id, 0, extra)
+// 			}
+// 		}
+// 		sign, template, action := sms_model.GetAliMsgContent(sms_model.LOGIN_PWD)
+// 		go sms_model.SendSmsWithAli([]string{tel}, sign, template, action, loginPwd)
+// 	} else {
+// 		self.ReturnError(403, apps.PhoneExist, "", nil)
+// 	}
+
+// 	self.SetSession(apps.SessionUserKey, user.Id)
+// 	//如果是体验金专题页面点击过来的,也送
+// 	key := beego.AppConfig.String("TYJName")
+// 	c := self.Ctx.GetCookie(key)
+
+// 	if c != "" {
+// 		id, err := strconv.ParseInt(c, 10, 64)
+// 		if err == nil {
+// 			trialInfo := trial_coin_model.GetTrialCoinById(id, true)
+// 			if trialInfo != nil && trialInfo.Deadline.Unix() >= time.Now().Unix() {
+// 				new(trial_coin_model.TrialCoinOrder).Create(user.Id, trialInfo.Id, trialInfo.Amount)
+// 			}
+// 		}
+// 	}
+
+// 	self.Data["json"] = user_model.User{Tel: tel}
+// 	self.ServeJSON()
+// }
+
+// //忘记密码
+// func (self *PermitController) ResetPwd() {
+// 	code := self.GetString("code")
+// 	tel := self.GetString("tel")
+// 	pwd := self.GetString("pwd")
+// 	confirmedPwd := self.GetString("confirmed_pwd")
+// 	if pwd != confirmedPwd {
+// 		self.ReturnError(403, apps.PasswordError, "", nil)
+// 	}
+
+// 	lengthPwd := len(pwd)
+// 	if lengthPwd < 6 || lengthPwd > 20 {
+// 		self.ReturnError(403, apps.PasswordLengthError, "", nil)
+// 	}
+
+// 	k := fmt.Sprintf("%s%s", sms_model.RESET_PWD, tel)
+// 	if cacheCode, ok := cache.Cache.Get(k).(string); ok {
+// 		if code != cacheCode {
+// 			self.ReturnError(403, apps.TelCodesError, "", nil)
+// 		}
+// 	} else {
+// 		//验证码过期
+// 		self.ReturnError(403, apps.TelCodesExpired, "", nil)
+// 	}
+// 	md5Ctx := md5.New()
+// 	md5Ctx.Write([]byte(pwd))
+// 	cipherStr := md5Ctx.Sum(nil)
+// 	md5Pwd := hex.EncodeToString(cipherStr)
+
+// 	user := user_model.GetByTel(tel, false)
+// 	if user == nil {
+// 		self.ReturnError(403, apps.UserNotExist, "", nil)
+// 	} else {
+// 		user.Pwd = md5Pwd
+// 		user.Save()
+// 	}
+
+// 	self.Data["json"] = user_model.User{Tel: tel}
+// 	self.ServeJSON()
+// }
+
+// func createXkUser(uid, openid string) {
+// 	if uid == "" || openid == "" {
+// 		return
+// 	}
+// 	url := fmt.Sprintf("http://api.xikego.com/v1/createwxuser/superd5c/%s/%s", uid, openid)
+// 	tool.PostJSON(url, nil)
+// 	return
+// }

+ 180 - 0
go/gopath/src/fohow.com/apps/controllers/poster_controller/poster_controller.go

@@ -0,0 +1,180 @@
+package poster_controller
+
+import (
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/models/poster_model"
+	"fohow.com/libs/ali_oss"
+	"fohow.com/libs/wx_mp"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"io/ioutil"
+	"strconv"
+	"strings"
+	"time"
+)
+
+var (
+	//不需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"GetPosterXcxQrcode", "ScanPosterXcxQrcode"}
+	exceptCheckWxUserLoginAction = []string{"ScanPosterXcxQrcode"}
+)
+
+type PosterController struct {
+	apps.BaseController
+}
+
+func GetCdnFullImgUrl(img string) string {
+	if img == "" {
+		return ""
+	}
+	if strings.HasPrefix(img, "http://") || strings.HasPrefix(img, "https://") {
+		return img
+	} else {
+		return fmt.Sprintf("%s/%s", beego.AppConfig.String("AliCDNImgHost"), img)
+	}
+}
+
+var DEFAULT_HEAD string = "xcx/poster/default_head.png"
+
+func (self *PosterController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+func (self *PosterController) GetPosterXcxQrcode() {
+
+	_type := self.GetString("type", "poster")
+
+	_id := self.Ctx.Input.Param(":tid")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+
+	wxUid := self.GetCurrentWxUserId()
+
+	posterQrcodeRecord := poster_model.GetPosterQrcodeRecordByWIdAndTypeAndRId(wxUid, id, _type, useCache)
+
+	var qrcodeUrl string
+	var wxHead string
+
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser != nil {
+		wxHead = GetCdnFullImgUrl(wxUser.Head)
+	}
+
+	if posterQrcodeRecord == nil {
+		homeUrl := fmt.Sprintf("pages/start/start")
+		width, _ := self.GetInt("width", 430)
+		scene := fmt.Sprintf("%s$%d$%d", _type, id, wxUid)
+		qrcodeData := wx_mp.GenerateQrcode(wx_mp.QR_SCENE, homeUrl, scene, width)
+		//if len(qrcodeData) < 400 {
+		//self.ReturnError(403, apps.Error, "", nil)
+		//}
+		filename := fmt.Sprintf("p_%d.jpg", time.Now().Unix())
+		localPath := fmt.Sprintf("%s/%s", beego.AppConfig.String("QrcodePath"), filename)
+		err := ioutil.WriteFile(localPath, qrcodeData, 0644)
+
+		if err != nil {
+			beego.BeeLogger.Error(err.Error())
+		}
+
+		uploadPath := fmt.Sprintf("qrcode_path/poster/%s", filename)
+		//上传到阿里云原目录下面
+		err = ali_oss.PutObjectFromFile(beego.AppConfig.String("AliOssEndPoint"),
+			beego.AppConfig.String("AliOssAccessId"),
+			beego.AppConfig.String("AliOssAccessSecret"),
+			beego.AppConfig.String("AliOssBucket"), "", uploadPath, localPath)
+
+		if err != nil {
+			beego.BeeLogger.Error("Upload Pngs err: %s", err)
+		} else {
+			qrcodeUrl = GetCdnFullImgUrl(uploadPath)
+		}
+
+		new(poster_model.PosterQrcodeRecord).Create(wxUid, id, id, _type, uploadPath)
+
+	} else {
+		qrcodeUrl = GetCdnFullImgUrl(posterQrcodeRecord.QrcodeUrl)
+	}
+
+	type Ret struct {
+		QrcodeUrl string `json:"qrcode_url"`
+		WxHead    string `json:"wx_head"`
+	}
+
+	self.Data["json"] = &Ret{QrcodeUrl: qrcodeUrl, WxHead: wxHead}
+	self.ServeJSON()
+}
+
+func (self *PosterController) TestGeneratePoster() {
+
+	homeUrl := fmt.Sprintf("pages/start/start")
+
+	width, _ := self.GetInt("width", 430)
+
+	scene := fmt.Sprintf("%s$%d$%d", "project", 510, 172344)
+	qrcodeData := wx_mp.GenerateQrcode(wx_mp.QR_SCENE, homeUrl, scene, width)
+
+	beego.BeeLogger.Error(scene)
+
+	filename := fmt.Sprintf("p_%d.jpg", time.Now().Unix())
+	localPath := fmt.Sprintf("%s/%s", beego.AppConfig.String("QrcodePath"), filename)
+	err := ioutil.WriteFile(localPath, qrcodeData, 0644)
+
+	if err != nil {
+		beego.BeeLogger.Error(err.Error())
+	}
+
+	uploadPath := fmt.Sprintf("qrcode_path/poster/%s", filename)
+	//上传到阿里云原目录下面
+	err = ali_oss.PutObjectFromFile(beego.AppConfig.String("AliOssEndPoint"),
+		beego.AppConfig.String("AliOssAccessId"),
+		beego.AppConfig.String("AliOssAccessSecret"),
+		beego.AppConfig.String("AliOssBucket"), "", uploadPath, localPath)
+
+	beego.BeeLogger.Error("%s", err)
+
+	//fmt.Println(qrcodeData)
+
+	self.Data["json"] = uploadPath
+	self.ServeJSON()
+}
+
+func (self *PosterController) ScanPosterXcxQrcode() {
+
+	params := self.GetString("params")
+	paramArray := strings.Split(params, "$")
+	if len(paramArray) != 3 {
+		self.ServeJSON()
+		return
+	}
+	beego.BeeLogger.Info("poster_controller.ScanPosterXcxQrcode().params(%s)", params)
+	_type := paramArray[0]
+	_id := paramArray[1]
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	_wxUId := paramArray[2]
+	inviteWxUid, _ := strconv.ParseInt(_wxUId, 10, 64)
+
+	posterQrcodeRecord := poster_model.GetPosterQrcodeRecordByWIdAndTypeAndRId(inviteWxUid, id, _type, false)
+
+	if posterQrcodeRecord == nil {
+		self.ServeJSON()
+		return
+	}
+
+	posterQrcodeRecord.ScanTimes = posterQrcodeRecord.ScanTimes + 1
+	posterQrcodeRecord.Save()
+
+	wxUId := self.GetCurrentWxUserId()
+	scanRecord := poster_model.GetPosterQrcodeRecordByWxUId(posterQrcodeRecord.Id, wxUId)
+	if scanRecord == nil {
+		go new(poster_model.PosterQrcodeScanRecord).Create(posterQrcodeRecord.Id, wxUId, self.Ctx.Input.IP())
+	} else {
+		scanRecord.ScanTimes = scanRecord.ScanTimes + 1
+		scanRecord.ScanLastTime = time.Now()
+		scanRecord.Save()
+	}
+
+	self.ServeJSON()
+}

+ 32 - 0
go/gopath/src/fohow.com/apps/controllers/product_controller/init.go

@@ -0,0 +1,32 @@
+package product_controller
+
+import (
+	// "fmt"
+	// "os"
+	// "net/url"
+	// "strings"
+
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	// "github.com/go-wkhtmltoimage"
+	// "github.com/skip2/go-qrcode"
+
+	"fohow.com/apps"
+)
+
+var (
+	//需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"Latest", "Get", "Categories", "GetProductsByCat", "GetNeedShare"}
+	exceptCheckWxUserLoginAction = []string{"Latest", "Get", "Categories", "GetProductsByCat"}
+)
+
+type ProductController struct {
+	apps.BaseController
+}
+
+func (self *ProductController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	// beego.BeeLogger.Info("invote controller Init func")
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 231 - 0
go/gopath/src/fohow.com/apps/controllers/product_controller/product_controller.go

@@ -0,0 +1,231 @@
+package product_controller
+
+import (
+	// "fmt"
+	"strconv"
+	"strings"
+	// "time"
+
+	// "github.com/astaxie/beego"
+
+	"fohow.com/apps"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/product_model"
+	// "fohow.com/apps/models/user_model"
+	"fmt"
+	"fohow.com/apps/models/share_model"
+	"time"
+)
+
+//精选推介-首页
+func (self *ProductController) Latest() {
+	recommend, _ := self.GetInt64("rd", 0)
+	page, _ := self.GetInt64("page")
+	perPage, _ := self.GetInt64("per_page")
+	cache, _ := self.GetBool("cache", false)
+	if page <= 0 {
+		page = 1
+	}
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	type Ret struct {
+		List      []*product_model.Product `json:"list"`
+		ListCount int64                    `json:"list_count"`
+	}
+
+	pds := product_model.GetLatest(page, perPage, recommend, cache)
+	count := product_model.GetLatestCount(recommend, cache)
+	for _, pd := range pds {
+		pd.SoldCount = pd.SaleNums
+		if pd.Count > pd.SoldCount {
+			pd.LeftCount = pd.Count - pd.SoldCount
+		}
+		pd.SoldCount = pd.SoldCount + pd.VirtualSoldCount
+		if pd.SeckilShowPrice > 0 {
+			now := time.Now()
+			pd.SeckillStartAt = pd.SeckillStart.Unix()
+			pd.SeckillEndAt = pd.SeckillEnd.Unix()
+			if pd.SeckillStart.Unix() > now.Unix() {
+				pd.SeckillState = product_model.SECKILL_PREPARING_STATE
+				pd.SeckillStateCn = product_model.SECKILL_PREPARING_STATE_CN
+				pd.SoldCount = pd.SoldCount - pd.VirtualSoldCount //准备中不加虚拟量
+			} else if pd.SeckillStart.Unix() <= now.Unix() && now.Unix() < pd.SeckillEnd.Unix() {
+				pd.IsUnderSeckill = true
+				pd.SeckillState = product_model.SECKILL_UNDER_STATE
+				pd.SeckillStateCn = product_model.SECKILL_UNDER_STATE_CN
+			} else {
+				pd.SeckillState = product_model.SECKILL_END_STATE
+				pd.SeckillStateCn = product_model.SECKILL_END_STATE_CN
+			}
+		}
+		pd.DeliverStartTime = pd.DeliverStartAt.Unix()
+		pd.DeliverStopTime = pd.DeliverStopAt.Unix()
+	}
+
+	self.Data["json"] = &Ret{List: pds, ListCount: count}
+	self.ServeJSON()
+}
+
+//商品详情
+func (self *ProductController) Get() {
+	_id := self.Ctx.Input.Param(":id")
+	cache, _ := self.GetBool("cache", false)
+
+	pId, _ := strconv.ParseInt(_id, 10, 64)
+	pd := product_model.GetProductById(pId, cache)
+	if pd == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	pd.SoldCount = pd.SaleNums
+	if pd.Count > pd.SoldCount {
+		pd.LeftCount = pd.Count - pd.SoldCount
+	}
+	pd.SoldCount = pd.SoldCount + pd.VirtualSoldCount
+	pd.Cover = product_model.GetCoverByPId(pId, cache)
+	pd.Album = product_model.GetPicturesByPIdAndPType(pId, product_model.PIC_TYPE_ALBUM, cache)
+	pd.ShareImg = self.GetCdnFullImgUrl(pd.ShareImg)
+
+	if pd.SeckilShowPrice > 0 {
+		now := time.Now()
+		pd.SeckillStartAt = pd.SeckillStart.Unix()
+		pd.SeckillEndAt = pd.SeckillEnd.Unix()
+		if pd.SeckillStart.Unix() > now.Unix() {
+			pd.SeckillState = product_model.SECKILL_PREPARING_STATE
+			pd.SeckillStateCn = product_model.SECKILL_PREPARING_STATE_CN
+			pd.SoldCount = pd.SoldCount - pd.VirtualSoldCount //准备中不加虚拟量
+		} else if pd.SeckillStart.Unix() <= now.Unix() && now.Unix() < pd.SeckillEnd.Unix() {
+			pd.IsUnderSeckill = true
+			pd.SeckillState = product_model.SECKILL_UNDER_STATE
+			pd.SeckillStateCn = product_model.SECKILL_UNDER_STATE_CN
+		} else {
+			pd.SeckillState = product_model.SECKILL_END_STATE
+			pd.SeckillStateCn = product_model.SECKILL_END_STATE_CN
+		}
+	}
+
+	pd.DeliverStartTime = pd.DeliverStartAt.Unix()
+	pd.DeliverStopTime = pd.DeliverStopAt.Unix()
+	// ret.Product = pd
+	self.Data["json"] = pd
+	self.ServeJSON()
+}
+
+//商品分类
+func (self *ProductController) Categories() {
+	ancestry := self.GetString("ancestry")
+	var catRet []*product_model.ProductCat
+	cat := new(product_model.ProductCat)
+	cat.Id = 0
+	cat.Name = "全部"
+	catRet = append(catRet, cat)
+	cats := product_model.GetProductCatsByAncestry(strings.TrimSpace(ancestry))
+	for _, item := range cats {
+		catRet = append(catRet, item)
+	}
+	self.Data["json"] = catRet
+	self.ServeJSON()
+}
+
+//某个类别下的商品列表
+func (self *ProductController) GetProductsByCat() {
+	_id := self.Ctx.Input.Param(":cat_id")
+	catId, _ := strconv.ParseInt(_id, 10, 64)
+	page, _ := self.GetInt64("page")
+	perPage, _ := self.GetInt64("per_page")
+	cache, _ := self.GetBool("cache", false)
+	if page <= 0 {
+		page = 1
+	}
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	type Ret struct {
+		List      []*product_model.Product `json:"list"`
+		ListCount int64                    `json:"list_count"`
+	}
+	list := product_model.GetProductsByCatId(catId, page, perPage, cache)
+	count := product_model.GetProductCountByCatId(catId, cache)
+	for _, pd := range list {
+		pd.SoldCount = pd.SaleNums
+		if pd.Count > pd.SoldCount {
+			pd.LeftCount = pd.Count - pd.SoldCount
+		}
+		pd.SoldCount = pd.SoldCount + pd.VirtualSoldCount
+		if pd.SeckilShowPrice > 0 {
+			now := time.Now()
+			pd.SeckillStartAt = pd.SeckillStart.Unix()
+			pd.SeckillEndAt = pd.SeckillEnd.Unix()
+			if pd.SeckillStart.Unix() > now.Unix() {
+				pd.SeckillState = product_model.SECKILL_PREPARING_STATE
+				pd.SeckillStateCn = product_model.SECKILL_PREPARING_STATE_CN
+				pd.SoldCount = pd.SoldCount - pd.VirtualSoldCount //准备中不加虚拟量
+			} else if pd.SeckillStart.Unix() <= now.Unix() && now.Unix() < pd.SeckillEnd.Unix() {
+				pd.IsUnderSeckill = true
+				pd.SeckillState = product_model.SECKILL_UNDER_STATE
+				pd.SeckillStateCn = product_model.SECKILL_UNDER_STATE_CN
+			} else {
+				pd.SeckillState = product_model.SECKILL_END_STATE
+				pd.SeckillStateCn = product_model.SECKILL_END_STATE_CN
+			}
+		}
+
+		pd.DeliverStartTime = pd.DeliverStartAt.Unix()
+		pd.DeliverStopTime = pd.DeliverStopAt.Unix()
+	}
+	self.Data["json"] = &Ret{List: list, ListCount: count}
+	self.ServeJSON()
+}
+
+//商品是否需要转发
+func (self *ProductController) GetNeedShare() {
+	_id := self.Ctx.Input.Param(":id")
+	cache, _ := self.GetBool("cache", false)
+
+	pId, _ := strconv.ParseInt(_id, 10, 64)
+	pd := product_model.GetProductById(pId, cache)
+	if pd == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	wxUId := self.GetCurrentWxUserId()
+
+	type Ret struct {
+		NeedShare int64 `json:"need_share"`
+	}
+
+	needShare := int64(0)
+	//限新逻辑: 微信支付完成购买过商品的用户
+	if pd.IsOnlyNew {
+		paiedOrder := order_model.GetPaiedOrderByWxUIdAndPayWayLimitOne(wxUId, order_model.PAY_WAY_WEIXIN, false)
+		if paiedOrder != nil {
+			self.Data["json"] = &Ret{NeedShare: 0}
+			self.ServeJSON()
+			self.StopRun()
+		}
+	}
+
+	//秒杀逻辑: 判断是否处于秒杀时间段内+ 是否需要分享
+	if pd.SeckilShowPrice > 0 {
+		now := time.Now()
+		if now.Unix() < pd.SeckillStart.Unix() {
+			self.Data["json"] = &Ret{NeedShare: 0}
+			self.ServeJSON()
+			self.StopRun()
+		} else if now.Unix() > pd.SeckillEnd.Unix() {
+			self.Data["json"] = &Ret{NeedShare: 0}
+			self.ServeJSON()
+			self.StopRun()
+		}
+		//是否需要转发
+		proId := fmt.Sprintf("%d", pd.Id)
+		share := share_model.GetShareInfo(wxUId, proId, proId, share_model.SHARE_CODE_XCX_PRODUCT, false)
+		if share == nil {
+			needShare = int64(1)
+		}
+	}
+
+	self.Data["json"] = &Ret{NeedShare: needShare}
+	self.ServeJSON()
+}

+ 40 - 0
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/cache_controller.go

@@ -0,0 +1,40 @@
+package railsadmin_controller
+
+import (
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+	"fohow.com/cache"
+	"strconv"
+)
+
+var (
+	exceptCheckUserLoginAction   = []string{"*"}
+	exceptCheckWxUserLoginAction = []string{"*"}
+)
+
+type RailsadminController struct {
+	apps.BaseController
+}
+
+func (self *RailsadminController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	// beego.BeeLogger.Info("invote controller Init func")
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+//项目信息清除缓存
+func (self *RailsadminController) CleanCache() {
+	t := self.Ctx.Input.Param(":type")
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	beego.BeeLogger.Warn("t: %s, id: %d", t, id)
+
+	if t == "project" {
+		beego.BeeLogger.Warn("railsadmin after update clean cache: %d", id)
+		k := cache.GetKey(cache.GetProjectById, id)
+		cache.Cache.Delete(k)
+	}
+	self.ServeJSON()
+}

+ 175 - 0
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/generate_controller.go

@@ -0,0 +1,175 @@
+package railsadmin_controller
+
+import (
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/models/channel_gzh_qrcode_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/apps/models/wx_gongzhonghao_model"
+	"fohow.com/apps/models/wx_menu_model"
+	"fohow.com/libs/ali_oss"
+	"fohow.com/libs/wx_mp"
+	"github.com/astaxie/beego"
+	"github.com/chanxuehong/wechat/mp/menu"
+	"io/ioutil"
+	"strconv"
+	"strings"
+	"time"
+)
+
+const (
+	//根节点Action
+	MenuActionRoot = "root"
+)
+
+//创建公众号自定义菜单
+func (self *RailsadminController) CreateWxMenu() {
+	// result := self.CheckIsInvokeFromRailsAdmin(self.Ctx)
+
+	id, _ := self.GetInt64("gzhId")
+
+	//获取所有菜单项
+	menuList := wx_menu_model.GetWxMenusByGzhId(id)
+
+	var menuBtns []menu.Button
+
+	for _, menuItem := range menuList {
+		ancestry := strings.Split(menuItem.Ancestry, "/")
+		if len(ancestry) == 1 && menuItem.Action != MenuActionRoot {
+			subAncestry := menuItem.Ancestry + "/" + fmt.Sprintf("%d", menuItem.Id)
+			subMenuList := wx_menu_model.GetWxMenusByAncestry(subAncestry)
+			var subBtns []menu.Button
+			for _, subItem := range subMenuList {
+				subBtns = append(subBtns, menu.Button{
+					Type:     subItem.Action,
+					Name:     subItem.Name,
+					Key:      subItem.Key,
+					URL:      subItem.Content,
+					Appid:    subItem.Appid,
+					PagePath: subItem.PagePath,
+				})
+			}
+			menuBtns = append(menuBtns, menu.Button{
+				Name:       menuItem.Name,
+				Type:       menuItem.Action,
+				Key:        menuItem.Key,
+				URL:        menuItem.Content,
+				Appid:      menuItem.Appid,
+				PagePath:   menuItem.PagePath,
+				SubButtons: subBtns,
+			})
+		}
+	}
+
+	gzh := wx_gongzhonghao_model.GetGZHById(id, true)
+	// fmt.Println("menuButtons: ", menuBtns)
+	err := wx_mp.CreateWxMenu(gzh.AppId, gzh.AppSecret, menuBtns)
+
+	if err != nil {
+		beego.BeeLogger.Critical("Can't create wx menus", err)
+		self.ReturnError(403, apps.WxMenusCreatedFailed, "", nil)
+	}
+
+	self.ServeJSON()
+}
+
+// 生成二维码
+func (self *RailsadminController) CreateWxQrcode() {
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+	cq := channel_gzh_qrcode_model.GetById(id)
+	if cq == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	var expireSeconds int64
+	var actionName string
+	if cq.IsPermanent == 1 {
+		actionName = wx_mp.WX_QR_LIMIT_SCENE
+		expireSeconds = 1000
+	} else {
+		actionName = wx_mp.WX_QR_SCENE
+		expireSeconds = cq.ExpiredAt.Unix() - time.Now().Unix()
+	}
+	gzh := wx_gongzhonghao_model.GetGZHById(cq.WxGongzhonghaoId, true)
+	src, err := wx_mp.CreateQrcode(gzh.AppId, gzh.AppSecret,
+		cq.SceneId, actionName, expireSeconds)
+	if err == nil {
+		cq.QrcodeImg = src
+		cq.Save()
+	}
+	// self.Data["json"] = pcms
+	self.ServeJSON()
+}
+
+// 生成二维码
+func (self *RailsadminController) CreateWxQrcodeWithSceneString() {
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+	cq := channel_gzh_qrcode_model.GetById(id)
+	if cq == nil || cq.SceneStr == "" {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	var expireSeconds int64
+	var actionName string
+	if cq.IsPermanent == 1 {
+		actionName = wx_mp.WX_QR_LIMIT_SCENE
+		expireSeconds = 1000
+	} else {
+		actionName = wx_mp.WX_QR_SCENE
+		expireSeconds = cq.ExpiredAt.Unix() - time.Now().Unix()
+	}
+	gzh := wx_gongzhonghao_model.GetGZHById(cq.WxGongzhonghaoId, true)
+	src, err := wx_mp.CreateQrcodeWithSceneString(gzh.AppId, gzh.AppSecret,
+		cq.SceneStr, actionName, expireSeconds)
+	if err == nil {
+		cq.QrcodeImg = src
+		cq.Save()
+	}
+	// self.Data["json"] = pcms
+	self.ServeJSON()
+}
+
+//生成小程序二维码
+func (self *RailsadminController) GenerateXcxQrcode() {
+	width, _ := self.GetInt("width", 430)
+	//wxUser := self.GetCurrentWxUser(false)
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+
+	wxUser := user_model.GetWxUserById(id, false)
+	if wxUser == nil {
+		self.ReturnError(403, apps.UserNotExist, "", nil)
+	}
+
+	homeUrl := fmt.Sprintf("pages/start/start")
+	type Ret struct {
+		QrcodeUrl string `json:"qrcode_url"`
+	}
+	qrcodeUrl := ""
+	if wxUser.InviteQrcodeUrl != "" {
+		qrcodeUrl = self.GetFullImgUrl(wxUser.InviteQrcodeUrl)
+	} else {
+		scene := fmt.Sprintf("invite_wx_%d", wxUser.Id)
+		qrcodeData := wx_mp.GenerateQrcode(wx_mp.QR_SCENE, homeUrl, scene, width)
+		beego.BeeLogger.Warn("invite_controller.GenerateQrcode() data_array: %d", qrcodeData)
+		filename := fmt.Sprintf("invite_qrcode_%d.jpg", wxUser.Id)
+		localPath := fmt.Sprintf("%s/%s", beego.AppConfig.String("InviteQrcodePath"), filename)
+		err := ioutil.WriteFile(localPath, qrcodeData, 0644)
+		if err != nil {
+			beego.BeeLogger.Error(err.Error())
+		}
+		uploadPath := fmt.Sprintf("qrcode_path/invite/%s", filename)
+		//上传到阿里云原目录下面
+		err = ali_oss.PutObjectFromFile(beego.AppConfig.String("AliOssEndPoint"),
+			beego.AppConfig.String("AliOssAccessId"),
+			beego.AppConfig.String("AliOssAccessSecret"),
+			beego.AppConfig.String("AliOssBucket"), "", uploadPath, localPath)
+		if err != nil {
+			beego.BeeLogger.Error("Upload Pngs err: %s", err)
+		} else {
+			wxUser.InviteQrcodeUrl = uploadPath
+			wxUser.Save()
+			qrcodeUrl = self.GetFullImgUrl(uploadPath)
+		}
+	}
+	self.Data["json"] = &Ret{QrcodeUrl: qrcodeUrl}
+	self.ServeJSON()
+}

+ 32 - 0
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/intro_user_controller.go

@@ -0,0 +1,32 @@
+package railsadmin_controller
+
+import (
+	"fohow.com/apps"
+	"fohow.com/apps/models/user_model"
+	"github.com/astaxie/beego"
+	"strconv"
+)
+
+//升级群主
+func (self *RailsadminController) UpdateIntroUser() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	beego.BeeLogger.Warn("UpdateIntroUser id:(%d)", id)
+
+	wxUser := user_model.GetWxUserById(id, false)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	if wxUser.ShowInviteMode == int64(1) {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	//升级群主,打开推广模块
+	wxUser.ShowInviteMode = int64(1)
+	wxUser.Save()
+
+	//更改下级会员群主ID
+	inviteList := user_model.GetWxUsersByInviteIdAll(wxUser.Id, false)
+	go user_model.UpdateClass(inviteList, wxUser.Id, wxUser.IntroUserId)
+	self.ServeJSON()
+}

+ 54 - 0
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/order_dispatch_controller.go

@@ -0,0 +1,54 @@
+package railsadmin_controller
+
+import (
+	"github.com/astaxie/beego"
+	"fohow.com/apps"
+	"fohow.com/apps/models/order_model"
+	"strconv"
+	"sync"
+	"time"
+)
+
+var updateExpressLock sync.Mutex
+
+//订单发货
+func (self *RailsadminController) OrderDispatch() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	beego.BeeLogger.Warn("OrderDispatch id:(%d)", id)
+
+	status := "dispatch"
+
+	defer updateExpressLock.Unlock()
+	updateExpressLock.Lock()
+	o := order_model.GetOrderByIntId(id)
+	if o == nil {
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	}
+	//只能处理待发货、待收货的订单物流信息。
+	if o.Status != order_model.STATUS_PROCESSING && o.Status != order_model.STATUS_DISPATCH {
+		self.ReturnError(403, apps.OrderStatusNotSuit, "", nil)
+	}
+
+	//避免重复发货
+	if o.Status == order_model.STATUS_DISPATCH && status == order_model.STATUS_DISPATCH {
+		self.ReturnError(403, apps.OrderAlreadyDispatch, "", nil)
+	}
+
+	// 校验状态参数,是否合法。
+	if status != "" && status != order_model.STATUS_DISPATCH {
+		// 只支持发货操作
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	} else if status == order_model.STATUS_DISPATCH {
+		o.Status = order_model.STATUS_DISPATCH
+		o.DispatchTime = time.Now()
+	} else {
+		o.Status = o.Status
+	}
+	if !o.Save() {
+		beego.BeeLogger.Error("user[%d]", id)
+	}
+
+	self.ServeJSON()
+}

+ 28 - 0
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/test_controller.go

@@ -0,0 +1,28 @@
+package railsadmin_controller
+
+import (
+	"fmt"
+
+	// "github.com/astaxie/beego"
+	// "fohow.com/apps"
+	// "fohow.com/apps/models/push_tmpl_model"
+	// "fohow.com/apps/models/subject_model"
+	// "fohow.com/apps/models/user_model"
+	// "fohow.com/libs/tool"
+	"fohow.com/libs/wx_mp"
+	// "strconv"
+	// "strings"
+	"time"
+)
+
+//销售专题开奖发送消息
+func (self *RailsadminController) TestEpPay() {
+	openid := self.GetString("openid")
+	name := self.GetString("name")
+	tradeNo := fmt.Sprintf("TX%d", time.Now().Unix())
+	desc := "测试提现"
+	// wxUser :=
+	ret := wx_mp.Transfers(openid, 100, tradeNo, wx_mp.PAY_FORCE_CHECK, name, desc)
+	self.Data["json"] = ret
+	self.ServeJSON()
+}

+ 13 - 0
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/test_test.go

@@ -0,0 +1,13 @@
+package railsadmin_controller
+
+import (
+	"fmt"
+	"testing"
+	"time"
+)
+
+func TestMain(t *testing.T) {
+	paiedAt, err := time.Parse("2006-01-02 15:04:05", "2018-10-20 12:52:14")
+	fmt.Println("*********%v, err:%v", paiedAt, err)
+	fmt.Println("12312312")
+}

+ 209 - 0
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/tmpl_controller.go

@@ -0,0 +1,209 @@
+package railsadmin_controller
+
+import (
+	"fmt"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/push_tmpl_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/wx_mp"
+	"github.com/astaxie/beego"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 推送模板消息给指定的用户
+func (self *RailsadminController) SendTemplate() {
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+	item := push_tmpl_model.GetPushTmplById(id)
+	if item == nil {
+		self.ServeJSON()
+		return
+	}
+	item.Times = item.Times + 1
+	item.LastUpdatedAt = time.Now()
+	go item.Save()
+	s := item.MsgType                         //模板:0 订单待支付提醒 1新项目通知 2提货通知
+	wxGongzhonghaoId := item.WxGongzhonghaoId //微信公众号ID,1:FOHOW玖玖
+
+	DEFAULTGONGZHONGHAO := int64(1)
+	if beego.BConfig.RunMode == beego.DEV {
+		DEFAULTGONGZHONGHAO = 2
+	}
+	if wxGongzhonghaoId == DEFAULTGONGZHONGHAO {
+		// (openId, url, title, task, nType, nTime, remark string)
+		go sendTmplmsgForHandle(item, s)
+	}
+	self.ServeJSON()
+}
+
+func sendTmplmsgForHandle(item *push_tmpl_model.PushTmpl, msgType string) {
+
+	users := getWxUserGzhsBySql(item.UserId)
+
+	url := item.Url
+	first := fmt.Sprintf("%s\n", item.First)
+	kw1 := item.Keyword1
+	kw2 := item.Keyword2
+	kw3 := item.Keyword3
+	//kw4 := item.Keyword4
+	//kw5 := item.Keyword5
+	remark := fmt.Sprintf("\n%s", item.Remark)
+
+	for _, u := range users {
+		if item.PushForce == 1 {
+			beego.BeeLogger.Warn("wxUserGongzhonghaos.GzhOpenId=%s", u.GzhOpenId)
+
+			if msgType == push_tmpl_model.MsgType_Unpay {
+				wx_mp.TmplmsgEventUnpayHandle(u.GzhOpenId, url, first, kw1, kw2, remark)
+			} else if msgType == push_tmpl_model.MsgType_NewProject {
+				wx_mp.TmplmsgEventNewProjectNotifyHandle(u.GzhOpenId, url, first, kw1, kw2, kw3, remark)
+			} else if msgType == push_tmpl_model.MsgType_PickUp {
+				kw4 := item.Keyword4
+				kw5 := item.Keyword5
+				wx_mp.TmplmsgEventPickUpNotifyHandle(u.GzhOpenId, url, first, kw1, kw2, kw3, kw4, kw5, remark)
+			} else if msgType == push_tmpl_model.MsgType_ApplyForSale {
+				wx_mp.Tmplmsg2ApplyForSaleNotifyHandle(u.GzhOpenId, url, first, kw1, kw2, kw3, remark)
+			} else if msgType == push_tmpl_model.MsgType_DollarRefund {
+				go wx_mp.TmplmsgEventDollarRefundNotifyHandle(u.GzhOpenId, url, first, kw1, kw2, remark)
+			}
+
+		} else {
+			r := push_tmpl_model.GetPushTmplRecordsByWxUIdAndTime(u.Id, time.Now())
+			if len(r) < push_tmpl_model.PUSH_TMPL_DAY_LIMIT {
+				beego.BeeLogger.Warn("wxUserGongzhonghaos.GzhOpenId=%s", u.GzhOpenId)
+
+				if msgType == push_tmpl_model.MsgType_Unpay {
+					wx_mp.TmplmsgEventUnpayHandle(u.GzhOpenId, url, first, kw1, kw2, remark)
+				} else if msgType == push_tmpl_model.MsgType_NewProject {
+					wx_mp.TmplmsgEventNewProjectNotifyHandle(u.GzhOpenId, url, first, kw1, kw2, kw3, remark)
+				} else if msgType == push_tmpl_model.MsgType_PickUp {
+					kw4 := item.Keyword4
+					kw5 := item.Keyword5
+					wx_mp.TmplmsgEventPickUpNotifyHandle(u.GzhOpenId, url, first, kw1, kw2, kw3, kw4, kw5, remark)
+				} else if msgType == push_tmpl_model.MsgType_ApplyForSale {
+					wx_mp.Tmplmsg2ApplyForSaleNotifyHandle(u.GzhOpenId, url, first, kw1, kw2, kw3, remark)
+				} else if msgType == push_tmpl_model.MsgType_DollarRefund {
+					go wx_mp.TmplmsgEventDollarRefundNotifyHandle(u.GzhOpenId, url, first, kw1, kw2, remark)
+				}
+
+				new(push_tmpl_model.PushTmplRecord).Create(u.Id, item.Id, time.Now())
+			} else {
+				beego.BeeLogger.Warn("wxUserGongzhonghaos.GzhOpenId=%s reach day limit times:%d, not push", u.GzhOpenId, push_tmpl_model.PUSH_TMPL_DAY_LIMIT)
+			}
+		}
+	}
+}
+
+func getWxUserGzhsBySql(key string) (list []*user_model.WxUserGongzhonghao) {
+	// var openIds []string
+	if key == "all" {
+		sql := "select * from wx_user_gongzhonghaos where subscribe=1;"
+		list = user_model.GetWxUserGzhsBySql(sql)
+	} else if key == "allUser" {
+		sql := "select wx_gzh.* from wx_user_gongzhonghaos wx_gzh, wx_users wxu where wx_gzh.wx_user_id = wxu.id and wxu.user_id > 0 and wx_gzh.subscribe=1;"
+		list = user_model.GetWxUserGzhsBySql(sql)
+	} else if strings.HasPrefix(key, "sql=") {
+		sql := strings.Split(key, "sql=")[1]
+		list = user_model.GetWxUserGzhsBySql(sql)
+		// users := user_model.GetWxUsersBySql(sql)
+		// for _, u := range users {
+		// 	openIds = append(openIds, u.MpOpenid)
+		// }
+	} else {
+		wxUserIds := strings.Split(key, ",")
+		for i := 0; i < len(wxUserIds); i++ {
+			_id := strings.Trim(wxUserIds[i], " ")
+			id, _ := strconv.ParseInt(_id, 10, 64)
+			wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(id, beego.AppConfig.String("WxMPAppId"), false)
+			if wxUserGzh != nil {
+				list = append(list, wxUserGzh)
+			}
+		}
+	}
+	return list
+}
+
+// 推送服务通知给指定的用户
+func (self *RailsadminController) SendXcxTemplate() {
+
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+	item := push_tmpl_model.GetXcxPushTmplById(id)
+	if item == nil {
+		self.ServeJSON()
+		return
+	}
+	item.Times = item.Times + 1
+	item.LastUpdatedAt = time.Now()
+	go item.Save()
+	s := item.MsgType
+	//模板:MessageTemplateId_ItemStatusRemind 物品状态提醒; MessageTemplateId_ProjectNewStateNofity  众筹项目最新状态通知
+
+	go sendXcxTmplmsgForHandle(item, s)
+
+	self.ServeJSON()
+}
+
+func sendXcxTmplmsgForHandle(item *push_tmpl_model.XcxPushTmpl, msgType string) {
+	users := getWxUsersBySql(item.UserId)
+	for _, user := range users {
+		if item.PushForce == 1 {
+			beego.BeeLogger.Warn("wxUsers.Xcx_OpenId=%s", user.Openid)
+
+			if msgType == push_tmpl_model.MSG_TYPE_SELF_USE_NOTIFY {
+
+				helpers.GranaryProductDealNotify(*user, item.Keyword1, item.Keyword2, item.Keyword3, item.Keyword4, item.EmphasisKw, item.Page)
+			} else if msgType == push_tmpl_model.MSG_TYPE_PROJECT_NEW_STATE_NOTIFY {
+
+				helpers.ProjectNewStateNotify(*user, item.Keyword1, item.Keyword2, item.Keyword3, item.EmphasisKw, item.Page)
+			} else if msgType == push_tmpl_model.MSG_TYPE_DELIVERY_NOTIFY {
+				helpers.DeliveryNotify(*user, item.Keyword1, item.Keyword2, item.Keyword3, item.EmphasisKw, item.Page)
+			}
+
+		} else {
+			r := push_tmpl_model.GetXcxPushTmplRecordsByWxUIdAndTime(user.Id, time.Now())
+			if len(r) < push_tmpl_model.XCX_PUSH_TMPL_DAY_LIMIT {
+				beego.BeeLogger.Warn("wxUsers.Xcx_OpenId=%s", user.Openid)
+
+				if msgType == push_tmpl_model.MSG_TYPE_SELF_USE_NOTIFY {
+
+					helpers.GranaryProductDealNotify(*user, item.Keyword1, item.Keyword2, item.Keyword3, item.Keyword4, item.EmphasisKw, item.Page)
+				} else if msgType == push_tmpl_model.MSG_TYPE_PROJECT_NEW_STATE_NOTIFY {
+
+					helpers.ProjectNewStateNotify(*user, item.Keyword1, item.Keyword2, item.Keyword3, item.EmphasisKw, item.Page)
+				} else if msgType == push_tmpl_model.MSG_TYPE_DELIVERY_NOTIFY {
+					helpers.DeliveryNotify(*user, item.Keyword1, item.Keyword2, item.Keyword3, item.EmphasisKw, item.Page)
+				}
+
+				new(push_tmpl_model.XcxPushTmplRecord).Create(user.Id, item.Id, time.Now())
+			} else {
+				beego.BeeLogger.Warn("wxUsers.Xcx_OpenId=%s reach day limit times:%d, not push", user.Openid, push_tmpl_model.XCX_PUSH_TMPL_DAY_LIMIT)
+			}
+		}
+	}
+}
+
+func getWxUsersBySql(key string) (list []*user_model.WxUser) {
+	if key == "all" {
+		sql := "select * from wx_users;"
+		list = user_model.GetWxUsersBySql(sql)
+	} else if key == "allUser" {
+		sql := "select wxu.* from wx_user_gongzhonghaos wx_gzh, wx_users wxu where wx_gzh.wx_user_id = wxu.id and wxu.user_id > 0 and wx_gzh.subscribe=1;"
+		list = user_model.GetWxUsersBySql(sql)
+	} else if strings.HasPrefix(key, "sql=") {
+		sql := strings.Split(key, "sql=")[1]
+		list = user_model.GetWxUsersBySql(sql)
+
+	} else {
+		wxUserIds := strings.Split(key, ",")
+		for i := 0; i < len(wxUserIds); i++ {
+			_id := strings.Trim(wxUserIds[i], " ")
+			id, _ := strconv.ParseInt(_id, 10, 64)
+			wxUser := user_model.GetWxUserById(id, false)
+			if wxUser != nil {
+				list = append(list, wxUser)
+			}
+		}
+	}
+	return list
+}

+ 148 - 0
go/gopath/src/fohow.com/apps/controllers/seckill_controller/seckill_controller.go

@@ -0,0 +1,148 @@
+package seckill_controller
+
+import (
+	"fohow.com/apps"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/libs/tool"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"time"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction   = []string{"GetSeckillProducts", "GetSeckillDates"}
+	exceptCheckWxUserLoginAction = []string{"GetSeckillProducts", "GetSeckillDates"}
+)
+
+type SeckillController struct {
+	apps.BaseController
+}
+
+func (self *SeckillController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+func (self *SeckillController) GetSeckillDates() {
+
+	useCache, _ := self.GetBool("cache", false)
+	now := time.Now()
+
+	rank := tool.WEEKDAY_ENUM[now.Weekday().String()]
+	startDate := time.Date(now.Year(), now.Month(), now.Day()-(rank-1), 0, 0, 0, 0, time.Local)
+	endDate := startDate.AddDate(0, 0, 7)
+
+	beego.BeeLogger.Warn("startdate : %s, enddate : %s", startDate, endDate)
+
+	existingDates := product_model.GetSeckillDates(startDate, endDate, useCache)
+
+	var list []string
+	if len(existingDates) > 0 {
+		for i := 0; i < 7; i++ {
+			list = append(list, startDate.AddDate(0, 0, i).Format("2006-01-02"))
+		}
+	}
+
+	type RetItem struct {
+		Date       int64  `json:"date"`
+		DateFormat string `json:"date_format"`
+		State      string `json:"state"`
+		StateCn    string `json:"state_cn"`
+		EndTime    int64  `json:"end_time"`
+		StartTime  int64  `json:"start_time"`
+	}
+	var retItemList []*RetItem
+
+	for i := 0; i < len(list); i++ {
+		item := list[i]
+		retItem := new(RetItem)
+		retItem.DateFormat = item
+		date, _ := time.ParseInLocation("2006-01-02", item, time.Local)
+		retItem.Date = date.Unix()
+
+		onOffSeckillTime := product_model.GetSeckillOnOffSeckillTimeByDate(item, useCache)
+		if onOffSeckillTime != nil && onOffSeckillTime.EarliestSeckillStart.Unix() > 0 {
+			if now.Unix() < onOffSeckillTime.EarliestSeckillStart.Unix() {
+				retItem.State = product_model.SECKILL_PREPARING_STATE
+				retItem.StateCn = product_model.SECKILL_PREPARING_STATE_CN
+				retItem.StartTime = onOffSeckillTime.EarliestSeckillStart.Unix()
+			} else if now.Unix() < onOffSeckillTime.LatestSeckillEnd.Unix() {
+				retItem.State = product_model.SECKILL_UNDER_STATE
+				retItem.StateCn = product_model.SECKILL_UNDER_STATE_CN
+				retItem.StartTime = onOffSeckillTime.EarliestSeckillStart.Unix()
+				retItem.EndTime = onOffSeckillTime.LatestSeckillEnd.Unix()
+			} else {
+				retItem.State = product_model.SECKILL_END_STATE
+				retItem.StateCn = product_model.SECKILL_END_STATE_CN
+			}
+		} else {
+			retItem.State = product_model.SECKILL_NONE_STATE
+			retItem.StateCn = product_model.SECKILL_NONE_STATE_CN
+		}
+		retItemList = append(retItemList, retItem)
+	}
+
+	type ApiRet struct {
+		RetItemList []*RetItem `json:"ret_item_list"`
+	}
+
+	self.Data["json"] = &ApiRet{RetItemList: retItemList}
+	self.ServeJSON()
+}
+
+func (self *SeckillController) GetSeckillProducts() {
+
+	date := self.GetString("query_date", time.Now().Format("2006-01-02"))
+	useCache, _ := self.GetBool("cache", true)
+
+	queryDate, err := time.ParseInLocation("2006-01-02", date, time.Local)
+	if err != nil {
+		beego.BeeLogger.Warn("%s", err)
+		self.ServeJSON()
+		return
+	}
+
+	type ApiRet struct {
+		ProductList []*product_model.Product `json:"product_list"`
+	}
+
+	products := product_model.GetSeckillProducts(queryDate, useCache)
+
+	for _, pd := range products {
+		pd.SoldCount = pd.SaleNums
+		if pd.Count > pd.SoldCount {
+			pd.LeftCount = pd.Count - pd.SoldCount
+		}
+		pd.SoldCount = pd.SoldCount + pd.VirtualSoldCount
+
+		pd.SeckillStartAt = pd.SeckillStart.Unix()
+		pd.SeckillEndAt = pd.SeckillEnd.Unix()
+
+		if pd.SeckilShowPrice > 0 {
+			now := time.Now()
+			pd.SeckillStartAt = pd.SeckillStart.Unix()
+			pd.SeckillEndAt = pd.SeckillEnd.Unix()
+			if pd.SeckillStart.Unix() > now.Unix() {
+				pd.SeckillState = product_model.SECKILL_PREPARING_STATE
+				pd.SeckillStateCn = product_model.SECKILL_PREPARING_STATE_CN
+				pd.SoldCount = pd.SoldCount - pd.VirtualSoldCount
+			} else if pd.SeckillStart.Unix() <= now.Unix() && now.Unix() < pd.SeckillEnd.Unix() {
+				pd.IsUnderSeckill = true
+				pd.SeckillState = product_model.SECKILL_UNDER_STATE
+				pd.SeckillStateCn = product_model.SECKILL_UNDER_STATE_CN
+			} else {
+				pd.SeckillState = product_model.SECKILL_END_STATE
+				pd.SeckillStateCn = product_model.SECKILL_END_STATE_CN
+			}
+		}
+	}
+
+	if products == nil || len(products) == 0 {
+		products = make([]*product_model.Product, 0, 0)
+	}
+
+	self.Data["json"] = &ApiRet{ProductList: products}
+	self.ServeJSON()
+}

+ 57 - 0
go/gopath/src/fohow.com/apps/controllers/share_controller/share_controller.go

@@ -0,0 +1,57 @@
+package share_controller
+
+import (
+	// "math/rand"
+	// "strings"
+
+	"github.com/astaxie/beego/context"
+	// "d"
+	"fohow.com/apps/models/share_model"
+	// "api.com/apps/models/zt_model"
+
+	"fohow.com/apps"
+	// "api.com/cache"
+	"time"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction   = []string{"DoShare"}
+	exceptCheckWxUserLoginAction = []string{"DoShare"}
+)
+
+type ShareController struct {
+	apps.BaseController
+}
+
+func (self *ShareController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+//统计分享
+func (self *ShareController) DoShare() {
+	sc := self.GetString("sc")
+	ri := self.GetString("ri")
+	rp := self.GetString("rp")
+	to := self.GetString("to")
+	uId := self.GetCurrentWxUserId()
+	//wxUser := self.GetCurrentWxUser(true)
+	//if wxUser == nil {
+	//	self.ReturnError(403, apps.WxUserNeedLogin, "", nil)
+	//}
+	//uId := wxUser.Id
+	ip := self.Ctx.Input.IP()
+
+	history := share_model.GetShareInfo(uId, ri, rp, sc, true)
+	if history != nil {
+		history.ShareTimes = history.ShareTimes + 1
+		history.ShareLatestTime = time.Now()
+		history.Save()
+	} else {
+		history = share_model.Insert(sc, ri, rp, uId, to, ip)
+	}
+	self.Data["json"] = history
+	self.ServeJSON()
+}

+ 110 - 0
go/gopath/src/fohow.com/apps/controllers/share_material_controller/share_material_controller.go

@@ -0,0 +1,110 @@
+package share_material_controller
+
+import (
+	"fohow.com/apps"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/share_material_model"
+	"fohow.com/apps/models/user_model"
+	"github.com/astaxie/beego/context"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction   = []string{"List"}
+	exceptCheckWxUserLoginAction = []string{""}
+)
+
+type ShareMaterialController struct {
+	apps.BaseController
+}
+
+func (self *ShareMaterialController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+func (self *ShareMaterialController) List() {
+
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	cache, _ := self.GetBool("cache", true)
+
+	list := share_material_model.GetShareMaterialList(page, perPage, cache)
+
+	listCount := share_material_model.GetShareMaterialListCount(cache)
+
+	for _, item := range list {
+		item.CTime = item.CreatedAt.Unix()
+		imageList := make([]string, 0, 0)
+		if item.Image1st != "" {
+			imageList = append(imageList, self.GetCdnFullImgUrl(item.Image1st))
+		}
+		if item.Image2nd != "" {
+			imageList = append(imageList, self.GetCdnFullImgUrl(item.Image2nd))
+		}
+		if item.Image3rd != "" {
+			imageList = append(imageList, self.GetCdnFullImgUrl(item.Image3rd))
+		}
+		if item.Image4th != "" {
+			imageList = append(imageList, self.GetCdnFullImgUrl(item.Image4th))
+		}
+		if item.Image5th != "" {
+			imageList = append(imageList, self.GetCdnFullImgUrl(item.Image5th))
+		}
+		if item.Image6th != "" {
+			imageList = append(imageList, self.GetCdnFullImgUrl(item.Image6th))
+		}
+		if item.Image7th != "" {
+			imageList = append(imageList, self.GetCdnFullImgUrl(item.Image7th))
+		}
+		if item.Image8th != "" {
+			imageList = append(imageList, self.GetCdnFullImgUrl(item.Image8th))
+		}
+		if item.Image9th != "" {
+			imageList = append(imageList, self.GetCdnFullImgUrl(item.Image9th))
+		}
+
+		item.ImageList = imageList
+
+		if item.MType == share_material_model.M_TYPE_ENUM_PRODUCT && item.TypeId > 0 {
+			product := product_model.GetProductById(item.TypeId, cache)
+
+			if product != nil {
+				productInfo := new(share_material_model.ProductInfo)
+				productInfo.Id = product.Id
+				productInfo.Name = product.Name
+				productInfo.Price = product.Price
+				productInfo.Album = product_model.GetPicturesByPIdAndPType(product.Id, product_model.PIC_TYPE_ALBUM, cache)
+				item.ProductInfo = productInfo
+			}
+		}
+
+		if item.WxUid > 0 {
+			wxUser := user_model.GetWxUserById(item.WxUid, cache)
+			if wxUser != nil {
+				wxUserInfo := new(share_material_model.WxUserInfo)
+				wxUserInfo.Id = wxUser.Id
+				wxUserInfo.Nickname = wxUser.Nickname
+				wxUserInfo.Head = self.GetCdnFullImgUrl(wxUser.Head)
+				item.WxUserInfo = wxUserInfo
+			}
+		}
+
+	}
+
+	if list == nil {
+		list = make([]*share_material_model.ShareMaterial, 0, 0)
+	}
+
+	type Ret struct {
+		List      []*share_material_model.ShareMaterial `json:"list"`
+		ListCount int64                                 `json:"list_count"`
+	}
+
+	self.Data["json"] = &Ret{List: list, ListCount: listCount}
+	self.ServeJSON()
+}

+ 148 - 0
go/gopath/src/fohow.com/apps/controllers/sms_controller/sms_controller.go

@@ -0,0 +1,148 @@
+package sms_controller
+
+import (
+	"fmt"
+	"math/rand"
+	// "strconv"
+	// "crypto/md5"
+	// "encoding/hex"
+	// "strings"
+	"time"
+
+	// "d"
+	// "github.com/alidayu"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"github.com/astaxie/beego/utils/captcha"
+	// "github.com/astaxie/beego/httplib"
+
+	"fohow.com/apps"
+	"fohow.com/apps/models/sms_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/cache"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction   = []string{"GetCaptcha", "Send"}
+	exceptCheckWxUserLoginAction = []string{"GetCaptcha", "Send"}
+	// cpt                          *captcha.Captcha
+	cpt = captcha.NewWithFilter("/captcha/", cache.Cache)
+)
+
+type SMSController struct {
+	apps.BaseController
+}
+
+func (self *SMSController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+	// cpt = captcha.NewWithFilter("/captcha/", cache.Cache)
+}
+
+//获取图片验证码
+func (self *SMSController) GetCaptcha() {
+	id, err := cpt.CreateCaptcha()
+	var img string
+	if err != nil {
+		beego.BeeLogger.Error("get captcha err=%s", err)
+		self.ReturnError(403, apps.GetCaptchaError, "", nil)
+	}
+	img = fmt.Sprintf("%s%s%s.png?reload=%d",
+		beego.AppConfig.String("ApiHost"), cpt.URLPrefix, id, time.Now().Unix())
+	self.SetSession("captcha_id", id)
+	type VC struct {
+		Id  string `json:"id"`
+		Src string `json:"src"`
+	}
+	vc := &VC{Id: id, Src: img}
+	self.Data["json"] = vc
+	self.ServeJSON()
+}
+
+func (self *SMSController) Send() {
+	tel := self.GetString("tel")
+	if len(tel) != 11 {
+		self.ReturnError(403, apps.PhoneInvalid, "", nil)
+	}
+	msg_type := self.GetString("valid_type")
+
+	//防刷,IP
+	ip := self.Ctx.Input.IP()
+	ipKey := fmt.Sprintf("SMS_Send_IP_%s_msgType_%s", ip, msg_type)
+	if ipTimes, ok := cache.Cache.Get(ipKey).(int64); ok {
+		ipTimes = ipTimes + 1
+		cache.Cache.Put(ipKey, ipTimes, 1*time.Minute)
+		if ipTimes > 2 {
+			//beego.BeeLogger.Error("sms send too often!!! IP: %s, tel:%s, msg_type: %s, times: %d", ip, tel, msg_type, ipTimes)
+			//self.ReturnError(403, apps.CodesSendTooOften, "", nil)
+		}
+	} else {
+		ipTimes = 1
+		cache.Cache.Put(ipKey, ipTimes, 1*time.Minute)
+	}
+
+	// //pc端弹出图片验证码
+	// if !self.IsMobile() && (msg_type == sms_model.SIGN_UP ||
+	// 	msg_type == sms_model.RESET_PWD ||
+	// 	msg_type == sms_model.RESET_TRADE_PWD) {
+	// 	id := self.GetString("id")
+	// 	picCode := self.GetString("code")
+	// 	if id == "" || picCode == "" {
+	// 		self.ReturnError(403, apps.PicVerifyCodeError, "", nil)
+	// 	}
+	// 	if !cpt.Verify(id, picCode) {
+	// 		self.ReturnError(403, apps.PicVerifyCodeError, "", nil)
+	// 	}
+	// }
+	// //只有管理员才发送这两种类型的验证码
+	// if msg_type == sms_model.SEND_BONUS || msg_type == sms_model.SEND_INVITE_BENEFIT {
+	// 	if tel != beego.AppConfig.String("AdminTel") {
+	// 		self.ReturnError(403, apps.TelCodesTypeError, "", nil)
+	// 	}
+	// }
+
+	code := fmt.Sprintf("%04d", rand.Int63n(9999))
+	beego.BeeLogger.Warn("sms code: %s, type: %s", code, msg_type)
+	k := fmt.Sprintf("%s%s", msg_type, tel)
+
+	//防刷,同一个号码一分钟内超过5次调用,返回错误提示
+	sendTimesKey := fmt.Sprintf("SMS_Send_Times_%s", tel)
+	if sendTimes, ok := cache.Cache.Get(sendTimesKey).(int64); ok {
+		sendTimes = sendTimes + 1
+		cache.Cache.Put(sendTimesKey, sendTimes, 1*time.Minute)
+		if sendTimes > 5 {
+			self.ReturnError(403, apps.CodesSendTooOften, "", nil)
+		}
+	} else {
+		sendTimes = 1
+		cache.Cache.Put(sendTimesKey, sendTimes, 1*time.Minute)
+	}
+
+	// //号码已被注册
+	// if msg_type == sms_model.SIGN_UP ||
+	// 	msg_type == sms_model.INVITE_SIGN_UP {
+	// 	user := user_model.GetByTel(tel, false)
+	// 	if user != nil {
+	// 		self.ReturnError(403, apps.PhoneExist, "", nil)
+	// 	}
+	// }
+
+	//号码已绑定了其他微信用户
+	if msg_type == sms_model.BINDING {
+		user := user_model.GetByTel(tel, false)
+		if user != nil {
+			wxUser := user_model.GetWxUserByUserId(user.Id, false)
+			if wxUser != nil {
+				self.ReturnError(403, apps.BindingTelExisted, "", nil)
+			}
+		}
+	}
+	cache.Cache.Put(k, code, 10*time.Minute)
+
+	sign, template, action := sms_model.GetAliMsgContent(msg_type)
+
+	go sms_model.SendSmsWithAli([]string{tel}, sign, template, action, code)
+	self.ServeJSON()
+}

+ 23 - 0
go/gopath/src/fohow.com/apps/controllers/test_controller/init.go

@@ -0,0 +1,23 @@
+package test_controller
+
+import (
+	"github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+)
+
+var (
+	exceptCheckUserLoginAction = []string{"*"}
+
+	exceptCheckWxUserLoginAction = []string{"*"}
+)
+
+type TestController struct {
+	apps.BaseController
+}
+
+func (self *TestController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 82 - 0
go/gopath/src/fohow.com/apps/controllers/test_controller/test_controller.go

@@ -0,0 +1,82 @@
+package test_controller
+
+import (
+	"fmt"
+	"fohow.com/apps/models/dollar_win_model"
+	"fohow.com/apps/models/order_model"
+	"github.com/astaxie/beego"
+	"strconv"
+)
+
+type LabiReturn struct {
+	RetCode string `json:"ret_code"`
+	RetMsg  string `json:"ret_msg"`
+	TradeNo string `json:"trade_no"`
+}
+
+func (self *TestController) UpdateInviteCount() {
+	_zt_id := self.Ctx.Input.Param(":zt_id")
+	zt_id, _ := strconv.ParseInt(_zt_id, 10, 64)
+	_temp_user_id := self.Ctx.Input.Param(":temp_user_id")
+	temp_user_id, _ := strconv.ParseInt(_temp_user_id, 10, 64)
+	//获取所有我推荐参与的会员
+	frendJoinList := dollar_win_model.GetJoinFrendPaiedWinListByZtConfigId(zt_id, temp_user_id, false)
+	frendCount := len(frendJoinList)
+	self.SetSession("dollar_count", frendCount)
+	self.UpdateCount(frendJoinList, zt_id)
+	count := self.GetSession("dollar_count").(int)
+	type Ret struct {
+		Count int64 `json:"Count"`
+	}
+	self.Data["json"] = &Ret{Count: int64(count)}
+	self.ServeJSON()
+}
+
+//测试订单
+func (self *TestController) TestOrderListen() {
+	orderId := self.GetString("order_id")
+	f := func() {
+		i := int64(1)
+		for {
+			i++
+			if i <= 10 {
+				fmt.Println(i)
+			} else {
+				break
+			}
+		}
+	}
+	for {
+		order := order_model.GetOrderById(orderId)
+		go func(v string) {
+			if v == order_model.STATUS_PROCESSING || v == order_model.STATUS_DISPATCH {
+				fmt.Println(v)
+				go f()
+			}
+		}(order.Status)
+		if order.Status == order_model.STATUS_DISPATCH {
+			//break
+		}
+	}
+
+	self.ServeJSON()
+}
+
+func (self *TestController) UpdateCount(list []*dollar_win_model.DollarWinZtJoin, ztConfigId int64) {
+
+	for _, item := range list {
+		if item.UserId == 600110 || item.UserId == 600113 || item.UserId == 602368 || item.UserId == 600106 {
+			continue
+		}
+		//更新直接下级推荐数量
+		beego.BeeLogger.Warn("dollar_win_model.UpdateCount().CinviteId(%d)", item.UserId)
+		inviteJoinList := dollar_win_model.GetJoinFrendPaiedWinListByZtConfigId(ztConfigId, item.UserId, false)
+		count := int(len(inviteJoinList))
+		oldCount := self.GetSession("dollar_count").(int)
+		count += oldCount
+		//更新参与数量
+		self.SetSession("dollar_count", count)
+		//递归调用
+		self.UpdateCount(inviteJoinList, ztConfigId)
+	}
+}

+ 30 - 0
go/gopath/src/fohow.com/apps/controllers/tool_controller/init.go

@@ -0,0 +1,30 @@
+package tool_controller
+
+import (
+	// "fmt"
+	// "os"
+	// "net/url"
+	// "strings"
+
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	// "github.com/go-wkhtmltoimage"
+	// "github.com/skip2/go-qrcode"
+
+	"fohow.com/apps"
+)
+
+var (
+	exceptCheckUserLoginAction   = []string{"GetQrcode", "GetHaibao", "Monitor"}
+	exceptCheckWxUserLoginAction = []string{"GetQrcode", "GetHaibao", "Monitor"}
+)
+
+type ToolController struct {
+	apps.BaseController
+}
+
+func (self *ToolController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 91 - 0
go/gopath/src/fohow.com/apps/controllers/tool_controller/qrcode_controller.go

@@ -0,0 +1,91 @@
+package tool_controller
+
+import (
+	"fmt"
+	// "os"
+	"net/url"
+	// "strings"
+
+	"github.com/astaxie/beego"
+	// "github.com/astaxie/beego/context"
+	"github.com/go-wkhtmltoimage"
+	"github.com/skip2/go-qrcode"
+	// "fohow.com/apps"
+	// "api.com/apps/controllers/user_controller"
+	// "api.com/apps/models/order_model"
+	// "api.com/apps/models/product_model"
+	// "api.com/apps/models/vas_model"
+	// qrcode "github.com/skip2/go-qrcode"
+)
+
+func (self *ToolController) GetHaibao() {
+	uri := self.GetString("url")
+	// if !isWhiteUrl(uri) {
+	// 	beego.BeeLogger.Error("BlackUrl=%s", uri)
+	// 	self.ReturnError(403, apps.BlackUrl, "", nil)
+	// }
+	h, _ := self.GetInt("h")
+	w, _ := self.GetInt("w")
+	q, _ := self.GetInt("q")
+	if h <= 0 {
+		h = 0 //default 0
+	}
+	if w <= 0 {
+		w = 1024 //default 1024
+	}
+	if q <= 0 {
+		q = 94 //0-100, default 94
+	}
+	c := wkhtmltoimage.ImageOptions{
+		BinaryPath: beego.AppConfig.String("WkhtmltoimgBinPath"),
+		Input:      uri,
+		Height:     h,
+		Width:      w,
+		Quality:    q,
+		Format:     "png"}
+	out, _ := wkhtmltoimage.GenerateImage(&c)
+	self.Ctx.Output.Header("Content-Type", "image/png; charset=utf-8")
+	self.Ctx.Output.Body(out)
+}
+
+func (self *ToolController) GetQrcode() {
+	uri := self.GetString("url")
+	// if !isWhiteUrl(uri) {
+	// 	beego.BeeLogger.Error("BlackUrl=%s", uri)
+	// 	self.ReturnError(403, apps.BlackUrl, "", nil)
+	// }
+	size, _ := self.GetInt("size")
+	if size <= 0 {
+		size = 256 //default 0
+	}
+	png, _ := createQrcode(uri, size)
+	self.Ctx.Output.Header("Content-Type", "image/png; charset=utf-8")
+	self.Ctx.Output.Body(png)
+}
+func createQrcode(uri string, size int) ([]byte, error) {
+	png, err := qrcode.Encode(uri, qrcode.Medium, size)
+	return png, err
+}
+func isWhiteUrl(uri string) bool {
+	if beego.AppConfig.String("RunMode") == "dev" {
+		return true
+	}
+	_uri, _ := url.Parse(uri)
+	checkUri := fmt.Sprintf("%s://%s", _uri.Scheme, _uri.Host)
+	var urls []string
+	urls = append(urls, beego.AppConfig.String("WxHost"))
+	urls = append(urls, beego.AppConfig.String("ApiHost"))
+	urls = append(urls, beego.AppConfig.String("RailsHost"))
+	urls = append(urls, "http://tapi.d5c360.com")
+	urls = append(urls, "https://m.d5ct.com")
+	urls = append(urls, "http://m.d5c360.com")
+	urls = append(urls, "http://www.d5c360.com")
+	urls = append(urls, "http://testadmin.zhppp.com")
+	urls = append(urls, "http://weixin.qq.com")
+	for _, v := range urls {
+		if v == checkUri {
+			return true
+		}
+	}
+	return false
+}

+ 26 - 0
go/gopath/src/fohow.com/apps/controllers/tool_controller/tool_controller.go

@@ -0,0 +1,26 @@
+package tool_controller
+
+import (
+	"fohow.com/apps/models/user_model"
+)
+
+func (self *ToolController) DeleteSession() {
+	key := self.Ctx.Input.Param(":key")
+	s := user_model.GetByKey(key)
+	if s != nil {
+		s.Delete()
+	}
+	self.Data["json"] = ""
+	self.ServeJSON()
+}
+
+func (self *ToolController) Monitor() {
+	self.ServeJSON()
+}
+
+//强制关注
+func (self *ToolController) CheckWxUserFollow() {
+	qrcodeSceneId, _ := self.GetInt64("qs_id")
+	self.CheckWxUserSubscribe(qrcodeSceneId)
+	self.ServeJSON()
+}

+ 140 - 0
go/gopath/src/fohow.com/apps/controllers/user_controller/binding_controller.go

@@ -0,0 +1,140 @@
+package user_controller
+
+import (
+	"fmt"
+	// "strconv"
+	// "bufio"
+	// "crypto/md5"
+	// "encoding/hex"
+	// "time"
+
+	// "github.com/astaxie/beego"
+
+	"fohow.com/apps"
+	// "fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/sms_model"
+	// "fohow.com/apps/models/trial_coin_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/cache"
+	// "fohow.com/libs/tool"
+	// "fohow.com/libs/user"
+	// "fohow.com/libs/wx_mp"
+	//"fohow.com/apps/models/address_model"
+	//"fohow.com/apps/models/balance_model"
+	//"fohow.com/apps/models/order_model"
+	//"fohow.com/apps/models/exchange_model"
+	"fohow.com/apps/models/channel_parent_model"
+	"strconv"
+)
+
+//绑定手机
+func (self *UserController) BindingTel() {
+	code := self.GetString("code")
+	tel := self.GetString("tel")
+	signUpchannelId, _ := self.GetInt64("channel", 0)
+	//校验短信验证码
+	k := fmt.Sprintf("%s%s", sms_model.BINDING, tel)
+	if cacheCode, ok := cache.Cache.Get(k).(string); ok {
+		if code != cacheCode {
+			self.ReturnError(403, apps.TelCodesError, "", nil)
+		}
+	} else {
+		//验证码过期
+		self.ReturnError(403, apps.TelCodesExpired, "", nil)
+	}
+
+	wxUser := self.GetCurrentWxUser(false)
+	//如果已经绑定了user,直接返回
+	if wxUser.UserId != 0 {
+		self.ReturnError(403, apps.WxUserHasBindingTel, "", nil)
+	}
+	user := user_model.GetByTel(tel, false)
+	if user != nil {
+		self.ReturnError(403, apps.BindingTelExisted, "", nil)
+	} else { //手机号未被注册
+		// loginPwd := tool.Get8Uuid()
+		// md5Ctx := md5.New()
+		// md5Ctx.Write([]byte(loginPwd))
+		// cipherStr := md5Ctx.Sum(nil)
+		// md5Pwd := hex.EncodeToString(cipherStr)
+		ip := self.Ctx.Input.IP()
+		user = user_model.Create(tel, ip)
+		if user == nil {
+			self.ReturnError(403, apps.BindingUserTelError, "", nil)
+		}
+		wxUser.UserId = user.Id
+		wxUser.Save()
+		user.CopyWxUserHead(wxUser.Head)
+		user.Nickname = wxUser.Nickname
+		user.Country = wxUser.Country
+		user.Province = wxUser.Province
+		user.City = wxUser.City
+		user.Sex = wxUser.Sex
+		//参数第一,cookie第二
+		cId, _ := strconv.ParseInt(self.Ctx.GetCookie("sign_up_channel"), 10, 64)
+		if signUpchannelId > 0 {
+			cId = signUpchannelId
+		}
+		channel := channel_parent_model.GetParentSignUpChannelById(cId, true)
+		if channel != nil {
+			user.SignupChannelId = cId
+		}
+		user.Save()
+		if user != nil {
+			self.SetSession(apps.SessionUserKey, user.Id)
+		}
+		// sign, template, action := sms_model.GetAliMsgContent(sms_model.LOGIN_PWD)
+		// go sms_model.SendSmsWithAli([]string{tel}, sign, template, action, loginPwd)
+		//go user_model.UpdateIsRegistD5c(user)
+		//go address_model.UpdateAddressUserId(wxUser)
+		//go balance_model.UpdateBalanceUserId(wxUser)
+		//go balance_model.UpdateBalanceOrderUserId(wxUser)
+		//go order_model.UpdateOrderUserId(wxUser)
+		//go exchange_model.UpdateExOrderUser(wxUser)
+	}
+	self.Data["json"] = user
+	self.ServeJSON()
+}
+
+//绑定手机
+func (self *UserController) BindingTelNew() {
+	code := self.GetString("code")
+	tel := self.GetString("tel")
+	//校验短信验证码
+	k := fmt.Sprintf("%s%s", sms_model.BINDING, tel)
+	if cacheCode, ok := cache.Cache.Get(k).(string); ok {
+		if code != cacheCode {
+			self.ReturnError(403, apps.TelCodesError, "", nil)
+		}
+	} else {
+		//验证码过期
+		self.ReturnError(403, apps.TelCodesExpired, "", nil)
+	}
+
+	wxUser := self.GetCurrentWxUser(false)
+	//如果未注册user,直接返回
+	if wxUser.UserId == 0 {
+		self.ReturnError(403, apps.UserNotExist, "", nil)
+	}
+	user := user_model.GetByTel(tel, false)
+	if user != nil {
+		self.ReturnError(403, apps.BindingTelExisted, "", nil)
+	} else { //手机号未被注册
+		// loginPwd := tool.Get8Uuid()
+		// md5Ctx := md5.New()
+		// md5Ctx.Write([]byte(loginPwd))
+		// cipherStr := md5Ctx.Sum(nil)
+		// md5Pwd := hex.EncodeToString(cipherStr)
+		user = user_model.GetUserById(wxUser.UserId, false)
+		if user == nil {
+			self.ReturnError(403, apps.BindingUserTelError, "", nil)
+		}
+		user.Tel = tel
+		user.Save()
+		if user != nil {
+			self.SetSession(apps.SessionUserKey, user.Id)
+		}
+	}
+	self.Data["json"] = user
+	self.ServeJSON()
+}

+ 189 - 0
go/gopath/src/fohow.com/apps/controllers/user_controller/binding_wx_phone_controller.go

@@ -0,0 +1,189 @@
+package user_controller
+
+import (
+	"encoding/json"
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/user_model"
+	"github.com/astaxie/beego"
+)
+
+func (self *UserController) BindingWxPhone() {
+
+	wxUser := self.GetCurrentWxUser(false)
+
+	//如果已经绑定了user,直接返回
+	if wxUser.UserId != 0 {
+		self.ReturnError(403, apps.WxUserHasBindingTel, "", nil)
+	}
+
+	var tel string
+
+	//获取参数
+	//sign := self.GetString("sign")
+	//unixTime := self.GetString("tistamp")
+	//tel := self.GetString("phonumer")
+	//beego.BeeLogger.Warn("BindingWxPhone-sign:%s, unixTime:%s, tel:%s", sign, unixTime, tel)
+	//if strings.TrimSpace(tel) == "" || strings.TrimSpace(sign) == "" || strings.TrimSpace(unixTime) == ""{
+	//	self.ReturnError(403, apps.ParamsError, "", nil)
+	//}
+	//
+	//if !self.CheckParamsIsValid(tel, sign, unixTime){
+	//	self.ReturnError(403, apps.ParamsError, "", nil)
+	//}
+
+	sessionKey, _ := self.GetSession(apps.XcxSessionKey).(string)
+	if sessionKey == "" {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+
+	params := self.GetString("userinfo")
+	type Info struct {
+		Iv            string `json:"iv"`
+		EncryptedData string `json:"encryptedData"`
+	}
+	info := new(Info)
+	err := json.Unmarshal([]byte(params), &info)
+
+	if err != nil {
+		beego.BeeLogger.Error("BindingWxPhone err: %s, info:%s", err, info)
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	type WatermarkInfo struct {
+		Appid     string `json:"appid"`
+		Timestamp string `json:"timestamp"`
+	}
+	type EncryptedData struct {
+		PhoneNumber     string         `json:"phoneNumber"`
+		PurePhoneNumber string         `json:"purePhoneNumber"`
+		CountryCode     string         `json:"countryCode"`
+		Watermark       *WatermarkInfo `json:"watermark"`
+	}
+	//phoneNumber	String	用户绑定的手机号(国外手机号会有区号)
+	//purePhoneNumber	String	没有区号的手机号
+	//countryCode	String	区
+
+	pc := helpers.WxBizDataCrypt{AppID: beego.AppConfig.String("WxFohowXcxAppId"), SessionKey: sessionKey}
+	result, err := pc.Decrypt(info.EncryptedData, info.Iv, true) //第三个参数解释: 需要返回 JSON 数据类型时 使用 true, 需要返回 map 数据类型时 使用 false
+	if err != nil {
+		beego.BeeLogger.Error("xcx BindingWxPhone descrypt failed, err:%s", err)
+		self.ReturnError(403, apps.BindingWxPhoneError, "", nil)
+	}
+	encryptedData := &EncryptedData{}
+	json.Unmarshal([]byte(result.(string)), encryptedData)
+
+	if encryptedData.PurePhoneNumber == "" {
+		self.ReturnError(403, apps.BindingWxPhoneError, "", nil)
+	}
+
+	tel = encryptedData.PurePhoneNumber
+
+	user := user_model.GetByTel(tel, false)
+	if user != nil {
+		self.ReturnError(403, apps.BindingTelExisted, "", nil)
+	} else { //手机号未被注册
+
+		ip := self.Ctx.Input.IP()
+		user = user_model.Create(tel, ip)
+		if user == nil {
+			self.ReturnError(403, apps.BindingUserTelError, "", nil)
+		}
+		wxUser.UserId = user.Id
+		wxUser.Save()
+		user.CopyWxUserHead(wxUser.Head)
+		user.Nickname = wxUser.Nickname
+		user.Country = wxUser.Country
+		user.Province = wxUser.Province
+		user.City = wxUser.City
+		user.Sex = wxUser.Sex
+		user.Save()
+		// 如果微信用户已绑定手机,则找出userId,并且赋值给session[userId]
+		if wxUser != nil && wxUser.UserId > 0 {
+			self.SetSession(apps.SessionWxUserKey, wxUser.Id)
+			self.SetSession(apps.SessionUserKey, wxUser.UserId)
+		}
+	}
+	self.Data["json"] = user
+	self.ServeJSON()
+
+}
+
+func (self *UserController) BindingWxPhoneNew() {
+
+	wxUser := self.GetCurrentWxUser(false)
+
+	//如果已经绑定了user,直接返回
+	if wxUser.UserId == 0 {
+		self.ReturnError(403, apps.UserNotExist, "", nil)
+	}
+
+	var tel string
+
+	sessionKey, _ := self.GetSession(apps.XcxSessionKey).(string)
+	if sessionKey == "" {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+
+	params := self.GetString("userinfo")
+	type Info struct {
+		Iv            string `json:"iv"`
+		EncryptedData string `json:"encryptedData"`
+	}
+	info := new(Info)
+	err := json.Unmarshal([]byte(params), &info)
+
+	if err != nil {
+		beego.BeeLogger.Error("BindingWxPhone err: %s, info:%s", err, info)
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	type WatermarkInfo struct {
+		Appid     string `json:"appid"`
+		Timestamp string `json:"timestamp"`
+	}
+	type EncryptedData struct {
+		PhoneNumber     string         `json:"phoneNumber"`
+		PurePhoneNumber string         `json:"purePhoneNumber"`
+		CountryCode     string         `json:"countryCode"`
+		Watermark       *WatermarkInfo `json:"watermark"`
+	}
+	//phoneNumber	String	用户绑定的手机号(国外手机号会有区号)
+	//purePhoneNumber	String	没有区号的手机号
+	//countryCode	String	区
+
+	pc := helpers.WxBizDataCrypt{AppID: beego.AppConfig.String("WxFohowXcxAppId"), SessionKey: sessionKey}
+	result, err := pc.Decrypt(info.EncryptedData, info.Iv, true) //第三个参数解释: 需要返回 JSON 数据类型时 使用 true, 需要返回 map 数据类型时 使用 false
+	if err != nil {
+		beego.BeeLogger.Error("xcx BindingWxPhone descrypt failed, err:%s", err)
+		self.ReturnError(403, apps.BindingWxPhoneError, "", nil)
+	}
+	encryptedData := &EncryptedData{}
+	json.Unmarshal([]byte(result.(string)), encryptedData)
+
+	if encryptedData.PurePhoneNumber == "" {
+		self.ReturnError(403, apps.BindingWxPhoneError, "", nil)
+	}
+
+	tel = encryptedData.PurePhoneNumber
+
+	user := user_model.GetByTel(tel, false)
+	if user != nil {
+		self.ReturnError(403, apps.BindingTelExisted, "", nil)
+	} else { //手机号未被注册
+		user = user_model.GetUserById(wxUser.UserId, false)
+		if user == nil {
+			self.ReturnError(403, apps.BindingUserTelError, "", nil)
+		}
+		user.Tel = tel
+		user.Save()
+		// 如果微信用户已绑定手机,则找出userId,并且赋值给session[userId]
+		if wxUser != nil && wxUser.UserId > 0 {
+			self.SetSession(apps.SessionWxUserKey, wxUser.Id)
+			self.SetSession(apps.SessionUserKey, wxUser.UserId)
+		}
+	}
+	self.Data["json"] = user
+	self.ServeJSON()
+
+}

+ 37 - 0
go/gopath/src/fohow.com/apps/controllers/user_controller/init.go

@@ -0,0 +1,37 @@
+package user_controller
+
+import (
+	// "fmt"
+	// "math/rand"
+	// "strconv"
+	// "strings"
+	// "time"
+
+	// "d"
+	// "github.com/alidayu"
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	// "github.com/astaxie/beego/httplib"
+
+	"fohow.com/apps"
+	// "fohow.com/cache"
+	// "fohow.com/apps/models/project_join_model"
+	// "fohow.com/apps/models/project_model"
+)
+
+var (
+	exceptCheckUserLoginAction = []string{"BindingTel", "UpdateWxUserInfo",
+		"CheckLogin", "GenerateQrcode",
+		"GetInviteList", "GetMonthlyInviteList", "OneClickBindingTel", "BindingWxPhone", "SetWxUserInviter", "Get"}
+	exceptCheckWxUserLoginAction = []string{"CheckLogin"}
+)
+
+type UserController struct {
+	apps.BaseController
+}
+
+func (self *UserController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 296 - 0
go/gopath/src/fohow.com/apps/controllers/user_controller/invite_controller.go

@@ -0,0 +1,296 @@
+package user_controller
+
+import (
+	// "crypto/md5"
+	// "encoding/hex"
+	"fmt"
+	"io/ioutil"
+	"strconv"
+	// "time"
+
+	"github.com/astaxie/beego"
+
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/user_model"
+	// "st.com/cache"
+	// "api.com/libs/tool"
+	"fohow.com/libs/ali_oss"
+	"fohow.com/libs/wx_mp"
+	// "encoding/csv"
+	// "os"
+	"time"
+)
+
+func (self *UserController) GenerateQrcode() {
+	width, _ := self.GetInt("width", 430)
+	wxUser := self.GetCurrentWxUser(false)
+	// wxUser = user_model.GetWxUserById(2, false)
+	if wxUser == nil {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+
+	homeUrl := fmt.Sprintf("pages/start/start")
+	type Ret struct {
+		QrcodeUrl string `json:"qrcode_url"`
+	}
+	qrcodeUrl := ""
+	if wxUser.InviteQrcodeUrl != "" {
+		qrcodeUrl = self.GetFullImgUrl(wxUser.InviteQrcodeUrl)
+	} else {
+		scene := fmt.Sprintf("invite_wx_%d", wxUser.Id)
+		qrcodeData := wx_mp.GenerateQrcode(wx_mp.QR_SCENE, homeUrl, scene, width)
+		beego.BeeLogger.Warn("invite_controller.GenerateQrcode() data_array: %d", qrcodeData)
+		filename := fmt.Sprintf("invite_qrcode_%d.jpg", wxUser.Id)
+		localPath := fmt.Sprintf("%s/%s", beego.AppConfig.String("InviteQrcodePath"), filename)
+		err := ioutil.WriteFile(localPath, qrcodeData, 0644)
+		if err != nil {
+			beego.BeeLogger.Error(err.Error())
+		}
+		uploadPath := fmt.Sprintf("qrcode_path/invite/%s", filename)
+		//上传到阿里云原目录下面
+		err = ali_oss.PutObjectFromFile(beego.AppConfig.String("AliOssEndPoint"),
+			beego.AppConfig.String("AliOssAccessId"),
+			beego.AppConfig.String("AliOssAccessSecret"),
+			beego.AppConfig.String("AliOssBucket"), "", uploadPath, localPath)
+		if err != nil {
+			beego.BeeLogger.Error("Upload Pngs err: %s", err)
+		} else {
+			wxUser.InviteQrcodeUrl = uploadPath
+			wxUser.Save()
+			qrcodeUrl = self.GetFullImgUrl(uploadPath)
+		}
+	}
+	self.Data["json"] = &Ret{QrcodeUrl: qrcodeUrl}
+	self.ServeJSON()
+}
+
+//查看邀请的一级关系列表 - 总的佣金奖励
+func (self *UserController) GetInviteList() {
+
+	// inviteWxId, _ := self.GetInt64("wx_uid")
+	// _id := self.Ctx.Input.Param(":invite_wxid")
+	// inviteWxId, _ := strconv.ParseInt(_id, 10, 64)
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	useCache, _ := self.GetBool("cache", false)
+	wxUser := self.GetCurrentWxUser(useCache)
+	// if inviteWxId == 0 {
+	// 	self.ReturnError(403, apps.NoExist, "", nil)
+	// }
+	// wxUser := user_model.GetWxUserById(inviteWxId, useCache)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	var inviter *user_model.WxUser
+	if wxUser.InviteId != 0 {
+		inviter = user_model.GetWxUserById(wxUser.InviteId, useCache)
+	}
+
+	wxUser.Head = self.GetFullImgUrl(wxUser.Head)
+	list := user_model.GetMyIntroListByInviteId(wxUser.Id, page, perPage, useCache)
+	count := user_model.GetMyIntroCountByInviteId(wxUser.Id)
+	total := user_model.GetInviteCountByInviteId(wxUser.Id)
+
+	if list == nil {
+		list = make([]*user_model.Invitee, 0, 0)
+	}
+
+	type Ret struct {
+		Inviter   *user_model.WxUser    `json:"wx_inviter"`
+		WxUser    *user_model.WxUser    `json:"wx_user"`
+		Total     int64                 `json:"total"`
+		List      []*user_model.Invitee `json:"list"`
+		ListCount int64                 `json:"list_count"`
+	}
+	self.Data["json"] = &Ret{WxUser: wxUser, Inviter: inviter, List: list, ListCount: count, Total: total}
+	self.ServeJSON()
+}
+
+//查看邀请的一级关系列表 - 每月的佣金奖励
+func (self *UserController) GetMonthlyInviteList() {
+
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	useCache, _ := self.GetBool("cache", false)
+
+	wxUser := self.GetCurrentWxUser(useCache)
+
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	var inviter *user_model.WxUser
+	if wxUser.InviteId != 0 {
+		inviter = user_model.GetWxUserById(wxUser.InviteId, useCache)
+	}
+
+	//查询月份
+	y, _ := self.GetInt("y", int(time.Now().Year()))
+	m, _ := self.GetInt("m", int(time.Now().Month()))
+
+	if y <= 0 || y > int(time.Now().Year()) {
+		y = int(time.Now().Year())
+	}
+	if m > 12 || m < 1 {
+		m = int(time.Now().Month())
+	}
+
+	checkMonth := time.Date(y, time.Month(m), 1, 0, 0, 0, 0, time.Local)
+
+	wxUser.Head = self.GetFullImgUrl(wxUser.Head)
+	list := user_model.GetMyMonthIntroListByInviteId(wxUser.Id, page, perPage, checkMonth, useCache)
+	count := user_model.GetMyMonthIntroListCount(wxUser.Id)
+	total := user_model.GetInviteCountByInviteIdAndTime(wxUser.Id, checkMonth)
+
+	//monthTotal = user_model.GetInviteCountByInviteIdAndTime(curWxUser.Id, checkMonth)
+	//monthCount = user_model.GetInviteListCountByInviteIdAndTime(curWxUser.Id, checkMonth)
+	if list == nil {
+		list = make([]*user_model.Invitee, 0, 0)
+	}
+
+	type Ret struct {
+		Inviter   *user_model.WxUser    `json:"wx_inviter"` //当前微信用户的邀请人
+		WxUser    *user_model.WxUser    `json:"wx_user"`    //当前微信用户
+		Total     int64                 `json:"total"`      //总奖励金
+		List      []*user_model.Invitee `json:"list"`       //好友列表
+		ListCount int64                 `json:"list_count"` //好友列表总数
+	}
+	self.Data["json"] = &Ret{WxUser: wxUser, Inviter: inviter, List: list, ListCount: count, Total: total}
+	self.ServeJSON()
+}
+
+func (self *UserController) GetSecInviteList() {
+
+	_id := self.Ctx.Input.Param(":invited_wxid")
+	invitedWxId, _ := strconv.ParseInt(_id, 10, 64)
+
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	useCache, _ := self.GetBool("cache", false)
+	wxUser := self.GetCurrentWxUser(useCache)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	invitedWxUser := user_model.GetWxUserById(invitedWxId, useCache)
+	if invitedWxUser == nil || invitedWxUser.InviteId != wxUser.Id {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	invitedWxUser.Head = self.GetFullImgUrl(invitedWxUser.Head)
+	list := user_model.GetInviteListByInviteId(invitedWxUser.Id, page, perPage, useCache)            //invitedWxUser好友列表
+	count := user_model.GetInviteListCountByInviteId(invitedWxUser.Id)                               //invitedWxUser好友数
+	total := user_model.GetInvitedTotalCountByBenefitWxUIdAndWxUId(wxUser.Id, invitedWxId, useCache) //invitedWxUser带来的佣金+ invitedWxUser的下级带来的佣金
+
+	for _, item := range list {
+		count := user_model.GetIndWxUIdCountByBenefitWxUIdAndWxUIdAndIndWxUId(wxUser.Id, invitedWxId, item.WxUId, useCache)
+		item.Total = count
+	}
+	if list == nil {
+		list = make([]*user_model.Invitee, 0, 0)
+	}
+
+	invitedCount := user_model.GetIndWxUIdCountByBenefitWxUIdAndWxUIdAndIndWxUId(wxUser.Id, invitedWxId, invitedWxId, useCache)
+	consume := user_model.GetInviteConsumeCountByBeWxUIdAndWxUId(wxUser.Id, invitedWxId)
+
+	type Ret struct {
+		WxUser        *user_model.WxUser    `json:"wx_user"`         //当前微信用户
+		InvitedWxUser *user_model.WxUser    `json:"invited_wx_user"` //当前微信用户邀请的一级invitedWxUser
+		Total         int64                 `json:"total"`           //invitedWxUser带来的佣金+ invitedWxUser的下级带来的佣金
+		Consume       int64                 `json:"consume"`         //消费人
+		InvitedCount  int64                 `json:"invited_count"`   //当前微信用户邀请的一级invitedWxUser-个人总产出
+		List          []*user_model.Invitee `json:"list"`            //invitedWxUser好友列表
+		ListCount     int64                 `json:"list_count"`      //invitedWxUser好友列表总数/好友人数
+	}
+
+	self.Data["json"] = &Ret{WxUser: wxUser, InvitedWxUser: invitedWxUser, Total: total, Consume: consume, InvitedCount: invitedCount, List: list, ListCount: count}
+	self.ServeJSON()
+}
+
+func (self *UserController) GetSecMonthlyInviteList() {
+	_id := self.Ctx.Input.Param(":invited_wxid")
+	invitedWxId, _ := strconv.ParseInt(_id, 10, 64)
+
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	useCache, _ := self.GetBool("cache", false)
+	wxUser := self.GetCurrentWxUser(useCache)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	invitedWxUser := user_model.GetWxUserById(invitedWxId, useCache)
+	if invitedWxUser == nil || invitedWxUser.InviteId != wxUser.Id {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	y, _ := self.GetInt("y", int(time.Now().Year()))
+	m, _ := self.GetInt("m", int(time.Now().Month()))
+
+	if y <= 0 || y > int(time.Now().Year()) {
+		y = int(time.Now().Year())
+	}
+	if m > 12 || m < 1 {
+		m = int(time.Now().Month())
+	}
+
+	checkMonth := time.Date(y, time.Month(m), 1, 0, 0, 0, 0, time.Local)
+
+	invitedWxUser.Head = self.GetFullImgUrl(invitedWxUser.Head)
+
+	list := user_model.GetMyMonthIntroListByInviteId(wxUser.Id, page, perPage, checkMonth, useCache)
+	count := user_model.GetMyMonthIntroListCount(wxUser.Id)
+	total := user_model.GetInvitedTotalCountByBenefitWxUIdAndWxUIdAndTime(wxUser.Id, invitedWxId, checkMonth, useCache) //本月奖励金
+
+	for _, item := range list {
+		count := user_model.GetIndWxUIdCountByBenWxUIdAndWxUIdAndIndWxUIdAndTime(wxUser.Id, invitedWxId, item.WxUId, checkMonth, useCache)
+		item.Total = count
+	}
+	if list == nil {
+		list = make([]*user_model.Invitee, 0, 0)
+	}
+
+	invitedCount := user_model.GetIndWxUIdCountByBenWxUIdAndWxUIdAndIndWxUIdAndTime(wxUser.Id, invitedWxId, invitedWxId, checkMonth, useCache)
+	monthConsume := user_model.GetInviteConsumeCountByBeWxUIdAndWxUIdAndTime(wxUser.Id, invitedWxId, checkMonth)
+
+	type Ret struct {
+		WxUser        *user_model.WxUser    `json:"wx_user"`         //当前微信用户
+		InvitedWxUser *user_model.WxUser    `json:"invited_wx_user"` //当前微信用户邀请的一级invitedWxUser
+		MonthTotal    int64                 `json:"month_total"`     //本月奖励金
+		MonthConsume  int64                 `json:"month_consume"`   //invitedWxUser本月消费人
+		InvitedCount  int64                 `json:"invited_count"`   //当前微信用户邀请的一级invitedWxUser-某月个人总产出
+		List          []*user_model.Invitee `json:"list"`            //invitedWxUser好友列表
+		ListCount     int64                 `json:"list_count"`      //invitedWxUser好友列表总数/好友人数
+	}
+
+	self.Data["json"] = &Ret{WxUser: wxUser, InvitedWxUser: invitedWxUser, MonthConsume: monthConsume, MonthTotal: total, InvitedCount: invitedCount, List: list, ListCount: count}
+	self.ServeJSON()
+}
+
+// 设置微信用户的邀请人,邀请人记录的也是微信用户ID
+func (self *UserController) SetWxUserInviter() {
+	if !self.IsWxClient() {
+		url := fmt.Sprintf("%s/notwx", beego.AppConfig.String("MHost"))
+		self.Redirect(url, 302)
+		return
+	}
+	ivId, _ := strconv.ParseInt(self.Ctx.Input.Param(":wx_inviter"), 10, 64)
+	currectWxUId := self.GetCurrentWxUserId()
+	if currectWxUId != 0 && ivId != 0 {
+		go helpers.SetInviter(currectWxUId, ivId)
+	}
+	self.ServeJSON()
+}

+ 60 - 0
go/gopath/src/fohow.com/apps/controllers/user_controller/one_click_binding_controller.go

@@ -0,0 +1,60 @@
+package user_controller
+
+import (
+	"fohow.com/apps"
+	"fohow.com/apps/models/user_model"
+)
+
+//绑定手机
+func (self *UserController) OneClickBindingTel() {
+
+	wxUser := self.GetCurrentWxUser(false)
+
+	//如果已经绑定了user,直接返回
+	if wxUser.UserId != 0 {
+		self.ReturnError(403, apps.WxUserHasBindingTel, "", nil)
+	}
+	tel := ""
+	if tel == "" {
+		self.ReturnError(403, apps.WxUserNotRegistD5c, "", nil)
+	}
+
+	user := user_model.GetByTel(tel, false)
+	if user != nil {
+		self.ReturnError(403, apps.BindingTelExisted, "", nil)
+	} else { //手机号未被注册
+		// loginPwd := tool.Get8Uuid()
+		// md5Ctx := md5.New()
+		// md5Ctx.Write([]byte(loginPwd))
+		// cipherStr := md5Ctx.Sum(nil)
+		// md5Pwd := hex.EncodeToString(cipherStr)
+		ip := self.Ctx.Input.IP()
+		user = user_model.Create(tel, ip)
+		if user == nil {
+			self.ReturnError(403, apps.BindingUserTelError, "", nil)
+		}
+		wxUser.UserId = user.Id
+		wxUser.Save()
+		user.CopyWxUserHead(wxUser.Head)
+		user.Nickname = wxUser.Nickname
+		user.Country = wxUser.Country
+		user.Province = wxUser.Province
+		user.City = wxUser.City
+		user.Sex = wxUser.Sex
+		// cId, _ := strconv.ParseInt(self.Ctx.GetCookie("sign_up_channel"), 10, 64)
+		// channel := user_model.GetSignUpChannelById(cId, true)
+		// if channel != nil {
+		// 	user.SignupChannelId = cId
+		// }
+		user.Save()
+		// sign, template, action := sms_model.GetAliMsgContent(sms_model.LOGIN_PWD)
+		// go sms_model.SendSmsWithAli([]string{tel}, sign, template, action, loginPwd)
+		// 如果微信用户已绑定手机,则找出userId,并且赋值给session[userId]
+		if wxUser != nil && wxUser.UserId > 0 {
+			self.SetSession(apps.SessionWxUserKey, wxUser.Id)
+			self.SetSession(apps.SessionUserKey, wxUser.UserId)
+		}
+	}
+	self.Data["json"] = user
+	self.ServeJSON()
+}

+ 383 - 0
go/gopath/src/fohow.com/apps/controllers/user_controller/user_controller.go

@@ -0,0 +1,383 @@
+package user_controller
+
+import (
+	"fmt"
+	// "strconv"
+	// // "bufio"
+	"crypto/md5"
+	"encoding/hex"
+	// "time"
+	"encoding/json"
+	"regexp"
+
+	"github.com/astaxie/beego"
+	// // "github.com/astaxie/beego/context"
+	"fohow.com/apps"
+	// "fohow.com/apps/models/balance_model"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/sms_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/cache"
+
+	// "fohow.com/libs/tool"
+	"fohow.com/apps/models/merchant_model"
+	"fohow.com/libs/user"
+	"strings"
+	"time"
+)
+
+// //生成登录Key, 解决微信端在浏览器中打开,自动登录的问题
+// func (self *UserController) GenerateLoginKey() {
+// 	if !self.IsWxClient() {
+// 		self.ReturnError(403, apps.NotWeixinClient, "", nil)
+// 	}
+// 	wxUser := self.GetCurrentWxUser(false)
+// 	type Ret struct {
+// 		Key    string `orm:"-"  json:"key"`
+// 		UserId int64  `orm:"-"  json:"user_id"`
+// 	}
+// 	key := ""
+// 	uId := int64(0)
+// 	//已注册的用户
+// 	if wxUser != nil && wxUser.UserId != 0 {
+// 		uId = wxUser.UserId
+// 		k := cache.GetKey(cache.WapAutoLoginKey, wxUser.UserId)
+// 		if s, ok := cache.Cache.Get(k).(string); ok {
+// 			key = s
+// 		} else {
+// 			key = tool.Get8Uuid()
+// 			cache.Cache.Put(k, key, 1*time.Minute)
+// 		}
+// 	}
+// 	self.Data["json"] = &Ret{Key: key, UserId: uId}
+// 	self.ServeJSON()
+// }
+
+// 校验当前用户是否登录
+func (self *UserController) CheckLogin() {
+	type UserInfo struct {
+		UserId       int64 `json:"user_id"`
+		WxUserId     int64 `json:"wx_user_id"`
+		MerchantId   int64 `json:"merchant_id"`
+		IsSuperAdmin bool  `json:"is_super_admin"` //商家中的超级管理员
+	}
+	userInfo := new(UserInfo)
+	useCache, err := self.GetBool("cache", true)
+	if err != nil {
+		useCache = false
+	}
+	curUser := self.GetCurrentUser(useCache)
+	if curUser != nil {
+		userInfo.UserId = curUser.Id
+	}
+	wxUser := self.GetCurrentWxUser(useCache)
+	if wxUser != nil {
+		userInfo.WxUserId = wxUser.Id
+	}
+
+	// 商户信息, 如果不是商户,则返回 0
+	merchant_user_id := int64(0)
+	if curUser != nil {
+		merchant_user_id = curUser.Id
+	}
+	merchantUser := merchant_model.GetMerchantUserRelationByUserId(merchant_user_id, false) //一个商户对应多个用户
+	if merchantUser != nil {
+		userInfo.MerchantId = merchantUser.MerchantId
+		userInfo.IsSuperAdmin = merchantUser.IsSuperAdmin
+	}
+
+	self.Data["json"] = userInfo
+	self.ServeJSON()
+}
+
+func (self *UserController) CheckCertificate() {
+	type UserInfo struct {
+		IsCertification      int64  `json:"is_certification"`        //是否认证
+		IdentityCardWithStar string `json:"identity_card_with_star"` //身份证号
+		RealName             string `json:"real_name"`
+		Head                 string `json:"head"`
+	}
+	userInfo := new(UserInfo)
+
+	user := self.GetCurrentUser(false)
+	if user != nil {
+		if strings.TrimSpace(user.IdentityCard) == "" || strings.TrimSpace(user.RealName) == "" {
+			user.IsCertification = 0
+			user.Save()
+		}
+		userInfo.IsCertification = user.IsCertification
+		userInfo.IdentityCardWithStar = user.GetCardNoWithStar()
+		userInfo.RealName = user.RealName
+		userInfo.Head = self.GetFullImgUrl(user.Head)
+	}
+
+	self.Data["json"] = userInfo
+	self.ServeJSON()
+}
+
+//修改用户信息
+func (self *UserController) UpdateWxUserInfo() {
+	wxUser := self.GetCurrentWxUser(false)
+	if wxUser == nil {
+		return
+	}
+	params := self.GetString("userinfo")
+	beego.BeeLogger.Warn("params start: %s", params)
+
+	type UserInfo struct {
+		NickName  string `json:"nickName"`  // 用户的昵称
+		Gender    int64  `json:"gender"`    // 用户的性别, 值为1时是男性, 值为2时是女性, 值为0时是未知
+		Language  string `json:"language"`  // 用户的语言, zh_CN, zh_TW, en
+		City      string `json:"city"`      // 用户所在城市
+		Province  string `json:"province"`  // 用户所在省份
+		Country   string `json:"country"`   // 用户所在国家
+		AvatarUrl string `json:"avatarUrl"` // 头像
+	}
+	type Info struct {
+		ErrMsg        string    `json:"errMsg"`
+		RawData       string    `json:"rawData"`
+		Signature     string    `json:"signature"`
+		Iv            string    `json:"iv"`
+		EncryptedData string    `json:"encryptedData"`
+		UserInfo      *UserInfo `json:"userInfo"`
+	}
+	info := new(Info)
+	err := json.Unmarshal([]byte(params), &info)
+
+	if err != nil {
+		beego.BeeLogger.Error("UpdateWxUserInfo err: %s, info:%s", err, info)
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	//临时创建的id前缀
+	var validID = regexp.MustCompile(`^TMP_UNIONID.*$`)
+
+	// 如果用户没有 unionid, 则通过 解密 EncryptedData 获取到 unionid, 并更新user字段
+	if wxUser.Unionid == "" || validID.MatchString(wxUser.Unionid) {
+		sessionKey, _ := self.GetSession(apps.XcxSessionKey).(string)
+		beego.BeeLogger.Debug("UpdateWxUserInfo.sessionKey=[%s]", sessionKey)
+		type UserInfoWithUnionId struct {
+			UnionId string `json:"unionId"`
+		}
+
+		if sessionKey != "" {
+			userInfoWithUnionId := &UserInfoWithUnionId{}
+			pc := helpers.WxBizDataCrypt{AppID: beego.AppConfig.String("WxFohowXcxAppId"), SessionKey: sessionKey}
+			result, err := pc.Decrypt(info.EncryptedData, info.Iv, true) //第三个参数解释: 需要返回 JSON 数据类型时 使用 true, 需要返回 map 数据类型时 使用 false
+			if err != nil {
+				fmt.Println(err)
+			} else {
+				json.Unmarshal([]byte(result.(string)), userInfoWithUnionId)
+				wxUser.Unionid = userInfoWithUnionId.UnionId
+				beego.BeeLogger.Warn("UpdateWxUserInfo.UnionId=[%s]", userInfoWithUnionId.UnionId)
+			}
+		}
+	}
+
+	beego.BeeLogger.Warn("userinfo: %s", info.UserInfo)
+	wxUser.Nickname = info.UserInfo.NickName
+	wxUser.Sex = info.UserInfo.Gender
+	wxUser.City = info.UserInfo.City
+	wxUser.Province = info.UserInfo.Province
+	wxUser.Country = info.UserInfo.Country
+	wxUser.Save()
+	wxUser.UploadHead(info.UserInfo.AvatarUrl)
+	self.Data["json"] = wxUser
+	self.ServeJSON()
+}
+
+// // 	if incomeErr == nil {
+// // 		user.IncomeId = incomeId
+// // 	}
+// // 	if income := user_model.GetUserIncomeById(user.IncomeId, false); income != nil {
+// // 		user.Income = income.Range
+// // 	}
+// // 	user.Save()
+// // 	// user.BirthdayStr = birthday
+// // 	self.Data["json"] = user
+// // 	self.ServeJSON()
+// // }
+
+// 获取当前用户信息
+func (self *UserController) Get() {
+	useCache, err := self.GetBool("cache", true)
+	if err != nil {
+		useCache = false
+	}
+	user := self.GetCurrentUser(useCache)
+	wxUser := self.GetCurrentWxUser(useCache)
+	if user != nil {
+		user.Head = self.GetFullImgUrl(user.Head)
+		user.HasTradePwd = 0
+		if user.TradePwd != "" {
+			user.HasTradePwd = 1
+		}
+		user.IdentityCardWithStar = user.GetCardNoWithStar()
+	}
+	if wxUser != nil {
+		wxUser.Head = self.GetFullImgUrl(wxUser.Head)
+		wxGzhUser := user_model.GetWxUserGzhByWxUIdAndAppId(wxUser.Id, beego.AppConfig.String("WxMPAppId"), useCache)
+		if wxGzhUser != nil {
+			wxUser.Subscribe = wxGzhUser.Subscribe
+		}
+	}
+
+	type Ret struct {
+		User         *user_model.User                     `json:"user"`
+		WxUser       *user_model.WxUser                   `json:"wx_user"`
+		MerchantUser *merchant_model.MerchantUserRelation `json:"merchant"`
+	}
+
+	// 商户信息, 如果不是商户,则返回 null
+	merchant_user_id := int64(0)
+	if user != nil {
+		merchant_user_id = user.Id
+	}
+	merchantUser := merchant_model.GetMerchantUserRelationByUserId(merchant_user_id, false) //一个商户对应多个用户
+
+	mergeUser := &Ret{User: user, WxUser: wxUser, MerchantUser: merchantUser}
+	self.Data["json"] = mergeUser
+	self.ServeJSON()
+}
+
+//修改交易密码
+func (self *UserController) UpdateTradePwd() {
+	code := self.GetString("code")
+	tel := self.GetString("tel")
+	pwd := self.GetString("pwd")
+	confirmedPwd := self.GetString("confirmed_pwd")
+	if pwd != confirmedPwd {
+		self.ReturnError(403, apps.PasswordError, "", nil)
+	}
+
+	user := self.GetCurrentUser(false)
+	if user == nil {
+		self.ReturnError(403, apps.UserNotExist, "", nil)
+	}
+
+	if user.Tel != tel {
+		self.ReturnError(403, apps.UserTelNotMatch, "", nil)
+	}
+
+	md5Ctx := md5.New()
+	md5Ctx.Write([]byte(pwd))
+	cipherStr := md5Ctx.Sum(nil)
+	md5Pwd := hex.EncodeToString(cipherStr)
+
+	k := fmt.Sprintf("%s%s", sms_model.RESET_TRADE_PWD, tel)
+	if cacheCode, ok := cache.Cache.Get(k).(string); ok {
+		if code != cacheCode {
+			self.ReturnError(403, apps.TelCodesError, "", nil)
+		} else {
+			cache.Cache.Delete(k)
+		}
+	} else {
+		//验证码过期
+		self.ReturnError(403, apps.TelCodesExpired, "", nil)
+	}
+
+	user.TradePwd = md5Pwd
+	user.Save()
+
+	self.ServeJSON()
+}
+
+//绑定身份证
+func (self *UserController) BindingIdCard() {
+	idCard := self.GetString("identity_card")
+	realName := self.GetString("real_name")
+	user := self.GetCurrentUser(false)
+	if user.IsCertification == 1 {
+		self.ReturnError(403, apps.UserHasBindIdCard, "", nil)
+	}
+	if idCard == "" || realName == "" {
+		self.ReturnError(403, apps.BindingIdCardEmpty, "", nil)
+	}
+
+	idCardLength := len(idCard)
+	if idCardLength != 15 && idCardLength != 18 {
+		self.ReturnError(403, apps.BindingIdCardLengthError, "", nil)
+	}
+
+	userWithIdCard := user_model.GetUserByIdCardNo(idCard, false)
+	if userWithIdCard != nil {
+		self.ReturnError(403, apps.BindingIdCardExist, "", nil)
+	}
+
+	birth, err := time.Parse("20060102", idCard[6:14])
+	if err == nil {
+		user.Birthday = birth
+	}
+
+	age, getAagError := getAgeByIdCardNo(idCard)
+
+	if getAagError != nil {
+		self.ReturnError(403, apps.BindingIdCardEmpty, "", nil)
+	}
+	if age >= 0 && age < 18 {
+		self.ReturnError(403, apps.IdCardAgeNotBeAdult, "", nil)
+	}
+
+	user.Age = age
+	user.RealName = realName
+	user.IdentityCard = idCard
+	user.IsCertification = 1
+	user.Save()
+	self.Data["json"] = user
+	self.ServeJSON()
+}
+
+//绑定银行卡
+func (self *UserController) SaveBankCard() {
+	bankName := self.GetString("bank_name")
+	bankAccount := self.GetString("bank_account")
+	accountName := self.GetString("account_name")
+
+	user := self.GetCurrentUser(false)
+	if user == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	if bankName == "" || bankAccount == "" || accountName == "" {
+		self.ReturnError(403, apps.BindingBankCardEmpty, "", nil)
+	}
+
+	user.BankName = bankName
+	user.BankAccount = bankAccount
+	user.AccountName = accountName
+	user.Save()
+	self.Data["json"] = user
+	self.ServeJSON()
+}
+
+// 获取当前用户信息
+func (self *UserController) GetBankInfo() {
+	useCache, err := self.GetBool("cache", true)
+	if err != nil {
+		useCache = false
+	}
+	user := self.GetCurrentUser(useCache)
+	if user != nil {
+		user.HasBank = false
+		if user.BankAccount != "" && user.AccountName != "" {
+			user.HasBank = true
+		}
+	}
+	type Ret struct {
+		HasBank     bool   `json:"has_bank"`
+		BankName    string `json:"bank_name"`
+		BankAccount string `json:"bank_account"`
+		AccountName string `json:"account_name"`
+	}
+	ret := &Ret{HasBank: user.HasBank, BankName: user.BankName, BankAccount: user.BankAccount, AccountName: user.AccountName}
+	self.Data["json"] = ret
+	self.ServeJSON()
+}
+
+//根据身份证获取年龄
+func getAgeByIdCardNo(idCardNo string) (int64, error) {
+	age, err := user.CaculateAgeByIdCard(idCardNo)
+	if err != nil {
+		return 0, err
+	}
+	return age, nil
+}

+ 165 - 0
go/gopath/src/fohow.com/apps/controllers/wxku_commodity_controller/wxku_commodity_controller.go

@@ -0,0 +1,165 @@
+package wxku_commodity_controller
+
+import (
+	// "fmt"
+	// "math/rand"
+	// "strconv"
+	// "strings"
+	// "time"
+
+	// "d"
+	// "github.com/alidayu"
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	// "github.com/astaxie/beego/httplib"
+
+	"fohow.com/apps"
+	// "fohow.com/cache"
+	// "fohow.com/apps/models/project_join_model"
+	// "fohow.com/apps/models/project_model"
+	"fmt"
+	"github.com/astaxie/beego"
+	"fohow.com/apps/models/wxku_commodity_model"
+	"fohow.com/libs/tool"
+	"fohow.com/libs/wx_mp"
+	"strconv"
+	"strings"
+)
+
+var (
+	exceptCheckUserLoginAction   = []string{"AddOrUpdateWxkuCommodity", "CheckWxkuCommodityStatus", "GetWxkuCommodityInfo"}
+	exceptCheckWxUserLoginAction = []string{"AddOrUpdateWxkuCommodity", "CheckWxkuCommodityStatus", "GetWxkuCommodityInfo"}
+)
+
+type WxkuCommodityController struct {
+	apps.BaseController
+}
+
+func (self *WxkuCommodityController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+//导入或修改
+func (self *WxkuCommodityController) AddOrUpdateWxkuCommodity() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	cache, _ := self.GetBool("cache", false)
+	partialUpdate, _ := self.GetInt64("partUpdate", 1)
+	beego.BeeLogger.Info("AddOrUpdateWxkuCommodity.partial_update : %d", partialUpdate)
+
+	wxkuCommodity := wxku_commodity_model.GetWxkuCommodityById(id, cache)
+	if wxkuCommodity == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	//组装数据
+	pId := fmt.Sprintf("%d", wxkuCommodity.Id)
+	var imgBaseList []*wx_mp.Base
+	if wxkuCommodity.ImageInfo1 != "" {
+		imgBase1 := &wx_mp.Base{Url: self.GetFullImgUrl(wxkuCommodity.ImageInfo1)}
+		imgBaseList = append(imgBaseList, imgBase1)
+	}
+	if wxkuCommodity.ImageInfo2 != "" {
+		imgBase2 := &wx_mp.Base{Url: self.GetFullImgUrl(wxkuCommodity.ImageInfo2)}
+		imgBaseList = append(imgBaseList, imgBase2)
+	}
+	if wxkuCommodity.ImageInfo3 != "" {
+		imgBase3 := &wx_mp.Base{Url: self.GetFullImgUrl(wxkuCommodity.ImageInfo3)}
+		imgBaseList = append(imgBaseList, imgBase3)
+	}
+	if wxkuCommodity.ImageInfo4 != "" {
+		imgBase4 := &wx_mp.Base{Url: self.GetFullImgUrl(wxkuCommodity.ImageInfo4)}
+		imgBaseList = append(imgBaseList, imgBase4)
+	}
+	if wxkuCommodity.ImageInfo5 != "" {
+		imgBase5 := &wx_mp.Base{Url: self.GetFullImgUrl(wxkuCommodity.ImageInfo5)}
+		imgBaseList = append(imgBaseList, imgBase5)
+	}
+	mainImageList := &wx_mp.MainImageList{imgBaseList}
+
+	categoryNames := strings.Split(wxkuCommodity.CategoryInfo, ",")
+	var categoryBaseList []*wx_mp.Base
+	for _, item := range categoryNames {
+		categoryBase := &wx_mp.Base{CategoryName: item}
+		categoryBaseList = append(categoryBaseList, categoryBase)
+	}
+	categoryInfo := &wx_mp.CategoryItem{categoryBaseList}
+
+	var offCategoryBaseList []*wx_mp.Base
+	offCategoryBase := &wx_mp.Base{CategoryName: wxkuCommodity.OffCategoryInfo}
+	offCategoryBaseList = append(offCategoryBaseList, offCategoryBase)
+	offCategoryInfo := &wx_mp.CategoryItem{offCategoryBaseList}
+
+	var customInfo *wx_mp.CustomList
+	var skuInfo []*wx_mp.SkuItem
+
+	linkInfo := &wx_mp.Base{Url: wxkuCommodity.Url, WxaAppid: wx_mp.WXA_APPID, LinkType: "wxa"}
+
+	shopInfo := &wx_mp.Base{Source: wxkuCommodity.Source}
+
+	priceInfo := &wx_mp.Base{MinPrice: tool.RoundFloat64(float64(wxkuCommodity.MinPrice)/100, 2),
+		MaxPrice:    tool.RoundFloat64(float64(wxkuCommodity.MaxPrice)/100, 2),
+		MinOriPrice: tool.RoundFloat64(float64(wxkuCommodity.MinOriPrice)/100, 2),
+		MaxOriPrice: tool.RoundFloat64(float64(wxkuCommodity.MaxOriPrice)/100, 2)}
+
+	saleInfo := &wx_mp.Base{SaleStatus: wxkuCommodity.SaleStatus, Stock: wxkuCommodity.Stock}
+
+	//导入或全量修改
+	result := wx_mp.AddOrUpdateWxkuCommodity(pId, wxkuCommodity.Title, wxkuCommodity.SubTitle,
+		wxkuCommodity.Brand, wxkuCommodity.Desc, partialUpdate, mainImageList, categoryInfo,
+		offCategoryInfo, customInfo, skuInfo, linkInfo, shopInfo, priceInfo, saleInfo)
+	if result.Errcode != 0 {
+		beego.BeeLogger.Warn("AddOrUpdateWxkuCommodity failed! errcode : %d, errmsg : %s", result.Errcode, result.Errmsg)
+		self.ReturnError(403, []string{fmt.Sprintf("%d", result.Errcode), result.Errmsg}, "", nil)
+	}
+
+	//如果是导入,要记录status_ticket
+	if strings.TrimSpace(result.StatusTicket) != "" && wxkuCommodity.StatusTicket == "" {
+		wxkuCommodity.StatusTicket = result.StatusTicket
+		wxkuCommodity.Save()
+	}
+
+	//返回结果
+	self.ServeJSON()
+}
+
+//查询导入或更新结果查询接口
+func (self *WxkuCommodityController) CheckWxkuCommodityStatus() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	cache, _ := self.GetBool("cache", true)
+
+	wxkuCommodity := wxku_commodity_model.GetWxkuCommodityById(id, cache)
+	if wxkuCommodity == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	result := wx_mp.CheckWxkuCommodityStatus(wxkuCommodity.StatusTicket)
+
+	self.Data["json"] = result
+	self.ServeJSON()
+
+}
+
+//单个商品信息查询
+func (self *WxkuCommodityController) GetWxkuCommodityInfo() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	cache, _ := self.GetBool("cache", true)
+
+	wxkuCommodity := wxku_commodity_model.GetWxkuCommodityById(id, cache)
+	if wxkuCommodity == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	result, resp := wx_mp.GetWxkuCommodityInfo(fmt.Sprintf("%d", wxkuCommodity.Id))
+	type Ret struct {
+		Result *wx_mp.CommodityResult `json:"result"`
+		Resp   string                 `json:"resp"`
+	}
+	self.Data["json"] = &Ret{Result: result, Resp: resp}
+	self.ServeJSON()
+}

+ 35 - 0
go/gopath/src/fohow.com/apps/controllers/xcx_controller/init.go

@@ -0,0 +1,35 @@
+package xcx_controller
+
+import (
+	// "fmt"
+	// "math/rand"
+	// "strconv"
+	// "strings"
+	// "time"
+
+	// "d"
+	// "github.com/alidayu"
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	// "github.com/astaxie/beego/httplib"
+
+	"fohow.com/apps"
+	// "fohow.com/cache"
+	// "fohow.com/apps/models/project_join_model"
+	// "fohow.com/apps/models/project_model"
+)
+
+var (
+	exceptCheckUserLoginAction   = []string{"GetXcxVersion", "GenerateQrcode", "ChannelQrcode", "GetChannelInfo", "FigureChannel"}
+	exceptCheckWxUserLoginAction = []string{"GetXcxVersion", "GenerateQrcode", "ChannelQrcode", "GetChannelInfo", "FigureChannel"}
+)
+
+type XcxController struct {
+	apps.BaseController
+}
+
+func (self *XcxController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 138 - 0
go/gopath/src/fohow.com/apps/controllers/xcx_controller/xcx_controller.go

@@ -0,0 +1,138 @@
+package xcx_controller
+
+import (
+	"fmt"
+	"strconv"
+	// "bufio"
+	// "crypto/md5"
+	// "encoding/hex"
+	"io/ioutil"
+	"time"
+
+	"github.com/astaxie/beego"
+
+	"fohow.com/apps/models/channel_xcx_qrcode_model"
+	"fohow.com/apps/models/version_model"
+	// "fohow.com/libs/tool"
+	"fohow.com/apps"
+	"fohow.com/libs/ali_oss"
+	"fohow.com/libs/wx_mp"
+)
+
+//获取小程序版本
+func (self *XcxController) GetXcxVersion() {
+	v := self.Ctx.Input.Param(":version")
+	if v == "" {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	version := version_model.GetVersion(v)
+	self.Data["json"] = version
+	self.ServeJSON()
+}
+
+//生成讯兑二维码
+func (self *XcxController) GenerateQrcode() {
+	width, _ := self.GetInt("width", 430)
+	url := self.GetString("url", "pages/user/exchange/exchange")
+	type Ret struct {
+		QrcodeUrl string `json:"qrcode_url"`
+	}
+	qrcodeUrl := ""
+
+	scene := fmt.Sprintf("xundui_rediect")
+	qrcodeData := wx_mp.GenerateQrcode(wx_mp.QR_SCENE, url, scene, width)
+	beego.BeeLogger.Warn("xcx_controller.GenerateQrcode() data_array: %d", len(qrcodeData))
+	filename := fmt.Sprintf("qrcode_%d.jpg", time.Now().Unix())
+	localPath := fmt.Sprintf("%s/%s", beego.AppConfig.String("QrcodePath"), filename)
+	err := ioutil.WriteFile(localPath, qrcodeData, 0644)
+	if err != nil {
+		beego.BeeLogger.Error(err.Error())
+	}
+	uploadPath := fmt.Sprintf("qrcode_path/xcx/%s", filename)
+	//上传到阿里云原目录下面
+	err = ali_oss.PutObjectFromFile(beego.AppConfig.String("AliOssEndPoint"),
+		beego.AppConfig.String("AliOssAccessId"),
+		beego.AppConfig.String("AliOssAccessSecret"),
+		beego.AppConfig.String("AliOssBucket"), "", uploadPath, localPath)
+	if err != nil {
+		beego.BeeLogger.Error("Upload Pngs err: %s", err)
+	} else {
+		qrcodeUrl = self.GetFullImgUrl(uploadPath)
+	}
+
+	self.Data["json"] = &Ret{QrcodeUrl: qrcodeUrl}
+	self.ServeJSON()
+}
+
+//根据后台配置生成小程序码
+func (self *XcxController) ChannelQrcode() {
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+	cq := channel_xcx_qrcode_model.GetById(id)
+	if cq == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	width, _ := self.GetInt("width", 430)
+	url := self.GetString("url", "pages/start/start")
+	type Ret struct {
+		QrcodeUrl string `json:"qrcode_url"`
+	}
+	qrcodeUrl := ""
+	scene := fmt.Sprintf("%d", id)
+	// scene := fmt.Sprintf("xundui_rediect")
+	qrcodeData := wx_mp.GenerateQrcode(wx_mp.QR_SCENE, url, scene, width)
+	beego.BeeLogger.Warn("xcx_controller.ChannelQrcode() data_array: %d", len(qrcodeData))
+	filename := fmt.Sprintf("qrcode_%d_%d.jpg", id, time.Now().Unix())
+	localPath := fmt.Sprintf("%s/%s", beego.AppConfig.String("QrcodePath"), filename)
+	err := ioutil.WriteFile(localPath, qrcodeData, 0644)
+	if err != nil {
+		beego.BeeLogger.Error(err.Error())
+	}
+	uploadPath := fmt.Sprintf("qrcode_path/xcx/%s", filename)
+	//上传到阿里云原目录下面
+	err = ali_oss.PutObjectFromFile(beego.AppConfig.String("AliOssEndPoint"),
+		beego.AppConfig.String("AliOssAccessId"),
+		beego.AppConfig.String("AliOssAccessSecret"),
+		beego.AppConfig.String("AliOssBucket"), "", uploadPath, localPath)
+	if err != nil {
+		beego.BeeLogger.Error("Upload Pngs err: %s", err)
+	} else {
+		qrcodeUrl = self.GetFullImgUrl(uploadPath)
+		cq.UrlImage = qrcodeUrl
+		cq.Save()
+	}
+
+	self.Data["json"] = &Ret{QrcodeUrl: qrcodeUrl}
+	self.ServeJSON()
+}
+
+func (self *XcxController) GetChannelInfo() {
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+	beego.BeeLogger.Warn("xcx_controller.GetChannelInfo_(%d)", id)
+	cq := channel_xcx_qrcode_model.GetById(id)
+	beego.BeeLogger.Warn("xcx_controller.GetChannelInfo_(%v)", cq)
+	//由于小程序码被扫了之后,请求根据scene场景值查找渠道信息,获取跳转的路径
+	if cq != nil {
+		cq.ScanTimes = cq.ScanTimes + 1
+		cq.Save()
+	}
+
+	self.Data["json"] = cq
+	self.ServeJSON()
+}
+
+//渠道数据统计
+func (self *XcxController) FigureChannel() {
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+	beego.BeeLogger.Warn("xcx_controller.FigureChannel_(%d)", id)
+	cq := channel_xcx_qrcode_model.GetById(id)
+	beego.BeeLogger.Warn("xcx_controller.FigureChannel_(%v)", cq)
+	if cq != nil {
+		wxUId := self.GetCurrentWxUserId()
+		if wxUId > 0 {
+			beego.BeeLogger.Warn("xcx_controller.FigureChannel_wxUId_(%d)", wxUId)
+			go new(channel_xcx_qrcode_model.SignUpChannelQrcodeResult).Create(cq.Id, wxUId)
+			beego.BeeLogger.Warn("xcx_controller.FigureChannel_(%d)", cq.ScanTimes)
+		}
+	}
+	self.ServeJSON()
+}

+ 53 - 0
go/gopath/src/fohow.com/apps/helpers/invite_helper.go

@@ -0,0 +1,53 @@
+package helpers
+
+import (
+	"time"
+
+	"github.com/astaxie/beego"
+
+	"fohow.com/apps/models/user_model"
+	// "st.com/libs/wx_mp"
+)
+
+//设置邀请人
+func SetInviter(loginedWxUId, ivId int64) {
+	beego.BeeLogger.Warn("******* SetInviter wxid:%d, ivId:%d", loginedWxUId, ivId)
+	// 不能设置自己为邀请人, 且没有下级
+	wxUser := user_model.GetWxUserById(loginedWxUId, false)
+	if wxUser != nil && wxUser.Id != ivId && wxUser.InviteId == 0 &&
+		user_model.GetCountByWxUserInviter(wxUser.Id) == 0 {
+		inviter := user_model.GetWxUserById(ivId, true)
+		if inviter == nil {
+			beego.BeeLogger.Error("wxUser SetInviter(%d) inviter not exsit", ivId)
+			return
+		}
+		now := time.Now()
+		//设置邀请人的时间,比创建wxuser晚60秒,就不设置邀请关系了。认为该wxUser是自己浏览创建的。
+		if now.Unix()-wxUser.CreatedAt.Unix() > 60 {
+			return
+		}
+		introUserId := int64(0)
+		innerNo := ""
+		beego.BeeLogger.Warn("*******inviteWxuser:%s", inviter)
+		//设置群员内部编号
+		if inviter != nil {
+			//邀请人是群主,则直接进入该群,邀请人不为群主,则进入邀请人所在群
+			if inviter.ShowInviteMode == 1 {
+				introUserId = inviter.Id
+				innerNo = inviter.IntroInnerNo
+			} else if inviter.ShowInviteMode == 0 && inviter.IntroUserId > 0 {
+				introUserId = inviter.IntroUserId
+				innerNo = inviter.IntroInnerNo
+			}
+		}
+		beego.BeeLogger.Warn("*******inviteWxuser introUserId:%d", introUserId)
+		wxUser.InviteId = ivId
+		wxUser.InviteId = introUserId
+		wxUser.IntroInnerNo = innerNo
+		err := wxUser.Save()
+		if err != nil {
+			beego.BeeLogger.Error("save err: %s", err)
+		}
+		beego.BeeLogger.Info("Wxuser Inviter Relationship-setInviter(), wxUserId:%d, inviteId:%d", wxUser.Id, wxUser.InviteId)
+	}
+}

+ 179 - 0
go/gopath/src/fohow.com/apps/helpers/notify_helper.go

@@ -0,0 +1,179 @@
+package helpers
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/astaxie/beego"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/apps/models/wx_message_model"
+	"fohow.com/libs/wx_mp"
+	"time"
+)
+
+//养红包金额到账
+func UserBalanceChangedNotify(user user_model.WxUser, amount string, reason string, remark string, page string) {
+	formIdInstance := wx_message_model.GetOne(user.Openid)
+	if formIdInstance == nil {
+		beego.BeeLogger.Warn("None form id is found, can not notify")
+		return
+	}
+	beego.BeeLogger.Info("Goe form id: %d|%s", formIdInstance.Id, formIdInstance.FormID)
+
+	data := wx_mp.BalanceChangedMessageData{
+		Amount:   wx_mp.DataKeywordValue{amount, ""},
+		Reason:   wx_mp.DataKeywordValue{reason, ""},
+		Remark:   wx_mp.DataKeywordValue{remark, "#DC143C"},
+		Datetime: wx_mp.DataKeywordValue{time.Now().Format("2006-01-02 15:04:05"), ""},
+	}
+	beego.BeeLogger.Debug("data:", data)
+	jsonTemplateData, _ := json.Marshal(data)
+
+	templateId := beego.AppConfig.String("MessageTemplateId_BalanceChanged")
+	beego.BeeLogger.Debug("MessageTemplateId_BalanceChanged got: %s", templateId)
+	beego.BeeLogger.Info("TestSendingTemplatemsgController get one: %d", formIdInstance.Id)
+
+	go wx_mp.SendTemplateMessage(user.Openid, templateId, page, formIdInstance.FormID, jsonTemplateData, "keyword1.DATA")
+	formIdInstance.Use()
+}
+
+//物流修改
+func ProductLogisticsChangedNotify(wxUser user_model.WxUser, dispathTime time.Time, productName, orderId, expressCompany, expressOrderNo, remark string, page string) {
+	formIdInstance := wx_message_model.GetOne(wxUser.Openid)
+	if formIdInstance == nil {
+		beego.BeeLogger.Warn("None form id is found, can not notify")
+		return
+	}
+	beego.BeeLogger.Info("Goe form id: %d|%s", formIdInstance.Id, formIdInstance.FormID)
+
+	data := wx_mp.ProductLogisticsChangedMessageData{
+		DispatchTime:   wx_mp.DataKeywordValue{dispathTime.Format("2006-01-02 15:04:05"), ""},
+		ProductName:    wx_mp.DataKeywordValue{productName, ""},
+		OrderId:        wx_mp.DataKeywordValue{orderId, ""},
+		ExpressCompany: wx_mp.DataKeywordValue{expressCompany, ""},
+		ExpressOrderNo: wx_mp.DataKeywordValue{expressOrderNo, ""},
+		Remark:         wx_mp.DataKeywordValue{remark, "#DC143C"},
+	}
+	beego.BeeLogger.Debug("data:", data)
+	jsonTemplateData, _ := json.Marshal(data)
+
+	templateId := beego.AppConfig.String("MessageTemplateId_ProductLogisticsChanged")
+	beego.BeeLogger.Debug("MessageTemplateId_ProductLogisticsChanged got: %s", templateId)
+	beego.BeeLogger.Info("TestSendingTemplatemsgController get one: %d", formIdInstance.Id)
+
+	go wx_mp.SendTemplateMessage(wxUser.Openid, templateId, page, formIdInstance.FormID, jsonTemplateData, "")
+	formIdInstance.Use()
+}
+
+//下单通知
+func OrderCreateNotify(wxUser user_model.WxUser, createdAt time.Time, productName, orderId, remark, statusText string, count int64, page string) {
+	formIdInstance := wx_message_model.GetOne(wxUser.Openid)
+	if formIdInstance == nil {
+		beego.BeeLogger.Warn("None form id is found, can not notify")
+		return
+	}
+	beego.BeeLogger.Info("Goe form id: %d|%s", formIdInstance.Id, formIdInstance.FormID)
+
+	data := wx_mp.OrderCreatedMessageData{
+		OrderId:     wx_mp.DataKeywordValue{orderId, ""},
+		Created_at:  wx_mp.DataKeywordValue{createdAt.Format("2006-01-02 15:04:05"), ""},
+		ProductName: wx_mp.DataKeywordValue{productName, ""},
+		Count:       wx_mp.DataKeywordValue{fmt.Sprintf("%d", count), ""},
+		OrderStatus: wx_mp.DataKeywordValue{statusText, ""},
+		Remark:      wx_mp.DataKeywordValue{remark, "#DC143C"},
+	}
+	beego.BeeLogger.Debug("data:", data)
+	jsonTemplateData, _ := json.Marshal(data)
+
+	templateId := beego.AppConfig.String("MessageTemplateId_OrderCreated")
+	beego.BeeLogger.Debug("MessageTemplateId_OrderCreated got: %s", templateId)
+	beego.BeeLogger.Info("TestSendingTemplatemsgController get one: %d", formIdInstance.Id)
+
+	go wx_mp.SendTemplateMessage(wxUser.Openid, templateId, page, formIdInstance.FormID, jsonTemplateData, "")
+	formIdInstance.Use()
+}
+
+//提醒自用/挂单
+func GranaryProductDealNotify(user user_model.WxUser, productName, selfUseCount, warmTip, servicePerson, emphasisKw string, page string) bool {
+	formIdInstance := wx_message_model.GetOne(user.Openid)
+	if formIdInstance == nil {
+		beego.BeeLogger.Warn("None form id is found, can not notify")
+		return false
+	}
+	beego.BeeLogger.Info("Goe form id: %d|%s", formIdInstance.Id, formIdInstance.FormID)
+
+	data := wx_mp.SelfUseNotifyMessageData{
+		ProductName:   wx_mp.DataKeywordValue{productName, ""},
+		SelfUseCount:  wx_mp.DataKeywordValue{selfUseCount, ""},
+		WarmTip:       wx_mp.DataKeywordValue{warmTip, "#DC143C"},
+		ServicePerson: wx_mp.DataKeywordValue{servicePerson, ""},
+	}
+	beego.BeeLogger.Debug("data:", data)
+	jsonTemplateData, _ := json.Marshal(data)
+
+	templateId := beego.AppConfig.String("MessageTemplateId_ItemStatusRemind")
+	beego.BeeLogger.Debug("MessageTemplateId_ItemStatusRemind got: %s", templateId)
+	beego.BeeLogger.Info("TestSendingTemplatemsgController get one: %d", formIdInstance.Id)
+
+	go wx_mp.SendTemplateMessage(user.Openid, templateId, page, formIdInstance.FormID, jsonTemplateData, emphasisKw)
+	formIdInstance.Use()
+	return true
+}
+
+/*
+众筹项目最新状态通知
+*/
+func ProjectNewStateNotify(user user_model.WxUser, kw1, kw2, kw3, emphasisKw string, page string) bool {
+	formIdInstance := wx_message_model.GetOne(user.Openid)
+	if formIdInstance == nil {
+		beego.BeeLogger.Warn("None form id is found, can not notify")
+		return false
+	}
+	beego.BeeLogger.Info("Goe form id: %d|%s", formIdInstance.Id, formIdInstance.FormID)
+
+	data := wx_mp.Template3MessageData{
+		Kerword1: wx_mp.DataKeywordValue{kw1, ""},
+		Kerword2: wx_mp.DataKeywordValue{kw2, ""},
+		Kerword3: wx_mp.DataKeywordValue{kw3, "#DC143C"},
+	}
+	beego.BeeLogger.Debug("data:", data)
+	jsonTemplateData, _ := json.Marshal(data)
+
+	templateId := beego.AppConfig.String("MessageTemplateId_ProjectNewStateNofity")
+	beego.BeeLogger.Debug("MessageTemplateId_ProjectNewStateNofity got: %s", templateId)
+	beego.BeeLogger.Info("TestSendingTemplatemsgController get one: %d", formIdInstance.Id)
+
+	go wx_mp.SendTemplateMessage(user.Openid, templateId, page, formIdInstance.FormID, jsonTemplateData, emphasisKw)
+	formIdInstance.Use()
+	return true
+}
+
+/*
+提货通知
+商品 {{keyword1.DATA}}
+提货方式 {{keyword2.DATA}}
+备注 {{keyword3.DATA}}
+*/
+
+func DeliveryNotify(user user_model.WxUser, kw1, kw2, kw3, emphasisKw string, page string) bool {
+	formIdInstance := wx_message_model.GetOne(user.Openid)
+	if formIdInstance == nil {
+		beego.BeeLogger.Warn("None form id is found, can not notify")
+		return false
+	}
+	beego.BeeLogger.Info("Goe form id: %d|%s", formIdInstance.Id, formIdInstance.FormID)
+
+	data := wx_mp.Template3MessageData{
+		Kerword1: wx_mp.DataKeywordValue{kw1, ""},
+		Kerword2: wx_mp.DataKeywordValue{kw2, ""},
+		Kerword3: wx_mp.DataKeywordValue{kw3, "#DC143C"},
+	}
+	beego.BeeLogger.Debug("data:", data)
+	jsonTemplateData, _ := json.Marshal(data)
+
+	templateId := beego.AppConfig.String("MessageTemplateId_DeliveryNotify")
+	beego.BeeLogger.Debug("MessageTemplateId_DeliveryNotify got: %s", templateId)
+
+	go wx_mp.SendTemplateMessage(user.Openid, templateId, page, formIdInstance.FormID, jsonTemplateData, emphasisKw)
+	formIdInstance.Use()
+	return true
+}

+ 96 - 0
go/gopath/src/fohow.com/apps/helpers/wxbizdatacrypt.go

@@ -0,0 +1,96 @@
+package helpers
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strings"
+)
+
+var errorCode = map[string]int{
+	"IllegalAesKey":     -41001,
+	"IllegalIv":         -41002,
+	"IllegalBuffer":     -41003,
+	"DecodeBase64Error": -41004,
+}
+
+// WxBizDataCrypt represents an active WxBizDataCrypt object
+type WxBizDataCrypt struct {
+	AppID      string
+	SessionKey string
+}
+
+type showError struct {
+	errorCode int
+	errorMsg  error
+}
+
+func (e showError) Error() string {
+	return fmt.Sprintf("{code: %v, error: \"%v\"}", e.errorCode, e.errorMsg)
+}
+
+// Decrypt Weixin APP's AES Data
+// If isJSON is true, Decrypt return JSON type.
+// If isJSON is false, Decrypt return map type.
+func (wxCrypt *WxBizDataCrypt) Decrypt(encryptedData string, iv string, isJSON bool) (interface{}, error) {
+	if len(wxCrypt.SessionKey) != 24 {
+		return nil, showError{errorCode["IllegalAesKey"], errors.New("sessionKey length is error")}
+	}
+	aesKey, err := base64.StdEncoding.DecodeString(wxCrypt.SessionKey)
+	if err != nil {
+		return nil, showError{errorCode["DecodeBase64Error"], err}
+	}
+
+	if len(iv) != 24 {
+		return nil, showError{errorCode["IllegalIv"], errors.New("iv length is error")}
+	}
+	aesIV, err := base64.StdEncoding.DecodeString(iv)
+	if err != nil {
+		return nil, showError{errorCode["DecodeBase64Error"], err}
+	}
+
+	aesCipherText, err := base64.StdEncoding.DecodeString(encryptedData)
+	if err != nil {
+		return nil, showError{errorCode["DecodeBase64Error"], err}
+	}
+	aesPlantText := make([]byte, len(aesCipherText))
+
+	aesBlock, err := aes.NewCipher(aesKey)
+	if err != nil {
+		return nil, showError{errorCode["IllegalBuffer"], err}
+	}
+
+	mode := cipher.NewCBCDecrypter(aesBlock, aesIV)
+	mode.CryptBlocks(aesPlantText, aesCipherText)
+	aesPlantText = PKCS7UnPadding(aesPlantText)
+
+	var decrypted map[string]interface{}
+	aesPlantText = []byte(strings.Replace(string(aesPlantText), "\a", "", -1))
+	err = json.Unmarshal([]byte(aesPlantText), &decrypted)
+	if err != nil {
+		return nil, showError{errorCode["IllegalBuffer"], err}
+	}
+
+	if decrypted["watermark"].(map[string]interface{})["appid"] != wxCrypt.AppID {
+		return nil, showError{errorCode["IllegalBuffer"], errors.New("appId is not match")}
+	}
+
+	if isJSON == true {
+		return string(aesPlantText), nil
+	}
+
+	return decrypted, nil
+}
+
+// PKCS7UnPadding return unpadding []Byte plantText
+func PKCS7UnPadding(plantText []byte) []byte {
+	length := len(plantText)
+	unPadding := int(plantText[length-1])
+	if unPadding < 1 || unPadding > 32 {
+		unPadding = 0
+	}
+	return plantText[:(length - unPadding)]
+}

+ 614 - 0
go/gopath/src/fohow.com/apps/init.go

@@ -0,0 +1,614 @@
+package apps
+
+import (
+	"fmt"
+	"regexp"
+	"strings"
+	// "time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"github.com/user_agent"
+	// "github.com/uuid"
+	// "fohow.com/apps/controllers/user_controller"
+	// "fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/channel_gzh_qrcode_model"
+	"fohow.com/apps/models/user_model"
+	// "fohow.com/libs/tool"
+	// "fohow.com/libs/wx_mp"
+)
+
+const (
+	SessionUserKey   = "uid"
+	SessionWxUserKey = "wxuid" //用于小程序
+	// SessionWxXcxUserKey = "wxxcxuid"
+	XcxSessionKey = "XcxSessionKey" // 小程序用于加密解密数据的key
+)
+
+var (
+	// 共用
+	NoCart                = []string{"noCart", "请选择您需要结算的产品"}
+	NoExist               = []string{"noExist", "不存在"}
+	HasOver               = []string{"hasOver", "已结束"}
+	HasPaied              = []string{"hasPaied", "已支付"}
+	AccountError          = []string{"accountError", "账户异常"}
+	GongZhongHaoNoExist   = []string{"gongZhongHaoNoExist", "公众号不存在"}
+	Error                 = []string{"error", "未知错误"}
+	TypeError             = []string{"error", "类型错误"}
+	CountError            = []string{"countError", "数量错误"}
+	RegisterUserError     = []string{"registerUserError", "注册会员错误"}
+	BindingWxPhoneError   = []string{"bindingWxPhone", "绑定失败"}
+	XcxAuthorizeError     = []string{"XcxAuthorizeError", "授权失败"}
+	PicVerifyCodeError    = []string{"picVerifyCodeError", "图片验证码错误"}
+	NotOpen               = []string{"notOpen", "暂未开放~带来不便,敬请原谅!"}
+	ChannelNotMatch       = []string{"channelNotMatch", "渠道不匹配"}
+	ChanneloExist         = []string{"channelNoExist", "渠道不存在"}
+	UploadFileError       = []string{"uploadFileError", "上传文件失败!"}
+	UploadFileTypeInvalid = []string{"uploadFileTypeError", "上传文件类型错误!仅支持以下格式:doc|docx|pdf|ppt|pptx|zip"}
+	UploadImgTypeInvalid  = []string{"uploadImgTypeError", "上传文件类型错误!仅支持以下格式:png|jpg|jpeg"}
+
+	// 登录相关
+	HasLogin            = []string{"hasLogin", "已登录"}
+	HasLogout           = []string{"hasLogout", "已登出"}
+	UserNeedLogin       = []string{"userNeedLogin", "需要用户登录"}
+	UserAuthorizeFailed = []string{"userAuthorizeFailed", "用户授权失败"}
+	UserNeedTel         = []string{"userNeedTel", "需要用户绑定手机"}
+	UserTelNotMatch     = []string{"userTelNotMatch", "手机号码不匹配,请使用当前登录用户的号码"}
+	WxUserNeedLogin     = []string{"wxUserNeedLogin", "需要微信用户登录"}
+	NotWeixinClient     = []string{"notWeixinClient", "不是微信客户端"}
+	SignupNotOpen       = []string{"signupNotOpen", "注册暂未开放,敬请期待"}
+	// 支付相关
+	PayWayNoMatch = []string{"payWayNoMatch", "请选择正确的支付方式"}
+	//请输入充值数额
+	RechargeCountWrong = []string{"rechargeCountWrong", "请输入充值数额"}
+	// 关注
+	WxUserNeedSubscribe = []string{"wxUserNeedSubscribe", "请先关注公众号"}
+	TGDKjNeedSub        = []string{"TGDKjNeedSub", "请先进入公众号领取大刀"}
+
+	BlackUser  = []string{"blackUser", "对不起,您已被拉黑"}
+	BlackUrl   = []string{"blackUrl", "对不起,网址不允许访问"}
+	BlackOrder = []string{"blackOrder", "该订单作弊,已被拉黑"}
+
+	CreateOrderFail = []string{"createOrderFail", "创建订单失败"}
+
+	OnlyPlayInWeixin = []string{"onlyPlayInWeixin", "只能在微信客户端进行"}
+	OrderNotExist    = []string{"orderNotExist", "订单不存在"}
+	OrderExist       = []string{"orderExist", "订单已存在"}
+
+	PayFail               = []string{"payFail", "支付失败"}
+	ParamsError           = []string{"paramsError", "参数错误"}
+	ParamsRequired        = []string{"paramsRequired", "缺少参数"}
+	ProductStockZero      = []string{"productStockZero", "商品库存为零"}
+	ProductStockNotEnough = []string{"productStockNotEnough", "商品库存不足"}
+	ProductNotExist       = []string{"productNotExist", "产品不存在"}
+	ProductNotApproved    = []string{"productNotApproved", "产品还未上架"}
+	ProductOffSale        = []string{"productOffSale", "产品已经下架"}
+	OverLimitCount        = []string{"overLimitCount", "超过限购数量"}
+	PurchasedReachLimit   = []string{"purchasedReachLimit", "已购买总数已达到限购数量"}
+
+	RedirectTo = []string{"redirectTo", "302跳转"}
+	ReduceFail = []string{"reduceFail", "帮砍失败"}
+
+	UserNotExist = []string{"userNotExist", "用户不存在"}
+
+	AddressNotExist     = []string{"addressNotExist", "地址不存在"}
+	UserAddressFull     = []string{"userAddressFull", "最多添加5条地址"}
+	UploadUserHeadError = []string{"uploadUserHeadError", "上传头像错误"}
+	AddressNotMatch     = []string{"addressNotMatch", "地址不匹配"}
+	NotUnPay            = []string{"notUnPay", "无法支付,该订单已支付或者已过期"}
+	HasAlreadyPay       = []string{"hasAlreadyPay", "该订单已支付过"}
+	NotPayInTime        = []string{"notPayInTime", "没在规定时间内完成支付"}
+	PayWayNotAllow      = []string{"payWayNotAllow", "不支持该支付方式"}
+	PayNoAuthed         = []string{"payNoAuthed", "支付未授权"}
+	PhoneInvalid        = []string{"phoneInvalid", "手机号码格式不正确"}
+	PhoneExist          = []string{"phoneExist", "手机号码已被注册,请更换其他号码"}
+	SMSInvalid          = []string{"smsInvalid", "短信验证码不正确"}
+	CodesSendTooOften   = []string{"codesSendTooOften", "短信验证码发送过于频繁,请半小时后重试"}
+
+	WxMenusCreatedFailed = []string{"wxMenusCreatedFailed", "微信菜单创建失败"}
+	//小程序相关
+	XcxGetSessionKeyError = []string{"xcxGetSessionKeyError", "获取session key失败"}
+
+	RailAdminIPReduced            = []string{"railsAdminIPReduced", "拒绝此IP访问,权限不够"}
+	TelCodesError                 = []string{"telCodeError", "验证码错误"}
+	TelCodesTypeError             = []string{"telCodesTypeError", "验证码类型错误"}
+	PasswordError                 = []string{"passwordError", "密码错误"}
+	LoginPwdSameWithTradePwdError = []string{"loginPwdSameWithTradePwdError", "登录密码与交易密码不能相同,请重新设置"}
+	OrginalPasswordError          = []string{"orginalPasswordError", "原始密码错误"}
+	TelCodesExpired               = []string{"telCodeExpired", "验证码已过期"}
+	LoginPasswordError            = []string{"loginPasswordError", "登录密码错误"}
+	TradePasswordError            = []string{"tradePasswordError", "交易密码错误"}
+	TradePasswordEmptry           = []string{"tradePasswordEmpty", "未设置交易密码"}
+	UpdatePasswordError           = []string{"updatePasswordError", "更新密码出错"}
+	PasswordLengthError           = []string{"passwordLengthError", "密码长度错误,需要6-20位"}
+	GetCaptchaError               = []string{"getCaptchaError", "获取图片验证码错误"}
+
+	BindingBankCardEmpty     = []string{"bindingBankCardEmpty", "绑定银行卡错误,请完善银行卡信息"}
+	BindingWxUserError       = []string{"bindingWxUserError", "绑定微信错误,请重试"}
+	BindingUserTelError      = []string{"bindingUserTelError", "绑定电话错误,请重试"}
+	BindingIdCardEmpty       = []string{"bindingIdCardEmpty", "绑定身份证错误,请填写真实姓名及身份证号"}
+	UserHasBindIdCard        = []string{"userHasBindIdCard", "该用户已绑定了身份证,请联系客服更改"}
+	BindingIdCardExist       = []string{"bindingIdCardExist", "该身份证已被绑定,请更换其他身份证"}
+	BindingIdCardLengthError = []string{"bindingIdCardLengthError", "身份证位数不合法"}
+	BindingTelExisted        = []string{"bindingTelExisted", "该号码已绑定了微信用户,请尝试其他号码"}
+	WxUserHasBindingTel      = []string{"wxUserHasBindingTel", "该微信用户已经绑定了手机,请勿重复绑定"}
+	WxUserHasBindingTelOnD5c = []string{"wxUserHasBindingTelOnD5c", "本次课程仅限新用户参加,老用户可联系专属客服领取资料包~"}
+	WxUserNotRegistD5c       = []string{"wxUserNotRegistD5c", "用户在第五创没有绑定手机号码"}
+	IdCardAgeNotBeAdult      = []string{"idCardAgeNotBeAdult", "未满18岁,身份认证失败"}
+
+	// 工具tool相关
+	DownloadError        = []string{"downloadError", "阿里云下载文件错误"}
+	ConvertToBase64Error = []string{"convertToBase64Error", "转base64错误"}
+	//资金账户相关
+	BalanceNotExist                  = []string{"balanceNotExist", "资金明细不存在"}
+	BalanceNotEnough                 = []string{"balanceNotEnough", "余额不足"}
+	BalanceNotEnoughForCharge        = []string{"balanceNotEnoughForCharge", "可提现余额不足支付手续费,无法提现"}
+	TakeCashAmountLessThanCharge     = []string{"balanceAmountLessThanCharge", "提现金额需大于手续费金额"}
+	TakeCashAmountInvalid            = []string{"takeCashAmountInvalid", "提现金额不合法,仅支持小数点后两位。如:88.88"}
+	RechargeAmountInvalid            = []string{"RechargeAmountInvalid", "充值金额不合法,仅支持小数点后两位。如:88.88"}
+	BankCardNotFound                 = []string{"bankCardNotFound", "请先绑定银行卡"}
+	BankCardNoInvalid                = []string{"bankCardNoInvalid", "银行卡号不合法,请重新输入"}
+	BankCardNotMatch                 = []string{"bankCardNotMatch", "银行卡号不匹配,请使用实名认证的本人银行卡绑定,如认证信息有误,请联系客服更改"}
+	BankCardHasNoRecordOnLianLianPay = []string{"bankCardHasNoRecordOnLianLianPay", "该银行卡在连连支付无记录,请先充值"}
+	BankCardCountLimit               = []string{"bankCardCountLimit", "银行卡绑定数量限制"}
+	CanNotDeleteDefaultBankCard      = []string{"canNotDeleteDefaultBankCard", "不能删除默认银行卡"}
+	BankCardHasBound                 = []string{"bankCardHasBound", "该银行卡已有绑定记录"}
+
+	//春节红包
+	ActivityNotStart = []string{"activityNotStart", "活动尚未开始"}
+	ActivityIsEnd    = []string{"activityIsEnd", "活动已经结束"}
+	HasGotLuckyMoney = []string{"hasGotLuckyMoney", "你已经拿过红包了"}
+
+	//商家
+	OrderNotBelongToCurrentUser       = []string{"orderNotBelongToCurrentUser", "订单不属于当前用户。"}
+	OrderStatusNotSuit                = []string{"orderStatusNotSuit", "只能处理待发货、待收货的订单"}
+	OrderAlreadyDispatch              = []string{"orderAlreadyDispatch", "该订单已经被处理发货"}
+	OrderAlreadyRefund                = []string{"orderAlreadyRefund", "该订单已经被处理退款"}
+	OrderRefundFaild                  = []string{"orderRefundFaild", "退款失败,请联系客服"}
+	CurrentMerIsNotSuperAdminMerchant = []string{"currentMerIsNotSuperAdminMerchant", "当前用户不是商家超级管理员"}
+	CurrentMerIsNotManageTheProduct   = []string{"currentMerIsNotManageTheProduct", "当前用户不负责管理该商品"}
+	//移动讯兑
+	DataUnMarsha1Error = []string{"dataUnMarsha1Error", "数据解析错误"}
+
+	//项目
+	ProjectNoExist          = []string{"projectNoExist", "项目不存在"}
+	ProjectInvestWayNoExist = []string{"projectInvestWayNoExist", "该资助方式不存在"}
+
+	//微信公众号登录
+	NetworkBusy = []string{"networkBusy", "对不起,网络繁忙,请稍后再试"}
+
+	//销售专题抽奖
+	OverOpenTime                    = []string{"overOpenTime", "开奖时间已经结束"}
+	ProductIdNotSuit                = []string{"productIdNotSuit", "商品与专题配置的商品不一样"}
+	UserSaleProductNotSupportRefund = []string{"productNotSupportRefund", "代销商品暂不支持退款, 请联系客服"}
+
+	//付费课程
+	CourseCodeNoExist    = []string{"courseCodeNoExist", "优惠码不存在"}
+	CourseCodeHasUsed    = []string{"courseCodeHasUsed", "优惠码已使用"}
+	CourseCodeHasExpired = []string{"courseCodeHasExpired", "优惠码已过期"}
+
+	//秒杀活动
+	SeckillNotStart = []string{"seckillNotStart", "秒杀活动尚未开始"}
+	SeckillIsEnd    = []string{"seckillIsEnd", "秒杀活动已经结束"}
+
+	OnlyNew = []string{"onlyNew", "仅限新人购买"}
+)
+
+type BaseController struct {
+	beego.Controller
+	OnlyCheckUserLoginAction     []string
+	ExceptCheckUserLoginAction   []string
+	OnlyCheckWxUserLoginAction   []string
+	ExceptCheckWxUserLoginAction []string
+	UserNeedLoginAction          []string
+}
+
+type Result struct {
+	Status int           `json:"status"`
+	Result []interface{} `json:"result"`
+}
+
+//定义正确结果集
+func (self *BaseController) FormatResult(result []interface{}) *Result {
+	return &Result{
+		Status: 200,
+		Result: result,
+	}
+}
+
+func (self *BaseController) GetCurrentUserId() int64 {
+	uId, _ := self.GetSession(SessionUserKey).(int64)
+	return uId
+}
+
+func (self *BaseController) GetCurrentUser(useCache bool) *user_model.User {
+	id := self.GetCurrentUserId()
+	if id <= 0 {
+		return nil
+	}
+	u := user_model.GetUserById(id, useCache)
+	if u == nil {
+		beego.BeeLogger.Error("User Not Found, uid=%d", id)
+	}
+	return u
+}
+
+func (self *BaseController) GetCurrentWxUserId() int64 {
+	id, _ := self.GetSession(SessionWxUserKey).(int64)
+	return id
+}
+
+func (self *BaseController) GetCurrentWxUser(useCache bool) *user_model.WxUser {
+	id := self.GetCurrentWxUserId()
+	if id <= 0 {
+		return nil
+	}
+	u := user_model.GetWxUserById(id, useCache)
+	if u == nil {
+		beego.BeeLogger.Error("WxUser Not Found, id=%d", id)
+	}
+	return u
+}
+
+// //检查是否是邀请注册,绑定上下级关系, 发送微信通知。使用范围:注册、绑定手机、拉新专题
+// func (self *BaseController) CheckInviteBinding(user *user_model.User) {
+// 	if user == nil {
+// 		return
+// 	}
+// 	//邀请注册,绑定关系
+// 	inviteKey := beego.AppConfig.String("InviteName")
+// 	inviterId := self.Ctx.GetCookie(inviteKey)
+// 	if inviterId != "" {
+// 		id, err := strconv.ParseInt(inviterId, 10, 64)
+// 		if err == nil {
+// 			inviter := user_model.GetUserById(id, true)
+// 			if inviter != nil && user.InviteId == 0 {
+// 				//绑定关系
+// 				user.InviteId = id
+// 				go user.Save()
+// 				//增加邀请收益统计记录, 记录注册用户的ip
+// 				ip := self.Ctx.Input.IP()
+// 				new(user_model.UserInviteBenefitStat).Create(user.Id, user.InviteId,
+// 					1, 0, time.Now(), ip)
+// 				//给邀请人发邀请成功的通知
+// 				WxInviter := user_model.GetWxUserByUserId(inviter.Id, true)
+// 				if WxInviter != nil {
+// 					//给邀请人发注册成功通知
+// 					openId := WxInviter.MpOpenid
+// 					url := fmt.Sprintf("%s/v1/cfc/36", beego.AppConfig.String("ApiHost"))
+// 					title := "有人通过您的邀请,并成功注册\n"
+// 					telwStar := tool.ReplaceTelMid4(inviter.Tel)
+// 					nickName, account := telwStar, telwStar
+// 					if user.Nickname != "" {
+// 						nickName = user.Nickname
+// 					}
+// 					cTime := inviter.CreatedAt.Format("2006-01-02 15:04:05")
+// 					remark := "\n点击详情,可以看一看TA"
+// 					wx_mp.TmplmsgInviteSignUp(openId, url, title, nickName, account, cTime, remark)
+// 				}
+// 				//给新人发红包,并发通知
+// 				c := int64(user_model.BenefitNewUser)
+// 				s := balance_model.BALANCE_SOURCE_SIGN_UP_ONLINE
+// 				remark := balance_model.BALANCE_SOURCE_SIGN_UP_ONLINE_NAME
+// 				rId := ""
+// 				balance := new(balance_model.Balance).Create(user.Id, c, s, rId, remark, 1)
+// 				if balance != nil {
+// 					wxUser := user_model.GetWxUserByUserId(user.Id, true)
+// 					//发送红包的通知
+// 					if wxUser != nil {
+// 						openId := wxUser.MpOpenid
+// 						url := fmt.Sprintf("%s/v1/cfc/37", beego.AppConfig.String("ApiHost"))
+// 						first := "注册成功,返现金额已到账\n"
+// 						count := fmt.Sprintf("%0.2f 元", float64(c)/100.0)
+// 						//新用户,余额是0
+// 						leftAmount := c //balance_model.GetUserTotalBalance(user.Id)
+// 						balance := fmt.Sprintf("%0.2f 元", float64(leftAmount)/100.0)
+// 						ctime := time.Now().Format("2006-01-02 15:04:05")
+// 						remark := "\n点击立即提现>"
+// 						wx_mp.TmplmsgBalanceChange(openId, url, first, ctime, count, balance, remark)
+// 					}
+// 				}
+// 			}
+// 		}
+// 	}
+// }
+
+//检验是否需要关注
+//sceneId, 关注场景id
+func (self *BaseController) CheckWxUserSubscribe(sceneId int64) {
+	beego.BeeLogger.Info("check wx_user subscribe")
+	ctx := self.Ctx
+	if !isWxClient(ctx) {
+		return
+	}
+	wxUserId, ok := ctx.Input.Session(SessionWxUserKey).(int64)
+	if ok {
+		wxUser := user_model.GetWxUserById(wxUserId, false)
+		if wxUser == nil {
+			beego.BeeLogger.Error("WxUser Not Found, id=%d", wxUserId)
+		} else {
+			if wxUser.Subscribe == 0 {
+				qrcode := channel_gzh_qrcode_model.GetBySceneId(sceneId, true)
+				if qrcode == nil {
+					//默认二维码
+					qrcode = channel_gzh_qrcode_model.GetBySceneId(25, true)
+				}
+				qrcodeUrl := qrcode.QrcodeImg
+				errCode := &ErrCode{
+					Code:     WxUserNeedSubscribe[0],
+					Msg:      WxUserNeedSubscribe[1],
+					Redirect: qrcodeUrl,
+					Data:     nil,
+				}
+				ctx.Output.SetStatus(403)
+				ctx.Output.JSON(errCode, true, true)
+				self.StopRun()
+			}
+		}
+	} else {
+		beego.BeeLogger.Error("WxUser Not Login")
+	}
+}
+
+func checkWxUserLogin(ctx *context.Context) {
+	// TODO : 整个项目也应该更加适合采用 token 机制
+	beego.BeeLogger.Info("check wx_user login")
+	if !isWxClient(ctx) {
+		return
+	}
+	wxUserId, ok := ctx.Input.Session(SessionWxUserKey).(int64)
+	if ok {
+		_, ok := ctx.Input.Session(SessionUserKey).(int64)
+		if !ok {
+			// 如果微信用户登录了,且有绑定了user,则设置用户登录
+			wxUser := user_model.GetWxUserById(wxUserId, true)
+			beego.BeeLogger.Warn("wx_user has login, ", wxUser)
+			if wxUser != nil && int(wxUser.UserId) > 0 {
+				ctx.Output.Session(SessionUserKey, wxUser.UserId)
+			}
+		}
+	} else {
+		// beego.BeeLogger.Warn("---------------%v", ctx.Input.IsAjax())
+		// beego.BeeLogger.Warn("---------------%s", ctx.Input)
+		// if ctx.Input.IsAjax() {
+
+		beego.BeeLogger.Info("show the request : %s from UserAgent: %s", ctx.Input.URI(), ctx.Input.UserAgent())
+		uri := ctx.Input.URI()
+		//外链进来需要自动登录
+		if strings.Contains(uri, "/v1/cfc/") && isWxClient(ctx) {
+
+			// 微信用户自动登录
+			url := fmt.Sprintf("%s/login/mp?cb=%s%s",
+				beego.AppConfig.String("ApiHost"),
+				ctx.Input.Site(), ctx.Input.URI())
+			ctx.Redirect(302, url)
+			return
+
+		} else {
+			errCode := &ErrCode{
+				Code:     UserNeedLogin[0],
+				Msg:      UserNeedLogin[1],
+				Redirect: "",
+				Data:     nil,
+			}
+			ctx.Output.SetStatus(401)
+			ctx.Output.JSON(errCode, true, true)
+			return
+
+		}
+
+		// } else {
+		// // FIXME : 这里 IsAjax 判断是否有问题? 微信来的应该属于api请求
+		// beego.BeeLogger.Debug("WEIXIN user not login", ctx.Input)
+
+		// errCode := &ErrCode{
+		// 	Code:     UserNeedLogin[0], // FIXME : 这里应该 是 WxUserNeedLogin, 因为前端编码用了UserNeedLogin,以后一起修改
+		// 	Msg:      UserNeedLogin[1],
+		// 	Redirect: "",
+		// 	Data:     nil,
+		// }
+		// ctx.Output.SetStatus(401)
+		// ctx.Output.JSON(errCode, true, true)
+		// return
+
+		// beego.BeeLogger.Warn("this is not a ajax request!")
+		// 微信用户自动登录
+		// if isWxClient(ctx) {
+		// 	// beego.BeeLogger.Warn("---------------%s", ctx.Input.Site())
+		// 	url := fmt.Sprintf("%s/login/mp?cb=%s%s",
+		// 		beego.AppConfig.String("ApiHost"),
+		// 		ctx.Input.Site(), ctx.Input.URI())
+		// 	// beego.BeeLogger.Warn("auto login mp cb url=%s", url)
+		// 	ctx.Redirect(302, url)
+		// 	return
+		// }
+		// }
+	}
+}
+
+func checkUserLogin(ctx *context.Context) {
+	beego.BeeLogger.Info("check user login")
+	userId, ok := ctx.Input.Session(SessionUserKey).(int64)
+	if ok {
+		_, ok := ctx.Input.Session(SessionWxUserKey).(int64)
+		if !ok {
+			beego.BeeLogger.Info("user has login, try to set seesion[wx_user]")
+			// 如果用户登录了,且有绑定了wx_user,则设置微信用户登录
+			wxUser := user_model.GetWxUserByUserId(userId, true)
+			if wxUser != nil {
+				ctx.Output.Session(SessionWxUserKey, wxUser.Id)
+			}
+		}
+	} else {
+		_, ok := ctx.Input.Session(SessionWxUserKey).(int64)
+		if ok {
+			beego.BeeLogger.Info("user not login, wx_user is login,and try to ask user binding tel.")
+			// 如果用户没登录了,但采用微信账户登录,则需要微信用户绑定手机从而创建用户记录
+			errCode := &ErrCode{
+				Code:     UserNeedTel[0],
+				Msg:      UserNeedTel[1],
+				Redirect: fmt.Sprintf("%s/user/binding/state", beego.AppConfig.String("WWWHost")),
+				Data:     nil,
+			}
+			ctx.Output.SetStatus(401)
+			ctx.Output.JSON(errCode, true, true)
+			return
+		}
+		errCode := &ErrCode{
+			Code:     UserNeedLogin[0],
+			Msg:      UserNeedLogin[1],
+			Redirect: "",
+			Data:     nil,
+		}
+		ctx.Output.SetStatus(401)
+		ctx.Output.JSON(errCode, true, true)
+		return
+	}
+}
+
+//检查来自大后台的请求是否为服务器IP
+func CheckIsInvokeFromRailsAdmin(ctx *context.Context) {
+	s := strings.Split(ctx.Request.RemoteAddr, ":")
+	ip := s[0]
+	beego.BeeLogger.Debug("railsadmin request ip is: %s", ip)
+	if ip != beego.AppConfig.String("RailsAdminAddr") {
+		errCode := &ErrCode{
+			Code:     RailAdminIPReduced[0],
+			Msg:      RailAdminIPReduced[1],
+			Redirect: "",
+			Data:     nil,
+		}
+		ctx.Output.SetStatus(403)
+		ctx.Output.JSON(errCode, true, true)
+	}
+}
+
+func (self *BaseController) Prepare() {
+	beego.BeeLogger.Info("invote controller Prepare func")
+	needChkWxUserLogin := true
+	needChkUserLogin := true
+	// needChkWxUserSubscribe := false
+	_, actionName := self.GetControllerAndAction()
+
+	if len(self.ExceptCheckWxUserLoginAction) > 0 {
+		for _, an := range self.ExceptCheckWxUserLoginAction {
+			if strings.ToLower(an) == "*" || strings.ToLower(an) == strings.ToLower(actionName) {
+				needChkWxUserLogin = false
+				break
+			}
+		}
+	}
+	if len(self.ExceptCheckUserLoginAction) > 0 {
+		for _, an := range self.ExceptCheckUserLoginAction {
+			if strings.ToLower(an) == "*" || strings.ToLower(an) == strings.ToLower(actionName) {
+				needChkUserLogin = false
+				break
+			}
+		}
+	}
+
+	if needChkWxUserLogin {
+		checkWxUserLogin(self.Ctx)
+	}
+	if needChkUserLogin {
+		checkUserLogin(self.Ctx)
+	}
+
+}
+
+type ErrCode struct {
+	Status   int               `json:"status"`
+	Code     string            `json:"err_code"`
+	Msg      string            `json:"err_msg"`
+	Redirect string            `json:"redirect_to"`
+	Data     map[string]string `json:"data"`
+}
+
+//定义返回错误信息
+func (self *BaseController) ReturnError(status int, code []string, redirect string, data map[string]string) {
+	errCode := &ErrCode{
+		Status:   status,
+		Code:     code[0],
+		Msg:      code[1],
+		Redirect: redirect,
+		Data:     data,
+	}
+	// jsonErrCode, err := json.Marshal(errCode)
+	// if err != nil {
+	// 	beego.BeeLogger.Error("json encode error=[%s]", err)
+	// }
+	self.Ctx.Output.SetStatus(status)
+	// self.Ctx.Output.Header("Content-Type", "application/json; charset=utf-8")
+	self.Ctx.Output.JSON(errCode, true, true)
+	self.StopRun()
+	// self.ServeJson(status, string(jsonErrCode))
+}
+
+//TODO校验是否微信客户端
+func (self *BaseController) IsWxClient() bool {
+	return isWxClient(self.Ctx)
+}
+
+//TODO校验是否微信客户端
+func isWxClient(ctx *context.Context) bool {
+	agent := ctx.Input.UserAgent()
+	b := getRealBrowser(agent)
+	isWxClient := b == "wx"
+	return isWxClient
+}
+
+// 是否是手机端
+func (self *BaseController) IsMobile() bool {
+	ua := strings.ToLower(self.Ctx.Input.UserAgent())
+	if ua == "" {
+		return true
+	}
+	ok, _ := regexp.MatchString("iphone|nokia|sony|ericsson|mot|samsung|sgh|lg|philips|panasonic|alcatel|lenovo|cldc|midp|wap|mobile", ua)
+	return ok
+}
+
+// 是否是开发模式
+func (self *BaseController) IsDev() bool {
+	//return beego.BConfig.RunMode == beego.DEV
+	return beego.AppConfig.String("Env") != "production"
+}
+
+func getRealBrowser(ua string) string {
+	userAgent := user_agent.New(ua)
+	browser, _ := userAgent.Browser()
+	browser = strings.ToLower(browser)
+	if browser == "internet explorer" {
+		browser = "ie"
+	}
+	lowerUa := strings.ToLower(ua)
+	// beego.BeeLogger.Warning("ua=%s", lowerUa)
+	if strings.Contains(lowerUa, "micromessenger") {
+		browser = "wx"
+	}
+	if strings.Contains(lowerUa, "wechatdevtools") {
+		browser = "wx"
+	}
+	return browser
+}
+
+func (self *BaseController) GetFullImgUrl(img string) string {
+	if img == "" {
+		return ""
+	}
+	if strings.HasPrefix(img, "http://") || strings.HasPrefix(img, "https://") {
+		return img
+	} else {
+		return fmt.Sprintf("%s/%s", beego.AppConfig.String("AliCDNImgHost"), img)
+	}
+}
+
+func (self *BaseController) GetCdnFullImgUrl(img string) string {
+	if img == "" {
+		return ""
+	}
+	if strings.HasPrefix(img, "http://") || strings.HasPrefix(img, "https://") {
+		return img
+	} else {
+		return fmt.Sprintf("%s/%s", beego.AppConfig.String("AliCDNImgHost"), img)
+	}
+}

+ 182 - 0
go/gopath/src/fohow.com/apps/models/ad_model/ad.go

@@ -0,0 +1,182 @@
+package ad_model
+
+import (
+	"fmt"
+	// "strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	"fohow.com/cache"
+)
+
+const (
+	ad_positions_tablename = "ad_positions"
+	ad_items_tablename     = "ad_items"
+)
+
+type AdPosition struct {
+	Id         int64     `orm:"column(id);pk"                                  json:"id"`          // int(11)
+	Code       string    `orm:"column(code)"                                   json:"code"`        // varchar(255)
+	Name       string    `orm:"column(name)"                                   json:"name"`        // varchar(255)
+	Remark     string    `orm:"column(remark)"                                 json:"remark"`      // varchar(255)
+	ClickTimes int64     `orm:"column(click_times);null"                       json:"click_times"` // int(11)
+	Img        string    `orm:"column(img)"                                    json:"img"`         // text
+	ClickUrl   string    `orm:"column(click_url);null"                         json:"click_url"`   // text
+	State      int64     `orm:"column(state);null"                             json:"state"`       // tinyint(1)
+	CreatedAt  time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"-"`           // datetime
+	UpdatedAt  time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"-"`           // datetime
+}
+
+type AdItem struct {
+	Id           int64     `orm:"column(id);pk"                                  json:"id"`        // int(11)
+	AdPositionId int64     `orm:"column(ad_position_id)"                         json:"-"`         // int(11)
+	Name         string    `orm:"column(name)"                                   json:"name"`      // varchar(255)
+	Img          string    `orm:"column(img)"                                    json:"img"`       // text
+	ClickUrl     string    `orm:"column(click_url);null"                         json:"click_url"` // text
+	Url          string    `orm:"column(url)"        json:"url"`                                   // varchar(4096)
+	UrlType      int64     `orm:"column(url_type)"   json:"url_type"`                              //tinyint(4)
+	ClickTimes   int64     `orm:"column(click_times);null"                       json:"-"`         // int(11)
+	ShowTimes    int64     `orm:"column(show_times);null"                        json:"-"`         // int(11)
+	ExpiredAt    time.Time `orm:"column(expired_at);null;type(datetime)"         json:"-"`         // datetime
+	State        int64     `orm:"column(state);null"                             json:"-"`         // tinyint(1)
+	Sort         int64     `orm:"column(sort);null"                              json:"-"`         // int(11)
+	CreatedAt    time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"-"`         // datetime
+	UpdatedAt    time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"-"`         // datetime
+}
+
+func (self *AdItem) TableName() string {
+	return ad_items_tablename
+}
+
+func (self *AdPosition) TableName() string {
+	return ad_positions_tablename
+}
+
+// //获取砍价活动show、order页面上面的广告图
+// func GetAdsOfActivityBanner(code, dailishop, bannerDdefault string) (ads []*AdItem) {
+// 	codes := []string{code, dailishop, bannerDdefault}
+// 	sql := fmt.Sprintf("SELECT * FROM `%s` WHERE `ad_position_id` IN (SELECT id FROM `%s` WHERE code IN (?,?,?) AND `state` = 1) AND `state` = 1 AND expired_at > ? ORDER BY sort desc, created_at desc", new(AdItem).TableName(), new(AdPosition).TableName())
+// 	if i, err := orm.NewOrm().Raw(sql, codes, time.Now()).QueryRows(&ads); err != nil {
+// 		beego.BeeLogger.Error("get ad items err=[%s]", err)
+// 	} else {
+// 		if i <= 0 {
+// 			beego.BeeLogger.Info("get ad items zero")
+// 		}
+// 	}
+// 	return ads
+// }
+
+//获取广告图
+func GetAdsByCode(code string, useCache bool) (ads []*AdItem) {
+	k := fmt.Sprintf("ad_model.GetAdsByCode(%d)", code)
+	if useCache {
+		if ret, ok := cache.Cache.Get(k).([]*AdItem); ok {
+			return ret
+		} else {
+			beego.BeeLogger.Info("get ad items by code=[%s] from cache.cache key=[%s]", code, k)
+		}
+	}
+
+	sql := fmt.Sprintf("SELECT * FROM `%s` WHERE `ad_position_id` IN (SELECT id FROM `%s` WHERE code = ? AND `state` = 1) AND (`state` = 1 or expired_at > ?) ORDER BY sort desc, created_at desc", new(AdItem).TableName(), new(AdPosition).TableName())
+
+	if i, err := orm.NewOrm().Raw(sql, code, time.Now()).QueryRows(&ads); err != nil {
+		beego.BeeLogger.Error("get ad items by code err=[%s]", err)
+	} else {
+		if i <= 0 {
+			beego.BeeLogger.Info("get ad items by code zero")
+		}
+	}
+
+	for _, item := range ads {
+		item.ClickUrl = fmt.Sprintf("%s/v1/ad/%d/click", beego.AppConfig.String("ApiHost"), item.Id)
+		item.Img = GetFullImgUrl(item.Img)
+	}
+
+	cache.Cache.Put(k, ads, 10*time.Minute)
+	return ads
+}
+
+//获取广告图byId
+func GetAdsByPosId(id int64, useCache bool) (ads []*AdItem) {
+	k := fmt.Sprintf("ad_model.GetAdsByPosId(%d)", id)
+	if useCache {
+		if ret, ok := cache.Cache.Get(k).([]*AdItem); ok {
+			return ret
+		}
+	}
+
+	sql := fmt.Sprintf("SELECT * FROM `%s` WHERE `ad_position_id` =? AND (`state` = 1 or expired_at > ?) ORDER BY sort desc, created_at desc", new(AdItem).TableName())
+
+	if i, err := orm.NewOrm().Raw(sql, id, time.Now()).QueryRows(&ads); err != nil {
+		beego.BeeLogger.Error("get ad items by code err=[%s]", err)
+	} else {
+		if i <= 0 {
+			beego.BeeLogger.Info("get ad items by code zero")
+		}
+	}
+
+	for _, item := range ads {
+		item.ClickUrl = fmt.Sprintf("%s/v1/ad/%d/click", beego.AppConfig.String("ApiHost"), item.Id)
+		item.Img = GetFullImgUrl(item.Img)
+	}
+
+	cache.Cache.Put(k, ads, 10*time.Minute)
+	return ads
+}
+
+//获取广告位
+func GetPostionById(id int64, useCache bool) *AdPosition {
+	k := fmt.Sprintf("ad_model.GetPostionById.(%d)", id)
+	if useCache {
+		if ret, ok := cache.Cache.Get(k).(*AdPosition); ok {
+			return ret
+		}
+	}
+	item := new(AdPosition)
+	if err := orm.NewOrm().QueryTable(item).Filter("id", id).One(item); err != nil {
+		beego.BeeLogger.Error("get ad item  by id=[%d] err=[%s]", id, err)
+		return nil
+	}
+
+	cache.Cache.Put(k, item, 5*time.Minute)
+	return item
+}
+
+//获取单条广告图信息
+func GetItemById(id int64, useCache bool) *AdItem {
+	k := fmt.Sprintf("GetItemById_%d", id)
+	if useCache {
+		if ret, ok := cache.Cache.Get(k).(*AdItem); ok {
+			return ret
+		} else {
+			beego.BeeLogger.Info("get ad item  from cache.cache key=[%s]", k)
+		}
+	}
+	item := new(AdItem)
+	if err := orm.NewOrm().QueryTable(item).Filter("id", id).One(item); err != nil {
+		beego.BeeLogger.Error("get ad item  by id=[%d] err=[%s]", id, err)
+		return nil
+	}
+	if useCache {
+		if err := cache.Cache.Put(k, item, 5*time.Minute); err != nil {
+			beego.BeeLogger.Error("set ad item err=[%s]", err)
+		}
+	}
+	return item
+}
+
+func (self *AdItem) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("SaveAdItem id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}
+
+func GetFullImgUrl(img string) string {
+	if img == "" {
+		return ""
+	}
+	return fmt.Sprintf("%s/%s", beego.AppConfig.String("AliOssImgHost"), img)
+}

+ 10 - 0
go/gopath/src/fohow.com/apps/models/ad_model/init.go

@@ -0,0 +1,10 @@
+package ad_model
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(new(AdPosition), new(AdItem),
+		new(AdItemShowStat), new(AdItemClickStat))
+}

+ 118 - 0
go/gopath/src/fohow.com/apps/models/ad_model/statistic.go

@@ -0,0 +1,118 @@
+package ad_model
+
+import (
+	// "fmt"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
+
+const (
+	ad_item_show_statistics_tablename  = "ad_item_show_statistics"
+	ad_item_click_statistics_tablename = "ad_item_click_statistics"
+)
+
+type AdItemShowStat struct {
+	Id           int64     `orm:"column(id);pk"                                       json:"id"`             // int(11)
+	AdItemId     int64     `orm:"column(ad_item_id)"                                  json:"ad_item_id"`     // int(11)
+	WxUId        int64     `orm:"column(wx_uid)"                                      json:"wx_uid"`         // int(11)
+	Ip           string    `orm:"column(ip);null"                                     json:"ip"`             // varchar(32)
+	ShowTimes    int64     `orm:"column(show_times)"                                  json:"show_times"`     // int(11)
+	ShowLastTime time.Time `orm:"column(show_last_time);null;type(datetime)"          json:"show_last_time"` // datetime
+	CreatedAt    time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)" json:"created_at"`     // datetime
+	UpdatedAt    time.Time `orm:"column(updated_at);null;auto_now;type(datetime)"     json:"updated_at"`     // datetime
+}
+
+type AdItemClickStat struct {
+	Id            int64     `orm:"column(id);pk"                                       json:"id"`               // int(11)
+	AdItemId      int64     `orm:"column(ad_item_id)"                                  json:"ad_item_id"`       // int(11)
+	WxUId         int64     `orm:"column(wx_uid)"                                      json:"wx_uid"`           // int(11)
+	Ip            string    `orm:"column(ip);null"                                     json:"ip"`               // varchar(32)
+	ClickTimes    int64     `orm:"column(click_times)"                                  json:"click_times"`     // int(11)
+	ClickLastTime time.Time `orm:"column(click_last_time);null;type(datetime)"          json:"click_last_time"` // datetime
+	CreatedAt     time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)" json:"created_at"`       // datetime
+	UpdatedAt     time.Time `orm:"column(updated_at);null;auto_now;type(datetime)"     json:"updated_at"`       // datetime
+}
+
+func (self *AdItemShowStat) TableName() string {
+	return ad_item_show_statistics_tablename
+}
+
+func (self *AdItemClickStat) TableName() string {
+	return ad_item_click_statistics_tablename
+}
+
+//创建展示统计项
+func (self *AdItemShowStat) Create(adId, wxUId int64, ip string) *AdItemShowStat {
+	item := &AdItemShowStat{
+		AdItemId:     adId,
+		WxUId:        wxUId,
+		Ip:           ip,
+		ShowTimes:    1,
+		ShowLastTime: time.Now(),
+	}
+	id, err := orm.NewOrm().Insert(item)
+	if err != nil {
+		beego.BeeLogger.Error("insert AdItemShowStat err=[%s]", err)
+		return nil
+	}
+	item.Id = id
+	return item
+}
+
+//创建点击统计项
+func (self *AdItemClickStat) Create(adId, wxUId int64, ip string) *AdItemClickStat {
+	item := &AdItemClickStat{
+		AdItemId:      adId,
+		WxUId:         wxUId,
+		Ip:            ip,
+		ClickTimes:    1,
+		ClickLastTime: time.Now(),
+	}
+	id, err := orm.NewOrm().Insert(item)
+	if err != nil {
+		beego.BeeLogger.Error("insert AdItemClickStat err=[%s]", err)
+		return nil
+	}
+	item.Id = id
+	return item
+}
+
+func (self *AdItemShowStat) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save AdItemShowStat id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}
+
+func (self *AdItemClickStat) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save AdItemClickStat id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}
+
+func GetShowStatByWxUId(adItemId, wxUId int64) *AdItemShowStat {
+	item := new(AdItemShowStat)
+	o := orm.NewOrm()
+	err := o.QueryTable(item).Filter("ad_item_id", adItemId).Filter("wx_uid", wxUId).
+		Limit(1).One(item)
+	if err != nil {
+		return nil
+	}
+	return item
+}
+
+func GetClickStatByWxUId(adItemId, wxUId int64) *AdItemClickStat {
+	item := new(AdItemClickStat)
+	o := orm.NewOrm()
+	err := o.QueryTable(item).Filter("ad_item_id", adItemId).Filter("wx_uid", wxUId).
+		Limit(1).One(item)
+	if err != nil {
+		return nil
+	}
+	return item
+}

+ 157 - 0
go/gopath/src/fohow.com/apps/models/address_model/address.go

@@ -0,0 +1,157 @@
+package address_model
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
+
+const (
+	addresses_tablename = "addresses"
+)
+
+type Address struct {
+	Id        int64     `orm:"column(id);pk"                                  json:"id"`         // int(11)
+	WxUserId  int64     `orm:"column(wx_user_id);null"                        json:"wx_user_id"` // int(11)
+	UserId    int64     `orm:"column(user_id);null"                           json:"user_id"`    // int(11)
+	Contact   string    `orm:"column(contact);null"                           json:"contact"`    // varchar(255)
+	Tel       string    `orm:"column(tel);null"                               json:"tel"`        // varchar(255)
+	Address   string    `orm:"column(address);null"                           json:"address"`    // varchar(255)
+	Remark    string    `orm:"column(remark);null"                            json:"remark"`     // text
+	State     int64     `orm:"column(state);null"                             json:"state"`      // tinyint(1)
+	CreatedAt time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"-"`          // datetime
+	UpdatedAt time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"-"`          // datetime
+	Province  string    `orm:"column(province);null"                          json:"province"`   // varchar(10)
+	City      string    `orm:"column(city);null"                              json:"city"`       // varchar(10)
+	Postcode  string    `orm:"column(postcode);null"                          json:"postcode"`   // varchar(20)
+	District  string    `orm:"column(district);null"                          json:"district"`   // varchar(20)
+}
+
+func (self *Address) TableName() string {
+	return addresses_tablename
+}
+
+func (self *Address) SetDefault() *Address {
+	addresses := GetAddressesByWxUId(self.WxUserId)
+	for _, address := range addresses {
+		if address.Id == self.Id {
+			address.State = 1
+		} else {
+			address.State = 0
+		}
+		go address.Save()
+	}
+	return self
+}
+
+func GetUserAddressById(addressId int64) *Address {
+	address := &Address{}
+	if err := orm.NewOrm().QueryTable(address).Filter("id", addressId).Limit(1).
+		One(address); err != nil {
+		beego.BeeLogger.Error("get address by id=[%s] err=[%s]", addressId, err)
+		return nil
+	}
+	return address
+}
+
+// 找出微信用户的默认地址
+func GetUserDefaultAddress(wxUId int64) *Address {
+	item := &Address{}
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("wx_user_id", wxUId).OrderBy("-state").
+		Limit(1).One(item); err != nil {
+		// beego.BeeLogger.Error("get user default address user_id=[%d] err=[%s]", uId, err)
+		return nil
+	}
+	return item
+}
+
+// 找出用户的所有地址
+func GetAddressesByWxUId(wxUId int64) (items []*Address) {
+	o := orm.NewOrm()
+	_, err := o.QueryTable(new(Address)).
+		Filter("wx_user_id", wxUId).All(&items)
+	if err != nil {
+		beego.BeeLogger.Error("GetAddressesByWxUId err=%s", wxUId, err)
+		return nil
+	}
+	return items
+}
+
+//根据userid, id找地址
+func GetAddressByWxUIdAndId(wxUId, id int64) *Address {
+	item := &Address{}
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("wx_user_id", wxUId).Filter("id", id).
+		Limit(1).One(item); err != nil {
+		beego.BeeLogger.Error("get address wx_user_id=[%d] id=[%d] err=[%s]", wxUId, id, err)
+		return nil
+	}
+	return item
+}
+
+func GetUserAddressList(wxUId int64, sort string) (items []*Address) {
+	o := orm.NewOrm()
+	orderBy := fmt.Sprintf("-%s", sort)
+	_, err := o.QueryTable(new(Address)).
+		Filter("wx_user_id", wxUId).OrderBy(orderBy).All(&items)
+	if err != nil {
+		beego.BeeLogger.Error("GetUserAddressList err=%s", wxUId, err)
+		return nil
+	}
+	return items
+}
+
+func CreateAddress(wxUId, uId int64, contact, tel, province, city, address, remark, postcode, district string) *Address {
+	item := &Address{
+		WxUserId: wxUId,
+		UserId:   uId,
+		Contact:  contact,
+		Tel:      tel,
+		Province: province,
+		City:     city,
+		Postcode: postcode,
+		District: district,
+		Address:  address,
+		Remark:   remark}
+	id, err := orm.NewOrm().Insert(item)
+	if err != nil {
+		beego.BeeLogger.Error("insert Address err=[%s]", err)
+		return nil
+	}
+	item.Id = id
+	return item
+}
+
+func (self *Address) Update(contact, tel, province, city, address, remark, postcode, district string) *Address {
+	self.Contact = contact
+	self.Tel = tel
+	self.Province = province
+	self.City = city
+	self.Address = address
+	self.Remark = remark
+	self.Postcode = postcode
+	self.District = district
+	self.Save()
+	return self
+
+}
+
+func (self *Address) Delete() error {
+	o := orm.NewOrm()
+	if _, err := o.Delete(&Address{Id: self.Id}); err != nil {
+		beego.BeeLogger.Error("Delete Address id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}
+
+func (self *Address) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save Address id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}

+ 9 - 0
go/gopath/src/fohow.com/apps/models/address_model/init.go

@@ -0,0 +1,9 @@
+package address_model
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(new(Address))
+}

+ 391 - 0
go/gopath/src/fohow.com/apps/models/article_model/article.go

@@ -0,0 +1,391 @@
+package article_model
+
+import (
+	"fmt"
+	// "strings"
+	// "strconv"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	// "fohow.com/apps/shop"
+	// "fohow.com/cache"
+	// "fohow.com/apps/models/user_model"
+	// "fohow.com/apps/models/vas_model"
+	"fohow.com/cache"
+	// "fohow.com/libs/wx_mp"
+)
+
+const (
+	articles_tablename = "articles"
+)
+
+type Article struct {
+	Id           int64         `orm:"column(id);pk"                                  json:"id"`             // int(11)
+	Title        string        `orm:"column(title)"                                  json:"title"`          // varchar(100)
+	Subtitle     string        `orm:"column(subtitle);null"                          json:"subtitle"`       // varchar(100)
+	About        string        `orm:"column(about);null"                             json:"about"`          // tinytext
+	Content      string        `orm:"column(content)"                                json:"content"`        // text
+	Url          string        `orm:"column(url);null"                               json:"url"`            // text
+	STitle       string        `orm:"column(s_title);null"                           json:"s_title"`        // varchar(255)
+	Cover        string        `orm:"column(cover);null"                             json:"cover"`          // varchar(255)
+	SImg         string        `orm:"column(s_img);null"                             json:"s_img"`          // varchar(255)
+	Source       string        `orm:"column(source);null"                            json:"source"`         // varchar(100)
+	Editor       string        `orm:"column(editor);null"                            json:"editor"`         // varchar(100)
+	Click        int64         `orm:"column(click);null"                             json:"click"`          // int(11)
+	Sort         int64         `orm:"column(sort);null"                              json:"sort"`           // int(11)
+	State        int64         `orm:"column(state);null"                             json:"state"`          // tinyint(1)
+	CreatedBy    string        `orm:"column(created_by);null"                        json:"created_by"`     // varchar(100)
+	ArticleCatId int64         `orm:"column(article_cat_id)"                         json:"article_cat_id"` // int(11)
+	ArticleCat   []*ArticleCat `orm:"-"                         json:"article_cat"`                         // int(11)
+	Agree        int64         `orm:"column(agree);null"                             json:"agree"`          // int(11)
+	Disagree     int64         `orm:"column(disagree);null"                          json:"disagree"`       // int(11)
+	DisplayCover int64         `orm:"column(display_cover);null"                     json:"display_cover"`  // tinyint(1)
+	Tags         string        `orm:"column(tags);null"                              json:"tags"`           // varchar(255)
+	// //用于文章详情翻页,记录当前缓存数组中的位置,不用int型是因为默认值的问题
+	// CurrentIdx string    `orm:"-"                              json:"current_index"`              // varchar(255)
+	Recommend  int64  `orm:"column(recommend);null"                         json:"recommend"`   // int(11)
+	CTime      int64  `orm:"-"                         json:"ctime"`                            // int(11)
+	SeoTitle   string `orm:"column(seo_title);null"                         json:"seo_title"`   // varchar(255)
+	SeoKeyword string `orm:"column(seo_keyword);null"                       json:"seo_keyword"` // varchar(255)
+	SeoDesc    string `orm:"column(seo_desc);null"                          json:"seo_desc"`    // varchar(255)
+	// 属性列不存在于数据库中
+	// ShareBenefitMin        int64  `orm:"column(share_benefit_min);null"                     json:"-"`                     // tinyint(1)
+	// ShareBenefitMax        int64  `orm:"column(share_benefit_max);null"                     json:"-"`                     // tinyint(1)
+	// ShareBenefitMustFollow int64  `orm:"column(share_benefit_must_follow);null"                     json:"-"`             // tinyint(1)
+	// BenefitMax             int64  `orm:"column(benefit_max);null"                       json:"benefit_max"`               // int(11)
+	// SingleBenefitMax       int64  `orm:"column(single_benefit_max);null"                json:"-"`                         // int(11)
+	// SameIpLimit            int64  `orm:"column(same_ip_limit);null"                     json:"-"`                         // int(11)
+	UrlGuide         string `orm:"column(url_guide)"                                     json:"url_guide"`          // text
+	ShareBenefitDesc string `orm:"column(share_benefit_desc)"                            json:"share_benefit_desc"` // text
+	//打开时间
+	OTime        int64     `orm:"-"                     json:"otime"`                                   // tinyint(1)
+	ShowCsQrcode int64     `orm:"column(show_cs_qrcode);null"                    json:"show_cs_qrcode"` // tinyint(1)
+	CreatedAt    time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"-"`              // datetime
+	UpdatedAt    time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"-"`              // datetime
+}
+
+func (self *Article) TableName() string {
+	return articles_tablename
+}
+
+//获取文章详情
+func GetById(id int64, useCache bool) *Article {
+	k := fmt.Sprintf("article_model.GetById(%d)", id)
+	beego.BeeLogger.Info("article_id:(%d)", id)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).(*Article); ok {
+			return s
+		}
+	}
+	item := new(Article)
+	o := orm.NewOrm()
+	if err := o.QueryTable(item).Filter("state", 1).Filter("id", id).Limit(1).One(item); err != nil {
+		beego.BeeLogger.Error("GetById id:%d, err:%s", id, err)
+		return nil
+	}
+	cache.Cache.Put(k, item, 10*time.Minute)
+	return item
+}
+
+//获取文章详情
+func GetLastestBenefitArticleByCatId(catId int64, useCache bool) *Article {
+	k := fmt.Sprintf("article_model.GetLastestBenefitArticleByCatId(%d)", catId)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).(*Article); ok {
+			return s
+		}
+	}
+	item := new(Article)
+	o := orm.NewOrm()
+	if err := o.QueryTable(item).Filter("article_cat_id", catId).
+		Exclude("share_benefit_max", 0).OrderBy("-recommend").
+		Limit(1).One(item); err != nil {
+		return nil
+	}
+	cache.Cache.Put(k, item, 10*time.Minute)
+	return item
+}
+
+func (self *Article) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save article id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}
+
+//文章列表
+func GetListByCatId(catId, page, perPage int64, useCache bool) (articleList []*Article) {
+	k := fmt.Sprintf("article_model.GetListByCatId(%d).page(%d).perPage(%d)", catId, page, perPage)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).([]*Article); ok {
+			return s
+		}
+	}
+
+	article := new(Article)
+	qs := orm.NewOrm().QueryTable(article)
+	qs = qs.Filter("article_cat_id", catId)
+	qs = qs.Filter("state", 1)
+
+	if _, err := qs.OrderBy("-created_at").
+		Limit(perPage, (page-1)*perPage).All(&articleList); err != nil {
+		beego.BeeLogger.Debug("get articles with catId=%d page=%d perPage=%d, err=[%s]", catId, page, perPage, err)
+		return nil
+	}
+	for _, item := range articleList {
+		item.CTime = item.CreatedAt.Unix()
+		item.Cover = GetFullImgUrl(item.Cover)
+		if item.ArticleCatId != 15 {
+			item.Content = ""
+		}
+	}
+	cache.Cache.Put(k, articleList, 10*time.Minute)
+	return articleList
+}
+
+//文章列表总条数
+func GetListCountByCatId(catId int64) int64 {
+	item := new(Article)
+	o := orm.NewOrm()
+	count, _ := o.QueryTable(item).Filter("article_cat_id", catId).Filter("state", 1).Count()
+	return count
+}
+
+//热门文章,取行业资讯、平台资讯内,推荐指数前四篇
+func Hotest(useCache bool) (list []*Article) {
+	k := fmt.Sprintf("article_model.Hotest")
+	if useCache {
+		if s, ok := cache.Cache.Get(k).([]*Article); ok {
+			return s
+		}
+	}
+
+	catIds := []int64{12, 13}
+	article := new(Article)
+	qs := orm.NewOrm().QueryTable(article)
+	qs = qs.Filter("article_cat_id__in", catIds)
+	qs = qs.Filter("state", 1)
+	if _, err := qs.OrderBy("-sort").OrderBy("-created_at").
+		Limit(4).All(&list); err != nil {
+		beego.BeeLogger.Debug("get hotest articles, err=[%s]", err)
+		return nil
+	}
+
+	for _, article := range list {
+		article.Content = ""
+		article.CTime = article.CreatedAt.Unix()
+		article.Cover = GetFullImgUrl(article.Cover)
+	}
+
+	cache.Cache.Put(k, list, 10*time.Minute)
+
+	return list
+}
+
+//通知类的文章
+func Notices(cId, n int64, useCache bool) (list []*Article) {
+	k := fmt.Sprintf("article_model.Notices.cId(%d).count(%d)", n)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).([]*Article); ok {
+			return s
+		}
+	}
+
+	article := new(Article)
+	qs := orm.NewOrm().QueryTable(article)
+	qs = qs.Filter("article_cat_id", cId)
+	qs = qs.Filter("state", 1)
+	if _, err := qs.OrderBy("-recommend").OrderBy("-created_at").
+		Limit(n).All(&list); err != nil {
+		beego.BeeLogger.Debug("get notice articles, err=[%s]", err)
+		return nil
+	}
+
+	for _, article := range list {
+		article.Content = ""
+	}
+
+	cache.Cache.Put(k, list, 10*time.Minute)
+
+	return list
+}
+
+func GetRepleyArticle(sId, cId int64, content string) *Article {
+	item := new(Article)
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("category_id", cId).Filter("shop_id", sId).
+		Filter("tags__contains", content).Limit(1).
+		One(item); err != nil {
+		return nil
+	}
+	return item
+}
+
+//增加文章详情页阅读数
+func (self *Article) AddClick() {
+	art := GetById(self.Id, false)
+	if art != nil {
+		art.Click = art.Click + 1
+		art.Save()
+	}
+}
+
+//增加文章详情阅读数,通过文章ID
+func AddClickById(id int64) {
+	art := GetById(id, false)
+	if art != nil {
+		art.Click = art.Click + 1
+		art.Save()
+	}
+}
+
+//获取前一条、后一条article id, 用于文章详情翻页
+func (self *Article) GetArtcleIdForDetailPage(useCache bool) (preOne, nextOne *Article) {
+	if self == nil {
+		return nil, nil
+	}
+	k := fmt.Sprintf("article_model.GetArtcleIdForDetailPage.catId[%d].AllValidArticles", self.ArticleCatId)
+	var articles []*Article
+	if useCache {
+		if s, ok := cache.Cache.Get(k).([]*Article); ok {
+			articles = s
+		}
+	}
+	if articles == nil {
+		// article := new(Article)
+		// qs := orm.NewOrm().QueryTable(article)
+		// qs = qs.Filter("state", 1).Filter("article_cat_id", self.ArticleCatId)
+
+		// if _, err := qs.OrderBy("id").All(&articles); err != nil {
+		// 	beego.BeeLogger.Debug("get GetArtcleIdForDetailPage articleCatId=[%d] err=[%s]", self.ArticleCatId, err)
+		// }
+		sql := fmt.Sprintf("SELECT * FROM %s WHERE state = ? and article_cat_id=?", articles_tablename)
+		if _, err := orm.NewOrm().Raw(sql, 1, self.ArticleCatId).QueryRows(&articles); err != nil {
+			beego.BeeLogger.Debug("get GetArtcleIdForDetailPage articleCatId=[%d] err=[%s]", self.ArticleCatId, err)
+		}
+		cache.Cache.Put(k, articles, 10*time.Minute)
+	}
+
+	length := len(articles)
+
+	//仅有一篇
+	if length <= 1 {
+		return nil, nil
+	}
+
+	lastIndex := length - 1
+	for i, item := range articles {
+		if item.Id == self.Id {
+			switch i {
+			case 0:
+				// preOne = articles[lastIndex]
+				nextOne = articles[1]
+			case lastIndex:
+				preOne = articles[lastIndex-1]
+				// nextOne = articles[0]
+			default:
+				preOne = articles[i-1]
+				nextOne = articles[i+1]
+			}
+			break
+		}
+	}
+
+	// lastIndex := length - 1
+	// index, _ := strconv.Atoi(self.CurrentIdx)
+
+	// switch index {
+	// case 0:
+	// 	preOne = articles[lastIndex]
+	// 	nextOne = articles[1]
+	// case lastIndex:
+	// 	preOne = articles[lastIndex-1]
+	// 	nextOne = articles[0]
+	// default:
+	// 	preOne = articles[index-1]
+	// 	nextOne = articles[index+1]
+	// }
+
+	return preOne, nextOne
+}
+
+// //新增评论记录
+// func (self *Comment) Create(tType string, tId int64, parent, uId int64, content string) (item *Comment) {
+// 	item = &Comment{
+// 		Content:         content,
+// 		CreatedBy:       uId,
+// 		CommentableId:   tId,
+// 		CommentableType: tType,
+// 		State:           0,
+// 		Parent:          parent}
+// 	id, err := orm.NewOrm().Insert(item)
+// 	if err != nil {
+// 		beego.BeeLogger.Error("Insert Comment err=[%s]", err)
+// 		return nil
+// 	}
+// 	item.Id = id
+// 	return item
+// }
+
+// // 获取评论列表
+// func GetList(tType string, tId, parent, state int64, page, perPage int) (items []*Comment) {
+// 	item := new(Comment)
+// 	qs := orm.NewOrm().QueryTable(item)
+// 	qs = qs.Filter("commentable_id", tId)
+// 	qs = qs.Filter("commentable_type", tType)
+// 	if state != -1 {
+// 		qs = qs.Filter("state", state)
+// 	}
+// 	if parent != -1 {
+// 		qs = qs.Filter("parent", parent)
+// 	}
+// 	if _, err := qs.OrderBy("-created_at").
+// 		Limit(perPage, (page-1)*perPage).All(&items); err != nil {
+// 		return nil
+// 	}
+// 	return items
+// }
+
+// // 获取评论的回复记录
+// func GetSubList(parent int64, page, perPage int) (items []*Comment) {
+// 	item := new(Comment)
+// 	qs := orm.NewOrm().QueryTable(item)
+// 	if parent != -1 {
+// 		qs = qs.Filter("parent", parent)
+// 	}
+// 	if _, err := qs.OrderBy("-created_at").
+// 		Limit(perPage, (page-1)*perPage).All(&items); err != nil {
+// 		return nil
+// 	}
+// 	return items
+// }
+
+// func GetCount(tType string, tId int64) int64 {
+// 	o := orm.NewOrm()
+// 	i, err := o.QueryTable(new(Comment)).
+// 		Filter("parent", 0).
+// 		Filter("state", 1).
+// 		Filter("commentable_id", tId).
+// 		Filter("commentable_type", tType).Count()
+// 	if err != nil {
+// 		beego.BeeLogger.Error("get comment count err=%s", err)
+// 		return 0
+// 	}
+// 	return i
+// }
+
+// func (self *Comment) Save() bool {
+// 	if _, err := orm.NewOrm().Update(self); err != nil {
+// 		beego.BeeLogger.Error("Save Comment id=[%d] .err=[%s]", self.Id, err)
+// 		return false
+// 	}
+// 	return true
+// }
+func GetFullImgUrl(img string) string {
+	if img == "" {
+		return ""
+	}
+	return fmt.Sprintf("%s/%s", beego.AppConfig.String("AliOssImgHost"), img)
+}

+ 77 - 0
go/gopath/src/fohow.com/apps/models/article_model/article_cat.go

@@ -0,0 +1,77 @@
+package article_model
+
+import (
+	"fmt"
+	"strconv"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+
+	"fohow.com/cache"
+)
+
+const (
+	article_cats_tablename = "article_cats"
+)
+
+type ArticleCat struct {
+	Id       int64  `orm:"column(id);pk"         json:"id,omitempty"`       // int(11)
+	Name     string `orm:"column(name)"          json:"name,omitempty"`     // varchar(100)
+	Position int64  `orm:"column(position);null" json:"position,omitempty"` // int(11)
+	Ancestry string `orm:"column(ancestry);null" json:"-"`                  // varchar(100)
+	State    int64  `orm:"column(state);null"    json:"state,omitempty"`    // tinyint(1)
+}
+
+func (self *ArticleCat) TableName() string {
+	return article_cats_tablename
+}
+
+func GetSubCats(catId int64) (subCats []*ArticleCat) {
+	articleCat := new(ArticleCat)
+	qs := orm.NewOrm().QueryTable(articleCat)
+
+	qs = qs.Filter("ancestry", catId)
+	qs = qs.Filter("state", 1)
+
+	if _, err := qs.OrderBy("id").All(&subCats); err != nil {
+		beego.BeeLogger.Debug("get article sub cats id=[%d], err=[%s]", catId, err)
+		return nil
+	}
+	return subCats
+}
+
+func GetParentCats(catId int64) (parentCats []*ArticleCat) {
+	artCat := GetArticleCatById(catId, false)
+	if artCat != nil {
+		//只有两级
+		if artCat.Ancestry != "" {
+			catId, _ := strconv.ParseInt(artCat.Ancestry, 10, 64)
+			pCat := GetArticleCatById(catId, false)
+			if pCat != nil {
+				parentCats = append(parentCats, pCat)
+			}
+
+		}
+		parentCats = append(parentCats, artCat)
+	}
+	return parentCats
+}
+
+func GetArticleCatById(cId int64, useCache bool) *ArticleCat {
+	k := fmt.Sprintf("article_model.GetById[%s]", cId)
+	if useCache {
+		if res, ok := cache.Cache.Get(k).(*ArticleCat); ok {
+			return res
+		}
+	}
+
+	item := &ArticleCat{}
+	if err := orm.NewOrm().QueryTable(item).Filter("id", cId).Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Error("GetById(%d), err=%s", cId, err)
+		return nil
+	}
+	cache.Cache.Put(k, item, 5*time.Minute)
+	return item
+}

+ 125 - 0
go/gopath/src/fohow.com/apps/models/article_model/category.go

@@ -0,0 +1,125 @@
+package article_model
+
+import (
+	// "fmt"
+	// "strings"
+	// "time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	// "fohow.com/apps/shop"
+	// "fohow.com/cache"
+	// "fohow.com/apps/models/user_model"
+	// "fohow.com/apps/models/vas_model"
+	// "fohow.com/cache"
+	// "fohow.com/libs/wx_mp"
+)
+
+const (
+	categories_tablename = "categories"
+
+	KEY_REPLY = "关键字文本回复栏目"
+)
+
+type Category struct {
+	ShopId   int64  `orm:"column(shop_id);null"  json:"shop_id,omitempty"`  // int(11)
+	Id       int64  `orm:"column(id);pk"         json:"id,omitempty"`       // int(11)
+	Name     string `orm:"column(name)"          json:"name,omitempty"`     // varchar(100)
+	Position int64  `orm:"column(position);null" json:"position,omitempty"` // int(11)
+	Ancestry string `orm:"column(ancestry);null" json:"ancestry,omitempty"` // varchar(100)
+	Status   string `orm:"column(status);null"   json:"status,omitempty"`   // enum('no','yes')
+	Url      string `orm:"column(url);null"      json:"url,omitempty"`      // text
+}
+
+func (self *Category) TableName() string {
+	return categories_tablename
+}
+
+func GetCatByNameAndShopId(sId int64, name string) *Category {
+	item := new(Category)
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("name", name).Filter("shop_id", sId).Limit(1).
+		One(item); err != nil {
+		return nil
+	}
+	return item
+}
+
+//这个是针对关键字回复中,文本回复而创建的隐藏栏目
+func TextreplyCategory(sId int64) *Category {
+	item := GetCatByNameAndShopId(sId, KEY_REPLY)
+	if item == nil {
+		item = new(Category).Create(sId)
+	}
+	return item
+}
+
+func (self *Category) Create(sId int64) (item *Category) {
+	item = &Category{
+		Name:     KEY_REPLY,
+		Position: 1,
+		Ancestry: "1",
+		ShopId:   sId}
+	id, err := orm.NewOrm().Insert(item)
+	if err != nil {
+		beego.BeeLogger.Error("Insert Category err=[%s]", err)
+		return nil
+	}
+	item.Id = id
+	return item
+}
+
+// // 获取评论列表
+// func GetList(tType string, tId, parent, state int64, page, perPage int) (items []*Comment) {
+// 	item := new(Comment)
+// 	qs := orm.NewOrm().QueryTable(item)
+// 	qs = qs.Filter("commentable_id", tId)
+// 	qs = qs.Filter("commentable_type", tType)
+// 	if state != -1 {
+// 		qs = qs.Filter("state", state)
+// 	}
+// 	if parent != -1 {
+// 		qs = qs.Filter("parent", parent)
+// 	}
+// 	if _, err := qs.OrderBy("-created_at").
+// 		Limit(perPage, (page-1)*perPage).All(&items); err != nil {
+// 		return nil
+// 	}
+// 	return items
+// }
+
+// // 获取评论的回复记录
+// func GetSubList(parent int64, page, perPage int) (items []*Comment) {
+// 	item := new(Comment)
+// 	qs := orm.NewOrm().QueryTable(item)
+// 	if parent != -1 {
+// 		qs = qs.Filter("parent", parent)
+// 	}
+// 	if _, err := qs.OrderBy("-created_at").
+// 		Limit(perPage, (page-1)*perPage).All(&items); err != nil {
+// 		return nil
+// 	}
+// 	return items
+// }
+
+// func GetCount(tType string, tId int64) int64 {
+// 	o := orm.NewOrm()
+// 	i, err := o.QueryTable(new(Comment)).
+// 		Filter("parent", 0).
+// 		Filter("state", 1).
+// 		Filter("commentable_id", tId).
+// 		Filter("commentable_type", tType).Count()
+// 	if err != nil {
+// 		beego.BeeLogger.Error("get comment count err=%s", err)
+// 		return 0
+// 	}
+// 	return i
+// }
+
+// func (self *Comment) Save() bool {
+// 	if _, err := orm.NewOrm().Update(self); err != nil {
+// 		beego.BeeLogger.Error("Save Comment id=[%d] .err=[%s]", self.Id, err)
+// 		return false
+// 	}
+// 	return true
+// }

+ 11 - 0
go/gopath/src/fohow.com/apps/models/article_model/init.go

@@ -0,0 +1,11 @@
+package article_model
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(new(Article),
+		new(Category),
+		new(ArticleCat))
+}

+ 350 - 0
go/gopath/src/fohow.com/apps/models/balance_model/balance.go

@@ -0,0 +1,350 @@
+package balance_model
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	"github.com/uuid"
+	// "fohow.com/apps/shop"
+	// "fohow.com/cache"
+	// "fohow.com/apps/models/user_model"
+	// "fohow.com/apps/models/vas_model"
+	"fohow.com/cache"
+	// "fohow.com/libs/wx_mp"
+)
+
+const (
+	balances_tablename = "balances"
+	// balance_hitories_tablename = "d5c_balance_hitories"
+	balance_orders_tablename = "balance_orders"
+	// BALANCE_SERVICE_CHARGE   = 100 //手续费
+	BALANCE_UPGRADE     = int64(420000) //升级代金券
+	BALANCE_PAIED       = int64(300000) //升级支付金额
+	BALANCE_BL          = int64(8)      //群主推荐比例
+	RECHARGE_BALANCE_BL = int64(85)     //群主优惠比例
+	AWARD_UPGRADE       = int64(50000)  //升级群主奖励
+
+	ORDER_ID_PREFIX_CZ = "CZ" //充值
+
+	BALANCE_SOURCE_PLATFORM_EXCHANGE      = "platform_exchange" //企业大使兑换拉比代金券
+	BALANCE_SOURCE_PLATFORM_EXCHANGE_NAME = "企业大使兑换"
+	BALANCE_SOURCE_RECHARGE               = "recharge" //充值
+	BALANCE_SOURCE_RECHARGE_NAME          = "充值"
+	BALANCE_SOURCE_EXCHANGE_PRODUCT       = "exchange_product" //拉比代金券兑换商品
+	BALANCE_SOURCE_EXCHANGE_PRODUCT_NAME  = "兑换商品"
+	BALANCE_SOURCE_ALL_REFUNDED           = "all_refunded"
+	BALANCE_SOURCE_ALL_REFUNDED_NAME      = "全额退款"
+	BALANCE_SOURCE_PART_REFUNDED          = "part_refunded"
+	BALANCE_SOURCE_PART_REFUNDED_NAME     = "部分退款"
+
+	BALANCE_FREND_BUY      = "frend_buy"
+	BALANCE_FREND_BUY_NAME = "群员购物"
+
+	BALANCE_SOURCE_DEDUCT      = "deduct"
+	BALANCE_SOURCE_DEDUCT_NAME = "扣除"
+
+	PAY_WAY_TYPE_SERVICE_WXPAY = "service_wxpay"
+)
+
+type Balance struct {
+	Id         int64     `orm:"column(id);pk"                                  json:"id"`          // int(11)
+	UserId     int64     `orm:"column(user_id);null"                           json:"user_id"`     // int(11)
+	WxUserId   int64     `orm:"column(wx_user_id);null"                        json:"wx_user_id"`  // int(11)
+	Count      int64     `orm:"column(count);null"                             json:"count"`       // int(11)
+	Source     string    `orm:"column(source);null"                            json:"source"`      // varchar(20)
+	SourceName string    `orm:"-"                                              json:"source_name"` // varchar(20)
+	RelateId   string    `orm:"column(relate_id);null"                         json:"relate_id"`   // varchar(255)
+	Remark     string    `orm:"column(remark);null"                            json:"remark"`      // varchar(255)
+	CreatedAt  time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"-"`           // datetime
+	CTime      int64     `orm:"-"                                              json:"ctime"`       // datetime
+	UpdatedAt  time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"-"`           // datetime
+}
+
+func (self *Balance) TableName() string {
+	return balances_tablename
+}
+
+type BalanceOrder struct {
+	Id                int64     `orm:"column(id);pk"                                  json:"id"`         // int(11)
+	OrderId           string    `orm:"column(order_id)"                               json:"order_id"`   // varchar(64)
+	UserId            int64     `orm:"column(user_id)"                                json:"user_id"`    // int(11)
+	WxUserId          int64     `orm:"column(wx_user_id);null"                        json:"wx_user_id"` // int(11)
+	PayWay            string    `orm:"column(pay_way);null"                           json:"pay_way"`    // varchar(20)
+	TradeNo           string    `orm:"column(trade_no);null"                          json:"-"`          // varchar(64)
+	PaiedAt           int64     `orm:"column(paied_at);null"                          json:"paied_at"`   // datetime
+	TotalPrice        int64     `orm:"column(total_price);null"     json:"total_price"`                  // int(11)
+	PaiedPrice        int64     `orm:"column(paied_price);null"     json:"paied_price"`                  // int(11)
+	State             int64     `orm:"column(state);null"                             json:"state"`      // tinyint(1)
+	Remark            string    `orm:"column(remark);null"                            json:"remark"`     // varchar(64)
+	CreatedAt         time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"-"`          // datetime
+	UpdatedAt         time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"-"`          // datetime
+	BalanceBankCardNo string    `orm:"column(balance_bank_card_no);null"              json:"balance_bank_card_no"`
+	// 该字段是给连连支付认证支付时,卡前置存银行卡号用的
+	BankCard string `orm:"-"     json:"bank_card"` // datetime
+}
+
+func (self *BalanceOrder) TableName() string {
+	return balance_orders_tablename
+}
+
+func (self *Balance) Create(wxUId, uId, c int64, s, rId, remark string) (item *Balance) {
+	item = &Balance{
+		Count:    c,
+		WxUserId: wxUId,
+		UserId:   uId,
+		Source:   s,
+		RelateId: rId,
+		Remark:   remark,
+	}
+	id, err := orm.NewOrm().Insert(item)
+	if err != nil {
+		beego.BeeLogger.Error("Create Balance err=[%s]", err)
+		return nil
+	}
+	item.Id = id
+	return item
+}
+
+//返回source名称
+func (self *Balance) GetSourceName() string {
+	sourceName := ""
+	switch self.Source {
+	case BALANCE_SOURCE_RECHARGE:
+		sourceName = BALANCE_SOURCE_RECHARGE_NAME
+	case BALANCE_SOURCE_PLATFORM_EXCHANGE:
+		sourceName = BALANCE_SOURCE_PLATFORM_EXCHANGE_NAME
+	case BALANCE_SOURCE_EXCHANGE_PRODUCT:
+		sourceName = BALANCE_SOURCE_EXCHANGE_PRODUCT_NAME
+	case BALANCE_SOURCE_ALL_REFUNDED:
+		sourceName = BALANCE_SOURCE_ALL_REFUNDED_NAME
+	case BALANCE_SOURCE_PART_REFUNDED:
+		sourceName = BALANCE_SOURCE_PART_REFUNDED_NAME
+	case BALANCE_SOURCE_DEDUCT:
+		sourceName = BALANCE_SOURCE_DEDUCT_NAME
+	case BALANCE_FREND_BUY:
+		sourceName = BALANCE_FREND_BUY_NAME
+	}
+	return sourceName
+}
+
+func GetBalanceById(id int64) *Balance {
+	item := &Balance{}
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("id", id).Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Debug("GetBalanceById(%s), err=%s",
+			id, err)
+		return nil
+	}
+	return item
+}
+
+//获取某人的现金流列表
+func GetBalanceListByWxUId(wxUId, page, perPage int64, useCache bool) (list []*Balance) {
+	k := fmt.Sprintf("balance_model.GetBalanceList.wxUId(%d).page(%d).perPage(%d)", wxUId, page, perPage)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).([]*Balance); ok {
+			return s
+		}
+	}
+
+	balance := new(Balance)
+	qs := orm.NewOrm().QueryTable(balance)
+	qs = qs.Filter("wx_user_id", wxUId)
+
+	if _, err := qs.OrderBy("-created_at", "-id").
+		Limit(perPage, (page-1)*perPage).All(&list); err != nil {
+		beego.BeeLogger.Debug("get balance with wxUId=%d, err=[%s]", wxUId, err)
+		return nil
+	}
+
+	cache.Cache.Put(k, list, 10*time.Minute)
+	return list
+}
+
+//获取某人的现金流列表总条数
+func GetBalanceCountByWxUId(wxUId int64) int64 {
+	item := new(Balance)
+	o := orm.NewOrm()
+	count, _ := o.QueryTable(item).Filter("wx_user_id", wxUId).Count()
+	return count
+}
+
+//获取某个用户的余额
+func GetUserTotalBalance(wxUId int64) int64 {
+	type Ret struct {
+		Count int64
+	}
+	ret := &Ret{}
+	o := orm.NewOrm()
+	tbn := new(Balance).TableName()
+	sql := fmt.Sprintf("SELECT sum(`count`) as count FROM `%s` WHERE wx_user_id=?;", tbn)
+	err := o.Raw(sql, wxUId).QueryRow(ret)
+	if err != nil {
+		beego.BeeLogger.Error("get user=[%d] total balance err=[%s]", wxUId, err)
+		return 0
+	}
+	if ret.Count < 0 {
+		return 0
+	}
+	return ret.Count
+}
+
+//获取某个source累计余额
+func GetBalanceCountBySource(wxUId int64, source string) int64 {
+	type Ret struct {
+		Count int64
+	}
+	ret := &Ret{}
+	o := orm.NewOrm()
+	tbn := new(Balance).TableName()
+	sql := fmt.Sprintf("SELECT sum(`count`) as count FROM `%s` WHERE wx_user_id=? and source=?", tbn)
+	err := o.Raw(sql, wxUId, source).QueryRow(ret)
+	if err != nil {
+		beego.BeeLogger.Error("get user=[%d] total balance err=[%s]", wxUId, err)
+		return 0
+	}
+	if ret.Count < 0 {
+		return 0
+	}
+	return ret.Count
+}
+
+func GetBalanceBySourceAndRId(source, rId string) *Balance {
+	item := &Balance{}
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("source", source).
+		Filter("relate_id", rId).Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Info("GetBalanceBySourceAndRId(%s,%s), err=%s",
+			source, rId, err)
+		return nil
+	}
+	return item
+}
+
+func GetBalanceByUIdAndRIdAndSource(uId int64, rId, source string) *Balance {
+	item := &Balance{}
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("user_id", uId).
+		Filter("source", source).
+		Filter("relate_id", rId).OrderBy("-created_at").Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Info("GetBalanceByUIdAndRIdAndSource(%d,%s,%s), err=%s",
+			uId, rId, source, err)
+		return nil
+	}
+	return item
+}
+
+func GetBalanceOrderByOId(oId string, useCache bool) *BalanceOrder {
+	k := fmt.Sprintf("balance_model.GetBalanceOrderByOId[%s]", oId)
+	if useCache {
+		if order, ok := cache.Cache.Get(k).(*BalanceOrder); ok {
+			return order
+		}
+	}
+
+	item := &BalanceOrder{}
+	if err := orm.NewOrm().QueryTable(item).Filter("order_id", oId).Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Info("GetBalanceOrderByOId(%s), err=%s", oId, err)
+		return nil
+	}
+	cache.Cache.Put(k, item, 5*time.Minute)
+	return item
+}
+
+func GetBuyBalanceByRelateId(rId string, useCache bool) *Balance {
+	k := fmt.Sprintf("balance_model.GetBuyBalanceByRelateId[%s]", rId)
+	if useCache {
+		if order, ok := cache.Cache.Get(k).(*Balance); ok {
+			return order
+		}
+	}
+	//lt < 0
+	item := &Balance{}
+	if err := orm.NewOrm().QueryTable(item).Filter("relate_id", rId).Filter("count__lt", 0).Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Info("GetBuyBalanceByRelateId(%s), err=%s", rId, err)
+		return nil
+	}
+	cache.Cache.Put(k, item, 5*time.Minute)
+	return item
+}
+
+func GetRefundBalanceByRelateId(rId string, useCache bool) *Balance {
+	k := fmt.Sprintf("balance_model.GetRefundBalanceByRelateId[%s]", rId)
+	if useCache {
+		if order, ok := cache.Cache.Get(k).(*Balance); ok {
+			return order
+		}
+	}
+	//gt > 0
+	item := &Balance{}
+	if err := orm.NewOrm().QueryTable(item).Filter("relate_id", rId).Filter("count__gt", 0).Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Info("GetRefundBalanceByRelateId(%s), err=%s", rId, err)
+		return nil
+	}
+	cache.Cache.Put(k, item, 5*time.Minute)
+	return item
+}
+
+// 获取订单号前缀
+func (self *BalanceOrder) GetOrderIdPrefix() string {
+	s := strings.Split(self.OrderId, "")
+	prefix := fmt.Sprintf("%s%s", s[0], s[1])
+	return prefix
+}
+
+//生成订单ID
+func createOrderId(prefix string) string {
+	if prefix == "" {
+		return ""
+	}
+	n := time.Now().Format("20060102")
+	u := uuid.NewV4().String()
+	c := strings.Split(u, "-")
+	oId := strings.ToUpper(fmt.Sprintf("%s%s%s", prefix, n, c[0]))
+	return oId
+}
+
+func (self *BalanceOrder) Create(wxUId, uId, tp, paidPrice int64, payway string) (item *BalanceOrder) {
+	oId := createOrderId(ORDER_ID_PREFIX_CZ)
+	item = &BalanceOrder{
+		OrderId:    oId,
+		WxUserId:   wxUId,
+		UserId:     uId,
+		PayWay:     payway,
+		TotalPrice: tp,
+		PaiedPrice: paidPrice,
+		Remark:     "充值",
+	}
+	id, err := orm.NewOrm().Insert(item)
+	if err != nil {
+		beego.BeeLogger.Error("Create BalanceOrder err=[%s]", err)
+		return nil
+	}
+	item.Id = id
+	return item
+}
+
+func (self *Balance) Save() error {
+	_, err := orm.NewOrm().Update(self)
+	if err != nil {
+		beego.BeeLogger.Error("Save Balance id=[%d] .err=[%s]", self.Id, err)
+	}
+	return err
+}
+
+func (self *BalanceOrder) Save() error {
+	_, err := orm.NewOrm().Update(self)
+	if err != nil {
+		beego.BeeLogger.Error("Save BalanceOrder id=[%d] .err=[%s]", self.Id, err)
+	}
+	return err
+}

+ 359 - 0
go/gopath/src/fohow.com/apps/models/balance_model/cash_balance.go

@@ -0,0 +1,359 @@
+package balance_model
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+
+	"fohow.com/cache"
+)
+
+const (
+	cash_balances_tablename = "cash_balances"
+
+	recharge_cash_orders_tablename = "recharge_cash_orders"
+
+	CASH_SOURCE_DOLLAR_WIN_RETURN      = "dollar_win_return"
+	CASH_SOURCE_DOLLAR_WIN_RETURN_NAME = "活动退款"
+	CASH_SOURCE_DOLLAR_WIN             = "dollar_win"
+	CASH_SOURCE_DOLLAR_WIN_NAME        = "活动报名"
+
+	CASH_SOURCE_TAKE_CASH      = "take_cash" //提现
+	CASH_SOURCE_TAKE_CASH_NAME = "提现"
+
+	CASH_SOURCE_CORRECT           = "correct" //冲正
+	CASH_SOURCE_CORRECT_NAME      = "冲正"
+	CASH_SOURCE_PROJECT_LOAN      = "project_loan"
+	CASH_SOURCE_PROJECT_LOAN_NAME = "项目放款"
+
+	CASH_SOURCE_EXTRACT_REJECT       = "extract_reject"  //拒绝提现
+	CASH_SOURCE_EXTRACT_REJECT_NAME  = "拒绝提现"            //拒绝提现
+	CASH_SOURCE_PRODUCT_BENEFIT      = "product_benefit" // 一级商品佣金
+	CASH_SOURCE_PRODUCT_BENEFIT_NAME = "商品佣金"
+
+	BALANCE_SOURCE_BENEFIT      = "balance_benefit" // 代金券充值佣金
+	BALANCE_SOURCE_BENEFIT_NAME = "代金券充值佣金"
+
+	FX_CASH_SOURCE_PRODUCT_BENEFIT      = "fx_product_benefit" // 二级商品佣金
+	FX_CASH_SOURCE_PRODUCT_BENEFIT_NAME = "分享商品佣金"
+	CASH_SOURCE_PROJECT_BENEFIT         = "project_benefit" //项目佣金
+	CASH_SOURCE_PROJECT_BENEFIT_NAME    = "项目佣金"
+	CASH_SOURCE_PRODUCT_SALE            = "product_sale" //商品代销结算
+	CASH_SOURCE_PRODUCT_SALE_NAME       = "商品代销"
+
+	CASH_SOURCE_RECHARGE_CASH      = "recharge" //充值
+	CASH_SOURCE_RECHARGE_CASH_NAME = "充值"
+
+	CASH_SOURCE_SALE_REWARD      = "sale_reward_monthly" //销售奖金
+	CASH_SOURCE_SALE_REWARD_NAME = "销售奖金"
+
+	CASH_SOURCE_PROJECT_INVEST      = "project_invest" //助农项目
+	CASH_SOURCE_PROJECT_INVEST_NAME = "助农项目"
+
+	CASH_SOURCE_RESERVE_ACT_RETURN      = "reserve_act_return"
+	CASH_SOURCE_RESERVE_ACT_RETURN_NAME = "预定金额返回"
+
+	CASH_SOURCE_RECEIVE_BUYBACK      = "receive_buyback"
+	CASH_SOURCE_RECEIVE_BUYBACK_NAME = "项目回款"
+
+	CASH_SOURCE_DEDUCT      = "deduct"
+	CASH_SOURCE_DEDUCT_NAME = "扣款"
+
+	CASH_SOURCE_LOAN      = "loan"
+	CASH_SOURCE_LOAN_NAME = "借款"
+
+	CASH_SOURCE_PROJECT_REFUND      = "project_refund"
+	CASH_SOURCE_PROJECT_REFUND_NAME = "项目退款"
+
+	CASH_SOURCE_PRODUCT_REFUND      = "product_refund"
+	CASH_SOURCE_PRODUCT_REFUND_NAME = "商品退款"
+
+	CASH_SOURCE_PRODUCT_PART_REFUND      = "product_part_refund"
+	CASH_SOURCE_PRODUCT_PART_REFUND_NAME = "商品部分退款"
+
+	CASH_SOURCE_GOOD_PAYMENT      = "good_payment"
+	CASH_SOURCE_GOOD_PAYMENT_NAME = "货款"
+
+	PAY_WAY_TYPE_RECHARGE_WXPAY = "recharge_wxpay" // 充值支付方式
+
+	ORDER_ID_PREFIX_CZCB = "CZCB" //充值余额
+
+)
+
+//现金余额表
+type CashBalance struct {
+	Id         int64     `orm:"column(id);pk"                                       json:"id"`        // int(11)
+	WxUId      int64     `orm:"column(wx_uid)"                                      json:"wx_uid"`    // int(11)
+	Count      int64     `orm:"column(count)"                                       json:"count"`     // int(11)
+	Source     string    `orm:"column(source);null"                                 json:"source"`    // varchar(64)
+	SourceName string    `orm:"-"                                 json:"source_name"`                 // varchar(64)
+	RelateId   string    `orm:"column(relate_id);null"                              json:"relate_id"` // varchar(255)
+	Remark     string    `orm:"column(remark);null"                                 json:"remark"`    // varchar(255)
+	CTime      int64     `orm:"-"                                       json:"c_time"`                // int(11)
+	CreatedAt  time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)" json:"-"`         // datetime
+	UpdatedAt  time.Time `orm:"column(updated_at);null;auto_now;type(datetime)"     json:"-"`         // datetime
+}
+
+//充值订单表
+type RechargeCashOrder struct {
+	Id                int64     `orm:"column(id);pk"                                  json:"id"`         // int(11)
+	OrderId           string    `orm:"column(order_id)"                               json:"order_id"`   // varchar(64)
+	UserId            int64     `orm:"column(user_id)"                                json:"user_id"`    // int(11)
+	WxUserId          int64     `orm:"column(wx_user_id);null"                        json:"wx_user_id"` // int(11)
+	PayWay            string    `orm:"column(pay_way);null"                           json:"pay_way"`    // varchar(20)
+	TradeNo           string    `orm:"column(trade_no);null"                          json:"-"`          // varchar(64)
+	PaiedAt           int64     `orm:"column(paied_at);null"                          json:"paied_at"`   // datetime
+	TotalPrice        int64     `orm:"column(total_price);null"     json:"total_price"`                  // int(11)
+	State             int64     `orm:"column(state);null"                             json:"state"`      // tinyint(1)
+	Remark            string    `orm:"column(remark);null"                            json:"remark"`     // varchar(64)
+	CreatedAt         time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"-"`          // datetime
+	UpdatedAt         time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"-"`          // datetime
+	BalanceBankCardNo string    `orm:"column(balance_bank_card_no);null"              json:"balance_bank_card_no"`
+}
+
+func (self *CashBalance) TableName() string {
+	return cash_balances_tablename
+}
+
+func (self *RechargeCashOrder) TableName() string {
+	return recharge_cash_orders_tablename
+}
+
+func (self *CashBalance) Create(wxUId, c int64, s, rId, remark string) (item *CashBalance) {
+	item = &CashBalance{
+		Count:    c,
+		WxUId:    wxUId,
+		Source:   s,
+		RelateId: rId,
+		Remark:   remark,
+	}
+	id, err := orm.NewOrm().Insert(item)
+	if err != nil {
+		beego.BeeLogger.Error("Create CashBalance err=[%s]", err)
+		return nil
+	}
+	item.Id = id
+	return item
+}
+
+//返回source名称
+func (self *CashBalance) GetSourceName() string {
+	sourceName := ""
+	switch self.Source {
+	case CASH_SOURCE_PRODUCT_BENEFIT:
+		sourceName = CASH_SOURCE_PRODUCT_BENEFIT_NAME
+	case CASH_SOURCE_PROJECT_BENEFIT:
+		sourceName = CASH_SOURCE_PROJECT_BENEFIT_NAME
+	case CASH_SOURCE_TAKE_CASH:
+		sourceName = CASH_SOURCE_TAKE_CASH_NAME
+	case CASH_SOURCE_PRODUCT_SALE:
+		sourceName = CASH_SOURCE_PRODUCT_SALE_NAME
+	case CASH_SOURCE_SALE_REWARD:
+		sourceName = CASH_SOURCE_SALE_REWARD_NAME
+	case CASH_SOURCE_PROJECT_INVEST:
+		sourceName = CASH_SOURCE_PROJECT_INVEST_NAME
+	case CASH_SOURCE_RESERVE_ACT_RETURN:
+		sourceName = CASH_SOURCE_RESERVE_ACT_RETURN_NAME
+	case CASH_SOURCE_EXTRACT_REJECT:
+		sourceName = CASH_SOURCE_EXTRACT_REJECT_NAME
+	case CASH_SOURCE_RECHARGE_CASH:
+		sourceName = CASH_SOURCE_RECHARGE_CASH_NAME
+	case CASH_SOURCE_RECEIVE_BUYBACK:
+		sourceName = CASH_SOURCE_RECEIVE_BUYBACK_NAME
+	case CASH_SOURCE_DEDUCT:
+		sourceName = CASH_SOURCE_DEDUCT_NAME
+	case CASH_SOURCE_LOAN:
+		sourceName = CASH_SOURCE_LOAN_NAME
+	case CASH_SOURCE_PROJECT_REFUND:
+		sourceName = CASH_SOURCE_PROJECT_REFUND
+	case CASH_SOURCE_PRODUCT_REFUND:
+		sourceName = CASH_SOURCE_PRODUCT_REFUND_NAME
+	case CASH_SOURCE_PRODUCT_PART_REFUND:
+		sourceName = CASH_SOURCE_PRODUCT_PART_REFUND_NAME
+	case CASH_SOURCE_CORRECT:
+		sourceName = CASH_SOURCE_CORRECT_NAME
+	case CASH_SOURCE_PROJECT_LOAN:
+		sourceName = CASH_SOURCE_PROJECT_LOAN_NAME
+	case CASH_SOURCE_GOOD_PAYMENT:
+		sourceName = CASH_SOURCE_GOOD_PAYMENT_NAME
+	case CASH_SOURCE_DOLLAR_WIN:
+		sourceName = CASH_SOURCE_DOLLAR_WIN_NAME
+	case CASH_SOURCE_DOLLAR_WIN_RETURN:
+		sourceName = CASH_SOURCE_DOLLAR_WIN_RETURN_NAME
+	case FX_CASH_SOURCE_PRODUCT_BENEFIT:
+		sourceName = FX_CASH_SOURCE_PRODUCT_BENEFIT_NAME
+		// case BALANCE_SOURCE_PART_REFUNDED:
+		// 	sourceName = BALANCE_SOURCE_PART_REFUNDED_NAME
+	}
+	return sourceName
+}
+
+//获取某人的现金流列表
+func GetCashBalanceListByWxUId(wxUId, page, perPage int64, useCache bool) (list []*CashBalance) {
+	k := fmt.Sprintf("balance_model.GetCashBalanceListByWxUId(%d).page(%d).perPage(%d)", wxUId, page, perPage)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).([]*CashBalance); ok {
+			return s
+		}
+	}
+
+	balance := new(CashBalance)
+	qs := orm.NewOrm().QueryTable(balance)
+	qs = qs.Filter("wx_uid", wxUId)
+
+	if _, err := qs.OrderBy("-created_at", "-id").
+		Limit(perPage, (page-1)*perPage).All(&list); err != nil {
+		beego.BeeLogger.Debug("get cash balance with wxUId=%d, err=[%s]", wxUId, err)
+		return nil
+	}
+
+	for _, item := range list {
+		item.SourceName = item.GetSourceName()
+	}
+
+	cache.Cache.Put(k, list, 10*time.Minute)
+	return list
+}
+
+//获取某人的现金流列表总条数
+func GetCashBalanceCountByWxUId(wxUId int64) int64 {
+	item := new(CashBalance)
+	o := orm.NewOrm()
+	count, _ := o.QueryTable(item).Filter("wx_uid", wxUId).Count()
+	return count
+}
+
+//账户进账总额
+func GetCashEnterBalance(wxUId int64) int64 {
+	type Ret struct {
+		Count int64
+	}
+	ret := &Ret{}
+	o := orm.NewOrm()
+	tbn := new(CashBalance).TableName()
+	sql := fmt.Sprintf("SELECT sum(`count`) as count FROM `%s` WHERE wx_uid=? and count > 0;", tbn)
+	err := o.Raw(sql, wxUId).QueryRow(ret)
+	if err != nil {
+		beego.BeeLogger.Error("GetCashEnterBalance, wxUser:%d err=[%s]", wxUId, err)
+		return 0
+	}
+	if ret.Count < 0 {
+		return 0
+	}
+	return ret.Count
+}
+
+//账户出账总额
+func GetCashOutBalance(wxUId int64) int64 {
+	type Ret struct {
+		Count int64
+	}
+	ret := &Ret{}
+	o := orm.NewOrm()
+	tbn := new(CashBalance).TableName()
+	sql := fmt.Sprintf("SELECT sum(`count`) as count FROM `%s` WHERE wx_uid=? and count < 0;", tbn)
+	err := o.Raw(sql, wxUId).QueryRow(ret)
+	if err != nil {
+		beego.BeeLogger.Error("GetCashOuterBalance, wxUser:%d err=[%s]", wxUId, err)
+		return 0
+	}
+	if ret.Count < 0 {
+		return 0
+	}
+	return ret.Count
+}
+
+//账户余额
+func GetCashTotalBalance(wxUId int64) int64 {
+	type Ret struct {
+		Count int64
+	}
+	ret := &Ret{}
+	o := orm.NewOrm()
+	tbn := new(CashBalance).TableName()
+	sql := fmt.Sprintf("SELECT sum(`count`) as count FROM `%s` WHERE wx_uid=?;", tbn)
+	err := o.Raw(sql, wxUId).QueryRow(ret)
+	if err != nil {
+		beego.BeeLogger.Error("GetCashTotalBalance, wxUser:%d err=[%s]", wxUId, err)
+		return 0
+	}
+	if ret.Count < 0 {
+		return 0
+	}
+	return ret.Count
+}
+
+func GetCashBalanceByWxUIdAndRIdAndSource(wxUId int64, rId, source string) *CashBalance {
+	item := &CashBalance{}
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("wx_uid", wxUId).
+		Filter("source", source).
+		Filter("relate_id", rId).OrderBy("-created_at").Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Info("GetCashBalanceByWxUIdAndRIdAndSource(%d,%s,%s), err=%s",
+			wxUId, rId, source, err)
+		return nil
+	}
+	return item
+}
+
+//创建充值订单
+func (self *RechargeCashOrder) Create(wxUId, uId, tp int64, payway string) (item *RechargeCashOrder) {
+	oId := createOrderId(ORDER_ID_PREFIX_CZCB)
+	item = &RechargeCashOrder{
+		OrderId:    oId,
+		WxUserId:   wxUId,
+		UserId:     uId,
+		PayWay:     payway,
+		TotalPrice: tp,
+		Remark:     "充值",
+	}
+	id, err := orm.NewOrm().Insert(item)
+	if err != nil {
+		beego.BeeLogger.Error("Create RechargeCashOrder err=[%s]", err)
+		return nil
+	}
+	item.Id = id
+	return item
+}
+
+func (self *RechargeCashOrder) Save() error {
+	_, err := orm.NewOrm().Update(self)
+	if err != nil {
+		beego.BeeLogger.Error("Save RechargeCashOrder id=[%d] .err=[%s]", self.Id, err)
+	}
+	return err
+}
+
+func GetRechargeCashOrderByOId(oId string, useCache bool) *RechargeCashOrder {
+	k := fmt.Sprintf("balance_model.GetRechargeCashOrderByOId[%s]", oId)
+	if useCache {
+		if order, ok := cache.Cache.Get(k).(*RechargeCashOrder); ok {
+			return order
+		}
+	}
+
+	item := &RechargeCashOrder{}
+	if err := orm.NewOrm().QueryTable(item).Filter("order_id", oId).Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Info("GetRechargeCashOrderByOId(%s), err=%s", oId, err)
+		return nil
+	}
+	cache.Cache.Put(k, item, 5*time.Minute)
+	return item
+}
+
+func GetCashBalanceBySourceAndRId(source, rId string) *CashBalance {
+	item := &CashBalance{}
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("source", source).
+		Filter("relate_id", rId).Limit(1).
+		One(item); err != nil {
+		beego.BeeLogger.Info("GetCashBalanceBySourceAndRId(%s,%s), err=%s",
+			source, rId, err)
+		return nil
+	}
+	return item
+}

+ 10 - 0
go/gopath/src/fohow.com/apps/models/balance_model/init.go

@@ -0,0 +1,10 @@
+package balance_model
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(new(Balance),
+		new(BalanceOrder), new(CashBalance), new(TakeCashOrder), new(RechargeCashOrder))
+}

+ 212 - 0
go/gopath/src/fohow.com/apps/models/balance_model/takecash.go

@@ -0,0 +1,212 @@
+package balance_model
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+
+	"fohow.com/cache"
+)
+
+const (
+	take_cash_orders_tablename = "take_cash_orders"
+
+	ORDER_ID_PREFIX_TX = "TX" //提现
+
+	//状态:提现失败(pay_state:0,audit_state:2), 提现中(pay_state:0,audit_state:0,1), 已完成(pay_state:1, audit_state:1)
+	STATE_CN_REJECTED    = "提现失败"
+	STATE_CN_IN_PROGRESS = "提现中"
+	STATE_CN_SUCESS      = "已完成"
+
+	TAKE_CASH_LIMIT = 10       //每天提现次数限制
+	REAL_STATE      = int64(0) //强制检验
+)
+
+type TakeCashOrder struct {
+	Id         int64     `orm:"column(id);pk"                                       json:"id"`           // int(11)
+	WxUId      int64     `orm:"column(wx_uid)"                                      json:"wx_uid"`       // int(11)
+	OrderId    string    `orm:"column(order_id)"                                    json:"order_id"`     // varchar(64)
+	TradeNo    string    `orm:"column(trade_no);null"                               json:"trade_no"`     // varchar(64)
+	Count      int64     `orm:"column(count);null"                                  json:"count"`        // bigint(20)
+	State      int64     `orm:"column(pay_state);null"                              json:"pay_state"`    // tinyint(1)
+	RealState  int64     `orm:"column(real_state);null"                              json:"-"`           // tinyint(1)
+	AuditState int64     `orm:"column(audit_state);null"                            json:"audit_state"`  // tinyint(1)
+	PaiedAt    int64     `orm:"column(paied_at);null"                               json:"paied_at"`     // int(11)
+	Remark     string    `orm:"column(remark);null"                                 json:"remark"`       // varchar(255)
+	ExpcPayAt  time.Time `orm:"column(expc_pay_at);null;auto_now_add;type(datetime)" json:"expc_pay_at"` // datetime
+	//银行信息
+	BankName    string    `orm:"column(bank_name)"     json:"bank_name"`
+	BankAccount string    `orm:"column(bank_account)"     json:"bank_account"`
+	AccountName string    `orm:"column(account_name)"     json:"account_name"`
+	CreatedAt   time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)" json:"created_at"` // datetime
+	UpdatedAt   time.Time `orm:"column(updated_at);null;auto_now;type(datetime)"     json:"updated_at"` // datetime
+}
+
+func (self *TakeCashOrder) TableName() string {
+	return take_cash_orders_tablename
+}
+
+func (self *TakeCashOrder) Create(wxUId, count int64) (item *TakeCashOrder) {
+	oId := createOrderId(ORDER_ID_PREFIX_TX)
+	item = &TakeCashOrder{
+		Count:     count,
+		WxUId:     wxUId,
+		OrderId:   oId,
+		RealState: REAL_STATE,
+	}
+
+	id, err := orm.NewOrm().Insert(item)
+	if err != nil {
+		beego.BeeLogger.Error("Create TakeCashOrder err=[%s]", err)
+		return nil
+	}
+	item.Id = id
+	return item
+}
+
+func (self *TakeCashOrder) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save take cash order id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}
+
+//状态值中文
+func (self *TakeCashOrder) GetStateCn() string {
+	stateCn := ""
+	switch self.State {
+	case 0:
+		switch self.AuditState {
+		case 0:
+			stateCn = STATE_CN_IN_PROGRESS
+
+		case 1:
+			stateCn = STATE_CN_IN_PROGRESS
+
+		case 2:
+			stateCn = STATE_CN_REJECTED
+		}
+	case 1:
+		switch self.AuditState {
+		case 1:
+			stateCn = STATE_CN_SUCESS
+		}
+	case 2:
+		stateCn = STATE_CN_REJECTED
+	}
+
+	return stateCn
+}
+
+func GetTakeCashOrderByOId(oId string, useCache bool) *TakeCashOrder {
+	k := fmt.Sprintf("balance_model.GetTakeCashOrderByOId[%s]", oId)
+	if useCache {
+		if order, ok := cache.Cache.Get(k).(*TakeCashOrder); ok {
+			return order
+		}
+	}
+	order := new(TakeCashOrder)
+	o := orm.NewOrm()
+	err := o.QueryTable(order).Filter("order_id", oId).Limit(1).One(order)
+	if err != nil {
+		beego.Debug("GetOrderByOrderId is not found err=[%s], oId=%s", err, oId)
+		return nil
+	} else {
+		cache.Cache.Put(k, order, 5*time.Minute)
+		return order
+	}
+}
+
+//获取某人的提现流列表
+func GetTakeCashOrderListByWxUId(wxUId, page, perPage int64, useCache bool) (list []*TakeCashOrder) {
+	k := fmt.Sprintf("balance_model.GetTakeCashOrderListByWxUId.wxUId(%d).page(%d).perPage(%d)", wxUId, page, perPage)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).([]*TakeCashOrder); ok {
+			return s
+		}
+	}
+
+	order := new(TakeCashOrder)
+	qs := orm.NewOrm().QueryTable(order)
+	qs = qs.Filter("wx_uid", wxUId)
+
+	if _, err := qs.OrderBy("-created_at", "-id").
+		Limit(perPage, (page-1)*perPage).All(&list); err != nil {
+		beego.BeeLogger.Debug("get GetTakeCashOrderListByWxUId with wxUId=%d, err=[%s]", wxUId, err)
+		return nil
+	}
+
+	cache.Cache.Put(k, list, 5*time.Minute)
+	return list
+}
+
+//获取某人的提现流列表总条数
+func GetTakeCashOrderCountByWxUId(wxUId int64, useCache bool) int64 {
+
+	k := fmt.Sprintf("balance_model.GetTakeCashOrderCountByWxUId.wxUId(%d)", wxUId)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).(int64); ok {
+			return s
+		}
+	}
+
+	item := new(TakeCashOrder)
+	o := orm.NewOrm()
+	count, _ := o.QueryTable(item).Filter("wx_uid", wxUId).Count()
+
+	if count > 0 {
+		cache.Cache.Put(k, count, 5*time.Minute)
+	}
+	return count
+}
+
+func GetTakeCashOrderListByWxUIdAndTime(wxUId int64, now time.Time, useCache bool) (list []*TakeCashOrder) {
+
+	k := fmt.Sprintf("balance_model.GetTakeCashOrderListByWxUIdAndTime.wxUId(%d).nowDay(%s)", wxUId, now.Format("2006-01-02"))
+	if useCache {
+		if s, ok := cache.Cache.Get(k).([]*TakeCashOrder); ok {
+			return s
+		}
+	}
+
+	qs := orm.NewOrm()
+
+	sql :=
+		`
+			SELECT
+				*
+			FROM
+				take_cash_orders
+			WHERE
+				wx_uid = ?
+			AND DATE_FORMAT(
+				DATE_ADD(created_at, INTERVAL 8 HOUR),
+				'%Y-%m-%d'
+			) = ?
+			LIMIT 1;
+
+		`
+
+	if _, err := qs.Raw(sql, wxUId, now.Format("2006-01-02")).QueryRows(&list); err != nil {
+		beego.BeeLogger.Debug("get GetTakeCashOrderListByWxUIdAndTime with wxUId=%d, err=[%s]", wxUId, err)
+		return nil
+	}
+
+	if len(list) > 0 {
+		cache.Cache.Put(k, list, 5*time.Minute)
+	}
+	return list
+
+}
+
+//取正在提现的订单列表
+func GetAllTakingCashOrders() (list []*TakeCashOrder) {
+	sql := fmt.Sprintf("select * from %s where pay_state=? and audit_state=? and expc_pay_at < ?", take_cash_orders_tablename)
+	if _, err := orm.NewOrm().Raw(sql, 0, 1, time.Now().Add(-8*time.Hour)).QueryRows(&list); err != nil {
+		return nil
+	}
+	return list
+}

+ 74 - 0
go/gopath/src/fohow.com/apps/models/category_model/category.go

@@ -0,0 +1,74 @@
+package category_model
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+
+	"fohow.com/apps/models/product_model"
+	"fohow.com/cache"
+)
+
+const (
+	platform_categories_tablename = "platform_categories"
+)
+
+type Category struct {
+	Id        int64                  `orm:"column(id);pk"      json:"id"`         // int(11)
+	Name      string                 `orm:"column(name)"       json:"name"`       // varchar(10)
+	Position  int64                  `orm:"column(position)"   json:"-"`          // int(11)
+	Ancestry  string                 `orm:"column(ancestry)"   json:"-"`          // varchar(20)
+	Platform  string                 `orm:"column(platform)"   json:"-"`          // varchar(20)
+	Url       string                 `orm:"column(url)"        json:"url"`        // varchar(4096)
+	UrlType   int64                  `orm:"column(url_type)"   json:"url_type"`   //tinyint(4)
+	State     int64                  `orm:"column(state)"      json:"state"`      // tinyint(1)
+	Remark    string                 `orm:"column(remark)"     json:"remark"`     // varchar(225)
+	ProductId int64                  `orm:"column(product_id)" json:"product_id"` // int(11)
+	Product   *product_model.Product `orm:"-"                  json:"product"`
+	SubCats   []*Category            `orm:"-"                  json:"sub_cats"`
+	Cover     string                 `orm:"column(cover)" json:"cover"`
+}
+
+func (self *Category) TableName() string {
+	return platform_categories_tablename
+}
+
+func GetCatsByPlatform(platform string, useCache bool) (cats []*Category) {
+	k := fmt.Sprintf("category_model.GetCatsByPlatform(%s)", platform)
+	if useCache {
+		if ret, ok := cache.Cache.Get(k).([]*Category); ok {
+			return ret
+		}
+	}
+
+	cat := new(Category)
+	if err := orm.NewOrm().QueryTable(cat).Filter("state", 1).
+		Filter("platform", platform).One(cat); err != nil {
+		beego.BeeLogger.Error("GetCatsByPlatform[%s] err=[%s]", err)
+	}
+	cats = getCatsByAncestry(fmt.Sprintf("%d", cat.Id))
+
+	cache.Cache.Put(k, cats, 10*time.Minute)
+	return cats
+}
+
+func getCatsByAncestry(ancestry string) (cats []*Category) {
+	cat := new(Category)
+	if _, err := orm.NewOrm().QueryTable(cat).Filter("state", 1).
+		Filter("ancestry", ancestry).OrderBy("position").All(&cats); err != nil {
+		beego.BeeLogger.Error("get all ancestry like '%d%', err=[%s]", cat.Id, err)
+	}
+	for _, m := range cats {
+		m.Cover = GetFullImgUrl(m.Cover)
+		if m.ProductId != 0 {
+			m.Product = product_model.GetProductById(m.ProductId, true)
+		}
+		_cats := getCatsByAncestry(fmt.Sprintf("%s/%d", m.Ancestry, m.Id))
+		if _cats != nil {
+			m.SubCats = _cats
+		}
+	}
+	return cats
+}

+ 24 - 0
go/gopath/src/fohow.com/apps/models/category_model/init.go

@@ -0,0 +1,24 @@
+package category_model
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(new(Category), new(NavigateIconConfig))
+}
+
+func GetFullImgUrl(img string) string {
+	if img == "" {
+		return ""
+	}
+	if strings.HasPrefix(img, "http://") {
+		return img
+	} else {
+		return fmt.Sprintf("%s/%s", beego.AppConfig.String("AliOssImgHost"), img)
+	}
+}

+ 48 - 0
go/gopath/src/fohow.com/apps/models/category_model/navigate_icon_config.go

@@ -0,0 +1,48 @@
+package category_model
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	"fohow.com/cache"
+	"time"
+)
+
+const (
+	navigate_icon_configs_tablename = "navigate_icon_configs"
+)
+
+type NavigateIconConfig struct {
+	Id              int64  `orm:"column(id);pk"      json:"id"`       // int(11)
+	Name            string `orm:"column(name)"       json:"name"`     // varchar(10)
+	Sort            int64  `orm:"column(sort)"   json:"-"`            // int(11)
+	Url             string `orm:"column(url)"        json:"url"`      // varchar(4096)
+	UrlType         int64  `orm:"column(url_type)"   json:"url_type"` //tinyint(4)
+	Cover           string `orm:"column(cover)" json:"cover"`
+	State           int64  `orm:"column(state)"      json:"state"` // tinyint(1)
+	Remark          string `orm:"column(remark)"     json:"-"`     // varchar(225)
+	ProductCatIndex int64  `orm:"column(product_cat_index)"  json:"product_cat_index"`
+}
+
+func (self *NavigateIconConfig) TableName() string {
+	return navigate_icon_configs_tablename
+}
+
+func GetNavigateIconsByState(state int64, useCache bool) (cats []*NavigateIconConfig) {
+	k := fmt.Sprintf("category_model.GetNavigateIconsByState(%d)", state)
+	if useCache {
+		if ret, ok := cache.Cache.Get(k).([]*NavigateIconConfig); ok {
+			return ret
+		}
+	}
+
+	cat := new(NavigateIconConfig)
+	if _, err := orm.NewOrm().QueryTable(cat).Filter("state", state).OrderBy("-sort").All(&cats); err != nil {
+		beego.BeeLogger.Error("GetCatsByPlatform[%s] err=[%s]", err)
+	}
+	for _, item := range cats {
+		item.Cover = GetFullImgUrl(item.Cover)
+	}
+	cache.Cache.Put(k, cats, 10*time.Minute)
+	return cats
+}

+ 106 - 0
go/gopath/src/fohow.com/apps/models/channel_gzh_qrcode_model/channel_qrcode.go

@@ -0,0 +1,106 @@
+package channel_gzh_qrcode_model
+
+import (
+	"fmt"
+	// "strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+
+	"fohow.com/cache"
+)
+
+const (
+	channel_qrcodes_tablename = "channel_qrcodes" //二维码
+)
+
+type ChannelQrcode struct {
+	Id                    int64     `orm:"column(id);pk"                                  json:"id"` // int(11)
+	WxGongzhonghaoId      int64     `orm:"column(wx_gongzhonghao_id);null"                          json:"wx_gongzhonghao_id"`
+	ParentSignUpChannelId int64     `orm:"column(parent_sign_up_channel_id);null"              json:"-"`
+	SceneId               int64     `orm:"column(scene_id);null"                          json:"scene_id"`          // int(11)
+	SceneStr              string    `orm:"column(scene_str)"                              json:"scene_str"`         // text
+	IsPermanent           int64     `orm:"column(is_permanent);null"                      json:"is_permanent"`      // tinyint(1)
+	Remark                string    `orm:"column(remark)"                                 json:"remark"`            // text
+	QrcodeImg             string    `orm:"column(qrcode_img)"                             json:"qrcode_img"`        // text
+	ExpiredAt             time.Time `orm:"column(expired_at);null;type(datetime)"         json:"expired_at"`        // datetime
+	ScanTimes             int64     `orm:"column(scan_times);null"                        json:"scan_times"`        // int(11)
+	PushAfterSubId        int64     `orm:"column(push_after_sub_id);null"                 json:"push_after_sub_id"` // int(11)
+	CreatedAt             time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"-"`                 // datetime
+	UpdatedAt             time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"-"`                 // datetime
+}
+
+func (self *ChannelQrcode) TableName() string {
+	return channel_qrcodes_tablename
+}
+
+func GetBySceneId(sId int64, useCache bool) *ChannelQrcode {
+	k := fmt.Sprintf("channel_gzh_qrcode_model.GetBySceneId(%d)", sId)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).(*ChannelQrcode); ok {
+			return s
+		}
+	}
+
+	item := new(ChannelQrcode)
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("scene_id", sId).Limit(1).OrderBy("-id").
+		One(item); err != nil {
+		return nil
+	}
+
+	cache.Cache.Put(k, item, 10*time.Minute)
+
+	return item
+}
+
+func GetBySceneStr(str string, useCache bool) *ChannelQrcode {
+	k := fmt.Sprintf("channel_gzh_qrcode_model.GetBySceneStr(%s)", str)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).(*ChannelQrcode); ok {
+			return s
+		}
+	}
+
+	item := new(ChannelQrcode)
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("scene_str__startswith", str).Limit(1).OrderBy("-id").
+		One(item); err != nil {
+		return nil
+	}
+
+	cache.Cache.Put(k, item, 10*time.Minute)
+
+	return item
+}
+
+func GetById(id int64) *ChannelQrcode {
+	item := new(ChannelQrcode)
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("id", id).Limit(1).OrderBy("-id").
+		One(item); err != nil {
+		return nil
+	}
+	return item
+}
+
+// func (self *ChannelQrcode) Create(uId, openid string) *SubSt {
+// 	item := &SubSt{
+// 		UserId:            uId,
+// 		Openid:            openid,
+// 		LastSubscribeTime: time.Now()}
+// 	if _, err := orm.NewOrm().Insert(item); err != nil {
+// 		beego.BeeLogger.Error("insert SubSt err=[%s]", err)
+// 		return nil
+// 	}
+// 	return item
+// }
+
+func (self *ChannelQrcode) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save ChannelQrcode id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}

+ 66 - 0
go/gopath/src/fohow.com/apps/models/channel_gzh_qrcode_model/channel_qrcode_result.go

@@ -0,0 +1,66 @@
+package channel_gzh_qrcode_model
+
+import (
+	// "fmt"
+	// "strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	// "fohow.com/cache"
+)
+
+const (
+	channel_qrcode_results_tablename = "channel_qrcode_results" //二维码扫描结果
+)
+
+type ChannelQrcodeResult struct {
+	Id              int64     `orm:"column(id);pk"                                  json:"id,omitempty"`                // int(11)
+	Openid          string    `orm:"column(mp_openid);null"                            json:"openid,omitempty"`         // varchar(255)
+	ChannelQrcodeId int64     `orm:"column(channel_qrcode_id);null"                 json:"channel_qrcode_id,omitempty"` // int(11)
+	CreatedAt       time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"created_at,omitempty"`        // datetime
+	UpdatedAt       time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"updated_at,omitempty"`        // datetime
+}
+
+func (self *ChannelQrcodeResult) TableName() string {
+	return channel_qrcode_results_tablename
+}
+
+func GetByOpenidAndCqId(openid string, cqId int64) *ChannelQrcodeResult {
+	item := new(ChannelQrcodeResult)
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("openid", openid).Filter("channel_qrcode_id", cqId).Limit(1).
+		One(item); err != nil {
+		return nil
+	}
+	return item
+}
+
+func (self *ChannelQrcodeResult) Create(cqId int64, openid string) *ChannelQrcodeResult {
+	item := &ChannelQrcodeResult{
+		ChannelQrcodeId: cqId,
+		Openid:          openid}
+	if _, err := orm.NewOrm().Insert(item); err != nil {
+		beego.BeeLogger.Info("insert ChannelQrcodeResult err=[%s]", err)
+		return nil
+	}
+	return item
+}
+
+// func (self *SubSt) Save() error {
+// 	if _, err := orm.NewOrm().Update(self); err != nil {
+// 		beego.BeeLogger.Error("Save SubSt id=[%d] .err=[%s]", self.Id, err)
+// 		return err
+// 	}
+// 	return nil
+// }
+
+func GetFirstByOpenid(openid string) *ChannelQrcodeResult {
+	item := new(ChannelQrcodeResult)
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("mp_openid", openid).OrderBy("id").Limit(1).
+		One(item); err != nil {
+		return nil
+	}
+	return item
+}

+ 13 - 0
go/gopath/src/fohow.com/apps/models/channel_gzh_qrcode_model/init.go

@@ -0,0 +1,13 @@
+package channel_gzh_qrcode_model
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(
+		new(ChannelQrcodeResult),
+		new(ChannelQrcode),
+		new(PushAfterSub),
+	)
+}

+ 53 - 0
go/gopath/src/fohow.com/apps/models/channel_gzh_qrcode_model/push_after_sub.go

@@ -0,0 +1,53 @@
+package channel_gzh_qrcode_model
+
+import (
+	// "fmt"
+	// "strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	// "fohow.com/cache"
+)
+
+const (
+	push_after_subs_tablename = "push_after_subs"
+
+	AFTER_SUB_PUSH_IMAGE   = "image"
+	AFTER_SUB_PUSH_TEXT    = "text"
+	AFTER_SUB_PUSH_ARTICLE = "article"
+	AFTER_SUB_PUSH_POSTER  = "poster"
+)
+
+type PushAfterSub struct {
+	Id        int64     `orm:"column(id);pk"                                       json:"id"`              // int(11)
+	PushType  string    `orm:"column(push_type)"                                   json:"push_type"`       // varchar(255)
+	PushTitle string    `orm:"column(push_title);null"                                  json:"push_title"` // varchar(255)
+	Cover     string    `orm:"column(cover);null"                                  json:"cover"`           // varchar(255)
+	Word      string    `orm:"column(word);null"                                   json:"word"`            // varchar(255)
+	Url       string    `orm:"column(url);null"                                    json:"url"`             // varchar(255)
+	CreatedAt time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)" json:"created_at"`      // datetime
+	UpdatedAt time.Time `orm:"column(updated_at);null;auto_now;type(datetime)"     json:"updated_at"`      // datetime
+}
+
+func (self *PushAfterSub) TableName() string {
+	return push_after_subs_tablename
+}
+
+func GetPushAfterSubById(id int64) *PushAfterSub {
+	item := new(PushAfterSub)
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("id", id).Limit(1).
+		One(item); err != nil {
+		return nil
+	}
+	return item
+}
+
+func (self *PushAfterSub) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save PushAfterSub id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}

+ 9 - 0
go/gopath/src/fohow.com/apps/models/channel_parent_model/init.go

@@ -0,0 +1,9 @@
+package channel_parent_model
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(new(ParentSignUpChannel))
+}

+ 40 - 0
go/gopath/src/fohow.com/apps/models/channel_parent_model/parent_signup_channel.go

@@ -0,0 +1,40 @@
+package channel_parent_model
+
+import (
+	"fmt"
+	"github.com/astaxie/beego/orm"
+	"fohow.com/cache"
+	"time"
+)
+
+const (
+	parent_sign_up_channels_tablename = "parent_signup_channels"
+)
+
+type ParentSignUpChannel struct {
+	Id        int64     `orm:"column(id);pk"                                  json:"id,omitempty"`         // int(11)
+	Name      string    `orm:"column(name);null"                              json:"name,omitempty"`       // varchar(255)
+	Desc      string    `orm:"column(desc);null"                              json:"desc,omitempty"`       // varchar(255)
+	CreatedAt time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"created_at,omitempty"` // datetime
+	UpdatedAt time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"updated_at,omitempty"` // datetime
+}
+
+func (self *ParentSignUpChannel) TableName() string {
+	return parent_sign_up_channels_tablename
+}
+
+func GetParentSignUpChannelById(id int64, useCache bool) *ParentSignUpChannel {
+	k := fmt.Sprintf("user_model.GetSignUpChannelById(%d)", id)
+	if useCache {
+		if s, ok := cache.Cache.Get(k).(*ParentSignUpChannel); ok {
+			return s
+		}
+	}
+	item := new(ParentSignUpChannel)
+	o := orm.NewOrm()
+	if err := o.QueryTable(item).Filter("id", id).Limit(1).One(item); err != nil {
+		return nil
+	}
+	cache.Cache.Put(k, item, 10*time.Minute)
+	return item
+}

+ 11 - 0
go/gopath/src/fohow.com/apps/models/channel_xcx_qrcode_model/init.go

@@ -0,0 +1,11 @@
+package channel_xcx_qrcode_model
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(
+		new(SignUpChannel), new(SignUpChannelQrcodeResult),
+	)
+}

+ 47 - 0
go/gopath/src/fohow.com/apps/models/channel_xcx_qrcode_model/sign_up_channel.go

@@ -0,0 +1,47 @@
+package channel_xcx_qrcode_model
+
+import (
+	// "strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
+
+const (
+	channel_qrcodes_tablename = "sign_up_channels" //小程序码定义
+)
+
+type SignUpChannel struct {
+	Id           int64     `orm:"column(id);pk"                                  json:"id"`        // int(11)
+	ParentId     int64     `orm:"column(parent_id)"                              json:"parent_id"` // int(11)
+	ChannelValue string    `orm:"column(channel_value)"                          json:"channel_value"`
+	ChannelUrl   string    `orm:"column(channel_url)"                            json:"channel_url"`
+	UrlImage     string    `orm:"column(url_image)"                              json:"url_image"` // text
+	ScanTimes    int64     `orm:"column(scan_times)"                             json:"scan_times"`
+	InviteId     int64     `orm:"column(invite_id)"                             json:"invite_id"`
+	CreatedAt    time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"-"` // datetime
+	UpdatedAt    time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"-"` // datetime
+}
+
+func (self *SignUpChannel) TableName() string {
+	return channel_qrcodes_tablename
+}
+
+func GetById(id int64) *SignUpChannel {
+	item := new(SignUpChannel)
+	if err := orm.NewOrm().QueryTable(item).
+		Filter("id", id).Limit(1).OrderBy("-id").
+		One(item); err != nil {
+		return nil
+	}
+	return item
+}
+
+func (self *SignUpChannel) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save SignUpChannel id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}

+ 35 - 0
go/gopath/src/fohow.com/apps/models/channel_xcx_qrcode_model/sign_up_channel_qrcode_result.go

@@ -0,0 +1,35 @@
+package channel_xcx_qrcode_model
+
+import (
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	"time"
+)
+
+const (
+	channel_qrcode_results_tablename = "sign_up_channel_qrcode_results" //小程序码扫描结果
+)
+
+type SignUpChannelQrcodeResult struct {
+	Id              int64     `orm:"column(id);pk"                                  json:"id,omitempty"`                 // int(11)
+	WxUid           int64     `orm:"column(wx_uid);null"                            json:"wx_uid,omitempty"`             // varchar(255)
+	SignUpChannelId int64     `orm:"column(sign_up_channel_id);null"                json:"sign_up_channel_id,omitempty"` // int(11)
+	CreatedAt       time.Time `orm:"column(created_at);auto_now_add;type(datetime)" json:"created_at,omitempty"`         // datetime
+	UpdatedAt       time.Time `orm:"column(updated_at);auto_now;type(datetime)"     json:"updated_at,omitempty"`         // datetime
+}
+
+func (self *SignUpChannelQrcodeResult) TableName() string {
+	return channel_qrcode_results_tablename
+}
+
+func (self *SignUpChannelQrcodeResult) Create(signUpChannelId, wxUid int64) *SignUpChannelQrcodeResult {
+	item := &SignUpChannelQrcodeResult{
+		SignUpChannelId: signUpChannelId,
+		WxUid:           wxUid}
+	if _, err := orm.NewOrm().Insert(item); err != nil {
+		beego.BeeLogger.Info("insert SignUpChannelQrcodeResult err=[%s]", err)
+		return nil
+	}
+	beego.BeeLogger.Warn("channel_xcx_qrcode_model.Create(%v)", item)
+	return item
+}

+ 10 - 0
go/gopath/src/fohow.com/apps/models/key_word_push_model/init.go

@@ -0,0 +1,10 @@
+package key_word_push_model
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	// 需要在init中注册定义的model
+	orm.RegisterModel(new(KeyWordPush))
+}

+ 56 - 0
go/gopath/src/fohow.com/apps/models/key_word_push_model/key_word_push.go

@@ -0,0 +1,56 @@
+package key_word_push_model
+
+import (
+	// "fmt"
+	// "regexp"
+	// "strconv"
+	// "strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	// "fohow.com/cache"
+)
+
+const (
+	d5c_key_word_pushs_tablename = "key_word_pushs"
+
+	KEY_WORD_PUSH_IMAGE   = "image"
+	KEY_WORD_PUSH_TEXT    = "text"
+	KEY_WORD_PUSH_ARTICLE = "article"
+)
+
+type KeyWordPush struct {
+	Id               int64     `orm:"column(id);pk"                                       json:"id,omitempty"`         // int(11)
+	KeyWord          string    `orm:"column(key_word);null"                               json:"key_word,omitempty"`   // varchar(255)
+	PushType         string    `orm:"column(push_type)"                                   json:"push_type,omitempty"`  // varchar(255)
+	PushTitle        string    `orm:"column(push_title);null"                             json:"push_title,omitempty"` // varchar(255)
+	Cover            string    `orm:"column(cover);null"                                  json:"cover,omitempty"`      // varchar(255)
+	Word             string    `orm:"column(word);null"                                   json:"word,omitempty"`       // varchar(255)
+	Url              string    `orm:"column(url);null"                                    json:"url,omitempty"`        // varchar(255)
+	Remark           string    `orm:"column(remark)"                                      json:"remark,omitempty"`     // varchar(255)
+	WxGongzhonghaoId int64     `orm:"column(wx_gongzhonghao_id)"                          json:"wx_gongzhonghao_id"`
+	CreatedAt        time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)" json:"created_at,omitempty"` // datetime
+	UpdatedAt        time.Time `orm:"column(updated_at);null;auto_now;type(datetime)"     json:"updated_at,omitempty"` // datetime
+}
+
+func (self *KeyWordPush) TableName() string {
+	return d5c_key_word_pushs_tablename
+}
+
+func GetPushTmplByKey(key string) *KeyWordPush {
+	item := new(KeyWordPush)
+	o := orm.NewOrm()
+	if err := o.QueryTable(item).Filter("key_word", key).Limit(1).One(item); err != nil {
+		return nil
+	}
+	return item
+}
+
+func (self *KeyWordPush) Save() error {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save KeyWordPush id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}

+ 9 - 0
go/gopath/src/fohow.com/apps/models/merchant_model/init.go

@@ -0,0 +1,9 @@
+package merchant_model
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(new(Merchant), new(MerchantUserRelation))
+}

+ 45 - 0
go/gopath/src/fohow.com/apps/models/merchant_model/merchant.go

@@ -0,0 +1,45 @@
+package merchant_model
+
+import (
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
+
+const (
+	merchants_tablename = "merchants"
+)
+
+//2018/6/5 商家与用户一对一,修改成商家与用户一对多。新增商家-用户对应关系的表结构。
+type Merchant struct {
+	Id int64 `orm:"column(id);pk"                                         json:"id"`
+	//UserId    int64     `orm:"column(user_id);null"                                    json:"user_id"`
+	Name      string    `orm:"column(name)"                                          json:"name"`
+	Contact   string    `orm:"column(contact)"                                       json:"contact"`
+	Tel       string    `orm:"column(tel)"                                           json:"tel"`
+	CreatedAt time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)"   json:"-"`
+	UpdatedAt time.Time `orm:"column(updated_at);null;auto_now;type(datetime)"       json:"-"`
+}
+
+func (self *Merchant) TableName() string {
+	return merchants_tablename
+}
+
+/*func GetMerchantByUserId(userId int64) *Merchant {
+	merchant := new(Merchant)
+	if err := orm.NewOrm().QueryTable(merchant).Filter("user_id", userId).One(merchant); err != nil {
+		beego.BeeLogger.Info("GetMerchantByUserId(%d) err=[%s]", userId, err)
+		return nil
+	}
+	return merchant
+}*/
+
+func GetMerchantById(id int64) *Merchant {
+	merchant := new(Merchant)
+	if err := orm.NewOrm().QueryTable(merchant).Filter("id", id).One(merchant); err != nil {
+		beego.BeeLogger.Error("GetMerchantById(%d) err=[%s]", id, err)
+		return nil
+	}
+	return merchant
+}

+ 67 - 0
go/gopath/src/fohow.com/apps/models/merchant_model/merchant_user.go

@@ -0,0 +1,67 @@
+package merchant_model
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	"fohow.com/cache"
+	"time"
+)
+
+const (
+	merchant_user_relations_tablename = "merchant_user_relations"
+)
+
+type MerchantUserRelation struct {
+	Id               int64     `orm:"column(id);pk"                                       json:"id"`
+	UserId           int64     `orm:"column(user_id);"                                    json:"user_id"`
+	MerchantId       int64     `orm:"column(merchant_id)"                                 json:"merchant_id"`
+	IsSuperAdmin     bool      `orm:"column(is_super_admin)"                              json:"is_super_admin"`
+	ManageProductIds string    `orm:"column(manage_product_ids)"                     json:"-"`
+	IsEffect         int64     `orm:"column(is_effect)"                                   json:"-"`
+	CreatedAt        time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)"   json:"-"`
+	UpdatedAt        time.Time `orm:"column(updated_at);null;auto_now;type(datetime)"       json:"-"`
+}
+
+func (self *MerchantUserRelation) TableName() string {
+	return merchant_user_relations_tablename
+}
+
+//根据用户查询记录
+func GetMerchantUserRelationByUserId(userId int64, useCache bool) *MerchantUserRelation {
+
+	k := fmt.Sprintf("merchant_model.GetMerchantUserRelationByUserId(%d)", userId)
+	if useCache {
+		if v, ok := cache.Cache.Get(k).(*MerchantUserRelation); ok {
+			return v
+		}
+	}
+
+	merchantUserRelation := new(MerchantUserRelation)
+	if err := orm.NewOrm().QueryTable(merchantUserRelation).Filter("user_id", userId).Filter("is_effect", 1).Limit(1).One(merchantUserRelation); err != nil {
+		beego.BeeLogger.Info("GetMerchantUserRelationByUserId(%d) err=[%s]", userId, err)
+		return nil
+	}
+
+	cache.Cache.Put(k, merchantUserRelation, 1*time.Minute)
+	return merchantUserRelation
+}
+
+//根据商家查询记录
+func GetMerchantUserRelationListByMerchantId(merchantId int64, useCache bool) (list []*MerchantUserRelation) {
+
+	k := fmt.Sprintf("merchant_model.GetMerchantUserRelationListByMerchantId(%d)", merchantId)
+	if useCache {
+		if v, ok := cache.Cache.Get(k).([]*MerchantUserRelation); ok {
+			return v
+		}
+	}
+	merchantUserRelation := new(MerchantUserRelation)
+	if _, err := orm.NewOrm().QueryTable(merchantUserRelation).Filter("merchant_id", merchantId).Filter("is_effect", 1).All(&list); err != nil {
+		beego.BeeLogger.Info("GetMerchantUserRelationListByMerchantId(%d) err=[%s]", merchantId, err)
+		return nil
+	}
+
+	cache.Cache.Put(k, list, 1*time.Minute)
+	return list
+}

+ 133 - 0
go/gopath/src/fohow.com/apps/models/order_model/cart.go

@@ -0,0 +1,133 @@
+package order_model
+
+import (
+	// "fmt"
+	"github.com/astaxie/beego"
+	// "strconv"
+	// "strings"
+	"time"
+
+	"github.com/astaxie/beego/orm"
+	// "github.com/uuid"
+	// "fohow.com/apps/models/shop_model"
+	// "fohow.com/apps/models/user_model"
+)
+
+const (
+	carts_tablename = "carts"
+)
+
+type Cart struct {
+	Id            int64     `orm:"column(id);pk"                                       json:"id"`         // int(11)
+	ProductId     int64     `orm:"column(product_id)"                                  json:"product_id"` // int(11)
+	UserId        int64     `orm:"column(user_id)"                            json:"user_id"`             // varchar(64)
+	WxUserId      int64     `orm:"column(wx_user_id)"                                  json:"wx_user_id"`
+	CreatedAt     time.Time `orm:"column(created_at);null;auto_now_add;type(datetime)" json:"-"`    // datetime
+	Count         int64     `orm:"column(nums)"                                       json:"count"` // int(11)
+	IsBuy         bool      `orm:"column(is_buy)" 			json:"is_under_seckill"`
+	ProductName   string    `orm:"-"     json:"product_name"`   // decimal(10,2)
+	Cover         string    `orm:"-"     json:"cover"`          // decimal(10,2)
+	OriginalPrice int64     `orm:"-"     json:"original_price"` // decimal(10,2)
+	Attrs         string    `orm:"-"     json:"attrs"`          // decimal(10,2)
+}
+
+func (self *Cart) TableName() string {
+	return carts_tablename
+}
+
+//创建订单项
+func (self *Cart) Create(wxId, uId, pId,
+	pCount int64) *Cart {
+	item := GetCartItemsByUserIdAndPid(uId, pId)
+	if item == nil {
+		item = &Cart{
+			WxUserId:  wxId,
+			UserId:    uId,
+			IsBuy:     false,
+			Count:     pCount,
+			ProductId: pId}
+		id, err := orm.NewOrm().Insert(item)
+		if err != nil {
+			beego.BeeLogger.Error("insert cart item err=[%s]", err)
+			return nil
+		}
+		item.Id = id
+
+	} else {
+		item.Count += pCount
+		item.Save()
+	}
+	return item
+}
+
+func (self *Cart) Save() bool {
+	if _, err := orm.NewOrm().Update(self); err != nil {
+		beego.BeeLogger.Error("Save Cart=[%d] .err=[%s]", self.Id, err)
+		return false
+	}
+	return true
+}
+
+func (self *Cart) Delete() error {
+	o := orm.NewOrm()
+	if _, err := o.Delete(&Cart{Id: self.Id}); err != nil {
+		beego.BeeLogger.Error("Delete Cart id=[%d] .err=[%s]", self.Id, err)
+		return err
+	}
+	return nil
+}
+
+//根据会员Id,产品ID获取订单项
+func GetCartItemsByUserIdAndPid(uId, pid int64) (cart *Cart) {
+	cart = &Cart{}
+	if err := orm.NewOrm().QueryTable(cart).Filter("user_id", uId).Filter("product_id", pid).Limit(1).
+		One(cart); err != nil {
+		beego.BeeLogger.Error("get cart by user_id=%d  product_id=%d err=%s", uId, pid, err)
+		cart = nil
+	}
+	return cart
+}
+
+//根据会员Id获取所有订单项
+func GetCartItemsByUserId(uId int64) (items []*Cart) {
+	item := new(Cart)
+	if _, err := orm.NewOrm().QueryTable(item).
+		Filter("user_id", uId).All(&items); err != nil {
+		beego.BeeLogger.Debug("get cart[%d] items err=[%s]", uId, err)
+		return nil
+	}
+	return items
+}
+
+//根据会员ID及状态项,获取购物车列表
+func GetCartItemsByUserIdAndBuy(uId int64, isBuy bool) (items []*Cart) {
+	item := new(Cart)
+	if _, err := orm.NewOrm().QueryTable(item).
+		Filter("user_id", uId).Filter("is_buy", isBuy).All(&items); err != nil {
+		beego.BeeLogger.Debug("get cart[%d] items err=[%s]", uId, err)
+		return nil
+	}
+	return items
+}
+
+// 根据Id获取购物车记录
+func GetCartById(id int64) (cart *Cart) {
+	cart = &Cart{}
+	if err := orm.NewOrm().QueryTable(cart).Filter("id", id).Limit(1).
+		One(cart); err != nil {
+		beego.BeeLogger.Error("get cart by id=%d err=%s", id, err)
+		cart = nil
+	}
+	return cart
+}
+
+// 根据UserId,productId获取购物车记录
+func GetCartByUidAndPid(uId, pId int64) (cart *Cart) {
+	cart = &Cart{}
+	if err := orm.NewOrm().QueryTable(cart).Filter("user_id", uId).Filter("product_id", pId).Limit(1).
+		One(cart); err != nil {
+		beego.BeeLogger.Error("get cart by uId=%d err=%s", uId, err)
+		cart = nil
+	}
+	return cart
+}

+ 12 - 0
go/gopath/src/fohow.com/apps/models/order_model/init.go

@@ -0,0 +1,12 @@
+package order_model
+
+import (
+	// "time"
+
+	// "github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+)
+
+func init() {
+	orm.RegisterModel(new(Order), new(Cart), new(OrderDetail))
+}

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


Некоторые файлы не были показаны из-за большого количества измененных файлов