%% ---------------------------------------------------------------------------
%% @doc 拍卖行
%% @author hek
%% @since  2016-11-14
%% @deprecated
%% ---------------------------------------------------------------------------

-module (lib_auction_mod).
-include ("common.hrl").
-include ("rec_auction.hrl").
-include ("server.hrl").
-include ("def_fun.hrl").
-include ("def_module.hrl").
-include ("def_id_create.hrl").
-include ("def_timer_quest.hrl").
-include ("errcode.hrl").
-include ("predefine.hrl").
-include ("language.hrl").
-include ("def_event.hrl").
-include ("goods.hrl").
-include ("def_goods.hrl").
-compile(export_all).
-export ([
		get_delay_time/2,
		init/0,
		timer_check/1,
		start_auction_notice/2,
		start_system_auction/3,
		get_sys_goods/2,
		start_auction/2,
		add_auction_goods_db_helper/2
]).
-export ([
		close_auction/2,
		close_all_auction_gm/1
]).
-export ([
		pay_auction/2,
		restart_close_auction_timer/1,
		count_every_guild_join_num/1,
		add_personal_goods/2
]).
-export ([daily_clear/2, quit_guild/2]).
-export ([
		send_auction_catalog/2,
		send_auction_goods/2,
		send_my_auction/2,
		send_auction_log/2,
		send_auction_estimate_bonus/2
]).

%% ------------------------------- 常量配置  %% ------------------------------
%% 竞价延时(秒)
%% Time 		:: 距离物品拍卖结束时间(s)
%% DelayNum 	:: 竞价延时次数
%% @return (秒)
get_delay_time(Time, DelayNum) when Time<10 ->
	if
		DelayNum=<1 -> 60;
		DelayNum=<2 -> 30;
		true -> 10
	end;
get_delay_time(_Time, _DelayNum) -> 0.

%% ----------------------------- init function  %% ----------------------------
init() ->
	lib_auction_data:init().

%% ------------------------------ timer_check  %% -----------------------------

%% 每分钟轮询一次检测拍卖活动开始以及拍卖结束
timer_check(State) ->
	{ok, NewState}  = check_auction_status(State),
	{ok, NewState2} = check_end(NewState),
	% {ok, NewState3} = check_perosnal(NewState2),
	{ok, LastState} = check_start(NewState2),
	{ok, LastState}.

check_end(State) ->
	NowTime   = utime:unixtime(),
	#auction_state{
		auction_map 	= AuctionMap
	} = State,
	AuctionList = maps:to_list(AuctionMap),
	AuctionId = get_end_auction_id(AuctionList, NowTime),
	case AuctionId > 0 of
		true ->
			{ok, NewState} = close_auction(State, {AuctionId, 0, ?CLOSE_AUCTION_NORMAL}),
			{ok, NewState};
		false -> {ok, State}
	end.

get_end_auction_id([], _NowTime) -> 0;
get_end_auction_id([{AuctionId, #auction{auction_type = AuctionType} = Auction}|T], NowTime)
		when AuctionType=/=?AUCTION_PERSONAL ->
	EndTime = lib_auction_data:get_auction_endtime(Auction),
	case NowTime >= EndTime of
		true -> AuctionId;
		false -> get_end_auction_id(T, NowTime)
	end;
get_end_auction_id([_N|T], NowTime) ->
	get_end_auction_id(T, NowTime).

check_perosnal(State) ->
	NowTime = utime:unixtime(),
	{ok, NewState} = case get_personal_close_goods(State, NowTime) of
		CloseList when CloseList=/=[] ->
			close_personal_goods(State, CloseList);
		_ -> {ok, State}
	end,
	EndList = get_end_personal_auction(NewState),
	F = fun(Auction, {ok, AccState}) ->
		delete_auction(AccState, Auction, ?CLOSE_AUCTION_NORMAL, 0, [])
	end,
	{ok, LastState} = lists:foldl(F, {ok, NewState}, EndList),
	{ok, LastState}.

%% 关闭结束的个人拍卖，只留一个场次
%% 注：合服后会有多个场次的个人拍卖，此处关闭结束了的个人拍卖
get_end_personal_auction(State) ->
	F = fun
		(#auction{auction_type = ?AUCTION_PERSONAL, goods_num = GoodsNum} = Auction, AccList)
			when GoodsNum=<0 ->
			AuctionId = Auction#auction.auction_id,
			GoodsList = lib_auction_data:get_auction_goods(State, AuctionId),
			case GoodsList of
				[] -> [Auction]++AccList;
				_ -> AccList
			end;
		(_Auction, AccList) -> AccList
		end,
	EndAuctionList = lists:foldl(F, [], maps:values(State#auction_state.auction_map)),
	case lists:reverse(lists:keysort(#auction.auction_id, EndAuctionList)) of
		[#auction{}|T] -> T;
		_ -> []
	end.

check_start(State) ->
	NowTime   = utime:unixtime(),
	OpdaySlot = lib_auction_data:get_opday_slot(),
	CfgIdList = data_auction:cfg_id_list(OpdaySlot),
	BeforeSec = ?WORLD_NOTICE1_BEFORE,
	CfgId = get_start_cfg_id(CfgIdList, OpdaySlot, NowTime, BeforeSec),
	case CfgId > 0 of
		true -> start_system_auction(State, OpdaySlot, CfgId);
		false -> {ok, State}
	end.

get_start_cfg_id([], _OpdaySlot, _NowTime, _BeforeSec) -> 0;
get_start_cfg_id([Id|T], OpdaySlot, NowTime, BeforeSec) ->
	case data_auction:get_act(OpdaySlot, Id) of
		#base_auction_act{start_time = StartTime, end_time = EndTime}
				when EndTime > StartTime ->
			%% 	预告时间：正式开始时间减去提前时长
			FormatTime = lib_auction_data:format_time(StartTime),
			NoticeTime = FormatTime - BeforeSec,
			DiffTime   = NowTime - NoticeTime,
			%% check_start每分钟轮询一次的，加上大于0小于60, 只取一次值
			case DiffTime >=0 andalso DiffTime<60 of
				true -> Id;
				false -> get_start_cfg_id(T, OpdaySlot, NowTime, BeforeSec)
			end;
		_ -> get_start_cfg_id(T, OpdaySlot, NowTime, BeforeSec)
	end.

%% 检查拍卖会状态(拍卖预告期间服务器重启，拍卖会状态更新不及时的纠正)
check_auction_status(State) ->
	NowTime   = utime:unixtime(),
	#auction_state{auction_map = AuctionMap} = State,
	F = fun({AuctionId, Auction}, AccState) ->
		StartTime = Auction#auction.time,
		NotBegin = Auction#auction.auction_status<?AUCTION_STATUS_BEGIN,
		if
			NowTime>=StartTime+30 andalso NotBegin==true->
				update_auction(AccState, {AuctionId, ?AUCTION_STATUS_BEGIN});
			true -> AccState
		end
	end,
	NewState = lists:foldl(F, State, maps:to_list(AuctionMap)),
	{ok, NewState}.

%% 开启系统物品拍卖（crontab触发）
start_system_auction(State, OpdaySlot, CfgId) ->
	case get_sys_goods(OpdaySlot, CfgId) of
		SysGoodsList when SysGoodsList=/=[] ->
			ModuleId       = 0,
			GuildIdList    = [0],
			GuildGoodsList = [{GuildId, 0, SysGoodsList}||GuildId<-GuildIdList],
			start_auction(State, {?AUCTION_WORLD, ModuleId, CfgId, GuildGoodsList});
		_ ->
			?ERR("unkown system_auction id:~p", [CfgId]),
			{ok, State}
	end.

get_sys_goods(OpdaySlot, CfgId) ->
	F = fun
	({GoodsId, Num, Ratio}, GoodsList) ->
		case urand:rand(1, 10000)=<Ratio*100 of
			true -> [{?TYPE_GOODS, GoodsId, Num}|GoodsList];
			_-> GoodsList
		end
	end,
	case data_auction:get_act(OpdaySlot, CfgId) of
		#base_auction_act{sys_goods = SysGoods} ->	lists:foldl(F, [], SysGoods);
		_ -> []
	end.

%% 拍卖预告
start_auction_notice(State, {AuctionType, AuctionId, ?AUCTION_STATUS_NOTICE1, ModuleId, GuildGoodsList}) ->
	case AuctionType of
		?AUCTION_GUILD -> Duration = ?GUILD_NOTICE1_BEFORE;
		_ -> Duration = ?WORLD_NOTICE1_BEFORE
	end,
	spawn(fun() -> send_auction_tv(AuctionType, ?AUCTION_STATUS_NOTICE1, Duration, ModuleId, GuildGoodsList) end),
	AuctionStatus = ?AUCTION_STATUS_NOTICE2,
	NewState	  = update_auction(State, {AuctionId, AuctionStatus}),
	timer_quest:add(?UTIMER_ID(?TIMER_AUCTION, AuctionId), Duration, 0, {
		mod_auction, start_auction_notice, [{AuctionType, AuctionId, AuctionStatus, ModuleId, GuildGoodsList}] }),
	{ok, NewState};

start_auction_notice(State, {AuctionType, AuctionId, ?AUCTION_STATUS_NOTICE2, ModuleId, GuildGoodsList}) ->
	% case AuctionType of
	% 	?AUCTION_GUILD -> Duration = ?GUILD_AUCTION_DURATION;
	% 	?AUCTION_WORLD -> Duration = ?WORLD_AUCTION_DURATION
	% end,
	spawn(fun() -> send_auction_tv(AuctionType, ?AUCTION_STATUS_NOTICE2, 0, ModuleId, GuildGoodsList) end),
	AuctionStatus = ?AUCTION_STATUS_BEGIN,
	NewState	  = update_auction(State, {AuctionId, AuctionStatus}),
	%% 再次初始化活动参与记录，预防活动功能延迟调用参与记录接口的情况
	AuctionMap 	  = NewState#auction_state.auction_map,
	Auction 	  = maps:get(AuctionId, AuctionMap),
	NewJoinLogMap = lib_auction_data:init_join_log(ModuleId, NewState#auction_state.join_log_map),
	NewAuctionMap = lib_auction_data:init_auction_join_num(Auction, AuctionMap, NewJoinLogMap),
	LastState     = NewState#auction_state{auction_map = NewAuctionMap, join_log_map = NewJoinLogMap},

	Duration = lib_auction_data:get_auction_duration(Auction#auction.auction_type, Auction#auction.cfg_id),
	timer_quest:add(?UTIMER_ID(?TIMER_AUCTION, AuctionId), Duration, 0, {
		mod_auction, close_auction, [{AuctionId, 0, ?CLOSE_AUCTION_NORMAL}] }),
	{ok, LastState};

start_auction_notice(State, {AuctionType, AuctionId, AuctionStatus, _ModuleId, _GuildGoodsList}) ->
	?ERR("unkown auction_status: ~p, ~p, ~p", [AuctionType, AuctionId, AuctionStatus]),
	{ok, State}.

update_auction(State, {AuctionId, AuctionStatus}) ->
	NowTime		  = utime:unixtime(),
	AuctionMap 	  = State#auction_state.auction_map,
	Auction 	  = maps:get(AuctionId, AuctionMap, #auction{}),
	NewAuction 	  = Auction#auction{auction_status = AuctionStatus, last_time = NowTime},
	NewAuctionMap = maps:put(AuctionId, NewAuction, AuctionMap),
	lib_auction_data:update_auction_db([AuctionStatus, NowTime, AuctionId]),
	State#auction_state{auction_map = NewAuctionMap}.

send_auction_tv(?AUCTION_GUILD, AuctionStatus, Duration, ModuleId, GuildGoodsList) ->
	Minute 	   = Duration div 60,
	ModuleName = lib_auction_data:get_module_name(ModuleId),
	case AuctionStatus of
		?AUCTION_STATUS_NOTICE1 ->
			{ok, Bin} = pt_154:write(15408, [ModuleId, 1]),
			F = fun({GuildId, _TaxRate, GoodsList}) ->
				GoodsStr = util:pack_tv_goods(GoodsList),
				lib_server_send:send_to_guild(GuildId, Bin),
				lib_chat:send_TV({guild, GuildId}, ?MOD_AUCTION, 1, [ModuleName, Minute, GoodsStr])
			end,
			lists:map(F, GuildGoodsList);
		?AUCTION_STATUS_NOTICE2 ->
			{ok, Bin} = pt_154:write(15408, [ModuleId, 2]),
			F = fun({GuildId, _TaxRate, _GoodsList}) ->
				lib_server_send:send_to_guild(GuildId, Bin),
				lib_chat:send_TV({guild, GuildId}, ?MOD_AUCTION, 2, [ModuleName])
			end,
			lists:map(F, GuildGoodsList);
		_ -> skip
	end;
send_auction_tv(_AuctionType, AuctionStatus, Duration, _ModuleId, GuildGoodsList) ->
	Minute 	   = Duration div 60,
	case AuctionStatus of
		?AUCTION_STATUS_NOTICE1 ->
			F = fun({_GuildId, _TaxRate, GoodsList}) ->
				GoodsStr = util:pack_tv_goods(GoodsList),
				lib_chat:send_TV({all}, ?MOD_AUCTION, 3, [Minute, GoodsStr])
			end,
			lists:map(F, GuildGoodsList);
		?AUCTION_STATUS_NOTICE2 ->
			lib_chat:send_TV({all}, ?MOD_AUCTION, 4, []);
		_ -> skip
	end.

%% 开启拍卖
start_auction(State, {AuctionType, ModuleId, CfgId, GuildGoodsList}) ->
	{GuildGoodsList2, EmptyGuildList} = check_auction_list(GuildGoodsList),
	send_empty_auction_tv(ModuleId, EmptyGuildList),
	CloseState = close_last_auction(State, {AuctionType, ModuleId}),
	do_start_auction(CloseState, {AuctionType, ModuleId, CfgId, GuildGoodsList2}).

%% 没有获得拍卖物消息
send_empty_auction_tv(ModuleId, EmptyGuildList) ->
	ModuleName = lib_auction_data:get_module_name(ModuleId),
	F = fun(GuildId) ->
		lib_chat:send_TV({guild, GuildId}, ?MOD_AUCTION, 5, [ModuleName])
	end,
	lists:foreach(F, EmptyGuildList).

do_start_auction(State, {AuctionType, ModuleId, CfgId, GuildGoodsList}) when GuildGoodsList=/=[] ->
	?PRINT("start_auction ~p....~n", [{AuctionType, ModuleId}]),
	NowTime   			= utime:unixtime(),
	#auction_state{
		auction_map 	= AuctionMap,
		goods_map 		= GoodsMap,
		guild_map 		= GuildMap,
		world_map  		= WorldMap,
		join_log_map 	= JoinLogMap
	} = State,

	%% 添加一场拍卖记录
	AuctionStatus= ?AUCTION_STATUS_NOTICE1,
	StartTime 	= case AuctionType of   %% 开始时间需要加上预告时长
		?AUCTION_GUILD -> NowTime + ?GUILD_NOTICE1_BEFORE;
		_ -> NowTime + ?WORLD_NOTICE1_BEFORE
	end,
	LastTime    = NowTime,
	AuctionId   = mod_id_create:get_new_id(?AUCTION_ID_CREATE),
	AuctionArgs = [AuctionId, AuctionType, CfgId, ModuleId, AuctionStatus, StartTime, LastTime],
	InitAuction = lib_auction_data:make(auction, AuctionArgs),
	lib_auction_data:add_auction_db(AuctionArgs),

	%% 给拍卖场次添加拍卖物品
	Args = [AuctionId, AuctionType, CfgId, ModuleId, 0, StartTime],
	{NewGoodsMap, _Args, AddGoodsList} = lists:foldl(
			fun add_auction_goods/2, {GoodsMap, Args, []}, GuildGoodsList),
	spawn(fun() -> lib_auction_mod:add_auction_goods_db_helper(AddGoodsList, 1) end),
	lib_log_api:log_auction_produce(ModuleId, AuctionType, AuctionId, GuildGoodsList),

	%% 初始化活动参与记录，计算预计分红总额
	Auction = InitAuction#auction{goods_num = length(AddGoodsList)},
	NewAuctionMap = maps:put(AuctionId, Auction, AuctionMap),
	NewAuctionMap2= lib_auction_data:init_auction_estimate_bonus(AddGoodsList, NewAuctionMap),
	NewAuction    = maps:get(AuctionId, NewAuctionMap2),
	NewJoinLogMap = lib_auction_data:init_join_log(ModuleId, JoinLogMap),
	LastAuctionMap= lib_auction_data:init_auction_join_num(NewAuction, NewAuctionMap2, NewJoinLogMap),

	%% 重置拍卖目录
	{NewGuildMap, NewWorldMap} = lib_auction_data:reinit_goods_catalog(reinit_all, {NewGoodsMap, GuildMap, WorldMap}),
	%% 添加一个动态定时器，Duration赋值为1无特别含义
	Duration = 1,
	timer_quest:add(?UTIMER_ID(?TIMER_AUCTION, AuctionId), Duration, 0, {
		mod_auction, start_auction_notice, [{AuctionType, AuctionId, AuctionStatus, ModuleId, GuildGoodsList}] }),
	NewState = State#auction_state{
		auction_map = LastAuctionMap,
		goods_map 	= NewGoodsMap,
		guild_map 	= NewGuildMap,
		world_map 	= NewWorldMap,
		join_log_map= NewJoinLogMap
	},
	{ok, NewState};
do_start_auction(State, Args) ->
	?INFO("start_auction info:~p", [Args]),
	{ok, State}.

%% return {AccList, EmptyGuildList}
%% AccList :: [{GuildId, TaxRate, GoodsList}]  公会产出拍卖物品
%% EmptyGuildList :: [GuildId]   	  无拍卖物产出的公会列表
check_auction_list(GuildGoodsList) ->
	WorldLv = util:get_world_lv(),
	F1 = fun
	({?TYPE_GOODS, GoodsType, Num} = Object, {GuildId, AccList}) when Num> 0 ->
		case {data_auction:get_goods(GoodsType, WorldLv), data_goods_type:get(GoodsType)} of
			{#base_auction_goods{}, #ets_goods_type{}} -> {GuildId, [Object|AccList] };
			_ ->
				?ERR("unkown goods_type id:~p~n", [GoodsType]),
				{GuildId, AccList}
		end;
	(Object, {GuildId, AccList}) ->
		?ERR("unkown ObjectType:~p~n", [{GuildId, Object}]),
		{GuildId, AccList}
	end,
	F2 = fun({GuildId, TaxRate, GoodsList}, {AccList, EmptyGuildList}) ->
		{GuildId, NewGoodsList} = lists:foldl(F1, {GuildId, []}, GoodsList),
		AccList2 = [{GuildId, TaxRate, NewGoodsList} | AccList],
		AccList3 = ?IF(NewGoodsList=/=[], AccList2, AccList),
		EmptyGuildList3 = ?IF(NewGoodsList==[], [GuildId|EmptyGuildList], EmptyGuildList),
		{AccList3, EmptyGuildList3}
	end,
	lists:foldl(F2, {[], []}, GuildGoodsList).

%% 公会拍卖需要先关闭上一场
close_last_auction(State, {AuctionType, ModuleId}) ->
	LastAuction = get_last_auction(State#auction_state.auction_map, ModuleId),
	if
		AuctionType == ?AUCTION_GUILD andalso LastAuction=/=[] ->
			#auction{auction_id = CloseAuctionId} = LastAuction,
			{ok, CloseState} = close_auction(State, {CloseAuctionId, 0, ?CLOSE_AUCTION_FORCE, 0});
		true -> CloseState = State
	end,
	CloseState.

add_auction_goods({GuildId, TaxRate, GoodsList}, {GoodsMap, Args, AddGoodsList}) ->
	WorldLv = util:get_world_lv(),
	F = fun({_ObjectType, GoodsType, Num}, {Map, AddList}) ->
		add_auction_goods_helper({GuildId, TaxRate, GoodsType, Num}, {Map, Args, AddList, WorldLv})
	end,
	{NewGoodsMap, NewAddList} = lists:foldl(F, {GoodsMap, AddGoodsList}, GoodsList),
	{NewGoodsMap, Args, NewAddList}.

% add_auction_goods_helper({_GuildId, _GoodsType, 0}, {GoodsMap, _Args, AddList}) -> {GoodsMap, AddList};
add_auction_goods_helper({GuildId, TaxRate, GoodsType, GoodsNum}, {GoodsMap, Args, AddList, WorldLv}) ->
	[AuctionId, AuctionType, CfgId, ModuleId, PlayerId, StartTime|_] = Args,
	GoodsId   	= mod_id_create:get_new_id(?AUCTION_GOODS_ID_CREATE),
	InfoList    = [],
	Type 		= get_goods_type(GoodsType, WorldLv),
	{NextPrice, NowPrice} = lib_auction_data:get_price(AuctionType, GoodsType, GoodsNum, WorldLv, InfoList),
	Elem 		= [GoodsId, AuctionId, GoodsType, GoodsNum, Type, AuctionType, CfgId, GuildId, PlayerId, ModuleId,
					?GOODS_STATUS_SELL, TaxRate, StartTime, NextPrice, NowPrice, InfoList],
	AuctionGoods= lib_auction_data:make(auction_goods, Elem),
	NewGoodsMap = maps:put(GoodsId, AuctionGoods, GoodsMap),
	{NewGoodsMap, [AuctionGoods|AddList]}.
	% add_auction_goods_helper({GuildId, GoodsType, Num-1}, {NewGoodsMap, Args, [AuctionGoods|AddList] }).

add_auction_goods_db_helper([], _Num) -> skip;
add_auction_goods_db_helper([Elem|T], Num) ->
	case Num rem 20 of
		0 -> timer:sleep(300);
		_ -> skip
	end,
	#auction_goods{
		goods_id 		= GoodsId,
		auction_id 		= AuctionId,
		goods_type 		= GoodsType,
		goods_num 		= GoodsNum,
		type 			= Type,
		auction_type 	= AuctionType,
		cfg_id 			= CfgId,
		guild_id 		= GuildId,
		player_id 		= PlayerId,
		module_id 		= ModuleId,
		goods_status 	= GoodsStatus,
		bonus_tax_rate 	= TaxRate,
		start_time 		= StartTime
	} = Elem,
	ArgList	= [GoodsId, AuctionId, GoodsType, GoodsNum, Type, AuctionType, CfgId, GuildId, PlayerId, ModuleId, GoodsStatus, TaxRate, StartTime],
	lib_auction_data:add_auction_goods_db(ArgList),
	add_auction_goods_db_helper(T, Num+1).

get_goods_type(GoodsType, _WorldLv) ->
	WorldLv = 1,
	%% 注：此处将WorldLv特殊设置为1，防止缺少高世界等级的配置
	case data_auction:get_goods(GoodsType, WorldLv) of
		#base_auction_goods{type = Type} -> Type;
		_ ->
			?ERR("get_goods_type:~p~n", [{GoodsType, WorldLv}]),
			1
	end.

%% 结束：关闭所有拍卖
close_all_auction_gm(State) ->
	#auction_state{auction_map = AuctionMap} = State,
	case maps:to_list(AuctionMap) of
		[{AuctionId, #auction{} }|_] ->
			spawn(fun() -> timer:sleep(500), mod_auction:close_all_auction_gm() end),
			close_auction(State, {AuctionId, 0, ?CLOSE_AUCTION_FORCE});
		_ -> {ok, State}
	end.

%% 关闭拍卖
close_auction(State, {AuctionId, GuildId, CloseType}) ->
	close_auction(State, {AuctionId, GuildId, CloseType, 1});

%% 关闭拍卖
%% @param GuildId   公会id
%% @param CloseType 1时间到正常结束|2秘籍强制结束
%% @param ClearJoin 是否清除参与记录 0否|1是
%%        开启某活动新一场公会拍卖时，若上一场未结束的（一般GM秘籍开启的），需要关闭，但不清除参与记录
close_auction(State, {AuctionId, GuildId, CloseType, ClearJoin}) ->
	?PRINT("close_auction ~p....~n", [AuctionId]),
	timer_quest:delete(?UTIMER_ID(?TIMER_AUCTION, AuctionId)),
	case maps:get(AuctionId, State#auction_state.auction_map, []) of
		%% 个人拍卖关闭逻辑不同，此处排除
		#auction{auction_type = AuctionType} = Auction when AuctionType=/=?AUCTION_PERSONAL ->
			do_close_auction(State, Auction, GuildId, CloseType, ClearJoin);
		_ -> {ok, State}
	end.

do_close_auction(State, Auction, GuildId, CloseType, ClearJoin) ->
	NowTime = utime:unixtime(),
	#auction{auction_id = AuctionId} = Auction,
	IsAuctionEnd = NowTime>=lib_auction_data:get_auction_endtime(Auction),
	CloseList = get_close_goods(State, Auction, GuildId, CloseType, NowTime, IsAuctionEnd),
	{ok, NewState, ToWorldList1} = close_goods(State, Auction, CloseList),

	NewGoodsMap = NewState#auction_state.goods_map,
	NewAuctionMap = NewState#auction_state.auction_map,
	NewAuction = maps:get(Auction#auction.auction_id, NewAuctionMap),
	% ToWorldList = ulists:object_list_plus([ToWorldList1, NewAuction#auction.to_world_list]),
	ToWorldList = ToWorldList1 ++ NewAuction#auction.to_world_list,
	IsAllSellOut = is_all_sellout(NewGoodsMap, AuctionId),

	case {IsAuctionEnd, CloseType, GuildId} of
		%% 如果此时是公会延时的，关闭拍卖场次时同时关闭剩余其他公会拍卖
		{true, ?CLOSE_AUCTION_NORMAL, GuildId} when GuildId=/=0 ->
			CloseList2 = get_close_goods(NewGoodsMap, AuctionId, CloseList),
			{ok, NewState2, ToWorldList2} = close_goods(NewState, NewAuction, CloseList2),
			% ToWorldList3 = ulists:object_list_plus([ToWorldList, ToWorldList2]),
			ToWorldList3 = ToWorldList2 ++ ToWorldList,
			delete_auction(NewState2, NewAuction, CloseType, ClearJoin, ToWorldList3);
		{true, ?CLOSE_AUCTION_NORMAL, _} ->
			delete_auction(NewState, NewAuction, CloseType, ClearJoin, ToWorldList);
		{true, _, _} ->
			delete_auction(NewState, NewAuction, CloseType, ClearJoin, ToWorldList);
		{false, _, _} when IsAllSellOut == true ->
			delete_auction(NewState, NewAuction, CloseType, ClearJoin, ToWorldList);
		{false, ?CLOSE_AUCTION_FORCE, _} ->
			delete_auction(NewState, NewAuction, CloseType, ClearJoin, ToWorldList);
		_ ->
			LastNewAuction = NewAuction#auction{to_world_list = ToWorldList},
			LastAuctionMap = maps:put(AuctionId, LastNewAuction, NewAuctionMap),
			{ok, NewState#auction_state{auction_map = LastAuctionMap}}
	end.

%% 从GoodsMap获取符合AuctionId且排除SkipList的待关闭物品列表
get_close_goods(GoodsMap, AuctionId, SkipList) ->
	F = fun
	(#auction_goods{
		goods_id = GoodsId, auction_id = Id} = AuctionGoods, AccList) when Id==AuctionId->
		case lists:keymember(GoodsId, #auction_goods.goods_id, SkipList) of
			false -> [AuctionGoods|AccList];
			_ -> AccList
		end;
	(_, AccList) -> AccList
	end,
	lists:foldl(F, [], maps:values(GoodsMap)).

get_close_goods(State, Auction, GuildId, CloseType, NowTime, IsAuctionEnd) ->
	#auction_state{
		goods_map 		= GoodsMap
	} = State,
	#auction{
		auction_id 		= AuctionId,
		delay_guild_map = DelayGuildMap
	} = Auction,
	case CloseType of
		%正常关闭某帮派的拍卖物品
		?CLOSE_AUCTION_NORMAL when GuildId=/=0 ->
			[
				AuctionGoods||#auction_goods{auction_id = Id, guild_id = Gid}
					= AuctionGoods<-maps:values(GoodsMap), Id==AuctionId, Gid == GuildId
			];
		%%正常关闭，且整场拍卖时间结束
		?CLOSE_AUCTION_NORMAL when IsAuctionEnd==true ->
			[
				AuctionGoods||#auction_goods{auction_id = Id} =
					AuctionGoods<-maps:values(GoodsMap), Id==AuctionId
			];
		%正常关闭，关闭没延时且已结束的拍卖物品
		?CLOSE_AUCTION_NORMAL ->
			F = fun
			(#auction_goods{auction_id = Id} = AuctionGoods, AccList) when Id==AuctionId ->
				Gid = AuctionGoods#auction_goods.guild_id,
				IsGoodsEnd = NowTime >= lib_auction_data:get_goods_endtime(AuctionGoods),
				case maps:get(Gid, DelayGuildMap, {0,0}) of
					{0,0} when IsGoodsEnd==true -> [AuctionGoods|AccList];
					_ -> AccList
				end;
			(_AuctionGoods, AccList) -> AccList
			end,
			lists:foldl(F, [], maps:values(GoodsMap));
		%强制关闭整场拍卖
		?CLOSE_AUCTION_FORCE ->
			[
				AuctionGoods||#auction_goods{auction_id = Id} =
					AuctionGoods<-maps:values(GoodsMap), Id==AuctionId
			]
	end.

%% 获取个人拍卖可以关闭的物品列表
get_personal_close_goods(State, NowTime) ->
	F = fun
		(#auction_goods{auction_type = ?AUCTION_PERSONAL} = AuctionGoods, AccList) ->
			case NowTime >= lib_auction_data:get_goods_endtime(AuctionGoods) of
				true -> [AuctionGoods|AccList];
				_ -> AccList
			end;
		(_AuctionGoods, AccList) -> AccList
		end,
	lists:foldl(F, [], maps:values(State#auction_state.goods_map)).

%% 关闭列表中的拍卖物品
close_goods(State, Auction, CloseList) ->
	#auction_state{
		guild_log_map   = GuildLogMap,
		world_log_list  = WorldLogList
	} = State,
	#auction{
		auction_id 		= AuctionId,
		module_id 		= ModuleId,
		auction_type 	= AuctionType
	} = Auction,
	%% 计算发放拍卖物品，分红数量
	{NewState, AwardArgs} = calc_auction_goods_award(CloseList, State, Auction),
	{LogList, MailList, ToWorldList, GuildMoneyMap} = AwardArgs,
	{NewGuildLogMap, NewWorldLogList} = lists:foldl(
		fun(LogElem, {AccMap, AccList}) ->
			lib_auction_data:init_auction_log(LogElem, {AccMap, AccList})
		end, {GuildLogMap, WorldLogList}, LogList),
	NewState2 = NewState#auction_state{
		guild_log_map   = NewGuildLogMap, world_log_list  = NewWorldLogList},

	%% 清理物品数据，修改奖励状态
	NewState3 = clear_close_goods(NewState2, CloseList),
	LastState = update_guild_award(NewState3, Auction, GuildMoneyMap),

	%% 发放拍卖物品，分红（公会拍卖才分红）
	spawn(
		fun() ->
			mail_auction_goods(MailList, ModuleId, AuctionType),
			mail_auction_bonus(State, {AuctionType, AuctionId, ModuleId, GuildMoneyMap}),
			lib_auction_data:add_auction_log_db_batch(LogList, 1)
		end
	),
	{ok, LastState, ToWorldList}.

%% 更新公会奖励状态
update_guild_award(State, Auction, GuildMoneyMap) ->
	NowTime = utime:unixtime(),
	#auction_state{
		auction_map 	= AuctionMap
	} = State,
	#auction{
		auction_id 		= AuctionId,
		guild_award_map = GuildAwardMap
	} = Auction,
	AwardStatus	 = 1,
	GuildIdList  =  maps:keys(GuildMoneyMap),
	NewGuildAwardMap = lists:foldl(
		fun(GuildId, AccMap) -> maps:put(GuildId, AwardStatus, AccMap)
		end, GuildAwardMap, GuildIdList),
	NewAuction   = Auction#auction{guild_award_map = NewGuildAwardMap},
	NewAuctionMap= maps:put(AuctionId, NewAuction, AuctionMap),
	LastState    = State#auction_state{auction_map = NewAuctionMap},

	AuctionEndTime= lib_auction_data:get_auction_endtime(Auction),
	case NowTime<AuctionEndTime of
		true ->
			Args = [[AuctionId, GuildId, AwardStatus, NowTime]||GuildId<-GuildIdList],
			spawn(fun() -> lib_auction_data:add_guild_award_db_batch(Args, 1) end);
		false -> skip
	end,
	LastState.

%% 个人拍卖：关闭列表中的拍卖物品
close_personal_goods(State, CloseList) ->
	#auction_state{
		guild_log_map = GuildLogMap, world_log_list = WorldLogList
	} = State,
	{NewState, AwardArgs} = calc_personal_goods_award(CloseList, State),
	{LogList, MailList, CloseGoodsIdList} = AwardArgs,
	{NewGuildLogMap, NewWorldLogList} = lists:foldl(
		fun(LogElem, {AccMap, AccList}) ->
			lib_auction_data:init_auction_log(LogElem, {AccMap, AccList})
		end, {GuildLogMap, WorldLogList}, LogList),
	NewState2 = NewState#auction_state{
		guild_log_map   = NewGuildLogMap, world_log_list  = NewWorldLogList},

	%% 清理物品数据，修改奖励状态
	LastState = clear_close_goods(NewState2, CloseList),
	%% 发放拍卖物品
	spawn(fun() ->
			lib_auction_data:delete_personal_close_goods_db_batch(CloseGoodsIdList, 1),
			mail_personal_goods(MailList, 0, ?AUCTION_PERSONAL),
			lib_auction_data:add_auction_log_db_batch(LogList, 1)
		end
	),
	{ok, LastState}.

%% 清理拍卖场次
delete_auction(State, Auction, _CloseType, ClearJoin, ToWorldList) ->
	#auction_state{
		auction_map 	= AuctionMap,
		join_log_map	= JoinLogMap
	} = State,
	#auction{
		auction_id 		= AuctionId,
		module_id 		= ModuleId,
		auction_type 	= AuctionType
	} = Auction,

	NewAuctionMap= maps:remove(AuctionId, AuctionMap),
	NewJoinLogMap = case AuctionType of
		?AUCTION_GUILD when ClearJoin==1 ->
			lib_act_join_api:clear_module(ModuleId),
			maps:remove(ModuleId, JoinLogMap);
		_ -> JoinLogMap
	end,
	spawn(fun() -> lib_auction_data:delete_close_auction_goods_db(AuctionId) end),
	NewState = State#auction_state{
		auction_map  = NewAuctionMap, join_log_map = NewJoinLogMap},

	%% 公会拍卖未拍出物品，流入世界拍卖
	LastState = trans_to_world(NewState, AuctionType, ToWorldList),
	{ok,  LastState}.

%% 流入世界拍卖
trans_to_world(State, _AuctionType, []) -> State;
trans_to_world(State, ?AUCTION_GUILD, ToWorldList) ->
	%% 开启新一轮世界拍卖
	ModuleId 	  = 0,
	GoodsListArgs = [{0, 0, ToWorldList}],
	{ok, NewState} = start_auction(State, {?AUCTION_WORLD, ModuleId, ?DEFAULT_CFG_ID, GoodsListArgs}),
	NewState;
trans_to_world(State, _AuctionType, _ToList) -> State.

clear_close_goods(State, CloseList) ->
	#auction_state{
		goods_map 	= GoodsMap,
		guild_map	= GuildMap,
		world_map	= WorldMap
	} = State,
	NewGoodsMap = lists:foldl(
		fun(#auction_goods{goods_id = GoodsId}, Map) ->	maps:remove(GoodsId, Map)
		end, GoodsMap, CloseList),
	{NewGuildMap, NewWorldMap} = lib_auction_data:reinit_goods_catalog(reinit_all, {NewGoodsMap, GuildMap, WorldMap}),
	State#auction_state{
		goods_map 	= NewGoodsMap,
		guild_map	= NewGuildMap,
		world_map	= NewWorldMap
	}.

calc_auction_goods_award(CloseList, State, Auction) ->
	NowTime = utime:unixtime(),
	WorldLv = util:get_world_lv(),
	CloseArgs = {[], [], [], #{}},
	calc_auction_goods_award_helper(CloseList, State, Auction, NowTime, WorldLv, CloseArgs).

%% @return {State, {LogList, MailList, ToWorldList, GuildMoneyMap}}
%% MailList 	:: 竞拍物品附件    [{OwnerId, GoodsType, [{?TYPE_GOODS, GoodsType, GoodsNum}] }|...]
%% ToWorldList  :: 流入世界拍卖物品	[{GoodsType, 1}]
%% GuildMoneyMap:: 各公会参与拍卖资金maps，用于计算分红 #{guild_id => [{?TYPE_BGOLD, 0, Num},{?TYPE_GOLD, 0, Num}]}
calc_auction_goods_award_helper([], State, _Auction, _NowTime, _WorldLv, CloseArgs) ->
	{State, CloseArgs};
calc_auction_goods_award_helper([AuctionGoods|T], State, Auction, NowTime, WorldLv, CloseArgs) ->
	{LogList, MailList, ToWorldList, GuildMoneyMap} = CloseArgs,
	#auction{
		guild_award_map = GuildAwardMap
	} = Auction,
	#auction_goods{
		goods_id 	  = GoodsId,
		guild_id  	  = GuildId,
		goods_type 	  = GoodsType,
		goods_num 	  = GoodsNum,
		auction_type  = AuctionType,
		module_id 	  = ModuleId,
		goods_status  = GoodsStatus,
		now_price     = NowPrice,
		info_list     = InfoList
	} = AuctionGoods,
	MoneyList = maps:get(GuildId, GuildMoneyMap, []),
	AwardStatus = maps:get(GuildId, GuildAwardMap, 0),
	SellAwardRatio = lib_auction_data:get_bonus_reward_ratio(AuctionGoods),
	NoSellAwardRatio = lib_auction_data:get_no_sell_reward_ratio(),
	if
		%% 物品未拍出
		AwardStatus == 0 andalso InfoList == [] ->
			if
			%% 流入世界的，写入拍卖记录
				AuctionType == ?AUCTION_GUILD ->
					MoneyType = lib_auction_data:get_need_money_type(AuctionType, GoodsType, WorldLv),
					LogElem = [AuctionType, ModuleId, GuildId, GoodsType, GoodsNum, MoneyType, NowPrice, NowTime, 1],
					NewLogList =[LogElem|LogList];
				true -> NewLogList = LogList
			end,
			BasePrice = lib_auction_data:get_base_price(AuctionType, GoodsType, GoodsNum, WorldLv),
			Price 		 = round(BasePrice*SellAwardRatio*NoSellAwardRatio),
			AddMoneyList = [{?TYPE_BGOLD, 0, Price}],
			NewMoneyList = ulists:object_list_plus([AddMoneyList, MoneyList]),
			NewGuildMoneyMap = maps:put(GuildId, NewMoneyList, GuildMoneyMap),
			ToWorldGoodsList = [{?TYPE_GOODS, GoodsType, GoodsNum}],
			% NewToWorldList 	 = ulists:object_list_plus([ToWorldGoodsList, ToWorldList]),
			NewToWorldList = ToWorldGoodsList ++ ToWorldList,
			NewCloseArgs = {NewLogList, MailList, NewToWorldList, NewGuildMoneyMap},
			calc_auction_goods_award_helper(T, State, Auction, NowTime, WorldLv, NewCloseArgs);
		%% 物品未拍出：当一个公会拍卖结束发放了奖励，但有其它公会拍卖未结束时重启了服务器，
		%%             且所有公会都结束拍卖时，统一计算需要流入世界拍卖的物品会执行到这里
		%% 注：服务器重启后内存中的ToWorldList需要重新计算
		InfoList == [] ->
			ToWorldGoodsList = [{?TYPE_GOODS, GoodsType, GoodsNum}],
			NewToWorldList 	 = ulists:object_list_plus([ToWorldGoodsList, ToWorldList]),
			NewCloseArgs = {LogList, MailList, NewToWorldList, GuildMoneyMap},
			calc_auction_goods_award_helper(T, State, Auction, NowTime, WorldLv, NewCloseArgs);
		%% 已拍出
		AwardStatus == 0 andalso GoodsStatus == ?GOODS_STATUS_SELLOUT->
			Info = lists:keyfind(NowPrice, #auction_info.price, InfoList),
			AddMoneyList = Info#auction_info.price_list,
			AddMoneyList2 = [
				{ObjectType, ObjectGoodsId, round(ObjectNum*SellAwardRatio)}
				||{ObjectType, ObjectGoodsId, ObjectNum}<-AddMoneyList],
			NewMoneyList = ulists:object_list_plus([AddMoneyList2, MoneyList]),
			NewGuildMoneyMap = maps:put(GuildId, NewMoneyList, GuildMoneyMap),
			NewCloseArgs = {LogList, MailList, ToWorldList, NewGuildMoneyMap},
			calc_auction_goods_award_helper(T, State, Auction, NowTime, WorldLv, NewCloseArgs);
		%% 未达到一口价，最后竞拍者获得物品
		AwardStatus == 0 ->
			OwnerId = lib_auction_data:get_top_player(NowPrice, InfoList),
			MoneyType = lib_auction_data:get_need_money_type(AuctionType, GoodsType, WorldLv),
			LogElem = [AuctionType, ModuleId, GuildId, GoodsType, GoodsNum, MoneyType, NowPrice, NowTime, 0],
			NewLogList =[LogElem|LogList],
			LastMailList = [{OwnerId, GuildId, GoodsId, NowPrice, [{?TYPE_GOODS, GoodsType, GoodsNum}] }]++MailList,
			Info = lists:keyfind(NowPrice, #auction_info.price, InfoList),
			AddMoneyList = Info#auction_info.price_list,
			AddMoneyList2 = [
				{ObjectType, ObjectGoodsId, round(ObjectNum*SellAwardRatio)}
				||{ObjectType, ObjectGoodsId, ObjectNum}<-AddMoneyList],
			NewMoneyList = ulists:object_list_plus([AddMoneyList2, MoneyList]),
			NewGuildMoneyMap = maps:put(GuildId, NewMoneyList, GuildMoneyMap),
			NewCloseArgs = {NewLogList, LastMailList, ToWorldList, NewGuildMoneyMap},
			calc_auction_goods_award_helper(T, State, Auction, NowTime, WorldLv, NewCloseArgs);
		true ->
			NewCloseArgs = {LogList, MailList, ToWorldList, GuildMoneyMap},
			calc_auction_goods_award_helper(T, State, Auction, NowTime, WorldLv, NewCloseArgs)
	end.

calc_personal_goods_award(CloseList, State) ->
	NowTime = utime:unixtime(),
	WorldLv = util:get_world_lv(),
	CloseArgs = {[], [], []},
	calc_personal_goods_award_helper(CloseList, State, NowTime, WorldLv, CloseArgs).

calc_personal_goods_award_helper([], State, _NowTime, _WorldLv, CloseArgs) ->
	{State, CloseArgs};
calc_personal_goods_award_helper([AuctionGoods|T], State, NowTime, WorldLv, CloseArgs) ->
	{LogList, MailList, CloseGoodsIdList} = CloseArgs,
	#auction_state{auction_map = AuctionMap} = State,
	#auction_goods{
		goods_id = GoodsId, auction_id = AuctionId, guild_id = GuildId, player_id = PlayerId,
		goods_type = GoodsType, goods_num = GoodsNum, auction_type = AuctionType, module_id = ModuleId,
		goods_status = GoodsStatus, now_price = NowPrice, info_list = InfoList
	} = AuctionGoods,
	Auction = maps:get(AuctionId, AuctionMap),
	if
		%% 物品未拍出，返还给玩家
		InfoList == [] ->
			NewAuction = Auction#auction{goods_num = max(Auction#auction.goods_num-1, 0)},
			NewAuctionMap = maps:put(AuctionId, NewAuction, AuctionMap),
			NewState = State#auction_state{auction_map = NewAuctionMap},

			NewMailList = [{
				{to_player, fail, {PlayerId, GoodsId, GoodsType, [], [{?TYPE_GOODS, GoodsType, GoodsNum}]} },
				{to_top_player, fail, {} }
			}] ++ MailList,
			NewCloseGoodsIdList = [GoodsId|CloseGoodsIdList],
			NewCloseArgs = {LogList, NewMailList, NewCloseGoodsIdList},
			calc_personal_goods_award_helper(T, NewState, NowTime, WorldLv, NewCloseArgs);
		%% 已拍出
		GoodsStatus == ?GOODS_STATUS_SELLOUT->
			NewCloseArgs = {LogList, MailList, CloseGoodsIdList},
			calc_personal_goods_award_helper(T, State, NowTime, WorldLv, NewCloseArgs);
		%% 未达到一口价，最后竞拍者获得物品
		true ->
			NewAuction = Auction#auction{goods_num = max(Auction#auction.goods_num-1, 0)},
			NewAuctionMap = maps:put(AuctionId, NewAuction, AuctionMap),
			NewState = State#auction_state{auction_map = NewAuctionMap},

			TopPlayerId = lib_auction_data:get_top_player(NowPrice, InfoList),
			MoneyType = lib_auction_data:get_need_money_type(AuctionType, GoodsType, WorldLv),
			LogElem = [AuctionType, ModuleId, GuildId, GoodsType, GoodsNum, MoneyType, NowPrice, NowTime, 0],
			NewLogList =[LogElem|LogList],
			Info = lists:keyfind(NowPrice, #auction_info.price, InfoList),
			AddMoneyList = Info#auction_info.price_list,
			AddMoneyList2 = calc_personal_money(AddMoneyList),
			NewMailList = [{
				{to_player, success, {PlayerId, GoodsId, GoodsType, AddMoneyList2, []} },
				{to_top_player, success, {TopPlayerId, GuildId, GoodsId, NowPrice, [{?TYPE_GOODS, GoodsType, GoodsNum}]} }
			}] ++ MailList,
			NewCloseGoodsIdList = [GoodsId|CloseGoodsIdList],
			NewCloseArgs = {NewLogList, NewMailList, NewCloseGoodsIdList},
			calc_personal_goods_award_helper(T, NewState, NowTime, WorldLv, NewCloseArgs)
	end.

mail_auction_goods(MailList, ModuleId, AuctionType) ->
	mail_auction_goods_helper(MailList, ModuleId, AuctionType, 1).

%% @param ReturnList 返回格式 :: [{object_type, goods_id, num}
mail_auction_goods_helper([], _ModuleId, _AuctionType, _Num) -> skip;
mail_auction_goods_helper([{PlayerId, GuildId, GoodsId, Price, ReturnList}|T], ModuleId, AuctionType, Num) ->
	case Num rem 20 of
		0 -> timer:sleep(300);
		_ -> skip
	end,
	case ReturnList of
		%% 拍得物品
		[{?TYPE_GOODS, GoodsType, GoodsNum}|_] ->
			Title   = ?LAN_MSG(1540001),
			Content = uio:format(?LAN_MSG(1540002), [data_goods_type:get_name(GoodsType)]),
			lib_mail_api:send_auction_mail([PlayerId], ?MOD_AUCTION, Title, Content, ReturnList),
			lib_player_event:async_dispatch(PlayerId, ?HAND_OFFLINE, ?EVENT_AUCTION_SUCCESS, []),
			lib_log_api:log_auction_award(PlayerId, GuildId, ModuleId, AuctionType, GoodsId, GoodsType, GoodsNum, Price);
		_ -> skip
	end,
	mail_auction_goods_helper(T, ModuleId, AuctionType, Num+1).

%% 拍卖分红：公会拍卖才分红
%% GuildMoneyMap:: 各公会参与拍卖资金maps，用于计算分红 #{guild_id => [{?TYPE_BGOLD, 0, Num},{?TYPE_GOLD, 0, Num}]}
mail_auction_bonus(State, {?AUCTION_GUILD, AuctionId, ModuleId, GuildMoneyMap}) ->
	#auction_state{
		auction_map = AuctionMap,
		join_log_map= JoinLogMap
	} = State,
	Auction = maps:get(AuctionId, AuctionMap, #auction{}),
	JoinLog = maps:get(ModuleId, JoinLogMap, #{}),
	GuildJoinNumMap = count_every_guild_join_num(JoinLog),
	GuildBonusMap   = count_every_guild_bonus(GuildMoneyMap, GuildJoinNumMap),
	mail_bonus(JoinLog, GuildBonusMap, Auction#auction.module_id),
	ok;
mail_auction_bonus(_State, {_AuctionType, _AuctionId, _ModuleId, _MoneyList}) ->
	skip.

%% 个人拍卖：发放物品及拍卖所得
mail_personal_goods(MailList, ModuleId, AuctionType) ->
	EndTime = utime:unixtime(),
	mail_personal_goods_helper(MailList, ModuleId, AuctionType, EndTime, 1).

mail_personal_goods_helper([], _ModuleId, _AuctionType, _EndTime, _Num) -> skip;
mail_personal_goods_helper([Elem|T], ModuleId, AuctionType, EndTime, Num) ->
	case Num rem 20 of
		0 -> timer:sleep(300);
		_ -> skip
	end,
	case Elem of
		{{to_player, success, Args1}, {to_top_player, success, Args2}} ->
			{PlayerId, _GoodsId, GoodsType, MoneyList, _ResGoodsList} = Args1,
			{TopPlayerId, GuildId, GoodsId, Price, [{_ObjectType, _GoodsId, GoodsNum}|_] = GoodsList} = Args2,
			Title1   = ?LAN_MSG(1540008),
			Content1 = uio:format(?LAN_MSG(1540010), [data_goods_type:get_name(GoodsType)]),
			lib_mail_api:send_auction_mail([PlayerId], ?MOD_AUCTION, Title1, Content1, MoneyList),

			Title2   = ?LAN_MSG(1540001),
			Content2 = uio:format(?LAN_MSG(1540002), [data_goods_type:get_name(GoodsType)]),
			lib_mail_api:send_auction_mail([TopPlayerId], ?MOD_AUCTION, Title2, Content2, GoodsList),
			lib_player_event:async_dispatch(TopPlayerId, ?HAND_OFFLINE, ?EVENT_AUCTION_SUCCESS, []),
			lib_log_api:log_auction_award(TopPlayerId, GuildId, ModuleId, AuctionType, GoodsId, GoodsType, GoodsNum, Price),
			lib_log_api:log_personal_auction(PlayerId, GoodsId, GoodsType, GoodsNum, 2, 1, TopPlayerId, Price, EndTime);
		{{to_player, fail, Args1}, {to_top_player, fail, _}} ->
			{PlayerId, GoodsId, GoodsType, _MoneyList, [{_ObjectType, _GoodsId, GoodsNum}|_] = ResGoodsList} = Args1,
			Title1   = ?LAN_MSG(1540011),
			Content1 = uio:format(?LAN_MSG(1540009), [data_goods_type:get_name(GoodsType), GoodsId]),
			lib_mail_api:send_auction_mail([PlayerId], ?MOD_AUCTION, Title1, Content1, ResGoodsList),
			lib_log_api:log_personal_auction(PlayerId, GoodsId, GoodsType, GoodsNum, 2, 2, 0, 0, EndTime);
		_ -> skip
	end,
	mail_personal_goods_helper(T, ModuleId, AuctionType, EndTime, Num+1).

%% GuildMoneyMap   :: #{guild_id => [{?TYPE_BGOLD, 0, Num},{?TYPE_GOLD, 0, Num}]}
%% GuildJoinNumMap :: #{guild_id => Num}
%% return          :: #{GuildId => {Gold, Bgold}}
count_every_guild_bonus(GuildMoneyMap, GuildJoinNumMap) ->
	maps:fold(
		fun(GuildId, Num, GuildBonusMap) when Num>0 ->
			MoneyList = maps:get(GuildId, GuildMoneyMap, []),
			{Gold, Bgold} = count_award_gold(MoneyList, 0, 0),
			%% 钻石分红只取整数部分，小数部分流入绑钻四舍五入
			NewGold = round(Gold div Num),
			NewBgold  = round((Bgold / Num) + ((Gold / Num) - NewGold)),
			maps:put(GuildId, {NewGold, NewBgold}, GuildBonusMap);
		(_GuildId, _Num, GuildBonusMap) -> GuildBonusMap
		end, #{}, GuildJoinNumMap).

count_every_guild_join_num(JoinLog) ->
	count_every_guild_join_num_helper(maps:to_list(JoinLog), #{}).

count_every_guild_join_num_helper([], GuildJoinNumMap) -> GuildJoinNumMap;
count_every_guild_join_num_helper([{_PlayerId, GuildId}|T], GuildJoinNumMap) ->
	Num = maps:get(GuildId, GuildJoinNumMap, 0),
	count_every_guild_join_num_helper(T, maps:put(GuildId, Num + 1, GuildJoinNumMap)).

count_award_gold([], Gold, Bgold) -> {Gold, Bgold};
count_award_gold([{ObjectType, _GoodsId, Num}|T], Gold, Bgold) ->
	case ObjectType of
		?TYPE_GOLD  -> count_award_gold(T, Num, Bgold);
		?TYPE_BGOLD -> count_award_gold(T, Gold, Num);
		_ -> count_award_gold(T, Gold, Bgold)
	end.

%% IdList 		:: [{PlayerId, GuildId}]
%% GuildBonusMap:: #{GuildId => {Gold, Bgold}}
mail_bonus(JoinLog, GuildBonusMap, ModuleId) ->
	IdList = maps:to_list(JoinLog),
	spawn(fun() -> mail_bonus_helper(IdList, GuildBonusMap, ModuleId, 0) end).

mail_bonus_helper([], _GuildBonusMap, _ModuleId, _Num) -> skip;
mail_bonus_helper([{PlayerId, GuildId}|T], GuildBonusMap, ModuleId, Num) ->
	case Num rem 20 of
		0 -> timer:sleep(300);
		_ -> skip
	end,
	{Gold, Bgold} = maps:get(GuildId, GuildBonusMap, {0, 0}),
	Title   = ?LAN_MSG(1540005),
	ModuleName = lib_auction_data:get_module_name(ModuleId),
	Content = uio:format(?LAN_MSG(1540006), [ModuleName]),
	GoodsList = if
		Gold >0 andalso Bgold > 0  ->
			[{?TYPE_BGOLD, 0, Bgold}, {?TYPE_FGOLD, 0, Gold}];
		Gold >0 ->
			[{?TYPE_FGOLD, 0, Gold}];
		Bgold > 0 ->
			[{?TYPE_BGOLD, 0, Bgold}];
		true ->
			[]
	end,
	case GoodsList=/=[] of
		true ->
			lib_mail_api:send_auction_mail([PlayerId], ?MOD_AUCTION, Title, Content, GoodsList),
			lib_log_api:log_auction_bonus(PlayerId, GuildId, ModuleId, GoodsList);
		false -> skip
	end,
	mail_bonus_helper(T, GuildBonusMap, ModuleId, Num+1).

%% 其它玩家出价更高，竞价失败，自动返还竞价的钻石
%% 注：加价不需要返还
return_auction_cost(PayPlayerId, PriceType, #auction_goods{info_list = InfoList} = OldAuctionGoods)
	when PriceType=/=?PRICE_TYPE_ADD_PRICE andalso InfoList=/=[] ->
	#auction_goods{
		goods_id 	= GoodsId,
		auction_type= AuctionType,
		type 		= Type,
		now_price  = NowPrice,
		module_id 	= ModuleId,
		goods_type 	= GoodsType,
		goods_num 	= GoodsNum
	} = OldAuctionGoods,

	Info = lists:keyfind(NowPrice, #auction_info.price, InfoList),
	TopPlayerId = Info#auction_info.player_id,
	PriceList = Info#auction_info.price_list,
	case lib_player:is_online_global(TopPlayerId) of
		true ->
			lib_player:apply_cast(TopPlayerId, ?APPLY_CAST_SAVE, ?MODULE,
					send_return_auction_cost, [PayPlayerId, PriceList, GoodsType, GoodsId]);
			% lib_goods_api:send_reward_by_id(PriceList, auction_fail, TopPlayerId);
		false ->
			Title   = ?LAN_MSG(1540003),
			Content = uio:format(?LAN_MSG(1540004), [data_goods_type:get_name(GoodsType), GoodsId]),
			lib_mail_api:send_auction_mail([TopPlayerId], ?MOD_AUCTION, Title, Content, PriceList)
	end,
	Bgold = get_gold_num(?TYPE_BGOLD, PriceList),
	Gold = get_gold_num(?TYPE_GOLD, PriceList),
	Args = [AuctionType, Type, ModuleId, GoodsId, GoodsType, GoodsNum, Bgold, Gold],
	lib_server_send:send_to_uid(TopPlayerId, pt_154, 15404, Args),
	ok;
%% 之前没有玩家出价或者是加价类型，不用返还
return_auction_cost(_PayPlayerId, _PriceType, _OldAuctionGoods) ->
	ok.

%% 竞拍失败, 返还竞拍资金给玩家（玩家进程）
send_return_auction_cost(PS, PayPlayerId, PriceList, GoodsType, GoodsId) ->
	PlayerId = PS#player_status.id,
	Produce = #produce{type = auction_fail, reward = PriceList, remark = ulists:concat(["id:", GoodsId])},
	NewPS = lib_goods_api:send_reward(PS, Produce),
	if
		PlayerId =/= PayPlayerId ->
			%% 返还到背包后，邮件通知
			Title   = ?LAN_MSG(1540003),
			Content = uio:format(?LAN_MSG(1540007), [data_goods_type:get_name(GoodsType), GoodsId]),
			lib_mail_api:send_auction_mail([PlayerId], ?MOD_AUCTION, Title, Content, []);
		%%  PlayerId == PayPlayerId，如果是自己顶价，如先竞价再一口价直接返还竞价部分，不发邮件提醒
		true ->
			skip
	end,
	{ok, NewPS}.

get_gold_num(Type, PriceList) ->
	case lists:keyfind(Type, 1, PriceList) of
		{Type, _GoodsId, Gold} -> Gold;
		_ -> 0
	end.

%% ------------------------------- 竞拍操作 %% -------------------------------

pay_auction(State, {Cmd, PlayerId, GuildId, GoodsId, PriceType, PriceList}) ->
	{_TypeList, _goodsList, NumList} = lists:unzip3(PriceList),
	Price = lists:sum(NumList),
	#auction_state{
		finish_init = FinishInit, goods_map = GoodsMap} = State,
	AuctionGoods = maps:get(GoodsId, GoodsMap, false),
	CheckList  = [
			{pay_auction, FinishInit, PlayerId, AuctionGoods, PriceType, Price, PriceList},
			{check_pay_right, PlayerId, GuildId, AuctionGoods, PriceType},
			{cost_money, PlayerId, PriceList}
	],
	Args = {PlayerId, Cmd, GoodsId, PriceType, Price, PriceList},
	case checklist(CheckList) of
		true ->
			case do_pay_auction(State, AuctionGoods, Args) of
				{ok, NewState} ->
					%% 注：下面返还AuctionGoods为旧的AuctionGoods，不从NewState新取值
					return_auction_cost(PlayerId, PriceType, AuctionGoods),
					lib_server_send:send_to_uid(PlayerId, pt_154, Cmd, [?SUCCESS, PriceType]),
					{ok, NewState};
				{false, Res} ->
					lib_server_send:send_to_uid(PlayerId, pt_154, Cmd, [Res, PriceType]),
					{ok, State}
			end;
		{false, Res} ->
			lib_server_send:send_to_uid(PlayerId, pt_154, Cmd, [Res, PriceType]),
			{ok, State}
	end.

do_pay_auction(State, AuctionGoods, {PlayerId, _Cmd, GoodsId, PriceType, Price, PriceList} = _Args) ->
	NowTime 	= utime:unixtime(),
	WorldLv 	= util:get_world_lv(),
	%% 计算竞拍物品最新价格，竞价详细列表
	{NewPrice, NewPriceList} = cacl_pay_auction_price(AuctionGoods, PriceType, Price, PriceList),
	%%%% 更新竞拍物品竞价，竞价详细列表
	UpdatePriceArgs = {PlayerId, PriceType, NewPrice, NewPriceList, NowTime},
	{NewState, NewAuctionGoods} = update_pay_auction_price(State, AuctionGoods, UpdatePriceArgs),
	#auction_state{
		goods_map = GoodsMap, guild_map = GuildMap, world_map = WorldMap, guild_log_map = GuildLogMap,
		world_log_list= WorldLogList} = NewState,
	#auction_goods{
		type = Type, goods_type = GoodsType, goods_num = GoodsNum, auction_type = AuctionType,
		goods_status = GoodsStatus, auction_id = AuctionId, module_id = ModuleId, guild_id = GuildId,
		player_id = UpPlayerId
	} = NewAuctionGoods,

	%% 物品拍出，更新拍卖记录，拍卖目录
	case GoodsStatus of
		?GOODS_STATUS_SELLOUT when AuctionType == ?AUCTION_GUILD ->
			CataLogArgs = {GoodsMap, GuildMap, WorldMap, GuildId, ModuleId, GoodsId},
			{NewGuildMap, NewWorldMap} = lib_auction_data:reinit_goods_catalog(reinit_guild, CataLogArgs),
			{NewGuildLogMap, NewWorldLogList} = update_auction_log(GuildLogMap, WorldLogList, NewAuctionGoods, 0);
		?GOODS_STATUS_SELLOUT when AuctionType == ?AUCTION_WORLD ->
			CataLogArgs = {GoodsMap, GuildMap, WorldMap, Type, GoodsId},
			{NewGuildMap, NewWorldMap} = lib_auction_data:reinit_goods_catalog(reinit_world, CataLogArgs),
			{NewGuildLogMap, NewWorldLogList} = update_auction_log(GuildLogMap, WorldLogList, NewAuctionGoods, 0);
		?GOODS_STATUS_SELLOUT when AuctionType == ?AUCTION_PERSONAL ->
			CataLogArgs = {GoodsMap, GuildMap, WorldMap, Type, GoodsId},
			{NewGuildMap, NewWorldMap} = lib_auction_data:reinit_goods_catalog(reinit_personal_reduce, CataLogArgs),
			{NewGuildLogMap, NewWorldLogList} = update_auction_log(GuildLogMap, WorldLogList, NewAuctionGoods, 0);
		_ ->
			NewGuildMap = GuildMap, NewWorldMap = WorldMap, NewGuildLogMap = GuildLogMap, NewWorldLogList = WorldLogList
	end,
	NewState2 = NewState#auction_state{
		guild_map = NewGuildMap, world_map = NewWorldMap, guild_log_map = NewGuildLogMap, world_log_list= NewWorldLogList},

	F = fun() ->
		MoneyType = lib_auction_data:get_need_money_type(AuctionType, GoodsType, WorldLv),
		InfoDbArgs = [GoodsId, AuctionId, PlayerId, PriceType, NewPrice, util:term_to_bitstring(NewPriceList), NowTime],
		case GoodsStatus of
			?GOODS_STATUS_SELLOUT when AuctionType == ?AUCTION_PERSONAL ->
				lib_auction_data:delete_personal_close_goods_db(GoodsId),
				lib_auction_data:add_auction_log_db([AuctionType, ModuleId, GuildId, GoodsType, GoodsNum, MoneyType, NewPrice, NowTime, 0]);
			?GOODS_STATUS_SELLOUT ->
				lib_auction_data:update_auction_goods_db([GoodsStatus, NowTime, GoodsId]),
				lib_auction_data:add_auction_log_db([AuctionType, ModuleId, GuildId, GoodsType, GoodsNum, MoneyType, NewPrice, NowTime, 0]),
				lib_auction_data:add_auction_info_db(InfoDbArgs);
			_ ->
				lib_auction_data:add_auction_info_db(InfoDbArgs)
		end,
		true
    end,
    case catch db:transaction(F) of
    	true when GoodsStatus == ?GOODS_STATUS_SELLOUT andalso AuctionType == ?AUCTION_PERSONAL->
    		%% 个人拍卖拍出时，直接移除该拍卖物品记录
    		#auction_state{goods_map = NewGoodsMap2} = NewState2,
    		LastGoodsMap = maps:remove(GoodsId, NewGoodsMap2),
    		LastState = NewState2#auction_state{goods_map = LastGoodsMap},
    		notify_goods_info_to_payer(PlayerId, NewAuctionGoods),
    		lib_log_api:log_auction_pay(PlayerId, GuildId, ModuleId, AuctionType, GoodsId, GoodsType,
    			GoodsNum, PriceType, PriceList, NewPrice),

			%% 发放拍卖物品
			AddMoneyList = calc_personal_money(NewPriceList),
			Args1 = {UpPlayerId, GoodsId, GoodsType, AddMoneyList, []},
			Args2 = {PlayerId, GuildId, GoodsId, NewPrice, [{?TYPE_GOODS, GoodsType, GoodsNum}]},
			MailList = [{{to_player, success, Args1}, {to_top_player, success, Args2}}],
			mail_personal_goods(MailList, ModuleId, AuctionType),
			{ok, LastState};
    	true when GoodsStatus == ?GOODS_STATUS_SELLOUT->
    		notify_goods_info_to_payer(PlayerId, NewAuctionGoods),
    		lib_log_api:log_auction_pay(PlayerId, GuildId, ModuleId, AuctionType, GoodsId, GoodsType,
    			GoodsNum, PriceType, PriceList, NewPrice),
    		%% 本公会的拍卖物品、本场次拍卖是否全部已拍出或结束(含定时器操作:是的话直接结束，跳过竞价延时判断)
			LastState = case is_all_sellout_or_endtime(GoodsMap, AuctionId, AuctionType, GuildId) of
				true -> NewState2;
				false -> pay_auction_delay(NewState2, NewAuctionGoods)
			end,
			%% 发放拍卖物品
			MailList = [{PlayerId, GuildId, GoodsId, NewPrice, [{?TYPE_GOODS, GoodsType, GoodsNum}] }],
			mail_auction_goods(MailList, ModuleId, AuctionType),
			{ok, LastState};
		true ->
			notify_goods_info_to_payer(PlayerId, NewAuctionGoods),
			lib_log_api:log_auction_pay(PlayerId, GuildId, ModuleId, AuctionType, GoodsId, GoodsType,
				GoodsNum, PriceType, PriceList, NewPrice),
			%% 处理竞价延时，含定时器操作
			LastState = pay_auction_delay(NewState2, NewAuctionGoods),
			{ok, LastState};
    	Error ->
    		?ERR("do_pay_auction Error:~p~n", [Error]),
    		{false, ?FAIL}
    end.

%% 计算竞拍物品最新价格，竞价详细列表
cacl_pay_auction_price(AuctionGoods, PriceType, Price, PriceList) ->
	case PriceType == ?PRICE_TYPE_ADD_PRICE of
		true ->
			NowPrice      = AuctionGoods#auction_goods.now_price,
			InfoList      = AuctionGoods#auction_goods.info_list,
			AuctionInfo   = lists:keyfind(NowPrice, #auction_info.price, InfoList),
			OldPriceList  = AuctionInfo#auction_info.price_list,
			{Bgold, Gold} = count_award_gold(OldPriceList, 0, 0),
			NewPrice      = Price + Bgold + Gold,
			NewPriceList  = ulists:object_list_plus([PriceList, OldPriceList]);
		false ->
			NewPrice 	  = Price, NewPriceList = PriceList
	end,
	{NewPrice, NewPriceList}.

calc_personal_money(PriceList) ->
	[
		{ObjectType, ObjectGoodsId, round(ObjectNum* (1-?ADD_PERSONAL_CHARGE))}
		||{ObjectType, ObjectGoodsId, ObjectNum}<-PriceList
	].

%%% 更新竞拍物品竞价，竞价详细列表
update_pay_auction_price(State, AuctionGoods, {PlayerId, PriceType, NewPrice, NewPriceList, NowTime}) ->
	#auction_state{
		auction_map = AuctionMap, goods_map = GoodsMap
	} = State,
	#auction_goods{
		auction_type = AuctionType, goods_type = GoodsType, goods_num = GoodsNum,
		auction_id = AuctionId, info_list = InfoList
	} = AuctionGoods,
	WorldLv 	= util:get_world_lv(),
	MaxPrice 	= NewPrice>=lib_auction_data:get_one_price(AuctionType, GoodsType, GoodsNum, WorldLv),
	GoodsStatus = ?IF(MaxPrice, ?GOODS_STATUS_SELLOUT, ?GOODS_STATUS_SELL),
	Info 		= lib_auction_data:make(auction_info, [PlayerId, PriceType, NewPrice, NewPriceList, NowTime]),
	NewAuctionGoods = AuctionGoods#auction_goods{
		goods_status 	= GoodsStatus,
		next_price      = lib_auction_data:get_next_price(AuctionType, GoodsType, GoodsNum, WorldLv, NewPrice),
		now_price  		= NewPrice,
		info_list  		= [Info|InfoList]
	},
	NewGoodsMap = maps:put(AuctionGoods#auction_goods.goods_id, NewAuctionGoods, GoodsMap),
	NewAuctionMap = case AuctionType of
		?AUCTION_GUILD -> recount_auction_estimate_bonus(AuctionId, AuctionMap, AuctionGoods, NewPrice);
		_ -> AuctionMap
	end,
	case GoodsStatus of
		?GOODS_STATUS_SELLOUT ->
			NewAuction = maps:get(AuctionId, NewAuctionMap),
			OldGoodsNum = NewAuction#auction.goods_num,
			LastAuction = NewAuction#auction{goods_num = max(OldGoodsNum-1, 0)},
			LastAuctionMap = maps:put(AuctionId, LastAuction, NewAuctionMap);
		_ -> LastAuctionMap = NewAuctionMap
	end,
	NewState = State#auction_state{goods_map = NewGoodsMap, auction_map = LastAuctionMap},
	{NewState, NewAuctionGoods}.

%% 更新拍卖记录
update_auction_log(GuildLogMap, WorldLogList, AuctionGoods, ToWorld) ->
	NowTime = utime:unixtime(),
	#auction_goods{
		goods_type 	  = GoodsType,
		goods_num 	  = GoodsNum,
		auction_type  = AuctionType,
		module_id 	  = ModuleId,
		guild_id 	  = GuildId,
		now_price     = NowPrice
	} = AuctionGoods,
	MoneyType = lib_auction_data:get_need_money_type(AuctionType, GoodsType, util:get_world_lv()),
	LogElem = [AuctionType, ModuleId, GuildId, GoodsType, GoodsNum, MoneyType, NowPrice, NowTime, ToWorld],
	lib_auction_data:init_auction_log(LogElem, {GuildLogMap, WorldLogList}).
	%% @reuturn {NewGuildLogMap, NewWorldLogList}

%% 竞价延时，通知拍卖物价格、最高出价者信息变化
pay_auction_delay(State, AuctionGoods) ->
	NowTime = utime:unixtime(),
	#auction_state{auction_map = AuctionMap, goods_map = GoodsMap} = State,
	#auction_goods{delay_num = DelayNum} = AuctionGoods,
	NewDelayNum = DelayNum + 1,
	GoodsEndTime = lib_auction_data:get_goods_endtime(AuctionGoods),
	DiffTime= max(GoodsEndTime - NowTime, 0),
	case get_delay_time(DiffTime, NewDelayNum) of
		DelayTime when DelayTime > 0 ->
			AuctionEndTime = GoodsEndTime + DelayTime,
			NewAuctionGoods = AuctionGoods#auction_goods{delay_num  = NewDelayNum},
			{NewAuctionMap, NewGoodsMap} = do_pay_delay_core(AuctionMap, GoodsMap,
				NewAuctionGoods, AuctionEndTime, NowTime),
			notify_goods_info_to_all(NewAuctionGoods);
		_ -> {NewAuctionMap, NewGoodsMap} = {AuctionMap, GoodsMap}
	end,
	State#auction_state{
		goods_map = NewGoodsMap,
		auction_map = NewAuctionMap
	}.

%% 竞价延时
%% @return {AuctionMap, AuctionGoods}
do_pay_delay_core(AuctionMap, GoodsMap, AuctionGoods, AuctionEndTime, NowTime) ->
	#auction_goods{
		goods_id = GoodsId, auction_id = AuctionId,
		guild_id = GuildId, delay_num = GoodsDelayNum} = AuctionGoods,
	Auction = maps:get(AuctionId, AuctionMap),
	DelayGuildMap = Auction#auction.delay_guild_map,
	{DelayNum, _EndTime} = maps:get(GuildId, DelayGuildMap, {0, 0}),
	case GoodsDelayNum > DelayNum of
		true ->
			NewDelayGuildMap = maps:put(GuildId, {GoodsDelayNum, AuctionEndTime}, DelayGuildMap),
			NewAuction = Auction#auction{delay_guild_map = NewDelayGuildMap},
			start_auction_delay_timer(Auction, GuildId, AuctionEndTime, NowTime);
		false -> NewAuction = Auction
	end,
	NewAuctionMap = maps:put(AuctionId, NewAuction, AuctionMap),
	NewGoodsMap   = maps:put(GoodsId, AuctionGoods, GoodsMap),
	{NewAuctionMap, NewGoodsMap}.

%% 开启延时定时器
start_auction_delay_timer(Auction, GuildId, AuctionEndTime, NowTime) ->
	AuctionId= Auction#auction.auction_id,
	Duration = max(AuctionEndTime - NowTime, 0),
	case Auction#auction.auction_type of
		%% 公会拍卖，延时该公会拍卖物品
		?AUCTION_GUILD ->
			timer_quest:delete(?UTIMER_ID(?TIMER_AUCTION_DELAY, {AuctionId, GuildId})),
			timer_quest:add(?UTIMER_ID(?TIMER_AUCTION_DELAY, {AuctionId, GuildId}), Duration, 0,
				{mod_auction, close_auction, [{AuctionId, GuildId, ?CLOSE_AUCTION_NORMAL}] });
		%% 世界拍卖，则直接重置场次结束定时器
		?AUCTION_WORLD -> restart_close_auction_timer(Auction, Duration);
		?AUCTION_PERSONAL -> skip;
		_ -> skip
	end.

%% 重置场次结束定时器
restart_close_auction_timer(AuctionMap) ->
	%% 个人拍卖结束方式与公会拍卖、世界拍卖不同，需要排除
	FilterAuctionMap = maps:filter(
		fun(_K, #auction{auction_type = AuctionType}) ->
			AuctionType=/=?AUCTION_PERSONAL
		end, AuctionMap),

	NowTime = utime:unixtime(),
	lists:foreach(
		fun({_AuctionId, #auction{} = Auction }) ->
			AuctionEndTime = lib_auction_data:get_auction_endtime(Auction),
			Duration = max(AuctionEndTime - NowTime, 0),
			restart_close_auction_timer(Auction, Duration)
		end, maps:to_list(FilterAuctionMap)),
	ok.

%% 重置场次结束定时器
restart_close_auction_timer(Auction, Duration) ->
	AuctionId = Auction#auction.auction_id,
	timer_quest:delete(?UTIMER_ID(?TIMER_AUCTION, AuctionId)),
	timer_quest:add(?UTIMER_ID(?TIMER_AUCTION, AuctionId), Duration, 0,
		{mod_auction, close_auction, [{AuctionId, 0, ?CLOSE_AUCTION_NORMAL}] }),
	ok.

%% 重新计算预计分红总额
recount_auction_estimate_bonus(AuctionId, AuctionMap, OldAuctionGoods, NewPrice) ->
	GuildId 	  = OldAuctionGoods#auction_goods.guild_id,
	OldPrice 	  = OldAuctionGoods#auction_goods.now_price,
	Auction  	  = maps:get(AuctionId, AuctionMap),
	EstimateBonusMap = Auction#auction.estimate_bonus_map,
	EstimateBonus    = maps:get(GuildId, EstimateBonusMap, 0),

	SellAwardRatio = lib_auction_data:get_bonus_reward_ratio(OldAuctionGoods),
	NoSellAwardRatio = lib_auction_data:get_no_sell_reward_ratio(),
	NewEstimateBonus = case OldAuctionGoods#auction_goods.info_list of
		[] -> max(EstimateBonus- round(OldPrice*SellAwardRatio*NoSellAwardRatio) + round(NewPrice*SellAwardRatio), 0);
		_ -> max(EstimateBonus - round(OldPrice*SellAwardRatio) + round(NewPrice*SellAwardRatio), 0)
	end,
	NewEstimateBonusMap = maps:put(GuildId, NewEstimateBonus, EstimateBonusMap),
	NewAuction 	        = Auction#auction{estimate_bonus_map = NewEstimateBonusMap},
	maps:put(AuctionId, NewAuction, AuctionMap).

%% 本场次拍卖是否全部已拍出或结束，是的话提前结束
is_all_sellout(GoodsMap, AuctionId) ->
	GoodsList = [AuctionGoods||#auction_goods{auction_id = Id} = AuctionGoods
		<-maps:values(GoodsMap), Id==AuctionId],
	SellOutList = [AuctionGoods||#auction_goods{goods_status = GoodsStatus} = AuctionGoods
		<- GoodsList, GoodsStatus==?GOODS_STATUS_SELLOUT],
	length(SellOutList) == length(GoodsList).

%% 1.本公会的拍卖物品是否全部拍出或者结束| 2.本场次拍卖是否全部已拍出或结束
is_all_sellout_or_endtime(GoodsMap, AuctionId, AuctionType, GuildId) ->
	Duration = 2,
	NowTime = utime:unixtime(),
	GoodsList = case AuctionType of
		?AUCTION_GUILD ->
			[AuctionGoods||#auction_goods{auction_id = Id, guild_id = Gid} = AuctionGoods
				<-maps:values(GoodsMap), Id==AuctionId, Gid==GuildId];
		_ ->
			[AuctionGoods||#auction_goods{auction_id = Id} = AuctionGoods
				<-maps:values(GoodsMap), Id==AuctionId]
	end,
	SellOutList = [
		AuctionGoods||#auction_goods{goods_status = GoodsStatus} = AuctionGoods<- GoodsList,
		GoodsStatus==?GOODS_STATUS_SELLOUT orelse NowTime>=lib_auction_data:get_goods_endtime(AuctionGoods)],
	case length(SellOutList) == length(GoodsList) of
		true when AuctionType== ?AUCTION_GUILD ->
			timer_quest:add(?UTIMER_ID(?TIMER_AUCTION, AuctionId), Duration, 0,
				{mod_auction, close_auction, [{AuctionId, GuildId, ?CLOSE_AUCTION_NORMAL}] }),
			true;
		true when AuctionType== ?AUCTION_WORLD ->
			timer_quest:add(?UTIMER_ID(?TIMER_AUCTION, AuctionId), Duration, 0,
				{mod_auction, close_auction, [{AuctionId, 0, ?CLOSE_AUCTION_NORMAL}] }),
			true;
		_ -> false
	end.

notify_goods_info_to_payer(PlayerId, AuctionGoods) ->
	#auction_goods{
		goods_id 	= GoodsId,
		goods_status= GoodsStatus,
		now_price 	= NowPrice,
		next_price 	= NextPrice,
		info_list	= InfoList,
		delay_num	= DelayNum} = AuctionGoods,
	EndTime = lib_auction_data:get_goods_endtime(AuctionGoods),
	TopPlayerId = PlayerId,
	IsDelay = is_delay(DelayNum),
	IdList  = lists:foldl(
		fun(#auction_info{player_id = Pid}, IdList) ->
			?IF(lists:member(Pid, IdList)==true, IdList, [Pid|IdList])
		end, [], InfoList),
	[notify_goods_info(Pid, [GoodsId, NowPrice, NextPrice, EndTime, TopPlayerId, IsDelay, GoodsStatus])||Pid<-IdList].

notify_goods_info_to_all(AuctionGoods) ->
	{ok, Bin} = pack_notify_goods(AuctionGoods),
	lib_server_send:send_to_all(Bin).

notify_goods_info(PlayerId, AuctionGoods) ->
	{ok, Bin} = pack_notify_goods(AuctionGoods),
	lib_server_send:send_to_uid(PlayerId, Bin).

pack_notify_goods(AuctionGoods) when is_record(AuctionGoods, auction_goods)->
	#auction_goods{
		goods_id 	= GoodsId,
		goods_status= GoodsStatus,
		now_price 	= NowPrice,
		next_price 	= NextPrice,
		info_list	= InfoList,
		delay_num	= DelayNum} = AuctionGoods,
	EndTime 	= lib_auction_data:get_goods_endtime(AuctionGoods),
	TopPlayerId = lib_auction_data:get_top_player(NowPrice, InfoList),
	IsDelay     = is_delay(DelayNum),
	Args = [GoodsId, NowPrice, NextPrice, EndTime, TopPlayerId, IsDelay, GoodsStatus],
	pack_notify_goods(Args);
pack_notify_goods(AuctionGoods) when is_list(AuctionGoods) ->
	pt_154:write(15402, AuctionGoods).

%% 日清：拍卖记录，活动参与记录
daily_clear(State, {_DelaySec}) ->
	lib_auction_data:delete_auction_log_db(),
	{ok, State#auction_state{guild_log_map = #{}, world_log_list = [], join_log_map = #{} }}.

%% 退出公会，清除活动参与记录
quit_guild(State, {PlayerId}) ->
	JoinLogMap 	= State#auction_state.join_log_map,
	NewJoinLogMap = maps:fold(
		fun(ModuleId, JoinLog, Map) ->
			NewJoinLog = maps:remove(PlayerId, JoinLog),
			maps:put(ModuleId, NewJoinLog, Map)
		end,
		JoinLogMap, JoinLogMap),
	{ok, State#auction_state{join_log_map = NewJoinLogMap }}.

%% --------------------------- personal auction %% ---------------------------

%% 奖励列表类型
%%-type object_list() :: [{ObjectType::integer(), GoodsTypeId::integer(), GoodsNum::integer()}].

%% ---------------------------------------------------------------------------
%% @doc 添加个人拍卖物品
%%-spec add_personal_goods(State, {PlayerId, GoodsList}) -> {ok, NewState} when
%%	State 		:: #auction_state{},
%%  PlayerId 	:: integer(),
%%  GoodsList   :: object_list(),
%%  NewState    :: #auction_state{}.
%% ---------------------------------------------------------------------------
add_personal_goods(State, {PlayerId, PlayerName, AutoId, GoodsTypeId, GoodsList}) when State#auction_state.finish_init>0 ->
	case catch lib_player:apply_call(PlayerId, ?APPLY_CALL_SAVE, lib_auction, cost_personal_goods, [AutoId, GoodsList], 2000) of
		true -> Res = true;
		ErrorCode when is_integer(ErrorCode) -> Res = {false, ErrorCode};
		_R -> Res = {false, ?FAIL}
	end,
	case Res of
		true ->
			{NewState, AuctionId} = select_personal_auction(State),
			{ok, LastState} = add_personal_goods_core(NewState, {AuctionId, PlayerId, PlayerName, GoodsTypeId, GoodsList}),
			lib_server_send:send_to_uid(PlayerId, pt_154, 15410, [?SUCCESS, AutoId, GoodsTypeId, 1]),
			{ok, LastState};
		{false, Code} ->
			lib_server_send:send_to_uid(PlayerId, pt_154, 15410, [Code, AutoId, GoodsTypeId, 1]),
			{ok, State}
	end;
add_personal_goods(State, {PlayerId, _PlayerName, AutoId, GoodsTypeId, _GoodsList}) ->
	Code = ?ERRCODE(err154_in_init),
	lib_server_send:send_to_uid(PlayerId, pt_154, 15410, [Code, AutoId, GoodsTypeId, 1]),
	{ok, State}.

add_personal_goods_core(State, {AuctionId, PlayerId, PlayerName, GoodsTypeId, GoodsList}) ->
	NowTime = utime:unixtime(),
	#auction_state{auction_map = AuctionMap, goods_map = GoodsMap, guild_map = GuildMap,world_map = WorldMap} = State,
	Auction = maps:get(AuctionId, AuctionMap, false),
	#auction{auction_type = AuctionType, cfg_id = CfgId, module_id = ModuleId, goods_num = AuctionGoodsNum} = Auction,
	AddGoodsList = [{0, 0, GoodsList}],

	%% 给拍卖场次添加拍卖物品
	Args = [AuctionId, AuctionType, CfgId, ModuleId, PlayerId, _StartTime = NowTime],
	{NewGoodsMap, _Args, NewAddGoodsList} = lists:foldl(
			fun add_auction_goods/2, {GoodsMap, Args, []}, AddGoodsList),
	lib_auction_mod:add_auction_goods_db_helper(NewAddGoodsList, 1),
	NewAuction = Auction#auction{goods_num = AuctionGoodsNum + length(NewAddGoodsList)},
	NewAuctionMap = maps:put(AuctionId, NewAuction, AuctionMap),

	[AuctionGoods|_] = NewAddGoodsList,
	Type = AuctionGoods#auction_goods.type,
	GoodsId = AuctionGoods#auction_goods.goods_id,
	GoodsNum = AuctionGoods#auction_goods.goods_num,
	GoodsEndTime = lib_auction_data:get_goods_endtime(AuctionGoods),
	lib_log_api:log_personal_auction(PlayerId, GoodsId, GoodsTypeId, GoodsNum, 1, 0, 0, 0, GoodsEndTime),

	%% 重置拍卖目录
	CataLogArgs = {GoodsMap, GuildMap, WorldMap, Type, GoodsId},
	{NewGuildMap, NewWorldMap} = lib_auction_data:reinit_goods_catalog(reinit_personal_add, CataLogArgs),
	GoodsStr = util:pack_tv_goods(GoodsList),
	lib_chat:send_TV({all}, ?MOD_AUCTION, 6, [PlayerName, GoodsStr]),
	NewState = State#auction_state{
		auction_map = NewAuctionMap, goods_map = NewGoodsMap, guild_map = NewGuildMap, world_map = NewWorldMap},
	{ok, NewState}.

%% ---------------------------------------------------------------------------
%% @doc 选择个人拍卖场次（合服后一段时间内有若干个人拍卖场次，选择id最大的）
-spec select_personal_auction(State) -> {NewState, AuctionId} when
	State 		:: #auction_state{},
    NewState    :: #auction_state{},
    AuctionId   :: integer().
%% ---------------------------------------------------------------------------
select_personal_auction(State) ->
	#auction_state{auction_map = AuctionMap} = State,
	FilterAuctionMap = maps:filter(
		fun(_K, #auction{auction_type = AuctionType}) ->
			AuctionType==?AUCTION_PERSONAL
		end, AuctionMap),
	PersonalList = maps:values(FilterAuctionMap),
	case lists:reverse(lists:keysort(#auction.auction_id,  PersonalList)) of
		[#auction{auction_id = AuctionId}|_] -> {State, AuctionId};
		_ -> start_personal_auction(State)
	end.

start_personal_auction(State) ->
	NowTime   	= utime:unixtime(),
	#auction_state{auction_map = AuctionMap} = State,
	AuctionId   = mod_id_create:get_new_id(?AUCTION_ID_CREATE),
	AuctionStatus = ?AUCTION_STATUS_BEGIN,
	AuctionArgs = [AuctionId, ?AUCTION_PERSONAL, _CfgId = 0, _ModuleId = 0, AuctionStatus, _StartTime = NowTime, _LastTime = NowTime],
	Auction     = lib_auction_data:make(auction, AuctionArgs),
	NewAuctionMap = maps:put(AuctionId, Auction, AuctionMap),
	lib_auction_data:add_auction_db(AuctionArgs),
	NewState = State#auction_state{auction_map = NewAuctionMap},
	{NewState, AuctionId}.

%% -------------------------- pp handle function %% --------------------------

%% 获取拍卖菜单
send_auction_catalog(State, {Cmd, PlayerId, GuildId}) ->
	NowTime = utime:unixtime(),
	#auction_state{
		goods_map = GoodsMap,
		guild_map = GuildMap,
		world_map = WorldMap} = State,
	ModuleList = maps:get(GuildId, GuildMap, []),
	WorldList  = maps:to_list(WorldMap),

	PackModuleList = lists:foldl(
		%% 物品数量不具体计算，客户端用不上，便于扩展，暂设为1
		fun({ModuleId, GoodsList}, AccList) when GoodsList=/=[] ->
			case get_catalog_goods(GoodsList, GoodsMap, GoodsList, NowTime) of
				[] -> AccList;
				_ -> [{ModuleId, 1}|AccList]
			end;
		({_ModuleId, _GoodsList}, AccList) -> AccList
		end, [], ModuleList),

	PackWorldList = lists:foldl(
		fun({Type, GoodsList}, AccList) when GoodsList=/=[] ->
			case get_catalog_goods(GoodsList, GoodsMap, [], NowTime) of
				[] -> AccList;
				_ -> [{Type, 1}|AccList]
			end;
		({_Type, _GoodsList}, AccList) -> AccList
		end, [], WorldList),
	lib_server_send:send_to_uid(PlayerId, pt_154, Cmd, [PackModuleList, PackWorldList]),
	ok.

get_catalog_goods([], _GoodsMap, GoodsList, _NowTime) -> GoodsList;
get_catalog_goods([GoodsId|T], GoodsMap, GoodsList, NowTime) ->
	case maps:get(GoodsId, GoodsMap, false) of
		#auction_goods{
			auction_type 	= AuctionType,
			start_time 		= StartTime,
			goods_status 	= GoodsStatus
		} = AuctionGoods when GoodsStatus ==?GOODS_STATUS_SELL ->
			EndTime  = lib_auction_data:get_goods_endtime(AuctionGoods),
			%% 公会拍卖物品列表筛选条件：物品待拍出，未到结束时间
			%% 世界拍卖/个人拍卖物品列表筛选条件：物品待拍出，到了开始时间且未到结束时间
			case (AuctionType == ?AUCTION_GUILD andalso NowTime < EndTime) orelse
				 (AuctionType == ?AUCTION_WORLD andalso NowTime < EndTime andalso NowTime>StartTime) orelse
				 (AuctionType == ?AUCTION_PERSONAL andalso NowTime < EndTime andalso NowTime>StartTime)
			of
				true -> get_catalog_goods(T, GoodsMap, [GoodsId|GoodsList], NowTime);
				false -> get_catalog_goods(T, GoodsMap, GoodsList, NowTime)
			end;
		_ -> get_catalog_goods(T, GoodsMap, GoodsList, NowTime)
	end.

%% 获取拍卖物品
send_auction_goods(State, {Cmd, PlayerId, GuildId, AuctionType, Type, ModuleId})  ->
	NowTime = utime:unixtime(),
	#auction_state{
		auction_map = AuctionMap,
		goods_map 	= GoodsMap,
		guild_map 	= GuildMap,
		world_map 	= WorldMap} = State,

	GoodsIdList= if
		%% ModuleId为0表示获取该公会拍卖全部拍卖物
		AuctionType == ?AUCTION_GUILD andalso ModuleId == 0 ->
			ModuleList = maps:get(GuildId, GuildMap, []),
			lists:foldl(
				fun({_ModuleId, L}, AccList) -> L ++ AccList
				end, [], ModuleList);
		AuctionType == ?AUCTION_GUILD ->
			ModuleList = maps:get(GuildId, GuildMap, []),
			case lists:keyfind(ModuleId, 1, ModuleList) of
				false -> [];
				{ModuleId, L} -> L
			end;
		%% Type为0表示获取世界拍卖/个人拍卖全部拍卖物
		(AuctionType == ?AUCTION_WORLD orelse
			AuctionType == ?AUCTION_PERSONAL) andalso Type == 0 ->
			maps:fold(
				fun(_Type, List, AccList) -> List ++ AccList
				end, [], WorldMap);
		(AuctionType == ?AUCTION_WORLD orelse
			AuctionType == ?AUCTION_PERSONAL) ->
			maps:get(Type, WorldMap, [])
	end,

	WorldLv   = util:get_world_lv(),
	GoodsList =	lists:foldl(
		fun(GoodsId, Acc) ->
			case maps:get(GoodsId, GoodsMap, false) of
				false -> Acc;
				Goods -> pack_goods(NowTime, Goods, WorldLv, Acc)
			end
		end, [], GoodsIdList),

	%% 处理公会拍卖处于预告状态时显示起拍时间
	{AuctionStatus, StartTime} = case AuctionType==?AUCTION_GUILD andalso GoodsList=/=[] of
		true -> pack_auction_status(AuctionMap, ModuleId);
		false -> {0, 0}
	end,
	Args = [AuctionType, AuctionStatus, StartTime, GoodsList],
	lib_server_send:send_to_uid(PlayerId, pt_154, Cmd, Args),
	ok.

pack_auction_status(AuctionMap, ModuleId) ->
	case get_last_auction(AuctionMap, ModuleId) of
		#auction{
			auction_status = AuctionStatus,
			time = StartTime
		} -> skip;
		_ -> AuctionStatus = 0, StartTime = 0
	end,
	{AuctionStatus, StartTime}.

%% 获取上一场活动id为ModuleId的公会拍卖
get_last_auction(AuctionMap, ModuleId) ->
	FilterAuctionMap = maps:filter(
		fun(_K, #auction{module_id = Mid}) ->
			Mid==ModuleId end, AuctionMap),
	AuctionList = lists:sort(
		fun({AuctionIdA, _AuctionA}, {AuctionIdB, _AuctionB}) ->
			AuctionIdA>AuctionIdB
		end, maps:to_list(FilterAuctionMap)),
	case AuctionList of
		[{_AuctionId, Auction}|_] -> Auction;
		_ -> []
	end.

pack_goods(NowTime, #auction_goods{
		goods_status = GoodsStatus} = AuctionGoods, WorldLv, Acc) when GoodsStatus ==?GOODS_STATUS_SELL ->
	#auction_goods{
		goods_id 		= GoodsId,
		goods_type 		= GoodsType,
		goods_num 		= GoodsNum,
		auction_type 	= AuctionType,
		start_time 		= StartTime,
		next_price 		= NextPrice,
		now_price 		= NowPrice,
		info_list     	= InfoList,
		delay_num		= DelayNum,
		player_id 		= PlayerId,
		bonus_tax_rate  = TaxRate
	} = AuctionGoods,
	EndTime  = lib_auction_data:get_goods_endtime(AuctionGoods),

	%% 公会拍卖物品列表筛选条件：物品待拍出，未到结束时间
	%% 世界拍卖/个人拍卖物品列表筛选条件：物品待拍出，到了开始时间且未到结束时间
	case (AuctionType == ?AUCTION_GUILD andalso NowTime < EndTime) orelse
		 (AuctionType == ?AUCTION_WORLD andalso NowTime < EndTime andalso NowTime>StartTime) orelse
		 (AuctionType == ?AUCTION_PERSONAL andalso NowTime < EndTime andalso NowTime>StartTime)
	of
		true ->
			MoneyType = lib_auction_data:get_need_money_type(AuctionType, GoodsType, WorldLv),
			Price    = lib_auction_data:get_one_price(AuctionType, GoodsType, GoodsNum, WorldLv),
			TopPlayerId = lib_auction_data:get_top_player(NowPrice, InfoList),
			IsDelay  = is_delay(DelayNum),
			[{GoodsId, GoodsType, GoodsNum, MoneyType, NowPrice, NextPrice, Price, EndTime, PlayerId, TopPlayerId, IsDelay, TaxRate}|Acc];
		false -> Acc
	end;
pack_goods(_NowTime, _AuctionGoods, _WorldLv, Acc) -> Acc.

is_delay(DelayNum) when DelayNum>0 -> 1;
is_delay(_DelayNum)  -> 0.

%% 我的竞拍
send_my_auction(State, {Cmd, PlayerId, Type}) ->
	PackList = get_my_auction(State, PlayerId, Type),
	lib_server_send:send_to_uid(PlayerId, pt_154, Cmd, [Type, PackList]),
	ok.

get_my_auction(State, PlayerId, Type) when Type == 1 ->
	NowTime = utime:unixtime(),
	WorldLv = util:get_world_lv(),
	#auction_state{goods_map = GoodsMap} = State,
	GoodsList = lists:foldl(
		fun({_K, Goods}, Acc) ->
			List = Goods#auction_goods.info_list,
			case lists:keyfind(PlayerId, #auction_info.player_id, List) of
				false -> Acc;
				_ -> [Goods|Acc]
			end
		end, [], maps:to_list(GoodsMap)),
	lists:foldl(
		fun(Goods, Acc) -> pack_goods(NowTime, Goods, WorldLv, Acc)
		end, [], GoodsList);
get_my_auction(State, PlayerId, Type) when Type == 2 ->
	NowTime = utime:unixtime(),
	WorldLv = util:get_world_lv(),
	#auction_state{goods_map = GoodsMap} = State,
	GoodsList = lists:foldl(
		fun({_K, Goods}, Acc) ->
			case Goods#auction_goods.player_id==PlayerId of
				true -> [Goods|Acc];
				_ -> Acc
			end
		end, [], maps:to_list(GoodsMap)),
	lists:foldl(
		fun(Goods, Acc) -> pack_goods(NowTime, Goods, WorldLv, Acc)
		end, [], GoodsList);
get_my_auction(State, PlayerId, Type) when Type == 3 ->
	NowTime = utime:unixtime(),
	WorldLv = util:get_world_lv(),
	#auction_state{goods_map = GoodsMap} = State,
	GoodsList = maps:to_list(GoodsMap),
	FilterGoodsList1 = lists:foldl(
		fun({_K, Goods}, Acc) ->
			List = Goods#auction_goods.info_list,
			case lists:keyfind(PlayerId, #auction_info.player_id, List) of
				false -> Acc;
				_ -> [Goods|Acc]
			end
		end, [], GoodsList),
	FilterGoodsList2 = lists:foldl(
		fun({_K, Goods}, Acc) ->
			case Goods#auction_goods.player_id==PlayerId of
				true -> [Goods|Acc];
				_ -> Acc
			end
		end, [], GoodsList),
	{PackList, IdList} = lists:foldl(
		fun(Goods, {AccPackList, AccIdList}) ->
			NewAccPackList = pack_goods(NowTime, Goods, WorldLv, AccPackList),
			NewAccIdList = [Goods#auction_goods.goods_id | AccIdList],
			{NewAccPackList, NewAccIdList}
		end, {[], []}, FilterGoodsList1),
	lists:foldl(
		fun(Goods, Acc) ->
			%% 我的竞拍与我的拍卖存在相同部分，此处去重
			case lists:member(Goods#auction_goods.goods_id, IdList) of
				false -> pack_goods(NowTime, Goods, WorldLv, Acc);
				_ -> Acc
			end
		end, PackList, FilterGoodsList2).

%% 拍卖记录
send_auction_log(State, {Cmd, PlayerId, GuildId, AuctionType}) ->
	#auction_state{guild_log_map  = GuildLogMap, world_log_list = WorldLogList} = State,
	LogList = case AuctionType of
		?AUCTION_GUILD -> maps:get(GuildId, GuildLogMap, []);
		_ -> WorldLogList
	end,
	List = [
		{Log#auction_log.module_id, Log#auction_log.time, Log#auction_log.goods_type,
		Log#auction_log.goods_num, max(Log#auction_log.money_type, ?TYPE_GOLD),
		Log#auction_log.price, Log#auction_log.to_world }||Log<-LogList ],
	lib_server_send:send_to_uid(PlayerId, pt_154, Cmd, [AuctionType, List]),
	ok.

%% 获取预计分红
send_auction_estimate_bonus(State, {Cmd, PlayerId, GuildId}) ->
	AverageBonus = get_auction_estimate_bonus(State, PlayerId, GuildId),
	lib_server_send:send_to_uid(PlayerId, pt_154, Cmd, [AverageBonus]),
	ok.

get_auction_estimate_bonus(State, PlayerId, GuildId) ->
	#auction_state{
		auction_map = AuctionMap, join_log_map= JoinLogMap} = State,
	AverageBonus =  lists:foldl(
		fun({_AuctionId, #auction{auction_type = ?AUCTION_GUILD} = Auction}, AccNum) ->
			#auction{
				module_id 			= ModuleId,
				guild_award_map 	= GuildAwardMap,
				estimate_bonus_map 	= EstimateBonusMap,
				act_join_num_map 	= JoinNumMap
			} = Auction,
			JoinLog 	  = maps:get(ModuleId, JoinLogMap, #{}),
			IsJoin 		  =	maps:get(PlayerId, JoinLog, []),
			JoinNum       = maps:get(GuildId, JoinNumMap, 0),
			EstimateBonus = maps:get(GuildId, EstimateBonusMap, 0),
			AwardStatus   = maps:get(GuildId, GuildAwardMap, 0),
			case IsJoin=/=[] andalso JoinNum >0 andalso AwardStatus==0 of
				true -> EstimateBonus/JoinNum + AccNum;
				false -> AccNum
			end;
		({_AuctionId, _Auction}, AccNum) -> AccNum
		end, 0, maps:to_list(AuctionMap)),
	round(AverageBonus).

%% ---------------------------- check function %% ----------------------------
check({pay_auction, FinishInit, PlayerId, AuctionGoods, PriceType, Price, PriceList}) ->
	WorldLv = util:get_world_lv(),
	BaseGoods = get_goods_config(AuctionGoods),
	NewAuctionGoods = ?IF(is_record(AuctionGoods, auction_goods), AuctionGoods, #auction_goods{}),
	#auction_goods{auction_type = AuctionType, goods_type = GoodsType} = NewAuctionGoods,
	NeedMoneyType = lib_auction_data:get_need_money_type(AuctionType, GoodsType, WorldLv),
	F = fun({Type, _GoodsId, _Num}) -> Type == ?TYPE_GOLD end,
	IsAllGold = lists:all(F, PriceList),
	if
		FinishInit =<0 ->
			%% 是否完成数据化
			{false, ?ERRCODE(err154_in_init)};
		is_record(AuctionGoods, auction_goods)=/=true ->
			%% 改物品拍卖已关闭
			{false, ?ERRCODE(err154_goods_closed)};
		BaseGoods == [] ->
			%% 缺失物品配置
			{false, ?ERRCODE(err154_missing_config)};
		NeedMoneyType == ?TYPE_GOLD andalso IsAllGold =/= true ->
			%% 本商品只能使用钻石参与拍卖
			{false, ?ERRCODE(err154_only_pay_by_gold)};
		NewAuctionGoods#auction_goods.goods_status == ?GOODS_STATUS_SELLOUT ->
			%% 物品已拍出
			{false, ?ERRCODE(err154_goods_sellout)};
		PriceType == ?PRICE_TYPE_AUCTION andalso
		Price =/= NewAuctionGoods#auction_goods.next_price  ->
			notify_goods_info(PlayerId, NewAuctionGoods),
			%% 竞价已发生变化
			{false, ?ERRCODE(err154_price_change)};
		Price == 0 ->
			%% 价格参数错误
			{false, ?ERRCODE(err154_error_price)};
		true ->
			check({pay_auction_extra, PlayerId, NewAuctionGoods, PriceType, Price})
	end;

check({pay_auction_extra, PlayerId, AuctionGoods, PriceType, Price}) ->
	NowTime   = utime:unixtime(),
	#auction_goods{
		auction_type 	= AuctionType,
		goods_type  	= GoodsType,
		goods_num  		= GoodsNum,
		now_price 		= NowPrice,
		next_price 		= NextPrice,
		start_time 		= StartTime,
		info_list 		= InfoList
		} = AuctionGoods,
	EndTime  = lib_auction_data:get_goods_endtime(AuctionGoods),
	WorldLv  = util:get_world_lv(),
	OnePrice = lib_auction_data:get_one_price(AuctionType, GoodsType, GoodsNum, WorldLv),
	case lists:keyfind(NowPrice, #auction_info.price, InfoList) of
		#auction_info{} = AuctionInfo ->
			TopPlayerId   = AuctionInfo#auction_info.player_id,
			PriceList 	  = AuctionInfo#auction_info.price_list,
			{Bgold, Gold} = count_award_gold(PriceList, 0, 0),
			OldPrice = Bgold + Gold;
		_ -> TopPlayerId = 0, OldPrice = 0
	end,

	if
		PriceType == ?PRICE_TYPE_ONE_PRICE andalso
		Price =/= OnePrice  ->
			%% 价格参数错误
			{false, ?ERRCODE(err154_error_price)};

		%% 加价：竞价已发生变化，您不是当前最高出价者
		PriceType == ?PRICE_TYPE_ADD_PRICE andalso
		PlayerId =/= TopPlayerId ->
			notify_goods_info(PlayerId, AuctionGoods),
			{false, ?ERRCODE(err154_price_change)};

		%% 加价： 竞价已发生变化，加价数额不对
		PriceType == ?PRICE_TYPE_ADD_PRICE andalso
		OldPrice + Price =/= NextPrice ->
			notify_goods_info(PlayerId, AuctionGoods),
			{false, ?ERRCODE(err154_price_change)};

		NowTime > EndTime ->
			%% 改物品拍卖已关闭
			{false, ?ERRCODE(err154_goods_closed)};
		NowTime < StartTime ->
			%% 拍卖未开启，请稍加等待
			{false, ?ERRCODE(err154_auction_not_start)};
		true -> true
	end;

check({check_pay_right, _PlayerId, GuildId, AuctionGoods, _PriceType}) ->
	CheckList  = [
		% @common 策划新需求：自己是最高竞价也是可以继续加价的
		% {check_top_price, PlayerId, AuctionGoods, PriceType},
		{check_guild, GuildId, AuctionGoods}
	],
	checklist(CheckList);

check({check_top_price, PlayerId, AuctionGoods, ?PRICE_TYPE_AUCTION}) ->
	#auction_goods{
		now_price = NowPrice,
		info_list = InfoList
	} = AuctionGoods,
	case lists:keyfind(NowPrice, #auction_info.price, InfoList) of
		#auction_info{player_id = PlayerId} ->
			%% 你当前已是最高竞价者
			{false, ?ERRCODE(err154_player_top_price)};
		_ -> true
	end;
check({check_top_price, _PlayerId, _AuctionGoods, ?PRICE_TYPE_ONE_PRICE}) ->
	true;

check({check_guild, GuildId, AuctionGoods}) ->
	#auction_goods{
		guild_id = Gid
	} = AuctionGoods,
	if
		Gid=/=0 andalso Gid=/=GuildId ->
			%% 不是你所在公会物品
			{false, ?ERRCODE(err154_error_guild_id)};
		true -> true
	end;

check({cost_money, PlayerId, PriceList}) ->
	case catch lib_player:apply_call(PlayerId, ?APPLY_CALL_SAVE, lib_goods_api, cost_object_list_with_check, [PriceList, pay_auction, ""], 2000) of
		true -> true;
		ErrorCode when is_integer(ErrorCode) -> {false, ErrorCode};
		_R -> {false, ?FAIL}
	end;

check(_) ->
    {false, ?FAIL}.

checklist([]) -> true;
checklist([H|T]) ->
    case check(H) of
        true -> checklist(T);
        {false, Res} -> {false, Res}
    end.

get_goods_config(#auction_goods{goods_type = GoodsType}) ->
	data_auction:get_goods(GoodsType, util:get_world_lv());
get_goods_config(_) -> [].
