كيفية أتمتة جمع مؤشرات الأداء الرئيسية شهريًا وإرضاء المستخدمين تقريبًا

في العديد من المنظمات ، يتم تقييم الوحدة باستخدام KPI (مؤشرات الأداء الرئيسية). في المؤسسة التي أعمل فيها ، يسمى هذا النظام "نظام مؤشرات الأداء" ، وفي هذه المقالة أريد أن أتحدث عن كيفية تمكننا من أتمتة جزء من العمل باستخدام المؤشرات في غضون شهر. مع كل هذا ، لم تكن تكاليف العمالة لدينا هي الأكبر ، ولكن في نفس الوقت حاولنا تنفيذ بعض قائمة الرغبات القديمة. في قصتي لن تكون هناك تقنيات أو اكتشافات ضجيج (بعد كل شيء ، التنمية الإقليمية قاسية) ، ولكن سيكون هناك بعض الرسومات حول الموضوع التي ستساعد على فهم كيف بدأنا ، وما فعلناه ، وما الأفكار التي حصلنا عليها من التطوير. إذا لم تشعر بالملل بعد ، أطلب قطة.

الخلفية


في عام 2006 ، قدمنا ​​نظامًا لمؤشرات الأداء: تم تطوير خمسة عشر معيارًا للتقييم ، ومنهجية لحسابها ، وتم إنشاء تكرار ربع سنوي لحساب هذه المؤشرات. تم إجراء التقييم على اثنين وعشرين فرعًا من المنظمة تقع في جميع مناطق منطقتنا. ومن أجل تحقيق المؤشرات بحماس كبير ، تم ربط المكافأة بها - فكلما ارتفع مجموع المؤشرات ، ارتفع المكان في التصنيف ، وارتفع المكان في التصنيف ، وزاد قسط التأمين ، وهكذا كل ربع سنة وكل عام.

بمرور الوقت ، تغير تكوين المعايير ؛ كل ربع سنة ، تم إضافة معايير جديدة أو استبعاد القديمة. في الذروة ، في حوالي عام 2016 ، تجاوز عدد المؤشرات أربعين ، والآن لا يوجد سوى أربعة عشر.

ومع ذلك ، طوال هذا الوقت كانت عملية حسابهم هي نفسها. يتم حساب كل معيار من قبل الوحدة المسؤولة في المنظمة الأم وفقًا لخوارزمية معتمدة خصيصًا لهذا المعيار. يمكن إجراء الحساب باستخدام صيغة بسيطة وعدد من الأنظمة المعقدة ، فقد يتطلب جمع البيانات من عدة أنظمة ، في حين أنه من المحتمل جدًا أن تتغير الخوارزمية في كثير من الأحيان. ثم أصبح كل شيء أبسط بكثير: يتم ضرب مؤشر النسبة المئوية المحسوبة في معامله ، وبالتالي ، يتم الحصول على النتيجة حسب المعيار ، ثم يتم ترتيب النقاط ويتم كل وحدة وفقًا للنتيجة. يتم نفس الشيء مع مجموع النقاط لحساب المكان النهائي.

منذ البداية ، تم استخدام Excel لمراعاة وحساب كل ما هو موضح أعلاه. لسنوات عديدة ، تم تنفيذ المجموعة النهائية من المعايير والمزيد من الحساب للنقاط والأماكن في لوحة جميلة ، يظهر جزء منها في الشكل.



ألاحظ فقط أنه في هذا الجهاز اللوحي الجميل ، يتم إخفاء معظم المتحدثين ببساطة ، ولكن في الواقع في الربع الرابع من عام 2016 بدوا مثل هذا



موافق ، مثل هذا العدد من المؤشرات ومثل هذا الحجم من الجدول لا يزيد من بساطته. بالإضافة إلى ذلك ، تم حساب كل معيار على حدة ، وصنعت الفتيات من الإدارة العامة هذا الجدول الموجز بأيديهن. مع الأخذ في الاعتبار أنه لم يتم تغيير تكوين المعايير فحسب ، بل يمكن أيضًا إعادة الحسابات عدة مرات (لأسباب عديدة) ، فإن الأيام الأخيرة من كل ربع سنة ، بعبارة ملطفة ، لم تكن سعيدة. اضطررت إلى تغيير البيانات في الجدول باستمرار ، والتحقق منها والتحقق منها مرة أخرى: مثل هذا ، نسيت إضافة عمود جديد إلى مجموع النقاط أو لا تقم بتحديث المعامل ، وبالنسبة إلى بعض الأشخاص يقل المكان ، ومعه يكون المكافأة أقل. لذلك ، بعد التخفيض البطولي للجدول ، بدأ التحقق الأقل بطولية ، تم التحقق من جميع أولئك الذين فكروا في كل معيار ، والمديرين ، وحتى أخصائي تكنولوجيا المعلومات المعين من الناحية التنظيمية والموافقة عليهم. وبالنظر إلى أن كل العمل تم يدويًا ، وتم إرسال حساب مؤشرات الجدول المحوري إلى البريد ، غالبًا ما كانت هناك حالات عندما احتوى الجدول المحوري على أخطاء في الصيغ وبيانات الإصدارات غير ذات الصلة. أبلغ قبطان الأدلة أنه بعد اكتشاف كل خطأ ، تم إعادة تشغيل عملية التحقق.
عندما يتم حساب جميع المؤشرات ، تتم الموافقة على الجهاز اللوحي وإرساله إلى الفروع ، هناك ، في الفروع ، تم فحصه مرتين: ماذا لو اعتقد Excel الخبيث أن هناك خطأ ما؟ أعتقد أنه من الواضح أن تجميع تقرير سنوي عن المعايير أو تحليل البيانات التاريخية تبين أنه ليس أقل إثارة.

ومع ذلك ، من وجهة نظر إدارية ، فإن مثل هذا النظام فعال للغاية ويسمح ، إذا لزم الأمر ، بإحكام مجالات العمل المتدلية والمراقبة والحصول على فكرة عما يحدث في كل فرع من الفروع في مجال عمل معين. بالإضافة إلى ذلك ، كان موقع الفرع ، المحسوب وفقًا للمعايير ، مؤشرًا لا يتجزأ.

يجب تغيير النظام


أثبتت الممارسة ضرورة وأهمية نظام التقييم. في بداية عام 2017 ، أصبح من الواضح أن حساب المعايير مرة واحدة في الربع جعل من الممكن تقييم العمل المنجز ، لكن رصده لم يسمح بذلك كثيرًا. فترة زمنية طويلة جدًا. وفي هذا الصدد ، تقرر أن يتم حساب المؤشرات مرة كل أسبوعين ، وسيتم تلخيص النتائج بشكل ربع سنوي. سمحت الزيادة في تكرار الحساب لمديري الفروع بالاستجابة السريعة للتغييرات وزيادة التحكم في العمليات داخل وحداتهم.

ولكن فقط في المنظمة الأم ، كان القليل منهم راضين عن احتمال تنفيذ عملية جمع البيانات الموضحة أعلاه ، ليس كل ربع سنة ، ولكن كل أسبوعين. نظرًا لأنه ليس من الصعب التكهن ، فقد قرروا أتمتة العملية. تحولت المواعيد النهائية إلى ضيق: منذ لحظة التحول إلى تردد جديد للحساب ، إلى الانتقال الفعلي ، كان يجب أن يمر شهر واحد فقط. لهذه الفترة ، كان من المرغوب فيه للغاية أن نخرج بشيء أفضل من مجموعة من Excel للتجميع والحساب ، والبريد للتجميع والإخطار.

في البداية كان هناك القليل من النقاش الساخن حول ما الذي يجب فعله حقًا تلقائيًا وحساب المؤشرات نفسها ، مع مراعاة جميع صيغها ومصادر البيانات الخاصة بها. ولكن بالنظر إلى المواعيد النهائية الضيقة ، فإن تعقيد هذه الوظيفة والحاجة إلى صيانتها المستمرة في الحالة الحالية تمكنت من الوصول إلى المتطلبات التالية للنظام:

  • يجب الاحتفاظ بدليل مرجعي للمعايير ، مع الحفاظ على تاريخ تغييرها ؛
  • يجب أن يكون من الممكن إدخال وتخزين المؤشرات المحسوبة ، وكذلك تحويلها إلى نقاط كما في نفس اللوحة ؛
  • بناءً على قيم المؤشرات ، يجب إعداد تقرير يمكن لجميع الأطراف المعنية الوصول إليه ؛
  • بطبيعة الحال ، يجب تزويد كل هذا بواجهة ويب.

الوظيفة صغيرة جدًا ، ولكن ليس الكثير من الوقت.

بداية التطوير


لقد انجذبت دائمًا إلى وسائل التطوير السريع والتوليد التلقائي للواجهات. في الحالات التي يكون فيها من الضروري تنفيذ وظيفة CRUD ، تبدو الفكرة عندما يتم توفير واجهة وجزء من منطق الأعمال خارج الصندوق ، إذا جاز التعبير ، تبدو مغرية للغاية. بالطبع ، ستكون الواجهة متواضعة وغير منطقية ، ولكن هذا يكفي بالنسبة للعديد من المهام.

انطلاقا من هذه الأفكار ، قررت تجربة شيء من هذا القبيل. هناك Spring Roo وكوبا وأدوات أخرى مثيرة للاهتمام ، لكن الاختيار وقع على OpenXava. أولاً ، بمجرد أن قدمت بالفعل تطبيقًا بسيطًا جدًا عليه وكنت راضيًا ، وثانيًا ، يتناسب هذا الإطار في ذلك الوقت بنجاح مع مجموعتنا التكنولوجية. بالإضافة إلى ذلك ، من الممتع للغاية الحصول على برنامج تعليمي قصير باللغة الروسية.

يمكن العثور على وصف موجز لميزات وإمكانات OpenXava هنا . OpenXava هو إطار عمل ينفذ الإنشاء التلقائي لواجهة ويب مدمجة مع قاعدة بيانات تستند إلى JPA ، ويستخدم التعليقات التوضيحية لوصف قواعد التصور. يعتمد التطبيق على مكونات الأعمال - فئات Java التي تحتوي على المعلومات اللازمة لإنشاء التطبيقات. تتضمن هذه المعلومات بنية البيانات ، والمصادقين ، والتمثيلات الصحيحة ، ورسم الخرائط لجداول قاعدة البيانات. يتم تنفيذ العمليات على مكونات الأعمال من خلال وحدات التحكم التي يمكنها CRUD من صندوق ، والبحث ، والتصدير إلى PDF ، إلخ. يقدم تطبيق OpenXava مجموعة من الوحدات النمطية. تقوم الوحدة بربط مكون الأعمال بوحدة تحكم واحدة أو أكثر. يتم استخدام التمثيلات المحددة لكل مكون من عناصر الأعمال لعرض الواجهة. لا شيء غير عادي ، MVC مع القليل من الغلاف الجوي الخاص بها.

تخزين البيانات


في معظم التطبيقات ، نستخدم IBM DB2 DBMS. تم إنشاء قاعدة بيانات صغيرة تخزن الكتب المرجعية لمجموعات المعايير والمعايير التي يتم من خلالها تقييم الفروع ، ودليل الفروع ولوحة يتم فيها إدخال القيم المحسوبة للمعايير. لكل معيار ، في وقت معين ، يتم تعيين معامل سيتم استخدامه في التسجيل. يتلقى كل فرع لكل معيار تقييمًا أيضًا لتاريخ معين. يتم تخزين البيانات المتعلقة بقيم المعايير تاريخيًا ، أي أن البيانات ذات الصلة بأي تاريخ ستكون تلك التي تم إدخالها في أقرب تاريخ في الماضي. يبدو هذا النهج ، المستوحى من سجلات المعلومات من 1C: Enterprise ، مناسبًا بما يكفي بالنسبة لي: هناك تاريخ ولا تعد مشكلات التحرير / الحذف شديدة الارتفاع.

هيكل قاعدة البيانات
CREATE TABLE SUMMAR.CLS_DEPART ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, PARENT_ID BIGINT NOT NULL DEFAULT 0, IS_DELETED INT DEFAULT 0, NAME CLOB, CODE VARCHAR(255), PRIMARY KEY (ID) ); CREATE TABLE SUMMAR.CLS_CRITERIA ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, IS_DELETED INT DEFAULT 0, NAME CLOB, CODE VARCHAR(255), PRIMARY KEY (ID) ); CREATE TABLE SUMMAR.CLS_GROUP_CRITERIA ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, IS_DELETED INT DEFAULT 0, NAME CLOB, CODE VARCHAR(255), PRIMARY KEY (ID) ); CREATE TABLE SUMMAR.REG_STATE_CRITERIA ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, ID_CRITERIA BIGINT NOT NULL, ID_GROUP_CRITERIA BIGINT NOT NULL, TIME_BEGIN TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP, TIME_END TIMESTAMP NOT NULL DEFAULT '9999-12-31-23.59.59.000000000000', TIME_CREATE TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP, KOEFFICIENT DECIMAL(15, 2), PRIMARY KEY (ID), CONSTRAINT FK_CRITERIA FOREIGN KEY (ID_CRITERIA) REFERENCES SUMMAR.CLS_CRITERIA(ID) ON DELETE NO ACTION ON UPDATE RESTRICT, CONSTRAINT FK_GROUP_CRITERIA FOREIGN KEY (ID_GROUP_CRITERIA) REFERENCES SUMMAR.CLS_GROUP_CRITERIA(ID) ON DELETE NO ACTION ON UPDATE RESTRICT ); CREATE TABLE SUMMAR.REG_VALUE_CRITERIA ( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY, ID_CRITERIA BIGINT NOT NULL, ID_GROUP_CRITERIA BIGINT NOT NULL, ID_DEPART BIGINT NOT NULL, DATE_REG TIMESTAMP(12) NOT NULL DEFAULT CURRENT TIMESTAMP, TIME_END TIMESTAMP NOT NULL DEFAULT '9999-12-31-23.59.59.000000000000', TIME_BEGIN TIMESTAMP NOT NULL DEFAULT CURRENT TIMESTAMP, PERCENT DECIMAL(15, 5), VAL DECIMAL(15, 5), PRIMARY KEY (ID), CONSTRAINT FK_CRITERIA FOREIGN KEY (ID_CRITERIA) REFERENCES SUMMAR.CLS_CRITERIA(ID) ON DELETE NO ACTION ON UPDATE RESTRICT, CONSTRAINT FK_DEPART FOREIGN KEY (ID_DEPART) REFERENCES SUMMAR.CLS_DEPART(ID) ON DELETE NO ACTION ON UPDATE RESTRICT, CONSTRAINT FK_GROUP_CRITERIA FOREIGN KEY (ID_GROUP_CRITERIA) REFERENCES SUMMAR.CLS_GROUP_CRITERIA(ID) ON DELETE NO ACTION ON UPDATE RESTRICT ); 


من أجل الحصول على التقرير المطلوب حول أداء الفروع من قاعدة البيانات ، تم إنشاء الوظائف المخزنة ، والتي تم تعيين نتائجها بالفعل إلى فئة Java. تبدو الوظائف شيئًا مثل هذا.
أولاً نحصل على جميع المعايير الصالحة للتاريخ ، بالإضافة إلى معاملات المعايير ذات الصلة بذلك التاريخ
 CREATE OR REPLACE FUNCTION SUMMAR.SLICE_STATE_ALL_CRITERIA ( PMAX_TIME TIMESTAMP ) RETURNS TABLE ( ID_CRITERIA BIGINT, ID_GROUP_CRITERIA BIGINT, TIME_BEGIN TIMESTAMP, TIME_END TIMESTAMP, TIME_CREATE TIMESTAMP, KOEFFICIENT DECIMAL(15, 2) ) LANGUAGE SQL RETURN SELECT RSC.ID_CRITERIA, RSC.ID_GROUP_CRITERIA, RSC.TIME_BEGIN, RSC.TIME_END, RSC.TIME_CREATE, RSC.KOEFFICIENT FROM SUMMAR.REG_STATE_CRITERIA AS RSC INNER JOIN ( SELECT ID_CRITERIA, MAX(TIME_BEGIN) AS TIME_BEGIN FROM ( SELECT DISTINCT ID_CRITERIA, TIME_BEGIN FROM SUMMAR.REG_STATE_CRITERIA WHERE TIME_BEGIN < PMAX_TIME AND TIME_END > PMAX_TIME ) AS SL GROUP BY ID_CRITERIA ) AS MAX_SLICE ON RSC.ID_CRITERIA = MAX_SLICE.ID_CRITERIA AND RSC.TIME_BEGIN = MAX_SLICE.TIME_BEGIN ; 


الآن سوف نتلقى في نفس التاريخ قيم جميع المعايير لجميع الفروع
 CREATE OR REPLACE FUNCTION SUMMAR.SLICE_VALUE_ACTUAL_ALL_CRITERIA_ALL_DEPART_WITH_NAMES ( PMAX_TIME TIMESTAMP ) RETURNS TABLE ( ID_CRITERIA BIGINT, ID_GROUP_CRITERIA BIGINT, ID_DEPART BIGINT, DATE_REG TIMESTAMP, PERCENT DECIMAL(15, 2), VAL DECIMAL(15, 2), KOEFFICIENT DECIMAL(15, 2), CRITERIA_NAME CLOB, CRITERIA_CODE VARCHAR(255), GROUP_CRITERIA_NAME CLOB, GROUP_CRITERIA_CODE VARCHAR(255), DEPART_NAME CLOB, DEPART_CODE VARCHAR(255), DEPART_CODE_INT INT ) LANGUAGE SQL RETURN SELECT CDEP.ID_CRITERIA, COALESCE(VALS.ID_GROUP_CRITERIA, 0) AS ID_GROUP_CRITERIA, CDEP.ID_DEPART, VALS.DATE_REG, COALESCE(VALS.PERCENT, 0.0) AS PERCENT, COALESCE(VALS.VAL, 0.0) AS VAL, COALESCE(VALS.KOEFFICIENT, 0.0) AS KOEFFICIENT, CDEP.CRITERIA_NAME, CDEP.CRITERIA_CODE, COALESCE(VALS.GROUP_CRITERIA_NAME, '') AS GROUP_CRITERIA_NAME, COALESCE(VALS.GROUP_CRITERIA_CODE, '') AS GROUP_CRITERIA_CODE, CDEP.DEPART_NAME, CDEP.DEPART_CODE, CDEP.DEPART_CODE_INT FROM ( SELECT CCRT.ID AS ID_CRITERIA, CCRT."NAME" AS CRITERIA_NAME, CCRT.CODE AS CRITERIA_CODE, CDEP.ID AS ID_DEPART, CDEP."NAME" AS DEPART_NAME, CDEP.CODE AS DEPART_CODE, CAST (CDEP.CODE AS INT) AS DEPART_CODE_INT FROM SUMMAR.CLS_DEPART AS CDEP, ( SELECT * FROM SUMMAR.CLS_CRITERIA AS CC INNER JOIN TABLE(SUMMAR.SLICE_STATE_ALL_CRITERIA (PMAX_TIME)) AS ACTC ON CC.ID = ACTC.ID_CRITERIA WHERE CC.IS_DELETED = 0 ) AS CCRT WHERE CDEP.IS_DELETED = 0 ) AS CDEP LEFT JOIN ( SELECT VALS.ID_CRITERIA, VALS.ID_GROUP_CRITERIA, VALS.ID_DEPART, VALS.DATE_REG, VALS.PERCENT, VALS.VAL, VALS.KOEFFICIENT, CGRT."NAME" AS GROUP_CRITERIA_NAME, CGRT.CODE AS GROUP_CRITERIA_CODE FROM TABLE(SUMMAR.SLICE_VALUE_ACTUAL_ALL_CRITERIA (PMAX_TIME)) AS VALS INNER JOIN SUMMAR.CLS_GROUP_CRITERIA AS CGRT ON VALS.ID_GROUP_CRITERIA = CGRT.ID ) as VALS ON CDEP.ID_DEPART = VALS.ID_DEPART AND CDEP.ID_CRITERIA = VALS.ID_CRITERIA ; 


في الاستعلام النهائي ، نقوم بترقيم قيم المؤشرات ، وتصنيفها وإيجاد الحد الأدنى والحد الأقصى ، وهذا مطلوب لحساب الأماكن في وقت لاحق
 SELECT ROW_NUMBER() OVER() AS ID_NUM, RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) AS RATING, CASE WHEN MAX(RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) ) OVER() = RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) THEN 1 ELSE 0 END AS MAX_RATING, CASE WHEN MIN(RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) ) OVER() = RANK() OVER( PARTITION BY ID_CRITERIA ORDER BY VAL DESC ) THEN 1 ELSE 0 END AS MIN_RATING, VALS.* FROM TABLE(SUMMAR.SLICE_VALUE_ACTUAL_ALL_CRITERIA_ALL_DEPART_WITH_NAMES (?)) AS VALS ORDER BY GROUP_CRITERIA_CODE, CRITERIA_CODE, DEPART_CODE_INT 


إن نقل جزء من منطق الأعمال إلى نظام إدارة قواعد البيانات مبرر تمامًا ، خاصة عندما يتعلق الأمر بإعداد البيانات لتقارير مختلفة. اتضح أنه يمكن كتابة العمليات في شكل أكثر إيجازًا وطبيعية ؛ في جافا ، تتطلب مثل هذه التلاعبات بالبيانات المزيد من التعليمات البرمجية وبعض الجهود المبذولة لبناءها. على الرغم من أن العمليات المعقدة نسبيًا أو غير التقليدية لا تزال أسهل في البرمجة في Java. لذلك ، في تطبيقنا ، فيما يتعلق بأخذ عينات البيانات ، يتم استخدام نهج حيث يتم تنفيذ اتصال مجموعات البيانات وشروط القطع وبعض العمليات التي يمكن إجراؤها بواسطة وظائف النافذة في الوظائف والإجراءات المخزنة ، ويتم تنفيذ منطق أكثر تعقيدًا في التطبيق.

التطبيق


كما قلت ، تم استخدام OpenXava لتنفيذ التطبيق. للحصول على واجهة نموذجية و CRUD خارج الصندوق ، تحتاج إلى تنفيذ بعض الإجراءات.
بادئ ذي بدء ، في web.xml ، تحتاج إلى وصف الفلتر و servlet من الملحق الذي يتنقل في التطبيق:

web.xml
 <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <filter> <filter-name>naviox</filter-name> <filter-class>com.openxava.naviox.web.NaviOXFilter</filter-class> </filter> <filter-mapping> <filter-name>naviox</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping> <filter-mapping> <filter-name>naviox</filter-name> <url-pattern>/modules/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <filter-mapping> <filter-name>naviox</filter-name> <servlet-name>naviox</servlet-name> </filter-mapping> <filter-mapping> <filter-name>naviox</filter-name> <servlet-name>module</servlet-name> </filter-mapping> <servlet> <servlet-name>naviox</servlet-name> <servlet-class>com.openxava.naviox.web.NaviOXServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>naviox</servlet-name> <url-pattern>/m/*</url-pattern> </servlet-mapping> </web-app> 


بعد ذلك ، في ملف controlers.xml ، نحدد وحدات التحكم المستخدمة في التطبيق. في حالتنا ، أبسط ما يكفي:

controlers.xml
 <controllers> <controller name="Typical_View"> <extends controller="Navigation"/> <extends controller="CRUD"/> <extends controller="ExtendedPrint"/> </controller> </controllers> 


تجمع وحدة التحكم المذكورة أعلاه وظائف وحدات التحكم المضمنة في OpenXava بشكل افتراضي ، والتي يسهل تخمين وظائفها من الأسماء.

وأخيرًا ، في ملف application.xml ، سنقوم بتوصيل وحدة التحكم التي تم إنشاؤها والنموذج. شيء من هذا القبيل:

application.xml
 <application name="summar"> <module name="RegValueCriteria"> <model name="RegValueCriteria"/> <controller name="Typical_View"/> </module> </application> 


كما هو مذكور أعلاه ، يعتمد التطبيق على مكونات الأعمال التي يتكون منها نموذج التطبيق. على سبيل المثال ، ضع في اعتبارك مكون RegValueCriteria المرتبط بوحدة التحكم في application.xml. يصف هذا المكون قيمة معيار الفرع (للإيجاز ، لم يتبق سوى وصف حقول الصف ، وسأحذف طرقًا مثل getters والمستوطنين):

فئة المكون
 @Entity @Table(name = "REG_VALUE_CRITERIA", catalog = "", schema = "SUMMAR") @XmlRootElement @Views({ @View(members = "idCriteria [idCriteria];" + "idGroupCriteria [idGroupCriteria];" + "idDepart [idDepart];" + "data [dateReg, percent, val]"), @View(name="massReg", members = "idDepart.name, percent, val") }) @Tab(properties= "idDepart.name, idCriteria.name, idGroupCriteria.name, dateReg, percent, val" ) public class RegValueCriteria implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "ID") private Long id; @Basic(optional = false) @NotNull @Column(name = "DATE_REG") @Temporal(TemporalType.TIMESTAMP) @DefaultValueCalculator(CurrentDateCalculator.class) @Stereotype("DATETIME") private Date dateReg; @Column(name = "PERCENT") @OnChange(OnChangePercentAction.class) private BigDecimal percent; @Column(name = "VAL") private BigDecimal val; @JoinColumn(name = "ID_CRITERIA", referencedColumnName = "ID") @ManyToOne(optional = false) @DescriptionsList( descriptionProperties="name" ) @OnChange(OnChangeClsCriteriaAction.class) private ClsCriteria idCriteria; @JoinColumn(name = "ID_GROUP_CRITERIA", referencedColumnName = "ID") @ManyToOne(optional = false) @DescriptionsList( descriptionProperties="name" ) private ClsGroupCriteria idGroupCriteria; @JoinColumn(name = "ID_DEPART", referencedColumnName = "ID") @ManyToOne(optional = false) @DescriptionsList( descriptionProperties="name" ) private ClsDepart idDepart; } 


بالإضافة إلى تعليقات JPA المعتادة. يمكنك أيضًا ملاحظة تعليقات OpenXava. ينبغي النظر فيها بمزيد من التفصيل.

يسمح @View بالتحكم في تنسيق العرض التقديمي لحقول الصف ، وبمساعدة بناء جملة خاص في شكل أقواس مربعة ، يمكن تجميع الحقول وترتيبها أفقيًا وعموديًا باستخدام الرموز , و ; . إذا كانت هناك حاجة إلى تعيين تعيينات متعددة لمكون واحد ، @View تجميع @View التعليقات التوضيحية باستخدام التعليقات التوضيحية @View . في مثالنا ، تم ترتيب الخصائص على النحو التالي:

 @View(members = "idCriteria [idCriteria];" + "idGroupCriteria [idGroupCriteria];" + "idDepart [idDepart];" + "data [dateReg, percent, val]") 

ويبدو مثل هذا:



متواضع ، ولكن بأقل جهد. ومع ذلك ، هناك طرق "لإحياء" النموذج قليلاً.
من أجل ملء تاريخ التسجيل عند إنشاء المكون ، يتم @DefaultValueCalculator التعليق التوضيحي @DefaultValueCalculator ، والذي يستدعي كائن آلة حاسبة خاص. هنا ، يتم استخدام آلة حاسبة من OpenXava نفسها ، ولكن يمكنك أيضًا إنشاء آلة مخصصة. يتم استخدام التعليق التوضيحي @Stereotype لعرض التاريخ باستخدام عنصر التحكم المناسب.

لتكوين قوائم منسدلة تحتوي على كائنات ذات صلة ، يتم @DescriptionsList التعليق التوضيحي @DescriptionsList ، حيث يمكنك تحديد الخاصية التي سيتم عرضها في القائمة.

باستخدام التعليقات التوضيحية ، يمكنك تنفيذ بعض منطق الأعمال للنموذج نفسه. على سبيل المثال ، حتى عندما تتغير النسبة المئوية ، يتم حساب القيمة تلقائيًا مع مراعاة معامل المعيار ، يمكنك تطبيق التعليق التوضيحي @OnChange BigDecimal val . لكي @OnChange التعليق التوضيحي @OnChange ، يجب أن يشير إلى الفصل الذي يقوم بتطبيق واجهة OnChangePropertyBaseAction . يجب execute() طريقة execute() واحدة execute() في الفصل ، حيث يتم أخذ بيانات الإدخال من العرض ، ويتم إجراء الحساب وإعادة كتابة القيمة المحسوبة إلى العرض:

فئة الأطفال OnChangePropertyBaseAction
 public class OnChangePercentAction extends OnChangePropertyBaseAction{ @Override public void execute() throws Exception { BigDecimal percent = (BigDecimal)getNewValue(); if (percent != null){ Map value = (Map)getView().getValue("idCriteria"); if (value != null){ Long idCriteria = (Long)value.get("id"); Query query = XPersistence.getManager().createNativeQuery( "SELECT KOEFFICIENT FROM SUMMAR.SLCLA_STATE_CRITERIA WHERE ID_CRITERIA = ?"); query.setParameter(1, idCriteria); List<?> list = query.getResultList(); if (list != null && !list.isEmpty()){ BigDecimal koef = (BigDecimal) list.get(0); BigDecimal vl = koef.multiply(percent); getView().setValue("val", vl); } } } } } 


للحصول على عرض جدولي للبيانات ، يتم @Tab التعليق التوضيحي @Tab ، والذي يسمح لك @Tab تلك الخصائص للكائنات التي سيتم عرضها في تمثيل جدولي. في مثالنا ، تم ترتيب التعليق التوضيحي على النحو التالي:

 @Tab(properties= "idDepart.name, idCriteria.name, idGroupCriteria.name, dateReg, percent, val" ) 

سيبدو على النحو التالي



يسرنا مع توفر الفلاتر ، وترقيم الصفحات والتصدير خارج الصندوق ، ومع ذلك ، تتطلب العديد من التفاصيل التحسين مع ملف.

يتم بناء العمل مع المكونات الأخرى بالمثل. أدى استخدام OpenXava إلى تقليل تكاليف العمالة بشكل كبير لتنفيذ وظائف CRUD ومعظم واجهة المستخدم. يؤدي استخدام الإجراءات من وحدات التحكم والتعليقات التوضيحية المحددة مسبقًا لبناء النماذج إلى توفير الوقت إلى حد كبير إذا لم تتعارض مع التفاصيل وحاولت تنفيذ شيء أكثر تعقيدًا من نموذج الإدخال مع العديد من الأحداث. على الرغم من أنه قد يكون هذا هو الحال في التجربة.

ما كان كل شيء


هل تتذكر ما كان التطبيق عليه؟ نعم ، بحيث لا يتم تقليل اللوحة ذات المؤشرات بشكل بطولي في Excel ، ولكن يتم إنشاؤها تلقائيًا استنادًا إلى البيانات المدخلة. في نافذة المتصفح ، بدأ الجدول المحوري يبدو كما يلي:




لن أعطي تفاصيل عن التنفيذ ، لأن كل شيء ليس جيدًا جدًا به ، وأن مزيج JSP مع HTML الذي تم إنشاؤه عند طلب البيانات ليس شيئًا لمشاركته مع عامة الناس. في الوقت نفسه ، تم توضيح عينات البيانات نفسها أعلاه.

ومع ذلك ، أود أن أتطرق إلى تفاصيل واحدة مثيرة للاهتمام. عندما تم جمع متطلبات التطبيق ، أرادت الإدارة حقًا ، بالإضافة إلى تقرير الملخص ، أن يتم عرض قيم المؤشر الفردي في شكل خريطة للمنطقة ، مقسمة إلى مناطق مع الإشارة إلى موقع وفرز الفرع المقابل. من تعرف على المنطقة على الخريطة - أحسنت =)



من ناحية ، كان المطلب اختياريًا ، ولكن من ناحية أخرى ، وعدت الصورة بأن تكون مرئية ، ومن وجهة نظر التنفيذ ، كان من المثير للاهتمام تجربتها. بعد بعض التفكير ، ظهرت الفكرة للعثور على صورة المنطقة بتنسيق SVG وعمل قالب XSLT للخروج منها.

يتم ملء القالب الناتج بسهولة بالبيانات ، وبعد ذلك يتم تحويله إلى PNG.
أولاً ، باستخدام الاستعلام الموضح أعلاه ، يتم أخذ عينات من البيانات ، ويتم تحويل البيانات المستلمة إلى كائن من هذه الفئة:

فئات لإخراج البيانات على الخريطة
 @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "VisualisedValuesCriteria") public class VisualisedValuesCriteria { private XMLGregorianCalendar date; private String criteriaName; private String koefficient; private List<VisualizedValueCriteria> departValues; public XMLGregorianCalendar getDate() { return date; } public void setDate(XMLGregorianCalendar date) { this.date = date; } public String getCriteriaName() { return criteriaName; } public void setCriteriaName(String criteriaName) { this.criteriaName = criteriaName; } public String getKoefficient() { return koefficient; } public void setKoefficient(String koefficient) { this.koefficient = koefficient; } public List<VisualizedValueCriteria> getDepartValues() { if (departValues == null){ departValues = new ArrayList<>(); } return departValues; } public void setDepartValues(List<VisualizedValueCriteria> departValues) { this.departValues = departValues; } } @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class VisualizedValueCriteria { private String departName; private String departCode; private String value; private String percent; private String colorCode; public String getDepartName() { return departName; } public void setDepartName(String departName) { this.departName = departName; } public String getDepartCode() { return departCode; } public void setDepartCode(String departCode) { this.departCode = departCode; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getPercent() { return percent; } public void setPercent(String percent) { this.percent = percent; } public String getColorCode() { return colorCode; } public void setColorCode(String colorCode) { this.colorCode = colorCode; } } } 


بعد ذلك ، قم بتحويل الكائن إلى XML ؛

التحويل
 Private String marshal (VisualisedValuesCriteria obj){ final Marshaller marshaller = JAXBContext.newInstance(VisualisedValuesCriteria.class).createMarshaller(); marshaller.setEventHandler(new DefaultValidationEventHandler()); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); StringWriter writer = new StringWriter(); marshaller.marshal(obj, writer); return writer.toString(); } 


الآن ، لنأخذ XML الناتج ، قالب XSLT المعد ، ونطبق التحويل ونحصل على svg في الإخراج:

احصل على SVG
  String xml = marshal(obj); TransformerFactory factory = TransformerFactory.newInstance(); FileInputStream xsltFis = new FileInputStream("C:\\TEMP\\map_xsl.svg"); InputStreamReader xsltIn = new InputStreamReader(xsltFis, "UTF-8"); Source xslt = new StreamSource(xsltIn); Transformer transformer = factory.newTransformer(xslt); InputStream xmlIn = new ByteArrayInputStream( xml.getBytes( "UTF-8" ) ); Source text = new StreamSource(xmlIn); String filename = "map" + System.currentTimeMillis() + ".svg"; String filePath = "C:\\TEMP\\" + filename; transformer.transform(text, new StreamResult(new File(filePath))); 


من حيث المبدأ ، يمكننا التوقف هنا ، وتعرض المستعرضات SVG دون أي مشاكل. ولكن تم الحصول على التقارير الموصوفة أيضًا من خلال Telegram bot ، لذلك يجب تحويل SVG إلى بعض التنسيقات مثل JPEG أو PNG. للقيام بذلك ، استخدم Apache Batik

قم بالتحويل إلى PNG
 private String convertToPNG(final String svg, final String filename, final String filePath){ String png = filePath + filename + ".png"; try { PNGTranscoder trancoder = new PNGTranscoder(); String svgURI = new File(svg).toURL().toString(); TranscoderInput input = new TranscoderInput(svgURI); OutputStream ostream = new FileOutputStream(png); TranscoderOutput output = new TranscoderOutput(ostream); trancoder.transcode(input, output); ostream.flush(); ostream.close(); return filename + ".png"; } catch (MalformedURLException ex) { Logger.getLogger(ActualCriteriaValueGraphicServlet.class.getName()).log(Level.SEVERE, null, ex); } catch (FileNotFoundException | TranscoderException ex) { Logger.getLogger(ActualCriteriaValueGraphicServlet.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(ActualCriteriaValueGraphicServlet.class.getName()).log(Level.SEVERE, null, ex); } return null; } 


التقرير في شكل خريطة جاهز. يمكن أيضًا عرضه من خلال المتصفح وطلبه من روبوت برقية. في رأيي ، ليس سيئا.

الخلاصة


بحلول الوقت المحدد ، كان لدينا الوقت بالفعل وفي مارس 2017 ، بدأ إدخال مؤشرات الأداء بدلاً من Excel بانتظام في النظام الذي تم إنشاؤه. من ناحية ، لم يتم حل المشكلة القصوى ؛ يتم إجراء الجزء الأصعب من حساب المؤشرات يدويًا. ولكن من ناحية أخرى ، حمل تنفيذ هذه الحسابات مخاطر التحسين المستمر. بالإضافة إلى ذلك ، حتى الواجهة البسيطة التي تم إنشاؤها لجمع البيانات أزالت عددًا كبيرًا من الأسئلة مع التغييرات المستمرة والتحكم في الإصدار وملخصًا لجدول بيانات Excel. تمت إزالة عدد كبير من العمل اليدوي والشيكات والشيكات المتقاطعة.

من المستحيل ألا نقول أن الواجهة على Open Xav لم تكن مرضية جدًا للمستخدمين. في البداية ، كان هناك العديد من الأسئلة لميزاته. في مرحلة ما ، بدأ المستخدمون يشكون من أن إدخال البيانات استغرق وقتًا طويلاً ، وبشكل عام ، "نريد ، مثل برنامج Excel ، برنامج فقط". حتى أنني اضطررت إلى مراقبة سرعة الإدخال بناءً على البيانات في وقت إنشاء السجلات. أظهرت هذه المراقبة أنه حتى في الحالات الأكثر شدة ، لم يمضي المستخدمون أكثر من 15 دقيقة على الإدخال ، ولكنهم احتواؤهم عادةً في 5-7 ، على الرغم من حقيقة أنهم كانوا بحاجة إلى إدخال البيانات في 22 فرعاً. تبدو هذه المؤشرات مقبولة تمامًا.

ومع ذلك ، أود أن أشير إلى شيئين:

  1. أثبت Open Xava أنه أداة جيدة لإنشاء واجهة بسرعة. أود أن أقول حتى واجهة النموذج الأولي. كما أن ميزتها العامة هي النظام العام والانتظام. يتم إنشاء جميع النماذج في التطبيق وفقًا لمبادئ موحدة ، مما يسمح للمطور بعدم الخروج بالدراجات حيث لا يحتاجها ، ولكن المستخدم للتعامل مع مجموعات قياسية من النماذج. ومع ذلك ، أدت محاولات تنفيذ منطق أكثر تعقيدًا أو تغيير عناصر التحكم لأنفسنا إلى عدد من المشاكل التي لم نتمكن من التعامل معها في الوقت المخصص. على الأرجح ، لم نفهم فقط ، وكان الهدف هو إنشاء واجهة CRUD بسيطة بأقل جهد. بالنسبة لي ، أخلص إلى أن Open Xava هي أداة مثيرة للاهتمام حيث يسهل القيام بأشياء بسيطة ، ولكن إذا كنت بحاجة إلى القيام بشيء معقد ، فإنني أفضل قضاء المزيد من الوقت في إنشاء جزء العميل باستخدام ExtJS أو React ، ولكن لدي المزيد من المرونة .
  2. حتى المستخدمون الواثقون يتعاملون مع واجهات جديدة بجد هذا بالطبع ليس سرا. في رأيي ، هذا يرجع في المقام الأول إلى عدم فهم الطبيعة النظامية للعديد من الواجهات. بالنسبة للكثيرين ، يحتوي أي تطبيق على مجموعة من الشاشات ، كل منها فريد ويعمل وفقًا لمبادئه غير المعروفة الخاصة به: على سبيل المثال ، هناك نموذج يحتوي على قائمة من الكائنات / السطور (نموذج القائمة) ، ولكن بالنسبة للعديد من المستخدمين ، ليس من الواضح على الإطلاق أن كل شكل من أشكال التطبيق يمكن أن يكون ترشيح منتظم ، ترقيم الصفحات ، وظائف الفرز وعموما نفس السلوك ، تكملها وظائف محددة. ويضاف إلى ذلك حقيقة أن عددًا كبيرًا من برامج الشركات عبارة عن كومة عفوية تقريبًا من الأزرار والنماذج ، محنك بوثائق غير واضحة بأسلوب "انقر فوق هذا الزر". من وجهة النظر هذه ، فإن الواجهات التي تم إنشاؤها بواسطة نفس مطوري مستخدم نظام Open Xav ، تنشئ المزيد من النظام في الرأس. صحيح ، مع هذا النهج ، لا تختفي الأزرار السحرية في أي مكان ، ولكن يتم وضعها بدقة في الأشكال.

إذا تحدثنا عن الفوائد للمؤسسة ككل ، ثم بعد تقديم التطبيق ، كما هو متوقع ، زادت درجة التحكم. تلقى القادة الميدانيون أداة لرصد المؤشرات ، يمكنهم من خلالها التأثير على العمل في وضع أكثر تشغيلية ، لمقارنة المؤشرات لفترات مختلفة ، دون الخلط بينه وبين تدفق الملفات المرسلة. اكتشفنا اهتمام القادة بالرصد المستمر واليقظ لفرعهم بطريقة مثيرة للغاية: بالنظر إلى سجلات Telegram الروبوتية ، لاحظنا أنه تم تلقي بعض التقارير في الساعة 4 أو 5 صباحًا. يمكنك أن ترى على الفور من هو مهتم بالعمل.

هذا كل شيء. سأكون ممتنا لردود الفعل البناءة!

Source: https://habr.com/ru/post/ar423919/


All Articles