Skip to content

4. TP 1: الإدارة الأساسية لبيانات الراتب

4.1. Introduction

لتطبيق ما تمت دراسته سابقًا، نقترح الآن مشروعًا يتمثل في كتابة عميل Android للأجهزة اللوحية، يسمح بمحاكاة حسابات كشوف رواتب موظفي إحدى الجمعيات.

سيكون للتطبيق بنية عميل/خادم:

Image

  • الخادم [1] متوفر؛
  • يجب إنشاء عميل Android [2].

4.2. قاعدة البيانات

4.2.1. التعريف

سيتم وضع البيانات الثابتة المفيدة لإنشاء كشف الراتب في قاعدة بيانات سنسميها لاحقًا dbpam. تحتوي قاعدة البيانات هذه على الجداول التالية:

الجدول EMPLOYES: يجمع معلومات عن مختلف المربيات

الهيكل:

ID
المفتاح الأساسي
VERSION
رقم الإصدار – يزداد مع كل تعديل في السطر
SS
رقم الضمان الاجتماعي للموظف – فريد
NOM
اسم الموظف
PRENOM
اسمه الأول
ADRESSE
عنوانه
VILLE
مدينته
CODEPOSTAL
الرمز البريدي
INDEMNITE_ID
مفتاح أجنبي في الحقل [ID] من الجدول [INDEMNITES]

قد يكون محتواه كما يلي:

Image

الجدول COTISATIONS: يجمع النسب المئوية اللازمة لحساب الاشتراكات الاجتماعية

الهيكل:

ID
المفتاح الأساسي
VERSION
رقم الإصدار – يزداد مع كل تعديل في السطر
CSGRDS
النسبة المئوية: الاشتراك الاجتماعي العام + الاشتراك في سداد الدين الاجتماعي
CSGD
النسبة المئوية: الاشتراك الاجتماعي العام القابل للخصم
SECU
النسبة المئوية: الضمان الاجتماعي، الأرامل، الشيخوخة
RETRAITE
النسبة المئوية: المعاش التكميلي + التأمين ضد البطالة

قد يكون محتواه كما يلي:

Image

معدلات الاشتراكات الاجتماعية مستقلة عن الموظف. الجدول السابق يحتوي على سطر واحد فقط.

الجدول INDEMNITES: يجمع العناصر التي تسمح بحساب الراتب المستحق الدفع.
ID
المفتاح الأساسي
VERSION
رقم الإصدار – يزداد مع كل تعديل للسطر
INDICE
مؤشر المعالجة – فريد
BASEHEURE
السعر الصافي باليورو لساعة الحراسة
ENTRETIENJOUR
بدل الإعاشة باليورو لكل يوم رعاية
REPASJOUR
بدل الوجبات باليورو لكل يوم رعاية
INDEMNITESCP
بدل الإجازات المدفوعة الأجر. وهي نسبة مئوية تُطبق على الراتب الأساسي.

قد يكون محتواها كما يلي:

Image

تجدر الإشارة إلى أن البدلات قد تختلف من مربية أطفال إلى أخرى. فهي مرتبطة بمربية أطفال معينة من خلال مؤشر راتبها. وبالتالي، فإن السيدة ماري جوفينال التي لديها مؤشر راتب يبلغ 2 (الجدول EMPLOYES) تحصل على أجر ساعي قدره 2,1 يورو (الجدول INDEMNITES).

4.2.2. إنشاء

يتم توفير البرنامج النصي [dbpam_hibernate.sql] لإنشاء قاعدة البيانات:

  

قم بإنشاء قاعدة البيانات [dbpam_hibernate] (هذا هو اسم BD الذي يستخدمه خادم الويب / jSON) وتأكد من أن تسجيل الدخول 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] يتوافق مع العمود [INDEMNITE_ID] الذي يمثل المفتاح الخارجي للجدول [EMPLOYES
  • السطر 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. تثبيت خادم الويب / jSON

4.3.1. التثبيت

يتم توفير ملف Java الثنائي لخادم الويب / jSON لك:

 

لتشغيل خادم الويب / jSON، اتبع الخطوات التالية:

  • قم بتشغيل SGBD MySQL ؛
  • تأكد من وجود BD [dbpam_hibernate
  • افتح نافذة DOS؛
  • انتقل إلى مجلد jar؛
  • اكتب الأمر:
java -jar pam-server-01-all-1.0.jar

هذا يفترض أن الملف الثنائي [java.exe] موجود في PATH على جهازك. إذا لم يكن الأمر كذلك، اكتب المسار الكامل لـ [java.exe]، على سبيل المثال:

D:\Programs\devjava\java\jdk1.8\bin\java -jar pam-server-01-all-1.0.jar

تظهر السجلات:

.   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.1.1.RELEASE)

2014-10-22 16:45:23.347  INFO 1868 --- [           main] pam.boot.BootWeb                         : Starting BootWeb on Gportpers3 with PID 1868 (D:\Temp\14-10-22\pam\server-pam.jar started by ST in D:\Temp\14-10-22\pam)
2014-10-22 16:45:23.414  INFO 1868 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@689ab9e2: startup date [Wed Oct 22 16:45:23 CEST 2014]; root of context hierarchy
...
...
2014-10-22 16:45:31.147  INFO 1868 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQLDialect
2014-10-22 16:45:31.484  INFO 1868 --- [           main] o.h.h.i.ast.ASTQueryTranslatorFactory    : HHH000397: Using ASTQueryTranslatorFactory
2014-10-22 16:45:33.564  INFO 1868 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-10-22 16:45:33.804  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/salaire/{SS}/{ht}/{jt}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public pam.restapi.FeuilleSalaireResponse pam.restapi.PamController.getFeuilleSalaire(java.lang.String,double,int)
2014-10-22 16:45:33.805  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/employes],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public pam.restapi.EmployesResponse pam.restapi.PamController.getEmployes()
2014-10-22 16:45:33.807  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2014-10-22 16:45:33.807  INFO 1868 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2014-10-22 16:45:33.839  INFO 1868 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] إلى معالج من النوع [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-10-22 16:45:33.839  INFO 1868 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] إلى معالج من النوع [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2014-10-22 16:45:34.384  INFO 1868 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2014-10-22 16:45:34.535  INFO 1868 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080/http
2014-10-22 16:45:34.538  INFO 1868 --- [           main] pam.boot.BootWeb                         : Started BootWeb in 11.916 seconds (JVM running for 12.725)
2014-10-22 16:45:39.329  INFO 1868 --- [       Thread-2] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@689ab9e2: startup date [Wed Oct 22 16:45:23 CEST 2014]; root of context hierarchy
2014-10-22 16:45:39.331  INFO 1868 --- [       Thread-2] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
2014-10-22 16:45:39.333  INFO 1868 --- [       Thread-2] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  • السطر 16: تم اكتشاف URL [/salaire/{SS}/{ht}/{jt}]؛
  • السطر 17: تم اكتشاف URL [/employes

4.3.2. تم اكتشاف URL لخدمة الويب/jSON

يتم تنفيذ خدمة الويب / 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) {

تقبل خدمة الويب القيمتين التاليتين:

  • السطر 1: /employes: للحصول على قائمة الموظفين؛
  • السطر 4: /salaire/SS/ht/jt: للحصول على كشف راتب الموظف رقم [SS] الذي عمل [ht] ساعة خلال [jt] يومًا؛

فيما يلي لقطات شاشة توضح ذلك.

يُطلب من الموظفين:

Image

نقوم بقطع الاتصال بالقاعدة، وإعادة تشغيل الخادم، ثم نطلب الموظفين:

Image

نطلب راتبًا:

Image

نطلب راتب شخص غير موجود:

Image

4.3.3. الردود jSON من خدمة الويب/jSON

  

تقوم 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) {
        ...
    }

    // الوصول والتعيين
   ...
}
  • السطر 9: تم عرض الفئة [Employe] في الفقرة 4.2.3؛
  • السطر 10: تم عرض الفئة [Cotisation] في الفقرة 4.2.3؛

الفئة [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]. سيتم بعد ذلك تسجيله ثم تنفيذه. قم أيضًا بتشغيل خادم الويب / jSON إذا لم تكن قد قمت بذلك بالفعل. يهدف عميل Android إلى استرداد المعلومات التي يرسلها خادم الويب / jSON وتنسيقها. فيما يلي العروض المختلفة لعميل Android:

يجب أولاً الاتصال بخدمة الويب / jSON:

Image

  • في [1]، يتم تقديم URL لخدمة الويب / jSON. باستخدام المحاكي، أدخل أحد عناوين IP من PC (ولكن ليس 127.0.0.1). باستخدام جهاز لوحي، أدخل عنوان wifi لجهاز خادم الويب / jSON وقم بتعطيل جدار الحماية الخاص بجهاز الخادم إن كان موجودًا لأنه قد يحظر المكالمات الواردة؛
  • في [2]، نقوم بالاتصال؛

ثم نصل إلى صفحة المحاكاة:

Image

  • في [3]، نختار موظفًا؛
  • في [4]، نحدد عدد الساعات؛
  • في [5]، نحدد عدد الأيام؛
  • في [6]، نطلب إجراء المحاكاة؛

صفحة المحاكاة التي يتم الحصول عليها هي التالية:

Image

  • في [7]، المحاكاة التي تم الحصول عليها؛
  • في [8]، يتم تسجيلها؛

Image

  • في [9]، قائمة المحاكاة؛
  • في [10]، يتم حذف محاكاة؛

Image

  • إلى [11]، لم تعد هناك محاكاة؛
  • في [12]، نعود إلى نموذج المحاكاة؛

Image

  • في [13]، نجد النموذج؛
  • في [14]، نعود إلى صفحة التكوين؛

Image

  • في [15]، نعود إلى نموذج تسجيل الدخول الأولي.

4.5. المهمة المطلوبة

يتم تزويدك بهيكل العميل Android المقدم سابقًا. وقد تم إنشاؤه استنادًا إلى المشروع [client-android-skel] الموصوف في الفقرة 2.

  

المشروع قابل للتنفيذ ويحتوي بالفعل على العروض اللازمة. ما عليك سوى إضافة بعض الأكواد حتى تقوم التطبيق بما يجب عليها القيام به. الإجراء الذي يجب اتباعه هو التالي:

  • قم بتشغيل النسخة الكاملة لفهم العمل المطلوب؛
  • قم بتشغيل النسخة المبسطة وادرس الكود الخاص بها. فهو يتوافق مع أساليب التصميم المستخدمة في الصفحات السابقة؛
  • أضف الكود المفقود؛