diff --git a/.gitignore b/.gitignore
index 72d2a79..94b4c7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,7 @@
-# ---> VisualStudio
 ## Ignore Visual Studio temporary files, build results, and
 ## files generated by popular Visual Studio add-ons.
 ##
-## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
 
 flower.db*
 TestCase/
@@ -66,6 +65,9 @@ project.lock.json
 project.fragment.lock.json
 artifacts/
 
+# Tye
+.tye/
+
 # ASP.NET Scaffolding
 ScaffoldingReadMe.txt
 
@@ -94,7 +96,6 @@ StyleCopReport.xml
 *.tmp_proj
 *_wpftmp.csproj
 *.log
-*.tlog
 *.vspscc
 *.vssscc
 .builds
@@ -298,17 +299,6 @@ node_modules/
 # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
 *.vbw
 
-# Visual Studio 6 auto-generated project file (contains which files were open etc.)
-*.vbp
-
-# Visual Studio 6 workspace and project file (working project files containing files to include in project)
-*.dsw
-*.dsp
-
-# Visual Studio 6 technical files
-*.ncb
-*.aps
-
 # Visual Studio LightSwitch build output
 **/*.HTMLClient/GeneratedArtifacts
 **/*.DesktopClient/GeneratedArtifacts
@@ -365,9 +355,6 @@ ASALocalRun/
 # Local History for Visual Studio
 .localhistory/
 
-# Visual Studio History (VSHistory) files
-.vshistory/
-
 # BeatPulse healthcheck temp database
 healthchecksdb
 
@@ -380,24 +367,91 @@ MigrationBackup/
 # Fody - auto-generated XML schema
 FodyWeavers.xsd
 
-# VS Code files for those working on multiple tools
-.vscode/*
-!.vscode/settings.json
-!.vscode/tasks.json
-!.vscode/launch.json
-!.vscode/extensions.json
-*.code-workspace
+##
+## Visual studio for Mac
+##
 
-# Local History for Visual Studio Code
-.history/
 
-# Windows Installer files from build outputs
+# globs
+Makefile.in
+*.userprefs
+*.usertasks
+config.make
+config.status
+aclocal.m4
+install-sh
+autom4te.cache/
+*.tar.gz
+tarballs/
+test-results/
+
+# Mac bundle stuff
+*.dmg
+*.app
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
 *.cab
 *.msi
 *.msix
 *.msm
 *.msp
 
+# Windows shortcuts
+*.lnk
+
 # JetBrains Rider
+.idea/
 *.sln.iml
 
+##
+## Visual Studio Code
+##
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
diff --git a/Server/Constants.cs b/Server/Constants.cs
new file mode 100644
index 0000000..a920deb
--- /dev/null
+++ b/Server/Constants.cs
@@ -0,0 +1,135 @@
+using System.Text.Json.Serialization;
+
+namespace Blahblah.FlowerStory.Server;
+
+/// <summary>
+/// 常数类
+/// </summary>
+public sealed class Constants
+{
+    /// <summary>
+    /// 其他分类
+    /// </summary>
+    public const string CategoryOther = "other";
+    /// <summary>
+    /// 未知事件
+    /// </summary>
+    public const string EventUnknown = "unknown";
+
+    /// <summary>
+    /// 类别字典
+    /// </summary>
+    public static readonly Dictionary<int, NamedItem> Categories = new()
+    {
+        [0] = new(CategoryOther, "其他"),
+        [1] = new("cactus", "仙人球"),
+        [2] = new("hibiscus", "扶桑花"),
+        [3] = new("bougainvillea", "三角梅"),
+        [4] = new("sunflower", "太阳花"),
+        [5] = new("milanflower", "米兰花"),
+        [6] = new("jasmine", "茉莉花"),
+        [7] = new("periwinkle", "长春花"),
+        [8] = new("nasturtium", "旱金莲"),
+        [9] = new("mirabilis", "紫薇花"),
+        [10] = new("tigerthornplum", "虎刺梅"),
+        [11] = new("geranium", "天竺葵"),
+        [12] = new("ballorchid", "球兰"),
+        [13] = new("medalchrysanthemum", "勋章菊"),
+        [14] = new("dianthus", "石竹"),
+        [15] = new("fivecolorplum", "五色梅"),
+        [16] = new("fuchsia", "倒挂金钟"),
+        [17] = new("bamboocrabapple", "竹节海棠"),
+        [18] = new("impatiens", "凤仙花"),
+        [19] = new("beautysakura", "美女樱"),
+        [20] = new("petunias", "矮牵牛"),
+        [21] = new("desertrose", "沙漠玫瑰"),
+        [22] = new("trailingcampanula", "蔓性风铃花"),
+        [23] = new("chineserose", "月季花"),
+    };
+
+    /// <summary>
+    /// 事件字典
+    /// </summary>
+    public static readonly Dictionary<int, Event> Events = new()
+    {
+        [(int)EventTypes.Unknown] = new(EventUnknown, "未知"),
+        [(int)EventTypes.Buy] = new("buy", "购买", true),
+        [(int)EventTypes.Born] = new("born", "出生", true),
+        [3] = new("changebasin", "换盆"),
+        [4] = new("watering", "浇水"),
+        [5] = new("fertilize", "施肥"),
+        [6] = new("germination", "发芽"),
+        [7] = new("blossom", "开花"),
+        [8] = new("fallenleaves", "落叶"),
+        [9] = new("prune", "修剪"),
+        [10] = new("sick", "生病"),
+        [(int)EventTypes.Death] = new("death", "死亡", true),
+        [(int)EventTypes.Sell] = new("sell", "出售", true),
+        [(int)EventTypes.Share] = new("share", "分享"),
+    };
+}
+
+/// <summary>
+/// 事件类型枚举
+/// </summary>
+public enum EventTypes
+{
+    /// <summary>
+    /// 未知
+    /// </summary>
+    Unknown = 0,
+    /// <summary>
+    /// 购买
+    /// </summary>
+    Buy = 1,
+    /// <summary>
+    /// 出生
+    /// </summary>
+    Born = 2,
+    /// <summary>
+    /// 死亡
+    /// </summary>
+    Death = 11,
+    /// <summary>
+    /// 出售
+    /// </summary>
+    Sell = 12,
+    /// <summary>
+    /// 分享
+    /// </summary>
+    Share = 13,
+}
+
+/// <summary>
+/// 事件类
+/// </summary>
+/// <param name="Key">名称</param>
+/// <param name="Description">描述</param>
+/// <param name="Unique">是否唯一</param>
+public record Event(string Key, string? Description = null, bool Unique = false) : NamedItem(Key, Description)
+{
+    /// <summary>
+    /// 是否唯一
+    /// </summary>
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+    public bool Unique { get; init; } = Unique;
+}
+
+/// <summary>
+/// 命名对象类
+/// </summary>
+/// <param name="Key">名称</param>
+/// <param name="Description">描述</param>
+public record NamedItem(string Key, string? Description = null)
+{
+    /// <summary>
+    /// 名称
+    /// </summary>
+    public string Key { get; init; } = Key;
+
+    /// <summary>
+    /// 描述
+    /// </summary>
+    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+    public string? Description { get; init; } = Description;
+}
diff --git a/Server/Controller/ApiController.cs b/Server/Controller/ApiController.cs
new file mode 100644
index 0000000..e05a82a
--- /dev/null
+++ b/Server/Controller/ApiController.cs
@@ -0,0 +1,59 @@
+using Blahblah.FlowerStory.Server.Data;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Blahblah.FlowerStory.Server.Controller;
+
+/// <summary>
+/// 基础 API 服务
+/// </summary>
+[ApiController]
+[Produces("application/json")]
+[Route("api")]
+public partial class ApiController : BaseController
+{
+    /// <inheritdoc/>
+    public ApiController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
+    {
+    }
+
+    /// <summary>
+    /// 获取版本号
+    /// </summary>
+    /// <remarks>
+    /// 请求示例:
+    /// 
+    ///     GET /api/version
+    /// 
+    /// </remarks>
+    /// <returns>版本号</returns>
+    /// <response code="200">返回版本号</response>
+    [Route("version", Name = "getVersion")]
+    [HttpGet]
+    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
+    public ActionResult<string> GetApiVersion()
+    {
+        return Ok(Program.Version);
+    }
+
+    /// <summary>
+    /// 获取常量字典定义
+    /// </summary>
+    /// <remarks>
+    /// 请求示例:
+    /// 
+    ///     GET /api/consts?{ver}
+    /// 
+    /// </remarks>
+    /// <returns>字典集</returns>
+    /// <response code="200">返回常量字典集</response>
+    [Route("consts", Name = "getConsts")]
+    [HttpGet]
+    public ActionResult<DefinitionResult> GetDefinitions()
+    {
+        return Ok(new DefinitionResult
+        {
+            Categories = Constants.Categories,
+            Events = Constants.Events,
+        });
+    }
+}
diff --git a/Server/Controller/ApiController.structs.cs b/Server/Controller/ApiController.structs.cs
new file mode 100644
index 0000000..c6f8711
--- /dev/null
+++ b/Server/Controller/ApiController.structs.cs
@@ -0,0 +1,17 @@
+namespace Blahblah.FlowerStory.Server.Controller;
+
+/// <summary>
+/// 字典、定义
+/// </summary>
+public record DefinitionResult
+{
+    /// <summary>
+    /// 花草分类
+    /// </summary>
+    public required Dictionary<int, NamedItem> Categories { get; init; }
+
+    /// <summary>
+    /// 事件
+    /// </summary>
+    public required Dictionary<int, Event> Events { get; init; }
+}
\ No newline at end of file
diff --git a/Server/Controller/BaseController.cs b/Server/Controller/BaseController.cs
index 2a30ed2..742b99e 100644
--- a/Server/Controller/BaseController.cs
+++ b/Server/Controller/BaseController.cs
@@ -15,6 +15,17 @@ public abstract partial class BaseController : ControllerBase
 {
     private const string Salt = "Blah blah, o! Flower story, intimately help you record every bit of the garden.";
 
+    /// <summary>
+    /// 临时 id
+    /// </summary>
+    protected const int TempId = -1;
+
+    private static string? uploadsDirectory;
+    /// <summary>
+    /// 文件上传路径
+    /// </summary>
+    protected static string UploadsDirectory => uploadsDirectory ??= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads");
+
     /// <summary>
     /// 支持的图片文件签名
     /// </summary>
@@ -195,43 +206,52 @@ public abstract partial class BaseController : ControllerBase
     /// <returns>文件结果对象</returns>
     protected static FileResult? WrapFormFile(IFormFile file)
     {
-        if (file == null)
+        if (file?.Length > 0)
         {
-            return null;
-        }
-        using var stream = file.OpenReadStream();
+            using var stream = file.OpenReadStream();
 
-        // check header
-        var headers = new byte[PhotoSignatures.Max(s => s.Length)];
-        int count = stream.Read(headers, 0, headers.Length);
-        if (PhotoSignatures.Any(s => headers.Take(s.Length).SequenceEqual(s)))
-        {
-            using var ms = new MemoryStream();
-            ms.Write(headers, 0, count);
-
-            // reading
-            const int size = 16384;
-            var buffer = new byte[size];
-            while ((count = stream.Read(buffer, 0, size)) > 0)
+            // check header
+            var headers = new byte[PhotoSignatures.Max(s => s.Length)];
+            int count = stream.Read(headers, 0, headers.Length);
+            if (PhotoSignatures.Any(s => headers.Take(s.Length).SequenceEqual(s)))
             {
-                ms.Write(buffer, 0, count);
+                using var ms = new MemoryStream();
+                ms.Write(headers, 0, count);
+
+                // reading
+                const int size = 16384;
+                var buffer = new byte[size];
+                while ((count = stream.Read(buffer, 0, size)) > 0)
+                {
+                    ms.Write(buffer, 0, count);
+                }
+                var data = ms.ToArray();
+                var name = file.FileName;
+                var ext = Path.GetExtension(name);
+                var path = $"{WebUtility.UrlEncode(Path.GetFileNameWithoutExtension(name))}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}{ext}";
+
+                return new FileResult
+                {
+                    Filename = name,
+                    FileType = ext,
+                    Path = path,
+                    Content = data
+                };
             }
-            var data = ms.ToArray();
-            var name = file.FileName;
-            var ext = Path.GetExtension(name);
-            var path = $"{WebUtility.UrlEncode(Path.GetFileNameWithoutExtension(name))}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}{ext}";
-
-            return new FileResult
-            {
-                Filename = name,
-                FileType = ext,
-                Path = path,
-                Content = data
-            };
         }
         return null;
     }
 
+    private static string GetFlowerDirectory(int fid, bool create = false)
+    {
+        var directory = Path.Combine(UploadsDirectory, fid.ToString());
+        if (create && !Directory.Exists(directory))
+        {
+            Directory.CreateDirectory(directory);
+        }
+        return directory;
+    }
+
     /// <summary>
     /// 写入文件到用户的花草目录中
     /// </summary>
@@ -240,11 +260,7 @@ public abstract partial class BaseController : ControllerBase
     /// <param name="token">取消令牌</param>
     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))
-        {
-            Directory.CreateDirectory(directory);
-        }
+        var directory = GetFlowerDirectory(fid, true);
         var path = Path.Combine(directory, file.Path);
         await System.IO.File.WriteAllBytesAsync(path, file.Content, token);
     }
@@ -257,7 +273,7 @@ public abstract partial class BaseController : ControllerBase
     /// <returns>返回是否已删除</returns>
     protected static bool DeleteFile(int fid, string path)
     {
-        var directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads", fid.ToString());
+        var directory = GetFlowerDirectory(fid);
         if (Directory.Exists(directory))
         {
             path = Path.Combine(directory, path);
@@ -270,6 +286,30 @@ public abstract partial class BaseController : ControllerBase
         return false;
     }
 
+    /// <summary>
+    /// 移动临时照片到花草目录
+    /// </summary>
+    /// <param name="fid">花草唯一 id</param>
+    /// <param name="path">文件路径</param>
+    protected static void MoveTempFileToFlower(int fid, string path)
+    {
+        var directory = GetFlowerDirectory(TempId);
+        if (Directory.Exists(directory))
+        {
+            var file = Path.Combine(directory, path);
+            if (System.IO.File.Exists(file))
+            {
+                directory = GetFlowerDirectory(fid, true);
+                var to = Path.Combine(directory, path);
+                if (System.IO.File.Exists(to))
+                {
+                    System.IO.File.Move(to, $"{to}.bak");
+                }
+                System.IO.File.Move(file, to);
+            }
+        }
+    }
+
     private const double EarthRadius = 6378137;
 
     private static double Radius(double degree)
diff --git a/Server/Controller/EventApiController.cs b/Server/Controller/EventApiController.cs
index 694b1dd..2330794 100644
--- a/Server/Controller/EventApiController.cs
+++ b/Server/Controller/EventApiController.cs
@@ -52,6 +52,7 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpGet]
     [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
     public ActionResult<RecordItem[]> GetRecords(
@@ -126,6 +127,7 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpDelete]
     public ActionResult<int> RemoveEvent([FromQuery][Required] int id)
     {
@@ -170,6 +172,7 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPost]
     [Consumes("application/json")]
     public ActionResult<int> RemoveEvents([FromBody] int[] ids)
@@ -199,12 +202,16 @@ public class EventApiController : BaseController
     /// 
     ///     POST /api/event/add
     ///     Authorization: authorization id
-    ///     {
-    ///         "flowerId": 1,
-    ///         "eventId": 4,   // 浇水
-    ///         "byUser": "朋友",
-    ///         "memo": "快干死了"
-    ///     }
+    /// 
+    /// 参数:
+    /// 
+    ///     flowerId: 1
+    ///     eventId": 4   // 浇水
+    ///     byUser: "朋友"
+    ///     memo: "快干死了"
+    ///     lon: 29.5462794
+    ///     lat: 106.5380034
+    ///     photos: &lt;photo&gt;[]
     /// 
     /// </remarks>
     /// <param name="event">事件参数</param>
@@ -218,9 +225,12 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPost]
-    [Consumes("application/json")]
-    public ActionResult<RecordItem> AddEvent([FromBody] EventParameter @event)
+    [Consumes("multipart/form-data")]
+    // 5 photos
+    [RequestSizeLimit(75 * 1024 * 1024)]
+    public async Task<ActionResult<RecordItem>> AddEvent([FromForm] EventParameter @event)
     {
         var (result, user) = CheckPermission();
         if (result != null)
@@ -232,19 +242,58 @@ public class EventApiController : BaseController
             return NotFound();
         }
 
+        var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
         var item = new RecordItem
         {
             OwnerId = user.Id,
             FlowerId = @event.FlowerId,
-            EventId = @event.EventId,
-            DateUnixTime = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
+            EventId = @event.CategoryId,
+            DateUnixTime = now,
             ByUserId = @event.ByUser == null ? user.Id : null,
             ByUserName = @event.ByUser,
-            Memo = @event.Memo
+            Memo = @event.Memo,
+            Latitude = @event.Latitude,
+            Longitude = @event.Longitude
         };
         database.Records.Add(item);
         SaveDatabase();
 
+        if (@event.Photos?.Length > 0)
+        {
+            try
+            {
+                await ExecuteTransaction(async token =>
+                {
+                    foreach (var photo in @event.Photos)
+                    {
+                        var file = WrapFormFile(photo);
+                        if (file == null)
+                        {
+                            continue;
+                        }
+                        var p = new PhotoItem
+                        {
+                            OwnerId = user.Id,
+                            FlowerId = @event.FlowerId,
+                            RecordId = item.Id,
+                            FileType = file.FileType,
+                            FileName = file.Filename,
+                            Path = file.Path,
+                            DateUploadUnixTime = now
+                        };
+                        AddPhotoItem(p);
+
+                        await WriteToFile(@event.FlowerId, file, token);
+                    }
+                });
+            }
+            catch (Exception ex)
+            {
+                return Problem(ex.ToString(), "api/event/add");
+                // TODO: Logger
+            }
+        }
+
         return Ok(item);
     }
 
@@ -256,13 +305,17 @@ public class EventApiController : BaseController
     /// 
     ///     PUT /api/event/update
     ///     Authorization: authorization id
-    ///     {
-    ///         "id": 1,
-    ///         "flowerId": 1,
-    ///         "eventId": 5,   // 施肥
-    ///         "byUser": null,
-    ///         "memo": "花多多1号"
-    ///     }
+    /// 
+    /// 参数:
+    /// 
+    ///     id: 1
+    ///     flowerId: 1
+    ///     eventId": 5   // 施肥
+    ///     byUser: null
+    ///     memo: "花多多1号"
+    ///     lon: 29.5462794
+    ///     lat: 106.5380034
+    ///     photos: &lt;photo&gt;[]
     /// 
     /// </remarks>
     /// <param name="update">修改参数</param>
@@ -276,9 +329,12 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPut]
-    [Consumes("application/json")]
-    public ActionResult<RecordItem> Update([FromBody] EventUpdateParameter update)
+    [Consumes("multipart/form-data")]
+    // 5 photos
+    [RequestSizeLimit(75 * 1024 * 1024)]
+    public async Task<ActionResult<RecordItem>> Update([FromForm] EventUpdateParameter update)
     {
         var (result, user) = CheckPermission();
         if (result != null)
@@ -295,8 +351,10 @@ public class EventApiController : BaseController
         {
             return NotFound($"Event id {update.Id} not found");
         }
+        var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
         record.FlowerId = update.FlowerId;
-        record.EventId = update.EventId;
+        record.EventId = update.CategoryId;
+        record.DateUnixTime = now;
         if (update.ByUser == null)
         {
             record.ByUserId = user.Id;
@@ -308,7 +366,60 @@ public class EventApiController : BaseController
             record.ByUserName = update.ByUser;
         }
         record.Memo = update.Memo;
-        SaveDatabase();
+        record.Latitude = update.Latitude;
+        record.Longitude = update.Longitude;
+
+        if (update.Photos?.Length > 0)
+        {
+            try
+            {
+                await ExecuteTransaction(async token =>
+                {
+                    foreach (var photo in update.Photos)
+                    {
+                        var file = WrapFormFile(photo);
+                        if (file == null)
+                        {
+                            continue;
+                        }
+
+                        var photos = database.Photos.Where(p => p.RecordId == record.Id).ToList();
+                        if (photos.Count > 0)
+                        {
+                            database.Photos.Where(p => p.RecordId == record.Id).ExecuteDelete();
+                            foreach (var p in photos)
+                            {
+                                DeleteFile(update.FlowerId, p.Path);
+                            }
+                        }
+                        SaveDatabase();
+
+                        var cover = new PhotoItem
+                        {
+                            OwnerId = user.Id,
+                            FlowerId = update.FlowerId,
+                            RecordId = record.Id,
+                            FileType = file.FileType,
+                            FileName = file.Filename,
+                            Path = file.Path,
+                            DateUploadUnixTime = now
+                        };
+                        AddPhotoItem(cover);
+
+                        await WriteToFile(update.FlowerId, file, token);
+                    }
+                });
+            }
+            catch (Exception ex)
+            {
+                return Problem(ex.ToString(), "api/event/update");
+                // TODO: Logger
+            }
+        }
+        else
+        {
+            SaveDatabase();
+        }
 
         return Ok(user);
     }
@@ -344,6 +455,7 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPost]
     [Consumes("multipart/form-data")]
     [RequestSizeLimit(15 * 1024 * 1024)]
@@ -380,6 +492,7 @@ public class EventApiController : BaseController
                 {
                     var p = new PhotoItem
                     {
+                        OwnerId = user.Id,
                         FlowerId = record.FlowerId,
                         RecordId = id,
                         FileType = file.FileType,
@@ -433,6 +546,7 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPost]
     [Consumes("multipart/form-data")]
     // 5 photos
@@ -474,6 +588,7 @@ public class EventApiController : BaseController
 
                         var p = new PhotoItem
                         {
+                            OwnerId = user.Id,
                             FlowerId = record.FlowerId,
                             RecordId = id,
                             FileType = file.FileType,
@@ -526,6 +641,7 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpDelete]
     public ActionResult<int> RemoveEventPhoto([FromQuery][Required] int id)
     {
@@ -539,12 +655,13 @@ public class EventApiController : BaseController
             return NotFound();
         }
 
-        var photo = database.Photos.Where(p => p.Id == id).Include(p => p.Record).SingleOrDefault();
+        var photo = database.Photos.Find(id);
         if (photo == null)
         {
             return NotFound();
         }
-        if (photo.Record != null && photo.Record.OwnerId != user.Id)
+        var item = database.Flowers.Find(photo.FlowerId);
+        if (item == null || item.OwnerId != user.Id)
         {
             return Unauthorized();
         }
@@ -553,10 +670,7 @@ public class EventApiController : BaseController
 
         SaveDatabase();
 
-        if (photo.Record != null)
-        {
-            DeleteFile(photo.Record.FlowerId, photo.Path);
-        }
+        DeleteFile(photo.FlowerId ?? TempId, photo.Path);
 
         return NoContent();
     }
@@ -585,6 +699,7 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPost]
     [Consumes("application/json")]
     public ActionResult<int> RemoveEventPhotos([FromBody] int[] ids)
@@ -604,17 +719,14 @@ public class EventApiController : BaseController
             return Unauthorized();
         }
 
-        var photos = database.Photos.Where(p => ids.Contains(p.Id)).Include(p => p.Record).ToList();
+        var photos = database.Photos.Where(p => ids.Contains(p.Id)).ToList();
         var count = database.Photos.Where(p => ids.Contains(p.Id)).ExecuteDelete();
 
         SaveDatabase();
 
         foreach (var photo in photos)
         {
-            if (photo.Record != null)
-            {
-                DeleteFile(photo.Record.FlowerId, photo.Path);
-            }
+            DeleteFile(photo.FlowerId ?? TempId, photo.Path);
         }
 
         return Ok(count);
@@ -645,6 +757,7 @@ public class EventApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpGet]
     [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
     public ActionResult<PhotoItem[]> GetPhotos([Required][FromQuery] int id)
diff --git a/Server/Controller/EventApiController.structs.cs b/Server/Controller/EventApiController.structs.cs
index 4244f69..64378e2 100644
--- a/Server/Controller/EventApiController.structs.cs
+++ b/Server/Controller/EventApiController.structs.cs
@@ -1,4 +1,5 @@
-using System.ComponentModel.DataAnnotations;
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
 
 namespace Blahblah.FlowerStory.Server.Controller;
 
@@ -11,24 +12,46 @@ public record EventParameter
     /// 花草唯一 id
     /// </summary>
     [Required]
-    public int FlowerId { get; init; }
+    [FromForm(Name = "flowerId")]
+    public required int FlowerId { get; init; }
 
     /// <summary>
-    /// 事件 id
+    /// 事件分类 id
     /// </summary>
     [Required]
-    public int EventId { get; init; }
+    [FromForm(Name = "categoryId")]
+    public required int CategoryId { get; init; }
 
     /// <summary>
     /// 操作人姓名
     /// </summary>
     [Required]
-    public string? ByUser { get; init; }
+    [FromForm(Name = "byUser")]
+    public required string ByUser { get; init; }
 
     /// <summary>
-    /// 事件备注
+    /// 备注
     /// </summary>
-    public string? Memo { get; init; }
+    [FromForm(Name = "memo")]
+    public string? Memo { get; set; }
+
+    /// <summary>
+    /// 纬度
+    /// </summary>
+    [FromForm(Name = "lat")]
+    public double? Latitude { get; set; }
+
+    /// <summary>
+    /// 经度
+    /// </summary>
+    [FromForm(Name = "lon")]
+    public double? Longitude { get; set; }
+
+    /// <summary>
+    /// 关联的照片
+    /// </summary>
+    [FromForm(Name = "photos")]
+    public IFormFile[]? Photos { get; init; }
 }
 
 /// <summary>
@@ -40,5 +63,6 @@ public record EventUpdateParameter : EventParameter
     /// 事件唯一 id
     /// </summary>
     [Required]
-    public int Id { get; set; }
+    [FromForm(Name = "id")]
+    public required int Id { get; set; }
 }
diff --git a/Server/Controller/FlowerApiController.cs b/Server/Controller/FlowerApiController.cs
index 129a9f1..2ced828 100644
--- a/Server/Controller/FlowerApiController.cs
+++ b/Server/Controller/FlowerApiController.cs
@@ -2,9 +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;
 
 namespace Blahblah.FlowerStory.Server.Controller;
 
@@ -68,6 +66,7 @@ public class FlowerApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpGet]
     [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
     public ActionResult<FlowerResult> GetFlowers(
@@ -96,7 +95,88 @@ public class FlowerApiController : BaseController
 
         SaveDatabase();
 
-        IEnumerable<FlowerItem> flowers = database.Flowers.Where(f => f.OwnerId == user.Id);
+        var flowers = GetFlowerResult(user.Id, null, categoryId, key, buyFrom, buyTo, costFrom, costTo, includePhoto, latitude, longitude, distance, page, pageSize);
+        return Ok(flowers);
+    }
+
+    /// <summary>
+    /// 获取最新、最近的花草
+    /// </summary>
+    /// <remarks>
+    /// 请求示例:
+    /// 
+    ///     GET /api/flower/latest
+    /// 
+    /// 参数:
+    /// 
+    ///     cid: int?
+    ///     key: string?
+    ///     from: long?
+    ///     to: long?
+    ///     cfrom: decimal?
+    ///     cto: decimal?
+    ///     photo: bool?
+    ///     lon: double?
+    ///     lat: double?
+    ///     distance: int?
+    ///     p: int?
+    ///     size: int?
+    /// 
+    /// </remarks>
+    /// <param name="categoryId">类别 id</param>
+    /// <param name="key">查询关键字</param>
+    /// <param name="buyFrom">起始购买日期</param>
+    /// <param name="buyTo">结束购买日期</param>
+    /// <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>
+    /// <response code="200">返回符合条件的花草集</response>
+    /// <response code="401">未找到登录会话或已过期</response>
+    /// <response code="403">用户已禁用</response>
+    /// <response code="404">未找到关联用户</response>
+    [Route("latest", Name = "queryLatestFlowers")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [HttpGet]
+    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
+    public ActionResult<FlowerResult> GetLatestFlowers(
+        [FromQuery(Name = "cid")] int? categoryId,
+        [FromQuery] string? key,
+        [FromQuery(Name = "from")] long? buyFrom,
+        [FromQuery(Name = "to")] long? buyTo,
+        [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)
+    {
+        var result = GetFlowerResult(null, (int)EventTypes.Share, categoryId, key, buyFrom, buyTo, costFrom, costTo, includePhoto, latitude, longitude, distance, page, pageSize);
+
+        return Ok(result);
+    }
+
+    private FlowerResult GetFlowerResult(int? userId, int? eventId, int? categoryId, string? key,
+        long? buyFrom, long? buyTo, decimal? costFrom, decimal? costTo,
+        bool? includePhoto, double? latitude, double? longitude, int? distance,
+        int? page = 0, int? pageSize = 20)
+    {
+        IEnumerable<FlowerItem> flowers;
+        if (userId != null)
+        {
+            flowers = database.Flowers.Where(f => f.OwnerId == userId);
+        }
+        else
+        {
+            flowers = database.Flowers;
+        }
         if (categoryId != null)
         {
             flowers = flowers.Where(f => f.CategoryId == categoryId);
@@ -125,6 +205,11 @@ public class FlowerApiController : BaseController
             flowers = flowers.Where(f => f.Cost != null && f.Cost <= costTo);
         }
 
+        if (eventId != null)
+        {
+            flowers = flowers.Where(f => database.Records.Any(r => r.FlowerId == f.Id && r.EventId == eventId));
+        }
+
         if (distance != null && latitude != null && longitude != null)
         {
             flowers = flowers.Where(f => f.Latitude != null && f.Longitude != null)
@@ -157,11 +242,11 @@ public class FlowerApiController : BaseController
             }
         }
 
-        return Ok(new FlowerResult
+        return new()
         {
             Flowers = flowers.ToArray(),
             Count = count
-        });
+        };
     }
 
     /// <summary>
@@ -191,6 +276,7 @@ public class FlowerApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpGet]
     [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
     public ActionResult<FlowerItem> GetFlower(
@@ -254,6 +340,7 @@ public class FlowerApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpDelete]
     public ActionResult<int> RemoveFlower([FromQuery][Required] int id)
     {
@@ -299,9 +386,10 @@ public class FlowerApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPost]
     [Consumes("application/json")]
-    public ActionResult<int> RemoveFlower([FromBody] int[] ids)
+    public ActionResult<int> RemoveFlowers([FromBody] int[] ids)
     {
         var (result, user) = CheckPermission();
         if (result != null)
@@ -337,22 +425,29 @@ public class FlowerApiController : BaseController
     ///     dateBuy: 1684919954743
     ///     cost: 5.00
     ///     purchase: "花鸟市场"
+    ///     memo: "备注信息"
+    ///     lon: 29.5462794
+    ///     lat: 106.5380034
+    ///     cid: 1
     ///     cover: &lt;photo&gt;
     /// 
     /// </remarks>
     /// <param name="flower">花草参数</param>
     /// <returns>添加成功则返回已添加的花草对象</returns>
     /// <response code="200">返回已添加的花草对象</response>
+    /// <response code="400">提交的内容不合法</response>
     /// <response code="401">未找到登录会话或已过期</response>
     /// <response code="403">用户已禁用</response>
     /// <response code="404">未找到关联用户</response>
     /// <response code="413">提交正文过大</response>
     [Route("add", Name = "addFlower")]
     [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPost]
     [Consumes("multipart/form-data")]
     [RequestSizeLimit(5 * 1024 * 1024)]
@@ -375,11 +470,15 @@ public class FlowerApiController : BaseController
             Name = flower.Name,
             DateBuyUnixTime = flower.DateBuy,
             Cost = flower.Cost,
-            Purchase = flower.Purchase
+            Purchase = flower.Purchase,
+            Memo = flower.Memo,
+            Latitude = flower.Latitude,
+            Longitude = flower.Longitude
         };
         database.Flowers.Add(item);
         SaveDatabase();
 
+        var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
         if (flower.Cover?.Length > 0)
         {
             var file = WrapFormFile(flower.Cover);
@@ -388,7 +487,6 @@ public class FlowerApiController : BaseController
                 return BadRequest();
             }
 
-            var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
             var record = database.Records.SingleOrDefault(r => r.FlowerId == item.Id && r.EventId == EventCover);
             if (record == null)
             {
@@ -399,8 +497,10 @@ public class FlowerApiController : BaseController
                     EventId = EventCover,
                     DateUnixTime = now,
                     ByUserId = user.Id,
-                    ByUserName = user.Name
-                    //Memo = ""
+                    ByUserName = user.Name,
+                    //Memo = flower.Memo,
+                    Latitude = flower.Latitude,
+                    Longitude = flower.Longitude
                 };
                 database.Records.Add(record);
             }
@@ -412,6 +512,7 @@ public class FlowerApiController : BaseController
                 {
                     var cover = new PhotoItem
                     {
+                        OwnerId = user.Id,
                         FlowerId = item.Id,
                         RecordId = record.Id,
                         FileType = file.FileType,
@@ -426,14 +527,135 @@ public class FlowerApiController : BaseController
             }
             catch (Exception ex)
             {
-                return Problem(ex.ToString(), "api/flower/add");
+                return Problem(ex.ToString(), "api/flower/add#WriteToFile");
                 // TODO: Logger
             }
         }
+        else if (flower.CoverId is int coverId)
+        {
+            var photo = database.Photos.SingleOrDefault(p => p.Id == coverId && p.OwnerId == user.Id);
+            if (photo != null)
+            {
+                var record = database.Records.SingleOrDefault(r => r.FlowerId == item.Id && r.EventId == EventCover);
+                if (record == null)
+                {
+                    record = new RecordItem
+                    {
+                        OwnerId = user.Id,
+                        FlowerId = item.Id,
+                        EventId = EventCover,
+                        DateUnixTime = now,
+                        ByUserId = user.Id,
+                        ByUserName = user.Name,
+                        //Memo = flower.Memo,
+                        Latitude = flower.Latitude,
+                        Longitude = flower.Longitude
+                    };
+                    database.Records.Add(record);
+                    SaveDatabase();
+                }
+
+                photo.FlowerId = item.Id;
+                photo.RecordId = record.Id;
+                SaveDatabase();
+
+                try
+                {
+                    MoveTempFileToFlower(item.Id, photo.Path);
+                }
+                catch (Exception ex)
+                {
+                    return Problem(ex.ToString(), "api/flower/add#MoveTempFileToFlower");
+                    // TODO: Logger
+                }
+            }
+        }
 
         return Ok(item);
     }
 
+    /// <summary>
+    /// 用户上传封面
+    /// </summary>
+    /// <remarks>
+    /// 请求示例:
+    /// 
+    ///     POST /api/flower/cover_upload
+    ///     Authorization: authorization id
+    /// 
+    /// 参数:
+    /// 
+    ///     cover: &lt;photo&gt;
+    /// 
+    /// </remarks>
+    /// <param name="p">封面参数</param>
+    /// <returns>添加成功则返回封面 id</returns>
+    /// <response code="200">返回已添加的封面 id</response>
+    /// <response code="400">提交的内容不合法</response>
+    /// <response code="401">未找到登录会话或已过期</response>
+    /// <response code="403">用户已禁用</response>
+    /// <response code="404">未找到关联用户</response>
+    /// <response code="413">提交正文过大</response>
+    [Route("cover_upload", Name = "uploadCover")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
+    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+    [ProducesResponseType(StatusCodes.Status403Forbidden)]
+    [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
+    [HttpPost]
+    [Consumes("multipart/form-data")]
+    [RequestSizeLimit(5 * 1024 * 1024)]
+    public async Task<ActionResult<int>> UploadCover([FromForm] CoverParameter p)
+    {
+        var (result, user) = CheckPermission();
+        if (result != null)
+        {
+            return result;
+        }
+        if (user == null)
+        {
+            return NotFound();
+        }
+
+        if (p.Cover?.Length > 0)
+        {
+            var file = WrapFormFile(p.Cover);
+            if (file == null)
+            {
+                return BadRequest();
+            }
+
+            var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
+
+            try
+            {
+                await WriteToFile(TempId, file);
+
+                var item = new PhotoItem
+                {
+                    OwnerId = user.Id,
+                    FileType = file.FileType,
+                    FileName = file.Filename,
+                    Path = file.Path,
+                    DateUploadUnixTime = now
+                };
+                database.Photos.Add(item);
+                SaveDatabase();
+
+                return Ok(item.Id);
+            }
+            catch (Exception ex)
+            {
+                return Problem(ex.ToString(), "api/flower/cover_upload");
+                // TODO: Logger
+            }
+        }
+
+        return BadRequest();
+    }
+
     /// <summary>
     /// 修改花草
     /// </summary>
@@ -451,22 +673,28 @@ public class FlowerApiController : BaseController
     ///     dateBuy: 1684935276117
     ///     cost: 15.40
     ///     purchase: null
+    ///     memo: "备注"
+    ///     lon: 29.5462794
+    ///     lat: 106.5380034
     ///     cover: &lt;photo&gt;
     /// 
     /// </remarks>
     /// <param name="update">修改参数</param>
     /// <returns>修改成功则返回已修改的花草对象</returns>
     /// <response code="200">返回已修改的花草对象</response>
+    /// <response code="400">提交的内容不合法</response>
     /// <response code="401">未找到登录会话或已过期</response>
     /// <response code="403">用户已禁用</response>
     /// <response code="404">未找到关联用户或者未找到将修改的花草对象</response>
     /// <response code="413">提交正文过大</response>
     [Route("update", Name = "updateFlower")]
     [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status400BadRequest)]
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPut]
     [Consumes("multipart/form-data")]
     [RequestSizeLimit(5 * 1024 * 1024)]
@@ -492,6 +720,9 @@ public class FlowerApiController : BaseController
         flower.DateBuyUnixTime = update.DateBuy;
         flower.Cost = update.Cost;
         flower.Purchase = update.Purchase;
+        flower.Memo = update.Memo;
+        flower.Latitude = update.Latitude;
+        flower.Longitude = update.Longitude;
 
         if (update.Cover?.Length > 0)
         {
@@ -512,8 +743,10 @@ public class FlowerApiController : BaseController
                     EventId = EventCover,
                     DateUnixTime = now,
                     ByUserId = user.Id,
-                    ByUserName = user.Name
-                    //Memo = ""
+                    ByUserName = user.Name,
+                    //Memo = flower.Memo,
+                    Latitude = flower.Latitude,
+                    Longitude = flower.Longitude
                 };
                 database.Records.Add(record);
             }
@@ -537,6 +770,7 @@ public class FlowerApiController : BaseController
                 {
                     var cover = new PhotoItem
                     {
+                        OwnerId = user.Id,
                         FlowerId = update.Id,
                         RecordId = record.Id,
                         FileType = file.FileType,
@@ -575,11 +809,12 @@ public class FlowerApiController : BaseController
     /// 参数:
     /// 
     ///     id: int
+    ///     lon: double?
+    ///     lat: double?
     ///     photo: IFormFile
     /// 
     /// </remarks>
-    /// <param name="id">花草唯一 id</param>
-    /// <param name="photo">封面图片</param>
+    /// <param name="param">封面修改参数</param>
     /// <returns>修改成功则返回 HTTP 204</returns>
     /// <response code="204">修改成功</response>
     /// <response code="400">照片格式非法</response>
@@ -594,10 +829,11 @@ public class FlowerApiController : BaseController
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
     [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpPost]
     [Consumes("multipart/form-data")]
     [RequestSizeLimit(5 * 1024 * 1024)]
-    public async Task<ActionResult> UploadCovers([Required][FromQuery] int id, [Required] IFormFile photo)
+    public async Task<ActionResult> UploadCovers([FromForm] FlowerCoverParameter param)
     {
         var (result, user) = CheckPermission();
         if (result != null)
@@ -609,35 +845,40 @@ public class FlowerApiController : BaseController
             return NotFound();
         }
 
-        var flower = database.Flowers.SingleOrDefault(f => f.Id == id && f.OwnerId == user.Id);
+        var flower = database.Flowers.SingleOrDefault(f => f.Id == param.Id && f.OwnerId == user.Id);
         if (flower == null)
         {
-            return NotFound($"Flower id {id} not found");
+            return NotFound($"Flower id {param.Id} not found");
         }
-        if (photo.Length > 0)
+        if (param.Cover?.Length > 0)
         {
-            var file = WrapFormFile(photo);
+            var file = WrapFormFile(param.Cover);
             if (file == null)
             {
                 return BadRequest();
             }
 
             var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
-            var record = database.Records.SingleOrDefault(r => r.FlowerId == id && r.EventId == EventCover);
+            var record = database.Records.SingleOrDefault(r => r.FlowerId == param.Id && r.EventId == EventCover);
             if (record == null)
             {
                 record = new RecordItem
                 {
                     OwnerId = user.Id,
-                    FlowerId = id,
+                    FlowerId = param.Id,
                     EventId = EventCover,
                     DateUnixTime = now,
                     ByUserId = user.Id,
-                    ByUserName = user.Name
-                    //Memo = ""
+                    ByUserName = user.Name,
+                    //Memo = "",
+                    Latitude = param.Latitude,
+                    Longitude = param.Longitude
                 };
                 database.Records.Add(record);
             }
+
+            flower.Latitude = param.Latitude;
+            flower.Longitude = param.Longitude;
             SaveDatabase();
 
             try
@@ -646,7 +887,8 @@ public class FlowerApiController : BaseController
                 {
                     var cover = new PhotoItem
                     {
-                        FlowerId = id,
+                        OwnerId = user.Id,
+                        FlowerId = param.Id,
                         RecordId = record.Id,
                         FileType = file.FileType,
                         FileName = file.Filename,
@@ -655,7 +897,7 @@ public class FlowerApiController : BaseController
                     };
                     AddPhotoItem(cover);
 
-                    await WriteToFile(id, file, token);
+                    await WriteToFile(param.Id, file, token);
                 });
             }
             catch (Exception ex)
@@ -695,6 +937,7 @@ public class FlowerApiController : BaseController
     [ProducesResponseType(StatusCodes.Status401Unauthorized)]
     [ProducesResponseType(StatusCodes.Status403Forbidden)]
     [ProducesResponseType(StatusCodes.Status404NotFound)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
     [HttpGet]
     [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
     public ActionResult<PhotoItem[]> GetCovers([Required][FromQuery] int id, [FromQuery(Name = "eid")] int? eventId = 0)
diff --git a/Server/Controller/FlowerApiController.structs.cs b/Server/Controller/FlowerApiController.structs.cs
index 62268ba..44d5277 100644
--- a/Server/Controller/FlowerApiController.structs.cs
+++ b/Server/Controller/FlowerApiController.structs.cs
@@ -4,17 +4,35 @@ using System.ComponentModel.DataAnnotations;
 
 namespace Blahblah.FlowerStory.Server.Controller;
 
+/// <summary>
+/// 封面参数
+/// </summary>
+public record CoverParameter
+{
+    /// <summary>
+    /// 封面
+    /// </summary>
+    [FromForm(Name = "cover")]
+    public IFormFile? Cover { get; init; }
+
+    /// <summary>
+    /// 封面 id
+    /// </summary>
+    [FromForm(Name = "cid")]
+    public int? CoverId { get; init; }
+}
+
 /// <summary>
 /// 花草参数
 /// </summary>
-public record FlowerParameter
+public record FlowerParameter : CoverParameter
 {
     /// <summary>
     /// 类别 id
     /// </summary>
     [Required]
     [FromForm(Name = "categoryId")]
-    public int CategoryId { get; init; }
+    public required int CategoryId { get; init; }
 
     /// <summary>
     /// 花草名称
@@ -28,7 +46,7 @@ public record FlowerParameter
     /// </summary>
     [Required]
     [FromForm(Name = "dateBuy")]
-    public long DateBuy { get; init; }
+    public required long DateBuy { get; init; }
 
     /// <summary>
     /// 购买花费
@@ -43,10 +61,22 @@ public record FlowerParameter
     public string? Purchase { get; init; }
 
     /// <summary>
-    /// 花草封面
+    /// 备注
     /// </summary>
-    [FromForm(Name = "cover")]
-    public IFormFile? Cover { get; init; }
+    [FromForm(Name = "memo")]
+    public string? Memo { get; set; }
+
+    /// <summary>
+    /// 纬度
+    /// </summary>
+    [FromForm(Name = "lat")]
+    public double? Latitude { get; set; }
+
+    /// <summary>
+    /// 经度
+    /// </summary>
+    [FromForm(Name = "lon")]
+    public double? Longitude { get; set; }
 }
 
 /// <summary>
@@ -59,7 +89,32 @@ public record FlowerUpdateParameter : FlowerParameter
     /// </summary>
     [Required]
     [FromForm(Name = "id")]
-    public int Id { get; init; }
+    public required int Id { get; init; }
+}
+
+/// <summary>
+/// 花草封面修改参数
+/// </summary>
+public record FlowerCoverParameter : CoverParameter
+{
+    /// <summary>
+    /// 花草唯一 id
+    /// </summary>
+    [Required]
+    [FromForm(Name = "id")]
+    public required int Id { get; init; }
+
+    /// <summary>
+    /// 纬度
+    /// </summary>
+    [FromForm(Name = "lat")]
+    public double? Latitude { get; set; }
+
+    /// <summary>
+    /// 经度
+    /// </summary>
+    [FromForm(Name = "lon")]
+    public double? Longitude { get; set; }
 }
 
 /// <summary>
diff --git a/Server/Controller/UserApiController.cs b/Server/Controller/UserApiController.cs
index 2f81e30..3c1d0ec 100644
--- a/Server/Controller/UserApiController.cs
+++ b/Server/Controller/UserApiController.cs
@@ -103,6 +103,44 @@ public partial class UserApiController : BaseController
         return NoContent();
     }
 
+    /// <summary>
+    /// 判断当前会话是否有效
+    /// </summary>
+    /// <remarks>
+    /// 请求示例:
+    /// 
+    ///     GET /api/user/validation
+    ///     Authorization: authorization id
+    /// 
+    /// </remarks>
+    /// <returns>会话有效则返回会话对象</returns>
+    /// <response code="200">返回会话对象</response>
+    /// <response code="401">认证失败</response>
+    [Route("validation", Name = "validation")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
+    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+    [ProducesErrorResponseType(typeof(ErrorResponse))]
+    [HttpGet]
+    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
+    public ActionResult<TokenItem> Validation()
+    {
+        var (result, token) = CheckToken();
+        if (result != null)
+        {
+            return result;
+        }
+        if (token == null)
+        {
+            return Unauthorized();
+        }
+
+        // update last active time
+        SaveDatabase();
+
+        return Ok(token);
+    }
+
     /// <summary>
     /// 注销当前登录会话
     /// </summary>
diff --git a/Server/Data/Model/FlowerItem.cs b/Server/Data/Model/FlowerItem.cs
index 2ef6950..1528308 100644
--- a/Server/Data/Model/FlowerItem.cs
+++ b/Server/Data/Model/FlowerItem.cs
@@ -24,7 +24,7 @@ public class FlowerItem : ILocation
     [Column("uid")]
     [ForeignKey(nameof(Owner))]
     [Required]
-    public int OwnerId { get; set; }
+    public required int OwnerId { get; set; }
 
     /// <summary>
     /// 所有人
@@ -37,7 +37,7 @@ public class FlowerItem : ILocation
     /// </summary>
     [Column("categoryid")]
     [Required]
-    public int CategoryId { get; set; }
+    public required int CategoryId { get; set; }
 
     /// <summary>
     /// 花草名称
@@ -52,7 +52,7 @@ public class FlowerItem : ILocation
     [Column("datebuy")]
     [Required]
     [JsonPropertyName("dateBuy")]
-    public long DateBuyUnixTime { get; set; }
+    public required long DateBuyUnixTime { get; set; }
 
     /// <summary>
     /// 购买花费
diff --git a/Server/Data/Model/PhotoItem.cs b/Server/Data/Model/PhotoItem.cs
index f09c2c5..0c716de 100644
--- a/Server/Data/Model/PhotoItem.cs
+++ b/Server/Data/Model/PhotoItem.cs
@@ -18,16 +18,29 @@ public class PhotoItem
     [Required]
     public int Id { get; set; }
 
+    /// <summary>
+    /// 关联人 id
+    /// </summary>
+    [Column("uid")]
+    [ForeignKey(nameof(Owner))]
+    [Required]
+    public required int OwnerId { get; set; }
+
+    /// <summary>
+    /// 关联人
+    /// </summary>
+    [JsonIgnore]
+    public UserItem? Owner { get; set; }
+
     /// <summary>
     /// 关联花草 id
     /// </summary>
     [Column("fid")]
     [ForeignKey(nameof(Flower))]
-    [Required]
-    public int FlowerId { get; set; }
+    public int? FlowerId { get; set; }
 
     /// <summary>
-    /// 关联花草
+    /// 关联的花草
     /// </summary>
     [JsonIgnore]
     public FlowerItem? Flower { get; set; }
@@ -37,8 +50,7 @@ public class PhotoItem
     /// </summary>
     [Column("rid")]
     [ForeignKey(nameof(Record))]
-    [Required]
-    public int RecordId { get; set; }
+    public int? RecordId { get; set; }
 
     /// <summary>
     /// 关联的事件
@@ -73,7 +85,7 @@ public class PhotoItem
     [Column("dateupload")]
     [Required]
     [JsonPropertyName("dateUpload")]
-    public long DateUploadUnixTime { get; set; }
+    public required long DateUploadUnixTime { get; set; }
 
     /// <summary>
     /// 上传时间
diff --git a/Server/Data/Model/RecordItem.cs b/Server/Data/Model/RecordItem.cs
index 0d28af8..12ff9fc 100644
--- a/Server/Data/Model/RecordItem.cs
+++ b/Server/Data/Model/RecordItem.cs
@@ -24,7 +24,7 @@ public class RecordItem : ILocation
     [Column("uid")]
     [ForeignKey(nameof(Owner))]
     [Required]
-    public int OwnerId { get; set; }
+    public required int OwnerId { get; set; }
 
     /// <summary>
     /// 关联人
@@ -38,7 +38,7 @@ public class RecordItem : ILocation
     [Column("fid")]
     [ForeignKey(nameof(Flower))]
     [Required]
-    public int FlowerId { get; set; }
+    public required int FlowerId { get; set; }
 
     /// <summary>
     /// 关联花草
@@ -51,7 +51,7 @@ public class RecordItem : ILocation
     /// </summary>
     [Column("eid")]
     [Required]
-    public int EventId { get; set; }
+    public required int EventId { get; set; }
 
     /// <summary>
     /// 操作时间
@@ -59,7 +59,7 @@ public class RecordItem : ILocation
     [Column("date")]
     [Required]
     [JsonPropertyName("date")]
-    public long DateUnixTime { get; set; }
+    public required long DateUnixTime { get; set; }
 
     /// <summary>
     /// 操作人 uid
diff --git a/Server/Data/Model/TokenItem.cs b/Server/Data/Model/TokenItem.cs
index dabe634..fd0667c 100644
--- a/Server/Data/Model/TokenItem.cs
+++ b/Server/Data/Model/TokenItem.cs
@@ -23,7 +23,7 @@ public class TokenItem
     /// </summary>
     [Column("uid")]
     [Required]
-    public int UserId { get; set; }
+    public required int UserId { get; set; }
 
     /// <summary>
     /// 登录时间
@@ -31,7 +31,7 @@ public class TokenItem
     [Column("logondate")]
     [Required]
     [JsonPropertyName("logonDate")]
-    public long LogonDateUnixTime { get; set; }
+    public required long LogonDateUnixTime { get; set; }
 
     /// <summary>
     /// 活动时间
@@ -39,7 +39,7 @@ public class TokenItem
     [Column("activedate")]
     [Required]
     [JsonPropertyName("activeDate")]
-    public long ActiveDateUnixTime { get; set; }
+    public required long ActiveDateUnixTime { get; set; }
 
     /// <summary>
     /// 过期时间
@@ -47,14 +47,14 @@ public class TokenItem
     [Column("expiredate")]
     [Required]
     [JsonPropertyName("expireDate")]
-    public long ExpireDateUnixTime { get; set; }
+    public required long ExpireDateUnixTime { get; set; }
 
     /// <summary>
     /// 过期秒数
     /// </summary>
     [Column("expiresecs")]
     [Required]
-    public int ExpireSeconds { get; set; }
+    public required int ExpireSeconds { get; set; }
 
     /// <summary>
     /// 验证码
diff --git a/Server/Data/Model/UserItem.cs b/Server/Data/Model/UserItem.cs
index 5377286..a10122f 100644
--- a/Server/Data/Model/UserItem.cs
+++ b/Server/Data/Model/UserItem.cs
@@ -41,7 +41,7 @@ public class UserItem
     /// </summary>
     [Column("level")]
     [Required]
-    public int Level { get; set; }
+    public required int Level { get; set; }
 
     /// <summary>
     /// 注册时间
@@ -49,7 +49,7 @@ public class UserItem
     [Column("regdate")]
     [Required]
     [JsonPropertyName("registerDate")]
-    public long RegisterDateUnixTime { get; set; }
+    public required long RegisterDateUnixTime { get; set; }
 
     /// <summary>
     /// 最后变动时间
diff --git a/Server/Migrations/20230522090224_InitialCreateDb.Designer.cs b/Server/Migrations/20230522090224_InitialCreateDb.Designer.cs
deleted file mode 100644
index e5a8942..0000000
--- a/Server/Migrations/20230522090224_InitialCreateDb.Designer.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-// <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("20230522090224_InitialCreateDb")]
-    partial class InitialCreateDb
-    {
-        /// <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<DateTimeOffset>("DateBuy")
-                        .HasColumnType("numeric")
-                        .HasColumnName("datebuy");
-
-                    b.Property<string>("Name")
-                        .IsRequired()
-                        .HasColumnType("TEXT")
-                        .HasColumnName("name");
-
-                    b.Property<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.Property<string>("Purchase")
-                        .HasColumnType("TEXT")
-                        .HasColumnName("purchase");
-
-                    b.HasKey("Id");
-
-                    b.ToTable("flowers");
-                });
-
-            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<DateTimeOffset>("Date")
-                        .HasColumnType("numeric")
-                        .HasColumnName("date");
-
-                    b.Property<int>("EventId")
-                        .HasColumnType("INTEGER")
-                        .HasColumnName("eid");
-
-                    b.Property<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.HasKey("Id");
-
-                    b.ToTable("records");
-                });
-
-            modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b =>
-                {
-                    b.Property<int>("Id")
-                        .ValueGeneratedOnAdd()
-                        .HasColumnType("INTEGER")
-                        .HasColumnName("uid");
-
-                    b.Property<DateTimeOffset?>("ActiveDate")
-                        .HasColumnType("numeric")
-                        .HasColumnName("activedate");
-
-                    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<DateTimeOffset>("RegisterDate")
-                        .HasColumnType("numeric")
-                        .HasColumnName("regdate");
-
-                    b.Property<string>("UserId")
-                        .IsRequired()
-                        .HasColumnType("TEXT")
-                        .HasColumnName("id");
-
-                    b.HasKey("Id");
-
-                    b.ToTable("users");
-                });
-#pragma warning restore 612, 618
-        }
-    }
-}
diff --git a/Server/Migrations/20230522090224_InitialCreateDb.cs b/Server/Migrations/20230522090224_InitialCreateDb.cs
deleted file mode 100644
index 0133c0f..0000000
--- a/Server/Migrations/20230522090224_InitialCreateDb.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Blahblah.FlowerStory.Server.Migrations
-{
-    /// <inheritdoc />
-    public partial class InitialCreateDb : Migration
-    {
-        /// <inheritdoc />
-        protected override void Up(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.CreateTable(
-                name: "flowers",
-                columns: table => new
-                {
-                    fid = table.Column<int>(type: "INTEGER", nullable: false)
-                        .Annotation("Sqlite:Autoincrement", true),
-                    categoryid = table.Column<int>(type: "INTEGER", nullable: false),
-                    name = table.Column<string>(type: "TEXT", nullable: false),
-                    datebuy = table.Column<DateTimeOffset>(type: "numeric", nullable: false),
-                    cost = table.Column<decimal>(type: "real", nullable: true),
-                    purchase = table.Column<string>(type: "TEXT", nullable: true),
-                    photo = table.Column<byte[]>(type: "BLOB", nullable: true)
-                },
-                constraints: table =>
-                {
-                    table.PrimaryKey("PK_flowers", x => x.fid);
-                });
-
-            migrationBuilder.CreateTable(
-                name: "records",
-                columns: table => new
-                {
-                    rid = table.Column<int>(type: "INTEGER", nullable: false)
-                        .Annotation("Sqlite:Autoincrement", true),
-                    eid = table.Column<int>(type: "INTEGER", nullable: false),
-                    date = table.Column<DateTimeOffset>(type: "numeric", nullable: false),
-                    byuid = table.Column<int>(type: "INTEGER", nullable: true),
-                    byname = table.Column<string>(type: "TEXT", nullable: true),
-                    photo = table.Column<byte[]>(type: "BLOB", nullable: true)
-                },
-                constraints: table =>
-                {
-                    table.PrimaryKey("PK_records", x => x.rid);
-                });
-
-            migrationBuilder.CreateTable(
-                name: "users",
-                columns: table => new
-                {
-                    uid = table.Column<int>(type: "INTEGER", nullable: false)
-                        .Annotation("Sqlite:Autoincrement", true),
-                    id = table.Column<string>(type: "TEXT", nullable: false),
-                    password = table.Column<string>(type: "TEXT", nullable: false),
-                    level = table.Column<int>(type: "INTEGER", nullable: false),
-                    regdate = table.Column<DateTimeOffset>(type: "numeric", nullable: false),
-                    activedate = table.Column<DateTimeOffset>(type: "numeric", nullable: true),
-                    name = table.Column<string>(type: "TEXT", nullable: false),
-                    email = table.Column<string>(type: "TEXT", nullable: true),
-                    mobile = table.Column<string>(type: "TEXT", nullable: true)
-                },
-                constraints: table =>
-                {
-                    table.PrimaryKey("PK_users", x => x.uid);
-                });
-        }
-
-        /// <inheritdoc />
-        protected override void Down(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.DropTable(
-                name: "flowers");
-
-            migrationBuilder.DropTable(
-                name: "records");
-
-            migrationBuilder.DropTable(
-                name: "users");
-        }
-    }
-}
diff --git a/Server/Migrations/20230522143925_ChangeDateColumnType.Designer.cs b/Server/Migrations/20230522143925_ChangeDateColumnType.Designer.cs
deleted file mode 100644
index a27cb2c..0000000
--- a/Server/Migrations/20230522143925_ChangeDateColumnType.Designer.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-// <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("20230522143925_ChangeDateColumnType")]
-    partial class ChangeDateColumnType
-    {
-        /// <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");
-
-                    b.Property<string>("Name")
-                        .IsRequired()
-                        .HasColumnType("TEXT")
-                        .HasColumnName("name");
-
-                    b.Property<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.Property<string>("Purchase")
-                        .HasColumnType("TEXT")
-                        .HasColumnName("purchase");
-
-                    b.HasKey("Id");
-
-                    b.ToTable("flowers");
-                });
-
-            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");
-
-                    b.Property<int>("EventId")
-                        .HasColumnType("INTEGER")
-                        .HasColumnName("eid");
-
-                    b.Property<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.HasKey("Id");
-
-                    b.ToTable("records");
-                });
-
-            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<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");
-
-                    b.Property<string>("UserId")
-                        .IsRequired()
-                        .HasColumnType("TEXT")
-                        .HasColumnName("id");
-
-                    b.HasKey("Id");
-
-                    b.ToTable("users");
-                });
-#pragma warning restore 612, 618
-        }
-    }
-}
diff --git a/Server/Migrations/20230522143925_ChangeDateColumnType.cs b/Server/Migrations/20230522143925_ChangeDateColumnType.cs
deleted file mode 100644
index eacee39..0000000
--- a/Server/Migrations/20230522143925_ChangeDateColumnType.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using System;
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Blahblah.FlowerStory.Server.Migrations
-{
-    /// <inheritdoc />
-    public partial class ChangeDateColumnType : Migration
-    {
-        /// <inheritdoc />
-        protected override void Up(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.AlterColumn<long>(
-                name: "regdate",
-                table: "users",
-                type: "INTEGER",
-                nullable: false,
-                oldClrType: typeof(DateTimeOffset),
-                oldType: "numeric");
-
-            migrationBuilder.AlterColumn<long>(
-                name: "activedate",
-                table: "users",
-                type: "INTEGER",
-                nullable: true,
-                oldClrType: typeof(DateTimeOffset),
-                oldType: "numeric",
-                oldNullable: true);
-
-            migrationBuilder.AlterColumn<long>(
-                name: "date",
-                table: "records",
-                type: "INTEGER",
-                nullable: false,
-                oldClrType: typeof(DateTimeOffset),
-                oldType: "numeric");
-
-            migrationBuilder.AlterColumn<long>(
-                name: "datebuy",
-                table: "flowers",
-                type: "INTEGER",
-                nullable: false,
-                oldClrType: typeof(DateTimeOffset),
-                oldType: "numeric");
-        }
-
-        /// <inheritdoc />
-        protected override void Down(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.AlterColumn<DateTimeOffset>(
-                name: "regdate",
-                table: "users",
-                type: "numeric",
-                nullable: false,
-                oldClrType: typeof(long),
-                oldType: "INTEGER");
-
-            migrationBuilder.AlterColumn<DateTimeOffset>(
-                name: "activedate",
-                table: "users",
-                type: "numeric",
-                nullable: true,
-                oldClrType: typeof(long),
-                oldType: "INTEGER",
-                oldNullable: true);
-
-            migrationBuilder.AlterColumn<DateTimeOffset>(
-                name: "date",
-                table: "records",
-                type: "numeric",
-                nullable: false,
-                oldClrType: typeof(long),
-                oldType: "INTEGER");
-
-            migrationBuilder.AlterColumn<DateTimeOffset>(
-                name: "datebuy",
-                table: "flowers",
-                type: "numeric",
-                nullable: false,
-                oldClrType: typeof(long),
-                oldType: "INTEGER");
-        }
-    }
-}
diff --git a/Server/Migrations/20230523031232_AddTokens.Designer.cs b/Server/Migrations/20230523031232_AddTokens.Designer.cs
deleted file mode 100644
index bfdf5f1..0000000
--- a/Server/Migrations/20230523031232_AddTokens.Designer.cs
+++ /dev/null
@@ -1,191 +0,0 @@
-// <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("20230523031232_AddTokens")]
-    partial class AddTokens
-    {
-        /// <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<string>("Name")
-                        .IsRequired()
-                        .HasColumnType("TEXT")
-                        .HasColumnName("name");
-
-                    b.Property<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.Property<string>("Purchase")
-                        .HasColumnType("TEXT")
-                        .HasColumnName("purchase");
-
-                    b.HasKey("Id");
-
-                    b.ToTable("flowers");
-                });
-
-            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<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.HasKey("Id");
-
-                    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");
-
-                    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");
-
-                    b.Property<int>("ExpireSeconds")
-                        .HasColumnType("INTEGER")
-                        .HasColumnName("expiresecs");
-
-                    b.Property<long>("LogonDateUnixTime")
-                        .HasColumnType("INTEGER")
-                        .HasColumnName("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<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");
-                });
-#pragma warning restore 612, 618
-        }
-    }
-}
diff --git a/Server/Migrations/20230523031232_AddTokens.cs b/Server/Migrations/20230523031232_AddTokens.cs
deleted file mode 100644
index ae656ca..0000000
--- a/Server/Migrations/20230523031232_AddTokens.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Blahblah.FlowerStory.Server.Migrations
-{
-    /// <inheritdoc />
-    public partial class AddTokens : Migration
-    {
-        /// <inheritdoc />
-        protected override void Up(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.CreateTable(
-                name: "tokens",
-                columns: table => new
-                {
-                    tid = table.Column<string>(type: "TEXT", nullable: false),
-                    uid = table.Column<int>(type: "INTEGER", nullable: false),
-                    logondate = table.Column<long>(type: "INTEGER", nullable: false),
-                    activedate = table.Column<long>(type: "INTEGER", nullable: false),
-                    expiredate = table.Column<long>(type: "INTEGER", nullable: false),
-                    expiresecs = table.Column<int>(type: "INTEGER", nullable: false),
-                    verifycode = table.Column<string>(type: "TEXT", nullable: true),
-                    clientapp = table.Column<string>(type: "TEXT", nullable: true),
-                    deviceid = table.Column<string>(type: "TEXT", nullable: true),
-                    clientagent = table.Column<string>(type: "TEXT", nullable: true)
-                },
-                constraints: table =>
-                {
-                    table.PrimaryKey("PK_tokens", x => x.tid);
-                });
-        }
-
-        /// <inheritdoc />
-        protected override void Down(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.DropTable(
-                name: "tokens");
-        }
-    }
-}
diff --git a/Server/Migrations/20230524062207_AddOwner.Designer.cs b/Server/Migrations/20230524062207_AddOwner.Designer.cs
deleted file mode 100644
index 71c671c..0000000
--- a/Server/Migrations/20230524062207_AddOwner.Designer.cs
+++ /dev/null
@@ -1,202 +0,0 @@
-// <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("20230524062207_AddOwner")]
-    partial class AddOwner
-    {
-        /// <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<string>("Name")
-                        .IsRequired()
-                        .HasColumnType("TEXT")
-                        .HasColumnName("name");
-
-                    b.Property<int>("OwnerId")
-                        .HasColumnType("INTEGER")
-                        .HasColumnName("uid");
-
-                    b.Property<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.Property<string>("Purchase")
-                        .HasColumnType("TEXT")
-                        .HasColumnName("purchase");
-
-                    b.HasKey("Id");
-
-                    b.ToTable("flowers");
-                });
-
-            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>("OwnerId")
-                        .HasColumnType("INTEGER")
-                        .HasColumnName("uid");
-
-                    b.Property<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.HasKey("Id");
-
-                    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<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");
-                });
-#pragma warning restore 612, 618
-        }
-    }
-}
diff --git a/Server/Migrations/20230524062207_AddOwner.cs b/Server/Migrations/20230524062207_AddOwner.cs
deleted file mode 100644
index 79fcc1f..0000000
--- a/Server/Migrations/20230524062207_AddOwner.cs
+++ /dev/null
@@ -1,40 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Blahblah.FlowerStory.Server.Migrations
-{
-    /// <inheritdoc />
-    public partial class AddOwner : Migration
-    {
-        /// <inheritdoc />
-        protected override void Up(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.AddColumn<int>(
-                name: "uid",
-                table: "records",
-                type: "INTEGER",
-                nullable: false,
-                defaultValue: 0);
-
-            migrationBuilder.AddColumn<int>(
-                name: "uid",
-                table: "flowers",
-                type: "INTEGER",
-                nullable: false,
-                defaultValue: 0);
-        }
-
-        /// <inheritdoc />
-        protected override void Down(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.DropColumn(
-                name: "uid",
-                table: "records");
-
-            migrationBuilder.DropColumn(
-                name: "uid",
-                table: "flowers");
-        }
-    }
-}
diff --git a/Server/Migrations/20230525004719_AddAvatarMemo.Designer.cs b/Server/Migrations/20230525004719_AddAvatarMemo.Designer.cs
deleted file mode 100644
index 9f36f2b..0000000
--- a/Server/Migrations/20230525004719_AddAvatarMemo.Designer.cs
+++ /dev/null
@@ -1,214 +0,0 @@
-// <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("20230525004719_AddAvatarMemo")]
-    partial class AddAvatarMemo
-    {
-        /// <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<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<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.Property<string>("Purchase")
-                        .HasColumnType("TEXT")
-                        .HasColumnName("purchase");
-
-                    b.HasKey("Id");
-
-                    b.ToTable("flowers");
-                });
-
-            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<string>("Memo")
-                        .HasColumnType("TEXT")
-                        .HasColumnName("memo");
-
-                    b.Property<int>("OwnerId")
-                        .HasColumnType("INTEGER")
-                        .HasColumnName("uid");
-
-                    b.Property<byte[]>("Photo")
-                        .HasColumnType("BLOB")
-                        .HasColumnName("photo");
-
-                    b.HasKey("Id");
-
-                    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");
-                });
-#pragma warning restore 612, 618
-        }
-    }
-}
diff --git a/Server/Migrations/20230525004719_AddAvatarMemo.cs b/Server/Migrations/20230525004719_AddAvatarMemo.cs
deleted file mode 100644
index ab51165..0000000
--- a/Server/Migrations/20230525004719_AddAvatarMemo.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Blahblah.FlowerStory.Server.Migrations
-{
-    /// <inheritdoc />
-    public partial class AddAvatarMemo : Migration
-    {
-        /// <inheritdoc />
-        protected override void Up(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.AddColumn<byte[]>(
-                name: "avatar",
-                table: "users",
-                type: "BLOB",
-                nullable: true);
-
-            migrationBuilder.AddColumn<string>(
-                name: "memo",
-                table: "records",
-                type: "TEXT",
-                nullable: true);
-
-            migrationBuilder.AddColumn<string>(
-                name: "memo",
-                table: "flowers",
-                type: "TEXT",
-                nullable: true);
-        }
-
-        /// <inheritdoc />
-        protected override void Down(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.DropColumn(
-                name: "avatar",
-                table: "users");
-
-            migrationBuilder.DropColumn(
-                name: "memo",
-                table: "records");
-
-            migrationBuilder.DropColumn(
-                name: "memo",
-                table: "flowers");
-        }
-    }
-}
diff --git a/Server/Migrations/20230525082941_AddPhotos.Designer.cs b/Server/Migrations/20230525082941_AddPhotos.Designer.cs
deleted file mode 100644
index a38277f..0000000
--- a/Server/Migrations/20230525082941_AddPhotos.Designer.cs
+++ /dev/null
@@ -1,300 +0,0 @@
-// <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("20230525082941_AddPhotos")]
-    partial class AddPhotos
-    {
-        /// <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<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<string>("Path")
-                        .IsRequired()
-                        .HasColumnType("TEXT")
-                        .HasColumnName("path");
-
-                    b.Property<int>("RecordId")
-                        .HasColumnType("INTEGER")
-                        .HasColumnName("rid");
-
-                    b.HasKey("Id");
-
-                    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<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.RecordItem", "Record")
-                        .WithMany("Photo")
-                        .HasForeignKey("RecordId")
-                        .OnDelete(DeleteBehavior.Cascade)
-                        .IsRequired();
-
-                    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.RecordItem", b =>
-                {
-                    b.Navigation("Photo");
-                });
-#pragma warning restore 612, 618
-        }
-    }
-}
diff --git a/Server/Migrations/20230525082941_AddPhotos.cs b/Server/Migrations/20230525082941_AddPhotos.cs
deleted file mode 100644
index 2f22de6..0000000
--- a/Server/Migrations/20230525082941_AddPhotos.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using Microsoft.EntityFrameworkCore.Migrations;
-
-#nullable disable
-
-namespace Blahblah.FlowerStory.Server.Migrations
-{
-    /// <inheritdoc />
-    public partial class AddPhotos : Migration
-    {
-        /// <inheritdoc />
-        protected override void Up(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.DropColumn(
-                name: "photo",
-                table: "records");
-
-            migrationBuilder.DropColumn(
-                name: "photo",
-                table: "flowers");
-
-            migrationBuilder.AddColumn<int>(
-                name: "fid",
-                table: "records",
-                type: "INTEGER",
-                nullable: false,
-                defaultValue: 0);
-
-            migrationBuilder.CreateTable(
-                name: "photos",
-                columns: table => new
-                {
-                    pid = table.Column<int>(type: "INTEGER", nullable: false)
-                        .Annotation("Sqlite:Autoincrement", true),
-                    rid = table.Column<int>(type: "INTEGER", nullable: false),
-                    filetype = table.Column<string>(type: "TEXT", nullable: false),
-                    filename = table.Column<string>(type: "TEXT", nullable: false),
-                    path = table.Column<string>(type: "TEXT", nullable: false),
-                    dateupload = table.Column<long>(type: "INTEGER", nullable: false)
-                },
-                constraints: table =>
-                {
-                    table.PrimaryKey("PK_photos", x => x.pid);
-                    table.ForeignKey(
-                        name: "FK_photos_records_rid",
-                        column: x => x.rid,
-                        principalTable: "records",
-                        principalColumn: "rid",
-                        onDelete: ReferentialAction.Cascade);
-                });
-
-            migrationBuilder.CreateIndex(
-                name: "IX_records_fid",
-                table: "records",
-                column: "fid");
-
-            migrationBuilder.CreateIndex(
-                name: "IX_records_uid",
-                table: "records",
-                column: "uid");
-
-            migrationBuilder.CreateIndex(
-                name: "IX_flowers_uid",
-                table: "flowers",
-                column: "uid");
-
-            migrationBuilder.CreateIndex(
-                name: "IX_photos_rid",
-                table: "photos",
-                column: "rid");
-
-            migrationBuilder.AddForeignKey(
-                name: "FK_flowers_users_uid",
-                table: "flowers",
-                column: "uid",
-                principalTable: "users",
-                principalColumn: "uid",
-                onDelete: ReferentialAction.Cascade);
-
-            migrationBuilder.AddForeignKey(
-                name: "FK_records_flowers_fid",
-                table: "records",
-                column: "fid",
-                principalTable: "flowers",
-                principalColumn: "fid",
-                onDelete: ReferentialAction.Cascade);
-
-            migrationBuilder.AddForeignKey(
-                name: "FK_records_users_uid",
-                table: "records",
-                column: "uid",
-                principalTable: "users",
-                principalColumn: "uid",
-                onDelete: ReferentialAction.Cascade);
-        }
-
-        /// <inheritdoc />
-        protected override void Down(MigrationBuilder migrationBuilder)
-        {
-            migrationBuilder.DropForeignKey(
-                name: "FK_flowers_users_uid",
-                table: "flowers");
-
-            migrationBuilder.DropForeignKey(
-                name: "FK_records_flowers_fid",
-                table: "records");
-
-            migrationBuilder.DropForeignKey(
-                name: "FK_records_users_uid",
-                table: "records");
-
-            migrationBuilder.DropTable(
-                name: "photos");
-
-            migrationBuilder.DropIndex(
-                name: "IX_records_fid",
-                table: "records");
-
-            migrationBuilder.DropIndex(
-                name: "IX_records_uid",
-                table: "records");
-
-            migrationBuilder.DropIndex(
-                name: "IX_flowers_uid",
-                table: "flowers");
-
-            migrationBuilder.DropColumn(
-                name: "fid",
-                table: "records");
-
-            migrationBuilder.AddColumn<byte[]>(
-                name: "photo",
-                table: "records",
-                type: "BLOB",
-                nullable: true);
-
-            migrationBuilder.AddColumn<byte[]>(
-                name: "photo",
-                table: "flowers",
-                type: "BLOB",
-                nullable: true);
-        }
-    }
-}
diff --git a/Server/Migrations/20230705083734_Add-Latitude-Longitude.cs b/Server/Migrations/20230705083734_Add-Latitude-Longitude.cs
deleted file mode 100644
index 4e5458b..0000000
--- a/Server/Migrations/20230705083734_Add-Latitude-Longitude.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-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/20230705083734_Add-Latitude-Longitude.Designer.cs b/Server/Migrations/20230712091736_InitialDb.Designer.cs
similarity index 95%
rename from Server/Migrations/20230705083734_Add-Latitude-Longitude.Designer.cs
rename to Server/Migrations/20230712091736_InitialDb.Designer.cs
index 3da48ea..53040af 100644
--- a/Server/Migrations/20230705083734_Add-Latitude-Longitude.Designer.cs
+++ b/Server/Migrations/20230712091736_InitialDb.Designer.cs
@@ -11,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 namespace Blahblah.FlowerStory.Server.Migrations
 {
     [DbContext(typeof(FlowerDatabase))]
-    [Migration("20230705083734_Add-Latitude-Longitude")]
-    partial class AddLatitudeLongitude
+    [Migration("20230712091736_InitialDb")]
+    partial class InitialDb
     {
         /// <inheritdoc />
         protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -40,11 +40,11 @@ namespace Blahblah.FlowerStory.Server.Migrations
                         .HasColumnName("datebuy")
                         .HasAnnotation("Relational:JsonPropertyName", "dateBuy");
 
-                    b.Property<double?>("LastLatitude")
+                    b.Property<double?>("Latitude")
                         .HasColumnType("REAL")
                         .HasColumnName("latitude");
 
-                    b.Property<double?>("LastLongitude")
+                    b.Property<double?>("Longitude")
                         .HasColumnType("REAL")
                         .HasColumnName("longitude");
 
@@ -94,7 +94,7 @@ namespace Blahblah.FlowerStory.Server.Migrations
                         .HasColumnType("TEXT")
                         .HasColumnName("filetype");
 
-                    b.Property<int>("FlowerId")
+                    b.Property<int?>("FlowerId")
                         .HasColumnType("INTEGER")
                         .HasColumnName("fid");
 
@@ -103,7 +103,7 @@ namespace Blahblah.FlowerStory.Server.Migrations
                         .HasColumnType("TEXT")
                         .HasColumnName("path");
 
-                    b.Property<int>("RecordId")
+                    b.Property<int?>("RecordId")
                         .HasColumnType("INTEGER")
                         .HasColumnName("rid");
 
@@ -286,15 +286,11 @@ namespace Blahblah.FlowerStory.Server.Migrations
                 {
                     b.HasOne("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", "Flower")
                         .WithMany("Photos")
-                        .HasForeignKey("FlowerId")
-                        .OnDelete(DeleteBehavior.Cascade)
-                        .IsRequired();
+                        .HasForeignKey("FlowerId");
 
                     b.HasOne("Blahblah.FlowerStory.Server.Data.Model.RecordItem", "Record")
                         .WithMany("Photos")
-                        .HasForeignKey("RecordId")
-                        .OnDelete(DeleteBehavior.Cascade)
-                        .IsRequired();
+                        .HasForeignKey("RecordId");
 
                     b.Navigation("Flower");
 
diff --git a/Server/Migrations/20230712091736_InitialDb.cs b/Server/Migrations/20230712091736_InitialDb.cs
new file mode 100644
index 0000000..88143af
--- /dev/null
+++ b/Server/Migrations/20230712091736_InitialDb.cs
@@ -0,0 +1,187 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Blahblah.FlowerStory.Server.Migrations
+{
+    /// <inheritdoc />
+    public partial class InitialDb : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.CreateTable(
+                name: "tokens",
+                columns: table => new
+                {
+                    tid = table.Column<string>(type: "TEXT", nullable: false),
+                    uid = table.Column<int>(type: "INTEGER", nullable: false),
+                    logondate = table.Column<long>(type: "INTEGER", nullable: false),
+                    activedate = table.Column<long>(type: "INTEGER", nullable: false),
+                    expiredate = table.Column<long>(type: "INTEGER", nullable: false),
+                    expiresecs = table.Column<int>(type: "INTEGER", nullable: false),
+                    verifycode = table.Column<string>(type: "TEXT", nullable: true),
+                    clientapp = table.Column<string>(type: "TEXT", nullable: true),
+                    deviceid = table.Column<string>(type: "TEXT", nullable: true),
+                    clientagent = table.Column<string>(type: "TEXT", nullable: true)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_tokens", x => x.tid);
+                });
+
+            migrationBuilder.CreateTable(
+                name: "users",
+                columns: table => new
+                {
+                    uid = table.Column<int>(type: "INTEGER", nullable: false)
+                        .Annotation("Sqlite:Autoincrement", true),
+                    id = table.Column<string>(type: "TEXT", nullable: false),
+                    password = table.Column<string>(type: "TEXT", nullable: false),
+                    level = table.Column<int>(type: "INTEGER", nullable: false),
+                    regdate = table.Column<long>(type: "INTEGER", nullable: false),
+                    activedate = table.Column<long>(type: "INTEGER", nullable: true),
+                    name = table.Column<string>(type: "TEXT", nullable: false),
+                    email = table.Column<string>(type: "TEXT", nullable: true),
+                    mobile = table.Column<string>(type: "TEXT", nullable: true),
+                    avatar = table.Column<byte[]>(type: "BLOB", nullable: true)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_users", x => x.uid);
+                });
+
+            migrationBuilder.CreateTable(
+                name: "flowers",
+                columns: table => new
+                {
+                    fid = table.Column<int>(type: "INTEGER", nullable: false)
+                        .Annotation("Sqlite:Autoincrement", true),
+                    uid = table.Column<int>(type: "INTEGER", nullable: false),
+                    categoryid = table.Column<int>(type: "INTEGER", nullable: false),
+                    name = table.Column<string>(type: "TEXT", nullable: false),
+                    datebuy = table.Column<long>(type: "INTEGER", nullable: false),
+                    cost = table.Column<decimal>(type: "real", nullable: true),
+                    purchase = table.Column<string>(type: "TEXT", nullable: true),
+                    memo = table.Column<string>(type: "TEXT", nullable: true),
+                    latitude = table.Column<double>(type: "REAL", nullable: true),
+                    longitude = table.Column<double>(type: "REAL", nullable: true)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_flowers", x => x.fid);
+                    table.ForeignKey(
+                        name: "FK_flowers_users_uid",
+                        column: x => x.uid,
+                        principalTable: "users",
+                        principalColumn: "uid",
+                        onDelete: ReferentialAction.Cascade);
+                });
+
+            migrationBuilder.CreateTable(
+                name: "records",
+                columns: table => new
+                {
+                    rid = table.Column<int>(type: "INTEGER", nullable: false)
+                        .Annotation("Sqlite:Autoincrement", true),
+                    uid = table.Column<int>(type: "INTEGER", nullable: false),
+                    fid = table.Column<int>(type: "INTEGER", nullable: false),
+                    eid = table.Column<int>(type: "INTEGER", nullable: false),
+                    date = table.Column<long>(type: "INTEGER", nullable: false),
+                    byuid = table.Column<int>(type: "INTEGER", nullable: true),
+                    byname = table.Column<string>(type: "TEXT", nullable: true),
+                    memo = table.Column<string>(type: "TEXT", nullable: true),
+                    latitude = table.Column<double>(type: "REAL", nullable: true),
+                    longitude = table.Column<double>(type: "REAL", nullable: true)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_records", x => x.rid);
+                    table.ForeignKey(
+                        name: "FK_records_flowers_fid",
+                        column: x => x.fid,
+                        principalTable: "flowers",
+                        principalColumn: "fid",
+                        onDelete: ReferentialAction.Cascade);
+                    table.ForeignKey(
+                        name: "FK_records_users_uid",
+                        column: x => x.uid,
+                        principalTable: "users",
+                        principalColumn: "uid",
+                        onDelete: ReferentialAction.Cascade);
+                });
+
+            migrationBuilder.CreateTable(
+                name: "photos",
+                columns: table => new
+                {
+                    pid = table.Column<int>(type: "INTEGER", nullable: false)
+                        .Annotation("Sqlite:Autoincrement", true),
+                    fid = table.Column<int>(type: "INTEGER", nullable: true),
+                    rid = table.Column<int>(type: "INTEGER", nullable: true),
+                    filetype = table.Column<string>(type: "TEXT", nullable: false),
+                    filename = table.Column<string>(type: "TEXT", nullable: false),
+                    path = table.Column<string>(type: "TEXT", nullable: false),
+                    dateupload = table.Column<long>(type: "INTEGER", nullable: false)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_photos", x => x.pid);
+                    table.ForeignKey(
+                        name: "FK_photos_flowers_fid",
+                        column: x => x.fid,
+                        principalTable: "flowers",
+                        principalColumn: "fid");
+                    table.ForeignKey(
+                        name: "FK_photos_records_rid",
+                        column: x => x.rid,
+                        principalTable: "records",
+                        principalColumn: "rid");
+                });
+
+            migrationBuilder.CreateIndex(
+                name: "IX_flowers_uid",
+                table: "flowers",
+                column: "uid");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_photos_fid",
+                table: "photos",
+                column: "fid");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_photos_rid",
+                table: "photos",
+                column: "rid");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_records_fid",
+                table: "records",
+                column: "fid");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_records_uid",
+                table: "records",
+                column: "uid");
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable(
+                name: "photos");
+
+            migrationBuilder.DropTable(
+                name: "tokens");
+
+            migrationBuilder.DropTable(
+                name: "records");
+
+            migrationBuilder.DropTable(
+                name: "flowers");
+
+            migrationBuilder.DropTable(
+                name: "users");
+        }
+    }
+}
diff --git a/Server/Migrations/20230525091254_AddPhotosFlowerForeignKey.Designer.cs b/Server/Migrations/20230712093151_AddPhotoOwnerKey.Designer.cs
similarity index 89%
rename from Server/Migrations/20230525091254_AddPhotosFlowerForeignKey.Designer.cs
rename to Server/Migrations/20230712093151_AddPhotoOwnerKey.Designer.cs
index 06ac24e..a1ffa27 100644
--- a/Server/Migrations/20230525091254_AddPhotosFlowerForeignKey.Designer.cs
+++ b/Server/Migrations/20230712093151_AddPhotoOwnerKey.Designer.cs
@@ -11,8 +11,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
 namespace Blahblah.FlowerStory.Server.Migrations
 {
     [DbContext(typeof(FlowerDatabase))]
-    [Migration("20230525091254_AddPhotosFlowerForeignKey")]
-    partial class AddPhotosFlowerForeignKey
+    [Migration("20230712093151_AddPhotoOwnerKey")]
+    partial class AddPhotoOwnerKey
     {
         /// <inheritdoc />
         protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -40,6 +40,14 @@ namespace Blahblah.FlowerStory.Server.Migrations
                         .HasColumnName("datebuy")
                         .HasAnnotation("Relational:JsonPropertyName", "dateBuy");
 
+                    b.Property<double?>("Latitude")
+                        .HasColumnType("REAL")
+                        .HasColumnName("latitude");
+
+                    b.Property<double?>("Longitude")
+                        .HasColumnType("REAL")
+                        .HasColumnName("longitude");
+
                     b.Property<string>("Memo")
                         .HasColumnType("TEXT")
                         .HasColumnName("memo");
@@ -86,16 +94,20 @@ namespace Blahblah.FlowerStory.Server.Migrations
                         .HasColumnType("TEXT")
                         .HasColumnName("filetype");
 
-                    b.Property<int>("FlowerId")
+                    b.Property<int?>("FlowerId")
                         .HasColumnType("INTEGER")
                         .HasColumnName("fid");
 
+                    b.Property<int>("OwnerId")
+                        .HasColumnType("INTEGER")
+                        .HasColumnName("uid");
+
                     b.Property<string>("Path")
                         .IsRequired()
                         .HasColumnType("TEXT")
                         .HasColumnName("path");
 
-                    b.Property<int>("RecordId")
+                    b.Property<int?>("RecordId")
                         .HasColumnType("INTEGER")
                         .HasColumnName("rid");
 
@@ -103,6 +115,8 @@ namespace Blahblah.FlowerStory.Server.Migrations
 
                     b.HasIndex("FlowerId");
 
+                    b.HasIndex("OwnerId");
+
                     b.HasIndex("RecordId");
 
                     b.ToTable("photos");
@@ -136,6 +150,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");
@@ -270,18 +292,22 @@ namespace Blahblah.FlowerStory.Server.Migrations
                 {
                     b.HasOne("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", "Flower")
                         .WithMany("Photos")
-                        .HasForeignKey("FlowerId")
+                        .HasForeignKey("FlowerId");
+
+                    b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner")
+                        .WithMany()
+                        .HasForeignKey("OwnerId")
                         .OnDelete(DeleteBehavior.Cascade)
                         .IsRequired();
 
                     b.HasOne("Blahblah.FlowerStory.Server.Data.Model.RecordItem", "Record")
                         .WithMany("Photos")
-                        .HasForeignKey("RecordId")
-                        .OnDelete(DeleteBehavior.Cascade)
-                        .IsRequired();
+                        .HasForeignKey("RecordId");
 
                     b.Navigation("Flower");
 
+                    b.Navigation("Owner");
+
                     b.Navigation("Record");
                 });
 
diff --git a/Server/Migrations/20230525091254_AddPhotosFlowerForeignKey.cs b/Server/Migrations/20230712093151_AddPhotoOwnerKey.cs
similarity index 69%
rename from Server/Migrations/20230525091254_AddPhotosFlowerForeignKey.cs
rename to Server/Migrations/20230712093151_AddPhotoOwnerKey.cs
index 5a77cad..2e5aac1 100644
--- a/Server/Migrations/20230525091254_AddPhotosFlowerForeignKey.cs
+++ b/Server/Migrations/20230712093151_AddPhotoOwnerKey.cs
@@ -5,29 +5,29 @@
 namespace Blahblah.FlowerStory.Server.Migrations
 {
     /// <inheritdoc />
-    public partial class AddPhotosFlowerForeignKey : Migration
+    public partial class AddPhotoOwnerKey : Migration
     {
         /// <inheritdoc />
         protected override void Up(MigrationBuilder migrationBuilder)
         {
             migrationBuilder.AddColumn<int>(
-                name: "fid",
+                name: "uid",
                 table: "photos",
                 type: "INTEGER",
                 nullable: false,
                 defaultValue: 0);
 
             migrationBuilder.CreateIndex(
-                name: "IX_photos_fid",
+                name: "IX_photos_uid",
                 table: "photos",
-                column: "fid");
+                column: "uid");
 
             migrationBuilder.AddForeignKey(
-                name: "FK_photos_flowers_fid",
+                name: "FK_photos_users_uid",
                 table: "photos",
-                column: "fid",
-                principalTable: "flowers",
-                principalColumn: "fid",
+                column: "uid",
+                principalTable: "users",
+                principalColumn: "uid",
                 onDelete: ReferentialAction.Cascade);
         }
 
@@ -35,15 +35,15 @@ namespace Blahblah.FlowerStory.Server.Migrations
         protected override void Down(MigrationBuilder migrationBuilder)
         {
             migrationBuilder.DropForeignKey(
-                name: "FK_photos_flowers_fid",
+                name: "FK_photos_users_uid",
                 table: "photos");
 
             migrationBuilder.DropIndex(
-                name: "IX_photos_fid",
+                name: "IX_photos_uid",
                 table: "photos");
 
             migrationBuilder.DropColumn(
-                name: "fid",
+                name: "uid",
                 table: "photos");
         }
     }
diff --git a/Server/Migrations/FlowerDatabaseModelSnapshot.cs b/Server/Migrations/FlowerDatabaseModelSnapshot.cs
index ac35087..6d33685 100644
--- a/Server/Migrations/FlowerDatabaseModelSnapshot.cs
+++ b/Server/Migrations/FlowerDatabaseModelSnapshot.cs
@@ -37,11 +37,11 @@ namespace Blahblah.FlowerStory.Server.Migrations
                         .HasColumnName("datebuy")
                         .HasAnnotation("Relational:JsonPropertyName", "dateBuy");
 
-                    b.Property<double?>("LastLatitude")
+                    b.Property<double?>("Latitude")
                         .HasColumnType("REAL")
                         .HasColumnName("latitude");
 
-                    b.Property<double?>("LastLongitude")
+                    b.Property<double?>("Longitude")
                         .HasColumnType("REAL")
                         .HasColumnName("longitude");
 
@@ -66,7 +66,7 @@ namespace Blahblah.FlowerStory.Server.Migrations
 
                     b.HasIndex("OwnerId");
 
-                    b.ToTable("flowers", (string)null);
+                    b.ToTable("flowers");
                 });
 
             modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.PhotoItem", b =>
@@ -91,16 +91,20 @@ namespace Blahblah.FlowerStory.Server.Migrations
                         .HasColumnType("TEXT")
                         .HasColumnName("filetype");
 
-                    b.Property<int>("FlowerId")
+                    b.Property<int?>("FlowerId")
                         .HasColumnType("INTEGER")
                         .HasColumnName("fid");
 
+                    b.Property<int>("OwnerId")
+                        .HasColumnType("INTEGER")
+                        .HasColumnName("uid");
+
                     b.Property<string>("Path")
                         .IsRequired()
                         .HasColumnType("TEXT")
                         .HasColumnName("path");
 
-                    b.Property<int>("RecordId")
+                    b.Property<int?>("RecordId")
                         .HasColumnType("INTEGER")
                         .HasColumnName("rid");
 
@@ -108,9 +112,11 @@ namespace Blahblah.FlowerStory.Server.Migrations
 
                     b.HasIndex("FlowerId");
 
+                    b.HasIndex("OwnerId");
+
                     b.HasIndex("RecordId");
 
-                    b.ToTable("photos", (string)null);
+                    b.ToTable("photos");
                 });
 
             modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
@@ -163,7 +169,7 @@ namespace Blahblah.FlowerStory.Server.Migrations
 
                     b.HasIndex("OwnerId");
 
-                    b.ToTable("records", (string)null);
+                    b.ToTable("records");
                 });
 
             modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.TokenItem", b =>
@@ -213,7 +219,7 @@ namespace Blahblah.FlowerStory.Server.Migrations
 
                     b.HasKey("Id");
 
-                    b.ToTable("tokens", (string)null);
+                    b.ToTable("tokens");
                 });
 
             modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b =>
@@ -265,7 +271,7 @@ namespace Blahblah.FlowerStory.Server.Migrations
 
                     b.HasKey("Id");
 
-                    b.ToTable("users", (string)null);
+                    b.ToTable("users");
                 });
 
             modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", b =>
@@ -283,18 +289,22 @@ namespace Blahblah.FlowerStory.Server.Migrations
                 {
                     b.HasOne("Blahblah.FlowerStory.Server.Data.Model.FlowerItem", "Flower")
                         .WithMany("Photos")
-                        .HasForeignKey("FlowerId")
+                        .HasForeignKey("FlowerId");
+
+                    b.HasOne("Blahblah.FlowerStory.Server.Data.Model.UserItem", "Owner")
+                        .WithMany()
+                        .HasForeignKey("OwnerId")
                         .OnDelete(DeleteBehavior.Cascade)
                         .IsRequired();
 
                     b.HasOne("Blahblah.FlowerStory.Server.Data.Model.RecordItem", "Record")
                         .WithMany("Photos")
-                        .HasForeignKey("RecordId")
-                        .OnDelete(DeleteBehavior.Cascade)
-                        .IsRequired();
+                        .HasForeignKey("RecordId");
 
                     b.Navigation("Flower");
 
+                    b.Navigation("Owner");
+
                     b.Navigation("Record");
                 });
 
diff --git a/Server/Program.cs b/Server/Program.cs
index 2752f73..eb8c403 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.5.705";
+    public const string Version = "0.6.713";
 
     /// <inheritdoc/>
     public static void Main(string[] args)
@@ -29,7 +29,7 @@ public class Program
 
             var scheme = new OpenApiSecurityScheme
             {
-                Description = "��Ȩͷ�� ʾ���� \"RG//HkvcTZdBospBOT6OuoWfsc1GS+P/js9zFdflBr0=\"",
+                Description = "��Ȩͷ�� ʾ���� \"RF4mfoUur0vHtWzHwD42ka0FhIfGaPnBxoQgrXOYEDg=\"",
                 Reference = new OpenApiReference
                 {
                     Type = ReferenceType.SecurityScheme,