领域模型应用 API接口以及页面实现

领域模型应用 API接口以及页面实现

一、概述

本章核心内容是将领域模型从API接口模型模板数据查询到前端页面输出展示。项目主要由服务模块、路由模块以及对应的测试代码组成。服务模块负责处理业务逻辑和数据获取,路由模块负责将客户端的请求准确分发到相应的处理逻辑,而测试代码则用于确保系统的各个接口能够正常工作,保证系统的稳定性和可靠性。

二、服务端API接口实现

2.1 服务端核心文件

app/service/project.js

此文件为服务模块,其主要职责是封装与项目数据相关的业务逻辑,为上层的路由模块和其他业务模块提供统一的数据获取接口。

js 复制代码
//service/project.js
module.exports = (app) => {
  const BaseService = require("./base")(app);
  const modelList = require("../../model/index.js")(app);

  return class ProjectService extends BaseService {
    // 获取所有模型与项目的结构化数据
    async getModelList() {
      return modelList;
    }
  };
};
代码解释
  • 模块导出 :使用 module.exports 导出一个函数,该函数接收 app 作为参数。app 通常是 Koa 应用实例,它包含了整个应用的上下文信息,通过传入 app,可以让服务模块与应用的其他部分进行交互。

  • 依赖引入

    • BaseService:从 ./base 模块引入,并传入 app 进行初始化。BaseService 可能是一个基础服务类,包含了一些通用的服务方法和属性,ProjectService 继承自它可以复用这些通用功能,提高代码的复用性。
    • modelList:从 ../../model/index.js 模块引入,同样传入 app 进行初始化。modelList 代表了所有领域模型与项目的结构化数据。
  • 类定义与方法 :定义了 ProjectService 类,它继承自 BaseServicegetModelList 方法是一个异步方法,用于获取 modelList 领域模型与项目的结构化数据。


/app/router/project.js

该文件为路由模块,其核心功能是定义路由规则,将客户端的请求路径映射到相应的控制器方法,实现请求的分发和处理。

js 复制代码
//router/project.js
module.exports = (app, router) => {
  const { project: projectController } = app.controller;
  router.get(
    "/api/project/model_list",
    projectController.getModelList.bind(projectController)
  );
};
代码解释
  • 模块导出 :使用 module.exports 导出一个函数,该函数接收 approuter 作为参数。app 是 Koa 应用实例,router 是 koa-router 路由实例,用于定义和管理路由规则。
  • 控制器提取 :通过对象解构赋值从 app.controller 中提取 project 控制器,并将其赋值给 projectController。这样做的好处是可以更方便地使用 project 控制器中的方法。
  • 路由定义 :使用 router.get 方法定义一个处理 GET 请求的路由。当客户端发送一个 GET 请求到 /api/project/model_list 路径时,会调用 projectControllergetModeList 方法,并使用 bind 方法将 getModeList 方法的 this 上下文绑定到 projectController 对象上,确保在 getModeList 方法内部使用 this 时,它指向的是 projectController 对象。

2.2 服务端模块的调用关系

路由模块(app/router/project.js)负责接收客户端的请求,并将请求分发到相应的控制器方法。在这个过程中,控制器方法会调用服务模块(app/service/project.js)中的方法来处理业务逻辑和获取数据。

具体调用流程如下:

  1. 客户端发送一个 GET 请求到 /api/project/model_list 路径。
  2. 路由模块接收到请求后,根据路由规则调用 projectControllergetModeList 方法。
  3. getModeList 方法在控制器中被调用,它会进一步调用服务模块中 ProjectService 类的 getModelList 方法。
  4. getModelList 方法从 ../../model/index.js 模块中获取 modelList 数据,并将其返回给控制器。
  5. 控制器将获取到的数据返回给客户端。

2.3 Mocha 测试文件结构

Mocha测试用例文件 /test/controller/project.test.js
Mocha 是一个功能强大的 JavaScript 测试框架,主要用于 Node.js 和浏览器环境中的单元测试和集成测试,对项目相关的接口进行测试,确保接口的功能正确性和稳定性。

js 复制代码
// test/controller/project.test.js
const assert = require("assert");
const supertest = require("supertest");
const md5 = require("md5");
const eplisCore = require("../../elpis-core");

const signKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCg";
const st = Date.now();

describe("测试 project 相关接口", function () {
  this.timeout(60000);

  let request;

  it("启动测试服务", async () => {
    const app = await eplisCore.start();
    request = supertest(app.listen());
  });

  it("GET /api/project/model_list", async () => {
    let tmpRes = request.get("/api/project/model_list");
    tmpRes = tmpRes.set("s_t", st);
    tmpRes = tmpRes.set("s_sign", md5(`${signKey}_${st}`));
    const res = await tmpRes;
    assert(res.body.success === true);

    const resData = res.body.data;
    assert(resData.length > 0);
    for (let i = 0; i < resData.length; i++) {
      const item = resData[i];
      assert(item.model);
      assert(item.model.key);
      assert(item.model.name);

      assert(item.project);
      for (const projKey in item.project) {
        assert(item.project[projKey].name);
        assert(item.project[projKey].key);
      }
    }
  });
});
配置启动脚本 (package.json)
json 复制代码
"scripts": {
    "test": "set _ENV='local' && mocha 'test/**/*.js'",
}
说明:
  1. set _ENV='local'
    设置环境变量 _ENV'local',通常用于指定测试运行的环境(如本地开发环境)。
  2. mocha 'test/**/*.js'
    使用 Mocha 执行所有位于 test 目录下的 .js 文件中的测试用例。
  3. 运行 npm test 后,Mocha 会自动找到并执行该测试文件。
测试用例代码解释
  • 依赖引入

    • assert:用于进行断言操作,验证测试结果是否符合预期。
    • supertest:用于模拟 HTTP 请求,方便对接口进行测试。
    • md5:用于生成签名,模拟请求时的签名验证。
    • eplisCore:用于启动测试服务,获取 Koa 应用实例。
  • 测试用例

    • 启动测试服务:调用 eplisCore.start() 方法启动测试服务,并使用 supertest 创建一个请求对象,用于后续的接口测试。
    • GET /api/project/model_list:模拟发送一个 GET 请求到 /api/project/model_list 接口,设置请求头 s_ts_sign,并对响应结果进行断言。确保响应数据的结构和内容符合预期,如 success 字段为 true,数据列表不为空,每个数据项包含 modelproject 字段,且这些字段包含必要的属性。
  • 具体调用流程如下

    • 测试代码调用 eplisCore.start() 方法启动测试服务,获取 Koa 应用实例。
    • 使用 supertest 创建一个请求对象,模拟客户端发送请求到 /api/project/model_list 接口。
    • 路由模块接收到模拟请求后,按照正常的请求处理流程,调用控制器方法,控制器方法再调用服务模块的 getModelList 方法获取数据。
    • 服务模块返回数据给控制器,控制器将数据返回给测试代码。
    • 测试代码使用 assert 进行断言,验证响应数据是否符合预期。

三、客户端页面实现

3.1 客户端文件及功能

  1. entry.project-list.js 项目入口文件
js 复制代码
// entry.project-list.js
import boot from "$pages/boot.js";
import projectList from "./project-list.vue";

boot(projectList);
代码解释
  • 导入模块

    • boot:从 $pages/boot.js 导入,用于初始化和启动组件。
    • projectList:从 ./project-list.vue 导入项目列表组件。
  • 启动组件 :调用 boot 函数并传入 projectList 组件,完成项目列表页面的初始化和启动。

  1. project-list.vue 项目列表组件
模板部分
js 复制代码
// project-list.vue
<template>
  <header-container title="项目列表">
    <template #main-content>
      <div v-loading="loading">
        <div v-for="item in modelList" :key="item.model?.key">
          <!-- 展示 model -->
          <div class="model-panel">
            <el-row type="flex" align="middle">
              <div class="title">
                {{ item.model?.name }}
              </div>
            </el-row>
            <div class="divider" />
          </div>
          <!-- 展示 project -->
          <el-row flex class="project-list">
            <el-card
              v-for="projectItem in item.project"
              :key="projectItem.key"
              class="project-card"
            >
              <!-- project 头部 -->
              <template #header>
                <div class="title">
                  {{ projectItem.name }}
                </div>
              </template>
              <!-- project 主体 -->
              <div class="content">
                {{ projectItem.desc ?? "----" }}
              </div>
              <!-- project 底部 -->
              <template #footer>
                <el-row justify="end">
                  <el-button link type="primary" @click="onEnter(projectItem)">
                    进入
                  </el-button>
                </el-row>
              </template>
            </el-card>
          </el-row>
        </div>
      </div>
    </template>
  </header-container>
</template>
代码解释
  • 使用 header-container 组件:作为页面的整体布局,设置标题为"项目列表"。
  • main-content 插槽:用于展示项目列表的具体内容。
  • 加载状态 :使用 v-loading 指令根据 loading 状态显示加载动画。
  • 循环渲染 :使用 v-for 指令遍历 modelList 数组,展示项目模型和具体项目。
脚本部分
js 复制代码
// project-list.vue
<script setup>
import { ref, onMounted } from "vue";
import $curl from "$common/curl.js";
import headerContainer from "$widgets/header-container/header-container.vue";

const loading = ref(false);
const modelList = ref([]);
const getModelList = async () => {
  loading.value = true;
  const res = await $curl({
    method: "GET",
    url: "/api/project/model_list",
    errorMessage: "获取项目列表失败",
  });
  loading.value = false;
  if (!res || !res.data || !res.success) {
    return;
  }

  modelList.value = res.data;
};

const onEnter = (projectItem) => {
  console.log("on enter project", projectItem);
};

onMounted(() => {
  getModelList();
});
</script>
代码解释
  • 导入模块

    • refonMounted:从 vue 导入,用于响应式数据和生命周期钩子。
    • $curl:从 $common/curl.js 导入,用于发送 HTTP 请求。
    • headerContainer:从 $widgets/header-container/header-container.vue 导入头部容器组件。
  • 响应式数据

    • loading:用于控制加载状态。
    • modelList:用于存储项目模型列表。
  • 获取项目列表getModelList 函数通过 $curl 发送 GET 请求获取项目列表,并更新 modelList

  • 进入项目事件onEnter 函数处理进入项目的点击事件。

  • 生命周期钩子onMounted 钩子在组件挂载后调用 getModelList 函数。

样式部分
css 复制代码
/* project-list.vue */
<style lang="less" scoped>
.model-panel {
  margin: 20px 50px;
  min-width: 500px;
  .title {
    font-size: 25px;
    font-weight: bold;
    color: #6d6c6c;
  }
  .divider {
    margin-top: 10px;
    border-bottom: 1px solid #d7d7d7;
    width: 200px;
  }
}

.project-list {
  margin: 0 50px;
  .project-card {
    margin-right: 30px;
    margin-bottom: 20px;
    width: 300px;
    .title {
      font-weight: bold;
      font-size: 17px;
      color: #47a2ff;
    }
    .content {
      height: 70px;
      color: darkgrey;
      font-size: 15px;
      overflow: auto;
    }
  }
}
</style>
代码解释
  • 使用 Less 预处理器:提高样式代码的可维护性。
  • scoped 属性:确保样式只作用于当前组件。
  • 样式定义:定义了项目模型面板和项目卡片的样式。
  1. header-container.vue 页面头部组件
模板部分
js 复制代码
// header-container.vue
<template>
  <el-container class="header-container">
    <el-header class="header">
      <el-row type="flex" align="middle" class="header-row">
        <!-- 左侧 上方 标题区域 -->
        <el-row type="flex" align="middle" class="title-panel">
          <img src="./asserts//icon.png" class="logo" />
          <el-row class="text">
            {{ title }}
          </el-row>
        </el-row>
        <!-- 插槽: 菜单区域 -->
        <slot name="menu-container" />
        <!-- 右侧: 上方 设置| 用户区域 -->
        <el-row type="flex" align="middle" justify="end" class="setting-panel">
          <!-- 插槽:设置区域  -->
          <slot name="setting-container" />
          <img src="./asserts//avatar.png" class="avatar" />
          <el-dropdown @command="hanleUserCommand">
            <span class="user-name">
              {{ userName }}
              <i class="el-icon-arrow-down el-icon--right" />
            </span>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item>我的消息</el-dropdown-item>
                <el-dropdown-item command="logout"> 退出登录 </el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </el-row>
      </el-row>
    </el-header>
    <el-main class="main-container">
      <!-- 核心内容: 外部拓展填充区域 -->
      <slot name="main-content" />
    </el-main>
  </el-container>
</template>
代码解释
  • 使用 ElementPlus 组件el-containerel-headerel-row 等,构建头部容器的布局。
  • 插槽 :提供 menu-containersetting-containermain-content 三个插槽,用于外部拓展内容。
  • 用户信息:展示用户头像和用户名,并提供下拉菜单,包含"我的消息"和"退出登录"选项。
脚本部分
js 复制代码
// header-container.vue
<script setup>
import { ref } from "vue";
defineProps({
  title: String,
});

const userName = ref("admin");

const hanleUserCommand = (event) => {
  console.log("handle user command");
};
</script>
代码解释
  • 导入模块refvue 导入,用于响应式数据。
  • 定义属性 :使用 defineProps 定义 title 属性,用于设置页面标题。
  • 响应式数据userName 用于存储用户名,初始值为"admin"。
  • 用户命令处理hanleUserCommand 函数处理用户下拉菜单的命令。
样式部分
css 复制代码
/* header-container.vue */
<style lang="less" scoped>
.header-container {
  height: 100%;
  // min-width: 1000px;
  overflow: hidden;
  .header {
    max-height: 120px;
    border-bottom: 1px solid #e8e8e8;

    .header-row {
      height: 60px;
      padding: 0 20px;
      .title-panel {
        width: 180px;
        min-width: 180px;
        .logo {
          margin-right: 10px;
          width: 25px;
          width: 25px;
          border-radius: 50%;
        }
        .text {
          font-size: 15px;
          font-weight: 500;
        }
      }
      .setting-panel {
        margin-right: auto;
        min-width: 180px;
        flex: 1;
        .avatar {
          margin-right: 12px;
          width: 25px;
          height: 25px;
          border-radius: 50%;
        }
        .user-name {
          font-size: 16px;
          font-weight: 500;
          cursor: pointer;
          height: 60px;
          line-height: 60px;
          outline: none;
        }
      }
    }
  }
  .main-container {
  }
}

:deep(.el-header) {
  padding: 0;
}
</style>
代码解释
  • 使用 Less 预处理器:提高样式代码的可维护性。
  • scoped 属性:确保样式只作用于当前组件。
  • 样式定义:定义了头部容器、标题区域、设置区域和用户信息区域的样式。

五、总结

服务端通过 app/service/project.js 封装业务逻辑,app/router/project.js 定义路由规则,将客户端请求分发到相应处理逻辑,同时使用 Mocha 进行接口测试以确保系统稳定性;客户端以 entry.project-list.js 为入口,project-list.vue 展示项目列表,header-container.vue 构建页面头部,利用 Vue 的响应式特性和组件化开发,结合 ElementPlus 组件库和 Less 样式预处理器,实现页面的渲染和交互。围绕领域模型应用展开,核心思想是实现从 API 接口模型模板数据查询到前端页面输出展示的完整流程。

相关推荐
moxiaoran57531 小时前
uni-app萌宠案例学习笔记--页面布局和CSS样式设置
前端·css·uni-app
CrissChan2 小时前
Pycharm 函数注释
java·前端·pycharm
小小小小宇3 小时前
Vue.nextTick()笔记
前端
小约翰仓鼠4 小时前
vue3子组件获取并修改父组件的值
前端·javascript·vue.js
Lin Hsüeh-ch'in4 小时前
Vue 学习路线图(从零到实战)
前端·vue.js·学习
烛阴4 小时前
bignumber.js深度解析:驾驭任意精度计算的终极武器
前端·javascript·后端
计蒙不吃鱼4 小时前
一篇文章实现Android图片拼接并保存至相册
android·java·前端
全职计算机毕业设计5 小时前
基于Java Web的校园失物招领平台设计与实现
java·开发语言·前端
啊~哈5 小时前
vue3+elementplus表格表头加图标及文字提示
前端·javascript·vue.js