Bladeren bron

广告支持按地区投放

懒得勤快 4 jaren geleden
bovenliggende
commit
5a920209aa
24 gewijzigde bestanden met toevoegingen van 212 en 106 verwijderingen
  1. 43 9
      src/Masuit.MyBlogs.Core/Common/CommonHelper.cs
  2. 2 6
      src/Masuit.MyBlogs.Core/Common/HttpContextExtension.cs
  3. 7 0
      src/Masuit.MyBlogs.Core/Controllers/AdvertisementController.cs
  4. 2 2
      src/Masuit.MyBlogs.Core/Controllers/ErrorController.cs
  5. 28 28
      src/Masuit.MyBlogs.Core/Controllers/HomeController.cs
  6. 1 1
      src/Masuit.MyBlogs.Core/Controllers/LinksController.cs
  7. 1 1
      src/Masuit.MyBlogs.Core/Controllers/MiscController.cs
  8. 2 2
      src/Masuit.MyBlogs.Core/Controllers/NoticeController.cs
  9. 15 15
      src/Masuit.MyBlogs.Core/Controllers/PostController.cs
  10. 7 7
      src/Masuit.MyBlogs.Core/Controllers/SearchController.cs
  11. 1 1
      src/Masuit.MyBlogs.Core/Controllers/SeminarController.cs
  12. 7 7
      src/Masuit.MyBlogs.Core/Controllers/SubscribeController.cs
  13. 1 1
      src/Masuit.MyBlogs.Core/Extensions/TranslateMiddleware.cs
  14. 5 3
      src/Masuit.MyBlogs.Core/Infrastructure/Services/AdvertisementService.cs
  15. 3 6
      src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IAdvertisementService.cs
  16. 1 1
      src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj
  17. 12 1
      src/Masuit.MyBlogs.Core/Models/DTO/AdvertisementDto.cs
  18. 1 1
      src/Masuit.MyBlogs.Core/Models/DTO/PostCommand.cs
  19. 1 1
      src/Masuit.MyBlogs.Core/Models/DTO/PostDto.cs
  20. 10 0
      src/Masuit.MyBlogs.Core/Models/Entity/Advertisement.cs
  21. 2 2
      src/Masuit.MyBlogs.Core/Models/Entity/Post.cs
  22. 10 0
      src/Masuit.MyBlogs.Core/Models/ViewModel/AdvertisementViewModel.cs
  23. 11 0
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/partner.js
  24. 39 11
      src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/partner.html

+ 43 - 9
src/Masuit.MyBlogs.Core/Common/CommonHelper.cs

@@ -125,7 +125,7 @@ namespace Masuit.MyBlogs.Core.Common
             {
                 foreach (var item in ips.Split(','))
                 {
-                    var pos = GetIPLocation(item);
+                    string pos = GetIPLocation(IPAddress.Parse(item));
                     return pos.Contains(denyAreas) || denyAreas.Intersect(pos.Split("|")).Any();
                 }
             }
@@ -154,17 +154,16 @@ namespace Masuit.MyBlogs.Core.Common
 
         public static string GetIPLocation(this string ips)
         {
-            var (location, network) = GetIPLocation(IPAddress.Parse(ips));
-            return location + "|" + network;
+            return GetIPLocation(IPAddress.Parse(ips));
         }
 
-        public static (string location, string network) GetIPLocation(this IPAddress ip)
+        public static IPLocation GetIPLocation(this IPAddress ip)
         {
             switch (ip.AddressFamily)
             {
                 case AddressFamily.InterNetwork when ip.IsPrivateIP():
                 case AddressFamily.InterNetworkV6 when ip.IsPrivateIP():
-                    return ("内网", "内网IP");
+                    return new IPLocation("内网", null, null, "内网IP", null);
                 case AddressFamily.InterNetworkV6 when ip.IsIPv4MappedToIPv6:
                     ip = ip.MapToIPv4();
                     goto case AddressFamily.InterNetwork;
@@ -173,16 +172,15 @@ namespace Masuit.MyBlogs.Core.Common
                     if (parts != null)
                     {
                         var asn = GetIPAsn(ip);
-                        var network = parts[^1] == "0" ? asn.AutonomousSystemOrganization : parts[^1];
-                        var location = parts[..^1].Where(s => s != "0").Distinct().Join("");
-                        return (location, network + $"(AS{asn.AutonomousSystemNumber})");
+                        var network = parts[^1] == "0" ? asn.AutonomousSystemOrganization : parts[^1] + "(" + asn.AutonomousSystemOrganization + ")";
+                        return new IPLocation(parts[0], parts[2], parts[3], network, asn.AutonomousSystemNumber);
                     }
 
                     goto default;
                 default:
                     var cityResp = Policy<CityResponse>.Handle<AddressNotFoundException>().Fallback(new CityResponse()).Execute(() => MaxmindReader.City(ip));
                     var asnResp = GetIPAsn(ip);
-                    return (cityResp.Country.Names.GetValueOrDefault("zh-CN") + cityResp.City.Names.GetValueOrDefault("zh-CN"), asnResp.AutonomousSystemOrganization + $"(AS{asnResp.AutonomousSystemNumber})");
+                    return new IPLocation(cityResp.Country.Names.GetValueOrDefault("zh-CN"), null, cityResp.City.Names.GetValueOrDefault("zh-CN"), asnResp.AutonomousSystemOrganization, asnResp.AutonomousSystemNumber);
             }
         }
 
@@ -354,4 +352,40 @@ namespace Masuit.MyBlogs.Core.Common
             return ToTimeZone(time, zone).ToString(format);
         }
     }
+
+    public class IPLocation
+    {
+        public IPLocation(string country, string province, string city, string isp, long? asn)
+        {
+            Country = country?.Trim('0');
+            Province = province?.Trim('0');
+            City = city?.Trim('0');
+            ISP = isp;
+            ASN = asn;
+        }
+
+        public string Country { get; set; }
+        public string Province { get; set; }
+        public string City { get; set; }
+        public string ISP { get; set; }
+        public long? ASN { get; set; }
+        public string Location => Country + Province + City;
+        public string Network => ASN.HasValue ? ISP + "(AS" + ASN + ")" : ISP;
+
+        public string[] ToArray()
+        {
+            return new[] { Country, Province, City, ISP, ASN + "" }.Where(s => !string.IsNullOrEmpty(s) && s != "0").ToArray();
+        }
+
+        public override string ToString()
+        {
+            return Location + "|" + Network;
+        }
+
+        public static implicit operator string(IPLocation entry)
+        {
+            return entry.ToString();
+        }
+
+    }
 }

+ 2 - 6
src/Masuit.MyBlogs.Core/Common/HttpContextExtension.cs

@@ -11,13 +11,9 @@ namespace Masuit.MyBlogs.Core.Common
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
-        public static string Location(this HttpRequest request)
+        public static IPLocation Location(this HttpRequest request)
         {
-            return (string)request.HttpContext.Items.GetOrAdd("ip.location", () =>
-            {
-                var (location, network) = request.HttpContext.Connection.RemoteIpAddress.GetIPLocation();
-                return location + "|" + network;
-            });
+            return (IPLocation)request.HttpContext.Items.GetOrAdd("ip.location", () => request.HttpContext.Connection.RemoteIpAddress.GetIPLocation());
         }
 
         /// <summary>

+ 7 - 0
src/Masuit.MyBlogs.Core/Controllers/AdvertisementController.cs

@@ -15,6 +15,7 @@ using System;
 using System.ComponentModel.DataAnnotations;
 using System.Linq;
 using System.Linq.Expressions;
+using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 
 namespace Masuit.MyBlogs.Core.Controllers
@@ -74,6 +75,12 @@ namespace Masuit.MyBlogs.Core.Controllers
         public async Task<IActionResult> Save(AdvertisementDto model)
         {
             model.CategoryIds = model.CategoryIds?.Replace("null", "");
+            model.Regions = Regex.Replace(model.Regions ?? "", @"(\p{P}|\p{Z}|\p{S})+", "|");
+            if (model.RegionMode == RegionLimitMode.All)
+            {
+                model.Regions = null;
+            }
+
             if (model.Types.Contains(AdvertiseType.Banner.ToString("D")) && string.IsNullOrEmpty(model.ImageUrl))
             {
                 return ResultData(null, false, "宣传大图不能为空");

+ 2 - 2
src/Masuit.MyBlogs.Core/Controllers/ErrorController.cs

@@ -90,9 +90,9 @@ namespace Masuit.MyBlogs.Core.Controllers
                     case NotFoundException:
                         return RedirectToAction("Index");
                     case AccessDenyException:
-                        var (location, network) = ip.GetIPLocation();
+                        var entry = ip.GetIPLocation();
                         var tips = Template.Create(CommonHelper.SystemSettings.GetOrAdd("AccessDenyTips", @"<h4>遇到了什么问题?</h4>
-                <h4>基于主观因素考虑,您所在的地区暂时不允许访问本站,如有疑问,请联系站长!或者请联系站长开通本站的访问权限!</h4>")).Set("clientip", ip.ToString()).Set(nameof(location), location).Set(nameof(network), network).Render();
+                <h4>基于主观因素考虑,您所在的地区暂时不允许访问本站,如有疑问,请联系站长!或者请联系站长开通本站的访问权限!</h4>")).Set("clientip", ip.ToString()).Set("location", entry.Location).Set("network", entry.Network).Render();
                         Response.StatusCode = 403;
                         return View("AccessDeny", tips);
                     case TempDenyException:

+ 28 - 28
src/Masuit.MyBlogs.Core/Controllers/HomeController.cs

@@ -12,7 +12,6 @@ using Masuit.MyBlogs.Core.Models.ViewModel;
 using Masuit.Tools;
 using Masuit.Tools.Core.Net;
 using Masuit.Tools.Linq;
-using Masuit.Tools.Models;
 using Masuit.Tools.Systems;
 using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
@@ -55,19 +54,19 @@ namespace Masuit.MyBlogs.Core.Controllers
         [HttpGet, ResponseCache(Duration = 600, VaryByHeader = "Cookie", Location = ResponseCacheLocation.Any)]
         public async Task<ActionResult> Index([FromServices] IFastShareService fastShareService)
         {
-            var banners = AdsService.GetsByWeightedPrice(8, AdvertiseType.Banner).OrderBy(a => Guid.NewGuid()).ToList();
+            var banners = AdsService.GetsByWeightedPrice(8, AdvertiseType.Banner, Request.Location()).OrderBy(a => Guid.NewGuid()).ToList();
             var fastShares = await fastShareService.GetAllFromCacheAsync(s => s.Sort);
             var postsQuery = PostService.GetQuery(p => p.Status == Status.Published); //准备文章的查询
             var posts = await postsQuery.Where(p => !p.IsFixedTop).OrderBy(OrderBy.ModifyDate.GetDisplay() + " desc").ToCachedPagedListAsync<Post, PostDto>(1, 15, MapperConfig);
             posts.Data.InsertRange(0, postsQuery.Where(p => p.IsFixedTop).OrderByDescending(p => p.ModifyDate).ProjectTo<PostDto>(MapperConfig).ToList());
-            CheckPermission(posts);
+            CheckPermission(posts.Data);
             var viewModel = await GetIndexPageViewModel();
             viewModel.Banner = banners;
             viewModel.Posts = posts;
             ViewBag.FastShare = fastShares;
             viewModel.PageParams = new Pagination(1, 15, posts.TotalCount, OrderBy.ModifyDate);
-            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar);
-            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList);
+            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
+            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList, Request.Location());
             return View(viewModel);
         }
 
@@ -89,11 +88,11 @@ namespace Masuit.MyBlogs.Core.Controllers
                 posts.Data.InsertRange(0, postsQuery.Where(p => p.IsFixedTop).OrderByDescending(p => p.ModifyDate).ProjectTo<PostDto>(MapperConfig).ToList());
             }
 
-            CheckPermission(posts);
+            CheckPermission(posts.Data);
             viewModel.Posts = posts;
             viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
-            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar);
-            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList);
+            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
+            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList, Request.Location());
             return View(viewModel);
         }
 
@@ -109,13 +108,13 @@ namespace Masuit.MyBlogs.Core.Controllers
         public async Task<ActionResult> Tag(string id, [Optional] OrderBy? orderBy, [Range(1, int.MaxValue, ErrorMessage = "页码必须大于0")] int page = 1, [Range(1, 50, ErrorMessage = "页大小必须在0到50之间")] int size = 15)
         {
             var posts = await PostService.GetQuery(p => p.Label.Contains(id) && p.Status == Status.Published).OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToCachedPagedListAsync<Post, PostDto>(page, size, MapperConfig);
-            CheckPermission(posts);
+            CheckPermission(posts.Data);
             var viewModel = await GetIndexPageViewModel();
             ViewBag.Tag = id;
             viewModel.Posts = posts;
             viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
-            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar);
-            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList);
+            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
+            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList, Request.Location());
             return View(viewModel);
         }
 
@@ -133,14 +132,14 @@ namespace Masuit.MyBlogs.Core.Controllers
             Expression<Func<Post, bool>> where = p => p.Author.Equals(author) || p.Modifier.Equals(author) || p.Email.Equals(author) || p.PostHistoryVersion.Any(v => v.Modifier.Equals(author) || v.ModifierEmail.Equals(author));
             where = where.And(p => p.Status == Status.Published);
             var posts = await PostService.GetQuery(where).OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToCachedPagedListAsync<Post, PostDto>(page, size, MapperConfig);
-            CheckPermission(posts);
+            CheckPermission(posts.Data);
             var viewModel = await GetIndexPageViewModel();
             ViewBag.Author = author;
             ViewBag.Total = posts.TotalCount;
             viewModel.Posts = posts;
             viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
-            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar);
-            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList);
+            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location());
+            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList, Request.Location());
             return View(viewModel);
         }
 
@@ -157,13 +156,13 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             var cat = await CategoryService.GetByIdAsync(id) ?? throw new NotFoundException("文章分类未找到");
             var posts = await PostService.GetQuery(p => p.CategoryId == cat.Id && p.Status == Status.Published).OrderBy($"{nameof(PostDto.IsFixedTop)} desc,{(orderBy ?? OrderBy.ModifyDate).GetDisplay()} desc").ToCachedPagedListAsync<Post, PostDto>(page, size, MapperConfig);
-            CheckPermission(posts);
+            CheckPermission(posts.Data);
             var viewModel = await GetIndexPageViewModel();
             viewModel.Posts = posts;
             ViewBag.Category = cat;
             viewModel.PageParams = new Pagination(page, size, posts.TotalCount, orderBy);
-            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, id);
-            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList, id);
+            viewModel.SidebarAds = AdsService.GetsByWeightedPrice(2, AdvertiseType.SideBar, Request.Location(), id);
+            viewModel.ListAdvertisement = AdsService.GetByWeightedPrice(AdvertiseType.PostList, Request.Location(), id);
             return View(viewModel);
         }
 
@@ -178,36 +177,36 @@ namespace Masuit.MyBlogs.Core.Controllers
             return Redirect(string.IsNullOrEmpty(referer) ? "/" : referer);
         }
 
-        private void CheckPermission(PagedList<PostDto> posts)
+        private void CheckPermission(List<PostDto> posts)
         {
             var location = Request.Location() + "|" + Request.Headers[HeaderNames.UserAgent];
-            posts.Data.RemoveAll(p =>
+            posts.RemoveAll(p =>
             {
                 switch (p.LimitMode)
                 {
-                    case PostLimitMode.AllowRegion:
+                    case RegionLimitMode.AllowRegion:
                         return !location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid && !Request.IsRobot();
-                    case PostLimitMode.ForbidRegion:
+                    case RegionLimitMode.ForbidRegion:
                         return location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid && !Request.IsRobot();
-                    case PostLimitMode.AllowRegionExceptForbidRegion:
+                    case RegionLimitMode.AllowRegionExceptForbidRegion:
                         if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid)
                         {
                             return true;
                         }
 
-                        goto case PostLimitMode.AllowRegion;
-                    case PostLimitMode.ForbidRegionExceptAllowRegion:
+                        goto case RegionLimitMode.AllowRegion;
+                    case RegionLimitMode.ForbidRegionExceptAllowRegion:
                         if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid)
                         {
                             return false;
                         }
 
-                        goto case PostLimitMode.ForbidRegion;
+                        goto case RegionLimitMode.ForbidRegion;
                     default:
                         return false;
                 }
             });
-            foreach (var item in posts.Data)
+            foreach (var item in posts)
             {
                 item.PostDate = item.PostDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
                 item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
@@ -229,7 +228,8 @@ namespace Masuit.MyBlogs.Core.Controllers
                 1 => nameof(OrderBy.VoteUpCount),
                 2 => nameof(OrderBy.AverageViewCount),
                 _ => nameof(OrderBy.TotalViewCount)
-            } + " desc").Skip(0).Take(5).Cacheable(); //热门文章
+            } + " desc").Skip(0).Take(5).Cacheable().ToList(); //热门文章
+            CheckPermission(hot6Post);
             var newdic = new Dictionary<string, int>(); //标签云最终结果
             var tagdic = postsQuery.Where(p => !string.IsNullOrEmpty(p.Label)).Select(p => p.Label).Distinct().Cacheable().ToList().SelectMany(s => s.Split(',', ',')).GroupBy(s => s).ToDictionary(g => g.Key, g => g.Count()); //统计标签
 
@@ -249,7 +249,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 HotSearch = hotSearches,
                 Notices = notices.Data,
                 Tags = newdic,
-                Top6Post = hot6Post.ToList(),
+                Top6Post = hot6Post,
                 PostsQueryable = postsQuery
             };
         }

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/LinksController.cs

@@ -33,7 +33,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             var list = await LinksService.GetQueryFromCacheAsync<bool, LinksDto>(l => l.Status == Status.Available, l => l.Recommend, false);
             ViewBag.Html = await System.IO.File.ReadAllTextAsync(Path.Combine(hostEnvironment.WebRootPath, "template", "links.html"));
-            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage);
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, Request.Location());
             return CurrentUser.IsAdmin ? View("Index_Admin", list) : View(list);
         }
 

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/MiscController.cs

@@ -51,7 +51,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         [Route("donate")]
         public async Task<ActionResult> Donate()
         {
-            ViewBag.Ads = AdsService.GetsByWeightedPrice(2, AdvertiseType.InPage);
+            ViewBag.Ads = AdsService.GetsByWeightedPrice(2, AdvertiseType.InPage, Request.Location());
             var text = await System.IO.File.ReadAllTextAsync(Path.Combine(HostEnvironment.WebRootPath, "template", "donate.html"));
             return CurrentUser.IsAdmin ? View("Donate_Admin", text) : View(model: text);
         }

+ 2 - 2
src/Masuit.MyBlogs.Core/Controllers/NoticeController.cs

@@ -45,7 +45,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 n.Content = ReplaceVariables(n.Content);
             }
 
-            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.PostList);
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.PostList, Request.Location());
             return CurrentUser.IsAdmin ? View("Index_Admin", list.Data) : View(list.Data);
         }
 
@@ -68,7 +68,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             notice.ModifyDate = notice.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
             notice.PostDate = notice.PostDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
             notice.Content = ReplaceVariables(notice.Content);
-            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage);
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, Request.Location());
             return View(notice);
         }
 

+ 15 - 15
src/Masuit.MyBlogs.Core/Controllers/PostController.cs

@@ -78,7 +78,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 ViewData["keywords"] = post.Content.Contains(kw) ? $"['{kw}']" : SearchEngine.LuceneIndexSearcher.CutKeywords(kw).ToJsonString();
             }
 
-            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, post.CategoryId);
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, Request.Location(), post.CategoryId);
             var related = PostService.ScoreSearch(1, 11, string.IsNullOrWhiteSpace(post.Keyword + post.Label) ? post.Title : post.Keyword + post.Label);
             related.RemoveAll(p => p.Id == id);
             if (related.Count <= 1)
@@ -110,34 +110,34 @@ namespace Masuit.MyBlogs.Core.Controllers
             var location = Request.Location() + "|" + Request.Headers[HeaderNames.UserAgent];
             switch (post.LimitMode)
             {
-                case PostLimitMode.AllowRegion:
+                case RegionLimitMode.AllowRegion:
                     if (!location.Contains(post.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid && !Request.IsRobot())
                     {
                         Disallow(post);
                     }
 
                     break;
-                case PostLimitMode.ForbidRegion:
+                case RegionLimitMode.ForbidRegion:
                     if (location.Contains(post.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid && !Request.IsRobot())
                     {
                         Disallow(post);
                     }
 
                     break;
-                case PostLimitMode.AllowRegionExceptForbidRegion:
+                case RegionLimitMode.AllowRegionExceptForbidRegion:
                     if (location.Contains(post.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid)
                     {
                         Disallow(post);
                     }
 
-                    goto case PostLimitMode.AllowRegion;
-                case PostLimitMode.ForbidRegionExceptAllowRegion:
+                    goto case RegionLimitMode.AllowRegion;
+                case RegionLimitMode.ForbidRegionExceptAllowRegion:
                     if (location.Contains(post.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid)
                     {
                         break;
                     }
 
-                    goto case PostLimitMode.ForbidRegion;
+                    goto case RegionLimitMode.ForbidRegion;
             }
         }
 
@@ -175,7 +175,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 item.ModifyDate = item.ModifyDate.ToTimeZone(HttpContext.Session.Get<string>(SessionKey.TimeZone));
             }
 
-            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, post.CategoryId);
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, Request.Location(), post.CategoryId);
             return View(list);
         }
 
@@ -196,7 +196,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             var prev = await PostHistoryVersionService.GetAsync(p => p.PostId == id && p.ModifyDate < post.ModifyDate, p => p.ModifyDate, false);
             ViewBag.Next = next;
             ViewBag.Prev = prev;
-            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, post.CategoryId);
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.InPage, Request.Location(), post.CategoryId);
             return CurrentUser.IsAdmin ? View("HistoryVersion_Admin", post) : View(post);
         }
 
@@ -220,7 +220,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             var diffOutput = diff.Build();
             right.Content = ReplaceVariables(Regex.Replace(Regex.Replace(diffOutput, "<ins.+?</ins>", string.Empty), @"<\w+></\w+>", string.Empty));
             left.Content = ReplaceVariables(Regex.Replace(Regex.Replace(diffOutput, "<del.+?</del>", string.Empty), @"<\w+></\w+>", string.Empty));
-            ViewBag.Ads = AdsService.GetsByWeightedPrice(2, AdvertiseType.InPage, main.CategoryId);
+            ViewBag.Ads = AdsService.GetsByWeightedPrice(2, AdvertiseType.InPage, Request.Location(), main.CategoryId);
             ViewBag.DisableCopy = post.DisableCopy;
             return View(new[] { main, left, right });
         }
@@ -834,8 +834,8 @@ namespace Masuit.MyBlogs.Core.Controllers
 
             switch (post.LimitMode)
             {
-                case PostLimitMode.AllowRegion:
-                case PostLimitMode.ForbidRegion:
+                case RegionLimitMode.AllowRegion:
+                case RegionLimitMode.ForbidRegion:
                     if (string.IsNullOrEmpty(post.Regions))
                     {
                         resultData = ResultData(null, false, "请输入限制的地区");
@@ -843,15 +843,15 @@ namespace Masuit.MyBlogs.Core.Controllers
                     }
 
                     break;
-                case PostLimitMode.AllowRegionExceptForbidRegion:
-                case PostLimitMode.ForbidRegionExceptAllowRegion:
+                case RegionLimitMode.AllowRegionExceptForbidRegion:
+                case RegionLimitMode.ForbidRegionExceptAllowRegion:
                     if (string.IsNullOrEmpty(post.ExceptRegions))
                     {
                         resultData = ResultData(null, false, "请输入排除的地区");
                         return false;
                     }
 
-                    goto case PostLimitMode.AllowRegion;
+                    goto case RegionLimitMode.AllowRegion;
             }
 
             if (string.IsNullOrEmpty(post.Label?.Trim()) || post.Label.Equals("null"))

+ 7 - 7
src/Masuit.MyBlogs.Core/Controllers/SearchController.cs

@@ -78,7 +78,7 @@ namespace Masuit.MyBlogs.Core.Controllers
                 {
                     CacheManager.AddOrUpdate(key, wd, s => wd);
                     CacheManager.Expire(key, TimeSpan.FromSeconds(10));
-                    ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.PostList);
+                    ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.PostList, Request.Location());
                 }
 
                 ViewBag.hotSearches = new List<KeywordsRank>();
@@ -96,24 +96,24 @@ namespace Masuit.MyBlogs.Core.Controllers
             {
                 switch (p.LimitMode)
                 {
-                    case PostLimitMode.AllowRegion:
+                    case RegionLimitMode.AllowRegion:
                         return !location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid && !Request.IsRobot();
-                    case PostLimitMode.ForbidRegion:
+                    case RegionLimitMode.ForbidRegion:
                         return location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid && !Request.IsRobot();
-                    case PostLimitMode.AllowRegionExceptForbidRegion:
+                    case RegionLimitMode.AllowRegionExceptForbidRegion:
                         if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid)
                         {
                             return true;
                         }
 
-                        goto case PostLimitMode.AllowRegion;
-                    case PostLimitMode.ForbidRegionExceptAllowRegion:
+                        goto case RegionLimitMode.AllowRegion;
+                    case RegionLimitMode.ForbidRegionExceptAllowRegion:
                         if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !CurrentUser.IsAdmin && !VisitorTokenValid)
                         {
                             return false;
                         }
 
-                        goto case PostLimitMode.ForbidRegion;
+                        goto case RegionLimitMode.ForbidRegion;
                     default:
                         return false;
                 }

+ 1 - 1
src/Masuit.MyBlogs.Core/Controllers/SeminarController.cs

@@ -48,7 +48,7 @@ namespace Masuit.MyBlogs.Core.Controllers
             ViewBag.Title = s.Title;
             ViewBag.Desc = s.Description;
             ViewBag.SubTitle = s.SubTitle;
-            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.PostList);
+            ViewBag.Ads = AdsService.GetByWeightedPrice(AdvertiseType.PostList, Request.Location());
             ViewData["page"] = new Pagination(page, size, posts.TotalCount, orderBy);
             return View(posts);
         }

+ 7 - 7
src/Masuit.MyBlogs.Core/Controllers/SubscribeController.cs

@@ -83,7 +83,7 @@ namespace Masuit.MyBlogs.Core.Controllers
         {
             if (posts.Count > 2)
             {
-                var ad = AdvertisementService.GetByWeightedPrice((AdvertiseType)(DateTime.Now.Second % 4 + 1), cid);
+                var ad = AdvertisementService.GetByWeightedPrice((AdvertiseType)(DateTime.Now.Second % 4 + 1), Request.Location(), cid);
                 if (ad is not null)
                 {
                     posts.Insert(new Random().Next(1, posts.Count), new Item()
@@ -211,24 +211,24 @@ namespace Masuit.MyBlogs.Core.Controllers
             {
                 switch (p.LimitMode)
                 {
-                    case PostLimitMode.AllowRegion:
+                    case RegionLimitMode.AllowRegion:
                         return !location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot();
-                    case PostLimitMode.ForbidRegion:
+                    case RegionLimitMode.ForbidRegion:
                         return location.Contains(p.Regions.Split(',', StringSplitOptions.RemoveEmptyEntries)) && !Request.IsRobot();
-                    case PostLimitMode.AllowRegionExceptForbidRegion:
+                    case RegionLimitMode.AllowRegionExceptForbidRegion:
                         if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)))
                         {
                             return true;
                         }
 
-                        goto case PostLimitMode.AllowRegion;
-                    case PostLimitMode.ForbidRegionExceptAllowRegion:
+                        goto case RegionLimitMode.AllowRegion;
+                    case RegionLimitMode.ForbidRegionExceptAllowRegion:
                         if (location.Contains(p.ExceptRegions.Split(',', StringSplitOptions.RemoveEmptyEntries)))
                         {
                             return false;
                         }
 
-                        goto case PostLimitMode.ForbidRegion;
+                        goto case RegionLimitMode.ForbidRegion;
                     default:
                         return false;
                 }

+ 1 - 1
src/Masuit.MyBlogs.Core/Extensions/TranslateMiddleware.cs

@@ -37,7 +37,7 @@ namespace Masuit.MyBlogs.Core.Extensions
             lang ??= context.Request.Cookies["lang"];
             if (string.IsNullOrEmpty(lang))
             {
-                if (context.Request.Location().Contains(new[] { "台湾", "香港", "澳门", "Taiwan", "TW", "HongKong", "HK" }))
+                if (context.Request.Location().Location.Contains(new[] { "台湾", "香港", "澳门", "Taiwan", "TW", "HongKong", "HK" }))
                 {
                     return Traditional(context);
                 }

+ 5 - 3
src/Masuit.MyBlogs.Core/Infrastructure/Services/AdvertisementService.cs

@@ -9,6 +9,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq.Expressions;
+using System.Text.RegularExpressions;
 
 namespace Masuit.MyBlogs.Core.Infrastructure.Services
 {
@@ -26,9 +27,9 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
         /// <param name="type">广告类型</param>
         /// <param name="cid">分类id</param>
         /// <returns></returns>
-        public Advertisement GetByWeightedPrice(AdvertiseType type, int? cid = null)
+        public Advertisement GetByWeightedPrice(AdvertiseType type, string location, int? cid = null)
         {
-            return GetsByWeightedPrice(1, type, cid).FirstOrDefault();
+            return GetsByWeightedPrice(1, type, location, cid).FirstOrDefault();
         }
 
         /// <summary>
@@ -38,12 +39,13 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services
         /// <param name="type">广告类型</param>
         /// <param name="cid">分类id</param>
         /// <returns></returns>
-        public List<Advertisement> GetsByWeightedPrice(int count, AdvertiseType type, int? cid = null)
+        public List<Advertisement> GetsByWeightedPrice(int count, AdvertiseType type, string location, int? cid = null)
         {
             return CacheManager.GetOrAdd($"{type}:{count}-{cid}", _ =>
             {
                 var atype = type.ToString("D");
                 Expression<Func<Advertisement, bool>> where = a => a.Types.Contains(atype) && a.Status == Status.Available;
+                where = where.And(a => a.RegionMode == RegionLimitMode.All || (a.RegionMode == RegionLimitMode.AllowRegion ? Regex.IsMatch(location, a.Regions) : !Regex.IsMatch(location, a.Regions)));
                 if (cid.HasValue)
                 {
                     var scid = cid.ToString();

+ 3 - 6
src/Masuit.MyBlogs.Core/Infrastructure/Services/Interface/IAdvertisementService.cs

@@ -1,5 +1,4 @@
-using CacheManager.Core;
-using Masuit.MyBlogs.Core.Models.Entity;
+using Masuit.MyBlogs.Core.Models.Entity;
 using Masuit.MyBlogs.Core.Models.Enum;
 using System.Collections.Generic;
 
@@ -7,15 +6,13 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
 {
     public partial interface IAdvertisementService : IBaseService<Advertisement>
     {
-        ICacheManager<List<Advertisement>> CacheManager { get; set; }
-
         /// <summary>
         /// 按价格随机筛选一个元素
         /// </summary>
         /// <param name="type">广告类型</param>
         /// <param name="cid">分类id</param>
         /// <returns></returns>
-        Advertisement GetByWeightedPrice(AdvertiseType type, int? cid = null);
+        Advertisement GetByWeightedPrice(AdvertiseType type, string location, int? cid = null);
 
         /// <summary>
         /// 按价格随机筛选多个元素
@@ -24,6 +21,6 @@ namespace Masuit.MyBlogs.Core.Infrastructure.Services.Interface
         /// <param name="type">广告类型</param>
         /// <param name="cid">分类id</param>
         /// <returns></returns>
-        List<Advertisement> GetsByWeightedPrice(int count, AdvertiseType type, int? cid = null);
+        List<Advertisement> GetsByWeightedPrice(int count, AdvertiseType type, string location, int? cid = null);
     }
 }

+ 1 - 1
src/Masuit.MyBlogs.Core/Masuit.MyBlogs.Core.csproj

@@ -33,7 +33,7 @@
         <PackageReference Include="CacheManager.StackExchange.Redis" Version="1.2.0" />
         <PackageReference Include="CHTCHSConv" Version="1.0.0" />
         <PackageReference Include="CSRedisCore" Version="3.6.6" />
-        <PackageReference Include="EFCoreSecondLevelCacheInterceptor" Version="3.1.1" />
+        <PackageReference Include="EFCoreSecondLevelCacheInterceptor" Version="3.1.2" />
         <PackageReference Include="Hangfire" Version="1.7.24" />
         <PackageReference Include="Hangfire.Autofac" Version="2.3.1" />
         <PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" />

+ 12 - 1
src/Masuit.MyBlogs.Core/Models/DTO/AdvertisementDto.cs

@@ -1,4 +1,5 @@
-using System;
+using Masuit.MyBlogs.Core.Models.Entity;
+using System;
 using System.ComponentModel.DataAnnotations;
 
 namespace Masuit.MyBlogs.Core.Models.DTO
@@ -55,5 +56,15 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// 到期时间
         /// </summary>
         public DateTime? ExpireTime { get; set; }
+
+        /// <summary>
+        /// 地区模式
+        /// </summary>
+        public RegionLimitMode RegionMode { get; set; }
+
+        /// <summary>
+        /// 地区,逗号或竖线分隔
+        /// </summary>
+        public string Regions { get; set; }
     }
 }

+ 1 - 1
src/Masuit.MyBlogs.Core/Models/DTO/PostCommand.cs

@@ -85,7 +85,7 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// <summary>
         /// 限制模式
         /// </summary>
-        public PostLimitMode? LimitMode { get; set; }
+        public RegionLimitMode? LimitMode { get; set; }
 
         /// <summary>
         /// 限制地区,逗号分隔

+ 1 - 1
src/Masuit.MyBlogs.Core/Models/DTO/PostDto.cs

@@ -131,7 +131,7 @@ namespace Masuit.MyBlogs.Core.Models.DTO
         /// <summary>
         /// 限制模式
         /// </summary>
-        public PostLimitMode? LimitMode { get; set; }
+        public RegionLimitMode? LimitMode { get; set; }
 
         /// <summary>
         /// 限制地区,逗号分隔

+ 10 - 0
src/Masuit.MyBlogs.Core/Models/Entity/Advertisement.cs

@@ -89,5 +89,15 @@ namespace Masuit.MyBlogs.Core.Models.Entity
         /// 到期时间
         /// </summary>
         public DateTime? ExpireTime { get; set; }
+
+        /// <summary>
+        /// 地区模式
+        /// </summary>
+        public RegionLimitMode RegionMode { get; set; }
+
+        /// <summary>
+        /// 地区,逗号或竖线分隔
+        /// </summary>
+        public string Regions { get; set; }
     }
 }

+ 2 - 2
src/Masuit.MyBlogs.Core/Models/Entity/Post.cs

@@ -149,7 +149,7 @@ namespace Masuit.MyBlogs.Core.Models.Entity
         /// <summary>
         /// 限制模式
         /// </summary>
-        public PostLimitMode? LimitMode { get; set; }
+        public RegionLimitMode? LimitMode { get; set; }
 
         /// <summary>
         /// 限制地区,逗号分隔
@@ -195,7 +195,7 @@ namespace Masuit.MyBlogs.Core.Models.Entity
     /// <summary>
     /// 地区限制
     /// </summary>
-    public enum PostLimitMode
+    public enum RegionLimitMode
     {
         [Description("不限")]
         All,

+ 10 - 0
src/Masuit.MyBlogs.Core/Models/ViewModel/AdvertisementViewModel.cs

@@ -87,5 +87,15 @@ namespace Masuit.MyBlogs.Core.Models.ViewModel
         /// 到期时间
         /// </summary>
         public DateTime? ExpireTime { get; set; }
+
+        /// <summary>
+        /// 地区模式
+        /// </summary>
+        public RegionLimitMode RegionMode { get; set; }
+
+        /// <summary>
+        /// 地区,逗号或竖线分隔
+        /// </summary>
+        public string Regions { get; set; }
     }
 }

+ 11 - 0
src/Masuit.MyBlogs.Core/wwwroot/ng-views/controllers/partner.js

@@ -31,11 +31,19 @@
 			self.data = res.data.Data;
 		});
 	}
+
     $('.ui.dropdown.types').dropdown({
 		onChange: function (value) {
 			$scope.partner.Types = value;
 		}
 	});
+
+    $('.ui.dropdown.region').dropdown({
+		onChange: function (value) {
+			$scope.partner.RegionMode = value;
+		}
+	});
+
     $scope.getCategory = function () {
 		$http.post("/category/getcategories", null).then(function (res) {
 			var data = res.data;
@@ -102,6 +110,7 @@
         $timeout(function () {
 		    $('.ui.dropdown.category').dropdown('clear');
 		    $('.ui.dropdown.types').dropdown('clear');
+		    $('.ui.dropdown.region').dropdown('clear');
 		}, 10);
 	}
 
@@ -114,8 +123,10 @@
         $timeout(function () {
 		    $('.ui.dropdown.category').dropdown('clear');
 		    $('.ui.dropdown.types').dropdown('clear');
+		    $('.ui.dropdown.region').dropdown('clear');
 			$('.ui.dropdown.category').dropdown('set selected', (item.CategoryIds+"").split(','));
 		    $('.ui.dropdown.types').dropdown('set selected', item.Types.split(','));
+			$('.ui.dropdown.region').dropdown('set selected', item.RegionMode);
 		}, 10);
 		layer.open({
 			type: 1,

+ 39 - 11
src/Masuit.MyBlogs.Core/wwwroot/ng-views/views/partner.html

@@ -175,19 +175,21 @@
                     </div>
                 </div>
             </div>
-            <div class="form-group">
-                <div class="input-group">
-                    <label for="desc" class="input-group-addon control-label">权重:</label>
-                    <div class="fg-line">
-                        <input type="number" class="form-control" id="weight" placeholder="权重值" ng-model="partner.Weight">
+            <div class="form-inline">
+                <div class="form-group">
+                    <div class="input-group">
+                        <label for="desc" class="input-group-addon control-label">权重:</label>
+                        <div class="fg-line">
+                            <input type="number" class="form-control" id="weight" placeholder="权重值" ng-model="partner.Weight">
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div class="form-group">
-                <div class="input-group">
-                    <label for="desc" class="input-group-addon control-label">当前竞价:</label>
-                    <div class="fg-line">
-                        <input type="number" class="form-control" id="price" placeholder="竞价" ng-model="partner.Price">
+                <div class="form-group">
+                    <div class="input-group">
+                        <label for="desc" class="input-group-addon control-label">当前竞价:</label>
+                        <div class="fg-line">
+                            <input type="number" class="form-control" id="price" placeholder="竞价" ng-model="partner.Price">
+                        </div>
                     </div>
                 </div>
             </div>
@@ -197,6 +199,24 @@
                     <input type="text" class="workinput wicon" id="timespan" ng-model="partner.ExpireTime" readonly="readonly" />
                 </div>
             </div>
+            <div class="form-group form-inline">
+                <div class="input-group">
+                    <label for="region" class="input-group-addon control-label">投放地区:</label>
+                    <div class="fg-line">
+                        <input type="text" class="form-control" id="region" placeholder="竖线或逗号分隔,支持国家、地区、城市、运营商、ASN" ng-model="partner.Regions" style="width: 460px">
+                        <div class="ui selection dropdown region" style="min-width: 60px">
+                            <input name="category" type="hidden">
+                            <i class="dropdown icon"></i>
+                            <div class="default text">不限</div>
+                            <div class="menu">
+                                <div class="item" data-value="0">不限</div>
+                                <div class="item" data-value="1">以内</div>
+                                <div class="item" data-value="2">以外</div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
             <div class="form-group">
                 <div class="col-xs-12">
                     <div class="fg-line">
@@ -278,6 +298,14 @@
                     <td>更新时间:</td>
                     <td>{{partner.UpdateTime|date:'yyyy-MM-dd HH:mm:ss'}}</td>
                 </tr>
+                <tr>
+                    <td>投放范围</td>
+                    <td colspan="3">
+                        <p ng-if="partner.RegionMode==0">不限</p>
+                        <p ng-if="partner.RegionMode==1">{{partner.Regions}}<span class="text-danger">(以内)</span></p>
+                        <p ng-if="partner.RegionMode==2">{{partner.Regions}}<span class="text-danger">(以外)</span></p>
+                    </td>
+                </tr>
             </table>
             <div class="btn-group">
                 <button class="btn btn-info btn-sm waves-effect" ng-click="edit(partner)">