Email Report engine

code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.ServiceProcess;
using System.Text;
using EmailAlerts.Service.Properties;
using System.Net.Mail;
using DAL.findahome;
using System.Data.Linq;
using System.IO;
using System.Transactions;

using TargetState = DAL.findahome.InOut_Target.States;
using KeyState = DAL.findahome.InOut_Key.States;
using System.Threading;
using System.Globalization;
using System.Net.Mime;

namespace EmailAlerts.Service
{
    partial class Engine : ServiceBase
    {
        BedroomList emptyBedroomList = new BedroomList();
        Agent emptyAgent = new Agent() { idOffice = -1 };
        SmtpClient smtpClient = new SmtpClient();
        AgentContactFrequency defaultPeriod = new AgentContactFrequency() { LastSent = DateTime.Today.AddDays(-8), Frequency = 7 };
        BedroomList bedroomList = new BedroomList();
        PropertyHit propertyHit = new PropertyHit() { Emails = 0, Hits = 0 };
        EmailComparer emailComparer = new EmailComparer();

        public Engine()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            Process.GetCurrentProcess().PriorityClass = Settings.Default.Priority;
            this.Pulse.Enabled = true;
#if DEBUG
            this.Pulse.Interval = 20000;
#else
            this.Pulse.Interval = Settings.Default.Pulse.TotalMilliseconds;
#endif
        }

        protected override void OnStop()
        {
            this.Pulse.Enabled = false;
        }

        private void Pulse_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            this.Pulse.Enabled = false;
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-IE");

            // Control Sent in the Morning
            DateTime dt = DateTime.Now;
            if (dt.Hour > 6 && dt.Hour < 9)
            {
                if (Settings.Default.ApplicantAlertsOn)
                {
                    ProcessApplicantAlerts();
                }
                if (Settings.Default.AgentAlertsOn)
                {
                    ProcessAgentAlerts();
                }
            }
            Trace.Flush();
            this.Pulse.Enabled = true;
        }

        private void ProcessAgentAlerts()
        {
            try
            {
                var now = DateTime.Now;
                var dlo = new DataLoadOptions();
                dlo.LoadWith<Agency>(c => c.User);
                dlo.LoadWith<AgencyOffice>(c => c.Agency);
                dlo.LoadWith<AgencyOffice>(c => c.Agents);
                dlo.LoadWith<Agent>(c => c.AgentContactFrequency);
                dlo.LoadWith<Agent>(c => c.User);
                dlo.LoadWith<Property>(c => c.AlertContents);
                dlo.LoadWith<Property>(c => c.Frequency);
                dlo.LoadWith<Property>(c => c.PropMarket);
                dlo.LoadWith<Property>(c => c.PropertyType);
                dlo.LoadWith<Property>(c => c.PropertyHits);
                dlo.LoadWith<Property>(c => c.District);
                dlo.LoadWith<Property>(c => c.CountyCity);
                dlo.LoadWith<Property>(c => c.Country);

                using (var dc = new findahomeDataContext() { CommandTimeout = 600, LoadOptions = dlo })
                {
#if DEBUG
                    dc.Log = new StreamWriter(@"\linq2sql.log") { AutoFlush = true };
#endif
                    var officeCollection =
                        (from office in dc.AgencyOffices
                         from agent in office.Agents
                         let report = (agent.AgentContactFrequency ?? defaultPeriod)
                         where report.Frequency != Settings.Default.Unsubscribe
                         && now >= (report.LastSent ?? now.AddYears(-report.Frequency)).AddDays(report.Frequency)
                         && agent.User.Properties.Any(e => e.idCountry == 1/*only properties from Ireland*/ && e.idCRule == Settings.Default.Active && e.idPropMarket != Settings.Default.CollegeMarket || e.idCRule == Settings.Default.Agreed)
                         && agent.User.idCountry == 1 //only agent who have address Ireland
                         //********************** Test for martin**********
                         //&& agent.User.idUser == 32158  // martin's office account
                         && agent.AgencyOffice.Agency.idAffiliation == 3 // all the account that belong to IPAV 
                        //&& agent.User.idUser == 65276 // kidd's personal test account
                         //************************************************
                         select office).Take(300)
                        .ToArray();
                    var exportTargetCollection = dc.InOut_Targets.Where(c => c.State == TargetState.OutOn).ToArray();

                    var template = dc.InOut_Target_Contents.Where(c => c.Website_Id == Settings.Default.fourpmWebsiteId).ToList();
                  //  template.ForEach(delegate(InOut_Target_Content c) { c.Text = Uri.UnescapeDataString(c.Text); });
                    var onstyle = template.First(c => c.Name.Equals(Settings.Default.propertyreport_onstyle)).Text;

                    foreach (var office in officeCollection)
                    {
                        try
                        {
                            var publisherCollection = office.Agents.Where(c => c.User.Properties.Any(e => e.idCRule == Settings.Default.Active || e.idCRule == Settings.Default.Agreed && e.idPropMarket != Settings.Default.CollegeMarket)).ToArray();
                            var marketCollection = new StringBuilder();
                            var marketCounter = 0;
                            var propertyCollection = new StringBuilder();
                            var currentMarket = string.Empty;
                            var currentOwner = string.Empty;
                            var odd = false;
                            var propCounter = 0;

                            
                            foreach (var p in publisherCollection.SelectMany(d => d.User.Properties.Where(c => c.idCRule == Settings.Default.Active || c.idCRule == Settings.Default.Agreed && c.idPropMarket != Settings.Default.CollegeMarket)).OrderBy(c => c.PropMarket.MarketName).ThenBy(c => c.idCreatedBy).ThenByDescending(c => c.ModifyDate))
                            {
                                if (currentMarket != p.PropMarket.MarketName)
                                {
                                    currentMarket = p.PropMarket.MarketName;
                                    propertyCollection.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_marketline)).Text, p.idPropMarket, currentMarket);
                                    marketCollection.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_marketindex)).Text, p.idPropMarket, currentMarket);
                                    marketCounter++;
                                    odd = false;
                                    if (currentOwner != p.User.Name)
                                    {
                                        currentOwner = p.User.Name;
                                    }
                                    propertyCollection.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_publisherline)).Text, currentOwner);
                                }
                                if (currentOwner != p.User.Name)
                                {
                                    currentOwner = p.User.Name;
                                    propertyCollection.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_publisherline)).Text, currentOwner);
                                }
                                var expire = p.ModifyDate.Add(p.PropMarket.StatusGroup == PropMarket.StatusGroupVariant.Sales ? Settings.Default.SaleExpiration : Settings.Default.LetExpiration);
                                expire = expire < p.ExpiryDate ? p.ExpiryDate : expire;
                                var expiredays = (expire - now).Days;
                                var pclass = expiredays <= 3 ? " color: #e00!important;" : string.Empty;
                                var temp = 0;
                                var size = currentMarket.ToLower().Contains("commercial") || currentMarket.ToLower().Contains("land") ?
                                    string.Format("<span>{0}</span> {1}", p.PropSize > 0 ? (Int32)p.PropSize : (Int32)p.PropSizeAcres, p.PropSize > 0 ? "SqM" : "Acr")
                                    :
                                    string.Format("<span>{0}</span> bed{1}", temp = (p.BedroomLists.FirstOrDefault() ?? bedroomList).TotalRooms, temp == 1 ? string.Empty : "s");
                                var price = string.Format("{0:c0} {1}", p.PriceVal, (p.PropMarket.StatusGroup == PropMarket.StatusGroupVariant.Sales ? string.Empty : p.Frequency.FrequencyName));
                                var pics = p.PictureAdded > 0 ? p.PropertyPictures.Count(c => (c.Thumbnail ?? 0) == 0) : 0;
                                var hit = (p.PropertyHits ?? propertyHit);
                                var alert = p.AlertContents.Count;
                                int? num = null;
                                propertyCollection.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_propertyline)).Text,
                                    pclass + ((odd = !odd) ? " background-color: #F8F4F0;" : string.Empty),
                                    p.idList, //{0}
                                    //string.Format("{0}, {1}, {2}, {3}", p.DisplayAddress, p.District.DistrictName, p.CountyCity.CountyCityName, p.Country.CountryName).Trim(' ', '.', ','),
                                    p.DisplayAddress,
                                    size,
                                    p.PropertyType.PTypeName,
                                    price,
                                    num,
                                    pics == 0 ? string.Empty : pics.ToString() + "pics",
                                    //pics,
                                    //pics == 1 ? string.Empty : "s",
                                    // ********** response the john request, when hits & Email Equal 0, hidden it *************
                                    //hit.Hits,  
                                    num,
                                    hit.Hits == 0 ? string.Empty : hit.Hits.ToString() + " hits",
                                    //hit.Hits == 1 ? string.Empty : "s",
                                    num,
                                    // hit.Emails,
                                    hit.Emails == 0 ? string.Empty : hit.Emails.ToString() + " Emails",
                                   // hit.Emails == 1 ? string.Empty : "s",
                                   // *****************************************************************************************
                                    //alert,
                                    //alert == 1 ? string.Empty : "s",
                                    num,
                                    alert == 0 ? string.Empty : alert.ToString() + "alerts",
                                    p.ModifyDate,
                                    expiredays < 30 && expiredays > 0 ? string.Format("  {0} day{1}", expiredays, (Math.Abs(expiredays) > 1 ? "s" : string.Empty)) : string.Empty,
                                    string.Format("{0}.{1}", p.User.idUser, p.User.Key),
                                    //p.idPropMarket == 3 || p.idPropMarket == 8 || p.idPropMarket == 10 || p.idPropMarket == 11 || p.idPropMarket == 13 ? "Sale Agreed" : "Rent Agreed",
                                    p.idCRule == Settings.Default.Active ?
                                    p.idPropMarket == 3 || p.idPropMarket == 8 || p.idPropMarket == 10 || p.idPropMarket == 11 || p.idPropMarket == 13 ? String.Format(Settings.Default.PropertyRuleControl, string.Format("{0}.{1}", p.User.idUser, p.User.Key), p.idList, "agreed", Settings.Default.GreenColor, "Sale Agreed") : String.Format(Settings.Default.PropertyRuleControl, string.Format("{0}.{1}", p.User.idUser, p.User.Key), p.idList, "agreed", Settings.Default.GreenColor, "Rent Agreed")
                                    : p.idPropMarket == 3 || p.idPropMarket == 8 || p.idPropMarket == 10 || p.idPropMarket == 11 || p.idPropMarket == 13 ? String.Format(Settings.Default.PropertyRuleControl, string.Format("{0}.{1}", p.User.idUser, p.User.Key), p.idList, "close", Settings.Default.BlueColor, "Take Offline") : String.Format(Settings.Default.PropertyRuleControl, string.Format("{0}.{1}", p.User.idUser, p.User.Key), p.idList, "close", Settings.Default.BlueColor, "Take Offline"),
                                    string.Format("{0}, {1}, {2}", p.District.DistrictName, p.CountyCity.CountyCityName, p.Country.CountryName)  //{18}
                                    );
                                propCounter++;
                            }


                            foreach (var p in
                                (from p in publisherCollection
                                 let report = (p.AgentContactFrequency ?? defaultPeriod)
                                 where p.User.ValidEmail
                                 && report.Frequency != Settings.Default.Unsubscribe
                                 && now >= (report.LastSent ?? now.AddYears(-report.Frequency)).AddDays(report.Frequency)
                                 select p)
                                .Distinct(emailComparer))
                            {
                                var officeName = office.OfficeName.Trim(' ', '.', ',');
                                var reportOwner = string.Format("{0}, {1} {2}", p.User.Name, officeName.Equals(office.Agency.User.Name, StringComparison.InvariantCultureIgnoreCase) ? string.Empty : officeName + ",", office.Agency.User.Name).Trim(' ', '.', ',');
                                var body = new StringBuilder();
                                var exportCollection = new StringBuilder();
                                var key = string.Format("{0}.{1}", p.User.idUser, p.User.Key);
                                foreach (var target in exportTargetCollection.OrderBy(c => c.Name))
                                {
                                    var tkey = p.User.InOut_Keys.FirstOrDefault(c => c.Target_Id == target.Id) ?? new InOut_Key() { Approved = !Settings.Default.DefaultClosedTargets.Contains(target.Id.ToString()), State = KeyState.Open };
                                    var on = tkey.Approved && tkey.State == KeyState.Open ? onstyle : string.Empty;
                                    var off = string.IsNullOrEmpty(on) ? onstyle : string.Empty;
                                    exportCollection.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_exportline)).Text, target.Name, key, target.Id, on, off);
                                }
                                //############################Template select function for Onview.ie#############################################

                                if (p.AgencyOffice.Agency.idAffiliation.Equals(3))
                                {
                                    body.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_template_onview)).Text, string.Empty, now, reportOwner, key, propertyCollection, marketCollection, marketCounter > 1 ? string.Empty : " display: none;");
                                }
                                else
                                {
                                    //body.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_template_onview)).Text, string.Empty, now, reportOwner, key, exportCollection, propertyCollection, marketCollection, marketCounter > 1 ? string.Empty : " display: none;");
                                    body.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_template)).Text, string.Empty, now, reportOwner, key, exportCollection, propertyCollection, marketCollection, marketCounter > 1 ? string.Empty : " display: none;");
                                }

                                //if(p.AgentContactFrequency.Template.Equals(0))
                                //{
                                //    body.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_template)).Text, string.Empty, now, reportOwner, key, exportCollection, propertyCollection, marketCollection, marketCounter > 1 ? string.Empty : " display: none;");
                                //}
                                //else if (p.AgentContactFrequency.Template.Equals(1))
                                //{
                                //    body.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_template_onview)).Text, string.Empty, now, reportOwner, key, propertyCollection, marketCollection, marketCounter > 1 ? string.Empty : " display: none;");
                                //}
                                //else
                                //{
                                //    body.AppendFormat(template.First(c => c.Name.Equals(Settings.Default.propertyreport_template)).Text, string.Empty, now, reportOwner, key, exportCollection, propertyCollection, marketCollection, marketCounter > 1 ? string.Empty : " display: none;");
                                //}
                                //####################################################################################################################
                                var report = new MailMessage(Settings.Default.ReportEmail,
#if DEBUG
 "kidd@4pm.ie,michael@4pm.ie,john@4pm.ie",
#else
//"kidd@4pm.ie",
 p.User.UserEmail,
#endif
 string.Format(template.First(c => c.Name.Equals(Settings.Default.propertyreport_subject)).Text, propCounter), body.ToString()) { IsBodyHtml = true };

                                report.Bcc.Add(Settings.Default.ReportEmail);
                                report.ReplyTo = new MailAddress(template.First(c => c.Name.Equals(Settings.Default.propertyreport_replyto)).Text);
                                smtpClient.Send(report);
#if !DEBUG
                                if (p.AgentContactFrequency == null)
                                {
                                    dc.AgentContactFrequencies.InsertOnSubmit(new AgentContactFrequency() { AgentId = p.idAgent, Frequency = 7, LastSent = now });
                                }
                                else
                                {
                                    p.AgentContactFrequency.LastSent = now;
                                }
                                dc.SubmitChanges();
#endif
                            }
                        }
                        catch (Exception ex)
                        {
                            Trace.TraceWarning("OfficeId={0}, err: {1}", office.idOffice, ex);
                        }
                    }
#if DEBUG
                    dc.Log.Close();
#endif
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.ToString());
            }
        }

        private void ProcessApplicantAlerts()
        {
            try
            {
                var dlo = new DataLoadOptions();
                dlo.LoadWith<Property>(c => c.PropertyPictures);
                dlo.AssociateWith<Property>(c => c.PropertyPictures.Where(d => (d.Thumbnail ?? 0) == 1));
                dlo.LoadWith<Property>(c => c.Country);
                dlo.LoadWith<Property>(c => c.CountyCity);
                dlo.LoadWith<Property>(c => c.LivingType);
                dlo.LoadWith<Property>(c => c.PropertyType);
                dlo.LoadWith<Property>(c => c.PropMarket);
                dlo.LoadWith<Property>(c => c.User);
                dlo.LoadWith<User>(c => c.Agents);
                dlo.LoadWith<Property>(c => c.District);
                dlo.LoadWith<AlertRequest>(c => c.Applicant);
                dlo.LoadWith<AlertRequest>(c => c.User);
                dlo.LoadWith<AlertRequest>(c => c.Country);
                dlo.LoadWith<AlertRequest>(c => c.CountyCity);
                dlo.LoadWith<AlertRequest>(c => c.District);
                dlo.LoadWith<AlertRequest>(c => c.DistrictGroup);
                dlo.LoadWith<AlertRequest>(c => c.LetCategory);
                dlo.LoadWith<AlertRequest>(c => c.PropertyType);
                dlo.LoadWith<AlertRequest>(c => c.PropMarket);
                dlo.LoadWith<AlertRequest>(c => c.Site);
                dlo.LoadWith<AlertRequest>(c => c.InOut_Target);
                dlo.LoadWith<Applicant>(c => c.User);
                dlo.LoadWith<AgencyOfficeWebSite>(c => c.ClientWebSite);

                using (var dc = new findahomeDataContext() { LoadOptions = dlo, CommandTimeout = 600, })
                {
#if DEBUG
                    dc.Log = new StreamWriter(@"\linq2sql.log") { AutoFlush = true };
#endif
                    var log = new AlertEngineLog();
                    var now = DateTime.Now;

                    try
                    {
                        log.CalledBy = this.ServiceName;
                        log.Date = DateTime.Now;
                        log.Error = string.Empty;

                        var currentAlertRequests = dc.AlertRequests.Where(c =>
                            c.Active
                            && now >= (c.LastSend ?? c.CreatedDate).AddDays(c.Frequency ?? 0)
                            && (c.Applicant.AllowEmail ?? c.Applicant.AllowSMS ?? c.Applicant.AllowMMS ?? c.idApplicant == 0)
                            && c.Applicant.User != null//this check constraint must be set on the db level!
                            )
                            .ToArray();
                        if (currentAlertRequests.Count() > 0)
                        {
                            var minDate = currentAlertRequests.Min(c => c.LastSend ?? c.CreatedDate);
                            var properties = dc.Properties.Where(c => c.idCRule == 1 && c.CreatedDate >= minDate).ToArray();

                            var portalIds = currentAlertRequests.Select(c => c.idSite).Where(c => c.HasValue).Distinct().ToArray();
                            var officeIds = currentAlertRequests.Select(c => c.idOffice).Where(c => c.HasValue).Distinct().ToArray();
                            var agentIds = dc.Agents.Where(c => officeIds.Contains(c.idOffice)).Select(c => new { c.idOffice, c.idUser }).ToArray();

                            var officesTemplates =
                                (from template in dc.gw_newsletter_templates
                                 join a in dc.Agents on template.UserId equals a.idAgent
                                 where officeIds.Contains(a.idOffice) && (template.Active ?? false) && template.idTemplateType == 1
                                 select new { template, a.idOffice })
                                .ToArray();

                            var portalsTemplates = dc.gw_newsletter_templates.Where(c => (c.Active ?? false) && c.idTemplateType == 1 && portalIds.Contains(c.idSite)).ToArray();
                            var defaultTemplate = dc.gw_newsletter_templates.FirstOrDefault(c => (c.Locked ?? false) && c.idTemplateType == 1);

                            var clientWebsites = dc.AgencyOfficeWebSites.Where(c => officeIds.Contains(c.idAgencyOffice)).Select(c => new { c.ClientWebSite, c.idAgencyOffice }).ToArray();
                            var portalWebsites = dc.Sites.ToArray();

                            foreach (var alertRequest in currentAlertRequests)
                            {
                                if (alertRequest.idApplicant != 0 && !alertRequest.Applicant.User.ValidEmail)
                                {
                                    alertRequest.Active = false;
                                    alertRequest.LastSend = now;
                                    log.Error += string.Format("\nUser.Id {0} have invalid email '{1}'", alertRequest.Applicant.User.idUser, alertRequest.Applicant.User.UserEmail);
                                    dc.SubmitChanges();
                                    continue;//bypass bad emails
                                }
                                else if (alertRequest.idApplicant == 0 && !alertRequest.User.ValidEmail)
                                {
                                    alertRequest.Active = false;
                                    alertRequest.LastSend = now;
                                    log.Error += string.Format("\nUser.Id {0} have invalid email '{1}'", alertRequest.User.idUser, alertRequest.User.UserEmail);
                                    dc.SubmitChanges();
                                    continue;//bypass bad emails
                                }
                                if (alertRequest.InOut_Target_Id != 0)//new EmailAlerts.Client
                                {
                                    var sentCollection = alertRequest.User.AlertRequests.SelectMany(a => a.Alerts.SelectMany(c => c.AlertContents.Select(d => d.idList))).Distinct().ToArray();
                                    var ownerCollection = alertRequest.InOut_Target.Office_Id == 0 ? alertRequest.Website.Agency.AgencyOffices.SelectMany(c => c.Agents.Select(d => d.idUser)).ToList()
                                        : alertRequest.InOut_Target.Agency_Id == 0 ? alertRequest.Website.AgencyOffice.Agents.Select(d => d.idUser).ToList()
                                        : new List<int>()
                                        ;
                                    var propertiesToSend = (from c in properties
                                                            let l = Compare(alertRequest, c)
                                                            let t = c.AdTypeList.Length > alertRequest.InOut_Target.SiteIndex - 1 ? (int)c.AdTypeList[alertRequest.InOut_Target.SiteIndex - 1] != (int)Ad_TypeX.Variant.DontShow : false
                                                            where ownerCollection.Contains(c.idCreatedBy) || t
                                                            && !sentCollection.Contains(c.idList)
                                                            && alertRequest.idPropertyMarket == c.idPropMarket
                                                            && alertRequest.idPropertyType == c.idPType
                                                            && alertRequest.idCountry == c.idCountry
                                                            && (alertRequest.idCounty ?? c.idCountyCity) == c.idCountyCity
                                                            && c.CreatedDate >= (alertRequest.LastSend ?? alertRequest.CreatedDate)
                                                            && l >= Settings.Default.MinimumLikeness
                                                            orderby string.Format("{0}.{1}", l, c.idList) descending
                                                            select c)
                                                           .Take(Settings.Default.PropertyLimit)
                                                           .ToArray();

                                    if (propertiesToSend.Count() > 0)
                                    {
                                        var header = alertRequest.InOut_Target.InOut_Target_Contents.FirstOrDefault(c => c.Name == Settings.Default.emailalert_header);
                                        var propertyline = alertRequest.InOut_Target.InOut_Target_Contents.FirstOrDefault(c => c.Name == Settings.Default.emailalert_propertyline);
                                        var footer = alertRequest.InOut_Target.InOut_Target_Contents.FirstOrDefault(c => c.Name == Settings.Default.emailalert_footer);

                                        var alert = new Alert()
                                        {
                                            Type = Alert.TypeVariant.Email,
                                            Request_Id = alertRequest.idRequest,
                                            SendDate = now,
                                        };
                                        dc.Alerts.InsertOnSubmit(alert);
                                        var key = alertRequest.Applicant.User.Key;
                                        var body = new StringBuilder(@"<html xmlns=""http://www.w3.org/1999/xhtml""><head><title>email alert</title></head><body>");

                                        body.AppendFormat(header.Text, now);

                                        foreach (var property in propertiesToSend)
                                        {
                                            //{0} - id property
                                            //{1} - main image src
                                            //{2} - address
                                            //{3} - property status
                                            //{4} - price
                                            //{5} - proprty type
                                            //{6} - part of description
                                            body.AppendFormat(propertyline.Text,
                                                property.idList,
                                                property.PropertyPictures.Any() ? string.Format("http://www.4pm.ie/ShowThumbnail.aspx?id={0}&img={1}", property.idList, Uri.EscapeDataString(property.PropertyPictures.First().PicPath)) : string.Empty,
                                                string.Format("{0}, {1}, {2}", property.DisplayAddress, property.District.DistrictName, property.CountyCity.CountyCityName),
                                                property.PropStatus.StatusName,
                                                property.PriceVal > 0 ? string.Format("{0:c0} {1}", property.PriceVal, property.PropMarket.StatusGroup == PropMarket.StatusGroupVariant.Rental  /*show frequency for rental only*/ ? property.Frequency.FrequencyName : string.Empty) : "On request",
                                                string.Format("{0} {1}", property.PropertyType.PTypeName, property.LivingType.LTypeName.Equals("not applicable", StringComparison.InvariantCultureIgnoreCase) ? string.Empty : string.Format("({0})", property.LivingType.LTypeName)),
                                                property.Description.PadRight(300).Substring(0, 300).Trim() + "..."
                                                )
                                            ;

                                            var alertContent = new AlertContent();
                                            alertContent.Alert = alert;
                                            alertContent.idList = property.idList;
                                            dc.AlertContents.InsertOnSubmit(alertContent);
                                        }
                                        body.AppendFormat(footer.Text,
                                            string.Format("http://api.4pm.ie/alert.svc/{0}.{1}/unsubscribe", alertRequest.idRequest, key)
                                        );
                                        body.Append(@"</body></html>");
                                        var message = new MailMessage(
                                                    string.Format("info@{0}", alertRequest.Website.Domain),
                                                    alertRequest.Applicant.User.UserEmail,
                                                    Settings.Default.EmailSubject,
                                                    body.ToString())
                                        {
                                            IsBodyHtml = true,
                                        };
                                        message.Bcc.Add(Settings.Default.ReportEmail);
                                        smtpClient.Send(message);
                                    }
                                    log.NbAlertsSent++;
                                }
                                else if (alertRequest.Website_Id != 0)//new EmailAlerts.Client
                                {
                                    var sentCollection = alertRequest.Applicant.AlertRequests.SelectMany(a => a.Alerts.SelectMany(c => c.AlertContents.Select(d => d.idList))).Distinct().ToArray();
                                    var ownerCollection = alertRequest.Website.Office_Id == 0 ?
                                        alertRequest.Website.Agency.AgencyOffices.SelectMany(c => c.Agents.Select(d => d.idUser)).ToList() :
                                        alertRequest.Website.AgencyOffice.Agents.Select(d => d.idUser).ToList()
                                        ;
                                    var propertiesToSend = (from c in properties
                                                            let l = Compare(alertRequest, c)
                                                            where ownerCollection.Contains(c.idCreatedBy)
                                                            && !sentCollection.Contains(c.idList)
                                                            && alertRequest.idPropertyMarket == c.idPropMarket
                                                            && alertRequest.idPropertyType == c.idPType
                                                            && alertRequest.idCountry == c.idCountry
                                                            && (alertRequest.idCounty ?? c.idCountyCity) == c.idCountyCity
                                                            && c.CreatedDate >= (alertRequest.LastSend ?? alertRequest.CreatedDate)
                                                            && l >= Settings.Default.MinimumLikeness
                                                            orderby string.Format("{0}.{1}", l, c.idList) descending
                                                            select c)
                                                           .Take(Settings.Default.PropertyLimit)
                                                           .ToArray();

                                    if ((alertRequest.Applicant.AllowEmail ?? false) && propertiesToSend.Count() > 0)
                                    {
                                        var header = alertRequest.Website.InOut_Target_Contents.FirstOrDefault(c => c.Name == Settings.Default.emailalert_header);
                                        var propertyline = alertRequest.Website.InOut_Target_Contents.FirstOrDefault(c => c.Name == Settings.Default.emailalert_propertyline);
                                        var footer = alertRequest.Website.InOut_Target_Contents.FirstOrDefault(c => c.Name == Settings.Default.emailalert_footer);

                                        var alert = new Alert()
                                        {
                                            Type = Alert.TypeVariant.Email,
                                            Request_Id = alertRequest.idRequest,
                                            SendDate = now,
                                        };
                                        dc.Alerts.InsertOnSubmit(alert);
                                        var key = alertRequest.Applicant.User.Key;
                                        var body = new StringBuilder(@"<html xmlns=""http://www.w3.org/1999/xhtml""><head><title>email alert</title></head><body>");

                                        body.AppendFormat(header.Text, now);

                                        foreach (var property in propertiesToSend)
                                        {
                                            //{0} - id property
                                            //{1} - main image src
                                            //{2} - address
                                            //{3} - property status
                                            //{4} - price
                                            //{5} - proprty type
                                            //{6} - part of description
                                            body.AppendFormat(propertyline.Text,
                                                property.idList,
                                                property.PropertyPictures.Any() ? string.Format("http://www.4pm.ie/ShowThumbnail.aspx?id={0}&img={1}", property.idList, Uri.EscapeDataString(property.PropertyPictures.First().PicPath)) : string.Empty,
                                                string.Format("{0}, {1}, {2}", property.DisplayAddress, property.District.DistrictName, property.CountyCity.CountyCityName),
                                                property.PropStatus.StatusName,
                                                property.PriceVal > 0 ? string.Format("{0:c0} {1}", property.PriceVal, property.PropMarket.StatusGroup == PropMarket.StatusGroupVariant.Rental  /*show frequency for rental only*/ ? property.Frequency.FrequencyName : string.Empty) : "On request",
                                                string.Format("{0} {1}", property.PropertyType.PTypeName, property.LivingType.LTypeName.Equals("not applicable", StringComparison.InvariantCultureIgnoreCase) ? string.Empty : string.Format("({0})", property.LivingType.LTypeName)),
                                                property.Description.PadRight(300).Substring(0, 300).Trim() + "..."
                                                )
                                            ;

                                            var alertContent = new AlertContent();
                                            alertContent.Alert = alert;
                                            alertContent.idList = property.idList;
                                            dc.AlertContents.InsertOnSubmit(alertContent);
                                        }
                                        body.AppendFormat(footer.Text,
                                            string.Format("http://api.4pm.ie/alert.svc/{0}.{1}/unsubscribe", alertRequest.idRequest, key)
                                        );
                                        body.Append(@"</body></html>");
                                        var message = new MailMessage(
                                                    string.Format("info@{0}", alertRequest.Website.Domain),
                                                    alertRequest.Applicant.User.UserEmail,
                                                    Settings.Default.EmailSubject,
                                                    body.ToString())
                                                    {
                                                        IsBodyHtml = true,
                                                    };
                                        message.Bcc.Add(Settings.Default.ReportEmail);
                                        smtpClient.Send(message);
                                    }
                                    log.NbAlertsSent++;
                                }
                                else//old EmailAlerts.Client TODO: remove
                                {
#if !DEBUG
                                    var sentList = alertRequest.Applicant.AlertRequests.SelectMany(a => a.Alerts.SelectMany(c => c.AlertContents.Select(d => d.idList))).Distinct().ToArray();
                                    var relatedAlertRequestList = alertRequest.Applicant.AlertRequests.Where(a => a.Active && ((alertRequest.idSite.HasValue && a.idSite == alertRequest.idSite) || (alertRequest.idOffice.HasValue && a.idOffice == alertRequest.idOffice))).Select(a => a.idRequest).ToArray();
                                    var ownerList = agentIds.Where(c => alertRequest.idOffice.HasValue && c.idOffice == alertRequest.idOffice).Select(c => c.idUser).Distinct().ToArray();
                                    var propertiesToSend =
                                        (from c in properties
                                         let l = Compare(alertRequest, c)
                                         where ((alertRequest.idOffice.HasValue && ownerList.Contains(c.idCreatedBy)) || (alertRequest.idSite.HasValue))
                                         && !sentList.Contains(c.idList)
                                         && alertRequest.idPropertyMarket == c.idPropMarket
                                         && alertRequest.idPropertyType == c.idPType
                                         && alertRequest.idCountry == c.idCountry
                                         && (alertRequest.idCounty ?? c.idCountyCity) == c.idCountyCity
                                         && c.CreatedDate >= (alertRequest.LastSend ?? alertRequest.CreatedDate)
                                         && l >= Settings.Default.MinimumLikeness
                                         && (alertRequest.idSite.HasValue || (alertRequest.idOffice ?? 0) == (c.User.Agents ?? emptyAgent).idOffice)
                                         orderby string.Format("{0}.{1}", l, c.idList) descending
                                         select c)
                                        .Take(Settings.Default.PropertyLimit)
                                        .ToArray();

                                    if ((alertRequest.Applicant.AllowEmail ?? false) && propertiesToSend.Count() > 0)
                                    {
                                        var template = defaultTemplate;
                                        if (alertRequest.idSite.HasValue)
                                        {
                                            var tempTemplate = portalsTemplates.FirstOrDefault(c => c.idSite == alertRequest.idSite);
                                            if (tempTemplate != null)
                                                template = tempTemplate;
                                        }
                                        else if (alertRequest.idOffice.HasValue)
                                        {
                                            var tempTemplate = officesTemplates.FirstOrDefault(c => c.idOffice == alertRequest.idOffice);
                                            if (tempTemplate != null)
                                                template = tempTemplate.template;
                                        }

                                        if (template != null)
                                        {
                                            var alert = new Alert()
                                            {
                                                Type = Alert.TypeVariant.Email,
                                                Request_Id = alertRequest.idRequest,
                                                SendDate = now,
                                            };
                                            dc.Alerts.InsertOnSubmit(alert);
                                            var key = alertRequest.Applicant.User.Key;
                                            var body = new StringBuilder();
                                            var websiteURL = string.Empty;
                                            var showPropertyPageURL = string.Empty;
                                            var showUserPageURL = string.Empty;
                                            if ((alertRequest.idOffice ?? 0) > 0)
                                            {
                                                var website = clientWebsites.FirstOrDefault(c => c.idAgencyOffice == alertRequest.idOffice);
                                                if (website != null)
                                                {
                                                    websiteURL = website.ClientWebSite.mainURL;
                                                    showPropertyPageURL = website.ClientWebSite.showPropertyURL;
                                                    showUserPageURL = websiteURL + "/AlertRequest.aspx";
                                                }
                                                else
                                                {
                                                    alertRequest.LastSend = now;
                                                    log.Error += string.Format("\nwe have no ClientWebSite for this office {0}", alertRequest.idOffice);
                                                    continue;//bypass because bad office configuration
                                                }
                                            }
                                            else if (alertRequest.idSite.HasValue)
                                            {
                                                websiteURL = "http://www." + alertRequest.Site.SiteName;
                                                showPropertyPageURL = websiteURL + "/ShowProp.aspx?id=";
                                                showUserPageURL = websiteURL + "/AlertRequest.aspx";
                                            }

                                            body.Append("<div style=\"color:#000; text-align:left; \">");
                                            body.Append(template.Content);

                                            var StyleBoldText = "font-family: Trebuchet MS, Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; color: #414141; line-height: 18px; font-weight:bold;";

                                    #region " Alert Request "
                                            body.Append("<br/><span style=\"font-family: Trebuchet MS, Verdana, Arial, Helvetica, sans-serif;font-size: 16px;font-weight: bold;color: #414141;\">You requested properties matching these criteria :</span>");
                                            body.Append("<i>(Request created on : " + alertRequest.CreatedDate.ToShortDateString() + ")</i><br/>");
                                            body.Append(@"<div style=""text-align:left; margin-bottom:10px;padding:0px;""><img src=""http://www.4pm.ie/emailAlertTemplates/bar.gif"" border=""0"" /></div>");
                                            body.Append("<table border=\"0\" style=\" \" cellpadding=\"5\">");
                                            body.Append("<tr><td>Country : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.Country.CountryName + "</td></tr>");
                                            body.Append("<tr><td>County/City : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.CountyCity.CountyCityName + "</td></tr>");
                                            if (alertRequest.idDistrict.HasValue)
                                                body.Append("<tr><td>District : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.District.DistrictName + "</td></tr>");
                                            if (alertRequest.idDistrictGroup.HasValue)
                                                body.Append("<tr><td>District group : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.DistrictGroup.GroupName + "</td></tr>");
                                            if (!string.IsNullOrEmpty(alertRequest.PostCode))
                                                body.Append("<tr><td>Post code : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.PostCode + "</td></tr>");
                                            body.Append("<tr><td>Market : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.PropMarket.MarketName);
                                            if (alertRequest.idLetCategory.HasValue)
                                                body.Append(" - " + alertRequest.LetCategory.CategoryName);
                                            body.Append("</td></tr>");
                                            body.Append("<tr><td>Type of property : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.PropertyType.PTypeName);
                                            if (alertRequest.idLivingType.HasValue)
                                                body.Append(" - " + alertRequest.LivingType.LTypeName);
                                            body.Append("</td></tr>");
                                            if ((alertRequest.NumberOfBeds ?? 0) != 0)
                                                body.Append("<tr><td>Number of beds : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.NumberOfBeds + "</td></tr>");
                                            if (alertRequest.MinPrice != 0)
                                                body.Append("<tr><td>Minimum price : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.MinPrice.ToString("c") + "</td></tr>");
                                            if (alertRequest.MaxPrice != 0)
                                                body.Append("<tr><td>Maximum price : </td><td style=\"" + StyleBoldText + "\"> " + alertRequest.MaxPrice.ToString("c") + "</td></tr>");
                                            body.Append("</table>");
                                    #endregion

                                            body.Append("<br/><br/><span style=\"font-family: Trebuchet MS, Verdana, Arial, Helvetica, sans-serif;font-size: 16px;font-weight: bold;color: #414141;\">Properties matching your request:</span><br/>");
                                            body.Append("<div style=\"text-align:left; margin-bottom:10px;padding:0px;\"><img src=\"http://www.4pm.ie/emailAlertTemplates/bar.gif\" border=\"0\" /></div>");
                                            body.Append("<table border=\"0\" style=\"\" cellspacing=\"4\"  >");

                                            var isAlternateRow = false;
                                            var isFirstInRow = true;

                                    #region " Property section "
                                            foreach (var property in propertiesToSend)
                                            {
                                                if (isFirstInRow)
                                                {
                                                    body.Append("<tr style=\"\">");
                                                }
                                                var detailsLink = showPropertyPageURL + property.idList;

                                                body.Append("<td width=\"309\" height=\"145\" valign=\"top\"><table width=\"98%\" border=\"0\" align=\"center\" cellpadding=\"0\" cellspacing=\"0\" style=\"background-color: #ffffff;border: #bda68e 1px solid;padding: 2px;\">");
                                                body.Append("<tr><td width=\"45\" valign=\"top\"><div style=\"width: 124px;height: 124px; background-color: #FFFFFF;\">");
                                                if (property.PictureAdded == 1)
                                                    body.Append("<img src=\"" + "http://www.4pm.ie/ShowThumbnail.aspx?id=" + property.idList + "&img=" + Uri.EscapeDataString(property.PropertyPictures.First().PicPath) + "\"  style=\"margin: 2px;\" />");
                                                else
                                                    body.Append("<div style=\"width:120px; height:120px; margin:2px; border: solid 1px #f4ebe0; color:#d8d1ca; text-align:center;line-height: 18px;vertical-align:middle\" ><br/><br/>No picture<br/>Available</div>");
                                                body.Append("</div></td><td width=\"62%\" valign=\"top\" style=\"padding:2px;\">");
                                                body.Append("<div style=\"overflow: hidden;height: 40px;background-color: #F8F4F0;vertical-align:middle; padding:4px;\"><span style=\"font-family: Trebuchet MS, Verdana, Arial, Helvetica, sans-serif;font-size: 12px;color: #ac651d;font-weight:bold;\">" + (String.Format("{0}, {1}, {2}", property.DisplayAddress, property.District.DistrictName, property.CountyCity.CountyCityName)) + "</span></div>");
                                                body.Append("<span style=\"font-family: Trebuchet MS, Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; color: #414141; line-height: 18px;\"><strong>" + property.PropertyType.PTypeName);
                                                if ((property.BedroomLists.FirstOrDefault() ?? emptyBedroomList).TotalBedspaces != 0)
                                                    body.Append(" - <span style=\"font-family: Trebuchet MS, Verdana, Arial, Helvetica, sans-serif;	font-size: 11px; color: #4d8989;\">" + (property.BedroomLists.FirstOrDefault() ?? emptyBedroomList).TotalBedspaces + "</span> </strong>bed(s)<strong>");
                                                body.Append("<br /></strong></span> ");
                                                body.Append("<span style=\"font-family: Trebuchet MS, Verdana, Arial, Helvetica, sans-serif;	font-size: 11px; color: #4d8989;\">" + (property.PriceVal > 0 ? property.PriceVal.ToString("c") : "Price on request") + "</span><br />");
                                                body.Append("</td></tr>");
                                                body.Append("<tr><td colspan=\"2\" style=\"background-color: #F8F4F0;height:12px;vertical-align:bottom; text-align:right;\"><a href=\"" + detailsLink + "\" target=\"_blank\" style=\"font-family: Trebuchet MS, Verdana, Arial, Helvetica, sans-serif;font-size: 10px;color: #414141;font-weight:bold; text-decoration:none;\">View more details >>></a></td></tr></table>");
                                                body.Append("</td>");

                                                if (!isFirstInRow)
                                                {
                                                    body.Append("</tr>");
                                                }

                                                isAlternateRow = !isAlternateRow;
                                                isFirstInRow = !isFirstInRow;

                                                var alertContent = new AlertContent();
                                                alertContent.Alert = alert;
                                                alertContent.idList = property.idList;
                                                dc.AlertContents.InsertOnSubmit(alertContent);
                                            }
                                    #endregion

                                            if (!isFirstInRow) body.Append("</tr>");//in case the last row was incomplete, close it
                                            body.Append("</table>");
                                            body.AppendFormat(@"<br/><a href=""{0}"" target=""_blank"">Click here</a> to access your alerts management page and change your research criteria or alerts frequency settings.<br/>
                                                      You can also <a href=""{1}"" target=""_blank"">unsubscribe from this alert</a>,<br/>
                                                      or <a href=""{2}"" target=""_blank"">cancel all email alerts at once</a>.<br/>
                                                      <br/>",
                                            showUserPageURL,
                                            string.Format("http://api.4pm.ie/alert.svc/{0}.{1}/unsubscribe", alertRequest.idRequest, key),
                                            string.Format("http://api.4pm.ie/alert.svc/{0}.{1}/unsubscribe", string.Join(",", relatedAlertRequestList.Select(c => c.ToString()).ToArray()), key)
                                            );
                                            body.Append(template.footer);
                                            body.Append("</div>");

                                            var senderEmail = template.sender;
                                            try { new MailAddress(senderEmail); }
                                            catch (FormatException ex)
                                            {
                                                log.Error += "\n" + ex.ToString();
                                                senderEmail = defaultTemplate.sender;
                                            }

                                            smtpClient.Send(
                                                new MailMessage(
                                                    senderEmail,
                                                    alertRequest.Applicant.User.UserEmail,
                                                    Settings.Default.EmailSubject,
                                                    body.ToString())
                                                    {
                                                        IsBodyHtml = true
                                                    });
                                        }
                                        log.NbAlertsSent++;
                                    }

                                    if ((alertRequest.Applicant.AllowSMS ?? false) && propertiesToSend.Count() > 0)
                                    {
                                        //TODO
                                    }

                                    if ((alertRequest.Applicant.AllowMMS ?? false) && propertiesToSend.Count() > 0)
                                    {
                                        //TODO
                                    }
#endif
                                }
                                alertRequest.LastSend = now;
                                dc.SubmitChanges();
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        log.Error += "\n" + ex.ToString();
                    }
                    finally
                    {
                        if (!string.IsNullOrEmpty(log.Error))
                        {
                            Trace.TraceWarning(log.Error);
                        }
                        log.ExecutionTime = (decimal)DateTime.Now.Subtract(log.Date).TotalSeconds;
                        dc.AlertEngineLogs.InsertOnSubmit(log);
                        dc.SubmitChanges();
                    }
#if DEBUG
                    dc.Log.Close();
#endif
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.ToString());
            }
        }

        private double Compare(AlertRequest alertRequest, Property property)
        {
            var result = 0.0;
            var likeness = 0.0;

            if ((!(alertRequest.MaxPrice >= property.PriceVal || alertRequest.MaxPrice == 0 || property.PriceVal == 0 || LikenessPercentage((double)alertRequest.MaxPrice, (double)property.PriceVal) > Settings.Default.MaxPriceK))
                ||
                (!(alertRequest.MinPrice <= property.PriceVal || alertRequest.MinPrice == 0 || property.PriceVal == 0 || LikenessPercentage((double)alertRequest.MinPrice, (double)property.PriceVal) > Settings.Default.MinPriceK)))
            {
                result = 1;//return 0;
            }
            else
            {

                if ((alertRequest.idDistrict ?? 0) != 0 && property.idDistrict != 0)
                {
                    result += Settings.Default.DistrictK;
                    likeness += Settings.Default.DistrictK;
                    if (alertRequest.idDistrict != property.idDistrict)
                        likeness -= Settings.Default.DistrictK;
                }
                if ((alertRequest.idDistrictGroup ?? 0) != 0)
                {
                    result += Settings.Default.DistrictGroupK;
                    likeness += Settings.Default.DistrictGroupK;
                    if (property.District.DistrictGroupLists.FirstOrDefault(c => c.idDistrictGroup == alertRequest.idDistrictGroup) == null)
                        likeness -= Settings.Default.DistrictGroupK;
                }
                if ((alertRequest.NumberOfBeds ?? 0) != 0)
                {
                    result += Settings.Default.NumberOfBedsK;
                    likeness += Settings.Default.NumberOfBedsK;
                    if (Math.Abs((property.BedroomLists.FirstOrDefault() ?? emptyBedroomList).TotalBedspaces - alertRequest.NumberOfBeds ?? 0) > 1)
                        likeness -= Settings.Default.NumberOfBedsK;
                }
                if ((alertRequest.idLivingType ?? 0) != 0)
                {
                    result += Settings.Default.LivingTypeK;
                    likeness += Settings.Default.LivingTypeK;
                    if (alertRequest.idLivingType != property.idLType)
                        likeness -= Settings.Default.LivingTypeK;
                }
                if (!string.IsNullOrEmpty(alertRequest.PostCode))
                {
                    result += Settings.Default.PostCodeK;
                    likeness += Settings.Default.PostCodeK;
                    if (!(property.Postcode ?? string.Empty).Trim().StartsWith(alertRequest.PostCode.Trim(), StringComparison.InvariantCultureIgnoreCase))
                        likeness -= Settings.Default.PostCodeK;
                }
                if ((alertRequest.idLetCategory ?? 0) != 0)
                {
                    result += Settings.Default.LetCategoryK;
                    likeness += Settings.Default.LetCategoryK;
                    if ((property.LetCategoryLists.Count == 0 && property.idPropMarket == 2 && alertRequest.idLetCategory != 4) ||
                        (property.LetCategoryLists.FirstOrDefault(c => c.idLetCategory == alertRequest.idLetCategory) == null)
                        )
                        likeness -= Settings.Default.LetCategoryK;
                }
                if (result == 0)
                {
                    likeness = result = 1;
                }
                if (property.PriceVal == 0)
                {
                    likeness -= Settings.Default.PriceOnRequestPenalty;
                }
            }

            return likeness / result;
        }

        private double LikenessPercentage(double val1, double val2)
        {
            var likeness = 1 - (Math.Abs(val1 - val2) / val1);
            return (likeness > 0 ? likeness : 0);
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值