Skip to content

4. المهمة 1: إدارة كشوف الرواتب الأساسية

4.1. مقدمة

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

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

Image

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

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

4.2.1. التعريف

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

جدول EMPLOYEES: يحتوي على معلومات حول مختلف مقدمي خدمات رعاية الأطفال

الهيكل:

المعرف
المفتاح الأساسي
الإصدار
رقم الإصدار – يزداد مع كل تعديل للصف
SS
رقم الضمان الاجتماعي للموظف – فريد
الاسم
لقب الموظف
الاسم
الاسم الأول
العنوان
عنوانهم
المدينة
مدينته/مدينتها
الرمز البريدي
الرمز البريدي الخاص به/بها
رقم التعويض
مفتاح خارجي في حقل [ID] في جدول [INDEMNITES]

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

Image

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

الهيكل:

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

يمكن أن يكون محتواه كما يلي:

Image

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

جدول ALLOWANCES: يحتوي على العناصر المستخدمة لحساب الراتب المقرر دفعه.
رقم التعريف
المفتاح الأساسي
الإصدار
رقم الإصدار – يزداد مع كل تعديل للصف
الفهرس
فهرس المعالجة – فريد
السعر بالساعة
السعر الصافي باليورو لساعة واحدة من الخدمة تحت الطلب
الصيانة اليومية
البدل اليومي باليورو لكل يوم رعاية
وجبة في اليوم
بدل الوجبات باليورو لكل يوم رعاية
أجر الإجازة
بدل الإجازة المدفوعة. وهي نسبة مئوية تُطبق على الراتب الأساسي.

ويمكن أن يكون نصها كما يلي:

Image

يرجى ملاحظة أن البدلات قد تختلف من مقدم رعاية أطفال لآخر. وهي مرتبطة بمقدم رعاية أطفال معين من خلال رتبته الوظيفية. على سبيل المثال، السيدة ماري جوفينال، التي تبلغ رتبتها الوظيفية 2 (جدول الموظفين)، يتقاضى أجرًا بالساعة قدره 2.1 يورو (جدول التعويضات).

4.2.2. التوليد

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

  

قم بإنشاء قاعدة البيانات [dbpam_hibernate] (وهذا هو اسم قاعدة البيانات التي يستخدمها خادم الويب/jSON) وتأكد من أن حساب root (بدون كلمة مرور) يمكنه الوصول إليها. يمكنك القيام بذلك على النحو التالي:

قم بتشغيل MySQL، ثم [PhpMyAdmin]:

 
  • [1-2]: قم باستيراد البرنامج النصي [dbpam_hibernate.sql] ثم قم بتنفيذه؛

4.2.3. نمذجة قاعدة البيانات بلغة Java

يتم نمذجة عناصر الجداول [EMPLOYEES] و[ALLOWANCES] و[CONTRIBUTIONS] بواسطة الفئات التالية:

[ Employee]


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) {
    ...
  }
   // getters and setters
....
}
  • الأسطر 8–15: تتوافق هذه الحقول مع الأعمدة في جدول [EMPLOYEES
  • السطر 16: يتوافق حقل [indemniteId] مع عمود [INDEMNITE_ID]، وهو المفتاح الخارجي لجدول [EMPLOYEES
  • السطر 17: بدل الموظف. لا يتم ملء هذا الحقل دائمًا:
    • لا يتم ملؤه عند طلب عنوان URL [/employees
    • يتم ملؤه عند طلب عنوان URL [/salary

[ 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) {
        ...
    }
 
     // getters and setters
   ....
}
  • الأسطر 8-14: الحقول تتوافق مع أعمدة جدول [INDEMNITES

[ Contribution]


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) {
        ...
    }
    // getters and setters
   ...
}
  • الأسطر 8-13: الحقول تتوافق مع أعمدة جدول [COTISATIONS

4.3. خادم الويب / تثبيت JSON

4.3.1. التثبيت

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

 

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

  • قم بتشغيل نظام إدارة قواعد البيانات MySQL؛
  • تأكد من وجود قاعدة البيانات [dbpam_hibernate
  • افتح نافذة موجه الأوامر؛
  • انتقل إلى مجلد 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/**] onto handler of type [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 [/**] onto handler of type [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 [/salary/{SS}/{ht}/{jt}
  • السطر 17: تم اكتشاف عنوان URL [/employees

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) {

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

  • السطر 1: /employees: لاسترداد قائمة الموظفين؛
  • السطر 4: /salary/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> {
 
    // ----------------- properties
    // operation status
    private int status;
    // any status messages
    private List<String> messages;
    // the body of the reply
    private T body;
 
    // manufacturers
    public Response() {
 
    }
 
    public Response(int status, List<String> messages, T body) {
        this.status = status;
        this.messages = messages;
        this.body = body;
    }
 
    // getters and setters
...
}
  • يعيد عنوان URL [/employees] استجابة من نوع Response<List<Employee>>؛
  • يعرض عنوان URL [/salary] نوع Response<PayStub>؛

فئة [PayrollSheet] هي كما يلي:


package pam.entities;
 
import java.io.Serializable;
 
public class FeuilleSalaire implements Serializable {
 
    private static final long serialVersionUID = 1L;
    // private fields
    private Employe employe;
    private Cotisation cotisation;
    private ElementsSalaire elementsSalaire;
 
    // manufacturers
    public FeuilleSalaire() {
    }
 
    public FeuilleSalaire(Employe employe, Cotisation cotisation, ElementsSalaire elementsSalaire) {
        ...
    }
 
    // getters and setters
   ...
}
  • السطر 9: تم تقديم فئة [Employee] في القسم 4.2.3؛
  • السطر 10: تم تقديم فئة [Contribution] في القسم 4.2.3؛

فيما يلي فئة [SalaryElements] (السطر 11):


package pam.entities;
 
import java.io.Serializable;
 
public class ElementsSalaire implements Serializable {
 
    private static final long serialVersionUID = 1L;
    // private fields
    private double salaireBase;
    private double cotisationsSociales;
    private double indemnitesEntretien;
    private double indemnitesRepas;
    private double salaireNet;
 
    // manufacturers
    public ElementsSalaire() {
 
    }
 
    public ElementsSalaire(double salaireBase, double cotisationsSociales, double indemnitesEntretien, double indemnitesRepas, double salaireNet) {
        ...
    }
 
    // getters and setters
    ...
}

4.4. اختبارات عميل Android

فيما يلي الملف الثنائي القابل للتنفيذ لعميل Android النهائي:

  

استخدم الماوس لسحب ملف [pam-client.apk] الموجود أعلاه إلى محاكي الجهاز اللوحي [GenyMotion]. سيتم بعد ذلك حفظه وتشغيله. قم أيضًا بتشغيل خادم الويب/JSON إذا لم تكن قد قمت بذلك بعد. الغرض من عميل Android هو استرداد المعلومات التي يعيدها خادم الويب/JSON وتنسيقها. فيما يلي طرق العرض المختلفة لعميل Android:

أولاً، يجب عليك الاتصال بخدمة الويب / JSON:

Image

  • في [1]، أدخل عنوان URL لخدمة الويب/JSON. مع المحاكي، أدخل أحد عناوين IP للكمبيوتر الشخصي (ولكن ليس 127.0.0.1). مع جهاز لوحي، أدخل عنوان Wi-Fi لجهاز خادم الويب/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.

  

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

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