Скрипт формирования последних платежей

Технические вопросы по UTM 5.0
Ответить
Аватара пользователя
marvin
Сообщения: 77
Зарегистрирован: Сб мар 24, 2007 11:18
Откуда: Нижняя Тура

Скрипт формирования последних платежей

Сообщение marvin »

Для решения вопроса исчерпания ресурса фиксированных адресов и просто поиска хронических неплательщиков был на коленке набросан следующий скрипт:

Код: Выделить всё

#!/usr/bin/perl

use Mysql;

$prefix = '/netup'; @force = grep {/--force/} @ARGV; @drop = grep {/--drop/} @ARGV;
open CONFIG, "$prefix/utm5/utm5.cfg"; @config = <CONFIG>; close CONFIG; foreach $line &#40;@config&#41; &#123; if &#40;$line =~ m/^&#40;&#91;^#&#93;.+?&#41;=&#40;.*&#41;$/&#41; &#123;$$1 = $2&#125; &#125;

$db = Mysql->connect&#40;$database_host, $database, $database_login, $database_password&#41;; $db->Query&#40;"SET NAMES koi8r"&#41;;

create_payments_updates_table&#40;&#41;;

$sth = $db->query&#40;"SELECT COUNT&#40;*&#41; AS num_rows FROM payments_updates"&#41;; %foo = $sth->fetchhash; if&#40;$foo&#123;num_rows&#125; == 0&#41;&#123;$clean = 1&#125; else &#123;$clean = 0&#125;

if&#40;$#force == -1&#41;&#123; my $sth = $db->query&#40;"SELECT MAX&#40;user_change_date&#41; AS last_update FROM payments_updates"&#41;;
                   my %bar = $sth->fetchhash; $last_users_change = $bar&#123;last_update&#125; + 0 &#125; else &#123;$last_users_change = 0&#125;

if&#40;$#force == -1&#41;&#123; my $sth = $db->query&#40;"SELECT MAX&#40;payment_date&#41; AS last_payment_date FROM payments_updates"&#41;;
                   my %baz = $sth->fetchhash; $last_payment_date = $baz&#123;last_payment_date&#125; + 0 &#125; else &#123;$last_payment_date = 0&#125;

fill_list&#40;$last_users_change&#41;; if&#40;$clean != 0&#41; &#123;traverse_history&#40;&#41;&#125;

update_payments&#40;"UTM5.payment_transactions", $last_payment_date&#41;;

#===========================================================================================

sub traverse_history &#123;
  my $sth = $db->query&#40;"SELECT table_name FROM archives WHERE table_type = 7 ORDER BY start_date ASC"&#41; or die $Mysql&#58;&#58;db_errstr;
  while &#40;%archives = $sth->fetchhash&#41;&#123; update_payments&#40;"$archives&#123;table_name&#125;", 0&#41;; &#125;
&#125;


sub update_payments &#123; my &#40;$ptr_table, $date&#41; = @_; print "update from $ptr_table\n"; 
  my $sth = $db->query&#40;"SELECT account_id, actual_date FROM $ptr_table WHERE actual_date > $date GROUP BY account_id HAVING MAX&#40;actual_date&#41;"&#41; or die $Mysql&#58;&#58;db_errstr;
  while &#40;%payments = $sth->fetchhash&#41;&#123; $sth1 = $db->query&#40;"SELECT payment_date FROM payments_updates WHERE id=$payments&#123;account_id&#125;"&#41;;
    %foo = $sth1->fetchhash; if&#40;$foo&#123;payment_date&#125; >= $payments&#123;actual_date&#125;&#41; &#123;next&#125;
    $db->query&#40;"UPDATE payments_updates SET payment_date = $payments&#123;actual_date&#125; WHERE id = $payments&#123;account_id&#125;"&#41; or die $Mysql&#58;&#58;db_errstr;&#125;
&#125;


sub fill_list &#123; my &#40;$last_update&#41; = @_; print "fill accounts list\n";
  my $sth = $db->query&#40;"SELECT accounts.id,
    users.login,
    users.full_name,
    GROUP_CONCAT&#40;INET_NTOA&#40;ip_groups.ip & 4294967295&#41; SEPARATOR ' '&#41; AS ip,
    MAX&#40;ip_groups.create_date&#41; AS ip_create_date,
    MAX&#40;ip_groups.delete_date&#41; AS ip_delete_date,
    accounts.balance,
    accounts.credit,
    accounts.unlimited,
    accounts.is_blocked,
    accounts.is_deleted,
    users.last_change_date,
    account_tariff_link.tariff_id,
    payments_updates.user_change_date,
    payments_updates.payment_date
    FROM accounts
    LEFT JOIN users_accounts ON users_accounts.account_id = accounts.id
    LEFT JOIN users ON users.id = users_accounts.uid
    LEFT JOIN service_links ON service_links.account_id = accounts.id
    LEFT JOIN iptraffic_service_links ON iptraffic_service_links.id = service_links.id
    LEFT JOIN ip_groups ON ip_groups.ip_group_id = iptraffic_service_links.ip_group_id
    LEFT JOIN account_tariff_link ON account_tariff_link.account_id = accounts.id
    LEFT JOIN payments_updates ON payments_updates.id = accounts.id
    WHERE &#40;ip_groups.ip IS NULL OR &#40;ip_groups.is_deleted = 0 AND &#40;ip_groups.ip_type & 0x1&#41; = 0x0 AND ip_groups.mask = -1&#41;&#41;
    GROUP BY accounts.id
    ORDER BY accounts.id"&#41; or die $Mysql&#58;&#58;db_errstr;

  while &#40;%accounts = $sth->fetchhash&#41;&#123;

    if&#40;$accounts&#123;payment_date&#125; eq ''&#41;&#123; $accounts&#123;payment_date&#125; = -1; &#125; if&#40;$accounts&#123;tariff_id&#125; eq ''&#41;&#123; $accounts&#123;tariff_id&#125; = -1; &#125;

    $last_change = max&#40;$accounts&#123;ip_create_date&#125;, $accounts&#123;ip_delete_date&#125;, $accounts&#123;last_change_date&#125;&#41;;

    if&#40;$accounts&#123;user_change_date&#125; < $last_change or $#force > -1&#41; &#123; print "$accounts&#123;login&#125;\n";

        $db->query&#40;"REPLACE INTO payments_updates
            SET id            = $accounts&#123;id&#125;,
            login             = '$accounts&#123;login&#125;',
            full_name         = '$accounts&#123;full_name&#125;',
            ip                = '$accounts&#123;ip&#125;',
            balance           = $accounts&#123;balance&#125;,
            credit            = $accounts&#123;credit&#125;,
            tariff_id         = $accounts&#123;tariff_id&#125;,
            unlimited         = $accounts&#123;unlimited&#125;,
            is_blocked        = $accounts&#123;is_blocked&#125;,
            is_deleted        = $accounts&#123;is_deleted&#125;,
            user_change_date  = $last_change,
            payment_date      = $accounts&#123;payment_date&#125;"&#41; or die $Mysql&#58;&#58;db_errstr;
    &#125;
  &#125;
&#125;


sub create_payments_updates_table &#123; if&#40;$#drop > -1&#41;&#123; $db->query&#40;"DROP TABLE payments_updates"&#41;; &#125;
  $db->query&#40;"CREATE TABLE IF NOT EXISTS `payments_updates` &#40;
    `id` INT&#40;11&#41; NOT NULL,
    `login` VARCHAR&#40;255&#41; NOT NULL,
    `full_name` VARCHAR&#40;255&#41;,
    `ip` VARCHAR&#40;255&#41;,
    `balance` DOUBLE,
    `credit` DOUBLE,
    `user_change_date` INT&#40;11&#41;,
    `payment_date` INT&#40;11&#41;,
    `tariff_id` INT&#40;11&#41;,
    `unlimited` INT&#40;11&#41;,
    `is_blocked` INT&#40;11&#41;,
    `is_deleted` INT&#40;11&#41;,
    PRIMARY KEY  &#40;`id`&#41;
  &#41; ENGINE=InnoDB DEFAULT CHARSET=utf8;"&#41; or die $Mysql&#58;&#58;db_errstr;
&#125;


sub max &#123;
  my $maximum = shift @$_;
  my $x;
  foreach $x &#40;@_&#41; &#123;
    $maximum=$x if &#40;$x > $maximum&#41; ;
  &#125;
  return $maximum;
&#125;
А также простенькая страничка для для отображения должников:

Код: Выделить всё

<?php

echo "<head>";
echo "<style><!--\n";
echo "body &#123;font-family&#58;verdana;font-size&#58;10pt;background-color&#58;ffffff;&#125;\n";
echo "td &#123;font-family&#58;verdana;font-size&#58;10px&#125;\n";
echo "a &#123;color&#58; blue;text-decoration&#58; none;&#125;\n";
echo "a&#58;hover &#123;color&#58; red;text-decoration&#58; underline;&#125;\n";
echo "th &#123;font-size&#58; 12px;&#125;\n";
echo ".copy &#123;font-size&#58; 10px;color&#58;cccccc;&#125;\n";
echo "--></style>\n";
echo "<meta http-equiv=\"Refresh\" CONTENT=\"300\">\n";
echo "<meta http-equiv=\"Pragma\" CONTENT=\"no-cache\">\n";
echo "<meta http-equiv=\"Cache-Control\" content=\"no-cache\">\n";
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1251\">";

$interval=3; $config = file&#40;'/netup/utm5/utm5.cfg'&#41;; foreach&#40;$config as $line_num => $line&#41;&#123;if&#40;preg_match&#40;"/^&#40;&#91;^#&#93;.+?&#41;=&#40;.*&#41;$/", $line, $param&#41;&#41;&#123;$$param&#91;1&#93;=$param&#91;2&#93;;&#125;&#125;
mysql_connect&#40;$databese_host, $database_login, $database_password&#41; or die&#40;"Could not connect &#58; " . mysql_error&#40;&#41;&#41;;
mysql_select_db&#40;'UTM5'&#41; or die&#40;"Could not select database"&#41;; $ip=$_SERVER&#91;'REMOTE_ADDR'&#93;;
$query = "SET NAMES cp1251"; $result = mysql_query&#40;$query&#41; or die&#40;"Query failed &#58; " . mysql_error&#40;&#41;&#41;;

echo "<div align=center><table width=\"100%\" cellspacing=\"3\" cellpadding=\"3\"><tr bgcolor=#CCCCCC>
<th><b>логин</th><th>полное имя</th><th>дата последнего платежа</th><th>ip адрес</th><th>баланс</th><th>кредит</th><th>безлимит</th></tr>";

$sql=@mysql_query&#40;"SELECT login, full_name, ip, truncate&#40;balance,0&#41; AS balance, truncate&#40;credit,0&#41; AS credit, from_unixtime&#40;payment_date&#41; AS payment_date, unlimited, tariff_id FROM payments_updates
WHERE is_deleted = 0 GROUP BY id HAVING MAX&#40;payment_date&#41; < UNIX_TIMESTAMP&#40;DATE_SUB&#40;NOW&#40;&#41;, INTERVAL $interval MONTH&#41;&#41; OR payment_date IS NULL ORDER BY payment_date ASC"&#41;;

while &#40;$ro=@mysql_fetch_array&#40;$sql&#41;&#41;&#123; if&#40;$ro&#91;tariff_id&#93; == 1 or $ro&#91;tariff_id&#93; == 17 or $ro&#91;ip&#93; == ''&#41;&#123; continue; &#125; $i++;
  echo "<tr><td>$ro&#91;login&#93;</td><td>$ro&#91;full_name&#93;</td><td>$ro&#91;payment_date&#93;</td><td>$ro&#91;ip&#93;</td><td align=right>$ro&#91;balance&#93;</td><td align=right>$ro&#91;credit&#93;</td><td align=right>$ro&#91;unlimited&#93;</td></tr>";
&#125;

echo "<tr bgcolor=#CCCCCC>
<th><b>логин</th><th>полное имя</th><th>дата последнего платежа</th><th>ip адрес</th><th>баланс</th><th>кредит</th><th>безлимит</th></tr>"; 

echo "</table></div>"; 

echo "<title>Должники &#40;$interval месяца&#41; - $i чел.</title>";
echo "</head>";

?>
Первый запуск может потребовать много времени, пока идёт обход всех [архивных] данных. Последующие запуски делаем периодически, можно кроном, используется оптимизация.

P.S. Код, конечно, далёк от совершенства :D, но может кому пригодится...

P.P.S. Также интересно, как эта задача решается у других операторов. Наверняка есть более красивые решения.
Последний раз редактировалось marvin Чт янв 31, 2013 17:33, всего редактировалось 1 раз.

Аватара пользователя
Magnum72
Сообщения: 1947
Зарегистрирован: Чт сен 22, 2005 06:54
Контактная информация:

Сообщение Magnum72 »

Решается триггерами повешанные на многие таблицы биллинга которые в свою очередь заполняют сводную табличку из более 100 параметров по каждому пользователю, начиная от наличия блокировки до суммы потребленных услуг в текущем периоде.

Аватара пользователя
marvin
Сообщения: 77
Зарегистрирован: Сб мар 24, 2007 11:18
Откуда: Нижняя Тура

Сообщение marvin »

Хорошая идея! Добавлю установку триггеров в этот скрипт.

Ответить