Bagaimana kami meningkatkan urutan makan siang di kantor (tanpa akses ke server)

Halo semuanya.

Saya bekerja di kantor. Pengembang perangkat lunak. Dan terkadang saya makan. Ya setiap hari. Majikan memberi kita makan siang - pekerja memesan makan siang untuk besok, dan besok pemasok makan siang membawakan apa yang dipesan karyawan. Apa yang diperintahkan dan apa yang dibawa tidak selalu bersamaan, tetapi ini tidak terjadi. Makan siang dipesan di halaman pesanan makan siang. Tapi ...

Tapi pertama-tama, tentang bagaimana halaman pesanan makan siang terbentuk: pemasok mengirim file XLS dengan daftar harga selama seminggu.

gambar
Contoh daftar harga yang dikirim oleh pemasok

Orang yang bertanggung jawab untuk makan malam mem-parsing melalui utilitas yang dikembangkan oleh seseorang di perut perusahaan kami, menerjemahkannya ke dalam bentuk yang dapat ditampilkan oleh portal perusahaan kami. Dan dia menampilkannya ...

gambar
Tangkapan layar dengan makan malam yang dipesan

gambar
Cuplikan layar halaman pemesanan makan siang

Posisi dibagi menjadi beberapa kategori. Informasi tentang nama dan komposisi dalam teks lengkap dan sulit dinavigasi.

Saya ingin mengerti bahwa lebih baik tidak memesan, dan apa yang dapat Anda coba, karena orang lain menyukainya. Artinya, saya ingin peringkat. Saya juga ingin menerima pesanan saya di Telegram sehingga saya tidak ingat apa yang saya pesan di ruang makan.

Jadi tujuannya jelas. Saya harus mengatakan segera: jalan yang telah diambil oleh rekan saya dan saya jauh dari yang paling benar dan rasional. Meski begitu: ini adalah permainan yang lengkap dalam hal arsitektur / keamanan / dukungan / toleransi kesalahan. Tetapi apa yang telah tumbuh telah tumbuh.

Kami tidak memiliki akses ke server, jadi Anda hanya dapat mengubah tampilan halaman dengan skrip pengguna. Tapi bagaimana dengan peringkat? Tidak ada akses ke database juga. Kita membutuhkan server untuk pemrosesan pesanan, penilaian, dan interaksi dengan Telegram. Peran ini diambil oleh server NodeJS.

Sisi server


Saya akan mengurus server, dan seorang rekan akan mengurus skrip pengguna yang menambahkan fungsionalitas ke halaman. Kami mengambil server nodejs, menghubungkan express, tambahkan MySQL. Letakkan Sequelize di atas . Dan kami akan berinteraksi dengan Telegram melalui node-telegram-bot-api :

//    const app = express(); // ... //   //    app.get("/dinners/user_menu", dinner.getUserMenu); //      app.get("/dinners/r/:id", dinner.getPersonalRatings); //   app.post("/dinners/r/:id", dinner.setRating); //      Telegram app.post("/dinners/resend/:id", dinner.resendMessage); //      app.post("/dinners/order", dinner.order); //  ,      app.post("/dinners/days", dinner.setDinnerDays); 

Secara singkat tentang fungsionalitas:
Path / dinners / user_menu mengembalikan skrip pengguna:

 res.sendFile(__dirname + '/public_html/user_script.js'); 

Ini dilakukan agar tidak mengganggu kolega yang menggunakannya dengan menginstal versi skrip baru. Dikoreksi - melemparkannya ke server - semua orang diperbarui.

Ya, saya tahu bahwa dari sudut pandang keamanan ini buruk, tetapi fungsi itu sendiri tidak kritis dan kami akan mempertimbangkan server tempat skrip disimpan cukup aman.

Selanjutnya, di sepanjang jalur / makan malam / r /: id, Anda bisa mendapatkan peringkat untuk semua posisi dan menyimpan peringkat, yaitu, memilih hidangan.

Path / dinners / kirim ulang /: id berfungsi untuk mengirim pesan ke Telegram. Teks pesan dihasilkan pada klien, hanya interaksi dengan Telegram yang terjadi di server:

 const parseMode: TelegramBot.SendMessageOptions = {parse_mode: "HTML"}; await this.bot.sendMessage(telegramId, htmlMessage, {...options, ...parseMode}); 

Setelah itu, Bot mengirim pesan dengan urutan.

gambar

Selanjutnya, di sepanjang jalur / makan malam / pesanan , pesanan disimpan. Karena permintaan pesanan asli sulit untuk ditentukan (setelah mengklik tombol "Simpan", peringatan muncul dengan tombol konfirmasi pesanan), permintaan ke server dengan pesanan dikirim ketika halaman pesanan dimuat (dan seluruh sistem pesanan di situs dibagi menjadi 2 halaman - halaman pesanan dan halaman menu - Pilihan hidangan untuk hari tertentu - yaitu, pembentukan pesanan). Sangat tidak rasional untuk mengirim permintaan setiap kali Anda memasuki halaman pesanan, tetapi tidak ada opsi yang lebih baik untuk snap.

Akhirnya, jalur / makan malam / hari menentukan hari untuk memesan makan siang. Bagian dari fungsi ini telah muncul untuk operasi yang benar dari pengingat tentang pesanan yang belum selesai - Anda perlu tahu apa hari berikutnya dari pesanan itu (ada akhir pekan dan hari libur di tengah minggu). Alih-alih mengambil penerapan kalender produksi, saya hanya menguraikan tanggal pada halaman pesanan, di mana hari kerja dan non-kerja sudah ditandai (Anda tidak dapat melakukan pemesanan untuk hari yang tidak bekerja). Hari yang tidak bekerja ditandai di portal dengan kelas isHoliday:

 //     const trToday = $(".dinner_today")[0]; const tbodyAllDays = $(trToday).parent(); const dinnerDays = []; $(tbodyAllDays).children().each(async function() { if ($(this).hasClass("isHoliday")) { return; } const itemMenuDate = $(this).find("> td:first-child").text().substring(0, 10); dinnerDays.push(itemMenuDate); // ... }); await sendRequest("POST", `https://****/dinners/days/`, {days: dinnerDays}); 

Oh ya, gunakan jquery untuk memetik. Sangat mudah untuk mempelajari pohon halaman.

Bot telegram


Bagian lain dari keseluruhan add-on adalah bot telegram.

gambar
Dengan fungsi seperti itu

Memperoleh ID adalah sistem identifikasi. Untuk mengaitkan skrip pengguna pada browser tertentu dengan userId di telegram.

Lihat pesanan hari ini, lihat daftar pesanan (5 terakhir), tetapkan pengingat.
Makan siang secara otomatis dikirim ke pemasok pada waktu yang sama setiap hari, jadi penting untuk melakukan pemesanan sebelum waktu tertentu, misalnya, 13:00.

Setelah itu, kemampuan untuk melakukan pemesanan diblokir.

Pengingat:

gambar
Bot memberikan kesempatan untuk memilih waktu pengingat: 9, 10 atau 11 jam.

Selain itu, jika setelah pengingat Anda tidak melakukan pemesanan, maka setiap 10 menit berikutnya bot akan mengingatkan Anda tentang pesanan sampai Anda memesannya, atau sampai pesanan diblokir.

Ini dilakukan oleh tugas cron (menggunakan node-schedule ):

 schedule.scheduleJob('*/10 9-13 * * 1-5', async function() { // ... }); 

Bagian klien. Menu


Saya ulangi bahwa antarmuka saat ini dalam hubungannya dengan teks dari item menu yang dikirim pemasok hanya mengerikan (lihat layar 2). Dan pada satu titik, Anda berhenti melihat apa pun dalam banyak teks monoton padat dan sedikit berguna.

Setelah mencari melalui Internet yang dapat membantu kami, kami menemukan plugin yang cukup bagus untuk skrip Greasemonkey khusus, dan mereka memutuskan untuk menggunakannya.

Pertama-tama, kami membuat skrip pengguna dan memberikan hak untuk berkomunikasi dengan portal dan server perusahaan, di mana peringkat dan kemampuan untuk mengirim permintaan ditingkatkan.

 // @include http://****.int/* // @include http://****/* // @grant GM.xmlHttpRequest 

Juga, untuk memodifikasi halaman makan siang itu sendiri, kami menggunakan jQuery, menghubungkannya menggunakan // @require

Sekarang mari kita mulai menyekop halaman makan siang. Setelah melihat kode html halaman, kami menemukan pengidentifikasi tabel makan siang, kami mendapatkan tabel dan memodifikasinya.

 const table = $(".dinner__innerData"); const categoryList = []; //      $(table).find(“tbody tr td:nth-child(2})”).each(function () { const text = $(this).text(); //           if (!categoryList.find(name => name === text)) { $(this).parent().before("<tr><th colspan='6'>" + text + "</th><th style='display:none'></th><th style='display:none'></th><th style='display:none'>0</th><th style='display:none'><span class='dish__amount'>0</span></th></tr>"); categoryList.push(text); } }); //      $(table).find(“thead th:nth-child(2)”).remove(); $(table).find("tbody tr td:nth-child(2)”).remove(); //     $(table).find(“tbody tr td:nth-child(2)”).after("<td></td>"); $(table).find(“thead th:nth-child(2)”).after("<th class='ui-state-default'></th>"); 

Saya ingin mencatat bahwa pada halaman pembentukan makan siang, ketika menghitung jumlah pesanan, itu dipertimbangkan pada semua baris tabel, menerima jumlah item yang dipesan dikalikan dengan harga. Untuk alasan ini, jika Anda menambahkan baris dengan nama kategori, semuanya akan rusak ... Saya harus memasukkan kolom tersembunyi dengan jumlah dan jumlah nol untuk baris ini.

Sekarang mari kita lanjutkan untuk membersihkan teks dan menambahkan informasi pada peringkat hidangan. Pertama, beberapa fungsi pembantu. Piringan dalam peringkat diidentifikasi dengan nama tanpa sampah dalam bentuk gram, tanda baca dan spasi. Yaitu, hidangan yang disebut “Kaldu Ayam dengan Telur (kaldu ayam, wortel, bawang, telur, sayuran). Dalam 100g: protein-3,43; lemak-2.86; karbohidrat-1.0; en.value-43.39kcal (200gr) ”diidentifikasi sebagai“ bouillon curd ”. Ini disebabkan oleh fakta bahwa pemasok dapat merangkak di ruang ekstra, tanda, dan sesuatu yang lain. Seperti yang telah ditunjukkan oleh praktik, ini cukup untuk mengidentifikasi piringan secara akurat dalam 90% kasus, dan kami memutuskan untuk tidak repot dan memasukkan pencarian teks lengkap.

 /** *         * @param items   * @param tdText    * @return   */ function findByName(items, tdText) { tdText = clearTrash(tdText, true, true, true); return items.find(({clear_name}) => { return clear_name.trim().toLowerCase() === tdText; }); } /** *     * @param text  * @param clearDescr       * @param clearGrams    * @return    */ function clearTrash(text, clearDescr, clearGrams, clearSymbols) { //   ,       } 

Dan ini adalah pembentukan peringkat:

 const table = $(".dinner__innerData"); const nameTd = $(table).find(“tr td:nth-child(2)”); for (let index = 0; index <= nameTd.length; index++) { const tdText = $(nameTd[index]).text(); //     const item = findByName(items, tdText); if (item) { let ratingTd = $(nameTd[index]).parent().find(“td:nth-child(2)”)[0]; //           let ratingText = "<i></i> " + parseFloat(item.avgrating).toFixed(1) + " (: " + item.orders + ", : " + item.ratingsCount + ")"; ratingText = item.persrating ? `<b><i></i> ${parseFloat(item.persrating).toFixed(1)} (: ${item.perscount})</b><br>` + ratingText : ratingText; //   $(ratingTd).css({ // getColorRating       background: getColorRating(item.avgrating) }).html(ratingText); } //           //   ,      const grams = getGrams(tdText); //     $(nameTd[index]).html(clearTrash(tdText, false, true, false)); //      ,       $(nameTd[index]).append("<br/><span></span>") .find("span") .append(grams) .css({"font-size": 10}); } 

Dan itulah yang terjadi.

gambar
Setuju, jauh lebih baik dan lebih nyaman?

Bagian klien. Voting


Selanjutnya, kami akan beralih ke menambahkan kemampuan untuk memilih hidangan yang dipesan, serta mengirim pesan dengan pesanan ke telegram.

gambar
Halaman dengan pesanan tanpa skrip

Pada halaman hidangan yang dipesan, tambahkan peringkat:

 async function addRatingForm() { const table = $(".dinner__innerData"); const nameTd = $(table).find("tr td:nth-child(1)"); //   for (let index = 0; index <= nameTd.length; index++) { const tdText = $(nameTd[index]).text(); $(nameTd[index]).html(clearTrash(tdText, false, true, false)); } //       Telegram $(table).append("<tfoot><tr><th colspan='6' class='rating-buttons btn-group margT0' style='display: table-cell;'></tr></tfoot>"); $(".rating-buttons").prepend(`<input type="submit" value="" class="btn_primary rating-button">`); $(".rating-buttons").prepend(`<input type="submit" value=" Telegram" class="btn_primary send-button">`); //      await diableButtonByDate(); //      for (let index = 0; index <= table.length; index++) { $(table[index]).find("tbody tr td:nth-child(4)").after("<td class='ratingInputTd'><input id='horizontal-spinner' class='ui-spinner-input' style='width:20px;'></td>"); $(table[index]).find("thead th:nth-child(4)").after("<th class='ui-state-default'></th>"); } $(".ui-spinner-input").spinner({ max: 10, min: 1 }); //   $(".rating-button").click(sendRating); $(".send-button").click(sendTelegram); } /** *      ,    */ async function diableButtonByDate() { //             . //              const buttons = $(".rating-button"); for (let index = 0; index <= buttons.length; index++) { const button = $(buttons[index]); const date = button.parent().parent().parent().parent().parent().parent().find("> td:nth-child(1)").text().substring(0, 10); if (await GM.getValue(date)) { button.attr({disabled: "disabled"}); } } } /** *  */ async function sendRating(event) { event.preventDefault(); const items = []; //         $(this).parent().parent().parent().parent().find("tr").each(function () { const tdList = $(this).find("td"); const ratingInput = $(tdList[4]).find("input"); if (!ratingInput.length) { return; } items.push({ count: $(tdList[2]).text(), price: $(tdList[1]).text(), name: $(tdList[0]).text(), rating: ratingInput.val(), }); }); await sendRequest("POST", `https://****/dinners/r/${telegramId}`, items); const menuDate = $(this).parent().parent().parent().parent().parent().parent().find("> td:nth-child(1)").text().substring(0, 10); await GM.setValue(menuDate, true); location.reload(); } 

Dan inilah yang kami dapatkan di output:

gambar

Ya - kodenya mengerikan. Ya - tidak dioptimalkan. Dan ya - di beberapa tempat tidak masuk akal. Tetapi waktu yang dihabiskan minimum, dan fungsi dan kenyamanan meningkat secara signifikan.

Tujuannya adalah membuat memesan makan malam lebih menyenangkan bagi saya dan rekan-rekan saya, dan tujuan ini, menurut pendapat saya, tercapai.

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


All Articles