kbnet.xaas.dapi

一、准备

今天是2024年1月24日,虽然天气比较寒冷,但是我的精神状态感觉越来越好,于是就把kbnet整体架构中的dapi程序架构弄出来了,至此,kbnet程序架构整理完毕。

因为前些时日微软已经正式发布.net8,所以dapi程序架构也升级了上来,从.net6升级到.net8对于asp.net core webapi项目几乎属于无感式升级,所以不用有过多担心。

废话不多说,如下继续......

 

二、程序架构

 

三、程序架构信息

一、解决方案
    1.方案名称:kbnet代指产品或项目名称,xaas表示解决方案为服务程序(如saas\paas),dapi表示为服务程序中的数据服务,d即data首字母缩写。
    2.隶属关系:kbnet.xaas.dapi位于view->papi->dapi末尾位置,是数据存储的核心节点,专职负责与关系型数据库的交互。
    3.介绍信息:
        a.本程序主要负责业务数据的持久化存储、以及增删改查操作,不涉及业务层面的逻辑处理。
        b.本程序基于kbgress进行设计研发,且结构清晰,因此仅需按照程序架构、项目信息和编码规范,便可以实现高效能的程序开发。
        c.本程序架构以提供webapi服务为基准,软件即服务。
        d.本程序可以访问不同的数据库实例,但仅限于对单个数据库实例进行交互作业,不允许跨多个数据库实例操作。
        e....


二、项目信息
    1.基础项目
        1.1.kbnet.xaas.dapi
            a.项目简介:该项目是启动项目,主要负责程序的初始化工作,以及API的定义、使用、权鉴、转发和管理等。
            b.项目结构:无
            c.规则规范:
                1)在项目中不允许编写业务相关代码。
                2)程序初始化相关功能应在kbnet.xaas.services项目中实现,确保本项目作为启动项目的纯粹性。
                3)程序初始化不仅包含自身的初始化,也包含kbnet.xaas.services项目对接kbgress功能的初始化。
                4)Controllers文件夹中所有的类,必须以xxxxController进行命名。
                5)xxxxController类中的方法仅允许对参数进行验证,不允许处理业务逻辑运算。
                7)权鉴和安全功能,首先由中间件拦截请求,通过kbnet.xaas.services项目转发kbgress.client,最终由kbgress.client实现,并返回结果。
            d.引用关系:
                1.类库引用:
                    1)Newtonsoft.Json
                2.项目引用:
                    1)kbnet.xaas.models
                    2)kbnet.xaas.common
                    3)kbnet.xaas.services
                    4)kbnet.xaas.xxxx.core.repositories
                    5)kbnet.xaas.xxxx.models.inner
                    6)kbnet.xaas.xxxx.models.data
            e.其他信息:无
        1.2.kbnet.xaas.common
            a.项目简介:该项目是通用项目,主要定义一些功能性的类,比如工具类、网络类、本地类、验证类等通用型的类。
            b.项目结构:无
            c.规则规范:
                1)不允许在此类中定义业务相关的功能类。
            d.引用关系:
                1.类库引用:
                    1)Newtonsoft.Json
                2.项目引用:
                    1)kbnet.xaas.models
            e.其他信息:无
        1.3.kbnet.xaas.services
            a.项目简介:该项目是基础服务项目,一方面负责为xxxx项目提供基础服务,另一方面负责与kbgress对接,即通过与kbgress对接实现xxxx项目所需的基础服务。
            b.项目结构:
                1)项目引用的SDK由“Microsoft.NET.Sdk”改为“Microsoft.NET.Sdk.Web”。注意,此次项目输出类型会变成“控制台应用程序”,应该回“类库”。
            c.规则规范:
                1)此项目负责中间件的定义与实现。
                2)此项目负责令牌、权限、安全的验证。
                3)此项目管理请求流量的控制与统计。
                4)此项目管理配置文件的解读与更新。
                5)此项目管理请求的定义和维护、以及分发和转发。
                6)此项目管理分布式计算与存储的接口定义和调用。
                7)此项目负责日志和消息记录与发送。
                8)此项目负责运维状态的记录和通信。
                9)此项目负责服务治理相关的事项。
            d.引用关系:
                1.类库引用:
                    1)Newtonsoft.Json
                2.项目引用:
                    1)kbnet.xaas.models
                    2)kbnet.xaas.common
            e.其他信息:无
        1.4.kbnet.xaas.models
            a.项目简介:该项目是基础模型项目,主要定义通用的基础模型类,因此它不引用任何项目和第三方类库,仅提供基础模型类给其他项目引用和使用。
            b.项目结构:无
            c.规则规范:
                1)不允许引用其他项目和第三方类库。
            d.引用关系:
                1.类库引用:无
                2.项目引用:无
            e.其他信息:无
        1.5.kbnet.xaas.tests
            a.项目简介:该项目是测试项目,主要用于编写业务功能的测试类和执行顺序。测试至关重要,既是对自己负责,也是对协同者负责。
            b.项目结构:
                1)本项目是一个控制台程序,所有测试代码均自己动手编码,不依赖任何第三方测试类库和工具。
            c.规则规范:
                1)测试以目录划分顺序、层级和范围,以目录名称创建并命名xxxxTest类,且继承抽象类BaseTest,实现抽象方法ExecTest。
                2)Program启动类,调用以项目命名的顶级xxxxTest类的ExecTest方法,通过层级关系实现所有xxxxTest类的测试。
                3)已经测试过,不需要再测试的xxxxTest类,只需要在上级的ExecTest方法中注释掉即可,或者把自身的ExecTest方法中的内容注释掉。
                4)多个xxxxTest类之间的执行顺序,由程序员依据业务实际需求自行控制。
                5)为避免给数据库留下无用的测试数据,应本着测试结束后,数据库中无测试数据为原则。
            d.引用关系:
                1.类库引用:
                    1)Newtonsoft.Json
                2.项目引用:
                    1)kbnet.xaas.models
                    2)kbnet.xaas.common
                    3)kbnet.xaas.services
                    4)kbnet.xaas.xxxx.core.repositories
                    5)kbnet.xaas.xxxx.core.services
                    6)kbnet.xaas.xxxx.models.data
                    7)kbnet.xaas.xxxx.models.inner
                    8)kbnet.xaas.xxxx.api.cache
                    9)kbnet.xaas.xxxx.api.local
            e.其他信息:无

    2.模块项目
        2.1.Core项目
            2.1.1.kbnet.xaas.xxxx.core.repositories
                a.项目简介:数据仓库项目,主要以面向对象的思维负责数据的读写与运算,它位于业务层(papi)与数据层中间,起着连接与隔离的双层含义。
                b.项目结构:无
                c.规则规范:
                    1)所有类均以xxxxRepository形式命名,即以Repository结尾。
                    2)以xxxx命名的类文件是业务对象,该文件中编写的方法均与业务对象xxxx直接相关,与数据库无关。实际文件中的方法会包含多个辅助对象的方法,此时方法命名应注意区分,通常采用“辅助对象名_方法名”的形式。
                    3)倘若对数据库涉及大于一次的增加、删除和修改操作的方法,需要把业务逻辑控制留在本项目中,不允许推到services项目中实现。并且需要开启TrasactionScope事务控制,确保ACID原则。
                    4)利用partial关键字,在xxxxRepository类文件中,定义相同名称和继承关系的两个xxxxRepository类代码块,使用partial修饰,实现在一个块中写公有方法,另一个块写私有方法。
                    5)原则上不允许通过拼接字符串的方式编写SQL脚本,如果必须尽心拼接SQL的场景,一定一定一定要严格控制SQL注入攻击,严格进行关键字验证。
                    6)一定要写明业务逻辑执行逻辑说明注释,写不明白说明注释需进行严肃考核。
                    7)业务功能绝对会改变,因此一定不要过度抽象,并且要尽可能的留出扩展接口。
                    8)所有类均继承BaseRepository基础类。
                d.引用关系:
                    1.类库引用:
                        1)Newtonsoft.Json
                    2.项目引用:
                        1)kbnet.xaas.models
                        2)kbnet.xaas.common
                        3)kbnet.xaas.services
                        4)kbnet.xaas.xxxx.core.services
                        5)kbnet.xaas.xxxx.core.entities
                        6)kbnet.xaas.xxxx.models.data
                        7)kbnet.xaas.xxxx.models.inner
                        8)kbnet.xaas.xxxx.models.converter
                        9)kbnet.xaas.xxxx.api.cache
                        10)kbnet.xaas.xxxx.api.local
                e.其他信息:无
            2.1.2.kbnet.xaas.xxxx.core.services
                a.项目简介:数据服务项目,主要面向数据库结构进行数据的读写操作,即一个xxxxService类文件对应一张数据表。
                b.项目结构:无
                c.规则规范:
                    1)所有类均以xxxxService形式命名,即以Service结尾。
                    2)原则上禁止一切以拼接字符串的方式编写SQL,如果必须尽心拼接SQL的场景,一定一定一定要严格控制SQL注入攻击,严格进行关键字验证。
                    3)尽量使用linq和lambda表达式方式编写数据库查询。
                    4)原则上方法内只允许实现一条增加或删除或更改的SQL脚本,如果执行多条请在repositories项目中使用TrasactionScope做事务控制。批量操作除外。
                    5)对于编写SQL脚本的需求场景,除SQL关键字以外,不允许直接写数据表字段,应该使用nameof方法间接实现,主要规避数据表字段名称改变后,程序中遗漏修改。
                    6)对象类的默认值一定要严格管控,尤其是在程序升级更新时,避免弄脏数据。
                    7)对于编写的SQL脚本一定要做严格的性能测试,找出并优化掉低效耗时的脚本。
                    8)作为数据服务项目,数据安全是基础,尽可能不要因为技术能力不足给数据库服务器制造性能压力,确保其平稳运行。
                    9)所有类均继承BaseService基础类。
                d.引用关系:
                    1.类库引用:
                        1)Newtonsoft.Json
                    2.项目引用:
                        1)kbnet.xaas.models
                        2)kbnet.xaas.common
                        3)kbnet.xaas.services
                        4)kbnet.xaas.xxxx.core.entities
                        5)kbnet.xaas.xxxx.models.data
                        6)kbnet.xaas.xxxx.models.inner
                e.其他信息:无
            2.1.3.kbnet.xaas.xxxx.core.entities
                a.项目简介:数据实体项目,此项目中的类和代码,主要使用ORM工具依据数据库结构自动生成,切勿随意改动。
                b.项目结构:无
                c.规则规范:无
                d.引用关系:
                    1.类库引用:
                        1)Microsoft.EntityFrameworkCore
                        2)Microsoft.EntityFrameworkCore.Design
                        3)Microsoft.EntityFrameworkCore.SqlServer
                        4)Microsoft.EntityFrameworkCore.Tools
                    2.项目引用:无
                e.其他信息:
                    1)类库引用中仅引用SqlServer数据库的类库,倘若使用其他厂商的数据库,通过nuget搜索EntityFrameworkCore,便可在查询结果中找到其他厂商的类库文件,如Oracle\MySql\Postgresql等。
                    2)如何使用Scaffold-DbContext工具
                        .NET Core CLI使用方法:dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
                        Visual Studio使用方法:Scaffold-DbContext 'Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook' Microsoft.EntityFrameworkCore.SqlServer
        2.2.API项目
            2.2.1.kbnet.xaas.xxxx.api.cache
                a.项目简介:缓存服务项目,此项目主要负责与文件服务器进行交互,实现数据的高速读写。
                b.项目结构:
                    1)Memcached文件夹表示使用memcached服务器进行数据的缓存。
                    2)Redis文件夹表示使用Redis服务器进行数据的缓存。
                    3)MyMemory文件夹表示使用框架自带的集合把数据缓存到服务器内存中。
                c.规则规范:
                    1)本项目仅限于为dapi提供数据的缓存服务,如果对缓存数据设计的等级较高,不建议直接连接Redis服务器,而是在中间建立专门与Redis服务器通讯的服务程序。
                    2)使用MyMemory缓存数据时,数据会随着dapi服务的启停而销毁。
                    3)设计编码时,一定要将业务类和功能类做区分与管理,提升和改进代码,确保高可读性与质量。
                d.引用关系:
                    1.类库引用:
                        1)Newtonsoft.Json
                    2.项目引用:
                        1)kbnet.xaas.common
                        2)kbnet.xaas.models
                        3)kbnet.xaas.services
                        4)kbnet.xaas.xxxx.models.data
                e.其他信息:无
            2.2.2.kbnet.xaas.xxxx.api.local
                a.项目简介:本地服务项目,此项目主要负责计算机硬盘里文件的读写与维护,比如Sqlite数据库文件、LiteDB、数据文件、文本文件等。
                b.项目结构:
                    1)DataJson文件夹表示负责读写与维护数据为Json格式的文件。
                    2)DataSqlite文件夹表示负责读写与维护Sqlite数据库文件。
                    3)DataText文件夹表示负责读写与维护数据为纯文本的文件。
                    4)...
                c.规则规范:
                    1)本项目仅限于为dapi提供本地化文件存储服务,因此,每一种类型文件均需在配置文件中,设置读写与维护规则。
                    2)本地文件的存储管理,如文件大小、保留时长、删除条件、更新步骤、是否会被误删或覆盖掉、文件存储位置等。
                    3)设计编码时,一定要将业务类和功能类做区分与管理,提升和改进代码,确保高可读性与质量。
                d.引用关系:
                    1.类库引用:
                        1)Newtonsoft.Json
                        2)Microsoft.Data.Sqlite
                    2.项目引用:
                        1)kbnet.xaas.common
                        2)kbnet.xaas.models
                        3)kbnet.xaas.services
                        4)kbnet.xaas.xxxx.models.data
                e.其他信息:
                    1)在.netcore中,使用Microsoft.Data.Sqlite不包含SqliteDataAdapter类,意味着DataSet不可用。倘若想使用可以继续使用旧的System.Data.Sqlite,但是不建议使用。
        2.3.Models项目
            2.3.1.kbnet.xaas.xxxx.models.data
                a.项目简介:数据模型项目,此项目定义的模型类,专门用于与papi传输结构化数据。
                b.项目结构:
                    1)Cache文件夹定义缓存服务数据模型类。
                    2)Local文件夹定义本地服务数据模型类。
                    3)Dms文件夹定义数据服务的模型类。
                    4)...
                c.规则规范:
                    1)所有模型类均以xxxxDModel形式命名,即DModel结尾。
                    2)模型类和属性均需注释说明其用意。
                    3)属性默认值必须注释解释清楚。
                    4)项目以文件夹方式将数据模型类分割管理,目的是为了清晰化目录结构,因此,切勿在项目内部跨文件夹相互引用。
                    5)Dms文件夹中的数据模型类需继承自kbnet.xaas.models项目的DataBaseModel类。
                d.引用关系:
                    1.类库引用:无
                    2.项目引用:
                        1)kbnet.xaas.models
                e.其他信息:无
            2.3.2.kbnet.xaas.xxxx.models.inner
                a.项目简介:自用模型项目,此项目定义的模型类,仅作用于dapi项目内部使用,不允许对外输出。
                b.项目结构:
                    1)Cache文件夹定义缓存服务内部模型类。
                    2)Common文件夹定义通用内部模型类。
                    3)Data文件夹定义数据服务内部模型类。
                    4)Local文件夹定义本地服务内部模型类。
                    5)...
                c.规则规范:
                    1)所有模型类均以xxxxIModel形式命名,即IModel结尾。
                    2)不同项目间的模型类存放在自己的文件夹中,原则上不允许在项目A调用项目B的模型类,实际操作中按照项目的规模、复杂度和难度自行掌控。
                    3)模型类和属性均需注释说明其用意。
                    4)属性默认值必须注释解释清楚。
                    5)所有模型类需继承自kbnet.xaas.models项目的BaseModel类。
                d.引用关系:
                    1.类库引用:无
                    2.项目引用:
                        1)kbnet.xaas.models
                e.其他信息:无
            2.3.3.kbnet.xaas.xxxx.models.converter
                a.项目简介:模型转换器项目,负责模型类对象转换,为了各项目保持代码的纯粹性和可读性、可复用性、可维护性,不允许在core和api项目中编写模型对象转换代码,尤其在项目repostories中。
                b.项目结构:无
                c.规则规范:
                    1)类命名:xxxxConverter。
                    2)方法命名:inner转换为data,xxxx_IM2DM()。data转换为inner,xxxx_DM2IM()。
                    3)方法命名:data转换为orm,xxxx_DM2OM()。orm转换为data,xxxx_OM2DM()。
                    4)方法命名:倘若模型转换的是集合(如List,Dictionary等),方法名表示为xxxx_XX2XX_List()。
                    5)转换操作:在转换前,必须做非空判断,避免出现空指针异常。
                    6)...
                d.引用关系:
                    1.类库引用:无
                    2.项目引用:
                        1)kbnet.xaas.models
                        2)kbnet.xaas.common
                        3)kbnet.xaas.xxxx.models.data
                        4)kbnet.xaas.xxxx.models.inner
                e.其他信息:无


三、配置规则
    1.说明信息:
        配置规则需要通过kbgress.web设置和定义好,然后下载到本地,或配置好绑定关系,程序在启动时从kbgress直接读取。
    2.规则类别:
        2.1.系统信息
            a.系统所属产品ID:xxxxxxxxxxxxxxxx。
            b.系统所属程序ID:xxxxxxxxxxxxxxxx。
            c.系统所属线路ID:xxxxxxxxxxxxxxxx。
            c.系统ID:xxxxxxxxxxxxxxxx。
            d.系统版本:v1.0.0。
            e.系统状态:可用、不可用/升级中、等待升级。
        2.2.安全权限
            a.令牌信息
            b.权限信息
            c.IP信息
            d.脚本攻击:xss或csrf等。
        2.3.日志信息
            a.是否上传云端:0或1。
        2.4.消息信息
        2.5.缓存信息
        2.6.本地信息
        2.7.数据库信息
        2.8.服务信息
            a.状态:
                1.心跳:频率(5s)。
            b.流量:
                1.app信息:
                    a)最大允许连接数:0。
                    b)最大连接数:0。
                    c)拒绝连接数:0。
                    d)当前连接数:02.api信息:
                    a)最大时耗(毫秒):以天为单位。
                    b)最小时耗(毫秒):以天为单位。
                    c)请求数量(10分钟):以10分钟为单位。
                    d)流量上限(0表示不限流量,次数/每秒):0。
                    e)最大流量(分钟):0。
                    f)拒绝次数(分钟):0。
                    g)重试次数(0-3):调用链路中api时。
                    h)是否熔断(0或1):被调用服务或api故障时。
        2.9.DCS信息
            a.主要依据业务对象的粒度和时间,定位目标数据库和数据表。
        2.10.硬件信息
            a.操作系统:xxxxxxxxxxxxxxx。
            b.内存容量:xxxxxxxxxxxxxxx。
            c.硬盘信息:xxxxxxxxxxxxxxx。
            d.CPU信息:xxxxxxxxxxxxxxx。
            e.网络信息:xxxxxxxxxxxxxxx。


四、编码规范
    1.编码流程:
        a.主流程:是指程序对关系型数据库(Oracle\MSSQL\MySql\Postgresql等)的操作。
            1.kbnet.xaas.dapi:此项目是程序的启动项目,定义了外界可以调用访问的WebApi方法。
            2.kbnet.xaas.xxxx.core.repositories:业务对象仓库项目,以业务对象形式定义和提供数据操作方法,项目中类不与数据表一一对应。
            3.kbnet.xaas.xxxx.core.services:数据对象服务项目,以数据对象形式定义和提供数据操作方法,项目中类与数据表一一对应。
            4.kbnet.xaas.xxxx.core.entities:EntityFrameworkCore的ORM项目,负责ORM映射转换。
        b.辅流程:
            1.缓存流程:是指程序从缓存数据库(Memcached\Redis等)中读写数据。
                a)缓存主指kbnet.xaas.xxxx.api.cache项目。
                b)repostories\services项目都可以调用本项目公开的方法。
            2.本地流程:是指程序从本地数据库(Sqlite)或文件中读写数据。
                a)本地主指kbnet.xaas.xxxx.api.local项目。
                b)repostories\services项目都可以调用本项目公开的方法。
    2.编码规范:
        由于各团队有自己的编码规范,所以此处便不公开了。
    3.测试流程:
        a.测试分为单元测试和案例测试,前者主要对单个功能进行测试,后者模拟现实业务操作进行测试。
        b.按照项目类别将测试对象分为二类,第一类api\repositories\services项目方法的测试,第三类cache\local项目方法的测试。
        c.在所有测试项目中,api和repositories项目测试是必须执行的,其他项目按实际需要自行决定。
        d....
    4.测试规范:
        a.所有成员都必须按照规定对自己编写的代码测试,经验证明几乎没有程序员可以编写出零BUG的方法代码。
        b.测试不仅是对自己编写的代码负责,也是对协同开发的成员负责。
        c.凡是因未测试引发的问题,皆由自己负责。
        d....
    5.发布部署:
        a.部署方式:依据实际情况,程序分为两种部署方式。
                    第一种方式,单线路环境,暂停对外提供服务,更新部署程序,影响使用。
                    第二种方式,多线路环境,对半暂停线路,一半一半的更新程序,不影响使用(无感更新)。
    6.常用单词:
        1)查询:select\get\find\query
        2)增加:insert\add\new\create
        3)更新:update\modify\edit
        4)删除:delete\remove
        5)对象:info,item\items,list,all
        6)条件:id,code,name,isDelete,isActive,isVisible,typeId,parentId,ownerId,userId\memberId,companyId
        7)时间:date,time,year\month\day,hour\minute\seconds,milliseconds
        8)分页:page\pages,count,index,num\no,size,total
        9)表格:table\row\column\cell
        10)集合:first\last,previous\current\next,above\below,before\after,exist,contains,max\min,sum,average,top,sort,asc\desc
        11)库表:master\slave,sync\async,lock\unlock,isInit,isMain,versionNo
        12)仓库:level层级,dimension维度,granularity粒度,measure度量\metric指标(特殊的度量),scale尺度(度量单位),field字段,caliber口径
        13)词对:start\stop,key\value,upload\download,import\export,encode\decode,encrypt/decrypt,split\join,go\back
        14)比较:+Plus,-Minus,*Multiply,/Divide,%Mod,=Equal,!=NotEqual,>GreatThan,>=GreatOrEqual,<LessThan,<=LessOrEqual,&BinAnd,|BinOr,^BinXor,<<BinShiftLeft,>>BinShiftRight
        15)其他:save,build,batch,copy,set,valid,gen(erate),parse,by,or,in,cut,result
        

五、注意事项
    1.三方服务:严禁擅自引用第三方服务的行为,尤其是在线服务,不论是商业、开源、或其他形式的服务,避免造成程序和产品的依赖性、稳定性、不可控性等风险和问题。
    2.自律习惯:软件产品或项目以更好的服务用户和客户为宗旨,因此,严禁在开发中以锻炼自己技术的思想和行为编写不可控或效率低劣的代码。
    3.自律习惯:团队成员需按照解决方案类型的程序架构进行设计与编码,不可以自我主观意念破坏编码流程与规范。
    4.成本警钟:由于软件设计研发是一门纯脑力劳动,所以一定要时不时的给自己敲一下警钟,测算一下时间周期、团队规模、成员能力等各方面的成本,坚决要避免一根筋的苦思冥想钻牛角尖。
    5....


六、总结
    本文既是对解决方案的介绍,也是编程规范的定义。然而上述内容并不是完全不变的,在实战过程中,如遇到特殊场景,需要与规则制定者或架构师商议,更改或制定合适的规则规范。
    依照上述内容便可完成DAPI项目中功能的设计、开发、测试、发布等工作。

 

四、总结

终于在春节前把kbnet架构搞完,算是了结了我一桩大心事。软件编程技术变化之快可谓日新月异,最让程序员提心吊胆和惊魂的就是学不完、跟不上,因为那样意味着淘汰出局。在技术积累与探索发现中领悟到,唯有以不变应万变之法才可以确保自己不会被淘汰出局,即构建自己的架构体系,技术、知识、能力等,形成架构体系后,便可以把凌乱的东西分门别类的归纳起来,过时落后的清理出去。

有了这一整套程序架构,便可以基于此,进行各种软件的设计与研发,十几年的技术钻研,算是有个交代了。

 

接下来,就会基于这套框架进行kbgress的设计与研发,想知道kbgress是什么软件?可以理解为它是这套程序框架的基础设施服务软件,提供网关服务、安全服务、负载均衡服务、分布式计算与存储服务等功能,一句话使开发人员能够使用kbnet专注软件产品功能开发,其他保障性需求它来提供服务。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章