当前位置:   article > 正文

软件需求最佳实践笔记(二)_“记录预约”用例的事件流,完成记录预约的活动图 1_用例事件流如何转换活动图

用例事件流如何转换活动图

软件开发过程中,最常使用的模型有三种:跨职责流程图、活动图和数据流图。

①跨职责流程图

跨职责流程图主要由流程名称、职责带区、流程阶段、业务活动、判断/审批等组成。

img

下面是一个示例:

img

img

跨职责流程图完成的标准:一是所有的职责带区上的命名都将细化到具体的岗位;二是每类活动之间的关系已经没有疑问。还有一点是需要特别注意的是不应该对业务活动进行细化。

②活动图

下图是一个常见的活动图:

img

常规活动图

带泳道的活动图:

每个活动节点、分支是必须只属于一个泳道,而转换、分岔与汇合是可以跨泳道的。通过泳道,我们不仅体现了整个活动控制流,还体现出了每个活动的实施者。

img

带对象流的活动图:

通过带对象流的活动图表示数据、文档的流转,可以修改对象的状态,还可以显示对象的角色、状态和属性值的变化。

img

复杂的活动图—辅助活动图:

如果一个活动图过于复杂,或者活动图中某一组活动与主控制流关联不大,那么就可以借助辅助活动图来描述它。

img

活动节点“收款”,包括了多种不同处理:网上银行、点卡、汇款等。

复杂的活动图—汇合描述:

在默认情况下,当汇合的所有入流均到汇合点时,就将执行汇合点指向的活动节点。但是有些时候,你希望对其做一些约束,这时就可以借助汇合描述来完成。

汇合描述实际上是一个约束,其格式就是“{约束条件}”。可以在汇合点加上“{Order.State=Payed}”来说明,只有当订单状态为已付款时才执行下一个活动节点“供应商送货”。这样就可以更加清晰地描述出,只有客户付了钱之后,才执行订单的送货操作。

img

复杂的活动图 —发送与接收信号:

如果需要表示出异步事件,这在活动中可以通过信号机制来表示。

img

小张去必胜客吃饭,发现要排队,他决定如果15分钟还轮不到,就到隔壁的肯德基吃饭,那么就可以通过上述的符号来表示。为了使例子保持简单,假设小张排在最前面。

复杂的活动图 —引脚(pin):

就像类中的方法可以包含不同的参数一样,一个活动节点也可能有相应的参数。如果打算标明每个活动节点所需的数据以及它将产生的数据时,引脚是一个很好的手段。

img

上图表示活动“计算利息”将接受三个参数:本金(principal)、利率 (rate)和年限(year);如果传入的参数合法,将输出利息值(accrual);如果传入的参数错误,则产生异常。该活动节点没有与其他节点相连,需在小矩形中添加箭头符号,以区别输入参数和输出参数。除用名字表示顺序之外,还可以在引脚边上标注表示顺序的数字。如果引脚产生异常对象,则可以在符号边上标注一个空心三角形。

就是当流程图绘制完之后,应该尽可能对其进行一些分析,包括瓶颈分析和利益分析,以此减少变更的影响,看下面的案例:

img

赊销活动图示例1

该流程是一种典型的赊销模式,收款和送货并行进行,必然会对财务部门的收款工作产生障碍。当然,这种模式对于销售部门而言是十分有利的,因此在公司初创时期,很多公司就会釆用这样的模式,毕竟这个时期总经理最关注的就是销售额。

而当销售量上升的同时,应收账款也不断上升时,这种问题就会暴露出来。特别是公司逐渐成长起来,销售额达到一定量的时候,总经理就会更关注资金安全问题。既然问题是收款和送货两个活动并行执行导致的,那么只要将这种并行模式转成串行模式就能解决问题了,也就是先收款后送货。

img

img

赊销活动图示例2

也许执行这种流程后,销售竞争力大幅下降,总经理又会认为主要矛盾在销售了,不就又有将流程改回去的冲动了吗?因此,可采用一种更合理的折中方案,引入“信用额度”控制,对于赊销顾客,根据历史交易情况设置一个信用额度,欠款在额度之内可继续赊销,否则先付款后发货。

img

赊销活动图示例3

③数据流图

数据流图的元素包括过程/加工、数据存储/文件、外部实体、数据流、实时连接等。

img

数据流图主要元素

数据流图一般是分层绘制的:

img

分层的数据流图示意图

第一步:构建顶层图

img

数据流图顶层图示例

这个课程注册系统的流程是:教务处提供有关课程信息,学生获得课程安排后,进行申请注册,教师在注册完成后得到班级列表。

第二步:根据业务事件绘制DFD片段

img

业务事件列表示例

img

DFD片段示例

第三步:将DFD片段合并成DFD

img

DFD 0层图示例

第四步:逐步细化,分解到底

img

DFD 1层图示例

2、业务实体分析

业务实体分析就是了解问题域中有哪些业务实体,它们之间存在什么样的逻辑关系、数量关系,以及有什么相应的结构规则。这样的工作就是“领域建模”、“概念建模”。领域建模采用“自底向上”方法,针对每一个业务事件、每一类报表创建局部的领域类图片段,完成这些建模后,再对其进行抽象、提炼,形成全局的领域模型。

主要步骤是识别出业务实体-> 确定实体之间的关系->定义实体的关键属性。类图 + E/R图(实体关系图)都可用于业务实体分析。

类图应用基础与要点:

要正确地理解类,就必须对面向对象的思想建立正确的认识:

img

住在厦门的张三,要给住在绍兴的李四送一个生日蛋糕。张三登录到一个电子商务网站购买一个蛋糕,并通过该网站将其送给李四。电子商务网站实际上是通过绍兴的蛋糕店来完成这个任务的。在整个传递过程中,各个实体之间的关联关系如上图。

实际情况要复杂得多,电子商务网站可以接受很多人的订单,也可以与不同地方的蛋糕店建立合作,以送给更多不同地方的人,因此可以对其进行抽象。

img

张三就是一个对象,它可以归到“订货人”这个类中;而绍兴蛋糕店也显然是一个对象,它可以归到“商户”这个类中。

从中得出以下定义:每个对象都扮演了一个角色,并为其他成员提供特定的服务或执行特定的行为。

①类的表示法

类是对一组具有相同属性、操作、关系和语义的对象的描述。关系是类之间的、语义是蕴藏的,对于一个类而言,其关键的特性是属性(成员变量)和操作(成员方法)。类用一个矩形表示的,包含三个分栏,每个分栏分别写入类的名称、类的属性和类的操作。

img

注:在需求建模时,应该用中文命名类和类的属性。

②类之间关系

类之间通常有关联关系、泛化关系、聚合与组合关系,见下图:

img

类图的主要元素

在阅读这种简单的类图时,重要在于把握三项内容:类、关系、多重性(这也就是类图中那个重要的20%)。其阅读的顺序应遵从以下原则:先看清有哪些类,然后看看类之间存在的关系,并结合多重性来理解类图的结构特点,以及各个属性和方法的含义。

img

第一步:找到类

上图中共有7个类:订单、订单项、客户、收货人、送货单、供应商、产品,并且在每一个类中都定义了若干属性。

第二步:找到类之间的关系

先从关系最复杂的类开始阅读。这个类是“订单”。然后,“订单项”和“订单”之间是组合关系,根据箭头方向可知“订单”是由“订单项”组成的;“订单”和“客户”、“收货人”、“送货单”之间是关联关系。也就是说,一个订单和客户、收货人、送货单是相关的;第二个类是“送货单”,和它相关的有4个类:“订单”、“订单项”、“收货人”、“供应商”。表示“送货单”是与“订单”相关的,同时还关联到“订单项” ;另外它和“供应商”、“收货人”之间的关联关系是很显然的。

第三步:理解数量关系

数量关系在类图中是以多重性(Multiplicity)的形式表示的,它又称重数。其表示格式为“n…m”其中整数n定义所连接的最少对象的数目,而m则为最多对象数(不知道确切的最大数时,最大数用*号表示,在Rose中则用n来表示)。

img

类图的辅助建模元素见下图:

img

⑤关联类

除了上述的建模元素之外,在类建模中还会用到一个很重要的概念,那就是关联类。具体来说,如果两个类之间具有多对多关系时,就会发现有些属性是不容易放到任何一个类中的。如果要记录每“人”在某个“协会”中所担任的职务,该存在哪呢?这种属性是属于“人”还是“协会”呢?显然都不合适。这个信息是属于关联本身的特性,因此可以通过关联类来建模。

img

⑥类模型的演化

对于类模型,许多需求分析人员会担心:非技术背景的用户真的能够理解“类”这种抽象的概念吗?它真的是应该在需求阶段来完成的吗?它毕竟看起来是一个源于软件开发领域的概念。

img

正如MVC架构模式对类的分类:除了源于问题域的模型(Model)类之外,还有许多是和设计、编码相关的计算机域的用户界面(View,视图)和控制逻辑(Control ,控制器)相关的类。因此类模型是从需求分析、设计阶段不断演化而成的。

注:其实“类”就是“类型”、“一类事物”的概念,例如一张桌子是一个对象,桌子就是一个类;一张订单是一个对象,订单就是一个类。只要从这个角度进行说明,用户往往能够马上建立正确的认识。

⑦总结

领域模型是以面向对象的视角看待现实世界的结果,通过类图来描述现实世界中各种事物之间的关系。在构建模型时,最主要的工作是找出相关类,然后命名类之间的关联关系,必要时加入一些多重性描述和业务规则约束。

分析模型和领域模型是很相近的,领域模型是一种全局的业务分析模型。在RUP中,分析模型主要是针对软件系统的分析,领域模型则更多是偏重对业务领域的分析。分析模型中有3种十分有用的构造型:实体类、控制类和边界类。

img

实体类:实体对象的抽象,通常来自域模型也就是现实世界,用来描述具体的实体,通常映射到数据库表格与文件中。

控制类:控制对象的抽象,主要用来体现应用程序的执行逻辑,将其抽象出来,可以使得变化不影响用户界面和数据库中的表。

边界类:边界对象的抽象,通常是用来完成参与者(用户、外部系统)与系统之间交互的对象,例如From、对话框、菜单、接口等。

img

分析模型实例(为了保持例子简单,在此略去了模型中类的属性信息)

边界类:CommandWindow 负责接收用户输入的命令,以及向用户显示命令结果

控制类:LightlnductorControl 负责与“航标灯器”感应器通信,获取灯器当前数据

控制类:RadarResponderInductorControl负责与“雷达应答器”感应器通信,获取雷达当前数据

控制类:GPSDeviceControl 负责与“GPS定位设备”感应器通信,获取当前位置

实体类:LightState(航标灯)负责存储航标灯器状态数据

实体类:RadarResponderState(雷达应答器)负责存储雷达应答器状态数据

实体类:GPSState(GPS设备)负责GPS定位数据

设计模型是在分析模型的基础上添加设计元素的结果。与分析模型相比,设计模型中类的属性集更趋完善;它将加入模板类、参数类、抽象类/接口等设计元素,以及框架类的使用、设计模式的使用等。设计模型是一种详细设计模型,将能够直接对编程予以指导。

img

设计模型示例

可以将不同的Control抽象成一个工厂类,这样就可以根据用户输入的命令来创建相应的Control,这样也达到了良好扩展性的目的。其次,选择JDBC来实现命令执行结果的存储。设计模型与选择什么语言开发是有很大关系。

E/R图应用基础:

①数据建模过程

描述业务实体之间的关系还可以用传统的E/R模型。它的优势在于能够更好地与后续的数据库设计结合,缺点在于语义相对于类图来说更弱一些,同时对面向对象开发的指导作用相对差一些。

img

在传统的数据建模理论中,将整个建模过程总结为6大阶段,它们将分别从不同的视角、循序渐进地完成

概念模型VS逻辑模型:

概念模型和逻辑模型实际上是对“需求视图”与“开发视图”的区分。换句话说,概念模型是需求人员的视图,等价于领域模型;逻辑模型是开发人员(包括设计人员)的视图,它约等于面向对象分析与设计方法中提到的“分析模型”。

例如,在某电子商务网站系统的需求调研阶段,通过与客户的沟通完成如下图所示的领域模型片段。

img

用E/R模型很难表示子类、组成等关系

img

逻辑模型片段示例,E/R模型只能通过一对多的关联关系来表示整体/部分关系

逻辑模型VS物理模型:

是对“开发视图”与“数据库视图”的区分,换句话说,物理模型就是DBA的视图。在许多团队中逻辑模型和物理模型几乎是一致的,如果说有区别的话,那就是中英文版本而已。物理模型会考虑到性能、效率等多方面的因素。

E**/R模型的主要元素**

E/R模型与关系型数据库关系紧密,因此在数据库设计阶段它的优势很明显,相关的工具也可以实现E/R与关系型数据库的正向、逆向工程,即基于E/R图创建数据库,以及基于数据库创建E/R图。

img

E/R模型的基本元素

img

E/R模型元素之间的关系

3、角色与使用场景分析

①用例分析技术概述

用例分析技术的关键是“发现使用系统的角色(参与者),了解并梳理这些角色将如何使用系统(场景)”,从而更好地完成“人”的视角的需求梳理。

img

用户包括两个有机的组织部分:用例图是目录,用例描述是封装所有需求的形式。

②参与者与用例

参与者是在系统之外,透过系统边界与系统进行有意义交互的任何事物。判断的要点在于他们是不是系统行为的触发者。

边界是一个逻辑的概念,是一种职责边界而非物理边界。简单来说,它就是指“待开发系统”;如果将整个待开发系统分成不同的部分,在对每个部分创建用例模型时,也可以将这个独立的部分视为一个系统的边界。

参与者不仅可以由人承担,还可以是其他系统、硬件设备,甚至是时钟。

注:MartinFowler认为,参与者更适合的术语是Role(角色),而Actor是源于对瑞典语(Iva是瑞典人)的误译。

一般,对于由人扮演的参与者,偏向采用下图左边的形象符号;对于系统扮演的参与者,则会倾向于右边的box符号。

③用例

用例实例是在系统中执行的一系列动作,这些动作将生成特定执行者可见的价值结果。一个用例定义一组用例实例。

•用例场景是有步骤的(执行一系列动作):是一个由一系列业务步骤组成的业务活动。

•用例场景是有目标的(可见的价值结果):能够为参与者带来有意义的结果,如“填写搜索条件”对参与者是没有意义的,不是一个合适的用例。

•用例是对一组用例实例(也称为场景)的抽象,也就是说,用例是有路径(基本事件流、扩展事件流、子事件流等)的。如我们在ATM上取款时,可能会遇到很多不同的场景:正常取到钱、卡里钱不够、密码忘了、机器里没有足够的钱、卡被吞了等;这些可以概括为“取款”用例。

④用例图

img

改系统以Internet的形式向客户提供座位预订的服务,如果暂时无法获取座位信息时,允许客户进入“等候队列”,当有人退订之后将及时通知客户。另外,还将为总台服务员提供座位的安排,以及结账的功能,支持现金和银行卡两种结账方式。

上图的用例图并不是一个很好的建模结果,其中存在着一些不足,文章最后会指出这些不足并说明如何进行改进。

参与者与用例的关系:

img

参与者和用例之间的关联关系表示两者之间将进行通信,任何一方都可以发送和接收消息。

img

通过泛化关系避免交叉线

用例之间的关系:

包括包含、扩展、泛化三种关系。

img

参与者之间的关系:

通过参与者泛化来简化模型表示

img

通过泛化关系避免交叉线

用例小结:

承接上面提出的用例图不足,因为“收款”这个子事件流只有“办理结账”一个用例用到,因此不应分解出来;只是为了讲解泛化关系才做这样的处理。改进后的用例图如下:

img

  • 怎样归纳用例

①自顶向下导出法

从流程图中派生出用例图。流程图中的岗位信息是参与者的候选者,他们负责的业务活动就是用例的候选者。评价的要点就在于“是否属于系统范围”。针对每张流程图进行分析之后,得到一组用例图片段,将它们叠加在一起,抽象出系统的用例模型。

img

某税务效能管理系统中针对“业务申请”流程图

第一步:边界确定(去除非EndUser的职责带区)。首先排除掉不直接使用系统的岗位,因为它们不是系统要涉及的范畴。本例中,我们会问用户“纳税人直接使用系统吗?省局/局外部门使用系统吗?”,当用户告诉我们纳税人要到办税服务中心提交申请,省局、局外部门不直接使用时,我们就可以将这两个职责带区中的所有活动忽略掉。

第二步:确定角色(对剩下的职责带区进行角色化)。接下来就可以将精力放在中间两个职责带区中了,通过与用户的交流,确定出与系统相关的参与者。

对于职责带区“涉税窗口”,可以询问具体是由哪个岗位负责,将其称为什么比较合适?例如用户会说:“这是由受理人员处理的”,那么就可以确定出一个名为“受理人员”的角色;

对于职责带区“局内业务科室”,可以进一步了解每个活动是由哪个岗位负责的?结果会发现不同“申请”负责的科室是不一样的,这属于业务规则的部分,属于需求细节。但可以归纳出两种角色:核查人员和审批人员,未来再根据需求细节确定岗位与角色之间的关系。

第三步:确定用例。用例是从职责带区中的业务活动派生出来的。

对于“涉税窗口”而言,图中有3个用矩形表示的活动、2个用菱形表示的判断。对于活动,主要判断它是否属于系统范畴之内;对于判断,要分析它是属于某个活动还是一个独立活动。

img

对于“局内业务科室”职责带区,一共包含4个活动、3个判断。

img

第四步:绘制用例图。

img

②自底向上合并法

对于流程不泾渭分明的某些中小型项目适合采用“自底向上合并法”。从需求捕获阶段获得的需求列表着手,合并出所需的用例。与前一种方法可结合使用。下面通过一个案例介绍:

第一步:收集原始需求。某开发组织为了更好地积累自己的项目经验数据,更好地进行项目估算与计划工作,决定开发一个时间记录系统,用户原始需求列表如下。

img

时间记录系统特性列表

第二步:确定参与者。从上面的描述中,我们可以确定候选参与者主要包括:管理层、研发经理、项目经理、开发人员。

然后我们再进一步分析他们所能承担的工作:

•开发人员:对任务进行操作和时间记录;

•项目经理:对项目的任务进行分配,了解项目内产能;

•研发经理、管理层:确定项目及进行产能统计工作。

另外,项目经理、研发经理也可以承担开发人员的角色,而研发经理则可以承担管理层的角色,也可以是项目经理的角色,因此它只是一个扮演了多个角色的岗位,可以将其去掉。

第三步:合并用例。参与者确定之后,将“原始需求”按参与者分组,然后再合并或分解为相应的用例。

img

第四步:绘制用例图。

img

  • 用例分析技术应用要点

①用例真的有粒度吗?

业务价值判断是关键

img

上图左边的用例模型片段就是一个不合理的建模结果,右边的才是正确的。一个会员可能会跑到网站上设定一个选择条件就离开吗?显然不会,除非是意外中断了。也就是这个用例并没有真正传达出有价值的结果。

注:实际的应用中会存在两方面干扰因素:被包含用例、扩展用例:有人将其认作了用例,它们表示的是子事件流、扩展事件流,不是真正意义上的用例;技术性用例的引入:例如“登录系统”之类的东西过早被建模出来,但业务价值却并不明显。

用例粒度与系统复杂度相关的观点是错误的

真正影响用例大小的是业务流程,是工作任务的分工。同样是“将保险合同输入电脑”这样一件事,针对不同的保险公司,用例的大小就是不同的:A保险公司的规定是:保险销售人员只负责将合同扫描到电脑中,由专门的后勤人员负责录入合同中的关键信息,那么就应该整理出“扫描合同”和“录入关键信息”两个用例,因为这是由不同人员来负责的。B保险公司的规定是:扫描合同、录入关键信息都是由保险销售人员负责的,那么就应该是一个名为“录入合同”的用例。

CRUD的价值被过于放大了

在互联网、各种书籍中都曾经提到一个“CRUD原则”,也就是建立将“新增XX、查询XX、更新XX和删除XX”之类的用例合并成“管理XX”。但实际上这个原则并不是那么常用的,很多时候被误用。

如在一个图书管理系统中,新增、删除、查询、更新操作实际上都不是由一个角色完成的,它怎么能够被合并在同一个用例中呢。CRUD原则对于“系统创造的东西”才适用,例如管理系统用户、管理数据字典、管理权限、管理购物车之类的东西就适用于该原则。

②用业务动词命名用例十分重要

某开发团队在开发银行信用卡管理系统时,整理了一些用例,其中包括:创建客户、更新客户、删除客户。有问题吗?当然有问题!别忙,千万不要错上加错,按CRUD原则将其合并成管理客户就将离正确越来越远了。

下图才是正确的做法:

img

③采用先事后人的方式分析是要点

在用例分析过程中,应该将人(角色、参与者)和事(场景、用例)分开考虑,先事后人地思考。如在开发一套医院管理系统时,分析人员了解到如下所示的需求。

在药房中,有3个主参与者:接待员、药房技师和药剂师。其中任何一个参与者都可能接待客户,接收处方。药房技师和药剂师都可以按照处方抓药,但只有药剂师有权审核处方并在处方中签字,而药房技师是协助药剂师的。

很多分析人员会先列出有哪些角色,然后再从角色看功能,结果就得到了如下图所示的用例模型。

看到这样的结果,自己也不满意,然后就开始使用扩展、包含关系来精化它,结果就得到了一个很怪异的结果。

img

如何获得更加合理的用例模型呢?在确定了参与者之后,再抽取出“事”(也就是用例),然后完成它们之间的连接,可轻松地获得更合理的结果。

img

四、周期一的产物

  • 工作任务说明

在需求分析的第一阶段,核心任务就是结合业务流程、报表的需求,梳理出结构框架(领域模型)和行为脉络(流程图+用例模型),为第二阶段的需求分析工作建立基础,指出方向。

具体就是从上一阶段标识出来的业务事件(业务流程的起点)和报表列表开始,展开对中层管理人员的访谈与调研,而范围就是“体检业务子系统”所对应的服务中心、体检科室和综合科三个部门。然后再根据访谈的结果完成事、物、人的分析,最后在此基础上抽象出该主题域的领域模型和用例模型。

  • 业务事件分析

在需求定义阶段中,我们已经通过和客户交流、绘制上下文关系图的方法,标识出了体检者申请体检、体检者中途改单、财务部门提交团队缴费情况、客服中心查询体检情况、维护人员管理体检项和系统通知用户取报告6个业务事件。

①体检者申请体检

业务流程分析:

img

体检者申请体检业务流程分析

注意1:开单收费环节涉及到业务判断,图中并未体现。因为它们属于细节层次,而判断的原则是对不影响泳道间协作的判断、活动均属于细节信息。

注意2:为便于在后续填充需求细节时更好地进行数据需求分析,可以标识出相关的表单、文档、规则等。可标识出以下相关文档:体检申请单(体检者在申请体检时填写的)、体检单(开单时打印出来的)、账单(收费时打印的)、体检项收费清单(在收费时使用的)、体检结果报告(体检科室的产物)以及体检报告(综合科出具的结果)。

业务实体分析:

实体分析的关键是理清问题域中的关键术语之间的关系,标识出类,确定它们之间的关系,并用类图表示出来。当主题域中所有业务流程、报表都分析完成后,再抽象出整个主题域的领域类图。本例中,听到的主要术语(候选类)就包括:体检者预约单 体检项目 体检套餐 体检单账单 体检结果 体检报告。

img

角色-使用场景分析

最后研究项目的边界,完成活动图到用例图的转换,完成系统的角色和使用场景分析。

img

img

综上,得到如下用例图片段:

img

  • 报表分析

报表分析工作可以分成Why(目标)、What(内容)与How(展现形式)三个层次;而Why应该在需求定义中明确(有时也会拖到本阶段来完成),在这个阶段关键在于对其数据内容进行分析。以“体检业务周期统计报表”为例进行说明。

Why

img

What

对于每一类报表,需确定与它相关的业务实体、主要数据项、数据项的计算方法,同时还要确定有多少具体的报表。

**相关业务实体分析。**根据对此类报表目的分析,我们可以知道所需的信息应该从“体检单”、“体检项”、“体检套餐”三个类中获得。另外我们还需要知道体检科室和体检项之间的关系,才可以统计出每个体检科室的任务量。因此关联的业务实体包括:体检单、体检项、体检套餐、体检科室4个,这样就可以用类图表示出来。

体检业务周期统计报表”类图片段图

报表项分析。“体检业务周期统计报表”是一类报表,我们需要根据实际的需要确定出具体的报表项,每个报表项可以建模为一个用例。通过与客户的交流,加上对报表目的的分析,我们可以得到如下用例图片段。

“体检业务周期统计报表”用例图片段

“体检业务周期统计报表”用例图片段

**数据项及计算方法分析。**据各类报表明确重要的数据项,对于派生出来的数据项还应该说明其计算方法。

  • 抽象与整理

通过前面的分析,将得到多个用例图片段(一个业务流程就有一个)、多个领域类片段(每个业务流程、每类报表都将有一个)。接下来可以通过Rose等建模工作对其进行抽象,创建出整个主题域的用例模型和领域模型。

①抽象用例模型

img

体检业务子系统用例模型(非完整版本)

②抽象类模型

img

体检业务子系统领域模型(非完版本)

  • 填充需求规格说明

通过以上这些分析,就可以完成结构框架和行为脉络的填充,同时将其填充到软件需求规格说明书中。

需求分析与建模周期一产物:

链接:https://pan.baidu.com/s/1gL_CyUT9ju8okM59rBcaDw

提取码:pjqa


8.软件需求最佳实践笔记 | 需求分析与建模(二)

已剪辑自: https://zhuanlan.zhihu.com/p/81742713

承接上文“软件需求最佳实践笔记 | 需求分析与建模(一)”。

三、周期二:确定需求细节

本阶段的任务是对用例模型、领域模型标识出用例、领域类的细节进行填充。对于组织行为需求的用例,要填充用例的事件流;对于组织数据(结构)需求的领域类,要填充它的字段与格式,具体包括:

字段/属性信息:也就是每个领域类所包含的成员属性,细化其构成;

字段格式与规则:也就是每个字段详细的格式(诸如字符型、日期型等类型,以及长度、可选值等内容)、组成规则(诸如由多少个字符、多少个数字组成等);

计算规则:对于一些非直接输入的派生属性,通过数据表达式的方式来描述;

结构规则:对于数据的组成、格式进行描述。

对于组织行为需求的用例而言,要细化的东西主要包括事件流、相关需求与功能点、界面原型,以及特定于该用例的规则与约束。

  • 确定行为需求的细节

①用例的灵活运用

根据行为需求的特点,可以将其分成“业务功能、报表功能、接口、技术支撑”4种类型。为了需求管理的方便,可以将其统一封装成用例,在具体细节描述方面,可以对其进行灵活处理。

img

②用例描述模板

针对业务功能类的用例来说,需要整理的内容主要包括事件流、相关需求与功能点、界面原型、规则与约束4个方面,描述的方法可以采用通用的用例描述模板来组织。

链接:https://pan.baidu.com/s/1mPAmN2uLyt1jMbGN6JrseQ 提取码:sqsn

用例编号:主要用于需求跟踪,不建议使用如UC101、UC102…之类的顺序编号:采用这种编号,插入一个用例时会显得十分麻烦。建议使用诸如“UC_子系统_类型_用例名称缩写”带有意义的编号规则。

用例名称:采用业务动词命名,是否合适关键在于用户代表是否能够认知,是否能够与实现的工作场景相对应。

用例概述:从用户执行该用例所能实现的目标着手,而不应简单地概括所有的动作;如无法写出目标,通常应反思用例的抽取是否合适。

主参与者与次要参与者:主参与者通常是指执行用例的用户,次要参与者则通常是用例执行过程中的协作者,大部分用例是没有次要参与者的。

项目相关人员利益说明:用例的功能设计不仅取决于执行它的最终用户,还和一些间接用户相关。

规则与约束:针对本用例的数据规则、业务规则、设计约束等信息。

前置条件、后置条件、成功保证、基本事件流、扩展事件流、子事件流则是用例描述中的核心部分。

③事件流分析

场景和用例之间的关系,与对象与类之间的关系类似;也就是说,一个用例是一组相似场景的抽象;因此,一个用例通常是一组事件流所构成的。

img

前、后置条件

前置条件是指在用例启动时,参与者(Actor)与系统应置于什么状态。这个状态应该是系统能够检测到的,在用例启动前能够检测到的,而且还应该是有意义的。

img

如上面的用例,客户已发出订单:不合适!因为这是系统无法检测到的;工作人员已登录系统:从前置条件的定义来看,它是吻合的。但它仍然是不合适的。因为这是一个意义不大的前置条件。这通常是“模板综合症”的表象,模板中列出的总感觉必须填写,这并不是一种科学、有效的态度。•库存大于订单数:不合适。这个值在用例开始之前是无法检测的。相反它恰好是一个正确的后置条件。

后置条件是指在用例结束时,系统应置于什么状态,这个状态应该是系统能够检测到的,在用例结束前能够检测到的,而且也应该是有意义的。

注:前、后置条件出现的频度并不高,不要画蛇添足。

事件流的类型与表述要点

对于用例而言,最常见的事件流类型包括3种:

img

在编写用例事件流的时候,为更加清晰地表达的含义,应该注意以下要点:

•使用简单语法:主语明确(尽可能采用主谓宾结构,少用修饰性语法),语义易于理解。

•明确写出“谁控制系统”:也就是在事件流描述中,让读者直观地了解是参与者在控制还是系统在控制。

•显示过程向前推移:每一步都有前进的感觉(如用户按下Tab键作为一个事件是不合适的)。

•用“确认”而非“检查是否”,如“系统确认所输入的信息中书名未有重名”;因为采用“检查是否”很难判断后续的动作是基于“是”还是基于“否”。

•从俯视的角度来编写:指出参与者的动作以及系统的响应,重点在于展示参与者的意图而非动作,否则很难让读者构建清晰的视图。

业务用例与系统用例

很多用例建模者都喜欢在用例描述中添加用户界面实现细节,其中一个原因是对业务用例和系统用例的错误理解。很多人认为:业务用例描述的是业务步骤,系统用例描述的是系统操作。其实错误的,实际上业务用例描述的是所有的业务步骤,而系统用例则只描述与系统相关的业务步骤。下面通过一个例子来说明业务用例以及系统用例的抽取过程。

img

•得到乘客的机票或记录标识符——1.确定乘客的预订信息

•确定乘客、航班、目的地是否正确——2.确保乘客身份正确,并与正确的预订联系起来

•检查护照有效并属于这名乘客——3.检查护照有效并属于这名乘客

护照必须是本人的;在旅行结束之前不能过期;对旅行的目的地国必须是有效的;签证必须是有效的;无目的国“拒绝入境”印章

•记下常客编号——4.记录常客的编号

5.分配一个座位

•询问安全问题——6.询问安全问题并得到正确回答

•7.行李托运

•8.打印登机牌和行李标签并递给乘客

•9.祝乘客“旅途愉快”

以上有许多步骤都是与系统无关的,是由用户手工完成的。因此,可以在此基础上筛选出与系统相关的步骤。第2、3、6、9步显然都是与计算机系统没有直接关系的;将它们从事件流中去除之后,就可以获得针对该场景的系统用例事件流。

创建业务用例描述的好处主要有两个方面:

•避免断章取义,帮助开发人员更好地理解场景。例如,根据后面抽象出来的系统用例事件流,开发人员在用户确定乘客预订信息的界面之后,就会直接跳转到常客编号页面;而如果看到了业务事件流,就会想到应该在此提供一个用户信息确认界面,帮助用户更好地完成第2、3两步工作。

•更好设置未来的扩展点。例如,第3步中的工作现在必然是人工完成的,但未来随着IC卡身份证、电子护照的普及,它就可能成为系统的一部分。

不当事件流示例分析

下面是一个综合型错误的例子:

img

针对上面这个事件流片段,相信没有几位读者能够从中获得清晰的脉络。导致这种结果产生的主要因素主要包括以下几个方面:

•过多实现细节:在用例事件流描述中,如果出现了诸如鼠标、键盘操作,窗口、下拉菜单等信息都是不好的表现。

•过于冗长:第3〜6步的描述给人的感觉比较拖沓,这样会导致整个描述详略不当。

•将分支结构交织在主线索中:第7、8两步中就包括了一些分支结构。

修改过的用例事件流:

img

下面是一个过于絮叨的例子:

img

建议采用如下所示的描述方法:

用户输入个人基本信息(字段内容参见业务对象BO_Customer)。

用户输入订单基本信息(字段内容参见业务对象BO_Order)。

这是因为对于某个实体(诸如个人、订单等)而言,字段内容的变化是很常见的,而且它通常会在多个用例中引用。如果直接采用Copy-Paste的策略,最终难以保证进行了正确的更新。

小结

编写用例的事件流时应该避免出现实现细节,不能过于冗长也不能过于简单(判断的要点在于是否写出了交互),并且每个事件流都应该明确地指出主语。另外,在编写事件流时应该避免直接出现if-else之类的分支结构、for…之类的循环结构,更不应该出现switch…case…之类的多路分支结构。

img

④相关需求整理

用例作为行为(功能)需求的封装单元,是按业务活动的角度将需求分而治之的方法,是对需求捕获阶段获得的用户原始需求的合并。实际应用中,可能会发现有时事件流很简单,没有编写的必要;有的时候却会发现事件流无法覆盖所有的功能需求。因此,需要对用例的相关需求进行整理。

用户原始需求

将需求捕获阶段获得的用户原始需求(通常每个原始需求是以一句话)整理到相应的用例中是一种很好的实践,可以更好地建立用户原始需求和软件需求(用例)之间的映射。通常,在以下几种情况下适合采用这种方法:

•事件流很简单:可以考虑直接将用户的原始需求罗列出来,忽略传统的事件流表述部分,开发人员在实现时只要注意满足这些需求即可。

•原始需求比较零散,有些超出了事件流、规则的范畴:可以考虑既编写事件流,同时还将原始需求罗列在用例描述之后。

•需求变更时:用户在提出需求变更时,我们需要找到其影响的用例,这样就能够更好地“对号入座”地处理。

具体操作时,可以考虑通过一个电子表格(或称为矩阵)来管理。

img

注:采用Word组织软件需求规格说明书的团队而言,可以直接把相关的原始需求放在用例描述之后

相关功能点

有时可能还会涉及一些无法有效地表述在事件流中的小功能点。例:在“受理业务申请”用例中,可能还需要实现受理表模板管理、打印格式模板管理等功能,如果它们属于局部的功能,则可以作为“相关功能点”来描述。这样当开发人员着手实现时,就能够更好地考虑到这些要求,提供更加科学、合理的解决方案。

⑤界面原型

界面原型要点

用例描述中是不建议包括界面实现细节的,而界面细节通常又属于需求的一部分,因此在需求分析、编写需求规约时也是不可忽略的一部分。

•“用户界面”的后面经常出现的词是“设计”,而非“分析”,因此用户界面不是分析出来的,而是设计出来的;需求人员应该提供设计依据而非设计结果,需求人员肯定无法了解所有最新的UI技术,也就不可能得出用户界面的最佳设计结果。

•软件需求规格说明书里的用户界面原型不是解决方案,它应该被视为约束、建议。也就是客户对用户界面原型的要求(约束),以及需求人员根据对用户场景、需要的理解给出的实现建议。开发人员不应该以此画地为牢,从而限制了合理用户界面的出现。

•创建用户界面原型时,重点在于实现用例事件流的交互过程,在于讲述每个界面中要满足的用户意图,而不仅仅是浮于表面的元素个数、位置、大小等信息。

交互不要忽略

可以考虑在原有的静态用户界面原型的基础上,绘制出“界面流转示意图”(可以用状态图或草图),以补充交互过程的描述。也可以使用状态图表示页面流转。

img

别让界面掩盖本质

img

从以上例子可以看出,用户界面至少还应该考虑从以下3个方面进行补充:

•目的:这个或者这组用户界面元素想让用户实现什么操作,达到什么意图,该信息能够有效地帮助开发人员理解场景。

•信息要求:说明信息显示的内容、格式、排序方法等方面的信息,帮助开发人员在实现时有效地实现它。例如,对于界面中心的“楼层平面图”就指出了应该显示房型、状态、房号三个信息,并建议考虑使用颜色来标识状态信息。

•操作要点:对于用户操作该界面时一些应该考虑到的要素或约束,例如,对于“完成”按钮指出要二次确认,要显示总结信息;对于“切换楼层”下拉列表而言,指出了应该能够直接通过输入数字的方法来进行快速定位。

⑥规则与约束

规则是在实现时应该考虑的东西,而约束则是对技术手段选择起到限制作用的各种条件。

规则的分类

从需求的角度来看,规则可分为:行为(功能、业务)规则、结构(或称为数据)规则、界面规则三类。

img

规则的影响层次

行为规则可能影响整个主题域,可能只是影响一个业务事件,也可能只是影响一个业务事件中的某张流程图,还可能只影响某个业务活动(即用例),甚至可能只对某个用例中的具体的某个步骤产生影响。通过层次分析,决定把规则写在什么地方。

img

对于结构规则,它影响的层次可以分为领域类间、领域类内、字段三个等级,我们也可以将其放在不同的位置上。

img

其他约束

img

⑦基于Stakeholder利益分析的需求挖掘

虽然每个用例都是为一个参与者实现一个价值的业务活动,可是在具体实现时它的功能细节却不仅仅受到参与者的影响,甚至连参与者本人也可能无法给出完整的需求。其中一个很重要的原因就在于Stakeholder的利益会对用例的功能细节产生微妙的影响。

img

img

超市收银用例Stakeholder

通过分析,我们发现收银员的上游是买单的顾客,下游是财务人员(收银员交班时要将收到的钱交给财务人员),管理者显然现场经理,但协作者还不明显。

下表分别为每类Stakeholder列出了一些分析示例:

img

img

超市收银系统LED显示位置示例

  • 确定结构需求的细节

①基本内容

软件需求规格说明书中如何对领域模型进行组织,以及如何对其进行细节描述。

领域模型的组织

通常首先按照主题域进行第一次分解,一个主题域对应一个领域模型,然后根据需要可以将各个主题域中共性的领域类抽取出来,形成全局公共领域类模型。

每个主题域内的领域模型(包括全局公共的领域模型),涉及的领域类可能还很多,可根据其逻辑关系划分成不同的部分,通常用包来表示;每个包中就是一个逻辑相关的领域类图片段。

接着,对每张领域类图片段进行简要的概述,罗列针对所有领域类的共性结构规则,然后再分而治之地进行描述。最终形成如下图所示的层次结构:

接下来,对每个领域类做进一步描述。通常可以从“数据窗口分析”、“数据组成与格式”、“派生数据的计算方法”三个角度进行描述。

数据窗口分析

数据窗口分析需求实践中一种视角,通过这一工作我们可以更客观地认识系统中的数据耦合。项目实践中经常发现公共的数据表成了大家工作交叉、冲突的温床。所有的修改都经过这张公共数据表或多或少地影响到其他人的工作。对于这种情况,建议采用如下图所示的方法,将共性的属性变成公共的,个性的属性压在各个模块中。

建立数据窗口的思想后,在对数据需求进行梳理时,针对每个类可以考虑采用如下图所示的组织方法来表示。

img

数据组成与格式

前面讲清楚的是该领域类是由哪些字段构成的,这些字段分别应用于什么主题域、哪个业务流程或报表,以及它们之间的共性属性有哪些。接下来,就应该对其格式、组成规则进行详细的描述。具体来说,这方面的信息包括以下几种类型:

•数据类型:诸如字符串型、整型、布尔型等;

•长度、精度信息:甚至可能涉及数据库存储的长度信息,诸如varchar(100),最好避免出现,此类信息应该放在数据库设计文档中。

•组成格式:也就是具体的构成规则,例如编号由2位字母、6位数字和一个可选的X组成。

组成格式建议考虑使用数据字典中的词条法表示:

=(等号):表示由…构成

+(加号):表示顺序连接的关系

|:表示从里面的项目中选择一个

{}n(花括号,外边是一个上标):表示n次重复

()(括号):表示可选的数据项

*…*表示特定限制的注释,诸如0—9表示数字

对于前面提到的编号而言,要表示它是由2位字母、6位数字和一个可选的x组成,可以表示为:编号={[A…Z丨a…z]}2+{0…9}6+(X)。

派生数据的计算方法

在领域类中,还涉及一些非直接输入值的属性,值是计算得出的。如订单类中的总金额。需要对它们的生成规则、计算方法进行表述,而描述的主要方法是算术表达式;有些时候可能会涉及较复杂的计算,如根据订单总金额所在的范围确定打折的幅度,这时决策表、决策树就是比较适合的方法。

img

②常见盲区

与结构需求相关的常见盲区包括数据结构特点、数据使用特点以及非功能要求三方面。

数据结构特点

img

数据使用特点

数据使用的角度不同,也会对具体的开发实现产生影响。最显然的区别在于联机事务处理系统和管理信息系统的数据使用特点;前者重在数据的记录,后者重在数据的查询、分析与统计。

数据的不同使用角度会对开发实现产生很大的影响,为了有效地预防这种情况的影响,建议为每类报表绘制一个相关领域类图片段,这样当我们动到任何一个领域类时,都可以找到所有可能受影响的报表、流程,从而采取一些相应的手段。

非功能需求

需求捕获阶段记录规模最大的用户的具体数量、数据提交的频率、数据量等信息。

img

img

  • 周期二的产物

①工作任务说明

在需求分析的第一阶段(周期一),完成了结构框架(领域模型)和行为脉络(流程图及用例模型)的梳理,因此在第二阶段,工作任务就是填充需求的细节,具体来说就是根据前面说明的过程与内容来填充用例、填充领域类。

从工作量的角度来说这个阶段比上个阶段要大得多,因此通常是无法一次性完成所有的用例、所有的领域类的细节填充的。在实际的工作中,建议根据迭代计划来分阶段细化,也就是将所有的用例划在不同的需求基线上,分配在不同的迭代中,然后使需求工作与设计、开发工作实现流水线。

②填充用例描述

开单用例分析

  1. 用例概述

内容主要包括用例名称、编号、参与者、用例概述、相关Stakeholder等几个部分。

用例名称:开单就是一个比较合适的用例名称,它是一个用户熟悉的业务动词,能够很好地理解。

编号:建议不要使用顺序号,而是使用有一定意义的编号;在此我们使用UC_B_TJ_KaiDan作为编号,表示这是一个业务功能类用例(B),属于体检主题域(TJ),用例名称为开单(KaiDan)。

参与者:该用例的参与者是“服务人员”。

用例概述:用例概述从目标、意图的角度进行表述;对于本用例而言,可写作“服务人员根据体检者的选择或预约单开具体检单,并打印出来交给体检者”。

相关Stakeholder:其相关的Stakeholder有两个,一个是上游的体检者,另一个是下游的收费人员。

2.事件流分析

先对该用例的前、后置条件进行分析。

前置条件:前置条件应该是系统可检测、在开始前可检测、有意义的;因此对于本用例而言,并没有相应的前置条件。要注意的是“体检人申请体检”是业务前提,但由于系统无法检测,因此不是合理的前置条件。

后置条件:后置条件应该是系统可检测、在结束时可检测、有意义的;对于本例而言,根据对业务的了解,我们发现体检单是由体检套餐和体检项目组成的,而体检套餐是包括体检项目的,这样就可能出现重复的项目,因此需要在用例结束时“确保没有重复的体检项目”,这就是它的后置条件。

结下来对用例的事件流进行分析。在实践中,可以询问“开单”工作是如何进行的,用户就可能谈出如下所示的场景:

当体检者要体检时,首先到我这里办理。如果已经预约,则告诉我他的预约号或者姓名;如果没有预约则填写体检申请表,选择体检项目或体检套餐(可以自由组合体检项目和体检套餐)。然后我将根据预约单或体检申请表的内容生成系统中的体检单,并打印出来。

开单用例的事件流描述:

基本事件流:

1.参与者输入用户姓名或预约号,系统确认用户已经预约,并从预约单中获取体检套餐与体检项目显出在屏幕上;

2.系统确认用户选择的体检套餐与体检项目符合要求(参见规则UC_KD_01);

3.系统保存并打印体检单。

备选(扩展)事件流:

la.参与者或系统确认用户没有预约

lal.参与者输入用户基本信息,并根据用户选择输入体检套餐与体检项目信息。

lb.系统发现有多个可能重名的预约用户

lbl.系统显示出所有可能重名的预约用户,并显示区分身份的主要信息;

lb2.参与者从中选择符合的预约用户,并从相应的预约单中调出数据。

2a.用户选择的体检套餐不符合要求

2al.系统给出具体的提示信息,并且阻止参与者完成体检单。异常事件流:

3a.系统保存或打印失败

3al.系统仍然显示信息录入界面,并提示失败原因。

3b.用户发现打印失败

3bl.系统已退出信息录入界面,参与者可切换到历史体检单界面,重新打印已保存的体检单。

得到事件流的初步脉络:

1.确认用户已经预约,并从预约单中获取体检套餐与体检项目;

2.系统确认用户选择的体检套餐与体检项目符合要求;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Go语言工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Go语言全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

预约则填写体检申请表,选择体检项目或体检套餐(可以自由组合体检项目和体检套餐)。然后我将根据预约单或体检申请表的内容生成系统中的体检单,并打印出来。*”

开单用例的事件流描述:

基本事件流:

1.参与者输入用户姓名或预约号,系统确认用户已经预约,并从预约单中获取体检套餐与体检项目显出在屏幕上;

2.系统确认用户选择的体检套餐与体检项目符合要求(参见规则UC_KD_01);

3.系统保存并打印体检单。

备选(扩展)事件流:

la.参与者或系统确认用户没有预约

lal.参与者输入用户基本信息,并根据用户选择输入体检套餐与体检项目信息。

lb.系统发现有多个可能重名的预约用户

lbl.系统显示出所有可能重名的预约用户,并显示区分身份的主要信息;

lb2.参与者从中选择符合的预约用户,并从相应的预约单中调出数据。

2a.用户选择的体检套餐不符合要求

2al.系统给出具体的提示信息,并且阻止参与者完成体检单。异常事件流:

3a.系统保存或打印失败

3al.系统仍然显示信息录入界面,并提示失败原因。

3b.用户发现打印失败

3bl.系统已退出信息录入界面,参与者可切换到历史体检单界面,重新打印已保存的体检单。

得到事件流的初步脉络:

1.确认用户已经预约,并从预约单中获取体检套餐与体检项目;

2.系统确认用户选择的体检套餐与体检项目符合要求;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Go语言工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Go语言全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-T3GCX5OY-1713084903018)]
[外链图片转存中…(img-m55I6zLO-1713084903019)]
[外链图片转存中…(img-QnXTz30g-1713084903019)]
[外链图片转存中…(img-yyEesyhq-1713084903020)]
[外链图片转存中…(img-GzqwGslw-1713084903020)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-UazjZz6k-1713084903021)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/706677
推荐阅读
相关标签
  

闽ICP备14008679号