概要

Java后端 + 鸿蒙前端用户管理系统(全栈项目)

后端基于 **Spring Boot 4.x + MyBatis-Plus + MySQL** 搭建,采用三层架构提供 RESTful API,实现用户数据的增删改查与模糊搜索,返回 JSON 数据。

前端基于 **HarmonyOS + ArkTS** 开发,采用 MVVM 架构,使用 ArkUI 组件搭建界面,通过 HTTP 网络请求调用后端接口,实现用户列表展示、添加、删除等功能。

前后端分离,通过 JSON 格式交互数据,打通从数据库到手机屏幕的完整数据链路。

整体架构流程

用户操作(手机屏幕)
    ↓
鸿蒙前端(ArkTS + ArkUI)
├── View 层:UserPage 页面展示,用户点击按钮
├── ViewModel 层:UserViewModel 处理业务逻辑,管理状态
├── Model 层:UserModel 定义用户数据结构
├── 网络工具类:NetWorkUtil 发送 HTTP 请求
    ↓  GET/POST/DELETE(JSON 数据)
SpringBoot 后端(Java)
├── Controller 层:UserController 接收请求,返回 JSON
├── Service 层:UserService 处理增删改查业务
├── Mapper 层:UserMapper 操作数据库(MyBatis-Plus)
    ↓  SQL
MySQL 数据库(user_manager)
    ↓  返回数据
逐层向上返回 JSON → 鸿蒙 ViewModel 解析数据 → View 刷新界面

JAVA部分

鸿蒙ArkTs部分:

运行结果图

技术名词解释

用户操作(手机屏幕)
    ↓
HarmonyOS 前端(ArkTS + ArkUI + ComponentV2)
├── View 层:UserPage(@ComponentV2 + @Local)
├── ViewModel 层:UserViewModel(@ObservedV2 + @Trace)
├── Model 层:UserModel(实体类)
├── 网络工具类:NetWorkUtil(@kit.NetworkKit + Promise 异步)
    ↓  HTTP RESTful API(JSON)
SpringBoot 4.x 后端(内嵌 Tomcat,端口 8080)
├── Controller 层:UserController(@RestController + @RequestMapping)
├── Service 层:UserService(@Service + 业务逻辑封装)
├── Mapper 层:UserMapper(继承 BaseMapper,零 SQL CRUD)
    ↓  JDBC
MySQL 8.0 数据库(user_manager.user 表,自增主键)
    ↓  返回数据
JSON 响应 → ViewModel 解析 → @Trace 触发 UI 刷新 → ArkUI 组件渲染

技术细节

一:JAVA部分(必须使用springboot):

UserController:
package com.itheima.student_manager.controller;


import com.itheima.student_manager.entity.User;
import com.itheima.student_manager.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 用户管理 RESTful API
 * 完整路径前缀:/api/users
 */
@RestController // 所有方法返回值自动转为 JSON
@RequestMapping("/users") // 路径前缀
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 查询所有用户
     * GET http://localhost:8080/api/users
     */
    @GetMapping
    public List<User> listAll() {
        return userService.listAll();
    }

    /**
     * 根据ID查询单个用户
     * GET http://localhost:8080/api/users/1
     */
    @GetMapping("/{id}")
    public User getById(@PathVariable Long id) {
        return userService.getById(id);
    }

    /**
     * 根据姓名模糊查询
     * GET http://localhost:8080/api/users/search?name=张
     */
    @GetMapping("/search")
    public List<User> search(@RequestParam String name) {
        return userService.searchByName(name);
    }

    /**
     * 添加用户(接收 JSON 数据)
     * POST http://localhost:8080/api/users
     * 请求体:{"name": "张三", "age": 20, "email": "zs@qq.com"}
     */
    @PostMapping
    public String add(@RequestBody User user) {
        boolean success = userService.add(user);
        return success ? "添加成功" : "添加失败";
    }

    /**
     * 修改用户
     * PUT http://localhost:8080/api/users/1
     * 请求体:{"name": "李四", "age": 21, "email": "ls@qq.com"}
     */
    @PutMapping("/{id}")
    public String update(@PathVariable Long id, @RequestBody User user) {
        user.setId(id); // 把路径中的ID设置到用户对象上
        boolean success = userService.update(user);
        return success ? "修改成功" : "修改失败";
    }

    /**
     * 删除用户
     * DELETE http://localhost:8080/api/users/1
     */
    @DeleteMapping("/{id}")
    public String delete(@PathVariable Long id) {
        boolean success = userService.delete(id);
        return success ? "删除成功" : "删除失败";
    }
}
User
package com.itheima.student_manager.entity;



import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * 用户实体类
 * 对应数据库中的 user 表
 */
@Data // 自动生成 getter/setter/toString
@TableName("user") // 指定数据库表名
public class User {

    /**
     * 用户ID(主键,自动生成)
     */
    @TableId(type = IdType.AUTO)
    private Long id;

    /**
     * 用户姓名
     */
    private String name;

    /**
     * 用户年龄
     */
    private Integer age;

    /**
     * 用户邮箱
     */
    private String email;
}
UserMapper
package com.itheima.student_manager.mapper;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.student_manager.entity.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * 用户数据访问层
 * 继承 BaseMapper 后,自动拥有增删改查方法,无需写 SQL
 */
@Mapper
public interface UserMapper extends BaseMapper<User> {
    // 继承的方法:
    // insert(User user)          → 添加用户
    // deleteById(Long id)        → 根据ID删除
    // updateById(User user)      → 根据ID更新
    // selectById(Long id)        → 根据ID查询
    // selectList(null)           → 查询全部
    // selectPage(...)            → 分页查询
}
UserService
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.student_manager.entity.User;
import com.itheima.student_manager.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 用户业务逻辑层
 * 处理具体业务,如查重、数据校验等
 */
@Service // 声明为 Spring 的 Service 组件
public class UserService {

    @Autowired
    private UserMapper userMapper;

    /**
     * 查询所有用户
     */
    public List<User> listAll() {
        return userMapper.selectList(null);
    }

    /**
     * 根据ID查询用户
     */
    public User getById(Long id) {
        return userMapper.selectById(id);
    }

    /**
     * 根据姓名模糊查询
     */
    public List<User> searchByName(String name) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.like("name", name); // SQL: WHERE name LIKE '%name%'
        return userMapper.selectList(wrapper);
    }

    /**
     * 添加用户
     */
    public boolean add(User user) {
        return userMapper.insert(user) > 0; // 返回true表示成功
    }

    /**
     * 更新用户
     */
    public boolean update(User user) {
        return userMapper.updateById(user) > 0;
    }

    /**
     * 删除用户
     */
    public boolean delete(Long id) {
        return userMapper.deleteById(id) > 0;
    }
}

StudentManagerApplication
package com.itheima.student_manager;


import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.itheima.student_manager.mapper") // 扫描 Mapper 接口
public class StudentManagerApplication {
    public static void main(String[] args) {
        SpringApplication.run(StudentManagerApplication.class, args);
    }
}

application.yaml

server:
  servlet:
    context-path: /api
  port: 8080

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/user_manager?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: 123456

# MyBatis-Plus 自动建表
mybatis-plus:
  global-config:
    db-config:
      table-auto: update

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>student_manager</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>student_manager</name>
    <description>student_manager</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.12</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.42</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

二:鸿蒙部分(前端):

UserModel
export class UserModel {
  id: number;
  name: string;
  age: number;
  email: string;

  constructor(id: number, name: string, age: number, email: string) {
    this.id = id;
    this.name = name;
    this.age = age;
    this.email = email;
  }
}
NetWorkUtil
import { http } from '@kit.NetworkKit';

export class NetWorkUtil {
  private static async request(
    url: string,
    method: http.RequestMethod,
    data?: Object
  ): Promise<string> {
    let httpRequest = http.createHttp();

    return new Promise<string>((resolve, reject) => {
      httpRequest.on('headersReceive', (header: Object) => {
        console.info('响应头: ' + JSON.stringify(header));
      });

      let requestParams: http.HttpRequestOptions = {
        method: method,
        header: {
          // 关键:伪装成浏览器
          'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
          'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
          'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
          'Accept-Encoding': 'gzip, deflate, br',
          'Connection': 'keep-alive',
          'Cache-Control': 'no-cache',
        },
        expectDataType: http.HttpDataType.STRING,
        connectTimeout: 60000,
        readTimeout: 60000
      };

      if (method === http.RequestMethod.POST && data) {
        requestParams.extraData = JSON.stringify(data);
      }

      httpRequest.request(url, requestParams, (err: Error, httpResponse: http.HttpResponse) => {
        if (!err) {
          console.info('响应码: ' + httpResponse.responseCode);
          if (httpResponse.responseCode === 200) {
            // 修复 result 处理
            const result = httpResponse.result;
            const resultStr = Array.isArray(result) ? result.join('') : result.toString();
            console.info('获取成功,数据长度: ' + resultStr.length);
            resolve(resultStr);
          } else {
            console.error('响应码异常: ' + httpResponse.responseCode);
            reject(new Error(`请求失败,响应码: ${httpResponse.responseCode}`));
          }
        } else {
          console.error('请求失败: ' + JSON.stringify(err));
          reject(err);
        }

        httpRequest.off('headersReceive');
        httpRequest.destroy();
      });
    });
  }

  static async get(url: string): Promise<string> {
    return NetWorkUtil.request(url, http.RequestMethod.GET);
  }

  static async post(url: string, data: Object): Promise<string> {
    return NetWorkUtil.request(url, http.RequestMethod.POST, data);
  }

  static async delete(url: string): Promise<string> {
    return NetWorkUtil.request(url, http.RequestMethod.DELETE);
  }
}
UserViewModel
import { UserModel } from '../model/UserModel';
import { NetWorkUtil } from '../util/NetWorkUtil';

const BASE_URL: string = 'http://10.0.2.2:8080/api';

@ObservedV2
export class UserViewModel {
  @Trace users: UserModel[] = [];
  @Trace isLoading: boolean = false;
  @Trace resultMessage: string = '';

  /**
   * 将 JSON 字符串解析为 UserModel 数组
   */
  private parseUsers(responseStr: string): UserModel[] {
    const rawData: Object[] = JSON.parse(responseStr) as Object[];
    const userList: UserModel[] = [];
    for (let i = 0; i < rawData.length; i++) {
      const item: Record<string, Object> = rawData[i] as Record<string, Object>;
      const user: UserModel = new UserModel(
        item['id'] as number,
        item['name'] as string,
        item['age'] as number,
        item['email'] as string
      );
      userList.push(user);
    }
    return userList;
  }

  /**
   * 查询所有用户
   */
  async fetchAllUsers(): Promise<void> {
    this.isLoading = true;
    try {
      const responseStr: string = await NetWorkUtil.get(`${BASE_URL}/users`);
      this.users = this.parseUsers(responseStr);
    } catch (err) {
      this.resultMessage = '获取用户列表失败';
    } finally {
      this.isLoading = false;
    }
  }

  /**
   * 添加用户
   */
  async addUser(name: string, age: number, email: string): Promise<void> {
    this.isLoading = true;
    try {
      const data: Record<string, Object> = {
        'name': name,
        'age': age,
        'email': email
      };
      const responseStr: string = await NetWorkUtil.post(`${BASE_URL}/users`, data);
      this.resultMessage = responseStr;
      await this.fetchAllUsers();
    } catch (err) {
      this.resultMessage = '添加用户失败';
    } finally {
      this.isLoading = false;
    }
  }

  /**
   * 删除用户
   */
  async deleteUser(id: number): Promise<void> {
    this.isLoading = true;
    try {
      const responseStr: string = await NetWorkUtil.delete(`${BASE_URL}/users/${id}`);
      console.info('=== 删除返回: ' + responseStr);
      this.resultMessage = responseStr;
      await this.fetchAllUsers();
    } catch (err) {
      console.error('=== 删除失败: ' + JSON.stringify(err));
      this.resultMessage = '删除失败';
    } finally {
      this.isLoading = false;
    }
  }

  /**
   * 搜索用户
   */
  async searchUsers(name: string): Promise<void> {
    this.isLoading = true;
    try {
      const responseStr: string = await NetWorkUtil.get(`${BASE_URL}/users/search?name=${name}`);
      this.users = this.parseUsers(responseStr);
    } catch (err) {
      this.resultMessage = '搜索失败';
    } finally {
      this.isLoading = false;
    }
  }
}
UserPage
import { UserViewModel } from '../viewmodel/UserViewModel';
import { UserModel } from '../model/UserModel';

@ComponentV2
export struct UserPage {
  @Local viewModel: UserViewModel = new UserViewModel();
  @Local inputName: string = '';
  @Local inputAge: string = '';
  @Local inputEmail: string = '';

  aboutToAppear(): void {
    this.viewModel.fetchAllUsers();
  }

  @Builder
  itemBuilder(user: UserModel) {
    Row() {
      Text(`${user.id}`).width('10%').fontSize(14)
      Text(user.name).width('20%').fontSize(16)
      Text(`${user.age}`).width('10%').fontSize(14)
      Text(user.email).width('35%').fontSize(14).fontColor(Color.Gray)
      Button('删除')
        .fontSize(12)
        .onClick(() => {
          this.viewModel.deleteUser(user.id);
        })
    }
    .width('100%')
    .padding(10)
    .border({ width: { bottom: 0.5 }, color: Color.Gray })
  }

  build() {
    Column() {
      Text('用户管理系统')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 20, bottom: 10 })

      Row() {
        TextInput({ placeholder: '姓名', text: this.inputName })
          .width('25%')
          .onChange((value: string) => { this.inputName = value; })

        TextInput({ placeholder: '年龄', text: this.inputAge })
          .width('20%')
          .onChange((value: string) => { this.inputAge = value; })

        TextInput({ placeholder: '邮箱', text: this.inputEmail })
          .width('30%')
          .onChange((value: string) => { this.inputEmail = value; })

        Button('添加')
          .fontSize(12)
          .onClick(() => {
            this.viewModel.addUser(this.inputName, parseInt(this.inputAge), this.inputEmail);
            this.inputName = '';
            this.inputAge = '';
            this.inputEmail = '';
          })
      }
      .width('100%')
      .padding(10)

      if (this.viewModel.resultMessage !== '') {
        Text(this.viewModel.resultMessage)
          .fontColor(this.viewModel.resultMessage.includes('成功') ? Color.Green : Color.Red)
          .margin(5)
      }

      if (this.viewModel.isLoading) {
        LoadingProgress().width(30).height(30).margin(20)
      }

      List() {
        ForEach(this.viewModel.users, (user: UserModel) => {
          ListItem() {
            this.itemBuilder(user)
          }
        })
      }
      .layoutWeight(1)
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .padding(10)
  }
}
Index 
import { UserPage } from './UserPage';

@Entry
@ComponentV2
struct Index {
  build() {
    Column() {
      UserPage()
    }
    .width('100%')
    .height('100%')
  }
}
module.json5 网络请求依赖
"requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]

小结

完成了一个 Java 后端(SpringBoot + MyBatis-Plus)+ 鸿蒙前端(ArkTS + ArkUI) 的全栈学生管理系统,打通了前后端分离的完整数据链路。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐