From 3d658259b25c4914c766c43aeea883bdd0847c5d Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期一, 04 三月 2019 16:39:42 +0800
Subject: [PATCH] 6250 【后端】【2.0】拍卖行开发单

---
 ServerPython/CoreServerGroup/GameServer/PyNetPack.ini                                                           |   28 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_AuctionHouse.py |   51 +
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py                                            |   11 
 ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py                                               |    8 
 ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py                                                 |  234 +++++-
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAuctionHouse.py                |  198 +++++
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py                                           |    4 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py                               |    6 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini                                      |   16 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/ItemCommon.py                  |    3 
 ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py                                                      |   35 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/AuctionItem.py                  |  110 +++
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/AuctionHouse.py                                   | 1373 +++++++++++++++++++++++++++++++++++++-
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                                 |   20 
 14 files changed, 1,976 insertions(+), 121 deletions(-)

diff --git a/ServerPython/CoreServerGroup/GameServer/PyNetPack.ini b/ServerPython/CoreServerGroup/GameServer/PyNetPack.ini
index 1a5332b..aaf50ba 100644
--- a/ServerPython/CoreServerGroup/GameServer/PyNetPack.ini
+++ b/ServerPython/CoreServerGroup/GameServer/PyNetPack.ini
@@ -221,6 +221,34 @@
 PacketSubCMD_1=0x03
 PacketCallFunc_1=OnPYQueryBourseItemOnSale
 
+;拍卖行
+[AuctionHouse]
+ScriptName = GameWorldLogic\AuctionHouse.py
+Writer = hxp
+Releaser = hxp
+RegType = 0
+RegisterPackCount = 5
+
+PacketCMD_1=0xB5
+PacketSubCMD_1=0x10
+PacketCallFunc_1=OnQueryAuctionItem
+
+PacketCMD_2=0xB5
+PacketSubCMD_2=0x17
+PacketCallFunc_2=OnQueryTagAuctionItem
+
+PacketCMD_3=0xB5
+PacketSubCMD_3=0x12
+PacketCallFunc_3=OnQueryAuctionRecord
+
+PacketCMD_4=0xB5
+PacketSubCMD_4=0x16
+PacketCallFunc_4=OnQueryAttentionAuctionItem
+
+PacketCMD_5=0xB5
+PacketSubCMD_5=0x18
+PacketCallFunc_5=OnAttentionAuctionItemChange
+
 ;境界渡劫
 [PlayerDuJie.py]
 ScriptName = Player\PlayerDuJie.py
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
index 090fc4d..76a086e 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
@@ -795,3 +795,38 @@
 VIPPrivilege_XianyuanCoinUpperAdd,    #33 仙缘币上限加成
 VIPPrivilege_XianyuanCoinAddPer,    #34 仙缘币获得倍率加成(万分比)
 ) = range(1, 35)
+
+Def_MailMoneySource = "MoneySource" # 邮件货币来源key
+
+#游戏货币来源类型定义
+(
+Def_GiveMoney_Unknown, # 未知 0
+Def_GiveMoney_GM,
+Def_GiveMoney_CTG,
+Def_GiveMoney_GMTCTG,
+Def_GiveMoney_CoinToGold,
+Def_GiveMoney_SuperAccountCreate, # 创角赠送 5
+Def_GiveMoney_Mission, # 任务
+Def_GiveMoney_Pray, # 祈祷
+Def_GiveMoney_UseItem, # 使用物品
+Def_GiveMoney_Pickup, # 拾取
+Def_GiveMoney_Bourse, # 交易所 10
+Def_GiveMoney_GoldInvest, # 绑钻投资
+Def_GiveMoney_Recover, # 资源找回
+Def_GiveMoney_RedPacket, # 红包
+Def_GiveMoney_RefineGift, # 炼制奖励
+Def_GiveMoney_Mail, # 邮件(补偿) 15
+Def_GiveMoney_RuneDecompose, # 符印
+Def_GiveMoney_Warehouse, # 仓库
+Def_GiveMoney_SellPackItem, # 出售背包物品
+Def_GiveMoney_CollectNPC, # 采集NPC
+Def_GiveMoney_HighLadder, # 天梯竞技场 20
+Def_GiveMoney_StallItem, # 摆摊
+Def_GiveMoney_Trade, # 交易
+Def_GiveMoney_Truck, # 运镖
+Def_GiveMoney_FreeGoods, # 极品白拿 
+Def_GiveMoney_BindJadeWheel, # 绑玉转盘 25
+Def_GiveMoney_GatherSoulDecompose, #聚魂分解 26
+Def_GiveMoney_AuctionBidReturn, #拍卖竞价返还
+Def_GiveMoney_AuctionGain, #拍卖获得利润
+) = range(1000, 1000 + 29)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/AuctionHouse.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/AuctionHouse.py
index 16d3b83..bdc0374 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/AuctionHouse.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/AuctionHouse.py
@@ -6,91 +6,1334 @@
 #
 # @todo:拍卖行
 # @author hxp
-# @date 2019-02-19
+# @date 2019-03-04
 # @version 1.0
 #
 # 详细描述: 拍卖行
 #
 #-------------------------------------------------------------------------------
-#"""Version = 2019-02-19 12:00"""
+#"""Version = 2019-03-04 17:00"""
 #-------------------------------------------------------------------------------
-import CommFunc
 import GameWorld
+import PyDataManager
+import PlayerControl
+import DataRecordPack
+import PyGameDataStruct
+import PlayerCompensation
+import ChPyNetSendPack
+import IpyGameDataPY
+import NetPackCommon
+import PlayerBourse
+import PlayerFamily
+import ChConfig
 
+import operator
+import time
+import math
+import json
 
-#拍卖关注管理,注意该类只处理数据逻辑,功能相关逻辑不要写在该类,不然重读脚本不会生效
-class AuctionAttentionManager(object):
-    
-    def __init__(self):
+# 拍卖记录类型定义
+AuctionRecordTypeList = (
+AuctionRecordType_MyAuction, # 我的拍品
+AuctionRecordType_FamilyAuction, # 仙盟拍品
+AuctionRecordType_MyBid, # 我的竞拍
+) = range(3)
+
+# 拍卖记录结果定义
+AuctionRecordResultList = (
+AuctionRecordResult_SellFail, # 流拍
+AuctionRecordResult_SellOK, # 拍卖成交
+AuctionRecordResult_Recycle, # 系统回收
+AuctionRecordResult_BidOK, # 竞价成功
+AuctionRecordResult_BidFail, # 竞价失败
+AuctionRecordResult_MoveToWorld, # 仙盟拍品转移到全服拍品
+) = range(6)
+
+# 当前拍品归类 0-全服拍品 1-仙盟私有拍品
+AuctionType_World = 0
+AuctionType_Family = 1
+
+#拍卖行状态开关
+def GetAuctionHouseState(): return PlayerBourse.GetOpenState()
+
+def OnPlayerLogin(curPlayer):
+    Sync_PlayerAuctionItemInfo(curPlayer)
+    Sync_FamilyAuctionItemInfo(curPlayer, curPlayer.GetFamilyID())
+    Sync_PlayerBiddingItemInfo(curPlayer)
+    Sync_PlayerAttentionAuctionItemID(curPlayer, False)
+    return
+
+def OnPlayerLeaveServer(curPlayer):
+    playerID = curPlayer.GetPlayerID()
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    auctionItemMgr.myAttentionItemDict.pop(playerID, None)
+    return
+
+##--------------------------------------- 拍卖记录 -------------------------------------------------
+def GetMyAuctionItemRecord(playerID):
+    ## 获取我的拍品记录
+    return PyDataManager.GetAuctionRecordManager().myAuctionItemRecordDict.get(playerID, [])
+
+def GetMyBiddingItemRecord(playerID):
+    ## 获取我的竞拍记录
+    return PyDataManager.GetAuctionRecordManager().myBidItemRecordDict.get(playerID, [])
+
+def GetFamilyAuctionItemRecord(familyID):
+    ## 获取仙盟拍品记录
+    return PyDataManager.GetAuctionRecordManager().familyAuctionItemRecordDict.get(familyID, [])
+
+def AddAuctionRecord(auctionItem, recordResult):
+    ''' 添加拍卖行记录
+    @param auctionItem: 相关的拍品实例
+    @param recordResult: 记录结果 0-流拍 1-拍卖成交 2-回收 3-竞价成功 4-竞价失败
+    '''
+    if not auctionItem:
         return
     
-    ## ===========================================================================================
+    playerID, familyID, bidderID = auctionItem.PlayerID, auctionItem.FamilyID, auctionItem.BidderID
+    recordPlayerID, recordFamilyID = 0, 0
     
-    # 保存数据 存数据库和realtimebackup
-    def GetSaveData(self):
-        savaData = ""
-        cntData = ""
-        cnt = 0
-        
-        GameWorld.Log("Save AuctionAttention count :%s" % cnt)
-        return CommFunc.WriteDWORD(cntData, cnt) + savaData
-    
-    # 从数据库载入数据
-    def LoadPyGameData(self, datas, pos, dataslen):
-        cnt, pos = CommFunc.ReadDWORD(datas, pos)
-        GameWorld.Log("Load AuctionAttention count :%s" % cnt)
-        
-        return pos
-
-
-#拍卖记录管理,注意该类只处理数据逻辑,功能相关逻辑不要写在该类,不然重读脚本不会生效
-class AuctionRecordManager(object):
-    
-    def __init__(self):
+    if recordResult in [AuctionRecordResult_BidOK, AuctionRecordResult_BidFail]:
+        recordType = AuctionRecordType_MyBid
+        recordPlayerID = bidderID
+    else:
+        if familyID:
+            recordType = AuctionRecordType_FamilyAuction
+            recordFamilyID = familyID
+        else:
+            recordType = AuctionRecordType_MyAuction
+            recordPlayerID = playerID
+            
+    if not recordPlayerID and not recordFamilyID:
         return
     
-    ## ===========================================================================================
+    newRecordData = PyGameDataStruct.tagDBAuctionRecord()
+    newRecordData.ItemGUID = auctionItem.ItemGUID
+    newRecordData.PlayerID = recordPlayerID
+    newRecordData.FamilyID = recordFamilyID
+    newRecordData.RecordType = recordType
+    newRecordData.RecordResult = recordResult
+    newRecordData.RecordTime = GameWorld.GetCurrentDataTimeStr()
+    newRecordData.BidderPrice = auctionItem.BidderPrice
+    newRecordData.BidderName = auctionItem.BidderName
+    newRecordData.ItemID = auctionItem.ItemID
+    newRecordData.Count = auctionItem.Count
+    newRecordData.UserData = auctionItem.UserData
+    newRecordData.UserDataLen = auctionItem.UserDataLen
+    AddNewAuctionRecord(newRecordData)
     
-    # 保存数据 存数据库和realtimebackup
-    def GetSaveData(self):
-        savaData = ""
-        cntData = ""
-        cnt = 0
-        
-        GameWorld.Log("Save AuctionRecord count :%s" % cnt)
-        return CommFunc.WriteDWORD(cntData, cnt) + savaData
-    
-    # 从数据库载入数据
-    def LoadPyGameData(self, datas, pos, dataslen):
-        cnt, pos = CommFunc.ReadDWORD(datas, pos)
-        GameWorld.Log("Load AuctionRecord count :%s" % cnt)
-        
-        return pos
+    if recordFamilyID:
+        Sync_PlayerAuctionRecordInfo(None, recordType, newRecordData, recordFamilyID)
+    elif recordPlayerID:
+        recordPlayer = GameWorld.GetPlayerManager().FindPlayerByID(recordPlayerID)
+        if recordPlayer and not PlayerControl.GetIsTJG(recordPlayer):
+            Sync_PlayerAuctionRecordInfo(recordPlayer, recordType, newRecordData)
+    return
 
+def AddNewAuctionRecord(newRecordData):
+    playerID = newRecordData.PlayerID
+    familyID = newRecordData.FamilyID
+    recordType = newRecordData.RecordType
+    if recordType == AuctionRecordType_MyAuction:
+        keyID = playerID
+        recordDict = PyDataManager.GetAuctionRecordManager().myAuctionItemRecordDict
+    elif recordType == AuctionRecordType_FamilyAuction:
+        keyID = familyID
+        recordDict = PyDataManager.GetAuctionRecordManager().familyAuctionItemRecordDict
+    elif recordType == AuctionRecordType_MyBid:
+        keyID = playerID
+        recordDict = PyDataManager.GetAuctionRecordManager().myBidItemRecordDict
+    else:
+        return
+    recordList = recordDict.get(keyID, [])
+    maxCount = min(50, IpyGameDataPY.GetFuncCfg("AuctionHouse", 3))
+    if len(recordList) >= maxCount:
+        del recordList[0]
+    recordList.append(newRecordData)
+    recordDict[keyID] = recordList
+    return
+##-------------------------------------- 拍卖关注 ------------------------------------------------
 
-#拍卖物品管理,注意该类只处理数据逻辑,功能相关逻辑不要写在该类,不然重读脚本不会生效
-class AuctionItemManager(object):
+def GetPlayerAuctionAttention(attentionMgr, playerID):
+    ## 获取玩家关注的物品ID列表
+    if playerID not in attentionMgr.playerAttentionDict:
+        return []
+    attentionObj = attentionMgr.playerAttentionDict[playerID]
+    attentionItemIDList = attentionObj.AttentionItemIDList
+    return attentionItemIDList
+
+def AddPlayerAuctionAttention(attentionMgr, playerID, itemID):
+    ## 添加玩家关注的物品ID
+    if playerID not in attentionMgr.playerAttentionDict:
+        dbData = PyGameDataStruct.tagDBAuctionAttention()
+        dbData.PlayerID = playerID
+        __InitAuctionAttentionAttrEx(dbData)
+        attentionMgr.playerAttentionDict[playerID] = dbData
+    attentionObj = attentionMgr.playerAttentionDict[playerID]
+    attentionItemIDList = attentionObj.AttentionItemIDList
+    if len(attentionItemIDList) >= IpyGameDataPY.GetFuncCfg("AuctionHouse", 5):
+        GameWorld.DebugLog("关注拍品数达到上限!", playerID)
+        return
+    if itemID in attentionItemIDList:
+        return True
+    attentionItemIDList.append(itemID)
+    attentionObj.AttentionInfo = str(attentionItemIDList)
+    attentionObj.AttentionLen = len(attentionObj.AttentionInfo)
+    return True
+
+def DelPlayerAuctionAttention(attentionMgr, playerID, itemID):
+    ## 删除玩家关注的物品ID
+    if playerID not in attentionMgr.playerAttentionDict:
+        return
+    attentionObj = attentionMgr.playerAttentionDict[playerID]
+    attentionItemIDList = attentionObj.AttentionItemIDList
+    if itemID not in attentionItemIDList:
+        return
+    attentionItemIDList.remove(itemID)
+    attentionObj.AttentionInfo = str(attentionItemIDList)
+    attentionObj.AttentionLen = len(attentionObj.AttentionInfo)
+    return True
+
+def OnLoadAuctionAttentionDataEx(attentionData):
+    ## 加载拍卖关注表时附加处理
+    __InitAuctionAttentionAttrEx(attentionData)
+    return
+
+def __InitAuctionAttentionAttrEx(attentionData):
+    ## 初始化拍卖关注实例附加属性
+    setattr(attentionData, "AttentionItemIDList", [] if not attentionData.AttentionInfo else json.loads(attentionData.AttentionInfo))
+    return
+
+##-------------------------------------------------------------------------------------------------
+
+def OnLoadAuctionItemDataEx(dbData):
+    ## 加载拍卖物品表时附加处理
+    __InitAuctionItemAttrEx(dbData)
+    playerID = dbData.PlayerID
+    familyID = dbData.FamilyID
+    bidderID = dbData.BidderID
     
-    def __init__(self):
+    pyAuctionItemMgr = PyDataManager.GetAuctionItemManager()
+    pyAuctionItemMgr.allAuctionItemByEndTimeList.append(dbData)
+    
+    if dbData.AuctionType == AuctionType_World:
+        pyAuctionItemMgr.worldAuctionItemList.append(dbData)
+        
+    if familyID:
+        familyItemList = pyAuctionItemMgr.familyAuctionItemDict.get(familyID, [])
+        familyItemList.append(dbData)
+        pyAuctionItemMgr.familyAuctionItemDict[familyID] = familyItemList
+        
+    if playerID:
+        myItemList = pyAuctionItemMgr.myAuctionItemDict.get(playerID, [])
+        myItemList.append(dbData)
+        pyAuctionItemMgr.myAuctionItemDict[playerID] = myItemList
+        
+        if str(playerID) in dbData.BidderIDInfo:
+            if playerID == bidderID:
+                nowBidItemList = pyAuctionItemMgr.nowBiddingAuctionItemDict.get(playerID, [])
+                nowBidItemList.append(dbData)
+                pyAuctionItemMgr.nowBiddingAuctionItemDict[playerID] = nowBidItemList
+            else:
+                hisBidItemList = pyAuctionItemMgr.hisBiddingAuctionItemDict.get(playerID, [])
+                hisBidItemList.append(dbData)
+                pyAuctionItemMgr.hisBiddingAuctionItemDict[playerID] = hisBidItemList
+                
+    return
+
+def OnLoadAuctionItemDataOK():
+    ## 加载拍卖物品表完成后的处理
+    __SortAuctionitem()
+    return
+
+def __InitAuctionItemAttrEx(auctionItem, ipyData=None):
+    ## 初始化拍品实例附加属性
+    if not ipyData:
+        ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", auctionItem.ItemID)
+    setattr(auctionItem, "Sortpriority", 99999 - (0 if not ipyData else ipyData.GetSortpriority())) # 排序优先级归组
+    setattr(auctionItem, "BiddingQueryID", 0) # 当前正在竞价的玩家ID
+    setattr(auctionItem, "BiddingQueryTick", 0) # 当前正在竞价的tick
+    setattr(auctionItem, "EndTime", 0) # 结束竞价time值
+    
+    __SetAuctionItemEndTime(auctionItem, ipyData)
+    return True
+
+def __SetAuctionItemEndTime(auctionItem, ipyData):
+    ## 更新拍品结束竞价time值
+    if not ipyData:
+        return
+    addTimeStr = auctionItem.AddTime
+    auctionType = auctionItem.AuctionType
+    addTime = GameWorld.ChangeTimeStrToNum(addTimeStr)
+    noticeMinutes = ipyData.GetNoticeSaleMinutes()
+    saleMinutes = ipyData.GetFamilySaleMinutes() if auctionType == AuctionType_Family else ipyData.GetWorldSaleMinutes()
+    auctionItem.EndTime = addTime + (noticeMinutes + saleMinutes) * 60
+    
+    __AddAuctionItemEndTimeByBid(auctionItem)
+    return
+
+def __AddAuctionItemEndTimeByBid(auctionItem):
+    # 根据最后一次竞价时间进行加时
+    if not auctionItem.BidderPrice:
+        return
+    bidTime = GameWorld.ChangeTimeStrToNum(auctionItem.BiddingTime)
+    endTime = bidTime + IpyGameDataPY.GetFuncCfg("AuctionHouse", 4)
+    if endTime < auctionItem.EndTime:
+        return
+    auctionItem.EndTime = endTime
+    return True
+
+def __GetAuctionItemDRDict(auctionItem):
+    drDict = {"GUID":auctionItem.ItemGUID, "PlayerID":auctionItem.PlayerID, "FamilyID":auctionItem.FamilyID,
+              "ItemID":auctionItem.ItemID, "Count":auctionItem.Count, "AuctionType":auctionItem.AuctionType,
+              "AddTime":auctionItem.AddTime, "BiddingTime":auctionItem.BiddingTime, "BidderID":auctionItem.BidderID,
+              "BidderPrice":auctionItem.BidderPrice, "UserData":auctionItem.UserData,
+              "EndTime":"" if not auctionItem.EndTime else GameWorld.ChangeTimeNumToStr(auctionItem.EndTime),
+              "FamilyPlayerIDInfo":auctionItem.FamilyPlayerIDInfo, "BidderIDInfo":auctionItem.BidderIDInfo}
+    return drDict
+
+def GetAuctionItem(itemGUID):
+    ## 获取拍品实例
+    return PyDataManager.GetAuctionItemManager().allAuctionItemDict.get(itemGUID)
+    
+def GetPlayerAuctionItemList(playerID):
+    ## 玩家拍卖中的拍品列表
+    return PyDataManager.GetAuctionItemManager().myAuctionItemDict.get(playerID, [])
+
+def GetFamilyAuctionItemList(familyID):
+    ## 仙盟拍卖中的拍品列表
+    return PyDataManager.GetAuctionItemManager().familyAuctionItemDict.get(familyID, [])
+
+def OnAuctionItemTimeProcess(curTime):
+    ## 拍卖行拍品定时处理,每秒触发一次
+    allAuctionItemByEndTimeList = PyDataManager.GetAuctionItemManager().allAuctionItemByEndTimeList
+    if not allAuctionItemByEndTimeList:
         return
     
-    ## ===========================================================================================
-    
-    # 保存数据 存数据库和realtimebackup
-    def GetSaveData(self):
-        savaData = ""
-        cntData = ""
-        cnt = 0
+    endItemList = [] # 结束竞价的拍品列表
+    moveToWorldItemList = [] # 转移到全服拍卖的仙盟拍品列表
+    doCount = len(allAuctionItemByEndTimeList)
+    while doCount > 0 and allAuctionItemByEndTimeList:
+        doCount -= 1
+        auctionItem = allAuctionItemByEndTimeList[0]
+        if curTime < auctionItem.EndTime:
+            break
+        allAuctionItemByEndTimeList.pop(0)
         
-        GameWorld.Log("Save AuctionItem count :%s" % cnt)
-        return CommFunc.WriteDWORD(cntData, cnt) + savaData
-    
-    # 从数据库载入数据
-    def LoadPyGameData(self, datas, pos, dataslen):
-        cnt, pos = CommFunc.ReadDWORD(datas, pos)
-        GameWorld.Log("Load AuctionItem count :%s" % cnt)
-        
-        return pos
-    
+        # 没有人竞价的仙盟拍品
+        if not auctionItem.BidderPrice and auctionItem.FamilyID and auctionItem.AuctionType == AuctionType_Family:
+            moveToWorldItemList.append(auctionItem)
+        else:
+            endItemList.append(auctionItem)
+            
+    __EndAuctionItem(endItemList, "ByTime")
+    __MoveFamilyAuctionItemToWorld(moveToWorldItemList)
+    return
 
+def __MoveFamilyAuctionItemToWorld(auctionItemList):
+    ## 仙盟拍品转移到全服
+    if not auctionItemList:
+        return
+    
+    curTimeStr = GameWorld.GetCurrentDataTimeStr()
+    attentionMgr = PyDataManager.GetAuctionAttentionManager()
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    notifyWorldAddItemList = [] # 新增全服拍品通知 [[itemGUID, itemID, playerID], ...]
+    
+    for auctionItem in auctionItemList:
+        itemGUID = auctionItem.ItemGUID
+        itemID = auctionItem.ItemID
+        itemCount = auctionItem.Count
+        playerID = auctionItem.PlayerID
+        familyID = auctionItem.FamilyID
+        
+        ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
+        auctionItem.AuctionType = AuctionType_World
+        auctionItem.AddTime = curTimeStr
+        __SetAuctionItemEndTime(auctionItem, ipyData)
+        
+        auctionItemMgr.allAuctionItemByEndTimeList.append(auctionItem)
+        auctionItemMgr.worldAuctionItemList.append(auctionItem)
+        notifyWorldAddItemList.append([itemGUID, itemID, playerID])
+        
+        # 添加进我的关注
+        for attentionPlayerID, attentionList in auctionItemMgr.myAttentionItemDict.items():
+            if itemID not in GetPlayerAuctionAttention(attentionMgr, attentionPlayerID):
+                continue
+            if auctionItem not in attentionList:
+                attentionList.append(auctionItem)
+                
+        if ipyData.GetNeedWorldNotify():
+            PlayerControl.WorldNotify(0, "Paimai4", [itemID, ipyData.GetNoticeSaleMinutes()])
+            
+        drDict = {"AuctionItemInfo":__GetAuctionItemDRDict(auctionItem)}
+        DR_AuctionHouse(None, "FamilyToWorld", drDict)
+        
+        GameWorld.DebugLog("仙盟拍品转移到全服: itemID=%s,itemCount=%s,familyID=%s,itemGUID=%s" 
+                           % (itemID, itemCount, familyID, itemGUID))
+        
+    __SortAuctionitem()
+    __SyncRefreshAuctionItem(auctionItemList)
+    __NotifyAuctionPlayerAddItem(notifyWorldAddItemList)
+    return
+
+def __SortAuctionitem(isSortWorldItem=True):
+    ''' 刷新全部拍品排序
+        后端排序
+        1.全服拍品: 因为全服拍品数量比较大,采用只查询指定个数拍品,所以只能由后端排序后同步
+        2.全部拍品竞价结束时间:后端需要处理竞价结束
+        
+        前端排序:
+        1.个人拍品:个数较少,全部同步,由前端自己排序
+        2.仙盟拍品:个数较少,全部同步,由前端自己排序
+        3.竞价拍品:个数较少,全部同步,由前端自己整合排序,区分最高竞价、历史竞价
+        4.关注拍品:个数较少,全部同步,由前端自己排序
+        
+        触发排序时机:
+        1-上架新全服拍品
+        2-仙盟拍品转移全服拍品,相当于上架新全服拍品
+        3-竞价拍品触发加时时,需要对结束时间重排,竞价加时的只触发加时,对全服拍品的排序没有影响,故可不重拍全服拍品
+    '''
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    auctionItemMgr.allAuctionItemByEndTimeList.sort(key=operator.attrgetter("EndTime"))
+    if isSortWorldItem:
+        auctionItemMgr.worldAuctionItemList.sort(key=operator.attrgetter("Sortpriority", "AddTime"))
+        auctionItemMgr.worldAuctionItemQueryDict = {} # 重置全服拍品条件查询,下次有玩家查询时再重新刷新
+    return
+
+def __EndAuctionItem(endItemList, endEvent):
+    ''' 结束拍品竞拍
+    @param delItemStateDict: 删除的拍品竞拍状态
+    '''
+    if not endItemList:
+        return 0
+    
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    clearAuctionItemList = []
+    for auctionItem in endItemList:
+        if not auctionItem:
+            continue
+        
+        itemGUID = auctionItem.ItemGUID
+        itemID = auctionItem.ItemID
+        itemCount = auctionItem.Count
+        playerID = auctionItem.PlayerID
+        familyID = auctionItem.FamilyID
+        bidderID = auctionItem.BidderID # 当前竞价玩家ID
+        bidderPrice = auctionItem.BidderPrice # 当前竞价,有人竞价的话代表竞拍成功
+        endType = ""
+        # 有人竞价,成交
+        if bidderID and bidderPrice:
+            endType = "OK"
+            
+            # 竞拍成功邮件,发放物品
+            paramList = [bidderPrice]
+            detail = {"ItemGUID":itemGUID}
+            addItemList = [{"ItemID":itemID, "Count":itemCount, "IsBind":True, "UserData":auctionItem.UserData}]
+            PlayerCompensation.SendMailByKey("PaimaiMail3", [bidderID], addItemList, paramList, detail=detail)
+            AddAuctionRecord(auctionItem, AuctionRecordResult_BidOK)
+            
+            # 拍卖成功收益,都以玩家收益向上取整
+            if familyID and auctionItem.FamilyPlayerIDInfo:
+                familyPlayerIDList = json.loads(auctionItem.FamilyPlayerIDInfo)
+                taxRate = IpyGameDataPY.GetFuncCfg("AuctionTaxrate", 2) # 仙盟拍品税率百分比
+                personMaxRate = IpyGameDataPY.GetFuncCfg("AuctionTaxrate", 3) # 仙盟拍品个人最大收益百分比
+                giveTotalGold = int(math.ceil(bidderPrice * (100 - taxRate) / 100.0))
+                giveMaxGold = int(math.ceil(giveTotalGold * personMaxRate / 100.0))
+                memCount = len(familyPlayerIDList)
+                giveGoldAverage = min(giveMaxGold, int(math.ceil(giveTotalGold * 1.0 / memCount))) # 有收益的人平分
+                
+                # 仙盟拍品收益邮件
+                detail = {"ItemGUID":itemGUID, "ItemID":itemID, "Count":itemCount, "BidderPrice":bidderPrice, "FamilyPlayerIDList":familyPlayerIDList,
+                          ChConfig.Def_MailMoneySource:ChConfig.Def_GiveMoney_AuctionGain}
+                paramList = [itemID, auctionItem.BidderName, bidderPrice, taxRate, giveGoldAverage, personMaxRate]
+                PlayerCompensation.SendMailByKey("PaimaiMail6", familyPlayerIDList, [], paramList, gold=giveGoldAverage, detail=detail)
+                
+            elif playerID:
+                taxRate = IpyGameDataPY.GetFuncCfg("AuctionTaxrate", 1) # 全服拍品税率百分比
+                givePlayerGold = int(math.ceil(bidderPrice * (100 - taxRate) / 100.0))
+                
+                # 个人拍卖收益邮件
+                detail = {"ItemGUID":itemGUID, "ItemID":itemID, "Count":itemCount, "BidderPrice":bidderPrice, 
+                          ChConfig.Def_MailMoneySource:ChConfig.Def_GiveMoney_AuctionGain}
+                paramList = [itemID, auctionItem.BidderName, bidderPrice, taxRate, givePlayerGold]
+                PlayerCompensation.SendMailByKey("PaimaiMail5", [playerID], [], paramList, gold=givePlayerGold, detail=detail)
+                
+            AddAuctionRecord(auctionItem, AuctionRecordResult_SellOK)
+        else:
+            # 仙盟拍品回收
+            if familyID:
+                endType = "Recycle"
+                AddAuctionRecord(auctionItem, AuctionRecordResult_Recycle)
+            # 个人拍品流拍,物品返还
+            else:
+                endType = "Return"
+                
+                # 流拍返还物品邮件
+                paramList = []
+                detail = {"ItemGUID":itemGUID}
+                addItemList = [{"ItemID":itemID, "Count":itemCount, "IsBind":True, "UserData":auctionItem.UserData}]
+                PlayerCompensation.SendMailByKey("PaimaiMail4", [playerID], addItemList, paramList, detail=detail)
+                
+                AddAuctionRecord(auctionItem, AuctionRecordResult_SellFail)
+                
+        drDict = {"AuctionItemInfo":__GetAuctionItemDRDict(auctionItem), "EndType":endType, "EndEvent":endEvent}
+        DR_AuctionHouse(None, "EndAuctionItem", drDict)
+        
+        GameWorld.DebugLog("结束竞拍: itemID=%s,itemCount=%s,familyID=%s,endType=%s,endEvent=%s,itemGUID=%s" 
+                           % (itemID, itemCount, familyID, endType, endEvent, itemGUID), playerID)
+        
+        auctionItemMgr.allAuctionItemDict.pop(itemGUID, None)
+        
+        if auctionItem in auctionItemMgr.allAuctionItemByEndTimeList:
+            auctionItemMgr.allAuctionItemByEndTimeList.remove(auctionItem)
+            
+        if auctionItem in auctionItemMgr.worldAuctionItemList:
+            auctionItemMgr.worldAuctionItemList.remove(auctionItem)
+            
+        for queryItemList in auctionItemMgr.worldAuctionItemQueryDict.values():
+            if auctionItem in queryItemList:
+                queryItemList.remove(auctionItem)
+                
+        if familyID and familyID in auctionItemMgr.familyAuctionItemDict:
+            familyItemList = auctionItemMgr.familyAuctionItemDict[familyID]
+            if auctionItem in familyItemList:
+                familyItemList.remove(auctionItem)
+                
+        for nowBiddingItemList in auctionItemMgr.nowBiddingAuctionItemDict.values():
+            if auctionItem in nowBiddingItemList:
+                nowBiddingItemList.remove(auctionItem)
+                
+        for hisBiddingItemList in auctionItemMgr.hisBiddingAuctionItemDict.values():
+            if auctionItem in hisBiddingItemList:
+                hisBiddingItemList.remove(auctionItem)
+                
+        if playerID and playerID in auctionItemMgr.myAuctionItemDict:
+            playerItemList = auctionItemMgr.myAuctionItemDict[playerID]
+            if auctionItem in playerItemList:
+                playerItemList.remove(auctionItem)
+                
+        for attentionItemList in auctionItemMgr.myAttentionItemDict.values():
+            if auctionItem in attentionItemList:
+                attentionItemList.remove(auctionItem)
+                
+        clearItem = ChPyNetSendPack.tagGCClearAuctionItem()
+        clearItem.ItemGUID = itemGUID
+        clearAuctionItemList.append(clearItem)
+        
+    if not clearAuctionItemList:
+        return 0
+    clearCount = len(clearAuctionItemList)
+    
+    clientPack = ChPyNetSendPack.tagGCClearAuctionItemInfo()
+    clientPack.ClearAuctionItemList = clearAuctionItemList
+    clientPack.ClearCount = len(clientPack.ClearAuctionItemList)
+    
+    playerManager = GameWorld.GetPlayerManager()
+    for i in xrange(playerManager.GetActivePlayerCount()):
+        player = playerManager.GetActivePlayerAt(i)
+        if player == None:
+            continue
+        if PlayerControl.GetIsTJG(player):
+            continue
+        NetPackCommon.SendFakePack(player, clientPack)
+        
+    return clearCount
+
+def MapServer_AuctionHouseLogic(curPlayer, msgList, tick):
+    ## 地图玩家拍卖处理
+    
+    playerID = 0 if not curPlayer else curPlayer.GetPlayerID()
+    queryType, queryData = msgList
+    result = []
+    
+    GameWorld.Log("收到地图拍卖行信息: queryType=%s,queryData=%s" % (queryType, queryData), playerID)
+    if not GetAuctionHouseState():
+        if curPlayer:
+            PlayerControl.NotifyCode(curPlayer, "AuctionHouseClose")
+        return
+    
+    # 查询玩家可否上架
+    if queryType == "AddAuctionItemQuery":
+        canSell = __CheckPlayerCanAddAuctionItem(curPlayer)
+        if not canSell:
+            return
+        result = [canSell]
+        
+    # 执行上架物品
+    elif queryType == "AddAuctionItem":
+        addAuctionItemList = queryData
+        __AddAuctionItemByList(curPlayer, addAuctionItemList)
+        return
+    
+    # 查询玩家可否竞价物品
+    elif queryType == "BidAuctionItemQuery":
+        itemGUID, biddingPrice = queryData
+        itemID, errInfo = __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, tick, True)
+        GameWorld.DebugLog("    itemID=%s,errInfo=%s" % (itemID, errInfo), playerID)
+        if errInfo:
+            return
+        if not itemID:
+            return
+        result = [itemID]
+        
+    # 执行玩家竞价物品
+    elif queryType == "BidAuctionItem":
+        itemGUID, biddingPrice = queryData
+        itemID, errInfo = __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, tick, False)
+        if errInfo:
+            GameWorld.DebugLog("    errInfo=%s" % errInfo, playerID)
+            drDict = {"ItemGUID":itemGUID, "BiddingPrice":biddingPrice, "ErrInfo":errInfo}
+            DR_AuctionHouse(curPlayer, "BidAuctionItemError", drDict)
+            
+        return
+    
+    elif queryType == "ClearAuctionItem":
+        __DoGMClearAuctionItem(curPlayer)
+        return
+    
+    elif queryType == "PrintAuctionItem":
+        __DoGMPrintAuctionItem(curPlayer)
+        return
+    
+    resultMsg = str([queryType, queryData, result])
+    if curPlayer:
+        curPlayer.MapServer_QueryPlayerResult(0, 0, "AuctionHouse", resultMsg, len(resultMsg))
+    return
+
+def __CheckPlayerCanAddAuctionItem(curPlayer):
+    ## 检查玩家可否上架拍品
+    playerID = curPlayer.GetPlayerID()
+    maxSellCount = IpyGameDataPY.GetFuncCfg("AuctionHouse", 2)
+    if maxSellCount:
+        playerAuctionItemList = GetPlayerAuctionItemList(playerID)
+        playerSellCount = len(playerAuctionItemList)
+        canSell = playerSellCount < maxSellCount
+        if not canSell:
+            PlayerControl.NotifyCode(curPlayer, "SellCountLimit")
+        return canSell
+    return True
+
+def __AddAuctionItemByList(curPlayer, addAuctionItemList):
+    ''' 批量添加拍品
+    @param curPlayer: 可能为None
+    '''
+    
+    notifyWorldAddItemList = [] # 新增全服拍品通知 [[itemGUID, itemID, playerID], ...]
+    notifyFamilyAddItemDict = {} # 新增仙盟拍品通知 {familyID:[auctionItem, ...], ...}
+    for playerID, familyID, familyPlayerIDList, itemData in addAuctionItemList:
+        if not playerID and not familyID:
+            continue
+        
+        auctionItem = __DoAddAuctionItem(curPlayer, playerID, familyID, familyPlayerIDList, itemData)
+        if not auctionItem:
+            continue
+        
+        if familyID:
+            familyAddItemList = notifyFamilyAddItemDict.get(familyID, [])
+            familyAddItemList.append(auctionItem)
+            notifyFamilyAddItemDict[familyID] = familyAddItemList
+        else:
+            itemGUID = auctionItem.ItemGUID
+            itemID = auctionItem.ItemID
+            notifyWorldAddItemList.append([itemGUID, itemID, playerID])
+            
+    if notifyFamilyAddItemDict or notifyWorldAddItemList:
+        isSortWorldItem = notifyWorldAddItemList != []
+        __SortAuctionitem(isSortWorldItem=isSortWorldItem)
+        
+    # 通知新增仙盟拍品
+    for familyID, familyAddItemList in notifyFamilyAddItemDict.items():
+        Sync_FamilyAuctionItemInfo(None, familyID, familyAddItemList)
+        
+    # 通知全服拍品关注玩家
+    __NotifyAuctionPlayerAddItem(notifyWorldAddItemList)
+    return
+
+def __DoAddAuctionItem(curPlayer, playerID, familyID, familyPlayerIDList, itemData):
+    ## 添加拍品
+    itemID = itemData.get("ItemID", 0)
+    ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
+    if not ipyData:
+        return
+    
+    itemGUID = itemData.get("GUID", "")
+    auctionItem = PyGameDataStruct.tagDBAuctionItem()
+    auctionItem.ItemGUID = itemGUID
+    auctionItem.PlayerID = playerID
+    auctionItem.FamilyID = familyID
+    auctionItem.ItemID = itemID
+    auctionItem.Count = itemData.get("ItemCount", 0)
+    auctionItem.AddTime = GameWorld.GetCurrentDataTimeStr()
+    auctionItem.ItemType = itemData.get("ItemType", 0)
+    auctionItem.ItemJobLimit = itemData.get("ItemJobLimit", 0)
+    auctionItem.ItemClassLV = itemData.get("ItemClassLV", 0)
+    auctionItem.UserData = itemData.get("UserData", "")
+    auctionItem.UserDataLen = len(auctionItem.UserData)
+    auctionItem.FamilyPlayerIDInfo = str(familyPlayerIDList)
+    auctionItem.FamilyPlayerIDLen = len(auctionItem.FamilyPlayerIDInfo)
+            
+    if not __InitAuctionItemAttrEx(auctionItem):
+        return
+    
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    auctionItemMgr.allAuctionItemDict[auctionItem.ItemGUID] = auctionItem
+    auctionItemMgr.allAuctionItemByEndTimeList.append(auctionItem)
+    
+    if familyID:
+        auctionItem.AuctionType = AuctionType_Family
+        familyItemList = auctionItemMgr.familyAuctionItemDict.get(familyID, [])
+        familyItemList.append(auctionItem)
+        auctionItemMgr.familyAuctionItemDict[familyID] = familyItemList
+    else:
+        auctionItem.AuctionType = AuctionType_World
+        auctionItemMgr.worldAuctionItemList.append(auctionItem)
+        
+        # 添加进我的拍卖
+        if playerID:
+            myAuctionItemList = auctionItemMgr.myAuctionItemDict.get(playerID, [])
+            myAuctionItemList.append(auctionItem)
+            auctionItemMgr.myAuctionItemDict[playerID] = myAuctionItemList
+            if curPlayer:
+                Sync_PlayerAuctionItemInfo(curPlayer, auctionItem)
+                
+        # 添加进我的关注
+        attentionMgr = PyDataManager.GetAuctionAttentionManager()
+        for attentionPlayerID, attentionList in auctionItemMgr.myAttentionItemDict.items():
+            if itemID not in GetPlayerAuctionAttention(attentionMgr, attentionPlayerID):
+                continue
+            if auctionItem not in attentionList:
+                attentionList.append(auctionItem)
+                
+        if ipyData.GetNeedWorldNotify():
+            PlayerControl.WorldNotify(0, "Paimai4", [itemID, ipyData.GetNoticeSaleMinutes()])
+            
+    drDict = {"AuctionItemInfo":__GetAuctionItemDRDict(auctionItem)}
+    DR_AuctionHouse(curPlayer, "AddAuctionItem", drDict)
+    GameWorld.DebugLog("上架拍品: playerID=%s,familyID=%s,%s" % (playerID, familyID, drDict), playerID)
+    GameWorld.DebugLog("更新拍品数: %s" % len(auctionItemMgr.allAuctionItemDict))
+    return auctionItem
+
+def __NotifyAuctionPlayerAddItem(notifyWorldAddItemList):
+    ## 通知关注物品的玩家新上架物品了
+    if not notifyWorldAddItemList:
+        return
+    attentionMgr = PyDataManager.GetAuctionAttentionManager()
+    playerManager = GameWorld.GetPlayerManager()
+    for i in xrange(playerManager.GetActivePlayerCount()):
+        player = playerManager.GetActivePlayerAt(i)
+        if player == None:
+            continue
+        if PlayerControl.GetIsTJG(player):
+            continue
+        playerAttentionIDList = GetPlayerAuctionAttention(attentionMgr, player.GetPlayerID())
+        if not playerAttentionIDList:
+            continue
+        infoPack = None
+        for itemGUID, itemID, playerID in notifyWorldAddItemList:
+            if playerID == player.GetPlayerID():
+                # 自己上架的物品不通知
+                continue
+            if itemID not in playerAttentionIDList:
+                continue
+            if not infoPack:
+                infoPack = ChPyNetSendPack.tagGCAddAuctionItemInfo()
+            itemInfo = ChPyNetSendPack.tagGCAddAuctionItem()
+            itemInfo.ItemGUID = itemGUID
+            itemInfo.ItemID = itemID
+            infoPack.AddAuctionItemList.append(itemInfo)
+            
+        if infoPack:
+            infoPack.AddCount = len(infoPack.AddAuctionItemList)
+            NetPackCommon.SendFakePack(player, infoPack)
+    return
+
+def __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, tick, isOnlyCheck):
+    ''' 玩家竞价物品
+    @param curPlayer: 竞价的玩家
+    @param itemGUID: 拍品GUID
+    @param biddingPrice: 竞价
+    @param isOnlyCheck: 是否仅检查可否竞价
+    '''
+    
+    errInfo = ""
+    itemID = 0
+    
+    auctionItem = GetAuctionItem(itemGUID)
+    if not auctionItem:
+        # 拍品不存在
+        PlayerControl.NotifyCode(curPlayer, "Paimai3")
+        errInfo = "not auctionItem"
+        return itemID, errInfo
+    itemID = auctionItem.ItemID
+    ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
+    if not ipyData:
+        errInfo = "not ipyData"
+        return itemID, errInfo
+    playerID = curPlayer.GetPlayerID()
+    addTimeStr = auctionItem.AddTime
+    auctionType = auctionItem.AuctionType # 0-全服拍品,1-仙盟拍品
+    addTime = GameWorld.ChangeTimeStrToNum(addTimeStr)
+    curTime = int(time.time())
+    passMinutes = (curTime - addTime) / 60
+    noticeMinutes = ipyData.GetNoticeSaleMinutes()
+    if passMinutes < noticeMinutes:
+        GameWorld.ErrLog("拍品尚未开放竞价! itemGUID=%s,itemID=%s,addTimeStr=%s" % (itemGUID, itemID, addTimeStr), playerID)
+        errInfo = "in notice time"
+        return itemID, errInfo
+    if auctionType == AuctionType_Family:
+        itemFamilyID = auctionItem.FamilyID
+        playerFamilyID = curPlayer.GetFamilyID()
+        if not playerFamilyID or playerFamilyID != itemFamilyID:
+            GameWorld.ErrLog("该拍品为仙盟私有拍品,非本仙盟人员不可竞拍! itemGUID=%s,itemID=%s,itemFamilyID=%s,playerFamilyID=%s" 
+                             % (itemGUID, itemID, itemFamilyID, playerFamilyID), playerID)
+            errInfo = "is family auction item"
+            return itemID, errInfo
+    if curTime >= auctionItem.EndTime:
+        GameWorld.ErrLog("拍品已结束竞价! itemGUID=%s,itemID=%s,addTimeStr=%s" % (itemGUID, itemID, addTimeStr), playerID)
+        errInfo = "end bid"
+        return itemID, errInfo
+    
+    nextPrice = ipyData.GetBasePrice() if not auctionItem.BidderPrice else (auctionItem.BidderPrice + ipyData.GetBiddingAdd())
+    buyoutPrice = ipyData.GetBuyoutPrice() # 允许没有一口价的,代表可以一直竞价
+    nextPrice = nextPrice if not buyoutPrice else min(nextPrice, buyoutPrice) # 不超过一口价
+    if not (biddingPrice == nextPrice or (buyoutPrice and biddingPrice == buyoutPrice)):
+        # 竞价价格错误
+        PlayerControl.NotifyCode(curPlayer, "Paimai2")
+        errInfo = "bid price error! biddingPrice=%s,nextPrice=%s,buyoutPrice=%s,itemGUID=%s,itemID=%s" % (biddingPrice, nextPrice, buyoutPrice, itemGUID, itemID)
+        return itemID, errInfo
+    
+    if isOnlyCheck:
+        queryTick = auctionItem.BiddingQueryTick
+        if queryTick and tick - queryTick < 10000:
+            # 有玩家正在竞价,请稍等
+            PlayerControl.NotifyCode(curPlayer, "Paimai1")
+            errInfo = "other player bidding"
+            return itemID, errInfo
+        auctionItem.BiddingQueryID = playerID
+        auctionItem.BiddingQueryTick = tick
+        return itemID, errInfo
+    
+    if auctionItem.BiddingQueryID != playerID:
+        PlayerControl.NotifyCode(curPlayer, "Paimai2")
+        errInfo = "bidding player error"
+        return itemID, errInfo
+    
+    lastBidderID = auctionItem.BidderID
+    lastBidderPrice = auctionItem.BidderPrice
+    itemCount = auctionItem.Count
+    
+    isBuyout = buyoutPrice and biddingPrice >= buyoutPrice # 大于等于一口价,直接成交
+    
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    nowBiddingAuctionItemDict = auctionItemMgr.nowBiddingAuctionItemDict
+    hisBiddingAuctionItemDict = auctionItemMgr.hisBiddingAuctionItemDict
+    
+    # 邮件返还上个竞价者
+    if lastBidderID and lastBidderPrice:
+        detail = {"ItemID":itemID, "ItemGUID":itemGUID, "Count":itemCount, ChConfig.Def_MailMoneySource:ChConfig.Def_GiveMoney_AuctionBidReturn}
+        if isBuyout:
+            # 竞拍失败,仅通知
+            paramList = [itemID, lastBidderPrice]
+            PlayerCompensation.SendMailByKey("PaimaiMail2", [lastBidderID], [], paramList, gold=lastBidderPrice, detail=detail)
+        else:
+            # 竞拍失败,可继续竞价邮件
+            paramList = [itemID, lastBidderPrice, itemGUID]
+            PlayerCompensation.SendMailByKey("PaimaiMail1", [lastBidderID], [], paramList, gold=lastBidderPrice, detail=detail)
+        AddAuctionRecord(auctionItem, AuctionRecordResult_BidFail)
+        
+        isSyncBiddingItem = False
+        nowBiddingAuctionItemList = nowBiddingAuctionItemDict.get(lastBidderID, [])
+        if auctionItem in nowBiddingAuctionItemList:
+            nowBiddingAuctionItemList.remove(auctionItem)
+            nowBiddingAuctionItemDict[lastBidderID] = nowBiddingAuctionItemList
+            isSyncBiddingItem = True
+        hisBiddingAuctionItemList = hisBiddingAuctionItemDict.get(lastBidderID, [])
+        if auctionItem not in hisBiddingAuctionItemList:
+            hisBiddingAuctionItemList.append(auctionItem)
+            hisBiddingAuctionItemDict[lastBidderID] = hisBiddingAuctionItemList
+            isSyncBiddingItem = True
+        if isSyncBiddingItem:
+            lastBidder = GameWorld.GetPlayerManager().FindPlayerByID(lastBidderID)
+            if lastBidder and not PlayerControl.GetIsTJG(lastBidder):
+                Sync_PlayerBiddingItemInfo(lastBidder, auctionItem)
+                
+    # 更新竞价信息
+    auctionItem.BiddingQueryID = 0
+    auctionItem.BiddingTick = 0
+    auctionItem.BiddingTime = GameWorld.GetCurrentDataTimeStr()
+    auctionItem.BidderID = playerID
+    auctionItem.BidderName = curPlayer.GetName()
+    auctionItem.BidderPrice = biddingPrice
+    isSyncBiddingItem = False
+    nowBiddingAuctionItemList = nowBiddingAuctionItemDict.get(playerID, [])
+    if auctionItem not in nowBiddingAuctionItemList:
+        nowBiddingAuctionItemList.append(auctionItem)
+        nowBiddingAuctionItemDict[playerID] = nowBiddingAuctionItemList
+        isSyncBiddingItem = True
+    hisBiddingAuctionItemList = hisBiddingAuctionItemDict.get(playerID, [])
+    if auctionItem in hisBiddingAuctionItemList:
+        hisBiddingAuctionItemList.remove(auctionItem)
+        hisBiddingAuctionItemDict[playerID] = hisBiddingAuctionItemList
+        isSyncBiddingItem = True
+    if isSyncBiddingItem:
+        Sync_PlayerBiddingItemInfo(curPlayer, auctionItem)
+        
+    # 添加历史竞价者
+    if str(playerID) not in auctionItem.BidderIDInfo:
+        auctionItem.BidderIDInfo += "%s|" % playerID
+        auctionItem.BidderIDLen = len(auctionItem.BidderIDInfo)
+        
+    GameWorld.DebugLog("玩家竞价拍品: itemGUID=%s,itemID=%s,isBuyout=%s,lastBidderID=%s,lastBidderPrice=%s,bidderIDInfo=%s" 
+                       % (itemGUID, itemID, isBuyout, lastBidderID, lastBidderPrice, auctionItem.BidderIDInfo), playerID)
+    
+    if isBuyout:        
+        __EndAuctionItem([auctionItem], "Buyout")
+    else:
+        if __AddAuctionItemEndTimeByBid(auctionItem):
+            __SortAuctionitem(isSortWorldItem=False)
+            
+        drDict = {"AuctionItemInfo":__GetAuctionItemDRDict(auctionItem)}
+        DR_AuctionHouse(curPlayer, "BidAuctionItem", drDict)
+        
+        __SyncRefreshAuctionItem([auctionItem])
+        
+    return itemID, errInfo
+
+def __SyncRefreshAuctionItem(auctionItemList):
+    ''' // B5 08 拍卖行刷新拍品 #tagGCRefreshAuctionItemInfo
+        1-仙盟拍品转移到全服时通知; 2-拍品有人竞价时刷新
+    '''
+    
+    refreshAuctionItemList = []
+    
+    for auctionItem in auctionItemList:
+        refreshItem = ChPyNetSendPack.tagGCRefreshAuctionItem()
+        refreshItem.ItemGUID = auctionItem.ItemGUID
+        refreshItem.AuctionType = auctionItem.AuctionType
+        refreshItem.AddTime = auctionItem.AddTime
+        refreshItem.BidderID = auctionItem.BidderID
+        refreshItem.BidderPrice = auctionItem.BidderPrice
+        refreshAuctionItemList.append(refreshItem)
+        
+    if not refreshAuctionItemList:
+        return
+    
+    clientPack = ChPyNetSendPack.tagGCRefreshAuctionItemInfo()
+    clientPack.RefreshAuctionItemList = refreshAuctionItemList
+    clientPack.RefreshCount = len(clientPack.RefreshAuctionItemList)
+    playerManager = GameWorld.GetPlayerManager()
+    for i in xrange(playerManager.GetActivePlayerCount()):
+        player = playerManager.GetActivePlayerAt(i)
+        if player == None:
+            continue
+        if PlayerControl.GetIsTJG(player):
+            continue
+        NetPackCommon.SendFakePack(player, clientPack)
+        
+    return
+
+def __DoGMClearAuctionItem(curPlayer):
+    if curPlayer.GetGMLevel() != 90:
+        return
+    
+    allAuctionItemByEndTimeList = PyDataManager.GetAuctionItemManager().allAuctionItemByEndTimeList
+    if not allAuctionItemByEndTimeList:
+        GameWorld.DebugAnswer(curPlayer, "当前没有拍品!")
+        return
+    
+    endItemList = []
+    for auctionItem in allAuctionItemByEndTimeList:
+        endItemList.append(auctionItem)
+        
+    clearCount = __EndAuctionItem(endItemList, "GMClear")
+    
+    GameWorld.DebugAnswer(curPlayer, "清空拍品数=%s" % clearCount)
+    return
+
+def __DoGMPrintAuctionItem(curPlayer):
+    if curPlayer.GetGMLevel() != 90:
+        return
+    
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    allAuctionItemByEndTimeList = auctionItemMgr.allAuctionItemByEndTimeList
+    GameWorld.DebugLog("TimeList总拍品数: =%s" % len(allAuctionItemByEndTimeList))
+    for i, auctionItem in enumerate(allAuctionItemByEndTimeList):
+        GameWorld.DebugLog("    i=%s, %s" % (i, __GetAuctionItemDRDict(auctionItem)))
+        
+    GameWorld.DebugLog("AllDict总拍品数: =%s" % len(auctionItemMgr.allAuctionItemDict))
+    
+    GameWorld.DebugLog("全服拍品个数: =%s" % len(auctionItemMgr.worldAuctionItemList))
+    for familyID, familyItemList in auctionItemMgr.familyAuctionItemDict.items():
+        GameWorld.DebugLog("仙盟拍品个数: familyID=%s, %s" % (familyID, len(familyItemList)))
+        
+    for playerID, myItemList in auctionItemMgr.myAuctionItemDict.items():
+        GameWorld.DebugLog("玩家拍品个数: playerID=%s, %s" % (playerID, len(myItemList)))
+        
+    for playerID, nowBiddingItemList in auctionItemMgr.nowBiddingAuctionItemDict.items():
+        GameWorld.DebugLog("玩家最高竞价拍品个数: playerID=%s, %s" % (playerID, len(nowBiddingItemList)))
+        
+    for playerID, hisBiddingItemList in auctionItemMgr.hisBiddingAuctionItemDict.items():
+        GameWorld.DebugLog("玩家历史竞价拍品个数: playerID=%s, %s" % (playerID, len(hisBiddingItemList)))
+    
+    for playerID, attentionItemList in auctionItemMgr.myAttentionItemDict.items():
+        GameWorld.DebugLog("玩家关注拍品个数: playerID=%s, %s" % (playerID, len(attentionItemList)))
+        
+    return
+
+#// B5 10 拍卖行查询拍卖中的物品 #tagCGQueryAuctionItem
+#
+#struct    tagCGQueryAuctionItem
+#{
+#    tagHead    Head;
+#    BYTE    Job;    //过滤职业,0为不限制
+#    BYTE    ItemTypeCount;
+#    DWORD    ItemTypeList[ItemTypeCount];    //指定的物品类型
+#    BYTE    ClassLV;    //过滤阶数,0为不限制
+#    BYTE    SpecItemIDCount;    //指定物品ID个数
+#    DWORD    SpecItemIDList[SpecItemIDCount];    //指定物品ID
+#    char    FromItemGUID[40];        //从哪个物品开始查询
+#    BYTE    QueryDir;        //查询方向,1-往后查,2-往前查
+#    BYTE    QueryCount;    //查询个数,0为全部
+#};
+def OnQueryAuctionItem(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    job = clientData.Job
+    itemTypeList = clientData.ItemTypeList
+    classLV = clientData.ClassLV
+    specItemIDList = clientData.SpecItemIDList
+    fromItemGUID = clientData.FromItemGUID
+    queryDir = clientData.QueryDir
+    queryCount = clientData.QueryCount
+    __Sync_WorldAuctionItemQueryResult(curPlayer, job, itemTypeList, classLV, specItemIDList, fromItemGUID, queryDir, queryCount)
+    return
+
+#// B5 17 拍卖行查询定位目标拍品 #tagCGQueryTagAuctionItem
+#
+#struct    tagCGQueryTagAuctionItem
+#{
+#    tagHead        Head;
+#    char        ItemGUID[40];
+#    DWORD        ItemID;
+#};
+def OnQueryTagAuctionItem(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    tagItemGUID = clientData.ItemGUID
+    queryDir = 3
+    __Sync_WorldAuctionItemQueryResult(curPlayer, fromItemGUID=tagItemGUID, queryDir=queryDir)
+    return
+
+def __Sync_WorldAuctionItemQueryResult(curPlayer, job=0, itemTypeList=[], classLV=0, specItemIDList=[], fromItemGUID="", queryDir=1, queryCount=10):
+    ## 根据过滤条件同步全服拍品列表,目前仅全服拍品需要通过查询服务器获得,个人拍品及仙盟拍品由于个数较少直接由上线或变更时主动同步
+    
+    fromAuctionItem = None
+    if fromItemGUID:
+        fromAuctionItem = GetAuctionItem(fromItemGUID)
+        if not fromAuctionItem:
+            GameWorld.DebugLog("查询的目标拍品不存在! fromItemGUID=%s" % fromItemGUID)
+            PlayerControl.NotifyCode(curPlayer, "Paimai5")
+            return
+        
+    # {(job, (itemType, ...), itemClassLV, (itemID, ...)):[tagDBAuctionItem, ...], ...}
+    queryKey = (job, tuple(itemTypeList), classLV, tuple(specItemIDList))
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    worldAuctionItemQueryDict = auctionItemMgr.worldAuctionItemQueryDict
+    if queryKey not in worldAuctionItemQueryDict:
+        # 载入对应查询条件拍品缓存
+        auctionItemQueryList = []
+        for worldAuctionItem in auctionItemMgr.worldAuctionItemList:
+            if job and worldAuctionItem.ItemJobLimit != job:
+                continue
+            if itemTypeList and worldAuctionItem.ItemType not in itemTypeList:
+                continue
+            if classLV and worldAuctionItem.ItemClassLV != classLV:
+                continue
+            if specItemIDList and worldAuctionItem.ItemID not in specItemIDList:
+                continue
+            auctionItemQueryList.append(worldAuctionItem)
+        worldAuctionItemQueryDict[queryKey] = auctionItemQueryList
+    else:
+        auctionItemQueryList = worldAuctionItemQueryDict[queryKey]
+    queryTotalCount = len(auctionItemQueryList)
+    
+    fromIndex = 0
+    if fromAuctionItem:
+        if fromAuctionItem not in auctionItemQueryList:
+            GameWorld.ErrLog("查询的目标拍品不在所在的过滤的条件里! fromItemGUID=%s" % fromItemGUID)
+            PlayerControl.NotifyCode(curPlayer, "Paimai5")
+            return
+        fromIndex = auctionItemQueryList.index(fromAuctionItem)
+        
+    # 向前查,其他的默认向后查
+    if queryDir == 2:
+        startIndex = max(0, fromIndex - queryCount + 1)
+        syncItemList = auctionItemQueryList[startIndex:fromIndex + 1]
+        queryRemainlCount = startIndex      
+    # 向后查
+    else:
+        syncItemList = auctionItemQueryList[fromIndex:fromIndex + queryCount]
+        queryRemainlCount = max(0, queryTotalCount - fromIndex - queryCount)
+        
+    clientPack = ChPyNetSendPack.tagGCAuctionItemInfo()
+    clientPack.Job = job
+    clientPack.ItemTypeList = itemTypeList
+    clientPack.ItemTypeCount = len(clientPack.ItemTypeList)
+    clientPack.ClassLV = classLV
+    clientPack.SpecItemIDList = specItemIDList
+    clientPack.SpecItemIDCount = len(clientPack.SpecItemIDList)
+    clientPack.FromItemGUID = fromItemGUID
+    clientPack.QueryDir = queryDir
+    clientPack.QueryCount = queryCount
+    clientPack.QueryRemainlCount = queryRemainlCount
+    clientPack.AuctionItemList = []
+    for auctionItem in syncItemList:
+        itemInfo = ChPyNetSendPack.tagGCAuctionItem()
+        itemInfo.ItemGUID = auctionItem.ItemGUID
+        itemInfo.FamilyID = auctionItem.FamilyID
+        itemInfo.ItemID = auctionItem.ItemID
+        itemInfo.ItemCount = auctionItem.Count
+        itemInfo.AddTime = auctionItem.AddTime
+        itemInfo.BidderPrice = auctionItem.BidderPrice
+        itemInfo.UserData = auctionItem.UserData
+        itemInfo.UserDataLen = auctionItem.UserDataLen
+        clientPack.AuctionItemList.append(itemInfo)
+    clientPack.AuctionItemCount = len(clientPack.AuctionItemList)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+
+#// B5 12 拍卖行查询拍卖记录 #tagCGQueryAuctionRecord
+#
+#struct    tagCGQueryAuctionRecord
+#{
+#    tagHead    Head;
+#    BYTE        RecordType;    //记录类型 0-我的拍品记录 1-仙盟拍品记录 2-我的竞拍记录
+#};
+def OnQueryAuctionRecord(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    recordType = clientData.RecordType
+    Sync_PlayerAuctionRecordInfo(curPlayer, recordType, None, curPlayer.GetFamilyID())
+    return
+
+#// B5 16 拍卖行查询关注中的拍品 #tagCGQueryAttentionAuctionItem
+#
+#struct    tagCGQueryAttentionAuctionItem
+#{
+#    tagHead    Head;
+#};
+def OnQueryAttentionAuctionItem(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    playerID = curPlayer.GetPlayerID()
+    
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    myAttentionItemDict = auctionItemMgr.myAttentionItemDict
+    if playerID in myAttentionItemDict:
+        attentionItemList = myAttentionItemDict[playerID]
+    else:
+        attentionItemList = []
+        attentionMgr = PyDataManager.GetAuctionAttentionManager()
+        attentionItemIDList = GetPlayerAuctionAttention(attentionMgr, playerID)
+        if attentionItemIDList:
+            for auctionItem in auctionItemMgr.worldAuctionItemList:
+                if auctionItem.ItemID not in attentionItemIDList:
+                    continue
+                attentionItemList.append(auctionItem)
+            myAttentionItemDict[playerID] = attentionItemList
+            
+    clientPack = ChPyNetSendPack.tagGCAttentionAuctionItemInfo()
+    clientPack.AuctionItemList = []
+    for attentionItem in attentionItemList:
+        itemObj = ChPyNetSendPack.tagGCAttentionAuctionItem()
+        itemObj.ItemGUID = attentionItem.ItemGUID
+        itemObj.FamilyID = attentionItem.FamilyID
+        itemObj.ItemID = attentionItem.ItemID
+        itemObj.ItemCount = attentionItem.Count
+        itemObj.AddTime = attentionItem.AddTime
+        itemObj.BidderPrice = attentionItem.BidderPrice
+        itemObj.UserData = attentionItem.UserData
+        itemObj.UserDataLen = attentionItem.UserDataLen
+        clientPack.AuctionItemList.append(itemObj)
+    clientPack.AuctionItemCount = len(clientPack.AuctionItemList)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+
+#// B5 18 拍卖行修改关注物品 #tagCGAttentionAuctionItemChange
+#
+#struct    tagCGAttentionAuctionItemChange
+#{
+#    tagHead        Head;
+#    DWORD        ItemID;
+#    BYTE        IsAttention;    //是否关注,取消关注发0
+#};
+def OnAttentionAuctionItemChange(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    playerID = curPlayer.GetPlayerID()
+    itemID = clientData.ItemID
+    isAttention = clientData.IsAttention
+    
+    attentionMgr = PyDataManager.GetAuctionAttentionManager()
+    if isAttention:
+        if not AddPlayerAuctionAttention(attentionMgr, playerID, itemID):
+            return
+    else:
+        if not DelPlayerAuctionAttention(attentionMgr, playerID, itemID):
+            return
+    Sync_PlayerAttentionAuctionItemID(curPlayer, True)
+    
+    # 关注信息变更,清空关注拍品实例缓存
+    auctionItemMgr = PyDataManager.GetAuctionItemManager()
+    auctionItemMgr.myAttentionItemDict.pop(playerID, None)
+    return
+
+def Sync_FamilyAuctionItemInfo(curPlayer, familyID, itemInfoList=[]):
+    # // B5 05 拍卖行仙盟拍卖中的物品信息 #tagGCFamilyAuctionItemInfo
+    
+    if not familyID:
+        return
+    
+    if not itemInfoList:
+        itemInfoList = GetFamilyAuctionItemList(familyID)
+        
+    if not itemInfoList:
+        return
+    
+    itemInfoPack = ChPyNetSendPack.tagGCFamilyAuctionItemInfo()
+    itemInfoPack.AuctionItemList = []
+    for auctionItem in itemInfoList:
+        packItem = ChPyNetSendPack.tagGCFamilyAuctionItem()
+        packItem.ItemGUID = auctionItem.ItemGUID
+        packItem.FamilyID = auctionItem.FamilyID
+        packItem.ItemID = auctionItem.ItemID
+        packItem.ItemCount = auctionItem.Count
+        packItem.AddTime = auctionItem.AddTime
+        packItem.BidderPrice = auctionItem.BidderPrice
+        packItem.UserData = auctionItem.UserData
+        packItem.UserDataLen = auctionItem.UserDataLen
+        packItem.FamilyPlayerIDInfo = auctionItem.FamilyPlayerIDInfo
+        packItem.FamilyPlayerIDLen = auctionItem.FamilyPlayerIDLen
+        packItem.AuctionType = auctionItem.AuctionType
+        itemInfoPack.AuctionItemList.append(packItem)
+    itemInfoPack.AuctionItemCount = len(itemInfoPack.AuctionItemList)
+    
+    if curPlayer:
+        NetPackCommon.SendFakePack(curPlayer, itemInfoPack)
+    else:
+        PlayerFamily.SendFamilyFakePack(familyID, itemInfoPack)
+    return
+
+def Sync_PlayerAuctionItemInfo(curPlayer, auctionItem=None):
+    # // B5 02 拍卖行玩家拍卖中的物品信息 #tagGCPlayerAuctionItemInfo
+    
+    if auctionItem:
+        syncItemList = [auctionItem]
+    else:
+        syncItemList = GetPlayerAuctionItemList(curPlayer.GetPlayerID())
+        
+    if not syncItemList:
+        return
+    
+    itemInfoPack = ChPyNetSendPack.tagGCPlayerAuctionItemInfo()
+    itemInfoPack.AuctionItemList = []
+    for auctionItem in syncItemList:
+        packItem = ChPyNetSendPack.tagGCPlayerAuctionItem()
+        packItem.ItemGUID = auctionItem.ItemGUID
+        packItem.FamilyID = auctionItem.FamilyID
+        packItem.ItemID = auctionItem.ItemID
+        packItem.ItemCount = auctionItem.Count
+        packItem.AddTime = auctionItem.AddTime
+        packItem.BidderPrice = auctionItem.BidderPrice
+        packItem.UserData = auctionItem.UserData
+        packItem.UserDataLen = auctionItem.UserDataLen
+        itemInfoPack.AuctionItemList.append(packItem)
+    itemInfoPack.AuctionItemCount = len(itemInfoPack.AuctionItemList)
+    NetPackCommon.SendFakePack(curPlayer, itemInfoPack)
+    return
+
+def Sync_PlayerAuctionRecordInfo(curPlayer, recordType, newRecordData=None, familyID=0):
+    ## B5 03 拍卖行玩家拍卖记录 #tagGCPlayerAuctionRecordInfo
+    if recordType == AuctionRecordType_MyAuction and curPlayer:
+        syncRecordList = [newRecordData] if newRecordData else GetMyAuctionItemRecord(curPlayer.GetPlayerID())
+    elif recordType == AuctionRecordType_FamilyAuction and familyID:
+        syncRecordList = [newRecordData] if newRecordData else GetFamilyAuctionItemRecord(familyID)
+    elif recordType == AuctionRecordType_MyBid and curPlayer:
+        syncRecordList = [newRecordData] if newRecordData else GetMyBiddingItemRecord(curPlayer.GetPlayerID())
+    else:
+        return
+    
+    clientPack = ChPyNetSendPack.tagGCPlayerAuctionRecordInfo()
+    clientPack.AuctionRecordList = []
+    for recordData in syncRecordList:
+        record = ChPyNetSendPack.tagGCPlayerAuctionRecord()
+        record.ItemGUID = recordData.ItemGUID
+        record.FamilyID = recordData.FamilyID
+        record.RecordType = recordData.RecordType
+        record.RecordResult = recordData.RecordResult
+        record.RecordTime = recordData.RecordTime
+        record.BidderPrice = recordData.BidderPrice
+        record.BidderName = recordData.BidderName
+        record.ItemID = recordData.ItemID
+        record.ItemCount = recordData.Count
+        record.UserData = recordData.UserData
+        record.UserDataLen = recordData.UserDataLen
+        clientPack.AuctionRecordList.append(record)
+    clientPack.Count = len(clientPack.AuctionRecordList)
+    
+    if curPlayer:
+        NetPackCommon.SendFakePack(curPlayer, clientPack)
+    elif familyID and recordType == AuctionRecordType_FamilyAuction:
+        PlayerFamily.SendFamilyFakePack(familyID, clientPack)
+    return
+
+def Sync_PlayerBiddingItemInfo(curPlayer, auctionItem=None):
+    #// B5 10 拍卖行玩家竞价中的物品信息 #tagGCBiddingItemInfo
+    # 上线同步、玩家相关的竞价拍品竞价变更时同步
+    
+    if auctionItem:
+        syncItemList = [auctionItem]
+    else:
+        playerID = curPlayer.GetPlayerID()
+        auctionItemMgr = PyDataManager.GetAuctionItemManager()
+        nowBiddingAuctionItemList = auctionItemMgr.nowBiddingAuctionItemDict.get(playerID, [])
+        hisBiddingAuctionItemList = auctionItemMgr.hisBiddingAuctionItemDict.get(playerID, [])
+        syncItemList = nowBiddingAuctionItemList + hisBiddingAuctionItemList
+        
+    if not syncItemList:
+        return
+    
+    itemInfoPack = ChPyNetSendPack.tagGCBiddingItemInfo()
+    itemInfoPack.AuctionItemList = []
+    for auctionItem in syncItemList:
+        packItem = ChPyNetSendPack.tagGCBiddingItem()
+        packItem.ItemGUID = auctionItem.ItemGUID
+        packItem.FamilyID = auctionItem.FamilyID
+        packItem.ItemID = auctionItem.ItemID
+        packItem.ItemCount = auctionItem.Count
+        packItem.AddTime = auctionItem.AddTime
+        packItem.BidderID = auctionItem.BidderID
+        packItem.BidderPrice = auctionItem.BidderPrice
+        packItem.UserData = auctionItem.UserData
+        packItem.UserDataLen = auctionItem.UserDataLen
+        itemInfoPack.AuctionItemList.append(packItem)
+    itemInfoPack.AuctionItemCount = len(itemInfoPack.AuctionItemList)
+    NetPackCommon.SendFakePack(curPlayer, itemInfoPack)
+    return
+
+def Sync_PlayerAttentionAuctionItemID(curPlayer, isForce=False):
+    # // B5 07 拍卖行关注的物品ID #tagGCAttentionAuctionItemID
+    attentionMgr = PyDataManager.GetAuctionAttentionManager()
+    attentionItemIDList = GetPlayerAuctionAttention(attentionMgr, curPlayer.GetPlayerID())
+    if not attentionItemIDList and not isForce:
+        return
+    clientPack = ChPyNetSendPack.tagGCAttentionAuctionItemID()
+    clientPack.AttentionItemIDList = attentionItemIDList
+    clientPack.AttentionCount = len(clientPack.AttentionItemIDList)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+
+def DR_AuctionHouse(curPlayer, eventName, drDict):
+    accID = "" if not curPlayer else curPlayer.GetAccID()
+    playerID = 0 if not curPlayer else curPlayer.GetPlayerID()
+    dataDict = {"EventName":eventName, "PlayerID":playerID, "AccID":accID}
+    dataDict.update(drDict)
+    DataRecordPack.SendEventPack("AuctionHouse", dataDict, curPlayer)
+    return
 
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py
index 26de2a6..eef5bf5 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py
@@ -100,6 +100,7 @@
 import IpyGameDataPY
 import PlayerFamilyParty
 import GameWorldFamilyWar
+import AuctionHouse
 import PlayerXMZZ
 import PlayerTeam
 import PyGameData
@@ -287,6 +288,8 @@
 def __Func_HighPrecisionProcess(tick):
     if not GameWorld.GetGameWorld().GetDictByKey(ChConfig.Def_WorldKey_IsGameWorldInit):
         return
+    curTime = int(time.time())
+    
     #仙盟宴会答题定时器
     PlayerFamilyParty.FamilyParty_Process(tick)
     # 帮主弹劾时钟调用
@@ -305,6 +308,9 @@
     #操作Python表
     PlayerDBOper.PyDBProccess(tick)
     
+    #拍卖行
+    AuctionHouse.OnAuctionItemTimeProcess(curTime)
+    
     #每整分钟处理一次
     curDateTime = datetime.datetime.today()
     curMinute = curDateTime.minute
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
index adbb5c7..b159e71 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
@@ -67,6 +67,7 @@
 import GMShell
 import IPY_PlayerDefine
 import CrossRealmPK
+import AuctionHouse
 #---------------------------------------------------------------------
 
 #---------------------------------------------------------------------
@@ -149,7 +150,9 @@
         PlayerTruck.SyncPlayerTruckStartTime(curPlayer)
         #通知玩家交易所挂单情况
         PlayerBourse.OnPlayerLogin(curPlayer)
-
+        #拍卖行
+        AuctionHouse.OnPlayerLogin(curPlayer)
+        
         #上线广播
         __CheckWorldNotifyOnLogin(curPlayer, tick)
         
@@ -512,7 +515,8 @@
     PlayerGeTui.NewGuyCallBackGeTui(curPlayer, tick)
     # 设置家族成员离线时间
     SetPlayerOfflineTime(curPlayer)
-    
+    #拍卖行
+    AuctionHouse.OnPlayerLeaveServer(curPlayer)
     #------------镖车逻辑
     #TruckPlayerDisconnectProcess(curPlayer, tick)
     return
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
index 3894633..fa5e8f3 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
@@ -40,6 +40,7 @@
 import PlayerFamilySWRH
 import PlayerViewCache
 import GameWorldBoss
+import AuctionHouse
 import PlayerTalk
 import PlayerTeam
 
@@ -368,7 +369,8 @@
     PlayerFamilyBoss.NotifyFamilyBossFBInfo(jionPlayer)
     #通知家族仓库
     PyDataManager.GetFamilyStoreItemManager().SyncFamilyStoreItem(jionPlayer, curFamily.GetID())
-    
+    #仙盟拍品
+    AuctionHouse.Sync_FamilyAuctionItemInfo(jionPlayer, curFamily.GetID())
     SetMemberFightPower(familyMember, jionPlayer.GetFightPower())
     AddFamilyIDToFightPowerChangeList(curFamily.GetID())
     
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
index bf03415..fa1f1ee 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
@@ -73,6 +73,7 @@
 import CrossRealmPK
 import ChPyNetSendPack
 import NetPackCommon
+import AuctionHouse
 
 import time
 import datetime
@@ -780,6 +781,16 @@
         PlayerBourse.OnGivePlayerBourseGainsResult(curPlayer, eval(resultName))
         return
     
+    # 拍卖行
+    if callName == "AuctionHouse":
+        curPlayer = None
+        if srcPlayerID:
+            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(srcPlayerID)
+            if not curPlayer:
+                return
+        AuctionHouse.MapServer_AuctionHouseLogic(curPlayer, eval(resultName), tick)
+        return
+    
     if callName == "TeamMemFuncData":
         PlayerTeam.MapServer_TeamMemFuncData(srcPlayerID, eval(resultName))
         return
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py b/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py
index a576b7c..a5c8d44 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py
@@ -23,10 +23,13 @@
 import PyGameData
 import CrossRealmPK
 import AuctionHouse
+import PyGameDataStruct
+import CommFunc
 
 def GetSavePyData():
-    result = PyGameData.g_pyGameDataManager.GetSaveData()
-    GameWorld.DebugLog("GetSavePyData!! id = %s-%s"%(id(PyGameData.g_pyGameDataManager), len(result)))
+    pyGameDataMgr = GetPyGameDataManager()
+    result = pyGameDataMgr.GetSaveData()
+    GameWorld.DebugLog("GetSavePyData!! id = %s-%s"%(id(pyGameDataMgr), len(result)))
     result = binascii.b2a_hex(result)
     #GameWorld.DebugLog("GetSavePyData!! result = %s-%s"%(result, len(result)))
     # 字节码在C++转化会发生错误must be string without null bytes, not str,但是可以正常保存,错误会在下次调用便宜接口才会触发
@@ -35,19 +38,141 @@
 
 
 def LoadPyGameData(gameBuffer, pos):
-    PyGameData.g_pyGameDataManager = PyGameDataManager()
-    GameWorld.Log("LoadPyGameData!!id = %s %s"%(id(PyGameData.g_pyGameDataManager), len(gameBuffer)))
-    return PyGameData.g_pyGameDataManager.LoadGameData(gameBuffer, pos)
+    pyGameDataMgr = GetPyGameDataManager()
+    GameWorld.Log("LoadPyGameData!!id = %s %s"%(id(pyGameDataMgr), len(gameBuffer)))
+    return pyGameDataMgr.LoadGameData(gameBuffer, pos)
 
-
+#拍卖记录管理,该类只做数据缓存存取,不写功能逻辑,防止重读脚本时功能逻辑脚本不生效
+class AuctionRecordManager(object):
+    
+    def __init__(self):
+        self.myAuctionItemRecordDict = {} # 我的拍品记录 {playerID:[tagDBAuctionRecord, ...], ...}
+        self.myBidItemRecordDict = {} # 我的竞拍记录 {playerID:[tagDBAuctionRecord, ...], ...}
+        self.familyAuctionItemRecordDict = {} # 仙盟拍品记录 {familyID:[tagDBAuctionRecord, ...], ...}
+        return
+    
+    # 保存数据 存数据库和realtimebackup
+    def GetSaveData(self):
+        savaData = ""
+        cntData = ""
+        cnt = 0
+        
+        for recordDict in [self.myAuctionItemRecordDict, self.myBidItemRecordDict, self.familyAuctionItemRecordDict]:
+            for recordList in recordDict.values():
+                for dbData in recordList:
+                    cnt += 1
+                    savaData += dbData.getBuffer()
+                    
+        GameWorld.Log("Save AuctionRecord count :%s" % cnt)
+        return CommFunc.WriteDWORD(cntData, cnt) + savaData
+    
+    # 从数据库载入数据
+    def LoadPyGameData(self, datas, pos, dataslen):
+        cnt, pos = CommFunc.ReadDWORD(datas, pos)
+        GameWorld.Log("Load AuctionRecord count :%s" % cnt)
+        
+        for _ in xrange(cnt):
+            dbData = PyGameDataStruct.tagDBAuctionRecord()
+            dbData.clear()
+            pos += dbData.readData(datas, pos, dataslen)
+            
+            AuctionHouse.AddNewAuctionRecord(dbData)
+            
+        return pos
+    
+#拍卖关注管理,该类只做数据缓存存取,不写功能逻辑,防止重读脚本时功能逻辑脚本不生效
+class AuctionAttentionManager(object):
+    
+    def __init__(self):
+        self.playerAttentionDict = {} # 玩家关注拍卖品ID {playerID:tagDBAuctionAttention, ...}
+        return
+    
+    # 保存数据 存数据库和realtimebackup
+    def GetSaveData(self):
+        savaData = ""
+        cntData = ""
+        cnt = 0
+        
+        for dbData in self.playerAttentionDict.values():
+            cnt += 1
+            savaData += dbData.getBuffer()
+            
+        GameWorld.Log("Save AuctionAttention count :%s" % cnt)
+        return CommFunc.WriteDWORD(cntData, cnt) + savaData
+    
+    # 从数据库载入数据
+    def LoadPyGameData(self, datas, pos, dataslen):
+        cnt, pos = CommFunc.ReadDWORD(datas, pos)
+        GameWorld.Log("Load AuctionAttention count :%s" % cnt)
+        
+        for _ in xrange(cnt):
+            dbData = PyGameDataStruct.tagDBAuctionAttention()
+            dbData.clear()
+            pos += dbData.readData(datas, pos, dataslen)
+            
+            self.playerAttentionDict[dbData.PlayerID] = dbData
+            AuctionHouse.OnLoadAuctionAttentionDataEx(dbData)
+            
+        return pos
+    
+    def __InitAuctionAttentionAttrEx(self, attentionData):
+        ## 初始化拍卖关注实例附加属性
+        setattr(attentionData, "AttentionItemIDList", [] if not attentionData.AttentionInfo else eval(attentionData.AttentionInfo))
+        return
+    
+#拍卖物品数据缓存,该类只做数据缓存存取,不写功能逻辑,防止重读脚本时功能逻辑脚本不生效
+class AuctionItemManager():
+    
+    def __init__(self):
+        self.allAuctionItemDict = {} # 所有拍品字典缓存 {ItemGUID:tagDBAuctionItem, ...}
+        self.allAuctionItemByEndTimeList = [] # 根据结束时间排序的所有拍品缓存 [tagDBAuctionItem, ...]
+        self.worldAuctionItemList = [] # 全服拍品列表缓存 [tagDBAuctionItem, ...]
+        self.worldAuctionItemQueryDict = {} # 全服拍品过滤查询缓存,添加拍品时重置 {(job, (itemType, ...), itemClassLV, (itemID, ...)):[tagDBAuctionItem, ...], ...}
+        self.familyAuctionItemDict = {} # 仙盟拍品列表缓存,包含转移到全服的仙盟拍品 {familyID:[tagDBAuctionItem, ...], ...}
+        
+        self.nowBiddingAuctionItemDict = {} # 玩家当前是最高竞价的拍品,含所有拍品 {playerID:[tagDBAuctionItem, ...], ...}
+        self.hisBiddingAuctionItemDict = {} # 玩家曾经参与过竞价的拍品,含所有拍品 {playerID:[tagDBAuctionItem, ...], ...}
+        self.myAuctionItemDict = {} # 玩家拍卖中的物品 ,不包含仙盟拍品,由前端界面自行整合数据 {playerID:[tagDBAuctionItem, ...], ...}
+        self.myAttentionItemDict = {} # 玩家关注中的物品 ,不包含仙盟拍品,由前端界面自行整合数据,只保存在线的玩家,离线清除,上线不同步,由前端查询 {playerID:[tagDBAuctionItem, ...], ...}
+        return
+    
+    # 保存数据 存数据库和realtimebackup
+    def GetSaveData(self):
+        savaData = ""
+        cntData = ""
+        cnt = 0
+        
+        for dbData in self.allAuctionItemDict.values():
+            cnt += 1
+            savaData += dbData.getBuffer()
+            
+        GameWorld.Log("Save AuctionItem count :%s" % cnt)
+        return CommFunc.WriteDWORD(cntData, cnt) + savaData
+    
+    # 从数据库载入数据
+    def LoadPyGameData(self, datas, pos, dataslen):
+        cnt, pos = CommFunc.ReadDWORD(datas, pos)
+        GameWorld.Log("Load AuctionItem count :%s" % cnt)
+        
+        for _ in xrange(cnt):
+            dbData = PyGameDataStruct.tagDBAuctionItem()
+            dbData.clear()
+            pos += dbData.readData(datas, pos, dataslen)
+            
+            self.allAuctionItemDict[dbData.ItemGUID] = dbData
+            AuctionHouse.OnLoadAuctionItemDataEx(dbData)
+            
+        AuctionHouse.OnLoadAuctionItemDataOK()
+        return pos
+    
 # 个人社交相关表
 # 好友表,仇人表,最近联系人,黑名单,四张表公用的社交信息表
 
 class PyGameDataManager(object):
     def __init__(self):
-        self.AuctionAttention = AuctionHouse.AuctionAttentionManager()
-        self.AuctionRecord = AuctionHouse.AuctionRecordManager()
-        self.AuctionItem = AuctionHouse.AuctionItemManager()
+        self.AuctionAttentionManager = AuctionAttentionManager()
+        self.AuctionRecordManager = AuctionRecordManager()
+        self.AuctionItemManager = AuctionItemManager()
         self.crossPKUnNotifyOverInfo = CrossRealmPK.CrossPKUnNotifyOverInfoManager()
         self.crossPKBillboard = CrossRealmPK.CrossPKBillboardManager()
         self.XMZZManager = PlayerXMZZ.XMZZManager()
@@ -65,9 +190,9 @@
 
     def GetSaveData(self):
         buff = ""
-        buff += self.AuctionAttention.GetSaveData()
-        buff += self.AuctionRecord.GetSaveData()
-        buff += self.AuctionItem.GetSaveData()
+        buff += self.AuctionAttentionManager.GetSaveData()
+        buff += self.AuctionRecordManager.GetSaveData()
+        buff += self.AuctionItemManager.GetSaveData()
         buff += self.crossPKUnNotifyOverInfo.GetSaveData()
         buff += self.crossPKBillboard.GetSaveData()
         buff += self.XMZZManager.GetSaveData()
@@ -84,9 +209,9 @@
         return buff
     
     def LoadGameData(self, gameBuffer, pos):
-        pos = self.AuctionAttention.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
-        pos = self.AuctionRecord.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
-        pos = self.AuctionItem.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
+        pos = self.AuctionAttentionManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
+        pos = self.AuctionRecordManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
+        pos = self.AuctionItemManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
         pos = self.crossPKUnNotifyOverInfo.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
         pos = self.crossPKBillboard.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
         pos = self.XMZZManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
@@ -101,76 +226,91 @@
         pos = self.blacklistManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
         pos = self.socialInfoManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
         return pos
+    
+def GetPyGameDataManager():
+    ## py数据库表数据管理器
+    pyGameDataMgr = PyGameData.g_pyGameDataManager
+    if not pyGameDataMgr:
+        pyGameDataMgr = PyGameDataManager()
+        PyGameData.g_pyGameDataManager = pyGameDataMgr
+    return pyGameDataMgr
 
-# 拍卖记录表
-def GetAuctionAttentionManager():
-    return PyGameData.g_pyGameDataManager.AuctionAttention
-
-# 拍卖记录表
-def GetAuctionRecordManager():
-    return PyGameData.g_pyGameDataManager.AuctionRecord
-
-# 拍卖物品表
 def GetAuctionItemManager():
-    return PyGameData.g_pyGameDataManager.AuctionItem
+    ## 拍卖物品缓存数据管理
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.AuctionItemManager
+
+def GetAuctionAttentionManager():
+    # 拍卖记录表
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.AuctionAttentionManager
+
+def GetAuctionRecordManager():
+    # 拍卖记录表
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.AuctionRecordManager
 
 # 跨服竞技场未通知玩家的比赛结果
 def GetCrossPKUnNotifyOverInfoManager():
-    return PyGameData.g_pyGameDataManager.crossPKUnNotifyOverInfo
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.crossPKUnNotifyOverInfo
 
 # 跨服竞技场排行榜管理
 def GetCrossPKBillboardManager():
-    return PyGameData.g_pyGameDataManager.crossPKBillboard
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.crossPKBillboard
 
 # 仙魔之争管理
 def GetXMZZManager():
-    return PyGameData.g_pyGameDataManager.XMZZManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.XMZZManager
 
 # 封魔坛结果管理
 def GetSealDemonRecordManager():
-    return PyGameData.g_pyGameDataManager.sealDemonManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.sealDemonManager
 
 # Boss关注记录管理
 def GetBossAttentionManager():
-    return PyGameData.g_pyGameDataManager.bossAttentionManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.bossAttentionManager
 
 # 交易所物品最近成交单价管理
 def GetBourseItemLastPriceManager():
-    return PyGameData.g_pyGameDataManager.bourseItemLastPriceManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.bourseItemLastPriceManager
 
 # 交易所记录管理
 def GetBourseRecordManager():
-    return PyGameData.g_pyGameDataManager.bourseRecordManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.bourseRecordManager
 
 # 战盟仓库物品管理
 def GetFamilyStoreItemManager():
-    return PyGameData.g_pyGameDataManager.familyStoreItemManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.familyStoreItemManager
 
 # 好友系统
 def GetFriendManager():
-    return PyGameData.g_pyGameDataManager.friendManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.friendManager
 
 # 社交信息管理
 def GetPersonalSocialManager():
-    return PyGameData.g_pyGameDataManager.socialInfoManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.socialInfoManager
 
 # 仇人系统
 def GetEnemyManager():
-    return PyGameData.g_pyGameDataManager.enemyManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.enemyManager
 
 # 最近联系人系统
 def GetContactsManager():
-    return PyGameData.g_pyGameDataManager.contactsManager
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.contactsManager
 
 # 黑名单系统
 def GetBlacklistManager():
-    return PyGameData.g_pyGameDataManager.blacklistManager
-
-#===============================================================================
-# PyGameData.g_pyGameDataManager = PyGameDataManager()
-# 
-# PyGameData.g_pyGameDataManager.GetFriendManager().AddFriendBoth(3, 4)
-# PyGameData.g_pyGameDataManager.GetFriendManager().AddFriendBoth(3, 5)
-# PyGameData.g_pyGameDataManager.GetFriendManager().AddFriendBoth(3, 6)
-# PyGameData.g_pyGameDataManager.GetFriendManager().AddFriendBoth(6, 4)
-#===============================================================================
+    pyGameDataMgr = GetPyGameDataManager()
+    return pyGameDataMgr.blacklistManager
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
index 36c4ee6..ee41056 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -1103,6 +1103,22 @@
 PacketSubCMD_2=0x02
 PacketCallFunc_2=OnPlayerBuyBourseItem
 
+;拍卖行
+[PlayerAuctionHouse]
+ScriptName = Player\PlayerAuctionHouse.py
+Writer = hxp
+Releaser = hxp
+RegType = 0
+RegisterPackCount = 2
+
+PacketCMD_1=0xB5
+PacketSubCMD_1=0x13
+PacketCallFunc_1=OnSellAuctionItem
+
+PacketCMD_2=0xB5
+PacketSubCMD_2=0x14
+PacketCallFunc_2=OnBiddingAuctionItem
+
 ;法宝
 [PlayerMagicWeapon]
 ScriptName = Player\PlayerMagicWeapon.py
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index cb93207..c01cc03 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -4656,6 +4656,7 @@
 Def_Cost_CrossRealmPK, # 跨服PK
 Def_Cost_LuckyTreasure, #幸运鉴宝
 Def_Cost_MysteryShopRefresh, # 神秘商店刷新
+Def_Cost_AuctionBid, # 拍卖行竞价
 #-----------以下为暂时没用的,先不删除,如有新增消费点则放在这些之前------------
 Def_Cost_RefreshArrestTask, # 刷新悬赏任务
 Def_Cost_OffLineExp, # 兑换离线经验
@@ -4672,24 +4673,24 @@
 Def_Cost_Trade, # 交易
 Def_Cost_Rename, # 改名
 Def_Cost_SkillLvUp, # 技能升级
-) = range(2000, 2000 + 59)
+) = range(2000, 2000 + 60)
 
 Def_Cost_Reason_SonKey = "reason_name_son" # 消费点原因子类说明key
 
 # 消费返利不处理的消费类型列表
 CostRebate_DisableType = [Def_Cost_BourseBuy, Def_Cost_BourseCharge, Def_Cost_FreeGoods, Def_Cost_FamilyRedPacket,
-                          Def_Cost_Unknown, "GMSetMoney", "Warehouse"]
+                          Def_Cost_Unknown, "GMSetMoney", "Warehouse", Def_Cost_AuctionBid]
 CostProfit_CostType = [Def_Cost_BourseBuy, Def_Cost_BourseCharge, Def_Cost_Unknown,
-                       "GMSetMoney", "Warehouse"]
+                       "GMSetMoney", "Warehouse", Def_Cost_AuctionBid]
 
 # 消费VIP不处理的消费类型列表
 CostVIP_CostType = [Def_Cost_BourseBuy, Def_Cost_BourseCharge, Def_Cost_Unknown,
-                    "GMSetMoney", "Warehouse"]
+                    "GMSetMoney", "Warehouse", Def_Cost_AuctionBid]
 
 # 可在跨服中消费的类型列表
 MergeServerCanCostType = [Def_Cost_UseSpeaker, Def_Cost_Revive, Def_Cost_FBEncourage]
 #内部仙玉不能用的消费类型列表
-UnUseInner_CostType = [Def_Cost_BourseBuy, Def_Cost_FamilyRedPacket]
+UnUseInner_CostType = [Def_Cost_BourseBuy, Def_Cost_FamilyRedPacket, Def_Cost_AuctionBid]
 # 消费类型对应信息字典{消费类型:[eventName, 中文说明reason_name, 发送给9377的数据是否分消费子类], }
 # 由于9377那边数据建议所有消费点总和最好不超过500个,故这里对部分发送给对方的数据进行归组为不分子类,或直接归属于商城分组
 #     .使用原价购买的物品统一归属为商城组,消费对应为AutoBuy的也归属于商城分组
@@ -4759,8 +4760,11 @@
 Def_Cost_CrossRealmPK:"CrossRealmPK",
 Def_Cost_LuckyTreasure:"LuckyTreasure",
 Def_Cost_MysteryShopRefresh:"MysteryShopRefresh",
+Def_Cost_AuctionBid:"AuctionBid",
 }
 ## -----------------------------------------------------
+
+Def_MailMoneySource = "MoneySource" # 邮件货币来源key
 
 #游戏货币来源类型定义
 (
@@ -4791,7 +4795,9 @@
 Def_GiveMoney_FreeGoods, # 极品白拿 
 Def_GiveMoney_BindJadeWheel, # 绑玉转盘 25
 Def_GiveMoney_GatherSoulDecompose, #聚魂分解 26
-) = range(1000, 1000 + 27)
+Def_GiveMoney_AuctionBidReturn, #拍卖竞价返还
+Def_GiveMoney_AuctionGain, #拍卖获得利润
+) = range(1000, 1000 + 29)
 
 Def_Give_Reason_SonKey = "reason_name_son" # 原因子类说明key
 
@@ -4824,6 +4830,8 @@
 Def_GiveMoney_FreeGoods:"FreeGoods",
 Def_GiveMoney_BindJadeWheel:"BindJadeWheel",
 Def_GiveMoney_GatherSoulDecompose:"GatherSoulDecompose",
+Def_GiveMoney_AuctionBidReturn:"AuctionBidReturn",
+Def_GiveMoney_AuctionGain:"AuctionGain",
 }
 
 ##==================================================================================================
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/AuctionItem.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/AuctionItem.py
new file mode 100644
index 0000000..6b383e1
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/AuctionItem.py
@@ -0,0 +1,110 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package GM.Commands.AuctionItem
+#
+# @todo:拍卖行
+# @author hxp
+# @date 2019-03-04
+# @version 1.0
+#
+# 详细描述: 拍卖行
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2019-03-04 17:00"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import ItemControler
+import PlayerAuctionHouse
+import IpyGameDataPY
+
+
+#逻辑实现
+## GM命令执行入口
+#  @param curPlayer 当前玩家
+#  @param msgList 参数列表
+#  @return None
+#  @remarks 函数详细说明.
+def OnExec(curPlayer, msgList):
+    GameWorld.DebugAnswer(curPlayer, "--------- %s" % GameWorld.GetCurrentDataTimeStr())
+    if not msgList:
+        __Help(curPlayer)
+        return
+        
+    paramA = msgList[0]
+    paramCount = len(msgList)
+    playerID = curPlayer.GetPlayerID()
+    isBind = False
+    
+    # 清空拍品
+    if paramA == 0:
+        PlayerAuctionHouse.QueryGameServer_AuctionHouse(playerID, "ClearAuctionItem", [])
+        return
+    
+    # 打印拍品信息
+    if paramA == 3:
+        PlayerAuctionHouse.QueryGameServer_AuctionHouse(playerID, "PrintAuctionItem", [])
+        return
+    
+    # 添加个人拍品
+    elif paramA == 1 and paramCount >= 2:
+        itemID = msgList[1]
+        itemCount = max(1, msgList[2] if paramCount > 2 else 1)
+        auctionGroup = max(1, msgList[3] if paramCount > 3 else 1)
+        ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
+        if not ipyData:
+            GameWorld.DebugAnswer(curPlayer, "非拍卖物品无法上架")
+            return
+        auctionItemList = []
+        if itemCount < auctionGroup:
+            GameWorld.DebugAnswer(curPlayer, "物品个数不能少于组数")
+            return
+        groupItemCount = itemCount / auctionGroup
+        for i in xrange(auctionGroup):
+            if i == auctionGroup - 1:
+                groupItemCount += itemCount % auctionGroup
+            curItem = ItemControler.GetOutPutItemObj(itemID, groupItemCount, isBind)
+            auctionItemList.append([curItem])
+        PlayerAuctionHouse.__DoAddAuctionItem(curPlayer, auctionItemList)
+        
+    # 添加仙盟拍品
+    elif paramA == 2 and paramCount >= 2:
+        familyID = curPlayer.GetFamilyID()
+        if not familyID:
+            GameWorld.DebugAnswer(curPlayer, "没有仙盟无法上架仙盟拍品")
+            return
+        
+        itemID = msgList[1]
+        itemCount = max(1, msgList[2] if paramCount > 2 else 1)
+        auctionGroup = max(1, msgList[3] if paramCount > 3 else 1)
+        if itemCount < auctionGroup:
+            GameWorld.DebugAnswer(curPlayer, "物品个数不能少于组数")
+            return
+        ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
+        if not ipyData:
+            GameWorld.DebugAnswer(curPlayer, "非拍卖物品无法上架")
+            return
+        
+        familyPlayerIDList = msgList[4:] if paramCount > 4 else []
+        if playerID not in familyPlayerIDList:
+            familyPlayerIDList.append(playerID)
+            
+        familyAuctionItemList = [[itemID, itemCount, auctionGroup]]    
+        familyAuctionItemDict = {familyID:[familyPlayerIDList, familyAuctionItemList]}
+        PlayerAuctionHouse.DoAddFamilyAuctionItem(familyAuctionItemDict)
+        
+    else:
+        __Help(curPlayer)
+        return
+        
+    return
+
+def __Help(curPlayer):
+    GameWorld.DebugAnswer(curPlayer, "清空所有拍品: AuctionItem 0")
+    GameWorld.DebugAnswer(curPlayer, "添加个人拍品: AuctionItem 1 物品ID 个数 分几组")
+    GameWorld.DebugAnswer(curPlayer, "添加仙盟拍品: AuctionItem 2 物品ID 个数 分几组 受益玩家IDA 玩家IDB...")
+    return
+
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/ItemCommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/ItemCommon.py
index fc54d99..76cc7d8 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/ItemCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/UseItem/ItemCommon.py
@@ -340,6 +340,9 @@
 #  @return curSingleItem
 #  @remarks 函数详细说明.
 def CreateSingleItem(itemID, itemCount=1, isBind=1, expireTime=0):
+    if itemCount < 1:
+        GameWorld.ErrLog("创建物品个数不能少于1! itemID=%s,itemCount=%s" % (itemID, itemCount))
+        return
     curSingleItem = GameWorld.GetItemFactory().CreateItem(itemID)
     if not curSingleItem:
         return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAuctionHouse.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAuctionHouse.py
new file mode 100644
index 0000000..7159004
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAuctionHouse.py
@@ -0,0 +1,198 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package Player.PlayerAuctionHouse
+#
+# @todo:玩家拍卖行处理
+# @author hxp
+# @date 2019-03-04
+# @version 1.0
+#
+# 详细描述: 玩家拍卖行处理
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2019-03-04 17:00"""
+#-------------------------------------------------------------------------------
+import GameWorld
+import IPY_GameWorld
+import IpyGameDataPY
+import DataRecordPack
+import PlayerControl
+import ItemControler
+import ItemCommon
+import ChConfig
+
+#// B5 13 拍卖行上架拍品 #tagCMSellAuctionItem
+#
+#struct    tagCMSellAuctionItem
+#{
+#    tagHead Head;
+#    BYTE        ItemIndex;    //物品在背包中索引
+#};
+def OnSellAuctionItem(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    playerID = curPlayer.GetPlayerID()
+    itemIndex = clientData.ItemIndex
+    GameWorld.DebugLog("玩家上架拍品: itemIndex=%s" % (itemIndex), playerID)
+    playerPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
+    curItem = playerPack.GetAt(itemIndex)
+    if curItem.IsEmpty():
+        return
+    if curItem.GetIsBind():
+        GameWorld.Log("非拍品,绑定无法上架!", playerID)
+        return
+    itemGUID = curItem.GetGUID()
+    itemID = curItem.GetItemTypeID()
+    ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
+    if not ipyData:
+        return
+    
+    QueryGameServer_AuctionHouse(playerID, "AddAuctionItemQuery", [itemIndex, itemGUID, itemID])
+    return
+
+#// B5 14 拍卖行竞价物品 #tagCMBiddingAuctionItem
+#
+#struct    tagCMBiddingAuctionItem
+#{
+#    tagHead    Head;
+#    char    ItemGUID[40];
+#    WORD    BiddingPrice;    //竞价价格
+#};
+def OnBiddingAuctionItem(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    playerID = curPlayer.GetPlayerID()
+    itemGUID = clientData.ItemGUID
+    biddingPrice = clientData.BiddingPrice
+    if not PlayerControl.HaveMoney(curPlayer, IPY_GameWorld.TYPE_Price_Gold_Money, biddingPrice):
+        return
+    QueryGameServer_AuctionHouse(playerID, "BidAuctionItemQuery", [itemGUID, biddingPrice])
+    return
+
+def QueryGameServer_AuctionHouse(playerID, queryType, queryData):
+    msgInfo = str([queryType, queryData])
+    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(playerID, 0, 0, "AuctionHouse", msgInfo, len(msgInfo))
+    GameWorld.DebugLog("拍卖行发送GameServer: playerID=%s,queryType=%s,queryData=%s" % (playerID, queryType, queryData))
+    return
+
+def QueryResult_AuctionHouse(curPlayer, resultList):
+    ## 拍卖行GameServer返回处理
+    if len(resultList) != 3:
+        return
+    queryType, queryData, result = resultList
+    if queryType == "AddAuctionItemQuery":
+        itemIndex, itemGUID, itemID = queryData
+        if not result or not result[0]:
+            return
+        __DoPlayerSellAuctionItem(curPlayer, itemIndex, itemGUID, itemID)
+        
+    elif queryType == "BidAuctionItemQuery":
+        itemGUID, biddingPrice = queryData
+        if not result or not result[0]:
+            return
+        itemID = result[0]
+        __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, itemID)
+    
+    return
+
+def __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, itemID):
+    ## 玩家竞价拍品
+    
+    # 地图只处理扣除竞价价格
+    infoDict = {"ItemID":itemID, "ItemGUID":itemGUID, "BiddingPrice":biddingPrice}
+    if not PlayerControl.PayMoney(curPlayer, IPY_GameWorld.TYPE_Price_Gold_Money, biddingPrice, ChConfig.Def_Cost_AuctionBid, infoDict):
+        return
+    
+    DR_AuctionHouse(curPlayer, "PlayerPayBid", infoDict)
+    playerID = curPlayer.GetPlayerID()
+    QueryGameServer_AuctionHouse(playerID, "BidAuctionItem", [itemGUID, biddingPrice])
+    return
+
+def __DoPlayerSellAuctionItem(curPlayer, itemIndex, itemGUID, itemID):
+    ## 玩家上架拍品
+    playerID = curPlayer.GetPlayerID()
+    playerPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
+    curItem = playerPack.GetAt(itemIndex)
+    if curItem.IsEmpty():
+        return
+    if curItem.GetIsBind():
+        GameWorld.Log("非拍品,无法上架!", playerID)
+        return
+    curItemGUID = curItem.GetGUID()
+    curItemID = curItem.GetItemTypeID()
+    if itemGUID != curItemGUID or itemID != curItemID:
+        GameWorld.Log("请求上架的拍品已变化,无法上架!", playerID)
+        return
+    __DoAddAuctionItem(curPlayer, [[curItem]])
+    return
+
+def DoAddFamilyAuctionItem(familyAuctionItemDict):
+    ''' 上架仙盟拍品,因为仙盟拍品默认上架,所以使用批量上架
+    @param familyAuctionItemDict: {仙盟ID:[[享受收益的成员ID, ...], [[拍品ID,总个数,拍品组数], ...]], ...}
+    '''
+    isBind = False
+    auctionItemList = []
+    for familyID, auctionInfo in familyAuctionItemDict.items():
+        familyPlayerIDList, familyAuctionItemList = auctionInfo
+        for itemID, itemCount, auctionGroup in familyAuctionItemList:
+            ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
+            if not ipyData:
+                GameWorld.ErrLog("非拍卖物品,无法上架仙盟拍品! familyID=%s,itemID=%s,itemCount=%s" % (familyID, itemID, itemCount))
+                continue
+            groupItemCount = itemCount / auctionGroup
+            for i in xrange(auctionGroup):
+                if i == auctionGroup - 1:
+                    groupItemCount += itemCount % auctionGroup
+                curItem = ItemControler.GetOutPutItemObj(itemID, groupItemCount, isBind)
+                if not curItem:
+                    continue
+                auctionItemList.append([curItem, familyID, familyPlayerIDList])
+    __DoAddAuctionItem(None, auctionItemList)
+    return
+
+def __DoAddAuctionItem(curPlayer, auctionItemList):
+    ''' 上架拍品,支持批量上架
+    @param curPlayer: 非个人拍品时为None
+    @param auctionItemList: [[curItem], [curItem, familyID, familyPlayerIDList], ...]
+    '''
+    playerID = 0 if not curPlayer else curPlayer.GetPlayerID()
+    addAuctionItemList = []
+    for itemInfo in auctionItemList:
+        curItem = itemInfo[0]
+        familyID, familyPlayerIDList = 0, []
+        if not curPlayer and len(itemInfo) == 3:
+            familyID, familyPlayerIDList = itemInfo[1:]
+            
+        itemID = curItem.GetItemTypeID()
+        itemData = {"GUID":curItem.GetGUID(), "ItemID":itemID, "ItemCount":curItem.GetCount(), 
+                    "ItemType":curItem.GetType(), "ItemJobLimit":curItem.GetJobLimit(), "ItemClassLV":ItemCommon.GetItemClassLV(curItem),
+                    "UserData":curItem.GetUserData()}
+        
+        #扣物品
+        if curPlayer and not familyID:
+            ItemCommon.DelItem(curPlayer, curItem, curItem.GetCount())
+        else:
+            if curItem.GetIsBind():
+                GameWorld.Log("非拍品,绑定无法上架!", playerID)
+                curItem.Clear()
+                continue
+            curItem.Clear()
+            
+        drDict = {"FamilyID":familyID, "FamilyPlayerIDList":familyPlayerIDList, "ItemData":itemData}
+        DR_AuctionHouse(curPlayer, "MapClearItem", drDict)
+        
+        addAuctionItemList.append([playerID, familyID, familyPlayerIDList, itemData])
+        GameWorld.DebugLog("上架拍品: playerID=%s,familyID=%s,%s" % (playerID, familyID, drDict), playerID)
+        
+    QueryGameServer_AuctionHouse(playerID, "AddAuctionItem", addAuctionItemList)
+    return
+
+def DR_AuctionHouse(curPlayer, eventName, drDict):
+    accID = "" if not curPlayer else curPlayer.GetAccID()
+    playerID = 0 if not curPlayer else curPlayer.GetPlayerID()
+    dataDict = {"EventName":eventName,"PlayerID":playerID, "AccID":accID}
+    dataDict.update(drDict)
+    DataRecordPack.SendEventPack("AuctionHouse", dataDict, curPlayer)
+    return
+
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_AuctionHouse.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_AuctionHouse.py
new file mode 100644
index 0000000..32c3da9
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_AuctionHouse.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package Player.RemoteQuery.GY_Query_AuctionHouse
+#
+# @todo:拍卖行
+# @author hxp
+# @date 2019-03-04
+# @version 1.0
+#
+# 详细描述: 拍卖行
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2019-03-04 17:00"""
+#-------------------------------------------------------------------------------
+
+import PlayerAuctionHouse
+import GameWorld
+
+
+## 请求逻辑
+#  @param query_Type 请求类型
+#  @param query_ID 请求的玩家ID
+#  @param packCMDList 发包命令 [ ]
+#  @param tick 当前时间
+#  @return resultDisc
+def DoLogic(query_Type, query_ID, packCMDList, tick):
+    return
+    
+#---------------------------------------------------------------------
+## 执行结果
+#  @param curPlayer 发出请求的玩家
+#  @param callFunName 功能名称
+#  @param funResult 查询的结果
+#  @param tick 当前时间
+#  @return None
+#  @remarks 函数详细说明.
+def DoResult(curPlayer, callFunName, funResult, tick):
+    try:
+        funResultList = eval(funResult)
+    except:
+        GameWorld.ErrLog("GY_Query_AuctionHouse %s eval Error" % funResult, curPlayer.GetPlayerID())
+        return
+    
+    GameWorld.DebugLog("GY_Query_AuctionHouse ResultList=%s" % str(funResultList), curPlayer.GetPlayerID())
+    PlayerAuctionHouse.QueryResult_AuctionHouse(curPlayer, funResultList)
+    return
+
+
+

--
Gitblit v1.8.0