abiao лет назад: 6
Родитель
Сommit
c12adefcb9
100 измененных файлов с 14726 добавлено и 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. 266 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. 118 0
      go/gopath/src/fohow.com/apps/controllers/cf_controller/cf_controller.go
  14. 513 0
      go/gopath/src/fohow.com/apps/controllers/chunjie_controller/chunjie_controller.go
  15. 23 0
      go/gopath/src/fohow.com/apps/controllers/chunjie_controller/init.go
  16. 44 0
      go/gopath/src/fohow.com/apps/controllers/click_controller/click_controller.go
  17. 185 0
      go/gopath/src/fohow.com/apps/controllers/consume_czc_order_controller/consume_czc_order_controller.go
  18. 147 0
      go/gopath/src/fohow.com/apps/controllers/copartner_controller/copartner_controller.go
  19. 22 0
      go/gopath/src/fohow.com/apps/controllers/copartner_controller/init.go
  20. 431 0
      go/gopath/src/fohow.com/apps/controllers/course_controller/course_controller.go
  21. 24 0
      go/gopath/src/fohow.com/apps/controllers/course_controller/init.go
  22. 30 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/comb_user_relation.go
  23. 56 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/copartner.go
  24. 80 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/dollar_win_notice.go
  25. 54 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/dollar_win_rank.go
  26. 135 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/granary.go
  27. 86 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/init.go
  28. 175 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/invite.go
  29. 48 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/order_receive_automatically.go
  30. 46 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/register_wxuser.go
  31. 122 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/sync_balance.go
  32. 46 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/three_user_relation.go
  33. 105 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/update_invite_benefit_order.go
  34. 19 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/update_user_is_regist_d5c.go
  35. 155 0
      go/gopath/src/fohow.com/apps/controllers/cron_controller/user_complete_info.go
  36. 46 0
      go/gopath/src/fohow.com/apps/controllers/customer_service_controller/customer_service_controller.go
  37. 308 0
      go/gopath/src/fohow.com/apps/controllers/dollar_win_controller/dollar_win_controller.go
  38. 136 0
      go/gopath/src/fohow.com/apps/controllers/exchange_controller/callback_exchange_controller.go
  39. 383 0
      go/gopath/src/fohow.com/apps/controllers/exchange_controller/exchange_controller.go
  40. 103 0
      go/gopath/src/fohow.com/apps/controllers/exchange_controller/exchange_test.go
  41. 31 0
      go/gopath/src/fohow.com/apps/controllers/exchange_controller/init.go
  42. 71 0
      go/gopath/src/fohow.com/apps/controllers/form_id_controller/form_id_controller.go
  43. 528 0
      go/gopath/src/fohow.com/apps/controllers/granary_controller/granary_controller.go
  44. 31 0
      go/gopath/src/fohow.com/apps/controllers/granary_controller/init.go
  45. 22 0
      go/gopath/src/fohow.com/apps/controllers/invite_sale_controller/init.go
  46. 166 0
      go/gopath/src/fohow.com/apps/controllers/invite_sale_controller/invite_sale_controller.go
  47. 187 0
      go/gopath/src/fohow.com/apps/controllers/mp_controller/mp_controller.go
  48. 316 0
      go/gopath/src/fohow.com/apps/controllers/order_controller/cart_controller.go
  49. 31 0
      go/gopath/src/fohow.com/apps/controllers/order_controller/init.go
  50. 875 0
      go/gopath/src/fohow.com/apps/controllers/order_controller/order_controller.go
  51. 122 0
      go/gopath/src/fohow.com/apps/controllers/order_controller/settle_order_controller.go
  52. 744 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/after_pay_controller.go
  53. 47 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/init.go
  54. 52 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_balance_controller.go
  55. 48 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_cashcz_controller.go
  56. 121 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_controller.go
  57. 150 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_course_controller.go
  58. 184 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_dollar_win_controller.go
  59. 333 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_exchange_controller.go
  60. 233 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_invest_controller.go
  61. 63 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/pay_reserve_controller.go
  62. 127 0
      go/gopath/src/fohow.com/apps/controllers/pay_controller/recharge_controller.go
  63. 870 0
      go/gopath/src/fohow.com/apps/controllers/permit_controller/permit_controller.go
  64. 170 0
      go/gopath/src/fohow.com/apps/controllers/poster_controller/poster_controller.go
  65. 32 0
      go/gopath/src/fohow.com/apps/controllers/product_controller/init.go
  66. 246 0
      go/gopath/src/fohow.com/apps/controllers/product_controller/product_controller.go
  67. 22 0
      go/gopath/src/fohow.com/apps/controllers/project_controller/init.go
  68. 195 0
      go/gopath/src/fohow.com/apps/controllers/project_controller/invest_controller.go
  69. 85 0
      go/gopath/src/fohow.com/apps/controllers/project_controller/invest_way_controller.go
  70. 215 0
      go/gopath/src/fohow.com/apps/controllers/project_controller/project_controller.go
  71. 131 0
      go/gopath/src/fohow.com/apps/controllers/project_controller/project_join_controller.go
  72. 237 0
      go/gopath/src/fohow.com/apps/controllers/rabbit_prize_draw_act_controller/rabbit_prize_draw_act_controller.go
  73. 40 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/cache_controller.go
  74. 202 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/dollar_win_controller.go
  75. 175 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/generate_controller.go
  76. 34 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/intro_user_controller.go
  77. 54 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/order_dispatch_controller.go
  78. 16 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/send_controller.go
  79. 28 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/test_controller.go
  80. 13 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/test_test.go
  81. 431 0
      go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/tmpl_controller.go
  82. 151 0
      go/gopath/src/fohow.com/apps/controllers/refund_controller/refund_controller.go
  83. 298 0
      go/gopath/src/fohow.com/apps/controllers/reserve_act_controller/reserve_act_controller.go
  84. 154 0
      go/gopath/src/fohow.com/apps/controllers/seckill_controller/seckill_controller.go
  85. 57 0
      go/gopath/src/fohow.com/apps/controllers/share_controller/share_controller.go
  86. 152 0
      go/gopath/src/fohow.com/apps/controllers/share_material_controller/share_material_controller.go
  87. 148 0
      go/gopath/src/fohow.com/apps/controllers/sms_controller/sms_controller.go
  88. 22 0
      go/gopath/src/fohow.com/apps/controllers/subject_controller/init.go
  89. 206 0
      go/gopath/src/fohow.com/apps/controllers/subject_controller/product_sale_subject_controller.go
  90. 61 0
      go/gopath/src/fohow.com/apps/controllers/subject_controller/subject_sale_code_controller.go
  91. 23 0
      go/gopath/src/fohow.com/apps/controllers/test_controller/init.go
  92. 82 0
      go/gopath/src/fohow.com/apps/controllers/test_controller/test_controller.go
  93. 30 0
      go/gopath/src/fohow.com/apps/controllers/tool_controller/init.go
  94. 91 0
      go/gopath/src/fohow.com/apps/controllers/tool_controller/qrcode_controller.go
  95. 26 0
      go/gopath/src/fohow.com/apps/controllers/tool_controller/tool_controller.go
  96. 365 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/binding_controller.go
  97. 135 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/binding_wx_phone_controller.go
  98. 37 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/init.go
  99. 296 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/invite_controller.go
  100. 0 0
      go/gopath/src/fohow.com/apps/controllers/user_controller/one_click_binding_controller.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
+}

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

@@ -0,0 +1,266 @@
+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/exchange_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()
+}
+
+//给内部人员调用查询前一天兑换代金券总数
+func (self *BalanceController) GetYesterdayTotalPaiedCount() {
+	cache, _ := self.GetBool("cache", false)
+	now := time.Now()
+	yesterday, _ := time.Parse("2006-01-02", now.AddDate(0, 0, -1).Format("2006-01-02"))
+	today, _ := time.Parse("2006-01-02", now.Format("2006-01-02"))
+
+	total := exchange_model.GetYesterdayTotalPaiedCount(yesterday, today, cache)
+
+	type Ret struct {
+		TotalPaiedCount int64 `json:"total_paied_count"`
+	}
+	self.Data["json"] = &Ret{TotalPaiedCount: total}
+	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()
+}

+ 118 - 0
go/gopath/src/fohow.com/apps/controllers/cf_controller/cf_controller.go

@@ -0,0 +1,118 @@
+package cf_controller
+
+import (
+	"fmt"
+	// "net/url"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+	// "api.com/apps/controllers/user_controller"
+	// "api.com/apps/models/daoku_model"
+	// "api.com/apps/models/duobao_model"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/cf_model"
+	// "api.com/apps/models/user_model"
+	// "api.com/apps/models/vas_model"
+	// "api.com/libs/wx_mp"
+	// "api.com/cache"
+	"fohow.com/apps/models/user_model"
+)
+
+var (
+	//需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"Click", "ClickIP"}
+	exceptCheckWxUserLoginAction = []string{""}
+)
+
+type CfController struct {
+	apps.BaseController
+}
+
+func (self *CfController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+func (self *CfController) Click() {
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+	useCache, _ := self.GetBool("cache", false)
+	cfc := cf_model.GetCfc(id, useCache)
+
+	var uri string
+	uri = fmt.Sprintf("%s/?cfc=wrong", beego.AppConfig.String("MHost"))
+	if cfc == nil {
+		self.Redirect(uri, 302)
+		return
+	}
+
+	wxUserId := self.GetCurrentWxUserId()
+	if wxUserId > 0 && cfc.InviterId > 0 {
+		go helpers.SetInviter(wxUserId, cfc.InviterId)
+	}
+
+	// 配置了注册渠道的
+	if cfc.SignUpChannelId > 0 {
+
+		//注册使用此cookie
+		age := 3600 * 24 * 7
+		self.Ctx.SetCookie("sign_up_channel", fmt.Sprintf("%d", cfc.SignUpChannelId), age, "/",
+			beego.AppConfig.String("CookieDomain"), nil, true)
+
+		if wxUserId > 0 {
+			wxUser := user_model.GetWxUserById(wxUserId, true)
+			if wxUser != nil && wxUser.ChannelQrcodeId == 0 && time.Now().Unix()-wxUser.CreatedAt.Unix() < 60 {
+				wxUser.ChannelQrcodeId = cfc.SignUpChannelId
+				wxUser.Save()
+			}
+		}
+	}
+
+	uri = cfc.Url
+
+	ip := self.Ctx.Input.IP()
+	uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	cfc.ClickTimes = cfc.ClickTimes + 1
+	go cfc.Save()
+	cfuId := doClick(cfc.Id, uId, wxUId, ip)
+	if cfuId > 0 {
+		key := beego.AppConfig.String("CFName")
+		value := ""
+		c := self.Ctx.GetCookie(key)
+		if c == "" || !strings.Contains(c, fmt.Sprintf(",%d,", cfuId)) {
+			if c == "" {
+				value = fmt.Sprintf(",%d,", cfuId)
+			} else {
+				value = fmt.Sprintf("%s%d,", c, cfuId)
+			}
+			age := 3600 * 24
+			self.Ctx.SetCookie(key, value, age, "/",
+				beego.AppConfig.String("CookieDomain"), nil, true)
+		}
+	}
+	self.Redirect(uri, 302)
+}
+
+func doClick(cfcId, uId, wxUId int64, ip string) int64 {
+	cfu := cf_model.GetCfcUserByWxUId(cfcId, wxUId)
+	if cfu == nil {
+		cfu = new(cf_model.ClickFromUser).Create(cfcId, uId, wxUId, ip)
+	}
+	if cfu != nil {
+		cfu.ClickTimes = cfu.ClickTimes + 1
+		cfu.ClickLastTime = time.Now()
+		go cfu.Save()
+		return cfu.Id
+	}
+	return 0
+}
+
+func (self *CfController) ClickIP() {
+	self.Click()
+}

+ 513 - 0
go/gopath/src/fohow.com/apps/controllers/chunjie_controller/chunjie_controller.go

@@ -0,0 +1,513 @@
+package chunjie_controller
+
+import (
+	"strconv"
+	"time"
+
+	"fmt"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/chunjie_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/cache"
+	"fohow.com/libs/tool"
+)
+
+const (
+	BIG_MONEY_TYPE             = int64(1)
+	SMALL_MONEY_TYPE           = int64(0)
+	SELF_OPEN_MONEY_SOURCE     = "SELF_OPEN"
+	INVITE_REWARD_MONEY_SOURCE = "INVITE_REWARD"
+	ACTIVITY_STATE_ON          = int64(1)
+	PARTICIPANT_IS_NEW         = int64(1)
+)
+
+//获取活动信息
+func (self *ChunjieController) GetConfigInfo() {
+	_configId := self.Ctx.Input.Param(":config_id")
+	configId, _ := strconv.ParseInt(_configId, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	config := chunjie_model.GetConfigById(configId, useCache)
+	inviteId, _ := self.GetInt64("invite_id", 0)
+
+	//self.ReturnError(403, apps.ActivityIsEnd, "", nil)
+
+	if config == nil {
+		beego.BeeLogger.Error("-------------------活动不存在----------")
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	//wxUser := user_model.GetByOpenid("o8HkVwAB4bNNuonBIozyZ_TcG99o", useCache)
+	if wxUser == nil {
+		beego.BeeLogger.Error("-------------------微信用户不存在----------")
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	participant := chunjie_model.GetParticipantByCIdAndWUId(config.Id, wxUser.Id, useCache)
+	// 若活动还在进行中,则初始化参加记录
+	now := time.Now().Unix()
+	if now > config.StartTime.Unix() && now < config.StopTime.Unix() && config.State == ACTIVITY_STATE_ON {
+
+		if participant == nil {
+			//判断是否是新人
+			isNew := int64(1)
+			if wxUser.IsRegisterInD5C() {
+				isNew = int64(0)
+			}
+			//记录IP
+			ip := self.Ctx.Input.IP()
+			if inviteId != 0 {
+				//校验inviteId一定要存在活动参与表里面,不能随便写
+				inviteParticipant := chunjie_model.GetParticipantByCIdAndWUId(config.Id, inviteId, useCache)
+				if inviteParticipant == nil {
+					beego.BeeLogger.Warn("-------------------邀请用户不存在----------")
+					inviteId = int64(0)
+				}
+			}
+			participant = new(chunjie_model.ChunjieParticipant).Create(config.Id, wxUser.Id, inviteId, isNew, ip)
+		}
+
+	}
+	type Ret struct {
+		Config      *chunjie_model.ChunjieConfig      `json:"act"`         //本次活动信息
+		Participant *chunjie_model.ChunjieParticipant `json:"participant"` //活动参与记录
+	}
+	self.Data["json"] = &Ret{Config: config, Participant: participant}
+	self.ServeJSON()
+}
+
+//生成红包记录
+func (self *ChunjieController) RobLuckyMoney() {
+	_configId := self.Ctx.Input.Param(":config_id")
+	configId, _ := strconv.ParseInt(_configId, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	config := chunjie_model.GetConfigById(configId, useCache)
+
+	//活动是否存在
+	if config == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	//活动是否开始和结束
+	now := time.Now().Unix()
+	if now < config.StartTime.Unix() {
+		self.ReturnError(403, apps.ActivityNotStart, "", nil)
+	}
+	if now > config.StopTime.Unix() {
+		self.ReturnError(403, apps.ActivityIsEnd, "", nil)
+	}
+	if config.State != ACTIVITY_STATE_ON {
+		self.ReturnError(403, apps.ActivityIsEnd, "", nil)
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	//wxUser := user_model.GetByOpenid("o8HkVwAB4bNNuonBIozyZ_TcG99o", useCache)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	//没有参加记录,非法
+	participant := chunjie_model.GetParticipantByCIdAndWUId(config.Id, wxUser.Id, useCache)
+	if participant == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	//是否已经领取
+	isJoinBefore := chunjie_model.IsJoinBefore(config.Id, wxUser.Id, useCache)
+	if isJoinBefore {
+		self.ReturnError(403, apps.HasGotLuckyMoney, "", nil)
+	}
+	//拿红包
+	//根据红包领取规则获取红包类型和金额
+	selfGetLuckyMoney := CreateRandSelfOpenLuckyMoney(participant, config, SELF_OPEN_MONEY_SOURCE)
+
+	//红包类型是大红包
+	relateLuckyMoney := int64(0)
+	if selfGetLuckyMoney.MoneyType == BIG_MONEY_TYPE {
+		//是否有邀请者(不是0和当前微信用户主键)
+		if participant.InviteWxUId != 0 && participant.InviteWxUId != participant.WxUId {
+			inviteWxUser := user_model.GetWxUserById(participant.InviteWxUId, useCache)
+
+			if inviteWxUser == nil {
+				self.ReturnError(403, apps.Error, "", nil)
+			}
+			//为邀请者生成红包记录
+			inviteGetLuckyMoney := CreateInviteRewardRandLuckyMoney(selfGetLuckyMoney, participant, config, INVITE_REWARD_MONEY_SOURCE, useCache)
+			//给邀请者发一个红包
+			inviteRewardLuckyMoney := new(chunjie_model.ChunjieLuckyMoney).Create(config.Id, inviteWxUser.Id, inviteGetLuckyMoney.MoneyType, inviteGetLuckyMoney.ReceiveBonus, int64(0), inviteGetLuckyMoney.MoneySource)
+			if inviteRewardLuckyMoney == nil {
+			}
+			//添加一条推送通知
+			helpers.UserBalanceChangedNotify(*inviteWxUser, tool.Int64ToFloat64ToString(inviteRewardLuckyMoney.ReceiveBonus, 100)+"元", "新春红包", "哇!你的好友拿了一个大红包,你也得到了一个大红包!", "pages/start/start?url=pages/activity/redpacket/redpacket&rid=1")
+			cache.Cache.Delete(fmt.Sprintf("chunjie_model.GetSelfReceiveBonus[%d,%d]", configId, inviteWxUser.Id))
+			//用户自己打开的大红包记录,要记住给邀请者奖励的红包金额
+			relateLuckyMoney = inviteGetLuckyMoney.ReceiveBonus
+
+		}
+	}
+
+	//给自己发一个红包
+	selfOpenLuckyMoney := new(chunjie_model.ChunjieLuckyMoney).Create(config.Id, wxUser.Id, selfGetLuckyMoney.MoneyType, selfGetLuckyMoney.ReceiveBonus, relateLuckyMoney, selfGetLuckyMoney.MoneySource)
+	if selfOpenLuckyMoney == nil {
+		return
+	}
+
+	//推送一条通知
+	helpers.UserBalanceChangedNotify(*wxUser, tool.Int64ToFloat64ToString(selfOpenLuckyMoney.ReceiveBonus, 100)+"元", "新春红包", "找好友一起拿大红包!好友拿了大红包,你一定少不了!", "pages/start/start?url=pages/activity/redpacket/redpacket&rid=1")
+
+	//清除自己的红包记录缓存
+	cache.Cache.Delete(fmt.Sprintf("chunjie_model.GetLuckyMoneyItemByCIdAndWxUId[%d,%d]", config.Id, wxUser.Id))
+	cache.Cache.Delete(fmt.Sprintf("chunjie_model.GetSelfReceiveBonus[%d,%d]", configId, wxUser.Id))
+
+	//返回红包记录
+	type Ret struct {
+		SelfOpenLuckyMoney *chunjie_model.ChunjieLuckyMoney `json:"self_open_lucky_money"`
+	}
+
+	self.Data["json"] = &Ret{SelfOpenLuckyMoney: selfOpenLuckyMoney}
+	self.ServeJSON()
+}
+
+//获取自己领取的红包类型和金额
+func CreateRandSelfOpenLuckyMoney(participant *chunjie_model.ChunjieParticipant, config *chunjie_model.ChunjieConfig, moneySource string) *chunjie_model.ChunjieLuckyMoney {
+	if participant == nil || config == nil {
+		return nil
+	}
+
+	//SELF_OPEN
+	suffix := strconv.FormatInt(participant.Id%10, 10)
+	isSatisfyNumbers := tool.JudgeEndInCharacter(suffix, config.LuckyNumbers)
+	isNew := participant.IsNew == PARTICIPANT_IS_NEW
+	moneyType := SMALL_MONEY_TYPE
+	receiveBonus := int64(0)
+	if moneySource == SELF_OPEN_MONEY_SOURCE {
+
+		if isSatisfyNumbers {
+			moneyType = BIG_MONEY_TYPE
+			if isNew {
+				receiveBonus = tool.RandInt64(config.BigNewBonusMin, config.BigNewBonusMax)
+			} else {
+				receiveBonus = tool.RandInt64(config.BigOldBonusMin, config.BigOldBonusMax)
+			}
+		} else {
+			moneyType = SMALL_MONEY_TYPE
+			if isNew {
+				receiveBonus = tool.RandInt64(config.SmallNewBonusMin, config.SmallNewBonusMax)
+			} else {
+				receiveBonus = tool.RandInt64(config.SmallOldBonusMin, config.SmallOldBonusMax)
+			}
+
+		}
+
+	}
+	chunjieLuckyMoney := new(chunjie_model.ChunjieLuckyMoney)
+	chunjieLuckyMoney.MoneyType = moneyType
+	chunjieLuckyMoney.MoneySource = moneySource
+	chunjieLuckyMoney.ReceiveBonus = receiveBonus
+	return chunjieLuckyMoney
+}
+
+//获取给邀请人奖励的红包金额:受邀者获取的大红包的一个随机比例,比例可以配置
+func CreateInviteRewardRandLuckyMoney(selfOpenLuckyMoney *chunjie_model.ChunjieLuckyMoney, participant *chunjie_model.ChunjieParticipant, config *chunjie_model.ChunjieConfig, moneySource string, useCache bool) *chunjie_model.ChunjieLuckyMoney {
+
+	if selfOpenLuckyMoney == nil || config == nil || participant == nil {
+		return nil
+	}
+
+	//INVITE_REWARD_MONEY_SOURCE
+	moneyType := int64(-1) //奖励红包不分大小红包
+	receiveBonus := int64(0)
+	inviterAllReceiveBonus := chunjie_model.GetAllReceiveBonus(config.Id, participant.InviteWxUId, useCache)
+	isOverLimit := inviterAllReceiveBonus > config.BonusLimit
+	if moneySource == INVITE_REWARD_MONEY_SOURCE {
+		if isOverLimit {
+			receiveBonus = int64(1)
+		} else {
+			rewardProportion := tool.RandInt64(config.RewardProportionMin, config.RewardProportionMax)
+			receiveBonus = selfOpenLuckyMoney.ReceiveBonus * rewardProportion / 100
+		}
+	}
+	chunjieLuckyMoney := new(chunjie_model.ChunjieLuckyMoney)
+	chunjieLuckyMoney.MoneyType = moneyType
+	chunjieLuckyMoney.MoneySource = moneySource
+	chunjieLuckyMoney.ReceiveBonus = receiveBonus
+	return chunjieLuckyMoney
+
+}
+
+//获取个人总金额(自己领取的+邀请奖励的)
+func (self *ChunjieController) GetTotalLuckyMoney() {
+	_configId := self.Ctx.Input.Param(":config_id")
+	configId, _ := strconv.ParseInt(_configId, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+
+	config := chunjie_model.GetConfigById(configId, useCache)
+	if config == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	type Ret struct {
+		AllReceiveBonus   int64 `json:"self_receive_bonus"`
+		InviteRewardBonus int64 `json:"invite_reward_bonus"`
+	}
+
+	wxUser := self.GetCurrentWxUser(true)
+	//wxUser := user_model.GetByOpenid("omtkY0SeyOmvAzgLcq84LtwbhwJs", useCache)
+	if wxUser == nil {
+		self.Data["json"] = &Ret{AllReceiveBonus: 0, InviteRewardBonus: 0}
+	} else {
+		allReceiveBonus := chunjie_model.GetAllReceiveBonus(config.Id, wxUser.Id, useCache)
+		self.Data["json"] = &Ret{AllReceiveBonus: allReceiveBonus, InviteRewardBonus: 0}
+	}
+	self.ServeJSON()
+}
+
+//获取最新当前序号
+func (self *ChunjieController) GetCurrentSequenceNumber() {
+	_configId := self.Ctx.Input.Param(":config_id")
+	configId, _ := strconv.ParseInt(_configId, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+
+	config := chunjie_model.GetConfigById(configId, useCache)
+	if config == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	lastInsertId := chunjie_model.GetLastInsertId(config.Id)
+
+	type Ret struct {
+		Participant int64 `json:"newest_participant"` //最新参与记录
+	}
+	self.Data["json"] = &Ret{Participant: lastInsertId}
+	self.ServeJSON()
+}
+
+//邀请者列表
+func (self *ChunjieController) GetMeAndFriendLuckyMoneyList() {
+
+	_configId := self.Ctx.Input.Param(":config_id")
+	configId, _ := strconv.ParseInt(_configId, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+
+	config := chunjie_model.GetConfigById(configId, useCache)
+	if config == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	type Item struct {
+		Rank          int64                         `json:"rank"`
+		WxUser        *user_model.WxUser            `json:"wx_user"`
+		LuckMoneyItem *chunjie_model.LuckyMoneyItem `json:"lucky_money"`
+		IsMe          int64                         `json:"is_me"`
+	}
+
+	var itemList []*Item = []*Item{}
+	itemListCount := int64(0)
+
+	var luckyMoneyList []*chunjie_model.LuckyMoneyItem
+	//用户不存在
+	wxUser := self.GetCurrentWxUser(true)
+	//wxUser := user_model.GetByOpenid("omtkY0SeyOmvAzgLcq84LtwbhwJs", useCache)
+	if wxUser == nil {
+		itemListCount = 0
+	} else {
+		//我与好友的红包记录
+		luckyMoneyList = chunjie_model.GetInviteLuckyMoneyList(config.Id, wxUser.Id, page, perPage, useCache)
+		itemListCount = chunjie_model.GetInviteLuckyMoneyListCount(config.Id, wxUser.Id, useCache)
+		if luckyMoneyList != nil {
+			for _, each := range luckyMoneyList {
+				item := new(Item)
+				item.Rank = each.Rank //(page-1)*perPage + (int64(i) + 1)
+
+				each.InviteRewardBonus = each.RelateId
+				item.LuckMoneyItem = each
+
+				wxUserEach := user_model.GetWxUserById(each.WxUId, useCache)
+				if wxUserEach != nil {
+					wxUserEach.Head = user_model.GetFullImgUrl(wxUserEach.Head)
+				}
+				item.WxUser = wxUserEach
+				//标志自己的红包
+				if each.WxUId == wxUser.Id {
+					item.IsMe = int64(1)
+				} else {
+					item.IsMe = int64(0)
+				}
+				itemList = append(itemList, item)
+			}
+		}
+	}
+	type Ret struct {
+		ItemList      []*Item `json:"list"`
+		ItemListCount int64   `json:"list_count"`
+	}
+	self.Data["json"] = &Ret{ItemList: itemList, ItemListCount: itemListCount}
+	self.ServeJSON()
+}
+
+//领取红包列表
+func (self *ChunjieController) GetOpenLuckyMoneyList() {
+	_configId := self.Ctx.Input.Param(":config_id")
+	configId, _ := strconv.ParseInt(_configId, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+
+	config := chunjie_model.GetConfigById(configId, useCache)
+	if config == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	type Item struct {
+		Rank          int64                         `json:"rank"`
+		WxUser        *user_model.WxUser            `json:"wx_user"`
+		LuckMoneyItem *chunjie_model.LuckyMoneyItem `json:"lucky_money"`
+		IsMe          int64                         `json:"is_me"`
+	}
+
+	var itemList []*Item = []*Item{}
+	itemListCount := int64(0)
+	var luckyMoneyList []*chunjie_model.LuckyMoneyItem
+
+	//所有领取人红包记录
+	luckyMoneyList = chunjie_model.GetReceiveLuckyMoneyList(config.Id, page, perPage, useCache)
+	itemListCount = chunjie_model.GetReceiveLuckyMoneyListCount(config.Id, useCache)
+
+	if luckyMoneyList != nil {
+		for _, each := range luckyMoneyList {
+			item := new(Item)
+			item.Rank = each.Rank
+			item.LuckMoneyItem = each
+			wxUser := user_model.GetWxUserById(each.WxUId, useCache)
+			if wxUser != nil {
+				wxUser.Head = user_model.GetFullImgUrl(wxUser.Head)
+			}
+			item.WxUser = wxUser
+			itemList = append(itemList, item)
+		}
+	}
+
+	type Ret struct {
+		ItemList      []*Item `json:"list"`
+		ItemListCount int64   `json:"list_count"`
+	}
+
+	self.Data["json"] = &Ret{ItemList: itemList, ItemListCount: itemListCount}
+	self.ServeJSON()
+}
+
+//发放总金额
+func (self *ChunjieController) GetGiveOutMoney() {
+	_configId := self.Ctx.Input.Param(":config_id")
+	configId, _ := strconv.ParseInt(_configId, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	config := chunjie_model.GetConfigById(configId, useCache)
+	if config == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	type Ret struct {
+		GiveOutTotalMoney int64 `json:"give_out_total_money"`
+	}
+	giveOutTotalMoney := chunjie_model.GetTotalGiveOutMoneyByConfigId(config.Id, useCache)
+	self.Data["json"] = &Ret{GiveOutTotalMoney: giveOutTotalMoney}
+	self.ServeJSON()
+}
+
+func (self *ChunjieController) Stat() {
+	configId, _ := strconv.ParseInt(self.Ctx.Input.Param(":config_id"), 10, 64)
+	config := chunjie_model.GetConfigById(configId, true)
+
+	type StatInfo struct {
+		StartTimestamp          int64 `json:"start_timestamp"`            // 活动开始时间
+		EndTimestamp            int64 `json:"end_timestamp"`              // 活动结束时间
+		ParticipantsCount       int64 `json:"participantsCount"`          // 总参加人数
+		ParticipantsNewCount    int64 `json:"participants_new_count"`     // 参加新人数
+		IsInvitedCount          int64 `json:"is_invited_count"`           // 被邀请参加人数
+		HongbaoCount            int64 `json:"hongbao_count"`              // 红包个数
+		HongbaoMoneyTotal       int64 `json:"hongbao_money_total"`        // 已发放红包总额
+		HongbaoBigCount         int64 `json:"hongbao_big_count"`          // "大"红包个数数量
+		HongbaoBigMoneyTotal    int64 `json:"hongbao_big_money_total"`    // "大"红包已发放总额
+		HongbaoCommonCount      int64 `json:"hongbao_common_count"`       // "普通"红包个数数量
+		HongbaoCommonMoneyTotal int64 `json:"hongbao_common_money_total"` // "普通"红包已发放总额
+		HongbaoNotOpenCount     int64 `json:"hongbao_not_open_count"`     // 红包未拆总数
+	}
+	statInfo := StatInfo{}
+	self.Data["json"] = &statInfo
+	statInfo.StartTimestamp = config.StartTime.Unix()
+	statInfo.EndTimestamp = config.StopTime.Unix()
+	statInfo.ParticipantsCount = chunjie_model.GetLastInsertId(configId)
+	statInfo.HongbaoMoneyTotal = chunjie_model.GetTotalGiveOutMoneyByConfigId(configId, false)
+
+	type TmpValue struct {
+		Value int64
+	}
+	tmpValue := &TmpValue{}
+	//sql := fmt.Sprintf(" select sum(receive_bonus) as total from %s where config_id = ? ", "chunjie_lucky_moneys")
+	o := orm.NewOrm()
+
+	sql := fmt.Sprintf(" select count(*) as value from chunjie_participants where config_id = ? AND invite_wx_uid>0")
+	o.Raw(sql, configId).QueryRow(tmpValue)
+	statInfo.IsInvitedCount = tmpValue.Value
+
+	sql = fmt.Sprintf(" select sum(receive_bonus) as value from chunjie_lucky_moneys where config_id = ?  AND money_type=?")
+	o.Raw(sql, configId, BIG_MONEY_TYPE).QueryRow(tmpValue)
+	statInfo.HongbaoBigMoneyTotal = tmpValue.Value
+	statInfo.HongbaoCommonMoneyTotal = statInfo.HongbaoMoneyTotal - statInfo.HongbaoBigMoneyTotal
+
+	sql = fmt.Sprintf(" select count(*) as value from chunjie_lucky_moneys where config_id = ?")
+	o.Raw(sql, configId).QueryRow(tmpValue)
+	statInfo.HongbaoCount = tmpValue.Value
+
+	sql = fmt.Sprintf(" select count(*) as value from chunjie_lucky_moneys where config_id = ?  AND money_type=?")
+	o.Raw(sql, configId, BIG_MONEY_TYPE).QueryRow(tmpValue)
+	statInfo.HongbaoBigCount = tmpValue.Value
+	statInfo.HongbaoCommonCount = statInfo.HongbaoCount - statInfo.HongbaoBigCount
+	statInfo.HongbaoNotOpenCount = statInfo.ParticipantsCount - statInfo.HongbaoCount
+
+	sql = fmt.Sprintf(" select count(*) as value from chunjie_participants where config_id = ? AND is_new=1")
+	o.Raw(sql, configId).QueryRow(tmpValue)
+	statInfo.ParticipantsNewCount = tmpValue.Value
+
+	self.ServeJSON()
+}
+
+func (self *ChunjieController) ReportTest() {
+	var _list []*chunjie_model.LuckyMoneyItem
+	startIndex, perPage := 0, 10000
+	sql := fmt.Sprintf(" SELECT cp.wx_uid,clm.money_source,clm.money_type,clm.receive_bonus,clm.relate_id FROM `chunjie_participants` cp LEFT JOIN `chunjie_lucky_moneys` clm on clm.wx_uid = cp.wx_uid WHERE cp.config_id = 1 AND clm.money_type = '1' AND clm.`money_source`='SELF_OPEN' limit %d,%d", startIndex, perPage)
+	o := orm.NewOrm()
+	_, err := o.Raw(sql).QueryRows(&_list)
+	if err != nil {
+	}
+
+	for _, each := range _list {
+		bonus := each.RelateId // 这个是给予邀请这的奖励金额
+		participant := chunjie_model.GetParticipantByCIdAndWUId(int64(1), each.WxUId, false)
+
+		inviteWxUId := participant.InviteWxUId
+		// 查找邀请者,是否有相应红包记录,
+		lm := new(chunjie_model.ChunjieLuckyMoney)
+		count, _ := orm.NewOrm().QueryTable(lm).Filter("config_id", 1).Filter("money_source", chunjie_model.INVITE_REWARD_MONEY_SOURCE).Filter("wx_uid", inviteWxUId).Filter("receive_bonus", bonus).Count()
+
+		recordId := int64(0)
+		if count > 0 {
+			beego.BeeLogger.Info("(((((has record)))))")
+		} else {
+			beego.BeeLogger.Info("(((((no record)))))")
+			// 增加红包记录
+			item := new(chunjie_model.ChunjieLuckyMoney).Create(int64(1), inviteWxUId, -1, bonus, int64(0), chunjie_model.INVITE_REWARD_MONEY_SOURCE)
+			if item != nil {
+				recordId = item.Id
+			}
+		}
+		beego.BeeLogger.Error("MoneyType[%d]--WxUId=[%d]--bonus=[%d]--inveiteWxUserId[%d]--recordId=[%d]", each.MoneyType, each.WxUId, bonus, inviteWxUId, recordId)
+	}
+	self.ServeJSON()
+
+}

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

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

+ 44 - 0
go/gopath/src/fohow.com/apps/controllers/click_controller/click_controller.go

@@ -0,0 +1,44 @@
+package click_controller
+
+import (
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+	"fohow.com/apps/models/click_model"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction   = []string{"DoClick"}
+	exceptCheckWxUserLoginAction = []string{"DoClick"}
+)
+
+type ClickController struct {
+	apps.BaseController
+}
+
+func (self *ClickController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+func (self *ClickController) DoClick() {
+
+	cc := self.GetString("cc")
+	ri := self.GetString("ri")
+	rp := self.GetString("rp")
+
+	uId := self.GetCurrentWxUserId()
+
+	click := click_model.GetClickInfoByWxUIdAndRelateCodeAndRelateId(cc, ri, rp, uId)
+
+	if click == nil {
+		new(click_model.ClickInfo).Insert(cc, ri, rp, uId)
+	} else {
+		click.ClickTimes = click.ClickTimes + 1
+		click.Save()
+	}
+
+	self.Data["json"] = click
+	self.ServeJSON()
+}

+ 185 - 0
go/gopath/src/fohow.com/apps/controllers/consume_czc_order_controller/consume_czc_order_controller.go

@@ -0,0 +1,185 @@
+package consume_czc_order_controller
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/base64"
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/pay_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/user_model"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"regexp"
+	"strconv"
+	"time"
+)
+
+var (
+	//需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"OrderLabiProduct"}
+	exceptCheckWxUserLoginAction = []string{"OrderLabiProduct"}
+)
+
+type ConsumeCzcOrderController struct {
+	apps.BaseController
+}
+
+func (self *ConsumeCzcOrderController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+//第五创奶油平台筹中筹,支付时(个人购买/团购众筹)收货地址时在FOHOW玖玖商城下单。
+func (self *ConsumeCzcOrderController) OrderLabiProduct() {
+	key := beego.AppConfig.String("CzcOrderProductKey")
+	// key := []byte("1234567890123456")
+	params := self.GetString("params")
+	ciphertext, _ := base64.StdEncoding.DecodeString(params)
+	block, err := aes.NewCipher([]byte(key))
+	if err != nil {
+		beego.BeeLogger.Error("aes newcipher err:%s", err)
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	// The IV needs to be unique, but not secure. Therefore it's common to
+	// include it at the beginning of the ciphertext.
+	if len(ciphertext) < aes.BlockSize {
+		beego.BeeLogger.Error("ciphertext too short")
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	iv := ciphertext[:aes.BlockSize]
+	ciphertext = ciphertext[aes.BlockSize:]
+
+	stream := cipher.NewCFBDecrypter(block, iv)
+
+	// XORKeyStream can work in-place if the two arguments are the same.
+	stream.XORKeyStream(ciphertext, ciphertext)
+	// beego.BeeLogger.Warn("plain params:%s", ciphertext)
+	type LabiOrderProductReturn struct {
+		RetCode string `json:"ret_code"`
+		RetMsg  string `json:"ret_msg"`
+		OrderId string `json:"order_id"`
+	}
+
+	regTel := regexp.MustCompile(`tel=([0-9]+)`)
+	matchTel := regTel.FindSubmatch([]byte(ciphertext))
+
+	regOId := regexp.MustCompile(`orderid=([A-Za-z0-9]+)`)
+	matchcOId := regOId.FindSubmatch([]byte(ciphertext))
+
+	regAddr := regexp.MustCompile(`address=([a-z0-9A-Z\p{Han}]+((,|,|-|_| |(|)|\)|\()[a-z0-9A-Z\p{Han}]+)*)`)
+	matchAddr := regAddr.FindSubmatch([]byte(ciphertext))
+
+	regContact := regexp.MustCompile(`contact=([a-z0-9A-Z\p{Han}]+((,|,|-|_| |(|)|\)|\()[a-z0-9A-Z\p{Han}]+)*)`)
+	matchContact := regContact.FindSubmatch([]byte(ciphertext))
+
+	regCount := regexp.MustCompile(`count=([0-9]+)`)
+	matchcCount := regCount.FindSubmatch([]byte(ciphertext))
+
+	regUserId := regexp.MustCompile(`userid=([0-9]+)`)
+	matchcUserId := regUserId.FindSubmatch([]byte(ciphertext))
+
+	if len(matchTel) < 2 || len(matchcOId) < 2 || len(matchAddr) < 2 || len(matchContact) < 2 || len(matchcCount) < 2 || len(matchcUserId) < 2 {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0001",
+			RetMsg:  "参数错误",
+			OrderId: "",
+		}, true, true)
+		self.StopRun()
+	}
+
+	_count := fmt.Sprintf("%s", matchcCount[1])
+	count, _ := strconv.ParseInt(_count, 10, 64)
+	_userId := fmt.Sprintf("%s", matchcUserId[1])
+	userId, _ := strconv.ParseInt(_userId, 10, 64)
+	orderId := fmt.Sprintf("%s", matchcOId[1])
+	tel := fmt.Sprintf("%s", matchTel[1])
+	address := fmt.Sprintf("%s", matchAddr[1])
+	contact := fmt.Sprintf("%s", matchContact[1])
+
+	if count == 0 || userId == 0 {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0001",
+			RetMsg:  "参数错误",
+			OrderId: orderId,
+		}, true, true)
+		self.StopRun()
+	}
+
+	remark := fmt.Sprintf("czc_%s_%d", orderId, userId)
+	existOrder := order_model.GetOrderByOrderRemark(remark, true)
+	if existOrder != nil {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0002",
+			RetMsg:  "不能重复下单",
+			OrderId: orderId,
+		}, true, true)
+		self.StopRun()
+	}
+
+	//写死产品id和下单员id
+	productId := int64(60)
+	orderUserId := int64(603363)
+	beego.BeeLogger.Warn("---OrderProductInRabbit---runmode:%s", beego.BConfig.RunMode)
+	if beego.BConfig.RunMode == beego.DEV {
+		productId = int64(24)
+		orderUserId = int64(600116)
+	}
+	labiProduct := product_model.GetProductById(productId, true)
+
+	beego.BeeLogger.Warn("-----%v", labiProduct)
+
+	wxUserId := int64(0)
+	wxUser := user_model.GetWxUserByUserId(orderUserId, true)
+	if wxUser != nil {
+		wxUserId = wxUser.Id
+	}
+
+	order := new(order_model.Order).Create(wxUserId, orderUserId, labiProduct.Id, count,
+		labiProduct.RoboBalancePrice*count, labiProduct.BuyPrice*count, labiProduct.Price, labiProduct.RoboBalancePrice, order_model.SOURCE_D5C_SYS)
+
+	if order == nil {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0003",
+			RetMsg:  "下单失败",
+			OrderId: orderId,
+		}, true, true)
+		self.StopRun()
+	}
+
+	order.Status = order_model.STATUS_PROCESSING
+	order.PaiedAt = time.Now().Unix()
+	order.PaiedPrice = order.TotalPrice
+	order.PayWay = pay_model.PAYWAY_BALANCE
+	order.OrderRemark = remark
+	order.Address = address
+	order.Contact = contact
+	order.Tel = tel
+
+	if !order.Save() {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0003",
+			RetMsg:  "下单失败",
+			OrderId: orderId,
+		}, true, true)
+		self.StopRun()
+	}
+
+	ok := &LabiOrderProductReturn{
+		RetCode: "0000",
+		RetMsg:  "下单成功",
+		OrderId: order.OrderId,
+	}
+	self.Ctx.Output.SetStatus(200)
+	self.Ctx.Output.JSON(ok, true, true)
+	self.StopRun()
+}

+ 147 - 0
go/gopath/src/fohow.com/apps/controllers/copartner_controller/copartner_controller.go

@@ -0,0 +1,147 @@
+package copartner_controller
+
+import (
+	"fohow.com/apps"
+	"fohow.com/apps/models/copartner_model"
+	"fohow.com/apps/models/project_model"
+	"fohow.com/apps/models/user_model"
+	"sync"
+	"time"
+)
+
+func (self *CopartnerController) Get() {
+	useCache, _ := self.GetBool("cache", true)
+
+	config := copartner_model.GetLastCopartnerRecruitConfig(useCache)
+	if config == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	config.StartAt = config.StartTime.Unix()
+	config.StopAt = config.StopTime.Unix()
+	config.ShareImg = self.GetFullImgUrl(config.ShareImg)
+
+	wxUser := self.GetCurrentWxUser(useCache)
+
+	type Ret struct {
+		CopartnerState int64                                   `json:"copartner_state"`
+		Config         *copartner_model.CopartnerRecruitConfig `json:"config"`
+	}
+
+	self.Data["json"] = &Ret{CopartnerState: wxUser.CopartnerState, Config: config}
+	self.ServeJSON()
+}
+
+//扫合伙人招募令按钮的api
+var beCopartnerLock sync.Mutex
+
+func (self *CopartnerController) GetText() {
+
+	useCache, _ := self.GetBool("cache", true)
+
+	config := copartner_model.GetLastCopartnerRecruitConfig(false)
+
+	now := time.Now().Unix()
+	if config == nil || config.StopTime.Unix() < now || now < config.StartTime.Unix() {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	user := self.GetCurrentUser(useCache)
+	wxUser := self.GetCurrentWxUser(useCache)
+	//判断成为合伙人逻辑
+	defer beCopartnerLock.Unlock()
+	beCopartnerLock.Lock()
+
+	var articleUrl string
+
+	if user != nil {
+		if wxUser.CopartnerState == user_model.COPARTNER_STATE_NO {
+			investAmount := project_model.GetUserHadSuccessInvest(user.Id, useCache)
+			if investAmount > 0 {
+				partnerCount := user_model.GetCountByWxUserCopartnerState(user_model.COPARTNER_STATE_YES, false)
+				if partnerCount < config.CopartnerCount { //未超过人数
+					wxUser.CopartnerState = user_model.COPARTNER_STATE_YES
+					wxUser.BeCopartnerTime = time.Now()
+					wxUser.SecondProductBenefitRate = config.SecondProductBenefitRate
+					wxUser.Save()
+					articleUrl = config.GuideEngroupArticle
+				} else {
+					//合伙人超过人数
+					articleUrl = config.OverCountArticle
+				}
+			} else {
+				articleUrl = config.GuideGrbuyArticle
+			}
+		} else if wxUser.CopartnerState == user_model.COPARTNER_STATE_YES {
+			articleUrl = config.GuideEngroupArticle
+		} else {
+			articleUrl = config.GuideRecoverArticle
+		}
+	} else {
+		articleUrl = config.GuideJoinArticle
+	}
+
+	//self.Redirect(articleUrl, 302)
+	type Ret struct {
+		CopartnerState int64  `json:"copartner_state"`
+		ArticleUrl     string `json:"article_url"`
+	}
+
+	self.Data["json"] = &Ret{CopartnerState: wxUser.CopartnerState, ArticleUrl: articleUrl}
+	self.ServeJSON()
+}
+
+//贡献值总数据
+func (self *CopartnerController) GetContributionTotal() {
+
+	cache, _ := self.GetBool("cache", false)
+	type Ret struct {
+		SelfContribute int64 `json:"self_contribute"`
+		PlatContribute int64 `json:"plat_contribute"`
+		PlatfromPcount int64 `json:"platfrom_pcount"`
+	}
+	wxUser := self.GetCurrentWxUser(cache)
+
+	selfContribute, _ := copartner_model.GetCopartnerContributionSumByBenefitWxUId(wxUser.Id)
+
+	platContribute := copartner_model.GetCopartnerContributionSum(cache)
+
+	//platformPcount := copartner_model.GetCopartnerContributionPCount(cache)
+	platformPcount := user_model.GetCountByWxUserCopartnerState(user_model.COPARTNER_STATE_YES, cache)
+
+	self.Data["json"] = &Ret{SelfContribute: selfContribute, PlatContribute: platContribute, PlatfromPcount: platformPcount}
+
+	self.ServeJSON()
+
+}
+
+func (self *CopartnerController) GetContributions() {
+
+	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)
+
+	list := copartner_model.GetContributionListByBenefitWxUId(page, perPage, wxUser.Id, cache)
+
+	for _, item := range list {
+		item.CTime = item.CreatedAt.Unix()
+	}
+	if list == nil {
+		list = make([]*copartner_model.CopartnerContribution, 0, 0)
+	}
+
+	listCount := copartner_model.GetContributionListCountByBenefitWxUId(wxUser.Id, cache)
+
+	type Ret struct {
+		List      []*copartner_model.CopartnerContribution `json:"list"`
+		ListCount int64                                    `json:"list_count"`
+	}
+
+	self.Data["json"] = &Ret{ListCount: listCount, List: list}
+	self.ServeJSON()
+}

+ 22 - 0
go/gopath/src/fohow.com/apps/controllers/copartner_controller/init.go

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

+ 431 - 0
go/gopath/src/fohow.com/apps/controllers/course_controller/course_controller.go

@@ -0,0 +1,431 @@
+package course_controller
+
+import (
+	"fmt"
+	// "math/rand"
+	"encoding/json"
+	"strconv"
+	// "strings"
+	"time"
+
+	"sync"
+	// "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/course_model"
+	"fohow.com/apps/models/user_model"
+	// "fohow.com/cache"
+	"fohow.com/libs/tool"
+	"fohow.com/libs/wx_mp"
+)
+
+func (self *CourseController) LatestCourse() {
+	cache, _ := self.GetBool("cache", true)
+	code := self.GetString("invite_code")
+	course := course_model.GetLatestCourse(cache)
+	url := "https://m.d5ct.com"
+	if course != nil {
+		url = fmt.Sprintf("%s/invest/lesson/%d", beego.AppConfig.String("WxHost"), course.Id)
+		if code != "" {
+			url += "?invite_code=" + code
+		}
+	}
+	self.Redirect(url, 302)
+}
+
+func (self *CourseController) CourseCheckJoin() {
+	unionId := self.Ctx.Input.Param(":unionid")
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	course := course_model.GetCourseById(id, useCache)
+	returnCode := "200"
+	returnMsg := "成功"
+	hasJoin := int64(0)
+	if course == nil {
+		returnCode = "403"
+		returnMsg = "课程不存在"
+	} else {
+		wxUser := user_model.GetWxUserByUnionid(unionId, useCache)
+		if wxUser == nil {
+			returnCode = "403"
+			returnMsg = "微信用户不存在"
+		} else {
+			courseOrder := course_model.GetOrderByWxUIdAndCIdAndState(wxUser.Id, course.Id, course_model.ORDER_STATE_PAIED)
+			if courseOrder != nil {
+				hasJoin = 1
+			}
+		}
+	}
+
+	type Ret struct {
+		ReturnCode string `json:"return_code"` //返回代码,200正常
+		ReturnMsg  string `json:"return_msg"`  //返回信息
+		HasJoin    int64  `json:"has_join"`    //参与状态
+	}
+	self.Data["json"] = &Ret{ReturnCode: returnCode, ReturnMsg: returnMsg, HasJoin: hasJoin}
+	self.ServeJSON()
+}
+
+//课程基本信息
+func (self *CourseController) CourseInfo() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	course := course_model.GetCourseById(id, useCache)
+	if course == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	course.Img = self.GetFullImgUrl(course.Img)
+	course.ServiceQrcode = self.GetFullImgUrl(course.ServiceQrcode)
+	course.Deadline = course.StopTime.Unix()
+	course.ShareImg = self.GetFullImgUrl(course.ShareImg)
+	type Ret struct {
+		Course    *course_model.Course `json:"course"`     //课程信息
+		HasJoin   int64                `json:"has_join"`   //参与状态
+		JoinCount int64                `json:"join_count"` //认购人数
+		Heads     []string             `json:"heads"`      //参与状态
+		KfCode    string               `json:"kf_code"`    //专属客服二维码
+	}
+	wxUId := self.GetCurrentWxUserId()
+	wxUser := self.GetCurrentWxUser(true)
+	courseOrder := course_model.GetOrderByWxUIdAndCIdAndState(wxUId, course.Id, course_model.ORDER_STATE_PAIED)
+	hasJoin := int64(0)
+	if courseOrder != nil {
+		hasJoin = 1
+	}
+	kf := ""
+	if wxUser != nil {
+		kf = getKfCode(wxUser.Unionid)
+	}
+
+	count := course_model.GetOrdersCountByCIdAndState(course.Id, course_model.ORDER_STATE_PAIED)
+	lastOrders := course_model.GetLastOrdersByCId(course.Id, 8, useCache)
+	var heads []string
+	for _, item := range lastOrders {
+		wxUser := user_model.GetWxUserById(item.WxUId, true)
+		if wxUser != nil {
+			heads = append(heads, self.GetFullImgUrl(wxUser.Head))
+		}
+	}
+	self.Data["json"] = &Ret{Course: course, HasJoin: hasJoin, JoinCount: count, Heads: heads, KfCode: kf}
+	self.ServeJSON()
+}
+
+//创建订单
+func (self *CourseController) CourseOrderCreate() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	course := course_model.GetCourseById(id, useCache)
+	if course == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	if course.StopTime.Unix() < time.Now().Unix() {
+		self.ReturnError(403, apps.HasOver, "", nil)
+	}
+	inviteWxUId, _ := self.GetInt64("iv_wx_id", 0)
+	wxUser := self.GetCurrentWxUser(true)
+	//检查是否注册过,老用户,不能购买训练营课程
+	if checkBinding(wxUser.Unionid) == 1 {
+		self.ReturnError(403, apps.WxUserHasBindingTelOnD5c, "", nil)
+	}
+	wxUId := wxUser.Id
+	// wxUId := int64(1)
+	code := self.GetString("code")
+	var order *course_model.Order
+	if code != "" {
+		coupon := course_model.GetCouponByCode(code)
+		if coupon == nil {
+			self.ReturnError(403, apps.CourseCodeNoExist, "", nil)
+		}
+		if coupon.State == course_model.COUPON_STATE_USED {
+			self.ReturnError(403, apps.CourseCodeHasUsed, "", nil)
+		}
+		now := time.Now().Unix()
+		//已过期
+		if coupon.Deadline <= now {
+			coupon.State = course_model.COUPON_STATE_EXPIRED
+			coupon.Save()
+			self.ReturnError(403, apps.CourseCodeHasExpired, "", nil)
+		} else { //有效
+			order = new(course_model.Order).Create(course.Id, wxUId, 0)
+			order.State = course_model.ORDER_STATE_PAIED
+			order.PaiedAt = now
+			order.Save()
+
+			coupon.State = course_model.COUPON_STATE_USED
+			coupon.RelateId = order.OrderId
+			coupon.Save()
+		}
+	} else {
+		order = new(course_model.Order).Create(course.Id, wxUId, course.Price)
+		if inviteWxUId != 0 && inviteWxUId != wxUser.Id {
+			order.InviteWxUId = inviteWxUId
+			order.Save()
+		}
+	}
+
+	self.Data["json"] = order
+	self.ServeJSON()
+}
+
+//生成毕业优惠码
+func (self *CourseController) CourseCouponsGenerate() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	// useCache, _ := self.GetBool("cache", true)
+	course := course_model.GetCourseById(id, true)
+	if course == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	deadline, _ := self.GetInt64("dl", 0)
+	if deadline == 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	orders := course_model.GetOrdersByCIdAndState(course.Id, course_model.ORDER_STATE_PAIED, false)
+	for _, order := range orders {
+		coupons := course_model.GetCouponsByCIdAndWxUId(order.CourseId, order.WxUId, false)
+		length := len(coupons)
+		if length >= course_model.COUPON_LIMIT {
+			continue
+		}
+		//生成优惠码
+		for i := 0; i < course_model.COUPON_LIMIT-length; i++ {
+			new(course_model.Coupon).Create(order.CourseId, order.WxUId, deadline)
+		}
+
+	}
+	self.ServeJSON()
+}
+
+//取用户优惠码的使用情况
+func (self *CourseController) UserCourseCoupons() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	// useCache, _ := self.GetBool("cache", true)
+	course := course_model.GetCourseById(id, true)
+	if course == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	unionId := self.GetString("unionid")
+	if unionId == "" {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	wxUser := user_model.GetWxUserByUnionid(unionId, true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	deadline, _ := self.GetInt64("dl", 0)
+	if deadline == 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	order := course_model.GetOrderByWxUIdAndCIdAndState(wxUser.Id, course.Id, course_model.ORDER_STATE_PAIED)
+	if order == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	coupons := course_model.GetCouponsByCIdAndWxUId(course.Id, wxUser.Id, false)
+	length := len(coupons)
+	if length < course_model.COUPON_LIMIT {
+		//生成优惠码
+		for i := 0; i < course_model.COUPON_LIMIT-length; i++ {
+			new(course_model.Coupon).Create(order.CourseId, order.WxUId, deadline)
+		}
+		coupons = course_model.GetCouponsByCIdAndWxUId(course.Id, wxUser.Id, false)
+	}
+
+	for _, coupon := range coupons {
+		if coupon.State == course_model.COUPON_STATE_USED {
+			order := course_model.GetOrderByOId(coupon.RelateId)
+			if order != nil {
+				wxUser := user_model.GetWxUserById(order.WxUId, true)
+				if wxUser != nil {
+					coupon.Head = self.GetFullImgUrl(wxUser.Head)
+				}
+			}
+		}
+	}
+	type Ret struct {
+		Coupons []*course_model.Coupon `json:"coupons"` //优惠券
+	}
+
+	self.Data["json"] = &Ret{Coupons: coupons}
+	self.ServeJSON()
+}
+
+var takeCashLock sync.Mutex
+
+// 提现
+func (self *CourseController) TakeCash() {
+
+	wxUser := self.GetCurrentWxUser(true)
+	count := course_model.GetTakeCashTimesByWxUIdAndTime(wxUser.Id, time.Now())
+	if count > course_model.TAKE_CASH_LIMIT {
+		msg := fmt.Sprintf("每天最多提现%d次", course_model.TAKE_CASH_LIMIT)
+		self.ReturnError(403, []string{"takecashTimesOverLimit", msg}, "", nil)
+	}
+
+	canExtract := course_model.GetTotalBalance(wxUser.Id)
+	if canExtract < 100 {
+		self.ReturnError(403, []string{"takecashAmountNotEnough", "余额不足1元,无法提现"}, "", nil)
+	}
+
+	takeCashLock.Lock()
+	defer takeCashLock.Unlock()
+
+	o := new(course_model.TakeCashOrder).Create(wxUser.Id, canExtract)
+	if o != nil {
+		new(course_model.Balance).Create(wxUser.Id, -canExtract, course_model.CASH_SOURCE_TAKE_CASH,
+			o.OrderId, course_model.CASH_SOURCE_TAKE_CASH_NAME)
+		wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(wxUser.Id, beego.AppConfig.String("WxMPAppId"), true)
+		if wxUserGzh != nil {
+			ret := wx_mp.WxTransfers(wxUserGzh.GzhOpenId, o.Count, o.OrderId, wx_mp.PAY_NO_CHECK, "", "推广奖励")
+			if ret["result_code"] == wx_mp.PAY_SUCCESS {
+				o.AuditState = 1
+				o.State = 1
+				o.TradeNo = ret["payment_no"]
+				o.Remark = "奖励已打款"
+				o.PaiedAt = time.Now().Unix()
+				o.Save()
+			} else {
+				o.State = 2
+				o.Remark = ret["err_code_des"]
+				o.Save()
+			}
+		}
+	}
+
+	self.Data["json"] = o
+	self.ServeJSON()
+}
+
+//提现流列表
+func (self *CourseController) 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 := course_model.GetTakeCashOrderListByWxUId(wxUId, page, perPage, cache)
+	listCount := course_model.GetTakeCashOrderCountByWxUId(wxUId, cache)
+
+	type Order struct {
+		Id      int64  ` json:"id"`    // int(11)
+		Count   int64  ` json:"count"` // bigint(20)
+		StateCN string ` json:"state_cn"`
+		CTime   int64  ` json:"ctime"` // datetime
+	}
+
+	retList := make([]*Order, 0, 0)
+
+	for _, item := range list {
+		order := new(Order)
+		order.Id = item.Id
+		order.Count = item.Count
+		order.StateCN = item.GetStateCn()
+		order.CTime = item.CreatedAt.Unix()
+		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()
+}
+
+//佣金列表
+func (self *CourseController) GetBenefits() {
+	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 := course_model.GetBenefitListByWxUId(wxUId, page, perPage, cache)
+	listCount := course_model.GetBenefitCountByWxUId(wxUId)
+	for _, item := range list {
+		wxUser := user_model.GetWxUserById(item.WxUId, true)
+		item.Nickname = wxUser.Nickname
+		item.Head = self.GetFullImgUrl(wxUser.Head)
+		item.CTime = item.CreatedAt.Unix()
+	}
+	type Ret struct {
+		List      []*course_model.Benefit `json:"list"`
+		ListCount int64                   `json:"list_count"`
+	}
+	self.Data["json"] = &Ret{List: list, ListCount: listCount}
+	self.ServeJSON()
+}
+
+//课程佣金余额
+func (self *CourseController) CourseBalanceInfo() {
+	type Ret struct {
+		Total    int64  `json:"total"`    //收益总额
+		Valid    int64  `json:"valid"`    //可提现余额
+		Count    int64  `json:"count"`    //可提现次数
+		Head     string `json:"head"`     //头像
+		Nickname string `json:"nickname"` //昵称
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	amount := course_model.GetTotalBalance(wxUser.Id)
+	count := course_model.GetTakeCashTimesByWxUIdAndTime(wxUser.Id, time.Now())
+	total := course_model.GetEnterBalance(wxUser.Id)
+	leftCount := course_model.TAKE_CASH_LIMIT - count
+	head := self.GetFullImgUrl(wxUser.Head)
+	self.Data["json"] = &Ret{Total: total, Valid: amount, Count: leftCount, Head: head, Nickname: wxUser.Nickname}
+	self.ServeJSON()
+}
+
+//检查是否在第五创投资过真实项目
+func checkBinding(unionId string) int64 {
+	url := fmt.Sprintf("%s/v1/wxuser/%s/binding/check", beego.AppConfig.String("D5CApiHost"), unionId)
+	resp := tool.HttpCall(url, "GET", nil, nil)
+	type Ret struct {
+		ReturnCode string `json:"return_code"` //返回代码,200正常
+		ReturnMsg  string `json:"return_msg"`  //返回信息
+		HasBinding int64  `json:"has_binding"` //是否已绑定手机
+	}
+	var ret Ret
+	json.Unmarshal([]byte(resp), &ret)
+	if ret.ReturnCode != "200" {
+		beego.BeeLogger.Error("CheckJoin Error, ret:%v", ret)
+	}
+	return ret.HasBinding
+}
+
+func getKfCode(unionId string) string {
+	url := fmt.Sprintf("%s/v1/user/kf_qrcode?unionid=%s", beego.AppConfig.String("D5CApiHost"), unionId)
+	resp := tool.HttpCall(url, "GET", nil, nil)
+	type Ret struct {
+		CheckCode string `json:"check_code"` //返回代码,200正常
+		ImgPath   string `json:"img_path"`   //返回信息
+	}
+	var ret Ret
+	json.Unmarshal([]byte(resp), &ret)
+	if ret.CheckCode != "0000" {
+		beego.BeeLogger.Error("CheckJoin Error, ret:%v", ret)
+		return ""
+	}
+	return ret.ImgPath
+	// http: //api.d5ct.com/v1/user/kf_qrcode?unionid=o8HkVwH3Qyvb0Sd5bz9rQ-iDcUVo
+}

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

@@ -0,0 +1,24 @@
+package course_controller
+
+import (
+	"github.com/astaxie/beego/context"
+
+	"fohow.com/apps"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction = []string{"CourseOrderCreate", "CourseInfo", "CourseCheckJoin", "CourseCouponsGenerate", "UserCourseCoupons",
+		"CourseBalanceInfo", "GetBenefits", "TakeCash", "GetTakeCashOrders", "LatestCourse"}
+	exceptCheckWxUserLoginAction = []string{"CourseCheckJoin", "CourseCouponsGenerate", "UserCourseCoupons", "LatestCourse"}
+)
+
+type CourseController struct {
+	apps.BaseController
+}
+
+func (self *CourseController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 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)
+		}
+	}
+}

+ 56 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/copartner.go

@@ -0,0 +1,56 @@
+package cron_controller
+
+import (
+	"fmt"
+	"fohow.com/apps/models/copartner_model"
+	"fohow.com/apps/models/user_model"
+	"time"
+)
+
+//没有佣金记录或者最近佣金记录距离目前超过100天则取消合伙人资格
+func checkCopartnerState() {
+
+	config := copartner_model.GetLastCopartnerRecruitConfig(false)
+	if config == nil || config.CancelDays <= 0 {
+		return
+	}
+
+	termSeconds := int64(config.CancelDays * 24 * 60 * 60)
+	wxUserList := user_model.GetWxUserListByCopartnerState(user_model.COPARTNER_STATE_YES, false)
+	for _, item := range wxUserList {
+
+		remark := fmt.Sprintf("超过%d天无佣金产生,贡献值清零", config.CancelDays)
+		now := time.Now()
+		if now.Unix()-item.BeCopartnerTime.Unix() < termSeconds { //成为合伙人满100天后
+			continue
+		}
+
+		lastInviteOrder := user_model.GetLastInviteOrderByBeWxUIdAndSource(item.Id, user_model.SOURCE_PRODUCT_BENEFIT)
+
+		if lastInviteOrder != nil {
+
+			if now.Unix()-lastInviteOrder.CreatedAt.Unix() > termSeconds { //最新佣金的创建时间距离当前时间超过100天则取消佣金
+				item.CopartnerState = user_model.COPARTNER_STATE_ONCE
+				item.CancelTime = now
+				item.Save()
+				contributionSum, contributionAmount := copartner_model.GetCopartnerContributionSumByBenefitWxUId(item.Id)
+				if contributionSum > 0 {
+					rId := fmt.Sprintf("%s_%s", copartner_model.CONTRIBTION_SOURCE_RESET_PREFIX, now.Format("2006-01-02_15:04"))
+					new(copartner_model.CopartnerContribution).Create(item.Id, item.Id, item.Id, -contributionSum, 0, contributionAmount, copartner_model.CONTRIBTION_SOURCE_RESET, rId, remark)
+				}
+			}
+
+		} else {
+			item.CopartnerState = user_model.COPARTNER_STATE_ONCE
+			item.CancelTime = now
+			item.Save()
+			contributionSum, contributionAmount := copartner_model.GetCopartnerContributionSumByBenefitWxUId(item.Id)
+			if contributionSum > 0 {
+				rId := fmt.Sprintf("%s_%s", copartner_model.CONTRIBTION_SOURCE_RESET_PREFIX, now.Format("2006-01-02_15:04"))
+				new(copartner_model.CopartnerContribution).Create(item.Id, item.Id, item.Id, -contributionSum, 0, contributionAmount, copartner_model.CONTRIBTION_SOURCE_RESET, rId, remark)
+			}
+		}
+
+	}
+
+}

+ 80 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/dollar_win_notice.go

@@ -0,0 +1,80 @@
+package cron_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"fohow.com/apps/models/dollar_win_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/wx_mp"
+	"time"
+)
+
+//给已经支付成功的邀请人进行模板消息通知,一小时一次
+func DollarWinNotice() {
+	configs := dollar_win_model.GetDollarWinZtConfigList(false)
+	endTime := time.Now().Unix()
+	startTime := time.Now().Add(-time.Hour * 1).Unix()
+	beego.BeeLogger.Warn("DollarWinNotice, startTime=[%d] endTime=[%d]", startTime, endTime)
+	for _, config := range configs {
+		beego.BeeLogger.Warn("DollarWinNotice, startTime2=[%d] endTime2=[%d]", startTime, endTime)
+		if time.Now().Unix()-config.StopTime.Unix() > 300 { //活动结束5分钟则不再进行通知
+			continue
+		}
+		beego.BeeLogger.Warn("DollarWinNotice, config.IsTemp=[%v]", config.IsTemp)
+		if !config.IsTemp { //未开启临时邀请机制,则无需通知邀请者
+			continue
+		}
+		newest := dollar_win_model.GetInviteJoinPaiedListByZtConfigIdAndPaiedAt(config.Id, startTime, endTime, false)
+		for i := 0; i < len(newest); i++ {
+			new := newest[i]
+			//获取推荐人id
+			uId := new.TempInviteId
+
+			inviteDollar := dollar_win_model.GetDollarWinZtJoinByTempInviteId(config.Id, uId, false)
+			if inviteDollar == nil { //若邀请人未参与,则不推送通知
+				continue
+			}
+			inviteTime := inviteDollar.PaiedAt
+
+			//被邀请者需要在邀请者参与之后才推送通知
+			if inviteTime > endTime {
+				continue
+			} else if inviteTime > startTime && inviteTime < endTime {
+				startTime = inviteTime
+			}
+
+			//获取推荐人支付之后最后一条支付记录
+			newInvite := dollar_win_model.GetInviteJoinPaiedListByZtConfigIdUidAndPaiedAt(config.Id, uId, startTime, endTime, false)
+
+			if newInvite == nil {
+				continue
+			}
+			tm := time.Unix(newInvite.PaiedAt, 0)
+			beego.BeeLogger.Warn("DollarWinNotice, tm=[%d]", tm)
+
+			//获取 最后一条支付人的会员帐号
+			newUser := user_model.GetWxUserByUserId(newInvite.UserId, false)
+
+			user := user_model.GetWxUserByUserId(uId, false)
+			if user != nil {
+				wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(user.Id, beego.AppConfig.String("WxMPAppId"), false)
+				if wxUserGzh != nil && wxUserGzh.GzhOpenId != "" {
+					var url string
+					if beego.BConfig.RunMode == beego.DEV {
+						url = fmt.Sprintf("http://testm.labitumall.com/activity/newoneinvest/%d", config.Id)
+					} else {
+						url = fmt.Sprintf("https://m.labitumall.com/activity/newoneinvest/%d", config.Id)
+					}
+					beego.BeeLogger.Warn("DollarWinNotice, url=[%s]", url)
+					first := "您邀请的好友已成功参加活动"
+					kw1 := newUser.Nickname
+					kw2 := config.Title
+					kw3 := tm.Format("2006-01-02 15:04:05")
+					kw4 := fmt.Sprintf("Ta的排名号%d", newInvite.RankNumber)
+					remark := "点击查看"
+					go wx_mp.TmplmsgEventDollarAddNotifyHandle(wxUserGzh.GzhOpenId, url, first, kw1, kw2, kw3, kw4, remark)
+				}
+			}
+		}
+	}
+}

+ 54 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/dollar_win_rank.go

@@ -0,0 +1,54 @@
+package cron_controller
+
+import (
+	"fohow.com/apps/models/dollar_win_model"
+	"sync"
+	"time"
+)
+
+var rankLock sync.Mutex
+
+//给已经支付成功的人进行名次排位,5分钟一次
+func DollarWinRank() {
+	defer rankLock.Unlock()
+	rankLock.Lock()
+
+	configs := dollar_win_model.GetDollarWinZtConfigList(false)
+
+	for _, config := range configs {
+
+		if time.Now().Unix()-config.StopTime.Unix() > 600 { //活动结束10分钟则不再进行名次更新
+			continue
+		}
+
+		first := dollar_win_model.GetFirstJoinPaiedByZtConfigId(config.Id, false)
+		if first == nil {
+			continue
+		}
+
+		paiedAt, rankInit := int64(0), int64(0)
+		if first.RankNumber > 0 {
+			newest := dollar_win_model.GetLatestRankJoinPaiedByZtConfigId(config.Id, false)
+			if newest == nil {
+				continue
+			}
+			paiedAt = newest.PaiedAt
+			rankInit = newest.RankNumber
+		} else {
+			first.RankNumber = 1
+			first.Save()
+			paiedAt = first.PaiedAt
+			rankInit = first.RankNumber
+		}
+
+		if rankInit <= 0 {
+			continue
+		}
+		needRankList := dollar_win_model.GetoinPaiedListByZtConfigIdAndPaiedAt(config.Id, paiedAt, false)
+		for i := 0; i < len(needRankList); i++ {
+			needRankOrder := needRankList[i]
+			needRankOrder.RankNumber = rankInit + int64(i+1)
+			needRankOrder.Save()
+		}
+	}
+}

+ 135 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/granary.go

@@ -0,0 +1,135 @@
+package cron_controller
+
+import (
+	// "time"
+
+	"github.com/astaxie/beego"
+
+	"fmt"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/granary_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/user_model"
+	"strings"
+	"time"
+)
+
+//更新代销价格
+func updateSalePrice() {
+	list := order_model.GetUserSaleOrders()
+	beego.BeeLogger.Warn("cron time task:---updateSalePrice---length: %d", len(list))
+	for _, item := range list {
+		p := product_model.GetProductById(item.ProductId, true)
+		if p != nil {
+			item.UnitUserSalePrice = p.UserSalePrice
+			item.Save()
+			beego.BeeLogger.Warn("oId: %s, price:%d", item.OrderId, item.UnitUserSalePrice)
+		}
+	}
+}
+
+//自动审核下架代销订单-5分钟执行一次
+func AutoOfflineSaleOrder() {
+	list := granary_model.GetOfflineSaleOrders()
+	beego.BeeLogger.Warn("cron time task:---AutoAuditOfflineSaleOrder---length: %d", len(list))
+
+	for _, item := range list {
+		item.State = granary_model.ORDER_STATE_OFFLINE
+		item.OperateState = granary_model.OPERATE_STATE_NOTHING
+		item.Save()
+	}
+}
+
+//自用数量为0,第2天和第7天通知
+func AutoSelfUseNotice() {
+
+	//查询自用数量为0的用户粮仓,加一个字段作最新通知自用时间
+	list := granary_model.GetZeroSelfUseGranaries()
+
+	now := time.Now()
+	warmTips := fmt.Sprintf("为防止商品过季影响口感,请赶紧到【个人中心】-【我的粮仓】进行下单自用吧")
+	service := fmt.Sprintf("可爱的小兔和小拉")
+	page := fmt.Sprintf("pages/start/start?url=packageUser/pages/user/granary/granary")
+	for _, item := range list {
+		if item.SelfUseMin <= 0 {
+			continue
+		}
+		timeDefer := now.Unix() - item.CreatedAt.Unix()
+		if timeDefer >= 1*24*60*60 && timeDefer < 6*24*60*60 {
+			if item.LastNoticeTime == 0 {
+				//发自用通知
+				wxUser := user_model.GetWxUserByUserId(item.UserId, false)
+				if wxUser == nil {
+					continue
+				}
+				product := product_model.GetProductById(item.ProductId, false)
+				if product == nil {
+					continue
+				}
+				isNotify := helpers.GranaryProductDealNotify(*wxUser, product.Name, fmt.Sprintf("自用%d份起", item.SelfUseMin), warmTips, service, "keyword1.DATA", page)
+				if isNotify {
+					item.LastNoticeTime = now.Unix()
+					item.NoticeCount = item.NoticeCount + 1
+					item.Save()
+				}
+			}
+		}
+
+		if timeDefer >= 6*24*60*60 {
+			if item.LastNoticeTime == 0 {
+				//发自用通知
+				wxUser := user_model.GetWxUserByUserId(item.UserId, false)
+				if wxUser == nil {
+					continue
+				}
+				product := product_model.GetProductById(item.ProductId, false)
+				if product == nil {
+					continue
+				}
+				isNotify := helpers.GranaryProductDealNotify(*wxUser, product.Name, fmt.Sprintf("自用%d份起", item.SelfUseMin), warmTips, service, "keyword1.DATA", page)
+				if isNotify {
+					item.LastNoticeTime = now.Unix()
+					item.NoticeCount = item.NoticeCount + 1
+					item.Save()
+				}
+			} else {
+				if item.NoticeCount < 2 && strings.Compare(time.Unix(item.LastNoticeTime, 0).Format("2006-01-02"), now.Format("2006-01-02")) < 0 {
+					//发自用通知
+					wxUser := user_model.GetWxUserByUserId(item.UserId, false)
+					if wxUser == nil {
+						continue
+					}
+					product := product_model.GetProductById(item.ProductId, false)
+					if product == nil {
+						continue
+					}
+					isNotify := helpers.GranaryProductDealNotify(*wxUser, product.Name, fmt.Sprintf("自用%d份起", item.SelfUseMin), warmTips, service, "keyword1.DATA", page)
+					if isNotify {
+						item.LastNoticeTime = now.Unix()
+						item.NoticeCount = item.NoticeCount + 1
+						item.Save()
+					}
+				}
+			}
+		}
+
+	}
+}
+
+//一次性处理已经收货的订单,代销金还没帐
+func dealTypeSaleFinishedOrders() {
+
+	orders := order_model.GetTypeSaleFinishedOrders()
+
+	beego.BeeLogger.Warn("cron time task:---dealTypeSaleFinishedOrders---len(orders):%d.", len(orders))
+
+	for _, item := range orders {
+
+		if item.OrderType == order_model.ORDER_TYPE_SALE {
+			helpers.SendBalanceWhileSaleOrderCompleteHandler(item)
+		}
+
+		beego.BeeLogger.Warn("cron time task:---dealTypeSaleFinishedOrders---item_id: %d, finished.", 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 "sendTopSaleListMonthlyReward": //发放每月销售奖金
+		sendTopSaleListMonthlyReward()
+	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)
+	}
+}

+ 175 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/invite.go

@@ -0,0 +1,175 @@
+package cron_controller
+
+import (
+	// "fmt"
+	"time"
+
+	"github.com/astaxie/beego"
+
+	"fmt"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/invite_sale_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/use_limit"
+	"fohow.com/apps/models/user_model"
+	"strings"
+)
+
+//同步产品佣金,发放到资金账户
+func syncProductBenefit() {
+	now := time.Now()
+	//yesterday := now.Add(-48 * time.Hour)
+	yesterday := now.Add(-10 * 24 * time.Hour)
+
+	today, _ := time.Parse("20060102", now.Format("20060102"))
+	from, _ := time.Parse("20060102", yesterday.Format("20060102"))
+	//昨天已收货的订单
+	orders := order_model.GetFinishedOrdersByRTime(from.Add(-8*time.Hour), today.Add(-8*time.Hour))
+	beego.BeeLogger.Warn("cron time task:---SyncProductBenefit---orders_num: %d, from_time : %s", len(orders), from.Format("2006-01-02 15:04:05"))
+
+	if len(orders) >= 1000 {
+		yesterday = now.Add(-48 * time.Hour)
+		from, _ = time.Parse("20060102", yesterday.Format("20060102"))
+		orders = order_model.GetFinishedOrdersByRTime(from.Add(-8*time.Hour), today.Add(-8*time.Hour))
+		beego.BeeLogger.Warn("cron time task:---SyncProductBenefit---orders_num: %d, from_time : %s", len(orders), from.Format("2006-01-02 15:04:05"))
+	}
+	for _, order := range orders {
+		//部分退款,不发放佣金
+		if order.IsPartRefund == 1 {
+			continue
+		}
+		wxUser := user_model.GetWxUserById(order.WxUserId, true)
+		if wxUser != nil && wxUser.InviteId != 0 {
+			inviter := user_model.GetWxUserById(wxUser.InviteId, true)
+			if inviter != nil {
+				benefitOrder := user_model.GetInviteOrderByWxUIdAndSourceAndRId(wxUser.Id, user_model.SOURCE_PRODUCT_BENEFIT, order.OrderId)
+				if benefitOrder == nil {
+					continue
+				}
+				s := balance_model.CASH_SOURCE_PRODUCT_BENEFIT
+				product := product_model.GetProductById(order.ProductId, true)
+				// remark := fmt.Sprintf("%s>", product.Name)
+				//发放现金佣金
+				b := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(inviter.Id, order.OrderId, s)
+				if b == nil {
+					b = new(balance_model.CashBalance).Create(inviter.Id, benefitOrder.Count, s, order.OrderId, product.Name)
+					if b != nil {
+						//标志进账
+						benefitOrder.IsEnterBalance = true
+						benefitOrder.EnterTime = b.CreatedAt
+						benefitOrder.Save()
+					}
+				}
+				//处理二级佣金进账
+				go dealSecondProductBenefit(wxUser, order, product, s)
+
+			}
+
+		}
+
+	}
+}
+
+//处理二级产品佣金进账
+func dealSecondProductBenefit(wxUser *user_model.WxUser, order *order_model.Order, product *product_model.Product, source string) {
+
+	if wxUser == nil || order == nil || product == nil {
+		return
+	}
+
+	rId := fmt.Sprintf("%s%s", user_model.SECOND_RELATE_ID_PREFIT, order.OrderId)
+	secondBenefitOrder := user_model.GetInviteOrderByRId(rId)
+	if secondBenefitOrder == nil {
+		return
+	}
+
+	sbenefit := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(secondBenefitOrder.BenefitWxUId, rId, source)
+	if sbenefit == nil {
+		sbenefit = new(balance_model.CashBalance).Create(secondBenefitOrder.BenefitWxUId, secondBenefitOrder.Count, source, rId, product.Name)
+		if sbenefit != nil {
+			secondBenefitOrder.IsEnterBalance = true
+			secondBenefitOrder.EnterTime = sbenefit.CreatedAt
+			secondBenefitOrder.Save()
+		}
+	}
+}
+
+//发放上一个月的销售奖励,每月十号02:00发放
+func sendTopSaleListMonthlyReward() {
+
+	now := time.Now()
+	ym := now.AddDate(0, -1, 0)
+
+	lastMonthConfigList := invite_sale_model.GetNotSendRewardConfigsByTime(ym)
+
+	if lastMonthConfigList == nil || len(lastMonthConfigList) == 0 {
+		return
+	}
+
+	first := lastMonthConfigList[0]
+	max := first.Rank
+
+	//beego.BeeLogger.Warn("sendTopSaleListMonthlyReward_max: %d", max)
+
+	useLimits := use_limit.GetEffectAndLimitUpSaleList()
+	var limitWxUids []string
+	for _, useLimit := range useLimits {
+		wxUid := fmt.Sprintf("%d", useLimit.WxUid)
+		limitWxUids = append(limitWxUids, wxUid)
+	}
+
+	list := invite_sale_model.GetInviterSaleListByLimitWxUidsAndTime(1, max, strings.Join(limitWxUids, ","), ym, false)
+
+	if list == nil || len(list) == 0 {
+		return
+	}
+
+	for _, config := range lastMonthConfigList {
+
+		if config.IsSend == 1 {
+			continue
+		}
+
+		rank := config.Rank
+
+		var saleItem *invite_sale_model.InviterSale
+		for i := int64(1); i <= int64(len(list)); i++ {
+			if i == rank {
+				saleItem = list[i-1]
+				break
+			}
+		}
+
+		if saleItem == nil {
+			continue
+		}
+
+		beego.BeeLogger.Warn("sendTopSaleListMonthlyReward.saleItem: %v", saleItem)
+		s := balance_model.CASH_SOURCE_SALE_REWARD
+		rId := fmt.Sprintf("%s_%s", s, ym.Format("200601"))
+		wxUid := saleItem.BenefitWxUid
+
+		cashBalance := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(wxUid, rId, s)
+		if cashBalance != nil {
+			continue
+		}
+		remark := fmt.Sprintf("%s%s", ym.Format("2006年01月"), balance_model.CASH_SOURCE_SALE_REWARD_NAME)
+
+		sendCount := config.RewardAmount
+		if config.RewardType == invite_sale_model.REWARD_TYPE_RATIO_ENUM {
+			sendCount = saleItem.Count * config.RewardAmount / 100
+		}
+
+		//现金记录
+		balance := new(balance_model.CashBalance).Create(wxUid, sendCount, s, rId, remark)
+		if balance != nil {
+			//发放记录
+			new(invite_sale_model.InviteSaleRewardRecord).Create(wxUid, saleItem.Count, sendCount, config.Rank, config.ConfigTime)
+		}
+
+		config.IsSend = 1
+		config.Save()
+	}
+
+}

+ 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)
+		}
+	}
+}

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

@@ -0,0 +1,122 @@
+package cron_controller
+
+import (
+	"crypto/md5"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"strconv"
+	"time"
+
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/wx_mp"
+)
+
+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 syncBalance() {
+	beego.BeeLogger.Warn("********** Cron task - handle syncBalance start at: %s********", time.Now())
+	// 找出50条未同步的记录
+	// 逐条同步,并标记成功记录
+	o := orm.NewOrm()
+
+	var rs []SyncData
+
+	sql := "SELECT `chunjie_lucky_moneys`.`id` AS id,  `wx_uid`, `receive_bonus`, `chunjie_lucky_moneys`.`created_at`, `unionid` FROM `chunjie_lucky_moneys` inner join `wx_users` on `chunjie_lucky_moneys`.`wx_uid`=`wx_users`.`id` WHERE `is_sync25`<>1 AND `wx_users`.`unionid`<>'' order by `is_sync25` ASC LIMIT 300 ;"
+	num, err := o.Raw(sql).QueryRows(&rs)
+	if err == nil {
+		fmt.Println("user nums: ", num)
+	}
+	for _, syncData := range rs {
+		beego.BeeLogger.Info("********** Cron task - handle syncBalance RecordID=[%d] ********", syncData)
+		if result := syncOne(syncData); result {
+			// update chunjie_lucky_moneys set is_sync25=1
+			o.Raw("UPDATE `chunjie_lucky_moneys` SET `is_sync25` = 1 WHERE id=?", syncData.RecordID).Exec()
+			beego.BeeLogger.Info("********** Cron task - handle syncBalance Success: ********", syncData)
+		} else {
+			o.Raw("UPDATE `chunjie_lucky_moneys` SET `is_sync25` = 2 WHERE id=?", syncData.RecordID).Exec()
+			beego.BeeLogger.Error("********** Cron task - handle syncBalance Fail: ********", syncData)
+		}
+	}
+	beego.BeeLogger.Warn("********** Cron task - handle syncBalance end   at: %s********", time.Now())
+}
+
+func syncOne(syncData SyncData) bool {
+	form := url.Values{}
+	form.Add("unionId", syncData.UnionId)
+	form.Add("amount", strconv.FormatInt(syncData.Amount, 10))
+	form.Add("timestamp", strconv.FormatInt(syncData.CreatedAt.Unix(), 10))
+	form.Add("signature", syncData.Sign())
+	apiUrl := beego.AppConfig.String("D5CApiHost") + "/v1/labi/cjhd/sync_balance"
+	resp, err := http.PostForm(apiUrl, form)
+	if err != nil {
+		beego.BeeLogger.Error("********** Cron task - handle syncBalance Http Error %s:  ********", err)
+	}
+	defer resp.Body.Close()
+	beego.BeeLogger.Debug("********** Cron task - handle syncBalance end   at:  ********", resp)
+	return resp.StatusCode == 201
+}
+
+//提现打款的定时任务
+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)
+		}
+	}
+}

+ 105 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/update_invite_benefit_order.go

@@ -0,0 +1,105 @@
+package cron_controller
+
+import (
+	"github.com/astaxie/beego"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/subject_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/tool"
+	"sync"
+)
+
+//修复数据-只执行一次
+func addInviteBenefitOrder() {
+
+	//专题3产生的订单
+	orders := order_model.GetPrizeSubjectOrdersBySbjId(3)
+	beego.BeeLogger.Warn("addInviteBenefitOrder-len(orders): %d", len(orders))
+	for i, order := range orders {
+		beego.BeeLogger.Warn("addInviteBenefitOrder-wxUser i: %d, wxuid:%d", i, order.WxUserId)
+		//购买者
+		wxUser := user_model.GetWxUserById(order.WxUserId, false)
+
+		if wxUser == nil || wxUser.InviteId <= 0 {
+			continue
+		}
+		beego.BeeLogger.Warn("addInviteBenefitOrder-wxUser: %d", wxUser.Id)
+
+		//购买者的邀请人
+		inviteWxUser := user_model.GetWxUserById(wxUser.InviteId, false)
+
+		if inviteWxUser == nil {
+			continue
+		}
+		if inviteWxUser.ShowInviteMode != 1 {
+			if inviteWxUser.ProductBenefitRate == 0 {
+				inviteWxUser.ProductBenefitRate = 5
+			}
+			if inviteWxUser.ProjectBenefitRate == 0 {
+				inviteWxUser.ProjectBenefitRate = 1
+			}
+			inviteWxUser.ShowInviteMode = int64(1)
+			inviteWxUser.Save()
+		}
+
+		beego.BeeLogger.Warn("addInviteBenefitOrder-inviteWxUser: %d", inviteWxUser.Id)
+
+		inviteBenefitOrder := user_model.GetInviteOrderByRId(order.OrderId)
+		beego.BeeLogger.Warn("addInviteBenefitOrder-inviteBenefitOrder: %v", inviteBenefitOrder)
+
+		if inviteBenefitOrder == nil {
+
+			count := int64(tool.RoundFloat64(float64(order.UnitPrice*order.Count)*float64(inviteWxUser.ProductBenefitRate)/100, 0))
+			if count == 0 {
+				count = 1
+			}
+			inviteOrder := new(user_model.InviteOrder).Create(inviteWxUser.Id, wxUser.Id, wxUser.Id, count, order.UnitPrice*order.Count,
+				user_model.SOURCE_PRODUCT_BENEFIT, order.OrderId)
+
+			beego.BeeLogger.Warn("addInviteBenefitOrder-inviteOrder-id: %d", inviteOrder.Id)
+		}
+
+	}
+}
+
+func addSubjectDrawCode() {
+
+	orders := order_model.GetValidAndNotJoinPrizeOrdersByPIdAndPaiedTime(127, 1536293489)
+
+	beego.BeeLogger.Warn("addSubjectDrawCode-len(orders): %d", len(orders))
+
+	if len(orders) == 41 {
+		for _, order := range orders {
+
+			beego.BeeLogger.Warn("addSubjectDrawCode-order: %s, count: %d", order.OrderId, order.Count)
+			order.IsJoinSubjectPrize = true
+			order.ProductSaleSubjectId = 3
+			CreateDrawCode(order)
+			order.Save()
+
+		}
+	}
+
+}
+
+var createDrawCode sync.Mutex
+
+func CreateDrawCode(order *order_model.Order) {
+
+	createDrawCode.Lock()
+	defer createDrawCode.Unlock()
+
+	subject := subject_model.GetProductSaleSubjectById(order.ProductSaleSubjectId, true)
+	if subject == nil {
+		return
+	}
+	for i := int64(1); i <= order.Count; i++ {
+		drawCode := subject_model.GetLastSaleDrawCodeBySubjectId(order.ProductSaleSubjectId)
+		if drawCode == nil {
+			drawCode = new(subject_model.SaleDrawCode).Create(int64(1), order.WxUserId, order.ProductSaleSubjectId, order.OrderId)
+		} else {
+			drawCode = new(subject_model.SaleDrawCode).Create(drawCode.Code+1, order.WxUserId, order.ProductSaleSubjectId, order.OrderId)
+		}
+		beego.BeeLogger.Warn("addSubjectDrawCode-CreateDrawCode success code: %d", drawCode.Code)
+	}
+}

+ 19 - 0
go/gopath/src/fohow.com/apps/controllers/cron_controller/update_user_is_regist_d5c.go

@@ -0,0 +1,19 @@
+package cron_controller
+
+import (
+	"github.com/astaxie/beego"
+	"fohow.com/apps/models/user_model"
+)
+
+func updateUserTelIsRegistD5c() {
+
+	users := user_model.GetUserByIsRegistAndIsRegistBefore()
+	for _, item := range users {
+		beego.BeeLogger.Warn("updateUserTelIsRegistD5c-tel:%s", item.Tel)
+		CheckAndUpdate(item)
+	}
+}
+
+func CheckAndUpdate(user *user_model.User) {
+	user_model.UpdateIsRegistD5c(user)
+}

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

@@ -0,0 +1,155 @@
+package cron_controller
+
+import (
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/orm"
+	"fohow.com/apps/models/project_model"
+	"fohow.com/apps/models/share_model"
+	"fohow.com/apps/models/user_model"
+)
+
+/*
+处理用户的昵称和头像,从微信用户那里同步过来
+*/
+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)
+		}
+
+	}
+
+}
+
+/*
+团购有支付成功的投资记录
+邀请佣金管理有记录的
+分享情况管理,有分享记录的
+给他们开通微信推广模块
+*/
+func updateWxUserShowInviteMode() {
+
+	//查找真实投资人
+	var list1 []*project_model.ProjectJoin
+	sql1 := `
+		select *
+		  FROM project_joins
+		 where state= 1
+		   and is_deleted= 0
+		   and is_refunded= 0
+		 GROUP BY user_id ;
+	`
+
+	_, err1 := orm.NewOrm().Raw(sql1).QueryRows(&list1)
+
+	if err1 != nil {
+		beego.Debug("GetProjectUsers err=[%s]", err1)
+	}
+
+	for _, item := range list1 {
+
+		wxUser := user_model.GetWxUserByUserId(item.UserId, true)
+		if wxUser != nil && wxUser.ShowInviteMode != 1 {
+			wxUser.ShowInviteMode = int64(1)
+			wxUser.Save()
+		}
+	}
+
+	//邀请佣金管理有记录的
+	var list2 []*user_model.InviteOrder
+	sql2 := `
+		select *
+		  from invite_benefit_orders
+		 where source!= 'binding'
+		 GROUP BY benefit_wx_uid ;
+	`
+	_, err2 := orm.NewOrm().Raw(sql2).QueryRows(&list2)
+	if err2 != nil {
+		beego.Debug("GetInviteOrderUsers err=[%s]", err2)
+	}
+
+	for _, item := range list2 {
+
+		wxUser := user_model.GetWxUserById(item.BenefitWxUId, true)
+
+		if wxUser != nil && wxUser.ShowInviteMode != 1 {
+			wxUser.ShowInviteMode = int64(1)
+			wxUser.Save()
+		}
+	}
+
+	//分享情况
+	var list3 []*share_model.ShareInfo
+	sql3 := `
+		select *
+		  from share_infos
+		 GROUP BY wx_user_id ;
+	`
+	_, err3 := orm.NewOrm().Raw(sql3).QueryRows(&list3)
+	if err3 != nil {
+		beego.Debug("GetInviteOrderUsers err=[%s]", err3)
+	}
+
+	for _, item := range list3 {
+
+		wxUser := user_model.GetWxUserById(item.WxUserId, true)
+		if wxUser != nil && wxUser.ShowInviteMode != 1 {
+			wxUser.ShowInviteMode = int64(1)
+			wxUser.Save()
+		}
+	}
+
+}

+ 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()
+}

+ 308 - 0
go/gopath/src/fohow.com/apps/controllers/dollar_win_controller/dollar_win_controller.go

@@ -0,0 +1,308 @@
+package dollar_win_controller
+
+import (
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+	"fohow.com/apps/controllers/cron_controller"
+	"fohow.com/apps/models/dollar_win_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/kefu"
+	"fohow.com/libs/regist"
+	"strconv"
+	"sync"
+	"time"
+)
+
+var (
+	//需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"GetConfig", "GetJoinStateList", "DollarWinNotice"}
+	exceptCheckWxUserLoginAction = []string{""}
+)
+
+type DollarWinController struct {
+	apps.BaseController
+}
+
+func (self *DollarWinController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+func (self *DollarWinController) GetConfig() {
+
+	if !self.IsWxClient() {
+		self.ReturnError(403, apps.NotWeixinClient, "", nil)
+	}
+
+	_id := self.Ctx.Input.Param(":id")
+	cId, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+
+	ztConfig := dollar_win_model.GetDollarWinZtConfigById(cId, useCache)
+	wxUser := self.GetCurrentWxUser(useCache)
+
+	userJoinCount := int64(0)
+	if ztConfig != nil {
+		ztConfig.StartAt = ztConfig.StartTime.Unix()
+		ztConfig.StopAt = ztConfig.StopTime.Unix()
+		ztConfig.ChannelId = int64(19) //写死注册用的渠道id
+		ztConfig.BgPicUrl = self.GetFullImgUrl(ztConfig.BgPicUrl)
+		ztConfig.WfPicUrl = self.GetFullImgUrl(ztConfig.WfPicUrl)
+		ztConfig.ShareImg = self.GetFullImgUrl(ztConfig.ShareImg)
+		ztConfig.UserNickName = wxUser.Nickname
+		ztConfig.IsTemp = ztConfig.IsTemp
+
+		ztConfig.CurTimes = dollar_win_model.GetJoinPaiedListCountByZtConfigId(cId, useCache)
+		userJoinList := dollar_win_model.GetJoinPaiedWinListByZtConfigIdAndUId(ztConfig.Id, wxUser.UserId, useCache)
+		userJoinCount = int64(len(userJoinList))
+
+		if wxUser != nil {
+			result := kefu.GetKfQrcodeImgFromD5c(wxUser.Unionid)
+			if result != nil && result.CheckCode == "0000" {
+				ztConfig.QrcodeImg = result.ImgPath
+			}
+		}
+		/*
+			gzhQrcode := channel_gzh_qrcode_model.GetById(ztConfig.AfterPaied)
+			if gzhQrcode != nil{
+						ztConfig.QrcodeImg = gzhQrcode.QrcodeImg
+				}*/
+	}
+
+	type ApiRet struct {
+		DollarWinConfg  *dollar_win_model.DollarWinZtConfig `json:"dollar_win_confg"`
+		UserJoinedCount int64                               `json:"user_joined_count"`
+	}
+
+	self.Data["json"] = &ApiRet{DollarWinConfg: ztConfig, UserJoinedCount: userJoinCount}
+	self.ServeJSON()
+
+}
+
+func (self *DollarWinController) GetJoinStateList() {
+
+	_id := self.Ctx.Input.Param(":id")
+	cId, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+
+	ztConfig := dollar_win_model.GetDollarWinZtConfigById(cId, useCache)
+	if ztConfig == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	var currenWxUser *user_model.WxUser
+	var joinWinList []*dollar_win_model.DollarWinZtJoin
+	var userJoinList []*dollar_win_model.DollarWinZtJoin
+	var list []*dollar_win_model.DollarWinZtJoin
+	listCount := int64(0)
+	frendCount := int64(0)
+
+	type ApiRet struct {
+		JoinWinList    []*dollar_win_model.DollarWinZtJoin `json:"join_win_list"`
+		UserJoinList   []*dollar_win_model.DollarWinZtJoin `json:"user_join_list"`
+		List           []*dollar_win_model.DollarWinZtJoin `json:"list"`
+		ListCount      int64                               `json:"list_count"`
+		FrendListCount int64                               `json:"frend_list_count"`
+	}
+
+	currenWxUser = self.GetCurrentWxUser(useCache)
+	joinWinList = dollar_win_model.GetJoinPaiedWinListByZtConfigId(ztConfig.Id, useCache)
+	userJoinList = dollar_win_model.GetJoinPaiedWinListByZtConfigIdAndUId(ztConfig.Id, currenWxUser.UserId, useCache)
+	list = dollar_win_model.GetJoinPaiedListByZtConfigId(ztConfig.Id, page, perPage, useCache)
+	listCount = dollar_win_model.GetJoinPaiedListCountByZtConfigId(ztConfig.Id, useCache)
+	frendCount = dollar_win_model.GetFrendJoinPaiedListCountByZtConfigId(ztConfig.Id, currenWxUser.UserId, useCache)
+
+	for _, item := range joinWinList {
+		wxUser := user_model.GetWxUserByUserId(item.UserId, useCache)
+		if wxUser != nil {
+			item.WxUserHead = self.GetFullImgUrl(wxUser.Head)
+			item.WxUserName = wxUser.Nickname
+		}
+	}
+	for _, item := range userJoinList {
+		wxUser := user_model.GetWxUserByUserId(item.UserId, useCache)
+		if wxUser != nil {
+			item.WxUserHead = self.GetFullImgUrl(wxUser.Head)
+			item.WxUserName = wxUser.Nickname
+		}
+	}
+	for _, item := range list {
+		wxUser := user_model.GetWxUserByUserId(item.UserId, useCache)
+		if wxUser != nil {
+			item.WxUserHead = self.GetFullImgUrl(wxUser.Head)
+			item.WxUserName = wxUser.Nickname
+			item.IsInvite = false
+			if item.TempInviteId > 0 && item.TempInviteId != 607148 {
+				//找出邀请人的参与记录,并比较,如果被邀请人在邀请人之后支付,则显示邀请人头像
+				inviteDollar := dollar_win_model.GetDollarWinZtJoinByTempInviteId(ztConfig.Id, item.TempInviteId, useCache)
+				if inviteDollar != nil {
+					inviter := user_model.GetWxUserByUserId(item.TempInviteId, false)
+					if inviter != nil {
+						if item.PaiedAt > inviteDollar.PaiedAt {
+							item.IsInvite = true
+							item.InviteUserHead = self.GetFullImgUrl(inviter.Head)
+						}
+					}
+				}
+			}
+		}
+	}
+
+	if joinWinList == nil {
+		joinWinList = make([]*dollar_win_model.DollarWinZtJoin, 0, 0)
+	}
+	if userJoinList == nil {
+		userJoinList = make([]*dollar_win_model.DollarWinZtJoin, 0, 0)
+	}
+	if list == nil {
+		list = make([]*dollar_win_model.DollarWinZtJoin, 0, 0)
+	}
+
+	self.Data["json"] = &ApiRet{JoinWinList: joinWinList, UserJoinList: userJoinList, List: list, ListCount: listCount, FrendListCount: frendCount}
+	self.ServeJSON()
+}
+
+func (self *DollarWinController) GetFrendJoinStateList() {
+
+	_id := self.Ctx.Input.Param(":id")
+	cId, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+
+	ztConfig := dollar_win_model.GetDollarWinZtConfigById(cId, useCache)
+	if ztConfig == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	var currenWxUser *user_model.WxUser
+
+	var frendJoinList []*dollar_win_model.DollarWinZtJoin
+	frendCount := int64(0)
+	type ApiRet struct {
+		FrendWinList   []*dollar_win_model.DollarWinZtJoin `json:"frend_list"`
+		FrendListCount int64                               `json:"frend_list_count"`
+	}
+
+	//找出会员自己的参与记录,如果自己的参与记录在好友之前,则在好友头像列表中加入会员自己的头像
+	currenWxUser = self.GetCurrentWxUser(useCache)
+	myDollar := dollar_win_model.GetDollarWinZtJoinByTempInviteId(ztConfig.Id, currenWxUser.UserId, useCache)
+	if myDollar == nil {
+		frendJoinList = make([]*dollar_win_model.DollarWinZtJoin, 0, 0)
+		frendCount = 0
+	} else {
+		paiedAt := myDollar.PaiedAt
+		frendJoinList = dollar_win_model.GetJoinFrendPaiedWinListByZtConfigIdAndUId(ztConfig.Id, currenWxUser.UserId, paiedAt, page, perPage, useCache)
+		frendCount = dollar_win_model.GetFrendJoinPaiedListCountByZtConfigId(ztConfig.Id, currenWxUser.UserId, useCache)
+	}
+
+	for _, item := range frendJoinList {
+		wxUser := user_model.GetWxUserByUserId(item.UserId, useCache)
+		if wxUser != nil {
+			item.WxUserHead = self.GetFullImgUrl(wxUser.Head)
+			item.WxUserName = wxUser.Nickname
+			if myDollar != nil {
+				if item.PaiedAt > myDollar.PaiedAt {
+					item.IsInvite = true
+					item.InviteUserHead = self.GetFullImgUrl(currenWxUser.Head)
+				} else {
+					item.IsInvite = false
+				}
+			}
+		}
+	}
+
+	if frendJoinList == nil {
+		frendJoinList = make([]*dollar_win_model.DollarWinZtJoin, 0, 0)
+	}
+	self.Data["json"] = &ApiRet{FrendWinList: frendJoinList, FrendListCount: frendCount}
+	self.ServeJSON()
+}
+
+var orderLock sync.Mutex
+
+func (self *DollarWinController) DollarWinOrder() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	temp_invite_id, _ := self.GetInt64("temp_invite_id")
+	useCache, _ := self.GetBool("cache", false)
+
+	wxUser := self.GetCurrentWxUser(false)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	user := user_model.GetUserById(wxUser.UserId, false)
+	if user == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	act := dollar_win_model.GetDollarWinZtConfigById(id, useCache)
+	if act == nil {
+		self.ReturnError(403, []string{"actNotExist", "活动不存在"}, "", nil)
+	}
+
+	//注册第五创
+	if user.IsRegistD5c == 0 {
+		go regist.RegistD5c(user.Tel)
+	}
+
+	orderLock.Lock()
+	defer orderLock.Unlock()
+
+	if act.StartTime.Unix() > time.Now().Unix() {
+		self.ReturnError(403, []string{"actNotStart", "活动尚未开始"}, "", nil)
+	}
+	if act.StopTime.Unix() < time.Now().Unix() {
+		self.ReturnError(403, []string{"actNotStart", "活动已经结束"}, "", nil)
+	}
+
+	joinOrderList := dollar_win_model.GetJoinPaiedWinListByZtConfigIdAndUId(id, user.Id, false)
+	if int64(len(joinOrderList)) >= act.JoinTimesLimit {
+		self.ReturnError(403, []string{"joinedTimesLimit", "您的参与次数已达到上限"}, "", nil)
+	}
+	//若开启临时邀请机制,则应记录临时邀请人
+	var tempInviteId int64
+	if act.IsTemp == true {
+		inviteWxUser := user_model.GetWxUserById(temp_invite_id, false)
+		if inviteWxUser != nil {
+			tempInviteId = inviteWxUser.UserId
+		}
+	}
+	//如果邀请人为本人,则不记录
+	if user.Id == tempInviteId {
+		tempInviteId = int64(0)
+	}
+
+	joinOrder := new(dollar_win_model.DollarWinZtJoin).Create(user.Id, id, act.JoinCostAmount, tempInviteId)
+	if joinOrder == nil {
+		self.ReturnError(403, []string{"orderFailed", "下单失败,请稍后重试"}, "", nil)
+	}
+
+	type Ret struct {
+		OrderId string `json:"order_id"`
+	}
+
+	self.Data["json"] = &Ret{OrderId: joinOrder.OrderId}
+	self.ServeJSON()
+}
+
+func (self *DollarWinController) DollarWinRank() {
+	cron_controller.DollarWinRank()
+	type Ret struct {
+		State string `json:"state"`
+	}
+	self.Data["json"] = &Ret{State: "ok"}
+	self.ServeJSON()
+}

+ 136 - 0
go/gopath/src/fohow.com/apps/controllers/exchange_controller/callback_exchange_controller.go

@@ -0,0 +1,136 @@
+package exchange_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/exchange_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/yidui"
+	"strings"
+	"time"
+)
+
+type YiZhiFuRet struct {
+	IsOk      bool   `json:"is_ok"`
+	ResultStr string `json:"result_str"`
+}
+
+//电信翼支付
+func (self *ExchangeController) PagePay() {
+	platform := self.Ctx.Input.Param(":platform")
+
+	user := self.GetCurrentUser(true)
+	wxUId := self.GetCurrentWxUserId()
+
+	count, _ := self.GetInt64("count") //兑换数量
+	if count <= 0 {
+		self.ReturnError(403, apps.ParamsRequired, "", nil)
+	}
+
+	if beego.BConfig.RunMode == beego.DEV {
+
+		user = user_model.GetUserById(600116, true)
+		user.Tel = "18024285081"
+	}
+
+	var ret *YiZhiFuRet
+	switch platform {
+	case "yizhifu":
+		ret = yiZhiFuPay(count, user.Tel, user.Id, wxUId)
+		//
+	}
+
+	self.Data["json"] = ret
+	self.ServeJSON()
+}
+
+//回调地址
+func (self *ExchangeController) CallbackAfterPagePay() {
+
+	_platform := self.Ctx.Input.Param(":platform") //平台
+	platform := exchange_model.GetPlatformByTag(_platform, false)
+	if platform == nil || platform.State != exchange_model.PLATFORM_STATE_ONLINE {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	beego.BeeLogger.Warn("CallbackAfterPagePay callback.....state:%s\n msg:%s\n orderseq:%s\n money:%s\n sign:%s\n", self.GetString("state"), self.GetString("msg"), self.GetString("ORDERSEQ"), self.GetString("money"), self.GetString("sign"))
+
+	state := self.GetString("state")
+	msg := self.GetString("msg")
+	orderSeq := self.GetString("ORDERSEQ")
+	money := self.GetString("money")
+	sign := self.GetString("sign")
+
+	beego.BeeLogger.Warn("CallbackAfterPagePay page jump pay: state(%s),msg(%s),orderSeq(%s),money(%s),sign:(%s)", state, msg, orderSeq, money, sign)
+	if state != "1" {
+		self.ReturnError(403, apps.PayFail, "", nil)
+	}
+
+	var verifySign bool
+	waitingSign := fmt.Sprintf("%s%s%s%s%s", state, msg, orderSeq, money, yidui.YIZHIFU_SIGN)
+	verifySign = yidui.VerifySignature(waitingSign, sign)
+	if !verifySign {
+		beego.BeeLogger.Error("CallbackAfterPagePay tradeNo=%s sign=%s not verify.", orderSeq, sign)
+		self.ReturnError(403, apps.PayFail, "", nil)
+	}
+
+	order := exchange_model.GetExchangeOrderByTradeNo(orderSeq, false)
+	if order == nil {
+		beego.BeeLogger.Error("CallbackAfterPagePay tradeNo=%s not exist.", orderSeq)
+		self.ReturnError(403, apps.PayFail, "", nil)
+	}
+
+	if order.State == 1 {
+		beego.BeeLogger.Error("CallbackAfterPagePay tradeNo=%s has pay", orderSeq)
+		self.ReturnError(403, apps.PayFail, "", nil)
+	}
+
+	order.State = 1
+	order.PaiedAt = time.Now().Unix()
+	if !order.Save() {
+		beego.BeeLogger.Error("CallbackAfterPagePay return. save tradeNo=%s fail", orderSeq)
+	}
+
+	s := balance_model.BALANCE_SOURCE_PLATFORM_EXCHANGE
+	remark := platform.Name
+	new(balance_model.Balance).Create(order.WxUserId, order.UserId, order.PaiedCount, s, order.OrderId, remark)
+
+	self.Data["json"] = order
+	self.ServeJSON()
+}
+
+func yiZhiFuPay(count int64, tel string, userId, wxUId int64) *YiZhiFuRet {
+
+	yizhifuPlatform := exchange_model.GetPlatformByTag("yizhifu", false)
+	if yizhifuPlatform == nil || yizhifuPlatform.State != exchange_model.PLATFORM_STATE_ONLINE {
+		return &YiZhiFuRet{false, apps.NoExist[1]}
+	}
+
+	//起兑代金券个数
+	labiStartCount := yizhifuPlatform.PStartCount * yizhifuPlatform.LabiCount / yizhifuPlatform.PCount
+
+	beego.BeeLogger.Warn("yizhifu_pay: labiStartCount(%d), count(%d), Pcount(%d), LabiCount(%d) , consumeJifen(%d)", labiStartCount, count, yizhifuPlatform.PCount, yizhifuPlatform.LabiCount, count*yizhifuPlatform.PCount/yizhifuPlatform.LabiCount)
+	//实际兑换个数不能小于起兑个数,且必须满足兑换比例,换算成商家代金券必须为整数
+	if count < labiStartCount || count*yizhifuPlatform.PCount%yizhifuPlatform.LabiCount != 0 {
+		return &YiZhiFuRet{false, apps.PlatformExchangeCountError[1]}
+	}
+
+	//商户平台扣除代金券数量
+	payAmount := count * yizhifuPlatform.PCount / yizhifuPlatform.LabiCount
+
+	tradeNo := exchange_model.CreateOrderId("YDEX")
+	//创建未兑换成功的订单
+	new(exchange_model.Order).BeforePayCreate(wxUId, userId, yizhifuPlatform.Id, payAmount, count, exchange_model.SOURCE_PLATFORM_EXCHANGE, tradeNo)
+
+	returnString := yidui.YiZhiFuPay(payAmount, tel, fmt.Sprintf("%s%s", beego.AppConfig.String("ApiHost"), "/v1/pf/yizhifu/page/callback"), "http://m.d5ct.com/user", tradeNo)
+
+	isOk := false
+
+	if strings.Contains(returnString, "https://") {
+		isOk = true
+	}
+
+	return &YiZhiFuRet{isOk, returnString}
+}

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

@@ -0,0 +1,383 @@
+package exchange_controller
+
+import (
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/exchange_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/cache"
+	"fohow.com/libs/yidui"
+	"github.com/astaxie/beego"
+	"strconv"
+	"time"
+)
+
+const (
+	ZHINENGYUN_YIDONG_GET_CODE = "yidong_code"
+	ZHINENGYUN_YIDONG_ACCEPTID = "yidong_acceptid"
+
+	ZHINENGYUN_YIDONG_EXCHANGE_BASE = 100 //广西智能云移动84代金券等于100分钱即1元。
+	// 作为FOHOW玖玖兑换基础。(用户兑换160代金券/系统设定80基数)*100分钱 = 移动消耗的金钱即100*2分钱 = 移动消耗的代金券84*2代金券。
+
+	YIDONG_PAY_NOTIFY_URL = "none" //兑换借口回调地址
+)
+
+type CheckResult struct {
+	RetCode string `json:"ret_code"`
+	RetMsg  string `json:"ret_msg"`
+	Balance int64  `json:"balance"`
+}
+
+type ExchangeResult struct {
+	RetCode string `json:"ret_code"`
+	RetMsg  string `json:"ret_msg"`
+	TradeNo string `json:"trade_no"`
+}
+
+//查询对接商户的代金券余额
+func (self *ExchangeController) Check() {
+	_id := self.Ctx.Input.Param(":id")
+	pId, _ := strconv.ParseInt(_id, 10, 64)
+	platform := exchange_model.GetPlatformById(pId, false)
+	if platform == nil || platform.State != exchange_model.PLATFORM_STATE_ONLINE {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	user := self.GetCurrentUser(false)
+
+	result := helpers.GetCheckPlatformMallBalance(platform.Secret, user.Tel, platform.CheckUrl)
+
+	if result == nil || result.RetCode != exchange_model.PL_RET_CODE_SUCCESS {
+		beego.BeeLogger.Error("Platform check failed, platform id:%d, retMsg:%s", platform.Id, result)
+		self.ReturnError(403, apps.PlatformCheckFailed, "", nil)
+	}
+	type Ret struct {
+		TotalBalance int64 `json:"total"`
+	}
+	self.Data["json"] = &Ret{TotalBalance: result.Balance}
+	self.ServeJSON()
+}
+
+//通兑接口
+func (self *ExchangeController) Exchange() {
+	_id := self.Ctx.Input.Param(":id")
+	pId, _ := strconv.ParseInt(_id, 10, 64)
+	platform := exchange_model.GetPlatformById(pId, false)
+	if platform == nil || platform.State != exchange_model.PLATFORM_STATE_ONLINE {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	if len(platform.Secret) < 16 {
+		self.ReturnError(403, apps.ExchangeSecretError, "", nil)
+	}
+	count, _ := self.GetInt64("count")
+	if count <= 0 {
+		self.ReturnError(403, apps.ParamsRequired, "", nil)
+	}
+	user := self.GetCurrentUser(false)
+	wxUserId := self.GetCurrentWxUserId()
+	// user := user_model.GetUserById(1, true)
+	// tel := "13570643147"
+	platformCount := count * platform.PCount / platform.LabiCount //商户平台扣除代金券数量
+	result := helpers.ExchangePlatformMallBalance(platformCount, platform.Secret, user.Tel, platform.ExchangeUrl, helpers.EXTYPE_TONGDUI)
+	if result == nil || result.RetCode != exchange_model.PL_RET_CODE_SUCCESS {
+		beego.BeeLogger.Error("Platform exchange failed, platform id:%d, retMsg:%s", platform.Id, result.RetMsg)
+		self.ReturnError(403, apps.PlatformExchangeFailed, "", nil)
+	}
+
+	//创建订单
+	order := new(exchange_model.Order).Create(wxUserId, user.Id, platform.Id, platformCount, count,
+		exchange_model.SOURCE_PLATFORM_EXCHANGE, result.TradeNo)
+	if order != nil {
+		s := balance_model.BALANCE_SOURCE_PLATFORM_EXCHANGE
+		remark := platform.Name
+		new(balance_model.Balance).Create(wxUserId, user.Id, count, s, order.OrderId, remark)
+	}
+	self.Data["json"] = order
+	self.ServeJSON()
+}
+
+//商户列表
+func (self *ExchangeController) 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 := exchange_model.GetPlatforms(page, perPage, cache)
+	count := exchange_model.GetPlatformsCount(cache)
+	type Ret struct {
+		List      []*exchange_model.Platform `orm:"-"         json:"list"`
+		ListCount int64                      `orm:"-"         json:"list_count"`
+	}
+	self.Data["json"] = &Ret{ListCount: count, List: list}
+	self.ServeJSON()
+}
+
+//特殊平台查询余额,对接他们的规则
+func (self *ExchangeController) SpecialCheck() {
+	platform := self.Ctx.Input.Param(":platform")
+	// tel := self.GetString("tel")
+	user := self.GetCurrentUser(true)
+	//余额(分)
+	balance := int64(0)
+	switch platform {
+	case "liantong":
+		balance = yidui.LianTongCheckBalance(user.Tel)
+	case "yidong":
+
+		/**
+		测试号码15007831001
+		*/
+		if beego.BConfig.RunMode == beego.DEV {
+			user.Tel = "15007831001"
+			if user.Id == 600123 || user.Id == 600126 {
+				balance = 3064
+				break
+			}
+		}
+
+		code := self.GetString("code")
+		k := fmt.Sprintf("%s_%s", ZHINENGYUN_YIDONG_ACCEPTID, user.Tel)
+		acceptId, ok := cache.Cache.Get(k).(string)
+		if !ok {
+			//验证码过期
+			self.ReturnError(403, apps.TelCodesExpired, "", nil)
+		}
+		beego.BeeLogger.Warn("yidongcheck: tel(%s),acceptid(%s),code(%s)", user.Tel, acceptId, code)
+		jifen, isOk := yidui.YiDongCheckBalance(user.Tel, code, acceptId)
+		balance = jifen
+		//若查询成功,则使流水号过期。
+		if isOk {
+			cache.Cache.Delete(k)
+		}
+	}
+
+	type Ret struct {
+		TotalBalance int64 `json:"total"` //单位分
+	}
+	self.Data["json"] = &Ret{TotalBalance: balance}
+	self.ServeJSON()
+}
+
+//特殊平台支付,对接他们的规则
+func (self *ExchangeController) SpecialExchange() {
+	platform := self.Ctx.Input.Param(":platform") //平台
+	count, _ := self.GetInt64("count")            //兑换数量
+	if count <= 0 {
+		self.ReturnError(403, apps.ParamsRequired, "", nil)
+	}
+
+	switch platform {
+	case "liantong":
+		self.liantongPay(count)
+	case "yidong":
+		self.yidongPay(count)
+	}
+}
+
+func (self *ExchangeController) liantongPay(count int64) {
+	user := self.GetCurrentUser(true)
+	wxUserId := self.GetCurrentWxUserId()
+	pwd := self.GetString("pwd") //支付密码
+	balance := yidui.LianTongCheckBalance(user.Tel)
+	if balance < count {
+		self.ReturnError(403, apps.BalanceNotEnough, "", nil)
+	}
+
+	platform := exchange_model.GetPlatformByTag("liantong", false)
+	if platform == nil || platform.State != exchange_model.PLATFORM_STATE_ONLINE {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	//起兑代金券个数
+	labiStartCount := platform.PStartCount * platform.LabiCount / platform.PCount
+	//实际兑换个数不能小于起兑个数,且必须满足兑换比例,换算为整数的商家代金券
+	if count < labiStartCount || count*platform.PCount%platform.LabiCount != 0 {
+		self.ReturnError(403, apps.PlatformExchangeCountError, "", nil)
+	}
+	//商户平台扣除代金券数量
+	platformCount := count * platform.PCount / platform.LabiCount
+	tradeNo := exchange_model.CreateOrderId("LTEX")
+	result := yidui.LianTongPay(platformCount, user.Tel, pwd, tradeNo)
+	if result == nil || result.Data != "success" {
+		self.ReturnError(403, apps.PlatformExchangeFailed, "", nil)
+	}
+	//创建订单
+	order := new(exchange_model.Order).Create(wxUserId, user.Id, platform.Id, platformCount, count,
+		exchange_model.SOURCE_PLATFORM_EXCHANGE, tradeNo)
+	if order != nil {
+		s := balance_model.BALANCE_SOURCE_PLATFORM_EXCHANGE
+		remark := platform.Name
+		new(balance_model.Balance).Create(wxUserId, user.Id, count, s, order.OrderId, remark)
+	}
+	self.Data["json"] = order
+	self.ServeJSON()
+}
+
+/**
+测试号码15007831001
+广西区移动号码查询代金券和兑换代金券,均需要短信验证码以及acceptid流水号。
+此接口需要调用移动接口发送短信,同时存储返回的acceptid流水号。
+需要ip防刷, 需要防止短信频繁发送。
+*/
+func (self *ExchangeController) SendYiDongCode() {
+	// tel := self.GetString("tel")
+	user := self.GetCurrentUser(true)
+	tel := user.Tel
+
+	type Ret struct {
+		IsOk bool   `json:"is_ok"`
+		Msg  string `json:"msg"`
+	}
+
+	if beego.BConfig.RunMode == beego.DEV {
+		tel = "15007831001"
+
+		if user.Id == 600123 || user.Id == 600126 {
+			self.Data["json"] = &Ret{IsOk: true, Msg: ""}
+			self.ServeJSON()
+		}
+	}
+
+	platform := exchange_model.GetPlatformByTag("yidong", false)
+	if platform == nil || platform.State != exchange_model.PLATFORM_STATE_ONLINE {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	//防刷,IP
+	ip := self.Ctx.Input.IP()
+	ipKey := fmt.Sprintf("SMS_Send_IP_%s_msgType_%s", ip, ZHINENGYUN_YIDONG_GET_CODE)
+	if ipTimes, ok := cache.Cache.Get(ipKey).(int64); ok {
+		ipTimes = ipTimes + 1
+		cache.Cache.Put(ipKey, ipTimes, 1*time.Minute)
+		if ipTimes > 10 {
+			// 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)
+	}
+
+	//防刷,发送太频繁
+	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 > 10 {
+			self.ReturnError(403, apps.CodesSendTooOften, "", nil)
+		}
+	} else {
+		sendTimes = 1
+		cache.Cache.Put(sendTimesKey, sendTimes, 1*time.Minute)
+	}
+
+	k := fmt.Sprintf("%s_%s", ZHINENGYUN_YIDONG_ACCEPTID, tel)
+
+	result := yidui.GetYiDongCode(tel)
+
+	if result == nil {
+		self.ReturnError(403, apps.DataUnMarsha1Error, "", nil)
+	}
+
+	if !result.IsOK {
+		if result.Msg == "发送短信出错" {
+			result.Msg = "对不起,您的手机所在地暂未支持"
+		}
+		self.ReturnError(403, []string{"error", result.Msg}, "", nil)
+	}
+
+	cache.Cache.Put(k, result.Data.AcceptId, 3*time.Minute) //此时间根据短信验证码时间来决定
+	beego.BeeLogger.Warn("yidong_sendcode: tel(%s) ,acceptid(%s)", user.Tel, result.Data.AcceptId)
+
+	self.Data["json"] = &Ret{IsOk: result.IsOK, Msg: result.Msg}
+	self.ServeJSON()
+}
+
+/**
+测试手机号15007831001
+根据手机号、流水号、短信验证码兑换代金券
+*/
+func (self *ExchangeController) yidongPay(count int64) {
+	user := self.GetCurrentUser(true)
+	wxUserId := self.GetCurrentWxUserId()
+	if beego.BConfig.RunMode == beego.DEV {
+		user.Tel = "15007831001"
+	}
+
+	platform := exchange_model.GetPlatformByTag("yidong", false)
+	if platform == nil || platform.State != exchange_model.PLATFORM_STATE_ONLINE {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	//起兑代金券个数
+	labiStartCount := platform.PStartCount * platform.LabiCount / platform.PCount
+
+	beego.BeeLogger.Warn("yidongpay: labiStartCount(%d), count(%d), Pcount(%d), LabiCount(%d) , consumeJifen(%d)", labiStartCount, count, platform.PCount, platform.LabiCount, count*platform.PCount/platform.LabiCount)
+	//实际兑换个数不能小于起兑个数,且必须满足兑换比例,换算成商家代金券必须为整数
+	if count < labiStartCount || count*platform.PCount%platform.LabiCount != 0 {
+		self.ReturnError(403, apps.PlatformExchangeCountError, "", nil)
+	}
+	//需要消耗的移动代金券, 为p_start_count的倍数
+	//consumeJifen := count*platform.PCount/platform.LabiCount
+	consumeJifen := count * platform.PStartCount / platform.LabiRange
+	if consumeJifen%platform.PStartCount != 0 {
+		self.ReturnError(403, apps.PlatformExchangeCountError, "", nil)
+	}
+
+	//传给接口的分钱
+	//fenMoney := (count / platform.LabiCount ) * ZHINENGYUN_YIDONG_EXCHANGE_BASE
+	fenMoney := (count / platform.LabiRange) * ZHINENGYUN_YIDONG_EXCHANGE_BASE
+
+	beego.BeeLogger.Warn("yidongpay: tel(%s), count(%d), labiRange(%d), fenMoney(%d)", user.Tel, count, platform.LabiRange, fenMoney)
+
+	if beego.BConfig.RunMode == beego.DEV {
+		if user.Id == 600123 || user.Id == 600126 {
+			order := new(order_model.Order)
+			self.Data["json"] = order
+			self.ServeJSON()
+		}
+	}
+
+	code := self.GetString("code")
+	k := fmt.Sprintf("%s_%s", ZHINENGYUN_YIDONG_ACCEPTID, user.Tel)
+	acceptId, ok := cache.Cache.Get(k).(string)
+	if !ok {
+		//验证码过期
+		self.ReturnError(403, apps.TelCodesExpired, "", nil)
+	}
+
+	balance, isOk := yidui.YiDongCheckBalance(user.Tel, code, acceptId)
+	beego.BeeLogger.Warn("yidongpay: balance(%d), isOk(%v)", balance, isOk)
+	if balance < consumeJifen {
+		self.ReturnError(403, apps.BalanceNotEnough, "", nil)
+	}
+
+	orderNum := exchange_model.CreateOrderId("YDEX")
+
+	beego.BeeLogger.Warn("yidongpay: tel(%s),code(%s),acceptid(%s), count(%d), labiRange(%d), fenMoney(%d)", user.Tel, code, acceptId, count, platform.LabiRange, fenMoney)
+
+	result := yidui.YiDongPay(fenMoney, user.Tel, code, acceptId, orderNum, YIDONG_PAY_NOTIFY_URL)
+	if result == nil || !result.IsOK {
+		self.ReturnError(403, apps.PlatformExchangeFailed, "", nil)
+	}
+
+	//创建订单
+	order := new(exchange_model.Order).Create(wxUserId, user.Id, platform.Id, consumeJifen, count,
+		exchange_model.SOURCE_PLATFORM_EXCHANGE, orderNum)
+	if order != nil {
+		s := balance_model.BALANCE_SOURCE_PLATFORM_EXCHANGE
+		remark := platform.Name
+		new(balance_model.Balance).Create(wxUserId, user.Id, count, s, order.OrderId, remark)
+	}
+
+	//若查询成功,则使流水号过期。
+	if result.IsOK {
+		cache.Cache.Delete(k)
+	}
+
+	self.Data["json"] = order
+	self.ServeJSON()
+}

+ 103 - 0
go/gopath/src/fohow.com/apps/controllers/exchange_controller/exchange_test.go

@@ -0,0 +1,103 @@
+package exchange_controller
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	// "crypto/rand"
+	"encoding/base64"
+	// "encoding/hex"
+	"fmt"
+	// "io"
+	"testing"
+	// "os"
+)
+
+func TestMain(t *testing.T) {
+	demo2()
+	// demo1()
+}
+
+func demo2() {
+	// key := []byte("example key 1234")
+	// plaintext := []byte("some plaintext")
+
+	// block, err := aes.NewCipher(key)
+	// if err != nil {
+	// 	panic(err)
+	// }
+
+	// // The IV needs to be unique, but not secure. Therefore it's common to
+	// // include it at the beginning of the ciphertext.
+	// ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+	// iv := ciphertext[:aes.BlockSize]
+	// if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+	// 	panic(err)
+	// }
+
+	// stream := cipher.NewCFBEncrypter(block, iv)
+	// stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
+	// base64Str := base64.StdEncoding.EncodeToString(ciphertext)
+	// hexStr := hex.EncodeToString(ciphertext)
+	// a := fmt.Sprintf("%s=>%v, %v", plaintext, base64Str, hexStr)
+	// fmt.Println(a)
+	// It's important to remember that ciphertexts must be authenticated
+	// (i.e. by using crypto/hmac) as well as being encrypted in order to
+	// be secure.
+
+	key := []byte("example key 1234")
+	ciphertext, _ := base64.StdEncoding.DecodeString("gMok0OuLNJSX91zIdaVPnWXMTYEqSA24mxnrgzfo")
+	fmt.Printf("11: %v \n", ciphertext)
+	// ciphertext, _ := hex.DecodeString("22277966616d9bc47177bd02603d08c9a67d5380d0fe8cf3b44438dff7b9")
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		panic(err)
+	}
+
+	// The IV needs to be unique, but not secure. Therefore it's common to
+	// include it at the beginning of the ciphertext.
+	if len(ciphertext) < aes.BlockSize {
+		panic("ciphertext too short")
+	}
+	iv := ciphertext[:aes.BlockSize]
+	fmt.Println(iv)
+	ciphertext = ciphertext[aes.BlockSize:]
+	fmt.Println(ciphertext)
+
+	stream := cipher.NewCFBDecrypter(block, iv)
+
+	// XORKeyStream can work in-place if the two arguments are the same.
+	stream.XORKeyStream(ciphertext, ciphertext)
+	fmt.Printf("%s", ciphertext)
+
+}
+
+func demo1() {
+
+	var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
+	//需要去加密的字符串
+	plaintext := []byte("My name is Astaxie")
+	//aes的加密字符串
+	key_text := "astaxie12798akljzmknm.ahkjkljl;k"
+
+	fmt.Println(len(key_text))
+
+	// 创建加密算法aes
+	c, err := aes.NewCipher([]byte(key_text))
+	if err != nil {
+		fmt.Printf("Error: NewCipher(%d bytes) = %s", len(key_text), err)
+	}
+
+	//加密字符串
+	cfb := cipher.NewCFBEncrypter(c, commonIV)
+	ciphertext := make([]byte, len(plaintext))
+	fmt.Printf("before: %v \n", ciphertext)
+	cfb.XORKeyStream(ciphertext, plaintext)
+	fmt.Printf("after: %x\n", ciphertext)
+	fmt.Printf("%s=>%x\n", plaintext, ciphertext)
+
+	// 解密字符串
+	cfbdec := cipher.NewCFBDecrypter(c, commonIV)
+	plaintextCopy := make([]byte, len(plaintext))
+	cfbdec.XORKeyStream(plaintextCopy, ciphertext)
+	fmt.Printf("%x=>%s\n", ciphertext, plaintextCopy)
+}

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

@@ -0,0 +1,31 @@
+package exchange_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{"GetYesterdayTotalPaiedCount", "List", "CallbackAfterPagePay"}
+	exceptCheckWxUserLoginAction = []string{"GetYesterdayTotalPaiedCount", "List", "CallbackAfterPagePay"}
+)
+
+type ExchangeController struct {
+	apps.BaseController
+}
+
+func (self *ExchangeController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 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()
+}

+ 528 - 0
go/gopath/src/fohow.com/apps/controllers/granary_controller/granary_controller.go

@@ -0,0 +1,528 @@
+package granary_controller
+
+import (
+	"fmt"
+	"strconv"
+	// "strings"
+	// "sync"
+	"time"
+	// // "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/address_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/project_model"
+	// // "fohow.com/libs/wx_mp"
+	// "fmt"
+	// "fohow.com/apps/helpers"
+	"fohow.com/apps/models/granary_model"
+	"fohow.com/apps/models/user_model"
+	// "fohow.com/libs/tool"
+	"github.com/astaxie/beego"
+	"fohow.com/apps/models/balance_model"
+)
+
+//开仓放粮
+func (self *GranaryController) DistributeBonus() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	bonus := project_model.GetProjectBonusById(id, false)
+	if bonus == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	investWay := project_model.GetProjectInvestWayById(bonus.ProjectInvestWayId, false)
+	if investWay == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	pjs := project_model.GetInvestListByPIdAndWId(bonus.ProjectId, bonus.ProjectInvestWayId, false)
+	for _, pj := range pjs {
+		granary := granary_model.GetGranaryByBonusIdAndUId(bonus.Id, pj.UserId, false)
+		count := pj.InvestCount / investWay.GapLimit * bonus.Count
+		selfUseMin := pj.InvestCount / investWay.GapLimit * bonus.SelfUseMinCount
+		buybackMax := pj.InvestCount / investWay.GapLimit * bonus.BuyBackMaxCount
+		buybackMin := pj.InvestCount / investWay.GapLimit * bonus.BuyBackMinCount
+		if granary == nil {
+			granary = new(granary_model.Granary).Create(bonus.Id, pj.UserId, bonus.ProductId, count, selfUseMin, buybackMax, buybackMin)
+		} else {
+			granary.TotalCount = count
+			granary.SelfUseMin = selfUseMin
+			granary.BuybackMax = buybackMax
+			granary.BuybackMin = buybackMin
+			granary.Save()
+		}
+	}
+	self.ServeJSON()
+}
+
+//针对某个收成的粮仓-回购-回款
+func (self *GranaryController) SendBuybackAmount() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	bonus := project_model.GetProjectBonusById(id, false)
+	now := time.Now().Unix()
+	beego.BeeLogger.Warn("bonus: %s", bonus)
+
+	if bonus == nil || now <= bonus.BuyBackStopAt.Unix() || bonus.BuyBackMaxCount <= int64(0) || bonus.BuyBackPrice <= int64(0) {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	project := project_model.GetProjectById(bonus.ProjectId, false)
+	if project == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	granarys := granary_model.GetGranaryByBonusId(bonus.Id, false)
+	for _, granary := range granarys {
+		if granary.BuybackCount <= 0 {
+			continue
+		}
+
+		receiveWxUser := user_model.GetWxUserByUserId(granary.UserId, false)
+		if receiveWxUser == nil {
+			continue
+		}
+
+		receiveBuybackAmount := bonus.BuyBackPrice * granary.BuybackCount
+
+		rId := fmt.Sprintf("buybackOrderId-%d", granary.Id)
+		source := balance_model.CASH_SOURCE_RECEIVE_BUYBACK
+		remark := fmt.Sprintf("%s", project.Title)
+		//查询有没相关资金记录
+		cashBalance := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(receiveWxUser.Id, rId, source)
+		//有就不发
+		if cashBalance == nil {
+			//没有就发
+			reCashBalance := new(balance_model.CashBalance).Create(receiveWxUser.Id, receiveBuybackAmount, source, rId, remark)
+			if reCashBalance != nil {
+				granary.ReBuybackAmount = receiveBuybackAmount
+				granary.Save()
+			}
+		}
+	}
+	self.ServeJSON()
+}
+
+//粮仓列表
+func (self *GranaryController) List() {
+	cache, _ := self.GetBool("cache", false)
+	user := self.GetCurrentUser(cache)
+	// user = user_model.GetUserById(600105, cache)
+	page, _ := self.GetInt64("page")
+	perPage, _ := self.GetInt64("per_page")
+	if page <= 0 {
+		page = 1
+	}
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	type Ret struct {
+		List      []*granary_model.Granary `json:"list"`
+		ListCount int64                    `json:"list_count"`
+	}
+	list := granary_model.GetGranariesByUId(user.Id, page, perPage, cache)
+	listCount := granary_model.GetGranariesCountByUId(user.Id)
+	for _, granary := range list {
+		p := product_model.GetProductById(granary.ProductId, true)
+		if p == nil {
+			self.ReturnError(403, apps.NoExist, "", nil)
+		}
+		//剩余份数
+		//granary.LeftCount = granary.TotalCount - granary.SoldCount - granary.SelfUseCount - granary.BuybackCount
+		////剩余份数,回购完成才减去回购份数
+		granary.LeftCount = granary.TotalCount - granary.SelfUseCount - granary.SoldCount
+		if granary.BuybackCount > 0 && granary.ReBuybackAmount > 0 {
+			granary.LeftCount = granary.LeftCount - granary.BuybackCount
+		}
+		granary.Product = p
+		granary.CTime = granary.CreatedAt.Unix()
+		bonus := project_model.GetProjectBonusById(granary.ProjectBonusId, true)
+		if bonus == nil {
+			self.ReturnError(403, apps.NoExist, "", nil)
+		}
+		//回购单价
+		granary.BuybackPrice = bonus.BuyBackPrice
+		//可回购金额
+		granary.BuybackAmount = bonus.BuyBackPrice * granary.BuybackCount
+		//回购开始时间
+		granary.BuybackStartAt = bonus.BuyBackStartAt.Unix()
+		//回购结束时间
+		granary.BuybackStopAt = bonus.BuyBackStopAt.Unix()
+	}
+	self.Data["json"] = &Ret{List: list, ListCount: listCount}
+	self.ServeJSON()
+}
+
+//收成详情
+func (self *GranaryController) Info() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	cache, _ := self.GetBool("cache", false)
+	granary := granary_model.GetGranaryById(id, cache)
+	user := self.GetCurrentUser(true)
+	// user = user_model.GetUserById(600105, cache)
+	if granary == nil || user.Id != granary.UserId {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	p := product_model.GetProductById(granary.ProductId, true)
+	if p == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	granary.Product = p
+
+	bonus := project_model.GetProjectBonusById(granary.ProjectBonusId, true)
+	if bonus == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	//粮仓剩余可操作份数
+	validCount := granary.TotalCount - granary.SelfUseCount - granary.BuybackCount
+	// beego.BeeLogger.Error("------ 1 validCount:%d", validCount)
+	saleOrder := granary_model.GetSaleOrderByGId(id, false)
+	//如果有代销订单,减除代销订单的数量
+	if saleOrder != nil {
+		if saleOrder.State == granary_model.ORDER_STATE_ONLINE ||
+			saleOrder.State == granary_model.ORDER_STATE_SELL_UP {
+			validCount = validCount - saleOrder.TotalCount
+		} else {
+			validCount = validCount - granary.SoldCount
+		}
+		//售罄了
+		if validCount == 0 && saleOrder.TotalCount == saleOrder.SoldCount {
+			saleOrder.State = granary_model.ORDER_STATE_SELL_UP
+			saleOrder.Save()
+		}
+	}
+	granary.ValidCount = validCount
+
+	//剩余份数,回购完成才减去回购份数
+	granary.LeftCount = granary.TotalCount - granary.SelfUseCount - granary.SoldCount
+	if granary.BuybackCount > 0 && granary.ReBuybackAmount > 0 {
+		granary.LeftCount = granary.LeftCount - granary.BuybackCount
+	}
+
+	//回购单价
+	granary.BuybackPrice = bonus.BuyBackPrice
+	//可回购金额
+	granary.BuybackAmount = bonus.BuyBackPrice * granary.BuybackCount
+	//回购开始时间
+	granary.BuybackStartAt = bonus.BuyBackStartAt.Unix()
+	//回购结束时间
+	granary.BuybackStopAt = bonus.BuyBackStopAt.Unix()
+	granary.CTime = granary.CreatedAt.Unix()
+	self.Data["json"] = granary
+	self.ServeJSON()
+}
+
+//自用
+func (self *GranaryController) SelfUse() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	count, _ := self.GetInt64("count")
+	addressId, _ := self.GetInt64("address_id")
+	if count <= 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	// wxUser = user_model.GetWxUserById(2, true)
+	address := address_model.GetAddressByWxUIdAndId(wxUser.Id, addressId)
+	if address == nil {
+		self.ReturnError(403, apps.AddressNotExist, "", nil)
+	}
+
+	//粮仓剩余可操作份数
+	granary := granary_model.GetGranaryById(id, false)
+	if granary == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	validCount := granary.TotalCount - granary.SelfUseCount - granary.BuybackCount
+
+	saleOrder := granary_model.GetSaleOrderByPIdAndUIdAndGId(granary.ProductId, wxUser.UserId, granary.Id)
+
+	if saleOrder != nil && (saleOrder.State == granary_model.ORDER_STATE_ONLINE ||
+		saleOrder.State == granary_model.ORDER_STATE_SELL_UP) {
+		validCount = validCount - saleOrder.TotalCount
+	} else {
+		validCount = validCount - granary.SoldCount
+	}
+
+	if count > validCount {
+		self.ReturnError(403, apps.CountError, "", nil)
+	}
+	if granary == nil || wxUser.UserId != granary.UserId {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	p := product_model.GetProductById(granary.ProductId, true)
+	if p == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	totalPrice := p.RoboBalancePrice * count
+	buyPrice := p.BuyPrice * count
+
+	order := new(order_model.Order).Create(wxUser.Id, wxUser.UserId, p.Id, count, totalPrice, buyPrice, p.Price, p.RoboBalancePrice, order_model.SOURCE_XCX)
+	if order != nil {
+		order.Contact = address.Contact
+		order.Tel = address.Tel
+		order.Address = address.Province + address.City + address.District + address.Address
+		order.OrderType = order_model.ORDER_TYPE_SELF
+		order.Status = order_model.STATUS_PROCESSING
+		order.PaiedAt = time.Now().Unix() //支付时间存储秒级别
+		order.PaiedPrice = order.TotalPrice
+		order.GranaryId = id
+		if order.Save() {
+			granary.SelfUseCount += count
+			granary.Save()
+		}
+	}
+	self.Data["json"] = order
+	self.ServeJSON()
+}
+
+//自用订单历史
+func (self *GranaryController) GetSelfUseOrders() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	wxUser := self.GetCurrentWxUser(true)
+	// wxUser = user_model.GetWxUserById(2, true)
+	granary := granary_model.GetGranaryById(id, true)
+	if granary == nil || granary.UserId != wxUser.UserId {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	page, _ := self.GetInt64("page")
+	perPage, _ := self.GetInt64("per_page")
+	if page <= 0 {
+		page = 1
+	}
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	type Ret struct {
+		List      []*order_model.Order `json:"list"`
+		ListCount int64                `json:"list_count"`
+	}
+	list := order_model.GetOrdersByOTypeAndWxUId(id, order_model.ORDER_TYPE_SELF, wxUser.Id, page, perPage)
+	listCount := order_model.GetOrdersCountByOTypeAndWxUId(id, order_model.ORDER_TYPE_SELF, wxUser.Id)
+	self.Data["json"] = &Ret{List: list, ListCount: listCount}
+	self.ServeJSON()
+}
+
+//获取代销订单
+func (self *GranaryController) GetSaleOrder() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	cache, _ := self.GetBool("cache", false)
+	granary := granary_model.GetGranaryById(id, cache)
+	user := self.GetCurrentUser(true)
+	// user = user_model.GetUserById(600105, true)
+	if granary == nil || user.Id != granary.UserId {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	p := product_model.GetProductById(granary.ProductId, true)
+	if p == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	saleOrder := granary_model.GetSaleOrderByGId(id, false)
+	type Ret struct {
+		SaleOrder *granary_model.SaleOrder           `json:"sale_order"`
+		Orders    []*granary_model.RelateOrderDetail `json:"orders"`
+	}
+	ret := new(Ret)
+	if saleOrder != nil {
+		saleOrder.Product = p
+		orders := granary_model.GetSaleRelateOrdersBySOId(saleOrder.OrderId)
+		for _, order := range orders {
+			wxUser := user_model.GetWxUserById(order.WxUid, true)
+			if wxUser != nil {
+				order.WxUserHead = self.GetFullImgUrl(wxUser.Head)
+				if !order.ReceiveTime.IsZero() {
+					order.ReceiveAt = order.ReceiveTime.Unix()
+				}
+				order.StatusCn = order_model.STATUS_CN_TEXT[order.Status]
+			}
+		}
+		ret.Orders = orders
+		ret.SaleOrder = saleOrder
+	}
+	self.Data["json"] = ret
+	self.ServeJSON()
+}
+
+//代销订单操作,上架、下架
+func (self *GranaryController) OperateSaleOrder() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	// cache, _ := self.GetBool("cache", false)
+	user := self.GetCurrentUser(true)
+	// user = user_model.GetUserById(600105, true)
+	granary := granary_model.GetGranaryById(id, false)
+	if granary == nil || user.Id != granary.UserId {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	p := product_model.GetProductById(granary.ProductId, true)
+	if p == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	saleOrder := granary_model.GetSaleOrderByGId(id, false)
+	operate := self.GetString("opt")
+	count, _ := self.GetInt64("count", 0) //可以销售的份数
+
+	leftCount := granary.TotalCount - granary.SoldCount - granary.BuybackCount
+	//如果自用份数没达到最小自用份数
+	if granary.SelfUseCount < granary.SelfUseMin && count > leftCount-granary.SelfUseMin {
+		msg := fmt.Sprintf("最少自用%d份", granary.SelfUseMin)
+		self.ReturnError(403, []string{"countError", msg}, "", nil)
+	}
+
+	granary.LeftCount = granary.TotalCount - granary.SoldCount - granary.SelfUseCount - granary.BuybackCount
+	if count <= 0 || count > granary.LeftCount {
+		self.ReturnError(403, apps.CountError, "", nil)
+	}
+
+	if operate == "on" {
+		if saleOrder == nil {
+			firstInvestTime := project_model.GetFirstInvestTimeByUIdAndPId(user.Id, p.Id)
+			if firstInvestTime == 0 {
+				self.ReturnError(403, apps.NoExist, "", nil)
+			}
+			saleOrder = new(granary_model.SaleOrder).Create(user.Id, granary.Id, p.Id, count, firstInvestTime)
+		} else {
+			saleOrder.TotalCount = count + saleOrder.SoldCount
+			saleOrder.State = granary_model.ORDER_STATE_ONLINE
+			saleOrder.OperateState = granary_model.OPERATE_STATE_NOTHING
+			saleOrder.Save()
+		}
+	} else if operate == "off" {
+		if saleOrder == nil {
+			self.ReturnError(403, apps.OrderNotExist, "", nil)
+		}
+		saleOrder.OperateState = granary_model.OPERATE_STATE_APPLY_OFF
+		saleOrder.Save()
+	} else {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	self.Data["json"] = saleOrder
+	self.ServeJSON()
+}
+
+//卖家换一换
+func (self *GranaryController) RefreshSaleOrder() {
+	oId := self.GetString("oid")
+	_pId := self.Ctx.Input.Param(":product_id")
+	pId, _ := strconv.ParseInt(_pId, 10, 64)
+	product := product_model.GetProductById(pId, true)
+	if product == nil || product.Ptype != product_model.TYPE_USER_SALE {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	order := granary_model.GetSaleOrderByOId(oId)
+	if order == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	saleOrder := granary_model.GetNextSaleOrderByPId(pId, oId)
+	if saleOrder == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	saler := user_model.GetUserById(saleOrder.SaleUserId, true)
+	if saler != nil {
+		saleOrder.SaleUserHead = self.GetFullImgUrl(saler.Head)
+		saleOrder.SaleUserName = saler.Nickname
+	}
+	self.Data["json"] = saleOrder
+	self.ServeJSON()
+}
+
+//回购
+func (self *GranaryController) BuyBack() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	count, _ := self.GetInt64("count")
+	beego.BeeLogger.Warn("count %d", count)
+	if count < 0 {
+		self.ReturnError(403, apps.FillCorrectBuybackCount, "", nil)
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	//粮仓
+	granary := granary_model.GetGranaryById(id, false)
+	if granary == nil || granary.UserId != wxUser.UserId {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	if granary.BuybackMax <= 0 {
+		self.ReturnError(403, apps.NotSupportBuyback, "", nil)
+	}
+
+	//回购数量是否有限制
+	beego.BeeLogger.Warn("count: %s", count)
+	beego.BeeLogger.Warn("granary.BuybackMax: %s", granary.BuybackMax)
+	if count > granary.BuybackMax {
+		self.ReturnError(403, apps.BuybackCountLimit, "", nil)
+	}
+
+	//收成
+	bonus := project_model.GetProjectBonusById(granary.ProjectBonusId, true)
+	if bonus == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	//是否在回购期内
+	now := time.Now()
+	if now.Unix() < bonus.BuyBackStartAt.Unix() || now.Unix() > bonus.BuyBackStopAt.Unix() {
+		self.ReturnError(403, apps.NoUnderBuybackPeriod, "", nil)
+	}
+
+	//剩余份数是否足够
+	validCount := granary.TotalCount - granary.SelfUseCount
+
+	saleOrder := granary_model.GetSaleOrderByPIdAndUIdAndGId(granary.ProductId, wxUser.UserId, granary.Id)
+
+	if saleOrder != nil && (saleOrder.State == granary_model.ORDER_STATE_ONLINE ||
+		saleOrder.State == granary_model.ORDER_STATE_SELL_UP) {
+		validCount = validCount - saleOrder.TotalCount
+	} else {
+		validCount = validCount - granary.SoldCount
+	}
+
+	if count > validCount {
+		self.ReturnError(403, []string{"overValidCountLimit", fmt.Sprintf("最多可设置%d份", validCount)}, "", nil)
+	}
+
+	granary.BuybackCount = count
+	granary.Save()
+
+	type Ret struct {
+		BuybackStartAt int64 `json:"buyback_start_at"`
+		BuybackEndAt   int64 `json:"buyback_end_at"`
+		ReceiveAmount  int64 `json:"receive_amount"`
+	}
+
+	startAt := bonus.BuyBackStartAt.Unix()
+	endAt := bonus.BuyBackStopAt.Unix()
+	receiveAmount := granary.ReBuybackAmount
+
+	self.Data["json"] = &Ret{BuybackStartAt: startAt, BuybackEndAt: endAt, ReceiveAmount: receiveAmount}
+	self.ServeJSON()
+}
+
+//根据回报方式返回粮仓
+func (self *GranaryController) GetGranaryRepo() {
+
+	type Ret struct {
+		IsHarvest bool `json:"is_harvest"`
+	}
+
+	isHarvest := false
+	wId, _ := self.GetInt64("invest_way_id")
+	uId := self.GetCurrentUserId()
+	userGranary := granary_model.GetProjectGranaryByWayIdAndUId(wId, uId, false)
+	if userGranary != nil {
+		isHarvest = true
+	}
+
+	self.Data["json"] = &Ret{IsHarvest: isHarvest}
+	self.ServeJSON()
+}

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

@@ -0,0 +1,31 @@
+package granary_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{"DistributeBonus", "SendBuybackAmount", "GetGranaryRepo"}
+	exceptCheckWxUserLoginAction = []string{"DistributeBonus", "SendBuybackAmount"}
+)
+
+type GranaryController struct {
+	apps.BaseController
+}
+
+func (self *GranaryController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 22 - 0
go/gopath/src/fohow.com/apps/controllers/invite_sale_controller/init.go

@@ -0,0 +1,22 @@
+package invite_sale_controller
+
+import (
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+)
+
+var (
+	//需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"Get", "GetCurConfig"}
+	exceptCheckWxUserLoginAction = []string{"Get", "GetCurConfig"}
+)
+
+type InviteSaleController struct {
+	apps.BaseController
+}
+
+func (self *InviteSaleController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 166 - 0
go/gopath/src/fohow.com/apps/controllers/invite_sale_controller/invite_sale_controller.go

@@ -0,0 +1,166 @@
+package invite_sale_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"fohow.com/apps"
+	"fohow.com/apps/models/invite_sale_model"
+	"fohow.com/apps/models/use_limit"
+	"fohow.com/apps/models/user_model"
+	"strings"
+	"time"
+)
+
+func (self *InviteSaleController) Get() {
+
+	timeStr := self.GetString("choose_time")
+	if strings.TrimSpace(timeStr) == "" {
+		timeStr = time.Now().Format("2006-01-02")
+	}
+
+	page, _ := self.GetInt64("page", 1)
+
+	perPage, _ := self.GetInt64("per_page", 10)
+
+	useCache, _ := self.GetBool("cache", true)
+
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+
+	timeArray := strings.Split(timeStr, "-")
+	if len(timeArray) != 3 {
+		timeStr = time.Now().Format("2006-01-02")
+	}
+
+	timeParse, _ := time.ParseInLocation("2006-01-02", timeStr, time.Local)
+
+	type Ret struct {
+		RewardConfigList []*invite_sale_model.InviteSaleRewardConfig `json:"reward_config_list"`
+
+		List []*invite_sale_model.InviterSale `json:"list"`
+
+		ListCount int64 `json:"list_count"`
+
+		SelfSale int64 `json:"self_sale"`
+
+		RewardWxUser *invite_sale_model.InviteSaleRewardRecord `json:"reward_wx_user"`
+	}
+
+	beego.BeeLogger.Warn("timeStr : %s,  timeParseGet : %v", timeStr, timeParse)
+	configList := invite_sale_model.GetRewardConfigsByTime(timeParse, useCache)
+
+	if configList == nil {
+		configList = make([]*invite_sale_model.InviteSaleRewardConfig, 0, 0)
+	}
+	for _, item := range configList {
+		if item == nil {
+			continue
+		}
+		item.ConfigTimeSt = item.ConfigTime.Unix()
+		if item.ConfigTimeSt < 0 {
+			item.ConfigTimeSt = 0
+		}
+		item.SImg = self.GetFullImgUrl(item.SImg)
+	}
+
+	useLimits := use_limit.GetEffectAndLimitUpSaleList()
+	var limitWxUids []string
+	for _, useLimit := range useLimits {
+		wxUid := fmt.Sprintf("%d", useLimit.WxUid)
+		limitWxUids = append(limitWxUids, wxUid)
+	}
+
+	list := invite_sale_model.GetInviterSaleListByLimitWxUidsAndTime(page, perPage, strings.Join(limitWxUids, ","), timeParse, useCache)
+	if list == nil {
+		list = make([]*invite_sale_model.InviterSale, 0, 0)
+	}
+	for _, item := range list {
+		if item == nil {
+			continue
+		}
+		item.WxHead = self.GetFullImgUrl(item.WxHead)
+	}
+
+	for i := int64(0); i < int64(len(configList)); i++ {
+
+		if configList[i] == nil {
+			continue
+		}
+		if configList[i].RewardType == invite_sale_model.REWARD_TYPE_RATIO_ENUM {
+
+			count := int64(0)
+			if list != nil && len(list) > 0 && list[i] != nil {
+				count = list[i].Count
+			}
+			if count > 0 {
+				configList[i].RewardAmount = count * configList[i].RewardAmount / 100 // 69000分 * 1%  == 690
+			} else {
+				configList[i].RewardAmount = 0
+			}
+		}
+
+	}
+
+	listCount := invite_sale_model.GetInviterSaleListCountByLimitWxUidsAndTime(strings.Join(limitWxUids, ","), timeParse, useCache)
+
+	wxUId := self.GetCurrentWxUserId()
+	selfSaleAmount := int64(0)
+	selfSale := invite_sale_model.GetInviterSaleByInviteIdAndTime(wxUId, timeParse)
+	if selfSale != nil {
+		selfSaleAmount = selfSale.Count
+	}
+
+	rewardWxUser := invite_sale_model.GetInviteSaleRewardRecordByTime(timeParse)
+	if rewardWxUser != nil {
+		wxUser := user_model.GetWxUserById(rewardWxUser.WxUid, true)
+		if wxUser != nil {
+			rewardWxUser.WxNickname = wxUser.Nickname
+			rewardWxUser.WxHead = self.GetFullImgUrl(wxUser.Head)
+		}
+	}
+
+	self.Data["json"] = &Ret{RewardConfigList: configList, List: list, ListCount: listCount, SelfSale: selfSaleAmount, RewardWxUser: rewardWxUser}
+	self.ServeJSON()
+
+}
+
+func (self *InviteSaleController) GetCurConfig() {
+	timeStr := self.GetString("choose_time")
+	if strings.TrimSpace(timeStr) == "" {
+		timeStr = time.Now().Format("2006-01-02")
+	}
+
+	useCache, _ := self.GetBool("cache", true)
+
+	timeArray := strings.Split(timeStr, "-")
+	if len(timeArray) != 3 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	timeParse, _ := time.ParseInLocation("2006-01-02", timeStr, time.Local)
+
+	type Ret struct {
+		RewardConfigList []*invite_sale_model.InviteSaleRewardConfig `json:"reward_config_list"`
+	}
+
+	beego.BeeLogger.Warn("timeStr : %s,  timeParseGet : %v", timeStr, timeParse)
+	configList := invite_sale_model.GetRewardConfigsByTime(timeParse, useCache)
+
+	if configList == nil {
+		configList = make([]*invite_sale_model.InviteSaleRewardConfig, 0, 0)
+	}
+	for _, item := range configList {
+		if item == nil {
+			continue
+		}
+		item.ConfigTimeSt = item.ConfigTime.Unix()
+		if item.ConfigTimeSt < 0 {
+			item.ConfigTimeSt = 0
+		}
+	}
+
+	self.Data["json"] = &Ret{RewardConfigList: configList}
+	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()
+}

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

@@ -0,0 +1,316 @@
+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
+}

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

@@ -0,0 +1,875 @@
+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/order_model"
+	"fohow.com/apps/models/product_model"
+	// "fohow.com/apps/models/user_model"
+	// "fohow.com/apps/models/vas_model"
+	// "fohow.com/libs/wx_mp"
+	"encoding/csv"
+	"fmt"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/cow_order_model"
+	"fohow.com/apps/models/merchant_model"
+	"fohow.com/apps/models/subject_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/tool"
+	"strings"
+	"sync"
+	"time"
+)
+
+var updateExpressLock sync.Mutex
+
+//下单
+func (self *OrderController) Create() {
+	_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.PurchaseLimitCount > 0 {
+		if product.PurchaseLimitCount < count {
+			self.ReturnError(403, []string{apps.OverLimitCount[0], fmt.Sprintf("该商品限购%d件", product.PurchaseLimitCount)}, "", nil)
+		} else {
+			purchaseTotalCount := order_model.GetOrderCountByPIdAndWxUId(pId, wxUId)
+			//历史已经买够到限购数量了
+			if product.PurchaseLimitCount <= purchaseTotalCount {
+				self.ReturnError(403, []string{apps.PurchasedReachLimit[0], fmt.Sprintf("该商品限购%d件", product.PurchaseLimitCount)}, "", nil)
+			}
+			//历史没买够数量,但是历史总数+想购买的数量 超过限购数量
+			if product.PurchaseLimitCount < (purchaseTotalCount + count) {
+				canBuyCount := product.PurchaseLimitCount - purchaseTotalCount
+				self.ReturnError(403, []string{apps.PurchasedReachLimit[0], fmt.Sprintf("您还可以购买%d件", canBuyCount)}, "", nil)
+			}
+		}
+	}
+	//判断剩余数量
+	leftCount := int64(0)
+	product.SoldCount = order_model.GetSoldCountByPId(product.Id, false)
+	leftCount = product.Count - product.SoldCount
+	if count > leftCount {
+		self.ReturnError(403, apps.ProductStockNotEnough, "", nil)
+	}
+	//商品销售专题---start
+	isJoinSubjectPrize := false      //商品是否参与专题的抽奖, 考虑开奖时间
+	productSaleSubjectId := int64(0) //专题的id
+	sId, _ := self.GetInt64("sid", 0)
+	if sId > 0 {
+		subject := subject_model.GetProductSaleSubjectById(sId, false)
+		if subject != nil {
+			productIds := subject.ProductIds
+			productIdArray := strings.Split(productIds, ",")
+			for _, productIdItem := range productIdArray {
+				productId, err := strconv.ParseInt(productIdItem, 10, 64)
+				if err == nil && productId == pId {
+					productSaleSubjectId = subject.Id
+					if subject.IsPrizeAct && subject.OpenPrizeTime.Unix() > time.Now().Unix() {
+						isJoinSubjectPrize = true
+					}
+				}
+			}
+		}
+	}
+	subjects := subject_model.GetPrizeActProductSaleSubjectsByProductId(pId, false)
+	if len(subjects) > 0 {
+		for _, item := range subjects {
+			productIds := item.ProductIds
+			productIdArray := strings.Split(productIds, ",")
+			for _, productIdItem := range productIdArray {
+				productId, err := strconv.ParseInt(productIdItem, 10, 64)
+				if err == nil && productId == pId {
+					productSaleSubjectId = item.Id
+					if item.IsPrizeAct && item.OpenPrizeTime.Unix() > time.Now().Unix() {
+						isJoinSubjectPrize = true
+					}
+				}
+			}
+		}
+	}
+	//商品销售专题---end
+
+	//秒杀逻辑: 判断是否处于秒杀时间段内
+	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)
+		}
+	}
+
+	//限新逻辑: 微信支付完成购买过商品的用户
+	if product.IsOnlyNew {
+		paiedOrder := order_model.GetPaiedOrderByWxUIdAndPayWayLimitOne(wxUId, order_model.PAY_WAY_WEIXIN, false)
+		if paiedOrder != nil {
+			self.ReturnError(403, apps.OnlyNew, "", nil)
+		}
+	}
+
+	totalPrice := product.RoboBalancePrice * count
+	//小兔微信,测试微信支付
+	if wxUId == 42452 {
+		totalPrice = 1
+	}
+	freight := order_model.FREIGHT
+	if totalPrice >= order_model.FREIGHT_LIMIT {
+		freight = int64(0)
+	}
+
+	order := new(order_model.Order).CreateNew(wxUId, uId,
+		totalPrice, freight, order_model.SOURCE_XCX)
+	if order != nil {
+		if productSaleSubjectId > 0 {
+			order.IsJoinSubjectPrize = isJoinSubjectPrize
+			order.ProductSaleSubjectId = productSaleSubjectId
+		}
+		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() {
+	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)
+	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)
+		}
+		product := product_model.GetProductById(cartItem.ProductId, false)
+		if product == nil {
+			self.ReturnError(403, apps.NoExist, "", nil)
+		}
+		//加入限购逻辑
+		if product.PurchaseLimitCount > 0 {
+			if product.PurchaseLimitCount < cNums {
+				self.ReturnError(403, []string{apps.OverLimitCount[0], fmt.Sprintf("%s商品限购%d件", product.Name, product.PurchaseLimitCount)}, "", nil)
+			} else {
+				purchaseTotalCount := order_model.GetOrderCountByPIdAndWxUId(cartItem.ProductId, wxUId)
+				//历史已经买够到限购数量了
+				if product.PurchaseLimitCount <= purchaseTotalCount {
+					self.ReturnError(403, []string{apps.PurchasedReachLimit[0], fmt.Sprintf("%s商品限购%d件", product.Name, product.PurchaseLimitCount)}, "", nil)
+				}
+				//历史没买够数量,但是历史总数+想购买的数量 超过限购数量
+				if product.PurchaseLimitCount < (purchaseTotalCount + cNums) {
+					canBuyCount := product.PurchaseLimitCount - purchaseTotalCount
+					self.ReturnError(403, []string{apps.PurchasedReachLimit[0], fmt.Sprintf("%s您还可以购买%d件", product.Name, canBuyCount)}, "", nil)
+				}
+			}
+		}
+		//普通下单--判断剩余数量
+		product.SoldCount = order_model.GetSoldCountByPId(product.Id, false)
+		leftCount := product.Count - product.SoldCount
+		if cNums > leftCount {
+			self.ReturnError(403, []string{apps.ProductStockNotEnough[0], fmt.Sprintf("%s商品库存不足", product.Name)}, "", nil)
+		}
+		//限新逻辑: 微信支付完成购买过商品的用户
+		if product.IsOnlyNew {
+			paiedOrder := order_model.GetPaiedOrderByWxUIdAndPayWayLimitOne(wxUId, order_model.PAY_WAY_WEIXIN, false)
+			if paiedOrder != nil {
+				self.ReturnError(403, []string{apps.OnlyNew[0], fmt.Sprintf("%s仅限新人购买", product.Name)}, "", nil)
+			}
+		}
+		totalPrice += product.Price * cNums
+		//小兔微信,测试微信支付
+		if wxUId == 12 {
+			//totalPrice = 1
+		}
+	}
+	freight := order_model.FREIGHT
+	if totalPrice >= order_model.FREIGHT_LIMIT {
+		freight = int64(0)
+	}
+	order := new(order_model.Order).CreateNew(wxUId, uId,
+		totalPrice, freight, order_model.SOURCE_XCX)
+	if order != nil {
+		order.Save()
+		//创建订单明细
+		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)
+			}
+			new(order_model.OrderDetail).Create(order.OrderId, order.Id, cartItem.ProductId, product.Price, product.RoboBalancePrice, product.Name,
+				cNums)
+		}
+	}
+	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)
+			item.Product = product
+			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 afterTakeDelivery(order *order_model.Order) {
+
+// }
+
+// 获取商家的订单列表
+func (self *OrderController) MerchantList() {
+
+	merchantId, _ := self.GetInt64("merchant_id", 0)
+	_queryPaiedStart := self.GetString("query_paied_start")
+	_queryPaiedEnd := self.GetString("query_paied_end")
+	_queryReceiveStart := self.GetString("query_receive_start")
+	_queryReceiveEnd := self.GetString("query_receive_end")
+	status := self.GetString("status")
+	page, _ := self.GetInt("page")
+	perPage, _ := self.GetInt("per_page")
+	contact := self.GetString("contact")
+	tel := self.GetString("tel")
+	orderType := self.GetString("order_type")
+	orderId := self.GetString("order_id")
+	province := self.GetString("province")
+	city := self.GetString("city")
+	district := self.GetString("district")
+
+	queryPaiedStart, queryPaiedEnd := int64(0), int64(0)
+	queryReceiveStart, queryReceiveEnd := int64(0), int64(0)
+	if strings.TrimSpace(_queryPaiedStart) != "" {
+		paiedStart, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 00:00:00", _queryPaiedStart), time.Local)
+		if err != nil {
+			self.ReturnError(403, []string{"paramsError", "查询日期错误"}, "", nil)
+		}
+		queryPaiedStart = paiedStart.Unix()
+	}
+	if strings.TrimSpace(_queryPaiedEnd) != "" {
+		paiedEnd, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 23:59:59", _queryPaiedEnd), time.Local)
+		if err != nil {
+			self.ReturnError(403, []string{"paramsError", "查询日期错误"}, "", nil)
+		}
+		queryPaiedEnd = paiedEnd.Unix()
+	}
+
+	if strings.TrimSpace(_queryReceiveStart) != "" {
+		receiveStart, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 00:00:00", _queryReceiveStart), time.Local)
+		if err != nil {
+			self.ReturnError(403, []string{"paramsError", "查询日期错误"}, "", nil)
+		}
+		queryReceiveStart = receiveStart.Unix()
+	}
+	if strings.TrimSpace(_queryReceiveEnd) != "" {
+		receiveEnd, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 23:59:59", _queryReceiveEnd), time.Local)
+		if err != nil {
+			self.ReturnError(403, []string{"paramsError", "查询日期错误"}, "", nil)
+		}
+		queryReceiveEnd = receiveEnd.Unix()
+	}
+
+	if status != "" && order_model.STATUS_CN_TEXT[status] == "" {
+		self.ReturnError(403, []string{"paramsError", "状态错误"}, "", nil)
+	}
+	if orderType != "" {
+		_orderType, err := strconv.ParseInt(orderType, 10, 64)
+		if order_model.ORDER_TYPE_CN_TEXT[_orderType] == "" || err != nil {
+			self.ReturnError(403, []string{"paramsError", "类型出错"}, "", nil)
+		}
+	}
+
+	if page <= 0 {
+		page = 1
+	}
+	if perPage <= 0 || perPage > 100 {
+		perPage = 10
+	}
+	cache, _ := self.GetBool("cache", false)
+	uId := self.GetCurrentUserId()
+
+	var orders []*order_model.MerchantOrder
+	count := int64(0)
+	merchantUser := merchant_model.GetMerchantUserRelationByUserId(uId, cache)
+	if merchantUser != nil {
+		if merchantUser.MerchantId != merchantId {
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+	} else {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	orders = order_model.GetMerchantOrders(merchantId, queryPaiedStart, queryPaiedEnd, queryReceiveStart, queryReceiveEnd, status,
+		contact, strings.TrimSpace(merchantUser.ManageProductIds),
+		tel, orderType, orderId, province, city, district, page, perPage)
+	count = order_model.GetMerchantOrdersCount(merchantId, queryPaiedStart, queryPaiedEnd, queryReceiveStart, queryReceiveEnd, status,
+		contact, strings.TrimSpace(merchantUser.ManageProductIds),
+		tel, orderType, orderId, province, city, district)
+
+	for _, item := range orders {
+		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.Product = product
+		item.WxUser = wxUser
+		item.OrderTypeCn = order_model.ORDER_TYPE_CN_TEXT[item.OrderType]
+		item.CTime = item.CreatedAt.Unix()
+		item.DTime = item.DispatchTime.Unix()
+		item.RTime = item.ReceiveTime.Unix()
+	}
+	type Ret struct {
+		List      []*order_model.MerchantOrder `json:"list"`
+		ListCount int64                        `json:"list_count"`
+	}
+
+	if orders == nil {
+		orders = make([]*order_model.MerchantOrder, 0, 0)
+	}
+	self.Data["json"] = &Ret{ListCount: count, List: orders}
+	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()
+}
+
+// 商家更新物流信息
+// status只接收""或者dispatch两个值
+// 只能处理待发货、待收货的订单物流信息
+// 当status是""、订单状态是已发货:表示更改物流,当status是dispatch、订单状态是处理中表示添加物流信息
+// 不允许status为dispatch、同时订单状态是已发货。避免多个管理员重复发货。
+func (self *OrderController) UpdateExpress() {
+	oId := self.Ctx.Input.Param(":order_id")
+	expressCompany := self.GetString("express_company")
+	expressOrderNo := self.GetString("express_order_no")
+	status := self.GetString("status", "")
+
+	defer updateExpressLock.Unlock()
+	updateExpressLock.Lock()
+
+	o := order_model.GetOrderById(oId)
+	if o == nil {
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	}
+
+	uId := self.GetCurrentUserId()
+	merchantUser := merchant_model.GetMerchantUserRelationByUserId(uId, true)
+	if merchantUser == nil {
+		// 不是商户
+		self.ReturnError(403, apps.ParamsError, "", 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
+	}
+
+	//校验订单所属商户
+	product := product_model.GetProductById(o.ProductId, true)
+	if product == nil {
+		self.ReturnError(403, apps.ProductNotExist, "", nil)
+	}
+	if product.MerchantId != merchantUser.MerchantId {
+		self.ReturnError(403, apps.OrderNotBelongToCurrentUser, "", nil)
+	}
+
+	//该商家管理员是否负责管理该商品?
+	isManageTheProduct, _ := tool.Contain(fmt.Sprintf("%d", product.Id), strings.Split(merchantUser.ManageProductIds, ","))
+	if merchantUser.ManageProductIds != "0" && !isManageTheProduct {
+		self.ReturnError(403, apps.CurrentMerIsNotManageTheProduct, "", nil)
+	}
+
+	o.ExpressCompany = expressCompany
+	o.ExpressOrderNo = expressOrderNo
+
+	if !o.Save() {
+		beego.BeeLogger.Error("user[%d] complete order[%s] fail", uId, oId)
+	}
+
+	//发送物流信息
+	if status == order_model.STATUS_DISPATCH { //发送发货通知
+
+		buyerWxUser := user_model.GetWxUserById(o.WxUserId, true)
+		if buyerWxUser != nil {
+			helpers.ProductLogisticsChangedNotify(*buyerWxUser, o.DispatchTime, product.Name, o.OrderId, o.ExpressCompany, o.ExpressOrderNo,
+				"您兑换的商品已经发货,请注意查收。", "pages/start/start?url=packageUser/pages/user/order/order&id="+o.OrderId)
+		}
+
+	} else { //发送物流修改信息
+
+		buyerWxUser := user_model.GetWxUserById(o.WxUserId, true)
+		if buyerWxUser != nil {
+			helpers.ProductLogisticsChangedNotify(*buyerWxUser, o.DispatchTime, product.Name, o.OrderId, o.ExpressCompany, o.ExpressOrderNo,
+				"您兑换的商品物流信息有改动,请注意查收。", "pages/start/start?url=packageUser/pages/user/order/order&id="+o.OrderId)
+		}
+
+	}
+	//同步牛就对了订单发货信息
+	if o.OrderType == order_model.ORDER_COW_BUY || o.OrderType == order_model.ORDER_COW_CHANGE {
+		cowOrder := cow_order_model.GetCowOrderByCowOrderId(o.OrderId)
+		if cowOrder != nil {
+			cowOrder.LogisNo = o.ExpressOrderNo
+			cowOrder.LogisComp = o.ExpressCompany
+			cowOrder.ConFlag = int64(1)
+			cowOrder.ConDate = time.Now().Unix()
+			cowOrder.Save()
+		}
+	}
+	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) MerchantOrderDetail() {
+
+	oId := self.Ctx.Input.Param(":order_id")
+	o := order_model.GetMerchantOrderById(oId)
+	cache, _ := self.GetBool("cache", false)
+	if o == nil {
+		beego.BeeLogger.Error("order not exist id=[%s]", oId)
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	}
+	user := self.GetCurrentUser(cache)
+
+	product := product_model.GetProductById(o.ProductId, cache)
+	if product == nil {
+		self.ReturnError(403, apps.ProductNotExist, "", nil)
+	}
+
+	merchantUser := merchant_model.GetMerchantUserRelationByUserId(user.Id, cache)
+	if merchantUser == nil {
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	} else {
+		if product.MerchantId != merchantUser.MerchantId {
+			beego.BeeLogger.Error("order not merchant_owner id=[%s]", oId)
+			self.ReturnError(404, apps.OrderNotExist, "", nil)
+		}
+	}
+
+	wxUser := user_model.GetWxUserByUserId(o.UserId, cache)
+	if wxUser == nil {
+		self.ReturnError(403, apps.UserNotExist, "", nil)
+	} else {
+		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.Product = product
+	o.WxUser = wxUser
+	o.ExCompanyArray = strings.Split(o.ExpressCompany, "/")
+	o.ExOrderNoArray = strings.Split(o.ExpressOrderNo, "/")
+	self.Data["json"] = o
+	self.ServeJSON()
+}
+
+func (self *OrderController) ExportMerchantOrders() {
+	merchantId, _ := self.GetInt64("merchant_id", 0)
+	_queryPaiedStart := self.GetString("query_paied_start")
+	_queryPaiedEnd := self.GetString("query_paied_end")
+	_queryReceiveStart := self.GetString("query_receive_start")
+	_queryReceiveEnd := self.GetString("query_receive_end")
+	status := self.GetString("status")
+	contact := self.GetString("contact")
+	tel := self.GetString("tel")
+	orderType := self.GetString("order_type")
+	orderId := self.GetString("order_id")
+	province := self.GetString("province")
+	city := self.GetString("city")
+	district := self.GetString("district")
+	cache, _ := self.GetBool("cache", false)
+
+	queryPaiedStart, queryPaiedEnd := int64(0), int64(0)
+	queryReceiveStart, queryReceiveEnd := int64(0), int64(0)
+	if strings.TrimSpace(_queryPaiedStart) != "" {
+		paiedStart, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 00:00:00", _queryPaiedStart), time.Local)
+		if err != nil {
+			self.ReturnError(403, []string{"paramsError", "查询日期错误"}, "", nil)
+		}
+		queryPaiedStart = paiedStart.Unix()
+	}
+	if strings.TrimSpace(_queryPaiedEnd) != "" {
+		paiedEnd, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 23:59:59", _queryPaiedEnd), time.Local)
+		if err != nil {
+			self.ReturnError(403, []string{"paramsError", "查询日期错误"}, "", nil)
+		}
+		queryPaiedEnd = paiedEnd.Unix()
+	}
+	if strings.TrimSpace(_queryReceiveStart) != "" {
+		receiveStart, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 00:00:00", _queryReceiveStart), time.Local)
+		if err != nil {
+			self.ReturnError(403, []string{"paramsError", "查询日期错误"}, "", nil)
+		}
+		queryReceiveStart = receiveStart.Unix()
+	}
+	if strings.TrimSpace(_queryReceiveEnd) != "" {
+		receiveEnd, err := time.ParseInLocation("2006-01-02 15:04:05", fmt.Sprintf("%s 23:59:59", _queryReceiveEnd), time.Local)
+		if err != nil {
+			self.ReturnError(403, []string{"paramsError", "查询日期错误"}, "", nil)
+		}
+		queryReceiveEnd = receiveEnd.Unix()
+	}
+
+	if status != "" && order_model.STATUS_CN_TEXT[status] == "" {
+		self.ReturnError(403, []string{"paramsError", "状态错误"}, "", nil)
+	}
+	if orderType != "" {
+		_orderType, err := strconv.ParseInt(orderType, 10, 64)
+		if order_model.ORDER_TYPE_CN_TEXT[_orderType] == "" || err != nil {
+			self.ReturnError(403, []string{"paramsError", "类型出错"}, "", nil)
+		}
+	}
+
+	uId := self.GetCurrentUserId()
+
+	var orders []*order_model.MerchantOrder
+	merchantUser := merchant_model.GetMerchantUserRelationByUserId(uId, cache)
+	if merchantUser != nil {
+		if merchantUser.MerchantId != merchantId {
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+	} else {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	orders = order_model.GetMerchantOrders(merchantId, queryPaiedStart, queryPaiedEnd, queryReceiveStart, queryReceiveEnd, status,
+		contact, strings.TrimSpace(merchantUser.ManageProductIds),
+		tel, orderType, orderId, province, city, district, 0, 0)
+
+	fileName := fmt.Sprintf("[%d-%s].csv", merchantUser.UserId, time.Now().Format("2006年01月02日15时04分"))
+	self.Ctx.Output.Header("Content-Type", "text/csv")
+	self.Ctx.Output.Header("Content-Disposition", "attachment; filename="+fileName)
+	writer := csv.NewWriter(self.Controller.Ctx.ResponseWriter)
+	writer.Write([]string{"订单号", "微信昵称", "交易时间", "发货时间", "收货时间",
+		"商品", "类型", "单价/商家获得(元)", "总额/商家获得(元)",
+		"收件号码", "收件人", "收件地址", "物流信息"})
+	for _, order := range orders {
+		orderId := order.OrderId
+		var wxNickname, productName string
+		wxUser := user_model.GetWxUserById(order.WxUserId, true)
+		if wxUser != nil {
+			wxNickname = wxUser.Nickname
+		}
+
+		var paiedAtFormat, dispatchTimeFormat, receiveTimeFormat string
+		if order.PaiedAt > 0 {
+			paiedAtFormat = time.Unix(order.PaiedAt, 0).Format("2006-01-02 15:04")
+		}
+		if order.DispatchTime.Unix() > 0 {
+			dispatchTimeFormat = order.DispatchTime.Format("2006-01-02 15:04")
+		}
+		if order.ReceiveTime.Unix() > 0 {
+			receiveTimeFormat = order.ReceiveTime.Format("2006-01-02 15:04")
+		}
+
+		product := product_model.GetProductById(order.ProductId, true)
+		if product != nil {
+			productName = product.Name
+		}
+		orderTypeCn := order_model.ORDER_TYPE_CN_TEXT[order.OrderType]
+		unitGet := fmt.Sprintf("%0.2f(%0.2f)", float64(order.UnitPrice)/100.0, float64(order.BuyPrice/order.Count)/100.0)
+		totalGet := fmt.Sprintf("%0.2f(%0.2f)", float64(order.UnitPrice*order.Count)/100.0, float64(order.BuyPrice)/100.0)
+		tel := order.Tel
+		contact := order.Contact
+		address := order.Address
+		logistics := fmt.Sprintf("物流公司:%s;物流号:%s", order.ExpressCompany, order.ExpressOrderNo)
+
+		writer.Write([]string{orderId, wxNickname, paiedAtFormat, dispatchTimeFormat, receiveTimeFormat,
+			productName, orderTypeCn, unitGet, totalGet, tel, contact, address, logistics})
+	}
+	writer.Flush()
+	return
+}

+ 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()
+}

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

@@ -0,0 +1,744 @@
+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/apps/models/project_model"
+	"fohow.com/libs/wx_mp"
+	"github.com/astaxie/beego"
+	"strconv"
+	"strings"
+	"time"
+	//"fohow.com/apps/models/product_model"
+	"fmt"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/copartner_model"
+	"fohow.com/apps/models/dollar_win_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/reserve_act_model"
+	"fohow.com/apps/models/subject_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/tool"
+)
+
+// // 支付同步回调
+// func (self *PayController) PaySync() {
+// 	target := self.Ctx.Input.Param(":target")
+
+// 	switch target {
+// 	// case "balance": // 余额充值
+// 	// 	self.payBalanceSync()
+// 	case "exchange": // 兑换
+// 		self.payExchangeSync()
+// 	default:
+// 		self.ReturnError(403, apps.ParamsError, "", nil)
+// 	}
+// }
+
+// 支付异步回调
+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 "invest": //投资记录支付
+
+		switch payWay { //支付方式
+
+		case project_model.PAY_WAY_TYPE_PROJECT_JOIN:
+			self.wxPayInvestAsync()
+		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 "reserve":
+
+		switch payWay {
+		case reserve_act_model.ORDER_PAY_WAY_WEIXINPAY:
+			self.wxPayReserveAsync()
+		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)
+		}
+	case COURSE_TARGET:
+		switch payWay {
+		case pay_model.PAYWAY_WEIXINPAY:
+			self.wxPayCourseAsync()
+		default:
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+	case DOLLAR_WIN_TARGET:
+		switch payWay {
+		case dollar_win_model.PAY_WAY_TYPE_DOLLAR_WIN_WX:
+			self.wxPayDollarWinAsync()
+		default:
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+	default:
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+}
+
+// // 回调后跳转页面
+// func GetUrlAfterPay(oId string) string {
+// 	var url string
+// 	s := strings.Split(oId, "")
+// 	oIdPrefix := fmt.Sprintf("%s%s", s[0], s[1])
+// 	switch oIdPrefix {
+// 	// case balance_model.ORDER_ID_PREFIX_CZ: // 余额充值
+// 	// 	url = fmt.Sprintf("%s/user/balance", beego.AppConfig.String("WWWHost"))
+// 	case order_model.ORDER_ID_PREFIX_EX: // 兑换
+// 		url = fmt.Sprintf("%s/user/exchanges", beego.AppConfig.String("WWWHost"))
+// 	default:
+// 		url = fmt.Sprintf("%s", beego.AppConfig.String("WWWHost"))
+// 	}
+// 	return url
+// }
+
+//微信购买代金券
+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.ProductBenefitRate = 15
+	wxUser.SecondProductBenefitRate = 8
+	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 (self *PayController) wxPayInvestAsync() {
+	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.wxPayInvestAsync(%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 := project_model.GetInvestByOrderId(orderId)
+	totalFee, _ := strconv.ParseInt(params.TotalFee, 10, 64)
+	settlementTotalFee, _ := strconv.ParseInt(params.SettlementTotalFee, 10, 64)
+	beego.BeeLogger.Warn("after_pay_controller.wxPayInvestAsync.settlementTotalFee(%d)", settlementTotalFee)
+	if order == nil || order.TotalPrice != totalFee {
+		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.State = 1
+	order.TradeNo = params.TransactionId
+	order.PayWay = project_model.PAY_WAY_TYPE_PROJECT_JOIN
+	order.PaiedAt = time.Now().UnixNano() //支付时间存储纳秒级别
+	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 notice()
+	go projectStateCheck(order)
+	go self.DistributeBonusAsync(order.ProjectId, order.ProjectInvestWayId)
+	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 projectStateCheck(order *project_model.ProjectJoin) {
+	project := project_model.GetProjectById(order.ProjectId, false)
+	if project != nil {
+		if project.State == project_model.STATE_UNDERWAY {
+			currentInvestment := project_model.GetValidTotalFundingByPId(project.Id, false)
+			beego.BeeLogger.Warn("project.state:%s, %d,%d, %d.", project.State, project.MinFunding, project.MaxFunding, currentInvestment)
+
+			//成功时间:某用户支付后,满最小融资额,订单的支付时间
+			if currentInvestment >= project.MinFunding && project.SuccessTime == 0 {
+				project.SuccessTime = int64(order.PaiedAt / 1e9)
+				project.Save()
+			}
+			if project.MinFunding == project.MaxFunding && currentInvestment >= project.MinFunding {
+				project.State = project_model.STATE_SUCCESS
+				project.Save()
+				if project.IsSendBenefit {
+					helpers.SendProjectBenefit(project)
+				}
+			}
+
+			if project.MinFunding < project.MaxFunding && currentInvestment >= project.MaxFunding {
+				project.State = project_model.STATE_SUCCESS
+				project.Save()
+				if project.IsSendBenefit {
+					helpers.SendProjectBenefit(project)
+				}
+			}
+		}
+	}
+}
+
+func sendInviterBenefit(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
+		}
+		SecondBenifitWxUser := FindInviter(benefitWxUser)
+		beego.BeeLogger.Warn("benefitWxUser: %s", benefitWxUser)
+		beego.BeeLogger.Warn("SecondBenifitWxUser: %s", SecondBenifitWxUser)
+		//beego.BeeLogger.Warn("sendInviterBenefit:%v,%v,%s", wxUser, benefitWxUser, orderId)
+		if source == user_model.SOURCE_PRODUCT_BENEFIT {
+			//群主购物,15%返给本人,8%返给上级群主,如果顾客购物,15%返给上级群主,8%返给再上级群主
+			productOrder := order_model.GetOrderById(orderId)
+			if productOrder == nil {
+				return
+			}
+
+			if benefitWxUser != nil {
+				//发放一级收益
+				count := int64(tool.RoundFloat64(float64(productOrder.PaiedPrice)*float64(benefitWxUser.ProductBenefitRate)/100, 0))
+				if count > 0 {
+					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
+					remark := fmt.Sprintf("%s%s", wxUser.Nickname, "购物")
+					b := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(benefitWxUser.Id, productOrder.OrderId, s)
+					if b == nil {
+						b = new(balance_model.CashBalance).Create(inviteOrder.BenefitWxUId, count, s, productOrder.OrderId, remark)
+						if b != nil {
+							//标志进账
+							inviteOrder.IsEnterBalance = true
+							inviteOrder.EnterTime = b.CreatedAt
+							inviteOrder.Save()
+						}
+					}
+				}
+			}
+
+			//发放二级收益
+			if SecondBenifitWxUser != nil && SecondBenifitWxUser.Id > int64(1) {
+				count := int64(tool.RoundFloat64(float64(productOrder.PaiedPrice)*float64(SecondBenifitWxUser.SecondProductBenefitRate)/100, 0))
+				if count > 0 {
+					inviteOrder := new(user_model.InviteOrder).Create(SecondBenifitWxUser.Id, wxUser.Id, wxUser.Id, count, productOrder.TotalPrice, user_model.FX_SOURCE_PRODUCT_BENEFIT, productOrder.OrderId)
+					//发放现金佣金
+					s := balance_model.FX_CASH_SOURCE_PRODUCT_BENEFIT
+					remark := fmt.Sprintf("%s%s", wxUser.Nickname, "购物")
+					b := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(SecondBenifitWxUser.Id, productOrder.OrderId, s)
+					if b == nil {
+						b = new(balance_model.CashBalance).Create(inviteOrder.BenefitWxUId, count, s, productOrder.OrderId, remark)
+						if b != nil {
+							//标志进账
+							inviteOrder.IsEnterBalance = true
+							inviteOrder.EnterTime = b.CreatedAt
+							inviteOrder.Save()
+						}
+					}
+				}
+			}
+		} else if source == user_model.BALANCE_BENEFIT {
+			balanceOrder := balance_model.GetBalanceOrderByOId(orderId, false)
+			if balanceOrder == nil {
+				return
+			}
+			//下线充值,上线获得8%返利,下线自动升级群主
+			if benefitWxUser != nil {
+				count := int64(0)
+				//发放一级收益, 升级群主返利540,其他情况返8%
+				if wxUser.ShowInviteMode != int64(1) && balanceOrder.PaiedPrice == balance_model.BALANCE_PAIED {
+					//count := int64(tool.RoundFloat64(float64(balanceOrder.PaiedPrice)*float64(balance_model.BALANCE_BL)/100, 0))
+					count = balance_model.AWARD_UPGRADE
+				} else {
+					count = int64(tool.RoundFloat64(float64(balanceOrder.TotalPrice)*float64(balance_model.BALANCE_BL)/100, 0))
+				}
+				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 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 DoRecover(benefitWxUser, wxUser *user_model.WxUser, inviteOrder *user_model.InviteOrder, count, totalPrice int64, source, orderId string) {
+
+	if benefitWxUser == nil || wxUser == nil || inviteOrder == nil {
+		return
+	}
+
+	//邀请人曾经是合伙人则恢复
+	if benefitWxUser.CopartnerState == user_model.COPARTNER_STATE_ONCE {
+		config := copartner_model.GetLastCopartnerRecruitConfig(false) //合伙人配置
+		if config != nil {
+			partnerCount := user_model.GetCountByWxUserCopartnerState(user_model.COPARTNER_STATE_YES, false) //合伙人人数
+			if partnerCount < config.CopartnerCount {
+				benefitWxUser.CopartnerState = user_model.COPARTNER_STATE_YES
+				benefitWxUser.BeCopartnerTime = time.Now()
+				benefitWxUser.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.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)
+
+	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 TmplMsgNotice(order *order_model.Order) {
+	if order == nil {
+		return
+	}
+
+	wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(order.WxUserId, beego.AppConfig.String("WxMPAppId"), true)
+	product := product_model.GetProductById(order.ProductId, true)
+	codeList := subject_model.GetSaleDrawCodesBySubjectIdAndWxUidAndOrderId(order.ProductSaleSubjectId, order.WxUserId, order.OrderId, false)
+
+	if wxUserGzh == nil || product == nil || len(codeList) == 0 {
+		return
+	}
+
+	var codes []string
+	for _, item := range codeList {
+		codes = append(codes, tool.GetAssignLengthStr(item.Code, 6))
+	}
+
+	if wxUserGzh != nil {
+		openId := wxUserGzh.GzhOpenId
+		url := fmt.Sprintf("%s/activity/project/%d", beego.AppConfig.String("WxHost"), order.ProductSaleSubjectId)
+		title := fmt.Sprintf("购买成功!获取抽奖码:%s", strings.Join(codes, ","))
+		pName := product.Name
+		orderId := order.OrderId
+		paiedPrice := fmt.Sprintf("%d份共%0.2f元", order.Count, tool.RoundFloat64(float64(order.TotalPrice)/100, 2))
+		if order.PayWay == pay_model.PAYWAY_BALANCE {
+			paiedPrice = fmt.Sprintf("%d份共%d代金券", order.Count, order.TotalPrice)
+		}
+		paiedAt := time.Unix(order.PaiedAt, 0).Format("2006-01-02 15:04:05")
+		remark := "备注:回复“我的抽奖号码”,可以看到您在当前活动的全部抽奖号码。\n了解更多内容请点击详情"
+		wx_mp.TmplmsgGetDrawCodeAfterPay(openId, url, title, pName, orderId, paiedPrice, paiedAt, remark)
+	}
+}
+
+//微信支付预定优惠
+func (self *PayController) wxPayReserveAsync() {
+	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.wxPayReserveAsync(%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.VerifyGzhPayResult(params) {
+		beego.BeeLogger.Error("VerifyPayResult not pass")
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	orderId := strings.Split(params.OutTradeNO, "_")[0]
+	order := reserve_act_model.GetOrderById(orderId)
+	totalFee, _ := strconv.ParseInt(params.TotalFee, 10, 64)
+	settlementTotalFee, _ := strconv.ParseInt(params.SettlementTotalFee, 10, 64)
+	beego.BeeLogger.Warn("after_pay_controller.wxPayReserveAsync.settlementTotalFee(%d)", settlementTotalFee)
+	if order == nil || order.TotalPrice != totalFee {
+		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.State = 1
+	order.TradeNo = params.TransactionId
+	order.PayWay = reserve_act_model.ORDER_PAY_WAY_WEIXINPAY
+	order.PaiedAt = time.Now().UnixNano() //支付时间存储纳秒级别
+	order.PaiedPrice = order.TotalPrice
+	if err := order.Save(); err != nil {
+		beego.BeeLogger.Error("payreserve weixinpay async return. save oId=%s fail", order.OrderId)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+	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 (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", "CheckBalanceBeforePayExchange"}
+	exceptCheckWxUserLoginAction = []string{"Pay", "PayAsync", "CheckBalanceBeforePayExchange"}
+)
+
+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()
+}

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

@@ -0,0 +1,121 @@
+package pay_controller
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/astaxie/beego"
+
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/course_model"
+	"fohow.com/apps/models/dollar_win_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/project_model"
+	"fohow.com/apps/models/reserve_act_model"
+)
+
+const (
+	BALANCE_TARGET    = "balance"
+	INVEST_TARGET     = "invest"
+	EXCHANGE_TARGET   = "exchange"
+	RESERVE_TARGET    = "reserve"
+	CASH_TARGET       = "cashcz"
+	COURSE_TARGET     = "course"
+	DOLLAR_WIN_TARGET = "dollar"
+)
+
+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 DOLLAR_WIN_TARGET: // 第五创活动夺宝付费
+
+		switch payWay {
+		case dollar_win_model.PAY_WAY_TYPE_DOLLAR_WIN_WX:
+			self.wxPayDollarWinAct(oId)
+		case dollar_win_model.PAY_WAY_TYPE_DOLLAR_WIN_CASH_BALANCE:
+			self.cashBalancePayDollarWinAct(oId)
+		default:
+			self.ReturnError(403, apps.ParamsError, "", nil)
+		}
+	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: // 商品购买
+		//if tradPwd == "" {
+		//	self.ReturnError(500, apps.TradePasswordError, "", nil)
+		//}
+		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
+	}
+	// 认购...
+	if strings.HasPrefix(oId, project_model.ORDER_ID_PREFIX_RG) {
+		return INVEST_TARGET
+	}
+	// 预定活动
+	if strings.HasPrefix(oId, reserve_act_model.ORDER_ID_PREFIX_RAO) {
+		return RESERVE_TARGET
+	}
+	// 付费课程
+	if strings.HasPrefix(oId, course_model.ORDER_ID_PREFIX_COURSE) {
+		return COURSE_TARGET
+	}
+
+	if strings.HasPrefix(oId, dollar_win_model.ORDER_ID_PREFIX_DW) {
+		return DOLLAR_WIN_TARGET
+	}
+	return ""
+}

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

@@ -0,0 +1,150 @@
+package pay_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"strconv"
+	"strings"
+
+	"fohow.com/apps"
+	// "fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/course_model"
+	"fohow.com/apps/models/pay_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/wx_mp"
+	"time"
+)
+
+//微信支付-付费课程
+func (self *PayController) wxPayCourse(orderId string) {
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	order := course_model.GetOrderByOId(orderId)
+	if order == nil || order.WxUId != wxUser.Id {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+
+	//无法支付,该订单已经支付或者已经删除或者已经退款
+	if order.State == course_model.ORDER_STATE_PAIED {
+		self.ReturnError(403, apps.HasPaied, "", nil)
+	}
+
+	//获取预支付信息
+	notifyUrl := fmt.Sprintf("%s/v1/pay/%s/async/%s", beego.AppConfig.String("ApiHost"), COURSE_TARGET, pay_model.PAYWAY_WEIXINPAY)
+	body := fmt.Sprintf("购买课程券")
+	wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(wxUser.Id, beego.AppConfig.String("WxMPAppId"), true)
+	if wxUserGzh == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	payData := wx_mp.GetWxPayData(wxUserGzh.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}
+	self.ServeJSON()
+}
+
+//付费课程-回调
+func (self *PayController) wxPayCourseAsync() {
+	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("wxPayCourseAsync(%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.VerifyWxPayResult(params) {
+		beego.BeeLogger.Error("VerifyPayResult not pass")
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	orderId := strings.Split(params.OutTradeNO, "_")[0]
+	order := course_model.GetOrderByOId(orderId)
+	totalFee, _ := strconv.ParseInt(params.TotalFee, 10, 64)
+	// settlementTotalFee, _ := strconv.ParseInt(params.SettlementTotalFee, 10, 64)
+	// beego.BeeLogger.Warn("after_pay_controller.wxPayInvestAsync.settlementTotalFee(%d)", settlementTotalFee)
+	if order == nil || order.TotalPrice != totalFee {
+		beego.BeeLogger.Error("wxPayCourseAsync err: %v", order)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	//重复回调通知时,判断订单状态是否处理过了
+	// beego.BeeLogger.Warn("小程序项目集助微信支付回调通知,订单编号=%s", order.OrderId)
+	if order.State == course_model.ORDER_STATE_PAIED {
+		notifyResponse["return_code"] = wx_mp.PAY_SUCCESS
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	order.State = 1
+	order.TradeNo = params.TransactionId
+	// order.PayWay = pay_model.PAYWAY_WEIXINPAY
+	order.PaiedPrice = totalFee
+	order.PaiedAt = time.Now().Unix()
+	if !order.Save() {
+		beego.BeeLogger.Error("weixinpay async return. save oId=%s fail", order.OrderId)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+	if order.InviteWxUId != 0 && order.InviteWxUId != order.WxUId {
+		wxInviter := user_model.GetWxUserById(order.InviteWxUId, true)
+		if wxInviter != nil {
+			go sendInviteBenefit(order, wxInviter)
+		}
+	}
+
+	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 sendInviteBenefit(order *course_model.Order, wxInviter *user_model.WxUser) {
+	if order == nil || wxInviter == nil {
+		return
+	}
+	course := course_model.GetCourseById(order.CourseId, true)
+	if course == nil || course.BenefitPrice == 0 {
+		return
+	}
+	// wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(wxInviter.Id, beego.AppConfig.String("WxMPAppId"), true)
+	// if wxUserGzh == nil {
+	// 	return
+	// }
+	//创建课程佣金记录
+	benefit := new(course_model.Benefit).Create(order.CourseId, wxInviter.Id, order.WxUId, order.TotalPrice, course.BenefitPrice)
+	//增加余额
+	if benefit != nil {
+		s := course_model.CASH_SOURCE_COURSE_BENEFIT
+		remark := course_model.CASH_SOURCE_COURSE_BENEFIT_NAME
+		new(course_model.Balance).Create(wxInviter.Id, benefit.BenefitPrice, s, benefit.OrderId, remark)
+	}
+
+	// ret := wx_mp.WxTransfers(wxUserGzh.GzhOpenId, benefit.BenefitPrice, benefit.OrderId, wx_mp.PAY_NO_CHECK, "", "推广奖励")
+	// if ret["result_code"] == wx_mp.PAY_SUCCESS {
+	// 	benefit.State = 1
+	// 	benefit.TradeNo = ret["payment_no"]
+	// 	// paiedAt, _ := time.Parse("2006-01-02 15:04:05", ret["payment_time"])
+	// 	benefit.Remark = "佣金已打款"
+	// 	benefit.PaiedAt = time.Now().Unix()
+	// 	benefit.Save()
+	// } else {
+	// 	benefit.State = 2
+	// 	benefit.Remark = ret["err_code_des"]
+	// 	benefit.Save()
+	// }
+}

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

@@ -0,0 +1,184 @@
+package pay_controller
+
+import (
+	"github.com/astaxie/beego"
+
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/dollar_win_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/wx_mp"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+
+//微信支付
+func (self *PayController) wxPayDollarWinAct(orderId string) {
+
+	user := self.GetCurrentUser(true)
+	if user == nil {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+	order := dollar_win_model.GetDollarWinZtJoinByOrderId(orderId, false)
+	if order == nil || order.UserId != user.Id {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+	//无法支付,该订单已经支付
+	if order.IsPaied {
+		self.ReturnError(403, apps.HasPaied, "", nil)
+	}
+	act := dollar_win_model.GetDollarWinZtConfigById(order.ZtConfigId, false)
+	now := time.Now()
+	if act == nil || act.StartTime.Unix() > now.Unix() || act.StopTime.Unix() < now.Unix() {
+		self.ReturnError(403, []string{"actNotExist", "活动不存在"}, "", nil)
+	}
+
+	////获取预支付信息
+	notifyUrl := fmt.Sprintf("%s/v1/pay/%s/async/%s", beego.AppConfig.String("ApiHost"), DOLLAR_WIN_TARGET, dollar_win_model.PAY_WAY_TYPE_DOLLAR_WIN_WX)
+	body := fmt.Sprintf("第五创活动报名")
+	wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(wxUser.Id, beego.AppConfig.String("WxMPAppId"), true)
+	if wxUserGzh == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	payData := wx_mp.GetWxPayData(wxUserGzh.GzhOpenId, order.OrderId, order.TotalAmount, body, notifyUrl, self.Ctx.Input.IP())
+
+	//返回数据
+	type PayData struct {
+		PayData map[string]string `json:"pay_data"`
+	}
+	self.Data["json"] = &PayData{PayData: payData}
+	self.ServeJSON()
+}
+
+//回调
+func (self *PayController) wxPayDollarWinAsync() {
+	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("wxPayDollarWinAsync(%v)", params)
+
+	if err != nil {
+		beego.BeeLogger.Error("wxPayDollarWinAsync.parsePayResult=[%s] err=[%s]", string(body), err)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	// 签名校验参数
+	if !wx_mp.VerifyWxPayResult(params) {
+		beego.BeeLogger.Error("wxPayDollarWinAsync.VerifyPayResult not pass")
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	orderId := strings.Split(params.OutTradeNO, "_")[0]
+	order := dollar_win_model.GetDollarWinZtJoinByOrderId(orderId, false)
+	totalFee, _ := strconv.ParseInt(params.TotalFee, 10, 64)
+
+	if order == nil || order.TotalAmount != totalFee {
+		beego.BeeLogger.Error("wxPayDollarWinAsync err: %v", order)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	//重复回调通知时,判断订单状态是否处理过了
+	if order.IsPaied {
+		notifyResponse["return_code"] = wx_mp.PAY_SUCCESS
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	order.IsPaied = true
+	order.TradeNo = params.TransactionId
+	order.PayWay = dollar_win_model.PAY_WAY_TYPE_DOLLAR_WIN_WX
+	order.PaiedAmount = totalFee
+	now := time.Now()
+	order.PaiedAt = now.Unix()
+	paiedAtStr := now.Format("20060102150405")
+	paiedAtNum, _ := strconv.ParseInt(paiedAtStr, 10, 64)
+	order.CalulatePaiedAt = paiedAtNum
+	if !order.Save() {
+		beego.BeeLogger.Error("wxPayDollarWinAsync.weixinpay async return. save oId=%s fail", order.OrderId)
+		self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+		return
+	}
+
+	notifyResponse["return_code"] = wx_mp.PAY_SUCCESS
+	self.Ctx.WriteString(wx_mp.MapToXmlString(notifyResponse))
+	return
+}
+
+var cashBalancePayDollarWinActLock sync.Mutex
+
+//余额支付
+func (self *PayController) cashBalancePayDollarWinAct(orderId string) {
+	user := self.GetCurrentUser(true)
+	if user == nil {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser == nil {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+
+	defer cashBalancePayDollarWinActLock.Unlock()
+	cashBalancePayDollarWinActLock.Lock()
+	order := dollar_win_model.GetDollarWinZtJoinByOrderId(orderId, false)
+	if order == nil || order.UserId != user.Id {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+	//无法支付,该订单已经支付
+	if order.IsPaied {
+		self.ReturnError(403, apps.HasPaied, "", nil)
+	}
+	act := dollar_win_model.GetDollarWinZtConfigById(order.ZtConfigId, false)
+	now := time.Now()
+	if act == nil || act.StartTime.Unix() > now.Unix() || act.StopTime.Unix() < now.Unix() {
+		self.ReturnError(403, []string{"actNotExist", "活动不存在"}, "", nil)
+	}
+
+	//判断余额是否足够
+	available := balance_model.GetCashTotalBalance(wxUser.Id)
+
+	if available < order.TotalAmount {
+		self.ReturnError(403, apps.BalanceNotEnough, "", nil)
+	}
+
+	if order.TotalAmount > 0 {
+		source := balance_model.CASH_SOURCE_DOLLAR_WIN
+		relateId := order.OrderId
+		remark := fmt.Sprintf("%s", act.Title)
+		//扣除余额
+		new(balance_model.CashBalance).Create(wxUser.Id, -order.TotalAmount, source, relateId, remark)
+	} else {
+		self.ReturnError(403, apps.Error, "", nil)
+	}
+
+	//订单完成支付,更新支付状态和支付时间和支付方式
+	order.IsPaied = true
+	nowTime := time.Now()
+	order.PaiedAt = nowTime.Unix()
+	paiedAtStr := nowTime.Format("20060102150405")
+	paiedAtNum, _ := strconv.ParseInt(paiedAtStr, 10, 64)
+	order.CalulatePaiedAt = paiedAtNum
+	order.PaiedAmount = order.TotalAmount
+	order.PayWay = dollar_win_model.PAY_WAY_TYPE_DOLLAR_WIN_CASH_BALANCE
+	if !order.Save() {
+		beego.BeeLogger.Error("cash_balance_pay dollar_win. save oId=%s fail", order.OrderId)
+		self.ReturnError(403, apps.Error, "", nil)
+	}
+
+	//返回数据
+	result := PayUrl{PayUrl: "", PayData: nil, OrderId: order.OrderId}
+	self.Data["json"] = self.FormatResult([]interface{}{result})
+	self.ServeJSON()
+}

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

@@ -0,0 +1,333 @@
+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"
+	// "fohow.com/apps/models/wx_gongzhonghao_model"
+	"fohow.com/apps/models/exchange_model"
+	"fohow.com/apps/models/subject_model"
+	"sync"
+)
+
+var createDrawCode sync.Mutex
+
+//支付兑换订单
+func (self *PayController) payExchange(oId, payWay, tradPwd, returnUrl, source string) {
+	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)
+	}
+	//获取购物车产品
+	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.PurchaseLimitCount > 0 {
+			beego.BeeLogger.Warn("product.PurchaseLimitCount: %d", product.PurchaseLimitCount)
+			if product.PurchaseLimitCount < item.Count {
+				self.ReturnError(403, []string{apps.OverLimitCount[0], fmt.Sprintf("%s超过限购数量", product.Name)}, "", nil)
+			} else {
+				purchaseTotalCount := order_model.GetOrderCountByPIdAndWxUId(product.Id, wxUId)
+				//历史已经买够到限购数量了
+				if product.PurchaseLimitCount <= purchaseTotalCount {
+					self.ReturnError(403, []string{apps.PurchasedReachLimit[0], fmt.Sprintf("%s已购买总数已达到限购数量", product.Name)}, "", nil)
+				}
+				//历史没买够数量,但是(历史总数+想购买的数量)>限购数量
+				if product.PurchaseLimitCount < (purchaseTotalCount + order.Count) {
+					canBuyCount := product.PurchaseLimitCount - purchaseTotalCount
+					self.ReturnError(403, []string{apps.PurchasedReachLimit[0], fmt.Sprintf("%s您还可以购买%d件", product.Name, canBuyCount)}, "", 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)
+			}
+		}
+		//限新逻辑: 微信支付完成购买过商品的用户
+		if product.IsOnlyNew {
+			paiedOrder := order_model.GetPaiedOrderByWxUIdAndPayWayLimitOne(wxUId, order_model.PAY_WAY_WEIXIN, false)
+			if paiedOrder != nil {
+				self.ReturnError(403, []string{apps.OnlyNew[0], fmt.Sprintf("%s仅限新人购买", product.Name)}, "", nil)
+			}
+		}
+		//判断库存
+		leftCount := int64(0)
+		//普通订单
+		soldCount := order_model.GetSoldCountByPId(product.Id, false)
+		leftCount = product.Count - soldCount
+		//商品已经卖完
+		if leftCount < order.Count {
+			self.ReturnError(403, []string{apps.ProductStockNotEnough[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
+		}
+	}
+	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: // 代金券购买
+
+		/*		platform := exchange_model.GetPlatformByTag(exchange_model.DEFAULT_PLATFORM_TAG, false)
+				if platform == nil {
+					self.ReturnError(403, apps.PlatformNoExist, "", nil)
+				}*/
+		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)
+		}
+		//防刷判断
+		/*		isContinue := AnalyseMallBalance(curUser, platform, order)
+				if !isContinue {
+					self.ReturnError(403, apps.NetworkBusy, "", nil)
+				}*/
+
+		userLeftBalanceCount := balance_model.GetUserTotalBalance(wxUId)
+		tp := order.TotalPrice
+		freight := order_model.FREIGHT
+		if tp >= order_model.FREIGHT_LIMIT {
+			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.PaiedPrice = order.TotalPrice
+		order.PayWay = pay_model.PAYWAY_BALANCE
+		order.Source = source
+		order.Save()
+
+		// go updateCache(project.Id)
+		//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 {
+				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))
+			}
+		}
+	}
+}
+
+func (self *PayController) CheckBalanceBeforePayExchange() {
+	oId := self.GetString("order_id")
+
+	//user := self.GetCurrentUser(false)
+	//if user == nil {
+	//	self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	//}
+	wxUser := self.GetCurrentWxUser(false)
+	if wxUser == nil {
+		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+	}
+
+	order := order_model.GetOrderById(oId)
+	if order == nil || order.WxUserId != wxUser.Id {
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	}
+	if order.Status != order_model.STATUS_UNPAY {
+		self.ReturnError(403, apps.NotUnPay, "", nil)
+	}
+
+	type Ret struct {
+		LackLuoboCount int64 `json:"lack_luobo_count"`
+		RechargeMoney  int64 `json:"need_recharge_money"`
+	}
+
+	userLeftBalanceCount := balance_model.GetUserTotalBalance(wxUser.Id)
+	tp := order.UnitRoboBalancePrice * order.Count
+	lackCount := tp - userLeftBalanceCount //(订单金额-用户剩余代金券1000-500=500,1000-2000=-1000,1000-1000=0)
+	if lackCount < 0 {
+		lackCount = 0
+	}
+
+	self.Data["json"] = &Ret{LackLuoboCount: lackCount, RechargeMoney: lackCount}
+	self.ServeJSON()
+}
+
+//处理生成抽奖码
+func CreateDrawCode(order *order_model.Order) {
+
+	createDrawCode.Lock()
+	defer createDrawCode.Unlock()
+
+	subject := subject_model.GetProductSaleSubjectById(order.ProductSaleSubjectId, true)
+	if subject == nil {
+		return
+	}
+	for i := int64(1); i <= order.Count; i++ {
+		drawCode := subject_model.GetLastSaleDrawCodeBySubjectId(order.ProductSaleSubjectId)
+		if drawCode == nil {
+			new(subject_model.SaleDrawCode).Create(int64(1), order.WxUserId, order.ProductSaleSubjectId, order.OrderId)
+		} else {
+			new(subject_model.SaleDrawCode).Create(drawCode.Code+1, order.WxUserId, order.ProductSaleSubjectId, order.OrderId)
+		}
+	}
+	go TmplMsgNotice(order)
+}
+
+func AnalyseMallBalance(user *user_model.User, platform *exchange_model.Platform, order *order_model.Order) bool {
+
+	if user != nil {
+		//查询是否在通用白名单上
+		if user.IsUnlimitPayProduct {
+			return true
+		}
+		//第五创查询
+		resultAnalyse := helpers.AnalysePlatformMallBalance(platform.Secret, user.Tel, helpers.ANALYSE_URL)
+		beego.BeeLogger.Warn("pay_controller.AnalyseMallBalance().Platform analyse retMsg:%v", resultAnalyse)
+		//查询错误,则提示用户错误请稍后重试
+		if resultAnalyse == nil || resultAnalyse.Normal == helpers.UNNORMAL {
+			return false
+		} else if resultAnalyse.Normal == helpers.UNNORMAL_POINT_TASK {
+			//非正常的代金券任务刷子,不能继续支付,关闭订单,且更新为黑名单用户
+			order.Status = order_model.STATUS_CLOSED
+			order.Save()
+			user.IsBlackUser = 1
+			user.Save()
+			return false
+		} else if resultAnalyse.Normal == helpers.NORMAL {
+			user.IsUnlimitPayProduct = true
+			user.Save()
+		}
+	}
+	return true
+}

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

@@ -0,0 +1,233 @@
+package pay_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"sync"
+
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/project_model"
+	"fohow.com/libs/tool"
+	"fohow.com/libs/wx_mp"
+	"time"
+)
+
+//投资助农项目时,微信、余额支付
+var payInvest sync.Mutex
+var cashPayInvest sync.Mutex
+
+//助农项目-微信支付
+func (self *PayController) wxPayInvest(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)
+	}
+
+	defer payInvest.Unlock()
+	payInvest.Lock()
+
+	//查询项目投资订单
+	join := project_model.GetInvestByOrderId(orderId)
+
+	if join == nil {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+
+	//属于当前用户
+	if join.UserId != user.Id {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+
+	//无法支付,该订单已经支付或者已经删除或者已经退款
+	if join.IsDeleted == int64(1) || join.IsRefunded == int64(1) || join.State == int64(1) {
+		self.ReturnError(403, apps.ProjectJoinNotUnPay, "", nil)
+	}
+
+	project := project_model.GetProjectById(join.ProjectId, false)
+	if project == nil {
+		self.ReturnError(403, apps.ProjectNoExist, "", nil)
+	}
+
+	if project.State == project_model.STATE_SUCCESS {
+		self.ReturnError(403, apps.ProjectSuccess, "", nil)
+	}
+
+	if project.State != project_model.STATE_UNDERWAY {
+		self.ReturnError(403, apps.ProjectNotUnderWay, "", nil)
+	}
+
+	investWay := project_model.GetProjectInvestWayById(join.ProjectInvestWayId, false)
+	if investWay == nil || investWay.State == 0 {
+		self.ReturnError(403, apps.ProjectInvestWayNoExist, "", nil)
+	}
+
+	if investWay.ProjectId != project.Id {
+		self.ReturnError(403, apps.ProjectInvestWayNotMatch, "", nil)
+	}
+
+	if investWay.OnlyNew == int64(1) {
+		/*userInvestments := project_model.GetInvestmentsByUId(1,1, user.Id, true)
+		if userInvestments != nil && len(userInvestments) != 0{
+			self.ReturnError(403, apps.ProjectInvestOnlyNew, "", nil)
+		}*/
+		specialProjectId := int64(7) //沙田柚项目
+		if beego.BConfig.RunMode == beego.DEV {
+			specialProjectId = int64(58) //内网某项目测试使用
+		}
+		specialProjectInvestments := project_model.GetInvestmentsByUIdExcludePId(1, 1, user.Id, specialProjectId, false)
+		if len(specialProjectInvestments) > 0 {
+			self.ReturnError(403, apps.ProjectInvestOnlyNew, "", nil)
+		}
+	}
+
+	//份数是否足够
+	userHasInvestCount := project_model.GetInvestSuccessCountByPIdAndWIdAndUId(project.Id, investWay.Id, user.Id, false)
+	investWayLeftCount := project_model.GetLeftCountByInvestWayId(investWay.Id)
+	if (investWayLeftCount < join.Count) ||
+		(investWay.MaxLimit < join.Count+userHasInvestCount) ||
+		(join.Count < investWay.MinLimit && investWay.MinLimit <= investWayLeftCount && userHasInvestCount == 0) ||
+		(join.Count < investWayLeftCount && investWayLeftCount < investWay.MinLimit && userHasInvestCount == 0) {
+		self.ReturnError(403, apps.CountLimit, "", nil)
+	}
+
+	//获取预支付信息
+	notifyUrl := fmt.Sprintf("%s/v1/pay/%s/async/%s", beego.AppConfig.String("ApiHost"), INVEST_TARGET, project_model.PAY_WAY_TYPE_PROJECT_JOIN)
+	body := fmt.Sprintf("FOHOW玖玖-我要助农")
+	payData := wx_mp.GetPayData(wxUser.Openid, join.OrderId, join.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()
+}
+
+//助农项目-余额支付
+func (self *PayController) cashBalancePayInvest(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)
+	}
+
+	defer cashPayInvest.Unlock()
+	cashPayInvest.Lock()
+
+	//查询项目投资订单
+	join := project_model.GetInvestByOrderId(orderId)
+
+	if join == nil {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+
+	//属于当前用户
+	if join.UserId != user.Id {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+
+	//无法支付,该订单已经支付或者已经删除或者已经退款
+	if join.IsDeleted == int64(1) || join.IsRefunded == int64(1) || join.State == int64(1) {
+		self.ReturnError(403, apps.ProjectJoinNotUnPay, "", nil)
+	}
+
+	project := project_model.GetProjectById(join.ProjectId, false)
+	if project == nil {
+		self.ReturnError(403, apps.ProjectNoExist, "", nil)
+	}
+
+	if project.State == project_model.STATE_SUCCESS {
+		self.ReturnError(403, apps.ProjectSuccess, "", nil)
+	}
+
+	if project.State != project_model.STATE_UNDERWAY {
+		self.ReturnError(403, apps.ProjectNotUnderWay, "", nil)
+	}
+
+	investWay := project_model.GetProjectInvestWayById(join.ProjectInvestWayId, false)
+	if investWay == nil || investWay.State == 0 {
+		self.ReturnError(403, apps.ProjectInvestWayNoExist, "", nil)
+	}
+
+	if investWay.ProjectId != project.Id {
+		self.ReturnError(403, apps.ProjectInvestWayNotMatch, "", nil)
+	}
+
+	if investWay.OnlyNew == int64(1) {
+		/*userInvestments := project_model.GetInvestmentsByUId(1,1, user.Id, true)
+		if userInvestments != nil && len(userInvestments) != 0{
+			self.ReturnError(403, apps.ProjectInvestOnlyNew, "", nil)
+		}*/
+		specialProjectId := int64(7) //沙田柚项目
+		if beego.BConfig.RunMode == beego.DEV {
+			specialProjectId = int64(58) //内网某项目测试使用
+		}
+		specialProjectInvestments := project_model.GetInvestmentsByUIdExcludePId(1, 1, user.Id, specialProjectId, false)
+		if len(specialProjectInvestments) > 0 {
+			self.ReturnError(403, apps.ProjectInvestOnlyNew, "", nil)
+		}
+	}
+
+	//份数是否足够
+	userHasInvestCount := project_model.GetInvestSuccessCountByPIdAndWIdAndUId(project.Id, investWay.Id, user.Id, false)
+	investWayLeftCount := project_model.GetLeftCountByInvestWayId(investWay.Id)
+	if (investWayLeftCount < join.Count) ||
+		(investWay.MaxLimit < join.Count+userHasInvestCount) ||
+		(join.Count < investWay.MinLimit && investWay.MinLimit <= investWayLeftCount && userHasInvestCount == 0) ||
+		(join.Count < investWayLeftCount && investWayLeftCount < investWay.MinLimit && userHasInvestCount == 0) {
+		self.ReturnError(403, apps.CountLimit, "", nil)
+	}
+
+	//判断余额是否足够
+	available := balance_model.GetCashTotalBalance(wxUser.Id)
+	tp := join.TotalPrice
+
+	if available < tp {
+		self.ReturnError(403, apps.BalanceNotEnough, "", nil)
+	}
+
+	if tp > 0 {
+		source := balance_model.CASH_SOURCE_PROJECT_INVEST
+		relateId := join.OrderId
+		remark := fmt.Sprintf("%s_%s", balance_model.CASH_SOURCE_PROJECT_INVEST_NAME, project.Title)
+		//扣除余额
+		new(balance_model.CashBalance).Create(wxUser.Id, -tp, source, relateId, remark)
+	}
+
+	//订单完成支付,更新支付状态和支付时间和支付方式
+	join.State = 1
+	join.PayWay = project_model.PAY_WAY_TYPE_PROJECT_JOIN_CASH_BALANCE
+	join.PaiedAt = time.Now().UnixNano() //支付时间存储纳秒级别
+	if err := join.Save(); err != nil {
+		beego.BeeLogger.Error("cash_balance_pay invest. save oId=%s fail", join.OrderId)
+		self.ReturnError(403, apps.UpdateProjectJoinOrderError, "", nil)
+	}
+	go projectStateCheck(join)
+	go self.DistributeBonusAsync(project.Id, investWay.Id)
+	//返回数据
+	result := PayUrl{PayUrl: "", PayData: nil, OrderId: join.OrderId}
+	self.Data["json"] = self.FormatResult([]interface{}{result})
+	self.ServeJSON()
+
+}
+
+//满足条件则异步开仓放粮
+func (self *PayController) DistributeBonusAsync(pId, wayId int64) {
+
+	projectBonus := project_model.GetProjectBonusByPIdAndWId(pId, wayId, false)
+	if projectBonus != nil && projectBonus.IsDistributeBonus {
+		url := fmt.Sprintf("%s%s%d", beego.AppConfig.String("ApiHost"), "/v1/granary/distribute/bonus/", projectBonus.Id)
+		resp := tool.HttpCall(url, "GET", nil, nil)
+		beego.BeeLogger.Warn("pay_invest_controller.DistributeBonusAsync resp, resp:%s", resp)
+	}
+
+}

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

@@ -0,0 +1,63 @@
+package pay_controller
+
+import (
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/models/reserve_act_model"
+	"fohow.com/libs/wx_mp"
+	"github.com/astaxie/beego"
+	"sync"
+)
+
+var payReserve sync.Mutex
+
+//预定优惠专题支付
+func (self *PayController) payReserve(oId, payWay, tradPwd, returnUrl string) {
+
+	wxUId := self.GetCurrentWxUserId()
+	uId := self.GetCurrentUserId()
+	wxUser := self.GetCurrentWxUser(false)
+	user := self.GetCurrentUser(false)
+
+	payReserve.Lock()
+	defer payReserve.Unlock()
+
+	if wxUser == nil || user == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	//订单是否存在//订单是否属于当前用户
+	order := reserve_act_model.GetOrderById(oId)
+	if order == nil || order.UserId != uId || order.WxUserId != wxUId {
+		self.ReturnError(404, apps.OrderNotExist, "", nil)
+	}
+
+	//订单是否不可支付
+	if order.State == 1 {
+		self.ReturnError(403, apps.NotUnPay, "", nil)
+	}
+
+	//活动是否存在
+	act := reserve_act_model.GetReserveActById(order.ActId, false)
+	//活动不是进行中
+	if act == nil || act.State != reserve_act_model.STATE_UNDERWAY {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	switch payWay {
+	case reserve_act_model.ORDER_PAY_WAY_WEIXINPAY:
+
+		notifyUrl := fmt.Sprintf("%s/v1/pay/%s/async/%s", beego.AppConfig.String("ApiHost"), RESERVE_TARGET, reserve_act_model.ORDER_PAY_WAY_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}
+	default:
+		beego.BeeLogger.Error("pay way not match, payway:%s", payWay)
+	}
+	self.ServeJSON()
+
+}

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

@@ -0,0 +1,127 @@
+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))
+}

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

@@ -0,0 +1,870 @@
+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)
+	if wxUser != nil {
+		wxUser.Openid = encryptedData.OpenId
+	} else {
+		introUserId := int64(0)
+		if inviteId == int64(0) {
+			inviteId = int64(1)
+			introUserId = int64(1)
+		}
+		ip := self.Ctx.Input.IP()
+		wxUser = new(user_model.WxUser).QuickCreate(encryptedData.OpenId, encryptedData.UnionId, ip, channel, time.Now().Unix())
+		beego.BeeLogger.Warn("XcxAuthorize_quickCreate_inviteId:%d", inviteId)
+		wxUser.InviteId = inviteId
+		inviter := user_model.GetWxUserById(inviteId, true)
+		if inviter.ShowInviteMode == 1 && inviteId != int64(1) {
+			introUserId = inviter.Id
+		} else {
+			introUserId = inviter.IntroUserId
+		}
+		wxUser.IntroUserId = introUserId
+		//设定会员内部编号
+		introArea := user_model.GetAvailableIntroArea(inviter.Id)
+		introInnerNo := inviter.IntroInnerNo + introArea
+		wxUser.IntroArea = introArea
+		wxUser.IntroInnerNo = introInnerNo
+
+		//注册会员
+		user := user_model.Create("", ip)
+		if user == nil {
+			self.ReturnError(403, apps.RegisterUserError, "", 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
+		//参数第一,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.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
+// }

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

@@ -0,0 +1,170 @@
+package poster_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"io/ioutil"
+	"fohow.com/apps"
+	"fohow.com/apps/models/poster_model"
+	"fohow.com/apps/models/project_model"
+	"fohow.com/libs/ali_oss"
+	"fohow.com/libs/wx_mp"
+	"strconv"
+	"strings"
+	"time"
+)
+
+var (
+	//不需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"GetPosterXcxQrcode", "ScanPosterXcxQrcode"}
+	exceptCheckWxUserLoginAction = []string{"ScanPosterXcxQrcode"}
+)
+
+type PosterController struct {
+	apps.BaseController
+}
+
+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 = project_model.GetCdnFullImgUrl(DEFAULT_HEAD)
+
+	wxUser := self.GetCurrentWxUser(true)
+	if wxUser != nil {
+		wxHead = project_model.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 = project_model.GetCdnFullImgUrl(uploadPath)
+		}
+
+		new(poster_model.PosterQrcodeRecord).Create(wxUid, id, id, _type, uploadPath)
+
+	} else {
+		qrcodeUrl = project_model.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
+}

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

@@ -0,0 +1,246 @@
+package product_controller
+
+import (
+	// "fmt"
+	"strconv"
+	"strings"
+	// "time"
+
+	// "github.com/astaxie/beego"
+
+	"fohow.com/apps"
+	"fohow.com/apps/models/granary_model"
+	"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 = order_model.GetSoldCountByPId(pd.Id, cache)
+		if pd.Ptype == product_model.TYPE_USER_SALE {
+			pd.LeftCount = granary_model.GetSaleOrderLeftCountByPId(pd.Id)
+		} else {
+			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 = order_model.GetSoldCountByPId(pId, cache)
+	//代销商品特殊计算库存
+	if pd.Ptype == product_model.TYPE_USER_SALE {
+		pd.LeftCount = granary_model.GetSaleOrderLeftCountByPId(pId)
+	} else {
+		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.GetFullImgUrl(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 = order_model.GetSoldCountByPId(pd.Id, cache)
+		if pd.Ptype == product_model.TYPE_USER_SALE {
+			pd.LeftCount = granary_model.GetSaleOrderLeftCountByPId(pd.Id)
+		} else {
+			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()
+}

+ 22 - 0
go/gopath/src/fohow.com/apps/controllers/project_controller/init.go

@@ -0,0 +1,22 @@
+package project_controller
+
+import (
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+)
+
+var (
+	//不需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"ProjectList", "ProjectDetail", "GetInvestorList", "GetInvestWayList"}
+	exceptCheckWxUserLoginAction = []string{"ProjectList", "ProjectDetail", "GetInvestorList", "GetInvestWayList"}
+)
+
+type ProjectController struct {
+	apps.BaseController
+}
+
+func (self *ProjectController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}

+ 195 - 0
go/gopath/src/fohow.com/apps/controllers/project_controller/invest_controller.go

@@ -0,0 +1,195 @@
+package project_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"fohow.com/apps"
+	"fohow.com/apps/models/project_model"
+	"strconv"
+)
+
+//查询用户是否有未支付的订单
+func (self *ProjectController) CheckUnpayOrder() {
+
+	_projectId := self.Ctx.Input.Param(":project_id")
+	projectId, _ := strconv.ParseInt(_projectId, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	investWayId, _ := self.GetInt64("invest_way_id", 0)
+
+	project := project_model.GetProjectById(projectId, useCache)
+
+	if project == nil {
+		self.ReturnError(403, apps.ProjectNoExist, "", nil)
+	}
+
+	investWay := project_model.GetProjectInvestWayById(investWayId, useCache)
+
+	if investWay == nil || investWay.State == 0 {
+		self.ReturnError(403, apps.ProjectInvestWayNoExist, "", nil)
+	}
+
+	if investWay.ProjectId != project.Id {
+		self.ReturnError(403, apps.ProjectInvestWayNotMatch, "", nil)
+	}
+
+	type Ret struct {
+		Order *project_model.ProjectJoin `json:"order"`
+	}
+
+	user := self.GetCurrentUser(useCache)
+	unpayOrder := project_model.GetUnPayOrderByPIdAndWIdAndUId(project.Id, investWay.Id, user.Id, false)
+	type InvestOrder struct {
+		OrderId string `json:"order_id"`
+	}
+	investOrder := new(InvestOrder)
+	investOrder.OrderId = unpayOrder.OrderId
+	self.Data["json"] = investOrder
+	self.ServeJSON()
+
+}
+
+//生成投资记录
+func (self *ProjectController) GenerateOrder() {
+	//回报方式ID,默认为0
+	investWayId, _ := self.GetInt64("invest_way_id", 0)
+	// 项目Id
+	_id := self.Ctx.Input.Param(":project_id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	// 认购份数
+	_c := self.Ctx.Input.Param(":count")
+	c, _ := strconv.ParseInt(_c, 10, 64)
+	if c <= 0 {
+		self.ReturnError(403, apps.CountLimit, "", nil)
+	}
+
+	project := project_model.GetProjectById(id, false)
+	if project == nil {
+		self.ReturnError(403, apps.ProjectNoExist, "", nil)
+	}
+
+	investWay := project_model.GetProjectInvestWayById(investWayId, false)
+
+	if investWay == nil || investWay.State == 0 {
+		self.ReturnError(403, apps.ProjectInvestWayNoExist, "", nil)
+	}
+
+	if investWay.ProjectId != project.Id {
+		self.ReturnError(403, apps.ProjectInvestWayNotMatch, "", nil)
+	}
+
+	if project.State == project_model.STATE_SUCCESS {
+		self.ReturnError(403, apps.ProjectSuccess, "", nil)
+	}
+
+	if project.State != project_model.STATE_UNDERWAY {
+		self.ReturnError(403, apps.ProjectNotUnderWay, "", nil)
+	}
+
+	user := self.GetCurrentUser(true)
+
+	if investWay.OnlyNew == int64(1) {
+		/*userInvestments := project_model.GetInvestmentsByUId(1,1, user.Id, false)
+		if userInvestments != nil && len(userInvestments) != 0{
+			self.ReturnError(403, apps.ProjectInvestOnlyNew, "", nil)
+		}*/
+		specialProjectId := int64(7) //沙田柚项目
+		if beego.BConfig.RunMode == beego.DEV {
+			specialProjectId = int64(58) //内网某项目测试使用
+		}
+		specialProjectInvestments := project_model.GetInvestmentsByUIdExcludePId(1, 1, user.Id, specialProjectId, false)
+		if len(specialProjectInvestments) > 0 {
+			self.ReturnError(403, apps.ProjectInvestOnlyNew, "", nil)
+		}
+	}
+
+	userHasInvestCount := project_model.GetInvestSuccessCountByPIdAndWIdAndUId(project.Id, investWay.Id, user.Id, false)
+
+	investWayLeftCount := project_model.GetLeftCountByInvestWayId(investWay.Id)
+
+	//超过剩余份数
+	if investWayLeftCount < c {
+		self.ReturnError(403, apps.CountNotEnough, "", nil)
+	}
+
+	//超过最大限购份数
+	if investWay.MaxLimit < (c + userHasInvestCount) {
+		self.ReturnError(403, apps.CountLimit, "", nil)
+	}
+
+	if (c+userHasInvestCount-investWay.MinLimit)%investWay.GapLimit != 0 && (investWayLeftCount%investWay.MinLimit)%investWay.GapLimit == 0 {
+		self.ReturnError(403, []string{apps.CountLimit[0], fmt.Sprintf("%d份起购,请确认您增加购买的数量是%d的倍数", investWay.MinLimit, investWay.GapLimit)}, "", nil)
+	}
+
+	//买过的用户支持兜底
+	//if (c + userHasInvestCount - investWay.MinLimit) % investWay.GapLimit != 0 && investWayLeftCount < investWay.MinLimit && userHasInvestCount > 0 && c!= investWayLeftCount{
+	//	self.ReturnError(403, []string{apps.CountLimit[0], fmt.Sprintf("剩余%d份, 您需要全部购买(支持手动输入)", investWayLeftCount)}, "", nil)
+	//}
+	if (c < investWay.MinLimit && investWay.MinLimit <= investWayLeftCount && userHasInvestCount > 0) ||
+		(c < investWayLeftCount && investWayLeftCount < investWay.MinLimit && userHasInvestCount > 0) {
+		if investWay.MinLimit <= investWayLeftCount {
+			self.ReturnError(403, []string{apps.CountLimit[0], fmt.Sprintf("%d份起购", investWay.MinLimit)}, "", nil)
+		} else {
+			self.ReturnError(403, []string{apps.CountLimit[0], fmt.Sprintf("剩余%d份, 您需要全部购买(支持手动输入)", investWayLeftCount)}, "", nil)
+		}
+	}
+
+	//没买过的用户支持兜底。
+	if (c < investWay.MinLimit && investWay.MinLimit <= investWayLeftCount && userHasInvestCount == 0) ||
+		(c < investWayLeftCount && investWayLeftCount < investWay.MinLimit && userHasInvestCount == 0) {
+		if investWay.MinLimit <= investWayLeftCount {
+			self.ReturnError(403, []string{apps.CountLimit[0], fmt.Sprintf("%d份起购", investWay.MinLimit)}, "", nil)
+		} else {
+			self.ReturnError(403, []string{apps.CountLimit[0], fmt.Sprintf("剩余%d份, 您需要全部购买(支持手动输入)", investWayLeftCount)}, "", nil)
+		}
+	}
+
+	projectJoin := new(project_model.ProjectJoin).Create(user.Id, project.Id, c, investWay.Price, investWay.Id)
+	if projectJoin == nil {
+		self.ReturnError(403, apps.CreateOrderFail, "", nil)
+	}
+
+	type InvestOrder struct {
+		OrderId string `json:"order_id"`
+	}
+	beego.BeeLogger.Warn("order_id:%s", projectJoin.OrderId)
+
+	//测试环境只需支付1分钱
+	/*if beego.BConfig.RunMode == beego.DEV {
+		projectJoin.TotalPrice = 1
+		projectJoin.PaiedPrice = 1
+		projectJoin.Save()
+	}*/
+
+	self.Data["json"] = &InvestOrder{OrderId: projectJoin.OrderId}
+	self.ServeJSON()
+}
+
+//查询投资记录
+func (self *ProjectController) OrderInfo() {
+	orderId := self.Ctx.Input.Param(":order_id")
+
+	join := project_model.GetInvestByOrderId(orderId)
+
+	user := self.GetCurrentUser(true)
+	if join != nil {
+		if join.UserId != user.Id {
+			self.ReturnError(403, apps.NoExist, "", nil)
+		}
+		join.CTime = join.CreatedAt.Unix()
+		if join.CTime < 0 {
+			join.CTime = 0
+		}
+		useCache, _ := self.GetBool("cache", true)
+		project := project_model.GetProjectById(join.ProjectId, useCache)
+		if project != nil {
+			join.ProjectName = project.Title
+			join.ProjectType = project_model.PTYPE_CN_TEXT[project.Ptype]
+			join.ProjectState = project_model.STATE_CN_TEXT[project.State]
+			join.ProjectThumbImg = project_model.GetFullImgUrl(project.ImgThumb)
+			join.ProjectDetailImg = project_model.GetFullImgUrl(project.ImgDetail)
+		}
+	}
+
+	self.Data["json"] = join
+	self.ServeJSON()
+}

+ 85 - 0
go/gopath/src/fohow.com/apps/controllers/project_controller/invest_way_controller.go

@@ -0,0 +1,85 @@
+package project_controller
+
+import (
+	"strconv"
+
+	"fohow.com/apps"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/project_model"
+)
+
+func (self *ProjectController) GetInvestWayList() {
+	// 获取项目ID
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+
+	project := project_model.GetProjectById(id, true)
+	if project == nil {
+		self.ReturnError(403, apps.ProjectNoExist, "", nil)
+	}
+
+	uId := self.GetCurrentUserId()
+
+	list := project_model.GetInvestWaysByProjectId(id)
+	type Ret struct {
+		ProjectId          int64                             `json:"project_id"`
+		InvestWay          []*project_model.ProjectInvestWay `json:"invest_way"`
+		TotalLeftCopiesMin int64                             `json:"total_left_copies_min"`
+		TotalLeftCopiesMax int64                             `json:"total_left_copies_max"`
+
+		//旧使用
+		TotalLeftCopies int64 `json:"total_left_copies"`
+	}
+
+	var investWayList []*project_model.ProjectInvestWay
+	for _, way := range list {
+
+		if way.State == 1 {
+
+			way.LeftCopies = project_model.GetLeftCountByInvestWayId(way.Id)
+			if way.LeftCopies < 0 {
+				way.LeftCopies = 0
+			}
+			//总份数重新计算 2018-08-25 by zhouxiaoling
+			//totalLeft = totalLeft + way.LeftCopies
+			userHasInvestCount := project_model.GetInvestSuccessCountByPIdAndWIdAndUId(id, way.Id, uId, false)
+			way.OwnedCopies = userHasInvestCount
+			way.Product = product_model.GetProductById(way.ProductId, true)
+
+			investWayList = append(investWayList, way)
+		}
+
+	}
+
+	totalCopiesMin, totalCopiesMax := int64(0), int64(0)
+	if project.MinFunding < project.MaxFunding {
+		totalCopiesMin = int64(float64(project.MinFunding) / float64(project.UnitPrice))
+		totalCopiesMax = int64(float64(project.MaxFunding) / float64(project.UnitPrice))
+	} else {
+		totalCopiesMin = int64(float64(project.MinFunding) / float64(project.UnitPrice))
+		totalCopiesMax = int64(float64(project.MinFunding) / float64(project.UnitPrice))
+	}
+
+	//如果是有超募的,预热和团购中时,显示剩余份数范围
+	joinCopies := project_model.GetInvestSuccessCountByPId(project.Id, false)
+	totalLeftMin := totalCopiesMin - joinCopies
+	totalLeftMax := totalCopiesMax - joinCopies
+	if totalLeftMin < 0 {
+		totalLeftMin = 0
+	}
+	if totalLeftMax < 0 {
+		totalLeftMax = 0
+	}
+
+	if len(investWayList) <= 0 {
+		investWayList = make([]*project_model.ProjectInvestWay, 0, 0)
+	}
+
+	ret := new(Ret)
+	ret.ProjectId = id
+	ret.InvestWay = investWayList
+	ret.TotalLeftCopiesMin = totalLeftMin
+	ret.TotalLeftCopiesMax = totalLeftMax
+	self.Data["json"] = ret
+	self.ServeJSON()
+}

+ 215 - 0
go/gopath/src/fohow.com/apps/controllers/project_controller/project_controller.go

@@ -0,0 +1,215 @@
+package project_controller
+
+import (
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/project_model"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//项目列表:项目名,参与人数,起投金额,助农进度,列表图
+func (self *ProjectController) ProjectList() {
+	state := self.GetString("state")
+	pType := self.GetString("ptype")
+	pName := self.GetString("title")
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	useCache, _ := self.GetBool("cache", true)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	//beego.BeeLogger.Warn("state: %v", state)
+	states := []int64{1, 2} //进行中,已成功
+	if strings.TrimSpace(state) != "" {
+		statesTmp := strings.Split(state, ",")
+		if len(statesTmp) > 0 {
+			states = nil
+			for _, each := range statesTmp {
+				n, _ := strconv.ParseInt(each, 10, 64)
+				states = append(states, n)
+			}
+		}
+	}
+	//beego.BeeLogger.Warn("states: %v", states)
+	list := project_model.GetProjectListByCondition(pName, pType, states, page, perPage, false)
+
+	count := project_model.GetProjectListCountByCondition(pName, pType, states, false)
+
+	for _, each := range list {
+
+		each.StartTime = each.StartAt.Unix()
+		if each.StartTime < 0 {
+			each.StartTime = 0
+		}
+		each.StopTime = each.StopAt.Unix()
+		if each.StopTime < 0 {
+			each.StopTime = 0
+		}
+		if each.SuccessTime < 0 {
+			each.SuccessTime = 0
+		}
+		each.ThumbImg = project_model.GetFullImgUrl(each.ImgThumb)
+
+		each.CurrentInvestUser = project_model.GetValidTotalCountByPId(each.Id, false)
+		each.CurrentInvestment = project_model.GetValidTotalFundingByPId(each.Id, false)
+		each.MinProjectWayPrice = project_model.GetMinProjeceWayPriceByPId(each.Id, useCache)
+
+		nowTime := time.Now().Unix()
+		if nowTime >= each.StartTime && nowTime < each.StopTime {
+			if each.State == project_model.STATE_PREHEAT {
+				each.State = project_model.STATE_UNDERWAY
+				each.Save()
+			}
+		}
+		if nowTime > each.StopTime {
+			if each.CurrentInvestment >= each.MinFunding && each.State != project_model.STATE_SUCCESS {
+				each.State = project_model.STATE_SUCCESS
+				each.Save()
+				if each.IsSendBenefit {
+					helpers.SendProjectBenefit(each)
+				}
+
+			}
+			if each.CurrentInvestment < each.MinFunding && each.State != project_model.STATE_FAILED {
+				each.State = project_model.STATE_FAILED
+				each.Save()
+			}
+		}
+
+		each.StateCn = project_model.STATE_CN_TEXT[each.State]
+
+		if each.MinFunding == each.MaxFunding {
+			if each.CurrentInvestment > each.MinFunding {
+				each.CurrentInvestment = each.MinFunding
+			}
+		}
+
+		//totalCopies := project_model.GetInvestWayCopiesByProjectId(each.Id)
+		totalCopiesMin, totalCopiesMax := int64(0), int64(0)
+		if each.MinFunding < each.MaxFunding {
+			totalCopiesMin = int64(float64(each.MinFunding) / float64(each.UnitPrice))
+			totalCopiesMax = int64(float64(each.MaxFunding) / float64(each.UnitPrice))
+		} else {
+			totalCopiesMin = int64(float64(each.MinFunding) / float64(each.UnitPrice))
+			totalCopiesMax = int64(float64(each.MinFunding) / float64(each.UnitPrice))
+		}
+
+		//如果是有超募的,预热和团购中时,显示剩余份数范围
+		joinCopies := project_model.GetInvestSuccessCountByPId(each.Id, false)
+		each.LeftCopiesMin = totalCopiesMin - joinCopies
+		each.LeftCopiesMax = totalCopiesMax - joinCopies
+		if each.LeftCopiesMin < 0 {
+			each.LeftCopiesMin = 0
+		}
+		if each.LeftCopiesMax < 0 {
+			each.LeftCopiesMax = 0
+		}
+
+		if each.IsDetailPics {
+			each.Detail = ""
+		}
+	}
+
+	if list == nil || len(list) == 0 {
+		list = make([]*project_model.Project, 0, 0)
+	}
+
+	type Ret struct {
+		List  []*project_model.Project `json:"list"`
+		Count int64                    `json:"count"`
+	}
+
+	self.Data["json"] = &Ret{List: list, Count: count}
+	self.ServeJSON()
+
+}
+
+//项目详情
+func (self *ProjectController) ProjectDetail() {
+
+	_id := self.Ctx.Input.Param(":project_id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+
+	project := project_model.GetProjectById(id, useCache)
+
+	if project != nil {
+
+		project.StartTime = project.StartAt.Unix()
+		if project.StartTime < 0 {
+			project.StartTime = 0
+		}
+		project.StopTime = project.StopAt.Unix()
+		if project.StopTime < 0 {
+			project.StopTime = 0
+		}
+		if project.SuccessTime < 0 {
+			project.SuccessTime = 0
+		}
+		project.ThumbImg = project_model.GetFullImgUrl(project.ImgThumb)
+		project.DetailImg = project_model.GetFullImgUrl(project.ImgDetail)
+
+		project.CurrentInvestUser = project_model.GetValidTotalCountByPId(project.Id, false)
+		project.CurrentInvestment = project_model.GetValidTotalFundingByPId(project.Id, false)
+		project.MinProjectWayPrice = project_model.GetMinProjeceWayPriceByPId(project.Id, useCache)
+		project.Album = project_model.GetProjectPicturesByPIdAndPType(project.Id, project_model.PIC_TYPE_CYCLE, useCache)
+
+		nowTime := time.Now().Unix()
+		if nowTime >= project.StartTime && nowTime < project.StopTime {
+			if project.State == project_model.STATE_PREPARING {
+				project.State = project_model.STATE_UNDERWAY
+				project.Save()
+			}
+		}
+		if nowTime > project.StopTime {
+			if project.CurrentInvestment >= project.MinFunding && project.State != project_model.STATE_SUCCESS {
+				project.State = project_model.STATE_SUCCESS
+				project.Save()
+				if project.IsSendBenefit {
+					helpers.SendProjectBenefit(project)
+				}
+			}
+			if project.CurrentInvestment < project.MinFunding && project.State != project_model.STATE_FAILED {
+				project.State = project_model.STATE_FAILED
+				project.Save()
+			}
+		}
+
+		project.StateCn = project_model.STATE_CN_TEXT[project.State]
+
+		if project.MinFunding == project.MaxFunding {
+			if project.CurrentInvestment > project.MinFunding {
+				project.CurrentInvestment = project.MinFunding
+			}
+		}
+
+		totalCopiesMin, totalCopiesMax := int64(0), int64(0)
+		if project.MinFunding < project.MaxFunding {
+			totalCopiesMin = int64(float64(project.MinFunding) / float64(project.UnitPrice))
+			totalCopiesMax = int64(float64(project.MaxFunding) / float64(project.UnitPrice))
+		} else {
+			totalCopiesMin = int64(float64(project.MinFunding) / float64(project.UnitPrice))
+			totalCopiesMax = int64(float64(project.MinFunding) / float64(project.UnitPrice))
+		}
+
+		//如果是有超募的,预热和团购中时,显示剩余份数范围
+		joinCopies := project_model.GetInvestSuccessCountByPId(project.Id, false)
+		project.LeftCopiesMin = totalCopiesMin - joinCopies
+		project.LeftCopiesMax = totalCopiesMax - joinCopies
+		if project.LeftCopiesMin < 0 {
+			project.LeftCopiesMin = 0
+		}
+		if project.LeftCopiesMax < 0 {
+			project.LeftCopiesMax = 0
+		}
+
+		if project.IsDetailPics {
+			project.IntroduceAlbum = project_model.GetAscProjectPicturesByPIdAndPType(project.Id, project_model.PIC_TYPE_PROJECT_INTODUCE, useCache, false)
+			project.Detail = ""
+		}
+	}
+
+	self.Data["json"] = project
+	self.ServeJSON()
+}

+ 131 - 0
go/gopath/src/fohow.com/apps/controllers/project_controller/project_join_controller.go

@@ -0,0 +1,131 @@
+package project_controller
+
+import (
+	"github.com/astaxie/beego"
+	"fohow.com/apps"
+	"fohow.com/apps/models/project_model"
+	"fohow.com/apps/models/user_model"
+	"strconv"
+	//"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/granary_model"
+)
+
+//项目投资人列表
+func (self *ProjectController) GetInvestorList() {
+	_id := self.Ctx.Input.Param(":project_id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache", true)
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	type ListWithCnt struct {
+		List      []*project_model.Investor `json:"investor_list"`
+		ListCount int64                     `json:"list_count"`
+	}
+	project := project_model.GetProjectById(id, useCache)
+	if project == nil {
+		self.ReturnError(403, apps.ProjectNoExist, "", nil)
+	}
+	useCache = project.State == project_model.STATE_SUCCESS
+
+	investList := project_model.GetInvestListByPId(id, page, perPage, useCache)
+	for _, item := range investList {
+		usr := user_model.GetUserById(item.UserId, useCache)
+		if usr != nil {
+			if usr.Nickname == "" || usr.Head == "" {
+				wxUsr := user_model.GetWxUserByUserId(item.UserId, useCache)
+				if wxUsr != nil {
+					item.UserName = wxUsr.GetNickNameWithStar()
+					item.Head = user_model.GetFullImgUrl(wxUsr.Head)
+				}
+			} else {
+				item.UserName = usr.GetNickNameWithStar()
+				item.Head = user_model.GetFullImgUrl(usr.Head)
+			}
+		} else {
+			item.Head = user_model.GetFullImgUrl("")
+		}
+	}
+
+	list := new(ListWithCnt)
+	list.List = investList
+	list.ListCount = project_model.GetInvestorCountByPId(id, useCache)
+	// list.InvestorCount = project_join_model.GetInvestorsCountByPId(id, useCache)
+	self.Data["json"] = list
+	self.ServeJSON()
+}
+
+//我的农场/我的投资记录
+func (self *ProjectController) GetInvestmentList() {
+	useCache, _ := self.GetBool("cache", true)
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 20)
+	if perPage <= 0 || perPage > 100 {
+		perPage = 20
+	}
+	userId := self.GetCurrentUserId()
+
+	list := project_model.GetInvestmentsByUId(page, perPage, userId, useCache)
+
+	for _, item := range list {
+		beego.BeeLogger.Debug("JoinItemPerProjectPerWay: %v", item)
+		project := project_model.GetProjectById(item.ProjectId, useCache)
+		if project != nil {
+			item.ProjectName = project.Title
+			item.ProjectThumbImg = self.GetFullImgUrl(project.ImgThumb)
+		}
+		investWay := project_model.GetProjectInvestWayById(item.ProjectInvestWayId, useCache)
+		beego.BeeLogger.Debug("invest-way: %v", investWay)
+		if investWay != nil {
+			item.InvestWayName = investWay.Title
+		}
+		projectBonus := project_model.GetProjectBonusByPIdAndWId(item.ProjectId, item.ProjectInvestWayId, useCache)
+		if projectBonus != nil {
+			//这里不是项目收成的回购结束时间,而是粮仓的创建时间
+			//item.ValidHarvestTime = projectBonus.BuyBackStopAt.Unix()
+			granary := granary_model.GetGranaryByBonusIdAndUId(projectBonus.Id, userId, useCache)
+			if granary != nil {
+				item.IsHarvest = true
+				item.ValidHarvestTime = granary.CreatedAt.Unix()
+				if item.ValidHarvestTime < 0 {
+					item.ValidHarvestTime = 0
+				}
+				item.GranaryId = granary.Id
+			} else {
+				if investWay != nil {
+					item.GuessHarvestTime = investWay.HarvestTime.Unix()
+					if item.GuessHarvestTime < 0 {
+						item.GuessHarvestTime = 0
+					}
+					item.GuessHarvestPrice = investWay.HarvestAmount*(item.SumCount-investWay.SelfUseMin) + investWay.SelfUseMin*investWay.Price
+				}
+			}
+
+		} else {
+			if investWay != nil {
+				item.GuessHarvestTime = investWay.HarvestTime.Unix()
+				if item.GuessHarvestTime < 0 {
+					item.GuessHarvestTime = 0
+				}
+				item.GuessHarvestPrice = investWay.HarvestAmount*(item.SumCount-investWay.SelfUseMin) + investWay.SelfUseMin*investWay.Price
+			}
+		}
+	}
+
+	count := project_model.GetInvestmentsCountByUId(userId, useCache)
+	type Ret struct {
+		List           []*project_model.JoinItemPerProjectPerWay `json:"list"`
+		Count          int64                                     `json:"count"`
+		HelpInvestment int64                                     `json:"help_investment"`
+	}
+
+	if list == nil || len(list) == 0 {
+		list = make([]*project_model.JoinItemPerProjectPerWay, 0, 0)
+	}
+
+	helpInvestment := project_model.GetUserHadSuccessInvest(userId, useCache)
+	self.Data["json"] = &Ret{List: list, Count: count, HelpInvestment: helpInvestment}
+	self.ServeJSON()
+}

+ 237 - 0
go/gopath/src/fohow.com/apps/controllers/rabbit_prize_draw_act_controller/rabbit_prize_draw_act_controller.go

@@ -0,0 +1,237 @@
+package rabbit_prize_draw_act_controller
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/base64"
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/granary_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"
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"regexp"
+	"strconv"
+	"time"
+)
+
+var (
+	//需要校验用户登录的Action
+	exceptCheckUserLoginAction   = []string{"OrderLabiProduct"}
+	exceptCheckWxUserLoginAction = []string{"OrderLabiProduct"}
+)
+
+type RabbitPrizeDrawActController struct {
+	apps.BaseController
+}
+
+func (self *RabbitPrizeDrawActController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+//五五节中奖。填写收货地址时在FOHOW玖玖商城下单。
+func (self *RabbitPrizeDrawActController) OrderLabiProduct() {
+	key := beego.AppConfig.String("RabbitOrderProductKey")
+	// key := []byte("1234567890123456")
+	params := self.GetString("params")
+	ciphertext, _ := base64.StdEncoding.DecodeString(params)
+	block, err := aes.NewCipher([]byte(key))
+	if err != nil {
+		beego.BeeLogger.Error("aes newcipher err:%s", err)
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	// The IV needs to be unique, but not secure. Therefore it's common to
+	// include it at the beginning of the ciphertext.
+	if len(ciphertext) < aes.BlockSize {
+		beego.BeeLogger.Error("ciphertext too short")
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+	iv := ciphertext[:aes.BlockSize]
+	ciphertext = ciphertext[aes.BlockSize:]
+
+	stream := cipher.NewCFBDecrypter(block, iv)
+
+	// XORKeyStream can work in-place if the two arguments are the same.
+	stream.XORKeyStream(ciphertext, ciphertext)
+	// beego.BeeLogger.Warn("plain params:%s", ciphertext)
+	type LabiOrderProductReturn struct {
+		RetCode string `json:"ret_code"`
+		RetMsg  string `json:"ret_msg"`
+		OrderId string `json:"order_id"`
+	}
+
+	regTel := regexp.MustCompile(`tel=([0-9]+)`)
+	matchTel := regTel.FindSubmatch([]byte(ciphertext))
+
+	regRpId := regexp.MustCompile(`rpid=([0-9]+)`)
+	matchcRpId := regRpId.FindSubmatch([]byte(ciphertext))
+
+	regOuId := regexp.MustCompile(`ouid=([0-9]+)`)
+	matchOuId := regOuId.FindSubmatch([]byte(ciphertext))
+
+	regWxuid := regexp.MustCompile(`wxuid=([0-9]+)`)
+	matchWxuid := regWxuid.FindSubmatch([]byte(ciphertext))
+
+	regActid := regexp.MustCompile(`actid=([0-9]+)`)
+	matchActid := regActid.FindSubmatch([]byte(ciphertext))
+
+	regAddr := regexp.MustCompile(`address=([a-z0-9A-Z\p{Han}]+((,|,|-|_| |(|)|\)|\()[a-z0-9A-Z\p{Han}]+)*)`)
+	matchAddr := regAddr.FindSubmatch([]byte(ciphertext))
+
+	regContact := regexp.MustCompile(`contact=([a-z0-9A-Z\p{Han}]+((,|,|-|_| |(|)|\)|\()[a-z0-9A-Z\p{Han}]+)*)`)
+	matchContact := regContact.FindSubmatch([]byte(ciphertext))
+
+	if len(matchTel) < 2 || len(matchcRpId) < 2 || len(matchOuId) < 2 || len(matchAddr) < 2 || len(matchContact) < 2 || len(matchWxuid) < 2 || len(matchActid) < 2 {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0001",
+			RetMsg:  "参数错误",
+			OrderId: "",
+		}, true, true)
+		self.StopRun()
+	}
+	_rpId := fmt.Sprintf("%s", matchcRpId[1])
+	rpId, _ := strconv.ParseInt(_rpId, 10, 64)
+	_ouId := fmt.Sprintf("%s", matchOuId[1])
+	ouId, _ := strconv.ParseInt(_ouId, 10, 64)
+	_wxuid := fmt.Sprintf("%s", matchWxuid[1])
+	wxuid, _ := strconv.ParseInt(_wxuid, 10, 64)
+	_actid := fmt.Sprintf("%s", matchActid[1])
+	actid, _ := strconv.ParseInt(_actid, 10, 64)
+	tel := fmt.Sprintf("%s", matchTel[1])
+	address := fmt.Sprintf("%s", matchAddr[1])
+	contact := fmt.Sprintf("%s", matchContact[1])
+
+	if rpId == 0 || ouId == 0 || wxuid == 0 || actid == 0 {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0001",
+			RetMsg:  "参数错误",
+			OrderId: "",
+		}, true, true)
+		self.StopRun()
+	}
+	beego.BeeLogger.Warn("prize_draw_act===rpId:%d, ouId:%d,wxuid:%d,actid:%d", rpId, ouId, wxuid, actid)
+	orderUser := user_model.GetUserById(ouId, true)
+	if orderUser == nil {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0002",
+			RetMsg:  "下单用户不存在",
+			OrderId: "",
+		}, true, true)
+		self.StopRun()
+	}
+	beego.BeeLogger.Warn("prize_draw_act===orderUser:%d", orderUser.Id)
+
+	labiProduct := product_model.GetProductById(rpId, true)
+	if labiProduct == nil {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0002",
+			RetMsg:  "商品不存在",
+			OrderId: "",
+		}, true, true)
+		self.StopRun()
+	}
+	beego.BeeLogger.Warn("prize_draw_act===labiProduct:%d", labiProduct.Id)
+
+	remark := fmt.Sprintf("wuwu_draw_%d_%d", actid, wxuid)
+	existOrder := order_model.GetOrderByOrderRemark(remark, true)
+	if existOrder != nil {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0002",
+			RetMsg:  "不能重复下单",
+			OrderId: "",
+		}, true, true)
+		self.StopRun()
+	}
+
+	wxUserId := int64(0)
+	wxUser := user_model.GetWxUserByUserId(orderUser.Id, true)
+	if wxUser != nil {
+		wxUserId = wxUser.Id
+	}
+
+	order := new(order_model.Order).Create(wxUserId, orderUser.Id, labiProduct.Id, 1,
+		labiProduct.RoboBalancePrice*1, labiProduct.BuyPrice*1, labiProduct.Price, labiProduct.RoboBalancePrice, order_model.SOURCE_D5C_SYS)
+
+	beego.BeeLogger.Warn("prize_draw_act===order:%d", order.Id)
+
+	if order == nil {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0003",
+			RetMsg:  "下单失败",
+			OrderId: "",
+		}, true, true)
+		self.StopRun()
+	}
+
+	if labiProduct.Ptype == product_model.TYPE_USER_SALE {
+		order.OrderType = order_model.ORDER_TYPE_SALE
+	}
+
+	//判断库存
+	leftCount := int64(0)
+	//普通订单
+	if order.OrderType == order_model.ORDER_TYPE_NORMAL {
+		soldCount := order_model.GetSoldCountByPId(labiProduct.Id, false)
+		leftCount = labiProduct.Count - soldCount
+	} else {
+		leftCount = granary_model.GetSaleOrderLeftCountByPId(labiProduct.Id)
+	}
+	//商品已经卖完
+	if leftCount < order.Count {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0003",
+			RetMsg:  "下单失败",
+			OrderId: "",
+		}, true, true)
+		self.StopRun()
+	}
+
+	order.Status = order_model.STATUS_PROCESSING
+	order.PaiedAt = time.Now().Unix()
+	order.PaiedPrice = order.TotalPrice
+	order.PayWay = pay_model.PAYWAY_BALANCE
+	order.OrderRemark = remark
+	order.Address = address
+	order.Contact = contact
+	order.Tel = tel
+
+	if !order.Save() {
+		self.Ctx.Output.SetStatus(403)
+		self.Ctx.Output.JSON(&LabiOrderProductReturn{
+			RetCode: "0003",
+			RetMsg:  "下单失败",
+			OrderId: "",
+		}, true, true)
+		self.StopRun()
+	}
+	beego.BeeLogger.Warn("prize_draw_act===order_type:%d", order.OrderType)
+
+	//TODO: 暂不考虑代销商品的库存量
+	//代销订单,处理代销逻辑
+	if order.OrderType == order_model.ORDER_TYPE_SALE {
+		helpers.SaleOrderHandler(order)
+	}
+
+	ok := &LabiOrderProductReturn{
+		RetCode: "0000",
+		RetMsg:  "下单成功",
+		OrderId: order.OrderId,
+	}
+	self.Ctx.Output.SetStatus(200)
+	self.Ctx.Output.JSON(ok, true, true)
+	self.StopRun()
+}

+ 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()
+}

+ 202 - 0
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/dollar_win_controller.go

@@ -0,0 +1,202 @@
+package railsadmin_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/dollar_win_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/wx_mp"
+	"strconv"
+	"time"
+	//"fohow.com/apps/models/d5c_project"
+)
+
+//一元夺宝没中奖的退款,退款余额账户里面
+func (self *RailsadminController) DollarWinRefund() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	perPage, _ := self.GetInt64("per_page", 50)
+
+	act := dollar_win_model.GetDollarWinZtConfigById(id, false)
+	if act == nil {
+		self.ReturnError(403, []string{"actNotExist", "活动不存在"}, "", nil)
+	}
+	if act.StartTime.Unix() < time.Now().Unix() && time.Now().Unix() < act.StopTime.Unix() {
+		self.ReturnError(403, []string{"actIsGoing", "活动仍在进行中"}, "", nil)
+	}
+
+	joinCount := dollar_win_model.GetJoinPaiedListCountByZtConfigId(act.Id, false)
+	pageIndex := int64(1)
+
+	page := (joinCount / perPage) + 1
+
+	/*	beego.BeeLogger.Warn("railsadmin_controller.DollarWinRefund().perPage(%d)", perPage)
+		beego.BeeLogger.Warn("railsadmin_controller.DollarWinRefund().joinCount(%d)", joinCount)*/
+
+	for i := int64(0); i < page; i++ {
+		beego.BeeLogger.Warn("railsadmin_controller.DollarWinRefund().i(%d)", i)
+		list := dollar_win_model.GetJoinPaiedListByZtConfigId(act.Id, (pageIndex + i), perPage, false)
+		for _, item := range list {
+			if !item.IsWin && !item.IsRefund {
+				//	非中奖者将退款到账户余额
+				refundCash := balance_model.GetCashBalanceBySourceAndRId(balance_model.CASH_SOURCE_DOLLAR_WIN_RETURN, item.OrderId)
+				if refundCash == nil {
+					remark := fmt.Sprintf("%s", act.Title)
+					wxUser := user_model.GetWxUserByUserId(item.UserId, false)
+					if wxUser == nil {
+						continue
+					}
+					refundCash = new(balance_model.CashBalance).Create(wxUser.Id, item.TotalAmount,
+						balance_model.CASH_SOURCE_DOLLAR_WIN_RETURN, item.OrderId, remark)
+				}
+				if refundCash != nil {
+					item.IsRefund = true
+					item.Save()
+					go self.DollarRefundUserNotice(item.UserId, act.Id, act.JoinCostAmount, act.Title, act.Prize)
+				}
+			}
+		}
+	}
+	self.ServeJSON()
+}
+
+//一元夺宝开奖 购买时间20060102150405之和除以人次,取余数 + 1
+func (self *RailsadminController) DollarWinOpen() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+
+	act := dollar_win_model.GetDollarWinZtConfigById(id, false)
+	if act == nil {
+		self.ReturnError(403, []string{"actNotExist", "活动不存在"}, "", nil)
+	}
+	if act.StartTime.Unix() < time.Now().Unix() && time.Now().Unix() < act.StopTime.Unix() {
+		self.ReturnError(403, []string{"actIsGoing", "活动仍在进行中"}, "", nil)
+	}
+
+	joinCount := dollar_win_model.GetJoinPaiedListCountByZtConfigId(act.Id, false)
+	beego.BeeLogger.Warn("joinCount, joinCount=[%d]", joinCount)
+	if joinCount == 0 {
+		self.ReturnError(403, apps.Error, "", nil)
+	}
+
+	if joinCount < act.TotalTimesMinLimit {
+		self.ReturnError(403, []string{"lessThanJoinTimesLimit", "活动参与人次不到开奖最低参与人次"}, "", nil)
+	}
+	//若项目未融资成功,则提示项目未成功,开奖失败
+
+	/*	D5cProjectInfo := d5c_project_model.GetD5cProjectById(act.ProjectId)
+		if D5cProjectInfo==nil{
+			//self.ReturnError(403, []string{"projectIsNull","该活动没有关联参与项目"}, "", nil)
+		}
+		if D5cProjectInfo.State != d5c_project_model.SUCESS_STATE{
+			//self.ReturnError(403, []string{"projectIsNotSucess","该活动关联项目没有融资成功"}, "", nil)
+		}*/
+	z := dollar_win_model.GetJoinPaiedZSumByZtConfigId(act.Id, false)
+	winRank := (z % joinCount) + 1
+	beego.BeeLogger.Warn("winRank, winRank=[%d]", winRank)
+	joinOrder := dollar_win_model.GetJoinPaiedByZtConfigIdAndRankNum(act.Id, winRank, false)
+	if joinOrder == nil {
+		self.ReturnError(403, apps.Error, "", nil)
+	}
+	joinOrder.IsWin = true
+	joinOrder.AwardRank = int64(1)
+	joinOrder.Save()
+	//若中奖,则自动推送中奖信息
+	self.DollarWinUserNotice(joinOrder.UserId, joinOrder.AwardRank, act.Id, time.Now().Unix(), act.Title, act.Prize)
+	//计算二等奖获得者,并通知
+	second := self.GetOtherWinNumber(winRank, joinCount, int64(5))
+	secondJoinOrder := dollar_win_model.GetJoinPaiedByZtConfigIdAndRankNum(act.Id, second, false)
+	if secondJoinOrder != nil {
+		secondJoinOrder.IsWin = true
+		secondJoinOrder.AwardRank = int64(2)
+		secondJoinOrder.Save()
+		go self.DollarWinUserNotice(secondJoinOrder.UserId, secondJoinOrder.AwardRank, act.Id, time.Now().Unix(), act.Title, act.Prize)
+	}
+
+	third := self.GetOtherWinNumber(winRank, joinCount, int64(10))
+	thirdJoinOrder := dollar_win_model.GetJoinPaiedByZtConfigIdAndRankNum(act.Id, third, false)
+	if thirdJoinOrder != nil {
+		thirdJoinOrder.IsWin = true
+		thirdJoinOrder.AwardRank = int64(3)
+		thirdJoinOrder.Save()
+		self.DollarWinUserNotice(thirdJoinOrder.UserId, thirdJoinOrder.AwardRank, act.Id, time.Now().Unix(), act.Title, act.Prize)
+	}
+	act.ResultText = fmt.Sprintf("%d%s%d 取余数 + 1 = %d", z, "/", joinCount, winRank)
+	act.Save()
+	self.ServeJSON()
+}
+
+//一元夺宝开奖通知
+func (self *RailsadminController) DollarWinUserNotice(uId, awardRank, ztConfigId, winTime int64, ztConfigTitle, ztConfigPrize string) {
+	user := user_model.GetWxUserByUserId(uId, false)
+	beego.BeeLogger.Warn("DollarWinNoticeTemplate, act.Prize=[%s]uId=[%d]", ztConfigPrize, uId)
+	if user != nil {
+		wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(user.Id, beego.AppConfig.String("WxMPAppId"), false)
+		if wxUserGzh != nil && wxUserGzh.GzhOpenId != "" {
+			var url string
+			if beego.BConfig.RunMode == beego.DEV {
+				url = fmt.Sprintf("http://testm.labitumall.com/activity/oneinvest/%d", ztConfigId)
+			} else {
+				url = fmt.Sprintf("https://m.labitumall.com/activity/oneinvest/%d", ztConfigId)
+			}
+			var str string
+			if awardRank == int64(1) {
+				str = "喜报,恭喜您活动获得一等奖"
+			} else if awardRank == int64(2) {
+				str = "喜报,恭喜您活动获得二等奖"
+			} else {
+				str = "喜报,恭喜您活动获得三等奖"
+			}
+			beego.BeeLogger.Warn("DollarWinNotice, url=[%s]", url)
+			first := str
+			kw1 := ztConfigTitle
+			kw2 := ztConfigPrize
+			kw3 := time.Unix(winTime, 0).Format("2006-01-02 15:04:05")
+			remark := "您已幸运获得本次奖励,点击查看"
+			go wx_mp.TmplmsgEventDollarWinNotifyHandle(wxUserGzh.GzhOpenId, url, first, kw1, kw2, kw3, remark)
+		}
+	}
+}
+
+//一元夺宝开奖通知
+func (self *RailsadminController) GetOtherWinNumber(winNumber, count, calcNums int64) int64 {
+
+	var winner int64 = 0
+	//计算其它得奖人数
+	if winNumber+calcNums <= count {
+		winner = winNumber + calcNums
+	} else {
+		if calcNums > count {
+			winner = winNumber + calcNums%count - count
+		} else {
+			winner = winNumber + calcNums - count
+		}
+	}
+	return winner
+}
+
+//一元活动开奖退款通知
+func (self *RailsadminController) DollarRefundUserNotice(uId, ztConfigId, ztConfigCost int64, ztConfigTitle, ztConfigPrize string) {
+	user := user_model.GetWxUserByUserId(uId, false)
+	beego.BeeLogger.Warn("DollarRefundUserNotice, act.Prize=[%s]uId=[%d]", ztConfigPrize, uId)
+	if user != nil {
+		wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(user.Id, beego.AppConfig.String("WxMPAppId"), false)
+		if wxUserGzh != nil && wxUserGzh.GzhOpenId != "" {
+			var url string
+			if beego.BConfig.RunMode == beego.DEV {
+				url = fmt.Sprintf("http://testm.labitumall.com/activity/oneinvest/%d", ztConfigId)
+			} else {
+				url = fmt.Sprintf("https://m.labitumall.com/activity/oneinvest/%d", ztConfigId)
+			}
+			beego.BeeLogger.Warn("DollarRefundUserNotice, url=[%s]", url)
+			first := "很遗憾,您参与的活动未中奖"
+			kw1 := fmt.Sprintf("<<%s>>退款", ztConfigTitle)
+			kw2 := fmt.Sprintf("%d元", int64(ztConfigCost/100))
+			remark := "点击参加最新一期"
+			beego.BeeLogger.Warn("DollarRefundUserNotice, remark=[%s]", remark)
+			go wx_mp.TmplmsgEventDollarRefundNotifyHandle(wxUserGzh.GzhOpenId, url, first, kw1, kw2, remark)
+		}
+	}
+}

+ 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()
+}

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

@@ -0,0 +1,34 @@
+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.ProductBenefitRate = 15
+	wxUser.SecondProductBenefitRate = 8
+	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()
+}

+ 16 - 0
go/gopath/src/fohow.com/apps/controllers/railsadmin_controller/send_controller.go

@@ -0,0 +1,16 @@
+package railsadmin_controller
+
+import (
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/project_model"
+	"strconv"
+)
+
+//发放成团项目的佣金
+func (self *RailsadminController) SendProjectBenefit() {
+	_pId := self.Ctx.Input.Param(":id")
+	pId, _ := strconv.ParseInt(_pId, 10, 64)
+	project := project_model.GetProjectById(pId, false)
+	helpers.SendProjectBenefit(project)
+	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")
+}

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

@@ -0,0 +1,431 @@
+package railsadmin_controller
+
+import (
+	"fmt"
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/granary_model"
+	"fohow.com/apps/models/product_model"
+	"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"
+	"github.com/astaxie/beego"
+	"strconv"
+	"strings"
+	"time"
+)
+
+//销售专题开奖发送消息
+func (self *RailsadminController) SendOpenPrizeTmpl() {
+
+	id, _ := strconv.ParseInt(self.Ctx.Input.Param(":id"), 10, 64)
+
+	saleSubject := subject_model.GetProductSaleSubjectById(id, false)
+
+	awardedList := subject_model.GetSaleDrawCodeIsAwardedList(id, false)
+
+	beego.BeeLogger.Warn("SendOpenPrizeTmpl:%d", saleSubject.Id)
+	if saleSubject == nil || !saleSubject.IsPrizeAct || time.Now().Unix() < saleSubject.OpenPrizeTime.Unix() || saleSubject.PrizeNumber == "" || len(awardedList) == 0 {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	joinList := subject_model.GetSaleDrawCodeAllBySubjectId(id, false)
+
+	awardedcount := subject_model.GetSaleDrawCodeIsAdwardedCount(id, false)
+
+	for _, join := range joinList {
+		wxUserGzh := user_model.GetWxUserGzhByWxUIdAndAppId(join.WxUserId, beego.AppConfig.String("WxMPAppId"), true)
+		url := fmt.Sprintf("%s/activity/project/%d", beego.AppConfig.String("WxHost"), id)
+		title := "开奖啦!再接再厉~ 你与大奖就差一根头发的距离!"
+		remark := "备注:别灰心~ 敬请期待下一期。"
+		for _, awarded := range awardedList {
+			if awarded.WxUserId == join.WxUserId {
+				title = fmt.Sprintf("恭喜你!你被大奖%s砸到了!", saleSubject.PrizeName)
+				remark = "备注:奖品将在5个工作日内寄出。请登录小程序查询物流状态,谢谢!"
+				break
+			}
+		}
+		name := fmt.Sprintf("第%d期", saleSubject.Id)
+		openTime := saleSubject.OpenPrizeTime.Format("2006-01-02 15:04:05")
+
+		var codeList []string
+		codes := subject_model.GetSaleDrawCodesBySubjectIdAndWxUid(id, join.WxUserId, true)
+		for _, code := range codes {
+			codeList = append(codeList, tool.GetAssignLengthStr(code.Code, 6))
+		}
+		wx_mp.TmplmsgOpenPrize(wxUserGzh.GzhOpenId, url, title, name, openTime, fmt.Sprintf("%d", awardedcount), strings.Join(codeList, ","), saleSubject.PrizeNumber, remark)
+	}
+
+	// self.Data["json"] = pcms
+	self.ServeJSON()
+}
+
+// 推送模板消息给指定的用户
+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
+}
+
+// 粮仓单独推送自用消息,先小程序通知,失败则公众号消息
+func (self *RailsadminController) NonAutoNoticeSelfUseByGranaryId() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache")
+	granary := granary_model.GetGranaryById(id, useCache)
+	wxUser := user_model.GetWxUserByUserId(granary.UserId, useCache)
+	if wxUser == nil {
+		self.ServeJSON()
+		self.StopRun()
+	}
+	var tmpl *push_tmpl_model.XcxPushTmpl
+	if beego.BConfig.RunMode == beego.DEV {
+		tmpl = push_tmpl_model.GetXcxPushTmplById(3)
+	} else {
+		tmpl = push_tmpl_model.GetXcxPushTmplById(5)
+	}
+	if tmpl == nil {
+		self.ServeJSON()
+		self.StopRun()
+	}
+	product := product_model.GetProductById(granary.ProductId, useCache)
+	if product == nil {
+		self.ServeJSON()
+		self.StopRun()
+	}
+	//粮仓剩余自用份数
+	zyCount := granary.SelfUseMin - granary.SelfUseCount
+	if zyCount <= 0 {
+		self.ServeJSON()
+		self.StopRun()
+	}
+
+	var remark string
+	kw2 := fmt.Sprintf("自用")
+	page := fmt.Sprintf("pages/start/start?url=packageUser/pages/user/granary/granary")
+	emphasisKw := tmpl.EmphasisKw
+	if tmpl.EmphasisKw == "-" {
+		emphasisKw = ""
+	}
+	if tmpl.PushForce == 1 {
+		remark = tmpl.Keyword3
+	} else {
+		remark = fmt.Sprintf("您还有%d份商品未提货自用,请在规定时间内填写地址邮寄自用。", zyCount)
+	}
+	isSuccess := helpers.DeliveryNotify(*wxUser, product.Name, kw2, remark, emphasisKw, page)
+	if !isSuccess {
+		gzhTmpl := push_tmpl_model.GetPushTmplById(5)
+		if gzhTmpl == nil {
+			self.ServeJSON()
+			self.StopRun()
+		}
+		if gzhTmpl.PushForce == 1 {
+			remark = gzhTmpl.Remark
+		} else {
+			remark = fmt.Sprintf("请在规定时间内填写地址邮寄自用,点击前往。")
+		}
+		url := fmt.Sprintf("https://api.labitumall.com/v1/cfc/43")
+		first := fmt.Sprintf("提货方式:自用")
+		kw1 := fmt.Sprintf("FOHOW玖玖")
+		kw2 := product.Name
+		kw3 := fmt.Sprintf("您还有%d份未提货自用", zyCount)
+		kw4 := fmt.Sprintf("共收成%d份", granary.TotalCount)
+		kw5 := fmt.Sprintf("从%s开始", granary.CreatedAt.Format("2006年01月02日"))
+		uGzh := user_model.GetWxUserGzhByWxUIdAndAppId(wxUser.Id, beego.AppConfig.String("WxMPAppId"), false)
+		err := wx_mp.TmplmsgEventPickUpNotifyHandle(uGzh.GzhOpenId, url, first, kw1, kw2, kw3, kw4, kw5, remark)
+		if err == nil {
+			granary.LastZyRemindAt = time.Now().Unix()
+			granary.Save()
+		}
+	} else {
+		granary.LastZyRemindAt = time.Now().Unix()
+		granary.Save()
+	}
+
+	self.ServeJSON()
+}
+
+// 粮仓单独推送挂单消息,先小程序通知,失败则公众号消息
+func (self *RailsadminController) NonAutoNoticeApplyForSaleByGranaryId() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	useCache, _ := self.GetBool("cache")
+	granary := granary_model.GetGranaryById(id, useCache)
+	wxUser := user_model.GetWxUserByUserId(granary.UserId, useCache)
+	if wxUser == nil {
+		self.ServeJSON()
+		self.StopRun()
+	}
+	var tmpl *push_tmpl_model.XcxPushTmpl
+	if beego.BConfig.RunMode == beego.DEV {
+		tmpl = push_tmpl_model.GetXcxPushTmplById(4)
+	} else {
+		tmpl = push_tmpl_model.GetXcxPushTmplById(6)
+	}
+	if tmpl == nil {
+		self.ServeJSON()
+		self.StopRun()
+	}
+	product := product_model.GetProductById(granary.ProductId, useCache)
+	if product == nil {
+		self.ServeJSON()
+		self.StopRun()
+	}
+	selfUse := int64(0)
+	if granary.SelfUseCount > granary.SelfUseMin {
+		selfUse = granary.SelfUseCount
+	} else {
+		selfUse = granary.SelfUseMin
+	}
+	//粮仓剩余可挂单份数
+	gdCount := granary.TotalCount - selfUse - granary.BuybackCount
+	saleOrder := granary_model.GetSaleOrderByGId(id, false)
+	//如果有代销订单,减除代销订单的数量
+	if saleOrder != nil {
+		if saleOrder.State == granary_model.ORDER_STATE_ONLINE ||
+			saleOrder.State == granary_model.ORDER_STATE_SELL_UP {
+			gdCount = gdCount - saleOrder.TotalCount
+		} else {
+			gdCount = gdCount - granary.SoldCount
+		}
+	}
+	if gdCount <= 0 {
+		self.ServeJSON()
+		self.StopRun()
+	}
+	var remark string
+	kw2 := fmt.Sprintf("挂单代销")
+	page := fmt.Sprintf("pages/start/start?url=packageUser/pages/user/granary/granary")
+	emphasisKw := tmpl.EmphasisKw
+	if tmpl.EmphasisKw == "-" {
+		emphasisKw = ""
+	}
+	if tmpl.PushForce == 1 {
+		remark = tmpl.Keyword3
+	} else {
+		remark = fmt.Sprintf("您还有%d份商品未挂单代销,请及时到会员中心-我的粮仓配置挂单数量,平台才会帮您进行代销售。", gdCount)
+	}
+	isSuccess := helpers.DeliveryNotify(*wxUser, product.Name, kw2, remark, emphasisKw, page)
+	if !isSuccess {
+		gzhTmpl := push_tmpl_model.GetPushTmplById(6)
+		if gzhTmpl == nil {
+			self.ServeJSON()
+			self.StopRun()
+		}
+		if gzhTmpl.PushForce == 1 {
+			remark = gzhTmpl.Remark
+		} else {
+			remark = fmt.Sprintf("请及时到会员中心-我的粮仓配置挂单数量,平台才会帮您进行代销售。")
+		}
+		url := fmt.Sprintf("https://api.labitumall.com/v1/cfc/43")
+		first := fmt.Sprintf("提货方式:挂单代销")
+		kw1 := fmt.Sprintf("FOHOW玖玖")
+		kw2 := product.Name
+		kw3 := fmt.Sprintf("您还有%d份未挂单代销", gdCount)
+		kw4 := fmt.Sprintf("共收成%d份", granary.TotalCount)
+		kw5 := fmt.Sprintf("从%s开始", granary.CreatedAt.Format("2006年01月02日"))
+		uGzh := user_model.GetWxUserGzhByWxUIdAndAppId(wxUser.Id, beego.AppConfig.String("WxMPAppId"), false)
+		err := wx_mp.TmplmsgEventPickUpNotifyHandle(uGzh.GzhOpenId, url, first, kw1, kw2, kw3, kw4, kw5, remark)
+		if err == nil {
+			granary.LastGdRemindAt = time.Now().Unix()
+			granary.Save()
+		}
+	} else {
+		granary.LastGdRemindAt = time.Now().Unix()
+		granary.Save()
+	}
+
+	self.ServeJSON()
+}

+ 151 - 0
go/gopath/src/fohow.com/apps/controllers/refund_controller/refund_controller.go

@@ -0,0 +1,151 @@
+package refund_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/exchange_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"
+	"sync"
+)
+
+type RefundController struct {
+	apps.BaseController
+}
+
+var refundMutex sync.Mutex
+
+//商家退款
+func (self *RefundController) Refund() {
+
+	oId := self.Ctx.Input.Param(":order_id")
+	useCache, _ := self.GetBool("cache", false)
+	//校验商家:是不是商家?是不是订单的商品的商家?
+	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)
+	}
+
+	defer refundMutex.Unlock()
+	refundMutex.Lock()
+
+	order := order_model.GetOrderById(oId)
+	if order == nil {
+		self.ReturnError(403, apps.OrderNotExist, "", nil)
+	}
+
+	product := product_model.GetProductById(order.ProductId, useCache)
+	if product == nil {
+		self.ReturnError(403, apps.ProductNotExist, "", nil)
+	}
+
+	if product.Ptype == product_model.TYPE_USER_SALE {
+		self.ReturnError(403, apps.UserSaleProductNotSupportRefund, "", nil)
+	}
+
+	if product.MerchantId != merchantUser.MerchantId {
+		self.ReturnError(403, apps.OrderNotBelongToCurrentUser, "", nil)
+	}
+
+	//是否重复退款?
+	if order.Status == order_model.STATUS_REFUNDED {
+		self.ReturnError(403, apps.OrderAlreadyRefund, "", nil)
+	}
+
+	//只允许给处理中、已发货的订单退款
+	if order.Status != order_model.STATUS_DISPATCH && order.Status != order_model.STATUS_PROCESSING {
+		self.ReturnError(403, apps.OrderStatusNotSuit, "", nil)
+	}
+
+	//抽奖或者筹中筹的订单不能给商家操作数据
+	if order.Source == order_model.SOURCE_D5C_SYS {
+		self.ReturnError(403, []string{"SysOrderNoSupportRefund", "系统下单不支持退款,请联系客服"}, "", nil)
+	}
+
+	//  退款-支持退回代金券和退回余额和退回代金券
+	switch order.PayWay {
+	case order_model.PAY_WAY_BALANCE:
+		//代金券支付需要有代金券支付记录
+		buyBalance := balance_model.GetBuyBalanceByRelateId(oId, useCache)
+		if buyBalance == nil {
+			self.ReturnError(403, apps.BalanceNotExist, "", nil)
+		}
+
+		//订单是否已经退款
+		refundBalance := balance_model.GetRefundBalanceByRelateId(oId, useCache)
+		if refundBalance != nil {
+			self.ReturnError(403, apps.OrderAlreadyRefund, "", nil)
+		}
+
+		refundBalance = new(balance_model.Balance).Create(order.WxUserId, order.UserId, order.TotalPrice,
+			balance_model.BALANCE_SOURCE_ALL_REFUNDED, order.OrderId, fmt.Sprintf("商品退款《%s》", product.Name))
+
+		if refundBalance == nil {
+			self.ReturnError(403, apps.OrderRefundFaild, "", nil)
+		}
+		//  更换订单状态
+		order.Status = order_model.STATUS_REFUNDED
+		order.Save()
+
+	case order_model.PAY_WAY_WEIXIN:
+
+		refundCashBalance := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(order.WxUserId, order.OrderId, balance_model.CASH_SOURCE_PRODUCT_REFUND)
+		if refundCashBalance != nil {
+			self.ReturnError(403, apps.OrderAlreadyRefund, "", nil)
+		}
+
+		refundCashBalance = new(balance_model.CashBalance).Create(order.WxUserId, order.TotalPrice,
+			balance_model.CASH_SOURCE_PRODUCT_REFUND, order.OrderId, fmt.Sprintf("商品退款《%s》", product.Name))
+
+		if refundCashBalance == nil {
+			self.ReturnError(403, apps.OrderRefundFaild, "", nil)
+		}
+		order.Status = order_model.STATUS_REFUNDED
+		order.Save()
+
+	case order_model.PAY_WAY_INTEGRAl:
+
+		platform := exchange_model.GetPlatformByTag(exchange_model.DEFAULT_PLATFORM_TAG, false)
+		if platform == nil {
+			self.ReturnError(403, apps.Error, "", nil)
+		}
+
+		buyerUser := user_model.GetUserById(order.UserId, false)
+		if buyerUser == nil {
+			self.ReturnError(403, apps.Error, "", nil)
+		}
+		resultReturn := helpers.ReturnPlatformMallBalance(platform.Secret, order.TradeNo, helpers.RETURN_URL, buyerUser.Tel, order.TotalPrice)
+
+		if resultReturn == nil || resultReturn.RetCode != exchange_model.PL_RET_CODE_SUCCESS {
+			beego.BeeLogger.Error("refund_contoller.ReturnPlatformMallBalance().Platform return failed, platform id:%d, retMsg:%s", platform.Id, resultReturn.RetMsg)
+			self.ReturnError(403, apps.PlatformReturnFailed, "", nil)
+		}
+
+		order.Status = order_model.STATUS_REFUNDED
+		order.Save()
+
+	default:
+		self.ReturnError(403, apps.Error, "", nil)
+	}
+
+	type Ret struct {
+		OrderId string `json:"order_id"`
+	}
+	self.Data["json"] = &Ret{OrderId: order.OrderId}
+	self.ServeJSON()
+
+}

+ 298 - 0
go/gopath/src/fohow.com/apps/controllers/reserve_act_controller/reserve_act_controller.go

@@ -0,0 +1,298 @@
+package reserve_act_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+	"fohow.com/apps/models/balance_model"
+	"fohow.com/apps/models/reserve_act_model"
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/tool"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+)
+
+var (
+	//以下Action无需登录校验,exceptCheckUserLoginAction = []string{"*"} *代表全部不需要
+	exceptCheckUserLoginAction   = []string{"GetAct", "GetJoiners", "GetCur", "ReturnAmount"}
+	exceptCheckWxUserLoginAction = []string{"GetAct", "GetJoiners", "GetCur", "ReturnAmount"}
+)
+
+type ReserveActController struct {
+	apps.BaseController
+}
+
+var orderLock sync.Mutex
+
+func (self *ReserveActController) Init(ctx *context.Context, controllerName, actionName string, app interface{}) {
+	self.BaseController.Init(ctx, controllerName, actionName, app)
+	self.ExceptCheckUserLoginAction = exceptCheckUserLoginAction
+	self.ExceptCheckWxUserLoginAction = exceptCheckWxUserLoginAction
+}
+
+//获取活动信息
+func (self *ReserveActController) GetAct() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+
+	cache, _ := self.GetBool("cache")
+
+	uId := self.GetCurrentUserId()
+
+	act := reserve_act_model.GetReserveActById(id, cache)
+
+	var reserveOrder *reserve_act_model.ReserveActOrder
+	if act != nil {
+
+		now := time.Now()
+		//自动开始
+		if act.StartAt.Before(now) && act.StopAt.After(now) {
+			if act.State != reserve_act_model.STATE_UNDERWAY {
+				act.State = reserve_act_model.STATE_UNDERWAY
+				act.Save()
+			}
+		}
+
+		//自动结束
+		if act.StopAt.Before(now) {
+			if act.State != reserve_act_model.STATE_FINISHED {
+				act.State = reserve_act_model.STATE_FINISHED
+				act.Save()
+			}
+		}
+
+		act.StartTime = act.StartAt.Unix()
+		act.StopTime = act.StopAt.Unix()
+		act.ShareImg = self.GetFullImgUrl(act.ShareImg)
+		act.GuideImg = self.GetFullImgUrl(act.GuideImg)
+		act.StateCn = reserve_act_model.STATE_CN_TEXT[act.State]
+		reserveOrder = reserve_act_model.GetValidReserveActOrderByUIdAndAId(uId, act.Id, cache)
+	}
+
+	if reserveOrder != nil && act != nil {
+		act.IsJoined = true
+	}
+
+	type Ret struct {
+		Act *reserve_act_model.ReserveAct `json:"act"`
+	}
+
+	self.Data["json"] = &Ret{Act: act}
+	self.ServeJSON()
+}
+
+//下单
+func (self *ReserveActController) Order() {
+
+	//TODO:手机号码和验证码校验
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+
+	cache, _ := self.GetBool("cache")
+
+	orderLock.Lock()
+	defer orderLock.Unlock()
+
+	wxUser := self.GetCurrentWxUser(false)
+	if wxUser == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	wxUId := wxUser.Id
+	user := user_model.GetUserById(wxUser.UserId, false)
+	if user == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+	uId := user.Id
+
+	act := reserve_act_model.GetReserveActById(id, cache)
+	if act == nil {
+		self.ReturnError(403, []string{"actNotExist", "活动不存在"}, "", nil)
+	}
+
+	if act.State != reserve_act_model.STATE_UNDERWAY {
+		self.ReturnError(403, []string{"actNotExist", "活动已结束"}, "", nil)
+	}
+
+	reserveOrder := reserve_act_model.GetValidReserveActOrderByUIdAndAId(uId, act.Id, false)
+
+	if reserveOrder != nil {
+		self.ReturnError(403, []string{"alreadyJoined", "已经参加了"}, "", nil)
+	}
+
+	totalprice := act.JoinAmount
+	paiedPrice := act.JoinAmount
+	var remark string
+	order := new(reserve_act_model.ReserveActOrder).Create(act.Id, totalprice, paiedPrice, uId, wxUId, remark)
+
+	if order == nil {
+		self.ReturnError(403, []string{"orderFailed", "下单失败"}, "", nil)
+	}
+
+	type Ret struct {
+		OrderId string `json:"order_id"`
+	}
+
+	self.Data["json"] = &Ret{OrderId: order.OrderId}
+	self.ServeJSON()
+}
+
+//查看前*名参与者
+func (self *ReserveActController) GetJoiners() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+
+	page, _ := self.GetInt64("page", 1)
+	perPage, _ := self.GetInt64("per_page", 100)
+	if page <= 0 {
+		page = 1
+	}
+	if perPage <= 0 || perPage > 150 {
+		perPage = 100
+	}
+	cache, _ := self.GetBool("cache", true)
+
+	act := reserve_act_model.GetReserveActById(id, cache)
+	if act == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	if act.State != reserve_act_model.STATE_FINISHED {
+		self.ReturnError(403, []string{"actIsUnderway", "活动尚未结束"}, "", nil)
+	}
+
+	orders := reserve_act_model.GetList(act.Id, page, perPage, cache)
+	count := reserve_act_model.GetListCount(act.Id, cache)
+
+	for i := int64(0); i < int64(len(orders)); i++ {
+
+		//orders[i].PaiedAt = int64(orders[i].PaiedAt / 1e9)
+
+		wxUser := user_model.GetWxUserById(orders[i].WxUserId, true)
+
+		if wxUser != nil {
+			orders[i].WxNickname = wxUser.Nickname
+			orders[i].WxHead = self.GetFullImgUrl(wxUser.Head)
+		}
+		orders[i].Rank = tool.GetAssignLengthStr(((page-1)*perPage + i + 1), 3)
+		orders[i].OrderId = ""
+	}
+
+	type Ret struct {
+		List      []*reserve_act_model.ReserveActOrder `json:"list"`
+		ListCount int64                                `json:"list_count"`
+	}
+
+	self.Data["json"] = &Ret{List: orders, ListCount: count}
+	self.ServeJSON()
+}
+
+//查看当前用户的参与记录
+func (self *ReserveActController) GetCur() {
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	cache, _ := self.GetBool("cache", true)
+
+	act := reserve_act_model.GetReserveActById(id, cache)
+	if act == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	if act.State != reserve_act_model.STATE_FINISHED {
+		self.ReturnError(403, []string{"", "活动尚未结束"}, "", nil)
+	}
+
+	uId := self.GetCurrentUserId()
+
+	order := reserve_act_model.GetValidReserveActOrderByUIdAndAId(uId, act.Id, cache)
+
+	if order != nil {
+
+		//order.PaiedAt = int64(order.PaiedAt / 1e9)
+
+		wxUser := user_model.GetWxUserById(order.WxUserId, true)
+
+		if wxUser != nil {
+			order.WxNickname = wxUser.Nickname
+			order.WxHead = self.GetFullImgUrl(wxUser.Head)
+		}
+
+		orders := reserve_act_model.GetList(act.Id, 1, act.ReturnCount, cache)
+
+		for i := int64(0); i < int64(len(orders)); i++ {
+			if orders[i].UserId == order.UserId {
+				order.Rank = tool.GetAssignLengthStr((i + 1), 3)
+			}
+		}
+
+		if strings.TrimSpace(order.Rank) == "" {
+			order.Rank = "未入围"
+		}
+
+		order.OrderId = ""
+	}
+	type Ret struct {
+		SelfOrder *reserve_act_model.ReserveActOrder `json:"self_order"`
+	}
+
+	self.Data["json"] = &Ret{SelfOrder: order}
+	self.ServeJSON()
+}
+
+//返还差额
+func (self *ReserveActController) ReturnAmount() {
+
+	id, _ := self.GetInt64("id")
+	cache, _ := self.GetBool("cache", false)
+
+	act := reserve_act_model.GetReserveActById(id, cache)
+	if act == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	if act.State != reserve_act_model.STATE_FINISHED {
+		self.ReturnError(403, []string{"", "活动尚未结束"}, "", nil)
+	}
+
+	if act.IsReturned {
+		self.ReturnError(403, []string{"", "活动已经返还差额"}, "", nil)
+	}
+
+	count := reserve_act_model.GetListCount(act.Id, false)
+	orders := reserve_act_model.GetList(act.Id, 1, count, false)
+
+	for i := int64(0); i < int64(len(orders)); i++ {
+
+		rank := i + 1
+		returnAmount := orders[i].TotalPrice
+		if rank <= act.ReturnCount {
+			returnAmount = orders[i].TotalPrice - rank*100
+		}
+
+		if returnAmount < 0 {
+			returnAmount = 1
+		}
+
+		cashBalance := balance_model.GetCashBalanceByWxUIdAndRIdAndSource(orders[i].WxUserId, orders[i].OrderId, balance_model.CASH_SOURCE_RESERVE_ACT_RETURN)
+
+		if cashBalance != nil {
+			continue
+		}
+
+		remark := fmt.Sprintf("%s", act.Title)
+		balance := new(balance_model.CashBalance).Create(orders[i].WxUserId, returnAmount, balance_model.CASH_SOURCE_RESERVE_ACT_RETURN, orders[i].OrderId, remark)
+
+		if balance != nil {
+			orders[i].ReturnedAmount = returnAmount
+			orders[i].Save()
+		}
+
+	}
+	act.IsReturned = true
+	act.Save()
+	self.ServeJSON()
+}

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

@@ -0,0 +1,154 @@
+package seckill_controller
+
+import (
+	"github.com/astaxie/beego"
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+	"fohow.com/apps/models/granary_model"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/libs/tool"
+	"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 = order_model.GetSoldCountByPId(pd.Id, useCache)
+		if pd.Ptype == product_model.TYPE_USER_SALE {
+			pd.LeftCount = granary_model.GetSaleOrderLeftCountByPId(pd.Id)
+		} else {
+			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()
+}

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

@@ -0,0 +1,152 @@
+package share_material_controller
+
+import (
+	"github.com/astaxie/beego/context"
+	"fohow.com/apps"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/project_model"
+	"fohow.com/apps/models/share_material_model"
+	"fohow.com/apps/models/user_model"
+)
+
+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 item.MType == share_material_model.M_TYPE_ENUM_PROJECT && item.TypeId > 0 {
+			project := project_model.GetProjectById(item.TypeId, cache)
+
+			if project != nil {
+
+				useCache := (project.State != project_model.STATE_UNDERWAY)
+				projectInfo := new(share_material_model.ProjectInfo)
+				projectInfo.Id = project.Id
+				projectInfo.State = project.State
+				projectInfo.StateCn = project_model.STATE_CN_TEXT[project.State]
+				projectInfo.Title = project.Title
+				projectInfo.MinFunding = project.MinFunding
+				projectInfo.MaxFunding = project.MaxFunding
+				projectInfo.IntroduceAlbum = project_model.GetAscProjectPicturesByPIdAndPType(project.Id, project_model.PIC_TYPE_CYCLE, cache, true)
+				projectInfo.CurrentInvestment = project_model.GetValidTotalFundingByPId(project.Id, useCache)
+
+				totalCopiesMin, totalCopiesMax := int64(0), int64(0)
+				if project.MinFunding < project.MaxFunding {
+					totalCopiesMin = int64(float64(project.MinFunding) / float64(project.UnitPrice))
+					totalCopiesMax = int64(float64(project.MaxFunding) / float64(project.UnitPrice))
+				} else {
+					totalCopiesMin = int64(float64(project.MinFunding) / float64(project.UnitPrice))
+					totalCopiesMax = int64(float64(project.MinFunding) / float64(project.UnitPrice))
+				}
+
+				//如果是有超募的,预热和团购中时,显示剩余份数范围
+				joinCopies := project_model.GetInvestSuccessCountByPId(project.Id, useCache)
+				projectInfo.LeftCopiesMin = totalCopiesMin - joinCopies
+				projectInfo.LeftCopiesMax = totalCopiesMax - joinCopies
+				if projectInfo.LeftCopiesMin < 0 {
+					projectInfo.LeftCopiesMin = 0
+				}
+				if projectInfo.LeftCopiesMax < 0 {
+					projectInfo.LeftCopiesMax = 0
+				}
+				projectInfo.MinProjectWayPrice = project_model.GetMinProjeceWayPriceByPId(project.Id, useCache)
+
+				item.ProjectInfo = projectInfo
+			}
+		}
+
+	}
+
+	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()
+}

+ 22 - 0
go/gopath/src/fohow.com/apps/controllers/subject_controller/init.go

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

+ 206 - 0
go/gopath/src/fohow.com/apps/controllers/subject_controller/product_sale_subject_controller.go

@@ -0,0 +1,206 @@
+package subject_controller
+
+import (
+	"fmt"
+	"github.com/astaxie/beego"
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/order_model"
+	"fohow.com/apps/models/product_model"
+	"fohow.com/apps/models/subject_model"
+	"strconv"
+	"strings"
+	//"time"
+	"fohow.com/apps/models/granary_model"
+	"time"
+)
+
+func (self *SubjectController) GetProductSaleSubjectById() {
+
+	_id := self.Ctx.Input.Param(":id")
+	id, _ := strconv.ParseInt(_id, 10, 64)
+	cache, _ := self.GetBool("useCache")
+
+	if !self.IsWxClient() {
+		url := fmt.Sprintf("%s/notwx", beego.AppConfig.String("MHost"))
+		self.Redirect(url, 302)
+		return
+	}
+
+	//根据id查询专题信息
+	product_sale_subject := subject_model.GetProductSaleSubjectById(id, cache)
+
+	if product_sale_subject == nil {
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	product_sale_subject.BackgroundImg = self.GetFullImgUrl(product_sale_subject.BackgroundImg)
+	product_sale_subject.QrcodeImg = self.GetFullImgUrl(product_sale_subject.QrcodeImg)
+	product_sale_subject.ShareImg = self.GetFullImgUrl(product_sale_subject.ShareImg)
+
+	products := make([]*product_model.Product, 0)
+	if strings.TrimSpace(product_sale_subject.ProductIds) != "" {
+		productIdArray := strings.Split(product_sale_subject.ProductIds, ",")
+		for _, productId := range productIdArray {
+			pId, _ := strconv.ParseInt(productId, 10, 64)
+			product := product_model.GetProductById(pId, cache)
+			if product != nil {
+				product.Cover = product_model.GetCoverByPId(pId, cache)
+				if product_sale_subject.IsPrizeAct {
+					product.SoldCount = order_model.GetSoldCountByPIdAndSId(pId, product_sale_subject.Id, cache)
+				} else {
+					product.SoldCount = order_model.GetSoldCountByPId(pId, cache)
+				}
+				product.SoldCount = product.SoldCount + product.VirtualSoldCount
+
+				//判断剩余数量
+				if product.Ptype == product_model.TYPE_USER_SALE { //代销下单
+					product.LeftCount = granary_model.GetSaleOrderLeftCountByPId(product.Id)
+				} else { //普通下单
+					product.LeftCount = product.Count - product.SoldCount
+				}
+				if product.LeftCount < 0 {
+					product.LeftCount = 0
+				}
+				products = append(products, product)
+			}
+		}
+	}
+	product_sale_subject.Products = products
+
+	//处理绑定关系
+	self.SetWxUserInviter()
+
+	//处理访问明细
+	go product_sale_subject.AddClick()
+	ip := self.Ctx.Input.IP()
+	uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	doClick(product_sale_subject.Id, uId, wxUId, ip)
+
+	type Ret struct {
+		Subject *subject_model.ProductSaleSubject `json:"subject"`
+	}
+
+	self.Data["json"] = &Ret{Subject: product_sale_subject}
+
+	self.ServeJSON()
+}
+
+// 设置微信用户的邀请人,邀请人记录的也是微信用户ID
+func (self *SubjectController) SetWxUserInviter() {
+
+	ivId, _ := self.GetInt64("wx")
+	currectWxUId := self.GetCurrentWxUserId()
+
+	if currectWxUId != 0 && ivId != 0 {
+		go helpers.SetInviter(currectWxUId, ivId)
+	}
+}
+
+//商品销售专题下单
+func (self *SubjectController) Create() {
+	/*_pid := self.Ctx.Input.Param(":id")
+	pId, _ := strconv.ParseInt(_pid, 10, 64)
+	_subjectId := self.Ctx.Input.Param(":subject_id")
+	subjectId, _ := strconv.ParseInt(_subjectId, 10, 64)
+	count, _ := self.GetInt64("count")
+
+	if count <= 0 {
+		self.ReturnError(403, apps.ParamsError, "", nil)
+	}
+
+	saleSubject := subject_model.GetProductSaleSubjectById(subjectId, false)
+	if saleSubject == nil{
+		self.ReturnError(403, apps.NoExist, "", nil)
+	}
+
+	//若是抽奖性质的,查看是否过了开奖时间
+	if saleSubject.IsPrizeAct && saleSubject.OpenPrizeTime.Unix() <= time.Now().Unix(){
+		self.ReturnError(403, apps.OverOpenTime, "", nil)
+	}
+
+	isContain := false
+	productIds := strings.Split(saleSubject.ProductIds, ",")
+	for _, id := range productIds{
+		_id,_ := strconv.ParseInt(id, 10, 64)
+		if _id == pId{
+			isContain = true
+			break
+		}
+	}
+
+	if !isContain{
+		self.ReturnError(403, apps.ProductIdNotSuit, "", nil)
+	}
+
+	product := product_model.GetProductById(pId, false)
+	if product == nil {
+		self.ReturnError(403, apps.ProductNotExist, "", nil)
+	}
+
+	//user := self.GetCurrentUser(true)
+	uId := self.GetCurrentUserId()
+	wxUId := self.GetCurrentWxUserId()
+	//加入限购逻辑
+	if product.PurchaseLimitCount > 0 {
+		if product.PurchaseLimitCount < count {
+			self.ReturnError(403, []string{apps.OverLimitCount[0], fmt.Sprintf("该商品限购%d件", product.PurchaseLimitCount)}, "", nil)
+		} else {
+			purchaseTotalCount := order_model.GetOrderCountByPIdAndWxUId(pId, wxUId)
+			//历史已经买够到限购数量了
+			if product.PurchaseLimitCount <= purchaseTotalCount {
+				self.ReturnError(403, []string{apps.PurchasedReachLimit[0], fmt.Sprintf("该商品限购%d件", product.PurchaseLimitCount)}, "", nil)
+			}
+			//历史没买够数量,但是历史总数+想购买的数量 超过限购数量
+			if product.PurchaseLimitCount < (purchaseTotalCount + count) {
+				canBuyCount := product.PurchaseLimitCount - purchaseTotalCount
+				self.ReturnError(403, []string{apps.PurchasedReachLimit[0], fmt.Sprintf("您还可以购买%d件", canBuyCount)}, "", nil)
+			}
+		}
+	}
+
+	product.SoldCount = order_model.GetSoldCountByPId(product.Id, false)
+	beego.BeeLogger.Warn("check if product stock not enough,product_count:%d,sold_count:%d,order_count:%d", product.Count, product.SoldCount, count)
+	if product.Count-product.SoldCount < count {
+		self.ReturnError(403, apps.ProductStockNotEnough, "", nil)
+	}
+
+	totalPrice := product.Price * count
+	if wxUId == 12 {
+		totalPrice = 1
+	}
+	order := new(order_model.Order).Create(wxUId, uId, product.Id, count,
+		totalPrice, product.BuyPrice*count)
+
+	if order != nil {
+		//是否参与专题抽奖
+		if saleSubject.IsPrizeAct{
+			order.IsJoinSubjectPrize = true
+		}
+		//记录专题id
+		order.ProductSaleSubjectId = subjectId
+		order.Save()
+	}
+
+	type Order struct {
+		OrderId string `json:"order_id"`
+	}
+
+	self.Data["json"] = &Order{OrderId: order.OrderId}*/
+	self.ServeJSON()
+}
+
+func doClick(sId, uId, wxUId int64, ip string) int64 {
+	scfu := subject_model.GetCfUserByWxUIdAndSubjectId(sId, wxUId)
+	if scfu == nil {
+		scfu = new(subject_model.SubjectClickFromUser).Create(sId, uId, wxUId, ip)
+	}
+	if scfu != nil {
+		scfu.ClickTimes = scfu.ClickTimes + 1
+		scfu.ClickLastTime = time.Now()
+		go scfu.Save()
+		return scfu.Id
+	}
+	return 0
+}

+ 61 - 0
go/gopath/src/fohow.com/apps/controllers/subject_controller/subject_sale_code_controller.go

@@ -0,0 +1,61 @@
+package subject_controller
+
+import (
+	"fohow.com/apps/models/subject_model"
+
+	"fohow.com/apps/models/user_model"
+	"fohow.com/libs/tool"
+	"strconv"
+)
+
+func (self *SubjectController) GetList() {
+
+	_subjectId := self.Ctx.Input.Param(":id")
+	subjectId, _ := strconv.ParseInt(_subjectId, 10, 64)
+	page, _ := self.GetInt64("page")
+	perpage, _ := self.GetInt64("per_page")
+	cache, _ := self.GetBool("useCache", true)
+
+	type Ret struct {
+		JoinPrizeNumber  []*subject_model.SaleDrawCode `json:"list"`
+		PrizeNumberCount int64                         `json:"list_count"`
+	}
+
+	joinList := subject_model.GetSaleDrawCodeListBySubjectId(subjectId, page, perpage, cache)
+	for _, item := range joinList {
+		wxUser := user_model.GetWxUserById(item.WxUserId, false)
+		if wxUser != nil {
+			item.WxUserNickname = wxUser.Nickname
+			item.WxUserHead = self.GetFullImgUrl(wxUser.Head)
+		}
+		item.CodeFormat = tool.GetAssignLengthStr(item.Code, 6)
+	}
+
+	list_count := subject_model.GetSaleDrawCodeListCount(subjectId, cache)
+
+	self.Data["json"] = &Ret{JoinPrizeNumber: joinList, PrizeNumberCount: list_count}
+	self.ServeJSON()
+
+}
+
+func (self *SubjectController) GetMyCodeList() {
+
+	_subjectId := self.Ctx.Input.Param(":id")
+	subjectId, _ := strconv.ParseInt(_subjectId, 10, 64)
+
+	type Ret struct {
+		CurPrizeNumbers []string `json:"my_numbers"`
+	}
+
+	wxUId := self.GetCurrentWxUserId()
+
+	var curPrizeNumbers []string
+	curList := subject_model.GetSaleDrawCodesBySubjectIdAndWxUid(subjectId, wxUId, false)
+	for _, item := range curList {
+		curPrizeNumbers = append(curPrizeNumbers, tool.GetAssignLengthStr(item.Code, 6))
+	}
+
+	self.Data["json"] = &Ret{CurPrizeNumbers: curPrizeNumbers}
+	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()
+}

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

@@ -0,0 +1,365 @@
+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) GetCurrentUserBindingState() {
+// 	user := self.GetCurrentUser(false)
+// 	wxUser := self.GetCurrentWxUser(false)
+// 	if wxUser == nil && user == nil {
+// 		self.ReturnError(403, apps.UserNeedLogin, "", nil)
+// 	}
+// 	type CurrentUser struct {
+// 		User         *user_model.User   `orm:"-"                                  json:"user"`
+// 		WxUser       *user_model.WxUser `orm:"-"                               json:"wx_user"`
+// 		WxBindingUrl string             `orm:"-"                               json:"wx_binding_url"`
+// 	}
+
+// 	bindingUrl := ""
+
+// 	//防止在其他端绑定,这里要再次做校验
+// 	if wxUser == nil && user != nil {
+// 		wxUser = user_model.GetWxUserByUserId(user.Id, false)
+// 	}
+
+// 	if wxUser != nil && user == nil {
+// 		if wxUser.UserId != 0 {
+// 			user = user_model.GetUserById(wxUser.UserId, false)
+// 		}
+// 	}
+
+// 	if wxUser == nil && user != nil {
+
+// 		uuId := tool.Get8Uuid()
+// 		k := fmt.Sprintf("user_controller.BindingWxUser[%s]", uuId)
+// 		cache.Cache.Put(k, user.Id, 20*time.Minute)
+// 		bindingUrl = fmt.Sprintf("%s/v1/tool/qrcode?url=%s/v1/user/binding/wx/%s",
+// 			beego.AppConfig.String("ApiHost"), beego.AppConfig.String("ApiHost"), uuId)
+
+// 		// fmt.Println("************* bindingUrl: %s", bindingUrl)
+// 	}
+
+// 	currentUser := new(CurrentUser)
+// 	if user != nil {
+// 		user.Head = user_model.GetFullImgUrl(user.Head)
+// 	}
+// 	if wxUser != nil {
+// 		wxUser.Head = user_model.GetFullImgUrl(wxUser.Head)
+// 	}
+// 	currentUser.User = user
+// 	currentUser.WxUser = wxUser
+// 	currentUser.WxBindingUrl = bindingUrl
+
+// 	self.Data["json"] = currentUser
+// 	self.ServeJSON()
+// }
+
+// //绑定微信用户
+// func (self *UserController) BindingWxUser() {
+// 	uuId := self.Ctx.Input.Param(":key")
+// 	var userId int64
+// 	//从缓存中取到待绑定userId
+// 	k := fmt.Sprintf("user_controller.BindingWxUser[%s]", uuId)
+// 	if r, ok := cache.Cache.Get(k).(int64); ok {
+// 		userId = r
+// 	}
+// 	//校验user存在
+// 	user := user_model.GetUserById(userId, false)
+// 	if user == nil {
+// 		self.ReturnError(403, apps.BindingWxUserError, "", nil)
+// 	}
+// 	//校验wx_user已登录,且记录被创建
+// 	wxUser := self.GetCurrentWxUser(false)
+// 	if wxUser == nil {
+// 		self.ReturnError(403, apps.BindingWxUserError, "", nil)
+// 	}
+
+// 	//确保没有wx_user绑定过该user_id
+// 	existedWxUser := user_model.GetWxUserByUserId(user.Id, false)
+// 	if wxUser.UserId == 0 && existedWxUser == nil {
+// 		//绑定微信user和user
+// 		wxUser.UserId = user.Id
+// 		wxUser.Save()
+// 		user.CopyWxUserHead(wxUser.Head)
+// 		if user.Nickname == "" {
+// 			user.Nickname = wxUser.Nickname
+// 		}
+// 		if user.Country == "" {
+// 			user.Country = wxUser.Country
+// 		}
+// 		if user.Province == "" {
+// 			user.Province = wxUser.Province
+// 		}
+// 		if user.City == "" {
+// 			user.City = wxUser.City
+// 		}
+// 		if user.Sex == 0 {
+// 			user.Sex = wxUser.Sex
+// 		}
+// 		user.Save()
+// 		cache.Cache.Delete(k)
+// 	} else {
+// 		self.ReturnError(403, apps.BindingWxUserError, "", nil)
+// 	}
+// 	self.Redirect(fmt.Sprintf("%s/user/binding/state", beego.AppConfig.String("WxHost")), 302)
+// }
+
+// //绑定身份证
+// 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)
+// 	}
+
+// 	user.RealName = realName
+// 	user.IdentityCard = idCard
+// 	user.IsCertification = 1
+// 	birth, err := time.Parse("20060102", idCard[6:14])
+// 	if err == nil {
+// 		user.Birthday = birth
+// 	}
+
+// 	age, getAagError := getAgeByIdCardNo(user.IdentityCard)
+// 	if getAagError == nil {
+// 		user.Age = age
+// 	}
+
+// 	user.Save()
+
+// 	self.Data["json"] = user
+// 	self.ServeJSON()
+// }
+
+// //根据身份证获取年龄
+// func getAgeByIdCardNo(idCardNo string) (int64, error) {
+// 	age, err := user.CaculateAgeByIdCard(idCardNo)
+// 	if err != nil {
+// 		return 0, err
+// 	}
+// 	return age, nil
+// }
+
+// //迁移余额,春节翻红包专题
+// func moveFHBBalance(wxUser *user_model.WxUser, user *user_model.User) {
+// 	list := balance_model.GetValidWxBalances(wxUser.Id, balance_model.WX_BALANCE_SOURCE_NEW_YEAR_FHB_2017)
+// 	for _, item := range list {
+// 		//标记迁移
+// 		item.State = 1
+// 		item.Save()
+// 		go new(balance_model.Balance).Create(user.Id, item.Amount, item.Source,
+// 			item.RelateId, item.Remark, 1)
+// 	}
+// }
+
+// //迁移wx user balance 中数据到balance
+// func moveWxBalacneToBalance(wxUser *user_model.WxUser, user *user_model.User) {
+// 	list := balance_model.GetAllValidWxBalances(wxUser.Id)
+// 	for _, item := range list {
+// 		//标记迁移
+// 		item.State = 1
+// 		item.Save()
+// 		new(balance_model.Balance).Create(user.Id, item.Amount, item.Source,
+// 			item.RelateId, item.Remark, 1)
+// 	}
+// }
+
+// //更新wxUser上下级的邀请关系。仅适用于user先创建,wxuser后绑定的情况
+// func updateInviteRelation(wxUser *user_model.WxUser, user *user_model.User) {
+// 	if wxUser == nil || user == nil {
+// 		return
+// 	}
+
+// 	//user有邀请人时才执行更新上级的动作
+// 	if user.InviteId != 0 {
+// 		wxInviter := user_model.GetWxUserByUserId(user.InviteId, true)
+// 		//更新上级:微信用户的邀请人与User的邀请人有冲突,以user为准
+// 		if wxInviter != nil && wxUser.InviteId != wxInviter.Id {
+// 			//更新树型结构中受益人
+// 			orgWxInviter := user_model.GetWxUserById(wxUser.InviteId, true)
+// 			if orgWxInviter != nil {
+// 				list := user_model.GetWxBindingBenefitRecords(wxUser.Id, orgWxInviter.Id)
+// 				for _, item := range list {
+// 					//变更受益人
+// 					item.BenefitWxUId = wxInviter.Id
+// 					item.Save()
+// 				}
+
+// 				//删除间接邀请绑定记录
+// 				inDirectList := user_model.GetIndirectWxBindingBenefitRecords(wxUser.Id, orgWxInviter.Id)
+// 				for _, item := range inDirectList {
+// 					item.Delete()
+// 				}
+// 			}
+// 			//更新wxUser邀请人
+// 			wxUser.InviteId = wxInviter.Id
+// 			wxUser.UpdateField("InviteId")
+// 			beego.BeeLogger.Warn("Wxuser Inviter Relationship-binding1, wxUserId:%d, inviteId:%d, userId: %d, tel: %d, userInviteId: %d", wxUser.Id, wxUser.InviteId, user.Id, user.Tel, user.InviteId)
+// 		}
+// 	}
+
+// 	//更新下级
+// 	list := user_model.GetInviteList(user.Id, 1, 99999, false)
+// 	for _, child := range list {
+// 		wxChild := user_model.GetWxUserByUserId(child.Id, false)
+// 		if wxChild != nil && wxChild.InviteId != wxUser.Id {
+// 			//更新树型结构受益人
+// 			orgWxChildInviter := user_model.GetWxUserById(wxChild.InviteId, true)
+// 			if orgWxChildInviter != nil {
+// 				// user_model.UpdateBenefitWxUId(wxUser.Id, orgWxInviter.Id, wxInviter.Id)
+// 				list := user_model.GetWxBindingBenefitRecords(wxChild.Id, orgWxChildInviter.Id)
+// 				for _, item := range list {
+// 					//变更受益人
+// 					item.BenefitWxUId = wxUser.Id
+// 					item.Save()
+// 				}
+// 			}
+
+// 			//更新wxUser邀请人
+// 			wxChild.InviteId = wxUser.Id
+// 			wxChild.UpdateField("InviteId")
+// 			beego.BeeLogger.Warn("Wxuser Inviter Relationship-binding2, wxUserId:%d, inviteId:%d", wxUser.Id, wxUser.InviteId)
+// 		}
+// 	}
+// }
+
+// //给邀请人发送绑定手机通知
+// func sendBindingMsgToInviter(wxUser *user_model.WxUser, user *user_model.User) {
+// 	if wxUser == nil || user == nil {
+// 		return
+// 	}
+
+// 	wxInviter := user_model.GetWxUserById(wxUser.InviteId, true)
+// 	if wxInviter != nil {
+// 		now := time.Now()
+// 		//给直属邀请人发邀请成功通知
+// 		openId := wxInviter.MpOpenid
+// 		url := fmt.Sprintf("%s/user/promot", beego.AppConfig.String("MHost"))
+// 		title := "有人通过您的邀请注册成功。若TA投资项目,您将获得投资金额1%的奖励\n"
+// 		telwStar := tool.ReplaceTelMid4(user.Tel)
+// 		cTime := now.Format("2006-01-02 15:04:05")
+// 		remark := "\n点击详情,可以看一看TA"
+// 		wx_mp.TmplmsgInviteSignUp(openId, url, title, wxUser.Nickname, telwStar, cTime, remark)
+
+// 		// 取消二级关系
+// 		// preWxInviter := user_model.GetWxUserById(wxInviter.InviteId, true)
+// 		// if preWxInviter != nil {
+// 		// 	//给间接邀请人发注册成功通知
+// 		// 	openId := preWxInviter.MpOpenid
+// 		// 	url := fmt.Sprintf("%s/user/promot", beego.AppConfig.String("MHost"))
+// 		// 	title := fmt.Sprintf("有人通过%s的邀请注册成功。若TA投资项目,您将获得投资金额3‰的奖励\n", wxInviter.Nickname)
+// 		// 	telwStar := tool.ReplaceTelMid4(user.Tel)
+// 		// 	cTime := now.Format("2006-01-02 15:04:05")
+// 		// 	remark := "\n点击详情,可以看一看TA"
+// 		// 	wx_mp.TmplmsgInviteSignUp(openId, url, title, wxUser.Nickname, telwStar, cTime, remark)
+// 		// }
+// 	}
+// }

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

@@ -0,0 +1,135 @@
+package user_controller
+
+import (
+	"encoding/json"
+	"github.com/astaxie/beego"
+	"fohow.com/apps"
+	"fohow.com/apps/helpers"
+	"fohow.com/apps/models/user_model"
+)
+
+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()
+		go user_model.UpdateIsRegistD5c(user)
+		// 如果微信用户已绑定手机,则找出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)CheckParamsIsValid(phonumer, sign, tistamp string) bool {
+//
+//	bindingWxPhoneKey := beego.AppConfig.String("BindingWxPhoneKey")
+//
+//	var KVArray []string
+//	tpKV := fmt.Sprintf("%s=%s", "phonumer", phonumer)
+//	tistampKV := fmt.Sprintf("%s=%s", "tistamp", tistamp)
+//	KVArray = append(KVArray, tpKV)
+//	KVArray = append(KVArray, tistampKV)
+//	sort.Strings(KVArray)
+//
+//	KVArray = append(KVArray, fmt.Sprintf("%s=%s", "salt", bindingWxPhoneKey))
+//
+//	var KVString = strings.Join(KVArray, "&")
+//
+//	md5Ctx := md5.New()
+//	md5Ctx.Write([]byte(KVString))
+//	val := md5Ctx.Sum(nil)
+//	calSign := hex.EncodeToString(val)
+//	beego.BeeLogger.Warn("BindingWxPhone-calSign:%s", calSign)
+//	return calSign == sign
+//}

+ 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", "UpdateIsRegistD5cAndIsRegistBeforeD5c", "GenerateQrcode",
+		"GetInviteList", "GetMonthlyInviteList", "OneClickBindingTel", "BindingWxPhone", "SetWxUserInviter", "GetUserCertificationFromD5c", "Get"}
+	exceptCheckWxUserLoginAction = []string{"CheckLogin", "UpdateIsRegistD5cAndIsRegistBeforeD5c"}
+)
+
+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 || wxUser.CopartnerState != user_model.COPARTNER_STATE_YES {
+		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 || wxUser.CopartnerState != user_model.COPARTNER_STATE_YES {
+		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()
+}

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


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