|
|
@@ -23,220 +23,237 @@ using System.Linq.Expressions;
|
|
|
using System.Net;
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
-namespace Masuit.MyBlogs.Core.Controllers
|
|
|
+namespace Masuit.MyBlogs.Core.Controllers;
|
|
|
+
|
|
|
+[Route("partner/[action]")]
|
|
|
+public class AdvertisementController : BaseController
|
|
|
{
|
|
|
- [Route("partner/[action]")]
|
|
|
- public class AdvertisementController : BaseController
|
|
|
+ public IAdvertisementClickRecordService ClickRecordService { get; set; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 前往
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="id">广告id</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpGet("/p{id:int}"), HttpGet("{id:int}", Order = 1), ResponseCache(Duration = 3600)]
|
|
|
+ public async Task<IActionResult> Redirect(int id)
|
|
|
{
|
|
|
- public IAdvertisementClickRecordService ClickRecordService { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 前往
|
|
|
- /// </summary>
|
|
|
- /// <param name="id">广告id</param>
|
|
|
- /// <returns></returns>
|
|
|
- [HttpGet("/p{id:int}"), HttpGet("{id:int}", Order = 1), ResponseCache(Duration = 3600)]
|
|
|
- public async Task<IActionResult> Redirect(int id)
|
|
|
+ var ad = await AdsService.GetByIdAsync(id) ?? throw new NotFoundException("推广链接不存在");
|
|
|
+ if (!Request.IsRobot() && string.IsNullOrEmpty(HttpContext.Session.Get<string>("ads" + id)))
|
|
|
{
|
|
|
- var ad = await AdsService.GetByIdAsync(id) ?? throw new NotFoundException("推广链接不存在");
|
|
|
- if (!Request.IsRobot() && string.IsNullOrEmpty(HttpContext.Session.Get<string>("ads" + id)))
|
|
|
+ HttpContext.Session.Set("ads" + id, id.ToString());
|
|
|
+ ad.ClickRecords.Add(new AdvertisementClickRecord()
|
|
|
{
|
|
|
- HttpContext.Session.Set("ads" + id, id.ToString());
|
|
|
- ad.ClickRecords.Add(new AdvertisementClickRecord()
|
|
|
- {
|
|
|
- IP = ClientIP,
|
|
|
- Location = ClientIP.GetIPLocation(),
|
|
|
- Referer = Request.Headers[HeaderNames.Referer].ToString(),
|
|
|
- Time = DateTime.Now
|
|
|
- });
|
|
|
- await AdsService.SaveChangesAsync();
|
|
|
- var start = DateTime.Today.AddMonths(-6);
|
|
|
- await ClickRecordService.GetQuery(a => a.Time < start).DeleteFromQueryAsync();
|
|
|
- }
|
|
|
-
|
|
|
- return Redirect(ad.Url);
|
|
|
+ IP = ClientIP,
|
|
|
+ Location = ClientIP.GetIPLocation(),
|
|
|
+ Referer = Request.Headers[HeaderNames.Referer].ToString(),
|
|
|
+ Time = DateTime.Now
|
|
|
+ });
|
|
|
+ await AdsService.SaveChangesAsync();
|
|
|
+ var start = DateTime.Today.AddMonths(-6);
|
|
|
+ await ClickRecordService.GetQuery(a => a.Time < start).DeleteFromQueryAsync();
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 获取分页
|
|
|
- /// </summary>
|
|
|
- /// <returns></returns>
|
|
|
- [MyAuthorize]
|
|
|
- public ActionResult GetPageData(int page = 1, [Range(1, int.MaxValue, ErrorMessage = "页大小必须大于0")] int size = 10, string kw = "")
|
|
|
- {
|
|
|
- Expression<Func<Advertisement, bool>> where = p => true;
|
|
|
- if (!string.IsNullOrEmpty(kw))
|
|
|
- {
|
|
|
- kw = Regex.Escape(kw);
|
|
|
- where = where.And(p => Regex.IsMatch(p.Title + p.Description + p.Url, kw, RegexOptions.IgnoreCase));
|
|
|
- }
|
|
|
-
|
|
|
- var list = AdsService.GetQuery(where).OrderByDescending(p => p.Status == Status.Available).ThenByDescending(a => a.Price).ThenByDescending(a => a.Id).ProjectTo<AdvertisementViewModel>(MapperConfig).ToPagedList(page, size);
|
|
|
- return Ok(list);
|
|
|
- }
|
|
|
+ return Redirect(ad.Url);
|
|
|
+ }
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 保存广告
|
|
|
- /// </summary>
|
|
|
- /// <param name="model"></param>
|
|
|
- /// <returns></returns>
|
|
|
- [HttpPost, MyAuthorize]
|
|
|
- public async Task<IActionResult> Save([FromBodyOrDefault] AdvertisementDto model)
|
|
|
+ /// <summary>
|
|
|
+ /// 获取分页
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [MyAuthorize]
|
|
|
+ public ActionResult GetPageData(int page = 1, [Range(1, int.MaxValue, ErrorMessage = "页大小必须大于0")] int size = 10, string kw = "")
|
|
|
+ {
|
|
|
+ Expression<Func<Advertisement, bool>> where = p => true;
|
|
|
+ if (!string.IsNullOrEmpty(kw))
|
|
|
{
|
|
|
- var entity = AdsService[model.Id] ?? new Advertisement();
|
|
|
- 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, "宣传大图不能为空");
|
|
|
- }
|
|
|
-
|
|
|
- if (model.Types.Length > 3 && string.IsNullOrEmpty(model.ThumbImgUrl))
|
|
|
- {
|
|
|
- return ResultData(null, false, "宣传小图不能为空");
|
|
|
- }
|
|
|
-
|
|
|
- Mapper.Map(model, entity);
|
|
|
- var b = await AdsService.AddOrUpdateSavedAsync(a => a.Id, entity) > 0;
|
|
|
- return ResultData(null, b, b ? "保存成功" : "保存失败");
|
|
|
+ kw = Regex.Escape(kw);
|
|
|
+ where = where.And(p => Regex.IsMatch(p.Title + p.Description + p.Url, kw, RegexOptions.IgnoreCase));
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 删除广告
|
|
|
- /// </summary>
|
|
|
- /// <param name="id"></param>
|
|
|
- /// <returns></returns>
|
|
|
- [HttpPost("{id}"), HttpGet("{id}"), MyAuthorize]
|
|
|
- public async Task<IActionResult> Delete(int id)
|
|
|
+ var list = AdsService.GetQuery(where).OrderByDescending(p => p.Status == Status.Available).ThenByDescending(a => a.Price).ThenByDescending(a => a.Id).ProjectTo<AdvertisementViewModel>(MapperConfig).ToPagedList(page, size);
|
|
|
+ return Ok(list);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 保存广告
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="model"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost, MyAuthorize]
|
|
|
+ public async Task<IActionResult> Save([FromBodyOrDefault] AdvertisementDto model)
|
|
|
+ {
|
|
|
+ var entity = AdsService[model.Id] ?? new Advertisement();
|
|
|
+ model.CategoryIds = model.CategoryIds?.Replace("null", "");
|
|
|
+ model.Regions = Regex.Replace(model.Regions ?? "", @"(\p{P}|\p{Z}|\p{S})+", "|");
|
|
|
+ if (model.RegionMode == RegionLimitMode.All)
|
|
|
{
|
|
|
- bool b = await AdsService.DeleteByIdAsync(id) > 0;
|
|
|
- return ResultData(null, b, b ? "删除成功" : "删除失败");
|
|
|
+ model.Regions = null;
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 广告上下架
|
|
|
- /// </summary>
|
|
|
- /// <param name="id">文章id</param>
|
|
|
- /// <returns></returns>
|
|
|
- [MyAuthorize, HttpPost("{id}")]
|
|
|
- public async Task<ActionResult> ChangeState(int id)
|
|
|
+ if (model.Types.Contains(AdvertiseType.Banner.ToString("D")) && string.IsNullOrEmpty(model.ImageUrl))
|
|
|
{
|
|
|
- var ad = await AdsService.GetByIdAsync(id) ?? throw new NotFoundException("广告不存在!");
|
|
|
- ad.Status = ad.Status == Status.Available ? Status.Unavailable : Status.Available;
|
|
|
- return ResultData(null, await AdsService.SaveChangesAsync() > 0, ad.Status == Status.Available ? $"【{ad.Title}】已上架!" : $"【{ad.Title}】已下架!");
|
|
|
+ return ResultData(null, false, "宣传大图不能为空");
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 随机前往一个广告
|
|
|
- /// </summary>
|
|
|
- /// <returns></returns>
|
|
|
- [HttpGet("/partner-random")]
|
|
|
- public async Task<ActionResult> RandomGo()
|
|
|
+ if (model.Types.Length > 3 && string.IsNullOrEmpty(model.ThumbImgUrl))
|
|
|
{
|
|
|
- var ad = AdsService.GetByWeightedPrice((AdvertiseType)new Random().Next(1, 4), Request.Location());
|
|
|
- if (!Request.IsRobot() && string.IsNullOrEmpty(HttpContext.Session.Get<string>("ads" + ad.Id)))
|
|
|
- {
|
|
|
- HttpContext.Session.Set("ads" + ad.Id, ad.Id.ToString());
|
|
|
- ad.ClickRecords.Add(new AdvertisementClickRecord()
|
|
|
- {
|
|
|
- IP = ClientIP,
|
|
|
- Location = ClientIP.GetIPLocation(),
|
|
|
- Referer = Request.Headers[HeaderNames.Referer].ToString(),
|
|
|
- Time = DateTime.Now
|
|
|
- });
|
|
|
- await AdsService.SaveChangesAsync();
|
|
|
- var start = DateTime.Today.AddMonths(-1);
|
|
|
- await ClickRecordService.GetQuery(a => a.Time < start).DeleteFromQueryAsync();
|
|
|
- }
|
|
|
-
|
|
|
- return Redirect(ad.Url);
|
|
|
+ return ResultData(null, false, "宣传小图不能为空");
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 广告访问记录
|
|
|
- /// </summary>
|
|
|
- /// <param name="id"></param>
|
|
|
- /// <param name="page"></param>
|
|
|
- /// <param name="size"></param>
|
|
|
- /// <returns></returns>
|
|
|
- [HttpGet("/partner/{id}/records"), MyAuthorize]
|
|
|
- public async Task<IActionResult> ClickRecords(int id, int page = 1, int size = 15, string kw = "")
|
|
|
- {
|
|
|
- Expression<Func<AdvertisementClickRecord, bool>> where = e => e.AdvertisementId == id;
|
|
|
- if (!string.IsNullOrEmpty(kw))
|
|
|
- {
|
|
|
- kw = Regex.Escape(kw);
|
|
|
- where = where.And(e => Regex.IsMatch(e.IP + e.Location + e.Referer, kw));
|
|
|
- }
|
|
|
+ Mapper.Map(model, entity);
|
|
|
+ var b = await AdsService.AddOrUpdateSavedAsync(a => a.Id, entity) > 0;
|
|
|
+ return ResultData(null, b, b ? "保存成功" : "保存失败");
|
|
|
+ }
|
|
|
|
|
|
- var pages = await ClickRecordService.GetPagesAsync<DateTime, AdvertisementClickRecordViewModel>(page, size, where, e => e.Time, false);
|
|
|
- return Ok(pages);
|
|
|
- }
|
|
|
+ /// <summary>
|
|
|
+ /// 删除广告
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="id"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpPost("{id}"), HttpGet("{id}"), MyAuthorize]
|
|
|
+ public async Task<IActionResult> Delete(int id)
|
|
|
+ {
|
|
|
+ bool b = await AdsService.DeleteByIdAsync(id) > 0;
|
|
|
+ return ResultData(null, b, b ? "删除成功" : "删除失败");
|
|
|
+ }
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 导出广告访问记录
|
|
|
- /// </summary>
|
|
|
- /// <param name="id"></param>
|
|
|
- /// <param name="page"></param>
|
|
|
- /// <param name="size"></param>
|
|
|
- /// <returns></returns>
|
|
|
- [HttpGet("/partner/{id}/records-export"), MyAuthorize]
|
|
|
- public IActionResult ExportClickRecords(int id)
|
|
|
- {
|
|
|
- using var list = ClickRecordService.GetQuery<DateTime, AdvertisementClickRecordViewModel>(e => e.AdvertisementId == id, e => e.Time, false).ToPooledList();
|
|
|
- using var ms = list.ToDataTable().ToExcel();
|
|
|
- var advertisement = AdsService[id];
|
|
|
- return this.ResumeFile(ms.ToArray(), ContentType.Xlsx, advertisement.Title + "访问记录.xlsx");
|
|
|
- }
|
|
|
+ /// <summary>
|
|
|
+ /// 广告上下架
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="id">文章id</param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [MyAuthorize, HttpPost("{id}")]
|
|
|
+ public async Task<ActionResult> ChangeState(int id)
|
|
|
+ {
|
|
|
+ var ad = await AdsService.GetByIdAsync(id) ?? throw new NotFoundException("广告不存在!");
|
|
|
+ ad.Status = ad.Status == Status.Available ? Status.Unavailable : Status.Available;
|
|
|
+ return ResultData(null, await AdsService.SaveChangesAsync() > 0, ad.Status == Status.Available ? $"【{ad.Title}】已上架!" : $"【{ad.Title}】已下架!");
|
|
|
+ }
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 广告访问记录图表
|
|
|
- /// </summary>
|
|
|
- /// <param name="id"></param>
|
|
|
- /// <param name="cancellationToken"></param>
|
|
|
- /// <returns></returns>
|
|
|
- [HttpGet("/partner/{id}/records-chart"), MyAuthorize]
|
|
|
- [ProducesResponseType((int)HttpStatusCode.OK)]
|
|
|
- public async Task<IActionResult> ClickRecordsChart(int id, CancellationToken cancellationToken)
|
|
|
+ /// <summary>
|
|
|
+ /// 随机前往一个广告
|
|
|
+ /// </summary>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpGet("/partner-random")]
|
|
|
+ public async Task<ActionResult> RandomGo()
|
|
|
+ {
|
|
|
+ var ad = AdsService.GetByWeightedPrice((AdvertiseType)new Random().Next(1, 4), Request.Location());
|
|
|
+ if (!Request.IsRobot() && string.IsNullOrEmpty(HttpContext.Session.Get<string>("ads" + ad.Id)))
|
|
|
{
|
|
|
- var list = await ClickRecordService.GetQuery(e => e.AdvertisementId == id).Select(e => e.Time).GroupBy(t => t.Date).Select(g => new
|
|
|
+ HttpContext.Session.Set("ads" + ad.Id, ad.Id.ToString());
|
|
|
+ ad.ClickRecords.Add(new AdvertisementClickRecord()
|
|
|
{
|
|
|
- Date = g.Key,
|
|
|
- Count = g.Count()
|
|
|
- }).OrderBy(a => a.Date).ToListAsync(cancellationToken);
|
|
|
- return Ok(list);
|
|
|
+ IP = ClientIP,
|
|
|
+ Location = ClientIP.GetIPLocation(),
|
|
|
+ Referer = Request.Headers[HeaderNames.Referer].ToString(),
|
|
|
+ Time = DateTime.Now
|
|
|
+ });
|
|
|
+ await AdsService.SaveChangesAsync();
|
|
|
+ var start = DateTime.Today.AddMonths(-1);
|
|
|
+ await ClickRecordService.GetQuery(a => a.Time < start).DeleteFromQueryAsync();
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 广告访问记录分析
|
|
|
- /// </summary>
|
|
|
- /// <param name="id"></param>
|
|
|
- /// <returns></returns>
|
|
|
- [HttpGet("/partner/{id}/insight"), MyAuthorize]
|
|
|
- public IActionResult ClickRecordsInsight(int id)
|
|
|
+ return Redirect(ad.Url);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 广告访问记录
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="id"></param>
|
|
|
+ /// <param name="page"></param>
|
|
|
+ /// <param name="size"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpGet("/partner/{id}/records"), MyAuthorize]
|
|
|
+ public async Task<IActionResult> ClickRecords(int id, int page = 1, int size = 15, string kw = "")
|
|
|
+ {
|
|
|
+ Expression<Func<AdvertisementClickRecord, bool>> where = e => e.AdvertisementId == id;
|
|
|
+ if (!string.IsNullOrEmpty(kw))
|
|
|
{
|
|
|
- return View(AdsService[id]);
|
|
|
+ kw = Regex.Escape(kw);
|
|
|
+ where = where.And(e => Regex.IsMatch(e.IP + e.Location + e.Referer, kw));
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 设置分类
|
|
|
- /// </summary>
|
|
|
- /// <param name="id"></param>
|
|
|
- /// <param name="cids"></param>
|
|
|
- /// <returns></returns>
|
|
|
- /// <exception cref="NotFoundException"></exception>
|
|
|
- [HttpPost("/partner/{id}/categories")]
|
|
|
- public async Task<ActionResult> SetCategories(int id, [FromBodyOrDefault] string cids)
|
|
|
+ var pages = await ClickRecordService.GetPagesAsync<DateTime, AdvertisementClickRecordViewModel>(page, size, where, e => e.Time, false);
|
|
|
+ return Ok(pages);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 导出广告访问记录
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="id"></param>
|
|
|
+ /// <param name="page"></param>
|
|
|
+ /// <param name="size"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpGet("/partner/{id}/records-export"), MyAuthorize]
|
|
|
+ public IActionResult ExportClickRecords(int id)
|
|
|
+ {
|
|
|
+ using var list = ClickRecordService.GetQuery<DateTime, AdvertisementClickRecordViewModel>(e => e.AdvertisementId == id, e => e.Time, false).ToPooledList();
|
|
|
+ using var ms = list.ToDataTable().ToExcel();
|
|
|
+ var advertisement = AdsService[id];
|
|
|
+ return this.ResumeFile(ms.ToArray(), ContentType.Xlsx, advertisement.Title + "访问记录.xlsx");
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 广告访问记录图表
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="id"></param>
|
|
|
+ /// <param name="cancellationToken"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpGet("/partner/{id}/records-chart"), MyAuthorize]
|
|
|
+ [ProducesResponseType((int)HttpStatusCode.OK)]
|
|
|
+ public async Task<IActionResult> ClickRecordsChart(int id, CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var list = await ClickRecordService.GetQuery(e => e.AdvertisementId == id).Select(e => e.Time).GroupBy(t => t.Date).Select(g => new
|
|
|
{
|
|
|
- var entity = await AdsService.GetByIdAsync(id) ?? throw new NotFoundException("广告未找到");
|
|
|
- entity.CategoryIds = cids;
|
|
|
- await AdsService.SaveChangesAsync();
|
|
|
- return Ok();
|
|
|
- }
|
|
|
+ Date = g.Key,
|
|
|
+ Count = g.Count()
|
|
|
+ }).OrderBy(a => a.Date).ToListAsync(cancellationToken);
|
|
|
+ return Ok(list);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 广告访问记录图表
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="cancellationToken"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpGet("/partner/records-chart"), MyAuthorize]
|
|
|
+ [ProducesResponseType((int)HttpStatusCode.OK)]
|
|
|
+ public async Task<IActionResult> ClickRecordsChart(CancellationToken cancellationToken)
|
|
|
+ {
|
|
|
+ var start = DateTime.Now.AddMonths(-1);
|
|
|
+ var list = await ClickRecordService.GetQuery(e => e.Time >= start).Select(e => e.Time).GroupBy(t => t.Date).Select(g => new
|
|
|
+ {
|
|
|
+ Date = g.Key,
|
|
|
+ Count = g.Count()
|
|
|
+ }).OrderBy(a => a.Date).ToListAsync(cancellationToken);
|
|
|
+ return Ok(list);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 广告访问记录分析
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="id"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ [HttpGet("/partner/{id}/insight"), MyAuthorize]
|
|
|
+ public IActionResult ClickRecordsInsight(int id)
|
|
|
+ {
|
|
|
+ return View(AdsService[id]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设置分类
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="id"></param>
|
|
|
+ /// <param name="cids"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ /// <exception cref="NotFoundException"></exception>
|
|
|
+ [HttpPost("/partner/{id}/categories")]
|
|
|
+ public async Task<ActionResult> SetCategories(int id, [FromBodyOrDefault] string cids)
|
|
|
+ {
|
|
|
+ var entity = await AdsService.GetByIdAsync(id) ?? throw new NotFoundException("广告未找到");
|
|
|
+ entity.CategoryIds = cids;
|
|
|
+ await AdsService.SaveChangesAsync();
|
|
|
+ return Ok();
|
|
|
}
|
|
|
-}
|
|
|
+}
|