add latitude & longitude
This commit is contained in:
@ -193,7 +193,7 @@ public abstract partial class BaseController : ControllerBase
|
||||
/// </summary>
|
||||
/// <param name="file">来自请求的文件</param>
|
||||
/// <returns>文件结果对象</returns>
|
||||
protected FileResult? WrapFormFile(IFormFile file)
|
||||
protected static FileResult? WrapFormFile(IFormFile file)
|
||||
{
|
||||
if (file == null)
|
||||
{
|
||||
@ -235,11 +235,10 @@ public abstract partial class BaseController : ControllerBase
|
||||
/// <summary>
|
||||
/// 写入文件到用户的花草目录中
|
||||
/// </summary>
|
||||
/// <param name="uid">用户唯一 id</param>
|
||||
/// <param name="fid">花草唯一 id</param>
|
||||
/// <param name="file">文件对象</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
protected async Task WriteToFile(int uid, int fid, FileResult file, CancellationToken token = default)
|
||||
protected static async Task WriteToFile(int fid, FileResult file, CancellationToken token = default)
|
||||
{
|
||||
var directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads", fid.ToString());
|
||||
if (!Directory.Exists(directory))
|
||||
@ -253,11 +252,10 @@ public abstract partial class BaseController : ControllerBase
|
||||
/// <summary>
|
||||
/// 删除花草下的文件
|
||||
/// </summary>
|
||||
/// <param name="uid">用户唯一 id</param>
|
||||
/// <param name="fid">花草唯一 id</param>
|
||||
/// <param name="path">文件路径</param>
|
||||
/// <returns>返回是否已删除</returns>
|
||||
protected bool DeleteFile(int uid, int fid, string path)
|
||||
protected static bool DeleteFile(int fid, string path)
|
||||
{
|
||||
var directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads", fid.ToString());
|
||||
if (Directory.Exists(directory))
|
||||
@ -271,6 +269,41 @@ public abstract partial class BaseController : ControllerBase
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private const double EarthRadius = 6378137;
|
||||
|
||||
private static double Radius(double degree)
|
||||
{
|
||||
return degree * Math.PI / 180.0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取两个经纬度之间的距离
|
||||
/// </summary>
|
||||
/// <param name="lat1">纬度1</param>
|
||||
/// <param name="lon1">经度1</param>
|
||||
/// <param name="lat2">纬度2</param>
|
||||
/// <param name="lon2">经度2</param>
|
||||
/// <returns></returns>
|
||||
protected static double GetDistance(double lat1, double lon1, double lat2, double lon2)
|
||||
{
|
||||
double rlat1 = Radius(lat1);
|
||||
double rlat2 = Radius(lat2);
|
||||
return EarthRadius * Math.Acos(
|
||||
Math.Cos(rlat1) * Math.Cos(rlat2) * Math.Cos(Radius(lon1) - Radius(lon2)) +
|
||||
Math.Sin(rlat1) * Math.Sin(rlat2));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected static double GetDistance2(double lat1, double lon1, double lat2, double lon2)
|
||||
{
|
||||
double rlat1 = Radius(lat1);
|
||||
double rlat2 = Radius(lat2);
|
||||
double a = rlat1 - rlat2;
|
||||
double b = Radius(lon1) - Radius(lon2);
|
||||
double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) + Math.Cos(rlat1) * Math.Cos(rlat2) * Math.Pow(Math.Sin(b / 2), 2)));
|
||||
return s * EarthRadius;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -389,7 +389,7 @@ public class EventApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(p);
|
||||
|
||||
await WriteToFile(user.Id, record.FlowerId, file, token);
|
||||
await WriteToFile(record.FlowerId, file, token);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -483,7 +483,7 @@ public class EventApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(p);
|
||||
|
||||
await WriteToFile(user.Id, record.FlowerId, file, token);
|
||||
await WriteToFile(record.FlowerId, file, token);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -555,7 +555,7 @@ public class EventApiController : BaseController
|
||||
|
||||
if (photo.Record != null)
|
||||
{
|
||||
DeleteFile(user.Id, photo.Record.FlowerId, photo.Path);
|
||||
DeleteFile(photo.Record.FlowerId, photo.Path);
|
||||
}
|
||||
|
||||
return NoContent();
|
||||
@ -613,7 +613,7 @@ public class EventApiController : BaseController
|
||||
{
|
||||
if (photo.Record != null)
|
||||
{
|
||||
DeleteFile(user.Id, photo.Record.FlowerId, photo.Path);
|
||||
DeleteFile(photo.Record.FlowerId, photo.Path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
using Blahblah.FlowerStory.Server.Data.Model;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@ -38,6 +39,9 @@ public class FlowerApiController : BaseController
|
||||
/// cfrom: decimal?
|
||||
/// cto: decimal?
|
||||
/// photo: bool?
|
||||
/// lon: double?
|
||||
/// lat: double?
|
||||
/// distance: int?
|
||||
/// p: int?
|
||||
/// size: int?
|
||||
///
|
||||
@ -49,6 +53,9 @@ public class FlowerApiController : BaseController
|
||||
/// <param name="costFrom">开销最小值</param>
|
||||
/// <param name="costTo">开销最大值</param>
|
||||
/// <param name="includePhoto">是否包含封面图片</param>
|
||||
/// <param name="latitude">纬度</param>
|
||||
/// <param name="longitude">经度</param>
|
||||
/// <param name="distance">距离(米)</param>
|
||||
/// <param name="page">页数</param>
|
||||
/// <param name="pageSize">分页大小</param>
|
||||
/// <returns>会话有效则返回符合条件的花草集</returns>
|
||||
@ -63,7 +70,7 @@ public class FlowerApiController : BaseController
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public ActionResult<FlowerItem[]> GetFlowers(
|
||||
public ActionResult<FlowerResult> GetFlowers(
|
||||
[FromQuery(Name = "cid")] int? categoryId,
|
||||
[FromQuery] string? key,
|
||||
[FromQuery(Name = "from")] long? buyFrom,
|
||||
@ -71,6 +78,9 @@ public class FlowerApiController : BaseController
|
||||
[FromQuery(Name = "cfrom")] decimal? costFrom,
|
||||
[FromQuery(Name = "cto")] decimal? costTo,
|
||||
[FromQuery(Name = "photo")] bool? includePhoto,
|
||||
[FromQuery(Name = "lat")] double? latitude,
|
||||
[FromQuery(Name = "lon")] double? longitude,
|
||||
[FromQuery] int? distance,
|
||||
[FromQuery(Name = "p")] int? page = 0,
|
||||
[FromQuery(Name = "size")] int? pageSize = 20)
|
||||
{
|
||||
@ -86,7 +96,7 @@ public class FlowerApiController : BaseController
|
||||
|
||||
SaveDatabase();
|
||||
|
||||
var flowers = database.Flowers.Where(f => f.OwnerId == user.Id);
|
||||
IEnumerable<FlowerItem> flowers = database.Flowers.Where(f => f.OwnerId == user.Id);
|
||||
if (categoryId != null)
|
||||
{
|
||||
flowers = flowers.Where(f => f.CategoryId == categoryId);
|
||||
@ -115,6 +125,20 @@ public class FlowerApiController : BaseController
|
||||
flowers = flowers.Where(f => f.Cost != null && f.Cost <= costTo);
|
||||
}
|
||||
|
||||
if (distance != null && latitude != null && longitude != null)
|
||||
{
|
||||
flowers = flowers.Where(f => f.Latitude != null && f.Longitude != null)
|
||||
.AsEnumerable()
|
||||
.Where(f =>
|
||||
{
|
||||
var d = GetDistance(latitude.Value, longitude.Value, f.Latitude ?? 0, f.Longitude ?? 0);
|
||||
f.Distance = (int)d;
|
||||
return d <= distance;
|
||||
});
|
||||
}
|
||||
|
||||
int count = flowers.Count();
|
||||
|
||||
var size = pageSize ?? 20;
|
||||
var p = page ?? 0;
|
||||
flowers = flowers.OrderByDescending(f => f.DateBuyUnixTime).Skip(p * size).Take(size);
|
||||
@ -128,12 +152,16 @@ public class FlowerApiController : BaseController
|
||||
r.FlowerId == f.Id && r.EventId == EventCover && r.Id == p.RecordId)).ToList();
|
||||
foreach (var photo in f.Photos)
|
||||
{
|
||||
photo.Url = $"{ImageController.BaseUrl}/photo/flower/{f.Id}/{photo.Path}";
|
||||
photo.Url = $"photo/flower/{f.Id}/{photo.Path}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(flowers.ToArray());
|
||||
return Ok(new FlowerResult
|
||||
{
|
||||
Flowers = flowers.ToArray(),
|
||||
Count = count
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -194,7 +222,7 @@ public class FlowerApiController : BaseController
|
||||
r.FlowerId == item.Id && r.EventId == EventCover && r.Id == p.RecordId)).ToList();
|
||||
foreach (var photo in item.Photos)
|
||||
{
|
||||
photo.Url = $"{ImageController.BaseUrl}/photo/flower/{item.Id}/{photo.Path}";
|
||||
photo.Url = $"photo/flower/{item.Id}/{photo.Path}";
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,7 +421,7 @@ public class FlowerApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(cover);
|
||||
|
||||
await WriteToFile(user.Id, item.Id, file, token);
|
||||
await WriteToFile(item.Id, file, token);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -491,11 +519,14 @@ public class FlowerApiController : BaseController
|
||||
}
|
||||
else
|
||||
{
|
||||
var photo = database.Photos.Where(p => p.RecordId == record.Id).SingleOrDefault();
|
||||
if (photo != null)
|
||||
var photos = database.Photos.Where(p => p.RecordId == record.Id).ToList();
|
||||
if (photos.Count > 0)
|
||||
{
|
||||
database.Photos.Where(p => p.RecordId == record.Id).ExecuteDelete();
|
||||
DeleteFile(user.Id, update.Id, photo.Path);
|
||||
foreach (var photo in photos)
|
||||
{
|
||||
DeleteFile(update.Id, photo.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
SaveDatabase();
|
||||
@ -515,7 +546,7 @@ public class FlowerApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(cover);
|
||||
|
||||
await WriteToFile(user.Id, update.Id, file, token);
|
||||
await WriteToFile(update.Id, file, token);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -524,8 +555,12 @@ public class FlowerApiController : BaseController
|
||||
// TODO: Logger
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveDatabase();
|
||||
}
|
||||
|
||||
return Ok(user);
|
||||
return Ok(flower);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -620,7 +655,7 @@ public class FlowerApiController : BaseController
|
||||
};
|
||||
AddPhotoItem(cover);
|
||||
|
||||
await WriteToFile(user.Id, id, file, token);
|
||||
await WriteToFile(id, file, token);
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Blahblah.FlowerStory.Server.Data.Model;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
@ -58,5 +59,21 @@ public record FlowerUpdateParameter : FlowerParameter
|
||||
/// </summary>
|
||||
[Required]
|
||||
[FromForm(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
public int Id { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 花草结果对象
|
||||
/// </summary>
|
||||
public record FlowerResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 花草列表
|
||||
/// </summary>
|
||||
public required FlowerItem[] Flowers { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 花草总数
|
||||
/// </summary>
|
||||
public int Count { get; init; }
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using Blahblah.FlowerStory.Server.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Net;
|
||||
|
||||
namespace Blahblah.FlowerStory.Server.Controller;
|
||||
|
||||
@ -11,9 +12,6 @@ namespace Blahblah.FlowerStory.Server.Controller;
|
||||
[Route("photo")]
|
||||
public class ImageController : BaseController
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public const string BaseUrl = "https://flower.tsanie.org";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ImageController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
|
||||
{
|
||||
@ -66,6 +64,8 @@ public class ImageController : BaseController
|
||||
/// GET /photo/flower/{fid}/{name}
|
||||
///
|
||||
/// </remarks>
|
||||
/// <param name="fid">花草唯一 id</param>
|
||||
/// <param name="name">照片名称</param>
|
||||
/// <returns>认证通过则显示花草照片</returns>
|
||||
/// <response code="200">返回花草照片</response>
|
||||
/// <response code="401">认证失败</response>
|
||||
@ -98,7 +98,7 @@ public class ImageController : BaseController
|
||||
return Forbid();
|
||||
}
|
||||
#endif
|
||||
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads", fid.ToString(), name);
|
||||
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads", fid.ToString(), WebUtility.UrlEncode(name));
|
||||
if (System.IO.File.Exists(path))
|
||||
{
|
||||
var data = await System.IO.File.ReadAllBytesAsync(path);
|
||||
|
@ -119,6 +119,7 @@ public partial class UserApiController : BaseController
|
||||
[Route("logout", Name = "logout")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesErrorResponseType(typeof(ErrorResponse))]
|
||||
[HttpPost]
|
||||
public ActionResult Logout()
|
||||
{
|
||||
@ -161,6 +162,7 @@ public partial class UserApiController : BaseController
|
||||
[Route("register", Name = "register")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
[ProducesErrorResponseType(typeof(ErrorResponse))]
|
||||
[HttpPost]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<UserItem> Register([FromBody] UserParameter user)
|
||||
@ -212,6 +214,7 @@ public partial class UserApiController : BaseController
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesErrorResponseType(typeof(ErrorResponse))]
|
||||
[HttpGet]
|
||||
[ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
|
||||
public ActionResult<UserItem> Profile()
|
||||
@ -260,6 +263,7 @@ public partial class UserApiController : BaseController
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
|
||||
[ProducesErrorResponseType(typeof(ErrorResponse))]
|
||||
[HttpPut]
|
||||
[Consumes("application/json")]
|
||||
public ActionResult<UserItem> Update([FromBody] UpdateParameter update)
|
||||
@ -314,6 +318,7 @@ public partial class UserApiController : BaseController
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
|
||||
[ProducesErrorResponseType(typeof(ErrorResponse))]
|
||||
[HttpPut]
|
||||
[Consumes("multipart/form-data")]
|
||||
[RequestSizeLimit(5 * 1024 * 1024)]
|
||||
@ -366,6 +371,7 @@ public partial class UserApiController : BaseController
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesErrorResponseType(typeof(ErrorResponse))]
|
||||
[HttpDelete]
|
||||
public ActionResult RemoveAvatar()
|
||||
{
|
||||
|
Reference in New Issue
Block a user