随诊医生社区医生版极速开发直播1.6

在这一节中,我们将实现首页医生日程列表条目从服务器端获取并显示的功能,主要涉及数据库设计和Node操作数据库的技术。

代码重构

我们在进行项目开发时,由于考虑不周等原因,经常在后期发现前期的实现存在问题,这就需要进行代码重构,以使代码结构更加合理,更加利于维护。我们倡导经常进行代码重构,这样可以提高代码质量和可维护性。
我们在进行网络访问时,直接给出了URL,如果我们一直采用这种方式,假设我们有100个网络请求,那么就有100个完整的URL,这时如果我们想改一下服务器的地址或从HTTP转为HTTPS,那么我们就需要修改100处,而且还不保证能改完,因此我们需要对这种方式进行重构。
我们首先定义一个存储全局变量的模块,在lib目录创建common目录,在其中创建全局类AppGlobal.dart,如下所示:

// 程序中的全局变量
// 需要在程序启动时加入hospitalId, deptId, doctorId参数
String serverBase = 'http://192.168.1.106:8443/';
String reqVerPlat = '?v=1&p=1'; // p:1-Android;2-IOS;3-web
String loginUser = ''; // hospitalId, deptId, doctorId
// 系统中所有可能用到的请求
String req_getServerDeault = serverBase + 'getServerDefault' +
        reqVerPlat;

void initAppGlobal() {
  initNetReqs();
}

void initNetReqs() {
  loginUser = '&hospitalId=1&deptId=2&doctorId=3';
  req_getServerDeault += loginUser;
}

在上面代码中,我们统一定义了服务器地址和协议,同时定了版本号和平台,这样我们服务器端就可以根据版本号和平台的平台,提供不同的服务。所有的网络请求都以req_为前缀。并定义了全局变量初始化方法,在系统启动时调用,对全局变量进行初始化,在这里我们需要为每个网络请求添加上注册用户对应的医院编号、科室编号、医生编号。
在程序开始时,需要调用全局变量初始化方法,我们在main.dart进行调用,如下所示:

import './common/AppGlobal.dart' as ag;
...
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
  int _counter = 0;
  TabController tabController;

  @override
  void initState() {
    // 初始化全局变量
    ag.initAppGlobal();
    tabController = new TabController(vsync: this, length: 5);
  }

我们在发起请求时,直接使用全局变量中定义的请求即可,在GetServerDefault.dart中的代码如下所示:

import 'package:szys/common/AppGlobal.dart' as ag;

Future<GetServerDefaultResp> fetchPost() async {
  final response =
  await http.get(ag.req_getServerDeault + '&msg=Hello&zw=中国');

  if (response.statusCode == 200) {
  ......
}

日程列表服务器端接口

我们先在服务器端实现一个获取日程条目的接口,在这里我们仅返回固定的数据用于调试开发。首先在index.js中定义一个新接口:

var server = require("./server.js");
var router = require("./router.js");
// 系统级请求
...
// 应用请求
var getServerDefaultHdlr = require("./controller/get_server_default.js");
var getSchedulesHdlr = require('./controller/get_schedules.js');


var handlers = new Object();
// 系统级请求
...
// 应用请求
handlers['/'] = getServerDefaultHdlr.processRequest;
handlers['/getServerDefault'] = getServerDefaultHdlr.processRequest;
handlers['/getSchedules'] = getSchedulesHdlr.processRequest;
......

如上所示,我们定义了getSchedules接口,接下来我们实现这个接口,我们先用固定的数据,在controller目录下创建get_schedules.js文件:

var exec = require("child_process").exec;

function processRequest(request, response) {
    console.log("get schedules\r\n");
    var options = {};
    options.timeout = 10000;
    options.maxBuffer = 20000 * 1024;
    response.writeHead(200, {"Content-Type": "application/json", "charset": "utf-8"});
    var obj = new Object();
    obj.total = 8;
    obj.startIdx = 0;
    obj.amount = 10;
    var idx = 0;
    obj.schedules = new Array();
    obj.schedules[idx++] = {
        'scheduleId': 1,
        'scheduleTypeId': 1,
        'scheduleTypeName': '出诊',
        'planStartTime': '2017-07-31 10:00:00',
        'onsiteAddr': '双榆树南里6#502',
        'patientId': 101,
        'patientName': '王一'
    };
......
    //
    obj.schedules[idx++] = {
        'scheduleId': 2,
        'scheduleTypeId': 2,
        'scheduleTypeName': '预约',
        'appointTime': '2017-08-02 15:00:00',
        'hospitalId': 201,
        'hospitalName': '海淀医院',
        'deptId': 301,
        'deptName': '普通内科',
        'appointStateId': 1,
        'appointStateName': '待批准',
        'patientId': 102,
        'patientName': '李一'
    };
    response.write(JSON.stringify(obj));
    response.end();
}

exports.processRequest = processRequest;

我们通过浏览器访问,会得到如下所示的结果,表明我们接口是开发成功的:


获取日程接口浏览器访问

客户端调用及显示

我们首先定义网络请求类,在lib/nets目录下创建GetSchedules.dart:

import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:szys/common/AppGlobal.dart' as ag;

Future<GetSchedulesResp> fetchPost() async {
  final response =
  await http.get(ag.req_getSchedules + '&msg=Hello&zw=中国');

  if (response.statusCode == 200) {
    // If the call to the server was successful, parse the JSON
    return GetSchedulesResp.fromJson(json.decode(response.body));
    //return json.decode(response.body);
  } else {
    // If that call was not successful, throw an error.
    throw Exception('Failed to load post');
  }
}

class GetSchedulesResp {
  final int total;
  final int startIdx;
  final int endIdx;
  final List<Map<String, dynamic>> schedules;

  GetSchedulesResp({this.total, this.startIdx, this.endIdx, this.schedules});

  factory GetSchedulesResp.fromJson(Map<String, dynamic> json) {
    return GetSchedulesResp(
      total: json['total'],
      startIdx: json['startIdx'],
      endIdx: json['endIdx'],
      schedules: new List<Map<String, dynamic>>.from(json['schedules']),
    );
  }
}

我们在日程页面的列表功能是在components/ScheduleListView.dart中实现的,我们将原来静态的列表内容,替换为从网络中获取。这里我们需要改变一下做法,在前面的例子中,我们直接在界面中显示网络获取到的数据,所以业务逻辑写在FutureBuilder里面了,但是现在我们使用的是列表控件,我们需要数据更新时,列表内容跟着改变,这就需要不同的实现方式了。
我们首先来看怎样进行网络服务调用获取数据,修改ScheduleListView.dart中的getScheduleItems方法:

 void getScheduleListItems(ScheduleListViewState stateObj) {
    List<ScheduleListItem> items = new List<ScheduleListItem>();
    GetSchedules.fetchPost().then((resp){
      int total = resp.total;
      int startIdx = resp.startIdx;
      int endIdx = resp.endIdx;
      List<Map<String, dynamic>> recs = resp.schedules;
      ScheduleListItem item = null;
      for (final rec in recs) {
        if (1 == rec['scheduleTypeId']) {
          // 出诊日程条目
          item = new OnsiteScheduleListItem(rec['scheduleId'],
              rec['planStartTime'], utf8.decode(rec['onsiteAddr'].codeUnits),
              rec['patientId'], utf8.decode(rec['patientName'].codeUnits)
          );
        } else if (2 == rec['scheduleTypeId']) {
          // 预约日程条目
          item = new AppointScheduleListItem(rec['scheduleId'],
              rec['hospitalId'], utf8.decode(rec['hospitalName'].codeUnits),
              rec['deptId'], utf8.decode(rec['deptName'].codeUnits),
              rec['appointId'], rec['appointTime'],
              rec['appointStateId'], utf8.decode(rec['appointStateName'].codeUnits),
              rec['patientId'], utf8.decode(rec['patientName'].codeUnits));
        } else {
          item = new OnsiteScheduleListItem(9999,
              '2999-09-28 10:30:00', '???????',
              102, '????');
        }
        items.add(item);
      }
      stateObj.onReceiveData(items);
    });
  }

在这里,我们看到方法的参数为ScheduleListViewState对象。因为GetSchedules里面定义的fetchPost方法返回的是Future对象,而Future对象在完成时会调用then里面的内容,我们将网络传过来的数据转化为列表控件需要的数据格式,这时我们不需要返回列表条目,而是调用ScheduleListViewState里面的onReceiveData方法,由该方法更新界面内容。
在ScheduleListViewState的build方法中,我们就不再去获取列表条目了,去掉该段代码。
我们重载State类的生命周期函数didUpdate,这个函数会在每次显示前调用,我们在这个方法中去获取列表数据:

  @override
  void didUpdateWidget(ScheduleListView oldWidget) {
    // TODO: implement didUpdateWidget
    debugPrint('didUpdateWidget is running...');
    getScheduleListItems(this);
    super.didUpdateWidget(oldWidget);
  }

当获取到网络数据并形成列表所需数据之后,我们调用了onReceiveData方法,这个方法将更新界面内容,更新的机制与React Native等前端机制差不太多,都是通过setState函数通知界面更新,代码如下所示:

  List<ScheduleListItem> items = new List<ScheduleListItem>();

  void onReceiveData(List<ScheduleListItem> _items) {
    debugPrint('onReceiveData is running...');
    setState(() {
      items = _items;
    });
  }

这时我们运行程序,会出现如下的界面:


程序运行界面

虽然界面上并没有明显的变化,但是数据已经是从后台服务器上获取的了。
在这一节里,我们虽然从服务器端获取到的列表的数据,但是这些数据在服务器端是在程序里定义的,而不是数据库驱动的。在下一节里,我们将讲述从数据库中读出相关内容并返回给客户端,敬请期待!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,492评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,048评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,927评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,293评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,309评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,024评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,638评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,546评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,073评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,188评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,321评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,998评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,678评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,186评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,303评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,663评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,330评论 2 358

推荐阅读更多精彩内容