hch
2018-09-18 ba85f1a09019fd5c8a67434e72539adcc20f5cd2
1558 【后台】兑换码后台开发
11个文件已添加
6476 ■■■■■ 已修改文件
Tool/WebCenter/Coupon/CouponDB.py 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/bottle.py 4403 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/config.ini 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/lib/CommFunc.py 371 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/lib/DBController.py 516 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/lib/ReadConfig.py 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/lib/TimeRotatingPathFileHandler.py 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/lib/__init__.py 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/lib/mylog.py 158 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/main.py 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/webapp.py 496 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Tool/WebCenter/Coupon/CouponDB.py
New file
@@ -0,0 +1,169 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#
##@package E:/GameSVN/TeamTool/PythonScribe/PyModule/Coupon/CouponDB.py
# @todo:
#
# @author: Alee
# @date 2017-6-2 下午05:21:48
# @version 1.0
#
# @note:
#
#---------------------------------------------------------------------
#-------------------------------------------------------------------------------
#检查并更新索引
#-------------------------------------------------------------------------------
from lib import ReadConfig
from lib import DBController
from lib import mylog
import os
g_dbController = None
CouponBatchColName = "tagCouponBatch"
CouponCodeColName = "tagCouponCode"
ModuleName = "CouponDB"
Def_ConfigPath = os.getcwd()
def InitDB():
    global g_dbController
    if not g_dbController:
        IP = ReadConfig.ReadConfig(Def_ConfigPath + "\\config.ini").GetValue("MongoDB", "IP")
        Port = ReadConfig.ReadConfig(Def_ConfigPath + "\\config.ini").GetInt("MongoDB", "Port")
        User = ReadConfig.ReadConfig(Def_ConfigPath + "\\config.ini").GetValue("MongoDB", "User")
        Pwd = ReadConfig.ReadConfig(Def_ConfigPath + "\\config.ini").GetValue("MongoDB", "Pwd")
        DBName = ReadConfig.ReadConfig(Def_ConfigPath + "\\config.ini").GetValue("MongoDB", "DBName")
        g_dbController = DBController.DBController(IP, Port, DBName, User, Pwd, None)
        if g_dbController.connected:
            #只在链接DB检查一次
            result = CheckAndUpdateIndexOnDb(g_dbController.db)
            if not result:
                mylog.debug('索引有问题')
                return False
        mylog.debug('获取新的数据库链接')
    if not g_dbController.connected:
        mylog.debug('无法链接数据库')
        return False
    return True
def GetDBEventCon():
    global g_dbController
    if not InitDB():
        return None
    return g_dbController
def ReadIndexInfo():
    indexInfoDict = {
    CouponBatchColName:
    {
        'CouponBatch_1':
        {
            'unique':True,
            'key':[('couponid', 1)]    #索引的字典和递增(1)或递减(-1)
         }
     },
    }
    return indexInfoDict
def CompareIndex(curIndexDict, expectIndexDict):
    #比较unique
    curUnique = curIndexDict.get('unique', None)
    expectUnique = expectIndexDict.get('unique', None)
    if curUnique is None:
        if expectUnique is None:
            pass
        elif expectUnique:
#            print '#debug 1'
            return False
        else:
            pass
    else:
        if expectUnique is None:
            if (not curUnique):
                return True
#            print '#debug 2'
            return False
        if curUnique and (not expectUnique):
#            print '#debug 3'
            return False
        if (not curUnique) and expectUnique:
#            print '#debug 4'
            return False
    #比较keylist
    curKeyList = curIndexDict.get('key', None)
    expectKeyList = expectIndexDict.get('key', None)
    if curKeyList is None:
        if expectKeyList is None:
            pass
        else:
#            print '#debug 5'
            return False
    else:
        if expectKeyList is None:
#            print '#debug 6'
            return False
        else:
            for pair in curKeyList:
                if pair not in expectKeyList:
#                    print '#debug 7'
                    return False
            for pair in expectKeyList:
                if pair not in curKeyList:
#                    print '#debug 8'
                    return False
    return True
def CheckIndexes(db, colName, expectIndexInfo):
    col = db[colName]
    '''检查表中是否有多余的索引'''
    indexDict = col.index_information()
    for k, v in indexDict.items():
        if k == '_id_':
            continue
        if k not in expectIndexInfo:
            return False
        if not CompareIndex(v, expectIndexInfo[k]):
            return False
    return True
def AddIndexes(db, colName, expectIndexes):
    '''添加索引'''
    col = db[colName]
    '''给表添加索引'''
    curIndexesDict = col.index_information()
    for k, v in expectIndexes.items():
        if k not in curIndexesDict:
            expectIndexInfo = expectIndexes[k]
            isUnique = expectIndexInfo.get('unique', False)
            try:
                col.create_index(expectIndexInfo['key'], name = k, unique = isUnique)
            except Exception, e:
                return False
    return True
def CheckAndUpdateIndexOnDb(db):
    '''对于索引文件中的每个表,数据库中该表有的索引信息,索引文件中必须有并且匹配unique和(key, direction)list'''
    indexInfoDict = ReadIndexInfo()
    for k, v in indexInfoDict.items():
        colName = k
        colIndexDict = v
        if not CheckIndexes(db, colName, colIndexDict):
            return False
        if not AddIndexes(db, colName, colIndexDict):
            return False
    return True
Tool/WebCenter/Coupon/bottle.py
New file
Diff too large
Tool/WebCenter/Coupon/config.ini
New file
@@ -0,0 +1,94 @@
[MongoDB]
; Mongo数据库
IP=127.0.0.1
Port=27017
User=sa
Pwd=sa
DBName=CouponData
[Coupon]
IsDebug = False
;下载卡批次地址,中心地址和端口
exporturl = http://mobiletest.173on.com
CenterPort = 53003
;发放礼包卡 通过邮件 失败通知通过 GMT_MediaCard,配置为同一个key
PushPort = 30001
PushKey = GVFoyGfhFDHfgHOU1OQ3RtUFgy5QxPV1
;固定码列表,没有则放空, 以字母z开头避免混淆; 公共固定码,所有人用同一个码,每个人只能领取一次
;CommonCards = ["zj98u2j0ud1"]
CommonCards = []
;----------------------------------------------------------------------------------------------------
;公共固定码,所有人用同一个码,每个人只能领取一次
;[zj98u2j0ud1]
;MailSender=系统邮件
;MailTitle=入门礼包
;MailText=感谢您对游戏的支持,请及时领取礼包。
;Items=[[1, 4], [2, 5]]
;礼包卡以邮件形式发放15天的领取期限
;卡类型 开头字母做标识如 g1~999代表此卡只能使用一次; z开头代表固定码查看CommonCards
; 物品格式 [[物品ID1,数量1], [物品ID2,数量2]] 默认绑定不提供配置 限制5个物品
[g001]
MailSender=系统邮件
MailTitle=新手入门礼包
MailText=感谢您对游戏的支持,请及时领取礼包。
Items=[[293,10],[911,1],[3501,1],[3511,1]]
[g002]
MailSender=系统邮件
MailTitle=修炼成长礼包
MailText=感谢您对游戏的支持,请及时领取礼包。
Items=[[922,1],[10,1],[911,1],[181,10]]
[g003]
MailSender=系统邮件
MailTitle=学有初成礼包
MailText=感谢您对游戏的支持,请及时领取礼包。
Items=[[293,20],[4001,1],[952,2],[923,1]]
[g004]
MailSender=系统邮件
MailTitle=孜孜不倦礼包
MailText=感谢您对游戏的支持,请及时领取礼包。
Items=[[3,1],[911,1],[2107,2],[149,1]]
[g005]
MailSender=系统邮件
MailTitle=渡劫历险礼包
MailText=感谢您对游戏的支持,请及时领取礼包。
Items=[[3,2],[911,1],[924,2],[4656,1]]
[g006]
MailSender=系统邮件
MailTitle=飞升成仙礼包
MailText=感谢您对游戏的支持,请及时领取礼包。
Items=[[3,2],[911,1],[4658,1],[2107,2]]
[g007]
MailSender=系统邮件
MailTitle=微信礼包
MailText=感谢您对游戏的支持,请及时领取礼包。
Items=[[2,1],[911,1],[2211,5],[3511,2]]
[g008]
MailSender=系统邮件
MailTitle=安抚礼包
MailText=感谢您对游戏的支持,请及时领取礼包。
Items=[[13,1],[3,1],[149,1],[142,5]]
[g009]
MailSender=系统邮件
MailTitle=VIP专属礼包
MailText=感谢您对游戏的支持,请及时领取礼包。
Items=[[2050,1],[4659,1],[2211,10],[151,1]]
Tool/WebCenter/Coupon/lib/CommFunc.py
New file
@@ -0,0 +1,371 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#
##@package
#
# @todo:
#
# @author: Alee
# @date 2018-7-18 上午11:22:43
# @version 1.0
#
# @note:
#
#---------------------------------------------------------------------
import struct
import string
import datetime
import os
import binascii
import socket
import sys
#python的读取/写入库
#要进行反转高低位的话,可使用!符号,如struct.unpack_from('!B', buf, pos)
#------------------------读取
def ReadBYTE(buf, pos):
    curValue = struct.unpack_from('b', buf, pos)
    pos += 1
    return curValue[0], pos
def ReadWORD(buf, pos):
    curValue = struct.unpack_from('H', buf, pos)
    pos += 2
    return curValue[0], pos
def ReadDWORD(buf, pos):
    curValue = struct.unpack_from('i', buf, pos)
    pos += 4
    return curValue[0], pos
def ReadFloat(buf, pos):
    curValue = struct.unpack_from('f', buf, pos)
    pos += 4
    return curValue[0], pos
def ReadDouble(buf, pos):
    curValue = struct.unpack_from('d', buf, pos)
    pos += 8
    return curValue[0], pos
def ReadString(buf, pos, _len):
    curValue = struct.unpack_from('%ds'%_len, buf, pos)
    pos += _len
    return curValue[0], pos
#----------------------写入
def WriteBYTE(buf, value):
    buf += struct.pack('b', value)
    return buf
def WriteWORD(buf, value):
    buf += struct.pack('H', value)
    return buf
def WriteDWORD(buf, value):
    buf += struct.pack('i', value)
    return buf
def WriteFloat(buf, value):
    buf += struct.pack('f', value)
    return buf
def WriteDouble(buf, value):
    buf += struct.pack('d', value)
    return buf
def WriteString(buf, len, value):
    buf += struct.pack('%ds'%len, value)
    return buf
#获得当前系统时间
def GetCurrentDataTimeStr():
    curTime = datetime.datetime.today()
    curTimeStr = str(curTime)
    curTimeStr = curTimeStr.split(".")[0]
    return curTimeStr
#获得系统时间(参数 -> 时间列表)
def GetDateTimeByStr(timeStr):
    timeStr = timeStr.split(".")[0]
    return  datetime.datetime.strptime(timeStr, "%Y-%m-%d %H:%M:%S")
#字符串转换为整型, 如果不能转换, 返回默认值
def ToIntDef(input, defValue = 0):
    try:
        result = int(input)
        return result
    except ValueError:
        return defValue
#字符串转换为整型, 如果不能转换, 返回False,原值
def StrToInt(input):
    try:
        result = int(input)
        return True,result
    except ValueError:
        return False,input
#16进制颜色转换
#"#FFFFFF"--"255,255,255"
def HcToSc(h):
    h="0x"+h[1:7]
    red=string.atoi(h[:2]+h[2:4], base=16)
    green=string.atoi(h[:2]+h[4:6], base=16)
    blue=string.atoi(h[:2]+h[6:8], base=16)
    cStr=str(red)+","+str(green)+","+str(blue)
    return cStr
#"255,255,255"--"#FFFFFF"
def ScToHc(s):
    red=hex(string.atoi(s.split(",")[0]))[2:]
    green=hex(string.atoi(s.split(",")[1]))[2:]
    blue=hex(string.atoi(s.split(",")[2]))[2:]
    hStr="#"+str(red+green+blue)
    return hStr
#16进制转换
#"0xFFFFFF"--"255,255,255"
def HdToSd(h):
    red=string.atoi(h[0:2]+h[2:4], base=16)
    green=string.atoi(h[0:2]+h[4:6], base=16)
    blue=string.atoi(h[0:2]+h[6:8], base=16)
    cStr=str(red)+","+str(green)+","+str(blue)
    return cStr
#"255,255,255"--"0xFFFFFF"
def SdToHd(s):
    red=hex(string.atoi(s.split(",")[0]))[2:]
    green=hex(string.atoi(s.split(",")[1]))[2:]
    blue=hex(string.atoi(s.split(",")[2]))[2:]
    hStr="0x"+str(red+green+blue)
    return hStr
def GetPercent(value1, value2):
    if value2 == 0:
        return 0
    return int(float(value1) / float(value2) * 100)
def OpenFileForWrite(fileName):
    dirName = os.path.dirname(fileName)
    if not os.path.isdir(dirName):
        os.makedirs(dirName)
    if os.path.isfile(fileName):
        return open(fileName, 'w')
    return  open(fileName, 'a')
#函数调用
def ParseNameGetObj(curCallObj, callName):
    callList = callName.split(".")
    if len(callList) <= 1:
        return None
    for curCallName in callList:
        if hasattr(curCallObj, curCallName) != True:
            #无此属性
            return None
        curCallObj = getattr(curCallObj, curCallName)
    return curCallObj
#获得执行函数
def GetExecFunc(curCallObj, callName):
    curCallObj = ParseNameGetObj(curCallObj, callName)
    if curCallObj == None:
        return None
    if callable(curCallObj) != True:
        #不可调用
        return None
    return curCallObj
#字符串异或处理
def str_xor(astring, xornum=150):
    a=[]
    for x in astring:
        a.append(chr(ord(x)^xornum))
    return ''.join(a)
#解析封包
def ParseBuff(buff):
    result=''
    for i in  range(len(buff)):
        if i%2==0 and i!=0:
            result=result + ' ' + buff[i]
        else:
            result = result + buff[i]
    return result
def b2a_hex(data):
    return ParseBuff(binascii.b2a_hex(data))
def GetHostName():
    return socket.gethostname()
def GetHostIP():
    return socket.gethostbyname(GetHostName())
def compact_traceback():
    t, v, tb = sys.exc_info()
    tbinfo = []
    if not tb: # Must have a traceback
        raise AssertionError("traceback does not exist")
    while tb:
        tbinfo.append((
            tb.tb_frame.f_code.co_filename,
            tb.tb_frame.f_code.co_name,
            str(tb.tb_lineno)
            ))
        tb = tb.tb_next
    # just to be safe
    del tb
    info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo])
    return t, v, info
import base64
import urllib
import urllib2
#---------------------------------------------------------------------
## 加密算法需要的定义
Def_NumEncodeCnt = 3  # 加密的次数
Def_NumXorKey = 151  # 异或的key
## 公司内部解密算法
def GetDecodePwd(strPwd):
    strResult = strPwd
    for index in xrange(Def_NumEncodeCnt):
        strResult = base64.b64decode(strResult)
    tempResult = ""
    for index in xrange(len(strResult)):
        tempResult += chr(ord(strResult[index])^Def_NumXorKey)
    return tempResult
## 公司内部加密算法
def GetEncodePwd(strPwd):
    strResult = ""
    for index in xrange(len(strPwd)):
        strResult += chr(ord(strPwd[index])^Def_NumXorKey)
    for index in xrange(Def_NumEncodeCnt):
        strResult = base64.b64encode(strResult)
    return strResult
#---------------------------------------------------------------------
## UTF-8 <==> GBK
def gbk2utf8(content):
    if type( content ) is not str:
        content = str(content)
    try:
        return content.decode('gbk').encode('utf-8')
    except:
        return content
def utf82gbk(content):
    if type( content ) is not str:
        content = str(content)
    try:
        return content.decode('utf-8').encode('gbk')
    except:
        return content
## GET请求
def DoGet(url, data=None, headers={}):
    if data:
        request = urllib2.Request(url + "?" + urllib.urlencode(data), None, headers)
    else:
        request = urllib2.Request(url, None, headers)
    response = urllib2.urlopen(request, timeout=5)
    content = response.read()
    response.close()
    return content
## POST请求
def DoPost(url, data, headers={}):
    try:
        data = urllib.urlencode(data)
    except:
        pass
    request = urllib2.Request(url, data, headers)
    try:
        response = urllib2.urlopen(request, timeout=30)
        content = response.read()
        response.close()
    except:
        content = "error timeout"
    return content
FileTypeDict = {
'.txt':'text/html',
'.html':'text/html',
'.htm':'text/html',
'.bmp':'application/x-bmp',
'.ico':'image/x-icon',
'.jpe':'image/jpeg',
'.jpeg':'image/jpeg',
'.jpg':'application/x-jpg',
'.png':'application/x-png',
'.gif':'image/gif',
'.bin':'application/octet-stream',
'.*':'application/octet-stream'
}
def GetHttpContentType(strType):
    return FileTypeDict[strType]
## 编码
#  @param srcEncoding 编码格式
#  @param input 字符串
#  @return None
def EncodingToUnicode(srcEncoding, msg):
    try:
        result = unicode(msg, srcEncoding)    #translate to utf-8
    except:
        return "EncodingToUnicode error"
    return result
## 编码
#  @param srcEncoding 编码格式
#  @param input 字符串
#  @return None
def UnicodeToEncoding(dstEncoding, msg):
    try:
        result = msg.encode(dstEncoding)
    except:
        return "UnicodeToEncoding error"
    return result
Tool/WebCenter/Coupon/lib/DBController.py
New file
@@ -0,0 +1,516 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
#-------------------------------------------------------------------------------
#
import pymongo
from pymongo.son_manipulator import SONManipulator
import base64
from functools import wraps
from time import (sleep)
import CommFunc
#模拟SQL的IDENT
def seq(db, collectionName, fieldName, feed, increment):
    try:
        result = 0
        collection = db['%s_seq'%collectionName]
        resultObj = collection.find_and_modify(query={'_id':fieldName}, update={'$inc':{'seq':increment}}, new=True)
        if resultObj:
            result = resultObj['seq']
        else:
            resultObj = collection.find_and_modify(query={'_id':fieldName}, update={'$set':{'seq':feed}}, new=True,
                                                   upsert=True)
            if resultObj:
                result = resultObj['seq']
            else:
                return False, None
    except Exception, e:
        return False, None
    return True, result
class ObjectIdRemover(SONManipulator):
    def transform_outgoing(self, son, collection):
        if '_id' in son:
            del son['_id']
        return son
class EncodeStringManipulator(SONManipulator):
    def __init__(self, encoding):
        self.encoding = encoding
    def transform_incoming(self, son, collection):
        def transform_value(value):
            if isinstance(value, dict):
                return transform_dict(value)
            elif isinstance(value, list):
                return [transform_value(v) for v in value]
            elif isinstance(value, basestring):
                result, value = CommFunc.EncodingToUnicode(self.encoding, value)
                return value
            return value
        def transform_dict(object):
            for (key, value) in object.items():
                object[key] = transform_value(value)
            return object
        def transform_list(container):
            for item in container:
                transform_dict(item)
            return container
        if isinstance(son, dict):
            return transform_dict(son)
        elif isinstance(son, list):
            return transform_list(son)
        return son
    def transform_outgoing(self, son, collection):
        def transform_value(value):
            if isinstance(value, dict):
                return transform_dict(value)
            elif isinstance(value, list):
                return [transform_value(v) for v in value]
            elif isinstance(value, basestring):
                result, value =CommFunc.UnicodeToEncoding(self.encoding, value)
                return value
            return value
        def transform_dict(object):
            for (key, value) in object.items():
                object[key] = transform_value(value)
            return object
        def transform_list(container):
            for item in container:
                transform_dict(item)
            return container
        if isinstance(son, dict):
            return transform_dict(son)
        elif isinstance(son, list):
            return transform_list(son)
        return son
class Base64StringManipulator(SONManipulator):
    def transform_incoming(self, son, collection):
        def transform_value(value):
            if isinstance(value, dict):
                return transform_dict(value)
            elif isinstance(value, list):
                return [transform_value(v) for v in value]
            elif isinstance(value, basestring):
                return base64.b64encode(value)
            return value
        def transform_dict(object):
            for (key, value) in object.items():
                object[key] = transform_value(value)
            return object
        def transform_list(container):
            for item in container:
                transform_dict(item)
            return container
        if isinstance(son, dict):
            return transform_dict(son)
        elif isinstance(son, list):
            return transform_list(son)
        return son
    def transform_outgoing(self, son, collection):
        def transform_value(value):
            if isinstance(value, dict):
                return transform_dict(value)
            elif isinstance(value, list):
                return [transform_value(v) for v in value]
            elif isinstance(value, basestring):
                return base64.b64decode(value)
            return value
        def transform_dict(object):
            for (key, value) in object.items():
                object[key] = transform_value(value)
            return object
        def transform_list(container):
            for item in container:
                transform_dict(item)
            return container
        if isinstance(son, dict):
            return transform_dict(son)
        elif isinstance(son, list):
            return transform_list(son)
        return son
#用于修饰DBController的数据库操作函数
#断线自动重试
def reconnect_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwds):
        MAX_RECONNECT = 10
        RECONNECT_INTERVAL = 0.1
        failCnt = 0
        while True:
            try:
                #去掉self
                return func(*args, **kwds)
            except pymongo.errors.AutoReconnect, e:
                failCnt += 1
                sleep(RECONNECT_INTERVAL)
                if failCnt > MAX_RECONNECT:
                    raise e
    return wrapper
class DBController:
    def __init__(self, host, port, dbName, user, pwd, encoding):
        self.host = host
        self.port = port
        self.dbName = dbName
        self.user = user
        self.pwd = pwd
        self.connected = False
        self.con = None
        self.db = None
        self.lastError = None
        self.translator = None
        #===========================================================================================
        # if encoding == 'base64':
        #    self.translator = Base64StringManipulator()
        # else:
        #    self.translator = EncodeStringManipulator(encoding)
        #===========================================================================================
        self.initialize()
    def initialize(self):
        if not self.connected:
            if not self.doConnect(self.host, self.port):
                return False
            authResult = self.doAuthentication(self.dbName, self.user, self.pwd)
            if self.db:
                self.db.add_son_manipulator(ObjectIdRemover())
            return authResult
        return True
    def doConnect(self, ip, port):
        try:
            self.con = pymongo.Connection(ip, port)
        except TypeError, typeError:
            raise
        except pymongo.errors.ConnectionFailure, failure:
            self.lastError = failure
            return False
        except Exception, e:
            self.lastError = e
            return False
        except:
            self.lastError = 'Unknown exception occur!'
            return False
        self.connected = True
        return True
    def doAuthentication(self, dbName, user, pwd):
        if not self.connected or not self.con:
            self.lastError = 'Not connected yet!'
            return False
        self.db = self.con[dbName]
        authDB = self.con['admin']
        try:
            return authDB.authenticate(user, pwd)
#            return self.db.authenticate(user, pwd)
        except TypeError, typeError:
            self.lastError = typeError
            return False
        except Exception, e:
            self.lastError = e
            return False
        except:
            self.lastError = 'Unknown exception occur!'
            return False
    def find_one(self, colName, spec, filter = None):
        result, recList = self.find(colName, spec, filter, 1)
        if not result:
            return False, None
        for rec in recList:
            return True, rec
        return True, None
    @reconnect_decorator
    def find(self, colName, spec = None, filter = None, maxCnt = 0, sortBy = None):
        if not self.connected:
            if not self.initialize():
                return False, []
        result = False
        resultDictList = []
        col = self.db[colName]
        if self.translator:
            spec = self.translator.transform_incoming(spec, None)
        try:
            resultCollection = col.find(spec, filter, limit = maxCnt, sort = sortBy)
            if self.translator:
                resultDictList = self.translator.transform_outgoing(list(resultCollection), None)
            else:
                resultDictList = list(resultCollection)
            return True, resultDictList
        except TypeError, typeError:
            self.lastError = typeError
            return result, resultDictList
        except pymongo.errors.OperationFailure, err:
            self.lastError = err
            return result, resultDictList
        except Exception, e:
            self.lastError = e
            return result, resultDictList
        except:
            self.lastError = 'Unknown exception occur!'
            return result, resultDictList
    @reconnect_decorator
    def insert(self, colName, doc_or_docs, isSafe = True):
        if not self.connected:
            if not self.initialize():
                return False
        col = self.db[colName]
        if self.translator:
            doc_or_docs = self.translator.transform_incoming(doc_or_docs, None)
        try:
            col.insert(doc_or_docs, safe = isSafe)
        except pymongo.errors.OperationFailure, err:
            self.lastError = err
            return False
        except Exception, e:
            self.lastError = e
            return False
        except:
            self.lastError = 'Unknown exception occur!'
            return False
        return True
    @reconnect_decorator
    def update(self, colName, spec, doc, isUpsert = False, isSafe = True, isMulti = False):
        if not self.connected:
            if not self.initialize():
                return False
        col = self.db[colName]
        #需要先对doc进行处理,但由不能开启collection.update的manipulate,因为那会应用所有处理
        if self.translator:
            spec = self.translator.transform_incoming(spec, None)
            doc = self.translator.transform_incoming(doc, None)
        try:
            col.update(spec, doc, upsert = isUpsert, safe = isSafe, multi = isMulti)
        except TypeError, typeError:
            self.lastError = typeError
            return False
        except pymongo.errors.OperationFailure, err:
            self.lastError = err
            return False
        except Exception, e:
            self.lastError = e
            return False
        except:
            self.lastError = 'Unknown exception occur!'
            return False
        return True
    @reconnect_decorator
    def save(self, colName, doc, isSafe = True):
        if not self.connected:
            if not self.initialize():
                return False
        col = self.db[colName]
        if self.translator:
            doc = self.translator.transform_incoming(doc, None)
        try:
            col.save(doc, safe = isSafe)
        except TypeError, typeError:
            self.lastError = typeError
            return False
        except pymongo.errors.OperationFailure, err:
            self.lastError = err
            return False
        except Exception, e:
            self.lastError = e
            return False
        except:
            self.lastError = 'Unknown exception occur!'
            return False
        return True
    @reconnect_decorator
    def remove(self, colName, spec = None, isSafe = True):
        if not self.connected:
            if not self.initialize():
                return False
        col = self.db[colName]
        if self.translator:
            spec = self.translator.transform_incoming(spec, None)
        try:
            col.remove(spec, safe = isSafe)
        except pymongo.errors.OperationFailure, err:
            self.lastError = err
            return False
        except Exception, e:
            self.lastError = e
            return False
        except:
            self.lastError = 'Unknown exception occur!'
            return False
        return True
    @reconnect_decorator
    def drop(self, colName):
        if not self.connected:
            if not self.initialize():
                return False
        col = self.db[colName]
        try:
            col.drop()
        except TypeError, typeError:
            self.lastError = typeError
            return False
        except Exception, e:
            self.lastError = e
            return False
        except:
            self.lastError = 'Unknown exception occur!'
            return False
        return True
    @reconnect_decorator
    def count(self, colName):
        if not self.connected:
            if not self.initialize():
                return False, 0
        col = self.db[colName]
        try:
            cnt = col.count()
        except pymongo.errors.OperationFailure, err:
            self.lastError = err
            return False, 0
        except Exception, e:
            self.lastError = e
            return False, 0
        except:
            self.lastError = 'Unknown exception occur!'
            return False, 0
        return True, cnt
def test_seq():
    con = pymongo.Connection()
    db = con.admin
    if not db.authenticate('sa', 'sa'):
        print 'auth failed!'
        return
    colName = 'tagSeqTest'
    fieldName = 'ID'
    db = con['test']
    db.drop_collection(colName)
    db.drop_collection('%s_seq'%colName)
    result, ID = seq(db, colName, fieldName, 1, 1)
    assert (result and ID == 1)
    result, ID = seq(db, colName, fieldName, 1, 1)
    assert (result and ID == 2)
def test_StringManipulator():
    translator = Base64StringManipulator()
    son = []
    result = translator.transform_incoming(son, None)
    assert (son == result)
    result = translator.transform_outgoing(son, None)
    assert (son == result)
    son = [{'a':1}]
    result = translator.transform_incoming(son, None)
    assert (son == result)
    result = translator.transform_outgoing(son, None)
    assert (son == result)
    son = [{'a':'a'}]
    result = translator.transform_incoming(son, None)
    assert (result and result == [{'a':base64.b64encode('a')}])
    result = translator.transform_outgoing(result, None)
    assert (result and result == son)
    son = [{'a':[{'b':'b'}, {'c':'c'}]}]
    result = translator.transform_incoming(son, None)
    assert (result and result == [{'a':[{'b':base64.b64encode('b')}, {'c':base64.b64encode('c')}]}])
    result = translator.transform_outgoing(result, None)
    assert (result and result == son)
def test_DBController():
    testColName = 'tagTestController'
    dbController = DBController('localhost', 27017, 'test', 'test', '1')
    result = dbController.drop(testColName)
    assert result
    result, cnt = dbController.count(testColName)
    assert (result and cnt == 0)
    doc = {'a':1}
    result = dbController.insert(testColName, doc)
    assert result
    result, recs = dbController.find(testColName)
    assert (result and len(recs) == 1)
    rec = recs[0]
#    del rec['_id']
#    print 'rec = %s\r\ndoc = %s'%(rec, doc)
    assert (rec == doc)
    spec = {'a':1}
    updateDoc = {'a':2}
    updateDocWithModifier = {'$set':updateDoc}
    result = dbController.update(testColName, spec, updateDocWithModifier)
    assert result
    result, recs = dbController.find(testColName)
    assert (result and len(recs) == 1)
    rec = recs[0]
    del rec['_id']
#    print 'rec = %s\r\nupdateDoc = %s'%(rec, updateDoc)
    assert (rec == updateDoc)
    result = dbController.remove(testColName)
    assert result
    result, recs = dbController.find(testColName)
    assert (result and recs == [])
    saveDoc = {'b':3}
    result = dbController.save(testColName, saveDoc)
    assert result
    result, recs = dbController.find(testColName)
    assert (result and len(recs) == 1)
    rec = recs[0]
#    del rec['_id']
    assert (rec == saveDoc)
def test():
    test_seq()
    test_StringManipulator()
    test_DBController()
    print 'test ok!'
if __name__ == '__main__':
    test()
Tool/WebCenter/Coupon/lib/ReadConfig.py
New file
@@ -0,0 +1,74 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#
##@package
#
# @todo:
#
# @author: Alee
# @date 2018-6-5 下午08:20:53
# @version 1.0
#
# @note:
#
#---------------------------------------------------------------------
import ConfigParser
class ReadConfig():
    def __init__(self, filePath):
        self.__config = ConfigParser.ConfigParser()
        self.__config.read(filePath)
    def GetValue(self, section, option, default=None, isThrowException=False):
        try:
            return self.__config.get(section, option);
        except Exception, err:
            if not isThrowException:
                return default;
            raise Exception, "Error:配置问题,%s"%err;
    get = GetValue;
    def GetInt(self, section, option, default = 0, isThrowException=False):
        try:
            return self.__config.getint(section, option);
        except Exception, err:
            if not isThrowException:
                return default;
            raise Exception, "Error:配置问题,%s"%err;
    getint = GetInt;
    def GetBoolean(self, section, option, default = False):
        try:
            return self.__config.getboolean(section, option);
        except ValueError:
            default = str(default).lower();
            if default not in self.__config._boolean_states:
                return False;
            else:
                return self.__config._boolean_states[default];
    getboolean = GetBoolean;
    def HasSection(self, section):
        return self.__config.has_section(section)
    def HasOption(self, section, option):
        return self.__config.has_option(section, option)
#===============================================================================
# ## 读取Eval的值
# def GetEvalConfig(path):
#    with open(path, "r") as file:
#        content = file.read()
#        try:
#            value = eval(content)
#        except:
#            value = None
#
#    return value
#===============================================================================
Tool/WebCenter/Coupon/lib/TimeRotatingPathFileHandler.py
New file
@@ -0,0 +1,155 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#
##@package
#
# @todo:
#
# @author: Alee
# @date 2018-7-18 上午11:40:53
# @version 1.0
#
# @note:
#
#---------------------------------------------------------------------
#根据时间切换目录和文件的文件处理器
from logging.handlers import *
class TimeRotatingPathFileHandler(TimedRotatingFileHandler):
    def __init__(self, root, filename, when = 'h', interval = 1, backupCount = 0, encoding = None, delay = False, utc = False):
        #创建输出目录数
        t = int(time.time())
        if utc:
            timeTup = time.gmtime(t)
        else:
            timeTup = time.localtime(t)
        year = timeTup[0]
        month = timeTup[1]
        day = timeTup[2]
        self.root = root
        self.filename = filename
        self.baseFilename = self.getBaseFileName(self.root, year, month, day, self.filename)
        #print 'baseFileName = %s'%self.baseFilename
        filepath = os.path.dirname(self.baseFilename)
        self.ensurePath(filepath)
        #改变当前工作目录
        #if not (os.getcwd() == filepath):
        #    os.chdir(filepath)
        TimedRotatingFileHandler.__init__(self, self.baseFilename, when, interval, backupCount, encoding, delay, utc)
        #self.suffix = ''#不指定后缀
    def getBaseFileName(self, root, year, month, day, filename):
        filenameWithoutExt = filename.split('.')[0]
        return os.path.join(root, '%04d-%02d\\%s\\%02d\\%s'%(year, month, filenameWithoutExt, day, filename))
    def ensurePath(self, filepath):
        if not os.path.exists(filepath):
            os.makedirs(filepath)
    def doRollover(self):
        lastRolloverTime = self.rolloverAt
        #TimedRotatingFileHandler.doRollover(self)
        if self.stream:
            self.stream.close()
            self.stream = None
        #获取上一次切换的时间
        t = self.rolloverAt - self.interval
        if self.utc:
            timeTuple = time.gmtime(t)
        else:
            timeTuple = time.localtime(t)
        filenameTuple = self.filename.split('.')
        filename = ''
        if len(filenameTuple) == 1:
            filename = '%s-%s'%(filenameTuple[0], time.strftime(self.suffix, timeTuple))
        else:
            filename = '%s-%s.%s'%(filenameTuple[0], time.strftime(self.suffix, timeTuple), filenameTuple[len(filenameTuple) - 1])
        dfn = os.path.join(self.root, '%04d-%02d\\%s\\%02d\\%s'%(timeTuple[0], timeTuple[1], filenameTuple[0], timeTuple[2], filename))
        #dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
        if os.path.exists(dfn):
            os.remove(dfn)
#        print 'dfn = %s baseFilename = %s'%(dfn, self.baseFilename)
        #可能由于改系统时间或其他人为原因造成文件找不到
        if os.path.exists(self.baseFilename):
            os.rename(self.baseFilename, dfn)
        if self.backupCount > 0:
            # find the oldest log file and delete it
            #s = glob.glob(self.baseFilename + ".20*")
            #if len(s) > self.backupCount:
            #    s.sort()
            #    os.remove(s[0])
            for s in self.getFilesToDelete():
                os.remove(s)
        currentTime = int(time.time())
        newRolloverAt = self.computeRollover(currentTime)
        while newRolloverAt <= currentTime:
            newRolloverAt = newRolloverAt + self.interval
        #If DST changes and midnight or weekly rollover, adjust for this.
        if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
            dstNow = time.localtime(currentTime)[-1]
            dstAtRollover = time.localtime(newRolloverAt)[-1]
            if dstNow != dstAtRollover:
                if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
                    newRolloverAt = newRolloverAt - 3600
                else:           # DST bows out before next rollover, so we need to add an hour
                    newRolloverAt = newRolloverAt + 3600
        self.rolloverAt = newRolloverAt
        #判断是否需要切换文件夹
        nextRolloverTime = self.rolloverAt
        if self.utc:
            lastRolloverTimeTup = time.gmtime(lastRolloverTime)
            nextRolloverTimeTup = time.gmtime(nextRolloverTime)
        else:
            lastRolloverTimeTup = time.localtime(lastRolloverTime)
            nextRolloverTimeTup = time.localtime(nextRolloverTime)
        lastRolloverYear = lastRolloverTimeTup[0]
        lastRolloverMonth = lastRolloverTimeTup[1]
        lastRolloverDay = lastRolloverTimeTup[2]
        nextRolloverYear = nextRolloverTimeTup[0]
        nextRolloverMonth = nextRolloverTimeTup[1]
        nextRolloverDay = nextRolloverTimeTup[2]
        if lastRolloverYear != nextRolloverYear or lastRolloverMonth != nextRolloverMonth or lastRolloverDay != nextRolloverDay:
            #年或月改变,更换文件夹
            self.baseFilename = self.getBaseFileName(self.root, nextRolloverYear, nextRolloverMonth, nextRolloverDay, self.filename)
#            print 'need to rollove path:%s-%s -> %s-%s'%(lastRolloverYear, lastRolloverMonth,
#                                                         nextRolloverYear, nextRolloverMonth)
            filepath = os.path.dirname(self.baseFilename)
            self.ensurePath(filepath)
        else:
            pass
        self.mode = 'w'
        self.stream = self._open()
def test():
    import logging
    mylogger = logging.getLogger('test')
    mylogger.setLevel(logging.DEBUG)
    hdlr = TimeRotatingPathFileHandler('d:\\ServerLog\\', 'AD.txt')
    fs = '%(asctime)s\t%(levelname)-8s\t%(message)s'
    dfs = '%Y-%m-%dT%H:%M:%S'
    fmt = logging.Formatter(fs, dfs)
    hdlr.setLevel(logging.DEBUG)
    hdlr.setFormatter(fmt)
    mylogger.addHandler(hdlr)
#    mylogger.info('test')
#    os.system('pause')
    exit = False
    cnt = 0
    lastTime = time.time()
    while not exit:
        curTime = time.time()
        if curTime - lastTime >= 60:
            lastTime = curTime
            mylogger.info('test')
if __name__ == "__main__":
    test()
Tool/WebCenter/Coupon/lib/__init__.py
Tool/WebCenter/Coupon/lib/mylog.py
New file
@@ -0,0 +1,158 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#
##@package
#
# @todo:
#
# @author: Alee
# @date 2018-7-18 上午11:40:23
# @version 1.0
#
# @note:
#
#---------------------------------------------------------------------\
import logging
from time import localtime
import TimeRotatingPathFileHandler
import CommFunc
__gLogger = None
def InitLog( LogPath, LogName, printToConsole = True, isDebug = True, mode = 'H' ):
    global __gLogger
    logLevel = logging.INFO
    if isDebug:
        logLevel = logging.DEBUG
    #设置日志的过滤等级
    __gLogger = logging.getLogger(LogName)
    __gLogger.setLevel(logLevel)
    hdlr = TimeRotatingPathFileHandler.TimeRotatingPathFileHandler(LogPath, LogName, mode)
    fs = '%(asctime)s\t%(levelname)-8s\t%(message)s'
    dfs = '%Y-%m-%dT%H:%M:%S'
    fmt = logging.Formatter(fs, dfs)
    hdlr.suffix = "%Y-%m-%d_%H-%M-%S"
    hdlr.setLevel(logLevel)
    hdlr.setFormatter(fmt)
    __gLogger.addHandler(hdlr)
    if printToConsole:
        console = logging.StreamHandler()
        console.setFormatter(fmt)
        __gLogger.addHandler(console)
        console.setLevel(logLevel)
__WARN_COUNT = 0
__ERR_COUNT = 0
__FATAL_COUNT = 0
__LastWarnInfo = ""
__LastWarnTime = ""
__LastErrInfo = ""
__LastErrTime = ""
__LastFatalInfo = ""
__LastFatalTime = ""
def GetLastWarnInfo():
    global __LastWarnInfo
    return __LastWarnInfo
def GetLastWarnTime():
    global __LastWarnTime
    return __LastWarnTime
def GetLastErrInfo():
    global __LastErrInfo
    return __LastErrInfo
def GetLastErrTime():
    global __LastErrTime
    return __LastErrTime
def GetLastFatalInfo():
    global __LastFatalInfo
    return __LastFatalInfo
def GetLastFatalTime():
    global __LastFatalTime
    return __LastFatalTime
def Get_Warn_Count():
    global __WARN_COUNT
    return __WARN_COUNT
def Get_Err_Count():
    global __ERR_COUNT
    return __ERR_COUNT
def Get_Fatal_Count():
    global __FATAL_COUNT
    return __FATAL_COUNT
def debug(msg):
    global __gLogger
    __gLogger.debug(msg)
def info(msg):
    global __gLogger
    __gLogger.info(msg)
def warn(msg):
    global __WARN_COUNT
    global __LastWarnInfo
    global __LastWarnTime
    global __gLogger
    __WARN_COUNT += 1
    __LastWarnInfo = msg
    __LastWarnTime = CommFunc.GetCurrentDataTimeStr()
    __gLogger.warning(msg)
__ErrInfoList = []
__OnErr = None
def OnErr(onErr):
    global __OnErr
    __OnErr = onErr
def error(msg):
    global __ERR_COUNT
    global __LastErrInfo
    global __LastErrTime
    global __gLogger
    global __ErrInfoList
    global __OnErr
    __ERR_COUNT += 1
    __LastErrInfo = msg
    __LastErrTime = CommFunc.GetCurrentDataTimeStr()
    if msg not in __ErrInfoList:
        __ErrInfoList.append(msg)
        if __OnErr:
            errInfo = ""
            for info in __ErrInfoList:
                errInfo += "%s\r\n"%info
            __OnErr(errInfo[:-2])
    __gLogger.error(msg)
def fatal(msg):
    global __FATAL_COUNT
    global __LastFatalInfo
    global __LastFatalTime
    global __gLogger
    __FATAL_COUNT += 1
    __LastFatalInfo = msg
    __LastFatalTime = CommFunc.GetCurrentDataTimeStr()
    __gLogger.critical(msg)
def test():
    import os
    InitLog(os.getcwd(), "AD")
    info('test')
if __name__ == '__main__':
    test()
Tool/WebCenter/Coupon/main.py
New file
@@ -0,0 +1,40 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#
##@package
#
# @todo: 礼包卡中心
#
# @author: Alee
# @date 2018-6-5 下午04:18:14
# @version 1.0
#
#
#需要安装第三方库
#waitress-1.1.0.tar.gz
#requests-2.18.4.tar.gz
#---------------------------------------------------------------------
import webapp
from bottle import run
from lib import mylog
import time
import os
import datetime
from lib import ReadConfig
Def_Debug = ReadConfig.ReadConfig(os.getcwd() + "\\config.ini").GetValue("Coupon", "IsDebug")
#center.secondworld.net.cn
def main():
    time.sleep(1)
    os.system("title Coupon-%s"%datetime.datetime.today())
    mylog.InitLog(r"D:/ServerLog", "Coupon.txt", False, Def_Debug)
    CenterPort = int(ReadConfig.ReadConfig(os.getcwd() + "\\config.ini").GetValue("Coupon", "CenterPort"))
    run(host='0.0.0.0', port=CenterPort, app=webapp.myapp, server='waitress')
    return
if __name__ == '__main__':
    main()
Tool/WebCenter/Coupon/webapp.py
New file
@@ -0,0 +1,496 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
##@package
#
# @todo: 激活码
#
# @author: Alee
# @date 2018-5-4 22:10:51
# @version 1.0
#
# @note: 中文必须传utf8,收到的为urlencode内容  .decode('gbk').encode('utf8')
# 1.生成礼包批次
# 2.查询礼包批次
# 3.删除礼包批次
# 4.下载礼包批次
# 5.查询礼包卡号/使用礼包卡号
#---------------------------------------------------------------------
from bottle import Bottle, request, static_file
import random
import time
import md5
import CouponDB
import datetime
from lib import ReadConfig
import json
import urllib2
import urllib
from lib import mylog, CommFunc
import os.path
import zipfile
# get: request.query.username request.GET.get('username','')
# post: request.forms.get('username')  request.POST.get('username')
#===============================================================================
# @myapp.route('/cool/kk/:name3/:count#\\d+#')
# def maybe(name3, count):
# client_ip = request.environ.get('REMOTE_ADDR')
# client_ip = request.environ.get('HTTP_X_FORWARDED_FOR') or request.environ.get('REMOTE_ADDR')
#===============================================================================
ConfigIO = ReadConfig.ReadConfig(os.getcwd() + "\\config.ini")
# 应用端口
CenterPort = ConfigIO.GetValue("Coupon", "CenterPort")
#mobiletest.173on.com:55000
Def_ExportUrl = ConfigIO.GetValue("Coupon", "exporturl") + ":" + CenterPort + "/Coupon/CouponLoad.php?couponid=%s&channel=%s"
# GM端口
PushPort = ConfigIO.GetValue("Coupon", "PushPort")
PushKey = ConfigIO.GetValue("Coupon", "PushKey")
CommonCards = eval(ConfigIO.GetValue("Coupon", "CommonCards"))
Def_CardMsg = {
               0:"CodeRewardSys2", #  兑换码无效
               1: "CodeRewardSys1", #   兑换卡奖励已发至邮件
               2:"CodeRewardSys3", #  兑换码已使用过
               3:"CodeRewardSys4",  # 同类型兑换码只能使用一次
         }
myapp = Bottle()
#申请礼包卡批次
@myapp.route('/Coupon/CouponBatch.php', method='POST')
def CouponBatch():
    dataDict = request.POST
    for key, value in dataDict.items():
        mylog.debug("key:%s  value:%s"%(key, value))
    appid = dataDict.get("channel", "")     # 运营提供的APPID,即渠道
    if not appid:
        mylog.debug("no appid")
        return json.dumps({"error":"param appid"}, ensure_ascii=False)
    couponType = dataDict.get("coupontype", "")    # 礼包卡类型
    if not couponType:
        mylog.debug("no couponType")
        return json.dumps({"error":"param couponType"}, ensure_ascii=False)
    if not ConfigIO.HasSection(couponType):
        return json.dumps({"error":"no couponType"}, ensure_ascii=False)
    minSNo = CommFunc.ToIntDef(dataDict.get("minserverno", 0), 0)   # 若只选中一个则代表只有单服使用
    maxSNo = CommFunc.ToIntDef(dataDict.get("maxserverno", 0), 0)
    amount = int(dataDict.get("amount", 0))     # 礼包数量
    expireTime = dataDict.get("expiretime", "") # 过期时间
    # 不可重复即可  生成卡号批次标志,随机
    coupon_id = md5.md5("%s%s%s%s"%(appid, random.randint(1, 10000), time.time(), couponType)).hexdigest()
    dbController = CouponDB.GetDBEventCon()
    if not dbController:
        # 无法获取数据库
        mylog.debug("no dbController")
        return json.dumps({"error":"db"}, ensure_ascii=False)
    tmpDict = {}
    #新增
    tmpDict['createtime'] = str(datetime.datetime.today()).split(".")[0]   #创建时间
    tmpDict['exportUrl'] = Def_ExportUrl%(coupon_id, appid)  # 卡批次下载地址 提供给运营商获取
    tmpDict['couponid'] = coupon_id  # 卡批次,用于控制这一批次卡的使用权限和下载依据
    # 若只选中一个则代表只有单服使用,0则不限制使用范围
    if minSNo == 0 or maxSNo == 0:
        minSNo = max(minSNo, maxSNo)
        maxSNo = minSNo
    tmpDict["minserverno"] = minSNo  #  使用区服限制
    tmpDict["maxserverno"] = maxSNo
    tmpDict["amount"] = amount  #此批次卡的总数量
    tmpDict["coupontype"] = couponType  # 卡类型 开头字母做标识如 g代表此卡只能使用一次 h卡可以重复使用
    tmpDict["expiretime"] = expireTime  # 结束时间 ""空为无限制,具体时间格式为2018-06-07
    tmpDict["channel"] = appid
    tmpDict["status"] = 0   # 0正常 1不可用
    # 插入表1批次清单,表2为具体卡号
    result = dbController.insert(CouponDB.CouponBatchColName, tmpDict)
    if not result:
        mylog.debug("insert error-%s"%(dbController.lastError))
        mylog.debug("insert error-%s-%s"%(coupon_id, tmpDict))
        return json.dumps({"error":"insert couponid"}, ensure_ascii=False)
    #===========================================================================
    # { "_id" : ObjectId("5b1a3e474eb1001100f7ad55"), "status" : 0, "code" : "m069cafd
    # 8ea1b30ajn", "coupontype" : "3", "couponid" : "0300229afaa52bbce59e7e6c104f307
    # e", "createtime" : "2018-06-08 16:28:55", "channel" : "asd" }
    #===========================================================================
    #批量插入卡号,暂且一次性插入十万
    tmpCodeList = []
    for i in xrange(amount):
        tmpCodeDoc = {}
        # 前3位为类型标识
        tmpCodeDoc['code'] = tmpDict["coupontype"][0] + md5.md5(tmpDict['channel'] + str(tmpDict['createtime']) + str(i) + \
                                     str(random.randint(1000, 2000000))).hexdigest()[2:14] + \
                                     random.choice('1234567890abcdefghijklmnopqrstuvwxyz') +\
                                     random.choice('1234567890abcdefghijklmnopqrstuvwxyz')
        tmpCodeDoc['coupontype'] = tmpDict['coupontype']
        tmpCodeDoc['couponid'] = tmpDict['couponid']
        tmpCodeDoc['channel'] = tmpDict['channel']
        tmpCodeDoc['status'] = 0
        tmpCodeDoc['usetime'] = ""
        tmpCodeDoc['accid'] = ""
        tmpCodeList.append(tmpCodeDoc)
    result = dbController.insert(CouponDB.CouponCodeColName + "_" + tmpDict['channel'], tmpCodeList)
    if not result:
        return json.dumps({"error":"insert card"}, ensure_ascii=False)
    if "_id" in tmpDict:
        del tmpDict["_id"]
    mylog.debug("插入成功%s"%json.dumps(tmpDict, ensure_ascii=False))
    return json.dumps(tmpDict, ensure_ascii=False)
# 查询此平台礼包批次或者指定礼包批次查询
@myapp.route('/Coupon/CouponQuery.php')
def QueryCouponBatch():
    dataDict = request.GET
    appid = dataDict.get("channel", "")     # 运营提供的APPID,即渠道
    if not appid:
        mylog.debug("no appid")
        return json.dumps({"error":"param channel"}, ensure_ascii=False)
    couponid = dataDict.get("couponid", "")
    # 单查
    dbController = CouponDB.GetDBEventCon()
    if not dbController:
        # 无法获取数据库
        mylog.debug("no dbController")
        return json.dumps({"error":"db"}, ensure_ascii=False)
    if couponid:
        result, findList = dbController.find(CouponDB.CouponBatchColName, {'channel':appid, 'couponid':couponid})
    else:
        result, findList = dbController.find(CouponDB.CouponBatchColName, {'channel':appid})
    if not findList:
        return json.dumps({"error":"no couponid"}, ensure_ascii=False)
    return json.dumps(findList, ensure_ascii=False)
# 更新指定礼包批次 0:正常  其他异常
@myapp.route('/Coupon/CouponUpdate.php')
def UpdateCouponBatch():
    dataDict = request.GET
    appid = dataDict.get("channel", "")     # 运营提供的APPID,即渠道
    if not appid:
        mylog.debug("no appid")
        return json.dumps({"error":"param appid"}, ensure_ascii=False)
    couponid = dataDict.get("couponid", "")
    if not couponid:
        return json.dumps({"error":"param couponid"}, ensure_ascii=False)
    status = CommFunc.ToIntDef(dataDict.get("status", 0), 0)
    dbController = CouponDB.GetDBEventCon()
    if not dbController:
        # 无法获取数据库
        mylog.debug("no dbController")
        return json.dumps({"error":"db"}, ensure_ascii=False)
    result, data = dbController.find_one(CouponDB.CouponBatchColName, {'channel':appid, 'couponid':couponid})
    if not data:
        return json.dumps({"error":"no couponid"}, ensure_ascii=False)
    data['status'] = status
    dbController.update(CouponDB.CouponBatchColName, {'channel':appid, 'couponid':couponid}, data)
    return json.dumps({"success":couponid}, ensure_ascii=False)
#/api/Coupon/index.php?couponBatchId=%s&agentName=%s"
# 下载指定礼包批次
@myapp.route('/Coupon/CouponLoad.php')
def LoadCouponBatch():
    dataDict = request.GET
    appid = dataDict.get("channel", "")     # 运营提供的APPID,即渠道
    if not appid:
        mylog.debug("no appid")
        return json.dumps({"error":"param appid"}, ensure_ascii=False)
    couponid = dataDict.get("couponid", "")
    if not couponid:
        return json.dumps({"error":"param couponid"}, ensure_ascii=False)
    downFile = r".\download\%s.zip"%couponid
    if os.path.isfile(downFile):
        return static_file("%s.zip"%couponid, r".\download", download=True)
    dbController = CouponDB.GetDBEventCon()
    if not dbController:
        # 无法获取数据库
        mylog.debug("no dbController")
        return json.dumps({"error":"db"}, ensure_ascii=False)
    result, data = dbController.find(CouponDB.CouponCodeColName + "_" + appid, {'channel':appid, 'couponid':couponid})
    if not data:
        return json.dumps({"error":"no couponid"}, ensure_ascii=False)
    fileIO = open(r".\download\%s.txt"%couponid, 'w')
    for codeInfo in data:
        fileIO.write(codeInfo["code"] + "\n")
    fileIO.close()
    f = zipfile.ZipFile(downFile, 'w', zipfile.ZIP_DEFLATED)
    f.write(r".\download\%s.txt"%couponid)
    f.close()
    return static_file("%s.zip"%couponid, r".\download", download=True)
# 查询卡使用情况
@myapp.route('/Coupon/QueryCouponCode.php')
def QueryCouponCode():
    dataDict = request.GET
    appid = dataDict.get("channel", "")     # 运营提供的APPID,即渠道
    if not appid:
        mylog.debug("no appid")
        return json.dumps({"error":"param appid"}, ensure_ascii=False)
    codeStr = dataDict.get("code", "")
    if not codeStr:
        return json.dumps({"error":"param code"}, ensure_ascii=False)
    dbController = CouponDB.GetDBEventCon()
    if not dbController:
        # 无法获取数据库
        mylog.debug("no dbController")
        return json.dumps({"error":"db"}, ensure_ascii=False)
    result, data = dbController.find(CouponDB.CouponCodeColName + "_" + appid, {'channel':appid, 'code':codeStr})
    if not data:
        return json.dumps({"error":"no code"}, ensure_ascii=False)
    return json.dumps(data, ensure_ascii=False)
# 卡类型 开头字母做标识如 g1~999代表此卡只能使用一次 h1~999卡可以重复使用 z开头代表固定码
# 玩家使用卡号 参数 平台 区服 账号 区服链接(GM用)卡号
# http://center.xxx.com:53003/Coupon/CouponCode.php?channel=appid&code=XXX&accid=YYY&sid=222&pushurl=s222.xxx.com
@myapp.route('/Coupon/CouponCode.php')
def CouponCode():
    dataDict = request.GET
    for key, value in dataDict.items():
        mylog.debug("CouponCode key:%s  value:%s"%(key, value))
    agentName = dataDict.get("channel", "")     # 运营提供的APPID,即渠道
    if not agentName:
        mylog.debug("no appid")
        return
    codeStr = dataDict.get("code", "")
    if not codeStr:
        return
    accid = dataDict.get("accid", "")
    if not accid:
        return
    sid = CommFunc.ToIntDef(dataDict.get("sid", 0), 0)
    if not sid:
        return
    pushurl = dataDict.get("pushurl", "")
    if not pushurl:
        return
    # 转化为游戏账号
    accid = "%s@%s@s%s"%(accid, agentName, sid)
    dbController = CouponDB.GetDBEventCon()
    if not dbController:
        # 无法获取数据库
        mylog.debug("no dbController")
        return
    #通过GM接口告知玩家使用情况
    gmresult = {'accID':accid, 'sid':sid, 'channel':agentName}
    #-----------统一固定码处理----------------------
    if codeStr in CommonCards:
        # 不同固定码各自只能领一次
        result, commondata = dbController.find_one(CouponDB.CouponCodeColName + "_Common", {"code":codeStr, "accid":accid})
        if commondata:
            # 已使用
            gmresult['status'] = 2
            mylog.debug("common status=2")
            return SendGm(gmresult, pushurl)
        gmresult['status'] = 1
        gmresult['code'] = codeStr
        gmresult['coupontype'] = codeStr  # 统一格式,服务器不一定有用,固定码根据 codeStr 发奖励
        dbController.insert(CouponDB.CouponCodeColName + "_Common", {"code":codeStr, "accid":accid})
        mylog.debug("common ok")
        return SendGm(gmresult, pushurl)
    result, data = dbController.find_one(CouponDB.CouponCodeColName + "_" + agentName, {"code":codeStr, "channel":agentName})
    if not data:
        #无此卡
        gmresult['status'] = 0
        mylog.debug("no card")
        return SendGm(gmresult, pushurl)
    if int(data['status']) == 1:
        #已使用,同卡号记录默认可用,避免断线发送失败的情况(未返回结果验证情况下)
        gmresult['status'] = 2
        mylog.debug("card used!")
        return SendGm(gmresult, pushurl)
    result, data2 = dbController.find_one(CouponDB.CouponBatchColName, {"couponid":data["couponid"]})
    if not data2:
        #此批次卡已删除
        gmresult['status'] = 0
        mylog.debug("no couponid")
        return SendGm(gmresult, pushurl)
    if int(data2['status']) == 1:
        #暂停使用该批次卡
        gmresult['status'] = 0
        mylog.debug("couponid frozed")
        return SendGm(gmresult, pushurl)
    if (data2["minserverno"] != 0 and data2["maxserverno"] != 0):
        if int(data2["minserverno"]) > sid or int(data2["maxserverno"]) < sid:
            #不在指定区
            gmresult['status'] = 0
            mylog.debug("no sid")
            return SendGm(gmresult, pushurl)
    if data2["expiretime"] != "" and datetime.datetime.today() > GetDateTimeByStr(data2["expiretime"]):
        #已过期
        gmresult['status'] = 0
        mylog.debug("time pass")
        return SendGm(gmresult, pushurl)
    #再查一次是否用过此批次的其他卡,
    result, data3 = dbController.find_one(CouponDB.CouponCodeColName + "_" + agentName, \
                                          {"couponid":data["couponid"], "accid":accid})
    if data3:
        #用过同类卡,已记录过不再做新记录
        gmresult['status'] = 3
        gmresult['coupontype'] = data["coupontype"]
        gmresult['code'] = codeStr
        mylog.debug("use again")
        return SendGm(gmresult, pushurl)
    data['status'] = 1
    data['accid'] = accid
    data['usetime'] = str(datetime.datetime.today())
    dbController.update(CouponDB.CouponCodeColName + "_" + agentName, {"code":codeStr, "channel":agentName}, data)
    # 0 不可用,1.可用发放奖励 ,2.已使用 3.同类型卡不能使用两次
    gmresult['status'] = 1
    gmresult['code'] = codeStr
    gmresult['coupontype'] = data["coupontype"]
    mylog.debug("card ok!")
    return SendGm(gmresult, pushurl)
def SendGm(gmresult, pushurl):
    try:
        # GM推送地址
        #GMToolPage = http://s1.yhlz.09ge.com:30001/Server/Tool.php
        gmurl = "http://%s:%s/Server/Tool.php"%(pushurl, PushPort)
        gmkey = PushKey
        if not gmkey or not gmurl:
            return
        useStatus = gmresult['status']
        gmresult['notifyMsg'] = Def_CardMsg.get(useStatus, "CodeRewardSys2")
        if useStatus != 1:
            #失败系统提示
            pack_data = {};
            pack_data["queryType"] = "accID"
            pack_data["playerFind"] = gmresult["accID"]
            pack_data["pack_type"] = "GMT_MediaCard"
            pack_data["notifyMsg"] = gmresult['notifyMsg']
            pack_data["key"] = gmkey;
            pack_data['coding'] = "utf8";
            #使用key加密
            pack_data_dict = json.dumps(pack_data)
            sign = md5.md5(pack_data_dict+gmkey).hexdigest()
            post = {}
            post['pack'] = pack_data_dict;
            post['sign'] = sign;
            urllib2.urlopen(gmurl, urllib.urlencode(post), 3)
            return
        mylog.debug("SendGm:%s"%gmurl)
        pack_data = {};
        pack_data["queryType"] = "accID"
        pack_data["playerList"] = gmresult["accID"]
        pack_data["Title"] = ConfigIO.GetValue(gmresult["coupontype"], "MailTitle").decode("gbk")
        pack_data["Text"] = ConfigIO.GetValue(gmresult["coupontype"], "MailText").decode("gbk")
        pack_data["EndTime"] = str(datetime.datetime.today() + datetime.timedelta(days=15)).split('.')[0]
        pack_data["Sender"] = ConfigIO.GetValue(gmresult["coupontype"], "MailSender").decode("gbk")
        Items =  eval(ConfigIO.GetValue(gmresult["coupontype"], "Items"))
        pack_data["itemNums"] = ','.join([str(i) for i in range(len(Items))])
        i = 0
        for itemList in Items:
            pack_data["ItemID%s"%i] = str(itemList[0])
            pack_data["ItemCnt%s"%i] = str(itemList[1])
            pack_data["IsBind%s"%i] = '1'
            i += 1
        pack_data["pack_type"] = "GMT_AddPersonalCompensation"
        pack_data["Detail"] = "cardCode:" + gmresult["code"]
        pack_data["key"] = gmkey;
        pack_data['coding'] = "utf8";
        #使用key加密
        pack_data_dict = json.dumps(pack_data)
        sign = md5.md5(pack_data_dict+gmkey).hexdigest()
        post = {}
        post['pack'] = pack_data_dict;
        post['sign'] = sign;
        result = urllib2.urlopen(gmurl, urllib.urlencode(post), 10)
        content = result.read()
        result.close()
        # 成功提示
        pack_data = {};
        pack_data["queryType"] = "accID"
        pack_data["playerFind"] = gmresult["accID"]
        pack_data["pack_type"] = "GMT_MediaCard"
        pack_data["notifyMsg"] = gmresult['notifyMsg']
        pack_data["key"] = gmkey;
        pack_data['coding'] = "utf8";
        #使用key加密
        pack_data_dict = json.dumps(pack_data)
        sign = md5.md5(pack_data_dict+gmkey).hexdigest()
        post = {}
        post['pack'] = pack_data_dict;
        post['sign'] = sign;
        urllib2.urlopen(gmurl, urllib.urlencode(post), 3)
        return content
    except Exception, e:
        mylog.debug("gm error %s"%e)
    return
def GetDateTimeByStr(timeStr, timeFomat="%Y-%m-%d"):
    try:
        return datetime.datetime.strptime(timeStr, timeFomat)
    except:
        mylog.debug("GetDateTimeByStr error %s"%timeStr)
    return