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;
+
+///
+/// 常数类
+///
+public sealed class Constants
+{
+ ///
+ /// 其他分类
+ ///
+ public const string CategoryOther = "other";
+ ///
+ /// 未知事件
+ ///
+ public const string EventUnknown = "unknown";
+
+ ///
+ /// 类别字典
+ ///
+ public static readonly Dictionary 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", "月季花"),
+ };
+
+ ///
+ /// 事件字典
+ ///
+ public static readonly Dictionary 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", "分享"),
+ };
+}
+
+///
+/// 事件类型枚举
+///
+public enum EventTypes
+{
+ ///
+ /// 未知
+ ///
+ Unknown = 0,
+ ///
+ /// 购买
+ ///
+ Buy = 1,
+ ///
+ /// 出生
+ ///
+ Born = 2,
+ ///
+ /// 死亡
+ ///
+ Death = 11,
+ ///
+ /// 出售
+ ///
+ Sell = 12,
+ ///
+ /// 分享
+ ///
+ Share = 13,
+}
+
+///
+/// 事件类
+///
+/// 名称
+/// 描述
+/// 是否唯一
+public record Event(string Key, string? Description = null, bool Unique = false) : NamedItem(Key, Description)
+{
+ ///
+ /// 是否唯一
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
+ public bool Unique { get; init; } = Unique;
+}
+
+///
+/// 命名对象类
+///
+/// 名称
+/// 描述
+public record NamedItem(string Key, string? Description = null)
+{
+ ///
+ /// 名称
+ ///
+ public string Key { get; init; } = Key;
+
+ ///
+ /// 描述
+ ///
+ [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;
+
+///
+/// 基础 API 服务
+///
+[ApiController]
+[Produces("application/json")]
+[Route("api")]
+public partial class ApiController : BaseController
+{
+ ///
+ public ApiController(FlowerDatabase database, ILogger? logger = null) : base(database, logger)
+ {
+ }
+
+ ///
+ /// 获取版本号
+ ///
+ ///
+ /// 请求示例:
+ ///
+ /// GET /api/version
+ ///
+ ///
+ /// 版本号
+ /// 返回版本号
+ [Route("version", Name = "getVersion")]
+ [HttpGet]
+ [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
+ public ActionResult GetApiVersion()
+ {
+ return Ok(Program.Version);
+ }
+
+ ///
+ /// 获取常量字典定义
+ ///
+ ///
+ /// 请求示例:
+ ///
+ /// GET /api/consts?{ver}
+ ///
+ ///
+ /// 字典集
+ /// 返回常量字典集
+ [Route("consts", Name = "getConsts")]
+ [HttpGet]
+ public ActionResult 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;
+
+///
+/// 字典、定义
+///
+public record DefinitionResult
+{
+ ///
+ /// 花草分类
+ ///
+ public required Dictionary Categories { get; init; }
+
+ ///
+ /// 事件
+ ///
+ public required Dictionary 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.";
+ ///
+ /// 临时 id
+ ///
+ protected const int TempId = -1;
+
+ private static string? uploadsDirectory;
+ ///
+ /// 文件上传路径
+ ///
+ protected static string UploadsDirectory => uploadsDirectory ??= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "uploads");
+
///
/// 支持的图片文件签名
///
@@ -195,43 +206,52 @@ public abstract partial class BaseController : ControllerBase
/// 文件结果对象
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;
+ }
+
///
/// 写入文件到用户的花草目录中
///
@@ -240,11 +260,7 @@ public abstract partial class BaseController : ControllerBase
/// 取消令牌
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
/// 返回是否已删除
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;
}
+ ///
+ /// 移动临时照片到花草目录
+ ///
+ /// 花草唯一 id
+ /// 文件路径
+ 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 GetRecords(
@@ -126,6 +127,7 @@ public class EventApiController : BaseController
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpDelete]
public ActionResult 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 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: <photo>[]
///
///
/// 事件参数
@@ -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 AddEvent([FromBody] EventParameter @event)
+ [Consumes("multipart/form-data")]
+ // 5 photos
+ [RequestSizeLimit(75 * 1024 * 1024)]
+ public async Task> 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: <photo>[]
///
///
/// 修改参数
@@ -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 Update([FromBody] EventUpdateParameter update)
+ [Consumes("multipart/form-data")]
+ // 5 photos
+ [RequestSizeLimit(75 * 1024 * 1024)]
+ public async Task> 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 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 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 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
///
[Required]
- public int FlowerId { get; init; }
+ [FromForm(Name = "flowerId")]
+ public required int FlowerId { get; init; }
///
- /// 事件 id
+ /// 事件分类 id
///
[Required]
- public int EventId { get; init; }
+ [FromForm(Name = "categoryId")]
+ public required int CategoryId { get; init; }
///
/// 操作人姓名
///
[Required]
- public string? ByUser { get; init; }
+ [FromForm(Name = "byUser")]
+ public required string ByUser { get; init; }
///
- /// 事件备注
+ /// 备注
///
- public string? Memo { get; init; }
+ [FromForm(Name = "memo")]
+ public string? Memo { get; set; }
+
+ ///
+ /// 纬度
+ ///
+ [FromForm(Name = "lat")]
+ public double? Latitude { get; set; }
+
+ ///
+ /// 经度
+ ///
+ [FromForm(Name = "lon")]
+ public double? Longitude { get; set; }
+
+ ///
+ /// 关联的照片
+ ///
+ [FromForm(Name = "photos")]
+ public IFormFile[]? Photos { get; init; }
}
///
@@ -40,5 +63,6 @@ public record EventUpdateParameter : EventParameter
/// 事件唯一 id
///
[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 GetFlowers(
@@ -96,7 +95,88 @@ public class FlowerApiController : BaseController
SaveDatabase();
- IEnumerable 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);
+ }
+
+ ///
+ /// 获取最新、最近的花草
+ ///
+ ///
+ /// 请求示例:
+ ///
+ /// 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?
+ ///
+ ///
+ /// 类别 id
+ /// 查询关键字
+ /// 起始购买日期
+ /// 结束购买日期
+ /// 开销最小值
+ /// 开销最大值
+ /// 是否包含封面图片
+ /// 纬度
+ /// 经度
+ /// 距离(米)
+ /// 页数
+ /// 分页大小
+ /// 会话有效则返回符合条件的花草集
+ /// 返回符合条件的花草集
+ /// 未找到登录会话或已过期
+ /// 用户已禁用
+ /// 未找到关联用户
+ [Route("latest", Name = "queryLatestFlowers")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [HttpGet]
+ [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
+ public ActionResult 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 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
- });
+ };
}
///
@@ -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 GetFlower(
@@ -254,6 +340,7 @@ public class FlowerApiController : BaseController
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
+ [ProducesErrorResponseType(typeof(ErrorResponse))]
[HttpDelete]
public ActionResult 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 RemoveFlower([FromBody] int[] ids)
+ public ActionResult 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: <photo>
///
///
/// 花草参数
/// 添加成功则返回已添加的花草对象
/// 返回已添加的花草对象
+ /// 提交的内容不合法
/// 未找到登录会话或已过期
/// 用户已禁用
/// 未找到关联用户
/// 提交正文过大
[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);
}
+ ///
+ /// 用户上传封面
+ ///
+ ///
+ /// 请求示例:
+ ///
+ /// POST /api/flower/cover_upload
+ /// Authorization: authorization id
+ ///
+ /// 参数:
+ ///
+ /// cover: <photo>
+ ///
+ ///
+ /// 封面参数
+ /// 添加成功则返回封面 id
+ /// 返回已添加的封面 id
+ /// 提交的内容不合法
+ /// 未找到登录会话或已过期
+ /// 用户已禁用
+ /// 未找到关联用户
+ /// 提交正文过大
+ [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> 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();
+ }
+
///
/// 修改花草
///
@@ -451,22 +673,28 @@ public class FlowerApiController : BaseController
/// dateBuy: 1684935276117
/// cost: 15.40
/// purchase: null
+ /// memo: "备注"
+ /// lon: 29.5462794
+ /// lat: 106.5380034
/// cover: <photo>
///
///
/// 修改参数
/// 修改成功则返回已修改的花草对象
/// 返回已修改的花草对象
+ /// 提交的内容不合法
/// 未找到登录会话或已过期
/// 用户已禁用
/// 未找到关联用户或者未找到将修改的花草对象
/// 提交正文过大
[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
///
///
- /// 花草唯一 id
- /// 封面图片
+ /// 封面修改参数
/// 修改成功则返回 HTTP 204
/// 修改成功
/// 照片格式非法
@@ -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 UploadCovers([Required][FromQuery] int id, [Required] IFormFile photo)
+ public async Task 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 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;
+///
+/// 封面参数
+///
+public record CoverParameter
+{
+ ///
+ /// 封面
+ ///
+ [FromForm(Name = "cover")]
+ public IFormFile? Cover { get; init; }
+
+ ///
+ /// 封面 id
+ ///
+ [FromForm(Name = "cid")]
+ public int? CoverId { get; init; }
+}
+
///
/// 花草参数
///
-public record FlowerParameter
+public record FlowerParameter : CoverParameter
{
///
/// 类别 id
///
[Required]
[FromForm(Name = "categoryId")]
- public int CategoryId { get; init; }
+ public required int CategoryId { get; init; }
///
/// 花草名称
@@ -28,7 +46,7 @@ public record FlowerParameter
///
[Required]
[FromForm(Name = "dateBuy")]
- public long DateBuy { get; init; }
+ public required long DateBuy { get; init; }
///
/// 购买花费
@@ -43,10 +61,22 @@ public record FlowerParameter
public string? Purchase { get; init; }
///
- /// 花草封面
+ /// 备注
///
- [FromForm(Name = "cover")]
- public IFormFile? Cover { get; init; }
+ [FromForm(Name = "memo")]
+ public string? Memo { get; set; }
+
+ ///
+ /// 纬度
+ ///
+ [FromForm(Name = "lat")]
+ public double? Latitude { get; set; }
+
+ ///
+ /// 经度
+ ///
+ [FromForm(Name = "lon")]
+ public double? Longitude { get; set; }
}
///
@@ -59,7 +89,32 @@ public record FlowerUpdateParameter : FlowerParameter
///
[Required]
[FromForm(Name = "id")]
- public int Id { get; init; }
+ public required int Id { get; init; }
+}
+
+///
+/// 花草封面修改参数
+///
+public record FlowerCoverParameter : CoverParameter
+{
+ ///
+ /// 花草唯一 id
+ ///
+ [Required]
+ [FromForm(Name = "id")]
+ public required int Id { get; init; }
+
+ ///
+ /// 纬度
+ ///
+ [FromForm(Name = "lat")]
+ public double? Latitude { get; set; }
+
+ ///
+ /// 经度
+ ///
+ [FromForm(Name = "lon")]
+ public double? Longitude { get; set; }
}
///
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();
}
+ ///
+ /// 判断当前会话是否有效
+ ///
+ ///
+ /// 请求示例:
+ ///
+ /// GET /api/user/validation
+ /// Authorization: authorization id
+ ///
+ ///
+ /// 会话有效则返回会话对象
+ /// 返回会话对象
+ /// 认证失败
+ [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 Validation()
+ {
+ var (result, token) = CheckToken();
+ if (result != null)
+ {
+ return result;
+ }
+ if (token == null)
+ {
+ return Unauthorized();
+ }
+
+ // update last active time
+ SaveDatabase();
+
+ return Ok(token);
+ }
+
///
/// 注销当前登录会话
///
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; }
///
/// 所有人
@@ -37,7 +37,7 @@ public class FlowerItem : ILocation
///
[Column("categoryid")]
[Required]
- public int CategoryId { get; set; }
+ public required int CategoryId { get; set; }
///
/// 花草名称
@@ -52,7 +52,7 @@ public class FlowerItem : ILocation
[Column("datebuy")]
[Required]
[JsonPropertyName("dateBuy")]
- public long DateBuyUnixTime { get; set; }
+ public required long DateBuyUnixTime { get; set; }
///
/// 购买花费
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; }
+ ///
+ /// 关联人 id
+ ///
+ [Column("uid")]
+ [ForeignKey(nameof(Owner))]
+ [Required]
+ public required int OwnerId { get; set; }
+
+ ///
+ /// 关联人
+ ///
+ [JsonIgnore]
+ public UserItem? Owner { get; set; }
+
///
/// 关联花草 id
///
[Column("fid")]
[ForeignKey(nameof(Flower))]
- [Required]
- public int FlowerId { get; set; }
+ public int? FlowerId { get; set; }
///
- /// 关联花草
+ /// 关联的花草
///
[JsonIgnore]
public FlowerItem? Flower { get; set; }
@@ -37,8 +50,7 @@ public class PhotoItem
///
[Column("rid")]
[ForeignKey(nameof(Record))]
- [Required]
- public int RecordId { get; set; }
+ public int? RecordId { get; set; }
///
/// 关联的事件
@@ -73,7 +85,7 @@ public class PhotoItem
[Column("dateupload")]
[Required]
[JsonPropertyName("dateUpload")]
- public long DateUploadUnixTime { get; set; }
+ public required long DateUploadUnixTime { get; set; }
///
/// 上传时间
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; }
///
/// 关联人
@@ -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; }
///
/// 关联花草
@@ -51,7 +51,7 @@ public class RecordItem : ILocation
///
[Column("eid")]
[Required]
- public int EventId { get; set; }
+ public required int EventId { get; set; }
///
/// 操作时间
@@ -59,7 +59,7 @@ public class RecordItem : ILocation
[Column("date")]
[Required]
[JsonPropertyName("date")]
- public long DateUnixTime { get; set; }
+ public required long DateUnixTime { get; set; }
///
/// 操作人 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
///
[Column("uid")]
[Required]
- public int UserId { get; set; }
+ public required int UserId { get; set; }
///
/// 登录时间
@@ -31,7 +31,7 @@ public class TokenItem
[Column("logondate")]
[Required]
[JsonPropertyName("logonDate")]
- public long LogonDateUnixTime { get; set; }
+ public required long LogonDateUnixTime { get; set; }
///
/// 活动时间
@@ -39,7 +39,7 @@ public class TokenItem
[Column("activedate")]
[Required]
[JsonPropertyName("activeDate")]
- public long ActiveDateUnixTime { get; set; }
+ public required long ActiveDateUnixTime { get; set; }
///
/// 过期时间
@@ -47,14 +47,14 @@ public class TokenItem
[Column("expiredate")]
[Required]
[JsonPropertyName("expireDate")]
- public long ExpireDateUnixTime { get; set; }
+ public required long ExpireDateUnixTime { get; set; }
///
/// 过期秒数
///
[Column("expiresecs")]
[Required]
- public int ExpireSeconds { get; set; }
+ public required int ExpireSeconds { get; set; }
///
/// 验证码
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
///
[Column("level")]
[Required]
- public int Level { get; set; }
+ public required int Level { get; set; }
///
/// 注册时间
@@ -49,7 +49,7 @@ public class UserItem
[Column("regdate")]
[Required]
[JsonPropertyName("registerDate")]
- public long RegisterDateUnixTime { get; set; }
+ public required long RegisterDateUnixTime { get; set; }
///
/// 最后变动时间
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 @@
-//
-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
- {
- ///
- 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("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("fid");
-
- b.Property("CategoryId")
- .HasColumnType("INTEGER")
- .HasColumnName("categoryid");
-
- b.Property("Cost")
- .HasColumnType("real")
- .HasColumnName("cost");
-
- b.Property("DateBuy")
- .HasColumnType("numeric")
- .HasColumnName("datebuy");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.Property("Purchase")
- .HasColumnType("TEXT")
- .HasColumnName("purchase");
-
- b.HasKey("Id");
-
- b.ToTable("flowers");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("rid");
-
- b.Property("ByUserId")
- .HasColumnType("INTEGER")
- .HasColumnName("byuid");
-
- b.Property("ByUserName")
- .HasColumnType("TEXT")
- .HasColumnName("byname");
-
- b.Property("Date")
- .HasColumnType("numeric")
- .HasColumnName("date");
-
- b.Property("EventId")
- .HasColumnType("INTEGER")
- .HasColumnName("eid");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.HasKey("Id");
-
- b.ToTable("records");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("ActiveDate")
- .HasColumnType("numeric")
- .HasColumnName("activedate");
-
- b.Property("Email")
- .HasColumnType("TEXT")
- .HasColumnName("email");
-
- b.Property("Level")
- .HasColumnType("INTEGER")
- .HasColumnName("level");
-
- b.Property("Mobile")
- .HasColumnType("TEXT")
- .HasColumnName("mobile");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("Password")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("password");
-
- b.Property("RegisterDate")
- .HasColumnType("numeric")
- .HasColumnName("regdate");
-
- b.Property("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
-{
- ///
- public partial class InitialCreateDb : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "flowers",
- columns: table => new
- {
- fid = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- categoryid = table.Column(type: "INTEGER", nullable: false),
- name = table.Column(type: "TEXT", nullable: false),
- datebuy = table.Column(type: "numeric", nullable: false),
- cost = table.Column(type: "real", nullable: true),
- purchase = table.Column(type: "TEXT", nullable: true),
- photo = table.Column(type: "BLOB", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_flowers", x => x.fid);
- });
-
- migrationBuilder.CreateTable(
- name: "records",
- columns: table => new
- {
- rid = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- eid = table.Column(type: "INTEGER", nullable: false),
- date = table.Column(type: "numeric", nullable: false),
- byuid = table.Column(type: "INTEGER", nullable: true),
- byname = table.Column(type: "TEXT", nullable: true),
- photo = table.Column(type: "BLOB", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_records", x => x.rid);
- });
-
- migrationBuilder.CreateTable(
- name: "users",
- columns: table => new
- {
- uid = table.Column(type: "INTEGER", nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
- id = table.Column(type: "TEXT", nullable: false),
- password = table.Column(type: "TEXT", nullable: false),
- level = table.Column(type: "INTEGER", nullable: false),
- regdate = table.Column(type: "numeric", nullable: false),
- activedate = table.Column(type: "numeric", nullable: true),
- name = table.Column(type: "TEXT", nullable: false),
- email = table.Column(type: "TEXT", nullable: true),
- mobile = table.Column(type: "TEXT", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_users", x => x.uid);
- });
- }
-
- ///
- 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 @@
-//
-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
- {
- ///
- 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("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("fid");
-
- b.Property("CategoryId")
- .HasColumnType("INTEGER")
- .HasColumnName("categoryid");
-
- b.Property("Cost")
- .HasColumnType("real")
- .HasColumnName("cost");
-
- b.Property("DateBuyUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("datebuy");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.Property("Purchase")
- .HasColumnType("TEXT")
- .HasColumnName("purchase");
-
- b.HasKey("Id");
-
- b.ToTable("flowers");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("rid");
-
- b.Property("ByUserId")
- .HasColumnType("INTEGER")
- .HasColumnName("byuid");
-
- b.Property("ByUserName")
- .HasColumnType("TEXT")
- .HasColumnName("byname");
-
- b.Property("DateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("date");
-
- b.Property("EventId")
- .HasColumnType("INTEGER")
- .HasColumnName("eid");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.HasKey("Id");
-
- b.ToTable("records");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("ActiveDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("activedate");
-
- b.Property("Email")
- .HasColumnType("TEXT")
- .HasColumnName("email");
-
- b.Property("Level")
- .HasColumnType("INTEGER")
- .HasColumnName("level");
-
- b.Property("Mobile")
- .HasColumnType("TEXT")
- .HasColumnName("mobile");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("Password")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("password");
-
- b.Property("RegisterDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("regdate");
-
- b.Property("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
-{
- ///
- public partial class ChangeDateColumnType : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AlterColumn(
- name: "regdate",
- table: "users",
- type: "INTEGER",
- nullable: false,
- oldClrType: typeof(DateTimeOffset),
- oldType: "numeric");
-
- migrationBuilder.AlterColumn(
- name: "activedate",
- table: "users",
- type: "INTEGER",
- nullable: true,
- oldClrType: typeof(DateTimeOffset),
- oldType: "numeric",
- oldNullable: true);
-
- migrationBuilder.AlterColumn(
- name: "date",
- table: "records",
- type: "INTEGER",
- nullable: false,
- oldClrType: typeof(DateTimeOffset),
- oldType: "numeric");
-
- migrationBuilder.AlterColumn(
- name: "datebuy",
- table: "flowers",
- type: "INTEGER",
- nullable: false,
- oldClrType: typeof(DateTimeOffset),
- oldType: "numeric");
- }
-
- ///
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AlterColumn(
- name: "regdate",
- table: "users",
- type: "numeric",
- nullable: false,
- oldClrType: typeof(long),
- oldType: "INTEGER");
-
- migrationBuilder.AlterColumn(
- name: "activedate",
- table: "users",
- type: "numeric",
- nullable: true,
- oldClrType: typeof(long),
- oldType: "INTEGER",
- oldNullable: true);
-
- migrationBuilder.AlterColumn(
- name: "date",
- table: "records",
- type: "numeric",
- nullable: false,
- oldClrType: typeof(long),
- oldType: "INTEGER");
-
- migrationBuilder.AlterColumn(
- 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 @@
-//
-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
- {
- ///
- 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("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("fid");
-
- b.Property("CategoryId")
- .HasColumnType("INTEGER")
- .HasColumnName("categoryid");
-
- b.Property("Cost")
- .HasColumnType("real")
- .HasColumnName("cost");
-
- b.Property("DateBuyUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("datebuy")
- .HasAnnotation("Relational:JsonPropertyName", "dateBuy");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.Property("Purchase")
- .HasColumnType("TEXT")
- .HasColumnName("purchase");
-
- b.HasKey("Id");
-
- b.ToTable("flowers");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("rid");
-
- b.Property("ByUserId")
- .HasColumnType("INTEGER")
- .HasColumnName("byuid");
-
- b.Property("ByUserName")
- .HasColumnType("TEXT")
- .HasColumnName("byname");
-
- b.Property("DateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("date")
- .HasAnnotation("Relational:JsonPropertyName", "date");
-
- b.Property("EventId")
- .HasColumnType("INTEGER")
- .HasColumnName("eid");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.HasKey("Id");
-
- b.ToTable("records");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.TokenItem", b =>
- {
- b.Property("Id")
- .HasColumnType("TEXT")
- .HasColumnName("tid");
-
- b.Property("ActiveDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("activedate");
-
- b.Property("ClientAgent")
- .HasColumnType("TEXT")
- .HasColumnName("clientagent");
-
- b.Property("ClientApp")
- .HasColumnType("TEXT")
- .HasColumnName("clientapp");
-
- b.Property("DeviceId")
- .HasColumnType("TEXT")
- .HasColumnName("deviceid");
-
- b.Property("ExpireDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("expiredate");
-
- b.Property("ExpireSeconds")
- .HasColumnType("INTEGER")
- .HasColumnName("expiresecs");
-
- b.Property("LogonDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("logondate");
-
- b.Property("UserId")
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("VerifyCode")
- .HasColumnType("TEXT")
- .HasColumnName("verifycode");
-
- b.HasKey("Id");
-
- b.ToTable("tokens");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("ActiveDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("activedate");
-
- b.Property("Email")
- .HasColumnType("TEXT")
- .HasColumnName("email");
-
- b.Property("Level")
- .HasColumnType("INTEGER")
- .HasColumnName("level");
-
- b.Property("Mobile")
- .HasColumnType("TEXT")
- .HasColumnName("mobile");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("Password")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("password");
-
- b.Property("RegisterDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("regdate")
- .HasAnnotation("Relational:JsonPropertyName", "registerDate");
-
- b.Property("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
-{
- ///
- public partial class AddTokens : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.CreateTable(
- name: "tokens",
- columns: table => new
- {
- tid = table.Column(type: "TEXT", nullable: false),
- uid = table.Column(type: "INTEGER", nullable: false),
- logondate = table.Column(type: "INTEGER", nullable: false),
- activedate = table.Column(type: "INTEGER", nullable: false),
- expiredate = table.Column(type: "INTEGER", nullable: false),
- expiresecs = table.Column(type: "INTEGER", nullable: false),
- verifycode = table.Column(type: "TEXT", nullable: true),
- clientapp = table.Column(type: "TEXT", nullable: true),
- deviceid = table.Column(type: "TEXT", nullable: true),
- clientagent = table.Column(type: "TEXT", nullable: true)
- },
- constraints: table =>
- {
- table.PrimaryKey("PK_tokens", x => x.tid);
- });
- }
-
- ///
- 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 @@
-//
-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
- {
- ///
- 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("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("fid");
-
- b.Property("CategoryId")
- .HasColumnType("INTEGER")
- .HasColumnName("categoryid");
-
- b.Property("Cost")
- .HasColumnType("real")
- .HasColumnName("cost");
-
- b.Property("DateBuyUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("datebuy")
- .HasAnnotation("Relational:JsonPropertyName", "dateBuy");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("OwnerId")
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.Property("Purchase")
- .HasColumnType("TEXT")
- .HasColumnName("purchase");
-
- b.HasKey("Id");
-
- b.ToTable("flowers");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("rid");
-
- b.Property("ByUserId")
- .HasColumnType("INTEGER")
- .HasColumnName("byuid");
-
- b.Property("ByUserName")
- .HasColumnType("TEXT")
- .HasColumnName("byname");
-
- b.Property("DateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("date")
- .HasAnnotation("Relational:JsonPropertyName", "date");
-
- b.Property("EventId")
- .HasColumnType("INTEGER")
- .HasColumnName("eid");
-
- b.Property("OwnerId")
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.HasKey("Id");
-
- b.ToTable("records");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.TokenItem", b =>
- {
- b.Property("Id")
- .HasColumnType("TEXT")
- .HasColumnName("tid");
-
- b.Property("ActiveDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("activedate")
- .HasAnnotation("Relational:JsonPropertyName", "activeDate");
-
- b.Property("ClientAgent")
- .HasColumnType("TEXT")
- .HasColumnName("clientagent");
-
- b.Property("ClientApp")
- .HasColumnType("TEXT")
- .HasColumnName("clientapp");
-
- b.Property("DeviceId")
- .HasColumnType("TEXT")
- .HasColumnName("deviceid");
-
- b.Property("ExpireDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("expiredate")
- .HasAnnotation("Relational:JsonPropertyName", "expireDate");
-
- b.Property("ExpireSeconds")
- .HasColumnType("INTEGER")
- .HasColumnName("expiresecs");
-
- b.Property("LogonDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("logondate")
- .HasAnnotation("Relational:JsonPropertyName", "logonDate");
-
- b.Property("UserId")
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("VerifyCode")
- .HasColumnType("TEXT")
- .HasColumnName("verifycode");
-
- b.HasKey("Id");
-
- b.ToTable("tokens");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("ActiveDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("activedate");
-
- b.Property("Email")
- .HasColumnType("TEXT")
- .HasColumnName("email");
-
- b.Property("Level")
- .HasColumnType("INTEGER")
- .HasColumnName("level");
-
- b.Property("Mobile")
- .HasColumnType("TEXT")
- .HasColumnName("mobile");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("Password")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("password");
-
- b.Property("RegisterDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("regdate")
- .HasAnnotation("Relational:JsonPropertyName", "registerDate");
-
- b.Property("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
-{
- ///
- public partial class AddOwner : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn(
- name: "uid",
- table: "records",
- type: "INTEGER",
- nullable: false,
- defaultValue: 0);
-
- migrationBuilder.AddColumn(
- name: "uid",
- table: "flowers",
- type: "INTEGER",
- nullable: false,
- defaultValue: 0);
- }
-
- ///
- 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 @@
-//
-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
- {
- ///
- 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("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("fid");
-
- b.Property("CategoryId")
- .HasColumnType("INTEGER")
- .HasColumnName("categoryid");
-
- b.Property("Cost")
- .HasColumnType("real")
- .HasColumnName("cost");
-
- b.Property("DateBuyUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("datebuy")
- .HasAnnotation("Relational:JsonPropertyName", "dateBuy");
-
- b.Property("Memo")
- .HasColumnType("TEXT")
- .HasColumnName("memo");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("OwnerId")
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.Property("Purchase")
- .HasColumnType("TEXT")
- .HasColumnName("purchase");
-
- b.HasKey("Id");
-
- b.ToTable("flowers");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.RecordItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("rid");
-
- b.Property("ByUserId")
- .HasColumnType("INTEGER")
- .HasColumnName("byuid");
-
- b.Property("ByUserName")
- .HasColumnType("TEXT")
- .HasColumnName("byname");
-
- b.Property("DateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("date")
- .HasAnnotation("Relational:JsonPropertyName", "date");
-
- b.Property("EventId")
- .HasColumnType("INTEGER")
- .HasColumnName("eid");
-
- b.Property("Memo")
- .HasColumnType("TEXT")
- .HasColumnName("memo");
-
- b.Property("OwnerId")
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("Photo")
- .HasColumnType("BLOB")
- .HasColumnName("photo");
-
- b.HasKey("Id");
-
- b.ToTable("records");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.TokenItem", b =>
- {
- b.Property("Id")
- .HasColumnType("TEXT")
- .HasColumnName("tid");
-
- b.Property("ActiveDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("activedate")
- .HasAnnotation("Relational:JsonPropertyName", "activeDate");
-
- b.Property("ClientAgent")
- .HasColumnType("TEXT")
- .HasColumnName("clientagent");
-
- b.Property("ClientApp")
- .HasColumnType("TEXT")
- .HasColumnName("clientapp");
-
- b.Property("DeviceId")
- .HasColumnType("TEXT")
- .HasColumnName("deviceid");
-
- b.Property("ExpireDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("expiredate")
- .HasAnnotation("Relational:JsonPropertyName", "expireDate");
-
- b.Property("ExpireSeconds")
- .HasColumnType("INTEGER")
- .HasColumnName("expiresecs");
-
- b.Property("LogonDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("logondate")
- .HasAnnotation("Relational:JsonPropertyName", "logonDate");
-
- b.Property("UserId")
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("VerifyCode")
- .HasColumnType("TEXT")
- .HasColumnName("verifycode");
-
- b.HasKey("Id");
-
- b.ToTable("tokens");
- });
-
- modelBuilder.Entity("Blahblah.FlowerStory.Server.Data.Model.UserItem", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("ActiveDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("activedate");
-
- b.Property("Avatar")
- .HasColumnType("BLOB")
- .HasColumnName("avatar");
-
- b.Property("Email")
- .HasColumnType("TEXT")
- .HasColumnName("email");
-
- b.Property("Level")
- .HasColumnType("INTEGER")
- .HasColumnName("level");
-
- b.Property("Mobile")
- .HasColumnType("TEXT")
- .HasColumnName("mobile");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("Password")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("password");
-
- b.Property("RegisterDateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("regdate")
- .HasAnnotation("Relational:JsonPropertyName", "registerDate");
-
- b.Property("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
-{
- ///
- public partial class AddAvatarMemo : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.AddColumn(
- name: "avatar",
- table: "users",
- type: "BLOB",
- nullable: true);
-
- migrationBuilder.AddColumn(
- name: "memo",
- table: "records",
- type: "TEXT",
- nullable: true);
-
- migrationBuilder.AddColumn(
- name: "memo",
- table: "flowers",
- type: "TEXT",
- nullable: true);
- }
-
- ///
- 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 @@
-//
-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
- {
- ///
- 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("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("fid");
-
- b.Property("CategoryId")
- .HasColumnType("INTEGER")
- .HasColumnName("categoryid");
-
- b.Property("Cost")
- .HasColumnType("real")
- .HasColumnName("cost");
-
- b.Property("DateBuyUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("datebuy")
- .HasAnnotation("Relational:JsonPropertyName", "dateBuy");
-
- b.Property("Memo")
- .HasColumnType("TEXT")
- .HasColumnName("memo");
-
- b.Property("Name")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("name");
-
- b.Property("OwnerId")
- .HasColumnType("INTEGER")
- .HasColumnName("uid");
-
- b.Property("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("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("pid");
-
- b.Property("DateUploadUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("dateupload")
- .HasAnnotation("Relational:JsonPropertyName", "dateUpload");
-
- b.Property("FileName")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("filename");
-
- b.Property("FileType")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("filetype");
-
- b.Property("Path")
- .IsRequired()
- .HasColumnType("TEXT")
- .HasColumnName("path");
-
- b.Property("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("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER")
- .HasColumnName("rid");
-
- b.Property("ByUserId")
- .HasColumnType("INTEGER")
- .HasColumnName("byuid");
-
- b.Property("ByUserName")
- .HasColumnType("TEXT")
- .HasColumnName("byname");
-
- b.Property("DateUnixTime")
- .HasColumnType("INTEGER")
- .HasColumnName("date")
- .HasAnnotation("Relational:JsonPropertyName", "date");
-
- b.Property("EventId")
- .HasColumnType("INTEGER")
- .HasColumnName("eid");
-
- b.Property