当前位置: 首页 > news >正文

软件工程之需求分析

一、对需求的基本认识

1.需求分析简介

(1)什么是需求

  • 用户需求:由用户提出。原始的用户需求通常是不能直接做成产品的,需要对其进行分析提炼,最终形成产品需求。

  • 产品需求:产品经理针对用户需求提出的解决方案。

(2)为什么要做需求分析

  • 做软件本身就是为了满足用户需求。那么,用户需求到底为何, 清楚定义。

  • 需求边界定义的需要。用户需求理清楚了,不代表产品理清楚了。用户需求的满足一 定会有行业分工,我们做什么,合作伙伴做什么,需要厘清大家的边界。

  • 架构设计的需要。架构需要切分子系统,需要我们梳理并对用户需求进行归纳与抽 象。架构还需要防止过度设计,把简单的事情复杂化。

(3)需求分析过程

  • 面向的核心用户人群是谁?

  • 用户原始需求是什么?最核心问题是哪几个?

  • 已经有哪些玩家在里面?上下游有哪些类型的公司,在我们之前,用户是怎么解决他们的 问题的?我们的替换方案又是怎样的?

  • 产品创造的价值点是什么?用户最关注的核心指标是什么?

  • 用户需求潜在的变化在哪些地方?区分出需求的变化点和稳定点。

产品设计过程需要架构师的深度参与,而不是单向的信息传递。产品是桥,它一端连接了用户需求,一端连接了先进的技术。产品经理是

需要有技术高度的,他不一定要深刻了解技术的原理,但是一定要深刻理解新技术的边界。产品经理和架构师其实是一体两面。两者都需要关心用户需求与产品定 义。 只不过产品经理更多从用户需求出发,而架构师更多从技术实现出发,两者是在产品这座桥 的两端相向而行,最终必然殊途同归。架构师在整个架构设计的过程中,至少应该花费三分之一的精力 在需求分析上。

2.需求分析内容

需求分析不是一个动作,而是一个过程,需要经历三个步骤:

  • 挖掘真实需求

  • 提出解决方案:直到目标用户,其使用场景和想要解决的问题,就可以结合产品定位,提出相应的解决方案。

  • 筛选和验证方案:对方案进行筛选,去掉不合适的方案,选定方案后,需要对方案进行验证,确保能解决用户需求。

挖掘用户的真实需求,可以聪三个角度入手:

  • 目标用户

  • 使用场景

  • 要解决的问题:用户背后想要解决的问题是什么

3.怎么做需求分析

软件项目的需求从来不是单一的,是一系列需求,需求分析的整个过程是迭代进行的:

  • 收集需求:对用户需求进行收集整理

  • 分析需求:对需求进行分析,挖掘用户真实需求

  • 需求评估:筛选过滤掉不可行的需求

  • 需求设计:针对用户需求提出解决方案,设计成产品方案

  • 验证需求:验证方案是否可行

(1)收集需求

收集用户需求有很多方法,可以参考:

  • 头脑风暴:头脑风暴会议

  • 用户调研:通过调查问卷或访谈,收集问题反馈

  • 竞品分析:通过分析同类产品的功能获得需求

  • 快速原型:通过原型来收集反馈,确认需求

(2)分析需求

用户的真实需求有三个层次:

  • 表层需求:用户对解决问题的期望

  • 深层需求:用户的深层次动机,诉求产生的原因

  • 底层需求:人性本能的需求

(3)需求评估

需求评估需要考虑的因素有:

  • 可行性:技术能否实现

  • 成本:人力成本、时间成本

  • 商业风险和收益:有没有商业风险,收益是否合理

  • 紧急性与重要性:是不是用户迫切的需求

确定可行后,需要评估优先级,简单的可以使用四象限法,复杂的有KANO模型

(4)需求设计

在需求设计的时候,可以用草图、原型设计工具、界面设计工具进行设计。同时,设计阶段可以参考其他成熟的产品。

(5)验证需求

对需求的验证方式其实是贯穿整个软件项目生命周期的,在需求分析阶段,会反复验证,确认设计好的需求是否满足用户的真实需求,比如各种设计评审。

产品开发完成后,需要有需求的验收,来确保开发出的需求满足用户的真实需求,比如可以使用 A/B测试。

4.注意事项

(1)避免过早精细化

需求分析不能过早的进行精细化,业务方还没有启动项目就要精确知道最后得到什么;开发方没有评估项目就希望知道要交付什么,这是不合理的,因为需求与实现都是变化的,随着项目的不断推进与开发,会发现到最初没有考虑到的地方,所以需求分析和架构设计都不可能在一开始就预估到了所有场景

(2)避免过晚模糊化

需求的大致方向一定要在最开始就达成统一,完成确定,否则开发将在大方向偏离

5.需求明确

下面给出一个简单的例子。你在一家出版纸质书和电子书的公司工作。你接到一个新的需求:所有 50 美元以上的订单都应该免运费。停一秒钟,把自己带入这个场景。首先想到的是什么?你有大把机会发现问题:

1、50 美元含税吗?

2、50 美元算没算上本应支付的运费?

3、50 美元必须全是用来买纸质书吗?还是允许在同一订单中有部分电子书?

4、包邮指的是怎样的服务?是加急还是平邮?

5、如果是国际订单如何处理?

6、未来会经常改变 50 美元这个限制吗?

这就是我们所做的。当某些事情看起来很简单的时候,我们却会去寻找那些边缘情况,并就其不胜其烦地问人。

很可能客户已经想到了其中的一些问题,并假定实现将以某种方式工作。问这类问题只是把信息明确下来。但有些问题可能客户之前并没有考虑到。这就是事情变得有趣之处,也能让好的开发人员从此处事老练。

6.需求拆解

无论是新功能,还是历史功能,都需要关注如下点:

  • 需要多少UI(UI组件、功能页面)(UI=User Interface)

  • 数据从哪里来?

  • 数据与UI如何关联?(数据到哪里去、怎么去)

  • 用户行为响应

  • 用户行为采集

  • 发布后,如何运维与监控(出现问题的时候,可以有哪些方法帮助问题的排查)

(1)关注点1:需要多少UI

要绘制多少UI组件、要增加多少UI页面,首先取决于“需求文档(PRD)”、“视觉交互设计稿”。对于开发来说,请重视需求评审、视觉交互评审。对于质量良好的需求文档或者视觉设计稿,文档中给出的UI组件/页面范围一般就是开发要实现的UI范围;对于质量差的需求甚至是一句话的需求,UI范围则需要研发“梳理确定”。无论需求文档或视觉设计稿质量如何,研发人员在需求评审、视觉交互评审、技术设计、编码实现过程中都需重点关注“异常逻辑”。什么是异常逻辑?比如,一个查询类的需求,“查询结果为空”“查询时无网络”这类场景就属于“异常逻辑”。很多时候,新手容易出问题或者遗漏的地方就是异常逻辑的处理与显示。

需求拆解阶段,关注UI如何实现的同时,也请务必关注UI的展示对象,包括:图片与多媒体内容(CDN图片、OSS图片文件、base64编码后的图片)、文案(文案的长度、多语言翻译的来源)、RTL(Right to Left)适配。已一个支持17种语言甚至是更多语言的海外APP开发为示例,这些内容虽不影响开发进度,但是会影响需求在交付时的质量、或者是到了需求实现后期补作业。

实操方法:需求拆解的时,对于UI范围,可以拆解出如下内容用于辅助后续研发实现:

  • UI范围:功能页面范围、页面状态及状态的变化逻辑、界面的异常逻辑态等等

  • 页面元素的拆解:列表元素、卡片元素、动画元素、UI特效等(这个也是沉淀通用组件的依据)

  • UI展示的对象:文案的范围、多语言翻译的来源、RTL适配的范围、图片、短视频等。

(2)关注点2:数据从哪里来?

这里的数据是包括用户直接感知的数据与富媒体内容流,也包括用户不感知但是用于支撑功能的研发类型的数据。数据的来源包括:网络数据、本地存储的数据、内存数据。不同的数据类型,需求实现的处理有差异,例如:

  • 网络数据:需求拆解时候重点关注接口文档、接口名称、接口返回的数据字段(包括字段类型、范围、字段名称等等)、接口对应的团队或具体的开发、接口的上下游链路(示例:搜索的接口会涉及广告、算法等)、接口测试与联调方式等。对于新手,需求拆解的时候还需要确认:数据网关类型(MTOP网关、Node编排后的数据、Web网站对应的数据网关等),明确网络数据调度所用的域名(比如,部分APP交易功能的域名与非交易功能的域名是分开的,调度方式也会有差异)。

  • 本地存储数据:需求拆解阶段,要明确本地存储数据还是自己负责还是使用他人的能力。如果是自己负责则需要进一步判断数据是写本地数据库,还是写本地文件;如果是他人负责,数据的读写方式与接口是什么。

  • 内存数据:需求拆解重点关注内存数据的读写方式,数据使用后的释放方式。

  • 富媒体内容流:图片、短视频、直播等多媒体流。需求拆解时关注这些内容存储的形式(以图片为示例:图片是CDN的URL,还是OSS的文件地址或者ID,甚至是base64处理后的编码结果);同时还需要判断这些展示能力是已有的还是需要新引入等。

总结来说,“数据从哪里来”很好记忆,但是,在真实的研发过程中,“数据从哪里来”往往是研发过程中最容易出问题的地方。以网络数据为例,典型的问题有:联调环境不稳定、接口返回的字段未遵守约定、接口上下游数据链路不通导致无法测试等。即使需求完成并且交付上线,也还是会有各类问题,比如:引入了新CDN导致现有的图片/短视频加载过程的优化策略不支持,导致线上反馈性能或者是稳定性问题等。

对于“数据从哪里来”,在拆解需求的时候,一定要关注到每一个字段。这么强调,既因为这个点既影响实际投入工作量,还特别影响交付的需求的可用性与质量。

实操方法:进行需求拆解的时候,围绕数据从哪里来,可以拆解出以下内容:

  • 数据逻辑的分层结构图(根据实际需要)

  • 功能与数据接口的对照关系、接口文档、接口的承接平台、接口的Mock方式等

  • 功能的数据流转与逻辑流程图

(3)关注点3:数据与UI如何关联?

“数据要到哪里去,怎么去”,也可以理解为数据绑定。对于有一定复杂度的需求,“数据与UI如何关联”很大程度上会决定这个功能的实现与维护难易。需求拆解的时候,无论是选择MVC、MVP、还是MVVM,数据绑定这个过程比较考验开发人员的设计能力。我个人将数据绑定总结为如下几种:

  • 单向数据绑定:数据变化自动驱动UI刷新,代码实现会体现为各种Observer或者Observable等。

  • 双向数据绑定:数据改变的同时使视图刷新,而视图改变也可以同时改变数据。

  • 无所谓数据绑定,数据关联到UI是过程式的编程,体现为:加载数据、加载成功手工代码上屏等。

当然,以上总结是简化后的总结。对于新功能,建议需求拆解的时候,反复理解所需的数据源、充分理解UI范围,结合控制逻辑,仔细提取关键特征后,充分设计后再进行编码等操作。设计的时候多借鉴团队内或行业内的最佳实践。

对于维护类型的需求,特别是有浓重祖传特质的功能(比如2013年功能迭代至2022年的需求),则要花点时间理解原有的关联关系,这个很有可能是一个不大不小的难点。比如,有的历史功能在实现的时候,数据与UI的关系是通过EventBus/消息广播类的事件触发;还有的功能数据在独立进程加载,加载完成后通过进程间通信再通过事件通知机制绑定数据到UI。总结来说,部分历史功能在最开始的实现,会用到各种酷爽的方案,但是到了后期维护,这类酷爽方法则会以一种超长技术链路或者拗口的技术链路呈现给维护者,变成一座需要咬牙才能翻阅的“山”。本质上来说,编码不是人与机器的交流,而是人与人之间通过写作的交流。对于一个维护性质的功能,掌握原有关系的一种方式就是植入各种测试性的代码辅助自己理解。

实操方法:拆解时,数据与UI关联,可拆解出的内容(数据与UI的关联,建议将“克制”作为自己的原则):

  • 功能的数据流转与逻辑流程图

  • 代码设计、数据关系流转设计等等

  • 数据状态变化、UI刷新时机等

  • 关联所用的绑定能力、数据变化后的通知机制等

(4)关注点4:用户行为响应

相对于上文提及的三个点,这一点相对容易。对于带UI的需求,按照如下范围拆解与实现即可:

  • 归纳需求文档或设计文档中的用户行动点,包括:点击、上下滑动、左右滑动、长按、开锁屏、虚实键盘响应、缩放手势等等。

  • 行动点的常见处理:页面跳转、tips/toast/弹窗等展示、动画处理、界面缩放、界面关闭等

  • 需约定的接口或规范:页面跳转的schema/传参、二三方能力唤起的方式等

(5)关注点5:用户行为采集

也就是收集用户行为的数据,简称“数据埋点”。比如,用户点击按钮、进入页面、在某区域停留一定时长之类的行为数据。这些行为数据是产品、数据、BI等角色关心并用于分析用户特征的数据。用户行为采集与用户行为响应密切相关。

数据埋点作为独立的关注点强调,是因为即使研发在每个需求都会与“页面点”“点击点”“曝光点”“自定义事件点”打交道,同时也是最容易出问题的地方,问题示例::

  • 数据漏采集:有可能是产品/数据同学无明确的数据要求,也有可能是开发漏写数据埋点代码等。

  • 数据点错误:包括事件类型错误、数据点名称错误,采集时机错误、多采集、少采集、数据参数错误(数据参数错误包括字段名称不对、字段实际传递的值不对)等等。

实操方法:研发人员在拆解需求的时候,针对用户行为采集,check以下点:

  • 需求是否需要采集埋点数据

  • 点的名称是否符合产品/数据等消费方的要求

  • 参数范围与参数值是否有要求

  • 需求完成后,数据采集谁验证?

(6)关注点6:发布后,如何运维与监控

线上一旦出问题,开发或测试可以用什么工具或者方法进行排查分析。这就涉及到:需求如何发布、交付后的功能如何监控、开发过程中是否要提前布点以支撑排查分析工具的使用。

功能发布形式有多种,例如:灰度、Beta、A/B测试形式发布、动态配置下发、直接全量等。不同发布形式对于功能发布之初的观测有差异,要支撑不同的发布形式,具体的实现也有差异:比如,通过Google Play进行APP灰度测试不需要开发人员针对功能做额外处理;以A/B测试形式上线的功能则需要在编码之初就要考虑通过什么平台能力进行,同时还需要编写对应代码。

关于如何监控:需要区分是技术指标监控,还是业务类指标的监控。不同的监控范围,在需求拆解的时候就要决定哪个平台进行,比如:业务类的指标可以在xflush平台上进行(需求拆解时要考虑数据回流xflush是已有的能力,还是需要新建能力);性能类指标可以在魔兔/iTrace上观测;研发问题的排查可以通过SLS进行,还可以自建排查能力。监控虽可以跟随功能的交付逐步补充完善,考虑需求的完整性,建议是在需求拆解的时候明确监控范围与形式。毕竟,实现监控也是需要工作量的。

上线后有哪些工具可以用于排查分析问题,在需求实现的时候就需要将能力预置好的。例如,对于“用户下单”这类容错较低的需求,研发在需求实现过程中如果没有写入足够的日志,一旦线上用户反馈“同一个产品在同一个时间下两个订单”,大概率这个问题就是无头无解问题。

总结来说:需求发布到线上后如何运维与监控,研发人员在拆解需求的时候需要思考明白:需求发布形式、上线后期望的监控方式、出问题时可用的排查方式与与排查数据。这些明确后,具体的实现,很容易通过历史功能、咨询、查资料等方式学习到。

二、原型设计

明确用户需求是一件至关重要的事,想要低成本地确认用户需求,可以借助原型工具来实现。

瀑布模型的衍生模型中有一个叫:快速原型模型。可以低成本、快速地确认好请求。但也有一个问题,需要开发人员配合才能完成。

快速原型模型可以简单分为三个阶段,原型设计对应这三个阶段要达到的效果进行分类:

  • 低保真原型设计:通过线框图来展示界面上有什么,布局是什么样的。

  • 中等保真原型设计:不仅反映界面上的布局和内容,还可以展示网站的整体结构和交互。在真实度、色彩上要比最终的产品差一些。

  • 高保真原型设计:在界面的美观和交互的炫酷上要求真高。学习和制作成本都比较高,一般和低保真原型设计配合使用,先用低保真原型设计快速确认清楚需求,再用高保真原型确认最终的交互和UI设计。

1.如何做好原型设计

(1)分析:搞清楚用户需求,原型设计目标

(2)设计:划分好产品的信息架构,设计好产品操作的流程

在设计阶段主要从两个维度来考虑:

  • 信息框架:考虑清楚整个产品的信息框架,划分出模块。比较好的实践:画产品的信息结构图。

  • 使用流程:考虑清楚界面之间的流程。好的实践:画产品使用流程图。

(3)实施:按照设计的结果,对每个界面制作原型,并做好界面之间的链接

(4)验证:和项目成员、客户进行确认,收集意见反馈,根据反馈进行修改。

2.原型设计工具

原型设计工具很多,选择的时候可以从这几个维度去考虑:

  • 面向的平台:web、桌面、移动端

  • 保真度

  • 功能

  • 成本

三、需求变更

目前有很多管理需求变更的解决方案。比如:

  • 增强需求变成流程,让需求变更规范起来。通过严格的流程来过滤没有意义的变更

  • 快速迭代,缩短版本周期。

比解决方案更重要的是,追本溯源,分析原因,做到即使场景变换,也可以对症下药。

1.需要变更的原因

对比建筑行业的需求变更,软件行业的变更可以说是非常频繁了,究其原因,主要是两个方面:

  • 需求的确定性:软件工程的需求经常是抽象的、模糊的、不精确的。

  • 需求变更的成本:软件行业中,很多老板对软件项目需求变更导致的成本缺少系统认识。

2.如何解决需求变更问题

在软件项目开发中,需求变更其实不可避免的,一味地址需求变更也是不可取的。我们能做的就是利用软件工程的知识,理解需求变更背后深层次的原因,找到合适的方案来改善,积极拥抱合理的需求变化,减少不必要的需求变更。

从源头出发,针对需求变更发生的原因来给出解决方案:

  • 提升需求确定性,把需求分析做好,减少需求变更

  • 提高需求变更的成本,让客户或者产品经理不能太容易变更需求,这样就可以达到减少需求变更的目的。

  • 降低响应需求变更的成本,可以方便快捷地响应需求变更。

四、软件分析

1.分析全景图

分析的起点是问题本身,比如现象、痛点、挑战、价值等,从这些基础点去分析,如分析一个业务时,从业务愿景和业务目标去看这个业务有哪些利益相关者,也即有哪些角色在使用这个业务,从这些利益相关者的角度去思考他们的本质诉求,正是他们的诉求构成了我们要做什么的输入,不管外部怎么变化,他们的本质诉求是不变的,如对于消费者来讲,他们的诉求是花最短的时间、最少的钱、更好的体验买到心仪的商品;对于商家来讲,他们的诉求是怎么卖出更多的货、怎样获得更大的利润。反而如果我们不去关注利益关注点的本质诉求,而只是自己凭空想出来的,自以为有价值,结果一落地就出现了问题。

    当明确了要做什么(What)之后,接下来就要思考业务流程以及业务中包含的要素(业务对象)、业务模型以及业务能力(How),实际上这部分就是提供一个解决方案去实现前面提到的诉求。分析的阶段,一定要非常细,在软件分析中,有一些分析的工具帮助我们更好地理解事物本身,具体地在下一节中讲到。分析的产物是业务模型和业务能力地图,通过业务模型可以看出业务是什么、有什么,通过业务能力地图可以看出具体的业务能力有哪些,可以支撑哪些业务场景。

    分析往上看一层,就是要分析商业价值链和商业模式,虽然这一块并不是开发同学负责的范畴,了解一些还比较好,能让我们对业务有更深刻的认识,商业模式决定商业结构,商业结构决定交易结构,交易结构决定业务组成结构。利益相关者也是从业务组成结构中推导出来的,这一部分是最顶层的分析,分析业务的可行性,也即我们常说的Why。

2.具体分析方法

在实际中,会看到各种各样的分析方法,这些方法本身并不重要,重要的是它能给我们带来什么的帮助,为什么需要它,个人的观点是分析方法不要贪多,真正融汇到实际中,有那么1、2个方法就足已,不要迷失在各种各样的分析方法中,真正还是要了解分析的本质是什么,在第一部分中,已经提到分析的本质是要洞察出事物的组成,包含组成结构和运行机制,你再去看各种各样的分析方法,它们都是为了找出事物的组成结构和运行机制。如黄金圈分析方法,它就包含了三层(Why、What、How),分析事物不断从宏观到微观、从目的到实现;再比如5W2H,真正的把一件事分析得非常仔细,什么人在什么时间什么地点因为什么做了什么事。

画UML图并非是做做样子,而是真正地挖掘出业务能力有哪些、系统能力有哪些、业务模型是怎样的、要有哪些对象、对象之间的关系是怎样的。在实际工作中,有些人在分析阶段在这一块落实得并不那么好,其实问几个问题很容易暴露出来,比如设计的类图的出发点是什么、这个类的职责为什么有这些、这个职责为什么在这个类而不是在另外一个类中。如果我们分析阶段做得不扎实,设计阶段的输入就会比较少,或者是浅层次的输入,设计的质量也不会高,因为并没有真正洞察出问题。

3.1个分析案例

了几个关键词进行概括:打基础、拓渠道、夯能力、搭体系、数据化。当有了这些认识之后,再去推导技术侧要做哪些就比较容易,以拓展渠道为例,当多个渠道接入进来时就暴露了一些问题,比如答疑成本比较高,因此就有一个重要的方面就是渠道接入保障,怎么减少渠道接入成本、答疑成本就是技术侧要思考的问题。

相关文章:

  • 利用R语言heatmap.2函数进行聚类并画热图
  • LeetCode 每日一题 Day 9 ||简单dp
  • OpenVINS学习2——VIRAL数据集eee01.bag运行
  • 【C++11】lambda表达式及包装器
  • 通俗易懂,什么是.NET Core以及.NET Core能做什么
  • Rxjs 学习笔记 - 简化版
  • openssl的x509命令工具
  • Kubernetes入门笔记——(2)k8s设计文档
  • Axure安装及面板各区域详解
  • 【ArcGIS Pro微课1000例】0053:基于SQL Server创建与启用地理数据库
  • 数据结构:栈(Stack)的各种操作(入栈,出栈,判断栈非空,判断栈已满,附源码)
  • 第十六届山东省职业院校技能大赛高职组“应用软件系统开发”赛项样题
  • 【EI会议征稿】第三届电气、电力与电网系统国际会议(ICEPGS 2024)
  • DAP数据集成与算法模型如何结合使用
  • JAVA基础知识:泛型
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 【RocksDB】TransactionDB源码分析
  • Android组件 - 收藏集 - 掘金
  • JavaScript对象详解
  • JavaScript类型识别
  • Laravel 中的一个后期静态绑定
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • React Transition Group -- Transition 组件
  • React-redux的原理以及使用
  • redis学习笔记(三):列表、集合、有序集合
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 搞机器学习要哪些技能
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 聊聊sentinel的DegradeSlot
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 听说你叫Java(二)–Servlet请求
  • 学习HTTP相关知识笔记
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • 选择阿里云数据库HBase版十大理由
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​水经微图Web1.5.0版即将上线
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (1)(1.13) SiK无线电高级配置(六)
  • (rabbitmq的高级特性)消息可靠性
  • (vue)页面文件上传获取:action地址
  • (二)Eureka服务搭建,服务注册,服务发现
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (三)uboot源码分析
  • (转) Face-Resources
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .NET CLR Hosting 简介
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .Net Core与存储过程(一)
  • .net 发送邮件
  • .NET 设计模式—简单工厂(Simple Factory Pattern)
  • .NET的数据绑定
  • .NET序列化 serializable,反序列化
  • [1127]图形打印 sdutOJ
  • [ai笔记3] ai春晚观后感-谈谈ai与艺术