您的位置:首页 > 编程语言 > ASP

ASP.NET Core 打造一个简单的图书馆管理系统(五)初始化书籍信息

2019-01-31 21:30 956 查看
前言:

本系列文章主要为我之前所学知识的一次微小的实践,以我学校图书馆管理系统为雏形所作。

本系列文章主要参考资料:

微软文档:https://docs.microsoft.com/zh-cn/aspnet/core/getting-started/?view=aspnetcore-2.1&tabs=windows

《Pro ASP.NET MVC 5》、《锋利的 jQuery》

 

此系列皆使用 VS2017+C# 作为开发环境。如果有什么问题或者意见欢迎在留言区进行留言。 

项目 github 地址:https://github.com/NanaseRuri/LibraryDemo

 

 

本章内容:自定义 HtmlHelper、Session 的使用、分页、数据库初始化器、取消 int 主键自动增长

 

 

一、对图书信息的域模型进行修改

由于在此处我使用了二维码作为主键,一个二维码不可能同时放在不同的书架上,因此重写之前建立的多对多的模型。

移除 BookMiddle,去除 Book类 中的 ICollection<BookMiddle> 字段,改为 Bookshelf 字段。

public class Book
{
/// <summary>
/// 二维码
/// </summary>
[Key]
public string BarCode { get; set; }

public string ISBN { get; set; }

/// <summary>
/// 书名
/// </summary>
[Required]
public string Name { get; set; }

/// <summary>
/// 取书号
/// </summary>
public string FetchBookNumber { get; set; }

/// <summary>
/// 所在书架
/// </summary>
//public Bookshelf Bookshelf { get; set; }
public Bookshelf Bookshelf { get; set; }

/// <summary>
/// 借出时间
/// </summary>
public DateTime? BorrowTime { get; set; }

/// <summary>
/// 到期时间
/// </summary>
public DateTime? MatureTime { get; set; }

/// <summary>
/// 预约最晚借书日期
/// </summary>
public DateTime? AppointedLatestTime { get; set; }

/// <summary>
/// 借阅状态
/// </summary>
public BookState State { get; set; }

/// <summary>
/// 持有者,指定外键
/// </summary>
public Student Keeper { get; set; }
}
View Code

 

移除 Bookshelf 类中的  ICollection<BookMiddle> 字段,改为 ICollection<Book> 字段

public class Bookshelf
{
/// <summary>
/// 书架ID
/// </summary>
[Key]
public int BookshelfId { get; set; }

/// <summary>
/// 书架的书籍类别
/// </summary>

[Required]
public string Sort { get; set; }
/// <summary>
/// 最小取书号
/// </summary>
[Required]
public string MinFetchNumber { get; set; }
[Required]
public string MaxFetchNumber { get; set; }

/// <summary>
/// 书架位置
/// </summary>
[Required]
public string Location { get; set; }

/// <summary>
/// 全部藏书
/// </summary>
public ICollection<Book> Books { get; set; }
}
View Code

 

为了能够给书籍添加缩略图以及更方便的编写逻辑,为 Book 和 BookDetail 添加多个字段:

/// <summary>
/// 用于借阅的书籍信息
/// </summary>
public class Book
{
/// <summary>
/// 二维码
/// </summary>
[Key][Required(ErrorMessage = "未填写二维码")]
[Display(Name = "二维码")]
public string BarCode { get; set; }

public string ISBN { get; set; }

/// <summary>
/// 书名
/// </summary>
[Required]
[Display(Name = "书名")]
public string Name { get; set; }

/// <summary>
/// 取书号
/// </summary>
[Display(Name = "取书号")]
public string FetchBookNumber { get; set; }

/// <summary>
/// 所在书架
/// </summary>
public Bookshelf Bookshelf { get; set; }

[Display(Name = "书架号")]
public int BookshelfId { get; set; }

/// <summary>
/// 借出时间
/// </summary>
[Display(Name = "借出时间")]
public DateTime? BorrowTime { get; set; }

/// <summary>
/// 到期时间
/// </summary>
[Display(Name = "到期时间")]
public DateTime? MatureTime { get; set; }

/// <summary>
/// 预约最晚借书日期
/// </summary>
[Display(Name = "预约取书时间")]
public DateTime? AppointedLatestTime { get; set; }

/// <summary>
/// 借阅状态
/// </summary>
[Display(Name = "书籍状态")]
public BookState State { get; set; }

/// <summary>
/// 持有者,指定外键
/// </summary>
public Student Keeper { get; set; }
[Display(Name = "持有者学号")]
public string KeeperId { get; set; }

[Display(Name = "位置")]
public string Location { get; set; }

[Display(Name = "分类")]
public string Sort { get; set; }
}

 

/// <summary>
/// 书籍自身的详细信息
/// </summary>
public class BookDetails
{
[Key]
public string ISBN { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Author { get; set; }
[Required]
public string Press { get; set; }
[Required]
public string FetchBookNumber { get; set; }
/// <summary>
/// 出版时间
/// </summary>
[Required]
public DateTime PublishDateTime{ get; set; }

/// <summary>
/// 书籍版本
/// </summary>
[Required]
public int Version { get; set; }

/// <summary>
/// 载体形态,包括页数、媒介等信息
/// </summary>
public string SoundCassettes { get; set; }

/// <summary>
/// 简介
/// </summary>
public string Description { get; set; }

/// <summary>
/// 缩略图
/// </summary>
public byte[] ImageData { get; set; }
public string ImageMimeType { get; set; }
}

 

由于 EF 会自动将 int 类型的主键设置为自动增长,因此自定义 Bookshelf 的 ID 在插入数据库时会报错,在此需添加修饰 [DatabaseGenerated(DatabaseGeneratedOption.None)] 告知 ef 取消该设置:

public class Bookshelf
{
/// <summary>
/// 书架ID
/// </summary>
[Key]
//不自动增长
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BookshelfId { get; set; }

/// <summary>
/// 书架的书籍类别
/// </summary>

[Required]
public string Sort { get; set; }
/// <summary>
/// 最小取书号
/// </summary>
[Required]
public string MinFetchNumber { get; set; }
[Required]
public string MaxFetchNumber { get; set; }

/// <summary>
/// 书架位置
/// </summary>
[Required]
public string Location { get; set; }

/// <summary>
/// 全部藏书
/// </summary>
public ICollection<Book> Books { get; set; }
}

 

然后添加迁移并更新数据库,若更新失败在此选择移除数据库以及迁移再执行该步骤,生产中应当备份原文件:

cd LibraryDemo
add-migration RemoveBookMiddle -c LibraryDemo.Data.LendingInfoDbContext
update-database -c LibraryDemo.Data.LendingInfoDbContext

 

新建 BookInitiator 用于初始化该数据库,在这里会报一个 Book 重复主键的错误,不知道为什么但是数据确实全都插进去了:

public class BookInitiator
{
public static async Task BookInitial(IServiceProvider serviceProvider)
{
LendingInfoDbContext context = serviceProvider.GetRequiredService<LendingInfoDbContext>();
if (!context.Books.Any() || !context.Bookshelves.Any())
{
Bookshelf[] bookshelfs = new[]
{
new Bookshelf()
{
BookshelfId = 1,
Location = "主校区",
Sort = "计算机"
},
new Bookshelf()
{
BookshelfId = 2,
Location = "主校区",
Sort = "文学"
},
new Bookshelf()
{
BookshelfId = 3,
Location = "东校区",
Sort = "计算机"
},
new Bookshelf()
{
BookshelfId = 4,
Location = "阅览室",
Sort = "文学"
},
new Bookshelf()
{
BookshelfId = 5,
Location = "阅览室",
Sort = "计算机"
},
};

Book[] books = new[]
{
new Book()
{
Name = "精通ASP.NET MVC 5",
BarCode = "001100987211",
ISBN = "978-7-115-41023-8",
State = BookState.Normal,
FetchBookNumber = "TP393.092 19",
Location = "主校区",
Sort = "计算机"
},
new Book()
{
Name = "精通ASP.NET MVC 5",
BarCode = "001100987212",
ISBN = "978-7-115-41023-8",
State = BookState.Normal,
FetchBookNumber = "TP393.092 19",
Location = "主校区",
Sort = "计算机"
},
new Book()
{
Name = "精通ASP.NET MVC 5",
BarCode = "001100987213",
ISBN = "978-7-115-41023-8",
State = BookState.Normal,
FetchBookNumber = "TP393.092 19",
Location = "东校区",
Sort = "计算机"
},
new Book()
{
Name = "精通ASP.NET MVC 5",
BarCode = "001100987214",
ISBN = "978-7-115-41023-8",
State = BookState.Readonly,
FetchBookNumber = "TP393.092 19",
Location = "阅览室",
Sort = "计算机"
},
new Book()
{
Name = "Entity Framework实用精要",
BarCode = "001101279682",
ISBN = "978-7-302-48593-3",
State = BookState.Normal,
FetchBookNumber = "TP393.09 447",
Location = "主校区",
Sort = "计算机"
},
new Book()
{
Name = "Entity Framework实用精要",
BarCode = "001101279683",
ISBN = "978-7-302-48593-3",
State = BookState.Normal,
FetchBookNumber = "TP393.09 447",
Location = "主校区",
Sort = "计算机"
},
new Book()
{
Name = "Entity Framework实用精要",
BarCode = "001101279684",
ISBN = "978-7-302-48593-3",
State = BookState.Normal,
FetchBookNumber = "TP393.09 447",
Location = "东校区",
Sort = "计算机"
},
new Book()
{
Name = "Entity Framework实用精要",
BarCode = "001101279685",
ISBN = "978-7-302-48593-3",
State = BookState.Normal,
FetchBookNumber = "TP393.09 447",
Location = "东校区",
Sort = "计算机"
},
new Book()
{
Name = "Entity Framework实用精要",
BarCode = "001101279686",
ISBN = "978-7-302-48593-3",
State = BookState.Normal,
FetchBookNumber = "TP393.09 447",
Location = "阅览室",
Sort = "计算机"
},
new Book()
{
Name = "Rails 5敏捷开发",
BarCode = "001101290497",
ISBN = "978-7-5680-3659-7",
State = BookState.Normal,
FetchBookNumber = "TP393.09 448",
Location = "主校区",
Sort = "计算机"
},
new Book()
{
Name = "Rails 5敏捷开发",
BarCode = "001101290498",
ISBN = "978-7-5680-3659-7",
State = BookState.Normal,
FetchBookNumber = "TP393.09 448",
Location = "主校区",
Sort = "计算机"
},
new Book()
{
Name = "Rails 5敏捷开发",
BarCode = "001101290499",
ISBN = "978-7-5680-3659-7",
State = BookState.Readonly,
FetchBookNumber = "TP393.09 448",
Location = "主校区",
Sort = "计算机"
},
new Book()
{
Name = "你必须掌握的Entity Framework 6.x与Core 2.0",
BarCode = "001101362986",
ISBN = "978-7-5680-3659-7",
State = BookState.Normal,
FetchBookNumber = "TP393.09 452",
Location = "主校区",
Sort = "计算机"
},
new Book()
{
Name = "你必须掌握的Entity Framework 6.x与Core 2.0",
BarCode = "001101362987",
ISBN = "978-7-5680-3659-7",
State = BookState.Readonly,
FetchBookNumber = "TP393.09 452",
Location = "主校区",
Sort = "计算机"
},
new Book()
{
Name = "毛选. 第一卷",
BarCode = "00929264",
ISBN = "7-01-000922-8",
State = BookState.Normal,
FetchBookNumber = "A41 1:1",
Location = "主校区",
Sort = "文学"
},
new Book()
{
Name = "毛选. 第一卷",
BarCode = "00929265",
ISBN = "7-01-000922-8",
State = BookState.Normal,
FetchBookNumber = "A41 1:1",
Location = "主校区",
Sort = "文学"
},
new Book()
{
Name = "毛选. 第一卷",
BarCode = "00929266",
ISBN = "7-01-000922-8",
State = BookState.Readonly,
FetchBookNumber = "A41 1:1",
Location = "阅览室",
Sort = "文学"
},
new Book()
{
Name = "毛选. 第二卷",
BarCode = "00929279",
ISBN = "7-01-000915-5",
State = BookState.Normal,
FetchBookNumber = "A41 1:2",
Location = "主校区",
Sort = "文学"
},
new Book()
{
Name = "毛选. 第二卷",
BarCode = "00929280",
ISBN = "7-01-000915-5",
State = BookState.Readonly,
FetchBookNumber = "A41 1:2",
Location = "阅览室",
Sort = "文学"
},
new Book()
{
Name = "毛选. 第三卷",
BarCode = "00930420",
ISBN = "7-01-000916-3",
State = BookState.Normal,
FetchBookNumber = "A41 1:3",
Location = "主校区",
Sort = "文学"
},
new Book()
{
Name = "毛选. 第三卷",
BarCode = "00930421",
ISBN = "7-01-000916-3",
State = BookState.Readonly,
FetchBookNumber = "A41 1:3",
Location = "阅览室",
Sort = "文学"
},
new Book()
{
Name = "毛选. 第四卷",
BarCode = "00930465",
ISBN = "7-01-000925-2",
State = BookState.Normal,
FetchBookNumber = "A41 1:4",
Location = "主校区",
Sort = "文学"
},
new Book()
{
Name = "毛选. 第四卷",
BarCode = "00930466",
ISBN = "7-01-000925-2",
State = BookState.Readonly,
FetchBookNumber = "A41 1:4",
Location = "阅览室",
Sort = "文学"
}
};

BookDetails[] bookDetails = new[]
{
new BookDetails()
{
Author = "Admam Freeman",
Name = "精通ASP.NET MVC 5",
ISBN = "978-7-115-41023-8",
Press = "人民邮电出版社",
PublishDateTime = new DateTime(2016,1,1),
SoundCassettes = "13, 642页 : 图 ; 24cm",
Version = 1,
FetchBookNumber = "TP393.092 19",
Description = "ASP.NET MVC 5框架是微软ASP.NET Web平台的新进展。它提供了高生产率的编程模型,结合ASP.NET的全部优势,促成更整洁的代码架构、测试驱动开发和强大的可扩展性。本书涵盖ASP.NET MVC 5的所有开发优势技术,包括用C#属性定义路由技术及重写过滤器技术等。且构建MVC应用程序的用户体验也有本质上的改进。其中书里也专一讲解了用新Visual Studio 2013创建MVC应用程序时的技术和技巧。本书包括完整的开发工具介绍以及对代码进行辅助编译和调试的技术。本书还涉及流行的Bootstrap JavaScript库,该库现已被纳入到MVC 5之中,为开发人员提供更广泛的多平台CSS和HTML5选项,而不必像以前那样去加载大量的第三方库。"
},
new BookDetails()
{
Author = "吕高旭",
Name = "Entity Framework实用精要",
ISBN = "978-7-302-48593-3",
Press = "清华大学出版社",
PublishDateTime = new DateTime(2018,1,1),
SoundCassettes = "346页 ; 26cm",
Version = 1,
FetchBookNumber = "TP393.09 447",
Description = "本书通过介绍Entity Framework与 LINQ 开发实战的案例,以 Entity Framework 技术内容的讨论为主线,结合关键的 LINQ技巧说明,提供读者系统性学习 Entity Framework 所需的内容。本书旨在帮助读者进入 Entity Framework的世界,建立必要的技术能力,同时希望读者在完成本书的教学课程之后,能够更进一步地将其运用在实际的项目开发中。"
},
new BookDetails()
{
Author = "鲁比",
Name = "Rails 5敏捷开发",
ISBN = "978-7-5680-3659-7",
Press = "华中科技大学出版社",
PublishDateTime = new DateTime(2018,1,1),
SoundCassettes = "xxi, 451页 : 图 ; 23cm",
Version = 1,
FetchBookNumber = "TP393.09 448",
Description = "本书以讲解“购书网站”案例为主线, 逐步介绍Rails的内置功能。全书分为3部分, 第一部分介绍Rails的安装、应用程序验证、Rails框架的体系结构, 以及Ruby语言知识; 第二部分用迭代方式构建应用程序, 然后依据敏捷开发模式开展测试, 最后用Capistrano完成部署; 第三部分补充日常实用的开发知识。本书既有直观的示例, 又有深入的分析, 同时涵盖了Web开发各方面的知识, 堪称一部内容全面而又深入浅出的佳作。第5版增加了关于Rails 5和Ruby 2.2新特性和最佳实践的内容。"
},
new BookDetails()
{
Author = "汪鹏",
Name = "你必须掌握的Entity Framework 6.x与Core 2.0",
ISBN = "978-7-302-50017-9",
Press = "清华大学出版社",
PublishDateTime = new DateTime(2018,1,1),
SoundCassettes = "X, 487页 : 图 ; 26cm",
Version = 1,
FetchBookNumber = "TP393.09 452",
Description = "本书分为四篇,第一篇讲解Entity Framework 6.x的基础,包括数据库表的创建,数据的操作和数据加载方式。第二篇讲解Entity Framework 6.x进阶,包括基本原理和性能优化。第三篇讲解跨平台Entity Framework Core 2.x的基础知识和开发技巧。第四篇讲解在Entity Framework Core 2.x中解决并发问题,并给出实战开发案例。"
},
new BookDetails()
{
Author = "毛",
Name = "毛选. 第一卷",
ISBN = "7-01-000914-7",
Press = "人民出版社",
PublishDateTime = new DateTime(1991,1,1),
SoundCassettes = "340页 : 肖像 ; 19厘米",
FetchBookNumber = "A41 1:1",
Version = 2,
Description = "《毛选》是对20世纪中国影响最大的书籍之一。"
},
new BookDetails()
{
Author = "毛",
Name = "毛选. 第二卷",
ISBN = "7-01-000915-5",
Press = "人民出版社",
PublishDateTime = new DateTime(1991,1,1),
SoundCassettes = "343-786页 : 肖像 ; 19厘米",
Version = 2,
FetchBookNumber = "A41 1:2",
Description = "《毛选》是对20世纪中国影响最大的书籍之一。"
},
new BookDetails()
{
Author = "毛",
Name = "毛选. 第三卷",
ISBN = "7-01-000916-3",
Press = "人民出版社",
PublishDateTime = new DateTime(1991,1,1),
SoundCassettes = "789Ł±1120页 ; 20厘米",
FetchBookNumber = "A41 1:3",
Version = 2,
Description = "《毛选》是对20世纪中国影响最大的书籍之一。"
},
new BookDetails()
{
Author = "毛",
Name = "毛选. 第四卷",
ISBN = "7-01-000925-2",
Press = "人民出版社",
PublishDateTime = new DateTime(1991,1,1),
SoundCassettes = "1123Ł±1517页 ; 20厘米",
FetchBookNumber = "A41 1:4",
Version = 2,
Description = "《毛选》是对20世纪中国影响最大的书籍之一。"
},
};

var temp = from book in books
from bookshelf in bookshelfs
where book.Location == bookshelf.Location && book.Sort == bookshelf.Sort
select new { BarCode = book.BarCode, BookshelfId = bookshelf.BookshelfId };

foreach (var bookshelf in bookshelfs)
{
bookshelf.Books=new List<Book>();
}

foreach (var tem in temp)
{
Bookshelf targetShelf = bookshelfs.Single(bookshelf => bookshelf.BookshelfId == tem.BookshelfId);
Book targetBook = books.Single(book => book.BarCode == tem.BarCode);
targetShelf.Books.Add(targetBook);
}

foreach (var bookshelf in bookshelfs)
{
bookshelf.MaxFetchNumber=bookshelf.Books.Max(b => b.FetchBookNumber);
bookshelf.MinFetchNumber=bookshelf.Books.Min(b => b.FetchBookNumber);
}

foreach (var bookshelf in bookshelfs)
{
await context.Bookshelves.AddAsync(bookshelf);
await context.SaveChangesAsync();
}

foreach (var bookDetail in bookDetails)
{
await context.BooksDetail.AddAsync(bookDetail);
await context.SaveChangesAsync();
}

foreach (var book in books)
{
await context.Books.AddAsync(book);
await context.SaveChangesAsync();
}
}
}
}
View Code

 

在 Configure 方法中调用该数据库初始化方法:

BookInitiator.BookInitial(app.ApplicationServices).Wait();

 

运行后查看数据库:

 

 

 

 

二、图书借阅主页面

首先创建一个视图模型用于确定分页信息:

public class PagingInfo
{
public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }

public int TotalPages
{
get => (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage);
}
}

 

然后创建另一个视图模型用于确定该分页信息以及各分页的书籍:

public class BookListViewModel
{
public IEnumerable<BookDetails> BookDetails { get; set; }
public PagingInfo PagingInfo { get; set; }
}

 

创建一个自定义 HtmlHelper 用于在视图中使用 Razor 语法获取分页,在 ASP.NET Core 中 TagBuilder 继承自 IHtmlContent,不能直接对 TagBuilder 进行赋值,需调用 MergeAttribute 方法和 InnerHtml 属性的 AppendHtml 方法编写标签;并且 TagBuilder 没有实现 ToString 方法,只能通过其 WriteTo 方法将内容写到一个 TextWriter 对象中以取出其值:

public static class PagingHelper
{
public static HtmlString PageLinks(this IHtmlHelper html, PagingInfo pagingInfo, Func<int, string> pageUrl)
{
StringWriter writer=new StringWriter();
for (int i = 1; i <= pagingInfo.TotalPages; i++)
{
TagBuilder tag=new TagBuilder("a");
tag.MergeAttribute("href",pageUrl(i));
tag.InnerHtml.AppendHtml(i.ToString());
if (i==pagingInfo.CurrentPage)
{
tag.AddCssClass("selected");
tag.AddCssClass("btn-primary");
}
tag.AddCssClass("btn btn-default");
tag.WriteTo(writer,HtmlEncoder.Default);
}
return new HtmlString(writer.ToString());
}
}

 

创建 BookInfo 控制器并确定其中每个分页的书籍数,使用 Session 获取更快地获取书籍信息:

public class BookInfoController : Controller
{
private LendingInfoDbContext _context;
private static int amout = 4;

public BookInfoController(LendingInfoDbContext context)
{
_context = context;
}

public IActionResult Index(string category, int page = 1)
{
IEnumerable<BookDetails> books = null;
if (HttpContext.Session != null)
{
books = HttpContext.Session.Get<IEnumerable<BookDetails>>("bookDetails");
}
if (books == null)
{
books = _context.BooksDetail;
HttpContext.Session?.Set<IEnumerable<BookDetails>>("books", books);
}
BookListViewModel model = new BookListViewModel()
{
PagingInfo = new PagingInfo()
{
ItemsPerPage = amout,
TotalItems = books.Count(),
CurrentPage = page,
},
BookDetails = books.OrderBy(b => b.FetchBookNumber).Skip((page - 1) * amout).Take(amout)
};
return View(model);
}

public FileContentResult GetImage(string isbn)
{
BookDetails target = _context.BooksDetail.FirstOrDefault(b => b.ISBN == isbn);
if (target != null)
{
return File(target.ImageData, target.ImageMimeType);
}
return null;
}
}

 

视图页面:

24 行利用 BookListViewModel 中 PagingInfo 的 CurrentPage 获取各序号,32 行中使 img 元素的 src 指向 BookInfoController 的 GetImage 方法以获取图片:

@using LibraryDemo.TagHelpers
@model BookListViewModel
@{
ViewData["Title"] = "Index";
int i = 1;
}
<style type="text/css">
tr > td {
padding: 5px;
padding-left: 20px;
}
tr+tr {
border-top: thin solid black;
padding-top: 10px;
}
</style>

<hr />
<table>
<tbody>
@foreach (var book in Model.BookDetails)
{
<tr>
<td style="width: 3%">@((@Model.PagingInfo.CurrentPage-1)*4+i++)</td>
<td style="text-align: center; width: 150px; height: 200px;">
@if (book.ImageData == null)
{
<label>No Image</label>
}
else
{
<img class="img-thumbnail pull-left" src="@Url.Action("GetImage", "BookInfo", new {book.ISBN})" />
}
</td>
<td style="text-align: left;">
<a style="margin-left: 1em;">@book.Name</a>
<div style="margin-left: 2em;margin-top: 5px">
<span>@book.Author</span>
<br />
<span>@book.Press</span>
<p>@book.FetchBookNumber</p>
</div>
<div style="text-indent: 2em">
<p>@book.Description</p>
</div>
</td>
</tr>
}
</tbody>
</table>
<div class="btn-group pull-right">
@Html.PageLinks(Model.PagingInfo, x => Url.Action("Index", new { page = x}))
</div>

 

最终结果:

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐