diff --git a/make_data.py b/make_data.py index 70b9f334..2110e360 100644 --- a/make_data.py +++ b/make_data.py @@ -6,12 +6,15 @@ from flask import current_app import uuid + +from sqlalchemy import false + from planet import create_app from planet.config.enums import ItemAuthrity, ItemPostion, ItemType, ActivityType, TimeLimitedStatus from planet.control.CExcel import CExcel from planet.extensions.register_ext import db from planet.models import Items, ProductBrand, Activity, PermissionType, Approval, ProductSku, Admin, Products, \ - TimeLimitedActivity, UserActivationCode, ActivationCodeApply, MakeOver + TimeLimitedActivity, UserActivationCode, ActivationCodeApply, MakeOver, ActivationType, User # 添加一些默认的数据 @@ -309,7 +312,7 @@ def check_abnormal_sale_volume(): db.session.add(product) # 修正商品月销量 ops = db.session.query(extract('month', OrderPart.createtime), func.count('*')).outerjoin( - OrderMain,OrderMain.OMid == OrderPart.OMid).filter( + OrderMain, OrderMain.OMid == OrderPart.OMid).filter( OrderMain.isdelete == False, OrderPart.isdelete == False, OrderMain.OMstatus != -40, @@ -429,6 +432,194 @@ def init_make_over(): db.session.add(mo) +def make_ActivationType(): + with db.auto_commit(): + print('start add attype') + attid = 100 + act_list = [] + + # 分享新用户 + share_new = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "分享新用户", + 'ATTnum': 20, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 0, + 'ADid': 'adid1' + }) + act_list.append(share_new) + attid += 100 + + # 分享老用户 + share_old = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "分享老用户", + 'ATTnum': 10, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 0, + 'ADid': 'adid1' + }) + act_list.append(share_old) + attid += 100 + + # 发布随笔 + publish = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "发布随笔", + 'ATTnum': 20, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 100, + 'ADid': 'adid1' + }) + act_list.append(publish) + attid += 100 + + # 加精 + selected = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "系统精选随笔", + 'ATTnum': 20, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 0, + 'ADid': 'adid1' + }) + act_list.append(selected) + attid += 100 + + # 打赏 该类型具体积分需按照实际情况添加 + print('get reward id is {}'.format(attid)) + reward = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "系统打赏", + 'ATTnum': 0, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 0, + 'ADid': 'adid1' + }) + act_list.append(reward) + attid += 100 + + # 小红书信息绑定 + redbook = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "小红书信息绑定", + 'ATTnum': 10, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 0, + 'ATTtype': 1, + 'ATTicon': '/img/base/2019/8/1/logo-red.png', + 'ADid': 'adid1' + }) + act_list.append(redbook) + attid += 100 + + # 抖音信息绑定 + tiktok = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "抖音信息绑定", + 'ATTnum': 10, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 0, + 'ATTtype': 1, + 'ATTicon': '/img/base/2019/8/1/logo-dou.png', + 'ADid': 'adid1' + }) + act_list.append(tiktok) + attid += 100 + + # QQ号信息绑定 + qq = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "QQ号信息绑定", + 'ATTnum': 10, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 0, + 'ATTtype': 1, + 'ATTicon': '/img/base/2019/8/1/logo-qq.png', + 'ADid': 'adid1' + }) + act_list.append(qq) + attid += 100 + + # 微信号信息绑定 + wechat = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "微信号信息绑定", + 'ATTnum': 10, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 0, + 'ATTtype': 1, + 'ATTicon': '/img/base/2019/8/1/logo-peng.png', + 'ADid': 'adid1' + }) + act_list.append(wechat) + attid += 10 + + # sina微博信息绑定 + sina = ActivationType.create({ + 'ATTid': str(attid), + 'ATTname': "sina微博信息绑定", + 'ATTnum': 10, + 'ATTupperLimit': 0, + 'ATTdayUpperLimit': 0, + 'ATTtype': 1, + 'ATTicon': '/img/base/2019/8/1/logo-weibo.png', + 'ADid': 'adid1' + }) + act_list.append(sina) + # attid += 1 + + print('max attid is {}'.format(attid)) + + db.session.add_all(act_list) + + +def add_user(): + import xlrd + import os + from PIL import Image + from planet.extensions.qiniu.storage import QiniuStorage + + file_path = os.path.join(current_app.config['BASEDIR'], 'img', 'avatar', '2019', '9', '29') + username = xlrd.open_workbook(os.path.join(file_path, 'username.xls')) + sheet = username.sheet_by_index(0) + one_col = sheet.col_values(0) + with db.auto_commit(): + last_user_id = db.session.query(User.USid).filter( + User.isdelete == false(), User.USid.ilike('id000%')).order_by( + User.USid.desc(), User.createtime.desc(), origin=True).first() + current_app.logger.info("query_last_user_id: {}".format(last_user_id)) + + if last_user_id: + last_user_id = last_user_id[0] + else: + last_user_id = 'id00000000' + new_id = int(str(last_user_id).split('id')[-1]) + 1 + user_list = [] + + qiniu = QiniuStorage(current_app) + for index, name in enumerate(one_col): + avatar_name = '/img/avatar/2019/9/29/aratar_{}.jpg'.format(index + 1) + avatar_path = os.path.join(current_app.config['BASEDIR'], avatar_name[1:]) + img = Image.open(avatar_path) + x, y = img.size + if x != 132 or y != 132: + img.resize((132, 132), Image.LANCZOS).save(avatar_path) + + print(name, avatar_name) + # 上传七牛云 + qiniu.save(data=avatar_path, filename=avatar_name[1:]) + usid = 'id%08d' % new_id + user = User.create({'USid': usid, + 'USname': name, + 'USheader': avatar_name, + 'USfrom': 2, + }) + user_list.append(user) + new_id += 1 + db.session.add_all(user_list) + + if __name__ == '__main__': app = create_app() with app.app_context(): @@ -437,7 +628,7 @@ def init_make_over(): # print(admin.__dict__) # print(admin_str) # make_acvitity() - make_items() + # make_items() # make_permissiontype() # make_admin() # cexcel = CExcel() @@ -450,4 +641,6 @@ def init_make_over(): # check_product_from() # change_tla_status() # add_uac_acaid() + # make_ActivationType() + add_user() pass diff --git a/planet/__init__.py b/planet/__init__.py index 86cb3925..1c347449 100644 --- a/planet/__init__.py +++ b/planet/__init__.py @@ -54,6 +54,7 @@ from planet.api.v2.ATicket import ATicket from planet.common.request_handler import error_handler, request_first_handler from planet.config.secret import DefaltSettig +from planet.api.v2.AActivation import AActivation from planet.extensions.register_ext import register_ext from planet.extensions.loggers import LoggerHandler from planet.route.RouteSocket import Mynamespace, JSONEncoder as socketjsonencoder @@ -118,7 +119,8 @@ def detail(self): 'method': self.method, 'data': self.data, 'query': self.args.to_dict(), - 'address': self.remote_addr + 'address': self.remote_addr, + 'user-agent': self.user_agent.__dict__, } # if self.files: # res.setdefault('files', dict(self.files)) @@ -197,6 +199,7 @@ def register(app): v2.add_url_rule('/play/', view_func=APlay.as_view('play')) # 活动 v2.add_url_rule('/ticket/', view_func=ATicket.as_view('ticket')) # 票务 v2.add_url_rule('/feedback/', view_func=AMaterialFeedback.as_view('feedback')) # 素材反馈 + v2.add_url_rule('/activation/', view_func=AActivation.as_view('activation')) # 活跃度 v2.add_url_rule('/personalcenter/', view_func=AMiniProgramPersonalCenter.as_view('personalcenter')) # 小程序个人中心 diff --git a/planet/api/v2/AActivation.py b/planet/api/v2/AActivation.py new file mode 100644 index 00000000..33b3bccc --- /dev/null +++ b/planet/api/v2/AActivation.py @@ -0,0 +1,25 @@ +from planet.common.base_resource import Resource +from planet.control.CActivation import CActivation + + +class AActivation(Resource): + def __init__(self): + self.cat = CActivation() + + def post(self, activation): + apis = { + 'update_activationtype': self.cat.update_activationtype, + 'bind_linkage': self.cat.bind_linkage, + 'reward': self.cat.reward, + 'select': self.cat.select, + } + return apis + + def get(self, activation): + apis = { + 'get_activationtype': self.cat.get_activationtype, + 'list_activationtype': self.cat.list_activationtype, + 'get_userlinkage': self.cat.get_userlinkage, + 'get_duration_activation': self.cat.get_duration_activation, + } + return apis diff --git a/planet/api/v2/APlay.py b/planet/api/v2/APlay.py index 568e652f..b0ea000c 100644 --- a/planet/api/v2/APlay.py +++ b/planet/api/v2/APlay.py @@ -32,6 +32,8 @@ def get(self, play): 'get_promotion': self.cplay.get_promotion, 'get_undertake_agreement': self.cplay.get_undertake_agreement, 'get_params': self.cplay.get_params, + 'get_role': self.cplay.get_role, + 'list_role': self.cplay.list_role, 'download_team': self.cplay.download_team_user_info, } return apis @@ -53,6 +55,7 @@ def post(self, play): 'make_over': self.cplay.make_over, 'undertake': self.cplay.undertake, 'payment': self.cplay.payment, + 'update_role': self.cplay.update_role, 'help': self.cplay.help } return apis diff --git a/planet/api/v2/ASupplizer.py b/planet/api/v2/ASupplizer.py index 11ecd57b..e8e4e685 100644 --- a/planet/api/v2/ASupplizer.py +++ b/planet/api/v2/ASupplizer.py @@ -13,6 +13,7 @@ def get(self, supplizer): 'code': self.csupplizer.send_reset_password_code, 'get_supplizeraccount': self.csupplizer.get_supplizeraccount, 'get_system_notes': self.csupplizer.get_system_notes, + 'get_verifier': self.csupplizer.get_verifier, } return apis @@ -26,5 +27,6 @@ def post(self, supplizer): 'offshelves': self.csupplizer.offshelves, 'set_supplizeraccount': self.csupplizer.set_supplizeraccount, 'add_update_notes': self.csupplizer.add_update_notes, + 'set_verifier': self.csupplizer.set_verifier, } return apis diff --git a/planet/api/v2/ATicket.py b/planet/api/v2/ATicket.py index 225b1a78..121d49d4 100644 --- a/planet/api/v2/ATicket.py +++ b/planet/api/v2/ATicket.py @@ -1,26 +1,28 @@ -from planet.common.base_resource import Resource -from planet.control.CTicket import CTicket - - -class ATicket(Resource): - def __init__(self): - self.cticket = CTicket() - - def get(self, ticket): - apis = { - 'get': self.cticket.get_ticket, - 'list': self.cticket.list_ticket, - 'get_promotion': self.cticket.get_promotion, - 'list_linkage': self.cticket.list_linkage, - 'list_trade': self.cticket.list_trade, - } - return apis - - def post(self, ticket): - apis = { - 'create': self.cticket.create_ticket, - 'update': self.cticket.update_ticket, - 'pay': self.cticket.pay, - 'award': self.cticket.set_award, - } - return apis +from planet.common.base_resource import Resource +from planet.control.CTicket import CTicket + + +class ATicket(Resource): + def __init__(self): + self.cticket = CTicket() + + def get(self, ticket): + apis = { + 'get': self.cticket.get_ticket, + 'list': self.cticket.list_ticket, + 'get_promotion': self.cticket.get_promotion, + 'list_linkage': self.cticket.list_linkage, + 'list_trade': self.cticket.list_trade, + 'tsostatus': self.cticket.list_tsostatus, + } + return apis + + def post(self, ticket): + apis = { + 'create': self.cticket.create_ticket, + 'update': self.cticket.update_ticket, + 'pay': self.cticket.pay, + 'award': self.cticket.set_award, + 'verify': self.cticket.ticketorder_verified, + } + return apis diff --git a/planet/api/v2/AUser.py b/planet/api/v2/AUser.py index 5d73e320..7eec7683 100644 --- a/planet/api/v2/AUser.py +++ b/planet/api/v2/AUser.py @@ -39,6 +39,7 @@ def post(self, user): 'mp_login': self.user.mini_program_login, # 小程序登录 'blog_login': self.user.blog_login, # 网页blog登录 'update_usinfo': self.user.update_usinfo, # 小程序编辑个人信息 + 'add_mock_user': self.user.add_mock_user, # 添加虚拟用户 # 'update': self.user.update, # 'destroy': self.user.destroy, diff --git a/planet/common/get_location.py b/planet/common/get_location.py index 2f99a85a..618d08f9 100644 --- a/planet/common/get_location.py +++ b/planet/common/get_location.py @@ -1,21 +1,27 @@ +import json import requests -from planet.config.secret import BAIDUMPAK +import hashlib + +from planet.config.secret import BAIDUMPAK, TencentMapKey, TencentMapSK from planet.common.error_response import SystemError class GetLocation(): """ api文档 http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding-abroad + https://lbs.qq.com/webservice_v1/guide-gcoder.html """ - api_url = "http://api.map.baidu.com/geocoder/v2/" + api_url = "http://api.map.baidu.com/geocoder/v2/" # baidu + url = 'https://apis.map.qq.com' # tencent - def __init__(self, lat, lng): + def __init__(self, lat, lng, map='Tencent'): self.lat = lat self.lng = lng - self.result = self.get_location() - - def get_location(self): + if map != 'Tencent': + self.result = self.get_location_b() + self.result = self.get_location_t() + def get_location_b(self): res = requests.get(self.api_url, params={ 'location': '{},{}'.format(self.lat, self.lng), 'output': 'json', 'ak': BAIDUMPAK}) @@ -23,7 +29,57 @@ def get_location(self): print(content) if content.get('status') != 0: raise SystemError('数据获取失败') - return content.get('result') + return self.format_userlocation_b(content.get('result')) + + def format_userlocation_b(self, result): + return { + 'ULformattedAddress': result.get('formatted_address'), + 'ULcountry': result.get('addressComponent').get('country'), + 'ULprovince': result.get('addressComponent').get('province'), + 'ULcity': result.get('addressComponent').get('city'), + 'ULdistrict': result.get('addressComponent').get('district'), + 'ULresult': json.dumps(result), + 'ULlng': result.get('location').get('lng'), + 'ULlat': result.get('location').get('lat'), + } + + def format_userlocation_t(self, result): + return { + 'ULformattedAddress': result.get('formatted_addresses').get('recommend'), + 'ULcountry': result.get('address_component').get('nation'), + 'ULprovince': result.get('address_component').get('province'), + 'ULcity': result.get('address_component').get('city'), + 'ULdistrict': result.get('address_component').get('district'), + 'ULresult': json.dumps(result), + 'ULlng': result.get('location').get('lng'), + 'ULlat': result.get('location').get('lat'), + } + + def md5_encode(self, text): + # 创建md5对象 + hl = hashlib.md5() + # Tips + # 此处必须声明encode + # 若写法为hl.update(str) 报错为: Unicode-objects must be encoded before hashing + hl.update(text.encode(encoding='utf-8')) + # print('MD5加密前为 :' + text) + res = hl.hexdigest() + # print('MD5加密后为 :' + res) + return res + + def get_location_t(self): + key = TencentMapKey + secret_key = TencentMapSK + # location = '30.183413980701,120.26527404785156' + args = '/ws/geocoder/v1?key={}&location={},{}'.format(key, self.lat, self.lng) + sig = self.md5_encode(args + secret_key) + + res = requests.get('{}{}&sig={}'.format(self.url, args, sig)) + # print(eval(res.content)) + content = res.json() + if content.get('status') != 0: + raise SystemError('数据获取失败') + return self.format_userlocation_t(content.get('result')) if __name__ == '__main__': diff --git a/planet/common/playpicture.py b/planet/common/playpicture.py index b3f6d51c..4a2e4d1f 100644 --- a/planet/common/playpicture.py +++ b/planet/common/playpicture.py @@ -36,6 +36,8 @@ def __init__(self): self.res_path = os.path.join(current_app.config['BASEDIR'], 'planet', 'extensions', 'staticres') self.pro_1 = '跟旗行一起游山玩水' self.pro_2 = '长按扫码加入我们' + self.pro_3 = '来旗行,放肆High!' + self.pro_4 = '长按扫码抢免费门票' self.temp_path = '' def _get_path(self, fold): @@ -211,7 +213,8 @@ def drawrec(self, dw, color, x, y, w, h, r=0): # dw = imd.Draw(im) dw.rectangle((x, y, x + w, y + h), fill=color) - def create_ticket(self, path, tiname, starttime, endtime, playprice, usid, tiid, wxacode): + def create_ticket(self, path, tiname, starttime, endtime, + starttime_grab, endtime_grab, playprice, usid, tiid, wxacode): if not str(path).startswith('/img'): if not (str(path).startswith('http') or str(path).startswith('https')): return @@ -245,11 +248,11 @@ def create_ticket(self, path, tiname, starttime, endtime, playprice, usid, tiid, new_im = new_im.filter(MyGaussianBlur(radius=30)) dw = imd.Draw(new_im) # 蒙版 - black = img.new('RGBA', (750, 1270), color=(0, 0, 0, 60)) - dw.bitmap((0, 0), black, fill=(0, 0, 0, 50)) + black = img.new('RGBA', (750, 1270), color=(0, 0, 0, 100)) + dw.bitmap((0, 0), black, fill=(0, 0, 0, 0)) # 大矩形 - # self.drawRoundRec(new_im, 'white', 35, 50, 680, 680, 60) - self.drawrec(dw, 'white', 35, 50, 680, 940) + self.draw_round_rec(dw, 'white', 35, 50, 680, 940, 60) + # self.drawrec(dw, 'white', 35, 50, 680, 940) # 内容图片 inner_im = img.open(local_path) if x != 640 or y != 640: @@ -261,45 +264,52 @@ def create_ticket(self, path, tiname, starttime, endtime, playprice, usid, tiid, new_im.paste(inner_im, (55, 90)) # 门票信息 - tinamefont = imf.truetype(os.path.join(self.res_path, 'PingFang Medium_downcc.otf'), 40) + tinamefont = imf.truetype(os.path.join(self.res_path, 'pingfangMedium_cu.ttf'), 40) tinamelist = [] for index, end_limit in enumerate(range(0, len(tiname), 16)): tinamelist.append(tiname[index * 16: end_limit + 16]) dw.text((55, 760), '\n'.join(tinamelist), font=tinamefont, fill='#000000') - # TODO 出游时间字段未定 + # 出发时间 - timefont = imf.truetype(os.path.join(self.res_path, 'PingFang Regular.otf'), 30) - dw.text((55, 902), '出游时间: {}-{}'.format(starttime, endtime), font=timefont, fill='#000000') + timefont = imf.truetype(os.path.join(self.res_path, 'PingFang Regular.otf'), 24) + dw.text((55, 898), '出游时间: {}-{}'.format(starttime, endtime), font=timefont, fill='#000000') + + # 抢票时间 + dw.text((55, 931), '领票时间: {}-{}'.format(starttime_grab, endtime_grab), font=timefont, fill='#000000') # ¥ - icon_font = imf.truetype(os.path.join(self.res_path, 'PingFang Regular.otf'), 48) + # icon_font = imf.truetype(os.path.join(self.res_path, 'PingFang Regular.otf'), 48) # 小矩形 - h_ = (len(str(playprice)) + 2) * 26 - x_ = 683 - h_ - self.drawrec(dw, '#FFCE00', x_ + 2, 913, h_ + 13, 34) - - dw.text((x_, 884), '¥', font=icon_font, fill='#000000') - - # 价格 - pricefont = imf.truetype(os.path.join(self.res_path, 'PingFang SC Semibold.ttf'), 60) - price_x = 670 - len(str(playprice) * 26) - dw.text((price_x, 874), playprice, font=pricefont, fill='#000000') - + # h_ = (len(str(playprice)) + 2) * 26 + # x_ = 683 - h_ + # self.drawrec(dw, '#FFCE00', x_ + 2, 913, h_ + 13, 34) + # + # dw.text((x_, 884), '¥', font=icon_font, fill='#000000') + # + # # 价格 + # pricefont = imf.truetype(os.path.join(self.res_path, 'PingFang SC Semibold.ttf'), 60) + # price_x = 670 - len(str(playprice) * 26) + # dw.text((price_x, 874), playprice, font=pricefont, fill='#000000')\ + + # free + free = img.open(os.path.join(self.res_path, 'free.png')).convert('RGBA') + new_im.paste(free, (445, 894), free) # pro1 profont_1 = imf.truetype(os.path.join(self.res_path, 'PangMenZhengDao.ttf'), 52) - dw.text((47, 1077), self.pro_1, font=profont_1, fill='#FFFFFF') + dw.text((47, 1077), self.pro_3, font=profont_1, fill='#FFFFFF') # pro2 profont_2 = imf.truetype(os.path.join(self.res_path, 'PingFangSCRegular.ttf'), 32) - dw.text((47, 1139), self.pro_2, font=profont_2, fill='#FFFFFF') + dw.text((47, 1139), self.pro_4, font=profont_2, fill='#FFFFFF') # 小程序码底层矩形 - # self.draw_round_rec(dw, 'white', 555, 789, 160, 160, 40) + self.draw_round_rec(dw, 'white', 555, 1066, 160, 160, 40) # 小程序码 wxacode = img.open(os.path.join(current_app.config['BASEDIR'], wxacode[1:])) - temp_path = os.path.join(self._get_path('tmp')[0], 'temp3{}.{}'.format(str(uuid.uuid1()), shuffix)) + + temp_path = os.path.join(self._get_path('tmp')[0], 'temp3{}.{}'.format(str(uuid.uuid1()), 'png')) wxacode.resize((160, 160), img.LANCZOS).save(temp_path) - wxacode = img.open(temp_path) - new_im.paste(wxacode, (555, 1050)) + wxacode = img.open(temp_path).convert('RGBA') + new_im.paste(wxacode, (555, 1066), wxacode) # new_im.show() new_im_path, new_im_db_path = self._get_path('play') random_num = datetime.now().timestamp() diff --git a/planet/common/request_handler.py b/planet/common/request_handler.py index 1ce4d778..f8239e11 100644 --- a/planet/common/request_handler.py +++ b/planet/common/request_handler.py @@ -4,16 +4,14 @@ import traceback import uuid from collections import namedtuple - from itsdangerous import TimedJSONWebSignatureSerializer as Serializer, BadSignature, SignatureExpired from flask import current_app, request -from sqlalchemy.exc import IntegrityError - from planet.extensions.register_ext import db from planet.models import UserLoginApi -from .error_response import ApiError, BaseError, SystemError, DumpliError +from .error_response import ApiError, BaseError, SystemError from .success_response import Success + # User = namedtuple('User', ('id', 'model', 'level')) @@ -32,7 +30,6 @@ def token_to_user_(token): # setattr(request, 'user', user) current_app.logger.info('current_user info : {}'.format(data)) - except BadSignature as e: pass except SignatureExpired as e: @@ -100,6 +97,11 @@ def token_to_user(): ula_instance = UserLoginApi.create(ula_dict1) db.session.add(ula_instance) + @app.before_request + def deny_ip_segment(): + if re.match(r'(223\.166\.222\..*|101\.91\.60\..*)', str(request.remote_addr)): + raise SystemError('(*^__^*) 嘻嘻……') + def error_handler(app): # @app.errorhandler(404) diff --git a/planet/config/enums.py b/planet/config/enums.py index a181c128..75cc3724 100644 --- a/planet/config/enums.py +++ b/planet/config/enums.py @@ -838,10 +838,18 @@ class TicketStatus(Enum): class TicketsOrderStatus(Enum): """已购票状态""" - not_won = -1, '未中奖' - pending = 0, '待开奖' - has_won = 1, '中奖' - completed = 2, '已出票' + not_won = -1, '未通过' + pending = 0, '已申请' + has_won = 1, '获得试用' + completed = 2, '已使用' # 2.0 修改取消二次押金 + accomplish = 3, '已完成' + + +class TicketPayType(Enum): + """购票支付类型""" + deposit = 1, '押金购' + cash = 2, '直购' + scorepay = 3, '支付分' class TicketDepositType(Enum): @@ -861,6 +869,42 @@ class UserMaterialFeedbackStatus(Enum): refund = 1, '已退' +class RoleType(Enum): + agreement = 0, '转让协议' + discountrefund = 1, '活动退款协议' + ticketrole = 2, '门票规则' + activationrole = 3, '活跃分获取规则' + + +class ActivationTypeEnum(Enum): + share_new = '100', '分享新人' + share_old = '200', '分享老人' + publish = '300', '发布随笔' + selected = '400', '系统精选随笔' + reward = '500', '系统打赏' + redbook = '600', '小红书信息绑定' + tiktok = '700', '抖音信息绑定' + qq = '800', 'QQ号信息绑定' + wechat = '900', '微信号信息绑定' + sina = '910', 'sina微博信息绑定' + + +class ActivationTypeType(Enum): + info = 1, '信息绑定' + other = 0, '其他' + + +class ShareType(Enum): + direct = 0, '直接分享小程序' + promotion = 1, '推广图分享' + usercode = 2, '用户二维码' + + +class SupplizerGrade(Enum): + product = 0, '普通商品供应商' + ticket = 1, '门票供应商' + + if __name__ == '__main__': print(TemplateID('enterlog').zh_value) # import diff --git a/planet/control/BaseControl.py b/planet/control/BaseControl.py index 1f71dc20..f5b57378 100644 --- a/planet/control/BaseControl.py +++ b/planet/control/BaseControl.py @@ -1,3 +1,4 @@ +import os import uuid from datetime import datetime, date from decimal import Decimal @@ -5,18 +6,20 @@ import json from flask import current_app, request - -from planet.config.enums import ApprovalType, ApplyStatus, ApprovalAction, ApplyFrom, UserMediaType, \ - TrialCommodityStatus -from planet.common.error_response import SystemError, ParamsError -from planet.common.request_handler import gennerc_log -from planet.extensions.register_ext import db -from planet.models import User, Supplizer, Admin, PermissionType, News, Approval, ApprovalNotes, Permission, CashNotes, \ - UserWallet, UserMedia, Products, ActivationCodeApply, TrialCommoditySkuValue, TrialCommodityImage, \ +from sqlalchemy import false + +from planet.config.enums import ApplyStatus, ApprovalAction, ApplyFrom, \ + TrialCommodityStatus, ActivationTypeEnum, TicketsOrderStatus +from planet.common.error_response import ParamsError, StatusError +from planet.extensions.register_ext import db, mp_miniprogram +from planet.extensions.weixin.mp import WeixinMPError +from planet.models import User, Supplizer, Admin, PermissionType, News, Approval, ApprovalNotes, CashNotes, \ + UserWallet, Products, ActivationCodeApply, TrialCommoditySkuValue, TrialCommodityImage, \ TrialCommoditySku, ProductBrand, TrialCommodity, FreshManFirstProduct, ProductSku, FreshManFirstSku, \ - FreshManFirstApply, MagicBoxApply, GuessNumAwardApply, ProductCategory, ProductSkuValue, Base, SettlenmentApply, \ + FreshManFirstApply, MagicBoxApply, GuessNumAwardApply, ProductCategory, SettlenmentApply, \ SupplizerSettlement, ProductImage, GuessNumAwardProduct, GuessNumAwardSku, TimeLimitedProduct, TimeLimitedActivity, \ - TimeLimitedSku, IntegralProduct, IntegralProductSku, NewsAward, AdminActions, GroupGoodsProduct, Toilet, Guide + TimeLimitedSku, IntegralProduct, IntegralProductSku, NewsAward, AdminActions, GroupGoodsProduct, Toilet, Guide, \ + ActivationType, Activation, Ticket, TicketsOrder, TicketsOrderActivation from planet.service.SApproval import SApproval from json import JSONEncoder as _JSONEncoder @@ -66,7 +69,7 @@ class BASEAPPROVAL(): def create_approval(self, avtype, startid, avcontentid, applyfrom=None, **kwargs): - gennerc_log('start create approval ptid = {0}'.format(avtype)) + current_app.logger.info('start create approval ptid = {0}'.format(avtype)) pt = PermissionType.query.filter_by_(PTid=avtype).first_('参数异常') start, content = self.__get_approvalcontent(pt, startid, avcontentid, applyfrom=applyfrom, **kwargs) @@ -558,7 +561,7 @@ def __fill_approval(self, pt, start, content, **kwargs): def __get_approvalcontent(self, pt, startid, avcontentid, **kwargs): start, content = self.__fill_approval(pt, startid, avcontentid, **kwargs) - gennerc_log('get start {0} content {1}'.format(start, content)) + current_app.logger.info('get start {0} content {1}'.format(start, content)) if not (start or content): raise ParamsError('审批流创建失败,发起人或需审批内容已被删除') return start, content @@ -591,23 +594,114 @@ def _commision_preview(self, *args, **kwargs): return self.get_two_float(current_user_comm) @staticmethod - def get_user_location(lat, lng, usid): + def get_user_location(lat, lng, usid, ul=None): from planet.common.get_location import GetLocation from planet.models.user import UserLocation - gl = GetLocation(lat, lng) - result = gl.result + try: + gl = GetLocation(lat, lng) + result = gl.result + except Exception as e: + current_app.logger.error('解析地址失败 {}'.format(e)) + result = { + 'ULlng': lng, + 'ULlat': lat, + 'ULformattedAddress': '请稍后再试' + } with db.auto_commit(): - ul = UserLocation.create({ - 'ULid': str(uuid.uuid1()), - 'ULformattedAddress': result.get('formatted_address'), - 'ULcountry': result.get('addressComponent').get('country'), - 'ULprovince': result.get('addressComponent').get('province'), - 'ULcity': result.get('addressComponent').get('city'), - 'ULdistrict': result.get('addressComponent').get('district'), - 'ULresult': json.dumps(result), - 'ULlng': result.get('location').get('lng'), - 'ULlat': result.get('location').get('lat'), - 'USid': usid, - }) + if ul: + ul.update(result) + db.session.add(ul) + return ul.ULformattedAddress + result.setdefault('USid', usid) + result.setdefault('ULid', str(uuid.uuid1())) + ul = UserLocation.create(result) db.session.add(ul) return ul.ULformattedAddress + + def img_check(self, filepath, msg='图片'): + """ + 图片校验 + :param msg: msg + :param filepath: 完整的绝对路径 + :return: + """ + try: + filesize = os.path.getsize(filepath) + except FileNotFoundError: + current_app.logger.error('FileNotFoundError: {}'.format(filepath)) + raise StatusError('服务器繁忙, 请稍后再试') + current_app.logger.info('size {} MB'.format(round(filesize / 1048576, 2))) + if filesize > 1024 * 1024: + current_app.logger.info('content size out of limit, path :{}'.format(filepath)) + # 图片太大 + from PIL import Image + img = Image.open(filepath) + x, y = img.size + x_ = 750 + y_ = int(y * (x / x_)) + if y_ > 1000: + y_ = 1000 + time_now = datetime.now() + year = str(time_now.year) + month = str(time_now.month) + day = str(time_now.day) + tmp_path = os.path.join( + current_app.config['BASEDIR'], 'img', 'temp', year, month, day) + if not os.path.isdir(tmp_path): + os.makedirs(tmp_path) + tmp_path = os.path.join(tmp_path, os.path.basename(filepath)) + img.resize((x_, y_), Image.LANCZOS).save(tmp_path) + filepath = tmp_path + current_app.logger.info('compressed size {} MB, path :{}'.format( + round(os.path.getsize(filepath) / 1048576, 2), filepath)) + try: + check_result = mp_miniprogram.img_sec_check(filepath) + current_app.logger.info(check_result) + except WeixinMPError as e: + current_app.logger.info('error is {}'.format(e)) + current_app.logger.error('傻逼在发黄色图片 usid = {}'.format(getattr(request, 'user').id)) + raise ParamsError('{}可能存在违法违规等不良信息,请检查后重试'.format(msg)) + + +class BASETICKET(): + + def add_activation(self, attid, usid, contentid, atnum=0, no_loop=False): + att = ActivationType.query.filter_by(ATTid=attid).first() + if not att: + return + if str(attid) != ActivationTypeEnum.reward.value: + atnum = att.ATTnum + + atnum = int(atnum) + at = Activation.create({ + 'ATid': str(uuid.uuid1()), + 'USid': usid, + 'ATTid': attid, + 'ATnum': atnum + }) + + now = datetime.now() + + tso_list = TicketsOrder.query.join(Ticket, Ticket.TIid == TicketsOrder.TIid).filter( + TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value, + Ticket.TIstartTime <= now, + Ticket.TIendTime >= now, + Ticket.isdelete == false(), + TicketsOrder.USid == usid, + TicketsOrder.isdelete == false()).all() + if not tso_list: + current_app.logger.info('活动已结束预热,活跃分不获取') + return + + db.session.add(at) + + for tso in tso_list: + current_app.logger.info('tso status {}'.format(tso.TSOstatus)) + if not no_loop: + tso.TSOactivation += atnum + db.session.add(TicketsOrderActivation.create({ + 'TOAid': str(uuid.uuid1()), + 'TSOid': tso.TSOid, + 'ATid': at.ATid, + 'TOAcontent': contentid + })) diff --git a/planet/control/CActivation.py b/planet/control/CActivation.py new file mode 100644 index 00000000..3841f935 --- /dev/null +++ b/planet/control/CActivation.py @@ -0,0 +1,179 @@ +import uuid +import re +from datetime import datetime + +from flask import request, current_app +from sqlalchemy import false + +from planet.common.error_response import ParamsError, AuthorityError, StatusError +from planet.common.params_validates import parameter_required +from planet.common.success_response import Success +from planet.common.token_handler import admin_required, get_current_admin, token_required, common_user, is_admin, \ + get_current_user +from planet.config.enums import AdminActionS, ActivationTypeType, ActivationTypeEnum +from planet.control.CTicket import CTicket +from planet.extensions.register_ext import db +from planet.models import ActivationType, Activation, UserLinkage, TicketsOrderActivation + + +class CActivation(CTicket): + @admin_required + def update_activationtype(self): + data = parameter_required('attid') + attid = data.pop('attid') + with db.auto_commit(): + att = ActivationType.query.filter_by(ATTid=attid, isdelete=False).first_('活跃度获取方式未被记录') + admin = get_current_admin() + update_dict = { + 'ADid': admin.ADid + } + for key in att.keys(): + lower_key = str(key).lower() + value = data.get(lower_key) + if value or value == 0: + if key != 'ATTname' and not str(value).isdigit(): + raise ParamsError('{} 只能是自然数'.format(getattr(ActivationType, key).comment)) + update_dict.setdefault(key, value) + att.update(update_dict) + db.session.add(att) + + self.BaseAdmin.create_action(AdminActionS.update.value, 'ActivationType', attid) + return Success('修改成功', data=attid) + + def get_activationtype(self): + data = parameter_required('attid') + att = ActivationType.query.filter_by(ATTid=data.get('attid'), isdelete=False).first_('活跃度获取方式未被记录') + return Success(data=att) + + def list_activationtype(self): + data = parameter_required() + filter_args = { + ActivationType.isdelete == false() + } + if data.get('atttype'): + filter_args.add(ActivationType.ATTtype == data.get('atttype')) + att_list = ActivationType.query.filter(*filter_args).order_by(ActivationType.updatetime.desc()).all_with_page() + return Success(data=att_list) + + @token_required + def get_duration_activation(self): + data = parameter_required('tistarttime', 'tiendtime') + if is_admin(): + usid = data.get('usid') + elif common_user(): + usid = getattr(request, 'user').id + else: + raise AuthorityError('请登录') + start = self._trans_time(data.get('tistarttime')) + end = self._trans_time(data.get('tiendtime')) + + at_list = Activation.query.filter( + Activation.USid == usid, Activation.createtime >= start, Activation.createtime <= end).all_with_page() + for at in at_list: + self._fill_at(at) + + def _trans_time(self, time): + if isinstance(time, datetime): + return time + try: + time = datetime.strptime(str(time), '%Y-%m-%d %H:%M:%S') + return time + except Exception as e: + current_app.logger.info('时间格式不正确 time str {} error {}'.format(time, e)) + raise ParamsError('时间格式不正确') + + def _fill_at(self, at): + att = ActivationType.query.filter_by(ATTid=at.ATTid, isdelet=False).first() + if not att: + return + at.fill('attname', att.ATTname) + + @token_required + def bind_linkage(self): + data = parameter_required() + userlinkages = data.get('userlinkages', []) + user = get_current_user() + with db.auto_commit(): + for ula in userlinkages: + if not ula.get('ulaaccount') or not re.sub(r'\s', '', str(ula.get('ulaaccount'))): + continue + ula_instance = UserLinkage.query.filter_by(USid=user.USid, ATTid=ula.get('attid'), + isdelete=False).first() + if ula_instance: + current_app.logger.info('已经绑定账号 {}'.format(ula_instance.ULAaccount)) + if ula.get('ulaaccount') != ula_instance.ULAaccount: + current_app.logger.info('修改已经绑定账号为 {}'.format(ula.get('ulaaccount'))) + + if ula_instance.ULAaccount: + continue + ula_instance.ULAaccount = ula.get('ulaaccount') + db.session.add(ula_instance) + else: + ula_instance = UserLinkage.create({ + 'ULAid': str(uuid.uuid1()), + 'ATTid': ula.get('attid'), + 'USid': user.USid, + 'ULAaccount': ula.get('ulaaccount') + }) + db.session.add(ula_instance) + current_app.logger.info('创建绑定账号 {}'.format(ula.get('ulaaccount'))) + self.Baseticket.add_activation(ula.get('attid'), user.USid, ula_instance.ULAid) + return Success('绑定成功') + + @token_required + def get_userlinkage(self): + user = get_current_user() + infoatt_list = ActivationType.query.filter_by(ATTtype=ActivationTypeType.info.value, isdelete=False).all() + usid = user.USid + for infoatt in infoatt_list: + ula = UserLinkage.query.filter_by(USid=usid, ATTid=infoatt.ATTid, isdelete=False).first() + ulaaccount = None + if ula: + ulaaccount = ula.ULAaccount + infoatt.fill('ulaaccount', ulaaccount) + return Success(data=infoatt_list) + + @admin_required + def reward(self): + data = parameter_required({'trid': '', 'atnum': '打赏数目'}) + admin = get_current_admin() + + with db.auto_commit(): + self._add_activation(data, ActivationTypeEnum.reward.value, admin.ADid) + + return Success('打赏成功') + + @admin_required + def select(self): + data = parameter_required('trid') + select_at = TicketsOrderActivation.query.join( + Activation, Activation.ATid == TicketsOrderActivation.ATid).filter( + Activation.isdelete == false(), + Activation.ATTid == ActivationTypeEnum.selected.value, + TicketsOrderActivation.TOAcontent == data.get('trid'), + TicketsOrderActivation.isdelete == false()).first() + if select_at: + raise StatusError('已经加精') + + with db.auto_commit(): + self._add_activation(data, ActivationTypeEnum.selected.value, data.get('trid')) + + return Success('精选成功') + + def _add_activation(self, data, attid, contentid): + toa_list = TicketsOrderActivation.query.join( + Activation, Activation.ATid == TicketsOrderActivation.ATid).filter( + Activation.isdelete == false(), + TicketsOrderActivation.isdelete == false(), + TicketsOrderActivation.TOAcontent == data.get('trid'), + Activation.ATTid == ActivationTypeEnum.publish.value, + ).all() + if not toa_list: + raise StatusError('当前随笔没有加分到任何活动中') + loop = 0 + for toa in toa_list: + at = Activation.query.filter_by(ATid=toa.ATid, isdelete=False).first() + + # 添加 打赏奖励 + self.Baseticket.add_activation(attid, at.USid, contentid, data.get('atnum', 0), loop) + loop += 1 diff --git a/planet/control/CMaterialFeedback.py b/planet/control/CMaterialFeedback.py index 386dd715..336893e0 100644 --- a/planet/control/CMaterialFeedback.py +++ b/planet/control/CMaterialFeedback.py @@ -9,10 +9,11 @@ from planet.common.params_validates import parameter_required from planet.common.success_response import Success from planet.common.token_handler import phone_required, get_current_user, token_required, is_admin, admin_required -from planet.config.enums import LinkageShareType, UserMaterialFeedbackStatus, ApplyFrom, TicketsOrderStatus +from planet.config.enums import LinkageShareType, UserMaterialFeedbackStatus, ApplyFrom, TicketsOrderStatus, \ + TravelRecordType, TravelRecordStatus, TicketPayType from planet.extensions.register_ext import db from planet.models import UserMaterialFeedback, MaterialFeedbackLinkage, Linkage, Ticket, TicketLinkage, UserWallet, \ - User, TicketsOrder + User, TicketsOrder, TravelRecord class CMaterialFeedback(): @@ -33,6 +34,7 @@ def create(self): mfls = data.get('mfls', []) umf_dict = self._create_umdetails(data) + # todo 同步随笔 with db.auto_commit(): umf_dict.update({ 'UMFid': str(uuid.uuid1()), @@ -54,7 +56,22 @@ def create(self): }) instance_list.append(mfl_instance) + db.session.add_all(instance_list) + + # 创建随笔 + travelrecord_dict = { + 'TRid': str(uuid.uuid1()), + 'AuthorID': user.USid, + 'TRtype': TravelRecordType.essay.value, + 'TRstatus': TravelRecordStatus.published.value, + # 'TRstatus': TravelRecordStatus.auditing.value # todo 待审核状态 + 'PLid': None, + 'TRcontent': umf_dict.get('UMFdetails'), + 'TRlocation': umf_dict.get('UMFlocation')} + + db.session.add(TravelRecord.create(travelrecord_dict)) + return Success('已经提交,请等待审核') @admin_required @@ -66,10 +83,12 @@ def refund(self): umf = UserMaterialFeedback.query.filter_by( UMFid=umfid, UMFstatus=UserMaterialFeedbackStatus.wait.value, isdelete=False).first_('素材反馈已处理') ticket = Ticket.query.filter_by(TIid=umf.TIid, isdelete=False).first_('票务已删除') + tso = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), TicketsOrder.TSOid == umf.TSOid).first() # 修改状态 umf.UMFstatus = UserMaterialFeedbackStatus.refund.value - price = Decimal(str(ticket.TIprice)).quantize(Decimal('0.00')) + price = Decimal(str(ticket.TIdeposit)).quantize( + Decimal('0.00')) if tso.TSOtype == TicketPayType.deposit.value else Decimal('0') # 退钱 user_wallet = UserWallet.query.filter_by(USid=umf.USid).first() if user_wallet: @@ -95,14 +114,18 @@ def refund(self): 'CommisionFor': ApplyFrom.user.value }) db.session.add(user_wallet_instance) + # 同一购票记录的其他凭证修改状态为已处理 UserMaterialFeedback.query.filter( UserMaterialFeedback.UMFid != umfid, - UserMaterialFeedback.isdelete == false(), - UserMaterialFeedback.UMFstatus != UserMaterialFeedbackStatus.reject.value, - UserMaterialFeedback.TSOid == umf.TSOid).update( + UserMaterialFeedback.isdelete == false(), + UserMaterialFeedback.UMFstatus != UserMaterialFeedbackStatus.reject.value, + UserMaterialFeedback.TSOid == umf.TSOid).update( {'UMFstatus': UserMaterialFeedbackStatus.refund.value}) + # 修改订单状态 + tso.TSOstatus = TicketsOrderStatus.accomplish.value + db.session.add(tso) return Success @admin_required diff --git a/planet/control/CMiniProgramPersonalCenter.py b/planet/control/CMiniProgramPersonalCenter.py index d32fb65e..38d3da77 100644 --- a/planet/control/CMiniProgramPersonalCenter.py +++ b/planet/control/CMiniProgramPersonalCenter.py @@ -9,10 +9,12 @@ from planet.common.success_response import Success from planet.common.token_handler import phone_required from planet.common.error_response import ParamsError -from planet.config.enums import EnterLogStatus, ApplyFrom, ApprovalAction, GuideApplyStatus, MakeOverStatus, PlayPayType +from planet.config.enums import EnterLogStatus, ApplyFrom, ApprovalAction, GuideApplyStatus, MakeOverStatus, \ + PlayPayType, TicketsOrderStatus, TicketPayType, UserMaterialFeedbackStatus from planet.control.BaseControl import BASEAPPROVAL from planet.extensions.register_ext import db from planet.extensions.tasks import auto_agree_task +from planet.models import TicketsOrder, Ticket, UserMaterialFeedback from planet.models.scenicspot import Guide from planet.models.user import User, UserWallet, CashNotes, CoveredCertifiedNameLog from planet.models.join import EnterLog, CancelApply @@ -31,7 +33,12 @@ def my_wallet(self): raise ParamsError('date 格式错误') year, month = str(date).split('-') if date else (datetime.now().year, datetime.now().month) if option == 'expense': + start_time = datetime.now() + current_app.logger.info('start query: {}'.format(start_time)) transactions, total = self._get_transactions(user, year, month, args) + end_time = datetime.now() + current_app.logger.info('end query: {}'.format(end_time)) + current_app.logger.info('query cost: {}'.format(end_time - start_time)) elif option == 'withdraw': transactions, total = self._get_withdraw(user, year, month) else: @@ -110,6 +117,52 @@ def _get_transactions(self, user, year, month, args): 'time': i[1], 'title': '[退团] ' + i[2] if i[3] == user.USid else '[团员退出] ' + i[2]} ) for i in ca_query if i[0] is not None] + # 门票付款 + ti_query = db.session.query(Ticket.TIdeposit, Ticket.TIprice, TicketsOrder.TSOtype, + TicketsOrder.createtime, Ticket.TIname, TicketsOrder.TSOstatus, + TicketsOrder.updatetime + ).outerjoin(TicketsOrder, TicketsOrder.TIid == Ticket.TIid + ).filter(Ticket.isdelete == false(), + TicketsOrder.isdelete == false(), + TicketsOrder.USid == user.USid, + # TicketsOrder.TSOstatus > TicketsOrderStatus.not_won.value, + extract('month', TicketsOrder.createtime) == month, + extract('year', TicketsOrder.createtime) == year + ).all() + [transactions.append( + {'amount': (i[0] if i[5] == TicketsOrderStatus.not_won.value and i[2] != TicketPayType.scorepay.value else + 0 if i[5] == TicketsOrderStatus.not_won.value and i[2] == TicketPayType.scorepay.value else + -i[0] if i[2] == TicketPayType.deposit.value else + -i[1] if i[2] == TicketPayType.cash.value else 0), + 'time': i[3] if i[5] != TicketsOrderStatus.not_won.value else i[6], + 'title': ('[退试用押金] ' if i[5] == TicketsOrderStatus.not_won.value else + '[押金试用] ' if i[2] == TicketPayType.deposit.value else + '[购买] ' if i[2] == TicketPayType.cash.value else + '[支付分试用] ') + i[4]} + ) for i in ti_query if i[0] is not None] + + # 完成反馈后退押金 + ticket_deposit_query = db.session.query( + Ticket.TIdeposit.label('amout'), + UserMaterialFeedback.updatetime.label('time'), + Ticket.TIname.label('title'), + TicketsOrder.TSOtype).join( + TicketsOrder, TicketsOrder.TIid == Ticket.TIid).join( + UserMaterialFeedback, + UserMaterialFeedback.TSOid == TicketsOrder.TSOid).filter( + TicketsOrder.isdelete == false(), + Ticket.isdelete == false(), + UserMaterialFeedback.isdelete == false(), + TicketsOrder.USid == user.USid, + UserMaterialFeedback.UMFstatus == UserMaterialFeedbackStatus.refund.value, + TicketsOrder.TSOtype == TicketPayType.deposit.value, + extract('month', UserMaterialFeedback.updatetime) == month, + extract('year', UserMaterialFeedback.updatetime) == year + ).all() + + [transactions.append({'amount': i[0], 'time': i[1], 'title': '[返还押金] ' + i[2]} + ) for i in ticket_deposit_query if i[1] is not None] + transactions.sort(key=lambda x: x.get('time'), reverse=True) total = sum(i.get('amount', 0) for i in transactions) for item in transactions: diff --git a/planet/control/CNews.py b/planet/control/CNews.py index b37efa44..a4116a42 100644 --- a/planet/control/CNews.py +++ b/planet/control/CNews.py @@ -1258,9 +1258,10 @@ def news_shelves(self): @token_required def get_location(self): """获取定位""" - user = get_current_user() + # user = get_current_user() + usid = getattr(request, 'user').id data = parameter_required(('longitude', 'latitude')) - current_location = BaseController().get_user_location(data.get('latitude'), data.get('longitude'), user.USid) + current_location = BaseController().get_user_location(data.get('latitude'), data.get('longitude'), usid) return Success(data={'nelocation': current_location}) def convert_test(self): diff --git a/planet/control/CPlay.py b/planet/control/CPlay.py index 9911bb2a..8f722376 100644 --- a/planet/control/CPlay.py +++ b/planet/control/CPlay.py @@ -16,11 +16,11 @@ from planet.common.playpicture import PlayPicture from planet.common.success_response import Success from planet.common.token_handler import get_current_user, phone_required, common_user, token_required, is_admin, \ - binded_phone + binded_phone, admin_required from planet.config.enums import PlayStatus, EnterCostType, EnterLogStatus, PayType, Client, OrderFrom, SigninLogStatus, \ - CollectionType, CollectStatus, MiniUserGrade, ApplyStatus, MakeOverStatus, PlayPayType, TicketDepositType, \ - TicketsOrderStatus + CollectionType, CollectStatus, MiniUserGrade, ApplyStatus, MakeOverStatus, PlayPayType, \ + TicketsOrderStatus, RoleType, TicketPayType from planet.common.Inforsend import SendSMS @@ -34,7 +34,7 @@ from planet.extensions.weixin.pay import WeixinPayError from planet.models import Cost, Insurance, Play, PlayRequire, EnterLog, EnterCost, User, Gather, SignInSet, SignInLog, \ HelpRecord, UserCollectionLog, Notice, UserLocation, UserWallet, CancelApply, PlayDiscount, Agreement, MakeOver, \ - SuccessorSearchLog, PlayPay, SharingParameters, UserInvitation, TicketDeposit, TicketsOrder + SuccessorSearchLog, PlayPay, SharingParameters, UserInvitation, TicketsOrder class CPlay(): @@ -144,6 +144,17 @@ def get_play(self): return Success(data=play) + def get_role(self): + data = parameter_required() + amtype = self._check_roletype(data.get('amtype')) + + role = Agreement.query.filter_by(AMtype=amtype, isdelete=False).first() + return Success(data=role) + + def list_role(self): + data = parameter_required() + return Success(data=Agreement.query.filter_by(isdelete=False).order_by(Agreement.AMtype.asc()).all()) + @phone_required def get_play_list(self): data = parameter_required() @@ -781,6 +792,18 @@ def set_play(self): self._auto_playstatus(play) return Success(data=plid) + @admin_required + def update_role(self): + data = parameter_required('amtype') + # amtype = int(data.get('amtype', 0) or 0) + with db.auto_commit(): + amtype = self._check_roletype(data.get('amtype', 0)) + role = Agreement.query.filter_by(AMtype=amtype, isdelete=False).first() + if not role: + raise ParamsError('规则失效') + role.AMcontent = data.get('amcontent') + return Success('更新成功') + @phone_required def set_cost(self): data = parameter_required() @@ -2308,21 +2331,26 @@ def _base_decode(self, raw): @staticmethod def _ticket_order(pp): - tds = TicketDeposit.query.filter(TicketDeposit.isdelete == false(), TicketDeposit.OPayno == pp.PPpayno).all() - for td in tds: - to = TicketsOrder.query.filter(TicketsOrder.TSOid == td.TSOid).first() - if to and td.TDtype == TicketDepositType.grab.value: - current_app.logger.info('grap tosid: {}'.format(to.TSOid)) - current_app.logger.info('grap toscode: {}'.format(to.TSOcode)) - while TicketsOrder.query.filter(TicketsOrder.isdelete == false(), - TicketsOrder.TSOcode == to.TSOcode, - TicketsOrder.TIid == to.TIid, - TicketsOrder.TSOid != to.TSOid).first(): - current_app.logger.info('found conflict toscode: {}'.format(to.TSOcode)) - to.TSOcode += 1 - to.isdelete = False + current_app.logger.info('ticket pay notify, ppid: {}'.format(pp.PPid)) + to = TicketsOrder.query.filter(TicketsOrder.TSOid == pp.PPcontent).first() + if to: + current_app.logger.info('get paid tsoid: {}'.format(to.TSOid)) + current_app.logger.info('tsotype is : {}'.format(to.TSOtype)) + to.isdelete = False + if to.TSOtype == TicketPayType.deposit.value: # 押金付 + to.TSOstatus = TicketsOrderStatus.pending.value + elif to.TSOtype == TicketPayType.scorepay.value: # 信用付 to.TSOstatus = TicketsOrderStatus.pending.value - elif to and td.TDtype == TicketDepositType.patch.value: - if to.TSOstatus == TicketsOrderStatus.has_won.value: - current_app.logger.info('patch tosid: {}'.format(to.TSOid)) - to.TSOstatus = TicketsOrderStatus.completed.value + elif to.TSOtype == TicketPayType.cash.value: # 直接买 + from planet.control.CTicket import CTicket + to.TSOstatus = TicketsOrderStatus.has_won.value + to.TSOqrcode = CTicket()._ticket_order_qrcode(to.TSOid, to.USid) + + def _check_roletype(self, amtype): + try: + amtype_ = int(amtype or 0) + amtype_ = RoleType(amtype_).value + return amtype_ + except: + current_app.logger.info('非法类型 {}'.format(amtype)) + raise ParamsError('规则不存在') diff --git a/planet/control/CScenicSpot.py b/planet/control/CScenicSpot.py index bfbbb8d6..92ec9f8e 100644 --- a/planet/control/CScenicSpot.py +++ b/planet/control/CScenicSpot.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import json +import os import uuid import re from datetime import datetime @@ -10,14 +11,14 @@ from planet.common.success_response import Success from planet.common.token_handler import admin_required, is_admin, phone_required, common_user from planet.config.enums import AdminActionS, TravelRecordType, TravelRecordStatus, MiniUserGrade, CollectionType, \ - EnterLogStatus, ApplyFrom, ApprovalAction, ApplyStatus + EnterLogStatus, ApplyFrom, ApprovalAction, ApplyStatus, ActivationTypeEnum from planet.config.http_config import API_HOST from planet.extensions.register_ext import db, mp_miniprogram from planet.extensions.weixin.mp import WeixinMPError -from planet.models import EnterLog, Play, Approval +from planet.models import EnterLog, Play, Approval, TicketsOrderActivation, Activation, TicketsOrder, Ticket from planet.models.user import AddressArea, AddressCity, AddressProvince, Admin, User, UserCollectionLog from planet.models.scenicspot import ScenicSpot, TravelRecord, Toilet, CustomizeShareContent -from planet.control.BaseControl import BASEADMIN, BaseController, BASEAPPROVAL +from planet.control.BaseControl import BASEADMIN, BaseController, BASEAPPROVAL, BASETICKET from planet.control.CPlay import CPlay from planet.control.CUser import CUser from pyquery import PyQuery @@ -28,6 +29,7 @@ class CScenicSpot(BASEAPPROVAL): def __init__(self): self.BaseAdmin = BASEADMIN() self.BaseController = BaseController() + self.BaseTicket = BASETICKET() self.cplay = CPlay() self.cuser = CUser() # self.scale_dict = {3: 1000000, 4: 500000, 5: 200000, 6: 100000, 7: 50000, @@ -166,6 +168,7 @@ def _get_search_scenicspots(args): def list(self): """景区列表""" + return Success(data=[]) # todo 防爬暂屏蔽 args = parameter_required(('page_num', 'page_size')) option = args.get('option') if option: @@ -226,6 +229,7 @@ def list(self): def get(self): """景区详情""" + return Success(data='') # todo 防爬暂屏蔽 args = parameter_required(('sspid',)) sspid = args.get('sspid') scenicspot = ScenicSpot.query.filter_by_(SSPid=sspid).first_('未找到该景区信息') @@ -315,17 +319,21 @@ def add_travelrecord(self): 'PLid': plid if plid else None } travelrecord_dict.update(tr_dict) + check_flag = False try: check_content = travelrecord_dict.get('TRcontent') if trtype == str(TravelRecordType.essay.value): check_content = json.loads(check_content).get('text') - mp_miniprogram.msg_sec_check(check_content) - except WeixinMPError: + self.BaseTicket.add_activation( + ActivationTypeEnum.publish.value, user.USid, travelrecord_dict.get('TRid')) + check_res = mp_miniprogram.msg_sec_check(check_content) + current_app.logger.info('content_sec_check: {}'.format(check_res)) + except WeixinMPError as e: + current_app.logger.info('check result: {}'.format(e)) + check_flag = True travelrecord_dict['isdelete'] = True db.session.add(TravelRecord.create(travelrecord_dict)) - try: - current_app.logger.info('content_sec_check: {}'.format(mp_miniprogram.msg_sec_check(check_content))) - except WeixinMPError: + if check_flag: raise ParamsError('您输入的内容含有部分敏感词汇,请检查后重新发布') return Success('发布成功', {'trid': travelrecord_dict['TRid']}) @@ -354,15 +362,25 @@ def _create_travels(data): def _create_essay(self, data): """随笔""" text, image, video = data.get('text'), data.get('image'), data.get('video') - # if image: - # current_app.logger.error("图片校验测试") - # current_app.logger.error(mp_miniprogram.img_sec_check(image)) + if image: + current_app.logger.info("图片校验测试") + for img in image: + img_ = str(img).split('.bigxingxing.com')[-1][1:] + filepath = os.path.join(current_app.config['BASEDIR'], str(img_).split('_')[0]) + self.BaseController.img_check(filepath) + if image and not isinstance(image, list): raise ParamsError('image 格式错误') if image and video: raise ParamsError('不能同时选择图片和视频') if image and len(image) > 9: raise ParamsError('最多可上传9张图片') + if video: + thumbnail = video.get('thumbnail') + current_app.logger.info("视频内容安全校验") + thumbnail_ = str(thumbnail).split('.bigxingxing.com')[-1][1:] + thumbnail_path = os.path.join(current_app.config['BASEDIR'], str(thumbnail_)) + self.BaseController.img_check(thumbnail_path, msg='视频') video = {'url': self._check_upload_url(video.get('url')), 'thumbnail': video.get('thumbnail'), 'duration': video.get('duration') @@ -386,6 +404,7 @@ def travelrecord_list(self): """时光记录(个人中心)列表""" args = request.args.to_dict() usid, date, area, trtype = args.get('usid'), args.get('date'), args.get('area'), args.get('trtype') + tiid = args.get('tiid') option = args.get('option') if usid: ucl_list = [usid] @@ -430,6 +449,18 @@ def travelrecord_list(self): or_(*map(lambda x: TravelRecord.TRlocation.ilike('%{}%'.format(x)), ssname))) if common_user() and option == 'my': trecords_query = trecords_query.filter(TravelRecord.AuthorID == getattr(request, 'user').id) + if tiid: + ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == tiid).first_('未找到相应信息') + # tso_usid = [i[0] for i in db.session.query(TicketsOrder.USid + # ).filter(TicketsOrder.isdelete == false(), + # TicketsOrder.TIid == tiid).all() if i is not None] + trecords_query = trecords_query.outerjoin(TicketsOrder, TicketsOrder.USid == TravelRecord.AuthorID + ).filter(TicketsOrder.isdelete == false(), + or_(and_(TravelRecord.createtime >= ticket.TIstartTime, + TravelRecord.createtime <= ticket.TIendTime)), + and_(TravelRecord.createtime >= ticket.TItripStartTime, + TravelRecord.createtime <= ticket.TItripEndTime), ) + trecords = trecords_query.order_by(TravelRecord.createtime.desc()).all_with_page() [self._fill_travelrecord(x) for x in trecords] return Success(data={'top': top, 'travelrecord': trecords}) @@ -487,6 +518,13 @@ def _fill_travelrecord(trecord): else: showtype = 'text' trecord.fill('showtype', showtype) + select_at = TicketsOrderActivation.query.join( + Activation, Activation.ATid == TicketsOrderActivation.ATid).filter( + Activation.isdelete == false(), + Activation.ATTid == ActivationTypeEnum.selected.value, + TicketsOrderActivation.TOAcontent == trecord.TRid, + TicketsOrderActivation.isdelete == false()).first() + trecord.fill('selected', bool(select_at)) elif trecord.TRtype == TravelRecordType.travels.value: # 游记 trecord.fields = ['TRid', 'TRlocation', 'TRtitle', 'TRtype', 'TRcontent', 'TRstatus'] img_path = PyQuery(trecord.TRcontent)('img').attr('src') diff --git a/planet/control/CSupplizer.py b/planet/control/CSupplizer.py index a3aa4f18..d35ff109 100644 --- a/planet/control/CSupplizer.py +++ b/planet/control/CSupplizer.py @@ -1,12 +1,10 @@ import random import re import uuid -import json from decimal import Decimal from threading import Thread from flask import current_app -from sqlalchemy import or_, and_ -# from pymysql.err import IntegrityError +from sqlalchemy import or_, and_, false from sqlalchemy.exc import IntegrityError from werkzeug.security import generate_password_hash, check_password_hash @@ -17,13 +15,14 @@ from planet.common.success_response import Success from planet.common.token_handler import admin_required, is_admin, is_supplizer, token_required, is_tourist from planet.config.enums import ProductBrandStatus, UserStatus, ProductStatus, ApplyFrom, NotesStatus, OrderMainStatus, \ - ApplyStatus, OrderRefundORAstate, OrderRefundOrstatus, WexinBankCode, AdminAction, AdminActionS + ApplyStatus, OrderRefundORAstate, OrderRefundOrstatus, WexinBankCode, AdminActionS, SupplizerGrade from planet.control.BaseControl import BASEADMIN from planet.extensions.register_ext import db, conn from planet.extensions.validates.user import SupplizerListForm, SupplizerCreateForm, SupplizerGetForm, \ - SupplizerUpdateForm, SupplizerSendCodeForm, SupplizerResetPasswordForm, SupplizerChangePasswordForm, request + SupplizerUpdateForm, SupplizerSendCodeForm, SupplizerResetPasswordForm, SupplizerChangePasswordForm, request, \ + GetVerifier, SetVerifier from planet.models import Supplizer, ProductBrand, Products, UserWallet, SupplizerAccount, ManagerSystemNotes, \ - OrderMain, OrderRefundApply, OrderRefund, OrderPart, SupplizerDepositLog, Admin + OrderMain, OrderRefundApply, OrderRefund, OrderPart, SupplizerDepositLog, Admin, TicketVerifier class CSupplizer: @@ -37,12 +36,30 @@ def list(self): kw = form.kw.data mobile = form.mobile.data sustatus = form.sustatus.data + option = form.option.data + sugrade = form.sugrade.data + # if sugrade is not None or sugrade: + # sugrade = int(sugrade) + if str(sugrade).isdigit(): + sugrade = int(sugrade) + else: + sugrade = None + + if option == 'ticket': + return self._list_ticket_sup() + filter_args = { + Supplizer.isdelete == false(), + } + if sugrade: + filter_args.add(Supplizer.SUgrade == sugrade) + if sustatus or sustatus == 0: + filter_args.add(Supplizer.SUstatus == sustatus) + if kw: + filter_args.add(Supplizer.SUname.contains(kw)) + if mobile: + filter_args.add(Supplizer.SUlinkPhone.contains(mobile)) + supplizers = Supplizer.query.filter(*filter_args).order_by(Supplizer.createtime.desc()).all_with_page() - supplizers = Supplizer.query.filter_by_().filter_( - Supplizer.SUname.contains(kw), - Supplizer.SUlinkPhone.contains(mobile), - Supplizer.SUstatus == sustatus - ).order_by(Supplizer.createtime.desc()).all_with_page() for supplizer in supplizers: supplizer.hide('SUpassword') if is_admin(): @@ -70,6 +87,16 @@ def list(self): supplizer.fill('sustatus_en', UserStatus(supplizer.SUstatus).name) return Success(data=supplizers) + @staticmethod + def _list_ticket_sup(): + sups = Supplizer.query.filter(Supplizer.isdelete == false(), Supplizer.SUstatus == UserStatus.usual.value, + Supplizer.SUgrade == SupplizerGrade.ticket.value).all_with_page() + for sup in sups: + sup.fields = ['SUid', 'SUname', 'SUgrade', 'SUstatus'] + sup.fill('sustatus_zh', UserStatus(sup.SUstatus).zh_value) + sup.fill('sugrade_zh', SupplizerGrade(sup.SUgrade).zh_value) + return Success(data=sups) + def create(self): """添加""" if is_admin(): @@ -114,6 +141,7 @@ def create(self): 'SUemail': form.suemail.data, 'SUlegalPersonIDcardFront': form.sulegalpersonidcardfront.data, 'SUlegalPersonIDcardBack': form.sulegalpersonidcardback.data, + 'SUgrade': form.sugrade.data or 0, }) db.session.add(supperlizer) if is_admin(): @@ -573,3 +601,57 @@ def add_update_notes(self): db.session.add(mn) BASEADMIN().create_action(AdminActionS.insert.value, 'ManagerSystemNotes', str(uuid.uuid1())) return Success('创建通告成功', data=mn.MNid) + + @token_required + def get_verifier(self): + form = GetVerifier().valid_data() + suid = form.suid.data + if is_supplizer(): + suid = request.user.id + if not suid: + raise ParamsError('未指定供应商') + tv_list = TicketVerifier.query.filter_by(SUid=suid, isdelete=False).order_by( + TicketVerifier.TVphone.desc()).all_with_page() + + phone_list = [tv.TVphone for tv in tv_list] + return Success(data=phone_list) + + @token_required + def set_verifier(self): + form = SetVerifier().valid_data() + if is_admin(): + suid = form.suid.data + assert suid, '供应商未指定' + elif is_supplizer(): + suid = request.user.id + else: + raise AuthorityError() + sup = Supplizer.query.filter(Supplizer.isdelete == false(), + Supplizer.SUstatus == UserStatus.usual.value, + Supplizer.SUid == suid).first_('无此供应商') + if sup.SUgrade != SupplizerGrade.ticket.value: + raise StatusError('仅虚拟商品供应商可设置核销员') + phone_list = form.phone_list.data + tvid_list = [] + instence_list = [] + phone_list = {}.fromkeys(phone_list).keys() + with db.auto_commit(): + for phone in phone_list: + tv = TicketVerifier.query.filter_by(SUid=suid, TVphone=phone).first() + if not tv: + tv = TicketVerifier.create({ + 'TVid': str(uuid.uuid1()), + 'SUid': suid, + 'TVphone': phone + }) + instence_list.append(tv) + tvid_list.append(tv.TVid) + + db.session.add_all(instence_list) + # 删除无效的 + TicketVerifier.query.filter( + TicketVerifier.isdelete == false(), + TicketVerifier.SUid == suid, + TicketVerifier.TVid.notin_(tvid_list) + ).delete_(synchronize_session=False) + return Success('修改成功', data=suid) diff --git a/planet/control/CTicket.py b/planet/control/CTicket.py index 1099877d..1d4ceed8 100644 --- a/planet/control/CTicket.py +++ b/planet/control/CTicket.py @@ -1,61 +1,65 @@ # -*- coding: utf-8 -*- +import os import json +import random import uuid -from datetime import datetime, timedelta +import requests +from sqlalchemy import false, func, cast, Date from flask import current_app, request -from sqlalchemy import false, func -from decimal import Decimal -from planet.extensions.tasks import start_ticket, end_ticket, celery +from datetime import datetime, timedelta +from planet.config.secret import API_HOST +from planet.common.playpicture import PlayPicture +from planet.common.make_qrcode import qrcodeWithtext +from planet.common.success_response import Success from planet.common.error_response import ParamsError, TokenError, StatusError from planet.common.params_validates import parameter_required, validate_price, validate_arg -from planet.common.success_response import Success -from planet.common.token_handler import admin_required, is_admin, phone_required, common_user -from planet.common.playpicture import PlayPicture -from planet.config.enums import AdminActionS, TicketStatus, TicketsOrderStatus, PlayPayType, TicketDepositType, \ - PayType, UserMaterialFeedbackStatus -from planet.extensions.register_ext import db, conn -from planet.config.secret import API_HOST +from planet.common.token_handler import admin_required, is_admin, phone_required, common_user, is_supplizer, \ + token_required +from planet.models import Guide, PlayPay, TicketVerifier, TicketVerifiedRecord +from planet.models.product import Supplizer +from planet.models.play import Agreement +from planet.models.user import User, UserInvitation, SharingType from planet.models.ticket import Ticket, Linkage, TicketLinkage, TicketsOrder, TicketDeposit, UserMaterialFeedback, \ TicketRefundRecord -from planet.models.user import User, UserInvitation -from planet.control.BaseControl import BASEADMIN +from planet.control.BaseControl import BASEADMIN, BASETICKET from planet.control.CPlay import CPlay +from planet.control.CUser import CUser +from planet.extensions.register_ext import db, conn +from planet.extensions.tasks import start_ticket, end_ticket, celery +from planet.config.enums import AdminActionS, TicketStatus, TicketsOrderStatus, PlayPayType, \ + PayType, ActivationTypeEnum, ShareType, UserStatus, SupplizerGrade, RoleType, TicketPayType, GuideApplyStatus class CTicket(CPlay): + TICKET_LIST_FIELDS = ['TIid', 'TIname', 'TIimg', 'TIstartTime', 'TIendTime', 'TIstatus', 'interrupt', + 'tistatus_zh', 'TInum', 'apply_num', 'tsoid'] def __init__(self): super(CTicket, self).__init__() self.BaseAdmin = BASEADMIN() + self.Baseticket = BASETICKET() + self.cuser = CUser() @admin_required def create_ticket(self): """创建票务""" data = request.json - (tistarttime, tiendtime, tiprice, tideposit, tinum, liids, ticategory, - titripstarttime, titripendtime) = self._validate_ticket_param(data) + ticket_dict, liids = self._validate_ticket_param(data) if Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIname == data.get('tiname')).first(): raise ParamsError('该门票名称已存在') instance_list = [] with db.auto_commit(): - ticket = Ticket.create({'TIid': str(uuid.uuid1()), - 'ADid': getattr(request, 'user').id, - 'TIname': data.get('tiname'), - 'TIimg': data.get('tiimg'), - 'TIstartTime': tistarttime, - 'TIendTime': tiendtime, - 'TItripStartTime': titripstarttime, - 'TItripEndTime': titripendtime, - 'TIrules': data.get('tirules'), - 'TIcertificate': data.get('ticertificate') if data.get('ticertificate') else None, - 'TIdetails': data.get('tidetails'), - 'TIprice': tiprice, - 'TIdeposit': tideposit, - 'TIstatus': TicketStatus.ready.value, - 'TInum': tinum, - 'TIabbreviation': data.get('tiabbreviation'), - 'TIcategory': ticategory - }) + ticket_dict.update({'TIid': str(uuid.uuid1()), + 'ADid': getattr(request, 'user').id, + 'TIname': data.get('tiname'), + 'TIimg': data.get('tiimg'), + # 'TIrules': data.get('tirules'), + 'TIcertificate': data.get('ticertificate') if data.get('ticertificate') else None, + 'TIdetails': data.get('tidetails'), + 'TIstatus': TicketStatus.ready.value, + # 'TIabbreviation': data.get('tiabbreviation'), + }) + ticket = Ticket.create(ticket_dict) instance_list.append(ticket) for liid in liids: linkage = Linkage.query.filter(Linkage.isdelete == false(), Linkage.LIid == liid).first() @@ -67,9 +71,9 @@ def create_ticket(self): instance_list.append(tl) db.session.add_all(instance_list) # 异步任务: 开始 - self._create_celery_task(ticket.TIid, tistarttime) + self._create_celery_task(ticket.TIid, ticket_dict.get('TIstartTime')) # 异步任务: 结束 - self._create_celery_task(ticket.TIid, tiendtime, start=False) + self._create_celery_task(ticket.TIid, ticket_dict.get('TIendTime'), start=False) self.BaseAdmin.create_action(AdminActionS.insert.value, 'Ticket', ticket.TIid) return Success('创建成功', data={'tiid': ticket.TIid}) @@ -87,6 +91,10 @@ def update_ticket(self): if data.get('delete'): if ticket.TIstatus == TicketStatus.active.value: raise ParamsError('无法直接删除正在抢票中的活动') + if TicketsOrder.query.filter(TicketsOrder.isdelete == false(), + TicketsOrder.TSOstatus != TicketsOrderStatus.not_won.value, + TicketsOrder.TIid == ticket.TIid).first(): + raise StatusError('暂时无法直接删除已产生购买记录的门票') ticket.update({'isdelete': True}) TicketLinkage.query.filter(TicketLinkage.isdelete == false(), TicketLinkage.TIid == ticket.TIid).delete_(synchronize_session=False) @@ -98,36 +106,28 @@ def update_ticket(self): raise StatusError('该状态下无法中止') if ticket.TIstatus == TicketStatus.active.value: # 抢票中的退押金 current_app.logger.info('interrupt active ticket') - ticket_orders = self._query_not_won(ticket.TIid) - total_row = 0 - for to_info in ticket_orders: - row_count = self._deposit_refund(to_info) - total_row += row_count - current_app.logger.info('共退款{}条记录'.format(total_row)) + ticket_orders = TicketsOrder.query.filter( + TicketsOrder.isdelete == false(), + TicketsOrder.TIid == ticket.TIid, + TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value, + TicketsOrder.TSOtype != TicketPayType.cash.value).all() + row_count = self._deposit_refund(ticket_orders, ticket) # 活动临时中断,除购买外全退钱 + current_app.logger.info('共退款{}条记录'.format(row_count)) ticket.update({'TIstatus': TicketStatus.interrupt.value}) self._cancle_celery_task('start_ticket{}'.format(ticket.TIid)) self._cancle_celery_task('end_ticket{}'.format(ticket.TIid)) else: if ticket.TIstatus < TicketStatus.interrupt.value: raise ParamsError('仅可编辑已中止或已结束的活动') - (tistarttime, tiendtime, tiprice, tideposit, tinum, liids, ticategory, - titripstarttime, titripendtime) = self._validate_ticket_param(data) - ticket_dict = {'TIname': data.get('tiname'), - 'TIimg': data.get('tiimg'), - 'TIstartTime': tistarttime, - 'TIendTime': tiendtime, - 'TItripStartTime': titripstarttime, - 'TItripEndTime': titripendtime, - 'TIrules': data.get('tirules'), - 'TIcertificate': data.get('ticertificate'), - 'TIdetails': data.get('tidetails'), - 'TIprice': tiprice, - 'TIdeposit': tideposit, - 'TIstatus': TicketStatus.ready.value, - 'TInum': tinum, - 'TIabbreviation': data.get('tiabbreviation'), - 'TIcategory': ticategory - } + ticket_dict, liids = self._validate_ticket_param(data) + ticket_dict.update({'TIname': data.get('tiname'), + 'TIimg': data.get('tiimg'), + # 'TIrules': data.get('tirules'), + 'TIcertificate': data.get('ticertificate'), + 'TIdetails': data.get('tidetails'), + 'TIstatus': TicketStatus.ready.value, + # 'TIabbreviation': data.get('tiabbreviation'), + }) if ticket.TIstatus == TicketStatus.interrupt.value: # 中止的情况 current_app.logger.info('edit interrupt ticket') ticket.update(ticket_dict) @@ -135,7 +135,8 @@ def update_ticket(self): TicketLinkage.TIid == ticket.TIid).delete_() # 删除原来的关联 else: # 已结束的情况,重新发起 current_app.logger.info('edit ended ticket') - ticket_dict.update({'TIid': str(uuid.uuid1())}) + ticket_dict.update({'TIid': str(uuid.uuid1()), + 'ADid': getattr(request, 'user').id}) ticket = Ticket.create(ticket_dict) for liid in liids: @@ -148,28 +149,28 @@ def update_ticket(self): instance_list.append(tl) self._cancle_celery_task('start_ticket{}'.format(ticket.TIid)) self._cancle_celery_task('end_ticket{}'.format(ticket.TIid)) - self._create_celery_task(ticket.TIid, tistarttime) - self._create_celery_task(ticket.TIid, tiendtime, start=False) + self._create_celery_task(ticket.TIid, ticket_dict.get('TIstartTime')) + self._create_celery_task(ticket.TIid, ticket_dict.get('TIendTime'), start=False) instance_list.append(ticket) db.session.add_all(instance_list) self.BaseAdmin.create_action(AdminActionS.update.value, 'Ticket', ticket.TIid) return Success('编辑成功', data={'tiid': ticket.TIid}) - @staticmethod - def _validate_ticket_param(data): + def _validate_ticket_param(self, data): parameter_required({'tiname': '票务名称', 'tiimg': '封面图', 'tistarttime': '抢票开始时间', - 'tiendtime': '抢票结束时间', 'tirules': '规则', 'tiprice': '票价', 'tideposit': '最低押金', - 'tinum': '门票数量', 'tidetails': '详情', 'tiabbreviation': '列表页活动类型简称', - 'ticategory': '列表页活动类型标签'}, datafrom=data) + 'tiendtime': '抢票结束时间', 'tiprice': '票价', 'tideposit': '最低押金', 'suid': '票务供应商', + 'tinum': '门票数量', 'tidetails': '详情', 'tibanner': '详情轮播图', 'tiaddress': '游玩场所位置' + }, datafrom=data) tistarttime = validate_arg(r'^\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}$', str(data.get('tistarttime')), '抢票开始时间格式错误') tiendtime = validate_arg(r'^\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}$', str(data.get('tiendtime')), '抢票结束时间格式错误') tistarttime, tiendtime = map(lambda x: datetime.strptime(x, '%Y-%m-%d %H:%M:%S'), (tistarttime, tiendtime)) + now = datetime.now() if tistarttime < now: raise ParamsError('抢票开始时间应大于现在时间') if tiendtime <= tistarttime: raise ParamsError('抢票结束时间应大于开始时间') - + latitude, longitude = super(CTicket, self).check_lat_and_long(data.get('latitude'), data.get('longitude')) titripstarttime = validate_arg(r'^\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}$', str(data.get('titripstarttime')), '游玩开始时间格式错误') titripendtime = validate_arg(r'^\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}$', str(data.get('titripendtime')), @@ -193,11 +194,31 @@ def _validate_ticket_param(data): liids = data.get('liids', []) if not isinstance(liids, list): raise ParamsError('liids 格式错误') - ticategory = data.get('ticategory', []) - if not isinstance(ticategory, list): - raise ParamsError('ticategory 格式错误') - ticategory = json.dumps(ticategory) - return tistarttime, tiendtime, tiprice, tideposit, tinum, liids, ticategory, titripstarttime, titripendtime + # ticategory = data.get('ticategory', []) + # if not isinstance(ticategory, list): + # raise ParamsError('ticategory 格式错误') + # ticategory = json.dumps(ticategory) + if not isinstance(data.get('tibanner'), list): + raise ParamsError('tibanner 格式错误') + tibanner = json.dumps(data.get('tibanner')) + sup = Supplizer.query.filter(Supplizer.isdelete == false(), Supplizer.SUid == data.get('suid'), + Supplizer.SUstatus == UserStatus.usual.value, + Supplizer.SUgrade == SupplizerGrade.ticket.value).first_('票务供应商状态错误') + ticket_dicket = {'TIstartTime': tistarttime, + 'TIendTime': tiendtime, + 'TItripStartTime': titripstarttime, + 'TItripEndTime': titripendtime, + 'TIprice': tiprice, + 'TIdeposit': tideposit, + 'TInum': tinum, + # 'TIcategory': ticategory, + 'TIbanner': tibanner, + 'SUid': sup.SUid, + 'longitude': longitude, + 'latitude': latitude, + 'TIaddress': data.get('tiaddress') + } + return ticket_dicket, liids def get_ticket(self): """门票详情""" @@ -205,9 +226,9 @@ def get_ticket(self): tiid = args.get('tiid') tsoid = args.get('tsoid') secret_usid = args.get('secret_usid') + ticketorder = None if not (tiid or tsoid): raise ParamsError - ticketorder = None if tsoid: if not common_user(): raise TokenError @@ -215,30 +236,50 @@ def get_ticket(self): TicketsOrder.USid == getattr(request, 'user').id, TicketsOrder.TSOid == tsoid).first() tiid = ticketorder.TIid if ticketorder else tiid - if secret_usid: - try: - superid = super(CTicket, self)._base_decode(secret_usid) - current_app.logger.info('secret_usid --> superid {}'.format(superid)) - if common_user() and superid != getattr(request, 'user').id: - with db.auto_commit(): - uin = UserInvitation.create({ - 'UINid': str(uuid.uuid1()), - 'USInviter': superid, - 'USInvited': getattr(request, 'user').id, - 'UINapi': request.path - }) - current_app.logger.info('已创建邀请记录') - db.session.add(uin) - except Exception as e: - current_app.logger.info('secret_usid 记录失败 error = {}'.format(e)) - + elif common_user(): + ticketorder = self._query_traded(tiid, getattr(request, 'user').id) + tiid = ticketorder.TIid if ticketorder else tiid + if secret_usid: # 创建邀请记录 + self._invitation_record(secret_usid, args) ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == tiid).first_('未找到该门票信息') self._fill_ticket(ticket, ticketorder=ticketorder) return Success(data=ticket) - @staticmethod - def _fill_ticket(ticket, ticketorder=None): - ticket.hide('ADid') + def _invitation_record(self, secret_usid, args): + try: + superid = super(CTicket, self)._base_decode(secret_usid) + current_app.logger.info('secret_usid --> superid {}'.format(superid)) + if common_user() and superid != getattr(request, 'user').id: + with db.auto_commit(): + today = datetime.now().date() + uin_exist = UserInvitation.query.filter( + cast(UserInvitation.createtime, Date) == today, + UserInvitation.USInviter == superid, + UserInvitation.USInvited == getattr(request, 'user').id, + ).first() + if uin_exist: + current_app.logger.info('{}今天已经邀请过这个人了{}'.format(superid, getattr(request, 'user').id)) + return + uin = UserInvitation.create({ + 'UINid': str(uuid.uuid1()), + 'USInviter': superid, + 'USInvited': getattr(request, 'user').id, + 'UINapi': request.path + }) + current_app.logger.info('已创建邀请记录') + db.session.add(uin) + db.session.add(SharingType.create({ + 'STid': str(uuid.uuid1()), + 'USid': superid, + 'STtype': args.get('sttype', 0) + })) + self.Baseticket.add_activation( + ActivationTypeEnum.share_old.value, superid, getattr(request, 'user').id) + except Exception as e: + current_app.logger.info('secret_usid 记录失败 error = {}'.format(e)) + + def _fill_ticket(self, ticket, ticketorder=None): + ticket.hide('ADid', 'SUid') now = datetime.now() if ticket.TIstatus == TicketStatus.ready.value and ticket.TIstartTime > now: # 距抢票开始倒计时 countdown = ticket.TIstartTime - now @@ -256,66 +297,151 @@ def _fill_ticket(ticket, ticketorder=None): ticket.fill('countdown', countdown) ticket.fill('tistatus_zh', TicketStatus(ticket.TIstatus).zh_value) - # ticket.fill('residual', ticket.TInum) # todo fake number ? ticket.fill('interrupt', False if ticket.TIstatus < TicketStatus.interrupt.value else True) # 是否中止 - ticket.fill('ticategory', json.loads(ticket.TIcategory)) - tirewardnum, residual_deposit, umf = None, None, None + ticket.fill('tirules', self._query_rules(RoleType.ticketrole.value)) + ticket.fill('scorerule', self._query_rules(RoleType.activationrole.value)) + ticket.fill('apply_num', self._query_award_num( + ticket, filter_status=(TicketsOrder.TSOstatus > TicketsOrderStatus.not_won.value,))) + + # ticket.fill('ticategory', json.loads(ticket.TIcategory)) # 2.0版多余 + show_record = True if ticket.TIstatus == TicketStatus.over.value else False + umf, traded, tsoid = None, False, None if ticketorder: - ticket.fill('tsoid', ticketorder.TSOid) - ticket.fill('tsocode', ticketorder.TSOcode) + show_record = traded = True + tsoid = ticketorder.TSOid + # ticket.fill('tsocode', ticketorder.TSOcode) # 2.0版多余 ticket.fill('tsostatus', ticketorder.TSOstatus) ticket.fill('tsostatus_zh', TicketsOrderStatus(ticketorder.TSOstatus).zh_value) - if ticket.TIstatus == TicketStatus.over.value: - tirewardnum = json.loads(ticket.TIrewardnum) if ticket.TIrewardnum else None - if ticketorder.TSOstatus == TicketsOrderStatus.has_won.value: - residual_deposit = ticket.TIprice - ticket.TIdeposit + # if ticket.TIstatus == TicketStatus.over.value: + # tirewardnum = json.loads(ticket.TIrewardnum) if ticket.TIrewardnum else None + # if ticketorder.TSOstatus == TicketsOrderStatus.has_won.value: # 2.0版多余 + # residual_deposit = ticket.TIprice - ticket.TIdeposit umf = UserMaterialFeedback.query.filter(UserMaterialFeedback.isdelete == false(), UserMaterialFeedback.USid == getattr(request, 'user').id, UserMaterialFeedback.TIid == ticket.TIid, UserMaterialFeedback.TSOid == ticketorder.TSOid, ).order_by(UserMaterialFeedback.createtime.desc()).first() ticket.fill('tsoqrcode', - ticketorder['TSOqrcode'] if ticketorder.TSOstatus > TicketsOrderStatus.has_won.value else None) - umfstatus = -1 if not umf or umf.UMFstatus == UserMaterialFeedbackStatus.reject.value else umf.UMFstatus + ticketorder['TSOqrcode'] if ticketorder.TSOstatus > TicketsOrderStatus.pending.value else None) + scorerank, rank = self._query_single_score(ticketorder, ticket) + ticket.fill('scorerank', scorerank) # 活跃分排名array + ticket.fill('rank', rank) # 自己所在排名 + ticket.fill('tsocreatetime', ticketorder.createtime) + ticket.fill('tsoactivation', ticketorder.TSOactivation) + umfstatus = umf.UMFstatus if umf else None ticket.fill('umfstatus', umfstatus) # 反馈素材审核状态 - ticket.fill('tirewardnum', tirewardnum) # 中奖号码 - ticket.fill('residual_deposit', residual_deposit) # 剩余押金 - ticket.fill('triptime', '{} - {}'.format(ticket.TItripStartTime.strftime("%Y/%m/%d"), - ticket.TItripEndTime.strftime("%Y/%m/%d"))) + # ticket.fill('tirewardnum', tirewardnum) # 中奖号码 2.0版多余 + # ticket.fill('residual_deposit', residual_deposit) # 剩余押金 2.0版多余 + ticket.fill('triptime', '{} - {}'.format(ticket.TItripStartTime.strftime("%Y/%m/%d %H:%M:%S"), + ticket.TItripEndTime.strftime("%Y/%m/%d %H:%M:%S"))) + if not traded and common_user(): + traded = self._query_traded(ticket.TIid, getattr(request, 'user').id) + if traded: + tsoid = traded.TSOid + traded = True + ticket.fill('traded', bool(traded)) # 是否已购买 + ticket.fill('tsoid', tsoid) + ticket.fill('show_record', show_record) + + verified = True if common_user() and Guide.query.filter_by( + isdelete=False, USid=getattr(request, 'user').id, + GUstatus=GuideApplyStatus.agree.value).first() else False + ticket.fill('verified', verified) if is_admin(): linkage = Linkage.query.join(TicketLinkage, TicketLinkage.LIid == Linkage.LIid ).filter(Linkage.isdelete == false(), TicketLinkage.isdelete == false(), TicketLinkage.TIid == ticket.TIid).all() ticket.fill('linkage', linkage) + sup = Supplizer.query.filter(Supplizer.SUid == ticket.SUid).first() + if sup: + sup.fields = ['SUname', 'SUid'] + ticket.fill('supplizer', sup) + ticket.fill('position', {'tiaddress': ticket.TIaddress, + 'longitude': ticket.longitude, + 'latitude': ticket.latitude}) + + def _query_single_score(self, ticketorder, ticket): + tinum = ticket.TInum + if ticketorder.TSOtype == TicketPayType.cash.value or ticketorder.TSOstatus > TicketsOrderStatus.pending.value: + return [], 1 + tsoid_array = [i[0] for i in db.session.query(TicketsOrder.TSOid).filter( + TicketsOrder.isdelete == false(), + TicketsOrder.TIid == ticketorder.TIid, + TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value, + ).order_by(TicketsOrder.TSOactivation.desc(), + TicketsOrder.createtime.asc(), + origin=True).all() if i is not None] + res = [self._init_score_dict(ticketorder.TSOid, '我的位置')] + rank = 1 + if tsoid_array and len(tsoid_array) > 1: + my_index = tsoid_array.index(ticketorder.TSOid) + rank = my_index + 1 + if my_index == 0: + res.append(self._init_score_dict(tsoid_array[my_index + 1], '后一名')) + elif my_index == len(tsoid_array) - 1: + temp_index = tinum - 1 if rank > tinum else my_index - 1 + res.insert(0, self._init_score_dict(tsoid_array[temp_index], '前一名')) + else: + temp_index = tinum - 1 if rank > tinum else my_index - 1 + res.insert(0, self._init_score_dict(tsoid_array[temp_index], '前一名')) + res.append(self._init_score_dict(tsoid_array[my_index + 1], '后一名')) + return res, rank + + @staticmethod + def _init_score_dict(tsoid, rank_zh): + score_info = db.session.query(TicketsOrder.TSOactivation, User.USheader).outerjoin( + User, User.USid == TicketsOrder.USid).filter(User.isdelete == false(), TicketsOrder.isdelete == false(), + TicketsOrder.TSOid == tsoid).first() + res = None + if score_info: + res = {'tsoactivation': score_info[0], + 'usheader': score_info[1] if score_info[1].startswith('http') else API_HOST + score_info[1], + 'rank_zh': rank_zh} + return res def list_ticket(self): """门票列表""" args = request.args.to_dict() option = args.get('option') if option == 'my': # 我的门票 - return self._list_ticketorders() + return self._list_ticketorders(args.get('tsostatus')) filter_args = [] if not is_admin(): - filter_args.append(Ticket.TIstatus < TicketStatus.interrupt.value) + filter_args.append(Ticket.TIstatus != TicketStatus.interrupt.value) + if is_supplizer(): + filter_args.append(Ticket.SUid == getattr(request, 'user').id) tickets = Ticket.query.filter(Ticket.isdelete == false(), *filter_args - ).order_by(Ticket.TIstatus.asc(), Ticket.createtime.asc()).all_with_page() + ).order_by(func.field(Ticket.TIstatus, TicketStatus.active.value, + TicketStatus.ready.value, TicketStatus.over.value), + Ticket.TIstartTime.asc(), + Ticket.createtime.desc()).all_with_page() + ticket_fields = self.TICKET_LIST_FIELDS[:] + ticket_fields.extend(('TItripStartTime', 'TItripEndTime', 'traded', 'TIprice')) for ticket in tickets: self._fill_ticket(ticket) - ticket.fields = ['TIid', 'TIname', 'TIimg', 'TIstartTime', 'TIendTime', 'TIstatus', - 'interrupt', 'tistatus_zh', 'ticategory', 'TItripStartTime', 'TItripEndTime'] - ticket.fill('short_str', '{}.{}抢票开启 | {}'.format(ticket.TIstartTime.month, - ticket.TIstartTime.day, ticket.TIabbreviation)) + ticket.fields = ticket_fields + # ticket.fill('short_str', '{}.{}抢票开启 | {}'.format(ticket.TIstartTime.month, + # ticket.TIstartTime.day, ticket.TIabbreviation)) return Success(data=tickets) - def _list_ticketorders(self): + def _list_ticketorders(self, tsostatus): import copy if not common_user(): raise TokenError + try: + tsostatus = int(tsostatus) + TicketsOrderStatus(tsostatus) + status_filter = (TicketsOrder.TSOstatus == tsostatus, ) + except (ValueError, AssertionError, TypeError): + status_filter = [] tos = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), - TicketsOrder.USid == getattr(request, 'user').id + TicketsOrder.USid == getattr(request, 'user').id, + *status_filter ).order_by(TicketsOrder.createtime.desc()).all_with_page() res = [] + ticket_fields = self.TICKET_LIST_FIELDS[:] + ticket_fields.extend(('tsostatus', 'tsostatus_zh', 'tsoqrcode')) for to in tos: ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == to.TIid).first() ticket = copy.deepcopy(ticket) @@ -323,10 +449,9 @@ def _list_ticketorders(self): current_app.logger.error('未找到ticket, tiid: {}'.format(to.TIid)) continue self._fill_ticket(ticket, to) - ticket.fields = ['TIid', 'TIname', 'TIimg', 'TIstartTime', 'TIendTime', 'TIstatus', 'tsoid', 'tsocode', - 'tsostatus', 'tsostatus_zh', 'interrupt', 'tistatus_zh', 'ticategory', 'tsoqrcode'] - ticket.fill('short_str', '{}.{}抢票开启 | {}'.format(ticket.TIstartTime.month, - ticket.TIstartTime.day, ticket.TIabbreviation)) + ticket.fields = ticket_fields + # ticket.fill('short_str', '{}.{}抢票开启 | {}'.format(ticket.TIstartTime.month, + # ticket.TIstartTime.day, ticket.TIabbreviation)) res.append(ticket) del ticket return Success(data=res) @@ -336,9 +461,21 @@ def list_linkage(self): linkages = Linkage.query.filter(Linkage.isdelete == false()).all() return Success(data=linkages) - @admin_required + def list_tsostatus(self): + """所有试用记录状态类型""" + res = [{'tsostatus': k, + 'tsostatus_en': TicketsOrderStatus(k).name, + 'tsostatus_zh': TicketsOrderStatus(k).zh_value + } for k in (TicketsOrderStatus.pending.value, TicketsOrderStatus.has_won.value, + TicketsOrderStatus.completed.value, TicketsOrderStatus.accomplish.value, + TicketsOrderStatus.not_won.value,)] + return Success(data=res) + + @token_required def list_trade(self): """门票购买记录""" + if common_user(): + raise StatusError('用户无权限') args = parameter_required('tiid') tiid = args.get('tiid') ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == tiid).first_('无信息') @@ -375,150 +512,216 @@ def list_trade(self): 'ticketorder': res} ) + def ticket_award_task(self, ticket): + if not ticket: + return + ticketorders = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), TicketsOrder.TIid == ticket.TIid, + TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value, + TicketsOrder.TSOtype != TicketPayType.cash.value + ).order_by(TicketsOrder.TSOactivation.desc(), + TicketsOrder.createtime.desc()).limit(ticket.TInum).all() + current_app.logger.info('总票数: {}, 开奖数: {}'.format(ticket.TInum, len(ticketorders))) + # todo 有活跃分的数量不够??? + tsoids = [] + for to in ticketorders: + tsoids.append(to.TSOid) + to.TSOstatus = TicketsOrderStatus.has_won.value + to.TSOqrcode = self._ticket_order_qrcode(to.TSOid, to.USid) + not_won_ticketorders = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), + TicketsOrder.TSOid.notin_(tsoids), + TicketsOrder.TIid == ticket.TIid, + TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value, + TicketsOrder.TSOtype != TicketPayType.cash.value).all() + self._deposit_refund(not_won_ticketorders, ticket) # 退钱 + + def _deposit_refund(self, tsos, ticket): + row_count = 0 + for to in tsos: + usid = to.USid + to.TSOstatus = TicketsOrderStatus.not_won.value # 改状态 + # 退钱 + pp = PlayPay.query.filter(PlayPay.isdelete == false(), PlayPay.PPcontent == to.TSOid + ).order_by(PlayPay.createtime.desc()).first() + if not pp: + current_app.logger.info('not found playpay, tsoid: {}'.format(to.TSOid)) + continue + if to.TSOtype == TicketPayType.scorepay.value: # 信用分支付的只改状态,不返钱 + current_app.logger.info('found score paied, tsoid: {}'.format(to.TSOid)) + row_count += 1 + continue + return_price = ticket.TIdeposit + mount_price = pp.PPpayMount + opayno = pp.PPpayno + current_app.logger.info('found refund opayno: {}, mount:{}'.format(opayno, return_price)) + current_app.logger.info('refund mount: {}; total deposit:{}'.format(return_price, mount_price)) + current_app.logger.info('refund usid: {}'.format(usid)) + + if API_HOST != 'https://www.bigxingxing.com': + mount_price = 0.01 + return_price = 0.01 + trr = TicketRefundRecord.create({'TRRid': str(uuid.uuid1()), + 'USid': usid, + 'TRRredund': return_price, + 'TRRtotal': mount_price, + 'OPayno': opayno, + 'TSOid': to.TSOid}) + db.session.add(trr) + try: + flag, count = True, 1 + while flag and count <= 5: + try: + super(CTicket, self)._refund_to_user( + out_trade_no=opayno, + out_request_no=trr.TRRid, + mount=return_price, + old_total_fee=mount_price + ) + flag = False + except requests.exceptions.ConnectionError as e: + flag = True + count += 1 + current_app.logger.error('refund deposit error: {}'.format(e)) + finally: + current_app.logger.info('post wx_refund api count: {}'.format(count)) + except Exception as e: + raise StatusError('微信商户平台:{}'.format(e)) + row_count += 1 + current_app.logger.info('change status to not won, count: {}'.format(row_count)) + return row_count + @admin_required def set_award(self): """设置中奖""" - data = parameter_required('tsoid') - tsoid = data.get('tsoid') - ticket_order = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), - TicketsOrder.TSOid == tsoid, - TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value - ).first_('状态错误') - ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == ticket_order.TIid).first() - if not ticket or ticket.TIstatus != TicketStatus.over.value: - raise ParamsError('抢票尚未结束') - award_num = self._query_award_num(ticket.TIid) - current_app.logger.info('已中奖数:{} / {}'.format(award_num, ticket.TInum)) - if award_num >= ticket.TInum: - raise StatusError('已达最大发放票数') - with db.auto_commit(): - update_dict = {'TSOqrcode': 'https://play.bigxingxing.com/img/qrcode/2019/9/3/QRCODE.png', - 'TSOstatus': TicketsOrderStatus.has_won.value} - if ticket.TIdeposit == ticket.TIprice: # 第二次支付押金0元的情况 - update_dict['TSOstatus'] = TicketsOrderStatus.completed.value - ticket_order.update(update_dict) - db.session.add(ticket_order) - db.session.flush() - awarded_num = self._query_award_num(ticket.TIid) - current_app.logger.info('设置后中奖数:{} / {}'.format(awarded_num, ticket.TInum)) - if awarded_num == ticket.TInum: # 未中奖退钱 - other_to = self._query_not_won(ticket.TIid) - total_row_count = 0 - for oto in other_to: - row_count = self._deposit_refund(oto) - total_row_count += row_count - current_app.logger.info('共{}条未中奖'.format(total_row_count)) - return Success('设置成功', data=tsoid) + raise StatusError('该功能暂停使用') + # data = parameter_required('tsoid') + # tsoid = data.get('tsoid') + # ticket_order = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), + # TicketsOrder.TSOid == tsoid, + # TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value + # ).first_('状态错误') + # ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == ticket_order.TIid).first() + # if not ticket or ticket.TIstatus != TicketStatus.over.value: + # raise ParamsError('抢票尚未结束') + # award_num = self._query_award_num(ticket.TIid) + # current_app.logger.info('已中奖数:{} / {}'.format(award_num, ticket.TInum)) + # if award_num >= ticket.TInum: + # raise StatusError('已达最大发放票数') + # with db.auto_commit(): + # update_dict = {'TSOqrcode': 'https://play.bigxingxing.com/img/qrcode/2019/9/3/QRCODE.png', + # 'TSOstatus': TicketsOrderStatus.has_won.value} + # if ticket.TIdeposit == ticket.TIprice: # 第二次支付押金0元的情况 + # update_dict['TSOstatus'] = TicketsOrderStatus.completed.value + # ticket_order.update(update_dict) + # db.session.add(ticket_order) + # db.session.flush() + # awarded_num = self._query_award_num(ticket.TIid) + # current_app.logger.info('设置后中奖数:{} / {}'.format(awarded_num, ticket.TInum)) + # if awarded_num == ticket.TInum: # 未中奖退钱 + # other_to = self._query_not_won(ticket.TIid) + # total_row_count = 0 + # for oto in other_to: + # row_count = self._deposit_refund(oto) + # total_row_count += row_count + # current_app.logger.info('共{}条未中奖'.format(total_row_count)) + # return Success('设置成功', data=tsoid) @phone_required def pay(self): """购买""" data = parameter_required() - tiid, tsoid, numbers = data.get('tiid'), data.get('tsoid'), data.get('num') + tiid, tsotype = data.get('tiid'), data.get('tsotype', 1) + try: + TicketPayType(int(tsotype)) + except (ValueError, AttributeError, TypeError): + raise ParamsError('支付方式错误') user = User.query.filter(User.isdelete == false(), User.USid == getattr(request, 'user').id).first_('请重新登录') opayno = super(CTicket, self)._opayno() - instance_list, tscode_list = [], [] + ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == tiid).first_('未找到该门票信息') + trade = self._query_traded(tiid, user.USid) # 直购不限制 + if ticket.TIstatus == TicketStatus.ready.value: + raise ParamsError('活动尚未开始') + elif ticket.TIstatus == TicketStatus.interrupt.value or (ticket.TIstatus == TicketStatus.over.value + and tsotype != TicketPayType.cash.value): + raise ParamsError('活动已结束') + redirect = False with db.auto_commit(): - if tiid and numbers: # 抢票 - ticket, mount_price, instance_list, tscode_list = self._grap_ticket_order(tiid, numbers, user, opayno, - instance_list, tscode_list) - elif tsoid: # 中奖后补押金 - ticket, mount_price, instance_list, tscode_list = self._patch_ticket_order(tsoid, user, opayno, - instance_list, tscode_list) + if tsotype == TicketPayType.deposit.value: + mount_price = ticket.TIdeposit + elif tsotype == TicketPayType.cash.value: + mount_price = ticket.TIprice + trade = False + elif tsotype == TicketPayType.scorepay.value: + if not user.USrealname: + raise StatusError('用户未进行信用认证') + temp_flag = True + if temp_flag: + raise StatusError('您当前的支付信用分不足,请换种姿势申请~') + mount_price = 0 + redirect = True else: - raise ParamsError - - db.session.add_all(instance_list) + raise StatusError('支付方式错误') + if trade: + raise StatusError('您已申请成功,请在“我的 - 我的试用”中查看') + ticket_order = self._creat_ticket_order(user.USid, tiid, tsotype) + db.session.add(ticket_order) body = ticket.TIname[:16] + '...' openid = user.USopenid1 pay_args = super(CTicket, self)._add_pay_detail(opayno=opayno, body=body, PPpayMount=mount_price, openid=openid, - PPcontent=ticket.TIid, - PPpayType=PlayPayType.ticket.value) + PPcontent=ticket_order.TSOid, + PPpayType=PlayPayType.ticket.value, + redirect=redirect) response = { 'pay_type': PayType.wechat_pay.name, 'opaytype': PayType.wechat_pay.value, - 'tscode': tscode_list, - 'args': pay_args + 'args': pay_args, + 'redirect': redirect } current_app.logger.info('response = {}'.format(response)) return Success(data=response) - def _grap_ticket_order(self, tiid, numbers, user, opayno, instance_list, tscode_list): - if not (isinstance(numbers, int) and 0 < numbers < 11): - raise ParamsError('数量错误, 单次可购票数 (1-10)') - ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == tiid - ).first_('未找到该门票信息') - if ticket.TIstatus != TicketStatus.active.value: - raise ParamsError('活动尚未开始') - last_trade = TicketsOrder.query.filter(TicketsOrder.TIid == tiid, - TicketsOrder.USid == user.USid, - TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value - ).order_by(TicketsOrder.createtime.desc()).first() - if last_trade: - current_app.logger.info('last trade time: {}'.format(last_trade.createtime)) - delta_time = datetime.now() - last_trade.createtime - current_app.logger.info('delta time: {}'.format(delta_time)) - if delta_time.seconds < 6: - raise ParamsError('正在努力排队中, 请稍后尝试重新提交') - mount_price = ticket.TIdeposit * numbers - last_tscode = db.session.query(TicketsOrder.TSOcode).filter( - TicketsOrder.isdelete == false(), - TicketsOrder.TIid == tiid, - TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value - ).order_by(TicketsOrder.TSOcode.desc(), - TicketsOrder.createtime.desc(), - origin=True).first() or 0 - if last_tscode: - last_tscode = last_tscode[0] - current_app.logger.info('last tscode: {}'.format(last_tscode)) - for i in range(numbers): - last_tscode += 1 - tscode = last_tscode - tscode_list.append(tscode) - current_app.logger.info('tscode: {}'.format(tscode)) - ticket_order = self._creat_ticket_order(user.USid, tiid, tscode) - ticket_deposit = self._creat_ticket_deposit(ticket_order.TSOid, TicketDepositType.grab.value, - ticket.TIdeposit, opayno) - instance_list.append(ticket_deposit) - instance_list.append(ticket_order) - return ticket, mount_price, instance_list, tscode_list - - def _patch_ticket_order(self, tsoid, user, opayno, instance_list, tscode_list): - tso = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), TicketsOrder.TSOid == tsoid, - TicketsOrder.USid == user.USid).first_('未找到该信息') - if tso.TSOstatus != TicketsOrderStatus.has_won.value: - raise StatusError('支付条件未满足') - ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == tso.TIid - ).first_('未找到该门票信息') - mount_price = ticket.TIprice - ticket.TIdeposit - ticket_deposit = self._creat_ticket_deposit(tsoid, TicketDepositType.patch.value, mount_price, opayno) - instance_list.append(ticket_deposit) - tscode_list.append(tso.TSOcode) - return ticket, mount_price, instance_list, tscode_list - - @staticmethod - def _creat_ticket_deposit(tsoid, tdtype, mount, opayno): - return TicketDeposit.create({'TDid': str(uuid.uuid1()), - 'TSOid': tsoid, - 'TDdeposit': mount, - 'TDtype': tdtype, - 'OPayno': opayno}) - @staticmethod - def _creat_ticket_order(usid, tiid, tscode): + def _creat_ticket_order(usid, tiid, tsotype): return TicketsOrder.create({'TSOid': str(uuid.uuid1()), 'USid': usid, 'TIid': tiid, - 'TSOcode': tscode, + 'TSOtype': tsotype, 'isdelete': True}) @staticmethod - def _query_award_num(tiid): - return db.session.query(func.count(TicketsOrder.TSOid) - ).filter(TicketsOrder.isdelete == false(), + def _query_traded(tiid, usid): + return TicketsOrder.query.filter(TicketsOrder.isdelete == false(), TicketsOrder.TIid == tiid, - TicketsOrder.TSOstatus == TicketsOrderStatus.has_won.value - ).scalar() or 0 + TicketsOrder.USid == usid, + TicketsOrder.TSOtype != TicketPayType.cash.value + ).first() + + @staticmethod + def _query_rules(ruletype): + return db.session.query(Agreement.AMcontent).filter(Agreement.isdelete == false(), + Agreement.AMtype == ruletype).scalar() + + @staticmethod + def _query_award_num(ticket, filter_status=None): + if not filter_status: + filter_status = (TicketsOrder.TSOstatus == TicketsOrderStatus.has_won.value,) + count = db.session.query(func.count(TicketsOrder.TSOid) + ).filter(TicketsOrder.isdelete == false(), + TicketsOrder.TIid == ticket.TIid, + *filter_status + ).scalar() or 0 + if ticket.TIstatus == TicketStatus.over.value: + if ticket.TIapplyFakeNum and ticket.TIapplyFakeNum >= 500: + count = ticket.TIapplyFakeNum + # current_app.logger.info('found ticket fake apply_num {}'.format(count)) + elif count < 500: + current_app.logger.info('fill ended ticket apply num, tiid: {}'.format(ticket.TIid)) + with db.auto_commit(): + current_app.logger.info('ticket true apply_num {}'.format(count)) + count = random.randint(800, 2000) + current_app.logger.info('ticket apply_num now to {}'.format(count)) + ticket.update({'TIapplyFakeNum': count}) + db.session.add(ticket) + return count @staticmethod def _query_not_won(tiid): @@ -549,48 +752,116 @@ def _cancle_celery_task(conid): celery.AsyncResult(exist_task_id).revoke() conn.delete(conid) - def _deposit_refund(self, to_info): - usid, tiid, tsoids = to_info - tsoids = tsoids.split(',') - current_app.logger.info('deposit refund, TSOids:{}'.format(tsoids)) - td_info = db.session.query(TicketDeposit.OPayno, func.sum(TicketDeposit.TDdeposit) - ).filter(TicketDeposit.isdelete == false(), - TicketDeposit.TSOid.in_(tsoids)).group_by(TicketDeposit.OPayno).all() - current_app.logger.info('td_info:{}'.format(td_info)) - for td in td_info: - opayno, return_price = td - current_app.logger.info('found refund opayno: {}, mount:{}'.format(opayno, return_price)) - mount_price = db.session.query(func.sum(TicketDeposit.TDdeposit)).filter( - TicketDeposit.isdelete == false(), - TicketDeposit.OPayno == opayno).scalar() - current_app.logger.info('refund mount: {}; total deposit:{}'.format(return_price, mount_price)) - current_app.logger.info('refund usid: {}'.format(usid)) + # def _deposit_refund(self, to_info): + # usid, tiid, tsoids = to_info + # tsoids = tsoids.split(',') + # current_app.logger.info('deposit refund, TSOids:{}'.format(tsoids)) + # td_info = db.session.query(TicketDeposit.OPayno, func.sum(TicketDeposit.TDdeposit) + # ).filter(TicketDeposit.isdelete == false(), + # TicketDeposit.TSOid.in_(tsoids)).group_by(TicketDeposit.OPayno).all() + # current_app.logger.info('td_info:{}'.format(td_info)) + # for td in td_info: + # opayno, return_price = td + # current_app.logger.info('found refund opayno: {}, mount:{}'.format(opayno, return_price)) + # mount_price = db.session.query(func.sum(TicketDeposit.TDdeposit)).filter( + # TicketDeposit.isdelete == false(), + # TicketDeposit.OPayno == opayno).scalar() + # current_app.logger.info('refund mount: {}; total deposit:{}'.format(return_price, mount_price)) + # current_app.logger.info('refund usid: {}'.format(usid)) + # + # if API_HOST != 'https://www.bigxingxing.com': + # mount_price = 0.01 + # return_price = 0.01 + # trr = TicketRefundRecord.create({'TRRid': str(uuid.uuid1()), + # 'USid': usid, + # 'TRRredund': return_price, + # 'TRRtotal': mount_price, + # 'OPayno': opayno}) + # db.session.add(trr) + # try: + # super(CTicket, self)._refund_to_user( + # out_trade_no=opayno, + # out_request_no=trr.TRRid, + # mount=return_price, + # old_total_fee=mount_price + # ) + # except Exception as e: + # raise StatusError('微信商户平台:{}'.format(e)) + # + # row_count = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), + # TicketsOrder.TSOid.in_(tsoids) + # ).update({'TSOstatus': TicketsOrderStatus.not_won.value}, + # synchronize_session=False) + # current_app.logger.info('change status to not won, count: {}'.format(row_count)) + # return row_count - if API_HOST != 'https://www.bigxingxing.com': - mount_price = 0.01 - return_price = 0.01 - trr = TicketRefundRecord.create({'TRRid': str(uuid.uuid1()), - 'USid': usid, - 'TRRredund': return_price, - 'TRRtotal': mount_price, - 'OPayno': opayno}) - db.session.add(trr) + @phone_required + def ticketorder_verified(self): + """门票核销""" + data = parameter_required('param') + param = data.get('param') + try: + tsoid, secret_usid = str(param).split('&') + except ValueError: + raise ParamsError('试用码无效') + current_app.logger.info('tsoid: {}, secret_usid: {}'.format(tsoid, secret_usid)) + tsoid = str(tsoid).split('=')[-1] + secret_usid = str(secret_usid).split('=')[-1] + current_app.logger.info('splited, tsoid: {}, secret_usid: {}'.format(tsoid, secret_usid)) + if not tsoid or not secret_usid: + raise StatusError('该试用码无效') + ticket_usid = self.cuser._base_decode(secret_usid) + ticket_user = User.query.filter(User.isdelete == false(), + User.USid == ticket_usid).first_('无效试用码') + tso = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), + TicketsOrder.TSOid == tsoid, + ).first() + if tso.TSOstatus != TicketsOrderStatus.has_won.value: + current_app.logger.error('tso status: {}'.format(tso.TSOstatus)) + raise StatusError('该票已使用') + ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == tso.TIid).first() + if not (ticket.TItripStartTime <= datetime.now() <= ticket.TItripEndTime): + raise StatusError('当前时间不在该票有效使用时间内') + + user = User.query.join(TicketVerifier, TicketVerifier.TVphone == User.UStelphone + ).join(Ticket, Ticket.SUid == TicketVerifier.SUid + ).filter(User.isdelete == false(), User.USid == getattr(request, 'user').id, + TicketVerifier.SUid == ticket.SUid + ).first_('请确认您是否拥有该门票的核销权限') + + with db.auto_commit(): + if tso.TSOtype == TicketPayType.cash.value: # 直购方式状态改为已完成 + tso.update({'TSOstatus': TicketsOrderStatus.accomplish.value}) + else: # 其余方式改为已使用 + tso.update({'TSOstatus': TicketsOrderStatus.completed.value}) + db.session.add(tso) + # 核销记录 + tvr = TicketVerifiedRecord.create({'TVRid': str(uuid.uuid1()), + 'TIownerId': ticket_user.USid, + 'VerifierId': user.USid, + 'TSOid': tso.TSOid, + 'TSOparam': param}) + db.session.add(tvr) + return Success('门票验证成功', data=tvr.TVRid) + + def _ticket_order_qrcode(self, tsoid, usid): + """创建票二维码""" + savepath, savedbpath = self.cuser._get_path('qrcode') + secret_usid = self.cuser._base_encode(usid) + filename = os.path.join(savepath, '{}.png'.format(tsoid)) + filedbname = os.path.join(savedbpath, '{}.png'.format(tsoid)) + current_app.logger.info('get basedir {0}'.format(current_app.config['BASEDIR'])) + text = 'tsoid={}&secret={}'.format(tsoid, secret_usid) + current_app.logger.info('get text content {0}'.format(text)) + qrcodeWithtext(text, filename) + + # 二维码上传到七牛云 + if API_HOST == 'https://www.bigxingxing.com': try: - super(CTicket, self)._refund_to_user( - out_trade_no=opayno, - out_request_no=trr.TRRid, - mount=return_price, - old_total_fee=mount_price - ) + self.cuser.qiniu.save(data=filename, filename=filedbname[1:]) except Exception as e: - raise StatusError('微信商户平台:{}'.format(e)) - - row_count = TicketsOrder.query.filter(TicketsOrder.isdelete == false(), - TicketsOrder.TSOid.in_(tsoids) - ).update({'TSOstatus': TicketsOrderStatus.not_won.value}, - synchronize_session=False) - current_app.logger.info('change status to not won, count: {}'.format(row_count)) - return row_count + current_app.logger.error('二维码转存七牛云失败 : {}'.format(e)) + return filedbname @phone_required def get_promotion(self): @@ -608,16 +879,24 @@ def get_promotion(self): starttime = super(CTicket, self)._check_time(ticket.TItripStartTime) endtime = super(CTicket, self)._check_time(ticket.TItripEndTime, fmt='%m/%d') + starttime_g = super(CTicket, self)._check_time(ticket.TIstartTime) + endtime_g = super(CTicket, self)._check_time(ticket.TIendTime, fmt='%m/%d') + # 获取微信二维码 - from planet.control.CUser import CUser - cuser = CUser() + # from planet.control.CUser import CUser + cuser = self.cuser + if not params or 'page=' not in params: + params = 'page=/pages/index/freeDetail' + if 'tiid' not in params: + params = '{}&tiid={}'.format(params, tiid) if 'secret_usid' not in params: params = '{}&secret_usid={}'.format(params, cuser._base_encode(usid)) + params = '{}&sttype={}'.format(params, ShareType.promotion.value) params_key = cuser.shorten_parameters(params, usid, 'params') wxacode_path = cuser.wxacode_unlimit( - usid, {'params': params_key}, img_name='{}{}'.format(usid, tiid), ) + usid, {'params': params_key}, img_name='{}{}'.format(usid, tiid), shuffix='png', is_hyaline=True) local_path, promotion_path = PlayPicture().create_ticket( - ticket.TIimg, ticket.TIname, starttime, endtime, str(0), usid, tiid, wxacode_path) + ticket.TIimg, ticket.TIname, starttime, endtime, starttime_g, endtime_g, str(0), usid, tiid, wxacode_path) from planet.extensions.qiniu.storage import QiniuStorage qiniu = QiniuStorage(current_app) if API_HOST == 'https://www.bigxingxing.com': diff --git a/planet/control/CUser.py b/planet/control/CUser.py index 2193635c..0e79c105 100644 --- a/planet/control/CUser.py +++ b/planet/control/CUser.py @@ -14,7 +14,7 @@ from planet.config.enums import UserIntegralType, AdminLevel, AdminStatus, UserIntegralAction, AdminAction, \ UserLoginTimetype, UserStatus, WXLoginFrom, OrderMainStatus, BankName, UserCommissionStatus, ApplyStatus, ApplyFrom, \ ApprovalAction, SupplizerSettementStatus, UserAddressFrom, CollectionType, UserGrade, WexinBankCode, \ - UserCommissionType, AdminActionS, MiniUserGrade, GuideApplyStatus + UserCommissionType, AdminActionS, MiniUserGrade, GuideApplyStatus, ActivationTypeEnum, ShareType from planet.config.secret import SERVICE_APPID, SERVICE_APPSECRET, \ SUBSCRIBE_APPID, SUBSCRIBE_APPSECRET, appid, appsecret, BASEDIR, MiniProgramAppId, MiniProgramAppSecret, BlogAppId, \ BlogAppSecret @@ -31,16 +31,18 @@ from planet.common.request_handler import gennerc_log from planet.common.id_check import DOIDCheck from planet.common.make_qrcode import qrcodeWithlogo +from planet.control.BaseControl import BaseController from planet.extensions.tasks import auto_agree_task from planet.extensions.weixin.login import WeixinLogin, WeixinLoginError from planet.extensions.register_ext import mp_server, mp_subscribe, db, wx_pay, mp_miniprogram from planet.extensions.validates.user import SupplizerLoginForm, UpdateUserCommisionForm, ListUserCommision +from planet.extensions.weixin.mp import WeixinMPError from planet.models import User, UserLoginTime, UserCommission, UserInvitation, \ UserAddress, IDCheck, IdentifyingCode, UserMedia, UserIntegral, Admin, AdminNotes, CouponUser, UserWallet, \ CashNotes, UserSalesVolume, Coupon, SignInAward, SupplizerAccount, SupplizerSettlement, SettlenmentApply, Commision, \ Approval, UserTransmit, UserCollectionLog, News, CashFlow, UserLoginApi, UserHomeCount, Guide, AddressArea, \ - AddressProvince, AddressCity, CoveredCertifiedNameLog, SharingParameters -from .BaseControl import BASEAPPROVAL, BASEADMIN + AddressProvince, AddressCity, CoveredCertifiedNameLog, SharingParameters, SharingType, TicketVerifier +from .BaseControl import BASEAPPROVAL, BASEADMIN, BASETICKET from planet.service.SUser import SUser from planet.models.product import Products, Items, ProductItems, Supplizer from planet.models.trade import OrderPart, OrderMain @@ -58,6 +60,8 @@ class CUser(SUser, BASEAPPROVAL): def __init__(self): super(CUser, self).__init__() self.qiniu = QiniuStorage(current_app) + self.Baseticket = BASETICKET() + self.basecontroller = BaseController() @staticmethod def __conver_idcode(idcode): @@ -292,22 +296,25 @@ def _create_qrcode(self, head, usid, url): current_app.logger.error('二维码转存七牛云失败 : {}'.format(e)) return filedbname - def wxacode_unlimit(self, usid, scene=None, img_name=None, **kwargs): + def wxacode_unlimit(self, usid, scene=None, img_name=None, shuffix='jpg', **kwargs): """ 生成带参数的小程序码 :param usid: 用户id :param scene: 需要携带的参数,dict型参数 :param img_name: 图片名,同一日再次生成同名图片会被替换 + :param shuffix: 图片格式,默认jpg """ savepath, savedbpath = self._get_path('qrcode') secret_usid = self._base_encode(usid) if not img_name: # 默认图片名称,再次生成会替换同名图片 img_name = secret_usid - filename = os.path.join(savepath, '{}.jpg'.format(img_name)) - filedbname = os.path.join(savedbpath, '{}.jpg'.format(img_name)) + filename = os.path.join(savepath, '{}.{}'.format(img_name, shuffix)) + filedbname = os.path.join(savedbpath, '{}.{}'.format(img_name, shuffix)) current_app.logger.info('filename: {} ; filedbname: {}'.format(filename, filedbname)) if not scene: - scene = {'params': self.shorten_parameters('secret_usid={}'.format(secret_usid), usid, 'params')} + scene = {'params': self.shorten_parameters('secret_usid={}&sttype={}'.format( + secret_usid, ShareType.usercode.value), usid, 'params')} + scene_str = self.dict_to_query_str(scene) current_app.logger.info('get scene str: {}'.format(scene_str)) try: @@ -593,7 +600,7 @@ def update_usinfo(self): """更新个人资料""" user = self.get_user_by_id(getattr(request, 'user').id) data = parameter_required() - usrealname, ustelphone = data.get('usrealname'), data.get('ustelphone') + usrealname, ustelphone, usheader = data.get('usrealname'), data.get('ustelphone'), data.get('usheader') usidentification = data.get('usidentification') usareaid, usbirthday = data.get('aaid') or data.get('usareaid'), data.get('usbirthday') usbirthday = validate_arg(r'^\d{4}-\d{2}-\d{2}$', usbirthday, '请按正确的生日格式填写') @@ -605,6 +612,18 @@ def update_usinfo(self): checked_name = self._verify_chinese(usrealname) if not checked_name or len(checked_name[0]) < 2: raise ParamsError('请正确填写真实姓名') + try: # 检查昵称填写 + check_content = data.get('usname') + check_res = mp_miniprogram.msg_sec_check(check_content) + current_app.logger.info('content_sec_check: {}'.format(check_res)) + except WeixinMPError as e: + current_app.logger.info('check result: {}'.format(e)) + raise ParamsError('您输入的昵称含有部分敏感词汇,请检查后重新填写') + # 图片校验 + filepath = os.path.join(current_app.config['BASEDIR'], + str(str(usheader).split('.bigxingxing.com')[-1][1:]).split('_')[0]) + self.basecontroller.img_check(filepath) + oldname = user.USrealname oldidentitynumber = user.USidentification with db.auto_commit(): @@ -637,10 +656,10 @@ def update_usinfo(self): @token_required def get_home(self): """获取个人主页信息""" - user = self.get_user_by_id(request.user.id) + user = User.query.filter(User.USid == getattr(request, 'user').id, User.isdelete == false()).first() gennerc_log('get user is {0}'.format(user)) if not user: - raise ParamsError('token error') + raise TokenError('请重新登录') # uscoupon = CouponUser.query.filter_(CouponUser.USid == request.user.id).count() # 过滤下可以使用的数量 time_now = datetime.datetime.now() @@ -685,6 +704,10 @@ def get_home(self): if not user.USwxacode: with db.auto_commit(): user.USwxacode = self.wxacode_unlimit(user.USid) + user.fill('ticketverifier', (False if not user.UStelphone else + True if TicketVerifier.query.filter(TicketVerifier.isdelete == false(), + TicketVerifier.TVphone == user.UStelphone + ).first() else False)) # 增加订单数 # order_count = OrderMain.query.filter_by(USid=user.USid, isdelete=False).count() user.fill('ordercount', OrderMain.query.filter_by(USid=user.USid, isdelete=False).count()) @@ -1984,11 +2007,13 @@ def mini_program_login(self): current_app.logger.info('get unionid is {}'.format(unionid)) current_app.logger.info('get openid is {}'.format(openid)) - user = User.query.filter_by_(USopenid1=openid).first() + user = User.query.filter(User.isdelete == false(), User.USopenid1 == openid + ).order_by(User.createtime.desc()).first() if user: current_app.logger.info('get exist user by openid1: {}'.format(user.__dict__)) elif unionid: - user = User.query.filter_by_(USunionid=unionid).first() + user = User.query.filter(User.isdelete == false(), User.USunionid == unionid + ).order_by(User.createtime.desc()).first() if user: current_app.logger.info('get exist user by unionid: {}'.format(user.__dict__)) @@ -2009,6 +2034,8 @@ def mini_program_login(self): else: upperd = None + isnewguy = ActivationTypeEnum.share_old.value + if user: usid = user.USid user.USheader = head @@ -2020,6 +2047,7 @@ def mini_program_login(self): user.USwxacode = self.wxacode_unlimit(usid) else: current_app.logger.info('This is a new guy : {}'.format(userinfo.get('nickName'))) + isnewguy = ActivationTypeEnum.share_new.value usid = str(uuid.uuid1()) user_dict = { 'USid': usid, @@ -2041,10 +2069,27 @@ def mini_program_login(self): # user_dict.setdefault('USsupper3', upperd.USsupper2) user = User.create(user_dict) db.session.add(user) + db.session.flush() if upperd: - uin = UserInvitation.create( - {'UINid': str(uuid.uuid1()), 'USInviter': upperd.USid, 'USInvited': usid, 'UINapi': request.path}) - db.session.add(uin) + today = datetime.datetime.now().date() + uin_exist = UserInvitation.query.filter( + cast(UserInvitation.createtime, Date) == today, + UserInvitation.USInviter == upperd.USid, + UserInvitation.USInvited == usid, + ).first() + if uin_exist: + current_app.logger.info('{}今天已经邀请过这个人了{}'.format(upperd.USid, usid)) + else: + uin = UserInvitation.create( + {'UINid': str(uuid.uuid1()), 'USInviter': upperd.USid, 'USInvited': usid, 'UINapi': request.path}) + db.session.add(uin) + # 分享类型累加 + db.session.add(SharingType.create({ + 'STid': str(uuid.uuid1()), + 'USid': upperd.USid, + 'STtype': args.get('sttype', 0) + })) + self.Baseticket.add_activation(isnewguy, upperd.USid, usid) userloggintime = UserLoginTime.create({"ULTid": str(uuid.uuid1()), "USid": usid, @@ -2256,7 +2301,7 @@ def supplizer_login(self): elif supplizer.SUstatus == UserStatus.forbidden.value: raise StatusError('该账号已被冻结, 详情请联系管理员') jwt = usid_to_token(supplizer.SUid, 'Supplizer', username=supplizer.SUname) # 供应商jwt - supplizer.fields = ['SUlinkPhone', 'SUheader', 'SUname'] + supplizer.fields = ['SUlinkPhone', 'SUheader', 'SUname', 'SUgrade'] return Success('登录成功', data={ 'token': jwt, 'supplizer': supplizer @@ -2584,15 +2629,14 @@ def list_user_commison(self): UserLoginApi.USid == usid ).order_by( UserLoginApi.createtime.desc() + ).first() or UserLoginTime.query.filter( + UserLoginTime.isdelete == False, + UserLoginTime.USid == usid + ).order_by( + UserLoginTime.createtime.desc() ).first() - if not userlogintime: - userlogintime = UserLoginTime.query.filter( - UserLoginTime.isdelete == False, - UserLoginTime.USid == usid - ).order_by( - UserLoginTime.createtime.desc() - ).first() - user.fill('userlogintime', userlogintime.createtime) + user.fill('userlogintime', + getattr(userlogintime, 'createtime', user.updatetime) or user.updatetime) if is_admin(): userquery = UserHomeCount.query.filter(UserHomeCount.UHid == usid, UserHomeCount.isdelete == False).count() @@ -3074,4 +3118,58 @@ def _get_year_month(self, time_, **kwargs): return return_sort[0] return tuple(return_sort) - + def add_mock_user(self): + """添加虚拟用户""" + from planet.common.token_handler import is_tourist + if is_tourist(): + raise TokenError('随便填个token,防止非法调用') + from planet.control.CFile import CFile + cfile = CFile() + cfile.check_file_size() + files = request.files.to_dict() + if not files: + raise ParamsError('传参数') + logs = current_app.logger.info + logs(">>> Mock Users Num: {} <<<".format(len(files))) + if len(files) > 30: + raise ParamsError('最多可同时上传30张图片') + folder = 'avatar' + user_list = [] + last_user_id = db.session.query(User.USid).filter(User.isdelete == false(), + User.USid.ilike('id000%') + ).order_by(User.USid.desc(), + User.createtime.desc(), + origin=True).first() + logs("query_last_user_id: {}".format(last_user_id)) + if last_user_id: + last_user_id = last_user_id[0] + else: + last_user_id = 'id00000000' + logs("last_user_id: {}".format(last_user_id)) + new_id = int(str(last_user_id).split('id')[-1]) + 1 + with db.auto_commit(): + for file_key in files.keys(): + img_path, video_thum, video_dur, upload_type = cfile._upload_file(files[file_key], folder) + if upload_type != 'image': + logs('上传的不是图像, {}'.format(file_key)) + continue + usid = 'id' + '0' * (10 - len(str(new_id)) - 2) + str(new_id) + logs("new_usid: {}".format(usid)) + if User.query.filter(User.isdelete == false(), User.USid == usid).first(): + raise ParamsError('usid: {} 已存在'.format(usid)) + elif User.query.filter(User.isdelete == false(), User.USheader == img_path).first(): + raise ParamsError('头像已使用过, {}'.format(img_path)) + elif User.query.filter(User.isdelete == false(), User.USname == file_key).first(): + raise ParamsError('昵称已存在: {}'.format(file_key)) + else: + pass + user = User.create({'USid': usid, + 'USname': file_key, + 'USheader': img_path, + 'USfrom': 2, + }) + user_list.append(user) + logs("Mock User: {}".format(file_key)) + new_id += 1 + db.session.add_all(user_list) + return Success('成功') diff --git a/planet/extensions/staticres/free.png b/planet/extensions/staticres/free.png new file mode 100644 index 00000000..e244c311 Binary files /dev/null and b/planet/extensions/staticres/free.png differ diff --git a/planet/extensions/staticres/pingfangMedium_cu.ttf b/planet/extensions/staticres/pingfangMedium_cu.ttf new file mode 100644 index 00000000..5cbe1649 Binary files /dev/null and b/planet/extensions/staticres/pingfangMedium_cu.ttf differ diff --git a/planet/extensions/tasks.py b/planet/extensions/tasks.py index aa374765..ca622902 100644 --- a/planet/extensions/tasks.py +++ b/planet/extensions/tasks.py @@ -25,7 +25,8 @@ FreshManFirstProduct, FreshManFirstApply, FreshManFirstSku, ProductSku, GuessNumAwardApply, GuessNumAwardProduct, \ GuessNumAwardSku, MagicBoxApply, OutStock, TrialCommodity, SceneItem, ProductScene, ProductUrl, Coupon, CouponUser, \ SupplizerDepositLog, TimeLimitedActivity, TimeLimitedProduct, TimeLimitedSku, Carts, IndexBanner, GuessGroup, \ - GuessRecord, GroupGoodsSku, GroupGoodsProduct, MagicBoxJoin, MagicBoxApplySku, ActivityDeposit, Play, Ticket + GuessRecord, GroupGoodsSku, GroupGoodsProduct, MagicBoxJoin, MagicBoxApplySku, ActivityDeposit, Play, Ticket, \ + UserLocation celery = Celery() @@ -1201,6 +1202,7 @@ def start_ticket(tiid): @celery.task() def end_ticket(tiid): current_app.logger.info('修改抢票为结束 tiid {}'.format(tiid)) + from planet.control.CTicket import CTicket try: with db.auto_commit(): ticket = Ticket.query.filter(Ticket.isdelete == false(), Ticket.TIid == tiid).first() @@ -1210,6 +1212,8 @@ def end_ticket(tiid): if ticket.TIstatus != TicketStatus.active.value: current_app.logger.error(">>> 该票状态异常, tistatus: {} <<<".format(ticket.TIstatus)) return + # 开奖 + 未中奖退钱 + CTicket().ticket_award_task(ticket) ticket.TIstatus = TicketStatus.over.value connid = 'end_ticket{}'.format(ticket.TIid) conn_value = conn.get(connid) @@ -1238,6 +1242,20 @@ def del_promotion(): current_app.logger.info('删除图片失败 error = {}'.format(e)) +@celery.task(name='update_location') +def update_location(): + current_app.logger.info('start update location') + from planet.control.BaseControl import BaseController + base_controller = BaseController() + try: + with db.auto_commit(): + ul_list = UserLocation.query.filter(UserLocation.ULformattedAddress == '请稍后再试').all() + for ul in ul_list: + base_controller.get_user_location(ul.ULlat, ul.ULlng, ul.USid, ul) + except Exception as e: + current_app.logger.error(' update location error {}'.format(e)) + + if __name__ == '__main__': from planet import create_app @@ -1254,4 +1272,5 @@ def del_promotion(): # return_coupon_deposite() # welfare_lottery_3d() # guess_group_draw() - del_promotion() + # del_promotion() + update_location() diff --git a/planet/extensions/validates/user.py b/planet/extensions/validates/user.py index d92b5a1e..cc0e7f40 100644 --- a/planet/extensions/validates/user.py +++ b/planet/extensions/validates/user.py @@ -15,6 +15,8 @@ class SupplizerListForm(BaseForm): kw = StringField('关键词', default='') mobile = StringField('手机号', default='') sustatus = StringField('筛选状态', default='all') + option = StringField('供应商类型') + sugrade = StringField('供应商类型') def validate_sustatus(self, raw): from planet.config.enums import UserStatus @@ -33,8 +35,8 @@ def validate_suid(self, raw): else: if not raw.data: raise ParamsError('供应商suid不可为空') - supplizer= Supplizer.query.filter(Supplizer.SUid == raw.data, - Supplizer.isdelete == False).first_('供应商不存在') + supplizer = Supplizer.query.filter(Supplizer.SUid == raw.data, + Supplizer.isdelete == False).first_('供应商不存在') self.supplizer = supplizer @@ -57,13 +59,14 @@ class SupplizerCreateForm(BaseForm): sucontract = FieldList(StringField(validators=[DataRequired('合同列表不可以为空')])) pbids = FieldList(StringField('品牌')) subusinesslicense = StringField('营业执照') - suregisteredfund = StringField('注册资金',) + suregisteredfund = StringField('注册资金', ) sumaincategory = StringField('主营类目', ) - suregisteredtime = DateField('注册时间',) - sulegalperson = StringField('法人',) + suregisteredtime = DateField('注册时间', ) + sulegalperson = StringField('法人', ) suemail = StringField('联系邮箱', ) sulegalpersonidcardfront = StringField('法人身份证正面', ) sulegalpersonidcardback = StringField('法人身份证反面', ) + sugrade = IntegerField('供应商类型') def validate_suloginphone(self, raw): is_exists = Supplizer.query.filter_by_().filter_( @@ -90,7 +93,7 @@ class SupplizerUpdateForm(BaseForm): sulinkman = StringField('联系人', validators=[DataRequired('联系人不可为空')]) sudeposit = DecimalField('押金') suaddress = StringField('地址', validators=[DataRequired('地址不可以为空')]) - sustatus = StringField('供应商状态',) + sustatus = StringField('供应商状态', ) subanksn = StringField('卡号') subankname = StringField('银行名字') suheader = StringField('头像') @@ -99,10 +102,10 @@ class SupplizerUpdateForm(BaseForm): suemail = StringField('邮箱') pbids = FieldList(StringField('品牌')) subusinesslicense = StringField('营业执照') - suregisteredfund = StringField('注册资金',) + suregisteredfund = StringField('注册资金', ) sumaincategory = StringField('主营类目', ) - suregisteredtime = StringField('注册时间',) - sulegalperson = StringField('法人',) + suregisteredtime = StringField('注册时间', ) + sulegalperson = StringField('法人', ) sulegalpersonidcardfront = StringField('法人身份证正面', ) sulegalpersonidcardback = StringField('法人身份证反面', ) @@ -193,3 +196,12 @@ class ListUserCommision(BaseForm): usid = StringField('用户id') upid = StringField('上级id') commision_level = IntegerField('代理商等级') + + +class GetVerifier(BaseForm): + suid = StringField('供应商id') + + +class SetVerifier(BaseForm): + suid = StringField('供应商id') + phone_list = FieldList(StringField()) diff --git a/planet/extensions/weixin/mp.py b/planet/extensions/weixin/mp.py index bc771d68..569fe969 100644 --- a/planet/extensions/weixin/mp.py +++ b/planet/extensions/weixin/mp.py @@ -52,6 +52,13 @@ def get_access_token(mp): api_uri = "https://api.weixin.qq.com" + contenttype_config_res = { + r'jpg': r'image/jpeg', + r'jpeg': r'image/jpeg', + r'gif': r'image/gif', + r'png': r'image/png', + } + def __init__(self, app_id, app_secret, ac_path=None, jt_path=None, ac_callback=None, jt_callback=None): """ :param :app_id 微信app id @@ -73,11 +80,16 @@ def __init__(self, app_id, app_secret, ac_path=None, jt_path=None, ac_callback=N self.ac_callback = ac_callback self.jt_callback = jt_callback - def fetch(self, method, url, params=None, data=None, headers=None, buffer=False): - req = requests.Request(method, url, params=params, - data=data, headers=headers) - prepped = req.prepare() - resp = self.session.send(prepped, timeout=20) + def fetch(self, method, url, params=None, data=None, headers=None, buffer=False, files=None): + if files: + resp = requests.post(url=url, params=params, data=data, files=files) + else: + req = requests.Request(method, url, params=params, + data=data, headers=headers) + + prepped = req.prepare() + resp = self.session.send(prepped, timeout=20) + if resp.status_code == 200 and buffer: return resp.content data = Map(resp.json()) @@ -92,17 +104,18 @@ def get(self, path, params=None, token=True, prefix="/cgi-bin"): token and params.setdefault("access_token", self.access_token) return self.fetch("GET", url, params) - def post(self, path, data, prefix="/cgi-bin", json_encode=True, token=True, buffer=False): + def post(self, path, data, prefix="/cgi-bin", json_encode=True, token=True, buffer=False, headers=None, files=None): url = "{0}{1}{2}".format(self.api_uri, prefix, path) params = {} token and params.setdefault("access_token", self.access_token) - headers = {} + if not headers: + headers = {} if json_encode: data = json.dumps(data, ensure_ascii=False).encode('utf-8') # data = json.dumps(data) headers["Content-Type"] = "application/json;charset=UTF-8" # print url, params, headers, data - return self.fetch("POST", url, params=params, data=data, headers=headers, buffer=buffer) + return self.fetch("POST", url, params=params, data=data, headers=headers, buffer=buffer, files=files) @property def access_token(self): @@ -428,12 +441,15 @@ def msg_sec_check(self, content): """ return self.post("/msg_sec_check", {'content': content}, prefix="/wxa") - def img_sec_check(self, media): + def img_sec_check(self, filename): """ 校验一张图片是否含有违法违规内容。 :param 要检测的图片文件,格式支持PNG、JPEG、JPG、GIF,图片尺寸不超过 750px x 1334px """ - return self.post("/msg_sec_check", {'media': media}, prefix="/wxa") + contenttype = self.contenttype_config_res.get(str(filename).split('.')[-1]) + media = open(filename, 'rb') + files = [(contenttype, media), ] + return self.post('/img_sec_check', data={'media': 'media'}, json_encode=False, files=files, prefix='/wxa') def get_wxacode_unlimit(self, scene, **kwargs): """ diff --git a/planet/models/play.py b/planet/models/play.py index ad663847..1a285484 100644 --- a/planet/models/play.py +++ b/planet/models/play.py @@ -96,7 +96,8 @@ class Agreement(Base): __tablename__ = 'Agreement' AMid = Column(String(64), primary_key=True) AMcontent = Column(Text, comment='协议内容') - AMtype = Column(Integer, default=0, comment='协议类型 0:转让协议 1: 退款规则') + AMtype = Column(Integer, default=0, comment='协议类型 0:转让协议 1: 退款规则 2:门票规则 3:活跃分规则') + AMname = Column(String(256), comment='规则名') class PlayDiscount(Base): diff --git a/planet/models/product.py b/planet/models/product.py index f6ff038f..8d086f9e 100644 --- a/planet/models/product.py +++ b/planet/models/product.py @@ -211,8 +211,7 @@ class Supplizer(Base): SUemail = Column(String(256), comment='供应商邮箱') SUlegalPersonIDcardFront = Column(Text, url=True, comment='法人身份证正面') SUlegalPersonIDcardBack = Column(Text, url=True, comment='法人身份证正面') - - SUgrade = Column(Integer, default=0, comment='供应商不知用处等级') + SUgrade = Column(Integer, default=0, comment='区分供应商类别 0:大行星商品供应商 1:门票供应商') class SupplizerProduct(Base): diff --git a/planet/models/ticket.py b/planet/models/ticket.py index b752c622..802898eb 100644 --- a/planet/models/ticket.py +++ b/planet/models/ticket.py @@ -42,24 +42,30 @@ class Ticket(Base): TIendTime = Column(DateTime, comment='结束时间') TItripStartTime = Column(DateTime, comment='票务有效期开始时间') TItripEndTime = Column(DateTime, comment='票务有效期结束时间') - TIrules = Column(Text, comment='规则') + TIrules = Column(Text, comment='规则') # 2.0版多余 TIcertificate = Column(Text, url=True, comment='景区资质凭证') TIdetails = Column(Text, comment='票详情') TIprice = Column(DECIMAL(precision=28, scale=2), comment='票价') TIdeposit = Column(DECIMAL(precision=28, scale=2), default=1, comment='抢票押金') TIstatus = Column(Integer, default=0, comment='抢票状态 0: 未开始, 1: 抢票中, 2: 中止 , 3: 已结束') TInum = Column(Integer, default=1, comment='数量') - TIrewardnum = Column(LONGTEXT, comment='中奖号码') - TIabbreviation = Column(String(200), comment='列表页封面的简称') - TIcategory = Column(Text, comment='列表页显示的类型') + TIrewardnum = Column(LONGTEXT, comment='中奖号码') # 2.0版多余 + TIabbreviation = Column(String(200), comment='列表页封面的简称') # 2.0版多余 + TIcategory = Column(Text, comment='列表页显示的类型') # 2.0版多余 + TIbanner = Column(Text, url_list=True, comment='票务轮播图片') + SUid = Column(String(64), comment='所属供应商') + TIapplyFakeNum = Column(Integer, comment='虚拟申请数[已结束状态时低于500,随机1000~2000]') + longitude = Column(String(255), comment='经度') + latitude = Column(String(255), comment='纬度') + TIaddress = Column(Text, comment='游玩场所位置') @orm.reconstructor def __init__(self): super(Ticket, self).__init__() - self.hide('TIcategory', 'TIrewardnum') + self.hide('TIabbreviation', 'TIcategory', 'TIrewardnum', 'TIapplyFakeNum') -class TicketDeposit(Base): +class TicketDeposit(Base): # 2.0版本去除 """票押金记录""" __tablename__ = 'TicketDeposit' TDid = Column(String(64), primary_key=True) @@ -77,6 +83,7 @@ class TicketRefundRecord(Base): TRRredund = Column(DECIMAL(precision=28, scale=2), comment='退款') TRRtotal = Column(DECIMAL(precision=28, scale=2), comment='原支付金额') OPayno = Column(String(64), comment='支付流水号') + TSOid = Column(String(64), comment='购票记录') class TicketsOrder(Base): @@ -85,11 +92,11 @@ class TicketsOrder(Base): TSOid = Column(String(64), primary_key=True) USid = Column(String(64), comment='用户') TIid = Column(String(64), comment='票id') - TSOcode = Column(Integer, comment='抢票码') + TSOcode = Column(Integer, comment='抢票码') # 2.0版多余 TSOqrcode = Column(Text, url=True, comment='票二维码') TSOstatus = Column(Integer, default=0, comment='状态:-1:未中奖 0: 待开奖 1:(已中奖)待补押金 2:已出票') - TSOtype = Column(Integer, comment='购票类型:{1:直购;2:信用购;3:押金购}') - TSOactivation = Column(Integer, default=0, comment='活跃度') + TSOtype = Column(Integer, comment='购票类型:{1:押金购;2:直购;3:信用购}') + TSOactivation = Column(Integer, default=0, index=True, comment='活跃度') class Linkage(Base): @@ -113,6 +120,7 @@ class TicketLinkage(Base): LIid = Column(String(64)) TIid = Column(String(64)) + class Activation(Base): """ 活跃度 @@ -120,7 +128,72 @@ class Activation(Base): __tablename__ = 'Activation' ATid = Column(String(64), primary_key=True) USid = Column(String(64)) - ATtype = Column(Integer, comment='活跃度类型:' - '{1:分享新用户;2:分享老用户;3:发布内容;4:加精;' - '5:打赏;6:提交联动平台账号}') - ATnum = Column(Integer, default=0, comment='活跃度') \ No newline at end of file + ATTid = Column(String(64), comment='活跃度类型:分享新用户,分享老用户,发布内容,加精,打赏,提交联动平台账号') + ATnum = Column(Integer, default=0, comment='活跃度') + + +class ActivationType(Base): + """ + 活跃度类型 + """ + __tablename__ = 'ActivationType' + ATTid = Column(String(64), primary_key=True, comment='该id需要脚本生成固定id') + ATTname = Column(String(256), comment='获取积分方式简述') + ATTnum = Column(Integer, default=0, comment='该获取方式获取的活跃度') + ATTupperLimit = Column(Integer, default=0, comment='该获取方式获取的活跃度上限') + ATTdayUpperLimit = Column(Integer, default=0, comment='该获取方式每日获取的活跃度上限') + ATTtype = Column(Integer, default=0, comment='是否信息绑定') + ATTicon = Column(Text, comment='信息绑定的icon') + ADid = Column(String(64), comment='创建管理员id') + + @orm.reconstructor + def __init__(self): + super(ActivationType, self).__init__() + self.hide('ADid') + + +class UserLinkage(Base): + """ + 用户绑定扩展信息 + """ + __tablename__ = 'UserLinkage' + ULAid = Column(String(64), primary_key=True) + ATTid = Column(String(64), comment='绑定类型') + USid = Column(String(64), comment='用户id') + ULAaccount = Column(String(256), comment='关联账号') + + @orm.reconstructor + def __init__(self): + super(UserLinkage, self).__init__() + self.hide('USid') + + +class TicketsOrderActivation(Base): + """ + 门票订单活跃度关联表 + """ + __tablename__ = 'TicketsOrderActivation' + TOAid = Column(String(64), primary_key=True) + TSOid = Column(String(64), comment='门票订单') + ATid = Column(String(64), comment='活跃度') + TOAcontent = Column(String(64), comment='如果是随笔,随笔实体id 分享: 分享人id 加精/打赏: 管理员id') + + +class TicketVerifier(Base): + """ + 门票核销员 + """ + __tablename__ = 'TicketVerifier' + TVid = Column(String(64), primary_key=True) + SUid = Column(String(64), comment='供应商') + TVphone = Column(String(13), nullable=False) + + +class TicketVerifiedRecord(Base): + """门票核销记录""" + __tablename__ = 'TicketVerifiedRecord' + TVRid = Column(String(64), primary_key=True) + TIownerId = Column(String(64), comment='门票持有者id') + VerifierId = Column(String(64), comment='验证人员id') + TSOid = Column(String(64), comment='门票id') + TSOparam = Column(Text, comment='扫描到的原参数') diff --git a/planet/models/user.py b/planet/models/user.py index f0ecef15..9ff01cf3 100644 --- a/planet/models/user.py +++ b/planet/models/user.py @@ -336,8 +336,8 @@ class UserLocation(Base): ULcity = Column(Text, comment='城市') ULdistrict = Column(Text, comment='区县') ULresult = Column(Text, comment='查询结果') - ULlng = Column(Text, comment='维度') - ULlat = Column(Text, comment='经度') + ULlng = Column(Text, comment='经度') + ULlat = Column(Text, comment='纬度') USid = Column(String(64), comment='用户id') @@ -355,3 +355,12 @@ class SharingParameters(Base): USid = Column(String(64), comment='用户id') SPScontent = Column(Text, comment='分享的原参数') SPSname = Column(String(30), comment='分享的参数名 如: secret_usid, plid') + # SPStype = Column(Integer, default=0, comment='0 直接分享 1 通过生成图片分享 2 个人二维码') + + +class SharingType(Base): + """分享类型""" + __tablename__ = 'SharingType' + STid = Column(String(64), primary_key=True) + USid = Column(String(64), comment='') + STtype = Column(Integer, default=0, comment='分享类型') diff --git a/ticket_mock.py b/ticket_mock.py new file mode 100644 index 00000000..438e9f38 --- /dev/null +++ b/ticket_mock.py @@ -0,0 +1,121 @@ +import time +from random import randint +from datetime import datetime +from planet import create_app +from planet.config.enums import TicketStatus, TicketsOrderStatus, TicketPayType +from planet.extensions.register_ext import db +from planet.models import User, Ticket, TicketsOrder +from sqlalchemy import false + +false = false() + + +def add_ticket_order(tiid, usids): + instance_list = [] + with db.auto_commit(): + for usid in usids: + score = 5 * randint(0, 100) + print('score: {}'.format(score)) + to = TicketsOrder.create({'TSOid': str(datetime.now().timestamp()) + '_' + str(randint(10, 10000)), + 'USid': usid, + 'TIid': tiid, + 'TSOstatus': TicketsOrderStatus.pending.value, + 'TSOtype': TicketPayType.scorepay.value, + 'TSOactivation': score + }) + instance_list.append(to) + db.session.add_all(instance_list) + return len(instance_list) + + +def list_pending_act(x=None): + tickets = Ticket.query.filter(Ticket.isdelete == false, Ticket.TIstatus == TicketStatus.active.value + ).order_by(Ticket.TIstartTime.asc()).all() + t_list = [{'index': index, 'name': t.TIname, 'tiid': t.TIid} for index, t in enumerate(tickets)] + if not x and x != 0: + print('>>> 目前所有进行中的抢票: {}个 <<< \n\n {}\n\n'.format(len(tickets), '\n'.join(map(lambda v: str(v), t_list)))) + else: + return t_list[x].get('tiid'), '\n选择了 >>> {} <<<\n'.format(t_list[x].get('name')) + + +def list_mock_user(usids=None, others_usid=None): + users = User.query.filter(User.isdelete == false, User.USid.ilike('id000%') + ).order_by(User.USid.asc(), User.createtime.desc()).all() + msg = '' + if usids: + users = filter(lambda k: k.USid in usids, users) + msg = '已加入的用户为:' + elif others_usid: + users = filter(lambda k: k.USid not in others_usid, users) + msg = '剩余可加入的用户为:' + else: + print('\n>>> 目前所有虚拟用户({}个): <<< \n'.format(len(users))) + if msg: + users_list = [{'index': index, 'usid': u.USid, 'name': u.USname} for index, u in enumerate(users)] + print('{} \n{}'.format(msg, '\n'.join(map(lambda k: str(k), users_list)))) + return users_list + + +def query_ticket_order(tiid): + tso_qurey = TicketsOrder.query.filter(TicketsOrder.isdelete == false, TicketsOrder.TIid == tiid, + TicketsOrder.TSOstatus == TicketsOrderStatus.pending.value) + total_count = tso_qurey.count() + print('\n >>> 该门票共有{}条申请记录 <<< \n'.format(total_count)) + mock_tso = tso_qurey.filter(TicketsOrder.USid.ilike('id000%')).all() + mock_users = [{'usid': to.USid, + 'score': to.TSOactivation, + 'name': db.session.query(User.USname).filter(User.isdelete == false, User.USid == to.USid).scalar(), + } + for to in mock_tso] + mock_users.sort(key=lambda y: y.get('usid')) + print('已在该门票记录中添加的虚拟用户为({}个): \n {}'.format(len(mock_users), '\n'.join(map(lambda x: str(x), mock_users)))) + usids = [i.get('usid') for i in mock_users] + return usids + + +def run(): + x = input('选择活动, 输入相应活动的index值(数字): ') + tiid = None + while x: + try: + tiid, msg = list_pending_act(int(x)) + print(msg) + x = None + except (ValueError, IndexError): + x = input('输入正确的index :') + print('\n' + '-' * 20 + '\n') + joined_usid = query_ticket_order(tiid) + print('-' * 20 + '\n') + can_join_list_usid = list_mock_user(others_usid=joined_usid) + choices = input('\n输入要加入该活动的用户index值(数字),可一次性输入多个,用英文状态逗号隔开,如 0,1,2,3 :') + choose = choices.split(',') + print(choose) + usids = None + while choose: + try: + usids = [can_join_list_usid[int(i)].get('usid') for i in choose] + choose = None + except (ValueError, IndexError): + choose = input('\n输入正确的用户index值(数字),可一次性输入多个,用英文状态逗号隔开,如 0,1,2,3 :') + choose = choose.split(',') + + print(usids) + counts = add_ticket_order(tiid, usids) + print('-' * 20 + '\n') + print('成功添加 {} 条记录'.format(counts)) + time.sleep(1) + joined_usid = query_ticket_order(tiid) + print('-' * 20 + '\n') + list_mock_user(others_usid=joined_usid) + input('\n >>>按回车键返回活动选择列表<<< \n') + return + + +if __name__ == '__main__': + app = create_app() + with app.app_context(): + list_mock_user() + print('\n' + '-' * 20 + '\n') + while True: + list_pending_act() + run()