public async Task <NoteGrpcResponse> GetNotesAsync(NoteGrpcRequest request, CallContext context = default)
    {
        var response = new NoteGrpcResponse();

        try
        {
            var notes = await _noteFileService.GetNotesAsync(request.Year);

            if (request.Offset.HasValue)
            {
                notes = notes.Skip(request.Offset.Value).Take(request.Count).ToList();
            }

            var tags = await _noteFileService.GetNoteTagsAsync();

            foreach (var note in notes)
            {
                response.Notes.Add(GetNoteRuntime(note, request.ExtractRuntime, tags));
            }
        }
        catch (Exception ex)
        {
            response.IsOk    = false;
            response.Message = ex.Message;
            _logger.LogError(ex, $"{nameof(GetNotesAsync)} failed. {JsonUtil.Serialize(request)}");
        }

        return(response);
    }
    public async Task <NoteGrpcResponse> GetNoteTagByLinkAsync(NoteGrpcRequest request, CallContext context = default)
    {
        var response = new NoteGrpcResponse();

        try
        {
            var tag = await _noteFileService.GetNoteTagByLinkAsync(request.TagLink);

            if (tag == null)
            {
                response.IsOk    = false;
                response.Message = $"Tag not found: {request.TagLink}";
            }
            else
            {
                var notes = await _noteFileService.GetNotesAsync();

                response.TagRuntime = GetNoteTagRuntime(tag, request.ExtractRuntime, notes);
            }
        }
        catch (Exception ex)
        {
            response.IsOk    = false;
            response.Message = ex.Message;
            _logger.LogError(ex, $"{nameof(GetNoteTagByLinkAsync)} failed. {JsonUtil.Serialize(request)}");
        }

        return(response);
    }
    public async Task <NoteGrpcResponse> GetNoteTagsAsync(NoteGrpcRequest request, CallContext context = default)
    {
        var response = new NoteGrpcResponse();

        try
        {
            var tags = await _noteFileService.GetNoteTagsAsync();

            foreach (var tag in tags)
            {
                var notes = await _noteFileService.GetNotesAsync();

                var runtime = GetNoteTagRuntime(tag, true, notes);
                response.Tags.Add(runtime);
            }
        }
        catch (Exception ex)
        {
            response.IsOk    = false;
            response.Message = ex.Message;
            _logger.LogError(ex, $"{nameof(GetNoteTagsAsync)} failed. {JsonUtil.Serialize(request)}");
        }

        return(response);
    }
    public async Task <NoteGrpcResponse> DeleteNoteTagAsync(NoteGrpcRequest request, CallContext context = default)
    {
        var response = new NoteGrpcResponse();

        try
        {
            await _noteFileService.DeleteNoteTagAsync(request.Id);
        }
        catch (Exception ex)
        {
            response.IsOk    = false;
            response.Message = ex.Message;
            _logger.LogError(ex, $"{nameof(DeleteNoteTagAsync)} failed. {JsonUtil.Serialize(request)}");
        }

        return(response);
    }
    public async Task <NoteGrpcResponse> GetNotesCountAsync(NoteGrpcRequest request, CallContext context = default)
    {
        var response = new NoteGrpcResponse();

        try
        {
            var notes = await _noteFileService.GetNotesAsync(request.Year);

            response.Count = notes.Count;
        }
        catch (Exception ex)
        {
            response.IsOk    = false;
            response.Message = ex.Message;
            _logger.LogError(ex, $"{nameof(GetNotesCountAsync)} failed. {JsonUtil.Serialize(request)}");
        }

        return(response);
    }
    public async Task <NoteGrpcResponse> GetStatWordsPerYearAsync(NoteGrpcRequest request, CallContext context = default)
    {
        var response = new NoteGrpcResponse();

        try
        {
            var notes = await _noteFileService.GetNotesAsync();

            foreach (var item in notes.GroupBy(x => x.CreateTime.Year).OrderBy(x => x.Key))
            {
                response.Data.Add($"{item.Key}年", item.Sum(x => x.MdContent.Length));
            }
        }
        catch (Exception ex)
        {
            response.IsOk    = false;
            response.Message = ex.Message;
            _logger.LogError(ex, $"{nameof(GetStatWordsPerYearAsync)} failed. {JsonUtil.Serialize(request)}");
        }

        return(response);
    }