From 32464cb49447344a121e2f5a1a82037d5477f9e4 Mon Sep 17 00:00:00 2001 From: Tsanie Lily <tsorgy@gmail.com> Date: Wed, 5 Jul 2023 17:33:22 +0800 Subject: [PATCH] add latitude & longitude --- Server/Controller/BaseController.cs | 43 ++- Server/Controller/EventApiController.cs | 8 +- Server/Controller/FlowerApiController.cs | 59 ++- .../Controller/FlowerApiController.structs.cs | 21 +- Server/Controller/ImageController.cs | 8 +- Server/Controller/UserApiController.cs | 6 + Server/Data/Model/FlowerItem.cs | 20 +- Server/Data/Model/ILocation.cs | 17 + Server/Data/Model/PhotoItem.cs | 5 +- Server/Data/Model/RecordItem.cs | 14 +- ...5083734_Add-Latitude-Longitude.Designer.cs | 335 ++++++++++++++++++ .../20230705083734_Add-Latitude-Longitude.cs | 58 +++ .../Migrations/FlowerDatabaseModelSnapshot.cs | 16 + Server/Program.cs | 2 +- 14 files changed, 581 insertions(+), 31 deletions(-) create mode 100644 Server/Data/Model/ILocation.cs create mode 100644 Server/Migrations/20230705083734_Add-Latitude-Longitude.Designer.cs create mode 100644 Server/Migrations/20230705083734_Add-Latitude-Longitude.cs diff --git a/Server/Controller/BaseController.cs b/Server/Controller/BaseController.cs index a43a0e7..2a30ed2 100644 --- a/Server/Controller/BaseController.cs +++ b/Server/Controller/BaseController.cs @@ -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> diff --git a/Server/Controller/EventApiController.cs b/Server/Controller/EventApiController.cs index 17e7a03..694b1dd 100644 --- a/Server/Controller/EventApiController.cs +++ b/Server/Controller/EventApiController.cs @@ -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); } } diff --git a/Server/Controller/FlowerApiController.cs b/Server/Controller/FlowerApiController.cs index db471c5..129a9f1 100644 --- a/Server/Controller/FlowerApiController.cs +++ b/Server/Controller/FlowerApiController.cs @@ -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) diff --git a/Server/Controller/FlowerApiController.structs.cs b/Server/Controller/FlowerApiController.structs.cs index fa502a0..62268ba 100644 --- a/Server/Controller/FlowerApiController.structs.cs +++ b/Server/Controller/FlowerApiController.structs.cs @@ -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; } +} \ No newline at end of file diff --git a/Server/Controller/ImageController.cs b/Server/Controller/ImageController.cs index be5f074..a640db9 100644 --- a/Server/Controller/ImageController.cs +++ b/Server/Controller/ImageController.cs @@ -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); diff --git a/Server/Controller/UserApiController.cs b/Server/Controller/UserApiController.cs index 4ab8fc5..2f81e30 100644 --- a/Server/Controller/UserApiController.cs +++ b/Server/Controller/UserApiController.cs @@ -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() { diff --git a/Server/Data/Model/FlowerItem.cs b/Server/Data/Model/FlowerItem.cs index aa0a424..2ef6950 100644 --- a/Server/Data/Model/FlowerItem.cs +++ b/Server/Data/Model/FlowerItem.cs @@ -8,7 +8,7 @@ namespace Blahblah.FlowerStory.Server.Data.Model; /// 花草对象 /// </summary> [Table("flowers")] -public class FlowerItem +public class FlowerItem : ILocation { /// <summary> /// 自增 id,主键 @@ -83,4 +83,22 @@ public class FlowerItem /// 封面相关照片 /// </summary> public ICollection<PhotoItem>? Photos { get; set; } + + /// <summary> + /// 纬度 + /// </summary> + [Column("latitude")] + public double? Latitude { get; set; } + + /// <summary> + /// 经度 + /// </summary> + [Column("longitude")] + public double? Longitude { get; set; } + + /// <summary> + /// 距离(米) + /// </summary> + [NotMapped] + public int? Distance { get; set; } } diff --git a/Server/Data/Model/ILocation.cs b/Server/Data/Model/ILocation.cs new file mode 100644 index 0000000..1800124 --- /dev/null +++ b/Server/Data/Model/ILocation.cs @@ -0,0 +1,17 @@ +namespace Blahblah.FlowerStory.Server.Data.Model; + +/// <summary> +/// 实体位置接口 +/// </summary> +public interface ILocation +{ + /// <summary> + /// 纬度 + /// </summary> + double? Latitude { get; } + + /// <summary> + /// 经度 + /// </summary> + double? Longitude { get; } +} diff --git a/Server/Data/Model/PhotoItem.cs b/Server/Data/Model/PhotoItem.cs index 72516a2..f09c2c5 100644 --- a/Server/Data/Model/PhotoItem.cs +++ b/Server/Data/Model/PhotoItem.cs @@ -82,6 +82,9 @@ public class PhotoItem [JsonIgnore] public DateTimeOffset DateUpload => DateTimeOffset.FromUnixTimeMilliseconds(DateUploadUnixTime); + /// <summary> + /// 前端显示的 URL + /// </summary> [NotMapped] - public string Url { get; set; } + public string? Url { get; set; } } diff --git a/Server/Data/Model/RecordItem.cs b/Server/Data/Model/RecordItem.cs index fe201b7..0d28af8 100644 --- a/Server/Data/Model/RecordItem.cs +++ b/Server/Data/Model/RecordItem.cs @@ -8,7 +8,7 @@ namespace Blahblah.FlowerStory.Server.Data.Model; /// 记录对象 /// </summary> [Table("records")] -public class RecordItem +public class RecordItem : ILocation { /// <summary> /// 自增 id,主键 @@ -90,4 +90,16 @@ public class RecordItem /// 事件关联照片 /// </summary> public ICollection<PhotoItem>? Photos { get; set; } + + /// <summary> + /// 纬度 + /// </summary> + [Column("latitude")] + public double? Latitude { get; set; } + + /// <summary> + /// 经度 + /// </summary> + [Column("longitude")] + public double? Longitude { get; set; } } diff --git a/Server/Migrations/20230705083734_Add-Latitude-Longitude.Designer.cs b/Server/Migrations/20230705083734_Add-Latitude-Longitude.Designer.cs new file mode 100644 index 0000000..3da48ea --- /dev/null +++ b/Server/Migrations/20230705083734_Add-Latitude-Longitude.Designer.cs @@ -0,0 +1,335 @@ +// <auto-generated /> +using System; +using Blahblah.FlowerStory.Server.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Blahblah.FlowerStory.Server.Migrations +{ + [DbContext(typeof(FlowerDatabase))] + [Migration("20230705083734_Add-Latitude-Longitude")] + partial class AddLatitudeLongitude + { + /// <inheritdoc /> + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.5"); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("fid"); + + b.Property<int>("CategoryId") + .HasColumnType("INTEGER") + .HasColumnName("categoryid"); + + b.Property<decimal?>("Cost") + .HasColumnType("real") + .HasColumnName("cost"); + + b.Property<long>("DateBuyUnixTime") + .HasColumnType("INTEGER") + .HasColumnName("datebuy") + .HasAnnotation("Relational:JsonPropertyName", "dateBuy"); + + b.Property<double?>("LastLatitude") + .HasColumnType("REAL") + .HasColumnName("latitude"); + + b.Property<double?>("LastLongitude") + .HasColumnType("REAL") + .HasColumnName("longitude"); + + b.Property<string>("Memo") + .HasColumnType("TEXT") + .HasColumnName("memo"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.Property<int>("OwnerId") + .HasColumnType("INTEGER") + .HasColumnName("uid"); + + b.Property<string>("Purchase") + .HasColumnType("TEXT") + .HasColumnName("purchase"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("flowers"); + }); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.PhotoItem", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("pid"); + + b.Property<long>("DateUploadUnixTime") + .HasColumnType("INTEGER") + .HasColumnName("dateupload") + .HasAnnotation("Relational:JsonPropertyName", "dateUpload"); + + b.Property<string>("FileName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("filename"); + + b.Property<string>("FileType") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("filetype"); + + b.Property<int>("FlowerId") + .HasColumnType("INTEGER") + .HasColumnName("fid"); + + b.Property<string>("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("path"); + + b.Property<int>("RecordId") + .HasColumnType("INTEGER") + .HasColumnName("rid"); + + b.HasKey("Id"); + + b.HasIndex("FlowerId"); + + b.HasIndex("RecordId"); + + b.ToTable("photos"); + }); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("rid"); + + b.Property<int?>("ByUserId") + .HasColumnType("INTEGER") + .HasColumnName("byuid"); + + b.Property<string>("ByUserName") + .HasColumnType("TEXT") + .HasColumnName("byname"); + + b.Property<long>("DateUnixTime") + .HasColumnType("INTEGER") + .HasColumnName("date") + .HasAnnotation("Relational:JsonPropertyName", "date"); + + b.Property<int>("EventId") + .HasColumnType("INTEGER") + .HasColumnName("eid"); + + b.Property<int>("FlowerId") + .HasColumnType("INTEGER") + .HasColumnName("fid"); + + b.Property<double?>("Latitude") + .HasColumnType("REAL") + .HasColumnName("latitude"); + + b.Property<double?>("Longitude") + .HasColumnType("REAL") + .HasColumnName("longitude"); + + b.Property<string>("Memo") + .HasColumnType("TEXT") + .HasColumnName("memo"); + + b.Property<int>("OwnerId") + .HasColumnType("INTEGER") + .HasColumnName("uid"); + + b.HasKey("Id"); + + b.HasIndex("FlowerId"); + + b.HasIndex("OwnerId"); + + b.ToTable("records"); + }); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.TokenItem", b => + { + b.Property<string>("Id") + .HasColumnType("TEXT") + .HasColumnName("tid"); + + b.Property<long>("ActiveDateUnixTime") + .HasColumnType("INTEGER") + .HasColumnName("activedate") + .HasAnnotation("Relational:JsonPropertyName", "activeDate"); + + b.Property<string>("ClientAgent") + .HasColumnType("TEXT") + .HasColumnName("clientagent"); + + b.Property<string>("ClientApp") + .HasColumnType("TEXT") + .HasColumnName("clientapp"); + + b.Property<string>("DeviceId") + .HasColumnType("TEXT") + .HasColumnName("deviceid"); + + b.Property<long>("ExpireDateUnixTime") + .HasColumnType("INTEGER") + .HasColumnName("expiredate") + .HasAnnotation("Relational:JsonPropertyName", "expireDate"); + + b.Property<int>("ExpireSeconds") + .HasColumnType("INTEGER") + .HasColumnName("expiresecs"); + + b.Property<long>("LogonDateUnixTime") + .HasColumnType("INTEGER") + .HasColumnName("logondate") + .HasAnnotation("Relational:JsonPropertyName", "logonDate"); + + b.Property<int>("UserId") + .HasColumnType("INTEGER") + .HasColumnName("uid"); + + b.Property<string>("VerifyCode") + .HasColumnType("TEXT") + .HasColumnName("verifycode"); + + b.HasKey("Id"); + + b.ToTable("tokens"); + }); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uid"); + + b.Property<long?>("ActiveDateUnixTime") + .HasColumnType("INTEGER") + .HasColumnName("activedate"); + + b.Property<byte[]>("Avatar") + .HasColumnType("BLOB") + .HasColumnName("avatar"); + + b.Property<string>("Email") + .HasColumnType("TEXT") + .HasColumnName("email"); + + b.Property<int>("Level") + .HasColumnType("INTEGER") + .HasColumnName("level"); + + b.Property<string>("Mobile") + .HasColumnType("TEXT") + .HasColumnName("mobile"); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.Property<string>("Password") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("password"); + + b.Property<long>("RegisterDateUnixTime") + .HasColumnType("INTEGER") + .HasColumnName("regdate") + .HasAnnotation("Relational:JsonPropertyName", "registerDate"); + + b.Property<string>("UserId") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("id"); + + b.HasKey("Id"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b => + { + b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.PhotoItem", b => + { + b.HasOne("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", "Flower") + .WithMany("Photos") + .HasForeignKey("FlowerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blahblah.FlowerStory.Server.Data.Model.RecordItem", "Record") + .WithMany("Photos") + .HasForeignKey("RecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Flower"); + + b.Navigation("Record"); + }); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b => + { + b.HasOne("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", "Flower") + .WithMany() + .HasForeignKey("FlowerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Flower"); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b => + { + b.Navigation("Photos"); + }); + + modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b => + { + b.Navigation("Photos"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/Migrations/20230705083734_Add-Latitude-Longitude.cs b/Server/Migrations/20230705083734_Add-Latitude-Longitude.cs new file mode 100644 index 0000000..4e5458b --- /dev/null +++ b/Server/Migrations/20230705083734_Add-Latitude-Longitude.cs @@ -0,0 +1,58 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Blahblah.FlowerStory.Server.Migrations +{ + /// <inheritdoc /> + public partial class AddLatitudeLongitude : Migration + { + /// <inheritdoc /> + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn<double>( + name: "latitude", + table: "records", + type: "REAL", + nullable: true); + + migrationBuilder.AddColumn<double>( + name: "longitude", + table: "records", + type: "REAL", + nullable: true); + + migrationBuilder.AddColumn<double>( + name: "latitude", + table: "flowers", + type: "REAL", + nullable: true); + + migrationBuilder.AddColumn<double>( + name: "longitude", + table: "flowers", + type: "REAL", + nullable: true); + } + + /// <inheritdoc /> + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "latitude", + table: "records"); + + migrationBuilder.DropColumn( + name: "longitude", + table: "records"); + + migrationBuilder.DropColumn( + name: "latitude", + table: "flowers"); + + migrationBuilder.DropColumn( + name: "longitude", + table: "flowers"); + } + } +} diff --git a/Server/Migrations/FlowerDatabaseModelSnapshot.cs b/Server/Migrations/FlowerDatabaseModelSnapshot.cs index bfa546e..ac35087 100644 --- a/Server/Migrations/FlowerDatabaseModelSnapshot.cs +++ b/Server/Migrations/FlowerDatabaseModelSnapshot.cs @@ -37,6 +37,14 @@ namespace Blahblah.FlowerStory.Server.Migrations .HasColumnName("datebuy") .HasAnnotation("Relational:JsonPropertyName", "dateBuy"); + b.Property<double?>("LastLatitude") + .HasColumnType("REAL") + .HasColumnName("latitude"); + + b.Property<double?>("LastLongitude") + .HasColumnType("REAL") + .HasColumnName("longitude"); + b.Property<string>("Memo") .HasColumnType("TEXT") .HasColumnName("memo"); @@ -133,6 +141,14 @@ namespace Blahblah.FlowerStory.Server.Migrations .HasColumnType("INTEGER") .HasColumnName("fid"); + b.Property<double?>("Latitude") + .HasColumnType("REAL") + .HasColumnName("latitude"); + + b.Property<double?>("Longitude") + .HasColumnType("REAL") + .HasColumnName("longitude"); + b.Property<string>("Memo") .HasColumnType("TEXT") .HasColumnName("memo"); diff --git a/Server/Program.cs b/Server/Program.cs index f6cc671..2752f73 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -11,7 +11,7 @@ public class Program /// <inheritdoc/> public const string ProjectName = "Flower Story"; /// <inheritdoc/> - public const string Version = "0.4.626"; + public const string Version = "0.5.705"; /// <inheritdoc/> public static void Main(string[] args)