Gogs 3 年 前
コミット
17e4c3cd19

+ 18 - 1
src/common/common_control.py

@@ -71,7 +71,6 @@ def no_cache(key="*"):
     def _wrapper(func):
         def __wrapper(*args,**kwargs):
             res = func(*args,**kwargs)
-            print 999999999999999
             print cache.delete_pattern("cdata_{}*".format(key))
             return res
         return __wrapper
@@ -134,6 +133,24 @@ def upload_file(request):
         rst["imgurl"] = imgurl
     return rst
 
+def get_cur_match():
+    """获取当前赛事
+    """
+    now = datetime.datetime.now().strftime("%Y-%m-%d")
+    #cur_match = cm.Match.objects.filter(start_time__lte=now,end_time__gte=now).order_by("-id").first()
+    cur_match = cm.Match.objects.filter(match_status=3).order_by("-id").first()
+    return cur_match
+
+def get_signup_match():
+    """获取报名赛事
+    """
+    now = datetime.datetime.now().strftime("%Y-%m-%d")
+    #先查询开始报名的赛事
+    cur_match = cm.Match.objects.filter(match_status=2).order_by("-id").first()
+    if not cur_match:
+        cur_match = cm.Match.objects.filter(match_status=3).order_by("-id").first()
+    return cur_match
+
 
 if __name__ == "__main__":
     #测试

+ 14 - 1
src/common/common_functions.py

@@ -1,10 +1,11 @@
 # coding:utf-8
 import calendar
 import hashlib
-import datetime
+import datetime,time
 import re
 import M2Crypto
 from PIL import Image,ImageDraw
+import requests
 
 def get_month_dates(month="202008"):
     """
@@ -183,6 +184,18 @@ def get_ip(request):
         ip = request.META['REMOTE_ADDR']
     return ip
 
+def get_city_from_ip(ip):
+    url = "https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=%s&co=&resource_id=5809&t=%s" \
+        % (ip,str(int(time.time()*1000)))
+    try:
+        result = requests.get(url).json()
+        location = result.get("data")[0].get("location")
+        location = location.split("省")[-1].split("市")[0]
+    except Exception as e:
+        print(e)
+        location = None
+    return location
+
 if __name__ == "__main__":
     pass
     print make_password("hnwz@2021")

+ 8 - 6
src/common/core_views.py

@@ -25,6 +25,8 @@ from common import error_info
 from common.models import UserInfo
 import common.models as cm
 import common.error_info as ce
+import common.common_functions as ccf
+import common.common_control as ccc
 
 logger = logging.getLogger(__name__)
 
@@ -111,15 +113,15 @@ def api_wapper(handler, request, is_vauth, *args, **kwargs):
         if token:
             dec_name = aescbc.decrypt(token)
             id = dec_name.split("_")[0]
-            #id = 80
             user = cm.UserInfo.objects.filter(id=id).first()
             if not user and False:
                 return JsonResponse({"code":403,"data":{}})
-            #选手
-            cur_match_id = cm.Player.objects.filter(user_id=id).order_by("-match_id").first().match_id
 
+            #选手
+            cur_match_id = ccc.get_cur_match().id
             player = cm.Player.objects.filter(user_id=id,match_id=cur_match_id).order_by("-id").first()
-
+            if not player:
+                player = cm.Player.objects.filter(user_id=id).order_by("-id").first()
 
             setattr(request, "ip", get_ip(request))
             setattr(request, "user", user)
@@ -189,10 +191,10 @@ def admin_handler(handler, request, is_vauth, *args, **kwargs):
     except Exception as e:
         return to_fail(e)
 
-def to_suc(data={}):
+def to_suc(data={},code=0):
     info = {}
     info["data"] = data
-    info["code"] = 0
+    info["code"] = code
     return JsonResponse(info,encoder=CusDjangoJSONEncoder)
 
 def to_fail(e=None):

+ 105 - 2
src/common/models.py

@@ -23,6 +23,9 @@ class UserInfo(models.Model):
     account_img = models.TextField(u"账号截图", max_length=255, blank=True, null=True)
     join_time = models.CharField(u"入市时间", max_length=255, blank=True, null=True)
     badge = models.CharField(u"选手标识", max_length=255,blank=True,null=True,default=u"选手")
+    phone = models.CharField(u"手机号", max_length=64, blank=True,null=True)
+    role = models.SmallIntegerField(u"角色",default=0)
+    player_type = models.SmallIntegerField(u"选手类型,0/游客,1/普通选手,2/种子选手,3/开户选手",default=0)
 
     ctime = models.DateTimeField(u"创建时间", auto_now_add=True)
 
@@ -50,9 +53,13 @@ class Player(models.Model):
     match_id = models.IntegerField(u"比赛id", blank=True,null=True)
     match_name = models.CharField(u"比赛名称", max_length=255,blank=True,null=True)
     match_group = models.CharField(u"比赛分组", max_length=255,blank=True,null=True)
+    match_group_name = models.CharField(u"比赛分组名称", max_length=255,blank=True,null=True)
     fund = models.FloatField(u"资金",default=0.0)
     match_status = models.SmallIntegerField(u"比赛状态,退赛/暂停/比赛中-1/0/1",default=0)
     badge = models.CharField(u"选手标识", max_length=255,blank=True,null=True,default=u"选手")
+    role = models.SmallIntegerField(u"角色",default=0)
+    player_type = models.SmallIntegerField(u"选手类型,0/游客,1/普通选手,2/种子选手,3/开户选手",default=1)
+    phone = models.CharField(u"手机号", max_length=50,blank=True,null=True)
 
     ctime = models.DateTimeField(u"创建时间", auto_now_add=True)
 
@@ -92,12 +99,14 @@ class PlayerRecord(models.Model):
     auto_complete = models.SmallIntegerField(u"请假次数",default=0)
     yesterday_auto_complete = models.SmallIntegerField(u"请假次数",default=0)
     wanzhu_comment = models.TextField(u"点评",blank=True,null=True)
-    experience = models.TextField(u"操盘总结",blank=True,null=True)
+    experience = models.TextField(u"操盘总结/今日反思",blank=True,null=True)
     zq = models.CharField(u"周期", max_length=255, blank=True, null=True)
     cw = models.CharField(u"仓位", max_length=255, blank=True, null=True)
     df = models.CharField(u"打法", max_length=255, blank=True, null=True)
     pz = models.CharField(u"品种", max_length=255, blank=True, null=True)
     badge = models.CharField(u"选手标识", max_length=255,blank=True,null=True,default=u"选手")
+    zans = models.IntegerField(u"点赞数", blank=True,null=True,default=0)
+    comments_count = models.IntegerField(u"点赞数", blank=True,null=True,default=0)
 
     ctime = models.DateTimeField(u"创建时间", auto_now_add=True)
 
@@ -119,6 +128,11 @@ class Match(models.Model):
     groups = models.TextField(u"结束时间", max_length=255, blank=True,null=True)
     calendar = models.TextField(u"报单日历", max_length=255, blank=True,null=True)
     valid_dates = models.TextField(u"有效报单时间", max_length=255, blank=True,null=True)
+    player_price = models.FloatField(u"选手价格", blank=True,null=True)
+    viewer_price = models.FloatField(u"游客价格", blank=True,null=True)
+    match_status = models.SmallIntegerField(u"赛事状态1/待发布,2/开始报名,3/比赛中,4/比赛结束", blank=True,null=True,default=1)
+    signup_start_time = models.CharField(u"报名开始时间", max_length=255, blank=True,null=True)
+    signup_end_time = models.CharField(u"报名结束时间", max_length=255, blank=True,null=True)
 
     ctime = models.DateTimeField(u"创建时间", auto_now_add=True)
 
@@ -128,7 +142,7 @@ class Match(models.Model):
         app_label = "common"
 
     def __str__(self):
-        return u"{}){}".format(self.id, self.username)
+        return u"{}){}".format(self.id, self.name)
 
 
 class MatchGroup(models.Model):
@@ -138,6 +152,7 @@ class MatchGroup(models.Model):
     name = models.CharField(u"名称", max_length=255, blank=True,null=True)
     is_active = models.SmallIntegerField(u"是否显示",default=1)
     order = models.IntegerField(u"排序字段",default=1)
+    charge = models.IntegerField(u"是否收费1/收费0/免费",default=1)
 
     ctime = models.DateTimeField(u"创建时间", auto_now_add=True)
 
@@ -343,3 +358,91 @@ class HotStockSellCount(models.Model):
 
     def __str__(self):
         return u"{}){}".format(self.stock_id, self.stock_name)
+
+
+class SignupOrder(models.Model):
+    """报名订单表
+    """
+    user_id = models.IntegerField(u"用户id",blank=True,null=True)
+    player_id = models.IntegerField(u"选手id",blank=True,null=True)
+    user_name = models.CharField(u"用户名",max_length=50,blank=True,null=True)
+    match_id = models.IntegerField(u"比赛id",blank=True,null=True)
+    match_name = models.CharField(u"比赛名称",max_length=50,blank=True,null=True)
+    total_fee = models.FloatField(u"价格",blank=True,null=True)
+    out_trade_no = models.CharField(u"订单号",max_length=50,blank=True,null=True)
+    order_status = models.SmallIntegerField(u"订单状态",blank=True,null=True,default=0)
+    pay_status = models.SmallIntegerField(u"支付状态",blank=True,null=True,default=0)
+    pay_time = models.DateTimeField(u"支付时间",blank=True,null=True)
+    signup_type = models.SmallIntegerField(u"报名类型1/游客,2/选手",blank=True,null=True)
+    transaction_id = models.CharField(u"交易id",max_length=50,blank=True,null=True)
+    remark = models.TextField(u"备注",max_length=50,blank=True,null=True)
+    phone = models.CharField(u"手机号",max_length=50,blank=True,null=True)
+    match_group = models.IntegerField(u"赛事分组",blank=True,null=True)
+
+    ctime = models.DateTimeField(u"创建时间", auto_now_add=True)
+
+    class Meta:
+        db_table = "signup_order"
+        verbose_name = u"报名订单表"
+        app_label = "common"
+
+
+class WanzhuConsult(models.Model):
+    """顽主咨询
+    """
+    user_id = models.IntegerField(u"用户id",blank=True,null=True)
+    user_name = models.CharField(u"用户名", max_length=255, blank=True,null=True)
+    user_avatar = models.TextField(u"头像", max_length=255, blank=True,null=True)
+    player_id = models.IntegerField(u"选手id",blank=True,null=True)
+    content = models.TextField(u"咨询内容", max_length=255, blank=True,null=True)
+    reply_content = models.TextField(u"回复内容", max_length=255, blank=True,null=True)
+    reply_status = models.SmallIntegerField(u"0/未回复,1/已回复",blank=True,null=True,default=0)
+    pid = models.IntegerField(u"上级id",blank=True,null=True)
+
+    ctime = models.DateTimeField(u"创建时间", auto_now_add=True)
+
+
+    class Meta:
+        db_table = "wanzhu_consult"
+        verbose_name = u"顽主咨询"
+        app_label = "common"
+
+
+class Comments(models.Model):
+    """作业评论
+    """
+    user_id = models.IntegerField(u"用户id",blank=True,null=True)
+    user_name = models.CharField(u"用户名", max_length=255, blank=True,null=True)
+    user_avatar = models.TextField(u"头像", max_length=255, blank=True,null=True)
+    player_id = models.IntegerField(u"选手id",blank=True,null=True)
+    content = models.TextField(u"咨询内容", max_length=255, blank=True,null=True)
+    record_id = models.IntegerField(u"作业id",blank=True,null=True)
+    pid = models.IntegerField(u"上级id",blank=True,null=True)
+    location = models.CharField(u"地理位置", max_length=255, blank=True,null=True)
+    stock_id = models.IntegerField(u"作业id",blank=True,null=True)
+    stock_name = models.CharField(u"股票名称", max_length=255, blank=True,null=True)
+
+    ctime = models.DateTimeField(u"创建时间", auto_now_add=True)
+
+    class Meta:
+        db_table = "comments"
+        verbose_name = u"评论"
+        app_label = "common"
+
+
+class UserChoice(models.Model):
+    """跟踪/自选
+    """
+    user_id = models.IntegerField(u"用户id",blank=True,null=True)
+    user_name = models.CharField(u"用户名", max_length=255, blank=True,null=True)
+    user_avatar = models.TextField(u"头像", max_length=255, blank=True,null=True)
+    player_id = models.IntegerField(u"选手id",blank=True,null=True)
+    stock_id = models.IntegerField(u"股票id",blank=True,null=True)
+    stock_name = models.CharField(u"股票名称", max_length=255,blank=True,null=True)
+
+    ctime = models.DateTimeField(u"创建时间", auto_now_add=True)
+
+    class Meta:
+        db_table = "user_choice"
+        verbose_name = u"跟踪/自选"
+        app_label = "common"

+ 117 - 7
src/manage/controls.py

@@ -169,6 +169,14 @@ def add_model(cls,**kwargs):
         return obj.id
 
     obj = model.objects.create(**kwargs)
+    #
+    if model_name == "Match":
+        cm.MatchGroup.objects.create(
+            match_id = obj.id, 
+            name = obj.name+u"游客组", 
+            is_active = 0,
+            charge = 0
+        )
 
     return obj.id
 
@@ -360,10 +368,22 @@ def get_list_info(cls,**kwargs):
                 qset = qset.filter(auto_complete__gte=kwargs.get("auto_complete"))
             else:
                 qset = qset.filter(auto_complete=kwargs.get("auto_complete"))
+    if model_name == "SignupOrder":
+        if kwargs.get("match_id"):
+            qset = qset.filter(match_id=kwargs.get("match_id"))
+        if kwargs.get("username"):
+            qset = qset.filter(user_name__icontains=kwargs.get("username"))
+        if kwargs.get("signup_type"):
+            qset = qset.filter(signup_type=kwargs.get("signup_type"))
+        if kwargs.get("order_status") or kwargs.get("order_status")==0:
+            qset = qset.filter(order_status=kwargs.get("order_status"))
 
     if model_name == "Article":
         if kwargs.get("type"):
             qset = qset.filter(type=kwargs.get("type"))
+    if model_name == "Stock":
+        if kwargs.get("username"):
+            qset = qset.filter(name__icontains=kwargs.get("username"))
 
     if model_name == "PlayerRecord":
         data = list(qset.order_by("-total_income").values())
@@ -376,13 +396,17 @@ def get_list_info(cls,**kwargs):
         if model_name == "Player":
             for item in data:
                 user = cm.UserInfo.objects.filter(id=item["user_id"]).first()
-                match = cm.Match.objects.filter(id=item["match_id"]).first()
-                item["username"] = user.username
-                item["usercode"] = user.usercode
-                item["nickname"] = user.nickname
-                item["match_name"] = match.name if match else ""
-                item["match_group"] = int(item["match_group"])
-                item["match_group_name"] = cm.MatchGroup.objects.filter(id=int(item["match_group"])).first().name
+                if user:
+                    match = cm.Match.objects.filter(id=item["match_id"]).first()
+                    item["username"] = user.username
+                    item["usercode"] = user.usercode
+                    item["nickname"] = user.nickname
+                    item["match_name"] = match.name if match else ""
+                    item["match_group"] = int(item["match_group"])
+                    item["match_group_name"] = cm.MatchGroup.objects.filter(id=int(item["match_group"])).first().name
+                    item["openid"] = user.openid
+                    item["player_type"] = user.player_type
+                    item["phone"] = user.phone
                 #cm.Player.objects.filter(id=item["id"]).update(username=user.username,usercode=user.usercode)
         if model_name == "PlayerRecord":
             for item in data:
@@ -422,6 +446,9 @@ def get_list_info(cls,**kwargs):
                 item["join_count"] = cm.Player.objects.filter(match_group=item["id"]).count()
                 item["out_count"] = cm.Player.objects.filter(match_group=item["id"],match_status=-1).count()
                 item["match_name"] = match_name
+        if model_name == "SignupOrder":
+            for item in data:
+                item["match_group_name"] = cm.MatchGroup.objects.filter(id=item["match_group"]).first().name
         return (total,data)
     else:
         return len(data),data
@@ -603,3 +630,86 @@ def reset_initfund(**kwargs):
     match_id = kwargs.get("match_id")
     cm.Player.objects.filter(match_id=match_id).update(fund=1)
     #cm.Player.objects.filter(match_id=match_id,id=3011).update(fund=None)
+
+def get_stock_comments_list(**kwargs):
+    """
+    """
+    qset = cm.Comments.objects.filter(stock_id__isnull=False)
+    if kwargs.get("name"):
+        qset = qset.filter(stock_name__icontains=kwargs.get("name"))
+    data = list(qset.values())
+    for item in data:
+        item["stock_name"] = cm.Stock.objects.filter(id=item["stock_id"]).first().name
+    #分页
+    page = int(kwargs.get("page",1))
+    page_size = int(kwargs.get("page_size",20))
+    if page and page_size:
+        total,data = ccf.get_page_list(data,page,page_size)
+    else:
+        total = len(data)
+    return total,data
+
+
+def get_record_comments_list(**kwargs):
+    """
+    """
+    qset = cm.Comments.objects.filter(record_id__isnull=False)
+    if kwargs.get("user_name"):
+        qset = qset.filter(user_name__icontains=kwargs.get("name"))
+    data = list(qset.values())
+    for item in data:
+        item["record_info"] = cm.PlayerRecord.objects.filter(id=item["record_id"]).values().first()
+    #分页
+    page = int(kwargs.get("page",1))
+    page_size = int(kwargs.get("page_size",20))
+    if page and page_size:
+        total,data = ccf.get_page_list(data,page,page_size)
+    else:
+        total = len(data)
+    return total,data
+
+
+def get_wanzhu_consult_list(**kwargs):
+    """
+    """
+    qset = cm.WanzhuConsult.objects.filter(user_id__gt=0).order_by("-id")
+    if kwargs.get("user_name"):
+        qset = qset.filter(user_name__icontains=kwargs.get("name"))
+    data = list(qset.values())
+    for item in data:
+        reply_list = list(cm.WanzhuConsult.objects.filter(pid=item["id"]).values())
+        item["reply_list"] = reply_list
+    #分页
+    page = int(kwargs.get("page",1))
+    page_size = int(kwargs.get("page_size",20))
+    if page and page_size:
+        total,data = ccf.get_page_list(data,page,page_size)
+    else:
+        total = len(data)
+    return total,data
+
+def reply_wanzhu_consult(**kwargs):
+    """
+    """
+    pid = kwargs.get("pid")
+    content = kwargs.get("reply_content")
+    cm.WanzhuConsult.objects.create(
+        pid = pid, 
+        reply_content = content,
+        user_id = 0,
+        player_id = 0
+    )
+    cm.WanzhuConsult.objects.filter(id=pid).update(reply_status=1)
+
+def update_player_type(**kwargs):
+    """
+    """
+    player_id = kwargs.get("player_id")
+    user_id = kwargs.get("user_id")
+    player_type = kwargs.get("player_type")
+    phone = kwargs.get("phone")
+
+    cm.UserInfo.objects.filter(id=user_id).update(player_type=player_type,phone=phone)
+
+
+

+ 9 - 0
src/manage/urls_backstage.py

@@ -32,6 +32,15 @@ urlpatterns = [
     url(r'^stock/list$', views.StockListView.as_view()),
     url(r'^player/record/comment$', views.PlayerRecordCommentView.as_view()),
     url(r'^reset/initfund$', views.ResetInitFundView.as_view()),
+    #3.0
+    url(r'^signup$', views.SignupOrderView.as_view()),
+    url(r'^signup/list$', views.SignupOrderListView.as_view()),
+    url(r'^stock/comments/list$', views.StockCommentsListView.as_view()),
+    url(r'^record/comments/list$', views.RecordCommentsListView.as_view()),
+    url(r'^consult/list$', views.ConsultListView.as_view()),
+    url(r'^consult$', views.WanzhuConsultView.as_view()),
+    url(r'^consult/reply$', views.WanzhuConsultReplyView.as_view()),
+    url(r'^player/type$', views.PlayerTypeView.as_view()),
 
 ]
 

+ 203 - 4
src/manage/views.py

@@ -285,7 +285,7 @@ class MatchView(cv.AdminView):
         if mse:
             raise ce.TipException(mse)
         try:
-            need_params.extend(["calendar","valid_dates"])
+            need_params.extend(["calendar","valid_dates","player_price","viewer_price","match_status","signup_start_time","signup_end_time"])
             vals = ccf.get_need_params(*need_params,**qdata)
             vals["valid_dates"] = json.dumps(vals["valid_dates"])
             rst = ctl.add_model(self,**vals)
@@ -302,12 +302,12 @@ class MatchView(cv.AdminView):
         @end_time:"结束时间"
         """
         qdata = request.json
-        need_params = ["id","name","start_time","end_time"]
+        need_params = ["id","name","start_time","end_time","player_price","viewer_price"]
         mse = ccf.check_params(*need_params,**qdata)
         if mse:
             raise ce.TipException(mse)
         try:
-            need_params.extend(["calendar","valid_dates"])
+            need_params.extend(["calendar","valid_dates","signup_start_time","signup_end_time","match_status"])
             vals = ccf.get_need_params(*need_params,**qdata)
             vals["valid_dates"] = json.dumps(vals["valid_dates"])
             rst = ctl.update_model(self,**vals)
@@ -643,7 +643,8 @@ class PlayerFastSaveView(cv.AdminView):
             vals = ccf.get_need_params(*need_params,**qdata)
             if not vals.get("usercode"):
                 vals["usercode"] = self.gen_code()
-            vals["fund"] = round(float(vals["fund"]),4)
+            if vals.get("fund"):
+                vals["fund"] = round(float(vals["fund"]),4)
             rst = ctl.fast_save_player(**vals)
             return cv.to_suc(rst)
         except Exception as e:
@@ -902,3 +903,201 @@ class ResetInitFundView(cv.AdminView):
         except Exception as e:
             cv.tracefail()
             return cv.to_fail(e)
+
+
+class SignupOrderListView(cv.AdminView):
+    def get(self, request):
+        """#订单列表(平台管理后台)
+        @name:"用户名"
+        @page:1
+        @page_size:20
+        """
+        qdata = request.json
+        try:
+            total,rst = ctl.get_list_info(self,**qdata)
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class StockCommentsListView(cv.AdminView):
+    def get(self, request):
+        """#股票评论列表(平台管理后台)
+        @name:"股票名称"
+        @page:1
+        @page_size:20
+        """
+        qdata = request.json
+        try:
+            total,rst = ctl.get_stock_comments_list(**qdata)
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class RecordCommentsListView(cv.AdminView):
+    def get(self, request):
+        """#作业评论列表(平台管理后台)
+        @name:"股票名称"
+        @page:1
+        @page_size:20
+        """
+        qdata = request.json
+        try:
+            total,rst = ctl.get_record_comments_list(**qdata)
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class ConsultListView(cv.AdminView):
+    def get(self, request):
+        """#顽主咨询列表(平台管理后台)
+        @name:"股票名称"
+        @page:1
+        @page_size:20
+        """
+        qdata = request.json
+        try:
+            total,rst = ctl.get_wanzhu_consult_list(**qdata)
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class WanzhuConsultView(cv.AdminView):
+    def get(self, request):
+        """#顽主咨询详情(平台管理后台)
+        @id:1
+        """
+        qdata = request.json
+        need_params = ["id"]
+        mse = ccf.check_params(*need_params,**qdata)
+        if mse:
+            raise ce.TipException(mse)
+        try:
+            vals = ccf.get_need_params(*need_params,**qdata)
+            rst = ctl.get_detail_info(self,**vals)
+            return cv.to_suc(rst)
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+    def delete(self,request):
+        """#删除咨询(平台管理后台)
+        @id:"1",多个逗号分隔
+        """
+        qdata = request.json
+        need_params = ["id"]
+        mse = ccf.check_params(*need_params,**qdata)
+        if mse:
+            raise ce.TipException(mse)
+        try:
+            vals = ccf.get_need_params(*need_params,**qdata)
+            rst = ctl.delete_model(self,**vals)
+            return cv.to_suc(rst)
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class WanzhuConsultReplyView(cv.AdminView):
+    def post(self, request):
+        """#顽主咨询回复(平台管理后台)
+        @id:1
+        """
+        qdata = request.json
+        need_params = ["pid","reply_content"]
+        mse = ccf.check_params(*need_params,**qdata)
+        if mse:
+            raise ce.TipException(mse)
+        try:
+            vals = ccf.get_need_params(*need_params,**qdata)
+            rst = ctl.reply_wanzhu_consult(**vals)
+            return cv.to_suc(rst)
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class SignupOrderView(cv.AdminView):
+    def get(self, request):
+        """#订单详情(平台管理后台)
+        @id:1
+        """
+        qdata = request.json
+        need_params = ["id"]
+        mse = ccf.check_params(*need_params,**qdata)
+        if mse:
+            raise ce.TipException(mse)
+        try:
+            vals = ccf.get_need_params(*need_params,**qdata)
+            rst = ctl.get_detail_info(self,**vals)
+            return cv.to_suc(rst)
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+    def delete(self,request):
+        """#删除订单(平台管理后台)
+        @id:"1",多个逗号分隔
+        """
+        qdata = request.json
+        need_params = ["id"]
+        mse = ccf.check_params(*need_params,**qdata)
+        if mse:
+            raise ce.TipException(mse)
+        try:
+            vals = ccf.get_need_params(*need_params,**qdata)
+            rst = ctl.delete_model(self,**vals)
+            return cv.to_suc(rst)
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+    def put(self,request):
+        """#修改订单(平台管理后台)
+        @id:"1"
+        @order_status:0/1/-1
+        @remark:""
+        """
+        qdata = request.json
+        need_params = ["id"]
+        mse = ccf.check_params(*need_params,**qdata)
+        if mse:
+            raise ce.TipException(mse)
+        try:
+            need_params.extend(["order_status","remark"])
+            vals = ccf.get_need_params(*need_params,**qdata)
+            rst = ctl.update_model(self,**vals)
+            return cv.to_suc(rst)
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class PlayerTypeView(cv.AdminView):
+    def put(self,request):
+        """#修改选手类型(平台管理后台)
+        @player_id:"1"
+        @player_type:0/1/2/3
+        @phone:"",手机号
+        """
+        qdata = request.json
+        need_params = ["player_id"]
+        mse = ccf.check_params(*need_params,**qdata)
+        if mse:
+            raise ce.TipException(mse)
+        try:
+            need_params.extend(["player_type","phone","user_id"])
+            vals = ccf.get_need_params(*need_params,**qdata)
+            rst = ctl.update_player_type(**vals)
+            return cv.to_suc(rst)
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)

+ 1 - 1
src/settings/__init__.py

@@ -1,3 +1,3 @@
 #coding=utf-8
-from .settings_online import *
+from .settings_dev import *
 

+ 1 - 1
src/settings/base.py

@@ -56,7 +56,7 @@ MIDDLEWARE = [
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
-    #'oplog.OpLogMiddleware.OpLogMiddleware'
+    'oplog.OpLogMiddleware.OpLogMiddleware'
 ]
 
 ROOT_URLCONF = 'urls'

+ 5 - 2
src/settings/settings_dev.py

@@ -30,7 +30,10 @@ DATABASES = {
         'PASSWORD': 'xjc890*()',
         'HOST': '127.0.0.1',
         'PORT': '3306',
-        'OPTIONS':{'charset':'utf8mb4'}
+        'OPTIONS':{
+            'init_command':'SET sql_mode="STRICT_TRANS_TABLES"',
+            'charset':'utf8mb4'
+        }
     }
 }
 
@@ -72,4 +75,4 @@ LOGGING = {
 RANK_LIST = "RANK_LIST"
 
 HOST = "https://wx.scxjc.club"
-PROJECT_NAME = u"顽主杯"
+PROJECT_NAME = u""

+ 37 - 0
src/tools/clean_data.py

@@ -0,0 +1,37 @@
+#coding:utf-8
+import os
+import time
+import datetime
+import sys
+import django
+from django.core.cache import cache
+from django.db import connection
+
+sys.path.append('/mnt/wzbapi/src')
+os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+django.setup()
+
+import common.models as cm
+
+def clean_user_stock():
+    """
+    """
+    prset = cm.UserStock.objects.all()
+    for index,pr in enumerate(prset):
+        player_id = pr.player_id
+        if not cm.Player.objects.filter(id=player_id).exists():
+            print(player_id)
+            cm.UserStock.objects.filter(player_id=player_id).delete()
+
+
+
+if __name__ == "__main__":
+    print "start clean_user_stock..."
+    st = time.time()
+    clean_user_stock()
+    print "time cost:",time.time()-st
+
+
+
+
+

+ 33 - 10
src/tools/update_group_rank.py

@@ -57,19 +57,22 @@ def update_group_rank(stock_date=None):
 def update_group_rank_cache(stock_date=None):
     """
     """
-    match_id = 7
-    prset = cm.PlayerRecord.objects.filter(match_id=match_id).order_by("-stock_date")
+    match_id = 10
+    #prset = cm.PlayerRecord.objects.filter(match_id=match_id).order_by("-stock_date")
+    prset = cm.PlayerRecord.objects.filter(id__gte=120000).order_by("-id")
     if prset:
         records = prset.values()
         case_id = " case id "
         cases = []
         where = []
         for index,pr in enumerate(prset):
+            print(pr.id)
             case = "WHEN %s THEN %s" % (pr.id,index+1)
             cases.append(case)
             where.append(str(pr.id))
 
             record = records[index]
+            update_player_latest(record)
             #
             key = "%s_%s_%s_%s" % (pr.player_id,pr.match_id,pr.match_group,pr.stock_date)
             if pr.player_id == 434:
@@ -78,20 +81,40 @@ def update_group_rank_cache(stock_date=None):
             ccc.pl.set(key,json.dumps(record,cls=ccc.CusJSONEncoder))
         ccc.pl.execute()
 
-        #case = case_id + " ".join(cases)
-        #where = ",".join(where)
-        #sql = "update player_record set group_rank = %s ELSE 0 END where id in (%s)" % (case,where)
-        #cursor = connection.cursor()
-        #cursor.execute(sql)
-        #cursor.close()
-
+def update_player_latest():
+    """更新选手最后一次数据
+    """
+    players = cm.Player.objects.filter(match_id=10,match_status=1)
+    for player in players:
+        player_id = player.id
+        match_id = player.match_id
+        print(player_id)
+        latest = cm.PlayerRecord.objects.filter(player_id=player_id,match_id=match_id).order_by("-stock_date").first()
+        if latest:
+            key = "PLAYER_LATEST_{}".format(player_id)
+            ccc.cache.hset(key,"stock_date",latest.stock_date)
+            ccc.cache.hset(key,"match_id",latest.match_id)
+            ccc.cache.hset(key,"match_group",latest.match_group)
+            #更新胜率
+            qset = cm.PlayerRecord.objects.filter(match_id=latest.match_id,player_id=latest.player_id)
+            win_rate = qset.filter(today_income__gte=0).count()/float(qset.count()) if qset else 0.0
+            win_rate = round(win_rate,3)
+            ccc.cache.hset(key,"win_rate",win_rate)
+        badest = cm.PlayerRecord.objects.filter(player_id=player_id,match_id=match_id).order_by("-today_income").first()
+        if badest:
+            key = "PLAYER_LATEST_{}".format(player_id)
+            ccc.cache.hset(key,"badest_income",latest.today_income)
+
+def update_user_stock():
+    pass
 
 
 if __name__ == "__main__":
     print "start update group rank..."
     st = time.time()
     #update_group_rank()
-    update_group_rank_cache()
+    #update_group_rank_cache()
+    update_player_latest()
     print "time cost:",time.time()-st
 
 

+ 12 - 137
src/utils/aliyun_sms.py

@@ -3,10 +3,10 @@
 import json
 from aliyunsdkcore.client import AcsClient
 from aliyunsdkcore.request import CommonRequest
-client = AcsClient('LTAIbY5V8m3zBjrT', 'H0UrXv6cVYoQLRUdD9ZR7DvF2Sr0FX', 'cn-hangzhou')
+client = AcsClient('LTAI5t8bioQxGXB1jtVugJcU', 'OdGWSBRjkJxaPjgmE38eQ8nzkI6nRk', 'cn-hangzhou')
 
-def send_audit_notice(phone,subject_item):
-    """发送审核通知
+def send_verify_code(phone,code):
+    """发送手机验证码
     """
     request = CommonRequest()
     request.set_accept_format('json')
@@ -18,141 +18,16 @@ def send_audit_notice(phone,subject_item):
 
     request.add_query_param('RegionId', "cn-hangzhou")
     request.add_query_param('PhoneNumbers', phone)
-    request.add_query_param('SignName', "逸沣安全培训")
-    request.add_query_param('TemplateCode', "SMS_192150140")
-    request.add_query_param('TemplateParam', json.dumps({"subject_item":subject_item}))
+    request.add_query_param('SignName', "顽主杯")
+    request.add_query_param('TemplateCode', "SMS_243482769")
+    request.add_query_param('TemplateParam', json.dumps({"code":code}))
     response = client.do_action(request)
+    print(response)
     return True,u"success"
 
-def send_unauthed_notice(phone):
-    """审核不通过短信通知
-    """
-    request = CommonRequest()
-    request.set_accept_format('json')
-    request.set_domain('dysmsapi.aliyuncs.com')
-    request.set_method('POST')
-    request.set_protocol_type('https') # https | http
-    request.set_version('2017-05-25')
-    request.set_action_name('SendSms')
-
-    request.add_query_param('RegionId', "cn-hangzhou")
-    request.add_query_param('PhoneNumbers', phone)
-    request.add_query_param('SignName', "逸沣安全培训")
-    request.add_query_param('TemplateCode', "SMS_192770033")
-    response = client.do_action(request)
-    return True,u"success"
+if __name__ == "__main__":
+    phone = "15982456282"
+    phone = "13883187629"
+    code = "1234"
+    send_verify_code(phone,code)
 
-def send_update_notice(phone,name,subject_item):
-    """证件复审通知
-    """
-    request = CommonRequest()
-    request.set_accept_format('json')
-    request.set_domain('dysmsapi.aliyuncs.com')
-    request.set_method('POST')
-    request.set_protocol_type('https') # https | http
-    request.set_version('2017-05-25')
-    request.set_action_name('SendSms')
-
-    request.add_query_param('RegionId', "cn-hangzhou")
-    request.add_query_param('PhoneNumbers', phone)
-    request.add_query_param('SignName', "逸沣安全培训")
-    request.add_query_param('TemplateCode', "SMS_192820850")
-    request.add_query_param('TemplateParam', json.dumps({"subject_item":subject_item,"name":name}))
-    response = client.do_action(request)
-    return True,u"success"
-
-def send_expired_notice(phone,name,subject_item):
-    """证件到期通知
-    """
-    request = CommonRequest()
-    request.set_accept_format('json')
-    request.set_domain('dysmsapi.aliyuncs.com')
-    request.set_method('POST')
-    request.set_protocol_type('https') # https | http
-    request.set_version('2017-05-25')
-    request.set_action_name('SendSms')
-
-    request.add_query_param('RegionId', "cn-hangzhou")
-    request.add_query_param('PhoneNumbers', phone)
-    request.add_query_param('SignName', "逸沣安全培训")
-    request.add_query_param('TemplateCode', "SMS_192820847")
-    request.add_query_param('TemplateParam', json.dumps({"subject_item":subject_item,"name":name}))
-    response = client.do_action(request)
-    return True,u"success"
-
-def send_pay_notice(phone):
-    """支付成功通知
-    """
-    request = CommonRequest()
-    request.set_accept_format('json')
-    request.set_domain('dysmsapi.aliyuncs.com')
-    request.set_method('POST')
-    request.set_protocol_type('https') # https | http
-    request.set_version('2017-05-25')
-    request.set_action_name('SendSms')
-
-    request.add_query_param('RegionId', "cn-hangzhou")
-    request.add_query_param('PhoneNumbers', phone)
-    request.add_query_param('SignName', "逸沣安全培训")
-    request.add_query_param('TemplateCode', "SMS_193240726")
-    response = client.do_action(request)
-    return True,u"success"
-
-def send_training_notice(phone,subject_item,begin_time,end_time):
-    """培训通知
-    """
-    request = CommonRequest()
-    request.set_accept_format('json')
-    request.set_domain('dysmsapi.aliyuncs.com')
-    request.set_method('POST')
-    request.set_protocol_type('https') # https | http
-    request.set_version('2017-05-25')
-    request.set_action_name('SendSms')
-
-    request.add_query_param('RegionId', "cn-hangzhou")
-    request.add_query_param('PhoneNumbers', phone)
-    request.add_query_param('SignName', "逸沣安全培训")
-    request.add_query_param('TemplateCode', "SMS_193240727")
-    request.add_query_param('TemplateParam', json.dumps({"subject_item":subject_item,"begin_time":begin_time,"end_time":end_time}))
-    response = client.do_action(request)
-    return True,u"success"
-
-def send_training_notice_special(phone,subject_item,begin_time):
-    """特种作业培训通知
-    """
-    request = CommonRequest()
-    request.set_accept_format('json')
-    request.set_domain('dysmsapi.aliyuncs.com')
-    request.set_method('POST')
-    request.set_protocol_type('https') # https | http
-    request.set_version('2017-05-25')
-    request.set_action_name('SendSms')
-
-    request.add_query_param('RegionId', "cn-hangzhou")
-    request.add_query_param('PhoneNumbers', phone)
-    request.add_query_param('SignName', "逸沣安全培训")
-    request.add_query_param('TemplateCode', "SMS_193230747")
-    request.add_query_param('TemplateParam', json.dumps({"subject_item":subject_item,"begin_time":begin_time}))
-    response = client.do_action(request)
-    return True,u"success"
-
-
-def send_exam_notice(phone,name,subject_item,exam_time):
-    """考试通知
-    """
-    request = CommonRequest()
-    request.set_accept_format('json')
-    request.set_domain('dysmsapi.aliyuncs.com')
-    request.set_method('POST')
-    request.set_protocol_type('https') # https | http
-    request.set_version('2017-05-25')
-    request.set_action_name('SendSms')
-
-    request.add_query_param('RegionId', "cn-hangzhou")
-    request.add_query_param('PhoneNumbers', phone)
-    request.add_query_param('SignName', "逸沣安全培训")
-    request.add_query_param('TemplateCode', "SMS_193235744")
-    request.add_query_param('TemplateParam', json.dumps({"name":name,"subject_item":subject_item,
-        "exam_time":exam_time,"phone":"15884997924"}))
-    response = client.do_action(request)
-    return True,u"success"

+ 118 - 11
src/weixin/control_auth.py

@@ -9,6 +9,18 @@ import common.common_functions as ccf
 import common.common_control as ccc
 from utils.aestool import aescbc
 
+def gen_code():
+    """
+    """
+    code = ""
+    while True:
+        code = "".join([str(random.choice([0,1,2,3,5,6,7,8,9])) for x in range(0,6)])
+        if cm.UserInfo.objects.filter(usercode=code).exists():
+            time.sleep(0.1)
+            continue
+        break
+    return code
+
 def add_wxauth_info(request):
     """
     """
@@ -31,17 +43,49 @@ def get_wxauth_info(request):
     """
     """
     #
-    uid = request.user.id
-    player = request.player
-    user = cm.UserInfo.objects.filter(id=uid).values().first()
-    user["nickname"] = user["username"]
-    player = request.player
-    if player and not player.fund:                                                            
-        user["need_fill"] = 1                                                      
-    else:                                                                          
-        user["need_fill"] = 0                                                      
+    uid = request.user.id if request.user else 0
+    openid = request.user.openid
+    player = request.player if request.player else None
+    user = cm.UserInfo.objects.filter(id=uid,openid=openid).order_by("-id").values().first()
+
+    player = cm.Player.objects.filter(user_id=user["id"]).first()
+
+    if player:
+        user["role"] = player.role
+        user["match_id"] = player.match_id
+    else:
+        user["role"] = 0
+        user["match_id"] = 0
+
     return user
 
+    #if not player:
+    #    #生产游客选手
+    #    cur_match = ccc.get_cur_match()
+    #    visit_group = cm.MatchGroup.objects.filter(match_id=cur_match.id,charge=0).first()
+    #    player,flag = cm.Player.objects.get_or_create(
+    #        user_id = uid, 
+    #        match_id = cur_match.id,
+    #        match_name = cur_match.name,
+    #        match_group = visit_group.id,
+    #        match_group_name = visit_group.name
+    #    )
+    #    player.username = request.user.nickname
+    #    player.usercode = request.user.usercode
+    #    player.save()
+
+    #if uid and player:
+    #    user = cm.UserInfo.objects.filter(id=uid).values().first()
+    #    user["nickname"] = user["username"]
+    #    if player and not player.fund:                                                            
+    #        user["need_fill"] = 0
+    #    else:                                                                          
+    #        user["need_fill"] = 0                                                      
+    #    user["role"] = player.role
+    #    return user
+    #else:
+    #    return {"role":0}
+
 
 
 def update_wxauth_info(request):
@@ -53,8 +97,6 @@ def update_wxauth_info(request):
     return user                                                                    
 
 
-
-
 def login_user(request):
     """
     """
@@ -85,5 +127,70 @@ def login_user(request):
     return {"id":user.id,"token":token}
 
 
+def login_user_v3(request):
+    """
+    """
+    info = request.json
+    openid = info.get('openid')
+    avatar = info.get('avatarUrl')
+    nickname = info.get('nickName')
+    phone = info.get('phone')
+    phcode = info.get('phcode')
+
+    if not openid:
+        raise ce.TipException(u"缺少openid!")
+
+    user = cm.UserInfo.objects.filter(openid=openid).first()
+    if not user:
+        #新用户
+        usercode = gen_code()
+        user,flag = cm.UserInfo.objects.get_or_create(
+            openid = openid,  
+        )
+
+        user.avatar  = avatar
+        user.nickname = nickname
+        user.usercode = usercode
+        user.is_bind = 1
+        user.username = nickname
+        user.save()
+
+        tstr = "{}_{}{}".format(user.id,time.time(),random.randint(100000,999999))
+        token = aescbc.encrypt(tstr)
+        return {"id":user.id,"token":token,"status":1,"role":user.role}
+    else:
+        role = user.role
+        if cm.Player.objects.filter(user_id=user.id).exists():
+            player = cm.Player.objects.filter(user_id=user.id).first()
+            role = player.role
+
+        if user.is_bind:
+            if not openid == user.openid:
+                raise ce.TipException(u"微信号与用户代码不配!")
+        if not user.phone:
+            user.phone = phone
+            user.save()
+            tstr = "{}_{}{}".format(user.id,time.time(),random.randint(100000,999999))
+            token = aescbc.encrypt(tstr)
+            #老用户未绑定手机
+            return {"id":user.id,"token":token,"status":2,"role":role}
+        else:
+            tstr = "{}_{}{}".format(user.id,time.time(),random.randint(100000,999999))
+            token = aescbc.encrypt(tstr)
+            #老用户已绑定手机
+            return {"id":user.id,"token":token,"status":3,"role":role}
+
+def bind_user_phone(request):
+    """
+    """
+    uid = request.user.id
+    qdata = request.json
+    phone = qdata.get("phone")
+    phcode = qdata.get("phcode")
+    orgcode = ccc.cache.get(phone)
+    if not phcode == orgcode:
+        raise ce.TipException(u"验证码不正确!")
+
+    cm.UserInfo.objects.filter(id=uid).update(phone=phone)
 
 

+ 597 - 86
src/weixin/controls.py

@@ -35,6 +35,7 @@ from xlutils.copy import copy
 from xltpl.writer import BookWriter
 from django_redis import get_redis_connection
 from constants import *
+from utils.aliyun_sms import send_verify_code
 
 def async(f):
     def wrapper(*args, **kwargs):
@@ -137,9 +138,7 @@ def get_player_match_list(request):
     """
     uid = request.user.id
     now_str = datetime.datetime.now().strftime("%Y-%m-%d")
-    cur_match_id = cm.Match.objects.filter(start_time__lte=now_str).first().id
-    cur_match_id = 7
-    #match_ids = list(cm.Player.objects.filter(user_id=uid,match_id=cur_match_id).values_list("match_id",flat=True))
+    cur_match_id = ccc.get_cur_match().id
     match_ids = list(cm.Player.objects.filter(user_id=uid).values_list("match_id",flat=True))
     matchs = list(cm.Match.objects.filter(id__in=match_ids).values())
     for item in matchs:
@@ -173,10 +172,17 @@ def get_player_match_detail(request):
     match_group = request.player.match_group
     match_id = qdata.get("id")
     record_id = qdata.get("record_id")
+    userinfo = {}
+    cur_user_id = user_id
     cur_player_id = qdata.get("player_id")
+    if not cur_player_id:
+        cur_player_id = player_id
 
     ismine = True if int(cur_player_id) == player_id else False
-
+    if not match_id:
+        player = cm.Player.objects.filter(id=cur_player_id).first()
+        match_id =player.match_id
+        cur_user_id = player.user_id 
     if record_id:
         records_set = cm.PlayerRecord.objects.filter(id=record_id)
         if records_set:
@@ -188,9 +194,7 @@ def get_player_match_detail(request):
         if qdata.get("player_id"):
             player_id = qdata.get("player_id")
             match_group = cm.Player.objects.filter(id=player_id).first().match_group
-        else:
-            #为了兼容老版本没有传player_id要获取用户的某届赛事player_id
-            player_id = cm.Player.objects.filter(user_id=user_id,match_id=match_id).first().id
+
         records_set = cm.PlayerRecord.objects.filter(player_id=player_id,match_id=match_id).order_by("-stock_date")
 
 
@@ -211,6 +215,7 @@ def get_player_match_detail(request):
         item["total_income"] = "{}%".format(item["total_income"]*100)
 
     today = get_today_date()
+    today_record = {}
     if records_set.first():
         today = records_set.first().stock_date
         #today_record = get_today_record(player_id,int(match_id),int(match_group),today)
@@ -234,8 +239,15 @@ def get_player_match_detail(request):
             today_record["today_stock_total"] = round(today_record["today_stock_total"],4)
             userinfo = get_user_info(today_record["user_id"])
             today_record["style"] = userinfo.get("style")
-    else:
+    if not today_record:
+        player_info = get_player_info(cur_player_id)
         today_record = {}
+        today_record["stock_date"] = today
+        today_record.update(player_info)
+        today_record["player_id"] = player_info["id"]
+        today_record["avatar"] = get_user_info(player_info["user_id"])["avatar"]
+        userinfo = get_user_info(today_record["user_id"])
+        today_record["style"] = userinfo.get("style")
 
     match["groups"] = [today_record["match_group_name"]] if today_record else []
     records = sorted(records,key=lambda x:x["stock_date"],reverse=True)
@@ -243,7 +255,19 @@ def get_player_match_detail(request):
         is_follow = 1
     else:
         is_follow = 0
-    ret = {"match":match,"today_record":today_record,"records":records,"is_follow":is_follow}
+    if today_record and today_record.get("today_fund"):
+        today_record["today_income_fund"] = round((today_record["today_fund"] - today_record["yesterday_fund"]),2)*10000 if today_record["yesterday_fund"] else 0.00
+    ret = {
+        "match":match,
+        "today_record":today_record,
+        "userinfo":get_user_info(cur_user_id),
+        "records":records,
+        "is_follow":is_follow,
+        "fans":cm.UserFollows.objects.filter(follow_id=player_id).count(),
+        "followers":cm.UserFollows.objects.filter(user_id=player_id).count(),
+        "stock_age":datetime.datetime.now().year - int(userinfo.get("join_time")) if userinfo.get("join_time") else 0,
+        "stock_follow":list(cm.UserChoice.objects.filter(player_id=cur_player_id).values_list("stock_name",flat=True))
+        }
 
     return ret
 
@@ -287,6 +311,8 @@ def get_today_record_actual(player_id,match_id,match_group,today=None):
     else:
         qset = cm.PlayerRecord.objects.filter(match_id=match_id,match_group=match_group,player_id=player_id).order_by("-stock_date")
     today_record = qset.values().first()
+    print(player_id,match_id,match_group,today,4444444444444444444)
+    print(today_record,1111111111)
 
     if today_record:
         user_info = get_user_info(today_record["user_id"])
@@ -417,6 +443,12 @@ def get_user_info(uid):
             user["style"].append(user["df"])
     return user
 
+
+#@ccc.cache_data()
+def get_player_info(player_id):
+    player = cm.Player.objects.filter(id=player_id).values().first()
+    return player
+
 @ccc.cache_data()
 def get_match_info(match_id):
     match = cm.Match.objects.filter(id=match_id).values().first()
@@ -520,12 +552,6 @@ def get_player_match_records(request):
     page = int(qdata.get("page",0))
     page_size = int(qdata.get("page_size",20))
 
-    #if int(match_id) == 7:
-    #    player = cm.Player.objects.filter(match_id=match_id,usercode=request.user.usercode).first()
-    #    if player:
-    #        player_id = player.id
-    #        match_group = player.match_group
-
     records_set = cm.PlayerRecord.objects.filter(player_id=player_id,match_id=match_id).order_by("-stock_date")
     data = list(records_set.values())
     for item in data:
@@ -539,6 +565,8 @@ def get_player_match_records(request):
         item["yesterday_stock_img"] = json.loads(item["yesterday_stock_img"]) if item["yesterday_stock_img"] else []
         item["today_income"] = "{}%".format(item["today_income"]*100)
         item["total_income"] = "{}%".format(item["total_income"]*100)
+        item["zans_count"] = item.get("zans",0)
+        item["comments_count"] = cm.Comments.objects.filter(player_id=item.get("id",0)).count()
     if page and page_size:
         total,data = ccf.get_page_list(data,page,page_size)
         return total,data
@@ -577,6 +605,7 @@ def add_model(cls,**kwargs):
         if now_time<"15:00" or now_time > "23:50":
             raise ce.TipException(u"当日数据请在当日15:00以后23:50以前提交数据!")
 
+
         match_id = kwargs.get("match_id")
         stock_date = kwargs.get("stock_date")
         #stock_date = "2022-04-26"
@@ -588,6 +617,9 @@ def add_model(cls,**kwargs):
         is_markt = int(kwargs.get("is_markt",0))
         experience = kwargs.get("experience")
 
+        if not ccc.get_cur_match().id == match_id:
+            raise ce.TipException(u"非当前赛事参赛选手不能提交作业!")
+
         #计算今日和昨日盈亏
         if float(today_fund)>9999 or float(today_fund)<=0:                              
             raise ce.TipException(u"数据错误,今日净资产不能超过9999万元,不能低于0万元,请仔细核对数据!")                                                                                                                                                                       
@@ -773,25 +805,10 @@ def get_detail_info(cls,**kwargs):
     id = kwargs.get("id")
     rst = list(model.objects.filter(id=id).values())
     rst = rst[0] if rst else {}
-    #if model_name == "PlayerRecord":
-    #    rst["match_group"] = cm.MatchGroup.objects.filter(id=rst["match_group"]).first().name
-    #    today_stock = json.loads(rst["today_stock"]) if rst["today_stock"] else []
-    #    today_stock = filter(lambda x:x["fund"] and x["name"],today_stock if today_stock else [])
-    #    rst["today_stock"] = today_stock
-    #    today_stock_img = json.loads(rst["today_stock_img"]) if rst["today_stock_img"] else []
-    #    rst["today_stock_img"] = today_stock_img
-
-    #    yesterday_stock = json.loads(rst["yesterday_stock"]) if rst["yesterday_stock"] else []
-    #    yesterday_stock = filter(lambda x:x["fund"] and x["name"],yesterday_stock if yesterday_stock else [])
-    #    rst["yesterday_stock"] = yesterday_stock
-    #    
-    #    yesterday_stock_img = json.loads(rst["yesterday_stock_img"]) if rst["yesterday_stock_img"] else []
-    #    rst["yesterday_stock_img"] = yesterday_stock_img
-
-    #    rst["today_income"] = "{}%".format(rst["today_income"]*100)
-    #    rst["total_income"] = "{}%".format(rst["total_income"]*100)
     if model_name == "Article":
         rst["ctime"] = ccf.datetime_to_str(rst["ctime"],"%Y-%m-%d")
+    if model_name == "Stock":
+        rst["choiced"] = cm.UserChoice.objects.filter(player_id=kwargs.get("player_id"),stock_id=rst["id"]).count()
     return rst
 
 #@ccc.cache_data()
@@ -1023,8 +1040,8 @@ def calc_win_rate(player_id,match_id):
     return win_rate
 
 
-def get_user_follows(request):
-    """获取用户关注的选手列表
+def get_user_follower(request):
+    """获取用户列表
     """
     user_id = request.player.id
     match_id = request.player.match_id
@@ -1033,36 +1050,31 @@ def get_user_follows(request):
     cur_match_group = match_group
     qdata = request.json
     today = get_today_date()
+    if qdata.get("player_id"):
+        user_id = int(qdata.get("player_id"))
 
     data = []
 
-    qset = cm.UserFollows.objects.filter(user_id=user_id)
-    follow_ids = list(qset.values_list("follow_id",flat=True))
+    qset = cm.UserFollows.objects.filter(follow_id=user_id)
+    follow_ids = list(qset.values_list("user_id",flat=True))
 
     _today = today
     for player_id in follow_ids:
         _match_id = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"match_id")
 
-        #last = cm.PlayerRecord.objects.filter(player_id=player_id,match_id=_match_id).order_by("-stock_date").first()
-        #if last:
-        #    _today = last.stock_date
-
         _today = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"stock_date")
         _match_group = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"match_group")
 
         today_record = get_today_record(player_id,_match_id,_match_group,_today)
-        #today_record = get_today_record_actual(player_id,_match_id,_match_group,_today)
         if today_record:
             data.append(today_record)
 
     data = sorted(data,key=lambda x:x["stock_date"],reverse=True)
 
 
-    _match_id = ccc.cache.hget("PLAYER_LATEST_%d"%user_id,"match_id")
     _today = ccc.cache.hget("PLAYER_LATEST_%d"%user_id,"stock_date")
-    _match_group = ccc.cache.hget("PLAYER_LATEST_%d"%user_id,"match_group")
 
-    cur_today_record = get_today_record(user_id,_match_id,_match_group,_today)
+    cur_today_record = get_today_record(user_id,match_id,match_group,_today)
     if cur_today_record:
         data.insert(0,cur_today_record)
 
@@ -1090,6 +1102,74 @@ def get_user_follows(request):
         return len(data),data
 
 
+def get_user_follows(request):
+    """获取用户关注的选手列表
+    """
+    if request.player:
+        user_id = request.player.id
+        match_id = request.player.match_id
+        cur_match_id = match_id
+        match_group = request.player.match_group
+        cur_match_group = match_group
+        qdata = request.json
+        today = get_today_date()
+        if qdata.get("player_id"):
+            user_id = int(qdata.get("player_id"))
+
+        data = []
+
+        qset = cm.UserFollows.objects.filter(user_id=user_id)
+        follow_ids = list(qset.values_list("follow_id",flat=True))
+
+        _today = today
+        for player_id in follow_ids:
+            _match_id = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"match_id")
+
+            _today = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"stock_date")
+            _match_group = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"match_group")
+
+            today_record = get_today_record(player_id,_match_id,_match_group,_today)
+            if today_record:
+                today_record["zan_count"] = today_record.get("zans",0) if today_record.get("zans") else 0
+                today_record["comments_count"] = cm.Comments.objects.filter(record_id=today_record.get("id",0)).count()
+                data.append(today_record)
+
+        data = sorted(data,key=lambda x:x["stock_date"],reverse=True)
+
+
+        _today = ccc.cache.hget("PLAYER_LATEST_%d"%user_id,"stock_date")
+
+        cur_today_record = get_today_record(user_id,match_id,match_group,_today)
+        if cur_today_record:
+            data.insert(0,cur_today_record)
+
+        page = int(qdata.get("page",1))
+        page_size = int(qdata.get("page_size",20))
+
+        if page and page_size:
+            total,data = ccf.get_page_list(data,page,page_size)
+            for item in data:
+                if item:
+                    today_stock = json.loads(item["today_stock"])
+                    today_stock = filter(lambda x:x["name"] and x["fund"],today_stock)
+                    item["today_stock"] = today_stock
+                    item["today_stock_img"] = json.loads(item["today_stock_img"])
+                    #item["win_rate"] = calc_win_rate(item["player_id"],item["match_id"])
+                    win_rate = ccc.cache.hget("PLAYER_LATEST_%d"%item["player_id"],"win_rate")
+                    if win_rate:
+                        item["win_rate"] = str(float(win_rate)*100)+"%"
+                    else:
+                        item["win_rate"] = "0.0%"
+                    item["today_income"] = "{}%".format(item["today_income"]*100)
+                    item["total_income"] = "{}%".format(item["total_income"]*100)
+                    item["zans_count"] = item.get("zans",0)
+                    item["comments_count"] = cm.Comments.objects.filter(record_id=item.get("id",0)).count()
+            return total,data
+        else:
+            return len(data),data
+    return 0,[]
+
+
 def get_hot_stock_rank(**kwargs):
     """
     """
@@ -1103,7 +1183,7 @@ def get_hot_stock_rank(**kwargs):
         }
     return data
 
-@ccc.cache_data()
+#@ccc.cache_data()
 def get_stock_info(stock_id):
     """
     """
@@ -1117,17 +1197,19 @@ def get_hot_stock_buy(**kwargs):
     """
     """
     stock_date = kwargs.get("stock_date")
-    qset = cm.UserStock.objects.filter(stock_date=stock_date)
-    if kwargs.get("name"):
-        qset = qset.filter(stock_name__icontains=kwargs.get("name"))
+    if not kwargs.get("name"):
+        qset = cm.UserStock.objects.filter(stock_date=stock_date)
+    else:
+        qset = cm.UserStock.objects.filter(stock_name__icontains=kwargs.get("name"))
     qset = qset.values("stock_id","stock_name").annotate(count=Count("stock_id")).order_by("-count")
     data = []
     for q in qset:
         stock_id = q["stock_id"]
         count = q["count"]
         stock_name = q.get("stock_name")
-        #stock = get_stock_info(stock_id)
-        data.append({"stock_name":stock_name,"id":stock_id,"count":count})
+        comments_count = cm.Comments.objects.filter(stock_id=stock_id).count()
+        choice_count = cm.UserChoice.objects.filter(stock_id=stock_id).count()
+        data.append({"stock_name":stock_name,"id":stock_id,"count":count,"comments_count":comments_count,"choice_count":choice_count})
 
 
     page = int(kwargs.get("page",1))
@@ -1145,8 +1227,7 @@ def get_hot_follow(**kwargs):
     """
     """
     stock_date = kwargs.get("stock_date")
-    #qset = cm.UserFollows.objects.filter(stock_date=stock_date)
-    cur_match_id = 9
+    cur_match_id = ccc.get_cur_match().id
     player_ids = list(cm.Player.objects.filter(match_id=cur_match_id,match_status=1).values_list("id",flat=True))
     qset = cm.UserFollows.objects.filter(follow_id__in=player_ids)
     qset = qset.values("follow_id").annotate(count=Count("follow_id")).order_by("-count")
@@ -1289,18 +1370,11 @@ def get_win_rate_rank(request):
     match_id = request.player.match_id
     match_group = request.player.match_group
     kwargs = request.json
-    match_id = 9
+    match_id = ccc.get_cur_match().id
 
     qset = cm.WinDefendRank.objects.filter(match_id=match_id,auto_complete__lt=5,match_status=1).order_by("-win_rate")
     data = list(qset.values())
 
-    #data = []
-    #for item in datas:
-    #    player = cm.Player.objects.filter(id=item["player_id"]).first()
-    #    item["match_status"] = player.match_status if player else -1
-    #    if item["match_status"] > -1:
-    #        data.append(item)
-
     page = int(kwargs.get("page",1))
     page_size = int(kwargs.get("page_size",20))
 
@@ -1520,6 +1594,7 @@ def get_mine_style(request):
         "df":user.df,     
         "pz":user.pz,     
         "join_time":user.join_time,     
+        "username":user.username,     
         "account_img":user.account_img,     
     }
     if player:
@@ -1540,6 +1615,9 @@ def update_user_style(**kwargs):
     cm.UserInfo.objects.filter(id=user_id).update(**kwargs)
     if player_id and init_fund:
         cm.Player.objects.filter(id=player_id).update(fund=init_fund)
+    if kwargs.get("username"):
+        cm.Player.objects.filter(id=player_id).update(username=kwargs.get("username"))
+    ccc.cache.delete("cdata_get_user_info_(%sL,)"%user_id)
     return True
 
 
@@ -1547,9 +1625,11 @@ def get_stock_players(**kwargs):
     """
     """
     stock_id = kwargs.get("stock_id")
-    stock_date = kwargs.get("stock_date")
+    stock_date = kwargs.get("stock_date",get_today_date())
     data = []
     user_stocks = cm.UserStock.objects.filter(stock_id=stock_id,stock_date=stock_date)
+    #user_stocks = cm.UserStock.objects.filter(stock_id=stock_id)
+    #user_stocks = cm.UserStock.objects.all()
     for us in user_stocks:
         player_id = us.player_id
         player = cm.Player.objects.filter(id=player_id).first()
@@ -1568,7 +1648,7 @@ def get_stock_players(**kwargs):
                 data.append(today_record)
     data = sorted(data,key=lambda x:x["today_fund"],reverse=True)
     #分页
-    page = int(kwargs.get("page",1))
+    page = int(kwargs.get("page",0))
     page_size = int(kwargs.get("page_size",20))
     if page and page_size:
         total,data = ccf.get_page_list(data,page,page_size)
@@ -1661,32 +1741,28 @@ def get_stock_search(**kwargs):
 def get_mine_latest(request):
     """
     """
-    player_id = request.player.id
-    match_id = request.player.match_id
-    match_group = request.player.match_group
-
-    today = get_today_date()
-
-    _match_id = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"match_id")
-    if _match_id:
-        match_id = _match_id
-    _today = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"stock_date")
-    if _today:
-        today = _today
-    _match_group = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"match_group")
-    if _match_group:
-        match_group = _match_group
-
-    #rst = get_today_record(player_id,match_id,match_group,today)
     try:
-        rst = get_today_record_actual(player_id,match_id,match_group)
-
-        rst["today_income"] = "{}%".format(rst["today_income"]*100)
-        rst["total_income"] = "{}%".format(rst["total_income"]*100)
+        player_id = request.player.id
+        match_id = request.player.match_id
+        match_group = request.player.match_group
+        today = get_today_date()
 
-        return rst
-    except:
-        return {}
+        _today = ccc.cache.hget("PLAYER_LATEST_%d"%player_id,"stock_date")
+        if _today:
+            today = _today
+            rst = get_today_record_actual(player_id,match_id,match_group)
+            if rst:
+                rst["today_income_fund"] = round((rst["today_fund"] - rst["yesterday_fund"]),2)*10000 if rst["yesterday_fund"] else 0.00
+
+            rst["today_income"] = "{}%".format(rst["today_income"]*100)
+            rst["total_income"] = "{}%".format(rst["total_income"]*100)
+            rst["zan_count"] = rst.get("zans",0)
+            rst["comments_count"] = cm.Comments.objects.filter(record_id=rst.get("id",0)).count()
+
+            return rst
+    except Exception as e:
+        print(e)
+        return {"player_id":player_id,"match_id":match_id}
 
 def get_match_validdates(match_id):
     """
@@ -1700,3 +1776,438 @@ def get_match_validdates(match_id):
         validdates.sort()
         return validdates
     return []
+
+
+def send_phcode(request):
+    """
+    """
+    qdata = request.json
+    phone = qdata.get("phone")
+    import random
+    code = "%s%s%s%s" % (random.randint(0,9),random.randint(0,9),random.randint(0,9),random.randint(0,9))
+    send_verify_code(phone,code)
+    ccc.cache.set(phone,code,120)
+    pass
+
+
+def get_signup_info(request):
+    """
+    """
+    qdata = request.json
+    user_id = request.user.id
+    try:
+        match_id = ccc.get_signup_match().id
+        match = cm.Match.objects.filter(id=match_id).values().first()
+        #
+        match_groups = list(cm.MatchGroup.objects.filter(match_id=match_id,is_active=1).values())
+        match["groups"] = match_groups
+        #报名状态
+        now = datetime.datetime.now().strftime("%Y-%m-%d")
+        signup_start_time = match["signup_start_time"]
+        signup_end_time = match["signup_end_time"]
+        if signup_start_time <= now and signup_end_time >= now:
+            match["signup_status"] = 1
+        else:
+            match["signup_status"] = 0
+
+        if cm.Player.objects.filter(match_id=match_id,user_id=user_id).exists():
+            match["signup_done"] = 1
+        else:
+            match["signup_done"] = 0
+        
+        return match
+    except Exception as e:
+        print(e)
+        return {}
+
+def do_wx_pay(request):
+    """
+    """
+    user = request.user
+    user_id = request.user.id
+    user_name = request.user.username
+    openid = request.user.openid
+    qdata = request.json
+
+    match_id = qdata.get("match_id")
+    match_name = qdata.get("match_name")
+    signup_type = qdata.get("signup_type")
+    total_fee = str(qdata.get("total_fee"))
+    phone = qdata.get("phone")
+    phcode = qdata.get("phcode")
+    match_group = qdata.get("match_group")
+
+    orgcode = ccc.cache.get(phone)
+    if not phcode == orgcode and False:
+        raise ce.TipException(u"验证码不正确!")
+
+    if int(signup_type) == 1:
+        if not match_group:
+            match_group = cm.MatchGroup.objects.filter(match_id=match_id,charge=0).first().id
+
+    out_trade_no = datetime.datetime.now().strftime("%Y%m%d%H%M%S") + str(int(time.time()*1000))
+
+    phone_user = cm.UserInfo.objects.filter(phone=phone).first()
+    phone_user_player_type = phone_user.player_type if phone_user else 0
+    #游客和普通选手付费报名
+    if user.player_type in [0,1] and phone_user_player_type in [0,1]:
+        #生成订单
+        order = cm.SignupOrder.objects.create(
+            user_id = user_id, 
+            user_name = user_name,
+            match_id = match_id,
+            match_name = match_name,
+            signup_type = signup_type,
+            total_fee = total_fee,
+            out_trade_no = out_trade_no,
+            phone = phone,
+            match_group = match_group
+        )
+        #支付
+        total_fee = str(int(float(order.total_fee)*100))
+        prepayinfo = wxpay.get_wx_unifiedorder(out_trade_no,total_fee,openid)
+        prepayinfo["key"] = wxpay.WxPayConf_pub.KEY
+        return prepayinfo
+    #种子选手和开户选手免费报名
+    else:
+        #生成订单
+        order = cm.SignupOrder.objects.create(
+            user_id = user_id, 
+            user_name = user_name,
+            match_id = match_id,
+            match_name = match_name,
+            signup_type = signup_type,
+            total_fee = total_fee,
+            out_trade_no = out_trade_no,
+            phone = phone,
+            match_group = match_group
+        )
+        if phone_user:
+            if user.id != phone_user.id:
+                user.phone = phone
+                user.player_type = phone_user_player_type
+                user.save()
+        #生成选手信息
+        cur_match = cm.Match.objects.filter(id=match_id).first()
+        visit_group = cm.MatchGroup.objects.filter(id=match_group).first()
+        player,flag = cm.Player.objects.get_or_create(
+            user_id = user.id, 
+            match_id = cur_match.id,
+            match_name = cur_match.name,
+            match_group = visit_group.id,
+            match_group_name = visit_group.name
+        )
+        player.username = request.user.nickname
+        player.usercode = request.user.usercode
+        player.role = 2
+        player.match_status = 1
+        player.save()
+        #
+        order.player_id = player.id
+        order.save()
+        if phone_user:
+            cm.UserInfo.objects.filter(id=phone_user.id).delete()
+            cm.Player.objects.filter(user_id=phone_user.id).delete()
+        return {"free":1}
+
+
+def do_wxpay_notify(request):                                                      
+    qdata = request.json                                                           
+    flag,res = wxpay.check_notify_valid(request.body)                              
+    if flag:                                                                       
+        out_trade_no = res.get("out_trade_no")                                     
+        transaction_id = res.get("transaction_id")                                 
+        pay_time = res.get("time_end")                                             
+        try:
+            sorder = cm.SignupOrder.objects.filter(out_trade_no=out_trade_no).first()
+            if sorder and not sorder.transaction_id:                                   
+                now = datetime.datetime.now()                                          
+                cm.SignupOrder.objects.filter(out_trade_no=out_trade_no).update(order_status=1,pay_status=1,pay_time=now,transaction_id=transaction_id)                                  
+                #生成选手信息
+                user = cm.UserInfo.objects.filter(id=sorder.user_id).first()
+                visit_group = cm.MatchGroup.objects.filter(id=sorder.match_group).first()
+                player,flag = cm.Player.objects.get_or_create(
+                    user_id = sorder.user_id, 
+                    match_id = sorder.match_id,
+                    match_name = sorder.match_name,
+                    match_group = visit_group.id,
+                    match_group_name = visit_group.name
+                )
+                player.username = user.nickname
+                player.usercode = user.usercode
+                if sorder.signup_type == 2:
+                    player.role = 2
+                else:
+                    player.role = 1
+                player.save()
+                #
+                sorder.player_id = player.id
+                sorder.save()
+
+                if sorder.signup_type == 2:
+                    player_id = sorder.player_id
+                    user_id = sorder.user_id
+                    match_group = sorder.match_group
+                    match_group_name = cm.MatchGroup.objects.filter(id=match_group).first().name
+                    cm.Player.objects.filter(id=player_id).update(role=2,match_group=match_group,match_group_name=match_group_name,match_status=1)
+                    cm.UserInfo.objects.filter(id=user_id).update(role=2)
+                if sorder.signup_type == 1:
+                    player_id = sorder.player_id
+                    user_id = sorder.user_id
+                    cm.Player.objects.filter(id=player_id).update(role=1,match_status=1)
+                    cm.UserInfo.objects.filter(id=user_id).update(role=1)
+                return True                                                            
+        except Exception as e:
+            import traceback
+            traceback.print_exc()
+            print(e,111111111111)
+    return False                                                                   
+
+def get_user_comments(request):
+    """
+    """
+    player_id = request.player.id
+    kwargs = request.json
+    stock_id = kwargs.get("stock_id")
+    record_id = kwargs.get("record_id")
+    if record_id:
+        qset = cm.Comments.objects.filter(record_id=record_id,pid__isnull=True)
+    else:
+        qset = cm.Comments.objects.filter(stock_id=stock_id,pid__isnull=True)
+    data = list(qset.values())
+    for item in data:
+        if record_id:
+            subcomments = cm.Comments.objects.filter(record_id=record_id,pid=item["id"])
+        else:
+            subcomments = cm.Comments.objects.filter(stock_id=stock_id,pid=item["id"])
+        item["children"] = list(subcomments.values())
+
+    #分页
+    page = int(kwargs.get("page",1))
+    page_size = int(kwargs.get("page_size",20))
+    if page and page_size:
+        total,data = ccf.get_page_list(data,page,page_size)
+    else:
+        total = len(data)
+
+    return total,data
+
+
+def get_user_choices(request):
+    """
+    """
+    player_id = request.player.id
+    kwargs = request.json
+    stock_id = kwargs.get("stock_id")
+    qset = cm.UserChoice.objects.filter(player_id=player_id)
+
+    data = list(qset.values())
+    for item in data:
+        stock_id = item["stock_id"]
+        item["count"] = cm.UserChoice.objects.filter(player_id=player_id,stock_id=stock_id).count()
+        comments_count = cm.Comments.objects.filter(stock_id=stock_id).count()
+        choice_count = cm.UserChoice.objects.filter(stock_id=stock_id).count()
+        item["comments_count"] = comments_count
+        item["choice_count"] = choice_count
+        player_id = item["player_id"]
+        match_id = cm.Player.objects.filter(id=player_id).first().match_id
+        item["match_id"] = match_id
+
+    #分页
+    page = int(kwargs.get("page",1))
+    page_size = int(kwargs.get("page_size",20))
+    if page and page_size:
+        total,data = ccf.get_page_list(data,page,page_size)
+    else:
+        total = len(data)
+
+    return total,data
+
+
+def get_user_consult_list(request):
+    """
+    """
+    player_id = request.user.id if request.user else 0
+    if not player_id:
+        return 0,[]
+    kwargs = request.json
+    record_id = kwargs.get("record_id")
+    qset = cm.WanzhuConsult.objects.filter(user_id=player_id)
+    data = list(qset.values())
+    for item in data:
+        if cm.WanzhuConsult.objects.filter(pid=item["id"]):
+            data.extend(cm.WanzhuConsult.objects.filter(pid=item["id"]).values())
+
+    data = sorted(data,key=lambda x:x["id"])
+    #分页
+    page = int(kwargs.get("page",1))
+    page_size = int(kwargs.get("page_size",20))
+    if page and page_size:
+        total,data = ccf.get_page_list(data,page,page_size)
+    else:
+        total = len(data)
+
+    return total,data
+
+def get_user_zans(request):
+    """
+    """
+    player_id = request.player.id
+    qdata = request.json
+    record_id = qdata.get("record_id")
+    zans = cm.PlayerRecord.objects.filter(id=record_id).first().zans
+    return zans
+
+
+def get_user_comments_count(request):
+    """
+    """
+    player_id = request.player.id
+    qdata = request.json
+    record_id = qdata.get("record_id")
+    count = cm.Comments.objects.filter(record_id=record_id).count()
+    return count
+
+
+def add_wanzhu_consult(request):
+    """咨询顽主
+    """
+    user_id = request.user.id
+    user_name = request.user.username
+    user_avatar = request.user.avatar
+    qdata = request.json
+    content = qdata.get("cotent")
+    pid = qdata.get("pid")
+
+    obj = cm.WanzhuConsult.objects.create(
+        user_id = user_id, 
+        user_name = user_name, 
+        user_avatar = user_avatar, 
+        content = content,
+        pid = pid
+    )
+
+
+def add_comments(request):
+    """
+    """
+    user_id = request.user.id
+    user_name = request.user.username
+    player_id = request.player.id
+    user_avatar = request.user.avatar
+    qdata = request.json
+    content = qdata.get("cotent")
+    record_id = qdata.get("record_id")
+    stock_id = qdata.get("stock_id")
+    pid = qdata.get("pid")
+
+    ip = ccf.get_ip(request)
+    location = ccf.get_city_from_ip(ip)
+
+    obj = cm.Comments.objects.create(
+        user_id = user_id, 
+        user_name = user_name, 
+        user_avatar = user_avatar, 
+        player_id = player_id, 
+        content = content,
+        record_id = record_id,
+        pid = pid,
+        location = location,
+        stock_id = stock_id
+    )
+    cm.PlayerRecord.objects.filter(id=record_id).update(comments_count=F("comments_count")+1)
+
+
+def add_choice(request):
+    """
+    """
+    user_id = request.user.id
+    user_name = request.user.username
+    player_id = request.player.id
+    user_avatar = request.user.avatar
+    qdata = request.json
+    stock_id = qdata.get("stock_id")
+    stock_name = cm.Stock.objects.filter(id=stock_id).first().name
+
+    obj = cm.UserChoice.objects.create(
+        user_id = user_id, 
+        user_name = user_name, 
+        user_avatar = user_avatar, 
+        player_id = player_id, 
+        stock_id = stock_id,
+        stock_name = stock_name
+    )
+
+
+def add_zan(request):
+    """
+    """
+    qdata = request.json
+    record_id = qdata.get("record_id")
+    cm.PlayerRecord.objects.filter(id=record_id).update(zans=F("zans")+1)
+    zans = cm.PlayerRecord.objects.filter(id=record_id).first().zans
+    return zans
+
+def get_experience_list(request):
+    """
+    """
+    kwargs = request.json
+    today = get_today_date()
+    match_id = ccc.get_cur_match().id
+    qset = cm.PlayerRecord.objects.filter(match_id=match_id,experience__isnull=False,stock_date=today).order_by("-total_income")[:100]
+
+    data = list(qset.values("id","user_id","username","today_fund","today_income","total_income","experience","match_id","player_id","today_stock_img","zans","comments_count"))
+
+    for item in data:
+        record_id = item["id"]
+        user_id = item["user_id"]
+        #user = cm.UserInfo.objects.filter(id=user_id).first()
+        #if user:
+        #    item["user_avatar"] = user.avatar
+        #    #item["comments_count"] = cm.Comments.objects.filter(record_id=record_id).count()
+        try:
+            item["user_avatar"] = get_user_info(user_id)["avatar"]
+        except:
+            item["user_avatar"] = ""
+        #item["comments_count"] = 0
+        item["zans_count"] = item["zans"]
+        item["today_stock_img"] = json.loads(item["today_stock_img"])
+        #item["win_rate"] = calc_win_rate(item["player_id"],item["match_id"])
+        win_rate = ccc.cache.hget("PLAYER_LATEST_%d"%item["player_id"],"win_rate")
+        if win_rate:
+            item["win_rate"] = str(float(win_rate)*100)+"%"
+        else:
+            item["win_rate"] = "0.0%"
+        item["today_income"] = "{}%".format(item["today_income"]*100)
+        item["total_income"] = "{}%".format(item["total_income"]*100)
+
+    #分页
+    page = int(kwargs.get("page",1))
+    page_size = int(kwargs.get("page_size",20))
+    if page and page_size:
+        total,data = ccf.get_page_list(data,page,page_size)
+    else:
+        total = len(data)
+
+    return total,data
+
+
+def delete_choice(request):
+    """
+    """
+    user_id = request.user.id
+    user_name = request.user.username
+    player_id = request.player.id
+    user_avatar = request.user.avatar
+    qdata = request.json
+    stock_id = qdata.get("stock_id")
+
+    cm.UserChoice.objects.filter(stock_id=stock_id,player_id=player_id).delete()
+
+
+def delete_comments(request):
+    """
+    """
+    qdata = request.json
+    id = qdata.get("id")
+    cm.Comments.objects.filter(id=id).delete()

+ 19 - 0
src/weixin/urls_backstage.py

@@ -48,6 +48,25 @@ urlpatterns = [
     url(r'^v3/user/follow$', views.FollowUserView.as_view()),
     url(r'^v2/mine/latest$', views.MineLatestView.as_view()),
     url(r'^v2/match/validdates$', views.MatchValidDatesView.as_view()),
+    #3.0
+    url(r'^v3/login$', views.V3LoginView.as_view()),
+    url(r'^v3/phone/code$', views.V3PhoneCodeView.as_view()),
+    url(r'^v3/phone/bind$', views.V3PhoneBindView.as_view()),
+    url(r'^v3/signup/info$', views.V3SignupInfoView.as_view()),
+    url(r'^v3/signup/wxpay$', views.V3SignupWXPayView.as_view()),
+    url(r'^v3/signup/notify$', views.V3SignupNotifyView.as_view()),
+    url(r'^v3/user/comments/list$', views.V3CommentsListView.as_view()),
+    url(r'^v3/user/comments$', views.V3CommentsView.as_view()),
+    url(r'^v3/user/comments/count$', views.V3CommentsCountView.as_view()),
+    url(r'^v3/user/zan$', views.V3ZanView.as_view()),
+    url(r'^v3/user/zancount$', views.V3ZanCountView.as_view()),
+    url(r'^v3/user/consult$', views.V3WanzhuConsultView.as_view()),
+    url(r'^v3/user/consult/list$', views.V3WanzhuConsultListView.as_view()),
+    url(r'^v3/experience/list$', views.V3ExperienceView.as_view()),
+    url(r'^v3/user/fund/modify$', views.V3UserFundModifyView.as_view()),
+    url(r'^v3/user/choice/list$', views.V3UserChoiceListView.as_view()),
+    url(r'^v3/user/choice$', views.V3UserChoiceView.as_view()),
+    url(r'^v3/user/follower/list$', views.FollowerUserListView.as_view()),
 
 ]
 

+ 270 - 1
src/weixin/views.py

@@ -386,6 +386,19 @@ class FollowUserListView(cv.AuthView):
             return cv.to_fail(e)
 
 
+class FollowerUserListView(cv.AuthView):                                                    
+    def get(self, request):                                                        
+        """#我的粉丝(3.0小程序)                                                       
+        """                                                                        
+        try:                                                                       
+            total,rst = ctl.get_user_follower(request)                                 
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e:                                                     
+            cv.tracefail()                                                         
+            return cv.to_fail(e)
+
+
+
 class HotStockListView(cv.AuthView):                                                    
     def get(self, request):                                                        
         """#热门股票(2.0小程序)                                                       
@@ -619,7 +632,7 @@ class MineStyleView(cv.AuthView):
         if mse:                                                                    
             raise ce.TipException(mse)                                             
         try:                                                                       
-            need_params.extend(["init_fund","account_img"])
+            need_params.extend(["init_fund","account_img","username"])
             vals = ccf.get_need_params(*need_params,**qdata)                       
             vals["user_id"] = request.user.id
             vals["player_id"] = request.player.id if request.player else None
@@ -761,3 +774,259 @@ class MatchValidDatesView(cv.AuthView):
         except Exception as e:                                                     
             cv.tracefail()                                                         
             return cv.to_fail(e)
+
+
+class V3LoginView(cv.BaseView):
+    def post(self, request):
+        """#小程序登录v3(3.0小程序)
+        @nickanme:"微信昵称"
+        @avatar:"微信头像"
+        @openid:"openid"
+        """
+        try:
+            rst = ca.login_user_v3(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+class V3PhoneCodeView(cv.BaseView):
+    def post(self, request):
+        """#短信验证码接口v3(3.0小程序)
+        @phone:"手机号"
+        """
+        try:
+            rst = ctl.send_phcode(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+class V3PhoneBindView(cv.AuthView):
+    def post(self, request):
+        """#手机绑定接口v3(3.0小程序)
+        @phone:"手机号"
+        @phcode:"验证码"
+        """
+        try:
+            rst = ca.bind_user_phone(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+class V3SignupInfoView(cv.AuthView):
+    def get(self, request):
+        """#报名信息v3(3.0小程序)
+        """
+        try:
+            rst = ctl.get_signup_info(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+class V3SignupWXPayView(cv.AuthView):
+    def post(self, request):
+        """#微信支付v3(3.0小程序)
+        @match_id:1,
+        @match_name:"测试比赛",
+        @signup_type:1,1/游客报名,2/选手报名,
+        @total_fee:10,总价
+        """
+        try:
+            rst = ctl.do_wx_pay(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class V3SignupNotifyView(cv.BaseView):
+    def post(self, request):
+        """#微信支付回调v3(3.0小程序)
+        @match_id:1,
+        @match_name:"",
+        @signup_type:1,1游客报名/2选手报名
+        @total_fee:200,总价
+        """
+        try:
+            rst = ctl.do_wxpay_notify(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+class V3CommentsListView(cv.AuthView):
+    def get(self, request):
+        """#获取评论列表v3(3.0小程序)
+        @record_id:29411,作业id
+        """
+        try:
+            total,rst = ctl.get_user_comments(request)
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e: 
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class V3WanzhuConsultListView(cv.AuthView):
+    def get(self, request):
+        """#获取我的咨询列表v3(3.0小程序)
+        """
+        try:
+            total,rst = ctl.get_user_consult_list(request)
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e: 
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class V3ZanCountView(cv.AuthView):
+    def get(self, request):
+        """#获取点赞数v3(3.0小程序)
+        @record_id:1,作业id
+        """
+        try:
+            rst = ctl.get_user_zans(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+class V3CommentsCountView(cv.AuthView):
+    def get(self, request):
+        """#获取评论数v3(3.0小程序)
+        @record_id:1,作业id
+        @stock_id:1,股票id
+        """
+        try:
+            rst = ctl.get_user_comments_count(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+class V3WanzhuConsultView(cv.AuthView):
+    def post(self, request):
+        """#顽主咨询v3(3.0小程序)
+        @cotent:"咨询内容"
+        @pid:1,评论id(相当于回复哪条评论)
+        """
+        try:
+            rst = ctl.add_wanzhu_consult(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+class V3CommentsView(cv.AuthView):
+    def post(self, request):
+        """#评论v3(3.0小程序)
+        @record_id:1,作业id
+        @stock_id:1,股票id
+        @cotent:"评论内容"
+        @pid:1,评论id
+        """
+        try:
+            rst = ctl.add_comments(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+    def delete(self, request):
+        """#删除评论v3(3.0小程序)
+        @id:1,评论id
+        """
+        try:
+            rst = ctl.delete_comments(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+class V3ZanView(cv.AuthView):
+    def post(self, request):
+        """#点赞v3(3.0小程序)
+        @record_id:1,作业id
+        """
+        try:
+            rst = ctl.add_zan(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class V3ExperienceView(cv.AuthView):
+    def get(self, request):
+        """#今日反思列表v3(3.0小程序)
+        """
+        try:
+            total,rst = ctl.get_experience_list(request)
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e: 
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class V3UserFundModifyView(cv.AuthView):
+    def put(self, request):
+        """#修改初始资金v3(3.0小程序)
+        @user_id:1,
+        @player_id:1,
+        @out_fund:10
+        @in_fund:10
+        @account_img:""
+        """
+        try:
+            return cv.to_suc({})
+            total,rst = ctl.get_experience_list(request)
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e: 
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class V3UserChoiceListView(cv.AuthView):
+    def get(self, request):
+        """#我的自选v3(3.0小程序)
+        @stock_id:1
+        """
+        try:
+            total,rst = ctl.get_user_choices(request)
+            return cv.to_suc({"total":total,"list":rst})
+        except Exception as e: 
+            cv.tracefail()
+            return cv.to_fail(e)
+
+
+class V3UserChoiceView(cv.AuthView):
+    def post(self, request):
+        """#加入首选v3(3.0小程序)
+        @stock_id:""
+        """
+        try:
+            rst = ctl.add_choice(request)
+            return cv.to_suc(rst)
+        except Exception as e: 
+            return cv.to_fail(e)
+
+
+    def delete(self,request):
+        """#取消自选(3.0小程序)
+        @id:"1",多个逗号分隔
+        @stock_id:"1",股票id
+        """
+        qdata = request.json
+        need_params = ["stock_id"]
+        mse = ccf.check_params(*need_params,**qdata)
+        if mse:
+            raise ce.TipException(mse)
+        try:
+            vals = ccf.get_need_params(*need_params,**qdata)
+            rst = ctl.delete_choice(request)
+            return cv.to_suc(rst)
+        except Exception as e:
+            cv.tracefail()
+            return cv.to_fail(e)

+ 3 - 3
src/weixin/wzhifuSDK.py

@@ -59,15 +59,15 @@ class WxPayConf_pub(object):
     #JSAPI接口中获取openid,审核后在公众平台开启开发模式后可查看
     APPSECRET = "20e278a60d52ad63822a07e49931435c"
     #受理商ID,身份标识
-    MCHID = ""
+    MCHID = "1625494503"
     #商户支付密钥Key。审核通过后,在微信发送的邮件中查看
-    KEY = ""
+    KEY = "7ce0aeb3eeeb9406a88652f550d6275e"
 
    
 
     #=======【异步通知url设置】===================================
     #异步通知url,商户根据实际开发过程设定
-    NOTIFY_URL = "https://wxapi.yifeng2016.com/api/wx/xcxzfnotify"
+    NOTIFY_URL = "https://wx.scxjc.club/api/wx/v3/signup/notify"
 
     #=======【JSAPI路径设置】===================================
     #获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面

+ 389 - 0
src/weixin/wzhifuSDK.py.hyxc

@@ -0,0 +1,389 @@
+#coding:utf-8
+"""
+Created on 2014-11-24
+
+@author: http://blog.csdn.net/yueguanghaidao,yihaibo@longtugame.com
+
+ * 微信支付帮助库
+ * ====================================================
+ * 接口分三种类型:
+ * 【请求型接口】--Wxpay_client_
+ *      统一支付接口类--UnifiedOrder
+ *      订单查询接口--OrderQuery
+ *      退款申请接口--Refund
+ *      退款查询接口--RefundQuery
+ *      对账单接口--DownloadBill
+ *      短链接转换接口--ShortUrl
+ * 【响应型接口】--Wxpay_server_
+ *      通用通知接口--Notify
+ *      Native支付——请求商家获取商品信息接口--NativeCall
+ * 【其他】
+ *      静态链接二维码--NativeLink
+ *      JSAPI支付--JsApi
+ * =====================================================
+ * 【CommonUtil】常用工具:
+ *      trimString(),设置参数时需要用到的字符处理函数
+ *      createNoncestr(),产生随机字符串,不长于32位
+ *      formatBizQueryParaMap(),格式化参数,签名过程需要用到
+ *      getSign(),生成签名
+ *      arrayToXml(),array转xml
+ *      xmlToArray(),xml转 array
+ *      postXmlCurl(),以post方式提交xml到对应的接口url
+ *      postXmlSSLCurl(),使用证书,以post方式提交xml到对应的接口url
+
+"""
+
+import json
+import time
+import random
+import urllib2
+import hashlib
+import threading
+import urllib
+from urllib import quote
+import xml.etree.ElementTree as ET
+
+try:
+    import pycurl
+    from cStringIO import StringIO
+except ImportError:
+    pycurl = None
+
+
+class WxPayConf_pub(object):
+    """配置账号信息"""
+
+    ##=======【基本信息设置】=====================================
+    #微信公众号身份的唯一标识。审核通过后,在微信发送的邮件中查看
+    APPID = "wx2938132b773c7b5a"
+    #JSAPI接口中获取openid,审核后在公众平台开启开发模式后可查看
+    APPSECRET = "80d9062d19dfded406587575e0f1aa2e"
+    #受理商ID,身份标识
+    MCHID = "1590893081"
+    #商户支付密钥Key。审核通过后,在微信发送的邮件中查看
+    KEY = "kAHuCc2g4MINcLRk3o0lxT6J1Z04WuZq"
+
+   
+
+    #=======【异步通知url设置】===================================
+    #异步通知url,商户根据实际开发过程设定
+    NOTIFY_URL = "https://wx.scxjc.club/api/wx/v3/signup/notify"
+
+    #=======【JSAPI路径设置】===================================
+    #获取access_token过程中的跳转uri,通过跳转将code传入jsapi支付页面
+    #JS_API_CALL_URL = "http://www.huodongjia.com/pay/?showwxpaytitle=1"
+    JS_API_CALL_URL = "http://baoming.siyusai.com/wxjspay/forwxjspay/"
+
+    #=======【证书路径设置】=====================================
+    #证书路径,注意应该填写绝对路径
+    SSLCERT_PATH = "/data/web/m_website_dev/m_web/weixinpay/cert/apiclient_cert.pem"
+    SSLKEY_PATH = "/data/web/m_website_dev/m_web/weixinpay/cert/apiclient_key.pem"
+
+    #=======【curl超时设置】===================================
+    CURL_TIMEOUT = 30
+
+    #=======【HTTP客户端设置】===================================
+    HTTP_CLIENT = "URLLIB"  # ("URLLIB", "CURL")
+
+
+class Singleton(object):
+    """单例模式"""
+
+    _instance_lock = threading.Lock()
+
+    def __new__(cls, *args, **kwargs):
+        if not hasattr(cls, "_instance"):
+            with cls._instance_lock:
+                if not hasattr(cls, "_instance"):
+                    impl = cls.configure() if hasattr(cls, "configure") else cls
+                    instance = super(Singleton, cls).__new__(impl, *args, **kwargs)
+                    if not isinstance(instance, cls):
+                        instance.__init__(*args, **kwargs)
+                    cls._instance = instance
+        return cls._instance
+
+
+class UrllibClient(object):
+    """使用urlib2发送请求"""
+
+    def get(self, url, second=30):
+        return self.postXml(None, url, second)
+
+    def post(self,url,data):
+        req = urllib2.Request(url)
+        data = urllib.urlencode(data)
+        opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
+        response = opener.open(req, data) 
+        return response.read()
+
+    def postXml(self, xml, url, second=30):
+        """不使用证书"""
+        data = urllib2.urlopen(url, xml, timeout=second).read()
+        return data
+
+    def postXmlSSL(self, xml, url, second=30):
+        """使用证书"""
+        raise TypeError("please use CurlClient")
+
+
+class CurlClient(object):
+    """使用Curl发送请求"""
+    def __init__(self):
+        self.curl = pycurl.Curl()
+        self.curl.setopt(pycurl.SSL_VERIFYHOST, False)
+        self.curl.setopt(pycurl.SSL_VERIFYPEER, False)
+        #设置不输出header
+        self.curl.setopt(pycurl.HEADER, False)
+
+    def get(self, url, second=30):
+        return self.postXmlSSL(None, url, second=second, cert=False, post=False)
+
+    def postXml(self, xml, url, second=30):
+        """不使用证书"""
+        return self.postXmlSSL(xml, url, second=second, cert=False, post=True)
+        
+
+    def postXmlSSL(self, xml, url, second=30, cert=True, post=True):
+        """使用证书"""
+        self.curl.setopt(pycurl.URL, url)
+        self.curl.setopt(pycurl.TIMEOUT, second)
+        #设置证书
+        #使用证书:cert 与 key 分别属于两个.pem文件
+        #默认格式为PEM,可以注释
+        if cert:
+            self.curl.setopt(pycurl.SSLKEYTYPE, "PEM")
+            self.curl.setopt(pycurl.SSLKEY, WxPayConf_pub.SSLKEY_PATH)
+            self.curl.setopt(pycurl.SSLCERTTYPE, "PEM")
+            self.curl.setopt(pycurl.SSLCERT, WxPayConf_pub.SSLCERT_PATH)
+        #post提交方式
+        if post:
+            self.curl.setopt(pycurl.POST, True)
+            self.curl.setopt(pycurl.POSTFIELDS, xml)
+        buff = StringIO()
+        self.curl.setopt(pycurl.WRITEFUNCTION, buff.write)
+
+        self.curl.perform()
+        return buff.getvalue()
+
+
+class HttpClient(Singleton):
+    @classmethod
+    def configure(cls):
+        if pycurl is not None and WxPayConf_pub.HTTP_CLIENT != "URLLIB":
+            return CurlClient
+        else:
+            return UrllibClient
+            
+
+class Common_util_pub(object):
+    """所有接口的基类"""
+
+    def trimString(self, value):
+        if value is not None and len(value) == 0:
+            value = None
+        return value
+
+    def createNoncestr(self, length = 32):
+        """产生随机字符串,不长于32位"""
+        chars = "abcdefghijklmnopqrstuvwxyz0123456789"
+        strs = []
+        for x in range(length):
+            strs.append(chars[random.randrange(0, len(chars))])
+        return "".join(strs)
+
+    def formatBizQueryParaMap(self, paraMap, urlencode):
+        """格式化参数,签名过程需要使用"""
+        slist = sorted(paraMap)
+        buff = []
+        for k in slist:
+            v = quote(paraMap[k]) if urlencode else paraMap[k]
+            buff.append("{0}={1}".format(k, v))
+
+        return "&".join(buff)
+
+    def getSign(self, obj):
+        """生成签名"""
+        #签名步骤一:按字典序排序参数,formatBizQueryParaMap已做
+        String = self.formatBizQueryParaMap(obj, False)
+        #签名步骤二:在string后加入KEY
+        String = "{0}&key={1}".format(String,WxPayConf_pub.KEY)
+        #签名步骤三:MD5加密
+        String = hashlib.md5(String).hexdigest()
+        #签名步骤四:所有字符转为大写
+        result_ = String.upper()
+        return result_
+
+    def arrayToXml(self, arr):
+        """array转xml"""
+        xml = ["<xml>"]
+        for k, v in arr.iteritems():
+            if v.isdigit():
+                xml.append("<{0}>{1}</{0}>".format(k, v))
+            else:
+                xml.append("<{0}>{1}</{0}>".format(k, v))
+        xml.append("</xml>")
+        return "".join(xml)
+
+    def xmlToArray(self, xml):
+        """将xml转为array"""
+        array_data = {}
+        root = ET.fromstring(xml)
+        for child in root:
+            value = child.text
+            array_data[child.tag] = value
+        return array_data
+
+    def postXmlCurl(self, xml, url, second=30):
+        """以post方式提交xml到对应的接口url"""
+        return HttpClient().postXml(xml, url, second=second)
+
+    def postXmlSSLCurl(self, xml, url, second=30):
+        """使用证书,以post方式提交xml到对应的接口url"""
+        return HttpClient().postXmlSSL(xml, url, second=second)
+
+
+class Wxpay_client_pub(Common_util_pub):
+    """请求型接口的基类"""
+    response = None  #微信返回的响应
+    url = None       #接口链接
+    curl_timeout = None #curl超时时间
+
+    def __init__(self):
+        self.parameters = {} #请求参数,类型为关联数组
+        self.result = {}     #返回参数,类型为关联数组
+
+
+    def setParameter(self, parameter, parameterValue):
+        """设置请求参数"""
+        self.parameters[self.trimString(parameter)] = self.trimString(parameterValue)
+
+    def createXml(self):
+        """设置标配的请求参数,生成签名,生成接口参数xml"""
+        return  self.arrayToXml(self.parameters)
+
+    def postXml(self):
+        """post请求xml"""
+        xml = self.createXml()
+        self.response = self.postXmlCurl(xml, self.url, self.curl_timeout)
+        return self.response
+
+    def postXmlSSL(self):
+        """使用证书post请求xml"""
+        xml = self.createXml()
+        self.response = self.postXmlSSLCurl(xml, self.url, self.curl_timeout)
+        return self.response
+
+    def getResult(self):
+        """获取结果,默认不使用证书"""
+        self.postXml()
+        self.result = self.xmlToArray(self.response)
+        return self.result
+
+
+class UnifiedOrder_pub(Wxpay_client_pub):
+    """统一支付接口类"""
+
+    def __init__(self, timeout=WxPayConf_pub.CURL_TIMEOUT):
+        #设置接口链接
+        self.url = "https://api.mch.weixin.qq.com/pay/unifiedorder"
+        #设置curl超时时间
+        self.curl_timeout = timeout
+        super(UnifiedOrder_pub, self).__init__()
+
+
+    def createXml(self):
+        """生成接口参数xml"""
+        #检测必填参数
+        if any(self.parameters[key] is None for key in ("out_trade_no", "total_fee")):
+            raise ValueError("missing parameter")
+        if self.parameters["trade_type"] == "JSAPI" and self.parameters["openid"] is None:
+            raise ValueError("JSAPI need openid parameters")
+
+        self.parameters["appid"] = WxPayConf_pub.APPID  #公众账号ID
+        self.parameters["mch_id"] = WxPayConf_pub.MCHID  #商户号
+        self.parameters["spbill_create_ip"] = "127.0.0.1"  #终端ip      
+        self.parameters["nonce_str"] = self.createNoncestr()  #随机字符串
+        self.parameters["notify_url"] = WxPayConf_pub.NOTIFY_URL  #随机字符串
+        self.parameters["body"] = "培训报名费"  #随机字符串
+        if self.parameters.has_key("sign"):
+            self.parameters.pop("sign")
+        sign = self.getSign(self.parameters)
+        print self.parameters
+        self.parameters["sign"] = sign  #签名
+        return  self.arrayToXml(self.parameters)
+
+    def getPrepayId(self):
+        """获取prepay_id"""
+        self.postXml()
+        self.result = self.xmlToArray(self.response)
+        print self.result
+        return self.result
+
+    def geth5url(self):
+        """获取prepay_id"""
+        self.postXml()
+        self.result = self.xmlToArray(self.response)
+        prepay_id = self.result["mweb_url"]
+        return prepay_id
+
+class Wxpay_server_pub(Common_util_pub):
+    """响应型接口基类"""
+    SUCCESS, FAIL = "SUCCESS", "FAIL"
+
+    def __init__(self):
+        self.data = {}  #接收到的数据,类型为关联数组
+        self.returnParameters = {} #返回参数,类型为关联数组
+
+    def saveData(self, xml):
+        """将微信的请求xml转换成关联数组,以方便数据处理"""
+        self.data = self.xmlToArray(xml)
+
+    def checkSign(self):
+        """校验签名"""
+        tmpData = dict(self.data) #make a copy to save sign
+        del tmpData['sign']
+        sign = self.getSign(tmpData) #本地签名
+        print sign,111111111111
+        print self.data["sign"],22222222222
+        if self.data['sign'] == sign:
+            return True
+        return False
+
+    def getData(self):
+        """获取微信的请求数据"""
+        return self.data
+
+    def setReturnParameter(self, parameter, parameterValue):
+        """设置返回微信的xml数据"""
+        self.returnParameters[self.trimString(parameter)] = self.trimString(parameterValue)
+
+    def createXml(self):
+        """生成接口参数xml"""
+        return self.arrayToXml(self.returnParameters)
+
+    def returnXml(self):
+        """将xml数据返回微信"""
+        returnXml = self.createXml()
+        return returnXml
+
+def get_wx_unifiedorder(out_trade_no,total_fee,openid,trade_type="JSAPI"):
+    par_obj = UnifiedOrder_pub()
+    par_obj.setParameter('out_trade_no',out_trade_no)
+    par_obj.setParameter('total_fee',total_fee)
+    par_obj.setParameter('openid',openid)
+    par_obj.setParameter('trade_type',trade_type)
+    par_obj.createXml()
+    return par_obj.getPrepayId()
+
+def check_notify_valid(xml_params):
+    """回调验证
+    """
+    wsp_notify = Wxpay_server_pub() 
+    wsp_notify.saveData(xml_params)
+    return wsp_notify.checkSign(),wsp_notify.getData()
+
+
+
+
+if __name__ == "__main__":
+    pass