Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Modeler   Code  
»áÔ±   
 
   
 
 
     
   
 ¶©ÔÄ
  ¾èÖú
¶àÐÎ̬MVCʽWeb¼Ü¹¹£ºÍê³ÉʵʱÏìÓ¦
 
 À´Ô´£ºÍøÂç ·¢²¼ÓÚ£º 2017-4-6
  2591  次浏览      30
 

±¾ÎÄÒªµã

1.ÔÚWebʱ´úµÄǰ¶þÊ®Ä꣬ÔÚÓû§ÊÓͼ¼°ÆäÏÖʵ»òÐéÄâÊÀ½ç¼äµÄMVC¿É¹Û²ìµÄʼþÇý¶¯Í¬²½ÒѾ­²»ÔÙ·¢»Óʲô×÷ÓÃÁË¡£

2.½üÆÚµÄһЩнøÕ¹Ê¹ÕâÒ»»ù´¡ÀíÄîµÃÒÔÔÚWeb¿ª·¢ÉçÇø¸´ËÕ¡£

3.dWMVC ºÍ pWMVC¼Ü¹¹·¶Ê½¿ÉÒÔÓÃÓÚÍê³É¶Ëµ½¶Ë±ä»¯¹Û²ì¡°Ê¼þ»·¡±È¥´´½¨ÎÞ·ì¸ßЧµÄʵʱÏìÓ¦Ó¦ÓÃÐÐΪ¡£

4.´«Í³Öмä¼þ¼Ü¹¹ºÍÐÂÉú´úµÄ·þÎñÆ÷Ô˶¯ÆÚ»·¾³¶¼¿É±»ÓÃÓÚÈ¥Íê³ÉÕâЩʵʱÏìÓ¦ÐÐΪ¡£

5.·Ç´«Í³·þÎñÔËÐл·¾³ºÍÊý¾Ý¿âÏíÓÐʹÓ÷DZê×¼»¯¼¼ÊõÈ¥´´½¨´´Ð½â¾ö·½°¸µÄ¼Ü¹¹×ÔÓÉ¡£

֮ǰÔÚ¡¶ ¶àÐÎ̬MVCʽWeb¼Ü¹¹µÄ·ÖÀà ¡·ÖУ¬ÎÒÃDzûÊö²¢ÌÖÂÛÁËWMVC(»ùÓÚWebµÄ MVC ) ¼Ü¹¹·¶Ê½µÄÈý¸öÖÖÀà¡£ËüÃÇÊÇ·þÎñÆ÷¶ËWMVC (sWMVC)¡¢Ë«ÖØWMVC (dWMVC)ÒÔ¼°µã¶ÔµãWMVC (pWMVC)¡£ sWMVCͨ³£±¾ÖÊÉÏÊǾ²Ì¬µÄ£¬¶øÆäËûÁ½¸ö¼Ü¹¹·¶Ê½¿ÉÓÃÓÚ¹¹½¨ÊµÊ±ÏìÓ¦µÄWebÓ¦ÓÃ×é¼þ¡£ÕâÊÇÆäºóÐøÎÄÕ£¬ÔÚÕâÆªÎÄÕÂÖУ¬ÎÒÃǽ«ÀûÓÃÕâÁ½¸ö¼Ü¹¹·¶Ê½È¥Éè¼ÆºÍÑÝʾÍêÈ«¶¯Ì¬ºÍÏìӦʽÏÖ´úWeb×é¼þ¡£

MVC¼Ü¹¹·½·¨µÄºËÐÄÊÇʵÏÖÓû§ÊÓͼÓëËüÃÇËù·´Ó³µÄÕæÊµ»òÐéÄâÊÀ½çÖ®¼äͬ²½µÄʼþÇý¶¯µÄ¹Û²ìÕßģʽ¡£¸ÃÊÓͼ£¨°üÀ¨»òδ°üÀ¨À´×ÔÓÚÓû§µÄ¶îÍâÖ¸ÁԤÆÚÊÇ·´Ó³ÊÀ½çµÄ±ä»¯¡£ÔÚÐí¶àMVCʵÏÖÖж¼ÌåÏÖÁËÕâһ˼Ï룬´Ó×î³õµÄ×ÀÃæ½çÃæµ½ÏÖ´úÔöÇ¿ºÍÐéÄâÏÖʵ£¨ AR ºÍ VR £©¡£ÔÚ¡¶ ¶àÐÎ̬MVCʽWeb¼Ü¹¹µÄ·ÖÀà ¡·µÄÌÖÂÛÖÐÌáµ½£¬Õâ¸ö»ù±¾Ë¼ÏëÔÚWebµÄǰ¶þÊ®ÄêÖ®ºóÒѾ­²»ÔÙ·¢»Óʲô×÷ÓÃÁË¡£ÔÚÕâ¶Îʱ¼ä£¬WebÓ¦ÓÃÒÔ»ùÓÚsWMVCµÄ·½·¨ÎªÖ÷µ¼¡£ÔÚ×î½ü¼¸Ä꣬ËüÔÚWUI£¨WebÓû§½çÃæ£©Ó¦Óÿª·¢ÉçÇøÖÐÓÐËù¸´ÐË¡£Õâ¸öÐÂÔ˶¯ÊÇÓÉÐí¶à¼¼Êõ²úÆ·ºÍ±ê׼ЭÒéÇý¶¯µÄ¡£

ÔÚ±¾ÎÄÖУ¬ÎÒÃǽ«ÔËÓÃһЩеĽø²½È¥ÊµÏÖÒì²½µÄ¡¢×ÔÈ»µÄ¡¢ÎÞ·ìµÄÒÔ¼°¸ßЧµÄ´ÓWUIµ½ºó¶ËSoRԭʼ¼Ç¼£¨source of record£©±ä»¯¹Û²ìÏìӦʽ¡°Ê¼þ»·¡±¡£Õâ·½Ãæ¹Ø¼üʵÓü¼ÊõÊÇ£º

ÒÔÏÂÌÖÂÛÏà¹ØµÄÔ´Âë¿É µã»÷ ÔÚGitHubÉÏ»ñÈ¡¡£

Óû§¹ÊÊÂ

ÎÒÃǼٶ¨¿Í»§ÏëÒªÒ»¸ö»ùÓÚä¯ÀÀÆ÷µÄ²©¿ÍÆÀÂÛϵͳ¡£¸ÃWebÓ¦ÓÃÔÊÐíÓû§¹Û¿´Ò»¸ö²©¿ÍÖ÷Ìâ²¢·¢±íÆÀÂÛ¡£

ÏÂÃæÊÇÒ»ÕÅWebÒ³ÃæµÄ¸ÅÄîÉè¼Æ½ØÍ¼£¬ÓÉÈý¸ö×ÓÊÓͼ¹¹³É¡£×îÉϲãÄÇÒ»¿éÏÔʾµÄÊDz©¿ÍÖ÷Ì⣬ÆäºóÊÇÆÀÂÛÊäÈëºÍÌá½»Óò¡£×îºóÒ»¿éÇøÓò¸ºÔðÏÔʾËùÓÐÓû§ÊäÈëµÄÆÀÂÛ¡£

ͼ1 ²©¿ÍÆÀÂÛÉè¼Æ½ØÍ¼

¸ÃÈÕ־ϵͳӦ¸Ã°üÀ¨Á½¸öºÜÓÐÌØÉ«µÄÓ¦Óãº

µÚÒ»¸öÓ¦Óûá»ñÈ¡²©¿ÍÆÀÂÛµÄËùÓÐȨ²¢°ÑËüÃÇ´æ´¢½ø¼¯ÖÐʽÊý¾Ý¿âÖС£

µÚ¶þ¸öÓ¦ÓÃÔÚ¼¯ÖÐʽÊý¾Ý¿âÖв»±£´æÈκÎÓû§ÆÀÂÛ£¬ÒÔÈ·±£Óû§Òþ˽¼°¿Í»§ÔðÈΡ£

¸ÃϵͳµÄµÚÈý¸ö×é¼þÊÇ°ÑÆäËûÀ´Ô´µÄ²©¿ÍÆÀÂÛÕûºÏµ½Õâ¸ö¼¯ÖÐʽÊý¾Ý¿âÖУ¬Ëü½«ÔÚδÀ´¿ª·¢¡£

ËùÓÐÓ¦ÓÃÓû§Ó¦¸ÃÓÐÒ»¸öÊÓͼÓÀÔ¶ÄÜ¿´µ½×îеIJ©¿ÍÆÀÂÛ¡£

ÔÚÒ»¸öÓû§ÕýÔÚÔĶÁÆÀÂÛʱ£¬ÓÉÆäËûÓû§»òͨ¹ý×Ô¶¯»¯ÕûºÏÌí¼ÓÁËÐÂµÄÆÀÂÛ£¬ÄǾÍÓ¦¸ÃÁ¢¼´ÏÔʾÔÚËùÓÐÓû§WebÒ³ÃæÉÏ£¬¶ø²»±ØËû»òËýÊÖ¹¤Ë¢Ð¡£

ϵͳ¼Ü¹¹

¾ßÓм¯ÖÐʽÊý¾Ý¿âµÄ²©¿ÍWebÓ¦Óý«ÓÃdWMVC·¶Ê½ÓèÒÔÉè¼ÆºÍ¿ª·¢¡£×ܵÄÀ´Ëµ£¬Ó¦ÓÃ×é¼þ¼äµÄͨѶ½«ÓÃAngularJS¡¢SSE¡¢InSoRºÍ CDCÀ´ÊµÏÖ¡£ÕâЩ¼¼Êõ½«Ê¹ÏµÍ³Äܹ»ÏìÓ¦ÈκζԼ¯ÖÐʽÊý¾Ý¿âÖмǼµÄÐ޸ģ¨Í¨¹ýÕâ¸öWebÓ¦ÓûòδÀ´µÄ¼¯³ÉÄ£¿é£©£¬²¢ÊµÊ±´«µÝÕâÖֱ仯¸ø×îÖÕÓû§£¬¸ÅÀÀͼÈçͼ2¡£

 

ͼ2 ¼¯ÖÐʽʵʱ²©¿ÍWebÓ¦ÓÃϵͳ¼Ü¹¹

¿Í»§¶ËÓë·þÎñÆ÷¶ËÖ®¼äµÄͨѶ»ùÓÚµÄÊÇHTTPºÍSSEЭÒ飬ÒòΪ InSoR ºÍ CDC ÍêÈ«ÊÇÔÚÓ¦Ó÷þÎñÆ÷ºÍÊý¾Ý¿âÖ®¼äÍù·µµÄ¡£

µÚ¶þ¸öWebÓ¦Óý«ÒÔpWMVCģʽʵÏÖ£¨Èçͼ3£©¡£Ëü½«µ£ÈÎÒ»¸öʹÄÜÕߵĽÇÉ«£¬ÔÚ²»±Ø¸Ä±äÄÚÈÝËùÓÐȨµÄÇé¿öϰÑÓû§¾Ûµ½Ò»Æð¡£

ͼ3 µã¶Ôµãʵʱ²©¿ÍWebÓ¦ÓõÄϵͳ¼Ü¹¹

ͨ¹ýdWMVCʵÏֵļ¯ÖÐʽWebÓ¦ÓÃ

ÏÂÃæµÄͼ4ÊÇ»ùÓÚdWMVCµÄ²©¿ÍWebÓ¦ÓÃÉè¼Æ¸ÅÀÀ¡£ÔÚä¯ÀÀÆ÷¶Ë£¬ÊÓͼºÍ¿ØÖÆÆ÷×é¼þÊÇ»ùÓÚAngularJSµÄ¡£Á½¸ö²»Í¬µÄ·þÎñÆ÷¶Ë¼¼ÊõÕ»×éºÏ±»ÓÃÓÚdWMVCÄ£ÐÍ×é¼þµÄʵÏÖ¡£×ó²àµÄÊÇ´«Í³JavaÕ»ºÍJ2EE¼Ü¹¹£¬ÒÔ¼°¹ØÏµÐÍÊý¾Ý¿â PostgresSQL ¡£NodeJSºÍ RethinkDB ÄÇÒ»²àÓÃÓÚͼ½â»ùÓÚJavaScriptµÄ·þÎñÆ÷¶ËÔËÐл·¾³ºÍ NoSQL Êý¾Ý¿âµÄ¼Ü¹¹·¶Ê½¡£ÕâЩ²»Í¬µÄ·þÎñÆ÷¶ËÉè¼ÆºÍʵÏÖ´ú±íÁËʵÏÖͬһ¹¦ÄܵÄÁ½ÖÖ²»Í¬·½·¨¡£³ýÁËNodeJSµÄÒì²½ÌØÐÔ£¬ÔÚInSoR ºÍ CDCÖÐÒ²´æÔÚÌØ±ðÃ÷ÏԵIJîÒ죬ÔÚNoSQLÊý¾Ý¿âÌṩÕßÖпÉ×ÔÓÉ¿ØÖƼܹ¹£¬´Ó¶ø¿ÉÒÔʹÓ÷DZê×¼»¯µÄ¼¼ÊõÈ¥´´½¨´´ÐµĽâ¾ö·½°¸£¨±ÈÈç lazy evaluation ºÍ lazy loading £©¡£ÕâÁ½ÖÖʵÏÖ»¹ÌṩÁ˺ܶ༼ÊõÐÔÑ¡Ôñ£¬ÒÔÂú×ãWeb¿ª·¢ÉçÇø£¨´Ó´«Í³Öмä¼þʵ¼ùÕßµ½NodeJS/NoSQL ¿ñÈÈÕߣ©¹ã·ºµÄÐËȤ¡£

ͼ4 ²©¿ÍÓ¦ÓÃdWMVCÉè¼ÆÄ£Ê½µÄ¼Ü¹¹Í¼¡£¿Í»§¶ËWMVCÊÓͼºÍ¿ØÖÆÆ÷ÊÇ»ùÓÚAngularJSµÄ¡£·þÎñÆ÷¶ËÄ£ÐÍ×é¼þµÄÁ½¸öÑ¡ÔñÊÇ£ºJava-RDBMS£¨×ó²à£©ºÍNodeJS-NoSQL£¨ÓҲࣩ¡£

dWMVCµÄÊÓͼºÍ¿ØÖÆÆ÷

¸Ã²©¿ÍÍøÒ³ÊÇÓÃAngularJS¾Ö²¿Ä£°åʵÏֵġ£ËüÊÇÒ»¸ö¸´ºÏÊÓͼ£¬ÓÃÓÚΪ²©¿ÍÈÕÖ¾µÄÌá½»ºÍÏÔʾÌṩ·þÎñ¡£

<div class="blocker1">

<h3>Topic: WMVC Real Time Reactive Fulfillment</h3>

</div>

<div id="castingId" class="blocker2">

<div>

<h4>Post a Comment:</h4>

</div>

<form id="commentFormId">

<div>

<input type="text" style="width: 30%" name="newCommentId" placeholder="What is in your mind?" ng-model="newComment"/>

<button role="submit" class="btn btn-default" ng-click="addComment()"><span class="glyphicon glyphicon-plus"></span>Send</button>

</div>

</form>

</div>

<div>

<h4>All Comments:</h4>

</div>

<div class="view" ng-switch on="comments.comments.length" ng-cloak>

<ul ng-switch-when="0">

<li>

<em>No comments yet available. Be the first to add a comment.</em>

</li>

</ul>

<ul ng-switch-default>

<li ng-repeat="comment in comments.comments">

<span>{{comment.comment}} </span>

</li>

</ul>

</div>

¸ÃHTMLÒ³ÃæÒÀÀµÓÚdWMVC¿ØÖÆÆ÷£¨Èçͼ5£©Óë·þÎñÆ÷¶ËµÄͨÐÅÈ¥Ôö¼ÓÐÂµÄÆÀÂÛ£¬²¢ÎªÆäËûÓû§Ë¢ÐÂÒ³Ãæ¡£

ͼ5 ²©¿ÍÆÀÂÛÓ¦ÓõÄÊÓͼºÍ¿ØÖÆÆ÷×é¼þ¡£

ΪÁËΪÓû§ÏÔʾºÍˢв©¿ÍÆÀÂÛ£¬¸Ã¿ØÖÆÆ÷£º

ͨ¹ýHTTPÖ®ÉϵÄSSEÁ¬½Óºó¶Ë·þÎñÆ÷¡£

Èç¹ûÓеϰ£¬ÔòÒì²½½ÓÊÕºÍÏÔʾËùÓÐÒÑÓеÄÈÕÖ¾ÆÀÂÛ¡£

±£³ÖÁ¬½Ó²¢¼àÌýδÀ´µÄSSEʼþ£¬Ëü½«¸üÐÂµÄÆÀÂÛʼþ×÷Ϊʼþ¸ºÔؽøÐд«µÝ¡£

µ±SSEʼþ·¢Éúʱ£¬ÍÆËͺͰ󶨸üеÄÈÕÖ¾ÆÀÂÛµ½Óû§µÄÊÓÍ¼Ò³Ãæ¡£

ËùÓÐÕâЩ½»»¥ºÍ·´Ó¦ÊÇÓÃÒÔÏ´úÂë¶ÎʵÏֵģº

var dataHandler = function (event)

{

var data = JSON.parse(event.data);

console.log('Real time feeding => ' + JSON.stringify(data));

$scope.$apply(function ()

{

$scope.comments = data;

});

};

var eventSource = new EventSource('/wmvcapp/svc/comments/all');

eventSource.addEventListener('message', dataHandler, false);

µ±Ò»ÃûÓû§Ôö¼ÓÒ»¸öÐÂµÄÆÀÂÛʱ£¬Ëü»áÖ±½Ó´«Ë͵½·þÎñÆ÷¶ËÓÃÓÚ´¦Àí£º

$scope.addComment = function ()

{

var newInput = $scope.newComment.trim();

if (!newInput.length)

{

return;

}

var url = '/wmvcapp/svc/comments/cast';

$http.post(url, newInput);

$scope.newComment = '';

};

½ÓÏÂÀ´£¬ÔÚÏÂÃæ½«ÌÖÂÛÓÉ·þÎñÆ÷Ä£ÐÍ×é¼þ²¶»ñºÍ´¦ÀíËüËù¹ØÁªµÄÊý¾Ý±ä¸ü¡£

dWMVCµÄJavaºÍPostgreSQLÄ£ÐÍ×é¼þ

Ö÷Òª×é¼þ¶¼°üº¬ÔÚ´«Í³¼¼ÊõÕ»ÄÚ£¬Ò»¸ö»ùÓÚJavaµÄÖмä¼þÓ¦ÓóÌÐò¿â×éºÏºÍÒ»¸ö¹ØÏµÐÍÊý¾Ý¿â£¬Èçͼ6Ëùʾ¡£

ͼ6 »ùÓÚJavaºÍPostgreSQLµÄdWMVCÄ£ÐÍ×é¼þ¡£

ÕâЩģÐÍÖеĽ»»¥ºÍÏìÓ¦Èçͼ7Ëùʾ¡£ËüչʾÁËÁ½¸ö·ÃÎʸò©¿ÍÓ¦ÓõÄÓû§¡£

ͼ7 Ϊ²é¿´²©¿ÍÆÀÂÛµÄÓû§Ìṩʵʱ¶èÐÔ¸üеÄһϵÁн»»¥£¬»ùÓÚµÄÊÇJavaºÍPostgreSQL¹ØÏµÐÍÊý¾Ý¿â¡£

µ±Ò»¸öÓû§´ò¿ª²©¿ÍÒ³ÃæÊ±£¬dWMVC¿ØÖÆÆ÷Á¢¼´ÊµÀý»¯Ò»¸öSSEʵÀý£¬ËüÆô¶¯Óë·þÎñÆ÷µÄͨÐÅÒÔ½ÓÊÕ²©¿ÍÆÀÂÛ¡£ÆäÏà¹ØµÄ·þÎñÆ÷×é¼þÈçÏÂËùʾ£¬×¢½âÁËSSEÇëÇóºÍʵÏÖ»ùÓÚSSEµÄÊä³ö¡£µ±·þÎñÆ÷¶Ë×é¼þ½ÓÊÕµ½À´×ÔÓÚdWMVC¿ØÖÆÆ÷»ùÓÚSSEµÄÇëÇóʱ£¬ËüÊ×ÏÈÕë¶ÔÒÑÓÐÆÀÂÛ²éѯһÏÂÊý¾Ý¿â£¬È»ºó¹ã²¥Ò»¸öÒì²½ EventOutput µ½¸Ã¿ØÖÆÆ÷£¬´Ó¶ø½«¸ÃÆÀÂÛÏÔʾ¸øÓû§ä¯ÀÀÆ÷¡£Óë´ËÆÚ¼ä£¬ÎªÁ˽ÓÊÕÔÚ¸ÃPostgreSQLÄڶԸò©¿ÍÖ÷ÌâºóÐø±ä¸üµÄ³ÖÐøÍ¨Öª£¬¸Ã·þÎñÆ÷¶Ë×é¼þÔö¼ÓÒ»¸ö¼àÌýÒÔ±£³Ö¶ÔPostgreSQLÊý¾ÝµÄ¡°Ö÷Ìâ¹Û²ìÕß¡±µÄ¼àÌý

@GET

@Path("/all")

@Produces(SseFeature.SERVER_SENT_EVENTS)

public EventOutput getAllComments() throws Exception

{

final EventOutput eventOutput = new EventOutput();

Statement sqlStatement = null;

//Query and return current data

String comments = BlogByPostgreSQL.getInstance().findComments(ConfigStringConstants.TOPIC_ID);

this.writeToEventOutput(comments, eventOutput);

//Listen to future change notifications

PGConnection conn = (PGConnection)BlogByPostgreSQL.getInstance().getConnection();

sqlStatement = conn.createStatement();

sqlStatement.execute("LISTEN topics_observer");

conn.addNotificationListener("topics_observer", new PGNotificationListener()

{

@Override

public void notification(int processId, String channelName, String payload)

{

JSONObject plJson = new JSONObject(payload);

String plComments = plJson.getJSONObject("topic_comments").toString();

writeToEventOutput(plComments, eventOutput);

}

});

return eventOutput;

}

private void writeToEventOutput(String comments, EventOutput eventOutput)

{

OutboundEvent.Builder eventBuilder = new OutboundEvent.Builder();

eventBuilder.mediaType(MediaType.APPLICATION_JSON_TYPE);

if(comments == null || comments.trim().equals(""))

{

comments = NO_COMMENTS;

}

eventBuilder.data(String.class, comments);

OutboundEvent event = eventBuilder.build();

eventOutput.write(event);

}

PostgreSQLÊÇÒ»¸ö¿ªÔ´µÄ¹ØÏµÐÍÊý¾Ý¿â¡£Ëü½üÆÚÌí¼ÓµÄÆäÖÐÒ»¸öÌØÐÔÊÇ£¬²¶»ñ²¢·¢ËÍËùÓмǼµÄ±ä¸ü×÷ΪÁ¬½ÓÓ¦ÓÃ×é¼þµÄÈëÕ¾¸ºÔØ¡£Õâ¸öInSoRÄÜÁ¦ÊÇÒÔÒ»¶ÔÊý¾Ý¿â´¥·¢Æ÷ºÍº¯ÊýÅäÖÃʵÏֵġ£Õë¶ÔÎÒÃǵIJ©¿ÍÖ÷Ìâ±í½øÐÐÈçÏÂÅäÖãº

CREATE OR REPLACE FUNCTION proc_topics_notify_trigger() RETURNS trigger AS $$

DECLARE

BEGIN

PERFORM pg_notify('topics_observer', json_build_object('topic_id', NEW.topic_id, 'topic_comments', NEW.comments)::text);

RETURN new;

END;

$$ LANGUAGE plpgsql

DROP TRIGGER trigger_topics_notify ON topics;

CREATE TRIGGER trigger_topics_notify AFTER INSERT OR UPDATE OR DELETE ON topics

FOR EACH ROW EXECUTE PROCEDURE proc_topics_notify_trigger()

¼ÙÉ裬ÔÚÓÐЩÓû§ÕýÔÚÔĶÁ²©¿ÍÆÀÂÛµÄͬʱ£¬ÆäÖÐÓÐÈË´òËãÔö¼ÓÒ»ÌõÐÂµÄÆÀÂÛ¡£

@POST

@Path("/cast")

@Consumes(MediaType.APPLICATION_JSON)

public void addComment(String newComment) throws Exception

{

if(newComment != null && !newComment.trim().equals(""))

{

ObjectMapper mapper = new ObjectMapper();

TopicComments topicComments;

String comments = BlogByPostgreSQL.getInstance().findComments(ConfigStringConstants.TOPIC_ID);

if(comments == null || comments.trim().equals(""))

{

topicComments = new TopicComments();

topicComments.addComment(newComment);

String topicCommentsStr = mapper.writeValueAsString(topicComments);

BlogByPostgreSQL.getInstance().addTopic(topicCommentsStr);

}

else

{

if(!comments.contains(newComment))

{

topicComments = mapper.readValue(comments, TopicComments.class);

topicComments.addComment(newComment);

String topicCommentsStr = mapper.writeValueAsString(topicComments);

BlogByPostgreSQL.getInstance().updateTopic(topicCommentsStr);

} } } }

È»ºó£¬Êý¾Ý¿âÖиüǼ±»ÕâÌõÐÂÆÀÂÛÒ»¸Ä£¬¸ÃÊý¾Ý¿âµÄ¡°trigger_topics_notify¡±´¥·¢Æ÷¾Í½«µ÷ÓÃÆäÏà¹ØµÄ¡°proc_topics_notify_trigger¡±º¯ÊýÕë¶Ô¡°topic_observer¡±·¢ÆðÒ»¸ö±ä¸üʼþ֪ͨ¡£¸Ã¡°topic_observer¡±Í¨Öª½«Á¢¼´ÍÆË͸ø¡°topic_observer¡±µÄ¼àÌýÕߣ¬Á¬Í¬JSON¸ñʽµÄ¸üÐÂÆÀÂÛÒ»Æð×÷ΪÊý¾Ý¸ºÔØ¡£¸ÃÓ¦ÓÃ×é¼þÓëÕâЩ¼àÌýÕß±£³ÖÁªÏµ£¬ÒÀ´Î´¦ÀíºÍ±àдÁíÍâµÄSSE EventOutputµ½¸Ã¿ØÖÆÆ÷ȥˢÐÂÕâЩ¸üÐÂµÄÆÀÂÛµ½ËùÓÐÓû§ÊÓͼ¡£ÕâÑùËùÓÐʾͶ¼ÒѾ­ÊµÏÖÁË£¬²»ÐèҪΪÓû§·¢ÆðеÄÇëÇó£¨Èçͼ7£©¡£

dWMVCµÄ½ÚµãºÍRethinkDBÄ£ÐÍ×é¼þ

¹ýÈ¥µÄ¼¸ÄêÀNodeJSÒѾ­³ÉΪÓÃÓÚ¹¹½¨WebÓ¦Ó÷þÎñÆ÷¶ËÔËÐÐÆÚ»·¾³ÐÂÈñÑ¡Ôñ¡£ËüµÄºËÐļܹ¹ÊÇʼþÇý¶¯¡¢Òì²½´¦Àí¡£RethinkDBÊÇÒ»¸ö¿ªÔ´NoSQLÊý¾Ý¿â£¬Ëü½«ÊµÊ±WebÓ¦ÓõĿª·¢·Åµ½ËüµÄ¼Ü¹¹ºÍÉè¼ÆÖнøÐÐÁËÉî˼ÊìÂÇ¡£ÆäÖÐÒ»¸öÄÚÖõÄÌØÐÔÊÇ£¬ÌṩÁ˱ä¸üʼþµÄ֪ͨȥµ÷ÓÃÓ¦ÓÃ×é¼þ¡£

¶Ô±Èͼ6£¬ÏÂÃæµÄͼ8×î´óµÄ²»Í¬ÊÇÊý¾Ý¿â´¥·¢Æ÷ºÍ¹ý³Ìº¯Êý²»ÔÙÐèÒªÓÉRethinkDBÀ´ÅäÖá£ËüµÄÊý¾Ý¿â±ä¸üʼþµÄ֪ͨÊÇÓÿÉÁ´½Ó£¨chainable£©µÄ²éѯÓïÑÔ ReQL ʵÏֵġ£

ͼ8 »ùÓÚNodeJSºÍRethinkDBÊý¾Ý¿âµÄdWMVCÄ£ÐÍ×é¼þ¡£

ͼ9չʾÁËÓ¦ÓúÍÊý¾Ý¿â×é¼þ¼äµÄһϵÁн»»¥ºÍÏìÓ¦¡£

ͼ9 »ùÓÚNodeJSºÍRethinkDBÊý¾Ý¿â£¬Í¨¹ýһϵÁн»»¥ÎªÕýÔڲ鿴²©¿ÍÆÀÂÛµÄÓû§Ìṩʵʱ¸üС£

µ±·þÎñÆ÷¶Ë×é¼þblogApp.js ½ÓÊÕµ½Ò»¸ö»ùÓÚSSEµÄgetAllCommentsÇëÇóʱ£¬ËüÊ×ÏȰ´ÕÕÐÂÔöµÄÌØ¶¨HTTP±¨Í·×¼±¸ÏìÓ¦£¬ÈçÏÂËùʾ£¬Îª×î³õµÄÏìӦȥºÍdWMVC¿ØÖÆÆ÷ÎÕÊÖ¡£Õâʹ¸Ã¿ØÖÆÆ÷¿ÉÒÔ³ÖÐø¼àÌýºóÐøSSEÁ÷ʼþ¡£

function setUpSSE(res)

{

res.writeHead(200,

{

'Content-Type': 'text/event-stream',

'Cache-Control': 'no-cache',

'Connection': 'keep-alive',

'Transfer-Encoding': 'chunked'

});

res.write('\n');

}

½ÓÏÂÀ´£¬Ëüͨ¹ýBlogByRethinkDB.jsÖ´ÐÐÒ»¸ö¿ÉÁ´½ÓµÄReQL²éѯȥ֪ͨ¸ÃÊý¾Ý¿â£¬ËüÏëÒª¹Û²ìºÍ½ÓÊÕ¸ÃÊý¾Ý¼Ç¼µÄδÀ´Èκεıä¸ü¡£ÕâÌõ¿É¹Û²ìµÄ²éѯʹ¸ÃÊý¾Ý¿âÒ»·¢Éú±ä¸ü¾Í½«¸Ã±ä¸ü ¶èÐÔÁ÷»¯ ·¢»Ø¸øÓ¦ÓÃ×é¼þ¡£

BlogByRethinkDB.prototype.observeComments = function(process)

{

connectToRDB(function(err, rdbConn)

{

if(err)

{

if(rdbConn) rdbConn.close();

return console.error(err);

}

//Listen for blog comment change events

r.table(config.wmvcBlog.dbTable).filter(r.row('topicId').eq(config.wmvcBlog.wmvcBlogTopicId))

.changes({includeInitial: false}).run(rdbConn, function(err, cursor)

{

if(err)

{

if(rdbConn) rdbConn.close();

return console.error(err);

}

//Retrieve all the comments in an array.

cursor.each(function(err, row)

{

if(err)

{

if(rdbConn) rdbConn.close();

return console.error(err);

}

if(row)

{

return process(null, row.new_val);

} }) }) }) };

È»ºó£¬ËüÖØÐ»ñÈ¡¸ÃÇëÇóÖ÷ÌâµÄËùÓÐÒÑÓÐÆÀÂÛ¡£

BlogByRethinkDB.prototype.getAllComments = function(process)

{

connectToRDB(function(err, rdbConn)

{

if(err)

{

if(rdbConn) rdbConn.close();

return console.error(err);

}

//Query for comments

r.table(config.wmvcBlog.dbTable).filter(r.row('topicId').eq(config.wmvcBlog.wmvcBlogTopicId))

.run(rdbConn, function(err, cursor)

if(rdbConn) rdbConn.close();

if(err)

{

return console.error(err);

}

//Retrieve all the comments in an array.

cursor.toArray(function(err, result)

{

if(err)

{

return console.error(err);

}

if(result && result.length > 0)

{

return process(null, result[0]);

}

else

{

return process(null, null)

} }); }); }); };

×îºó£¬¸Ã·þÎñÆ÷¶Ë¶ÔÏó×éÖ¯²¢·µ»ØÒ»¸öHTTPÏìÓ¦£¬Ëü´øÓÐSEE¼æÈݸñʽµÄÊý¾Ý¡£

function handleSSEResponse(err, blogComments, res, next)

{

if(err)

{

return next(err);

}

if(blogComments) {

var id = new Date().getTime();

res.write('id: ' + id + '\n');

res.write('data: ' + JSON.stringify(blogComments) + '\n\n');

}

else

{

var empty = new Array();

var noResult = { comments: empty };

var id = new Date().getTime();

res.write('id: ' + id + '\n');

res.write('data: ' + JSON.stringify(noResult) + '\n\n');

} }

Æäºó£¬µ±ÐÂµÄÆÀÂÛÌí¼Óµ½ÏµÍ³ÖÐʱ£¬observeComments½«Òì²½ÏìÓ¦ËüµÄÊý¾Ý¿â±ä¸üʼþ£¬²¢¹ã²¥¸Ã¸üÐÂµÄÆÀÂÛ¸øËùÓÐÕýÔڲ鿴µÄÓû§£¬Èçͼ9Ëùʾ¡£

pWMVCµã¶ÔµãWebÓ¦ÓÃ

pWMVC¼Ü¹¹·½°¸µÄÖ§ÖùÊÇWebRTCЭÒé¡£ÌØ±ðÊÇÆäÖ÷Òª×é¼þÖеÄRTCDataChannelÒÑÔڴ˲©¿ÍÓ¦ÓõÄʵÏÖÖеõ½ÁËÓ¦Ó᣸Ã×é¼þÌṩÁËÔÚä¯ÀÀÆ÷¼äË«Ïòµã¶ÔµãÊý¾Ý´«ÊäµÄÄÜÁ¦£¬¶ø²»Ðè°²×°¶îÍâµÄ²å¼þ¡£

DataChannelJS ÊÇÒ»¸öÕë¶ÔRTCDataChannelµÄJavaScript°ü×°Æ÷Àà¿â£¬ÓÃËü¿ÉÒÔʹµ×²ã²»±ØÌ«¹ý¸´ÔÓ£¬´Ó¶ø¼ò»¯ÊµÏÖ¡£³öÏÖͬÑùµÄÄ¿µÄ£¬ PusherJS ±»Ñ¡ÔñÀ´ÌṩÐźŷþÎñ¡£WebRTC-awareµÄÓ¦ÓÃÐèÒªÒ»¸öÐźÅͨµÀ£¬ÓÃÓÚÌØ¶¨¿Í»§¶Ë½»»»»áÒéÃèÊöºÍÍøÂç¿É´ïÐÔµÄÐÅÏ¢¡£Õû¸öÓ¦ÓÃÕûºÏ²¿ÊðΪһ¸öNodeJS Web·þÎñÆ÷¡£

ÐèҪעÒâµÄÊÇ£¬NodeJS·þÎñÆ÷ºÍPusherJSÐźÅ×°Öö¼²»±£Áôä¯ÀÀÆ÷¼äµÄÈκÎÊý¾Ý½»»»¡£Èçͼ10Ëùʾ£¬²ÎÓëÆäÖеÄÐÅÏ¢½»»»±£´æÔÚÿ¸öÓû§µÄä¯ÀÀÆ÷ ±¾µØ´æ´¢ ÖС£Á¬Í¬ÕâЩ±¾µØ´æ´¢Ò»Æð£¬ËùÓÐÖ÷ÒªÓ¦ÓÃ×é¼þÒ²¶¼Î»ÓÚä¯ÀÀÆ÷¶Ë£¬²¢ÔÚÔËÐÐÆÚÖ´ÐС£¸ÃNodeJS×é¼þÖ»ÔÚä¯ÀÀÆ÷¼äת²¥²©¿ÍÆÀÂÛ£¬Î¬»¤×éÁ¬½Ó״̬£¬±£³ÖͨÐÅͨµÀµÄ¿ªÍ¨¡£

ͼ10 ¸Ã²©¿ÍÓ¦ÓÃpWMVCʵÏֵļܹ¹Í¼¡£ËùÓÐÓ¦ÓÃÆÀÂÛºÍÊý¾Ý´æ´¢¶¼ÔÚÓû§ä¯ÀÀÆ÷ÉÏ¡£ Node.jsµÄÖ÷ÒªÖ°ÔðÊǸºÔðËùÓвÎÓëÕßä¯ÀÀÆ÷¼äµÄÐźŴ«Êä¡£

ͼ11 ²ûÊöÁËÁ½¸öÓû§Ö®¼ä½¨Á¢ºÍÐγɲ©¿ÍÖ÷Ìâ×éµÄ˳Ðò¹ý³ÌÁ÷¡£µÚÒ»¸öÓû§ÔÚËûµÄä¯ÀÀÆ÷ÉÏ·ÃÎʺͳõʼ»¯pWMVCÓ¦Óã¬p2pControllerͨ¹ýÈô¸É²½Öè´ò¿ªÁËÒ»¸öDataChannelJSʵÀý£¬°ó¶¨µ½Ò»¸öPusherJSÐÅºÅÆµµÀ£¬²¢¿ªÊ¼·¢ËÍͨÐÅÐźš£´Ëʱ£¬ÓÉÓÚûÓÐÆäËûͬÑùµÄ²ÎÓëÕߣ¬¸ÃÓ¦ÓÃΪÊ׸öÓû§ÏÔʾһ¸öĬÈÏÒ³Ãæ¡£½ÓÏÂÀ´£¬ÁíһλÓû§´ò¿ª¸Ã²©¿ÍWebÒ³Ãæ¡£p2pCcontroller¼ì²â¸Ã²©¿Í×éÒѾ­´ò¿ª£¬ÓÚÊÇËü¾ÍÖ±½ÓÁ¬½ÓÕâµÚ¶þλÓû§µÄDataChannelJS²¢°ó¶¨Ëüµ½PusherJSÐźÅ×°Öá£È»ºó£¬ÕâÁ½¸öä¯ÀÀÆ÷½øÐÐһϵÁÐ ICE £¨½»»¥Ê½Á¬½Ó½¨Á¢£©Í¨ÐŲ¢Ð­ÉÌÍê³ÉÒ»´Îp2pÎÕÊÖ¡£Õâ¸ö¹ý³ÌÔÚä¯ÀÀÆ÷¿ØÖÆÌ¨´°¿Úͨ¹ýÒ»¿é½ÓÒ»¿éµÄ·½Ê½À´±íʾ£¬¶ø³öÓÚ¼ò½à¿¼ÂǾͲ»ÏÔʾϸ½ÚÁË¡£ÔÚÎÕÊÖÖ®ºó£¬ÕâÁ½¸öÓû§×¼±¸ºÃ˽Ͻ»»»ÐÅÏ¢ÁË£¬´Ë½öÏÞÓÚÏÖÔÚÔÚËüÃÇÖ®¼ä¿ªÍ¨µÄDataChannelJS¡£

ͼ11 »ùÓÚPusher.js¡¢DataChannel.jsºÍNode.js£¨ÑÓÐøÍ¼10£©£¬ Á½¸öÓû§ä¯ÀÀÆ÷Ö®¼ä½¨Á¢»ùÓÚWebRTCͨÐŵÄһϵÁн»»¥

Ò»µ©Á½¸öÓû§Ö®¼ä¿ªÍ¨ÁËDataChannelJS£¨Èçͼ12£©£¬¸ÃÓ¦ÓþͻáÊ×ÏÈ´Ó¸Ãä¯ÀÀÆ÷±¾µØ´æ´¢½ÓÊÕºÍÏÔʾ¸ÃÖ÷ÌâÒÑÓÐµÄÆÀÂÛ£¨Èç¹ûÓеϰ£©£¬ÒÔ±ãËûÃÇÁ˽âÉϴν»Á÷ÖÁ½ñ´í¹ýµÄÄÚÈÝ¡£

webRTCDatachannel.onopen = function (userId)

{

p2pModel.getAllComments(groupName)

.success(function(updatedComments)

{

if(updatedComments === null)

{

updatedComments = { comments: new Array() };

}

$scope.comments = updatedComments;

})

.error(function(error)

{

alert('Failed to save the new comment' + error);

});

}

getAllComments: function (groupName)

{

var savedComments = $window.localStorage.getItem(groupName);

if(savedComments !== null)

{

savedComments = JSON.parse(savedComments);

}

var updatedComments = aggregateComments("", null, savedComments);

return handlePromise($q, updatedComments);

}

ͼ12 ÑÓÐøÍ¼11£¬»ùÓÚPusher.js¡¢DataChannel.jsºÍNode.js£¬Á½¸öÓû§ä¯ÀÀÆ÷½»»»»ùÓÚWebRTCÐÅÏ¢µÄһϵÁн»»¥¡£¸ÃÐÅÏ¢±£´æÔÚ¸öÈËÓû§ä¯ÀÀÆ÷µÄ±¾µØ´æ´¢ÖС£

ÔÚÕâЩÓû§²é¿´ÆÀÂÛµÄͬʱ£¬ËûÃǵÄä¯ÀÀÆ÷»á¼ÌÐø¸ø±Ë´Ë·¢ÐźÅÒÔ±£³ÖͨÐÅͨµÀµÄ¿ªÍ¨¡£Òò´Ë£¬Óû§¿ÉÒÔ·¢±íÆäËûÐÂµÄÆÀÂÛ£¬Èçͼ12¼°ÏÂÃæµÄ´úÂëÆ¬¶ÎËùʾ¡£

$scope.addComment = function ()

{

var newInput = $scope.newComment.trim();

if (!newInput.length)

{

return;

}

var currentComments = $scope.comments;

p2pModel.aggregateAndStoreComments(groupName, newInput, currentComments)

.success(function(updatedComments)

{

webRTCDatachannel.send(updatedComments);

$scope.comments = updatedComments;

})

.error(function(error)

{

alert('Failed to save the new comment' + error);

});

$scope.newComment = '';

}

µ±ÐÂµÄÆÀÂÛ·¢±í³öÀ´Ê±£¬p2pControllerÊ×ÏÈʹÓÃp2pModelÕë¶Ô¸ÃÖ÷Ìâ¾Û¼¯ºÍ¸üб¾µØ´æ´¢£¨ÈçÏÂËùʾ£©¡£È»ºó£¬Í¨¹ýDataChannelJS½«¸üÐÂµÄÆÀÂÛ·¢Ë͸øÆäËû²ÎÓëÕß¡£

aggregateAndStoreComments: function (groupName, comment, currentComments)

{

var savedComments = $window.localStorage.getItem(groupName);

if(savedComments !== null)

{

savedComments = JSON.parse(savedComments);

}

var updatedComments = aggregateComments(comment, currentComments, savedComments);

storeComments(groupName, updatedComments, $window);

return handlePromise($q, updatedComments);

}

µ±ÆäËû²ÎÓëÕß½ÓÊÕµ½¸üÐÂµÄÆÀÂÛʱ£¬ÆÀÂÛ±»ÏÔʾÔÚ¸ÃWebÒ³ÃæÉÏ£¬²¢±£´æµ½ËûÃǵı¾µØ´æ´¢ÖС£

webRTCDatachannel.onmessage = function (newInput, userId)

{

p2pModel.aggregateAndStoreComments(groupName, "", newInput)

.success(function(updatedComments)

{

$scope.comments = updatedComments;

})

.error(function(error)

{

alert('Failed to save the new comment' + error);

});

}

×ܽá

¾¡¹ÜMVC¼Ü¹¹·½·¨µÄ½»»¥ºÍÏìÓ¦µÄµä·¶ÔÚÍòÎ¬ÍøÇ°¶þÊ®ÄêÆÚ¼äµÄWebÓ¦ÓÃÁìÓòµÄÓ¦ÓüõÈõÁË£¬µ«½üÆÚµÄ½ø²½ÓÖʹ¸Ã»ù´¡ÀíÂÛµÃÒÔÔÚWeb¿ª·¢ÉçÇø¸´ÐË¡£±ê׼ͨÐÅЭÒé¼°ÆäÌØÓеÄInSoRÄÜÁ¦Ê¹ÐÅÏ¢±ä¸üʼþ¿ÉÒÔʵʱµØ¶¯Ì¬ºÍÒ첽ѭ»·±éÀúWebÓ¦ÓÃϵͳµÄ±ß½ç¡£ÕâЩʹÏÖ´úWebÓ¦Óÿª·¢ÈËÔ±¿ÉÒÔÀûÓÃdWMVCºÍpWMVC¼Ü¹¹·¶Ê½È¥Íê³ÉMVC-esqueʵʱ±ä¸ü¹Û²ì¡°Ê¼þÑ­»·¡±£¬°´¶à±äµÄÁ÷ÐзçÉд´½¨ÎÞ·ì¡¢¸ßЧµÄÏìӦʽӦÓÃÐÐΪ¡£ÕâЩ¹¤¾ß²»½ö¿ÉÓ¦ÓÃÓÚÏÖ´úеķþÎñÆ÷¶ËÔËÐÐÆÚ»·¾³£¬Ò²¿ÉÒÔÓÃÓÚ´«Í³µÄÖмä¼þ¼Ü¹¹¡£

   
2591 ´Îä¯ÀÀ       30
Ïà¹ØÎÄÕÂ

ÆóÒµ¼Ü¹¹¡¢TOGAFÓëArchiMate¸ÅÀÀ
¼Ü¹¹Ê¦Ö®Â·-ÈçºÎ×öºÃÒµÎñ½¨Ä££¿
´óÐÍÍøÕ¾µçÉÌÍøÕ¾¼Ü¹¹°¸ÀýºÍ¼¼Êõ¼Ü¹¹µÄʾÀý
ÍêÕûµÄArchimateÊÓµãÖ¸ÄÏ£¨°üÀ¨Ê¾Àý£©
Ïà¹ØÎĵµ

Êý¾ÝÖÐ̨¼¼Êõ¼Ü¹¹·½·¨ÂÛÓëʵ¼ù
ÊÊÓÃArchiMate¡¢EA ºÍ iSpace½øÐÐÆóÒµ¼Ü¹¹½¨Ä£
ZachmanÆóÒµ¼Ü¹¹¿ò¼Ü¼ò½é
ÆóÒµ¼Ü¹¹ÈÃSOAÂ䵨
Ïà¹Ø¿Î³Ì

ÔÆÆ½Ì¨Óë΢·þÎñ¼Ü¹¹Éè¼Æ
ÖÐ̨սÂÔ¡¢ÖÐ̨½¨ÉèÓëÊý×ÖÉÌÒµ
ÒÚ¼¶Óû§¸ß²¢·¢¡¢¸ß¿ÉÓÃϵͳ¼Ü¹¹
¸ß¿ÉÓ÷ֲ¼Ê½¼Ü¹¹Éè¼ÆÓëʵ¼ù

×îл¼Æ»®
DeepSeekÔÚÈí¼þ²âÊÔÓ¦ÓÃʵ¼ù 4-12[ÔÚÏß]
DeepSeek´óÄ£ÐÍÓ¦Óÿª·¢Êµ¼ù 4-19[ÔÚÏß]
UAF¼Ü¹¹ÌåϵÓëʵ¼ù 4-11[±±¾©]
AIÖÇÄÜ»¯Èí¼þ²âÊÔ·½·¨Óëʵ¼ù 5-23[ÉϺ£]
»ùÓÚ UML ºÍEA½øÐзÖÎöÉè¼Æ 4-26[±±¾©]
ÒµÎñ¼Ü¹¹Éè¼ÆÓ뽨ģ 4-18[±±¾©]

Ïà¹ØÎÄÕÂ


ר¼ÒÊӽǿ´ITÓë¼Ü¹¹
Èí¼þ¼Ü¹¹Éè¼Æ
ÃæÏò·þÎñÌåϵ¼Ü¹¹ºÍÒµÎñ×é¼þ
ÈËÈËÍøÒÆ¶¯¿ª·¢¼Ü¹¹
¼Ü¹¹¸¯»¯Ö®ÃÕ
̸ƽ̨¼´·þÎñPaaS

Ïà¹ØÅàѵ¿Î³Ì


ÃæÏòÓ¦Óõļܹ¹Éè¼ÆÊµ¼ù
µ¥Ôª²âÊÔ+ÖØ¹¹+Éè¼ÆÄ£Ê½
Èí¼þ¼Ü¹¹Ê¦¡ª¸ß¼¶Êµ¼ù
Èí¼þ¼Ü¹¹Éè¼Æ·½·¨¡¢°¸ÀýÓëʵ¼ù
ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ¡ª¸ß¼¶Êµ¼ù
SOAÌåϵ½á¹¹Êµ¼ù

³É¹¦°¸Àý


Èñ°²¿Æ¼¼ Èí¼þ¼Ü¹¹Éè¼Æ·½·¨
³É¶¼ ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ
ÉϺ£Æû³µ ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ
±±¾© Èí¼þ¼Ü¹¹Éè¼Æ
ÉϺ£ Èí¼þ¼Ü¹¹Éè¼Æ°¸ÀýÓëʵ¼ù
±±¾© ¼Ü¹¹Éè¼Æ·½·¨°¸ÀýÓëʵ¼ù
ÉîÛÚ ¼Ü¹¹Éè¼Æ·½·¨°¸ÀýÓëʵ¼ù
ǶÈëʽÈí¼þ¼Ü¹¹Éè¼Æ¡ª¸ß¼¶Êµ¼ù