step by step开发一个简易股票交易系统(五)---数据库使用

Step5:使用mnesia代替进程字典
1.创建mnesia表init_db.erl
-module(init_db).
-import(lists, [foreach/2]).
-compile(export_all).

-include_lib("stdlib/include/qlc.hrl").
-record(stock,{id,pid}).

do_this_once() ->
    mnesia:create_schema([node()]),
    mnesia:start(),
    mnesia:create_table(stock,[{ram_copies, [node()]},{attributes, record_info(fields,stock)}]),
    mnesia:stop().


2.stock_server.erl
-module(stock_server).

-behaviour(gen_server).

-export([list/1, sell/2, buy/2, start/0,stop/0,start_trade/1,stop_trade/1]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-record(stock,{id,pid}).

-import(lists, [foreach/2]).
-import(stock,[start/1,stop/1]).

-include_lib("stdlib/include/qlc.hrl").

start()->gen_server:start_link({local,stock_server},stock_server,[],[]).
stop()->gen_server:cast(?MODULE,stop).
sell(Id,Trade)->gen_server:cast(?MODULE,{sell,Id,Trade}).
buy(Id,Trade)->gen_server:cast(?MODULE,{buy,Id,Trade}).
list(Id)->gen_server:cast(?MODULE,{list,Id}).
start_trade(Id)->gen_server:cast(?MODULE,{start_trade,Id}).
stop_trade(Id)->gen_server:cast(?MODULE,{stop_trade,Id}).

init([]) ->
    mnesia:start(),
    mnesia:wait_for_tables([stock], 20000),
    foreach(fun start_stock/1,getStock()),
    {ok, []}.

handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.


handle_cast(stop,State) ->
    {stop,normal,State};

handle_cast({start_trade,Id},State) ->
    start_stock(Id),
    {noreply,  State};
handle_cast({stop_trade,Id},State) ->
    case getPid(Id) of
        []->
            io:format("stock Id=~w no such stock~n",[Id]);
        [stop]->
            io:format("stock Id=~w have no start trade~n",[Id]);
        
        [Pid]->
            gen_server:cast(Pid,stop),
            removeId(Id)    
    end,
    {noreply, State};

handle_cast({list,Id}, State) ->
    case getPid(Id) of
        []->
            io:format("stock Id=~w no such stock~n",[Id]);
        [stop]->
            io:format("stock Id=~w have no start trade~n",[Id]);
        [Pid]->
            gen_server:cast(Pid,{list,5})
    end,
    {noreply,  State};

handle_cast({buy,Id,Trade}, State) ->
    case getPid(Id) of
        []->
            io:format("stock Id=~w no such stock~n",[Id]);
        [stop]->
            io:format("stock Id=~w have no start trade~n",[Id]);
        [Pid]->
            
            gen_server:cast(Pid,{buy,Trade})
    end,
    {noreply,  State};

handle_cast({sell,Id,Trade}, State) ->
    case getPid(Id) of
        []->
            io:format("stock Id=~w no such stock~n",[Id]);
        [stop]->
            io:format("stock Id=~w have no start trade~n",[Id]);
        [Pid]->
            
            gen_server:cast(Pid,{sell,Trade})
    end,
    {noreply,  State};

handle_cast({ok,Msg}, State) ->
    io:format("ok: ~w~n",[Msg]),
    {noreply, State};

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    foreach(fun stop_stock/1,getStock()),  
    mnesia:stop(),    
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%% --------------------------------------------------------------------
%%% Internal functions
%% --------------------------------------------------------------------
removeId(Item) ->
    Oid = {stock, Item},
    F = fun() ->
                mnesia:delete(Oid)
        end,
    mnesia:transaction(F).

saveId(Id,Pid)->
    Row = #stock{id=Id,pid=Pid},
    F = fun() ->
                mnesia:write(Row)
        end,
    mnesia:transaction(F).

getStock()->
    do(qlc:q([{X#stock.id,X#stock.pid} || X <- mnesia:table(stock)])).

getPid(Id)->
    do(qlc:q([X#stock.pid || X <- mnesia:table(stock),
                             X#stock.id =:=Id])).

do(Q) ->
    F = fun() -> qlc:e(Q) end,
    {atomic, Val} = mnesia:transaction(F),
    Val.

start_stock({Id,_})->
    case gen_server:start(stock,[],[]) of
        {ok,Pid}->saveId(Id,Pid);
        Any->
            io:format("start_trade error:~w~n",[Any])
    end.

stop_stock({Id,Pid})->    
    case Pid of
        stop->
            ok;
        Pid->
            gen_server:cast(Pid, stop)
    end,
    saveId(Id,stop).

3.stock.erl
-module(stock).

-behaviour(gen_server).
-export([start/0,stop/0]).

-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).


start()->gen_server:start_link({local,?MODULE},?MODULE,[],[]).
stop()->gen_server:cast(?MODULE,stop). 
init([]) ->
    {ok, {[],[],1}}.

handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.


handle_cast({sell,{Price,Number,Name}},  {BuyL,SellL,TradeId}) ->
    case lookup(buy,{Price,none},BuyL) of
        {_,Price,0,none}->
            L2=add(sell,{TradeId,Price,Number,Name},SellL),
            gen_server:cast(stock_server,{ok,hangup}),
            {noreply,{BuyL,L2,TradeId+1}};
        {TradeId1,Price1,Number1,Buy}->
            if Number1=:=Number->
                   L2=remove({TradeId1,Price1,Number1,Buy},BuyL),
                   gen_server:cast(stock_server,{ok,Buy}),
                   {noreply,{L2,SellL,TradeId}};
               Number1<Number->
                   L2=remove({TradeId1,Price1,Number1,Buy},BuyL),
                   gen_server:cast(stock_server,{ok,Buy}),
                   handle_cast({sell,{Price,Number-Number1,Name}},{L2,SellL,TradeId});
               Number1>Number->
                   L2=[replace(X,{TradeId1,Price1,Number1,Buy},Number1-Number)||X<-BuyL],
                   gen_server:cast(stock_server,{ok,Buy}),
                   {noreply,{L2,SellL,TradeId}}
            end
    
    end;


handle_cast({buy,{Price,Number,Name}}, {BuyL,SellL,TradeId}) ->
    case lookup(sell,{Price,none},SellL) of
        {_,Price,0,none}->
            L2=add(buy,{TradeId,Price,Number,Name},BuyL),
             gen_server:cast(stock_server,{ok,hangup}),
            {noreply,{L2,SellL,TradeId+1}};
        {TradeId1,Price1,Number1,Sell}->
            if Number1=:=Number->
                   L2=remove({TradeId1,Price1,Number1,Sell},SellL),
                    gen_server:cast(stock_server,{ok,Sell}),
                   {noreply,{BuyL,L2,TradeId}};
               Number1<Number->
                   L2=remove({TradeId1,Price1,Number1,Sell},SellL),
                   gen_server:cast(stock_server,{ok,Sell}),
                   handle_cast({buy,{Price,Number-Number1,Name}},{BuyL,L2,TradeId});                          
               Number1>Number->
                   L2=[replace(X,{TradeId1,Price1,Number1,Sell},Number1-Number)||X<-SellL],
                   gen_server:cast(stock_server,{ok,Sell}),
                   {noreply,{BuyL,L2,TradeId}}
            end
    
    end;

handle_cast({list,Count}, {BuyL,SellL,TradeId}) ->
  	X = getList(Count,BuyL),
    gen_server:cast(stock_server,{ok,{buy_list,X}}),
    Y = getList(Count,SellL),
    gen_server:cast(stock_server,{ok,{sell_list,Y}}),
    {noreply,{BuyL,SellL,TradeId}};

handle_cast(stop,State) ->
    {stop,normal,State};

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

lookup(sell,{Price,Buy},[{TradeId,Price1,Number,Name}|_T]) ->
    if
        Price>=Price1 -> {TradeId,Price1,Number,Name};
        true->{0,Price,0,Buy}
    end;       

lookup(buy,{Price,Sell},[{TradeId,Price1,Number,Name}|_T]) ->
    if
        Price=<Price1 -> {TradeId,Price1,Number,Name};
        true->{0,Price,0,Sell}
    end;    
lookup(_,{Price,Name},[])->{0,Price,0,Name}.

add(sell,{TradeId,Price,Number,Name},[{TradeId1,Price1,Number1,Name1}|T]) ->
    if
        Price>=Price1 -> [{TradeId1,Price1,Number1,Name1}|add(sell,{TradeId,Price,Number,Name},T)];
        true->[{TradeId,Price,Number,Name}|[{TradeId1,Price1,Number1,Name1}|T]]
    end;       

add(buy,{TradeId,Price,Number,Name},[{TradeId1,Price1,Number1,Name1}|T]) ->
    if
        Price=<Price1 -> [{TradeId1,Price1,Number1,Name1}|add(buy,{TradeId,Price,Number,Name},T)];
        true->[{TradeId,Price,Number,Name}|[{TradeId1,Price1,Number1,Name1}|T]]
    end;       
add(_,{TradeId,Price,Number,Name},[])->[{TradeId,Price,Number,Name}].

remove(Trade,L)-> [X||X<-L,X/=Trade].

replace({TradeId,Price,Number,Name},{TradeId1,Price1,Number1,Name1},Number2)->
    if
        {TradeId1,Price1,Number1,Name1}=:={TradeId,Price,Number,Name}->
            {TradeId,Price,Number2,Name};
        true->{TradeId,Price,Number,Name}
    end.

getList(_,[])->[];
getList(0,_)->[];
getList(Count,[{_,Price,Number,Name}|T])->
    [{Price,Number,Name}|getList(Count-1,T)].

你可能感兴趣的:(F#)