<< назад Проекты нового Вотпуска.
На этой страничке я расскажу еще об одном сложном проекте, программировать который я упирался весь 2013 year и который совершенно неожиданно загнулся в процессе внедрения в начале 2014-го года. Это еще один из моих Epic Fail Project, которые произошли в последние годы.
Этот проект оказался неудачным чисто из-за увеличения рейтинга Солцеликого Хуепутина, ибо как известно, каждое увеличение рейтинга Хуйла и сопутствующее усиление патриотизма всего на 1% - автоматически приводит к увеличения бегства капиталлов на 10%, коллапсу экономики на 10%, сокражению доходов на 10%, увеличению безработицы на 10% и падению туристического трафика на 20%.
У каждого из моих эпических фейлов последних лет была своя причина, не все они так или иначе связаны с нарастанием рейтинга Верховного Чекистского Лысогнома и сопутствующему коллапсу экономики России:
- Сайт с Google-maps API, имперсонализацией и Dynamic LINQ Expression. - проект был полностью завершен, чудесно работал, хорошо продвигался в интернете. Несмотря на то, что владелец успел заплатить мне всего 20-ти процентную предоплату. После чего, воодушевившись патриотизмом (а проще говоря, наглотавшись хуйлятины из хуйловизора) - двинулся устанавливать 'руцкий мир' в Лугандоне (как я предполагаю по беседам с ним) и, видимо, был где-то там бесславно закопан там в придорожной канаве. Соотвественно этот неплохой проект погиб на корню, причем я даже не получил оплату за программирование этого проекта.
- Сайт поиска работы. - этот деятель вообще заплатил мне аванс всего 10% от оговоренной суммы. И когда я довел проект до 70-80% завершенности, этот деятель растворился в тумане. При этом, вместо денег, начав мне рассказывать сказки, что проекты поисков работы, типа HH.RU, JOB.RU и так далее, коммерчески невыгодны и ему дорого соблюдать наши первоначальные договоренности об оплате моей работы по программированию.
- CMS сайта продажи авиабилетов и B2B-Сервисы с криптографическим залогиниванием (для клиентской и серверной интеграции) - этот проект работал идеально в течении примерно трех лет. Фейл заключается в том, что проект изначально задумывался как система интеграции сотен агенств по торговли авиабилетов, с развитой системой диллеров, оптовыми закупками авиабилетов. Проект был очень сложный, денег было немного, я рассчитывал получить хоть какое-то возмещение затраченных усилий при клонировании проекта, при адаптации клонов, при хостинге клонов проекта. Но владелец этого бизнеса тупо слился, отказался оплачивать копеечную сумму даже за хостинг этого проекта. Этот фейл я рассматриваю тоже как последствия роста рейтинга Солнцеликого Хуепутина, и вызванного этим ростом полного обвала туристического трафика, который и давал оборот торговли авиабилетами.
- Описанный на данной страничке проект тоже из туристической индустрии, поэтому его фейл тоже связан со "вставанием Раиси с колен" (о обвалом любого бизнеса, связанного с туризмом), но в отличие от двух предыдущих проектов, это известным мне люди, для которых я программирую уже 9 лет, и конечно, все было нормально оплачено. Можно даже сказать, что я не доделал в этом проекте лишь несколько мелких фишек (хотя я заранее предупреждал, что будут недоделки, которые надо будет доделывать в процессе внедрения). Но несмотря на то, что проект полностью работает нормально (за исключением десятка вспомогаельных мелких фишек), он внедрен не был, не раскручивался в ГУГЛе - и просто висит в интернете как надгробный камень на туристическом бизнесе.
Итак, ближе к проекту. У компании ВОТПУСК много проектов. Восемь-девять лет назад я сделал для этой компании несколько своих первых проектов: foto, poputi, story , user и search. Изначально я сделал им еще видеоконвертер, аналогичный https://www.youtube.com/ , но по невыясненной причине у него появлялись утечки памяти и он подвисал и у него требовалось примерно раз в три дня нажимать кнопку RESTART. Однако модератор был такой ленивый, что забывал это делать постоянно, в итоге компания не смогла победить лень модератора и через пару лет видеохостинг прекратил свое существование. А может быть дело не только в лени модератора, а и коммерческой неэффективности и неконткурентности видеохостинга. Позже я делал и многие другие проекты для этой компании, но несмотря на их шероховатости и баги, они отбирались, проекты доводились до совершенства и работают и поныне, например аренда.
Но вернемся к этим пяти проектам. По состоянию на 2006-й год выглядели вполне на уровне.
Особенно шикарным было техническое ядро этих проектов, например все рисунки хранились в базе, что позволяло не бодаться с гигабайтными каталогами, которые бы бекапились и копирвоались сутками, а сделать BAK-RESTORE базы за две-три секунды. Ну и все остальное было сделано круто - например в каталогах проекта ничего не хранилось, всякий кеширующий мусор хранился на отдельном (внешнем по отношению к проекту ресурсе), этот диск с кешами можно было в любой момент отмонтировать и примонтировать новый и он постепенно сам заполнялся нужной для проекта ерундой.
Кроме того, у этого проекта был очень нехилого размера уровень SQL, состоящий из сотен SQL-CRL-сборок, вьюшек, SQL-процедур. Вот скрин лишь небольшого фрагмента, причем только из одной базы.
Естестественно, за 8-9 лет внешний ползовательский интерфейс устарел. Как визуально, так появились и новые фишки (например рейтингование всего и вся) в такого рода проектах. Изначальное техническое задание, по которому я программировал этот проект 9 лет назад, также содержало некоторые раздражающие ограничения, например фотки в рассказ не вставлялись в любое место, а была лишь титуальная фотка и привязанный к рассказу альбом, а народ уже привык к OpenSource-интерфейсу LJ, где можно пихать фотки в любое место рассказа.
Поэтому было изготовлено ТЗ нового проекта, и назначен руководитель проекта, некий Алексей Тараканов, которому я и отчитывался о ходе работы.
Сложность нового проекта была в том, что старый проект, работающий на этой же базе, а также библиотеки BLL старого проекта, работающие с базой - должны были работать одновременно с новым, то есть база расширялась постоянно под новые фишки нового проекта, но эти расширения не должны были привести к сбоям в старых библиотеках BLL и в сделанных 8-9 лет назад сайтах. И админка у новых и старых проектов должна была остаться единая.
Кроме того, в силу того, что в проекте работал отдельный верстальщик (а старый проект, сделанный мною 8-9 лет назад, верстал я сам), то с внешним версталщиком, как вы понимаете, работать на ASP.NET практически невозможно - то было принято решение делать новый проект на ASP.NET.MVC.
В итоге, в новых проектах ВОТПУСКА получилось 323 тысячи строк и 1064 файла. Я понимаю, что часть этого кода автоматически сгенерированная, но все-таки обьем этого проекта оценить это позволяет. Предположим, по этой стаистике - кода верхнего уровня тут впятеро больше, чем например в моем проекте http://www.shel-auto.ru/ или втрое больше, чем в моем проекте FlySeason. Ну конечно все эти сравнения относительны - например во FlySeason уровень SQL был довольно скромный, зато было немерянно кода на FLEX-AIR. А в shel-auto.ru на уровене SQL вообще всего десяток процедур. Однако, общая оценка обьемов программирования проекта нового ВОТПУСКА и трудоемкость относительно других моих проектов - из этих цифр более ли менее понятна.
Из-за того, что MVC вместо простого ASP.NET, из-за нового функционала - мне пришлось практически весь этот проект (объемом в треть миллиона строк кода и тысячу файлов) делать практически заново, то ест старого кода удалось использовать всего лишь 1-5 проецентов.
А кроме того, было решено, что все значимые вещи в новом проекте (в целях ускорения) будут выполнены с кешированием, то есть без прямых запросов в базу. Сравните трудоемкость прямых запросов в базу и запросов с кешированием. Для того, чтобы сделать простой запрос - надо просто разметить кеш в DBML (первый скрин) и в одну LINQ-строку (строка 16 на вором скрине) получить данные. А на третьем-четвертом скрине вы видите трудоемкость этого процесса без прямых запросов в базу, а с запросами в кеш.
Теперь я покажу, как выглядит этот программный проект в студии:
Ну и теперь собственно формы этого проекта. Вы можете и сами по ним пощелкать, но поскольку проект заброшен и никому теперь не нужен (c учетом коллапса туриндустрии) - что-то могло уже отвалится и уже перестало работать.
Проект foto
Проект story
Проект poputi
Проект search
Проект user
В заключение я возьму какой-нибудь осмысленный кусочек из тысячи файлов, составляющих этот проект - опубликую его и пояснию как этот фрагмент работает. Возьмем для примера и посмотрим на кусочек кода, который управляет переключением вкладок ("С высоким рейтингом", "Обсуждаемые", "Новые") - на проекте Foto и формирует соответствующие URL и хлебные крошки.
Итак, для начала есть контрольчик, содержащий разные стили активной/неактивной вкладки.
1: <%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl" %>
2:
3: <div style="margin-bottom: 0;" id="tabBlockMy">
4: <ul class="tabNavigation">
5: <% If Model.Tab = "RatingTab" Then%>
6: <li>
7: <div id="bor_botStart" class="bor_bot">
8: </div>
9: </li>
10: <li>
11:
12: <%: Html.ActionLink("С высоким рейтингом", "RatingTab", "Top", Nothing, New With {.class = "selected"})%>
13: </li>
14: <li>
15: <div class="bor_bot">
16: </div>
17: </li>
18: <li>
19: <%: Html.ActionLink("Обсуждаемые", "CommentTab", "Top")%>
20: </li>
21: <li>
22: <div class="bor_bot">
23: </div>
24: </li>
25: <li>
26: <%: Html.ActionLink("Новые", "NewsTab", "Top")%>
27: </li>
28: <% ElseIf Model.Tab = "CommentTab" Then%>
29: <li>
30: <div id="bor_botStart" class="bor_bot">
31: </div>
32: </li>
33: <li>
34: <%: Html.ActionLink("С высоким рейтингом", "RatingTab", "Top")%>
35: </li>
36: <li>
37: <div class="bor_bot">
38: </div>
39: </li>
40: <li>
41: <%: Html.ActionLink("Обсуждаемые", "CommentTab", "Top", Nothing, New With {.class = "selected"})%>
42: </li>
43: <li>
44: <div class="bor_bot">
45: </div>
46: </li>
47: <li>
48: <%: Html.ActionLink("Новые", "NewsTab", "Top")%>
49: </li>
50: <% ElseIf Model.Tab = "NewsTab" Then%>
51: <li>
52: <div id="bor_botStart" class="bor_bot">
53: </div>
54: </li>
55: <li>
56: <%: Html.ActionLink("С высоким рейтингом", "RatingTab", "Top")%>
57: </li>
58: <li>
59: <div class="bor_bot">
60: </div>
61: </li>
62: <li>
63: <%: Html.ActionLink("Обсуждаемые", "CommentTab", "Top")%>
64: </li>
65: <li>
66: <div class="bor_bot">
67: </div>
68: </li>
69: <li>
70: <%: Html.ActionLink("Новые", "NewsTab", "Top", Nothing, New With {.class = "selected"})%>
71: </li>
72: <% End If%>
73:
74: </ul>
75: <div id="zakladka_p">
76: </div>
77: </div>
78:
Соотвественно, TopController содержит код подготовки данных для каждой вкладки и, что нам важно дальше, формирует коллекцию, из которой будут формироваться URL и описания хлебных крошек:
1: Namespace Foto
2: Public Class TopController
3: Inherits System.Web.Mvc.Controller
4: Dim PAGESIZE As Integer = 30
5:
6: Protected Overloads Overrides Sub OnActionExecuting(ByVal ctx As ActionExecutingContext)
7: MyBase.OnActionExecuting(ctx)
8: 'Для навигатора
9: Dim LinkNodes As String() = ctx.HttpContext.Request.FilePath.Split("/")
10: Dim TxtNodes As New ArrayList
11: For i As Integer = 0 To LinkNodes.Count - 1
12: Select Case LinkNodes(i)
13: Case ""
14: TxtNodes.Add("")
15: Case "Top"
16: TxtNodes.Add("Лучшие фото")
17: Case "RatingTab"
18: TxtNodes.Add("С высоким рейтингом")
19: Case "CommentTab"
20: TxtNodes.Add("Обсуждаемые")
21: Case "NewsTab"
22: TxtNodes.Add("Новые")
23: Case "GoCountry"
24: Select Case Session("Tab")
25: Case "RatingTab"
26: TxtNodes.Add("С высоким рейтингом")
27: Case "CommentTab"
28: TxtNodes.Add("Обсуждаемые")
29: Case "NewsTab"
30: TxtNodes.Add("Новые")
31: End Select
32: Case Else
33: If HttpContext.Application("CountryList") IsNot Nothing Then
34: For j As Integer = 0 To HttpContext.Application("CountryList").Count - 1
35: If LinkNodes(i).ToLower.Trim = HttpContext.Application("CountryList")(j).ID.ToLower.Trim Then
36: TxtNodes.Add(HttpContext.Application("CountryList")(j).Name)
37: End If
38: Next
39: End If
40: End Select
41: Next
42: ViewData("Navigator") = New With {.Link = LinkNodes, .Txt = TxtNodes}
43: End Sub
44:
45: Function Index() As ActionResult
46: Return RedirectToAction("RatingTab")
47: End Function
48:
49: Sub GetTopFoto()
50: Dim db1 As New Votpusk_DBDataContext
51: Dim BestUser = (db1.GetTopBestUser1).ToList
52: ViewData("BestUser") = BestUser
53:
54: Dim GetTopFoto = (db1.GetTopDayFoto).ToList
55: ViewData("TopFoto") = GetTopFoto(0)
56:
57: Dim TopFotoID As Integer = GetTopFoto(0).UserData_i
58: Dim Group = (From X In db1.UserDatas Select X Where X.i = TopFotoID).ToList(0).ToGroup
59: Dim FotoGroupID As Integer
60: If Group Is Nothing Then
61: FotoGroupID = 0
62: Else
63: FotoGroupID = Group
64: End If
65: ViewData("FotoGroup") = (From X In db1.UserDatas Select X Where X.ToGroup = FotoGroupID Order By X.i).ToList
66:
67: 'Dim BestFoto = (db1.GetTopBestFoto).ToList
68: 'ViewData("BestFoto") = BestFoto
69:
70: End Sub
71:
72: Function RatingTab(id As String) As ActionResult
73: Session("Tab") = "RatingTab"
74: GetTopFoto()
75: Dim DB1 As New Votpusk_DBDataContext
76: Dim BestFotoList
77: If id IsNot Nothing Then
78: BestFotoList = (From X In DB1.TmpFotoWithBestRatings Take 30 Select X Where X.GroupName_CountryID = id Order By X.ROW_NUMBER).ToList
79: Else
80: BestFotoList = (From X In DB1.TmpFotoWithBestRatings Take 30 Select X Order By X.ROW_NUMBER).ToList
81: End If
82: Dim Model = New With {.Tab = "RatingTab", .FotoList = BestFotoList, .FotoListCount = PAGESIZE + 1}
83: If ViewData("Navigator").Link.Length > 3 Then
84: ViewData("PageTitle") = "foto.votpusk.ru - фото с высоким рейтингом (" & ViewData("Navigator").Txt(3).ToString.Trim & ")"
85: Else
86: ViewData("PageTitle") = "foto.votpusk.ru - фото с высоким рейтингом "
87: End If
88: Return View("Index", Model)
89: End Function
90:
91: Function CommentTab(id As String) As ActionResult
92: Session("Tab") = "CommentTab"
...
108: End Function
109:
110: Function NewsTab(id As String) As ActionResult
111: Session("Tab") = "NewsTab"
...
123: End Function
И наконец, тот самый контрольчик хлебных крошек, который читает сформированную коллекцию и формирует хлебные крошки.
1: <%@ Control Language="VB" Inherits="System.Web.Mvc.ViewUserControl" %>
2:
3: <div id="blockBreadCrumbs">
4: <a href="http://www.votpusk.ru">Главная</a>
5: <img src="http://www.votpusk.ru/image/Hlebnye-kroshki.gif" class="breadCrumbs">
6: <a href="http://<%:Application("FotoDomain")%>">Фото туристов</a>
7: <img src="http://www.votpusk.ru/image/Hlebnye-kroshki.gif" class="breadCrumbs">
8: <% For i As Integer = 1 To ViewData("Navigator").Txt.Count - 1%>
9: <% Dim Link As String = ""
10: For j As Integer = 1 To i
11: Link &= ViewData("Navigator").Link(j).ToString() & "/"
12: Next
13: %>
14: <a href="http://<%:Application("FotoDomain")%>/<%: Link %>"><%: ViewData("Navigator").Txt(i).ToString()%></a>
15: <%If i < ViewData("Navigator").Txt.Count - 1 Then%>
16: <img src="http://www.votpusk.ru/image/Hlebnye-kroshki.gif" class="breadCrumbs">
17: <%End If%>
18: <% Next%>
19: </div>
Красиво, не правда ли?
|