2.4.1 View部分


  • SessionView

    chatroom.view.xml:


    <view name="UserInfo" lifecycle="session">
    	<bind name="name" table="userinfo">
    		<ref name="nickname" />
    	</bind>
    	<variable name="recvedmessage" type="ChatMessage" />
    	<variable name="sendedmessage" type="ChatMessage" />
    	<variable name="lasterror" type="int" />
    </view>
    

    • <variable name="recvedmessage" type="ChatMessage" /> 
      

      <variable name="sendedmessage" type="ChatMessage" > 
      

      这两个字段用来实现私聊。

      用户向当前的ChatRoom发送聊天类Message,这里getMessageTo()返回接受用户id, toid!=-1

      chatroom.js:


      function sendMessage() {
      	var sendmsg = document.getElementById('sendmsg');
      	var msg = sendmsg.value.trim();
      	if (msg.length > 0)
      		context.send(currentChatView, "message=" + getMessageTo() + "," + msg);
      	sendmsg.value = "";
      }
      

      服务器ChatRoom收到Message进行处理

      ChatRoom.java:


      protected void onMessage(String message, long sessionid) {
      	int index = message.indexOf('=');
      	if (-1 == index)
      		return;
      	final String cmd = message.substring(0, index).trim();
      	final String params = message.substring(index + 1);
      	switch (cmd) {
      	case "message": {
      		..................................
      		if (-1L == toid) {
      		..................................
      		} else {
      			UserInfo.getInstance(toid).setRecvedmessage(new ChatMessage(msg, sessionid));
      			UserInfo.getInstance(sessionid).setSendedmessage(new ChatMessage(msg, toid));
      			RoomInfo.increment_privateMessages_mapkey(roomid);
      		}
      		return;
      	}
      	..................................
      }
      

      UserInfo.getInstance(toid).setRecvedmessage 将消息和发送用户id通告给接收用户

      UserInfo.getInstance(sessionid).setSendedmessage 将消息和接收用户id通告回发送用户

      chatroom.js:


      ctx.register(v100.chatviews.UserInfo, "recvedmessage", function(e) {
      	onReceiveMessage(e.value);
      });
      ctx.register(v100.chatviews.UserInfo, "sendedmessage", function(e) {
      	onSendedMessage(e.value);
      });
      

      接收用户显示 xxx to you : msg

      发送用户显示 you to xxx : msg



    • <bind name="name" table="userinfo">
      

      这个字段用来设置用户nickname

      UserInfo.java:


      private UserInfo(SessionView.CreateParameter param) {
      	super(param);
      	// bind here
      	bindUserinfo(sessionid);
      	long sessionid = param.getSessionId();
      	Procedure.execute(ProcedureHelper.nameProcedure("UserInfo init nickname", () -> {
      		xbean.UserInfo userinfo = table.Userinfo.insert(sessionid);
      		if (userinfo != null)
      			userinfo.setNickname("");
      		return true;	
      	}));
      	logintime = System.currentTimeMillis();
      	checkUpdateHallInfos();
      }
      

      用户登入,UserInfo被自动创建,其中


      <bind name="name" table="userinfo">
      	<ref name="nickname" />
      </bind>
      

      chatroom.zdb.xml:


      <xbean name="UserInfo">
      	<variable name="nickname" type="string" />
      </xbean>
      <table name="userinfo" key="long" value="UserInfo" />
      

      bindUserinfo建立了name与userinfo表的记录xbean字段nickname的绑定, userinfo表的key对应到这里的sessionid,也就是说,一旦userinfo表中,对应的xbean字段nickname发生改变,这个改变立刻同步到客户端。bindUserinfo时,如果sessionid对应的记录存在,则将记录同步给客户端,作为初始值;如果不存在就不发送,表示没有,这种情况下bind建立的关系依然存在,以后发生了改变再同步到客户端。

      接下的存储过程初始化nickname,如果insert成功表示记录并不存在,这种情况下把nickname设置为空串,客户端检测到空串,就应该初始化nickname。如果insert失败,表示记录存在,这种情况下,前面的bindUserinfo会把记录同步到客户端。

      总的来说,这是一种典型的bind方式,先bind,然后初始化对应记录,要么记录存在,记录的值被发送出去,要么存储过程初始化了值,导致了改变,由于bind的关系,这个初始化的值被发送出去。

      chatroom.js:


      ctx.register(v100.chatviews.UserInfo, "name", function(e) {
      	if (e.value.nickname == "")
      		doRename();
      	else
      		showNickname();
      });
      

      客户端检测到name的值发生变化,如果为空串,则调用doRename初始化nickname。

      chatroom.js:


      function doRename() {
      	var v100 = context[100];
      	var name = prompt("input name", v100.chatviews.UserInfo.name.nickname);
      	if (name != null && name.length != 0 && name != v100.chatviews.UserInfo.name.nickname)
      		context.send(v100.chatviews.UserInfo, "nickname=" + name);
      }
      

      初始化名字,改名均用这个方法,获取到名字以后向UserInfo发送Message告知新的name。

      UserInfo.java:


      protected void onMessage(String message, long sessionid) {
      	final int index = message.indexOf('=');
      	if (-1 == index)
      		return;
      	checkUpdateHallInfos();
      	final String cmd = message.substring(0, index).trim();
      	final String param = message.substring(index + 1).trim();
      	switch (cmd) {
      	case "nickname":
      		Procedure.execute(ProcedureHelper.nameProcedure("onMessage nickname", () -> {
      			xbean.UserInfo info = table.Userinfo.update(sessionid);
      			if (param.equals(info.getNickname())) 
      				UserInfo.this._setLasterror(ErrorCodes.EC_NAME_UNMODIFIED);
      				return false;
      			}
      			if (!GlobalId.create("chatserver", param)) {
      				UserInfo.this._setLasterror(ErrorCodes.EC_NAME_EXISTING);
      				return false;
      			}
      			info.setNickname(param);
      			SysInfo.increment_nickNameChange();
      			return true;
      		}));
      		return;
      	..............................................
      	}
      }
      

      这里第一个if判断nickname是否发生改变,这个用来避免客户端漏写检测。

      第二个if向GlobalId注册一个这个名字,如果返回false表示重名,报告重名错误,这里ErrorCodes.EC_NAME_EXISTING = 2,GlobalId的create, delete, exists三个方法必须在事务环境中使用, 也就是说写在存储过程内部,这些方法参与事务,也就是说如果事务回滚,分配的名字自动被释放。

      注意到两个报错使用了_setLasterror,View的下划线版本的字段设置方法一旦调用立即更新View,向客户端同步数据;非下划线版本在事务环境下,直到事务提交才真正更新View,事务回滚则忽略修改。报告错误使用下划线版本属于通常用法。

      setNickname在userinfo表中修改了当前用户的nickname,因为之前已经bind,所以修改成功以后,这个修改后的值通过Userinfo的bind字段name被自动同步到客户端。

      如果修改成功,在客户端:

      chatroom.js:


      ctx.register(v100.chatviews.UserInfo, "name", function(e) {
      	if (e.value.nickname == "")
      		doRename();
      	else
      		showNickname();
      });
      

      这里显示修改后的nickname;

      如果修改过程检测到重名:

      chatroom.js:


      ctx.register(v100.chatviews.UserInfo, "lasterror", function(e) {
      	if (e.value == 2)
      		doRename();
      	else
      		showErrorFrame(e.value);
      });
      

      重名返回错误码2,这里要求重新Rename。其它错误显示一个错误页面。



    • <variable name="lasterror" type="int" />
      

      向用户报告错误,错误码定义在:

      chatroom.view.xml:


      <bean name="ErrorCodes">
      	<enum name="EC_SUCCEED" value="0" />
      	<enum name="EC_NAME_UNMODIFIED" value="1" />
      	<enum name="EC_NAME_EXISTING" value="2" />
      	<enum name="EC_BAD_ROOM_ID" value="11" />
      	<enum name="EC_BAD_ARGS" value="12" />
      	<enum name="EC_BAD_HALL_NAME" value="13" />
      	<enum name="EC_BAD_ROOM_NAME" value="14" />
      </bean>
      

上一页 下一页