4. TP 1:工资单的基本管理
4.1. Introduction
为了应用之前所学的内容,我们现在提出一项任务:编写一个适用于平板电脑的 Android 客户端,用于模拟某协会员工工资单的计算。
该应用将采用客户端/服务器架构:

- 服务器 [1] 已提供;
- 需自行构建 Android 客户端 [2]。
4.2. 数据库
4.2.1. 定义
用于生成工资单的静态数据将存储在一个数据库中,我们将其命名为 dbpam。该数据库包含以下表:
结构:
主键 | |
版本号——每修改一行即递增 | |
员工社保号——唯一编号 | |
员工姓名 | |
其名字 | |
他的地址 | |
他的城市 | |
他的邮政编码 | |
表[INDEMNITES]中字段[ID]的外键 |
其内容可能如下:

结构:
主键 | |
版本号——每修改一行即递增 | |
百分比:普遍社会缴费 + 社会债务偿还缴费 | |
百分比:可抵扣的通用社会保险费 | |
百分比:社会保险、丧偶保险、养老保险 | |
百分比:补充养老金 + 失业保险 |
其内容可能如下:
![]()
社会保险费率与员工无关。上表仅有一行。
主键 | |
版本号——每修改一行即递增 | |
处理索引 - 唯一 | |
每小时看护服务的净价(欧元) | |
每小时看护的欧元生活津贴 | |
每日看护的餐费补贴(欧元) | |
带薪休假补贴。该金额为基本工资的百分比。 |
其内容可能如下:

需注意,不同保育员的津贴可能有所不同。这些津贴实际上是通过保育员的薪级指数与特定保育员关联的。 例如,薪级指数为2的Marie Jouveinal女士(表EMPLOYES),其时薪为2.1欧元(表INDEMNITES)。
4.2.2. 生成
提供用于生成数据库的脚本 [dbpam_hibernate.sql]:
![]() |
创建数据库 [dbpam_hibernate] (这是 Web 服务器 / jSON 所使用的 BD 的名称),并确保登录名 root(无需密码)能够访问该数据库。您可以按以下步骤操作:
先启动 MySQL,然后启动 [PhpMyAdmin]:
![]() | ![]() |
- [1-2]:导入脚本 [dbpam_hibernate.sql] 并运行;
4.2.3. 数据库的Java建模
表 [EMPLOYES]、[INDEMNITES] 和 [COTISATIONS] 的字段由以下类进行建模:
[Employe]
package pam.entities;
import java.io.Serializable;
public class Employe implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private int version;
private String SS;
private String nom;
private String prenom;
private String adresse;
private String ville;
private String codePostal;
private int idIndemnite;
private Indemnite indemnite;
public Employe() {
}
public Employe(String SS, String nom, String prenom, String adresse, String ville, String codePostal, Indemnite indemnite) {
...
}
// 获取器和设置器
....
}
- 第 8-15 行:这些字段对应于表 [EMPLOYES] 的列;
- 第 16 行:字段 [indemniteId] 对应于表 [EMPLOYES] 的外键 [INDEMNITE_ID];
- 第17行:员工的津贴。该字段并非总是填写:
- 当查询 URL [/employes] 时,该字段为空,
- 但在查询 URL 和 [/salaire] 时会填入;
[Indemnite]
package pam.entities;
import java.io.Serializable;
public class Indemnite implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private int version;
private int indice;
private double baseHeure;
private double entretienJour;
private double repasJour;
private double indemnitesCp;
public Indemnite() {
}
public Indemnite(int indice, double baseHeure, double entretienJour, double repasJour, double indemnitesCP) {
...
}
// 获取器和设置器
....
}
- 第8-14行:字段对应于表[INDEMNITES]的列;
[Cotisation]
package pam.entities;
import java.io.Serializable;
public class Cotisation implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private int version;
private double csgrds;
private double csgd;
private double secu;
private double retraite;
public Cotisation() {
}
public Cotisation(double csgrds, double csgd, double secu, double retraite) {
...
}
// 获取器和设置器
...
}
- 第 8-13 行:字段对应于表 [COTISATIONS] 的列;
4.3. Web 服务器安装 / jSON
![]() |
4.3.1. 安装
已为您提供 Web 服务器的 Java 二进制文件 / jSON:
![]() |
要启动 Web 服务器 / jSON,请按以下步骤操作:
- 运行 SGBD MySQL;
- 确保 BD 和 [dbpam_hibernate] 存在;
- 打开一个 DOS 窗口;
- 进入 jar 文件所在的文件夹;
- 输入命令:
这假设二进制文件 [java.exe] 位于您机器上的 PATH 目录中。如果不是,请输入 [java.exe] 的完整路径,例如:
此时将显示日志:
- 第 16 行:发现 URL [/salaire/{SS}/{ht}/{jt}];
- 第 17 行:发现 URL [/employes];
4.3.2. Web 服务中的 URL / jSON
![]() |
Web 服务 / jSON 由 Spring MVC 实现,并暴露两个 URL:
@RequestMapping(value = "/employes", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public EmployesResponse getEmployes() {
...
@RequestMapping(value = "/salaire/{SS}/{ht}/{jt}", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public FeuilleSalaireResponse getFeuilleSalaire(@PathVariable("SS") String SS, @PathVariable("ht") double ht, @PathVariable("jt") int jt) {
该 Web 服务支持以下两个 URL 请求:
- 第 1 行:/employes:用于获取员工列表;
- 第 4 行:/salaire/SS/ht/jt:用于获取编号为 [SS] 的员工工资单,该员工在 [jt] 天内工作了 [ht] 小时;
以下是展示该功能的屏幕截图。
需要查询的员工:

关闭数据库,重启服务器,然后查询员工:

查询工资:

查询一个不存在人员的工资:

4.3.3. Web服务返回的响应:jSON/jSON
![]() |
![]() |
Web服务URL / jSON发送的响应类型为[Response<T>]:
package client.android.dao.service;
import java.util.List;
public class Response<T> {
// ----------------- 属性
// 操作状态
private int status;
// 可能的状态消息
private List<String> messages;
// 响应正文
private T body;
// 构造函数
public Response() {
}
public Response(int status, List<String> messages, T body) {
this.status = status;
this.messages = messages;
this.body = body;
}
// 获取器和设置器
...
}
- URL [/employes] 返回类型为 Response<List<Employe>>;
- URL 和 [/salaire] 返回类型为 Response<FeuilleSalaire>;
[FeuilleSalaire] 类如下:
package pam.entities;
import java.io.Serializable;
public class FeuilleSalaire implements Serializable {
private static final long serialVersionUID = 1L;
// 私有字段
private Employe employe;
private Cotisation cotisation;
private ElementsSalaire elementsSalaire;
// 构造函数
public FeuilleSalaire() {
}
public FeuilleSalaire(Employe employe, Cotisation cotisation, ElementsSalaire elementsSalaire) {
...
}
// getter 和 setter
...
}
类 [ElementsSalaire](第 11 行)如下:
package pam.entities;
import java.io.Serializable;
public class ElementsSalaire implements Serializable {
private static final long serialVersionUID = 1L;
// 私有字段
private double salaireBase;
private double cotisationsSociales;
private double indemnitesEntretien;
private double indemnitesRepas;
private double salaireNet;
// 构造函数
public ElementsSalaire() {
}
public ElementsSalaire(double salaireBase, double cotisationsSociales, double indemnitesEntretien, double indemnitesRepas, double salaireNet) {
...
}
// 获取器和设置器
...
}
4.4. Android 客户端测试
![]() |
以下是已完成的Android客户端可执行二进制文件:
![]() |
请使用鼠标将上方的二进制文件 [pam-client.apk] 拖放到平板模拟器 [GenyMotion] 上。该文件将被保存并执行。如果尚未启动,请同时启动 Web 服务器 / jSON。 Android客户端的目的是获取Web服务器 / jSON 返回的信息并进行格式化。Android客户端的各个视图如下:
首先需连接至 Web 服务 / jSON:

- 在 [1] 中,需提供 Web 服务 / jSON 的 URL。 使用模拟器时,请输入 PC 的其中一个地址 IP(但不要输入 127.0.0.1)。 使用平板电脑时,请输入Web服务器主机的Wi-Fi地址 / jSON,并禁用服务器端的防火墙(如有),以免其阻挡传入请求;
- 在 [2] 上登录;
随后将进入模拟页面:

- 在 [3] 中,选择一名员工;
- 在 [4] 页面,输入工时数;
- 在 [5] 页面,输入天数;
- 在 [6] 中,执行模拟;
生成的模拟页面如下:

- 在 [7] 中,显示模拟结果;
- 在 [8] 中,将其保存;

- 在 [9] 中,显示模拟列表;
- 在 [10] 中,删除一个模拟;

- 转换为 [11],此时已无模拟;
- 在 [12] 中,返回模拟表单;

- 在 [13],重新显示表单;
- 在 [14],返回配置页面;

- 在 [15],返回初始登录表单。
4.5. 待完成的工作
前面介绍的 Android 客户端框架已提供给您。它是基于第 2 段中描述的 [client-android-skel] 项目构建的。
![]() |
该项目可运行,且已包含必要的视图。只需添加一些代码,应用程序即可完成其应有的功能。操作步骤如下:
- 运行完整版本以了解需要完成的工作;
- 运行精简版并研究其代码。该代码遵循了前几页中采用的设计方法;
- 添加缺失的代码;










